序言

任何相对完整的应用服务都不可能是由单一的程序来完成支持,计划使用Docker来布署的服务更是这么。小型服务须要进行分拆,产生微服务集群方能提高其稳定性和可维护性。本篇随笔将对DockerCompose和DockerSwarm的原理和配置做整理归纳,并分享其使用经验。

1.YAML简介

DockerCompose的配置文件采用YAML格式,因而有必要在正文之前简略说明下。YAML是一门专门拿来写配置文件的语言,设计目标就是便捷读写linux vps,其实质上是一种通用的数据串行化格式,基本句型规则如下:

YAML支持的数据结构有三种:

# ex1
  - cat
  - dog
  - bird
  
# ex2
  -
    - cat
    - dog
    - bird
    
# ex3
  animal: [cat, dog, bird]

2.DockerCompose2.1安装与简介

Docker可以极为便捷地布署单个服务,但这时侯我们须要一个工具来整合Docker的功能,使之才能更方便地去管理整个微服务集群的布署和迁移,DockerCompose正是应此而生。他是由Python编撰的程序,才能按照指令结合配置文件转换成对应的DockerAPI的操作,并直接彰显到DockerDaemon中,这就取代我们完成了重复输入复杂指令的过程,主要功能可分为以下两点:

安装命令:

curl -L https://github.com/docker/compose/releases/download/1.19.0/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose && chmod +x /usr/local/bin/docker-compose

2.2配置参数

DockerCompose的核心就是其配置文件,采用YAML格式,默认为docker-compose.ymldocker compose和swarm,参数解读可查阅“官方文档”,以下只做一个常规摘要。

services

所有服务的根节点。

image

指定服务的镜像名,若本地不存在,则Compose会去库房拉取这个镜像:

services:
  web:
    image: nginx

ports

端口映射,例:

ports:
  - "80:80"
  - "81:81"

volumes

挂载主机目录linux运维博客,其中ro表示只读,例:

volumes:
  - "/etc/nginx/www:/www"
  - "/var/run/docker.sock:/tmp/docker.sock:ro"

大多数情况下集群中布署的应当都是无状态服务,服务可复制且不固定在某一台宿主机,所以挂载的数据卷最好应该与宿主机脱离关系,例:

  web:
  services:
    image: nginx
    volumes:
      - type: volume
        source: logs
        target: /mnt
        volume:
          nocopy: true
          
volumes:
  logs:
    driver_opts:
      type: nfs

docker-swarm集群管理_docker compose和swarm_docker-compose原理与配置

o: addr=***.cn-hangzhou.nas.aliyuncs.com,rw device: ":/"

其实,这些情况下最好是优先创建数据卷,后在配置文件中引用,例:

docker volume create --driver local 
    --opt type=nfs 
    --opt o=addr=***.cn-hangzhou.nas.aliyuncs.com,rw 
    --opt device=:/ 
    logs

volumes:
  logs:
    external: true

若必须挂载集群中一台宿主机的目录作为数据卷,则要安装一个docker插件:

docker plugin install vieux/sshfs
# 若配置了密钥对则可省略 password 参数
docker volume create 
  -d vieux/sshfs 
  --name sshvolume 
  -o "sshcmd=user@1.2.3.4:/remote" 
  -o "password=$(cat file_containing_password_for_remote_host) 
sshvolume

networks

配置服务间的网络互通与隔离,例:

services:
  web:
    image: nginx
    networks:
      - proxy
      - youclk
networks:
  youclk:
    external: true
  proxy:
    external: true

secrets

配置服务密码访问,例:

services:
  redis:
    image: redis:latest
    deploy:
      replicas: 1
    secrets:
      - my_secret
      - my_other_secret
secrets:
  my_secret:
    file: "./my_secret.txt"
  my_other_secret:
    external: true

docker secret create [OPTIONS] SECRET [file|-]
echo "admin:password" | docker secret create my_secret -

docker compose和swarm_docker-swarm集群管理_docker-compose原理与配置

docker secret create my_secret ./secret.json

healthcheck

健康检测,这个十分有必要,等服务打算好之后再上线,防止更新过程中出现短暂的难以访问。

healthcheck:
  test: ["CMD", "curl", "-f", "http://localhost/alive"]
  interval: 5s
  timeout: 3s

虽然大多数情况下健康检测的规则就会写在Dockerfile中:

FROM nginx
RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*
HEALTHCHECK --interval=5s --timeout=3s CMD curl -f http://localhost/alive || exit 1

depends_on

依赖的服务,优先启动,例:

depends_on:
  - redis

environment&env_file

设置环境变量和指定环境变量的文件,例:

environment:
  - VIRTUAL_HOST=test.youclk.com
env_file:
  - ./common.env

deploy

布署相关的配置都在这个节点下,例:

deploy:
  mode: replicated
  replicas: 2
  restart_policy:
    condition: on-failure
    max_attempts: 3
  update_config:
    delay: 5s
    order: start-first # 默认为 stop-first,推荐设置先启动新服务再终止旧的
  resources:
    limits:
      cpus: "0.50"
      memory: 1g

deploy:
  mode: global # 不推荐全局模式(仅个人意见)。
  placement:
    constraints: [node.role == manager]

若非特殊服务,以上各节点的配置就能满足大部份布署场景了。

3.Swarm

Docker默认包含了Swarm,因而可以直接使用,初始化命令:dockerswarminit,此时将会默认当前节点为Leader,以下命令为查看token:dockerswarmjoin-token(worker|manager),其他节点可以用manager或则worker的身分加入到当前集群,例:

docker swarm join --token SWMTKN-1-3pu6hszjas19xyp7ghgosyx9k8atbfcr8p2is99znpy26u2lkl-1awxwuwd3z9j1z3puu7rcgdbx 172.17.0.2:2377

执行dockerswarmleave脱离集群。

以下各节点常规操作命令,比较简单,就不解释了:

4.应用案例

集群最擅长的就是解决多服务问题,只要在同一network之下,服务之间默认可以直接通过service_name互通有无。但为了防止混乱,各服务与外部的通讯最好统一交给一个反向代理服务转发。因对nginx比较熟悉,所以我最初选择的代理是“jwilder/nginx-proxy”:

server
{
    listen 80;
    server_name localhost;
    location /alive {
        return 200;
    }
}
server {
    listen  81;
    return  301 https://$host$request_uri;
}

FROM jwilder/nginx-proxy
ADD ./src /etc/nginx/conf.d
ADD https://gitee.com/youclk/entry/raw/master/debian/sources-vpc.list /etc/apt/sources.list
RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*
HEALTHCHECK --interval=5s --timeout=3s CMD curl -f http://localhost/alive || exit 1

version: "3.5"
services:
  proxy:
    image: $REGISTRY/proxy
    ports:
      - "80:80"
      - "81:81"
    volumes:
      - "/var/run/docker.sock:/tmp/docker.sock:ro"
    deploy:
      placement:
        constraints: [node.role == manager]
      restart_policy:
        condition: on-failure
        max_attempts: 3
      update_config:
        delay: 5s
        order: start-first 
      resources:
        limits:
          cpus: "0.50"
          memory: 1g

负载均衡使用的是阿里云的SLB,窃听80->81,443->80docker compose和swarm,这样一个服务就实现了节点检测、代理和https重定向为一身。拖nginx的福,总之用上去就是爽,点击“Nginx原理解析和配置摘要”进一步了解。

正所谓乐极生悲,某一次我在扩充Swarm集群的时侯提高了部份work节点为manager,但是扩充了代理的数目,这让好多服务频繁出现503,找来找去我发觉问题出在nginx-proxy代理上。当服务在各节点分布不均的时侯,非leader节点上的那种代理难以找到服务,废了老大的劲儿也没找到合理的解决方案。

最后我决定选择“DockerFlowProxy”作为新的代理(好家伙,这一看文档吓我一跳,我还是第一次见到私人的开源项目能把参考文档写得如此详尽,作者的质朴程度“令人发指”,鄙人忆念膜拜之),以下是我的案例:

version: "3.5"
services:
  proxy:
    image: vfarcic/docker-flow-proxy
    ports:
      - "80:80"
    networks:
      - proxy
    environment:
      - LISTENER_ADDRESS=swarm-listener
      - MODE=swarm
    secrets:
      - dfp_users_admin
    deploy:
      replicas: 2
      labels:
        - com.df.notify=true
        - com.df.port=8080
        - com.df.serviceDomain=localhost
        - com.df.reqPathSearchReplace=/alive,/v1/docker-flow-proxy/ping
  swarm-listener:
    image: vfarcic/docker-flow-swarm-listener
    networks:
      - proxy
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    environment:
      - DF_NOTIFY_CREATE_SERVICE_URL=http://proxy:8080/v1/docker-flow-proxy/reconfigure
      - DF_NOTIFY_REMOVE_SERVICE_URL=http://proxy:8080/v1/docker-flow-proxy/remove
    deploy:
      placement:
        constraints: [node.role == manager]
networks:
  proxy:
    external: true
secrets:
  dfp_users_admin:
    external: true

更换代理的过程也并非一帆风顺,我在https重定向这个问题浪费了很多时间,最后也没在代理中解决。作者其实是考虑到了这个问题,精典的解决方案应如下:

services:
  proxy:
    image: vfarcic/docker-flow-proxy
    ports:
      - "80:80"
      - "443:443"
    networks:
      - proxy
    environment:
      - LISTENER_ADDRESS=swarm-listener
      - MODE=swarm
    deploy:
      replicas: 2
      labels:
        - com.df.notify=true
        - com.df.httpsOnly=true
        - com.df.httpsRedirectCode=301

但怎奈姐姐“非精典”呀,我的https证书和负载均衡都委托给阿里云的SLB了,SLB代理的前端恳求只能限定http。我的看法还是窃听所有恳求443端口的域名并返回301,但以下方案并没有成功:

labels:

docker-compose原理与配置_docker compose和swarm_docker-swarm集群管理

- com.df.notify=true - com.df.httpsRedirectCode=301 - com.df.serviceDomainAlgo=hdr_dom(host) - com.df.srcPort.1=80 - com.df.port.1=8080 - com.df.serviceDomain.1=localhost - com.df.reqPathSearchReplace.1=/alive,/v1/docker-flow-proxy/ping - com.df.srcPort.2=443 - com.df.port.2=8080 - com.df.serviceDomain.2=youclk.com,localhost - com.df.httpsOnly.2=true

其实重定向可以在各服务内部实现,但我不觉得这是个好的解决方案。最后的最后,我想总之迟早都要上CND,于是就在CND中加了https重定向(哎,就是带宽的费用要double咯…):

不仅代理,最好再加一个监控服务,我选择了官方案例中的visualizer,配合proxy示例:

services:
  visualizer:
    image: dockersamples/visualizer
    networks:
      - proxy
    volumes:
      - "/var/run/docker.sock:/var/run/docker.sock"
    deploy:
      placement:
        constraints: [node.role == manager]
      labels:
        - com.df.notify=true
        - com.df.serviceDomain=visualizer.youclk.com
        - com.df.port=8080
        - com.df.usersSecret=admin

visualizer算是敏感服务了,通常须要用密码保护,这儿通过com.df.usersSecret指定了密码文件,密码已写入secretsdfp_users_admin中。注意,com.df.usersSecret的值与dfp_users_*必须相同,示例已在上文。布署后显示如下:

docker-flow-proxy还有一个默认的监控服务,显示如下:

不过数据没有统一搜集,因而意义不大,瞧瞧就好。除此之外就是真正须要布署的应用了,只要服务器性能足够,随意想来几个来几个。

5.布署与维护dockerstack

布署命令:dockerstackdeploy-cdocker-compose.yml--with-registry-authyouclk,私有库房必须加--with-registry-auth能够下载镜像。除此之外常用的如下:

# network volume service secret 用法都类似,同出一系嘛...
docker stack ls
docker stack ps youclk
docker stack rm youclk

dockerservice

我使用Compose的场景通常都结合Swarm,因而极少去记自动创建或则修改配置的命令了,意义也不大。不仅查看移除等与上文相像以外,此处还应记两个:

docker service logs --tail 10  youclk_proxy
docker service update --force youclk_proxy

分别是查看日志和服务异常后强制重启。

结语

到此为止写了蛮多了,其余还有一些比较重要内容的后续有空再整理一篇。总结一下,开头我放的那张图虽然很形象:Docker可以看做集装箱把零乱的货物一个个整理归类,Compose则是用于编排这种集装箱,最后Swarm就是多提供几条船,死掉一两条能够继续走,增强稳定性。

我的公众号《捷义》

Tagged:
Author

这篇优质的内容由TA贡献而来

刘遄

《Linux就该这么学》书籍作者,RHCA认证架构师,教育学(计算机专业硕士)。

发表回复