7-容器

容器

运行容器

docker run 是启动容器的方法。

三种方式指定容器启动时执行的命令:

CMD 指令。

ENDPOINT 指令。

在 docker run 命令行中指定。

docker run ubuntu pwd
/# 容器启动时执行 pwd,返回的 / 是容器中的当前目录  执行完容器结束
docker ps
docker container ls

执行 docker ps 或 docker container ls 可以查看 Docker host 中当前运行的容器:

image-20220507002749401

增加参数 –a 会显示所有状态的容器 包括已经退出的 exited的容器

docker container ls -a
docker ps -a 

image-20220507003051682

如何让容器长期运行 容器的生命周期依赖于启动时执行的命令 命令不结束 容器不会退出

docker run ubuntu /bin/bash -c  ”while true ; do sleep 1; done”

while 语句让 bash 不会退出 -d 以后台方式启动容器。

容器启动后回到了 docker host 的终端。这里看到 docker 返回了一串字符,这是容器的 ID。通过 docker ps 查看容器:

这里注意一下容器的 CONTAINER ID和 NAMES 这两个字段。

CONTAINER ID 是容器的 “短ID”,前面启动容器时返回的是 “长ID”。短ID是长ID的前12个字符。

NAMES 字段显示容器的名字,在启动容器时可以通过 --name 参数显示地为容器命名,如果不指定,docker 会自动为容器分配名字。

对于容器的后续操作,我们需要通过 “长ID”、“短ID” 或者 “名称”

比如 停止一个容器

docker stop 短ID

通过 while 启动的容器虽然能够保持运行,但实际上没有干什么有意义的事情。容器常见的用途是运行后台服务

docker  run --name "my_http_server" -d httpd

进入容器

有两种方法进入容器:attach 和 exec。

docker attach 7458bd1b334c187e01b4f6fc08a87998c0d91092793cb244ede95bd7d10607ba
#attach 直接进入容器 启动命令 的终端,不会启动新的进程 如果想直接在终端中查看启动命令的输出,用 attach 作用与 tail -f 类似,能够持续打印输出

docker exec

-it 以交互模式打开 pseudo-TTY,执行 bash,其结果就是打开了一个 bash 终端。

进入到容器中,容器的 hostname 就是其 “短ID”。

可以像在普通 Linux 中一样执行命令。ps -elf 显示了容器启动进程while 以及当前的 bash 进程。

执行 exit 退出容器,回到 docker host。

docker exec -it bash|sh 是执行 exec 最常用的方式。

docker exec -it e2be1090f75a bash
docker exec -it e2be1090f75a sh 
exit #exit退出容器

image-20220507004748258

容器启动与重启

容器在 docker host 中实际上是一个进程,

docker stop 命令本质上是向该进程发送一个 SIGTERM 信号。

如果想快速停止容器,可使用 docker kill 命令,其作用是向容器进程发送 SIGKILL 信号。

对于处于停止状态的容器,可以通过 docker start 重新启动。

docker start 会保留容器的第一次启动时的所有参数。

docker restart 可以重启容器,其作用就是依次执行 docker stop 和docker start。

提供服务类的容器 会出现某种错误停止运行 通常我们需要他自动重启恢复提供服务

可以通过--restart 就可以达到这个效果。

docker run –d --restart=allways httpd

--restart=always 意味着无论容器因何种原因退出(包括正常退出),就立即重启。该参数的形式还可以是 --restart=on-failure:3,意思是如果启动进程退出代码非0,则重启容器,最多重启3次。

对容器的文件系统打个快照,或者 dcoker host 需要使用 CPU,这时可以执行 docker pause。暂停容器
处于暂停状态的容器不会占用 CPU 资源,直到通过 docker unpause 恢复运行。

docker pause httpd  #暂停httpd容器提供服务
docker unpause httpd  #恢复httpd容器提供服务

删除容器 退出的容器会占用系统资源 通过 docker rm 删除 docker rm 一次可以指定多个容器

docker rm -v $(docker ps -aq -f status=exited)  #批量删除已经退出的容器

docker rm 是删除容器,而 docker rmi 是删除镜像

docker create --name  myhttpd1 httpd #创建的容器处于 Created 状态
docker start myhttpd1 # 将以后台方式启动容器。 
docker run 命令实际上是 docker create 和 docker start 的组合。
只有当容器的启动进程 退出 时,--restart 才生效

image-20220507010045000

dockers资源限制

内存

容器可使用的内存包括两部分:物理内存和 swap。 Docker 通过下面两组参数来控制容器内存的使用量。

-m 或 --memory:设置内存的使用限额,例如 100M, 2G。

--memory-swap:设置 内存+swap 的使用限额。

docker run -m 200M --memory-swap=300M ubuntu

允许该容器最多使用 200M 的内存和 300M 的 swap 默认情况下容器内存和 swap 的使用没有限制

使用 progrium/stress 镜像来学习如何为容器分配内存。该镜像可用于对容器执行压力测试

docker run -it -m 200M --memory-swap=300M progrium/stress --vm 1 --vm-bytes 280M
--vm 1:启动 1 个内存工作线程。
--vm-bytes 280M:每个线程分配 280M 内存。

image-20220507011040719

因为 280M 在可分配的范围(300M)内,所以工作线程能够正常工作,其过程是:

分配 280M 内存。

释放 280M 内存。

再分配 280M 内存。

再释放 280M 内存。

一直循环......

如果让工作线程分配的内存超过 300M

分配的内存超过限额,stress 线程报错,容器退出

image-20220507011410538

如果在启动容器时只指定 -m 而不指定 --memory-swap,那么 --memory-swap 默认为 -m 的两倍,比如:

docker run -it -m 200M ubuntu

容器最多使用 200M 物理内存和 200M swap。

cpu

默认设置下,所有容器可以平等地使用 host CPU 资源并且没有限制。

Docker 可以通过 -c 或 --cpu-shares 设置容器使用 CPU 的权重。如果不指定,默认值为 1024。

与内存限额不同,通过 -c 设置的 cpu share 并不是 CPU 资源的绝对数量,而是一个相对的权重值。某个容器最终能分配到的 CPU 资源取决于它的 cpu share 占所有容器 cpu share 总和的比例。

通过 cpu share 可以设置容器使用 CPU 的优先级

docker run --name "container_A" -c 1024 ubuntu
docker run --name "container_B" -c 512 ubuntu

当两个容器都需要 CPU 资源时,container_A 可以得到的 CPU 是 container_B 的两倍。

这种按权重分配 CPU 只会发生在 CPU 资源紧张的情况下。如果 container_A 处于空闲状态,这时,为了充分利用 CPU 资源,container_B 也可以分配到全部可用的 CPU

--cpu 用来设置工作线程的数量。因为当前 host 只有 1 颗 CPU,所以一个工作线程就能将 CPU 压满。如果 host 有多颗 CPU,则需要相应增加 --cpu 的数量。

docker run --name "container_s" -c 512 -it progrium/stress --cpu 1
docker run --name "container_z" -c 1024 -it progrium/stress --cpu 1

image-20220507012730589

image-20220507012750801

image-20220507012703448

container_z消耗的 CPU 是 container_s 的两倍。

暂停container_z

image-20220507012912764

top 显示 container_z 在 container_s空闲的情况下能够用满整颗 CPU

image-20220507013001685

block I/O限制

Block IO 指的是磁盘的读写,docker 可通过设置权重、限制 bps 和 iops 的方式控制容器读写磁盘的带宽

目前 Block IO 限额只对 direct IO(不使用文件缓存)有效

block IO 权重

默认情况下,所有容器能平等地读写磁盘,可以通过设置 --blkio-weight 参数来改变容器 block IO 的优先级。

--blkio-weight 与 --cpu-shares 类似,设置的是相对权重值,默认为 500。

在下面的例子中,container_A 读写磁盘的带宽是 container_B 的两倍。

docker run -it --name container_A --blkio-weight 600 ubuntu  
docker run -it --name container_B --blkio-weight 300 ubuntu

image-20220507013657818

image-20220507013717640

限制 bps 和 iops

bps 是 byte per second,每秒读写的数据量。
iops 是 io per second,每秒 IO 的次数

可通过以下参数控制容器的 bps 和 iops:

--device-read-bps,限制某个设备的 bps。

--device-write-bps,限制某个设备的 bps。

--device-read-iops,限制读某个设备的 iops。

--device-write-iops,限制写某个设备的 iops。

下面这个例子限制容器写 /dev/sda 的速率为 30 MB/s

docker run -it --device-write-bps /dev/sda:30MB ubuntu

image-20220507014512588

容器的底层实现技术

cgroup 和 namespace 是最重要的两种技术。cgroup 实现资源限额, namespace 实现资源隔离。

cgroup

cgroup 全称 Control Group。Linux 操作系统通过 cgroup 可以设置进程使用 CPU、内存 和 IO 资源的限额。前面我们看到的--cpu-shares、-m、--device-write-bps 实际上就是在配置 cgroup。

cgroup 到底长什么样子呢?我们可以在 /sys/fs/cgroup 中找到它

启动一个容器,设置 --cpu-shares=512:

image-20220507015110478

image-20220507015205288

在 /sys/fs/cgroup/cpu/docker 目录中,Linux 会为每个容器创建一个 cgroup 目录,以容器长ID 命名

目录中包含所有与 cpu 相关的 cgroup 配置,文件 cpu.shares 保存的就是 --cpu-shares 的配置,值为 512。

同样的,/sys/fs/cgroup/memory/docker 和 /sys/fs/cgroup/blkio/docker 中保存的是内存以及 Block IO 的 cgroup 配置。

namespace

在每个容器中,我们都可以看到文件系统,网卡等资源,这些资源看上去是容器自己的。拿网卡来说,每个容器都会认为自己有一块独立的网卡,即使 host 上只有一块物理网卡。这种方式非常好,它使得容器更像一个独立的计算机。

Linux 实现这种方式的技术是 namespace。namespace 管理着 host 中全局唯一的资源,并可以让每个容器都觉得只有自己在使用它。换句话说,namespace 实现了容器间资源的隔离。

Linux 使用了六种 namespace,分别对应六种资源:Mount、UTS、IPC、PID、Network 和 User

Mount namespace

Mount namespace 让容器看上去拥有整个文件系统。

容器有自己的 / 目录,可以执行 mount 和 umount 命令。当然我们知道这些操作只在当前容器中生效,不会影响到 host 和其他容器。

UTS namespace

简单的说,UTS namespace 让容器有自己的 hostname。 默认情况下,容器的 hostname 是它的短ID,可以通过 -h 或 --hostname 参数设置。

image-20220507015501831

IPC namespace

IPC namespace 让容器拥有自己的共享内存和信号量(semaphore)来实现进程间通信,而不会与 host 和其他容器的 IPC 混在一起。

PID namespace

我们前面提到过,容器在 host 中以进程的形式运行。例如查看当前 host 中运行的容器

image-20220507015745127

4个容器在运行

通过 ps axf 可以查看容器进程

image-20220507020141343

所有容器的进程都挂在 dockerd 进程下,同时也可以看到容器自己的子进程。 如果我们进入到某个容器,ps 就只能看到自己的进程了:

image-20220507020344986

进程的 PID 不同于 host 中对应进程的 PID,容器中 PID=1 的进程当然也不是 host 的 init 进程。也就是说:容器拥有自己独立的一套 PID,这就是 PID namespace 提供的功能

Network namespace

Network namespace 让容器拥有自己独立的网卡、IP、路由等资源。

User namespace

User namespace 让容器能够管理自己的用户,host 不能看到容器中创建的用户。

image-20220507020540191

image-20220507020557312

posted @ 2022-07-06 16:49  机猿巧合  阅读(162)  评论(0编辑  收藏  举报