Docker基础知识 (21) - Kubernetes(四) | 在 K8s 集群上部署 NFS 实现共享存储 (1)


在 “Docker基础知识 (20) - Kubernetes(三) | 在 K8s 集群上部署 Nginx” 里部署的 Nginx,通过存储卷(volumes)挂载到 master 的 /home/k8s/nginx-test/nginx 目录下的子目录。

当 K8s 集群把 Pod 调度到 node 上时,node 上没有 /home/k8s/nginx-test/nginx 目录,很显然,这种情况下 nginx 无法在 node 上正常运行。  

要想让存储卷(volumes)能被 Pod 任意挂载,我们需要变更存储的方式,不能限定在本地磁盘,而是要改成网络存储,这样 Pod 无论在哪里运行,只要知道 IP 地址或者域名,就可以通过网络访问存储设备。

本文我们在 “Docker基础知识 (20) - Kubernetes(三) | 在 K8s 集群上部署 Nginx” 基础上,来演示如何部署 NFS 和创建静态 PV/PVC。

Kubernetes 的共享存储详细介绍,请参考 “系统架构与设计(7)- Kubernetes 的共享存储”。

NFS (Network File System),即网络文件系统,是 FreeBSD 支持的文件系统中的一种。NFS 允许一个系统在网络上与它人共享目录和文件。通过使用 NFS,用户和程序可以像访问本地文件一样访问远端系统上的文件。


1. 部署环境

    虚拟机: Virtual Box 6.1.30(Windows 版)
    操作系统: Linux CentOS 7.9 64位
    Docker 版本:20.10.7
    Docker Compose 版本:2.6.1
    Kubernetes 版本:1.23.0

    工作目录:/home/k8s
    Linux 用户:非 root 权限用户 (用户名自定义,这里以 xxx 表示),属于 docker 用户组,

    1) 主机列表

        主机名          IP            角色       操作系统
        k8s-master  192.168.0.10    master      CentOS 7.9
        k8s-node01  192.168.0.11    node        CentOS 7.9


    2) 工作目录结构

        以下目录结构处于 master 节点 /home/k8s 目录下:

            k8s
             |- init.default.yaml
             |- kube-flannel.yml
             |- nginx-test
                    |- namespace.yaml
                    |- nginx-deployment.yaml
                    |- nginx-service.yaml
                    |- nginx
                    |   |- conf.d
                    |   |   |- nginx.conf
                    |   |   
                    |   |- html
                    |   |   |- test.html
                    |   |
                    |   |- log
                    |
                    |- nfs
                        |- nginx-nfs-pv.yaml
                        |- nginx-nfs-pvc.yaml                  
                        |- nginx-nfs-deployment.yaml  


2. 安装配置 NFS

    NFS 采用的是 Client/Server 架构,需要选定一台主机安装 NFS 服务端,其他要使用存储的主机安装 NFS 客户端。

    1) 服务端操作(master,192.168.0.10)

        NFS 服务端可以安装在一台独立的主机上,本文把 NFS 服务端安装在 master 上。

        $ sudo yum install -y nfs-utils

            ...

            Installed:
            nfs-utils.x86_64 1:1.3.0-0.68.el7.2

            Dependency Installed:
            gssproxy.x86_64 0:0.7.0-30.el7_9                     keyutils.x86_64 0:1.5.8-3.el7
            libbasicobjects.x86_64 0:0.1.1-32.el7                libcollection.x86_64 0:0.7.0-32.el7
            libevent.x86_64 0:2.0.21-4.el7                       libini_config.x86_64 0:1.3.1-32.el7
            libnfsidmap.x86_64 0:0.25-19.el7                     libpath_utils.x86_64 0:0.2.1-32.el7
            libref_array.x86_64 0:0.1.5-32.el7                   libtirpc.x86_64 0:0.2.4-0.16.el7
            libverto-libevent.x86_64 0:0.2.5-4.el7               quota.x86_64 1:4.01-19.el7
            quota-nls.noarch 1:4.01-19.el7                       rpcbind.x86_64 0:0.2.0-49.el7
            tcp_wrappers.x86_64 0:7.6-77.el7

            Complete!    


        # 配置 NFS 服务
        $ sudo vim /etc/exports

            # 共享目录    
            /home/k8s   192.168.0.0/16(rw,sync,all_squash)

            参数说明:

                读写权限: rw - 读写权限,ro - 只读权限;
                用户映射:

                    all_squash:无论 NFS 客户端使用什么身份访问,都映射为 NFS 服务器的匿名用户;
                    no_all_squash:无论 NFS 客户端使用什么身份访问,都不压缩用户权限;
                    root_squash: 当 NFS 客户端以 root 身份访问时,映射为 NFS 服务器的匿名用户;
                    no_root_squash:当 NFS 客户端以 root 身份访问时,映射为 NFS 服务器的 root 管理员;

                其它:

                    secure:    NFS 通过 <= 1024 的安全 TCP/IP 端口发送;
                    insecure: NFS 通过 > 1024 的端口发送;
                    sync:同时将数据写入到内存和硬盘中,不丢失数据;
                    async:优先将数据保存到内存中,然后在写入到硬盘中,效率高,但有可能丢失数据;
                    anonuid=xxx    NFS 服务器 /etc/passwd 文件中匿名用户的 UID;
                    anongid=xxx    NFS 服务器 /etc/passwd 文件中匿名用户的 GID;

                192.168.0.0/16: 允许访问 NFS 服务端的网段,* 表示所有网段;

        # 启动 nfs 服务,并设置开机启动
        $ sudo systemctl enable nfs
        $ sudo systemctl start nfs
        
        # 查看 nfs 服务状态
        $ sudo systemctl status nfs

        # 查看 /home/k8s 共享目录
        $ cd /home/k8s
        $ ls -la

            total 12
            drwxr-xr-x  3 xxx xxx     73 Nov 20 02:20 .
            drwxr-xr-x. 5 root root   46 Nov 20 02:27 ..
            -rw-r--r--  1 xxx xxx    834 Nov 20 02:19 init.default.yaml
            -rw-r--r--  1 xxx xxx   4583 Nov 20 02:20 kube-flannel.yml
            drwxr-xr-x  4 xxx xxx    107 Nov 20 02:25 nginx-test

    
    2) 客户端操作(node,192.168.0.11)

        $ sudo yum install -y nfs-utils

        # 查看服务端里面可以挂载的目录
        $ showmount -e 192.168.0.10

            Export list for 192.168.0.10:
            /home/k8s                         192.168.0.0/16

        # 创建本地 /home/k8s 目录,把目录 owner 从 root 改成当前登陆用户
        $ sudo mkdir -p /home/k8s
        $ sudo chown -R xxx:xxx /home/k8s

        # 手动挂载 master 的共享目录
        $ sudo mount -t nfs 192.168.0.10:/home/k8s /home/k8s
        $ cd /home/k8s
        $ ls -la

            total 12
            drwxr-xr-x  3 xxx xxx       73 Nov 20 02:20 .
            drwxr-xr-x. 4 root root     31 Nov 20 09:21 ..
            -rw-r--r--  1 xxx xxx      834 Nov 20 02:19 init.default.yaml
            -rw-r--r--  1 xxx xxx     4583 Nov 20 02:20 kube-flannel.yml
            drwxr-xr-x  4 xxx xxx      107 Nov 20 02:25 nginx-test


            注:node 上挂载的 /home/k8s 目录和 master 上 /home/k8s 目录里的内容一致,挂载成功。

                此时 node 上的 /home/k8s 目录就相当于本地目录,被调度到 node 的 Pod 可以正常访问 /home/k8s/nginx-test/nginx 目录,Pod 里的 nginx 服务可以正常运行。    

                本文的测试环境下,master 和 node 上使用的 Linux 用户名/用户组,都是同名的 xxx。没有测试在不同 Linux 用户名/用户组的情况,如果遇到文件和目录访问权限问题,可以尝试把 master 上的 /home/k8s 目录改成 777 属性。    

        # 卸载共享目录
        $ sudo umount /home/k8s

        # 删除共享目录
        $ sudo rm -rf  /home/k8s


3. 修改 NFS Server 配置

    上面在 node 节点上手动挂载 master 节点共享目录的方式,在 node 节点不多(3 ~ 5 个)的情况下,或许是一种方便的共享存储实现方式,但这个种方式有很明显的弊端:

        (1) node 节点超过一定数量时,在 node 各节点上手动挂载目录,是一件工作量很大的事情;
        (2) 没有程序监控各手动挂载目录的状态,出现挂载失效时,无法实现自动通知和修复;
        (3) 在 master 上 Deployment 里要调整 Volumes 时,各 node 上的手动挂载目录也需要手动调整,这显然违背了 K8s 轻松部署的原则;

    我们需要使用基于 K8s 提供的 PersistentVolume(PV)、PersistentVolumeClaim(PVC)和存储分配器(Provisioner)等来实现更灵活的共享存储配置。

    # 添加共享目录
    $ sudo vim /etc/exports

        # 共享目录    
        /home/k8s   192.168.0.0/16(rw,sync,all_squash)
        /home/k8s/nginx-test/nginx/conf.d   192.168.0.0/16(ro,sync,all_squash)
        /home/k8s/nginx-test/nginx/html     192.168.0.0/16(ro,sync,all_squash)
        /home/k8s/nginx-test/nginx/log      192.168.0.0/16(rw,sync,all_squash,anonuid=1000,anongid=1000)


        注:假设 master 上当前登陆用户是 xxx:xxx (用户:用户组),可是运行 id 命令获取 uid 和 gid。

                $ id

                    uid=1000(xxx) gid=1000(xxx) groups=1000(xxx),994(docker)

            把 /home/k8s 目录及子目录的 owner 设置为 xxx:xxx (即当前登陆用户:用户组)。
            
            在 /etc/exports 里的配置:
            
                home/k8s/nginx-test/nginx/log      192.168.0.0/16(rw,sync,all_squash,anonuid=1000,anongid=1000)
            
            其中 anonuid=1000,anongid=1000 就是用户 xxx:xxx,/home/k8s 目录及子目录的 owner 也是 xxx:xxx,所以 NFS 间接有了 /home/k8s/nginx-test/nginx/log 目录的写操作权限。

    # 重启 nfs 服务
    $ sudo systemctl restart nfs
    
    # 查看服务端里面可以挂载的目录
    $ showmount -e 192.168.0.10

        Export list for 192.168.0.10:
        /home/k8s/nginx-test/nginx/log    192.168.0.0/16
        /home/k8s/nginx-test/nginx/html   192.168.0.0/16
        /home/k8s/nginx-test/nginx/conf.d 192.168.0.0/16
        /home/k8s                         192.168.0.0/16

 

4. 创建静态 PV/PVC

    1) 创建 nginx-nfs-pv.yaml 文件

        $ cd  /home/k8s/nginx-test/nfs   # 手动创建 nfs 子目录   
        $ vim nginx-nfs-pv.yaml

            apiVersion: v1
            kind: PersistentVolume
            metadata:
              name: nginx-nfs-pv-confd
              labels:
                name: nginx-nfs-pv-confd
            spec:
              storageClassName: nfs-server
              accessModes:
                - ReadOnlyMany
              capacity:
                storage: 1Mi
              nfs:
                path: /home/k8s/nginx-test/nginx/conf.d
                server: 192.168.0.10
            ---
            apiVersion: v1
            kind: PersistentVolume
            metadata:
              name: nginx-nfs-pv-html
              labels:
                name: nginx-nfs-pv-html
            spec:
              storageClassName: nfs-server
              accessModes:
                - ReadOnlyMany
              capacity:
                storage: 100Mi
              nfs:
                path: /home/k8s/nginx-test/nginx/html
                server: 192.168.0.10
            ---
            apiVersion: v1
            kind: PersistentVolume
            metadata:
              name: nginx-nfs-pv-log
              labels:
                name: nginx-nfs-pv-log
            spec:
              storageClassName: nfs-server
              accessModes:
                - ReadWriteMany
              capacity:
                storage: 50Mi
              nfs:
                path: /home/k8s/nginx-test/nginx/log
                server: 192.168.0.10


            参数说明:

                spec.storageClassName: 驱动类型 nfs-server,可以简写成 nfs
                spec.accessModes:

                    ReadWriteOnce – 可以 read-write 模式被 mount 到单个节点;
                    ReadOnlyMany – 可以 read-only 模式被 mount 到多个节点;
                    ReadWriteMany – 可以 read-write 模式被 mount 到多个节点;

                spec.capacity.storage: PV 的容量大小,比如 1Mi,1Gi
                spec.nfs.path:  NFS 服务器上的共享目录全路径;
                spec.nfs.server: NFS 服务器的 DNS 或 IP 地址;

            注:spec.nfs.path 要先手动创建好,否则 K8s 按照 PV 的描述会无法挂载 NFS 共享目录,PV 就会处于 Pending 状态无法使用。

        # 执行创建命令
        $ kubectl apply -f nginx-nfs-pv.yaml

            persistentvolume/nginx-nfs-pv-confd created
            persistentvolume/nginx-nfs-pv-html created
            persistentvolume/nginx-nfs-pv-log created


        # 查看 PV
        $ kubectl get pv

            NAME                 CAPACITY   ACCESS MODES  ...   STATUS       STORAGECLASS
            nginx-nfs-pv-confd   1Mi        ROX                 Available        nfs
            nginx-nfs-pv-html    100Mi      ROX                 Available        nfs
            nginx-nfs-pv-log     50Mi       RWX                 Available        nfs


    2) 创建 nginx-nfs-pvc.yaml 文件

        $ cd  /home/k8s/nginx-test/nfs
        $ vim nginx-nfs-pvc.yaml

            apiVersion: v1
            kind: PersistentVolumeClaim
            metadata:
              name: nginx-nfs-pvc-confd
              namespace: nginx-test
            spec:
              storageClassName: nfs-server
              accessModes:
                - ReadOnlyMany
              resources:
                requests:
                  storage: 1Mi
            ---
            apiVersion: v1
            kind: PersistentVolumeClaim
            metadata:
              name: nginx-nfs-pvc-html
              namespace: nginx-test
            spec:
              storageClassName: nfs-server
              accessModes:
                - ReadOnlyMany
              resources:
                requests:
                  storage: 100Mi
            ---
            apiVersion: v1
            kind: PersistentVolumeClaim
            metadata:
              name: nginx-nfs-pvc-log
              namespace: nginx-test  
            spec:
              storageClassName: nfs-server
              accessModes:
                - ReadWriteMany
              resources:
                requests:
                  storage: 50Mi

            参数说明:

                spec.resources.requests.storage: 表示请求(或申请)多大的容量,比如 1Mi,1Gi;

        # 执行创建命令
        $ kubectl apply -f nginx-nfs-pvc.yaml

            persistentvolumeclaim/nginx-nfs-pvc-confd created
            persistentvolumeclaim/nginx-nfs-pvc-html created
            persistentvolumeclaim/nginx-nfs-pvc-log created


        # 查看 PVC
        $ kubectl get pvc -n nginx-test

            NAME                 STATUS  VOLUME              CAPACITY  ACCESS ...  STORAGECLASS
            nginx-nfs-pvc-confd  Bound   nginx-nfs-pv-confd  1Mi       ROX          nfs         
            nginx-nfs-pvc-html   Bound   nginx-nfs-pv-html   100Mi     ROX          nfs         
            nginx-nfs-pvc-log    Bound   nginx-nfs-pv-log    50Mi      RWX          nfs         
 


        # 查看 PV
        $ kubectl get pv

            NAME                CAPACITY ACCESS ... STATUS  CLAIM                       STORAGECLASS  
            nginx-nfs-pv-confd  1Mi      ROX        Bound   nginx-test/nginx-nfs-pvc-confd  nfs  
            nginx-nfs-pv-html   100Mi    ROX        Bound   nginx-test/nginx-nfs-pvc-html   nfs
            nginx-nfs-pv-log    50Mi     RWX        Bound   nginx-test/nginx-nfs-pvc-log    nfs


        # 删除 PVC
        $ kubectl delete pvc [PVC_NAME] -n nginx-test

        # 删除 PV,要先删除 CLAIM 的 PVC
        $ kubectl delete pv [PV_NAME]

    3) 创建 nginx-nfs-deployment.yaml 文件

        $ cd /home/k8s/nginx-test/nfs  
        $ vim nginx-nfs-deployment.yaml

            apiVersion: apps/v1
            kind: Deployment
            metadata:
              name: nginx-nfs-deployment
              namespace: nginx-test
            spec:
              replicas: 1
              selector:
                matchLabels:
                  app: nginx-nfs-pod
              template:
                metadata:
                  labels:
                    app: nginx-nfs-pod
                spec:
                  containers:
                  - name: nginx-nfs-1-21
                    image: nginx:1.21
                    ports:
                    - containerPort: 80
                    volumeMounts:
                    - name: conf
                      mountPath: /etc/nginx/conf.d
                    - name: log
                      mountPath: /var/log/nginx
                    - name: html
                      mountPath: /usr/share/nginx/html
                  volumes:
                  - name: conf
                    persistentVolumeClaim:
                      claimName: nginx-nfs-pvc-confd
                  - name: log
                    persistentVolumeClaim:
                      claimName: nginx-nfs-pvc-log
                  - name: html
                    persistentVolumeClaim:
                      claimName: nginx-nfs-pvc-html


            # 执行创建命令
            $ kubectl apply -f nginx-nfs-deployment.yaml

                  deployment.apps/nginx-nfs-deployment created            

            # 查询 pod
            $ kubectl get pod -n nginx-test -o wide

               NAME                                   READY  STATUS    ...  IP         NODE ...
               nginx-nfs-deployment-7d5ff8978c-xmrvk  1/1   Running         10.244.1.7   k8s-node01   


            # 查看 pod 详情
            $ kubectl describe pod nginx-nfs-deployment-7d5ff8978c-xmrvk -n nginx-test

                Name:         nginx-nfs-deployment-7d5ff8978c-xmrvk
                Namespace:    nginx-test
                Priority:     0
                Node:         k8s-node01/192.168.0.11
                Start Time:   Sun, 20 Nov 2022 06:08:28 -0500
                Labels:       app=nginx-nfs-pod
                            pod-template-hash=7d5ff8978c
                Annotations:  <none>
                Status:       Running
                IP:           10.244.1.20
                ...


            # 查看 pod 日志
            $ kubectl logs nginx-nfs-deployment-7d5ff8978c-xmrvk -n nginx-test
            
            # 集群内访问 nginx(Pod 的虚拟 IP,外部无法访问)  
            $ curl http://10.244.0.10/test.html

                <p>K8s Nginx - HTML page</p>

    注:静态 PV 的每一个目录,都需要在 NFS Server 上创建一个对应的共享目录,每个共享目录本身是由 NFS Server 管理的,NFS client 不能修改这个目录本身,只能根据指定的权限操作目录下的子文件夹和文件。

posted @ 2022-11-23 20:50  垄山小站  阅读(889)  评论(0编辑  收藏  举报