目录
一、服务发布
1.service
(1)service和pod之间的关系
(2) service内部服务创建访问
(3)service访问外部服务
(4)基于域名访问外部
(5)service的访问方式
2.服务发现ingress
(1)什么是ingress
(2)安装helm和ingress-nginx
*1)安装helm
*2)下载ingress-nginx安装包
*3)编辑ingress配置文件
*4)启动
(3)使用ingress-nginx
(4)多域名配置
二、配置管理
1.ConfigMap
(1)创建ConfigMap
(2)使用ConfigMap
2.secret密文
(1)直接创建
(2)拉取镜像密文校验
3.SubPath解决configMap数据挂载覆盖的问题
4.配置的热更新
5.不可变的secret和configMap
三、持久化存储
1.volumes
(1)HostPath
(2)EmptyDir
2.NFS挂载
(1)安装NFS
(2)将NFS挂载到容器中
3.pv和pvc(重要)
(1)什么是pv和pvc
(2)生命周期
(3)创建pv,pvc,并关联pod,实现持久化数据
*1)创建pv
*2)创建pvc
*3)pvc和pod关联,实现容器数据持久化
(4) StorageClass动态申领
四、感谢支持
一、服务发布
1.service
负责 同层级/内部服务网络 的通信。
(1)service和pod之间的关系
- 一个子节点node = kubelet + kube-proxy + container runtime(容器运行时环境)+pod
- pod = pause容器(此处up是10.244.36.107) + 具体运行的项目(此处为nginx,nginx自带端口80)+ pod的基本信息(名称、标签)
- 这里有一个要对应的是,一个node宿主机的自己的ip和端口,和pod中的pause容器的ip和端口是对应上的
- master = service + EndPoint
- service中本身有一个pause容器并且暴露一个端口,同时因为service本身是一个服务并且部署在master宿主机上。因此我们可以通过宿主机ip + 宿主机真实端口访问service。也可以通过service的pause容器的ip + pause中的端口访问service。
- 一个service会生成一个endpoint,endpoint中会记录子宿主机中的pause容器的ip和端口
- service经过endpoint知道子宿主机的pause容器和端口后,通过iptables能够访问到子宿主机的真实机器,发送请求到子宿主机的kube-proxy上。并且转给自己的pod
(2) service内部服务创建访问
cd /opt/k8s/services/
vim nginx-svc.yaml
###############################################
apiVersion: v1
kind: Service #资源类型是service
metadata:
name: nginx-svc #service名称
labels: #service自己本身的标签
app: nginx
spec:
selector: #匹配哪些pod会被该service代理
app: nginx-deploy #所有匹配到这个标签的pod都可以通过该service进行访问
ports: #端口映射
- port: 80 #service自己的端口,k8s内网ip访问时使用
targetPort: 80 #目标pod的端口
name: web #为端口起个名字
type: NodePort #随机启动一个端口(30000-32767),映射到ports中的端口,该端口是直接绑定在宿主node上面,且集群中每一个宿主node都会绑定这个端口。也可以将服务暴露给外部访问,但是这种方式实际生产环境不推荐,因为效率低,而且service是四层负载
###############################################
# 创建 service
kubectl create -f nginx-svc.yaml
# 查看
kubectl get svc
kubectl get po -o wide
#创建其他 pod 通过 service name 进行访问
kubectl run -it --image busybox:1.28.4 dns-test --restart=Never --rm /bin/sh
wget http://nginx-svc
cat index.html
(3)service访问外部服务
不使用selector属性,而是自己建endpoint。
先新建一个service:
/opt/k8s/services
vim nginx-svc-external.yaml
#####################################################################
apiVersion: v1
kind: Service #资源类型是service
metadata:
name: nginx-svc-external #service名称
labels: #service自己本身的标签
app: nginx
spec:
ports: #端口映射
- port: 80 #service自己的端口,k8s内网ip访问时使用
targetPort: 80 #目标pod的端口
name: web #为端口起个名字
type: ClusterIP
#####################################################################
#启动,可以看到,我们这个没有启动endpoint,因为我们没有指定 selector 属性,需要自己建endpoint
kubectl create -f nginx-svc-external.yaml
kubectl get svc
kubectl get ep
再新建一个endpoint:
vim nginx-svc-external.yaml
#########################################################
apiVersion: v1
kind: Endpoints
metadata:
labels:
app: nginx # 与 service 一致
name: nginx-svc-external # 与 service 一致
namespace: default # 与 service 一致
subsets:
- addresses:
- ip: 120.78.159.117 # 目标 ip 地址
ports: # 与 service 一致
- name: web
port: 80
protocol: TCP
#########################################################
#启动
kubectl create -f nginx-svc-external.yaml
kubectl get svc
kubectl get ep
kubectl describe ep nginx-svc-external
#测试
kubectl run -it --image busybox:1.28.4 dns-test --restart=Never --rm /bin/sh
wget http://nginx-svc-external
可以看到,我们请求的是内部的service,但是实际上调用到了外部的地址:
(4)基于域名访问外部
vim nginx-svc-externalname.yaml
###########################################################
apiVersion: v1
kind: Service
metadata:
labels:
app: wolfcode-external-domain
name: wolfcode-external-domain
spec:
type: ExternalName
externalName: www.wolfcode.cn
###########################################################
#启动
kubectl create -f nginx-svc-externalname.yaml
kubectl get svc
#测试
kubectl run -it --image busybox:1.28.4 dns-test --restart=Never --rm /bin/sh
wget wolfcode-external-domain
(5)service的访问方式
-
ClusterIP:只能在集群内部使用,不配置类型的话默认就是 ClusterIP
-
ExternalName:返回定义的 CNAME 别名,可以配置为域名
-
NodePort:会在所有安装了 kube-proxy 的节点都绑定一个端口,此端口可以代理至对应的 Pod,集群外部可以使用任意节点 ip + NodePort 的端口号访问到集群中对应 Pod 中的服务。端口范围:30000~32767。不推荐
-
LoadBalancer:使用云服务商(阿里云、腾讯云等)提供的负载均衡器服务,不推荐
2.服务发现ingress
(1)什么是ingress
看调用流程会发现,ingress充当的是以前nginx的角色。但是他对nginx额外做了一层封装和抽象。我们可以简单的吧ingress认为是一个抽象,然后nginx是对ingress的一个实现。
(2)安装helm和ingress-nginx
helm是k8s里面的包管理工具,类似于Java中的Maven。
*1)安装helm
#下载、解压二进制文件
cd /opt/k8s/
mkdir helm
cd helm/
wget https://get.helm.sh/helm-v3.2.3-linux-amd64.tar.gz
tar -zxvf helm-v3.2.3-linux-amd64.tar.gz
cd /opt/k8s/
chmod +x helm/
#将配置文件拷贝到指定目录
cd linux-amd64/
cp helm /usr/local/bin/
#查看helm
cd ~
helm version
#添加helm仓库
*2)下载ingress-nginx安装包
如果你下面这些能正常运行最好:
# 添加仓库
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
# 查看仓库列表
helm repo list
# 搜索 ingress-nginx
helm search repo ingress-nginx
# 下载安装包
helm pull ingress-nginx/ingress-nginx --version 4.2.2
# 解压
cd /opt/k8s/helm
tar -zxvf ingress-nginx-4.2.2.tgz
如果你的服务器被墙了,一直添加不了仓库,手动去直接下载ingress-nginx,下载地址如下:
Release helm-chart-4.2.2 · kubernetes/ingress-nginx · GitHub
上传到/helm目录下,然后解压即可:
*3)编辑ingress配置文件
cd ingress-nginx
vim values.yaml
################################################################
#controller下的image改动
#registry: registry.cn-hangzhou.aliyuncs.com
#image: google_containers/nginx-ingress-controller
#tag: "v1.3.0"
#将digest,digestChroot 两行注释掉
#66行dnsPolicy修改:
dnsPolicy: ClusterFirstWithHostNet
#89行hostNetwork改成true
hostNetwork: true
#194行kind修改
#kind: DaemonSet
#292行nodeSelector新增ingress
ingress: "true" # 增加选择器,如果 node 上有 ingress=true 就部署
#503行type修改:
type: ClusterIP
#541行patch下面的image改动
#registry: registry.cn-hangzhou.aliyuncs.com
#image: google_containers/kube-webhook-certgen
#tag: "v1.3.0"
#将digest注释掉
#596行修改admissionWebhooks:
enabled: false
################################################################
controller下的image改动:
541行patch下面的image改动:
194行kind修改:
292行nodeSelector新增ingress:
89行hostNetwork改成true:
66行dnsPolicy修改:
503行type修改:
596行修改admissionWebhooks:
*4)启动
#为ingress创建命名空间
kubectl create ns ingress-nginx
# 为主宿主机节点上加标签,我的是kubernete140
kubectl label node kubernete140 ingress=true
# 安装 ingress-nginx
helm install ingress-nginx . -n ingress-nginx
# 查看
helm -n ingress-nginx ls -a
# 我们会发现主节点找不到,因为主节点有污点,不推荐放在主节点
kubectl get po -n ingress-nginx
# 我们给141的子节点新增标签
kubectl label no kubernete141 ingress=true
# 查看,显示成功
kubectl get po -n ingress-nginx
kubectl get po -n ingress-nginx -o wide
(3)使用ingress-nginx
cd /opt/k8s/
mkdir ingress
cd ingress/
#配置文件
vim xupeng-ingress.yaml
##########################################################
apiVersion: networking.k8s.io/v1
kind: Ingress # 资源类型为 Ingress
metadata:
name: xupeng-nginx-ingress
annotations:
kubernetes.io/ingress.class: "nginx"
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
rules: # ingress 规则配置,可以配置多个
- host: k8s.wolfcode.cn # 域名配置,可以使用通配符 *
http:
paths: # 相当于 nginx 的 location 配置,可以配置多个
- pathType: Prefix # 路径类型,按照路径类型进行匹配 ImplementationSpecific 需要指定 IngressClass,具体匹配规则以 IngressClass 中的规则为准。Exact:精确匹配,URL需要与path完全匹配上,且区分大小写的。Prefix:以 / 作为分隔符来进行前缀匹配
backend:
service:
name: nginx-svc # 代理到哪个 service
port:
number: 80 # service 的端口
path: /api # 等价于 nginx 中的 location 的路径前缀匹配
##########################################################
#启动
kubectl create -f xupeng-ingress.yaml
#查看
kubectl get ingress
kubectl get po -o wide
#查看ingress-nginx日志
kubectl logs -f nginx-deploy-776dbd5599-v6kpb
在windows机器中,配置dns,路径:
C:\Windows\System32\drivers\etc\hosts
#此处的ip不是master宿主机,而是子宿主机
192.168.200.141 k8s.wolfcode.cn
测试:
(4)多域名配置
apiVersion: networking.k8s.io/v1
kind: Ingress # 资源类型为 Ingress
metadata:
name: wolfcode-nginx-ingress
annotations:
kubernetes.io/ingress.class: "nginx"
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
rules: # ingress 规则配置,可以配置多个
- host: k8s.wolfcode.cn # 域名配置,可以使用通配符 *
http:
paths: # 相当于 nginx 的 location 配置,可以配置多个
- pathType: Prefix # 路径类型,按照路径类型进行匹配 ImplementationSpecific 需要指定 IngressClass,具体匹配规则以 IngressClass 中的规则为准。Exact:精确匹配,URL需要与path完全匹配上,且区分大小写的。Prefix:以 / 作为分隔符来进行前缀匹配
backend:
service:
name: nginx-svc # 代理到哪个 service
port:
number: 80 # service 的端口
path: /api # 等价于 nginx 中的 location 的路径前缀匹配
- pathType: Exec # 路径类型,按照路径类型进行匹配 ImplementationSpecific 需要指定 IngressClass,具体匹配规则以 IngressClass 中的规则为准。Exact:精确匹配>,URL需要与path完全匹配上,且区分大小写的。Prefix:以 / 作为分隔符来进行前缀匹配
backend:
service:
name: nginx-svc # 代理到哪个 service
port:
number: 80 # service 的端口
path: /
- host: api.wolfcode.cn # 域名配置,可以使用通配符 *
http:
paths: # 相当于 nginx 的 location 配置,可以配置多个
- pathType: Prefix # 路径类型,按照路径类型进行匹配 ImplementationSpecific 需要指定 IngressClass,具体匹配规则以 IngressClass 中的规则为准。Exact:精确匹配>,URL需要与path完全匹配上,且区分大小写的。Prefix:以 / 作为分隔符来进行前缀匹配
backend:
service:
name: nginx-svc # 代理到哪个 service
port:
number: 80 # service 的端口
path: /
二、配置管理
1.ConfigMap
明文键值对的配置。
(1)创建ConfigMap
#创建文件夹
cd /opt/k8s/
mkdir config
cd config/
#新增两个配置文件
mkdir test
cd test/
touch db.properties
touch redis.properties
##################################
文件内容随便:
username=aa
port: 111
##################################
#基于文件夹方式创建
cd /opt/k8s/config/
kubectl create configmap test-dir-config --from-file=test/
kubectl get cm
kubectl describe cm test-dir-config
#基于单个文件创建
cd /opt/k8s/config/
vim application.yaml
###################################
spring:
application:
name: test-app
server:
port: 8080
###################################
kubectl create cm spring-boot-test-yaml --from-file=/opt/k8s/config/application.yaml
kubectl get cm
kubectl describe cm spring-boot-test-yaml
#基于单文件重命名方式创建
kubectl create cm spring-boot-test-alises-yaml --from-file=app.yml=/opt/k8s/config/application.yaml
kubectl describe cm spring-boot-test-alises-yaml
#直接写入key-value方式创建
kubectl create cm test-key-value-config --from-literal=username=root --from-literal=password=admin
kubectl describe cm test-key-value-config
(2)使用ConfigMap
*1)方法一:不推荐
#先创建一个cm
kubectl create cm test-env-config --from-literal=JAVA_OPTS_TEST='-Xms512m -Xmx512m' --from-literal=APP_NAME=springboot-env-test
kubectl describe cm/test-env-config
#运行一个pod
vim env-test-cm.yaml
#############################################
apiVersion: v1
kind: Pod
metadata:
name: test-env-cm
spec:
containers:
- name: env-test
image: alpine
command: ["/bin/sh","-c","env;sleep 3600"]
imagePullPolicy: IfNotPresent
env:
- name: JAVA_VM_OPTS
valueFrom:
configMapKeyRef:
name: test-env-config #configMap的名字
key: JAVA_OPTS_TEST #从name的cm中获取名字为key的value,将其赋值给本地变量JAVA_VM_OPTS
restartPolicy: Never
#############################################
#启动
kubectl create -f env-test-pod.yaml
#查看
kubectl logs -f test-env-cm
可以看到我们已经使用了配置文件中的value:
*2)方法二:数据卷方式挂载cm
#删除上面的pod
kubectl delete po test-env-cm
#配置文件
vim file-test-pod.yaml
#################################################################
apiVersion: v1
kind: Pod
metadata:
name: test-configfile-cm
spec:
containers:
- name: config-test
image: alpine
command: ["/bin/sh","-c","sleep 3600"]
imagePullPolicy: IfNotPresent
env:
- name: JAVA_VM_OPTS
valueFrom:
configMapKeyRef:
name: test-env-config #configMap的名字
key: JAVA_OPTS_TEST #从name的cm中获取名字为key的value,将其赋值给本地变量JAVA_VM_OPTS
volumeMounts: #加载数据卷
- name: db-config #加载volumes中哪个数据卷
mountPath: "/usr/local/mysql/conf" #想要将数据卷中的文件加载到哪个目录下
readOnly: true #只读
volumes: #数据卷挂载:configmap,secret
- name: db-config
configMap: #数据卷类型为configMap
name: test-dir-config #cm的名字,必须要跟想加载的cm一致
items: #对cm中的key进行映射,如果不指定,默认会将cm中所有key全部转化为一个同名的文件
- key: "db.properties" #cm中的key
path: "db.properties" #将该key的值转化为文件
restartPolicy: Never
#################################################################
#启动
kubectl create -f file-test-pod.yaml
#查看
kubectl get po
#进入容器查看配置文件有没有挂载
kubectl exec -it test-configfile-cm -- sh
cd /usr/local/mysql/conf/
cat db.properties
2.secret密文
使用场景:
(1)直接创建
密文创建,有特殊字符需要加单引号:用得不多
#直接创建密文
kubectl create secret generic ori-secret --from-literal=username=admin --from-literal=password='ss@!3~/\'
#加解密方式
echo 'ss@!3~/\' | base64
echo 'c3NAITN+L1wK' | base64 --decode
(2)拉取镜像密文校验
# 生成密文
kubectl create secret docker-registry harbor-secret --docker-username=admin --docker-password=wolfcode --docker-email=aa@163.com --docker-server=192.168.113.122:8858
kubectl edit secret/harbor-secret
docker image
# 打包nginx上传镜像仓库
docker tag nginx:1.9.1 192.168.113.122:8858/opensource/nginx:1.9.1
docker login -uadmin 192.168.113.122:8858
docker push 192.168.113.122:8858/opensource/nginx:1.9.1
# 编写配置文件,拉取镜像需要校验密文
vim private-image-pull.yaml
#########################################################################
apiVersion: v1
kind: Pod
metadata:
name: private-image-pull
spec:
imagePullSecrets: #配置登录docker仓库的secret
- name: harbor-secret
containers:
- name: nginx
image: 192.168.113.122:8858/opensource/nginx:1.9.1
command: ["/bin/sh","-c","sleep 3600"]
imagePullPolicy: IfNotPresent
env:
- name: JAVA_VM_OPTS
valueFrom:
configMapKeyRef:
name: test-env-config #configMap的名字
key: JAVA_OPTS_TEST #从name的cm中获取名字为key的value,将其赋值给本地变量JAVA_VM_OPTS
volumeMounts: #加载数据卷
- name: db-config #加载volumes中哪个数据卷
mountPath: "/usr/local/mysql/conf" #想要将数据卷中的文件加载到哪个目录下
readOnly: true #只读
volumes: #数据卷挂载:configmap,secret
- name: db-config
configMap: #数据卷类型为configMap
name: test-dir-config #cm的名字,必须要跟想加载的cm一致
items: #对cm中的key进行映射,如果不指定,默认会将cm中所有key全部转化为一个同名的文件
- key: "db.properties" #cm中的key
path: "db.properties" #将该key的值转化为文件
restartPolicy: Never
#########################################################################
#启动
kubectl create -f private-image-pull.yaml
kubectl get po -o wide
3.SubPath解决configMap数据挂载覆盖的问题
目的:
我们在一个po里面复制出来他的配置文件,然后吧配置文件用configMap保存,然后用数据卷映射到容器里的配置文件,目的是想直接改映射文件中的内容,同步修改容器中的配置。
问题:
configMap的映射文件一旦挂载数据卷,原本容器里有很多文件的,挂载后就只剩下我们挂载的文件了。其他的文件都没了,被覆盖了
复现如下:
#先找一个运行的po,找到里面的配置文件,我们这边复制nginx配置
kubectl get po
kubectl exec -it nginx-deploy-776dbd5599-v6kpb -- sh
cd /etc/nginx
cat nginx.conf
#将配置文件复制出来,作为一个configMap保存
cd /opt/k8s/config/
vim config-nginx
###########################################
user nginx;
worker_processes 1;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
#tcp_nopush on;
keepalive_timeout 65;
#gzip on;
include /etc/nginx/conf.d/*.conf;
}
###########################################
#设置成cm
kubectl create cm nginx-conf-cm --from-file=./config-nginx
kubectl describe cm nginx-conf-cm
#编辑deploy
kubectl edit deploy nginx-deploy
###########################################
#新增数据卷挂载相关内容
spec:
containers:
- image: nginx:1.7.9
imagePullPolicy: IfNotPresent
name: nginx
resources:
limits:
cpu: 100m
memory: 128Mi
requests:
cpu: 100m
memory: 128Mi
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
volumeMounts:
- mountPath: /etc/nginx
name: nginx-conf
dnsPolicy: ClusterFirst
restartPolicy: Always
schedulerName: default-scheduler
securityContext: {}
terminationGracePeriodSeconds: 30
volumes:
- configMap:
items:
- key: config-nginx
path: config-nginx
name: nginx-conf-cm
name: nginx-conf
###########################################
#等到po重启好了,进入容器
kubectl get po
kubectl exec -it nginx-deploy-564b4bc87b-4k4jc -- sh
cd /etc/nginx
ls
解决方案:
修改路径path,并且添加subPath。注意,路径path的etc前面不要/
spec:
containers:
- command:
- /bin/sh
- -c
- 'nginx daemon off;sleep 3600 '
image: nginx:1.7.9
imagePullPolicy: IfNotPresent
name: nginx
resources:
limits:
cpu: 100m
memory: 128Mi
requests:
cpu: 100m
memory: 128Mi
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
volumeMounts:
- mountPath: /etc/nginx/nginx.conf
name: nginx-conf
subPath: etc/nginx/nginx.config
dnsPolicy: ClusterFirst
restartPolicy: Always
schedulerName: default-scheduler
securityContext: {}
terminationGracePeriodSeconds: 30
volumes:
- configMap:
defaultMode: 420
items:
- key: config-nginx
path: etc/nginx/nginx.config
name: nginx-conf-cm
name: nginx-conf
可以看到,那些被覆盖的文件又出现了:
注意事项:
使用SubPath不支持热更新!!
4.配置的热更新
我们通常会将项目的配置文件作为 configmap 然后挂载到 pod,那么如果更新 configmap 中的配置,会不会更新到 pod 中呢?
这得分成几种情况:
- 默认方式:会更新,更新周期是更新时间 + 缓存时间。但是默认情况挂载后会覆盖原来的文件
- subPath:不会覆盖原来文件,但是他不支持热更新
- 变量形式:如果 pod 中的一个变量是从 configmap 或 secret 中得到,同样也是不会更新的
最终实现热更新的思路:
使用默认方式,将配置文件挂载到一个不存在的目录,避免目录的覆盖,然后再利用软连接的形式,将该文件链接到目标位置
对于默认方式的更新方式有两种:
直接edit更新:
kubectl edit cm test.config
使用replace替换:
以往我们使用replace都是变更yaml配置文件,然后配置文件替换就行。但是cm中没有配置文件的概念,实现方式是: --dry-run
该参数的意思打印 yaml 文件,但不会将该文件发送给 apiserver,再结合 -o yaml 输出 yaml 文件就可以得到一个配置好但是没有发给 apiserver 的文件,然后再结合 replace 监听控制台输出得到 yaml 数据即可实现替换
#最后的-f-:管道前面会输出一个内容,这个输出的内容会作为输入,发送给k8s执行
kubectl create cm test-dir-config --from-file=./test/ --dry-run -o yaml | kubectl replace -f-
5.不可变的secret和configMap
对于一些敏感服务的配置文件,在线上有时是不允许修改的,此时在配置 configmap 时可以设置 immutable: true 来禁止修改
kubectl edit cm test-dir
#前面不需要空格,就是第一层级添加即可
immutable: true
三、持久化存储
1.volumes
(1)HostPath
将节点上的文件或目录挂载到 Pod 上,此时该目录会变成持久化存储目录,即使 Pod 被删除后重启,也可以重新加载到该目录,该目录下的文件不会丢失
cd /opt/k8s/
mkdir volumes
cd volumes/
vim volume-test-pd.yaml
##################################################
apiVersion: v1
kind: Pod
metadata:
name: test-pd
spec:
containers:
- image: nginx
name: nginx-volume
volumeMounts:
- mountPath: /test-pd # 挂载到容器的哪个目录
name: test-volume # 挂载哪个 volume
volumes:
- name: test-volume
hostPath: #与主机共享目录,加载主机中的指定目录到容器中
path: /data # 节点中的目录
type: DirectoryOrCreate # 检查类型,在挂载前对挂载目录做什么检查操作,有多种选项,默认为空字符串,不做任何检查
##################################################
#启动,并查看在哪台机器运行
kubectl create -f volume-test-pd.yaml
kubectl get po -o wide
#来到对应的机器
cd /data/
touch index.html
#回到主机器,进入容器内部
kubectl exec -it test-pd -- sh
cd /test-pd
echo 'a' > index.html
类型:
空字符串:默认类型,不做任何检查
DirectoryOrCreate:如果给定的 path 不存在,就创建一个 755 的空目录
Directory:这个目录必须存在
FileOrCreate:如果给定的文件不存在,则创建一个空文件,权限为 644
File:这个文件必须存在
Socket:UNIX 套接字,必须存在
CharDevice:字符设备,必须存在
BlockDevice:块设备,必须存在
运行效果:
(2)EmptyDir
EmptyDir 主要用于一个 Pod 中不同的 Container 共享数据使用的,由于只是在 Pod 内部使用,因此与其他 volume 比较大的区别是,当 Pod 如果被删除了,那么 emptyDir 也会被删除。不能持久化。
cd /opt/k8s/volumes/
vim empty-dir-pd.yaml
#############################################
apiVersion: v1
kind: Pod
metadata:
name: empty-dir-pd
spec:
containers:
- image: alpine
name: nginx-emptydir1
command: ["/bin/sh","-c","sleep 3600;"]
volumeMounts:
- mountPath: /cache
name: cache-volume
- image: alpine
name: nginx-emptydir2
command: ["/bin/sh","-c","sleep 3600;"]
volumeMounts:
- mountPath: /opt
name: cache-volume
volumes:
- name: cache-volume
emptyDir: {}
#############################################
#启动
kubectl create -f empty-dir-pd.yaml
kubectl get po
#看一下两个容器能不能互通
kubectl exec -it empty-dir-pd -c nginx-emptydir1 -- sh
cd /cache
kubectl exec -it empty-dir-pd -c nginx-emptydir2 -- sh
cd /opt
touch a.txt
2.NFS挂载
nfs 卷能将 NFS (网络文件系统) 挂载到你的 Pod 中。 不像 emptyDir 那样会在删除 Pod 的同时也会被删除,nfs 卷的内容在删除 Pod 时会被保存,卷只是被卸载。 这意味着 nfs 卷可以被预先填充数据,并且这些数据可以在 Pod 之间共享。
因为有磁盘io和网络io,所以稳定性会略差一点。
(1)安装NFS
在三个机器都要运行:
# 安装 nfs
yum install nfs-utils -y
# 启动 nfs
systemctl start nfs-server
# 查看 nfs 版本
cat /proc/fs/nfsd/versions
在141上执行:
#创建读写、只读目录
cd /home
mkdir nfs
cd nfs/
mkdir rw
mkdir ro
#设置共享目录 export
vim /etc/exports
#########################################################################
/home/nfs/rw 192.168.200.0/24(rw,sync,no_subtree_check,no_root_squash)
/home/nfs/ro 192.168.200.0/24(ro,sync,no_subtree_check,no_root_squash)
#########################################################################
# 重新加载
exportfs -f
systemctl reload nfs-server
#创建一个只读文件
cd ro
touch README.md
echo 'hello NFS' > README.md
在主机器上执行:
#创建目录
mkdir -p /mnt/nfs/rw
mkdir -p /mnt/nfs/ro
#映射目录
mount -t nfs 192.168.200.141:/home/nfs/rw /mnt/nfs/rw
mount -t nfs 192.168.200.141:/home/nfs/ro /mnt/nfs/ro
#能够看到141中创建的内容,并且只读,不能编辑。同样读写里面我们自己创建文件,141中也能看到
cd /mnt/nfs/ro
ls
141中:
主机器:
(2)将NFS挂载到容器中
先在141中创建好目录:
cd /home/nfs/rw
mkdir -p www/xupeng
echo 'xupeng' > /home/nfs/rw/www/xupeng/index.html
在主机器中执行:
cd /opt/k8s/volumes/
vim nfs-test-pd.yaml
####################################################
apiVersion: v1
kind: Pod
metadata:
name: nfs-test-pd1
spec:
containers:
- image: nginx
name: test-container
volumeMounts:
- mountPath: /usr/share/nginx/html
name: test-volume
volumes:
- name: test-volume
nfs:
server: 192.168.200.141 # 网络存储服务地址
path: /home/nfs/rw/www/xupeng # 网络存储路径
readOnly: false # 是否只读
####################################################
#启动
kubectl create -f nfs-test-pd.yaml
kubectl get po -o wide
#访问pod对应的内网ip
curl 10.244.91.144
#然后我们更改141里面的内容,再次访问会发现内容变了
我们可以把配置文件的名称改成nfs-test-pd2,在生成一个,还是可以访问到共享内容。并且两个contianer都删了,共享文件还能保留
3.pv和pvc(重要)
(1)什么是pv和pvc
假设公司有一台数据服务器,其他多个项目的共享目录都在这个服务器里面,当每个项目和数据服务器都是用不同的持久化方式,比如A用nfs挂载,B用其他的方式挂载,就会导致要安装各种配置、环境。没有统一的存储规范。
pv(持久卷),他是对各种具体挂载的抽象,可以理解为是一个接口,他是一个规范,具体方案是对接口的实现。
pvc(持久卷申领),不同的微服务会需要不同的资源,微服务会告诉pvc,然后pvc拿着微服务的需求来向pv要资源,pv把资源分配给微服务。
- 微服务:消费者
- pvc:代购
- pv:商家的门店,用来卖东西
- 具体对pv的实现:工厂,真正生产东西
(2)生命周期
- pv构建
-
静态构建:集群管理员创建若干 PV 卷
-
动态构建:如果集群中已经有的 PV 无法满足 PVC 的需求,那么集群会根据 PVC 自动构建一个 PV,该操作是通过 StorageClass 实现的
-
-
绑定:当用户创建一个 PVC 对象后,主节点会监测新的 PVC 对象,并且寻找与之匹配的 PV 卷,找到 PV 卷后将二者绑定在一起。如果找不到对应的 PV,则需要看 PVC 是否设置 StorageClass 来决定是否动态创建 PV,若没有配置,PVC 就会一致处于未绑定状态,直到有与之匹配的 PV 后才会申领绑定关系。
-
使用:Pod 一旦使用 PVC 绑定 PV 后,为了保护数据,避免数据丢失问题,PV 对象会受到保护,在系统中无法被删除。
-
回收策略
-
保留(Retain):pv删除,数据保留
-
删除(Delete):pv、数据都删除
-
回收(Recycle):回收策略 Recycle 已被废弃
-
(3)创建pv,pvc,并关联pod,实现持久化数据
*1)创建pv
先在141创建一个nfs的路径:
cd /home/nfs/rw
mkdir test-pv
主机器创建pv:
cd /opt/k8s/volumes/
vim pv-nfs.yaml
################################################
apiVersion: v1
kind: PersistentVolume #描述资源对象为pc类型
metadata:
name: pv0001 #pv名字
spec:
capacity: #容量配置
storage: 5Gi # pv 的容量
volumeMode: Filesystem # 存储类型为文件系统
accessModes: # 访问模式:ReadWriteOnce、ReadWriteMany、ReadOnlyMany
- ReadWriteMany # 可以被多节点独写
persistentVolumeReclaimPolicy: Retain # 回收策略
storageClassName: slow # 创建 PV 的存储类名,需要与 pvc 的相同
mountOptions: # 加载配置
- hard
- nfsvers=4.1
nfs: # 连接到 nfs
path: /home/nfs/rw/test-pv # 存储路径
server: 192.168.200.141 # nfs 服务地址
################################################
#启动
kubectl create -f pv-nfs.yaml
kubectl get pv
pv的状态:
- Available:空闲,未被绑定
- Bound:已经被 PVC 绑定
- Released:PVC 被删除,资源已回收,但是 PV 未被重新使用
- Failed:自动回收失败
*2)创建pvc
vim pvc-test.yaml
###################################################################
apiVersion: v1
kind: PersistentVolumeClaim #资源类型pvc
metadata:
name: nfs-pvc
spec:
accessModes:
- ReadWriteMany # 权限需要与对应的 pv 相同
volumeMode: Filesystem
resources:
requests:
storage: 5Gi # 资源可以小于 pv 的,但是不能大于,如果大于就会匹配不到 pv
storageClassName: slow # 名字需要与对应的 pv 相同
# selector: # 使用选择器选择对应的 pv
# # matchLabels:
# # release: "stable"
# # matchExpressions:
# # - {key: environment, operator: In, values: [dev]}
#
#
#
###################################################################
#启动
kubectl create -f pvc-test.yaml
kubectl get pvc
*3)pvc和pod关联,实现容器数据持久化
实现通过pv、pvc来将pod中存储内容和nfs中内容进行挂载:
/opt/k8s/volumes
vim pvc-test-pd.yaml
#################################################
apiVersion: v1
kind: Pod
metadata:
name: test-pvc-pd
spec:
containers:
- image: nginx
name: nginx-volume
volumeMounts:
- mountPath: /usr/share/nginx/html # 挂载到容器的哪个目录
name: test-volume # 挂载哪个 volume
volumes:
- name: test-volume
persistentVolumeClaim: #关联pvc
claimName: nfs-pvc #要关联到哪个pvc
#################################################
#启动,找到pod对应的ip
kubectl create -f pvc-test-pd.yaml
kubectl get po -o wide
#我们先去curl那个ip是没有的,然后我们去141的/home/nfs/rw/test-pv/路径下新增一个
echo 'hello' > index.html
#再去curl就可以看到了
(4) StorageClass动态申领
我们每构建一个pod,就要绑定一个pvc,然后去关联pv。当我们的pod非常多的时候是不是需要创建非常多的pvc和pv?这很不方便。我们需要动态申领sc( StorageClass)
k8s 中提供了一套自动创建 PV 的机制,就是基于 StorageClass 进行的,通过 StorageClass 可以实现仅仅配置 PVC,然后交由 StorageClass 根据 PVC 的需求动态创建 PV。
sc通过制备器(Provisioner)选择需要的资源真正去创建pv。
vim nfs-provisioner-rbac.yaml:
apiVersion: v1
kind: ServiceAccount
metadata:
name: nfs-client-provisioner
namespace: kube-system
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: nfs-client-provisioner-runner
namespace: kube-system
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
namespace: kube-system
subjects:
- kind: ServiceAccount
name: nfs-client-provisioner
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
namespace: kube-system
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
namespace: kube-system
subjects:
- kind: ServiceAccount
name: nfs-client-provisioner
roleRef:
kind: Role
name: leader-locking-nfs-client-provisioner
apiGroup: rbac.authorization.k8s.io
vim nfs-storage-class.yaml:
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: managed-nfs-storage
namespace: kube-system
provisioner: fuseim.pri/ifs # 外部制备器提供者,编写为提供者的名称
parameters:
archiveOnDelete: "false" # 是否存档,false 表示不存档,会删除 oldPath 下面的数据,true 表示存档,会重命名路径
reclaimPolicy: Retain # 回收策略,默认为 Delete 可以配置为 Retain
volumeBindingMode: Immediate # 默认为 Immediate,表示创建 PVC 立即进行绑定,只有 azuredisk 和 AWSelasticblockstore 支持其他值
vim nfs-provisioner-deployment.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
name: nfs-client-provisioner
namespace: kube-system
labels:
app: nfs-client-provisioner
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: registry.cn-beijing.aliyuncs.com/pylixm/nfs-subdir-external-provisioner:v4.0.2
volumeMounts:
- name: nfs-client-root
mountPath: /persistentvolumes
env:
- name: PROVISIONER_NAME
value: fuseim.pri/ifs
- name: NFS_SERVER
value: 192.168.200.141
- name: NFS_PATH
value: /home/nfs/rw
volumes:
- name: nfs-client-root
nfs:
server: 192.168.200.141
path: /home/nfs/rw
vim ndf-sc-demo-statefulset.yaml:
---
apiVersion: v1
kind: Service
metadata:
name: nginx-sc
labels:
app: nginx-sc
spec:
type: NodePort
ports:
- port: 80
name: web
protocol: TCP
selector:
app: nginx-sc
---
apiVersion: apps/v1
kind: StatefulSet #资源类型
metadata:
name: nginx-sc
spec:
serviceName: "nginx-sc" #使用哪个service管理管理dns
replicas: 1
selector:
matchLabels:
app: nginx-sc
template:
metadata:
labels:
app: nginx-sc
spec:
containers:
- name: nginx-sc
image: nginx
imagePullPolicy: IfNotPresent
volumeMounts:
- mountPath: /usr/share/nginx/html #挂载到容器内哪个目录
name: nginx-sc-test-pvc
volumeClaimTemplates: #数据卷模板
- metadata: #数据卷描述
name: nginx-sc-test-pvc #数据卷名称
spec: #数据卷规约配置
storageClassName: managed-nfs-storage
accessModes:
- ReadWriteMany #访问模式
resources:
requests:
storage: 1Gi #需要一个G存储资源
vim nfs-pvc-test.yaml:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: auto-pv-test-pvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 300Mi
storageClassName: managed-nfs-storage
执行:
#删除pv、pvc
kubectl get pvc
kubectl get pv
kubectl delete pv XXX
kubectl delete pvc XXX
#创建角色
kubectl apply -f nfs-provisioner-rbac.yaml
#创建deployment
kubectl apply -f nfs-provisioner-deployment.yaml
#创建sc
kubectl apply -f nfs-storage-class.yaml
#查看
kubectl get sc
kubectl get po -n kube-system|grep nfs
#创建sts
kubectl apply -f ndf-sc-demo-statefulset.yaml
#查看
kubectl get po -n kube-system | grep nfs
kubectl get po
kubectl get pvc
kubectl get pv
四、感谢支持
感谢各位大佬支持,如果觉得满意可以请喝一杯咖啡吗: