记一次跨云服务商&跨网络搭建 Docker Swarm 集群 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
juzisang
V2EX    分享发现

记一次跨云服务商&跨网络搭建 Docker Swarm 集群

  •  
  •   juzisang 2021-04-23 15:26:03 +08:00 4373 次点击
    这是一个创建于 1680 天前的主题,其中的信息可能已经有所发展或是发生改变。

    跨云服务商&网络 Docker Swarm 集群搭建

    前言

    由于隔一段时间各个云服务商都会搞活动,然后就会剁手入一个,手上已经有 4 个云服务器了。

    • 阿里云轻量一台

    • 腾讯云 ESC 一台

    • 腾讯云轻量两台

    然后家里有用 PVE 搞了个虚拟机化,来运行软路由,NAS 之类的家庭服务。由于有高配强迫症,组了台 16 核 32 线程的服务器,导致性能严重过剩,就琢磨着能不能和云服务器组网,来组建一个小集群

    最终选定方案是用 zerotier 搭建 VPN 组内网,docker swarm 来组建集群,基于此安装管理面板,以及 https 证书,网关服务日志记录搜索之类的,当然还有服务滚动更新,期间遇到一些坑,记录一下

    Zerotier 搭建内网

    首先请去 zerotier 组成账号,以及创建一个网络,这里网上教程很多,搜一下就有了。我给个简单的安装以及加入网络的代码。

    # 安装 curl -s https://install.zerotier.com | sudo bash # 加入 zerotier 后台自己创建的网络 sudo zerotier-cli join xxx 

    安装 Docker 并配置加速镜像源

    可以按照 腾讯云 的文档,来配置,这里就不赘述了

    https://cloud.tencent.com/document/product/1207/45596?from=information.detail.腾讯云加速 docker

    初始化集群管理节点&加入 Worker

    初始化集群 Manager

    注意把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 中的容器,这就失去了组建集群的意义了。

    这是需要特别注意,踩了好久最后通过搜索才发现,我一度以为是不是这是厂商为了卖自己的集群服务,禁止了用户自建的可能。来源可以参考

    加入 Worker

    在其他服务器中运行,加入到集群当中

    # 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 状态

    Node 提权降权操作

    我将我所有的云服务器都作为流量的出入口节点,家里虚拟机的流量将会通过域名指定的云服务器来对外开放。 我是用的是 traefik 作为网关及容器内的负载均衡, 由于 treafik 需要监听 docker 的 event 事件,节点必须是 manager 才能有权限,所以我将所有的云服务器都提升为 manager

    # 将 worker 节点升级为 manager 节点 sudo docker node promote swarm-node1 # 将 manager 节点降级为 worker 节点 sudo docker node demote swarm-node1 

    创建 Swarm 网络

    所有需要跨 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 

    Traefik网关及负载均衡

    由于配置过多,我这里直接贴上我现在的配置+注释,这是 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 打上标签
    # 给 node 打上对应标签 sudo docker node update --label-add role=node1 swarm-node1 # 删除标签 sudo docker node update --label-rm node1 swarm-node1 

    安装 swarmpit 面板

    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 

    安装 splunk

    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 
    • 上传 docker 容器日志
     # 查看上面 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 

    补充

    13 条回复    2023-02-10 16:09:57 +08:00
    maniaccn
        1
    maniaccn  
       2021-04-23 16:36:04 +08:00
    NB
    akirarika
        2
    akirarika  
       2021-04-23 16:54:05 +08:00 via Android
    好耶!是橘子
    learningman
        3
    learningman  
       2021-04-23 16:55:15 +08:00 via Android
    zerotier 为啥不自建一个 moon
    juzisang
        4
    juzisang  
    OP
       2021-04-23 16:57:49 +08:00
    @learningman #3 因为我家里的虚拟机,云服务器都有公网,用 zerotier 就第一次延迟高,后面就直接直通了。所以暂时不需要 moon,后面看需要在自建吧
    juzisang
        5
    juzisang  
    OP
       2021-04-23 16:58:01 +08:00
    @akirarika #2 啊这
    maxat20xx
        6
    maxat20xx  
       2021-04-23 17:10:43 +08:00 via Android
    …还可以这么玩,受教了
    Flamie
        7
    Flamie  
       2021-04-23 17:18:18 +08:00
    谢谢楼主,学习了!
    zvcs
        8
    zvcs  
       2021-04-23 17:26:28 +08:00 via iPhone
    非常值得学习。
    ringwraith
        9
    ringwraith  
       2021-04-23 17:43:33 +08:00
    学习一下
    Skmgo
        10
    Skmgo  
       2021-04-24 01:21:23 +08:00
    正好用的上,多云环境冗余。
    labulaka521
        11
    labulaka521  
       2021-04-24 16:13:48 +08:00
    嘿嘿 我搭了一个 k3s 集群
    bpf2049
        12
    bpf2049  
       2021-04-25 09:44:27 +08:00
    学习大佬操作~
    craftx
        13
    craftx  
       2023-02-10 16:09:57 +08:00
    很全啊。但貌似 docker stack deploy 不支持 compose 中的 configs ,正在找解决方案
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     844 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 31ms UTC 20:50 PVG 04:50 LAX 12:50 JFK 15:50
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86