Docker容器管理:运行、启动、停止、删除容器,端口映射、数据卷
好,咱们进入容器管理的核心环节。说白了,Docker 容器就像一个个轻量级的虚拟机,但比虚拟机快得多。我刚开始接触的时候,觉得它跟普通进程没什么两样——嗯,这个感觉是对的,容器其实就是一组被隔离的进程。
容器的生命周期:从生到死
每个容器都会经历几个基本状态:Created(已创建)、Running(运行中)、Paused(暂停)、Exited(已退出)、Deleted(已删除)。我个人习惯把这串状态记成「一辆车的四个档位」。你想想看,开车得有启动、加速、暂停、熄火,对吧?容器也是这个道理。
核心规律:容器本质上是一个进程。它活着,就提供服务;它死了,就释放资源。但容器退出后,文件系统快照还在,除非你显式删除它。
运行容器:docker run 的讲究
docker run 是使用频率最高的命令。它等于先 docker create 再 docker start。我在项目中遇到过一个问题:新人经常把 docker start 和 docker run 混用,结果容器跑起来后怎么都连不上。为什么会这样?因为 docker run 可以一次性附加端口映射和挂载卷,而 docker start 只是启动一个已存在的容器。
基本用法:
# 运行一个Nginx容器,映射端口80,后台运行
docker run -d --name my-nginx -p 8080:80 nginx:latest
# -d: 后台运行(daemon模式)
# --name: 给容器起个名,方便管理
# -p: 端口映射(宿主机端口:容器端口)
补充一个我踩过的坑。曾经我直接 docker run alpine,结果容器秒退。因为我没给它任何任务。Alpine 镜像默认的 CMD 是 /bin/sh,但交互式终端没连上,sh 进程跑完就退出了。解决方案很简单:
# 保持容器运行
docker run -dit --name my-alpine alpine /bin/sh
-it 组合起来是「交互式终端」,-d 是后台。这样容器启动后不会立即退出。
启动与停止:pause、stop、kill
这三个操作容易混淆。我简单说一下区别:
| 命令 | 行为 | 信号 | 释放资源 |
|---|---|---|---|
docker pause | 暂停进程 | SIGSTOP | 不释放CPU,但暂停执行 |
docker stop | 优雅停止 | SIGTERM(默认)→ SIGKILL(10秒后) | 释放CPU和内存 |
docker kill | 强制杀死 | SIGKILL | 立即释放CPU和内存 |
一点建议:我在实际生产环境里,优先用 docker stop,给容器进程一个清理现场的机会。比如停止数据库容器前,它需要刷写缓存、关闭连接池。直接用 docker kill 可能会丢数据。
常用命令一览:
# 启动已创建但未运行的容器
docker start my-container
# 暂停运行中的容器
docker pause my-container
# 恢复暂停的容器
docker unpause my-container
# 优雅停止容器,等待10秒
docker stop my-container
# 强制杀死容器
docker kill my-container
删除容器:清理的艺术
容器不删除会一直占用磁盘空间。我见过一台测试服务器上挂了上千个已退出的容器,磁盘直接爆满。所以定期清理很重要。
删除的三种姿势:
# 删除一个已停止的容器
docker rm my-container
# 强制删除运行中的容器(不推荐)
docker rm -f my-container
# 批量删除所有已退出的容器
docker container prune
# 或者更犀利的:删除所有容器(包括运行中的那个)
docker rm -f $(docker ps -aq)
警告:docker rm -f 加上 $(docker ps -aq) 是个核武器。我曾不小心在线上环境执行过这个命令,结果所有容器瞬间消失。恢复起来非常费劲。所以使用前一定要确认容器是否重要。
端口映射:容器怎么对外提供服务
容器有自己的网络命名空间,默认与宿主机隔离。想让外部访问容器内的服务,就得做端口映射。说白了,就是宿主机开一个「门」,把请求转发到容器的「门」上。
映射的两种方式:
# 1. 固定映射:宿主机8080 -> 容器80
docker run -d -p 8080:80 nginx
# 2. 随机映射:宿主机随机端口 -> 容器80
docker run -d -P nginx
-P(大写)会自动将容器暴露的所有端口映射到宿主机高位端口。我个人不太用这种方式,因为随机端口不好记,也没法放进防火墙规则。
我曾经遇到过一个案例:质谱数据的上传服务需要暴露一个 Web 接口,同时内部还要通过 gRPC 通信。我把两个端口映射出来:
docker run -d \
--name ms-web \
-p 443:443 \
-p 50051:50051 \
my-quality-image:latest
数据卷:容器消亡,数据不死
容器的文件系统是临时的。一旦删除容器,所有写入的数据都会消失。这对于质谱数据处理来说是致命的——你辛辛苦苦跑了一天的结果可能说没就没。数据卷就是用来解决这个问题的。
数据卷相当于宿主机上的一个「持久化目录」,挂载到容器内部。容器可以读写它,容器删除后数据仍然保留。
三种挂载方式:
| 类型 | 命令选项 | 特点 | 适用场景 |
|---|---|---|---|
| 绑定挂载 | -v /宿主机路径:/容器路径 | 直接映射宿主机的目录 | 开发环境,代码热更新 |
| 命名卷 | -v 卷名:/容器路径 | 由Docker管理,目录在/var/lib/docker/volumes | 生产环境,数据持久化 |
| 匿名卷 | -v /容器路径 | Docker自动生成随机卷名 | 临时调试,很少用 |
实际场景演示:
# 1. 绑定挂载:宿主机的/data/ms导出原始数据
docker run -v /data/ms:/app/data my-ms-processor:latest
# 2. 命名卷:创建专用卷存放数据库文件
docker volume create ms-results
docker run -v ms-results:/var/lib/mysql mysql:8
# 3. 查看卷信息
docker volume ls
docker volume inspect ms-results
我的经验:质谱数据量通常很大,一个 RAW 文件可能有几个 GB。我建议使用命名卷,这样备份、迁移都方便。我曾经直接挂载宿主机的 NFS 目录,结果因为网络延迟导致容器写入变慢,整个批量处理流程都卡住了。后来改成命名卷,配合定时同步到 NFS,再没出现过问题。
本章知识体系总览
下面这个 SVG 图梳理了容器管理的核心逻辑。我从运行开始,经过启动、停止、暂停等状态变更,一直到删除容器;同时把端口映射和数据卷作为两个独立但重要的配置点连接进来。
综合实战:一个完整的质谱容器管理流程
结合上面的知识,我们来模拟一个实际场景。你有一批质谱 RAW 文件需要批量处理,处理结果要持久化到宿主机。我已经用这种方式跑了上百个批次,流程如下:
- 创建卷:
docker volume create ms-raw-data和docker volume create ms-output - 启动处理容器:用
docker run -d --name ms-batch-processor -v ms-raw-data:/input -v ms-output:/output -p 9090:9090 ms-processor:latest - 监控状态:
docker logs -f ms-batch-processor看实时日志 - 暂停 / 停止:如果发现参数配错了,先
docker stop ms-batch-processor,调整配置后再docker start - 清理:处理完成后
docker rm ms-batch-processor,但ms-output卷里的结果文件完好无损
最后一个小提示:我习惯给每个容器都加上 --restart=unless-stopped 参数。这样即使宿主机重启,我的质谱处理容器也会自动恢复。除非我手动停掉它,不然它一直开在那。你想想看,半夜跑大批量处理,要是机器突然重启,得少睡多少安稳觉?