文章目录
本文主要描述了如下内容
- 对容器和虚拟机两种模型进行了比较
- 虚拟机模型的OS tax问题,并且分析了虚拟机模型相对于物理机模型的巨大优势,以及容器模型如何能够带来更加显著的提升
- 使用docker container run命令启动一组简单的容器,并且对比了前台和后台运行容器在交互方面的差异性。
- 杀死容器中PID为1的进程会杀死容器。
- 如何启动、停止以及删除容器。
- docker container inspect命令,该命令可以查看容器元数据的细节信息。
Docker已经基本实现由OCI发布的镜像和容器标准。这意味着读者在Docker容器这里学习的内容,同样可以在其他实现了OCI标准的容器运行时上应用。
一. Docker容器简介
容器是镜像的运行时实例。正如从虚拟机模板上启动VM一样,用户也同样可以从单个镜像上启动一个或多个容器。虚拟机和容器最大的区别是容器更快并且更轻量级——与虚拟机运行在完整的操作系统之上相比,容器会共享其所在主机的操作系统/内核。
相关命令
# 启动一个新的容器
# 指定了启动所需的镜像以及要运行的应用。
docker container run <image> <app>
docker container run -it ubuntu /bin/bash
# -it参数可以将**当前终端**连接到容器的Shell终端之上。容器随着其中运行应用的退出而终止。
# 容器的停止、启动、删除
docker container stop
docker container start
docker container rm
二. Docker容器详解
1. 容器vs虚拟机
容器和虚拟机都依赖于宿主机才能运行。宿主机可以是笔记本,是数据中心的物理服务器,也可以是公有云的某个实例。
1.1. 虚拟机模型
在虚拟机模型中,首先要开启物理机并启动Hypervisor引导程序。一旦Hypervisor启动,就会占有机器上的全部物理资源,如CPU、RAM、存储和NIC。Hypervisor接下来就会将这些物理资源划分为虚拟资源,并且看起来与真实物理资源完全一致。然后Hypervisor会将这些资源打包进一个叫作虚拟机(VM)的软件结构当中。这样用户就可以使用这些虚拟机,并在其中安装操作系统和应用。
假设需要在物理机上运行4个应用,所以在Hypervisor之上需要创建4个虚拟机并安装4个操作系统,然后安装4个应用。当操作完成后,结构如图所示。
1.2. 容器模型
在Docker世界中可以选择Linux,或者内核支持内核中的容器原语的新版本Windows。与虚拟机模型相同,OS也占用了全部硬件资源。 在OS层之上,需要安装容器引擎(如Docker)。
容器引擎可以获取系统资源,比如进程树、文件系统以及网络栈,接着将资源分割为安全的互相隔离的资源结构,称之为容器。
如下划分出的4个容器并在每个容器中运行一个应用。
Hypervisor(虚拟机监控器)和容器在虚拟化方面的区别和作用。
1.3. 虚拟机的额外开销
每个操作系统都需要开销
容器具备
2. 容器启动过程描述
docker container run -it ubuntu:latest /bin/bash
Unable to find image 'ubuntu:latest' locally
latest: Pulling from library/ubuntu
eed1663d2238: Pull complete
Digest: sha256:2e863c44b718727c860746568e1d54afd13b2fa71b160f5cd9058fc436217b30
Status: Downloaded newer image for ubuntu:latest
# Shell提示符发生了变化,说明目前已经位于容器内部了。
# @之后的一长串数字就是容器唯一ID的前12个字符。
root@80e819b93ae8:/#
启动过程描述
3. 容器进程
ps -elf命令在容器内部查看存在的进程。按下Ctrl-Q组合键则会退出容器但并不终止容器运行。这样做会切回到Docker主机的Shell,并保持容器在后台运行。
查看容器列表
docker container ls
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
8cdbf1e2555e nginx:latest "/docker-entrypoint.…" 8 weeks ago Up 4 minutes 0.0.0.0:80->80/tcp docker-nginx-1
ff4ad6aedc59 langgenius/dify-api:0.6.11 "/bin/bash /entrypoi…" 8 weeks ago Up 4 minutes 5001/tcp docker-worker-1
进入到容器内部
docker container exec -it 8cdbf1e2555e bash
root@8cdbf1e2555e:/# ls
bin boot dev docker-entrypoint.d docker-entrypoint.sh etc home lib media mnt opt proc root run sbin srv sys tmp usr var
root@8cdbf1e2555e:/# whereis nginx
nginx: /usr/sbin/nginx /usr/lib/nginx /etc/nginx /usr/share/nginx
停止和删除容器
docker container stop 3027eb64487
docker container rm 3027eb64487
4. 容器生命周期与文件保存
本节主要关注容器的生命周期——从创建、运行、休眠,直至销毁的整个过程。
新建一个容器,并在这个容器中创建一个文件
$ docker container run --name percy -it ubuntu:latest /bin/bash
root@9cb2d2fd1d65:/#
root@9cb2d2fd1d65:/# cd tmp
root@9cb2d2fd1d65:/tmp# echo "DevOps FTW" > newfile
root@9cb2d2fd1d65:/tmp# cat newfile
DevOps FTW
停止然后重启,看到文件存在
docker container stop percy
docker container ls -a
docker container start percy
# percy
docker container exec -it percy bash
root@41ea8da1ab68:/# cd /tmp
root@41ea8da1ab68:/tmp# ls
newfile
root@41ea8da1ab68:/tmp# cat newfile
DevOps FTW
上面说明了容器的持久化特性,但是卷(volume)才是在容器中存储持久化数据的首选方式。
docker container stop percy
docker container rm percy
docker container ls -a |grep percy
直至明确删除容器前,容器都不会丢弃其中的数据。
5. 优雅地停止容器:两阶段方式停止并删除容器
当使用docker container rm <container> -f
来销毁运行中的容器时,不会发出任何告警,来不及做任何事情。
先通过:docker stop,再rm
docker container stop命令向容器内的PID 1进程发送了SIGTERM这样的信号,会为进程预留一个清理并优雅停止的机会。如果10s内进程没有终止,那么就会收到SIGKILL信号。
6. 利用重启策略进行容器的自我修复
重启策略是容器一种自我修复能力,通常建议在运行容器时配置好重启策略,可以在指定事件或者错误后重启来完成自我修复。重启策略应用于每个容器,可以作为参数被强制传入docker-container run命令中,或者在Compose文件中声明。
目前容器支持的重启策略包括always、unless-stopped和on-failed。
6.1. always策略
除非容器被明确停止(docker container stop),否则该策略会一直尝试重启处于停止状态的容器。
如下示例
# 我们在命令中指定运行Shell进程。当容器启动的时候,会登录到该Shell。
# 退出Shell时会杀死容器中PID为1的进程,并且杀死这个容器。
# 但是因为指定了--restart always策略,所以容器会自动重启。
$ docker container run --name neversaydie -it --restart always alpine sh
//等待几秒后输入exit
/# exit
docker container ls
CONTAINER ID IMAGE COMMAND CREATED STATUS
0901afb84439 alpine "sh" 35 seconds ago Up 1 second
#运行docker container ls命令,就会看到容器的启动时间小于创建时间。
docker container stop neversaydie
neversaydie
docker container ls -a |grep neversaydie
7e3c5c2cef3e alpine "sh" 5 minutes ago Exited (137) 2 seconds ago neversaydie
6.2. unless-stopped 策略
always和unless-stopped
的最大区别,处于Stopped (Exited)状态的容器,不会在Docker daemon重启的时候被重启。
6.3. on-failure策略
on-failure策略会在退出容器并且返回值不是0的时候,重启容器。就算容器处于stopped状态,在Docker daemon重启的时候,容器也会被重启。
Docker Compose中配置如下
version: "3.5"
services:
myservice:
<Snip>
restart_policy:
condition: always | unless-stopped | on-failure
7. Web服务器示例
在该示例中,会使用到我用于Pluralsight视频教程网站中的一个镜像。这个镜像会在8080端口启动一个相当简单的Web服务。
$ docker container run -d --name webserver -p 80:8080 \
nigelpoulton/pluralsight-docker-ci
Unable to find image 'nigelpoulton/pluralsight-docker-ci:latest' locally
latest: Pulling from nigelpoulton/pluralsight-docker-ci
a3ed95caeb02: Pull complete
3b231ed5aa2f: Pull complete
7e4f9cd54d46: Pull complete
。。。
Digest: sha256:7a6b0125fe7893e70dc63b2...9b12a28e2c38bd8d3d
Status: Downloaded newer image for nigelpoulton/plur...docker-ci:latest
6efa1838cd51b92a4817e0e7483d103bf72a7ba7ffb5855080128d85043fef21
启动命令解释:
查看当前运行的容器以及端口的映射情况
$ docker container ls
CONTAINER ID COMMAND STATUS PORTS NAMES
6efa1838cd51 /bin/sh -c... Up 2 mins 0.0.0.0:80->8080/tcp webserver
# 注意:
# 端口信息按照host-port:container-port的格式显示
为什么需要端口映射
直接访问容器的端口(如 8080)通常需要在容器内执行,或者使用 Docker 网络模式使容器直接暴露端口。
但在默认的桥接网络模式下,外部流量无法直接访问容器的端口,必须通过宿主机的端口进行转发。通过 -p 选项进行端口映射,确保外部可以访问到容器内部的服务。如果容器直接运行在宿主机上,使用宿主机的 IP 和指定端口是更常用的做法。
8. 查看容器详情
docker image inspect 38532784de04 > /Users/lianggao/Downloads/11.log
...
"Config": {
"Hostname": "",
"Domainname": "",
"User": "",
"AttachStdin": false,
"AttachStdout": false,
"AttachStderr": false,
"ExposedPorts": {
"5001/tcp": {}
},
"Tty": false,
"OpenStdin": false,
"StdinOnce": false,
"Env": [
"PATH=/usr/local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
"LANG=C.UTF-8",
"GPG_KEY=A035C8C19219BA821ECEA86B64E628F8D684696D",
"PYTHON_VERSION=3.10.14",
"PYTHON_PIP_VERSION=23.0.1",
"PYTHON_SETUPTOOLS_VERSION=65.5.1",
"PYTHON_GET_PIP_URL=https://github.com/pypa/get-pip/raw/dbf0c85f76fb6e1ab42aa672ffca6f0a675d9ee4/public/get-pip.py",
"PYTHON_GET_PIP_SHA256=dfe9fd5c28dc98b5ac17979a953ea550cec37ae1b47a5116007395bfacff2ab9",
"FLASK_APP=app.py",
"EDITION=SELF_HOSTED",
"DEPLOY_ENV=PRODUCTION",
"CONSOLE_API_URL=http://127.0.0.1:5001",
"CONSOLE_WEB_URL=http://127.0.0.1:3000",
"SERVICE_API_URL=http://127.0.0.1:5001",
"APP_WEB_URL=http://127.0.0.1:3000",
"TZ=UTC",
"COMMIT_SHA=12c815c597b121357151c798aae6580304416937"
],
"Cmd": null,
"ArgsEscaped": true,
"Image": "",
"Volumes": null,
"WorkingDir": "/app/api",
"Entrypoint": [
"/bin/bash",
"/entrypoint.sh"
],
三. 容器命令小结