译者按:在本专栏的前面的文章中,我们已经提到过使用 Docker 的基本方法,因此本次讲解一下 使用 SSH 和 shell 脚本进行 Docker 镜像的自动化部署 ,原文仅供参考,因为对于 Docker 镜像,我们可以有更好的解决方案: Docker Registry Hub 。但是,本文仍然可以作为 shell 脚本的参考范例。
当我们将本站转移到 Docker 容器内之后,我一直在寻求能进行自动化构建和部署镜像的方法。毫无疑问,Docker 本身是一个非常完美的应用容器,但是 Docker 并没有提供能够自动化更新镜像的标准方法。当然,我写了一些 shell 脚本,实现了 Docker 容器镜像的自动化部署。
我们假设基础架构是一台 Linux 宿主机和几个独立的 Docker 镜像,没有网站运行时产生的动态文件,例如用户上传的文件。
当然,要解决这些动态文件也非常简单,本篇文章中的脚本只需要修改一小部分,然后加上 data only container 便可以完美解决动态文件的问题。
那么,我们开始进行自动化部署吧。
脚本的初衷非常简单:构建镜像,上传镜像,使用新镜像重启容器。我们会分段讲解脚本,当然你只需要把本文的脚本段落组合起来,便可以执行自动化部署了。
假设我们的 apache 文件在 apache/ 子目录,一个监控程序在 monitoring/ 子目录。
假设我们的脚本名称为 deploy.sh
,使用如下命令进行初始化:
#!/bin/bash set -e REMOTE_USERNAME="..." REMOTE_HOST="..." IMAGE_REPOSITORY="my_repository"
前面两个变量并不需要解释,后面上传镜像的时候用的到。最后一个变量是 Docker 镜像的名称,你需要设置自己的镜像名称,在后面我们也会有这个名称进行检测。
再后文中我们会建立同一个名称但是 tag 不同的两个镜像分别存储 apache 和 monitor。
第一件要做的事情便是构建镜像,构建过程和普通的 Docker 命令一样。我们写了一个函数专门用来构建镜像:
function build_image { docker build -t $IMAGE_REPOSITORY:$1 $2 } build_image apache apache/ build_image monitoring monitoring/
我们使用上面的已经定义的 IMAGE_REPOSITORY
名称命名镜像,并且对我们的两个 apache 和 monitoring 镜像贴标签。
当然本专栏的前文中提到,镜像可以输出到文件,也可以由文件输入。当然也可以从标准输入输出流进行输入输出。我们使用标准输入输出流和管道进行操作。这个输入和上传的操作很容易用一行 shell 语句写出来。
docker save $IMAGE_REPOSITORY:$1 | bzip2 | pv | ssh $REMOTE_USERNAME@$REMOTE_HOST 'bunzip2 | docker load'
当然,成熟的程序员都会写个函数,顺便做做重复性检测,毕竟几百 M 的文件呢,上传都要好久,还能节省带宽。尤其是有很多镜像需要上传的时候,万一有几个重复的呢。我们所需要的就是按照镜像名称和标签列出本机和远程服务器上的 Docker 容器的 ID,然后检测他们的 ID 是否相同。
本节的 shell 脚本如下。
function upload_image_if_needed { if [[ $(ssh $REMOTE_USERNAME@$REMOTE_HOST "docker images $IMAGE_REPOSITORY | grep $1 | tr -s ' ' | cut -d ' ' -f 3") != $(docker images $IMAGE_REPOSITORY | grep $1 | tr -s ' ' | cut -d ' ' -f 3) ]] then echo "$1 image changed, updating..." docker save $IMAGE_REPOSITORY:$1 | bzip2 | pv | ssh $REMOTE_USERNAME@$REMOTE_HOST 'bunzip2 | docker load' else echo "$1 image did not change" fi } upload_image_if_needed apache upload_image_if_needed monitoring
上面说的是更新镜像,本节讲的是更新容器。现在是最后一步,我们需要使用新镜像重启容器。当然,和其他的语言一样,我们可以将远程主机上的命令写成本机上的输入形式:
ssh -tt $REMOTE_USERNAME@$REMOTE_HOST << EOF ... exit EOF
然后判断容器是否存在,如果存在就结束容器。
docker rm -f ${IMAGE_REPOSITORY}_apache || true docker rm -f ${IMAGE_REPOSITORY}_monitoring || true
注:可能有读者对 docker rm -f ${IMAGE_REPOSITORY}_apache
这条命令感到不解,在此解释一下。原文作者使用制定名称 ${IMAGE_REPOSITORY}_apache
对容器进行命名。
||
是必需的,因为如果容器不存在的话, docker rm
命令便会返回一个错误。我们只需要删除容器,并不去判断他们是否存在。不存在的也就不用删除,当然,删除也没问题。
下面一步便是启动容器:
docker run -d --name ${IMAGE_REPOSITORY}_apache $IMAGE_REPOSITORY:apache docker run -d --name ${IMAGE_REPOSITORY}_monitoring $IMAGE_REPOSITORY:monitoring
为了便于阅读,我特意整理了所有脚本的完全版,如下:
#!/bin/bash set -e REMOTE_USERNAME="..." REMOTE_HOST="..." IMAGE_REPOSITORY="my_repository" function upload_image_if_needed { if [[ $(ssh $REMOTE_USERNAME@$REMOTE_HOST "docker images $IMAGE_REPOSITORY | grep $1 | tr -s ' ' | cut -d ' ' -f 3") != $(docker images $IMAGE_REPOSITORY | grep $1 | tr -s ' ' | cut -d ' ' -f 3) ]] then echo "$1 image changed, updating..." docker save $IMAGE_REPOSITORY:$1 | bzip2 | pv | ssh $REMOTE_USERNAME@$REMOTE_HOST 'bunzip2 | docker load' else echo "$1 image did not change" fi } function build_image { docker build -t $IMAGE_REPOSITORY:$1 $2 } build_image apache apache/ build_image monitoring monitoring/ upload_image_if_needed apache upload_image_if_needed monitoring ssh -tt $REMOTE_USERNAME@$REMOTE_HOST << EOF docker rm -f ${IMAGE_REPOSITORY}_apache || true docker rm -f ${IMAGE_REPOSITORY}_monitoring || true docker run -d --name ${IMAGE_REPOSITORY}_apache $IMAGE_REPOSITORY:apache docker run -d --name ${IMAGE_REPOSITORY}_monitoring $IMAGE_REPOSITORY:monitoring exit EOF
Docker 确实是容器中的佼佼者,而且有很好的命令行支持,但是目前还是缺少能便捷部署 Docker 容器的方式。当然,通过几段简单的脚本,我们便可以解决这个问题。我希望这些脚本也能帮助到你。
本文对容器的操作比较简单粗暴,使用 docker rm
命令进行强行删除,可能会导致一段时间(一般不到半分钟,视情况而定)的网站 403,404 或者 503。
除此之外,本文的 shell 操作可以当成是 shell 远程执行命令的范例。
本专栏将继续推出 Docker 系列文章,欢迎关注。