目录
参考书目: 黄靖钧. Docker从入门到实战[M]. 机械工业出版社, 2017.
一、容器的概念
Docker容器是镜像的运行实例。容器在镜像已有的文件层添加一层可读可写的文件层,使得容器就像是一个动态的镜像。所以,docker的内部结构必定与镜像结构十分相似。
如图所示,在docker容器中包含docekr镜像层以及在镜像层上建立的只读初始化层以及可读写层。在初始化层存储的大多是容器环境初始化时与容器相关的环境信息(容器主机名、主机host信息和域名服务文件)。可读写层用到了写时复制技术,虽然docker容器在可读写层可以看到数据卷的内容,但仅是挂载点,真实内容在宿主机上。
二、容器的基本操作
命令 | 说明 |
---|---|
attach | 依附到正在运行的容器 |
cp | 从容器里面复制文件或者目录到宿主机文件系统或以STDOUT形式输出 |
create | 创建一个新的容器 |
diff | 检查容器的文件系统改动 |
events | 实时获得Docker服务器端的事件信息 |
exec | 在一个运行的容器里面运行命令 |
export | 导出容器的文件系统到一个归档文件 |
kill | 杀死一个运行中的容器 |
logs | 获取容器的日志 |
pause | 暂停容器内部的所有进程 |
port | 输出容器的端口信息 |
ps | 显示容器列表 |
rename | 重命名一个容器 |
restart | 重启容器 |
rm | 删除一个或者多个容器 |
run | 运行一个新容器 |
start | 运行一个或者多个非运行状态的容器 |
stats | 实时显示容器的资源使用情况 |
stop | 停止正在运行的容器 |
top | 显示容器内正在运行的进程 |
unpause | 恢复容器内部所有进程 |
update | 更新一个或多个容器的配置 |
wait | 阻塞指导容器停止,然后打印他的退出代码 |
三、容器操作实例
3.1 docker create
# 创建一个容器
gupan@ubuntu:~$ sudo docker create -it ubuntu
Unable to find image 'ubuntu:latest' locally
latest: Pulling from library/ubuntu
a48c500ed24e: Pull complete
1e1de00ff7e1: Pull complete
0330ca45a200: Pull complete
471db38bcfbf: Pull complete
0b4aba487617: Pull complete
Digest: sha256:c8c275751219dadad8fa56b3ac41ca6cb22219ff117ca98fe82b42f24e1ba64e
Status: Downloaded newer image for ubuntu:latest
59df0946091d4b0408521404413e5a501977c1153667220ad28774ab290bf17a
gupan@ubuntu:~$ sudo docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
59df0946091d ubuntu "/bin/bash" 2 minutes ago Created laughing_saha
gupan@ubuntu:~$
3.2 docker run
启动容器有两种情况
- 原来没有这个容器,需要基于一个镜像启动新的容器
- 宿主机本来有一个容器,但是这个容器处于非运行状态,可以把这个容器启动起来
使用docker run新建容器并启动,用这个容器来输出一句话
# 使用docker run新建容器并启动,用这个容器来输出一句话
gupan@ubuntu:~$ sudo docker run ubuntu /bin/echo "Hello World"
Hello World
# 查看容器状态
gupan@ubuntu:~$ sudo docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
9a2b868de95b ubuntu "/bin/echo 'Hello ..." 35 seconds ago Exited (0) 33 seconds ago gallant_poitras
59df0946091d ubuntu "/bin/bash" 10 minutes ago Created laughing_saha
gupan@ubuntu:~$
启动容器的内部运行步骤
- 检查本地是否存在这个镜像,如果没有就从仓库下载
- 如果有检查命令是否有参数冲突
- 利用本地镜像创建一个容器
- 挂载可读写层,启动容器的一系列配置(各种资源隔离操作)
- 在应用参数值时,如果入到参数有误,启动会终止
- 如果没有问题则执行应用程序,执行完毕终止容器
案例:
# -i 代表让容器的标准输出始终打开
# -t 让Docker分配一个标准并绑定到容器标准输出上
# -it 代表绑定到容器内部,因此在该终端下执行的动作会在容器内部执行
gupan@ubuntu:~$ sudo docker run -it ubuntu bash
root@374d959286b5:/# ps
PID TTY TIME CMD
1 ? 00:00:00 bash
10 ? 00:00:00 ps
root@374d959286b5:/#
# 退出当前容器
root@374d959286b5:/# exit
exit
gupan@ubuntu:~$
后台运行容器
d参数:代表后台运行
sudo docker run -d nginx:alpine
自动重启容器
又是程序内部错误导致程序退出,从而波及容器进程,导致整个容器退出,所以需要让容器在意外退出时自动重启。docker提供的解决方法是在docker run后加 --restart=always
gupan@ubuntu:~$ sudo docker run -d --restart=always ubuntu /bin/bash
[sudo] password for gupan:
eea5acb7c2432250439780dca3d86d4155470f3cdf7dfd647b88798c83daa51d
gupan@ubuntu:~$ sudo docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
eea5acb7c243 ubuntu "/bin/bash" 40 seconds ago Restarting (0) 10 seconds ago brave_montalcini
ab1ae8a9db39 nginx:alpine "nginx -g 'daemon ..." 10 hours ago Up 10 hours 80/tcp confident_ramanujan
gupan@ubuntu:~$
3.2 docker stop、docker kill
3.2.1 docker stop
正常情况下,docker stop会向容器发送一个SIGTERM信号,此时容器会正常退出,但有时候,容器会因各种原因对SIGTERM信号没有响应,这时候可以在docker stop后带上-t参数,指定超时多长时间后对SIGTERM信号没有响应就像向容器发送SIGKILL信号。该信号会杀死所有正在运行的容器进程
sudo docker stop -t 3
3.2.2 docker kill
使用docker kill命令可以强制停止一个容器
// docker kill命令语法
docker kill <container id>
// 案例
gupan@ubuntu:~$ sudo docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
eea5acb7c243 ubuntu "/bin/bash" 9 minutes ago Restarting (0) 17 seconds ago brave_montalcini
ab1ae8a9db39 nginx:alpine "nginx -g 'daemon ..." 10 hours ago Up 10 hours 80/tcp confident_ramanujan
374d959286b5 ubuntu "bash" 10 hours ago Exited (0) 10 hours ago wonderful_hermann
6ad9de3fc9a9 ubuntu "bash" 10 hours ago Exited (127) 10 hours ago gifted_wozniak
36e9299c5b0b ubuntu "bash" 10 hours ago Exited (0) 10 hours ago wonderful_fermi
9a2b868de95b ubuntu "/bin/echo 'Hello ..." 10 hours ago Exited (0) 10 hours ago gallant_poitras
59df0946091d ubuntu "/bin/bash" 10 hours ago Created laughing_saha
gupan@ubuntu:~$ sudo docker kill eea5acb7c243
eea5acb7c243
gupan@ubuntu:~$ sudo docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
eea5acb7c243 ubuntu "/bin/bash" 14 minutes ago Exited (0) 5 seconds ago brave_montalcini
ab1ae8a9db39 nginx:alpine "nginx -g 'daemon ..." 10 hours ago Up 10 hours 80/tcp confident_ramanujan
374d959286b5 ubuntu "bash" 10 hours ago Exited (0) 10 hours ago wonderful_hermann
6ad9de3fc9a9 ubuntu "bash" 10 hours ago Exited (127) 10 hours ago gifted_wozniak
36e9299c5b0b ubuntu "bash" 10 hours ago Exited (0) 10 hours ago wonderful_fermi
9a2b868de95b ubuntu "/bin/echo 'Hello ..." 10 hours ago Exited (0) 10 hours ago gallant_poitras
59df0946091d ubuntu "/bin/bash" 11 hours ago Created laughing_saha
gupan@ubuntu:~$
停止所有进程:
sudo docker kill $(docker ps -a -q)
删除所有停止的进程:
sudo docker rm $(docker ps -a -q)
3.2.2 docker rm
删除容器
// 基本语法
sudo docker rm <container id>
-f :强制删除容器,即使容器正在运行,不加-f参数,只能删除停止的容器
-l:删除容器和其他容器的关联,但会保留容器
-v:删除容器的同时也删除数据卷,默认情况下容器和数据卷的生命周期是独立的
3.2 docker inspect
查看容器信息
docker inspect的-f参数:
docker inspect -f是按模板来使用的,更多关于模板的知识可以参照以下网址:https://golang.org/pkg/text/template/
// 容器ubuntu中的信息
gupan@ubuntu:~$ sudo docker inspect ubuntu
[
{
"Id": "sha256:452a96d81c30a1e426bc250428263ac9ca3f47c9bf086f876d11cb39cf57aeec",
"RepoTags": [
"ubuntu:latest"
],
"RepoDigests": [
"ubuntu@sha256:c8c275751219dadad8fa56b3ac41ca6cb22219ff117ca98fe82b42f24e1ba64e"
],
"Parent": "",
"Comment": "",
"Created": "2018-04-27T23:28:36.319694807Z",
"Container": "e1a8ac8f61e4bd40dd223471e86b0328609182a3112dd45a435575753bbc7924",
"ContainerConfig": {
"Hostname": "e1a8ac8f61e4",
"Domainname": "",
"User": "",
"AttachStdin": false,
"AttachStdout": false,
"AttachStderr": false,
"Tty": false,
"OpenStdin": false,
"StdinOnce": false,
"Env": [
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
],
"Cmd": [
"/bin/sh",
"-c",
"#(nop) ",
"CMD ["/bin/bash"]"
],
"ArgsEscaped": true,
"Image": "sha256:a43f69020c4ca7feb3cfb5fa5857a24b138efa953f6108975205c1f121c7c9cb",
"Volumes": null,
"WorkingDir": "",
"Entrypoint": null,
"OnBuild": null,
"Labels": {}
},
"DockerVersion": "17.06.2-ce",
"Author": "",
"Config": {
"Hostname": "",
"Domainname": "",
"User": "",
"AttachStdin": false,
"AttachStdout": false,
"AttachStderr": false,
"Tty": false,
"OpenStdin": false,
"StdinOnce": false,
"Env": [
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
],
"Cmd": [
"/bin/bash"
],
"ArgsEscaped": true,
"Image": "sha256:a43f69020c4ca7feb3cfb5fa5857a24b138efa953f6108975205c1f121c7c9cb",
"Volumes": null,
"WorkingDir": "",
"Entrypoint": null,
"OnBuild": null,
"Labels": null
},
"Architecture": "amd64",
"Os": "linux",
"Size": 79620962,
"VirtualSize": 79620962,
"GraphDriver": {
"Data": null,
"Name": "aufs"
},
"RootFS": {
"Type": "layers",
"Layers": [
"sha256:65bdd50ee76a485049a2d3c2e92438ac379348e7b576783669dac6f604f6241b",
"sha256:ec75999a0cb1218bbfedeaf535afb516b739c0a2475f89d6c8bdf6ccfdf73c85",
"sha256:67885e448177114ca1d82161955ba7400139b5acc1e9cca633dfff163ccdb1b6",
"sha256:8db5f072feeccc451f94b357a1f596dd455859807917e7c98ed7264601043cbf",
"sha256:059ad60bcacfe9c03116f467f9e025f3519d1641358e0422bee478445d679313"
]
}
}
]
// 获取容器中的Os的信息
gupan@ubuntu:~$ sudo docker inspect -f "Os is :{{.Os}}" ubuntu
Os is :linux
gupan@ubuntu:~$
// 获取容器中RootFS下Layers的信息
gupan@ubuntu:~$ sudo docker inspect -f "Layers are :{{.RootFS.Layers}}" ubuntu
Layers are :[sha256:65bdd50ee76a485049a2d3c2e92438ac379348e7b576783669dac6f604f6241b sha256:ec75999a0cb1218bbfedeaf535afb516b739c0a2475f89d6c8bdf6ccfdf73c85 sha256:67885e448177114ca1d82161955ba7400139b5acc1e9cca633dfff163ccdb1b6 sha256:8db5f072feeccc451f94b357a1f596dd455859807917e7c98ed7264601043cbf sha256:059ad60bcacfe9c03116f467f9e025f3519d1641358e0422bee478445d679313]
gupan@ubuntu:~$
四、进入容器内部
4.1 docker attach
这个命令应用于实时查看容器日志等操作
// 基本语法
sudo docker attach <container name>
// 案例
gupan@ubuntu:~$ sudo docker run -d --name testdemo ubuntu /usr/bin/top -b
a1cad39488bec333d9ce9a964c18180b3bdf1db25d091768a2bb7b35c7ecce12
gupan@ubuntu:~$
// 在另一个终端依附到testdemo
gupan@ubuntu:~$ sudo docker attach testdemo
top - 02:49:57 up 1 day, 3:24, 0 users, load average: 0.00, 0.00, 0.00
Tasks: 1 total, 1 running, 0 sleeping, 0 stopped, 0 zombie
%Cpu(s): 0.7 us, 0.0 sy, 0.0 ni, 99.0 id, 0.3 wa, 0.0 hi, 0.0 si, 0.0 st
KiB Mem : 998408 total, 207460 free, 392320 used, 398628 buff/cache
KiB Swap: 1046524 total, 1015828 free, 30696 used. 438424 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
1 root 20 0 36484 3048 2700 R 0.0 0.3 0:00.02 top
4.2 docker exec
这个命令可以上ssh一样,直接进入到容器内部进行相关操作
# 基本语法
docker exec [OPTIONS] CONTAINER COMMAND [ARG...]
# docker exec命令行参数
-d:后台运行
-i:交互模式
-t:分配一个tty
-u:指定用户和用户组<nameuid>[:<groupid>]
--privileged:参数会分配一个特权给tty界面,相当于拥有宿主机的root权限,慎用
案例:
在一个终端启动容器
gupan@ubuntu:~$ sudo docker run -it --name test ubuntu bash
root@0071d6bbab54:/#
在另一个终端使用docker exec进入容器
gupan@ubuntu:~$ sudo docker exec -it test bash
root@0071d6bbab54:/#
4.3 docker nsenter
一个第三方工具,可以方便的进入容器,当然,功能不止于此,可以通过shell脚本将使用nsenter进入容器繁琐的操作简化。脚本地址:https://github.com/jpetazzo/nsenter/blob/master/docker-enter,使用方式为: ./docker-enter <container_name_or_ID> ,脚本内容如下:
#!/bin/sh
if [ -e $(dirname "$0")/nsenter ]; then
# with boot2docker, nsenter is not in the PATH but it is in the same folder
NSENTER=$(dirname "$0")/nsenter
else
NSENTER=nsenter
fi
if [ -e $(dirname "$0")/importenv ]; then
# with boot2docker, importenv is not in the PATH but it is in the same folder
IMPORTENV=$(dirname "$0")/importenv
else
IMPORTENV=importenv
fi
if [ -z "$1" ]; then
echo "Usage: `basename "$0"` CONTAINER [COMMAND [ARG]...]"
echo ""
echo "Enters the Docker CONTAINER and executes the specified COMMAND."
echo "If COMMAND is not specified, runs an interactive shell in CONTAINER."
exit
fi
PID=$(docker inspect --format "{{.State.Pid}}" "$1")
[ -z "$PID" ] && exit 1
shift
if [ "$(id -u)" -ne "0" ]; then
which sudo > /dev/null
if [ "$?" -eq "0" ]; then
LAZY_SUDO="sudo "
else
echo "Warning: Cannot find sudo; Invoking nsenter as the user $USER." >&2
fi
fi
ENVIRON="/proc/$PID/environ"
# Prepare nsenter flags
OPTS="--target $PID --mount --uts --ipc --net --pid --"
# env is to clear all host environment variables and set then anew
if [ $# -lt 1 ]; then
# No arguments, default to `su` which executes the default login shell
$LAZY_SUDO "$IMPORTENV" "$ENVIRON" "$NSENTER" $OPTS su -m root
else
# Has command
# "$@" is magic in bash, and needs to be in the invocation
$LAZY_SUDO "$IMPORTENV" "$ENVIRON" "$NSENTER" $OPTS "$@"
fi
五、容器导入和导出
5.1 导出容器
导出容器是把容器导出到一个归档文件中,不管容器处于运行还是停止状态都可以导出容器,容器导出可以把文件的可读可写文件层也打包进去,但是不会把Volume的内容包括进来。
# 案例:虽然看不懂
# 在容器内部创建一个文件test
gupan@ubuntu:~$ sudo docker exec -it test sh
[sudo] password for gupan:
# touch test
# exit
# 导出容器
gupan@ubuntu:~$ sudo docker export test > test.tar
gupan@ubuntu:~$ ls
Desktop Documents examples.desktop Pictures Templates Videos
docker-engine_17.05.0_ce-0_ubuntu-xenial_amd64.deb Downloads Music Public test.tar
gupan@ubuntu:~$
5.2 导入容器
导入容器会变成一个镜像,启动这个镜像才可以恢复容器。
# 导入容器语法
docker import tar包包名(还可以采用网址的形式)
# 但是用上述方法生成的镜像是没有标签的,如果想要生成标签,可以使用管道命令
gupan@ubuntu:~$ cat test.tar | sudo docker import - gupan/test:latest
[sudo] password for gupan:
sha256:aec228c185c9e570026610eb1b422d95423e4dba80f1e981243ee5e1291553ab
gupan@ubuntu:~$ sudo docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
gupan/test latest aec228c185c9 26 seconds ago 69.8MB
ubuntu latest 452a96d81c30 2 weeks ago 79.6MB
hello-world latest e38bc07ac18e 4 weeks ago 1.85kB
nginx alpine ebe2c7c61055 4 weeks ago 18MB
gupan@ubuntu:~$
--message参数
--message可以添加commit的信息
--change参数
--change可以在原有的Dockerfile后面追加指令
gupan@ubuntu:~$ cat test.tar | sudo docker import --change "CMD cat /etc/hosts" - gupan/test:latest
sha256:cc19a900cb5515e7213bc5e7098544810fc9c9d3ad4c12d9ec69bef3b64f46a5
gupan@ubuntu:~$ sudo docker run gupan/test:latest
127.0.0.1 localhost
::1 localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
172.17.0.4 7d7369b6e7ea
gupan@ubuntu:~$