MENU

kubernetes进阶:K8S集群配置管理

• March 8, 2019 • Read: 61 • 日常

本篇文章建立在Kubernetes 入门:K8S 基础操作之上。
本篇文章对pod/Service/Ingress/Volume数据卷进行深度配置。

深入了解Pod对象

之前大概提过一次,PodK8s中最小部署单元,我们实际的应用都是在pod中运行的,首先podk8S中最小的部署单元,也是一组容器的集合,可以是一个容器或多个容器去组成,可以理解为一个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就会启动失败,这种容器的功能说白了就是类似composedepends_on参数,也就是指定容器的启动顺序,之前我在swarm中提到过,目前swarm执行compose文件还不支持定义启动顺讯,记得当时的kafka集群,最开始的时候kafka&zookeeper的配置是写在一起的,开始部署容器启动顺讯不对,导致kafka启动N次后才启动成功,最后没办法给拆出来了,之后用K8S去做的话,将zookeeper定义为InitContainer 容器,kafka定义为Containers(服务容器)去启动就好了。

Containers

业务容器,也就是具体部署你应用程序的容器,并行启动,没有顺讯,所以在你定义了容器类型后,启动顺讯就是Infrastructure ContainerInitContainer Containers,目前容器就分这三类。

Pod镜像拉取策略

这个是通过imagePullpolicy这个字段来设置的,目前有三种策略,如下。

参数含义
IfNotPresent默认值,镜像在宿主机上存在才回拉取
Always每次创建一个Pod都会重新拉取一次镜像
NeverPod永远不会主动拉取这个镜像

swarm中也有类似的设置,现在看一下之前创建的那个nginx拉取策略是什么,现在就回用到上面的东西了。

[root@master-1 ~]# kubectl get deployments.apps nginx-deployment -o yaml | grep -i imagepull
        imagePullPolicy: IfNotPresent

现在默认值,没有就回拉取,之前没设置过,之前拉取的镜像仓库都是公开的,如果要登录就不行了,我目前用得还是阿里云的镜像仓库,以后会考虑用Harbor,要拉去私有仓库的东西,操作如下。

拉取私有仓库镜像

私有仓库是需要登陆的,认证过了之后就能下载了,所以在YAML文件中要添加一个imagePullSecrets的认证凭据,在本机登陆了并不代表通过k8S去部署时也是以登陆状态去拉取镜像的,dockerK8S使用的凭证并不是一套,是独立的,之前用swarm时需要用的一个参数是--with-registry-auth,这个参数就可以把认证当前的认证信息同步到worker节点,镜像怎么打标签传仓库我就不写了,先来试试不登录直接拉取,现在有一个服务,就是上面我创建的那个nginx,现在把他镜像地址改一下,改成我的私有仓库,然后更新一下服务。

我现在连的是Master节点,刚刚的那些都是在loadbalancer-1上操作的,我现在没有配置文件,就不复制过来了,直接用上面提到的命令把nginxDeployment导成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

可以看到data1,如果是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值去做调度分配,也就是说如果你现在有Nnode,现在需要创建一个pod,但是node1的资源已经满足不了创建podrequests值,这个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 cpu128MiB 内存,也就是pod在创建时需要分配的最低资源,这个值会比limits值低。

每个容器的limits值为0.5 cpu128MiB 内存,所以这个pod的总限制为1 cpu256内存,其实上面写的那些他都是通过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 Requests500,也就是0.5 CPUCPU Limits1,也就是1 cpuMemory Requests128MMemory Limits256M,后面的百分号是代表目前使用了多少,没问题,和指定的一样,删掉就行了,这块暂时就过了。

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,上图可以看到nginxpod是由三个容器组成的,也有相对应的三个容器IP,并且这个pod被关联到了名为nginx-serviceservice,可以理解为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

用到的参数

参数释义
pathhttp服务器上请求访问的路径
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

Last Modified: May 28, 2019
Archives QR Code Tip
QR Code for this page
Tipping QR Code
Leave a Comment