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

四、感谢支持

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

相关推荐
dessler1 分钟前
Docker-如何启动docker
运维·docker·云原生·容器·eureka
zhy295632 分钟前
【DOCKER】基于DOCKER的服务之DUFS
运维·docker·容器·dufs
Algorithm157623 分钟前
云原生相关的 Go 语言工程师技术路线(含博客网址导航)
开发语言·云原生·golang
蜜獾云1 小时前
docker 安装雷池WAF防火墙 守护Web服务器
linux·运维·服务器·网络·网络安全·docker·容器
年薪丰厚2 小时前
如何在K8S集群中查看和操作Pod内的文件?
docker·云原生·容器·kubernetes·k8s·container
zhangj11253 小时前
K8S Ingress 服务配置步骤说明
云原生·容器·kubernetes
岁月变迁呀3 小时前
kubeadm搭建k8s集群
云原生·容器·kubernetes
墨水\\3 小时前
二进制部署k8s
云原生·容器·kubernetes
Source、3 小时前
k8s-metrics-server
云原生·容器·kubernetes
上海运维Q先生3 小时前
面试题整理15----K8s常见的网络插件有哪些
运维·网络·kubernetes