90%的网友都不会的一个Docker面试题!
最近一段时间是春季的招聘季,我看到很多 Java 程序员都写着熟练使用 Docker。然后我就问了他们一个稍微略带刁钻的 Docker 面试题。如何防止 Docker 容器自动退出?几乎没有人能够回答出来!
基于此,本文主要简单介绍 docker 容器与前置进程的关系,以及如何编写Dockerfile/docker-compose.yml
,优雅的让容器可以常驻运行。
不会 docker 的看我历史文章中的下面几篇,或者直接在历史文章中搜索 Docker 关键字!
面试官:你简历中写用过docker,能说说容器和镜像的区别吗?
docker 容器的生命周期是同容器中的前置进程相关在一起的(这点和 JVM 很像,JVM 中如果没有守护进程,JVM 也自动退出),这也是我们平时可能会遇到一些容器只是运行几秒便自动退出的原因:因为容器中没有一个常驻的前置进程,前置进程运行结束后,容器便自动退出了
。
给大家举个例子。
比如 docker hello-world:
# 一闪而过 输出一堆东西
docker run --name hello-world hello-world
# 可以看到 hello-world 容器已经退出了
docker ps -a
那怎样可以让容器不自动退出?具体点说,就是容器中的程序挂了,崩溃了,Docker 容器还能够正常运行。容器还在,只是容器内的程序未正常运行而已。这就好比,类似我们常见的 OS 容器 alpine/centos/ubuntu,操作系统之类的,在系统中启动 QQ 或者微信,或者其他程序员,这些程序崩溃了,不影响我们操作系统的正常运行。
这个问题,我在网上看到,也有不少网友提出了自己的做法。比如,有网友提议创建容器时执行一个while(true)
的死循环(当然,sleep 一下)或者用tail -f /dev/null 一
类的命令,或者做一个ping
,反正就是以开启一个可以常驻的进程作为前置目的,从而实现 Docker 容器不随着进程同时挂掉。
实际上,我们可以更优雅的方案。推荐大家使用 docker 容器的interactive
(https://stackoverflow.com/questions/40854482/interactive-command-in-dockerfile
)和tty
参数来将sh/bash
(*nix 系统必有)命令作为前置命令开启常驻运行,如此容器便不会自动退出了。
下面举例,以 alpine 为例,以 alpine 镜像做为基础镜像,创建一个 alpine 系统小容器,让其可以常驻运行,以便我们登录交互执行某些命令。
❝部分镜像可能有自己的 entrypoint 命令作为容器的前置进程,会无效化我们 run 时传入的执行 sh 的命令,需使用 --entrypoint="" 进行覆盖。如需涉及相关的访问权限,也请加上
❞--privileges=true
选项。
# 使用 alpine 系统镜像创建容器
# -i interactive=true 开启 stdin
# -t tty=true 分配会话终端
# -d 守护模式 不加也可以 不加就直接进入容器中了 需要 ctrl+p+q 切出,不能 exit,exit 相当于结束前置的 sh 会话了 容器会退出的
docker run -it -d --name my_alpine alpine sh
# my_alpine 容器处于运行状态
docker ps
# 登入容器
docker exec -it my_alpine sh
# 查看 sh 会话数量 你会发现我们 run 时开启的 sh 正在其中
ps
# apline 使用的 apk 作为包管理
# 安装个小火车
# 后续可以使用 docker commit -m "alpine with sl cmd" -a "xttblog" my_alpine xttblog/alpine_sl 生成新的镜像
apk add sl
# 退出当前sh会话 run 时开启的 sh 依然会作为前置进程保证容器的运行
exit
提交容器变更生成新的镜像。
docker commit -m "alpine with sl cmd" -a "xttblog" my_alpine xttblog/alpine_sl
docker images
# 有账号的话发布到 docker hub 上去
docker push xttblog/alpine_sl
# 后续停止/启动容器日常操作即可
docker stop alpine
docker start alpine
以上命令其实是借助 sh/bash 会话终端作为前置进程,使得容器不会自动退出。
如果你觉得在创建容器时如此书写会很粗陋,没关系,我们可以将这些都推给docker-compose
。
下面是我的docker-compose.yml
文件中的内容:
version: '3'
services:
xttblog_alpine:
container_name: xttblog_alpine
image: alpine
stdin_open: true # -i interactive
tty: true # -t tty
privileged: true
entrypoint: ["sh"] # 执行 sh
创建容器 & 登入容器。
docker-compose up -d xttblog_alpine ./
docker ps
docker exec -it xttblog_alpine sh
通过 docker-compose
将那两个参数传入进去,编排后启动服务容器。至此,我们就可以巧妙的实现 Docker 容器不随着进程的崩溃而停止运行了。