Unix Domain Socket

## Unix Domain Socketとは
Unix Domain Socketとは、プロセス間通信のための一つの手段です。

通常プロセス間通信を行う際には、IPネットワークを使用することが一般的ですが、同一ホスト内での通信はUnix Domain Socketを使う方が効率的です。
TCP/IPなどを使った場合、その分ネットワークスタックのレイヤーを通す必要がありますがUnix Domain Socketの場合はその処理が不要になるので効率的ということができます。

Unix Domain Socketはファイルシステム上にファイルを作ることによって通信を可能にしているので、ホストが異なる場合は使うことができません。
(ファイルは`/var/run/`もしくは`/run/`配下に作成されます。)

## 使用例
使ってみるのが一番だと思うので、nginx + pumaの構成で試してみます。
都合が良いのでisucon13の環境を使います。

https://github.com/matsuu/aws-isucon?tab=readme-ov-file#ami

初期実装だとTCPを使ってpumaとnginxを繋いでいることがわかります。

- Ruby実装のunit file
```sh
# /etc/systemd/system/isupipe-ruby.service
[Unit]
Description=isupipe-ruby
After=syslog.target
After=mysql.service
Requires=mysql.service

[Service]
WorkingDirectory=/home/isucon/webapp/ruby
Environment=RUBY_YJIT_ENABLE=1
EnvironmentFile=/home/isucon/env.sh

User=isucon
Group=isucon
ExecStart=/home/isucon/.x bundle exec puma --bind tcp://0.0.0.0:8080 --workers 8 --threads 0:8 --environment production
ExecStop=/bin/kill -s QUIT $MAINPID

Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target
```

- nginx
```conf
# ...
server {
  listen 443 ssl;
  server_name u.isucon.local;
  server_name *.u.isucon.local;

  ssl_certificate     /etc/nginx/tls/_.u.isucon.local.crt;
  ssl_certificate_key /etc/nginx/tls/_.u.isucon.local.key;

  ssl_protocols TLSv1.3;
  ssl_prefer_server_ciphers off;

  client_max_body_size 10m;
  root /home/isucon/webapp/public/;
  location / {
    try_files $uri /index.html;
  }
  location /api {
    proxy_set_header Host $host;
    proxy_pass http://localhost:8080;
  }
}
```

これをUnix Domain Socketを使って繋ぐようにします。
まずはRuby実装のunit fileを変更します。

```diff
# /etc/systemd/system/isupipe-ruby.service
[Unit]
Description=isupipe-ruby
After=syslog.target
After=mysql.service
Requires=mysql.service

[Service]
+RuntimeDirectory=isupipe
WorkingDirectory=/home/isucon/webapp/ruby
Environment=RUBY_YJIT_ENABLE=1
EnvironmentFile=/home/isucon/env.sh

User=isucon
Group=isucon
-ExecStart=/home/isucon/.x bundle exec puma --bind tcp://0.0.0.0:8080 --workers 8 --threads 0:8 --environment production
+ExecStart=/home/isucon/.x bundle exec puma --bind unix:///run/isupipe/puma.sock --workers 8 --threads 0:8 --environment production
ExecStop=/bin/kill -s QUIT $MAINPID

Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target
```
この状態で、プロセスをrestartするとUnix Domain Socketを使ってpumaが立ち上がっていることがわかります。
```sh
$ sudo ss -lnpx | grep puma
u_str LISTEN 0      1024                       /run/isupipe/puma.sock 58753            * 0    users:(("bundle",pid=3883,fd=5),("bundle",pid=3882,fd=5),("bundle",pid=3881,fd=5),("bundle",pid=3880,fd=5),("bundle",pid=3879,fd=5),("bundle",pid=3878,fd=5),("bundle",pid=3877,fd=5),("bundle",pid=3875,fd=5),("bundle",pid=3874,fd=5))
```
これでpumaに対してUnix Domain Socket越しに通信ができるようになったので、nginxでそのファイルを指定して通信するようにします。

```diff
# ...
server {
  listen 443 ssl;
  server_name u.isucon.local;
  server_name *.u.isucon.local;

  ssl_certificate     /etc/nginx/tls/_.u.isucon.local.crt;
  ssl_certificate_key /etc/nginx/tls/_.u.isucon.local.key;

  ssl_protocols TLSv1.3;
  ssl_prefer_server_ciphers off;

  client_max_body_size 10m;
  root /home/isucon/webapp/public/;
  location / {
    try_files $uri /index.html;
  }
  location /api {
    proxy_set_header Host $host;
-    proxy_pass http://localhost:8080;
+    proxy_pass http://unix:/run/isupipe/puma.sock;
  }
}
```

## 参考

https://tatsu-zine.com/books/superuser-linux-system

https://www.oreilly.co.jp/books/9784814400072/