09_Docker和虚拟化
约 3134 字大约 10 分钟
2025-11-05
Docker 是容器化技术的代表,能高效打包、部署和运行应用。本文档从简单的工作流入手,介绍镜像(Image)、容器(Container)、常用docker命令,端口映射、卷(Volume)和 Dockerfile 等核心概念。
1. Docker 工作流概述

在安装了 docker 之后,运行的第一条 docker 命令大概率就是:
docker run hello-world以这条命令为例,我们可以了解到,Docker的工作流如下所示:
- 客户端联系守护进程:客户端 将请求发送给后台 Docker daemon。
- 拉取镜像:daemon 从 Docker Hub(远端仓库) 下载
hello-world镜像Image。 - 创建容器:基于镜像Image生成容器Container,运行容器内的可执行文件,产生输出(如 "Hello from Docker!")。
- 返回输出:daemon 将结果流回客户端,显示在终端。
从上述工作流,可以看出docker最重要的概念就是围绕: Image,Container, daemon展开的
2. Dockerfile 和 镜像(Image) 和 compose文件
2.1 Dockerfile
Docker_File 就是在将现有的程序,打包成镜像的时候,定义这个程序运行所需的文件系统,依赖的安装步骤,项目文件放置位置,以及容器启动时执行什么程序的说明文档。
说明文档就位后 docker build 根据说明书生成镜像,docker run 根据镜像启动容器
对于一个复杂的业务程序,比如说某个管理系统,为了运行该系统,对应的机器可能要满足EXT4的文件系统,nginx,redis等中间件,各种库文件,才能保障业务程序能够正常运行,也可能对依赖的版本和安装顺序均有要求,而这通常是 人工确认 为主,工具辅助为辅的。
因为 Docker并不需要知道这个项目的业务意图,也就没必要支持根据现有项目自动导入依赖
以一个基础的 Dockerfile 为例 
- FROM debian:bookworm-slim 是基础文件系统
首先要为运行的容器准备一份用户态的文件系统,里面也有 /bin/sh, /tmp/等相关目录,但是这个文件系统没有自己的kernel,所以容器启动的时候使用的还是宿主机的kernel (轻量化考量)
Bookworm-slim是 Debian官方Docker镜像的一个tag, 其中:
- debian:镜像仓库名,默认从 Docker Hub 的官方 debian 仓库拉取
- bookworm:Debian 12 的发行代号
- slim:精简版,去掉了很多文档、工具、非必要包,比完整 Debian 镜像小
- RUN apt-get install .. 把真实的依赖写进镜像层
使用Docker build命令的时候,会临时启动一个构建容器,在内部执行 apt-get install命令。这个时候,之前第一步的文件系统里面就多了项目所需的依赖
- ENTRYPOINT 决定容器启动时运行什么
ENTRYPOINT ["/entrypoint.sh"], 当你执行:
docker run artcmdb/bmc-kvm本质上就是 Docker 用这个镜像的文件系统启动了 /entrypoint.sh 这个进程。
- 镜像是只读的,容器是在镜像上加一层可写层
根据上面的描写,大家对容器和镜像的关系理解可能就会更深一些
- 只读层:Debian
- 只读层:apt 安装的软件
- 只读层:COPY 进去的项目文件
- 可写层:当前容器运行期间产生的日志、临时文件、修改
镜像将应用及其依赖打包成只读模板,用于生成容器。同一镜像可创建多个容器。
2.2 镜像Image
通过Dockerfile, 将现有的项目打包成镜像后,接下来的操作就都简单了,针对镜像没啥好说的,主要就是一些常用命令。
- 列出镜像:
docker image ls - 删除镜像:
docker image rm <imageName> - 查看全部docker容器的元数据
docker inspect [容器id]2.3 compose文件
3.容器(Container)
容器是镜像的可运行副本:
- 生成后,与镜像并存。
- 停止容器仅暂停其运行,但容器的数据还会保留;需手动删除释放空间。
3.1常用命令
列出运行容器:
docker container ls # 或旧版简写 docker psdocker ps源于 Linuxps命令,现保留兼容。
列出所有容器:
docker container ls -a # 或 docker ps -a删除容器:
docker container rm <containerID>- 停止后删除释放名称和空间。
启动停止容器:
docker container start <containerID>
docker container stop <containerID> # 发送 SIGTERM,优雅关闭
docker container kill <containerID> # 发送 SIGKILL,强制终止run创建新容器;start复用现有。查看日志:
docker container logs <containerID>- 用于调试崩溃容器。
3.2 容器内部路径与隔离
容器文件系统与主机隔离,容器的文件系统来自于镜像,而镜像里面的镜像使用的文件系统是自己From出来的。
因为文件系统是隔离的,因此容器和主机之前互通数据,是通过端口映射达成的。端口映射的内容其实也很好理解,外部机器访问的实际上还是宿主机,完了宿主机开放虚拟机的端口出来提供服务。不做端口映射的话,那自然外界的请求也到不了docker内,也就无法处理
3.3 端口映射(Port Mapping)
容器端口与主机隔离。暴露端口(Expose)指通过映射让容器服务(如 nginx:8080)可被主机/外部访问。
docker run -d --name mynginx -p 8080:8080 nginx- 格式:
-p <宿主机端口>:<容器端口> - 效果:
- 容器内:nginx 监听 8080。
- 主机:
http://localhost:8080访问。 - 外部:若防火墙允许,访问主机 8080 转发至容器。
| 格式 | 含义 |
|---|---|
| 主机端口:容器端口 | 主机 8080 → 容器 8080 |
3.4 进入容器
既然容器有自己的文件系统,本来从外界来看,其也是一台独立的机器,自然也可以进入其文件系统内进行操作,只是在成熟的运维中,我们几乎不会有进入容器内部操作的必要,其本身就应该只是一个封装良好的独立黑盒。对docker运行状态的确认应该通过 docker log 来实现,这才是正确的决策。
docker exec -it <containerName> bash-i:交互模式;-t:伪终端。- 若无
bash,用/bin/sh。

4. 卷(Volume):数据持久化
容器的文件系统是临时的,不具备长时间存储的必要,所以删除容器会丢失数据。 使用卷Volume 如外置硬盘,就可以独立于容器生命周期,这样容器被删掉,本来的内容也不会丢失
在实际应用中,Volume 几乎等同于 LVM, 首先用户通过LVM技术,把对应的磁盘分成对应的逻辑卷,并且往往是这样的
lvcreate -L 850G -n pvbox_vod_850g <vg名称> /dev/sda- lvcreate:核心命令,表示“创建逻辑卷”
- L 850G:指定容量大小(Size)。这里是划分出 850G 的空间
- -n :指定逻辑卷的名称(Name)
- <vg名称>:指定从哪个卷组(VG)里切出空间
- /dev/sda:指定物理卷(PV)。强制 LVM 只能从 sda 这块盘上拿空间
在确认LVM卷后,现实的玩法通常是: 首先将LVM 挂载(Mount) 到宿主机的某一个目录下,然后由容器来绑定该目录Docker 会自动在这个目录下创建一个名为 _data 的文件夹来管理数据
docker run -d \
--name my_pcdn_container \
-v your_volume_name:/data \
artifact.bytedance.com/x86_64/...-v(volume): 表示要进行数据卷挂载。它的格式永远是 宿主机资源 : 容器内路径。这里的Volume就是已经挂载好目录的LVM,对应的路径也就是: /var/lib/docker/volumes/
4.1 配置挂载
使用Volume除了对容器进行扩容处理以外,还可以将容器运行的配置文件进行挂载,方便后续修改容器的配置;几乎所有成熟的开源组件和企业级应用,都是通过这种方式来注入配置的。
volumes:
- /data/nginx/nginx.conf:/etc/nginx/nginx.conf:ro5. 查看 docker 容器使用的资源
在容器的使用过程中,如果能及时的掌握容器使用的系统资源,无论对开发还是运维工作都是非常有益的。幸运的是 docker 自己就提供了这样的命令: docker stats。
docker stats 命令用来显示容器使用的系统资源。不带任何选项执行 docker stats 命令:

默认情况下,stats 命令会每隔 1 秒钟刷新一次输出的内容直到你按下 ctrl + c。下面是输出的主要内容:
- [CONTAINER]:以短格式显示容器的 ID。
- [CPU %]:CPU 的使用情况。
- [MEM USAGE / LIMIT]:当前使用的内存和最大可以使用的内存。
- [MEM %]:以百分比的形式显示内存使用情况。
- [NET I/O]:网络 I/O 数据。
- [BLOCK I/O]:磁盘 I/O 数据。
- [PIDS]:PID 号。
如果不想持续输出,可以只返回当前的状态
docker stats --no-stream6. 查看 docker 容器
docker inspect 命令用于获取 Docker 对象(容器、镜像、卷、网络等)的详细信息,返回的是 JSON 格式的详细信息,当接触新业务的时候,使用该命令可以快速了解到容器相应配置的情况。
因为直接使用 docker inspect 输出的内容太多,因此一般都是搭配 grep 命令使用
# 查看容器的compose文件
docker inspect victoriametrics | grep -i compose# Args 记录了容器在启动时追加给入口程序(ENTRYPOINT)的具体参数列表
docker inspect ba1a4310cb2f | grep -iA 15 "Args"Compose文件的解析
Dockefile是针对单个容器进行镜像打包啥做的操作,在实际的工作中,往往涉及的是多个docker容器之间相互通信,维护,这个使用就需要运维人员有查看 docker-compose.yml 文件的能力,下面是一个compose文件里面,关于部署postgres数据库的说明。
往往实际工作中,就是一台装好操作系统的裸机,下载好docker后,使用一个compose文件,然后直接
docker compose up -dversion: '3.7'
services: postgres: image: postgres:latest # 容器使用的镜像 container_name: postgres # 容器名称 network_mode: "host" # 直接使用宿主机的网络,不走容器的bridge environment: POSTGRES_USER: postgres # 改为默认管理用户 POSTGRES_PASSWORD: postgres123 #POSTGRES_DB: ttydash POSTGRES_INITDB_ARGS: "--auth-host=scram-sha-256 --auth-local=md5" volumes: - /data/postgresql/data:/var/lib/postgresql/data # 数据库数据目录 - /data/postgresql/sql/00-create-user.sql:/docker-entrypoint-initdb.d/00-create-user.sql - /data/postgresql/sql/01-init.sql:/docker-entrypoint-initdb.d/01-init.sql - /data/postgresql/sql/02-security.sql:/docker-entrypoint-initdb.d/02-security.sql - /data/postgresql/sql/03-rbac.sql:/docker-entrypoint-initdb.d/03-rbac.sql - /data/postgresql/sql/04-p1-enhancements.sql:/docker-entrypoint-initdb.d/04-p1-enhancements.sql - /data/postgresql/sql/05-seed-data.sql:/docker-entrypoint-initdb.d/05-seed-data.sql restart: unless-stopped # 容器重启策略,除非检测到手动stop,否则都会自动拉起 healthcheck: # 该容器运行状态的健康检测 test: ["CMD-SHELL", "pg_isready -U postgres -d postgres"] start_period: 30s
开发和运维工程师在docker上面的对接
作为运维,不需要去懂开发具体的业务代码是怎么写的,但为了能为开发搭建环境并编写 docker-compose.yml,则必须向开发索要以下 4 份核心资料:
软件镜像(Image)或 Dockerfile
如果是打包好的镜像:开发提供镜像地址(如 registry.company.com/backend/user-service:v1.0)或解压包(.tar)。 如果是源码:开发提供代码仓库地址,并附带一个写好的 Dockerfile(定义了怎么把源码编成镜像)。
软件依赖关系与架构清单(Dependencies) 你需要问清:“你的程序需要依赖哪些组件?” 数据库:PostgreSQL/MySQL?什么版本?是否需要初始化 SQL 脚本? 缓存/中间件:Redis?RabbitMQ?以什么配置运行? 存储需求:程序产生的日志、上传的文件需要保存在宿主机的哪个路径?(例如 /var/log/app)
环境变量与运行参数(Environment Variables) 程序运行所需的配置项,尤其是不同环境(测试 vs 生产)不一样的参数: 数据库连接地址、账号密码。 外部 API 密钥、监听端口(如 HTTP 8080)。 内存/CPU 的预期开销(方便你在 compose 里做资源限制)。
健康检查与验证方法(Health Check & Smoke Test) “我怎么知道你的程序真的启动成功了?” 让开发提供一个健康检查接口(例如 GET /health 或返回 200 OK 的 HTTP 路径),或者一个简单的验证命令。
