ラズパイ上のUbuntuでnginx-proxyを走らせて複数のウェブアプリをホストする

2019年12月25日 Yasu

Dockerおよびdocker-composeで構成されたコンテナ上で実行される、複数のウェブアプリ等を一台のPC上で実行したい。例えばこんなイメージ:

http://blog.pi4.local/ => /home/ubuntu/blog/docker-compose.yml
http://app1.pi4.local/ => /home/ubuntu/app1/docker-compose.yml
http://app2.pi4.local/ => /home/ubuntu/app2/docker-compose.yml

nginxをリバースプロキシとして使うと実現できるのですが調べてみると設定が難しそう…

手軽な方法を探していたところ、こちらの記事およびその元となったnginx-proxyというリポジトリがありました。詳しい仕組みは解説に書いてあります。かんたんに説明すると、nginx-proxy自身がDockerコンテナで構成されていて、環境変数にVIRTUAL_HOST=を持つDockerコンテナを、同じDockerコンテナネットワーク上に見つけると、自動的にnginxのリバースプロキシの設定を作成し、要求されたサブドメインへのアクセスを適当なコンテナへ誘導してくれます。ローカル環境を汚さずに立ち上げることができるので、もう全てがDockerで支配されて良いんじゃないか〜という感じです。

実際に試したところ上手く行ったので記録しておきます。

nginx-proxyコンテナのセットアップ

今回もRaspberry Pi 4上で64bit版のarm64向けUbuntu 18.04.3を使っています。最新のDockerおよびdocker-composeがインストール済みです。詳細は以前の記事を参照してください。

Dockerネットワークの作成

nginx-proxyがコンテナの追加を監視するDockerコンテナ・ネットワークを作成します。

$ docker network create nginx-proxy

arm64に対応したDockerfileの準備

nginx-proxyのリポジトリではarm64用のDockerfileが提供されていないため、既存のx86用のDockerfileを基にしました。適当なディレクトリ、ここでは/home/ubuntu/nginx-proxy/に以下のDockerfileを作成します。

FROM arm64v8/nginx

LABEL maintainer="Jason Wilder mail@jasonwilder.com, Ondřej Záruba <info@zaruba-ondrej.cz> (https://zaruba-ondrej.cz)"

# Install wget and install/updates certificates
RUN apt-get update \
 && apt-get install -y -q --no-install-recommends \
    ca-certificates \
    wget \
    git \
 && apt-get clean \
 && rm -r /var/lib/apt/lists/*


# Configure Nginx and apply fix for very long server names
RUN echo "daemon off;" >> /etc/nginx/nginx.conf \
 && sed -i 's/^http {/&\n    server_names_hash_bucket_size 128;/g' /etc/nginx/nginx.conf


# Install Forego 変更点
RUN wget --quiet https://bin.equinox.io/c/ekMN3bCZFUn/forego-stable-linux-arm64.tgz && \
	tar xvf forego-stable-linux-arm64.tgz -C /usr/local/bin && \
	chmod u+x /usr/local/bin/forego

ENV DOCKER_GEN_VERSION 0.7.4

RUN wget --quiet https://github.com/jwilder/docker-gen/releases/download/$DOCKER_GEN_VERSION/docker-gen-alpine-linux-armhf-$DO
CKER_GEN_VERSION.tar.gz \
 && tar -C /usr/local/bin -xvzf docker-gen-alpine-linux-armhf-$DOCKER_GEN_VERSION.tar.gz \
 && rm /docker-gen-alpine-linux-armhf-$DOCKER_GEN_VERSION.tar.gz


ENV NGINX_PROXY_VERSION "0.6.0"
RUN git clone --branch ${NGINX_PROXY_VERSION} https://github.com/jwilder/nginx-proxy.git /app

WORKDIR /app/

ENV DOCKER_HOST unix:///tmp/docker.sock

VOLUME ["/etc/nginx/certs", "/etc/nginx/dhparam"]

ENTRYPOINT ["/app/docker-entrypoint.sh"]
CMD ["forego", "start", "-r"]

変更点は、Foregoのarm64版を公式サイトからダウンロードして展開するようになっています。

docker-compose.ymlの準備

同じディレクトリにdocker-compose.ymlを作成します。ここでは先のDockerファイルをビルドすることによりnginx-proxyを立ち上げる設定に変更しています。

version: "3"
services:
  nginx-proxy:
    build: .
    container_name: nginx-proxy
    restart: always
    ports:
      - "80:80"
    volumes:
      - /var/run/docker.sock:/tmp/docker.sock:ro

networks:
  default:
    external:
      name: nginx-proxy

準備ができたら

$ docker-compose up -d

としてコンテナを起動し、以下のようになっていればOKです。

$ docker ps
CONTAINER ID        IMAGE                     COMMAND                  CREATED             STATUS              PORTS                NAMES
1e102858616b        nginx-proxy_nginx-proxy   "/app/docker-entrypo…"   7 hours ago         Up 10 minutes       0.0.0.0:80->80/tcp   nginx-proxy

ウェブアプリケーション側の設定

Dockerの公式のクイックスタートガイドに、WordPressを立ち上げるdocker-compose.ymlがあります。これに少し変更を加えて立ち上げる例を用意してみました。ここでは適当なディレクトリ/home/ubuntu/blog/に下記のようなdocker-compose.ymlを作成します。

version: '2'
services:
  db:
    image: mariadb # mysqlのarm64用のイメージが用意されていないのでmariadbを使用
    volumes:
      - "./.data/db:/var/lib/mysql"
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: wordpress
      MYSQL_DATABASE: wordpress
      MYSQL_USER: wordpress
      MYSQL_PASSWORD: wordpress

  wordpress:
    depends_on:
      - db
    image: wordpress:latest
    links:
      - db
        # 8000番ポートにマッピングする必要がないのでコメントアウト
        #    ports:
        #      - "8000:80"
    expose: # 80番ポートにアクセスしたいので公開しておきます
      - 80
    restart: always
    environment:
      WORDPRESS_DB_HOST: db:3306
      WORDPRESS_DB_PASSWORD: wordpress
      VIRTUAL_HOST: blog.pi4.local # 使用するサブドメイン名に変更します

# nginx-proxyが監視しているDockerネットワークに参加
networks:
  default:
    external:
      name: nginx-proxy

起動してみて以下のようになっていればOKです。

$ docker-compose up -d
$ docker ps
CONTAINER ID        IMAGE                     COMMAND                  CREATED             STATUS              PORTS                NAMES
1e102858616b        nginx-proxy_nginx-proxy   "/app/docker-entrypo…"   7 hours ago         Up 10 minutes       0.0.0.0:80->80/tcp   nginx-proxy
d55553138a83        wordpress:latest          "docker-entrypoint.s…"   8 hours ago         Up 10 minutes       80/tcp               wp-test_wordpress_1
cf647e31827f        mariadb                   "docker-entrypoint.s…"   8 hours ago         Up 10 minutes       3306/tcp             wp-test_db_1

自分でDNSの設定を変更することができるのであればblog.pi4.localをDNSに登録します。とりあえずテストであれば、クライアント側のPC上で/etc/hostsにこのホスト名とIPアドレスの関連をセットします。

クライアント側のブラウザからhttp://blog.pi4.localにアクセスしていつもの画面が現れたらOKです。

WordPressのインストール画面
WordPressのインストール画面

mDNSで任意のサブドメインもアナウンス

mDNSはサブドメインの面倒をみてくれない仕様のようで、きちんとしたDNSを立てるか/etc/hostsに登録が必要だということに気がつかずしばし悩みました。できればmDNSの設定でなんとかならないかなと調べたところ、どうも単純ではない様子ですが、こちらのissueを参考に、とりあえず目的が達成できるようになったのでメモしておきます。

まずは固定IPアドレスに変更

Ubuntu 18.04になって(17.10以降のようです)IPアドレスの変更方法が大分変わったようで、調べてみると/etc/netplan/50-cloud-init.yamlを以下のように編集します。ここで注意が必要なのは、ネットワークインターフェイス名がenp0s3となっている例が多くありますが、この部分はシステムが実際に認識しているネットワークインターフェイス(ここではeth0)をセット必要があります。

# This file is generated from information provided by
# the datasource.  Changes to it will not persist across an instance.
# To disable cloud-init's network configuration capabilities, write a file
# /etc/cloud/cloud.cfg.d/99-disable-network-config.cfg with the following:
# network: {config: disabled}
network:
    version: 2
    renderer: networkd
    ethernets:
        eth0: # `ip address`で設定したいネットワークインターフェイスを確認
            dhcp4: no
            dhcp6: no
            addresses: [192.168.1.33/24]
            gateway4: 192.168.1.1
            nameservers:
                addresses: [192.168.1.1, 8.8.8.8]

サブドメインを登録するサービスの定義

avashi-publishというコマンドを使うと、サブドメインをアナウンスできるようなります。しかしこれをシステムリブート時に毎回行うのも大変です。このコメントに記載されたスクリプトを使うと、システムサービスとしてサブドメインの登録を自動化できます。

下記のような/etc/systemd/system/avahi-subdomain@.serviceという新しいファイルを作成します。

[Unit]
Description=Publish %I.%H.local via mdns

[Service]
Type=simple
# ExecStart=/bin/bash -c "/usr/bin/avahi-publish -a -R %I.$(avahi-resolve -4 -n %H.local)"
ExecStart=/bin/bash -c "/usr/bin/avahi-publish -a -R %I.%H.local $(ip -f inet address show dev eth0 | grep 'inet' | cut -d/ -f1 | awk '{print $2}')"

[Install]
WantedBy=multi-user.target

今回はDockerを使っているため自ホスト名に対するIPアドレスが複数存在し、avahi-resolveの結果が必ずしも期待する外向きのIPアドレスとはならないため、eth0に割り当てられたIPアドレスを取得するよう変更してあります。

サービスの登録

サービスの登録は次のコマンドとなります。

ubuntu@pi4:~$ sudo systemctl enable --now avahi-subdomain@blog.service
Created symlink /etc/systemd/system/multi-user.target.wants/avahi-subdomain@blog.service → /etc/systemd/system/avahi-subdomain@.service.

ここで@マークに続いてサブドメイン名.service、今回の例ではblog.serviceを指定しています。クライアント側のブラウザからhttp://blog.pi4.localにアクセスしてWordPressの初期画面が表示されれば成功です!サービスとして登録されていますので、次回再起動したときも自動的にサブドメインの登録が行われます。

まとめ

ウェブアプリケーション側でのdocker-compose.ymlにおいて、以下の設定をすれば自動的に追加されるハズ!

  • コンテナ側の待ち受けポートも80番になるのでexpose: 80とする
  • 環境変数VIRTUAL_HOST: blog.pi4.localによってサブドメイン名が決定される
  • networksnginx-proxyと同じコンテナネットワークに参加する
相場のお問い合わせはお電話・メール・LINEにてお気軽にどうぞ!
 tel:0120-41-1578
 email:info@officeiko.co.jp
 LINE ID:@oue6072d