目录
一、简介
1、什么是pod
Pod是可以创建和管理Kubernetes计算的最小可部署单元,一个Pod代表着集群中运行的一个进程,每个pod都有一个唯一的ip。
一个pod类似一个豌豆英,包含一个或多个容器 (通常是docker) ,多个容器间共享IPC、Network和UTC namespace。
2、为什么要有pod
k8s作为集群调度需要解决的一些问题
1、容器建超亲密关系,容器建共享问题如何解决,互通的问题
2、数据间共享问题
每个容器都会有自己的namespace,容器是通过namespace来做的隔离,就是说容器间网络是不通的,有些容器之间我们希望是像localhost这样超亲密容器之间如何互访?
容器中放的就是我们的业务,假设有两个容器,他们的关系非常的亲密,他们之间的通信非常的频繁,而且还需要共享数据,这样就需要pod,在k8s中pod就解决了上述的两个问题,打通了这两个超亲密容器之间的通信和数据共享的问题,pod就给他提供了这样的环境,在同一个pod内的容器共享一个网络栈,pod还可以为pod内的容器提供一个相对持久化的存储也就是卷,不删除pod存储就是在的(pod是怎么来的在我们下载额镜像中reg.westos.org/k8s/pause是汇编语言写的,很小,大概几百kb,永远处于一种暂停的方式,会通过镜像为我们提供一个k8s的环境,这是谷歌为我们准备好的,他的设计理念就是为了解决打通容器间的网络和存储,提供一个相对持久化的存储)
二、pod的分类
Kubernetes 集群中的
Pod 主要有两种用法
:
运行单个容器的 Pod
。”每个 Pod 一个容器” 模型是最常见的 Kubernetes 用例; 在这种情况下,可以将 Pod 看作单个容器的包装器,并且 Kubernetes 直接管理 Pod,而不是容器。
运行多个协同工作的容器的 Pod
。 Pod 可能封装由多个紧密耦合且需要共享资源的共处容器组成的应用程序。 这些位于同一位置的容器可能形成单个内聚的服务单元 —— 一个容器将文件从共享卷提供给公众, 而另一个单独的 “边车”(sidecar)容器则刷新或更新这些文件。 Pod 将这些容器和存储资源打包为一个可管理的实体。
0、pod常用命令命令
kubectl get pod ##获取pod
kubectl get ns ##查看namespace
kubectl get pod -n kube-system ##查看相应namespace对应的pod,不加namespace就自动选择默认的namespace
kubectl describe pod xxx ##这条命令来查看pod详细信息
kubectl run -h ##查看创建pod帮助
kubectl get pod -o wide ##查看节点的具体信息
kubectl delete pod demo ##删除pod
1、准备镜像
下载测试镜像
[root@k8s1 docker]# docker pull yakexi007/myapp:v1
[root@k8s1 docker]# docker pull yakexi007/myapp:v2
修改名称
[root@k8s1 docker]# docker tag yakexi007/myapp:v1 reg.westos.org/library/myapp:v1
[root@k8s1 docker]# docker tag yakexi007/myapp:v2 reg.westos.org/library/myapp:v2
上传harbor仓库
[root@k8s1 docker]# docker push reg.westos.org/library/myapp:v1
[root@k8s1 docker]# docker push reg.westos.org/library/myapp:v2
2、自主式pod
(生产不推荐,没有控制器维护的pod就是自主式pod,自主式pod没有自愈性)
[root@k8s2 ~]# kubectl run demo –image=myapp:v1
[root@k8s2 ~]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
demo 1/1 Running 0 35s 10.244.2.2 k8s4 <none> <none>
查看pod详情
[root@k8s2 ~]# kubectl describe pod demo
Name: demo
Namespace: default
Priority: 0
Node: k8s4/192.168.56.174
Start Time: Mon, 09 Jan 2023 15:40:49 +0800
Labels: run=demo
Annotations: <none>
Status: Running
IP: 10.244.2.2
IPs:
IP: 10.244.2.2
…
删除pod
[root@k8s2 ~]# kubectl delete pod demo
3、控制器创建pod
这里我们先使用deployment控制器,控制器内容会在下一章节讲解
创建
kubectl create deployment myapp –image=myapp:v1
拉伸pod的副本数
kubectl scale deployment myapp –replicas 3
创建时直接设定好副本数:
kubectl create deployment myapp –image=myapp:v1 –replicas=3
控制器会自动维护pod副本数
[root@k8s2 ~]# kubectl get pod
NAME READY STATUS RESTARTS AGE
myapp-678fcbc488-gp6pr 1/1 Running 0 3s
myapp-678fcbc488-gqdgk 1/1 Running 0 3s
myapp-678fcbc488-qqkzx 1/1 Running 0 3s
获取当前
deployment控制器,有3个pod
[root@k8s2 ~]# kubectl get deployments.apps
NAME READY UP-TO-DATE AVAILABLE AGE
myapp 3/3 3 3 16s
删除其中一个pod,控制器会自动创建一个新的,只要不删除控制器,他会自动维护pod
[root@k8s2 ~]# kubectl delete pod myapp-678fcbc488-gp6pr
pod “myapp-678fcbc488-gp6pr” deleted
删除后查看还是3个副本数
在远程pod中执行命令
[root@k8s2 ~]# kubectl exec myapp-fcfbd4477-b2hkm — ls /usr/share/nginx/html
[root@k8s2 ~]# kubectl exec myapp-fcfbd4477-b2hkm — ls /usr/share/nginx/html
50x.html
index.html
4、扩容pod数量
扩容
[root@k8s2 ~]# kubectl scale deployment myapp –replicas=6
[root@k8s2 ~]# kubectl get pod
NAME READY STATUS RESTARTS AGE
myapp-678fcbc488-gqdgk 1/1 Running 0 11m
myapp-678fcbc488-jb5dw 1/1 Running 0 7s
myapp-678fcbc488-qqkzx 1/1 Running 0 11m
myapp-678fcbc488-tm55k 1/1 Running 0 7s
myapp-678fcbc488-v7ftf 1/1 Running 0 11m
myapp-678fcbc488-wrjt7 1/1 Running 0 7s
缩容
[root@k8s2 ~]# kubectl scale deployment myapp –replicas=3
deployment.apps/myapp scaled
[root@k8s2 ~]# kubectl get pod
NAME READY STATUS RESTARTS AGE
myapp-678fcbc488-gqdgk 1/1 Running 0 12m
myapp-678fcbc488-qqkzx 1/1 Running 0 12m
myapp-678fcbc488-v7ftf 1/1 Running 0 11m
5、通过service暴露pod(负载均衡,自动发起)
指定暴露端口和目标端口,目标端口相当于做了一个映射
[root@k8s2 ~]# kubectl expose deployment myapp –port=80 –target-port=80
查看svc详情
describe查看命令详情
有3个Endpoints,这个就是三个pod的ip地址
[root@k8s2 ~]# kubectl describe svc myapp
Name: myapp
Namespace: default
Labels: app=myapp
Annotations: <none>
Selector: app=myapp
Type: ClusterIP
IP Family Policy: SingleStack
IP Families: IPv4
IP: 10.106.225.101
IPs: 10.106.225.101
Port: <unset> 80/TCP
TargetPort: 80/TCP
Endpoints: 10.244.1.2:80,10.244.1.3:80,10.244.2.4:80
Session Affinity: None
Events: <none>
访问时默认是iptable的负载均衡算法,不是特别的均匀但是会提供一个健康的
[root@k8s2 ~]# curl 10.106.225.101
Hello MyApp | Version: v1 | <a href=”hostname.html”>Pod Name</a>
[root@k8s2 ~]# curl 10.106.225.101/hostname.html
myapp-678fcbc488-v7ftf
service自动发现pod扩容与缩容,自动更新endpoints,实现对应用的负载均衡
service默认使用clusterip类型,只能在集群中访问
nodeport类型,可以在集群外部访问
[root@k8s2 ~]# kubectl edit svc myapp
[root@k8s2 ~]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 120m
myapp
NodePort
10.106.225.101 <none> 80:
30280
/TCP 23m
6、更新应用版本
更新镜像版本
[root@k8s2 ~]# kubectl set image deployment/myapp myapp=myapp:v2
[root@k8s1 docker]# curl 192.168.18.14:30840
查看应用历史版本
[root@k8s2 ~]# kubectl rollout history deployment myapp
回滚
[root@k8s2 ~]# kubectl rollout undo deployment myapp –to-revision=1
[root@k8s2 ~]# curl 192.168.18.14:30840
删除应用
[root@k8s2 ~]# kubectl delete deployments.apps myapp
[root@k8s2 ~]# kubectl delete svc myapp
[root@k8s2 ~]# kubectl get pod
以上操作我们都是在默认的namespace中进行,在k8s中通过namespace进行逻辑隔离,只要不指定就是在default中
[root@k8s2 ~]# kubectl get ns
三、编写yaml文件
在实际中我们更常用yaml文件的方式创建pod
资源清单
获取帮助
[root@k8s2 pod]# kubectl explain pod.spec.containers
获取yaml模板
[root@k8s2 pod]# kubectl run demo –image nginx –dry-run=client -o yaml > pod.yaml
编写yaml文件
[root@k8s2 pod]# vim pod.yaml
apiVersion: v1
kind: Pod
metadata:
labels:
run: demo
name: demo
spec:
containers:
– image: nginx
name: demo
imagePullPolicy: IfNotPresent
imagePullPolicy: IfNotPresent
镜像拉取策略,有3种,always总是拉取、Never不拉取、IfNotPresent如果本地有就不拉取镜像,1.25版本默认就是 IfNotPresent
创建pod
[root@k8s2 pod]# kubectl create -f pod.yaml
查看详情
[root@k8s2 pod]# kubectl get pod -o wide
[root@k8s2 pod]# kubectl describe pod demo
[root@k8s2 pod]# kubectl get pod demo -o yaml
示例
[root@k8s2 pod]# vim pod.yml
apiVersion: v1
kind: Pod
metadata:
labels:
run: demo
name: demo
spec:
hostNetwork: true
nodeSelector: #nodeSelector调动策略,是以标签的形式写的 kubectl getnode --show-labels
kubernetes.io/hostname: k8s3
containers:
- image: myapp:v1
name: demo
imagePullPolicy: IfNotPresent
#ports: #端口映射
#- name: http
# containerPort: 80
# hostPort: 80
#resources: #资源限制
# limits:
# cpu: 1
# memory: 200Mi
# requests:
# cpu: 0.5
# memory: 100Mi
#- name: demo2 #定义多个容器
# image: busyboxplus
# command: ["/bin/sh", "-c", "sleep 3600"]
资源敏感型业务:
resources
这里会有一个资源敏感型业务,最小requests和最大上限limits是一样的,并且cpu和内存都需要设置。如果不是资源敏感型业务,在这个pod上如果资源不够时会把一些资源要求比较宽容的pod踢出去,从而保证资源敏感型pod正常运行。(Mi是1024换算,M是1000换算),在k8s中QoS Class不可以直接赋值,必须要通过resources来赋值
k8s根据Pod中Containers Resource的request和limit的值来定义Pod的QoS Class。其中,指定容器request,代表系统确保能够提供的资源下限值。指定容器limit,代表系统允许提供的资源上限值。
Pods需要保证长期稳定运行需要设定“确保运行的最少资源”,然而pod能够使用的资源经常是不能确保的。
通常,Kubernetes通过设置request和limit的值来指定超卖比例,进而提升资源利用率。K8S的调度基于request,而不是limit。Borg通过使用“non-guranteed”的资源,提升了20%的资源利用率。
在一个资源被“超卖”的系统(总limits > machine capacity),容器在资源被耗尽的情况下会被kill。理想情况是那些“不重要”的容器先被kill。
对于每一种Resource都可以将容器分为3中QoS Classes:Guaranteed敏感型, Burstable次敏感型, and Best-Effort宽容型,它们的QoS级别依次递减。K8S底层实际上是通过 limit和request值来实现不同等级QoS的划分。
Guaranteed 如果Pod中所有Container的所有Resource的limit和request都相等且不为0,则这个Pod的QoS Class就是Guaranteed。
注意,如果一个容器只指明了limit,而未指明request,则表明request的值等于limit的值。
Best-Effort 如果Pod中所有容器的所有Resource的request和limit都没有赋值,则这个Pod的QoS Class就是Best-Effort
Burstable 除了符合Guaranteed和Best-Effort的场景,其他场景的Pod QoS Class都属于Burstable。 当limit值未指定时,其有效值其实是对应Node Resource的Capacity
[root@k8s2 pod]# kubectl apply -f
pod.yml
删除
[root@k8s2 pod]# kubectl delete -f pod.yml
四、Pod生命周期
1、init容器
为什么要用初始化容器,Init 容器能做什么?
Init 容器可以包含一些安装过程中应用容器中不存在的实用工具或个性化代码。Init 容器可以安全地运行这些工具,避免这些工具导致应用镜像的安全性降低。
应用镜像的创建者和部署者可以各自独立工作,而没有必要联合构建一个单独的应用镜像。
Init 容器能以不同于Pod内应用容器的文件系统视图运行。因此,Init容器可具有访问 Secrets 的权限,而应用容器不能够访问。由于Init 容器必须在应用容器启动之前运行完成,因此 lnit 容器提供了一种机制来阻塞或延迟应用容器的启动,直到满足了一组先决条件。一旦前置条件满足,Pod内的所有的应用容器会并行启动。
案例:需要service解析,没有解析init容器启动失败,主容器不会运行,
初始化容器运行成功后主容器才能运行
[root@k8s2 pod]# vim init-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: myapp-pod
labels:
app.kubernetes.io/name: MyApp
spec:
containers:
– name: myapp-container
image: busybox
command: [‘sh’, ‘-c’, ‘echo The app is running! && sleep 3600’]
initContainers:
– name: init-myservice
image: busybox
command: [‘sh’, ‘-c’, “until nslookup myservice.default.svc.cluster.local; do echo waiting for myservice; sleep 2; done”]
[root@k8s2 pod]# kubectl apply -f init-pod.yaml
[root@k8s2 pod]# kubectl get pod
NAME READY STATUS RESTARTS AGE
myapp-pod 0/1 Init:0/1 0 41s
在init容器没有成功运行之前,主容器不会被运行
添加svc定义
[root@k8s2 pod]# vim myservice.yaml
—
apiVersion: v1
kind: Service
metadata:
name: myservice
spec:
ports:
– protocol: TCP
port: 80
targetPort: 80
[root@k8s2 pod]# kubectl apply -f myservice.yaml
[root@k8s2 pod]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 3d1h
myservice ClusterIP 10.103.221.131 <none> 80/TCP 2s
svc解析成功后,init容器退出,主容器运行
[root@k8s2 pod]# yum install -y bind-utils
[root@k8s2 pod]# dig -t A myservice.default.svc.cluster.local. @10.96.0.10
[root@k8s2 pod]# kubectl get pod
NAME READY STATUS RESTARTS AGE
myapp-pod 1/1 Running 0 3m35s
回收资源
[root@k8s2 pod]# kubectl delete -f
init-pod.yml
[root@k8s2 pod]# kubectl delete -f myservice.yml
2、探针
共三种探针:分别是存活探针,就绪探针,启动探针,前两个使用居多。
1、存活探针
[root@k8s2 pod]# vim liveness-pod.yaml
apiVersion: v1
kind: Pod
metadata:
labels:
test: liveness
name: liveness-http
spec:
containers:
- name: liveness
image: nginx
livenessProbe:
tcpSocket:
port: 8080
initialDelaySeconds: 3 #容器启动多少秒后开始检测
periodSeconds: 3 #检测频率,间隔
timeoutSeconds: 1 #超时时间
[root@k8s2 pod]# kubectl apply -f liveness-pod.yaml
在存活探针检测失败导致容器不断被重启
[root@k8s2 pod]# kubectl get pod -w
NAME READY STATUS RESTARTS AGE
liveness-http 1/1 Running 0 5s
liveness-http 1/1 Running 1 (0s ago) 13s
liveness-http 1/1 Running 2 (0s ago) 22s[root@k8s2 pod]# kubectl describe pod liveness-http
释放资源
[root@k8s2 pod]# kubectl delete -f liveness-pod.yaml
2、就绪探针
[root@k8s2 pod]# vim liveness-pod.yaml
apiVersion: v1
kind: Pod
metadata:
labels:
test: liveness
name: liveness-http
spec:
containers:
- name: liveness
image: nginx
livenessProbe:
tcpSocket:
port: 80
initialDelaySeconds: 3
periodSeconds: 3
readinessProbe:
httpGet:
path: /test.html
port: 80
initialDelaySeconds: 5
periodSeconds: 5
[root@k8s2 pod]# kubectl apply -f liveness-pod.yaml
就绪探针失败导致容器一直未就绪
[root@k8s2 pod]# kubectl get pod
NAME READY STATUS RESTARTS AGE
liveness-http 0/1 Running 0 34s
查看详情
[root@k8s2 pod]# kubectl describe pod liveness-http
创建测试页面
[root@k8s2 pod]# kubectl exec liveness-http — touch /usr/share/nginx/html/test.html就绪探针成功
[root@k8s2 pod]# kubectl get pod
NAME READY STATUS RESTARTS AGE
liveness-http 1/1 Running 0 100s
创建svc
[root@k8s2 pod]# kubectl expose pod liveness-http –port 80 –target-port 80
svc通过就绪探针判断这个应用是否正常,能否上线
就绪容器自动上线
[root@k8s2 pod]# kubectl describe svc liveness-http
Name: liveness-http
Namespace: default
Labels: test=liveness
Annotations: <none>
Selector: test=liveness
Type: ClusterIP
IP Family Policy: SingleStack
IP Families: IPv4
IP: 10.108.21.178
IPs: 10.108.21.178
Port: <unset> 80/TCP
TargetPort: 80/TCP
Endpoints:
10.244.1.33:80
Session Affinity: None
Events: <none>
删除测试页面
[root@k8s2 pod]# kubectl exec liveness-http — rm /usr/share/nginx/html/test.html
就绪探针失败,容器未就绪,svc发现不了服务,就没有Endpoints
[root@k8s2 pod]# kubectl get pod
NAME READY STATUS RESTARTS AGE
liveness-http 0/1 Running 0 6m15s
在svc中容器自动下线
[root@k8s2 pod]# kubectl describe svc liveness-http
Name: liveness-http
Namespace: default
Labels: test=liveness
Annotations: <none>
Selector: test=liveness
Type: ClusterIP
IP Family Policy: SingleStack
IP Families: IPv4
IP: 10.108.21.178
IPs: 10.108.21.178
Port: <unset> 80/TCP
TargetPort: 80/TCP
Endpoints:
Session Affinity: None
Events: <none>
回收
[root@k8s2 pod]# kubectl delete -f liveness-pod.yaml
总结:
存活探针如果启动失败,他会不断的重启这个容器,让他达到一个自愈的功能,就绪探针和
svc自动发现相关联,就绪我们就可以认为这个业务可以正常对外发布,正常才让他出现在svc的负载均衡列表中,就相当于健康检测,这是k8s对我们应用上线和下线的一个优雅操作。