K8S实战

核心概念

架构

Kubernetes把集群里的计算资源定义为节点(Node),其中又划分成控制面和数据面两类。

  • 控制面是Master节点,负责管理集群和运维监控应用,里面的核心组件是apiserver、etcd、scheduler、controller-manager。
  • 数据面是Worker节点,受Master节点的管控,里面的核心组件是kubelet、kube-proxy、container-runtime。在这里插入图片描述

在这里插入图片描述
busy-pod.yml 文件定义

apiVersion: v1
kind: Pod
metadata:
  name: busy-pod
  labels:
    owner: chrono
    env: demo
    region: north
    tier: back
spec:
  containers:
  - image: busybox:latest
    name: busy
    imagePullPolicy: IfNotPresent
    env:
      - name: os
        value: "ubuntu"
      - name: debug
        value: "on"
    command:
      - /bin/echo
    args:
      - "$(os), $(debug)"
  • ports:列出容器对外暴露的端口,和Docker的 -p 参数有点像。
  • imagePullPolicy:指定镜像的拉取策略,可以是Always/Never/IfNotPresent,一般默认是IfNotPresent,也就是说只有本地不存在才会远程拉取镜像,可以减少网络消耗。
  • env:定义Pod的环境变量,和Dockerfile里的 ENV 指令有点类似,但它是运行时指定的,更加灵活可配置。
  • command:定义容器启动时要执行的命令,相当于Dockerfile里的 ENTRYPOINT 指令。
  • args:它是command运行时的参数,相当于Dockerfile里的 CMD 指令,这两个命令和Docker的含义不同,要特别注意。

用yml操作pod

//创建
kubectl apply -f busy-pod.yml
//删除
kubectl delete -f busy-pod.yml
kubectl delete pod busy-pod
//查看pod日志
kubectl logs busy-pod
//查看Pod列表和运行状态
kubectl get pod
//检查pod的详细状态
kubectl describe pod busy-pod
//拷贝本地文件到pod(拷贝进Pod的“/tmp”目录里)
echo 'aaa' > a.txt
kubectl cp a.txt ngx-pod:/tmp
//进入pod内部
kubectl exec -it ngx-pod -- sh

ConfigMap/Secret

一类是明文配置,也就是不保密,可以任意查询修改,比如服务端口、运行参数、文件路径等等。
另一类则是机密配置,由于涉及敏感信息需要保密,不能随便查看,比如密码、密钥、证书等等。

ConfigMap

用命令 kubectl create 来创建一个ConfigMap的YAML样板

export out="--dry-run=client -o yaml"        # 定义Shell变量
kubectl create cm info $out

生成的样板文件如下

apiVersion: v1
kind: ConfigMap
metadata:
  creationTimestamp: null
  name: info

因为ConfigMap存储的是配置数据,是静态的字符串,并不是容器,所以它们就不需要用“spec”字段来说明运行时的“规格”

生成带有“data”字段的YAML样板
需要在 kubectl create 后面多加一个参数 --from-literal ,表示从字面值生成一些数据,注意,因为在ConfigMap里的数据都是Key-Value结构,所以 --from-literal 参数需要使用 k=v 的形式。

kubectl create cm info --from-literal=k=v $out

生成的样板文件

apiVersion: v1
data:
  k: v
kind: ConfigMap
metadata:
  creationTimestamp: null
  name: info

创建ConfigMap

kubectl apply -f cm.yml

查看ConfigMap状态

kubectl get cm
kubectl describe cm info

Secret

Kubernetes里Secret对象又细分出很多类,比如:

  • 访问私有镜像仓库的认证信息
  • 身份识别的凭证信息
  • HTTPS通信的证书和私钥
  • 一般的机密信息(格式由用户自行解释)

创建一般机密信息模板

kubectl create secret generic user --from-literal=name=root $out

生成的Secret对象模板如下:

apiVersion: v1
data:
  name: cm9vdA==
kind: Secret
metadata:
  creationTimestamp: null
  name: user

“name”值是一串“乱码”,只是做了Base64编码,根本算不上真正的加密
手动用Linux小工具“base64”来对数据编码,然后写入YAML文件,比如:

echo -n "123456" | base64
MTIzNDU2

要注意这条命令里的 echo ,必须要加参数 -n 去掉字符串里隐含的换行符,否则Base64编码出来的字符串就是错误的。
重新编辑Secret的YAML,为它添加两个新的数据,方式可以是参数 --from-literal 自动编码,也可以是自己手动编码:

apiVersion: v1
kind: Secret
metadata:
  name: user

data:
  name: cm9vdA==  # root
  pwd: MTIzNDU2   # 123456
  db: bXlzcWw=    # mysql

创建secret

kubectl apply  -f secret.yml

查看secret

kubectl get secret
kubectl describe secret user

如何在Pod里引用ConfigMap和Secret对象

pod可以使用另一个“valueFrom”字段,从ConfigMap或者Secret对象里获取值,这样就实现了把配置信息以环境变量的形式注入进Pod,也就是配置与应用的解耦。

“valueFrom”字段在YAML里的嵌套层次比较深,初次使用最好看一下 kubectl explain 对它的说明

kubectl explain pod.spec.containers.env.valueFrom

“valueFrom”字段指定了环境变量值的来源,可以是“configMapKeyRef”或者“secretKeyRef”,然后你要再进一步指定应用的ConfigMap/Secret的“name”和它里面的“key”,要当心的是这个**“name”字段是API对象的名字**,而不是Key-Value的名字。

pod引用示范

apiVersion: v1
kind: Pod
metadata:
  name: env-pod

spec:
  containers:
  - env:
      - name: COUNT
        valueFrom:
          configMapKeyRef:
            name: info
            key: count
      - name: GREETING
        valueFrom:
          configMapKeyRef:
            name: info
            key: greeting
      - name: USERNAME
        valueFrom:
          secretKeyRef:
            name: user
            key: name
      - name: PASSWORD
        valueFrom:
          secretKeyRef:
            name: user
            key: pwd

    image: busybox
    name: busy
    imagePullPolicy: IfNotPresent
    command: ["/bin/sleep", "300"]

图解引用关系
在这里插入图片描述
验证是否生效

kubectl apply -f env-pod.yml
kubectl exec -it env-pod -- sh

echo $COUNT
echo $GREETING
echo $USERNAME $PASSWORD

结果证明Pod对象成功组合了ConfigMap和Secret对象

以Volume的方式使用ConfigMap/Secret

把Pod理解成是一个虚拟机,那么Volume就相当于是虚拟机里的磁盘,Pod“挂载(mount)”多个Volume,里面存放供Pod访问的数据

在Pod里挂载Volume很容易,只需要在“spec”里增加一个“volumes”字段,然后再定义卷的名字和引用的ConfigMap/Secret就可以了。要注意的是Volume属于Pod,不属于容器,所以它和字段“containers”是同级的,都属于“spec”。

定义两个Volume,分别引用ConfigMap和Secret,名字是 cm-vol 和 sec-vol:

spec:
  volumes:
  - name: cm-vol
    configMap:
      name: info
  - name: sec-vol
    secret:
      secretName: user

定义volumeMounts,可以把定义好的Volume挂载到容器里的某个路径下,所以需要在里面用“mountPath”“name”明确地指定挂载路径和Volume的名字。

containers:
  - volumeMounts:
    - mountPath: /tmp/cm-items
      name: cm-vol
    - mountPath: /tmp/sec-items
      name: sec-vol

图解定义关系
在这里插入图片描述
pod的完整YAML如下

apiVersion: v1
kind: Pod
metadata:
  name: vol-pod

spec:
  volumes:
  - name: cm-vol
    configMap:
      name: info
  - name: sec-vol
    secret:
      secretName: user

  containers:
  - volumeMounts:
    - mountPath: /tmp/cm-items
      name: cm-vol
    - mountPath: /tmp/sec-items
      name: sec-vol

    image: busybox
    name: busy
    imagePullPolicy: IfNotPresent
    command: ["/bin/sleep", "300"]

创建以及查看

kubectl apply -f vol-pod.yml
kubectl get pod
kubectl exec -it vol-pod -- sh

单点K8s搭建WordPress网站

整体调用链路

在这里插入图片描述

第一步:编排MariaDB对象

定义一个 maria-cm 对象

apiVersion: v1
kind: ConfigMap
metadata:
  name: maria-cm

data:
  DATABASE: 'db'
  USER: 'mch'
  PASSWORD: '0000'
  ROOT_PASSWORD: '0000'

定义一个maria-pod对象

apiVersion: v1
kind: Pod
metadata:
  name: maria-pod
  labels:
    app: wordpress
    role: database

spec:
  containers:
  - image: mariadb:10
    name: maria
    imagePullPolicy: IfNotPresent
    ports:
    - containerPort: 3306

    envFrom:
    - prefix: 'MARIADB_'
      configMapRef:
        name: maria-cm

注意:这里使用了一个新的字段“envFrom”,这是因为ConfigMap里的信息比较多,如果用 env.valueFrom 一个个地写会非常麻烦,容易出错,而 envFrom 可以一次性地把ConfigMap里的字段全导入进Pod,并且能够指定变量名的前缀(即这里的 MARIADB_),非常方便。

创建pod

kubectl apply -f mariadb-pod.yml
kubectl get pod -o wide

第二步:编排WordPress对象

定义wp-cm.yml

apiVersion: v1
kind: ConfigMap
metadata:
  name: wp-cm

data:
  HOST: '172.17.0.2'
  USER: 'mch'
  PASSWORD: '0000'
  NAME: 'db'

注意:“HOST”字段,它必须是MariaDB Pod的IP地址,如果不写正确WordPress会无法正常连接数据库。

定义wp-pod.yml

apiVersion: v1
kind: Pod
metadata:
  name: wp-pod
  labels:
    app: wordpress
    role: website

spec:
  containers:
  - image: wordpress:5
    name: wp-pod
    imagePullPolicy: IfNotPresent
    ports:
    - containerPort: 80

    envFrom:
    - prefix: 'WORDPRESS_DB_'
      configMapRef:
        name: wp-cm

创建pod

kubectl apply -f wp-pod.yml
kubectl get pod -o wide

第三步: 为WordPress Pod映射端口号,让它在集群外可见

kubectl port-forward wp-pod 8080:80 &

注意在命令的末尾使用了一个 & 符号,让端口转发工作在后台进行,这样就不会阻碍我们后续的操作。

如果想关闭端口转发,需要敲命令 fg ,它会把后台的任务带回到前台,然后就可以简单地用“Ctrl + C”来停止转发了。

第四步:创建反向代理的Nginx,让网站对外提供服务

这是因为WordPress网站使用了URL重定向,直接使用“8080”会导致跳转故障,所以为了让网站正常工作,还应该在Kubernetes之外启动Nginx反向代理,保证外界看到的仍然是“80”端口号。(这里的细节和我们的课程关系不大,感兴趣的同学可以留言提问讨论)

Nginx的配置文件目标地址变成了“127.0.0.1:8080”,它就是我们在第三步里用 kubectl port-forward 命令创建的本地地址

server {
  listen 80;
  default_type text/html;

  location / {
      proxy_http_version 1.1;
      proxy_set_header Host $host;
      proxy_pass http://127.0.0.1:8080;
  }
}

运行nginx代理

docker run -d --rm \
    --net=host \
    -v /tmp/proxy.conf:/etc/nginx/conf.d/default.conf \
    nginx:alpine

第五步 查看网站

地址:虚拟机ip(我这里是“http://192.168.10.208”)

用kubeadm搭建K8s集群

引用文章: https://blog.csdn.net/xuezhiwu001/article/details/128444657?spm=1001.2014.3001.5501

Deploymen

创建了一个名字叫 ngx-dep 的对象,使用的镜像是 nginx:alpine:

export out="--dry-run=client -o yaml"
kubectl create deploy ngx-dep --image=nginx:alpine $out

模板样式

apiVersion: apps/v1
kind: Deployment
metadata:
  creationTimestamp: null
  labels:
    app: ngx-dep
  name: ngx-dep
spec:
  replicas: 1
  selector:
    matchLabels:
      app: ngx-dep
  strategy: {}
  template:
    metadata:
      creationTimestamp: null
      labels:
        app: ngx-dep
    spec:
      containers:
      - image: nginx:alpine
        name: nginx
        resources: {}
status: {}

selector,它的作用是“筛选”出要被Deployment管理的Pod对象,下属字段“matchLabels”定义了Pod对象应该携带的label,它必须和“template”里Pod定义的“labels”完全相同,否则Deployment就会找不到要控制的Pod对象,apiserver也会告诉你YAML格式校验错误无法创建

理解Deployment与被它管理的Pod的组合关系
在这里插入图片描述
kubectl操作Deployment

//创建
kubectl apply -f deploy.yml
//查看Deployment的状态
kubectl get deploy
//删除其中一个pod
kubectl delete pod ngx-dep-6796688696-jm6tt
//应用扩容到5个
kubectl scale --replicas=5 deploy ngx-dep

DaemonSet

在线业务API对象,它会在Kubernetes集群的每个节点上都运行一个Pod,就好像是Linux系统里的“守护进程”(Daemon)

  • 网络应用(如kube-proxy),必须每个节点都运行一个Pod,否则节点就无法加入Kubernetes网络。
  • 监控应用(如Prometheus),必须每个节点都有一个Pod用来监控节点的状态,实时上报信息。
  • 日志应用(如Fluentd),必须在每个节点上运行一个Pod,才能够搜集容器运行时产生的日志数据。
  • 安全应用,同样的,每个节点都要有一个Pod来执行安全审计、入侵检查、漏洞扫描等工作。

DaemonSet的目标是在集群的每个节点上运行且仅运行一个Pod,就好像是为节点配上一只“看门狗”,忠实地“守护”着节点

DaemonSet的YAML示例

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: redis-ds
  labels:
    app: redis-ds

spec:
  selector:
    matchLabels:
      name: redis-ds

  template:
    metadata:
      labels:
        name: redis-ds
    spec:
      containers:
      - image: redis:5-alpine
        name: redis
        ports:
        - containerPort: 6379

DaemonSet与Deployment的差异
在这里插入图片描述
kubectl操作DaemonSet

//创建
kubectl apply -f ds.yml
//查看Deployment的状态
kubectl get daemonset
//发现master没有这个pod
kubectl get pod -o wide

污点(taint)和容忍度(toleration)
可以用 kubectl describe node 来查看Master和Worker的状态:

kubectl describe node master

Name:     master
Roles:    control-plane,master
...
Taints:   node-role.kubernetes.io/master:NoSchedule
...

kubectl describe node worker

Name:     worker
Roles:    <none>
...
Taints:   <none>
...

可以看到,Master节点默认有一个 taint,名字是 node-role.kubernetes.io/master,它的效果是 NoSchedule,也就是说这个污点会拒绝Pod调度到本节点上运行,而Worker节点的 taint 字段则是空的。

这正是Master和Worker在Pod调度策略上的区别所在,通常来说Pod都不能容忍任何“污点”,所以加上了 taint 属性的Master节点也就会无缘Pod了。

怎么让DaemonSet在Master节点(或者任意其他节点)上运行了,方法有两种

(一) 是去掉Master节点上的 taint,让Master变得和Worker一样“纯洁无瑕”
去掉Master节点的“NoSchedule”效果,就要用这条命令:

kubectl taint node master node-role.kubernetes.io/master:NoSchedule-

去掉后,用 kubectl get 来查看发现会在Master节点上创建一个“守护”Pod

这种方法修改的是Node的状态,影响面会比较大,可能会导致很多Pod都跑到这个节点上运行,所以我们可以保留Node的“污点”,为需要的Pod添加“容忍度”,只让某些Pod运行在个别节点上,实现“精细化”调度。

用 kubectl taint 命令把Master的“污点”加上:

kubectl taint node master node-role.kubernetes.io/master:NoSchedule

(二) 为Pod添加字段 tolerations,让它能够“容忍”某些“污点”,就可以在任意的节点上运行了

tolerations 是一个数组,里面可以列出多个被“容忍”的“污点”,需要写清楚“污点”的名字、效果。比较特别是要用 operator 字段指定如何匹配“污点”,一般我们都使用 Exists,也就是说存在这个名字和效果的“污点”。

如果我们想让DaemonSet里的Pod能够在Master节点上运行,就要写出这样的一个 tolerations,容忍节点的 node-role.kubernetes.io/master:NoSchedule 这个污点:

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: redis-ds
  labels:
    app: redis-ds

spec:
  selector:
    matchLabels:
      name: redis-ds

  template:
    metadata:
      labels:
        name: redis-ds
    spec:
      containers:
      - image: redis:5-alpine
        name: redis
        ports:
        - containerPort: 6379
      //新增容忍点
      tolerations:
      - key: node-role.kubernetes.io/master
        effect: NoSchedule
        operator: Exists

再重新部署加上了“容忍度”的DaemonSet

kubectl apply -f ds.yml

就会看到DaemonSet仍然有两个Pod,分别运行在Master和Worker节点上

特别说明一下,“容忍度”并不是DaemonSet独有的概念,而是从属于Pod

什么是静态Pod

Kubernetes还支持另外一种叫“静态Pod”的应用部署手段。

“静态Pod”非常特殊,它不受Kubernetes系统的管控,不与apiserver、scheduler发生关系,所以是“静态”的。

但既然它是Pod,也必然会“跑”在容器运行时上,也会有YAML文件来描述它,而唯一能够管理它的Kubernetes组件也就只有在每个节点上运行的kubelet了。

“静态Pod”的YAML文件默认都存放在节点的 /etc/kubernetes/manifests 目录下,它是Kubernetes的专用目录。

下面的这张截图就是Master节点里目录的情况:
在这里插入图片描述
可以看到,Kubernetes的4个核心组件apiserver、etcd、scheduler、controller-manager原来都以静态Pod的形式存在的,这也是为什么它们能够先于Kubernetes集群启动的原因

Service

本质上就是一个由kube-proxy控制的四层负载均衡,在TCP/IP协议栈上转发流量
在这里插入图片描述

生成Service模板命令

export out="--dry-run=client -o yaml"
kubectl expose deploy ngx-dep --port=80 --target-port=80 $out

模板格式:

apiVersion: v1
kind: Service
metadata:
  creationTimestamp: null
  labels:
    app: ngx-dep
  name: ngx-dep
spec:
  ports:
  - port: 80
    protocol: TCP
    targetPort: 80
  selector:
    app: ngx-dep
status:
  loadBalancer: {}

service与引用pod的关系
在这里插入图片描述
使用Service实践
(1) 创建一个ConfigMap,定义一个Nginx的配置片段,它会输出服务器的地址、主机名、请求的URI等基本信息

apiVersion: v1
kind: ConfigMap
metadata:
  name: ngx-conf

data:
  default.conf: |
    server {
      listen 80;
      location / {
        default_type text/plain;
        return 200
          'srv : $server_addr:$server_port\nhost: $hostname\nuri : $request_method $host $request_uri\ndate: $time_iso8601\n';
      }
    }

(2) 在Deployment的“template.volumes”里定义存储卷,再用“volumeMounts”把配置文件加载进Nginx容器里

apiVersion: apps/v1
kind: Deployment
metadata:
  name: ngx-dep

spec:
  replicas: 2
  selector:
    matchLabels:
      app: ngx-dep

  template:
    metadata:
      labels:
        app: ngx-dep
    spec:
      volumes:
      - name: ngx-conf-vol
        configMap:
          name: ngx-conf

      containers:
      - image: nginx:alpine
        name: nginx
        ports:
        - containerPort: 80

        volumeMounts:
        - mountPath: /etc/nginx/conf.d
          name: ngx-conf-vol

部署这个Deployment

(3) Service定义

apiVersion: v1
kind: Service
metadata:
  name: ngx-svc
  
spec:
  selector:
    app: ngx-dep
    
  ports:
  - port: 80
    targetPort: 80
    protocol: TCP

部署Service
用 kubectl describe describe svc ngx-svc命令查看
在这里插入图片描述
查看容器 kubectl get pod -o wide
在这里插入图片描述
测试Service的负载均衡
进入其中一个pod ,kubectl exec -it ngx-dep-6796688696-rkvfc – sh
用curl访问Service的IP地址,就会看到它把数据转发给后端的Pod,输出信息会显示具体是哪个Pod响应了请求,就表明Service确实完成了对Pod的负载均衡任务
在这里插入图片描述

以域名的方式使用Service
Service对象的域名完全形式是“对象.名字空间.svc.cluster.local”,但很多时候也可以省略后面的部分,直接写“对象.名字空间”,甚至“对象名”就足够了,默认会使用对象所在的名字空间(比如这里就是default)

测试 :进入其中一个pod ,kubectl exec -it ngx-dep-6796688696-rkvfc – sh
在这里插入图片描述
(顺便说一下,Kubernetes也为每个Pod分配了域名,形式是“IP地址.名字空间.pod.cluster.local”,但需要把IP地址里的 . 改成 - 。比如地址 10.10.1.87,它对应的域名就是 10-10-1-87.default.pod。)

Service对外暴露服务
Service对象有一个关键字段“type”,分别是“ExternalName”、“LoadBalancer”、“NodePort”、“ClusterIP”,表示Service是哪种类型的负载均衡。

默认的是“ClusterIP”,表示Service的静态IP地址只能在集群内访问,前两种类型一般由云服务商提供,我们的实验环境用不到,所以接下来就重点看“NodePort”这个类型

使用命令 kubectl expose 的时候加上参数 --type=NodePort,或者在YAML里添加字段 type:NodePort,那么Service除了会对后端的Pod做负载均衡之外,还会在集群里的每个节点上创建一个独立的端口,用这个端口对外提供服务,这也正是“NodePort”这个名字的由来。

修改一下Service的YAML文件,加上字段“type”:

apiVersion: v1
...
spec:
  ...
  type: NodePort

然后创建对象,再查看它的状态:
在这里插入图片描述
看到“TYPE”变成了“NodePort”
而在“PORT”列里的端口信息也不一样,除了集群内部使用的“80”端口,还多出了一个“30651”端口,这就是Kubernetes在节点上为Service创建的专用映射端口。

因为这个端口号属于节点,外部能够直接访问,所以现在我们就可以不用登录集群节点或者进入Pod内部,直接在集群外使用任意一个节点的IP地址,就能够访问Service和它代理的后端服务了。

比如我现在所在的服务器是“192.168.228.130”,在这台主机上用curl访问Kubernetes集群的两个节点“192.168.228.132”“192.168.228.133”,就可以得到Nginx Pod的响应数据:
在这里插入图片描述

NodePort与Service、Deployment的对应关系如图

在这里插入图片描述

缺点

  • 第一个缺点是它的端口数量很有限。Kubernetes为了避免端口冲突,默认只在“30000~32767”这个范围内随机分配,只有2000多个,而且都不是标准端口号,这对于具有大量业务应用的系统来说根本不够用。

  • 第二个缺点是它会在每个节点上都开端口,然后使用kube-proxy路由到真正的后端Service,这对于有很多计算节点的大集群来说就带来了一些网络通信成本,不是特别经济。

  • 第三个缺点,它要求向外界暴露节点的IP地址,这在很多时候是不可行的,为了安全还需要在集群外再搭一个反向代理,增加了方案的复杂度。

Ingress

流量的总入口,统管集群的进出口数据在这里插入图片描述
Ingress由Ingress规则、IngressController、IngressClass这3部分组成

  • Ingress只是规则的集合,自身不具备流量管理能力,需要Ingress Controller应用Ingress规则才能真正发挥作用

  • Ingress Class解耦了Ingress和Ingress Controller,用来关联Ingress和Ingress Controller。

  • ingress Controller通常是一组拥有7层代理能力服务的Pod,最流行的Ingress Controller是Nginx Ingress Controller,它基于经典反向代理软件Nginx

Ingress Controller,会指定要关联的ingress class,和在k8s中注册自己controller的信息;
Ingress Class,会指定要关联的controller的信息;
Ingress,会指定要关联的Ingress Class的信息;
最终,Ingress Class作为“桥梁”,将Ingress和Ingress Controller关联起来。

查看Ingress模板

export out="--dry-run=client -o yaml"
kubectl create ing ngx-ing --rule="ngx.test/=ngx-svc:80" --class=ngx-ink $out
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ngx-ing
  
spec:

  ingressClassName: ngx-ink
  
  rules:
  - host: ngx.test
    http:
      paths:
      - path: /
        pathType: Exact
        backend:
          service:
            name: ngx-svc
            port:
              number: 80

Ingress Class模板
Ingress Class本身并没有什么实际的功能,只是联系Ingress和Ingress Controller的作用,定义非常简单,在“spec”里只有一个必需的字段“controller”,表示要使用哪个Ingress Controller,具体的名字就要看实现文档了。

比如,如果要用Nginx开发的Ingress Controller,那么就要用名字“nginx.org/ingress-controller”:

apiVersion: networking.k8s.io/v1
kind: IngressClass
metadata:
  name: ngx-ink

spec:
  controller: nginx.org/ingress-controller

创建这两个对象

kubectl apply -f ngxIngress.yml
kubectl apply -f ngxIngressClass.yml

查看创建结果
在这里插入图片描述
kubectl describe ing ngx-ing 查看更详细的信息
在这里插入图片描述

安装Ingress Controller
官方文档: https://docs.nginx.com/nginx-ingress-controller/installation/installing-nic/installation-with-manifests/#clone-the-repository

安装步骤:

//先克隆文件
git clone https://github.com/nginxinc/kubernetes-ingress.git --branch <version_number>
cd kubernetes-ingress/deployments
//创建命名空间和服务帐户:
kubectl apply -f common/ns-and-sa.yaml
//为服务帐户创建集群角色和绑定:
kubectl apply -f rbac/rbac.yaml
//创建一个 ConfigMap 来自定义您的 NGINX 设置
kubectl apply -f common/nginx-config.yaml
//创建IngressClass资源。如果没有资源,NGINX Ingress Controller 将无法启动IngressClass。
kubectl apply -f common/ingress-class.yaml (由于我们之前已经创建过了,所以这一步不需要)
//核心自定义资源定义
kubectl apply -f common/crds/k8s.nginx.org_virtualservers.yaml
kubectl apply -f common/crds/k8s.nginx.org_virtualserverroutes.yaml
kubectl apply -f common/crds/k8s.nginx.org_transportservers.yaml
kubectl apply -f common/crds/k8s.nginx.org_policies.yaml

修改deployment/nginx-ingress.yaml文件
修改后,Ingress Controller的YAML大概是这个样子:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: ngx-kic-dep
  namespace: nginx-ingress

spec:
  replicas: 1
  selector:
    matchLabels:
      app: ngx-kic-dep

  template:
    metadata:
      labels:
        app: ngx-kic-dep
    ...
    spec:
      containers:
      - image: nginx/nginx-ingress:2.2-alpine
        ...
        args:
          - -ingress-class=ngx-ink

部署

kubectl apply -f deployment/nginx-ingress.yaml

查看部署结果

kubectl get deploy -n nginx-ingress
kubectl get pod -n nginx-ingress

在这里插入图片描述
因为Ingress Controller本身也是一个Pod,想要向外提供服务还是要依赖于Service对象。所以你至少还要再为它定义一个Service,使用NodePort或者LoadBalancer暴露端口,才能真正把集群的内外流量打通。

这里,我就用命令kubectl port-forward,它可以直接把本地的端口映射到Kubernetes集群的某个Pod里,在测试验证的时候非常方便。

下面这条命令就把本地的8080端口映射到了Ingress Controller Pod的80端口:

kubectl port-forward -n nginx-ingress ngx-kic-dep-8859b7b86-cplgp 8080:80 &

在这里插入图片描述

验证
可以修改 /etc/hosts 来手工添加域名解析,也可以使用 --resolve 参数,指定域名的解析规则,比如在这里我就把“ngx.test”强制解析到“127.0.0.1”,也就是被 kubectl port-forward 转发的本地地址:

curl --resolve ngx.test:8080:127.0.0.1 http://ngx.test:8080

在这里插入图片描述

K8s集群搭建WordPress网站

基本架构
在这里插入图片描述
第一步:搭建maria文件

  • 用ConfigMap定义数据库的环境变量,有 DATABASE、USER、PASSWORD、ROOT_PASSWORD:
apiVersion: v1
kind: ConfigMap
metadata:
  name: maria-cm

data:
  DATABASE: 'db'
  USER: 'mch'
  PASSWORD: '0000'
  ROOT_PASSWORD: '0000'
  • 把MariaDB由Pod改成Deployment的方式,replicas设置成1个,用 envFrom把配置信息以环境变量的形式注入Pod
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: maria-dep
  name: maria-dep

spec:
  replicas: 1
  selector:
    matchLabels:
      app: maria-dep

  template:
    metadata:
      labels:
        app: maria-dep
    spec:
      containers:
      - image: mariadb:10
        name: mariadb
        ports:
        - containerPort: 3306

        envFrom:
        - prefix: 'MARIADB_'
          configMapRef:
            name: maria-cm
  • 为MariaDB定义一个Service对象,映射端口3306,让其他应用不再关心IP地址,直接用Service对象的名字来访问数据库服务:
apiVersion: v1
kind: Service
metadata:
  labels:
    app: maria-dep
  name: maria-svc

spec:
  ports:
  - port: 3306 //对其他应用暴露的端口
    protocol: TCP
    targetPort: 3306
  selector:
    app: maria-dep

将三个YAML放在同一个文件里书写,对象之间用 — 分开,这样用 kubectl apply 就可以一次性创建

kubectl apply -f wp-maria.yml

第二步 部署WordPress

  • 因为刚才创建了MariaDB的Service,所以在写ConfigMap配置的时候“HOST”就不应该是IP地址了,而应该是DNS域名,也就是Service的名字maria-svc,这点需要特别注意:
metadata:
  name: wp-cm

data:
  HOST: 'maria-svc'
  USER: 'mch'
  PASSWORD: '0000'
  NAME: 'db'
  • WordPress的Deployment写法和MariaDB也是一样的,replicas设置成2个,用字段“envFrom”配置环境变量:
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: wp-dep
  name: wp-dep

spec:
  replicas: 2
  selector:
    matchLabels:
      app: wp-dep

  template:
    metadata:
      labels:
        app: wp-dep
    spec:
      containers:
      - image: wordpress:5
        name: wordpress
        ports:
        - containerPort: 80

        envFrom:
        - prefix: 'WORDPRESS_DB_'
          configMapRef:
            name: wp-cm
  • 为WordPress创建Service对象,这里我使用了“NodePort”类型,并且手工指定了端口号“30088”(必须在30000~32767之间):
apiVersion: v1
kind: Service
metadata:
  labels:
    app: wp-dep
  name: wp-svc

spec:
  ports:
  - name: http80
    port: 80
    protocol: TCP
    targetPort: 80
    nodePort: 30088

  selector:
    app: wp-dep
  type: NodePort

同样写到一个文件中,kubectl apply -f wp-dep.yml 部署

访问测试
因为WordPress的Service对象是NodePort类型的,我们可以在集群的每个节点上访问WordPress服务。

比如一个节点的IP地址是“192.168.228.133”,在浏览器的地址栏里输入“http://192.168.228.133:30088”,其中的“30088”就是在Service里指定的节点端口号,然后就能够看到WordPress的安装界面了

第三步:搭建Ingress、IngressClass、IngressController

  • 搭建IngressClass、名字就叫“wp-ink”
apiVersion: networking.k8s.io/v1
kind: IngressClass
metadata:
  name: wp-ink

spec:
  controller: nginx.org/ingress-controller
  • 搭建Ingress
//可以先用该命令生成模板
kubectl create ing wp-ing --rule="wp.test/=wp-svc:80" --class=wp-ink $out
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: wp-ing

spec:
  ingressClassName: wp-ink

  rules:
  - host: wp.test
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: wp-svc
            port:
              number: 80
  • 搭建IngressController(从模板中修改)
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: wp-kic-dep
  namespace: nginx-ingress

spec:
  replicas: 1
  selector:
    matchLabels:
      app: wp-kic-dep

  template:
    metadata:
      labels:
        app: wp-kic-dep

    spec:
      serviceAccountName: nginx-ingress


      # use host network
      hostNetwork: true
      dnsPolicy: ClusterFirstWithHostNet #可有可无
      automountServiceAccountToken: true ## 改为true ,不然80端口没有权限

      containers:
      	args:
        	- -ingress-class=wp-ink
      ...
---
## 增加、使用service
apiVersion: v1
kind: Service
metadata:
  name: wp-kic-svc
  namespace: nginx-ingress

spec:
  ports:
  - port: 80
    protocol: TCP
    targetPort: 80
    nodePort: 30080
  selector:
    app: wp-kic-dep
  type: NodePort
---

可以将Ingress、IngressClass 合成一个文件先创建,然后再创建IngressController

访问测试

Ingress使用的是HTTP路由规则,用IP地址访问是无效的,所以在集群外的主机上必须能够识别我们的“wp.test”域名,也就是说要把域名“wp.test”解析到Ingress Controller所在的节点上。

如果你用的是Mac,那就修改 /etc/hosts;如果你用的是Windows,就修改 C:\Windows\System32\Drivers\etc\hosts,添加一条解析规则就行:

cat /etc/hosts
192.168.228.133  wp.test

需要注意的是ip是IngressController 的pod所在节点的ip

有了域名解析,在浏览器里你就不必使用IP地址,直接用域名“wp.test”走Ingress Controller就能访问我们的WordPress网站了

PersistentVolume(PV)

  • PV实际上就是一些存储设备、文件系统,比如Ceph、GlusterFS、NFS,甚至是本地磁盘,管理它们已经超出了Kubernetes的能力范围,所以,一般会由系统管理员单独维护,然后再在Kubernetes里创建对应的PV。要注意的是,PV属于集群的系统资源,是和Node平级的一种对象,Pod对它没有管理权,只有使用权。

  • PersistentVolumeClaim,简称PVC,从名字上看比较好理解,就是用来向Kubernetes申请存储资源的。PVC是给Pod使用的对象,它相当于是Pod的代理,代表Pod向系统申请PV。一旦资源申请成功,Kubernetes就会把PV和PVC关联在一起,这个动作叫做“绑定”(bind)。但是,系统里的存储资源非常多,如果要PVC去直接遍历查找合适的PV也很麻烦,所以就要用到StorageClass。

  • StorageClass的作用有点像IngressClass,它抽象了特定类型的存储系统(比如Ceph、NFS),在PVC和PV之间充当“协调人”的角色,帮助PVC找到合适的PV。也就是说它可以简化Pod挂载“虚拟盘”的过程,让Pod看不到PV的实现细节。

在这里插入图片描述
1、PV的yaml示例:

apiVersion: v1
kind: PersistentVolume
metadata:
  name: host-10m-pv

spec:
  storageClassName: host-test
  accessModes:
  - ReadWriteOnce
  capacity:
    storage: 10Mi
  hostPath:
    path: /tmp/host-10m-pv/

storageClassName:对存储类型的抽象StorageClass。这个PV是我们手动管理的,名字可以任意起
accessModes:定义了存储设备的访问模式,简单来说就是虚拟盘的读写权限,和Linux的文件访问模式差不多,目前Kubernetes里有3种:

  • ReadWriteOnce:存储卷可读可写,但只能被一个节点上的Pod挂载。
  • ReadOnlyMany:存储卷只读不可写,可以被任意节点上的Pod多次挂载。
  • ReadWriteMany:存储卷可读可写,也可以被任意节点上的Pod多次挂载。

显然,本地目录只能是在本机使用,所以这个PV使用了 ReadWriteOnce。

capacity:表示存储设备的容量,这里设置为10MB。
注意,Kubernetes里定义存储容量使用的是国际标准,我们日常习惯使用的KB/MB/GB的基数是1024,要写成Ki/Mi/Gi,一定要小心不要写错了,否则单位不一致实际容量就会对不上

hostPath:指定了存储卷的本地路径,也就是我们在节点上创建的目录。

2、PVC的yaml示例:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: host-5m-pvc

spec:
  storageClassName: host-test
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 5Mi

示例要求使用一个5MB的存储设备,访问模式是 ReadWriteOnce,PVC里的 storageClassName、accessModes 和PV是一样的,但不会有字段 capacity,而是要用 resources.request 表示希望要有多大的容量

3、Kubernetes里使用PersistentVolume
定义好PV和PVC,就可以让Pod实现持久化存储了
首先需要用 kubectl apply 创建PV对象:

kubectl apply -f host-path-pv.yml
kubectl get pv

在这里插入图片描述

接下来创建PVC,申请存储资源:

kubectl apply -f host-path-pvc.yml
kubectl get pvc

在这里插入图片描述

4、如何为Pod挂载PersistentVolume
PV和PVC绑定好了,有了持久化存储,现在我们就可以为Pod挂载存储卷
因为我们用的是PVC,所以要在 volumes 里用字段 persistentVolumeClaim 指定PVC的名字。
Pod的YAML描述文件,把存储卷挂载到了Nginx容器的 /tmp 目录

apiVersion: v1
kind: Pod
metadata:
  name: host-pvc-pod

spec:
  volumes:
  - name: host-pvc-vol
    persistentVolumeClaim:
      claimName: host-5m-pvc

  containers:
    - name: ngx-pvc-pod
      image: nginx:alpine
      ports:
      - containerPort: 80
      volumeMounts:
      - name: host-pvc-vol
        mountPath: /tmp

Pod和PVC/PV的关系图

在这里插入图片描述
创建这个Pod,查看它的状态:

kubectl apply -f host-path-pod.yml
kubectl get pod -o wide

PersistentVolume + NFS:使用网络共享存储

使用HostPath,存储卷只能在本机使用,而Kubernetes里的Pod经常会在集群里“漂移”,所以这种方式不是特别实用

要想让存储卷真正能被Pod任意挂载,不能限定在本地磁盘,而是要改成网络存储,这样Pod无论在哪里运行,只要知道IP地址或者域名,就可以通过网络通信访问存储设备。

网络存储是一个非常热门的应用领域,有很多知名的产品,比如AWS、Azure、Ceph,Kubernetes还专门定义了CSI(Container Storage Interface)规范,不过这些存储类型的安装、使用都比较复杂,在我们的实验环境里部署难度比较高。

我选择了相对来说比较简单的NFS系统(Network File System),以它为例讲解如何在Kubernetes里使用网络存储,以及静态存储卷和动态存储卷的概念。

NFS采用的是Client/Server架构,需要选定一台主机作为Server,安装NFS服务端;其他要使用存储的主机作为Client,安装NFS客户端工具。这里我就复用了Console作为服务器。

新的架构图如下:
在这里插入图片描述

1、安装NFS服务器

Console 安装 NFS服务端

sudo apt -y install nfs-kernel-server

给NFS指定一个存储位置,也就是网络共享目录。这儿创建临时目录 /tmp/nfs:

mkdir -p /tmp/nfs

接着需要配置NFS访问共享目录,修改 /etc/exports,指定目录名、允许访问的网段,还有权限等参数。注意目录名和IP地址要改成和自己的环境一致:(我的服务节点ip是:192.168.228.130)

/tmp/nfs 192.168.228.0/24(rw,sync,no_subtree_check,no_root_squash,insecure)

改好之后,需要用 exportfs -ra 通知NFS,让配置生效,再用 exportfs -v 验证效果:

sudo exportfs -ra
sudo exportfs -v

在这里插入图片描述
现在,可以使用 systemctl 来启动NFS服务器:

sudo systemctl start  nfs-server
sudo systemctl enable nfs-server
sudo systemctl status nfs-server

还可以使用命令 showmount 来检查NFS的网络挂载情况:

showmount -e 127.0.0.1

在这里插入图片描述
2、安装NFS客户端

有了NFS服务器之后,为了让Kubernetes集群能够访问NFS存储服务,还需要在每个节点上都安装NFS客户端。

安装命令

sudo apt -y install nfs-common

同样,在节点上可以用 showmount 检查NFS能否正常挂载,注意IP地址要写成NFS服务器的地址,我在这里就是“192.168.228.130”:

showmount -e 192.168.228.130

现在尝试手动挂载一下NFS网络存储,先创建一个目录 /tmp/test 作为挂载点:

mkdir -p /tmp/test

然后用命令 mount 把NFS服务器的共享目录挂载到刚才创建的本地目录上:

sudo mount -t nfs 192.168.228.130:/tmp/nfs /tmp/test

最后测试一下,我们在 /tmp/test 里随便创建一个文件,比如 x.yml:

touch /tmp/test/x.yml

再回到NFS服务器,检查共享目录 /tmp/nfs,应该会看到也出现了一个同样的文件 x.yml,这就说明NFS安装成功了。之后集群里的任意节点,只要通过NFS客户端,就能把数据写入NFS服务器,实现网络存储。

3、使用NFS存储卷

Kubernetes配置好了NFS存储系统,就可以使用它来创建新的PV存储对象了。

注意,spec.nfs 里的IP地址一定要正确,路径一定要存在(事先创建好),否则Kubernetes按照PV的描述会无法挂载NFS共享目录,PV就会处于“pending”状态无法使用

apiVersion: v1
kind: PersistentVolume
metadata:
  name: nfs-1g-pv

spec:
  storageClassName: nfs
  accessModes:
    - ReadWriteMany # 多个节点同时访问一个共享目录
  capacity:
    storage: 1Gi   #分配1Gi

  nfs:
    path: /tmp/nfs/1g-pv  # NFS服务器的共享目录名
    server: 192.168.228.130  # NFS服务器的IP地址

定义PVC对象
用 resources.request 来表示希望要有多大的容量

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: nfs-static-pvc

spec:
  storageClassName: nfs
  accessModes:
    - ReadWriteMany

  resources:
    requests:
      storage: 1Gi

定义一个Pod
把PVC挂载成它的一个volume,用 persistentVolumeClaim 指定PVC的名字就可以了

apiVersion: v1
kind: Pod
metadata:
  name: nfs-static-pod

spec:
  volumes:
  - name: nfs-pvc-vol
    persistentVolumeClaim:
      claimName: nfs-static-pvc

  containers:
    - name: nfs-pvc-test
      image: nginx:alpine
      ports:
      - containerPort: 80

      volumeMounts:
        - name: nfs-pvc-vol
          mountPath: /tmp

验证
操作NFS共享目录,再看一下NFS服务器的 /tmp/nfs/1g-pv 目录,你就会发现Pod里创建的文件确实写入了共享目录
在这里插入图片描述

Pod、PVC、PV和NFS存储的关系可以用下图来形象地表示

在这里插入图片描述
4、部署NFS Provisoner

有了NFS这样的网络存储系统,Kubernetes里的数据持久化问题并没有完全解决

没有完全解决:因为PV还是需要人工管理,必须要由系统管理员手动维护各种存储设备,再根据开发需求逐个创建PV,而且PV的大小也很难精确控制,容易出现空间不足或者空间浪费的情况。如果是在一个大集群里,每天可能会有几百几千个应用需要PV存储,如果仍然用人力来管理分配存储,管理员很可能会忙得焦头烂额,导致分配存储的工作大量积压。

如何自动化创建PV?

这个在Kubernetes里就是“动态存储卷”的概念,它可以用StorageClass绑定一个Provisioner对象,而这个Provisioner就是一个能够自动管理存储、创建PV的应用,代替了原来系统管理员的手工劳动。

有了“动态存储卷”的概念,前面的手工创建的PV就可以称为“静态存储卷”。

目前,Kubernetes里每类存储设备都有相应的Provisioner对象,对于NFS来说,它的Provisioner就是“NFS subdir external provisioner”,你可以在GitHub上找到这个项目(https://github.com/kubernetes-sigs/nfs-subdir-external-provisioner)。

NFS Provisioner也是以Pod的形式运行在Kubernetes里的,在GitHub的 deploy 目录里是部署它所需的YAML文件,一共有三个,分别是rbac.yaml、class.yaml和deployment.yaml。

需要修改两个文件:

第一个是rbac.yaml,它使用的是默认的 default 名字空间,应该把它改成其他的名字空间,避免与普通应用混在一起,你可以用“查找替换”的方式把它统一改成 kube-system。

第二个是deployment.yaml,它要修改的地方比较多。首先要把名字空间改成和rbac.yaml一样,比如是 kube-system,然后重点要修改 volumes 和 env 里的IP地址和共享目录名,必须和集群里的NFS服务器配置一样。

deployment.yaml的镜像仓库用的是gcr.io,把镜像的名字由原来的“k8s.gcr.io/sig-storage/nfs-subdir-external-provisioner:v4.0.2”改成“chronolaw/nfs-subdir-external-provisioner:v4.0.2”

spec:
  template:
    spec:
      serviceAccountName: nfs-client-provisioner
      containers:
		  ...
          env:
            - name: PROVISIONER_NAME
              value: k8s-sigs.io/nfs-subdir-external-provisioner
            - name: NFS_SERVER
              value: 192.168.228.130        #改IP地址
            - name: NFS_PATH
              value: /tmp/nfs              #改共享目录名
      volumes:
        - name: nfs-client-root
          nfs:
            server: 192.168.228.130         #改IP地址
            Path: /tmp/nfs                 #改共享目录名

把这两个YAML修改好之后,在Kubernetes里创建NFS Provisioner

kubectl apply -f rbac.yaml
kubectl apply -f class.yaml
kubectl apply -f deployment.yaml

在这里插入图片描述
在这里插入图片描述

5、使用NFS动态存储卷
比起静态存储卷,动态存储卷的用法简单很多。因为有了Provisioner,我们就不再需要手工定义PV对象了,只需要在PVC里指定StorageClass对象,它再关联到Provisioner。

NFS默认的StorageClass定义:

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: nfs-client

provisioner: k8s-sigs.io/nfs-subdir-external-provisioner 
parameters:
  archiveOnDelete: "false"

provisioner它指定了应该使用哪个Provisioner。
parameters 是调节Provisioner运行的参数,这里的 archiveOnDelete: “false” 就是自动回收存储空间。

定义一个PVC
向系统申请10MB的存储空间,使用的StorageClass是默认的 nfs-client

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: nfs-dyn-10m-pvc

spec:
  storageClassName: nfs-client
  accessModes:
    - ReadWriteMany

  resources:
    requests:
      storage: 10Mi

在Pod里用 volumes 和 volumeMounts 挂载
挂载完成后,Kubernetes就会自动找到NFS Provisioner,在NFS的共享目录上创建出合适的PV对象

apiVersion: v1
kind: Pod
metadata:
  name: nfs-dyn-pod

spec:
  volumes:
  - name: nfs-dyn-10m-vol
    persistentVolumeClaim:
      claimName: nfs-dyn-10m-pvc

  containers:
    - name: nfs-dyn-test
      image: nginx:alpine
      ports:
      - containerPort: 80

      volumeMounts:
        - name: nfs-dyn-10m-vol
          mountPath: /tmp

使用 kubectl apply 创建好PVC和Pod,查看一下集群里的PV状态:
在这里插入图片描述
从截图可以看到,虽然我们没有直接定义PV对象,但由于有NFS Provisioner,它就自动创建一个PV,大小刚好是在PVC里申请的10MB。

如果你这个时候再去NFS服务器上查看共享目录,也会发现多出了一个目录,名字与这个自动创建的PV一样,但加上了名字空间和PVC的前缀:
在这里插入图片描述
Pod、PVC、StorageClass和Provisioner的关系图
在这里插入图片描述

StatefulSet:管理有状态的应用

无状态应用: 只是有的应用的状态信息不是很重要,即使不恢复状态也能够正常运行
有状态应用: 一些应用,运行状态信息就很重要了,如果因为重启而丢失了状态是绝对无法接受的

1、一个使用Redis的StatefulSet的对象

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: redis-sts

spec:
  serviceName: redis-svc
  replicas: 2
  selector:
    matchLabels:
      app: redis-sts

  template:
    metadata:
      labels:
        app: redis-sts
    spec:
      containers:
      - image: redis:5-alpine
        name: redis
        ports:
        - containerPort: 6379

发现: 相比Deployment,文件里除了 kind 必须是“StatefulSet”,在 spec 里还多出了一个“serviceName”字段

2、在Kubernetes里使用StatefulSet
用 kubectl apply 创建StatefulSet对象

kubectl apply -f redis-sts.yml
kubectl get sts
kubectl get pod

在这里插入图片描述
在这里插入图片描述
(1)解决了“有状态应用”的第一个问题:启动顺序
StatefulSet所管理的Pod不再是随机的名字,而是有了顺序编号,从0开始分别被命名为 redis-sts-0、redis-sts-1
Kubernetes也会按照这个顺序依次创建(0号比1号的AGE要长一点)

(2)解决了“有状态应用”的第二个问题:依赖关系
有了启动的先后顺序,应用通过hostname,进而确定互相之间的依赖关系呢
hostname就是每个Pod里的主机名,让我们再用 kubectl exec 登录Pod内部看看:

kubectl exec -it redis-sts-0 -- sh

在这里插入图片描述
(3)解决了“有状态应用”的第三个问题:网络标识
解决网络标识,需要用到Service对象
定义Service对象

apiVersion: v1
kind: Service
metadata:
  name: redis-svc

spec:
  selector:
    app: redis-sts

  ports:
  - port: 6379
    protocol: TCP
    targetPort: 6379

kubectl apply 创建这个对象,查看如下
在这里插入图片描述

  • Service自己会有一个域名,格式是“对象名.名字空间”
  • 每个Pod也会有一个域名,格式是“IP地址.名字空间”。但因为IP地址不稳定,所以Pod的域名并不实用,一般我们会使用稳定的Service域名。

StatefulSet的奥秘就在它的域名上,当我们把Service对象应用于StatefulSet的时候,情况就不一样了
Service发现这些Pod不是一般的应用,而是有状态应用,需要有稳定的网络标识,所以就会为Pod再多创建出一个新的域名,格式是“Pod名.服务名.名字空间.svc.cluster.local”。当然,这个域名也可以简写成“Pod名.服务名”。

用 kubectl exec 进入Pod内部,用ping命令来验证一下:

kubectl exec -it redis-sts-0 -- sh

在这里插入图片描述
虽然Pod的IP地址可能会变,但这个有编号的域名由Service对象维护,是稳定不变的

关于Service,有一点值得再多提一下。

Service原本的目的是负载均衡,应该由它在Pod前面来转发流量,但是对StatefulSet来说,这项功能反而是不必要的,因为Pod已经有了稳定的域名,外界访问服务就不应该再通过Service这一层了。所以,从安全和节约系统资源的角度考虑,我们可以在Service里添加一个字段 clusterIP: None ,告诉Kubernetes不必再为这个对象分配IP地址。

StatefulSet与Service对象关系图
在这里插入图片描述

3、如何实现StatefulSet的数据持久化

Kubernetes为StatefulSet专门定义了一个字段“volumeClaimTemplates”,直接把PVC定义嵌入StatefulSet的YAML文件里。这样能保证创建StatefulSet的同时,就会为每个Pod自动创建PVC,让StatefulSet的可用性更高

上面的Redis StatefulSet对象稍微改造一下,加上持久化存储功能:

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: redis-pv-sts

spec:
  serviceName: redis-pv-svc

  volumeClaimTemplates:
  - metadata:
      name: redis-100m-pvc
    spec:
      storageClassName: nfs-client
      accessModes:
        - ReadWriteMany
      resources:
        requests:
          storage: 100Mi

  replicas: 2
  selector:
    matchLabels:
      app: redis-pv-sts

  template:
    metadata:
      labels:
        app: redis-pv-sts
    spec:
      containers:
      - image: redis:5-alpine
        name: redis
        ports:
        - containerPort: 6379

        volumeMounts:
        - name: redis-100m-pvc
          mountPath: /data

这个StatefulSet对象完整的关系图

在这里插入图片描述

滚动更新:应用平滑的升级降级

1、滚动更新
由Deployment控制的两个同步进行的“应用伸缩”操作,老版本缩容到0,同时新版本扩容到指定值,是一个“此消彼长”的过程
在这里插入图片描述
2、管理应用更新

(1)查看更新历史使用的命令是 kubectl rollout history:

kubectl rollout history deploy ngx-dep

在这里插入图片描述
在命令后加上参数 --revision 来查看每个版本的详细信息,包括标签、镜像名、环境变量、存储卷等等

kubectl rollout history deploy --revision=2

在这里插入图片描述

(2) 回退到上一个版本

可以使用命令 kubectl rollout undo,也可以加上参数 --to-revision 回退到任意一个历史版本:

kubectl rollout undo deploy ngx-dep

版本降级图解
在这里插入图片描述
3、Kubernetes添加更新描述
kubectl rollout history 的版本列表好像有点太简单了、只有一个版本更新序号,而另一列 CHANGE-CAUSE 为什么总是显示成 ,能不能像Git一样,每次更新也加上说明信息呢?
在这里插入图片描述
做法也很简单,我们只需要在Deployment的 metadata 里加上一个新的字段 annotations。

  • annotations 添加的信息一般是给Kubernetes内部的各种对象使用的,有点像是“扩展属性”;
  • labels 主要面对的是Kubernetes外部的用户,用来筛选、过滤对象的。

简单的比喻来说呢,annotations 就是包装盒里的产品说明书,而 labels 是包装盒外的标签贴纸

应用保障:让Pod运行得更健康

1、资源申请
CPU、内存与存储卷有明显的不同,因为它是直接“内置”在系统里的,不像硬盘那样需要“外挂”,所以申请和管理的过程也就会简单很多

yaml示例

apiVersion: v1
kind: Pod
metadata:
  name: ngx-pod-resources

spec:
  containers:
  - image: nginx:alpine
    name: ngx

    resources:
      requests:
        cpu: 10m # 申请的是1%的CPU时间
        memory: 100Mi # 100MB的内存
      limits:
        cpu: 20m
        memory: 200Mi
  • “requests”,意思是容器要申请的资源,也就是说要求Kubernetes在创建Pod的时候必须分配这里列出的资源,否则容器就无法运行。
  • “limits”,意思是容器使用资源的上限,不能超过设定值,否则就有可能被强制停止运行。

内存的写法和磁盘容量一样,使用 Ki、Mi、Gi 来表示 KB、MB、GB,比如 512Ki、100Mi、0.5Gi 等。
Kubernetes允许容器精细分割CPU,即可以1个、2个地完整使用CPU,也可以用小数0.1、0.2的方式来部分使用CPU。这其实是效仿了UNIX“时间片”的用法,意思是进程最多可以占用多少CPU时间。

不过CPU时间也不能无限分割,Kubernetes里CPU的最小使用单位是0.001,为了方便表示用了一个特别的单位 m,也就是“milli”“毫”的意思,比如说500m就相当于0.5。

如果Pod不写 resources 字段,Kubernetes会如何处理呢? 意味着Pod对运行的资源要求“既没有下限,也没有上限”

测试一下: 先删除Pod的资源限制 resources.limits,把 resources.request.cpu 改成比较极端的“10”,也就是要求10个CPU:

  ...
  
    resources:
      requests:
        cpu: 10

使用 kubectl apply 创建这个Pod,你可能也会发现,虽然我们的Kubernetes集群里只有3个CPU,但Pod也能创建成功
如果再用 kubectl get pod 去查看的话,就会发现它处于“Pending”状态,实际上并没有真正被调度运行:
在这里插入图片描述

2、容器状态探针

Kubernetes为检查应用状态定义了三种探针,它们分别对应容器不同的状态:

  • Startup,启动探针,用来检查应用是否已经启动成功,适合那些有大量初始化工作要做,启动很慢的应用。
  • Liveness,存活探针,用来检查应用是否正常运行,是否存在死锁、死循环。
  • Readiness,就绪探针,用来检查应用是否可以接收流量,是否能够对外提供服务。

三种探针是递进的关系,只有到最后的Readiness状态才是一个容器最健康可用的状态,三种状态关系图
在这里插入图片描述
如何使用状态和探针来管理容器的呢?

如果一个Pod里的容器配置了探针,Kubernetes在启动容器后就会不断地调用探针来检查容器的状态:

  • 如果Startup探针失败,Kubernetes会认为容器没有正常启动,就会尝试反复重启,当然其后面的Liveness探针和Readiness探针也不会启动。
  • 如果Liveness探针失败,Kubernetes就会认为容器发生了异常,也会重启容器。
  • 如果Readiness探针失败,Kubernetes会认为容器虽然在运行,但内部有错误,不能正常提供服务,就会把容器从Service对象的负载均衡集合中排除,不会给它分配流量。

3、如何使用容器状态探针
Pod的YAML描述文件里定义探针有startupProbe、livenessProbe、readinessProb三种

(1)探针的配置方式都是一样的,关键字段有下面几个:

  • periodSeconds,执行探测动作的时间间隔,默认是10秒探测一次。
  • timeoutSeconds,探测动作的超时时间,如果超时就认为探测失败,默认是1秒。
  • successThreshold,连续几次探测成功才认为是正常,对于startupProbe和livenessProbe来说它只能是1。
  • failureThreshold,连续探测失败几次才认为是真正发生了异常,默认是3次。

(2)探测方式,Kubernetes支持3种:Shell、TCP Socket、HTTP GET,它们也需要在探针里配置:

  • exec,执行一个Linux命令,比如ps、cat等等,和container的command字段很类似。
  • tcpSocket,使用TCP协议尝试连接容器的指定端口。
  • httpGet,连接端口并发送HTTP GET请求。

(3) 要使用这些探针,必须要在开发应用时预留出“检查口”,这样Kubernetes才能调用探针获取信息。

  • 以Nginx作为示例,用ConfigMap编写一个配置文件:
apiVersion: v1
kind: ConfigMap
metadata:
  name: ngx-conf

data:
  default.conf: |
    server {
      listen 80;
      location = /ready {
        return 200 'I am ready';
      }
    }

在这个配置文件里,启用了80端口,然后用 location 指令定义了HTTP路径 /ready,它作为对外暴露的“检查口”,用来检测就绪状态,返回简单的200状态码和一个字符串表示工作正常。

  • Pod里三种探针的具体定义
apiVersion: v1
kind: Pod
metadata:
  name: ngx-pod-probe

spec:
  volumes:
  - name: ngx-conf-vol
    configMap:
      name: ngx-conf

  containers:
  - image: nginx:alpine
    name: ngx
    ports:
    - containerPort: 80
    volumeMounts:
    - mountPath: /etc/nginx/conf.d
      name: ngx-conf-vol

    startupProbe:
      periodSeconds: 1
      exec:
        command: ["cat", "/var/run/nginx.pid"]

    livenessProbe:
      periodSeconds: 10
      tcpSocket:
        port: 80

    readinessProbe:
      periodSeconds: 5
      httpGet:
        path: /ready
        port: 80

StartupProbe使用了Shell方式,使用 cat 命令检查Nginx存在磁盘上的进程号文件(/var/run/nginx.pid),如果存在就认为是启动成功,它的执行频率是每秒探测一次。

LivenessProbe使用了TCP Socket方式,尝试连接Nginx的80端口,每10秒探测一次。

ReadinessProbe使用的是HTTP GET方式,访问容器的 /ready 路径,每5秒发一次请求。

用 kubectl apply 创建这个Pod,然后查看它的状态:

在这里插入图片描述
用 kubectl logs 来查看Nginx的访问日志,里面会记录HTTP GET探针的执行情况
在这里插入图片描述

集群管理:用名字空间分隔系统资源

1、使用名字空间

创建一个名字空间

kubectl create ns test-ns 
kubectl get ns

想要把一个对象放入特定的名字空间,需要在它的 metadata 里添加一个 namespace 字段,比如要在“test-ns”里创建一个简单的Nginx Pod,就要这样写:

apiVersion: v1
kind: Pod
metadata:
  name: ngx
  namespace: test-ns

spec:
  containers:
  - image: nginx:alpine
    name: ngx

想要操作其他名字空间的对象必须要用 -n 参数明确指定:

kubectl get pod -n test-ns

因为名字空间里的对象都从属于名字空间,所以一旦名字空间被删除,它里面的所有对象也都会消失。删除刚才创建的名字空间“test-ns”:

kubectl delete ns test-ns

就会发现删除名字空间后,它里面的Pod也会无影无踪了。

2、什么是资源配额

名字空间可以像管理容器一样,给名字空间设定配额

名字空间的资源配额需要使用一个专门的API对象,叫做 ResourceQuota,简称是 quota
使用命令 kubectl create 创建一个它的样板文件:

export out="--dry-run=client -o yaml"
kubectl create quota dev-qt $out

因为资源配额对象必须依附在某个名字空间上,所以在它的 metadata 字段里必须明确写出 namespace(否则就会应用到default名字空间)。

下面我们先创建一个名字空间“dev-ns”,再创建一个资源配额对象“dev-qt”:

apiVersion: v1
kind: Namespace
metadata:
  name: dev-ns

---

apiVersion: v1
kind: ResourceQuota
metadata:
  name: dev-qt
  namespace: dev-ns

spec:
  ... ...

ResourceQuota对象的使用方式比较灵活,既可以限制整个名字空间的配额,也可以只限制某些类型的对象(使用scopeSelector),此处看第一种

在ResourceQuota里设置各类资源配额,简单地归类:

  • CPU和内存配额,使用 request.、limits.,这是和容器资源限制是一样的。
  • 存储容量配额,使 requests.storage 限制的是PVC的存储总量,也可以用
  • persistentvolumeclaims 限制PVC的个数。
  • 核心对象配额,使用对象的名字(英语复数形式),比如 pods、configmaps、secrets、services。
  • 其他API对象配额,使用 count/name.group 的形式,比如 count/jobs.batch、count/deployments.apps。

下面的这个YAML就是一个比较完整的资源配额对象

apiVersion: v1
kind: ResourceQuota
metadata:
  name: dev-qt
  namespace: dev-ns

spec:
  hard:
    requests.cpu: 10
    requests.memory: 10Gi
    limits.cpu: 10
    limits.memory: 20Gi

    requests.storage: 100Gi
    persistentvolumeclaims: 100

    pods: 100
    configmaps: 100
    secrets: 100
    services: 10

    count/jobs.batch: 1
    count/cronjobs.batch: 1
    count/deployments.apps: 1

名字空间加上的全局资源配额解释:

  • 所有Pod的需求总量最多是10个CPU和10GB的内存,上限总量是10个CPU和20GB的内存。
  • 只能创建100个PVC对象,使用100GB的持久化存储空间。
  • 只能创建100个Pod,100个ConfigMap,100个Secret,10个Service。
  • 只能创建1个Job,1个CronJob,1个Deployment。

3、如何使用资源配额

用 kubectl apply 创建这个资源配额对象

kubectl apply -f quota-ns.yml
//查看
kubectl get quota -n dev-ns
kubectl describe quota -n dev-ns

试验:

在名字空间里运行两个busybox Job,要加上 -n 参数:

kubectl create job echo1 -n dev-ns --image=busybox -- echo hello
kubectl create job echo2 -n dev-ns --image=busybox -- echo hello

在这里插入图片描述
ResourceQuota限制了名字空间里最多只能有一个Job,所以创建第二个Job对象时会失败,提示超出了资源配额。

4、默认资源配额

(1)在名字空间加上了资源配额限制之后,它会有一个合理但比较“烦人”的约束:要求所有在里面运行的Pod都必须用字段 resources 声明资源需求,否则就无法创建。

比如说,现在用命令 kubectl run 创建一个Pod:

kubectl run ngx --image=nginx:alpine -n dev-ns

在这里插入图片描述
提示说不满足配额要求。

为了保证名字空间的资源总量可管可控,Kubernetes就只能拒绝创建这样的Pod了

(2)如何让Kubernetes自动为Pod加上资源限制呢?也就是说给个默认值,省去反复设置配额?

辅助对象了—— LimitRange,简称是 limits,它能为API对象添加默认的资源配额限制
用命令 kubectl explain limits 来查看它的YAML字段详细说明

  • spec.limits 是它的核心属性,描述了默认的资源限制。
  • type 是要限制的对象类型,可以是 Container、Pod、PersistentVolumeClaim。
  • default 是默认的资源上限,对应容器里的 resources.limits,只适用于 Container。
  • defaultRequest 默认申请的资源,对应容器里的 resources.requests,同样也只适用于 Container。
  • max、min 是对象能使用的资源的最大最小值。

LimitRange对象的YAML示范

apiVersion: v1
kind: LimitRange
metadata:
  name: dev-limits
  namespace: dev-ns

spec:
  limits:
  - type: Container
    defaultRequest:
      cpu: 200m
      memory: 50Mi
    default:
      cpu: 500m
      memory: 100Mi
  - type: Pod
    max:
      cpu: 800m
      memory: 200Mi

它设置了每个容器默认申请0.2的CPU和50MB内存,容器的资源上限是0.5的CPU和100MB内存,每个Pod的最大使用量是0.8的CPU和200MB内存。

使用 kubectl apply 创建LimitRange之后,再用 kubectl describe 就可以看到它的状态:

kubectl describe limitranges -n dev-ns

在这里插入图片描述

现在就可以直接创建Pod了,不用编写 resources 字段,再运行之前的 kubectl run 命令:

kubectl run ngx --image=nginx:alpine -n dev-ns

有了这个默认的资源配额作为“保底”,这次就没有报错,Pod顺利创建成功,用 kubectl describe 查看Pod的状态,也可以看到LimitRange为它自动加上的资源配额:

在这里插入图片描述

系统监控:使用Metrics Server和Prometheus

Kubernetes为集群提供的两种系统级别的监控项目:Metrics Server和Prometheus

Metrics Server

监测系统指标

Linux系统有一个命令 top 能够实时显示当前系统的CPU和内存利用率,它是性能分析和调优的基本工具,非常有用。
Kubernetes也提供了类似的命令,就是 kubectl top,不过默认情况下这个命令不会生效,必须要安装一个插件Metrics Server才可以。

安装
Metrics Server的项目网址(https://github.com/kubernetes-sigs/metrics-server)
Metrics Server的所有依赖都放在了一个YAML描述文件里,可以使用wget或者curl下载:

wget https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml

kubectl apply 创建对象之前,有两个准备工作要做

第一个工作是修改YAML文件,需要在Metrics Server的Deployment对象里,加上一个额外的运行参数 --kubelet-insecure-tls,也就是这样:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: metrics-server
  namespace: kube-system
spec:
  ... ... 
  template:
    spec:
      containers:
      - args:
        - --kubelet-insecure-tls
        ... ... 

因为Metrics Server默认使用TLS协议,要验证证书才能与kubelet实现安全通信,而我们的实验环境里没有这个必要,加上这个参数可以让我们的部署工作简单很多(生产环境里就要慎用)。

第二个工作,是预先下载Metrics Server的镜像。
Metrics Server的镜像仓库用的是gcr.io,下载很困难。好在它也有国内的镜像网站,可以下载后再改名,然后把镜像加载到集群里的节点上。

这里我给出一段Shell脚本代码,供你参考:

repo=registry.aliyuncs.com/google_containers

name=k8s.gcr.io/metrics-server/metrics-server:v0.6.1
src_name=metrics-server:v0.6.1

docker pull $repo/$src_name

docker tag $repo/$src_name $name
docker rmi $repo/$src_name

两个准备工作都完成之后,我们就可以使用YAML部署Metrics Server了:

kubectl apply -f components.yaml

Metrics Server属于名字空间“kube-system”,可以用 kubectl get pod 加上 -n 参数查看它是否正常运行:

kubectl get pod -n kube-system

在这里插入图片描述
有了Metrics Server插件,就可以使用命令 kubectl top 来查看Kubernetes集群当前的资源状态了。它有两个子命令,node 查看节点的资源使用率,pod 查看Pod的资源使用率。

kubectl top node
kubectl top pod -n kube-system

在这里插入图片描述
从这个截图里可以看到:

  • 集群里两个节点CPU使用率都不高,分别是8%和4%,但内存用的很多,master节点用了差不多一半(48%),而worker节点几乎用满了(89%)。
  • 名字空间“kube-system”里有很多Pod,其中apiserver最消耗资源,使用了75m的CPU和363MB的内存。’
HorizontalPodAutoscaler(水平自动伸缩)

“HorizontalPodAutoscaler”,简称是“hpa”。顾名思义,它是专门用来自动伸缩Pod数量的对象,适用于Deployment和StatefulSet,但不能用于DaemonSet(原因很明显吧)。
HorizontalPodAutoscaler的能力完全基于Metrics Server,它从Metrics Server获取当前应用的运行指标,主要是CPU使用率,再依据预定的策略增加或者减少Pod的数量。

使用HorizontalPodAutoscaler,首先要定义Deployment和Service,创建一个Nginx应用,作为自动伸缩的目标对象:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: ngx-hpa-dep

spec:
  replicas: 1
  selector:
    matchLabels:
      app: ngx-hpa-dep

  template:
    metadata:
      labels:
        app: ngx-hpa-dep
    spec:
      containers:
      - image: nginx:alpine
        name: nginx
        ports:
        - containerPort: 80

        resources:
          requests:
            cpu: 50m
            memory: 10Mi
          limits:
            cpu: 100m
            memory: 20Mi
---

apiVersion: v1
kind: Service
metadata:
  name: ngx-hpa-svc
spec:
  ports:
  - port: 80
    protocol: TCP
    targetPort: 80
  selector:
    app: ngx-hpa-dep

在这个YAML里只部署了一个Nginx实例,名字是 ngx-hpa-dep。注意在它的 spec 里一定要用 resources 字段写清楚资源配额,否则HorizontalPodAutoscaler会无法获取Pod的指标,也就无法实现自动化扩缩容。

接下来我们要用命令 kubectl autoscale 创建一个HorizontalPodAutoscaler的样板YAML文件,它有三个参数:

  • min,Pod数量的最小值,也就是缩容的下限。
  • max,Pod数量的最大值,也就是扩容的上限。
  • cpu-percent,CPU使用率指标,当大于这个值时扩容,小于这个值时缩容。

现在就来为刚才的Nginx应用创建HorizontalPodAutoscaler,指定Pod数量最少2个,最多10个,CPU使用率指标设置的小一点,5%,方便观察扩容现象:

export out="--dry-run=client -o yaml"              # 定义Shell变量
kubectl autoscale deploy ngx-hpa-dep --min=2 --max=10 --cpu-percent=5 $out

得到的YAML描述文件就是这样:

apiVersion: autoscaling/v1
kind: HorizontalPodAutoscaler
metadata:
  name: ngx-hpa

spec:
  maxReplicas: 10
  minReplicas: 2
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: ngx-hpa-dep
  targetCPUUtilizationPercentage: 5

使用命令 kubectl apply 创建这个HorizontalPodAutoscaler后,它会发现Deployment里的实例只有1个,不符合min定义的下限的要求,就先扩容到2个:

在这里插入图片描述
从这张截图里你可以看到,HorizontalPodAutoscaler会根据YAML里的描述,找到要管理的Deployment,把Pod数量调整成2个,再通过Metrics Server不断地监测Pod的CPU使用率。

下面我们来给Nginx加上压力流量,运行一个测试Pod,使用的镜像是“httpd:alpine”,它里面有HTTP性能测试工具ab(Apache Bench):

kubectl run test -it --image=httpd:alpine -- sh

在这里插入图片描述
然后我们向Nginx发送一百万个请求,持续1分钟,再用 kubectl get hpa 来观察HorizontalPodAutoscaler的运行状况:

ab -c 10 -t 60 -n 1000000 'http://ngx-hpa-svc/'

在这里插入图片描述
因为Metrics Server大约每15秒采集一次数据,所以HorizontalPodAutoscaler的自动化扩容和缩容也是按照这个时间点来逐步处理的。

当它发现目标的CPU使用率超过了预定的5%后,就会以2的倍数开始扩容,一直到数量上限,然后持续监控一段时间,如果CPU使用率回落,就会再缩容到最小值。

Prometheus

1、Prometheus官方的架构图
在这里插入图片描述

Prometheus系统的核心是它的Server,里面有一个时序数据库TSDB,用来存储监控数据,另一个组件Retrieval使用拉取(Pull)的方式从各个目标收集数据,再通过HTTP Server把这些数据交给外界使用。

在Prometheus Server之外还有三个重要的组件:

  • Push Gateway,用来适配一些特殊的监控目标,把默认的Pull模式转变为Push模式。
  • Alert Manager,告警中心,预先设定规则,发现问题时就通过邮件等方式告警。
  • Grafana是图形化界面,可以定制大量直观的监控仪表盘。

2、部署

在Kubernetes实验环境里部署Prometheus,选用了“kube-prometheus”项目(https://github.com/prometheus-operator/kube-prometheus/)

先下载kube-prometheus的源码包,当前的最新版本是0.11:

wget https://github.com/prometheus-operator/kube-prometheus/archive/refs/tags/v0.11.0.tar.gz

解压缩后,Prometheus部署相关的YAML文件都在 manifests 目录里,有近100个

要做一些准备工作,才能够安装Prometheus

第一步,是修改 prometheus-service.yaml、grafana-service.yaml。这两个文件定义了Prometheus和Grafana服务对象,我们可以给它们添加 type: NodePort,这样就可以直接通过节点的IP地址访问(当然也可以配置成Ingress)。

第二步,是修改 kubeStateMetrics-deployment.yaml、prometheusAdapter-deployment.yaml,因为它们里面有两个存放在gcr.io的镜像,必须解决下载镜像的问题。
需要修改镜像名字,把前缀都改成 chronolaw

image: k8s.gcr.io/kube-state-metrics/kube-state-metrics:v2.5.0
image: k8s.gcr.io/prometheus-adapter/prometheus-adapter:v0.9.1

image: chronolaw/kube-state-metrics:v2.5.0
image: chronolaw/prometheus-adapter:v0.9.1

要执行两个 kubectl create 命令来部署Prometheus,先是 manifests/setup 目录,创建名字空间等基本对象,然后才是 manifests 目录:

kubectl create -f manifests/setup
kubectl create -f manifests

Prometheus的对象都在名字空间“monitoring”里,创建之后可以用 kubectl get 来查看状态:

在这里插入图片描述

这些Pod都运行正常,我们再来看看它对外的服务端口:

在这里插入图片描述
前面修改了Grafana和Prometheus的Service对象,所以这两个服务就在节点上开了端口,Grafana是“30358”,Prometheus有两个端口,其中“9090”对应的“30827”是Web端口。

Prometheus界面: ip:30827
Grafana界面:ip:30358

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值