☰
Docker核心操作与实战:从镜像容器到MySQL/Redis部署
2026/10/7 3:12:44 网站建设 项目流程

1. 先搞懂 Docker 到底解决什么问题

聊到 docker 使用,我身边不少同事第一反应还是“跟虚拟机差不多”。这个理解其实不准确,也很容易导致后面的使用方向跑偏。Docker 本质上是一种操作系统级别的虚拟化技术,它跟 VMware 那种跑完整操作系统的虚拟机完全是两回事。

最直观的差别就是启动速度。虚拟机启动要几分钟,Docker 容器基本是秒级启动。原因是容器直接共享宿主机内核,不需要自己带一套操作系统。docker 使用中最核心的价值可以归结为三点:环境一致性、资源隔离、快速交付。环境一致性这点我感触最深。以前在项目里配 MySQL、Redis,不同机器上装出来的版本、配置文件、依赖库五花八门,经常出现“我这边跑得好好的,到你那就挂了”。用了 Docker 之后,镜像把运行环境完整打包,开发、测试、生产用的都是同一个镜像,环境差异问题直接被消灭掉。

资源隔离就更好理解了。每个容器都像单独的小房间,里面跑着进程,房间之间互不干扰。资源不够用可以限制,跑完可以直接拆掉重来,宿主机本身干净得很。我见过太多人在自己电脑上装各种服务,装了一堆依赖,系统越用越慢,最后只能重装。用 Docker 之后再也不存在这种烦恼。

再说三个必须记牢的核心概念:镜像(Image)、容器(Container)、仓库(Registry)。镜像就是打包好的模板,类似安装光盘,只读不可改。容器是从镜像运行出来的实例,可以启动、停止、删除,所有写入操作都发生在容器层。仓库是存放镜像的地方,Docker Hub 是最大的公共仓库,企业里也会搭私有仓库。

拿做菜来类比的话,镜像就是菜谱加所有配料的组合,容器就是按菜谱做出来的一道菜,仓库就是存放各种菜谱的图书馆。你从图书馆借菜谱(拉镜像),按菜谱做菜(创建容器),吃完收拾干净(删掉容器),随时可以重新做一道一模一样的。这套逻辑想通了,后面的 docker 使用就是围绕这几个概念的命令操作而已。

2. 安装部署:Windows 和 Ubuntu 全流程

2.1 Windows 上装 Docker Desktop 的大坑

Windows 用户基本都会选 Docker Desktop,但安装过程中最常见的报错就是 “Virtualization support not detected” 或者 “Docker Desktop failed to start because virtualisation support wasn't detected”。这个报错的原因很直白:虚拟化功能没开或者没启用对应的 Windows 组件。

排查顺序建议从下面几步走。第一步按 Ctrl+Shift+Esc 打开任务管理器,看“性能”标签页里“虚拟化”状态。如果显示“已启用”说明 BIOS 层面没问题;显示“已禁用”,就需要重启进 BIOS,找到 Intel VT-x 或 AMD-V 相关选项开启,不同主板菜单名不一样,常见的有 Virtualization Technology、SVM Mode、VT-d,反正看到含 Virtual 或 SVM 的选项都开起来。

第二步确认 Windows 功能。如果跑的是 WSL2 后端,要保证“适用于 Linux 的 Windows 子系统”和“虚拟机平台”这两个功能处于开启状态。在控制面板的“启用或关闭 Windows 功能”里勾上,重启后生效。我自己实测下来,WSL2 后端比传统 Hyper-V 后端更稳,启动速度也更快,所以强烈建议在 Docker Desktop 设置里选 WSL2,而不是 Hyper-V,特别是老机器,WSL2 对硬件的兼容性要好很多。

第三步是检查 Hyper-V 的状态。在管理员 PowerShell 里执行 systeminfo,最后几行会显示 Hyper-V 要求是否全部满足。有两条“已检测到虚拟机监控程序”说明当前已经启用。还有一个隐藏很深的坑:如果你以前装过其他虚拟机软件,比如 VirtualBox 或者 VMWare,它们可能锁定了一些虚拟化资源,导致 Docker Desktop 的引擎起不来。遇到这种情况,要么把虚拟机软件彻底卸载,要么在 Docker Desktop 里切换后端模式。

2.2 Ubuntu 下装 Docker Engine 的操作

Linux 下没有 Desktop 图形界面那套负担,一个命令就能搞定。我个人推荐用官方安装脚本,省事,而且会匹配当前系统版本:

curl -fsSL https://get.docker.com | sh

如果不想用脚本,也可以走 apt 源安装:

sudo apt update sudo apt install docker.io

装完之后设置开机自启并手动拉起来:

sudo systemctl enable --now docker docker version

这里有个 docker 使用中非常经典的问题:普通用户执行 docker 命令会报 Permission denied while trying to connect to the Docker daemon socket。原因很简单,docker 的 socket 文件默认只对 root 和 docker 组成员开放。解决办法是把当前用户加进 docker 组:

sudo usermod -aG docker $USER

注意执行完命令后要重新登录或者重启 shell 才能生效,否则还是会报一样的错误。把用户加入 docker 组等于授予了等同于 root 的特权,只有你自己用的开发机可以这么干,生产服务器建议老老实实配 sudo。

2.3 镜像下载慢怎么解决

镜像加速是 docker 使用绕不开的话题。国内环境直接从 Docker Hub 拉镜像经常卡到怀疑人生。解决方案是配置镜像加速器。编辑 /etc/docker/daemon.json(Windows 上通过 Docker Desktop 设置界面填就行):

{ "registry-mirrors": ["https://<你的加速器地址>"] }

改完后执行 systemctl daemon-reload && systemctl restart docker。目前网上公开的加速器有不少已经停止服务,建议自己搜一下当前可用的公共镜像站,或者用云厂商提供个人专属加速地址。配置完成后,docker pull 的速度通常能提升一个数量级。

3. 镜像与容器的核心操作

3.1 镜像生命周期管理

docker pull 拉取镜像后,用 docker images 查看本地已有镜像列表。清理时用 docker rmi 镜像ID,删除前要确保没有容器在使用它,否则会报冲突。

当你想给别人分发镜像,或者把镜像拷到离线环境,需要用到两个命令:

docker save -o myimage.tar 镜像名:tag docker load -i myimage.tar

我经常在办公网络和机房之间的离线环境用这招,一条命令导出,到目标机器一条命令导入,比从公共仓库拉取稳定太多。另外 docker tag 可以给镜像打标签,没有 tag 的镜像推不到私有仓库。

3.2 容器起停与进入容器内部

创建容器最核心的命令是 docker run,先看一个标准示例:

docker run -d --name nginx-test -p 8080:80 nginx

参数含义:-d 表示后台运行,--name 指定容器名,-p 8080:80 把宿主机 8080 端口映射到容器 80 端口。容器运行后,用 docker ps 看状态,docker ps -a 能看到停止状态的容器。停止的容器可以 docker start 重新启动。

进入容器内部调试的话用 exec:

docker exec -it nginx-test bash

-it 表示分配一个交互式终端。这个命令几乎每天都要用,进入容器后可以查看运行日志、检查配置、手动执行脚本。不过有一点要提醒:为了保持镜像精简,很多官方镜像里没有 bash,比如 alpine 系列只有 sh,进入时要写成 docker exec -it 容器名 sh。

查看日志同样常用:

docker logs -f nginx-test

服务半天起不来,第一步就是看日志,不要瞎猜。日志会默认保存所有标准输出和标准错误,加 -f 参数可以持续追踪。

3.3 数据卷和端口映射

容器本身是短暂的,删除容器后所有写入的文件跟着消失。为了持久化存储,需要用 -v 参数把宿主机的目录挂载到容器里:

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

-v /data/mysql:/var/lib/mysql 的含义是:宿主机 /data/mysql 目录映射到容器内的 /var/lib/mysql 目录。MySQL 的数据文件会写到容器内这个目录,实际上落到了宿主机硬盘上。只要宿主机数据目录在,哪怕容器删了重建一个,数据也不丢。

端口映射对应的是 -p 宿主机端口:容器端口。它的作用是让外部能够访问到容器内的服务。如果容器内 nginx 监听 80 端口,你希望外面通过 8080 访问,就写 -p 8080:80。这个方向别搞反,第一个是宿主机端口,第二个是容器端口。

3.4 容器之间如何互联

很多场景下多个容器需要互相通信,比如应用容器要连数据库容器。比较老的教程会教你用 --link 参数,但这个方式现在已经不推荐了。正确做法是创建自定义网络,然后把容器都加入同一个网络:

docker network create my-net docker run -d --name mysql8 --network my-net -p 3306:3306 mysql:8.0 docker run -d --name myapp --network my-net myapp-image

在同一个自定义网络里,容器之间可以直接通过容器名访问,不需要暴露端口。myapp 容器里连接数据库可以直接写 mysql8:3306,Docker 内置 DNS 会自动解析。用自定义网络的好处是不依赖端口顺序,容器重建后 IP 变了也不怕,只要容器名不变,其他容器照常访问。

4. 实战一:MySQL 8.0 部署与访问

4.1 单机部署的命令和参数选择

先用最标准的命令起一个 MySQL 8.0 容器:

docker run -d \ --name mysql8 \ -p 3306:3306 \ -e MYSQL_ROOT_PASSWORD=YourPassword \ -e TZ=Asia/Shanghai \ -v /data/mysql:/var/lib/mysql \ -v /data/mysql-config:/etc/mysql/conf.d \ --restart=always \ mysql:8.0

几个参数展开说明一下。MYSQL_ROOT_PASSWORD 是初始化 root 密码必填项,不写这个变量容器无法初始化。TZ 设置时区,不设置的话容器默认 UTC 时间,比北京时间慢八小时,做日志排查会非常难受。第二个 -v 挂载的是配置目录,自定义的 my.cnf 可以放进宿主机 /data/mysql-config 目录,容器启动时会自动加载。--restart=always 表示 Docker 守护进程启动时自动拉起容器,一旦主机重启,MySQL 服务能跟着恢复,不用手动干预。

如果你是在小内存机器上部署,内存不足会导致 MySQL 容器反复重启甚至直接挂掉。建议限制一下资源:

docker run -d --name mysql8 -p 3306:3306 -e MYSQL_ROOT_PASSWORD=123456 --memory=1g --memory-swap=1g mysql:8.0

--memory=1g 限制容器最多使用 1G 内存,如果机器本身只有 2G 内存,这招能防止 MySQL 把主机搞死。实测在 2G 内存的机器上跑 MySQL 8.0,不限制内存的话很容易把所有内存吃掉,加上限制后稳定很多。

4.2 访问容器内 MySQL 与外部访问

从容器内部访问用的是 exec:

docker exec -it mysql8 mysql -uroot -p

从宿主机或者其他机器访问,直接用映射出来的端口:

mysql -h127.0.0.1 -P3306 -uroot -p

这里有个非常常见的坑:用 Navicat、DBeaver 这类客户端连接 MySQL 8.0 容器时,可能会报错 Authentication plugin 'caching_sha2_password' cannot be loaded。这是 MySQL 8.0 默认认证插件变更导致的。解决办法有两条路:一是把客户端升级到支持 caching_sha2_password 的版本;二是进入容器把 root 账号的插件改回 mysql_native_password:

docker exec -it mysql8 mysql -uroot -p ALTER USER 'root'@'%' IDENTIFIED WITH mysql_native_password BY 'YourPassword'; FLUSH PRIVILEGES;

执行完后,老客户端就能正常连接了。还要注意一个细节:初始化时如果没有设置 MYSQL_ROOT_HOST=% ,root 账号默认只能在容器内部登录,宿主机连接也会被拒。不过上面那个标准的 docker run 命令里虽然没有显式写 MYSQL_ROOT_HOST,MySQL 官方镜像默认会创建 root@% 允许任意主机访问,所以外部连接通常没问题。如果遇到 Access denied,就进容器手动补一条授权:

CREATE USER 'root'@'%' IDENTIFIED BY 'YourPassword'; GRANT ALL PRIVILEGES ON *.* TO 'root'@'%'; FLUSH PRIVILEGES;

另一个实际会遇到的问题是:局域网内其他机器访问不到数据库,但本机可以。优先检查宿主机的防火墙和云服务器的安全组,端口没放行一切都白搭。Ubuntu 上防火墙默认可能是开着的,执行 sudo ufw allow 3306/tcp 放行。

5. 实战二:Redis 主从架构部署

5.1 用 Docker 起一主一从

Redis 主从结构是生产环境的基础配置,目的很简单:主节点负责写,从节点同步数据并负责读,既能做读写分离,又能做数据冗余。以前没容器的时候,要准备两台机器,配 redis.conf,比较繁琐。现在用 Docker 几分钟搞定。

先把主节点跑起来:

docker run -d \ --name redis-master \ -p 6379:6379 \ -v /data/redis-master:/data \ redis:7 redis-server --appendonly yes

这里的 --appendonly yes 开启 AOF 持久化,数据写进 /data 目录,配合宿主机挂载实现持久化。然后创建自定义网络,把主从容器都放进去,方便互相通信:

docker network create redis-net docker network connect redis-net redis-master

或者更干脆,直接在创建主节点的时候加 --network redis-net,但网络创建要在容器之前。无所谓,容器先起来再加网络也没问题。

启动从节点,关键点是加 replicaof 参数声明主节点地址:

docker run -d \ --name redis-slave \ -p 6380:6379 \ --network redis-net \ -v /data/redis-slave:/data \ redis:7 redis-server --appendonly yes --replicaof redis-master 6379

注意这里用的不是 --slaveof,因为在 Redis 5.0 之后官方就把 slave 术语改成了 replica,参数也跟着变了。--replicaof redis-master 6379 表示从节点从 redis-master 这个主机名对应的 6379 端口同步数据。

5.2 验证主从状态是否正常

容器运行起来之后,进到从节点容器里检查复制状态:

docker exec -it redis-slave redis-cli info replication

关键信息看这三处:role 为 slave(新版本显示 role:slave),master_link_status 为 up,master_host 显示 redis-master。master_link_status 如果是 down,通常原因是从节点访问不到主节点,检查网络配置,确认两个容器在同一个自定义网络里。

验证主从同步最直观的方法是写一个 key 看能否同步过去:

docker exec -it redis-master redis-cli set test "hello" docker exec -it redis-slave redis-cli get test

能取到值说明复制链路没问题。还有一个很多人会忽略的点:主从节点都要配置持久化。如果主节点没开 AOF,重启后内存数据全丢,从节点同步到的数据也得重新全量复制,极端情况可能出现主从数据不一致。所以我个人建议主从都开 appendonly yes,稳妥。

6. 进阶玩法:青龙面板、DVWA 靶场、Kodbox 网盘

6.1 青龙面板和它的依赖管理

青龙面板是个定时任务管理工具,很多人拿它跑签到脚本、自动任务,热度一直很高。部署命令很简单:

docker run -d \ --name qinglong \ -p 5700:5700 \ -v /data/ql:/ql/data \ whyour/qinglong:latest

启动后浏览器访问 http://ip:5700,设置账号密码就能用。第一次登录后配置脚本,拉取脚本仓库时经常遇到的问题就是脚本拉不干净、依赖报错。最典型的报错是执行脚本时报 Cannot find module 'xxx',说白了就是脚本需要的 Node.js 依赖没装全。青龙面板里内置了“依赖管理”页面,进去先选类型 Node.js,然后填依赖名,点安装。

给两个我实测的经验。第一,青龙的服务端和脚本运行环境都是 Node.js,模块版本冲突经常出现,依赖装多了反而出问题,装的时候尽量按脚本作者的说明来,不要一股脑全装。第二,默认仓库源在国外,拉库速度很慢甚至超时,解决办法是在“配置文件”里配置镜像加速地址,或者把脚本仓库换成国内镜像仓库。很多脚本项目作者会在文档里额外给一份国内仓库地址,优先用那个。

6.2 用 Docker 快速搭 DVWA 靶场

DVWA 是学习 Web 安全的入门靶场,在 Kali 里搭建一套原本要装 Apache、MySQL、PHP,手工配置麻烦得很。用 Docker 一句话搞定:

docker run -d -p 8080:80 --name dvwa vulnerables/web-dvwa

访问 http://ip:8080,默认账号 admin,密码 password。首次访问页面底下有个 Create/Reset Database 按钮,点一下初始化数据库。如果点完进入登录页后发现报数据库连接错误,大概率是数据库初始化还没完成,等十几秒刷新页面就好。如果还是连不上,重启容器:

docker restart dvwa

容器重启后,内置数据库服务会重新拉起,一般就能恢复。这是个我踩过很多次的坑,一键起服务很方便,但等待初始化要有耐心。

6.3 Kodbox 个人网盘部署

Kodbox(可道云)是目前口碑不错的私有网盘方案,部署方式也是 Docker 化:

docker run -d \ --name kodbox \ -p 8081:80 \ -v /data/kodbox:/var/www/html \ kodbox/kodbox

浏览器访问 http://ip:8081,按提示设置管理员账号和存储目录。有个坑必须单独说:容器里写 www 目录需要写权限,宿主机目录权限不够的话,安装过程中会提示目录不可写。解决办法是给宿主机目录授权:

chmod -R 777 /data/kodbox

或者加 --privileged 参数启动容器。前者更可控,我一般只用 chmod 处理。Kodbox 这种 Web 程序对权限敏感,权限设置不合适会导致上传文件失败、无法生成缩略图等一系列诡异问题。

7. 常见问题排查与避坑实录

7.1 高频报错一页速查表

docker 使用过程中最常见的报错就那么几类,我把踩过的问题整理成一张表格,方便遇到问题直接查:

报错信息根本原因处理办法
Permission denied while trying to connect to the Docker daemon socket当前用户不在 docker 组sudo usermod -aG docker $USER 后重新登录
Cannot connect to the Docker daemon at unix:///var/run/docker.sockDocker 服务没启动systemctl start docker 或启动 Docker Desktop
Virtualization support not detectedBIOS 未开启虚拟化重启进 BIOS 打开 VT-x/AMD-V
Docker Desktop failed to start because virtualisation support wasn't detectedWSL2 或 Hyper-V 未启用启用“虚拟机平台”和 WSL 功能
failed to start docker application container engine引擎进程资源不足或 daemon.json 配置错误检查 daemon.json JSON 格式,释放内存
镜像拉取超时或速度极慢网络原因配置 registry-mirrors 加速器

7.2 网络不通的排查思路

容器之间互访不了,第一反应用 docker inspect 容器名 查看 IP 和网络配置。然后确认两个容器是否在同一个自定义网络里,不在就 docker network connect 连进去。

宿主机访问不了容器暴露的端口,先确认容器真的在监听端口:

docker ps docker exec -it 容器名 netstat -tulpn | grep 端口

如果容器内服务启动正常,宿主机还是访问不了,下一步就是防火墙。Ubuntu 下 ufw status 看防火墙状态,CentOS 下 systemctl status firewalld,把对应端口放行。云服务器还要看安全组规则是否放行端口。

容器访问不了外网,大多是 DNS 问题。容器内 ping 不通外网域名但能 ping 通 IP,说明 /etc/resolv.conf 的 DNS 没配好。在 daemon.json 里加全局 DNS 配置:

{ "dns": ["223.5.5.5", "114.114.114.114"] }

重启 Docker 后再试。这个配置能解决大部分容器内 DNS 解析不了的问题。

7.3 资源占用过高怎么办

小主机跑多个容器(比如 N100 这种低功耗平台跑二十个容器)是当下的热门玩法,但资源规划没做好很容易把整机拖垮。我个人的经验是三件事必须做:

每个容器都要限制可用内存,docker run 加 --memory 参数。不限制的话,一旦宿主机的某个进程占用所有内存,整机可能直接 OOM。限制容器日志大小,docker run 加 --log-opt max-size=10m --log-opt max-file=3,否则日志文件会无限膨胀,占满磁盘。尽量用 alpine、slim 之类的精简镜像,同样的服务精简镜像体积能小一半以上,运行时内存占用也更低。

用 docker stats 命令能实时查看每个容器的 CPU 和内存占用,排查性能问题时非常好用。如果是容器数量多导致 Docker Desktop 卡顿,Windows 用户可以在 Docker Desktop 设置里调整分配给 WSL2 的内存上限。

7.4 其他实际踩过的坑

CentOS 7 升级 Docker 是很多运维会遇到的场景,旧版本 Docker 的 daemon.json 某些配置项在新版本不再支持,升级后服务起不来。处理方式是升级前备份 /etc/docker/daemon.json,升级后逐项检查配置是否有 deprecated 项,用 dockerd --validate 命令校验配置合法性。

docker sql2008、人大金仓数据库这类冷门镜像,部署思路和其他数据库一样,无非是拉镜像、起容器、映射端口、挂数据目录。国产数据库大多提供了官方镜像,使用流程高度类似。hadoop 这种重量级组件用 Docker 跑要注意内存规划,一个集群十几个容器拉起,内存不够会非常难受,建议用 docker compose 统一编排并通过 --memory 限制每个服务的内存上限。

8. 用 Compose 和私有仓库进阶管理

8.1 Docker Compose 统一编排

服务一多,一条条 docker run 命令敲既容易遗漏参数,又没法整体管理。Docker Compose 就是解决多容器编排问题的。用一个 docker-compose.yml 文件描述所有服务,一行命令全部启停。

举个典型的多服务示例:

version: "3.9" services: mysql: image: mysql:8.0 container_name: mysql8 environment: MYSQL_ROOT_PASSWORD: "123456" ports: - "3306:3306" volumes: - /data/mysql:/var/lib/mysql restart: always redis: image: redis:7 container_name: redis7 ports: - "6379:6379" restart: always app: build: . container_name: myapp ports: - "8080:8080" depends_on: - mysql - redis restart: always

保存后执行 docker compose up -d,整套环境都起来了。docker compose ps 查看状态,docker compose logs -f 追踪日志,docker compose down 一键关闭。

有个细节必须提醒:depends_on 只控制服务启动的先后顺序,MySQL 容器启动了不代表 MySQL 进程已经就绪。应用容器可能在数据库还没初始化完就开始连接导致报错,解决方法是应用的启动逻辑里加重试,或者用 healthcheck 让 Compose 等到服务健康后再启动依赖服务。

8.2 搭建私有镜像仓库

企业内部不希望镜像上传到公共仓库,就需要私有仓库。Docker 官方 registry 镜像就能轻松搭:

docker run -d -p 5000:5000 --name registry --restart=always registry:2

之后把本地镜像打个标签推上去:

docker tag mysql:8.0 localhost:5000/mysql:8.0 docker push localhost:5000/mysql:8.0

这里有个坑:docker 默认只允许 HTTPS 访问镜像仓库。如果你在内网用 HTTP 访问,需要在客户端的 daemon.json 里加 insecure-registries:

{ "insecure-registries": ["192.168.1.10:5000"] }

然后重启 Docker。企业内部用这套方案能极大提升镜像管理效率。

8.3 IDEA 打包镜像与微服务部署

Java 后端用 IDEA 打包 Docker 镜像的工作流也很成熟。先在项目的 pom.xml 里加 dockerfile-maven-plugin,或者在 IDEA 的 Docker 插件里配置连接远程 Docker 守护进程的 TCP 地址。写好 Dockerfile:

FROM openjdk:8-jre-alpine COPY target/app.jar /app.jar EXPOSE 8080 ENTRYPOINT ["java", "-jar", "/app.jar"]

IDEA 的 Docker 面板里右键 Dockerfile 直接 Build Image,构建出来的镜像可以直接部署。这种方式对微服务项目特别友好,每个微服务模块对应一个镜像,版本迭代、回滚都很方便。

PHP 项目打包镜像同理,基底用 php:7.4-fpm-alpine,把项目代码 COPY 进去,安装需要的扩展,构建后就能跑。思路都是一套:把项目文件、运行环境、入口命令固化进镜像。

我个人在实际工作中的体会是:docker 使用不是背命令,而是理解“一切皆镜像、服务皆容器”这个模型。你只要玩懂镜像构建、容器生命周期、网络和数据卷这三块,剩下的各种框架、数据库、中间件怎么部署,基本都是查对应的官方镜像文档照方抓药。踩过的坑越多,后面越顺手。希望这篇整理能帮你在 Docker 这条路上少走点弯路。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询