news 2026/11/2 7:58:39

Docker实战:从安装报错到MySQL与Redis容器化部署

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Docker实战:从安装报错到MySQL与Redis容器化部署

我搜了一圈云原生和 Docker 相关的热搜词,发现一个很有意思的现象:排在前面的大多数不是"什么是容器"这类概念问题,而是"docker 安装失败""permission denied while trying to connect to the docker api""virtualization support not detected"这种报错排查。这说明大家的痛点根本不是理论不够,而是卡在了最基础的"跑起来"这一步上。作为一个从虚机时代一路折腾到 K8s 的过来人,这篇文章我就按自己实际踩坑的路径来写,从为什么云原生绕不开 Docker,到安装、核心概念、常用命令,再到用 MySQL 和 Redis 主从两个实战案例把整个流程串起来。文章会尽量把每个"坑"背后的原因讲清楚,而不是只甩给你一句"把命令粘贴回车"。

1. 为什么"云原生"绕不开 Docker 容器技术

1.1 容器和虚拟机的本质区别

先解决一个很多人问过我的问题:我有 VMware、有 VirtualBox,为什么还要用 Docker?

虚拟机做的事情是把你的一台物理主机切成很多台"虚拟电脑",每一台都有完整的操作系统内核。你装一个 Ubuntu 虚拟机,里面跑着全套的 systemd、网络服务、用户体系,没个大几 GB 根本起不来。而容器完全不一样,它直接共用宿主机操作系统内核,只是在用户空间做了隔离和限制。打个比方,虚拟机像是你在同一栋楼里隔出好多套独立公寓,每套都要配全套家电;容器更像是给每个租客定制了独立的房间门禁和家具,但水电网管道是共享的。

这个差异带来最直接的影响就是:容器极轻。一个 Ubuntu 镜像往往只要几十 MB 甚至几 MB,启动时间以毫秒计算,一台普通笔记本跑几十个容器毫无压力。热搜里那个"n100 跑 20 个 docker",我自己在低功耗小主机上确实跑过接近 30 个容器,内存占用还不到 8GB,换成虚拟机早卡死了。

1.2 Docker 到底解决了什么问题

很多人说 Docker 解决了"环境一致性"问题,这没错,但我觉得更本质的是它改变了软件交付的方式。

以前部署一个 Java 项目,你需要写一份部署文档:JDK 什么版本、Tomcat 用什么版本、MySQL 初始化脚本怎么执行、Redis 配置怎么改。这份文档到了运维手里,大概率要走一套"被开发坑过的经验"流程,环境不一样就各种踩坑。现在有了 Docker,开发者直接把整个运行环境连同代码一起打包成一个镜像,镜像推到仓库里,运维拉下来docker run就完事。镜像就是一个"上帝视角"的可执行文件,它把操作系统里需要的所有依赖都锁死在里面。

这其实就是云原生的基石。不管是后面的 Kubernetes、Service Mesh 还是 Serverless,底层都是拿容器作为统一交付单元。你哪怕暂时用不到 K8s,先把 Docker 理解透,就等于把云原生这条路上最难啃的那块骨头啃掉了。

2. 环境准备:Docker 安装与启动的那些坑

2.1 Windows 和 macOS 上安装 Docker Desktop 的正确选择

官方推荐方式都是安装 Docker Desktop。Windows 上坑最多,十个人里至少有五个人栽在"Virtualization support not detected"或者"WSL 2 内核未安装"这些报错上。macOS 相对省心,Intel 芯片和 Apple Silicon 下载对应版本直接拖进 Applications 就行。

Windows 端我建议按这个顺序来检查:

  1. 确认 CPU 虚拟化已开启。任务管理器 -> 性能 -> CPU,看"虚拟化"是不是"已启用"。没启用就进 BIOS 打开 Intel VT-x 或 AMD-V,这一步绕不过去。
  2. Windows 功能里开启"虚拟机平台"和"适用于 Linux 的 Windows 子系统"。控制面板 -> 程序 -> 启用或关闭 Windows 功能,这一步做完大概率会要求重启。
  3. 安装 WSL2 内核更新包。微软官网有独立安装包,注意 Windows 10 和 11 的版本要求略有不同。
  4. 最后才装 Docker Desktop。装完启动,如果还报 virtualization support,多半是 BIOS 里虚拟化没开全,回头检查第 1 步。

2.2 Ubuntu 和 CentOS 上安装 Docker 的高频报错

Linux 上装 Docker 相对简单,但版本不一样,坑也不一样。

Ubuntu 上我比较推荐用官方 apt 源安装,而不是随手apt install docker.io。原因很简单:apt 源里的 docker.io 版本感觉总是慢半拍,有些新版功能用不上。官方源安装步骤大概是:

sudo apt-get update sudo apt-get install ca-certificates curl gnupg lsb-release curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null sudo apt-get update sudo apt-get install docker-ce docker-ce-cli containerd.io

CentOS 7 用户要注意,老版本系统装新版 Docker 经常报内核兼容问题,特别是 OverlayFS 存储驱动。搜热搜里那个"centos7 升级 docker",大概率是想把 Docker 1.x 升到 20.x。我的建议是:如果内核是 3.10 的 CentOS 7,别硬上太新的 Docker,用 docker-ce 的 20.10 系列比较稳。

2.3 安装完成后第一件事:配置用户组

这一步非常关键,而且很多人就是栽在这里——热搜里那个"permission denied while trying to connect to the docker api at unix:///var/run/docker.sock"就是典型。

Docker 的守护进程默认以 root 用户运行,socket 文件/var/run/docker.sock的权限只有 root 能访问。普通用户执行docker ps就会报 permission denied。正确解法是把你自己的用户加入 docker 用户组:

sudo groupadd docker # 如果 docker 组不存在 sudo usermod -aG docker $USER newgrp docker

然后重新登录终端再试docker ps。这里有个安全提示:加入 docker 用户组的用户相当于拥有了 root 权限,因为可以通过挂载宿主目录的方式完全控制宿主机。个人开发机无所谓,生产服务器上要谨慎。

3. 镜像、容器、仓库:理清三个核心概念

3.1 镜像和容器:一个是"安装包",一个是"运行中的进程"

很多新手会把镜像和容器搞混。最简单的理解方式:**镜像是模板,容器是模板的运行实例。**你从 Docker Hub 上docker pull mysql:8.0,拉下来的是一个 mysql 镜像,它像是一个"安装包";每次docker run都会基于这个镜像创建出一个全新的容器,像是运行中的项目进程。

镜像还有一个特点是分层结构。镜像不是一个大文件,而是由很多只读层叠加起来的。你修改了 Dockerfile 里的一行,重新构建时 Docker 会复用没有变化的层,只构建变化的那几层。这就是为什么构建镜像通常比传统的"重新部署一次"快很多。

容器之间是完全隔离的。你在一个容器里rm -rf /都不会影响宿主机和其他容器。这是 Linux 内核的 namespace 和 cgroup 特性在起作用,前者负责隔离视图,后者负责限制资源。

3.2 仓库和镜像源:为什么docker pull总是那么慢

镜像仓库好比应用商店,Docker Hub 是最主要的公共商店。你下载镜像用docker pull,本质上是和 Docker Hub 通信。由于网络环境的原因,在国内直连 Docker Hub 经常慢到怀疑人生,甚至超时失败。热搜里"docker 镜像下载慢""docker 镜像源"说的就是这个问题。

解决方案是配置镜像加速器。修改或创建/etc/docker/daemon.json:

{ "registry-mirrors": [ "https://docker.m.daocloud.io" ] }

然后重启 Docker:

sudo systemctl restart docker

这里要提醒一句,网上很多加速地址说挂就挂,不要只信一个,最好配两到三个备用。配置完可以用docker info查看 Registry Mirrors 是否生效。

3.3 自建镜像仓库的适用场景

如果只是个人开发,用公共仓库加加速器就够了。但如果你要在公司内部分发镜像,或者构建产物比较敏感,那就需要自建仓库。最轻量的方案是直接跑一个registry:2容器:

docker run -d -p 5000:5000 --name registry -v /opt/registry:/var/lib/registry registry:2

然后给镜像打标签推到本地仓库:

docker tag my-app:latest localhost:5000/my-app:latest docker push localhost:5000/my-app:latest

自建仓库注意两点:一是要给 Docker daemon 配置insecure-registries或者用 HTTPS,否则默认拒绝非 HTTPS 仓库;二是如果多人使用,最好加一层认证,配合一个简单的镜像清理策略,否则仓库体积会越滚越大。

4. 高频命令与踩坑:从拉镜像到跑通第一个容器

4.1 命令速查:这几条覆盖了你 80% 的日常操作

我平时最常用的容器操作命令,整理了一张表:

操作命令说明
拉取镜像docker pull nginx:latest不指定 tag 默认 latest
运行容器docker run -d -p 8080:80 nginx-d 后台运行,-p 端口映射
查看容器docker ps -a加 -a 包含已停止的容器
查看日志docker logs -f 容器名-f 持续跟踪
进入容器docker exec -it 容器名 /bin/bash交互式进入
停止容器docker stop 容器名给容器发送停止信号
删除容器docker rm 容器名只能删已停止的
删除镜像docker rmi 镜像名有容器引用时会失败
构建镜像docker build -t my-app:1.0 .-t 指定名字
查看资源占用docker stats实时查看 CPU/内存

有几个容易忽略的细节。docker ps -a和docker ps的差别一定要记住,很多人以为容器没了,其实是容器停了,要用-a才能看到。docker rm -f可以强制删除运行中的容器,但生产环境慎用,因为这样删除容器不会走优雅停止流程,可能造成数据不一致。

4.2 端口映射和数据卷:最容易被忽略的两个参数

端口映射的原理很简单:容器内有自己的网络命名空间,宿主机访问不到容器的 IP。-p 8080:80表示把宿主机的 8080 端口转发到容器的 80 端口。我见过好几个人访问容器内的 MySQL 失败,一查是只做了-p 3306:3306却没注意宿主机防火墙没放行,这个锅有时候真的不在 Docker。另外,-p 3306:3306这种写法端口一样容易混淆,建议映射到不一样的端口,比如-p 13306:3306,一眼就能看出来是容器里的数据库。

数据卷是另一个容易坑人的点。容器是无状态的,你把文件写进容器里,容器一删,数据就全没了。怎么办?用-v把宿主机目录挂载进容器:

docker run -d -p 3306:3306 \ -v /opt/mysql-data:/var/lib/mysql \ -e MYSQL_ROOT_PASSWORD=123456 \ mysql:8.0

这样 MySQL 的数据实际写在了宿主机的/opt/mysql-data目录,容器删了重建数据还在。我强烈建议任何有状态的服务(数据库、缓存、文件存储)都挂载数据卷,这是血泪教训换来的经验。

4.3 网络不通:从 ping 不通到连不上的排查链路

热搜里有"docker 网络不通",这个问题的排查链路其实很固定。容器网络有几个维度:

  • 容器之间互相访问:推荐用自定义 bridge 网络,互相用容器名当 DNS 解析。
  • 宿主机访问容器:用curl 宿主机IP:映射端口。
  • 外部机器访问容器:防火墙 + 端口映射两个前提缺一不可。

具体排查时我习惯按这个顺序来:

  1. 先确认容器是不是在运行:docker ps。
  2. 看端口映射是否生效:docker port 容器名。
  3. 在宿主机上curl localhost:映射端口,通了说明容器没问题,问题在外部。
  4. 用docker exec -it 容器名 curl 目标地址从容器内部测试网络,不通可能是容器内 DNS 配置问题。
  5. 查看容器网络模式:docker inspect 容器名 | grep NetworkMode。

如果是跨主机的容器互通,那就别折腾自建 bridge 了,直接上 overlay 网络或者把服务端口暴露出来,否则你会被网络问题耗死。

5. 实战一:Docker 部署 MySQL 8.0 并正常访问

5.1 初始化配置和时区、字符集的坑

mysql 8.0 算是我用 Docker 部署最多的一个镜像,因为公司内部需要环境的人实在太多了。正常拉镜像跑起来很简单:

docker run -d \ --name mysql8 \ -p 13306:3306 \ -e MYSQL_ROOT_PASSWORD=Root@123 \ -e TZ=Asia/Shanghai \ -v /opt/mysql8/data:/var/lib/mysql \ -v /opt/mysql8/conf:/etc/mysql/conf.d \ mysql:8.0 --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci

注意最后那两个参数是传给 MySQL 服务的启动参数,不是 Docker 的参数。看着简单,但实际中总是有人忘记指定字符集,导致建表默认把 emoji 给拒了。MySQL 8.0 默认字符集其实已经是 utf8mb4 了,但 collation 的默认值仍然有隐患,建议显式指定。

容器跑起来之后,进入容器用客户端验证:

docker exec -it mysql8 mysql -uroot -p

如果报Access denied for user 'root'@'localhost',多半是环境变量里的 MYSQL_ROOT_PASSWORD 没生效,或者数据卷里已经有旧版初始化数据了。删掉容器和旧数据目录,重新跑一次。

5.2 "docker 安装 mysql 失败"的高频原因

热搜里"docker 安装 mysql 失败"出现的频率很高,我排查下来九成是这几种情况:

  • 镜像拉不下来:网络问题,配加速器。
  • 端口被占用:-p 3306:3306报 bind: address already in use,换宿主端口或docker ps -a查一下是不是有残留容器。
  • 数据目录权限不对:mysqld: Cannot change permissions of the file '/var/lib/mysql/'这类报错,通常是宿主机挂载目录的权限和容器内 mysql 用户(uid 999)不匹配。解法是chown -R 999:999 /opt/mysql8/data。
  • 内存不足:MySQL 8.0 起步占 400MB 左右内存,小机器上容易被 OOM killer 干掉,docker stats看一眼。

5.3 从宿主机和远程访问容器内的 MySQL

容器起来了,映射端口也对了,但本地连接不上?先确认 MySQL 用户允许的 host。因为容器里有自己的网络,MySQL 默认 root 只允许 localhost 或者容器网络内访问,你用宿主机连的时候相当于走网络连接,很可能会被拒绝。

创建远程访问用户是正路:

CREATE USER 'app'@'%' IDENTIFIED BY 'App@123'; GRANT ALL PRIVILEGES ON *.* TO 'app'@'%'; FLUSH PRIVILEGES;

然后检查端口监听:ss -tlnp | grep 13306,确认确实监听在宿主机上。如果用的是 Windows 的 Docker Desktop,还有一种经典问题:容器端口通了,但localhost:13306连接时快时慢甚至拒绝,这是 Docker Desktop 在 Windows 上的网络转发层偶发抽搐,重启 Docker Desktop 或者用127.0.0.1显式连接可以缓解。

6. 实战二:Redis 主从架构与 Docker Compose 编排

6.1 为什么要用 Docker Compose 而不是一条条 docker run

搜"docker compose"的人很多,但真正理解"为什么需要 compose"的人不多。说到底,docker run是单条命令,你部署一个 MySQL 要敲一长串参数,部署一个微服务要敲一长串参数,如果服务有依赖关系,还得手动控制启动顺序。Compose 把这一切声明式地写在一个 YAML 文件里,一条docker compose up -d全部搞定。

我之前部署一个前后端分离项目加数据库、缓存,一共六个服务,用 docker run 拼命令的时候头皮发麻。改成 compose 之后,每次新机器上部署就三行命令:docker compose up -d、docker compose ps、docker compose logs -f。这在微服务架构下尤其重要,因为服务数量一多,单条命令已经无法维护了。

6.2 Redis 主从编排:一次搞懂容器互联和配置挂载

用 Redis 主从来演示 compose 再合适不过,因为主从之间天然有网络依赖,能让你理解容器名解析和自定义网络是怎么工作的。

目录结构先规划好:

redis-compose/ ├── docker-compose.yml ├── redis-master/ │ └── redis.conf └── redis-slave/ └── redis.conf

主节点的 redis.conf 很简单,主要是开启保护模式允许容器网络访问:

bind 0.0.0.0 protected-mode yes port 6379 appendonly yes

从节点的配置多一行,指定主节点地址。注意这里用的是容器名redis-master,不是 IP。在 compose 自动创建的网络里,服务名天然就是可解析的 DNS 名称:

bind 0.0.0.0 protected-mode yes port 6379 appendonly yes replicaof redis-master 6379

docker-compose.yml 长这样:

version: '3.8' services: redis-master: image: redis:7.0 container_name: redis-master ports: - "16379:6379" volumes: - ./redis-master/redis.conf:/usr/local/etc/redis/redis.conf - ./redis-master/data:/data command: ["redis-server", "/usr/local/etc/redis/redis.conf"] redis-slave: image: redis:7.0 container_name: redis-slave depends_on: - redis-master ports: - "16380:6379" volumes: - ./redis-slave/redis.conf:/usr/local/etc/redis/redis.conf - ./redis-slave/data:/data command: ["redis-server", "/usr/local/etc/redis/redis.conf"]

启动:

docker compose up -d docker compose ps

然后在另一个容器里验证主从状态:

docker exec -it redis-master redis-cli INFO replication docker exec -it redis-slave redis-cli INFO replication

从节点的role字段如果显示slave,master_link_status是up,就说明主从关系建立成功。这里有一个我在实际项目中反复遇到的坑:**depends_on只保证启动顺序,不保证主节点已经 ready。**Redis 这种轻量服务通常秒级起来,问题不大,但如果换成 MySQL 或者一些启动慢的服务,依赖方很可能前几次连接直接失败。生产环境建议在应用层做重试,而不是完全依赖 compose 的启动顺序。

6.3 Dockerfile 构建镜像:从"用别人镜像"到"打包自己应用"

跑通 Redis 主从之后,你大概率就该想一件事:怎么把我的 Java/Node/Python 项目打成镜像?这就是 Dockerfile 的用武之地。

拿一个常见的 Java Spring Boot 项目举例。如果你用的是 Maven 构建,Dockerfile 可以写成多阶段构建:

FROM maven:3.8-openjdk-11 AS build 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 --from=build /app/target/*.jar app.jar EXPOSE 8080 ENTRYPOINT ["java", "-jar", "app.jar"]

多阶段构建的好处是最终镜像里没有 Maven 和源码,只有编译好的 jar 包和运行环境,镜像体积可以从 700MB 缩到 200MB 左右。想直接在 IDEA 里打包镜像的话,装一个 Docker 插件,把 Dockerfile 放在项目根目录,右键就能 Build Image,然后在 Run Configuration 里配置容器启动方式。热搜里有"idea 打包 docker 镜像",指的就是这个流程。

如果是 Node.js 项目,核心思路也一样,只是注意用.dockerignore把node_modules和.git排除掉,否则上下文传上去体积大还慢。

6.4 微服务部署组合拳:Compose 编排 + 多容器互联

当你从单容器走到 compose,其实已经摸到微服务部署的门槛了。一套典型的微服务本地环境,无非就是:网关、注册中心、若干业务服务、MySQL、Redis、消息队列,全部用 compose 编排在一个网络里。

我的习惯是分文件管理:docker-compose.yml放业务服务,docker-compose.infra.yml放中间件,用docker compose -f docker-compose.infra.yml up -d先启动中间件,再启动业务服务。或者用extends字段做基础配置复用,避免每个服务重复写同样的环境变量和日志配置。如果服务之间有调用关系,depends_on加condition: service_healthy比我上面说的裸depends_on要可靠得多,前提是你给依赖的服务定义了 healthcheck。这个细节在本地开发看不出太大区别,一旦部署到 K8s 里,存活探针就变成了基本操作。

7. 写在最后:我的实际体会

文章写到这里,基本把 Docker 从安装到实战的一条完整路径捋了一遍。回想我自己从第一次docker run hello-world到现在用容器编排整套微服务环境,最大的感受是:**Docker 不是一个需要你背命令的工具,而是一种重新理解软件交付的方式。**很多你以前觉得难的事,比如"在我机器上明明是好的""这个环境的 MySQL 版本怎么又对不上",在容器化之后都变成了同一个镜像、同一份配置,问题自然就消失了。

最后再分享一个小技巧:如果你刚开始接触,不要一上来就追求把所有服务容器化。先拿 Redis 或者一些无状态的小工具练手,跑通了再碰 MySQL 这种有持久化需求的,最后再尝试自己写 Dockerfile 打包应用。顺序反了,很容易被一开始的报错劝退。Docker 这个东西,只要你把镜像、容器、卷、网络这四个概念在脑子里建立起来,剩下的都是查命令的事。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/23 14:30:33

麒麟V10上.NET图形程序报错?libgdiplus安装配置与避坑指南

简介:这份文档面向在国产 Kylin 操作系统上部署 .NET 或依赖 Windows GDI 图形接口应用的运维与开发人员,聚焦 LibGdiPlus 的安装与配置这一关键环节,帮助解决跨平台程序在 Linux 环境下因缺少 GDI 支持而无法正常绘图、渲染的问题。资源包共…

作者头像 李华
网站建设 2026/10/24 1:33:23

鸿蒙Flutter流式解析JSON:json_events内存优化实践

做鸿蒙 Flutter 开发的朋友,应该都经历过这种崩溃现场:后台同步一份几十 MB 的 JSON,你下意识jsonDecode下去,内存直接爆掉。我在做鸿蒙版日志上报模块时,就被一份 200MB 的堆栈 JSON 干翻过。后来把整套解析链路切成了…

作者头像 李华
网站建设 2026/10/29 14:27:12

OSPF区域划分与LSA类型详解:虚链路、Stub/NSSA与多进程重分发避坑指南

简介:这是一份面向网络工程师与HCIP/CCNP备考者的OSPF协议细节汇总资料,系统梳理了链路状态路由协议的核心机制与实验要点。内容覆盖LSA七种类型、区域划分原则、虚链路与多进程路由导入、Stub与NSSA特殊区域、网络类型、路由汇总及安全认证等进阶主题&a…

作者头像 李华
网站建设 2026/10/24 20:44:52

从“一键部署”到“一地鸡毛”:Docker容器化实践与排坑全复盘

“一键部署”这四个字,大概是我入坑 Docker 最初也是最天真的信仰。某天我刷到一篇“一行命令跑起 YOLO 检测”的教程,脑子里立刻浮现出点一下鼠标、目标检测程序就在浏览器里跑起来的画面。结果呢,Win 上装 Docker Desktop 先被“virtualiza…

作者头像 李华
网站建设 2026/10/24 16:44:47

OpenClaw云端部署实战:基于Docker与安全组从零上云指南

1. 项目拆解:OpenClaw(Clawdbot)为什么要跑在云端OpenClaw(很多人叫它 Clawdbot)本质上是一个自动化智能体框架,你可以把它理解成一个“带爪子的机器人管家”:它负责监听消息、读取任务、调用各…

作者头像 李华
网站建设 2026/10/24 6:31:05

OpenClaw投研实践:本地大模型Agent的部署与自动化工作流

简介:太平洋证券《AI投研应用系列之四:OpenClaw投研实践——从部署到应用》是一份面向金融投研人员与AI应用开发者的实战报告,解决开源框架OpenClaw从环境搭建到投研场景落地的关键问题。报告对比纯本地、WSL2云端模型、纯云端三种部署方案的…

作者头像 李华