Docker + Traefik でセルフホストして遊んだ記録 準備編

Docker + Traefik でセルフホストして遊んだ。今回は準備編。

きっかけ

今まで YunoHost という初心者向けのセルフホスト特化の OS を使って色々立てていたが、YunoHost 向けにカスタムされたアプリ しかホストできない制約のため、飽きてきていた。少し前からウォッチしている r/selfhosted で発見した面白いサービスを試すため、YunoHost を卒業することにした。

VPS の選定

YunoHost は Linode の Shared CPU Plan の RAM 2GB で動かしていたが、コスト削減のために見直すことに。 要件としては

  • RAM 2 GB 以上
  • 日本からのレイテンシが 100 ms 以下
  • ディスクが SATA SSD 及び NVMe SSD

の2点のみで、できるだけ安価なものを絞った。

プロバイダー vCPU RAM ディスク容量 リージョン 価格 (月) 備考
Linode 1 vCPU 2 GB 50 GB 東京 $10 -
OVH (Starter) 1 vCPU 2 GB 20 GB シンガポール $3.5 -
OVH (Value) 1 vCPU 2 GB 40 GB シンガポール $6 -
VirMach 2 vCPU 2 GB 50 GB 東京 $10 初年度のみ $60/年

ディスク容量が少々不安だが、OVH の Starter プランに決定。理由はもちろん価格である。ディスク容量が足りなくなってきたら Value プランに変更すればいいだろうということで、とりあえず契約。

今まで使ってきた Vultr や Linode などと比べて、購入から実際に使用できるようになるまで少し時間がかかった。具体的には覚えていないが、メールを遡ってみると PayPal のレシートが届いてから OVH からログイン情報が届くまで20分程度かかっている。

構成

OS は Debian で、サービスはすべて Docker で運用し、リバースプロキシは Traefik を採用。証明書の発行は Cloudflare で行う。また、各イメージは Watchtower で自動的にアップデートし、Gotify に通知する。

基本的には BaptisteBdn/docker-selfhosted-apps を参考にした。

Docker の導入

公式ドキュメント に従ってインストールする。Docker のバージョンアップに伴って Docker Compose のインストール方法やコマンドが変わっていた。

Traefik (+ socket-proxy) の導入

ファイル構成

1
2
3
4
5
6
7
8
.
|-- .env
|-- docker-compose.yml
|-- ssl/
|   |-- tls-certs.yml
|   |-- <ドメイン名>.key
|   `-- <ドメイン名>.pem
`-- traefik.yml

ファイルの作成

BaptisteBdn/docker-selfhosted-apps を参考にし、各ファイルを作成する。

  • docker-compose.yml
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    
    version: "3"
    
    networks:
    proxy:
        external: true
    
    services:
    traefik:
        # The official v2 Traefik docker image
        image: traefik:latest
        container_name: traefik
        restart: unless-stopped
        depends_on:
        - socket-proxy
        ports:
        # The HTTP port
        - "80:80"
        - "443:443"
        networks:
        - proxy
        labels:
        - "traefik.enable=true"
    
        # global redirect to https
        - "traefik.http.routers.http-catchall.rule=hostregexp(`{host:.+}`)"
        - "traefik.http.routers.http-catchall.entrypoints=http"
        - "traefik.http.routers.http-catchall.middlewares=redirect-to-https"
    
        # middleware redirect
        - "traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https"
        - "traefik.http.middlewares.redirect-to-https.redirectscheme.permanent=true"
    
        # Watchtower update
        - "com.centurylinklabs.watchtower.enable=true"
    
        volumes:
        # So that Traefik can listen to the Docker events
        - "/var/run/docker.sock:/var/run/docker.sock"
        # To read certs
        - "./ssl/tls-certs.yml:/ssl/tls-certs.yml:ro"
        - "./ssl/<ドメイン名>.pem:/ssl/<ドメイン名>.pem:ro"
        - "./ssl/<ドメイン名>.key:/ssl/<ドメイン名>.key:ro"
        - "./traefik.yml:/traefik.yml:ro"
    
    socket-proxy:
        image: tecnativa/docker-socket-proxy
        container_name: traefik-socket-proxy
        restart: unless-stopped
        volumes:
        - /var/run/docker.sock:/var/run/docker.sock:ro
        environment:
        CONTAINERS: 1
        labels:
        - "com.centurylinklabs.watchtower.enable=true"
        networks:
        - proxy
    

参考元にならって socket-proxy も導入している。Traefik が docker.sock に対して自由にアクセスできると、Traefik が攻撃を受けたときにホストにも影響が及んでしまうため、プロキシを挟んで docker.sock を保護するらしい。(合ってる?)

  • .env

    1
    2
    3
    
    # DOMAIN.TLD = example.com
    DOMAIN=example
    TLD=com
    

自分のドメインで置き換える。

  • tls-certs.yml

    1
    2
    3
    4
    5
    6
    7
    8
    9
    
    tls:
        certificates:
            - certFile: ssl/<ドメイン名>.pem
            keyFile: ssl/<ドメイン名>.key
        stores:
            default:
            defaultCertificate:
                certFile: ssl/<ドメイン名>.pem
                keyFile: ssl/<ドメイン名>.key
    
  • traefik.yml

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    
    providers:
        docker:
            exposedByDefault: false
            network: proxy
        file:
            filename: ./ssl/tls-certs.yml
    entryPoints:
        http:
            address: ":80"
        https:
            address: ":443"
    tls:
        entryPoints: https
    

Cloudflare で証明書を発行する

Cloudflare のダッシュボード から発行する。なお、ドメインはすでに登録してあり、DNS レコードも設定してあるものとする。

証明書を発行したいドメインを選択し、[SSL/TLS] > [オリジン サーバー] > [証明書を作成] > [作成] から発行する。

キーフォーマットは PEM を指定し、オリジン証明書とプライベートキーをそれぞれ <Traefikのディレクトリ>/ssl/<ドメイン名>.pem <ドメイン名>.key で保存する。(各ファイルは Traefik の docker-compose.yml の30-31行目で指定してあるので、該当箇所を変更すれば場所やファイル名は自由である)

[SSL/TLS] > [概要] から SSL/TLS 暗号化モードを「フル (厳密)」に設定する。

起動

準備が終わったらコンテナを起動する。

1
docker compose up -d

ログの確認は下記のコマンドで行う。

1
docker compose logs -t -f

Gotify の導入

ファイル構成

1
2
3
4
.
|-- .env
|-- docker-compose.yml
`-- data/

ファイルの作成

  • docker-compose.yml

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    
    version: "3"
    
    networks:
    proxy:
        external: true
    
    services:
        gotify:
            image: gotify/server
            container_name: gotify
            restart: unless-stopped
            volumes:
            - "./data:/app/data"
            environment:
            - GOTIFY_DEFAULTUSER_PASS=${GOTIFY_DEFAULTUSER_PASS}
            networks:
            - proxy
            labels:
            - "traefik.enable=true"
            - "traefik.http.routers.gotify.rule=Host(`${TRAEFIK_GOTIFY}`)"
            - "traefik.http.routers.gotify.entrypoints=https"
            - "traefik.http.routers.gotify.tls=true"
    
            # Watchtower Update
            - "com.centurylinklabs.watchtower.enable=true"
    
  • .env

    1
    2
    
    TREAFIK_GOTIFY=gotify.example.com
    GOTIFY_DEFAULTUSER_PASS=<パスワード>
    

ドメインは好きなものに置き換え、パスワードは適当に設定。1 DNS レコードの追加を忘れずに。

起動

1
docker compose up -d

パスワードの設定

Gotify にブラウザからアクセスし、ユーザー名: admin パスワード: admin でログインしたあと、メニューの [admin] をクリックするとパスワードを変更できるので、忘れずに設定すること。

Watchtower の導入

ファイル構成

1
2
3
.
|-- .env
`-- docker-compose.yml

ファイルの作成

  • docker-compose.yml

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    
    version: "3"
    
    services:
        watchtower:
            image: containrrr/watchtower
            container_name: watchtower
            restart: unless-stopped
            volumes:
            - /var/run/docker.sock:/var/run/docker.sock
            environment:
            - WATCHTOWER_CLEANUP=true
            - WATCHTOWER_LABEL_ENABLE=true
            #- WATCHTOWER_MONITOR_ONLY=true
            #- WATCHTOWER_POLL_INTERVAL=30
            - WATCHTOWER_SCHEDULE=0 0 4 * * MON
            - WATCHTOWER_NOTIFICATIONS=gotify
            - WATCHTOWER_NOTIFICATION_GOTIFY_URL=${GOTIFY_URL}
            - WATCHTOWER_NOTIFICATION_GOTIFY_TOKEN=${GOTIFY_TOKEN}
            labels:
            - "com.centurylinklabs.watchtower.enable=true"
    
  • .env

    1
    2
    
    GOTIFY_URL=https://gotify.example.com/
    GOTIFY_TOKEN=xxxxxxxxxxxxxxx
    

GOTIFY_URL は先程設定した Gotify のリンクを設定する。 GOTIFY_TOKEN は下記の方法で取得できる。

  1. ブラウザから Gotify にログインする
  2. メニューの [APPS] > [CREATE APPLICATION] からアプリの作成画面へ行き、適当なアプリ名を設定して表示されたトークンをコピーする

起動

1
docker compose up -d

作業は以上。

まとめ

Docker Compose 自体は噂通り簡単だった。

Traefik の証明書周りは参考資料が少なく大変だったが、公式ドキュメントや断片的な情報を集めて動くように直すのはやはり面白い。

次は色々サービス動かす編。


  1. これ動いてなくないか? ↩︎

Built with Hugo
テーマ StackJimmy によって設計されています。