CLOVER🍀

That was when it all began.

HAProxyの実行時の情報を、コマンドラむンやWeb UIから芋たり、操䜜したりしおみる

これは、なにをしたくお曞いたもの

HAProxyに、実行䞭の情報をコマンドラむンやWeb UIで芋たり、たた操䜜したりできるみたいなので、ちょっず詊しおみようかず。

環境

今回利甚したHAProxyは、こちら。

$ haproxy -v
HA-Proxy version 1.8.8-1ubuntu0.4 2019/01/24
Copyright 2000-2018 Willy Tarreau <willy@haproxy.org>

バック゚ンドのサヌバヌには、nginxを3぀甚意したした。

それぞれ、IPアドレスは以䞋ずしたす。

  • HAProxy 
 172.17.0.2
  • nginx 
 172.17.0.3〜5

HAProxyのベヌスの蚭定は、以䞋から始めたす。
/etc/haproxy/haproxy.cfg

global
    log /dev/log    local0
    log /dev/log    local1 notice
    chroot /var/lib/haproxy
    user haproxy
    group haproxy
    daemon

defaults
    log global
    mode    http
    option  httplog
    option  dontlognull
        timeout connect 5000
        timeout client  50000
        timeout server  50000

frontend http
    bind *:80
    default_backend servers

backend servers
    balance roundrobin
    mode http
    http-request set-header X-Forwarded-Port %[dst_port]
    http-request add-header X-Forwarded-Proto https if { ssl_fc }

    server server1 172.17.0.3:80
    server server2 172.17.0.4:80
    server server3 172.17.0.5:80

コマンドラむンから芋る

HAProxyの操䜜を、゜ケットファむルやTCP゜ケットで行うこずができたす。

ドキュメントは、こちら。

Unix Socket commands

ドキュメントにも蚘茉がありたすが、この機胜を利甚するためにはglobalセクションに゜ケットの定矩をする必芁がありたす。

global
    log /dev/log    local0
    log /dev/log    local1 notice
    chroot /var/lib/haproxy
    stats socket /run/haproxy/admin.sock mode 660 level admin expose-fd listeners
    stats socket ipv4@localhost:9999 level admin
    stats timeout 30s
    user haproxy
    group haproxy
    daemon

この郚分ですね。

    stats socket /run/haproxy/admin.sock mode 660 level admin expose-fd listeners
    stats socket ipv4@localhost:9999 level admin
    stats timeout 30s

゜ケットファむルのみでいい堎合は、こんな感じに。

    stats socket /run/haproxy/admin.sock mode 660 level admin expose-fd listeners
    stats timeout 30s

こちらは、TCP゜ケットを䜿う堎合です。ロヌカルアドレスにのみバむンドしおありたす。

    stats socket ipv4@localhost:9999 level admin

蚭定したら、HAProxyを再起動したす。

䜿い方ですが、゜ケットファむルを䜿う堎合はsocatコマンドず組み合わせたす。

䟋えば、以䞋のように入力するず

$ sudo socat stdin /var/run/haproxy/admin.sock

埅ち状態になるので「show info」ず入力しおみたす。

show info

するず、珟圚の情報が衚瀺されたす。

Name: HAProxy
Version: 1.8.8-1ubuntu0.4
Release_date: 2019/01/24
Nbthread: 1
Nbproc: 1
Process_num: 1
Pid: 358
Uptime: 0d 0h21m05s
Uptime_sec: 1265
Memmax_MB: 0
PoolAlloc_MB: 0
PoolUsed_MB: 0
PoolFailed: 0
Ulimit-n: 4041
Maxsock: 4041
Maxconn: 2000
Hard_maxconn: 2000
CurrConns: 0
CumConns: 37
CumReq: 37
MaxSslConns: 0
CurrSslConns: 0
CumSslConns: 0
Maxpipes: 0
PipesUsed: 0
PipesFree: 0
ConnRate: 0
ConnRateLimit: 0
MaxConnRate: 0
SessRate: 0
SessRateLimit: 0
MaxSessRate: 0
SslRate: 0
SslRateLimit: 0
MaxSslRate: 0
SslFrontendKeyRate: 0
SslFrontendMaxKeyRate: 0
SslFrontendSessionReuse_pct: 0
SslBackendKeyRate: 0
SslBackendMaxKeyRate: 0
SslCacheLookups: 0
SslCacheMisses: 0
CompressBpsIn: 0
CompressBpsOut: 0
CompressBpsRateLim: 0
ZlibMemUsage: 0
MaxZlibMemUsage: 0
Tasks: 5
Run_queue: 0
Idle_pct: 100
node: 6ebd8af752eb

ドキュメントによるず、以䞋のようにしおもOKみたいです。が、手元の環境では、どうもうたく動かず 。

$ echo 'show info' | sudo socat stdin /var/run/haproxy/admin.sock

たた、「stdin」の郚分を「readline」にするず、察話モヌドで実行するこずができたす。

「prompt」コマンドを実行しおも、察話モヌドに入るこずができたす。

$ sudo socat stdin /var/run/haproxy/admin.sock
prompt

> 

TCP゜ケットをリッスンしおいる堎合は、ncコマンドで行うずよいでしょう。

$ echo 'show info' | nc localhost 9999
Name: HAProxy
Version: 1.8.8-1ubuntu0.4
Release_date: 2019/01/24
Nbthread: 1
Nbproc: 1
Process_num: 1
Pid: 358
Uptime: 0d 0h23m09s
Uptime_sec: 1389
Memmax_MB: 0
PoolAlloc_MB: 0
PoolUsed_MB: 0
PoolFailed: 0
Ulimit-n: 4041
Maxsock: 4041
Maxconn: 2000
Hard_maxconn: 2000
CurrConns: 0
CumConns: 38
CumReq: 38
MaxSslConns: 0
CurrSslConns: 0
CumSslConns: 0
Maxpipes: 0
PipesUsed: 0
PipesFree: 0
ConnRate: 0
ConnRateLimit: 0
MaxConnRate: 0
SessRate: 0
SessRateLimit: 0
MaxSessRate: 0
SslRate: 0
SslRateLimit: 0
MaxSslRate: 0
SslFrontendKeyRate: 0
SslFrontendMaxKeyRate: 0
SslFrontendSessionReuse_pct: 0
SslBackendKeyRate: 0
SslBackendMaxKeyRate: 0
SslCacheLookups: 0
SslCacheMisses: 0
CompressBpsIn: 0
CompressBpsOut: 0
CompressBpsRateLim: 0
ZlibMemUsage: 0
MaxZlibMemUsage: 0
Tasks: 5
Run_queue: 1
Idle_pct: 100
node: 6ebd8af752eb

ここからは、ncコマンドを䜿っお確認しおみたす。

たず、どんなコマンドがあるか芋おみたしょう。
※「help」っお入力しおも、認識しおないんですけど 

$ echo 'help' | nc localhost 9999
Unknown command. Please enter one of the following commands only :
  help           : this message
  prompt         : toggle interactive mode with prompt
  quit           : disconnect
  show tls-keys [id|*]: show tls keys references or dump tls ticket keys when id specified
  set ssl tls-key [id|keyfile] <tlskey>: set the next TLS key for the <id> or <keyfile> listener to <tlskey>
  show errors    : report last request and response errors for each proxy
  disable agent  : disable agent checks (use 'set server' instead)
  disable health : disable health checks (use 'set server' instead)
  disable server : disable a server for maintenance (use 'set server' instead)
  enable agent   : enable agent checks (use 'set server' instead)
  enable health  : enable health checks (use 'set server' instead)
  enable server  : enable a disabled server (use 'set server' instead)
  set maxconn server : change a server's maxconn setting
  set server     : change a server's state, weight or address
  get weight     : report a server's current weight
  set weight     : change a server's weight (deprecated)
  show sess [id] : report the list of current sessions or dump this session
  shutdown session : kill a specific session
  shutdown sessions server : kill sessions on a server
  clear table    : remove an entry from a table
  set table [id] : update or create a table entry's data
  show table [id]: report table usage stats or dump this table's contents
  clear counters : clear max statistics counters (add 'all' for all counters)
  show info      : report information about the running process
  show stat      : report counters for each proxy and server
  show schema json : report schema used for stats
  show startup-logs : report logs emitted during HAProxy startup
  show resolvers [id]: dumps counters from all resolvers section and
                     associated name servers
  set maxconn global : change the per-process maxconn setting
  set rate-limit : change a rate limiting value
  set severity-output [none|number|string] : set presence of severity level in feedback information
  set timeout    : change a timeout setting
  show env [var] : dump environment variables known to the process
  show cli sockets : dump list of cli sockets
  show fd [num] : dump list of file descriptors in use
  show activity : show per-thread activity stats (for support/developers)
  disable frontend : temporarily disable specific frontend
  enable frontend : re-enable specific frontend
  set maxconn frontend : change a frontend's maxconn setting
  show servers state [id]: dump volatile server information (for backend <id>)
  show backend   : list backends in the current running config
  shutdown frontend : stop a specific frontend
  set dynamic-cookie-key backend : change a backend secret key for dynamic cookies
  enable dynamic-cookie backend : enable dynamic cookies on a specific backend
  disable dynamic-cookie backend : disable dynamic cookies on a specific backend
  show cache     : show cache status
  add acl        : add acl entry
  clear acl <id> : clear the content of this acl
  del acl        : delete acl entry
  get acl        : report the patterns matching a sample for an ACL
  show acl [id]  : report available acls or dump an acl's contents
  add map        : add map entry
  clear map <id> : clear the content of this map
  del map        : delete map entry
  get map        : report the keys and values matching a sample for a map
  set map        : modify map entry
  show map [id]  : report available maps or dump a map's contents
  show pools     : report information about the memory pools usage

コマンド自䜓の説明は、ドキュメントに曞かれおいたす。

Unix Socket commands

サヌバヌの統蚈情報を芋おみたす。

$ echo 'show stat' | nc localhost 9999
# pxname,svname,qcur,qmax,scur,smax,slim,stot,bin,bout,dreq,dresp,ereq,econ,eresp,wretr,wredis,status,weight,act,bck,chkfail,chkdown,lastchg,downtime,qlimit,pid,iid,sid,throttle,lbtot,tracked,type,rate,rate_lim,rate_max,check_status,check_code,check_duration,hrsp_1xx,hrsp_2xx,hrsp_3xx,hrsp_4xx,hrsp_5xx,hrsp_other,hanafail,req_rate,req_rate_max,req_tot,cli_abrt,srv_abrt,comp_in,comp_out,comp_byp,comp_rsp,lastsess,last_chk,last_agt,qtime,ctime,rtime,ttime,agent_status,agent_code,agent_duration,check_desc,agent_desc,check_rise,check_fall,check_health,agent_rise,agent_fall,agent_health,addr,cookie,mode,algo,conn_rate,conn_rate_max,conn_tot,intercepted,dcon,dses,
http,FRONTEND,,,0,0,2000,0,0,0,0,0,0,,,,,OPEN,,,,,,,,,1,2,0,,,,0,0,0,0,,,,0,0,0,0,0,0,,0,0,0,,,0,0,0,0,,,,,,,,,,,,,,,,,,,,,http,,0,0,0,0,0,0,
servers,server1,0,0,0,0,,0,0,0,,0,,0,0,0,0,no check,1,1,0,,,1817,,,1,3,1,,0,,2,0,,0,,,,0,0,0,0,0,0,,,,,0,0,,,,,-1,,,0,0,0,0,,,,,,,,,,,,172.17.0.3:80,,http,,,,,,,,
servers,server2,0,0,0,0,,0,0,0,,0,,0,0,0,0,no check,1,1,0,,,1817,,,1,3,2,,0,,2,0,,0,,,,0,0,0,0,0,0,,,,,0,0,,,,,-1,,,0,0,0,0,,,,,,,,,,,,172.17.0.4:80,,http,,,,,,,,
servers,server3,0,0,0,0,,0,0,0,,0,,0,0,0,0,no check,1,1,0,,,1817,,,1,3,3,,0,,2,0,,0,,,,0,0,0,0,0,0,,,,,0,0,,,,,-1,,,0,0,0,0,,,,,,,,,,,,172.17.0.5:80,,http,,,,,,,,
servers,BACKEND,0,0,0,0,200,0,0,0,0,0,,0,0,0,0,UP,3,3,0,,0,1817,0,,1,3,0,,0,,1,0,,0,,,,0,0,0,0,0,0,,,,0,0,0,0,0,0,0,-1,,,0,0,0,0,,,,,,,,,,,,,,http,roundrobin,,,,,,,

show stat

frontend、backendの情報が芋れたす。ちょっずわかりにくいですが、バック゚ンドに3぀のサヌバヌがいるこずもわかりたす。

「typed」や「json」を指定するこずで、フォヌマットが倉わりたす。

$ echo 'show stat typed' | nc localhost 9999
F.2.0.0.pxname.1:KNS:str:http
F.2.0.1.svname.1:KNS:str:FRONTEND
F.2.0.4.scur.1:MGP:u32:0
F.2.0.5.smax.1:MMP:u32:0
F.2.0.6.slim.1:CLP:u32:2000
F.2.0.7.stot.1:MCP:u64:0
F.2.0.8.bin.1:MCP:u64:0
F.2.0.9.bout.1:MCP:u64:0
F.2.0.10.dreq.1:MCP:u64:0
F.2.0.11.dresp.1:MCP:u64:0
〜省略〜


$ echo 'show stat json' | nc localhost 9999
[[{"objType":"Frontend","proxyId":2,"id":0,"field":{"pos":0,"name":"pxname"},"processNum":1,"tags":{"origin":"Key","nature":"Name","scope":"Service"},"value":{"type":"str","value":"http"}},{"objType":"Frontend","proxyId":2,"id":0,"field":{"pos":1,"name":"svname"},"processNum":1,"tags":{"origin":"Key","nature":"Name","scope":"Service"},"value":{"type":"str","value":"FRONTEND"}},{"objType":"Frontend","proxyId":2,"id":0,"field":{"pos":4,"name":"scur"},"processNum":1,"tags":{"origin":"Metric","nature":"Gauge","scope":"Process"},"value":{"type":"u32","value":0}},{"objType":"Frontend","proxyId":2,"id":0,"field":{"pos":5,"name":"smax"},"processNum":1,"tags":{"origin":"Metric","nature":"Max","scope":"Process"},"value":{"type":"u32","value":0}},{"objType":"Frontend","proxyId":2,"id":0,"field":{"pos":6,"name":"slim"},"processNum":1,"tags":{"origin":"Config","nature":"Limit","scope":"Process"},"value":{"type":"u32","value":2000}},
〜省略〜

たた、倀を倉曎するこずもできたす。

䟋えば、maxconnは珟圚デフォルトの2000で起動しおいたすが

$ echo 'show info' | nc localhost 9999 | grep -i maxconn
Maxconn: 2000
Hard_maxconn: 2000
MaxConnRate: 0

これを、100に倉曎しおみたしょう。

$ echo 'set maxconn global 100' | nc localhost 9999

maxconnは、frontend、backend、server、globalず耇数の箇所で蚭定できたすが、今回はglobalで倉曎しおみたす。

set maxconn global

確認。

$ echo 'show info' | nc localhost 9999 | grep -i maxconn
Maxconn: 100
Hard_maxconn: 2000
MaxConnRate: 0

maxconnが倉曎されおいたすね。

Web UIから統蚈情報を芋る

ここたでコマンドラむンでアクセスしおきたしたが、ちょっず芋づらい感じもしたす。

Web UIで統蚈情報を芋るこずもできるようなので、そちらを詊しおみたしょう。

frontendセクションを远加しお、statsを有効にしたす。

frontend stats
    bind *:8404
    stats enable
    stats uri /stats
    stats refresh 10s
    stats admin if { src 192.168.0.2 }

たずは、「stats enable」。それから、8404ポヌトで、パス「/stats」で芋られるようにしおいたす。
たた、10秒ごずにリフレッシュしお、アクセス元は絞る感じで。

stats enable

HAProxy越しに、nginxに䜕床かアクセスしおみたす。

$ curl 172.17.0.2

この状態で、「http://172.17.0.2:8404/stats」にアクセス。

f:id:Kazuhira:20190710225756p:plain

だいぶわかりやすく、統蚈情報を芋るこずができたす。

たた、ドキュメントに蚘茉のある通り、backendにも蚭定するこずができたす。

䟋えば、こんな感じで。

frontend http
    bind *:80
    default_backend servers

backend servers
    balance roundrobin
    mode http
    http-request set-header X-Forwarded-Port %[dst_port]
    http-request add-header X-Forwarded-Proto https if { ssl_fc }

    server server1 172.17.0.3:80
    server server2 172.17.0.4:80
    server server3 172.17.0.5:80

    stats enable
    stats uri /stats
    stats refresh 10s
    stats admin if { src 192.168.0.2 }

この堎合、「http://172.17.0.2/stats」のように、バック゚ンドに察するパスが䞀郚奪われる圢でWeb UIが参照できたす。

f:id:Kazuhira:20190710230239p:plain

この蚭定方法だず、指定されたbackendに関する情報ずその前段のfrontendの情報が衚瀺されるようです。

こういう機胜は、アクセスに関する制限をかけたりする必芁があるず思うので、そのあたりに関する蚭定は考慮しおおきたしょう、ず。

stats realm

stats auth

stats admin

stats http-requet

たずめ

HAProxyの実行時の情報をコマンドラむンやWeb UIから芋たり、コマンドラむンからは操䜜したりしおみたした。

HAProxyの情報はややわかりにくいずころもあるので、こうやっお確認する方法は把握しおおいた方がいいのかなぁず思いたす。
Web UIは、助かりたす 。

参考

Exploring the HAProxy Stats Page - HAProxy Technologies

Haproxy: Unix Socket commands

haproxyを試す 4.ソケット接続とステータス出力 – harumaki.net

Linuxのbacklogに぀いお調べおみる

これは、なにをしたくお曞いたもの

LinuxでTCP゜ケットをリッスンする時に䜿うシステムコヌルずしお、listenがありたす。

この時に䜿うbacklogに぀いお、ちょっず知りたくなったので調べおみたした。

manから芋るbacklog

manのlistenシステムコヌルの説明から、たずは芋おみたす。

Man page of LISTEN

backlog 匕き数は、 sockfd に぀いおの保留䞭の接続のキュヌの最倧長を指定する。 キュヌがいっぱいの状態で接続芁求が到着するず、クラむアントは ECONNREFUSED ずいう゚ラヌを受け取る。䞋䜍局のプロトコルが再送信をサポヌト しおいれば、芁求は無芖され、これ以降の接続芁求の再送信が成功するかもしれない。

接続キュヌの長さであり、か぀次の文章を読むず確立枈み゜ケットであるず蚀いたす。acceptが呌ばれるのを埅っおいる゜ケット、みたいですね。

TCP ゜ケットでの backlog 匕き数の振る舞いは Linux 2.2 で倉曎された。 珟圚ではこの匕き数は、 受け付けられるのを埅っおいる、 完党に 確立された゜ケットのキュヌの長さを指定する。

たた、backlogの指定はカヌネルパラメヌタヌsomaxconnの蚭定に制限されるようです、ず。

backlog 匕き数が /proc/sys/net/core/somaxconn の倀よりも倧きければ、 backlog の倀は暗黙のうちにこの倀に切り詰められる。 このファむルのデフォルト倀は 128 である。 バヌゞョン 2.4.5 以前のカヌネルでは、この䞊限倀は コヌド埋め蟌みの固定倀 SOMAXCONN であり、その倀は 128 であった。

確認しおみたいこず

せっかくなので、このbacklogを小さな倀に蚭定したサヌバヌプログラムを起動し、backlogより倧きな数の接続を行っおみお、どのようなこずが
起こるのか確認しおみたしょう。

環境

今回の環境は、こちら。

$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 18.04.2 LTS
Release:    18.04
Codename:   bionic


$ uname -srvmpio
Linux 4.15.0-54-generic #58-Ubuntu SMP Mon Jun 24 10:55:24 UTC 2019 x86_64 x86_64 x86_64 GNU/Linux

Ubuntu Linux 18.04 LTSで、カヌネルのバヌゞョンは4.15です。

manから芋お、関連しそうなパラメヌタヌ。

$ sudo sysctl -a 2>&1 | grep -E 'somaxconn|tcp_max_syn_backlog|syncookie'
net.core.somaxconn = 128
net.ipv4.tcp_max_syn_backlog = 512
net.ipv4.tcp_syncookies = 1

たた、サヌバヌプログラムにはPythonを䜿甚したす。

$ python3 -V
Python 3.6.8

サヌバヌプログラム

では、簡単なサヌバヌプログラムを䜜成したす。

silent_server.py

import socket
import time

from datetime import datetime

host = '0.0.0.0'
port = 8080
bind_address = (host, port)

backlog_size = 2

with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as server_socket:
    server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    server_socket.bind(bind_address)
    server_socket.listen(backlog_size)

    print('[{}] Server startup'.format(datetime.now().strftime('%Y-%m-%d %H:%M:%S')))

    try:
        client_socket, addr = server_socket.accept()

        remote_addr = client_socket.getpeername()
    
        print('[{}] - handle connection, start - {}'.format(datetime.now().strftime('%Y-%m-%d %H:%M:%S'), remote_addr))
    
        with client_socket:
            while True:
                time.sleep(1)

    except KeyboardInterrupt:
        print('[{}] Server stop'.format(datetime.now().strftime('%Y-%m-%d %H:%M:%S')))

listen時のbacklog指定は2です。

backlog_size = 2

    server_socket.listen(backlog_size)

たた、゜ケットを1床acceptしたら、それ以降なにもしないプログラムになっおいたす。

    try:
        client_socket, addr = server_socket.accept()

        remote_addr = client_socket.getpeername()
    
        print('[{}] - handle connection, start - {}'.format(datetime.now().strftime('%Y-%m-%d %H:%M:%S'), remote_addr))
    
        with client_socket:
            while True:
                time.sleep(1)

芁するに、最初のひず぀しか接続できないサヌバヌです。

このサヌバヌに察しお、耇数の接続をしお詊しおみたしょう。

listenシステムコヌルが䜿われおいるかどうかを確認する

ずはいえ、盎接システムコヌルを呌んでいるわけでないのでPythonを䜿っおいる、ちゃんずlistenシステムコヌルが䜿われおいるかを
先に確認しおみたしょう。

straceで確認。

$ strace python3 silent_server.py

〜省略〜

listen(3, 2) 

listenの第2匕数に、指定したbacklogの倀が蚭定されおいるこずが確認できたした。

たた、䟋えばbacklogのサむズを10にしたりするず、こんな感じになりたす。

listen(3, 10)

サヌバヌプログラムの起動ずbacklogのサむズ確認

では、サヌバヌプログラムを起動したす。

$ python3 silent_server.py 
[2019-07-09 23:50:08] Server startup

この時、backlogに指定した倀が反映されおいるかどうか、ssコマンドで確認しおみたしょう。リッスンしおいる゜ケットの情報を
芋おみたす。

$ ss -tnl
State                 Recv-Q                 Send-Q                                  Local Address:Port                                   Peer Address:Port                 
LISTEN                0                      2                                             0.0.0.0:8080                                        0.0.0.0:*

この「Send-Q」がbacklogの数です。

先ほどず同じように、backlogの数を10にしたりするず、以䞋のようになりたす。

$ ss -tnl
State                 Recv-Q                 Send-Q                                  Local Address:Port                                   Peer Address:Port                 
LISTEN                0                      10                                            0.0.0.0:8080                                        0.0.0.0:*

backlogのサむズが確認できたずころで、プログラム䞭のbacklogは2に戻しお、再床起動しなおしたす。

接続しおみる

このサヌバヌに、接続しおみたす。今回はtelnetを䜿っおみたしょう。「Connected to〜」ず衚瀺されるので、接続できたこずがわかりやすいので。

$ telnet localhost 8080
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.

ssコマンドで芋おみたす。「Recv-Q」は0です。

$ ss -tnl
State                 Recv-Q                 Send-Q                                  Local Address:Port                                   Peer Address:Port                 
LISTEN                0                      2                                             0.0.0.0:8080                                        0.0.0.0:*

接続を3぀増やしおみたす。

$ telnet localhost 8080
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.


$ telnet localhost 8080
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.


$ telnet localhost 8080
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.

蚈4぀のtelnetプロセスが接続しおいる状態です。

ssコマンドで芋おみたす。

$ ss -tnl
State                 Recv-Q                 Send-Q                                  Local Address:Port                                   Peer Address:Port                 
LISTEN                3                      2                                             0.0.0.0:8080                                        0.0.0.0:*

「Recv-Q」が3たで䞊がりたした。3 

※远蚘
この1になる点に぀いお、確認しおみたした。

ssコマンドのRecv-Qが、backlog+1の値まで上がるのはどうして? - CLOVER🍀

ここで、さらに3぀ほどtelnetで接続しおみたす。

$ telnet localhost 8080
Trying 127.0.0.1...


$ telnet localhost 8080
Trying 127.0.0.1...


$ telnet localhost 8080
Trying 127.0.0.1...

いずれも、「Connected to〜」が衚瀺されなくなりたした。

リッスンしおいる゜ケットに察しお、ssコマンドの結果には倉化がありたせん。

$ ss -tnl
State                 Recv-Q                 Send-Q                                  Local Address:Port                                   Peer Address:Port                 
LISTEN                3                      2                                             0.0.0.0:8080                                        0.0.0.0:*

この時、サヌバヌ偎から芋た゜ケットの状態を芋おみたす。「ESTABLISH」が4぀ありたす。

 ss -tn | grep 8080 | grep -vE '8080 +$'
ESTAB      0         0                                       127.0.0.1:8080                                                     127.0.0.1:47220                 
ESTAB      0         0                                       127.0.0.1:8080                                                     127.0.0.1:47214                 
ESTAB      0         0                                       127.0.0.1:8080                                                     127.0.0.1:47218                 
ESTAB      0         0                                       127.0.0.1:8080                                                     127.0.0.1:47222

telnetの動䜜状態ずは䞀臎したす。backlogに入っおいる状態は、接続確立枈みESTABLISHではあるけれど、acceptはただ呌ばれおいない状態、
ず蚀えそうですね。

クラむアント偎の゜ケットから芋おみるず、「ESTABLISH」が4぀、「SYN-SENT」が3぀ありたす。

$ ss -tn | grep 8080 | grep -E '8080 +$'
SYN-SENT   0         1                                       127.0.0.1:47262                                                    127.0.0.1:8080                  
ESTAB      0         0                                       127.0.0.1:47222                                                    127.0.0.1:8080                  
SYN-SENT   0         1                                       127.0.0.1:47264                                                    127.0.0.1:8080                  
ESTAB      0         0                                       127.0.0.1:47220                                                    127.0.0.1:8080                  
SYN-SENT   0         1                                       127.0.0.1:47266                                                    127.0.0.1:8080                  
ESTAB      0         0                                       127.0.0.1:47214                                                    127.0.0.1:8080                  
ESTAB      0         0                                       127.0.0.1:47218                                                    127.0.0.1:8080

ESTABLISHは接続確立、SYN-SENTは接続しにいった偎クラむアント偎から芋おSYNを送ったけれど、サヌバヌ偎からSYNACKが
返っおきおいない状態ですね。

第16回 信頼性のある通信を実現するTCPプロトコル(3) (3/4):基礎から学ぶWindowsネットワーク - @IT

RFC: 793 / Terminology

ちなみに、SYN-SENTの状態のtelnetは、ずヌっず攟っおおくずタむムアりトしたす。

$ telnet localhost 8080
Trying 127.0.0.1...
telnet: Unable to connect to remote host: Connection timed out

接続できおいるものに぀いおは、そのたたです。

$ telnet localhost 8080
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.

SYN-SENTのものは、どうなっおいるのでしょうSYN-SENTなtelnetプロセスがひず぀の状態で、tcpdumpで確認しおみたした。

$ sudo tcpdump -i lo tcp port 8080 -n
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on lo, link-type EN10MB (Ethernet), capture size 262144 bytes
01:36:27.971473 IP 127.0.0.1.49728 > 127.0.0.1.8080: Flags [S], seq 1374582617, win 43690, options [mss 65495,sackOK,TS val 3648443592 ecr 0,nop,wscale 7], length 0
01:36:28.994888 IP 127.0.0.1.49728 > 127.0.0.1.8080: Flags [S], seq 1374582617, win 43690, options [mss 65495,sackOK,TS val 3648444615 ecr 0,nop,wscale 7], length 0
01:36:31.010974 IP 127.0.0.1.49728 > 127.0.0.1.8080: Flags [S], seq 1374582617, win 43690, options [mss 65495,sackOK,TS val 3648446631 ecr 0,nop,wscale 7], length 0
01:36:35.042911 IP 127.0.0.1.49728 > 127.0.0.1.8080: Flags [S], seq 1374582617, win 43690, options [mss 65495,sackOK,TS val 3648450663 ecr 0,nop,wscale 7], length 0
01:36:43.234975 IP 127.0.0.1.49728 > 127.0.0.1.8080: Flags [S], seq 1374582617, win 43690, options [mss 65495,sackOK,TS val 3648458855 ecr 0,nop,wscale 7], length 0
01:36:59.362981 IP 127.0.0.1.49728 > 127.0.0.1.8080: Flags [S], seq 1374582617, win 43690, options [mss 65495,sackOK,TS val 3648474983 ecr 0,nop,wscale 7], length 0
01:37:33.154941 IP 127.0.0.1.49728 > 127.0.0.1.8080: Flags [S], seq 1374582617, win 43690, options [mss 65495,sackOK,TS val 3648508775 ecr 0,nop,wscale 7], length 0

Flagsが「S」なので、SYNを再送しおいたすね 。

なお、先ほどのプログラムではacceptした埌になにもしないのですが、以䞋のようなものに倉えお、リク゚ストを受け付けたらレスポンスを返しお
クラむアントを切断するようにした堎合は、埅たされおいたtelnetプロセスが順次接続されおいきたした。
echo_server.py

import socket
import time

from datetime import datetime

host = '0.0.0.0'
port = 8080
bind_address = (host, port)

backlog_size = 2

with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as server_socket:
    server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    server_socket.bind(bind_address)
    server_socket.listen(backlog_size)

    print('[{}] Server startup'.format(datetime.now().strftime('%Y-%m-%d %H:%M:%S')))

    try:
        while True:
            client_socket, addr = server_socket.accept()

            remote_addr = client_socket.getpeername()
    
            print('[{}] - handle connection, start - {}'.format(datetime.now().strftime('%Y-%m-%d %H:%M:%S'), remote_addr))
    
            with client_socket:
                data = client_socket.recv(1024)

                client_socket.send(b'Reply: ' + data)

                print('[{}] - handle connection, exit - {}'.format(datetime.now().strftime('%Y-%m-%d %H:%M:%S'), remote_addr))

    except KeyboardInterrupt:
        print('[{}] Server stop'.format(datetime.now().strftime('%Y-%m-%d %H:%M:%S')))

再送䞭にサヌバヌ偎がacceptできたり、backlogが空いおいる状態になっおいるず、サヌバヌ偎ず接続できたす、ず。

ずはいえ、Linux的にはここたででも拒吊したこずになっおいお、LINUX_MIB_LISTENOVERFLOWSずいうオヌバヌフロヌを衚す
カりンタヌ、そしお接続をドロップしたこずを衚すLINUX_MIB_LISTENDROPSずいうカりンタヌが䞊がりたす。

これは、/proc/net/netstatのListenOverflows、ListenDropsずいう項目で確認できたす。

$ cat /proc/net/netstat

ですが、catでは芋づらい netstatではキレむに芋れるみたいですが、ssだずちゃんず芋れないです 。

ここたでで、なんずなく挙動はわかりたしたね。

今回の蚭定だず、クラむアント偎の挙動にも䟝存するのでしょうが、すぐに゚ラヌで匟かれるずいうわけではない 

もう1床、listenの説明を芋おみたしょう。

Man page of LISTEN

キュヌがいっぱいの状態で接続芁求が到着するず、クラむアントは ECONNREFUSED ずいう゚ラヌを受け取る。䞋䜍局のプロトコルが再送信をサポヌト しおいれば、芁求は無芖され、これ以降の接続芁求の再送信が成功するかもしれない。

再送信のケヌスを今回は芋た、ずいうこずになりたすね。

backlogのサむズを倧きくする

最埌に、backlogのサむズを倧きくしおプログラムを起動しおみたす。

backlog_size = 1024

ですが、backlogの倀は128で頭打ちになりたす。

$ ss -tnl
State                 Recv-Q                 Send-Q                                  Local Address:Port                                   Peer Address:Port                 
LISTEN                0                      128                                           0.0.0.0:8080                                        0.0.0.0:*

これは、カヌネルパラメヌタヌの「net.core.somaxconn」にbacklogの倀が切り詰められた状態です。

なので、「net.core.somaxconn」の倀を䞊げたす。今回は1024にしおみたしょう。

$ sudo sysctl -w net.core.somaxconn=1024
net.core.somaxconn = 1024

サヌバヌプロセスを再起動するず、今床はbacklogの倀が倧きくなったこずが確認できたす。

$ ss -tnl
State                 Recv-Q                 Send-Q                                  Local Address:Port                                   Peer Address:Port                 
LISTEN                0                      1024                                          0.0.0.0:8080                                        0.0.0.0:*

たずめ

Linuxのbacklogたわりの挙動を調べお、簡単な確認たで行っおみたした。

backlogがacceptされる前のESTABLISHな゜ケットに察するキュヌであるこずや、backlogのサむズはカヌネルパラメヌタヌの圱響を受けるこず、
backlogを超えた接続数を受け取った時はクラむアントが再送したりする、ずいうこずが確認できたした。

その他のTCP゜ケットの接続に関するパラメヌタヌも、ちゃんず芋おいかないずいけない気がしたすね。頑匵りたしょう。

関連しそうなLinuxカヌネルの゜ヌスコヌド。

backlogの長さを蚭定したり、条件刀定したりするずころ。

https://github.com/torvalds/linux/blob/v4.15/net/ipv4/af_inet.c#L232

https://github.com/torvalds/linux/blob/v4.15/include/net/sock.h#L833-L836

backlogがいっぱいの堎合の動䜜。

https://github.com/torvalds/linux/blob/v4.15/net/ipv4/tcp_ipv4.c#L1347-L1348

https://github.com/torvalds/linux/blob/v4.15/net/ipv4/tcp_ipv4.c#L1418-L1424

backlogがsomaxconnより少ない堎合に、切り詰められる郚分。

https://github.com/torvalds/linux/blob/v4.15/net/socket.c#L1480-L1481

ECONNREFUSEDずなる郚分今回は盎接の関連はないずいうか、クラむアント偎の話。

https://github.com/torvalds/linux/blob/v4.15/net/ipv4/tcp_input.c#L3977-L3979

参考

How TCP backlog works in Linux