ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

云原生与K8S入门指南:从核心概念到Minikube实战部署

云原生与K8S入门指南:从核心概念到Minikube实战部署

1. 从一个真实困惑说起:为什么大家都在聊云原生和K8S

如果你最近逛技术社区,或者参加一些架构师聚会,大概率会反复听到两个词:云原生和K8S。有人把它们说得神乎其神,好像不上云原生就是落后;也有人一脸茫然,觉得这不过是运维圈的新一轮造词运动。我刚开始接触这两个概念的时候,也是这种感受——文档看了一堆,名词记了一箩筐,但真到动手的时候,还是不知道从哪下手。

这篇文章就是写给和我当初一样困惑的人。不管你是刚入行的开发,还是做了几年传统部署想往云原生方向转的运维,或者是技术团队的负责人正在评估要不要引入K8S,我都希望用最直白的方式,把这两个东西讲清楚。云原生不是某一个具体技术,而是一套方法论和工具集的组合;K8S(Kubernetes)则是这套方法论落地时最核心的容器编排平台。理解了它们之间的关系,你就能明白为什么大厂都在往这个方向走,也能判断自己的项目到底适不适合跟这个风。

我会从概念拆解开始,然后讲清楚K8S到底解决了什么问题,再带你走一遍最小可用的实操流程,最后把我自己踩过的坑和常见问题的排查思路整理出来。整篇内容基于我实际做过的几个项目经验,涉及具体操作的地方我会给出可以直接参考的命令和配置,但不会涉及任何特定公司的内部信息。

2. 云原生到底是什么:拆开这个词看本质

2.1 云原生的核心定义与四个支柱

云原生(Cloud Native)这个词最早出现在2010年前后,但真正被广泛接受是在云原生计算基金会(CNCF)成立之后。它的核心思想其实一句话就能概括:让应用从设计之初就假设自己运行在云环境里,从而充分利用云平台的弹性和分布式优势。

这句话听起来有点抽象,我换个说法。传统的应用部署方式,是你先买几台服务器,装好系统,然后把应用放上去,出了问题就登录机器去查。这种方式假设服务器是“宠物”——每台都有名字,都要精心照料。而云原生假设服务器是“牛群”——数量很多,个体不重要,坏了就换一头,整体保持健康就行。

具体来说,云原生包含四个核心支柱:

  • 容器化:把应用和它的依赖打包成一个标准单元,这个单元在任何支持容器的环境里都能以相同方式运行。Docker是这方面最常用的工具。
  • 微服务:把一个大应用拆成多个小服务,每个服务独立开发、独立部署、独立扩展。这样改一个功能不需要重新部署整个系统。
  • 持续交付:通过自动化流水线,让代码从提交到上线的过程尽可能快且可靠。核心是减少人工干预,降低出错概率。
  • 动态编排:当你有几十上百个容器需要管理时,手动操作不现实。编排工具负责自动调度、扩缩容、故障恢复。K8S就是干这个的。

这四个支柱不是孤立的,它们互相配合。容器化是基础,微服务是架构风格,持续交付是流程保障,动态编排是运行时的管理手段。你不需要一次性全部做到,但方向对了,后面每一步都会更顺。

2.2 为什么传统部署方式越来越吃力

我做过一个项目,早期是典型的传统部署:三台物理服务器,一台跑Web应用,一台跑数据库,一台做备份。刚开始用户少,没什么问题。后来用户量涨上来,问题就一个接一个:

  • 大促前要扩容,得提前买服务器、装系统、配环境,流程走完至少一周,经常赶不上业务变化。
  • 某台机器硬盘坏了,服务直接挂掉,恢复要几个小时,因为没人记得当初装了什么版本的依赖。
  • 开发环境和生产环境不一致,本地测试通过的功能,上线后报错,排查半天发现是系统库版本不同。
  • 每次发版都要停机,用户投诉不断。

这些问题的根源在于:传统部署方式把应用和基础设施绑得太死。应用依赖特定的操作系统、特定的库版本、特定的网络配置,迁移和扩展的成本极高。云原生的思路就是解耦——把应用打包成容器,让它不依赖具体环境;把服务拆小,让变更影响范围可控;把运维自动化,让扩缩容和故障恢复不再依赖人工。

2.3 云原生和K8S的关系:不是等同,而是承载

很多人会把云原生和K8S混为一谈,其实它们的关系是理念和工具的关系。云原生是目标和方法论,K8S是实现这个目标过程中最关键的运行时平台。

你可以没有K8S而实践部分云原生理念,比如只用Docker做容器化,用GitLab CI做持续集成。但当你的容器数量超过一定规模,手动管理变得不现实时,就需要K8S这样的编排系统。反过来,如果你只是装了个K8S,但应用还是单体架构、手动发版、没有监控,那也不能叫云原生。

CNCF的云原生全景图里有几百个工具,K8S只是其中一块,但它是承上启下的核心。没有它,上面的服务网格、可观测性、CI/CD流水线都很难高效运转。

3. K8S到底解决了什么问题:从容器编排说起

3.1 容器编排的核心需求

假设你有一个应用,打包成了Docker镜像。现在你要把它跑起来,最简单的方式是docker run。但如果这个应用需要跑10个实例来扛流量呢?你需要手动执行10次docker run,还要记住每个实例的端口、日志位置、资源限制。如果某个实例挂了,你得手动重启。如果流量下降了,你得手动停掉多余的实例。

这些操作在实例少的时候还能应付,一旦上了规模,就是灾难。容器编排要解决的就是:自动调度、自动扩缩容、自动故障恢复、服务发现、负载均衡、配置管理、存储编排。K8S把这些能力都做成了标准接口,你只需要声明“我想要什么状态”,它负责把实际状态调整成你想要的样子。

这个“声明式”的思路是K8S最核心的设计哲学。你写一个YAML文件,说“我要3个副本,每个用1核CPU、512M内存,暴露8080端口”,K8S就会持续检查当前状态,如果只有2个副本,它会再启动1个;如果有4个,它会停掉1个。你不需要告诉它“先启动一个,再启动第二个”,只需要告诉它最终目标。

3.2 K8S的架构:控制平面与工作节点

K8S的架构分两部分:控制平面(Control Plane)和工作节点(Worker Node)。

控制平面负责做决策,包含几个核心组件:

  • API Server:所有操作的入口,kubectl命令、Dashboard、其他组件都通过它通信。
  • etcd:分布式键值存储,保存整个集群的状态。它是集群的“唯一真相来源”,丢了etcd就等于丢了集群。
  • Scheduler:负责决定新创建的Pod应该跑在哪个节点上,会考虑资源需求、亲和性、污点容忍等因素。
  • Controller Manager:运行各种控制器,比如Deployment控制器负责维持副本数量,Node控制器负责处理节点故障。

工作节点负责跑实际的应用容器:

  • Kubelet:节点上的代理,负责和控制平面通信,管理本节点上的Pod生命周期。
  • Kube-proxy:负责网络规则,实现Service的负载均衡。
  • 容器运行时:实际运行容器的组件,早期用Docker,现在主流是containerd或CRI-O。

这个架构的好处是职责清晰。控制平面可以部署在高可用的多节点上,工作节点可以随时增减。你只需要通过API Server提交期望状态,剩下的交给各个组件协同完成。

3.3 核心概念速览:Pod、Service、Deployment

K8S里有一堆概念,新手最容易晕。我挑三个最核心的讲清楚,理解了这三个,其他的都好说。

Pod是K8S最小的调度单位。一个Pod可以包含一个或多个容器,这些容器共享网络命名空间和存储卷。大多数情况下,一个Pod跑一个容器。Pod是临时的,重启或重新调度后IP会变,所以不能直接依赖Pod的IP做服务发现。

Service是一组Pod的抽象。它给这组Pod一个固定的虚拟IP和DNS名称,外部不需要知道具体有哪些Pod、IP是多少。Service通过标签选择器(Label Selector)找到对应的Pod,然后做负载均衡。比如你有一个Deployment起了3个Pod,创建一个Service指向它们,用户访问Service的IP,请求会自动分发到3个Pod上。

Deployment是管理Pod的控制器。你定义一个Deployment,指定镜像、副本数、更新策略,它就会帮你创建ReplicaSet,再由ReplicaSet管理Pod。滚动更新、回滚、扩缩容都是通过Deployment来做的。生产环境里,你几乎不会直接创建Pod,而是通过Deployment或StatefulSet这类控制器来管理。

这三个概念的关系可以这样理解:Deployment管ReplicaSet,ReplicaSet管Pod,Service把Pod暴露出去。你写YAML的时候,大部分时间是在写Deployment和Service。

4. 从零搭建一个最小可用K8S环境

4.1 工具选型:为什么选Minikube做入门

生产环境的K8S集群搭建涉及大量网络、存储、高可用配置,不适合入门。我建议新手从Minikube开始。它是一个单节点的K8S集群,跑在你本地的虚拟机或容器里,一条命令就能启动。

选Minikube的理由很简单:安装快、资源占用少、功能完整。它支持大部分K8S核心功能,包括Service、Ingress、ConfigMap、Secret、持久卷等。你可以在上面练习所有基本操作,不用担心搞坏生产环境。等熟悉了之后,再去看kubeadm、k3s或者云厂商的托管K8S服务。

安装Minikube之前需要先装一个容器运行时或虚拟机驱动。在Linux上可以用Docker驱动,在macOS上可以用HyperKit或Docker Desktop。具体命令根据系统不同有差异,我以Linux + Docker为例。

# 下载Minikube二进制文件 curl -LO https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64 sudo install minikube-linux-amd64 /usr/local/bin/minikube # 启动集群,指定用Docker驱动 minikube start --driver=docker --cpus=2 --memory=4096

启动完成后,用minikube status检查状态,用kubectl get nodes查看节点。如果看到节点状态是Ready,说明集群正常。

注意:Minikube默认会下载一个基础镜像,国内网络环境下可能比较慢。可以提前配置镜像加速,或者使用--image-mirror-country=cn参数。

4.2 部署第一个应用:Nginx示例

集群跑起来之后,我们部署一个Nginx应用来走一遍完整流程。

第一步,创建一个Deployment。写一个YAML文件nginx-deployment.yaml:

apiVersion: apps/v1 kind: Deployment metadata: name: nginx-deployment labels: app: nginx spec: replicas: 3 selector: matchLabels: app: nginx template: metadata: labels: app: nginx spec: containers: - name: nginx image: nginx:1.25 ports: - containerPort: 80 resources: requests: cpu: 100m memory: 128Mi limits: cpu: 250m memory: 256Mi

这个文件里几个关键点:replicas: 3表示要3个副本;selector告诉Deployment管理哪些Pod;template是Pod的模板;resources定义了资源请求和限制。requests是调度依据,K8S会根据它决定Pod放哪个节点;limits是硬限制,超过会被限制或杀掉。

应用这个文件:

kubectl apply -f nginx-deployment.yaml

然后查看Pod状态:

kubectl get pods -l app=nginx

你应该看到3个Pod,状态从ContainerCreating变成Running。如果卡在ContainerCreating,用kubectl describe pod <pod-name>查看事件,通常是镜像拉取问题。

第二步,创建一个Service把应用暴露出来。写nginx-service.yaml:

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

应用之后,用kubectl get svc查看。ClusterIP类型的Service只能在集群内部访问。如果想从本地访问,可以用kubectl port-forward:

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

然后在浏览器访问http://localhost:8080,应该能看到Nginx的欢迎页面。

4.3 扩缩容与滚动更新实操

K8S的扩缩容非常简单。把副本数从3改成5:

kubectl scale deployment nginx-deployment --replicas=5

几秒钟后,kubectl get pods会显示5个Pod。缩容同理,改成2就会停掉多余的Pod。生产环境还可以配置Horizontal Pod Autoscaler(HPA),根据CPU或内存使用率自动调整副本数。

滚动更新是另一个常用操作。假设Nginx镜像要从1.25升级到1.26,修改YAML里的image字段,然后重新apply:

kubectl set image deployment/nginx-deployment nginx=nginx:1.26

K8S会逐个替换Pod,而不是一次性全部停掉。你可以用kubectl rollout status deployment/nginx-deployment观察进度。如果新版本有问题,用kubectl rollout undo deployment/nginx-deployment回滚到上一个版本。

实操心得:滚动更新时,建议配置maxSurge和maxUnavailable参数。默认是25%,意味着更新过程中最多有25%的Pod不可用。对可用性要求高的服务,可以把maxUnavailable设为0,maxSurge设为1,这样会先启动新Pod再停旧Pod,保证服务不中断。

5. 常见问题与排查技巧实录

5.1 Pod一直处于Pending状态

这是新手最常遇到的问题。Pod处于Pending,说明调度器找不到合适的节点。排查步骤:

  1. kubectl describe pod <pod-name>查看Events部分。常见原因有:

    • 资源不足:节点没有足够的CPU或内存满足Pod的requests。
    • 节点选择器不匹配:Pod的nodeSelector或affinity规则没有匹配的节点。
    • 污点和容忍:节点有污点,Pod没有对应的容忍。
  2. 如果是资源不足,可以kubectl describe node <node-name>查看节点已分配资源。Minikube单节点资源有限,可以调大内存或减少Pod的requests。

  3. 如果是污点问题,用kubectl taint nodes --all node-role.kubernetes.io/master-移除master节点的污点(仅限测试环境)。

5.2 Pod处于CrashLoopBackOff状态

这个状态说明容器启动后反复崩溃。排查思路:

  • kubectl logs <pod-name> --previous查看上一次崩溃的日志。
  • kubectl describe pod <pod-name>看Events里有没有OOMKilled。如果是内存超限,调大limits或优化应用内存占用。
  • 检查应用的启动命令和参数是否正确。有时候是配置文件路径不对,或者依赖的服务没启动。

我遇到过一次,是因为应用启动时要连数据库,但数据库还没就绪,连接超时后进程退出。解决办法是加一个initContainer等待数据库端口可用,或者配置livenessProbe的initialDelaySeconds。

5.3 Service无法访问

Service创建了但访问不通,常见原因:

  • Selector不匹配:Service的selector和Pod的labels不一致。用kubectl get endpoints <service-name>查看,如果Endpoints为空,说明没匹配到Pod。
  • 端口映射错误:Service的targetPort和容器实际监听的端口不一致。检查容器里应用监听的端口。
  • 网络策略限制:如果集群启用了NetworkPolicy,可能阻止了流量。检查是否有策略限制了入站或出站。

5.4 常见问题速查表

问题现象可能原因排查命令解决方向
Pod Pending资源不足、节点选择器不匹配、污点kubectl describe pod调整资源请求、修改选择器、添加容忍
CrashLoopBackOff应用崩溃、OOM、启动命令错误kubectl logs --previous修复应用、调大内存、修正命令
Service不通Selector不匹配、端口错误、网络策略kubectl get endpoints修正标签、检查端口、调整策略
镜像拉取失败镜像名错误、私有仓库认证失败kubectl describe pod检查镜像名、配置imagePullSecrets
节点NotReadykubelet异常、网络插件故障kubectl describe node重启kubelet、检查CNI插件

避坑技巧:生产环境一定要给Pod配置livenessProbe和readinessProbe。livenessProbe检测到失败会重启容器,readinessProbe检测到失败会把Pod从Service的Endpoints里移除。没有这两个探针,K8S不知道你的应用是否健康,故障恢复就无从谈起。

6. 云原生落地的现实考量与个人体会

6.1 不是所有项目都适合上K8S

我见过一些团队,项目只有两三个服务,日活几千,也非要搭一套K8S集群。结果运维成本比省下来的资源还高。K8S的价值在规模效应——当你的容器数量超过几十个,服务之间调用关系复杂,需要频繁扩缩容和滚动更新时,它才能体现出优势。

小项目用Docker Compose或者直接跑在虚拟机上,可能更简单高效。云原生是一种思路,但具体工具的选择要看场景。你可以先容器化,用CI/CD自动化部署,等规模上来了再引入K8S。

6.2 学习路径建议

如果你决定要学K8S,我的建议是:

  1. 先理解容器基础,知道镜像、容器、仓库的区别,会写Dockerfile。
  2. 用Minikube或k3s搭一个本地集群,把Pod、Service、Deployment、ConfigMap、Secret这几个核心对象玩熟。
  3. 学习Helm,它是K8S的包管理工具,能把一堆YAML打包成一个Chart,方便复用和版本管理。
  4. 了解可观测性工具,比如Prometheus做监控,Grafana做展示,ELK或Loki做日志收集。
  5. 最后再去看服务网格(如Istio)和GitOps(如ArgoCD)这些进阶内容。

不要一上来就啃官方文档的每一个细节,那样很容易放弃。先跑起来,遇到问题再查,这样学得最快。

6.3 我踩过的一个真实坑

最后分享一个我实际踩过的坑。有一次在测试环境部署一个StatefulSet,用的是本地存储卷。节点重启后,Pod一直起不来,报错说卷被占用。排查后发现是节点异常关机时,K8S没来得及清理挂载点,导致新Pod无法挂载同一个卷。

解决办法是给StatefulSet配置podManagementPolicy: Parallel,并且在节点恢复后手动清理残留的挂载目录。后来我们改用网络存储(如NFS或云厂商的块存储),这个问题就没再出现过。本地存储适合测试,生产环境一定要用支持多节点挂载的网络存储,否则节点故障时数据和服务都恢复不了。

这个坑让我明白,K8S虽然把很多运维工作自动化了,但存储和网络这两个领域,仍然需要根据实际环境做仔细的配置和测试。不要假设默认配置能应对所有情况,多看看官方文档里关于生产环境最佳实践的章节,能少走很多弯路。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进