GVKun编程网logo

利用kubernetes资源锁完成自己的HA应用(kubernetes 资源限制)

7

在这篇文章中,我们将为您详细介绍利用kubernetes资源锁完成自己的HA应用的内容,并且讨论关于kubernetes资源限制的相关问题。此外,我们还会涉及一些关于(八)KubernetesIngr

在这篇文章中,我们将为您详细介绍利用kubernetes资源锁完成自己的HA应用的内容,并且讨论关于kubernetes 资源限制的相关问题。此外,我们还会涉及一些关于(八)Kubernetes Ingress资源、Kubernetes as Database: 使用kubesql查询kubernetes资源、Kubernetes 使用 Kubeadm 推出自己的托管服务、Kubernetes 学习22 kubernetes容器资源需求资源限制及HeapSter(翻车章节)的知识,以帮助您更全面地了解这个主题。

本文目录一览:

利用kubernetes资源锁完成自己的HA应用(kubernetes 资源限制)

利用kubernetes资源锁完成自己的HA应用(kubernetes 资源限制)

Backgroud

前一章中,对kubernetes的选举原理进行了深度剖析,下面就通过一个example来实现一个,利用kubernetes提供的选举机制完成的高可用应用。

对于此章需要提前对一些概念有所了解后才可以继续看下去

  • leader election mechanism
  • RBCA
  • Pod runtime mechanism

Implementation

代码实现

如果仅仅是使用Kubernetes中的锁,实现的代码也只有几行而已。

package main

import (
	"context"
	"flag"
	"fmt"
	"os"
	"os/signal"
	"syscall"
	"time"

	Metav1 "k8s.io/apimachinery/pkg/apis/Meta/v1"
	clientset "k8s.io/client-go/kubernetes"
	"k8s.io/client-go/rest"
	"k8s.io/client-go/tools/clientcmd"
	"k8s.io/client-go/tools/leaderelection"
	"k8s.io/client-go/tools/leaderelection/resourcelock"
	"k8s.io/klog/v2"
)

func buildConfig(kubeconfig string) (*rest.Config, error) {
	if kubeconfig != "" {
		cfg, err := clientcmd.BuildConfigFromFlags("", kubeconfig)
		if err != nil {
			return nil, err
		}
		return cfg, nil
	}

	cfg, err := rest.InClusterConfig()
	if err != nil {
		return nil, err
	}
	return cfg, nil
}

func main() {
	klog.InitFlags(nil)

	var kubeconfig string
	var leaseLockName string
	var leaseLockNamespace string
	var id string
	// 初始化客户端的部分
	flag.StringVar(&kubeconfig, "kubeconfig", "", "absolute path to the kubeconfig file")
	flag.StringVar(&id, "id", "", "the holder identity name")
	flag.StringVar(&leaseLockName, "lease-lock-name", "", "the lease lock resource name")
	flag.StringVar(&leaseLockNamespace, "lease-lock-namespace", "", "the lease lock resource namespace")
	flag.Parse()

	if leaseLockName == "" {
		klog.Fatal("unable to get lease lock resource name (missing lease-lock-name flag).")
	}
	if leaseLockNamespace == "" {
		klog.Fatal("unable to get lease lock resource namespace (missing lease-lock-namespace flag).")
	}
	config, err := buildConfig(kubeconfig)
	if err != nil {
		klog.Fatal(err)
	}
	client := clientset.NewForConfigOrDie(config)

	run := func(ctx context.Context) {
		// 实现的业务逻辑,这里仅仅为实验,就直接打印了
		klog.Info("Controller loop...")

		for {
			fmt.Println("I am leader, I was working.")
			time.Sleep(time.Second * 5)
		}
	}

	// use a Go context so we can tell the leaderelection code when we
	// want to step down
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	// 监听系统中断
	ch := make(chan os.Signal, 1)
	signal.Notify(ch, os.Interrupt, syscall.SIGTERM)
	go func() {
		<-ch
		klog.Info("Received termination, signaling shutdown")
		cancel()
	}()

	// 创建一个资源锁
	lock := &resourcelock.LeaseLock{
		LeaseMeta: Metav1.ObjectMeta{
			Name:      leaseLockName,
			Namespace: leaseLockNamespace,
		},
		Client: client.CoordinationV1(),
		LockConfig: resourcelock.ResourceLockConfig{
			Identity: id,
		},
	}

	// 开启一个选举的循环
	leaderelection.RunorDie(ctx, leaderelection.leaderElectionConfig{
		Lock:            lock,
		ReleaSEOnCancel: true,
		LeaseDuration:   60 * time.Second,
		RenewDeadline:   15 * time.Second,
		RetryPeriod:     5 * time.Second,
		Callbacks: leaderelection.leaderCallbacks{
			OnStartedLeading: func(ctx context.Context) {
				// 当选举为leader后所运行的业务逻辑
				run(ctx)
			},
			OnStoppedLeading: func() {
				// we can do cleanup here
				klog.Infof("leader lost: %s", id)
				os.Exit(0)
			},
			OnNewleader: func(identity string) { // 申请一个选举时的动作
				if identity == id {
					return
				}
				klog.Infof("new leader elected: %s", identity)
			},
		},
	})
}

注:这种lease锁只能在in-cluster模式下运行,如果需要类似二进制部署的程序,可以选择endpoint类型的资源锁。

生成镜像

这里已经制作好了镜像并上传到dockerhub(cylonchau/leaderelection:v0.0.2)上了,如果只要学习运行原理,则忽略此步骤

FROM golang:alpine AS builder
MAINTAINER cylon
workdir /election
copY . /election
ENV goproxy https://goproxy.cn,direct
RUN GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -o elector main.go

FROM alpine AS runner
workdir /go/elector
copY --from=builder /election/elector .
VOLUME ["/election"]
ENTRYPOINT ["./elector"]

准备资源清单

默认情况下,Kubernetes运行的pod在请求Kubernetes集群内资源时,默认的账户是没有权限的,默认服务帐户无权访问协调 API,因此我们需要创建另一个serviceaccount并相应地设置 对应的RBAC权限绑定;在清单中配置上这个sa,此时所有的pod就会有协调锁的权限了

apiVersion: v1
kind: ServiceAccount
Metadata:
  name: sa-leaderelection
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
Metadata:
  name: leaderelection
rules:
  - apiGroups:
      - coordination.k8s.io
    resources:
      - leases
    verbs:
      - '*'
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
Metadata:
  name: leaderelection
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: leaderelection
subjects:
  - kind: ServiceAccount
    name: sa-leaderelection
---
apiVersion: apps/v1
kind: Deployment
Metadata:
  labels:
    app: leaderelection
  name: leaderelection
  namespace: default
spec:
  replicas: 3
  selector:
    matchLabels:
      app: leaderelection
  template:
    Metadata:
      labels:
        app: leaderelection
    spec:
      containers:
        - image: cylonchau/leaderelection:v0.0.2
          imagePullPolicy: IfNotPresent
          command: ["./elector"]
          args:
          - "-id=$(POD_NAME)"
          - "-lease-lock-name=test"
          - "-lease-lock-namespace=default"
          env:
          - name: POD_NAME
            valueFrom:
              fieldRef:
                apiVersion: v1
                fieldpath: Metadata.name
          name: elector
      serviceAccountName: sa-leaderelection

集群中运行

执行完清单后,当pod启动后,可以看到会创建出一个 lease

$ kubectl get lease
NAME   HOLDER                            AGE
test   leaderelection-5644c5f84f-frs5n   1s


$ kubectl describe lease
Name:         test
Namespace:    default
Labels:       <none>
Annotations:  <none>
API Version:  coordination.k8s.io/v1
Kind:         Lease
Metadata:
  Creation Timestamp:  2022-06-28T16:39:45Z
  Managed Fields:
    API Version:  coordination.k8s.io/v1
    Fields Type:  FieldsV1
    fieldsV1:
      f:spec:
        f:acquireTime:
        f:holderIdentity:
        f:leaseDurationSeconds:
        f:leaseTransitions:
        f:renewTime:
    Manager:         elector
    Operation:       Update
    Time:            2022-06-28T16:39:45Z
  Resource Version:  131693
  Self Link:         /apis/coordination.k8s.io/v1/namespaces/default/leases/test
  UID:               bef2b164-a117-44bd-bad3-3e651c94c97b
Spec:
  Acquire Time:            2022-06-28T16:39:45.931873Z
  Holder Identity:         leaderelection-5644c5f84f-frs5n
  Lease Duration Seconds:  60
  Lease Transitions:       0
  Renew Time:              2022-06-28T16:39:55.963537Z
Events:                    <none>

通过其持有者的信息查看对应pod(因为程序中对holder Identity设置的是pod的名称),实际上是工作的pod。

如上实例所述,这是利用Kubernetes集群完成的leader选举的方案,虽然这不是最完美解决方案,但这是一种简单的方法,因为可以无需在集群上部署更多东西或者进行大量的代码工作就可以利用Kubernetes集群来完成一个高可用的HA应用。

(八)Kubernetes Ingress资源

(八)Kubernetes Ingress资源

前言

Kubernetes提供了两种内建的云端负载均衡机制(cloud load balancing)用于发布公共应用,一种是工作于传输层的Service资源,它实现的是“TCP负载均衡器”,另一种是Ingress资源,它实现的是“HTTP(S)负载均衡器”

  • TCP负载均衡器

    无论是iptables还是ipvs模型的Service资源都配置于Linux内核中的Netfilter之上进行四层调度,是一种类型更为通用的调度器,支持调度HTTPMySQL等应用层服务。不过,也正是由于工作于传输层从而使得它无法做到类似卸载HTTPS中的SSL会话等一类操作,也不支持基于URL的请求调度机制,而且,Kubernetes也不支持为此类负载均衡器配置任何类型的健康状态检查机制。

  • HTTP(S)负载均衡器

    HTTP(S)负载均衡器是应用层负载均衡机制的一种,支持根据环境做出更好的调度决策。与传输层调度器相比,它提供了诸如可自定义URL映射和TLS卸载等功能,并支持多种类型的后端服务器健康状态检查机制。

Ingress概述

什么是Ingress?

通常情况下,servicepod仅可在集群内部网络中通过IP地址访问。所有到达边界路由器的流量或被丢弃或被转发到其他地方。从概念上讲,可能像下面这样:

 internet
     |
------------
[ Services ]

Ingress是授权入站连接到达集群服务的规则集合。

 internet
     |
[ Ingress ]
--|-----|--
[ Services ]

你可以给Ingress配置提供外部可访问的URL、负载均衡、SSL、基于名称的虚拟主机等。用户通过POST Ingress资源到API Server的方式来请求IngressIngress controller负责实现Ingress,通常使用负载平衡器,它还可以配置边界路由和其他前端,这有助于以HA方式处理流量。

Ingress和Ingress Controller

IngressKubernetes API的标准资源类型之一,它其实就是一组基于DNS名称(host)或URL路径把请求转发至指定的Service资源的规则,用于将集群外部的请求流量转发至集群内部完成服务发布。然而,Ingress资源自身并不能进行“流量穿透”,它仅是一组路由规则的集合,这些规则要想真正发挥作用还需要其他功能的辅助,如监听某套接字,然后根据这些规则的匹配机制路由请求流量。这种能够为Ingress资源监听套接字并转发流量的组件称为Ingress控制器(Ingress Controller)。

Ingress控制器并不直接运行为kube-controller-manager的一部分,它是Kubernetes集群的一个重要组件,类似CoreDNS,需要在集群上单独部署。

Ingress工作流程

如下图所示,流量到达外部负载均衡器(externalLB)后,首先转发至Service资源Ingres-nginx上,然后通过Ingress控制器基于Ingress资源定义的规则将客户端请求流量直接转发至与Service对应的后端Pod资源之上。这种转发机制会绕过Service资源(app Serviceapi Service),从而省去了由kube-proxy实现的端口代理开销。Ingress规则需要由一个Service资源对象辅助识别相关的所有Pod资源。如下Ingress通过app service资源去匹配后端的pod1pod2;这个app service只是起到一个辅助识别功能。

先决条件

在使用Ingress resource之前,必须先了解下面几件事情。Ingressbeta版本的resource,在kubernetes1.1之前还没有。你需要一个Ingress Controller来实现Ingress,单纯的创建一个Ingress没有任何意义。

GCE/GKE会在master节点上部署一个Ingress Controller。你可以在一个Pod中部署任意个自定义的Ingress Controller。你必须正确的annotate每个Ingress,比如运行多个Ingress Controller和关闭glbc

Ingress清单文件几个字段说明

Ingress资源是基于HTTP虚拟主机或URL的转发规则,spec字段中嵌套了rulesbackendtls等字段进行定义。下面这个示例中,它包含了一个转发规则,把发往www.ilinux.io的请求代理给名为myapp-svcService资源。

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: ingress-demo
  namespace: default
  annotations:
    kubernetes.io/ingress.class: "nginx"
spec:
  rules:
  - host: www.ilinux.io
    http:
      paths:
      - backend:
          serviceName: myapp-svc
          servicePort: 80

#说明:上面资源清单文件中的annotations用于识别其所属的Ingress控制器的类别,这一点在集群上部署多个Ingress控制器时尤为重要。

Ingress Spec# kubectl explain ingress.spec)中的字段是定义Ingress资源的核心组成部分,主要嵌套如下三个字段:

  • rules <[]Object>:用于定义当前Ingress资源的转发规则列表;未由rules定义规则,或者没有匹配到任何规则时,所有流量都会转发到由backend定义的默认后端。

  • backend <Object>:默认的后端用于服务那些没有匹配到任何规则的请求;定义Ingress资源时,至少应该定义backendrules两者之一;此字段用于让负载均衡器指定一个全局默认的后端。

  • tls <[]Object>:TLS配置,目前仅支持通过默认端口443提供服务;如果要配置指定的列表成员指向了不同的主机,则必须通过SNI TLS扩展机制来支持此功能。

ingress.spec.rules.http.paths.backend对象的定义由两个必须的内嵌字段组成:serviceNameservicePort,分别用于指定流量转发的后端目标Service资源的名称和端口。

部署Ingress Controller(Nginx)

描述

Ingress 控制器自身是运行于Pod中的容器应用,一般是NginxEnvoy一类的具有代理及负载均衡功能的守护进程,它监视着来自API ServerIngress对象状态,并根据规则生成相应的应用程序专有格式的配置文件并通过重载或重启守护进程而使新配置生效。

Ingress控制器其实就是托管于Kubernetes系统之上的用于实现在应用层发布服务的Pod资源,跟踪Ingress资源并实时生成配置规则。

运行为Pod资源的Ingress控制器进程通过下面两种方式接入外部请求流量:

1、以Deployment控制器管理Ingress控制器的Pod资源,通过NodePortLoadBalancer类型的Service对象为其接入集群外部的请求流量,这就意味着,定义一个Ingress控制器时,必须在其前端定义一个专用的Service资源。

2、借助于DaemonSet控制器,将Ingress控制器的Pod资源各自以单一实例的方式运行于集群的所有或部分工作节点之上,并配置这类Pod对象以HostPort(如下图中的a)或HostNetwork(如下图中的b)的方式在当前节点接入外部流量。

部署

Ingress-nginx官网

Ingress-nginx GitHub仓库地址

Ingress安装文档

1)在github上下载配置清单yaml文件,并创建部署

[root@k8s-master ~]# mkdir ingress-nginx   #这里创建一个目录专门用于ingress-nginx(可省略)
[root@k8s-master ~]# cd ingress-nginx/
[root@k8s-master ingress-nginx]# wget  https://raw.githubusercontent.com/kubernetes/ingress-nginx/master/deploy/static/mandatory.yaml    #下载配置清单yaml文件
[root@k8s-master ingress-nginx]# ls    #查看下载的文件
mandatory.yaml

[root@k8s-master ingress-nginx]# kubectl apply -f mandatory.yaml    #创建Ingress
namespace/ingress-nginx created
configmap/nginx-configuration created
configmap/tcp-services created
configmap/udp-services created
serviceaccount/nginx-ingress-serviceaccount created
clusterrole.rbac.authorization.k8s.io/nginx-ingress-clusterrole created
role.rbac.authorization.k8s.io/nginx-ingress-role created
rolebinding.rbac.authorization.k8s.io/nginx-ingress-role-nisa-binding created
clusterrolebinding.rbac.authorization.k8s.io/nginx-ingress-clusterrole-nisa-binding created
deployment.apps/nginx-ingress-controller created

2)验证

[root@k8s-master ingress-nginx]# kubectl get pods -n ingress-nginx    #查看生成的pod,注意这里在ingress-nginx名称空间
NAME                                        READY   STATUS    RESTARTS   AGE
nginx-ingress-controller-79f6884cf6-5fb6v   1/1     Running   0          18m
[root@k8s-master ingress-nginx]# kubectl describe pod nginx-ingress-controller-79f6884cf6-5fb6v -n ingress-nginx    查看该pod的详细信息
Name:           nginx-ingress-controller-79f6884cf6-5fb6v
Namespace:      ingress-nginx
Priority:       0
Node:           k8s-node2/192.168.1.33
Start Time:     Fri, 27 Sep 2019 17:53:07 +0800
Labels:         app.kubernetes.io/name=ingress-nginx
                app.kubernetes.io/part-of=ingress-nginx
                pod-template-hash=79f6884cf6
Annotations:    prometheus.io/port: 10254
                prometheus.io/scrape: true
Status:         Running
IP:             10.244.2.73
......

3)如果是裸机部署,还需要安装service。(比如VMware虚拟机、硬件服务器等)

---同样去官网下载配置清单文件,也可以自定义创建。
[root@k8s-master ingress-nginx]# wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/master/deploy/static/provider/baremetal/service-nodeport.yaml
[root@k8s-master ingress-nginx]# kubectl apply -f service-nodeport.yaml    #创建service资源
service/ingress-nginx created
[root@k8s-master ingress-nginx]# kubectl get svc -n ingress-nginx    #查看service资源
NAME            TYPE       CLUSTER-IP       EXTERNAL-IP   PORT(S)                      AGE
ingress-nginx   NodePort   10.107.40.182   <none>        80:32699/TCP,443:30842/TCP   9s
[root@k8s-master ingress-nginx]# kubectl describe svc/ingress-nginx -n ingress-nginx    #查看该service的详细信息
Name:                     ingress-nginx
Namespace:                ingress-nginx
Labels:                   app.kubernetes.io/name=ingress-nginx
                          app.kubernetes.io/part-of=ingress-nginx
Annotations:              kubectl.kubernetes.io/last-applied-configuration:
                            {"apiVersion":"v1","kind":"Service","metadata":{"annotations":{},"labels":{"app.kubernetes.io/name":"ingress-nginx","app.kubernetes.io/par...
Selector:                 app.kubernetes.io/name=ingress-nginx,app.kubernetes.io/part-of=ingress-nginx
Type:                     NodePort
IP:                       10.107.40.182
Port:                     http  80/TCP
TargetPort:               80/TCP
NodePort:                 http  32699/TCP
Endpoints:                10.244.2.73:80
Port:                     https  443/TCP
TargetPort:               443/TCP
NodePort:                 https  30842/TCP
Endpoints:                10.244.2.73:443
Session Affinity:         None
External Traffic Policy:  Cluster
Events:                   <none>

通过上面创建的service资源对象可以看出,随机分配的httpNodePort32668httpsNodePort的为30606。该端口也可以自定义,在前面的service章节说过。单一般不建议自定义。

示例1:使用Ingress发布Nginx

该示例中创建的所有资源都位于新建的testing名称空间中。与其他的资源在逻辑上进行隔离,以方便管理。

首先创建一个单独的目录为了方便管理

[root@k8s-master ~]# mkdir ingress-nginx/ingress
[root@k8s-master ~]# cd ingress-nginx/ingress/

(1)、创建testing名称空间(也可以使用命令直接创建# kubectl create namespace my-namespace,不过这里使用资源清单格式创建)

[root@k8s-master ingress]# vim namespace-testing.yaml    #编写namespace清单文件
apiVersion: v1
kind: Namespace
metadata:
  name: testing
  labels:
    env: testing
[root@k8s-master ingress]#
[root@k8s-master ingress]# kubectl apply -f namespace-testing.yaml    #创建namespace
namespace/testing created
[root@k8s-master ingress]#
[root@k8s-master ingress]# kubectl get namespace testing    #验证
NAME      STATUS   AGE
testing   Active   12s

(2)、部署nginx实例,这里使用Deployment控制器于testing中部署nginx相关的Pod对象。

[root@k8s-master ingress]# vim deployment-nginx.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: deploy-nginx
  namespace: testing
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.12
        ports:
        - name: http
          containerPort: 80
[root@k8s-master ingress]# 
[root@k8s-master ingress]# kubectl apply -f deployment-nginx.yaml 
deployment.apps/deploy-nginx created
[root@k8s-master ingress]# 
[root@k8s-master ingress]# kubectl get deploy -n testing
NAME           READY   UP-TO-DATE   AVAILABLE   AGE
deploy-nginx   3/3     3            3           5s
[root@k8s-master ingress]# 
[root@k8s-master ingress]# kubectl get pods -n testing
NAME                            READY   STATUS    RESTARTS   AGE
deploy-nginx-686bddcb56-9g7pq   1/1     Running   0          6s
deploy-nginx-686bddcb56-gqpm2   1/1     Running   0          6s
deploy-nginx-686bddcb56-vtwkq   1/1     Running   0          6s

(3)、创建Service资源,关联后端的Pod资源。这里通过service资源svc-nginx80端口去暴露容器的80端口。

[root@k8s-master ingress]# vim service-nginx.yaml
apiVersion: v1
kind: Service
metadata:
  name: svc-nginx
  namespace: testing
  labels:
    app: svc-nginx
spec:
  selector:
    app: nginx
  ports:
  - name: http
    port: 80
    targetPort: 80
    protocol: TCP
[root@k8s-master ingress]# 
[root@k8s-master ingress]# kubectl apply -f service-nginx.yaml 
service/svc-nginx created
[root@k8s-master ingress]# 
[root@k8s-master ingress]# kubectl get svc -n testing
NAME        TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)          AGE
svc-nginx   ClusterIP   10.99.233.90   <none>        80/TCP           6s
[root@k8s-master ingress]# 
[root@k8s-master ingress]# kubectl describe svc/svc-nginx -n testing
Name:              svc-nginx
Namespace:         testing
Labels:            app=svc-nginx
Annotations:       kubectl.kubernetes.io/last-applied-configuration:
                     {"apiVersion":"v1","kind":"Service","metadata":{"annotations":{},"labels":{"app":"svc-nginx"},"name":"svc-nginx","namespace":"testing"},"s...
Selector:          app=nginx
Type:              ClusterIP
IP:                10.99.233.90
Port:              http  80/TCP
TargetPort:        80/TCP
Endpoints:         10.244.1.76:80,10.244.1.77:80,10.244.2.74:80
Session Affinity:  None
Events:            <none>

(4)、创建Ingress资源,匹配Service资源svc-nginx,并将svc-nginx的80端口暴露。

[root@k8s-master ingress]# vim ingress-nginx.yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: nginx
  namespace: testing
  annotations:
    kubernetes.io/ingress.class: "nginx"
spec:
  rules:
  - host: nginx.ilinux.io
    http:
      paths:
      - path:
        backend:
          serviceName: svc-nginx
          servicePort: 80
[root@k8s-master ingress]# 
[root@k8s-master ingress]# kubectl apply -f ingress-nginx.yaml 
ingress.extensions/nginx created
[root@k8s-master ingress]# 
[root@k8s-master ingress]# kubectl get ingress -n testing
NAME    HOSTS              ADDRESS   PORTS   AGE
nginx   nginx.ilinux.io             80      16s
[root@k8s-master ingress]# 
[root@k8s-master ingress]# kubectl describe ingress -n testing
Name:             nginx
Namespace:        testing
Address:          
Default backend:  default-http-backend:80 (<none>)
Rules:
  Host              Path  Backends
  ----              ----  --------
  tomcat.ilinux.io  
                       svc-nginx:80 (10.244.1.76:80,10.244.1.77:80,10.244.2.74:80)
Annotations:
  kubectl.kubernetes.io/last-applied-configuration:  {"apiVersion":"extensions/v1beta1","kind":"Ingress","metadata":{"annotations":{"kubernetes.io/ingress.class":"nginx"},"name":"nginx","namespace":"testing"},"spec":{"rules":[{"host":"nginx.ilinux.io","http":{"paths":[{"backend":{"serviceName":"svc-nginx","servicePort":80},"path":null}]}}]}}

  kubernetes.io/ingress.class:  nginx
Events:                         <none>

(5)、测试,通过Ingress控制器的前端的Service资源的NodePort来访问此服务,

#首先查看前面部署Ingress控制器的前端的Service资源的映射端口
[root@k8s-master ingress-nginx]# kubectl get svc -n ingress-nginx
NAME            TYPE       CLUSTER-IP      EXTERNAL-IP   PORT(S)                      AGE
ingress-nginx   NodePort   10.107.40.182   <none>        80:32699/TCP,443:30842/TCP   3m59s

#终端测试,添加hosts
[root@k8s-master ~]# cat /etc/hosts
192.168.1.31    k8s-master nginx.ilinux.io
192.168.1.32    k8s-node1 nginx.ilinux.io
192.168.1.33    k8s-node2 nginx.ilinux.io
#访问测试
[root@k8s-master ~]# curl nginx.ilinux.io:32699
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
......

验证是否调度到后端的Pod资源,查看日志

[root@k8s-master ~]# kubectl get pods -n testing
NAME                            READY   STATUS    RESTARTS   AGE
deploy-nginx-686bddcb56-9g7pq   1/1     Running   0          56m
deploy-nginx-686bddcb56-gqpm2   1/1     Running   0          56m
deploy-nginx-686bddcb56-vtwkq   1/1     Running   0          56m
[root@k8s-master ~]# kubectl logs deploy-nginx-686bddcb56-9g7pq -n testing
10.244.2.75 - - [28/Sep/2019:02:33:45 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.29.0" "10.244.0.0"
10.244.2.75 - - [28/Sep/2019:02:44:02 +0000] "GET / HTTP/1.1" 200 612 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.100 Safari/537.36" "10.244.0.0"

(6)、配置TLS Ingress资源(这里使用自签证书)

1)生成key
[root@k8s-master ingress]# openssl genrsa -out tls.key 2048
2)生成证书
[root@k8s-master ingress]# openssl req -new -x509 -key tls.key -out tls.crt -subj /C=CN/ST=ShenZhen/L=ShenZhen/O=DevOps/CN=nginx.ilinux.io -days 3650

3)创建secret资源
[root@k8s-master ingress]# kubectl create secret tls nginx-ingress-secret --cert=tls.crt --key=tls.key -n testing
secret/nginx-ingress-secret created
[root@k8s-master ingress]# kubectl get secret -n testing
NAME                   TYPE                                  DATA   AGE
default-token-lfzrt    kubernetes.io/service-account-token   3      116m
nginx-ingress-secret   kubernetes.io/tls                     2      16s

4)编写Ingress资源清单文件
[root@k8s-master ingress]# vim ingress-nginx-https.yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: nginx-ingress-tls
  namespace: testing
  annotations:
    kubernetes.io/ingress.class: "nginx"
spec:
  tls:
  - hosts:
    - nginx.ilinux.io
    secretName: nginx-ingress-secret
  rules:
  - host: nginx.ilinux.io
    http:
      paths:
      - path: /
        backend:
          serviceName: svc-nginx
          servicePort: 80

5)查看Ingress资源信息
[root@k8s-master ingress]# kubectl get ingress -n testing
NAME                HOSTS             ADDRESS   PORTS     AGE
nginx               nginx.ilinux.io             80        66m
nginx-ingress-tls   nginx.ilinux.io             80, 443   15s
[root@k8s-master ingress]# kubectl describe ingress/nginx-ingress-tls -n testing
Name:             nginx-ingress-tls
Namespace:        testing
Address:          
Default backend:  default-http-backend:80 (<none>)
TLS:
  nginx-ingress-secret terminates nginx.ilinux.io
Rules:
  Host             Path  Backends
  ----             ----  --------
  nginx.ilinux.io  
                   /   svc-nginx:80 (10.244.1.76:80,10.244.1.77:80,10.244.2.74:80)
Annotations:
  kubectl.kubernetes.io/last-applied-configuration:  {"apiVersion":"extensions/v1beta1","kind":"Ingress","metadata":{"annotations":{"kubernetes.io/ingress.class":"nginx"},"name":"nginx-ingress-tls","namespace":"testing"},"spec":{"rules":[{"host":"nginx.ilinux.io","http":{"paths":[{"backend":{"serviceName":"svc-nginx","servicePort":80},"path":"/"}]}}],"tls":[{"hosts":["nginx.ilinux.io"],"secretName":"nginx-ingress-secret"}]}}

  kubernetes.io/ingress.class:  nginx
Events:
  Type    Reason  Age   From                      Message
  ----    ------  ----  ----                      -------
  Normal  CREATE  64s   nginx-ingress-controller  Ingress testing/nginx-ingress-tls

(7)、测试https(这里由于是自签,所以上面提示不安全)

#首先查看前面部署Ingress控制器的前端的Service资源的映射端口
[root@k8s-master ingress-nginx]# kubectl get svc -n ingress-nginx
NAME            TYPE       CLUSTER-IP      EXTERNAL-IP   PORT(S)                      AGE
ingress-nginx   NodePort   10.107.40.182   <none>        80:32699/TCP,443:30842/TCP   3m59s

示例2:使用Ingress发布多个服务

将不同的服务映射不同的主机上

准备工作:这里创建一个目录保存本示例的所有资源配置清单

[root@k8s-master ~]# mkdir ingress-nginx/multi_svc
[root@k8s-master ~]# cd !$

创建名称空间

创建一个名称空间保存本示例的所有对象(方便管理)

[root@k8s-master multi_svc]# vim namespace-ms.yaml    #编写配置清单文件
apiVersion: v1
kind: Namespace
metadata:
  name: multisvc 
  labels:
    env: multisvc

[root@k8s-master multi_svc]# kubectl apply -f namespace-ms.yaml     #创建上面定义的名称空间
namespace/multisvc created

[root@k8s-master multi_svc]# kubectl get namespace multisvc    #查看名称空间
NAME       STATUS   AGE
multisvc   Active   9s

创建后端应用和Service

这里后端应用创建为一组nginx应用和一组tomcat应用

1)编写资源清单文件,这里将service资源对象和deployment控制器写在这一个文件里

[root@k8s-master multi_svc]# vim deploy_service-ms.yaml
#tomcat应用的Deployment控制器
apiVersion: apps/v1
kind: Deployment
metadata:
  name: tomcat-deploy
  namespace: multisvc
spec:
  replicas: 3
  selector:
    matchLabels:
      app: tomcat
  template:
    metadata:
      labels: 
        app: tomcat
    spec:
      containers:
      - name: tomcat
        image: tomcat:jdk8
        imagePullPolicy: IfNotPresent
        ports:
        - name: httpport 
          containerPort: 8080
        - name: ajpport
          containerPort: 8009
---
#tomcat应用的Service资源
apiVersion: v1
kind: Service
metadata:
  name: tomcat-svc
  namespace: multisvc
  labels:
    app: tomcat-svc
spec:
  selector:
    app: tomcat
  ports:
  - name: httpport
    port: 8080
    targetPort: 8080
    protocol: TCP
  - name: ajpport
    port: 8009
    targetPort: 8009
    protocol: TCP

---
#nginx应用的Deployment控制器
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deploy
  namespace: multisvc
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels: 
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.12
        imagePullPolicy: IfNotPresent
        ports:
        - name: http
          containerPort: 80
---
#nginx应用的Service资源
apiVersion: v1
kind: Service
metadata:
  name: nginx-svc
  namespace: multisvc
  labels:
    app: nginx-svc
spec:
  selector:
    app: nginx
  ports:
  - name: http
    port: 80
    targetPort: 80
    protocol: TCP

2)创建上面定义资源对象并查看验证

[root@k8s-master multi_svc]# kubectl apply -f deploy_service-ms.yaml 
deployment.apps/tomcat-deploy created
service/tomcat-svc created
deployment.apps/nginx-deploy created
service/nginx-svc created
[root@k8s-master multi_svc]# kubectl get pods -n multisvc -o wide    #查看pod资源
NAME                             READY   STATUS    RESTARTS   AGE   IP            NODE        NOMINATED NODE   READINESS GATES
nginx-deploy-86c667ff66-hl6rx    1/1     Running   0          13s   10.244.2.78   k8s-node2   <none>           <none>
nginx-deploy-86c667ff66-hx4j8    1/1     Running   0          13s   10.244.2.77   k8s-node2   <none>           <none>
nginx-deploy-86c667ff66-tl9mm    1/1     Running   0          13s   10.244.1.79   k8s-node1   <none>           <none>
tomcat-deploy-6484688ddc-n25hn   1/1     Running   0          13s   10.244.1.78   k8s-node1   <none>           <none>
tomcat-deploy-6484688ddc-s8dts   1/1     Running   0          13s   10.244.1.80   k8s-node1   <none>           <none>
tomcat-deploy-6484688ddc-snszk   1/1     Running   0          13s   10.244.2.76   k8s-node2   <none>           <none>
[root@k8s-master multi_svc]# kubectl get svc -n multisvc    #查看service资源对象
NAME         TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)             AGE
nginx-svc    ClusterIP   10.104.213.237   <none>        80/TCP              26s
tomcat-svc   ClusterIP   10.103.75.161    <none>        8080/TCP,8009/TCP   26s

[root@k8s-master multi_svc]# kubectl describe svc/nginx-svc -n multisvc    #查看service对象nginx-svc的详细信息
Name:              nginx-svc
Namespace:         multisvc
Labels:            app=nginx-svc
Annotations:       kubectl.kubernetes.io/last-applied-configuration:
                     {"apiVersion":"v1","kind":"Service","metadata":{"annotations":{},"labels":{"app":"nginx-svc"},"name":"nginx-svc","namespace":"multisvc"},"...
Selector:          app=nginx
Type:              ClusterIP
IP:                10.104.213.237
Port:              http  80/TCP
TargetPort:        80/TCP
Endpoints:         10.244.1.79:80,10.244.2.77:80,10.244.2.78:80
Session Affinity:  None
Events:            <none>

[root@k8s-master multi_svc]# kubectl describe svc/tomcat-svc -n multisvc    #查看service对象tomcat-svc的详细信息
Name:              tomcat-svc
Namespace:         multisvc
Labels:            app=tomcat-svc
Annotations:       kubectl.kubernetes.io/last-applied-configuration:
                     {"apiVersion":"v1","kind":"Service","metadata":{"annotations":{},"labels":{"app":"tomcat-svc"},"name":"tomcat-svc","namespace":"multisvc"}...
Selector:          app=tomcat
Type:              ClusterIP
IP:                10.103.75.161
Port:              httpport  8080/TCP
TargetPort:        8080/TCP
Endpoints:         10.244.1.78:8080,10.244.1.80:8080,10.244.2.76:8080
Port:              ajpport  8009/TCP
TargetPort:        8009/TCP
Endpoints:         10.244.1.78:8009,10.244.1.80:8009,10.244.2.76:8009
Session Affinity:  None
Events:            <none>

创建Ingress资源对象

1)编写资源清单文件

[root@k8s-master multi_svc]# vim ingress_host-ms.yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: multi-ingress
  namespace: multisvc
spec:
  rules:
  - host: nginx.imyapp.com
    http:
      paths: 
      - path: /
        backend:
          serviceName: nginx-svc
          servicePort: 80
  - host: tomcat.imyapp.com
    http:
      paths:
      - path: /
        backend:
          serviceName: tomcat-svc
          servicePort: 8080

2)创建上面定义资源对象并查看验证

[root@k8s-master multi_svc]# kubectl apply -f ingress_host-ms.yaml 
ingress.extensions/multi-ingress created
[root@k8s-master multi_svc]# kubectl get ingress -n multisvc    #查看ingress资源对象
NAME            HOSTS                                ADDRESS   PORTS   AGE
multi-ingress   nginx.imyapp.com,tomcat.imyapp.com             80      18s

[root@k8s-master multi_svc]# kubectl describe ingress/multi-ingress -n multisvc    #查看ingress资源multi-ingrsss的详细信息
Name:             multi-ingress
Namespace:        multisvc
Address:          
Default backend:  default-http-backend:80 (<none>)
Rules:
  Host               Path  Backends
  ----               ----  --------
  nginx.imyapp.com   
                     /   nginx-svc:80 (10.244.1.79:80,10.244.2.77:80,10.244.2.78:80)
  tomcat.imyapp.com  
                     /   tomcat-svc:8080 (10.244.1.78:8080,10.244.1.80:8080,10.244.2.76:8080)
Annotations:
  kubectl.kubernetes.io/last-applied-configuration:  {"apiVersion":"extensions/v1beta1","kind":"Ingress","metadata":{"annotations":{},"name":"multi-ingress","namespace":"multisvc"},"spec":{"rules":[{"host":"nginx.imyapp.com","http":{"paths":[{"backend":{"serviceName":"nginx-svc","servicePort":80},"path":"/"}]}},{"host":"tomcat.imyapp.com","http":{"paths":[{"backend":{"serviceName":"tomcat-svc","servicePort":8080},"path":"/"}]}}]}}

Events:
  Type    Reason  Age   From                      Message
  ----    ------  ----  ----                      -------
  Normal  CREATE  39s   nginx-ingress-controller  Ingress multisvc/multi-ingress

测试访问

这是测试自定义的域名,故需要配置host

192.168.1.31     nginx.imyapp.com tomcat.imyapp.com
192.168.1.32     nginx.imyapp.com tomcat.imyapp.com
192.168.1.33     nginx.imyapp.com tomcat.imyapp.com

查看部署的IngressService对象的端口

[root@k8s-master multi_svc]# kubectl get svc -n ingress-nginx
NAME            TYPE       CLUSTER-IP      EXTERNAL-IP   PORT(S)                      AGE
ingress-nginx   NodePort   10.107.40.182   <none>        80:32699/TCP,443:30842/TCP   6h39m

访问nginx.imyapp.com:32699

访问tomcat.imyapp.com:32699

配置Ingress处理TLS传输

这里使用自签证书,通过OpenSSL进行创建

1)创建证书

#创建nginx.imyapp.com域名的证书
[root@k8s-master multi_svc]# openssl genrsa -out nginx.imyapp.com.key 2048
[root@k8s-master multi_svc]# openssl req -new -x509 -key nginx.imyapp.com.key -out nginx.imyapp.com.crt -subj /C=CN/ST=ShenZhen/L=ShenZhen/O=DevOps/CN=nginx.imyapp.com -days 3650

#创建tomcat.imyapp.com域名的证书
[root@k8s-master multi_svc]# openssl genrsa -out tomcat.imyapp.com.key 2048
[root@k8s-master multi_svc]# openssl req -new -x509 -key tomcat.imyapp.com.key -out tomcat.imyapp.com.crt -subj /C=CN/ST=ShenZhen/L=ShenZhen/O=DevOps/CN=tomcat.imyapp.com -days 3650

#查看生成的证书
[root@k8s-master multi_svc]# ll *.com.*
-rw-r--r-- 1 root root 1298 9月  28 17:23 nginx.imyapp.com.crt
-rw-r--r-- 1 root root 1675 9月  28 17:22 nginx.imyapp.com.key
-rw-r--r-- 1 root root 1302 9月  28 17:24 tomcat.imyapp.com.crt
-rw-r--r-- 1 root root 1679 9月  28 17:24 tomcat.imyapp.com.key

2)创建secrte

#创建nginx域名的secret
[root@k8s-master multi_svc]# kubectl create secret tls nginx-ingress-secret --cert=nginx.imyapp.com.crt --key=nginx.imyapp.com.key -n multisvc
secret/nginx-ingress-secret created

#创建tomcat域名的secret
[root@k8s-master multi_svc]# kubectl create secret tls tomcat-ingress-secret --cert=tomcat.imyapp.com.crt --key=tomcat.imyapp.com.key -n multisvc
secret/tomcat-ingress-secret created

#查看secret
[root@k8s-master multi_svc]# kubectl get secret -n multisvc
NAME                    TYPE                                  DATA   AGE
default-token-mf5wd     kubernetes.io/service-account-token   3      5h12m
nginx-ingress-secret    kubernetes.io/tls                     2      53s
tomcat-ingress-secret   kubernetes.io/tls                     2      27s

3)编写带TLSIngress资源清单(这里通过复制,没有删除上面创建的ingress

[root@k8s-master multi_svc]# cp ingress_host-ms.yaml ingress_host_https-ms.yaml
[root@k8s-master multi_svc]# vim ingress_host_https-ms.yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: multi-ingress-https
  namespace: multisvc
  annotations:
    kubernetes.io/ingress.class: "nginx"
spec:
  tls:
  - hosts:
    - nginx.imyapp.com
    secretName: nginx-ingress-secret
  - hosts: 
    - tomcat.imyapp.com
    secretName: tomcat-ingress-secret
  rules:
  - host: nginx.imyapp.com
    http:
      paths: 
      - path: /
        backend:
          serviceName: nginx-svc
          servicePort: 80
  - host: tomcat.imyapp.com
    http:
      paths:
      - path: /
        backend:
          serviceName: tomcat-svc
          servicePort: 8080

4)创建ingress资源

[root@k8s-master multi_svc]# kubectl apply -f ingress_host_https-ms.yaml
ingress.extensions/multi-ingress-https created
[root@k8s-master multi_svc]# kubectl get ingress -n multisvc
NAME                  HOSTS                                ADDRESS   PORTS     AGE
multi-ingress         nginx.imyapp.com,tomcat.imyapp.com             80        44m
multi-ingress-https   nginx.imyapp.com,tomcat.imyapp.com             80, 443   3s

5)测试,通过Ingress控制器的前端的Service资源的NodePort来访问此服务,上面看到ingress控制器的service资源的443端口对应的节点的30842端口。

访问nginx

访问tomcat

将不同的服务映射到相同主机的不同路径

在这种情况下,根据请求的URL中的路径,请求将发送到两个不同的服务。因此,客户端可以通过一个IP地址(Ingress 控制器的IP地址)访问两种不同的服务。

注意:这里Ingresspath的定义,需要与后端真实Service提供的Path一致,否则将被转发到一个不存在的path上,引发错误。

Ingress定义示例

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: tomcat-ingress
  namespace: multisvc
spec:
  rules:
  - host: www.imyapp.com
    http:
      paths: 
      - path: /nginx
        backend:
          serviceName: nginx-svc
          servicePort: 80
      - path: /tomcat
        backend:
          serviceName: tomcat-svc
          servicePort: 8080

 

 

 

Kubernetes as Database: 使用kubesql查询kubernetes资源

Kubernetes as Database: 使用kubesql查询kubernetes资源

写在前面

kubectl虽然查询单个的kubernetes资源或者列表都已经比较方便,但是进行更为多个资源的联合查询(比如pod和node),以及查询结果的二次处理方面却是kubectl无法胜任的。所以一直以来,我都有想法将kubernetes作为数据库进行查询。在去年,我开发了第二个版本的kubesql。相关信息在https://xuxinkun.github.io/2019/03/11/kubesql/,代码留存在https://github.com/xuxinkun/kubesql/tree/python。这个版本较之我最早的spark离线方式已经有所改观,但是无法应对中型、甚至较小规模的集群,性能上存在较大问题。部署上也较为繁杂,且不够稳定,有一些bug(会异常退出)。而且对于label等字段都无法处理,可用性较差。我总起来不满意,但是一直没时间去重构。直到最近,听了关于presto的一个分享,我感觉重构的机会来了。

这一次kubesql完全抛弃了原有的架构,基于presto进行开发。这里摘抄一段presto的简介:presto是一个开源的分布式SQL查询引擎,适用于交互式分析查询,数据量支持GB到PB字节。Presto的设计和编写完全是为了解决像Facebook这样规模的商业数据仓库的交互式分析和处理速度的问题。presto具有丰富的插件接口,可以极为便捷的对接外部存储系统。

考虑使用presto的主要原因是避免了SQL查询引擎的逻辑耦合到kubesql中,同时其稳定和高性能保证了查询的效率。这样kubesql的主要逻辑专注于获取k8s的resource变化,以及将resource转化为关系型数据的逻辑上。

kubesql使用

重构后的kubesql开源项目地址在https://github.com/xuxinkun/kubesql。

先介绍下如何部署和使用。部署方式目前主要使用docker部署,很快会支持k8s的部署方式。

部署前需要获取kubeconfig。假设kubeconfig位于/root/.kube/config路径下,则只要一条命令即可运行。

docker run -it -d --name kubesql -v /root/.kube/config:/home/presto/config xuxinkun/kubesql:latest

如果桥接网络不能通k8s api,则可以使用物理机网络,加入--net=host参数即可。注意presto端口使用8080,可能会有端口冲突。

而后就可以进行使用了。使用命令为

docker exec -it kubesql presto --server localhost:8080 --catalog kubesql --schema kubesql

这时自动进入交互式查询模式,即可进行使用了。目前已经支持了pods和nodes两种资源的查询,对应为三张表,nodes,pods和containers(container是从pod中拆出来的,具体原因见下文原理一节)。

三张表支持的列参见https://github.com/xuxinkun/kubesql/blob/master/docs/table.md。

presto支持一些内置的函数,可以用这些函数来丰富查询。https://prestodb.io/docs/current/functions.html。

这里我举一些使用kubesql查询的例子。

比如想要查询每个pod的cpu资源情况(requests和limits)。

presto:kubesql> select pods.namespace,pods.name,sum("requests.cpu") as "requests.cpu" ,sum("limits.cpu") as "limits.cpu" from pods,containers where pods.uid = containers.uid group by pods.namespace,pods.name
     namespace     |                 name                 | requests.cpu | limits.cpu 
-------------------+--------------------------------------+--------------+------------
 rongqi-test-01    | rongqi-test-01-202005151652391759    |          0.8 |        8.0 
 ljq-nopassword-18 | ljq-nopassword-18-202005211645264618 |          0.1 |        1.0 

又比如我想要查询每个node上剩余可以分配的cpu情况(用node上allocatable.cpu减去node上所有pod的requests.cpu的总和)

presto:kubesql> select nodes.name, nodes."allocatable.cpu" - podnodecpu."requests.cpu" from nodes, (select pods.nodename,sum("requests.cpu") as "requests.cpu" from pods,containers where pods.uid = containers.uid group by pods.nodename) as podnodecpu where nodes.name = podnodecpu.nodename;
    name     |       _col1        
-------------+--------------------
 10.11.12.29 | 50.918000000000006 
 10.11.12.30 |             58.788 
 10.11.12.32 | 57.303000000000004 
 10.11.12.34 |  33.33799999999999 
 10.11.12.33 | 43.022999999999996 

再比如需要查询所有所有2020-05-12后创建的pod。

presto:kube> select name, namespace,creationTimestamp from pods where creationTimestamp > date(''2020-05-12'') order by creationTimestamp desc;
                         name                         |        namespace        |    creationTimestamp    
------------------------------------------------------+-------------------------+-------------------------
 kube-api-webhook-controller-manager-7fd78ddd75-sf5j6 | kube-api-webhook-system | 2020-05-13 07:56:27.000 

还可以根据标签来查询,查询所有标签的appid是springboot,且尚未调度成功的pod。以及计数。

标签appid在pods表里则会有一列,列名为"labels.appid",使用该列作为条件来删选pod。

presto:kubesql> select namespace,name,phase from pods where phase = ''Pending'' and "labels.appid" = ''springboot'';
     namespace      |     name     |  phase  
--------------------+--------------+---------
 springboot-test-rd | v6ynsy3f73jn | Pending 
 springboot-test-rd | mu4zktenmttp | Pending 
 springboot-test-rd | n0yvpxxyvk4u | Pending 
 springboot-test-rd | dd2mh6ovkjll | Pending 
 springboot-test-rd | hd7b0ffuqrjo | Pending
 
 presto:kubesql> select count(*) from pods where phase = ''Pending'' and "labels.appid" = ''springboot'';
  _col0 
 -------
      5 

kubesql原理

kubesql的架构如图所示:

kubesql-arc

kubesql里主要有三个模块部分:

  • kubesql-watcher: 监听k8s api pod和node的变化。并将pod和node的结构化数据转化为关系型数据(以Map的方式进行保存)。
  • kubecache: 用于缓存pod和node的数据。
  • kubesql-connector: 作为presto的connector,接受来自presto的调用,通过kubecache查询列信息和对应数据,并返回给presto关于列和数据的信息。

其中最主要的部分是kubesql-connector。presto插件开发指南可以参考https://prestodb.io/docs/current/develop.html。我没有选择从零开始,而是基于已有的localfile插件https://github.com/prestodb/presto/tree/0.234.2/presto-local-file进行的开发。如何进行presto的插件开发,后面我再写文章来解读。

由于所有数据都缓存在内存中,因此几乎无磁盘需求。但是也需要根据集群的规模来提供较大的内存。

以pod数据为例,pod中主要数据分成三部分,metadata,spec和status。

metadata中比较难以处理的部分是label和annotation。我将label这个map进行展平,每个key都作为一列。比如

labels:
    app: mysql
    owner: xxx

我使用labels作为前缀,拼合labels里面的key作为列名。从而得到两条数据为:

labels.app: mysql
labels.owner: xxx

对于pod A存在app的label但是pod B并没有该标签,则对于pod B来说,该列labels.app的值则为null。

类似的annotations也是类似的处理方式。从而让annotations也就可以成为可以用于筛选pod的条件了。

对于spec来说,最大的困难在于containers的处理。因为一个pod里面可能有若干个containers,因此我直接将containers作为一张新的表。同时在containers表里增加一个uid的列,用来表明该行数据来自于哪个pod。containers里面的字段也对应都加入到containers表中。containers中比较重要的关于资源的如request和limit,我直接使用requests.作为前缀,拼合resource作为列名。比如requests.cpurequests.memory等。这里cpu的单独处理为double类型,单位为核,比如100m这里会转化为0.1。内存等则为bigint,单位为B。

对于status中,比较难于处理的是conditions和containerStatus。conditions是一个列表,但是每个condition的type不相同。因此我将type作为前缀,用来生成conditon的列名。比如:

  conditions:
  - lastProbeTime: null
    lastTransitionTime: 2020-04-22T09:03:10Z
    status: "True"
    type: Ready
  - lastProbeTime: null
    lastTransitionTime: 2020-04-22T09:03:10Z
    status: "True"
    type: ContainersReady

那么在pod表中,我对应可以得到这些列:

Column Type Extra Comment
containersready.lastprobetime timestamp
containersready.lasttransitiontime timestamp
containersready.message varchar
containersready.reason varchar
containersready.status varchar
ready.lastprobetime timestamp
ready.lasttransitiontime timestamp
ready.message varchar
ready.reason varchar
ready.status varchar

这样我就可以通过"ready.status" = "True" 来筛选condition里type为ready且status为True的pod了。

containerStatus因为与containers一一对应,因此我将containerStatus合并到containers表里,并且根据container name一一对应起来。

后记

本次重构后kubesql我直接发布为1.0.0版本,并且已经在日常使用了。且借助于内存和presto的高性能,我测试过5万pod的集群,查询时间为毫秒级。目前暂未发现明显的bug。大家有发现bug或者新的feature也可以提issue给我。我后期也会再维护该项目。

因为目前只有pods和nodes资源,相对于k8s庞大的资源来说,还只是冰山一角。但是增加每个资源要加入相当数量的代码。我也在考虑如何使用openapi的swagger描述来自动生成代码。

部署上现在是用docker来部署,马上也会增加kubernetes的部署方式,这样会更加便捷。

同时我在考虑,在未来,让presto的每个worker负责一个集群的cache。这样一个presto集群可以查询所有的k8s集群的信息。该功能还需要再做设计和考虑。

Kubernetes 使用 Kubeadm 推出自己的托管服务

Kubernetes 使用 Kubeadm 推出自己的托管服务

最近的 Kubernetes 容器协调软件kubeadm发布。

Kubeadm 可以通过单个命令启动 Kubernetes 集群。群集的设置由最佳实践默认值定义,完全符合  Certified Kubernetes  准则。

Kubeadm 在部署 Kubernetes 集群时自动化了一些棘手的步骤,例如发布和协调每个节点的安全证书,以及理顺系统基于角色的访问控制(RBAC)的必要权限。除了在集群上安装 Kubernetes 之外,kubeadm 还可以升级 Kubernetes 集群,或者只是执行选择操作作为安装过程的一部分,称为阶段。

对于想要第一次尝试 Kubernetes 的人,以及想要在裸机系统上部署 Kubernetes 的人来说,Kubeadm 可以节省时间。对于那些从云端获取 Kubernetes 的人来说,kubeadm 提供了一种简单的方法来定制 Kubernetes 运行云服务,如  Amazon EKS  和  Google Kubernetes Engine,符合他们自己的规范。

kubeadm 可能更适合作为更大的部署系统的组件,虽然该系统负责 Kubernetes 部署的其他方面,例如网络。但它并不意味着是一个全栈 Kubernetes 安装工具,例如 Kops。Kubeadm可以使Kops维护者更简单,因为它标准化了堆栈的那一部分的安装过程和接口。

适用于 Mac或Windows 的 Minikube 使用 kubeadm 作为基础。它适用于  Cluster API,Kubernetes 提供了一组用于创建,配置和管理集群的声明式Kubernetes 样式 API。

最后,要运行 kubeadm,现有的群集硬件或虚拟机必须已启动并正在运行。并且与符合 Container Networking Interface 的覆盖网络配合良好。通过在一台计算机上运行 kubeadm init 命令来创建新的控件 plain 和 Kubernetes 实例  。该过程还使用 kubeadm_join命令 为其他节点生成连接令牌以进行登录。 

Kubernetes 学习22 kubernetes容器资源需求资源限制及HeapSter(翻车章节)

Kubernetes 学习22 kubernetes容器资源需求资源限制及HeapSter(翻车章节)

一、概述

  1、接下来介绍在k8s上运行pod对象时我们如何去监控我们系统级的资源指标以及业务级别的资源指标。数据如何获取和监控。在此之前先介绍一下Pod对象的资源请求和资源限制。即容器的资源需求和资源限制。在docker中说到过容器是可以资源限额的,在启动容器时候我们可以定义一个容器可以使用多少的cpu和内存资源。在当时说过cpu资源是可压缩资源,一个pod或一个容器在应该获取指定资源获取不到时等待即可,但是对内存来讲就不是这样的,假如他用到的资源不够就有可能会因为内存资源耗尽而被killd 。不过在我们k8s之上资源有两个维度限制,一个是定义其起始值,一个是定义其终结值。

    a、requests:可以使用资源的requests来定义pod至少会用到多少CPU或内存资源。即资源需求,最低保障。

    b、limits:即限额,也叫限制,也是硬限制。一般来讲我们的requests是小于等于limits的。

二、Pod资源限额  

  1、虽然这些限制是应用在容器之上的,我们一般会把其称之为Pod的资源需求或Pod的资源限制。配置之前先描述一下二者的不同指标。

    a、CPU:在k8s上一个单位的cpu相当于我们对应的一颗虚拟CPU,一颗CPU是指一颗逻辑CPU,也就是指一个核心。或者是一个超线程。一个两核双线程的CPU其实是会被虚拟成4颗逻辑cpu。 1个cpu还能被单独划分成子单位。一个核心相当于1000个毫核心。也就是milicores。也即 500m=0.5CPU

    b、内存:计量单位为E、P、T、G、M、K,一般而言我们以Ei,Pi...等为计量单位。

  2、我们一般是这样划分CPU的资源指标占用量的。比如有一个区间是我们的CPU limit,假设我们现在一个pod中的某一容器最多只能使用0.5个cpu核心,那么我们就定义cpu.limits=500m,但是这500m是其硬上限。即多用是不可以的,不用的话是可以的。除此之外我们还有一个限制叫request,意思是我们要想运行这个容器至少得有多少个cpu才行。假如cpu.requests=200m,意思就是我们要想能够确保这个容器运行至少得给其0.2个cpu核心。这是他最低保障。并不是他上来真正会用这么多,而是说要确保被调度的目标节点上得有这么多空闲cpu可用。每一个节点已经有多少资源被分配出去了就是靠这个节点上所运行的所有容器的cpu的requests加起来。比如我们现在有4个容器,每个容器至少需要0.5核我们cpu只有四核则意味着还有2核空闲,因此我们随后调度时如果一个pod中的容器要求至少要使用3个cpu那么在第一次预选时就被淘汰了,因为他无法在节点资源级满足这个对应的容器的需要。所以我们去计算一个pod中的容器需要多少资源时是靠这个requests作为他的需求量的。cpu.limits叫最大保证,如果我们系统资源无法保证你能用500m,但是至少要确保要有cpu.requests中定义的200m这么多的资源可用。所以我们说cpu.requests对我们系统调度来说是非常重要的。

     

  3、接下来我们简单创建一个pod看看定义他的资源需求以后他是怎么运行的。我们定义资源需求他只定义了资源的请求下限。我们明确说他至少要有200m,但很有可能系统上有4个cpu核心,而这个应用程序因为bug或其它原因把这四核全吃了也是有可能的。因此我们一般而言要把需求和上限要同时使用,不然其有可能会吃掉节点所有资源。所以我们这两个维度的限制作用各不相同。requests需求用于确保我们在调度时以确保对应节点上要拥有这么多资源满足这个pod运行的某些功能时的基本需求。而limits则用于限制这个容器无论怎么运行绝对不能超过的资源阈值。

    a、首先我们来看其定义方式。可以看到对应的使用方式我们可以在explain的连接中查看

[root@k8smaster ~]# kubectl explain pods.spec.containers.resources
KIND:     Pod
VERSION:  v1

RESOURCE: resources <Object>

DESCRIPTION:
     Compute Resources required by this container. Cannot be updated. More info:
     https://kubernetes.io/docs/concepts/storage/persistent-volumes#resources

     ResourceRequirements describes the compute resource requirements.

FIELDS:
   limits    <map[string]string>
     Limits describes the maximum amount of compute resources allowed. More
     info:
     https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/

   requests    <map[string]string>
     Requests describes the minimum amount of compute resources required. If
     Requests is omitted for a container, it defaults to Limits if that is
     explicitly specified, otherwise to an implementation-defined value. More
     info:
     https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/

    b、首先我们去除前面打的污点然后再创建pod

[root@k8smaster metrics]# kubectl taint node k8snode1 node-type-
node/k8snode1 untainted
[root@k8smaster metrics]# kubectl taint node k8snode2 node-type-
node/k8snode2 untainted
[root@k8smaster metrics]# cat pod-demo.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: pod-demo
  namespace: default
  labels: #也可以在此处写上{app:myapp,tier:frontend}代替下面两行
    app: myapp #应用层级标签
    tier: frontend #架构层级标签,在分层架构中属于frontend层
  annotations:
    wohaoshuai.com/created-by: "cluster admin"
spec:
  containers: #是一个列表,具体定义方式如下
  - name: myapp
    image: ikubernetes/stress-ng #压测用的容器
    command: ["/usr/bin/stress-ng","-m 1","-c 1","--metrics-brief"] #-m 1表示启动一个子进程对内存做压测,-c 1表示启动一个子进程对cpu做压测。--metrics-brief表示使用简洁的输出格式。
    resources:
      requests:
        cpu: "200m"
        memory: "128Mi"
      limits: 
        cpu: "500m"
        memory: "200Mi"
[root@k8smaster metrics]# kubectl apply -f pod-demo.yaml 
pod/pod-demo created
[root@k8smaster metrics]# kubectl get pods -o wide
NAME                            READY     STATUS    RESTARTS   AGE       IP            NODE
myapp-deploy-5d9c6985f5-46rtw   1/1       Running   0          3h        10.244.2.12   k8snode2
myapp-deploy-5d9c6985f5-7nhwq   1/1       Running   0          3h        10.244.1.9    k8snode1
myapp-deploy-5d9c6985f5-p8gsg   1/1       Running   0          3h        10.244.2.13   k8snode2
pod-demo                        1/1       Running   0          2m        10.244.2.14   k8snode2

    c、b中的pod好像会压出问题,此处我们只压测cpu

[root@k8smaster metrics]# cat pod-demo.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: pod-demo
  namespace: default
  labels: #也可以在此处写上{app:myapp,tier:frontend}代替下面两行
    app: myapp #应用层级标签
    tier: frontend #架构层级标签,在分层架构中属于frontend层
  annotations:
    wohaoshuai.com/created-by: "cluster admin"
spec:
  containers: #是一个列表,具体定义方式如下
  - name: myapp
    image: ikubernetes/stress-ng #压测用的容器
    command: ["/usr/bin/stress-ng","-c 1","--metrics-brief"] #-c 1表示启动一个子进程对cpu做压测。--metrics-brief表示使用简洁的输出格式。
    resources:
      requests:
        cpu: "200m"
        memory: "128Mi"
      limits: 
        cpu: "500m"
        memory: "512Mi"
[root@k8smaster metrics]# kubectl apply -f pod-demo.yaml 
pod/pod-demo created
[root@k8smaster metrics]# kubectl get pods -o wide
NAME                            READY     STATUS    RESTARTS   AGE       IP            NODE
myapp-deploy-5d9c6985f5-46rtw   1/1       Running   0          3h        10.244.2.12   k8snode2
myapp-deploy-5d9c6985f5-7nhwq   1/1       Running   0          3h        10.244.1.9    k8snode1
myapp-deploy-5d9c6985f5-p8gsg   1/1       Running   0          3h        10.244.2.13   k8snode2
pod-demo                        1/1       Running   0          32s       10.244.2.15   k8snode2

[root@k8smaster ~]# kubectl exec -it pod-demo top #可以看到cpu用了25%,我们虚拟机cpu为2核,因为容器上限为500m因此使用率为25%
Mem: 1605360K used, 261688K free, 19624K shrd, 2108K buff, 1002740K cached
CPU:  26% usr   1% sys   0% nic  72% idle   0% io   0% irq   0% sirq
Load average: 2.16 1.32 0.89 4/393 12
  PID  PPID USER     STAT   VSZ %VSZ CPU %CPU COMMAND
    7     1 root     R     6884   0%   1  25% {stress-ng-cpu} /usr/bin/stress-ng -c 1 --metrics-brief
    1     0 root     S     6244   0%   1   0% /usr/bin/stress-ng -c 1 --metrics-brief
    8     0 root     R     1504   0%   0   0% top

     d、requests是定义了能够被调度器作为基本衡量条件的调度值使用的,比如我们调度器他能根据requests属性定义的资源请求量来判定哪些节点能够接受这个pod去运行。对每一个节点来讲他们会把所有容器的资源需求量加起来作为已分配处理的配额。

  4、需要说明的是容器中的可见资源量和他们的可用资源量不是一回事。我们限制了以后,比如使用limit限制资源限额,使用requests限制资源需求。使用free命令看到的应该是整个节点上的内存而不会仅仅是当前这个容器中的内存。那么这样会有问题,比如我在容器中运行一个jvm,jvm上的很多程序在跑起来怎么区分队列层呢?可能会使用默认节点可用内存的百分之多少的样子,如果我们在容器中看到的内存可用量不是对应的容器的最大限额而是节点的内存量这儿可能就有点麻烦,计算结果有可能吞掉整个容器内存都不够。所以我们说目前来讲在容器的资源限制方面在这个维度上还是存在一些问题的。另外,我们每创建一个pod以后我们去describe这个pod可以看到我们pod有个QoS Class(服务质量类别)。因此我们在容器中限制了资源应用以后他会自动被分配一个叫QoS的。他会自动属于某个QoS类别。默认情况下QoS类别有三个:

    a、Guranteed:保证。即每个容器的cpu资源设置了相同值的requests和limit属性。即cpu的资源需求量,内存资源需求量两边都定义了,并且requests都等于limits。即同时设置了CPU和内存的requests和limits,最重要的是要满足 cpu.limits=cpu.requests同理memory.limits=memory.requests。这类pod就具有最高优先级,即我们现在有很多Pod需要运行的时候假如节点资源不够无法满足所有的pod的容器去运行这类容器是要被优先运行的。因为他的优先级最高。而但凡这样做了属性设置的会被自动归类为Guranteed类别。

    b、Burstable:至少有一个容器设置了cpu或内存资源的requests属性。其定义了requests但是可能没有定义limit之类的。因此这一类就具有中等优先级。我们刚刚设置了内存的requests也设置了cpu的requests,但是内存和cpu的requests和其limit是不相等的。因此其被自动归类为Burstable。

[root@k8smaster metrics]# cat pod-demo.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: pod-demo
  namespace: default
  labels: #也可以在此处写上{app:myapp,tier:frontend}代替下面两行
    app: myapp #应用层级标签
    tier: frontend #架构层级标签,在分层架构中属于frontend层
  annotations:
    wohaoshuai.com/created-by: "cluster admin"
spec:
  containers: #是一个列表,具体定义方式如下
  - name: myapp
    image: ikubernetes/myapp:v1
    resources:
      requests:
        cpu: "200m"
        memory: "512Mi"
      limits: 
        cpu: "200m"
        memory: "512Mi"
[root@k8smaster metrics]# kubectl apply -f pod-demo.yaml 
pod/pod-demo created
[root@k8smaster metrics]# kubectl describe pods pod-demo |grep QoS
QoS Class:       Guaranteed

    c、BestEffort:没有任何一个容器设置了requests或limits属性;最低优先级别

[root@k8smaster metrics]# kubectl get pods
NAME                            READY     STATUS    RESTARTS   AGE
myapp-deploy-5d9c6985f5-46rtw   1/1       Running   0          1d
myapp-deploy-5d9c6985f5-7nhwq   1/1       Running   0          1d
myapp-deploy-5d9c6985f5-p8gsg   1/1       Running   0          1d
pod-demo                        1/1       Running   0          10m

[root@k8smaster metrics]# kubectl describe pod myapp-deploy-5d9c6985f5-46rtw |grep QoS
QoS Class:       BestEffort

  5、需要注意的是当内存资源紧缺时,即资源不够用时BestEffort中的容器会被优先终止以腾出资源确保其它两种类型的pod中的容器能正常运行。如果一个节点上只有Guranteed和Burstable类型的容器资源不够用时又会杀优先级较低的Burstable。现在我们可能有很多pod中的容器都属于Burstable那么我们应该先杀哪个呢?因此他需要有一种计算标准去终止。其实在我们对应的k8s代码中有一段设置的是尽可能把他对应的requests资源已经占用量比例较大的给关掉。比如我们现在有两个pod,对第一个pod来讲他的内存的limits是1G,他的requests是512M,而现在已经占了500M了,已经满足基本最低需求了。对第二个pod来讲他需求2G,最低要求1G,现在已经占据512了。第一个pod占了500M,第二个占了512M,第二个基本保证是1G,第一个基本保证是512M,那么谁会被干掉呢?他是这样来评估的,就是谁已分配的量已经接近其需求量那么就认为说他其实没有空间了,马上就会被干掉了,所以第一个会被干掉。即已占用量与需求量的比例越大则优先被干掉,不管用了多少。

    

三、heapster

  1、刚刚有个很尴尬的地方在于,我创建了这样的限制pod以后我想观察Pod到底占了多少资源呢?比如我们分配说limits是512M,requests是256M,我现在想看看到底用了多少要怎么看呢?我们可以用kubectl top,不过此时没法用,因为我们k8s集群top命令应用是依赖于集群上部署一个addons叫heapster,意思是说我们整个k8s需要一个运行在集群级别的各pod甚至是各节点资源用量的采集和存储工具。kubectl top这样的命令是根据采集和存储数据的机制而显示的。如果没有这样的heapster我们系统中的top是没法运行的,包括我们部署的dashboard中的某些资源用量的显示功能也是依赖于heapster的。简单来讲我们可以这样来理解,我们虽然在节点级我们曾经使用过top,stat,vmstat等命令能够统计节点级的进程使用资源。但是当k8s运行在众多节点以后每一个pod运行在哪儿都没法确定,这个pod进程中到底占了多少资源并且要用同一视图查看时是没法的,因为他只是在节点级。所以为了实现这样的功能我们就需要在整个集群上部署一个统一的资源指标收集和存储工具,至少我们要有一个收集功能。当我们需要看的时候他要联系到每一个节点上,通过每一个节点的agent来获取到这个节点之上的每一个进程甚至是节点本身的资源用量,而后用kubectl top命令来显示。但是我们系统默认没有部署此工具,他需要单独额外部署一套这样的工具,传统的k8s集群中默认建议使用的指标数据的采集和存储工具叫HeapSter,但是HeapSter只是一个汇聚指标数据的,因为每个节点上都要采集节点自身的和上面运行的pod的指标数据。这个数据收集完以后也只在节点级别有效。因此我们需要一个集中统一来收集并存储的工具。

  2、那么HeapSter是一套什么样的架构呢?首先在每个节点上我们运行了一个工具叫kubelet,这是作为集群的代理插件的一个非常重要的组件,kubelet其实是能够获取当前系统上由他所负责创建和管理的各种资源对象,尤其是Pod的相关信息数据,但是真正完整去采集节点级和pod的数据是kubelet中的有一个子组件也是kubelet的一个插件叫cAdvisor,早些时候cAdvisor是一个单独的组件,较新的版本中其已经是kubelet内建的一个功能了,即装上Kubelet就有了。他是专门负责收集当前节点上各pod上的各容器以及当前节点在节点级的各种系统级资源指标的占用量,比如节点级和pod级的cpu,内存和存储用量等信息。他可以去收集这些信息,收集完了后你可以在单节点查看,早些时候的版本是支持的,现在已经不支持了。因此kubelet中没有打开这个功能,默认情况下我们把kubelet的cAdvisor这个功能打开以后他会监听在默认叫做4194端口上。现在我们为了安全起见已经自动把这个功能给关掉了。他不需要用户主动连接他去采集数据,而是主动报告给 。我们的HeapSter是专门为我们的k8s集群上去收集由cAdvisor在每一个节点上采集的数据为统一的存储位的这么一个工具。所以我们在集群上托管运行一个pod,这个pod就叫HeapSter,而后各cAdvisor就能够向这个pod去主动发送报告其所采集到的数据。所以其是一个统一的各种指标数据的收集工具。收集完成后他可以缓存在内存中,但内存是有限的,所以他只能缓存一段时间的数据用于让我们的kubectl top查看。我想看历史上昨天,前天,以及趋势数据是怎么样的,发报告给HeapSter是没法做这个功能的,能做一段时间历史数据的缓存。因为他使用缓存系统来存储的。如果我们想要长期存储得靠另一个组件叫做influxDB,他是由HeapSter调用的外部的一个持续数据库存储系统。我们HeapSter采集了数据以后可以整合存储到其中。这样就能达到持久存储的目的。这样我们就能查看历史中的资源使用情况,不过我们需要借助于另外一个工具来查看,一个非常漂亮的图形界面的提供者Grafana。他可以将influxDB作为数据源。这样我们就能愉快的展示influxDB中曾经采集到的每一个pod甚至是每一个节点的相关数据的统计结果了。

    

  3、一般而言pod的资源监控的大体行为可分为三类指标。

    a、kubernetes系统指标

    b、容器指标:指容器的cpu,内存,存储等资源利用状况

    c、应用指标:即我们业务指标中的应用程序中的自身的一些指标数据。接收了多少用户请求,内部有多少个子进程大概在处理什么请求。

  4、对k8s来讲,有几个核心的组件。有一个叫kube-dns默认就已经被装上去了。还有就是dashboard,现在就是另一个HeapSter。要想安装部署HeapSter就需要安装上面所述的三个组件:HeapSter,InfluxDB,Grafana。而这三个组件要想能够在系统级和整个集群级收集各名称空间中的pod资源的指标数据在RBAC强制已经启用的条件下他们还要定义RBAC的各种基于角色资源控制所需要依赖的资源授权才行。所以我们还应该使用他的rbac的一些资源特性。

三、部署HeapSter

  1、部署InfluxDB。他是一个持续数据库系统,我们这儿直接部署并使用其功能即可,更何况我们有了k8s编排工具以后他所有的设置做好自动识别以后使用他的配置清单文件应用就可以直接生效了。但是要注意的是HeapSter和InfluxDB是有依赖关系的,InfluxDB将被HeapSter所依赖,因此HeapSter去访问InfluxDB他的访问端点是什么呢?正常情况下我们InfluxDB应该是一个服务器上运行的进程,访问时应该是InfluxDB监听了一个地址的一个端口。因此我们HeapSter应该通过这个地址的这个端口来访问他。现在我们如果把他们都托管到我们k8s集群上我们InfluxDB作为一个pod来讲:第一他的数据需不需要持久存储?如果需要持久存储我们需要给其提供一个有存储能力的存储卷。无论是nfs,glusterfs等等,不过默认情况下这个存储卷功能默认情况下并没有配置。所以你自己在生产环境中使用时并不能拿那个在线清单直接用,而是应该把清单下下来根据自己的存储卷的使用状态给其编辑修改好以后再运用。第二,如果InfluxDB如果自身down了会怎么样?他down了可能会被其控制器重建,重建以后IP肯定就变了,因此HeapSter访问他时不能直接使用其IP进行访问。而应该用service,并且也可以用servicename去访问。因此coredns还是被依赖。

    a、部署链接为https://github.com/kubernetes-retired/heapster/blob/master/deploy/kube-config/influxdb/influxdb.yaml,我们先看看他的内容

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: monitoring-influxdb
  namespace: kube-system
spec:
  replicas: 1
  template:
    metadata:
      labels:
        task: monitoring
        k8s-app: influxdb
    spec:
      containers:
      - name: influxdb
        image: k8s.gcr.io/heapster-influxdb-amd64:v1.5.2
        volumeMounts:
        - mountPath: /data
          name: influxdb-storage
      volumes:
      - name: influxdb-storage
        emptyDir: {} #意味着这个pod删除后就废了,所以建议生产环境中要改为真正意义的具有持久存储功能的存储卷
---
apiVersion: v1
kind: Service
metadata:
  labels:
    task: monitoring
    # For use as a Cluster add-on (https://github.com/kubernetes/kubernetes/tree/master/cluster/addons)
    # If you are NOT using this as an addon, you should comment out this line.
    kubernetes.io/cluster-service: ''true''
    kubernetes.io/name: monitoring-influxdb
  name: monitoring-influxdb
  namespace: kube-system
spec:
  ports:
  - port: 8086
    targetPort: 8086
  selector:
    k8s-app: influxdb

    b、当influxdb定义完以后他的service名称叫monitoring-influxdb,并且他被部署在kube-system名称空间,上面也是。然后监听在8086端口。所以我们前面的HeapSter访问他时应该使用monitoring-influxdb再加上namespace再加上svc.cluster.local这样的后缀来访问,当然也可以使用相对名称。此处我们部署时需要修改我们apiVersion

[root@k8smaster metrics]# cat influxdb.yaml 
apiVersion: apps/v1 
kind: Deployment
metadata:
  name: monitoring-influxdb
  namespace: kube-system
spec:
  replicas: 1
  selector: 
    matchLabels:
      task: monitoring
      k8s-app: influxdb
  template:
    metadata:
      labels:
        task: monitoring
        k8s-app: influxdb
    spec:
      containers:
      - name: influxdb
        image: k8s.gcr.io/heapster-influxdb-amd64:v1.5.2
        volumeMounts:
        - mountPath: /data
          name: influxdb-storage
      volumes:
      - name: influxdb-storage
        emptyDir: {}
---
apiVersion: v1
kind: Service
metadata:
  labels:
    task: monitoring
    # For use as a Cluster add-on (https://github.com/kubernetes/kubernetes/tree/master/cluster/addons)
    # If you are NOT using this as an addon, you should comment out this line.
    kubernetes.io/cluster-service: ''true''
    kubernetes.io/name: monitoring-influxdb
  name: monitoring-influxdb
  namespace: kube-system
spec:
  ports:
  - port: 8086
    targetPort: 8086
  selector:
    k8s-app: influxdb
[root@k8smaster metrics]# kubectl apply -f influxdb.yaml 
deployment.apps/monitoring-influxdb unchanged
service/monitoring-influxdb unchanged

    c、此时我们可以看到相应的svc和pod以及pod的很多初始化信息

[root@k8smaster metrics]# kubectl get svc -n kube-system
NAME                   TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)         AGE
kube-dns               ClusterIP   10.96.0.10       <none>        53/UDP,53/TCP   110d
kubernetes-dashboard   NodePort    10.109.237.216   <none>        443:31450/TCP   19d
monitoring-influxdb    ClusterIP   10.102.40.103    <none>        8086/TCP        1d
[root@k8smaster metrics]# kubectl get pods -n kube-system |grep influxdb
monitoring-influxdb-848b9b66f6-tjvz4   1/1       Running   0          1d
[root@k8smaster metrics]# kubectl logs monitoring-influxdb-848b9b66f6-tjvz4 -n kube-system
ts=2019-08-26T02:13:40.400423Z lvl=info msg="InfluxDB starting" log_id=0HUw2ih0000 version=unknown branch=unknown commit=unknown
ts=2019-08-26T02:13:40.400448Z lvl=info msg="Go runtime" log_id=0HUw2ih0000 version=go1.10.3 maxprocs=2
ts=2019-08-26T02:13:40.576298Z lvl=info msg="Using data dir" log_id=0HUw2ih0000 service=store path=/data/data
ts=2019-08-26T02:13:40.576409Z lvl=info msg="Open store (start)" log_id=0HUw2ih0000 service=store trace_id=0HUw2jO0000 op_name=tsdb_open op_event=start
ts=2019-08-26T02:13:40.576439Z lvl=info msg="Open store (end)" log_id=0HUw2ih0000 service=store trace_id=0HUw2jO0000 op_name=tsdb_open op_event=end op_elapsed=0.032ms
ts=2019-08-26T02:13:40.576457Z lvl=info msg="Opened service" log_id=0HUw2ih0000 service=subscriber
ts=2019-08-26T02:13:40.576461Z lvl=info msg="Starting monitor service" log_id=0HUw2ih0000 service=monitor
ts=2019-08-26T02:13:40.576464Z lvl=info msg="Registered diagnostics client" log_id=0HUw2ih0000 service=monitor name=build
ts=2019-08-26T02:13:40.576467Z lvl=info msg="Registered diagnostics client" log_id=0HUw2ih0000 service=monitor name=runtime
...

  2、部署HeapSter

    a、部署时需要依赖于rbac的设定。对应yaml连接为https://raw.githubusercontent.com/kubernetes-retired/heapster/master/deploy/kube-config/rbac/heapster-rbac.yaml

[root@k8smaster metrics]# cat heapster-rbac.yaml 
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
  name: heapster
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: system:heapster
subjects:
- kind: ServiceAccount
  name: heapster
  namespace: kube-system
[root@k8smaster metrics]# kubectl apply -f heapster-rbac.yaml 
clusterrolebinding.rbac.authorization.k8s.io/heapster created

     b、部署HeapSter,对应yaml链接为https://raw.githubusercontent.com/kubernetes-retired/heapster/master/deploy/kube-config/influxdb/heapster.yaml。下载后做些许修改即可

 

[root@k8smaster metrics]# cat heapster.yaml 
apiVersion: v1
kind: ServiceAccount
metadata:
  name: heapster
  namespace: kube-system
---
apiVersion: apps/v1 
kind: Deployment
metadata:
  name: heapster
  namespace: kube-system
spec:
  replicas: 1
  selector:
    matchLabels:
      task: monitoring
      k8s-app: heapster
  template:
    metadata:
      labels:
        task: monitoring
        k8s-app: heapster
    spec:
      serviceAccountName: heapster
      containers:
      - name: heapster
        image: k8s.gcr.io/heapster-amd64:v1.5.4
        imagePullPolicy: IfNotPresent
        command:
        - /heapster
        - --source=kubernetes:https://kubernetes.default
        - --sink=influxdb:http://monitoring-influxdb.kube-system.svc:8086
---
apiVersion: v1
kind: Service
metadata:
  labels:
    task: monitoring
    # For use as a Cluster add-on (https://github.com/kubernetes/kubernetes/tree/master/cluster/addons)
    # If you are NOT using this as an addon, you should comment out this line.
    kubernetes.io/cluster-service: ''true''
    kubernetes.io/name: Heapster
  name: heapster
  namespace: kube-system
spec:
  ports:
  - port: 80
    targetPort: 8082
  type: NodePort
  selector:
    k8s-app: heapster
[root@k8smaster metrics]# kubectl apply -f heapster.yaml 
serviceaccount/heapster created
deployment.apps/heapster created
service/heapster created
[root@k8smaster metrics]# kubectl get svc
NAME         TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
kubernetes   ClusterIP   10.96.0.1    <none>        443/TCP   110d
[root@k8smaster metrics]# kubectl get svc -n kube-system
NAME                   TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)         AGE
heapster               NodePort    10.100.27.100    <none>        80:30629/TCP    20s
kube-dns               ClusterIP   10.96.0.10       <none>        53/UDP,53/TCP   110d
kubernetes-dashboard   NodePort    10.109.237.216   <none>        443:31450/TCP   19d
monitoring-influxdb    ClusterIP   10.102.40.103    <none>        8086/TCP        1d
[root@k8smaster metrics]# kubectl get pod -n kube-system|grep heap
heapster-84c9bc48c4-pfgn7              1/1       Running   0          49s

  3、接下来我们部署grafana

    a、对应的链接为https://raw.githubusercontent.com/kubernetes-retired/heapster/master/deploy/kube-config/influxdb/grafana.yaml下载后修改部分内容即可使用

[root@k8smaster metrics]# cat grafana.yaml 
apiVersion: apps/v1 
kind: Deployment
metadata:
  name: monitoring-grafana
  namespace: kube-system
spec:
  replicas: 1
  selector: 
    matchLabels:
      task: monitoring
      k8s-app: grafana
  template:
    metadata:
      labels:
        task: monitoring
        k8s-app: grafana
    spec:
      containers:
      - name: grafana
        image: k8s.gcr.io/heapster-grafana-amd64:v5.0.4
        ports:
        - containerPort: 3000
          protocol: TCP
        volumeMounts:
        - mountPath: /etc/ssl/certs
          name: ca-certificates
          readOnly: true
        - mountPath: /var
          name: grafana-storage
        env:
        - name: INFLUXDB_HOST
          value: monitoring-influxdb
        - name: GF_SERVER_HTTP_PORT
          value: "3000"
          # The following env variables are required to make Grafana accessible via
          # the kubernetes api-server proxy. On production clusters, we recommend
          # removing these env variables, setup auth for grafana, and expose the grafana
          # service using a LoadBalancer or a public IP.
        - name: GF_AUTH_BASIC_ENABLED
          value: "false"
        - name: GF_AUTH_ANONYMOUS_ENABLED
          value: "true"
        - name: GF_AUTH_ANONYMOUS_ORG_ROLE
          value: Admin
        - name: GF_SERVER_ROOT_URL
          # If you''re only using the API Server proxy, set this value instead:
          # value: /api/v1/namespaces/kube-system/services/monitoring-grafana/proxy
          value: /
      volumes:
      - name: ca-certificates
        hostPath:
          path: /etc/ssl/certs
      - name: grafana-storage
        emptyDir: {}
---
apiVersion: v1
kind: Service
metadata:
  labels:
    # For use as a Cluster add-on (https://github.com/kubernetes/kubernetes/tree/master/cluster/addons)
    # If you are NOT using this as an addon, you should comment out this line.
    kubernetes.io/cluster-service: ''true''
    kubernetes.io/name: monitoring-grafana
  name: monitoring-grafana
  namespace: kube-system
spec:
  # In a production setup, we recommend accessing Grafana through an external Loadbalancer
  # or through a public IP.
  # type: LoadBalancer
  # You could also use NodePort to expose the service at a randomly-generated port
  # type: NodePort
  ports:
  - port: 80
    targetPort: 3000
  selector:
    k8s-app: grafana
  type: NodePort
[root@k8smaster metrics]# kubectl apply -f grafana.yaml 
deployment.apps/monitoring-grafana created
service/monitoring-grafana created
[root@k8smaster metrics]# kubectl get svc -n kube-system
NAME                   TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)         AGE
heapster               NodePort    10.100.27.100    <none>        80:30629/TCP    12m
kube-dns               ClusterIP   10.96.0.10       <none>        53/UDP,53/TCP   110d
kubernetes-dashboard   NodePort    10.109.237.216   <none>        443:31450/TCP   19d
monitoring-grafana     NodePort    10.98.72.62      <none>        80:30070/TCP    20s
monitoring-influxdb    ClusterIP   10.102.40.103    <none>        8086/TCP        1d
[root@k8smaster metrics]# kubectl get pods -n kube-system |grep gran
[root@k8smaster metrics]# kubectl get pods -n kube-system |grep gra
monitoring-grafana-555545f477-g7hdd    1/1       Running   0          2m

    b、访问192.168.10.10:30070即可

  4、接下来运行我们的kubectl top命令,此时我们可以发现还是不能使用这个命令,显示获取不到各节点的10255下的容器信息

[root@k8smaster metrics]# kubectl logs heapster-84c9bc48c4-pfgn7 -n kube-system
I0827 06:11:30.880667       1 heapster.go:78] /heapster --source=kubernetes:https://kubernetes.default --sink=influxdb:http://monitoring-influxdb.kube-system.svc:8086
I0827 06:11:30.880741       1 heapster.go:79] Heapster version v1.5.4
I0827 06:11:30.881054       1 configs.go:61] Using Kubernetes client with master "https://kubernetes.default" and version v1
I0827 06:11:30.881092       1 configs.go:62] Using kubelet port 10255
I0827 06:11:31.003814       1 influxdb.go:312] created influxdb sink with options: host:monitoring-influxdb.kube-system.svc:8086 user:root db:k8s
I0827 06:11:31.003943       1 heapster.go:202] Starting with InfluxDB Sink
I0827 06:11:31.003952       1 heapster.go:202] Starting with Metric Sink
I0827 06:11:31.036488       1 heapster.go:112] Starting heapster on port 8082
E0827 06:12:05.007881       1 manager.go:101] Error in scraping containers from kubelet:192.168.10.12:10255: failed to get all container stats from Kubelet URL "http://192.168.10.12:10255/s
tats/container/": Post http://192.168.10.12:10255/stats/container/: dial tcp 192.168.10.12:10255: getsockopt: connection refusedE0827 06:12:05.011822       1 manager.go:101] Error in scraping containers from kubelet:192.168.10.11:10255: failed to get all container stats from Kubelet URL "http://192.168.10.11:10255/s
tats/container/": Post http://192.168.10.11:10255/stats/container/: dial tcp 192.168.10.11:10255: getsockopt: connection refusedE0827 06:12:05.012647       1 manager.go:101] Error in scraping containers from kubelet:192.168.10.10:10255: failed to get all container stats from Kubelet URL "http://192.168.10.10:10255/s
tats/container/": Post http://192.168.10.10:10255/stats/container/: dial tcp 192.168.10.10:10255: getsockopt: connection refused

  5、kubernetes的HeapSter从1.11 版本开始就已经开始废弃了,从1.12版本就直接彻底废除了。妈的,部署半天就这样翻车了。

 

 

    

 

我们今天的关于利用kubernetes资源锁完成自己的HA应用kubernetes 资源限制的分享已经告一段落,感谢您的关注,如果您想了解更多关于(八)Kubernetes Ingress资源、Kubernetes as Database: 使用kubesql查询kubernetes资源、Kubernetes 使用 Kubeadm 推出自己的托管服务、Kubernetes 学习22 kubernetes容器资源需求资源限制及HeapSter(翻车章节)的相关信息,请在本站查询。

本文标签: