kubernetes中的资源
1.资源管理介绍
在kubernetes中,所有的内容都抽象为资源,用户需要通过操作资源来管理kubernetes。 kubernetes的本质上就是一个集群系统,用户可以在集群中部署各种服务 所谓的部署服务,其实就是在kubernetes集群中运行一个个的容器,并将指定的程序跑在容器中。
kubernetes的最小管理单元是pod而不是容器,只能将容器放在 Pod 中, kubernetes一般也不会直接管理Pod,而是通过 Pod控制器来管理Pod的。
Pod中服务的访问是由kubernetes提供的 Service 资源来实现。 Pod中程序的数据需要持久化是由kubernetes提供的各种存储系统来实现
2.什么是pod
Pod就是Kubernetes世界里装容器的"豆荚"(Pod直译就是豆荚)。 一个豆荚里可以装一颗豆子(一个容器),也可以装好几颗紧紧挨着的豆子(多个容器)。
(1)它是K8s里最小的"干活单位"
在Kubernetes里,没有"单个容器"这个概念。你没法直接让K8s去跑一个Docker容器,你必须把容器装进Pod这个"盒子"里,K8s只认这个"盒子"。所以,Pod是K8s调度和分配资源(比如CPU、内存)的最小单元。
(2)里面的容器"同生共死"
一个Pod里的所有容器,是打包在一起部署的,它们一定运行在同一台物理机或虚拟机上。
-
共享IP地址 :Pod里的所有容器共用同一个IP地址。它们之间互相访问,直接用
localhost:端口就行,就像住在一个房子里互相串门。 -
共享存储:Pod可以挂载一个"共享文件夹",里面的容器都能往里读写文件。比如一个容器负责写日志,另一个容器负责读取这个日志上传到云端。
-
同生共死:只要这个Pod在,里面的容器就都在。一旦Pod被销毁(比如崩溃了被重启),里面的所有容器都会被一起销毁重建,IP地址也会变。
(3)为什么要多放几个容器在一个pod里
有一个经典场景叫**"主容器 + 边车容器"**:
-
主容器:跑你核心的业务程序(比如一个Java Web服务)。
-
边车容器(Sidecar):作为辅助,专门负责给主程序做"杂活"。比如自动拉取最新的配置文件、给主程序做代理网关、或者把主程序的日志格式转换一下再上报。
因为这两个容器必须配合得天衣无缝(共享网络和文件),而且必须同时启动、同时消亡,所以把它们塞进同一个Pod里,让K8s统一管理,最省心。
3.pod管理的核心目标
Pod管理的终极目标,是将应用的部署、扩缩容、自愈、更新等运维工作自动化,让应用按照用户期望的状态运行
4.pod管理的手段
在实践中,我们极少直接创建和管理Pod,而是通过Kubernetes提供的各种"控制器"(Controller)来间接管理。
控制器就像一个自动化的管家,你只需告诉它"我需要什么类型的应用、需要几个副本",它就会自动创建、监控并维护Pod的数量和状态
常见的管理方式如下:
(1)命令空间管理
就好比你直接踹一脚售货机,喊一嗓子:"给我来罐可乐!"对应的命令就是kubectl create deployment nginx --image=nginx。这种搞法简单粗暴,适合你自己在家练手。但毛病也很明显------你踹完这脚,售货机是给你可乐了,可明天这机器重启了,它不记得你踹过它,那罐可乐就没了。也就是说,你用命令创建的东西,Kubernetes不会帮你长期维护。
#查看命名空间

#创建命名空间

删除命名空间

#查看pod运行情况和在哪里运行

#创建pod


#当pod创建出现问题

#查看pod运行的详细信息

#删除pod

(2)声明式
你提前写一张纸条(YAML文件),上面画清楚:"我要一罐可乐,而且只要我这纸条在,你就得保证我这罐可乐永远在。"然后你把纸条交给售货员,喊一声**kubectl apply -f 那张纸条.yaml** 。售货员就会一直盯着,可乐没了就补,凉了给你换热的。这才是生产环境该有的玩法,因为你的需求变成了文件,能存进Git,能追溯历史,别人也能看懂你当初到底想要什么。
5.kubectl命令
kubectl是你跟Kubernetes集群对话的唯一工具。你想让集群干点啥,都得通过它。这11个命令基本覆盖了你日常运维80%以上的操作。
(1)create------创建资源
两种用法:
直接敲命令:kubectl create deployment nginx --image=nginx
用YAML文件:kubectl create -f nginx.yaml
两者的区别:直接敲命令适合快速测试,用YAML文件适合正式环境,因为文件能存进Git,可追溯、可复用。
注意,create是强制创建,如果你创建的资源已经存在了,它会报错告诉你"已经有了"。所以日常更常用的是apply(声明式),而不是create。apply的特点是"有则更新,无则创建",更符合声明式管理的思路。

(2)edit------直接编辑资源
这个命令会打开一个文本编辑器(默认是vim),让你直接修改集群里某个资源的YAML配置。
比如你想改一下Nginx的镜像版本,敲kubectl edit deployment nginx,它会自动打开配置文件,你改完保存退出,集群立刻生效。
这玩意儿的好处是快,不用手动写文件再apply。坏处是:你的修改不会保留在任何本地文件里,如果集群重启了,改的东西可能就丢了。所以临时调试可以用edit,正式变更还是建议改本地的YAML文件再apply。

(3)patch------打补丁
patch比edit更"轻量"。edit是把整个配置文件打开让你改,patch是你只告诉系统"我要把某个字段改成某个值",其他不动。
比如你想给Deployment打个标签,可以敲:
kubectl patch deployment nginx -p '{"spec":{"template":{"metadata":{"labels":{"version":"v2"}}}}}'
这玩意儿长得吓人,但它确实有用。适合自动化脚本里做小范围修改,或者紧急情况下快速改动一个字段。日常手工操作,edit比patch用得更多,因为edit更直观。

(4)expose------暴露服务
这个命令的作用是:把你跑在Pod里的服务,变成集群内部或外部可以访问的Service。
举个例子,你跑了个Nginx在Pod里,端口是80,但集群外面的人根本访问不到,因为Pod的IP是虚拟的、不稳定的。这时候你用kubectl expose deployment nginx --port=80 --target-port=80,就创建了一个Service,别人就能通过Service的IP或者域名访问到你的Nginx了。
简单说,create是"造东西",expose是"把东西亮出来让别人看见"。


(5)logs------查看日志
排错最常用的命令之一,没有它你基本没法干活。
用法很简单:kubectl logs pod-name,就能看到Pod里容器的标准输出日志。
如果Pod里有多个容器,得指定容器名:kubectl logs pod-name -c container-name。
常用的几个参数:
-f:持续跟踪日志输出,跟tail -f一样。
--tail=100:只看最后100行日志。
--since=1h:只看过去一小时的日志。
这个命令的本质就是:你去翻容器的stdout和stderr打印的东西。所以写程序的时候一定记得把关键信息打印到标准输出,不然Kubernetes没法帮你收集。

(6)attach------连接到运行中的容器
这个命令是让你"钻进"一个正在运行的容器里,连接到它的主进程上。
比如你跑了个程序,它有个交互式命令行(像Python的REPL),用kubectl attach能直接连上去跟它交互。
但说实话,这命令日常用得很少。因为大多数业务程序不是交互式的,你需要的是"进容器里看看"或者"在容器里执行命令",而不是"连到主进程"。所以下面这个命令才是你真正需要的。

(7)exec------在容器里执行命令
它让你在Pod的容器里执行任意命令。
最常用的就是:kubectl exec -it pod-name -- /bin/bash,意思是"给我一个交互式的bash shell,让我进这个容器里操作"。
一旦进去了,你就可以用linux那套东西:ls看看文件、ps看看进程、cat看看配置文件、curl调调本地服务。这比看日志来得更直接。
如果Pod里有多个容器,同样得加-c container-name指定容器。
记住:logs是"从外面看程序打印了什么",exec是"进到里面亲自看看",两者配合使用,什么问题都能查出来。

(8)cp------复制文件
这个命令用来在容器和宿主机之间拷文件。
两个方向:
从宿主机拷到容器:kubectl cp /本地文件路径 pod-name:/容器里的路径
从容器拷到宿主机:kubectl cp pod-name:/容器里的文件路径 /本地路径
这玩意儿在出问题的时候特别管用。比如你的程序出core了,产生了coredump文件在容器里,容器一重启文件就没了。这时候赶紧kubectl cp把文件拷出来,然后慢慢分析。
或者你的Pod里缺了个配置文件,暂时不想重新打镜像,也可以临时拷一个进去。

(9)rollout------版本变更管理
这个命令专门管Deployment、StatefulSet这些控制器的版本发布过程。
具体命令有这几个:
kubectl rollout status deployment/nginx:查看发布状态,看新版本是正在滚动更新还是已经完成了。
kubectl rollout history deployment/nginx:查看发布历史,看看你之前发布过哪些版本。
kubectl rollout undo deployment/nginx:回退到上一个版本,这就是传说中的"后悔药"。
kubectl rollout undo deployment/nginx --to-revision=2:回退到指定的某个历史版本。
这个命令是做版本发布和回退的核心工具,跟Deployment控制器配合使用,能轻松实现无停机更新。


(10)scale------扩缩容
这个命令用来调整Pod的副本数量。
用法:kubectl scale deployment/nginx --replicas=5,意思是把这个应用的Pod数量从现在的数调整到5个。
比如双11流量来了,你敲个命令把副本数从3扩到10;流量过去了再缩回来。这个操作配合HPA(Horizontal Pod Autoscaler)自动扩缩容之前,手动操作全靠它。
注意,scale只改数量,不改版本。也就是说,扩容只是加机器跑老代码,不会动程序本身。

(11)label------打标签
标签是Kubernetes里最核心的组织方式。Pod、节点、各种资源都可以打标签,然后通过标签来筛选和操作。
kubectl label pod my-pod app=frontend:给这个Pod打个标签,标明它是前端应用。
更常用的场景是:
给节点打标签:kubectl label node node-1 disktype=ssd,然后你的Pod就可以用nodeSelector指定只跑在有ssd标签的节点上。
筛选资源:kubectl get pods -l app=frontend,只查看带有app=frontend标签的Pod。
label本身只是打了个标记,真正起作用的是Kubernetes的各种控制器和调度器会"读"这些标记来决定行为。所以可以把label理解成"给资源贴便签",方便后续的管理和筛选。

6.deployment控制器
在Kubernetes里,你绝对不要直接创建一个"裸Pod"。什么叫裸Pod?就是没有老板管的打工仔,今天在这台机器上,明天挂了没人理。你得给他配个包工头,这个包工头就叫Deployment。
怎么雇这个包工头呢?两种方式:
命令式:kubectl create deployment web --image=nginx:1.18。这一下子,包工头就到位了,他立马给你拉起来一个Pod。
声明式:写个YAML,里面kind: Deployment,然后kubectl apply -f。这是正规军的玩法。
(1)建立控制器


(2)更新业务版本
包工头干的最牛的一件事叫滚动更新 。假设你现在的应用版本是1.18,想升级到1.19。你只要告诉包工头:"给我把镜像换成1.19。"包工头不会傻乎乎地把所有旧Pod全停了再起新的------那服务就断了。他的骚操作是:先偷偷起一个新Pod(1.19),等这个新Pod确定能跑了,再干掉一个旧Pod(1.18)。如此循环,始终保持在线数量不变。整个过程用户几乎无感知。对应的命令就是kubectl set image deployment/web nginx=nginx:1.19。


(3)版本回退
万一新版本有坑,出了Bug,你也别慌。包工头手里有个"后悔药",叫版本回退 。你只需要敲kubectl rollout undo deployment/web,他立马按照之前的步骤反过来操作,把新Pod一个个换成旧Pod。这就跟游戏存档读档一样,任何时候都能回到上一个稳定版本。


7.怎么写pod的YAML文件
(1)多容器模式
什么时候你会往一个Pod里放多个容器?最典型的场景是主容器+边车容器。主容器跑你的业务代码,边车容器在旁边干杂活,比如收集日志、拉取配置文件、做本地代理。
为什么非要塞一起?因为这哥俩得共享IP地址和硬盘空间。塞一起了,他俩用localhost就能互相访问,就像在同一个屋里喊一嗓子就能听见。你如果分开成两个Pod,那就得通过网络通信,复杂得多。
YAML里怎么写?在spec.containers下面写两个- name就行。第一个是主容器,第二个是辅助的。Kubernetes会把他俩同时启动,同时销毁。

(2)把pod端口暴露到主机(hostport)
这个字段的意思是:把Pod里容器的端口,映射到Pod所在那台物理机(或者虚拟机)的端口上。
比如你的容器里跑的是Nginx,监听80端口。你设置了hostPort: 8080,那么外部的人就能通过"这台机器的IP+8080端口"访问到你的Nginx。
但这里得说一句实在话:生产环境很少这么干。因为Pod是飘忽不定的,今天在这台机器,明天挂了重启就到那台机器了。你把端口绑死在一台机器上,灵活性就没了。生产环境暴露服务用的是Service,那是另一套东西。所以你只需要知道有这回事就行,调试的时候偶尔用用。


(3)给Pod传环境变量(env)
这个好理解。你的程序可能需要连接数据库,数据库的地址是mysql.company.com。你不想把这个地址硬编码在镜像里,因为测试环境和生产环境地址不一样。
那就在YAML里写env,传个键值对进去。比如- name: DB_HOST,value: "mysql.company.com"。程序启动的时候读环境变量DB_HOST就能拿到这个值。
这东西说白了就是给容器塞进去几个全局变量。


#在浏览器中访问 node下面看到的主机ip
(4)挑一台机器来运行(nodeSelector)
Kubernetes集群里有很多台物理机,每台机器的配置可能不一样。有的机器装了SSD硬盘,有的没装;有的机器是GPU机器,专门跑AI。
你可以在Pod的YAML里写nodeSelector: disktype: ssd,意思是:调度器你给我听好了,必须把这个Pod安排到有SSD硬盘的机器上去,别的地儿我不去。
这是最基础的节点选择方式,简单粗暴。



(5)让Pod直接用主机的网络(hostNetwork)
这个比较特殊。正常情况下,Pod有自己的IP地址,是Kubernetes内部的一个虚拟IP。但你设置了hostNetwork: true之后,Pod就不再拥有自己的IP了,而是直接使用宿主机的IP和网络端口。
这就意味着,如果你跑个Nginx监听80端口,它就真的占用了这台宿主机的80端口。这台机器上再也跑不了第二个监听80端口的服务了。
这玩意儿一般什么时候用?通常是那些需要跟宿主机网络紧密耦合的组件,比如某些网络插件或者监控组件。普通业务应用千万别随便开这个开关,开了就绑死了,调度不灵活,端口还容易冲突。


6. 资源优先级(QoS Class)------系统怎么给你"排座位"
这是Kubernetes里一个很重要的机制,但很多人不太在意。你得理解,你跑的Pod在系统眼里是有三六九等的。
系统怎么给你划分等级?就看两个参数:requests(我要多少资源)和limits(我最多用多少资源)。
Guaranteed(保障型) :期望值和最大使用限制相同,优先级最高 这是最高等级,VIP。条件是你Pod里每个容器 的requests和limits都必须设置,而且数值必须相等。比如CPU都是1核,内存都是1G。这种Pod,系统把它当亲儿子,任何情况下都不驱逐它,除非它自己挂了。


Burstable(弹性型) :中等等级。设定了资源限制,但是期望值和限制值不同,资源使用优先级次之 条件是你设置了requests,但limits比requests大,或者limits没设。比如你要了0.5核,但最多能用2核。平时资源充足的时候随便你用,万一系统资源紧张了,系统会优先干掉BestEffort,再不行才会考虑干掉你。


BestEffort(尽力型) :没有做任何资源限制,资源使用优先级最低 最低等级,没人管的孩子。条件是你压根没设置requests和limits。系统就当你是乞丐,有什么剩饭给你吃一口,没得吃随时把你踢出去。


7. 容器挂了要不要重启(restartPolicy)
这个策略管的是:容器进程退出之后,系统该怎么处理。
Always:这是默认值。只要容器退出,不管是因为报错退出还是正常退出,统统给我重启。适合Web服务这种需要7x24小时在线的。


OnFailure:只有容器以非0状态码退出(也就是报错退出)时才重启。如果容器正常执行完毕,状态码是0,就不重启。这适合Job任务,比如跑个批处理脚本,跑完就拉倒。


Never:死活不重启。无论因为什么原因退出,都不重启。这也适合一次性任务,你执行一次就完事,不想留后患。


8.pod生命周期
(1)Init容器(初始化容器)
这是在主容器启动之前,必须先跑完的特殊容器。
打个比方,你的主程序是个餐厅,但开始营业之前,你得先让服务员把桌子擦干净、把食材备好。这些准备工作就由Init容器来完成。
Init容器有几个特点:
它是串行执行的,如果一个Pod里有多个Init容器,必须一个一个来,前一个成功了,后一个才开始。
它必须成功,如果某个Init容器执行失败了,Kubernetes会一直重试,直到成功为止,否则主容器永远不会启动。
常见的用法是:在Init容器里检查数据库是不是已经准备好了,或者从Git仓库拉取一些配置数据到共享目录里,供主容器使用。


(2)探针(Probe)------系统的"医生"
Pod启动之后,Kubernetes怎么知道你的程序是死是活?靠的就是探针。这相当于系统给你安排了一个私人医生,定期给你做体检。
体检有两种:
livenessProbe(存活探针) :检查你是不是还"活着"。如果这个检查失败了,系统会认为你的程序已经死锁或者崩溃了,然后直接把容器杀掉,再重新启动一个。这叫自动恢复。
没有存活探针时



有存活探针时


readinessProbe(就绪探针):检查你是不是"准备好了"接待请求。如果这个检查失败了,系统不会杀你,但会把你的Pod从服务的负载均衡列表里踢出去,不再给你分发流量。直到你检查通过了,才重新给你流量。
没有就绪探针时





有就绪探针时



为什么要有两个?因为一个程序启动可能要加载大量数据,比如加载缓存需要30秒。如果这时候用存活探针检查,发现你没响应,可能会把你杀掉重启------那就永远起不来了。所以正确的做法是:存活探针的检查要宽松一点,就绪探针的检查要精确一点。
探针怎么检查呢?有三种方式:
httpGet :向容器里的某个HTTP接口发GET请求,返回200-399就认为健康。比如你的应用有个/health接口。
tcpSocket:尝试连接容器里的某个TCP端口,能连上就认为健康。
exec:在容器里执行一条命令,返回0就认为健康。
配置里还有几个关键参数:initialDelaySeconds(启动后等几秒再开始检查,避免程序还没起来就误报)、periodSeconds(每隔几秒检查一次)、failureThreshold(连续失败几次才算真正失败)。