本記事は東京学芸大学 櫨山研究室 Advent Calendar 2020の16日目の記事になります.
はじめに
本記事はPythonのWebフレームワークであるflaskで作成したアプリケーションをuWSGIサーバを使って動作させる内容を通してUnix Domain Socket通信について学ぶという内容になります.
Unix Domain Socketとは
Unix Domain SocketはPOSIX系のOSに搭載されている機能でTCP/UDPによるソケット通信とは異なり,カーネル内部で完結する高速なネットワークインタフェースを作成します.
カーネル内部で完結するため外部のコンピュータとは接続することはできません.
flaskのアプリケーションの作成
エンドポイント/
でアクセスしてHello, World
とだけ返す簡単なアプリケーションを作成します.
from flask import Flask
app = Flask(__name__)
@app.route("/")
def index():
return "Hello World"
if __name__ == "__main__":
app.run(host="0.0.0.0", debug=True)
そして以下のコマンドを実行することでアプリケーションが起動します.
この時,起動には開発用の組み込みサーバが立ち上がります.
python app.py
http://localhost:5000/
にアクセスしてHello, World
と表示されれば成功です.
uWSGIを使って動かす
先ほどの例では開発用の組み込みサーバを使用していました.
PythonにはアプリケーションサーバとしてuWSGI
があります.
uWSGI
ではUnix Domain Socketでの通信がサポートされています.
まずはuWSGI
を使って先ほどのアプリケーションを動作させてみます.
pip install uWSGI
from flask import Flask
app = Flask(__name__)
@app.route("/")
def index():
return "Hello World"
if __name__ == "__main__":
app.run()
uWSGI
で先ほどのアプリケーションを起動するには以下のコマンドを入力します.
uwsgi --http=0.0.0.0:4000 --wsgi-file=app.py --callable=app
http://localhost:4000/
にアクセスしてHello, World
と表示されれば成功です.
uwsgi.ini
という設定ファイルを作成することでより短いコマンドで起動することも可能です.
[uwsgi]
wsgi-file=app.py
callable=app
http=0.0.0.0:4000
uwsgi uwsgi.ini
Nginxの導入
さてここからが本題のUnix Domina Socketでのアクセスです.これまでの起動方法ではHTTP通信(TCPソケット)でのアクセスでした.
実際に運用する場合は同一サーバで複数のアプリケーションを動作させることを考慮しNginxをリバースプロキシとする場合が多いでしょう.
この時,同一サーバ内の通信であればUnix Domain Socketを使ってHTTP通信よりも高速にアクセスすることができます.
今回は便宜的にDockerを用いて動作させてみます.
uwsgi.ini
の編集
uwsgi.ini
を編集してUnix Domain Socketで通信するようにします.
Unix Domain Socketではファイルシステム上の指定した位置にソケットファイルができます.
クライアントはソケットファイルにアクセスを試みます.
つまりIPアドレスとポート番号ではなくファイルパスによって通信相手を決めます.
今回は/var/app/app.sock
というソケットファイルを作成するようにします.
[uwsgi]
wsgi-file=app.py
callable=app
http=0.0.0.0:4000
# 追記
socket=/var/app/app.sock
Docker環境の準備
まずは先ほどのflaskのアプリケーションを動作させる環境を用意するDockerfile
を用意します.
FROM python:3.7
ENV LANG C.UTF-8
ENV TZ Asia/Tokyo
WORKDIR /app
ADD app.py ./
ADD uwsgi.ini ./
RUN pip install flask uWSGI
CMD ["uwsgi", "uwsgi.ini"]
nginx
も動かしたいのでdocker-compose.yml
を用意します.
version: '3.7'
services:
app:
container_name: uds_app
build:
context: .
dockerfile: ./Dockerfile
volumes:
- ./tmp/:/var/app/
tty: true
nginx:
container_name: uds_nginx
image: nginx
volumes:
- ./tmp:/var/app/
- ./nginx.conf:/etc/nginx/conf.d/default.conf
ports:
- 80:80
tty: true
ここで重要なのはホストマシン上の./tmp
ディレクトリをflaskアプリケーションコンテナとNginxコンテナの両方と共有していることです.
- ホストマシン上の
./tmp
ディレクトリ ←→ flaskアプリケーションコンテナの/var/app
ディレクトリ - ホストマシン上の
./tmp
ディレクトリ ←→ Nginxコンテナの/var/app
ディレクトリ
これによりNginxコンテナからもソケットファイルを参照できます.
Nginxの設定ファイルを記述し/
へのアクセスをflaskのアプリケーションを呼び出すようにします.
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name _;
location / {
include uwsgi_params;
uwsgi_pass unix:/var/app/app.sock;
}
error_page 404 /404.html;
location = /404.html {
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
}
}
docker-compose up
コマンドで起動してみましょう.
docker-compose up
http://localhost/
にアクセスして先ほどと同様にHello, World
の文字列が確認できれば成功です.
おわりに
本記事ではflaskで作成したアプリケーションにリバースプロキシのNginxからUnix Domain Socketで通信するということを扱いました.
TCP通信と異なりファイルパスによって通信相手を決めるという部分が伝わればと思います.
また余裕があれば性能も調査してきたいですね.