k8s 是干什么的?容器编排解决的问题与 Docker 的区别
k8s 就是 Kubernetes,一个容器编排平台。你告诉它“这个网站跑 3 个副本、用这个镜像、对外提供 80 端口”,它负责把容器安排到合适的服务器上,挂了自动拉起,按负载增减副本,发新版时逐个替换,并给这组容器一个固定的访问地址。Docker 管的是一台机器上的容器,k8s 管的是一群机器上的容器。
k8s 是干什么的?一句话:在多台服务器上自动部署、运行和管理容器。你用一份 YAML 文件描述想要的状态,比如“网站要 3 个副本、用 1.4.2 版本的镜像、每个副本要半核 CPU 和 256 MB 内存”,k8s 负责把它变成现实,并一直维持下去:容器挂了自动重启,服务器宕机就在别的服务器上重建,流量上来了加副本,发新版时一个一个替换。k8s 是 Kubernetes 的缩写,K 和 s 之间正好 8 个字母,于是写成 k8s。这个词来自希腊语,意思是舵手。它由 Google 在 2014 年开源,吸收了 Google 内部集群管理系统 Borg 的经验,2015 年发布 1.0 并捐给了新成立的云原生计算基金会(CNCF),现在是主流的容器编排平台。
从部署一个网站说起:没有 k8s 时要做什么
假设你要运维一个电商网站:前面是 nginx,中间是 3 个应用实例,后面是 Redis 和 MySQL,一共 3 台服务器。不用任何编排工具,日常工作是这样的:
- 部署:登录每台服务器装运行环境、上传代码、改配置、启动进程。三台机器的系统补丁、依赖版本稍有不同,就会出现“这台好的、那台报错”。
- 故障:凌晨应用进程崩溃,靠监控告警叫醒值班人员去重启;一台服务器硬件故障,上面的实例要手工挪到另外两台上,还要改 nginx 的 upstream 列表。
- 扩容:大促前加两台机器,重复一遍部署步骤,再把新实例加进负载均衡;活动结束后再手工撤下来。
- 发版:逐台停服务、换包、启动、验证;第二台出问题,再逐台回滚。
- 互相访问:应用连接 Redis 和 MySQL 的地址写在配置文件里,换一台机器就要把配置改一圈。
Docker 解决的是第 1 件事:把应用和依赖打成镜像,在任何装了 Docker 的机器上运行结果都一样。但后面 4 件事跨越了多台机器,Docker 本身管不了,这正是 k8s 要做的。
k8s 解决的五个问题
k8s 的工作方式可以概括为“声明期望状态,由控制器持续纠偏”:你只说要什么,不说怎么做;集群里的各个控制器不停地对比实际状态和期望状态,发现不一致就动手修正。上面的五件事对应到 k8s 里是这样的:
| 问题 | k8s 的做法 | 相关的对象或命令 |
|---|---|---|
| 调度:容器放在哪台机器上 | 你声明每个容器需要的 CPU 和内存(requests),调度器在集群里挑资源够用的节点放置 | Pod、resources、调度器 |
| 自愈:挂了怎么办 | 容器进程退出会被重启;健康检查失败的容器也会被重启;节点失联后,由 Deployment 管理的 Pod 默认约 5 分钟后在其他节点重建 | 重启策略、存活探针、Deployment |
| 扩缩容 | 改副本数即可;配合 HPA(水平自动扩缩)可以按 CPU 使用率等指标自动增减,前提是集群里装了 metrics-server | kubectl scale、HorizontalPodAutoscaler |
| 滚动更新与回滚 | 换镜像版本后分批替换,默认多出或缺少的 Pod 都不超过副本数的 25%,新 Pod 通过就绪检查后才继续替换下一批;出问题一条命令退回上一版 | kubectl set image、kubectl rollout undo |
| 服务发现与负载均衡 | Service 给一组 Pod 一个固定的虚拟 IP 和 DNS 名称,请求在这组 Pod 之间分发,Pod 重建换了 IP 也不用改配置 | Service、集群 DNS |
另外,配置和密码可以从镜像里拆出来,放进 ConfigMap 和 Secret,同一个镜像在测试和生产环境挂不同的配置。
把上面那个网站的应用层写成 k8s 的清单,大致是这样(镜像地址是示例):
apiVersion: apps/v1
kind: Deployment
metadata:
name: shop-web
spec:
replicas: 3
selector:
matchLabels:
app: shop-web
template:
metadata:
labels:
app: shop-web
spec:
containers:
- name: web
image: registry.example.internal/shop/web:1.4.2
ports:
- containerPort: 8080
resources:
requests:
cpu: 500m
memory: 256Mi
readinessProbe:
httpGet:
path: /healthz
port: 8080
---
apiVersion: v1
kind: Service
metadata:
name: shop-web
spec:
selector:
app: shop-web
ports:
- port: 80
targetPort: 8080
Deployment 描述“跑 3 个副本、用哪个镜像、怎么判断它准备好了”,Service 给这 3 个副本一个统一入口:集群里的其他程序访问 shop-web 这个名字(完整写法是 shop-web.default.svc.cluster.local)的 80 端口,请求会被分到某个就绪的副本上。日常操作用 kubectl:
kubectl apply -f shop-web.yaml # 创建或更新
kubectl get pods -l app=shop-web -o wide # 3 个 Pod 分别在哪个节点上
kubectl delete pod shop-web-7d9c8b6f5-x2k4q # 模拟故障:删掉一个,几秒后会出现新的补位
kubectl scale deployment shop-web --replicas=6 # 扩到 6 个副本
kubectl set image deployment/shop-web web=registry.example.internal/shop/web:1.4.3
kubectl rollout status deployment/shop-web # 观察滚动更新进度
kubectl rollout undo deployment/shop-web # 新版本有问题,退回上一版
delete pod 那一条最能说明 k8s 的思路:你没有告诉它“再起一个”,是 Deployment 控制器发现实际只有 2 个副本、少于期望的 3 个,自己补上的。
k8s 和 Docker、Docker Compose 的区别
三者经常被放在一起比较,但它们不在同一层:
- Docker 做两件事:用
docker build把应用打成镜像,用docker run在一台机器上运行容器。 - Docker Compose 用一个 compose.yaml 描述多个容器组成的应用(web、redis、db),
docker compose up -d一条命令在一台机器上全部拉起,容器之间按服务名互相访问。 - k8s 不负责构建镜像,它负责在多台机器上运行和管理容器。镜像照样用 Docker 构建、推到镜像仓库,k8s 的节点再从仓库拉取。节点上实际启动容器的是 containerd、CRI-O 这类容器运行时;k8s 从 1.24 起不再直接对接 Docker Engine,但 Docker 构建的镜像符合 OCI 标准,在 k8s 里照常运行。
所以“k8s 和 Docker 的区别”的答案是:它们是上下游关系,不是二选一。Docker 也有自己的集群编排 Docker Swarm,概念少、上手快,但周边生态和社区规模不如 k8s。
按网站那个例子,把四种方式放在一起对比:
| 对比项 | 手工运维 | Docker | Docker Compose | k8s |
|---|---|---|---|---|
| 交付的东西 | 代码包加部署文档 | 镜像 | 镜像加 compose.yaml | 镜像加 YAML 清单 |
| 环境一致性 | 靠人和文档 | 镜像保证 | 镜像保证 | 镜像保证 |
| 管理范围 | 每台单独操作 | 一台机器上的单个容器 | 一台机器上的一组容器 | 整个集群 |
| 进程崩溃 | 告警后人工重启 | 设置重启策略后自动重启 | 同 Docker | 自动重启,健康检查失败也会重启 |
| 服务器宕机 | 人工迁移 | 无法处理 | 无法处理 | 在其他节点重建 |
| 扩容 | 加机器、重复部署 | 手工多起几个容器 | --scale 在单机上加副本 |
改副本数,或按指标自动扩缩 |
| 发版与回滚 | 逐台操作 | 手工停旧起新 | 重建容器,期间会短暂中断 | 滚动更新,一条命令回滚 |
| 服务互相访问 | 配置里写死 IP | 端口映射加手工配置 | 同一项目内按服务名访问 | Service 名称和集群 DNS |
| 学习与维护成本 | 低 | 低 | 低 | 高:控制平面、网络、存储、版本升级都要有人管 |
什么规模的业务值得上 k8s
k8s 的收益来自“机器多、服务多、变化多”,成本则是一整套需要专人维护的系统。可以对照下面几条判断:
| 判断依据 | 倾向于上 k8s | 倾向于先不上 |
|---|---|---|
| 服务数量 | 多个独立部署的服务,或者多个项目想共用一套基础设施 | 一个应用加一个数据库 |
| 服务器数量 | 3 台以上,并且还在增加 | 一两台 |
| 发布频率 | 每周多次甚至每天发版 | 一个月一两次 |
| 流量 | 峰谷明显,需要临时扩容 | 平稳 |
| 团队 | 有人能负责集群升级、网络和存储排障 | 没有专人,出了问题没人能修 |
| 应用形态 | 能容器化的无状态 Web、API、后台任务 | 强依赖本机状态的老系统、Windows 桌面类软件 |
左列占了多数,k8s 带来的自动化值得它的维护成本;只有一两台服务器、一个月发一次版,用 Docker Compose 加 systemd 管理就足够,出问题时排查路径也短得多。
维护成本要提前算清楚:k8s 每年发布三个左右的小版本,每个小版本的补丁支持约 14 个月,升级又只能逐个小版本进行,所以一个集群至少每年要升级一次;控制平面的证书、etcd 的备份、网络插件和存储插件的版本,也都要有人跟。没有人力承担这些,集群跑上一两年就会变成不敢动的系统。
机房里几台服务器怎么起步
用自有机房的物理机起步,最常见的形态是 3 台服务器组成一个小集群,每台既是控制平面又跑业务。选 3 台的原因是 etcd(k8s 存放全部集群状态的数据库)靠多数派工作:3 个节点能容忍坏 1 台,2 个节点坏 1 台就失去多数,和 1 台没有区别。数据库先留在集群外的独立服务器上。
搭建工具按需要选:
| 方式 | 特点 | 适合 |
|---|---|---|
| kubeadm | 官方工具,组件标准;网络插件、对外入口、存储都要自己选择和安装 | 想按标准方式搭建,后续规模会变大 |
| k3s | 单个二进制,默认自带 flannel 网络、Traefik 入口、ServiceLB 负载均衡和本地存储,高可用模式内置 etcd | 小集群,想先跑起来再说 |
| 云厂商托管 | 控制平面由厂商维护 | 业务在云上而不在自有机房 |
以 k3s 为例,3 台服务器内网地址为 10.10.30.11 至 10.10.30.13,以 root 执行:
# 第一台:生成共享密钥,以内置 etcd 初始化集群
TOKEN=$(openssl rand -hex 16); echo "$TOKEN"
curl -sfL https://get.k3s.io | K3S_TOKEN=$TOKEN sh -s - server --cluster-init
# 第二、三台:用第一台输出的密钥加入,同样作为 server 参与 etcd
curl -sfL https://get.k3s.io | K3S_TOKEN=第一台输出的密钥 sh -s - server --server https://10.10.30.11:6443
# 任意一台上确认三个节点都是 Ready
k3s kubectl get nodes -o wide
国内网络访问安装脚本和 GitHub 较慢时,按 k3s 官方文档里的说明改用国内镜像,或者用离线(air-gap)方式安装,镜像地址以文档当前写法为准。集群起来只是第一步,物理机上没有云平台代劳的那些东西,要自己补齐:
| 要补的 | 为什么 | 常见做法 |
|---|---|---|
| 对外入口 | 没有云负载均衡,LoadBalancer 类型的 Service 拿不到地址 | k3s 自带 ServiceLB;kubeadm 集群常用 MetalLB,从机房分配的一段地址中给 Service 分配 IP |
| 持久化存储 | Pod 换了节点还要能读到原来的数据 | 起步用本地存储,数据跟着节点走;要跨节点就用 NFS、Ceph 或 Longhorn |
| 镜像仓库 | 节点从仓库拉镜像,公网仓库在国内可能拉不下来 | 自建 Harbor 等私有仓库 |
| 备份 | 全部集群状态都在 etcd 里 | k3s 内置 etcd 时默认定时生成快照,要把快照再拷到集群之外 |
| 监控与日志 | 容器随时会被重建,登录节点翻日志不现实 | Prometheus 采集指标,日志集中收集 |
| 网段 | Pod、Service 网段不能和机房已有的网段重叠 | k3s 默认 Pod 网段 10.42.0.0/16、Service 网段 10.43.0.0/16,冲突时在安装时修改 |
容量也要按“坏一台”来算:3 个节点要能在其中 1 台故障时把所有 Pod 挪到另外 2 台上,所以平时所有 Pod 的 requests 加起来,不宜超过集群总容量的三分之二左右。
在机房里怎么落地
k8s 的思路是把节点当成可以随时替换的资源:节点出了问题,与其登录上去慢慢修,不如把它移出集群、重装系统、再加回来。这要求节点之间的硬件和系统尽量一致,重装也要足够快。Toplink DCIM 的 IPMI 远程管理可以把一批服务器设为从 PXE 启动、由任务队列批量重装,坏掉的节点远程重装后重新加入集群即可;机柜与资产会向服务器下发采集脚本,记录每台机器的 CPU、内存、硬盘配置并保留历史,节点的配件被更换或者配置对不上,在台账里能直接看出来,不会等到调度出问题才发现。
常见问题
学 k8s 之前要先学 Docker 吗?
建议先学。k8s 里运行的就是容器镜像,镜像怎么构建、容器怎么启动、端口和数据卷怎么映射,这些在 Docker 上更容易理解。会用 Docker 和 Docker Compose 跑起一个多容器应用,再学 k8s 的 Pod、Deployment、Service 会顺很多。
数据库适合放进 k8s 吗?
能放,但不建议起步就放。数据库依赖稳定的存储和网络,迁移、备份、故障切换都比无状态服务复杂。常见做法是先把 Web、API 这类无状态服务搬进 k8s,数据库继续跑在独立的服务器上,等团队熟悉了持久化存储和相关的运维工具后再评估。
k8s 能管理虚拟机吗?
原生的 k8s 只管容器。社区有 KubeVirt 这类扩展项目,可以在 k8s 上运行和管理虚拟机,但这属于额外的组件。只是想把物理机切成虚拟机,用 ESXi、Proxmox VE 等虚拟化平台更直接。