Docker run 参数全解析:那些让容器更听话的实战技巧
凌晨 3 点的告警
凌晨 3 点,监控大屏突然变红。磁盘使用率:100%。
连上服务器排查了一圈,发现罪魁祸首是一个才跑了 3 天的 Docker 容器——日志文件撑到了 40GB。再一看启动命令,docker run 后面只跟了 -d -p 8080:80 nginx,什么都没限制。
这大概是 Docker 使用中最常见的一种死法:命令写得简单,代价是上线之后不知道什么时候爆雷。
docker run 是 Docker 里最核心的命令,参数有七八十个。大多数人入门时只学了最基础那几个,剩下的要么从文档里零散查到,要么等踩坑了才去翻。这篇文章把这些参数按实战场景重新组织一遍,配合图表和避坑提示,争取让每一次敲下 docker run 时都有一个清醒的判断。
参数总览
先上一张总图,建立全局感:
场景一:给容器一个正经名字
--name
docker run -d --name my-nginx nginx
不指定名字时,Docker 会随机生成一个,比如 stoic_lovelace 或 vibrant_wozniak。查日志、进入容器、链接容器的时候,全靠猜,非常影响排查效率。
生产环境里,命名规范比想象中重要。建议统一格式,例如:${服务名}-${环境}-${序号},像 api-prod-01 这样,一眼能认出是谁。
--hostname
docker run -d --hostname app-server nginx
这只影响容器内部的 hostname,会写入 /etc/hostname。在微服务互相通信的场景里,hostname 能让日志的归属更清晰——一条来自 app-server 的报错比来自一串容器 ID 友好得多。
场景二:环境变量怎么传
-e / --env
docker run -e DB_HOST=localhost -e DB_PORT=5432 myapp
容器内进程通过 getenv() 或 os.environ 读取这些值。适合少量配置项。
注意:环境变量是明文传递的,不适合放密码。密码相关的内容走 Docker Secrets,或者挂载只读配置文件。
--env-file
docker run --env-file .env myapp
参数一多,逐个 -e 就显得很乱。.env 文件格式清晰,维护起来方便得多:
DB_HOST=localhost
DB_PORT=5432
APP_ENV=production
场景三:资源限制——不配这个迟早出事
先看一张资源限制体系图:
-m / --memory
docker run -m 512m myapp
限制容器最大使用 512MB 内存。单位支持 b、k、m、g。
突破内存上限时,容器进程会被 Linux OOM Killer 直接杀掉,进程退出码 137,属于非优雅终止。对于 Java、Node.js 这类预分配堆内存的应用,要留足够的余量,比如一个常驻 256MB 的服务,上限设到 512m 比较稳妥。
--memory-swap
docker run -m 512m --memory-swap 1g myapp
这里 --memory-swap 表示内存加 swap 的总上限,不是 swap 单独的大小。上例中物理内存 512MB,swap 可用 512MB,加起来 1GB。
如果设成和 -m 相同值,则 swap 完全禁用。
--cpus
docker run --cpus 1.5 myapp
容器最多能占用 1.5 个 CPU 核,属于绝对上限。可以是小数。
--cpu-shares
docker run --cpu-shares 512 myapp
这是相对权重,默认为 1024。当 CPU 资源紧张时,按权重比例分配空闲 CPU 时间。设成 512 意味着只有其他容器设默认值 1024 时的一半。
重要:CPU 空闲时不受限制,没有"只能用一半"的说法,所以这个参数在资源充足的机器上基本看不出效果。
场景四:网络怎么配通
--network
# 加入自定义网络(推荐)
docker run --network my-custom-net myapp
# 使用宿主机网络栈
docker run --network host myapp
# 无网络
docker run --network none myapp
自定义 bridge 网络下,容器之间可以直接用容器名做 DNS 解析,不需要记 IP。生产环境强烈推荐。
host 模式跳过 Docker 的网络 NAT 层,性能最好,但端口要自己管理——两个容器同时用 80 端口就直接冲突了。
--add-host
docker run --add-host db-server:192.168.1.100 myapp
往容器内的 /etc/hosts 添加一条静态映射。本地开发连内网数据库、又不想动系统 hosts 文件的时候,这个参数很顺手。
--dns
docker run --dns 8.8.8.8 --dns 114.114.114.114 myapp
覆盖容器内的 DNS 服务器。有些镜像默认 DNS 配置不合理,解析慢甚至超时,加上这个就能解决。
场景五:重启策略——别让服务器半夜 call 你
--restart
# 不自动重启(默认)
docker run --restart no myapp
# 异常退出时重启,可选重试上限
docker run --restart on-failure:5 myapp
# 总是重启(包括手动 stop 后)
docker run --restart always myapp
# 总是重启,但手动 stop 后不再自动拉起
docker run --restart unless-stopped myapp
unless-stopped 是生产环境最常用的选择。服务器重启后容器自动拉起,正常维护时运维手动停了容器不会被再次自动启动。always 适合那些宁可多重启也不能宕机的场景。
on-failure 常用于批处理任务:失败了自动重试几次,成功跑完就直接退出。
场景六:用户权限——别让容器以 root 身份乱跑
-u / --user
docker run -u 1000:1000 myapp
docker run -u nobody myapp
大多数镜像默认以 root 运行。生产环境这是大忌——容器逃逸漏洞一旦被利用,宿主机也跟着完蛋。
指定 UID/GID 运行,能在大多数场景下把风险降一档。如果挂载目录后报权限错误,通常就是容器进程的用户和宿主机目录的属主不匹配导致的。
--read-only
docker run --read-only myapp
把容器的根文件系统设为只读。进程写不进去,自然不会被植入恶意文件。应用如果需要写临时文件,配合 --tmpfs 给 /tmp 分配一块内存盘:
docker run --read-only --tmpfs /tmp myapp
场景七:挂载——数据放哪里
--mount(推荐写法)
-v 简单但参数顺序容易搞混。--mount 更显式,格式也更适合自动化:
# 绑定挂载宿主机目录
docker run --mount type=bind,source=/host/data,target=/app/data myapp
# 使用命名卷(Docker 管理存储)
docker run --mount type=volume,source=myvolume,target=/app/data myapp
# 内存临时文件系统
docker run --mount type=tmpfs,target=/tmp,tmpfs-size=100m myapp
# 只读挂载
docker run --mount type=bind,source=/config,target=/etc/app,readonly myapp
命名卷由 Docker 统一管理,备份和迁移都比较方便。绑定挂载直接暴露宿主机路径,权限控制更灵活。
--tmpfs
docker run --tmpfs /tmp:size=100m,mode=1777 myapp
tmpfs 放在内存里,容器停止后内容消失。适合存会话 token、缓存、临时文件这类不需要持久化的东西。速度比磁盘快很多。
场景八:日志——40GB 的教训
默认日志驱动是 json-file,存在 /var/lib/docker/containers/<container-id>/ 下,没有大小限制。上线三个月磁盘悄悄满了,根因往往就是这个。
docker run \
--log-driver json-file \
--log-opt max-size=10m \
--log-opt max-file=3 \
myapp
每个日志文件最大 10MB,保留 3 个文件,总共 30MB 上限。上线前加上这个,能省掉无数次的"磁盘满了"告警。
其他日志驱动:
| 驱动 | 说明 |
|---|---|
syslog |
转发到系统 syslog |
journald |
写入 systemd journal |
gelf |
发给 Graylog / ELK |
none |
完全不记录日志 |
场景九:交互——进入容器调试
-i / -t / -it
# 交互式 shell
docker run -it ubuntu bash
# 非交互执行一条命令
docker run ubuntu echo "hello"
-i 保持标准输入连接,-t 分配一个伪终端。需要敲命令交互就 -it,纯取输出不需要加。进了容器之后想退出但不杀进程,按 Ctrl+P+Q。
场景十:退出即删
--rm
docker run --rm ubuntu echo "done"
容器退出后自动删除,临时任务和 CI 流水线里很常用,省得手动的去清理。需要注意的是:--rm 和 --restart 互斥,不可能同时既是自动重启又是自动删除。
场景十一:安全加固四件套
--cap-add / --cap-drop
Linux Capabilities 把 root 权限拆成了几十个小块。容器默认只持有其中一部分,但不是最小集合。
# 允许绑定低端口(1024以下)
docker run --cap-add NET_BIND_SERVICE myapp
# 先全部去掉,再按需加回
docker run --cap-drop ALL --cap-add NET_BIND_SERVICE myapp
最小权限原则在这里体现得最直接。生产镜像建议第一步就加 --cap-drop ALL,然后只加真正需要的 capabilities。
常用 capabilities 一览:
| Capability | 作用 |
|---|---|
NET_BIND_SERVICE |
绑定 1024 以下端口 |
SYS_PTRACE |
进程调试 |
CHOWN |
修改文件所有者 |
SYS_ADMIN |
大量系统操作(慎用) |
--security-opt
# 自定义 seccomp profile
docker run --security-opt seccomp=/path/to/profile.json myapp
# 禁用 AppArmor
docker run --security-opt apparmor=unconfined myapp
Seccomp 和 AppArmor 是内核层面的沙箱,默认规则已经很严格。关掉它们往往是某些商业软件(比如杀毒软件)的容器版要求,能不动就不动。
--privileged
docker run --privileged myapp
特权模式,容器基本等于拿到了宿主机 root 权限。可以挂载设备、修改内核参数——风险极高。
真正用到的场景很少,Docker in Docker 是一个。普通业务服务绝对不要开这个,一旦容器被攻破,宿主机也跟着沦陷。
场景十二:健康检查
--health-cmd
进程在跑不等于应用正常。数据库连接池耗尽、API 卡死但进程没崩,这些情况 docker ps 只会显示进程状态正常。
docker run \
--health-cmd "curl -f http://localhost/ || exit 1" \
--health-interval 30s \
--health-timeout 5s \
--health-retries 3 \
--health-start-period 10s \
nginx
--health-start-period:留给应用启动的宽限期,冷启动慢的服务需要调大--health-interval:检查频率--health-retries:连续失败几次才标记为 unhealthy
这些参数也可以写在 Dockerfile 里 HEALTHCHECK 指令中,命令行参数会覆盖 Dockerfile 的设置。
场景十三:命令入口控制
--entrypoint 和 CMD 覆盖
# 覆盖 ENTRYPOINT,换成 shell
docker run --entrypoint /bin/sh myapp
# 覆盖 CMD 传入不同参数
docker run myapp --config /etc/myapp/custom.conf
镜像默认入口不适用当前场景时,这两个参数就能派上用场。比如用同一个镜像,测试环境传测试配置,生产环境传生产配置。
完整示例:生产级启动命令
把上面这些拼在一起:
docker run -d \
--name myapp \
--network app-net \
--hostname myapp-01 \
-p 8080:8080 \
-e APP_ENV=production \
--env-file .env \
-m 512m \
--cpus 1 \
--restart unless-stopped \
-u 1000:1000 \
--read-only \
--tmpfs /tmp:size=50m \
--mount type=volume,source=app-data,target=/app/data \
--log-driver json-file \
--log-opt max-size=10m \
--log-opt max-file=3 \
--cap-drop ALL \
--cap-add NET_BIND_SERVICE \
--health-cmd "curl -f http://localhost:8080/health || exit 1" \
--health-interval 30s \
--health-retries 3 \
myapp:latest
这一长串放到项目里肯定用 Docker Compose 管理更好,但逐条拆开看,能清楚知道每个参数解决的是什么问题。知道问题在哪里,Compose 文件里的 YAML 才写得出来。
快速参考表
| 参数 | 作用 | 常用值 |
|---|---|---|
--name |
容器命名 | 统一格式字符串 |
-e |
环境变量 | KEY=VALUE |
--env-file |
从文件注入环境变量 | .env |
-m |
内存上限 | 512m, 1g |
--memory-swap |
内存加 swap 上限 | 1g |
--cpus |
CPU 绝对上限 | 1, 1.5 |
--cpu-shares |
CPU 相对权重 | 512, 1024 |
--network |
网络模式 | bridge, host, 自定义 |
--add-host |
添加 hosts 映射 | name:IP |
--dns |
指定 DNS 服务器 | 8.8.8.8 |
--restart |
重启策略 | unless-stopped, on-failure |
-u |
运行用户 | UID:GID |
--read-only |
根文件系统只读 | 无值 |
--tmpfs |
内存临时文件系统 | /tmp:size=100m |
--mount |
显式挂载 | type=bind/volume/tmpfs |
--log-opt max-size |
日志文件大小 | 10m |
--log-opt max-file |
日志文件数量 | 3 |
--cap-drop ALL |
丢弃所有 capabilities | 无值 |
--cap-add |
添加指定 capability | NET_BIND_SERVICE |
--health-cmd |
健康检查命令 | shell 命令 |
--rm |
退出后自动删除 | 无值 |
小结
docker run 的参数不需要全记住,但需要知道两件事:哪些参数必须配,以及哪些坑埋在哪里。
必配清单:命名、网络、资源限制、日志大小、重启策略。这五样到位了,生产环境 80% 的问题能提前避免。
安全加固层面,优先把 --cap-drop ALL 和 -u 加上,--read-only 也值得推一把。--privileged 没特殊情况就不用碰。
下一篇文章是 「Docker + Nginx 反向代理 + HTTPS 全套配置实战」,从零把一个带 HTTPS 的反向代理完整跑起来,踩坑实录,敬请期待。
更多推荐





所有评论(0)