Docker容器化部署实战:从安装到MySQL与Redis全攻略
一个项目在开发机器上跑得好好的换到服务器就一片红这是后端和运维同学最常见的噩梦。我当年也在这个坑里爬了很久直到彻底搞明白Docker这套容器化方案才真正结束“环境地狱”的日子。这篇Docker学习教程不会像官方文档那样从抽象概念讲到天荒地老而是从实际部署角度出发带你把Docker安装、镜像与容器、常用命令、MySQL和Redis实战全部跑通每一步都附上我踩过的坑和排查思路希望帮不同基础的读者都少走弯路。1. 容器解决的是流程问题不只是技术问题Docker这类容器技术本质上是在回答一个问题怎么让应用在任意环境里都表现一致。在没有容器的年代部署一个Web应用要经历多少步骤安装JDK、配置环境变量、装Tomcat、调端口、塞进war包、写启动脚本。每台服务器都得来一遍每一步都可能出幺蛾子。更麻烦的是开发环境用Windows或Mac生产环境用Linux操作系统的差异会放大这些幺蛾子。这背后最核心的矛盾是应用运行依赖的环境太复杂而人工维护环境的方式效率太低。Docker把解决方案推到了一个新维度把应用连同它的运行环境一起打包成镜像。镜像里有代码、有依赖库、有配置文件、有运行时它不关心宿主机器上装了什么只要宿主机有Docker引擎就能把这个镜像跑成容器。这样一来“在我机器上能跑”这句话有了更严格、也更让人安心的版本“在任意装了Docker的机器上都能跑。”1.1 应用打包方式的演变聊一个我实际参与过的项目。早年部署一个Java服务运维同学有一套自己的环境准备手册30多个步骤每一条都对应一条命令。有一次服务器硬盘损坏需要换机整整折腾了一个下午才把环境恢复。后来项目迁移到Docker同样的服务变成了一份Dockerfile和一套镜像构建脚本恢复一台新机器只需要装好Docker然后执行docker run十分钟内服务就能起来。这个对比让我非常直观地理解了容器化不是花架子而是切切实实降低了交付成本和故障恢复时间。这不是个例。前端项目、Python项目、Node项目、数据库、缓存几乎所有通用组件都有官方镜像或社区维护镜像。有了Docker之后项目的可交付物从“源码长长的部署文档”变成了“镜像一条run命令”交付物本身更加稳定和纯粹。1.2 镜像、容器、仓库三者的关系很多新手搞不清这三个概念我用一个生活类比来解释。镜像是制作披萨的模具包含了做披萨所需的全部原料和工艺它本身是静态的、只读的容器是用模具做出来的一个具体披萨可以切、可以吃、可以丢弃仓库是存放模具的仓库你随时随地可以从仓库取出模具来做新的披萨。在Docker的世界里镜像Image是一个打包好的只读模板容器Container是镜像运行时的实例仓库Registry是存放和分发镜像的地方。Docker Hub是最大的公共仓库也可以自己搭建私有仓库。理解这三者的关系对后续操作非常关键。你平时执行的docker pull是从仓库拉镜像到本地docker run是把本地镜像创建成容器docker push是把本地镜像上传到仓库。这组动作构成了日常工作的主线。1.3 Docker与虚拟机的本质区别和传统虚拟机相比Docker最大的区别是共享宿主机的操作系统内核不需要为每个容器都单独跑一个Guest OS。虚拟机通常是硬件级别的虚拟化启动一个虚拟机意味着要分配CPU、内存、磁盘并启动一整套操作系统容器是进程级别的隔离多个容器共享宿主机内核通过Namespace和Cgroups这两套内核机制实现资源隔离与限制。这意味着容器镜像更小、启动更快、资源占用更低。同一台2核4G的云服务器上跑两个虚拟机可能已经吃力了但跑十几个容器通常是没问题的。当然隔离性上虚拟机更彻底容器共享内核也意味着容器里的进程始终受宿主机内核影响这是后续选型时需要权衡的点。如果面对的是多租户强隔离场景虚拟机仍然是更稳妥的选择如果是自用服务、微服务拆分、CI/CD流水线容器方案的性价比高得多。2. Windows、Ubuntu、CentOS安装实录与避坑Docker本身是运行在Linux上的Windows和Mac上的Docker Desktop本质是通过轻量级虚拟机把Linux运行环境包起来了。所以不同平台的安装方式差异很大坑也不太一样。2.1 WindowsDocker Desktop与WSL2前置准备在Windows上装Docker Desktop有两个前置条件必须确认CPU虚拟化已开启以及WSL2可用。虚拟机监控程序平台和Windows Subsystem for Linux这两个Windows功能要提前启用。我在Windows 11上安装时比较顺利但在Windows 10上遇到过几次问题原因大多是没有提前在BIOS里开启虚拟化或者WSL2内核没有更新。检查虚拟化是否开启可以打开任务管理器在“性能”标签页看“虚拟化”状态检查WSL2在PowerShell里执行wsl --status如果输出的是WSL 1就需要执行wsl --set-version 发行版名 2或者wsl --update把内核更新到最新。安装之后如果Docker Desktop始终提示Virtualization support not detected基本可以断定是CPU虚拟化没开或没被识别。处理方式是重启进BIOS/UEFI找Intel Virtualization Technology或AMD SVM选项开启后保存退出即可。不同品牌的BIOS界面差异很大找不准对应选项时直接搜自己主板型号加“虚拟化开启”就能找到图文指引。装完Docker Desktop后建议新开一个PowerShell窗口执行docker version验证CLI和引擎是否都正常避免终端里还残留旧环境变量导致不必要的误会。2.2 Ubuntu与CentOS两条推荐的安装路径Ubuntu推荐用官方apt仓库安装。先更新apt索引安装ca-certificates、curl这样的依赖包再把Docker官方GPG key添加进来随后添加stable仓库最后apt install docker-ce docker-ce-cli containerd.io。安装完成之后执行systemctl enable --now docker把服务设置为开机自启。CentOS 7/8的流程类似但有一条更省事的路径使用官方脚本sudo curl -fsSL https://get.docker.com -o get-docker.sh sudo sh get-docker.sh。脚本会自动配置yum源并完成安装。我第一次在CentOS上看到这个脚本时担心不够可控但用了几次后发现官方维护得挺好适合不想折腾源配置的人。需要注意CentOS 7默认内核3.10对Docker的某些新特性支持不好iptables版本也偏低。如果发现容器网络异常优先考虑升级系统到更新的发行版或者至少升级内核而不是花大力气调Docker参数。按照我个人的经历CentOS 7上遇到docker0网桥或iptables相关的怪异问题十有八九是内核太老与其绕来绕去不如直接在较新的操作系统上重装Docker。2.3 权限错误与启动自检无论哪个平台安装完成后的第一步都是验证docker version。Linux上如果普通用户运行docker命令出现permission denied或cannot connect to the Docker daemon大概率是用户不在docker用户组里。解决方法是sudo usermod -aG docker $USER退出重新登录生效。把用户加入docker组相当于给了用户近似root的宿主机权限所以只对信任的账号操作。这个就是“docker权限错误怎么解决”里最常见的情况比较简单但几乎每周都有人问。验证Docker是否正常还有一个组合拳先docker info看引擎和插件状态再docker run --rm hello-world跑一个测试容器。hello-world镜像虽小却能完整体验从拉取镜像到创建容器的流程是安装自检最经典的工具。2.4 装完Docker先做这几件事安装只是一切的起点。我建议装完之后立刻做三件事第一配置镜像加速器避免第一次拉镜像就卡在下载上这就是下一章会详细展开的内容第二启动一个长期运行的测试容器比如docker run -d -p 8080:80 nginx验证端口映射链路是否通第三确认Docker服务重启策略Linux上确保systemctl enable docker已生效。这三件事做完Docker环境才算真正可以拿来干活的。3. 镜像与容器理解这两个概念就入门了一半很多Docker教程上来就让读者敲命令命令敲完也不知道为什么。实际上只要把镜像和容器的关系、核心机制搞明白后面所有命令都能串起来。3.1 镜像的分层结构与构建原理镜像是分层的。每一层是在构建过程中生成的只读快照多个层叠加在一起构成完整的镜像。这样设计的好处很多多个镜像可以共享相同的层比如多个镜像都基于ubuntu:22.04宿主机只需保存一份ubuntu基础层大大节省磁盘空间同时拉取镜像时相同的层不会被重复下载也能提升拉取速度。构建镜像时Dockerfile里的每一条指令通常都会生成一个新层。以PHP项目为例FROM php:8.2-apache在基础层之上添加RUN docker-php-ext-install pdo_mysql会新增一个带扩展的层COPY本地代码进去又新增一层。最终推送到仓库的就是这些层的组合。这里有个很实用的排查思路如果容器运行时报缺依赖很多新手喜欢直接进容器里apt install但镜像重建后问题又会回来。正确的做法是修改Dockerfile把依赖安装写成RUN指令然后重新构建镜像。因为镜像是不可变的模板任何运行时的修改都只存在于容器层容器删除就什么都没了。3.2 容器生命周期管理容器常用的生命周期是create创建、start启动、stop停止、restart重启、rm删除。docker run其实是create加start的合并操作。一个重要的经验是不要在运行中的容器里直接改配置因为容器是临时的、可随时重建的。需要修改时先停止容器调整配置或修改挂载点再重新创建容器。养成这个习惯之后你会发现部署升级的思路会彻底转变——从“登录机器改配置”变成“替换一个新的容器实例”这正是不可变基础设施理念的雏形。另外docker ps只显示运行的容器想看已停止的加-adocker rm可以删除已停止的容器想删除所有停止容器用docker container prune。这个命令在服务器上跑过一段时间后特别有用能清掉一大堆残留容器。3.3 数据卷与绑定挂载数据不能随容器消失容器的文件系统是临时的容器删除后容器内写入的文件会随之消失。数据卷Volume和绑定挂载Bind Mount就是解决这个问题的。数据卷有两种常见形式匿名卷由Docker自动管理使用方便但多容器共享时不好定位命名卷通过docker volume create mydata创建数据由Docker统一管理适合数据库这类服务。绑定挂载则是把宿主机的某个目录直接挂到容器目录适合开发调试、覆盖配置、输出日志等场景。举个例子MySQL容器一定要把/var/lib/mysql挂出来否则删掉容器数据就全没了。我在生产环境处理过的一个事故就是有同事直接启动了一个不带挂载的MySQL容器跑了两个月之后因为镜像版本需要升级执行了docker rm docker run建新容器结果所有数据全部消失。这件事之后我在任何生产容器的run命令里都强制要求明确数据卷没有挂载的数据库容器一律不允许上线。3.4 网络模式与容器间通信Docker的默认网络模式是bridge。默认网桥会给每个容器分配一个内网IP容器之间通过这个IP或容器名通信但外部无法直接访问。想让外部访问容器里的服务需要做端口映射比如-p 3306:3306把宿主机的3306端口转发到容器的3306端口。容器间通信除了IP还有隐藏的DNS问题重启容器后IP可能变化。生产环境推荐用Docker Compose创建自定义网络这样容器之间可以用服务名互相解析IP怎么变都不影响。除了bridge还有host和none两种常见模式。host模式让容器直接使用宿主机网络性能好但没什么隔离性端口冲突需要自己小心处理none模式常用于特殊调试场景。新手先掌握bridge和host就足够了等用到Kubernetes时再深入CNI网络模型也不迟。4. 从拉取镜像到跑通服务常用命令清单这一章整理一份新手必须常备的命令清单全部来自我平时使用频率最高的操作。命令本身不复杂但组合起来的变化特别多建议边用边记。4.1 镜像管理命令docker pull nginx:1.25拉取指定版本的镜像不写tag默认拉latest。docker images查看本地镜像列表。docker rmi 镜像ID或名字删除镜像。docker build -t myapp:v1 .用当前目录下的Dockerfile构建镜像-t指定名称和tag。docker tag 旧名:旧tag 新名:新tag给镜像打标签常用于准备推送。删除镜像时如果镜像被容器引用会报错需要先删除对应容器再删镜像。这个报错信息很容易看懂但新手经常会愣住以为镜像出问题了。另外docker system df可以查看镜像、容器、数据卷占用的磁盘空间是运维时非常有用的统计命令。4.2 容器管理命令docker ps / docker ps -a查看运行中的或所有容器。docker run -d --name myweb -p 8080:80 nginx以后台方式运行nginx容器把宿主机8080映射到容器80。docker start/stop/restart 容器名管理容器状态。docker rm 容器名删除已停止的容器。docker exec -it 容器名 bash进入容器内部。docker logs -f 容器名跟踪容器日志。docker run里常用参数需要重点掌握-d后台运行、--name为容器起名、-p端口映射、-e注入环境变量、-v挂载数据卷、--network指定网络、--restart always设置重启策略。MySQL、Redis这些官方镜像主要就是靠这些参数组合出不同的部署效果。我在一开始记不住这些参数时会刻意把命令写全而不是依赖工具自动补全大概几十次之后基本就形成肌肉记忆了。4.3 日志、进入容器与文件复制进入容器排查问题是最常见的操作。有时候镜像里没有bash只有sh所以docker exec -it 容器名 sh是更通用的选择。如果容器内没有shell工具还可以用docker cp在宿主机和容器之间复制文件docker cp 宿主机文件路径 容器名:容器内路径 docker cp 容器名:容器内路径 宿主机文件路径这个命令在调试时很管用。比如某个Java容器突然内存溢出但日志文件在容器里直接把日志copy出来分析比进入容器一台一台翻方便得多。容器日志的查看也值得一提nginx、tomcat这类进程型容器日志一般会输出到标准输出用docker logs直接看但如果应用自己写文件日志就需要先确定日志写到容器内哪个路径再通过挂载目录持久化到宿主机。4.4 镜像构建Dockerfile与IDE集成现在主流IDE几乎都内置了Docker插件。IDEA里选中项目根目录的Dockerfile右键选择Build Image配置好Docker连接即可一键打包镜像。如果项目没有Dockerfile插件还可以根据运行环境生成一个基础模板。IDEA打包Docker镜像这个场景在Java项目里很常见需要确认Docker连接方式支持远程TCP连接否则IDEA无法触达远程Docker引擎。PHP项目打包镜像时除了基础的php镜像往往要装扩展和Composer依赖。新手容易忽略的是构建阶段需要设置时区、配置php.ini的上传大小限制这些都应该写进Dockerfile而不是启动后手动改否则镜像的“不可重复性”就丢失了。一个典型的最小PHP Dockerfile大概是FROM php:8.2-fpm RUN docker-php-ext-install pdo_mysql mysqli opcache COPY --fromcomposer:latest /usr/bin/composer /usr/bin/composer RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime WORKDIR /var/www/html构建镜像时注意.dockerignore文件把vendor目录、.git、log文件都忽略掉否则构建上下文会变得非常大build时间和镜像体积都会失控。5. 镜像下载慢加速源、私有仓库与离线导入镜像下载速度是新手使用Docker时的第一个劝退点。明明一条docker pull nginx很简单结果等了几分钟进度条都不动第一反应往往就是“Docker是不是坏了”。其实不是这属于网络链路问题。5.1 Docker Hub与镜像加速原理默认情况下Docker从Docker Hub拉取镜像。由于Docker Hub服务器在海外国内网络环境下直连速度很不稳定拉一个大镜像要几个小时是常有的事。解决方案之一是配置镜像加速器。Docker守护进程支持registry-mirrors配置项拉取镜像时会先尝试从镜像源拉取拉不到再回退到Docker Hub。国内主流云厂商都提供免费的加速地址具体地址建议去对应官网获取最新可用的因为这类地址会随时间调整网上搜到的一些老地址可能已经失效。5.2 配置registry-mirrorsLinux下编辑/etc/docker/daemon.json{ registry-mirrors: [https://你的加速地址] }然后执行sudo systemctl daemon-reload sudo systemctl restart docker配置完成后docker info输出的Registry Mirrors一栏会列出配置的加速源拉取镜像试一下即可生效。在Docker Desktop中打开Settings找到Docker Engine在JSON配置里加上同样格式的registry-mirrors保存后Docker会自动重启。需要注意加速源不是万能的有些加速源对热门官方镜像速度很好但对冷门镜像仓库支持有限。如果加速源拉不动可以尝试带仓库前缀拉取指定镜像或者临时换备用的加速地址。5.3 私有Registry搭建与推送企业内部经常需要私有仓库避免镜像直接暴露在公网上也便于离线网络环境分发。搭建一个最简私有仓库非常容易docker run -d -p 5000:5000 --name registry --restartalways registry:2推送镜像之前需要给镜像打上仓库地址的tagdocker tag myapp:v1 127.0.0.1:5000/myapp:v1 docker push 127.0.0.1:5000/myapp:v1拉取的时候执行docker pull 127.0.0.1:5000/myapp:v1。生产环境一般会在registry前面加nginx做TLS和认证再配置垃圾回收和备份。如果项目规模不大用轻量Private Registry就行如果对镜像权限管理有复杂要求可以考虑Harbor这类企业级方案。这里要提醒一点默认registry没有认证如果端口暴露到公网任何人都可以往你的仓库里推镜像务必做好访问控制。5.4 离线场景镜像导出与导入在没有外网的服务器上安装应用最朴素的方法是离线导入镜像。Docker提供了两个便捷命令docker save -o myserver.tar myapp:v1 docker load -i myserver.tardocker save把镜像保存成一个tar文件docker load把这个文件重新加载成镜像。另一个相关命令是docker export和docker import区别在于export导出的是容器文件系统会丢失镜像的历史层信息和部分元数据所以离线场景建议优先使用save和load。这个方案在客户内网部署时非常实用我经常先在联网机器上把镜像打包好再拷贝进内网服务器加载整个部署过程完全不依赖外网。6. 实战MySQL 8.0、Redis主从与多服务编排实战部分选了三个最常见也最能覆盖热词的场景MySQL 8.0、Redis主从、Docker Compose。这三个跑通之后大部分容器化部署的套路你基本就掌握了。6.1 MySQL 8.0挂载、初始化与字符集问题MySQL的Docker部署非常成熟。先准备数据目录和配置目录mkdir -p /data/mysql/{conf,data,logs}然后运行容器docker run -d \ --name mysql8 \ -p 3306:3306 \ -e MYSQL_ROOT_PASSWORDyour_password \ -e TZAsia/Shanghai \ -v /data/mysql/conf:/etc/mysql/conf.d \ -v /data/mysql/data:/var/lib/mysql \ -v /data/mysql/logs:/var/log/mysql \ --restartalways \ mysql:8.0新手很容易踩的坑有三个。第一MySQL 8默认身份认证插件是caching_sha2_password有些老版本客户端连不上需要在配置文件里加default-authentication-pluginmysql_native_password或者创建用户时指定IDENTIFIED WITH mysql_native_password BY ...。第二挂载了conf目录后如果里面没有my.cnfMySQL会认为没有配置文件而使用默认值但目录权限不对会导致容器启动失败建议在conf目录里放一个最小的my.cnf再启动。第三初始化完成后容器日志里会有临时密码如果首次使用官方镜像启动且没有设置MYSQL_ROOT_PASSWORD可以从docker logs mysql8里查看这个密码只在首次初始化时出现错过就只能重新初始化数据目录。6.2 Redis单机与主从复制Redis的Docker部署同样简单。单机版docker run -d \ --name redis \ -p 6379:6379 \ -v /data/redis/redis.conf:/etc/redis/redis.conf \ -v /data/redis/data:/data \ --restartalways \ redis:7 redis-server /etc/redis/redis.conf主从复制部署需要两个容器从节点配置replicaof master 6379。用Docker Compose管理更清晰version: 3 services: redis-master: image: redis:7 container_name: redis-master network_mode: host command: redis-server /etc/redis/redis.conf volumes: - ./master/redis.conf:/etc/redis/redis.conf restart: always redis-slave: image: redis:7 container_name: redis-slave network_mode: host command: redis-server /etc/redis/redis.conf volumes: - ./slave/redis.conf:/etc/redis/redis.conf restart: always从节点配置文件的重点项是replicaof和requirepass以及masterauth。如果主节点设置了密码从节点必须配置masterauth否则复制链路会直接失败。这个坑我踩过好几次日志里显示MASTER - REPLICA sync started但一直卡在receive master info。检查主从是否正常在从节点执行info replication看master_link_status是否为up。6.3 用Docker Compose编排生产环境Docker Compose的核心价值是把多个容器的定义、网络、依赖关系写进一个yaml文件一条docker compose up -d拉起整个环境。生产环境部署Redis主从、MySQL、应用服务、Nginx反代时Compose几乎是标配。需要注意的几点服务名在自定义网络中就是DNS名应用连接数据库直接用服务名即可用depends_on控制启动顺序但注意它只等待容器启动不保证服务就绪必要时在应用层做重试环境变量建议用env_file从文件读取避免在yaml文件里泄露密码数据卷在compose里声明为命名卷或绑定挂载确保数据持久化升级镜像时先docker compose pull再docker compose up -d服务会自动按依赖关系重建。我见过很多团队把Compose文件写成一长串环境变量维护起来非常痛苦。我的习惯是拆分成docker-compose.yml和.env文件前者保持服务定义清晰后者放具体环境的差异配置。这样同一份Compose文件可以比较方便地在开发、测试、生产环境之间切换。6.4 更多常用镜像GitLab、KodBox、Milvus与DifyDocker的优势在于生态丰富很多复杂软件都有现成镜像。GitLab有官方镜像一条docker run加几个挂载目录就能搭建完整的代码托管平台KodBox网盘系统也能几行命令跑起来向量数据库Milvus官方文档直接给出了单机版Compose文件启动后就能体验向量检索Dify这类开源LLM应用平台同样依赖Docker Compose部署解压后在dify-main的docker文件夹路径下复制.env.example为.env再执行docker compose up -d即可。这些场景的共同特点是镜像作者已经把依赖关系、运行参数都安排好了你只要做好端口、数据卷和网络规划就能快速验证和使用。换句话说Docker不是只能跑数据库和Web服务的玩具而是整个软件交付生态的底座。学会看官方镜像文档、理解镜像暴露的端口和数据卷才是使用这些复杂软件的核心能力。7. Docker启动与运行故障高频错误排查实录Docker用得越久遇到的报错越千奇百怪。这一章把最高频的几个错误按“表象、排查链路、根因、解决方案”的结构拆解一遍方便对照排查。7.1 Virtualization support not detected这个错误几乎占了Docker Desktop启动失败案例的一半。原因很简单Windows没有检测到CPU虚拟化支持可能没在BIOS开启也可能是Hyper-V或相关Windows功能未启用。排查步骤是先打开任务管理器在“性能”标签页的CPU部分查看“虚拟化”状态是否显示“已启用”。如果显示“已禁用”或“固件中已禁用”重启进BIOS/UEFI开启Intel VT-x或AMD-V。如果状态正常但还是报错在PowerShell管理员模式下检查Windows功能Get-WindowsOptionalFeature -Online -FeatureName Microsoft-Hyper-V如果机器较老还需要确认CPU本身是否支持虚拟化并满足WSL2的要求。另外一类特殊场景是电脑上同时装了VMware或VirtualBox它们可能和Docker Desktop的虚拟化后端冲突。Docker Desktop 4.x默认使用WSL2后端与新版本VMware冲突较少但如果遇到启动卡在starting the Docker Engine可以先禁用第三方虚拟化软件的嵌套虚拟化功能再试。7.2 failed to connect to the docker api at npipe这个报错常见于Docker Desktop已经启动但终端无法连接引擎。本质是Docker CLI和Docker Engine之间的通信管道没有建立。排查时先确认Docker Desktop右下角鲸鱼图标是绿色运行状态如果图标是红色或灰色点击Restart。然后在PowerShell里执行docker context ls确认当前context指向的是default或desktop-linux。如果context被改过执行docker context use desktop-linux即可。还有一种情况是Docker Desktop刚装完终端还开着旧环境变量新开一个PowerShell窗口通常就好了。这个错误容易让人误以为Docker没安装成功其实很可能只是CLI和引擎的通信上下文错乱了。遇到先别急着卸载重装按“引擎状态、context指向、终端环境变量”的顺序排查大部分都能解决。7.3 unexpected EOF与拉取中断docker pull或docker run时出现unexpected EOF通常是网络问题导致连接被中断。长连接被防火墙切断、代理配置错误、磁盘空间不足都可能出现。如果使用代理确认Docker Desktop代理设置与实际代理端口一致如果没使用代理但系统里配置了HTTP_PROXY、HTTPS_PROXY环境变量反而会导致Docker请求走一个不存在的代理需要临时清除unset HTTP_PROXY HTTPS_PROXY磁盘空间不足时Docker会报no space left on device检查/var/lib/docker所在分区使用率定期执行docker system prune清理无用的镜像和容器。我在一台跑满日志的服务器上遇到过类似问题日志直接把磁盘撑爆导致所有容器同时异常退出最后靠清日志和调日志轮转才恢复。这也把话题引到下面这个常见的配置上。7.4 磁盘、日志与端口类常见问题容器日志无限增长是非常隐蔽的运维隐患。默认的json-file日志驱动会让容器日志一直追加不做限制的话几个月就能吃掉整个磁盘。建议在daemon.json里加上{ log-driver: json-file, log-opts: { max-size: 50m, max-file: 3 } }这样每个容器日志文件单文件不超过50M最多保留3个配置完重启Docker后对新容器生效。时间不同步也是常见问题容器默认使用UTC如果应用要读取本地时间可以在docker run时通过-e TZAsia/Shanghai设置时区或者在Dockerfile里执行RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime。端口被占用时docker run会提示bind失败用netstat -ano | findstr 3306这类命令找到占用进程然后决定释放端口还是换宿主机的映射端口。最后分享一个我坚持了很久的习惯每次改动docker run参数或Compose文件之前先docker ps看一眼当前服务状态docker compose down -v操作前务必确认数据卷里没有需要保留的东西。删容器容易删数据卷里唯一的数据库目录可就真要哭了。Docker的学习曲线其实不陡最难的部分往往不是命令本身而是建立起“镜像不可变、容器可替换、数据必须持久化”的思维习惯这个习惯一旦形成后面接触Kubernetes、Serverless都会轻松很多。