我有一个小型 VPS,上面有各种服务,例如 WordPress 安装和一些 Web 应用程序。一段时间以来,我已将所有服务作为 Docker 容器运行。由于我有指向此框的各种域和子域,因此我使用前端代理 Traefik 来捕获 Web 端口,然后在 Docker 网络中对其进行内部路由。
我像这样启动 Traefik:
#!/bin/bash
# Removes the restart policy from previous containers
CONTAINER_LABEL=traefik-instance
../../bin/remove-restart.sh $CONTAINER_LABEL
mkdir --parents /var/log/traefik
mkdir --parents /etc/letsencrypt-traefik
docker run \
--label $CONTAINER_LABEL \
--publish 80:80 \
--publish 443:443 \
--volume $PWD/traefik.toml:/etc/traefik/traefik.toml \
--volume $PWD/rules:/etc/traefik/rules \
--volume /etc/letsencrypt-traefik:/etc/letsencrypt-traefik \
--volume /var/log/traefik:/log \
--network dockernet \
--detach \
--restart always \
traefik:1.6
这一切都运行得很好。我最近发现了 Docker Swarm,并希望将所有容器转换为服务,这将为我提供复制服务、滚动更新和零停机部署。但是,我想逐个进行更改,以便 Traefik 可以路由到 Swarm 服务和普通(非 Swarm)容器。
因此,为了将 Traefik 作为服务启动,我现在执行以下操作。您会注意到,出于测试目的,我使用了非标准端口:
#!/bin/bash
# Using "traefik2" while I am experimenting with multiple services
mkdir --parents /var/log/traefik2
mkdir --parents /etc/letsencrypt-traefik
docker service create \
--publish 8080:80 \
--publish 8443:443 \
--mount type=bind,source=$PWD/traefik.toml,target=/etc/traefik/traefik.toml \
--mount type=bind,source=$PWD/rules,target=/etc/traefik/rules \
--mount type=bind,source=/etc/letsencrypt-traefik,target=/etc/letsencrypt-traefik \
--mount type=bind,source=/var/log/traefik2,target=/log \
--network traefiknet \
traefik:1.6
当指向出现在同一网络上的 Swarm 网络服务时,这也有效。
因此,我有两个 Docker 网络(在 Docker 为自己创建的各种默认网络之间),如下所示:
root@box:~/docker# docker network ls
NETWORK ID NAME DRIVER SCOPE
1aa479f13faa dockernet bridge local
k71hpg1n0lo9 traefiknet overlay swarm
这样,我就有了一个可以查看 Docker 容器的 Traefik 容器和一个可以查看 Swarm 服务的 Traefik 服务。但是,它们彼此之间无法查看。
为了尝试解决这个问题,我尝试将 Docker 网络添加到 Traefik Swarm 服务的启动中:
--network dockernet \
换句话说,我希望这项服务能够同时连接到桥接网络(旧网络)和覆盖网络(新网络)。不幸的是,我得到了以下信息:
守护进程的错误响应:网络 dockernet 无法与服务一起使用。只能使用群集范围内的网络,例如使用覆盖驱动程序创建的网络。
我的新服务是否有办法连接到旧网络,或者我的旧容器是否有办法连接到新网络?我尝试搜索错误,但似乎没有太多提及它;我想知道是否还没有很多人遇到过 Swarm 的这种极端情况。
(当然,对我来说,一个解决方案就是将所有容器转换为服务,但为了避免大幅度的改变,如果可能的话,我宁愿慢慢地进行)。
尝试可连接网络
然后我删除了我的服务并尝试了这个:
docker network rm traefiknet
docker network create driver=overlay --attachable traefiknet
然后我重新创建了 Traefik 服务,它启动了。显然它仍在工作,因为它将流量路由到也加入了 traefiknet 覆盖的服务。
但是,我创建了一个非服务容器,并将其专门连接到traefiknet
,而--network-alias
我使用该容器创建的 无法被服务看到。奇怪的是,如果我进入这个非 Swarm 容器,它能对 Swarm Traefik 容器执行 ping 操作,这样网络就可以正常工作了。(我尝试创建一个 Alpine shell 服务,连接到traefiknet
,但从这里我无法 ping 通我的非 Swarm 容器的容器名称,也无法 ping 通它的--network-alias
)。
升级 Docker
我尝试将 Docker 从 17.03.2-ce 升级到 18.06.1-ce,因为出现一个短语在手册中表明我的旧 Docker 版本可能是导致问题的原因:
容器与 Swarm 服务之间的通信使用可连接的覆盖网络在独立容器与 Swarm 服务之间建立通信。Docker 17.06 及更高版本支持此功能。
然而,这也无济于事。
答案1
我相信我已经解决了这个问题,尽管还有一些我不明白的地方。为了设置这个答案的上下文,下面是我现在安装 Docker 非 Swarm 容器的方式:
#!/bin/bash
# Save pwd and then change dir to the project root
STARTDIR=`pwd`
cd `dirname $0`/../..
# Removes the restart policy from previous containers
CONTAINER_LABEL=ilovephp-staging
NETWORK_ALIAS=${CONTAINER_LABEL}
./bin/remove-restart.sh $CONTAINER_LABEL
docker run \
--network swarmnet \
--network-alias ${NETWORK_ALIAS} \
--env TUTORIAL_ENVIRONMENT_NAME=staging \
--detach \
--restart always \
ilovephp:2018-08-19
# Go back to original dir
cd $STARTDIR
如您所见,根据我的问题更新,我现在能够将容器放在可连接的 Swarm 网络上。
我发现无法从 Swarm 服务 ping 通此端口的原因是缺少--name
。只要我添加--name
,它就可以访问。但有趣的是,如果我尝试从 Docker 容器 ping 通 Swarm 服务或 Swarm 容器,它会成功:
root@server:~# docker exec -it loving_allen sh
/ # # *** Ping a specific Swarm container ***
/ # ping alpine-swarm.1.9llv2hvv5xnc1c8diuhfh5m09
PING alpine-swarm.1.9llv2hvv5xnc1c8diuhfh5m09 (10.0.1.23): 56 data bytes
64 bytes from 10.0.1.23: seq=0 ttl=64 time=0.516 ms
^C
--- alpine-swarm.1.9llv2hvv5xnc1c8diuhfh5m09 ping statistics ---
1 packets transmitted, 1 packets received, 0% packet loss
round-trip min/avg/max = 0.516/0.516/0.516 ms
/ # # *** Ping the Swarm service ***
/ # ping alpine-swarm
PING alpine-swarm (10.0.1.22): 56 data bytes
64 bytes from 10.0.1.22: seq=0 ttl=64 time=0.376 ms
^C
--- alpine-swarm ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max = 0.137/0.231/0.376 ms
因此,我最初的计划是将 Traefik 实例从容器转换为 Swarm 服务,但这样做行不通,因为无法访问未命名的非 Swarm 容器。我现在的解决方案是先将所有系统从容器转换为服务,然后在完成所有操作后,最后转换 Traefik 实例。这样,我将始终从容器连接到服务,而不是相反。
(我无法添加--name
s,因为如果重新启动 Docker 主机,它将尝试重新创建具有相同名称的容器,并且它们将失败,因为旧容器仍然会具有那些冲突的名称。我无法使用它--rm
来解决这个问题,因为它与 不兼容--restart
!更多详情请见此处)。
我仍然不明白为什么 Docker 生成的容器名称无法从 Swarm 容器中 ping 通,也不明白为什么这--network-alias
也没有帮助。但是,考虑到我的解决方案只需要暂时使用,直到我的所有系统都成为 Swarm 服务,这可能并不重要。