容器是一个标准的软件单元,它将代码及其所有依赖项打包,从而使应用程序能够从一个计算环境快速可靠地运行到另一个计算环境。Docker容器镜像是一个轻量级的、独立的、可执行的软件包,包含运行应用程序所需的一切:代码、运行时、系统工具、系统库和设置。
容器镜像在runtime成为容器,在Docker容器的情况下——镜像在Docker引擎上运行时成为容器。 不论基础架构如何,容器化软件都可用于基于Linux和Windows的应用程序,运行效果总是相同的。容器将软件与其环境隔离开来,并确保软件能够一致地工作,尽管在开发和暂存阶段之间存在差异。
您可以使用Docker API或CLI创建,启动,停止,移动或删除容器。您可以将容器连接到一个或多个网络,将存储附加到其中,甚至根据其当前状态创建新镜像。
默认情况下,容器与其他容器及其主机之间的隔离程度相对较高。您可以控制容器的网络,存储或其他底层子系统与其他容器或主机之间的隔离程度。
容器由其镜像以及在创建或启动时为其提供的任何配置选项定义。删除容器后,未存储在持久性存储中的状态更改将消失。
启动
docker container run命令的基础格式为
docker container run [OPTIONS] IMAGE [COMMAND] [ARG…]
以下命令运行一个ubuntu容器,以交互方式附加到本地命令行会话,然后运行/ bin / bash。
$ docker container run -i -t ubuntu /bin/bash
当您运行此命令时,会发生以下情况(假设您使用的是默认registry配置):
- 如果您在本地没有ubuntu镜像,则Docker会将其从配置的registry中拉出,就像您已手动运行docker pull ubuntu一样。
- Docker会创建一个新容器,就像您已经手动运行docker container create命令一样
- Docker将一个读写文件系统分配给容器,作为其最后一层。这允许正在运行的容器在其本地文件系统中创建或修改文件和目录。
- Docker创建一个网络接口来将容器连接到默认网络,因为您没有指定任何网络选项。这包括为容器分配IP地址。 默认情况下,容器可以使用主机的网络连接连接到外部网络。
- Docker启动容器并执行/ bin / bash。由于容器以交互方式运行并附加到终端(由于-i和-t标志),所以可以在输出记录到终端时使用键盘提供输入。
- 当您键入exit终止/ bin / bash命令时,该容器将停止但不会被删除。 您可以重新启动或删除它。
从上图我们可以看到,就会发现Shell提示符发生了变化,说明目前已经位于容器内部了。
在上面的示例中,Shell提示符已经变为root@bba94767002f:/#。@之后的一长串数字就是容器唯一ID的前12个字符。若尝试在容器内执行一些基础命令,可能会发现某些指令无法正常工作。这是因为大部分容器镜像都是经过高度优化的。这意味着某些命令或者包可能没有安装。
下面的示例展示了两个命令:一条执行成功,一条执行失败。
从上面的输出中可以看出,ping工具包并不是官方Ubuntu镜像的一部分。
进程
启动Ubuntu容器之时,让容器运行Bash Shell(/bin/bash)。这使得Bash Shell成为容器中运行的且唯一运行的进程。
列表中PID为1的进程,是容器被告知要运行的Bash Shell;第二个进程是ps -elf命令产生的,这是个临时进程,并且在输出后就已经退出了。也就是说,这个容器当前只运行了一个进程(/bin/bash)。
这意味着如果通过输入exit退出Bash Shell,那么容器也会退出(终止)。原因是容器如果不运行任何进程则无法存在。杀死Bash Shell即杀死了容器唯一运行的进程,导致这个容器也被杀死。
从上面的图中我们可以看到,我们输入了exit,然后再用exec连接到容器,提示is not running。那么我们该如何退出呢?
我们可以按下Ctrl-PQ组合键则会退出容器但并不终止容器运行。这样做会切回到Docker主机的Shell,并保持容器在后台运行。
从上面的图中可以看到,我们按下了Ctrl-PQ组合键退出了容器,这一点我们可以根据最前面的shell提示符来判断。然后我们docker contaien ls 也看到了这个容器确实在运行,并且后续利用docker exec重新连接到该容器了。
生命周期
我们来说一下容器的生命周期——从创建、运行、休眠,直至销毁的整个过程。
我们首先从创建一个容器开始。
我们可以看到,我们用到了—name参数,那么这个参数是什么意思呢,我们可以使用docker container run —help,文档中写明–name string Assign a name to the container,意思给容器指派一个名字。
我们可以打开另外一个窗口来看,主要查看NAMES列。
我们可以看到,CONTAINER ID为ecc42cfc2fdf的NAMES的确是container_life,也就是我们上面指定的。
接下来把该容器投入使用,将一部分数据写入其中。
然后我们按下Ctrl-PQ退出容器。然后使用docker container stop命令来停止容器运行,切换到暂停(vacation)状态。
从上图中可以看到,container_life这个容器确实不在运行中了。然后我们再docker container start 和docker container exec来验证一下newfile.txt这个文件还存不存在。
我们可以看到container_life已经启动了,那么我们用docker container exec连接进去。
我们可以看到,newfile.txt文件还是存在的。这证明停止容器运行并不会损毁容器或者其中的数据。
尽管上面的示例阐明了容器的持久化特性,还是需要指出卷(volume)才是在容器中存储持久化数据的首选方式。
现在我们开始删除容器。
通过在docker container rm命令后面添加-f参数来一次性删除运行中的容器是可行的。但是,删除容器的最佳方式还是分两步,先停止容器然后删除。这样可以给容器中运行的应用/进程一个停止运行并清理残留数据的机会。
现在容器已经删除了–在系统中消失了。
总结一下容器的生命周期。可以根据需要多次停止、启动、暂停以及重启容器,并且这些操作执行得很快。但是容器及其数据是安全的。直至明确删除容器前,容器都不会丢弃其中的数据。就算容器被删除了,如果将容器数据存储在卷中,数据也会被保存下来。
删除和停止
我们一般推荐两阶段方式来停止并删除容器,也就是先停止后删除。分别使用docker container stop和docker container rm 。
docker container stop用来停止容器,那么就有docker container start来启动容器。
有时候我们也可以使用docker container rm -r 直接删除容器,直接跳过stop操作,注意,这里加了一个-f选项。
意思是强制删除一个正在运行中的容器,也就是不需要先停止,直接删除。
两者的区别:
docker container stop命令向容器内的PID 1进程发送了SIGTERM这样的信号。给容器内进程发送将要停止的警告信息,给进程机会来有序处理停止前要做的事情。如果10s内进程没有终止,那么就会收到SIGKILL信号。这是致命一击。但是,进程起码有10s的时间来善后。
docker container rm <container> -f命令不会先友好地发送SIGTERM,这条命令会直接发出SIGKILL,也就是直接就结束了,没有给进程一点善后时间。
案例
angry_shaw先停止后删除, crazy_hamilton直接强制删除
重启策略
在Docker运行中使用–restart标志,您可以为容器在退出时应该或不应该重新启动指定重启策略。
当容器上的重启策略处于活动状态时,它将在docker ps中显示为“Up”或“Restarting”。使用docker events查看重启策略是否有效也很有用。
Docker支持以下重启策略:
在每次重新启动之前都会添加一个不断增加的延迟(是之前延迟的两倍,从100毫秒开始),以防止服务器溢出。这意味着守护进程将等待100 ms、200 ms、400、800、1600等等,直到达到on-failure限制,或者当docker stop或docker rm -f 容器时。
如果容器重新启动成功(容器启动并运行至少10秒),则延迟将重置为其默认值100 ms。
下面我们来开始进行操作:
always:
$ docker container run –name container_life –restart=always -it ubuntu /bin/bash
root@45490d0770b7:/#
#接下来我们检测一下重启策略设置正确
$ docker inspect –format='{{json .HostConfig.RestartPolicy}}’ container_life{“Name”:”always”,”MaximumRetryCount”:0}
#设置是正确的
$ docker container ls
#接着我用exit退出一下
root@45490d0770b7:/# exit
exit
#再看下容器的状态
$ docker container ls
#状态还是启动中。我们从上图看到,我们有3个容器是启动状态,那么我们如果重启docker一下,看下还有哪些容器会重新启动(注意: 我这里的重启是针对mac系统的,如果是windows或者linxu系统可能会不一样)
$ osascript -e ‘quit app “Docker”‘
$ docker container ls
Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?
$ open -a Docker$ docker container ls
#我们在这里可以看到,最后只有container_life重启了。
unless-stopped:
always和unless-stopped的最大区别,就是那些指定了–restart unless-stopped并处于Stopped (Exited)状态的容器,不会在Docker daemon重启的时候被重启。
我们来一个案例
$ docker container run –name redis_life –restart=unless-stopped -it redis /bin/bash
root@51f701ab78af:/data#
$ docker container ls
$ docker inspect –format='{{json .HostConfig.RestartPolicy}}’ container_life
{“Name”:”always”,”MaximumRetryCount”:0}
$ docker inspect –format='{{json .HostConfig.RestartPolicy}}’ redis_life
{“Name”:”unless-stopped”,”MaximumRetryCount”:0}
#我们可以看到container_life设置的是always,redis_life设置的是unless-stopped
$ docker container stop redis_life redis_life
$ docker container stop container_life
container_life
#我们现在把这两个容器都给停止了,那么也就是处于Exited状态了。
$ docker container ls -a
#接下来我们重启docker
$ osascript -e ‘quit app “Docker”‘
$ docker container ls
Error response from daemon: dial unix docker.raw.sock: connect: connection refused
$ open -a Docker
$ docker container ls
我们可以看到,确实只有container_life重启了。
on-failure[:max-retries]。
仅当容器以非零退出状态退出时,才重新启动。可选地,限制重新启动Docker守护进程尝试的次数。
我们先写一个Dockfile
$ cat Dockerfile
FROM ubuntu:14.04
ADD crash.sh
/CMD /bin/bash /crash.sh
接着在写一个 crash.sh脚本
$ cat crash.sh
#/bin/bash
sleep 30
exit 1
#上面的脚本很简单;启动后,它将休眠30秒,然后退出,退出码为1表示出错。下面我们来根据Dockfile构建镜像。
$ docker build -t test_ubuntu .
$ docker run -d –name test_ubuntu –restart on-failure:5 test_ubuntu
d8a8f76d15a8f57bc51e4cc9a2785ba0dc2553255095b674a0c9e29278e249fe
$ docker container ls
然后我们再过几分钟看一下
我们发现容器被停止了,从crash.sh我们知道这个脚本启动后,它将休眠30秒,然后退出,退出码为1,那就是非零,然后我们设置的重启政策是on-failure:5,说明只会重启5次,那么到3分钟的时候就说明要重启第6次了,这时候6超过了最大重启次数5,也就没有重启成功了。
参考资料:
https://docs.docker.com/engine/docker-overview/
https://docs.docker.com/engine/reference/run/#restart-policies—restart
https://blog.codeship.com/ensuring-containers-are-always-running-with-dockers-restart-policy/
https://www.docker.com/resources/what-container
《深入浅出docker》