不少兄弟问我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为例):
brewinstallminikube minikube start--driver=docker kubectl get nodes看到节点状态变成Ready,恭喜你,你已经有了第一个K8S集群。
kubectl是K8S的命令行工具,记住它,后面要天天用。建议配置上自动补全:
source<(kubectl completionbash)echo"source <(kubectl completion bash)">>~/.bashrczsh用户就把bash换成zsh,这能给你省下大量时间。
Pod:K8S里最小的调度单位
很多人以为Pod就是容器,其实不完全对。Pod可以理解成一个"盒子",里面可以装一个或多个紧耦合的容器。这些容器共享网络、存储,命运也连在一起——一个挂了,整个Pod都会被重启。
举个真实的例子:我们有个业务是数据采集,需要一个主程序加一个日志收集的sidecar。这种场景下,把它们放在同一个Pod里就很合适。Sidecar模式也是K8S里非常推荐的做法。
apiVersion:v1kind:Podmetadata:name:my-app-podspec:containers:-name:appimage:nginx:1.21ports:-containerPort:80-name:log-collectorimage:log-collector:v1volumeMounts:-name:log-volumemountPath:/var/log/appvolumes:-name:log-volumeemptyDir:{}Deployment:管Pod的"包工头"
光有Pod不够,因为Pod挂了就没了,不能自动恢复。Deployment就是来解决这个问题的——它负责保证你指定数量的Pod始终在运行。
apiVersion:apps/v1kind:Deploymentmetadata:name:web-deploymentspec:replicas:3selector:matchLabels:app:webtemplate:metadata:labels:app:webspec:containers:-name:nginximage:nginx:1.21ports:-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:v1kind:Servicemetadata:name:web-servicespec:type:ClusterIPselector:app:webports:-port:80targetPort: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/v1kind:Deploymentmetadata:name:myappnamespace:productionlabels:app:myappspec:replicas:3selector:matchLabels:app:myapptemplate:metadata:labels:app:myappspec:containers:-name:myappimage:registry.company.com/myapp:v1.0.0ports:-containerPort:8080resources:requests:memory:"512Mi"cpu:"500m"limits:memory:"1Gi"cpu:"1000m"livenessProbe:httpGet:path:/healthport:8080initialDelaySeconds:30periodSeconds:10readinessProbe:httpGet:path:/readyport:8080initialDelaySeconds:5periodSeconds:5env:-name:DB_HOSTvalueFrom:configMapKeyRef:name:app-configkey:db.host重点说一下几个配置:
resources这块,requests是K8S调度时给Pod分配的最低资源,limits是Pod能用的最大资源。不设limits的话,Pod可能把整个节点资源吃满,影响其他Pod。我们之前就遇到过这种情况,一个有内存泄漏的服务把整个节点搞挂了。
健康检查也很关键,livenessProbe判断容器是不是活着,不健康就重启。readinessProbe判断容器能不能接流量,不健康就不接流量。这两个一定都要配,不然滚动更新的时候新版本可能还没起来流量就切过去了,导致服务短暂不可用。
环境变量这块,配置和镜像分离,不要把配置写死在代码里。生产上我们用ConfigMap管理配置,用Secret管理敏感信息。
应用配置并验证:
kubectl apply-fdeployment.yaml kubectl get pods-nproduction kubectl describe pod pod-name-nproduction kubectl logs-fpod-name-nproduction kubectlexec-itpod-name-nproduction -- /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跑起来。跑起来了,你就有信心继续深入了。
如果觉得这篇文章对你有帮助,欢迎点赞、在看、转发三连支持一波。你的支持是我持续创作的最大动力!
后续会持续分享更多运维干货、生产实战经验、各种踩坑指南。咱们一起在运维这条路上走得更远、更稳。