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 图梳理了容器管理的核心逻辑。我从运行开始,经过启动、停止、暂停等状态变更,一直到删除容器;同时把端口映射和数据卷作为两个独立但重要的配置点连接进来。

Docker容器管理核心流程 docker run Running Paused Exited / Stopped docker rm docker pause unpause docker stop docker stop docker rm 端口映射 -p 宿主机端口 → 容器端口 数据卷 -v 持久化数据 / 共享数据 容器删除后,数据卷中的数据仍然保留 端口映射只影响网络访问,不影响容器内进程

综合实战:一个完整的质谱容器管理流程

结合上面的知识,我们来模拟一个实际场景。你有一批质谱 RAW 文件需要批量处理,处理结果要持久化到宿主机。我已经用这种方式跑了上百个批次,流程如下:

  1. 创建卷:docker volume create ms-raw-data 和 docker volume create ms-output
  2. 启动处理容器:用 docker run -d --name ms-batch-processor -v ms-raw-data:/input -v ms-output:/output -p 9090:9090 ms-processor:latest
  3. 监控状态:docker logs -f ms-batch-processor 看实时日志
  4. 暂停 / 停止:如果发现参数配错了,先 docker stop ms-batch-processor,调整配置后再 docker start
  5. 清理:处理完成后 docker rm ms-batch-processor,但 ms-output 卷里的结果文件完好无损

最后一个小提示:我习惯给每个容器都加上 --restart=unless-stopped 参数。这样即使宿主机重启,我的质谱处理容器也会自动恢复。除非我手动停掉它,不然它一直开在那。你想想看,半夜跑大批量处理,要是机器突然重启,得少睡多少安稳觉?