第三章 K8s存储与网络笔记

一、K8s存储

1、存储卷基础

在docker容器中,为了实现数据的持久性存储,在宿主机和容器内做映射,可以保证在容器的生命周期结束,数据依旧可以实现持久性存储。

但是在k8s中,由于pod分布在各个不同的节点之上,并不能实现不同节点之间持久性数据的共享,并且,在节点故障时,可能会导致数据的永久性丢失。为此,k8s就引入了外部存储卷的功能。

1.1、emptyDir目录

emptyDir:Pod挂载在本地的磁盘或者内存,被称为emptyDir,称为临时空目录,随着Pod删除,也会被删除。注意:一个容器崩溃了不会导致数据的丢失,因为容器的崩溃并不移除pod.

做一个empty数据卷示例:pod-emp.yaml

第三章 K8s存储与网络笔记

命令:kubectl create -f pod-emp.yaml

进入容器:kubectl exec -it empty-pod /bin/sh

运行后查看,挂载描述如下:

第三章 K8s存储与网络笔记

使用场景,容器共享文件。在一个pod中有两类容器,一个是主容器,一个是辅助容器,两个容器使用同一个存储卷,辅助容器用来生成新的内容,主容器加载使用。

示例2运行pod-empty.yaml文件

第三章 K8s存储与网络笔记

运行:

kubectl create -f pod-empty.yaml

查看容器:

kubectl get pods -o wide

可看到页面内容不断增加

第三章 K8s存储与网络笔记

1.2、gitrepo目录

gitrepo实际上并不是一个新的存储类型,只是emptydir上补添一个git命令来拉取文件而已。当pod创建时候,会拉取git(依赖于宿主机git命令驱动)仓库中数据克隆到本地,并且作为存储卷定义在pod之上。gitrepo基于emptyDir,此存储卷是emptyDir,git仓库中拉取的代码存放在emptyDir后被定义到pod。

因为拉取git仓库的动作,仅仅是在pod创建时触发一次,并不会待续更新文件,因此在工作中基本上无用处。有兴趣的同学可以自行研究一下。

1.3、hostPath目录

hostPath类型则是映射node文件系统中的文件或者目录到pod里。可实现针对某一节点的数据持久化,如果节点宕机了,那数据就丢失了

示例:pod-hostpath.yaml

第三章 K8s存储与网络笔记

创建容器:

kubectl create -f pod-hostpath.yaml

进入pod中创建一个测试文件:

kubectl exec -it host-pod /bin/sh

在挂在目录下,创建一个文件:

[[email protected] k8s-config]# kubectl exec -it host-pod /bin/sh

/data # echo 123 > 123file

/data # ls

123file

/data # cat 123file

123

/data #

回到pod运行的node机器上查看(leader192.168.30.161),是否存在了这个测试文件

[[email protected] ~]#  ls /data

ls: cannot access /data: No such file or directory

查看pod信息发现该容器运行在work1上面

第三章 K8s存储与网络笔记

然后到work1机器上查看,果然有此文件

[[email protected] ~]# ls /data

123file

[[email protected] ~]#

此后,删除掉pod,此文件也依然存在,可自行去验证。

1.4、nfs共享存储卷

nfs使的我们可以挂在已经存在的共享到的我们的Pod中,nfs不会被删除,仅仅是解除挂在状态而已,这就意味着NFS能够允许我们提前对数据进行处理,而且这些数据可以在Pod之间相互传递.并且,nfs可以同时被多个pod挂在并进行读写

1.5、节点安装配置nfs

1)这里在leader机器上安装nfs

yum install -y nfs-utils

2)配置nfs:

vi /etc/exports

添加内容:/data/volumes *(rw,no_root_squash)

3)启动并查看:

[[email protected] ~]# systemctl start nfs

[[email protected] ~]# systemctl start rpcbind

[[email protected] ~]# showmount -e localhost

Export list for localhost:

/data/volumes *

[[email protected] ~]#

[[email protected] ~]# showmount -e

Export list for leader:

/data/volumes *

[[email protected] ~]#

1.6、node节点挂载

需要在所有节点安装`nfs-utils` 组件,否则当Pod被分配到没有组件的节点,会启动失败,因为没有`mount.nfs`。

1)安装nfs:

yum install -y nfs-utils

注意:

为了让leader与work节点能互相访问,需要在work节点的host配置里面添加leader节点的信息

 

第三章 K8s存储与网络笔记

2)挂载nfs:

mount -t nfs leader:/data/volumes /nfs

注意:没有则先在work机器新建nfs目录,leader机器新建目录: /data/volumes

解除挂载:umount /nfs

查看挂载效果:mount

在 leader节点/data/volumes目录下新建文件leader_create_file并输入内容its leader create by leader

[[email protected] ~]# cd /data/volumes/

[[email protected] volumes]# echo its leader create by leader >  leader_create_file

[[email protected] volumes]# ls

leader_create_file

[[email protected] volumes]#

然后回到work节点的nfs目录下查看:

[[email protected] ~]# cd /nfs/

[[email protected] nfs]# ls

leader_create_file

[[email protected] nfs]# cat leader_create_file

its leader create by leader

[[email protected] nfs]#

 

第三章 K8s存储与网络笔记

进入node节点,可查看/mnt目录下文件与master已互通

1.7、测试NFS

先在nfs服务内,加一个html测试页面

echo '<h1>NFS success</h1>' > /data/volumes/index.html

创建一个pod测试:

Leader机器创建vol-nfs.yaml

 

第三章 K8s存储与网络笔记

运行pod后测试:

kubectl create -f vol-nfs.yaml

kubectl get pods -o wide

可看到nginx输出页面

 

第三章 K8s存储与网络笔记

你还可以测试pod的删除重建,丝毫不影响pod服务效果

2、k8s的namespace概念

一个namespaces可以理解为kubernetes集群中的一个虚拟化集群。在一个Kubernetes集群中可以拥有多个命名空间,它们在逻辑上彼此隔离。 这样可以方便我们区分各个团队的不同服务,增加安全甚至性能方面的帮助。K8S默认已经为了我们初始化了三个namespace:

  • default:你的service和app默认被创建于此。
  • kube-system:kubernetes系统组件使用。
  • kube-public:公共资源使用。但实际上现在并不常用。

执行以下命令查看:

kubectl get namespaces

kubectl get pods --namespace kube-system

 

第三章 K8s存储与网络笔记

总而言之,namespace是一种为了你方便管理服务的辅助,你可以根据需要创建若干个namespace供业务服务分组治理。从另一方面来说,K8s的任何一个服务,都是归属于某一个namespace的。

3、PVC和PV

通过上面的存储卷的基础知识,我们大概了解了在K8S中,要持久化存储数据,最好还是存入到第三方服务里更安全,如NFS等。而在业界,这样的存储很多,使用方式五花八门,这就要求运维工程师必须要精通这些存储才能用好它。 PersistentVolume(pv)和PersistentVolumeClaim(pvc)就是k8s提供的两种API资源,用于抽象存储细节:

  • 管理员关注于如何通过pv提供存储功能而无需如何使用
  • 用户只需要挂载pvc到容器中而不需要关注存储卷采用何种技术实现。

pvc和pv的关系与pod和node关系类似,前者消耗后者的资源。pvc可以向pv申请指定大小的存储资源并设置访问模式。

 

第三章 K8s存储与网络笔记

接下来我们使用PV与PVC的方式,实现上面nfs模式的资源挂载效果,定义一个PV资源。

3.1、PV概念与使用

PersistentVolume(PV)是集群中由管理员配置的一段网络存储。 它是集群中的资源,就像节点是集群资源一样。因此PV是全局资源,不归属于任何namespace的。

查看vol-pv.yaml文件:

[[email protected] k8s-config]# cat vol-pv.yaml

apiVersion: v1

kind: PersistentVolume  

metadata:

  name: pv-nfs2      # pv的名称 与后面的PVC绑定

spec:

  storageClassName: manual

  capacity:

    storage: 2Gi   # 存储容量

  accessModes:

    - ReadWriteMany

  nfs:  # 引入nfs服务

    server: 192.168.30.161

    path: /data/volumes

创建PV:

kubectl create -f vol-pv.yaml

 

第三章 K8s存储与网络笔记

此处的RWX是PV资源的AccessModes(访问模式),有以下几种模式

  • ReadWriteOnce(RWO):读写权限,但是只能被单个节点挂载
  • ReadOnlyMany(ROX):只读权限,可以被多个节点挂载
  • ReadWriteMany(RWX):读写权限,可以被多个节点挂载

此处的NFS资源,是支持多个pod同时挂载使用的,同时PV同一时间只能被一个PVC申请使用,这里有个状态变迁过程:

  • Available(可用):表示可用状态,还未被任何 PVC 绑定
  • Bound(已绑定):表示 PV 已经被 PVC 绑定
  • Released(已释放):PVC 被删除,但是资源还未被集群重新声明
  • Failed(失败): 表示该 PV 的自动回收失败

3.2、PVC的概念与使用

PersistentVolumeClaim(PVC)是由用户进行存储的请求。 它类似于pod。 Pod消耗节点资源,PVC消耗PV资源。因此PVC是归属于某个namespace的。

1)文件vol-pvc.yaml内容:

[[email protected] k8s-config]# cat vol-pvc.yaml

apiVersion: v1

kind: PersistentVolumeClaim

metadata:

  name: pvc-nfs

  namespace: default     # 归属namespace

spec:

  accessModes:

    - ReadWriteMany

  storageClassName: manual

  resources:

    requests:

      storage: 1Gi  # 要求的容量

2)创建PVC

kubectl create -f vol-pvc.yaml

kubectl get pvc

kubectl get pv

现次查看PV的状态,发现其已经被绑定使用了

 

第三章 K8s存储与网络笔记

测试一个pod的使用效果,将上面的pvc引入这里使用,这里我们创建两个资源(pod与PVC)

3)文件vol-pod.yaml内容:

[[email protected] k8s-config]# cat vol-pod.yaml

apiVersion: v1

kind: Pod

metadata:

  name: pod-pvc

spec:

  containers:

  - name: nginx

    image: nginx:1.9.7

    ports:

    - containerPort: 80

    volumeMounts:

        - name: pvc-nfs

          mountPath: "/usr/share/nginx/html"   # 将PVC挂载到路劲下

  volumes:

  - name: pvc-nfs

       persistentVolumeClaim:  # 挂载类型为PVC

      claimName: pvc-nfs  # 申请PVC的名字

---

apiVersion: v1

kind: PersistentVolumeClaim

metadata:

  name: nfs-pvc

  namespace: default

spec:

  accessModes:

    - ReadWriteMany

  storageClassName: manual

  resources:

    requests:

      storage: 1Gi

这里我们先删除之前创建的那个PVC:

kubectl delete pvc pvc-nfs

注意:如果pv的状态是released,那么这个pv也得删除重建。

创建新的PVC pod:

kubectl create -f vol-pod.yaml

查看效果:

 

第三章 K8s存储与网络笔记

3.3、PVC的阻塞

当PVC没有足够的PV来满足时,状态为挂起pending

 

第三章 K8s存储与网络笔记

如果有PV被释放,或者新建了PV能满足请求时,自动绑定占用

 

第三章 K8s存储与网络笔记

4、statefulset控制器

现实工作中,我们的应用,分两类,有状态和无状态:

  • 无状态的,关注的是群体,如nginx,tomcat等
  • 有状态的,关注的是个体,如redis,mysql等

我们前面的控制器,对无状态的应用操作很好,但对有状态的应用完全不实用。

有状态应用集的特点:

稳定且需要唯一的网络标识符,如Redis集群,在Redis集群中,它是通过槽位来存储数据的,假如第一个节点是01000,第二个节点是1001~2000,第三个节点2001~3000...等等,这就使得Redis集群中每个节点要通过ID来标识自己,如果第二个节点宕机了,重建后它必须还叫第二个节点,或者说第二个节点叫R2,它必须还叫R2,这样在获取1001~2000槽位的数据时,才能找到数据,否则Redis集群将无法找到这段数据。

稳定且持久的存储,可实现持久存储,新增或减少pod,存储不会随之发生变化;针对这样的特点,k8s推出StatefulSet。

4.1、PV资源配置

制做三个基础PV资源:pv.yaml

apiVersion: v1

kind: PersistentVolume

metadata:

  name: pv001

  labels:

    name: pv001

spec:

  nfs:

    path: /data/volumes/v1

    server: 192.168.30.161

  accessModes: ["ReadWriteMany", "ReadWriteOnce"]

  capacity:

    storage: 1Gi

---

apiVersion: v1

kind: PersistentVolume

metadata:

  name: pv002

  labels:

    name: pv002

spec:

  nfs:

    path: /data/volumes/v2

    server: 192.168.30.161

  accessModes: ["ReadWriteOnce"]

  capacity:

    storage: 2Gi

---

apiVersion: v1

kind: PersistentVolume

metadata:

  name: pv003

  labels:

    name: pv003

spec:

  nfs:

    path: /data/volumes/v3

    server: 192.168.30.161

  accessModes: ["ReadWriteMany", "ReadWriteOnce"]

  capacity:

    storage: 3Gi

创建PV

kubectl create -f pv.yaml

 

第三章 K8s存储与网络笔记

4.2、创建StatefulSet服务

StatefulSet资源清单包含三个部分:

  • headless service 用于定义网络标识(DNS)
  • StatefulSet 控制器,用于定义具体应用
  • volumeClaimTemplate 存储卷申请模板,用于创建PVC

1)查看stateful.yaml文件内容

[[email protected] k8s-config]# cat stateful.yaml

apiVersion: v1

kind: Service

metadata:

  name: myapp-svc

  namespace: default

  labels:

    app: myapp                  # service 名称

spec:

  ports:

  - port: 80

    name: web

  clusterIP: None               # 配置headless service

  selector:

    app: myapp-pod              # 匹配Pod 标签

---

apiVersion: apps/v1

kind: StatefulSet

metadata:

  name: myapp

spec:

  serviceName: myapp            # 名称

  replicas: 2                   

  selector:

    matchLabels:

      app: myapp-pod            # 匹配Pod

  template:

    metadata:

      labels:

        app: myapp-pod

    spec:

      containers:

      - name: myapp

        image: nginx:1.9.7

        ports:

        - containerPort: 80

          name: web

        volumeMounts:

        - name: myappdata

          mountPath: /usr/shar/nginx/html

  volumeClaimTemplates:

  - metadata:

      name: myappdata                   # pvc名称

    spec:

      accessModes: ["ReadWriteOnce"]    # 权限

      resources:

        requests:

          storage: 1Gi                  # pv 大小

2)创建服务:

kubectl create -f stateful.yaml

查看详情:

 

第三章 K8s存储与网络笔记

查看pod,发现sts创建的pod集群,是有序编号的,从第0个开始,依次创建。

查看pvc列表(sts自动创建管理的),它与上面的pod是序号一一对应的。也就是说,此pod重启重建,对应的pvc都是一样的

3)服务扩容:

命令:kubectl scale sts myapp --replicas=3

 

第三章 K8s存储与网络笔记

可以看到pod扩展,会连带pvc一起扩容,但是缩容却不会

命令:kubectl scale sts myapp --replicas=2

 

第三章 K8s存储与网络笔记

删除pod继续查看:

 

第三章 K8s存储与网络笔记

可以看到,新建出来的pod名称/node都是不变的,对应的PVC当然也不变。

5、configmap与secret

secret和configmap可以理解为特殊的存储卷,但是它们不是给Pod提供存储功能的,而是提供了从集群外部向集群内部的应用注入配置信息的功能。ConfigMap扮演了K8S集群中配置中心的角色。

ConfigMap定义了Pod的配置信息,可以以存储卷的形式挂载至Pod中的应用程序配置文件目录,从configmap中读取配置信息;也可以基于环境变量的形式,从ConfigMap中获取变量注入到Pod容器中使用。

5.1、命令行方式创建ConfigMap

--from-literal=key=value格式直接赋值,示例如下:

创建命令:

kubectl create configmap web-config --from-literal=db.host=192.168.30.128 --from-literal=db.port='3306'

 

第三章 K8s存储与网络笔记

查看这个configmap信息:

[[email protected] k8s-config]# kubectl get configmaps -o yaml

apiVersion: v1

items:

- apiVersion: v1

  data:

    db.host: 192.168.30.128

    db.port: "3306"

  kind: ConfigMap

  metadata:

    creationTimestamp: "2020-04-11T11:32:07Z"

    name: web-config

    namespace: default

    resourceVersion: "42780"

    selfLink: /api/v1/namespaces/default/configmaps/web-config

    uid: 4c914861-421d-4e4e-842c-594c6784c0a7

kind: List

metadata:

  resourceVersion: ""

  selfLink: ""

[[email protected] k8s-config]#

--from-file=path方式,将文件中信息存入configmap

先将值放入文件中:

[[email protected] k8s-config]# echo -n 192.168.30.128 > ./db.host

[[email protected] k8s-config]# echo -n 3306 > ./db.port

[[email protected] k8s-config]# cat db.port

3306

[[email protected] k8s-config]# cat db.host

192.168.30.128

再合建configmap:

kubectl create configmap config2 --from-literal=db.host=./db.host --from-literal=db.port=./db.port

查看信息,效果一样

 

第三章 K8s存储与网络笔记

5.2、YAML 配置文件方式创建ConfigMap

直接创建一个yaml文件,配置好信息

[[email protected] k8s-config]# cat db.yaml

apiVersion: v1

kind: ConfigMap

metadata:

  name: config3

data:

  db.host: 192.168.30.128

  db.port: "3306"

[[email protected] k8s-config]#

创建并查验信息:

kubectl get cm

kubectl get cm config3 -o yaml

 

第三章 K8s存储与网络笔记

5.3、环境变量方式使用configmap

使用valueFrom、configMapKeyRef、name、key指定要用的key。

1)文件 cm-pod.yaml内容如下:

[[email protected] k8s-config]# cat cm-pod.yaml

apiVersion: v1

kind: Pod

metadata:

  name: cmpod

spec:

  containers:

  - name: cmpod

    image: busybox

    args: [ "/bin/sh", "-c", "sleep 3000" ]

    env:

    - name: DB_HOST      # 我们需要配置这个值

      valueFrom:

        configMapKeyRef:

          name: config3     # 到config3中获取db.host的值

          key: db.host       #

    - name: DB_PORT

      valueFrom:

        configMapKeyRef:

          name: config3

          key: db.port

[[email protected] k8s-config]#

2)运行pod

kubectl create -f cm-pod.yaml

进入pod容器中,查看环境信息已经引入:

kubectl exec -it cmpod -- /bin/sh

 

第三章 K8s存储与网络笔记

5.4、volume挂载使用

把configmap当作文件挂载进pod容器

1)文件cm2.yaml内容如下:

[[email protected] k8s-config]# cat cm2.yaml

apiVersion: v1

kind: Pod

metadata:

  name: cmpod2

spec:

  containers:

  - name: cmpod2

    image: busybox

    args: [ "/bin/sh", "-c", "sleep 3000" ]

    volumeMounts:     # 文件挂载

    - name: db

      mountPath: "/etc/db"    # 文件挂载目录

      readOnly: true

  volumes:

  - name: db    # 定义挂载数据

    configMap:   # 定义挂载数据

      name: config3

[[email protected] k8s-config]#

2)创建容器:

kubectl create -f cm2.yaml

进入容器查看详情:kubectl get pod -o wide

kubectl exec -it cmpod2 /bin/sh

ls /etc/db/

 

第三章 K8s存储与网络笔记

二、K8S网络

K8S中包含三种网络:

container之间的通信。

Pod网络:Pod之间通信的网络。

节点网络: 各个节点主机之间的通信网络。

1Pod内容器间的通讯

K8s的用处是容器的编排和管理,但最小组成却不是容器,是pod。物理机或者虚拟机叫node,pod是基础单元,pod里可以有多个容器,也可以只有一个容器,同一个pod的容器彼此是共享网络和主机配置的,换句话说,彼此是可以直接localhost通信的,类似于同一台机器上进行通信,所以这里面是无所谓隔离和安全一说,对外而言就是一个环境,所以pod就是这个环境的业务实体。如下图:

 

第三章 K8s存储与网络笔记

创建一个多容器的pod,在容器内,你可以查看到它们的ip地址是同一个。

1)文件net-pod.yaml内容如下:

[[email protected] k8s-config]# cat net-pod.yaml

apiVersion: v1

kind: Pod

metadata:

  name: netpod

  labels:

    app: netpod     #给自己打上标签

spec:

  containers:         #创建了3个容器

  - name: nginx

    image: nginx:1.7.9

  - name: tomcat

    image: tomcat:latest

  - name: busybox

    image: busybox

    command: [ "sleep", "3600" ]

[[email protected] k8s-config]#

2)创建命令:

kubectl create -f net-pod.yaml

3)查看每个容器的ip地址:

kubectl exec -it netpod -c nginx /bin/sh

kubectl exec -it netpod -c tomcat /bin/sh

 

第三章 K8s存储与网络笔记

这也告诉我们,同一个pod内共享网络,容器间的port是互斥的,你无法让pod内的两个容器使用同一个port。

2、Pod间的容器通讯

两个pod间的网络通讯,可以直接理解为两台主机的ip通讯

 

第三章 K8s存储与网络笔记

只要能寻址到对方pod的ip,就轻松通讯了。在K8s的集群虚拟的ip中,ip网段是根据node节点来分配的,每个node节点得一个独立的ip子网段,如下例的3个pod集群:

 

第三章 K8s存储与网络笔记

可以看到,节点work2上的所有pod处理同一个网段,work3的pod处于另一个网段。很明显,work2上的两台pod处于同一个子网,网络天然是通的。但是K8s还为我们多做了一步,即work2与work3上的网络,相互之间也是畅通无阻的。这里就用到了k8s的网络插件的功能,常用的就是flannel。

3、flannel原理、跨node的通信

flannel组建一个大二层扁平网络,pod的ip分配由flannel统一分配,通讯过程也是走flannel的网桥。
每个node上面都会创建一个flannel0虚拟网卡,用于跨node之间通讯,所以容器直接可以直接使用pod id进行通讯。跨节点通讯时,发送端数据会从docker0路由到flannel0虚拟网卡,接收端数据会从flannel0路由到docker0。

 

第三章 K8s存储与网络笔记

跨Node的Pod,通信需要网络组件协调。