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的情况