3.k8s:服务发布:service,ingress;配置管理:configMap,secret,热更新;持久化存储:volumes,nfs,pv,pvc

目录​​​​​​​

一、服务发布

1.service

(1)service和pod之间的关系

[(2) service内部服务创建访问](#(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动态申领](#(4) StorageClass动态申领)

四、感谢支持


一、服务发布

1.service

负责 同层级/内部服务网络 的通信。

(1)service和pod之间的关系

  1. 一个子节点node = kubelet + kube-proxy + container runtime(容器运行时环境)+pod
  2. pod = pause容器(此处up是10.244.36.107) + 具体运行的项目(此处为nginx,nginx自带端口80)+ pod的基本信息(名称、标签)
    1. 这里有一个要对应的是,一个node宿主机的自己的ip和端口,和pod中的pause容器的ip和端口是对应上的
  3. master = service + EndPoint
    1. service中本身有一个pause容器并且暴露一个端口,同时因为service本身是一个服务并且部署在master宿主机上。因此我们可以通过宿主机ip + 宿主机真实端口访问service。也可以通过service的pause容器的ip + pause中的端口访问service。
    2. 一个service会生成一个endpoint,endpoint中会记录子宿主机中的pause容器的ip和端口
  4. 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)生命周期

  1. pv构建

    1. 静态构建:集群管理员创建若干 PV 卷

    2. 动态构建:如果集群中已经有的 PV 无法满足 PVC 的需求,那么集群会根据 PVC 自动构建一个 PV,该操作是通过 StorageClass 实现的

  2. 绑定:当用户创建一个 PVC 对象后,主节点会监测新的 PVC 对象,并且寻找与之匹配的 PV 卷,找到 PV 卷后将二者绑定在一起。如果找不到对应的 PV,则需要看 PVC 是否设置 StorageClass 来决定是否动态创建 PV,若没有配置,PVC 就会一致处于未绑定状态,直到有与之匹配的 PV 后才会申领绑定关系。

  3. 使用:Pod 一旦使用 PVC 绑定 PV 后,为了保护数据,避免数据丢失问题,PV 对象会受到保护,在系统中无法被删除。

  4. 回收策略

    1. 保留(Retain):pv删除,数据保留

    2. 删除(Delete):pv、数据都删除

    3. 回收(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

四、感谢支持

感谢各位大佬支持,如果觉得满意可以请喝一杯咖啡吗:

相关推荐
YCyjs44 分钟前
K8S群集调度二
云原生·容器·kubernetes
Hoxy.R44 分钟前
K8s小白入门
云原生·容器·kubernetes
€☞扫地僧☜€4 小时前
docker 拉取MySQL8.0镜像以及安装
运维·数据库·docker·容器
全能全知者5 小时前
docker快速安装与配置mongoDB
mongodb·docker·容器
为什么这亚子7 小时前
九、Go语言快速入门之map
运维·开发语言·后端·算法·云原生·golang·云计算
ZHOU西口9 小时前
微服务实战系列之玩转Docker(十八)
分布式·docker·云原生·架构·数据安全·etcd·rbac
牛角上的男孩9 小时前
Istio Gateway发布服务
云原生·gateway·istio
JuiceFS11 小时前
好未来:多云环境下基于 JuiceFS 建设低运维模型仓库
运维·云原生
景天科技苑11 小时前
【云原生开发】K8S多集群资源管理平台架构设计
云原生·容器·kubernetes·k8s·云原生开发·k8s管理系统
wclass-zhengge12 小时前
K8S篇(基本介绍)
云原生·容器·kubernetes