K8S基础-ingress&数据存储

一、ingress

1.概述

ingress相当于一个7层负载均衡器。是K8S反向代理的抽象,工作原理类似于Nginx,可以理解成ingress里简历诸多映射规则,Ingress Controller通过监听则这些配置并转化成Nginx的反向代理配置,然后对外部提供服务

ingress:K8S中的一个对象,作用定义请求如何转发带service的规则

ingress controller:具体实现反向代理及负载均衡的程序,对ingress定义的规则进行解析,通过配置尽心请求转发。实现方式有很多Nginx、Contour、Haproxy等

Ingress(以 Nginx 为例)的工作原理如下:

1.用户编写 Ingress 规则,说明哪个域名对应 kubernetes 集群中的哪个 Service

2.Ingress 控制器动态感知 Ingress 服务规则的变化,然后生成一段对应的 Nginx 反向代理配置

3.Ingress 控制器会将生成的 Nginx 配置写入到一个运行着的 Nginx 服务中,并动态更新

4.到此为止,其实真正在工作的就是一个 Nginx 了,内部配置了用户定义的请求转发规则

2.使用

1)环境准备 搭建ingress环境

bash 复制代码
# 创建文件夹
mkdir ingress-controller
cd ingress-controller

# 获取ingress-nginx,使用的是0.30版本
wget https://gitcode.com/mirrors/ingress-nginx/raw/nginx-0.30.0/deploy/static/mandatory.yaml

wget https://gitcode.com/mirrors/ingress-nginx/raw/nginx-0.30.0/deploy/static/provider/baremetal/service-nodeport.yaml


# 创建ingress-nginx
kubectl apply -f ./

# 查看ingress-nginx
kubectl get pod -n ingress-nginx

# 查看serive
kubectl get svc -n ingress-nginx

2)创建两个不同控制器显示效果

创建两个deployment分别为tomcat、nginx两个不同的页面来显示ingress-nginx效果

bash 复制代码
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  namespace: dev
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx-pod
  template:
    metadata:
      labels:
        app: nginx-pod
    spec:
      containers:
      - name: nginx
        image: nginx:1.17.1
        ports:
        - containerPort: 80
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: tomcat-deployment
  namespace: dev
spec:
  replicas: 3
  selector:
    matchLabels:
      app: tomcat-pod
  template:
    metadata:
      labels:
        app: tomcat-pod
    spec:
      containers:
      - name: tomcat
        image: tomcat:8.5-jre10-slim
        ports:
        - containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
  name: nginx-service
  namespace: dev
spec:
  selector:
    app: nginx-pod
  clusterIP: None
  type: ClusterIP
  ports:
  - port: 80
    targetPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: tomcat-service
  namespace: dev
spec:
  selector:
    app: tomcat-pod
  clusterIP: None
  type: ClusterIP
  ports:
  - port: 8080
    targetPort: 8080

3)创建ingress-http.yaml

内部的nginx负载均衡策略

bash 复制代码
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ingress-http
  namespace: dev
spec:
  rules:
  - host: nginx.com
    http:
      paths:
      - path: /
        pathType: ImplementationSpecific
        backend:
          service:
            name: nginx-service
            port:
              number: 80
  - host: tomcat.com
    http:
      paths:
      - path: /
        pathType: ImplementationSpecific
        backend:
          service:
            name: tomcat-service
            port:
              number: 8080

其中pathType中三种路径的区别

(1)ImplementationSpecific

匹配规则:路径作为前缀匹配,支持$精准锁定结尾

bash 复制代码
path: /api
pathType: ImplementationSpecific

# 匹配:/api、/api/、/api/user、/api/list/123
# 不匹配:/apis、/a
bash 复制代码
path: /api$
pathType: ImplementationSpecific

# 仅匹配/api
# /api/、/api/user 都不匹配
(2)Prefix

匹配规则:路径是 URL 的前缀,自动区分路径分段,不会跨单词匹配

bash 复制代码
path: /api
pathType: Prefix

# 匹配成功:/api、/api/、/api/user、/api/list
# 不匹配:/apis、/ap、/aapi
(3)Exact

匹配规则:URL 必须和 path 字符串一字不差完全相等,多一个斜杠、少一个字符都不行。

bash 复制代码
path: /api
pathType: Exact

# 仅匹配/api
(4)https代理

创建证书

复制代码
openssl req -x509 -sha256 -nodes -days 365 -newkey rsa:2048 -keyout tls.key -out tls.crt -subj "/C=CN/ST=BJ/L=BJ/O=nginx/CN=xiaobai.com"

内部的nginx负载均衡策略https代理

复制代码
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ingress-https
  namespace: dev
spec:
  tls:
    - hosts:
      - www.nginx.com
      - www.tomcat.com
      secretName: tls-secret # 指定秘钥
  rules:
    - host: www.nginx.com
      http:
        paths:
          - path: /
            pathType: ImplementationSpecific
            backend:
              service:
                name: nginx-service
                port:
                  number: 80
    - host: www.tomcat.com
      http:
        paths:
          - path: /
            pathType: ImplementationSpecific
            backend:
              service:
                name: tomcat-service
                port:
                  number: 8080

二、数据存储

Volume是pod中能被多个容器访问的共享目录,被定义在pod上,然后被一个pod多个容器挂载到具体的文件目录下,K8S通过Volume实现同一个Pod中不同容器的数据存储与持久化存储且与Pod的生命周期无关,容器重启和终止数据不会丢失

1.K8S中的Vloume支持多种类型,常见的有一下几个:

简单存储:EmptyDir、HostPath、NFS

高级存储:PV、PVC

配置存储:ConfigMap、Secret

1.基本存储

1)EmptyDir

EmptyDir是最基础的数据类型,一个Empty就是Host上的一个空目录

Emptyis是Pod被分配到Node时创建的,初始内容为空,且无需指定宿主机的目录文件,K8S会自动分配一个目录,当Pod销毁时,Empty数据会被永久删除。用途:

1.临时空间,例如用于某些应用程序所需的临时目录,无须永久保留

2.一个容器需要从另一个容器中获取数据的目录

示例:一个pod中准备两个容器,声明一个Volume分别挂在两个容器的目录中,然后nginx容器负责向Volume中写日志,busybox中通过命令将日志内容读取到控制台

创建volume-emptydir.yaml

bash 复制代码
apiVersion: v1
kind: Pod
metadata:
  name: volume-emptydir
  namespace: nginx-ns
spec:
  containers:
  - name: nginx
    image: nginx:latest
    ports:
    - containerPort: 80
    volumeMounts:
    - name: logs-volume
      mountPath: /var/log/nginx
  - name: busybox
    image: busybox:1.30
    command: ["/bin/sh","-c","tail -f /log/access.log"]
    volumeMounts:
    - name: logs-volume
      mountPath: /logs
  volumes:
  - name: logs-volume
    emptyDir: {}

测试创建pod

bash 复制代码
# 创建Pod
kubectl create -f volume-emptydir.yaml

# 查看pod
kubectl get pods volume-emptydir -n nginx-ns -o wide

# 访问pod产生日志
curl 10.244.1.62

# 通过kubectl logs命令查看容器的标准输出
kubectl logs -f volume-emptydir -n nginx -c busybox

此时每访问一次就会产生一行日志

且当容器重新创建之后不会持续保存

2)HostPath

若想简单的实现数据的持久化存储可以选择HostPath。 HostPath就是将node主机中一个实际目录挂到pod中,以供容器使用,这样设计就可以保证Pod销毁了,但是数据依旧可以存在于Node主机上

常见volume-hostpath.yaml

bash 复制代码
apiVersion: v1
kind: Pod
metadata:
  name: volume-hostpath
  namespace: nginx-ns
spec:
  containers:
  - name: nginx
    image: nginx:latest
    ports:
    - containerPort: 80
    volumeMounts:
    - name: logs-volume
      mountPath: /var/log/nginx
  - name: busybox
    image: busybox:1.30
    command: ["/bin/sh","-c","tail -f /logs/access.log"]
    volumeMounts:
    - name: logs-volume
      mountPath: /logs
  volumes:
  - name: logs-volume
    hostPath:
      path: /root/logs
      type: DirectoryOrCreate

此时当容器创建后会在他创建Pod的节点上创建一个目录/root/logs来挂载到容器中,此时就算是节点重新创建也是不会消失的

此时删除pod后重启不会文件不会消失

3)NFS

Hostpath可以解决数据持久化的问题,但是一旦Node节点故障了,Pod如果转移到了别的节点,又会出现问题了,此时需要准备单独的网络存储系统,比较常用的用NFS、CIFS。

NFS是一个网络文件存储系统,可以搭建一台NFS服务器,然后将Pod中的存储直接连接到NFS系统上,这样的话,无论Pod在节点上怎么转移,只要Node跟NFS的对接没问题,数据就可以成功访问。

1)准备一台nfs服务器,直接使用master节点做nfs服务器

bash 复制代码
# 在服务器上安装nfs服务
yum install nfs-utils -y

# 准备一个共享目录
mkdir /root/data/nfs -pv

# 将共享目录的读写权限给192.168.22.0/24网段中的所有主机
vim /etc/exports
more /etc/exports
/root/data/nfs    192.168.22.0(rw,no_root_squash)

# 启动nfs服务器
systemctl restart nfs

2)接下来在每个node节点上都安装nfs,这样的目的就是为了node节点可以驱动nfs设备

bash 复制代码
# 在node上安装nfs服务,但是不用启动
yum install nfs-untils -y

3)接下来,就可以编写volume-nfs.yaml

bash 复制代码
apiVersion: v1
kind: Pod
metadata:
  name: volume-nfs
  namespace: nginx-ns
spec:
  containers:
  - name: nginx
    image: nginx:latest
    ports:
    - containerPort: 80
    volumeMounts:
    - name: logs-volume
      mountPath: /var/log/nginx
  - name: busybox
    image: busybox:1.30
    command: ["/bin/sh","-c","tail -f /logs/access.log"]
    volumeMounts:
    - name: logs-volume
      mountPath: /logs
  volumes:
  - name: logs-volume
    hostPath:
      path: /root/logs
      type: DirectoryOrCreate

创建pod节点此时随机调度到node2节点上了

此时里面有这几条日志目录

删除pod节点重新创建,将其创建到node1节点上访问日志

此时就实现了不同节点上的持久化存储

2.高级存储

前面已经使用NFS提供了存储但是就要要求用户会搭建NFS系统,并且会在yaml中配置nfs。但是K8S中支持的存储系统有很多,要求全部掌握不显示。为屏蔽底层存储实现细节方便使用,于是就有了PV和PVC两种资源对象

使用PV和PVC后可以进一步细分

存储:存储工程师维护

PV:K8S管理员维护

PVC:K8S用户维护

1)PV

PV是存储资源的抽象,下面是资源清单文件

bash 复制代码
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv2
spec:
  nfs: # 存储类型,与底层真正存储对应
  capacity: # 存储能力,目前只支持存储空间的设置
    storage: 2Gi
  accessModes:  # 访问模式
  storageClassName: # 存储类别
  persistentVolumeReclaimPolicy: # 回收策略

PVC 的关键配置参数说明:

访问模式(accessModes): 用于描述用户应用对存储资源的访问权限

选择条件(selector): 通过Label selector的设置,可使PVC对于系统中已存在的PV进行筛选

存储类别(storageClassName): PVC在定义时可以设定需要的后端存储的类别,只有设置了该class的pv才能被系统选出

资源请求(Resources): 描述对存储资源的请求

  • 存储类型:底层实际存储的类型,kubernetes 支持多种存储类型,每种存储类型的配置都有所差异
  • 存储能力(capacity):目前只支持存储空间的设置 (storage=1Gi),不过未来可能会加入 IOPS、吞吐量等指标的配置
  • 访问模式(accessModes):用于描述用户应用对存储资源的访问权限,访问权限包括下面几种方式:
    • ReadWriteOnce(RWO):读写权限,但是只能被单个节点挂载
    • ReadOnlyMany(ROX):只读权限,可以被多个节点挂载
    • ReadWriteMany(RWX):读写权限,可以被多个节点挂载
  • 回收策略(persistentVolumeReclaimPolicy): 当 PV 不再被使用了之后,对其的处理方式。目前支持三种策略:
    • Retain(保留)保留数据,需要管理员手工清理数据
    • Recycle(回收)清除 PV 中的数据,效果相当于执行 rm -rf /thevolume/*
    • Delete(删除)与 PV 相连的后端存储完成 volume 的删除操作,当然这常见于云服务商的存储服务
  • 存储类别:PV 可以通过 storageClassName 参数指定一个存储类别
    • 具有特定类别的 PV 只能与请求了该类别的 PVC 进行绑定
    • 未设定类别的 PV 则只能与不请求任何类别的 PVC 进行绑定
  • 状态(status):一个 PV 的生命周期中,可能会处于 4 中不同的阶段
    • Available(可用):表示可用状态,还未被任何 PVC 绑定
    • Bound(已绑定):表示 PV 已经被 PVC 绑定
    • Released(已释放):表示 PVC 被删除,但是资源还未被集群重新声明
    • Failed(失败):表示该 PV 的自动回收失败

实验:

使用NFS作为存储,来演示PV的使用,创建3个PV,对应NFS中的三个暴露的路径

准备NFS环境

bash 复制代码
# 创建目录
mkdir /root/data/{pv1,pv2,pv3} -pv

# 暴露服务
more /etc/exports
/root/data/pv1    192.168.22.0/24(rw,no_root_spuash)
/root/data/pv2    192.168.22.0/24(rw,no_root_spuash)
/root/data/pv3    192.168.22.0/24(rw,no_root_spuash)

# 重启服务
systemctl restart nfs

创建pv.yaml

bash 复制代码
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv1
spec:
  capacity:
    storage: 1Gi
  accessModes:
  - ReadwriteMany
  persistentVolumeReclaimPolicy: Retain
  nfs:
    path: /root/data/pv1
    server: 192.168.22.156
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv2
spec:
  capacity:
    storage: 2Gi
  accessModes:
  - ReadwriteMany
  persistentVolumeReclaimPolicy: Retain
  nfs:
    path: /root/data/pv2
    server: 192.168.22.156
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv3
spec:
  capacity:
    storage: 3Gi
  accessModes:
  - ReadwriteMany
  persistentVolumeReclaimPolicy: Retain
  nfs:
    path: /root/data/pv3
    server: 192.168.22.156

创建pv

2)PVC

bash 复制代码
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pvc
  namespace: dev
spec:
  accessModes: # 访问模式
  selector: # 采用标签对PV选择
  storageClassName: # 存储类别
  resources: # 请求空间
    requests:
      storage: 5Gi

PVC 的关键配置参数说明:

访问模式(accessModes):用于描述用户应用对存储资源的访问权限

选择条件(selector):通过 Label Selector 的设置,可使 PVC 对于系统中已存在的 PV 进行筛选

存储类别(storageClassName):PVC 在定义时可以设定需要的后端存储的类别,只有设置了该 class 的 pv 才能被系统选出

资源请求(Resources ):描述对存储资源的请求

创建pvc.yaml申请pv

bash 复制代码
apiVersion: v1
kind: PersistentVolumeClain
metadata:
  name: pvc1
  namespace: nginx-ns
spec:
  accessModes:
  - ReadWriteMany
  resources:
    requests:
      storage: 1Gi
---
apiVersion: v1
kind: PersistentVolumeClain
metadata:
  name: pvc2
  namespace: nginx-ns
spec:
  accessModes:
  - ReadWriteMany
  resources:
    requests:
      storage: 1Gi
---
apiVersion: v1
kind: PersistentVolumeClain
metadata:
  name: pvc2
  namespace: nginx-ns
spec:
  accessModes:
  - ReadWriteMany
  resources:
    requests:
      storage: 1Gi

创建pvc

此时STATUS被状态就是Bound绑定状态一般来说申请pv的时候分配是按照申请的大小分配的但是这里都是1Gi因此分配的时候就按照顺序了

3)创建pod使用pvc

创建pods.yaml,使用pv

bash 复制代码
apiVersion: v1
kind: Pod
metadata:
  name: pod1
  namespace: nginx-ns
spec:
  containers:
  - name: busybox
    image: busybox:1.30
    command: ["/bin/sh","-c","while true;do echo pod1 >> /root/out.txt; sleep 10;done;"]
    volumeMounts:
    - name: volume
      mountPath: /root/
    volumes:
      - name: volume
        persistentVolumeClaim:
          claimName: pvc1
          readOnly: false
---
apiVersion: v1
kind: Pod
metadata:
  name: pod2
  namespace: nginx-ns
spec:
  containers:
  - name: busybox
    image: busybox:1.30
    command: ["/bin/sh","-c","while true;do echo pod2 >> /root/out.txt; sleep 10;done;"]
    volumeMounts:
    - name: volume
      mountPath: /root/
    volumes:
      - name: volume
        persistentVolumeClaim:
          claimName: pvc2
          readOnly: false

创建好之后pv、pvc、还有pod的情况

创建好之后访问到pv下分别查看pv1 pv2下的内容

此时两个节点下的写入的内容就对应上面的配置文件

4)生命周期

PVC和PV是一一对应的,PV和PVC中间相互尊重下面的生命周期

**资源供应:**用户先手动创建底层存储和PV

**资源绑定:**用户创建PVC,K8S根据PVC的声明去寻找PV并绑定

用户定义好PVC后会根据PVC对资源的请求找存在且满足条件的PV进行绑定就能使用 这个PVC了若没找到就是无线Pending的状态,直到管理员创建一个符合要求的PV

**资源使用:**用户可在 pod 中像 volume 一样使用 pvc,Pod 使用 Volume 的定义,将 PVC 挂载到容器内的某个路径进行使用

**资源释放:**用户删除 pvc 来释放 pv

当存储资源使用完毕后,用户可以删除 PVC,与该 PVC 绑定的 PV 将会被标记为 "已释放",但还不能立刻与其他 PVC 进行绑定。通过之前 PVC 写入的数据可能还被留 在存储设备上,只有在清除之后该 PV 才能再次使用

**资源回收:**kubernetes 根据 pv 设置的回收策略进行资源的回收

对于 PV,管理员可以设定回收策略,用于设置与之绑定的 PVC 释放资源之后如何处 理遗留数据的问题。只有 PV 的存储空间完成回收,才能供新的 PVC 绑定和使用

3.配置存储

1)ConfigMap

ConfigMap是一种比较特殊的存储卷,它的主要作用是用来存储配置信息的。

创建configmap.yaml

bash 复制代码
apiVersion: v1
kind: ConfigMap
metadata:
  name: configmap
  namespace: nginx-ns
data:
  info: |
    username:admin
    password:123456

使用配置文件创建configmap

bash 复制代码
# 创建configmap
kubectl apply -f configmap.yaml

# 查看configmap详情
kubectl describe cm configmap -n nginx-ns

运行情况

创建pod-configmap.yaml,将上面的configmap文件挂载进去

bash 复制代码
apiVersion: v1
kind: Pod
metadata:
  name: nginx
  namespace: nginx-ns
spec:
  containers:
  - name: nginx
    image: nginx:latest
    ports:
    - containerPort: 80
    volumeMounts:
    - name: config
      mountPath: /var/share/nginx
  volumes:
  - name: config
    configMap:
      name: configmap

进入容器查看配置文件挂载情况

2)secret

在K8S中,存在一中ConfigMap相似的对象,称为Secret对象,通常用于存储敏感信息,例如密码、密钥、证书等等

首先使用base64对数据编码(这只是编码不是加密)

bash 复制代码
echo -n "admin" | base 64
YWRtaW4=

echo -n "123456" | base64
MTIzNDU2

编写secret.yaml,并创建Secret

bash 复制代码
apiVersion: v1
kind: Secret
metadata:
  name: secret
  namespace: nginx-ns
type: Opaque
data:
  username: YWRtaW4=
  password: MTIzNDU2

创建pod-secret.yaml将上面创建的secret挂载进去

创建pod挂载使用

bash 复制代码
apiVersion: v1
kind: Pod
metadata:
  name: pod-secret
  namespace: nginx-ns
spec:
  containers:
  - name: nginx
    image: nginx:latest
    volumeMounts:
    - name: config
      mountPath: /var/share/nginx
  volumes:
  - name: config
    secret:
      secretName: secret

进入容器内查看

直接就是明文