第 25/30 天(重访进阶版)
前言
在首发文章中,我们用 Docker Compose 搭建了 LEMP 应用栈,并用 Minikube 体验了 Kubernetes 的基本流程。但在生产环境中,”能跑起来”和”可靠地跑”之间隔着无数细节:数据库还没就绪应用就启动了怎么办?容器日志把磁盘写满怎么办?Minikube 真的能上生产吗?单节点 K8s 集群又有哪些必须绕开的坑?
本文是容器编排进阶实战:上半部分讲 Docker Compose 的生产化改造(健康检查、依赖编排、多环境管理、日志治理),下半部分用 kubeadm 搭建一个真实的生产级单节点 Kubernetes 集群(而不是玩具级的 Minikube),并给出备份、升级、排查的完整方案。每一节都配有可以直接复制的命令和真实踩坑经验。
一、Docker Compose 生产化:从「能跑」到「可靠」
1.1 健康检查与启动依赖:根治「数据库未就绪」竞态
入门教程里的 depends_on 只保证启动顺序,不保证可用状态。MySQL 容器刚启动时端口还没就绪,应用容器先跑起来连接数据库必然失败。生产级写法是 healthcheck + depends_on.condition: service_healthy:
# docker-compose.prod.yml
services:
db:
image: mysql:8.4
environment:
MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD:?请设置环境变量}
MYSQL_DATABASE: ${DB_NAME}
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-p${MYSQL_ROOT_PASSWORD}"]
interval: 5s
timeout: 3s
retries: 12
start_period: 30s
app:
image: myapp:latest
depends_on:
db:
condition: service_healthy
restart: unless-stopped
# 检查各服务健康状态(HEALTHY 才是真就绪)
docker compose -f docker-compose.prod.yml ps
# 输出示例:
# NAME IMAGE STATUS
# db mysql:8.4 Up 40 seconds (healthy)
# app myapp:latest Up 5 seconds
⚠️ 关键坑:
${VAR:?错误提示}语法让 Compose 在变量缺失时直接报错退出,防止把空密码带到生产环境。这是 CI 中非常实用的防护。
1.2 资源限制与优雅停机
不加资源限制的容器会吃光宿主机内存引发 OOM;stop_grace_period 太短则应用来不及完成收尾。
services:
app:
image: myapp:latest
deploy:
resources:
limits:
cpus: "1.0"
memory: 512M
reservations:
memory: 256M
stop_grace_period: 30s
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
# 查看容器实际资源占用,确认限制生效
docker stats --no-stream
# 输出示例:
# CONTAINER ID NAME CPU % MEM USAGE / LIMIT
# a1b2c3d4e5f6 app 12.50% 268MiB / 512MiB
⚠️ 关键坑:
logging.max-size必须显式配置——不配置的话 json-file 日志无限增长,/var/lib/docker/containers 目录会被撑爆,这是生产环境最常见的磁盘故障之一。
1.3 多环境管理:一份 Compose 文件走天下
用 .env 分离环境差异,用 profiles 控制可选服务(如调试工具只在开发环境启动):
# .env.production 示例
DB_NAME=myapp
DB_ROOT_PASSWORD=SuperSecret123!
NGINX_PORT=8080
# docker-compose.yml(基础文件,不写死任何密码)
version: "3.8"
services:
nginx:
image: nginx:1.26-alpine
ports:
- "${NGINX_PORT:-8080}:80"
profiles: ["web"]
# 仅开发环境启用
phpmyadmin:
image: phpmyadmin:latest
profiles: ["dev"]
# 生产启动(只起 nginx,不起 phpmyadmin)
docker compose --env-file .env.production --profile web up -d
# 开发启动(附加上调试工具)
docker compose --env-file .env.development --profile web --profile dev up -d
# 校验配置是否正确(CI 中用于静态检查,不会启动任何容器)
docker compose config --quiet && echo "COMPOSE FILE VALID"
1.4 Compose 生产常见坑速查
| 现象 | 原因 | 解决 |
|---|---|---|
| 容器时间比宿主机晚 8 小时 | 镜像默认 UTC 时区 | 挂载 /etc/localtime 或设置 TZ=Asia/Shanghai |
docker compose down 后数据丢失 |
未用命名卷,数据在容器可写层 | 数据目录必须用 volumes: 命名的卷或 bind mount |
| 升级镜像后容器没变 | 镜像 tag 用了 latest 且本地有缓存 |
用固定版本号 tag;docker compose up -d --pull always |
| 端口冲突 | 多个项目都映射 3306 | 容器间走 Compose 网络用服务名互访,仅网关端口映射到宿主机 |
二、Kubernetes 单节点:用 kubeadm 搭生产级集群
2.1 为什么生产不用 Minikube
Minikube 是本地开发工具:单二进制、资源受限、kubelet 是独立进程管理而非 systemd,无法平滑升级到多节点。生产级单节点集群应该用 kubeadm——它生成的是与云厂商托管集群同构的标准集群,以后加节点只需一条 kubeadm join。
2.2 前置准备:内核模块、sysctl 与 containerd
# 1. 关闭 swap(kubelet 强校验,不关则无法启动)
sudo swapoff -a
sudo sed -i '/ swap / s/^/#/' /etc/fstab
# 2. 加载内核模块
cat <<'EOF' | sudo tee /etc/modules-load.d/k8s.conf
overlay
br_netfilter
EOF
sudo modprobe overlay && sudo modprobe br_netfilter
# 3. 设置网络转发参数
cat <<'EOF' | sudo tee /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.ip_forward = 1
EOF
sudo sysctl --system
containerd 配置是最容易踩的坑:Kubernetes 1.28+ 中 kubelet 默认使用 systemd cgroup driver,containerd 必须保持一致,否则 kubelet 报 failed to run Kubelet: failed to validate kubelet flags ... cgroup driver is "cgroupfs":
# 查看当前 containerd 版本
containerd --version
# 生成默认配置并启用 SystemdCgroup
sudo mkdir -p /etc/containerd
containerd config default | sudo tee /etc/containerd/config.toml > /dev/null
sudo sed -i 's/SystemdCgroup = false/SystemdCgroup = true/' /etc/containerd/config.toml
sudo systemctl restart containerd
2.3 安装 kubeadm、kubelet、kubectl 并初始化
# 添加 Kubernetes apt 源(以 1.30 为例,版本号可按需调整)
sudo apt-get update
sudo apt-get install -y apt-transport-https ca-certificates curl gpg
curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.30/deb/Release.key |
sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg
echo 'deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.30/deb/ /' |
sudo tee /etc/apt/sources.list.d/kubernetes.list
sudo apt-get update
sudo apt-get install -y kubelet kubeadm kubectl
sudo apt-mark hold kubelet kubeadm kubectl
# 初始化单节点集群(Calico 需要的网段)
sudo kubeadm init
--pod-network-cidr=192.168.0.0/16
--apiserver-advertise-address=192.168.1.10
# 配置 kubectl 访问权限(普通用户)
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
2.4 安装 Calico CNI 并让单节点参与调度
# 安装 Calico 网络插件
curl -L https://raw.githubusercontent.com/projectcalico/calico/v3.28/manifests/calico.yaml -O
kubectl apply -f calico.yaml
# 等待所有系统 Pod 就绪
kubectl get pods -n kube-system
# 单节点集群默认不调度业务 Pod(控制面有 taint),需要移除:
kubectl taint nodes --all node-role.kubernetes.io/control-plane-
# 输出示例:node/ubuntu-node untainted
# 部署测试应用验证集群可用
kubectl create deployment nginx-demo --image=nginx:alpine --replicas=2
kubectl get pods -o wide
⚠️ 关键坑:kubeadm 版本号必须与 kubelet/kubectl 完全一致;
apt-mark hold防止apt upgrade悄悄升级 kubeadm 造成版本错配。
三、生产必备:备份、升级与故障排查
3.1 etcd 备份:救命的最后一根稻草
集群的唯一持久状态在 etcd 里,必须定期快照:
# etcd 备份(kubeadm 集群中 etcd 以 static pod 运行)
sudo ETCDCTL_API=3 etcdctl
--endpoints=https://127.0.0.1:2379
--cacert=/etc/kubernetes/pki/etcd/ca.crt
--cert=/etc/kubernetes/pki/etcd/server.crt
--key=/etc/kubernetes/pki/etcd/server.key
snapshot save /backup/etcd-snapshot-$(date +%F).db
# 验证快照完整性
sudo ETCDCTL_API=3 etcdctl snapshot status /backup/etcd-snapshot-2026-08-13.db
# 输出示例:{"hash":..., "revision":12345, "totalKey":678, "totalSize":1048576}
# 配合 cron 每日自动备份 + rsync 异地保存(参考本系列第 13 天)
3.2 故障排查三板斧
# 1. 看 Pod 状态与事件
kubectl describe pod <pod-name> -n <namespace>
# 2. 看容器启动日志(-p 看上一次崩溃前的日志)
kubectl logs <pod-name> -n <namespace> --previous
# 3. containerd 环境没有 docker 命令,用 crictl 直接调试
sudo crictl ps -a # 列出所有容器
sudo crictl logs <container-id> # 查看容器日志
sudo crictl images # 查看本地镜像
⚠️ 关键坑:kubeadm 集群默认 CRI 是 containerd,没有 Docker 命令可用。别再敲
docker ps排查 K8s 容器,crictl才是正解。另外国内服务器拉镜像慢时,编辑/etc/containerd/config.toml的registry.mirrors配置镜像加速器后systemctl restart containerd。
3.3 升级策略
# 查看可升级版本与计划
sudo kubeadm upgrade plan
# 逐版本升级(先升级 kubeadm,再 kubelet/kubectl,最后部署的组件)
sudo kubeadm upgrade apply v1.30.2
sudo apt-get install -y kubelet=1.30.2-* kubectl=1.30.2-*
sudo systemctl restart kubelet
规则:不要跨大版本升级(如 1.28 → 1.30),必须 1.28 → 1.29 → 1.30 逐级升;升级前务必先做 etcd 快照。
总结
| 场景 | 推荐方案 | 理由 |
|---|---|---|
| 单机多容器应用 | Docker Compose | 轻量、声明式、学习成本低 |
| 需要自愈/扩缩容/滚动更新 | Kubernetes | 生产级调度与声明式管理 |
| 长期单节点生产 | kubeadm 单节点 + 去 taint | 与多节点集群同构,可平滑扩容 |
| 数据与备份 | 命名卷 + etcd 快照 | 容器可销毁,数据不可丢 |
容器编排的进阶之路,核心不是会敲多少命令,而是理解声明式状态、健康检查、数据生命周期这三个概念。生产环境里每一次「玄学故障」,追根溯源几乎都能落到这三个维度上。
下一期我们将进入第 26 天(重访):高可用与负载均衡——Keepalived + HAProxy 集群方案的生产级实践,看看如何让单点变成高可用、让流量分配更加智能,敬请期待!
系列目录
| 天数 | 主题 | 状态 |
|---|---|---|
| 第 1 天 | Ubuntu 简介与版本选择 | ✅ |
| 第 2 天 | 手把手安装 Ubuntu | ✅ |
| 第 3 天 | Ubuntu 桌面环境初探 | ✅ |
| 第 4 天 | Ubuntu 终端基础 | ✅ |
| 第 5 天 | 用户与权限管理 | ✅ |
| 第 6 天 | 软件包管理 | ✅ |
| 第 7 天 | 文件与文本操作 | ✅ |
| 第 8 天 | 系统服务管理 | ✅ |
| 第 9 天 | 磁盘与文件系统管理 | ✅ |
| 第 10 天 | 网络配置与管理 | ✅ |
| 第 11 天 | 进程管理与监控 | ✅ |
| 第 12 天 | 计划任务与自动化 | ✅ |
| 第 13 天 | 备份与恢复策略 | ✅ |
| 第 14 天 | 系统更新与升级管理 | ✅ |
| 第 15 天 | SSH 远程管理与安全加固 | ✅ |
| 第 16 天 | Web 服务器搭建 | ✅ |
| 第 17 天 | 数据库服务器部署 | ✅ |
| 第 18 天 | Docker 安装与容器化管理 | ✅ |
| 第 19 天 | 文件共享服务 | ✅ |
| 第 20 天 | 监控与告警系统 | ✅ |
| 第 21 天 | 邮件服务器基础 | ✅ |
| 第 22 天 | 系统安全加固 | ✅ |
| 第 23 天 | 性能调优与内核参数 | ✅ |
| 第 24 天 | KVM 虚拟化高级实战(重访) | ✅ |
| 第 25 天 | 容器编排进阶(重访) | 🟢 今日 |
| 第 26 天 | 高可用与负载均衡 | ✅ |
| 第 27 天 | Ubuntu 自动部署 | ✅ |
| 第 28 天 | 故障排查实战 | ✅ |
| 第 29 天 | Ubuntu 社区与文档 | ✅ |
| 第 30 天 | 30 天回顾总结 | ✅ |















暂无评论内容