刚接触 Docker 的朋友,十有八九都踩过同一个坑:兴致勃勃地敲下一串启动命令,结果终端里瞬间刷出一大片红色的报错。要么是数据库直接拒绝连接,要么是端口被占用,容器“秒退”。别急着重装系统或者怀疑网络,这通常不是 Docker 坏了,而是容器里的“设置菜单”没对上号。咱们今天就顺着这个线索,把环境变量和
-e
参数掰开揉碎讲清楚,保证你看完就能自己把环境配得稳稳当当。
你可以把 Docker 容器想象成一个刚拆封的智能小助手。它肚子里装好了运行程序所需的一切工具,但开机后它不知道你是谁、想让它用什么密码、连哪个端口。这时候就需要你给它发一张“点菜清单”,这张清单在 Docker 里就叫环境变量。给小朋友打个比方:就像你告诉机器人“请穿红色外套,拿蓝色水杯”,机器人就会照做;如果你什么都不说,它就穿默认的灰色衣服。环境变量就是 Docker 容器的点菜清单,而
-e
参数就是你递清单的那只手。
命令行里使用
-e
的语法非常直白:
-e 变量名=变量值
。官方镜像的作者在设计镜像时,早就在 Dockerfile 里预留好了变量名。比如 MySQL 8.0 镜像,核心变量叫
MYSQL_ROOT_PASSWORD
;PostgreSQL 叫
POSTGRES_PASSWORD
;Nginx 一般不需要密码,但可以通过
NGINX_PORT
或类似变量调整行为。当你启动容器时,如果不主动传值,大多数安全型镜像会直接拒绝启动。这就是为什么你经常看到“Access denied”或者容器日志里明确写着
ERROR: XXX must be set!
。这时候补上对应的
-e
参数,问题往往迎刃而解。
docker run -d \
–name my-mysql \
-e MYSQL_ROOT_PASSWORD=MyStr0ngP@ss! \
-e MYSQL_DATABASE=my_app_db \
-p 3306:3306 \
mysql:8.0
上面这段命令里,
-e
出现了两次,分别负责设置 root 密码和创建初始数据库。注意变量名的大小写必须一字不差,Docker 对拼写极其敏感。
MYSQL_ROOT_PASSWORD
写成了
mysql_root_password
,容器就会当成未知变量直接忽略,然后因为密码为空而启动失败。很多新手在这里栽跟头,其实只需要多核对一次官方文档的 Variables 章节就能避免。
端口报错的逻辑稍微有点不同。“Address already in use”或者“Bind for 0.0.0.0:80 failed: port is already allocated”这类信息,说明宿主机上的某个端口已经被其他进程占用了。端口映射的左边是宿主机端口,右边是容器内部端口,中间用冒号隔开。比如
-p 8080:80
,意思是访问宿主机的 8080 端口,流量会被转发到容器内部的 80 端口。如果你之前已经跑过一个 Nginx 容器占用了 8080,第二次再用同样的端口就会直接冲突。解决起来也很简单:把左边的宿主机端口错开,比如改成
-p 8081:80
,或者彻底停用之前残留的容器
docker rm -f <旧容器名>
。有些镜像确实支持通过环境变量修改容器内的监听端口(例如
-e PORT=9000
),但这属于少数情况,大多数时候遵循
-p 宿主机:容器
的映射规则最稳妥。
当项目逐渐变大,命令行里塞满十几个
-e
参数会变得极其难以维护,改个密码还得重新敲一遍整段命令。这时候
.env
文件就是你的救星。在同一个目录下创建一个纯文本文件,按
KEY=VALUE
的格式把所有配置列出来:
# .env 文件示例
DB_ROOT_PASS=Sup3rS3cret!
APP_ENV=production
API_TIMEOUT=30
NGINX_LISTEN_PORT=80
然后在
docker-compose.yml
里直接引用这些变量,Docker 会在启动时自动把
.env
的值注入到容器中:
version: ‘3.8’
services:
db:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASS}
MYSQL_DATABASE: myapp
ports:
– “3306:3306”
volumes:
– db_data:/var/lib/mysql
web:
image: nginx:alpine
ports:
– “${NGINX_LISTEN_PORT}:80”
environment:
– APP_ENV=${APP_ENV}
– API_TIMEOUT=${API_TIMEOUT}
volumes:
db_data:
这样写的好处是配置集中、版本控制方便,而且一眼就能看出每个服务的依赖关系。记得把
.env
加入
.gitignore
,尤其是里面包含数据库密码的时候,安全永远是第一位的。
有时候变量明明传进去了,容器里却没生效,或者值被意外截断。怎么快速排查?进容器内部看一眼是最直接的:
docker exec -it <容器名或ID> env
这条命令会打印出容器内所有的环境变量列表。找到你传的键名,检查右边的值是否完整。如果发现值少了引号或者带了多余的空格,通常是 shell 解析顺序的问题。在 Linux/Mac 终端里,如果变量值包含特殊字符(如
!
、
$
、
&
),建议用单引号把整个赋值语句包起来,防止终端提前解释符号。例如:
-e ‘MYSQL_ROOT_PASSWORD=MyStr0ngP@ss!’
。Windows PowerShell 的转义规则略有不同,但原则一致:让 Docker 拿到干净的原始字符串。
还有一个容易被忽略的细节:变量覆盖顺序。Docker 解析环境变量的优先级是:
docker run -e
>
docker-compose.yml environment
>
.env 文件
> 镜像默认值。如果你在 Compose 里写了
environment: – DB_PASS=old
,又在命令行加了
-e DB_PASS=new
,最终生效的是命令行传进来的值。这种机制在调试时非常有用,你不需要改配置文件,临时覆盖一下就能快速验证某个变量是否真的在起作用。
把环境变量用熟练之后,你会发现部署不再是碰运气。无论是搭个人博客、跑微服务测试环境,还是给团队搭建统一的开发栈,掌握
-e
的传递逻辑和
.env
的管理习惯,能省去大量反复试错的时间。下次再看到红屏报错,先别慌,对照日志找关键词,检查变量名拼写,确认端口有没有撞车,顺着手把手把配置理顺。容器世界的大门就这样一步步打开了,慢慢来,你已经走在老手的路上。