如何将 Traefik 服务添加到现有 Docker Swarm 集群?

How to add a Traefik service to an existing Docker Swarm cluster?

我有一个 Web 应用程序,我可以使用 Docker Swarm 和 Compose 文件成功部署:

docker stack deploy --compose-file docker-compose.yml common

并由文件描述:

version: "3.8"
services:
  www:
    image: localhost:5000/www.learnintouch
    ports:
      - "81:80"
      - "444:443"
    networks:
      common:
networks:
  common:
    name: common

现在我给它添加一个Traefik服务:

version: "3.8"
services:
  traefik:
    image: traefik
    ports:
      - target: 81
        published: 80
        mode: host
      - target: 8080
        published: 8080
        mode: host
      - target: 444
        published: 443
        mode: host
    deploy:
      replicas: 1
      placement:
        constraints:
          - node.role == manager
      labels:
        - traefik.enable=true
        - traefik.docker.network=common
        - traefik.constraint-label=common
        - traefik.docker.lbswarm=true
    command:
      --log.level=DEBUG
      --api.insecure=true
      --providers.docker
      --providers.docker.swarmmode
      --accesslog
      --api.dashboard=true
    volumes:
      - "/var/run/docker.sock:/var/run/docker.sock:ro"
      - "~/dev/docker/projects/common/volumes/traefik/logs:/traefiklog"
      - "~/dev/docker/projects/common/volumes/traefik/rules:/rules"
  www:
    image: localhost:5000/www.learnintouch
    ports:
      - "81:80"
      - "444:443"
    labels:
      - "traefik.http.routers.www_learnintouch.rule=Host('www_learnintouch.docker.localhost')"
    networks:
      common:
networks:
  common:
    name: common
  default:
    driver: overlay

容器已部署:

22:49 $ docker ps
CONTAINER ID   IMAGE                                       COMMAND                  CREATED              STATUS                        PORTS                                                                                                                         NAMES
ff38498c061f   localhost:5000/www.learnintouch:latest      "/bin/bash /usr/loca…"   About a minute ago   Up About a minute (healthy)                                                                                                                                 www_learnintouch_www.1.n9mvfo64hp8unzvcau6rws99q
df070019539f   traefik:latest                              "/entrypoint.sh --lo…"   2 minutes ago        Up 2 minutes                  80/tcp, 0.0.0.0:8080->8080/tcp, :::8080->8080/tcp, 0.0.0.0:80->81/tcp, :::80->81/tcp, 0.0.0.0:443->444/tcp, :::443->444/tcp   common_traefik.1.t141a053tvcbi0ql5ztbjmfgp

但是端口映射存在一些问题:

common_traefik.1.t141a053tvcb@stephane-pc    | time="2021-06-13T20:52:24Z" level=error msg="service \"common-traefik\" error: port is missing" providerName=docker container=common-traefik-m6sxqj8giwuex2jlbrsgilsm9
common_traefik.1.t141a053tvcb@stephane-pc    | time="2021-06-13T20:52:39Z" level=error msg="service \"www-learnintouch-www\" error: port is missing" providerName=docker container=www-learnintouch-www-n9mvfo64hp8unzvcau6rws99q
common_traefik.1.t141a053tvcb@stephane-pc    | time="2021-06-13T20:53:24Z" level=warning msg="Could not find network named 'common' for container 'common_traefik'! Maybe you're missing the project's prefix in the label? Defaulting to first available network." providerName=docker container=common-traefik-m6sxqj8giwuex2jlbrsgilsm9 serviceName=common-traefik

我没有使用其他命令手动创建网络,因为在添加 Traefik 服务之前我不需要这样做。

作为 Traefik 和网络虚拟机,我希望安装简单,而不必手动冗余指定主机名,也不必 运行 一些外部命令。

在搜索提示后,我找到了这个 opened issue 但我不知道该怎么做。

更新:我为所有服务添加了一个端口,如下所示:

labels:
  - "traefik.http.routers.www_learnintouch.rule=Host('www_learnintouch.docker.localhost')"
  - "traefik.http.services.www.loadbalancer.server.port=5000"

但日志仍然显示相同的错误消息。

更新:提供解决方案后的文件:

version: "3.9"
services:
  traefik:
    image: traefik
    networks:
      common:
    ports:
      - target: 81
        published: 80
        mode: host
      - target: 444
        published: 443
        mode: host
    deploy:
      mode: global
      placement:
        constraints:
          - node.role == manager
      labels:
        - "traefik.enable=true"
        - "traefik.http.services.traefik.loadbalancer.server.port=8080"
        - "traefik.constraint-label=common"
    command:
      --log.level=DEBUG
      --api.insecure=true
      --providers.docker
      --providers.docker.swarmmode
      --accesslog
      --api.dashboard=true
      --providers.docker.network=common
      --providers.docker.exposedbydefault=false
      --providers.docker.defaultRule=Host(`{{normalize .Name}}.docker.local`)
    volumes:
      - "/var/run/docker.sock:/var/run/docker.sock:ro"
      - "~/dev/docker/projects/common/volumes/traefik/logs:/traefiklog"
      - "~/dev/docker/projects/common/volumes/traefik/rules:/rules"
  logrotate:
    image: localhost:5000/logrotate
    networks:
      common:
    labels:
      - "traefik.enable=false"
    volumes:
      - "~/dev/docker/projects/common/volumes/logs:/usr/local/logrotate/logs"
    user: "${CURRENT_UID}:${CURRENT_GID}"
    environment:
      HOST_USER_ID: ${CURRENT_UID}
      HOST_GROUP_ID: ${CURRENT_GID}
  www:
    image: localhost:5000/www.learnintouch
    labels:
      - "traefik.enable=true"
      - "traefik.http.services.www.loadbalancer.server.port=80"
    networks:
      common:
    volumes:
      - "~/dev/docker/projects/learnintouch/volumes/www.learnintouch/account/data:/usr/local/learnintouch/www/learnintouch.com/account/data"
      - "~/dev/docker/projects/learnintouch/volumes/www.learnintouch/account/backup:/usr/local/learnintouch/www/learnintouch.com/account/backup"
      - "~/dev/docker/projects/learnintouch/volumes/engine:/usr/local/learnintouch/engine"
      - "~/dev/docker/projects/common/volumes/letsencrypt/certbot/conf/live/thalasoft.com:/usr/local/learnintouch/letsencrypt"
      - "~/dev/docker/projects/common/volumes/logs:/usr/local/apache/logs"
      - "~/dev/docker/projects/common/volumes/logs:/usr/local/learnintouch/logs"
    user: "${CURRENT_UID}:${CURRENT_GID}"
    environment:
      HOST_USER_ID: ${CURRENT_UID}
      HOST_GROUP_ID: ${CURRENT_GID}
      DB_HOST: mysql
      DB_PORT: 3306
      WWW_LEARNINTOUCH_DB_NAME: db_learnintouch
      WWW_LEARNINTOUCH_DB_USER: learnintouch
      WWW_LEARNINTOUCH_NAME: learnintouch
      WWW_LEARNINTOUCH_DOMAIN: dev.learnintouch.com
      WWW_LEARNINTOUCH_SCHEME: http
      STAFF_EMAIL: mittiprovence@yahoo.se
      NODEJS_SOCKET_PORT: 9001
    deploy:
      replicas: 1
      restart_policy:
        condition: any
        delay: 5s
        max_attempts: 3
        window: 10s
    healthcheck:
      test: curl --fail http://127.0.0.1:80/engine/ping.php || exit 1
      interval: 10s
      timeout: 10s
      retries: 3
networks:
  common:
    name: common
  default:
    driver: overlay

在 docker swarm 模式下 traefik 无法(出于某种原因!?)检测服务正在侦听的端口。

因此,每个 docker 服务都需要具有以下形式的标签:

"traefik.http.services.www_learnintouch.loadbalancer.server.port=8080"

--

我在第一遍中错过了 www 服务。

您需要从 www 中删除 ports 部分,因为 traefik 需要处理入口,并成为实际公开这些端口的服务。

然后,你还需要添加 networks: [ common ] 到 traefik,因为它不能在不共享公共网络的情况下进行路由。

最后,traefik 默认公开所有服务,因此将为您的 swarm 上未配置 full/minimal 组 traefik 标签(包括其自身)的任何和所有服务发出错误。出于某种原因,维护人员对这种愚蠢且应该修复的想法充耳不闻。

如果我们解决了这些问题,我建议使用如下所示的 traefik 服务:

version: "3.9"
services:
  traefik:
    image: traefik
    ports:
      - target: 80
        published: 80
        mode: host
      - target: 443
        published: 443
        mode: host
    networks:
      - common
    deploy:
      mode: global
      placement:
        constraints:
          - node.role == manager
      labels:
        - traefik.enable=true
        - traefik.http.services.traefik.loadbalancer.server.port=8080
    command:
      --log.level=DEBUG
      --api.insecure=true
      --providers.docker
      --providers.docker.swarmmode
      --accesslog
      --api.dashboard=true
      --providers.docker.network=common
      --providers.docker.exposedbydefault=false
      --providers.docker.defaultRule=Host(`{{normalize .Name}}.docker.local`)
    volumes:
      - "/var/run/docker.sock:/var/run/docker.sock:ro"

您的 www 服务将是:

 www_learnintouch:
    image: localhost:5000/www.learnintouch
    labels:
      - traefik.enable=true
      - traefik.http.services.www_learnintouch.loadbalancer.server.port=80
    networks:
      common: