因为pod是有生命周期的,pod一重启,里面的数据就没了。所以我们需要数据持久化存储。

在k8s中,存储卷不属于容器,而是属于pod。也就是说同一个pod中的容器可以共享一个存储卷。

存储卷可以是宿主机上的目录,也可以是挂载在宿主机上的外部设备。

存储卷类型

 emptyDIR存储卷 :pod一重启,存储卷也删除,这叫emptyDir存储卷。一般用于当做临时空间或缓存关系

  hostPath存储卷 :宿主机上目录作为存储卷,这种也不是真正意义实现了数据持久性。

SAN(iscsi)或NAS(nfs、cifs): 网络存储设备

  分布式存储(ceph,glusterfs,cephfs,rbd) 

 云存储(亚马逊的EBS,Azure Disk,阿里云): 这种一般k8s也在云上部署的。

关键数据一定要有异地备份,否则数据一删,多少个副本都没用。

[root@master ingress]# kubectl explain pods.spec.volumes

  

hostPath

功能:使用宿主机上目录作为存储卷,这种也不是真正意义实现了数据持久性。

[root@master ~]# kubectl explain pods.spec.volumes.hostPath.type
KIND: Pod
VERSION: v1
FIELD: type <string>
DESCRIPTION:
Type for HostPath Volume Defaults to "" More info:
https://kubernetes.io/docs/concepts/storage/volumes#hostpath

  

查看帮助: https://kubernetes.io/docs/concepts/storage/volumes#hostpath

hostPath.type的类型说明:

DirectoryOrCreate:意思是我们要挂载的路径在宿主机上是个已经存在的目录,不存在就创建一个新的目录。

Directory:宿主机上必须实现存在目录,如果不存在就报错

FileOrCreate:表示挂载的是文件,如果不存在就挂载一个文件。文件也可以当做存储挂载的。

File:表示要挂载的文件必须事先存在,否则就报错。

Socket:表示必须是一个Socket类型的文件。

CharDevice:表示是一个字符类型的设备文件。

BlockDevice:表示的是一个块类型的设备文件。

例子:

[root@master volumes]# cat pod-hostpath-vol.yaml

apiVersion: v1
kind: Pod
metadata:
name: pod-vol-hostpath
namespace: default
spec:
containers:
- name: myapp
image: ikubernetes/myapp:v1
volumeMounts:
- name: html #存储卷的名字叫html
mountPath: /usr/share/nginx/html/ #挂载路径
volumes:
- name: html
hostPath:
path: /data/pod/volume1
type: DirectoryOrCreate

  

[root@master volumes]# kubectl apply -f pod-hostpath-vol.yaml
pod/pod-vol-hostpath created

  

然后到node1节点上可以看到/data/pod/volume1目录已经创建出来了。

[root@master volumes]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE
client 0/1 Error 0 15d 10.244.2.4 node2
pod-vol-hostpath 1/1 Running 0 4m 10.244.1.105 node1

  

当node1节点宕机后,pod就飘到node2节点上,并使用node2节点上的/data/pod/volume1目录。这就有问题了,因为node2节点上的目录并没有同步node1节点上目录的数据,所以出现数据不一致。

解决这个问题的方法就是使用类似nfs方法,让两个node节点共享一个存储。

使用nfs做共享存储

我这里为了方便,把master节点当做nfs存储。

[root@master ~]# yum -y install nfs-utils

  

[root@master ~]# mkdir /data/volumes

  

[root@master ~]# cat /etc/exports
#no_root_squash:登入 NFS 主机使用分享目录的使用者,如果是 root 的话,那么对于这个分享的目录来说,他就具有 root 的权限!这个项目『极不安全』,不建议使用!
#root_squash:在登入 NFS 主机使用分享之目录的使用者如果是 root 时,那么这个使用者的权限将被压缩成为匿名使用者,通常他的 UID 与 GID 都会变成 nobody 那个系统账号的身份;
/data/volumes 172.16.0.0/16(rw,no_root_squash)

  

[root@master ~]# systemctl start nfs

  

在node1和node2上也安装nfs-utils包

[root@node1 ~]# yum -y install nfs-utils

  

在node1和node2上挂载:

[root@node1 ~]# mount -t nfs 172.16.1.100:/data/volumes /mnt

  

在master上

[root@master ~]# kubectl explain pods.spec.volumes.nfs

  

[root@master volumes]# cat pod-vol-nfs.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-vol-nfs
namespace: default
spec:
containers:
- name: myapp
image: ikubernetes/myapp:v1
volumeMounts:
- name: html #存储卷的名字叫html
mountPath: /usr/share/nginx/html/ #挂载路径,myapp容器里面的路径
volumes:
- name: html
nfs:
path: /data/volumes
server: 172.16.1.100 #nfs server ip

  

[root@master volumes]# kubectl apply -f pod-vol-nfs.yaml

  

[root@master volumes]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE
pod-vol-nfs 1/1 Running 0 1m 10.244.1.106 node1

  

[root@master volumes]# cat /data/volumes/index.html
hello world

  

[root@master volumes]# curl  10.244.1.106 #容器的ip
hello world

  

可见容器使用的是nfs提供的共享存储。

不过,nfs自身没有冗余能力,所以如果nfs宕机了,数据也丢了。因此,我们一般用glusterfs或者cephfs分布式存储。

pvc和pv

用户只需要挂载pvc到容器中而不需要关注存储卷采用何种技术实现。pvc和pv的关系与pod和node关系类似,前者消耗后者的资源。pvc可以向pv申请指定大小的存储资源并设置访问模式。

在定义pod时,我们只需要说明我们要一个多大的存储卷就行了。pvc存储卷必须与当前namespace的pvc建立直接绑定关系。pvc必须与pv建立绑定关系。而pv是真正的某个存储设备上的空间。

[root@master volumes]# kubectl explain pods.spec.volumes.persistentVolumeClaim

  

[root@master volumes]# kubectl explain pvc

 

 一个pvc和pv是一一对应关系,一旦一个pv被一个pvc绑定了,那么这个pv就不能被其他pvc绑定了。

一个pvc是可以被多个pod所访问的。

在存储机器上建立如下几个目录(这里我以master节点做存储,生产中可以单独拿出 一个机器做存储):

[root@master volumes]# mkdir v{1,2,3,4,5}

  

[root@master volumes]# cat  /etc/exports
#no_root_squash:登入 NFS 主机使用分享目录的使用者,如果是 root 的话,那么对于这个分享的目录来说,他就具有 root 的权限!这个项目『极不安全』,不建议使用!
#root_squash:在登入 NFS 主机使用分享之目录的使用者如果是 root 时,那么这个使用者的权限将被压缩成为匿名使用者,通常他的 UID 与 GID 都会变成 nobody 那个系统账号的身份;
/data/volumes/v1 172.16.0.0/16(rw,no_root_squash)
/data/volumes/v2 172.16.0.0/16(rw,no_root_squash)
/data/volumes/v3 172.16.0.0/16(rw,no_root_squash)
/data/volumes/v4 172.16.0.0/16(rw,no_root_squash)
/data/volumes/v5 172.16.0.0/16(rw,no_root_squash)

  

[root@master volumes]# exportfs  -arv #不用重启nfs服务,配置文件就会生效
exporting 172.16.0.0/16:/data/volumes/v5
exporting 172.16.0.0/16:/data/volumes/v4
exporting 172.16.0.0/16:/data/volumes/v3
exporting 172.16.0.0/16:/data/volumes/v2
exporting 172.16.0.0/16:/data/volumes/v1

  

[root@master volumes]# showmount -e
Export list for master:
/data/volumes/v5 172.16.0.0/16
/data/volumes/v4 172.16.0.0/16
/data/volumes/v3 172.16.0.0/16
/data/volumes/v2 172.16.0.0/16
/data/volumes/v1 172.16.0.0/16

  

[root@master volumes]# kubectl explain pv.spec.nfs

  

[root@master ~]# kubectl explain pv.spec
FIELDS:
accessModes<[]string>
AccessModes contains all ways the volume can be mounted. More info:
https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes

  

访问  https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes看帮助。

accessModes模式有:

ReadWriteOnce:单路读写,可以简写为RWO

ReadOnlyMany:多路只读,可以简写为ROX

ReadWriteMany :多路读写,可以简写为RWX

不同类型的存储卷支持的accessModes也不同。

[root@master volumes]# cat pv-demo.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv001 #注意,定义pv时一定不要加名称空间,因为pv是属于整个集群,而不是属于某个名称空间。但pvc是属于某个名称空间的
labels:
name: pv001
spec:
nfs:
path: /data/volumes/v1
server: 172.16.1.100
accessModes: ["ReadWriteMany","ReadWriteOnce"]
capacity: #分配磁盘空间大小
storage: 1Gi
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv002 #注意,定义pv时一定不要加名称空间,因为pv是属于整个集群,而不是属于某个名称空间。但pvc是属于某个名称空间的
labels:
name: pv002
spec:
nfs:
path: /data/volumes/v2
server: 172.16.1.100
accessModes: ["ReadWriteOnce"]
capacity: #分配磁盘空间大小
storage: 2Gi
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv003 #注意,定义pv时一定不要加名称空间,因为pv是属于整个集群,而不是属于某个名称空间。但pvc是属于某个名称空间的
labels:
name: pv003
spec:
nfs:
path: /data/volumes/v3
server: 172.16.1.100
accessModes: ["ReadWriteMany","ReadWriteOnce"]
capacity: #分配磁盘空间大小
storage: 1Gi
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv004 #注意,定义pv时一定不要加名称空间,因为pv是属于整个集群,而不是属于某个名称空间。但pvc是属于某个名称空间的
labels:
name: pv004
spec:
nfs:
path: /data/volumes/v4
server: 172.16.1.100
accessModes: ["ReadWriteMany","ReadWriteOnce"]
capacity: #分配磁盘空间大小
storage: 1Gi
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv005 #注意,定义pv时一定不要加名称空间,因为pv是属于整个集群,而不是属于某个名称空间。但pvc是属于某个名称空间的
labels:
name: pv005
spec:
nfs:
path: /data/volumes/v5
server: 172.16.1.100
accessModes: ["ReadWriteMany","ReadWriteOnce"]
capacity: #分配磁盘空间大小
storage: 1Gi

  

[root@master volumes]# kubectl apply -f pv-demo.yaml
persistentvolume/pv001 created
persistentvolume/pv002 created
persistentvolume/pv003 created
persistentvolume/pv004 created
persistentvolume/pv005 created

  

[root@master volumes]# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
pv001 1Gi RWO,RWX Retain Available 2m
pv002 2Gi RWO Retain Available 2m
pv003 1Gi RWO,RWX Retain Available 2m
pv004 1Gi RWO,RWX Retain Available 2m
pv005 1Gi RWO,RWX Retain Available 2m

  

 回收策略 :如果某个pvc在pv里面存数据了,后来pvc删了,那么 pv里面的数据怎么处理呢。有如下几种策略:

reclaim_policy:即pvc删了,但是pv里面的数据不擅长,还保留着。

recycle:即pvc删了,那么就把pv里面的数据也删了。

delete:即pvc删了,那么就把pv也删了。

下面我们再创建pvc的清单文件。

[root@master ~]# kubectl explain pvc.spec
[root@master ~]# kubectl explain pods.spec.volumes.persistentVolumeClaim
[root@master volumes]# cat pod-vol-pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim #简称pvc
metadata:
name: mypvc
namespace: default #pvc和pod是在同一个名称空间
spec:
accessModes: ["ReadWriteMany"] #一定是pv策略的子集
resources:
requests:
storage: 1Gi #表示我要pvc 为1G的空间
---
apiVersion: v1
kind: Pod
metadata:
name: pod-vol-pvc
namespace: default
spec:
containers:
- name: myapp
image: ikubernetes/myapp:v1
volumeMounts:
- name: html #存储卷的名字叫html
mountPath: /usr/share/nginx/html/ #挂载路径
volumes:
- name: html
persistentVolumeClaim:
claimName: mypvc #表示我要使用哪个pvc

  

[root@master volumes]# kubectl apply -f pod-vol-pvc.yaml
persistentvolumeclaim/mypvc created
pod/pod-vol-pvc created

  

[root@master volumes]# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
pv001 1Gi RWO,RWX Retain Available 7h
pv002 2Gi RWO Retain Available 7h
pv003 1Gi RWO,RWX Retain Available 7h
pv004 1Gi RWO,RWX Retain Bound default/mypvc 7h
pv005 1Gi RWO,RWX Retain Available

  

上面看到pv004被default名称空间的mypvc绑定了。

[root@master volumes]# kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
mypvc Bound pv004 1Gi RWO,RWX 33m
[root@master volumes]# kubectl get pods
NAME READY STATUS RESTARTS AGE
client 0/1 Error 0 16d
pod-vol-pvc 1/1 Running 0 35m

  

生产上,pv并不属于node节点,而是独立于node节点的。所以,node节点坏了,pv里面的数据还在。另外,pod才是属于node节点的。

在k8s 1.10之后,不能手工从底层删除pv,这样做很安全。

StorageClass(存储类)

Kubernetes集群管理员通过提供不同的存储类,可以满足用户不同的服务质量级别、备份策略和任意策略要求的存储需求。动态存储卷供应使用StorageClass进行实现,其允许存储卷按需被创建。如果没有动态存储供应,Kubernetes集群的管理员将不得不通过手工的方式类创建新的存储卷。通过动态存储卷,Kubernetes将能够按照用户的需要,自动创建其需要的存储。

storageclass底层可以是glusterfs,cephfs等不同的集群。

subPath

          volumeMounts:
- name: logs-volume
mountPath: /data/xxx/logs
subPath: logs
- name: logs-volume
mountPath: /home
subPath: model-file
volumes:
- name: logs-volume
persistentVolumeClaim:
claimName: xxxx-pvc

  

configmap

configmap和secret是两种特殊的存储卷,它们不是给pod提供存储空间用的,而是给我们的管理员或者用户提供了从外部向pod内部注入信息的方式。

  configmap :把配置文件放在配置中心上,然后多个pod读取配置中心的配置文件。不过,configmap中的配置信息都是明文的,所以不安全。

  secret: 功能和configmap一样,只不过配置中心存储的配置文件不是明文的。

configmap和secret也是专属于某个名称空间的。

[root@master ~]# kubectl explain configmap
[root@master ~]# kubectl explain cm #简写
[root@master ~]# kubectl create configmap --help

  

简单的我们可以用命令行来创建configmap。

[root@master ~]# kubectl create configmap nginx-config --from-literal=nginx_port=80 --from-literal=server_name=myapp.zhixin.com
configmap/nginx-config created

  

[root@master ~]# kubectl get cm
NAME DATA AGE
nginx-config 2 3m

  

[root@master ~]# kubectl describe cm nginx-config
Name: nginx-config
Namespace: default
Labels: <none>
Annotations: <none>
Data
====
nginx_port:
----
80
server_name:
----
myapp.zhixin.com

  

下面我们用配置清单的方式来创建configmap:

[root@master configmap]# cat www.conf
server {
server_name myapp.zhixin.com;
listen 80;
root /data/web/html;
}

  

[root@master configmap]# kubectl create configmap nginx-www --from-file=www.conf
configmap/nginx-www created

  

[root@master configmap]# kubectl get cm
NAME DATA AGE
nginx-config 2 3m
nginx-www 1 7s

  

[root@master configmap]# kubectl describe cm nginx-www
Name: nginx-www
Namespace: default
Labels: <none>
Annotations: <none>
Data
====
www.conf:
----
server {
server_name myapp.zhixin.com;
listen 80;
root /data/web/html;
}

  

我们创建的configmap,可用ENV等方式注入到Pod中。

我们用ENV方式来把configmap注入到pod中去。

[root@master configmap]# cat pod-configmap.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-cm-1
namespace: default
labels:
app: myapp #kv格式的,也可以用花括号表示
tier: frontend #定义所属的层次
annotations:
chenzx.com/created-by: "cluster-admin" #这是注解的键值对
spec:
containers:
- name: myapp #前面的-号表示这是一个列表格式的,也可以用中括号表示
image: tomcat
ports:
- name: http
containerPort: 80
env: #这是一个容器的属性
- name: NGINX_SERVER_PORT
valueFrom: #kubectl explain pods.spec.containers.env.valueFrom
configMapKeyRef: #表示我们要引用一个configmap来获取数据
name: nginx-config #这是configmap的名字,也就是通过kubectl get cm获取的名字
key: nginx_port #通过kubectl describe cm nginx-config的键
#下面开始引用第二个环境变量
- name: NGINX_SERVER_NAME
valueFrom:
configMapKeyRef:
name: nginx-config
key: server_name

  

[root@master configmap]# kubectl apply -f pod-configmap.yaml
pod/pod-cm-1 created

  

这样,我们就建立了一个pod-cm-1的pod,并且这个pod的配置文件来自于configmap。

[root@master configmap]# kubectl get pods
NAME READY STATUS RESTARTS AGE
pod-cm-1 0/1 Running 0 15m

  

[root@master configmap]# kubectl exec -it pod-cm-1 -- /bin/sh
# printenv
NGINX_SERVER_PORT=80
NGINX_SERVER_NAME=myapp.zhixin.com

  

[root@master configmap]# kubectl edit cm nginx-config
configmap/nginx-config edited

  

通过edit方式编辑的配置文件,在Pod里面不会立即理解生效,需要重启pod才能生效。

[root@master configmap]# kubectl delete -f pod-configmap.yaml
pod "pod-cm-1" deleted

  

下面我们用配置mount存储卷的方法把configmap注入到pod中。

[root@master configmap]# cat pod-configmap2.ymal
apiVersion: v1
kind: Pod
metadata:
name: pod-cm-2
namespace: default
labels:
app: myapp #kv格式的,也可以用花括号表示
tier: frontend #定义所属的层次
annotations:
chenzx.com/created-by: "cluster-admin" #这是注解的键值对
spec:
containers:
- name: myapp #前面的-号表示这是一个列表格式的,也可以用中括号表示
image: ikubernetes/myapp:v1
ports:
- name: http
containerPort: 80
volumeMounts:
- name: nginxconf
mountPath: /etc/nginx/conf.d/
readOnly: true
volumes:
- name: nginxconf
configMap:
name: nginx-config

  

[root@master configmap]# kubectl apply -f pod-configmap2.ymal
pod/pod-cm-2 created

  

[root@master configmap]# kubectl get pods
NAME READY STATUS RESTARTS AGE
pod-cm-2 1/1 Running 0 1m

  

[root@master configmap]# kubectl exec -it pod-cm-2 -- /bin/sh
/ # cd /etc/nginx/conf.d/
/etc/nginx/conf.d # ls
nginx_port server_name
/etc/nginx/conf.d # ls -l
total 0
lrwxrwxrwx 1 root root 17 Sep 27 05:07 nginx_port -> ..data/nginx_port
lrwxrwxrwx 1 root root 18 Sep 27 05:07 server_name -> ..data/server_name
/etc/nginx/conf.d # cat nginx_port
8080

  

下面我们再把前面我们创建的www.conf文件注入到pod中:

[root@master configmap]# cat pod-configmap3.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-cm-3
namespace: default
labels:
app: myapp #kv格式的,也可以用花括号表示
tier: frontend #定义所属的层次
annotations:
chenzx.com/created-by: "cluster-admin" #这是注解的键值对
spec:
containers:
- name: myapp #前面的-号表示这是一个列表格式的,也可以用中括号表示
image: ikubernetes/myapp:v1
ports:
- name: http
containerPort: 80
volumeMounts:
- name: nginxconf
mountPath: /etc/nginx/conf.d/
readOnly: true
volumes:
- name: nginxconf
configMap:
name: nginx-www

  

[root@master configmap]# kubectl apply -f pod-configmap3.yaml
pod/pod-cm-3 created

  

[root@master configmap]#
[root@master configmap]# kubectl get pods
NAME READY STATUS RESTARTS AGE
client 0/1 Error 0 16d
pod-cm-3

  

[root@master configmap]# kubectl exec -it pod-cm-3 -- /bin/sh
/ # cd /etc/nginx/conf.d/
/etc/nginx/conf.d # ls
www.conf
/etc/nginx/conf.d # cat www.conf
server {
server_name myapp.zhixin.com;
listen 80;
root /data/web/html;
}

  

通过上面的例子,大家看到我们已经把 www.conf中的内容注入到了pod myapp中。

[root@master configmap]# kubectl edit cm nginx-www

  

改个端口,然后再到pod里面,多等一会就会看到刚才修改的在pod里面生效了。

[root@master configmap]# kubectl exec -it pod-cm-3 -- /bin/sh
/ # cd /etc/nginx/conf.d/
/etc/nginx/conf.d # cat www.conf
server {
server_name myapp.zhixin.com;
listen 8081;
root /data/web/html;
[root@master configmap]# /etc/init.d/nginx reload #重载nginx使8081端口生效

  

如果我们期望只注入部分,而非所有,该怎么做呢?

[root@master configmap]# kubectl explain pods.spec.volumes.configMap.items
[root@master configmap]# kubectl create secret generic --help

  

通过items来注入部分,这里面就不演示了,请读者自行解决。

secret

功能和configmap一样,只不过secret配置中心存储的配置文件不是明文的。

[root@master configmap]# kubectl create secret --help
generic:保存密码用的类型
tls:保存证书用的类型
docker-registry:保存docker认证信息用的类型,比如从私有docker仓库拉镜像时,就用这个类型。
备注:k8s拖镜像的进程是kublete

  

[root@master configmap]# kubectl explain pods.spec.imagePullSecrets
如果是从私有仓库拉镜像,就用imagePullSecrets存登录验证的信息

  

例子:

[root@master configmap]# kubectl create secret generic mysql-root-password --from-literal=password=123456
secret/mysql-root-password created

  

[root@master configmap]# kubectl get secret
NAME TYPE DATA AGE
default-token-5r85r kubernetes.io/service-account-token 3 19d
mysql-root-password Opaque 1 40s
tomcat-ingress-secret kubernetes.io/tls 2 2d

  

[root@master configmap]# kubectl describe secret mysql-root-password
Name: mysql-root-password
Namespace: default
Labels: <none>
Annotations: <none>
Type: Opaque
Data
====
password: 6 bytes

  

看到password的内容就是base64加密的形式了。

[root@master configmap]# kubectl get secret mysql-root-password -o yaml
apiVersion: v1
data:
password: MTIzNDU2
kind: Secret
metadata:
creationTimestamp: 2018-09-27T06:01:24Z
name: mysql-root-password
namespace: default
resourceVersion: "2482795"
selfLink: /api/v1/namespaces/default/secrets/mysql-root-password
uid: c3d3e8ec-c21a-11e8-bb35-005056a24ecb
type: Opaque

  

可以用命令base64命令进行明文解码:

[root@master configmap]# echo MTIzNDU2 |base64 -d
123456

  

可见secret是防君子不防小人,是个伪加密,哈哈。

下面我们把secret通过env的方式注入到pod里面。

[root@master configmap]# cat  pod-secret-1.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-secret-1
namespace: default
labels:
app: myapp #kv格式的,也可以用花括号表示
tier: frontend #定义所属的层次
annotations:
chenzx.com/created-by: "cluster-admin" #这是注解的键值对
spec:
containers:
- name: myapp #前面的-号表示这是一个列表格式的,也可以用中括号表示
image: tomcat
ports:
- name: http
containerPort: 80
env: #这是一个容器的属性
- name: MYSQL_ROOT_PASSWORD
valueFrom: #kubectl explain pods.spec.containers.env.valueFrom
secretKeyRef: #表示我们要引用一个configmap来获取数据
name: mysql-root-password #这是configmap的名字,也就是通过kubectl get secret获取的名字
key: password #通过kubectl describe secret mysql-root-password的键
#下面开始引用第二个环境变量
- name: NGINX_SERVER_NAME
valueFrom:
configMapKeyRef:
name: nginx-config
key: server_name

  

[root@master configmap]# kubectl apply -f pod-secret-1.yaml
pod/pod-secret-1 created

  

[root@master configmap]# kubectl get pods
NAME READY STATUS RESTARTS AGE
pod-secret-1 1/1 Running 0 1m

  

[root@master configmap]# kubectl exec -it pod-secret-1 -- /bin/sh
# printenv
MYSQL_ROOT_PASSWORD=123456

  

看到secret通过env的方式,是以明文注入到pod里面的。

另外,secret还可以用mount的方式注入pod中,这部分略,如需要请参考本小节的configmap的相关例子。

因为pod是有生命周期的,pod一重启,里面的数据就没了。所以我们需要数据持久化存储。

在k8s中,存储卷不属于容器,而是属于pod。也就是说同一个pod中的容器可以共享一个存储卷。

存储卷可以是宿主机上的目录,也可以是挂载在宿主机上的外部设备。

存储卷类型

 emptyDIR存储卷 :pod一重启,存储卷也删除,这叫emptyDir存储卷。一般用于当做临时空间或缓存关系

  hostPath存储卷 :宿主机上目录作为存储卷,这种也不是真正意义实现了数据持久性。

SAN(iscsi)或NAS(nfs、cifs): 网络存储设备

  分布式存储(ceph,glusterfs,cephfs,rbd) 

 云存储(亚马逊的EBS,Azure Disk,阿里云): 这种一般k8s也在云上部署的。

关键数据一定要有异地备份,否则数据一删,多少个副本都没用。

1
[root@master ingress]# kubectl explain pods.spec.volumes

hostPath

功能:使用宿主机上目录作为存储卷,这种也不是真正意义实现了数据持久性。

1
2
3
4
5
6
7
[root@master ~]# kubectl explain pods.spec.volumes.hostPath.type
KIND:     Pod
VERSION:  v1
FIELD:    type <string>
DESCRIPTION:
     Type for HostPath Volume Defaults to "" More info:
     https://kubernetes.io/docs/concepts/storage/volumes#hostpath

查看帮助: https://kubernetes.io/docs/concepts/storage/volumes#hostpath

hostPath.type的类型说明:

DirectoryOrCreate:意思是我们要挂载的路径在宿主机上是个已经存在的目录,不存在就创建一个新的目录。

Directory:宿主机上必须实现存在目录,如果不存在就报错

FileOrCreate:表示挂载的是文件,如果不存在就挂载一个文件。文件也可以当做存储挂载的。

File:表示要挂载的文件必须事先存在,否则就报错。

Socket:表示必须是一个Socket类型的文件。

CharDevice:表示是一个字符类型的设备文件。

BlockDevice:表示的是一个块类型的设备文件。

例子:

[root@master volumes]# cat pod-hostpath-vol.yaml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
apiVersion: v1
kind: Pod
metadata:
  name: pod-vol-hostpath
  namespace: default
spec:
  containers:
  - name: myapp
    image: ikubernetes/myapp:v1
    volumeMounts:
    - name: html #存储卷的名字叫html
      mountPath: /usr/share/nginx/html/ #挂载路径
  volumes:
  - name: html
    hostPath:
      path: /data/pod/volume1
      type: DirectoryOrCreate
1
2
[root@master volumes]# kubectl apply -f pod-hostpath-vol.yaml 
pod/pod-vol-hostpath created

然后到node1节点上可以看到/data/pod/volume1目录已经创建出来了。

1
2
3
4
[root@master volumes]# kubectl get pods -o wide
NAME                             READY     STATUS             RESTARTS   AGE       IP             NODE
client                           0/1       Error              0          15d       10.244.2.4     node2
pod-vol-hostpath                 1/1       Running            0          4m        10.244.1.105   node1

当node1节点宕机后,pod就飘到node2节点上,并使用node2节点上的/data/pod/volume1目录。这就有问题了,因为node2节点上的目录并没有同步node1节点上目录的数据,所以出现数据不一致。

解决这个问题的方法就是使用类似nfs方法,让两个node节点共享一个存储。

使用nfs做共享存储

我这里为了方便,把master节点当做nfs存储。

1
[root@master ~]# yum -y install nfs-utils
1
[root@master ~]# mkdir /data/volumes
1
2
3
4
[root@master ~]# cat /etc/exports
#no_root_squash:登入 NFS 主机使用分享目录的使用者,如果是 root 的话,那么对于这个分享的目录来说,他就具有 root 的权限!这个项目『极不安全』,不建议使用! 
#root_squash:在登入 NFS 主机使用分享之目录的使用者如果是 root 时,那么这个使用者的权限将被压缩成为匿名使用者,通常他的 UID 与 GID 都会变成 nobody 那个系统账号的身份;
/data/volumes 172.16.0.0/16(rw,no_root_squash)
1
[root@master ~]# systemctl start nfs

在node1和node2上也安装nfs-utils包

1
[root@node1 ~]# yum -y install nfs-utils

在node1和node2上挂载:

1
[root@node1 ~]# mount -t nfs 172.16.1.100:/data/volumes /mnt

在master上:

1
[root@master ~]# kubectl explain pods.spec.volumes.nfs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
[root@master volumes]# cat pod-vol-nfs.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: pod-vol-nfs
  namespace: default
spec:
  containers:
  - name: myapp
    image: ikubernetes/myapp:v1
    volumeMounts:
    - name: html #存储卷的名字叫html
      mountPath: /usr/share/nginx/html/ #挂载路径,myapp容器里面的路径
  volumes:
  - name: html
    nfs:
      path: /data/volumes
      server: 172.16.1.100 #nfs server ip
1
[root@master volumes]# kubectl apply -f pod-vol-nfs.yaml
1
2
3
[root@master volumes]# kubectl get pods -o wide
NAME                             READY     STATUS             RESTARTS   AGE       IP             NODE
pod-vol-nfs                      1/1       Running            0          1m        10.244.1.106   node1
1
2
[root@master volumes]# cat /data/volumes/index.html
hello world
1
2
[root@master volumes]# curl  10.244.1.106 #容器的ip
hello world

可见容器使用的是nfs提供的共享存储。

不过,nfs自身没有冗余能力,所以如果nfs宕机了,数据也丢了。因此,我们一般用glusterfs或者cephfs分布式存储。

pvc和pv

用户只需要挂载pvc到容器中而不需要关注存储卷采用何种技术实现。pvc和pv的关系与pod和node关系类似,前者消耗后者的资源。pvc可以向pv申请指定大小的存储资源并设置访问模式。

在定义pod时,我们只需要说明我们要一个多大的存储卷就行了。pvc存储卷必须与当前namespace的pvc建立直接绑定关系。pvc必须与pv建立绑定关系。而pv是真正的某个存储设备上的空间。

1
[root@master volumes]# kubectl explain pods.spec.volumes.persistentVolumeClaim
1
[root@master volumes]# kubectl explain pvc

一个pvc和pv是一一对应关系,一旦一个pv被一个pvc绑定了,那么这个pv就不能被其他pvc绑定了。

一个pvc是可以被多个pod所访问的。

在存储机器上建立如下几个目录(这里我以master节点做存储,生产中可以单独拿出 一个机器做存储):

1
[root@master volumes]# mkdir v{1,2,3,4,5}
1
2
3
4
5
6
7
8
[root@master volumes]# cat  /etc/exports
#no_root_squash:登入 NFS 主机使用分享目录的使用者,如果是 root 的话,那么对于这个分享的目录来说,他就具有 root 的权限!这个项目『极不安全』,不建议使用! 
#root_squash:在登入 NFS 主机使用分享之目录的使用者如果是 root 时,那么这个使用者的权限将被压缩成为匿名使用者,通常他的 UID 与 GID 都会变成 nobody 那个系统账号的身份;
/data/volumes/v1 172.16.0.0/16(rw,no_root_squash) 
/data/volumes/v2 172.16.0.0/16(rw,no_root_squash) 
/data/volumes/v3 172.16.0.0/16(rw,no_root_squash) 
/data/volumes/v4 172.16.0.0/16(rw,no_root_squash) 
/data/volumes/v5 172.16.0.0/16(rw,no_root_squash)
1
2
3
4
5
6
[root@master volumes]# exportfs  -arv #不用重启nfs服务,配置文件就会生效
exporting 172.16.0.0/16:/data/volumes/v5
exporting 172.16.0.0/16:/data/volumes/v4
exporting 172.16.0.0/16:/data/volumes/v3
exporting 172.16.0.0/16:/data/volumes/v2
exporting 172.16.0.0/16:/data/volumes/v1
1
2
3
4
5
6
7
[root@master volumes]# showmount -e
Export list for master:
/data/volumes/v5 172.16.0.0/16
/data/volumes/v4 172.16.0.0/16
/data/volumes/v3 172.16.0.0/16
/data/volumes/v2 172.16.0.0/16
/data/volumes/v1 172.16.0.0/16
1
[root@master volumes]# kubectl explain pv.spec.nfs
1
2
3
4
5
[root@master ~]# kubectl explain pv.spec
FIELDS:
   accessModes<[]string>
     AccessModes contains all ways the volume can be mounted. More info:
     https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes

访问  https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes看帮助。

accessModes模式有:

ReadWriteOnce:单路读写,可以简写为RWO

ReadOnlyMany:多路只读,可以简写为ROX

ReadWriteMany :多路读写,可以简写为RWX

不同类型的存储卷支持的accessModes也不同。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
[root@master volumes]# cat pv-demo.yaml 
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv001 #注意,定义pv时一定不要加名称空间,因为pv是属于整个集群,而不是属于某个名称空间。但pvc是属于某个名称空间的
  labels:
    name: pv001
spec:
  nfs:
    path: /data/volumes/v1
    server: 172.16.1.100
  accessModes: ["ReadWriteMany","ReadWriteOnce"]
  capacity: #分配磁盘空间大小
    storage: 1Gi
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv002 #注意,定义pv时一定不要加名称空间,因为pv是属于整个集群,而不是属于某个名称空间。但pvc是属于某个名称空间的
  labels:
    name: pv002
spec:
  nfs:
    path: /data/volumes/v2
    server: 172.16.1.100
  accessModes: ["ReadWriteOnce"]
  capacity: #分配磁盘空间大小
    storage: 2Gi
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv003 #注意,定义pv时一定不要加名称空间,因为pv是属于整个集群,而不是属于某个名称空间。但pvc是属于某个名称空间的
  labels:
    name: pv003
spec:
  nfs:
    path: /data/volumes/v3
    server: 172.16.1.100
  accessModes: ["ReadWriteMany","ReadWriteOnce"]
  capacity: #分配磁盘空间大小
    storage: 1Gi
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv004 #注意,定义pv时一定不要加名称空间,因为pv是属于整个集群,而不是属于某个名称空间。但pvc是属于某个名称空间的
  labels:
    name: pv004
spec:
  nfs:
    path: /data/volumes/v4
    server: 172.16.1.100
  accessModes: ["ReadWriteMany","ReadWriteOnce"]
  capacity: #分配磁盘空间大小
    storage: 1Gi
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv005 #注意,定义pv时一定不要加名称空间,因为pv是属于整个集群,而不是属于某个名称空间。但pvc是属于某个名称空间的
  labels:
    name: pv005
spec:
  nfs:
    path: /data/volumes/v5
    server: 172.16.1.100
  accessModes: ["ReadWriteMany","ReadWriteOnce"]
  capacity: #分配磁盘空间大小
    storage: 1Gi
1
2
3
4
5
6
[root@master volumes]# kubectl apply -f pv-demo.yaml 
persistentvolume/pv001 created
persistentvolume/pv002 created
persistentvolume/pv003 created
persistentvolume/pv004 created
persistentvolume/pv005 created
1
2
3
4
5
6
7
[root@master volumes]# kubectl get pv
NAME      CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM     STORAGECLASS   REASON    AGE
pv001     1Gi        RWO,RWX        Retain           Available                                      2m
pv002     2Gi        RWO            Retain           Available                                      2m
pv003     1Gi        RWO,RWX        Retain           Available                                      2m
pv004     1Gi        RWO,RWX        Retain           Available                                      2m
pv005     1Gi        RWO,RWX        Retain           Available                                      2m

 回收策略 :如果某个pvc在pv里面存数据了,后来pvc删了,那么 pv里面的数据怎么处理呢。有如下几种策略:

reclaim_policy:即pvc删了,但是pv里面的数据不擅长,还保留着。

recycle:即pvc删了,那么就把pv里面的数据也删了。

delete:即pvc删了,那么就把pv也删了。

下面我们再创建pvc的清单文件。

1
[root@master ~]# kubectl explain pvc.spec
1
[root@master ~]# kubectl explain pods.spec.volumes.persistentVolumeClaim
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
[root@master volumes]# cat pod-vol-pvc.yaml 
apiVersion: v1
kind: PersistentVolumeClaim #简称pvc
metadata:
  name: mypvc
  namespace: default #pvc和pod是在同一个名称空间
spec:
  accessModes: ["ReadWriteMany"] #一定是pv策略的子集
  resources:
    requests:
      storage: 1Gi #表示我要pvc 为1G的空间
---
apiVersion: v1
kind: Pod
metadata:
  name: pod-vol-pvc
  namespace: default
spec:
  containers:
  - name: myapp
    image: ikubernetes/myapp:v1
    volumeMounts:
    - name: html #存储卷的名字叫html
      mountPath: /usr/share/nginx/html/ #挂载路径
  volumes:
  - name: html
    persistentVolumeClaim:
      claimName: mypvc #表示我要使用哪个pvc
1
2
3
[root@master volumes]# kubectl apply -f pod-vol-pvc.yaml 
persistentvolumeclaim/mypvc created
pod/pod-vol-pvc created
1
2
3
4
5
6
7
[root@master volumes]# kubectl get pv
NAME      CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM           STORAGECLASS   REASON    AGE
pv001     1Gi        RWO,RWX        Retain           Available                                            7h
pv002     2Gi        RWO            Retain           Available                                            7h
pv003     1Gi        RWO,RWX        Retain           Available                                            7h
pv004     1Gi        RWO,RWX        Retain           Bound       default/mypvc                            7h
pv005     1Gi        RWO,RWX        Retain           Available                                            7h

上面看到pv004被default名称空间的mypvc绑定了。

1
2
3
[root@master volumes]# kubectl get pvc
NAME      STATUS    VOLUME    CAPACITY   ACCESS MODES   STORAGECLASS   AGE
mypvc     Bound     pv004     1Gi        RWO,RWX                       33m
1
2
3
4
[root@master volumes]# kubectl get pods 
NAME                             READY     STATUS             RESTARTS   AGE
client                           0/1       Error              0          16d
pod-vol-pvc                      1/1       Running            0          35m

生产上,pv并不属于node节点,而是独立于node节点的。所以,node节点坏了,pv里面的数据还在。另外,pod才是属于node节点的。

在k8s 1.10之后,不能手工从底层删除pv,这样做很安全。

StorageClass(存储类)

Kubernetes集群管理员通过提供不同的存储类,可以满足用户不同的服务质量级别、备份策略和任意策略要求的存储需求。动态存储卷供应使用StorageClass进行实现,其允许存储卷按需被创建。如果没有动态存储供应,Kubernetes集群的管理员将不得不通过手工的方式类创建新的存储卷。通过动态存储卷,Kubernetes将能够按照用户的需要,自动创建其需要的存储。

storageclass底层可以是glusterfs,cephfs等不同的集群。

configmap

configmap和secret是两种特殊的存储卷,它们不是给pod提供存储空间用的,而是给我们的管理员或者用户提供了从外部向pod内部注入信息的方式。

  configmap :把配置文件放在配置中心上,然后多个pod读取配置中心的配置文件。不过,configmap中的配置信息都是明文的,所以不安全。

  secret: 功能和configmap一样,只不过配置中心存储的配置文件不是明文的。

configmap和secret也是专属于某个名称空间的。

1
2
3
[root@master ~]# kubectl explain configmap
[root@master ~]# kubectl explain cm #简写
[root@master ~]# kubectl create configmap --help

简单的我们可以用命令行来创建configmap。

1
2
[root@master ~]# kubectl create configmap nginx-config --from-literal=nginx_port=80 --from-literal=server_name=myapp.zhixin.com
configmap/nginx-config created
1
2
3
[root@master ~]# kubectl get cm
NAME           DATA      AGE
nginx-config   2         3m
1
2
3
4
5
6
7
8
9
10
11
12
13
[root@master ~]# kubectl describe cm nginx-config
Name:         nginx-config
Namespace:    default
Labels:       <none>
Annotations:  <none>
Data
====
nginx_port:
----
80
server_name:
----
myapp.zhixin.com

下面我们用配置清单的方式来创建configmap:

1
2
3
4
5
6
[root@master configmap]# cat www.conf 
server {
      server_name myapp.zhixin.com;
      listen 80;
      root /data/web/html;
}
1
2
[root@master configmap]# kubectl create configmap nginx-www --from-file=www.conf
configmap/nginx-www created
1
2
3
4
[root@master configmap]# kubectl get cm
NAME           DATA      AGE
nginx-config   2         3m
nginx-www      1         7s
1
2
3
4
5
6
7
8
9
10
11
12
13
14
[root@master configmap]# kubectl describe cm nginx-www 
Name:         nginx-www
Namespace:    default
Labels:       <none>
Annotations:  <none>
Data
====
www.conf:
----
server {
      server_name myapp.zhixin.com;
      listen 80;
      root /data/web/html;
}

我们创建的configmap,可用ENV等方式注入到Pod中。

我们用ENV方式来把configmap注入到pod中去。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
[root@master configmap]# cat pod-configmap.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: pod-cm-1
  namespace: default
  labels:
    app: myapp  #kv格式的,也可以用花括号表示
    tier: frontend #定义所属的层次
  annotations:
    chenzx.com/created-by: "cluster-admin" #这是注解的键值对
spec:
  containers: 
  - name: myapp  #前面的-号表示这是一个列表格式的,也可以用中括号表示
    image: tomcat 
    ports:
    - name: http
      containerPort: 80
    env: #这是一个容器的属性
    - name: NGINX_SERVER_PORT
      valueFrom: #kubectl explain pods.spec.containers.env.valueFrom
        configMapKeyRef: #表示我们要引用一个configmap来获取数据
          name: nginx-config #这是configmap的名字,也就是通过kubectl get cm获取的名字
          key: nginx_port #通过kubectl describe cm nginx-config的键
     #下面开始引用第二个环境变量
    - name: NGINX_SERVER_NAME
      valueFrom:
        configMapKeyRef:
          name: nginx-config
          key: server_name
1
2
[root@master configmap]# kubectl apply -f pod-configmap.yaml 
pod/pod-cm-1 created

这样,我们就建立了一个pod-cm-1的pod,并且这个pod的配置文件来自于configmap。

1
2
3
[root@master configmap]# kubectl get pods
NAME                             READY     STATUS                       RESTARTS   AGE
pod-cm-1                         0/1       Running   0          15m
1
2
3
4
[root@master configmap]# kubectl exec -it pod-cm-1 -- /bin/sh
# printenv
NGINX_SERVER_PORT=80
NGINX_SERVER_NAME=myapp.zhixin.com
1
2
[root@master configmap]# kubectl edit cm nginx-config
configmap/nginx-config edited

通过edit方式编辑的配置文件,在Pod里面不会立即理解生效,需要重启pod才能生效。

1
2
[root@master configmap]# kubectl delete -f pod-configmap.yaml 
pod "pod-cm-1" deleted

下面我们用配置mount存储卷的方法把configmap注入到pod中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
[root@master configmap]# cat pod-configmap2.ymal 
apiVersion: v1
kind: Pod
metadata:
  name: pod-cm-2
  namespace: default
  labels:
    app: myapp  #kv格式的,也可以用花括号表示
    tier: frontend #定义所属的层次
  annotations:
    chenzx.com/created-by: "cluster-admin" #这是注解的键值对
spec:
  containers: 
  - name: myapp  #前面的-号表示这是一个列表格式的,也可以用中括号表示
    image: ikubernetes/myapp:v1 
    ports:
    - name: http
      containerPort: 80
    volumeMounts:
    - name: nginxconf
      mountPath: /etc/nginx/conf.d/
      readOnly: true
  volumes:
  - name: nginxconf
    configMap:
      name: nginx-config
1
2
[root@master configmap]# kubectl apply -f pod-configmap2.ymal 
pod/pod-cm-2 created
1
2
3
[root@master configmap]# kubectl get pods
NAME                             READY     STATUS             RESTARTS   AGE
pod-cm-2                         1/1       Running            0          1m
1
2
3
4
5
6
7
8
9
10
[root@master configmap]# kubectl exec -it pod-cm-2 -- /bin/sh
/ # cd /etc/nginx/conf.d/
/etc/nginx/conf.d # ls
nginx_port   server_name
/etc/nginx/conf.d # ls -l
total 0
lrwxrwxrwx    1 root     root            17 Sep 27 05:07 nginx_port -> ..data/nginx_port
lrwxrwxrwx    1 root     root            18 Sep 27 05:07 server_name -> ..data/server_name
/etc/nginx/conf.d # cat nginx_port
8080

下面我们再把前面我们创建的www.conf文件注入到pod中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
[root@master configmap]# cat pod-configmap3.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: pod-cm-3
  namespace: default
  labels:
    app: myapp  #kv格式的,也可以用花括号表示
    tier: frontend #定义所属的层次
  annotations:
    chenzx.com/created-by: "cluster-admin" #这是注解的键值对
spec:
  containers: 
  - name: myapp  #前面的-号表示这是一个列表格式的,也可以用中括号表示
    image: ikubernetes/myapp:v1 
    ports:
    - name: http
      containerPort: 80
    volumeMounts:
    - name: nginxconf
      mountPath: /etc/nginx/conf.d/
      readOnly: true
  volumes:
  - name: nginxconf
    configMap:
      name: nginx-www
1
2
[root@master configmap]# kubectl apply -f pod-configmap3.yaml 
pod/pod-cm-3 created
1
2
3
4
5
[root@master configmap]# 
[root@master configmap]# kubectl get pods
NAME                             READY     STATUS             RESTARTS   AGE
client                           0/1       Error              0          16d
pod-cm-3                         1/1       Running            0          1m
1
2
3
4
5
6
7
8
9
10
[root@master configmap]# kubectl exec -it pod-cm-3 -- /bin/sh
/ # cd /etc/nginx/conf.d/
/etc/nginx/conf.d # ls
www.conf
/etc/nginx/conf.d # cat www.conf 
server {
      server_name myapp.zhixin.com;
      listen 80;
      root /data/web/html;
}

通过上面的例子,大家看到我们已经把 www.conf中的内容注入到了pod myapp中。

1
[root@master configmap]# kubectl edit cm nginx-www

改个端口,然后再到pod里面,多等一会就会看到刚才修改的在pod里面生效了。

1
2
3
4
5
6
7
8
[root@master configmap]# kubectl exec -it pod-cm-3 -- /bin/sh
/ # cd /etc/nginx/conf.d/
/etc/nginx/conf.d # cat www.conf 
server {
      server_name myapp.zhixin.com;
      listen 8081;
      root /data/web/html;
[root@master configmap]# /etc/init.d/nginx reload #重载nginx使8081端口生效

如果我们期望只注入部分,而非所有,该怎么做呢?

1
2
[root@master configmap]# kubectl explain pods.spec.volumes.configMap.items
[root@master configmap]# kubectl create secret generic --help

通过items来注入部分,这里面就不演示了,请读者自行解决。

secret

功能和configmap一样,只不过secret配置中心存储的配置文件不是明文的。

1
2
3
4
5
 [root@master configmap]# kubectl create secret --help
 generic:保存密码用的类型
 tls:保存证书用的类型
 docker-registry:保存docker认证信息用的类型,比如从私有docker仓库拉镜像时,就用这个类型。
 备注:k8s拖镜像的进程是kublete
1
2
[root@master configmap]# kubectl explain pods.spec.imagePullSecrets
如果是从私有仓库拉镜像,就用imagePullSecrets存登录验证的信息

例子:

1
2
[root@master configmap]# kubectl create secret generic mysql-root-password --from-literal=password=123456
secret/mysql-root-password created
1
2
3
4
5
[root@master configmap]# kubectl get secret
NAME                    TYPE                                  DATA      AGE
default-token-5r85r     kubernetes.io/service-account-token   3         19d
mysql-root-password     Opaque                                1         40s
tomcat-ingress-secret   kubernetes.io/tls                     2         2d
1
2
3
4
5
6
7
8
9
[root@master configmap]# kubectl describe secret mysql-root-password
Name:         mysql-root-password
Namespace:    default
Labels:       <none>
Annotations:  <none>
Type:  Opaque
Data
====
password:  6 bytes

看到password的内容就是base64加密的形式了。

1
2
3
4
5
6
7
8
9
10
11
12
13
[root@master configmap]# kubectl get secret mysql-root-password -o yaml
apiVersion: v1
data:
  password: MTIzNDU2
kind: Secret
metadata:
  creationTimestamp: 2018-09-27T06:01:24Z
  name: mysql-root-password
  namespace: default
  resourceVersion: "2482795"
  selfLink: /api/v1/namespaces/default/secrets/mysql-root-password
  uid: c3d3e8ec-c21a-11e8-bb35-005056a24ecb
type: Opaque

可以用命令base64命令进行明文解码:

1
2
[root@master configmap]# echo MTIzNDU2 |base64 -d
123456

可见secret是防君子不防小人,是个伪加密,哈哈。

下面我们把secret通过env的方式注入到pod里面。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
[root@master configmap]# cat  pod-secret-1.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: pod-secret-1
  namespace: default
  labels:
    app: myapp  #kv格式的,也可以用花括号表示
    tier: frontend #定义所属的层次
  annotations:
    chenzx.com/created-by: "cluster-admin" #这是注解的键值对
spec:
  containers: 
  - name: myapp  #前面的-号表示这是一个列表格式的,也可以用中括号表示
    image: tomcat 
    ports:
    - name: http
      containerPort: 80
    env: #这是一个容器的属性
    - name: MYSQL_ROOT_PASSWORD
      valueFrom: #kubectl explain pods.spec.containers.env.valueFrom
        secretKeyRef: #表示我们要引用一个configmap来获取数据
          name: mysql-root-password  #这是configmap的名字,也就是通过kubectl get secret获取的名字
          key: password #通过kubectl describe secret mysql-root-password的键
     #下面开始引用第二个环境变量
    - name: NGINX_SERVER_NAME
      valueFrom:
        configMapKeyRef:
          name: nginx-config
          key: server_name
1
2
[root@master configmap]# kubectl apply -f pod-secret-1.yaml 
pod/pod-secret-1 created
1
2
3
[root@master configmap]# kubectl get pods
NAME                             READY     STATUS             RESTARTS   AGE
pod-secret-1                     1/1       Running            0          1m
1
2
3
[root@master configmap]# kubectl exec -it pod-secret-1 -- /bin/sh
# printenv
MYSQL_ROOT_PASSWORD=123456

看到secret通过env的方式,是以明文注入到pod里面的。

另外,secret还可以用mount的方式注入pod中,这部分略,如需要请参考本小节的configmap的相关例子。

k8s系列---存储卷pv/pvc。configMap/secert的更多相关文章

  1. k8s之存储卷及pvc

    1.存储卷概述 因为pod是有生命周期的,pod一重启,里面的数据就没了,所以我们需要数据持久化存储,在k8s中,存储卷不属于容器,而是属于pod,也就是说同一个pod中的容器可以共享一个存储卷,存储 ...

  2. Kubernetes 学习13 kubernetes pv pvc configmap 和secret

    一.概述 1.我们在pvc申请的时候未必就有现成的pv能正好符合这个pvc在申请中指定的条件,毕竟上一次的成功是我们有意设定了有一些满足有一些不满足的前提下我们成功创建了一个pvc并且被pod绑定所使 ...

  3. k8s volume存储卷

    k8s volume存储卷   介绍 volume存储卷是Pod中能够被多个容器访问的共享目录,kubernetes的volume概念,用途和目的与docker的volume比较类似,但两者不能等价, ...

  4. K8s的存储卷使用总结

    K8s的存储卷: 它有四种存储卷: 1. emptyDir: 空目录,这种存储卷会随着Pod的删除而被清空,它一般作为缓存目录使用,或临时目录, 当做缓存目录时,通常会将一块内存空间映射到该目录上,让 ...

  5. 6.k8s.存储Volume.PV.PVC

    #Volume Volume 解决数据持久化和容器间共享数据 Kubernetes支持几十种类型的后端存储卷 #hostPath挂载实例,挂载Node节点/tmp/test-volume目录到容器/t ...

  6. k8s volume存储卷(四)

    介绍 volume存储卷是Pod中能够被多个容器访问的共享目录,kubernetes的volume概念,用途和目的与docker的volume比较类似,但两者不能等价,首先,kubernetes中的v ...

  7. k8s的存储卷

    存储卷查看:kubectl explain pods.spec.volumes 一.简单的存储方式 1)2个容器之间共享存储..(删除则数据消失) apiVersion: v1 kind: Pod m ...

  8. 12.k8s的存储卷创建过程

    数据持久化需要数据卷.kubernetes生态提供海量的存储驱动和存储使用方式. [root@master song]# cat pod-demo.yml apiVersion: v1 kind: P ...

  9. k8s系列----索引

    day1:k8s集群准备搭建和相关介绍 day2:k8spod介绍与创建 day3:k8sService介绍及创建 day4:ingress资源和ingress-controller day5:存储卷 ...

随机推荐

  1. 1、Vue 实战-入门篇

    先决条件:需要 Node.js . npm 基础. 如果没有基础看先看下面简单的两点介绍. 1.npm 命令介绍. 1.所有命令  -h 可以查看.也可以从官网查 docs,结果如下. --help ...

  2. Oracle v_$和v$的解释

    以v_$mystat和v$mystat具体说明 grant语句中使用的v_$mystat和test用户访问的v$mystat不一样 这里说一下 v$mystat 和 v_$mystat 的区别 初始状 ...

  3. 为什么Mozilla Thunderbird无法登陆腾讯企业邮?

    (一)问题描述 登陆腾讯企业邮提示"无法登录到服务器.可能是配置.用户名或者密码错误." (二)解决方案 手动配置 IMAP | imap.exmail.qq.com | 993 ...

  4. 史上最简约的vi教程,复制和粘贴

    上一篇博客,讲了"新手"如何"入门"vi,解决了"两眼一抹黑"的情况.知道在vi下如何进行基本的操作,如部署在Linux下的项目,修改配置文 ...

  5. Java语法进阶16-Lambda-Stream-Optional

    Lambda 大年初二,大门不出二门不迈.继续学习! 函数式接口 Lambda表达式其实就是实现SAM接口的语法糖,所谓SAM接口就是Single Abstract Method,即该接口中只有一个抽 ...

  6. Java中SMB的相关应用

    目录 SMB 服务操作 Ⅰ SMB简介 Ⅱ SMB配置 2.1 Windows SMB Ⅲ 添加SMB依赖 Ⅳ 路径格式 Ⅴ 操作共享 Ⅵ 登录验证 SMB 服务操作 Ⅰ SMB简介 ​ SMB(全称 ...

  7. Windows和Linux换行的区别

    Linux下的换行符是: "\n" Windows的换行符 : "\r\n" r = return n = newline TODO 原因: 根据平台自己转化的 ...

  8. mysql 常用sql操作总结

    批量修改表的引擎,从MyISAM修改为InnoDB 生成执行语句: select CONCAT('alter table ',table_name,' engine=InnoDB;') FROM in ...

  9. servlet 深入了解

    servlet  作用 在Java web b/s架构中,servlet扮演了重要的角色,作为一个中转处理的容器,他连接了客户端和服务器端的信息交互和处理.简单来说,客户端发送请求,传递到servle ...

  10. springboot中使用自定义注解实现策略模式,去除工厂模式的switch或ifelse,实现新增策略代码零修改

    前言 思路与模拟业务 源码地址 https://gitee.com/houzheng1216/springboot 整体思路就是通过注解在策略类上指定约定好的type,项目启动之后将所有有注解的typ ...