Kubernetes从入门到精通
Kubernetes从入门到精通
一、缘起:容器多了,也便有了烦恼
前回说到 Docker,一只只容器跑起来,倒也利落。然而容器一多,烦恼便来了。
一个人养三五只猫,尚且照应得过来;一旦养到三五百万只,便非得有个章程不可。哪只病了,哪只跑了,哪只该喂食,哪只该隔离,总不能全凭主人一双眼睛盯着。容器也是一样。一台机器上几十个,还好说;若是几百台机器,成千上万个容器,谁来管它们死活?谁来做主,让它们此起彼伏,各得其所?
这便是”容器编排”的由来。世间编排的工具原不止一种,但最终拔得头筹、几乎一统江湖的,是 Kubernetes。这名字源自希腊语,意为”舵手”——掌舵的人。Google 把自家内部用了十年的 Borg 系统的道理,开源了出来,便成了它。如今大抵简称 K8s——K 与 s 之间隔着八个字母,故云。
我以为,学 K8s 之前,先把 Docker 弄明白,是断然必要的。容器尚且不知为何物,便谈编排,无异于不会走路便要学骑马,非摔不可。
二、它究竟管些什么
舵手的职责,无非是让船开得稳。K8s 管的,也无非这么几桩:
- 调度:新来的容器,该放到哪台机器上?它替你定夺。
- 自愈:容器死了,它拉起来;机器坏了,它把上头的活儿挪到别处。
- 扩缩容:忙了多起几个,闲了收掉几个。
- 滚动更新与回滚:换新版,一点一点换;出了岔子,退回去。
- 服务发现与负载均衡:容器之间彼此怎么找,怎么分担,它管。
- 存储编排:数据往哪里搁,它替你张罗。
一言以蔽之,它替你照看着那一群朝生暮死的容器,叫它们此起彼伏,却总体上稳如泰山。
三、几个名词,先要弄清
K8s 的行话,比 Docker 还要多些。然而真正入门,要紧的也就下面这些。不必望而生畏。
Pod —— 最小的小卒
K8s 里调度的最小单位,不叫容器,叫 Pod。一个 Pod 里头,可以装一个或几个容器,它们同生共死,共享网络和存储。大抵相当于几个同住一间屋的伙计,一荣俱荣,一损俱损。日常用得最多的,是一个 Pod 一个容器——那多出来的位置,是留给”边车”的,比如顺带做个日志收集、代理转发之类。
Node —— 干活的机器
集群里的机器,叫 Node。分两种:Master(控制面)和 Worker(干活节点)。Master 是发号施令的,Worker 是实干的。Master 自己不下场跑业务,它只管调度指挥。
Deployment —— 带兵的将官
你平日说”我要三个 Nginx”,其实并非直接造三个 Pod,而是造一个 Deployment,告诉它”给我维持三个副本”。Deployment 便忠实地替你维持着,死一个补一个。它还管滚动更新——换版本时,新旧交替,缓缓而行。
Service —— 给 Pod 一个名分
Pod 是朝生暮死的,今日这个 IP,明日重启便换了。别的程序要寻它,总不能靠这朝三暮四的 IP。于是有 Service:它给一组 Pod 盖一个章,定一个固定的名字和 IP,无论底下 Pod 怎么换,寻这名字总找得到。它顺便还做负载均衡,把请求分到各 Pod 上。
Ingress —— 门房
Service 固然给了名字,可外头的世界要进来,总得有个门。Ingress 便是这扇门,管着 HTTP/HTTPS 的路由——哪个域名走哪个 Service,谁该走 HTTPS,它说了算。
ConfigMap 与 Secret —— 账本与密件
配置信息,写在 ConfigMap 里,像个账本,谁要谁来取;敏感的密码密钥,则放进 Secret,虽也是存着,却做了 base64 的一层遮掩,且可以挂载为文件或环境变量,不叫它明晃晃地写在镜像里。
Volume —— 给数据留的后路
Pod 一死,里头的文件便没了。要紧的数据,得挂个 Volume,独立于 Pod 而活。这点道理,与 Docker 一脉相承。
Namespace —— 分而治之
一个大集群,许多人共用,总得划出几间屋子,各管各的,互不相扰。这屋子便是 Namespace。生产一个、测试一个,或这个团队一个、那个团队一个,井水不犯河水。
四、架构:一座小小的朝廷
K8s 的架构,活像一个小朝廷,分作”控制面”与”工作节点”两头。
1 | ┌─────────────── 控制面 (Control Plane) ───────────────┐ |
- kube-apiserver:一切命令的入口。你用
kubectl敲的每条命令,都先到它这里。 - etcd:一个键值数据库,集群的全部家底——有哪些 Pod、哪些 Node、什么配置——都记在它里头。它是命根子,丢了它,整个集群便失了忆。
- kube-scheduler:新来的 Pod 没着落,它来安排去哪个 Node。
- kube-controller-manager:一群监工的合集。Deployment 的副本数对不对、Node 掉线了要不要挪 Pod,都归它管。
- kubelet:每个 Node 上一个,听 Master 的令,照看着本机的 Pod,该起起,该停停。
- kube-proxy:每个 Node 上一个,管着网络转发与负载均衡的规则。
- 容器运行时:真正跑容器的家伙,如今多用 containerd,Docker 也行,但已渐渐退居二线。
五、安装:搭一个练手的集群
生产环境的 K8s,装起来颇为繁复(推荐用 kubeadm,或干脆用云厂商的托管服务如 AKS/EKS/GKE)。但若是练手,我劝你用轻量工具,三五分钟便起一套,免得在安装上耗尽了兴致。
minikube —— 单机练手
1 | # 装 minikube(Linux) |
kind —— 用 Docker 跑 K8s
1 | # 装好后 |
kubectl —— 那条指挥棒
无论哪种集群,你都得装 kubectl,这是你与集群说话的嘴。
1 | # Linux |
装好,连上集群,kubectl get nodes 能看到节点,便算成了。
开启 kubectl 自动补全
1 | echo 'source <(kubectl completion bash)' >> ~/.bashrc |
六、kubectl:那条指挥棒
kubectl 的命令,大抵是这个模样:kubectl <动作> <资源类型> [名字] [选项]。例如 kubectl get pod nginx,便是”取一下名叫 nginx 的 Pod 看看”。
查看类
1 | kubectl get nodes # 看节点 |
操作类
1 | kubectl create deployment nginx --image=nginx:1.25 --replicas=3 |
Namespace 的进退
1 | kubectl get ns # 看有哪些屋子 |
一句话:记住
kubectl get、kubectl describe、kubectl apply -f这三板斧,便能走遍天下。其余的,用得着时再查不迟。
七、第一个实战:部署一个 Nginx
道理说了这许多,不如动手。我们来部署一个 Nginx,三层都搭齐:Deployment、Service、Ingress。
然而,真正像样的做法,不是敲 kubectl create 那种一次性的命令,而是写 YAML——把你想的样子,用文字白纸黑字写下来,存进版本库。这便是所谓”声明式”的道理:你不说”去给我做这个”,只说”我要它变成这个样子”,K8s 自会去把现实捋成你写的样子。
Deployment
1 | # nginx-deployment.yaml |
1 | kubectl apply -f nginx-deployment.yaml |
Service
1 | # nginx-service.yaml |
1 | kubectl apply -f nginx-service.yaml |
Service 有几种类型,用途各异:
- ClusterIP(默认):集群内部一个虚拟 IP,外头进不来。
- NodePort:在每个 Node 上开一个端口(30000–32767),外头能进。练手用。
- LoadBalancer:云上自动给你配一个负载均衡器,生产常用。
- ExternalName:做个 DNS 别名,指向集群外的服务。
Ingress
1 | # nginx-ingress.yaml |
用 Ingress,得先装个 Ingress Controller(如 ingress-nginx),否则这规则写了也没人执行,如同贴了告示却没有门房。
八、YAML 的骨架
所有的 K8s YAML,骨架都是这么个模样,记住它,便算入门了一半:
1 | apiVersion: apps/v1 # 用哪个 API 版本 |
最常搅混的,是 Deployment 里 selector 与 template 的标签必须对得上——selector.matchLabels 说”我管带这些标签的”,而 template 造出来的 Pod 必须带着这些标签。两者对不上,Deployment 便不认自己造的 Pod,白白干着急。
九、配置与敏感信息
镜像是死的,配置却是活的。同一套镜像,开发用一套配置,生产用另一套,这才像样。所以配置不该塞进镜像,而该分开来放。
ConfigMap —— 账本
1 | apiVersion: v1 |
Secret —— 密件
1 | apiVersion: v1 |
用法:挂为环境变量,或挂为文件
1 | spec: |
须注意:Secret 的 base64,只是不让人一眼看清,并非真加密。真正要紧的密钥,该上 Vault、云 KMS,或开启 etcd 的静态加密,方才稳妥。
十、数据持久化:StatefulSet 与 PVC
Deployment 管的是无状态的兵——死一个补一个,谁是谁无所谓。可有些应用是有状态的:数据库便是。它的数据不能丢,它的身份也不能乱——主从之分,各司其职。这类,得用 StatefulSet。
StatefulSet 造出来的 Pod,名字是排了号的:db-0、db-1、db-2,顺序井然,各有各的存储,各记各的账。即便重建,db-0 还是 db-0,还连着 db-0 那块旧盘。
1 | apiVersion: apps/v1 |
这里头出现了 volumeClaimTemplates,便是 PVC——向存储系统申请一块地。这块地由 StorageClass(存储类)来供给,云上自动给你造一块云盘,本地则有 local、nfs 等。
三种负载类型,理清它们
| 类型 | 用谁 | 何时用 |
|---|---|---|
| Deployment | 无状态应用 | Nginx、Tomcat、Web 后端 |
| StatefulSet | 有状态应用 | 数据库、消息队列、需稳定身份/存储的 |
| DaemonSet | 每个 Node 一个 | 日志采集、监控代理、网络插件 |
外加一个 Job / CronJob:跑完即退的活儿,如数据迁移;定时跑的,如备份。
十一、调度:谁去哪台机器
Pod 该落到哪个 Node 上,本是调度器的事。但有时你总想插一手——这几个 Pod 不能挤一台,那几个只准跑在带 SSD 的机器上。这便靠”亲和性”与”污点”。
节点选择器(最简)
1 | spec: |
亲和与反亲和(更精细)
1 | spec: |
污点与容忍
有些 Node 你想专用——比如 GPU 机器,只准跑 AI 的活。便给这 Node 打个”污点”,别人家的 Pod 嫌脏不肯来;只有声明了”容忍”的 Pod,才上得去。
1 | # 给节点打污点 |
1 | spec: |
十二、HPA:忙则增,闲则减
人来人往,流量有高低。固定副本数,既费钱又怕扛不住。HPA(Horizontal Pod Autoscaler)便是管这事:看着 CPU 或自定义指标,忙了加 Pod,闲了减 Pod。
1 | apiVersion: autoscaling/v2 |
用 HPA,得先装个 metrics-server,否则它没处看指标。
十三、Helm:包管理,一捆一捆地装
手写一堆 YAML,部署一个应用尚可;若要部署一整套(如 Prometheus 监控全家桶),一个个文件敲下来,未免啰嗦。于是有 Helm——K8s 的包管理器,相当于 Ubuntu 的 apt、CentOS 的 yum。它把一组 YAML 打成一个”Chart”,配置项抽出来,改几个值便成一套新部署。
1 | # 装 Helm |
自定义 Chart:
1 | helm create myapp # 生成一个 Chart 骨架 |
十四、排错:出了岔子怎么办
我以为,学 K8s,一半的功夫在排错上。集群这东西,不出问题则已,一出便叫人抓瞎。这里有几条路数,按图索骥便好。
第一招:看 Pod 状态
1 | kubectl get pods |
常见的几种异常状态:
- Pending:Pod 没被调度。多半是资源不够、没合适的 Node、或被污点挡了。
kubectl describe pod <名字>,看最底下的 Events。 - ContainerCreating:正在创建,卡住多半是拉镜像失败、挂存储失败、或 ConfigMap/Secret 不存在。
- CrashLoopBackOff:起来就崩,崩了又起,循环往复。多半是程序自身的问题。
kubectl logs <名字>看它说了什么。 - ImagePullBackOff:镜像拉不下来。检查镜像名对不对、仓库凭证配没配。
- OOMKilled:内存超了 limits 被杀。调大 limits,或查程序为何吃内存。
第二招:describe 看 Events
1 | kubectl describe pod <名字> |
最底下的 Events 一栏,往往直指病根——什么时候调度了、什么时候拉镜像了、为什么失败。这是排错头一等的工具。
第三招:看日志
1 | kubectl logs <pod> |
第四招:钻进去
1 | kubectl exec -it <pod> -- bash |
第五招:看节点
1 | kubectl get nodes -o wide |
Node 若是 NotReady,多半是 kubelet 挂了、或机器本身出了问题。
常见疑难
1 | # Pod 一直 Pending |
十五、安全:几分警惕
K8s 默认并非铁桶一只,该加固处仍须加固。我以为有几条,断不可省:
1 | 1. RBAC:用最小权限。ServiceAccount 别图省事绑 cluster-admin, |
RBAC 一例:
1 | apiVersion: v1 |
十六、速查表
| 要做什么 | 念这句 |
|---|---|
| 看节点 | kubectl get nodes |
| 看 Pod | kubectl get pods -A |
| 看 Pod 详情 | kubectl describe pod <名字> |
| 看日志 | kubectl logs -f <名字> |
| 看崩前的日志 | kubectl logs <名字> --previous |
| 进 Pod | kubectl exec -it <名字> -- bash |
| 部署 | kubectl apply -f xxx.yaml |
| 删 | kubectl delete -f xxx.yaml |
| 扩缩容 | kubectl scale deploy <名字> --replicas=5 |
| 回滚 | kubectl rollout undo deploy/<名字> |
| 看资源 | kubectl top nodes / kubectl top pods |
| 切 Namespace | kubectl config set-context --current --namespace=dev |
| 看所有资源 | kubectl get all -n <ns> |
| 查字段 | kubectl explain pod.spec.containers |
结尾的话
K8s 这东西,初看庞杂得吓人——概念多,名词多,YAML 又长。然而细细理来,脉络其实分明:无非是用 声明式 的 YAML,描述你想要的模样,然后让控制面去把现实捋成那个模样。Pod 是兵,Deployment 是将,Service 是名分,Ingress 是门,ConfigMap/Secret 是账本与密件,StatefulSet 管有状态的,PVC 管存数据的。把这些个角色的道理弄通,大半的场面便能应付了。
学它的法子,我以为别无他途:先 minikube 起一套,亲手写几个 YAML,部署、扩缩、更新、回滚、排错,各做一遍。看十遍文档,不如自己崩一次 Pod 再救回来。纸上得来终觉浅,绝知此事要躬行——这话虽是古人说的,放在 K8s 上,再贴切不过。
至于再往前的 operator、service mesh(Istio)、CI/CD 流水线、多集群管理,那是各人的造化与机缘了。地基打牢了,上头盖什么楼,都好说。
参考的所在: