Docker Swarm 教程-第1部分: 介绍

写于 2019-2-11,第1部分只有文字介绍。不需要照着打命令。看完介绍就明白这系列要干什么了,然后你可以决定对你有没有用,有用就继续看,没用就关掉。

Docker Swarm 教程-第1部分: 介绍

看完之后,作为读者你能学到什么?

如何手动部署程序到 Docker Swarm(只用一台机器)
(这里一台机器指的是普通的云主机,比如阿里云的主机,Amazon 的 EC2,等等)
我这里会用 Azure 的机器(因为有代金券……)外加随便找一个免费的私有容器仓库(还挺多的,国内外都有,可以参考这篇文章

Docker 的好处是什么?Docker Swarm 的好处是什么?

(注:如果你已经知道了,这一大段可以跳过)

Docker 的好处:

把环境和代码打包在一起。

  • 这样到一台新机器上你就不用手动去装 nginx 装 ruby 装 rails 装 bundle 装 gem 装一些你程序要用的依赖。装环境很烦的。
  • 因为环境不一致带来的问题也很烦的。(解决:可是在我的机器上能运行啊?)
  • 再想象不同程序依赖不同版本,一个要 python3 一个要 python2.7。你怎么办?

举个例子,有了 Docker 你只需要运行 docker run -p 8080:80 nginx
然后访问 8080 端口就可以看到 nginx 了。
nginx
(配图是在云主机上运行的,你可以在自己的机器上运行后访问 localhost:8080)

注意,这里的重点是:Docker 只解决了隔离的问题,真实环境中的程序需要解决更高层的问题。
那些更高层的问题应该用更高层的工具来解决,而不是把所有功能都塞进去一个工具里。

Docker Swarm 的好处:

(解决了更高层的问题)
一个实际面向客户的程序,不止有1个组件,你可能有 nginx, python, redis 等。
如果每次都 docker run 跑起来:

docker run (省略参数) nginx,
docker run (省略参数) redis
docker build 你的主程序,
然后 docker run (省略参数) 你的主程序。

这样很烦,因为:

  1. 你需要记住3个 docker run 命令(参数和镜像名)。
  2. 你需要手动跑3条命令。

别忘了,不同环境需要不同环境变量
(docker run 环境变量的官方文档)
env

想象一下你在本地开发环境要这样跑,测试环境要这样跑,在 staging/qa 环境要这样跑,在生产环境要这样跑。
想象一下你有一大堆带了环境变量的命令,比如:
docker run -e development [镜像名]
docker run -e production [镜像名]

你可能马上想到了如下的解决方案(这个方案不够好,后面会解释为什么):

为了不用记住命令:把 docker run 命令写在 README.md 里。每次需要就复制黏贴。
为了不用每次复制黏贴跑命令:写一个 shell script,把所有 docker run 命令 docker build 命令或任何你需要的命令,都放进去这个文件,每次执行这个文件。这样就变成只有1条命令了。

以上方案解决了一部分问题,没有解决的问题:

1. 如果需要跑同一个镜像的多个容器(为了支撑更多用户),应该怎么做?

在 shell script 里多几行 docker run [镜像名] 命令?
ok,那你现在怎么 load balance?
如果你多开了几个容器,但是流量全部导向一个容器,那开了跟没开一样。
你肯定要把流量分给这几个容器,让它们干活,这样才能支持更多用户。

以上还只是单台机器的情况。
如果有多台机器,3个容器在机器A,2个容器在机器B。怎么 load balance?

2. 需要 0 downtime 更新的时候,应该怎么做?

0 downtime 意思是:更新期间用户依然可以访问。
这个也叫 blue/green deployment,蓝绿部署。

你很少(同时也越来越少)看到一个网站或 App。
每隔1,2天就贴一个公告说:
“不好意思我们今天下午2点到4点维护,维护期间不能访问,对造成的不便我们深感抱歉”
这种公告会有,但是频率非常低,一般是升级硬件,或者其他难以不停机就修改的东西。被逼无奈才会做这个。没有人希望时不时的就因为更新程序导致无法访问

理论上,更新程序做法是:比如你有3个容器,镜像都是 my-app:v1。
先关掉一个容器,剩下2个容器。然后开一个新容器,这次用镜像 my-app:v2。
然后依次类推,关一个开一个,把3个容器都变成 my-app:v2。
此时你再想象一下多台机器,多个环境的更新。
结论:手动更新不但很烦,而且容易犯错,而且速度还慢。

正确方法:用容器编排工具(比如 Docker Swarm 或者 Kubenetes)
提供的 rolling update 功能。
让工具来帮你一个个更新(可能散布在多台机器上)的容器。而且还能控制 rollback (回滚)。
rolling update 可以控制一些配置。比如跑10个容器的时候,一次性更新2个。2个2个的来更新。或者3个3个的来更新。还可以加 health check (健康检查),比如访问容器的某个 url,确保返回 http 200 状态码。

另外,因为容器包着你的程序,而你的程序要花多久才能启动起来,是看情况的。
所以 health check 是需要稍微调整的,比如容器开起来之后,等10秒才进行第一次 health check,如果超时了3秒就算是失败,然后隔15秒再试一次,一共试3次,如果3次都不行,那就算失败。

所以可以这样配置:先更新2个容器,通过 health check 检查容器健康了才继续更新接下来的2个。
如果期间碰到任何问题。回滚到之前的版本。

3. Secret 和 Config,应该怎么做?

Secret 是秘密,比如 https 证书,你的微信支付证书,各种第三方工具的 api 访问密码。
任何不希望公开的东西就是 Secret。

Config 就是配置,就是一些不太重要的东西。
比如网站的多语言配置或什么其他配置,我暂时想不到好例子,┑( ̄Д  ̄)┍

再想一下多容器,多台机器应该怎么管理 Secret 和 Config?
(肯定不能塞到源代码里然后提交到 Github ……,不但不安全,而且不能依据不同环境进行调整)

结论

一句话总结:
Docker Swarm 是用于实际生产环境的。它基于 Docker 之上,解决更高层的问题。

一段话总结:
如果单单依靠 Docker,它只是把环境和代码打包在一起,仅仅解决了环境不一致这一个问题。
实际生产环境中,需要跑多个 container。而且可能跑在多台机器上。需要处理更新,需要处理回滚,需要管理 secret 和 config。某些容器可能只需要跑1份,只跑在 manager 节点上
所有这些要求,加在一起就复杂了。所以需要 container orchestration tools(容器编排工具)

(解释下什么是 manager:所谓 manager 节点就是管理 Docker Swarm 的机器,
一个 Swarm 里可以有多个 manager,建议奇数个,比如1,3,5,7个mangager。
另外,在一个 Docker Swarm 里,机器只有2种身份,mangaer 和 worker,worker 是干活的,mangaer 是管理的。)

为什么选 Docker Swarm?(而不是其他容器编排工具?)

容器编排工具有 Kubenetes,Docker Swarm。
还有几个工具因为我没接触过,不太确定怎么分类:Nomad, Openshift, Mesos。

如果想知道整个生态圈有多少工具,可以看 CNCF

(各种工具):
cncf1

(容器编排工具有这些):
cncf2

简单对比下 Kubenetes 和 Docker Swarm:
安装:Swarm 自带在 docker 里,无需额外安装。
学习:Swarm 比 kubenetes 简单。
使用:Swarm 比 kubenetes 方便。

当然,业界还是以 kubenetes 为主流,因为稍大一点的公司的确需要 kubenetes。
第三方工具对 k8s 的支持度也好过 Swarm。

说明

我们用 Ruby on Rails 5 新建一个全新的项目进行演示。
解释一下为什么用 Rails:
因为我比较熟悉 Rails...
后端这么多语言 PHP/Python/Nodejs/等,没有一个语言是所有人都会的,我总得选一个吧。

rv6yvmM1

本文适合个人或小团队
因为演示的是把程序部署到1台机器上的 Docker Swarm。
(本文没有讲:数据库, Secret, Config)

本文假设读者有一定 Docker Swarm 知识,
文章是直接教你怎么 Swarm
而不是把重点放在 Docker Swarm 本身的概念。

问题:为什么要手动部署?自动部署不行吗?

回答1:学习意义

手动部署都不会的话,自动部署你知道怎么做吗?(:зゝ∠)

回答2:省钱省时间。

省钱的意思是:完全不用第三方 CI/CD 工具 (CircleCI, TravisCI, CodeFresh, Buddy)。
这些第三方工具都是几十美金起步的,对于个人来说还是挺贵的。对公司来说可能没什么所谓。
不过这些工具一般有免费限额,TravisCI 100次一个月,CodeFresh 120次 build 一个月。 Buddy 是 120次 Execution 一个月。

“省时间”的意思是(这里打个引号,因为不是一直是对的):

  1. 每一个 CI/CD 工具都有自己的语法。
    比如 Gitlab 的 .gitlab-ci.yml。 Drone 的 drone.yml。Codefresh 的 codefresh.yml。等等等等。一家一个语法。这是没办法的事情,因为各家是独立的,而且提供的功能也略有不同。
    照着文档和 example 学呗,还能咋样。这得花时间

  2. 每一个 CI/CD 工具都有一些自己的概念
    比如 Gitlab 有所谓的 Runner。Runner 就是负责跑代码的东西。Runner 负责跑你的 .gitlab-ci.yml。Gitlab 有几个 share runner,你可以凑合着用。但是如果有更高的性能要求,定制要求,或者你自己是 self-host Gitlab 的。那就要自己部署一个 Runner。要照着文档看怎么部署 Runner。这得花时间

  3. 有的 CI/CD 工具可能需要你自己部署
    比如 Drone 就可以自己部署。这得花时间

"省时间"这个不是一直为真的,看阶段,1个人的时候,如果急着写代码不想折腾这些(部署的东西)
那么手工做就是省时间。因为说白了也就只是
build docker image -> push docker image -> tell Docker Swarm to use that new docker image

坏处是什么?

因为手动操作是和命令行打交道。
没有一个 Web UI 进行中心管理(所有人都可以登录上去看)

如果只有1个人,那么手动部署没什么问题,只要你不嫌烦。
2个人3个人可能需要一定沟通,也还可以。
如果十几个人就容易冲突了。不好管。

结论:1个人的时候+急着做业务逻辑。
那就手动部署吧。真的没时间没心情去折腾第三方CI/CD工具。
如果人多了,也说明你有钱雇那么多人了。那一个月掏个不到1000块钱人民币来提升效率就不是问题了。

题外话:当然如果你愿意折腾 Jenkins,那当我没说。
我不喜欢 Jenkins 是嫌弃太丑 + 觉得过于复杂,学习成本高。

补充:解释下为什么题图里 Docker Swarm 写着 18.09.1

这个可能有点让人困惑。因为 Swarm 的版本一般不会单独拿出来提。
其实这个 18.09.1 来自 docker version 命令。
docker-v

讲了一堆为什么,现在该动手做了 👇👇👇

第2部分点这里

(因为塞在同一篇文章里会很长。所以拆出去单独变成一篇文章)