zoukankan      html  css  js  c++  java
  • k8s 挂载卷介绍(四)

    kubernetes关于pod挂载卷的知识
    首先要知道卷是pod资源的属性,pv,pvc是单独的资源。pod 资源的volumes属性有多种type,其中就包含有挂载pvc的类型。这也帮我理清了之间的关系。
    pv一般是系统管理员做的
    pvc 是一般k8s用户声明的,大概意思就是说我需要一个 什么权限的,多少存储空间的卷,然后k8s api收到这个请求就去找pv资源对象,如果两者相匹配,那么pv就和pvc绑定了。
    从这里我们也想到了,pv如果是手动创建的话,那就麻烦大了。几个,几十个还好说,上万个,管理员该如何创建这么多。所以才有了动态创建pv的需求。这就引出另外一个资源 storageClass ,这个资源声明后端挂载什么样的存储服务,比如nfs,chef等,有了这个一般用户在定义pvc的时候,在提出存储空间和读写权限的同时声明用那个storageClass了,
    如下:

    apiVersion: v1
    kind: PersistentVolumeClaim
    metadata: mysql-pvc
    spec:
      accessModes:
          - ReadWriteMany
      storageClassName: managed-nfs-storage  (注意这里)
      resources:
         requests:
            storage: 5Gi
    以上便是pvc使用storageClass资源对象创建pv,pvc的绑定了
    api收到对storageClass声明后,就会调用这个storageClass的生产者进行生产。
    我们就会想到,在创建storageClass资源对象的时候肯定会指定生产者。
    kind: StorageClass
    apiVersion: storage.k8s.io/v1
    metadata:
      name: example-nfs
    provisioner: example.com/nfs  这里指定生产者,其中example.com/nfs只是一个标签,你在deploymend里定义什么这里就写什么。
    mountOptions:
      - vers=4.1
    那么问题又来了。在k8s中生产者组件不支持nfs,所以才会安装第三方组件。第三方组件的安装就需要创建相关的rbac的角色和账户。第三方组件是用deploymend的资源托管相关组件pod的。
    那么通过deploy部署的pod怎么就是provisioner了?这个我不清楚,后面学习后在总结吧。
     
     
    回到正题上。
    volumes 的6种
    1. emptyDir
    2. gitRepo
    3. hostpath
    4.PersistentVolumeClaim
    5.configMap,secret
    6. 各种云平台的存储磁盘卷如google的gce,aws的ebs,azure的azureDisk
    其实4只是一个概括,nfs,chef 这些网络存储通通可以单独来使用。但我觉得实际使用中还是讲这些网络存储转化成pv,pvc
     
    从简单开始学习
    emptyDir 两种应用场景: 1. 同一个pod中,各个容器间共享文件。 2. 当程序对大数据处理时内存空间不够时临时写入文件(当然也可以使用宿主主机的内存)
    例子:
    apiVersion: v1
    kind: Pod
    metadata:
        name: fortune
    spec:
        containers:
        -   image: luksa/fortune
             name: html-generator
             volumeMounts:
             -   name: html
                 mountPath: /var/htdocs
        -   image: nginx:alpine
             name: web-server
             volumeMounts:
             -   name: html
                  mountPaht: /usr/share/nginx/html
                  readOnly: true
             ports:
             -   containerPort: 80
                  protocol: TCP
          volumes:
          -  name: html
              emptyDir: {}     (为{}表示使用节点服务器的文件系统)
          -  name: html-2
              emptyDir:
                   medium: Memory   (使用节点服务器的内存)
    以上就是emptyDir的用法。gitRepo其实是依赖于emptyDir的,你可以把它理解成,声明了一个emptyDir然后在把gitrepo下载填充到emptyDir
    例子:
    apiVersion: v1
    kind: Pod
    metadata:
        name: gitrepo-volume-pod
    spec:
        containers:
        -   image: nginx: alpine
             name: web-nginx
             volumeMounts:
             -   name: html
                  mountPath: /usr/share/nginx/html
                  readOnly: true
              ports:
              -   containerPort: 80
                   protocol: TCP
         volumes:
         -   name: html
              gitRepo:
                  repository: https://github.com/luksa/kubia-website-example.git
                  revision: master
                  directory: .   (这个.很重要,表示在当前emptyDir目录下不然就会创建一个kubia-website-example目录)
    以上就是gitRepo的用法,其中有个问题就是pod中的gitrepo并不会及时更新。如果想要及时更新需要用到sidecar ,加入到pod中,在Docker Hub搜索 “git ryc”获取相关镜像。
    还有一个情况不得不使用sidecar container 。gitrepo 不支持连接私有库,也就是不能是ssh密钥连接,也不可以有用户名和密码。这时候就需要使用支持验证连接的sidecar container来实现了。具体怎么使用,用到的时候在研究吧.
    至此gitRepo卷类型算是简单介绍了,下面学习hostpath
     
    大多数时候pod应该忽略他们的主机节点,因此他们也不需要访问节点文件系统上的文件。但是某些系统级别的pod(通常由DaemonSet管理)确实需要读取节点的文件或使用节点文件系统来访问节设备,这时候就需要用到hostPath
    hostPath 算是第一个持久化存储卷了,但是这个很少用到,因为这个卷只能在某一单一节点上,pod重启后很可能在另外一个节点上。当然可以使用NodeSelector但这样看起来也不高明。所以建议使用网络存储。hostPath过
     
    接下来是网络存储和云平台提供的存储磁盘卷。这两种在用到的时候找相关的属性进行配置即可。也没什么要注意的,实际应用场景用到最多的持久存储是pv,pvc,storageClass
     
     
    configmap
    kubectl create configmap fortune-config --from-literal=sleep-interval=25
     
    一般configmap包含多个映射条目所以创建时可以多次使用--from-literal参数
    kubectl create conigmap fortune-config --from-literal=foo=bar --from-literal=bar=baz --from-literal=one=two
     
    kubectl get configmap fortune-config -o yaml
     
    configmap同样可以存储粗力度的配置数据,比如完整的配置文件。kubectl create configmap 命令支持从硬盘上读取文件,并将文件内容单独存储为ConfigMap中的条目:
    kubectl create configmap my-config --from-file=config-file.conf
    运行此命令,kubectl 会在当前目录下找config-file.conf文件,并将文件内容存储在configmap中以config-file.conf 为键名的条目下。当然也可以手动指定键名
    kubectl create configmap my-config --from-file=customkey=config-file.conf
     
    kubectl create configmap my-config --from-file=/path/to/dir
    这时候,kubectl会为文件中每个文件单独创建条目,仅限文件名可作为合法ConfigMap键名的文件。
     
    当然也可以将上面的参数混合使用
     
    configmap设置完成了,如何将映射的值传递给pod的容器?三种方法
    一,设置环境变量,例子如下:
    apiVersion: v1
    kind: Pod
    metadata:
        name: fortune-env-from-configmap
    spec:
        containers:
        -   image: luksa/fortune:env
            env:
            -   name: INTERVAL
                 valueFrom:
                     configMapKeyRef:
                         name: fortune-config
                         key: sleep-interval
    很好奇当pod 容器中引用不存在的configmap会发生什么?
    pod会尝试运行所有容器,只有这个容器启动失败,其他正常(假如有多个容器),当你事后创建处这个configmap,无需重启pod,容器就会成功。当然也可以引用为可选的,设置configMapKeyRef.optional: true即可,这样即使ConfigMap不存在,容器也能正常启动。
     
    如果configmap中条目很多,用env属性列出麻烦且容易出错。那么有没有办法一次导入呢。用envFrom, 例如configmap中有三个条目FOO、BAR和FOO-BAR
    spec:
        containers:
        -   image: some-image
             envFrom:
             -   prefix: CONFIG_  所有环境变量均包含前缀CONFIG_  ,不设置就将引用configmap中的键名
                  configMapRef:
                      name: my-config-map
    如此,容器中多出两个变量 CONFIG_FOO 、CONFIG_BAR
    为什么是两个,因为另外一个FOO-BAR包含破折号,不是一个合法的环境变量名称。被忽略了,所以我们应该注意创建configmap时 不要使用-
     
    上面我们学习了如何将configmap中的条目以变量的形式传入到容器中
    那么如何将configmap中的条目作为容器运行的参数args呢?例子:
    apiVersion: v1
    kind: Pod
    metadata:
        name: fortune-env-from-configmap
    spec:
        containers:
        -   image: luksa/fortune:env
            env:
            -   name: INTERVAL
                 valueFrom:
                     configMapKeyRef:
                         name: fortune-config
                         key: sleep-interval
            args: ["$(INTERVAL)"]
    环境变量和参数都适合于变量值较短的场景。configmap是可以包含完整配置文件内容的,当你想要将其暴露给容器时,可以使用上一章中提到的卷的类型。configmap卷会将ConfigMap中的每个条目均暴露成一个文件。运行在容器的进程通过读取文件内容获得对应的条目值。
    configmap-files为一个目录
    kubectl create configmap fortune-config --from-file=configmap-files
    例子:
    apiVersion: v1
    kind: Pod
    metadata:
        name: fortune-configmap-volume
    spec:
        containers:
        - image: nginx:alpine
           name: web-server
           volumeMounts:
           ...
           - name: config
              mountPaht: /etc/nginx/config.d
              readOnly: true
           ....
       volumes:
       ...
       - name: config
          configMap:
               name: fortune-config
       ...
    一种特殊情况,当你只想暴露configmap-files目录下的某一个配置文件,该如何做:
    volumes:
    -   name: config
         configmap:
             name: fortune-config
             items:
                 - key: my-nginx-config.conf
                    path: gzip.conf
    这样配置后,挂载fortune-config 卷后,就只有my-nginx-config.conf 并且挂载后的名称为gzip.conf
     
    另一种特殊情况,我们通过上述挂载configmap卷后会发现,被挂载的目录之前的文件都被隐藏掉了。那么如果你需求不想隐藏之前的文件,该如何做:
    spec:
        containers:
        -   image: some/image
             volumeMounts:
             -   name: myvolume
                  mountPath: /etc/someconfig.conf 挂载到指定的某一个文件,而不是某个文件夹
                  subPath: myconfig.conf 挂载指定的条目,而不是完整的卷
    为configMap卷中的文件设置权限
    volumes:
    -   name: config
        configmap:
            name: fortune-config
            defaultMode: "6600"
    更新应用配置且不重启应用程序
    使用环境变量或命令行参数作为配置源的弊端在于无法在进程运行时更新配置。将ConfigMap暴露为卷可以达到配置热更新的效果,无需重新创建pod或重启容器。
    ConfigMap被更新后,卷中引用它的文件也会相应更新,进程发现文件被改变后进行重载。kubernetes同样支持文件更新后手动通知容器。但要注意的是,更新configmap之后对应文件的更新会相当耗时。
    我们使用kubectl edit configmap fortune-config 来更改某一个值。
    然后执行
    kubectl exec fortune-configmap-volume -c web-server -- cat /etc/nginx/config.d/my-nginx-config.conf
    查看文件是不是被修改了,如果没有稍等一会再查看。在确认更改后,我们来手动通知容器进行重载。
    kubectl exec fortune-configmap-volume -c web-server -- nginx -s reload
     
    你可能会疑惑 Kubernetes更新完configmap卷中的所有文件之前(所有的文件被更新而不是一部分),应用是否会监听到文件编号并主动进行重载。答案是不会,会在所有的文件更新后,一次性更新到pod容器中。kubernetes通过富豪链接做到这一点的。
    kubectl exec -ti fortune-configmap-volume -c web-server -- ls -lA /etc/nginx/config.d
    ..4989_09_04_15_06.028485
    ..data -> ..4989_09_04_15_06.028485
    my-nginx-config.conf -> ..data/my-nginx-config.conf
    sleep-interval -> ..data/sleep-interval
    可以看到,被挂载configMap卷中的文件是..data文件夹中文件的符号链接,而..data又是连接到 ..4989的符号链接,每当ConfigMap被更新后,Kubernetes 会创建一个这样的文件夹,卸任所有文件并重新将符号..data链接至新文件夹,通过这种方式可以一次性修改所有文件。
     
    如果挂载configmap中的某一个文件,而不是文件夹,configmap更新之后对应的文件不会被更新。如果现在你需要挂载单个文件且在修改Configmap后想自动更新,可以将该卷挂载到其他文件夹,然后做一个软链接。
    至于如何实现应用重载配置 ,需要应用自己实现了。
     
    configmap都是以名文存储的,有些信息比较敏感,就需要用秘文存储了。
    这就需要使用kubernetes 的secret资源了。
    首先有一个情况我们需要了解
    使用kubectl describe pod pod-id 查看任何一个pod你都会发现默认有挂载一个secret卷。
    Volumes:
      default-token-ps7ff:
        Type:        Secret (a volume populated by a Secret)
        SecretName:  default-token-ps7ff
        Optional:    false
    这个卷里的内容我们可以使用kubectl describe secrets查看
    #  kubectl describe secrets default-token-ps7ff
    Name:         default-token-ps7ff
    Namespace:    default
    Labels:       <none>
    Annotations:  kubernetes.io/service-account.name: default
                  kubernetes.io/service-account.uid: 6efa7f7c-6a61-11e9-bfdb-0a382f97318e
    
    Type:  kubernetes.io/service-account-token
    
    Data
    ====
    ca.crt:     1359 bytes
    namespace:  7 bytes
    token:      eyJhbGciOiJSUzI1NiIsImtp...
    可以看出这个Secret包含是哪个条目ca.crt 、namespace与token
    包含了从pod内部安全访问Kubernetes API服务器所需的全部信息。尽管我们希望做到应用对kubernetes无感知,但是直连Kubernetes没有其他方法,你只能使用secret卷提供的文件来访问kubernetes API。
    使用kubectl describe pod命令显示secret卷北挂载的位置:
    mounts:
    /var/run/secrets/kubernetes.io/serviceaccount from default-token-ps7ff (ro)
     
    注意: default-token Secret默认会被挂载至每个容器。可以通过设置pod定义中的automountServiceAccountToken字段为false,或者设置pod使用的服务账户中的相同字段为false来关闭这种默认行为。
     
    查看容器中挂载了哪些条目
    # kubectl exec nginx-dnm9n -- ls /var/run/secrets/kubernetes.io/serviceaccount
    ca.crt
    namespace
    token
     
    创建Secret
    openssl genrsa -out https.key 2048
    openssl req -new -x509 -key https.key -out https.cert -days 3650 -subj /CN=www.kubia-example.com
    # kubectl create secret generic fortune-https --from-file=https.key --from-file=https.cert --from-file=foo
    secret/fortune-https created
    也可以使用 --from-file=fortune-https 囊括这个文件夹中的文件
    # kubectl get secret fortune-https -o yaml
    # kubectl describe secret fortune-https
     
    对比configmap与secret
    secret与configMap有比较大的区别,这也是为何kubernetes开发者们在支持了Secret一段时间之后会选择创建ConfigMap。创建的Secret的YAML格式定义显示
    # kubectl get secret fortune-https -o yaml
    apiVersion: v1
    data:
      foo: YmFyCg==
      https.cert: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURFekNDQWZ1Z0F3SUJBZ0lKQU96Y00rNzI3RWJHTUEwR0NTcUdTSWIzRFFFQkN3VUFNQ0F4SGpBY0JnTlYKQkF
      https.key: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFcFFJQkFBS0NBUUVBeFYvUVJiazJiRm8zRmdZdWpaTWxPVGg3MUxpY3AyUS9pL2pib2E1SExlUlpSTDBi
    kind: Secret
    . . .
    将其与CoinfigMap的Yaml格式定义做对比
    apiVersion: v1
    data:
      bar: baz
      foo: bar
      one: two
    kind: ConfigMap
    注意到Secret条目的内容会被以Base64格式编译,而ConfigMap直接以纯文本展示。这种区别导致在处理YAML和JSON格式的Secret时会稍许有些麻烦,需要在设置和读取相关条目时对内容进行编解码。
    这个具体使用中是这样的,
    比如你现在想把一个配置文件加入到Secret中,那么你首先将配置文件中的内容通过BASE64进行编码后才能作为条目。
    当然你会问难道kubernetes不提供base64编码?提供,只能对字符串,不能接受文件。如下:
    kund: Secret
    apiVersion: v1
    stringData:
        foo: plain text
    data:
        https.cert: lksjkaldjldasjladgjsjl...
        https.key: lsiodjsdlfjahcdo...
    创建后使用kubectl get secret -o yaml会看到stringData字段中的所有条目会被Base64编码后展示在data字段下。所以stringData字段是只写不可读的。
    如何在pod中使用Secret
    apiVersion:  v1
    kind: Pod
    metadata:
        name: fortune-https
    spec:
        containers:
        -   image: luksa/fortune:env
             name: html-generator
             env:
             -   name: INTERVAL
                  valueFrom:
                  configMapKeyRef:
                      name: fortune-config
                      key: sleep-interval
              volumeMounts:
              -   name: html
                   mountPaht: /var/htdocs
        -   image: nginx:alpine
            name: web-server
            volumeMounts:
            -   name: html
                 mountPath: /usr/share/nginx/html
                 readOnly: true
            -   name: config
                 mountPath: /etc/nginx/conf.d
                 readOnly: true
            -   name: certs
                 mountPath: /etc/nginx/certs/
                 readOnly: true
            ports:
            - containerPort: 80
            - containerPort: 443
        volumes:
        -   name:  html
             emptyDir: {}
        -   name: config
             configmap:
                 name: fortune-config
                 items:
                 -   key: my-nginx-config.conf
                      path: https.conf
        -   name: certs
             secret:
                 secretname: fortune-https
    简单点的实例:

    apiVersion:  v1
    kind: Pod
    metadata:
        name: fortune-https
    spec:
        containers:
        - image: nginx:alpine
          name: web-server
          volumeMounts:
          - name: certs
            mountPath: /etc/nginx/certs/
            readOnly: true
          ports:
          - containerPort: 80
          - containerPort: 443
        volumes:
        - name: certs
          secret:
            secretName: fortune-https
    当然也可以将secret条目暴露为环境变量。但不建议这么做,应用通常会在错误报告时转储环境变量,或者是启动时打印在应用日志中,无意中就暴露Secret信息。另外,子进程会继承父进程的所有环境变量,如果是通过第三方二进制程序启动应用,你并不知道它使用敏感数据做了什么。所以不建议用环境变量,建议使用secret卷的形式挂载到pod.
    env:
    - name: FOO_SECRET
       valueFrom:
           secretKeyRef:
               name: fortune-https
               key: foo
    学会了secret,接下来就有一个比较常用的secret实际应用,dockerhub
    kubectl create secret docker-registry mydockerhubsecret --docker-username=myusername --docker-password=mypassword --docker-email=my.email@provider.com
     
    使用:
    apiVersion: v1
    kind: Pod
    metadata:
        name: private-pod
    spec:
        imagePullSecrets:
        - name: mydockerhubsecret
        containers:
        - image: username/private:tag
           name: main
    假设系统通常运行大量Pod,你可能会好奇是否需要为每个Pod都添加相同的镜像拉取Secret.并不是,可以通过添加Secret至ServiceAccount 使所有pod都能自动添加上镜像拉取的Secret.

  • 相关阅读:
    JavaScript 面向对象的编程(二) 类的封装
    js 防止重复提交表单
    JavaScript 面向对象的编程(一)
    layui laypage 当前页刷新问题
    jquery 选中设置的值
    突然觉得前端js挺不错的
    centos6.8 安装gitlab记录
    3/25/2018 学**况
    Threading、multiprocessing
    正则表达式 RegEx(Regular Expression)
  • 原文地址:https://www.cnblogs.com/zhming26/p/11719547.html
Copyright © 2011-2022 走看看