运维 运维环境简单化 - 容器封装

pinewong · 2016年11月19日 · 最后由 pinewong 回复于 2016年12月05日 · 11428 次阅读
本帖已被管理员设置为精华贴

Overview

一个产品出现,从线下开发到线上 internal 服务器最后到 release 服务器,需要维护许多处的环境。现在,我们需要将环境同样抽象出来,做到本地开发环境变了,所有线上服务器部署前可以一并自动得到同步。

Summary

  • 构建镜像 (在项目中添加 Dockerfile)
  • 增加部署项目,例如:app-deploy
  • 新建 Docker Hub 账户,并绑定你的项目地址 (GitHub, Bitbucket)

Quickly realize

1. 构建基础镜像

Docker Docs 里学习镜像相关操作,最后将现有的环境封装到镜像里,作为你应用的基础镜像,并最后上传 Docker Hub.

基础镜像的构建可以参照官方 Library. 例如:Rails 基础镜像

假设我们最后得到基础镜像名为:pinewong/app-base:latest.

2. 构建部署镜像 (将基础镜像与项目源码组合)

在项目主目录添加 Dockerfile 文件,内容不需要太多,对于 Rails 应用,类似这样:

FROM pinewong/app-base:latest
MAINTAINER PineWong <[email protected]>

# Bundle
WORKDIR /app
ADD ./Gemfile Gemfile
ADD ./Gemfile.lock Gemfile.lock
RUN bundle install

# Assets
ADD ./app
RUN bundle exec rake assets:precompile RAILS_ENV=production

最后在 Docker Hub 上建立该项目的自动构建关联,并 Trigger 第一个镜像,得到部署镜像:pinewong/app:latest

Docker Hub与GitHub项目关联

3. 新建 app-deploy 项目

目录大概如下:

|app-deploy/
|__config/               # 配置文件目录
|__|__nginx_site.conf    # nginx site 设置
|__|__...             
|__docker-compose.yml    # 主配置文件
|__app.default.env       # 默认环境变量
|__...

docker-compose.yml:

version: '2'

services:
  app:
    image: pinewong/app:latest
    command: puma -C config/puma.rb
    depends_on:
      - postgres
    volumes:
      - ./config/nginx_site.conf:/etc/nginx/conf.d/default.conf
  nginx:
    image: nginx:1.11.5
    ports:
      - "80:80"
    depends_on:
      - app
  postgres:
    image: postgres:9.6.1
  # ...

该步骤具体配置可以参照社区 homeland-docker 项目。

homeland-docker install

最后将该项目分发到各服务器和开发人员,在目录下,直接:docker-compose up 即可运行这个完全封装的应用。

Advance

开发调试

上述步骤简单封装了一个生产环境,开发环境只需要做一下小修改,添加一个 docker-compose-dev.yml 文件,内容大致是去掉 Nginx 这类生产中用的组件,和替换一些轻量级开发组件:postgres 替换成 sqlite 等,并在环境变量配置中修改 RAILS_ENV 值,最后启动应用时添加文件参数 --file docker-compose-dev.yml

另外开发需要实时变化源码,我们直接使用容器的 Volume 功能,将本地源码目录映射替换容器源码目录:

docker-compose.yml:

app:
  volumes:
    - ./app-dir:/app    # app-dir代表本地当前目录下的源码目录,app是上述部署容器所添加源码的目录

最后,当启动容器运行程序后,你还可以进到容器内部调试:

docker-compose exec app bash

于是,你就进到了一个应用环境的命令窗口,这里可以得到虚拟机的体验。

对于项目的基础镜像管理

为了更好管理各项目的基础镜像,这里可以学习官方操作,在自己的 GitHub 中建立一个 Library 源,这里有我一些环境的例子:pinewong/docker-library

这样还有一个好处,可以让基础镜像也使用自动构建服务。

简化安装和命令输入

第一次部署项目时具体需要的操作有许多,例如初始化本地环境变量文件,数据库初始化,生成秘钥等等,这些都可以自动化,你可以写一个 shell 脚本来做这些,但更优雅的是学习 homeland-docker 项目,使用 Makefile 清晰实现封装。

另外如果使用了 Makefile 实现一些封装,你还可以将开发中的命令简短不少,例如这样设置 Makefile:

status:
  @docker-compose ps
stop:
  @docker-compose stop
restart:
  @docker-compose restart $(name)
start:
  @docker-compose up -d
console:
  @docker-compose run app rails console
update:
  @make pull
  @make reload
pull:
  @docker pull pinewong/app:latest
  @docker pull nginx:1.11.5
  @docker pull postgres:9.6.1
reload:
  @docker-compose up -d --force-recreate
# ...

之后当你想要进入 Rails 控制台,可以使用命令make console 代替 docker-compose run app rails console;当发布新容器,需要升级时可以使用make update代替

docker pull pinewong/app:latest
docker pull nginx:1.11.5
docker pull postgres:9.6.1
# ...
docker-compose up -d --force-recreate

当然,你也可以自己在 Makefile 中定义需要的命令。

持续部署

上面虽然解决了环境一致的问题,但部署由于脱离了 Mina 等工具变得麻烦起来,这时我们可以使用一些 CI 工具来自动完成,且上述用到的基本都是标准组件,持续部署将很容易实现。

需要实现持续部署,首先你要明确整个流程,如果我们从项目源码提交开始,大致是这样:

Commit -> Docker Hub 构建部署镜像 -> 各服务器升级程序 (pull 新镜像,重启容器)

如果是基础环境变了,例如应用从 MySQL 迁移到 Postgres , 环境依赖需要安装 libmysqlclient-dev, 大致流程变成这样:

docker-library 更新 -> Docker Hub 重新构建基础镜像 -> Docker Hub 检测到基础镜像改变 -> Docker Hub 重新构建部署镜像 -> 各服务器升级程序 (pull 新镜像,重启容器)

综上,不管什么触发,当我们理解了流程,我们就可以在 CI 工具中新建一个任务。开心的是,Docker Hub 和 GitHub 已经结合的相当紧密,一般的触发可以直接在项目设置 webholk 来实现。最后对于 CI 工具,我使用的是 Jenkins

Extend

lgn21st 将本帖设为了精华贴。 11月20日 01:12

厉害了 word 的哥

简单应用 Docker 可以胜任。复杂应用,配置管理类平台会可靠得多。

楼主太给面子,一下给了两个参考链接。可以继续看下 docker swarm, 现在比较火的 docker 集群管理方案

#2 楼 @matrixbirds 谢谢

#3 楼 @kgen #4 楼 @embbnux 好的好的,一下又有该学习的东东。

另外 embbnux 我可一直偷偷关注着你啊😀

现在用 docker 做虚拟化方案的公司还不多,不过趋势已经很明显了

Docker 的好处是应用打包(Dockfile),实例一多,容器的管理监控变的比较麻烦,随着 Swarm 合并到 Docker,不知道这方面是否让它如虎添翼,不过对其他调度框架也蛮感兴趣 http://dockone.io/article/1138

如果觉得 Docker 不好用,可以尝试 rkt + k8s

另外,对于监控方面,强烈推荐 prometheus, 对于容器监控支持不错,提供告警通知

docker 在 rails 项目的应用上,我一直很纠结的是,怎么能用上 capistrano,尝试了几种方式,感觉都有点不舒服。

之前尝试的是放弃 capistrano,每次打包一个 image,再部署,这个缺点是,asset 和 bundle install 弄不好总是要全部重新写层,所以每次部署的时间都会很长。现在用的方式是,用 docker 开一个容器,然后对后开好 ssh,通过 capistrano 部署到这个容器中,部署路径用的是一个 data volume,这样换容器没有影响。

这样做,至少在开发阶段,部署的速度能和直接用 cap 一样了,但其实只是绕路了,和 docker 本身的意图有点背道。

不知道大家在 rails 项目上使用 docker 有啥可以借鉴分享的吗

#10 楼 @pzgz 我感觉本身 rails 的 image 里面并不需要关注 assets 的资源,那么每次打包部署启动的时候只需要在本机编译 assets 然后上传到服务器,container 挂载一个 volumn 加载那个 json 文件就好。这样的好处就是可以使用 capistrano 也不违背 docker 的本意。

#11 楼 @hging 但是这样的话,每次编译就会在开发本机上做,这当然不是说不可以,不过 compile 这个过程也是非常耗时耗力的,有时候还是希望扔给服务器。 其实除了这个点,还有另外的一些点,比如说,migrate,还有 bundle install,前者的问题是,跑的前提是 image 已经做好了,需要用一个新的容器来跑一下,后者的问题是,和 assets 类似,做不好,每次都是要重新安装,相对 cap 本身来说,速度会慢了很多

capistrano 已经被我干掉了,自己写脚本调用 git status 和 git push,如果 git status 的文件变动信息包含 assets 字符串,则执 rake assets:precompile,对 migrate 也是同理,这样小改动部署基本上只需要 3 ~ 4 秒钟时间 (重启 server),基本满足了像 php 那样边写程序边刷浏览器的要求。有了 docker 迁移服务器环境也不用 bundle install 了,capistrano 基本上可以退休

#12 楼 @pzgz 并不需要每次都从新安装啊 你需要一个装好 gem 什么的基础 image 然后在这个基础上进行修改就行了 每次更新的 image 容量不会很大。用 capistrano 一样要本机挂着等啊 在服务器上编译跟在本机编译没什么区别,而且一般情况下本机编译要比服务器快得多。

@hging 这倒是一个好办法,我前面说的 precompile,虽然在开发机上等,但终究服务器运算还是快,不过你刚才说的这个 gem 和楼上 @Trump 说的有异曲同工之妙,我太拘泥于 cap 的方式了,开发上的确 cap 还是上手快,不过我是可以继续尝试一下在 docker 上看如何借鉴二位说的方法了,多谢

用 docker-compose 做开发环境,往代码里插入 binding.pry 时不知道楼主是怎么操作的。

docker-compose 本身不支持 attach 到运行着的容器,给他们提了相关 issue 也没人跟进,最后是自己封装了个命令从 docker-compose 获取容器 id,然后用这个 id 去 docker attach,实现可以在代码中插入 binding.pry 调试。

总的来说,docker-compose 带给我的开发体验并不好,各种小问题很多。我觉得对于 Rails 来说把开发和生产统一成一个环境是个伪需求。Rails 开发涉及到的东西都很容易安装在本地做开发,没必要为了统一环境都用 docker 做开发,丧失体验。Rails 本身已经是个很完善的生态了,开发流程工具都很成熟,何必非要套上一个不成熟的 docker-compose。

而利用 docker-compose 进行部署,我认为也是一个很扯的事情。docker-compose 目前的主要目标还是实现在一个宿主机上进行部署,swarm 还不够成熟,实际部署时候还是要用其他方式多机部署,docker-compose 在这个过程中可能只能起到一个指导作用。

#16 楼 @qinix

Docker 如何使用 binding.pry

binding.pry 这类需要终端和进程状态变化的调试操作,要使用有点违背 Docker 单进程的意图,但也可以实现。我的具体做法是在 docker-compose.yml 或 Dockerfile 中修改 CMD 入口,换一个一直运行的 0 号进程,然后 exec 进入终端,此时的程序终端跟一般状态无异 (已经不依赖 Rails 进程了), 之后启动 Rails 进程,就可以修改代码,进行终端调试了。

开发体验不好,但是分使用场景

你说的很对,docker 给 Rails 开发带来的体验是不好,甚至对于刚接触的人,因为有一个单进程的限制,会觉得没有虚拟机方便,但是这是对后端需要一些高级调试的人来说。现在假如有一个需求,一个系统有多个部分,需要前端,后端以及一些底层运算成员组成,对于前端和底层运算成员,他们没必要本地实现一套 Rails 环境,他们并不关心这个,这里可以用虚拟机来实现,但是虚拟机即使是 vagrant 没有像 Dockerfile 这样使环境完全透明的东西,运维修改起环境没有 docker 方便。

而对于 Rails 后端人员,可以用 Docker 运行前端的一套 node 项目和底层运算成员的一些 Maven 项目,而对于自己的 Rails 项目,如果开发调试不够用了,也可以直接使用本地环境代替,这时候的操作只需要暴露 Node 和其他需要组件的端口,这里实际上可以理解为将其他不关心的系统封装成一个 app 来跑,而这些 app 由 Docker Compose 来管理。当然,后端对于 Rails 环境也是需要的,当环境依赖需要修改时,可以很方便本地在 Docker 中测试好,之后上传新的 Dockerfile, 在下个版本时,让其他工程师一并得到更新。虽然这种需求量不是很多,但用起来就舍不得放了。

生产环境

楼主还是小白,没有接触到 swarm 多机部署😂 我现在应用到生产环境,益处主要是统一了环境,没有了曾经阿里云运行良好,然后其他云跑不起来的问题。最后,Docker Compose 是很不成熟。

需要 登录 后方可回复, 如果你还没有账号请 注册新账号