ARTICLE DETAIL

资讯详情

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

Docker入门实战:从核心概念到MySQL部署与Compose编排

Docker入门实战:从核心概念到MySQL部署与Compose编排 我第一次把一个跑了大半年的应用塞进 Docker 容器时内心是拒绝的——又是镜像又是容器还有一堆-n -d参数看起来像在背咒语。但真正让我心服口服的不是“一条命令起服务”这种宣传语而是后来有一次我误删了生产服务器的整个应用目录最后靠一份 Dockerfile 和几张镜像备份半小时内把服务原样拉了回来。那一刻我才意识到Docker 最值钱的东西不是虚拟化而是“环境状态的可携带性”。这篇博文把 Docker 的基础用法完整过一遍核心概念、Windows 和 Linux 安装、高频命令、MySQL 8.0 实战、镜像加速、Docker Compose 编排最后是日常最容易翻车的几个报错。你可以把它当成一篇直接可以照着抄的入门手册不需要提前懂任何容器概念跟着走就能把环境跑起来。1. 先搞懂镜像、容器和仓库在说什么Docker 不是虚拟机新手最容易卡住的地方不是命令而是概念。很多人把 Docker 理解成“轻量级虚拟机”这个理解方向对了一半但会带来一连串误导。如果你带着虚拟机的思维去用 Docker后面遇到容器删除、镜像更新、数据持久化这些问题时会非常别扭。1.1 镜像只读、容器可写一层叠一层的文件系统逻辑Docker 镜像Image是一个只读模板里面打包了应用运行所需的操作系统文件、依赖库、环境变量和启动命令。它不像 Windows 的 ISO 镜像那样是一个完整的大文件而是由一层一层只读文件系统叠加组成的。每一层对应 Dockerfile 里的一个指令比如FROM ubuntu:22.04构成基础层RUN apt install nginx新增一层。每一层会被缓存下次构建时如果对应指令没有变化这一层可以直接复用构建速度会快很多。容器Container是镜像运行时的实例。它在镜像之上加了一层可写层你对容器做的所有修改——写文件、改配置、装软件——都发生在这层可写层里。镜像本身始终没有被改动过同一个镜像可以同时跑出十个互不影响的容器。这里有一个很多人踩过的坑容器停止后对可写层的修改依然保留但一旦执行docker rm把容器删除可写层连同里面的所有修改一起消失。所以容器内部的文件默认是“不可靠”的想要持久化必须用数据卷Volume或目录挂载这个后面 MySQL 实战部分会细说。1.2 容器和虚拟机最大的不同共享内核的进程隔离虚拟机通过 Hypervisor 虚拟出完整的硬件设备每个虚拟机里跑一个完整的客户操作系统所以虚拟机之间是硬件级隔离。容器则完全不同它直接共享宿主机内核只通过 Linux 内核的命名空间Namespace做进程隔离、通过控制组CGroup做资源限制。容器里看到的“操作系统”不过是镜像里打包的那一层 rootfs真正干活的内核还是宿主机的。用一句话区分虚拟机是“一栋楼里每个房间自带发电机和水箱”容器是“一栋楼里每个房间共享中央水电只是每个房间的门锁和电表独立”。这个差异带来两个直接后果一是容器启动是秒级因为不需要引导操作系统二是容器内不能运行和宿主机内核不兼容的东西比如在 Linux 容器里想跑 Windows 程序、在低版本内核宿主机上跑需要高内核特性的应用都会出问题。1.3 从 Dockerfile 到 run一次“构建-分发-运行”的完整链路把 Docker 的工作链路串起来看整个流程是这样的编写 Dockerfile描述“基础镜像 依赖安装 文件复制 启动命令”。执行docker build构建出镜像。把镜像推送到仓库Registry比如 Docker Hub或者公司内部的私有仓库。在任意一台装有 Docker 的机器上执行docker run从仓库拉取镜像并启动容器。所以 Docker 解决的核心问题不是“虚拟化”而是“环境一致性”。你在本地构建出来的镜像跑到测试环境、生产环境行为应该是一致的。这也是为什么很多团队用 Docker 之后把“在我机器上是好的”这句话彻底消灭掉了。2. 安装这一关就卡掉一半人Windows 和 Linux 的坑分别踩一遍Docker 的安装问题一半出现在 Windows 上另一半出现在 Linux 的发行版差异上。官方文档写得虽然清楚但实际安装时报错信息经常让人一头雾水尤其是 Docker Desktop 那几个启动失败提示。2.1 Windows 上“virtualisation support wasnt detected”的完整排查Windows 装 Docker Desktop报错率最高的就是Docker Desktop failed to start because virtualisation support wasnt detected。这句话表面意思是“没检测到虚拟化支持”但真实原因至少有三种第一BIOS 里确实没开启硬件虚拟化。重启进 BIOS找到Intel Virtualization TechnologyIntel或SVM ModeAMD并启用。怎么确认开没开最简单的方法是按Ctrl Shift Esc打开任务管理器进“性能”标签看 CPU 信息右下角是否显示“虚拟化已启用”。如果显示已启用但 Docker 还报这个错往下看。第二Windows 功能里没开启“虚拟机平台”和“适用于 Linux 的 Windows 子系统”。在控制面板“启用或关闭 Windows 功能”里勾选这两个选项后重启系统。注意 Windows 家庭版没有 Hyper-V 组件但 Docker Desktop 最新版默认走 WSL2 后端不依赖 Hyper-V所以家庭版也能装。第三如果你是在虚拟机里再装 Windows比如在 VMware 或 VirtualBox 里跑 Windows需要在虚拟机的 CPU 设置里开启“虚拟化 Intel VT-x/EPT 或 AMD-V/RVI”这类嵌套虚拟化选项否则虚拟机里的 Windows 无论如何都检测不到硬件虚拟化。2.2 Docker Desktop 与 WSL2 后端配置选对后端比选对版本更重要现在 Docker Desktop 默认使用 WSL2 后端比老版的 Hyper-V 后端启动更快、内存占用更低。安装 Docker Desktop 时它会提示你启用 WSL2如果当时跳过了后续也可以在设置里补。先检查 WSL 状态管理员身份打开 PowerShellwsl --status如果提示没有安装 WSL执行wsl --install这会安装 WSL2 并默认装一个 Ubuntu 发行版。装完后用wsl -l -v查看版本确保是 2。如果还是 1 的话执行wsl --set-version Ubuntu 2接着打开 Docker Desktop 设置确认Settings - General里勾选了Use the WSL 2 based engine。有些版本还会在Settings - Resources - WSL Integration里让你选择把 Docker 集成到哪些 WSL 发行版中默认全勾即可。还有一个很老的报错weve detected that you have an incompatible version of windows。出现这个提示是因为新版 Docker Desktop 要求 Windows 10 21H2 或更高版本64 位Windows 11 更是不在话下。如果是旧版本 Windows优先升级系统确实没法升级的只能找旧版 Docker Desktop 安装包但强烈不建议旧版在安全性和稳定性上都差不少。2.3 Linux 服务器端安装Ubuntu 与 CentOS 7 的差异Linux 上装 Docker 分两大派系Debian/Ubuntu 系和 CentOS/RHEL 系。Ubuntu 上最省事的方式是直接用官方自动脚本curl -fsSL https://get.docker.com | sh然后启动服务并设置开机自启systemctl enable --now dockerCentOS 7 稍微曲折一点。CentOS 7 自带的 yum 源里虽然也有docker包但版本非常老强烈建议用官方源安装 docker-ceyum install -y yum-utils yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo yum install -y docker-ce docker-ce-cli containerd.io如果机器上之前装过旧版 docker 或 docker-io需要先卸载yum remove -y docker docker-common docker-selinux docker-engineCentOS 7 还有一个隐藏问题默认内核是 3.10.x和 overlay2 存储驱动配合时偶尔会有 bug。虽然新版 Docker 在 CentOS 7 上能跑起来但如果你要跑高版本 MySQL、K8s 这类重负载应用建议先把内核升级到 5.x或尽量避免用太老的 CentOS 7 小版本。我个人的建议是如果业务还没上线优先选 Ubuntu 22.04 或 Debian 12 作为 Docker 宿主机。2.4 装完先别急着用三件事验证环境是否正常安装完成后不要急着拉镜像先做三件事确保环境真的健康。第一查看 Docker 版本docker version客户端和服务端都要有输出如果Server部分报错说明 Docker 守护进程没起来执行systemctl status docker查看状态。第二跑官方测试镜像docker run hello-world能输出一段说明文字说明整个拉取、创建、运行链路是通的。第三把当前用户加进 docker 组之后不用每次敲 sudosudo usermod -aG docker $USER执行完重新登录终端才生效。这一步必须做否则你会在网上搜半天“为什么我已经在 docker 用户组还是 permission denied”这类问题。3. 高频命令不是背下来的理解镜像层、端口映射和数据卷之后的自然结果Docker 命令多但九成使用场景集中在十几条。我不建议死记而是把每条命令和它背后的机制对应起来。机制理解了命令自然忘不掉。3.1 镜像侧pull、images、rmi、tagdocker pull 镜像名:标签从仓库拉取镜像。标签默认是latest但生产环境务必指定具体版本比如mysql:8.0否则哪天别人重新 pull 了一下拉到的可能是破坏性升级的新版本。docker images列出本地所有镜像。注意观察IMAGE ID列它是镜像的真实标识而镜像名和标签只是引用。同一个镜像可以打多个标签。删除镜像用docker rmi 镜像ID或名称。如果镜像被某个容器引用删除会报错需要先删容器或加-f强制删。提醒一句-f强制删除虽然方便但会留下无标签的悬空镜像dangling image久了磁盘占用也不小后续可以用docker image prune统一清理。docker tag给镜像打标签常用于把本地镜像标记成私有仓库地址。比如docker tag myapp:latest registry.example.com/myapp:1.03.2 容器侧run 的每个参数到底在解决什么问题docker run是使用频率最高的命令没有之一。一条完整的运行命令长这样docker run -d \ --name mynginx \ -p 8080:80 \ -v /opt/nginx/html:/usr/share/nginx/html:ro \ -e TZAsia/Shanghai \ --restartalways \ nginx:1.25逐个拆-d后台运行detach。不加的话容器会挂在前台CtrlC 一按容器就停了。--name容器名字。不加的话 Docker 会随机生成一个名字运维时非常痛苦。-p 8080:80端口映射格式是“宿主机端口:容器端口”。访问宿主机 8080 端口流量会转发到容器内的 80 端口。这个方向一定不要搞反反了就是你访问容器 8080 却映射到宿主机 80直接 404。-v /opt/nginx/html:/usr/share/nginx/html:ro目录挂载。宿主机/opt/nginx/html映射到容器内/usr/share/nginx/html后面加:ro表示容器内只读。这是最常用的持久化手段nginx 的静态文件放在宿主机目录下改文件不需要进容器。-e TZAsia/Shanghai环境变量。很多镜像通过环境变量做初始化配置比如 MySQL 的 root 密码。--restartalways容器退出后自动重启。宿主机重启后也会跟着启动适合需要长期在线的服务。3.3 进容器里折腾exec、logs、cp、inspect容器运行起来之后常用操作就这几条docker ps # 查看运行中的容器 docker ps -a # 查看所有容器包括已停止的 docker logs -f 容器名 # 跟踪查看日志 docker exec -it 容器名 bash # 进入容器内部 docker inspect 容器名 # 查看容器详细信息docker exec -it里的-i保持标准输入打开-t分配一个伪终端。进入容器后你会发现自己“进入了”一个独立的文件系统里。要注意的是有些精简镜像比如 alpine 系列的里没有 bash只有 sh报错no such file or directory时改用sh就行。docker cp用于在宿主机和容器之间拷贝文件docker cp foo.txt mynginx:/tmp/foo.txt docker cp mynginx:/var/log/nginx/access.log ./access.logdocker cp适合应急操作不适合作为常规部署手段。常规部署应该把文件放到宿主机挂载目录里容器内自然就能看到。3.4 磁盘和资源清理容器堆积的隐患容器用得越久磁盘占用越吓人。堆积的来源主要有三个停止但不删除的容器、无用的镜像、容器日志文件。docker ps -a会显示所有历史容器包括退出状态的。长期堆积的容器会占用可写层磁盘空间。清理方式docker rm 容器名 # 删除指定容器 docker container prune # 删除所有已停止的容器镜像层面的清理docker image prune # 清理悬空镜像 docker image prune -a # 清理所有未被容器引用的镜像最暴力的全局清理docker system prune -a这条命令会删除所有停止的容器、所有未被使用的网络、所有悬空镜像和构建缓存。慎用尤其是同一台机器上跑着测试环境的人清理完下次部署要重新拉镜像又是一轮漫长的等待。4. 第一个实战项目用 Docker 跑一个带持久化的 MySQL 8.0概念说了一堆不如一个实战来得实在。这里我用最典型的场景——Docker 部署 MySQL 8.0把镜像拉取、参数配置、数据持久化、常见认证问题全部串起来。这个流程跑通了其他中间件Redis、Nginx、PostgreSQL基本都是一个套路。4.1 拉镜像之前先想清楚数据目录和配置文件放宿主机哪里很多新手上来直接docker run mysql:8.0跑起来是没问题但过几天容器一删数据全没了这才发现没做持久化。正确做法是先规划好宿主机目录再启动容器。mkdir -p /opt/mysql/data mkdir -p /opt/mysql/conf mkdir -p /opt/mysql/logs三个目录的用途目录挂载到容器内作用/opt/mysql/data/var/lib/mysqlMySQL 数据文件核心中的核心/opt/mysql/conf/etc/mysql/conf.d自定义配置文件目录容器启动时自动读取/opt/mysql/logs/var/log/mysql日志文件然后拉取镜像docker pull mysql:8.0拉取速度如果很慢先跳到第 5 章配置镜像加速再回来看这一步。4.2 docker run 逐参数拆解端口、密码、挂载、自启执行启动命令docker run -d \ --name mysql8 \ -p 3306:3306 \ -e MYSQL_ROOT_PASSWORDRoot123456 \ -v /opt/mysql/data:/var/lib/mysql \ -v /opt/mysql/conf:/etc/mysql/conf.d \ -v /opt/mysql/logs:/var/log/mysql \ -e TZAsia/Shanghai \ --restartalways \ mysql:8.0看几个关键点。-e MYSQL_ROOT_PASSWORD是 MySQL 官方镜像要求的环境变量第一次初始化时会用这个值设置 root 用户的密码。如果忘了设置容器会启动失败并提示你需要指定密码。-p 3306:3306把宿主机 3306 映射到容器的 3306。如果宿主机 3306 已经被占用可以改成-p 3307:3306外部访问就用 3307。--restartalways在这条命令里尤其重要。MySQL 是基础服务必须保证机器重启后能自动拉起。启动后验证状态docker ps docker logs mysql8看到ready for connections或类似日志说明启动成功。4.3 顺利连上之后的三个坑认证插件、字符集、时区进入容器连接数据库docker exec -it mysql8 mysql -uroot -p输入密码后能进 MySQL 命令行算是正式跑通了。但后面的坑才刚开始。第一个坑MySQL 8.0 默认认证插件是caching_sha2_password但很多老版本客户端、Java 老驱动、Python 老库都不支持这个插件连接时会报Authentication plugin caching_sha2_password cannot be loaded解决办法是给用户改成老认证插件ALTER USER root% IDENTIFIED WITH mysql_native_password BY Root123456; FLUSH PRIVILEGES;第二个坑字符集。MySQL 8.0 默认字符集已经是utf8mb4但为了保险建议在启动命令里显式加上--character-set-serverutf8mb4 --collation-serverutf8mb4_unicode_ci或者在/opt/mysql/conf下新建my.cnf[mysqld] character-set-serverutf8mb4 collation-serverutf8mb4_unicode_ci第三个坑时区。MySQL 默认时区是 UTC在容器里执行SELECT NOW()会比北京时间慢 8 小时。已经启动的容器里的全局时区可以这样改SET GLOBAL time_zone 8:00;但更推荐的方式是启动时就用-e TZAsia/Shanghai这个环境变量会同时影响容器内操作系统和 MySQL 的时区处理。4.4 验证持久化把容器删了数据还在才算成功这一步非常重要很多人跑完 MySQL 觉得“能用就行”结果没验证持久化。先创建一个测试数据库CREATE DATABASE test_db;退出容器执行删除docker stop mysql8 docker rm mysql8然后重新用同一套挂载目录启动一个新容器docker run -d \ --name mysql8_new \ -p 3306:3306 \ -e MYSQL_ROOT_PASSWORDRoot123456 \ -v /opt/mysql/data:/var/lib/mysql \ -v /opt/mysql/conf:/etc/mysql/conf.d \ -v /opt/mysql/logs:/var/log/mysql \ -e TZAsia/Shanghai \ --restartalways \ mysql:8.0再进容器执行SHOW DATABASES;test_db还在说明持久化成功。这里体现的是容器的核心理念容器是“牲畜”不是“宠物”。MySQL 出了问题不要想着进容器里修修补补直接把容器删了重新起一个新的数据在挂载目录里一切照旧。这也是 Docker 化的应用和传统运维方式最大的思维差异。5. 镜像下载慢怎么破registry-mirrors 配置与拉取失败的排查docker pull慢是刚接触 Docker 时一定会遇到的事。这不是你的网络问题也不是 Docker 的 bug而是 Docker Hub 的官方镜像服务器在国外跨海链路拉取镜像天然慢。解决方案是配置镜像加速器registry mirror。5.1 慢在哪一步分层下载与并发拉取的基本逻辑镜像拉取是分层的docker pull的时候终端里会显示Waiting和Downloading每一层独立下载。如果镜像包含很多层其中某一层特别大整个拉取时间就会被拖长。镜像加速器的作用是在你本地 Docker 和 Docker Hub 之间架一个代理缓存节点。你配置了加速器地址后Docker 会优先从加速器拉取加速器上如果没有缓存它会替你从 Docker Hub 拉取一份再传给你。这样原本要跨海的流量变成了你和国内节点之间的流量速度自然快得多。5.2 daemon.json 的 registry-mirrors 写法Linux 和 Docker Desktop 分别怎么改Linux 上修改/etc/docker/daemon.json。如果文件不存在就新建{ registry-mirrors: [ https://your-registry-mirror.example.com ] }your-registry-mirror.example.com替换成你实际拿到的加速器地址。国内各大云厂商都提供容器镜像加速服务通常会分配一个专属于你账号的加速地址比如登录云厂商的容器镜像服务控制台在“镜像加速器”页面能看到类似https://xxxx.mirror.xxx.com的地址把它填进去。改完重启 Dockersystemctl daemon-reload systemctl restart dockerWindows Docker Desktop 更简单不需要手动改文件。打开 Docker Desktop进入Settings - Docker Engine在 JSON 配置里加同样的一行点Apply Restart即可。验证加速器是否生效docker info在输出里找Registry Mirrors这一段能看到你配置的地址就说明生效了。5.3 拉取失败的几种典型报错超时、标签不存在、架构不匹配配置完加速器大部分慢的问题能解决但拉取失败偶尔还会出现常见的有三种。第一种超时。报错信息类似dial tcp: lookup xxx on x.x.x.x:53: no such host或EOF。先检查加速器地址是否可用、网络是否正常再检查 daemon.json 的 JSON 格式有没有写错。格式错了 Docker 直接拒绝启动用docker info会提示配置有误。第二种标签不存在。docker pull mysql:8.0.34报manifest unknown说明这个具体版本号不存在。去 Docker Hub 镜像仓库页面确认可用的 tag 列表不要凭感觉写版本号。实际经验是“小版本号经常在调整”生产环境锁定 tag 时最好先验证一下再写进部署脚本。第三种架构不匹配。在 x86 服务器上拉 ARM 平台的镜像或者反过来会报no matching manifest for linux/amd64。用docker manifest inspect 镜像名:tag可以查看镜像支持的架构列表确认镜像是否兼容当前机器的 CPU 架构。6. 当两条 docker run 不够用时用 Docker Compose 编排 Redis 主从单容器用docker run没问题但一旦涉及多个容器比如应用 数据库 缓存再加上容器间的网络打通和依赖关系一条条命令敲不仅容易漏而且无法版本化管理。Docker Compose 就是来解决这个问题的。6.1 从两条命令到一份 YAMLCompose 解决的是组合与网络问题拿 Redis 主从复制来说用docker run的思路是docker run -d --name redis-master -p 6379:6379 redis:7.0 docker run -d --name redis-slave -p 6380:6379 --link redis-master redis:7.0 redis-server --slaveof redis-master 6379这里有两个问题一是--link已经是被淘汰的旧特性容器重启后 IP 可能变化--link建立的连接就会失效二是这些命令是过程式的换个机器部署还得重新敲一遍。Compose 的做法是把整个环境的定义写进一份docker-compose.yml文件用声明式语法描述“我要跑哪几个容器、它们之间怎么连、数据放哪里”。Compose 会自动创建一个独立的网络所有服务通过在 YAML 里定义的服务名互相访问相当于内置了 DNS 解析不再依赖 IP。6.2 Redis 主从示例逐行拆解服务、网络、依赖关系创建项目目录mkdir ~/redis-cluster cd ~/redis-cluster新建docker-compose.ymlservices: redis-master: image: redis:7.0 container_name: redis-master restart: always ports: - 6379:6379 command: [redis-server, --appendonly, yes] volumes: - master-data:/data redis-slave: image: redis:7.0 container_name: redis-slave restart: always ports: - 6380:6379 command: [redis-server, --replicaof, redis-master, 6379] depends_on: - redis-master volumes: master-data:逐行解释services下面定义各个服务每个服务对应一个容器。image指定镜像和docker run里的镜像名是同一个概念。ports是端口映射格式是宿主机端口:容器端口。command覆盖镜像的默认启动命令。注意主节点开启了--appendonly yes这是 Redis 的 AOF 持久化开关。从节点通过--replicaof redis-master 6379指定主节点的地址和端口这里的redis-master就是 Compose 自动创建的网络里的服务名。depends_on表示启动顺序从节点会等主节点先启动。它不解决健康检查问题只解决启动顺序这个差异后面会提到。最下面的volumes声明了命名卷master-dataRedis 数据会存在这个卷里容器删了数据还在。启动docker compose up -d查看状态docker compose ps验证主从docker exec -it redis-master redis-cli info replication输出里能看到connected_slaves:1说明从节点已经连上主节点。6.3 Compose 日常操作命令与版本差异Compose 的日常命令不多记住这几个就够了docker compose up -d # 启动所有服务 docker compose down # 停止并删除所有服务容器和网络 docker compose logs -f # 查看所有服务的日志 docker compose exec redis-master redis-cli # 进入某个服务执行命令 docker compose restart # 重启所有服务注意一个版本差异新版 Docker Desktop 和较新的 Docker Engine 内置了 Compose 插件命令是docker compose中间有空格。老版本需要单独安装docker-compose命令中间有连字符用法参数大体相同但执行命令前先用docker compose version确认一下当前环境支持哪种。另外docker compose down默认不会删除命名卷。如果你确实想把数据卷一起清掉加-vdocker compose down -v这条命令会把你声明的命名卷里的所有数据删得干干净净执行前确认清楚。7. 日常使用最容易翻车的几个点连接失败、端口占用和日志暴涨基础命令跑熟之后日常使用中真正让人抓狂的不是学习曲线而是几个反复出现的环境问题。这里把最高频的几个报错和排查思路列出来你可以直接当成速查表用。7.1 连不上 Docker 引擎先分清是守护进程问题还是权限问题Linux 上最常见的报错是Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?这句话信息量不大实际原因分两种。第一种Docker 守护进程真的没起来。执行systemctl status docker查看状态没起来就systemctl start docker并设置开机自启systemctl enable docker。第二种当前用户不在 docker 组里。新安装 Docker 后容易遇到执行sudo usermod -aG docker $USER重新登录终端即可。注意用户组变更只在新的登录会话里生效当前终端继续用还是会报 permission denied。Windows Docker Desktop 上对应的报错是failed to connect to the docker api at npipe:////./pipe/docker-desktop-linux这种通常是 Docker Desktop 的引擎没起来。先看 Windows 右下角托盘区的鲸鱼图标确认是运行状态还是红点/蓝点。右键选择Restart等几十秒再执行docker version。如果反复启动失败按第 2 章的方法重新检查 WSL2 和虚拟化配置。7.2 端口被占用和容器秒退日志比状态列表更诚实启动容器时报Error starting userland proxy: listen tcp4 0.0.0.0:3306: bind: address already in use意思是宿主机 3306 端口已经被占用。用以下命令查看谁占了端口ss -lntp | grep 3306处理方式有两种如果那个进程没用了关掉如果那个进程要用比如宿主机本来就有 MySQL那 Docker 里的 MySQL 换个宿主机端口比如把-p 3306:3306改成-p 3307:3306。另一种极易遇到的情况是容器启动后立即退出。docker ps -a会显示出Exited (1)一类的状态。“为什么 Exited”这个问题docker ps回答不了但日志能。执行docker logs 容器名MySQL 密码没设置、Redis 配置语法错误、挂载目录权限不对这类问题都会如实写在日志里。记住一个原则容器异常先看日志不要在状态列表前瞎猜。7.3 日志无上限增长和时区偏差两个容易被忽视的长期隐患容器日志增长是一场“温水煮青蛙”。默认情况下Docker 会把容器的标准输出和标准错误全部记录成 JSON 文件存到宿主机的/var/lib/docker/containers目录下不限制大小。跑了一年的容器日志文件可能膨胀到几十个 GB直接把宿主机磁盘撑爆。推荐在启动容器时限制日志文件大小docker run -d \ --log-opt max-size10m \ --log-opt max-file3 \ nginx也可以在 Docker 配置文件里全局设置。Linux 上在/etc/docker/daemon.json加入{ log-driver: json-file, log-opts: { max-size: 10m, max-file: 3 } }已经膨胀的日志文件要么删容器重建要么用truncate -s 0直接清空。时区问题看起来小影响却不小。容器默认使用 UTC 时区应用打印的日志时间、定时任务的执行时间都会和本地时间不一致。最简单的处理方式是每个容器启动时都带上-e TZAsia/ShanghaiLinux 上更彻底的方案是把宿主机时区文件挂载进容器-v /etc/localtime:/etc/localtime:ro这个方案在部分精简镜像里可能不生效但大多数主流镜像都能正常读到。我的习惯是-e TZAsia/Shanghai优先因为它在所有镜像上表现一致不需要关心挂载文件的具体内容。最后再分享一个我个人的经验但凡准备长期运行的容器不要直接用docker run裸跑哪怕只有一个服务也建议先写一份 docker-compose.yml。理由很简单——容器化最大的价值在于“可复现”一份写清楚的 YAML 文件比你在终端历史里翻出三个月前那条动不动换行的长命令要可靠得多。等你哪天要部署到新机器把文件传过去docker compose up -d一条命令搞定就知道这个习惯有多值钱了。
返回列表