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
pts/ :: bash
pts/ :: ps
root@db3bc701340a:/# top top - :: up min, users, load average: 0.36, 0.29, 0.15
Tasks: total, running, sleeping, stopped, 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: total, used, free, buffers
KiB Swap: total, used, free. cached Mem PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
root S 0.0 0.2 :00.04 bash
root R 0.0 0.1 :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 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…" minutes ago Up 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" seconds ago Up seconds elastic_easley
3c091389b4ff ubuntu:14.04 "/bin/sh -c 'while t…" minutes ago Up 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
说是找不到nsenter文件
后面分开运行:
$./configure --without-ncurses
$make
又有问题:
login-utils/login.c::: 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" seconds ago Up seconds boring_jackson
b4a512f0230f registry "/entrypoint.sh /etc…" days ago Up About an hour 0.0.0.0:->/tcp registry
userdeMBP:~ user$ docker exec -it 2084e92eea8c /bin/bash
root@2084e92eea8c:/#
还可以使用其来打印容器中的ip:
userdeMBP:~ user$ docker exec -it 2084e92eea8c ip a
: lo: <LOOPBACK,UP,LOWER_UP> mtu qdisc noqueue state UNKNOWN group default qlen
link/loopback ::::: brd :::::
inet 127.0.0.1/ scope host lo
valid_lft forever preferred_lft forever
: tunl0@NONE: <NOARP> mtu qdisc noop state DOWN group default qlen
link/ipip 0.0.0.0 brd 0.0.0.0
: ip6tnl0@NONE: <NOARP> mtu qdisc noop state DOWN group default qlen
link/tunnel6 :: brd ::
: eth0@if11: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu qdisc noqueue state UP group default
link/ether ::ac::: brd ff:ff:ff:ff:ff:ff
inet 172.17.0.3/ 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" minutes ago Exited () minutes ago jovial_burnell
3adcf64dd300 ubuntu:14.04 "/bin/bash" hours ago Exited () hours ago elastic_easley
3c091389b4ff ubuntu:14.04 "/bin/sh -c 'while t…" hours ago Up hours gallant_franklin
f7efcb534bb0 ubuntu:14.04 "/bin/sh -c 'while t…" hours ago Exited () hours ago fervent_ritchie
db3bc701340a ubuntu:14.04 "/bin/bash" hours ago Exited () hours ago focused_sanderson
9dc39aa922f4 ubuntu:14.04 "/bin/echo 'Hello wo…" hours ago Exited () hours ago nostalgic_burnell
83fbcec3feda 568c4670fa80 "/bin/sh -c 'apt-get…" hours ago Exited () hours ago ecstatic_ritchie
889e5311532c nginx:v2 "nginx -g 'daemon of…" hours ago Exited () hours ago 0.0.0.0:->/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 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…" hours ago Up hours gallant_franklin
f6a9a111a55d kumavis/zeroclient "/bin/sh -c 'npm sta…" weeks ago Up hours 0.0.0.0:->/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 --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: [] dispatching hogs: cpu, io, vm, hdd
stress: dbug: [] using backoff sleep of 3000us
stress: dbug: [] --> hogvm worker [] forked
stress: dbug: [] allocating bytes ...
stress: dbug: [] touching bytes in strides of bytes ...
stress: dbug: [] freed bytes
stress: dbug: [] allocating bytes ...
stress: dbug: [] touching bytes in strides of bytes ...
--vm :启动 个内存工作线程。
--vm-bytes 280M:每个线程分配 280M 内存
因为设置给每个线程分配的280M内存小于设置的300M内存,所以成功运行
但是如果每个线程分配的内存大于300M的话,就会出错,stress 线程报错,容器退出:
userdeMacBook-Pro:~ user$ docker run -it -m 200M --memory-swap=300M progrium/stress --vm --vm-bytes 310M
stress: info: [] dispatching hogs: cpu, io, vm, hdd
stress: dbug: [] using backoff sleep of 3000us
stress: dbug: [] --> hogvm worker [] forked
stress: dbug: [] allocating bytes ...
stress: dbug: [] touching bytes in strides of bytes ...
stress: FAIL: [] () <-- worker got signal
stress: WARN: [] () now reaping child worker processes
stress: FAIL: [] () kill error: No such process
stress: FAIL: [] () failed run completed in 0s
userdeMacBook-Pro:~ user$
2》cpu
运行docker run命令时使用以下的参数进行限制即可:
-c, --cpu-shares int CPU shares (relative weight)CPU份额(相对权重),如--cpu-shares=;默认为1024
⚠️通过 cpu share 可以设置容器使用 CPU 的优先级
还是使用progrium/stress镜像:
userdeMacBook-Pro:~ user$ docker run --name test1 -it --cpuset-cpus="" -c progrium/stress --cpu
stress: info: [] dispatching hogs: cpu, io, vm, hdd
stress: dbug: [] using backoff sleep of 3000us
stress: dbug: [] --> hogcpu worker [] forked
userdeMacBook-Pro:~ user$ docker run --name test2 -it --cpuset-cpus="" -c progrium/stress --cpu
stress: info: [] dispatching hogs: cpu, io, vm, hdd
stress: dbug: [] using backoff sleep of 3000us
stress: dbug: [] --> hogcpu worker [] forked
两个都设置为1024,通过--cpuset-cpus="0"参数设置它们使用的是同一个CPU。因为当 CPU 资源充足时,设置 CPU 的权重是没有意义的。只有在容器争用 CPU 资源的情况下, CPU 的权重才能让不同的容器分到不同的 CPU 用量
然后调用docker stats命令来查看它们的CPU占比情况:
可见基本上是1:1的情况