LoginSignup
3
1

More than 3 years have passed since last update.

Mono Docker で ASP.NET MVC (.NET Framework 4.7.2)

Posted at

これはなに?

.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プロジェクトの雛形に用意されているサンプルにちょっとページを追加して、環境変数などを表示するようにしました。
aspinfo_on_vs.png
(どっかで見たことのあるデザインですが・・・)
このアプリを「発行」して、一式を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 起動用の設定ファイルを作成します。

コンテナ:/etc/supervisor/conf.d/xsp4.conf
[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

うまく動きました :sunny:

docker-compose

これらを踏まえて、MonoとNginxのコンテナを作成します。
ディレクトリ構成はこんな感じ
directory.png

サンプルアプリも含めた一式はこちら

docker-compose.yml

Nginxは alpine ベースを使ってみました。
イメージのサイズは21.2MBとそこそこ軽量です。(1.17.3時点の値)

docker-compose.yml
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で起動します。

mono/Dockerfile
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"]
mono/xsp4.conf
[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 で通信を行います。

nginx/default.conf
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用の設定を追記したパラメータファイル

nginx/fastcgi_params
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 で動いていると確認できます :sunny:
aspinfo_on_mono.png

感想

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

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
1