Docker 小记 — Compose & Swarm
前言
任何相对完整的应用服务都不可能是由单一的程序来完成支持,计划使用 Docker 来部署的服务更是如此。大型服务需要进行拆分,形成微服务集群方能增强其稳定性和可维护性。本篇随笔将对 Docker Compose 和 Docker Swarm 的原理和配置做整理归纳,并分享其使用经验。
1. YAML 简介
Docker Compose 的配置文件采用 YAML 格式,因此有必要在正文之前简要说明下。YAML 是一门专门用来写配置文件的语言,设计目标就是方便读写,其实质上是一种通用的数据串行化格式,基本语法规则如下:
- 大小写敏感。
#
表示注释。- 使用缩进表示层级关系。
- 缩进时不允许使用 Tab 键,只允许使用空格。
- 缩进的空格数目不重要,只要相同层级的元素左侧对齐即可。
YAML 支持的数据结构有三种:
- 对象:
animal:cat
。 - 数组:一组中划线开头的行,例如:
# ex1
- cat
- dog
- bird
# ex2
-
- cat
- dog
- bird
# ex3
animal: [cat, dog, bird]
- 值类型和字符串。
2. Docker Compose
2.1 安装与简介
Docker 可以极为方便地部署单个服务,但这时候我们需要一个工具来整合 Docker 的功能,使之能够更便捷地去管理整个微服务集群的部署和迁移,Docker Compose 正是应此而生。他是由 Python 编写的程序,能够根据指令结合配置文件转换成对应的 Docker API 的操作,并直接体现到 Docker Daemon 中,这就代替我们完成了重复输入复杂指令的过程,主要功能可分为以下两点:
- Service:代表的是运行同种应用程序的一个或多个相同容器的抽象定义,也是我们在Docker Compose 中配置的主要对象。在每个 Docker Compose 的配置文件中,我们可以定义多个服务,并定义服务的配置,以及服务于服务之间的以来关系。
- Project:代表的是由多个服务所组成的一个相对完整的业务单元。
安装命令:
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 配置参数
Docker Compose 的核心就是其配置文件,采用 YAML 格式,默认为 docker-compose.yml
,参数详解可查阅“官方文档”,以下只做一个常规摘要。
services
所有服务的根节点。
image
指定服务的镜像名,若本地不存在,则 Compose 会去仓库拉取这个镜像:
services:
web:
image: nginx
ports
端口映射,例:
ports:
- "80:80"
- "81:81"
volumes
挂载主机目录,其中 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
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 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,因此可以直接使用,初始化命令:docker swarm init
,此时将会默认当前节点为 Leader,以下命令为查看 token:docker swarm join-token (worker|manager)
,其他节点可以用 manager 或者 worker 的身份加入到当前集群,例:
docker swarm join --token SWMTKN-1-3pu6hszjas19xyp7ghgosyx9k8atbfcr8p2is99znpy26u2lkl-1awxwuwd3z9j1z3puu7rcgdbx 172.17.0.2:2377
执行 docker swarm leave
脱离集群。
以下各节点常规操作命令,比较简单,就不解释了:
- docker node demote [NODE]
- docker node inspect [NODE]
- docker node ls
- docker node promote [NODE]
- docker node ps [NODE]
- docker node rm [NODE]
- docker node update [OPTIONS] NODE
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 -> 80
,这样一个服务就实现了节点检查、代理和 https 重定向为一身。拖 nginx 的福,反正用起来就是爽,点击“Nginx 原理解析和配置摘要”进一步了解。
正所谓乐极生悲,某一次我在扩展 Swarm 集群的时候提升了部分 work 节点为 manager, 并且扩展了代理的数量,这让很多服务频繁出现 503,找来找去我发现问题出在 nginx-proxy 代理上。当服务在各节点分布不均的时候,非 leader 节点上的那个代理无法找到服务,废了老大的劲儿也没找到合理的解决方案。
最后我决定选择“Docker Flow Proxy”作为新的代理(好家伙,这一看文档吓我一跳,我还是第一次看到私人的开源项目能把参考文档写得这么详细,作者的细腻程度“令人发指”,小弟顶礼膜拜之),以下是我的案例:
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:
- 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
指定了密码文件,密码已写入 secrets dfp_users_admin
中。注意,com.df.usersSecret 的值与 dfp_users_* 必须相同,示例已在上文。部署后显示如下:
docker-flow-proxy 还有一个默认的监控服务,显示如下:
不过数据没有统一收集,因此意义不大,看看就好。除此之外就是真正需要部署的应用了,只要服务器性能足够,随便想来几个来几个。
5. 部署与维护
docker stack
部署命令:docker stack deploy -c docker-compose.yml --with-registry-auth youclk
,私有仓库必须加 --with-registry-auth
才能下载镜像。除此之外常用的如下:
# network volume service secret 用法都类似,同出一系嘛...
docker stack ls
docker stack ps youclk
docker stack rm youclk
docker service
我使用 Compose 的场景一般都结合 Swarm,因此很少去记手动创建或者更改配置的命令了,意义也不大。除了查看移除等与上文相似以外,此处还应记两个:
docker service logs --tail 10 youclk_proxy
docker service update --force youclk_proxy
分别是查看日志和服务异常后强制重启。
结语
到此为止写了蛮多了,其余还有一些比较重要内容的后续有空再整理一篇。总结一下,开头我放的那张图其实很形象:Docker 可以看做集装箱把杂乱的货物一个个整理归类, Compose 则是用于编排这些集装箱,最后 Swarm 就是多提供几条船,挂掉一两条还能继续走,提高稳定性。
不知为何此刻我会突然想到一句诗:“天苍苍野茫茫风吹草低见牛羊”,有关联吗?没关联,想到就写了,晚安:)
我的公众号《有刻》,我们共同成长!
Docker 小记 — Compose & Swarm的更多相关文章
- Docker三剑客之Swarm介绍
DockOne技术分享(二十): 我用swarm在多台物理机调度管理容器,用ovs实现跨主机的容器互联问题 [编者的话]Swarm项目是Docker公司发布三剑客中的一员,用来提供容器集群服务,目的是 ...
- 小白学Docker之Compose
承接上篇文章:小白学Docker之基础篇,自学网站来源于https://docs.docker.com/get-started 概念 Compose是一个编排和运行多容器Docker应用的工具,主要是 ...
- Docker 入门之swarm部署web应用
笔者近期在利用的docker搭建一个swarm集群,目前的应用还是入门级的,读者可自行根据自己的需要修改自己需要部署的应用,今天笔者介绍的是一个web应用的swarm集群的搭建.看这篇文章之前,我希望 ...
- Docker 小记 — MySQL 与 Redis 配置
前言 本篇随笔是继 "Docker Engine" 与 "Compose & Swarm" 之后的一个实例补充,初衷是记录测试环境中的一次 MySQL ...
- Docker管理工具 - Swarm部署记录
之前介绍了Docker集群管理工具-Kubernetes部署记录,下面介绍另一个管理工具Swarm的用法,Swarm是Docker原生的集群管理软件,与Kubernetes比起来比较简单. Swarm ...
- DockOne技术分享(二十):Docker三剑客之Swarm介绍
[编者的话]Swarm项目是Docker公司发布三剑客中的一员,用来提供容器集群服务,目的是更好的帮助用户管理多个Docker Engine,方便用户使用,像使用Docker Engine一样使用容器 ...
- Docker深入浅出系列 | Swarm多节点实战
目录 前期准备 Swarm基本概念 什么是Docker Swarm 为什么要用Swarm Swarm的网络模型 Swarm的核心实现机制 服务发现机制 负载均衡机制Routing Mesh Docke ...
- Docker学习—Compose
前言 前面<Docker学习-DockerFile>文中介绍了dockerfile相关的语法,及使用方式:接下来了解docker三剑客之一的 Compose:接下来详细学习. 一.dock ...
- Docker之Compose服务编排
Compose是Docker的服务编排工具,主要用来构建基于Docker的复杂应用,Compose 通过一个配置文件来管理多个Docker容器,非常适合组合使用多个容器进行开发的场景. 说明:Comp ...
随机推荐
- ABP官方文档翻译 6.1.2 MVC视图
ASP.NET MVC 视图 介绍 AbpWebViewPage基类 介绍 ABP通过Abp.Web.Mvc nuget包集成到MVC视图.你可以如往常一样创建正常的MVC视图. AbpWebView ...
- linux中vim常用的快捷键
移动光标的方法 h或者向左箭头:光标向左移动一个字符 j或者向下箭头:光标向下移动一个字符 k或者向上箭头:光标向上移动一个字符 i或者向右箭头:光标向右移动一个字符 Ctrl+f:屏幕向下移动一页[ ...
- 为Ghost博客扩展代码高亮、数学公式、页面统计、评论
前几天捣鼓了一下博客首页,接下来再丰富一下博客页面的功能与内容.由于我所使用的Ghost博客专注于轻量简洁,因此标题中提到的功能在Ghost中默认均不支持.下面将逐个介绍一下如何为Ghost扩展这些功 ...
- 使用webpack、babel、react、antdesign配置单页面应用开发环境
这是Webpack+React系列配置过程记录的第一篇.其他内容请参考: 第一篇:使用webpack.babel.react.antdesign配置单页面应用开发环境 第二篇:使用react-rout ...
- hdu 4656 Evaluation [任意模数fft trick]
hdu 4656 Evaluation 题意:给出\(n,b,c,d,f(x) = \sum_{i=1}^{n-1} a_ix^i\),求\(f(b\cdot c^{2k}+d):0\le k < ...
- jenkins入门系列之一 jenkins的安装
Jenkins是一个CI(持续集成环境)工具.它可以根据设定持续定期编译,运行相应代码:运行UT或集成测试:将运行结果发送至邮件,或展示成报告... 这样做的最终目的是: 让项目保持健康的状态.如果任 ...
- Java并发编程Semaphore
信号量 信号量类Semaphore,用来保护对唯一共享资源的访问.一个简单的打印队列,并发任务进行打印,加入信号量同时之能有一个线程进行打印任务 . import java.util.concurre ...
- php分布式redis实现session共享
方法一:找到配置文件php.ini,修改为下面内容,保存并重启服务 session.save_handler = redis session.save_path = "tcp://127.0 ...
- 织梦autoindex应用 dedecms循环中判断第几条数据
arclist 标签下使用 [field:global.autoindex/] 默认从1开始 {dede:arclist row='10' titlelen='48' typeid='1' chann ...
- 市面上有没有靠谱的PM2.5检测仪?如何自己动手制作PM2.5检测仪
市面上能买到的11中常见的pm2.5检测仪 网上大佬实测并不是很准,我这里没测过(全买下来有点贵,贫穷限制了我的想象力) 这些检测仪多数是复合式.多功能的空气质量检测仪.具体就不一一介绍了.这篇文章 ...