
1. 先把 Docker 的参数逻辑理顺镜像、容器、守护进程三层关系在真正开始记 docker run 那一大堆--xxx之前我强烈建议你先明白一件事Docker 里几乎所有参数都不是在给命令加选项而是在给容器运行环境和进程启动方式写一份完整配置。docker CLI 只是客户端你敲的每一条命令都会发给 dockerd 守护进程由它调用 containerd、runc 等底层组件完成容器创建、资源隔离和文件系统挂载。这意味着命令行里的参数绝大多数会被组装成一份容器配置描述再交给运行时执行。这个视角一旦建立看到--cpus会想到 cgroup看到--network会想到网络命名空间理解成本会大幅下降记忆也不再是一堆孤立词条。1.1 参数最终落在内核的哪些机制上如果你把一次 docker run 拆开看所有参数大概落在四个层面镜像与构建层使用哪个镜像、哪个 tag、是否拉取、是否指定平台、是否覆盖入口容器运行时层容器名、主机名、DNS、网络模式、端口映射、数据卷挂载、rootfs 是否只读资源与安全层CPU、内存、PID、文件描述符限制、capability 增删、SELinux/AppArmor 配置、是否特权模式进程与环境层环境变量、工作目录、运行用户、主进程命令、健康检查命令、停止信号。这四层和 Linux 隔离机制严格对应网络命名空间对应网络参数mount 命名空间对应 volume 参数cgroup 对应资源限制用户命名空间和 capability 机制对应安全参数。所以你在宿主机上执行docker inspect看到的一堆 JSON其实就是这四层配置的落地结果。1.2 为什么同一个功能有多个写法Docker 的 CLI 演进过好几轮早期参数比较随意一个短字符经常承载多个含义后来为了工程化推出了更严格、更结构化的长参数格式。最典型的就是挂载卷有-v和--mount两种写法发布端口有-p和--publish。我的建议是查全量参数时优先理解长参数因为它语义明确还支持更多扩展子参数短参数适合命令行快速操作两者并不矛盾。比如-v适合临时调试--mount适合写进脚本或 compose 配置可读性完全不同。2. docker run 参数全拆解最常用的启动入口docker run 是使用频率最高、参数也最多的命令。它本质上是 docker create 加 docker start 的合并操作。它后面的所有参数几乎可以组合出任何你想要的容器运行方式。2.1 前台交互与后台运行-d、-it、--rm 的取舍先看三组最基础、也最容易被搞混的参数-d或--detach容器以后台守护方式运行启动后立即返回终端日志不会直接打到当前终端要用docker logs查看-i与-t-i表示保持标准输入打开-t表示给容器分配伪终端两者组合成-it用于需要交互的容器--rm容器退出时自动删除容器及其文件系统适合临时测试和-d不冲突。这里有个非常经典的坑你执行docker run -d centos会发现容器秒退。原因是 CentOS 这类系统镜像默认命令是bash没有-it分配终端bash 在非交互模式下读到 EOF 就退出主进程一结束容器生命周期就走完了。所以判断容器能否长期运行关键看主进程是否阻塞式地活着而不是看加没加-d。我举个实际例子。某次我在 CI 里写docker run --rm -d --name tempworker redis:7-alpine然后另一台机器连上去执行 redis-cli 一直失败。查日志发现容器确实在运行但 redis 默认没配密码监听地址也没对外开放。这种问题不是参数错误而是参数组合后的外部表现但确实是新手最容易在 run 阶段遇到的困惑。2.2 资源限制参数CPU、内存、IO 控制在生产环境不能省生产环境容器不可能裸奔资源限制必须设置。Docker 的 CPU、内存限制最终都会落到 cgroup 上--memory或-m限制容器最大可用内存例如--memory1g。注意它会联动限制 swap具体行为看--memory-swap--memory-swap表示 memoryswap 的总量。比如--memory1g --memory-swap1g表示不允许使用 swap--memory1g --memory-swap2g则允许额外使用 1g swap--cpus限制容器可使用的 CPU 核心数支持小数例如--cpus1.5--cpu-shares相对权重值默认 1024值越大在 CPU 争抢时获得的时间片比例越高但它不是绝对数量限制--pids-limit限制容器内 PID 数量防止某个应用 fork 失控耗尽宿主资源--ulimit设置容器内进程的资源限制比如--ulimit nofile65536:65536修改文件描述符数量。特别提醒--cpus不等于 CPU 亲和性它只限制 CPU 时间配额。--cpus1.5表示容器最多使用 1.5 个 CPU 核的总计算时间但具体跑在哪几个核上由内核调度决定。如果服务对延迟敏感需要绑定特定核心请用--cpuset-cpus。这两个参数经常被混为一谈。内存方面还有个容易踩的坑默认到上限后内核会触发 OOM要么回收、要么杀进程。常见做法是设置 JVM-Xmx时留出 Native Memory 和 Metaspace 的余量再用容器限制兜底。我见过太多容器完全没设内存限制某个线上服务一波动直接把整台宿主机打挂教训相当深刻。2.3 安全与特权参数--privileged、--cap-add、--security-opt 的边界容器不是虚拟机默认情况下容器内用户对设备、系统调用的访问受 Linux capabilities 机制限制Docker 会默认屏蔽 mount、sys_time、net_admin 等高危权限。常用参数如下--privileged相当于赋予容器几乎全部 capabilities同时放开 device cgroup 限制。能让容器随意挂载设备、操作网络、加载内核模块但隔离性急剧下降能不用尽量不用--cap-add单独增加某个 capability例如--cap-addNET_ADMIN容器内就可以配置 iptables 规则但不会获得其他权限--cap-drop移除默认赋予的 capability常用于安全加固比如--cap-dropALL先全部去掉再按需--cap-add--security-opt配置 SELinux 标签、AppArmor profile、no-new-privileges等后者能防止容器内进程通过 setuid 提权--device直接把宿主设备映射进容器--device-cgroup-rule用来动态放行设备规则比--privileged精细得多。我自己的运维习惯是能精确到 capability 就绝不用--privileged。比如某个日志采集容器需要读取块设备做统计我会把对应设备文件映射进去再按需加SYS_RAWIO之类能力。看起来麻烦但这是线上风险控制的基本功。这里还要纠正一个误解--privileged不是万能的有些操作涉及内核模块、系统启动参数等更深层的限制给了权限也未必成功。2.4 端口与网络参数-p、-P、--network、--network-alias、--add-host端口发布是最常见的需求几乎每个 Web 服务容器都要用到。常用参数拆解-p或--publish格式是[宿主机IP:]宿主机端口:容器端口/协议例如-p 8080:80、-p 127.0.0.1:8080:80/tcp。不指定协议时默认同时覆盖 tcp 和 udp多次使用-p可以发布多个端口-P或--publish-all把容器内通过 EXPOSE 声明的端口全部随机映射到宿主机高位端口配合docker port查看--expose只声明暴露哪些端口不实际发布到宿主机适用于同网络内容器间访问--network指定加入哪个网络常见有 bridge、host、none、overlay、macvlan以及用户自定义的 bridge 网络--network-alias容器在该网络内的额外别名多容器通过别名解析时很实用--add-host往容器/etc/hosts追加记录形如--add-hostmyhost:192.168.1.10。端口发布最大的坑是容器内服务只监听127.0.0.1时即使写了-p也会连接失败。比如很多框架默认 localhost 模式容器外完全进不去。排查时先进容器确认监听地址是不是0.0.0.0不要上来就怀疑端口映射写错。另一个容易忽略的点-p会帮你在宿主机 iptables/nftables 里加端口转发规则如果规则有冲突或系统ip_forward未开启映射会失败。我曾在网络隔离很严的主机上部署容器docker run一直提示端口绑定失败最后查出来是防火墙策略把转发禁掉了和 Docker 本身无关。2.5 环境变量与参数文件-e 和 --env-file容器内应用配置大多通过环境变量传入-e或--env直接设置例如-e TZAsia/Shanghai、-e MYSQL_ROOT_PASSWORDxxx--env-file从文件读取环境变量文件每行KEYVALUE支持注释--env的优先级高于--env-file--env-file不会自动解析引号里的特殊字符密码里如果包含空格、$、引号建议用编码或安全方式处理。环境变量是否生效完全取决于镜像的启动脚本怎么写不要想当然认为所有镜像都接受同一套变量。比如跑 MySQL镜像里的 docker-entrypoint.sh 会读取MYSQL_ROOT_PASSWORD、MYSQL_DATABASE这些变量跑 Redis则需要用命令行参数或配置文件方式设置密码环境变量并不能直接改 redis.conf。遇到变量不生效时第一反应应该是去看镜像的 entrypoint 脚本而不是反复改 Docker 参数。3. 镜像构建、拉取与传输参数从 build 到 save/load容器运行之外镜像相关的参数体系同样庞大。这节我们把拉取、构建、标记、传输这些环节的参数拆开讲清楚。3.1 docker pull 与多架构平台参数docker pull本身参数不多但有一个关键参数容易被忽视--platform指定拉取远程平台的镜像例如在 amd64 机器上临时拉取 arm64 镜像做交叉验证docker pull --platform linux/arm64 nginx:alpine--all-tags拉取某个仓库的全部 tag在镜像数量可控的私有仓库里很有用但公共镜像慎用会拉下海量数据--quiet或-q不打印进度信息脚本里比较干净。多架构镜像是当前镜像仓库的主流形态registry 会根据请求端的架构自动返回对应 manifest。但如果你需要在构建机上为其他平台准备镜像--platform就派上用场了。这个参数能帮你省掉大量临时换机器测试的时间。3.2 docker build 的上下文与缓存参数docker build的参数里最核心的有几个上下文路径命令尾部那个路径例如.Docker 会把该目录整体打包发给守护进程所以.dockerignore很重要。如果忘了排除node_modules、.git这类目录每次构建都会传输海量无效文件速度慢到怀疑人生-f或--file指定 Dockerfile 路径默认是./Dockerfile-t或--tag给镜像打标签可多次使用例如-t myapp:v1 -t myapp:latest--build-arg传入构建期变量格式--build-arg VERSION1.2对应 Dockerfile 里的ARG--no-cache禁用构建缓存排查缓存导致的旧依赖问题时非常有用--target多阶段构建中只构建到某个指定阶段。构建缓存的优化思路很重要。尽量按缓存友好顺序排列 Dockerfile先 COPY 依赖声明文件再 RUN 安装依赖最后 COPY 代码。这样代码变化时依赖层能命中缓存。很多初级开发者把 COPY 整个项目放在最前面改一行代码就要重新安装所有依赖这是很典型的构建效率陷阱。另外要注意ARG值变化会导致后续层缓存失效所以频繁变化的构建参数应该尽量往后放。多阶段构建配合--target可以只构建测试阶段或产物阶段在 CI 里能把构建时间压到很短。3.3 tag、push、save、load 的参数细节镜像的标记和传输也有一组固定套路docker tag给镜像打多个标签例如docker tag myapp:1.0 registry.example.com/myapp:1.0docker push推送镜像到仓库--quiet可以让输出干净一些docker save把镜像导出为 tar 包常用参数-o例如docker save -o nginx.tar nginx:alpinedocker load从 tar 包导入镜像常用参数-i例如docker load -i nginx.tardocker rmi删除镜像-f强制删除但要注意正在运行容器的镜像不能直接删docker prune清理未使用的镜像例如docker image prune -a删除所有未被容器引用的镜像。save和load在内网离线部署时几乎是必备技能。我在无外网环境里部署服务流程通常是这样先在联网机器上 pull 镜像再docker save -o导出scp 到目标机器然后docker load -i导入最后 docker run。这里容易犯的错是docker save -o导出的 tar 包含多个 tag 时load之后镜像名可能和预期不一致所以保存前最好先确认 tag 列表。4. 容器生命周期管理参数ps、exec、logs、inspect 与清理容器创建只是第一步后续的运维操作同样离不开参数。这一节把日常管理命令里值得注意的参数全部过一遍。4.1 docker ps 的过滤与格式化docker ps的参数看起来简单实际信息量很大-a查看所有状态的容器包括 Exited、Created-q只输出容器 ID脚本里批量处理非常常用比如docker rm -f $(docker ps -aq)清掉所有容器-s显示容器占用的磁盘总大小--filter或-f过滤容器常用写法有--filter statusexited、--filter ancestornginx、--filter nameweb、--filter labelxxx--format自定义输出Go template 语法例如docker ps --format table {{.Names}}\t{{.Status}}\t{{.Ports}}。实际运用中我最常用的是docker ps --filter statusexited找出已关闭容器再批量清理。但这里必须强调docker rm -f $(docker ps -aq)非常危险会删掉所有容器包括正在提供服务的容器。公司新同学在开发机敲过一次把自己的数据库容器删了当场心态崩了。另外 filter 里的 name 是前缀匹配不是完全匹配过滤web会把web1、webapp都匹配出来自动化脚本里要特别注意。4.2 docker exec 的参数docker exec是在运行中的容器里执行新进程语法和docker run后半部分很像-i保持标准输入打开-t分配伪终端和-i组成-it进入交互式 Shell-u指定容器内运行命令的用户比如-u root或--user 1000:1000-w指定工作目录-e给这次执行临时注入环境变量只在本次进程中生效--env-file同理临时生效。有个很容易被忽略的坑如果镜像的 Dockerfile 指定了USER app那docker exec默认也以 app 用户运行除非显式指定-u root。排查以非 root 身份启动的服务时一定要记得这个机制否则你看到的权限不足可能不是文件权限而是用户身份不对。4.3 docker logs 和 docker inspectdocker logs查看日志常用参数--tail指定最近行数例如--tail 100-f或--follow跟随输出--since/--until按时间过滤例如--since 5m、--since 2025-01-01T00:00:00--timestamps显示时间戳。docker inspect查看容器或镜像的底层配置输出是 JSON。配合--format做精简提取docker inspect --format {{.State.Pid}} 容器名获取主进程 PIDdocker inspect --format {{json .Mounts}} 容器名查看挂载详情常用字段包括.HostConfig资源限制、端口绑定、.NetworkSettingsIP、网关、.State运行状态、ExitCode、OOMKilled。这里有一个值得培养的习惯容器异常退出时不要只盯日志先跑docker inspect看State.OOMKilled。如果值为true说明是内存超限被内核杀掉日志里通常没有直接报错只会看到进程消失。我排查过某个 Java 服务频繁重启docker logs里只有 JVM 启动日志翻到最后一句戛然而止后来 inspect 一看OOMKilled: true立刻锁定是内存限制太小调大后问题消失。不懂 inspect 参数的话不知道要浪费多少时间在错误方向上。4.4 停止、删除与资源清理参数容器停止和删除也有各自的参数细节docker stop默认发 SIGTERM 给主进程等优雅退出宽限期默认 10 秒超时可用-t指定例如docker stop -t 30 容器名docker kill直接发 SIGKILL也可用--signal指定其他信号docker rm删除已停止容器-f强制删除运行中的容器-v同时删除关联的匿名卷这对数据清理很关键docker system prune清理未使用的镜像、容器、网络和构建缓存加-a --volumes会连未引用的数据卷一起清除务必谨慎。停止容器的信号机制容易被忽视。如果你的容器主进程是 shell 启动的shell 可能不会把 SIGTERM 转发给子进程导致优雅退出失败最后被强杀。这个问题的源头通常不在 docker 参数而在镜像的 ENTRYPOINT 写法后面 Dockerfile 部分会详细说。5. Dockerfile 编译期参数镜像不是靠命令堆出来的镜像构建是整个 Docker 体系中最需要理解参数含义的环节。很多人以为 Dockerfile 就是把 Linux 命令罗列一遍其实指令参数的选择直接决定镜像层数、缓存命中率、安全属性和最终运行时的进程行为。5.1 FROM、RUN、COPY、ADD 与构建上下文FROM基础镜像来源可加--platform指定平台例如FROM --platformlinux/amd64 alpine:3.19RUN执行构建命令两种写法Shell 形式RUN apt-get update apt-get install -y xxx和 Exec 形式RUN [apt-get,install,-y,xxx]COPY从构建上下文复制文件进镜像支持--from在多阶段构建里复制上一阶段产物ADD比 COPY 多了解压 tar 和识别远程 URL 的能力但行为隐式官方建议默认用 COPY只有明确需要解压时才用 ADD--chown复制文件时直接指定所有者例如COPY --chownapp:app app.jar /app/app.jar避免在镜像里多执行一次 chown 生成多余层。构建上下文之前提过再强调一层你运行docker build .时后面的路径就是上下文Docker 会把目录打包发送给守护进程。.dockerignore直接影响构建效率。如果一个项目没写.dockerignore每次构建传一堆日志、依赖、临时文件CI 慢到怀疑人生这不是 Docker 本身慢而是上下文太大。优化逻辑上RUN 命令按更新频率从小到大排列经常变化的 COPY 放在靠后位置能最大程度利用缓存。比如先COPY package.json再RUN npm install最后COPY . .这样代码改动时依赖层缓存依然命中。5.2 CMD 与 ENTRYPOINT 的两层设计容器启动的进程行为由 ENTRYPOINT 和 CMD 共同决定ENTRYPOINT定义主进程程序和固定参数通常不会被 docker run 后面的命令行覆盖但--entrypoint可以覆盖它CMD提供默认参数可以被 docker run 后面的命令行整体替换两者同时存在时CMD 的内容会追加到 ENTRYPOINT 后面作为默认参数。举个例子。假设 Dockerfile 写ENTRYPOINT [nginx]CMD [-g,daemon off;]那docker run mynginx实际执行的是nginx -g daemon off;。如果执行docker run mynginx -tCMD 被覆盖为[-t]最终进程是nginx -t用来测试配置。反过来docker run busybox echo hi里的echo hi会整体替换 CMD而不是 ENTRYPOINT。这里有个非常隐蔽的坑ENTRYPOINT nginx这种 Shell 写法会在容器内先启动一个 shell 再启动 nginxshell 收到 SIGTERM 时不会自动传给子进程docker stop无法优雅关闭容器只能等超时后被强杀。生产镜像建议统一使用 Exec 形式也就是 JSON 数组写法。这个问题我在排查服务优雅退出失效时碰到过好几次根源都在这里。5.3 ARG 与 ENV 的边界构建期和运行期ARG只存在于构建过程用--build-arg从外部传入对应docker build --build-arg VERSION1.2 .ENV既影响构建过程也写入镜像的运行时环境变量ENV变量不会自动传递到 RUN 的 Shell 子进程需要在同一 RUN 里显式使用或 export可以用 ENV 固化 ARGARG APP_VERSION1.0 ENV APP_VERSION$APP_VERSION这样构建期传入的版本号会固化到运行环境。还有个常见 bug--build-arg传参时必须先在 Dockerfile 里用ARG声明对应名字否则命令行里传了也会被忽略。很多人遇到--build-arg 不生效第一反应是命令写法问题其实是 Dockerfile 里根本没有声明。这个参数传递链路一定要记住。5.4 HEALTHCHECK、EXPOSE、STOPSIGNAL 等进程参数HEALTHCHECK定义健康检查方式重要参数有--interval检查间隔、--timeout超时、--start-period启动宽限期、--retries连续失败次数。检查结果可通过docker inspect查看EXPOSE只声明端口不发布端口如果镜像里没写 EXPOSE-P不会映射任何端口STOPSIGNAL设置容器停止时发送的信号默认 SIGTERM某些应用需要改成 SIGQUIT 等信号才能优雅退出WORKDIR设置工作目录影响 RUN、CMD、ENTRYPOINT、COPY、ADD 的默认路径USER指定最终进程的运行用户强烈建议生产镜像避免以 root 运行VOLUME声明匿名卷挂载点启动时即使没挂载也会创建匿名卷SHELL修改 Shell 形式指令的默认 Shell。特别提一下HEALTHCHECK的 start-period。很多人只设置检查间隔结果容器还在初始化数据库健康检查就连续失败调度系统判定不健康服务被过早杀掉。正确做法是给足够启动时间比如 MySQL 初始化可能要几十秒start-period 40s很常见。这个参数不解决应用启动慢的问题但能避免误杀。6. 数据卷与网络专项参数持久化、连通性与隔离运行状态之外容器最容易被忽略的两个领域就是卷和网络它们的参数直接影响数据安全和集群可访问性。6.1 -v 与 --mount两种写法与权限问题挂载卷有两种语法老式-v简洁源路径:目标路径[:模式]比如-v /data:/var/lib/mysql、-v myvol:/data命名卷、-v /host/path:/container/path:ro只读挂载。--mount是结构化写法采用typexxx,sourcexxx,targetxxx,readonly的键值对形式支持 bind、volume、tmpfs、npipe 类型。还能设置 bind-propagation例如--mount typebind,source/host,target/container,bind-propagationrshared。生产环境建议尽量用--mount语义清晰更适合写进脚本和维护。卷与权限是这里最大的坑。很多镜像内进程以非 root 用户运行比如 uid 1000但宿主机挂载目录属主是 root容器内进程无法写入。解决方式有两种一是创建目录后 chown 成与容器内用户一致的 uid二是用--user参数让容器进程以宿主机某用户运行。如果不想改宿主目录权限还可以用 SELinux 的:Z或:z标签。总之挂载卷的权限问题十有八九是 uid 不匹配docker exec进去执行id一看便知。6.2 网络驱动与自定义 subnet 参数Docker 默认提供 bridge、host、none 三种网络Swarm 模式还会用到 overlay 和 macvlanbridge默认模式容器通过虚拟网桥互通外部访问依赖端口映射host容器直接使用宿主网络栈性能和兼容性最好但没有网络隔离none容器没有网络接口适合纯离线计算任务macvlan让容器获得独立 MAC 地址从网络设备视角看就是一台真实主机overlay跨宿主机容器通信时使用主要在 Swarm 下用。docker network create常用参数--driver指定网络驱动--subnet指定 CIDR例如--subnet 172.20.0.0/16--gateway指定网关--ip-range指定动态分配范围--attachable允许非 swarm 容器连接。同一自定义 bridge 网络里的容器可以直接通过容器名互相访问这是 compose 项目里服务互相连通的基础。如果需要容器固定 IP必须在docker network create时指定 subnet再在docker run --ip里指定。这个参数组合在搭建跨主机通信的开发环境里非常常用。6.3 日志驱动与 log-optsDocker 日志系统通过 json-file、journald、syslog、fluentd、gelf 等驱动工作。docker run --log-driver指定日志驱动默认 json-file 会写本地文件长期运行可能占用大量磁盘。所以必须配置 log-opts--log-opt max-size10m单个日志文件大小上限--log-opt max-file3最多保留几个文件--log-opt modenon-blocking写入慢时容器不阻塞丢弃部分日志避免影响业务--log-opt tagcustom-{{.Name}}给日志对象打标识便于汇聚后过滤。这里特别提醒默认 json-file 驱动如果没有设置 max-size 和 max-file生产环境很容易出现日志堆积把磁盘撑爆。我处理过最典型的情况是接口服务一天输出 GB 级日志容器本身只有 2G 磁盘空间最后整台宿主机/var/lib/docker分区被写满所有容器全部故障。根源就是 run 时没给日志参数设限。所以凡是运维入口务必主动把日志大小限制在合理范围这比事后写清理脚本更靠谱。7. Docker Compose 的字段参数从单容器到服务编排单容器用 docker run 足够但一套完整服务通常包含多个容器应用、数据库、缓存、消息队列。这时用 Compose 描述服务之间的依赖和网络关系更合适。Compose 文件本质上是把 docker run 参数映射成 YAML 字段但多了编排层概念。7.1 services 关键字段与 docker run 参数的对应services 下常用字段image镜像对应docker pull run 的 image 部分build构建配置对应docker build包含 context、dockerfile、argscontainer_name显式指定容器名ports发布端口格式宿主机端口:容器端口也可以用 target/published/protocol 长写法expose只暴露端口给网络内其他服务environment环境变量类似docker run -e也支持env_filevolumes挂载配置可用短语法或长语法restart重启策略no、always、on-failure、unless-stoppedcommand覆盖镜像 CMDentrypoint覆盖镜像 ENTRYPOINThealthcheck健康检查字段和 Dockerfile HEALTHCHECK 一致depends_on服务启动顺序依赖deploy部署资源限制配置。新版 compose.yaml 已经不再推荐写version字段早期version: 3这种写法已经过时新项目不用再写。这里重点说depends_on如果只看字面以为它控制服务启动完成后再启动下游但早期版本只控制启动顺序不保证服务健康。正确的做法是配合 healthcheck 和condition: service_healthy下游服务才会真正等依赖就绪。7.2 .env 与变量插值的机制compose 支持在 .env 文件里定义变量并在 compose.yaml 中用${VARIABLE}做插值。例如数据库密码可以不再明文写在配置文件里而是通过 .env 引入.env 文件可以留在本地不进代码仓库既保结构又保密级隔离。这里有个容易混乱的坑.env文件里的变量和environment字段下的变量是两套机制。environment会直接传给容器.env只负责 compose.yaml 本身的插值。如果你在 .env 里写了MYSQL_ROOT_PASSWORD但 compose.yaml 的environment字段没有引用这个变量不会自动进入容器很多人在这里兜圈子。7.3 资源限制与重启策略的高频组合在 compose 中控制资源限制可以写deploy.resources.limits但在本地 docker compose 环境下部分字段不一定直接对容器生效。更稳妥的方式是使用 run 时的底层资源限制能力或者在 compose 里配置明确的配置。实际项目中我最常用的组合是restart: unless-stopped服务器重启后能自动把服务拉起来healthcheck设置合理 start-period避免误判volumes使用命名卷而不是宿主机路径便于备份和迁移networks显式指定自定义网络把不需要对外暴露的端口只绑定到127.0.0.1。特别想提醒不要把数据库放在容器层一旦容器被删除、升级或异常重建数据很可能跟着丢失。正确方案是命名卷或宿主机目录持久化。这个参数层面看似简单背后是容器设计里最核心的无状态与有状态分离原则。8. 高频报错与参数相关的排查记录最后这部分专门记录我在实际运维中遇到的几个典型问题它们都和参数理解不到位有关希望帮你节省排查时间。8.1 端口映射失败bind address already in use这个报错出现时很多人第一反应是端口被占用但实际上一半以上情况不是真正的进程占用而是 docker-proxy 仍在运行或 iptables 规则残留。处理步骤一般是docker ps看是否已有容器占用了端口lsof -i:8080或ss -tlnp确认宿主端口占用者如果确认规则残留谨慎重启 docker 服务恢复。还有一小类场景是容器内服务监听127.0.0.1即使-p映射关系正确外部访问仍然失败因为流量到达容器网卡后应用没监听容器 IP。排查思路是进容器同时测试curl 127.0.0.1:端口和curl 容器IP:端口基本一测便知。8.2 容器启动后退出Restart (0) 或 Restarting看到容器一直 Restarting第一反应用docker logs看日志方向是对的。但也别忽略docker inspect里State.ExitCode和State.ErrorExitCode127通常是命令不存在比如镜像里没有 bash但 entrypoint 写的是 bash。Alpine 镜像默认是 ash临时验证可以改用/bin/sh或安装 bashExitCode137或143进程被 SIGKILL/SIGTERM 杀死和资源限制、stop 超时、OOM 有关ExitCode139段错误常见于架构不匹配比如 amd64 宿主上跑 arm64 镜像这时要检查--platform参数和镜像 tag。8.3 权限不足operation not permitted 与 cannot mkdir这类报错多半是 SELinux 或 capabilities 问题。如果宿主机开启 SELinux挂载卷后容器内进程访问宿主目录被拒绝报错可能只是operation not permitted。解决方式要么给目录做好 SELinux 标签要么挂载时加:z或:Z。另一个常见操作是容器内执行 mount、iptables 时被拒绝一般是因为容器默认缺少相关 capabilities。解决办法不是直接--privileged而是按需加--cap-add比如需要 iptables 就--cap-addNET_ADMIN。长期维护下来按需加权限既能保住隔离边界也方便未来安全审计。8.4 磁盘被 /var/lib/docker 占满这个问题在上文日志参数里提过再补充清理思路docker system prune可以清理未引用的镜像、容器、网络和构建缓存但加-a --volumes时一定要谨慎因为它会连所有未使用容器引用的数据卷一起删。稳妥流程是先用docker ps -a确认没有需要保留的容器再分开执行docker image prune和docker volume prune。如果磁盘已经写满Docker 守护进程可能已经无法正常工作需要先腾出空间再恢复服务。平时就应通过 log-opts 和定期清理策略把风险窗口控制在最小。这篇笔记基本覆盖了我日常运维和开发中使用 Docker 的绝大多数参数场景。个人体会是参数本身只是表象真正值得琢磨的是参数背后的资源隔离、文件挂载、进程模型这三个核心逻辑只要把这三块想明白碰到陌生参数时也能马上猜到它属于哪个层面、会带来什么影响。最后分享一个小习惯每学一个新参数我都会用docker inspect去看这个容器创建后的实际配置等于把参数翻译成可观测的底层状态这个反查过程比背十遍命令都管用。