Cloud Penguins

Flying penguins in the cloud.

Synology上のDockerでPort80,443が使えない件をなんとかする

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をここに持ってくることも出来てしまう。便利。

github.com

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)の画面が表示されることが分かるだろう。

単に静的なコンテンツをホストするだけであれば、パッケージでいくつかのウェブサーバーが提供されているのでそれを使えば良い。 f:id:jaco-m:20210818201241p:plain

しかし、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

無事ブラウザからアクセス出来た。

f:id:jaco-m:20210818210019p:plain

なお、この設定はDSM 6.2系で確認している。最新の7系ではどうなるかはまだ未確認。