服务器买回来第一件事是什么?很多人会直接装宝塔、装LNMP、跑业务,但我的习惯是先花半天时间把Docker环境彻底捋顺。原因很简单:不管后续是部署一个个人博客、跑一套数据分析任务,还是给团队搭一套内部工具链,Docker都能把环境一致性这个问题一次性解决掉。这篇内容就是我在多台Linux服务器上反复踩坑后,整理出的一套从零开始的Docker部署整体流程,包含完整的命令、配置参数、排错思路,以及那些文档里不会写明白的注意事项。
这篇文章适合谁看?刚接触服务器运维、准备把自己的服务容器化的开发者,也包括已经在用Docker但想搞清楚底层配置逻辑、把部署流程规范化的运维朋友。我不会只给你一串命令让你照着敲,而是会讲清楚每一步为什么要这么做、哪些参数能改、哪些坑绕不过去,让你部署完一套环境之后,遇到问题自己能定位,而不是到处搜索复制粘贴。
1. 部署前的整体规划与思路拆解
1.1 先想清楚:你要用Docker解决什么问题
在动手敲命令之前,我建议你先回答一个问题:在这台Linux服务器上,Docker在你的架构里扮演什么角色?
我见过不少朋友把Docker当成虚拟机用——一个容器里塞一堆进程,跑坏了就进去修,这其实违背了容器设计的初衷。Docker最适合的场景是单一容器跑单一主进程,通过容器之间的网络通信和数据卷共享来协同工作。比如你要部署一套应用,合理的拆法是:一个容器跑Nginx做反向代理,一个容器跑后端服务,一个容器跑数据库,甚至Redis、消息队列都各自独立。这样做的好处是显而易见的:某个组件版本升级、配置调整,不会影响其他服务;资源占用也被天然隔离,某个进程出现内存泄漏,不会拖垮整台机器。
这里要补充一个我在实际项目中总结出的判断标准:如果你的服务是无状态的(可以随便重启、可以水平扩展),或者需要频繁更新版本、需要在不同机器间迁移,那就非常适合容器化。反过来,如果你要跑的是那种强依赖服务器特殊硬件、需要直接操作GPU或者需要精细调整内核参数的负载,容器化的成本会比较高,建议谨慎评估,不要为了用Docker而用Docker。
1.2 版本与发行版选型:CentOS、Ubuntu、Debian怎么选
Docker本身对Linux发行版的兼容性很好,但不同发行版的包管理方式、系统初始化机制不一样,安装步骤也会有些差异。就我实测的经验来说,做生产环境部署,我优先推荐两条路线:
一条是Ubuntu/Debian系,优点是软件源里的包比较新,社区文档丰富,遇到问题一搜一大把解决方案,apt管理依赖也省心,非常适合新手上手;另一条是CentOS/RHEL系或者它的替代品Rocky Linux、AlmaLinux,优点是稳定性口碑好,很多企业存量服务器都是这个系统,如果你所在的公司统一用这个体系,保持一致性反而比追求新版本更重要。
我这边目前的主力服务器是一台Ubuntu 22.04 LTS,选择它的原因很实际:LTS版本有五年的安全更新支持,不用频繁折腾系统升级;同时22.04默认的Linux内核是5.15,对cgroups v2、iptables-nft这些现代容器依赖的特性支持很完整,Docker跑起来基本是零障碍。
如果你是CentOS 7的老用户,这里必须多提醒一句:CentOS 7的内核是3.10,对overlay2存储驱动支持得不算好,Docker官方其实已经不再建议在生产环境继续用这个组合。有条件的话还是升级到Rocky Linux 8+或者直接换Ubuntu LTS,别在存量系统上硬扛。
1.3 部署路径总览:从装系统到跑容器的四个阶段
我把整套部署流程拆成四个阶段,后面每一节都会按这个顺序来展开:
- 阶段一:环境检查与基础优化——确认系统版本、内核参数、清理旧版本、配置时钟同步和DNS,这是很多人会跳过的部分,但实际上大部分安装失败都源于这个阶段没做好。
- 阶段二:Docker引擎安装——包括配置软件源、安装docker-ce和相关组件、启动服务验证运行状态。
- 阶段三:运行时配置调优——镜像加速、存储路径规划、cgroup驱动对齐、日志策略设置,这部分是"部署完能跑"和"部署完跑得稳"的分水岭。
- 阶段四:容器化业务落地——从拉取镜像、启动容器到编写compose文件,把一套完整服务串起来。
整个流程走完,你的服务器就具备了一个稳定、可持续扩展的容器运行基础。
2. 环境准备与前置检查
2.1 硬件配置与系统版本确认
部署之前先花三分钟检查一下服务器配置,避免后续容器跑起来才发现资源不足。我这里会依次查看CPU核数、内存大小、磁盘剩余空间和系统版本:
# 查看CPU信息 lscpu | grep -E "CPU\(s\)|Architecture" # 查看内存总量与可用量 free -h # 查看磁盘挂载情况 df -h # 查看发行版版本信息 cat /etc/os-release这几个命令输出里,我最关注的是磁盘的挂载点和剩余空间。Docker的默认数据存储目录是/var/lib/docker,镜像文件、容器读写层、数据卷全都存在这里。如果根分区空间不大(比如只有20G的云主机),而你又买了一块数据盘挂载在/data,那最好直接把Docker的数据目录迁到数据盘上,这一步我会在后面的配置章节详细说。
关于资源配置,我给一个参考线:纯跑几个小型Web服务(Nginx、Node.js、MySQL各一个),2核4G的机器勉强够用;如果要跑Java应用、大数据组件这类吃内存的东西,起步建议4核8G。容器不是魔法,它只是隔离了进程,物理资源总归是上限。
2.2 关键内核参数与模块检查
Docker运行依赖Linux内核的若干特性,最核心的两个是网络命名空间和cgroups资源隔离。绝大多数现代发行版默认都开启了这些特性,但我们还是要确认一下,避免在特定云主机镜像上碰到精简过内核的情况:
# 检查cgroup版本(Docker推荐使用cgroup v2) stat -fc %T /sys/fs/cgroup/ # 检查overlay模块是否加载 lsmod | grep overlay # 检查iptables的nat表是否正常 iptables -t nat -L -n | head -20关于cgroup版本这里多说一句。如果输出结果显示cgroup2fs,说明系统用的是cgroup v2,那么安装的Docker版本建议不低于20.10(最好是23.0+),因为早期版本对cgroup v2的支持不完整;如果显示tmpfs,则是cgroup v1,属于老配置,大部分Docker版本都能兼容。有些云厂商提供的自定义镜像可能会禁用overlay模块,导致Docker无法使用overlay2存储驱动,只能退回vfs这种性能和磁盘占用都很难看的模式,所以预先检查很有必要。
2.3 系统基础优化:时间同步、防火墙与软件源
在正式安装Docker之前,我通常会把下面三项基础配置顺手做掉,它们不直接属于Docker安装步骤,但决定了你后面用起来顺不顺手。
第一项:配置时间同步。容器日志的时区错乱、HTTPS证书校验失败、集群节点间通信异常,这些问题有很大概率是系统时间漂移引起的。使用systemd-timesyncd做基础同步就够了:
# 确认时间同步服务状态 timedatectl # 如果NTP未激活,手动开启 timedatectl set-ntp true # 设置时区为Asia/Shanghai(按需) timedatectl set-timezone Asia/Shanghai注意,容器默认使用UTC时区,如果你希望容器内日志也显示北京时间,可以在启动容器时通过-e TZ=Asia/Shanghai传入环境变量,或者在compose文件的environment里声明。这个细节很细微,但排查日志时特别有用。
第二项:理清防火墙策略。Docker服务启动后,会自动在iptables里插入自己的规则链,如果服务器上还跑着firewalld或ufw,它们之间有时会发生配置冲突。我的习惯是:生产服务器用云平台的安全组策略做第一层隔离,系统层防火墙只放行必要的端口(如SSH、业务端口),并把Docker需要的端口段一并放行。
以ufw为例,如果要放行8080端口:
ufw allow 22/tcp ufw allow 8080/tcp ufw enable systemctl restart docker有一种常见误区是"关掉防火墙万事大吉",千万别这么干,公网服务器裸奔的风险太大了。
第三项:替换软件源。这步主要针对国内服务器访问官方源慢的情况。不管是Ubuntu的apt源还是Docker的apt源,都建议换成国内镜像站。具体做法我会在下一节安装Docker时一起演示,这里先不展开。
3. Docker完整安装流程实操
3.1 清理旧版本与安装依赖包
有些服务器之前可能通过yum install docker或者apt install docker.io装过老版本的Docker,这时候不能直接覆盖安装,容易留下版本残留问题。先做一遍清理:
# Debian/Ubuntu系 sudo apt remove docker docker-engine docker.io containerd runc # CentOS/RHEL系 sudo yum remove docker docker-client docker-common docker-engine这步操作只会删除Docker相关的旧包装,不会动你已经存在的容器数据和镜像文件。如果你确定旧数据不需要了,可以手动清理/var/lib/docker目录,否则保留下来后面还能救回来。
然后安装依赖包,接下来以Ubuntu 22.04为例:
sudo apt update sudo apt install -y ca-certificates curl gnupg lsb-release这些包的作用分别是:ca-certificates用于HTTPS证书验证,curl用来下载密钥,gnupg用来导入GPG公钥,lsb-release用来识别系统版本。少装任何一个都可能让后续步骤报些莫名其妙的错。
3.2 配置Docker软件源(国内服务器优化)
Docker官方推荐的安装方式是从download.docker.com拉取安装包,但国内网络环境访问这个域名经常超时或速度极慢。因此我建议先把软件源切换为国内镜像站。在Ubuntu系统上,具体做法是:
# 添加Docker官方GPG密钥(通过国内源代理下载) sudo install -m 0755 -d /etc/apt/keyrings curl -fsSL https://mirrors.aliyun.com/docker-ce/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg sudo chmod a+r /etc/apt/keyrings/docker.gpg # 写入软件源配置 echo \ "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://mirrors.aliyun.com/docker-ce/linux/ubuntu \ $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/nullCentOS/RHEL系的做法类似,区别在于用yum的repofile格式:
sudo yum install -y yum-utils sudo yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo这里有个易错点:$(lsb_release -cs)会输出系统的代号,比如Ubuntu 22.04是jammy。如果你用的系统版本太老、代号不受Docker源支持,这个命令可能会失败或者写入一个无效的源地址,此时可以手动指定一个相近的版本代号。这个我在排错那节还会提到。
3.3 安装docker-ce并验证运行状态
源配置好之后,更新索引并安装核心组件:
sudo apt update sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin安装的这几个组件分别对应Docker引擎、命令行客户端、容器运行时、BuildKit构建工具和Compose插件。注意我把docker-compose-plugin也一并装上了,这样你就不需要再去单独下载docker-compose二进制文件,直接使用docker compose子命令(注意新版命令中间有空格,不是老版的docker-compose)。
安装完成后启动服务并设置开机自启:
sudo systemctl enable docker --now sudo systemctl status docker看到Active: active (running)后,用docker version确认客户端和服务端都正常:
sudo docker version输出里会分成Client和Server两段,如果Server那段的Version也能正常显示,说明Docker引擎已经跑起来了。接下来跑一下官方推荐的验证命令:
sudo docker run hello-world这个镜像只有几KB,网络不通畅的话可能会卡在拉取阶段。如果你恰好遇到拉不动的情况,先别急着排查网络,把下面一节镜像加速配好再试,大概率能直接解决。
3.4 免sudo配置与普通用户管理
默认情况下,执行docker命令需要root权限或sudo。日常操作总是加sudo比较繁琐,而且有些第三方CI工具在普通用户环境下调用Docker会因权限不足意外失败。我的做法是创建一个docker用户组,把需要操作Docker的账户加进去,然后重新登录使组权限生效:
sudo groupadd docker sudo usermod -aG docker $USER newgrp docker这样设置之后,普通用户就能直接执行docker ps、docker run这些命令了。要特别提醒的是:能操作Docker的用户基本等同于有root权限,因为容器可以挂载宿主机任意路径、可以配置特权模式,风险非常大。所以这些组成员务必控制在可信任的运维人员范围内,别图方便把一堆人都加进来,这是生产环境的高危操作。
4. 核心配置与运行时优化
4.1 镜像加速配置
Docker安装完成后的第一件大事,不是急着拉镜像,而是配置镜像加速器。国内直连Docker Hub的速度,大家应该都深有体会,一个几百MB的镜像拉半小时是常有的事,甚至直接超时失败。目前很多云厂商都提供容器镜像加速服务,配置方法是在/etc/docker/daemon.json里写入registry-mirrors:
sudo mkdir -p /etc/docker sudo tee /etc/docker/daemon.json <<-'EOF' { "registry-mirrors": [ "https://docker.m.daocloud.io", "https://dockerproxy.com", "https://docker.nju.edu.cn" ] } EOF注意一点:这些第三方加速源的可用性和速度是动态变化的,没有一个是永久稳定的。建议你用几个常见源都试一遍,哪个通就用哪个,或者直接选用自己云厂商提供的加速地址。改完配置后要重启Docker:
sudo systemctl daemon-reload sudo systemctl restart docker验证加速器是否生效,可以执行docker info,在输出里找到Registry Mirrors字段,看到你配置的地址就说明生效了。
4.2 数据目录迁移与磁盘规划
我前面提到,Docker默认把数据放在/var/lib/docker。很多云服务器的系统盘只有40G左右,几个镜像加数据卷就能把根分区塞满,进而拖垮整个操作系统。所以我习惯把这个目录迁移到独立的数据盘上。
假设你有一块挂载在/data的独立数据盘,推荐的做法是直接把数据目录迁过去:
# 停止Docker服务,避免写入 sudo systemctl stop docker # 把现有数据目录整体移动到新位置 sudo mv /var/lib/docker /data/docker # 创建软链接,让Docker“无感知”新路径 sudo ln -s /data/docker /var/lib/docker # 启动Docker并验证 sudo systemctl start docker sudo docker info | grep "Docker Root Dir"还有一种做法是在/etc/docker/daemon.json里显式指定>{ "data-root": "/data/docker" }
两种方案我推荐第二种,更直观、不依赖软链接,新人都能看懂配置意图。迁移前要注意磁盘性能,容器对磁盘IO的要求比普通应用高得多,数据盘尽量选SSD,尤其是数据库类容器,机械盘的随机读写性能会让你怀疑人生。
4.3 cgroup驱动对齐与资源限制
Docker和systemd都通过cgroup来管理进程资源,但这两者对cgroup驱动的要求不一样。如果Docker使用cgroupfs驱动而系统使用systemd驱动,在部分新内核和发行版上会出现资源统计异常,严重时会导致容器无法启动。
解决办法是在daemon.json里显式声明Docker使用systemd作为cgroup驱动:
{ "exec-opts": ["native.cgroupdriver=systemd"], "log-driver": "json-file", "log-opts": { "max-size": "100m", "max-file": "3" } }这个配置里我顺手把日志策略也设置好了:单个容器日志文件超过100MB会自动轮转,最多保留3个文件。别小看日志限制,我见过好几次服务器磁盘被疯狂刷日志的容器打满的情况,尤其是那些开了debug级别的应用,一天写几十个GB日志都是正常的。这个配置在大规模容器集群里是不可或缺的,单机部署同样很有必要。
4.4 网络模式与应用场景选择
Docker提供多种网络模式,不同模式解决不同场景的问题,我把常见的几种整理成了一张对照表:
| 网络模式 | 特点 | 适用场景 |
|---|---|---|
| bridge(默认) | 容器间可通过虚拟网桥互相通信,默认通过NAT访问外网 | 单机多容器互联,最常用 |
| host | 容器直接使用宿主机网络栈,无端口映射开销 | 网络性能要求极高且端口规划简单的场景 |
| none | 容器无网络配置,完全隔离 | 纯离线任务、安全敏感场景 |
| container:容器名 | 与指定容器共享网络命名空间 | 调试网络问题、sidecar模式 |
单机部署我基本都用默认的bridge模式,但有一个前提:容器间通信不要依赖IP地址。Docker网桥的IP分配是不稳定的,容器重启后IP就可能变了,服务间调用应该通过--link或者自定义网络里的DNS解析来互相访问对方容器名(也就是服务发现)。更优雅的方式是创建自定义bridge网络:
docker network create myapp-net docker run -d --name nginx --network myapp-net nginx:latest docker run -d --name backend --network myapp-net backend:latest之后backend容器里直接访问nginx:80就行,这个域名会被Docker内置DNS解析到对应容器的IP。这是我在部署多容器服务时一定会用的方案,也让后面的compose配置更顺滑。
5. 部署实操:从拉取镜像到业务落地
5.1 首次业务部署:拉取镜像与启动容器
配置调优结束,环境具备了上线能力,我们找一个实际场景完整走一遍:在一台新服务器上用Nginx加一个简单的HTML页面,把容器从零启动起来。选择Nginx做示例是因为它镜像小、启动快、依赖少,能让我们把精力集中在流程本身。
先拉取镜像:
docker pull nginx:1.26-alpine这里特意指定alpine标签,因为alpine基础镜像非常小,Nginx加系统总大小不过几十MB,比标准版(约190MB)轻量很多,拉取和启动都快得多。
然后准备一个简单的挂载目录和页面文件:
mkdir -p /opt/nginx/html echo '<h1>Hello Docker</h1>' > /opt/nginx/html/index.html执行启动命令:
docker run -d \ --name my-nginx \ -p 8080:80 \ -v /opt/nginx/html:/usr/share/nginx/html:ro \ -e TZ=Asia/Shanghai \ --restart always \ nginx:1.26-alpine启动后可以用docker ps查看状态,用curl localhost:8080验证页面。这看似简单的命令,其实每个参数都值得展开说说。
5.2 参数语义详解:端口映射、数据卷与重启策略
上面的启动命令里,-p 8080:80是端口映射:宿主机8080端口接收的流量会转发到容器的80端口。宿主机端口不能冲突,两台容器不能同时占用一个宿主机端口,这点要记住,后面排查容器启动失败时经常遇到端口被占用的报错。
-v参数是数据卷挂载,把宿主机的/opt/nginx/html目录映射到容器的/usr/share/nginx/html,并加了ro标志表示只读。这样修改宿主机上的HTML文件,容器里立即生效,不需要重新构建镜像或者进入容器,特别适合开发和更新静态文件。数据卷的默认权限是不可覆盖的,即使容器内部的进程写了文件,也不会改动宿主机挂载目录的内容(只读时更安全)。
--restart always参数设置容器退出后的重启策略。这个我建议在生产环境用起来:宿主机重启后容器会自动跟着起来,容器进程异常退出时Docker守护进程也会尝试拉起它。注意always和unless-stopped的区别:前者只要容器没被手动stop,关机重启后一定会运行;后者在手动stop后,即使机器重启也不会自动运行。如果你希望某容器是"显式关闭后别再来烦我"的类型,用unless-stopped更合适。
-e TZ=Asia/Shanghai设置容器内时区环境变量,对于解决日志时间和当前时间对不上的问题很有效。有些镜像还支持通过环境变量配置数据库密码、应用密钥等参数,这也是容器配置管理的基础用法。
5.3 使用Docker Compose编排多服务
单容器部署用docker run就够了,但一旦服务变多,比如Nginx加后端加Redis加MySQL加消息队列,每次启动都敲一长串命令,既容易错也难维护。这时候应该改用Docker Compose把服务定义固化成一个docker-compose.yml文件。
下面是一个典型的Web应用编排示例:
version: '3.8' services: nginx: image: nginx:1.26-alpine ports: - "80:80" volumes: - ./nginx/conf.d:/etc/nginx/conf.d:ro - ./nginx/html:/usr/share/nginx/html:ro depends_on: - backend restart: always backend: image: myapp-backend:1.0.0 environment: - DB_HOST=mysql - DB_PORT=3306 - TZ=Asia/Shanghai ports: - "8080:8080" restart: always depends_on: - mysql mysql: image: mysql:8.0 environment: - MYSQL_ROOT_PASSWORD=StrongP@ssw0rd volumes: - mysql-data:/var/lib/mysql restart: always volumes: mysql-data:这个文件值得注意的地方有两个:一是服务间的依赖通过depends_on声明,比如backend依赖mysql,compose会优先启动mysql;二是数据库的数据通过命名卷mysql-data持久化,卷的数据存在/var/lib/docker/volumes下,容器删除重建数据都不会丢。千万不要试图把MySQL的数据放在容器可写层里,容器一删数据就没了,这个教训我公开讲过很多次了。
启动全部服务:
docker compose up -d查看服务状态和日志:
docker compose ps docker compose logs -f扩容某个服务:
docker compose up -d --scale backend=36. 常见问题与排查技巧实录
6.1 安装阶段常见错误与解决办法
问题一:apt安装时提示"无法下载软件包"。多半是Docker源没配好或者系统代号不对。检查方法是用apt-cache policy docker-ce看能不能搜到软件包,搜不到就核对/etc/apt/sources.list.d/docker.list里的lsb_release -cs输出是否是一个可用的代号。某些精简系统可能没有lsb_release命令,需要先安装:
sudo apt install lsb-release问题二:Docker服务启动失败,日志提示冲突。很多人忽略了服务器上还残留着老版本的containerd或docker,安装新版后在启动时数据库目录冲突。彻底清理旧组件再重装:
sudo systemctl stop docker sudo rm -rf /var/lib/docker sudo systemctl start docker这是在确定容器数据可丢弃时使用的方法,操作前一定确认数据有备份。
问题三:docker: permission denied错误。说明当前用户不在docker用户组里,执行之前第3.4小节的组配置步骤,并且重新登录一次让组生效。
6.2 运行时故障排查:日志、状态与权限三板斧
容器启动失败是最常见的运行时问题,我通常按三步排查:
# 第一步:查看容器状态 docker ps -a # 第二步:查看容器日志 docker logs --tail 100 <容器名> # 第三步:检查容器配置和宿主机资源 docker inspect <容器名> df -hdocker logs是所有排查操作里信息价值最高的,容器内部到底发生了什么、报了什么错误,都会写在日志文件里。docker inspect可以看到容器的详细配置,包括挂载情况、端口绑定、环境变量、健康检查状态等,是定位复杂问题的利器。
举一个我实际碰到过的例子:某次Nginx容器反复启动失败,日志里只显示"exited(1)"没有更多细节。用docker inspect一看,Mounts列表里有一个挂载路径的宿主机目录被误删了,容器启动时无法绑定目录,自然起不来。这种问题光看日志基本找不到线索,必须靠inspect。
另外有一个排查目录资源的关键点:磁盘满了也会导致容器启动失败,因为Docker写入需要空间。如果df -h显示根分区100%,优先清理/var/lib/docker/containers下的日志文件,或者直接用我第4.3节配置的日志轮转参数。其次是清理无用的镜像和悬空镜像:
docker system prune -a这行命令会干掉所有不用的镜像和停止的容器,操作前先确认没有需要保留的东西。
6.3 网络问题定位:跨容器访问不通的三种场景
网络问题在容器化环境里花样最多,我最近排查的经验总结起来主要是以下三种:
场景一:容器间通过容器名ping不通。最常见的原因是两个容器不在同一个自定义bridge网络里。用默认的bridge网络,Docker的DNS解析对容器名的支持并不可靠。解决方法是创建一个自定义网络,把两个容器都加入其中,参考第4.4节的做法。
场景二:容器能访问外网但业务请求卡住。有一种隐蔽情况是MTU不匹配。云服务器底层虚拟网络的MTU可能小于默认值1500,导致容器访问外网时大包被丢弃。解决方法是创建自定义网络时指定小一点的MTU:
docker network create --driver bridge --opt com.docker.network.driver.mtu=1400 myapp-net遇到某些云主机上容器DNS解析超时、HTTP请求无响应的情况,可以检查一下这个参数,往往一改就好了。
场景三:宿主机防火墙规则与Docker的iptables冲突。表现为容器映射的端口外网访问不了,但本机curl正常。这通常是因为防火墙拦住了新端口,或者是Docker的iptables规则被防火墙服务覆盖。解决方法是先停止防火墙服务再启动Docker,确认规则无冲突后,再放行所需端口并重新启动防火墙,找对顺序就行了。
6.4 独家避坑技巧与习惯建议
最后分享几个我长期养成的运维习惯,它们看起来不起眼,但能避免大量低级的线上事故。
习惯一:容器名旁边永远加上项目前缀。比如project1-nginx、project1-backend,不要用裸的nginx、mysql当容器名。多项目共存一台机器时,名字冲突不仅导致启动失败,还会让人看docker ps列表时根本分不清哪个容器属于哪个业务。
习惯二:镜像标签绝不使用latest。在生产环境,我会把标签具体到版本号,比如nginx:1.26.2-alpine,而不是nginx:latest。原因很直接:latest是移动的指针,某天你执行docker pull后,服务器上的镜像静默升级了大版本,配置可能不兼容,服务直接起不来。运维的世界里,确定性比新鲜感重要得多。
习惯三:持久化数据定期备份。Docker的docker export和docker cp适合临时拷贝文件,不能当作备份方案。对MySQL这类数据库容器,备份应该用数据库自带的工具(如mysqldump)在宿主机上定时执行,把生成的备份文件放到独立的数据卷或云存储。容器本身是可丢弃的,数据才是无价的。
习惯四:写一个部署说明文档。每次部署完一套环境,花十分钟把镜像版本、端口映射、数据卷路径、环境变量、部署日期和操作人整理到一起,放到项目的docs目录里。这个文档对三个多月后的自己就像灯塔一样宝贵,那时候你可能早已忘了当初为什么特意设置了某个奇怪的挂载路径。
7. 后续扩展与个人体会
我在这套流程上踩过的坑,翻来覆去其实都归结到几个关键词:版本、路径、权限、网络。环境一致性是容器最吸引人的地方,但前提是你得把宿主机的环境和容器的运行参数都搞清楚。很多人部署完就不管了,等到线上出问题再回头排查,那时候发现daemon.json里少配了一个加速源、数据目录塞满了系统盘、cgroup驱动不匹配导致资源限制失灵,后悔都来不及。
如果你打算在这套基础环境上继续深耕,我建议沿着三个方向扩展:一是把部署过程脚本化,比如用Ansible写好playbook,以后新服务器五分钟就能复现这套配置;二是研究一下容器监控,把docker stats的数据接入到通知系统里,资源飙高就能及早发现;三是学习镜像构建规范,给项目写一份合适的多阶段构建Dockerfile,做出来的镜像既安全又干净。把基础设施当成产品来维护,回报率远高于每次都在生产环境临时救火。
回到开头那句话,部署Docker整体流程并不复杂,真正拉开大家差距的,是对细节的敬畏和排查问题的思路。希望这篇内容能把你的运维工具箱补得更扎实一些——当你下一次面对一台新服务器时,能从从容容地把环境从零到一搭好,而不是复制一串不明所以的命令然后祈祷它能跑起来。