ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Docker常用镜像启动参数对照表:docker run端口、数据卷与环境变量详解

Docker常用镜像启动参数对照表:docker run端口、数据卷与环境变量详解 一条在网上抄来的 docker run 命令容器起来了应用却连不上或者干脆卡在启动阶段反复报错这种场景我见过太多次。Docker 常用镜像启动参数的坑说大不大说小不小但每次都能把人卡到怀疑人生。官方文档写得很全可日常使用中大多数人根本不翻文档而是打开搜索引擎复制一条看着差不多的命令改个名字就往上跑。结果就是MySQL 拉下来已经是 8.x初始化参数却还是 5.7 时代的写法Redis 主从复制怎么配都不认GitLab 起来之后内存直接拉满整台机器鼠标都挪不动。这不是 Docker 的锅也不是镜像的锅问题出在我们对启动参数的理解是碎片化的。所以这篇东西不打算讲高深原理就做一件事把常用镜像的启动参数整理成一张能直接照着抄的对照表并且把每个参数到底在干什么、为什么这么写、哪些地方容易踩坑全部讲透。这篇内容更适合个人开发者、小团队内部环境搭建以及刚开始把服务迁到容器里的同学。生产环境里你还要考虑 K8s、资源限额、编排平台但理解了 docker run 这套参数设计换到哪一层都不会懵。1. 为什么需要一张能真正跑起来的启动参数对照表先说明一下网上不是没有命令大全但很多命令存在三种硬伤。一是版本不对作者写博客的时候 MySQL 还是 5.7现在拉镜像默认可能是 8.0 甚至 8.4参数和初始化逻辑早就变了。二是缺少关键参数比如数据卷没挂、时区没指定、初始化环境变量没给容器能启动但根本没法正常用。三是把短期调试参数当长期运行参数来用比如临时测试用的--rm被用在服务上重启一次数据就没了哭都来不及。我见过最典型的案例是有人照着旧教程启动 MySQL容器状态一直显示 healthy但他用客户端从宿主机连 3306 永远超时。一查发现教程里根本没有-p 宿主机端口:容器端口容器网络默认桥接3306 只暴露在 Docker 内部网络里宿主机外面根本摸不到。这就是参数缺位的典型后果也是网上那些命令看着对、跑起来错的核心原因。所以这张对照表的第一个作用是帮你把能跑起来和跑得对之间的差距填上。第二个作用是给你一个参照系让你修改某个镜像的参数时至少知道该动哪个位置、动了会有什么影响。我不会把重点放在罗列所有镜像的所有参数上那没有意义真正的核心是选常用、高频、典型的那几个把参数逻辑拆开讲透。下面每个镜像的启动命令我都按当前主流的版本习惯来写你直接复制改改密码和端口就能用。2. 先拆开一条 docker run参数到底在配置什么在给每个镜像列参数之前得先弄清 docker run 后面那一长串东西分别是什么。很多人记参数完全是死记硬背一旦镜像换了就不知道怎么改。其实一套容器启动参数背后就四件事镜像是谁、端口怎么暴露、数据放在哪、初始状态和环境怎么交代。把这四件事想清楚任何镜像的启动命令你都能自己拼出来。2.1 镜像名和标签latest 的隐藏风险docker run的第一个参数是镜像名也就是仓库名/镜像名:标签。很多新手容易忽略标签直接写mysql这时 Docker 默认拉取latest标签。latest 本身不是一个特殊版本只是维护者打上去的一个标签时间一长它指向的版本谁也不知道。我建议所有镜像都显式写版本标签比如mysql:8.0、redis:7、nginx:stable。确定版本号还有个额外好处是排障时可以复现。同一个镜像不同版本的启动参数兼容性差异很大比如 MySQL 从 5.7 到 8.0默认认证插件从 mysql_native_password 变成了 caching_sha2_password你如果拿着 5.7 的连接配置去连 8.0客户端版本旧一点就直接报认证失败。写上明确的标签至少保证你是用同一个版本在排查问题而不是在一个薛定谔的版本上浪费时间。2.2 端口映射-p 的写法决定了谁能访问-p是最容易出错的参数。它的完整格式是-p 宿主机地址:宿主机端口:容器端口比如-p 127.0.0.1:3306:3306。如果只写-p 3306:3306就等价于0.0.0.0:3306:3306宿主机所有网卡上的 3306 都会被映射进去。本地开发数据库如果不想暴露到局域网我会习惯写成127.0.0.1:3306:3306这样只有本机能连。另外要注意宿主机端口冲突。本地 3306 已经被原生 MySQL 占了容器再映射 3306 就会启动失败这时候要么改宿主侧端口-p 3307:3306要么停掉本机的原生服务。容器内部的应用端口保持不变改的永远是冒号左边这个规则对任何镜像都适用。2.3 数据卷容器可以删数据必须留容器本身是一次性的镜像删了重建都很正常但数据不能跟着没。数据卷有三种常见用法对应不同场景命名卷-v mysql-data:/var/lib/mysql由 Docker 管理目录位置不固定但迁移方便。路径挂载-v /data/mysql:/var/lib/mysql数据直接落在宿主机指定目录方便查看和备份。匿名卷-v /var/lib/mysql只有容器内路径用完不好找位置日常使用不太推荐。挂到哪个路径是镜像决定的不能凭感觉猜。比如 MySQL 数据目录是/var/lib/mysqlPostgreSQL 是/var/lib/postgresql/data。路径挂错的结果是容器起来了写的数据根本不在你挂的目录里或者目录权限不对数据库起不来。这个坑我在下面具体镜像部分会再展开。2.4 环境变量与重启策略容器启动时的初始状态-e环境变量是镜像对外暴露的配置入口。同一个镜像在不同场景下行为不同靠的就是环境变量。比如 MySQL 初始化 root 密码用的MYSQL_ROOT_PASSWORDPostgreSQL 创建用户用的POSTGRES_USER。注意环境变量只在首次初始化时生效如果数据卷里已经有初始化过的数据再改环境变量不会重设密码这是很多人改密码不生效的原因所在。重启策略--restart是容器服务化的关键。unless-stopped表示除非手动 stop否则开机和异常退出都会自动拉起适合长期服务always更激进Docker 重启后一定会拉起no是默认值适合调试用的一次性任务。调试临时容器时还可以加--rm容器停止后自动删除避免机器上堆满死容器。长期服务一定记得选unless-stopped不然宿主机一重启服务全没了。3. 数据库类镜像启动参数MySQL、Redis、PostgreSQL数据库是容器化里的重头戏也是启动参数最容易出事的类别。下面这三款我分别给出完整命令和逐项参数解释如果你只打算看一部分建议优先看自己正在用的那款再把其他两款扫一眼因为数据库镜像的参数设计思路是相通的。3.1 MySQL 8.0初始化参数、字符集与远程访问以 MySQL 8.0 为例一条可用的启动命令大概是docker run -d \ --name mysql8 \ -p 3306:3306 \ -e MYSQL_ROOT_PASSWORDYourPass123 \ -e TZAsia/Shanghai \ -v mysql-data:/var/lib/mysql \ mysql:8.0 \ --character-set-serverutf8mb4 \ --collation-serverutf8mb4_unicode_ci这条命令里有几个点值得展开讲。第一镜像名后面的--character-set-server和--collation-server不是 docker run 的参数而是传给容器内 mysqld 的额外启动参数docker run 会把镜像名之后的所有内容原样传给镜像的默认启动脚本。MySQL 官方镜像支持这种写法所有 mysqld 选项都可以这样透传。第二TZAsia/Shanghai这个环境变量很多人不写结果数据库里的NOW()和宿主机的当前时间差 8 小时排查日志特别折磨人。建议数据库、中间件、应用镜像统一把时区写进去这是花的力气最少、收益最高的参数。第三MYSQL_ROOT_PASSWORD只在数据卷为空、第一次初始化时生效。如果之前已经在同一个命名卷上启动过你再改密码环境变量是没用的。要么删掉卷重建要么进容器用ALTER USER修改。很多人以为改了-e里的密码就能重置结果连不上就开始怀疑一切其实问题就出在这个只生效一次的机制上。CREATE USER app% IDENTIFIED BY AppPass123; GRANT ALL PRIVILEGES ON *.* TO app%; FLUSH PRIVILEGES;第四远程访问要注意两件事。一是确认端口映射写的是0.0.0.0或具体宿主机 IP而不是只映射到 Docker 内部网络二是 MySQL 8.0 默认 root 用户只允许 localhost 登录你从宿主机或局域网连的时候会直接报Access denied。这时需要手动创建一个远程访问用户或者把 root 的 host 改成%上面的 SQL 就是干这个用的。我遇到过不少容器正常、端口也映射了、就是连不上的案例一大半是卡在 MySQL 用户授权这里。3.2 Redis从单机到主从的启动参数变化Redis 的启动命令比 MySQL 简单但主从复制的参数细节很值得单独说。先看单机版docker run -d \ --name redis-server \ -p 6379:6379 \ -v redis-data:/data \ redis:7 \ redis-server --appendonly yes --requirepass YourPassredis-server后面跟的是 Redis 自身的启动参数--appendonly yes开启 AOF 持久化数据默认写入容器的/data目录这和挂载的redis-data正好对应。--requirepass设置访问密码不写的话 Redis 默认无密码公网映射出去很容易被扫描器盯上几分钟内就会被恶意写入这个真不是危言耸听。再看主从复制。同主机上搭建一主一从我建议先建一个自定义网络让容器之间可以通过名称互相解析而不是去查容器 IP 再手动写死docker network create redis-net docker run -d \ --name redis-master \ --network redis-net \ -p 6379:6379 \ -v redis-master-data:/data \ redis:7 \ redis-server --appendonly yes --requirepass masterpass docker run -d \ --name redis-slave \ --network redis-net \ -p 6380:6379 \ -v redis-slave-data:/data \ redis:7 \ redis-server --appendonly yes --requirepass slavepass \ --replicaof redis-master 6379 \ --masterauth masterpass注意几个关键点。从库的--replicaof后面写的是容器名:容器端口不是宿主机端口。因为两个容器在同一个自定义网络中Docker 内置 DNS 会把redis-master解析成它的容器 IP。从库自己设了密码时连接主库要配--masterauth否则复制连接会被主库的密码挡在外面。主从起来后用这条命令确认复制链路状态docker exec -it redis-slave redis-cli -a slavepass info replication输出里找到master_link_status:up就代表主从已经连通。如果显示down优先检查--replicaof的地址对不对、--masterauth的密码是不是主库的--requirepass这两处是我踩过最多的点。3.3 PostgreSQL一条命令完成用户、密码、库的初始化PostgreSQL 官方镜像把初始化需要的信息都做成了环境变量一条命令能同时把业务用户、密码、数据库全建好docker run -d \ --name pg16 \ -p 5432:5432 \ -e POSTGRES_USERappuser \ -e POSTGRES_PASSWORDapppass \ -e POSTGRES_DBappdb \ -v pg-data:/var/lib/postgresql/data \ postgres:16这里POSTGRES_USER会创建一个超级用户POSTGRES_PASSWORD设置该用户密码POSTGRES_DB会自动创建对应的数据库。如果只写POSTGRES_PASSWORD不写用户名默认创建postgres超级用户。初次连接时直接用appuser因为它已经是超级用户开发环境完全够用。PostgreSQL 的一个常见坑是数据卷权限。官方镜像的 postgres 进程默认以 uid 999 运行如果你用路径挂载到宿主机目录目录所有者不是 999容器会直接报could not create directory之类的权限错误。解决方法是先把目录权限改给 999或者用命名卷让 Docker 自己管。这也是为什么我在命令里默认用命名卷它能省掉一大半权限问题的排查时间。4. 应用服务类镜像启动参数Nginx、GitLab、青龙面板、系统镜像数据库讲完再来看看应用服务。这一类镜像的共性是配置从哪来和资源怎么控启动参数的设计逻辑也因此各有侧重。4.1 Nginx静态站点与反向代理的挂载方式Nginx 镜像的启动参数核心是两处挂载站点文件挂到/usr/share/nginx/html配置文件挂到/etc/nginx/conf.ddocker run -d \ --name nginx-web \ -p 8080:80 \ -v /data/www:/usr/share/nginx/html:ro \ -v /data/nginx/conf.d:/etc/nginx/conf.d:ro \ nginx:stable挂载时加:ro只读是个好习惯。宿主机上的文件更新后Nginx 会自动读取文件系统里的新内容静态文件不需要重启容器就能生效改配置文件之后执行docker exec nginx-web nginx -t先做语法检查再docker exec nginx-web nginx -s reload平滑重载不用把整个容器重启一遍。有人会把 Nginx 的官方默认配置删掉然后自己写一份nginx.conf挂到/etc/nginx/nginx.conf这样也不是不行但挂载conf.d目录更简洁不会影响官方镜像内置的主配置结构。反向代理时proxy_pass指向同网络里的其他容器名称比如http://app-server:8080前提是 Nginx 容器和应用容器在同一个自定义网络里。跨网络访问时Nginx 默认解析的是容器 IP网络一变 IP 就变这是Nginx 突然 502的常见原因之一。4.2 GitLab内存瓶颈与必须调整的启动参数GitLab 是出了名的贪吃镜像官方自己都在文档里强调要调整/dev/shm大小。我见过太多人直接docker run -d gitlab/gitlab-ce然后发现机器卡死容器虽然起来了但 Web 页面打开极慢跑一段时间直接被系统 OOM 杀掉。可用的基础命令大概是docker run -d \ --name gitlab \ --restart unless-stopped \ -p 8082:80 -p 8443:443 \ --shm-size 256m \ -v gitlab-etc:/etc/gitlab \ -v gitlab-log:/var/log/gitlab \ -v gitlab-data:/var/opt/gitlab \ -e GITLAB_ROOT_PASSWORDYourGitLabPass \ gitlab/gitlab-ce:latest--shm-size 256m是必然会写进任何一份 GitLab 启动命令里的参数。GitLab 用到大量共享内存默认的 64MB 很容易不够用导致重启、卡顿、后台任务失败。GITLAB_ROOT_PASSWORD用来预设管理员root的密码。这里有个坑如果忘了设置而容器首次初始化已经完成环境变量就不会生效了需要进容器执行gitlab-rails runner来重置密码。GitLab 第一次启动非常慢初始化可能需要几分钟到十几分钟看到日志没有新增内容也别急着断言失败用docker logs -f gitlab观察是否打印出gitlab Reconfigured之类的完成标记。想省内存的话可以通过挂载自定义/etc/gitlab/gitlab.rb关闭几个不用的组件比如prometheus[enable] false但这属于另一个话题了先用上面的命令跑起来再说。4.3 青龙面板数据目录、端口与依赖管理青龙面板在自动化任务圈子里很常见它的启动参数不算复杂但数据目录一定要单独挂出来docker run -d \ --name ql \ --restart unless-stopped \ -p 5700:5700 \ -v ql-data:/ql/data \ whyour/qinglong:latest面板跑起来之后访问http://宿主机IP:5700就能看到初始化页面。首次登录后第一件事是检查容器内的脚本环境依赖。很多人面板起来了任务却一直在报缺少依赖就是因为镜像内置的依赖不全需要进入容器手动补装。常见操作是用docker exec -it ql bash进入容器然后在容器内执行面板自身的依赖管理命令来安装 Node.js 或 Python 相关组件。依赖管理也是这类容器最容易出问题的地方因为任务脚本的生态变化很快镜像版本和脚本要求的运行时版本经常对不上。/ql/data这个目录里全是配置和数据库文件不挂载的话容器一删全部配置清零脚本要重新配一遍这个教训很痛。另外 5700 是面板的默认端口如果本机被占改-p左侧的宿主端口就行容器内部端口不要动。4.4 Ubuntu / CentOS 系统镜像临时环境与构建基座系统镜像的启动参数思路和上面完全不一样它们往往不是跑一个常驻服务而是作为临时排障环境或构建阶段的基础。最典型的两条命令docker run -it --rm --name ubuntu-test ubuntu:22.04 bashdocker run -it --rm --hostname centos-dev centos:7 bash-it分配交互式终端--rm退出即删除容器最适合临时验证命令、测试脚本、检查某个包的行为。--hostname可以指定容器内主机名某些环境对主机名敏感比如 CentOS 里和 systemd 相关的操作。系统镜像是容器世界里最容易被忽略的一类但它恰恰是最适合玩坏就丢的试验田我建议所有新手都拿它练手。不过要提醒一句想在容器里跑 systemd 来模拟完整操作系统环境会遇到很多问题比如 init 进程不工作、权限不够通常需要加--privileged之类的特殊参数但这会削弱容器隔离性。我的建议是系统镜像容器更适合做干净的试验台不适合硬当作虚拟机用。真要模拟完整操作系统老老实实用虚拟机。5. 参数照抄了还是起不来排障链路与关键操作参数写对了容器依然可能起不来。排障不能靠猜要有一条固定的排查链路下面这套顺序是我日常用的省时间、覆盖面广。5.1 排查顺序docker logs 到 docker inspect容器起不来或起来后行为异常我建议按这个顺序查docker ps -a看容器状态区分Exited、Restarting、Up。docker logs 容器名看启动日志大多数初始化错误都会在这里留下明确信息。docker inspect 容器名看实际配置确认端口映射、数据卷、环境变量是否真的挂上了。docker exec -it 容器名 命令进容器实测应用本身是否正常。很多问题其实在第二步就已经暴露了比如 MySQL 报权限错误、PostgreSQL 报目录不可写。但有时候容器状态是Up日志也正常就是连不上那就得用docker inspect验证网络配置。比如我见过有人以为-p 3306:3306写进去了实际用的却是--network host端口映射参数在这种网络模式下根本没有意义这一步不查后面猜半天都是白费。5.2 端口映射正常却连不上从防火墙和绑定地址入手端口映射检查了三遍都对宿主机上还是连不上这时候要往 Docker 外找。最常见的拦截来自宿主机本身的防火墙Linux 上可以是firewalld、ufw也可能是云服务器安全组规则。端口映射只负责把流量引到 Docker 的网络栈宿主机层面的拦截它管不着。另外注意-p左侧不要写错绑定地址。-p 127.0.0.1:3306:3306只允许本机访问你在另一台机器上访问自然会失败。想对外提供服务要么显式写宿主机 IP要么写0.0.0.0并配合防火墙规则来控制访问范围。我在测试环境图省事会直接写0.0.0.0但生产环境宁可多写几步安全策略也不会把这个口子开成全网可连。5.3 数据卷权限引发的启动失败数据卷权限出错时报错信息往往很直白但也容易被忽略。比如宿主目录是 root 所有容器进程以普通用户运行写不进去就退出。MySQL、PostgreSQL 这类官方镜像对数据目录的所有者要求很严格。处理方式有几种。一是改宿主目录所有者让它匹配容器内运行用户的 uid比如 PostgreSQL 的 999二是直接用命名卷让 Docker 管理权限避免错位三是在支持 SELinux 的环境里挂载时加上:Z或:z标签。需要说明的是-v挂载的目录不具备正确 SELinux 上下文时SELinux 会拒绝容器写入这不是权限数字的问题而是安全上下文的问题。国内用 CentOS 当宿主机的人常遇到这个改之前先确认宿主机有没有开启 SELinux。5.4 Docker Desktop 在 Windows 上启动失败的常见原因virtualization support not detected这个报错出现的频率非常高。Docker Desktop 在 Windows 上依赖虚拟化支持报这个错通常意味着两件事BIOS/固件里没开启虚拟化或者 Windows 的 Hyper-V/WHPX 功能没启用。排查顺序也很固定先进 BIOS 确认 CPU 虚拟化开关处于开启状态然后在 Windows 可选功能里启用虚拟机平台和适用于 Linux 的 Windows 子系统最后重启电脑再试。如果公司电脑有策略锁定了 Hyper-V那就要找管理员解决命令行里怎么折腾都没用。这个报错本身和 Docker 的启动参数无关但很多人在这一步就卡住了所以排障时不要只盯着容器配置。5.5 镜像拉取超时配置镜像加速与重试策略拉取镜像慢或者超时在 Docker 使用里几乎必然遇到。稳妥的办法是给 Docker 配置镜像加速原理是让 Docker 从指定的 Registry 镜像站拉取内容。配置文件在 Linux 下是/etc/docker/daemon.jsonWindows 上可以在 Docker Desktop 的 Docker Engine 设置里直接改{ registry-mirrors: [ https://docker.m.daocloud.io ] }改完重启 Docker 再重新拉取。如果拉取中断重试时尽量保持docker pull命令不变不要反复换 tag因为 Docker 会对已下载的镜像层做缓存换 tag 反而会丢掉进度。临时需要某个容器但镜像拉不下来时可以换一个架构相同的其他可用镜像顶替但那只适合调试不适合作为长期方案。6. 从照着抄到自己改四个值得养成的参数设计习惯对照表能给一时给不了一世。镜像更新、场景变化、迁移到编排平台都会让你不得不自己改参数。下面这四个习惯是我踩过不少坑之后总结出来的值得慢慢养成。6.1 动手前先写一行意图注释启动一条 MySQL 容器前先在命令上方或旁边写清楚这条容器是干什么用的、数据放在哪、数据是否可以丢。这个习惯帮你把命令分成两类一次性调试和长期服务。调试用--rm服务用--restart unless-stopped两种命令的形态完全不同。我在本地写测试脚本时经常一条docker run -it --rm回车就跑完一个实验而不会创建一个永远留着不删的服务容器不然/var/lib/docker里会堆一堆不知道干嘛的容器。6.2 用 docker inspect 验证实际配置写完命令不代表配置生效。最直接的办法是docker inspect后查看Mounts、NetworkSettings.Ports、Config.Env三个字段确认数据卷路径、端口映射、环境变量是不是自己设想的样子。有时候命令里写了-e MYSQL_PASSWORD...但镜像根本不认识这个变量外表看起来没问题实际初始化时没生效这类命令看起来对的问题只有 inspect 才能发现。我把这个当成启动容器后的默认动作验证一次花不了十秒钟省下的却是几小时的排障时间。6.3 记录上次能跑的完整命令我自己的习惯是每个项目的启动命令都会存成 shell 脚本或者 README 片段标注日期、镜像版本、宿主端口甚至是当初为什么这样配置。镜像版本更新后我会先对比旧命令再下手不能一把梭直接拉最新版跑。这也是为什么我前面反复强调写明确版本标签没有标签就无法还原上次能跑的环境。生产环境尤其如此能跑的状态必须可以被精确记录和回放参数设计也是一样的思路。6.4 从 docker run 平滑过渡到 docker compose当你发现某个环境里有三四个关联容器要一起启动时docker run 的劣势就出来了每条命令都要重复写网络、端口、数据卷而且容易漏配置。这时候把命令转成 docker compose 是自然的下一步。逻辑上其实就是把 docker run 的参数搬进docker-compose.yml-p对应ports-e对应environment-v对应volumes--restart unless-stopped对应restart自定义网络对应顶层networks之前 Redis 主从的例子用 docker compose 写会比两条 docker run 直观很多服务间依赖关系也能一眼看清。但我自己依然保留着一张docker run 参数怎么映射到 compose 字段的对照表因为迁移到 K8s 时Pod 里的 initContainer、volume、env 也还是这套底层概念的变体。参数永远在变容器化的思维模型是不变的。以上是我这次想分享的全部内容。一张对照表真正发挥作用的前提是你能解释清楚每个参数为什么出现。建议你把上面这些镜像挨个跑一遍改一个参数观察一次现象MySQL 改密码、Redis 断主从、Nginx 改代理目标都试过了之后再回到这张表就会发现它已经属于你了。
返回列表