Kubernetes 入门实战:从 docker run 到声明式集群
Kubernetes 入门实战:从 docker run 到声明式集群
上一篇《Docker 容器化入门实战》结尾留了个钩子:什么时候轮到 K8s。这篇就来接住它——当你需要多机调度、故障自愈、滚动发布、弹性扩缩容时,单机 Docker 的天花板就到了。目标不是读完就能当运维,而是建立一张正确的概念地图,外加一条能在本地跑通的最小上手路径。
一、先回答:K8s 到底解决什么问题
Docker 单机有四个天花板:
· 多机:docker run 只管一台机器,机器挂了服务就没了
· 自愈:进程崩了 Docker 能重启容器,但整台机器宕机时,没人把容器搬到别的机器
· 发布:升级靠手动 stop、rm、run,停机窗口和回滚全靠手速
· 扩缩容:流量来了手动多开几个容器,流量走了再手动关掉
K8s(Kubernetes)的答案是一个控制循环:你声明"我要 3 个副本跑 myapp:1.0",它永远把集群现状对齐到这个期望——容器挂了自动重建,机器挂了把负载搬到健康节点,升级时逐个替换实例,出问题一键回滚。
命令式是"帮我做这件事",声明式是"让世界保持这个样子"。K8s 只接受后者——这是理解它所有设计的钥匙。
二、概念地图:从 Docker 语言翻译过来
|
你熟悉的 |
K8s 对应 |
一句话区别 |
|
docker run |
Pod |
K8s 最小调度单位,通常一个 Pod 一个业务容器 |
|
--restart=always |
Deployment |
不是"崩了重启",而是"永远对齐期望副本数" |
|
docker network |
Service |
稳定虚拟 IP + 负载均衡,后面的 Pod 换 IP 无感 |
|
-e 环境变量 |
ConfigMap / Secret |
配置与镜像分离;注意 Secret 只是 base64,不是加密 |
|
手动 build & run |
kubectl apply |
把 yaml 提交给集群,其余交给控制器 |
五个对象各管一段:
· Pod:一个或多个容器的组合,共享网络和存储。99% 的场景一个 Pod 就一个业务容器
· Deployment:Pod 的"包工头",管副本数、滚动更新、回滚
· Service:Pod 会死、会换 IP,Service 提供稳定访问入口,自动负载均衡到健康副本
· Ingress:集群的统一门卫,按域名和路径把外部流量路由到各 Service
· Namespace:资源分组隔离,dev、staging、prod 各占一个
三、最小上手路径:本地起一个真集群
本地集群工具二选一:minikube,或更轻更快的 kind(用 Docker 跑容器来模拟节点)。90 秒得到一个单节点集群:
kind create cluster --name demo # 创建本地集群
kubectl get nodes # 确认节点 Ready
kubectl 是你和集群说话的唯一方式,先记六条:
kubectl apply -f myapp.yaml # 提交声明(最重要的一条)
kubectl get pods -w # 实时盯 Pod 状态
kubectl logs -f deploy/myapp # 看日志
kubectl describe pod <名字> # 看事件与失败原因(排障第一入口)
kubectl rollout status deploy/myapp # 看发布进度
kubectl rollout undo deploy/myapp # 一键回滚
四、把 Docker 篇的 myapp 部署上去
一个最小但完整的 Deployment,注意资源限额和健康探针已经内嵌:
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
spec:
replicas: 3
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
spec:
containers:
- name: myapp
image: myapp:1.0
ports:
- containerPort: 3000
resources:
requests: { cpu: "100m", memory: "128Mi" }
limits: { cpu: "500m", memory: "256Mi" }
livenessProbe:
httpGet: { path: /healthz, port: 3000 }
initialDelaySeconds: 10
再配一个 Service 把流量导进来:
apiVersion: v1
kind: Service
metadata:
name: myapp
spec:
type: NodePort
selector:
app: myapp
ports:
- port: 80
targetPort: 3000
新手第一次 apply 失败的头号原因:Deployment 的 selector.matchLabels 必须和 template.metadata.labels 完全一致——控制器靠这组标签认领自己管的 Pod,对不上就直接拒绝。
五、生产保命件:资源限额与健康探针
· requests / limits:requests 是调度依据(节点按它装箱),limits 是运行上限。两个都不写的 Pod 属于 BestEffort,节点资源紧张时第一个被杀
· livenessProbe:检查失败就重启容器——死锁、假死的进程靠它救活
· readinessProbe:没就绪就不接流量——发布时慢启动的应用靠它避免一波 5xx
· startupProbe:给慢启动应用一个宽限期,探测通过前不跑 liveness
发布与回滚的完整闭环:
kubectl set image deploy/myapp myapp=myapp:1.1
kubectl rollout status deploy/myapp # 盯发布进度
kubectl rollout undo deploy/myapp # 1.1 有问题,30 秒回到 1.0
六、六个新手坑
· 镜像用 latest 标签:发布不可追溯,"回滚"可能拉回同一个镜像。永远用明确的版本号
· 不写 requests/limits:一个失控容器吃光节点内存,殃及同节点所有邻居
· 只配 liveness 不配 readiness:应用还在预热,流量已经打进去,全是 5xx
· 把 Secret 当加密:它只是 base64 编码,本质是明文——真正的秘密交给外部密管系统或开启 etcd 加密
· 直接 `kubectl edit` 改线上:改完即漂移,下次 apply 又被 git 里的声明覆盖。一切以仓库里的 yaml 为准——这就是 GitOps 的起点
· 上来就把数据库搬进 K8s:有状态应用的存储和故障恢复复杂度高一个量级。先用云数据库,让无状态服务先进 K8s
七、学习路线:三步走,别跳
· 第 1 步 · 本地:用 kind 或 minikube 把上面的 myapp 跑通,练熟 apply、logs、describe、rollout 四板斧
· 第 2 步 · 单节点试验:找一台 2C4G 云主机装 k3s,体验真实的 NodePort 访问和 Ingress 路由
· 第 3 步 · 托管集群:云厂商托管 K8s(EKS、AKS、TKE 等)——控制面人家管,你只管工作节点和 yaml
K8s 的学习曲线在"概念多"而不是"难"——把 Pod、Deployment、Service 三件套的声明式模型想通,后面 80% 的对象都是同一套模式的重复。
速查卡
|
场景 |
命令 |
|
起本地集群 |
kind create cluster |
|
提交 / 更新 |
kubectl apply -f xxx.yaml |
|
盯副本 |
kubectl get pods -w |
|
看日志 |
kubectl logs -f deploy/xxx |
|
排障 |
kubectl describe pod xxx |
|
发布进度 |
kubectl rollout status deploy/xxx |
|
回滚 |
kubectl rollout undo deploy/xxx |
写在最后
从 docker run 到 kubectl apply,改变的不是一个命令,而是你和基础设施的关系:从"我来操作"变成"我来声明"。先在本地 kind 里把 myapp 跑起来——等你真正被半夜爬起来重启服务折磨过,就会感谢那个写了 livenessProbe 的自己。
- 点赞
- 收藏
- 关注作者
评论(0)