学而实习之 不亦乐乎

Docker 容器操作:创建、停止、进入、删除、导入和导出、查看

2023-09-12 07:58:30

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

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

容器是直接提供应用服务的组件,也是 Docker 整个技术栈中最为核心的概念。围绕容器, Docker 提供了十分丰富的操作命令,允许用户高效地管理容器的整个生命周期。使用 docker container help 命令查看 Docker 支持的容器操作子命令。在生产环境中,为了提高容器的高可用性和安全性,一方面要合理使用资源限制参数来管理容器的资源消耗;另一方面要指定合适的容器重启策略,来自动重启退出的容器。此外,还可以使用 HAProxy 等辅助工具来处理负载均衡,自动切换故障的应用容器。

一、创建容器

1、新建容器

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

# docker create -it ubuntu:latest

使用 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, no-failure[:max-retry], always, unless-stopped 等
  • --rm=true|false 容器退出后是否自动删除, 不能跟-d 同时使用
  • -t, --tty=true|false    是否分配一个伪终端,默认为 : false
  • --tmpfs=[]  挂载零时文件系统到容器
  • -v, --volume[=[[HOST-DIR:] CONTAINERE-DIR [:OPTIONS]]]  挂在主机上的文件卷到容器内
  • --volume-driver=""  挂载文件卷的驱动类型
  • --volumes-form=[]   其他容器挂载卷
  • -w, --workdir=""    容器内的默认工作目录

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

  • --add-host=[]   在容器内添加一个主机名到IP地址的映射关系 (通过 /etc/hosts 文件)
  • --device=[] 映射物理机上的设备到容器内
  • --dns-search=[] DNS 搜索域
  • --dns-opt=[]    自定义DNS
  • --dns=[]    自定义DNS服务器
  • -e, --env=[]    指定容器内的环境变量
  • -h, hostname=[] 指定容器内的主机名
  • --ip="" 指定容器的ipv4 地址
  • --ip6=""    指定容器的ipv6 地址
  • --link=[<name or id>:alias] 链接到其他容器
  • --mac-address=""    指定容器的MAC地址
  • --name=""   指定容器的别名

create 命令与容器资源限制和安全保护相关的选项

  • --blkio-weight=10~1000  容器的读写块设备的I/O性能权重,默认为 0
  • --blkio-weight-device=[DEVICE_NAME:WEIGHT]  指定各个设备的 I/O 性能权重,
  • --cpu-shares=0  允许容器使用CPU资源的对权重, 默认一个容器能用满一个核的CPU
  • --cap-add=[]    增加容器的 Linux 指定安全能力
  • --cap-drop=[]   移除容器的 Linux 指定安全能力
  • --cgroup-parent=""  容器cgroups 限制的创建路径
  • --cid-file=""   指定容器进程ID号写到文件
  • --cpu-period=0  限制容器在CFS调度器下 的CPU 占用时间片
  • --cpuset-cpus=""    限制容器能用那些CPU核心
  • --cpuset-mems=""    NUMA架构下使用那些核心的内存
  • --cpu-quota=0   限制容器在CFS 调度器下的CPU 配额
  • --device-read-bps=[]    挂在设备的读吞吐率(以 bps 为单位)限制
  • --device-write-bps=[]   挂在设备的写吞吐率(以 bps 为单位)限制
  • --device-read-iops=[]   挂在设备的读速率(以每秒 i/o 次数 为单位)限制
  • --device-write-iops=[]  挂在设备的写速率(以每秒 i/o 次数 为单位)限制
  • --kernel-memory=""  限制容器使用内核的内存大小,单位可以是 b 、k 、m 或 g
  • -m, --memory="" 限制容器内使用的内存,单位可以是 b 、k 、m 或 g
  • --memory-reservation="" 当系统中内存过低时,容器会被牵制限制内存到给定值,默认情况下等于内存限制值
  • --memory-swap="LIMIT"   限制容器是用内存和交换区的总大小
  • --oom-kill-disable=true|false   内存耗尽(Out-Of-Memory)时是否杀死容器
  • --oom-score-adj=""  调整容器内存耗尽参数
  • --pids-limit="" 限制容器的pid个数
  • --privileged=true|false 是否给容器以最高权限,这意味着容器内应用将不受权限下限制,一般不推荐
  • --read-only=true|false  是否让容器内的文件系统只读
  • --security-opt=[]   指定一些安全参数,包括权限、安全能力、apparmor 等
  • --stop-signal=SIGTERM   指定停止容器的系统信号
  • --shm-size=""   /dev/shm 的大小
  • --sig-proxy=true|false  是否代理收到的信号给应用,默认为true,不能代理SIGCHLD、SIGSTOP 和 SIGKILL 信号
  • memory-swappiness="0~100"   调整容器内内存交换区参数
  • -u, --user=""   指定容器在执行命令的用户信息
  • --ulimit=[] 通过ulimit 来限制最大文件数、 最大进程数等

 

其他比较重要的选项还包括:

  • -l,--label=[]:以键值对方式指定容器的标签信息;
  • --label-file=[]:从文件中读取标签信息。

2、启动容器

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

# docker start af
af

af 为容器 id 的缩写即可定位容器,当然可以是完整的容器 ID(一般可以使用该 ID 的前若干个字符组成的可区分串来替代完整的 ID)。

此时,通过 docker ps 命令,可以查看到一个运行中的容器:

# docker ps

3、新建并启动容器

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

docker [container] run 命令等价于先执行 docker [container] creat 命令,再执行 docker [container] start 命令。

例如:

# docker run ubuntu /bin/echo 'Hello world'

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

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

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

如果想要启动一个 bash 终端后允许用户进行交互,可执行如下命令:

# docker run -it ubuntu:14.04 /bin/bash
  • -t 选项让Docker分配一个伪终端(pseudo-tty)并绑定到容器的标准输入上
  • -i 让容器的标准输入保持打开。

4、守护态运行

大部分情况下,需要让 Docker 容器在后台以守护态(Daemonized)形式运行。此时,可以通过添加 -d 参数来实现。

如:

# docker run -d ubuntu  /bin/sh -c "while true; do echo hello world; sleep 1; done"
ce554267d7a4c34eefc92c5517051dc37b91Bb588736d0823e4c846596b04d83

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

5、查看容器输出

要获取容器的输出信息,可以通过 docker [container] logs 命令。

该命令支持的选项包括:

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

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

# docker logs ce5
... ...
(ce5 是容器 ID)

二、停止容器

1、暂停容器

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

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

# docker run --name test --rm -it ubuntu bash
# docker pause test
# docker ps

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

2、终止容器

使用 docker [container] stop 来终止一个运行中的容器。

命令格式:docker [container] stop [-t | --time[=10]] [CONTAINER... ]

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

#dockers stop ce5
ce5

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

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

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

用 docker ps -qa 命令查看所有容器的 ID 。如:

# docker ps -qa
ce554267d7a4
d5805008lfe3
e812617b4lf6

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

# docker start ce554267d7a4
ce554267d7a4
# docker ps
... ...

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

# docker restart ce554267d7a4
ce554267d7a4
# docker ps
... ...

三、 进入容器

在使用 -d 参数时,容器启动后会进入后台,用户无法看到容器中的信息,也无法进行操作。这个时候如果需要进入容器进行操作,推荐使用官方的 attach 或 exec 命令。

1、attach 命令

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

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

选项:

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

 

例如:

# docker run -itd ubuntu
243c32535da7d142fb0e6df616a3c3ada0b8ab417937c853a9e1c251f499f550
# docker ps
CONTAINER ID   IMAGE    COMMAND   CREATED   STATUS    PORTS   NAMES
243c32535da7   ubuntu:latest  "/bin/bash"    18 seconds ago   Up 17 seconds  
    nostalgic_hypatia
# docker attach nostalgic_hypatia
root@243c32535da7:/#

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

2、exec命令

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

该命令的基本格式为:

docker exec [-d|--detach] [--detach-keys[=[]]] [-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。

如 :

# docker exec -it 243c32535da7 /bin/bash

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

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

四、删除容器

使用 docker [container] rm 命令可以删除处于终止或退出状态的容器。

命令格式:docker [container] rm [-f|--force] [-l|--link] [-v|--volumes] CONTAINER[CONTAINER...]

选项:

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

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

$ docker ps -a
... ...
$ docker rm ce554267d7a4

默认情况下,docker rm 命令只能删除处于终止或退出状态的容器,并不能删除还处于运行状态的容器,如果要直接删除一个运行中的容器,可以添加 -f 参数。Docker 会先发送 SIGKILL 信号给容器,终止其中的应用,之后强行删除,如下所示:

root@ubuntu:~# docker run -d ubuntu:18.04  /bin/sh -c "while true; do echo hello world; sleep 1; done"
43c1b1b60ca5de4e0d01cd655179b1c95703bd01083adaa9ff0a1edb2232b0bc
root@ubuntu:~# docker rm 43
Error response from daemon: You cannot remove a running container... ...
root@ubuntu:~# docker rm -f 43
43
root@ubuntu:~#

五、导入和导出容器

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

1、导出容器

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

命令格式:

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

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

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

$ docker ps -a
... ...

导出 ce554267d7a4 容器和 e812617b41f6 容器到 tar 文件:

# docker export -o test_for_run.tar ce5
# docker export e81 > test_for_stop.tar

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

2、导入容器

导出的文件可以使用 docker [container] import 命令导入变成镜像,

命令格式:docker import [-c|--change[=[]]] [-m|--message[=MESSAGE]] file|URL|-[REPOSITORY[:TAG]]

用户可以通过-c, --change=[] 选项在导入的同时执行对容器进行修改的 Dockerfile 指令。

如:

# docker import test_for_run.tar - test/ubuntu:v1.0

实际上,既可以使用 docker load 命令来导入镜像存储文件到本地镜像库,也可以使用 docker import 命令来导入一个容器快照到本地镜像库, 这两者的区别在于容器快照文件将丢弃所有的历史记录和元数据信息(即仅保存容器当时的快照状态),而镜像存储文件将保存完整记录,体积也更大。此外,从容器快照文件导入时可以重新指定标签等元数据信息。

六、查看容器

1、查看容器详情

查看容器详情可以使用 docker container inspect [OPTIONS] CONTAINER [CONTAINER ...] 子命令。

查看某容器的具体信息,会以 json 格式返回包括容器 Id 、创建时间、路径、状态、镜像、配置等在内的各项信息,如下:

$ docker container inspect test

2、查看容器内进程

查看容器内进程可以使用 docker [container] top [OPTIONS] CONTAINER [CONTAINER ...]子命令。

这个子命令类似于 Linux 系统中的 top 命令,会打印出容器内的进程信息,包括 PID、用户、时间、命令等。

查看某容器内的进程信息,如下:

$ docker top test
... ...

3、查看统计信息

查看统计信息可以使用 docker [container] stats [OPTIONS] [CONTAINER ...] 子命令,会显示 CPU 、内存、存储、网络等使用情况的统计信息。

选项:

  • -a,--all: 输出所有容器统计信息,默认仅在运行中;
  • -format string:格式化输出信息;
  • -no-stream:不持续输出,默认会自动更新持续实时结果;
  • -no-trunc: 不截断输出信息。

七、其他容器命令

1、复制文件

container cp 命令支持在容器和主机之间复制文件。

命令格式:docker [container] cp [OPTIONS] CONTAINER:SRC_PATH DEST_PATH|-

选项:

  • -a,--archive:打包模式,复制文件会带有原始的 uid/gid 信息;
  • -L,-follow-link :跟随软连接。当原路径为软连接时,默认只复制链接信息,使用该选项会复制链接的目标内容。

例如,将本地的路径 data 复制到 test 容器的 /tmp 路径下:

docker [container] cp data test:/tmp/

2、查看变更

container diff 查看容器内文件系统的变更。

命令格式:docker [container] diff CONTAINER

如:查看 test 容器内的数据修改

$ docker container diff test
... ...

3、查看端口映射

container port 命令可以查看容器的端口映射情况。

命令格式:docker container port CONTAINER [PRIVATE_PORT [/PROTO]]

如:查看 test 容器的端口映射情况

$ docker container port test
9000/tcp -> 0.0.0.0:9000

4、更新配置

container update 命令可以更新容器的一些运行时配置,主要是一些资源限制份额。

命令格式:docker [container] update [OPTIONS] CONTAINER [CONTAINER...]

选项:

  • -blkio-weigh uint16:更新块 IO 限制, 10~lOOO ,默认值为 ,代表着无限制;
  • -cpu-period int:限制 CPU 调度器 CFS (Completely Fair Scheduler) 使用时间,单位为微秒,最小 1000;
  • -cpu-quota int:限制 CPU 调度器 CFS 配额,单位为微秒,最小 1000;
  • -cpu-rt-period int:限制 CPU 调度器的实时周期,单位为微秒;
  • -cpu-rt-runtime int:限制 CPU 调度器的实时运行时,单位为微秒;
  • -c,-cpu-shares int:限制 CPU 使用份额;
  • -cpus decimal: 限制 CPU 个数;
  • -cpuset-cpus string: 允许使用的 CPU 核,如 0-3, 0,1;
  • -cpuset-mems string: 允许使用的内存块,如 0-3, 0,1;
  • -kernel-memory bytes: 限制使用的内核内存;
  • -m, -memory bytes :限制使用的内存;
  • -memory-reservation bytes: 内存软限制;
  • -memory-swap bytes :内存加上缓存区的限制,- 表示为对缓冲区无限制;
  • -restart string: 容器退出后的重启策略。

例如,限制总配额为1秒,容器 test 所占用时间为 10% ,代码如下所示:

$ docker update --cpu-quota 1000000 test
test
$ docker update --cpu-period 100000 test
test