3、Docker容器管理:容器生命周期管理、容器网络配置、容器数据持久化(Volume、Bind Mount)、容器资源限制
好,咱们进入第三章。这一章我打算聊聊Docker容器管理的几个核心操作。说实话,很多新手刚接触Docker时,觉得无非就是docker run一下完事。但真正到了生产环境,你会发现容器生命周期、网络、数据持久化、资源限制,每一个都是坑。我在项目中踩过不少雷,今天一并分享给你。
3.1 容器生命周期管理
容器的生命周期,说白了就是「创建→运行→停止→删除」这一套流程。但实际用起来,远不止这么简单。
3.1.1 容器的创建与启动
最常用的命令当然是docker run。我个人习惯用--name给容器起个有意义的名字,不然一堆随机ID,后期根本分不清谁是谁。
# 创建并启动一个nginx容器
docker run -d --name my-nginx -p 8080:80 nginx:latest
-d表示后台运行,-p做端口映射。嗯,这里要注意:如果你忘了加-d,容器会直接挂在前台,Ctrl+C一按,容器就停了。我曾经在测试环境犯过这个错,一关终端,服务全挂了。
3.1.2 容器的暂停与恢复
有时候你需要临时暂停容器,但又不想彻底停止它。比如在做数据库迁移时,我习惯先docker pause住容器,等迁移完再docker unpause。这和docker stop的区别在于:pause只是冻结进程,不释放资源;stop则是发送SIGTERM信号,走完整关闭流程。
# 暂停容器
docker pause my-nginx
# 恢复容器
docker unpause my-nginx
3.1.3 容器的重启策略
你想想看,如果容器因为内部进程崩溃退出了,怎么办?Docker提供了重启策略,我个人强烈建议生产环境一定要设置--restart参数。
| 策略 | 说明 |
|---|---|
| no | 不自动重启(默认) |
| on-failure | 退出码非0时重启 |
| always | 总是重启,除非手动stop |
| unless-stopped | 总是重启,除非手动stop或Docker重启 |
# 设置容器退出后自动重启
docker run -d --name my-app --restart always my-app:latest
我曾经有个Java应用,内存泄漏导致OOM,容器反复崩溃。当时没设--restart,半夜报警才发现服务挂了几个小时。从那以后,我所有生产容器都至少配on-failure。
3.2 容器网络配置
网络这块,很多初学者容易搞混。Docker默认有三种网络模式:bridge、host、none。但实际项目中,我们经常需要自定义网络。
3.2.1 默认网络模式
Bridge模式:这是默认模式。每个容器通过虚拟网桥(docker0)与宿主机通信。容器之间可以通过IP访问,但默认不能通过容器名通信。
Host模式:容器直接使用宿主机网络栈,没有独立IP。性能好,但端口冲突风险大。我个人很少用,除非对网络性能有极致要求。
None模式:容器没有网络接口。适合一些离线计算任务。
3.2.2 自定义网络
我建议你养成一个好习惯:创建自定义网络,让容器通过容器名互相访问。这样即使IP变了,服务也不会断。
# 创建自定义bridge网络
docker network create --driver bridge my-net
# 在自定义网络中启动容器
docker run -d --name web --network my-net nginx
docker run -d --name db --network my-net mysql
# 现在web容器可以直接ping db容器名
docker exec web ping db
嗯,这里要注意:自定义网络还支持--ip指定静态IP,但我不推荐这么做。容器化部署的核心思想就是「不要依赖IP」,用服务发现或DNS解析才是正道。
3.2.3 端口映射与暴露
-p参数大家都会用,但--expose很多人不知道。它只是声明容器开放了哪些端口,并不做映射。通常配合docker-compose或网络策略使用。
# 暴露端口但不映射
docker run -d --name api --expose 3000 my-api
3.3 容器数据持久化
容器是「无状态」的,一旦删除,里面的数据就没了。这显然不行。Docker提供了两种持久化方式:Volume和Bind Mount。
3.3.1 Volume(卷)
Volume由Docker管理,存储在宿主机/var/lib/docker/volumes/目录下。我个人的经验是:能用Volume就用Volume,因为它更安全、更易迁移。
# 创建卷
docker volume create my-data
# 挂载卷到容器
docker run -d --name db -v my-data:/var/lib/mysql mysql
你还可以用docker volume ls查看所有卷,用docker volume inspect查看详情。我曾经在迁移数据时,直接打包卷目录,到新机器上恢复,非常方便。
3.3.2 Bind Mount(绑定挂载)
Bind Mount直接把宿主机目录挂载到容器里。适合开发环境,因为你可以实时修改代码,容器内立刻生效。
# 挂载宿主机目录到容器
docker run -d --name dev -v /home/user/code:/app node:14
但要注意:Bind Mount会覆盖容器内的目录内容。我曾经犯过一个错,把宿主机空目录挂载到/etc/nginx,结果nginx配置全没了,服务直接挂掉。所以挂载前,一定确认好目录结构。
核心区别总结:
- Volume:Docker管理,跨主机迁移方便,推荐生产环境使用
- Bind Mount:依赖宿主机路径,适合开发和调试
3.3.3 tmpfs挂载
还有一种临时挂载方式——tmpfs。数据存在内存中,容器停止后自动消失。适合存放敏感信息或临时缓存。
# 挂载tmpfs
docker run -d --name cache --tmpfs /tmp:rw,size=100m my-app
3.4 容器资源限制
如果不限制资源,一个容器可能把宿主机CPU和内存吃光,影响其他容器。我在生产环境见过太多这样的案例了。
3.4.1 内存限制
用-m或--memory限制最大内存。建议同时设置--memory-swap,否则容器可能使用swap,导致性能下降。
# 限制内存为512M,swap为1G
docker run -d --name app -m 512m --memory-swap 1g my-app
如果容器超出内存限制,会被OOM Killer杀掉。你可以用--oom-kill-disable禁用OOM杀进程,但我不建议这么做——让容器重启比让宿主机崩溃好得多。
3.4.2 CPU限制
CPU限制相对灵活。你可以限制CPU份额(相对权重),也可以绑定到特定CPU核心。
# 限制CPU份额(默认1024)
docker run -d --name app --cpu-shares 512 my-app
# 绑定到CPU 0和1
docker run -d --name app --cpuset-cpus 0,1 my-app
我个人习惯用--cpus参数,直接限制容器能使用的CPU核心数(可以是小数)。比如限制使用1.5个核心:
docker run -d --name app --cpus 1.5 my-app
3.4.3 磁盘I/O限制
这个用得相对少,但如果你有多个容器竞争磁盘,可以考虑限制读写速率。
# 限制读速率为10MB/s,写速率为5MB/s
docker run -d --name app --device-read-bps /dev/sda:10mb --device-write-bps /dev/sda:5mb my-app
避坑指南:我曾经在生产环境没设资源限制,一个日志采集容器把CPU吃满,导致业务容器响应超时。从那以后,我所有容器都至少设置-m和--cpus。记住:不设限制,就是给故障留后门。
警告:资源限制不是万能的。如果宿主机本身资源不足,限制再多也没用。建议配合监控工具(如Prometheus + Grafana)实时观察资源使用情况。
好了,这一章的内容就到这里。容器生命周期、网络、数据持久化、资源限制,这四个点你掌握了,Docker容器管理基本就入门了。下一章我们会聊聊Docker Compose,到时候把这些知识点串起来,做一个完整的微服务部署示例。