ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

Docker实战指南:从镜像容器原理到MySQL与Redis部署排障

2026/9/19 18:48:36 拓冰建站 浏览量
Docker实战指南:从镜像容器原理到MySQL与Redis部署排障 1. 从零开始理解 Docker镜像、容器和仓库到底是什么接触 Docker 这么久我发现很多新手上来就急着敲命令结果镜像拉不下来、容器起不来、数据一删就丢折腾一晚上直接劝退。其实 Docker 的核心概念就三个镜像、容器、仓库。把这三个东西想通了后面所有操作都是顺水推舟。镜像Image可以理解成一个“模板”像装系统用的 Ghost 镜像一样里面打包好了操作系统基础环境、运行时、依赖库和应用程序代码。你拉下来的镜像文件只读、不可修改但是可以被复制、被导出、被反复使用。容器Container则是镜像运行时的实例相当于用 Ghost 镜像装出来的一台台电脑。每个容器相互隔离有自己的文件系统、网络栈和进程空间但底层共用宿主机的内核。仓库Registry就更直白了用来存放和分发镜像的地方公开的 Docker Hub 就像手机应用商店国内各大云厂商也有自己的镜像加速仓库。这三者的关系我用一句大白话总结镜像定义了“跑什么”容器决定了“怎么跑”仓库解决了“去哪拿”。搞懂这三件事你就不会被 Docker 的各种概念绕晕了。这篇教程我会从安装开始覆盖镜像加速、MySQL 8.0 与 Redis 主从实战、docker compose 编排、以及高频故障排查把我实际生产环境里踩过的坑和总结出来的经验全部写出来。适合刚接触容器化、或者已经部署过几个容器但对原理和排障还比较模糊的开发者。2. 安装 DockerLinux 和 Windows 两条路线怎么选2.1 Linux 环境下用脚本一键安装我最早是在 Ubuntu 服务器上接触 Docker 的那会儿最怕的就是各种依赖冲突和源的问题。现在服务器端安装基本就两种方式官方脚本和手动配置。对于绝大多数人我推荐直接用官方安装脚本省时省力。curl -fsSL https://get.docker.com -o get-docker.sh sudo sh get-docker.sh脚本会自动识别系统版本配置官方源安装 Docker Engine、containerd 和 docker-compose-plugin 等一整套组件。执行完以后设置开机自启并验证版本sudo systemctl enable docker sudo systemctl start docker sudo docker version如果系统是 CentOS、Debian 或者麒麟这类国产系统脚本也都兼容。只是国内服务器访问官方脚本偶尔会超时这种情况下可以先把脚本下载到本机再上传或者直接改用清华、阿里云等开源镜像站配置源后手动安装。手动安装虽然步骤多但胜在可控对于内网环境尤其友好。验证 Docker 是否装好最快的方式是跑一个 hello-world 镜像sudo docker run --rm hello-world这条命令会从仓库拉取 hello-world 镜像并创建容器容器执行完输出提示信息后自动退出--rm参数让容器退出后自动清理适合用来测试环境。如果能正常打印信息说明整个链路已经通了。2.2 Windows 安装 Docker Desktop 的完整步骤Windows 上面装 Docker 的体验比 Linux 复杂不少因为 Docker 本身是跑在 Linux 内核上的Windows 需要依赖虚拟化技术。现在的 Docker Desktop 默认使用 WSL 2 后端比老版本的 Hyper-V 方案轻量很多、启动也更快。安装前先确认自己 Windows 版本是 10 64 位专业版/企业版/教育版或者 Windows 11 任意版本。然后在“控制面板 - 启用或关闭 Windows 功能”里勾选“适用于 Linux 的 Windows 子系统”没有勾选的话后续大概率报 WSL 相关的错。接下来以管理员身份打开 PowerShell执行wsl --install装好 WSL 2 后重启电脑再去 Docker 官网下载 Docker Desktop Installer.exe。双击安装时有个细节安装向导里有一个 “Use WSL 2 instead of Hyper-V” 的勾选框务必保持勾选状态。装完打开 Docker Desktop等待引擎启动看到鲸鱼图标变成绿色就说明成功了。有人会问既然是本地开发环境能不能不装 Docker Desktop直接用 WSL 里的 Docker Engine当然可以甚至我后来在 Windows 上写项目时就是这么干的在 WSL 的 Ubuntu 里装 Docker Engine然后用 VS Code 的 Remote-WSL 插件连进去既省掉了 Docker Desktop 这个图形壳资源占用也更低。但如果你需要 GUI 界面、需要 K8s 单机集群支持、或者希望借助 Docker Desktop 统一管理容器和镜像那还是直接装它更方便。2.3 启动失败排查virtualization support not detected 等常见坑Windows 安装 Docker Desktop 最常见的错误就是标题里那句Docker Desktop failed to start because virtualization support was not detected。出现这个提示先别急着重装按顺序排查三层问题第一层BIOS 里虚拟化开关没开。开机按 F2/F10/Del 进入 BIOS找到 Intel Virtualization Technology 或者 AMD SVM Mode改成 Enabled 并保存退出。进系统后打开任务管理器 - 性能 - CPU右下角能看到“虚拟化: 已启用”才说明 OK。第二层Windows 可选功能没启用。以管理员身份运行 PowerShell执行systeminfo查看 Hyper-V 要求那一项如果显示“已检测到虚拟机监控程序。将不显示 Hyper-V 所需的功能”说明虚拟化功能已经被占用这种情况下需要在“启用或关闭 Windows 功能”里把“Windows 虚拟机监控程序平台”和“虚拟机平台”都勾上。第三层VBS基于虚拟化的安全性导致冲突。有时候 WSL2 和 VBS 会抢资源解决办法是在 PowerShell 里执行bcdedit /set hypervisorlaunchtype auto然后重启再试。如果一直卡在启动阶段可以执行wsl --shutdown关闭所有 WSL 实例再重启 Docker Desktop。我遇到过最离谱的情况是 VMware Workstation 和 WSL2 抢 Hyper-V后来升级了 VMware 并开启 Hyper-V 支持才解决。3. 镜像加速与存储配置告别下载慢和 C 盘爆满3.1 为什么镜像拉取这么慢以及加速器配置方法刚接触 Docker 的人大概率会问为什么docker pull mysql:8.0拉了半天进度条都不动原因说起来也简单默认走的 Docker Hub 仓库服务器在国外国内直连速度不稳定尤其是大镜像经常卡在某一层几十秒不动。解决办法就是给 Docker 配置国内镜像加速器。加速器的原理是各大容器服务商把 Docker Hub 的热门镜像同步到了国内节点客户端请求加速器地址时从就近节点拉取数据吞吐量提升非常明显。以 Ubuntu 和 CentOS 为例修改/etc/docker/daemon.json{ registry-mirrors: [ https://docker.m.daocloud.io, https://dockerproxy.com, https://docker.nju.edu.cn ] }改完执行sudo systemctl daemon-reload sudo systemctl restart docker使配置生效。重启后用docker info命令查看Registry Mirrors字段能看到已生效的加速器地址就说明配置成功了。这里多说一句网上有些加速器地址可能已经失效或者需要登记内测建议配置多个备选地址。我之前踩过坑只配了一个地址结果过几天加速器维护所有镜像都拉不下来了后来配置了三个备选才稳定。实测下来几 MB 的小镜像速度差别不明显但像 MySQL、Redis、GitLab 这种几百 MB 甚至上 GB 的镜像用加速器前后体感完全是两个级别。3.2 daemon.json 里的其他实用配置项很多教程只讲了 registry-mirrors但 daemon.json 里还有几个高频配置实际部署时非常有用。第一个是data-root用来指定 Docker 的数据存储目录。默认情况下Docker 的镜像、容器、卷全部存放在/var/lib/docker如果系统盘不够大几年下来会被镜像撑爆。我工作中常用的一招是先把 Docker 目录迁移到数据盘修改 daemon.json{ data-root: /data/docker }然后执行sudo systemctl restart docker。需要注意的是迁移前要确保新目录有足够的磁盘空间并且旧数据路径没有残留容器。如果只是想清掉缓存文件用docker system prune -a也会释放大量空间但会删掉所有未使用的镜像和容器操作前务必确认没有需要保留的环境。Windows 上同理Docker Desktop 的Settings - Resources - Advanced里可以调整镜像存储位置和磁盘镜像大小默认是放在 C 盘的如果 C 盘空间紧张建议尽早改到其他盘。第二个是log-driver和log-opts控制容器日志的大小。默认情况下容器日志无限增长容易撑爆磁盘。我习惯给全局日志加上限制{ log-driver: json-file, log-opts: { max-size: 20m, max-file: 3 } }这样每个容器单个日志文件不超过 20MB最多保留 3 个文件。对于日志量大的服务比如 Nginx、Java 应用这条配置能显著延长磁盘寿命。不过要注意daemon.json 里的 log 配置是全局生效的默认情况下也会覆盖容器启动时未指定的日志配置。第三个是live-restore。设置成 true 以后Docker 守护进程重启时不会杀掉运行中的容器这个在线上环境触不及防的维护场景里很实用能避免重启 Docker 导致服务中断。{ live-restore: true }但要注意部分老版本 Docker 对该功能的支持不完善生产环境升级前建议先在测试环境验证。3.3 拉取镜像后修改存储位置的思路还有一个经常被问的问题Docker 可以安装到其他盘吗这个分两种情况。如果是 Linux 原生 Docker改>docker image prune -a docker container prune前者删除所有未被容器引用的镜像后者清理所有已停止的容器。结合日志限制生产服务器跑几个月磁盘依然干净。4. 实战部署MySQL 8.0 安装与使用4.1 创建网络与数据目录先做前置准备学习 Docker 最好的方式就是拿真实的服务练手。我选 MySQL 8.0 作为第一个实战项目因为数据库对持久化、网络配置、端口映射的要求特别典型把 MySQL 部署熟了后面部署任何中间件都会轻松很多。先创建一个专用的 Docker 网络叫 mysql-net。为什么要自己建网络因为 Docker 默认的 bridge 网络虽然也能容器互联但容器重启后 IP 可能变化服务和数据库之间用 IP 通信会不稳定。创建一个自定义网络后容器之间可以通过容器名互相访问IP 变了也不影响。docker network create mysql-net接下来创建 MySQL 数据目录和配置文件目录。容器删除以后数据还能保留的关键就是把宿主机目录挂载进容器我习惯这样规划mkdir -p /data/mysql/{data,conf,logs}这三个目录分别用来存放实际数据、自定义配置文件和运行日志。接下来写一个基础配置文件/data/mysql/conf/my.cnf核心参数如下[mysqld] character-set-serverutf8mb4 collation-serverutf8mb4_unicode_ci default-storage-engineINNODB max_connections500 lower_case_table_names1lower_case_table_names1让表名不区分大小写国内大多数业务都要求这个配置避免开发环境 Windows 和线上 Linux 行为不一致。utf8mb4是必选字符集兼容全量 Unicode 字符包括 emoji这是老项目谈之色变的编码问题。4.2 拉取镜像并启动 MySQL 8.0 容器前置工作做完后拉取镜像并启动容器docker pull mysql:8.0镜像比较大配合加速器的话大约一两分钟就能拉完。启动命令docker run -d \ --name mysql8 \ --network mysql-net \ -p 3306:3306 \ -e MYSQL_ROOT_PASSWORDYourPassword123 \ -v /data/mysql/data:/var/lib/mysql \ -v /data/mysql/conf/my.cnf:/etc/mysql/conf.d/my.cnf \ -v /data/mysql/logs:/var/log/mysql \ --restartalways \ mysql:8.0逐个解释一下关键参数。-d表示后台运行--name给容器命名--network指定刚才创建的专用网络-p 3306:3306把宿主机的 3306 端口映射到容器的 3306 端口-e MYSQL_ROOT_PASSWORD设置 root 密码-v是目录挂载--restartalways表示 Docker 或宿主机重启后自动拉起容器。启动后查看实时日志docker logs -f mysql8日志中如果出现ready for connections说明 MySQL 已经启动成功。用宿主机上的 mysql 客户端验证一下mysql -h 127.0.0.1 -P 3306 -uroot -p如果宿主机没有安装 mysql 客户端也可以进入容器操作docker exec -it mysql8 mysql -uroot -p这里有个细节MySQL 8.0 默认的认证方式是 caching_sha2_password有些老的客户端工具比如 5.x 的 Navicat连不上。需要改成 mysql_native_passwordALTER USER root% IDENTIFIED WITH mysql_native_password BY YourPassword123; FLUSH PRIVILEGES;4.3 连接信息与常见使用误区我见过不少人在使用容器化 MySQL 时掉进同一个坑容器重启后数据丢了。实际上只要你挂载了宿主机目录到/var/lib/mysql即使容器被删除数据还在宿主机上。删除容器后用同样参数重新 run 一次数据就回来了。另外要注意端口占用问题。如果宿主机已经有 MySQL 实例在跑-p 3306:3306会启动失败报错port is already allocated。解决方式有两个要么停掉宿主机的 MySQL要么把容器端口映射改到 3307比如-p 3307:3306。在开发环境我建议数据库这类基础设施尽量放在容器里跑宿主机保持干净也方便多版本切换。MySQL 8.0 默认开启了二进制日志日志增长非常快尤其是建表频繁或者大批量导入数据时/data/mysql/logs所在的磁盘经常被写满。经验之谈除非要做主从否则可以关闭二进制日志或者定期清理PURGE BINARY LOGS BEFORE NOW() - INTERVAL 3 DAY;如果已经出现磁盘满的情况且开启了 binlog登录进去执行上面的清理语句再配合docker system prune清除无用镜像磁盘就释放出来了。5. 容器化部署 Redis 主从复制手把手配置5.1 Redis 主从架构为什么值得用容器化方式部署Redis 在中小型项目里的角色通常是缓存、Session 存储、或者排行榜这类实时数据服务。单机部署的 Redis 有一个致命问题进程挂了数据就丢了虽然有 RDB 和 AOF 持久化但恢复也需要时间。主从架构可以做到自动故障转移配合哨兵或 Cluster从节点分担读压力是生产环境的高频需求。用 Docker 部署 Redis 主从有一个非常大的好处复制配置文件超简单。传统方式下你需要在一台机器上准备两套不同端口、不同配置文件的 Redis 实例还要注意目录隔离。容器化以后每个容器天然隔离端口和数据目录自己管自己的主从关系只需要几行配置就能搭好。我的规划是master 节点容器名 redis-master端口 6379slave 节点容器名 redis-slave1端口 6380slave 节点容器名 redis-slave2端口 6381三个容器共用同一个自定义网络通过内网互相通信外部只能访问映射出来的端口。5.2 编写 Redis 主从配置并启动容器先准备好配置目录mkdir -p /data/redis/{master,slave1,slave2}主节点/data/redis/master/redis.conf内容如下bind 0.0.0.0 protected-mode yes port 6379 daemonize no dir /data appendonly yes appendfsync everysec requirepass RedisMasterPwd123这里说明三个重点。bind 0.0.0.0在容器环境下必须设置否则 Redis 只监听容器回环地址宿主机和其他容器都连不上daemonize no防止 Redis 在容器内后台化因为 Docker 容器需要前台进程作为主进程一旦 Redis 转为后台进程容器会认为进程结束了直接退出requirepass给主节点设置密码从节点同步也需要认证。从节点redis.conf和主节点基本一致只是端口不同并多了replicaof和masterauth两行bind 0.0.0.0 protected-mode yes port 6380 dir /data appendonly yes appendfsync everysec requirepass RedisMasterPwd123 replicaof redis-master 6379 masterauth RedisMasterPwd123replicaof redis-master 6379告诉从节点主节点的主机名和端口。在自定义网络中redis-master就是主节点的容器名。注意 Redis 5.0 之前语法是slaveof之后的版本已经统一改成replicaof虽然老参数兼容但新项目建议直接用新语法。启动主节点docker run -d \ --name redis-master \ --network redis-net \ -p 6379:6379 \ -v /data/redis/master:/data \ -v /data/redis/master/redis.conf:/etc/redis/redis.conf \ --restartalways \ redis:7.0 redis-server /etc/redis/redis.conf注意最后的redis-server /etc/redis/redis.conf它是容器启动命令。官方镜像的默认 CMD 命令是直接启动 redis-server不指定配置文件的话容器内所有自定义配置都不会生效这是个非常容易踩的坑。从节点启动命令完全相同只需要把名字、端口、配置文件路径替换掉例如docker run -d \ --name redis-slave1 \ --network redis-net \ -p 6380:6379 \ -v /data/redis/slave1:/data \ -v /data/redis/slave1/redis.conf:/etc/redis/redis.conf \ --restartalways \ redis:7.0 redis-server /etc/redis/redis.conf这里有个小技巧从节点容器内部 Redis 仍然监听 6379 端口只是映射到宿主机的 6380所以配置文件里的port 6380反而不需要写保持容器内的 6379 即可。不过为了可读性我在配置里保留了 6380这种情况下要保证/data/redis/slave1/redis.conf里的port和容器映射一致否则客户端连接会出错。启动后用 docker exec 进入从节点执行docker exec -it redis-slave1 redis-cli -a RedisMasterPwd123 info replication如果看到role:slave且master_link_status:up说明主从同步已经建立了。往 master 写一个 key再到 slave 上能读出来就说明同步链路完全正常。5.3 主从切换和故障恢复的实操心得主从配置好以后不要以为就万事大吉了。实际生产中 Redis 主节点宕机后从节点只会被动接受同步不会自动提升为主节点需要引入 Redis Sentinel哨兵或者 Cluster 模式才能实现自动故障转移。如果只是测试环境手动切换也很快停掉主容器docker stop redis-master选一个从节点提升为临时主节点docker exec -it redis-slave1 redis-cli -a RedisMasterPwd123 replicaof no one把另一个从节点指向新的主节点docker exec -it redis-slave2 redis-cli -a RedisMasterPwd123 replicaof redis-slave1 6379这样架构就变成了 slave1 为主、slave2 为从。但要注意容器 IP 和容器名在 Docker 内部是动态解析的一旦原主节点被重新启动这个提升过程会被打断。生产环境建议直接上哨兵模式三个节点加三个哨兵进程。我还想特别提醒一个坑从节点一旦配置了replicaof它就变成了只读节点往从节点写入数据会报错READONLY You cant write against a read only replica。有朋友因为分不清请求打到了从节点调试半天以为代码有问题结果发现是客户端配置的端口是 6380。业务连接时一定要明确读写分离策略读可以走从节点写必须走主节点。6. docker compose 编排一条命令拉起整套环境6.1 为什么单条 docker run 命令不够用当你管理的容器变多比如 MySQL、Redis、应用服务三个容器每条都用docker run启动不仅命令冗长参数还容易写错而且不便于版本管理。docker compose 的定位就是解决单容器编排的问题用 YAML 文件描述整套环境的所有服务和配置一条docker compose up -d全部搞定。我最早被 docker compose 吸引是因为用一个docker-compose.yml文件就能把某套项目的全部中间件配置下来换机器部署时只需要把目录和文件拷过去一条命令环境就起来了。相比逐个敲 docker run效率和可维护性完全是两个维度。当前最新版本 docker compose 已经不需要单独安装 compose 插件因为 Docker Engine 20.10 的安装包通常会包含docker-compose-plugin直接使用docker compose命令即可。老版本的docker-compose命令和docker compose主要区别是中间多了一个空格语法大部分兼容。6.2 MySQL Redis 主从的 compose 文件实战接下来用一个真实案例展示 docker compose 的编写。我把前面手动部署的 MySQL 8.0 和 Redis 主从环境统一写成docker-compose.yml这样后续换机器部署时不用记一堆 docker run 参数。version: 3.8 services: mysql8: image: mysql:8.0 container_name: mysql8 restart: always ports: - 3306:3306 environment: MYSQL_ROOT_PASSWORD: YourPassword123 TZ: Asia/Shanghai volumes: - /data/mysql/data:/var/lib/mysql - /data/mysql/conf/my.cnf:/etc/mysql/conf.d/my.cnf:cached - /data/mysql/logs:/var/log/mysql networks: - backend redis-master: image: redis:7.0 container_name: redis-master restart: always ports: - 6379:6379 volumes: - /data/redis/master/redis.conf:/etc/redis/redis.conf command: redis-server /etc/redis/redis.conf networks: - backend redis-slave1: image: redis:7.0 container_name: redis-slave1 restart: always ports: - 6380:6379 volumes: - /data/redis/slave1/redis.conf:/etc/redis/redis.conf command: redis-server /etc/redis/redis.conf depends_on: - redis-master networks: - backend networks: backend: driver: bridge这里有几个 compose 专属的配置细节值得注意。volumes里的:cached是 macOS 上的优化选项在 Linux 上无效我保留它是为了兼容 Mac 开发环境。depends_on只控制启动顺序并不能保证服务已经就绪——也就是说redis-slave1会在redis-master容器启动后立即启动但 Redis 主进程可能还没有 ready从节点连接不上的话会不断重试这也算是一个默认行为下的容错机制。写完文件后执行docker compose up -d查看状态docker compose ps如果某个服务启动失败先看日志docker compose logs mysql8停止所有服务用docker compose down停止并删除数据卷用docker compose down -v。请注意down -v会把 volumes 里配置的数据卷一并删除数据就真没了这个命令在生产环境一定要谨慎我见过有人手滑把本地数据全部清空血泪教训。6.3 环境变量和 .env 文件的使用技巧compose 文件里如果把数据库密码、端口这些参数硬编码进去换环境的时候改起来很麻烦。更专业的做法是引入.env文件在docker-compose.yml里通过${VAR}引用变量。定义.envMYSQL_ROOT_PASSWORDYourPassword123 MYSQL_PORT3306 REDIS_REQUIREPASSRedisMasterPwd123 REDIS_MASTER_PORT6379 REDIS_SLAVE1_PORT6380修改docker-compose.yml对应位置mysql8: ports: - ${MYSQL_PORT}:3306 environment: MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}其实 compose 文件本身不需要预设一个固定密码也可以使用MYSQL_RANDOM_ROOT_PASSWORD: yes让系统随机生成密码然后从容器日志里查看。这种方式更适合临时测试环境生产环境不建议因为密码不好管理。.env文件要加入.gitignore避免把密码提交到代码仓库。部署时只需要分发.env和docker-compose.yml别人不需要知道密码也能了解整套服务结构。7. 高频实战场景部署 GitLab、KodBox 和打包镜像7.1 用 Docker 快速搭建 GitLab 代码仓库GitLab 是很多团队内部代码托管的第一选择但原生安装依赖非常多装了容易、维护难。用 Docker 部署可以把这些乱七八糟的依赖全部隔离掉缺点是镜像特别大启动也比较慢首次启动可能要等几分钟。先准备挂载目录mkdir -p /data/gitlab/{config,logs,data}启动命令docker run -d \ --name gitlab \ --network backend \ -p 80:80 \ -p 443:443 \ -p 22:22 \ -v /data/gitlab/config:/etc/gitlab \ -v /data/gitlab/logs:/var/log/gitlab \ -v /data/gitlab/data:/var/opt/gitlab \ --restartalways \ gitlab/gitlab-ce:latest这里映射了三个端口80 是 Web 访问443 是 HTTPS22 是 SSH 提交代码用的。如果宿主机的 80 或 22 端口已经被其他服务占用建议改成非标准端口比如8080:80和2222:22。改完以后需要通过外部 URL 访问 GitLab需要在/data/gitlab/config/gitlab.rb中修改external_url http://gitlab.example.com:8080 gitlab_rails[gitlab_shell_ssh_port] 2222启动过程中可以用docker logs -f gitlab观察。看到类似gitlab Reconfigured!的日志时说明 GitLab 配置完成。初始管理员账号是root初始密码会在首次启动时生成并打印在日志里或者存放在/etc/gitlab/initial_root_password文件中该文件会在 24 小时后自动删除。GitLab 是资源大户最低 4GB 内存才能跑得流畅8G 内存会更从容我这台测试机 8G 内存跑起来 CPU 占用率长期在 20% 左右。部署 GitLab 前建议检查可用内存和磁盘否则即使容器能启动访问起来也很卡。7.2 私有云盘 KodBox 的一键部署如果你的需求是搭建一个私有网盘KodBox 是个不错的轻量选择官方提供了容器镜像部署非常简单docker run -d \ --name kodbox \ -p 8088:80 \ -v /data/kodbox:/var/www/html \ --restartalways \ kodcloud/kodbox部署完成后访问http://服务器IP:8088按 Web 向导初始化。KodBox 安装向导要求填写数据库信息也就是说它需要依赖一个 MySQL 实例。可以复用前面部署的 MySQL 8.0创建一个专门给 KodBox 用的数据库和用户。KodBox 的体积很小部署速度非常快适合个人用户快速搭建私有云盘。这里有一个细节KodBox 默认会把上传的文件存储在容器内的/var/www/html/data目录我把整个/var/www/html挂载出来了这样升级容器时数据不会丢。升级 KodBox 镜像时先备份宿主机上的/data/kodbox目录再拉新镜像重新创建容器旧数据挂载进去后会自动识别。7.3 PHP 和 Java 项目怎么用 Docker 打包镜像程序员问得最多的问题之一就是我写了代码怎么把项目打成 Docker 镜像这里我分别说 PHP 和 Java 两类最常见的情况。PHP 项目的打包思路是基于官方 PHP 镜像把代码复制进去。Dockerfile示例如下FROM php:8.2-fpm RUN docker-php-ext-install pdo_mysql mysqli WORKDIR /var/www/html COPY . /var/www/html EXPOSE 9000 CMD [php-fpm]然后执行docker build -t my-php-app .即可构建出镜像。这个手法对 PHP 项目特别友好因为官方 php 镜像里已经把编译扩展的工具链都准备好了docker-php-ext-install一条命令就能装好 pdo_mysql、redis 等扩展不用手动解决依赖。Java 项目通常用多阶段构建先用 Maven 镜像编译出 jar再复制到精简的 JRE 镜像里运行最终镜像体积会小很多FROM maven:3.8-jdk-11 AS builder WORKDIR /app COPY pom.xml . RUN mvn dependency:go-offline COPY src ./src RUN mvn package -DskipTests FROM openjdk:11-jre-slim WORKDIR /app COPY --frombuilder /app/target/my-app.jar app.jar EXPOSE 8080 ENTRYPOINT [java, -jar, app.jar]第一个 FROM 是构建阶段用来编译项目第二个 FROM 是运行阶段只需要一个运行环境和一个 jar 包。多阶段构建是减少最终镜像体积的必经之路一个几 MB 的 jar 包如果直接用 maven 镜像当运行环境体积可能膨胀到几百 MB多阶段构建后只需要 JRE 基础镜像就够了。在 IntelliJ IDEA 里可以通过 Docker 插件实现“一键打包并部署”。安装 Docker 插件后在 Run/Debug Configuration 里选择 Dockerfile 文件指定好路径点击运行就会自动 build 镜像并启动容器。这种方式很适合本地开发调试改完代码直接跑容器不需要手动敲 docker build 命令。8. 常用命令速查与故障排查实录8.1 日常操作必会命令清单Docker 的命令多但成体系掌握核心命令就能覆盖大多数场景。我把频率最高的命令整理成了一张速查表新手照着用就行。操作命令示例说明查看容器docker ps -a查看所有容器状态不加-a只看运行中的进入容器docker exec -it 容器名 bash进入容器操作内部环境查看镜像docker images列出本地所有镜像构建镜像docker build -t 名称:版本 .在当前目录找 Dockerfile 构建查看日志docker logs -f 容器名实时跟踪容器日志输出停止/启动docker stop 容器名/docker start 容器名停止和启动已有容器删除容器docker rm -f 容器名强制删除容器删除镜像docker rmi 镜像名删除本地镜像清理资源docker system prune -a清理未使用的镜像、容器、网络查看资源占用docker stats实时查看容器 CPU、内存、网络端口映射查看docker port 容器名查看容器的端口映射关系补充一个非常实用的排查技巧docker inspect 容器名可以查看容器的完整配置包括网络模式、IP 地址、挂载情况、环境变量、健康检查结果等等。排查网络问题的时候我会先用这个命令确认容器实际 IP 和挂载路径调试问题能少走很多弯路。8.2 服务启动失败的常见原因与处理思路场景一docker: Cannot connect to the Docker daemon at unix:///var/run/docker.sockLinux 上出现这个报错通常原因是当前用户不在 docker 组里或者 Docker 服务没启动。解决方式sudo systemctl status docker sudo systemctl start docker # 如果权限问题把当前用户加入 docker 组 sudo usermod -aG docker $USER newgrp docker场景二Windows 下failed to connect to the docker api at npipe:////./pipe/docker_engine这是 Docker Desktop 引擎没启动或者 WSL 2 环境异常导致的。我的排查顺序是先确认 Docker Desktop 是否在托盘区运行再执行wsl --shutdown后重启然后打开 Docker Desktop 等引擎图标变绿如果还不行就重置 WSL 发行版。场景三容器秒退日志为空常见原因是容器内的主进程没有以前台方式运行。比如启动 Redis 时忘记使用redis-server /etc/redis/redis.conf而 Redis 默认配置是后台运行容器以为进程结束就退出了。解决办法是确认命令里主进程保持前台运行。场景四端口映射失败port is already allocated宿主机的端口已经被占用用sudo netstat -tunlp | grep 端口号看看是哪个进程占用的改用其他映射端口即可。场景五容器内时区错误默认时区是 UTC国内应用会出现日志时间差 8 小时的问题。在 docker run 里增加环境变量解决-e TZAsia/Shanghai场景六docker pull一直超时参考第 3 节的内容配置镜像加速器或者检查服务器网络是否能够访问外部镜像仓库。8.3 Kafka 和 DVWA 等特殊镜像的几个注意点热搜词里有人搜 Kafka 部署报错error while fetching metadata with correlation id这个问题在容器化 Kafka 环境里非常典型。Kafka 部署时客户端通过bootstrap.servers获取元数据时如果 Kafka 配置里暴露的advertised.listeners是容器内网 IP而宿主机客户端通过映射端口访问就会出现这个错误。简单来说宿主机客户端请求的 IP:端口和实际能够连通的地址对不上。Kafka 部署得用KAFKA_ADVERTISED_LISTENERS指定外部可达的地址斜杠后是监听协议。比如容器部署在宿主机上外部通过PLAINTEXT://宿主机IP:9092访问那么就需要这么配置KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://192.168.1.100:9092 KAFKA_LISTENERS: PLAINTEXT://0.0.0.0:9092另一个常见的搜素词是 Kali 搭建 DVWA 靶场DVWADamn Vulnerable Web Application是做 Web 安全测试训练用的靶场环境通过 Docker 部署可以避免污染宿主机环境docker run -d -p 8080:80 vulnerables/web-dvwa启动后访问http://localhost:8080默认账号 admin 密码 password。这个镜像自带 MySQL 配置向导安装完后需要登录并初始化数据库才能正常使用。DVWA 部署本身很简单环境上越隔离越好容器化也是网络安全爱好者和相关专业学生做实验的好帮手。8.4 Docker Desktop 在 Windows 上的特殊维护Windows 用户还需要知道 Docker Desktop 的“Close”和“Restart”其实只是关掉前端程序WSL 2 里的 docker daemon 可能还在运行。如果你改了daemon.json或者重置了网络建议在任务栏图标菜单里选Restart让整个引擎彻底重启否则配置不生效。Docker Desktop 有一个容易忽略的坑Windows 防火墙可能会拦截容器端口转发导致宿主机能访问localhost:3306但局域网内其他机器访问不了。排查方式在防火墙高级设置里添加入站规则允许 Docker 使用的端口或者直接关闭防火墙测试确认问题后放行具体端口。还有如果 Docker Desktop 设置里选的网络模式是 NAT没那么容易出这种问题但改过网络模式的话要多留个心眼。另外Docker Desktop 的虚拟机磁盘文件默认是动态增长每次 build 镜像或拉取大镜像都会占用磁盘。经常用 Docker 的话建议在Settings - Resources里手动设置一个 disk size 上限比如 64GB并在磁盘空间紧张时用docker system prune清理缓存避免磁盘占满导致 WSL 直接崩溃。9. 我个人在长期使用 Docker 后的一些体会前面讲了很多操作细节和踩坑处理最后聊一点实践经验。如果你是从零开始学 Docker我的建议非常简单不用追求把所有概念和命令都背下来先跑起来一个 MySQL、一个 Redis把“拉镜像、起容器、看日志、进容器、删容器”这个流程走一遍比看十篇教程都管用。我自己的学习路径是一开始在虚拟机里反复练习 docker run不小心删过数据库、清空过环境也从这些失误里学会了备份的重要性。等命令熟悉以后再花一个晚上把 docker compose 用起来你的部署能力会上一个台阶。等到需要多机部署、需要弹性扩缩容的时候再去学 Docker Swarm 或者 K8s基础扎实了之后上手也快。最后再分享一个小技巧所有 docker run 命令我都建议加上--restartalways所有需要持久化的目录都明确挂载到宿主机所有密码都不要写死在命令历史里放到.env文件中管理。这三点守住了线上出问题的概率能降一大半。Docker 不是银弹但掌握了正确的使用姿势它确实能从“玩具”变成真正提高生产力的工具。