资讯详情

Kubernetes入门:从核心概念到集群搭建与部署实战

📅 2026/10/22 8:57:51 | 华诺云谱 👁 阅读
Kubernetes入门:从核心概念到集群搭建与部署实战
1. 先搞清楚k8s到底在解决什么问题先说个场景你去年在几台服务器上用Docker部署了业务平时人少的时候还挺安稳。结果今年流量涨了每次发版要靠人去登录机器拉镜像、重启容器扩容要提前申请机器、手动加副本流量一来某个节点挂了还得半夜爬起来恢复服务。这时候你大概率已经听过“要不上一套k8s吧”。k8s全称Kubernetes是容器编排领域的实际标准。它做的核心事情可以概括成一句话把一堆服务器变成一个资源池然后你告诉它“我要跑多少个实例、每个实例用什么镜像、需要多少CPU和内存”它自己去调度、去拉起容器、去保持副本数稳定。你不直接操作具体的某台机器你面对的是一个“集群”。为什么大家都说它扫盲难因为它涉及的概念确实多而且很多概念之间相互关联。但换个思路看你不需要一开始就搞懂所有内部机制你只需要先回答清楚三个问题它解决了我什么痛苦它里面有哪些常用对象我怎么把一个应用跑起来这篇文章就按这个顺序来说。适合谁看呢适合那些用过Docker、但还没真正上手过k8s的人也适合刚被安排去搭建集群、但不知道从哪下手的同学。我不会讲过于底层的架构源码重点是把“用得起来”这件事讲透。1.1 单机容器时代的“手工运维”困境在k8s出现之前或者说没有编排工具的情况下用Docker部署服务是什么状态你会在每台服务器上安装Docker然后用docker run把容器拉起来用--restartalways保证进程挂了能自动重启。这是单机自愈但它有几个很现实的问题你无法自动把容器调度到空闲的机器上。所有实例都靠人肉记录“哪个服务部署在哪台机器”。滚动更新很难做。要么停机更新要么自己写脚本先启一批新的再杀旧的脚本一旦没写好发布到一半状态就很尴尬。没有服务发现。容器A要访问容器B你得通过端口映射、外部负载均衡或者手工维护的配置文件去连通。容器一旦换了一台机器IP变了你得改配置。弹性伸缩基本靠运维人员“看见告警了再去加容器”而且加完之后机器资源够不够也不好说。我见过很多团队在没有k8s时会自己写一套“基于SSH连接远程主机、批量执行Docker命令”的部署脚本。刚开始十几台机器、五六个服务还能忍服务数量涨到几十个后这套脚本就成了定时炸弹。某个服务发布失败都不知道是脚本bug还是环境差异排查一次发布事故要花掉大半天。这不是说Docker不好而是当你的规模上去后单靠命令行和脚本管理容器人的精力根本跟不上机器的节奏。1.2 k8s给出的答案声明式编排k8s最大的思想转变在于从“命令式”变成“声明式”。命令式是什么就是你告诉计算机“下一步做什么”。你输入docker run nginx就是把nginx这个容器拉起来。你要扩容到10个容器你得再输入9遍或者写一个循环脚本。你要让这10个容器分布在不同机器上你得知道每台机器还剩多少资源然后手动指定。声明式是什么你告诉k8s“我期望最终的状态是10个nginx副本”。你写入一个YAML文件里面写replicas: 10然后提交给k8s。剩下的调度、拉起、健康检查、保持存活全部由k8s内部的控制器循环去处理。如果有一台机器宕机了上面有3个nginx副本k8s会因为“当前实际副本数是7、期望是10”而在其它健康的机器上自动补3个。这就像你以前每天打电话给餐厅订餐说“今天要3份饭”到了明天说“今天要5份饭”。而声明式是你直接告诉餐厅“我每天中午都需要保证桌上有5份饭”至于哪桌怎么送、几点送达餐厅自己去安排。你还是需要表达你的需求但你不用再关心一个个具体操作。1.3 哪些人最需要关注k8s如果你所在的团队有以下几种情况优先级可以往前提微服务数量增多。服务数量超过二三十个部署、回滚、配置管理都已经开始互相牵扯。流量有明显的峰谷。比如每天固定时间流量暴涨需要快速扩容、闲时缩容。频繁发布迭代。一天要发很多次每次发版都要手动操作已经成了流程瓶颈。资源利用率低。平均每台机器负载不到20%但你不敢回收机器因为担心某台机器上的服务会受影响。如果你只是在一台服务器上跑两三个服务k8s对你不一定是刚需你完全可以继续用docker-compose。但如果已经遇到上述痛点早一点把编排能力建立起来后续会省下非常多的人工成本和沟通成本。2. 扫盲第一课必懂的核心概念这一部分我建议你当作“高频词汇手册”来看不用背知道它们是什么、在什么场景下出现就能读得懂配置文件了。2.1 Podk8s调度的最小单位很多新手最大的困惑在于我都已经有容器了为什么还搞出个Pod一方面k8s不直接操作容器它认为Pod才是它调度和管理的最小单元。Pod是一个逻辑边界里面可能运行一个容器也可能运行多个容器。为什么需要多个容器在同一个Pod里典型场景是边车模式Sidecar。比如你的业务容器是一个Web应用旁边再挂一个日志收集容器它俩共享网络栈和存储卷能通过localhost直接通信日志文件也不用跨容器拷贝。你只需要记住一个结论**同一Pod里的容器共享同一个IP、同一组端口和同一份存储卷并且它们会被调度到同一台物理机上。**如果需要解耦的、各自独立扩展的服务应该分成不同的Pod。举个生活化类比Pod像一个合租公寓里面住着的几个容器室友共享水电表网络和存储这间公寓是整个楼栋Node节点里的基本出租单元而k8s就是那个管家它不关心室友们内部怎么分工只管把整间公寓分配到哪栋楼去。2.2 Deployment你想要的副本数由它保证你要运行一个服务不会直接去创建Pod因为Pod没有自愈能力——如果Pod所在的机器宕了Pod就没了。所以你应该创建Deployment。Deployment是k8s里最常用的控制对象它负责管理一堆Pod副本并保证期望的副本数。它内部还会创建ReplicaSet副本集Deployment的每次更新都会生成新的ReplicaSet用来做版本管理。你改一下镜像版本重新apply配置Deployment就会滚动更新一个一个替换Pod期间服务不会全部停止。这个对象承载了三个能力扩容缩容、滚动更新、回滚。打个比方Deployment就是“门店的店长”ReplicaSet是“排班表”Pod是“员工”。店长一声令下“今天店里需要有10个人上班”排班表会拉出一个10人的团队。如果一个人请假了Pod挂了排班表自动补充一个人进来。店长说“明天换成另一套服务标准”排班表全部换一批员工。如果新标准出了问题店长说“退回今天的那套”排班表又恢复到上一个版本。2.3 Service与Ingress外部流量怎么进得来你的应用在Pod里跑Pod是有生命周期的。容器重启、节点迁移IP都会变化你不能让调用方记住某个Pod的IP。Service就是解决这个问题的“抽象层”。Service给一组Pod提供了一个稳定的虚拟IPClusterIP以及一个DNS名字。你部署了一个名为nginx的Deployment然后创建同名的Service集群内部其它应用就可以通过http://nginx:80来访问它。Service背后会自动关联匹配Label标签的Pod并把请求负载均衡到各个健康的Pod上。Ingress则负责集群外部访问的路由。它更像一台“七层路由”可以通过域名和路径把请求转发给不同Service。比如你有一个域名api.example.com可以配置Ingress规则把/user开头的转发给用户Service把/order开头的转发给订单Service。这两个概念刚学时比较容易和Service的NodePort模式搞混。简单区分一下ClusterIP Service仅供集群内部访问。NodePort Service通过“每台节点IP 随机高位端口”从集群外部访问。LoadBalancer Service云平台场景下创建负载均衡器把流量打到节点上的某个端口。Ingress在HTTP/HTTPS层面按域名和路径做路由集群外部流量先到Ingress控制器再由它转发给内部Service。2.4 Namespace、ConfigMap、Secret这些“辅助角色”Namespace用来隔离资源。你在团队里划环境、划部门都可以用它区分。比如dev、prod各建一个Namespace里面的资源互不影响。新手刚上手时如果分不清就在默认的default命名空间里操作当资源多到无法一眼找到时再考虑拆分。ConfigMap和Secret都是配置管理的载体区别是Secret用于存放敏感信息比如数据库密码、API密钥存储的值会做base64编码。ConfigMap存普通配置比如环境变量、配置文件内容。它们的好处是配置和镜像解耦不用改代码和镜像就能调整运行参数。这组概念确实杂但我建议按使用频率去消化Deployment和Service是最常用、最先要懂的Pod是你要排障时必须面对的Namespace是项目大到需要隔离时再用ConfigMap和Secret则是你学会写YAML之后自然而然就会遇到的。3. 动手实操在真实集群上把应用跑起来概念讲完了现在就差动手。我推荐一条不走弯路的上手路线先用生产可用的工具比如kubeadm搭一个三节点集群在上面部署一个nginx应用把它从集群内部和外部都访问一遍。这个流程走通你基本上就有“k8s似乎也没那么神秘”的实感了。很多新人上来就想着先学Docker Compose再学minikube最后再接触kubeadm。这种路径有它的理由但面对“k8s扫盲”这个目标我更建议直接进入集群世界的思维模式。因为k8s和单机的思考方式差异很大越早切换越好。3.1 节点规划与基础环境准备为了模拟生产又不复杂可以准备三台Linux服务器虚拟机就行云主机也行角色建议配置系统建议master节点2核4GUbuntu 22.04 / CentOS 7.9以上worker节点12核4G同上worker节点22核4G同上master节点主要负责集群的管理控制worker节点负责调度运行应用容器。你需要配置好三台机器的静态IP保证它们之间可以互相ping通并且都能访问镜像仓库。内存不能太小否则集群初始化后系统组件本身的负载就把机器压垮了后面排障都变困难。然后是容器运行时的选型。很多人纠结装Docker还是containerd。直接从k8s 1.24版本之后dockershim已经被移除kubelet不再直接兼容Docker了。虽然你依然可以在节点上装Docker但最终kubelet对接的还是containerd。所以新手直接用containerd作为运行时反而少一层适配问题。我实测下来Ubuntu系安装比较省心因为内核版本较新iptables相关的兼容性也比较好。三台机器上执行以下基础操作关闭swapswapoff -a并注释掉/etc/fstab里的swap行。为什么必须关因为kubelet在资源管理上如果发现swap在用它的cgroup资源统计就会出现偏差节点调度时算不好内存容量。加载两个内核模块cat EOF | sudo tee /etc/modules-load.d/k8s.conf overlay br_netfilter EOF sudo modprobe overlay sudo modprobe br_netfilter配置sysctl让IPv4流量转发和iptables对桥接流量可见cat EOF | sudo tee /etc/sysctl.d/k8s.conf net.bridge.bridge-nf-call-iptables 1 net.bridge.bridge-nf-call-ip6tables 1 net.ipv4.ip_forward 1 EOF sudo sysctl --system这些看起来像是“仪式感”操作但每一条都有实际作用。不装br_netfilter的话集群内Pod之间通讯经过网络插件时iptables规则可能匹配不到转发的流量导致网络通不了排查起来非常挠头。3.2 安装containerd与crictl很多新人在安装containerd之后启动容器会报错例如failed to load cni config: file does not exist之类。原因多半是containerd默认没有启用CNI配置或者SystemdCgroup没有设置为true。这一步不做对后面初始化集群Pod时大概率失败。先安装依赖并添加官方源然后安装sudo apt-get update sudo apt-get install -y ca-certificates curl gnupg lsb-release sudo mkdir -p /etc/apt/sources.list.d # 添加docker官方源的步骤略不同系统下源的配置方式不同 sudo apt-get install -y containerd装好后需要初始化containerd的默认配置sudo mkdir -p /etc/containerd containerd config default | sudo tee /etc/containerd/config.toml然后修改这个config.toml重点有两处找到SystemdCgroup把false改成true。这是因为kubelet以systemd作为cgroup驱动containerd要和它统一否则两边管理出现分歧。如果你在的网络环境访问官方镜像仓库比较吃力可以在sandbox_image处把pause镜像源换成一个自己方便访问的源地址。这一步介于可改与不可改之间取决于实际环境我先按下不表等遇到镜像拉取问题时再展开。改完重启sudo systemctl restart containerd sudo systemctl enable containerd同时安装crictl命令行工具它是containerd生态里检查容器运行时的重要调试工具。后面排查容器启动失败时crictl ps -a、crictl logs container是最高频的命令。3.3 用kubeadm初始化master节点三台节点都装好kubeadm、kubelet、kubectl之后先在master节点执行初始化sudo kubeadm init --apiserver-advertise-endpoint192.168.x.x --pod-network-cidr10.244.0.0/16--apiserver-advertise-endpoint填master节点的IP。--pod-network-cidr必须和后面安装的网络插件对应比如用Flannel就用10.244.0.0/16用Calico则用192.168.0.0/16两者不能冲突否则Pod网段和Service网段会乱套。初始化成功后会输出一段kubeadm join命令包含token和ca证书hash这个token默认24小时后过期。如果之后才发现忘了保存也可以重新生成kubeadm token create --print-join-commandroot用户执行初始化后普通用户要连集群还要配置一下kubectl的权限文件mkdir -p $HOME/.kube sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config sudo chown $(id -u):$(id -g) $HOME/.kube/config这里有一个常见的坑如果不复制admin.conf而是直接尝试用kubectl get nodes会报一个connection refused或者权限不足的错误。原因是kubectl默认读取$HOME/.kube/config而kubeadm把配置生成了在/etc/kubernetes/admin.conf两者路径没对上。初始化完成后你会看到master节点的状态是NotReady。先不用慌这是正常的因为没有安装网络插件。网络插件是必须装的它负责给每个Pod分配集群内的虚拟IP并打通跨节点Pod间流量。以Flannel为例kubectl apply -f https://raw.githubusercontent.com/flannel-io/flannel/master/Documentation/kube-flannel.yml然后过一两分钟再查状态kubectl get nodes看到STATUS变成Ready就算master节点完成了。3.4 把worker节点加入集群在worker节点上执行刚才保存的kubeadm join命令。加入后如果节点加入失败多半是token过期或者--cri-socket参数没有指定到containerd。如果你的节点装了不止一个容器运行时必须添加kubeadm join 192.168.x.x:6443 --token xxx --discovery-token-ca-cert-hash sha256:xxx \ --cri-socket unix:///var/run/containerd/containerd.sock在master上再查询看到两个worker节点都Ready就可以进行下一步了。3.5 部署一个nginx应用并验证访问既然集群已经就绪我们就跑一个最经典的nginx Deploymentkubectl create deployment nginx-demo --imagenginx:latest为了确保正常访问还可以通过文件方式创建。我个人建议从第一个YAML开始就用文件管理因为后面你一定会越来越依赖Git管理这些配置。以下是最简示例apiVersion: apps/v1 kind: Deployment metadata: name: nginx-demo spec: replicas: 3 selector: matchLabels: app: nginx template: metadata: labels: app: nginx spec: containers: - name: nginx image: nginx:latest ports: - containerPort: 80保存为nginx-deploy.yaml执行kubectl apply -f nginx-deploy.yaml等待Pod拉取镜像并变成Runningkubectl get pods -o wide再创建一个Service让集群内能稳定访问kubectl expose deployment nginx-demo --port80 --target-port80 --typeClusterIP然后你在集群内任意一台节点上用Service的ClusterIP试一下curl http://ClusterIP能看到nginx欢迎页说明集群内部链路已经打通。如果你还想从集群外访问可以把Service改成NodePortkubectl get svc nginx-demo # 找到显示的端口比如80:32345/TCP然后浏览器访问http://任意节点IP:32345同样能看到页面。这一步跑通你对“Deployment管副本、Service管访问”的理解就能落地了。接下来不管你用不用深度知识调优至少对k8s的运作模式已经建立了直觉。4. 新手入坑最常见的几个“坑”说实话k8s出问题的时候报错信息经常不是一般人能一眼看懂的。我把自己踩过的一些坑整理出来你们碰到类似问题时可以少走弯路。4.1 Pod一直Pending半天起不来Pending状态说明Pod在调度阶段就被卡住了。可以先看具体原因kubectl describe pod pod-name里面Events字段会给出线索。最常见的几类原因节点资源不足。节点头上标有taint污点或者CPU、内存都已被占满Pod无法调度。这时你可以kubectl describe node node-name查看Allocatable资源。镜像拉取失败。Events里会出现Failed to pull image或Back-off pulling image。这种情况在下一节单独说。PVC无法绑定。如果Pod声明了持久化存储而对应的StorageClass没有提供动态供给PVC会一直PendingPod自然起不来。还有一种容易忽略的情况master节点默认有NoSchedule污点用来防止Pod调度到master上。如果你在测试环境只有单节点可以在master上执行kubectl taint nodes --all node-role.kubernetes.io/control-plane-注意这个命令会移除所有节点上的控制平面污点可以让Pod正常调度到master。生产环境不建议这么干但单机学习时它是最快的一条路。4.2 镜像拉取失败的正确排查顺序新手在这里最容易懵因为错误信息五花八门。我的排查顺序是先看是否能本地手动拉取。在节点上执行crictl pull nginx:latest如果这个都失败说明确实拉不下来基本是网络或仓库源问题。再看containerd配置里的sandbox镜像pause是否可用。pause镜像如果拉不下来Pod也会卡在ContainerCreating。最后看是否设置了私有仓库认证。私有仓库需要先在节点上配置Docker registry的认证文件并且要在kubelet/containerd层面做好引用。如果是自签证书的仓库还要处理证书信任问题着实不省心。如果默认镜像源速度太慢可以改containerd的config.toml配置通过增加镜像的endpoint列表来指定一个更快、可用的镜像地址。这种配置方式灵活度较高但每个版本写法略有不同改完一定要重启containerd再测试。4.3 节点显示NotReady问题排不出节点变成NotReady首先是看kubelet是否活着systemctl status kubelet sudo journalctl -u kubelet -f大多数情况下kubelet日志里会出现类似failed to run Kubelet: ... misconfiguration: kubelet cgroup driver systemd but cgroupfs is set的报错这就是典型的cgroup driver不一致问题。但这种情况一般发生在老版本上。新版本里更常见的是网络插件问题。尤其是重启过节点之后Flannel或Calico的Pod没起来节点会仍然处于NotReady状态。处理方式很直接kubectl get pods -n kube-flannel # 或者 kubectl get pods -n kube-system看对应的网络组件Pod有没有Running。如果一直CrashLoopBackOff删掉让它重新调度一次多半能恢复。还有一种很隐蔽的情况如果你更换了节点IP或者修改了/etc/hosts里的主机名映射会导致kubelet上报的IP地址变化进而让控制平面无法和节点通信。改主机名也该在装集群之前一次性定好之后尽量避免变更。4.4 想在一台机器上快速体验怎么办如果你暂时拿不出三台机器只是想快速看一眼k8s长什么样可以用minikube或者Kind。minikube适合本地单机体验它内部自带一个虚拟机或使用Docker配置较轻。Kind更绝直接在Docker容器里跑一套集群每条Node都是一个容器。对于本地学习、写demo、调YAML非常友好。不过它们的宿主机性能要求不低。minikube共默认分配2核2G如果你电脑只有16G内存也建议至少给k8s相关环境富余一部分出来否则集群起来后Pod之间访问的体验会很差出现莫名其妙的超时。等你理解了k8s的基本操作还是建议回到kubeadm方式搭一套真正的多节点集群。毕竟很多问题只有在多节点场景才会暴露出来比如跨节点Pod连通、节点容忍度、亲和性调度等。4.5 关于kubectl高频命令的建议最后补充一个我用得最多的命令清单# 查看资源状态-w可以实时监听 kubectl get pods -w kubectl get nodes -o wide kubectl get svc # 详细查看事件排障核心 kubectl describe pod 名称 # 进入容器 kubectl exec -it pod名称 -- bash # 查看容器日志 kubectl logs pod名称 # 临时端口转发适合本地调试内部服务 kubectl port-forward svc/nginx-demo 8080:80port-forward是个宝藏命令。很多服务没有对外暴露时测试接口、调本地代码联调都可以用它临时打通隧道不需要特意改Service类型。根据我个人的经验k8s扫盲最大的门槛并不在某个单独概念有多难而是信息太碎、术语太密新手容易迷失在细节里。如果你能先把“Deployment管应用、Service管访问、Pod是基本单位、Node是机器抽象”这条主线抓住再往下学就自然很多。遇到问题也不要慌多用describe多看Events大部分问题都会在事件里直接告诉你答案。如果看完这篇文章你能在一套真实的集群上成功部署一个应用并打开页面那这个门槛基本上就算踏过去了。之后无论是继续研究Ingress、Helm还是深入调度和网络插件都会有自己的节奏。
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑