これはなに?
.NET Framework 4.7.2 でビルドしたASP.NET MVC アプリを、Linux Dockerで動かそうと試した備忘録です。
作戦
- Monoのイメージは公式のものを利用
- ASP.NETアプリは fastcgi-mono-server4 で動かす
- Nginxでホスティングし、fastcgi-mono-server4 とは unix domain socketで通信させる
- fastcgi-mono-server4 は Supervisorでサービス起動する
- NginxとMono(fastcgi-mono-server4)のコンテナは分ける
- Monoのバージョンはlatestではなく、5.18.1.28で検証
- データベースは今は考えない
完成した docker-compose 一式はこちら(Github)
サンプルアプリ
まずは VisualStudio でサンプルアプリを作ります。
ASP.NET MVCプロジェクトの雛形に用意されているサンプルにちょっとページを追加して、環境変数などを表示するようにしました。
(どっかで見たことのあるデザインですが・・・)
このアプリを「発行」して、一式をLinuxサーバに転送します。
Mono Dockerイメージの調査
公式のDockerイメージがナンボのもんか知るためにシェルに入って調査します。
※サンプルアプリは、/home/mono-docker/app/aspinfo に配置しています
# docker run -v /home/mono-docker/app:/home/monoapp -p 9000:9000 -i -t mono:5.18.1.28 /bin/bash
xsp4
MonoのWebサーバ (xsp と fastcgi-mono-server)は含まれていないようなので、インストールします。
ついでにSupervisor と vim も入れます。
# apt update
# apt -y install mono-xsp4 mono-fastcgi-server4 supervisor vim
簡易サーバ xsp を使ってアプリの動作を確認します。
# cd /home/monoapp/aspinfo
# xsp4
xsp4
Listening on address: 0.0.0.0
Root directory: /home/app/aspinfo
Listening on port: 9000 (non-secure)
Hit Return to stop the server.
この状態でLAN内のWebブラウザから http://Linuxホスト:9000
を叩いてページが出ることを確認します。
Supervisor
次にSupervisorの設定と起動の確認を行います。
実はここで大ハマリしました。
Nginxとunix socket通信を行うには、お互いの実行ユーザがsocketに対して書き込み権限がある必要があります。
普通にSupervisorからfastcgiを起動すると、root所有の755でsocketが作成されるため、Nginxが通信できなくなります。
socketファイルは、DockerfileのCMDで Supervisorを起動したあとに作成されるため、後からパーミッションを変更することもできません。
PHP-FPMのように、socketファイルのパーミッションを指定できればよいのですが、fastcgi-mono-serverにはそんな機能が無いのです。
仕方ないので、socketが作られるディレクトリを777で作成し、Nginxの実行ユーザと同じIdを持つユーザを作成し、fastcgi-mono-server はこのユーザで起動するようにします。
※Nginxのコンテナはいじりたくないので、Mono側がNginxのユーザに合わせるようにしています
Nginx の Docker は、ユーザid が "101"の"nginx"ユーザで実行されるようです。
Since 1.17.0, both alpine- and debian-based images variants use the same user and group ids to drop the privileges for worker processes:
$ id
uid=101(nginx) gid=101(nginx) groups=101(nginx)
なのでこれと同じユーザを作ります。
# useradd -u 101 -U -s /sbin/nologin -d /home/monoapp -m nginx
また、socketファイル用ディレクトリを777で作成します。
# mkdir -p /var/run/xsp4
# chmod 777 /var/run/xsp4
これを受けて、fastcgi-mono-server 起動用の設定ファイルを作成します。
[supervisord]
nodaemon=true
[program:xsp4]
directory=/home/monoapp/aspinfo
environment=MONO_IOMAP=all
command=fastcgi-mono-server4 /applications=/:/home/monoapp/aspinfo/ /filename=/var/run/xsp4/mono-xsp4.socket /socket=unix
user=nginx
autostart=true
autorestart=true
Supervisorの試運転
# supervisord &
[1] 775
/usr/lib/python2.7/dist-packages/supervisor/options.py:298: UserWarning: Supervisord is running as root and it is
searching for its configuration file in default locations (including its current working directory); you probably
want to specify a "-c" argument specifying an absolute path to a configuration file for improved security.
'Supervisord is running as root and it is searching '
2019-08-16 10:17:06,882 CRIT Supervisor running as root (no user in config file)
2019-08-16 10:17:06,884 INFO Included extra file "/etc/supervisor/conf.d/xsp4.conf" during parsing
2019-08-16 10:17:06,916 INFO RPC interface 'supervisor' initialized
2019-08-16 10:17:06,918 CRIT Server 'unix_http_server' running without any HTTP authentication checking
2019-08-16 10:17:06,920 INFO supervisord started with pid 775
2019-08-16 10:17:07,933 INFO spawned: 'xsp4' with pid 778
2019-08-16 10:17:08,942 INFO success: xsp4 entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)
# ps axu
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.0 0.3 18152 3248 pts/0 Ss 09:29 0:00 /bin/bash
root 775 0.7 1.9 57920 20212 pts/0 S 10:17 0:00 /usr/bin/python /usr/bin/supervisord
nginx 778 0.5 2.8 293076 28932 pts/0 Sl 10:17 0:00 /usr/bin/mono /usr/lib/mono/4.5/fastcgi-mono-server4.ex
root 783 0.0 0.2 36636 2856 pts/0 R+ 10:19 0:00 ps axu
うまく動きました
docker-compose
これらを踏まえて、MonoとNginxのコンテナを作成します。
ディレクトリ構成はこんな感じ
docker-compose.yml
Nginxは alpine ベースを使ってみました。
イメージのサイズは21.2MBとそこそこ軽量です。(1.17.3時点の値)
version: '3'
services:
mono:
build: ./mono
volumes:
- ./app:/home/monoapp
- xsp4socket:/var/run/xsp4
nginx:
image: nginx:alpine
ports:
- 80:80
depends_on:
- mono
volumes:
- ./nginx/default.conf:/etc/nginx/conf.d/default.conf
- ./nginx/fastcgi_params:/etc/nginx/fastcgi_params
- ./app:/home/monoapp
- xsp4socket:/var/run/xsp4
volumes:
xsp4socket:
mono
調査した操作をDockerfileに記述し、イメージを作成します。
Supervisorの設定ファイルをコピーし、CMDで起動します。
FROM mono:5.18.1.28
RUN apt update \
&& apt -y install mono-xsp4 mono-fastcgi-server4 supervisor \
&& useradd -u 101 -U -s /sbin/nologin -d /home/monoapp -m nginx \
&& mkdir -p /var/run/xsp4 \
&& chmod 777 /var/run/xsp4 \
&& mkdir -p /etc/mono/registry \
&& chmod 777 /etc/mono/registry
COPY ./xsp4.conf /etc/supervisor/conf.d/
CMD ["/usr/bin/supervisord"]
[supervisord]
nodaemon=true
[program:xsp4]
directory=/home/monoapp/aspinfo
environment=MONO_IOMAP=all
command=fastcgi-mono-server4 /applications=/:/home/monoapp/aspinfo/ /filename=/var/run/xsp4/mono-xsp4.socket /socket=unix
user=nginx
autostart=true
autorestart=true
Nginx
サイトの設定ファイル。
fastcgi-mono-server と unix domain socket で通信を行います。
server {
listen 80;
root /home/monoapp/aspinfo;
server_name _;
location / {
index index.html index.htm default.aspx Default.aspx;
fastcgi_pass unix:/var/run/xsp4/mono-xsp4.socket;
include /etc/nginx/fastcgi_params;
client_max_body_size 10M; # upload limit
}
# favicon.ico
location = /favicon.ico {
log_not_found off;
access_log off;
}
}
fastcgi-mono-server用の設定を追記したパラメータファイル
fastcgi_param QUERY_STRING $query_string;
fastcgi_param REQUEST_METHOD $request_method;
fastcgi_param CONTENT_TYPE $content_type;
fastcgi_param CONTENT_LENGTH $content_length;
fastcgi_param SCRIPT_NAME $fastcgi_script_name;
fastcgi_param REQUEST_URI $request_uri;
fastcgi_param DOCUMENT_URI $document_uri;
fastcgi_param DOCUMENT_ROOT $document_root;
fastcgi_param SERVER_PROTOCOL $server_protocol;
fastcgi_param HTTPS $https if_not_empty;
fastcgi_param GATEWAY_INTERFACE CGI/1.1;
fastcgi_param SERVER_SOFTWARE nginx/$nginx_version;
fastcgi_param REMOTE_ADDR $remote_addr;
fastcgi_param REMOTE_PORT $remote_port;
fastcgi_param SERVER_ADDR $server_addr;
fastcgi_param SERVER_PORT $server_port;
fastcgi_param SERVER_NAME $server_name;
# PHP only, required if PHP was built with --enable-force-cgi-redirect
fastcgi_param REDIRECT_STATUS 200;
# for Mono configration
fastcgi_param PATH_INFO "";
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
起動
# docker-compose up -d --build
うまく行けば、サンプリアプリが動きます。 ちゃんと Nginx経由 / unix OS で動いていると確認できます

感想
- ニッチなソリューションなため、情報が少なくて困りました
- とっとと.NET Coreに移行したいです