TL;DR
SynologyでPort 80や443を使うコンテナ動かしたければ、 システムのnginxをリバースプロキシとして使おう。
/etc/nginx/sites-enabled
に nginxのVirtual Hostの設定をぶっ込んでリロードすれば行ける
自宅サーバーとしてSynology便利だけど・・・
みんな大好きSynologyのNAS。
一般家庭でも手の届きやすい金額で入手できる上に、豊富なパッケージにより単なるネットワークストレージだけでなく、サーバーとしての機能も持たせられる。メディアサーバーにもできるしウェブサーバーにも出来る。何ならLDAPサーバーやActiveDirectoryも出来ちゃう。パッケージにないものであれば、ハイパーバイザも提供しているのでVMで建てることもできるし、Dockerパッケージもあるのでコンテナとして立ち上げてしまえば実質何もできちゃう。マジ便利。
自宅で勉強がてらKubernetesクラスタを立ち上げているような人であれば、有志によりSynology CSIが提供されているのでPVをここに持ってくることも出来てしまう。便利。
Dockerでウェブサーバーが立ち上げられないぞ・・・!
じゃあDockerで何でも立ち上げられるんだね、やったー! ということで、Synology上のDockerでnginxを立ち上げようとすると、こういうエラーになってしまい立ち上げられない。
jacopen@synology:~$ sudo docker run -p 80:80 nginx docker: Error response from daemon: driver failed programming external connectivity on endpoint epic_turing (3f98a359a914e07caa28b26ec4900650668df4eeecdefd28fca18be4a799e974): Error starting userland proxy: listen tcp 0.0.0.0:80: bind: address already in use. ERRO[0001] error waiting for container: context canceled
tcp 0.0.0.0:80: bind: address already in use.
はい。80番や443番は既にシステムによって使われてしまっているため、Dockerから叩けないのである。実際SynologyのIPをブラウザで叩くと、5000番にリダイレクトされてDSM(DiskStation Manager)の画面が表示されることが分かるだろう。
単に静的なコンテンツをホストするだけであれば、パッケージでいくつかのウェブサーバーが提供されているのでそれを使えば良い。
しかし、Dockerで立ち上げてたウェブアプリに対してSynologyのIP経由で80や443経由でアクセスさせたいというケースも多いだろう。実際自分も、S3互換のオブジェクトストレージであるMinioや、コンテナレジストリであるHarborをSynology上に立ち上げようとしてこの問題に当たってしまった。
Synology上の80や443は誰が使っているのか
じゃあこのSynology上の80や443は誰が使っているのか。SynologyにSSHでログインしてnetstatしてみるとこう出る。
$ sudo netstat -anp | grep -e "0.0.0.0:80" -e "0.0.0.0:443" tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN 13584/nginx: master tcp 0 0 0.0.0.0:443 0.0.0.0:* LISTEN 13584/nginx: master
どうやらnginxが立ち上がっているようだ。これはWebサーバーのパッケージを入れなくても、必ず立ち上がっている。
またDSMのデフォルトポートである5000や5001も、nginxを経由していることが分かる
$ sudo netstat -anp | grep -e "0.0.0.0:5000" tcp 0 0 0.0.0.0:5000 0.0.0.0:* LISTEN 13584/nginx: master
nginxに任意の設定を食わせる
これらの設定ファイルは、/etc/nginx
に存在する。 /etc/nginx/nginx.conf
を親として、 /etc/nginx/app.d
や /etc/nginx/conf.d
に個別の設定が格納されている。DSM自体や、パッケージでインストールした各アプリケーションへのルーティングも、app.dやconf.dに格納されるようになっている。
/etc/nginx/nginx.conf
を開いてみると、以下のような記述があることが分かる。
include conf.d/http.*.conf; include app.d/server.*.conf; include sites-enabled/*;
そう、 nginxがこれらのファイルをincludeしてくれるのである。なので、nginxをリバースプロキシとすれば任意のアプリに対してルーティングを設定出来るというわけだ。
ただ、app.d配下に関してはDSMをアップグレードすると初期化されて消えてしまう。そこで、sites-enabled内に以下のようなファイルを設置した。
server { server_name minio.udcp.run; listen 80; access_log /var/log/nginx/concourse.access.log; location / { proxy_set_header Host $http_host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; client_max_body_size 100M; proxy_pass http://localhost:9000/; } }
DockerでPort 9000でMinioを立ち上げている。minio.udcp.runへのリクエストがあった場合は、そこへのリバースプロキシとして機能するという設定だ。
コンフィグファイルの作成が終わったら、以下のコマンドでnginxをリロードする。
sudo synoservice --reload nginx
無事ブラウザからアクセス出来た。
なお、この設定はDSM 6.2系で確認している。最新の7系ではどうなるかはまだ未確認。