Implement Træfik Into API Platform Dockerized

An open-source reverse proxy and load balancer for HTTP and TCP-based applications that is easy, dynamic, automatic, fast, full-featured, production proven, provides metrics and integrates with every major cluster technology.

HTTP および TCP ベースのアプリケーション用のオープンソースのリバース プロキシおよびロード バランサーは、簡単、動的、自動、高速、フル機能、実稼働実績があり、メトリクスを提供し、すべての主要なクラスター テクノロジと統合します。

https://traefik.io

—https://traefik.io

Basic Implementation

This tutorial will help you to define your own routes for your client, api and more generally for your containers.

このチュートリアルは、クライアント、API、およびより一般的にはコンテナー用に独自のルートを定義するのに役立ちます。

Use this custom API Platform docker-compose.yml file which implements ready-to-use Træfik container configuration. Override ports and add labels to tell Træfik to listen on the routes mentioned and redirect routes to a specified container.

すぐに使用できる Træfik コンテナー構成を実装する、このカスタム API プラットフォーム docker-compose.yml ファイルを使用します。ポートをオーバーライドし、ラベルを追加して、Træfik が言及されたルートをリッスンし、指定されたコンテナーにルートをリダイレクトするように指示します。

A few points to note:

いくつかの注意点:

  • --api.insecure=true Tells Træfik to generate a browser view to watch containers and IP/DNS associated easier
    --api.insecure=true コンテナと IP/DNS の関連付けを簡単に監視するためのブラウザ ビューを生成するように Træfik に指示します
  • --providers.docker Tells Træfik to listen on Docker Api
    --providers.docker Træfik に Docker API をリッスンするように指示します
  • labels: Key for Træfik configuration into Docker integration
    ラベル: Docker 統合への Træfik 構成のキー

yaml services: # ... api: labels: - traefik.http.routers.api.rule=Host(`api.localhost`)

yamlservices:#...api:labels:- traefik.http.routers.api.rule=Host(`api.localhost`)

The API DNS will be specified with traefik.http.routers.api.rule=Host(`your.host`) (here api.localhost) * --traefik.routers.clientloadbalancer.server.port=3000 The port specified to Træfik will be exposed by the container (here the React app exposes the 3000 port), but if your container exposes only one port, it can be ignored

API DNS は traefik.http.routers.api.rule=Host(`your.host`) (ここでは api.localhost)* で指定されます --traefik.routers.clientloadbalancer.server.port=3000 Træfik に指定されたポートコンテナーによって公開されます (ここでは、React アプリは 3000 ポートを公開します) が、コンテナーが 1 つのポートのみを公開する場合は無視できます。

We assume that you've generated a SSL localhost.crt and associated localhost.key combo under ./certs folder Then you edited your admin/Dockerfile and client/Dockerfile like this:

./certs フォルダーの下に SSL localhost.crt と関連する localhost.key コンボを生成したと仮定します。次に、admin/Dockerfile と client/Dockerfile を次のように編集しました。

-ENV HTTPS true
+EXPOSE 3000

After that, don't forget to re-build your containers

その後、コンテナを再構築することを忘れないでください

# docker-compose.yml
version: '3.4'

x-cache-from:
  - &cache
    cache_from:
      - ${NGINX_IMAGE:-quay.io/api-platform/nginx}
      - ${PHP_IMAGE:-quay.io/api-platform/php}

services:
  traefik:
      image: traefik:latest
      command: --api.insecure=true --providers.docker
      ports:
        - target: 80
          published: 80
          protocol: tcp
        - target: 443
          published: 443
          protocol: tcp
        - target: 8080
          published: 8080
          protocol: tcp
      volumes:
        - /var/run/docker.sock:/var/run/docker.sock

  php:
    build:
      context: ./api
      target: api_platform_php
      <<: *cache
    image: ${PHP_IMAGE:-quay.io/api-platform/php}
    healthcheck:
      interval: 10s
      timeout: 3s
      retries: 3
      start_period: 30s
    depends_on:
      - db
    volumes:
      - ./api:/srv/api:rw,cached
      - ./api/docker/php/conf.d/api-platform.dev.ini:/usr/local/etc/php/conf.d/api-platform.ini

  api:
    build:
      context: ./api
      target: api_platform_nginx
      <<: *cache
    image: ${NGINX_IMAGE:-quay.io/api-platform/nginx}
    depends_on:
      - php
    volumes:
      - ./api/public:/srv/api/public:ro

  vulcain:
    image: dunglas/vulcain
    environment:
      - CERT_FILE=/certs/localhost.crt
      - KEY_FILE=/certs/localhost.key
      - UPSTREAM=http://api
    depends_on:
      - api
    volumes:
      - ./certs:/certs:ro
    labels:
      - traefik.http.routers.vulcain.rule=Host(`vulcain.localhost`)

  db:
    image: postgres:12-alpine
    environment:
      - POSTGRES_DB=api
      - POSTGRES_PASSWORD=!ChangeMe!
      - POSTGRES_USER=api-platform
    volumes:
      - db-data:/var/lib/postgresql/data:rw
    labels:
      - traefik.http.routers.db.rule=Host(`db.localhost`)

  mercure:
    image: dunglas/mercure
    environment:
#      - ACME_HOSTS=${DOMAIN_NAME}
#      - CERT_FILE=/certs/localhost.crt
#      - KEY_FILE=/certs/localhost.key
      - JWT_KEY=${JWT_KEY}
      - ALLOW_ANONYMOUS=1
      - USE_FORWARDED_HEADERS=true
      - CORS_ALLOWED_ORIGINS=*
      - READ_TIMEOUT=0s
      - WRITE_TIMEOUT=0s
      - PUBLISH_ALLOWED_ORIGINS=*
    volumes:
      - ./certs:/certs:ro
    labels:
      - traefik.http.routers.mercure.rule=Host(`mercure.localhost`)

  client:
    build:
      context: ./client
      target: api_platform_client_development
      cache_from:
        - ${CLIENT_IMAGE:-quay.io/api-platform/client}
    image: ${CLIENT_IMAGE:-quay.io/api-platform/client}
    tty: true
    environment:
      - API_PLATFORM_CLIENT_GENERATOR_ENTRYPOINT=http://api
      - API_PLATFORM_CLIENT_GENERATOR_OUTPUT=src
    volumes:
      - ./client:/usr/src/client:rw,cached
    labels:
      - traefik.http.routers.client.rule=Host(`client.localhost`)
      - traefik.http.services.client.loadbalancer.server.port=3000

  admin:
    build:
      context: ./admin
      target: api_platform_admin_development
      cache_from:
        - ${ADMIN_IMAGE:-quay.io/api-platform/admin}
    image: ${ADMIN_IMAGE:-quay.io/api-platform/admin}
    tty: true
    volumes:
      - ./admin:/usr/src/admin:rw,cached
    labels:
      - traefik.http.routers.admin.rule=Host(`admin.localhost`)
      - traefik.http.services.admin.loadbalancer.server.port=3000

volumes:
  db-data: {}

Don't forget the db-data, or the database won't work in this dockerized solution.

db-data を忘れないでください。そうしないと、この Docker 化されたソリューションでデータベースが機能しなくなります。

localhost is a reserved domain referred to in your /etc/hosts. If you want to implement custom DNS such as production DNS in local, just add them at the end of your /etc/host file like that:

localhost は、/etc/hosts で参照される予約済みドメインです。ローカルで本番 DNS などのカスタム DNS を実装する場合は、/etc/host ファイルの最後に次のように追加します。

# /etc/hosts
# ...

127.0.0.1       your.domain.com

If you do that, you'll have to update the CORS_ALLOW_ORIGIN environment variable api/.env to accept the specified URL.

その場合、CORS_ALLOW_ORIGIN 環境変数 api/.env を更新して、指定された URL を受け入れる必要があります。

Known Issues

If your network is of type B, it may conflict with the Træfik sub-network.

ネットワークがタイプ B の場合、Træfik サブネットワークと競合する可能性があります。

Going Further

As this Træfik configuration listens on 80 and 443 ports, you can run only 1 Træfik instance per server. However, you may want to run multiple API Platform projects on the same server. To deal with it, you'll have to externalize the Træfik configuration to another docker-compose.yml file, anywhere on your server.

この Træfik 構成は 80 および 443 ポートでリッスンするため、サーバーごとに実行できる Træfik インスタンスは 1 つだけです。ただし、同じサーバーで複数の API プラットフォーム プロジェクトを実行したい場合があります。これに対処するには、サーバー上の任意の場所にある別の docker-compose.yml ファイルに Træfik 構成を外部化する必要があります。

Here is a working example:

これが実際の例です:

# /somewhere/docker-compose.yml
version: '3.4'

services:
  traefik:
    image: traefik:latest
    command: --api.insecure=true --providers.docker
    ports:
      - target: 80
        published: 80
        protocol: tcp
      - target: 443
        published: 443
        protocol: tcp
      - target: 8080
        published: 8080
        protocol: tcp
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    networks:
      - api_platform_network
    # Add other networks here

networks:
  api_platform_network:
    external: true
  # Add other networks here

Then update the docker-compose.yml file belonging to your API Platform projects:

次に、API プラットフォーム プロジェクトに属する docker-compose.yml ファイルを更新します。

# /anywhere/api-platform/docker-compose.yml
version: '3.4'

x-cache:
  &cache
  cache_from:
    - ${CONTAINER_REGISTRY_BASE}/php
    - ${CONTAINER_REGISTRY_BASE}/nginx
    - ${CONTAINER_REGISTRY_BASE}/varnish

+x-network:
+  &network
+  networks:
+    - api_platform_network

services:
-  traefik:
-    image: traefik:latest
-    command: --api.insecure=true --providers.docker
-    ports:
-      - target: 80
-        published: 80
-        protocol: tcp
-      - target: 443
-        published: 443
-        protocol: tcp
-      - target: 8080
-        published: 8080
-        protocol: tcp
-    volumes:
-      - /var/run/docker.sock:/var/run/docker.sock

  php:
    build:
      context: ./api
      target: api_platform_php
      <<: *cache
    image: ${PHP_IMAGE:-quay.io/api-platform/php}
+    environment:
+      # You should remove these variables from .env into api folder
+      - TRUSTED_HOSTS=^(((${SUBDOMAINS_LIST}\.)?${DOMAIN_NAME})|api)$$
+      - CORS_ALLOW_ORIGIN=^${HTTP_OR_SSL}(${SUBDOMAINS_LIST}.)?${DOMAIN_NAME}$$
+      - DATABASE_URL=postgres://${DB_USER}:${DB_PASS}@db/${DB_NAME}
+      - MERCURE_SUBSCRIBE_URL=${HTTP_OR_SSL}mercure.${DOMAIN_NAME}$$
+      - MERCURE_PUBLISH_URL=${HTTP_OR_SSL}mercure.${DOMAIN_NAME}$$
+      - MERCURE_JWT_TOKEN=${JWT_KEY}
    healthcheck:
      interval: 10s
      timeout: 3s
      retries: 3
      start_period: 30s
    depends_on:
      - db
-      - dev-tls
    volumes:
      - ./api:/srv/api:rw,cached
      - ./api/docker/php/conf.d/api-platform.dev.ini:/usr/local/etc/php/conf.d/api-platform.ini
      - ./certs:/certs:ro
+    <<: *network

  api:
    build:
      context: ./api
      target: api_platform_nginx
      <<: *cache
    image: ${NGINX_IMAGE:-quay.io/api-platform/nginx}
    depends_on:
      - php
    volumes:
      - ./api/public:/srv/api/public:ro
    labels:
      - traefik.http.routers.api.rule=Host(`api.${DOMAIN_NAME}`)
+    <<: *network

  vulcain:
    image: dunglas/vulcain
    environment:
      - CERT_FILE=/certs/localhost.crt
      - KEY_FILE=/certs/localhost.key
      - UPSTREAM=http://api
    depends_on:
      - api
-      - dev-tls
    volumes:
      - ./certs:/certs:ro
    labels:
      - traefik.http.routers.vulcain.rule=Host(`vulcain.${DOMAIN_NAME}`)
+    <<: *network

  db:
    image: postgres:12-alpine
    environment:
      - POSTGRES_DB=${DB_NAME}
      - POSTGRES_PASSWORD=${DB_PASS}
      - POSTGRES_USER=${DB_USER}
    volumes:
      - db-data:/var/lib/postgresql/data:rw
    labels:
      - traefik.http.routers.db.rule=Host(`db.${DOMAIN_NAME}`)
+    <<: *network

  mercure:
    image: dunglas/mercure
    environment:
#      - ACME_HOSTS=${DOMAIN_NAME}
#      - CERT_FILE=/certs/localhost.crt
#      - KEY_FILE=/certs/localhost.key
      - JWT_KEY=${JWT_KEY}
      - ALLOW_ANONYMOUS=1
      - USE_FORWARDED_HEADERS=true
      - CORS_ALLOWED_ORIGINS=*
      - READ_TIMEOUT=0s
      - WRITE_TIMEOUT=0s
      - PUBLISH_ALLOWED_ORIGINS=*
-    depends_on:
-      - dev-tls
    volumes:
      - ./certs:/certs:ro
    labels:
      - traefik.http.routers.mercure.rule=Host(`mercure.${DOMAIN_NAME}`)
+    <<: *network

  client:
    build:
      context: ./client
      target: api_platform_client_development
      cache_from:
        - ${CLIENT_IMAGE:-quay.io/api-platform/client}
    image: ${CLIENT_IMAGE:-quay.io/api-platform/client}
    tty: true
    environment:
      - API_PLATFORM_CLIENT_GENERATOR_ENTRYPOINT=http://api
      - API_PLATFORM_CLIENT_GENERATOR_OUTPUT=src
+      # You should remove this variable from .env into client folder
+      - REACT_APP_API_ENTRYPOINT=${HTTP_OR_SSL}api.${DOMAIN_NAME}
-    depends_on:
-      - dev-tls
    volumes:
      - ./client:/usr/src/client:rw,cached
    expose:
      - 3000
    labels:
      - traefik.http.routers.client.rule=Host(`client.${DOMAIN_NAME}`)
      - traefik.http.services.client.loadbalancer.server.port=3000
+    <<: *network

  admin:
    build:
      context: ./admin
      target: api_platform_admin_development
      cache_from:
        - ${ADMIN_IMAGE:-quay.io/api-platform/admin}
    image: ${ADMIN_IMAGE:-quay.io/api-platform/admin}
    tty: true
-    depends_on:
-      - dev-tls
+    environment:
+      # You should remove this variable from .env into client folder
+      - REACT_APP_API_ENTRYPOINT=${HTTP_OR_SSL}api.${DOMAIN_NAME}
    volumes:
      - ./admin:/usr/src/admin:rw,cached
    expose:
      - 3000
    labels:
      - traefik.http.routers.admin.rule=Host(`admin.${DOMAIN_NAME}`)
      - traefik.http.services.admin.loadbalancer.server.port=3000
+    <<: *network

volumes:
  db-data: {}

+networks:
+  api_platform_network:
+    external: true

Finally, some environment variables must be defined, here is an example of a .env file to set them:

最後に、いくつかの環境変数を定義する必要があります。これらを設定する .env ファイルの例を次に示します。

CONTAINER_REGISTRY_BASE=quay.io/api-platform
DOMAIN_NAME=localhost
HTTP_OR_SSL=https://
DB_NAME=api-platform-db-name
DB_PASS=YouMustChangeThisPassword
DB_USER=api-platform
JWT_KEY=!UnsecureChangeMe!
SUBDOMAINS_LIST=(admin|api|mercure)

This way, you can configure your main variables into one single file.

このようにして、メイン変数を 1 つのファイルに構成できます。

Multiple Instances

If you want to run multiple API Platform instances on the same server and behind only one Træfik instance, you'll have to define different service names for each service to avoid named conflicts error since Træfik v2.0.
To achieve that, by setting only one more environment variable, you'll be able to make each instance unique. Here is a working example below:

同じサーバー上で 1 つの Træfik インスタンスのみの背後で複数の API Platform インスタンスを実行する場合は、Træfik v2.0 以降の名前の競合エラーを回避するために、サービスごとに異なるサービス名を定義する必要があります。もう 1 つの環境変数を使用すると、各インスタンスを一意にすることができます。以下に実際の例を示します。

# /anywhere/first/api-platform/.env
#...
RANDOM_UNIQUE_KEY=yourUniqueKeyForYourFirstInstance
# /anywhere/second/api-platform/.env
#...
RANDOM_UNIQUE_KEY=yourUniqueKeyForYourSecondInstance

Then update each traefik http routers names and services following this sample for admin

次に、管理者向けのこのサンプルに従って、各 traefik http ルーターの名前とサービスを更新します。

# /anywhere/first/api-plaform/docker-compose.yml
# ...
    labels:
      - traefik.http.routers.admin-${RANDOM_UNIQUE_KEY}.rule=Host(`admin.${DOMAIN_NAME}`)
      - traefik.http.services.admin-${RANDOM_UNIQUE_KEY}.loadbalancer.server.port=3000

More Generic Approach

Here is a fully working sample for Træfik generic config with a little script using docker-compose override approach.
We assume that you've set EXPOSE 3000 in your client and admin Dockerfile.

これは、docker-compose オーバーライド アプローチを使用した小さなスクリプトを含む Træfik 汎用構成の完全に機能するサンプルです。クライアントと管理者の Dockerfile で EXPOSE 3000 を設定していると仮定します。

Create a new init-dc.sh which contains the generation code that will be written in docker-compose.override.yaml file.

docker-compose.override.yaml ファイルに書き込まれる生成コードを含む新しい init-dc.sh を作成します。

#!/bin/sh
# /anywhere/api-platform/init-dc.sh

services=("admin:admin." "api:api." "mercure:mercure.") # Define your services keys following this format: "{container key}:{sub DNS}". To define root DNS write nothing after the colon
text="version: '3.4'
services:"

for k in "${services[@]}" ; do
    key=${k%%:*}
    value=${k#*:}
    text+="
  $key:
    labels:
      - traefik.http.routers.$key-\${RANDOM_UNIQUE_KEY}.entrypoints=web-secure
      - traefik.http.routers.$key-\${RANDOM_UNIQUE_KEY}.rule=Host(\`${value}\${DOMAIN_NAME}\`)
      - traefik.http.routers.$key-\${RANDOM_UNIQUE_KEY}.tls=true
      - traefik.http.routers.$key-\${RANDOM_UNIQUE_KEY}.tls.domains[0].main=\${DOMAIN_NAME}
      - traefik.http.routers.$key-\${RANDOM_UNIQUE_KEY}.tls.domains[0].sans=*.\${DOMAIN_NAME}
      - traefik.http.routers.$key-\${RANDOM_UNIQUE_KEY}.tls.certresolver=sample
"
done

echo "$text" > ./docker-compose.override.yml

Write this minimal configuration into your traefik.toml file:

この最小限の構成を traefik.toml ファイルに書き込みます。

# /anywhere/traefik/traefik.toml
[providers.docker]
  endpoint = "unix:///var/run/docker.sock"

[api]
  insecure = true
  dashboard = true
  debug = true

[entryPoints.web]
  address = ":80"
  [entryPoints.web.http]
    [entryPoints.web.http.redirections]
      [entryPoints.web.http.redirections.entryPoint]
        to = "web-secure"
        scheme = "https"

[entryPoints.web-secure]
  address = ":443"

Then after that update respectively your API Platform and Træfik docker-compose.yml following these examples below.

次に、以下の例に従って、API プラットフォームと Træfik docker-compose.yml をそれぞれ更新します。

# /anywhere/api-platform/docker-compose.yml
version: '3.4'

x-cache:
  &cache
  cache_from:
    - ${CONTAINER_REGISTRY_BASE}/php
    - ${CONTAINER_REGISTRY_BASE}/nginx
    - ${CONTAINER_REGISTRY_BASE}/varnish

x-network:
  &network
  networks:
    - api_platform_network

services:
  php:
    build:
      context: ./api
      target: api_platform_php
      <<: *cache
    image: ${PHP_IMAGE:-quay.io/api-platform/php}
    environment:
      # You should remove these variables from .env into api folder
      - TRUSTED_HOSTS=^(((${SUBDOMAINS_LIST}\.)?${DOMAIN_NAME})|api)$$
      - CORS_ALLOW_ORIGIN=^${HTTP_OR_SSL}(${SUBDOMAINS_LIST}.)?${DOMAIN_NAME}$$
      - DATABASE_URL=postgres://${DB_USER}:${DB_PASS}@db/${DB_NAME}
      - MERCURE_SUBSCRIBE_URL=http://mercure/.well-known/mercure
      - MERCURE_PUBLISH_URL=http://mercure/.well-known/mercure
      - MERCURE_JWT_TOKEN=${JWT_KEY}
    healthcheck:
      interval: 10s
      timeout: 3s
      retries: 3
      start_period: 30s
    depends_on:
      - db
    volumes:
      - ./api:/srv/api:rw,cached
      - ./api/docker/php/conf.d/api-platform.dev.ini:/usr/local/etc/php/conf.d/api-platform.ini
      - ./certs:/certs:ro
    <<: *network

  api:
    build:
      context: ./api
      target: api_platform_nginx
      <<: *cache
    image: ${NGINX_IMAGE:-quay.io/api-platform/nginx}
    depends_on:
      - php
    volumes:
      - ./api/public:/srv/api/public:ro
    <<: *network

  vulcain:
    image: dunglas/vulcain
    environment:
      - CERT_FILE=/certs/localhost.crt
      - KEY_FILE=/certs/localhost.key
      - UPSTREAM=http://api
    depends_on:
      - api
    volumes:
      - ./certs:/certs:ro
    <<: *network

  db:
    image: postgres:12-alpine
    environment:
      - POSTGRES_DB=${DB_NAME}
      - POSTGRES_PASSWORD=${DB_PASS}
      - POSTGRES_USER=${DB_USER}
    volumes:
      - db-data:/var/lib/postgresql/data:rw
    <<: *network

  mercure:
    image: dunglas/mercure
    environment:
#      - ACME_HOSTS=${DOMAIN_NAME}
#      - CERT_FILE=/certs/localhost.crt
#      - KEY_FILE=/certs/localhost.key
      - JWT_KEY=${JWT_KEY}
      - ALLOW_ANONYMOUS=1
      - USE_FORWARDED_HEADERS=true
      - CORS_ALLOWED_ORIGINS=*
      - READ_TIMEOUT=0s
      - WRITE_TIMEOUT=0s
      - PUBLISH_ALLOWED_ORIGINS=*
    volumes:
      - ./certs:/certs:ro
    <<: *network

  client:
    build:
      context: ./client
      target: api_platform_client_development
      cache_from:
        - ${CLIENT_IMAGE:-quay.io/api-platform/client}
    image: ${CLIENT_IMAGE:-quay.io/api-platform/client}
    tty: true
    environment:
      - API_PLATFORM_CLIENT_GENERATOR_ENTRYPOINT=http://api
      - API_PLATFORM_CLIENT_GENERATOR_OUTPUT=src
      # You should remove this variable from .env into client folder
      - REACT_APP_API_ENTRYPOINT=${HTTP_OR_SSL}api.${DOMAIN_NAME}
    volumes:
      - ./client:/usr/src/client:rw,cached
    <<: *network

  admin:
    build:
      context: ./admin
      target: api_platform_admin_development
      cache_from:
        - ${ADMIN_IMAGE:-quay.io/api-platform/admin}
    image: ${ADMIN_IMAGE:-quay.io/api-platform/admin}
    tty: true
    environment:
      # You should remove this variable from .env into client folder
      - REACT_APP_API_ENTRYPOINT=${HTTP_OR_SSL}api.${DOMAIN_NAME}
    volumes:
      - ./admin:/usr/src/admin:rw,cached
    <<: *network

volumes:
  db-data: {}

networks:
  api_platform_network:
    external: true
# /anywhere/traefik/docker-compose.yml
version: '3.4'

x-network:
  &network
  networks:
    - api_platform_network

services:
  traefik:
    image: traefik:latest
    ports:
      - target: 80
        published: 80
        protocol: tcp
      - target: 443
        published: 443
        protocol: tcp
      - target: 8080
        published: 8080
        protocol: tcp

    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - ./traefik.toml:/etc/traefik/traefik.toml
      - ./acme.json:/acme.json
    <<: *network

networks:
  api_platform_network:
    external: true

For a more detailed step-by-step configuration, take a look at this repository which include Fail2ban link to Træfik instance.

より詳細な段階的な構成については、Træfik インスタンスへの Fail2ban リンクを含むこのリポジトリを参照してください。