02-K8S之自主式Pod

创建pod及deployment、service

kubectl run nginx-deploy --image=nginx:1.20-alpine --port=80 --dry-run=true
kubectl run nginx-deploy --image=nginx:1.20-alpine --port=80 # 创建静态pod
kubectl run nginx-deploy --image=nginx:1.20-alpine --port=80 --replicas=1 # 创建deployment副本,运行此命令,会从远程拉取镜像,创建pod,并运行pod。这个pod受刚创建的deployment控制器管理。 
    
kubectl expose deployment nginx-deploy --name=nginx --port=80 --target-port=80 --protocol=TCP # 将某个控制器所管理的一组pod暴露出去,能够通过service向pod代理
kubectl expose pod  nginx-deploy --name=nginx --port=80 --target-port=80 --protocol=TCP # 将某个静态pod暴露出去,
 kubectl get svc
 '''
 nginx        ClusterIP   10.104.76.181   <none>        80/TCP    8s
 '''
curl 10.104.76.181
'''
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
    body {
        width: 35em;
        margin: 0 auto;
        font-family: Tahoma, Verdana, Arial, sans-serif;
    }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>
'''

kubectl run client -it --image=busybox --restart=Never # 图1
	ping 10.244.2.4
    cat /etc/resolv.conf
    '''
	nameserver 10.96.0.10
	search default.svc.cluster.local svc.cluster.local cluster.local com
	options ndots:5
    '''
    while true;do wget -O - -q http://nginx:80;done
# 在master节点上进行域名解析
dig -t A nginx.default.svc.cluster.local. @10.96.0.10  # nginx是service名称,default是名称空间,svc.cluster.local.是pod名称,是由核心组件coreDNS服务解析。图2

图1:

图2:

扩缩容(scale)、滚动更新(升级)及回滚

kubectl run myapp --image=ikubernetes/myapp:v1 --replicas=2
kubectl get deployment -w # -w watch 动态监视
kubectl expose deploment myapp --name=myapp

# 1. 扩缩容
kubectl scale --replicas=5 deployment myapp --port=80  # 扩容
kubectl scale --replicas=3 deployment myapp --port=80  # 缩容

# kubectl set image 
kubectl set image (-f FILENAME | TYPE NAME) CONTAINER_NAME_1=CONTAINER_IMAGE_1 ... CONTAINER_NAME_N=CONTAINER_IMAGE_N
[options]

# 2. 滚动更新(升级)
# 类似于金丝雀发布(或者叫灰度发布,但是粒度没有那么细) 
kubectl set image deployment myapp myapp=ikubernetes/myapp:v2 # 更改镜像指定是更改哪个控制器下面的pod的镜像,这里控制器名是myapp, 容器名字也叫myapp,后面对应的是镜像 。首先要从远程仓库下载镜像
kubectl rollout status deployment myapp # 查看滚动更新deployment副本的状态

kubectl get pods # 下图1
kubectl describe po myapp-74c9dcb8c-642fm # 查看新版本的pod镜像已经更新到ikubernetes/myapp:v2

# 3. 回滚
kubectl rollout undo (TYPE NAME | TYPE/NAME) [flags] [options]
kubectl rollout undo  deployment myapp # 不指定回滚的镜像版本,默认回滚到上一个版本 图3

图1:

图2:

图3:

创建service的时候就会在iptables表中生成转发规则(如果没有ipvs,就会降级到iptables)
iptables -nvL -t nat

集群外部访问service代理的pod

[root@master01 yum.repos.d]# kubectl get svc
'''
NAME         TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE
kubernetes   ClusterIP   10.96.0.1       <none>        443/TCP   7h1m
nginx        ClusterIP   10.104.76.181   <none>        80/TCP    104m
'''
kubectl edit service nginx # 下图1
[root@master01 yum.repos.d]# kubectl get svc
'''
NAME         TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
kubernetes   ClusterIP   10.96.0.1       <none>        443/TCP        7h4m
nginx        NodePort    10.104.76.181   <none>        80:32444/TCP   108m
'''

curl 192.168.1.130:32444 # 

图1:

图2:

kubernetes资源清单

RESTful:
    GET PUT DELETE POST、...
    kubectl run,get,edit,...
资源:对象
	workload: Pod,ReplicaSet,Deployment,StatefulSet,DaemonSet,Job,CronJob,...
	服务发现及均衡: Service,Ingress,...
    配置与存储: Volume,CSI
    	ConfigMap,Secret
        DownwardAPI:向容器中注入信息
	集群级资源;
    	Namespace,Node,Role,ClusterRole,RoleBinding,ClusterRoleBinding
	元数据型资源
    	HPA,PodTemplate,LimitRange

kubectl get pods
kubectl get po nginx-deploy -o yaml # 将pod信息输出为yaml格式 
kubectl get po nginx-deploy -o yaml >nginx-deploy.yaml 

apiVersion: v1 # apiversion 的组成是 group/version 如果省略group,表示core,是核心组。
spec: # specification:规格,我们创建的资源对象应该具有什么样的特性,或者满足什么样的规范。用户定义的
status: # 当前资源的当前状态,如果当前状态和目标状态不一样,以目标状态为准,因为目标状态是用户期望的状态;当某一个k8s资源定义好之后,该资源的当前状态应该是无限靠近目标状态。只读的,系统维护

创建资源的方法

apiserver仅接收JSON格式的资源定义;命令行run 后跟的参数只不过被准换为json格式;
yaml格式提供配置清单,apiserver可自动将其转为json格式,而后再提交;

大部分资源的配置清单:
	apiVersion: # group/version 
		kubectl api-Versions
		pod属于最核心资源,所以属于核心群组v1;控制器deployment、replicaSet属于应用程序管理的资源对象,它们属于apps/v1群组
	kind:资源类别,都是内建的或自定义的(按照固定的语法格式进行定义),不能随意定义;
	metadata:元数据
		name:当前对象创建,实例化出来的当前类别的资源名称;
		namespace:对象所属名称空间,是kuernates级别的概念,与NET、IPC、UTS不同。。。
		labels:每一种类型资源的标签
		annotations:资源注解
		selflink:
			每个资源引用的PATH
				/api/GROUP/VERSION/namespaces/NAMESPACE/TYPE/NAME
	spec:期望的状态,disired state
	status:当前状态
	
# 群组中各版本区别
alpha:内测版,不公开使用
beta:公测版,非稳定,大部分功能都已经测试过并保留,但有极少部分内容有可能会改变,所以不稳定。
stable:稳定版,已有的内容不会改变,最多是在原有的版本中再添加一些内容。

k8s资源属性的类型

<string> :字符串,直接就是 key: value
<integer>:整型
<[]string> : 字符串列表 
    第一种形式:[" "," "," "]
    第二种形式:
    - " "
    - " "
    - " "
 <map[string]string> : 映射类型
    key1: value1
    key2: value2
<Object> 对象,在对象紧接一行,空两格写属性
  
<[]Object> :对象列表 在对象列表紧接一行,以"-"开头,空一格然后写属性

定义一个pod资源清单

apiVersion: v1
kind: Pod
metadata: 
 name: pod-demo
 namespace: default
 labels: # <map[string]string> 映射类型,类似于json
 	app: myapp
 	tier: frontend
spec:
  containers:
  - name: myapp
    image: nginx:1.20 # 使用镜像启动默认的命令
  - name: busybox
    image: busybox:latest 
    command: # ["/bin/sh","-c","sleep 3600"] # 这里使用自定义的命令,而没使用镜像启动默认的命令,就是为了防止默认命令启动之后会退出
    - "/bin/sh"
    - "-c"
    - "sleep 3600"

运行pod-demo:

kubectl create -f pod-demo.yaml 
kubectl get po -o wide -w
kubectl describe po pod-demo
kubectl logs pod-demo -c myapp # 查看pod-demopod内myapp容器日志,如下图2
kubectl logs pod-demo -c busybox  # 查看pod-demopod内busybox容器日志
kubectl exec -it pod-demo -c myapp -- /bin/bash
kubectl exec -it pod-demo -c busybox -- /bin/sh

kubectl delete -f pod-demo.yaml # 或者 kubectl delete pod-demo

kubectl get po -l app --show-labels # -l 显示具有app标签名称的pod,--show-labels,显示此类pod的标签列
kubectl get po -L app,run # 显示具有app和run标签名称对应值的pod 

kubectl label po pod-demo release=canary  # 给pod添加额外的标签
kubectl label po pod-demo release=stable --overwrite # 修改pod的已有标签 

kubectl label nodes worker01 disktype=ssd
kubectl get nodes -l disktype --show-labels
kubectl label nodes worker01 disktype=general-disk --overwrite
kubectl get nodes -l disktype --show-labels

图1:

图2:

自主式pod资源对象清单常用字段

资源清单格式:
	一级字段:apiVersion(group/version),kind,metadata(name,namespace,labels,annotations,...),spec,status(只读)

Pod资源:
	spec.containers <[]object>
    - name <string>
      image<string>
      imagePullPolicy <string>
      	Always,Never,IfNotPresent
	
    修改镜像中的默认应用:
    	command,agrs
	标签:
    	key=value # key、value不能超过63个字符,且key的前缀(域名)不能超过253个字符
        	key:字母、数字、_、-、. 
            value:可以为空,只能为字母数字开头及结尾,中间可使用
	
    标签选择器:
		等值关系:=,==,!=
            kubectl get pods -l release=canary
            kubectl get pods -l release,app
            kubectl get pods -l release=stable,app=myapp
            kubectl get pods -l release!=canary   

		集合关系:
            KEY in (VALUE1,VALUE2,...)
			kubectl get pods -l "release in (canary,beta,alpha)"
            KEY notin (VALUE1,VALUE2,...)
             kubectl get pods -l "release notin (canary,beta,alpha)"
	
    许多资源支持内嵌字段定义其使用的标签选择器:
		matchLabels: # 直接给定键值支持service,deployment,replicaSet
		matchExpressions: # 基于给定的表达式来定义使用标签选择器,支持deployment,replicaSet
			{key:"KEY",operator:"OPERATOR",values:[VAL1,VAL2,...]}
			操作符:
            	In,NotIn:values字段的值必须

nodeSelector <map [string]string> # 将此pod调度到节点标签为名称为disktype,值为general-disk的这一类节点上。

nodeName <string>  # 将pod调度到节点名称为某个值的某个节点上。
annotations <map[string]string>:
    	与label不同的地方在于,它不能用于挑选资源对象,仅用于为对象提供"元数据",这些元数据可能被某些程序用到,而且很重要。键值长度不受限制。

pod清单示例

apiVersion: v1
kind: Pod
metadata: 
  name: pod-demo
  namespace: default
  labels: # <map[string]string> 映射类型,类似于json
 	app: myapp
 	tier: frontend
  annotations: 
    master01/created-by: cluster admin
spec:
  containers:
  - name: myapp
    image: nginx:1.20 # 使用镜像启动默认的命令
    ports: # 仅仅只是告诉我们容器内部监听的端口,并不像docker容器动态绑定到宿主机上的某个端口。
    - name: http
      containerPort: 80
    - name: https
      containerPort: 443
  - name: busybox
    image: busybox:latest 
    imagePullPolicy: IfNotPresent
    command: # ["/bin/sh","-c","sleep 3600"] # 这里使用自定义的命令,而没使用镜像启动默认的命令,就是为了防止默认命令启动之后会退出
    - "/bin/sh"
    - "-c"
    - "sleep 3600"
  nodeSelector: 
    disktype: general-disk
  #nodeName:

k8s中command、args和docker中ENTRYPOINT、CMD的区别和联系

dockerfile中既有CMD:运行容器时,就执行CMD命令;如果既有CMD,又有EntryPoint,CMD命令将被传送给EntryPoint作为EntryPoint的参数,被执行;
k8s中资源清单spec.containers中的command、args参数的含义是:
	command(<[]string>):是一个EntryPoint数组,类似于dockerfile中的EntryPoint,但是区别在于,如果要执行shell脚本,必须要在command列表中手动指定shell参数(比如/bin/sh,/bin/bash)。
	如果没指定command,就会运行镜像中的EntryPoint,如果只指定command,镜像当中的CMD和ENTRYPOINT都没用了。
	args(<[]string>):在k8s中作为entrypoint的参数,就是command的参数;如果没有定义args,镜像中既有CMD指令,又有ENTRYPINT指令,镜像中的CMD指令的参数将作为参数传递给ENTRYPOINT后面的代码。如果这里我们指定了args 参数,镜像中CMD后的参数将不再作为参数传递,而传递agrs中所定义的内容。

官方对command、args同docker中ENTRYPOINT、CMD的区别联系的解释:

Pod生命周期

状态:
	Pending:处于挂起状态,已经创建,但是没有适合运行它的节点(调度尚未完成)。
	Running:运行状态
	Failed:
	Successed:
	Unknown:比如,apiserver无法连接pod所处节点的kubelet(加入kubelet故障),此时所处的状态就是Unknown
	
pod创建所经历的阶段:用户请求创建pod时,会将请求提交给apiserver,apiserver将请求信息保存在etcd中,apiserver接下来请求scheduler调度pod,如果成功调度到某个节点,将调度的结果信息更新到etcd资源的的状态信息当中,目标节点kubelet根据apiserver当中的状态变化,发现自己有任务要完成,kubelet会通过apiserver拿到此前用户提交创建的清单,根据此清单去启动并运行这个pod,启动、运行的状态并通过apiserver返回给etcd并保存。
	

pod生命周期中的重要行为:
1.使用初始化容器完成初始化;
2.在主容器启动后可以做启动后钩子;
3.容器探测
4.在主容器结束前可以做结束前钩子;

在主容器运行当中可以做容器探测:
	liveness_probe:探测主容器是否处于运行状态;
	rediness_probe:探测主容器中的主进程是否准备就绪并可以对外提供服务;
不论是liveness_probe还是rediness_probe,都可以支持三种方式的探测行为:
	1.执行自定义命令;exec
    2.向指定的TCP套接字发请求;
    3.向指定的http服务发请求;

容器重启策略

kubectl explain pod.spec
restartPolicy # 默认是Always
	Always:一旦容器挂了,总是重启
	OnFailure:只有当容器状态为错误时才重启,如果是正常终止不重启(比如:在容器启动时运行一个sleep 10,在睡完10s以后,容器就是正常终止,不会重启。)
	Never:不重启,即使容器挂了。 
当一个容器挂了,根据重启策略,第一次重启是立即进行的,随后可能隔10s,20s,40s,80s,160s,300s去重启。当一个pod被删除时,会进行平滑的迁移,给一个宽限期(好像是30s),先给pod发送terminate信号,之后再发送kill信号,将pod杀死。

1.livenessprobe容器探针

kubectl explain pod.spec.containers


1.livenessProbe	<Object>
	kubectl explain pod.spec.containers.livenessProbe
    exec	<Object>
    kubectl explain pod.spec.containers.livenessProbe.exec
    	command	<[]string>
        
    httpGet	<Object>
    kubectl explain pod.spec.containers.livenessProbe.httpGet
        host	<string> # 主机名,默认是pod IP.
        httpHeaders	<[]Object> # 自定义携带的请求头
        path	<string> # uri
        port	<string> -required- # 端口号或端口名称
        scheme	<string> # 协议
		      
    tcpSocket	<Object>
    kubectl explain pod.spec.containers.livenessProbe.tcpSocket
    	host	<string> # 默认是pod IP
		port	<string> -required- # 端口号,或者端口号的名称
    
    failureThreshold	<integer> # 探测几次失败之后才认为是失败的,默认是3次
    periodSeconds       <integer> # 探测周期,每隔多长时间探测一次,默认是10s
    timeoutSeconds	<integer> # 探测时多长时间没有响应,确认为超时。默认是1s
    initialDelaySeconds	<integer> # 再容器启动之后的多长时间开始探测,不指定,默认是容器一旦启动,立马进行探测。但是容器启动,不代表容器内的主进程已经正常运行起来了。
    
2.readinessProbe	<Object> # 与livenessprobe探针相似
kubectl explain pod.spec.containers.readinessProbe
	exec
    httpGet
    tcpSocket

3.lifecycle	<Object>
kubectl explain pod.spec.containers.lifecycle
    postStart	<Object>
    kubectl explain pod.spec.containers.lifecycle.postStart # 与livenessprobe探针相似
        exec	<Object>
        httpGet	<Object>
        tcpSocket	<Object>
        
    preStop	<Object>
    kubectl explain pod.spec.containers.lifecycle.preStop # 与livenessprobe探针相似
        exec	<Object>
        httpGet	<Object>
        tcpSocket	<Object> 

1.定义一个exec的容器存活性探针

vim liveness-exec-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: liveness-exec-container
  namespace: default
spec:
  containers:
  - name: liveness-exec-container
    image: busybox:latest
    imagePullPolicy: IfNotPresent
    command: ['/bin/sh','-c','touch /tmp/healthy; sleep 30; rm -rf /tmp/healthy; sleep 3600']
    livenessProbe:
      exec:
        command: ['test', '-e', '/tmp/healthy']
      initialDelaySeconds: 2
      periodSeconds: 3
      
kubectl get po -w
kubectl describe po liveness-exec-container


2.定义一个tcpSocket的容器存活性探针

tcpSocket探针,类似httpGet中的字段:
    host	<string>
    port	<string> -required-

3.定义一个httpGet的容器存活性探针

vim liveness-httpget-container.yaml
apiVersion: v1
kind: Pod
metadata:
  name: liveness-exec-container
  namespace: default
spec:
  containers:
  - name: liveness-httpget-container
    image: nginx:1.20
    ports:
    - name: http
      containerPort: 80
    imagePullPolicy: IfNotPresent
    livenessProbe:
      httpGet:
        port: http # 基于上面定义的port名称引用
        #port: 80
        path: /index.html
      initialDelaySeconds: 2
      periodSeconds: 3

进入容器liveness-httpget-container删除index.html,模拟httpget方式探针探测容器响应失败,容器的重启情况。
kubectl exec -it liveness-httpget-container -- /bin/bash
cd /usr/share/nginx/html/
rm -rf index.html # 删除容器层的index.html,其实并未删除镜像层的index.html,只是对容器层的index.html文件做了隐藏

当探测默认三次httpget探测,均为请求到index页面,将会在k8s默认的重启策略指定的时间时重启容器,这时,会将镜像层的index.html再次挂到/usr/share/nginx/html/目录内。。容器就可以正常运行了。

2.readinessprobe容器探针

什么是就绪的容器?
kubectl get po # 如下图1
其中READY列:'/' 右侧的数字表示当前pod中有几个容器,'/'左侧的数字表示有几个容器已经就绪(已经运行起来,并可对外提供服务) 

service关联一组pod,当一个pod加刚入到service时,可能容器并未初始化完成,并不能对外提供服务。这时候如果没做就绪性探针,那么service代理到刚加入的pod上就无法响应客户端的请求。

1.定义一个httpGet的容器就绪性探针

vim readiness-httpget-container.yaml 

apiVersion: v1
kind: Pod
metadata:
  name: readiness-httpget-container
  namespace: default
spec:
  containers:
  - name: readiness-httpget-container
    image: nginx:1.20
    ports:
    - name: http
      containerPort: 80
    imagePullPolicy: IfNotPresent
    readinessProbe:
      httpGet:
        port: http # 基于上面定义的port名称引用
        #port: 80
        path: /index.html
      initialDelaySeconds: 2
      periodSeconds: 3
kubectl create -f readiness-httpget-container.yaml
kubectl get po # 如下图

删除容器中的index.html页面,模拟就绪性探测失败,容器不能对外提供服务。这时service不会把流量接入到当前pod上。
kubectl exec -it readiness-httpget-container -- /bin/bash
rm -rf /usr/share/nginx/html/index.html
kubectl get po # 如下图1
kubectl describe po readiness-httpget-container # 如下图2

图1:

图2:

再向容器中添加index.html页面,容器立即处于就绪态。
kubectl exec -it readiness-httpget-container -- /bin/bash
echo readiness-httpget-container > /usr/share/nginx/html/index.html
kubectl get po # 如下图1
kubectl describe po readiness-httpget-container # 如下图2

图1:

图2:

3.lifecycle生命周期钩子 poststart

busybox镜像中自带httpd服务,但是没带主页,所以当busybox启动之后可以立马通过poststart钩子发送一个请求,如果请求失败,则终止容器,并根据重启策略重启容器,直到钩子请求正常为止

定义一个poststart钩子

vim poststart-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: poststart-pod
  namespace: default
spec:
  containers:
  - name: busybox-httpd
    image: busybox:latest
    imagePullPolicy: IfNotPresent
    lifecycle:
      postStart:
        exec:
          command: ["/bin/sh","-c","echo 'Home Page' >> /tmp/index.html"]
    # command: ["/bin/sh","-c","sleep 3600"]
    command: ["/bin/httpd"] # 容器在启动之后,执行httpd,没发现/tmp下的index.html文件失败,导致容器终止,在 启动的瞬间,同时postStart钩子也执行exec,发现容器已经终止,所以创建index.html页面失败。
    args: ["-f","-h /tmp"] # -f 表示前台运行 -h 指定家目录
    
场景:在容器启动之后(postStart)克隆仓库,在容器退出之前(preStop)保存数据
posted on 2021-12-06 08:44  jueyuanfengsheng  阅读(421)  评论(0编辑  收藏  举报