运维知识
悠悠
2026年7月9日

K8S入门真不难,把这篇文章看完就够了

不少兄弟问我K8S怎么学,这事儿说来话长。

之前我们公司搞大促,一个传统部署的Java服务突然挂了。运维同事手忙脚乱登上服务器,进程还在,但就是接不进请求。最后排查下来是机器负载太高了,想扩容?行,去申请机器、走审批流程、等IDC上架,一套下来活动都结束了。

后来上了K8S,同样的场景再发生怎么办?HPA自动扩容,五分钟不到新Pod就起来了,流量切过去,稳稳当当。从那之后我才真正意识到,K8S这东西不是锦上添花,是雪中送炭。

今天就把我这几年踩过的坑、总结的经验,毫无保留地分享给大家。

把K8S当成"包工头"来理解

很多新手一上来就去看官方文档,看到Pod、Deployment、Service这些名词直接就懵了。我跟你说,别那么正经地去学它,你就把它想象成一个"包工头"。

包工头管什么呢?管工人(Pod)、管工地(Node)、管图纸(Deployment)、管门牌号(Service)。这么一想,是不是就接地气多了?

具体到技术层面,K8S集群里有两种角色。控制节点(Master)相当于包工头的大脑,负责调度、决策、监控集群状态。一般包括API Server、etcd、Scheduler、Controller Manager这几个组件。etcd是存储集群所有数据的数据库,别小看它,集群挂了它都不能挂。工作节点(Node)是真正干活的工人,每个Node上跑着kubelet、kube-proxy、容器运行时(一般是containerd或者docker)。Pod就是在这个上面实际运行的。

我们生产环境用的是3个Master节点 + 10个Worker节点的配置,高可用方面至少Master得保证3个,不然一个挂了集群就出问题了。

环境搭建:先别上来就搭生产集群

新手最常犯的错误是什么?一上来就要搭生产级K8S集群。兄弟,你连Pod是啥都没搞清楚,搭什么生产集群?

我强烈建议,入门阶段就用 minikube 或者 kind 来练手。这两个工具能在你本机起一个单节点的K8S集群,特别适合学习。

minikube安装(Mac为例):

brew install minikube
minikube start --driver=docker
kubectl get nodes

看到节点状态变成Ready,恭喜你,你已经有了第一个K8S集群。

kubectl是K8S的命令行工具,记住它,后面要天天用。建议配置上自动补全:

source <(kubectl completion bash)
echo "source <(kubectl completion bash)" >> ~/.bashrc

zsh用户就把bash换成zsh,这能给你省下大量时间。

Pod:K8S里最小的调度单位

很多人以为Pod就是容器,其实不完全对。Pod可以理解成一个"盒子",里面可以装一个或多个紧耦合的容器。这些容器共享网络、存储,命运也连在一起——一个挂了,整个Pod都会被重启。

举个真实的例子:我们有个业务是数据采集,需要一个主程序加一个日志收集的sidecar。这种场景下,把它们放在同一个Pod里就很合适。Sidecar模式也是K8S里非常推荐的做法。

apiVersion: v1
kind: Pod
metadata:
  name: my-app-pod
spec:
  containers:
  - name: app
    image: nginx:1.21
    ports:
    - containerPort: 80
  - name: log-collector
    image: log-collector:v1
    volumeMounts:
    - name: log-volume
      mountPath: /var/log/app
  volumes:
  - name: log-volume
    emptyDir: {}

Deployment:管Pod的"包工头"

光有Pod不够,因为Pod挂了就没了,不能自动恢复。Deployment就是来解决这个问题的——它负责保证你指定数量的Pod始终在运行。

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

这个Deployment会保证集群里始终有3个nginx Pod在跑。Pod挂了?自动拉起新的。机器挂了?把Pod调度到其他机器上。

我们生产上一个核心服务配置的是replicas=5,然后配合HPA,根据CPU使用率自动扩缩容,平时5个够用,大促的时候能自动扩到20多个,挺省心的。

Service:给Pod一个稳定的"门牌号"

Pod的IP是不固定的,今天是10.244.1.5,明天可能就变成10.244.1.8了。那别的服务怎么找到它?Service就是来解决这个问题的。

最常用的Service类型是ClusterIP,只能在集群内部访问。如果要从外部访问,用NodePort或者LoadBalancer。云上一般用LoadBalancer,会自动创建SLB。

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

这个Service会监听所有标签是app=web的Pod,然后把请求转发过去。Service的IP是固定的,Pod再怎么变,Service IP都不变。

Namespace:给集群"分房间"

当集群里跑了成百上千个服务时,全部堆在一起肯定乱。Namespace就是用来隔离资源的,相当于给集群分了不同的房间。

我们生产上是这么分的:按环境分(dev、test、staging、prod),按业务线分(user、order、pay)。这样资源配额也能分开管理,避免某个环境的测试Pod把生产资源吃满。

kubectl create namespace production
kubectl get ns

生产环境实战:一个完整的部署流程

光说不练假把式。下面我带大家走一遍生产上部署一个服务的完整流程,包含我们踩过的坑。

Dockerfile编写:

FROM openjdk:11-jre-slim
WORKDIR /app
COPY target/myapp.jar /app/
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "myapp.jar"]

这里有个坑:基础镜像别用latest标签,版本要固定死。我们之前有个服务用openjdk:latest,结果某天构建的时候拉了最新版本,JDK 8和JDK 11不兼容,服务起不来了。血的教训。

K8S部署文件:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp
  namespace: production
  labels:
    app: myapp
spec:
  replicas: 3
  selector:
    matchLabels:
      app: myapp
  template:
    metadata:
      labels:
        app: myapp
    spec:
      containers:
      - name: myapp
        image: registry.company.com/myapp:v1.0.0
        ports:
        - containerPort: 8080
        resources:
          requests:
            memory: "512Mi"
            cpu: "500m"
          limits:
            memory: "1Gi"
            cpu: "1000m"
        livenessProbe:
          httpGet:
            path: /health
            port: 8080
          initialDelaySeconds: 30
          periodSeconds: 10
        readinessProbe:
          httpGet:
            path: /ready
            port: 8080
          initialDelaySeconds: 5
          periodSeconds: 5
        env:
        - name: DB_HOST
          valueFrom:
            configMapKeyRef:
              name: app-config
              key: db.host

重点说一下几个配置:

resources这块,requests是K8S调度时给Pod分配的最低资源,limits是Pod能用的最大资源。不设limits的话,Pod可能把整个节点资源吃满,影响其他Pod。我们之前就遇到过这种情况,一个有内存泄漏的服务把整个节点搞挂了。

健康检查也很关键,livenessProbe判断容器是不是活着,不健康就重启。readinessProbe判断容器能不能接流量,不健康就不接流量。这两个一定都要配,不然滚动更新的时候新版本可能还没起来流量就切过去了,导致服务短暂不可用。

环境变量这块,配置和镜像分离,不要把配置写死在代码里。生产上我们用ConfigMap管理配置,用Secret管理敏感信息。

应用配置并验证:

kubectl apply -f deployment.yaml
kubectl get pods -n production
kubectl describe pod pod-name -n production
kubectl logs -f pod-name -n production
kubectl exec -it pod-name -n production -- /bin/bash

如果Pod一直起不来,describe命令是你的救命稻草,它会把Pod的详细信息都列出来,包括事件、状态、配置等等。

那次差点让我丢工作的故障

讲个真实案例,希望大家能引以为戒。

去年有个周五下午,我们上线一个新版本。开发那边说就改了个小功能,让运维直接发。流程是这样的:先灰度10%流量,观察10分钟,没问题再全量。

结果灰度上去之后,监控系统告警了,错误率飙升到15%。我第一反应是版本有问题,赶紧回滚。回滚之后错误率下来了,但是!有用户反馈数据不一致。

我慌了,赶紧看日志、看监控、看链路追踪。最后发现,回滚的时候,K8S是先创建新Pod再删除旧Pod的(滚动更新策略)。那10%灰度的Pod虽然被回滚了,但已经处理了一部分用户请求,这些请求触发的数据变更在新版本里有特殊处理逻辑(他们改的"小功能"),回滚之后这部分数据就出问题了。

教训:

任何发版都要有回滚预案。这次幸亏灰度比例小,要是全量发布,后果不堪设想。灰度发布要多观察一会儿。我们只观察了10分钟就全量了,有些问题在长时间运行后才会暴露。开发说"小改动"也要走完整测试流程。别问,问就是被坑过。

后来我们调整了发布流程:灰度发布最低观察30分钟,配置回滚时自动保留新Pod数据快照,重要业务必须蓝绿发布。流程虽然复杂了,但再没出过类似的事故。

入门到进阶,你还需要学这些

K8S入门之后,往哪些方向深入?我列几个我们生产上用得最多的:

Helm包管理工具必须学。手写YAML文件管理少量服务还行,服务一多就乱套了。Helm相当于K8S的"软件包管理器",能让你像apt、yum一样管理K8S应用。我们生产上所有服务都用Helm管理,模板统一,配置抽离,省了大量重复劳动。

ConfigMap和Secret是基本功。配置和代码分离是基本操作。ConfigMap存普通配置,Secret存敏感信息(密码、token等)。注意Secret默认是base64编码的,不是加密的,敏感信息一定要开启加密存储。

持久化存储(PV/PVC)也得懂。Pod里的数据是临时的,Pod一删数据就没了。有状态服务(数据库、文件存储)需要持久化存储。我们用的是云厂商提供的块存储CSI插件,PVC声明存储需求,K8S自动创建PV。

自动扩缩容(HPA/VPA)这个对业务很有用。HPA(Horizontal Pod Autoscaler)根据CPU、内存或者自定义指标自动调整Pod数量。VPA(Vertical Pod Autoscaler)自动调整Pod的资源配置。这两个配合用,效果更佳。

监控和日志(Prometheus + Grafana + ELK)是生产必备。生产环境没有监控就是裸奔。Prometheus采集指标,Grafana做可视化,ELK(或者Loki)做日志聚合。我们自建了完整的监控告警体系,每个服务上线必须接入监控,否则不允许上线。

服务网格(Istio)等有基础了再看。微服务多了之后,服务间调用关系复杂,流量管理、熔断限流、链路追踪这些需求自然就出来了。Istio就是解决这些问题的。不过Istio复杂度比较高,建议有一定K8S基础之后再学习。

给新手的几条掏心窝子的话

别光看文档,一定要动手。K8S是个实操性很强的东西,光看文档是学不会的。自己搭个环境,从部署一个Nginx开始,慢慢深入,遇到问题就去查、去解决,这个过程比看十遍文档都管用。

先会用,再懂原理。很多人陷入了一个误区:非要搞懂K8S每个组件的原理才开始用。兄弟,先把集群搭起来,把服务跑起来,再回头看原理,事半功倍。我们公司新来的运维,都是先培训基本操作,再讲原理。

生产环境慎之又慎。学习环境随便折腾,删库也没事。生产环境每一个操作都要三思而后行。我们生产操作都有严格的审批流程,重要操作要双人复核。

多看官方文档,多逛社区。K8S官方文档(kubernetes.io)是最权威的学习资料,遇到问题先查文档。社区推荐CNCF、Stack Overflow、GitHub Issues。

加入一个学习社群。一个人学容易放弃,找几个志同道合的兄弟一起学,互相分享经验,能走得更远。


好了,今天的分享就到这里。这篇文章涵盖了我从生产事故中总结出来的K8S核心概念、环境搭建、部署流程、真实案例以及后续学习方向。

K8S这个东西,会了不难,难了不会。关键是要迈出第一步,先把环境搭起来,把第一个Pod跑起来。跑起来了,你就有信心继续深入了。

如果觉得这篇文章对你有帮助,欢迎点赞、在看、转发三连支持一波。你的支持是我持续创作的最大动力!

后续会持续分享更多运维干货、生产实战经验、各种踩坑指南。咱们一起在运维这条路上走得更远、更稳。

公众号:耕云躬行录

个人博客:躬行笔记

文章目录

博主介绍

热爱技术的云计算运维工程师,Python全栈工程师,分享开发经验与生活感悟。
欢迎关注我的微信公众号@运维躬行录,领取海量学习资料

微信二维码