Skip to content

容器

容器是 Docker 的另一个核心概念。简单来说,容器是镜像的一个运行实例。
所不同的是,镜像是静态的只读文件,而容器带有运行时需要的可写文件层,同时,容器中的应用进程处于运行状态。

如果认为虚拟机是模拟运行的一整套操作系统(包括内核、应用运行态环境和其他系统环境)和跑在上面的应用。
那么 Docker 容器就是独立运行的一个(或一组)应用,以及它们必须的运行环境。

创建容器

新建容器

可以使用docker [container] create命令新建一个容器,例如

1
2
3
4
5
➜  Desktop docker create -it ubuntu:latest
c96f6b187cede927d1d196b7de972cd9082b6e738de06e485021628b6e4793da
➜  Desktop docker ps -a
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
c96f6b187ced        ubuntu:latest       "/bin/bash"         10 seconds ago      Created                                 awesome_turing

使用docker [container] create命令新建的容器处于停止状态,可以使用docker [container] start命令来启动它

由于容器是整个 Docker 技术栈的核心,create 命令和后续的 run 命令支持的选项都十分复杂,需要在实践中不断体会。

选项主要包括如下几大类:与容器运行模式相关、与容器环境配置相关、与容器资源限制和安全保护相关

create 命令与容器运行模式相关的选项

选项 说明
-a, --attach=[] 是否绑定到标准输入、输出和错误
-d, --detach=true|false 是否在后台运行容器,默认为否
--detach-keys="" 从 attach 模式退出的快捷键
--entrypoint="" 镜像存在入口命令时,覆盖为新的命令
--expose=[] 指定容器会暴露出来的端口或端口范围
--group-add=[] 运行容器的用户组
-i, --interactive=true|false 保持标准输入打开,默认为 false
--ipc="" 容器 IPC 命名空间,可以为其他容器或主机
--isolation="default" 容器使用的隔离机制
--log-driver="json-file" 指定容器的日志驱动类型,可以为json-file、syslog、journald、gelf、fluentd、awslogs、splunk、etwlogs、gcplogs 或 none
--log-opt=[] 传递给日志驱动的选项
--net="bridge" 指定容器网络模式,包括 bridge、none、其他容器内网络、host的网络或某个现有网络等
--net=alias=[] 容器在网络中的别名
-p, --publish-all-true|false 通过NAT机制将容器标记暴露的端口自动映射到本地主机的临时端口
-p, --publish=[] 指定如何映射到本地主机端口,例如 -p 11234-12234:1234-2234
--pid=host 容器的 PID 命名空间
--userns="" 启动 userns-remap 时配置用户命名空间的模式
--uts=host 容器的 UTS 命名空间
--restart="no" 容器的重启策略,包括 no、on-failure[:max-retry]、always、unless-stopped 等
--rm, --rm=true|false 容器退出后是否自动删除,不能跟 -d 同时使用
-t, --tty=true|false 是否分配一个伪终端,默认为 false
--tmpfs=[] 挂在临时文件系统到容器
-v|--volume[=[HOST-DIR:]CONTAINER-DIR[:OPTIONS]]] 挂载主机上的文件卷到容器内
--volume-driver="" 挂载文件卷的驱动类型
--volumes-from=[] 从其他容器挂载卷
-w, --workdir="" 容器内的默认工作目录

create 命令与容器环境和配置相关的选项

选项 说明
--add-host=[] 在容器内添加一个主机名到 IP 地址的映射关系(通过/etc/hosts文件)
--device=[] 映射物理机上的设备到容器内
--dns-search=[] DNS搜索域
--dns-opt=[] 自定义的 DNS 选型
--dns=[] 自定义的 DNS 服务器
-e, --env=[] 指定容器内的环境变量
--env-file=[] 从文件中读取环境变量到容器内
-h, --hostname="" 指定容器内的容器内
--ip="" 指定容器的 IPv4 地址
--ip6="" 指定容器的 IPv6 地址
--link=[<name or id>:alias] 链接到其他容器
--link-local-ip=[]: 容器的本地链接地址列表
--mac-address="" 指定容器的 Mac 地址
--name="" 指定容器的别名

启动容器

使用docker [container] start命令来启动一个已经创建的容器。

1
2
3
4
5
6
7
8
➜  Desktop docker create -it ubuntu
6c2261de8e8ded22325fed3beb91b300dc43818d4e0fa39f32eca2099ce428b7
➜  Desktop docker start 6c
6c
➜  Desktop docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
6c2261de8e8d        ubuntu              "/bin/bash"         16 seconds ago      Up 7 seconds                            nostalgic_buck
➜  Desktop

新建并启动容器

除了创建容器后通过 start 命令来启动,也可以直接新建并启动容器

所需要的命令主要为docker [comntainer] run,等价于先执行docker [container] create命令,再执行docker [container] start命令。

例如,下面的命令输出一个"Hello World",之后容器自动终止:

1
2
➜  ~ docker run ubuntu /bin/echo "Hello World"
Hello World

这跟在本地直接执行/bin/echo 'hello world'相比几乎感觉不出任何区别

当利用docker [container] run来创建并启动容器时,Docker 在后台运行的标准操作包括:

  • 检查本地是否存在指定的镜像,不存在就从公有仓库下载
  • 利用镜像创建一个容器,并启动该容器
  • 分配一个文件系统给容器,并在只读的镜像层外面挂载一层可读写层
  • 从宿主主机配置的网桥接口中桥接一个虚拟接口到容器中去
  • 从网桥的地址池配置一个 IP 地址给容器
  • 执行用户指定的应用程序
  • 执行完毕后容器被自动终止

下面的命令启动一个 bash 终端,允许用户进行交互:

1
2
➜  ~ docker run -it ubuntu /bin/bash
root@acc93199df2a:/#

其中,-t选项让 Docker 分配一个伪终端(pseudo-tty)并绑定到容器的标准输入上,-i则让容器的标准输入保持打开。
更多的命令选项可以通过docker run --help命令来查看

在交互模式下,用户可以通过所创建的终端来输入命令,例如:

1
2
3
4
5
6
7
8
root@acc93199df2a:/# pwd
/
root@acc93199df2a:/# ls
bin  boot  dev  etc  home  lib  lib32  lib64  libx32  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var
root@acc93199df2a:/# ps
  PID TTY          TIME CMD
    1 pts/0    00:00:00 bash
    9 pts/0    00:00:00 ps

在容器内用 ps 命令查看进程,可以看到,只运行了 bash 应用,并没有运行其他无关的进程。

用户可以按Ctrl + d或输入exit命令来退出容器:

1
2
root@acc93199df2a:/# exit
exit

对于所创建的 bash 容器,当用户使用exit命令退出 bash 进程之后,容器也会自动退出。
这是因为对于容器来说,当其中的应用退出后,容器的使命完成,也就没有继续执行的必要了

可以使用docker container wait CONTAINER[CONTAINER...]子命令来等待容器退出,并打印退出返回结果。

某些时候,执行docker [container] run时候因为命令无法正常执行容器会出错直接退出,此时可以查看退出的错误代码。

默认情况下,常见的错误代码包括:

  • 125: Docker daemon 执行出错,例如指定了不支持的 Docker 命令参数
  • 126: 所指定命令无法执行,例如权限出错
  • 127: 容器内命令无法找到

命令执行后出错,会默认返回命令的退出错误码

守护态运行

更多的时候,需要让 Docker 容器在后台以守护态形式运行。
此时,可以通过添加-d参数来实现。

例如,下面的命令会在后台运行容器:

1
2
➜  ~ docker run -d ubuntu /bin/sh -c "while true; do echo hello world; sleep 1; done"
a021a4cfe54bdf5c2453fd2eb7f012a2a350f7945ef7282b58d19a2cdfc2f1b6

容器启动后会返回一个唯一的 id,也可以通过docker psdocker container ls命令来查看容器信息

查看容器输出

要获取容器的输出信息,可以通过docker [container] logs命令。
该命令支持的选项包括:

  • -details: 打印详细信息
  • -f, -follow: 持续保持输出
  • -since string:输出从某个时间开始的日志
  • -tail string: 输出最近的若干日志
  • -t, -timestamps: 显示时间戳信息
  • -until string: 输出某个时间之前的日志

例如,查看某容器的输出可以使用如下命令:

1
2
3
4
5
6
7
➜  ~ docker logs a021
hello world
hello world
hello world
hello world
hello world
...

停止容器

暂停容器

可以使用docker [container] pause CONTAINER[CONTAINER...]命令来暂停一个运行中的容器。

例如,启动一个容器,并将其暂停:

1
2
3
4
5
6
7
8
9
➜  ~ docker run --name test --rm -it ubuntu bash
➜  ~ docker ps -a
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
e35163ac3105        ubuntu              "bash"              22 seconds ago      Up 21 seconds                           test
➜  ~ docker pause test
test
➜  ~ docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS                   PORTS               NAMES
e35163ac3105        ubuntu              "bash"              42 seconds ago      Up 41 seconds (Paused)                       test

处于 paused 状态的容器,可以使用docker [container] unpause CONTAINER[CONTAINER...]命令来恢复到运行状态。

1
2
3
➜  ~ docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
e35163ac3105        ubuntu              "bash"              5 minutes ago       Up 5 minutes                            test

终止容器

可以使用docker [container] stop来终止一个运行中的运行中的容器。
该命令的格式为docker [container] stop [-t| --time[=10]][CONTAINER...]

该命令会首先向容器发送 SIGTERM 信号,等待一段超时时间后(默认为 10 秒),再发送 SIGKILL 信号来终止容器

1
2
3
4
5
➜  ~ docker ps -a
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
e35163ac3105        ubuntu              "bash"              25 minutes ago      Up 25 minutes                           test
➜  ~ docker stop e35
e35

此时,执行docker container prune命令,会自动清除掉所有处于停止状态的容器

此外,还可以通过docker [container] kill直接发送 SIGKILL 信号来强行终止容器

当 Docker 容器中指定的应用终结时,容器也会自动终止。
对于启动了一个终端的容器,用户通过 exit 命令或Ctrl + d来退出终端时,所创建的容器立刻停止,处于stopper状态

可以用docker ps -qa命令看到所有容器的 ID。例如:

1
2
➜  ~ docker ps -qa
3e02f5ef52b3

处于停止状态的容器,可以通过docker [container] start命令来重新启动:

docker [container] restart命令会将一个运行态的容器先终止,然后再重新启动

进入容器

在使用-d参数时,容器启动后会进入后台,用户无法看到容器中的信息,也无法进行操作。

这个时候如果需要进入容器进行操作,推荐使用官方的attachexec命令。

attach 命令

attach 是 Docker 自带的命令,命令格式为:

docker [container] attach [--detach-keys[=[]]] [--no-stdin] [--sig-proxy[=true]] CONTAINER

这个命令支持三个主要选项:

  • --detach-keys[=[]]: 指定退出 attach 模式的快捷键序列,默认是CTRL -p CRTL -q
  • --no-stdin=true|false: 是否关闭标准输入,默认是保持打开
  • --sig-proxy=true|false: 是否代理收到的系统信号给应用进程,默认为 true

示例:

1
2
3
4
5
6
7
➜  ~ docker run -itd ubuntu
6d6057b75b7bc0071dee7d3ddcadc539471d7e7c5f70f82106237f9f3ff76542
➜  ~ docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
6d6057b75b7b        ubuntu              "/bin/bash"         45 seconds ago      Up 44 seconds                           romantic_kowalevski
➜  ~ docker attach romantic_kowalevski
root@6d6057b75b7b:/#

然而使用 attach 命令有时候并不方便。
当多个窗口同时 attach 到同一个容器的时候,所有窗口都会同步显示;
当某个窗口因命令阻塞时,其他窗口也无法执行操作了

exec 命令

从 Docker 的 1.3.0 版本起,Docker 提供了一个更加方便的工具exec命令,可以在运行中容器内直接执行任意命令。

该命令的基本格式为:

docker [container] exec [-d|--detach] [--detach-key[=[]] [-i|--interactive] [--privileged] [-t|--tty] [-u|--user[=USER]] CONTAINER COMMAND [ARG...]

比较重要的参数有:

  • -d, --detach: 在容器中后台执行命令
  • --detach-keys="": 指定将容器切回后台的按键
  • -e, --env=[]: 指定环境变量列表
  • -i, --interactive=true|false: 打开标准输入接受用户输入命令,默认值为 false
  • --privileged=true|false: 是否给执行命令以高权限,默认值为 false
  • -t, --tty=true|false: 分配伪终端,默认值为false
  • -u, --user="": 执行命令的用户名或 ID

例如,进入到刚创建的容器中,并启动一个 bash

1
2
3
4
5
➜  ~ docker ps -a
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
6d6057b75b7b        ubuntu              "/bin/bash"         21 minutes ago      Up 21 minutes                           romantic_kowalevski
➜  ~ docker exec -it 6d60 /bin/bash
root@6d6057b75b7b:/#

可以看到会打开一个新的 bash 终端,在不影响容器内其他应用的前提下,用户可以与容器进行交互

注意
通过指定-it参数来保持标准输入打开,并且分配一个伪终端。
通过exec命令对容器执行操作是最为推荐的方式

进一步地,可以在容器中查看用户和进程信息:

1
2
3
4
5
6
7
8
root@6d6057b75b7b:/# w
 09:37:21 up 1 day,  3:04,  0 users,  load average: 0.00, 0.00, 0.00
USER     TTY      FROM             LOGIN@   IDLE   JCPU   PCPU WHAT
root@6d6057b75b7b:/# ps -ef
UID        PID  PPID  C STIME TTY          TIME CMD
root         1     0  0 09:12 pts/0    00:00:00 /bin/bash
root         8     0  0 09:33 pts/1    00:00:00 /bin/bash
root        17     8  0 09:37 pts/1    00:00:00 ps -ef

删除容器

可以使用docker [container] rm命令来删除处于终止或退出状态的容器,命令格式为docker [container] rm [-f|--force] [-l|--link] [-v|--volumes] CONTAINER [CONTAINER...]

主要支持的选项包括:

  • -f, --force=false: 是否强行终止并删除一个运行中的容器
  • -l, --link=false: 删除容器的连接,但保留容器
  • -v, --volumes=false: 删除容器挂载的数据卷。

例如,查看处于终止状态的容器,并删除

1
2
3
4
5
➜  ~ docker ps -a
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS                     PORTS               NAMES
6d6057b75b7b        ubuntu              "/bin/bash"         35 minutes ago      Exited (0) 3 seconds ago                       romantic_kowalevski
➜  ~ docker rm 6d60
6d60

默认情况下,docker rm命令只能删除已经处于终止或退出状态的容器,并不能删除还处于运行状态的容器。

如果要直接删除一个运行中的容器,可以添加-f参数。
Docker 会先发送SIGKILL信号给容器,终止其中的应用,之后强行删除

1
2
3
4
5
6
➜  ~ docker run -d ubuntu:18.04 /bin/sh -c "while true; do echo hello world; sleep 1; done"
27f29aaa5fe13cafaabafb1d124ee2c9032dccff375b6c015894a53f41c55b87
➜  ~ docker rm 27f
Error response from daemon: You cannot remove a running container 27f29aaa5fe13cafaabafb1d124ee2c9032dccff375b6c015894a53f41c55b87. Stop the container before attempting removal or force remove
➜  ~ docker rm -f 27f
27f
1
2
3
docker ps -a  | grep Exited | awk '{print $1}' | xargs docker rm

docker container prune

容器与本地拷贝文件

https://docs.docker.com/engine/reference/commandline/cp/

1
2
docker cp [OPTIONS] CONTAINER:SRC_PATH DEST_PATH|-
docker cp [OPTIONS] SRC_PATH|- CONTAINER:DEST_PATH

导入和导出容器

某些时候,需要将容器从一个系统迁移到另外一个系统,此时可以使用 Docker 的导入和导出功能,这也是 Docker 自身提供的一个重要特性

导出容器

导出容器是指,导出一个已经创建的容器到一个文件,不管此时这个容器是否处于运行状态。
可以使用docker [container] export命令,该命令格式为

docker [container] export [-o|--output[=""]] CONTAINER

其中,可以通过-o选项来指定导出的tar文件名,也可以直接通过重定向来实现

首先,查看所有的容器,如下所示:

1
2
3
➜  ~ docker ps -a
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS                     PORTS               NAMES
27cdfbff5999        ubuntu              "echo hhh"          5 seconds ago       Exited (0) 3 seconds ago                       vigilant_swanson

导出27cdfbff5999容器到文件test_for_run.tar

1
2
3
➜  ~ docker export -o test_for_run.tar 27cd
➜  ~ rm test_for_run.tar
➜  ~ docker export 27c > test_for_stop.tar

之后,可将导出的 tar 文件传输到其他机器上,然后再通过导入到系统中,实现如果容器的迁移

导入容器