一条docker run启动Nginx,一条docker run启动MySQL------这篇搞懂容器运行、管理和排障的核心操作,顺带避开"容器跑着跑着就退了"这个初学者必踩的坑。
docker run------从一条命令说起
docker run是Docker的核心命令,作用是把镜像变成正在运行的容器。先看最简单的用法:
bash
# 最简形式:拉取nginx镜像并启动
docker run nginx:1.27
# 但这样运行有个问题------容器占着你的终端
# 加 -d 让它后台运行
docker run -d nginx:1.27
按场景掌握参数
docker run有几十个参数,但日常常用的就这几个,按场景比较容易记:
| 场景 | 参数 | 作用 | 示例 |
|---|---|---|---|
| 后台运行 | -d |
容器在后台跑,不占终端 | docker run -d nginx:1.27 |
| 给容器起名 | --name |
方便管理,不然得记ID | docker run --name myweb nginx:1.27 |
| 端口映射 | -p 宿主机:容器 |
外部访问容器内的服务 | docker run -p 8080:80 nginx:1.27 |
| 环境变量 | -e KEY=VALUE |
向容器传配置 | docker run -e MYSQL_ROOT_PASSWORD=secret mysql:8.4 |
| 交互式终端 | -it |
进入容器内操作 | docker run -it ubuntu:24.04 bash |
| 自动清理 | --rm |
容器停止后自动删除 | docker run --rm hello-world |
| 重启策略 | --restart |
容器挂了自动重启 | docker run --restart unless-stopped nginx:1.27 |
💡
-v数据卷挂载和--network网络配置也很常用,但内容较多,咱们后面再深入讲解。

镜像名和标签
bash
# ❌ 禁止:不指定标签,默认用latest
docker run nginx
# ✅ 正确:明确指定版本号
docker run nginx:1.27
docker run mysql:8.4
docker run redis:8.0-alpine
latest不等于"最新稳定版"------它只是一个标签名,镜像维护者想指向哪个版本就指向哪个版本。某天latest从8.4跳到9.0,你的环境就炸了。永远明确指定版本号。
容器生命周期------主进程活着容器就活着
理解容器的生命周期,关键记住一句话:主进程(PID 1)活着,容器就活着;主进程退出,容器就停止。
sql
docker create docker start docker pause docker stop docker rm
│ │ │ │ │
↓ ↓ ↓ ↓ ↓
┌────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐
│Created │ ───→ │ Running │ ─→ │ Paused │ ─→ │ Stopped │ ─→ │ Removed │
└────────┘ └────┬────┘ └────┬────┘ └────┬────┘ └─────────┘
│ │ │
│ docker │ docker │ docker
│ restart │ unpause │ start
↓ ↓ ↓
┌─────────┐ ┌─────────┐ ┌─────────┐
│ Running │ │ Running │ │ Running │
└─────────┘ └─────────┘ └─────────┘
| 状态 | 说明 | 触发方式 |
|---|---|---|
| Created | 容器已创建但未启动 | docker create |
| Running | 主进程运行中 | docker run / docker start |
| Paused | 进程冻结(内存保留) | docker pause |
| Stopped | 主进程已退出 | docker stop / 主进程退出 |
| Removed | 容器被删除 | docker rm |
为什么容器一启动就退出? 这是初学者最高频的问题。常见原因:
bash
# 原因1:主进程不是前台运行
# Nginx默认fork子进程后父进程退出,容器跟着停了
docker run nginx:1.27 # 容器立即退出
# 原因2:一次性任务执行完就退出
docker run ubuntu:24.04 echo "hello" # 输出后容器退出,exit code 0
# 原因3:配置错误导致主进程崩溃
docker run -e MYSQL_ROOT_PASSWORD="" mysql:8.4 # 密码不能为空
排查方法:
bash
# 1. 看容器状态和退出码
docker ps -a
# EXIT CODE 列:0=正常退出, 1=应用错误, 137=OOM, 143=被stop
# 2. 看日志
docker logs 容器名
# 3. 用交互模式调试(覆盖默认命令)
docker run -it --entrypoint bash mysql:8.4
💡 退出码速查:0 =正常结束 | 1 =应用错误 | 137 =内存超限被Kill | 143=SIGTERM优雅退出。后续会深入讲解所有退出码。
容器管理命令
光会docker run还不够,日常管理需要这些命令:
查看
bash
# 列出运行中的容器
docker ps
# 列出所有容器(包括已停止的)
docker ps -a
# 自定义输出格式
docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"
启停和删除
bash
# 停止容器(发送SIGTERM,10秒后SIGKILL)
docker stop 容器名
# 启动已停止的容器
docker start 容器名
# 重启容器
docker restart 容器名
# 删除已停止的容器
docker rm 容器名
# 强制删除运行中的容器
docker rm -f 容器名
# 批量清理已停止的容器
docker container prune
日志和调试
bash
# 实时跟踪日志
docker logs -f 容器名
# 只看最后50行
docker logs --tail 50 容器名
# 查看最近5分钟的日志
docker logs --since 5m 容器名
# 进入容器执行命令(推荐方式)
docker exec -it 容器名 bash
# Alpine镜像没有bash,用sh
docker exec -it 容器名 sh
# 在容器内执行单条命令(不进入容器)
docker exec 容器名 cat /etc/os-release
⚠️ exec vs attach :初学者只用docker exec。docker attach连接的是主进程(PID 1),你敲exit容器就停了。docker exec创建新进程,exit不影响容器运行。
检查和监控
bash
# 查看容器详细信息(JSON格式)
docker inspect 容器名
# 格式化输出常用字段
docker inspect --format '{{.State.Status}}' 容器名 # 运行状态
docker inspect --format '{{.State.ExitCode}}' 容器名 # 退出码
docker inspect --format '{{.State.OOMKilled}}' 容器名 # 是否OOM
docker inspect --format '{{.RestartCount}}' 容器名 # 重启次数
docker inspect --format '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' 容器名 # IP地址
# 实时资源监控
docker stats
# 单次快照(不持续刷新)
docker stats --no-stream
# 查看容器内进程
docker top 容器名
--restart重启策略
生产环境的容器挂了不能靠人工重启,--restart参数让Docker自动帮你处理:
| 策略 | 行为 | 推荐场景 |
|---|---|---|
no(默认) |
不重启 | 一次性任务 |
on-failure[:N] |
非零退出码时重启,最多N次 | 有重启循环风险的服务 |
always |
任何情况都重启 | ⚠️ 注意:手动stop后daemon重启还会拉起来 |
unless-stopped |
除非手动stop,否则重启 | 生产首选 |
bash
# 生产环境推荐
docker run -d --restart unless-stopped --name myweb -p 8080:80 nginx:1.27
# 限制重启次数(防止无限循环)
docker run -d --restart on-failure:5 --name myapp myapp:1.0
⚠️ always的坑 :手动docker stop后,如果Docker daemon重启(比如服务器重启),always策略的容器会重新启动。而unless-stopped会尊重你的手动停止操作。所以生产环境优先选unless-stopped。
Docker重启采用指数退避:100ms → 200ms → 400ms → ...,上限1分钟。容器成功运行10秒后重置退避计数器。
实战一:运行Nginx Web服务器
bash
# 1. 后台运行Nginx,映射端口,指定版本
docker run -d --name mynginx -p 8080:80 --restart unless-stopped nginx:1.27-alpine
# 2. 确认运行状态
docker ps
# CONTAINER ID IMAGE STATUS PORTS NAMES
# a1b2c3d4e5f6 nginx:1.27-alpine Up 3 seconds 0.0.0.0:8080->80/tcp mynginx
# 3. 浏览器访问 http://localhost:8080,看到Nginx欢迎页
# 4. 用curl验证
curl http://localhost:8080
自定义页面
bash
# 1. 创建本地HTML目录
mkdir -p ~/nginx-html
# 2. 写一个简单页面
cat > ~/nginx-html/index.html << 'EOF'
<!DOCTYPE html>
<html>
<head><title>My Docker Nginx</title></head>
<body>
<h1>Hello from Docker! 🐳</h1>
</body>
</html>
EOF
cat > nginx-html/index.html << 'EOF'
<!DOCTYPE html>
<html>
<head><title>My Docker Nginx</title></head>
<body>
<h1>Hello from Docker! 🐳</h1>
</body>
</html>
EOF
# 3. 重新运行,挂载本地目录到Nginx默认站点目录
docker stop mynginx && docker rm mynginx
docker run -d --name mynginx -p 8080:80 --restart unless-stopped \
-v ~/nginx-html:/usr/share/nginx/html:ro \
nginx:1.27-alpine
# 4. 刷新浏览器,看到自定义页面
-v ~/nginx-html:/usr/share/nginx/html:ro表示把宿主机目录挂载到容器内,:ro表示容器内只读。数据卷的完整用法见专栏相关文章。
实战二:运行MySQL数据库
bash
# 运行MySQL 8.4(LTS版本)
docker run -d \
--name mysql-dev \
-p 3306:3306 \
-e MYSQL_ROOT_PASSWORD=devpass123 \
-e MYSQL_DATABASE=myapp \
--restart unless-stopped \
mysql:8.4
# 参数说明:
# -e MYSQL_ROOT_PASSWORD:必须设置,否则容器启动失败
# -e MYSQL_DATABASE:初始化时创建指定数据库
等待MySQL就绪
MySQL启动需要10-30秒,用日志确认就绪:
bash
# 跟踪日志,等待 "ready for connections"
docker logs -f mysql-dev
# 看到 [System] [MY-010931] /usr/sbin/mysqld: ready for connections 就OK了
连接MySQL
bash
# 方式1:进入容器内用mysql客户端
docker exec -it mysql-dev mysql -uroot -pdevpass123
# 方式2:用宿主机客户端(需要安装mysql-client)
mysql -h 127.0.0.1 -P 3306 -uroot -pdevpass123
连接后测试:
sql
-- 查看数据库
SHOW DATABASES;
-- 使用初始化创建的库
USE myapp;
-- 创建表
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(50) NOT NULL,
email VARCHAR(100)
);
-- 插入数据
INSERT INTO users (name, email) VALUES ('张三', 'zhangsan@example.com');
-- 查询
SELECT * FROM users;
-- 退出
EXIT;
数据持久化
⚠️ 默认情况下,MySQL数据存在容器内部,删容器=删数据! 挂载卷才能持久化:
bash
docker stop mysql-dev && docker rm mysql-dev
# 挂载宿主机目录保存数据
docker run -d \
--name mysql-dev \
-p 3306:3306 \
-e MYSQL_ROOT_PASSWORD=devpass123 \
-e MYSQL_DATABASE=myapp \
--restart unless-stopped \
-v ~/mysql-data:/var/lib/mysql \
mysql:8.4
⚠️ 环境变量只在首次初始化 (数据目录为空)时生效。如果
~/mysql-data已经有数据,改-e参数不会生效。
实战三:运行Redis缓存服务
bash
# 基础运行
docker run -d \
--name redis-dev \
-p 6379:6379 \
--restart unless-stopped \
redis:8.0-alpine
# 连接Redis
docker exec -it redis-dev redis-cli
连接后测试:
bash
127.0.0.1:6379> SET mykey "Hello Redis!"
OK
127.0.0.1:6379> GET mykey
"Hello Redis!"
127.0.0.1:6379> SETEX token 3600 "abc123" # 3600秒后过期
OK
127.0.0.1:6379> KEYS *
1) "mykey"
2) "token"
127.0.0.1:6379> EXIT
如果需要数据持久化,加上AOF配置:
bash
docker run -d \
--name redis-dev \
-p 6379:6379 \
--restart unless-stopped \
-v ~/redis-data:/data \
redis:8.0-alpine \
redis-server --appendonly yes
端口映射与目录挂载要点
这两个操作在实战中高频使用,咱快速梳理要点:
端口映射(-p)
bash
# 格式:-p 宿主机端口:容器端口
-p 8080:80 # 基本映射
-p 127.0.0.1:3306:3306 # 只允许本机访问
-p 8080:80 -p 8443:443 # 多个端口
常见误区:
-p 8080:80的意思是:访问宿主机的8080 → 转发到容器的80- 映射后外部仍可能访问不了,检查防火墙和云平台安全组

目录挂载(-v)
bash
# 格式:-v 宿主机路径:容器路径[:ro]
-v ~/html:/usr/share/nginx/html # 普通挂载
-v ~/html:/usr/share/nginx/html:ro # 只读挂载
-v mydata:/var/lib/mysql # 命名卷(Docker管理)
💡 端口映射的网络原理和容器间通信在网络部分深入,数据卷的三种挂载方式和命名卷管理在专栏其他文章详细讲解。
常见问题
容器一启动就退出了怎么办?
按顺序排查:
bash
# 1. 看退出码
docker ps -a
# 0=正常退出, 1=应用错误, 137=OOM被杀, 143=被stop
# 2. 看日志
docker logs 容器名
# 3. 交互模式调试
docker run -it --entrypoint bash 镜像名
最常见的原因:主进程没有前台运行。Docker容器需要主进程保持前台,否则容器立即退出。
端口映射后外部访问不了?
bash
# 1. 确认容器在运行且端口映射正确
docker ps
# 看PORTS列是否有映射关系
# 2. 本地先试
curl localhost:8080
# 3. 检查防火墙
sudo ufw status # Linux
# 或云平台安全组规则
docker exec进去后exit容器也停了?
你用的是docker attach而不是docker exec:
bash
# ❌ 危险:exit会停止容器
docker attach 容器名
# ✅ 安全:exit不影响容器
docker exec -it 容器名 bash
如果一定要用attach,退出时按Ctrl+P, Ctrl+Q(不是exit)来分离。
环境变量修改后不生效?
MySQL等镜像的环境变量只在首次初始化 时生效。如果数据目录已有数据,改-e参数不会重新初始化。需要删除数据目录后重建容器,或者进入容器手动修改。
如何清理所有容器?
bash
# 停止所有运行中的容器
docker stop $(docker ps -q)
# 删除所有容器(包括运行中的)
docker rm -f $(docker ps -aq)
# 或者分步清理
docker container prune # 只删除已停止的
⚠️ 清理前确认没有未持久化的重要数据!
总结
docker run核心参数按场景记:-d后台、--name命名、-p端口、-e环境变量、--restart重启策略- 主进程=容器生命周期,主进程退出容器就停止------这是理解所有容器退出问题的钥匙
- 退出码速查:0正常 | 1应用错误 | 137 OOM | 143被stop
docker exec -it进入容器,docker logs -f看日志,docker inspect查配置------排障三件套- 永远指定镜像版本号 ,禁止用
latest标签 --restart unless-stopped是生产首选重启策略- 重要数据必须挂载
-v,否则删容器=删数据
实战演练
项目背景
你所在团队准备用Docker部署开发环境,需要在一台服务器上同时运行Nginx、MySQL、Redis三个服务,并确保数据持久化和自动重启。
功能需求
FR-001 运行Nginx容器 作为前端开发者,我希望运行Nginx容器并提供自定义静态页面,以便验证Web服务是否正常工作。 验收标准:
- Given:Docker已安装且镜像加速器已配置
- When:执行
docker run启动Nginx,挂载自定义HTML目录 - Then:
curl localhost:8080返回自定义页面内容
FR-002 运行MySQL容器 作为后端开发者,我希望运行MySQL容器并创建初始数据库,以便微服务应用可以连接使用。 验收标准:
- Given:Docker已安装且3306端口未被占用
- When:执行
docker run启动MySQL,设置root密码和数据库名 - Then:
docker exec连接MySQL后SHOW DATABASES能看到myapp数据库
FR-003 运行Redis容器 作为后端开发者,我希望运行Redis容器并开启持久化,以便缓存数据不会因容器重启丢失。 验收标准:
- Given:Docker已安装且6379端口未被占用
- When:执行
docker run启动Redis并挂载数据目录 - Then:
docker exec连接Redis后SET testkey "hello"成功,重启容器后GET testkey仍返回"hello"
FR-004 验证重启策略 作为运维人员,我希望所有容器配置了自动重启策略,以便服务器重启后服务自动恢复。 验收标准:
- Given:三个容器均配置了
--restart unless-stopped - When:执行
sudo systemctl restart docker - Then:
docker ps显示三个容器均为Running状态
需求范围
In Scope :运行Nginx/MySQL/Redis容器、数据持久化、重启策略验证 Out Scope:Docker Compose编排、自定义网络通信、Dockerfile构建
约束条件
- 所有镜像必须指定版本号,禁止
latest - MySQL root密码不得为空
- Redis必须开启AOF持久化
- 所有容器配置
--restart unless-stopped
依赖与前置条件
- 已完成Docker环境已安装并配置镜像加速器
- 端口8080、3306、6379未被占用
有问题评论区见!