Docker技术入门与实战 第二版-学习笔记-5-容器-命令及限制内存与cpu资源

1.启动容器

启动容器有两种方式:

  • 基于镜像新建一个容器并启动
  • 将在终止状态(stopped)的容器重新启动

1)新建并启动——docker run

比如在启动ubuntu:14.04容器,并输出“Hello World”,之后终止容器:

userdeMBP:~ user$ docker run ubuntu:14.04 /bin/echo 'Hello world'
Hello world

 如果要启动一个bash终端,并且允许用户进行交互:

userdeMacBook-Pro:~ user$ docker run -t -i ubuntu:14.04 /bin/bash
root@db3bc701340a:/# 

 -t 选项让Docker分配一个伪终端(pseudo-tty)并绑定到容器的标准输入上

-i 则让容器的标准输入保持打开

 然后在交互模式上就能够通过所创建的终端对ubuntu系统进行操作,如:

root@db3bc701340a:/# pwd
/
root@db3bc701340a:/# ls
bin  boot  dev  etc  home  lib  lib64  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var
root@db3bc701340a:/# 

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

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

2) 启动已终止容器——docker start

容器的核心为所执行的应用程序,所需要的资源都是应用程序运行所必需的。

除此之外,并没有其它的资源。可以在伪终端中利用 ps或 top来查看进程信息。

root@db3bc701340a:/# ps
  PID TTY          TIME CMD
    1 pts/0    00:00:00 bash
   18 pts/0    00:00:00 ps
root@db3bc701340a:/# top

top - 03:22:22 up 8 min,  0 users,  load average: 0.36, 0.29, 0.15
Tasks:   2 total,   1 running,   1 sleeping,   0 stopped,   0 zombie
%Cpu(s):  1.7 us,  1.8 sy,  0.0 ni, 96.2 id,  0.2 wa,  0.0 hi,  0.1 si,  0.0 st
KiB Mem:   2046748 total,  1942204 used,   104544 free,    23888 buffers
KiB Swap:  1048572 total,     1844 used,  1046728 free.   984796 cached Mem

  PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND                                                                                                                                     
    1 root      20   0   18188   3248   2764 S   0.0  0.2   0:00.04 bash                                                                                                                                        
   19 root      20   0   19868   2392   2068 R   0.0  0.1   0:00.00 top  

可见,容器中仅运行了指定的 bash 应用。这种特点使得 Docker 对资源的利用率 极高,是货真价实的轻量级虚拟化。

2.后台(background)运行

很多时候,需要让 Docker在后台运行而不是直接把执行命令的结果输出在当前宿主机下。

此时,可以通过添加 -d 参数来实现。

  -d, --detach     Run container in background and print container ID 即在后台运行容器,并打印出容器ID

比如,当你不使用-d 参数时:

userdeMacBook-Pro:~ user$ docker run ubuntu:14.04 /bin/sh -c "while true; do echo hello world; sleep 1; done"
hello world
hello world
hello world
hello world
hello world
.....

容器会把输出的结果(STDOUT)打印到宿主机上面

但是如果使用 -d 参数:

userdeMacBook-Pro:~ user$ docker run -d ubuntu:14.04 /bin/sh -c "while true; do echo hello world; sleep 1; done"
3c091389b4ffb583b4cca578b751e0a4b4e1d556852fe58a5f3d50003cb95737

此时容器会在后台运行并不会把输出的结果(STDOUT)打印到宿主机上面

输出结果可以用docker logs 查看:

userdeMacBook-Pro:~ user$ docker logs 3c091389b4ff
hello world
hello world
hello world
hello world
hello world
hello world
hello world
....

注: 容器是否会长久运行,是和docker run指定的命令有关,和 -d 参数无关。

使用docker ps查看容器信息:

userdeMacBook-Pro:~ user$ docker ps
CONTAINER ID        IMAGE                COMMAND                  CREATED              STATUS              PORTS                     NAMES
3c091389b4ff        ubuntu:14.04         "/bin/sh -c 'while t…"   About a minute ago   Up 59 seconds                                 gallant_franklin

3.终止容器 ——docker stop

终止状态的容器可以使用docker ps -a命令查看:

userdeMacBook-Pro:~ user$ docker ps -a
CONTAINER ID        IMAGE                COMMAND                  CREATED             STATUS                        PORTS                     NAMES
3c091389b4ff        ubuntu:14.04         "/bin/sh -c 'while t…"   5 minutes ago       Up 5 minutes                                            gallant_franklin

对于这些终止了的容器,可以通过docker start来重启;

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

4.进入容器

某些时候需要进入容器进行操作,有很多种方法,包括使用 docker attach命令或 nsenter工具等

1) attach 命令

使用方法:

  • 首先以后台方式打开一个容器
  • 然后使用docker attach +容器名 来进入该容器
userdeMacBook-Pro:~ user$ docker run -idt ubuntu:14.04
3adcf64dd30067011f15ef1c8b341f505f9900f382252da27d963c81aee4ba10

userdeMacBook-Pro:~ user$ docker ps
CONTAINER ID        IMAGE                COMMAND                  CREATED             STATUS              PORTS                     NAMES
3adcf64dd300        ubuntu:14.04         "/bin/bash"              16 seconds ago      Up 15 seconds                                 elastic_easley
3c091389b4ff        ubuntu:14.04         "/bin/sh -c 'while t…"   23 minutes ago      Up 23 minutes                                 gallant_franklin

userdeMacBook-Pro:~ user$ docker attach elastic_easley
root@3adcf64dd300:/# 

但是使用 attach 命令有时候并不方便。

当多个窗口同时 attach 到同一个容器的时候,所有窗口都会同步显示。

当某个窗口因命令阻塞时,其他窗口也无法执行操作了。

2) nsenter 命令

工具在 util-linux 2.23版本后包含,如果没有,本地的安装方法为:

$ cd /tmp; curl https://www.kernel.org/pub/linux/utils/util-linux/v2.24/util-linux-2.24.tar.gz | tar -zxf-; cd util-linux-2.24;
$ ./configure --without-ncurses
$ make nsenter && sudo cp nsenter /usr/local/bin

使用方法:

nsenter启动一个新的shell进程(默认是/bin/bash), 同时会把这个新进程切换到和目标(target)进程相同的命名空间,这样就相当于进入了容器内部。

nsenter 要正常工作需要有 root 权限。 很不幸,Ubuntu 14.04 仍然使用的是 util-linux 2.20。安装最新版本的 util-linux(2.29)版,请按照以下步骤:

$ wget https://www.kernel.org/pub/linux/utils/util-linux/v2.29/util-linux-2.29.tar.xz; tar xJvf util-linux-2.29.tar.xz
$ cd util-linux-2.29
$ ./configure --without-ncurses && make nsenter
$ sudo cp nsenter /usr/local/bin

运行时有错:

userdeMacBook-Pro:util-linux-2.29 user$ ./configure --without-ncurses && make nsenter
...省略
    warnings:

 -fno-common -Wall -Werror=sequence-point -Wextra -Wextra-semi -Wembedded-directive -Wmissing-declarations -Wmissing-prototypes -Wno-missing-field-initializers -Wredundant-decls -Wsign-compare -Wtype-limits -Wuninitialized -Wunused-parameter -Wunused-result -Wunused-variable -Wnested-externs -Wpointer-arith -Wstrict-prototypes -Wformat-security -Wimplicit-function-declaration

    Type 'make' or 'make <utilname>' to compile.
   CCLD     nsenter
clang: error: no input files
make: *** [nsenter] Error 1

说是找不到nsenter文件

后面分开运行:

$./configure --without-ncurses
$make

又有问题:

login-utils/login.c:61:10: fatal error: 'sys/sendfile.h' file not found
#include <sys/sendfile.h>

查找资料后在发现上面的安装方法是linux系统的,sys/sendfile.h是Linux自带的文件,而我使用的是mac系统,所以打算使用别的方法来使用nsenter

后面发现其实不是这个问题,不应该运行mask,就是要运行make nsenter

后面就换了一个版本看看,下载2.24版本,也没有成功

后面使用github的jpetazzo/nsenter也没能成功

安装nsenter到/usr/local/bin:

userdeMacBook-Pro:~ user$ docker run --rm -v /usr/local/bin:/target jpetazzo/nsenter
Unable to find image 'jpetazzo/nsenter:latest' locally
latest: Pulling from jpetazzo/nsenter
5c90d4a2d1a8: Pull complete 
c6c4c486dd77: Pull complete 
0ed6ac9f06ed: Pull complete 
404416bec766: Pull complete 
4bf954ba4ae2: Pull complete 
23f698ff1fd0: Pull complete 
b39fba43fbdb: Pull complete 
7889943d47f6: Pull complete 
446df4bc8efe: Pull complete 
6074415f722e: Pull complete 
72024cea4c47: Pull complete 
6c4b4f4219d3: Pull complete 
93da7ec1688f: Pull complete 
0c4337c5a938: Pull complete 
Digest: sha256:a30e7da907a9abb715027677c21468005beee06251b7737c86f84fa148d572b0
Status: Downloaded newer image for jpetazzo/nsenter:latest
Installing nsenter to /target
Installing docker-enter to /target
Installing importenv to /target

  -v, --volume list    Bind mount a volume 绑定装入卷

jpetazzo/nsenter容器将检测到/target是一个挂载点,并将nsenter二进制文件复制到其中

运行时有错误:/usr/local/bin/nsenter: /usr/local/bin/nsenter: cannot execute binary file

如果有小伙伴有解决的办法,希望能告知

为了连接到容器,你还需要找到容器的第一个进程的 PID,可以通过下面的命令获取:

 PID=$(docker inspect --format "{{ .State.Pid }}" <container>)

通过这个 PID,就可以连接到这个容器:

$ nsenter --target $PID --mount --uts --ipc --net --pid

3)exec命令

userdeMBP:~ user$ docker run -idt ubuntu:14.04
2084e92eea8c494023db35709d3d13054359ba71bca533371d04463652272a7b
userdeMBP:~ user$ docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                    NAMES
2084e92eea8c        ubuntu:14.04        "/bin/bash"              4 seconds ago       Up 3 seconds                                 boring_jackson
b4a512f0230f        registry            "/entrypoint.sh /etc…"   2 days ago          Up About an hour    0.0.0.0:5000->5000/tcp   registry
userdeMBP:~ user$ docker exec -it 2084e92eea8c /bin/bash
root@2084e92eea8c:/# 

还可以使用其来打印容器中的ip:

userdeMBP:~ user$ docker exec -it 2084e92eea8c ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
2: tunl0@NONE: <NOARP> mtu 1480 qdisc noop state DOWN group default qlen 1
    link/ipip 0.0.0.0 brd 0.0.0.0
3: ip6tnl0@NONE: <NOARP> mtu 1452 qdisc noop state DOWN group default qlen 1
    link/tunnel6 :: brd ::
10: eth0@if11: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default 
    link/ether 02:42:ac:11:00:03 brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.3/16 brd 172.17.255.255 scope global eth0
       valid_lft forever preferred_lft forever

5.导出和导入容器

1) 导出容器——docker export

userdeMacBook-Pro:~ user$ docker ps -a
CONTAINER ID        IMAGE                COMMAND                  CREATED             STATUS                      PORTS                     NAMES
dd8f619aef03        jpetazzo/nsenter     "bash"                   41 minutes ago      Exited (0) 41 minutes ago                             jovial_burnell
3adcf64dd300        ubuntu:14.04         "/bin/bash"              2 hours ago         Exited (127) 2 hours ago                              elastic_easley
3c091389b4ff        ubuntu:14.04         "/bin/sh -c 'while t…"   3 hours ago         Up 3 hours                                            gallant_franklin
f7efcb534bb0        ubuntu:14.04         "/bin/sh -c 'while t…"   3 hours ago         Exited (0) 3 hours ago                                fervent_ritchie
db3bc701340a        ubuntu:14.04         "/bin/bash"              3 hours ago         Exited (0) 3 hours ago                                focused_sanderson
9dc39aa922f4        ubuntu:14.04         "/bin/echo 'Hello wo…"   15 hours ago        Exited (0) 15 hours ago                               nostalgic_burnell
83fbcec3feda        568c4670fa80         "/bin/sh -c 'apt-get…"   21 hours ago        Exited (100) 21 hours ago                             ecstatic_ritchie
889e5311532c        nginx:v2             "nginx -g 'daemon of…"   27 hours ago        Exited (255) 3 hours ago    0.0.0.0:81->80/tcp        web2

然后导出上面的nginx:v2:

userdeMacBook-Pro:~ user$ docker export 889e5311532c > nginx.tar

然后就会在本地的~目录下生成nginx.tar文件

2)导入容器快照——docker import

userdeMacBook-Pro:~ user$ cat nginx.tar | docker import - test/nginx:v1
sha256:02548ab0445a4490b1801762d2d8c342a8ec4a38f1686165c4e6eb4f10fc87ad
userdeMacBook-Pro:~ user$ docker images
REPOSITORY                                 TAG                 IMAGE ID            CREATED             SIZE
test/nginx                                 v1                  02548ab0445a        6 seconds ago       107MB

也可以通过指定 URL 或者某个目录来导入,如:

docker import http://example.com/exampleimage.tgz example/
imagerepo

⚠️用户既可以使用 docker load来导入镜像存储文件到本地镜像库,也可以使用 docker import来导入一个容器快照到本地镜像库。

这两者的区别在于容器快照文件(docker import)将丢弃所有的历史记录和元数据信息(即仅保存容器当时的快照状态),而镜像存储文件(docker load)将保存完整记录,体积也要大。

此外,从容器快照文件导入时可以重新指定标签等元数据信息。

6) 删除容器——docker rm

删除一个处于终止状态(先docker stop)的容器

如果要删除一个运行中的容器,可以添加 -f参数。Docker 会发送 SIGKILL信号给容器。

清理所有处于终止状态的容器

先使用docker ps -a查看所有查看所有已经创建的包括终止状态的容器

一个个删除会很麻烦,可以使用下面的方法:

userdeMacBook-Pro:~ user$ docker rm $(docker ps -a -q)
dd8f619aef03
3adcf64dd300
f7efcb534bb0
db3bc701340a
9dc39aa922f4
83fbcec3feda
889e5311532c
f97514a2ac93
45104c30f94e
67cf9831aa1b
eb38c07055c8
40d9af4487ce
f44af23ba62c
b6ea66b4a0d8
b9606b2017da
6b94d698640d
60b85b6d1d40
a9905c000180
90dfc4e533af
Error response from daemon: You cannot remove a running container 3c091389b4ffb583b4cca578b751e0a4b4e1d556852fe58a5f3d50003cb95737. Stop the container before attempting removal or force remove
Error response from daemon: You cannot remove a running container f6a9a111a55d08f44da9b0a41755618e23baf3298f77b64f1c7375667721648b. Stop the container before attempting removal or force remove
userdeMacBook-Pro:~ user$ docker ps -a
CONTAINER ID        IMAGE                COMMAND                  CREATED             STATUS              PORTS                     NAMES
3c091389b4ff        ubuntu:14.04         "/bin/sh -c 'while t…"   3 hours ago         Up 3 hours                                    gallant_franklin
f6a9a111a55d        kumavis/zeroclient   "/bin/sh -c 'npm sta…"   4 weeks ago         Up 3 hours          0.0.0.0:32768->9000/tcp   zero-client_zeroClient_1
userdeMacBook-Pro:~ user$ docker stop 3c091389b4ff
3c091389b4ff
userdeMacBook-Pro:~ user$ docker stop f6a9a111a55d
f6a9a111a55d
userdeMacBook-Pro:~ user$ docker rm $(docker ps -a -q)
3c091389b4ff
f6a9a111a55d

上面可见其实docker rm默认是不会清除正在运行中的容器的,你需要先docker stop他们,再删除即可

此时再查看就发现容器已经清空了

userdeMacBook-Pro:~ user$ docker ps 
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES

7)限制资源:

1》内存

因为虚拟机容量有限,容器的内存如果总是增加,那么就可能会出现容器报错的情况,所以要对容器利用的资源进行限制,避免某个容器因占用太多资源而影响其他容器乃至整个 host 的性能

运行docker run命令时使用以下的参数进行限制即可:

 -m, --memory bytes         Memory limit,限制内存,如--memory=200M
--memory-swap 设置 内存+swap 的使用限额

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

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

参考https://www.cnblogs.com/CloudMan6/p/6986499.html

userdeMacBook-Pro:~ user$ docker run -it -m 200M --memory-swap=300M progrium/stress --vm 1 --vm-bytes 280M
Unable to find image 'progrium/stress:latest' locally
latest: Pulling from progrium/stress
a3ed95caeb02: Pull complete 
871c32dbbb53: Pull complete 
dbe7819a64dd: Pull complete 
d14088925c6e: Pull complete 
58026d51efe4: Pull complete 
7d04a4fe1405: Pull complete 
1775fca35fb6: Pull complete 
5c319e267908: Pull complete 
Digest: sha256:e34d56d60f5caae79333cee395aae93b74791d50e3841986420d23c2ee4697bf
Status: Downloaded newer image for progrium/stress:latest
stress: info: [1] dispatching hogs: 0 cpu, 0 io, 1 vm, 0 hdd
stress: dbug: [1] using backoff sleep of 3000us
stress: dbug: [1] --> hogvm worker 1 [8] forked
stress: dbug: [8] allocating 293601280 bytes ...
stress: dbug: [8] touching bytes in strides of 4096 bytes ...
stress: dbug: [8] freed 293601280 bytes
stress: dbug: [8] allocating 293601280 bytes ...
stress: dbug: [8] touching bytes in strides of 4096 bytes ...
--vm 1:启动 1 个内存工作线程。
--vm-bytes 280M:每个线程分配 280M 内存

因为设置给每个线程分配的280M内存小于设置的300M内存,所以成功运行

但是如果每个线程分配的内存大于300M的话,就会出错,stress 线程报错,容器退出:

userdeMacBook-Pro:~ user$ docker run -it -m 200M --memory-swap=300M progrium/stress --vm 1 --vm-bytes 310M
stress: info: [1] dispatching hogs: 0 cpu, 0 io, 1 vm, 0 hdd
stress: dbug: [1] using backoff sleep of 3000us
stress: dbug: [1] --> hogvm worker 1 [8] forked
stress: dbug: [8] allocating 325058560 bytes ...
stress: dbug: [8] touching bytes in strides of 4096 bytes ...
stress: FAIL: [1] (416) <-- worker 8 got signal 9
stress: WARN: [1] (418) now reaping child worker processes
stress: FAIL: [1] (422) kill error: No such process
stress: FAIL: [1] (452) failed run completed in 0s
userdeMacBook-Pro:~ user$ 

 2》cpu

运行docker run命令时使用以下的参数进行限制即可:

 -c, --cpu-shares int       CPU shares (relative weight)CPU份额(相对权重),如--cpu-shares=10;默认为1024

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

 还是使用progrium/stress镜像:

userdeMacBook-Pro:~ user$ docker run --name test1 -it --cpuset-cpus="0" -c 1024 progrium/stress --cpu 1
stress: info: [1] dispatching hogs: 1 cpu, 0 io, 0 vm, 0 hdd
stress: dbug: [1] using backoff sleep of 3000us
stress: dbug: [1] --> hogcpu worker 1 [6] forked
userdeMacBook-Pro:~ user$ docker run --name test2 -it --cpuset-cpus="0" -c 1024 progrium/stress --cpu 1
stress: info: [1] dispatching hogs: 1 cpu, 0 io, 0 vm, 0 hdd
stress: dbug: [1] using backoff sleep of 3000us
stress: dbug: [1] --> hogcpu worker 1 [6] forked

两个都设置为1024,通过--cpuset-cpus="0"参数设置它们使用的是同一个CPU。因为当 CPU 资源充足时,设置 CPU 的权重是没有意义的。只有在容器争用 CPU 资源的情况下, CPU 的权重才能让不同的容器分到不同的 CPU 用量

然后调用docker stats命令来查看它们的CPU占比情况:

可见基本上是1:1的情况

原文地址:https://www.cnblogs.com/wanghui-garcia/p/10122158.html