Docker Swarm 教程-第3部分: 初次部署

写于2019-2-11。第3部分做完,终于看到 Rails 的 hello world 页面了。

Docker Swarm 教程-第3部分: 初次部署

第3部分接着 第2部分

第2部分做了什么

  • 新建了1台 Azure 云主机
  • 在 Azure 云主机上安装了 Docker,打开了端口 3000
  • 在 Azure Container Registry 开了一个仓库。用于存储镜像
  • 用 Google Cloud Shell 打包了个镜像 (docker build 命令)
  • 用 Google Cloud Shell 把镜像推到了 Azure (docker push 命令)

长话短说就是:把镜像推到了 Azure。

第3部分的目标

用 Swarm 跑起来!

第1步:修改 docker-stack.yml,把镜像填进去

image-key
修改 image: c1c7c.azurecr.io/c1c7c/myapp:v1 这里就行,注意填你自己的镜像。

第2步:ssh 登录到 Azure 服务器

ssh [email protected]

第3步:git clone 把代码拉下来

sudo mkdir -p /var/www/docker-swarm-tutorial
sudo git clone --branch part1-to-3 https://github.com/1c7/docker-swarm-tutorial.git docker-swarm-tutorial /var/www/docker-swarm-tutorial

注意复制的是 part1-to-3 这个分支,理由在第2部分的教程说过了,这里不重复了。

第4步:切换成 root 用户并且进入代码目录

su -l
cd /var/www/docker-swarm-tutorial

第5步:新建需要的目录

mkdir -p /var/www/db/

新建这个目录是因为:
根据 docker-stack.yml 里的配置 PostgreSQL 会把数据存在这里。

实际开发中,database.yml 可以填环境变量。
指向比如 Amazon RDS,
或者 UCloud 提供的 PostgreSQL/MySQL 数据库服务。
或者你自建了一个允许远程连接的 PostgreSQL 数据库,然后填那个地址。

重点是:因为这里只是简单演示,所以数据就随便存在本地了。
实际生产环境中不能这样用。

第6步:登录 Azure 的 Container Registry,不然镜像拉不下来

比如我这里运行的是:

docker login c1c7c.azurecr.io
username: c1c7c
password: [填你自己的]

第7步:运行 docker swarm

docker stack deploy --compose-file docker-stack.yml --with-registry-auth app-2019

a1

第8步:看运行成功没有

docker service ls

a2
说明:我对 part1-to-3 分支进行了修改,以上这个图片里。REPLICAS 应该都是 1/1。而不是 3/3。

第9步:验证是否可以访问

curl localhost:3000

curl
可以看到的确返回了 html,网站可以访问

第8步:访问网站

访问 40.121.23.32:3000
ip
因为机器的 ip 地址就是这个。
你需要访问你的机器 ip 和端口。
hello-world

终于成功了!

总结下到底做了什么

  1. 打包镜像
  2. 推送镜像
  3. 用镜像部署 Swarm

虽然啰啰嗦嗦写到现在写了3个部分,但实质上做的就是几个简单的步骤。

需要手动在新机器上装 PostgreSQL 吗?没有!
装 Ruby?没有。
装 Rails?没有。
装 bundle 和 gem? 没有。
assest pipeline 编译?没有。

一台新机器 git clone 下来然后跑 docker stack deploy 就跑起来了。多方便。
告别每次有新机器就需要装这个装那个。。

第3部分结束。

接下来我们可以做什么?

到现在我们仅仅是看到 rails hello world 页面而已。

后面还有很多可以做的。日志,监控。0 downtime 更新(blue/green deployment)。
数据库还要弄对 (database.yml), secret 和 config 也还没学。
我们用的环境还是 RAILS_ENV=development
而且我们监听的是3000端口,实际用户肯定会访问80端口。
等等等等

展开讲讲更新代码要怎么做:

  1. 本地开发,写代码写到满意。
  2. 打包一个新镜像,比如用 v2 作为 tag (之前是 v1)
  3. 修改 docker-stack.yml,把 v2 镜像填进去
  4. 去服务器上再运行一次 docker-stack deploy

我知道这个步骤还是相对麻烦(还是有手工操作在里面)
这个部分是可以通过 CI/CD 工具做成完全自动化的。
只是我们的教程才刚刚开始,还没讲到那里呢。

展开讲讲 0 downtime 更新要怎么做:

之前完全没用 docker 时,
为了更新代码期间 Ruby on Rails 后端不能挂掉,我试过2个方案:

1. puma 的 phased_restart

这个方案的缺点是它不是 language agnostic。
意思就是如果我换成 passenger 或者 unicorn,这个方案就完全废掉了。

而且实际使用过程中我碰到过一个奇怪的问题,
在机器A上面 phased_restart 完美工作(大概10-20秒内就可以了)
但是在机器B上面 phased_restart 非常糟糕。启动很慢而且占用CPU高(更新一次要3-5分钟甚至更久)
我想尽么办法去 debug 解决问题,就是无法解决。
我去 Github 提 issue。
去 ruby-china 提问。都解决不了问题。这个方案最终在浪费掉我好几天时间后,只能放弃。

2. nginx 的 load balance
我是在网上搜的时候学会这个方法的。我也是 nginx 新手。

upstream app_witt {
  server 127.0.0.1:9292;
  # server 127.0.0.1:9696;
}

就是开2份 rails,然后指向其中一个。每次要更新了就更新另一个。
这个方法需要很多手工操作。非常闹心。

总之:有了 docker 和 docker swarm 之后,0 downtime deployment 就再也不是问题了。
而且是 language agnostic 的。哪怕我不用 Ruby on Rails 了。我用 PHP, Python, Java, Nodejs 任何语言都可以。

第4部分演示如何更新代码