本篇文章建立在Kubernetes 入门:K8S 基础操作之上。
本篇文章对pod/Service/Ingress/Volume数据卷进行深度配置。
深入了解Pod对象
之前大概提过一次,Pod是K8s中最小部署单元,我们实际的应用都是在pod中运行的,首先pod是k8S中最小的部署单元,也是一组容器的集合,可以是一个容器或多个容器去组成,可以理解为一个pod相当于一个虚拟机,在虚拟机里,你可以创建多个应用,应用就是容器。在一个pod中的容器共享一个网络命名空间。Pod是短暂的,下面对pod进行了简单的分类
Pod容器分类
在一个pod中,是有多个类型容器的,如下。
Infrastructure Container
基础容器,维护整个POD网络命名空间的,刚刚提到在一个pod中的容器共享一个网络命名空间,就是由基础容器来搞的,当你创建一个应用时,他就会启动起来,对于用户来说是透明的,所以你看到他创建的网络容器,但你能在节点上看到这个容器,譬如上文我们创建了一个nginx的服务,现在去节点看一下。
[root@master-1 ~]# ssh node-1
Last login: Tue Mar 5 15:47:10 2019 from etcd01
[root@node-1 ~]# docker ps
你会发现有一个使用pause-amd64镜像启动的容器,如果你有印象会想道这个镜像的地址是在kubelet的配置文件中指定的,也就是这里。
[root@node-1 ~]# cat /usr/local/kubernetes/cfg/kubelet | grep image
--pod-infra-container-image=swr.cn-north-1.myhuaweicloud.com/rj-bai/pause-amd64:3.1"这个镜像默认是在Google镜像仓库拉取的,但是国内无法访问,再次感谢快活提供国外服务器,在你每次创建pod的时候,都会启动一个pause-amd64容器,一一对应的,这个操作用户是没有感知的,用户不知道他还启了个这个。
InitContainer
这种容器启动优先于业务容器开始执行,之前K8S中,一个pod中有多个容器,容器启动也是没有顺讯的,都是并行启动,而有些服务依赖某些服务,譬如kafka依赖zookeeper,得先启动zookeeper,再启动kafka,如果反向kafka就会启动失败,这种容器的功能说白了就是类似compose的depends_on参数,也就是指定容器的启动顺序,之前我在swarm中提到过,目前swarm执行compose文件还不支持定义启动顺讯,记得当时的kafka集群,最开始的时候kafka&zookeeper的配置是写在一起的,开始部署容器启动顺讯不对,导致kafka启动N次后才启动成功,最后没办法给拆出来了,之后用K8S去做的话,将zookeeper定义为InitContainer 容器,kafka定义为Containers(服务容器)去启动就好了。
Containers
业务容器,也就是具体部署你应用程序的容器,并行启动,没有顺讯,所以在你定义了容器类型后,启动顺讯就是Infrastructure Container→InitContainer →Containers,目前容器就分这三类。
Pod镜像拉取策略
这个是通过imagePullpolicy这个字段来设置的,目前有三种策略,如下。
| 参数 | 含义 |
|---|---|
| IfNotPresent | 默认值,镜像在宿主机上存在才回拉取 |
| Always | 每次创建一个Pod都会重新拉取一次镜像 |
| Never | Pod永远不会主动拉取这个镜像 |
在swarm中也有类似的设置,现在看一下之前创建的那个nginx拉取策略是什么,现在就回用到上面的东西了。
[root@master-1 ~]# kubectl get deployments.apps nginx-deployment -o yaml | grep -i imagepull
imagePullPolicy: IfNotPresent现在默认值,没有就回拉取,之前没设置过,之前拉取的镜像仓库都是公开的,如果要登录就不行了,我目前用得还是阿里云的镜像仓库,以后会考虑用Harbor,要拉去私有仓库的东西,操作如下。
拉取私有仓库镜像
私有仓库是需要登陆的,认证过了之后就能下载了,所以在YAML文件中要添加一个imagePullSecrets的认证凭据,在本机登陆了并不代表通过k8S去部署时也是以登陆状态去拉取镜像的,docker和K8S使用的凭证并不是一套,是独立的,之前用swarm时需要用的一个参数是--with-registry-auth,这个参数就可以把认证当前的认证信息同步到worker节点,镜像怎么打标签传仓库我就不写了,先来试试不登录直接拉取,现在有一个服务,就是上面我创建的那个nginx,现在把他镜像地址改一下,改成我的私有仓库,然后更新一下服务。
我现在连的是Master节点,刚刚的那些都是在loadbalancer-1上操作的,我现在没有配置文件,就不复制过来了,直接用上面提到的命令把nginx的Deployment导成YAML文件改改就行了。
[root@master-1 ~]# mkdir demo
[root@master-1 ~]# cd demo/
[root@master-1 ~/demo]# kubectl get deployments.apps nginx-deployment -o yaml > nginx-deployment.yaml大概看一下导出的文件吧,内容可能偏多,很多都是默认值,不了解是干啥的参数全部删掉就好,修改后我的文件内容如下。
[root@master-1 ~/demo]# cat nginx-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: nginx
name: nginx-deployment
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- image: registry.cn-beijing.aliyuncs.com/rj-bai/blog:nginx-1.15.8
name: nginx
ports:
- containerPort: 80主要就是镜像地址,这是我博客用的nginx镜像,需要登录才能pull的,剩下的就没动过,配置很多,下面在node节点登陆到我的镜像仓库。
[root@master-1 ~/demo]# ansible node -m shell -a "docker login --username=xxx registry.cn-beijing.aliyuncs.com --password xxxx"都登陆成功了,下面更新下试试。
[root@master-1 ~/demo]# kubectl apply -f nginx-deployment.yaml
Warning: kubectl apply should be used on resource created by either kubectl create --save-config or kubectl apply
deployment.apps/nginx-deployment configured
[root@master-1 ~/demo]# kubectl get pods
nginx-deployment-cd454b696-9blj5 0/1 ImagePullBackOff 0 15s然后就出错了,提示ImagePullBackOff,是在滚动更新,一个失败之后就不再进行下去了,这个证明了docker&k8s使用的认证不是一套的,查看一下详细的错误信息,命令如下,查看资源的详细信息,现在开始涉及到排错命令了。
[root@master-1 ~/demo]# kubectl describe pods nginx-deployment-cd454b696-9blj5
提示让我登陆,所以失败了,现在就要为他设置一个凭据了,需要用到一个文件,也就是config.json,在~/.docker目录下,这个文件会保存登陆信息,现在需要将它编码,使用base64,命令如下。
[root@master-1 ~/demo]# cat ~/.docker/config.json | base64 -w 0然后会出来一串,这个就是配置凭据要用到的,我的我就不贴了,然后需要创建一个Secret来管理这个凭据,主要是配置一下加密的数据,YAML如下。
apiVersion: v1
kind: Secret
metadata:
name: registry-secret
data:
.dockerconfigjson: 这里是cat ~/.docker/config.json | base64 -w 0的结果。
type: kubernetes.io/dockerconfigjson然后创建一下。
[root@master-1 ~/demo]# kubectl create -f registry-secret.yaml
secret/registry-secret created
[root@master-1 ~/demo]# kubectl get secrets
NAME TYPE DATA AGE
registry-secret kubernetes.io/dockerconfigjson 1 66s可以看到data是1,如果是0说明数据没有保存进去,所以我们现在拿着个名字就可以配置到文件了。
[root@master-1 ~/demo]# cat nginx-deployment.yaml | grep imagePullS -A 3 -B 1
spec:
imagePullSecrets:
- name: registry-secret
containers:
- image: registry.cn-beijing.aliyuncs.com/rj-bai/blog:nginx-1.15.8可以看到我加到了spec下面,但是现在下载之后这个镜像是无法启动的,他依赖PHP,启动失败不要在意,下面更新一下试试。
[root@master-1 ~/demo]# kubectl apply -f nginx-deployment.yaml
deployment.apps/nginx-deployment configured
[root@master-1 ~/demo]# kubectl get pods -w
NAME READY STATUS RESTARTS AGE
nginx-deployment-7977886b-9v6vd 1/1 Running 0 49s
nginx-deployment-7977886b-c4m6w 0/1 Error 1 51s
nginx-deployment-7977886b-qccg9 0/1 Error 1 81s-w参数是实时查看创建状态,这里我只是取了一段,根本无法启动,看一下用的镜像是不是我博客的nginx
[root@master-1 ~/demo]# kubectl describe pods nginx-deployment-7977886b-9v6vd | grep image
事实证明是,但是为毛这个镜像起不来,看log
[root@master-1 ~/demo]# kubectl logs -f nginx-deployment-7977886b-9v6vd
nginx: [emerg] host not found in upstream "phpfpm" in /usr/local/nginx/vhost/blog.rj-bai.com.ssl.conf:26找不到phpfpm这个主机,所以起不来,上面提到过这个依赖php,镜像能正常下载就行了,暂时不管别的。
Pod资源限制
为了防止一个pod占用资源过高影响到宿主机,在K8S中,资源限制主要分为两个方面,其实,一个是swarm也一样limits,一个是requests。
limits是对资源的总限制,而requests是在pod创建时分配的最低资源,在创建pod的时候,K8S会根据requests值去做调度分配,也就是说如果你现在有N个node,现在需要创建一个pod,但是node1的资源已经满足不了创建pod的requests值,这个pod就不会被调度到node1,只有满足requests这个值的服务器才会被调度过去,下面一个例子,直接在官网扒的,加了一个数据库的全局变量。
apiVersion: v1
kind: Pod
metadata:
name: wp
spec:
containers:
- name: db
image: mysql
env:
- name: MYSQL_ROOT_PASSWORD
value: "Sowhat?"
resources:
requests:
memory: "64Mi"
cpu: "250m"
limits:
memory: "128Mi"
cpu: "500m"
- name: wp
image: wordpress
resources:
requests:
memory: "64Mi"
cpu: "250m"
limits:
memory: "128Mi"
cpu: "500m"上面Pod有两个容器。每个容器的requests值为0.25 cpu 和 64MiB内存,因为是两个容器,所以请求最低的资源为0.5 cpu和 128MiB 内存,也就是pod在创建时需要分配的最低资源,这个值会比limits值低。
每个容器的limits值为0.5 cpu和 128MiB 内存,所以这个pod的总限制为1 cpu和256内存,其实上面写的那些他都是通过docker run的一些命令去实现的,request.cpu等同于用--cpu-shares参数去限制,limits.cpu等同于--cpu-quota参数去限制,内存这块相当于是使用--memory值去限制,大概就是这样,下面去实践一下,就用上面的那个文件。
[root@master-1 ~/demo]# kubectl create -f wp.yaml
pod/wp created
[root@master-1 ~/demo]# kubectl get pod
NAME READY STATUS RESTARTS AGE
wp 2/2 Running 0 21s
[root@master-1 ~/demo]# kubectl describe pod wp
可以看到他是被调度到node-2节点了,然后去看一下node-2上所有pod的资源利用率。
[root@master-1 ~]# kubectl describe nodes node-2
重点就是这个,其他的都是之前创建的撒,可以看到CPU Requests为500,也就是0.5 CPU,CPU Limits为1,也就是1 cpu,Memory Requests为128M,Memory Limits为256M,后面的百分号是代表目前使用了多少,没问题,和指定的一样,删掉就行了,这块暂时就过了。
Pod重启策略
决定了pod在故障后所做的动作,这个策略是由restartPolicy参数来设置的,,和--restart的选项几乎一样,一共有三个参数
| 参数 | 解释 |
|---|---|
| Always: | 容器终止退出后总是重启容器,默认策略 |
| OnFailure: | 当容器异常退出(退出状态码非0)时才重启容器 |
| Never: | 当容器终止退出,从不重启容器 |
现在看一下现在的默认策略,就看之前创建的nginx的吧,之前在创建或更新的时候没有指定过。
[root@master-1 ~]# kubectl get deployments.apps nginx-deployment -o yaml | grep -i restart -C 2
imagePullSecrets:
- name: registry-secret
restartPolicy: Always
schedulerName: default-scheduler
securityContext: {}现在默认就是Always,也就是说你容器停了,不管是什么原因停的,他就会帮你再启动一个新的,不是重启,pod是没有重启这个概念的,之前的容器挂了就是挂了,不管,他会直接去启一个新的。
记得前段时间有人叫我把一个项目重启一下,我用的是swarm,那个项目的副本数是三个,我说也没办法进行重启啊,然后想了个损招,登录到worker节点去停项目容器,停了之后swarm会帮你再启动一个新的,哈哈,但是仔细想想还是随便更新一下服务吧,向容器里加了一条host解析,也就是用了--host-add,随便写的,然后服务就开始滚动更新,也算是重启了,在k8S中也一样,服务是无法重启的,这个就不去做演示了,看自己的实际情况去配置吧。
pod健康检查
健康检查和重启策略有点联系,健康检查(Probe),在默认的情况下,无论是K8S还是swarm是根据容器的状态决定是否要重启的,但是无法判断出容器内部署的应用是否正常,所以就用到了健康检查,用来检测你容器里应用是否存活,保障应用程序在生产环境存活的重要手段,目前为止Probe有两种类型,如下。
| 类型 | 释义 |
|---|---|
| livenessProbe | 如果检查失败,杀死容器,根据pod的restartPolicy来操作 |
| readinessProbe | 如果检查失败,Kubernetes会把Pod从service endpoints中剔除掉 |
livenessProbe 类型
livenessProbe类型的逻辑是这样,restartPolicy策略上文提到过了,也就是重启策略,如果一个容器健康检查失败,就杀了这个容器,根据重启策略去操作,如果是Always就会重新启动一个新的容器,以恢复他的正常工作,以此类推,这个不用多解释了,下面主要看一下readinessProbe类型的。
readinessProbe 类型
`service是为pod提供统一入口,endpoints是一个控制器,用来管理所有pod容器的IP的,下面看一下
[root@master-1 ~]# kubectl get service
[root@master-1 ~]# kubectl get service -n kube-system
[root@master-1 ~]# kubectl get endpoints
[root@master-1 ~]# kubectl get endpoints -n kube-system
[root@master-1 ~]# kubectl get pod -o wide
[root@master-1 ~]# kubectl get pod -o wide -n kube-system
上面提到了ENDPOINTS也是一个控制器,他的作用就是动态感知你所有pod的是由哪些IP组成的,这个IP也就是容器的IP,上图可以看到nginx的pod是由三个容器组成的,也有相对应的三个容器IP,并且这个pod被关联到了名为nginx-service的service,可以理解为ENDPOINTS控制器相当于一个地址池,但这个地址池是动态的。
service只是提供了一个负载均衡入口,他并不能知道service所关联的pod是哪些容器IP组成的,所以动态感知这些信息是由ENDPOINTS控制器去做的,这样service才知道要把请求转发到哪里。
所以readinessProbe类型的健康检查失败之后,他的逻辑就是将健康检查失败容器的IP在下面的列表中剔除掉。
[root@master-1 ~]# kubectl get endpoints nginx-service
NAME ENDPOINTS AGE
nginx-service 12.13.13.2:80,12.13.2.2:80,12.13.69.4:80 23h因为service是根据这个列表去进行转发的,所以就会不路由到健康检查失败的节点了,目前两种检查策略可以同时使用,也可以单独使用,视情况而定,下面看一下健康检查的方法。
健康检查方法
目前健康检查有三种方法,如下所示
| 方法 | 释义 |
|---|---|
| httpGet | 发送http请求,返回200-400状态码视为成功 |
| exec | 执行shell命令返回状态码为0视为成功 |
| tcpSocket | 发起TCP Socket 建立成功视为成功 |
看实际情况选择使用哪种方法吧,说白了httpGet就是获取http响应头的状态码,exec就是在容器内执行一条命令去判断返回码,tcpSocket就是判断能否和目标端口建立连接,下面实践一下。
实践操作
分别使用三种方法,各个都试试,不过有些参数要注意一下。
httpGet
[root@master-1 ~/demo]# cat nginx-pod.yaml
apiVersion: v1
kind: Pod
metadata:
labels:
app: nginx
name: nginx
spec:
containers:
- name: nginx
image: nginx:latest
livenessProbe:
httpGet:
path: /
port: 8080
scheme: HTTP
initialDelaySeconds: 3
periodSeconds: 3
timeoutSeconds: 1
successThreshold: 1
failureThreshold: 3用到的参数
| 参数 | 释义 |
|---|---|
| path | http服务器上请求访问的路径 |
| port | 要访问容器上的哪个端口 |
| scheme | 连接端口的协议(HTTP/HTTPS),默认HTTP,要大写撒 |
| initialDelaySeconds | 容器启动后多长时间开始健康检查 |
| periodSeconds | 监控频率,默认10s,最小1s |
| timeoutSeconds | 超时秒数,默认1s,最小值为1s |
| failureThreshold | 检查失败次数,超了这个次数直接根据重启策略进行操作 |
我用的是livenessProbe方式,常用的参数就这些,端口我是故意写错的,下面启动一下。
[root@master-1 ~/demo]# kubectl create -f nginx-pod.yaml
pod/nginx created
[root@master-1 ~/demo]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE
nginx 1/1 Running 2 44s 12.13.2.2 node-1 <none>
[root@master-1 ~/demo]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE
nginx 0/1 CrashLoopBackOff 4 2m27s 12.13.2.2 node-1 <none>可以看到无法启动了,其实端口不对,检查失败,无限重启,看一下日志。
[root@master-1 ~/demo]# kubectl describe pods nginx
说明健康检查没有问题,但总是检查失败已经进入无限重启的状态了,现在跑的是nginx,启动属于是秒级的,像是真的部署JAVA项目的时候,很多参数就得调了,像是initialDelaySeconds,