
由于隔一段时间各个云服务商都会搞活动,然后就会剁手入一个,手上已经有 4 个云服务器了。
阿里云轻量一台
腾讯云 ESC 一台
腾讯云轻量两台
然后家里有用 PVE 搞了个虚拟机化,来运行软路由,NAS 之类的家庭服务。由于有高配强迫症,组了台 16 核 32 线程的服务器,导致性能严重过剩,就琢磨着能不能和云服务器组网,来组建一个小集群

最终选定方案是用 zerotier 搭建 VPN 组内网,docker swarm 来组建集群,基于此安装管理面板,以及 https 证书,网关服务日志记录搜索之类的,当然还有服务滚动更新,期间遇到一些坑,记录一下
首先请去 zerotier 组成账号,以及创建一个网络,这里网上教程很多,搜一下就有了。我给个简单的安装以及加入网络的代码。
# 安装 curl -s https://install.zerotier.com | sudo bash # 加入 zerotier 后台自己创建的网络 sudo zerotier-cli join xxx 可以按照 腾讯云 的文档,来配置,这里就不赘述了
https://cloud.tencent.com/document/product/1207/45596?from=information.detail.腾讯云加速 docker
注意把192.168.xxx.xx 替换成你自己 zerotier 后台中的 ip
sudo docker swarm init --advertise-addr=192.168.xxx.xx:2377 --data-path-addr=192.168.xxx.xx --data-path-port 5789 可以注意到我指定了--data-path-addr=192.168.xxx.xx --data-path-port 5789
这是因为云服务的网络也是基于 vxlan, 占用了 docker 默认的 4789 端口,导致如果不指定端口,会导致集群虽然能组建成功,但是 docker 容器之间的网络不通。如加入了同一个 network,node1 中的容器,ping 不通 node2 中的容器,这就失去了组建集群的意义了。
这是需要特别注意,踩了好久最后通过搜索才发现,我一度以为是不是这是厂商为了卖自己的集群服务,禁止了用户自建的可能。来源可以参考
在其他服务器中运行,加入到集群当中
# manager 节点中运行,获取加入集群的命令 sudo docker swarm join-token worker # 在 manager 以外的节点中运行,加入到集群当中 sudo docker swarm join --token xxx 192.168.xxx.xx:2377 在 manager 节点运行 sudo docker node ls 查看加入的 node 状态

我将我所有的云服务器都作为流量的出入口节点,家里虚拟机的流量将会通过域名指定的云服务器来对外开放。 我是用的是 traefik 作为网关及容器内的负载均衡, 由于 treafik 需要监听 docker 的 event 事件,节点必须是 manager 才能有权限,所以我将所有的云服务器都提升为 manager
# 将 worker 节点升级为 manager 节点 sudo docker node promote swarm-node1 # 将 manager 节点降级为 worker 节点 sudo docker node demote swarm-node1 所有需要跨 Node 通信的容器,都需要加入该网络
# 创建一个名为 proxy 的网络 sudo docker network create -d overlay --attachable proxy # 在所有 Node 中都起一个容器 sudo docker service create --mode global --network proxy --name web srampal/nginx-netutils:2 # 在任意节点中获取到 nginx-netutils 容器的 ip sudo docker network inspect proxy "Containers": { "39a532786c2c23a1033f7899afe0973bdac9100191b2077306477129f78eafe4": { "Name": "nginx-netutils.1.atc36jt29aidgbtgqx95hfefu", "EndpointID": "8368996ff2921687ec57ce51412a987c95390b5cb9bd757c6094a74e48ca6640", "MacAddress": "02:42:0a:00:01:68", "IPv4Address": "10.0.1.104/24", "IPv6Address": "" } } # 在其他节点的容器中 ping 上面的 ip,检测网络是否通 sudo docker exec xxxId ping 10.0.1.104 由于配置过多,我这里直接贴上我现在的配置+注释,这是 Treafik 的后台面板

version: '3.4' services: proxy: image: traefik:v2.4 environment: - TZ=Asia/Shanghai # 用于 acme.sh 获取 https 证书 - ALICLOUD_ACCESS_KEY=xxx - ALICLOUD_SECRET_KEY=xxx command: # 开启监听 Docker 事件 - '--providers.docker.endpoint=unix:///var/run/docker.sock' # 开启集群模式 - '--providers.docker.swarmMode=true' # 忽略没有 traefik.enable=true 标签的容器 - '--providers.docker.exposedbydefault=false' # 使用 proxy 网络,proxy 为上面创建的 swarm overlay 网络 - '--providers.docker.network=proxy' # 定一个一个名为 http 的入口,端口为 80 - '--entrypoints.http.address=:80' - '--entrypoints.https.address=:443' # 开启 https 入口的 tls - '--entrypoints.https.http.tls=true' # 定义 mysql 的入口 - '--entrypoints.mysql.address=:3306' - '--api' # 开启请求日志,明确不使用 UTC,采用容器时区 - '--accesslog=true' - '--accesslog.fields.names.StartUTC=drop' # - '--accesslog.filepath=/var/log/traefik/access.log' # - '--log.level=DEBUG' # - '--log.filePath=/var/log/traefik/traefik.log' # 具体域名证书的申请,域名必须指向当前机器 # - '--certificatesresolvers.letsencryptresolver.acme.httpchallenge=true' # - '--certificatesresolvers.letsencryptresolver.acme.httpchalenge.entrypoint=web' # 泛域名证书 - '--certificatesresolvers.letsencryptresolver.acme.dnschallenge.provider=alidns' - '--certificatesresolvers.letsencryptresolver.acme.email=xxx@gmail.com' - '--certificatesresolvers.letsencryptresolver.acme.storage=/www/config/acme.json' # 使用 letsencrypt 的测试环境 # - '--certificatesresolvers.letsencryptresolver.acme.caserver=https://acme-staging-v02.api.letsencrypt.org/directory' ports: # 为了解决流量在 node 节点中跳两次的问题 # https://github.com/traefik/traefik/issues/1880 - target: 80 published: 80 protocol: tcp mode: host - target: 443 published: 443 protocol: tcp mode: host - target: 3306 published: 3306 protocol: tcp mode: host # 这样写会导致 node1 入口的流量被 docker 负载均衡到 node2,就算服务只在 node1 上部署 # - 80:80 # - 443:443 # - 3306:3306 volumes: - /var/run/docker.sock:/var/run/docker.sock:ro # letsencrypt-config 为远程卷,为了解决多机共享证书 - letsencrypt-config:/www/config/:ro # 将本机时区映射到容器,解决日志时间错乱的问题 - /etc/timezone:/etc/timezone:ro - /etc/localtime:/etc/localtime:ro # 日志上报到 splunk logging: driver: splunk options: splunk-token: xxxxx-xxx-xxxx-xxx-xxxxx splunk-url: http://192.168.xxx.xx:8088/ splunk-format: raw networks: - proxy deploy: # 部署到所有节点当中 mode: global update_config: # 更新时将会一个一个更新 parallelism: 1 # 更新失败将会回滚 failure_action: rollback restart_policy: # 如果不是非 0 状态退出,这回执行重启 condition: on-failure # 重启间隔时间 delay: 5s # 第一次启动失败之后,继续重试 3 次 max_attempts: 3 # 检测容器是否启动成功的等待时间 window: 120s placement: # 只在 manager 节点中部署 constraints: [node.role == manager] labels: # 开启 traefik 监听 - 'traefik.enable=true' # 定一个名为 traefik 的节点,入口为上面定义的 http 端口 80 - 'traefik.http.routers.traefik.entrypoints=http' # 路由到 traefik.xxx.com - 'traefik.http.routers.traefik.rule=Host(`traefik.xxx.com`)' # 定义一个名为 traefik-https-redirect 的中间件,将会吧 http 302 到 https - 'traefik.http.middlewares.traefik-https-redirect.redirectscheme.scheme=https' # 使用 traefik-https-redirect - 'traefik.http.routers.traefik.middlewares=traefik-https-redirect' # 定一个名为 traefik-secure 的节点,入口为上面定义的 https 端口 443 - 'traefik.http.routers.traefik-secure.rule=Host(`traefik.xxx.com`)' - 'traefik.http.routers.traefik-secure.entrypoints=https' # 使用内置中间件 authtraefik,访问需要账号密码 - 'traefik.http.routers.traefik-secure.middlewares=authtraefik' # 下面的设置将会申请 返回码证书 - 'traefik.http.routers.traefik-secure.tls=true' - 'traefik.http.routers.traefik-secure.tls.certresolver=letsencryptresolver' - 'traefik.http.routers.traefik-secure.tls.domains[0].main=xxx.com' - 'traefik.http.routers.traefik-secure.tls.domains[0].sans=*.xxx.com' # 使用 traefik 内置的服务 - 'traefik.http.routers.traefik-secure.service=api@internal' # Swarm 模式下必须手动指定对外端口 - 'traefik.http.services.traefik-secure.loadbalancer.server.port=80' # 设置 authtraefik 中间件密码,所有的单个 $ 需要替换为 $$ ,生成密码 echo $(htpasswd -nb user yourpassword) | sed -e s/\\$/\\$\\$/g - 'traefik.http.middlewares.authtraefik.basicauth.users=user:&&xxxxx&&xxxx' # 使用外部手动创建的 proxy 网络 networks: proxy: external: true volumes: # sudo docker plugin install vieux/sshfs 安装。注意,所有 node 都要执行安装 # 在集群中共享数据,比如证书 letsencrypt-config: driver: vieux/sshfs:latest driver_opts: sshcmd: '[email protected]:/home/' password: 'xxxx' version: '3.4' services: helloworld: image: traefik/whoami networks: - proxy deploy: labels: - 'traefik.enable=true' - 'traefik.http.routers.helloworld.entrypoints=http' - 'traefik.http.routers.helloworld.rule=Host(`helloworld.xxx.top`)' - 'traefik.http.middlewares.helloworld-https-redirect.redirectscheme.scheme=https' - 'traefik.http.routers.helloworld.middlewares=helloworld-https-redirect' - 'traefik.http.routers.helloworld-secure.entrypoints=https' - 'traefik.http.routers.helloworld-secure.rule=Host(`helloworld.xxx.top`)' - 'traefik.http.routers.helloworld-secure.tls=true' - 'traefik.http.routers.helloworld-secure.service=helloworld' # 注意,Swarm 模式下必须手动指定对外端口 - 'traefik.http.services.helloworld.loadbalancer.server.port=80' networks: proxy: external: true appserver image: juzisang/xxx networks: - proxy deploy: # 生成的副本数量 replicas: 2 # 升级时的配置 update_config: # 每次更新两个 parallelism: 2 # 每组更新的间隔时间 delay: 10s # 升级失败则回滚 pause rollback continue,默认 pause failure_action: rollback resources: # 限制内存最高占用 1024M,单核 cpu 的 50% limits: cpus: '0.50' memory: 1024M # 最低保留 512M 内存,单核 0.25 reservations: cpus: '0.25' memory: 512M placement: constraints: # 部署到管理机 - 'node.role == worker' # 部署到对应标签的 - 'node.labels.role==node1' # 容器异常退出之后的重启策略 restart_policy: # 以非 0 返回值退出 condition: on-failure # 间隔 5s 重启 delay: 5s # 重试 3 次 max_attempts: 3 # 等待至多 120s 来检测是否启动成功 window: 120s # 给 node 打上对应标签 sudo docker node update --label-add role=node1 swarm-node1 # 删除标签 sudo docker node update --label-rm node1 swarm-node1 swarmpit 可以用于监控集群状态,操纵节点回滚,升级,已经查看日志等操作

这是我的配置,也是基于官方 docker-compose.yml 基础,加上了 traefik 的配置
version: '3.3' services: app: image: swarmpit/swarmpit:latest environment: - TZ=Asia/Shanghai - SWARMPIT_DB=http://db:5984 - SWARMPIT_INFLUXDB=http://influxdb:8086 volumes: - /etc/timezone:/etc/timezone:ro - /etc/localtime:/etc/localtime:ro - /var/run/docker.sock:/var/run/docker.sock:ro healthcheck: test: ['CMD', 'curl', '-f', 'http://localhost:8080'] interval: 60s timeout: 10s retries: 3 networks: - proxy deploy: resources: limits: cpus: '0.50' memory: 1024M reservations: cpus: '0.25' memory: 512M placement: constraints: - node.labels.role==node2 labels: - 'traefik.enable=true' - 'traefik.http.routers.swarmpit.entrypoints=http' - 'traefik.http.routers.swarmpit.rule=Host(`swarmpit.xxx.com`)' - 'traefik.http.middlewares.swarmpit-https-redirect.redirectscheme.scheme=https' - 'traefik.http.routers.swarmpit.middlewares=swarmpit-https-redirect' - 'traefik.http.routers.swarmpit-secure.entrypoints=https' - 'traefik.http.routers.swarmpit-secure.rule=Host(`swarmpit.xxx.com`)' - 'traefik.http.routers.swarmpit-secure.tls=true' - 'traefik.http.routers.swarmpit-secure.service=swarmpit' - 'traefik.http.services.swarmpit.loadbalancer.server.port=8080' db: image: couchdb:2.3.0 environment: - TZ=Asia/Shanghai volumes: - /etc/timezone:/etc/timezone:ro - /etc/localtime:/etc/localtime:ro - db-data:/opt/couchdb/data networks: - proxy deploy: placement: constraints: - node.labels.role==node2 resources: limits: cpus: '0.30' memory: 256M reservations: cpus: '0.15' memory: 128M influxdb: image: influxdb:1.7 environment: - TZ=Asia/Shanghai volumes: - /etc/timezone:/etc/timezone:ro - /etc/localtime:/etc/localtime:ro - influx-data:/var/lib/influxdb networks: - proxy deploy: placement: constraints: - node.labels.role==node2 resources: limits: cpus: '0.60' memory: 512M reservations: cpus: '0.30' memory: 128M agent: image: swarmpit/agent:latest environment: - TZ=Asia/Shanghai - DOCKER_API_VERSION=1.35 volumes: - /etc/timezone:/etc/timezone:ro - /etc/localtime:/etc/localtime:ro - /var/run/docker.sock:/var/run/docker.sock:ro networks: - proxy deploy: mode: global labels: swarmpit.agent: 'true' resources: limits: cpus: '0.10' memory: 64M reservations: cpus: '0.05' memory: 32M networks: proxy: external: true volumes: db-data: driver: local influx-data: driver: local version: '3.4' services: splunk: image: splunk/splunk:latest networks: - proxy environment: - TZ=Asia/Shanghai - SPLUNK_START_ARGS=--accept-license - SPLUNK_PASSWORD=xxxx # - SPLUNK_UPGRADE=true volumes: - /etc/timezone:/etc/timezone:ro - /etc/localtime:/etc/localtime:ro # 导出配置,防止重启丢配置 - splunk-var:/opt/splunk/var - splunk-etc:/opt/splunk/etc ports: # 用于外部服务上传日志 - target: 8088 published: 8088 protocol: tcp mode: host deploy: replicas: 1 restart_policy: condition: on-failure delay: 5s max_attempts: 3 window: 120s placement: constraints: - node.labels.role==node3 labels: - 'traefik.enable=true' - 'traefik.http.routers.splunk.entrypoints=http' - 'traefik.http.routers.splunk.rule=Host(`splunk.xxx.com`)' - 'traefik.http.middlewares.splunk-https-redirect.redirectscheme.scheme=https' - 'traefik.http.routers.splunk.middlewares=splunk-https-redirect' - 'traefik.http.routers.splunk-secure.entrypoints=https' - 'traefik.http.routers.splunk-secure.rule=Host(`splunk.xxx.com`)' - 'traefik.http.routers.splunk-secure.tls=true' - 'traefik.http.routers.splunk-secure.service=splunk' - 'traefik.http.services.splunk.loadbalancer.server.port=8000' networks: proxy: external: true volumes: splunk-var: driver: local splunk-etc: driver: local # 查看上面 Traefik 的配置 logging: driver: splunk options: splunk-token: xxxx-xxxx-xxxx-xxxx-xxxx splunk-url: http://192.168.xxx.xxx:8088/ splunk-format: raw sudo docker stack deploy -c proxy-compose.yml proxy sudo docker stack deploy -c splunk-compose.yml splunk sudo docker stack deploy -c swarmpit-compose.yml swarmpit 1 maniaccn 2021-04-23 16:36:04 +08:00 NB |
2 akirarika 2021-04-23 16:54:05 +08:00 via Android 好耶!是橘子 |
3 learningman 2021-04-23 16:55:15 +08:00 via Android zerotier 为啥不自建一个 moon |
4 juzisang OP @learningman #3 因为我家里的虚拟机,云服务器都有公网,用 zerotier 就第一次延迟高,后面就直接直通了。所以暂时不需要 moon,后面看需要在自建吧 |
6 maxat20xx 2021-04-23 17:10:43 +08:00 via Android …还可以这么玩,受教了 |
7 Flamie 2021-04-23 17:18:18 +08:00 谢谢楼主,学习了! |
8 zvcs 2021-04-23 17:26:28 +08:00 via iPhone 非常值得学习。 |
9 ringwraith 2021-04-23 17:43:33 +08:00 学习一下 |
10 Skmgo 2021-04-24 01:21:23 +08:00 正好用的上,多云环境冗余。 |
11 labulaka521 2021-04-24 16:13:48 +08:00 嘿嘿 我搭了一个 k3s 集群 |
12 bpf2049 2021-04-25 09:44:27 +08:00 学习大佬操作~ |
13 craftx 2023-02-10 16:09:57 +08:00 很全啊。但貌似 docker stack deploy 不支持 compose 中的 configs ,正在找解决方案 |