如何将Capistrano与Docker集成以进行部署?

25

我不确定我的问题是否相关,因为我可能会尝试混合不应该混合的工具(Capistrano和Docker)。

我最近对一个使用Capistrano部署的应用程序进行了容器化。 Docker Compose在开发和演示环境中都被使用。

这就是我的项目看起来像什么(没有显示应用程序文件):

Capfile
docker-compose.yml
docker-compose.staging.yml
config/
    deploy.rb
    deploy
        staging.rb
Docker Compose文件创建所有必要的容器(包括Nginx、PHP、MongoDB、Elasticsearch等),以便在开发或分段环境中运行应用程序(因此定义了一些特定参数在docker-compose.staging.yml中)。
使用此命令将应用部署到分段环境:
cap staging deploy

服务器上的文件夹结构与Capistrano的文件夹结构相同:

current
releases
    20160912150720
    20160912151003
    20160912153905
shared

已在暂存服务器的current目录中运行以下命令,以实例化所有必要的容器来运行应用程序:

docker-compose -f docker-compose.yml -f docker-compose.staging.yml up -d

目前为止都很好。在下一次部署中,情况变得更加复杂:current符号链接将指向releases目录中的一个新目录:

  • 如果deploy.rb定义了需要在容器内执行的命令(比如PHP的docker-compose exec php composer install),Docker会告诉你这些容器不存在(因为现有的容器是在前一个发布文件夹中创建的)。
  • 如果在Capistrano部署过程中执行docker-compose up -d命令,则会出现一些错误,因为端口冲突(以前的容器仍然存在)。

你有解决这个问题的想法吗?我应该远离Capistrano并做一些不同的事情吗?

这个想法是保持Capistrano所提供的(几乎)零停机部署,并具有Docker容器的灵活性(为同一台服务器上的各种应用程序提供多个PHP版本,例如)。

2个回答

28
据我理解,您正在主机上使用Capistrano重新部署整个应用程序堆栈(即容器)。因此,您正在使用Capistrano进行编排、构建、容器创建和部署等操作。
当运行“cap deploy”时,您基本上会:
- 根据当前主机上拉取的基础镜像构建应用程序(可能包括gulp/grunt/build任务) - 然后将其“打包”到镜像中并使用“volume mounts” - 同时启动/替换容器
通过这种方式,您可以实现“几乎”零停机时间部署。
如果您真的关心停机时间并且非常重视部署流程,那么应该使用适当的流水线实现:
- 打包 / CI - 部署 / 分发
我认为Capistrano不应该是用于此策略的工具之一。 Capistrano旨在使用ssh和git作为传输手段在服务器上直接部署应用程序。在目标服务器上使用Capistrano构建整个镜像再启动这些镜像作为容器,这确实过于复杂,我个人认为不应该采取这种方式。
要打包/构建,可以使用CI/CD服务器(如Jenkins/Bamboo/GoCD)为您的应用程序构建发布镜像。假设只有应用程序在“发布”方面进行了自定义设置,例如您的数据库和应用程序作为容器/服务,应用程序将包含您的源代码,并且在发布过程中会经常更改。
因此,这是一个CI/CD过程,在CI服务器上构建新的应用程序镜像(发布)。拉取应用程序源代码并使用“COPY”将其打包到您的镜像中,然后使用任何“RUN”语句编译资产(npm / gulp / grunt等)。所有这些都不会在生产服务器上发生,而是在CI/CD代理上进行。鼓励使用多阶段构建生成精简镜像。然后,您将推送此版本映像(称为yourregistry.com/yourapp)到您的私有注册表作为新的“版本”以进行部署。
部署
有停机时间(简单)
要在生产或暂存服务器上进行有停机时间的部署,您只需执行docker-composer pull && docker-composer up - 这将拉取更新的映像,然后在堆栈中启动它 - 您的应用程序已升级。在发布阶段使用标记的映像需要更改docker-compose.yml文件。
服务器当然应该能够从您的私有存储库中拉取。
无停机时间(更多工作)
要实现零停机时间部署,您应该使用蓝绿部署概念。因此,您需要向设置添加代理,并且不再公开应用程序的公共端口,而是使用此代理公共端口。您当前的在线系统可能正在运行随机端口21231,代理将从443转发到21231。
我们使用随机端口来避免在部署“第二个”系统时出现冲突,这涵盖了您提到的问题之一。
重新部署时,您仅会基于新的应用程序映像启动一个“新”容器(而不是旧容器),它会获得新的随机端口12312 - 如果需要,可以针对12312直接运行集成测试(不要使用代理)。如果您完成并满意,则重新配置代理以将其转发到12312 - 然后删除旧的容器(21231)。
如果您想自动化代理重新配置(对于此问题来说过于详细),您可以使用服务发现和注册器,这使随机端口更加实用,并使重配置代理变得容易,可以是nginx / haproxy等。例如,可能会使用以下工具:

谢谢你的回答! 那么我应该放弃使用Capistrano来从主机部署到服务器了。 我已经将应用程序作为容器,因此构建其镜像应该是可以的。由于它不会使用卷共享(对于开发环境很有用),所以我需要一个特定的Dockerfile来添加COPY / RUN语句吗?我想我必须确保应用程序中的上传目录仍然是挂载卷,这样当部署新的应用程序镜像时它们不会被重置。 有没有关于整个部署过程设置的好教程?(简单易懂的) - Michaël Perrin
正如上面提到的,需要在构建时将COPY添加到映像中以部署代码。 - Eugen Mayer

5

我认为Capistrano不是这项工作的合适工具。最近在SSHKit的PR中讨论了这个问题,它是Capistrano的基础。

https://github.com/capistrano/sshkit/pull/368

@EugenMayer更好地解释了使用Docker的“正常”方式。


1
谢谢你的回答,它确认了在这个Docker化的体系结构中我不应再使用Capistrano。我没有找到任何好的教程来解释整个过程(从安装私有仓库到准备一个(示例)应用程序作为镜像进行部署),但我会尽快获取更多信息。 - Michaël Perrin
1
获取书籍《DevOps 2.0》,它涵盖了所有这些主题,绝对值得一读。 - Eugen Mayer
Victor Farcic 写了 DevOps 2.0 视频 https://www.youtube.com/watch?v=QhPEOhKXm-s - Kevin Monk

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接