文档:https://kubernetes.io/zh-cn/docs/concepts/workloads/pods/
Pod 是可以在 Kubernetes 中创建和管理的、最小的可部署的计算单元。
#运行一个镜像版本为6.2.5 的redis程序
kubectl run redis --image=redis:6.2.5
[root@k8s1 redis]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
redis 1/1 Running 0 7m27s 10.244.0.15 k8s2 > >
[root@k8s1 redis]# vim pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: redis
spec:
# 定义容器,可以多个
containers:
- name: redis # 容器名字
image: redis:6.2.5 # 镜像
#启动pod
[root@k8s1 redis]# kubectl apply -f pod.yaml
#关闭pod
[root@k8s1 redis]# kubectl delete -f pod.yaml
若存在多Pod的情况下,某些应用可能需要在指定节点上运行。当然目前数据还未持久化,后面再说。
apiVersion: v1
kind: Pod
metadata:
name: redis
spec:
nodeName: node01 # 调度 Pod 到特定的节点
containers:
- name: redis # 容器名字
image: redis:6.2.5 # 镜像
Service是高于Pod的抽象,特性如下:
外部访问需要先经过Service层,再由Service转到不同的Pod。
Pod与Service可写在同一yaml,只需要使用—分割。
apiVersion: v1
kind: Pod
metadata:
name: redis
labels:
app: redis
spec:
containers:
- name: redis # 容器名字
image: redis:6.2.5 # 镜像
ports:
- containerPort: 6379
---
apiVersion: v1
kind: Service
metadata:
name: redis
spec:
selector:
app: redis
type: ClusterIP
ports:
- port: 6379
targetPort: 6379
启动一个Pod与一个Service
[root@k8s1 redis]# kubectl apply -f redis.yaml
pod/redis created
service/my-service created
[root@k8s1 redis]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
my-service ClusterIP 10.103.246.122 > 6379/TCP 54s
yaml文件中的kind类别为工作负载分类
文档:https://kubernetes.io/zh-cn/docs/concepts/workloads/controllers/deployment/
一个 Deployment 为 **Pod **和 **ReplicaSet **提供声明式的更新能力
**ReplicaSet :**负责在后台创建 Pod。 检查 ReplicaSet 的上线状态,查看其是否成功。
下面是一个 Deployment 示例。其中创建了一个 ReplicaSet,负责启动两个 nginx Pod:
apiVersion: apps/v1
kind: Deployment
metadata:
# 部署名字
name: redis-test
spec:
#声明启动副本数,意味着启动了两个副本
replicas: 2
# 用来查找关联的 Pod,所有标签都匹配才行
selector:
matchLabels:
app: redis-test
# 定义 Pod 相关数据
template:
metadata:
labels:
app: redis-test
spec:
containers:
- name: redis-test # 容器名字
image: redis:6.2.5 # 镜像
启动成功
以Deployment启动Pod后,将会一直保持两个redis副本,除非更改副本数。
#查看deployments
[root@k8s1 redis]# kubectl get deployments
NAME READY UP-TO-DATE AVAILABLE AGE
redis-test 2/2 2 2 4m37s
StatefulSet 适用情况:
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: redis
spec:
serviceName: redis
replicas: 4
selector:
matchLabels:
app: redis
template:
metadata:
labels:
app: redis
spec:
containers:
- name: redis
image: redis:6.2.5
# IfNotPresent 仅本地没有镜像时才远程拉,Always 永远都是从远程拉,Never 永远只用本地镜像,本地没有则报错
imagePullPolicy: IfNotPresent
---
apiVersion: v1
kind: Service
metadata:
name: redis
spec:
selector:
app: redis
type: ClusterIP
ports:
- port: 6379
targetPort: 6379
运行中Pod的Name出现明显变化。
[root@k8s1 redis]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
redis-0 1/1 Running 0 92s 10.244.0.24 k8s3 > >
redis-1 1/1 Running 0 92s 10.244.0.22 k8s2 > >
redis-2 1/1 Running 0 91s 10.244.0.23 k8s2 > >
redis-3 1/1 Running 0 90s 10.244.0.25 k8s3 > >
要连接指定 Pod,可以这样pod-name.service-name。
以上运行的Pod只要重启数据就会丢失,需要手动将数据挂载到本地磁盘上,但是这需要限定 pod 在这个节点上运行,否则就会挂载失败。
层级关系如图所示,SC为实际磁盘存储区,PV对SC进行划分,PVC对PV的空间进行申请,最后交由Pod使用。
将存储卷划分为不同的种类,例如:SSD,普通磁盘,本地磁盘,按需使用。文档
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: slow
provisioner: kubernetes.io/aws-ebs
parameters:
type: io1
iopsPerGB: "10"
fsType: ext4
描述卷的具体信息,例如磁盘大小,访问模式,可以由SC动态创建。文档
apiVersion: v1
kind: PersistentVolume
metadata:
name: mongodata
spec:
capacity:
storage: 2Gi
volumeMode: Filesystem # Filesystem(文件系统) Block(块)
accessModes:
- ReadWriteOnce # 卷可以被一个节点以读写方式挂载
persistentVolumeReclaimPolicy: Delete
storageClassName: local-storage
local:
path: /root/data
nodeAffinity:
required:
# 通过 hostname 限定在某个节点创建存储卷
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/hostname
operator: In
values:
- k8s2
对存储需求的一个申明,可以理解为一个申请单,系统根据这个申请单去找一个合适的 PV
PVC与PV必是一对一互相绑定的关系。
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: redisdata
spec:
accessModes: ["ReadWriteOnce"]
storageClassName: "local-storage"
resources:
requests:
storage: 2Gi
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: redis
spec:
serviceName: redis
replicas: 4
selector:
matchLabels:
app: redis
template:
metadata:
labels:
app: redis
spec:
containers:
- name: redis
image: redis:6.2.5
# IfNotPresent 仅本地没有镜像时才远程拉,Always 永远都是从远程拉,Never 永远只用本地镜像,本地没有则报错
imagePullPolicy: IfNotPresent
volumeMounts:
- mountPath: /data
name: redis-data
volumes:
- name: redis-data
persistentVolumeClaim:
claimName: redisdata
---
apiVersion: v1
kind: Service
metadata:
name: redis
spec:
selector:
app: redis
type: ClusterIP
ports:
- port: 6379
targetPort: 6379
因为SC指定存储在节点k8s2
上,所以所有的redis也都运行在k8s2
上。
[root@k8s1 redis]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
redis-0 1/1 Running 0 11s 10.244.0.24 k8s2 > >
redis-1 1/1 Running 0 10s 10.244.0.25 k8s2 > >
redis-2 1/1 Running 0 9s 10.244.0.26 k8s2 > >
redis-3 1/1 Running 0 8s 10.244.0.27 k8s2 > >
为了不受集群影响,数据可以挂载到NFS上实现任意Pod都可访问存储。
NFS(Network File System)是一种用于在网络上共享文件系统的协议。它允许多台计算机通过网络访问共享存储资源,就像访问本地文件系统一样。
NFS通过客户端-服务器模型工作,其中服务器上的文件系统称为NFS服务器,客户端计算机通过网络连接到该服务器以访问共享的文件和目录。NFS提供了对文件的透明访问,使得客户端可以像访问本地文件一样读取、写入和执行远程文件系统上的文件。
#安装nfs
[root@k8s1 /]# yum -y install nfs-utils
# 创建文件夹
[root@k8s1 /]# mkdir -p /nfs/data/
#修改权限
[root@k8s1 /]# chmod -R 777 /nfs/data
# 暴露文件夹
#编辑export文件,这个文件就是nfs默认的配置文件,要共享多个文件就都在这里注册一下,并创建相应目录并给与权限,也可以将*改为指定的IP地址,多个ip就空格隔开
[root@k8s1 /]# echo "/nfs/data/ *(insecure,rw,sync,no_root_squash)" > /etc/exports
#配置生效
[root@k8s1 /]# exportfs -r
#查看生效
[root@k8s1 /]# exportfs
/nfs/data <world>
/etc/exports
文件配置说明:
<输出目录> [客户端1 选项(访问权限,用户映射,其他)] [客户端2 选项(访问权限,用户映射,其他)]
a. 输出目录:
输出目录是指NFS系统中需要共享给客户机使用的目录;
b. 客户端:
客户端是指网络中可以访问这个NFS输出目录的计算机
客户端常用的指定方式
指定ip地址的主机:192.168.0.200
指定子网中的所有主机:192.168.0.0/24 192.168.0.0/255.255.255.0
指定域名的主机:david.bsmart.cn
指定域中的所有主机:.bsmart.cn
所有主机:
c. 选项:
选项用来设置输出目录的访问权限、用户映射等。NFS主要有3类选项:
访问权限
设置输出目录只读:ro
设置输出目录读写:rw
用户映射
all_squash:将远程访问的所有普通用户及所属组都映射为匿名用户或用户组(nfsnobody);
no_all_squash:与all_squash取反(默认设置);
root_squash:将root用户及所属组都映射为匿名用户或用户组(默认设置);
no_root_squash:与rootsquash取反;
anonuid=xxx:将远程访问的所有用户都映射为匿名用户,并指定该用户为本地用户(UID=xxx);
anongid=xxx:将远程访问的所有用户组都映射为匿名用户组账户,并指定该匿名用户组账户为本地用户组账户(GID=xxx);
其它选项
secure:限制客户端只能从小于1024的tcp/ip端口连接nfs服务器(默认设置);
insecure:允许客户端从大于1024的tcp/ip端口连接服务器;
sync:将数据同步写入内存缓冲区与磁盘中,效率低,但可以保证数据的一致性;
async:将数据先保存在内存缓冲区中,必要时才写入磁盘;
wdelay:检查是否有相关的写操作,如果有则将这些写操作一起执行,这样可以提高效率(默认设置);
no_wdelay:若有写操作则立即执行,应与sync配合使用;
subtree:若输出目录是一个子目录,则nfs服务器将检查其父目录的权限(默认设置);
no_subtree:即使输出目录是一个子目录,nfs服务器也不检查其父目录的权限,这样可以提高效率;
# 启动rpcbind服务
[root@k8s1 /]# systemctl enable rpcbind --now
# 启动nfs服务器
[root@k8s1 /]# systemctl enable nfs-server --now
Created symlink from /etc/systemd/system/multi-user.target.wants/nfs-server.service to /usr/lib/systemd/system/nfs-server.service.
#查看 RPC 服务的注册状况
[root@k8s1 /]# rpcinfo -p localhost
program vers proto port service
100000 4 tcp 111 portmapper
100000 3 tcp 111 portmapper
100000 2 tcp 111 portmapper
100000 4 udp 111 portmapper
#showmount测试
[root@k8s1 /]# showmount -e 192.168.2.61
Export list for 192.168.2.61:#挂载对应的nfs目录到本地, 挂载前要检查此目录是否存在
子节点配置
[root@k8s2 /]# yum -y install nfs-utils
[root@k8s2 /]# mkdir /nfs/data
#挂载对应的nfs目录到本地, 挂载前要检查此目录是否存在
#为了提高NFS的稳定性,使用TCP协议挂载,NFS默认用UDP协议
[root@k8s1 /]# mount -t nfs 192.168.2.61:/nfs/data /nfs/data -o proto=tcp -o nolock
#取消挂载(对应的本地路径)
[root@k8s1 /]# umount /nfs/data
#设置开机自动挂载
[root@k8s-node1 ~]# vi /etc/fstab
#
# /etc/fstab
# Created by anaconda on Tue Nov 23 09:19:43 2021
#
# Accessible filesystems, by reference, are maintained under '/dev/disk'
# See man pages fstab(5), findfs(8), mount(8) and/or blkid(8) for more info
#
UUID=8af70500-d87e-4b91-b88e-dff450511d3f / ext4 defaults 1 1
192.168.2.61:/nfs/data /nfs/data nfs defaults 0 0
创建pod
apiVersion: apps/v1
kind: Deployment
metadata:
name: nfs-nginx
namespace: default
spec:
selector:
matchLabels:
app: nfs-nginx
replicas: 2
template:
metadata:
labels:
app: nfs-nginx
spec:
containers:
- name: nginx-web
image: nginx:latest
ports:
- containerPort: 80
volumeMounts:
- name: html
mountPath: "/usr/share/nginx/html"
- name: vhost
mountPath: "/etc/nginx/conf.d"
volumes:
- name: html
nfs:
server: 192.168.2.61
path: "/nfs/data/nginx/html"
- name: vhost
nfs:
server: 192.168.2.61
path: "/nfs/data/nginx/conf.d"
---
#service
apiVersion: v1
kind: Service
metadata:
name: nfs-nginx
namespace: default
spec:
type: NodePort
ports:
- port: 80
protocol: TCP
targetPort: 80
nodePort: 31681
selector:
app: nfs-nginx
nginx.conf单文件挂载
#创建configmap from-file=/nfs/data/nginx/nginx.conf为真实可用的nginx.conf文件
[root@k8s1 nginx]# kubectl create configmap nginx-config -- from-file=/nfs/data/nginx/nginx.conf
#导出文件
[root@k8s1 nginx]# kubectl get cm nginx-config -o yaml >> configmap.yaml
#編輯pod.yaml
[root@k8s1 nginx]# vi pod.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nfs-nginx
namespace: default
spec:
selector:
matchLabels:
app: nfs-nginx
replicas: 2
template:
metadata:
labels:
app: nfs-nginx
spec:
containers:
- name: nginx-web
image: nginx:latest
ports:
- containerPort: 80
volumeMounts:
- name: html
mountPath: "/usr/share/nginx/html"
- name: vhost
mountPath: "/etc/nginx/conf.d"
- name: nginx-config
mountPath: "/etc/nginx/conf.d"
subPath: nginx.conf
volumes:
- name: html
nfs:
server: 192.168.2.61
path: "/nfs/data/nginx/html"
- name: vhost
nfs:
server: 192.168.2.61
path: "/nfs/data/nginx/conf.d"
- name: nginx-config
configMap:
name: nginx-config
items:
- key: nginx.conf
path: nginx.conf
在master节点创建1.html文件
自动同步到node节点
进入容器
[root@k8s1 ~]# kubectl exec -it nfs-nginx-6cdf65c4d4-dfm87 -- /bin/bash
root@nfs-nginx-6cdf65c4d4-dfm87:/# cd /usr/share/nginx/html/
root@nfs-nginx-6cdf65c4d4-dfm87:/usr/share/nginx/html# ls
1.html
root@nfs-nginx-6cdf65c4d4-dfm87:/usr/share/nginx/html# cat < 2.html
<html>
<body>Test02</body>
</html>
EOF
[root@k8s1 nfs]# kubectl delete -f nginx.yaml
deployment.apps "nfs-nginx" deleted
service "nfs-nginx" deleted
[root@k8s1 nfs]# kubectl apply -f nginx.yaml
deployment.apps/nfs-nginx created
service/nfs-nginx created
#重启后访问依旧没问题,证明文件已本地持久化
[root@k8s1 nfs]# curl -X GET http://k8s2:31681/2.html
<html>
<body>Test02</body>
</html>
创建pv.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: nfs-pv
namespace: default
labels:
pv: nfs-pv
spec:
capacity:
storage: 10Gi
accessModes:
- ReadWriteMany
persistentVolumeReclaimPolicy: Retain
# storageClassName: nfs 可以不指定,但要指定前必须先创建,否则pvc会报错
nfs:
server: 192.168.2.66
path: "/nfs/data" #NFS目录,需要该目录在NFS上存在
[root@k8s1 /]# kubectl apply -f pv.yaml
创建pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: nfs-pvc
namespace: default
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 1Gi #容量
selector:
matchLabels:
pv: nfs-pv #关联pv 的label,key/value要一致,可以不指定,pvc会自动找到匹配的pv,否则会一直处于pending状态直到有合适的pv
[root@k8s1 /]# kubectl apply -f pvc.yaml
创建pod
[root@k8s1 /]# vim nginx.yaml
#deploy
apiVersion: apps/v1
kind: Deployment
metadata:
name: nfs-nginx
namespace: default
spec:
selector:
matchLabels:
app: nfs-nginx
replicas: 2
template:
metadata:
labels:
app: nfs-nginx
spec:
containers:
- name: nginx-web
image: nginx:latest
ports:
- containerPort: 80
volumeMounts:
- mountPath: /usr/share/nginx/html
name: html
volumes:
- name: html
persistentVolumeClaim:
claimName: nfs-pvc
---
#service
apiVersion: v1
kind: Service
metadata:
name: nfs-nginx
namespace: default
spec:
type: NodePort
ports:
- port: 80
protocol: TCP
targetPort: 80
nodePort: 31681
selector:
app: nfs-nginx
[root@k8s1 nfs]# kubectl apply -f nginx.yaml
[root@k8s1 data]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nfs-nginx-6cdf65c4d4-xpnmm 1/1 Running 0 16h 10.244.0.2 k8s2 <none> <none>
nfs-nginx-6cdf65c4d4-zhv5l 1/1 Running 0 16h 10.244.0.2 k8s3 <none> <none>
pod可能存在的问题
[root@k8s1 ~]# kubectl describe pod nfs-nginx-6cdf65c4d4-xpnmm
............省略
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Warning FailedCreatePodSandBox 5m21s (x4432 over 16h) kubelet (combined from similar events): Failed to create pod sandbox: rpc error: code = Unknown desc = failed to setup network for sandbox "83a7c29e045657bcfa992c7d102c6a81a3a44dfa1a0d4a8b9dbd271623c93150": plugin type="flannel" failed (add): loadFlannelSubnetEnv failed: open /run/flannel/subnet.env: no such file or directory
#出现此问题,创建对应的文件并且分发到所有节点即可
[root@k8s1 ~]# vi /run/flannel/subnet.env
FLANNEL_NETWORK=10.244.0.0/16
FLANNEL_SUBNET=10.244.0.1/24
FLANNEL_MTU=1450
FLANNEL_IPMASQ=true
如果一个集群中部署了多个应用,所有应用都在一起,就不太好管理,也可以导致名字冲突等。
我们可以使用 namespace 把应用划分到不同的命名空间,跟代码里的 namespace 是一个概念,只是为了划分空间。
# 创建命名空间
kubectl create namespace testapp
# 部署应用到指定的命名空间
kubectl apply -f app.yml --namespace testapp
# 查询
kubectl get pod --namespace kube-system
可以用 kubens 快速切换 namespace
# 切换命名空间
kubens kube-system
# 回到上个命名空间
kubens -
# 切换集群
kubectx minikube
# 位于名字空间中的资源
kubectl api-resources --namespaced=true
# 不在名字空间中的资源
kubectl api-resources --namespaced=false