Kubernetes-管理应用配置

K8S中定义变量的方式
1、自定义值
2、从Pod属性中获取值
3、从ConfigMap、Secret获取值

ConfigMap

ConfigMap是一个K8S存储资源,用于存储应用程序配置文件
Pod使用configmap数据有两种方式:

  • 变量注入
  • 数据卷挂载
    在这里插入图片描述
# 先定义一个ConfigMap
[root@k8s-master configMap]# vim configmap-env.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: configmap-demo
data:
  # 键值
  abc: "123"
  cde: "456"
  
  # 这个是多行数据,一般这个更常用,这个可以直接将配置文件粘贴进去,知道行首位置对应上即可,一般的配置文件也是key:value形式的,这个"|",是表示多行数据的意思,没有这个"|",表示是键值
  redis-config: |
    port: 6379
    host: 192.168.113.149

# 写入到K8S中
[root@k8s-master configMap]# kubectl apply -f configmap-env.yaml 
configmap/configmap-demo created

# 查看信息可以看到其中的数据
[root@k8s-master configMap]# kubectl get configmap
NAME               DATA   AGE
configmap-demo     3      5s
kube-root-ca.crt   1      10d
[root@k8s-master configMap]# kubectl describe configmap configmap-demo 
Name:         configmap-demo
Namespace:    default
Labels:       <none>
Annotations:  <none>

Data
====
abc:
----
123
cde:
----
456
redis-config:
----
port: 6379
host: 192.168.113.149

Events:  <none>
# 定义一个Pod去获取值
[root@k8s-master configMap]# vim configmap-pod.yaml
kind: Pod
metadata:
  name: configmap-demo-pod
spec:
  containers:
    - name: web
      image: nginx
      # 以下是固定格式,是从ConfigMap中获取的值,这些值是刚刚定义过的
      env:
        - name: ABCD
          valueFrom:
            configMapKeyRef:
              name: configmap-demo
              key: abc
        - name: CDEF
          valueFrom:
            configMapKeyRef:
              name: configmap-demo
              key: cde
      # 这个是数据卷挂载
      volumeMounts:
      # 通过name去关联volumes
      - name: config
        # 挂载的路径
        mountPath: "/config"
        # 这里设置的是只读,可以取消也可以加上
        readOnly: true
  # 定义数据卷的来源,他这里是从ConfigMap获取
  volumes:
    # 定义一个name,在volumeMounts中会使用
    - name: config
      configMap:
        name: configmap-demo
        items:
        # 这个key刚刚也在创建的ConfigMap中有定义
        - key: "redis-config"
          # 挂载到容器的文件名
          path: "redis.properties"

写好yaml后执行并查看变量情况


# 创建pod
[root@k8s-master configMap]# kubectl apply -f configmap-pod.yaml
[root@k8s-master configMap]# kubectl get pods
NAME                         READY   STATUS    RESTARTS   AGE
configmap-demo-pod           1/1     Running   0          12s

# 查看pod中的内容
[root@k8s-master configMap]# kubectl exec -it configmap-demo-pod bash
# 这个是刚刚挂载的配置文件
root@configmap-demo-pod:/# cat config/redis.properties 
port: 6379
host: 192.168.113.149
root@configmap-demo-pod:/# echo $ABCD
123
root@configmap-demo-pod:/# echo $CDEF
456
# 以上这些变量都是刚刚在配置ConfigMap中定义好的

Secret

Secret和ConfigMap类似,区别在于Secret主要存储敏感数据
可以存储以下三种数据类型:

  • docker-registry(kubernetes.io/dockerconfigjson):存储镜像仓库认证信息
  • generic(Opaque):存储密码、密钥等(yaml里数据需要经过base64编码)
  • tls(kubernetes.io/tls):存储TLS证书

创建Secret

分为散布
第一步:将用户密码进行编码

echo -n 'admin' |base64
echo -n '1f2d1e2e67df' | base64

第二步、将编码后值放到Secret

apiVersion: v1
kind: Secret
metadata:
  name: db-user-pass
  # 指定使用的那种类型,也就是之前说的内三种类型(docker-registry(kubernetes.io/dockerconfigjson)、generic(Opaque)、tls(kubernetes.io/tls)),type写括号内的
type: Opaque
data:
  username: YWRtaW4=
  password: MWYyZDFlMmU2N2Rm

# 查看创建好的secret
[root@k8s-master secret]# kubectl apply -f secret.yaml 
[root@k8s-master secret]# kubectl get secret
db-user-pass          Opaque                                2      6s

secret创建好后,我们来创建Pod来看看

# 创建一与Secret相关联的Pod
apiVersion: v1
kind: Pod
metadata:
  name: secret-demo-pod
spec:
  containers:
    - name: demo
      image: nginx
      env:
        - name: USER
          valueFrom:
            secretKeyRef:
              name: db-user-pass
              key: username
        - name: PASS
          valueFrom:
            secretKeyRef:
              name: db-user-pass
              key: password
      volumeMounts:
      - name: config
        mountPath: "/config"
        readOnly: true
  volumes:
    - name: config
      secret:
        secretName: db-user-pass
        items:
        - key: username
          path: username.txt
        - key: password
          path: password.txt
# 可以进入Pod验证一下

应用程序如何动态更新配置

当我修改了一些变量后,即使变量会同步到Pod中,但是服务在不重启的情况下也是不会应用的
应用程序动态更新配置方案

  • 当ConfigMap发生变更时,应用程序自动感知动态加载(需要程序自身支持)
  • 触发滚动更新,即重启服务
  • 给程序预留一个接口,用于通知动态加载配置(Sidecar),例如nginx -s reload

当然这上边可能需要开发去支持下,但是运维也可以通过inotify去动态感知配置文件的变化,如果监控到配置文件有更新,那么可以通过reload类似的接口去下载

数据卷

数据卷概述

为什么需要数据卷
容器中的文件在磁盘上是临时存放的,这给容器中运行比较重要的应用程序带来一些问题

  • 问题1:当容器升级或崩溃时,kubelet会重建容器,容器内文件会丢失
  • 问题2:一个pod中运行多个容器需要共享文件
    Kubernetes卷(Volume)这一抽象概念能够解决着两个问题

常用的数据卷:

  • 节点本地(hostPath,emptyDir)
  • 网络(NFS、Ceph、GlusterFS)
  • 公有云(AWS EBS)
  • K8S资源(configmap,secret)

数据卷更多支持功能

emptyDir

emptyDir卷: 是一个临时存储卷(Pod所在节点),与Pod生命周期绑定一起,如果Pod删除了卷也会被删除
应用场景: Pod容器之间数据共享

# 案例
apiVersion: v1
kind: Pod
metadata:
  name: my-pod
spec:
  containers:
  # 这个是写容器(业务容器),产生一些数据
  - name: write
    image: centos
    #  模拟运行中生成的数据
    command: ["bash","-c","for i in {1..100};do echo $i >> /data/access.log;sleep 1;done"]
    # 数据挂载点
    volumeMounts:
      - name: data
        mountPath: /data
  # 读容器(辅助容器)
  - name: read
    image: centos
    # 模拟从业务容器读取数据
    command: ["bash","-c","tail -f /data/access.log"]
    # 数据挂载点
    volumeMounts:
      - name: data
        mountPath: /data
  # 数据共享点(Pod所在节点)
  volumes:
  - name: data
    emptyDir: {}

# apply后进入到Pod中查看
# 无论是进入read还是进入write,都可以看到data目录下有一个access.log的文件
[root@k8s-master emptyDir]# kubectl exec -it my-pod -c read -- bash
# emptyDir是本地挂载的,所以可以去找到对应的节点,在对应节点找到指定的data目录中的数据
# 根据Pod名称去寻找,"k8s_write_my-pod_default_d06fc492-c2bc-4e1b-97e7-61f74796296a_3"中后面的"d06fc492-c2bc-4e1b-97e7-61f74796296a"就是它的Id
[root@k8s-node1 pods]# docker ps | grep my-pod
9880225f17ac   centos                                                "bash -c 'for i in {…"   About a minute ago   Up About a minute             k8s_write_my-pod_default_d06fc492-c2bc-4e1b-97e7-61f74796296a_3
3c4c26ae2205   centos                                                "bash -c 'tail -f /d…"   7 minutes ago        Up 7 minutes                  k8s_read_my-pod_default_d06fc492-c2bc-4e1b-97e7-61f74796296a_0
# 进入到kubelet的存储目录下
[root@k8s-node1 pods]# cd /var/lib/kubelet/pods/
# 找到对应ID
[root@k8s-node1 data]# pwd
/var/lib/kubelet/pods/d06fc492-c2bc-4e1b-97e7-61f74796296a/volumes/kubernetes.io~empty-dir/data
# 可以看到数据都在这下面,将Pod删除后该目录就会消失
[root@k8s-node1 data]# tailf access.log 

hostPath

hostPath: 挂载Node(Pod所在节点)文件系统上文件或者目录到Pod中的容器
应用场景: Pod中容器需要访问宿主机文件

案例

[root@k8s-master volume]# vim hostpath.yaml
apiVersion: v1
kind: Pod
metadata:
  name: my-pod3
spec:
  containers:
  - name: test
    image: busybox
    args:
    - /bin/sh
    - -c
    - sleep 36000
    volumeMounts:
    - name: data
      mountPath: /data
    - name: tmp
      mountPath: /aaa
  # 将宿主机的/tmp目录挂载到容器的/data目录
  volumes:
  - name: data
    hostPath:
      path: /tmp
      # 指定类型,具体的可以在官网查看详情
      type: Directory
  # 可以定义其他类型的,也可以定义多个相同类型的
  - name: tmp
    emptyDir: {}


# 验证,该Pod被分配到了Node1节点上
[root@k8s-master volume]# kubectl apply -f hostpath.yaml 
[root@k8s-master volume]# kubectl exec -it my-pod3 -- sh
/ # ls data/
/ # mkdir data/aa
# 创建一个也可以在node1节点上发现创建的文件
[root@k8s-node1 tmp]# ls
aa 

以上两种方式可以解决"问题2",但是无法解决"问题1",为了解决"问题1"可以使用以下方式解决

数据卷:NFS

NFS数据卷: 提供对NFS挂载支持,可以自动将NFS共享路径挂载到Pod中
NFS: 是一个主流的文件共享服务器

NFS部署

每个节点都需要部署

yum -y install nfs-utils
# 服务端配置以下内容
vim /etc/exports
# 加入以下内容
/data/kubernetes *(rw,no_root_squash)
mkdir -p /data/kubernetes
systemctl start nfs
systemctl enable nfs

示例

# 创建yaml
[root@k8s-master nfs]# kubectl create deployment nginx --image=nginx --dry-run=client -o yaml > deployment.yaml
# 修改为以下内容
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: nginx
  name: nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - image: nginx
        name: nginx
        volumeMounts:
        - name: wwwroot
          mountPath: /usr/share/nginx/html
      volumes:
      - name: wwwroot
        nfs:
         server: 192.168.113.148
         path: /data/kubernetes
# 创建service     
[root@k8s-master nfs]# kubectl expose deployment nginx --port=80 --target-port=80 --type=NodePort
# 查看pod
[root@k8s-master nfs]# kubectl get pods  |grep nginx
nginx-5c4886479-4lbv5        1/1     Running   0          6m54s
nginx-5c4886479-bsctn        1/1     Running   0          6m54s
nginx-5c4886479-sn2ls        1/1     Running   0          6m54s
# 查看svc和ep
[root@k8s-master nfs]# kubectl get svc,ep |grep nginx
service/nginx        NodePort    10.103.201.7   <none>        80:31040/TCP     6m40s
endpoints/nginx        10.244.169.144:80,10.244.36.88:80,10.244.36.89:80       6m40s
# 验证NFS挂载存储
# 在挂载目录下加入html页面
[root@k8s-master nfs]# cd /data/kubernetes/
[root@k8s-master kubernetes]# echo "aaa" > index.html

访问
在这里插入图片描述

当删除pod或者加入新的pod也会存在这个目录,但是正常生产环境中是不会直接使用这个的,下面通过PV、PVC来实现这个功能

持久卷概述

  • PersistentVolume(PV):持久数据卷,对存储资源的抽象,使得存储作为集群中的资源管理
  • PersistentVolumeClaim(PVC):持久数据卷申请,用户定义使用的存储容量,使得用户不需要关心后端存储实现

Pod申请PVC作为卷来使用,Kubernetes通过PVC查找绑定的PV,并挂载到Pod中提供程序使用

PV与PVC使用流程

使用pv和pvc最终其实也是使用到nfs之类的共享存储,不过引入了pv和pvc的概念来保障安全性、专业性等,可以通过一下示例可以简单的了解创建流程和实现原理,一个PV只能对应一个PVC

# 创建Pod及PVC
[root@k8s-master pv]# vim deployment-pvc.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-pvc
  labels:
    app: nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      app: pvc
  template:
    metadata:
      labels:
        app: pvc
    spec:
      containers:
      - name: nginx
        image: nginx
        ports:
        - containerPort: 80
        volumeMounts:
        - name: wwwroot
          mountPath: /usr/share/nginx/html
      volumes:
      - name: wwwroot # pvc
        persistentVolumeClaim:
          claimName: my-pvc
---
# pvc
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: my-pvc
spec:
  accessModes:
    - ReadWriteMany
  resources:
      requests:
      storage: 10Gi
---
apiVersion: v1
kind: Service
metadata:
  name: web-pvc
spec:
  ports:
  - port: 80
    protocol: TCP
    targetPort: 80
  selector:
    app: pvc
  type: NodePort



[root@k8s-master pv]# vim pv.yaml
# 创建PV
apiVersion: v1
kind: PersistentVolume
metadata:
  name: my-pv
spec:
  capacity:
    storage: 10Gi
  accessModes:
    - ReadWriteMany
  persistentVolumeReclaimPolicy: Retain    
  nfs:
    path: /data/kubernetes
    server: 192.168.113.148
PV生命周期

ACCESS MODES(访问模式)
AccessModes是用来对PV进行访问模式的设置,用于描述用户应用对存储资源的访问权限,访问权限包括下面几种方式:

  • ReadWriteOnce(RWO):读写权限,但是只能被单个节点挂载(类似于块儿存储)
  • ReadOnlyMany(ROX):只读权限,可以被多个节点挂载
  • ReadWriteMany(RWX):读写权限,可以被多个节点挂载(文件存储)

RECLAIM POLICY(回收策略):
目前PV支持的策略有三种:

  • Retain(保留):保留数据,需要管理员手工清理数据
  • Recycle(回收):清除PV中的数据。效果相当于执行rm -rf
  • Delete(删除):与PV相连的后端存储同时删除

修改回收策略:persistentVolumeReclaimPolicy:Retain

STATUS(状态):
一个PV的生命周期中,可能会处于4中不同的阶段:

  • Available(可用):表示可用状态,还未被任何PVC绑定
  • Bound(已绑定):表示PV已经被PVC绑定
  • Released(已释放):PVC被删除,但是资源还未被集群重新声明
  • Failed(失败):表示该PV的自动回收失败
PV和PVC匹配条件

根据存储容量和访问模式匹配,匹配最接近的PV容量(向大的匹配),并且PV和PVC从资源来讲,不具备限制能力,具体还得根据后端存储而定,像NFS是不具备存储限制能力的,即使通过PVC去申请下存储资源,那么也不会根据申请的资源去限制他的使用

PV的动态供给

 以上所讲述的PV和PVC都是静态供给,需要运维人员提前手动创建好
 而这种方式繁琐复杂,PV多了也不方便运维,所以就有了PV的动态供给(StorageClass)
 这里还是以NFS为共享存储来实现(可以在该链接中查看支持动态供给的存储插件,其中打√的就是支持,不打√的就需要去社区获取插件来支持了,NFS就是如此)
 K8S默认不支持NFS动态供给,需要单独部署社区开发的插件
项目地址:https://github.com/kubernetes-sigs/nfs-subdir-external-provisioner

创建一个动态供给

[root@k8s-master nfs-external-provisioner]# vim deployment.yaml 
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nfs-client-provisioner
  labels:
    app: nfs-client-provisioner
  # replace with namespace where provisioner is deployed
  namespace: default
spec:
  replicas: 1
  strategy:
    type: Recreate
  selector:
    matchLabels:
      app: nfs-client-provisioner
  template:
    metadata:
      labels:
        app: nfs-client-provisioner
    spec:
      serviceAccountName: nfs-client-provisioner
      containers:
        - name: nfs-client-provisioner
          # 国内的一个镜像站
          image: lizhenliang/nfs-subdir-external-provisioner:v4.0.1
          volumeMounts:
            - name: nfs-client-root
              mountPath: /persistentvolumes
          env:
            - name: PROVISIONER_NAME
              # 配置StorageClass的一个标识
              value: k8s-sigs.io/nfs-subdir-external-provisioner
            - name: NFS_SERVER
              value: 192.168.113.148
            - name: NFS_PATH
              value: /data/kubernetes
      volumes:
        - name: nfs-client-root
          nfs:
            server: 192.168.113.148
            path: /data/kubernetes

# 存储类,其实就是定义了一个标记,
[root@k8s-master nfs-external-provisioner]# vim class.yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: managed-nfs-storage
# 对应后端供给插件的一个标识,和deployment中env.value的内个标识一致的,不懂得原理不要去改动他
provisioner: k8s-sigs.io/nfs-subdir-external-provisioner # or choose another name, must match deployment's env PROVISIONER_NAME'
parameters:
  # 在这个动态供给中,它创建的pv默认规则为delete,删除pvc后pv也会删除,存储的数据也会删除,但是可以将下方参数"false"改为"true",他会在删除pv后将原来的目录做一个归档备份,并且class.yaml不支持动态更新,更新的话需要使用delete -f删掉后apply -f重新构建,不会影响已有数据的
  archiveOnDelete: "false"

# 创建k8s的认证
[root@k8s-master nfs-external-provisioner]# vim rbac.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
  name: nfs-client-provisioner
  # replace with namespace where provisioner is deployed
  namespace: default
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: nfs-client-provisioner-runner
rules:
  - apiGroups: [""]
    resources: ["persistentvolumes"]
    verbs: ["get", "list", "watch", "create", "delete"]
  - apiGroups: [""]
    resources: ["persistentvolumeclaims"]
    verbs: ["get", "list", "watch", "update"]
  - apiGroups: ["storage.k8s.io"]
    resources: ["storageclasses"]
    verbs: ["get", "list", "watch"]
  - apiGroups: [""]
    resources: ["events"]
    verbs: ["create", "update", "patch"]
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: run-nfs-client-provisioner
subjects:
  - kind: ServiceAccount
    name: nfs-client-provisioner
    # replace with namespace where provisioner is deployed
    namespace: default
roleRef:
  kind: ClusterRole
  name: nfs-client-provisioner-runner
  apiGroup: rbac.authorization.k8s.io
---
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: leader-locking-nfs-client-provisioner
  # replace with namespace where provisioner is deployed
  namespace: default
rules:
  - apiGroups: [""]
    resources: ["endpoints"]
    verbs: ["get", "list", "watch", "create", "update", "patch"]
---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: leader-locking-nfs-client-provisioner
  # replace with namespace where provisioner is deployed
  namespace: default
subjects:
  - kind: ServiceAccount
    name: nfs-client-provisioner
    # replace with namespace where provisioner is deployed
    namespace: default
roleRef:
  kind: Role
  name: leader-locking-nfs-client-provisioner
  apiGroup: rbac.authorization.k8s.io
# 构建yaml
[root@k8s-master nfs-external-provisioner]# kubectl apply -f .
# 查看创建的存储类,默认的回收策略是Delete
[root@k8s-master nfs-external-provisioner]# kubectl get sc
NAME                  PROVISIONER                                   RECLAIMPOLICY   VOLUMEBINDINGMODE   ALLOWVOLUMEEXPANSION   AGE
managed-nfs-storage   k8s-sigs.io/nfs-subdir-external-provisioner   Delete          Immediate           false                  2m56s

创建一个pvc

# 创建一个测试Pod
[root@k8s-master pv]# vim deployment-pvc.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-pvc 
  labels:
    app: nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      app: pvc
  template:
    metadata:
      labels:
        app: pvc
    spec:
      containers:
      - name: nginx
        image: nginx
        ports:
        - containerPort: 80
        volumeMounts:
        - name: wwwroot
          mountPath: /usr/share/nginx/html
      volumes:
      - name: wwwroot # pvc
        persistentVolumeClaim:
          claimName: my-pvc
---
# pvc
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: my-pvc 
spec:
  # 加入指定存储类的名字,改参数标识指定存储类,使用动态供给
  storageClassName: "managed-nfs-storage"
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: 10Gi
---
apiVersion: v1
kind: Service
metadata:
  name: web-pvc
spec:
  ports:
  - port: 80
    protocol: TCP
    targetPort: 80
  selector:
    app: pvc 
  type: NodePort
[root@k8s-master pv]# kubectl apply -f deployment-pvc.yaml 
[root@k8s-master pv]# kubectl get pvc
NAME     STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS          AGE
my-pvc   Bound    pvc-06220b0a-fc6b-493b-a031-7057d8022a05   10Gi       RWX            managed-nfs-storage   6s
[root@k8s-master pv]# kubectl get pv
NAME                                       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM            STORAGECLASS          REASON   AGE
pvc-06220b0a-fc6b-493b-a031-7057d8022a05   10Gi       RWX            Delete           Bound    default/my-pvc   managed-nfs-storage            8s

这里理一下思路吧,先通过nfs的插件创建一个Pod用来管理nfs的动态供给,由之前的三种yaml实现(class.yaml、deployment.yaml、rbac.yaml),其中class定义了一个标签和名称,标签在deployment中也有定义,而名称是在用户需要配置pvc时配置storageClassName项,和单纯的PV使用NFS存储而言,他为每个应用分配独立的存储,之间是不会冲突的,但是同一组Pod的存储还是共享的,不是独立的

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值