八、存储
本文最后更新于135 天前,其中的信息可能已经过时,如有错误请发送邮件到big_fw@foxmail.com

1.ConfigMap

就是想取代传统运维的配置中心
所以他存的都是配置文件或者配置参数,不管是kv对象,还是文本,都可以存
就是写在etcd中,需要的时候拿出来用就好了

配置中心的概念

大部分都是图形化的,后端的nginx只需要安装个agent端,这些agent只需要定时的访问这个配置中心的nginx项目
就可以更新的,我们运维工程师只需要在配置文件中更改,那么agent端就会收到消息,然后去修改配置文件

集群内的微服务也有自己的配置中心,例如gogs,nacos等等,但是需要重启服务

file

configMap 描述信息

ConfigMap 功能在 Kubernetes1.2 版本中引入,许多应用程序会从配置文件、命令行参数或环境变量中读取配置信息。ConfigMap API 给我们提供了向容器中注入配置信息的机制,ConfigMap 可以被用来保存单个属性,也可以用来保存整个配置文件或者 JSON 二进制等对象

ConfigMap 的创建

Ⅰ、使用目录创建

$ ls docs/user-guide/configmap/kubectl/
    game.file
    ui.file

$ cat docs/user-guide/configmap/kubectl/game.file   ==  key
version=1.17
name=dave               == values
age=18

$ cat docs/user-guide/configmap/kubectl/ui.properties
level=2
color=yellow

# 这样用命令创建比写清单容易很多
$ kubectl create configmap game-config --from-file=docs/user-guide/configmap/kubectl

—from-file 指定在目录下的所有文件都会被用在 ConfigMap 里面创建一个键值对,键的名字就是文件名,值就是文件的内容,也就是把 文件名=文件内容 存储在etcd数据库中

Ⅱ、使用文件创建

只要指定为一个文件就可以从单个文件中创建 ConfigMap

$ kubectl create configmap game-config-2 --from-file=./game.file

—from-file 这个参数可以使用多次,你可以使用两次分别指定上个实例中的那两个配置文件,效果就跟指定整个目录是一样的

Ⅲ、使用字面值创建

使用文字值创建,利用 —from-literal 参数传递配置信息,该参数可以使用多次,格式如下

$ kubectl create configmap literal-config --from-literal=zxs=shuaige --from-literal=lzt=bigshuaige

$ kubectl get configmaps literal-config -o yaml
apiversion:vl
data:
  1zt : bigshuaige
  zxs : shuaige
kind:configMap
...

Pod 中使用 ConfigMap

Ⅰ、使用 ConfigMap 来替代环境变量

apiVersion: v1
kind: ConfigMap
metadata:
  name: literal-config
  namespace: default
data:
  name: dave
  password: pass
apiVersion: v1
kind: ConfigMap
metadata:
  name: env-config
  namespace: default
data:
  log_level: INFO
apiVersion: v1
kind: Pod
metadata:
  name: cm-env-test-pod
spec:
  containers:
    - name: test-container
      image: wangyanglinux/myapp:v1
      command: [ "/bin/sh", "-c", "env" ]
      #env是打印当前的环境变量,他不是一个持续的前台进程,所以打印完就死了,后面的restartPolicy: Never 与它对应。打印完死了不重启,可以查看日志看及执行结果
      env:
        - name: USERNAME
          valueFrom:
            configMapKeyRef:
              name: literal-config
              key: name
        - name: PASSWORD
          valueFrom:
            configMapKeyRef:
              name: literal-config
              key: password
      envFrom:
      #全部注入,不需要自己写名字了,只要里面的符合键值对格式
        - configMapRef:
            name: env-config
  restartPolicy: Never
Ⅱ、用 ConfigMap 设置命令行参数

就是Pod的启动命令

apiVersion: v1
kind: Pod
metadata:
  name: cm-command-dapi-test-pod
spec:
  containers:
    - name: test-container
      image: wangyanglinux/myapp:v1
      command: [ "/bin/sh", "-c", "echo $(USERNAME) $(PASSWORD)" ]
      env:
        - name: USERNAME
          valueFrom:
            configMapKeyRef:
              name: literal-config
              key: name
        - name: PASSWORD
          valueFrom:
            configMapKeyRef:
              name: literal-config
              key: password
  restartPolicy: Never
Ⅲ、通过数据卷插件使用ConfigMap

在数据卷里面使用这个 ConfigMap,有不同的选项。最基本的就是将文件填入数据卷,在这个文件中,键就是文件名,键值就是文件内容

apiVersion: v1
kind: Pod
metadata:
  name: cm-volume-test-pod
spec:
  containers:
    - name: test-container
      image: wangyanglinux/myapp:v1
      volumeMounts:
      - name: config-volume
        mountPath: /etc/config
  volumes:
    - name: config-volume
      configMap:
        name: literal-config
  restartPolicy: Never

进去后发现这是个软链接,..data/有原文件,但是目录很怪,实际上这是配置中心的版本号,为了以后的热更新
configmap文件不是共享方式进来了,而是注入,类似于echo >>
为什么是注入,因为可以分批次注入,一次注入10%或者别的数值,而如果是共享,那么一旦发生修改,要是有一千台机器的话就瞬间全过来下载
压力会很大。

file

ConfigMap 的热更新

apiVersion: v1
kind: ConfigMap
metadata:
  name: log-config
  namespace: default
data:
  log_level: INFO
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: hot-update
spec:
  replicas: 2
  template:
    metadata:
      labels:
        run: my-nginx
    spec:
      containers:
      - name: my-nginx
        image: wangyanglinux/myapp:v1
        ports:
        - containerPort: 80
        volumeMounts:
        - name: config-volume
          mountPath: /etc/config
      volumes:
        - name: config-volume
          configMap:
            name: log-config
# 命令的作用是在运行着my-nginx标签应用的Pod中检索日志级别配置文件log_level的内容,并将其打印出来。
$ kubectl exec `kubectl get pods -l run=my-nginx  -o=name|cut -d "/" -f2` cat /etc/config/log_level
INFO

修改 ConfigMap

$ kubectl edit configmap log-config

修改 log_level 的值为 DEBUG 等待大概 10 秒钟时间,再次查看环境变量的值

$ kubectl exec `kubectl get pods -l run=my-nginx  -o=name|cut -d "/" -f2` cat /tmp/log_level
DEBUG

!!!特别注意 configMap 如果以 ENV 的方式挂载至容器,修改 configMap 并不会实现热更新

ConfigMap 更新后滚动更新 Pod

更新 ConfigMap 目前并不会触发相关 Pod 的滚动更新,可以通过修改 pod annotations 的方式强制触发滚动更新

$ kubectl patch deployment my-nginx --patch '{"spec": {"template": {"metadata": {"annotations": {"version/config": "20190411" }}}}}'

这个例子里我们在 .spec.template.metadata.annotations 中添加 version/config,每次通过修改 version/config 来触发滚动更新

!!! 更新 ConfigMap 后:

  • 使用该 ConfigMap 挂载的 Env 不会同步更新
  • 使用该 ConfigMap 挂载的 Volume 中的数据需要一段时间(实测大概10秒)才能同步更新

只有卷插件当成文件的configmap才允许热更新,因为环境变量是Pod启动之前就注入的,如果环境变量想变只能把Pod破坏掉,再产生新的。卷插件是在Pod之后才有的

subPath

当使用ConfigMap挂载到容器的目录时,如果该目录中已经存在文件,默认情况下,ConfigMap中的内容会覆盖整个目录中的现有文件。这是因为在Linux系统中,目录的挂载通常会替换目标目录下的所有内容.

为了避免这种覆盖,可以使用subPath属性。subPath允许你将ConfigMap中的特定文件挂载到容器的指定路径,而不会影响该路径下的其他文件。例如,如果你只想挂载ConfigMap中的application.properties文件到容器的/etc/config目录,而不覆盖该目录下的其他文件,可以在volumeMounts中设置subPath: application.properties.

注意!!!如果使用了ConfigMap的subPath挂载为容器的Volume,Kubernetes不会做自动热更新

如果希望在不重启Pod的情况下更新ConfigMap的内容,您可能需要采用以下策略之一:

  • 使用支持热更新的卷插件,如ConfigMap的emptyDir或hostPath卷,这些卷可以在更新ConfigMap后立即反映变化。
  • 使用Kubernetes的滚动更新机制,通过更新Deployment或StatefulSet的配置来触发新的Pod实例的创建,从而使用更新后的ConfigMap。
  • 使用外部工具或自写脚本定期检查ConfigMap的变化,并在检测到变化时滚动重启相关的Pod。

案例

在elasticsearch 6.x版本中,默认的回收策略是 CMS(Concurrent Mark Sweep).CMS垃圾回收器在老年代内存使用率达到特定阈值时触发,通过标记-清除算法进行垃圾回收。在某些情况下,如果集群的堆内存较大,,会导致容器的内存使用达到limit限制,之后就会被OOM.所以考虑更换为G1(Garbage-First)垃圾回收器,因为G1在处理大堆内存和低延迟需求方面表现更佳,但是在六版本中,更改env并不生效,所以采用cm挂载subPath的方案实现修改回收策略

apiVersion: v1
data:
  jvm.options: |-
    ## JVM configuration

    ################################################################
    ## IMPORTANT: JVM heap size
    ################################################################
    ##
    ## You should always set the min and max JVM heap
    ## size to the same value. For example, to set
    ## the heap to 4 GB, set:
    ##
    ## -Xms4g
    ## -Xmx4g
    ##
    ## See https://www.elastic.co/guide/en/elasticsearch/reference/current/heap-size.html
    ## for more information
    ##
    ################################################################

    # Xms represents the initial size of total heap space
    # Xmx represents the maximum size of total heap space

    -Xms8g
    -Xmx8g

    ################################################################
    ## Expert settings
    ################################################################
    ##
    ## All settings below this section are considered
    ## expert settings. Don't tamper with them unless
    ## you understand what you are doing
    ##
    ################################################################

    ## GC configuration
    #-XX:+UseConcMarkSweepGC
    #-XX:CMSInitiatingOccupancyFraction=75
    #-XX:+UseCMSInitiatingOccupancyOnly
    -XX:+UseG1GC
    -XX:MaxGCPauseMillis=200

    ## optimizations

    # pre-touch memory pages used by the JVM during initialization
    -XX:+AlwaysPreTouch

    ## basic

    # explicitly set the stack size
    -Xss1m

    # set to headless, just in case
    -Djava.awt.headless=true

    # ensure UTF-8 encoding by default (e.g. filenames)
    -Dfile.encoding=UTF-8

    # use our provided JNA always versus the system one
    -Djna.nosys=true

    # turn off a JDK optimization that throws away stack traces for common
    # exceptions because stack traces are important for debugging
    -XX:-OmitStackTraceInFastThrow

    # flags to configure Netty
    -Dio.netty.noUnsafe=true
    -Dio.netty.noKeySetOptimization=true
    -Dio.netty.recycler.maxCapacityPerThread=0

    # log4j 2
    -Dlog4j.shutdownHookEnabled=false
    -Dlog4j2.disable.jmx=true

    -Djava.io.tmpdir=${ES_TMPDIR}

    ## heap dumps

    # generate a heap dump when an allocation from the Java heap fails
    # heap dumps are created in the working directory of the JVM
    -XX:+HeapDumpOnOutOfMemoryError

    # specify an alternative path for heap dumps; ensure the directory exists and
    # has sufficient space
    -XX:HeapDumpPath=data

    # specify an alternative path for JVM fatal error logs
    -XX:ErrorFile=logs/hs_err_pid%p.log

    ## JDK 8 GC logging

    8:-XX:+PrintGCDetails
    8:-XX:+PrintGCDateStamps
    8:-XX:+PrintTenuringDistribution
    8:-XX:+PrintGCApplicationStoppedTime
    8:-Xloggc:logs/gc.log
    8:-XX:+UseGCLogFileRotation
    8:-XX:NumberOfGCLogFiles=32
    8:-XX:GCLogFileSize=64m

    # JDK 9+ GC logging
    9-:-Xlog:gc*,gc+age=trace,safepoint:file=logs/gc.log:utctime,pid,tags:filecount=32,filesize=64m
    # due to internationalization enhancements in JDK 9 Elasticsearch need to set the provider to COMPAT otherwise
    # time/date parsing will break in an incompatible way for some date patterns and locals
    9-:-Djava.locale.providers=COMPAT

    # temporary workaround for C2 bug with JDK 10 on hardware with AVX-512
    10-:-XX:UseAVX=2
kind: ConfigMap
metadata:
  name: es-pss-jvm

停止svc流量


kubectl patch -n test svc elasticsearch-pss -p '{"spec":{"selector": {"app": "elasticsearch-pss-nonexistent-label", "release": "elasticsearch-pss-nonexistent-label"}}}'

kubectl patch -n pro svc elasticsearch-pss -p '{"spec":{"selector": {"app": "elasticsearch-pss-nonexistent-label", "release": "elasticsearch-pss-nonexistent-label"}}}'

挂载

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: elasticsearch-pss
    chart: elasticsearch-pss-1.16.3
    heritage: Tiller
    io.cattle.field/appId: elasticsearch-pss
    release: elasticsearch-pss
    velero.io/backup-name: test-backup
    velero.io/restore-name: test-backup-20230210174913
  name: elasticsearch-pss
  namespace: test
spec:
  progressDeadlineSeconds: 6000
  replicas: 1
  revisionHistoryLimit: 10
  selector:
    matchLabels:
      app: elasticsearch-pss
      release: elasticsearch-pss
  strategy:
    type: Recreate
  template:
    metadata:
      labels:
        app: elasticsearch-pss
        release: elasticsearch-pss
    spec:
      containers:
      - env:
        - name: ES_JAVA_OPTS
          value: -Xms4g -Xmx4g -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/tmp/elasticsearch-pss/elasticsearch-pss.hprof
        image: registry-hz.rubikstack.com/lichen/elasticsearch-ik-pinyin:6.4.3
        imagePullPolicy: IfNotPresent
        name: elasticsearch-pss
        ports:
        - containerPort: 9200
          name: http
          protocol: TCP
        - containerPort: 9300
          name: transport
          protocol: TCP
        resources:
          limits:
            memory: 8Gi
          requests:
            memory: 2Gi
        terminationMessagePath: /test/termination-log
        terminationMessagePolicy: File
        volumeMounts:
        - mountPath: /usr/share/elasticsearch/config/jvm.options
          name: es-jvm
          subPath: jvm.options
        - mountPath: /usr/share/elasticsearch/data
          name: data
        - mountPath: /tmp/elasticsearch-pss
          name: vol1
      dnsPolicy: ClusterFirst
      initContainers:
      - command:
        - sysctl
        - -w
        - vm.max_map_count=262144
        image: registry-hz.rubikstack.com/library/job:1115
        imagePullPolicy: IfNotPresent
        name: configure-sysctl
        resources: {}
        securityContext:
          privileged: true
          runAsUser: 0
        terminationMessagePath: /dev/termination-log
        terminationMessagePolicy: File
      restartPolicy: Always
      schedulerName: default-scheduler
      securityContext:
        fsGroup: 1000
      terminationGracePeriodSeconds: 30
      volumes:
      - configMap:
          defaultMode: 420
          name: es-pss-jvm
        name: es-jvm
      - name: data
        persistentVolumeClaim:
          claimName: elasticsearch-pss
      - hostPath:
          path: /tmp/elasticsearch-pss
          type: ""
        name: vol1

恢复流量


kubectl patch -n test svc elasticsearch-pss  -p '{"spec":{"selector": {"app": "elasticsearch-pss", "release": "elasticsearch-pss"}}}'

kubectl patch -n pro svc elasticsearch-pss  -p '{"spec":{"selector": {"app": "elasticsearch-pss", "release": "elasticsearch-pss"}}}'

2.Secret

secret的意义

Secret 解决了密码、token、密钥等敏感数据的配置问题,而不需要把这些敏感数据暴露到镜像或者 Pod Spec 中。Secret 可以以 Volume 或者环境变量的方式使用

Secret 有三种类型:

  • Service Account :用来访问 Kubernetes API,由 Kubernetes 自动创建,并且会自动挂载到 Pod 的 /run/secrets/kubernetes.io/serviceaccount 目录中
  • Opaque :base64 编码格式的 Secret,用来存储密码、密钥等
  • kubernetes.io/dockerconfigjson :用来存储私有 docker 仓库的认证信息

Service Account

Service Account 用来访问 Kubernetes API,由 Kubernetes 自动创建,并且会自动挂载到 Pod的 /run/secrets/kubernetes.io/serviceaccount 目录中

$ kubectl run nginx --image nginx
deployment "nginx" created
$ kubectl get pods
NAME                     READY     STATUS    RESTARTS   AGE
nginx-3137573019-md1u2   1/1       Running   0          13s
$ kubectl exec nginx-3137573019-md1u2 ls /run/secrets/kubernetes.io/serviceaccount
ca.crt
namespace
token

Opaque Secret

Ⅰ、创建说明

Opaque 类型的数据是一个 map 类型,要求 value 是 base64 编码格式:(防君子不防小人)

$ echo -n "admin" | base64
YWRtaW4=
$ echo -n "1f2d1e2e67df" | base64
MWYyZDFlMmU2N2Rm

secrets.yml

key不需要加密,但是values必须要加密
放在资源清单里,他是自动给你解密的,没有加密的话原文直接乱码

apiVersion: v1
kind: Secret
metadata:
  name: mysecret
type: Opaque
data:
  password: MWYyZDFlMmU2N2Rm
  username: YWRtaW4=
Ⅱ、使用方式

1、将 Secret 挂载到 Volume 中

apiVersion: v1
kind: Pod
metadata:
  labels:
    name: seret-test
  name: seret-test
spec:
  volumes:
  - name: volumes12
    secret:
      secretName: mysecret
  containers:
  - image: wangyanglinux/myapp:v1
    name: db
    volumeMounts:
    - name: volumes12
      mountPath: "/data"

2、将 Secret 导出到环境变量中

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: pod-deployment
spec:
  replicas: 2
  template:
    metadata:
      labels:
        app: pod-deployment
    spec:
      containers:
      - name: pod-1
        image: wangyanglinux/myapp:v1
        ports:
        - containerPort: 80
        env:
        - name: TEST_USER
          valueFrom:
            secretKeyRef:
              name: mysecret
              key: username
        - name: TEST_PASSWORD
          valueFrom:
            secretKeyRef:
              name: mysecret
              key: password

用起来和configmap差不多,如果用在数据库中的MYSQL_ROOT_PASSWD中就不是明文显示了
防君子不防小人,想要安全点就用secret

kubernetes.io/dockerconfigjson

使用 Kuberctl 创建 docker 仓库认证的 secret

# #创建secret,类型是docker-registry,名字是myregistrykey,指定docker服务器的地址,用户名,密码,邮箱
$ kubectl create secret docker-registry myregistrykey --docker-server=DOCKER_REGISTRY_SERVER --docker-username=DOCKER_USER --docker-password=DOCKER_PASSWORD --docker-email=DOCKER_EMAIL
secret "myregistrykey" created.

在创建 Pod 的时候,通过 imagePullSecrets 来引用刚创建的 myregistrykey

apiVersion: v1
kind: Pod
metadata:
  name: foo
spec:
  containers:
    - name: foo
      image: hub.hongfu.com/wangyang/myapp:v1
  imagePullSecrets:
    - name: myregistrykey

3.volume

容器磁盘上的文件的生命周期是短暂的,这就使得在容器中运行重要应用时会出现一些问题。首先,当容器崩溃时,kubelet 会重启它,但是容器中的文件将丢失——容器以干净的状态(镜像最初的状态)重新启动。其次,在 Pod 中同时运行多个容器时,这些容器之间通常需要共享文件。Kubernetes 中的 Volume 抽象就很好的解决了这些问题

背景

  • Kubernetes 中的卷有明确的寿命 —— 与封装它的 Pod 相同。所以,卷的生命比 Pod 中的所有容器都长,当这个容器重启时数据仍然得以保存。当然,当 Pod 不再存在时,卷也将不复存在。也许更重要的是,Kubernetes 支持多种类型的卷,Pod 可以同时使用任意数量的卷

卷的类型

Kubernetes 支持以下类型的卷:

  • awsElasticBlockStore azureDisk azureFile cephfs csi downwardAPI emptyDir
  • fc flocker gcePersistentDisk gitRepo glusterfs hostPath iscsi local nfs
  • persistentVolumeClaim projected portworxVolume quobyte rbd scaleIO secret
  • storageos vsphereVolume

emptyDir

当 Pod 被分配给节点时,首先创建 emptyDir 卷,并且只要该 Pod 在该节点上运行,该卷就会存在。正如卷的名字所述,它最初是空的。Pod 中的容器可以读取和写入 emptyDir 卷中的相同文件,尽管该卷可以挂载到每个容器中的相同或不同路径上。当出于任何原因从节点中删除 Pod 时,emptyDir 中的数据将被永久删除

注意:容器崩溃不会从节点中移除 pod,因此 emptyDir 卷中的数据在容器崩溃时是安全的

emptyDir 的用法有:
  • 共享文件:当需要在同一个Pod中的多个容器之间读写相同的文件时,emptyDir可以作为这些容器之间共享数据的临时存储
  • 缓存数据:某些应用程序需要缓存数据以提高性能。emptyDir可以用作缓存目录,多个容器可以在其中读取和写入缓存数据。
  • 临时计算:某些任务需要在同一个Pod中进行多个容器之间的中间数据计算。emptyDir提供了一个临时的、快速的存储解决方案。
  • 多容器通信:在一个Pod中,如果有一个容器产生临时数据,而另一个容器需要对数据进行分析并储存,也可以使用emptyDir来实现数据的共享和传递。
  • 容器间的数据共享:在一个Pod中部署两个容器,然后声明一个Volume,分别挂到两个容器的目录中,这样,两个容器就能读写文件,实现数据共享。
  • 动态更新配置:emptyDir可以与initContainer结合使用,实现ConfigMap的动态更新,从而在Pod启动时提供最新的配置文件
emptyDir的优缺点

优点

  • 数据共享:emptyDir允许同一个Pod内的多个容器共享数据,这些容器可以将数据写入或从中读取,即使它们将数据卷挂载到不同的路径。
  • 临时存储:emptyDir适用于需要临时存储的场景,如缓存数据或处理中间文件,这些数据在Pod被删除时不会被保留。
  • 故障安全性:如果Pod中的某个容器失败并重启,emptyDir中的数据不会丢失,因为Pod本身没有被删除。

缺点

  • 非持久化存储:emptyDir的数据在Pod被删除或重新调度时会被永久清除,不适用于需要长期存储的数据。
  • 跨Pod数据隔离:不同Pod之间无法共享emptyDir中的数据,每个Pod的emptyDir是独立的,这限制了数据的复用和共享范围。
  • 节点依赖性:emptyDir的数据存储在Pod所在节点的本地存储上,如果节点发生故障,数据可能会丢失,且在节点维护或更换时需要额外的数据迁移步骤。
apiVersion: batch/v1
kind: Job
metadata:
  name: jobs-empty
spec:
  template:
    spec:
      restartPolicy: Never
      initContainers:
        - name: job-1
          image: busybox:1.34.1
          command:
            - 'sh'
            - '-c'
            - >
              for i in 1 2 3;
              do
                echo "job-1 `date`";
                sleep 1s;
              done;
              echo job-1 GG > /srv/input/code
          volumeMounts:
            - mountPath: /srv/input/
              name: input
        - name: job-2
          image: busybox:1.34.1
          command:
            - 'sh'
            - '-c'
            - >
              for i in 1 2 3;
              do
                echo "job-2 `date`";
                sleep 1s;
              done;
              cat /srv/input/code &&
              echo job-2 GG  > /srv/input/output/file
          resources:
            requests:
              cpu: 3
          volumeMounts:
            - mountPath: /srv/input/
              name: input
            - mountPath: /srv/input/output/
              name: output
      containers:
        - name: job-3
          image: busybox:1.34.1
          command:
            - 'sh'
            - '-c'
            - >
              echo "job-1 and job-2 completed";
              sleep 3s;
              cat /srv/output/file
          volumeMounts:
            - mountPath: /srv/output/
              name: output
      volumes:
        - name: input
          emptyDir: {}
        - name: output
          emptyDir: {}

hostPath

hostPath 卷将主机节点的文件系统中的文件或目录挂载到集群中

hostPath 的用途如下:

  • 运行需要访问 Docker 内部的容器;使用 /var/lib/dockerhostPath
  • 在容器中运行 cAdvisor;使用 /dev/cgroupshostPath
  • 允许 pod 指定给定的 hostPath 是否应该在 pod 运行之前存在,是否应该创建,以及它应该以什么形式存在

除了所需的 path 属性之外,用户还可以为 hostPath 卷指定 type


行为
空字符串(默认)用于向后兼容,这意味着在挂载 hostPath 卷之前不会执行任何检查。
DirectoryOrCreate 如果在给定的路径上没有任何东西存在,那么将根据需要在那里创建一个空目录,权限设置为 0755,与 Kubelet 具有相同的组和所有权。
Directory 给定的路径下必须存在目录
FileOrCreate 如果在给定的路径上没有任何东西存在,那么会根据需要创建一个空文件,权限设置为 0644,与 Kubelet 具有相同的组和所有权。
File 给定的路径下必须存在文件
Socket 给定的路径下必须存在 UNIX 套接字
CharDevice 给定的路径下必须存在字符设备
BlockDevice 给定的路径下必须存在块设备

使用这种卷类型是请注意,因为:

  • 由于每个节点上的文件都不同,具有相同配置(例如从 podTemplate 创建的)的 pod 在不同节点上的行为可能会有所不同
  • 当 Kubernetes 按照计划添加资源感知调度时,将无法考虑 hostPath 使用的资源
  • 在底层主机上创建的文件或目录只能由 root 写入。您需要在特权容器中以 root 身份运行进程,或修改主机上的文件权限以便写入 hostPath
apiVersion: v1
kind: Pod
metadata:
  name: test-pd
spec:
  containers:
  - image: wangyanglinux/myapp:v1
    name: test-container
    volumeMounts:
    - mountPath: /test-pd
      name: test-volume
  volumes:
  - name: test-volume
    hostPath:
      # directory location on host
      path: /data
      # this field is optional
      type: Directory

hostpath的优缺点

优点

  • 直接使用宿主机资源:HostPath卷可以利用宿主机上已有的文件系统空间,这对于资源受限的环境或需要快速部署的场景非常有用。
  • 简化配置:相比于需要额外配置的网络存储解决方案,HostPath卷的设置相对简单,不需要额外的网络配置或存储集群管理。
  • 成本效益:由于不涉及额外的存储硬件或服务费用,HostPath卷可以降低成本。
  • 读取速度: 相比于网络存储来说,直接在宿主机目录上读写,速度会快很多

缺点

  • 可移植性差:如果Pod被调度到不同的宿主机上,之前通过HostPath卷挂载的数据将不可用,除非采取额外措施进行数据迁移。
  • 安全风险:允许容器直接访问宿主机文件系统可能带来安全隐患,特别是如果容器进程具有较高的权限时。
  • 单点故障:如果宿主机出现故障,所有依赖HostPath卷的应用程序都会受到影响,数据完整性和可用性得不到保障。
  • 资源隔离性差:不同Pod之间通过HostPath卷共享宿主机资源可能导致资源争用和隔离性问题。

4.PV,PVC

概念

PersistentVolume(PV)

是由管理员设置的存储,它是群集的一部分。就像节点是集群中的资源一样,PV 也是集群中的资源。 PV 是 Volume 之类的卷插件,但具有独立于使用 PV 的 Pod 的生命周期。此 API 对象包含存储实现的细节,即 NFS、iSCSI 或特定于云供应商的存储系统

PV是实际上链接后端的存储,是一个抽象的,PV会直接记录后端的容量,读写策略,
比如iscsi就不支持多节点读写,因为他没有跨集群的分布式文件系统,这就是需要分布式文件锁的含义。
红帽RHCS就开发了GFS2支持分布式文件锁,但是需要很多支持
可以分类,比如abc类,a的高可用可空间都很好,可以分为a,但是这都是自己公司集群内部分类,不是k8s官方的
如果二类匹配不到,他是不会去匹配1和3的,只会阻塞pending
创建pod的时候写好清单,指定需要多大容量的,使用什么策略,要哪类的,然后pod就会和PVC绑定,之后PVC会自动根据需要去选择最优解PV
如果直接绑定是需要地址,命令什么的,这样只需要写出想要什么就好了

PersistentVolumeClaim(PVC)

是用户存储的请求。它与 Pod 相似。Pod 消耗节点资源,PVC 消耗 PV 资源。Pod 可以请求特定级别的资源(CPU 和内存)。声明可以请求特定的大小和访问模式(例如,可以以读/写一次或 只读多次模式挂载)

静态 pv

集群管理员创建一些 PV。它们带有可供群集用户使用的实际存储的细节。它们存在于 Kubernetes API 中,可用于消费
先存在,后绑定,提前就创建好的
代价低

动态storageclass

当管理员创建的静态 PV 都不匹配用户的 PersistentVolumeClaim 时,集群可能会尝试动态地为 PVC 创建卷。此配置基于 StorageClasses:PVC 必须请求 [存储类],并且管理员必须创建并配置该类才能进行动态创建。声明该类为 "" 可以有效地禁用其动态配置

要启用基于存储级别的动态存储配置,集群管理员需要启用 API server 上的 DefaultStorageClass [准入控制器] 。例如,通过确保 DefaultStorageClass 位于 API server 组件的 --admission-control 标志,使用逗号分隔的有序值列表中,可以完成此操作

根据需要创建出来的PV
更方便,减少不必要浪费

绑定

master 中的控制环路监视新的 PVC,寻找匹配的 PV(如果可能),并将它们绑定在一起。如果为新的 PVC 动态调配 PV,则该环路将始终将该 PV 绑定到 PVC。否则,用户总会得到他们所请求的存储,但是容量可能超出要求的数量。一旦 PV 和 PVC 绑定后,PersistentVolumeClaim 绑定是排他性的,不管它们是如何绑定的。 PVC 跟 PV 绑定是一对一的映射

持久化卷声明的保护

PVC 保护的目的是确保由 pod 正在使用的 PVC 不会从系统中移除,因为如果被移除的话可能会导致数据丢失

也就是pod存在的时候,PVC不允许被删除

注意:当 pod 状态为 Pending 并且 pod 已经分配给节点或 pod 为 Running 状态时,PVC 处于活动状态

当启用PVC 保护 alpha 功能时,如果用户删除了一个 pod 正在使用的 PVC,则该 PVC 不会被立即删除。PVC 的删除将被推迟,直到 PVC 不再被任何 pod 使用

持久化卷类型

PersistentVolume 类型以插件形式实现。Kubernetes 目前支持以下插件类型:

  • 支持被绑定成PV的类型
  • GCEPersistentDisk: **AWSElasticBlockStore** AzureFile AzureDisk FC (Fibre Channel)
  • FlexVolume: Flocker NFS iSCSI RBD (Ceph Block Device) CephFS
  • Cinder (OpenStack block storage): Glusterfs VsphereVolume Quobyte Volumes
  • HostPath: VMware Photon Portworx Volumes ScaleIO Volumes StorageOS

持久卷演示代码

apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv0003
spec:
  capacity:            #容量
    storage: 5Gi        #存储5个G
  volumeMode: Filesystem    #卷的模式,文件系统类型,这个是固定的,比如iscsi就是block
  accessModes:            #访问策略
    - ReadWriteOnce        #单节点读写
  persistentVolumeReclaimPolicy: Recycle    #回收策略,rm  -rf  *,全删了
  storageClassName: slow        #存储类的名称,叫啥都行,公司内自己定义的,存储工程师对类划分,应用工程师使用类就行
  mountOptions:        #挂在选项
    - hard               #hard类型
    - nfsvers=4.1        #nfs4.1的版本,不是所有的都需要设置
  nfs:                    #基于nfs提供的
    path: /tmp            #nfs挂载路径是/tmp
    server: 172.17.0.2    #服务器地址

PV 访问模式

PersistentVolume 可以以资源提供者支持的任何方式挂载到主机上。如下表所示,供应商具有不同的功能,每个 PV 的访问模式都将被设置为该卷支持的特定模式。例如,NFS 可以支持多个读/写客户端,但特定的 NFS PV 可能以只读方式导出到服务器上。每个 PV 都有一套自己的用来描述特定功能的访问模式

  • ReadWriteOnce——该卷可以被单个节点以读/写模式挂载
  • ReadOnlyMany——该卷可以被多个节点以只读模式挂载
  • ReadWriteMany——该卷可以被多个节点以读/写模式挂载

在命令行中,访问模式缩写为:

  • RWO - ReadWriteOnce
  • ROX - ReadOnlyMany
  • RWX - ReadWriteMany

一个卷一次只能使用一种访问模式挂载,即使它支持很多访问模式。例如,GCEPersistentDisk 可以由单个节点作为 ReadWriteOnce 模式挂载,或由多个节点以 ReadOnlyMany 模式挂载,但不能同时挂载

Volume 插件 ReadWriteOnce ReadOnlyMany ReadWriteMany
AWSElasticBlockStoreAWSElasticBlockStore - -
AzureFile
AzureDisk - -
CephFS
Cinder - -
FC -
FlexVolume -
Flocker - -
GCEPersistentDisk -
Glusterfs
HostPath - -
iSCSI -
PhotonPersistentDisk - -
Quobyte
NFS
RBD -
VsphereVolume - - (当 pod 并列时有效)
PortworxVolume -
ScaleIO -
StorageOS - -

回收策略

  • Retain(保留)——手动回收(可以等管理员进去,该保存的保存,该删除的删除,然后再释放给别人用.建议使用)
  • Recycle(回收)——基本擦除(rm -rf /thevolume/*
  • Delete(删除)——关联的存储资产(例如 AWS EBS、GCE PD、Azure Disk 和 OpenStack Cinder 卷)将被删除(- 就是直接连PV都删了,这一般是云供应商做的)

当前,只有 NFS 和 HostPath 支持回收策略。AWS EBS、GCE PD、Azure Disk 和 Cinder 卷支持删除策略

状态

卷可以处于以下的某种状态:

  • Available(可用)——一块空闲资源还没有被任何声明绑定
  • Bound(已绑定)——卷已经被声明绑定
  • Released(已释放)——声明被删除,但是资源还未被集群重新声明
  • Failed(失败)——该卷的自动回收失败

命令行会显示绑定到 PV 的 PVC 的名称

持久化演示说明 - NFS

Ⅰ、安装 NFS 服务器
yum install -y nfs-common nfs-utils  rpcbind
mkdir /nfsdata
chmod 666 /nfsdata
chown nfsnobody /nfsdata
cat /etc/exports
    /nfsdata *(rw,no_root_squash,no_all_squash,sync)
  systemctl start rpcbind
systemctl start nfs
Ⅱ、部署 PV
apiVersion: v1
kind: PersistentVolume
metadata:
  name: nfspv1
spec:
  capacity:
    storage: 1Gi
  accessModes:
    - ReadWriteOnce
  persistentVolumeReclaimPolicy: Recycle
  storageClassName: nfs
  nfs:
    path: /data/nfs
    server: 10.66.66.10
Ⅲ、创建服务并使用 PVC
# headless
apiVersion: v1
kind: Service
metadata:
  name: nginx
  labels:
    app: nginx
spec:
  ports:
  - port: 80
    name: web
  clusterIP: None
  selector:
    app: nginx
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: web
spec:
  selector:
    matchLabels:
      app: nginx
  # 写的是无头服务的名字,并且是必需字段,也就是说statefulSet必须匹配无头服务
  serviceName: "nginx"
  replicas: 3
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: wangyanglinux/myapp:v1
        ports:
        - containerPort: 80
          name: web
        volumeMounts:
        - name: www
          mountPath: /usr/share/nginx/html
  volumeClaimTemplates:
  - metadata:
      name: www
    spec:
      accessModes: [ "ReadWriteOnce" ]
      storageClassName: "nfs"
      resources:
        requests:
          storage: 1Gi  

可以看见statefulset是个有序的线性过程,其他的控制器都是并发的,而且stateful的名字是固定的,很规整
既然pod的名字是固定的,无头服务又会给每个pod一个地址,结合起来pod的地址就固定了

关于 StatefulSet

  • 匹配 Pod name ( 网络标识 ) 的模式为:$(statefulset名称)-$(序号),比如上面的示例:web-0,web-1,web-2
  • StatefulSet 为每个 Pod 副本创建了一个 DNS 域名,这个域名的格式为: $(podname).(headless server name),也就意味着服务间是通过Pod域名来通信而非 Pod IP,因为当Pod所在Node发生故障时, Pod 会被飘移到其它 Node 上,Pod IP 会发生变化,但是 Pod 域名不会有变化
  • StatefulSet 使用 Headless 服务来控制 Pod 的域名,这个域名的 FQDN 为:$(service name).$(namespace).svc.cluster.local,其中,“cluster.local” 指的是集群的域名
  • 根据 volumeClaimTemplates,为每个 Pod 创建一个 pvc,pvc 的命名规则匹配模式:(volumeClaimTemplates.name)-(pod_name),比如上面的 volumeMounts.name=www, Pod name=web-[0-2],因此创建出来的 PVC 是 www-web-0、www-web-1、www-web-2
  • 删除 Pod 不会删除其 pvc,手动删除 pvc 将自动释放 pv

Statefulset的启停顺序:

  • 有序部署:部署StatefulSet时,如果有多个Pod副本,它们会被顺序地创建(从0到N-1)并且,在下一个Pod运行之前所有之前的Pod必须都是Running和Ready状态。

  • 有序删除:当Pod被删除时,它们被终止的顺序是从N-1到0。

  • 有序扩展:当对Pod执行扩展操作时,与部署一样,它前面的Pod必须都处于Running和Ready状态。 

StatefulSet使用场景:

  • 稳定的持久化存储,即Pod重新调度后还是能访问到相同的持久化数据,基于 PVC 来实现。
  • 稳定的网络标识符,即 Pod 重新调度后其 PodName 和 HostName 不变。
  • 有序部署,有序扩展,基于 init containers 来实现。
  • 有序收缩。
男孩子都是香香软软的小猪
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇