挂载本地目录到k8s的pod实现持久化存储
本地目录实现持久化存储
容器是无状态的,每次重启都是新的进程,但是我们需要将一些状态数据如配置、用户数据等存到本地来方便新的容器可以拿到历史状态。先创建一个目录来存放数据,并且挂载到minikube虚拟机内(不是pod里面)。注意要新开一个终端来调用,这个命令会阻塞,不能中断。
=> mkdir storage
=> minikube mount storage:/data
可以测试一下是否成功了
=> echo aaa > storage/test.txt
=> minikube ssh 'cat /data/test.txt'
aaa
然后创建一个持久化存储卷(PersistentVolume)的配置,挂载本机目录,注意是minikube虚拟机的目录,所以是/data
apiVersion: v1 # 版本信息
kind: PersistentVolume # 这个是一个持久化存储卷
metadata:
name: test-local-pv # 元数据定义名称
spec:
capacity:
storage: 1Ki # 定义容量1KB,定义不定义都一样,因为目录挂载的方式取决于原始目录的容量
storageClassName: local # 定义这个为了和pvc匹配,否则会绑定不了
persistentVolumeReclaimPolicy: Retain # pvc被删除时,pv的回收策略,retain为保留pv,手动清理
accessModes:
- ReadWriteMany # 表示该卷可以被多个节点以读写的方式挂载。这意味着多个 Pod 可以同时访问这个卷并进行读写操作。
hostPath:
path: /data
pv是不能直接给pod访问和挂载使用的,需要使用pvc才能给到pod使用,所以写一个pvc的配置
apiVersion: v1 # 版本
kind: PersistentVolumeClaim # pvc类型,请求持久化存储卷
metadata:
name: test-local-pvc
spec:
accessModes:
- ReadWriteMany # 这个模式表示多个节点可以同时读写这个存储。
resources:
requests:
storage: 1Ki # 要和pv匹配,不然会无法绑定
volumeName: test-local-pv # 指定要绑定的pv的名字
storageClassName: local # 同样要和pv一致
启动pv和pvc,要看一下是否启动和绑定成功
kubectl apply -f test-pv.yaml
kubectl apply -f test-pvc.yaml
pv和pvc的绑定关系使用describe看起来会更清晰一点
=> kubectl describe pv test-local-pv
Name: test-local-pv
Labels: <none>
Annotations: pv.kubernetes.io/bound-by-controller: yes
Finalizers: [kubernetes.io/pv-protection]
StorageClass: local
Status: Bound
Claim: default/test-local-pvc
Reclaim Policy: Retain
Access Modes: RWX
VolumeMode: Filesystem
Capacity: 1Ki
Node Affinity: <none>
Message:
Source:
Type: HostPath (bare host directory volume)
Path: /data
HostPathType:
Events: <none>
=> kubectl describe pvc test-local-pvc
Name: test-local-pvc
Namespace: default
StorageClass: local
Status: Bound
Volume: test-local-pv
Labels: <none>
Annotations: pv.kubernetes.io/bind-completed: yes
Finalizers: [kubernetes.io/pvc-protection]
Capacity: 1Ki
Access Modes: RWX
VolumeMode: Filesystem
Used By: <none>
Events: <none>
可以看到pv和pvc绑定成功
下面要修改一下test.go,让我们的服务端可以读取和写入存储的一个文件
package main
import (
"fmt"
"io/ioutil"
"net/http"
"os"
)
func main() {
fmt.Println("start main")
hostName := os.Getenv("HOSTNAME")
// 正常请求根目录读取这个文件返回内容
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
contentBytes, err := ioutil.ReadFile("/data/test.txt")
outStr := ""
if err != nil {
outStr = err.Error()
} else {
outStr = string(contentBytes)
}
fmt.Fprintf(w, "hello, world, %s, txt: '%s'\n", hostName, outStr)
})
// 保存文件,取xxx:xxx/save?content=xxx来存入
http.HandleFunc("/save", func(w http.ResponseWriter, r *http.Request) {
r.ParseForm()
tmp := r.Form["content"]
inputText := ""
if len(tmp) > 0 {
inputText = tmp[0]
}
ioutil.WriteFile("/data/test.txt", []byte(inputText), 0644)
fmt.Fprintf(w, "%s", inputText)
})
http.HandleFunc("/exit", func(w http.ResponseWriter, r *http.Request) {
fmt.Println("recieve exit, bye!")
os.Exit(0)
})
err := http.ListenAndServe(":7878", nil)
if err != nil {
panic(err)
}
os.Exit(0)
}
修改完重新做一个镜像加载到minikube中,然后要修改一下deployment的配置来让pod使用pvc作为存储
apiVersion: apps/v1 # 版本,虽然不知道为什么,但是要写apps/v1
kind: Deployment # 选deployment
metadata:
name: test-dep # 给deployment定义名字,创建的pod会以此为前缀
spec: # deployment的要求
strategy:
type: RollingUpdate # 策略为滚动更新
rollingUpdate:
maxUnavailable: 1 # 最多只能停止1个pod
maxSurge: 1 # 在更新过程中,最多可以多创建多少个 Pod,本身停止了1个,创建1个,这里设置1会停1个创建2个
replicas: 3 # pods启动3个
selector: # 定义deployment管理的pod选择器
matchLabels: # 要跟下面的template中一样,不一样会报错
app: test
template: # 定义deployment管理的pod
metadata:
labels: # 定义标签,要和deployment中一样
app: test
spec: # pod的定义
volumes: # 定义要求的存储卷
- name: test-volume # 起个名字给pod使用
persistentVolumeClaim: # 对应的pvc
claimName: test-local-pvc
containers:
- name: test
image: test-con:latest
imagePullPolicy: Never # 使用本地镜像,不从远端拉取
command: ["go"]
args: ["run", "/root/test.go"]
volumeMounts: # 挂载到当前容器
- mountPath: /data # 挂载目录
name: test-volume # 使用的volumes,是上面定义的名字
readinessProbe: # 就绪探针,什么时候可以开始接收流量,不会重启pod
httpGet:
path: /
port: 7878
timeoutSeconds: 1 # 探测超时时间5s
initialDelaySeconds: 5 # 第一次探测的等待时间
periodSeconds: 5 # 探测周期
livenessProbe: # 存活探针,什么时候需要杀掉这个pod,如果启动一直失败就会杀掉pod重启一个
httpGet:
path: /
port: 7878
timeoutSeconds: 1 # 探测超时时间5s
initialDelaySeconds: 20 # 第一次探测的等待时间
periodSeconds: 5 # 探测周期
failureThreshold: 10 # 错误阈值,超过这个阈值将会重启pod
生效一下这个配置文件,查看pod的pvc使用情况
kubectl apply -f test-dep.yaml
查看pvc里面更能看出来挂载情况
=> kubectl describe pvc test-local-pvc
Name: test-local-pvc
Namespace: default
StorageClass: local
Status: Bound
Volume: test-local-pv
Labels: <none>
Annotations: pv.kubernetes.io/bind-completed: yes
Finalizers: [kubernetes.io/pvc-protection]
Capacity: 1Ki
Access Modes: RWX
VolumeMode: Filesystem
Used By: test-dep-6dc4bdc5bd-7mf7w
test-dep-6dc4bdc5bd-ftv24
test-dep-6dc4bdc5bd-w8zxf
Events: <none>