Docker笔记(三) 容器

本笔记是记录一些学习微信公众号CloudMan的Docker的文章。本篇介绍容器的基本操作。

1. 运行容器

docker run 使用 –name 可以指定所运行容器的名称

docker run --name "my_httpd" -d httpd

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

docker attach

docker run -d ubuntu /bin/bash -c "while true ; do sleep 1; echo hello; done"
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

docker attach xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
hello
hello
hello

使用Ctrl+p再Ctrl+q,可以退出attach
注:笔者试验了,似乎并不能退出

docker exec

docker exec -it xxxxxxxxx bash 

docker attach V.S. docker exec

attach 与 exec 主要区别如下:

  • attach 直接进入容器 启动命令 的终端,不会启动新的进程。
  • exec 则是在容器中打开新的终端,并且可以启动新的进程。
  • 如果想直接在终端中查看启动命令的输出,用 attach;其他情况使用 exec

当然,如果只是为了查看启动命令的输出,可以使用 docker logs 命令:

docker logs -f <container>

运行容器的最佳实践

按用途容器大致可分为两类:服务类容器和工具类的容器。

  • 服务类容器以 daemon 的形式运行,对外提供服务。比如 web server,数据库等。通过 -d 以后台方式启动这类容器是非常合适的。
    如果要排查问题,可以通过 exec -it 进入容器。
  • 工具类容器通常给能我们提供一个临时的工作环境,通常以 run -it 方式运行。

2. 容器的常用操作

start/stop/restart 容器

容器在 docker host 中实际上是一个进程,docker stop 命令本质上是向该进程发送一个 SIGTERM 信号。
如果想快速停止容器,可使用 docker kill 命令,其作用是向容器进程发送 SIGKILL 信号。

docker start 会保留容器的第一次启动时的所有参数。
docker restart 可以重启容器,其作用就是依次执行 docker stop 和docker start。

容器可能会因某种错误而停止运行。对于服务类容器,通常希望在这种情况下容器能够自动重启。
启动容器时设置 –restart=always 就可以达到这个效果。
–restart=always 意味着无论容器因何种原因退出(包括正常退出),就立即重启。
–restart=on-failure:3,意思是如果启动进程退出代码非0,则重启容器,最多重启3次。

pause/unpause 容器

处于暂停状态的容器不会占用 CPU 资源,直到通过 docker unpause 恢复运行。

rm 删除容器

使用 docker 一段时间后,host 上可能会有大量已经退出了的容器。
这些容器依然会占用 host 的文件系统资源,如果确定不会再重启此类容器,可以通过 docker rm 删除。

docker rm 一次可以指定多个容器,如果希望批量删除所有已经退出的容器,可以执行如下命令:

docker rm -v $(docker ps -aq -f status=exited)

另外,docker rmi 是删除镜像。

3. 限制容器的内存使用

与操作系统类似,容器可使用的内存包括两部分:物理内存和 swap。
Docker 通过下面两组参数来控制容器内存的使用量。

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

如果在启动容器时只指定 -m 而不指定 –memory-swap,那么 –memory-swap 默认为 -m 的两倍。
也就是说,只有-m的时候,默认swap是和memory一样大。

当我们执行如下命令:

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

其含义是允许该容器最多使用 200M 的内存和 100M 的 swap。
默认情况下,上面两组参数为 -1,即对容器内存和 swap 的使用没有限制。

又比如例子:

docker run -it -m 200M --memory-swap=300M progrium/stress --vm 1 --vm-bytes 280M

--vm 1:启动 1 个内存工作线程。
--vm-bytes 280M:每个线程分配 280M 内存。

4. 限制容器的CPU使用

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

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

与内存限额不同,通过 -c 设置的 cpu share 并不是 CPU 资源的绝对数量,而是一个相对的权重值。
某个容器最终能分配到的 CPU 资源取决于它的 cpu share 占所有容器 cpu share 总和的比例。
换句话说:通过 cpu share 可以设置容器使用 CPU 的优先级。
比如在 host 中启动了两个容器:

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

container_A 的 cpu share 1024,是 container_B 的两倍。当两个容器都需要 CPU 资源时,container_A 可以得到的 CPU 是 container_B 的两倍。
需要特别注意的是,这种按权重分配 CPU 只会发生在 CPU 资源紧张的情况下。如果 container_A 处于空闲状态,这时,为了充分利用 CPU 资源,container_B 也可以分配到全部可用的 CPU。

5. 限制容器的Block IO

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

限制 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
time dd if=/dev/zero of=test.out bs=1M count=800 oflag=direct 

通过 dd 测试在容器中写磁盘的速度。因为容器的文件系统是在 host /dev/sda 上的,在容器中写文件相当于对 host /dev/sda 进行写操作。
另外,oflag=direct 指定用 direct IO 方式写文件,这样 –device-write-bps 才能生效。

6. 容器的底层技术

容器的底层实现技术,主要包括 cgroup 和 namespace.
cgroup 实现资源限额, namespace 实现资源隔离。

cgroup

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

可以在 /sys/fs/cgroup 中找到cgroup相关的配置。
/sys/fs/cgroup/cpu/docker 目录中,Linux 会为每个容器创建一个 cgroup 目录,以容器长ID 命名。

同样的,/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 参数设置。

docker run -h myhost -it ubuntu

IPC namespace

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

PID namespace

通过 ps axf 可以查看容器进程,所有容器的进程都挂在 dockerd 进程下,同时也可以看到容器自己的子进程。
如果我们进入到某个容器,ps 就只能看到自己的进程了。而且进程的 PID 不同于 host 中对应进程的 PID,容器中 PID=1 的进程当然也不是 host 的 init 进程。
也就是说:容器拥有自己独立的一套 PID,这就是 PID namespace 提供的功能。

Network namespace

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

User namespace

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

7. 小结

下面是容器的常用操作命令:

create      创建容器  
run         运行容器  
pause       暂停容器  
unpause     取消暂停继续运行容器  
stop        发送 SIGTERM 停止容器  
kill        发送 SIGKILL 快速停止容器  
start       启动容器  
restart     重启容器  
attach      attach 到容器启动进程的终端  
exec        在容器中启动新进程,通常使用 "-it" 参数  
logs        显示容器启动进程的控制台输出,用 "-f" 持续打印  
rm          从磁盘中删除容器
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值