ᕕ( ᐛ )ᕗ xiaoli's blog

云原生之k8s入门

; 9021 words  

目录

在学习docker的时候我们说到,docker的使用标志着进入容器化时代,但随之也产生了许多问题:

  1. 如何协调和调度这些容器?
  2. 如何保证升级应用时不会中断服务的提供?
  3. 如何监控容器的运行状况?
  4. 如何批量操作容器中的应用?
  5. ……

解决这些问题所需要的就是容器编排技术,k8s是当前流行的大规模容器编排系统。

架构

https://kubernetes.io/docs/concepts/overview/components/

集群部署

k8s的集群创建需要最少三台机器,我在阿里云购买了三台centos服务器,对于服务器有以下要求。

对于三台服务器,创建了网段为171.31.0.0/16的专用网络,和网段为171.31.0.0/24的交换机,并让三台服务器位于该专用网络和交换机下。

以下命令默认在所有机器中都要执行

1. 安装kubeadm

  1. 配置基础环境
#各个机器设置自己的域名
hostnamectl set-hostname xxxx
# 刷新,显示主机名
bash

# 将 SELinux 设置为 permissive 模式(相当于将其禁用)
sudo setenforce 0
sudo sed -i 's/^SELINUX=enforcing$/SELINUX=permissive/' /etc/selinux/config

#关闭swap
swapoff -a  
sed -ri 's/.*swap.*/#&/' /etc/fstab

#允许 iptables 检查桥接流量
cat <<EOF | sudo tee /etc/modules-load.d/k8s.conf
br_netfilter
EOF

cat <<EOF | sudo tee /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
EOF
sudo sysctl --system
  1. 安装kubectl、kubelet、kubeadm
# 配置yum源
cat <<EOF | sudo tee /etc/yum.repos.d/kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=http://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-x86_64
enabled=1
gpgcheck=0
repo_gpgcheck=0
gpgkey=http://mirrors.aliyun.com/kubernetes/yum/doc/yum-key.gpg
   http://mirrors.aliyun.com/kubernetes/yum/doc/rpm-package-key.gpg
exclude=kubelet kubeadm kubectl
EOF

# 下载指定版本
sudo yum install -y kubelet-1.20.9 kubeadm-1.20.9 kubectl-1.20.9 --disableexcludes=kubernetes

sudo systemctl enable --now kubelet

kubelet每隔几秒会重启,使用systemctl status kubelet查看时状态可能是inactive也可能是active,因为它陷入了等待kubeadm指令的死循环。

2. 使用kubeadm引导集群

  1. 下载镜像
sudo tee ./images.sh <<-'EOF'
#!/bin/bash
images=(
kube-apiserver:v1.20.9
kube-proxy:v1.20.9
kube-controller-manager:v1.20.9
kube-scheduler:v1.20.9
coredns:1.7.0
etcd:3.4.13-0
pause:3.2
)
for imageName in ${images[@]} ; do
docker pull registry.cn-hangzhou.aliyuncs.com/lfy_k8s_images/$imageName
done
EOF

# 执行该命令行需要下载docker环境,可以参考https://zerlina-ysl.github.io/posts/blog/devops_docker/
chmod +x ./images.sh && ./images.sh
  1. 初始化master节点
# 所有机器添加master域名映射,需要修改为自己的master节点的私有ip,设置该ip机器为集群入口节点
echo "171.31.0.86  cluster-endpoint" >> /etc/hosts
# 执行后各节点执行ping命令,能ping通即配置成功
ping cluster-endpoint```
**以下命令只在主节点中运行**
```shell
# 同理,第一个addr是master的私有ip
kubeadm init \
--apiserver-advertise-address=171.31.0.86 \
--control-plane-endpoint=cluster-endpoint \
--image-repository registry.cn-hangzhou.aliyuncs.com/lfy_k8s_images \
--kubernetes-version v1.20.9 \
--service-cidr=10.96.0.0/16 \ # service的ip范围
--pod-network-cidr=192.168.0.0/16 # pod的ip范围
#所有网络范围不重叠

# 主节点初始化成功后会返回以下指令,需要保留
Your Kubernetes control-plane has initialized successfully!

To start using your cluster, you need to run the following as a regular user:

  mkdir -p $HOME/.kube
  sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
  sudo chown $(id -u):$(id -g) $HOME/.kube/config

Alternatively, if you are the root user, you can run:

  export KUBECONFIG=/etc/kubernetes/admin.conf

You should now deploy a pod network to the cluster.
Run "kubectl apply -f [podnetwork].yaml" with one of the options listed at:
  https://kubernetes.io/docs/concepts/cluster-administration/addons/

You can now join any number of control-plane nodes by copying certificate authorities
and service account keys on each node and then running the following as root:

  kubeadm join cluster-endpoint:6443 --token 7okoz9.ti7losttdfw118hi \
    --discovery-token-ca-cert-hash sha256:92dad63ae98727291c666b048098178e19c9b1584f560283fe2b5d8e9613a941 \
    --control-plane 

Then you can join any number of worker nodes by running the following on each as root:

kubeadm join cluster-endpoint:6443 --token 7okoz9.ti7losttdfw118hi \
    --discovery-token-ca-cert-hash sha256:92dad63ae98727291c666b048098178e19c9b1584f560283fe2b5d8e9613a941

分析以上返回信息,需要执行以下操作(⚠️,仍是只在主节点运行): 2.1 开启容器的运行

mkdir -p $HOME/.kube
  sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
  sudo chown $(id -u):$(id -g) $HOME/.kube/config

2.2 部署网络插件

运行后主节点初始化成功,但是kubectl get nodes后状态仍是not ready,此时还需要安装一个网络插件,根据提示的网址https://kubernetes.io/docs/concepts/cluster-administration/addons/,选择安装calico插件,注意安装的calico的版本

curl https://docs.projectcalico.org/v3.20/manifests/calico.yaml -O

# 运行后生成.yaml文件,根据配置文件为集群创建资源
kubectl apply -f calico.yaml

# 查询此时集群中的所有应用(Pod)
kubectl get pod -A

此时重新运行kubectl get nodes,主节点的状态已变为ready。

  1. 集群中加入node节点(⚠️:在除了主节点的其他节点中运行)
    以下命令只在24h内有效,但是过期可以在主节点使用kubeadm token create --print-join-command创建新的令牌,将返回结果重新在从节点运行即可。
kubeadm join cluster-endpoint:6443 --token 7okoz9.ti7losttdfw118hi \
    --discovery-token-ca-cert-hash sha256:92dad63ae98727291c666b048098178e19c9b1584f560283fe2b5d8e9613a941

返回"This node has joined the cluster:",表明节点加入集群成功。
此时在主节点执行kubectl get nodes,可以看到集群中节点情况。此时集群部署成功。

  1. 验证kubernetes的集群能力 reboot重启三台机器,查看集群状态,会发现k8s集群状态不受影响,这是因为集群的自我修复能力。

  2. 安装dashboard可视化界面

[https://github.com/kubernetes/dashboard

# 运行远程配置文件,安装可视化工具
kubectl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/v2.3.1/aio/deploy/recommended.yaml
# 设置访问端口,将配置文件中spec.type的value设置为 NodePort
kubectl edit svc kubernetes-dashboard -n kubernetes-dashboard
# 找到端口
kubectl get svc -A |grep kubernetes-dashboard

会生成一个超过3万的端口号。对于32427,就是访问可视化界面的端口,需要在安全组放行。之后可以使用集群中任一机器的公网ip地址+32427,就可以访问可视化界面。
创建dash-usr.yaml,粘贴内容。

#创建访问账号,准备一个yaml文件; vi dash-usr.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
  name: admin-user
  namespace: kubernetes-dashboard
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: admin-user
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: cluster-admin
subjects:
- kind: ServiceAccount
  name: admin-user
  namespace: kubernetes-dashboard

kubectl apply -f dash-usr.yaml,应用配置文件,创建访问账号。

#获取访问令牌
kubectl -n kubernetes-dashboard get secret $(kubectl -n kubernetes-dashboard get sa/admin-user -o jsonpath="{.secrets[0].name}") -o go-template="{{.data.token | base64decode}}"
# 以下是返回的内容,就是访问可视化dashboard的token
eyJhbGciOiJSUzI1NiIsImtpZCI6IkstSF9iTUlCaTVNNFVTVW5CZVVJYzh0WjA1QlRuMERELXpRRW5xUFZrNEkifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJrdWJlcm5ldGVzLWRhc2hib2FyZCIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VjcmV0Lm5hbWUiOiJhZG1pbi11c2VyLXRva2VuLXdjNHZ4Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZXJ2aWNlLWFjY291bnQubmFtZSI6ImFkbWluLXVzZXIiLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC51aWQiOiJmZGNhZDgzMy01ZmUzLTQzYjAtYjU5NC1lNTlkNGYwZjhhNzAiLCJzdWIiOiJzeXN0ZW06c2VydmljZWFjY291bnQ6a3ViZXJuZXRlcy1kYXNoYm9hcmQ6YWRtaW4tdXNlciJ9.dTI-mL9SuaTU-NCSjNsXCJ5BKdaye5NLocY47FMPzEJY6OPuhIdAuk7WXEA5o6AVSncWP55fG2s55WEn1dlU-nj90a2A6RkRHGwubiH761GSkDqg3GV7tKVCyzSA_-UDzu0VbdZVbPITYFNDUSIayKyOjRPUHW0IxvQzY2CRkWF4Dt0OvmmWANe-yqlAecSegEfizx08_KVxkYmtRUpGrD4KFqAzg9RiP6qtymOUEDkoxIP6qaZtD6bJ6No14ZaEM142cGvUSQoZfetMq0OjLqEHXLkVQem8d84cTBYlRu606SIdFowD4NafdNXFdH6N3en0PEK_LofWABxca7ogkA

k8s实战

NameSpace

In Kubernetes, namespaces provides a mechanism for isolating groups of resources within a single cluster. Names of resources need to be unique within a namespace, but not across namespaces. Namespace-based scoping is applicable only for namespaced objects (e.g. Deployments, Services, etc) and not for cluster-wide objects (e.g. StorageClass, Nodes, PersistentVolumes, etc).

namespace提供了一种在集群中隔离资源组的机制,每一个资源都有所属的namespace。可以使用kubectl get ns查询namespace的种类。也可以使用"-n"参数查询指定namespace的资源。


可以使用kubectl create/delete ns name对namespace进行创建/删除,注意删除时会将namespace中的资源共同删除。
也可以使用yaml配置文件创建namespace,同理,删除.yaml文件(kubectl delete -f .yaml)就对namespace的删除。

apiVersion: v1 # 版本号
kind: Namespace # 资源类型
metadata:
  name: hello # namespace的名称
# 使用kubectl apply -f对该配置文件进行应用,就成功创建了名称为hello的namespace

Pod

Pods are the smallest deployable units of computing that you can create and manage in Kubernetes. A Pod (as in a pod of whales or pea pod) is a group of one or more containers, with shared storage and network resources, and a specification for how to run the containers. A Pod’s contents are always co-located and co-scheduled, and run in a shared context. A Pod models an application-specific “logical host”: it contains one or more application containers which are relatively tightly coupled. In non-cloud contexts, applications executed on the same physical or virtual machine are analogous to cloud applications executed on the same logical host.

Pod是运行的一组容器,k8s创建和管理的最小的可部署计算单元。
可以使用kubectl run mynginx --image=nginx创建pod,此时创建的pod在default的namesapce下。也可以使用.yaml配置文件来创建。注意:因为端口号冲突,一个pod中不能启动两个相同的镜像。

apiVersion: v1
kind: Pod
metadata:
	labels:
		run: mynginx
	name: mynginx # pod名称
	namespace: default
spec:
	containers: # 容器
	- image: nginx # 镜像名称 
      name: mynginx		 
	- image: tomcat
	  name: mytomcat

kubectl describe pod xx 查看pod的状况。
kubectl delete pod -n namespace xx 删除pod。
kubectl logs xx 查看pod日志,加入-f可以追踪pod运行日志。
kubectl get pod -owide 查看pod详细信息。k8s会为每一个pod分配ip,使用pod ip+容器port就可以访问对应的应用。集群中任一机器及任意应用都可以访问该应用,但是不能在集群外部访问。同理,-w可以追踪pod日志。
kubectl exec -it mynginx -- /bin/bash 进入k8s中pod节点的某个容器的控制台。

Deployment

Deployment provides declarative updates for Pods and ReplicaSets. You describe a desired state in a Deployment, and the Deployment Controller changes the actual state to the desired state at a controlled rate. You can define Deployments to create new ReplicaSets, or to remove existing Deployments and adopt all their resources with new Deployments.

Deployment用来控制Pod,使Pod拥有多副本、自愈、扩缩容等能力。
kubectl create deployment [deploymentName] nginx --image=nginx --replicas=3 创建由deployment控制的、名称为mytomcat的Pod。也可以使用.yaml配置文件创建。

apiVersion: apps/v1
kind: Deployment
metadata:
	labesl: 
		app: my-dep
	name: my-dep # deployment名称
spec:
	replicas: 3 # 副本数量
	selector:
		matchLabels: 
			app: my-dep 
	template:
		metadata:
			labels:
				app: my-dep 
			spec:
				containers:
				- image: nginx # 镜像
				  name: nginx # 镜像名称

即使该Pod宕机被删除,deployment会在其他机器重新下载该Pod。只有kubectl delete deplyment ,才会真正删除该deployment控制的pod。
kubectl get deploy 查看所有的deployment状态。

  1. 扩缩容。deployment会根据当前网络和流量情况对pod进行动态扩缩容,也可以命令进行扩缩容。kubectl scale deploy/my-dep --replicas=5 。也可以通过kubectl edit deploy/my-dep修改.yaml配置文件的方式进行扩缩容。
  2. 自愈和故障转移。当pod出现问题,status不再是running时,deployment会对故障pod进行重启操作。当pod所处机器宕机,deployment在一个时间阈值内感知不到该节点,就会在其他机器上对该节点的pod进行创建。
  3. 滚动更新,即不停机更新。kubectl set image deploy/my-dep nginx=nginx:1.16.1 --record 此时会新启动一个pod,当状态为running时,会将旧镜像的pod状态设置为Terminating。
  4. 版本回退kubectl rollout history deploy/my-dep可以查询my-dep下所有pod的版本回退记录。kubectl rollout undo deploy/my-dep --to-revision=1可以将my-dep下的pod回退到第一次的历史版本。如果不携带–to-revison参数,默认回退到上一个版本。
其他工作负载

Service

An abstract way to expose an application running on a set of Pods as a network service. A key aim of Services in Kubernetes is that you don’t need to modify your existing application to use an unfamiliar service discovery mechanism. You can run code in Pods, whether this is a code designed for a cloud-native world, or an older app you’ve containerized. You use a Service to make that set of Pods available on the network so that clients can interact with it. If you use a Deployment to run your app, that Deployment can create and destroy Pods dynamically. From one moment to the next, you don’t know how many of those Pods are working and healthy; you might not even know what those healthy Pods are named. Kubernetes Pods are created and destroyed to match the desired state of your cluster. Pods are emphemeral resources (you should not expect that an individual Pod is reliable and durable). Each Pod gets its own IP address (Kubernetes expects network plugins to ensure this). For a given Deployment in your cluster, the set of Pods running in one moment in time could be different from the set of Pods running that application a moment later. This leads to a problem: if some set of Pods (call them “backends”) provides functionality to other Pods (call them “frontends”) inside your cluster, how do the frontends find out and keep track of which IP address to connect to, so that the frontend can use the backend part of the workload?

Service其实就是将运行在一套pod上的应用公开作为网络服务。对于Deployment部署的应用无法保障节点的状态,即节点是不可靠和不耐用的。此时如果后端服务提供函数,前端如何追踪到该接口并连接到对应的ip?这就是service的作用—服务发现。
kubectl expose deploy my-dep --port=8000 --target-port=80 --type=ClusterIP,–port参数是service对外的目标端口,–target-port是要访问的集群内的容器端口。此时使用kubectl get service,就能获取到名为my-dep的service以及对应的ip,加上设置的对外port就可以集群内负载均衡访问对应的容器。也可以使用.yaml配置文件实现。

apiVersion: v1
kind: Service
metadata:
	labels:
		app: my-dep
	name: my-dep
spec:
	selector:
		app: my-dep
	ports:
	- port: 8000
	  protocal: TCP
	  targetPort: 80

也可以直接通过访问服务名在集群内直接访问容器:curl my-dep.default.svc:8000
service可以感知到pod的状态变化,当pod下线,就无法访问该pod对应的服务,而pod重新上线,又会加入到pod的服务中。
以上演示的暴露只能在集群内暴露,如果想对外暴露,在公网中访问,需要采用NodePort模式kubectl expose deploy my-dep --port=8000 --target-port=80 --type=NodePort。此时运行kubectl get svc,拥有ClusterIP信息,和type=ClusterIP相比,还多了Port数据,此时访问集群内任一机器ip+该port都可以访问服务,实现服务对外暴露。

Ingress

An API object that manages external access to the services in a cluster, typically HTTP. Ingress may provide load balancing, SSL termination and name-based virtual hosting.

Ingress是Service的统一网关入口,对于Service是访问Pod的入口。即流量->Ingress->Service->Pod。

https://kubernetes.github.io/ingress-nginx/ 底层还是nginx

安装
wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v0.47.0/deploy/static/provider/baremetal/deploy.yaml

#修改镜像
vi deploy.yaml
#将image的值改为如下值:
registry.cn-hangzhou.aliyuncs.com/lfy_k8s_images/ingress-nginx-controller:v0.46.0

# 检查安装的结果
kubectl get pod,svc -n ingress-nginx
# 此时会因NodePort模式暴露访问端口,需要在安全组把svc暴露的端口放行
环境测试
apiVersion: apps/v1
kind: Deployment
metadata:
  name: hello-server # deployment的名称
spec:
  replicas: 2 # 副本
  selector:
    matchLabels:
      app: hello-server
  template:
    metadata:
      labels:
        app: hello-server
    spec:
      containers:
      - name: hello-server
        image: registry.cn-hangzhou.aliyuncs.com/lfy_k8s_images/hello-server
        ports:
        - containerPort: 9000 # 容器的工作端口
---
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: nginx-demo # deployment的名称
  name: nginx-demo
spec:
  replicas: 2 # 副本
  selector:
    matchLabels:
      app: nginx-demo
  template:
    metadata:
      labels:
        app: nginx-demo
    spec:
      containers:
      - image: nginx
        name: nginx
---
apiVersion: v1
kind: Service 
metadata:
  labels:
    app: nginx-demo
  name: nginx-demo # Service的名称
spec:
  selector:
    app: nginx-demo
  ports:
  - port: 8000 # 访问端口
    protocol: TCP
    targetPort: 80
---
apiVersion: v1
kind: Service
metadata:
  labels:
    app: hello-server
  name: hello-server # Service的名称
spec:
  selector:
    app: hello-server
  ports:
  - port: 8000 # 访问端口
    protocol: TCP
    targetPort: 9000

应用上述配置文件。配置文件中部署了两个Service,第一个Service是hello-server,负责两个有hello-server镜像的pod;第二个Service是nginx-demo,控制两个有nginx镜像的pod。

域名访问

目前想实现如下功能:访问hello.atguigui.com:31405可以将请求转给hello-server处理,访问demo.atguigu.com:31405将请求转给nginx-demo处理。

  1. 配置域名和service映射访问规则。
apiVersion: networking.k8s.io/v1
kind: Ingress  
metadata:
  name: ingress-host-bar
spec:
  ingressClassName: nginx
  rules:
  - host: "hello.atguigu.com"  
    http:
      paths:
      - pathType: Prefix
        path: "/"
        backend:
          service:
            name: hello-server
            port:
              number: 8000
  - host: "demo.atguigu.com"
    http:
      paths:
      - pathType: Prefix
        path: "/nginx"  # 把请求会转给下面的服务,下面的服务一定要能处理这个路径,不能处理就是404
        backend:
          service:
            name: nginx-demo  ## java,比如使用路径重写,去掉前缀nginx
            port:
              number: 8000
  1. 配置域名和节点ip的映射。(/etc/hosts)。此时就实现了域名访问的功能。
路径重写

以下配置是使用正则表达式实现路径截串。

apiVersion: networking.k8s.io/v1
kind: Ingress  
metadata:
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /$2
  name: ingress-host-bar
spec:
  ingressClassName: nginx
  rules:
  - host: "hello.atguigu.com"
    http:
      paths:
      - pathType: Prefix
        path: "/"
        backend:
          service:
            name: hello-server
            port:
              number: 8000
  - host: "demo.atguigu.com"
    http:
      paths:
      - pathType: Prefix
        path: "/nginx(/|$)(.*)"  # 把请求会转给下面的服务,如/nginx解析为/,/nginx/router解析为/router
        backend:
          service:
            name: nginx-demo  ## java,比如使用路径重写,去掉前缀nginx
            port:
              number: 8000
流量限制
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ingress-limit-rate
  annotations:
    nginx.ingress.kubernetes.io/limit-rps: "1" # 每s限流1个
spec:
  ingressClassName: nginx
  rules:
  - host: "haha.atguigu.com"
    http:
      paths:
      - pathType: Exact # 精确匹配
        path: "/"
        backend:
          service:
            name: nginx-demo
            port:
              number: 8000

如果超过流量限制,会返回503(service temporarily unavailable)。而这些状态码也可以在配置文件自定义配置。

当请求流量来时,先通过LB负载均衡,根据负载均衡策略将流量打到任一机器的Ingress上,根据配置的域名或路由规则达到对应的Service。

存储抽象

在使用docker的时候,会将容器中镜像的某些文件挂载到容器上,方便对文件内容进行修改。k8S中也有类似操作,可以将Pod中的文件挂载到外部的文件。但是当节点宕机时,虽然deployment会自动在其他节点重起一个pod,但对应挂载的文件不会转移,pod中的数据在故障后无法被保存。为解决这个问题,k8s有一个单独的存储层,处理各节点文件的挂载。也有一些专业的存储层框架可以使用,如Glusterfs、NFS、CephFS。
当使用NFS存储系统时,一个Pod的文件会多节点备份,并进行同步,即使出现故障转移,也不会影响数据。
在搭建NFS存储系统时,采用主从架构,主节点将数据同步到从节点。

  1. 安装NFS系统,集群中所有机器都要安装。yum install -y nfs-utils
  2. 配置主从节点。
# 主节点配置
#nfs主节点 向其他节点以非安全、读写、同步的方式暴露/nfs/data/目录
echo "/nfs/data/ *(insecure,rw,sync,no_root_squash)" > /etc/exports
mkdir -p /nfs/data 
systemctl enable rpcbind --now # 启动rpc远程绑定
systemctl enable nfs-server --now 
# 检查配置是否生效
exportfs -r
exportfs # 输出共享目录

# 从节点配置 
showmount -e 172.31.0.4 # 注意:这里需要主节点机器的私有ip地址
 #执行以下命令挂载 nfs 服务器上的共享目录到本机路径 /nfs/data
 mkdir -p /nfs/data
# 挂载命令 远程服务器的ip地址 远程服务器的文件夹 本地文件夹
mount -t nfs 172.31.0.4:/nfs/data /nfs/data
# 测试 nfs系统部署成功后,主节点进行如下操作 从节点也会同步该数据
echo "hello nfs server" > /nfs/data/test.txt
  1. 原生方式数据挂载,使用配置文件的方式应用。
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: nginx-pv-demo
  name: nginx-pv-demo
spec:
  replicas: 2 # 部署两个pod,两个 pod中文件的修改都会影响到外层文件的内容
  selector:
    matchLabels:
      app: nginx-pv-demo
  template:
    metadata:
      labels:
        app: nginx-pv-demo
    spec:
      containers:
      - image: nginx
        name: nginx
        volumeMounts: # 卷挂载
        - name: html # 配置命名
          mountPath: /usr/share/nginx/html # 将nginx内部的该路径文件挂载到外部
      volumes:
        - name: html  # 配置命名
          nfs: # 挂载方式,使用nfs网络文件系统
            server: 172.31.0.4 # nfs服务器主节点ip
            path: /nfs/data/nginx-pv # 即nginx镜像到mountPath路径挂在到容器的path路径 挂在前外部目录需要先创建

使用原生挂载的方式,如果将pod节点全部删除,仍会保留挂载的文件,在pod节点过多的情况下,容易造成数据冗余。并且在挂载时,对于pod中原文件的大小没有限制,因此我们可以使用PV&PVC来解决以上问题。

PV&PVC

PV:持久卷,persistent volumn,将应用需要持久化的数据保存在指定位置。如上述的/nfs/data/nginx-pv就是持久卷。
PVC:持久卷声明,persistent volumn claim ,申明需要使用的持久卷规格。
PV池:将固定大小的PV提前创建好,静态供应。
PV和PVC主要用来高效挂载镜像文件。当pod需要挂载某个目录时,需要先进行PVC,声明需要挂载的持久卷以及对应的大小,PV池会根据声明从PV池中分配对应的PV。此时,原始目录和pvc和pv进行了绑定。即使pod宕机,重起pod也会根据pvc找到对应挂载的数据,而pod如果被删除,对应的pv也会被回收。

  1. 创建PV。PV1名称是pv01-10m,可读可写多节点,容量为10M,路径为/nfs/data/01,PV2名称是pv02-1gi,容量为1Gi,路径为/nfs/data/02,PV3名称是pv03-3gi,容量是3Gi,路径为/nfs/data/03,配置文件应用后PV池就创建成功,可以使用kubectl get persistentvolumn/pv获取PV资源。
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv01-10m # 名称需要全小写,命名规则和域名规则类似
spec:
  capacity:
    storage: 10M # 限制PV容量
  accessModes:
    - ReadWriteMany # 可读可写多节点
  storageClassName: nfs # 需要和PVC的name对应
  nfs:
    path: /nfs/data/01 # 需要提前创建该文件
    server: 172.31.0.4 # nfs服务器主节点的私有ip地址
--- # 文档分割,表示该yaml文件由三个独立的文档组成
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv02-1gi
spec:
  capacity:
    storage: 1Gi
  accessModes:
    - ReadWriteMany
  storageClassName: nfs 
  nfs:
    path: /nfs/data/02
    server: 172.31.0.4
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv03-3gi
spec:
  capacity:
    storage: 3Gi
  accessModes:
    - ReadWriteMany
  storageClassName: nfs
  nfs:
    path: /nfs/data/03
    server: 172.31.0.4
  1. PVC和PV的绑定和创建。配置文件应用后,PV池根据storage分配对应的PV,被分配的PV状态由available变为bound,claim属性会显示被绑定的PVC(default/nginx-pvc)。如果删除该文件,被绑定的PV状态会变为Released。
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: nginx-pvc
spec:
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: 200Mi
  storageClassName: nfs # 和PV的spec.storageClassName对应
  1. 创建Pod和PVC绑定。
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: nginx-deploy-pvc
  name: nginx-deploy-pvc
spec:
  replicas: 2
  selector:
    matchLabels:
      app: nginx-deploy-pvc
  template:
    metadata:
      labels:
        app: nginx-deploy-pvc
    spec:
      containers:
      - image: nginx
        name: nginx
        volumeMounts:
        - name: html
          mountPath: /usr/share/nginx/html
      volumes:
        - name: html
	#	  nfs:  没有PV和PVC之前的配置
    #        server: 172.31.0.4
    #        path: /nfs/data/nginx-pv 
          persistentVolumeClaim: # 从PV池申请PV
            claimName: nginx-pvc

以上使用的是静态供应的PV池,同时也有动态供应的PV池,会根据PVC声明所需要的大小在池中动态创建PV并分配绑定,无需人工手动首先创建。

ConfigMap

目录可以使用PV和PVC来实现挂载,而对于配置文件的挂载和同步更新,可以使用ConfigMap。以下的演示以redis为例。

  1. 把配置文件做成配置集,之后可以把原始配置文件删除。
# 创建配置,redis保存到k8s的etcd;
kubectl create cm redis-conf --from-file=redis.conf

此时使用kubectl get cm -oyaml可以得到ConfigMap的yaml格式。

apiVersion: v1
data:    #data是所有真正的数据 
  redis.conf: | # key:默认是文件名  
    appendonly yes # value:配置文件的内容
kind: ConfigMap # 创建ConfigMap
metadata:
  name: redis-conf
  namespace: default
  1. 创建Pod
apiVersion: v1
kind: Pod
metadata:
  name: redis
spec:
  containers:
  - name: redis
    image: redis
    command: # 启动镜像
      - redis-server
      - "/redis-master/redis.conf"  # redis容器内部的位置
    ports:
    - containerPort: 6379 
    volumeMounts: # 卷挂载
    - mountPath: /data # redis中的/data目录
      name: data # 挂载名称为name
    - mountPath: /redis-master # redis中的/redis-master文件,redis.conf在该文件下
      name: config # 挂载名称为config
  volumes:
    - name: data
      emptyDir: {}
    - name: config # name=config的挂载
      configMap:
        name: redis-conf # name=redis-conf的配置集,即第一步创建的
        items:
        - key: redis.conf # 引入ConfigMap中key=redis.conf的配置
          path: redis.conf # 指redis镜像中的redis.conf,/redis-master/redis.conf
  1. 检查默认配置。kubectl exec -it redis -- redis-cli。此时修改配置集中的value会对应修改redis中的配置文件,可以在redis中使用CONFIG GET xx获取配置项的值,但是修改的同步有一定延迟。注意:Pod部署的中间件无法热更新,虽然配置文件中进行修改,但是需要重新启动Pod才能应用修改后的配置。
apiVersion: v1
kind: ConfigMap
metadata:
  name: example-redis-config
data:
  redis-config: | # 修改配置集中redis的配置
    maxmemory 2mb
    maxmemory-policy allkeys-lru 
Secret

Secret对象类型专门保存密码、令牌、密钥等敏感信息,与ConfigMap原理类似。

# 创建Secret
kubectl create secret docker-registry [secret-name] \
  --docker-server=<你的镜像仓库服务器> \
  --docker-username=<你的用户名> \
  --docker-password=<你的密码> \
  --docker-email=<你的邮箱地址>
# 以yaml格式查看创建的Secret
kubectl get secret secret-name -oyaml
# 此时再创建镜像,就避免账号密码的泄漏
apiVersion: v1
kind: Pod
metadata:
  name: private-nginx
spec:
  containers:
  - name: private-nginx
    image: leifengyang/guignginx:v1.0 # 拉取私有镜像时,避免账号密码的暴露
  imagePullSecrets:
  - name: secret-name # secret名称

实战小结

#kubernetes