基于docker swarm构建一套可以快速伸缩的应用系统。我们的系统并非是微服务架构,考虑到微服务需要的面对的挑战(分布式事务等)太多了,我们仅仅是多个单体服务,服务间的调用很少,服务间均衡负载使用docker的service实现。

可视化管理集群,网关统一输出API,日志统一处理,存储统一处理,网关流量监控,故障转移。

准备工作的内容需要为每台机器都执行一遍。

关闭防火墙

systemctl stop firewalld
systemctl disable firewalld

安全起见,生产环境建议保留防火墙,只开放需要的端口。

修改DNS

vi /etc/resolv.conf 

nameserver修改为114.114.114.114

修改Hostname

给每台机器重新设置一下hostname,node-1、node-2、…..node8
hostnamectl set-hostname node-1 --static

关闭selinux

vim /etc/selinux/config
#重启服务器
reboot 

安装docker-ce

yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
dnf install -y yum-utils device-mapper-persistent-data lvm2
dnf makecache
dnf install https://download.docker.com/linux/centos/7/x86_64/stable/Packages/containerd.io-1.2.6-3.3.el7.x86_64.rpm
# centos8默认自带podman,因此会冲突,需要加上参数自动替换
dnf install docker-ce docker-ce-cli --allowerasing
# 验证一下是否安装成功
docker version
# 开机自启
systemctl enable docker

修改docker配置文件

vim /etc/docker/daemon.json 

配置内容:

"registry-mirrors": [ "https://docker.mirrors.ustc.edu.cn" "dns":["114.114.114.114"]

主要是加速docker镜像拉取。

管理节点必须是奇数个

  • 172.16.113.4
  • 172.16.113.5
  • 172.16.113.6
  • 172.16.113.7
  • 172.16.113.8
  • 172.16.113.9
  • 172.16.113.10
  • 172.16.113.11
  • 172.16.113.12
  • 初始化swarm集群

    我们在172.16.113.4上执行初始化命令

    docker swarm init --listen-addr 0.0.0.0:2377 --cert-expiry 87600h0m0s --task-history-limit 1
    

    然后我们在每个工作节点、运维节点、网关节点上运行

    # 该命令在初始化完成后会打印在控制台中,后续也可以在管理节点上执行docker swarm join-token worker来获取
    docker swarm join --token SWMTKN-1-5ua3tzuqsglxry0kwb7tg3b2mtiqfg40dwt3na0zwa322tkdfn-03essdmekuv68m4s9l7kv6rud 172.16.113.4:2377
    

    然后我们在剩下的每个管理节点上运行

    # 在管理节点上运行docker swarm join-token manager来获取
    docker swarm join --token SWMTKN-1-5ua3tzuqsglxry0kwb7tg3b2mtiqfg40dwt3na0zwa322tkdfn-c25ey7vpenvhz4glf8vegnj26 172.16.113.4:2377
    
    # 在管理节点上,应该列出6个节点
    docker node ls
    

    部署Portainer-ce

    Portainer我们不需要安装成集群,我们安装到运维节点上。

    在运维节点上

    docker network create \
     --driver overlay \
     --attachable \
     --subnet 10.12.0.0/24 \
     portainer_agent_network
    

    安装portainer-ce

    docker run -d \
    -p 8000:8000 \
    -p 9000:9000 \
    --name=portainer \
    --network=portainer_agent_network \
    --restart=always \
    -v /var/run/docker.sock:/var/run/docker.sock \
    -v /data/portainer:/data \
    portainer/portainer-ce
    

    安装portainer-agent

    docker service create \
        --name portainer_agent \
        --network portainer_agent_network \
        --mode global \
        --constraint 'node.platform.os == linux' \
        --mount type=bind,src=//var/run/docker.sock,dst=/var/run/docker.sock \
        --mount type=bind,src=//var/lib/docker/volumes,dst=/var/lib/docker/volumes \
        portainer/agent
    

    配置portainer-ce

    用浏览器打开运维节点IP:9000,登录完成后,如下配置即可。

    部署traefik网关

    创建应用网络

    在管理节点上运行

    docker network create \
     --driver overlay \
     --attachable \
     --subnet 10.100.0.0/24 \
     application_net
    

    创建配置文件

    ################################################################
    # Configuration sample for Traefik v2.
    # For Traefik v1: https://github.com/traefik/traefik/blob/v1.7/traefik.sample.toml
    ################################################################
    ################################################################
    # Global configuration
    ################################################################
    [global]
      checkNewVersion = true
      sendAnonymousUsage = true
    ################################################################
    # Entrypoints configuration
    ################################################################
    # Entrypoints definition
    # Optional
    # Default:
    [entryPoints]
      [entryPoints.web]
        address = ":80"
      [entryPoints.websecure]
        address = ":443"
    ################################################################
    # Traefik logs configuration
    ################################################################
    # Traefik logs
    # Enabled by default and log to stdout
    # Optional
    [log]
      # Log level
      # Optional
      # Default: "ERROR"
      # level = "DEBUG"
      # Sets the filepath for the traefik log. If not specified, stdout will be used.
      # Intermediate directories are created if necessary.
      # Optional
      # Default: os.Stdout
      filePath = "log/traefik.log"
      # Format is either "json" or "common".
      # Optional
      # Default: "common"
      # format = "json"
    ################################################################
    # Access logs configuration
    ################################################################
    # Enable access logs
    # By default it will write to stdout and produce logs in the textual
    # Common Log Format (CLF), extended with additional fields.
    # Optional
    [accessLog]
      # Sets the file path for the access log. If not specified, stdout will be used.
      # Intermediate directories are created if necessary.
      # Optional
      # Default: os.Stdout
      filePath = "/log/log.txt"
      # Format is either "json" or "common".
      # Optional
      # Default: "common"
      # format = "json"
    ################################################################
    # API and dashboard configuration
    ################################################################
    # Enable API and dashboard
    [api]
      # Enable the API in insecure mode
      # Optional
      # Default: false
      insecure = true
      # Enabled Dashboard
      # Optional
      # Default: true
      dashboard = true
    ################################################################
    # Ping configuration
    ################################################################
    # Enable ping
    [ping]
      # Name of the related entry point
      # Optional
      # Default: "traefik"
      # entryPoint = "traefik"
    ################################################################
    # Docker configuration backend
    ################################################################
    # Enable Docker configuration backend
    [providers.docker]
      # Docker server endpoint. Can be a tcp or a unix socket endpoint.
      # Required
      # Default: "unix:///var/run/docker.sock"
      swarmMode = true
      endpoint = "ssh://root@172.16.113.4"
      network = "application_net"
      # Default host rule.
      # Optional
      # Default: "Host(`{{ normalize .Name }}`)"
      # defaultRule = "Host(`{{ normalize .Name }}.docker.localhost`)"
      # Expose containers by default in traefik
      # Optional
      # Default: true
      # exposedByDefault = false
    #[metrics]
    #  [metrics.influxDB] 
    #    address = "influxdb:8089"
    #    protocol = "udp"
    #    database = "traefik"
    

    注意:配置文件中的endpoint应该任意2个管理节点的ip,这里利用ssh保障docker access api的安全。

    SSH免密登录配置

    每个管理节点都需要支持ssh免密登录,然后把公钥存放到网关节点上,这样网关节点就不需要密码就能ssh到管理节点上。免密登录的配置请自己查资料了。

    创建traefik容器

    在2台网关节点上都要运行,创建2个网关入口,以保障高可用(可以使用keepalived来实现vIP漂移)。

    docker run -d -p 81:8080 -p 80:80 \
    -v /data/traefik/conf/traefik.toml:/etc/traefik/traefik.toml \
    --name=traefik \
    --network=application_net \
    -v /root/.ssh:/root/.ssh \
    traefik:v2.4.8
    

    到这一步,portainer上应该可以看到运行traefik的容器了。

    这步很关键!(安装ssh)

    目前traefik其实还没正确连接到swarm集群。ok! 我们在portainer上进入traefik的容器,然后运行

    apk update
    apk add openssh
    

    等待安装完成后,traefik就能自动连接上swarm集群,至此集群和网关就搭建完成了。

    这里使用nginx来充当应用。 在任何一台管理节点上运行

     docker service create \
     --replicas 10 \
     --publish 8080:80 \
     --network=application_net \
     --label="traefik.http.routers.nginx-service.rule=Host(\`nb.io\`)" \
     --label="traefik.http.services.nginx-service.loadbalancer.server.port=80" \
     --name nginx nginx
    

    Host中的nb.io实际应该换成自己的域名,没有域名自己想办法吧,可以使用docker镜像sameersbn/bind搭建一个私有dns服务器。 等待完成后,我们可以在traefik的webUI上看到:

    现在你可以访问nb.io来访问nginx服务集群了。

    约束应用部署的节点

    现在在swarm集群中部署服务,服务的容器会跑到运维节点和网关节点,其实这不是我们想要的。

    给节点打标签

    在portainer的swarm菜单下为每个节点进行打标签,或者使用命令,该命令需要在管理节点执行

    docker node update —label-add role=application node-1
    docker node update —label-add role=application node-2
    docker node update —label-add role=application node-3
    docker node update —label-add role=application node-4
    docker node update —label-add role=application node-5
    docker node update —label-add role=application node-6
    docker node update —label-add role=gateway node-7
    docker node update —label-add role=gateway node-8
    docker node update —label-add role=sys node-9
    

    调整服务创建命令

     docker service create \
     --replicas 10 \
     --publish 8080:80 \
     --network=application_net \
     --constraint 'node.labels.role == application' \
     --label="traefik.http.routers.nginx-service.rule=Host(\`nb.io\`)" \
     --label="traefik.http.services.nginx-service.loadbalancer.server.port=80" \
     --name nginx nginx
    

    现在应用服务只会部署在node-1到node-6节点了。