【Docker 那些事儿】如何安全地进入到容器内部

🌟 前言
镜像是构建容器的蓝图,Docker 以镜像为模板,构建出容器。 容器在镜像的基础上被构建,也在镜像的基础上运行,容器依赖于镜像。 本文将对 容器的运行 及相关内容进行详细讲解。
![]()
1. 容器运行
在 Docker 官方网站可以查询与 Docker 相关的资料以及帮助手册,但是内容都是英文的,可能会对一些小白造成困扰。(而且,访问 Docker 官方网站特别缓慢 ),所以这里向大家推荐 Docker 中文社区。
这是一个中文的 Docker 资料库,其中有很多整理好的技术文档,Docker 中文社区的官方介绍是:系统整理 Docker 官方的教程和手册、报道 Docker 相关动态和进展、整合网络上其他社区相关资源。
当然用户也可以将自己在学习过程中整理的技术文档上传与大家分享。
使用 docker run 命令可以运行容器,该命令底层其实是 docker create 与 docker start 两条命令的结合体,运行容器需要先基于镜像创建一个容器,然后启动容器,完成一个容器的运行,如图所示👇

例如,基于镜像启动一个新容器,并打印当月的日历,示例代码如下:

从以上示例中可以看到日历已经被打印出来,但无法看到容器是否运行。
ps 命令在 Linux 系统中被用来查看进程,在 Docker 中被用来查看容器,因为运行中的容器也是一个进程,示例代码如下:

从以上示例中可以看到,一个 Docker 容器以 CentOS 镜像为基础运行,并传了一个 cal(打印当前月份日历)命令,容器正常启动并执行了 cal 命令。
除此之外,还可以通过指定参数,启动一个 bash 交互终端,代码如下:

上述代码创建了一个交互式的容器,并分配了一个伪终端,使用户可以通过命令行与容器进行交互。终端对宿主机进行直接操作,宿主机通过一个虚拟终端将对 Docker 的指令传输给容器,这个虚拟终端就是伪终端,对容器进行直接操作。
执行 docker run 命令启动容器时,Docker 会进行如下操作。
(1)检测本地是否存在指定的镜像,不存在则从默认的 Docker Hub 公有仓库下载。
(2)使用镜像创建(docker create)并启动(docker run)容器。
(3)分配一个文件系统,并在只读层外面挂载一个可读可写层。
(4)从宿主机配置的网桥接口中桥接一个虚拟接口到容器中去。
(5)从地址池分配一个 IP 地址给容器。
(6)执行用户指定的命令。
(7)执行之后容器被终止(docker stop)。
另外,在 docker run 命令中可以添加相应参数,实现不同的功能。
下面运行一个容器,并使用终端对其进行操作,示例代码如下:

以上命令执行成功的前提是本地含有 CentOS 镜像。其中,-i 表示捕获标准输入输出,-t 表示分配一个终端或控制台。
下面运行一个容器,并为其设置环境变量,示例代码如下:

其中,-e 参数是在创建容器时为容器配置环境变量。
此时已经成功创建了一个容器,接着查看它的环境变量,示例代码如下:

从以上示例中可以看到,key=1000 的环境变量已经设置成功。
🍇 自动重启的容器
下面运行一个正常的容器,示例代码如下:

在新创建的容器中,使用 exit 命令即可退出容器,但容器也将停止运行。
查看容器状态,示例代码如下:

可以看到,容器此时的状态为 “Exited”,说明容器处于终止状态。
下面运行一个添加参数的容器,示例代码如下:

不出意外的话,此时容器应该是终止状态。
接着,验证容器的状态,示例代码如下:

从示例中可以看到,容器此时不是终止状态,而是运行状态。这是由于添加了 --restart 参数的容器被终止后自动重启。
🍇 自定义名称的容器
下面运行一个自定义名称的容器,示例代码如下:

从示例中可以看到,创建容器时添加了 -name 参数来定义容器名称。创建之后容器的名字就是指定的 "test"。
🍇 开启端口的容器
下面创建一个开启 80 端口的容器,示例代码如下:

参数冒号之前是宿主机端口号,冒号之后是容器的端口号,表示宿主机的 80 端口映射到容器的 80 端口上。
从示例中可以看到,容器正在运行,并且可以看到开启了 80 端口。
为了验证,使用 curl 工具访问容器端口,示例代码如下:

访问容器 80 端口的返回值为 200,说明容器端口能够被用户正常访问。
接下来,将容器停止,并再次访问容器端口,示例代码如下:

再次访问容器端口时,连接被拒绝,说明先前的服务由是 Docker 容器来提供的,只是通过宿主机的端口向外网开放。
🍇 与宿主机共享目录的容器
首先在宿主机上创建需要共享的目录与文件,示例代码如下:

已经在 /root/test/ 目录下别创建了 a.txt 与 b.txt 两个文件,接着创建一个可以共享这两个文件的容器,示例代码如下:

-v 参数用来指定文件路径,--privileged 参数用来给用户添加操作权限。
从示例中可以看到,目录与文件共享成功。
2. 进入容器
容器在宿主机中共有三种状态,分别为运行(Up)状态、暂停(Paused)状态与终止(Exited)状态。
下面通过示例来观察容器的三种状态。
2.1 容器的三种状态
🍑 运行状态
运行一个名为 test-nginx 的 Nginx 容器,并将容器 80 端口映射到宿主机 80 端口,示例代码如下:

这时,容器已经创建完成,通过 ps 命令查看容器是否为运行状态,示例代码如下:

从以上示例中可以看出,此时容器状态为运行状态。
🍑 暂停状态
下面通过命令使容器进入暂停状态,示例代码如下:

docker pause 是暂停容器的命令,上述示例中,暂停了名为 test-nginx 的容器。
接着通过命令查看容器是否成功暂停,示例代码如下:

从以上示例中可以看到,容器仍是运行状态,但同时也是暂停状态。
接着通过 curl 工具对该容器进行访问测试,示例代码如下:

通过访问测试发现,此时无法访问到容器网页,但是服务器没有拒绝连接,说明暂停容器的本质是暂停容器中的服务。
下面使用 docker unpause 命令使暂停状态的容器终止暂停状态,示例代码如下:

此时,命令执行完毕,接着查看容器状态,示例代码如下:

从以上代码中可以看到,暂停状态已经被终止,容器只处于运行状态。
接着用 curl 工具对容器进行访问测试,示例代码如下:

从以上示例中可以看到,此时网站已经可以正常访问,说明容器中的服务正常运行。
🍑 终止状态
当不再需要某一个业务继续运行时,就要通过命令使该业务的容器终止,示例代码如下:

以上示例使用 docker stop 命令终止了容器 test-nginx,接着验证容器状态,示例代码如下:

从以上示例中可以看出,此时容器为终止状态,接着对容器进行访问测试,示例代码如下:

从测试结果中可以看出,客户端请求被拒绝,服务已关闭。与暂停状态的容器不同是,终止状态的容器会给客户端发送拒绝的回应。
下面使用 docker start 命令将终止状态的容器唤醒,示例代码如下:

示例中使用 docker start 命令对处于终止状态的容器进行了唤醒,接着查看容器此刻状态,示例代码如下:

从以上示例中可以看出,此时容器状态为运行状态。接着对该容器进行访问测试,示例代码如下:

通过访问测试结果可以看出,此时容器中的服务已经可以正常访问。
2.2 docker attach 与 docker exec
在企业中,运维工程师与开发工程师都可能会有进入容器内部的需求。
但是不建议使用 SSH(Secure Shell)登录容器,因为这违背了一个容器里只有一个进程的原则,同时增加了被攻击的风险。
建议使用以下两种 Docker 原生方式进入容器。
🍑 docker attach
通过 docker attach 命令可以进入到一个已经在运行容器的虚拟输入设备,然后执行其他命令。
下面演示 docker attach 命令的使用方式。
创建任意一个容器,这里以 CentOS 为例,示例代码如下:

此时 CentOS 容器已经创建成功,接着使用 docker attach 命令与容器 ID 号进入容器中,示例代码如下:

在以上示例中,不仅进入了容器,还对容器执行了 ls 命令,说明此时已经可以在命令行直接对容器进行操作。
在退出容器时需要注意的是,直接从容器中使用 exit 命令或者 Ctrl+d 组合键退出容器,会导致容器终止。如果想要退出当前容器,并且不终止容器,可以使用 Ctrl+P+Q 组合键退出终端。下面进行示例演示,示例代码如下:

从以上示例中可以看到,容器已经被终止。接着将容器启动并进入容器,再使用 Ctrl+P+Q 组合键退出,示例代码如下:

上述示例启动了容器并使用 Ctrl+P+Q 组合键退出了容器,接着查看当前容器状态,示例代码如下:

上述示例中可以看到,容器处于运行状态,并没有被终止。
docker attach 还有有共享屏幕的功能,两个终端同时使用 docker attach 进入同一个容器时可以看到同步操作。如图所示:

🍑 docker exec
下面对 exec 的参数进行介绍,如表所示。

docker exec 可以在宿主机上向运行的容器传输命令,示例代码如下:

以上示例通过 docker exec 命令向容器发送 ls 命令,并将结果回显至终端。
下面创建一个新容器,并为容器启动一个虚拟终端,使用命令行对容器进行操作,示例代码如下:

上述示例通过虚拟终端对容器进行一系列的操作。接着使用 exit 命令退出容器,并查看容器状态,示例代码如下:

以上示例使用 exit 命令退出了容器,但容器仍在运行状态。
这说明 docker exec 与 docker attach 不同,在使用 exec 进入的容器中执行 exit 命令不会终止容器,只会退出当前 bash 终端。
所以在工作中,建议大家使用 docker exec 命令进入容器,这样不容易出现操作失误。
版权声明: 本文为 InfoQ 作者【飞向星的客机】的原创文章。
原文链接:【http://xie.infoq.cn/article/80e385e6cc454f05cc961fd40】。文章转载请联系作者。
评论