容器是Docker的另一个核心。
简单地说,容器时镜像的一个运行实例,不同之处在于,它带有额外的可写文件层。
如果认为虚拟机是模拟运行的一整套操作系统和跑在上面的应用。那么Docker容器就是独立运行的一个或者一组应用,以及他们的必需运行环境。它是直接提供应用服务的组件,也是Docker实现快速的启停和高效服务性能的基础。
这里,我们将进入Docker的另一个核心,学习第一个Docker容器。
1、确保Docker已经就绪
首先,我们要先学会查看Docker是否能正常工作,然后学习基本的Docker的工作流:创建并管理容器。
我们将浏览容器的典型生命周期:从创建、管理到停止、直到最终删除。
【第一步:】查看docker进程是否存在,功能是否正常。
[root@master03 /]# docker info
Containers: 5
Images: 5
Kernel Version: 3.10.0-957.21.3.el7.x86_64
Registry: https://index.docker.io/v1/
.....
[root@master03 /]# docker version
Client:
Version: 18.03.1-ce
API version: 1.37
Go version: go1.9.2
Git commit: 9ee9f40
Built: Thu Apr 26 07:12:25 2018
OS/Arch: linux/amd64
Experimental: false
Orchestrator: swarm
Server:
Engine:
Version: 18.03.1-ce
API version: 1.37 (minimum version 1.12)
Go version: go1.9.5
Git commit: 9ee9f40
Built: Thu Apr 26 07:23:03 2018
OS/Arch: linux/amd64
Experimental: false
Docker是基于客户端-服务器(C/S)架构的,docker程序既能作为客户端,也能作为服务器端。作为客户端时,docker程序向docker守护进程发送请求(如请求返回守护进程自身的信息),然后再对返回的请求结果进行处理。
2、创建我们的第一个容器
docker的容器十分轻量级,用户可以随时创建或者删除容器。
【1】可以使用docker create创建一个容器:
此时,我们新建的容器是处于停滞状态,可以使用docke rstart命令来启动它:
[root@master03 /]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
506a711dbc2c ubuntu:latest "/bin/bash" 50 seconds ago Created mystifying_wright
可以使用docker start命令来启动它:
[root@master03 /]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
506a711dbc2c ubuntu:latest "/bin/bash" 2 minutes ago Up 1 second mystifying_wright
【2】新建并启动容器
启动容器有两种方式:基于镜像新建一个容器并启动、将在停止状态的容器重新启动。
所需要的命令主要为docker run,等价于先执行docker create命令,再执行docker start命令。
现在,我们尝试启动第一个Docker容器。命令:docker run命令提供了docker容器的创建到启动的功能。
创建一个容器:
docker run -i -t ubuntu /bin/bash
当我们执行docker run命令时,指定了-i和-t连个命令行参数。-i标志保证容器中STDIN是开启的,让容器的标准输入保持打开;-t告诉docker为要创建的容器分配一个为tty终端。这样,新创建的容器才能提供一个交互式shell。这个执行命令接下来告诉docker创建一个基于ubuntu镜像的容器,那么这一切的背后又发生了什么呢?
首先,docker会检查本地是否存在ubuntu镜像,如果本地没有该镜像的话,那么docker就会通过我们配置的docker镜像源文件/etc/docker/daemon.json去访问镜像仓库地址,并查看仓库中是否有该镜像。一旦docker找到该镜像,就会下载该镜像并将其保存到本地宿主机中。
随后,docker在文件系统内部用这个镜像创建了一个容器。该容器拥有自己的网络、IP地址、以及一个用来和宿主机进行通信的桥接网络接口。
最后,我们告诉docker在新的容器中要运行什么命令,在本实例中我们在容器中运行/bin/bash命令启动了一个Bash shell。
当容器创建完毕后,docker就会执行容器中的/bin/bash命令,这个时候我们就可以看到容器内的shell了。
对于docker容器来说,当运行的应用退出后,容器也就没有继续运行的必要了。
root@60540160cceb:/#
当利用docker run来创建并启动容器时,docker在后台运行的标准操作包括:
- 检查本地是否存在指定的镜像,不存在就从公有/自定义仓库下载;
- 利用镜像创建并启动一个容器;
- 分配一个文件系统,并在只读的容器层外面挂载一层可读写层;
- 从宿主主机配置的网桥接口中桥接一个虚拟接到容器中去;
- 从地址池配置一个IP地址给容器;
- 执行用户指定的应用程序;
- 执行完毕后容器被终止。
3、使用第一个容器
现在,我们已经以root用户登录到新的容器中了,容器ID为60540160cceb,这个容器是一个完整的ubuntu系统,我们可以用它来做任何事情。接下来我们来研究一下这个容器。
检查下容器的主机名:
root@60540160cceb:/# hostname
60540160cceb
root@60540160cceb:/#
可以看到容器的主机名就是该容器的ID。 再来看一下/etc/hosts文件:
root@60540160cceb:/# cat /etc/hosts
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.2 60540160cceb
root@60540160cceb:/#
docker 已经在hosts文件中为该容器的IP地址添加了一条主机配置项。接下来,我们想使用这个容器,那就安装一个软件包,试一下:
root@60540160cceb:/# apt-get update && apt-get isntall vim
Get:1 http://security.ubuntu.com/ubuntu bionic-security InRelease [88.7 kB]
Get:2 http://archive.ubuntu.com/ubuntu bionic InRelease [242 kB]
Get:3 http://security.ubuntu.com/ubuntu bionic-security/restricted amd64 Packages [5436 B]
Get:4 http://security.ubuntu.com/ubuntu bionic-security/main amd64 Packages [597 kB]
Get:5 http://archive.ubuntu.com/ubuntu bionic-updates InRelease [88.7 kB]
Get:6 http://archive.ubuntu.com/ubuntu bionic-backports InRelease [74.6 kB]
Get:7 http://archive.ubuntu.com/ubuntu bionic/restricted amd64 Packages [13.5 kB]
Get:8 http://archive.ubuntu.com/ubuntu bionic/multiverse amd64 Packages [186 kB]
Get:9 http://archive.ubuntu.com/ubuntu bionic/main amd64 Packages [1344 kB]
Get:10 http://security.ubuntu.com/ubuntu bionic-security/universe amd64 Packages [730 kB]
Get:11 http://security.ubuntu.com/ubuntu bionic-security/multiverse amd64 Packages [4172 B]
Get:12 http://archive.ubuntu.com/ubuntu bionic/universe amd64 Packages [11.3 MB]
Get:13 http://archive.ubuntu.com/ubuntu bionic-updates/multiverse amd64 Packages [14.2 kB]
Get:14 http://archive.ubuntu.com/ubuntu bionic-updates/universe amd64 Packages [1256 kB]
Get:15 http://archive.ubuntu.com/ubuntu bionic-updates/restricted amd64 Packages [10.8 kB]
Get:16 http://archive.ubuntu.com/ubuntu bionic-updates/main amd64 Packages [906 kB]
Get:17 http://archive.ubuntu.com/ubuntu bionic-backports/universe amd64 Packages [3930 B]
Get:18 http://archive.ubuntu.com/ubuntu bionic-backports/main amd64 Packages [2496 B]
Fetched 16.9 MB in 11s (1589 kB/s)
Reading package lists... Done
E: Invalid operation isntall
root@60540160cceb:/#
上述命令,我们就在这个容器中安装了vim软件,当然,我们可以像操作虚拟机一样继续在容器中做任何自己想做的事情。所有工作结束时,输入exit,即可退出容器并返回到我们的宿主机命令提示符了。
退出以后,这个容器状态是怎样的?容器现在已经停止运行了!只有在指定的/bin/bash命令处于运行状态时,我们的容器才胡相应地处于运行状态。一旦退出容器,/bin/bash命令也就结束了,容器也将随之停止运行。
但是容器依然是存在的,我们可以使用docker ps -a查看一下当前系统中容器的列表:
docker ps -a
或者
docker container ls -a
从命令输出结果中,我们能够看到关于容器的很多信息:容器ID、用于创建该容器的镜像、容器最后执行的命令、创建时间、已经容器的推出状态,还有容器的名称。
4、容器命名
docker会为我们创建的每一个容器自动生成一个随机的名称。例如,上边我们刚刚创建的容器就被命名为gallant_chatterjee。如果想为容器指定一个名称,而不是使用自动生成的名称,那么可以使用--name标志来实现。
docker run --name my_container_dev -i -t ubuntu /bin/bash
上边的命令将会创建一个名称为my_container_dev的容器。
注意:一个合法的容器名称只能包含以下字符:小写字母a~z、大写字母A~Z、数字0~9、下划线、圆点、横线(正则表达式表示:[a-zA-Z0-9_.-])
一般情况下,我们用容器的名称来代替容器ID,容器名称有助于分辨容器,当构建容器和应用程序之间的逻辑连接时,容器名称有助于从逻辑上理解连接关系。容器名称的命名必须是唯一的,如果要使用的容器名称已经存在,可以先用docker rm删除已有的同名容器后,再来创建新的容器。
5、重新启动已经停止的容器
重新启动已经停止的容器:
docker start my_container_dev
[root@master03 /]# docker start my_container_dev
my_container_dev
[root@master03 /]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
0450eb9c1263 ubuntu "/bin/bash" 3 hours ago Up 2 seconds my_container_dev
处理容器名称,我们也可以用容器ID来启动指定容器。
作用实际上都差不多的。
[root@master03 /]# docker start 0450eb9c1263
[root@master03 /]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
0450eb9c1263 ubuntu "/bin/bash" 3 hours ago Up 2 seconds my_container_dev
而且,我们也可以使用docker restart命令来重新启动一个容器。
6、进入容器
使用-d参数时,容器启动会进入后台,用户无法看到容器中的信息,某些时候我们需要进入容器进行操作,有多重方法:docker attach命令、dockerexec命令、nsenter工具等。
【1】docker attach命令
docker容器重新启动的时候,会沿用docker run命令时指定的参数来运行,因此我们容器重新启动后会运行一个交互式会话shell。可以使用docker attach命令重新附着到该容器的会话上。
docker attach my_container_dev
这里,也可以使用容器ID,重新附着到容器的会话上,即重新回到了容器的Bash提示符。 当然了,如果推出容器的shell,容器也将会随之停止运行。
使用attach命令有时并不方便,当多个窗口同时attach到同一个容器时,所有窗口都会同步显示。当某个窗口因为命令阻塞是,其他窗口也无法执行操作了。
【2】exec命令
exec命令可以直接在容器内运行命令,如进入到刚创建的容器中,并启动一个bash:
[root@master03 /]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
506a711dbc2c ubuntu:latest "/bin/bash" 15 minutes ago Up 2 seconds mystifying_wright
[root@master03 /]#
[root@master03 /]# docker exec -it 506a711dbc2c /bin/bash
root@506a711dbc2c:/#
注意:容器必须是已启动状态,才能执行exec进入到容器中。
7、创建守护式容器
除了交互式运行的容器,我们可以创建长期运行的守护式(Daemonized)容器,可以通过-d参数来实现。守护式容器没有交互式会话,适合运行应用程序和服务。下面我们启动一个守护式容器,并给容器命名。
docker run --name=demon_dev -d ubuntu /bin/sh -c "while true;
do echo hello world; sleep 1; done"
使用-d参数,docker会将容器放到后台运行,容器启动后将返回一个唯一的ID。
在主机的命令行中,如果我们执行docker ps -a命令,可以看到一个正在运行的容器:
[root@master03 /]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
c3eeacb929ec centos "/bin/sh -c 'while t…" About a minute ago Up 59 seconds daemon_dev
60540160cceb ubuntu "/bin/bash" 24 hours ago Exited (130) 23 hours ago gallant_chatterjee
d622cca65db7 hello-world "/hello" 41 hours ago Exited (0) 41 hours ago infallible_turing
5517d6d70a09 microsoft/dotnet-samples "dotnet dotnetapp.dl…" 41 hours ago Exited (0) 41 hours ago romantic_goodall
39327f2deca5 centos "/bin/bash" 45 hours ago Exited (1) 41 hours ago gifted_elbakyan
[root@master03 /]# docker stop daemon_dev
daemon_dev
[root@master03 /]#
8、容器内部都在干些什么
现在,我们已经有了一个在后台运行while循环的守护型容器。为了探究该容器内部都在干什么,我们可以用docker logs命令来获取容器的日志。
[root@master03 /]# docker logs daemon_dev
hello world
hello world
hello world
hello world
hello world
hello world
hello world
hello world
hello world
hello world
hello world
... ...
可以看到while循环正在想日志里打印hello world。docker会输出最后几条日志项并返回。我们可以在命令后使用-f参数来监控日志,这个与tail -f命令非常相似。
如果想跟踪容器日志的某一个片段,只需要在tail命令后加入-f --line标志即可,如docker logs --tail 10 daemon_dev获取日志的最后10行内容,也可以用docker logs --tail 0 -f daemon_dev来跟踪容器的最新日志而不是读取整个日志文件。
为了让调试更简单,可以使用-t标志位每条日志项加上时间戳:
[root@master03 /]# docker logs -f -t daemon_dev
2019-08-08T02:58:12.341456335Z hello world
2019-08-08T02:58:13.368520719Z hello world
2019-08-08T02:58:14.370227631Z hello world
2019-08-08T02:58:15.374530594Z hello world
2019-08-08T02:58:16.379652693Z hello world
2019-08-08T02:58:17.380910970Z hello world
2019-08-08T02:58:18.388605194Z hello world
2019-08-08T02:58:19.387379839Z hello world
2019-08-08T02:58:20.388318717Z hello world
2019-08-08T02:58:21.392746572Z hello world
... ...
9、查看容器内的进程
查看容器内部运行的进程,可以使用docker top命令,执行该命令,我们将看到容器内的所有进程、运行进程的用户及进程ID
[root@master03 /]# docker top daemon_dev
UID PID PPID C STIME TTY TIME CMD
root 8204 8192 0 23:12 ? 00:00:00 /bin/sh -c while true; do echo hello world; sleep 1; done
root 8260 8204 0 23:12 ? 00:00:00 sleep 1
[root@master03 /]#
10、在容器内部运行进程
docker exec在容器内部额外启动新进程。
在容器内运行的进程有两种类型:后台任务和交互式任务。
后台任务在容器内运行且没有交互需求,交互式认为则保持在前台运行。
对于需要在容器内部打开shell的任务,交互式任务是很实用的。
下面我们在容器中运行后台任务:
[root@master03 /]# docker exec -d demon_dev touch /etc/new_config_file
-d标志标明需要运行一个后台进程,-d标志后指定的是要在内部执行这个命令的容器的名字以及要执行的命令。上面将在demon_dev容器内创建一个空文件夹,文件名为/etc/new_config_file。通过docker exec后台命令,我们可以在正在运行的容器中进行维护、监控及管理任务。
同时,也可以在demon_dev容器中启动一个诸如打开shell的交互式任务,下面我们在容器内运行交互命令:
docker exec -t -i demon_dev /bin/bash
它在容器内创建了一个新的bash会话,有了这个会话,我们就可以在该容器中运行其他命令了。
11、停止守护式容器
要停止守护式容器,只需执行docker stop命令即可,这里有两种方式:
【1】通过容器名称停止正在运行的docker容器:docker stop demon_dev
【2】通过容器ID停止正在运行的docker容器:docker stop a237a340da39
注意:docker stop命令会向docker容器进程发送SIGTERM信号。如果想快速停止某个容器,可以使用docker kill 命令。
查看已经停止容器的状态:
【1】docker ps -a
【2】docker container ls -a;该命令和上边命令作用一样。
【3】docker ps -n x,该命令会显示最后x个容器,不论这些容器正在运行还是已经停止
12、自动重启容器
在某些情况下,我们错误导致容器停止运行,--restart可以让docker容器自动重启,它会检查容器的推出代码,并据此来决定是否要重启容器,默认情况下docker不会重启容器。
docker run --restart=always --name=demon_dev -d ubuntu /bin/sh
字段说明:
--restart标志有几种参数可选:
--restart=always:无论容器的推出代码是什么,docker都会自动重启该容器;
--restart=on-failure:只有当容器的退出代码为非0值的时候,容器才会自动重启;
--restart=on-failure:5:on-failure还接受一个可选的重启次数参数,这样当容器的退出代码为非0值的时,docker会尝试自动重启该容器,最多重启5次。
13、深入容器
除了docker ps获取容器的信息外,我们可以使用docker inspect <Container NAME/ID>来获取更多的容器信息:
[root@master03 /]# docker inspect demon_dev
[
{
"Id": "a237a340da39b27f8f29c5f96e42db7a14b8864bfde3ef305c3ddf48529e6c9f",
"Created": "2019-08-08T03:20:48.775140744Z",
"Path": "/bin/sh",
"Args": [
"-c",
"while true;\ndo echo hello world; sleep 1; done"
],
"State": {
"Status": "exited",
"Running": false,
"Paused": false,
"Restarting": false,
"OOMKilled": false,
"Dead": false,
"Pid": 0,
"ExitCode": 137,
"Error": "",
"StartedAt": "2019-08-08T03:21:59.785575092Z",
"FinishedAt": "2019-08-08T03:22:16.04715018Z"
},
"Image": "sha256:3556258649b2ef23a41812be17377d32f568ed9f45150a26466d2ea26d926c32",
"ResolvConfPath": "/var/lib/docker/containers/a237a340da39b27f8f29c5f96e42db7a14b8864bfde3ef305c3ddf48529e6c9f/resolv.conf",
"HostnamePath": "/var/lib/docker/containers/a237a340da39b27f8f29c5f96e42db7a14b8864bfde3ef305c3ddf48529e6c9f/hostname",
"HostsPath": "/var/lib/docker/containers/a237a340da39b27f8f29c5f96e42db7a14b8864bfde3ef305c3ddf48529e6c9f/hosts",
"LogPath": "/var/lib/docker/containers/a237a340da39b27f8f29c5f96e42db7a14b8864bfde3ef305c3ddf48529e6c9f/a237a340da39b27f8f29c5f96e42db7a14b8864bfde3ef305c3ddf48529e6c9f-json.log",
"Name": "/demon_dev",
"RestartCount": 0,
"Driver": "overlay2",
"Platform": "linux",
"MountLabel": "",
"ProcessLabel": "",
"AppArmorProfile": "",
"ExecIDs": null,
"HostConfig": {
"Binds": null,
"ContainerIDFile": "",
"LogConfig": {
"Type": "json-file",
"Config": {}
},
"NetworkMode": "default",
"PortBindings": {},
"RestartPolicy": {
"Name": "no",
"MaximumRetryCount": 0
},
"AutoRemove": false,
"VolumeDriver": "",
"VolumesFrom": null,
"CapAdd": null,
"CapDrop": null,
"Dns": [],
"DnsOptions": [],
"DnsSearch": [],
"ExtraHosts": null,
"GroupAdd": null,
"IpcMode": "shareable",
"Cgroup": "",
"Links": null,
"OomScoreAdj": 0,
"PidMode": "",
"Privileged": false,
"PublishAllPorts": false,
"ReadonlyRootfs": false,
"SecurityOpt": null,
"UTSMode": "",
"UsernsMode": "",
"ShmSize": 67108864,
"Runtime": "runc",
"ConsoleSize": [
0,
0
],
"Isolation": "",
"CpuShares": 0,
"Memory": 0,
"NanoCpus": 0,
"CgroupParent": "",
"BlkioWeight": 0,
"BlkioWeightDevice": [],
"BlkioDeviceReadBps": null,
"BlkioDeviceWriteBps": null,
"BlkioDeviceReadIOps": null,
"BlkioDeviceWriteIOps": null,
"CpuPeriod": 0,
"CpuQuota": 0,
"CpuRealtimePeriod": 0,
"CpuRealtimeRuntime": 0,
"CpusetCpus": "",
"CpusetMems": "",
"Devices": [],
"DeviceCgroupRules": null,
"DiskQuota": 0,
"KernelMemory": 0,
"MemoryReservation": 0,
"MemorySwap": 0,
"MemorySwappiness": null,
"OomKillDisable": false,
"PidsLimit": 0,
"Ulimits": null,
"CpuCount": 0,
"CpuPercent": 0,
"IOMaximumIOps": 0,
"IOMaximumBandwidth": 0
},
"GraphDriver": {
"Data": {
"LowerDir": "/var/lib/docker/overlay2/da240243eb4afd0a1bda9b90482ff1f3875555da41d48237ee61d35b90c7497a-init/diff:/var/lib/docker/overlay2/cf3353c81ce611aa32fcbb7aa233552e99a19b793272f3d937bbebd5f9e95336/diff:/var/lib/docker/overlay2/e39a6f591379509df8a47813878a7ca08c374436283b1be42879ed44d63111ca/diff:/var/lib/docker/overlay2/e48a2000a68b6f3454557e441d9cbf6dc005e96c708e5b7ddbc9989ef76b4348/diff:/var/lib/docker/overlay2/5610dc8a51f708f89fecf7aaba876834d416610d478bd0ba0b7c45e75b7ea8d7/diff",
"MergedDir": "/var/lib/docker/overlay2/da240243eb4afd0a1bda9b90482ff1f3875555da41d48237ee61d35b90c7497a/merged",
"UpperDir": "/var/lib/docker/overlay2/da240243eb4afd0a1bda9b90482ff1f3875555da41d48237ee61d35b90c7497a/diff",
"WorkDir": "/var/lib/docker/overlay2/da240243eb4afd0a1bda9b90482ff1f3875555da41d48237ee61d35b90c7497a/work"
},
"Name": "overlay2"
},
"Mounts": [],
"Config": {
"Hostname": "a237a340da39",
"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",
"while true;\ndo echo hello world; sleep 1; done"
],
"Image": "ubuntu",
"Volumes": null,
"WorkingDir": "",
"Entrypoint": null,
"OnBuild": null,
"Labels": {}
},
"NetworkSettings": {
"Bridge": "",
"SandboxID": "0f680aed62cb39ea1679cb6f6c68f6cbcf88a80a99c8772c286b317283a304da",
"HairpinMode": false,
"LinkLocalIPv6Address": "",
"LinkLocalIPv6PrefixLen": 0,
"Ports": {},
"SandboxKey": "/var/run/docker/netns/0f680aed62cb",
"SecondaryIPAddresses": null,
"SecondaryIPv6Addresses": null,
"EndpointID": "",
"Gateway": "",
"GlobalIPv6Address": "",
"GlobalIPv6PrefixLen": 0,
"IPAddress": "",
"IPPrefixLen": 0,
"IPv6Gateway": "",
"MacAddress": "",
"Networks": {
"bridge": {
"IPAMConfig": null,
"Links": null,
"Aliases": null,
"NetworkID": "e98047820ea089d37eb8671e5ebce996f1a0fe7107f9e764c8c00aea187e8a56",
"EndpointID": "",
"Gateway": "",
"IPAddress": "",
"IPPrefixLen": 0,
"IPv6Gateway": "",
"GlobalIPv6Address": "",
"GlobalIPv6PrefixLen": 0,
"MacAddress": "",
"DriverOpts": null
}
}
}
}
]
docker inspect会对容器进行详细的检查,然后返回其配置信息,包括名称、命令、网络配置及其他有用数据。
也可以使用-f或--format标志来选定查看结果:
docker inspect --format='{{.State.Running}}' demon_dev
false
查看容器IP地址:
docker inspect --format '{{.NetworkSettings.IPAddress}}' demon_dev
同时指定多个容器,并显示每个容器的输出结果:
[root@master03 /]# docker inspect --format '{{.Name}} {{.State.Running}}' demon_dev romantic_goodall
/demon_dev false
/romantic_goodall false
[root@master03 /]#
注意:通过浏览/var/lib/docker目录来深入了解Docker的工作原理。该目录存放着docker镜像、容器以及容器的配置。所有的容器都保存在/var/lib/docker/containers目录下。
14、删除容器
如果容器已经不再使用,可以使用docker rm命令来删除它们。
命令格式:docker rm [OPTIONS] CONTAINER [CONTAINER...]
支持的选项包括:
- -f:--force=false强行终止并删除一个运行中的容器;
- -l:--link=false删除容器的连接,但是保留容器;
- -v:--volumes=false删除容器挂载的数据券;
如果要删除一个运行中的容器,可以添加-f参数。
[root@master03 /]# docker rm a237a340da39
a237a340da39
[root@master03 /]#
注意:运行中的docker容器是无法删除的。必须先停止容器,才能将其删除,进而才能删除容器中的镜像。
15、导入和导出容器
【1】docker export导出容器
导出容器是指导出一个已经创建的容器到一个文件,不管此时这个容器是否处于运行状态,使用docker export命令。
命令格式:docker export CONTAINER
可以将这些文件传输到其他机器上,在其他机器上通过导入命令实现容器的迁移。
【2】docker import导入容器
导出的文件可以通过docker import命令导入,成为镜像。
[root@master03 /]# cat test_for_run.tar |docker import - test/ubuntu:v1.0
sha256:636a48f42a2e9b7ff948bd0bfb22fd00a3be65df64edd9d3d38bae41255458fa
[root@master03 /]#
[root@master03 /]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
test/ubuntu v1.0 636a48f42a2e 3 seconds ago 64.2MB
mysql latest 2151acc12881 2 weeks ago 445MB
centos latest 9f38484d220f 4 months ago 202MB
microsoft/dotnet-samples latest 70e25069fca7 5 months ago 181MB
[root@master03 /]#
可以看到,我们新导入的镜像test/ubuntu,tag标签为v1.0 。
实际上,我们既可以使用docker load命令来导入镜像存储文件到本地的镜像库,又可以使用docker import命令来导入一个镜像快照到本地镜像库。
【3】docker save保存镜像
docker save保存持久化镜像,我们先获取镜像名称/ID,再执行保存。
# docker save -o [定义一个tar文件的文件名] [要保存的镜像的id]
docker save -o SAVE_FILE.tar IMAGE ID
# 生成的tar文件存储路径为当前路径下
操作一下:
[root@master03 /]# docker save -o mysql.tar 2151acc12881
[root@master03 /]# ls
bin etc lib64 mysql.tar root srv test_for_stop.tar usr
boot home media opt run sys tmp var
dev lib mnt proc sbin test_for_run.tar ubuntu-latest.tar
可以看到,我们已经保存了一个mysql.tar镜像文件到当前路径下。这里,我们就可以在任何地方通过load命令加载镜像了,
【4】docker load加载镜像
上边save命令保存的镜像文件,通过在任何虚拟机上通过load命令加载。
可以看到镜像已经load成功,但是镜像名字却并没有,全部为none。
如果要指定镜像名称,则需要执行docker tag为其打上标签。
格式:docker tag <IMAGE ID> <IMAGE NAME>
执行一下:
【5】docker save 与docker export 以及docker load和 docker import的区别:
- docker import命令可以重新指定镜像的名称,容器快照将丢弃所有的历史记录和元数据信息(即仅保存容器当时的快照状态);cat EXPORT_FILE.tar | docker import - <IMAGE NAME>
docker load命令的镜像存储文件将保存完整的级联,体积也要大。docker load -i <SAVE_FILE.tar>
- 在使用docker export导出镜像包时,主要用于持久化容器(不是镜像),所以我们需要获取容器ID,再去执行导出:
docker export <CONTAINER ID> >EXPORT_FILE.tar
- docker save命令用于持久化镜像(不是容器),我们需要先获取镜像名称,在执行保存:
docker images
docker save <IMAGE NAME/ID> >SAVE_FILE.tar
- 在使用时,docker的 save保存镜像(load与其对应)
- docker的export导出容器(import与其对应操作)
比如,我们已经在docker中完整的布置好整个服务后,里面有mysql、ngnix和自己的微服务等,如果要全部重新布置完全没有必要,只需要将复用docker容器即可,将线上docker导成tar包,并在需要的地方导入并配置即可。
16、小结
容器是直接提供应用服务的组件,也是docker实现快速的启停和高效复古性能的基础。
在生产环境中,因为容器自身的轻量级特性,我们推荐使用容器时在容器前端引入HA(高可靠性)机制,例如使用HAProxy工具来代理容器访问,这样在容器出现故障时,可以快速切换到其他容器上,还可以自动重启故障容器。
本节介绍了docker容器的基本工作原理和基础命令操作,这也是学习如何使用docker的基础,接下来我们将继续深入学习如何使用Docker仓库和Docker Registry。