在Docker容器中如何用docker exec运行命令
导语
Docker为开发者们提供了一个打包工具,帮助开发者创建与管理可移植且能保持一致的的Linux容器。
在开发与使用容器时,经常需要查看一个正在运行的容器,检查其状态或是进行debug。对此,可以使用Docker提供的 docker exec
命令,在运行中的容器内部再运行命令。
本教程中,我们将了解 docker exec
命令,了解如何用它在一个运行中的Docker容器中运行命令并得到一个交互式shell。
前期准备
本教程需要读者提前完成Docker的安装,并拥有运行 docker
的权限。如果需要作为root用户运行 docker
,请在本教程中的命令前加上 sudo
。
启动一个测试用容器
docker exec
命令需要在一个运行中的Docker容器里运行。如果没有现成的容器,可以用 docker run
命令启动一个测试用容器:
$ docker run -d --name container-name alpine watch "date >> /var/log/date.log"
这会创建一个基于官方 alpiane
镜像的容器。alpine
镜像是一个很流行的容器镜像,使用了Linux的轻量简化版Alpine Linux。
-d
标记用于将容器从终端中分离,使其在后台运行。命令 --name container-name
会将容器命名为 container-name
,可以使用任意名称,也可以留为空白,Docker会为新容器自动生成一个独特名称。
随后的 alpine
指定了容器使用的镜像。
最后的 watch "date >> /var/log/date.log"
部分是需要在容器内部运行的命令。watch
会重复运行给定的命令,默认间隔为两秒。在这个例子里,watch
会运行命令 date >> /var/log/date.log
。date
会打印现在的日期与时间,效果如下:
Output
Fri Jul 23 14:57:05 UTC 2021
而指令中的 >> /var/log/date.log
部分会将 date
的输出导入到文件 /var/log/date.log
中,附加在文件底部。每过两秒,新的一行就会附加在文件底部。一段时间后,文件内部就会如下:
Output
Fri Jul 23 15:00:26 UTC 2021
Fri Jul 23 15:00:28 UTC 2021
Fri Jul 23 15:00:30 UTC 2021
Fri Jul 23 15:00:32 UTC 2021
Fri Jul 23 15:00:34 UTC 2021
下一步中将会解释如何找到Docker容器的名称。当如果已经有一个目标容器,却不确定该容器的名称时,下一步中的内容会十分有用。
寻找Docker容器的名称
在运行 docker exec
前,需要输入容器的名称(或ID)来指定想要使用的容器。可以用 docker ps
命令来寻找这些信息:
$ docker ps
这条命令会列出目前服务器中正在运行的所有Docker容器,还会给出它们的一些高级信息:
Output
CONTAINER ID IMAGE COMMAND
76aded7112d4 alpine "watch 'date >> /var…"
CREATED STATUS PORTS
11 seconds ago Up 10 seconds
NAMES
container-name
在这个例子中,容器名与ID都可以用在 docker exec
中指定所用容器。
如果需要重命名容器,可以用 docker rename
命令:
$ docker rename container-name new-name
接下来,我们会分析几个用 docker exec
在运行中的Docker容器里运行命令的例子。
在Docker容器内运行交互性shell
当需要在Docker容器中启动一个交互式shell时,例如为了检视文件系统或debug正在运行的进程时,可以用 docker exec
后加 -i
与 -t
标记。
-i
标记用来保持输入始终在容器中打开,而 -t
标记会给shell分配一个伪终端。这些标记可以结合,写法如下:
$ docker exec -it container-name sh
该命令会在指定的容器中运行 sh
shell,并提供基本的shell提示符。如果要退出容器,输入 exit
然后按下ENTER键:
/# exit
如果容器镜像中包含更高级的shell,例如 bash
,可以将上面的 sh
替换为 bash
。
在Docker容器内部运行一个非交互式的命令
如果想在运行中的容器内部运行非交互式的命令,可以直接使用 docker exec
命令,不加任何标记:
$ docker exec container-name tail /var/log/date.log
这个命令会在 container-name
容器中运行 tail /var/log/date.log
,并输出结果。默认情况下,tail
会打印出文件的最后十行。以刚才在第一部分创建的容器为例,以下结果会被输出:
Output
Mon Jul 26 14:39:33 UTC 2021
Mon Jul 26 14:39:35 UTC 2021
Mon Jul 26 14:39:37 UTC 2021
Mon Jul 26 14:39:39 UTC 2021
Mon Jul 26 14:39:41 UTC 2021
Mon Jul 26 14:39:43 UTC 2021
Mon Jul 26 14:39:45 UTC 2021
Mon Jul 26 14:39:47 UTC 2021
Mon Jul 26 14:39:49 UTC 2021
Mon Jul 26 14:39:51 UTC 2021
本质上,这等同于在Docker容器内打开一个交互式shell,并在shell内运行 tail /var/log/date.log
命令。然而,这个方法不需要打开shell及在命令完成后关闭shell,也不需要分配一个伪终端,只需要一条命令就可以得到一样的输出。
在Docker容器中的自选路径运行命令
用 --workdir
标记指定路径,就可以在容器内部的指定路径运行命令:
$ docker exec --workdir /tmp container-name pwd
这条指令会创建 /tmp
路径作为工作路径,然后运行 pwd
命令。pwd
命令会打印出目前的工作路径:
Output
/tmp
这个结果确认了目前工作路径是 /tmp
。
以不同用户身份在Docker容器内部运行命令
添加 --user
标记即可在容器内以不同用户身份运行命令:
$ docker exec --user guest container-name whoami
这会以guest用户在容器内运行 whoami
命令。whoami
会打印目前用户的用户名:
Output
guest
这个结果证实了目前容器内的用户是guest。
将环境变量传入Docker容器内
有时,在将命令传入容器的同时还需要一同传入环境变量。-e
标记可以用于指定一个环境变量。
$ docker exec -e TEST=sammy container-name env
这条命令会将 sammy
赋值给 TEST
环境变量,然后在容器内运行 env
命令。env
会打印所有环境变量:
Output
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOSTNAME=76aded7112d4
TEST=sammy
HOME=/root
可以看到,TEST
已被设置为 sammy
。
如果要给多个变量赋值,需要重复 -e
标记:
$ docker exec -e TEST=sammy -e ENVIRONMENT=prod container-name env
如果需要将一个全部是环境变量的文档传入,可以用 --env-file
标记。
首先,用文档编辑器创建一个文件。在这个例子中,我们使用 nano
打开新文件,你也可以用任何你喜欢的编辑器:
$ nano .env
用 .env
作为文件名,这是用此类文件控制版本之外信息的一个普遍标准。
现在在文件内用 KEY=value
格式赋值变量,每个变量一行,如下:
TEST=sammy
ENVIRONMENT=prod
随后,保存并关闭文件。先按 CTRL+0
,在按 ENTER
就可以保存,然后按 CTRL+X
就可以退出 nano
了。
现在运行 docker exec
命令,并在 --env-file
后指定文件名:
$ docker exec --env-file .env container-name env
Output
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOSTNAME=76aded7112d4
TEST=sammy
ENVIRONMENT=prod
HOME=/root
可以看到文件中的两个变量已被赋值。
使用是 --env-file
标记时,可以指定多个文件。如果不同文件内部多次覆盖了同一个变量,指令内排在后面的文件会覆盖之前的。
常见报错
在使用 docker exec
命令时,可能会遇到以下几个常见报错:
Error: No such container: container-name
No such container
报错表明指定的容器不存在,可能是容器名拼写错误导致。需要用 docker ps
命令列出所有运行中容器的名字,并检查容器名输入是否有误。
Error response from daemon: Container 2a94aae70ea5dc92a12e30b13d0613dd6ca5919174d73e62e29cb0f79db6e4ab is not running
not running
消息表明虽然指定的容器存在,但已经停止运行了。可以通过 docker start container-name
命令启动容器。
Error response from daemon: Container container-name is paused, unpause the container before exec
Container is paused
报错将问题解释的比较清楚,需要用 docker unpause container-name
恢复容器的进程。
结语
本教程简单介绍了如何在运行中的容器内部再执行命令,同时给出了一些可能有帮助的命令行举例。 如果需要了解更多有关Docker的信息,请参阅Docker tag page,那里有各类Docker教程、相关问答及其它有用信息的链接。