0 Comments

Kubernetes 入门:从 Pod 到 Deployment

很多人第一次接触 Kubernetes(以下简称 K8s)时,都会被它那张密密麻麻的架构图劝退:API Server、etcd、Controller Manager、Scheduler、kubelet、kube-proxy……名词一个接一个,还没动手就先懵了。

实际上,日常写应用、跑服务的开发者,真正需要打交道的对象就那么几个:Pod、Service、Deployment、ConfigMap、Secret、Ingress。把这些吃透,剩下的细节用的时候再查文档也来得及。

这篇文章不讲架构图,从最核心的 Pod 说起,一步步讲到 Deployment,用最小的 YAML 让你跑起第一个真正”能自愈”的服务。

一、Pod:最小的调度单位

Pod 是 K8s 里最小的、可以创建和调度的单元。一个 Pod 里可以有一个或多个容器,这些容器共享网络命名空间和存储卷。

关键点在于:你几乎从不直接创建 Pod。生产环境里 Pod 通常是被 Deployment、StatefulSet 或 DaemonSet 这类控制器管起来的。但理解 Pod 是理解一切的前提,所以先看一个最小的 Pod 定义:

apiVersion: v1
kind: Pod
metadata:
  name: hello-pod
  labels:
    app: hello
spec:
  containers:
    - name: hello
      image: nginx:1.25
      ports:
        - containerPort: 80

几个容易忽略的字段说明一下:

  • labels 是给 Pod 打的标签。K8s 的很多机制(Service 选择后端、Deployment 管理副本)都靠标签来”认领”对象,标签设计得好不好,直接决定后续维护是否顺畅。
  • containerPort 只起声明作用,不真正暴露端口。要让外部能访问,得靠 Service 或者直接 kubectl port-forward

kubectl apply -f pod.yaml 就能创建它。不过要是这个 Pod 挂了,它不会自动重启,因为没人”负责”它。这正是控制器登场的原因。

二、Deployment:让副本可以自愈

直接管理 Pod 有几个明显的坑:删了不会重建、更新要手动换镜像、副本多了少了要自己调。Deployment 解决的就是这些问题——你声明”我想要 3 个副本跑这个镜像”,剩下的交给它。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: hello-deploy
spec:
  replicas: 3
  selector:
    matchLabels:
      app: hello
  template:
    metadata:
      labels:
        app: hello
    spec:
      containers:
        - name: hello
          image: nginx:1.25
          ports:
            - containerPort: 80

这段 YAML 里有两个地方要特别留意,也是新手最容易翻车的:

  1. selector.matchLabelstemplate.metadata.labels 必须对得上。 Deployment 通过 selector 找到自己管理的 Pod,而 Pod 又是从 template 生成出来的。如果两边标签不一致,apply 的时候 apiserver 会直接报错拒绝。

  2. template 里的内容本质上就是一个 Pod 的定义。 你把第一个例子里 Pod 的 spec 原样搬进来即可,区别只是少了 apiVersionkindname 这几行元信息。

Deployment 底下其实是 ReplicaSet(简称 RS)在干活。每做一次滚动更新,Deployment 会新建一个 RS,老的 RS 逐步缩到零。所以你会看到 kubectl get rs 里有好几个名字带哈希后缀的对象,这是正常的。

滚动更新与回滚

更新镜像只需要改 image 字段然后重新 apply:

kubectl set image deployment/hello-deploy hello=nginx:1.26
kubectl rollout status deployment/hello-deploy

默认策略是滚动更新:逐步用新 Pod 替换旧 Pod,保证过程中始终有副本在提供服务。如果新版有问题,可以快速回滚:

kubectl rollout undo deployment/hello-deploy

rollout undo 回退到上一个版本,靠的是 Deployment 保留的历史 ReplicaSet。这也解释了为什么更新频繁时 get rs 会冒出一堆旧对象。

三、资源限制与探针:两个不能省的东西

跑起来只是第一步,能不能稳定跑,取决于你有没有配资源限制和健康检查。

资源限制

不给 Pod 设资源,等于告诉集群”这个容器想吃多少吃多少”。一旦某个容器内存泄漏,可能把整个节点拖垮,连带同节点的其他服务一起遭殃。

spec:
  containers:
    - name: hello
      image: nginx:1.25
      resources:
        requests:
          cpu: "100m"
          memory: "128Mi"
        limits:
          cpu: "500m"
          memory: "256Mi"

requests 是调度器做决策的依据,保证节点上有足够资源才把 Pod 放上去;limits 是硬上限。内存超过 limit 会被 OOM 杀掉;CPU 超了不会被杀,只会被限流。这两个值一天不合适,节点的资源利用率就很难看。

就绪与存活探针

只靠”容器进程在不在”判断服务是否健康,是远远不够的。进程可能没退,但应用已经起不来了。这时候需要探针:

spec:
  containers:
    - name: hello
      image: nginx:1.25
      readinessProbe:
        httpGet:
          path: /
          port: 80
        initialDelaySeconds: 3
        periodSeconds: 5
      livenessProbe:
        httpGet:
          path: /healthz
          port: 80
        initialDelaySeconds: 10
        periodSeconds: 10

两个探针的分工不一样:

  • readinessProbe(就绪探针):判断容器能不能接流量。失败时 Service 会把流量从这个 Pod 摘掉,但不会重启它。
  • livenessProbe(存活探针):判断容器要不要重启。失败会触发容器重建,适合用来补救卡死的进程。

新手常见错误是把 liveness 探针指向一个依赖外部资源(比如数据库)的接口,一旦数据库抖动,探针集体失败触发重启风暴,反而把服务搞得更糟。记住一个原则:liveness 只测”进程自己是不是废了”,readiness 才测”现在能不能接活”。

四、让外部访问:Service 兜底

Pod 的 IP 是会变的——Pod 一重建,IP 就换。所以不能用 IP 直连,得用 Service 提供一个稳定的入口,并负责负载均衡:

apiVersion: v1
kind: Service
metadata:
  name: hello-svc
spec:
  selector:
    app: hello
  ports:
    - port: 80
      targetPort: 80
  type: ClusterIP

selector 会选中所有 app: hello 的 Pod,作为后端。Service 拿到的是一个固定的 ClusterIP,集群内部通过这个名字就能访问到健康的副本,Pod 再怎么重建也不受影响。

默认的 ClusterIP 只在集群内部可达。要做到从外部访问,有几种常见做法:

  • NodePort:在每个节点上开一个固定端口,把流量转给 Service。简单,但端口范围有限,生产环境不太推荐直接暴露。
  • LoadBalancer:依赖云厂商分配一个外部负载均衡器,云上部署最常用。
  • Ingress:做七层路由,按域名、路径把请求分发给不同的 Service,一个入口管多个服务。

对于自建集群或者本机调试,最快的方式其实是 kubectl port-forward,把 Service 端口映射到本地:

kubectl port-forward service/hello-svc 8080:80

浏览器打开 localhost:8080 就能看到 Nginx 页面了。

五、小结

把这套串起来,一个”像样”的服务应该具备这几个要素:

  1. Deployment 声明副本数和滚动更新策略,让它自愈、可回滚;
  2. 在 template 里配好 resources 的 requests/limits,别让单个容器拖垮节点;
  3. readiness/liveness 探针 区分”能不能接流量”和”要不要重启”;
  4. Service 提供稳定入口,屏蔽 Pod IP 的漂移。

Pod → Deployment → Service,这三层走通,K8s 在你手里就不再是一堆抽象名词,而是一个能真正扛住线上流量的运行平台。下一步再去啃 ConfigMap、Secret 和 Ingress,把配置和外部流量也理顺,思路会顺畅很多。

刚开始接触,别急着背架构图和那两百多个 API 对象。先跑起来,遇到问题再查,比一上来啃全书有效得多。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注