k8s程序员Kuberne...

③k8s部署应用的流程与管理

2020-07-24  本文已影响0人  Linux丶晨星

适用于大部分项目(大同小异)


一、 项目迁移到K8S平台是怎样的流程

image

镜像的分类

二、 示例:在k8s中部署Java应用

学习案例使用为一位k8s大牛的案例

tomcat镜像包链接: https://pan.baidu.com/s/1v4EHVaSs2D38NhP0W5JE8A 提取码: n5qc

[root@k8s-master ~]# unzip tomcat-java-demo-master.zip
[root@k8s-master ~]# ll tomcat-java-demo-master
total 24
drwxr-xr-x 2 root root    34 Aug  5  2019 db    #sql测试文件
-rw-r--r-- 1 root root   148 Aug  5  2019 Dockerfile    #构建打包项目镜像
-rw-r--r-- 1 root root 11357 Aug  5  2019 LICENSE
-rw-r--r-- 1 root root  1930 Aug  5  2019 pom.xml   #java编译环境
-rw-r--r-- 1 root root   270 Aug  5  2019 README.md
drwxr-xr-x 3 root root    18 Aug  5  2019 src       #源码目录

提前准备一台数据库测试环境

# IP 192.168.0.40
[root@k8s-mariadb ~]# mysql --version
mysql  Ver 15.1 Distrib 5.5.65-MariaDB, for Linux (x86_64) using readline 5.1

将sql测试数据导入到数据库中

scp -rp db/tables_ly_tomcat.sql root@192.168.0.40:~

#进入到数据库导入数据
MariaDB [test]> use test;
MariaDB [test]> source /root/tables_ly_tomcat.sql;
MariaDB [test]> show tables;
+----------------+
| Tables_in_test |
+----------------+
| user           |
+----------------+
1 row in set (0.00 sec)

#授权
MariaDB [test]> grant all on test.* to 'test'@'%' identified by '123456';

在其他节点上测试数据库是否可以连接

[root@k8s-node02 ~]# mysql -h192.168.0.40 -utest -p
Enter password: 
Welcome to the MariaDB monitor.  Commands end with ; or \g.
Your MariaDB connection id is 9
Server version: 5.5.65-MariaDB MariaDB Server

Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

MariaDB [(none)]> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| test               |
+--------------------+
2 rows in set (0.00 sec)

1. 下载java编译环境进行构建

yum install openjdk-1.8.0-java maven -y
mvn clean package -Dmaven.test.skup=true

#生成target目录,和下面的war包
[root@k8s-master tomcat-java-demo-master]# ls target/
classes            ly-simple-tomcat-0.0.1-SNAPSHOT      maven-archiver
generated-sources  ly-simple-tomcat-0.0.1-SNAPSHOT.war  maven-status

2. 修改连接数据库的配置

#mysql地址修改为提前准备的mysql测试环境IP,test用户和密码
[root@k8s-master tomcat-java-demo-master]# vim src/main/resources/application.yml
url: jdbc:mysql://192.168.0.40:3306/test?characterEncoding=utf-8
username: test
password: 123456

3. 重新构建

mvn clean package -Dmaven.test.skup=true

4. 查看Dcokerfile文件

[root@k8s-master tomcat-java-demo-master]# cat Dockerfile 
FROM 245684979/tomcat #镜像
LABEL maintainer www.ctnrs.com  #标签作者
RUN rm -rf /usr/local/tomcat/webapps/*  #删除tomcat镜像中默认程序
ADD target/*.war /usr/local/tomcat/webapps/ROOT.war  #添加war包,当前目录下生成的

5. 使用doclerfile构建镜像

# Dockerfile文件名格式可以识别所以不需要-f指定
[root@k8s-master tomcat-java-demo-master]# docker build -t 245684979/java-demo .
...
Successfully tagged 245684979/java-demo:latest

6. 推送到镜像仓库

#推送到DockerHub,需要登录账号
docker push 245684979/java-demo:latest

#建议搭建一个私有镜像仓库

7. 其他node节点可以提前pull镜像

docker pull  245684979/tomcat

8. master节点生成yaml文件

kubectl create deployment java-demo --image=245684979/java-demo --dry-run=client -o yaml >deploy.yaml

#进行修改,副本数改为3,删除一些多余项
[root@k8s-master ~]# cat deploy.yaml 
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: java-demo
  name: java-demo
spec:
  replicas: 3
  selector:
    matchLabels:
      app: java-demo
  template:
    metadata:
      labels:
        app: java-demo
    spec:
      containers:
      - image: 245684979/java-demo
        name: java-demo

9. 创建pod

[root@k8s-master ~]# kubectl apply -f deploy.yaml 
deployment.apps/java-demo created

[root@k8s-master ~]# kubectl get pods
NAME                        READY   STATUS    RESTARTS   AGE
java-demo-b57dd87fb-5rlsl   1/1     Running   0          65s
java-demo-b57dd87fb-hr4ch   1/1     Running   0          65s
java-demo-b57dd87fb-m5dzg   1/1     Running   0          65s

10. 检查状态

#现在只是pod的状态成功了,容器内是否成功可以通过日志查看某个pod
[root@k8s-master ~]# kubectl logs java-demo-b57dd87fb-5rlsl
...
  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v2.0.1.RELEASE)
...
21-Jul-2020 10:55:51.099 INFO [main] org.apache.catalina.startup.Catalina.start Server startup in 26598 ms

11. 暴露应用端口

# --port:集群内部service之间访问端口
# --target-port:pod内应用程序访问端口
# --type=NodePort:生成集群外部访问端口
[root@k8s-master ~]#  kubectl expose deployment java-demo --port=80 --target-port=8080 --type=NodePort -o yaml --dry-run >svc.yaml

[root@k8s-master ~]# cat svc.yaml 
apiVersion: v1
kind: Service
metadata:
  labels:
    app: java-demo
  name: java-demo
spec:
  ports:
  - port: 80
    protocol: TCP
    targetPort: 8080
  selector:
    app: java-demo
  type: NodePort
  
#构建svc
[root@k8s-master ~]# kubectl apply -f svc.yaml 
service/java-demo created

#查看
[root@k8s-master ~]# kubectl get svc
NAME                 TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)        AGE
service/java-demo    NodePort    10.10.17.207   <none>        80:32433/TCP   72s
service/kubernetes   ClusterIP   10.10.0.1      <none>        443/TCP        3d17h

12. 进行访问暴露端口成功的应用

http://192.168.0.10:32433/
image

13. 进行添加数据

image
image

14. 在测试数据库中查看数据

MariaDB [test]> select * from user;
+----+-----------+-----+------+
| id | name      | age | sex  |
+----+-----------+-----+------+
|  1 | 高圆圆    |  18 | F    |
|  2 | 蔡徐坤    |  20 | M    |
|  3 | 刘芳      |  16 | F    |
|  4 | 刘芳      |  18 | F    |
|  5 | 貂蝉      | 800 | F    |
|  6 | 范冰冰    |  40 | F    |
|  7 | 金星      |  45 | Y    |
+----+-----------+-----+------+
7 rows in set (0.00 sec)

三、 Pod对象的理解

1. Pod的基本概念:one:

2. Pod存在的意义:two:

3. Pod实现机制:three:

测试Pod共享存储的机制

#编写yaml文件,一个读一个写,都挂载到/data目录下
[root@k8s-master ~]# cat emtydir.yaml
apiVersion: v1  
kind: Pod  
metadata:  
  name: my-pod  
spec:  
  containers:
  - name: write  
    image: centos
    command: ["bash","-c","for i in {1..100};do echo $i >> /data/hello;sleep 1;done"]  
    volumeMounts:
      - name: data  
        mountPath: /data
        
  - name: read  
    image: centos
    command: ["bash","-c","tail -f /data/hello"]  
    volumeMounts:
      - name: data 
        mountPath: /data

  volumes:
  - name: data  
    emptyDir: {}
    
#进行构建
[root@k8s-master ~]# kubectl apply -f emtydir.yaml

#访问Pod查看write写容器
[root@k8s-master ~]# kubectl exec -it my-pod -c write bash
[root@my-pod /ls /data/
hello
[root@my-pod /]# tail -f /data/hello 
59
60
61
62
63

#访问Pod查看read读容器
[root@k8s-master ~]# kubectl logs my-pod -c read
1
2
3
...
97
98
99
100

4. Pod容器分类与设计模式:four:

Pod Template常用功能字段解析

5. 健康检查

探针的种类
livenessProbe:健康状态检查,周期性检查服务是否存活,检查结果失败,将重启容器
readinessProbe:可用性检查,周期性检查服务是否可用,不可用将从service的endpoints中移除

探针的检测方法

四、 Deployment 控制器(无状态应用)

Deployment同样也是Kubernetes系统的一个核心概念,主要职责和RC一样的都是保证Pod的数量和健康,二者大部分功能都是完全一致的,我们可以看成是一个升级版的RC控制器。

Deployment具备的新特性

Deployment作为新一代的RC,不仅在功能上更为丰富了,同时我们也说过现在官方也都是推荐使用Deployment来管理Pod的,比如一些官方组件kube-dnskube-proxy也都是使用的Deployment来管理的,所以最好使用Deployment来管理Pod

image

可以看出一个Deployment拥有多个Replica Set,而一个Replica Set拥有一个或多个Pod。一个Deployment控制多个rs主要是为了支持回滚机制,每当Deployment操作时,Kubernetes会重新生成一个Replica Set并保留,以后有需要的话就可以回滚至之前的状态。

Replica Set的作用就是副本数记录和历史版本记录

1. Pod 与 controllers 的关系

controllers:在集群上管理和运行容器的对象
通过label-selector相关联
Pod通过控制器实现应用的运维,如伸缩,滚动升级等

2. Deployment功能与应用场景

部署"无状态应用"
管理Pod和ReplicaSet
具有上线部署、副本设定、滚动升级、回滚等功能
提供声明式更新,例如只更新一个新的 image

应用场景:Web服务,微服务

3. YAML字段解析

image

4. 使用Deployment部署一个Java应用

4.1 创建

生成yaml文件

kubectl create deployment web --image=245684979/java-demo --dry-run -o yaml >web.yaml

修改yaml文件

#使用准备好的docker hub上的java镜像:245684979/java-demo
#修改为3个副本
[root@k8s-master ~]# cat web.yaml 
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: web
  name: web
spec:
  replicas: 3
  selector:
    matchLabels:
      app: web
  template:
    metadata:
      labels:
        app: web
    spec:
      containers:
      - image: 245684979/java-demo
        name: java

构建yaml

[root@k8s-master ~]# kubectl apply -f web.yaml
deployment.apps/web created
[root@k8s-master ~]# kubectl get pod
NAME                        READY   STATUS    RESTARTS   AGE
java-demo-b57dd87fb-5rlsl   1/1     Running   1          23h
java-demo-b57dd87fb-cp2tq   1/1     Running   1          23h
java-demo-b57dd87fb-shr4k   1/1     Running   1          23h
web-5ccd9ffd6-694m2         1/1     Running   0          51s
web-5ccd9ffd6-llq9z         1/1     Running   0          51s
web-5ccd9ffd6-tlh9q         1/1     Running   0          51s

4.2 发布

通过service暴露端口

[root@k8s-master ~]# kubectl expose deployment web --port=80 --target-port=8080 --type=NodePort 
service/web exposed

#如果要生成yaml文件后面添加 --dry-run=client -o yaml >web_service.yaml

查看service

[root@k8s-master ~]# kubectl get svc
NAME         TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)        AGE
java-demo    NodePort    10.10.17.207   <none>        80:32433/TCP   24h
kubernetes   ClusterIP   10.10.0.1      <none>        443/TCP        4d17h
web          NodePort    10.10.9.46     <none>        80:32683/TCP   2m36s

进行访问

image

数据库的配置已经打包到镜像中了,所以之前的数据也存在

5. 升级与回滚

5.1 升级

升级为nginx镜像

#做测试修改为nginx的pod
[root@k8s-master ~]# kubectl set image deployment web java=nginx
deployment.apps/web image updated

#默认为滚动更新,启动一台新pod,删除一台旧pod,直到全部替换更新

进行访问会发现无法访问

image

需要修改service的端口为nginx的80

 [root@k8s-master ~]# kubectl edit service web
 ...
    targetPort: 80

再次访问已经为nginx的首页了

image

查看升级状态

[root@k8s-master ~]# kubectl rollout status deployment web 
deployment "web" successfully rolled out

5.2 回滚

查看历史版本记录

[root@k8s-master ~]# kubectl rollout history deployment web 
deployment.apps/web 
REVISION  CHANGE-CAUSE
1         <none>
2         <none>

回滚到上一版本

[root@k8s-master ~]# kubectl rollout undo deployment web 
deployment.apps/web rolled back

会发现访问网页提示无法访问了,说明回到了未修改端口的上一版本


image
#修改端口为8080
[root@k8s-master ~]# kubectl edit service web
...
    targetPort: 8080
image

回滚到指定版本的参数

kubectl rollout undo deployment web --to-revision=2

6. 弹性伸缩 scale

扩容和缩容一样的操作,只需要修改副本数

#将web扩容为5个副本,会在之前的3个之上再添加2个pod
[root@k8s-master ~]# kubectl scale deployment web --replicas=5
deployment.apps/web scaled
[root@k8s-master ~]# kubectl get pod
NAME                        READY   STATUS              RESTARTS   AGE
java-demo-b57dd87fb-5rlsl   1/1     Running             1          27h
java-demo-b57dd87fb-cp2tq   1/1     Running             1          26h
java-demo-b57dd87fb-shr4k   1/1     Running             1          27h
web-64c686b49d-28zbv        0/1     ContainerCreating   0          3s
web-64c686b49d-cb65c        0/1     ContainerCreating   0          3s
web-64c686b49d-479qh        1/1     Running             0          7m11s
web-64c686b49d-s95bb        1/1     Running             0          4m52s
web-64c686b49d-vbbnx        1/1     Running             0          6m49s

#缩容为3个副本
kubectl scale deployment web --replicas=3

#也可以直接去修改副本数
kubectl edit deployment web

k8s的扩容只是一个维度,需要建立在集群的每个节点资源之上。根据业务的量去扩容缩容。

在业务高峰或者低峰的时候,可以手动的调整Pod数量来提供资源的利用率,也可以使用HPA这种资源对象,可以做到自动伸缩。

五、 Service对象

Pod的生命是有限的,死亡过后不会复活了。RCDeployment可以用来动态的创建和销毁Pod。尽管每个Pod都有自己的IP地址,但是如果Pod重新启动了的话那么它的IP很有可能也就变化了。这就会带来一个问题:比如我们有一些后端的Pod的集合为集群中的其他前端的Pod集合提供API服务,如果我们在前端的Pod中把所有的这些后端的Pod的地址都写死了,然后去某种方式去访问其中一个Pod的服务,这样看上去是可以正常工作的,但是如果这个Pod挂掉了,然后重新启动起来了,IP地址非常有可能就变了,这个时候前端就极大可能访问不到后端的服务了。

遇到这样的问题该怎么解决呢?:question:

1. Kubernetes的三种IP

:one:首先,Node IPKubernetes集群中节点的物理网卡doIP地址(一般为内网),所有属于这个网络的服务器之间都可以直接通信,所以Kubernetes集群外要想访问Kubernetes集群内部的某个节点或者服务,肯定得通过Node IP进行通信(这个时候一般是通过外网IP了)

:two:然后Pod IP是每个PodIP地址,它是Docker Engine根据docker0网桥的IP地址段进行分配的。

:three:最后Cluster IP是一个虚拟的IP,仅仅作用于Kubernetes Service这个对象,由Kubernetes自己来进行管理和分配地址,当然我们也无法ping这个地址,他没有一个真正的实体对象来响应,他只能结合Service Port来组成一个可以通信的服务。

2. Service存在的意义

3. Pod与Service的关系

#查看之前创建的java-demo的yaml文件
[root@k8s-master ~]# cat deploy.yaml 
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: java-demo
  name: java-demo
spec:
  replicas: 3
  selector:     #定义了标签为java-demo
    matchLabels:
      app: java-demo
  template:
    metadata:
      labels:
        app: java-demo
    spec:
      containers:
      - image: 245684979/java-demo
        name: java-demo

通过的使用kubectl apply -f deploy.yaml就可以创建一个名为java-demoService对象,具有标签app=java-demoPod上,这个Service会被系统分配一个Cluster IP,该Service还会持续的监听selector下面的Pod,会把这些Pod信息更新到一个名为java-demoendpoints对象上去,这个对象就类似于我们上面说的Pod集合。

[root@k8s-master ~]# kubectl get endpoints
NAME         ENDPOINTS                                                  AGE
java-demo    10.244.58.211:8080,10.244.85.210:8080,10.244.85.211:8080   28h
kubernetes   192.168.0.10:6443                                          4d22h
web          10.244.58.221:8080,10.244.85.228:8080,10.244.85.232:8080   4h23m

4. kube-proxy与Service的关系

负责为 Service 提供 cluster 内部的服务发现和负载均衡

Kubernetes中默认是使用的iptables这种模式来代理

kube-proxy会监视Kubernetes master对 Service 对象和 Endpoints 对象的添加和移除。

对每个 Service,它会添加上 iptables 规则,从而捕获到达该 Service 的 clusterIP(虚拟 IP)和端口的请求。


image

5. Service类型

在定义Service的时候可以指定一个自己需要的类型的Service,如果不指定的话默认是ClusterIP类型。

可以使用的四种服务类型

6. 创建ClusterIP类型的Service

之前已经创建了NodePort类型的service

再创建一个新的service类型svc2.yaml文件来测试ClusterIP

# --port:集群内部service之间访问端口
# --target-port:pod内应用程序访问端口
# --type=NodePort:生成集群外部访问端口

kubectl expose deployment java-demo --port=80 --target-port=8080 --type=ClusterIP --dry-run -o yaml >svc2.yaml

#删除之前的,构建新创建的
[root@k8s-master ~]# kubectl delete -f svc.yaml
[root@k8s-master ~]# kubectl apply -f svc2.yaml

#查看svc
[root@k8s-master ~]# kubectl get svc
NAME         TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)        AGE
java-demo    ClusterIP   10.10.154.28   <none>        80/TCP         2m
kubernetes   ClusterIP   10.10.0.1      <none>        443/TCP        5d
web          NodePort    10.10.9.46     <none>        80:32683/TCP   6h36m

#可以直接访问集群内部IP了
curl 10.10.154.28
....
              whatToType: "这里有很多美女,挑一个回家吧!",
              typeSpeed: 300,
              lifeLike: true,
              breakLines :true
            }, function() {
                  console.log('This is tomcat callback function!');
            });
         </script>
</body>
</html>

7. ipvs负载均衡替代iptables

service多了以后 iptables表会影响性能

支持多种调度算法

生产中使用ipvs来做service的负载均衡转发

六、 ingress 实现集群外部的服务发现

Kubernetes 集群中的应用如何暴露给外部的用户使用呢?:question:

1. 简介

vivo 公司 Kubernetes 集群 Ingress 网关实践
推荐阅读学习ingress的文章

Ingress其实就是从 kuberenets 集群外部访问集群的一个入口,将外部的请求转发到集群内不同的 Service 上,其实就相当于 nginx、haproxy 等负载均衡代理服务器,会想到直接使用 nginx 就实现了,但是只使用 nginx 这种方式有很大缺陷,每次有新服务加入的时候怎么改 Nginx 配置?不可能去手动更改或者滚动更新前端的 Nginx 的Pod 。

如果再加上一个服务发现的工具的话其实就可以了,Ingress 实际上就是这样实现的,只是服务发现的功能自己实现了,不需要使用第三方的服务了,然后再加上一个域名规则定义,路由信息的刷新需要一个靠 Ingress controller 来提供。

Ingress controller 可以理解为一个监听器,通过不断地与 kube-apiserver 打交道,实时的感知后端 service、pod 的变化,当得到这些变化信息后,Ingress controller 再结合 Ingress 的配置,更新反向代理负载均衡器,达到服务发现的作用。

现在可以供大家使用的 Ingress controller 有很多,比如 traefiknginx-controllerKubernetes Ingress Controller for KongHAProxy Ingress controller,当然你也可以自己实现一个 Ingress Controller,现在普遍用得较多的是 traefik 和 nginx-controller,traefik 的性能较 nginx-controller 差,但是配置使用要简单许多。

2. 各种 Ingress 横向对比

image

2. Pod与Ingress的关系

3. Ingress Nginx的工作原理分析

参考博客链接

Nginx-ingressKubernetes生态中的重要成员,主要负责向外暴露服务,同时提供负载均衡等附加功能;

nginx-ingress已经能够完成 7/4 层的代理功能;4层代理基于 ConfigMap

1. Nginx 的 7 层反向代理模式

image

Nginx 对后端运行的服务(Service1、Service2)提供反向代理,在配置文件中配置了域名与后端服务 Endpoints集合的对应关系。客户端通过使用 DNS 服务或者直接配置本地的 hosts 文件,将域名都映射到 Nginx 代理服务器。当客户端访问 service1.com 时,浏览器会把包含域名的请求发送给 nginx 服务器,nginx 服务器根据传来的域名,选择对应的 Service,这里就是选择 Service 1 后端服务,然后根据一定的负载均衡策略,选择 Service1 中的某个容器接收来自客户端的请求并作出响应。过程很简单,nginx 在整个过程中仿佛是一台根据域名进行请求转发的“路由器”,这也就是7层代理的整体工作流程。

对于 Nginx 反向代理做了什么,上面说的内容已经大概了解了。在 k8s 中,后端服务的变化是十分频繁的,单纯依靠人工来更新nginx 的配置文件几乎不可能,nginx-ingress 由此应运而生。Nginx-ingress 通过监视 k8s 的资源状态变化实现对 nginx 配置文件的自动更新

2. nginx-ingress 工作流程分析

image

nginx-ingress 模块在运行时主要包括三个主体:NginxController、Store、SyncQueue。其中,Store 主要负责从 kubernetes APIServer 收集运行时信息,感知各类资源(如 ingress、service等)的变化,并及时将更新事件消息(event)写入一个环形管道;SyncQueue 协程定期扫描 syncQueue 队列,发现有任务就执行更新操作,即借助 Store 完成最新运行数据的拉取,然后根据一定的规则产生新的 nginx 配置,(有些更新必须 reload,就本地写入新配置,执行 reload),然后执行动态更新操作,即构造 POST 数据,向本地 Nginx Lua 服务模块发送 post 请求,实现配置更新;NginxController 作为中间的联系者,监听 updateChannel,一旦收到配置更新事件,就向同步队列 syncQueue 里写入一个更新请求。

当添加一个web服务 wordpress 后,在 nginx 配置中会添加一个 server 条目,但该条目中没有具体指出后端的容器地址,而是指向了一个叫 http://upstream_balancer 的地址,这个 balancer 其实是由 Lua 动态提供路由的。既然没有实际的容器后端在配置文件中进行配置,自然地,服务中容器数量的增减变化也就不必修改 nginx 配置文件了,这就是免 reload 的关键!简单推测,Lua 模块所做的就是维持一个服务到容器的映射关系,动态地提供负载均衡路由。

4. 部署ingress-nginx Controller

https://github.com/kubernetes/ingress-nginx/

下载官方yaml文件

wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v0.34.1/deploy/static/provider/cloud/deploy.yaml

#这一步为可选项:修改为 DaemonSet 控制器,特性是在每个node节点运行一个同样的Pod
#默认为 Deployment
apiVersion: apps/v1
kind: DaemonSet

#修改镜像地址为国内(阿里云仅支持到0.32版)
[root@k8s-master ingress-nginx]# vim deploy.yaml
...
    spec:
      dnsPolicy: ClusterFirst
      containers:
        - name: controller
          image: registry.aliyuncs.com/google_containers/nginx-ingress-controller:0.32.0
          imagePullPolicy: IfNotPresent

构建ingress nginx

kubectl apply -f deploy.yaml

[root@k8s-master ingress-nginx]# kubectl get pod -n ingress-nginx  
NAME                                        READY   STATUS      RESTARTS   AGE
ingress-nginx-admission-create-wjnhs        0/1     Completed   0          88s
ingress-nginx-admission-patch-rtg6c         0/1     Completed   0          88s
ingress-nginx-controller-574f54bc47-vp2fs   1/1     Running     0          99s

查看ingress的pod和版本

[root@k8s-master ingress-nginx]# kubectl get pods -n ingress-nginx 
NAME                                        READY   STATUS      RESTARTS   AGE
ingress-nginx-admission-create-dqg8l        0/1     Completed   0          6m58s
ingress-nginx-admission-patch-tj2js         0/1     Completed   0          6m58s
ingress-nginx-controller-574f54bc47-qzjmv   1/1     Running     0          7m8s

[root@k8s-master ingress-nginx]# kubectl exec -it -n ingress-nginx ingress-nginx-controller-574f54bc47-qzjmv -- /nginx-ingress-controller --version
-------------------------------------------------------------------------------
NGINX Ingress controller
  Release:       0.32.0
  Build:         git-446845114
  Repository:    https://github.com/kubernetes/ingress-nginx
  nginx version: nginx/1.17.10

-------------------------------------------------------------------------------

5. 创建ingress规则

[root@k8s-master ingress-nginx]# cat ingress.yaml
apiVersion: extensions/v1beta1  
kind: Ingress
metadata:
  name: web  
spec:
  rules:
  - host: blog.girl.com 
    http:
      paths:
      - backend:  
          serviceName: web  
          servicePort: 80
#查看当前service,这里使用web的的80端口
[root@k8s-master ingress-nginx]# kubectl get svc
NAME         TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
java-demo    NodePort    10.10.206.186   <none>        80:31985/TCP   23h
kubernetes   ClusterIP   10.10.0.1       <none>        443/TCP        5d23h
web          NodePort    10.10.9.46      <none>        80:32683/TCP   30h

#构建ingress
[root@k8s-master ingress-nginx]# kubectl apply -f ingress.yaml 
ingress.extensions/web created
[root@k8s-master ingress-nginx]# kubectl get ingress
NAME   CLASS    HOSTS           ADDRESS   PORTS   AGE
web    <none>   blog.girl.com             80      11s

在本地配置hosts解析

image

在本地进行ping是否畅通

image

浏览器使用域名访问

image

如果想不使用端口访问需要在web的pod中添加hostPort端口,因为现在是NodePort端口

6. 示例Nginx服务负载均衡测试

#创建nginx的pod
[root@k8s-master pod]# cat nginx.yaml 
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: nginx
  name: nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - image: nginx
        name: nginx
        
[root@k8s-master pod]# kubectl apply -f nginx.yaml
[root@k8s-master pod]# kubectl get pod 
NAME                        READY   STATUS    RESTARTS   AGE
nginx-f89759699-5bn67       1/1     Running   0          8m
nginx-f89759699-h9jqt       1/1     Running   0          7m
nginx-f89759699-m2kgt       1/1     Running   0          7m
#创建service
[root@k8s-master service]# cat svc_nginx.yaml 
apiVersion: v1
kind: Service
metadata:
  labels:
    app: nginx
  name: nginx
spec:
  ports:
  - port: 80
    protocol: TCP
    targetPort: 80
  selector:
    app: nginx
  type: NodePort

[root@k8s-master pod]# kubectl apply -f svc_nginx.yaml 
#创建ingress
[root@k8s-master ingress-nginx]# cat ingress_nginx.yaml 
apiVersion: extensions/v1beta1  
kind: Ingress
metadata:
  name: nginx
spec:
  rules:
  - host: www.nginx.com 
    http:
      paths:
      - backend:  
          serviceName: nginx
          servicePort: 80

[root@k8s-master pod]# kubectl apply -f ingress_nginx.yaml 

[root@k8s-master ingress-nginx]# kubectl get svc,ingress
NAME                 TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
service/nginx        NodePort    10.10.121.138   <none>        80:32645/TCP   8m

NAME                       CLASS    HOSTS           ADDRESS   PORTS   AGE
ingress.extensions/nginx   <none>   www.nginx.com             80       3m
#修改Pod中的三台容器的首页index.html文件
[root@k8s-master ingress-nginx]# kubectl exec -it nginx-f89759699-5bn67 bash
root@nginx-f89759699-5bn67:/# echo nginx-f89759699-5bn67  >/usr/share/nginx/html/index.html

[root@k8s-master ingress-nginx]# kubectl exec -it nginx-f89759699-h9jqt /bin/bash
root@nginx-f89759699-h9jqt:/# echo nginx-f89759699-h9jqt >/usr/share/nginx/html/index.htm

[root@k8s-master ingress-nginx]# kubectl exec -it nginx-f89759699-m2kgt /bin/bash
root@nginx-f89759699-m2kgt:/# echo nginx-f89759699-m2kgt >/usr/share/nginx/html/index.html

添加本地hosts解析

image

浏览器访问并刷新页面查看负载均衡的效果

image image
image

七、Helm包管理工具

官网: https://helm.sh/

1. Helm作用与概念

Helm是个啥?

Helm就相当于kubernetes环境下的yum包管理工具。包管理器类似于我们在 Ubuntu 中使用的apt、Centos中使用的yum 或者Python中的 pip 一样,能快速查找、下载和安装软件包。Helm 由客户端组件 helm 和服务端组件 Tiller 组成, 能够将一组K8S资源打包统一管理, 是查找、共享和使用为Kubernetes构建的软件的最佳方式。

Helm的用途

做为 Kubernetes 的一个包管理工具,Helm具有如下功能:

Helm相关组件及概念

Helm 包含两个组件,分别是 helm 客户端 和 Tiller 服务器:

2. Helm的v3和v2版本变化

Helm3是CLI工具的最新主要版本。Helm 3基于Helm 2的成功,不断满足不断发展的生态系统的需求。Helm 3的内部实现已从Helm 2发生了很大变化。

https://github.com/helm/helm/releases/tag/

3. Helm安装

1. 下载Helm包

https://get.helm.sh/helm-v3.2.4-linux-amd64.tar.gz

[root@k8s-master tools]# wget https://get.helm.sh/helm-v3.2.4-linux-amd64.tar.gz
[root@k8s-master tools]# tar xf helm-v3.2.4-linux-amd64.tar.gz
[root@k8s-master tools]# ls linux-amd64/
helm  LICENSE  README.md
[root@k8s-master tools]# mv linux-amd64/helm /usr/bin/

2. 配置Helm国内镜像源

阿里云的源已经不更新了,这里使用微软的国内源 http://mirror.azure.cn/kubernetes/charts

[root@k8s-master ~]# helm repo add stable http://mirror.azure.cn/kubernetes/charts
"stable" has been added to your repositories

[root@k8s-master ~]# helm repo list
NAME    URL                                     
stable  http://mirror.azure.cn/kubernetes/charts

3. 设置 Helm 命令自动补齐

source <(helm completion bash)

4. Helm的测试使用

1. 搜索一个weave的包

weave是k8s的一个ui界面

[root@k8s-master ~]# helm search repo weave
NAME                CHART VERSION   APP VERSION DESCRIPTION                                       
stable/weave-cloud  0.3.7           1.4.0       Weave Cloud is a add-on to Kubernetes which pro...
stable/weave-scope  1.1.10          1.12.0      A Helm chart for the Weave Scope cluster visual...

2. 下载包

[root@k8s-master ~]# helm install ui stable/weave-scope 
NAME: ui
LAST DEPLOYED: Mon Jul 27 10:48:59 2020
NAMESPACE: default
STATUS: deployed
REVISION: 1
NOTES:
You should now be able to access the Scope frontend in your web browser, by
using kubectl port-forward:

kubectl -n default port-forward $(kubectl -n default get endpoints \
ui-weave-scope -o jsonpath='{.subsets[0].addresses[0].targetRef.name}') 8080:4040

then browsing to http://localhost:8080/.
For more details on using Weave Scope, see the Weave Scope documentation:

https://www.weave.works/docs/scope/latest/introducing/

3. 查看安装好的release、Pod、Service

[root@k8s-master ~]# helm list 
NAME    NAMESPACE   REVISION    UPDATED                                 STATUS      CHART               APP VERSION
ui      default     1           2020-07-27 10:48:59.644952211 +0800 CST deployed    weave-scope-1.1.10  1.12.0     

[root@k8s-master ~]# kubectl get pod
NAME                                            READY   STATUS    RESTARTS   AGE
weave-scope-agent-ui-khzbl                      1/1     Running   0          4m25s
weave-scope-agent-ui-x9kgm                      1/1     Running   0          4m25s
weave-scope-cluster-agent-ui-7498b8d4f4-w7x94   1/1     Running   0          4m25s
weave-scope-frontend-ui-649c7dcd5d-rl2m4        1/1     Running   0          4m25s

[root@k8s-master ~]# kubectl get svc
NAME             TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
ui-weave-scope   ClusterIP   10.10.97.176    <none>        80/TCP         6m12s

4. 将service修改为NodePort暴露给外部

#将ClusterIP修改为NodePort
[root@k8s-master ~]# kubectl edit service ui-weave-scope
...
  sessionAffinity: None
  type: NodePort
  
[root@k8s-master ~]# kubectl get svc
NAME             TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
ui-weave-scope   NodePort    10.10.97.176    <none>        80:32328/TCP   14m

5. 访问

http://192.168.0.10:32328


image

可以直接登录控制台


image

5. Helm的模板创建

1. 手动创建一个 Chart

其中最核心的是 templates 这个文件夹,里面其实就是 K8s 资源描述yaml文件的模版。模版里面的内容可以通过 values.yaml 里面的内容去渲染,同时也可以在使用 helm install --set key=value xxx 部署的时候去覆盖 values.yaml 里面的默认值。

[root@k8s-master helm]# helm create mychart
Creating mychart
[root@k8s-master helm]# tree mychart/
mychart/
├── charts
├── Chart.yaml
├── templatesll
│   ├── deployment.yaml
│   ├── _helpers.tpl
│   ├── hpa.yaml
│   ├── ingress.yaml
│   ├── NOTES.txt
│   ├── serviceaccount.yaml
│   ├── service.yaml
│   └── tests
│       └── test-connection.yaml
└── values.yaml

3 directories, 10 files
#先将默认生成的文件删除掉
[root@k8s-master helm]# cd mychart/templates/
[root@k8s-master templates]# rm -rf *

2. 将之前创建的nginx的yaml文件复制进来

cp /root/service/svc_nginx.yaml /root/deployment/nginx.yaml ./
mv nginx.yaml deployment.yaml
[root@k8s-master templates]# ls
deployment.yaml  svc_nginx.yaml

3. 进行安装nginx模板

如果有之前创建相同名字的资源,需要删除掉,否则会报错

[root@k8s-master ~]# helm install nginx helm/mychart/
NAME: nginx
LAST DEPLOYED: Mon Jul 27 14:06:43 2020
NAMESPACE: default
STATUS: deployed
REVISION: 1
TEST SUITE: None

[root@k8s-master ~]# kubectl get pod
NAME                                            READY   STATUS    RESTARTS   AGE
nginx-f89759699-f2gg5                           1/1     Running   0          45s
nginx-f89759699-fqrpx                           1/1     Running   0          45s
nginx-f89759699-kmrlg                           1/1     Running   0          45s

[root@k8s-master ~]# kubectl get svc
NAME             TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
...
nginx            NodePort    10.10.182.160   <none>        80:32580/TCP   9s
ui-weave-scope   NodePort    10.10.97.176    <none>        80:32328/TCP   3h17m

4. 查看已经创建的模板

[root@k8s-master ~]# helm list
NAME    NAMESPACE   REVISION    UPDATED                                 STATUS      CHART               APP VERSION
nginx   default     1           2020-07-27 14:06:43.23384726 +0800 CST  deployed    mychart-0.1.0       1.16.0     
ui      default     1           2020-07-27 10:48:59.644952211 +0800 CST deployed    weave-scope-1.1.10  1.12.0 

5. helm模板的升级

#如果yaml文件中有修改,修改后需要执行upgrade

[root@k8s-master ~]# helm upgrade nginx helm/mychart/
Release "nginx" has been upgraded. Happy Helming!
NAME: nginx
LAST DEPLOYED: Mon Jul 27 14:14:42 2020
NAMESPACE: default
STATUS: deployed
REVISION: 2
TEST SUITE: None

6. Helm Chart开发与模板使用

一套yaml文件部署多个应用时,yaml文件需要修改的位置

1. 动态的渲染模板

[root@k8s-master mychart]# pwd
/root/helm/mychart

#删除默认内容,添加下面的基础信息用来测试
[root@k8s-master mychart]# cat values.yaml 
replicas: 1

image: 245684979/java-demo
tag: latest

label: java-demo

port: 8080

2. 修改nginx模板的yaml文件

[root@k8s-master mychart]# cat templates/deployment.yaml 
apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ .Release.Name }}-dp  #-dp 标识符
spec:
  replicas: {{ .Values.replicas }}
  selector:
    matchLabels:
      app: {{ .Values.label }}
  template:
    metadata:
      labels:
        app: {{ .Values.label }}
    spec:
      containers:
      - image: {{ .Values.image }}:{{ .Values.tag }}
        name: nginx

###
[root@k8s-master ~]# cat helm/mychart/templates/svc_nginx.yaml 
apiVersion: v1
kind: Service
metadata:
  name: {{ .Release.Name }}-svc  #标识符
spec:
  ports:
  - port: 80
    protocol: TCP
    targetPort: {{ .Values.port }}
  selector:
    app: {{ .Values.label }}
  type: NodePort

3. 将渲染变量模板应用到yaml中

输出一下变量渲染生效后的输出结果

# --dry-run: 不执行查看渲染结果,如果不加此参数则会创建

[root@k8s-master ~]# helm install --dry-run nginx helm/mychart/
NAME: nginx
LAST DEPLOYED: Mon Jul 27 14:52:55 2020
NAMESPACE: default
STATUS: pending-install
REVISION: 1
TEST SUITE: None
HOOKS:
MANIFEST:
---
# Source: mychart/templates/svc_nginx.yaml
apiVersion: v1
kind: Service
metadata:
  name: nginx-svc
spec:
  ports:
  - port: 80
    protocol: TCP
    targetPort: 8080
  selector:
    app: java-demo
  type: NodePort
---
# Source: mychart/templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-dp
spec:
  replicas: 1
  selector:
    matchLabels:
      app: java-demo
  template:
    metadata:
      labels:
        app: java-demo
    spec:
      containers:
      - image: 245684979/java-demo:latest
        name: nginx

执行此模板

[root@k8s-master ~]# helm install java-demo helm/mychart/
NAME: java-demo
LAST DEPLOYED: Mon Jul 27 14:56:53 2020
NAMESPACE: default
STATUS: deployed
REVISION: 1
TEST SUITE: None

查看生成的pod和service

[root@k8s-master ~]# kubectl get pod
NAME                                            READY   STATUS    RESTARTS   AGE
...
java-demo-dp-859745b69c-2r78f                   1/1     Running   0          101s

[root@k8s-master ~]# kubectl get svc
NAME             TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
java-demo-svc    NodePort    10.10.217.33    <none>        80:31618/TCP   2m3s
...

也可以尝试访问一下是否成功

http://192.168.0.10:31618


image

4. 更新chart包

[root@k8s-master ~]# helm upgrade java-demo --set replicas=3 helm/mychart/
Release "java-demo" has been upgraded. Happy Helming!
NAME: java-demo
LAST DEPLOYED: Mon Jul 27 15:08:34 2020
NAMESPACE: default
STATUS: deployed
REVISION: 2
TEST SUITE: None

[root@k8s-master ~]# kubectl get pod
NAME                                            READY   STATUS    RESTARTS   AGE
java-demo-dp-859745b69c-2r78f                   1/1     Running   0          12m
java-demo-dp-859745b69c-gzx8k                   1/1     Running   0          46s
java-demo-dp-859745b69c-zxvwl                   1/1     Running   0          46s

5. 回滚chart包

#列出模板的历史版本信息
[root@k8s-master ~]# helm history java-demo 
REVISION    UPDATED                     STATUS      CHART           APP VERSION DESCRIPTION     
1           Mon Jul 27 14:56:53 2020    superseded  mychart-0.1.0   1.16.0      Install complete
2           Mon Jul 27 15:08:34 2020    deployed    mychart-0.1.0   1.16.0      Upgrade complete

#回滚到 1 版本,1版本为创第一次创建,副本数为1;创建完成后可以检查一下
[root@k8s-master ~]# helm rollback java-demo 1
Rollback was a success! Happy Helming!

#由三副本变为1副本了
[root@k8s-master ~]# kubectl get pod
NAME                                            READY   STATUS    RESTARTS   AGE
java-demo-dp-859745b69c-2r78f                   1/1     Running   0          17m

7. Chart 的UI管理界面 monocular

可以在里面添加 chart 的仓库,能够查看到当前集群中已经运行的 Chart,同时还能够直接在页面上就部署一个 chart 至 kuberntes 集群,就好像一个 kubernetes 的 appstore 一样。不支持覆盖 chart 中指定一个的默认参数,唯一可选择的就是部署的 namespace。

安装方法比较简单 可访问官方查看:https://github.com/helm/monocular

8. Helm命令总结

#搜索可用于安装的Chart
helm search mysql

#安装一个Chart
helm install stable/mysql

#列出k8s中已经部署的Chart
helm list --all

#helm的仓库更新
helm repo update

#查看镜像源
helm repo list

#添加镜像源
helm repo add stable <url>

#删除镜像源
helm repo remove <url>

#创建Chart包管理模板,会产生一个Chart所需的目录结构
helm create mychart

#升级chart包
helm upgrade --set xxx java-demo

#查看历史版本
helm history java-demo

#回滚chart包版本
helm rollback java-demo 1

#删除release
helm delete nginx

#打包Chart
helm package helm/mychart/
Successfully packaged chart and saved it to: /root/mychart-0.1.0.tgz

八、弹性伸缩(扩容/缩容)

1. Node扩容/缩容

从Kubernetes集群中删除节点的方法:https://blog.csdn.net/erhaiou2008/article/details/104986006/

1、Cluster AutoScaler

扩容: Cluster AutoScaler 定期检测是否有充足的资源来调度新创建的 Pod,当资源不足时会调用 Cloud Provider 创建新的 Node。

图片来自网络 image

缩容: Cluster AutoScaler 也会定期监测 Node 的资源使用情况,当一个 Node 长时间资源利用率都很低时(低于 50%)自动将其所在虚拟机从云服务商中删除。此时,原来的 Pod 会自动调度到其他 Node 上面。

图片来自网络 image

支持的云提供商:

2. Pod自动扩容/缩容(HPA)

Horizontal Pod Autoscaler(HPA,Pod水平自动伸缩),根据资源利用率或者自定义指标自动调整replication controller, deployment 或 replica set,实现部署的自动扩展和缩减,让部署的规模接近于实际服务的负载。HPA不适于无法缩放的对象,例如DaemonSet。

1、HPA基本原理

Kubernetes 中的 Metrics Server 持续采集所有 Pod 副本的指标数据。HPA 控制器通过 Metrics Server 的 API(Heapster 的 API 或聚合 API)获取这些数据,基于用户定义的扩缩容规则进行计算,得到目标 Pod 副本数量。当目标 Pod 副本数量与当前副本数量不同时,HPA 控制器就向 Pod 的副本控制器(Deployment、RC 或 ReplicaSet)发起 scale 操作,调整 Pod 的副本数量,完成扩缩容操作。如图所示。

在弹性伸缩中,冷却周期是不能逃避的一个话题, 由于评估的度量标准是动态特性,副本的数量可能会不断波动。有时被称为颠簸, 所以在每次做出扩容缩容后,冷却时间是多少。

在 HPA 中,默认的扩容冷却周期是 3 分钟,缩容冷却周期是 5 分钟。

可以通过调整kube-controller-manager组件启动参数设置冷却时间:

2、HPA的演进历程

目前 HPA 已经支持了 autoscaling/v1autoscaling/v2beta1autoscaling/v2beta2 三个大版本 。

目前大多数人比较熟悉是autoscaling/v1,这个版本只支持CPU一个指标的弹性伸缩。

而autoscaling/v2beta1增加了支持自定义指标,autoscaling/v2beta2又额外增加了外部指标支持。

而产生这些变化不得不提的是Kubernetes社区对监控与监控指标的认识与转变。从早期Heapster到Metrics Server再到将指标边界进行划分,一直在丰富监控生态。

3. 基于CPU指标进行缩放

1、 Kubernetes API Aggregation

在 Kubernetes 1.7 版本引入了聚合层,允许第三方应用程序通过将自己注册到kube-apiserver上,仍然通过 API Server 的 HTTP URL 对新的 API 进行访问和操作。为了实现这个机制,Kubernetes 在 kube-apiserver 服务中引入了一个 API 聚合层(API Aggregation Layer),用于将扩展 API 的访问请求转发到用户服务的功能。

图片来自网络


image

当你访问 apis/metrics.k8s.io/v1beta1 的时候,实际上访问到的是一个叫作 kube-aggregator 的代理。而 kube-apiserver,正是这个代理的一个后端;而 Metrics Server,则是另一个后端 。通过这种方式,我们就可以很方便地扩展 Kubernetes 的 API 了。

因是使用kubeadm部署的,默认已开启。如果使用二进制方式部署的话,需要在kube-APIServer中添加启动参数,增加以下配置:

# vim /opt/kubernetes/cfg/kube-apiserver.conf
...
--requestheader-client-ca-file=/opt/kubernetes/ssl/ca.pem \
--proxy-client-cert-file=/opt/kubernetes/ssl/server.pem \
--proxy-client-key-file=/opt/kubernetes/ssl/server-key.pem \
--requestheader-allowed-names=kubernetes \
--requestheader-extra-headers-prefix=X-Remote-Extra- \
--requestheader-group-headers=X-Remote-Group \
--requestheader-username-headers=X-Remote-User \
--enable-aggregator-routing=true \
...

设置完成重启 kube-apiserver 服务,就启用 API 聚合功能了。


2. autoscaling/v1(CPU指标测试)

autoscaling/v1版本只能支持CPU一个指标。

先部署一个应用pod

[root@k8s-master autoscaling]# cat deployment_web.yaml 
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx-php
  template:
    metadata:
      labels:
        app: nginx-php
    spec:
      containers:
      - image: 245684979/nginx-php
        name: java
        resources: 
           requests:
             memory: "300Mi"
             cpu: "250m"

---

apiVersion: v1
kind: Service
metadata:
  name: web
spec:
  ports:
  - port: 80
    protocol: TCP
    targetPort: 80
  selector:
    app: nginx-php


[root@k8s-master autoscaling]# kubectl get pod
NAME                                            READY   STATUS    RESTARTS   AGE
web-5bf5bb785f-tnjzn                            1/1     Running   0          46s

#ClusterIP为10.244.58.206 接 -o wide参数查看

创建HPA策略组

[root@k8s-master autoscaling]# cat autoscaling_v1.yaml 
apiVersion: autoscaling/v1
kind: HorizontalPodAutoscaler
metadata:
  name: web
spec:
  maxReplicas: 5
  minReplicas: 1
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: web
  targetCPUUtilizationPercentage: 60

#- scaleTargetRef:表示当前要伸缩对象是谁
#- targetCPUUtilizationPercentage:当整体的资源利用率超过60%的时候,会进行扩容

[root@k8s-master autoscaling]# kubectl get hpa
NAME   REFERENCE        TARGETS   MINPODS   MAXPODS   REPLICAS   AGE
web    Deployment/web   0%/60%    1         1         1          1m

进行压测

yum install httpd-tools
ab -n 100000 -c 100  http://10.244.58.206/status.php

检查扩容状态

[root@k8s-master ~]# kubectl get hpa
NAME   REFERENCE        TARGETS    MINPODS   MAXPODS   REPLICAS   AGE
web    Deployment/web   537%/60%   1         5         4          3m42s

[root@k8s-master ~]# kubectl top pods
NAME                                            CPU(cores)   MEMORY(bytes)           
web-5bf5bb785f-tnjzn                            1343m        25Mi 

[root@k8s-master ~]# kubectl get pods
NAME                                    READY   STATUS              RESTARTS   AGE
web-5bf5bb785f-7l5tl                    0/1     ContainerCreating   0          41s
web-5bf5bb785f-7n886                    1/1     Running             0          56s
web-5bf5bb785f-pzgzs                    1/1     Running             0          56s
web-5bf5bb785f-qm8wb                    0/1     ContainerCreating   0          56s
web-5bf5bb785f-tnjzn                    1/1     Running             0          6m13s

关闭压测,过5分钟检查缩容状态

[root@k8s-master ~]# kubectl get pod
NAME                                     READY   STATUS    RESTARTS   AGE
web-5bf5bb785f-tnjzn                     1/1     Running   0          13m

工作流程:
hpa -> apiserver -> kube aggregation -> metrics-server -> kubelet(cadvisor)


3. autoscaling/v2beta2(多指标)

为满足更多的需求, HPA 还有 autoscaling/v2beta1autoscaling/v2beta2两个版本。

这两个版本的区别是 autoscaling/v1beta1支持了 Resource Metrics(CPU)和 Custom Metrics(应用程序指标),而在 autoscaling/v2beta2的版本中额外增加了 External Metrics (外部指标)的支持。

#生成yaml
kubectl get hpa.v2beta2.autoscaling -o yaml > hpa-v2.yaml

v2与上面v1版本效果一样,只不过这里格式有所变化。

v2还支持其他另种类型的度量指标:Pods和Object。

metrics中的type字段有四种类型的值:Object、Pods、Resource、External。

#测试的yaml文件
[root@k8s-master autoscaling]# cat hpa-v2.yaml 
apiVersion: autoscaling/v2beta2
kind: HorizontalPodAutoscaler
metadata:
  name: web
  namespace: default
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: web
  minReplicas: 1
  maxReplicas: 10
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 50
  - type: Pods
    pods:
      metric:
        name: packets-per-second
      target:
        type: AverageValue
        averageValue: 1k
  - type: Object
    object:
      metric:
        name: requests-per-second
      describedObject:
        apiVersion: networking.k8s.io/v1beta1
        kind: Ingress
        name: main-route
      target:
        type: Value
        value: 10k

工作流程:
hpa -> apiserver -> kube aggregation -> prometheus-adapter -> prometheus -> pods

4. 基于Prometheus自定义指标弹性伸缩

参考博客
https://blog.51cto.com/12480612/2457624

资源指标只包含CPU、内存,一般来说也够了。但如果想根据自定义指标:如请求qps/5xx错误数来实现HPA,就需要使用自定义指标了,目前比较成熟的实现是 Prometheus Custom Metrics。自定义指标由Prometheus来提供,再利用k8s-prometheus-adpater聚合到apiserver,实现和核心指标(metric-server)同样的效果。

1. 准备一个nfs环境

提前部署好nfs-client,否则prometheus无法启动
为防止Pod漂移到其他节点无法访问,所以需要创建一个nfs共享目录来进行挂载

#准备一台nfs服务器或者用master节点作为nfs服务器测试即可
#在服务端安装nfs软件:
yum install -y nfs-utils rpcbind
#启动rpcbind和nfs服务
systemctl restart rpcbind nfs
systemctl enable rpcbind nfs
#nfs配置文件/etc/exports
echo '/ifs/kubernetes   *(rw,no_root_squash' >>/etc/exports
#创建nfs共享目录
mkdir -p /ifs/kubernetes
#平滑重启nfs
systemctl reload nfs
#检查可以挂载的目录
[root@k8s-master ~]# showmount -e 
Export list for k8s-master:
/ifs/kubernetes *
#在所有k8s节点安装nfs客户端
yum install  nfs-utils -y

nfs-client文件包下载链接:提取码: rr89

[root@k8s-master ~]# unzip nfs-client.zip
[root@k8s-master ~]# cd nfs-client/
[root@k8s-master nfs-client]# ll
total 12
-rw-r--r-- 1 root root  225 Nov 30  2019 class.yaml
-rw-r--r-- 1 root root  994 Nov 30  2019 deployment.yaml
-rw-r--r-- 1 root root 1526 Nov 30  2019 rbac.yaml

修改deployment.yaml文件

[root@k8s-master nfs-client]# cat deployment.yaml
...
              value: 192.168.0.10 #nfs服务器地址
            - name: NFS_PATH
              value: /ifs/kubernetes    #nfs共享目录
      volumes:
        - name: nfs-client-root
          nfs:
            server: 192.168.0.10    #nfs服务器地址
            path: /ifs/kubernetes   #nfs共享目录

构建nfs-client

[root@k8s-master nfs-client]# kubectl apply -f .
[root@k8s-master nfs-client]# kubectl get pod
NAME                                            READY   STATUS    RESTARTS   AGE
nfs-client-provisioner-f7886779f-cwqkr          1/1     Running   0          26s

# kubectl get pv
# kubectl get sc

查看nfs共享目录下成功了

[root@k8s-master nfs-client]# ls /ifs/kubernetes/
kube-system-grafana-data-grafana-0-pvc-469e13d0-8429-4766-b23a-af9baf3eb80c
kube-system-prometheus-data-prometheus-0-pvc-074bfaaf-4ceb-42d9-b940-d2f4905bc69b

2. 部署Prometheus

Prometheus文件包下载链接: 提取码: 3buh

[root@k8s-master ~]# unzip  prometheus.zip
[root@k8s-master ~]# cd prometheus/
[root@k8s-master prometheus]# ll
total 68
-rw-r--r-- 1 root root 5124 Nov 24  2019 prometheus-configmap.yaml
-rw-r--r-- 1 root root 1080 Nov 23  2019 prometheus-rbac.yaml
-rw-r--r-- 1 root root 4884 Nov 24  2019 prometheus-rules.yaml
-rw-r--r-- 1 root root  392 Nov 23  2019 prometheus-service.yaml
-rw-r--r-- 1 root root 3257 Jul 30 14:28 prometheus-statefulset.yaml

#当前只需要用到prometheus的yaml文件
kubectl apply -f prometheus-rbac.yaml 
kubectl apply -f prometheus-configmap.yaml 
kubectl apply -f prometheus-rules.yaml 
kubectl apply -f prometheus-service.yaml 
kubectl apply -f prometheus-statefulset.yaml

#查看Pod和svc资源并进行访问
[root@k8s-master ~]# kubectl get pod,svc -n kube-system 
NAME                                           READY   STATUS    RESTARTS   AGE
...
pod/prometheus-0                               2/2     Running   0          10m

NAME                              TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)                       AGE
service/prometheus                NodePort    10.10.82.85     <none>        9090:30090/TCP                10m

访问Prometheus UI:http://NdeIP:30090

3. 基于prometheus采集QPS速率指标测试

部署一个应用

[root@k8s-master hpa]# cat metrics-app.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: metrics-app
  name: metrics-app
spec:
  replicas: 3
  selector:
    matchLabels:
      app: metrics-app
  template:
    metadata:
      labels:
        app: metrics-app
      annotations:
        prometheus.io/scrape: "true"    #表明可以采集Pod
        prometheus.io/port: "80"    #采集Pod允许访问的端口
        prometheus.io/path: "/metrics"  #采集Pod要连接的url地址
    spec:
      containers:
      - image: 245684979/metrics-app
        name: metrics-app
        ports:
        - name: web
          containerPort: 80
        resources:
          requests:
            cpu: 200m
            memory: 256Mi
        readinessProbe:
          httpGet:
            path: /
            port: 80
          initialDelaySeconds: 3
          periodSeconds: 5
        livenessProbe:
          httpGet:
            path: /
            port: 80
          initialDelaySeconds: 3
          periodSeconds: 5
---
apiVersion: v1
kind: Service
metadata:
  name: metrics-app
  labels:
    app: metrics-app
spec:
  ports:
  - name: web
    port: 80
    targetPort: 80
  selector:
    app: metrics-app

[root@k8s-master hpa]# kubectl apply -f metrics-app.yaml

该metrics-app暴露了一个Prometheus指标接口,可以通过访问service看到:

[root@k8s-master hpa]# kubectl get pod,svc
NAME                                                READY   STATUS    RESTARTS   AGE
pod/metrics-app-b4d7dd845-5h5r9                     1/1     Running   0          2m49s
pod/metrics-app-b4d7dd845-dgmlc                     1/1     Running   0          2m49s
pod/metrics-app-b4d7dd845-vmdf5                     1/1     Running   0          2m49

NAME                     TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
service/metrics-app      ClusterIP   10.10.81.19     <none>        80/TCP         2m49s

[root@k8s-master hpa]# curl 10.10.81.19
Hello! My name is metrics-app-b4d7dd845-dgmlc. The last 10 seconds, the average QPS has been 0.5. Total requests served: 44

[root@k8s-master hpa]# curl 10.10.81.19/metrics
# HELP http_requests_total The amount of requests in total
# TYPE http_requests_total counter
http_requests_total 210 #当前Pod累计的请求值
# HELP http_requests_per_second The amount of requests per second the latest ten seconds
# TYPE http_requests_per_second gauge
http_requests_per_second 0.5    #10s之内有0.5个http请求

在prometheus的界面上可以请求到数据

创建HPA策略

[root@k8s-master hpa]# vim app-hpa-v2.yaml
apiVersion: autoscaling/v2beta2
kind: HorizontalPodAutoscaler
metadata:
  name: metrics-app-hpa 
  namespace: default
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: metrics-app
  minReplicas: 1
  maxReplicas: 10
  metrics:
  - type: Pods
    pods:
      metric:
        name: http_requests_per_second
      target:
        type: AverageValue
        averageValue: 800m   # 800m 即0.8个/秒


[root@k8s-master hpa]# kubectl apply -f app-hpa-v2.yaml
[root@k8s-master hpa]# kubectl get hpa
NAME              REFERENCE                TARGETS          MINPODS   MAXPODS   REPLICAS   AGE
metrics-app-hpa   Deployment/metrics-app   <unknown>/800m   1         10        3          15m

这里使用Prometheus提供的指标测试来测试自定义指标(QPS)的自动缩放。
获取不到值:<unknown>

但是 prometheus采集到的metrics并不能直接给k8s用,因为两者数据格式不兼容,还需要另外一个组件(k8s-prometheus-adpater),将prometheus的metrics 数据格式转换成k8s API接口能识别的格式,转换以后,因为是自定义API,所以还需要用Kubernetes aggregator在主APIServer中注册,以便直接通过/apis/来访问。

4. 部署 Custom Metrics Adapter

https://github.com/DirectXMan12/k8s-prometheus-adapter
该 PrometheusAdapter 有一个稳定的Helm Charts,可以直接使用helm的包。

准备好helm的环境:
上文已经安装好helm包管理工具了

[root@k8s-master ~]# helm repo list
NAME    URL                                     
stable  http://mirror.azure.cn/kubernetes/charts
[root@k8s-master ~]# helm list 

部署prometheus-adapter
指定prometheus地址:

helm install prometheus-adapter stable/prometheus-adapter --namespace kube-system --set prometheus.url=http://prometheus.kube-system,prometheus.port=9090

[root@k8s-master ~]# helm list -n kube-system
NAME                NAMESPACE   REVISION    UPDATED                                 STATUS      CHART                       APP VERSION
prometheus-adapter  kube-system 1           2020-07-30 18:04:42.895949887 +0800 CST deployed    prometheus-adapter-2.5.0    v0.7.0
[root@k8s-master ~]# kubectl get pods -n kube-system
NAME                                       READY   STATUS    RESTARTS   AGE
prometheus-adapter-56c85455f8-rrmxc        0/1     Running   0          40s

保证适配器注册到APIServer:

[root@k8s-master ~]# kubectl get apiservices |grep custom 
v1beta1.custom.metrics.k8s.io          kube-system/prometheus-adapter   True        86s

#输出监控指标
kubectl get --raw "/apis/custom.metrics.k8s.io/v1beta1"

5. 配置适配器收集特定的指标

当创建好HPA还没结束,因为适配器还不知道你要什么指标(http_requests_per_second),HPA也就获取不到Pod提供指标。
ConfigMap在default名称空间中编辑prometheus-adapter ,并在该rules: 部分的顶部添加一个新的seriesQuery

kubectl edit cm prometheus-adapter -n kube-system
...
    rules:
    - seriesQuery: 'http_requests_total{kubernetes_namespace!="",kubernetes_pod_name!=""}'
      resources:
        overrides:
          kubernetes_namespace: {resource: "namespace"}
          kubernetes_pod_name: {resource: "pod"}
      name:
        matches: "^(.*)_total"
        as: "${1}_per_second"
      metricsQuery: 'sum(rate(<<.Series>>{<<.LabelMatchers>>}[2m])) by (<<.GroupBy>>)'
...

该规则将http_requests在2分钟的间隔内收集该服务的所有Pod的平均速率。

需要重建prometheus-adapter的Pod才能获取到监控指标

kubectl delete pod -n kube-system prometheus-adapter-56c85455f8-zth2c

[root@k8s-master hpa]# kubectl get hpa
NAME             REFERENCE               TARGETS     MINPODS   MAXPODS   REPLICAS   AGE
metrics-app-hpa  Deployment/metrics-app  416m/800m   1         10        2          34m

测试一下 API:

kubectl get --raw "/apis/custom.metrics.k8s.io/v1beta1/namespaces/default/pods/*/http_requests_per_second"

进行压力测试

ab -n 100000 -c 100  http://10.10.81.19/metrics

查看HPA状态:

[root@k8s-master ~]# kubectl get hpa
NAME            REFERENCE              TARGETS       MINPODS  MAXPODS  REPLICAS   AGE
metrics-app-hpa Deployment/metrics-app 254176m/800m  1        10       4          6m29s

[root@k8s-master ~]# kubectl describe hpa metrics-app-hpa
  Normal  SuccessfulRescale  53s   horizontal-pod-autoscaler  New size: 4; reason: pods metric http_requests_per_second above target
  Normal  SuccessfulRescale  37s   horizontal-pod-autoscaler  New size: 8; reason: pods metric http_requests_per_second above target
  Normal  SuccessfulRescale  22s   horizontal-pod-autoscaler  New size: 10; reason: pods metric http_requests_per_second above target
  
[root@k8s-master ~]# kubectl get pod
NAME                                     READY   STATUS              RESTARTS   AGE
metrics-app-b4d7dd845-2mr5c              0/1     ContainerCreating   0          50s
metrics-app-b4d7dd845-bndtx              0/1     Running             0          50s
metrics-app-b4d7dd845-dgmlc              1/1     Running             2          16h
metrics-app-b4d7dd845-dlbjn              1/1     Running             0          80s
metrics-app-b4d7dd845-fvmlp              0/1     Running             0          65s
metrics-app-b4d7dd845-ldqwg              1/1     Running             0          65s
metrics-app-b4d7dd845-npkzg              1/1     Running             0          65s
metrics-app-b4d7dd845-vmdf5              1/1     Running             1          16h
metrics-app-b4d7dd845-zpvdr              1/1     Running             0          80s
metrics-app-b4d7dd845-zx66k              1/1     Running             0          65s

6. 总结

图片来自网络
  1. 通过/metrics收集每个Pod的http_request_total指标;
  2. prometheus将收集到的信息汇总;
  3. APIServer定时从Prometheus查询,获取request_per_second的数据;
  4. HPA定期向APIServer查询以判断是否符合配置的autoscaler规则;
  5. 如果符合autoscaler规则,则修改Deployment的ReplicaSet副本数量进行伸缩。
  6. 监控的是所有Pod的平均值,是一个deployment中部署的。
  7. 暴露的/metrics接口需要开发在代码框架中提前定义好用prometheus采集相关的Url接口。也有一些应用默认自带prometheus的监控接口。可以去查找prometheus针对于应用程序暴露指标的客户端。
上一篇下一篇

猜你喜欢

热点阅读