Docker - Service Building
Created by : Mr Dk.
2020 / 09 / 10 22:45
Nanjing, Jiangsu, China
一个简单的博客网站
通过灵活运用 Docker 的卷、网络等特性,通过不同的镜像和容器共同构建应用服务。只记录思路。使用 Vue.js 开发一个博客网站,构建为静态资源后,部署到 Nginx 服务器上。在这个场景中,最起码需要用到两个镜像:
- 一个镜像中包含 Node.js 环境,能够将 Vue.js 代码编译为静态资源
- 一个镜像中运行 Nginx 服务器,使网站能够工作
对上述两个镜像进行构建 (只需要构建一次即可)。每当网站更新时,需要进行以下流程:
- 从 Node.js 镜像中启动容器,通过一个卷将源代码挂载到容器中,执行
npm run build
将 Vue.js 代码编译为静态资源,保存到一个专门的资源卷中 - 从 Nginx 镜像中启动容器 (实际上启动一次也够了),挂载与 Node.js 共享的资源卷,其中保存了网站静态资源
- 当网站需要更新时,清理并重复上述流程
Node.js 镜像
基于一个基础镜像:
- 安装 Node.js 环境
- 通过
VOLUME
声明 网页代码卷 和编译后的 资源卷 - 通过
WORKDIR
将工作目录声明为网页代码卷所在位置 - 通过
ENTRYPOINT
指令指定构建命令与参数,如npm run build
从这个镜像启动容器时,需要将网页的 Vue.js 源代码目录作为卷挂载到容器中。容器将在这个目录下运行网页构建。
Nginx 镜像
基于一个基础镜像:
- 安装 Nginx
- 通过
ADD
指令添加好配置文件 - 通过
VOLUME
指定存放网页静态资源的卷 - 通过
EXPOSE
指令声明暴露的 Nginx 服务端口 - 通过
EXTRYPOINT
和CMD
指令启动 Nginx 服务
启动容器
在启动多个容器时,当这些容器共享卷时,通过 docker run
的 --volumes-from <container>
选项可以直接将指定容器的所有卷全部加入到命令正要启动的容器中。即使删除了使用卷的最后一个容器,卷中的数据也会持久保存。
当网站的 Vue.js 源代码被更新后,只需重新从 Node.js 镜像通过 docker start
启动容器即可。由于容器间共享的卷会被实时自动更新,因此 Nginx 容器不需要被更新或重启。
备份卷
当担心不小心删除某个卷时,我们可以轻松备份它。简单来说,我们可以专门构建一个用于备份卷的容器:
- 挂载一个将被备份的卷
- 挂载一个存放备份卷的宿主机路径 (实际上这也是一个卷)
- 在容器启动时,对将要备份的卷运行
tar
命令打包,并保存到存放备份卷的卷中
这里会使用到 docker run
命令的 --rm
标志 - 这个标志会使容器进程运行完毕后自动删除容器,因此专门用于那些 只会被使用一次 的容器。
最后,启动一次这个容器,就成功把指定的卷备份了一次。
捕获多容器应用的日志
当宿主机上运行着一个由多个容器组成的应用时,如何方便地聚合它们的日志?
首先,所有的容器都会挂载一个卷,将日志存放到宿主机上。不同的容器肯定会对应着不同的日志文件 (比如一个 Node.js + Redis 构成的系统)。接下来,通过一个构建一个能够运行 Logstash 的镜像来聚合日志。
在镜像中安装 Logstash,并将 Logstash 的配置文件通过 ADD
拷贝到容器内,配置文件中指定了两类信息:
- Logstash 将要监视的所有日志文件 (如 Node.js 容器的日志、Redis 主从结点的日志)
- Logstash 的输出方式 (比如输出到 STDOUT)
最后,将 Logstash 的启动命令 /bin/logstash --config=/etc/logstash.conf
作为镜像的 ENTRYPOINT
。从这个镜像中启动容器,就能够在当前容器的 STDOUT 上聚合所有容器输出的日志。
不使用 SSH 管理 Docker 容器
当我们想要操作一台远程服务器 (虚拟机) 时,通常要通过 SSH 登录上去。当我们想操作一个容器时,由于容器内没有运行 sshd
服务,否则不能使用这种方法。Docker 提供了很多种方法满足了类似需要:
- 使用卷,可以直接公开挂载到容器内的卷目录,甚至可以公开容器内的 socket
- 可以通过
docker kill
给容器发送信号 - 通过 nsenter 登入容器
Nsenter 可以实现与 SSH 类似的功能,它可以让用户登入一个已经存在的容器的 shell,而不需要任何 sshd
等守护进程。Nsenter 的安装命令还让我的脑子有点没转过来:
sudo docker run -v /usr/local/bin:/target jpetazzo/nsenter
大致意思就是这个镜像会把 nsenter 安装到容器内的 /target
目录下。现在把宿主机的 /usr/local/bin
作为卷挂载到容器内的 /target
目录下,也就是容器内运行的命令会把 nsenter 安装到宿主机的 /usr/local/bin
目录下。
安装成功后,在宿主机上获取容器进程的 pid,然后使用 nsenter 登入容器:
sudo nsenter --target <pid> --mount --uts --ipc --net --pid
在上述命令的后面,还可以直接给出想要在容器内 shell 上执行的命令 (比如 ls
)。