pv和pvc存储 - nfs

2022-05-02  本文已影响0人  Robin92

RC、RS、Deployment 可以控制副本数量,但需要所有 Pod 副本使用的数据与 Pod 自身的生命周期分离,这就需要用额外的空间存储这些数据。

PV(Persistent Volume),是一个全局资源,在创建之初定义了有多大的存储能力。
PVC(Persistent Volume Claim),是 Namespace 中的资源,描述的是对 PV 的一个使用请求。
每个需要使用的 Pod 需要有一个 PVC 与全局的 PV 相绑定,并且声明自己使用 PV 的多大空间。

安装 nfs

每个节点需要有 nfs 的客户端,nfs 的服务端可以只有一个。(C/S模式)

yum install nfs-utils -y # 所有节点安装客户端

配置 nfs 访问

以下配置,共享 /data 目录,使用 192.168.208.0/24 段访问,读写,同步、指定不做 root 用户的 uid 映射,不做其他用户的 uid 映射。

# vim /etc/exports
/data 192.168.208.0/24(rw,async,no_root_squash,no_all_squash) # 

重启 rpcbind 和 nfs

systemctl restart rpcbind
systemctl restart nfs #
systemctl enable nfs # 记得设置为开机自启动

nfs 启动状态错误


image.png

原来是因为括号中是逗号而不是空格,教程上没体现到变动。以上代码已经是修正版的了

验证安装成功

在任何一台机器上通过 showmount -e 192.168.208.130 查看是否能查到 nfs。

image.png

创建 pv

# pv/test-pv.yml
apiVersion: v1
kind: PersistentVolume
metadata: 
  name: test
  labels:
    type: test
spec:
  capacity:
    storage: 10Gi # 提供 10G 的存储
  accessModes:
    - ReadWriteMany # 允许多个 Pod 同时读写
  persistentVolumeReclaimPolicy: Recycle # pvc 的策略,允许回收
  nfs:
    path: "/data/k8s"
    server: 192.168.208.130 # 
    readOnly: false # 取消只读,就是可写
kubectl create -f pv/test-pv.yml # persistentvolume "test" created

以下通过更改名字和空间大小,创建了三个 pv,查看 pv:

image.png

创建 pvc

# test-pvc.yml
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: nfs # pvc 的名字
spec:
  accessModes:
    - ReadWriteMany # 模式允许多人读写访问
  resources:
    requests:
      storage: 1Gi # 需求资源 1G
image.png

更新 pvc 请求资源量,pvc 会对应使用容量最小能用的 pv 进行绑定。

image.png

实践 Pod 使用 nfs pv/pvc

  1. 写了一个向服务器文件目录中上传文件以及获取文件列表的程序
  2. 创建 k8s 资源配置文件,使用 pv、pvc、svc、deployment 资源
  3. 通过 postman 上传文件,通过浏览器查看列表

制作程序:显示文件列表及上传文件

用 go 写一个提供上传/访问文件系统的程序,代码如下:

package main

import (
    // ...
    "github.com/gin-gonic/gin"
)

var (
    filedir = flag.String("d", "./data", "the file dir for server")
    port    = flag.String("p", "8080", "the port which server is running on")
)

func main() {
    flag.Parse()

    abs, err := filepath.Abs(*filedir)
    if err != nil {
        abs = *filedir
    }
    fmt.Println("the file dir:", abs)
    fmt.Println("runing on port:", *port)
    fmt.Println()

    app := gin.New()
    app.GET("/", func(c *gin.Context) {
        c.Redirect(301, "/fs")
    })
    app.StaticFS("/fs", gin.Dir(*filedir, true)) // API:列表指定目录的内容
    app.POST("/upload", func(c *gin.Context) { // API 上传文件
        datafile, header, err := c.Request.FormFile("datafile")
        if err != nil {
            c.AbortWithError(400, err)
            return
        }
        defer datafile.Close()

        fd, err := os.Create(filepath.Join(*filedir, header.Filename))
        if err != nil {
            c.AbortWithError(500, err)
            return
        }
        defer fd.Close()

        io.Copy(fd, datafile)
    })
    if err := app.Run(":" + *port); err != nil {
        fmt.Println("service run failed:", err.Error())
    }
}

使用 CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build . 交叉编译出 linux 上二进制文件。(输出 fs-svr)

构建 Docker

创建 Dockerfile:

FROM 192.168.208.130:5000/alpine:latest
COPY fs-svr /service/
WORKDIR /service
RUN mkdir /data /service/data # RUN 在 build 阶段执行
EXPOSE 9000
ENTRYPOINT ["./fs-svr"] # ENTRYPOINT 的命令在 run 阶段执行,可以和 CMD 拼接到一块
CMD ["-p", "9000", "-d", "/data"] # CMD 在 run 阶段执行,可以认为是默认的启动命令/参数,可以被传入的命令/参数覆盖

注意:
这里使用 alpine 作为基础镜像,注意先从网上下载这个基础镜像,然后改 tag 推到私有镜像库中。
这里用服务名为 fs-svr,并且在服务本地目录位置有个 data 目录是服务内部默认用的目录

通过一系列操作将镜像构建出来并 push 到私有库中:

docker build . # 构建  
docker tag 676d70bef0a7 192.168.208.130:5000/fs-svr:0.0.1 # 打私有仓库的标签
docker push 192.168.208.130:5000/fs-svr:0.0.1 # 提交到私有仓库

创建 k8s 资源

编辑 k8s.yml 文件。创建 deployment 和 service 资源

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: fs-deployment
spec:
  replicas: 3
  template:
    metadata:
      labels:
        app: fs-svr 
    spec:
      containers:
        - name: fs-svr 
          image: 192.168.208.130:5000/fs-svr:0.0.1
          ports:
            - containerPort: 9000
          volumeMounts:
            - name: fsdata     # 绑定 template 定义的 volumes
              mountPath: /data/
      volumes:                    # 与 containers 并级
        - name: fsdata            # 自定义一个名称,必须小写,用于 containers[].volumeMounts[].name
          persistentVolumeClaim:  # 
            claimName: nfs2       # 使用的 pvc 的名字
---
apiVersion: v1
kind: Service # 创建 service,允许通过宿主机访问
metadata:
  name: fs-service
spec:
  type: NodePort # 端口映射的方式,还有其他支持
  ports:
    - port: 80            # cluster IP 的端口
      nodePort: 30000     # 宿主机的端口(宿主机的端口在配置文件中指定)
      targetPort: 9000    # pod 地址的端口
  selector:       # 为哪些 Pod 做负载均衡是通过标签选择器选择的
    app: fs-svr   # 选择 app: fs-svr 的 Label

通过 kubectl create -f k8s.yaml 创建资源。创建的资源列表:

所有资源

测试上传和显示

curl --location --request POST 'k8s-master:30000/upload' \
--form 'datafile=@"/I:/workspace/k8s/kube-dns-svc.yml"'

其他技巧

限制重启后 deployment 资源的数量

如上文中下图是因为重复了多次 kubectl apply 资源操作,导致遗留了很多无用的资源信息。

所有资源

可以通过 deployment.spec.revisionHistoryLimit 来进行限制:

➜  ~ kubectl explain deployment.spec.revisionHistoryLimit
FIELD: revisionHistoryLimit <integer>

DESCRIPTION:
     The number of old ReplicaSets to retain to allow rollback. This is a
     pointer to distinguish between explicit zero and not specified.

shell 操作没有提示

参考 k8s命令自动补全(只是 bash 使用的,在 zsh 或者说 Oh-my-zsh 上不好用)

kubectl api-resources

kubectl api-resources 可以显示所有 k8s 资源,但目前由于集群版本问题不显示。后面再了解。

上一篇下一篇

猜你喜欢

热点阅读