Docker環境でドメインごとにアクセスするコンテナを切り替える（複数ポートも可能）

Docker環境でドメインごとにコンテナを切り替える為の方法はいくつか提供して下さっている方がいます。
その中で私が試したのは下記の二つになります。

* nginx-proxy  
→既に積極的な開発は行われていません  
→１コンテナで複数のポートを開放している場合、私が思ったようなアクセスができませんでした。
* traefik

上記の機能を試した結果、私の目的には「traefik」が合致しました。

## ■traefikを動作させる

```/tmp/traefik.yml
# Providers config
providers:
  docker: {} # Dockerとの連携を有効

# API/Dashboard config
api:
  insecure: true # WebUI にアクセスできるように設定

# コンテナを作る前に必ずポートが使用できることを確認してください
entryPoints:
  web:
    address: ":80"
  secure:
    address: ":443"
  nodejs:
    address: ":8080"

certificatesResolvers:
  default:
    acme:
      email: system@test.local
      storage: acme.json
      httpChallenge:
        entryPoint: web
```

entryPointsは各環境のポート等に合わせて定義を行ってください。

```docker-compose.yml
version: '3.7'

services:
  traefik:
    image: traefik
    container_name: traefik
    restart: "always"
    ports:
      - "80:80"
      - "443:443"
      - "8080:8080"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - /tmp/traefik.yml:/etc/traefik/traefik.yml:ro
```

```docker-compose-app.yml
version: '3.7'
services:
  test-app-test:
    # =====================
    # 既存のコンテナの設定
    # =====================
    # アプリのコンテナの設定に下記を追加する
    labels:
      - traefik.enable=true
      - traefik.docker.network=docker-network
      - traefik.http.middlewares.app-https-redirect.redirectscheme.scheme=https

      # 80設定（HTTPSにリダイレクト）
      - traefik.http.routers.test-app-test.rule=Host(`apptest.local`))
      - traefik.http.routers.test-app-test.entrypoints=web
      - traefik.http.routers.test-app-test.middlewares=app-https-redirect

      # 443設定
      - traefik.http.routers.test-app-test-secure.rule=Host(`apptest.local`))
      - traefik.http.routers.test-app-test-secure.entrypoints=secure
      - traefik.http.routers.test-app-test-secure.tls=true
      - traefik.http.routers.test-app-test-secure.service=test-app-test-secure-service
      - traefik.http.services.test-app-test-secure-service.loadbalancer.server.port=80

      # 8080設定
      - traefik.http.routers.test-app-test-nodejs.rule=Host(`apptest.local`))
      - traefik.http.routers.test-app-test-nodejs.entrypoints=nodejs
      - traefik.http.routers.test-app-test-nodejs.tls=true
      - traefik.http.routers.test-app-test-nodejs.service=test-app-test-nodejs-service
      - traefik.http.services.test-app-test-nodejs-service.loadbalancer.server.port=8080

```

* とりあえず、動作する環境を作ってみてください。（そのあと設定を変更し、理解をしてください）
* traefikの各entrypointsごとの設定を行っています。
* http は https へリダイレクトを行っています。
* ```Host(`apptest.local\`)``` は各アプリ環境のドメインを記載してください
* 「traefik.http.routers.test-app-test」の「test-app-test」は「services:」直下の「test-app-test:」と一致する必要があります。  
※「test-app-test:」で使用できない文字は使用できません。
※「.(ドット）」も使用しない方が良いかと思います。（何かしら手を加えれば設定できるかもしれませんが、私は確認をしていません）
* httpsでアクセスがあるならば「.tls=true」は記載をしてください
* サーバ側のポートは「.loadbalancer.server.port」で設定します

# ■動作確認サンプル一式

「/tmp/test」に下記のファイルを配置してください。

* traefik.yml
* docker-compose.yml（共通の定義ファイル）
* docker-compose-base.yml（アプリケーション用のベースの定義ファイル）
* docker-compose-app.yml（アプリケーションの個別の定義ファイル）


```traefik.yml
# Providers config
providers:
  docker: {} # Docker との連携を有効

# API/Dashboard config
api:
  insecure: true # WebUI にアクセスできるように設定

entryPoints:
  web:
    address: ":80"
  secure:
    address: ":443"
  nodejs:
    address: ":8888"

certificatesResolvers:
  default:
    acme:
      email: test@local.test
      storage: acme.json
      httpChallenge:
        entryPoint: web
```
```docker-compose.yml
version: '3.7'
services:
  traefik:
    image: traefik
    container_name: traefik
    restart: "always"
    ports:
      - "80:80"
      - "443:443"
      - "8888:8888"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - /tmp/test/traefik.yml:/etc/traefik/traefik.yml:ro
    networks:
      - test-network

  db:
    image: mysql:5.7.12
    container_name: db
    environment:
      MYSQL_ROOT_PASSWORD: root
    networks:
      - test-network

networks:
  test-network:
    external: true
```
```docker-compose-base.yml
version: '3.7'
services:
  app-#SERVICE_NAME#:
    container_name: app-${SERVICE_NAME}
    privileged: true
    networks:
      - test-network
    labels:
      - traefik.enable=true
      - traefik.docker.network=docker-network
      - traefik.http.middlewares.app-https-redirect.redirectscheme.scheme=https

      - traefik.http.routers.app-${SERVICE_NAME}.rule=Host(`${SERVICE_DOMAIN}`)
      - traefik.http.routers.app-${SERVICE_NAME}.entrypoints=web
      - traefik.http.routers.app-${SERVICE_NAME}.middlewares=app-https-redirect

      - traefik.http.routers.app-${SERVICE_NAME}-secure.rule=Host(`${SERVICE_DOMAIN}`)
      - traefik.http.routers.app-${SERVICE_NAME}-secure.entrypoints=secure
      - traefik.http.routers.app-${SERVICE_NAME}-secure.tls=true
      - traefik.http.routers.app-${SERVICE_NAME}-secure.service=app-${SERVICE_NAME}-secure-service
      - traefik.http.services.app-${SERVICE_NAME}-secure-service.loadbalancer.server.port=80

      - traefik.http.routers.app-${SERVICE_NAME}-nodejs.rule=Host(`${SERVICE_DOMAIN}`)
      - traefik.http.routers.app-${SERVICE_NAME}-nodejs.entrypoints=nodejs
      - traefik.http.routers.app-${SERVICE_NAME}-nodejs.tls=true
      - traefik.http.routers.app-${SERVICE_NAME}-nodejs.service=app-${SERVICE_NAME}-nodejs-service
      - traefik.http.services.app-${SERVICE_NAME}-nodejs-service.loadbalancer.server.port=8888

networks:
  test-network:
    external: true

```
```docker-compose-app.yml
version: '3.7'
services:
  app-#SERVICE_NAME#:
    #簡単なサンプル用です
    image: httpd:alpine
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro

```

上記のファイルを配置後、下記コマンドを実行していきます

```bash
# ymlファイルに定義しているネットワークを作成します。
# 必要に応じて、NAT用などを作成してください
docker network create test-network

# 共通定義のコンテナを作成します
# ※traefikのentryPointsのポートは開放しておくこと
docker-compose -f docker-compose.yml up -d --build

#### アプリケーションコンテナを作成（一つ目）
# 同じ定義で複数のアプリケーションを追加していく想定の為、変数で定義します
SERVICE_NAME=test1
SERVICE_DOMAIN=${SERVICE_NAME}.local.host

# sedで置き換えているのは、環境変数が使用できない部分を変更するためです
# この部分はアプリケーションコンテナのビルドで共通の処理を想定しています
sed -e "s/#SERVICE_NAME#/${SERVICE_NAME}/g" docker-compose-base.yml > base.yml \
  && sed -e "s/#SERVICE_NAME#/${SERVICE_NAME}/g" docker-compose-app.yml > app.yml \
  && export SERVICE_NAME=${SERVICE_NAME} \
  && export SERVICE_DOMAIN=${SERVICE_DOMAIN} \
  && docker-compose -f base.yml -f app.yml up -d --build

#### アプリケーションコンテナを作成（二つ目）
SERVICE_NAME=test2
SERVICE_DOMAIN=${SERVICE_NAME}.local.host

sed -e "s/#SERVICE_NAME#/${SERVICE_NAME}/g" docker-compose-base.yml > base.yml \
  && sed -e "s/#SERVICE_NAME#/${SERVICE_NAME}/g" docker-compose-app.yml > app.yml \
  && export SERVICE_NAME=${SERVICE_NAME} \
  && export SERVICE_DOMAIN=${SERVICE_DOMAIN} \
  && docker-compose -f base.yml -f app.yml up -d --build
```

上記のコマンドを実行すればアプリケーションのコンテナが二つ立ち上がっている状態になります。
次はアクセスを行い、確認します。

```hosts
# ローカルdocker環境にドメインでアクセスする為、hostsに記載をします
192.168.1.10 test1.local.host
192.168.1.10 test2.local.host
```

```
# アクセスする為のURLは下記になります。
https://test1.local.host/
https://test2.local.host/
```

私はコマンドの部分のshellを用意して使用しています。
実際に自身の環境作成に組み込むと処理は多くなると思いますが、とりあえずのサンプルになりますので、、、

# ■関連記事
https://qiita.com/error484/items/ef222c50a4bb81e5c2a7

https://qiita.com/error484/items/91e507c67924c4af88a1

https://qiita.com/error484/items/391ff8d27db8b522c082

https://qiita.com/error484/items/5c538d4f0d39fd0df5d0

https://qiita.com/error484/items/356087c8e7b6826e0dee

※動作させるため、stackoverflow等の多くサイトを参考にさせて頂いています