未记录事实核验 · 状态:维护中

记录仅说明核验时间与维护状态,不保证适用于所有环境;使用前请留意正文中的版本、适用条件与风险提示。

warning 适用范围与待维护事项

本文适用于已安装 Docker、能使用 Bash 的本地隔离开发环境,不是生产部署或数据库备份方案。示例弱口令不得用于真实服务,发布端口须限制访问;批量停止、删除命令执行前需核对目标。各段复用容器名,应分开执行。第三方镜像标签及启动条件尚未核验,Kafka 缺少完整配置,ETL 脚本与 my-app 镜像未提供,相关片段仅演示参数。挂载需自行准备路径与权限,复制运行中 MySQL 数据目录不保证备份一致性。

Docker 容器基本操作

abstract 本章要点

  • docker run 的各种参数,能看懂各种启动命令
  • 端口映射:让宿主机访问到容器内的服务
  • 目录挂载:容器和宿主机共享文件
  • 容器生命周期管理:启动、停止、删除
  • 进入容器内部和查看日志

容器的生命周期

Created(已创建)
    │
    ▼ docker start
Running(运行中)◄──── docker run(直接创建并运行)
    │
    ├──► docker pause ──► Paused(已暂停)
    │         docker unpause ◄──────────────┘
    │
    ▼ docker stop / docker kill
Stopped(已停止)
    │
    ▼ docker rm
Deleted(已删除)

docker run:创建并启动容器

docker run 是最常用的命令,把镜像变成运行中的容器。

基础语法

docker run [参数选项] 镜像名[:标签] [容器内执行的命令]

最常用的参数

参数 全称 含义
-d --detach 后台运行(detached 模式),不占用终端
-it --interactive --tty 交互模式,进入容器终端
--name --name 给容器起一个名字
-p --publish 端口映射,格式 宿主机端口:容器端口
-v --volume 目录/文件挂载
-e --env 设置环境变量
--rm --rm 容器退出后自动删除(适合临时任务)
--network --network 指定网络
--restart --restart 重启策略
-m --memory 限制内存使用
--cpus --cpus 限制 CPU 使用

实际使用示例

# 最简单:前台运行一个容器(会占用终端,Ctrl+C 退出)
docker run nginx

# 后台运行 MySQL(最常用的模式)
docker run -d \
  --name mysql-dev \
  -e MYSQL_ROOT_PASSWORD=root123 \
  -p 3306:3306 \
  mysql:8.0

# 运行一个临时的 Python 容器,执行完就删除
docker run --rm python:3.11-slim python -c "print('Hello Docker')"

# 交互式进入 Ubuntu 容器(常用于调试)
docker run -it --rm ubuntu:22.04 bash

# 运行 Jupyter Notebook,挂载当前目录
docker run -d \
  --name jupyter \
  -p 8888:8888 \
  -v $(pwd)/notebooks:/home/jovyan/work \
  jupyter/pyspark-notebook:latest

# 带资源限制的运行(生产环境建议设置)
docker run -d \
  --name spark-master \
  --memory="4g" \
  --cpus="2.0" \
  bitnami/spark:3.5

端口映射详解

容器是隔离的,容器内的端口默认外部访问不到。端口映射(Port Mapping)就是把容器内的端口"打通"到宿主机。

语法格式

-p 宿主机端口:容器端口
-p 宿主机IP:宿主机端口:容器端口   # 指定绑定的宿主机 IP
-p 容器端口                        # 随机分配宿主机端口
-P                                 # 随机映射所有暴露端口

实际示例

# MySQL:宿主机 3306 → 容器 3306
docker run -d -p 3306:3306 --name mysql -e MYSQL_ROOT_PASSWORD=root123 mysql:8.0

# 如果宿主机 3306 已被占用,换一个端口
docker run -d -p 13306:3306 --name mysql-test -e MYSQL_ROOT_PASSWORD=root123 mysql:8.0
# 现在通过 localhost:13306 访问这个 MySQL

# Redis:默认端口 6379
docker run -d -p 6379:6379 --name redis redis:7-alpine

# Kafka:多个端口
docker run -d \
  -p 9092:9092 \
  -p 9093:9093 \
  --name kafka bitnami/kafka:3.7

# 多端口映射(Jupyter + 可能的 Spark UI)
docker run -d \
  -p 8888:8888 \
  -p 4040:4040 \
  --name jupyter jupyter/pyspark-notebook

# 查看容器端口映射情况
docker port mysql
# 输出:3306/tcp -> 0.0.0.0:3306

tip 端口冲突处理

如果提示"端口已被占用"(bind: address already in use),可以:

  1. 换一个宿主机端口:-p 13306:3306
  2. 查找并停止占用端口的进程:lsof -i:3306(Mac/Linux)或 netstat -ano | findstr 3306(Windows)

目录挂载详解

挂载(Mount/Volume)是让容器可以访问宿主机文件系统的机制,有两种方式:

方式 语法 说明
Bind Mount(绑定挂载) -v /宿主机路径:/容器路径 将宿主机目录挂载到容器,路径必须是绝对路径
Named Volume(命名卷) -v 卷名:/容器路径 Docker 管理的持久化存储,详见[[【技术与工具】/Docker/08-Docker网络与存储

Bind Mount 示例(最常用)

# 挂载数据目录:MySQL 数据存放到宿主机
docker run -d \
  --name mysql \
  -e MYSQL_ROOT_PASSWORD=root123 \
  -p 3306:3306 \
  -v /data/mysql:/var/lib/mysql \
  mysql:8.0
# 上述挂载将 MySQL 数据目录保存到宿主机 /data/mysql
# 以下为重复的镜像名,仅作注释保留,不应另行执行:
#   mysql:8.0

# 挂载配置文件和数据目录;宿主机配置文件须事先存在
 docker run -d \
  --name mysql \
  -e MYSQL_ROOT_PASSWORD=root123 \
  -v /conf/mysql/my.cnf:/etc/mysql/conf.d/my.cnf \
  -v /data/mysql:/var/lib/mysql \
  mysql:8.0

# Jupyter:将当前工作目录挂载进去,$(pwd) 获取当前目录的绝对路径
docker run -d \
  --name jupyter \
  -p 8888:8888 \
  -v "$(pwd):/home/jovyan/work" \
  jupyter/pyspark-notebook

# ETL 脚本:将脚本目录挂载到 /app,数据目录挂载到 /data
# 需自行提供 scripts/etl_job.py、输入数据及脚本所需依赖
docker run --rm \
  -v "$(pwd)/scripts:/app" \
  -v "$(pwd)/data:/data" \
  python:3.11-slim \
  python /app/etl_job.py

# 只读挂载(容器内只能读,不能写),:ro 表示 read-only
# 需自行提供 my-app:latest 镜像及 /conf/app.json 文件
docker run -d \
  -v /conf/app.json:/app/config.json:ro \
  my-app:latest

warning 挂载路径必须用绝对路径

Bind Mount 的宿主机路径必须是绝对路径(以 / 开头或用 $(pwd) 获取当前目录)。 在本文使用的 -v 语法中,绑定挂载应使用绝对路径;./data 这样的写法通常会因无效卷名报错,而 data 这样的名称表示命名卷,不是当前目录下的绑定路径。

# 错误:相对路径
docker run -v ./data:/var/lib/mysql mysql:8.0

# 正确:绝对路径
docker run -v /home/user/data:/var/lib/mysql mysql:8.0
# 或者用变量
docker run -v $(pwd)/data:/var/lib/mysql mysql:8.0

容器生命周期管理

查看容器

# 查看运行中的容器
docker ps

# 输出示例:
# CONTAINER ID   IMAGE       COMMAND                  CREATED        STATUS       PORTS                  NAMES
# a1b2c3d4e5f6   mysql:8.0   "docker-entrypoint.s…"  2 hours ago    Up 2 hours   0.0.0.0:3306->3306/tcp  mysql-dev

# 查看所有容器(包括已停止的)
docker ps -a

# 只显示容器 ID
docker ps -q

# 以更易读的格式显示
docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"

启动/停止/重启

# 停止容器(发送配置的停止信号,通常为 SIGTERM;超时后强制终止)
# 等待时间受容器配置和平台影响:未配置时 Linux 默认 10 秒,Windows 默认 30 秒
docker stop mysql-dev

# 强制停止容器(发送 SIGKILL,立即终止)
docker kill mysql-dev

# 启动已停止的容器
docker start mysql-dev

# 重启容器
docker restart mysql-dev

# 停止所有运行中的容器
docker stop $(docker ps -q)

删除容器

# 删除已停止的容器
docker rm mysql-dev

# 强制删除运行中的容器(先 kill 再 rm)
docker rm -f mysql-dev

# 删除所有已停止的容器
docker container prune

# 删除所有容器(包括运行中的)—— 谨慎使用!
docker rm -f $(docker ps -aq)

# 停止并删除容器(常用两步操作)
docker stop mysql-dev && docker rm mysql-dev

重启策略

# 设置 always 重启策略(手动停止后暂不重启,Docker 守护进程重启后可再次启动)
docker run -d \
  --restart always \
  --name mysql \
  -e MYSQL_ROOT_PASSWORD=root123 \
  mysql:8.0

# 重启策略选项:
# no          不自动重启(默认)
# always      总是重启
# on-failure  只在退出码非 0 时重启
# unless-stopped  停止前一直重启(手动 docker stop 不会重启)

进入容器内部

docker exec:在运行中的容器执行命令

# 进入容器的交互式 Shell(最常用)
docker exec -it mysql-dev bash
docker exec -it mysql-dev /bin/sh   # 有些镜像没有 bash,用 sh

# 在容器内执行单条命令
docker exec mysql-dev mysql -uroot -proot123 -e "SHOW DATABASES;"

# 以 root 用户执行(某些操作需要 root 权限)
docker exec -u root -it mysql-dev bash

# 在容器内创建文件
docker exec mysql-dev touch /tmp/test.txt

# 查看容器内的进程
docker exec mysql-dev ps aux

docker attach:附加到容器前台进程

# 附加到容器的主进程(Ctrl+C 通常会转发 SIGINT,是否停止取决于主进程的信号处理,一般少用)
docker attach mysql-dev

# 对以 -it 创建且保持交互输入的容器,可用默认脱离键 Ctrl+P 然后 Ctrl+Q 退出而不停止容器
# 上述 mysql-dev 未以 -it 创建,不能依赖此脱离方式

tip exec vs attach 怎么选

  • exec:在容器内开一个新进程执行命令,最常用,推荐
  • attach:附加到容器的主进程,退出时可能导致容器停止,一般不用

查看容器日志

容器主进程及继承其输出流的进程写入 stdout/stderr 的日志通常由 Docker 日志驱动处理;docker logs 能否读取及保留多少日志取决于驱动和配置,应用只写入容器内文件的日志不会自动显示在这里。

# 查看容器日志(输出当前日志驱动仍保留且可读取的日志)
docker logs mysql-dev

# 实时跟踪日志(类似 tail -f)
docker logs -f mysql-dev

# 只看最后 50 行
docker logs --tail 50 mysql-dev

# 看最后 50 行并持续跟踪
docker logs --tail 50 -f mysql-dev

# 看某个时间之后的日志
docker logs --since 2024-01-01T00:00:00 mysql-dev

# 看最近 1 小时的日志
docker logs --since 1h mysql-dev

# 带时间戳显示
docker logs -t mysql-dev

# 查看 Kafka 启动日志,定位启动失败原因
docker logs --tail 100 kafka 2>&1 | grep -i error

容器信息查看

# 查看容器详细信息(JSON 格式,包含网络、挂载等所有配置)
docker inspect mysql-dev

# 只查看 IP 地址
docker inspect --format '{{.NetworkSettings.IPAddress}}' mysql-dev

# 查看容器资源使用情况(实时刷新)
docker stats

# 查看指定容器的资源使用
docker stats mysql-dev

# 查看容器内的进程
docker top mysql-dev

文件传输

# 从容器复制文件到宿主机
docker cp mysql-dev:/etc/mysql/my.cnf ./my.cnf

# 从宿主机复制文件到容器
docker cp ./my.cnf mysql-dev:/etc/mysql/conf.d/my.cnf

# 复制目录(仅演示文件传输,不是有效数据库备份流程)
# 运行中 MySQL 的数据文件复制不保证一致性;需另行采用数据库备份工具或一致性停机流程
docker cp mysql-dev:/var/lib/mysql ./mysql-backup

综合示例:完整的 MySQL 开发环境

# 1. 创建数据目录
mkdir -p ~/docker-data/mysql/data ~/docker-data/mysql/conf

# 2. 创建自定义配置文件
cat > ~/docker-data/mysql/conf/my.cnf <<EOF
[mysqld]
character-set-server=utf8mb4
collation-server=utf8mb4_unicode_ci
max_connections=200
innodb_buffer_pool_size=256M
EOF

# 3. 启动 MySQL 容器
docker run -d \
  --name mysql-dev \
  --restart unless-stopped \
  -e MYSQL_ROOT_PASSWORD=root123 \
  -e MYSQL_DATABASE=dev_db \
  -e MYSQL_USER=dev_user \
  -e MYSQL_PASSWORD=dev123 \
  -p 3306:3306 \
  -v ~/docker-data/mysql/data:/var/lib/mysql \
  -v ~/docker-data/mysql/conf/my.cnf:/etc/mysql/conf.d/my.cnf \
  mysql:8.0

# 4. 观察 MySQL 就绪日志(首次初始化耗时取决于环境,并非固定约 30 秒)
# 初始化期间的临时服务也可能输出该标记,首次匹配不代表最终服务已就绪
# 下一步查询须在初始化完成后成功,才能作为连接验证
docker logs -f mysql-dev 2>&1 | grep -m 1 "ready for connections"

# 5. 验证连接
docker exec -it mysql-dev mysql -uroot -proot123 -e "SHOW DATABASES;"

# 6. 从宿主机连接(使用 MySQL 客户端工具)
# Host: 127.0.0.1, Port: 3306, User: root, Password: root123

常用操作速查

操作 命令
后台运行容器 docker run -d --name xxx image:tag
查看运行容器 docker ps
查看所有容器 docker ps -a
进入容器 docker exec -it xxx bash
查看日志 docker logs -f xxx
停止容器 docker stop xxx
删除容器 docker rm xxx
强制删除 docker rm -f xxx
复制文件 docker cp xxx:/path ./local
查看资源 docker stats


info 前置知识

  • 镜像管理 - 知道如何获取镜像,容器是从镜像启动的

掌握检查

  • [ ] 能说出 docker run -d -p 3306:3306 -v /host:/container --name mysql mysql:8.0 每个参数的含义
  • [ ] 知道 -d 和不带 -d 的区别:前者后台运行,后者前台运行(容器输出直接打印到终端)
  • [ ] 能用 docker exec -it 容器名 bash 进入容器内部,检查进程、查看日志、修改配置
  • [ ] 容器挂了后用 docker logs 容器名 查看最近的日志来定位原因,而不是盲目重启
  • [ ] 知道停止容器用 docker stop(优雅停止),强制停止用 docker kill,以及为什么生产上优先用 stop

实践练习

基础题:启动一个 MySQL 容器,要求:(1) 后台运行;(2) 设置 root 密码;(3) 宿主机 3306 端口映射到容器 3306;(4) 数据挂载到宿主机目录(防止容器删除后数据丢失);(5) 设置 --restart unless-stopped。验证:用 docker exec 连入执行 SQL 查询。

进阶题:观察容器资源使用:同时启动 MySQL 和 Redis 两个容器,用 docker stats 实时监控两者的 CPU 和内存占用;然后停止 MySQL,再次查看 stats,理解如何判断容器是否消耗异常。


下一步