ARTICLE DETAIL

资讯详情

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

Docker实战指南:容器部署、Compose编排与高频报错排查

Docker实战指南:容器部署、Compose编排与高频报错排查 玩 Docker 这几年我最大的感受是它不是装完就扔一边的工具而是用顺手之后你会恨不得把所有常驻服务全塞进容器里。MySQL、Redis、GitLab、Metabase甚至跑在一台低配小主机上的二十来个自动化任务全部变成一条条干净利落的启动命令迁移备份都比以前省心太多。这篇内容对应两种人一种是刚接触 Docker、想搞清楚安装和基础命令的新手另一种是已经在用、但被虚拟化报错、权限错误、网络不通这些边缘问题卡住的老手。我会按一条完整的动手链路来写从环境准备、镜像与容器概念到 MySQL 8.0、Redis 主从的实际部署再到 Compose 编排和微服务打包最后把高频报错的排查方案集中捋一遍。文章里不会去复述官方文档绝大多数内容都是我自己踩过坑之后沉淀下来的操作习惯你可以直接照着抄。这阵子在社群里收消息问得最密集的问题和标题里的热词基本对齐Docker Desktop 启动报 virtualisation support 检测不到、docker 权限错误、docker 安装 mysql 失败、容器网络不通、镜像下载慢。下面我按实际操作顺序一步一步展开。1. 环境准备不同平台跑 Docker 的三种姿势1.1 Windows 11 装 Docker Desktop先解决虚拟化问题Windows 上跑 Docker最常规的路径就是装 Docker Desktop。它本身是个图形化管理界面后端实际工作在 WSL2 或者 Hyper-V 虚拟机里。很多用户装完双击图标看到的就是那条经典报错Docker Desktop failed to start because virtualisation support wasnt detected。这基本不是软件坏了而是主机层面的虚拟化没就绪。处理思路按顺序排查。先去 BIOS/UEFI 里确认虚拟化开关Intel 平台找 VT-x / Intel Virtualization TechnologyAMD 平台找 SVM Mode确认状态是 Enabled。开机时按 Del 或 F2 进 BIOS每家主板菜单位置不一样但关键词就这几个搜一下就能定位。改完保存重启这一步能解决相当一部分启动失败问题。接着看 Windows 功能。在“控制面板 - 程序 - 启用或关闭 Windows 功能”里把“虚拟机平台”和“适用于 Linux 的 Windows 子系统”这两项勾上。如果你的 CPU 比较新Hyper-V 本身这一项也可以一并勾上。勾完后重启大多数情况 Docker Desktop 就能正常启动了。至于 WSL2 内核只要系统是较新的 Windows 11 版本一般装 Docker Desktop 时就会自动处理不用单独折腾。我见过最隐蔽的一个坑用户装的 Windows 是精简版或者某些优化版Hyper-V 组件被裁剪掉了功能列表里根本看不到“虚拟机平台”。这时候重装完整版系统或者求稳直接换到 Linux 环境反而省事。1.2 Linux 服务器装 Docker一条命令和一次升级Linux 环境下的 Docker 安装比 Windows 顺利得多。主流发行版都用官方安装脚本起步curl -fsSL https://get.docker.com -o get-docker.sh sudo sh get-docker.sh脚本会自动检测系统版本配好软件源并完成安装。装完后顺手把当前用户加进 docker 组省得每条命令都加 sudosudo usermod -aG docker $USER newgrp docker注意修改组之后要重新登录终端才生效。如果急着验证可以先执行newgrp docker切换当前会话的用户组立刻就能免 sudo 用 docker 命令。CentOS 7 是个容易出状况的点。老系统默认带的 docker 版本往往停留在 1.13 左右这个版本太老Compose 语法、网络特性都不够用。升级思路是移除旧包、安装新版本但不要直接卸载因为系统里可能还有其他依赖。我实际验证过的操作流程是这样sudo yum remove docker docker-client docker-common docker-engine sudo yum install -y yum-utils sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo sudo yum install -y docker-ce docker-ce-cli containerd.ioCentOS 7 的内核比较保守装新版 Docker 后如果启动报 iptables 相关错误通常是系统防火墙策略和 Docker 的转发规则冲突。我的习惯是启动服务之前先把net.bridge.ipv6.conf.all.disable_ipv6这类内核参数确认一下但更直接的办法是升级后立刻执行sudo systemctl enable --now docker看服务状态再根据报错信息对症处理。1.3 轻量小主机不一定非得用桌面版热词里有一条是“N100 跑 20 个 docker”这个场景说明很多人已经把 Docker 带到了迷你主机、NAS 甚至软路由上。这类设备性能有限我的建议是不装 Docker Desktop直接装纯 Docker Engine再用 Portainer 这类 Web 面板管理容器。N100 这种级别的 CPU 跑二十来个轻量容器不是天方夜谭关键控制资源配额启动容器时尽量加--memory和--cpus限制避免某个服务把整机拖垮。小主机装 Docker 等于把一堆服务的管理工作收敛成一个入口后续不管迁移到新机器还是备份恢复成本都低很多。这部分是个人项目的最佳实践适合自托管玩家也适合团队内部放测试环境。2. 核心命令与概念先把这几个词吃透2.1 镜像和容器一个像模板一个像实例很多人对 Docker 半懂不懂卡就卡在“镜像”和“容器”这两个概念上。打个比方镜像是一个封装好系统环境和应用代码的安装包相当于电脑城卖的“装机版系统镜像”里面什么都有但不运行容器是用这个镜像启动出来的一个独立运行实例相当于你照着镜像装好的一台电脑。同一个镜像可以启动多个容器各容器之间互不干扰。比如你用同一个 MySQL 镜像启动两个容器分别映射到 3306 和 3307 端口那就是两台互不影响的数据库实例。理解了这一层后面所有命令的逻辑都会顺起来。镜像的拉取和更新对应两条命令docker pull mysql:8.0 docker pull redis:7镜像有 tag 概念mysql:8.0表示 8.0 大版本下的最新补丁版本latest代表最新发布版。生产环境我强烈不建议追 latest因为镜像内容的不可控更新可能把环境带崩。用固定版本 tag至少在出问题时能快速复现。2.2 高频命令速记run、ps、logs、exec 怎么配合不需要把 Docker 命令全集背下来日常使用最高频的就是这几条docker run -d --name app -p 8080:80 -v /opt/data:/data nginx:1.26 docker ps docker ps -a docker logs -f app docker exec -it app bash docker stop app docker rm app docker rmi nginx:1.26逐个拆开看。docker run负责创建并启动容器参数-d表示后台运行--name指定容器名-p做端口映射-v挂载数据卷。docker ps看当前运行中的容器-a把已停止的也列出来。docker logs -f跟踪容器日志排查问题最常用。docker exec -it app bash是进入容器内部执行命令相当于 SSH 进虚拟机。一个我经常提醒别人的细节容器是易失的。docker rm删掉容器后容器内所有未挂载的数据都没了。所以从一开始就要有“容器不保存数据”的意识数据和配置一律走数据卷。这不只是好习惯是必须形成的肌肉记忆。2.3 数据卷、端口映射和容器网络的搭配数据卷是容器持久化的核心机制。启动容器时用-v 宿主机目录:容器目录做绑定宿主机目录里的文件会直接映射到容器内。MySQL 的/var/lib/mysql、Redis 的持久化文件、应用日志目录都应该挂出来。这样容器删了再建数据还在升级镜像也不会丢数据。端口映射解决的是“怎么访问容器”的问题。容器内部有自己独立的网络命名空间外部访问容器服务必须通过-p 宿主机端口:容器端口做桥接。比如-p 3306:3306就是把宿主机的 3306 端口流量转发到容器内的 3306 端口。如果只想本地访问可以绑127.0.0.1:3306:3306避免端口暴露到局域网。关于权限错误的补充挂载数据卷之后容器进程对宿主机目录的访问权限经常出问题。比如 MySQL 容器里的 mysql 用户 UID 是 999宿主机目录如果属于 root容器内就会报Cant read dir of /var/lib/mysql/。最简单的做法是提前创建目录并调整属主mkdir -p /opt/mysql8/data sudo chown -R 999:999 /opt/mysql8/data这条命令我在实际部署中反复用99% 的 MySQL 容器启动失败都和它有关。学会看 UID 而不是用户名是容器数据卷排错的第一课。3. 服务部署实例MySQL、Redis 主从、GitLab 一次讲清楚3.1 MySQL 8.0 容器化部署完整流程MySQL 是容器化部署的高频对象但也是报错重灾区。先看最基础的完整命令docker run -d \ --name mysql8 \ -p 3306:3306 \ -e MYSQL_ROOT_PASSWORD你的密码 \ -e TZAsia/Shanghai \ -v /opt/mysql8/data:/var/lib/mysql \ -v /opt/mysql8/conf:/etc/mysql/conf.d \ mysql:8.0这里有三个关键点。第一MYSQL_ROOT_PASSWORD环境变量只在第一次初始化数据目录时生效。如果数据卷里已经有旧数据这个变量就失效密码还是原来那个。第二挂载配置目录/etc/mysql/conf.d可以在宿主机直接写配置文件比如自定义字符集[mysqld] character-set-serverutf8mb4 collation-serverutf8mb4_unicode_ci第三容器跑起来之后务必第一时间检查连接是否正常docker exec -it mysql8 mysql -uroot -p我见过很多人卡在“docker 安装 mysql 失败”上归根结底是三个原因。端口被占用时启动报bind: address already in use换一个宿主机端口映射即可或者先停掉占用程序数据目录权限不对造成初始化失败按上一节chown处理还有一种是内存不够MySQL 8.0 初始化时内存占用较大小内存机器会直接 OOM观察docker logs mysql8如果出现Out of memory要么加内存限制要么限制缓冲池大小再加--innodb-buffer-pool-size128M这类参数。MySQL 8.0 默认的认证插件是caching_sha2_password老版本客户端连接时会报认证失败。处理办法是连接服务器后执行ALTER USER root% IDENTIFIED WITH mysql_native_password BY 你的密码;这个改动针对老客户端兼容新项目可以直接保持默认插件没有兼容包袱就别动它。3.2 Redis 主从复制的容器搭建方案Redis 主从复制用容器搭很直观核心是把两个实例连通起来。步骤分三段先建主节点再建从节点最后配置复制关系。主节点命令docker run -d \ --name redis-master \ -p 6379:6379 \ -v /opt/redis/master:/data \ redis:7 redis-server --appendonly yes从节点命令注意端口和容器名不同docker run -d \ --name redis-slave \ -p 6380:6379 \ -v /opt/redis/slave:/data \ redis:7 redis-server --appendonly yes两个实例起来后进入从节点执行复制指令docker exec -it redis-slave redis-cli REPLICAOF 宿主机IP 6379这里有个容易踩的坑如果主从容器都跑在同一台宿主机上从节点不能直接用127.0.0.1去连接主节点因为容器里的 localhost 指向自己。要填宿主机的局域网 IP。如果想避开 IP 写死的问题更稳妥的做法是用 Docker 自定义网络让容器之间通过容器名互访。创建网络的命令docker network create redis-net然后在两个容器的启动参数里都加上--network redis-net这样从节点连接主节点只需要redis-cli REPLICAOF redis-master 6379容器名天然解析成网络地址。这就是容器网络带来的便利。复制关系建立后用docker exec -it redis-slave redis-cli info replication查看输出master_link_status:up是期望看到的状态。日志里出现MASTER - REPLICA sync started也代表链路已经建立。主备切换、故障转移不在本文范围但生产环境建议直接用官方哨兵容器或者外部编排工具去管理不要纯手工维护。3.3 GitLab、Metabase 这些“重服务”同样能塞进容器容器不只能跑轻量服务重量级应用一样用得很顺。GitLab 是典型代表它会占用不少内存按官方推荐最低 4G但 2G 内存机器勉强也能跑起来慢一点。部署命令如下docker run -d \ --name gitlab \ --hostname gitlab.example.com \ -p 8443:443 -p 8081:80 -p 2222:22 \ -v /opt/gitlab/config:/etc/gitlab \ -v /opt/gitlab/logs:/var/log/gitlab \ -v /opt/gitlab/data:/var/opt/gitlab \ --restart unless-stopped \ gitlab/gitlab-ce:latest首次启动初始化要等几分钟期间用docker logs -f gitlab观察。端口映射不要贪省事直接占 443 和 80很容易跟宿主机的 Web 服务冲突用高位端口更省心。Metabase 就轻松多了一条命令连数据库都能省掉它会默认启动内置 H2 数据库docker run -d --name metabase -p 3000:3000 -v /opt/metabase:/metabase-data metabase/metabase:latest分析类工具、面板类服务、协作类应用这套部署模板反复适用数据目录挂出来端口改成高位加--restart unless-stopped保证机器重启以后服务自动拉起。我个人的习惯是权重级服务一律用这套固定格式避免迁移时还要重查文档。4. 用 Compose 编排多容器从“一条条命令”到“一份清单”4.1 什么时候该放弃 docker run 改用 Compose如果你只是起一个单容器测试docker run完全够用。但服务一多问题就来了MySQL、Redis、应用服务、监控面板要启动四个容器得记住四条命令启动顺序还要自己控制依赖关系梳理一遍就是体力活。Compose 就是干这个的用一份 YAML 文件描述所有容器、网络、数据卷、依赖关系一条命令全部拉起。Docker Compose 本身在装 Docker 的时候一般就带上了校验版本用docker compose version如果提示不存在那就单独装一下 compose 插件。Compose 的文件名约定是docker-compose.yml或compose.yaml放在项目根目录。我的判断标准很简单两个以上互相依赖的容器直接上 Compose。不要觉得自己记忆好半年后再看这些零散命令你也会翻文档半天。4.2 一个完整的 Web 服务编排示例下面是一个 MySQL Redis 从节点 应用服务的 Compose 文件结构services: mysql: image: mysql:8.0 container_name: app-mysql restart: unless-stopped environment: MYSQL_ROOT_PASSWORD: 你的密码 TZ: Asia/Shanghai volumes: - /opt/app/mysql:/var/lib/mysql ports: - 3306:3306 networks: - app-net redis-master: image: redis:7 container_name: app-redis-master restart: unless-stopped command: redis-server --appendonly yes volumes: - /opt/app/redis-master:/data ports: - 6379:6379 networks: - app-net redis-slave: image: redis:7 container_name: app-redis-slave restart: unless-stopped command: redis-server --appendonly yes depends_on: - redis-master volumes: - /opt/app/redis-slave:/data ports: - 6380:6379 networks: - app-net # 从节点启动后可手动或通过脚本执行 REPLICAOF app-redis-master 6379 app: build: . container_name: app-service restart: unless-stopped depends_on: - mysql - redis-master ports: - 8080:8080 networks: - app-net volumes: {} networks: app-net: driver: bridge这个 Compose 文件的逻辑很清爽四个服务共享一个自定义网络app-net应用内连接数据库直接用服务名mysql连接 Redis 主节点直接用redis-master不用关心 IP。depends_on控制启动顺序但也只是顺序关系不等于“数据库已就绪”应用层还需要做重试逻辑这是微服务部署中一个容易忽略的经验点。启动与停止命令docker compose up -d docker compose ps docker compose logs -f app docker compose downup -d后台启动全部服务down停止并移除容器。注意down默认不会删除数据卷数据还是安全的。如果连数据卷一起清掉要加-v这个参数我建议你只在确认不需要数据时使用。4.3 微服务项目镜像打包与 IDEA 联动微服务项目要部署到 Docker得先把项目打成镜像。以 Java 项目为例最常用的方式是在项目根目录放一份 DockerfileFROM openjdk:17-jdk-slim WORKDIR /app COPY target/demo.jar app.jar EXPOSE 8080 ENTRYPOINT [java, -jar, app.jar]接着构建并运行docker build -t demo:v1 . docker run -d --name demo -p 8080:8080 demo:v1如果你的开发环境是 JetBrains IDEA它有内置的 Docker 支持。连上本地 Docker 之后可以直接在运行配置里加一个 Docker 运行项选择 Dockerfile 路径、镜像名称、端口映射一键构建并启动。这个操作可以把“构建镜像 - 推仓库 - 服务器拉取 - 启动容器”的循环缩短成 IDEA 里的一个启动按钮。适合单机开发调试生产环境还是建议走 CI/CD 流水线提交代码触发构建推镜像到仓库服务器拉取并滚动更新。就个人项目或者几十人的小团队来说这一套方式足够用了。等业务量起来再把镜像仓库、编排调度这些组件加上去那是后话。5. 高频报错与排查思路5.1 Docker Desktop 启动失败virtualisation support 问题热词里反复出现的 Docker Desktop 启动报错其实就是没通过虚拟化检测。前面第 1 节讲过开启虚拟化这里补充一个很多人想不到的点Docker Desktop 新版默认走 WSL2如果你的 WSL2 太老或者之前装过旧发行版子系统它也可能触发虚拟化检测失败。排查时可以打开命令行执行wsl --status wsl --update把 WSL 内核更新到最新再重启 Docker Desktop。还有一类情况是装过 VMware、VirtualBox它们和 Hyper-V 共存时会出现冲突表现为开机提示“虚拟化已启用但检测不到”。这种环境要么选 Hyper-V 路线把第三方虚拟机软件关掉要么反过来在 Docker Desktop 设置里切到 WSL2 后端两者一般不兼容混用。如果这些操作都试过还失败再考虑是不是 BIOS 里确实没开——用systeminfo命令查看输出中的 Hyper-V 要求部分如果是“已检测到虚拟化固件”则说明 BIOS 已开问题在 Windows 组件上如果显示“未检测到虚拟化固件”那就得回 BIOS 翻开关了。5.2 权限报错和服务启动失败的排查“docker 权限错误怎么解决”是高频关键词最典型的现象是执行docker ps报permission denied while trying to connect to the Docker daemon socket。原因就是当前用户不在 docker 组里解决方案在 1.2 节给了命令。还有一种隐蔽情况用户虽然加了组但当前 SSH 会话还是旧身份的需要重新登录或者执行newgrp docker。socker权限如果被手动改过也会造成类似报错。把/var/run/docker.sock权限改回660属主改成root:dockersudo chown root:docker /var/run/docker.sock sudo chmod 660 /var/run/docker.sock服务启动失败的情况比较多样。先看服务状态和日志sudo systemctl status docker sudo journalctl -u docker --no-pager | tail -n 100常见原因之一是/etc/docker/daemon.json写坏了。这个文件是 JSON 格式多个配置项要用英文逗号分隔少一个逗号或者多了注释都会导致服务起不来。我建议写完配置后先跑一遍sudo docker daemon --validate或者用任何 JSON 校验工具确认格式正确再重启服务。另一个常见原因是磁盘空间不足Docker 日志和数据占满分区后守护进程也会异常退出df -h看一眼盘符用量就能定位。5.3 网络不通与镜像拉取速度优化容器网络不通要分情况判断。同一个 Compose 网络里的容器互访通过服务名如果 ping 不通先确认它们是否在同一个自定义网络里。容器访问宿主机优先用host.docker.internal这个特殊域名在 Linux 上可能需要加--add-hosthost.docker.internal:host-gateway才能用。很多人在容器里访问本地 MySQL 失败都是因为直接用localhost把请求发到自己容器的回环地址上自然连不通。还有一种常见问题容器内访问外网失败但宿主机正常。这是因为 Docker 的 NAT 规则被防火墙覆盖了。排查思路很简单检查防火墙策略里的 FORWARD 链允许 Docker 网桥的流量转发。如果环境不允许改防火墙就把容器网络模式改成 host 模式测试但要注意端口冲突风险。镜像下载慢的优化手段实际运维里最通用的是配置镜像源。这是 Docker 加速拉取的标准做法不属于什么特殊操作。在 Docker Desktop 的 Settings - Docker Engine 里找到配置项填入格式类似的镜像源地址即可。Linux 环境则是修改/etc/docker/daemon.json{ registry-mirrors: [https://你的镜像源地址] }修改后重启 Dockersudo systemctl restart docker配置生效后拉镜像的速度明显提升特别是几个 GB 的大基础镜像。如果团队内部有容器仓库也可以直接把拉取地址换成内网仓库的前缀做法一致效果更稳。注意不要同时堆一堆镜像源选一个稳定可用的就行配置多了反而因为重复尝试拉取拖慢速度。5.4 清理与维护别让小主机被镜像堆满容器和镜像会把磁盘一点一点吃掉尤其是经常构建新镜像的开发机。养成定期清理的习惯很重要docker system df docker image prune -f docker container prune -f docker system prune -afdocker system df会列出镜像、容器、数据卷各自占用的空间我每次部署完都会先执行一次心里有数。prune系列命令删掉的是无用镜像和停止的容器不影响数据卷。docker system prune -af更激进会把未使用的资源一次性全清掉执行前确认不是在生产环境手滑。我这里想单独提醒一句数据卷不在默认 prune 范围内但如果用了docker system prune -af --volumes它会把未使用的数据卷一起删。这个命令我几乎不使用因为误删数据卷的成本太高了。最后再分享一个经验。很多人第一次用容器跑 MySQL觉得启动成功就万事大吉结果宿主机一重启容器没自动起来服务全挂了。所以从第一天起所有常驻服务都养成加--restart unless-stopped的习惯用 Compose 就写restart: unless-stopped。这条规则解决了 99% 的“机器重启后服务消失”问题。如果你还不知道这个参数建议打开你现有的每条启动命令把-d后面补上它哪怕只是开发环境也值得因为它帮你省掉的是半夜爬起来查日志的时间。
返回列表