K8S安装部署配置

木屋管理员
木屋管理员
发布于 2024-02-05 / 174 阅读
0
0

K8S安装部署配置

基本命令

含义 命令
部署应用 kubectl apply -f app.yaml
查看 pod kubectl get pod -o wide
查看 log kubectl logs pod-name
查看 pod 详情 kubectl describe pod pod-name
查看deployment kubectl get deployment
查看service kubectl get svc
查看service详情 kubectl describe svc test-k8s
获取service的endpoints的信息 kubectl get endpoints
获取service的endpoints的详细信息 kubectl get endpoints service-name -o yaml
查看statefulset的状态 kubectl get statefulsets
删除全部资源 kubectl delete all --all
删除部署 kubectl delete deployment test-k8s
删除service kubectl delete svc test-k8s
进入 Pod 容器终端, -c container-name 可以指定进入哪个容器。 kubectl exec -it pod-name – bash
伸缩扩展副本 kubectl scale deployment test-k8s --replicas=5
把集群内端口映射到节点 kubectl port-forward pod-name 8090:8080
查看部署的ingress kubectl get ingress
查看RC和RS kubectl get rc , kubectl get rs
删除对应POD的RC和RS kubectl delete rc name ,kubectl delete rs name
检查您可以使用的资源 kubectl api-resources
编辑服务器上定义的资源 Kubectl edit (RESOURCE/NAME -f FILENAME)
编辑名为“mysql”的service kubectl edit svc/mysql
以YAML格式输出编辑deployment“mydeployment”,并将修改的配置保存在annotation中 kubectl edit deployment/mydeployment -o yaml --save-config
删除POD kubectl delete pod name

基础概念

Pod里是容器,Pod被ReplicaSet管理,ReplicaSet控制pod的数量;ReplicaSet被Deployment管理,Deployment控制pod应用的升级、回滚,当然也能控制pod的数量。Service提供一个统一固定入口,负责将前端请求转发给Pod。

Pod 是可以在 Kubernetes中创建和管理的、最小的可部署的计算单元。Pod中包含了一个或多个容器,还包括了存储、网络等各个容器共享的资源。Pod支持多种容器环境,Docker则是最流行的容器环境。

每个Pod中都有一个pause容器,pause是Kubernetes基础设施的一部分,Kubernetes管理的所有pod里,pause容器是第一个启动的,用于实现Kubernetes集群里pod之间的网络通讯。

ReplicaSet 的目的是维护一组在任何时候都处于运行状态的 Pod 副本的稳定集合。 因此,它通常用来保证给定数量的、完全相同的 Pod 的可用性。(确保任何时间都有指定数量的 Pod 副本在运行)

Label(标签)是Kubernetes系统中的一个核心概念。Label以key/value键值对的形式附加到各种对象上,如Pod、Service、RC、Node等。Label定义了这些对象的可识别属性,用来对它们进行管理和选择。Label可以在创建时附加到对象上,也可以在对象创建后通过API进行管理。
在为对象定义好Label后,其他对象就可以使用Label Selector(选择器)来定义其作用的对象了。

Deployment (部署)提供了一种对 Pod 和 ReplicaSet 的管理方式,每一个 Deployment 都对应集群中的一次部署,是非常常见的 Kubernetes 对象。
Deployment 是一个比 RS 应用模式更广的 API 对象,可以用来创建一个新的服务,更新一个新的服务,也可以用来滚动升级一个服务。滚动升级一个服务,滚动升级一个服务,实际是创建一个新的 RS,然后逐渐将新 RS 中副本数增加到理想状态,将旧 RS 中的副本数减小到 0 的复合操作;这样一个复合操作用一个 RS 是不太好描述的,所以用一个更通用的 Deployment 来描述。

服务(Service) RC、RS 和 Deployment 只是保证了支撑服务的微服务 Pod 的数量。但是没有解决如何访问这些服务的问题。一个 Pod 只是一个运行服务的实例,随时可能在节点上停止,然后再新的节点上用一个新的 IP 启动一个新的 Pod,因此不能使用确定的 IP 和端口号提供服务。这对于业务来说,就不能根据 Pod 的 IP 作为业务调度。kubernetes 就引入了 Service 的概 念,它为 Pod 提供一个入口,主要通过 Labels 标签来选择后端Pod,这时候不论后端 Pod 的 IP 地址如何变更,只要 Pod 的 Labels 标签没变,那么 业务通过 service 调度就不会存在问题。同时 service 对绑定的 Pod 提供了负载均衡的功能,我们业务直接使用 service 即可。
当声明Service的时候,会自动生成一个cluster IP,这个IP是虚拟IP。我们就可以通过这个IP来访问后端的Pod,当然,如果集群配置了DNS服务,比如现在 的CoreDNS,那么也可以通过Service的名字来访问,它会通过DNS自动解析Service的IP地址。

Service 对外暴露服务的方式
1、ClusterIP (默认) :在集群的内部 IP 上公开 Service 。这种类型使得 Service 只能从集群内访问,一般这种类型的 Service 上层会挂一个 Ingress,通过 Ingress 暴露服务;
2、NodePort:在每个选定 Node 的相同端口上公开 Service,使用 `:` 即可从集群外部访问 Service;
3、LoadBalancer:使用云厂商的 K8S 集群,即可使用这种方式的暴露 Service,自建的服务一般不支持。使用 LoadBalancer ,会生成一个 IP 地址,通过这个即可访问 Service, 通知这个 IP 也是高可用的;
4、ExternalName: 通过返回带有该名称的 CNAME 记录,使用任意名称(由 spec 中的externalName指定)公开 Service。不使用代理。这种类型需要kube-dns的v1.7或更高版本。

什么是 CNAME:这种记录允许您将多个名字映射到同一台计算机。
例如:当您拥有多个域名需要指向同一服务器IP,此时您就可以将一个域名做A记录指向服务器IP,然后将其他的域名做别名(即CNAME)到A记录的域名上;那么当您的服务器IP地址变更时,您就可以不必对一个一个域名做更改指向了,只需要更改A记录的那个域名到服务器新IP上,其他做别名(即CNAME)的那些域名的指向将自动更改到新的IP地址上(以上操作均需要在DNS处执行)。

Ingress 是反向代理规则,用来规定 HTTP/S 请求应该被转发到哪个 Service 上,比如根据请求中不同的 Host 和 url 路径让请求落到不同的 Service 上。
`Ingress Controller` 就是一个反向代理程序,它负责解析 Ingress 的反向代理规则,如果 Ingress 有增删改的变动,所有的 `Ingress Controller` 都会及时更新自己相应的转发规则,当 `Ingress Controller` 收到请求后就会根据这些规则将请求转发到对应的 Service。
Kubernetes 并没有自带 `Ingress Controller`,它只是一种标准,具体实现有多种,需要自己单独安装,常用的是 `Nginx Ingress Controller` 和 `Traefik Ingress Controller`。

[!note]
port
port是k8s集群内部访问service的端口,即通过clusterIP: port可以访问到某个service。
仅限于集群节点内互相访问,外部无法访问。
nodePort
nodePort是外部访问k8s集群中service的端口,通过nodeIP: nodePort可以从外部访问到某个service。
targetPort
targetPort是pod的端口,从port和nodePort来的流量经过kube-proxy流入到后端pod的targetPort上,最后进入容器。
targetPort和containerPort必须一致。
containerPort
containerPort是pod内部容器的端口,targetPort映射到containerPort。

命名空间(Namespace)

命名空间为 Kubernetes 集群提供虚拟的隔离作用,Kubernetes 集群初始有两个命名空间,分别是默认命名空间 default 和系统命名空间 kube-system,除此以外,管理员可以可以创建新的命名空间满足需要。

名字空间适用于存在很多跨多个团队或项目的用户的场景。对于只有几到几十个用户的集群,根本不需要创建或考虑名字空间。

deployment和service yaml:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  labels:
    app: nginx
spec:
  replicas: 2
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.7.9
        ports:
        - containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: nginx-service
spec:
  ports:
  - port: 81   
    protocol: TCP
    targetPort: 80
  selector:
    app: nginx
  type: NodePort

Deployment:

apiVersion 是当前配置格式的版本。

kind 是要创建的资源类型,这里分别创建了Deployment和Service。

metadata 是该资源的元数据,name 是必需的元数据项。

spec 部分是 Deployment 的规格说明。

replicas 指明副本数量,默认为 1。

matchLabels 指匹配的pod的标签是什么。

template 定义 Pod 的模板,这是配置文件的重要部分。

metadata 定义 Pod 的元数据,至少要定义一个 label。label 的 key 和 value 可以任意指定。

spec 描述 Pod 的规格,此部分定义 Pod 中每一个容器的属性,name 和 image 是必需的。

Service:

port Service 服务暴露的端口

targetPort 容器暴露的端口

type Service的类型

kubectl create:

  • 是祈使式命令,明确告诉k8s要创建的资源或者对象
  • 首先删除集群中现有的资源,然后重新根据yaml文件生成新的资源对象
  • yaml文件必须是完整的配置
  • yaml文件中的所有字段都会被create
  • 在没有改动yaml文件时,使用同一个yaml文件执行命令kubectl replace,将不会成功(fail掉),因为缺少相关改动信息。

kubectl apply

  • 是声明式命令,apply不告诉k8s具体要干什么,而是kubectl根据yaml文件自动探测要做哪些操作,如果不存在则create,如果存在则对比差异,进行更新。
  • 根据yaml文件中包含的字段,直接升级集群中的现有资源对象
  • yaml文件可以不完整,只写需要修改的字段
  • 只有要改动的字段才会被apply
  • 在只改动了yaml文件中的某些声明时,而不是全部改动,你可以使用kubectl apply

K8S之yaml 文件详解

Kubernetes 支持 YAML 和 JSON格式 管理资源对象

  • JSON 格式:主要用于 api 接口之间消息的传递
  • YAML 格式:用于配置和管理,YAML是一种简洁的非标记性语言,内容格式人性化,较易读。

YAML语法格式:

  • 大小写敏感;
  • 使用缩进表示层级关系;不支持Tab键制表符缩进,只使用空格缩进;
  • 缩进的空格数目不重要,只要相同层级的元素左侧对齐即可,通常开头缩进两个空格;
  • 字符后缩进一个空格,如冒号,逗号,短横杆(-) 等
  • "---" 表示YAML格式,一个文件的开始,用于分隔文件; 可以将创建多个资源写在同一个 yaml 文件中,用 -- 隔开,就不用写多个 yaml 文件了。
  • "#” 表示注释;

yaml 文件的学习方法:

  • 多看别人(官方)写的,能读懂
  • 能照着现场的文件改着用
  • 遇到不懂的,善用 kubectl explain ...命令查.

deployment资源

简述

Deployment 为 Pod 和 ReplicaSet 提供了一个声明式定义 (declarative) 方法,用来替代以前的 ReplicationController 更方便的管理应用。

作为最常用的 Kubernetes 对象,Deployment 经常会用来创建 ReplicaSet 和 Pod,我们往往不会直接在集群中使用 ReplicaSet 部署一个新的微服务,一方面是因为 ReplicaSet 的功能其实不够强大,一些常见的更新、扩容和缩容运维操作都不支持,Deployment 的引入就是为了支持这些复杂的操作。

典型用例如下:

  • 使用 Deployment 来创建 ReplicaSet。ReplicaSet 在后台创建 pod。检查启动状态,看它是成功还是失败。
  • 然后,通过更新 Deployment 的 PodTemplateSpec 字段来声明 Pod 的新状态。这会创建一个新的 ReplicaSet,Deployment 会按照控制的速率将 pod 从旧的 ReplicaSet 移动到新的 ReplicaSet 中。
  • 如果当前状态不稳定,回滚到之前的 Deployment revision。每次回滚都会更新 Deployment 的 revision。
  • 扩容 Deployment 以满足更高的负载。
  • 暂停 Deployment 来应用 PodTemplateSpec 的多个修复,然后恢复上线。
  • 根据 Deployment 的状态判断上线是否 hang 住了。
  • 清除旧的不必要的 ReplicaSet。

deployment 原理

  • 控制器模型

在Kubernetes架构中,有一个叫做kube-controller-manager的组件。这个组件,是一系列控制器的集合。其中每一个控制器,都以独有的方式负责某种编排功能。而Deployment正是这些控制器中的一种。它们都遵循Kubernetes中一个通用的编排模式,即:控制循环

用一段go语言伪代码,描述这个控制循环

for {
    实际状态 := 获取集群中对象X的实际状态
    期望状态 := 获取集群中对象X的期望状态
    if 实际状态 == 期望状态 {
        什么都不做
    }else{
        执行编排动作,将实际状态调整为期望状态
    }
}

在具体实现中,实际状态往往来自于Kubernetes集群本身。比如Kubelet通过心跳汇报的容器状态和节点状态,或者监控系统中保存的应用监控数据,或者控制器主动收集的它感兴趣的信息,这些都是常见的实际状态的来源;期望状态一般来自用户提交的YAML文件,这些信息都保存在Etcd中

对于Deployment,它的控制器简单实现如下:

  1. Deployment Controller从Etcd中获取到所有携带 “app:nginx”标签的Pod,然后统计它们的数量,这就是实际状态
  2. Deployment对象的replicas的值就是期望状态
  3. Deployment Controller将两个状态做比较,然后根据比较结果,确定是创建Pod,还是删除已有Pod
  • 滚动更新

Deployment滚动更新的实现,依赖的是Kubernetes中的ReplicaSet

Deployment控制器实际操纵的,就是Replicas对象,而不是Pod对象。对于Deployment、ReplicaSet、Pod它们的关系如下图:

ReplicaSet负责通过“控制器模式”,保证系统中Pod的个数永远等于指定的个数。这也正是Deployment只允许容器的restartPolicy=Always的主要原因:只有容器能保证自己始终是running状态的前提下,ReplicaSet调整Pod的个数才有意义。

Deployment同样通过控制器模式,操作ReplicaSet的个数和属性,进而实现“水平扩展/收缩”和“滚动更新”两个编排动作对于“水平扩展/收缩”的实现,Deployment Controller只需要修改replicas的值即可。用户执行这个操作的指令如下:

kubectl scale deployment nginx-deployment --replicas=4

Deployment.yaml 文件解析

apiVersion: apps/v1		# 指定api版本,此值必须在kubectl api-versions中。业务场景一般首选”apps/v1“
kind: Deployment		# 指定创建资源的角色/类型   
metadata:  		# 资源的元数据/属性 
  name: demo  	# 资源的名字,在同一个namespace中必须唯一
  namespace: default 	# 部署在哪个namespace中。不指定时默认为default命名空间
  labels:  		# 自定义资源的标签
    app: demo
    version: stable
  annotations:  # 自定义注释列表
    name: string
spec: 	# 资源规范字段,定义deployment资源需要的参数属性,诸如是否在容器失败时重新启动容器的属性
  replicas: 1 	# 声明副本数目
  revisionHistoryLimit: 3 	# 保留历史版本
  selector: 	# 标签选择器
    matchLabels: 	# 匹配标签,需与上面的标签定义的app保持一致
      app: demo
      version: stable
  strategy: 	# 策略
    type: RollingUpdate 	# 滚动更新策略
    rollingUpdate: 			# 滚动更新
      maxSurge: 1 			# 滚动升级时最大额外可以存在的副本数,可以为百分比,也可以为整数
      maxUnavailable: 0 	# 在更新过程中进入不可用状态的 Pod 的最大值,可以为百分比,也可以为整数
  template: 	# 定义业务模板,如果有多个副本,所有副本的属性会按照模板的相关配置进行匹配
    metadata: 	# 资源的元数据/属性 
      annotations: 		# 自定义注解列表
        sidecar.istio.io/inject: "false" 	# 自定义注解名字
      labels: 	# 自定义资源的标签
        app: demo	# 模板名称必填
        version: stable
    spec: 	# 资源规范字段
      restartPolicy: Always		# Pod的重启策略。[Always | OnFailure | Nerver]
      							# Always :在任何情况下,只要容器不在运行状态,就自动重启容器。默认
      							# OnFailure :只在容器异常时才自动容器容器。
      							  # 对于包含多个容器的pod,只有它里面所有的容器都进入异常状态后,pod才会进入Failed状态
      							# Nerver :从来不重启容器
      nodeSelector:   	# 设置NodeSelector表示将该Pod调度到包含这个label的node上,以key:value的格式指定
        caas_cluster: work-node
      containers:		# Pod中容器列表
      - name: demo 		# 容器的名字   
        image: demo:v1 		# 容器使用的镜像地址   
        imagePullPolicy: IfNotPresent 	# 每次Pod启动拉取镜像策略
                                      	  # IfNotPresent :如果本地有就不检查,如果没有就拉取。默认 
                                      	  # Always : 每次都检查
                                      	  # Never : 每次都不检查(不管本地是否有)
        command: [string] 	# 容器的启动命令列表,如不指定,使用打包时使用的启动命令
        args: [string]     	# 容器的启动命令参数列表
            # 如果command和args均没有写,那么用Docker默认的配置
            # 如果command写了,但args没有写,那么Docker默认的配置会被忽略而且仅仅执行.yaml文件的command(不带任何参数的)
            # 如果command没写,但args写了,那么Docker默认配置的ENTRYPOINT的命令行会被执行,但是调用的参数是.yaml中的args
            # 如果如果command和args都写了,那么Docker默认的配置被忽略,使用.yaml的配置
        workingDir: string  	# 容器的工作目录
        volumeMounts:    	# 挂载到容器内部的存储卷配置
        - name: string     	# 引用pod定义的共享存储卷的名称,需用volumes[]部分定义的的卷名
          mountPath: string    	# 存储卷在容器内mount的绝对路径,应少于512字符
          readOnly: boolean    	# 是否为只读模式
        - name: string
          configMap: 		# 类型为configMap的存储卷,挂载预定义的configMap对象到容器内部
            name: string
            items:
            - key: string
              path: string
        ports:	# 需要暴露的端口库号列表
          - name: http 	# 端口号名称
            containerPort: 8080 	# 容器开放对外的端口 
          # hostPort: 8080	# 容器所在主机需要监听的端口号,默认与Container相同
            protocol: TCP 	# 端口协议,支持TCP和UDP,默认TCP
        env:    # 容器运行前需设置的环境变量列表
        - name: string     # 环境变量名称
          value: string    # 环境变量的值
        resources: 	# 资源管理。资源限制和请求的设置
          limits: 	# 资源限制的设置,最大使用
            cpu: "1" 		# CPU,"1"(1核心) = 1000m。将用于docker run --cpu-shares参数
            memory: 500Mi 	# 内存,1G = 1024Mi。将用于docker run --memory参数
          requests:  # 资源请求的设置。容器运行时,最低资源需求,也就是说最少需要多少资源容器才能正常运行
            cpu: 100m
            memory: 100Mi
        livenessProbe: 	# pod内部的容器的健康检查的设置。当探测无响应几次后将自动重启该容器
        				  # 检查方法有exec、httpGet和tcpSocket,对一个容器只需设置其中一种方法即可
          httpGet: # 通过httpget检查健康,返回200-399之间,则认为容器正常
            path: /healthCheck 	# URI地址。如果没有心跳检测接口就为/
            port: 8089 		# 端口
            scheme: HTTP 	# 协议
            # host: 127.0.0.1 	# 主机地址
        # 也可以用这两种方法进行pod内容器的健康检查
        # exec: 		# 在容器内执行任意命令,并检查命令退出状态码,如果状态码为0,则探测成功,否则探测失败容器重启
        #   command:   
        #     - cat   
        #     - /tmp/health   
        # 也可以用这种方法   
        # tcpSocket: # 对Pod内容器健康检查方式设置为tcpSocket方式
        #   port: number 
          initialDelaySeconds: 30 	# 容器启动完成后首次探测的时间,单位为秒
          timeoutSeconds: 5 	# 对容器健康检查等待响应的超时时间,单位秒,默认1秒
          periodSeconds: 30 	# 对容器监控检查的定期探测间隔时间设置,单位秒,默认10秒一次
          successThreshold: 1 	# 成功门槛
          failureThreshold: 5 	# 失败门槛,连接失败5次,pod杀掉,重启一个新的pod
        readinessProbe: 		# Pod准备服务健康检查设置
          httpGet:
            path: /healthCheck	# 如果没有心跳检测接口就为/
            port: 8089
            scheme: HTTP
          initialDelaySeconds: 30
          timeoutSeconds: 5
          periodSeconds: 10
          successThreshold: 1
          failureThreshold: 5
        lifecycle:		# 生命周期管理  
          postStart:	# 容器运行之前运行的任务  
            exec:  
              command:  
                - 'sh'  
                - 'yum upgrade -y'  
          preStop:		# 容器关闭之前运行的任务  
            exec:  
              command: ['service httpd stop']
      initContainers:		# 初始化容器
      - command:
        - sh
        - -c
        - sleep 10; mkdir /wls/logs/nacos-0
        env:
        image: {{ .Values.busyboxImage }}
        imagePullPolicy: IfNotPresent
        name: init
        volumeMounts:
        - mountPath: /wls/logs/
          name: logs
      volumes:
      - name: logs
        hostPath:
          path: {{ .Values.nfsPath }}/logs
      volumes: 	# 在该pod上定义共享存储卷列表
      - name: string     	# 共享存储卷名称 (volumes类型有很多种)
        emptyDir: {}     	# 类型为emtyDir的存储卷,与Pod同生命周期的一个临时目录。为空值
      - name: string
        hostPath:      	# 类型为hostPath的存储卷,表示挂载Pod所在宿主机的目录
          path: string    # Pod所在宿主机的目录,将被用于同期中mount的目录
      - name: string
        secret: 			# 类型为secret的存储卷,挂载集群与定义的secre对象到容器内部
          scretname: string  
          items:     
          - key: string
            path: string
      imagePullSecrets: 	# 镜像仓库拉取镜像时使用的密钥,以key:secretkey格式指定
      - name: harbor-certification
      hostNetwork: false    # 是否使用主机网络模式,默认为false,如果设置为true,表示使用宿主机网络
      terminationGracePeriodSeconds: 30 	# 优雅关闭时间,这个时间内优雅关闭未结束,k8s 强制 kill
      dnsPolicy: ClusterFirst	# 设置Pod的DNS的策略。默认ClusterFirst
      		# 支持的策略:[Default | ClusterFirst | ClusterFirstWithHostNet | None]
      		# Default : Pod继承所在宿主机的设置,也就是直接将宿主机的/etc/resolv.conf内容挂载到容器中
      		# ClusterFirst : 默认的配置,所有请求会优先在集群所在域查询,如果没有才会转发到上游DNS
      		# ClusterFirstWithHostNet : 和ClusterFirst一样,不过是Pod运行在hostNetwork:true的情况下强制指定的
      		# None : 1.9版本引入的一个新值,这个配置忽略所有配置,以Pod的dnsConfig字段为准
      affinity:  # 亲和性调试
        nodeAffinity: 	# 节点亲和力
          requiredDuringSchedulingIgnoredDuringExecution: 	# pod 必须部署到满足条件的节点上
            nodeSelectorTerms:	 	# 节点满足任何一个条件就可以
            - matchExpressions: 	# 有多个选项时,则只有同时满足这些逻辑选项的节点才能运行 pod
              - key: beta.kubernetes.io/arch
                operator: In
                values:
                - amd64
      tolerations:		# 污点容忍度
      - operator: "Equal"		# 匹配类型。支持[Exists | Equal(默认值)]。Exists为容忍所有污点
        key: "key1"
        value: "value1"
        effect: "NoSchedule"		# 污点类型:[NoSchedule | PreferNoSchedule | NoExecute]
        								# NoSchedule :不会被调度 
										# PreferNoSchedule:尽量不调度
										# NoExecute:驱逐节点

Deployment.yaml 配置项说明

  • ***livenessProbe:存活指针

用于判断 Pod(中的应用容器)是否健康,可以理解为健康检查。使用 livenessProbe 来定期的去探测,如果探测成功,则 Pod 状态可以判定为 Running;如果探测失败,可kubectl会根据Pod的重启策略来重启容器。

如果未给Pod设置 livenessProbe,则默认探针永远返回 Success。

执行 kubectl get pods 命令,输出信息中 STATUS 一列可以看到Pod是否处于Running状态。

livenessProbe使用场景:有些后端应用在出现某些异常的时候会有假死的情况,这种情况容器依然是running状态,但是应用是无法访问的,所以需要加入存活探测livenessProbe来避免这种情况的发生。

参考:https://blog.csdn.net/qq_41980563/article/details/122139923

  • ***readinessProbe:就绪指针

就绪的意思是已经准备好了,Pod 的就绪可以理解为这个 Pod 可以接受请求和访问。使用 readinessProbe 来定期的去探测,如果探测成功,则 Pod 的 Ready 状态判定为 True;如果探测失败,Pod 的 Ready 状态判定为 False。

与 livenessProbe 不同的是,kubelet 不会对 readinessProbe 的探测情况有重启操作。

当执行 kubectl get pods 命令,输出信息中 READY 一列可以看到 Pod 的 READY 状态是否为 True。

readinessProbe 使用场景:k8s 应用更新虽然是滚动升级方式,但是很多后端程序启动都比较久,容器起来了,但是服务未起来,而 k8s 只要容器起来了就会移除掉旧的容器,这种情况就会导致在更新发版的时候应用访问失败。这时候就需要配置 readinessProbe 就绪检测,保证新的 pod 已经能正常使用了才会移除掉旧的 pod。

  • ***initContainers:初始化容器

用于主容器启动时先启动可一个或多个初始化容器,如果有多个,那么这几个 Init Container 按照定义的顺序依次执行,只有所有的 Init Container 执行完后,主容器才会启动。由于一个 Pod 里的存储卷是共享的,所以 Init Container 里产生的数据可以被主容器使用到。

Init Container 可以在多种K8S资源里被使用到,如 Deployment、Daemon Set、Pet Set、Job 等,但归根结底都是在 Pod 启动时,在主容器启动前执行,做初始化工作。

  • ***tolerations:污点容忍度

节点污点:类似节点上的标签或注解信息,用来描述对应节点的元数据信息;污点定义的格式和标签、注解的定义方式很类似,都是用一个 key-value 数据来表示,不同于节点标签,污点的键值数据中包含对应污点的 effect,污点的 effect 是用于描述对应节点上的污点有什么作用;在 k8s 上污点有三种 effect(效用)策略,第一种策略是 NoSchedule,表示拒绝 pod 调度到对应节点上运行;第二种策略是 PreferSchedule,表示尽量不把 pod 调度到此节点上运行;第三种策略是 NoExecute,表示拒绝将 pod 调度到此节点上运行;该效用相比 NoSchedule 要严苛一点;从上面的描述来看,对应污点就是来描述拒绝pod运行在对应节点的节点属性。

pod 对节点污点的容忍度:pod 要想运行在对应有污点的节点上,对应 pod 就要容忍对应节点上的污点;pod 对节点污点的容忍度就是在对应 pod 中定义怎么去匹配节点污点;通常匹配节点污点的方式有两种,一种是等值(Equal)匹配,一种是存在性(Exists)匹配;等值匹配表示对应pod的污点容忍度,必须和节点上的污点属性相等,所谓污点属性是指污点的 key、value 以及 effect;即容忍度必须满足和对应污点的key、value 和 effect 相同,这样表示等值匹配关系,其操作符为 Equal;存在性匹配是指对应容忍度只需要匹配污点的 key 和 effect 即可,value 不纳入匹配标准,即容忍度只要满足和对应污点的 key 和 effect 相同就表示对应容忍度和节点污点是存在性匹配,其操作符为 Exists;

Service.yaml 文件解析

apiVersion: v1 	# 指定api版本,此值必须在kubectl api-versions中 
kind: Service 	# 指定创建资源的角色/类型 
metadata: 	# 资源的元数据/属性
  name: demo 	# 资源的名字,在同一个namespace中必须唯一
  namespace: default 	# 部署在哪个namespace中。不指定时默认为default命名空间
  labels: 		# 设定资源的标签
  - app: demo
  annotations:  # 自定义注解属性列表
  - name: string
spec: 	# 资源规范字段
  type: ClusterIP 	# service的类型,指定service的访问方式,默认ClusterIP。
      # ClusterIP类型:虚拟的服务ip地址,用于k8s集群内部的pod访问,在Node上kube-porxy通过设置的iptables规则进行转发
      # NodePort类型:使用宿主机端口,能够访问各个Node的外部客户端通过Node的IP和端口就能访问服务器
      # LoadBalancer类型:使用外部负载均衡器完成到服务器的负载分发,需要在spec.status.loadBalancer字段指定外部负载均衡服务器的IP,并同时定义nodePort和clusterIP用于公有云环境。
  clusterIP: string		#虚拟服务IP地址,当type=ClusterIP时,如不指定,则系统会自动进行分配,也可以手动指定。当type=loadBalancer,需要指定
  sessionAffinity: string	#是否支持session,可选值为ClietIP,默认值为空。ClientIP表示将同一个客户端(根据客户端IP地址决定)的访问请求都转发到同一个后端Pod
  ports:
    - port: 8080 	# 服务监听的端口号
      targetPort: 8080 	# 容器暴露的端口
      nodePort: int		# 当type=NodePort时,指定映射到物理机的端口号
      protocol: TCP 	# 端口协议,支持TCP或UDP,默认TCP
      name: http 	# 端口名称
  selector: 	# 选择器。选择具有指定label标签的pod作为管理范围
    app: demo
status:	# 当type=LoadBalancer时,设置外部负载均衡的地址,用于公有云环境    
  loadBalancer:	# 外部负载均衡器    
    ingress:
      ip: string	# 外部负载均衡器的IP地址
      hostname: string	# 外部负载均衡器的主机名

ingress.yaml 文件详解

apiVersion: extensions/v1beta1 		# 创建该对象所使用的 Kubernetes API 的版本     
kind: Ingress 		# 想要创建的对象的类别,这里为Ingress
metadata:
  name: showdoc		# 资源名字,同一个namespace中必须唯一
  namespace: op 	# 定义资源所在命名空间
  annotations: 		# 自定义注解
    kubernetes.io/ingress.class: nginx    	# 声明使用的ingress控制器
spec:
  rules:
  - host: showdoc.example.cn     # 服务的域名
    http:
      paths:
      - path: /      # 路由路径
        backend:     # 后端Service
          serviceName: showdoc		# 对应Service的名字
          servicePort: 80           # 对应Service的端口

开始准备

IP地址规划

检查MAC地址和product_uuid

你可以使用命令 ip link 或 ifconfig -a 来获取网络接口的 MAC 地址
可以使用 sudo cat /sys/class/dmi/id/product_uuid 命令对 product_uuid 校验
检查所有服务器路由可达,保证所有服务器互通。

查看必要端口是否开放

建议关闭防火墙

控制端

协议 方向 端口范围 目的 使用者
TCP 入站 6443 Kubernetes API server 所有
TCP 入站 2379-2380 etcd server client API kube-apiserver, etcd
TCP 入站 10250 Kubelet API 自身, 控制面
TCP 入站 10259 kube-scheduler 自身
TCP 入站 10257 kube-controller-manager 自身

工作节点

协议 方向 端口范围 目的 使用者
TCP 入站 10250 Kubelet API 自身, 控制面
TCP 入站 30000-32767 NodePort Services† 所有
firewall-cmd --permanent --zone=public --add-port=6443/tcp
firewall-cmd --permanent --zone=public --add-port=2379/tcp
firewall-cmd --permanent --zone=public --add-port=2380/tcp
firewall-cmd --permanent --zone=public --add-port=10250/tcp
firewall-cmd --permanent --zone=public --add-port=10259/tcp
firewall-cmd --permanent --zone=public --add-port=10257/tcp

firewall-cmd --permanent --zone=public --add-port=10250/tcp
firewall-cmd --permanent --zone=public --add-port=30000-32767/tcp
firewall-cmd --reload

禁用交换分区和selinux

[root@k8s-master yum.repos.d]# swapoff -a
[root@k8s-master yum.repos.d]# free -m
[root@k8s-master ~]# setenforce 0
[root@k8s-master ~]# getenforce
vim /etc/selinux/
###接着编辑/etc/fstab文件
vi /etc/fstab
将/dev/mapper/centos-swap swap swap default 0 0这一行前面加个#号将其注释掉。

重新命名主机名,并修改hosts文件

hostnamectl set-hostname k8s-master01
hostnamectl set-hostname k8s-node01
hostnamectl set-hostname k8s-node02
hostnamectl set-hostname k8s-node03
hostnamectl set-hostname k8s-node04
hostnamectl set-hostname k8s-node05

[root@k8s-master yum.repos.d]# cat >> /etc/hosts << EOF
192.168.123.19 k8s-master01
192.168.123.20 k8s-node01
192.168.123.21 k8s-node02
192.168.123.22 k8s-node03
EOF

修改一些内核参数,容器运行时前置

cat <<EOF | sudo tee /etc/modules-load.d/k8s.conf
overlay
br_netfilter
EOF

modprobe overlay
sudo modprobe br_netfilter

[root@k8s-master yum.repos.d]# cat <<EOF >>  /etc/sysctl.conf 
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
net.ipv4.ip_nonlocal_bind = 1
net.ipv4.ip_forward = 1
vm.swappiness=0
EOF
sysctl -p 让参数生效到内核里面

[root@k8s-master yum.repos.d]# sysctl -p
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
net.ipv4.ip_nonlocal_bind = 1
net.ipv4.ip_forward = 1
vm.swappiness = 0

通过运行以下指令确认 br_netfilter 和 overlay 模块被加载:

lsmod | grep br_netfilter
lsmod | grep overlay

在所有机器上安装docker

DOCKER版本要跟K8S版本兼容

安装必要的一些系统工具
yum install -y yum-utils device-mapper-persistent-data lvm2

添加软件源信息
yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
或
yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
更新并安装Docker-CE
yum install docker-ce-20.10.16-3.el7 docker-ce-cli-20.10.16-3.el7 containerd.io docker-compose-plugin -y
开启Docker服务
systemctl start docker
systemctl enable docker
配置文件修改
vim /etc/docker/daemon.json
{
  "log-driver":"json-file",
   "registry-mirrors": ["https://registry.cn-hangzhou.aliyuncs.com"],
  "exec-opts": ["native.cgroupdriver=systemd"],
  "log-opts":{"max-size":"500m", "max-file":"3"},
        "data-root":"/data/docker",
  "bip":"172.172.172.1/24"
}
systemctl daemon-reload
systemctl restart docker

安装kubadm,kubctl,kublet软件

你需要在每台机器上安装以下的软件包:

kubeadm:用来初始化集群的指令。

kubelet:在集群中的每个节点上用来启动 Pod 和容器等。

kubectl:用来与集群通信的命令行工具。

添加kubernetes yum软件源

[root@k8s-master ~]# cat > /etc/yum.repos.d/kubernetes.repo << EOF
[kubernetes]
name=Kubernetes
baseurl=https://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-x86_64
enabled=1
gpgcheck=0
repo_gpgcheck=0
gpgkey=https://mirrors.aliyun.com/kubernetes/yum/doc/yum-key.gpg https://mirrors.aliyun.com/kubernetes/yum/doc/rpm-package-key.gpg
EOF
安装kubeadm,kubelet,kubectl,并且指定版本,因为1.24的版本默认运行时环境不是docker了

yum install -y kubelet-1.23.16 kubeadm-1.23.16 kubectl-1.23.16

设置开机自启,因为kubelet是k8s在node节点上的代理,必须开机要运行的
[root@k8s-master ~]# systemctl enable  kubelet
Created symlink from /etc/systemd/system/multi-user.target.wants/kubelet.service to /usr/lib/systemd/system/kubelet.service.

部署kubernetes master

提前准备coredns:1.8.4的镜像,后面需要使用,需要在每台机器上下载镜像
[root@k8s-master ~]# docker pull  coredns/coredns:1.8.4
1.8.4: Pulling from coredns/coredns
c6568d217a00: Pull complete 
bc38a22c706b: Pull complete 
Digest: sha256:6e5a02c21641597998b4be7cb5eb1e7b02c0d8d23cce4dd09f4682d463798890
Status: Downloaded newer image for coredns/coredns:1.8.4
docker.io/coredns/coredns:1.8.4
[root@k8s-master ~]# docker images
REPOSITORY        TAG       IMAGE ID       CREATED         SIZE
coredns/coredns   1.8.4     8d147537fb7d   16 months ago   47.6MB
[root@k8s-master ~]# docker tag coredns/coredns:1.8.4 registry.aliyuncs.com/google_containers/coredns:v1.8.4
[root@k8s-master ~]# docker images
REPOSITORY                                        TAG       IMAGE ID       CREATED         SIZE
coredns/coredns                                   1.8.4     8d147537fb7d   16 months ago   47.6MB
registry.aliyuncs.com/google_containers/coredns   v1.8.4    8d147537fb7d   16 months ago   47.6MB

在master服务器上进行初始化操作
[root@k8s-master ~]# kubeadm init \
> --apiserver-advertise-address=192.168.123.19 \
> --image-repository registry.aliyuncs.com/google_containers \
> --service-cidr=10.1.0.0/16 \
> --pod-network-cidr=10.244.0.0/16

初始化,--kubernetes-version版本就是上面查询出来的,可以不写,默认会自动获取版本,--image-repository:默认是官网k8s.gcr.io,但是很慢,这里换成了阿里云的;--apiserver-advertise-address=192.168.0.113:这里的ip为master节点ip,记得更换。
# –image-repository string:    这个用于指定从什么位置来拉取镜像(1.13版本才有的),默认值是k8s.gcr.io,我们将其指定为国内镜像地址:registry.aliyuncs.com/google_containers
# –kubernetes-version string:  指定kubenets版本号,默认值是stable-1,会导致从https://dl.k8s.io/release/stable-1.txt下载最新的版本号,我们可以将其指定为固定版本(v1.22.1)来跳过网络请求。
# –apiserver-advertise-address  指明用 Master 的哪个 interface 与 Cluster 的其他节点通信。如果 Master 有多个 interface,建议明确指定,如果不指定,kubeadm 会自动选择有默认网关的 interface。
# –pod-network-cidr             指定 Pod 网络的范围。Kubernetes 支持多种网络方案,而且不同网络方案对  –pod-network-cidr有自己的要求,这里设置为10.244.0.0/16 是因为我们将使用 flannel 网络方案,必须设置成这个 CIDR。
• --service-cidr 集群内部虚拟网络,Pod统一访问入

[WARNING Firewalld]: firewalld is active, please ensure ports [6443 10250] are open or your cluster may not function correctly
[WARNING SystemVerification]: this Docker version is not on the list of validated versions: 23.0.1. Latest validated version: 20.10
关闭防火墙,降低DOCKER版本,yum list docker-ce --showduplicates | sort -r查看DOCKER版本
yum install docker-ce-20.10.16-3.el7 docker-ce-cli-20.10.16-3.el7 containerd.io docker-compose-plugin -y

[kubelet-check] The HTTP call equal to 'curl -sSL http://localhost:10248/healthz' failed with error: Get "http://localhost:10248/healthz": dial tcp [::1]:10248: connect: connection refused.
之前我的Docker是用yum安装的,docker的cgroup驱动程序默认设置为systemd。默认情况下Kubernetes cgroup为system,我们需要把服务器都更改Docker cgroup驱动
# 添加以下内容
vim /etc/docker/daemon.json
{
  "exec-opts": ["native.cgroupdriver=systemd"]
}

# 重启docker
systemctl restart docker
# 重新初始化
kubeadm reset # 先重置

完成初始化的新建文件和目录的操作,在master上完成 

mkdir -p $HOME/.kube
cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
chown $(id -u):$(id -g) $HOME/.kube/config

将node节点服务器加入k8s集群

kubeadm join 192.168.123.19:6443 --token ilc905.xyo6aa2har0u5qwy \
        --discovery-token-ca-cert-hash sha256:3c204e954e4294ab68af60aa73fe509d45807e7328d123afaa64a86cf6f85d9
在三台node节点服务器上都执行,初始化的时候会有个sha码
[root@k8s-node1 ~]# kubeadm join 192.168.123.19:6443 --token ilc905.xyo6aa2har0u5qwy --discovery-token-ca-cert-hash sha256:3c204e954e4294ab68af60aa73fe509d45807e7328d123afaa64a86cf6f85d9
[root@k8s-node2 ~]# kubeadm join 192.168.123.19:6443 --token ilc905.xyo6aa2har0u5qwy --discovery-token-ca-cert-hash sha256:3c204e954e4294ab68af60aa73fe509d45807e7328d123afaa64a86cf6f85d9
[root@k8s-node3 ~]# kubeadm join 192.168.123.19:6443 --token ilc905.xyo6aa2har0u5qwy --discovery-token-ca-cert-hash sha256:3c204e954e4294ab68af60aa73fe509d45807e7328d123afaa64a86cf6f85d9
在master上查看node节点信息
[root@k8s-master ~]# kubectl get nodes
NotReady 说明master和node节点之间的通信还是有问题的,容器之间通信还没有准备好

**如果添加WORDER节点**
执行kubeadm token create --print-join-command,重新生成 join 命令 可用来join work节点
kubeadm join 192.168.123.19:6443 --token 9zst8u.539a5blnu6htsjyn --discovery-token-ca-cert-hash sha256:3c204e954e4294ab68af60aa73fe509d45807e7328d123afaa64a86cf6f85d9

**如果需要添加master节点需要**
*  使用 kubeadm init phase upload-certs --upload-certs 重新生成certificate-key
   (这里需要注意老版本 需要用 kubeadm init phase upload-certs --experimental-upload-certs 生成)
kubeadm join 10.136.17.12:6443 --token abcdef.0123456789abcdef \
    --discovery-token-ca-cert-hash sha256:2fbacdf6a9473d5da1d98900f73cxxx772b12ac99017d6ae756d8c3cc \
    --control-plane --certificate-key f0725584c26c192478d266c4dc5804a1ss5d7b40257837eea0676d1972cca

删除工作节点node

一、删除node节点
 1、先查看一下这个node节点上的pod信息

 kubectl get nodes

 2、驱逐这个node节点上的pod

 kubectl drain node1 --delete-local-data --force --ignore-daemonsets

3、删除这个node节点 

 kubectl delete nodes node1 

4.在k8s-node01节点上执行以下命令删除所有数据(以上命令都是在控制节点执行)

kubeadm reset

# 或者手动删除,在node01这个节点上执行如下命令: (必须!!!否则会影响改机器以后加入新集群)

kubeadm reset
systemctl stop kubelet
systemctl stop docker
rm -rf /var/lib/cni/
rm -rf /var/lib/kubelet/*
rm -rf /etc/cni/
ifconfig cni0 down
ifconfig flannel.1 down
ifconfig docker0 down
ip link delete cni0
ip link delete flannel.1
systemctl start docker
systemctl start kubelet

如果不做上面的操作的话会导致这个节点上的pod无法启动,具体报错信息为:networkPlugin cni failed to set up pod “alertmanager-main-1_monitoring” network: failed to set bridge ad has an IP address different from 10.244.5.1/24 ,意思是已经集群网络cni已经有一个不同于10.244.51.1/24 的网络地址,所以需要执行上述命令重置节点网络。 

安装网络插件flannel(在master节点执行)

k8s的网络插件:作用就是实现不同宿主机之间pod的通信

1、flannel --》overlay --》vxlan

2、calico --》 ipip BGP

kube-flannel.yaml 文件需要自己去创建,内容如下:

---
kind: Namespace
apiVersion: v1
metadata:
  name: kube-flannel
  labels:
    pod-security.kubernetes.io/enforce: privileged
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: flannel
rules:
- apiGroups:
  - ""
  resources:
  - pods
  verbs:
  - get
- apiGroups:
  - ""
  resources:
  - nodes
  verbs:
  - list
  - watch
- apiGroups:
  - ""
  resources:
  - nodes/status
  verbs:
  - patch
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: flannel
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: flannel
subjects:
- kind: ServiceAccount
  name: flannel
  namespace: kube-flannel
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: flannel
  namespace: kube-flannel
---
kind: ConfigMap
apiVersion: v1
metadata:
  name: kube-flannel-cfg
  namespace: kube-flannel
  labels:
    tier: node
    app: flannel
data:
  cni-conf.json: |
    {
      "name": "cbr0",
      "cniVersion": "0.3.1",
      "plugins": [
        {
          "type": "flannel",
          "delegate": {
            "hairpinMode": true,
            "isDefaultGateway": true
          }
        },
        {
          "type": "portmap",
          "capabilities": {
            "portMappings": true
          }
        }
      ]
    }
  net-conf.json: |
    {
      "Network": "10.244.0.0/16",
      "Backend": {
        "Type": "vxlan"
      }
    }
---
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: kube-flannel-ds
  namespace: kube-flannel
  labels:
    tier: node
    app: flannel
spec:
  selector:
    matchLabels:
      app: flannel
  template:
    metadata:
      labels:
        tier: node
        app: flannel
    spec:
      affinity:
        nodeAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            nodeSelectorTerms:
            - matchExpressions:
              - key: kubernetes.io/os
                operator: In
                values:
                - linux
      hostNetwork: true
      priorityClassName: system-node-critical
      tolerations:
      - operator: Exists
        effect: NoSchedule
      serviceAccountName: flannel
      initContainers:
      - name: install-cni-plugin
       #image: flannelcni/flannel-cni-plugin:v1.1.0 for ppc64le and mips64le (dockerhub limitations may apply)
        image: docker.io/rancher/mirrored-flannelcni-flannel-cni-plugin:v1.1.0
        command:
        - cp
        args:
        - -f
        - /flannel
        - /opt/cni/bin/flannel
        volumeMounts:
        - name: cni-plugin
          mountPath: /opt/cni/bin
      - name: install-cni
       #image: flannelcni/flannel:v0.19.1 for ppc64le and mips64le (dockerhub limitations may apply)
        image: docker.io/rancher/mirrored-flannelcni-flannel:v0.19.1
        command:
        - cp
        args:
        - -f
        - /etc/kube-flannel/cni-conf.json
        - /etc/cni/net.d/10-flannel.conflist
        volumeMounts:
        - name: cni
          mountPath: /etc/cni/net.d
        - name: flannel-cfg
          mountPath: /etc/kube-flannel/
      containers:
      - name: kube-flannel
       #image: flannelcni/flannel:v0.19.1 for ppc64le and mips64le (dockerhub limitations may apply)
        image: docker.io/rancher/mirrored-flannelcni-flannel:v0.19.1
        command:
        - /opt/bin/flanneld
        args:
        - --ip-masq
        - --kube-subnet-mgr
        resources:
          requests:
            cpu: "100m"
            memory: "50Mi"
          limits:
            cpu: "100m"
            memory: "50Mi"
        securityContext:
          privileged: false
          capabilities:
            add: ["NET_ADMIN", "NET_RAW"]
        env:
        - name: POD_NAME
          valueFrom:
            fieldRef:
              fieldPath: metadata.name
        - name: POD_NAMESPACE
          valueFrom:
            fieldRef:
              fieldPath: metadata.namespace
        - name: EVENT_QUEUE_DEPTH
          value: "5000"
        volumeMounts:
        - name: run
          mountPath: /run/flannel
        - name: flannel-cfg
          mountPath: /etc/kube-flannel/
        - name: xtables-lock
          mountPath: /run/xtables.lock
      volumes:
      - name: run
        hostPath:
          path: /run/flannel
      - name: cni-plugin
        hostPath:
          path: /opt/cni/bin
      - name: cni
        hostPath:
          path: /etc/cni/net.d
      - name: flannel-cfg
        configMap:
          name: kube-flannel-cfg
      - name: xtables-lock
        hostPath:
          path: /run/xtables.lock
          type: FileOrCreate

部署flannel

所有机子都要运行安装 
[root@k8s-master ~]# kubectl apply -f kube-flannel.yaml
Warning: policy/v1beta1 PodSecurityPolicy is deprecated in v1.21+, unavailable in v1.25+
podsecuritypolicy.policy/psp.flannel.unprivileged created
clusterrole.rbac.authorization.k8s.io/flannel created
clusterrolebinding.rbac.authorization.k8s.io/flannel created
serviceaccount/flannel created
configmap/kube-flannel-cfg created
daemonset.apps/kube-flannel-ds created

还notready ; kubectl get pods --all-namespaces查看发现镜像拉取失败了,查看错误POD原因:
kubectl describe pod kube-flannel-ds-t6vlg --namespace=kube-flannel
kube-flannel   kube-flannel-ds-fskz8                  0/1     Init:ImagePullBackOff   0          21m
需要失败的节点服务器上手动拉取
docker pull docker.io/rancher/mirrored-flannelcni-flannel:v0.19.1
docker pull docker.io/rancher/mirrored-flannelcni-flannel-cni-plugin:v1.1.0

查看集群状态(状态为Ready就代表k8s部署成功)

[root@k8s-master ~]# kubectl get nodes
NAME         STATUS   ROLES                  AGE   VERSION
k8s-master   Ready    control-plane,master   21m   v1.23.6
k8s-node1    Ready    <none>                 20m   v1.23.6
k8s-node2    Ready    <none>                 20m   v1.23.6
k8s-node3    Ready    <none>                 20m   v1.23.6

查看各个节点详细信息

[root@k8s-master ~]# kubectl get pod -n kube-system
NAME                                 READY   STATUS    RESTARTS   AGE
coredns-6d8c4cb4d-92g7b              1/1     Running   0          20m
coredns-6d8c4cb4d-kl4q5              1/1     Running   0          20m
etcd-k8s-master                      1/1     Running   0          20m
kube-apiserver-k8s-master            1/1     Running   0          20m
kube-controller-manager-k8s-master   1/1     Running   0          20m
kube-proxy-422b5                     1/1     Running   0          19m
kube-proxy-6qpcz                     1/1     Running   0          19m
kube-proxy-ggnnt                     1/1     Running   0          20m
kube-proxy-vjcnc                     1/1     Running   0          19m
kube-scheduler-k8s-master            1/1     Running   0          20m

查看k8s里的命名空间有哪些(k8s自己创建的)

[root@k8s-master ~]# kubectl get ns
NAME              STATUS   AGE
default           Active   22m
kube-flannel      Active   10m
kube-node-lease   Active   22m
kube-public       Active   22m
kube-system       Active   22m

查看pod运行在哪个node上,

[root@k8s-master ~]# kubectl get pod -n kube-flannel 
NAME                    READY   STATUS    RESTARTS   AGE
kube-flannel-ds-c7crw   1/1     Running   0          29m
kube-flannel-ds-pr5pr   1/1     Running   0          29m
kube-flannel-ds-rphnc   1/1     Running   0          29m
kube-flannel-ds-v8rxz   1/1     Running   0          29m
[root@k8s-master ~]# kubectl get pod -n kube-flannel -o wide
NAME                    READY   STATUS    RESTARTS   AGE   IP               NODE         NOMINATED NODE   READINESS GATES
kube-flannel-ds-c7crw   1/1     Running   0          29m   192.168.44.211   k8s-node1    <none>           <none>
kube-flannel-ds-pr5pr   1/1     Running   0          29m   192.168.44.212   k8s-node2    <none>           <none>
kube-flannel-ds-rphnc   1/1     Running   0          29m   192.168.44.210   k8s-master   <none>           <none>
kube-flannel-ds-v8rxz   1/1     Running   0          29m   192.168.44.213   k8s-node3    <none>           <none>

dashboard部署

推荐网址:https://segmentfault.com/a/1190000023130407
执行如下命令,以安装 Kubernetes Dashboard
kubectl apply -f https://kuboard.cn/install-script/k8s-dashboard/v2.0.0-beta5.yaml

Kubernetes Dashboard 当前,只支持使用 Bearer Token登录。 由于 Kubernetes Dashboard 默认部署时,只配置了最低权限的 RBAC。
因此,我们要创建一个名为 admin-user 的 ServiceAccount,再创建一个 ClusterRolebinding,将其绑定到 Kubernetes 集群中默认初始化的 cluster-admin 这个 ClusterRole。
创建 Service Account 和 ClusterRoleBinding
kubectl apply -f https://kuboard.cn/install-script/k8s-dashboard/auth.yaml
获取Bearer Token
kubectl -n kubernetes-dashboard describe secret $(kubectl -n kubernetes-dashboard get secret | grep admin-user | awk '{print $1}')

eyJhbGciOiJSUzI1NiIsImtpZCI6InR6aXFpRm1JTGdPcWNsSTE5OVBHNFBkVW1wWHBidWFSM3pIc2V4UzJ2WFkifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJrdWJlcm5ldGVzLWRhc2hib2FyZCIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VjcmV0Lm5hbWUiOiJhZG1pbi11c2VyLXRva2VuLXdrZHY5Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZXJ2aWNlLWFjY291bnQubmFtZSI6ImFkbWluLXVzZXIiLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC51aWQiOiI1ZmVmZmQxMS01ZjI4LTQyMDctYmUwMy1lZmM2NjNmYzAyYjkiLCJzdWIiOiJzeXN0ZW06c2VydmljZWFjY291bnQ6a3ViZXJuZXRlcy1kYXNoYm9hcmQ6YWRtaW4tdXNlciJ9.lvBNFkMi_KrHIwPWjh8JdA0y-fjw4dsfBvHoAik05MiL409BfxL10LdOcfLanyc2S1YMdF7iUiVKEHgqziWPYRk3xjYWnEG7qqq6qqt23lNZ3r9N8520vMHlLdzIfgr6VnNFH5fuf2JHzBLlTrmnokE9LOPRV9X7HpmNkF3x1J2WEa02uVyflQNukqPC9xFi31dqendgXfd2tSfcDm80TEcnLvAS4iA3CsCcxkEJ0Kq_tPupepc9RNaZLYiSMcL-PTXRgd4RSVSkMF0r-GuTOq7Zu4KKYc4xurSeSRW3l_NEhLaVrYaC5Y9FeJ-AcAl01-p50LdEWbY_ShewDsA5Sg
执行 kubectl proxy 命令,使API server监听在本地的8001端口上
访问路径: http://localhost:8001/api/v1/namespaces/kubernetes-dashboard/services/https:kubernetes-dashboard:/proxy/
将上一个步骤中获得的 Token 输入到登录界面中,点击 Sign in 按钮,完成登录
如果想通过其它主机访问就需要指定监听的地址:
nohup kubectl proxy --address='0.0.0.0' --accept-hosts='^\*$' &
页面还是没有任何反应.
于是你想到按下F12, 打开浏览器的调试窗口"Networking". 发现登录是登录成功了, 但是config接口却返回了状态401.
原因是, 即便 proxy 允许外网访问, 但是从 dashboard-1.7.x 起, 只允许 localhost 访问. Github 有人提过 issue, 但是回复说这是 feature, 不是 bug, 怪只怪初学者不懂的太多.
如需要使用 nodePort 或 Ingress 的方式访问 Kubernetes Dashboard 请配置正确的 https 证书,或者使用 Firefox 浏览器,并忽略 HTTPS 校

使用Ingress对外发布Dashboard

查看dashboard默认的访问方式

我们可以看到默认是clusterip只能在集群内部访问,是无法在集群外部访问的。
# kubectl get pods,svc -n kubernetes-dashboard
NAME                                             READY   STATUS    RESTARTS   AGE
pod/dashboard-metrics-scraper-7c857855d9-chmm9   1/1     Running   0          74m
pod/kubernetes-dashboard-6b79449649-xgdph        1/1     Running   0          74m

NAME                                TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)    AGE
service/dashboard-metrics-scraper   ClusterIP   10.96.129.181   <none>        8000/TCP   74m
service/kubernetes-dashboard        ClusterIP   10.96.13.147    <none>        443/TCP    74m

检查Ingress Controller是否正常

# kubectl get pods -n ingress-nginx
NAME                             READY   STATUS    RESTARTS        AGE
ingress-nginx-controller-bnmpt   1/1     Running   25 (3d2h ago)   18d
ingress-nginx-controller-cfblk   1/1     Running   26 (3d1h ago)   18d

创建自签证书,并创建tls类型Secrets

# openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout kube-dashboard.key -out kube-dashboard.crt -subj "/CN=dashboard.kube.com/O=k8s.dashboard.local"
Generating a 2048 bit RSA private key
......+++
.......+++
writing new private key to 'kube-dashboard.key'
-----
创建tls类型的Secret为ingress提供配置。
# kubectl create secret tls dashboard-tls --key kube-dashboard.key --cert kube-dashboard.crt -n kubernetes-dashboard
secret/dashboard-tls created

查看secrets,可以看见类型为tls类型
# kubectl get secret -n kubernetes-dashboard
NAME                               TYPE                                  DATA   AGE
dashboard-tls                      kubernetes.io/tls                     2      15h
default-token-7d7z8                kubernetes.io/service-account-token   3      17h
kubernetes-dashboard-certs         Opaque                                0      17h
kubernetes-dashboard-csrf          Opaque                                1      17h
kubernetes-dashboard-key-holder    Opaque                                2      17h
kubernetes-dashboard-token-c2z56   kubernetes.io/service-account-token   3      17h

配置Ingress规则

# cat ingress-dashboard.yaml 
apiVersion: networking.k8s.io/v1   类型为v1
kind: Ingress
metadata:
  name: dashboard-ingress
  namespace: kubernetes-dashboard
  annotations:
    nginx.ingress.kubernetes.io/backend-protocol: "HTTPS"   #注意这里:必须指定后端服务为HTTPS服务。
spec:
  ingressClassName: "nginx"  控制器的类型为nginx
  tls:
  - hosts:    
    - k8s.dashboard.local   主机名
    secretName: dashboard-tls  这里引用创建的secrets
  rules:
  - host: k8s.dashboard.local   
    http:
      paths:
      - path: /
        pathType: Prefix   起始与根都进行代理。
        backend:   
          service:
            name: kubernetes-dashboard   service名称
            port:     后端端口
              number: 443
加载配置文件	  
# kubectl apply -f ingress-dashboard.yaml 
ingress.networking.k8s.io/dashboard-ingress created

检查Ingress配置文件

# kubectl describe ingress -n kubernetes-dashboard
Name:             dashboard-ingress
Namespace:        kubernetes-dashboard
Address:          xxxxx
Default backend:  default-http-backend:80 (<error: endpoints "default-http-backend" not found>)
TLS:
  dashboard-tls terminates k8s.dashboard.local
Rules:
  Host                 Path  Backends
  ----                 ----  --------
  k8s.dashboard.local    可以看到后端的服务已被代理到。
                       /   kubernetes-dashboard:443 (192.168.3.56:8443)
Annotations:           nginx.ingress.kubernetes.io/backend-protocol: HTTPS
Events:
  Type    Reason  Age               From                      Message
  ----    ------  ----              ----                      -------
  Normal  Sync    6s (x2 over 54s)  nginx-ingress-controller  Scheduled for sync

编辑本地hosts解析并访问UI

# echo "xxxxx k8s.dashboard.local"  >> /etc/hosts

登陆dashboard

方法一: kubectl port-forward

注意: 这种方法适合有正式ssl证书的情况. 如果没有, 先用方法二进行访问.

In an environment where one needs to access the dashboard from another host than the kubectl proxy's localhost,
 one can run: 

nohup kubectl port-forward --namespace kubernetes-dashboard service/kubernetes-dashboard 8080:443 --address 0.0.0.0 &

to access the dashboard through HTTPS

然后访问: https://ttg12:10443.

不过因为测试环境没有正式的ssl证书, 所以浏览器会拒绝访问. 需要采用方法二.

方法二: ssh端口转发

在工作电脑(需要能 ssh 连接 k8s master)上, 使用以下命令转发端口:

$ ssh -L localhost:8001:localhost:8001 -NT faceless@ttg12
faceless@ttg12's password:

输入master机器faceless账号的登录密码后, 转发成功.

GITLAB连接现有KUBERNETS

1. 准备工作

首先需要安装kubectl和helm客户端,使用的版本需要和Kubernetes集群的版本匹配。使用命令 kubectl version 可以查询到当前客户端和集群的版本。

2. 创建Kubernetes集群

在Kubernetes集群中创建命名空间和服务账号,用于GitLab的访问。

命名空间:

kubectl create namespace gitlab-managed-apps

服务账号:

kubectl create serviceaccount -n gitlab-managed-apps tiller

kubectl create clusterrolebinding tiller \
  --clusterrole cluster-admin \
  --serviceaccount=gitlab-managed-apps:tiller

3. 配置Kubernetes集群

在GitLab的管理界面中,选择集成Kubernetes,配置集群的API URL和证书,以及服务账号的Token。
kubectl cluster-info 命令查看 api 地址
API URL:

https://<kubernetes-master-ip-address>

证书:

从Kubernetes集群中获取证书内容,并写入到GitLab的配置文件中。
kubectl get secrets # 获取一个类似default-token-xxxxx的名称,填写在下面这个命令<secret name>
kubectl get secret default-token-9vxqs -o jsonpath="{['data']['ca\.crt']}" | base64 --decode

服务账号Token:

使用命令 kubectl -n gitlab-managed-apps describe secret $(kubectl -n gitlab-managed-apps get secret | grep tiller | awk '{print $1}') 获取Token,并写入到GitLab的配置文件中。

4. 部署GitLab应用

在GitLab的管理界面中,选择应用商店,搜索Kubernetes,选择GitLab Runner并进行安装。

在安装过程中,需要选择创建Runner所使用的命名空间和服务账号,以及选择连接到的Kubernetes集群。

5. 通过GitLab管理Kubernetes集群

安装完成后,就可以在GitLab上管理Kubernetes集群了。在GitLab的管理界面中,选择Kubernetes选项,可以查看Kubernetes集群的资源列表、Pod的运行状态和日志,以及进行Pod的重启、删除等操作。同时,也可以在GitLab中定义CI/CD流水线,将代码部署到Kubernetes集群中。

Helm安装部署

没有使用Helm之前,在Kubernetes部署应用,我们要依次部署deployment、service等,步骤比较繁琐。况且随着很多项目微服务化,复杂的应用在容器中部署以及管理显得较为复杂。

helm通过打包的方式,支持发布的版本管理和控制,很大程度上简化了Kubernetes应用的部署和管理。

Helm本质就是让k8s的应用管理(Deployment、Service等)可配置,能动态生成。通过动态生成K8S资源清单文件(deployment.yaml、service.yaml)。然后kubectl自动调用K8S资源部署。

Helm是官方提供类似于YUM的包管理,是部署环境的流程封装,Helm有三个重要的概念:chart、release和Repository

  • chart是创建一个应用的信息集合,包括各种Kubernetes对象的配置模板、参数定义、依赖关系、文档说明等。可以将chart想象成apt、yum中的软件安装包。
  • release是chart的运行实例,代表一个正在运行的应用。当chart被安装到Kubernetes集群,就生成一个release。chart能多次安装到同一个集群,每次安装都是一个release【根据chart赋值不同,完全可以部署出多个release出来】。
  • Repository用于发布和存储 Chart 的存储库。

helm 2019年 V3版本,变化项

  • V3 版本删除 Tiller (架构变化)
  • Release 可以在不同命名空间重用
  • 将 Chart 推送到 docker 仓库中

安装

https://helm.sh/
https://helm.sh/zh/docs/intro/install/

下载 helm 安装压缩包,上传到 linux
解压 helm,把目录复制到 usr/bin 目录中
[root@k8smaster ~]# cd /opt/k8s/
[root@k8smaster k8s]# tar zxfv helm-v3.0.0-linux-amd64.tar.gz 
linux-amd64/
linux-amd64/helm
linux-amd64/README.md
linux-amd64/LICENSE
[root@k8smaster k8s]# mv ./linux-amd64/helm /usr/bin/
# 显示版本,安装完成
[root@k8smaster k8s]# helm version
version.BuildInfo{Version:"v3.0.0", GitCommit:"e29ce2a54e96cd02ccfce88bee4f58bb6e2a28b6", GitTreeState:"clean", GoVersion:"go1.13.4"}
[root@k8smaster k8s]#

**配置-**添加仓库

# 添加 仓库
[root@k8smaster ~]# helm repo add stable http://mirror.azure.cn/kubernetes/charts
"stable" has been added to your repositories
# 添加 阿里云仓库
[root@k8smaster ~]# helm repo add aliyun https://kubernetes.oss-cn-hangzhou.aliyuncs.com/charts
"aliyun" has been added to your repositories
# 查看
[root@k8smaster ~]# helm repo list
NAME    URL
stable  http://mirror.azure.cn/kubernetes/charts
aliyun  https://kubernetes.oss-cn-hangzhou.aliyuncs.com/charts
# 更新
[root@k8smaster ~]# helm repo update
Hang tight while we grab the latest from your chart repositories...
...Successfully got an update from the "aliyun" chart repository
...Successfully got an update from the "stable" chart repository
Update Complete. ⎈ Happy Helming!⎈ 
# 删除阿里云
[root@k8smaster ~]# helm repo remove aliyun
"aliyun" has been removed from your repositories
[root@k8smaster ~]# helm repo list
NAME    URL
stable  http://mirror.azure.cn/kubernetes/charts
[root@k8smaster ~]#

部署应用

# 查找 chart
[root@k8smaster ~]# helm search repo weave
NAME                    CHART VERSION   APP VERSION     DESCRIPTION                                       
stable/weave-cloud      0.3.9           1.4.0           DEPRECATED - Weave Cloud is a add-on to Kuberne...
stable/weave-scope      1.1.12          1.12.0          DEPRECATED - A Helm chart for the Weave Scope c...
# 查看 chrt 信息
[root@k8smaster ~]# helm show chart stable/weave-scope
apiVersion: v1
appVersion: 1.12.0
deprecated: true
description: DEPRECATED - A Helm chart for the Weave Scope cluster visualizer.
home: https://www.weave.works/oss/scope/
icon: https://avatars1.githubusercontent.com/u/9976052?s=64
keywords:
- containers
- dashboard
- monitoring
name: weave-scope
sources:
- https://github.com/weaveworks/scope
version: 1.1.12
# 安装,名字叫 ui
[root@k8smaster ~]# helm install ui stable/weave-scope
NAME: ui
LAST DEPLOYED: Mon Nov 28 13:15:05 2022
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/
# 查看列表
[root@k8smaster ~]# helm list
NAME    NAMESPACE       REVISION        UPDATED                                 STATUS          CHART                   APP VERSION
ui      default         1               2022-11-28 13:15:05.404335352 +0800 CST deployed        weave-scope-1.1.12      1.12.0 # 查看发布状态
[root@k8smaster ~]# helm status ui
NAME: ui
LAST DEPLOYED: Mon Nov 28 13:15:05 2022
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/
# 查看服务 当前是不暴露端口的
[root@k8smaster ~]# kubectl get svc
NAME             TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)          AGE
javademo1        NodePort    10.106.43.46   <none>        8111:31452/TCP   40d
kubernetes       ClusterIP   10.96.0.1      <none>        443/TCP          45d
nginx            NodePort    10.103.87.81   <none>        80:30339/TCP     45d
nginx-nfs        NodePort    10.99.84.9     <none>        80:30205/TCP     19d
ui-weave-scope   ClusterIP   10.101.4.212   <none>        80/TCP           3m19s
# 修改 ui-weave-scpoe 暴露端口
[root@k8smaster ~]# kubectl edit svc ui-weave-scope
#  type: ClusterIP =>  改成 type: NodePort
service/ui-weave-scope edited
# 再次查看,暴露了 30690 端口
[root@k8smaster ~]# kubectl get svc
NAME             TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)          AGE
javademo1        NodePort    10.106.43.46   <none>        8111:31452/TCP   40d
kubernetes       ClusterIP   10.96.0.1      <none>        443/TCP          45d
nginx            NodePort    10.103.87.81   <none>        80:30339/TCP     45d
nginx-nfs        NodePort    10.99.84.9     <none>        80:30205/TCP     19d
ui-weave-scope   NodePort    10.101.4.212   <none>        80:30690/TCP     5m26s
# 删除 ui-weave-scope
[root@k8smaster ~]# kubectl delete deployment ui-weave-scope
[root@k8smaster ~]# kubectl delete svc ui-weave-scope

Prometheus 监控配置

Prometheus Operator vs. kube-prometheus vs. community helm chart

Prometheus Operator

Prometheus Operator 使用 Kubernetes 自定义资源,简化了 Prometheus、Alertmanager 和相关监控组件的部署和配置。

kube-prometheus

kube-prometheus 提供了一个基于 Prometheus 和 Prometheus Operator 的完整集群监控堆栈的示例配置。这包括部署多个 Prometheus 和 Alertmanager 实例、用于收集节点指标的指标导出器(如 node_exporters)、将 Prometheus 链接到各种指标端点的目标配置,以及用于通知集群中潜在问题的示例警报规则。

helm chart

prometheus-community/kube-prometheus-stack helm chart 提供了与 kube-prometheus 相似的特性集。这张 chart 是由 prometheus 社区维护的。

使用Helm chart形式部署Prometheus

helm charts地址

https://github.com/prometheus-operator/prometheus-operator

部署

helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo update
helm search repo prometheus-community
##helm install [RELEASE_NAME] prometheus-community/kube-prometheus-stack
helm install prometheus prometheus-community/kube-prometheus-stack

如果报错:Error: failed pre-install: timed out waiting for the condition,可按步骤分开部署
helm pull prometheus-community/kube-prometheus-stack
报错是:cannot re-use a name that is still in use
# helm list
# helm uninstall prometheus

# kubectl get pods
# kubectl describe prometheus-kube-prometheus-adm

官方给出的yaml文件中拉取的镜像不在docker hub中,在k8s.gcr.io中,所以在国内我们拉取就会报错:ErrImagePull

kubelet Failed to pull image “registry.k8s.io/ingress-nginx/kube-webhook-certgen:v20221220controller-v1.5.1-58-g787ea74b6”: rpc error: code = Unknown desc = Error response from daemon: Head “https://asia-northeast1-docker.pk.dev/v2/k8s-artifacts-prod/images/ingress-nginx/kube-webhook-certgen/manifests/v20221220-controller-v1.5.1-58-g787ea74b6”: dial tcp 64233.188.82:443: i/o timeout

在dokcer仓库里找了一个类似的,然后通过kubectl edit pod修改一下

下载了新的
docker pull dyrnq/kube-webhook-certgen:v20221220-controller-v1.5.1-58-g787ea74b6
或者也可以修改配置文件从新install(记得要把sha注释掉)我没改
vim values.yaml
镜像改成:docker.io/dyrnq/kube-webhook-certgen:v20221220-controller-v1.5.1-58-g787ea74b6
卸载
# helm uninstall prometheus
重装
# # helm install prometheus . --debug
问题:
prometheus-kube-state-metrics-5599456694-5kr85         0/1     ErrImagePull
下载个新镜像
docker pull dyrnq/kube-state-metrics:v2.8.1
重命名新镜像:
docker tag dyrnq/kube-state-metrics:v2.8.1 registry.k8s.io/kube-state-metrics/kube-state-metrics:v2.8.1
重新看一下kubectl get pods是不是恢复了

修改成NodePort方便外部访问

kubectl edit svc prometheus-grafana
# kubectl edit svc prometheus-kube-prometheus-prometheus
# kubectl edit svc prometheus-kube-prometheus-alertmanager
访问prometheus的web界面:http://192.168.123.22:30351/
访问alertmanager的web界面:http://$NodeIP:30902
访问grafana的web界面:http://192.168.123.21:32719/login (默认用户密码 admin/prom-operator)

配置prometheus

添加配置自动发现规则


cat prometheus-additional.yaml

- job_name: 'kubernetes-endpoints'
  kubernetes_sd_configs:
    - role: endpoints
  relabel_configs:
    - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_scrape]
      action: keep
      regex: true
    - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_scheme]
      action: replace
      target_label: __scheme__
      regex: (https?)
    - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_path]
      action: replace
      target_label: __metrics_path__
      regex: (.+)
    - source_labels: [__address__, __meta_kubernetes_service_annotation_prometheus_io_port]
      action: replace
      target_label: __address__
      regex: ([^:]+)(?::\d+)?;(\d+)
      replacement: $1:$2
    - action: labelmap
      regex: __meta_kubernetes_service_label_(.+)
    - source_labels: [__meta_kubernetes_namespace]
      action: replace
      target_label: kubernetes_namespace
    - source_labels: [__meta_kubernetes_service_name]
      action: replace
      target_label: kubernetes_name
    - source_labels: [__meta_kubernetes_pod_name]
      action: replace
      target_label: kubernetes_pod_name

存入集群的secret

kubectl create secret generic additional-configs --from-file=prometheus-additional.yaml 

注入prometheus

kubectl edit prometheuses 

spec:
   additionalScrapeConfigs:
      key:prometheus-additional.yaml
      name:additional-configs
   alerting:
      alertmanagers:

配置RBAC

kubectl apply -f prometheus-rbac.yml
#cat prometheus-rbac.yml

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: prometheus-k8s
rules:
  - apiGroups:
      - ""
    resources:
      - nodes
      - services
      - endpoints
      - pods
      - nodes/proxy
    verbs:
      - get
      - list
      - watch
  - apiGroups:
      - ""
    resources:
      - configmaps
      - nodes/metrics
    verbs:
      - get
  - nonResourceURLs:
      - /metrics
    verbs:
      - get

登录prometheus网页查看

#使用port-forward映射端口
kubectl port-forward -n prometheus-stack  --address 0.0.0.0 svc/kube-prometheus-stack-prometheus 9090:9090

应用配置服务发现自动接入prometheus

添加红色两行就行
kind: Service
metadata:
   annotations:
       prometheus.io/scrape: "true"
       prometheus.io/port: "80"
   name:acserver
   namespace: analysis-center
###prometheus.io/scrape:该注解信息主要用来描述对应pod是否允许抓取指标数据,true表示允许,false表示不允许;
##prometheus.io/port:用于描述对应抓取指标数据使用的端口信息;

配置grafana

登录grafana

#grafana默认会安装好,映射端口登录
kubectl port-forward -n prometheus-stack  --address 0.0.0.0 svc/kube-prometheus-stack-grafana 3000:80

通过secrets解密获取用户名密码

$kubectl get secrets 
$kubectl get secrets prometheus-grafana -o yaml
$kubectl get secrets prometheus-grafana -o jsonpath='{.data.admin-user}}'| base64 -d
$kubectl get secrets prometheus-grafana -o jsonpath='{.data.admin-password}}'| base64 -d
得到用户名密码:admin/prom-operator

导入监控模板

安装DNS

在K8S集群未部署DNS之前,K8S中运行的Pod是无法访问外部网络的,因为无法完成域名解析。

需要指定一个clusterIP,不能靠k8s自动分配,每个Node的kubelet都是用这个IP地址,另外,这个IP需要在kube-apiserver启动参数–service-cluster-ip-range指定的IP范围内(–service-cluster-ip-range=10.1.0.0/16),kube-apiserver的配置文件在/etc/kubernetes/manifests目录下

修改每台Node上的kubelet参数

根据自己的配置环境修改参数cluster_dns和cluster_domain。
添加以下两个参数:

--cluster_dns=169.169.0.100: 为dns服务的clusterIP地址
--cluster_domain=cluster.local: 为dns服务中设置的域名
比如我这边的是这样:

vim /usr/lib/systemd/system/kubelet.service.d/10-kubeadm.conf
#添加如下一行,cat >> /usr/lib/systemd/system/kubelet.service.d/10-kubeadm.conf <<EOF
Environment="KUBELET_DNS_ARGS=--cluster-dns=10.96.0.10 --cluster-domain=cluster.local"
然后重启kubelet,使用ps -ef | grep kubelet查看是否生效

重启kubelet

systemctl daemon-reload
systemctl restart kubelet

部署CoreDNS服务

部署CoreDNS 服务时需要创建3个资源对象: 1个ConfigMap,1个Deployment 和 1个Service 。在启用了RBAC
的集群中,还可以设置ServiceAccount ,ClusterRole,ClusterRoleBinding 对CoreDNS 容器进行权限设置。

ConfigMap

ConfigMap 的 “coredns”
主要设置CoreDNS的主配置文件Corefile的内容,其中可以定义各种域名的解析方式和使用的插件

apiVersion: v1
kind: ConfigMap
metadata:
	name: coredns
	namespace: kube-system 
	labels: 
		addonmanager.kubernetes.io/mode: EnsureExists
data:
	Corefile: | 
		cluster.local{
			errors
			health{
				lameduck 5s 
			}
			ready
			kubernetes cluster.local 10.1.0.0/16{ 
				fallthrough in-addr.arpa ip6.arpa
			}
			prometheus: 9153
			forward	./etc/resolv.conf 
			cache 30 
			loop
			reload
			loadbalance
		}
		. {
			cache 30 
			loadbalance
			forward /etc/resolv.conf
		}

Deployment

**Deployment 的“coredns”**主要设置CoreDNS容器应用的内容;其中,replicas
副本的数量通常应该根据集群的规模和服务数量确定,如果单个CoreDNS进程不足以支撑整个集群的DNS
查询,则可以通过水平扩展提高查询能力。由于DNS服务是Kubernetes集群的关键核心服务,所以建议为其Deployment
设置自动扩缩容控制器,自动管理其副本数量。

apiversion: apps/v1
kind: Deployment
metadata:
	name: coredns
	namespace: kube-system 
	labels:
		k8s-app: kube-dns
		kubernetes.io/name: "CoreDNS"
spec:
	replicas: 1
	strategy:
		type: RollingUpdate
		rollingUpdate:
			maxUnavailable: 1
	selector:
		matchLabels: 
			k8s-app: kube-dns
	template:
		metadata:
			labels:
				k8s-app: kube-dns
		spec:
		priorityClassName: system-cluster-critical 
		tolerations:
			- key: "CriticalAddonsonly"
			  operator: "Exists"
		nodeSelector:
			kubernetes.io/os: linux 
		affinity:
			podAntiAffinity:
			preferredDuringSchedulingIgnoredDuringExecution: 
			- weight: 100
			  podAffinityTerm:
			  	labelSelector:
					matchExpressions:
					- key: k8s-app
			          operator: In
			          values: ["kube-dns"]
			    topologyKey: kubernetes.io/hostname
		containers:
		- name: coredns
		  image: coredns/coredns:1.7.0 
		  imagePullPolicy: IfNotPresent
		  resources:
		    limits:
		      memory: 170Mi
		    requests:
		      cpu: 100m
		      memory: 70Mi
		  args: ["-conf","/etc/coredns/Corefile" ]
		volumeMounts:
		- name: config-volume 
		  mountPath: /etc/coredns 
		  readOnly: true
		ports:
		- containerPort: 53
		  name: dns
		  protocol: UDP
		- containerPort: 53
		  name: dns-tcp
		  protocol: TCP
		- containerPort: 9153
		  name: metrics
		  protocol: TCP
		securityContext:
		  allowPrivilegeEscalation: false
		  capabilities:
		    add:
		    - NET_BIND_SERVICE
		    drop:
		    - a11
		  readOnlyRootFilesystem: true 
		livenessProbe:
		  httpGet:
		    path: /health
		    port: 8080
		    scheme: HTTP
		  initialDelaySeconds: 60 
		  timeoutSeconds: 5
		  successThreshold: 1
		  failureThreshold: 5
		readinessProbe:
		  httpGet:
		    path: /ready
		    port: 8181
		    scheme: HTTP
		dnsPolicy: Default
		volumes:
		- name: config-volume
		  configMap:
		    name: coredns
		    items:
		    - key: Corefile
		      path: Corefile

Service

**Service“kube-dns”**是DNS服务的配置,这个服务需要设置固定的ClusterIP地址,也需要将所有Node
上的kubelet启动参数--cluster-dns都设置为这个ClusterIP地址

apiVersion: v1
kind: Service
metadata:
	name: kube-dns
	namespace: kube-system 
	annotations:
		prometheus.io/port: "9153" 
		prometheus.io/scrape: "true" 
	labels:
		k8s-app: kube-dns
		kubernetes.io/cluster-service: "true" 
		kubernetes.io/name: "CoreDNS" 
spec:
	selector:
		k8s-app: kube-dns
	clusterIP: 10.1.254.254 
	ports:
	- name: dns
	  port: 53
	  protocol: UDP
	- name: dns-tcp
	  port: 53
	  protocol: TCP
	- name: metrics
	  port: 9153
	  protocol: TCP

通过kubectlcreate 命令完成CoreDNS 服务的创建

$ kubectl create -f coredns.yaml
$ kubectl get deployment --namespace=kube-system
$ kubectl get pods --namespace=kube-system
$ kubectl get service --namespace=kube-system

k8s集群的pod无法访问service的clusterIP

进入集群的任意一个pod,ping不通集群内的clusterIP和service
解决方案:开启ipvs,我们需要修改kube-proxy的配置文件,添加mode 为ipvs

验证是否安装ipvs

所有节点验证
ipvsadm -Ln

所有节点安装

yum install ipset ipvsadm -y

设置ipvs

cat > /etc/sysconfig/modules/ipvs.modules <<EOF
 
modprobe -- ip_vs
modprobe -- ip_vs_rr
modprobe -- ip_vs_wrr
modprobe -- ip_vs_sh
modprobe -- nf_conntrack_ipv4
EOF

chmod +x /etc/sysconfig/modules/ipvs.modules
# 查看
lsmod | grep -e ip_vs -e nf_conntrack_ipv4

重新创建kube-proxy pod

kubectl get pod -n kube-system

kubectl delete pod kube-proxy-pffsm -n kube-system
测试ipvs模块是否开启成功
ipvsadm -Ln

EFK(Elasticsearch,Kibana,Fluentd)安装

EFK插件是k8s项目的一个日志解决方案,它包括三个组件:Elasticsearch, Fluentd, Kibana;Elasticsearch 是日志存储和日志搜索引擎,Fluentd 负责把k8s集群的日志发送给 Elasticsearch, Kibana 则是可视化界面查看和检索存储在 ES 中的数据。

一般ES集群的节点至少要有3个,quorum设置为2=discovery.zen.minimum_master_nodes

Elasticsearch 镜docker.elastic.co/elasticsearch/elasticsearch:7.17.1

Kibana 镜像docker.elastic.co/kibana/kibana:7.17.1

Fluentd 镜像quay.io/fluentd_elasticsearch/fluentd:v3.4.0

nfs镜像:nfs-client-provisioner:v3.1.0-k8s1.11

NFS部署

配置NFS服务器

# 安装 
yum -y install nfs-utils
#创建共享目录
mkdir -p /data/nfs_efk
#修改权限
chmod -R 777 /data/nfs_efk
#修改配置文件
vi /etc/exports
#前面是共享目录,后面星代表所有ip,fsid、anonuid、anongid是给从节点写入权限,0代表root用户
/data/nfs_efk *(rw,sync,fsid=0,anonuid=0,anongid=0)
# 配置生效并查看生效
exportfs -r
exportfs
(显示如下:)
/data/nfs_efk <world>
# 启动rpcbind、nfs服务
systemctl restart rpcbind && systemctl enable rpcbind
systemctl restart nfs && systemctl enable nfs 
# 查看 RPC 服务的注册状况
rpcinfo -p localhost

# 显示如下 
program vers proto   port  service
100000    4   tcp    111  portmapper
100000    3   tcp    111  portmapper
100000    2   tcp    111  portmapper
100000    4   udp    111  portmapper
100000    3   udp    111  portmapper
100000    2   udp    111  portmapper

# showmount测试
showmount -e 192.168.31.58

# 显示如下
Export list for 192.168.31.58:
/data/nfs_efk *

拉取镜像


docker pull jmgao1983/nfs-client-provisioner:latest
docker tag jmgao1983/nfs-client-provisioner:latest  nfs-client-provisioner:latest

新建storageclass-nfs.yaml

## 创建了一个存储类
apiVersion: storage.k8s.io/v1
kind: StorageClass                  #存储类的资源名称
metadata:
  name: nfs-storage                 #存储类的名称,自定义
  annotations:
    storageclass.kubernetes.io/is-default-class: "true"          #注解,是否是默认的存储,注意:KubeSphere默认就需要个默认存储,因此这里注解要设置为“默认”的存储系统,表示为"true",代表默认。
provisioner: k8s-sigs.io/nfs-subdir-external-provisioner         #存储分配器的名字,自定义
parameters:
  archiveOnDelete: "true"  ## 删除pv的时候,pv的内容是否要备份

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nfs-client-provisioner
  labels:
    app: nfs-client-provisioner
  # replace with namespace where provisioner is deployed
  namespace: default
spec:
  replicas: 1                 #只运行一个副本应用
  strategy:                   #描述了如何用新的POD替换现有的POD
    type: Recreate            #Recreate表示重新创建Pod
  selector:        #选择后端Pod
    matchLabels:
      app: nfs-client-provisioner
  template:
    metadata:
      labels:
        app: nfs-client-provisioner
    spec:
      serviceAccountName: nfs-client-provisioner          #创建账户
      containers:
        - name: nfs-client-provisioner         
          image: nfs-client-provisioner:latest  #使用NFS存储分配器的镜像
          imagePullPolicy: Never #这个是代表使用本地镜像,默认从网上拉取
          # resources:
          #    limits:
          #      cpu: 10m
          #    requests:
          #      cpu: 10m
          volumeMounts:
            - name: nfs-client-root           #定义个存储卷,
              mountPath: /persistentvolumes   #表示挂载容器内部的路径
          env:
            - name: PROVISIONER_NAME          #定义存储分配器的名称
              value: k8s-sigs.io/nfs-subdir-external-provisioner         #需要和上面定义的保持名称一致
            - name: NFS_SERVER                                       #指定NFS服务器的地址,你需要改成你的NFS服务器的IP地址
              value: 192.168.123.28 ## 指定自己nfs服务器地址
            - name: NFS_PATH                                
              value: /data/nfs_efk  ## nfs服务器共享的目录            #指定NFS服务器共享的目录
      volumes:
        - name: nfs-client-root           #存储卷的名称,和前面定义的保持一致
          nfs:
            server: 192.168.123.28            #NFS服务器的地址,和上面保持一致,这里需要改为你的IP地址
            path: /data/nfs_efk               #NFS共享的存储目录,和上面保持一致
--- 
apiVersion: v1
kind: ServiceAccount                 #创建个SA账号
metadata:
  name: nfs-client-provisioner        #和上面的SA账号保持一致
  # replace with namespace where provisioner is deployed
  namespace: default
---
#以下就是ClusterRole,ClusterRoleBinding,Role,RoleBinding都是权限绑定配置,不在解释。直接复制即可。
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: nfs-client-provisioner-runner
rules:
  - apiGroups: [""]
    resources: ["nodes"]
    verbs: ["get", "list", "watch"]
  - apiGroups: [""]
    resources: ["persistentvolumes"]
    verbs: ["get", "list", "watch", "create", "delete"]
  - apiGroups: [""]
    resources: ["persistentvolumeclaims"]
    verbs: ["get", "list", "watch", "update"]
  - apiGroups: ["storage.k8s.io"]
    resources: ["storageclasses"]
    verbs: ["get", "list", "watch"]
  - apiGroups: [""]
    resources: ["events"]
    verbs: ["watch","create", "update", "patch"]
  - apiGroups: [""]
    resources: ["services", "endpoints"]
    verbs: ["get","create","list", "watch","update"]
  - apiGroups: ["extensions"]
    resources: ["podsecuritypolicies"]
    resourceNames: ["nfs-provisioner"]
    verbs: ["use"]    
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: run-nfs-client-provisioner
subjects:
  - kind: ServiceAccount
    name: nfs-client-provisioner
    # replace with namespace where provisioner is deployed
    namespace: default
roleRef:
  kind: ClusterRole
  name: nfs-client-provisioner-runner
  apiGroup: rbac.authorization.k8s.io
---
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: leader-locking-nfs-client-provisioner
  # replace with namespace where provisioner is deployed
  namespace: default
rules:
  - apiGroups: [""]
    resources: ["endpoints"]
    verbs: ["get", "list", "watch", "create", "update", "patch"]
  - apiGroups: [""]
    resources: ["events"]
    verbs: ["watch","create", "update", "patch"]
---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: leader-locking-nfs-client-provisioner
  # replace with namespace where provisioner is deployed
  namespace: default
subjects:
  - kind: ServiceAccount
    name: nfs-client-provisioner
    # replace with namespace where provisioner is deployed
    namespace: default
roleRef:
  kind: Role
  name: leader-locking-nfs-client-provisioner
  apiGroup: rbac.authorization.k8s.io

创建StorageClass

kubectl apply -f storageclass-nfs.yaml

# 查看是否存在
kubectl get sc

测试PVC

kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: test-claim
  annotations:
    volume.beta.kubernetes.io/storage-class: "nfs-storage"   #与nfs-StorageClass.yaml metadata.name保持一致
spec:
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: 1Mi
kubectl apply -f test_pvc.yml
kubectl get pvc
pvc是bound状态成功

报错


报错信息“selfLink was empty”,在官方1.20的变更说明中:

Stop propagating SelfLink (deprecated in 1.16) in kube-apiserver
selfLink在1.16版本以后已经弃用,在1.20版本停用。

而由于nfs-provisioner的实现是基于selfLink功能(同时也会影响其他用到selfLink这个功能的第三方软件),需要等nfs-provisioner的制作方重新提供新的解决方案。

目前可用的临时方案是:

修改/etc/kubernetes/manifests/kube-apiserver.yaml文件,找到如下内容后,在最后添加一项参数

spec:
  containers:
  - command:
    - kube-apiserver
    - --advertise-address=192.168.210.20
    - --.......  #省略多行内容
    - --feature-gates=RemoveSelfLink=false  #添加此行

安装ELASTICSEARCH

创建一个命名空间

kubectl create namespace efk
[root@k8s-master01 ~]# kubectl get ns
NAME                   STATUS   AGE
default                Active   35d
efk                    Active   4s

创建一个名为 elasticsearch 的无头服务


**kind: Service
apiVersion: v1
metadata:
  name: elasticsearch
  namespace: efk
  labels:
    app: elasticsearch
spec:
  selector:
    app: elasticsearch
  clusterIP: None
  ports:
    - port: 9200
      name: rest
    - port: 9300
      name: inter-node
定义了一个名为 elasticsearch 的 Service,指定标签 app=elasticsearch,当我们将 Elasticsearch StatefulSet 与此服务关联时,
服务将返回带有标签 app=elasticsearch的 Elasticsearch Pods 的 DNS A 记录,然后设置 clusterIP=None,将该服务设置成无头服务。
最后,我们分别定义端口9200、9300,分别用于与 REST API 交互,以及用于节点间通信。

$ kubectl create -f elasticsearch-svc.yaml
service/elasticsearch created
$ kubectl get services --namespace=logging
Output
NAME            TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)             AGE
elasticsearch   ClusterIP   None         <none>        9200/TCP,9300/TCP   26s**

现在我们已经为 Pod 设置了无头服务和一个稳定的域名.elasticsearch.logging.svc.cluster.local,接下来我们通过 StatefulSet 来创建具体的 Elasticsearch 的 Pod 应用。

Kubernetes StatefulSet 允许我们为 Pod 分配一个稳定的标识和持久化存储,Elasticsearch 需要稳定的存储来保证 Pod 在重新调度或者重启后的数据依然不变,所以需要使用 StatefulSet 来管理 Pod。

新建名为 elasticsearch-statefulset.yaml 的资源清单文件

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: es
  namespace: logging
spec:
  serviceName: elasticsearch
  replicas: 3
  selector:
    matchLabels:
      app: elasticsearch
  template:
    metadata:
      labels:
        app: elasticsearch		
该内容中,我们定义了一个名为 es 的 StatefulSet 对象,然后定义serviceName=elasticsearch和前面创建的 Service 相关联,
这可以确保使用以下 DNS 地址访问 StatefulSet 中的每一个 Pod:es-[0,1,2].elasticsearch.logging.svc.cluster.local,其中[0,1,2]对应于已分配的 Pod 序号。
然后指定3个副本,将 matchLabels 设置为app=elasticsearch,所以 Pod 的模板部分.spec.template.metadata.lables也必须包含app=elasticsearch标签。
...
    spec:
      nodeSelector:
        es: log
      containers:
      - name: elasticsearch
        image: docker.elastic.co/elasticsearch/elasticsearch:7.17.1
        imagePullPolicy: "IfNotPresent"
        ports:
        - name: rest
          containerPort: 9200
        - name: inter
          containerPort: 9300
        resources:
          limits:
            cpu: 1000m
          requests:
            cpu: 1000m
        volumeMounts:
        - name: data
          mountPath: /usr/share/elasticsearch/data
        env:
        - name: cluster.name
          value: k8s-logs
        - name: node.name
          valueFrom:
            fieldRef:
              fieldPath: metadata.name
        - name: cluster.initial_master_nodes
          value: "es-0,es-1,es-2"
        - name: discovery.zen.minimum_master_nodes
          value: "2"
        - name: discovery.seed_hosts
          value: "elasticsearch"
        - name: ES_JAVA_OPTS
          value: "-Xms512m -Xmx512m"
        - name: network.host
          value: "0.0.0.0"		
该部分是定义 StatefulSet 中的 Pod,暴露了9200和9300两个端口,注意名称要和上面定义的 Service 保持一致。然后通过 volumeMount 声明了数据持久化目录,下面我们再来定义 VolumeClaims。最后就是我们在容器中设置的一些环境变量了:
cluster.name:Elasticsearch 集群的名称,我们这里命名成 k8s-logs。
Node Selector是指定pod分配到指定node上最简单的方法,使用Pod中的nodeSelector属性来实现。Node Selector会指定key-value pairs,pod会被分配到特定node上,
该node具有所有指定key-value pairs对应labels。通常只有一对key-value。
kubectl label nodes k8s-node01 es=log# 创建标签
kubectl label nodes k8s-node01 es- # 删除标签
kubectl get nodes --show-labels
node.name:节点的名称,通过 metadata.name 来获取。这将解析为 es-[0,1,2],取决于节点的指定顺序。
discovery.seed_hosts:此字段用于设置在 Elasticsearch 集群中节点相互连接的发现方法。由于我们之前配置的无头服务,我们的 Pod 具有唯一的 DNS 域es-[0,1,2].elasticsearch.logging.svc.cluster.local,因此我们相应地设置此变量。
discovery.zen.minimum_master_nodes:我们将其设置为(N/2) + 1,N是我们的群集中符合主节点的节点的数量。我们有3个 Elasticsearch 节点,因此我们将此值设置为2(向下舍入到最接近的整数)。
ES_JAVA_OPTS:这里我们设置为-Xms512m -Xmx512m,告诉JVM使用512 MB的最小和最大堆。您应该根据群集的资源可用性和需求调整这些参数。
...
      initContainers:
      - name: increase-vm-max-map
        image: busybox
        imagePullPolicy: "IfNotPresent"
        command: ["sysctl", "-w", "vm.max_map_count=262144"]
        securityContext:
          privileged: true
      - name: increase-fd-ulimit
        image: busybox
        command: ["sh", "-c", "ulimit -n 65536"]
        securityContext:
          privileged: true
		这里我们定义了几个在主应用程序之前运行的 Init 容器,这些初始容器按照定义的顺序依次执行,执行完成后才会启动主应用容器。
第一个名为 increase-vm-max-map 的容器用来增加操作系统对mmap计数的限制,默认情况下该值可能太低,导致内存不足的错误,
最后一个初始化容器是用来执行ulimit命令增加打开文件描述符的最大数量的。此外 Elastisearch Notes for Production Use 文档还提到了由于性能原因最好禁用 swap,当然对于 Kubernetes 集群而言,最好也是禁用 swap 分区的。
现在我们已经定义了主应用容器和它之前运行的 Init Containers 来调整一些必要的系统参数,接下来我们可以添加数据目录的持久化相关的配置,在 StatefulSet 中,使用 volumeClaimTemplates 来定义 volume 模板即可.
...
  volumeClaimTemplates:
 - metadata:
      name: data
      labels:
        app: elasticsearch
    spec:
      accessModes: [ "ReadWriteOnce" ]
      storageClassName: nfs-storage
      resources:
        requests:
          storage: 50Gi # 可自行设置大小
我们这里使用 volumeClaimTemplates 来定义持久化模板,Kubernetes 会使用它为 Pod 创建 PersistentVolume,设置访问模式为ReadWriteOnce,
这意味着它只能被 mount 到单个节点上进行读写,然后最重要的是使用了一个 StorageClass 对象,
这里我们就直接使用前面创建的 Ceph RBD 类型的名为 rook-ceph-block 的 StorageClass 对象即可。最后,我们指定了每个 PersistentVolume 的大小为 50GB,
我们可以根据自己的实际需要进行调整该值。

部署

$ kubectl create -f elasticsearch-statefulset.yaml
statefulset.apps/es created

资源查看

[root@k8s-master01 deploy]# kubectl get sts -n efk
NAME   READY   AGE
es     0/3     7m11s
[root@k8s-master01 deploy]# kubectl get pod -n efk
NAME   READY   STATUS    RESTARTS   AGE
es-0   1/1     Running   0          7m51s
es-1   1/1     Running   0          4m37s
es-2   1/1     Running   0          3m49s
[root@k8s-master01 deploy]# kubectl get sts -n efk
NAME   READY   AGE
es     0/3     8m3s
[root@k8s-master01 deploy]# kubectl get svc -n efk
NAME            TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)             AGE
elasticsearch   ClusterIP   None         <none>        9200/TCP,9300/TCP   3h

测试

Pods 部署完成后,我们可以通过请求一个 REST API 来检查 Elasticsearch 集群是否正常运行。
使用下面的命令将本地端口9200 转发到 Elasticsearch 节点(如es-0)对应的端口:

$ kubectl port-forward es-0 9200:9200 --namespace=efk
Forwarding from 127.0.0.1:9200 -> 9200
Forwarding from [::1]:9200 -> 9200

然后,在另外的终端窗口中,执行如下请求:

$ curl http://localhost:9200/_cluster/state?pretty

部署kibana

新建一个名为 kibana.yaml

apiVersion: v1
kind: ConfigMap
metadata:
  namespace: efk
  name: kibana-config
  labels:
    app: kibana
data:
  kibana.yml: |
    server.name: kibana
    server.host: "0.0.0.0"
    i18n.locale: zh-CN                      #设置默认语言为中文
    elasticsearch:
      hosts: ${ELASTICSEARCH_HOSTS}         #es集群连接地址,由于我这都都是k8s部署且在一个ns下,可以直接使用service name连接
---
apiVersion: v1
kind: Service
metadata:
  name: kibana
  namespace: efk
  labels:
    app: kibana
spec:
  ports:
  - port: 5601
  type: NodePort
  selector:
    app: kibana

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: kibana
  namespace: efk
  labels:
    app: kibana
spec:
  selector:
    matchLabels:
      app: kibana
  template:
    metadata:
      labels:
        app: kibana
    spec:
      nodeSelector:
        es: log
      containers:
      - name: kibana
        image: docker.elastic.co/kibana/kibana:7.17.1
        imagePullPolicy: "IfNotPresent"
        resources:
          limits:
            cpu: 1000m
          requests:
            cpu: 1000m
        env:
        - name: ELASTICSEARCH_URL
          value: http://elasticsearch:9200                 #设置为handless service dns地址即可
        - name: ELASTICSEARCH_HOSTS
          value: http://elasticsearch:9200
        ports:
        - containerPort: 5601
        volumeMounts:
        - name: config
          mountPath: /usr/share/kibana/config/kibana.yml     #kibana配置文件挂载地址
          readOnly: true
          subPath: kibana.yml
      volumes:
      - name: config             # 跟上面得volumeMounts名称匹配
        configMap:
          name: kibana-config                               #对应configmap名称

创建KIBANA

$ kubectl create -f kibana.yml
service/kibana created
deployment.apps/kibana created

查看 Kibana Pod

$ kubectl get pods --namespace=efk
$ kubectl get svc -n efk

报错

Warning  FailedScheduling  106s (x31 over 32m)  default-scheduler  0/4 nodes are available: 1 Insufficient cpu, 1 node(s) 
had taint {node-role.kubernetes.io/master: }, that the pod didn't tolerate, 2 node(s) didn't match Pod's node affinity/selector.
资源不足,Insufficient:不足的。 很显然,资源不足导致的。遇到这种情况,两种思路:一是增加cpu和内存,二是调低工作负载占用的内存和cpu的配置。
**node01的CPU资源不足,增加服务器CPU后可以用了。**
**查看节点taints**
kubectl describe node NODE_NAME | grep Taint
**删除节点taints**
kubectl taint node k8s-master node-role.kubernetes.io/master:NoSchedule-

requests:代表容器启动请求的资源限制,分配的资源必须要达到此要求, limits:代表最多可以请求多少资源
单位m:CPU的计量单位叫毫核(m)。一个节点的CPU核心数量乘以1000,得到的就是节点总的CPU总数量。如,一个节点有两个核,那么该节点的CPU总量为2000m。
resources:
  requests:
    cpu: 50m     #等同于0.05
    memory: 512Mi
  limits:
    cpu: 100m    #等同于0.1
    memory: 1Gi
含义:该容器启动时请求50/2000的核心(2.5%)并且允许最多使用100/1000核心(5%)。

浏览页面

[http://192.168.123.20:30035/

部署fluentd

日志源配置

<source>
  @id fluentd-containers.log
  @type tail                             # Fluentd 内置的输入方式,其原理是不停地从源文件中获取新的日志。
  path /var/log/containers/*.log         # 挂载的服务器Docker容器日志地址
  pos_file /var/log/es-containers.log.pos
  tag raw.kubernetes.*                   # 设置日志标签
  read_from_head true
  <parse>                                # 多行格式化成JSON
    @type multi_format                   # 使用 multi-format-parser 解析器插件
    <pattern>
      format json                        # JSON 解析器
      time_key time                      # 指定事件时间的时间字段
      time_format %Y-%m-%dT%H:%M:%S.%NZ  # 时间格式
    </pattern>
    <pattern>
      format /^(?<time>.+) (?<stream>stdout|stderr) [^ ]* (?<log>.*)$/
      time_format %Y-%m-%dT%H:%M:%S.%N%:z
    </pattern>
  </parse>
</source>

id:表示引用该日志源的唯一标识符,该标识可用于进一步过滤和路由结构化日志数据

type:Fluentd 内置的指令,tail 表示
Fluentd 从上次读取的位置通过 tail 不断获取数据,另外一个是 http 表示通过一个 GET 请求来收集数据。

path:tail 类型下的特定参数,告诉 Fluentd 采集 /var/log/containers 目录下的所有日志,这是
docker 在 Kubernetes 节点上用来存储运行容器 stdout 输出日志数据的目录。

pos_file:检查点,如果
Fluentd 程序重新启动了,它将使用此文件中的位置来恢复日志数据收集。

tag:用来将日志源与目标或者过滤器匹配的自定义字符串,Fluentd 匹配源/目标标签来路由日志数据。

数据发送

<match **>

@id elasticsearch

@type elasticsearch

@log_level info

include_tag_key true

type_name fluentd

host "#{ENV['OUTPUT_HOST']}"

port "#{ENV['OUTPUT_PORT']}"

logstash_format true

<buffer>

@type file

path /var/log/fluentd-buffers/kubernetes.system.buffer

flush_mode interval

retry_type exponential_backoff

flush_thread_count 2

flush_interval 5s

retry_forever

retry_max_interval 30

chunk_limit_size "#{ENV['OUTPUT_BUFFER_CHUNK_LIMIT']}"

queue_limit_length "#{ENV['OUTPUT_BUFFER_QUEUE_LIMIT']}"

overflow_action block

</buffer>

match:标识一个目标标签,后面是一个匹配日志源的正则表达式,我们这里想要捕获所有的日志并将它们发送给 Elasticsearch,所以需要配置成**。

id:目标的一个唯一标识符。

type:支持的输出插件标识符,我们这里要输出到 Elasticsearch,所以配置成 elasticsearch,这是 Fluentd 的一个内置插件。

log_level:指定要捕获的日志级别,我们这里配置成 info,表示任何该级别或者该级别以上(INFO、WARNING、ERROR)的日志都将被路由到 Elsasticsearch。

host/port:定义 Elasticsearch 的地址,也可以配置认证信息,我们的 Elasticsearch 不需要认证,所以这里直接指定 host 和 port 即可。

logstash_format:Elasticsearch 服务对日志数据构建反向索引进行搜索,将 logstash_format 设置为 true,Fluentd 将会以 logstash 格式来转发结构化的日志数据。

Buffer: Fluentd 允许在目标不可用时进行缓存,比如,如果网络出现故障或者 Elasticsearch 不可用的时候。缓冲区配置也有助于降低磁盘的 IO。

过滤

# 删除无用的属性
<filter kubernetes.**>
  @type record_transformer
  remove_keys $.docker.container_id,$.kubernetes.container_image_id,$.kubernetes.pod_id,$.kubernetes.namespace_id,$.kubernetes.master_url,$.kubernetes.labels.pod-template-hash
</filter>
# 只保留具有logging=true标签的Pod日志
<filter kubernetes.**>
  @id filter_log
  @type grep
  <regexp>
    key $.kubernetes.labels.logging
    pattern ^true$
  </regexp>
</filter>

只采集具有 logging=true 这个 Label 标签的 Pod 日志,这个时候就需要使用 filter

通过 ConfigMap 对象来指定 Fluentd 配置文件,新建 fluentd-configmap.yaml 文件

kind: ConfigMap
apiVersion: v1
metadata:
  name: fluentd-config
  namespace: efk
data:
  system.conf: |-
    <system>
      root_dir /tmp/fluentd-buffers/
    </system>
  containers.input.conf: |-
    <source>
      @id fluentd-containers.log
      @type tail                              # Fluentd 内置的输入方式,其原理是不停地从源文件中获取新的日志。
      path /var/log/containers/*.log          # 挂载的服务器Docker容器日志地址
      pos_file /var/log/es-containers.log.pos
      tag raw.kubernetes.*                    # 设置日志标签
      read_from_head true
      <parse>                                 # 多行格式化成JSON
        @type multi_format                    # 使用 multi-format-parser 解析器插件
        <pattern>
          format json                         # JSON解析器
          time_key time                       # 指定事件时间的时间字段
          time_format %Y-%m-%dT%H:%M:%S.%NZ   # 时间格式
        </pattern>
        <pattern>
          format /^(?<time>.+) (?<stream>stdout|stderr) [^ ]* (?<log>.*)$/
          time_format %Y-%m-%dT%H:%M:%S.%N%:z
        </pattern>
      </parse>
    </source>
    # 在日志输出中检测异常,并将其作为一条日志转发 
    # https://github.com/GoogleCloudPlatform/fluent-plugin-detect-exceptions
    <match raw.kubernetes.**>           # 匹配tag为raw.kubernetes.**日志信息
      @id raw.kubernetes
      @type detect_exceptions           # 使用detect-exceptions插件处理异常栈信息
      remove_tag_prefix raw             # 移除 raw 前缀
      message log                       
      stream stream                     
      multiline_flush_interval 5
      max_bytes 500000
      max_lines 1000
    </match>

    <filter **>  # 拼接日志
      @id filter_concat
      @type concat                # Fluentd Filter 插件,用于连接多个事件中分隔的多行日志。
      key message
      multiline_end_regexp /\n$/  # 以换行符“\n”拼接
      separator ""
    </filter> 

    # 添加 Kubernetes metadata 数据
    <filter kubernetes.**>
      @id filter_kubernetes_metadata
      @type kubernetes_metadata
    </filter>

    # 修复 ES 中的 JSON 字段
    # 插件地址:https://github.com/repeatedly/fluent-plugin-multi-format-parser
    <filter kubernetes.**>
      @id filter_parser
      @type parser                # multi-format-parser多格式解析器插件
      key_name log                # 在要解析的记录中指定字段名称。
      reserve_data true           # 在解析结果中保留原始键值对。
      remove_key_name_field true  # key_name 解析成功后删除字段。
      <parse>
        @type multi_format
        <pattern>
          format json
        </pattern>
        <pattern>
          format none
        </pattern>
      </parse>
    </filter>

    # 删除一些多余的属性
    <filter kubernetes.**>
      @type record_transformer
      remove_keys $.docker.container_id,$.kubernetes.container_image_id,$.kubernetes.pod_id,$.kubernetes.namespace_id,$.kubernetes.master_url,$.kubernetes.labels.pod-template-hash
    </filter>

    # 只保留具有logging=true标签的Pod日志
    <filter kubernetes.**>
      @id filter_log
      @type grep
      <regexp>
        key $.kubernetes.labels.logging
        pattern ^true$
      </regexp>
    </filter>
  
  ###### 监听配置,一般用于日志聚合用 ######
  forward.input.conf: |-
    # 监听通过TCP发送的消息
    <source>
      @id forward
      @type forward
    </source>

  output.conf: |-
    <match **>
      @id elasticsearch
      @type elasticsearch
      @log_level info
      include_tag_key true
      host elasticsearch
      port 9200
      logstash_format true
      logstash_prefix k8s  # 设置 index 前缀为 k8s
      request_timeout    30s
      <buffer>
        @type file
        path /var/log/fluentd-buffers/kubernetes.system.buffer
        flush_mode interval
        retry_type exponential_backoff
        flush_thread_count 2
        flush_interval 5s
        retry_forever
        retry_max_interval 30
        chunk_limit_size 2M
        queue_limit_length 8
        overflow_action block
      </buffer>
    </match>

只配置了 docker 容器日志目录,收集到数据经过处理后发送到 elasticsearch:9200 服务

fluentd-daemonset.yaml

apiVersion: v1
kind: ServiceAccount
metadata:
  name: fluentd-es
  namespace: efk
  labels:
    k8s-app: fluentd-es
    kubernetes.io/cluster-service: "true"
    addonmanager.kubernetes.io/mode: Reconcile
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: fluentd-es
  labels:
    k8s-app: fluentd-es
    kubernetes.io/cluster-service: "true"
    addonmanager.kubernetes.io/mode: Reconcile
rules:
- apiGroups:
  - ""
  resources:
  - "namespaces"
  - "pods"
  verbs:
  - "get"
  - "watch"
  - "list"
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: fluentd-es
  labels:
    k8s-app: fluentd-es
    kubernetes.io/cluster-service: "true"
    addonmanager.kubernetes.io/mode: Reconcile
subjects:
- kind: ServiceAccount
  name: fluentd-es
  namespace: logging
  apiGroup: ""
roleRef:
  kind: ClusterRole
  name: fluentd-es
  apiGroup: ""
---
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: fluentd-es
  namespace: efk
  labels:
    k8s-app: fluentd-es
    kubernetes.io/cluster-service: "true"
    addonmanager.kubernetes.io/mode: Reconcile
spec:
  selector:
    matchLabels:
      k8s-app: fluentd-es
  template:
    metadata:
      labels:
        k8s-app: fluentd-es
        kubernetes.io/cluster-service: "true"
      # 此注释确保如果节点被驱逐,fluentd不会被驱逐,支持关键的基于 pod 注释的优先级方案。
      annotations:
        scheduler.alpha.kubernetes.io/critical-pod: ''
    spec:
      serviceAccountName: fluentd-es
      containers:
      - name: fluentd-es
        image: harbor.domain.com/efk/fluentd:v3.4.0
        imagePullPolicy: "IfNotPresent"
        env:
        - name: FLUENTD_ARGS
          value: --no-supervisor -q
        resources:
          limits:
            memory: 500Mi
          requests:
            cpu: 100m
            memory: 200Mi
        volumeMounts:
        - name: varlog
          mountPath: /var/log
        - name: varlibdockercontainers
          mountPath: /data/docker/containers
          readOnly: true
        - name: config-volume
          mountPath: /etc/fluent/config.d
      nodeSelector:
        beta.kubernetes.io/fluentd-ds-ready: "true"
      tolerations:
      - operator: Exists
      terminationGracePeriodSeconds: 30
      volumes:
      - name: varlog
        hostPath:
          path: /var/log
      - name: varlibdockercontainers
        hostPath:
          path: /var/lib/docker/containers
      - name: config-volume
        configMap:
          name: fluentd-config

我们将上面创建的 fluentd-config 这个 ConfigMap 对象通过 volumes 挂载到了 Fluentd 容器中,另外为了能够灵活控制哪些节点的日志可以被收集,所以我们这里还添加了一个 nodSelector 属性:

nodeSelector:
  beta.kubernetes.io/fluentd-ds-ready: "true"

意思就是要想采集节点的日志,那么我们就需要给节点打上上面的标签,比如我们这里只给节点4和节点6打上了该标签,如果你需要在其他节点上采集日志,则需要给对应节点打上标签,使用如下命令:kubectl label nodes node名 beta.kubernetes.io/fluentd-ds-ready=true。

创建上面的 ConfigMap 对象和 DaemonSet

$ kubectl create -f fluentd-configmap.yaml
configmap "fluentd-config" created
$ kubectl create -f fluentd-daemonset.yaml
serviceaccount/fluentd-es created
clusterrole.rbac.authorization.k8s.io/fluentd-es created
clusterrolebinding.rbac.authorization.k8s.io/fluentd-es created
Warning: spec.template.metadata.annotations[scheduler.alpha.kubernetes.io/critical-pod]: non-functional in v1.16+; use the "priorityClassName" field instead
daemonset.apps/fluentd-es created

测试

新建 counter.yaml 文件
apiVersion: v1
kind: Pod
metadata:
  name: counter
  labels:
    logging: "true"  # 一定要具有该标签才会被采集
spec:
  containers:
  - name: count
    image: busybox
    args: [/bin/sh, -c,
            'i=0; while true; do echo "$i: $(date)"; i=$((i+1)); sleep 1; done']

$ kubectl create -f counter.yaml
$ kubectl get pods
NAME                             READY   STATUS    RESTARTS   AGE
counter                          1/1     Running   0          9h

Pod 创建并运行后,回到 Kibana Dashboard 页面,点击左侧最下面的 Stack Management图标,然后点击 Kibana 下面的 Index Patterns 开始导入索引数据:
在这里可以配置我们需要的 Elasticsearch 索引,前面 Fluentd 配置文件中我们采集的日志使用的是 logstash 格式,定义了一个 k8s 的前缀,所以这里只需要在文本框中输入k8s-*即可匹配到 Elasticsearch 集群中采集的 Kubernetes 集群日志数据,然后点击下一步,进入以下页面:
还可以根据一些条件来筛选数据

helm部署gitrunner到K8S

添加chart存储库

helm repo add gitlab [https://charts.gitlab.io](https://charts.gitlab.io/)

查询可安装的gitlab-runner chart

helm search repo -l gitlab/gitlab-runner
然后查询gitlab的版本,根据gitlab的版本选择gitlab-runner的版本。比如这里查询到gitlab的版本是14.9.0,runner选择0.39.0

helm fetch gitlab/gitlab-runner --version=0.39.0

编辑配置文件

tar -zxvf gitlab-runner-0.39.0.tgz
cd gitlab-runner
vi values.yaml

imagePullPolicy: IfNotPresent
#gitlab服务器地址
gitlabUrl: http://192.168.123.24:9080
#runner注册token
runnerRegistrationToken: "6c69tRXzcZ4QFDvzgpkw"
#当停止管道时等待其他作业终止时间
terminationGracePeriodSeconds: 3600
#最大并发作业数量
concurrent: 10
#新作业检查时隔
checkInterval: 30
sessionServer:
  enabled: false
rbac:
  create: true
  resources: ["pods", "pods/exec", "secrets"]
  verbs: ["get", "list", "watch", "create", "patch", "delete"]
  rules: []
  clusterWideAccess: false
  podSecurityPolicy:
    enabled: false
    resourceNames:
    - gitlab-runner
metrics:
  enabled: true
  portName: metrics
  port: 9252
  serviceMonitor:
    enabled: false
service:
  enabled: false
  type: ClusterIP
runners:
  config: |
    [[runners]]
      [runners.kubernetes]
        namespace = "{{.Release.Namespace}}"
        image = "ubuntu:16.04"
  #执行器类型
  executor: kubernetes
  #是否锁定false
  locked: false
  #你的tags
  tags: "k8s-runner,k8s"
  #是否运行没有标签的项目
  runUntagged: true
  #开启docker in docker
  privileged: true
  cache: {}
  builds: {}
  services: {}
  helpers: {}
securityContext:
  runAsUser: 100
  fsGroup: 65533
resources: {}
affinity: {}
nodeSelector: {}
tolerations: []
hostAliases: []
podAnnotations: {}
podLabels: {}
secrets: []
configMaps: {}

gitlabUrl	http://192.168.10.9/
runnerRegistrationToken	runner注册toke
terminationGracePeriodSeconds:	当停止管道时等待其他作业终止时间: 3600
concurrent:	最大并发作业数量 10
checkInterval:	新作业检查时隔 30
executor	执行器类型 : kubernetes
locked	是否锁定 false
tags	你的"k8s-runner,k8s"
runUntagged	是否运行没有标签的项目 true
privileged	开启docker in docker

部署chart

cd ..
kubectl create namespace gitlab-runner
helm install gitlab-runner --namespace gitlab-runner ./gitlab-runner
若后续更新了配置文件,则使用如下命令更新即可

helm upgrade gitlab-runner --namespace gitlab-runner ./gitlab-runner

测试CI/CD

基于gitlab+docker+k8s打造自动化构建部署流程

在其中一台node上安装gitlab-runner


curl -L --output /usr/local/bin/gitlab-runner "https://gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-linux-amd64"

chmod +x /usr/local/bin/gitlab-runner

##添加用户,此处可以不添加,如果用root权限的话,建议直接指定root用户。防止后续持续性集成的时候出现权限问题
useradd --comment 'GitLab Runner' --create-home gitlab-runner --shell /bin/bash
## 切换到root用户  --user=root
cd /usr/local/bin/ 或者加到sbin目录下,直接就可以敲命令了
./gitlab-runner install --user=root --working-directory=/home/gitlab-runner
#gitlab-runner install --user=gitlab-runner --working-directory=/home/gitlab-runner
 
./gitlab-runner start
## 也可以后续调整配置文件
vim /etc/systemd/system/gitlab-runner.service

**配置文件 : /etc/systemd/system/gitlab-runner.service**
[Unit]
Description=GitLab Runner
ConditionFileIsExecutable=/usr/local/bin/gitlab-runner
After=syslog.target network.target

[Service]
StartLimitInterval=5
StartLimitBurst=10
ExecStart=/usr/local/bin/gitlab-runner "run" "--working-directory" "/home/gitlab-runner" "--config" "/etc/gitlab-runner/config.toml" "--service" "gitlab-runner" "--user" "root"

Restart=always
RestartSec=120
EnvironmentFile=-/etc/sysconfig/gitlab-runner
[Install]
WantedBy=multi-user.target

**重启服务**

systemctl daemon-reload
systemctl restart gitlab-runner.service

注册到gitlab上

说明:
gitlab-ci-multi-runner register:执行注册命令
Please enter the gitlab-ci coordinator URL:输入 ci 地址
Please enter the gitlab-ci token for this runner:输入 ci token
Please enter the gitlab-ci description for this runner:输入 runner 名称
Please enter the gitlab-ci tags for this runner:设置 tag
Whether to run untagged builds:这里选择 true ,代码上传后会能够直接执行
Whether to lock Runner to current project:直接回车,不用输入任何口令 
Please enter the executor:选择 runner 类型,这里我们选择的是 shell

**执行注册:(后面在GitLab的UI中更改)**
gitlab-runner register
##输入gitlab的地址,以下信息在上图中有体现,图中4的位置
Enter the GitLab instance URL (for example, https://gitlab.com/):
http://daggoo.cn:9080/
##输入token
Enter the registration token:
c_JVEPteFyffpAxDhJjn
##输入runner的描述,一般可以设置为runner的hostname
Enter a description for the runner:
k8s-node02-runner
## 输入与Runner关联的标签
Enter tags for the runner (comma-separated):
master
##输入Runner执行程序,一般选择shell,根据真实情况选择
Enter an executor: docker+machine, parallels, shell, ssh, virtualbox, kubernetes, custom, docker, docker-ssh, docker-ssh+machine:
shell
##执行完毕
Runner registered successfully. Feel free to start it, but if it's running already the config should be automatically reloaded!

刷新刚刚的CI/CD页面,发现已经添加runner机器成功

配置gitlab-ci.yml文件

注意:此处demo是演示的go版本,可以根据具体的项目环境进行调整脚本操作,大体操作是不变的

项目添加.gitlab-ci.yml

before_script:
  - go env -w GOPROXY=https://goproxy.cn,direct
  - go env -w GO111MODULE=on
stages:
  - build
  - test
  - lint
  - docker-deploy
build:
  stage: build
  allow_failure: false  
  script:
    - go mod tidy
    - make
  only:
    - tags
    - branches
  retry: 1
test:
  stage: test
  script:
    - echo
    - go test -test.v  ./...  -nacosDir=$PWD/conf  -logDir=$PWD/
  only:
    - tags
    - branches
  retry: 1
lint:
  stage: lint
  allow_failure: false
  script:
    - golangci-lint --version
    - golangci-lint run -v
  only:
    - tags
    - branches
  retry: 1
docker-deploy:
  stage: docker-deploy
  script:
    - make
    - docker build --rm -t harbor.xx.net/risk/riskclient:$CI_COMMIT_REF_NAME .
    - docker push harbor.xx.net/risk/riskclient:$CI_COMMIT_REF_NAME
  only:
    - tags
复制代码

说明:
触发build、test、lint 流程: 当branchs和tags变更的时候。注意allow_failure: false ,设置是否允许该job失败。
触发docker-deploy 流程:当项目打tag版本的时候,触发docker编译和推送到公司私有仓库上。此处需要特别注意:
1:代码的tag版本需要严格按照发版操作,vx.y.z。因为下面的docker 镜像就是根据这个版本来创建,同时生产环境升级也是根据此情况来执行。
2:runner服务器如果已经登录过私服,则可以不执行docker login 192.168.1.1 -u admin -p 123456,否则会推送到仓库失败
3:如果上面执行gitlab-runner 的用户不是root,则此处有可能会出现权限问题,因为docker是用root启动的。

提交代码到gitlab

观察项目此时会有pipline的日志流程,可以点进去看每一个job的执行日志

查看docker 私服

调整k8s版本

由于程序升级的时间需要根据具体的情况进行发布,故此处操作为手动调整镜像版本进行升级

部署gitlab-runner到k8s


安装 Gitlab Runner 非常简单,我们可以完全安装官方文档:https://docs.gitlab.com/runner/install/即可,比如可以直接使用二进制、Docker 等来安装。同样的,我们这里还是将 Gitlab Runner 安装到 Kubernetes 集群中来,让我们的集群来统一管理 Gitlab 相关的服务。

1.验证 Kubernetes 集群

执行下面的命令验证 Kubernetes 集群:

$ kubectl cluster-info
Kubernetes master is running at https://10.151.30.11:6443
KubeDNS is running at https://10.151.30.11:6443/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy

To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.
cluster-info这个命令会显示当前链接的集群状态和可用的集群服务列表。

2.获取 Gitlab CI Register Token

前面的章节中我们已经成功安装了 Gitlab,在浏览器中打开git.qikqiak.com页面,然后登录后进入到管理页面http://git.qikqiak.com/admin,然后点击导航栏中的Runner,可以看到该页面中有两个总要的参数,一个是 URL,另外一个就是 Register Token,下面的步骤中需要用到这两个参数值。

gitlab runner
gitlab runner
注意:不要随便泄露 Token

3.编写 Gitlab CI Runner 资源清单文件

同样我们将 Runner 相关的资源对象都安装到kube-ops这个 namespace 下面,首先,我们通过 ConfigMap 资源来传递 Runner 镜像所需的环境变量(runner-cm.yaml):

apiVersion: v1
data:
  REGISTER_NON_INTERACTIVE: "true"
  REGISTER_LOCKED: "false"
  METRICS_SERVER: "0.0.0.0:9100"
  CI_SERVER_URL: "http://gitlab.kube-ops.svc.cluster.local/ci"
  RUNNER_REQUEST_CONCURRENCY: "4"
  RUNNER_EXECUTOR: "kubernetes"
  KUBERNETES_NAMESPACE: "kube-ops"
  KUBERNETES_PRIVILEGED: "true"
  KUBERNETES_CPU_LIMIT: "1"
  KUBERNETES_MEMORY_LIMIT: "1Gi"
  KUBERNETES_SERVICE_CPU_LIMIT: "1"
  KUBERNETES_SERVICE_MEMORY_LIMIT: "1Gi"
  KUBERNETES_HELPER_CPU_LIMIT: "500m"
  KUBERNETES_HELPER_MEMORY_LIMIT: "100Mi"
  KUBERNETES_PULL_POLICY: "if-not-present"
  KUBERNETES_TERMINATIONGRACEPERIODSECONDS: "10"
  KUBERNETES_POLL_INTERVAL: "5"
  KUBERNETES_POLL_TIMEOUT: "360"
kind: ConfigMap
metadata:
  labels:
    app: gitlab-ci-runner
  name: gitlab-ci-runner-cm
  namespace: kube-ops
要注意CI_SERVER_URL对应的值需要指向我们的 Gitlab 实例的 URL(可以是外网地址,也可以是 Kubernetes 集群内部的 Service DNS 地址,因为 Runner 也是运行在 Kubernetes 集群中的),并加上/ci( http://gitlab.kube-ops.svc.cluster.local/ci )。此外还添加了一些构建容器运行的资源限制,我们可以自己根据需要进行更改即可。

注意:在向 ConfigMap 添加新选项后,需要删除 GitLab CI Runner Pod。因为我们是使用 envFrom来注入上面的这些环境变量而不是直接使用env的(envFrom 通过将环境变量放置到ConfigMaps或Secrets来帮助减小清单文件。

另外如果要添加其他选项的话,我们可以在 Pod 中运行gitlab-ci-multi-runner register --help命令来查看所有可使用的选项,只需为要配置的标志添加 env 变量即可,如下所示:

gitlab-runner@gitlab-ci-runner-0:/$ gitlab-ci-multi-runner --help
[...]
--kubernetes-cpu-limit value                          The CPU allocation given to build containers (default: "1") [$KUBERNETES_CPU_LIMIT]
--kubernetes-memory-limit value                       The amount of memory allocated to build containers (default: "4Gi") [$KUBERNETES_MEMORY_LIMIT]
--kubernetes-service-cpu-limit value                  The CPU allocation given to build service containers (default: "1") [$KUBERNETES_SERVICE_CPU_LIMIT]
--kubernetes-service-memory-limit value               The amount of memory allocated to build service containers (default: "1Gi") [$KUBERNETES_SERVICE_MEMORY_LIMIT]
--kubernetes-helper-cpu-limit value                   The CPU allocation given to build helper containers (default: "500m") [$KUBERNETES_HELPER_CPU_LIMIT]
--kubernetes-helper-memory-limit value                The amount of memory allocated to build helper containers (default: "3Gi") [$KUBERNETES_HELPER_MEMORY_LIMIT]
--kubernetes-cpu-request value                        The CPU allocation requested for build containers [$KUBERNETES_CPU_REQUEST]
[...]
除了上面的一些环境变量相关的配置外,还需要一个用于注册、运行和取消注册 Gitlab CI Runner 的小脚本。只有当 Pod 正常通过 Kubernetes(TERM信号)终止时,才会触发转轮取消注册。 如果强制终止 Pod(SIGKILL信号),Runner 将不会注销自身。必须手动完成对这种被杀死的 Runner 的清理,配置清单文件如下:(runner-scripts-cm.yaml)

apiVersion: v1
data:
  run.sh: |
    #!/bin/bash
    unregister() {
        kill %1
        echo "Unregistering runner ${RUNNER_NAME} ..."
        /usr/bin/gitlab-ci-multi-runner unregister -t "$(/usr/bin/gitlab-ci-multi-runner list 2>&1 | tail -n1 | awk '{print $4}' | cut -d'=' -f2)" -n ${RUNNER_NAME}
        exit $?
    }
    trap 'unregister' EXIT HUP INT QUIT PIPE TERM
    echo "Registering runner ${RUNNER_NAME} ..."
    /usr/bin/gitlab-ci-multi-runner register -r ${GITLAB_CI_TOKEN}
    sed -i 's/^concurrent.*/concurrent = '"${RUNNER_REQUEST_CONCURRENCY}"'/' /home/gitlab-runner/.gitlab-runner/config.toml
    echo "Starting runner ${RUNNER_NAME} ..."
    /usr/bin/gitlab-ci-multi-runner run -n ${RUNNER_NAME} &
    wait
kind: ConfigMap
metadata:
  labels:
    app: gitlab-ci-runner
  name: gitlab-ci-runner-scripts
  namespace: kube-ops
我们可以看到需要一个 GITLAB_CI_TOKEN,然后我们用 Gitlab CI runner token 来创建一个 Kubernetes secret 对象。将 token 进行 base64 编码:

$ echo rcVZF-mdHt9qCyyrCDgS | base64 -w0
cmNWWkYtbWRIdDlxQ3l5ckNEZ1MK
base64 命令在大部分 Linux 发行版中都是可用的。

然后使用上面的 token 创建一个 Secret 对象:(gitlab-ci-token-secret.yaml)

apiVersion: v1
kind: Secret
metadata:
  name: gitlab-ci-token
  namespace: kube-ops
  labels:
    app: gitlab-ci-runner
data:
  GITLAB_CI_TOKEN: cmNWWkYtbWRIdDlxQ3l5ckNEZ1MK
然后接下来我们就可以来编写一个用于真正运行 Runner 的控制器对象,我们这里使用 Statefulset。首先,在开始运行的时候,尝试取消注册所有的同名 Runner,当节点丢失时(即NodeLost事件),这尤其有用。然后再尝试重新注册自己并开始运行。在正常停止 Pod 的时候,Runner 将会运行unregister命令来尝试取消自己,所以 Gitlab 就不能再使用这个 Runner 了,这个是通过 Kubernetes Pod 生命周期中的hooks来完成的。

另外我们通过使用envFrom来指定Secrets和ConfigMaps来用作环境变量,对应的资源清单文件如下:(runner-statefulset.yaml)

apiVersion: apps/v1beta1
kind: StatefulSet
metadata:
  name: gitlab-ci-runner
  namespace: kube-ops
  labels:
    app: gitlab-ci-runner
spec:
  updateStrategy:
    type: RollingUpdate
  replicas: 2
  serviceName: gitlab-ci-runner
  template:
    metadata:
      labels:
        app: gitlab-ci-runner
    spec:
      volumes:
      - name: gitlab-ci-runner-scripts
        projected:
          sources:
          - configMap:
              name: gitlab-ci-runner-scripts
              items:
              - key: run.sh
                path: run.sh
                mode: 0755
      serviceAccountName: gitlab-ci
      securityContext:
        runAsNonRoot: true
        runAsUser: 999
        supplementalGroups: [999]
      containers:
      - image: gitlab/gitlab-runner:latest
        name: gitlab-ci-runner
        command:
        - /scripts/run.sh
        envFrom:
        - configMapRef:
            name: gitlab-ci-runner-cm
        - secretRef:
            name: gitlab-ci-token
        env:
        - name: RUNNER_NAME
          valueFrom:
            fieldRef:
              fieldPath: metadata.name
        ports:
        - containerPort: 9100
          name: http-metrics
          protocol: TCP
        volumeMounts:
        - name: gitlab-ci-runner-scripts
          mountPath: "/scripts"
          readOnly: true
      restartPolicy: Always
可以看到上面我们使用了一个名为 gitlab-ci 的 serviceAccount,新建一个 rbac 资源清单文件:(runner-rbac.yaml)

apiVersion: v1
kind: ServiceAccount
metadata:
  name: gitlab-ci
  namespace: kube-ops
---
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: gitlab-ci
  namespace: kube-ops
rules:
  - apiGroups: [""]
    resources: ["*"]
    verbs: ["*"]
---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: gitlab-ci
  namespace: kube-ops
subjects:
  - kind: ServiceAccount
    name: gitlab-ci
    namespace: kube-ops
roleRef:
  kind: Role
  name: gitlab-ci
  apiGroup: rbac.authorization.k8s.io
4.创建 Runner 资源对象

资源清单文件准备好后,我们直接创建上面的资源对象:

$ ls
gitlab-ci-token-secret.yaml  runner-cm.yaml  runner-rbac.yaml  runner-scripts-cm.yaml  runner-statefulset.yaml
$ kubectl create -f .
secret "gitlab-ci-token" created
configmap "gitlab-ci-runner-cm" created
serviceaccount "gitlab-ci" created
role.rbac.authorization.k8s.io "gitlab-ci" created
rolebinding.rbac.authorization.k8s.io "gitlab-ci" created
configmap "gitlab-ci-runner-scripts" created
statefulset.apps "gitlab-ci-runner" created
创建完成后,可以通过查看 Pod 状态判断 Runner 是否运行成功:

$ kubectl get pods -n kube-ops
NAME                                           READY     STATUS    RESTARTS   AGE
gitlab-7bff969fbc-k5zl4                        1/1       Running   0          4d
gitlab-ci-runner-0                             1/1       Running   0          3m
gitlab-ci-runner-1                             1/1       Running   0          3m
......
可以看到已经成功运行了两个(具体取决于StatefulSet清单中的副本数) Runner 实例,然后切换到 Gitlab Admin 页面下面的 Runner 页面:

gitlab runner list
gitlab runner list
当然我们也可以根据需要更改 Runner 的一些配置,比如添加 tag 标签等。

Untitled

Untitled

开发人员把项目代码通过git推送到gitlab,触发gitla-runner自动从拉取gitlab上面拉取代码下来,然后进行build,编译、生成镜像、然后把镜像推送到Harbor仓库;然后在部署的时候通过k8s拉取Harbor上面的代码进行创建容器和服务,最终发布完成,然后可以用外部访问

Untitled

HELM安装部署ingress

参考文档:https://kubernetes.github.io/ingress-nginx/deploy/

添加 ingress-nginx 官方 helm 仓库

$ helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx 
$ helm repo update

下载 chart 包

# 查找所有的版本 
$ helm search repo ingress-nginx/ingress-nginx -l 
NAME CHART VERSION APP VERSION DESCRIPTION ingress-nginx/ingress-nginx 4.4.0 1.5.1 Ingress controller for Kubernetes using NGINX a... ingress-nginx/ingress-nginx 4.3.0 1.4.0 Ingress controller for Kubernetes using NGINX a... ingress-nginx/ingress-nginx 4.2.5 1.3.1 Ingress controller for Kubernetes using NGINX a... 
# 下载 
$ helm fetch ingress-nginx/ingress-nginx --version 4.4.0 
# 解压缩 
$ tar -zxvf ingress-nginx-4.4.0.tgz $ cd ingress-nginx

拉取 ingress-nginx-controller 镜像

官方提供的 registry.k8s.io/ingress-nginx/controller无法直接拉取,需要使用替代的镜像
dockerhub地址:https://hub.docker.com/r/giantswarm/ingress-nginx-controller/tags

# 拉取
$ docker pull giantswarm/ingress-nginx-controller:v1.5.1
# 修改
tag $ docker tag giantswarm/ingress-nginx-controller:v1.5.1 registry.k8s.io/ingress-nginx/controller:v1.5.1
# 删除之前的tag 
$ docker rmi giantswarm/ingress-nginx-controller:v1.5.1

修改 values.yaml 文件

修改 ingress-nginx-contorller,注释掉 digest

![[我的附件/39fa37035b77acb8e3f5772c4b52f87a_MD5.png]]

修改 hostNetwork 的值为 true

![[我的附件/25894138069046d0f50ea4c61b6a7030_MD5.png]]

修改 dnsPolicy 的值为 ClusterFirstWithHostNet

![[我的附件/50200b9d5273f842a6b8d63298a14e78_MD5.png]]

nodeSelector 添加标签: ingress: “true”,用于部署 ingress-controller 到指定节点

![[我的附件/fa5d42d4a19fb4b6e36a39cf1a709c89_MD5.png]]

修改 kind 类型为 DaemonSet

![[我的附件/20c741fbfc850fb48aca03785f13c0b3_MD5.png]]

(这个版本没改)修改 kube-webhook-certgen 的镜像地址为国内仓库 registry.cn-hangzhou.aliyuncs.com/google_containers/kube-webhook-certgen:v1.5.1

![[我的附件/6ffecb8b3f98c7b8c5ae8a6219ca0d34_MD5.png]]

修改 service 类型为 NodePort

![[我的附件/c75a442b1b90060c5acf24969afc0cdb_MD5.png]]

安装

# 创建命名空间
$ kubectl create ns ingress-nginx

# helm安装
$ helm install ingress-nginx -n ingress-nginx .
NAME: ingress-nginx
LAST DEPLOYED: Thu Nov 24 17:12:22 2022
NAMESPACE: ingress-nginx
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
The ingress-nginx controller has been installed.
It may take a few minutes for the LoadBalancer IP to be available.
You can watch the status by running 'kubectl --namespace ingress-nginx get services -o wide -w ingress-nginx-controller'

An example Ingress that makes use of the controller:
  apiVersion: networking.k8s.io/v1
  kind: Ingress
  metadata:
    name: example
    namespace: foo
  spec:
    ingressClassName: nginx
    rules:
      - host: www.example.com
        http:
          paths:
            - pathType: Prefix
              backend:
                service:
                  name: exampleService
                  port:
                    number: 80
              path: /
    # This section is only required if TLS is to be enabled for the Ingress
    tls:
      - hosts:
        - www.example.com
        secretName: example-tls

If TLS is enabled for the Ingress, a Secret containing the certificate and key must also be provided:

  apiVersion: v1
  kind: Secret
  metadata:
    name: example-tls
    namespace: foo
  data:
    tls.crt: <base64 encoded cert>
    tls.key: <base64 encoded key>
  type: kubernetes.io/tls

安装完成后,需要给节点打上刚刚设置的标签ingress=true,让 Pod 调度到指定的节点

# 查看节点
$ kubectl get nodes
NAME          STATUS   ROLES                      AGE   VERSION
hecs-357699   Ready    controlplane,etcd,worker   19h   v1.23.12

# 设置标签
	$ kubectl label node K8S-NODE04 ingress=true

执行完成之后,就可以看到 ingress-nginx 部署到了master节点了

$ kubectl get all -n ingress-nginx
NAME                                 READY   STATUS    RESTARTS   AGE
pod/ingress-nginx-controller-2xn6d   1/1     Running   0          65m

NAME                                         TYPE           CLUSTER-IP     EXTERNAL-IP   PORT(S)                      AGE
service/ingress-nginx-controller             LoadBalancer   10.43.17.53    <pending>     80:32163/TCP,443:31541/TCP   70m
service/ingress-nginx-controller-admission   ClusterIP      10.43.164.91   <none>        443/TCP                      70m

NAME                                      DESIRED   CURRENT   READY   UP-TO-DATE   AVAILABLE   NODE SELECTOR                         AGE
daemonset.apps/ingress-nginx-controller   1         1         1       1            1           ingress=true,kubernetes.io/os=linux   70m

其他配置

设置 TCP 反代

在 tcp 节点下添加对应的规则

然后更新资源
$ helm upgrade ingress-nginx -n ingress-nginx .

测试 ingress-nginx

  • 创建后端的 nginx 的 Pod 和 Service:
apiVersion: apps/v1
kind: Deployment
metadata:
  name: svc-demo
spec:
  replicas: 2
  selector:
    matchLabels:
      app: myapp
  template:
    metadata:
      labels:
        app: myapp
    spec:
      containers:
      - image: nginx:1.18.0
        name: svc-demo
        ports:
        - containerPort: 80

---

apiVersion: v1
kind: Service
metadata:
  name: svc-demo
spec:
  selector:  
    app: myapp
  ports:
  - targetPort: 80  # 后端Pod的端口
    port: 8080 # 服务要暴露的端口

查看 Pod 和 Service:

[root@k8s-master service]# kubectl get pods,svc 
NAME                           READY   STATUS    RESTARTS   AGE
pod/svc-demo-f9785fc46-hz6db   1/1     Running   1          7d22h
pod/svc-demo-f9785fc46-m99mj   1/1     Running   1          7d22h

NAME                 TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)          AGE
service/kubernetes   ClusterIP   10.96.0.1       <none>        443/TCP          36d
service/svc-demo     NodePort    10.106.152.13   <none>        8080:31195/TCP   29d

创建 ingress 规则,ingress-nginx.yaml

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: nginx-example
  annotations:
    kubernetes.io/ingress.class: nginx
spec:
  rules:
  - host: www.localnginx.com
    http:
      paths:
      - path: /index
        pathType: Prefix
        backend:
          service:
            name: svc-demo
            port:
              number: 8080

创建ingress:

[root@k8s-master service]#  kubectl apply -f ingress-nginx.yaml

[root@k8s-master service]# kubectl get ingress
Warning: extensions/v1beta1 Ingress is deprecated in v1.14+, unavailable in v1.22+; use networking.k8s.io/v1 Ingress
NAME      CLASS    HOSTS         ADDRESS   PORTS   AGE
example   <none>   foo.bar.com             80      21m

window的 hosts 配置解析:

highlighter-

192.168.5.60 foo.bar.com

浏览器访问,可以看到已经成功通过ingress将请求转发到后端Pod上了。

问题集

Q:Error from server (InternalError): error when creating “ingress-nginx.yml”:

Internal error occurred: failed calling webhook “validate.nginx.ingress.kubernetes.io”:
failed to call webhook: Post “https://ingress-nginx-controller-admission.ingress-nginx.svc:443/networking/v1/ingresses?timeout=10s”: x509: certificate signed by unknown authority

A:

1. [root@k8s-master01 ~]# kubectl get -A ValidatingWebhookConfiguration
    
2. NAME WEBHOOKS AGE
    
3. ingress-nginx-admission 1 52m
    
4. [root@k8s-master01 ~]# kubectl delete -A ValidatingWebhookConfiguration ingress-nginx-admission
    
5. validatingwebhookconfiguration.admissionregistration.k8s.io "ingress-nginx-admission" deleted
    
6. [root@k8s-master01 ~]# kubectl create -f ingress-http.yaml
    
7. ingress.networking.k8s.io/ingress-http created
    
8. [root@k8s-master01 ~]# kubectl get ing ingress-http -n dev
    
9. NAME CLASS HOSTS ADDRESS PORTS AGE
    
10. ingress-http nginx nginx.xiaohan.com,tomcat.xiaohan.com 10.105.40.71 80 92s
    

12. #再次创建就发现没有问题了

评论