Kubernetes基础

一.k3s环境搭建

  • 这里安装的是k3s,目的是简化繁琐的环境搭建快速上手使用
1. 每个机器都要运行(root)
cat > /etc/sysctl.d/k8s.conf << EOF
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
EOF

2. 安装docker
sudo apt-get update

# 安装一些必须的包,这些包让 apt 可以通过 HTTPS 来使用一个仓库
sudo apt-get install apt-transport-https ca-certificates curl software-properties-common

# 添加 Docker 的官方 GPG 密钥
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -

# 设置 Docker 稳定版仓库:你可以使用 Docker 提供的脚本来设置仓库,或者手动添加
sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"

sudo apt-get update
sudo apt-get install docker-ce
sudo systemctl start docker
sudo systemctl enable docker  # 开机自启

二.使用

1.Pod

  • pod包含一个或者多个容器的容器组,是kubernetes中创建和管理的最小对象
1. pod是k8s中最小的调度单元,k8s直接管理pod而不是容器
2. 同一个pod容器总是被自动安排到集群的同一节点(物理机或虚拟机)上,并且一起调度
3. pod可以理解为运行特定应用的"逻辑主机",这些容器共享存储,网络和配置声明(如资源限制)
4. 每个pod有唯一的IP地址,IP地址分配给pod,在同一个pod内,所有容器共享一个IP地址和端口空间,pod内的容器可以使用localhost互相通信
kubectl run my_nginx --image=nginx:1.22
#           -------- ------------------
#             名字            容器镜像
kubectl get pod  #查看容器
kubectl logs -f my_nginx  #查看指定容器日志
kubectl describe(单词意思:简述) pod my_nginx  #查看容器的创建信息
kubectl get pod -owide  #现实详细的pod信息
curl ip  #可以访问ip
kubectl exec -it my_nginx -- /bin/bash #和容器进行交互
kubectl rum my-busybox --image=busybox -it --rm  #一次性任务,退出pod时自动退出
kubectl delete pod my_nginx  #删除pod

2.Deployment(部署) 和ReplilcaSet(副本集)

  • ReplicaSet(副本集,是Pod的集合):它可以设置运行pod的数量,确保任何时间都有指定数量的Pod副本在运行,通常我们不直接使用ReplicaSet而是在Deployment中声明。
  • Deployment(部署):Deployment是对ReplicaSet和pod更高级的抽象,它使pod拥有多副本,自愈,扩缩容,滚动升级能力
# 创建一个deployment, 指定副本的数量为3, 镜像为nginx:1.22
kubectl create deployment nginx-deploy --image=nginx:1.22 --replicas=3
	
kubectl get deploy
kubectl get pod
kubectl get replicaSet
	
# 你可以删除几个pod来测试一下,当你有pod下线,马上就有新的pod上线,使用保持pod数量和你设置的--replicas一致
# deployment缩放功能(手动缩放)
kubectl get replicaSet --watch  #这个命令可以观察容器的缩放功能
kubectl scale deploy nginx-deploy --replicas=5  # 修改副本容器数量为5(此时容器会产生缩放)
	
# 自动缩放
# 创建容器pod最少数量为3 最大数量为10 cpu维持在75以下
kubectl autoscale deployment/nginx-auto --min=3 --max=10 --cpu-percent=75
# 自动缩放通过泽佳和减少副本的数量,保持所有的pod的平均cpu利用率不超过75%
# 自动缩放需要声明pod的资源限制,同时使用Metrice Server服务(k3s默认已经安装)	
		
# 滚动更新
# 更新镜像,指定副本集,更新镜像版本
kubectl set image deploy/nginx-deploy nginx=nginx1.23
		
# 观察副本集
kubectl get rs[reploy缩写] --watch
# 会创建新的副本集,新的更新一个,旧的下线一个
		
# 版本回滚
# 可以查看副本集的版本
kubectl rollout history deploy/nginx-deploy  
# 查看详情可以加上版本号
kubectl rollout history deploy/nginx-deploy --revision=1	
# 进行回滚,加上版本号
kubectl rollout undo deploy/nginx-deploy --to-revision=1

3. Service服务

  • Service将运行在一组Pods上的应用程序公开为网络服务的抽象方法, Service为一组Pod提供相同的DNS名,并且在它们之间负载均衡,k8s为Pod分配了IP地址,但IP地址可能会发生变化集群内可以使用Service的名称访问服务,而不需要担心Pod的IP发生变化。
  • k8s service 定义了一种抽象,逻辑上的一组可以互相替换掉pod,通常称为微服务Service对应的Pod集合通常是通过选择运算符来确定的,比如一个Service里面有3个nginx副本,这些副本可以互换,我们不需要关心调用了哪个nginx,也不用去关注Pod的运行状态,只需要调用这个服务即可。
# --name=服务名称, --port=服务端口 --target-port=容器端口
kubectl expose deploy/nginx-deploy --name=nginx-service --port=8080 --target-port=80 

# 查看服务
kubectl get service
# 通过查看到的ip + port 访问服务
curl ip:port

# 创建一次性pod,--rm表示退出时自动删除该容器
kubectl run test -it --image=nginx:1.22 --rm -- bash
# 通过service+port访问
curl nginx-service:8080

# 查看service信息,service 在Endpoints上的机器进行负载均衡
kubectl describe service nginx-service 
# 可以进去修改其中一个pod,然后再多次访问service来测试负载均衡 
kubectl exec -it [pod-name] -- bash 
  • 想要外部的机器能访问到service,需要给service定义类型。
    ServiceType取值:
  1. ClusterIP:将服务公开在集群内部,k8s会给服务分配一个集群内部的IP,集群内的所有主机都可以通过这个Cluster-IP访问服务,集群内部的Pod可以通过service名称访问服务。
  2. NodePort: 通过每个节点的主机IP和静态端口(NodePort)暴露服务,集群的外部主机可以使用节点IP和NodePort访问服务。
  3. ExternalName:将集群外部的网络引入集群内部。
  4. LoadBalancer:使用云提供商的负载均衡器向外部暴露服务。
# 通过以下命令,可以让外部端口,访问到内部的nginx-deploy上,名字是nginx-outside,对外端口是新生成的,需要查看节点信息来对对照
kubectl expose deploy/nginx-deploy --name=nginx-outside --type=NodePort --port=8081 --target-port=80
[root@k8s-master ~]# kubectl get service
NAME            TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)          AGE
kubernetes      ClusterIP   10.43.0.1       <none>        443/TCP          20h
nginx-service   ClusterIP   10.43.7.249     <none>        8080/TCP         18h
nginx-outside   NodePort    10.43.110.179   <none>        8081:31997/TCP   11s
		
# 这时候其他节点ip也能访问到该服务,是公布到所有节点上的,node-ip+31997就可以访问了

4.Namespace(命名空间)

  • 命名空间是一种资源隔离机制,将同一资源划分为相互隔离的组,命名空间可以在多个用户之间划分集群资源。
# k8s默认会创建4个命名空间
[root@k8s-master ~]# kubectl get namespace
NAME              STATUS   AGE
default           Active   21h
kube-system       Active   21h
kube-public       Active   21h
kube-node-lease   Active   21h
	
# default: 默认,不可删除,未指定命名空间的对象都会分配到这里
# kube-system: 系统对象(控制平面和Node组件)所使用的命名空间
# kube-public:自动创建的公共命名空间,所有用户(未经过身份验证的用户)都可以读取,通常我们约定,将整个集群中公用的可见和可读的资源放在这个空间中
# kube-node-lease: (租约Lease) 对象使用的命名空间,每个节点都有一个关联的lease对象,lease是一个轻量级的资源,lease通过发送心跳,检测集群中的每个节点是否发生故障
	
	
kubectl get lease -A  #查看lease对象
	
	
kubectl create ns develop  # 创建新的命名空间
kubectl run nginx --image=nginx:1.22 -n=develop  # 指定命名空间创建pod
kubectl get pod -n=develop  # 查看指定命名空间下的pod
	
# 如果不指定命名空间,查询到的pod都是default
kubectl get pod  # 默认default
kubectl config set-context ${kubectl config current-context} --namespace=devlop  # 修改默认查询的命名空间
kubectl get pod  # 这个时候就是查询的devlop命名空间下的内容了
	
# 删除命名空间(会清空所有命名空间下的内容,除非无法自动删除某些对象,那么这个命名空间就无法自动删除,通常是因为对象处于错误状态或者其他,删除掉这些对象就可以继续删除命名空间了)
kubectl delete ns develop

5.声明式对象配置

k8s中管理对象,有两种方式:

  1. 命令行指令:就是上面内容我们一直在玩的东西。
  2. 声明式配置:k8s使用yaml文件来描述k8s对象,声明式配置好比申请列表,学习难度大且配置困难,麻烦好处是操作留痕,适合操作复杂的对象,多用于生产。
常用命令缩写
		namespace	ns
		nodes		no
		pods		po
		services	svc
		deployment	deploy
		replicasets	rs
		statefulsets	sts
# yaml规范
		* 缩进代表上下级关系
		* 缩进只允许使用空格,通常两个字符
		* : 键值对,后面必须有空格
		* - 列表,后面必须有空格
		* [] 数组
		* # 注释
		* | 多行文本块
		* --- 表示文档的开始,多用于分割多个资源对象
group:
  name: group-1
  members:
    - name: "Luo"
      UID: 1001
    - name: "JIU"
      UID: 1002
  # comments
  word:
    ["I don't care money", "R U OK"]
  test: |
    line
    new line
    3rd line
---
group:
  name: group-2
  members:
    - name: "Luo"
      UID: 1001
    - name: "JIU"
      UID: 1002
  # comments
  word:
    ["I don't care money", "R U OK"]
  test: |
    line
    new line
    3rd line

在创建k8s对象所对应的yaml文件中,需要配置的字段如下:
* apiVersion: k8s API的版本
* kind: 对象类别,比如Pod,Deployment,Service,ReplicaSet
* metadata: 描述对象的元数据,包括一个name字符串,UID和可选的namespace
* spec: 对象的配置

使用yaml定义一个Pod
# 官方文档地址
https://kubernetes.io/zh-cn/docs/concepts/workloads/pods/
simple-pod.yaml

apiVersion: v1
kind: Pod
metadata:
  name: nginx
spec:
  containers:
  - name: nginx
    image: nginx:1.22
    ports:
    - containerPort: 80
kubectl apply -f simple-pod.yaml  #是配置文件生效
kubectl get pod
kubectl delete -f simple-pod.yaml  #删除pod

6. 标签

  • 标签(Labels)是附加到对象(比如Pod)上的键值对,用于补充对象的描述信息,标签能是用户能够以松散的方式管理对象的映射,而无需客户端存储这些映射,由于一个集群中可能管理程前上万个容器,我们可以使用标签高效的进行选择和操作容器集合。
# 键的格式:
	* 前缀(可选)/名称(必须)
# 有效名称和值
	* 必须为63个字符或者更少(可以为空)
	* 如果不为空,必须以字母数字字符([a-z0-9A-Z])开头和结尾
	* 包含破折号'-',下划线'_',点'.'和字母或数字

abel-pod.yaml

apiVersion: v1
kind: Pod
metadata:
  name: label-demo
  # 标签
  labels:
    environment: production
    app: nginx
spec:
  containers:
  - name: nginx
    image: nginx:1.22
    ports:
    - containerPort: 80
kubectl apply -f label-pod.yaml  
kubectl get pod --show-labels  #查看容器的标签
	
# 可以使用-l来过滤标签,多个标签用逗号分割
kubectl get pod -l "app=nginx,environment=production"

选择器:标签选择器可以识别一组对象,标签不支持唯一性,标签选择器最常见的用法就是为Service选择一组Pod作为后端。
my-service.yaml

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  type: NodePort
  selector:
    # 选择自己的选择器
    app: nginx  
  ports:
      # 默认情况下,为了方便起见,`targetPort` 被设置为与 `port` 字段相同的值。
    - port: 80
      targetPort: 80
      # 可选字段
      # 默认情况下,为了方便起见,Kubernetes 控制平面会从某个范围内分配一个端口号(默认:30000-32767)
kubectl apply -f my-service.yaml
kubectl describe svc/my-service  #查看service详情
Name:                     my-service
Namespace:                default
Labels:                   <none>
Annotations:              <none>
Selector:                 app=nginx
Type:                     NodePort
IP Family Policy:         SingleStack
IP Families:              IPv4
IP:                       10.43.58.138
IPs:                      10.43.58.138
Port:                     <unset>  80/TCP
TargetPort:               80/TCP
NodePort:                 <unset>  30007/TCP
Endpoints:                10.42.1.11:80
Session Affinity:         None
External Traffic Policy:  Cluster
Events:                   <none>

# 你能看到Endpoints: 10.42.1.11:80,我们用以下命令查看pod机器
kubectl get pod -l "app=nginx" -owide
NAME         READY   STATUS    RESTARTS   AGE   IP           NODE         NOMINATED NODE   READINESS GATES
lable-demo   1/1     Running   0          18m   10.42.1.11   k8s-woker1   <none>           <none>
# 你会发现机器就是我们刚才启动的机器,选择器给你选择的Pod节点作为你服务的Pod

7.容器与镜像

  • 容器运行时接口(CRI),Kubelet运行在每个节点(Node)上,用于管理和维护Pod和容器的状态,容器运行时接口(CRI)是Kubelet和容器运行时之间通信的主要协议,它将Kubelet与容器运行时解耦,理论上,实现了CRI接口的容器引擎,都可以作为k8s的容器运行时(Docker没有实现(CRI)接口,k8s使用dockershim来兼容docker,自V1.24版本其,Dockershim已从k8s项目中移除),crictl是一个兼容了CRI的容器运行时命令,他的用法和docker命令一样,可以用来检查和调试底层的运行时容器。
crictl ps  
crictl images
# crictl和docker的操作很像,但是没有太多命令
	
# 在一些局域网的环境下,没发从互联网拉取镜像,可以手动的导出,导入镜像
# crictl没有命令导出导入镜像功能
# 需要使用ctr命令导入,导出镜像,他是containerd的命令行接口
	 
# 我们只需要学会导入导出镜像即可,其他功能太难用了

# [导入]
docker images
docker pull alpine:3.15
docker save alpine:3.15 > alpine-3.15.tar
ls
scp alpine-3.15.tar root@ip:~
# 开始导入镜像
# k8s中,所有的镜像都位于(k8s.io)这个命名空间当中的,-n表示导入命名空间,最后还要指定上平台(linux/amd64)
ctr -n k8s.io images import alpine-3.15.tar --platform linux/amd64
	 
# [导出]
ctr -n k8s.io iamges export alpine.tar docker.io/library/alpine:3.15 --platform linux/amd64
#     =------=              =--------= =---------------------------= =--------------------=
#     命名空间                  导出位置              镜像位置                 指定平台(arm64等)
 
# 导入镜像需要在每台机器上都导入,一般是在Google镜像下载不下来的时候这样干

三.实战

1. 金丝雀发布
流程描述:
1. 首先发布v1版本
2. 然后上线一部分v2版本
3. 继续扩大v2版本数量
4.v1版本全部下线
部署第一个版本
发布v1版本的应用,镜像使用nginx:1.22,数量为3
	* 创建Namespace
	* 创建Deployment
	* 创建外部访问的Service
  1. 部署第一个版本
apiVersion: v1
kind: Namespace
metadata:
  name: dev
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment-v1
  namespace: dev
  labels:
    app: nginx-deployment-v1
spec:
  replicas: 3
  selector:
    matchLabels: # 跟template.metadata.labels一致
      app: nginx
  template:
    metadata:
      labels:
  app: nginx # 也就是和这行一致
    spec:
      containers:
      - name: nginx
  image: nginx:1.22
  ports:
  - containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: canary-demo
  namespace: dev
spec:
  type: NodePort
  selector: # 更Deployment中的selector一致
    app: nginx
  ports:
      # By default and for convenience, the `targetPort` is set to the same value as the `port` field.
    - port: 80
      targetPort: 80
      # Optional field
      # By default and for convenience, the Kubernetes control plane will allocate a port from a range (default: 30000-32767)
      nodePort: 30008
部署deploy-v1
[root@k8s-master ~]# kubectl apply -f deploy-v1.yaml 
namespace/dev created
deployment.apps/nginx-deployment-v1 created
service/canary-demo created
# 可以看到创建了三个对象

# 查看指定命名空间下的所有对象
[root@k8s-master ~]# kubectl get all -n=dev
NAME                                       READY   STATUS    RESTARTS   AGE
pod/nginx-deployment-v1-645549fcf7-6vp4g   1/1     Running   0          111s
pod/nginx-deployment-v1-645549fcf7-dn4m8   1/1     Running   0          111s
pod/nginx-deployment-v1-645549fcf7-sg9vh   1/1     Running   0          111s

NAME                  TYPE       CLUSTER-IP     EXTERNAL-IP   PORT(S)        AGE
service/canary-demo   NodePort   10.43.134.77   <none>        80:30008/TCP   111s

NAME                                  READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/nginx-deployment-v1   3/3     3            3           111s

NAME                                             DESIRED   CURRENT   READY   AGE
replicaset.apps/nginx-deployment-v1-645549fcf7   3         3         3       111s
  1. 创建Canary Deployment
    发布新版本的应用,镜像使用docker/getting-staeted,数量为1
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment-canary # 修改名称
  namespace: dev
  labels:
    app: nginx-deployment-canary # 修改标签
spec:
  replicas: 1
  selector:
    matchLabels: # 跟template.metadata.labels一致
      app: nginx
  template:
    metadata:
      labels:
  app: nginx
  track: canary # 用于区别的新标签,便于单独查找
    spec:
      containers:
  - name: new-nginx # 修改容器名称
    image: docker/getting-started # 新镜像
    ports:
      - containerPort: 80
# 因为标签里面都有app: nginx,所以新部署的容器会被Selector自动加入到服务里面
[root@k8s-master ~]# kubectl apply -f deploy-canary.yaml 
deployment.apps/nginx-deployment-canary created

[root@k8s-master ~]# kubectl get all -n=dev
NAME                                           READY   STATUS    RESTARTS   AGE
pod/nginx-deployment-v1-645549fcf7-6vp4g       1/1     Running   0          8m19s
pod/nginx-deployment-v1-645549fcf7-dn4m8       1/1     Running   0          8m19s
pod/nginx-deployment-v1-645549fcf7-sg9vh       1/1     Running   0          8m19s
pod/nginx-deployment-canary-74577469b5-w2vtp   1/1     Running   0          58s

NAME                  TYPE       CLUSTER-IP     EXTERNAL-IP   PORT(S)        AGE
service/canary-demo   NodePort   10.43.134.77   <none>        80:30008/TCP   8m19s

NAME                                      READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/nginx-deployment-v1       3/3     3            3           8m19s
deployment.apps/nginx-deployment-canary   1/1     1            1           58s

NAME                                                 DESIRED   CURRENT   READY   AGE
replicaset.apps/nginx-deployment-v1-645549fcf7       3         3         3       8m19s
replicaset.apps/nginx-deployment-canary-74577469b5   1         1         1       58s

[root@k8s-master ~]# kubectl describe service canary-demo -n=dev
Name:                     canary-demo
Namespace:                dev
Labels:                   <none>
Annotations:              <none>
Selector:                 app=nginx
Type:                     NodePort
IP Family Policy:         SingleStack
IP Families:              IPv4
IP:                       10.43.134.77
IPs:                      10.43.134.77
Port:                     <unset>  80/TCP
TargetPort:               80/TCP
NodePort:                 <unset>  30008/TCP
Endpoints:                10.42.0.43:80,10.42.1.12:80,10.42.2.11:80 + 1 more...
Session Affinity:         None
External Traffic Policy:  Cluster
Events:                   <none>
# 注意Endpoints后面有一个+ 1 more...说明有一个新的pod加入到Service当中了

# 你可以尝试多访问几次该Service,负载均衡会让你访问到新上线的服务
curl 10.43.134.77:80

# 当你使用了一段时间,发现没什么问题,就可以把节点数量调整为3
kubectl scale deploy nginx-deployment-canary --replicas=3 -n=dev

# 然后开始慢慢下线nginx-deployment-v1中的内容(这里调整为了0)
kubectl scale deploy nginx-deployment-v1 --replicas=0 -n=dev
# 局限性
按照 Kubernetes 默认支持的这种方式进行金丝雀发布,有一定的局限性:
	> 不能根据用户注册时间、地区等请求中的内容属性进行流量分配
	> 同一个用户如果多次调用该 Service,有可能第一次请求到了旧版本的 Pod,第二次请求到了新版本的 Pod
# 在 Kubernetes 中不能解决上述局限性的原因是:Kubernetes Service 只在 TCP 层面解决负载均衡的问题,并不对请求响应的消息内容做任何解析和识别,如果想要更完善地实现金丝雀发布,可以考虑Istio灰度发布,Istio是服务网格技术。
2. 运行有状态的应用
以MySQL数据库为例,在kubernetes集群中运行一个有状态的应用,该部分比较难,部署数据库几乎覆盖了kubernetes中常见的对象和概念
	* 配置文件--ConfigMap
	* 保存密码--Secret
	* 数据存储--持久卷(PV)和持久卷声明(PVC)
	* 动态创建卷--存储类(StorageClass)
	* 部署多个实例--StatefulSet
	* 数据库访问--Headless Service
	* 主从复制--初始化容器和sidecar
	* 数据库调试--port-forward
	* 部署Mysql集群--helm
  1. 创建mysql文件夹,用来单独操作
mkdir mysql

# 配置环境变量
	* 使用MySQL镜像创建Pod,需要使用环境变量设置MySQL的初始密码。
	* 环境变量配置示例(可以在docker hub中查看MySQL镜像的用法,配置文件见664行[mysql-pod.yaml])
		  	
# 挂载卷
	* 将数据存储在容器中,一旦容器被删除,数据也会被删除。
	* 将数据存储到卷(Volume)中,删除容器时,卷不会被删除。
		  	
# hostPath卷: hostPath卷将主机节点上的文件或目录挂载到Pod中(配置见681行)

mysql-pod.yaml

apiVersion: v1
kind: Pod
metadata:
  name: mysql-pod
spec:
  containers:
    - name: mysql
      image: mysql:5.7
      env:                                         #环境变量,用于配置容器内应用程序的环境
  - name: MYSQL_ROOT_PASSWORD                #环境变量名称,MySQL初始化时的root用户密码
    value: "123456"                          #环境变量值,设置root用户的密码为"123456"
      ports:                                       #定义容器的端口
  - containerPort: 3306                      #暴露的端口号,此处为MySQL默认的3306端口
      volumeMounts:                                #挂载卷
  - mountPath: /var/lib/mysql                #定义卷的挂载点,即容器内的目录
    name: data-volume
  volumes:                                         #定义Pod内的卷
    - name: data-volume
      hostPath:
  path: /home/mysql/data                     #主机上的目录路径
  type: DirectoryOrCreate                    #如果路径不存在,则创建目录
挂载卷的主要目的是将主机上的目录映射到容器内的目录,以实现数据持久化。具体效果如下:
主机路径:/home/mysql/data
容器路径:/var/lib/mysql
当Pod启动时,主机上的/home/mysql/data目录会被挂载到容器内的/var/lib/mysql目录,
这意味着在容器内对/var/lib/mysql目录的任何读写操作都会直接作用于主机上的/home/mysql/data目录
kubectl apply -f mysql-pod.yaml
kubectl get pod -owide
  1. ConfigMap与Secret
  • 在Docker中,我们一般通过绑定挂载的方式将配置文件挂载到容器里,在Kubernetes集群中,容器可能被调度到任意节点,配置文件需要能在集群任意节点上访问、分发和更新。
ConfigMap
	* ConfigMap 用来在键值对数据库(etcd)中保存非加密数据。一般用来保存配置文件。
	* ConfigMap 可以用作环境变量、命令行参数或者存储卷。
	* ConfigMap 将环境配置信息与 容器镜像 解耦,便于配置的修改。
	* ConfigMap 在设计上不是用来保存大量数据的。
	* 在 ConfigMap 中保存的数据不可超过 1 MiB。

mysql-pod.yaml

apiVersion: v1
kind: Pod
metadata:
  name: mysql-pod
  labels:
    app: mysql
spec:
  containers:
    - name: mysql
      image: mysql:5.7
      env:
  - name: MYSQL_ROOT_PASSWORD
    value: "123456"
      volumeMounts:
  - mountPath: /var/lib/mysql
    name: data-volume
  - mountPath: /etc/mysql/conf.d              #最后将conf-volume挂在到/etc/mysql/conf.d(参考mysql官方文档)
    name: conf-volume			    #指定要挂载的名字
    readOnly: true			    #将配置文件设置为只读
  volumes:
    - name: conf-volume				    #conf-volume
      configMap:                                    #需要在这里引入configMap
  name: mysql-config
    - name: data-volume
      hostPath:
  # directory location on host
  path: /home/mysql/data
  # this field is optional
  type: DirectoryOrCreate
---
apiVersion: v1                                      #创建configMap
kind: ConfigMap
metadata:
  name: mysql-config
data:
  mysql.cnf: |
    [mysqld]
    character-set-server=utf8mb4
    collation-server=utf8mb4_general_ci
    init-connect='SET NAMES utf8mb4'

    [client]
    default-character-set=utf8mb4

    [mysql]
    default-character-set=utf8mb4
[root@k8s-master mysql]# kubectl apply -f mysql-pod.yaml 
pod/mysql-pod created
configmap/mysql-config created
#可以看见创建了两个,一个是ConfigMap,一个是pod

[root@k8s-master mysql]# kubectl describe cm mysql-config  #查看cm(缩写,configMap)信息
Name:         mysql-config
Namespace:    default
Labels:       <none>
Annotations:  <none>

Data
====
mysql.cnf:
----
[mysqld]
character-set-server=utf8mb4
collation-server=utf8mb4_general_ci
init-connect='SET NAMES utf8mb4'

[client]
default-character-set=utf8mb4

[mysql]
default-character-set=utf8mb4


BinaryData
====

Events:  <none>

[root@k8s-master mysql]# kubectl exec -it mysql-pod -- bash  # 进入mysql
bash-4.2# mysql -uroot -p123456
mysql: [Warning] Using a password on the command line interface can be insecure.
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 2
Server version: 5.7.44 MySQL Community Server (GPL)

Copyright (c) 2000, 2023, Oracle and/or its affiliates.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> 

# 进入数据库后,可以查看其字符集
mysql> show variables like '%char%'
# 可以看到都是utf-8开头,有两个是系统字符集,无法更改

# 使用edit命令修改mysql-config配置文件
kubectl edit cm mysql-config
# 随便在里面加入一行注释
# eg: this is a msg

# 查看mysql-pod配置文件
kubectl exec -it mysql-pod -- bash
cat /etc/mysql/conf.d/mysql.cnf
# 可以看到多了一行注释,说明配置文件已经自动更新了

Secret:

Secret 用于保存机密数据的对象。一般由于保存密码、令牌或密钥等, 当修改secret后环境变量不会自动更新,需要手动更新
	* data字段用来存储 base64 编码数据。
	* stringData存储未编码的字符串。
	* Secret 意味着你不需要在应用程序代码中包含机密数据,减少机密数据(如密码)泄露的风险。
	* Secret 可以用作环境变量、命令行参数或者存储卷文件。
			
[备注]: 给数据加密成base64命令
echo your-passwd | base64 

mysql-pod.yaml

apiVersion: v1
kind: Secret
metadata:
  name: mysql-password
type: Opaque
data:
  PASSWORD: MTIzNDU2Cg==                           #BASE64编码(echo 123456 | base64)
---
apiVersion: v1
kind: Pod
metadata:
  name: mysql-pod
spec:
  containers:
    - name: mysql
      image: mysql:5.7
      env:
  - name: MYSQL_ROOT_PASSWORD                # 密码是环境变量设置的,指定我们上面创建的Secret
    valueFrom:
      secretKeyRef:
        name: mysql-password		   # 名字为mysql-password,对应上面834行的Secret名称
        key: PASSWORD
        optional: false 			   # 此值为默认值;表示Secret已经创建了,可以直接使用我们创建的
      volumeMounts:
  - mountPath: /var/lib/mysql
    name: data-volume
  - mountPath: /etc/mysql/conf.d
    name: conf-volume
    readOnly: true
  volumes:
    - name: conf-volume
      configMap:
  name: mysql-config
    - name: data-volume
      hostPath:
  # directory location on host
  path: /home/mysql/data
  # this field is optional
  type: DirectoryOrCreate
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: mysql-config
data:
  mysql.cnf: |
    [mysqld]
    character-set-server=utf8mb4
    collation-server=utf8mb4_general_ci
    init-connect='SET NAMES utf8mb4'

    [client]
    default-character-set=utf8mb4

    [mysql]
    default-character-set=utf8mb4
[root@k8s-master mysql]# kubectl apply -f mysql-pod.yaml 
secret/mysql-password created
pod/mysql-pod created
configmap/mysql-config created
# 可以很明显看到多创建了一个secret

[root@k8s-master mysql]# kubectl describe secret/mysql-password  #查看Secret
Name:         mysql-password
Namespace:    default
Labels:       <none>
Annotations:  <none>

Type:  Opaque

Data
====
PASSWORD:  7 bytes
# 可以看到加密数据: [PASSWORD:  7 bytes] 是不显示的
# [注意]: 当Secret被修改后,环境变量不会自动更新,需要重启pod

卷(Volume)

# 将数据存储在容器中,一旦容器被删除,数据也会被删除, 卷是'独立于容器之外'的一块存储区域,通过挂载(Mount)的方式供Pod中的容器使用
# 使用场景
  * 卷可以在多个容器之间共享数据。
  * 卷可以将容器数据存储在外部存储或云存储上。
  * 卷更容易备份或迁移。
# k8s中常见的卷类型:
  * 临时卷(Ephemeral Volume):与 Pod 一起创建和删除,生命周期与 Pod 相同
        emptyDir  - 作为缓存或存储日志
        configMap 、secret、downwardAPI - 给Pod注入数据(注意这里configMap 、secret是卷的类型,并不是configMap 、secret对象)
  * 持久卷(Persistent Volume):删除Pod后,持久卷不会被删除
        本地存储 - hostPath、 local
        网络存储 - NFS
        分布式存储 - Ceph(cephfs文件存储、rbd块存储)
  * 投射卷(Projected Volumes):projected 卷可以将多个卷映射到同一个目录上			

  [补充]: 一个集群中可以有多种存储,比如local,Nfs,Ceph,Cloud Storage,每一种存储单独对应一个存储类(Storage Class),存储类是存储和集群沟通的桥梁,
  通过存储类,我们可以创建不同的持久卷,这些持久卷可以是预先创建好的,也可以是Pod请求动态创建的
  1. 临时卷(Ephemeral Volume),可以缩写为(EV),临时卷,顾名思义用来存储临时数据
* 与 Pod 一起创建和删除,生命周期与 Pod 相同
* emptyDir - 初始内容为空的本地临时目录
* configMap - 为Pod注入配置文件
* secret - 为Pod注入加密数据
[注意]: configMap和secret是卷的类型,他们会引用configMap和secret对象,创建一个临时卷,再将临时卷挂载到Pod中
apiVersion: v1
kind: Pod
metadata:
  name: redis-pod
spec:
  containers:
  - name: redis
    image: redis
    volumeMounts:
    - name: redis-storage
      mountPath: /data/redis
  volumes:
  - name: redis-storage
    emptyDir: {}			#设置为emptyDir卷
其他类型也一样的格式写法
# 可以用以下命令查看临时卷
kubectl get ev 
  1. 持久卷(PV)与持久卷声明(PVC)
	* 持久卷(Persistent Volume):删除Pod后,卷不会被删除
  > 本地存储
        * hostPath - 节点主机上的目录或文件
      (仅供单节点测试使用;多节点集群请用 local 卷代替)
        * local - 节点上挂载的本地存储设备(不支持动态创建卷)
    > 网络存储
        * NFS - 网络文件系统 (NFS) 
    > 分布式存储
        * Ceph(cephfs文件存储、rbd块存储)

* 持久卷(PersistentVolume,PV): 是集群中的一块存储, 可以理解为一块虚拟硬盘
  持久卷可以由管理员事先创建, 或者使用存储类(Storage Class)根据用户请求来动态创建, 持久卷属于集群的公共资源,并不属于某个namespace
  
  > 创建持久卷(PV)是服务端的行为,通常集群管理员会提前创建一些常用规格的持久卷以备使用。
  hostPath仅供单节点测试使用,当Pod被重新创建时,可能会被调度到与原先不同的节点上,导致新的Pod没有数据。
  多节点集群使用本地存储,可以使用local卷, 创建local类型的持久卷,需要先创建存储类(StorageClass)
  
  local卷不支持动态创建,必须手动创建持久卷(PV)
  创建local类型的持久卷,必须设置nodeAffinity(节点亲和性)
  调度器使用nodeAffinity信息来将使用local卷的 Pod 调度到持久卷所在的节点上,不会出现Pod被调度到别的节点上的情况
  
* 持久卷声明(PersistentVolumeClaim,PVC): 表达的是用户对存储的请求
  PVC声明好比申请单,它更贴近云服务的使用场景,使用资源先申请,便于统计和计费, Pod 将 PVC 声明当做存储卷来使用,
  PVC 可以请求指定容量的存储空间和访问模式 。PVC对象是带有namespace的

剩下内容待更新...

posted @ 2025-03-10 11:30  洛又笙  阅读(42)  评论(0)    收藏  举报