k8s集合

一 kubernetes 简介

  • 在Docker 作为高级容器引擎快速发展的同时,在Google内部,容器技术已经应用了很多年

  • Borg系统运行管理着成千上万的容器应用。

  • Kubernetes项目来源于Borg,可以说是集结了Borg设计思想的精华,并且吸收了Borg系统中的经验和教训。

  • Kubernetes对计算资源进行了更高层次的抽象,通过将容器进行细致的组合,将最终的应用服务交给用户。

K8s 就像是数据中心的"操作系统",帮你管理成千上万个容器。

概念 作用
Pod K8s 最小调度单位,一个 Pod 可包含多个容器。
Deployment 管理 Pod 的副本数和滚动更新。
Service 为 Pod 提供稳定的网络访问入口(负载均衡)。
Namespace 逻辑隔离集群资源(如测试/生产环境)。
ConfigMap/Secret 管理配置信息和敏感数据。

kubernetes的本质是一组服务器集群,它可以在集群的每个节点上运行特定的程序,来对节点中的容器进行管理。目的是实现资源管理的自动化,主要提供了如下的主要功能:

  • 自我修复:一旦某一个容器崩溃,能够在1秒中左右迅速启动新的容器

  • 弹性伸缩:可以根据需要,自动对集群中正在运行的容器数量进行调整

  • 服务发现:服务可以通过自动发现的形式找到它所依赖的服务

  • 负载均衡:如果一个服务起动了多个容器,能够自动实现请求的负载均衡

  • 版本回退:如果发现新发布的程序版本有问题,可以立即回退到原来的版本

  • 存储编排:可以根据容器自身的需求自动创建存储卷

二 K8S集群环境搭建

2.1 k8s中容器的管理方式

kubelet 如何把 Pod 调度到节点并最终变成真正的容器进程

  • dockershim(已退役)

    • 旧时代 kubelet 内置的"翻译器",把 CRI 调用翻译成 Docker Engine 能听懂的命令。

    • Kubernetes 1.24 删除,现已不可用。

  • cri-dockerd

    • 社区/Mirantis 维护的 独立进程 ,替代退役的 dockershim,让 kubelet 继续通过 CRI 与 Docker Engine 交互。
  • CRI-O

    • 红帽主推的 轻量级 CRI 实现 ,直接对接 runc,不依赖 Docker Engine。

    • CRI-O的方式是Kubernetes创建容器最直接的一种方式,在创建集群的时候,需要借助于cri-o插件的方式来实现Kubernetes集群的创建。

  • containerd

    • 默认情况下,K8S在创建集群时使用的方式
    • CNCF 毕业项目,内置 CRI-plugin,天生支持 CRI,无需额外翻译层。

2.2 k8s 集群部署

2.2.1 k8s 环境部署说明

K8S中文官网:Kubernetes

主机名 ip 角色
harbor 192.168.217.200 harbor仓库
k8s-master 192.168.217.100 master,k8s集群控制节点
k8s-node1 192.168.217.10 worker,k8s集群工作节点
k8s-node2 192.168.217.20 worker,k8s集群工作节点
  • 所有节点禁用selinux和防火墙
  • 所有节点同步时间和解析
bash 复制代码
[root@k8s-master ~]# cat /etc/hosts
127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
::1         localhost localhost.localdomain localhost6 localhost6.localdomain6
192.168.217.100 k8s-master
192.168.217.10  k8s-node1
192.168.217.20  k8s-node2
192.168.217.200 reg.timinglee.org

2.2.2 集群环境初始化

所有k8s集群节点执行以下步骤

2.2.2.1.所有禁用swap和本地解析

彻底关闭 Linux 的 swap,防止 kubelet 启动失败。

bash 复制代码
所有节点执行
[root@master ~]# systemctl daemon-reload
[root@master ~]# systemctl mask swap.target  	永久屏蔽 swap.target,无论任何服务依赖 swap.target,都当它不存在。生成到 /dev/null 的符号链接,确保重启后 swap 分区不会被自动挂载。
Created symlink /etc/systemd/system/swap.target → /dev/null.
[root@master ~]# swapoff -a  #立即生效关闭当前所有已激活的 swap 设备/文件。
2.2.2.2.所有安装docker

配置仓库

bash 复制代码
[root@k8s-master ~]# vim /etc/yum.repos.d/docker.repo
[docker]
name=docker
baseurl=https://mirrors.aliyun.com/docker-ce/linux/rhel/9/x86_64/stable/
gpgcheck=0

安装:

bash 复制代码
[root@k8s-master ~]# dnf install docker-ce -y
2.2.2.3.所有节点设定docker的资源管理模式为systemd
bash 复制代码
[root@k8s-master ~]# cat /etc/docker/daemon.json
{
        "registry-mirrors": ["https://reg.timinglee.org"],  #harbor仓库
        "exec-opts": ["native.cgroupdriver=systemd"]
}
[root@k8s-master ~]#
2.2.2.4.所有阶段复制harbor仓库中的证书并启动docker

部署harbor仓库

bash 复制代码
#解压下载的压缩包
[root@docker ~]# tar zxf harbor-offline-installer-v2.5.4.tgz
[root@docker ~]# ls
anaconda-ks.cfg  certs   harbor-offline-installer-v2.5.4.tgz
auth             harbor
[root@docker ~]# cd harbor/

[root@docker harbor]# cp harbor.yml.tmpl harbor.yml
#设置仓库名和密码
[root@docker harbor]# vim harbor.yml

  hostname: reg.timinglee.org
  certificate: /data/certs/timinglee.org.crt
  private_key: /data/certs/timinglee.org.key
  harbor_admin_password: 123

[root@docker harbor]# ./install.sh --help


Please set --with-notary 				#证书签名
Please set --with-trivy  				#安全扫描
Please set --with-chartmuseum if needs enable Chartmuseum in Harbor


[root@docker harbor]# ./install.sh --with-chartmuseum


#管理harbor的容器    要进入harbor这个目录执行
[root@docker harbor]# docker compose stop   #停止
[root@docker harbor]# docker compose  up -d #启动
bash 复制代码
将harbor仓库的证书复制到节点
[root@reg harbor]# ll -s /etc/docker/certs.d/reg.timinglee.org/ca.crt
4 -rw-r--r-- 1 root root 2191 Aug 11 06:12 /etc/docker/certs.d/reg.timinglee.org/ca.crt
[root@reg harbor]#

[root@k8s-master ~]#scp /etc/docker/certs.d/reg.timinglee.org/ca.crt root@192.168.217.10:/etc/docker/certs.d/reg.timinglee.org/

启动docker
[root@k8s-master ~]# systemctl enable --now docker

登录仓库:
[root@k8s-master ~]# docker login reg.timinglee.org
Username: admin
Password:

WARNING! Your credentials are stored unencrypted in '/root/.docker/config.                                                                              json'.
Configure a credential helper to remove this warning. See
https://docs.docker.com/go/credential-store/

Login Succeeded
[root@k8s-master ~]#

[root@k8s-master ~]# docker info
2.2.2.5 安装K8S部署工具
bash 复制代码
#部署软件仓库,添加K8S源
[root@k8s-master ~]# vim /etc/yum.repos.d/k8s.repo
[k8s]
name=k8s
baseurl=https://mirrors.aliyun.com/kubernetes-new/core/stable/v1.30/rpm
gpgcheck=0

#安装软件
[root@k8s-master ~]# dnf install kubelet-1.30.0 kubeadm-1.30.0 kubectl-1.30.0 -y
2.2.2.6 设置kubectl命令补齐功能
bash 复制代码
#安装 bash-completion 软件包,它提供了 /usr/share/bash-completion/bash_completion 脚本,是大多数 CLI 补全功能的基础。
[root@k8s-master ~]# dnf install bash-completion -y

#把 kubectl 的 Bash 补全脚本追加到当前用户的 ~/.bashrc 末尾,保证以后每次登录 shell 都会自动加载。
[root@k8s-master ~]# echo "source <(kubectl completion bash)" >> ~/.bashrc

#让修改立即生效,无需重新登录。
[root@k8s-master ~]# source  ~/.bashrc
2.2.2.7 在所节点安装cri-docker

k8s从1.24版本开始移除了dockershim,所以需要安装cri-docker插件才能使用docker

软件下载:https://github.com/Mirantis/cri-dockerd

bash 复制代码
[root@k8s-master ~]# dnf install libcgroup-0.41-19.el8.x86_64.rpm \
> cri-dockerd-0.3.14-3.el8.x86_64.rpm -y

[root@k8s-master ~]# vim /lib/systemd/system/cri-docker.service
[Unit]
Description=CRI Interface for Docker Application Container Engine
Documentation=https://docs.mirantis.com
After=network-online.target firewalld.service docker.service
Wants=network-online.target
Requires=cri-docker.socket

[Service]
Type=notify

#指定网络插件名称及基础容器镜像
ExecStart=/usr/bin/cri-dockerd --container-runtime-endpoint fd:// --network-plugin=cni --pod-infra-container-image=reg.timinglee.org/k8s/pause:3.9

ExecReload=/bin/kill -s HUP $MAINPID
TimeoutSec=0
RestartSec=2
Restart=always

[root@k8s-master ~]# systemctl daemon-reload
[root@k8s-master ~]# systemctl start cri-docker
[root@k8s-master ~]# ll /var/run/cri-dockerd.sock
srw-rw---- 1 root docker 0  8月 26 22:14 /var/run/cri-dockerd.sock		#cri-dockerd的套接字文件
2.2.2.8 在master节点拉取K8S所需镜像
bash 复制代码
#拉取k8s集群所需要的镜像
[root@k8s-master ~]# kubeadm config images pull \
--image-repository registry.aliyuncs.com/google_containers \
--kubernetes-version v1.30.0 \
--cri-socket=unix:///var/run/cri-dockerd.sock

#上传镜像到harbor仓库
[root@k8s-master ~]# docker images | awk '/google/{ print $1":"$2}' \
| awk -F "/" '{system("docker tag "$0" reg.timinglee.org/k8s/"$3)}'

[root@k8s-master ~]# docker images  | awk '/k8s/{system("docker push "$1":"$2)}'	
2.2.2.9 集群初始化
bash 复制代码
#启动kubelet服务
[root@k8s-master ~]# systemctl status kubelet.service

#执行初始化命令
[root@k8s-master ~]# kubeadm init --pod-network-cidr=10.244.0.0/16 \
--image-repository reg.timinglee.org/k8s \
--kubernetes-version v1.30.0 \
--cri-socket=unix:///var/run/cri-dockerd.sock

#指定集群配置文件变量
[root@k8s-master ~]# echo "export KUBECONFIG=/etc/kubernetes/admin.conf" >> ~/.bash_profile

#当前节点没有就绪,因为还没有安装网络插件,容器没有运行
[root@k8s-master ~]# kubectl get node
NAME                       STATUS     ROLES           AGE     VERSION
k8s-master.timinglee.org   NotReady   control-plane   4m25s   v1.30.0
root@k8s-master ~]# kubectl get pod -A
NAMESPACE     NAME                                               READY   STATUS    RESTARTS   AGE
kube-system   coredns-647dc95897-2sgn8                           0/1     Pending   0          6m13s
kube-system   coredns-647dc95897-bvtxb                           0/1     Pending   0          6m13s
...

在此阶段如果生成的集群token找不到了可以重新生成

root@k8s-master \~\]# kubeadm token create --print-join-command kubeadm join 192.168.217.100:6443 --token 5hwptm.zwn7epa6pvatbpwf --discovery-token-ca-cert-hash sha256:52f1a83b70ffc8744db5570288ab51987ef2b563bf906ba4244a300f6 1e9db23

2.2.2.10 安装flannel网络插件

官方网站:https://github.com/flannel-io/flannel

bash 复制代码
#下载flannel的yaml部署文件
[root@k8s-master ~]# wget https://github.com/flannel-io/flannel/releases/latest/download/kube-flannel.yml

#下载镜像:
[root@k8s-master ~]# docker pull docker.io/flannel/flannel:v0.25.5
[root@k8s-master ~]# docekr docker.io/flannel/flannel-cni-plugin:v1.5.1-flannel1

#上传镜像到仓库
[root@k8s-master ~]# docker tag flannel/flannel:v0.25.5 \
reg.timinglee.org/flannel/flannel:v0.25.5
[root@k8s-master ~]# docker push reg.timinglee.org/flannel/flannel:v0.25.5

[root@k8s-master ~]# docker tag flannel/flannel-cni-plugin:v1.5.1-flannel1 \
reg.timinglee.org/flannel/flannel-cni-plugin:v1.5.1-flannel1
[root@k8s-master ~]# docker push reg.timinglee.org/flannel/flannel-cni-plugin:v1.5.1-flannel1
bash 复制代码
#编辑kube-flannel.yml 修改镜像下载位置
[root@k8s-master ~]# vim kube-flannel.yml

#需要修改以下几行
[root@k8s-master ~]# grep -n image kube-flannel.yml
146:        image: reg.timinglee.org/flannel/flannel:v0.25.5
173:        image: reg.timinglee.org/flannel/flannel-cni-plugin:v1.5.1-flannel1
184:        image: reg.timinglee.org/flannel/flannel:v0.25.5

#安装flannel网络插件
[root@k8s-master ~]# kubectl apply -f kube-flannel.yml
2.2.2.11 节点扩容

基本配置和上面的一样

加入集群,在node节点上做

bash 复制代码
[root@k8s-node1~]# kubeadm join 192.168.217.100:6443 --token 5hwptm.zwn7epa6pvatbpwf --discovery-token-ca-cert-hash   #这里我的192,168.217.100是我k8s的master主机

在master阶段中查看所有node的状态,当status都是ready状态就说明okl

三 k8s的pod管理及优化

  • Pod是可以创建和管理Kubernetes计算的最小可部署单元

  • 一个Pod代表着集群中运行的一个进程,每个pod都有一个唯一的ip。

  • 一个pod类似一个豌豆荚,包含一个或多个容器(通常是docker)

  • 多个容器间共享IPC、Network和UTC namespace。

3.1 创建自主式pod (生产不推荐)

优点:

灵活性高

  • 可以精确控制 Pod 的各种配置参数,包括容器的镜像、资源限制、环境变量、命令和参数等,满足特定的应用需求。

学习和调试方便

  • 对于学习 Kubernetes 的原理和机制非常有帮助,通过手动创建 Pod 可以深入了解 Pod 的结构和配置方式。在调试问题时,可以更直接地观察和调整 Pod 的设置。

适用于特殊场景

  • 在一些特殊情况下,如进行一次性任务、快速验证概念或在资源受限的环境中进行特定配置时,手动创建 Pod 可能是一种有效的方式。

缺点:

管理复杂

  • 如果需要管理大量的 Pod,手动创建和维护会变得非常繁琐和耗时。难以实现自动化的扩缩容、故障恢复等操作。

缺乏高级功能

  • 无法自动享受 Kubernetes 提供的高级功能,如自动部署、滚动更新、服务发现等。这可能导致应用的部署和管理效率低下。

可维护性差

  • 手动创建的 Pod 在更新应用版本或修改配置时需要手动干预,容易出现错误,并且难以保证一致性。相比之下,通过声明式配置或使用 Kubernetes 的部署工具可以更方便地进行应用的维护和更新。
bash 复制代码
#查看所有pods
[root@k8s-master ~]# kubectl get pods
No resources found in default namespace.

#建立一个名为test的pod
[root@master ~]# kubectl run test --image nginx
pod/test created
#查看test的状态
[root@master ~]# kubectl get pods test
NAME   READY   STATUS    RESTARTS   AGE
test   0/1     Pending   0          9s

#显示pod的较为详细的信息
[root@master ~]# kubectl get pods test -o wide
NAME   READY   STATUS    RESTARTS   AGE   IP       NODE     NOMINATED NODE   READINESS GATES
test   0/1     Pending   0          12s   <none>   <none>   <none>           <none>
[root@master ~]#

3.2 利用控制器管理pod(推荐)

高可用性和可靠性

  • 自动故障恢复:如果一个 Pod 失败或被删除,控制器会自动创建新的 Pod 来维持期望的副本数量。确保应用始终处于可用状态,减少因单个 Pod 故障导致的服务中断。

  • 健康检查和自愈:可以配置控制器对 Pod 进行健康检查(如存活探针和就绪探针)。如果 Pod 不健康,控制器会采取适当的行动,如重启 Pod 或删除并重新创建它,以保证应用的正常运行。

可扩展性

  • 轻松扩缩容:可以通过简单的命令或配置更改来增加或减少 Pod 的数量,以满足不同的工作负载需求。例如,在高流量期间可以快速扩展以处理更多请求,在低流量期间可以缩容以节省资源。

  • 水平自动扩缩容(HPA):可以基于自定义指标(如 CPU 利用率、内存使用情况或应用特定的指标)自动调整 Pod 的数量,实现动态的资源分配和成本优化。

版本管理和更新

  • 滚动更新:对于 Deployment 等控制器,可以执行滚动更新来逐步替换旧版本的 Pod 为新版本,确保应用在更新过程中始终保持可用。可以控制更新的速率和策略,以减少对用户的影响。

  • 回滚:如果更新出现问题,可以轻松回滚到上一个稳定版本,保证应用的稳定性和可靠性。

声明式配置

  • 简洁的配置方式:使用 YAML 或 JSON 格式的声明式配置文件来定义应用的部署需求。这种方式使得配置易于理解、维护和版本控制,同时也方便团队协作。

  • 期望状态管理:只需要定义应用的期望状态(如副本数量、容器镜像等),控制器会自动调整实际状态与期望状态保持一致。无需手动管理每个 Pod 的创建和删除,提高了管理效率。

服务发现和负载均衡

  • 自动注册和发现:Kubernetes 中的服务(Service)可以自动发现由控制器管理的 Pod,并将流量路由到它们。这使得应用的服务发现和负载均衡变得简单和可靠,无需手动配置负载均衡器。

  • 流量分发:可以根据不同的策略(如轮询、随机等)将请求分发到不同的 Pod,提高应用的性能和可用性。

多环境一致性

  • 一致的部署方式:在不同的环境(如开发、测试、生产)中,可以使用相同的控制器和配置来部署应用,确保应用在不同环境中的行为一致。这有助于减少部署差异和错误,提高开发和运维效率。

示例

bash 复制代码
[root@master ~]# kubectl create deployment hh --image myapp:v1
deployment.apps/hh created
[root@master ~]# kubectl get pods -o wide
NAME                  READY   STATUS    RESTARTS   AGE   IP                NODE    NOMINATED NODE   READINESS GATES
hh-857cb68d9f-b99c5   1/1     Running   0          87s   192.168.166.146   node1   <none>           <none>
[root@master ~]#

扩容

bash 复制代码
[root@master ~]# kubectl scale deployment hh --replicas 4

运行容器由一个变为4个

缩容

bash 复制代码
[root@master ~]# kubectl scale deployment hh --replicas 2 

#通过修改--replicas后跟的数值来控制运行容器的数量

3.3利用yaml文件部署

获取资源帮助

bash 复制代码
kubectl explain pod.spec.containers
示例:

1.运行简单的单个容器pod

bash 复制代码
[root@master ~]# kubectl run test --image myapp:v1 --dry-run=client -o yaml > test.yml
[root@master ~]# cat test.yml
apiVersion: v1
kind: Pod
metadata:
  labels:
    run: test  #pod标签
  name: test    #pod名称
spec:
  containers:
  - image: myapp:v1 #pod使用的镜像
    name: test      #容器名称

修改完yml文件后运行

bash 复制代码
[root@master ~]# kubectl apply -f test.yml   #-f后跟文件名

删除

bash 复制代码
[root@master ~]# kubectl delete -f test.yml

2.运行简单的多个容器pod

在一个pod中开启多个容器时一定要确保容器彼此不能互相干扰

bash 复制代码
[root@master ~]# cat test.yml
apiVersion: v1
kind: Pod
metadata:
  labels:
    run: test
  name: test
spec:
  containers:
  - image: busyboxplus:latest
    name: busybox
    command:
      - /bin/sh
      - -c
      - sleep 100000
  - image: nginx:latest
    name: nginx
[root@master ~]# kubectl apply -f test.yml
pod/test unchanged
[root@master ~]# kubectl get pods
NAME   READY   STATUS    RESTARTS   AGE
test   2/2     Running   0          79s
[root@master ~]#

3.端口映射

bash 复制代码
[root@master ~]# cat test.yml
apiVersion: v1
kind: Pod
metadata:
  labels:
    run: test
  name: test
spec:
  containers:
  - image: myapp:v1
    name: myappv1
    ports:
    - name: http
      containerPort: 80
      hostPort: 80
      protocol: TCP
[root@master ~]# kubectl apply -f test.yml
pod/test unchanged

4.如何设定环境变量

bash 复制代码
[root@master hh]# cat test.yml
apiVersion: v1
kind: Pod
metadata:
  labels:
    run: test
  name: test
spec:
  containers:
    - image: busyboxplus:latest
      name: busybox
      command: ["/bin/sh","-c","echo $NAME;sleep 3000000"]
      env:
      - name: NAME
        value: haha
[root@master hh]# kubectl logs pods/test busybox
haha
[root@master hh]#

5.资源限制

资源限制会影响pod的Qos Class资源优先级,资源优先级分为Guaranteed > Burstable > BestEffort

QoS(Quality of Service)即服务质量

资源设定 优先级类型
资源限定未设定 BestEffort
资源限定设定且最大和最小不一致 Burstable
资源限定设定且最大和最小一致 Guaranteed
bash 复制代码
[root@master hh]# cat test.yml
apiVersion: v1
kind: Pod
metadata:
  labels:
    run: test
  name: test
spec:
  containers:
    - image: myapp:v1
      name: myapp
      resources:
        limits:
          cpu: 500m
          memory: 200M
        requests:
          cpu: 500m
          memory: 200M
[root@master hh]# kubectl describe pods test

6.容器启动管理

bash 复制代码
[root@master hh]# cat test.yml
apiVersion: v1
kind: Pod
metadata:
  labels:
    run: test
  name: test
spec:
  restartPolicy: Always   #kubelet 会不停地在本节点重启这个 Pod 的容器
  containers:
    - image: myapp:v1
      name: myapp
[root@master hh]# kubectl get pods -o wide
NAME   READY   STATUS    RESTARTS   AGE     IP                NODE    NOMINATED NODE   READINESS GATES
test   1/1     Running   1          6m48s   192.168.166.159   node1   <none>           <none>
[root@master hh]#

到pod运行的node节点上删除容器,观察是否会重启

bash 复制代码
[root@node1 ~]# docker ps | grep myapp
862ce93039b3   d4a5e0eaa84f                      "nginx -g 'daemon of..."   1 second ago     Up 1 second               k8s_myapp_test_default_8e3d402c-8677-40cb-a11a-b19260e71877_2
[root@node1 ~]# docker rm -f 862ce93039b3
862ce93039b3

7.选择运行节点

bash 复制代码
[root@master hh]# cat test.yml
apiVersion: v1
kind: Pod
metadata:
  labels:
    run: test
  name: test
spec:
  restartPolicy: Always
  nodeSelector:
    kubernetes.io/hostname: node2  #手动选择运行的节点
  containers:
    - image: myapp:v1
      name: myapp

8.共享宿主机网络

bash 复制代码
[root@master hh]# cat test.yml
apiVersion: v1
kind: Pod
metadata:
  labels:
    run: test
  name: test
spec:
  hostNetwork: true
  restartPolicy: Always
  nodeSelector:
    kubernetes.io/hostname: node2
  containers:
    - image: busyboxplus:latest
      name: busybox
      command:
        - /bin/sh
        - -c
        - sleep 100000
[root@master hh]# kubectl get pods  -o wide
NAME   READY   STATUS    RESTARTS   AGE   IP               NODE    NOMINATED NODE   READINESS GATES
test   1/1     Running   0          97s   192.168.217.20   node2   <none>           <none>
[root@master hh]#

四 k8s中的控制器应用

官方文档:

工作负载管理 | Kubernetes

控制器也是管理pod的一种手段

  • 自主式pod:pod退出或意外关闭后不会被重新创建

  • 控制器管理的 Pod:在控制器的生命周期里,始终要维持 Pod 的副本数目

Pod控制器是管理pod的中间层,使用Pod控制器之后,只需要告诉Pod控制器,想要多少个什么样的Pod就可以了,它会创建出满足条件的Pod并确保每一个Pod资源处于用户期望的目标状态。如果Pod资源在运行中出现故障,它会基于指定策略重新编排Pod

当建立控制器后,会把期望值写入etcd,k8s中的apiserver检索etcd中我们保存的期望状态,并对比pod的当前状态,如果出现差异代码自驱动立即恢复

类型:

控制器名称 控制器用途
Replication Controller 比较原始的pod控制器,已经被废弃,由ReplicaSet替代
ReplicaSet ReplicaSet 确保任何时间都有指定数量的 Pod 副本在运行
Deployment 一个 Deployment 为 PodReplicaSet 提供声明式的更新能力
DaemonSet DaemonSet 确保全指定节点上运行一个 Pod 的副本
StatefulSet StatefulSet 是用来管理有状态应用的工作负载 API 对象。
Job 执行批处理任务,仅执行一次任务,保证任务的一个或多个Pod成功结束
CronJob Cron Job 创建基于时间调度的 Jobs。
HPA全称Horizontal Pod Autoscaler 根据资源利用率自动调整service中Pod数量,实现Pod水平自动缩放

4.1 replicaset控制器

replicaset参数说明

参数名称 字段类型 参数说明
spec Object 详细定义对象,固定值就写Spec
spec.replicas integer 指定维护pod数量
spec.selector Object Selector是对pod的标签查询,与pod数量匹配
spec.selector.matchLabels string 指定Selector查询标签的名称和值,以key:value方式指定
spec.template Object 指定对pod的描述信息,比如lab标签,运行容器的信息等
spec.template.metadata Object 指定pod属性
spec.template.metadata.labels string 指定pod标签
spec.template.spec Object 详细定义对象
spec.template.spec.containers list Spec对象的容器列表定义
spec.template.spec.containers.name string 指定容器名称
spec.template.spec.containers.image string 指定容器镜像

示例:

bash 复制代码
#生成yml文件
[root@k8s-master ~]# kubectl create deployment replicaset --image myapp:v1 --dry-run=client -o yaml > replicaset.yml

[root@k8s-master ~]# vim replicaset.yml
apiVersion: apps/v1
kind: ReplicaSet
metadata:
  name: replicaset		#指定pod名称,一定小写,如果出现大写报错
spec:
  replicas: 2			#指定维护pod数量为2
  selector:				#指定检测匹配方式
    matchLabels:		#指定匹配方式为匹配标签
      app: myapp		#指定匹配的标签为app=myapp

  template:				#模板,当副本数量不足时,会根据下面的模板创建pod副本
    metadata:
      labels:
        app: myapp
    spec:
      containers:
      - image: myapp:v1
        name: myapp

[root@k8s-master ~]# kubectl apply -f replicaset.yml
replicaset.apps/replicaset created


[root@k8s-master ~]# kubectl get pods  --show-labels
NAME               READY   STATUS    RESTARTS   AGE   LABELS
replicaset-l4xnr   1/1     Running   0          96s   app=myapp
replicaset-t2s5p   1/1     Running   0          96s   app=myapp


#replicaset是通过标签匹配pod
[root@k8s-master ~]# kubectl label pod replicaset-l4xnr app=timinglee --overwrite
pod/replicaset-l4xnr labeled
[root@k8s-master ~]# kubectl get pods  --show-labels
NAME               READY   STATUS    RESTARTS   AGE     LABELS
replicaset-gd5fh   1/1     Running   0          2s      app=myapp		#新开启的pod
replicaset-l4xnr   1/1     Running   0          3m19s   app=timinglee
replicaset-t2s5p   1/1     Running   0          3m19s   app=myapp

#恢复标签后
[root@k8s2 pod]# kubectl label pod replicaset-example-q2sq9 app- 
[root@k8s2 pod]# kubectl get pod --show-labels
NAME                       READY   STATUS    RESTARTS   AGE     LABELS
replicaset-example-q2sq9   1/1     Running   0          3m14s   app=nginx
replicaset-example-th24v   1/1     Running   0          3m14s   app=nginx
replicaset-example-w7zpw   1/1     Running   0          3m14s   app=nginx

#replicaset自动控制副本数量,pod可以自愈
[root@k8s-master ~]# kubectl delete pods replicaset-t2s5p
pod "replicaset-t2s5p" deleted

[root@k8s-master ~]# kubectl get pods --show-labels
NAME               READY   STATUS    RESTARTS   AGE     LABELS
replicaset-l4xnr   1/1     Running   0          5m43s   app=myapp
replicaset-nxmr9   1/1     Running   0          15s     app=myapp


回收资源
[root@k8s2 pod]# kubectl delete -f rs-example.yml

4.2 deployment 控制器

  • 为了更好的解决服务编排的问题,kubernetes在V1.2版本开始,引入了Deployment控制器。

  • Deployment控制器并不直接管理pod,而是通过管理ReplicaSet来间接管理Pod

  • Deployment管理ReplicaSet,ReplicaSet管理Pod

  • Deployment 为 Pod 和 ReplicaSet 提供了一个申明式的定义方法

  • 在Deployment中ReplicaSet相当于一个版本

示例:

bash 复制代码
#生成yaml文件
[root@k8s-master ~]# kubectl create deployment deployment --image myapp:v1  --dry-run=client -o yaml > deployment.yml

[root@k8s-master ~]# vim deployment.yml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: deployment
spec:
  replicas: 3
  selector:
    matchLabels:
      app: myapp
  template:
    metadata:
      labels:
        app: myapp
    spec:
      containers:
      - image: myapp:v1
        name: myapp
#建立pod
root@k8s-master ~]# kubectl apply -f deployment.yml
deployment.apps/deployment created

4.3 daemonset控制器

DaemonSet 确保全部(或者某些)节点上运行一个 Pod 的副本。当有节点加入集群时, 也会为他们新增一个 Pod ,当有节点从集群移除时,这些 Pod 也会被回收。删除 DaemonSet 将会删除它创建的所有 Pod

DaemonSet 的典型用法:

  • 在每个节点上运行集群存储 DaemonSet,例如 glusterd、ceph。

  • 在每个节点上运行日志收集 DaemonSet,例如 fluentd、logstash。

  • 在每个节点上运行监控 DaemonSet,例如 Prometheus Node Exporter、zabbix agent等

  • 一个简单的用法是在所有的节点上都启动一个 DaemonSet,将被作为每种类型的 daemon 使用

  • 一个稍微复杂的用法是单独对每种 daemon 类型使用多个 DaemonSet,但具有不同的标志, 并且对不同硬件类型具有不同的内存、CPU 要求

示例:

bash 复制代码
[root@k8s2 pod]# cat daemonset.yml
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: daemonset
spec:
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      tolerations:		    #对于污点节点的容忍
      - effect: NoSchedule
        operator: Exists
      containers:
      - name: nginx
        image: nginx

#回收
[root@k8s2 pod]# kubectl delete -f daemonset.yml

4.4 job 控制器

job,主要用于负责批量处理(一次要处理指定数量任务)短暂的一次性(每个任务仅运行一次就结束)任务

Job特点如下:

  • 当Job创建的pod执行成功结束时,Job将记录成功结束的pod数量

  • 当成功结束的pod达到指定的数量时,Job将完成执行

示例:

bash 复制代码
[root@k8s2 pod]# vim job.yml
apiVersion: batch/v1
kind: Job
metadata:
  name: pi
spec:
  completions: 6		#一共完成任务数为6		
  parallelism: 2		#每次并行完成2个
  template:
    spec:
      containers:
      - name: pi
        image: perl:5.34.0
        command: ["perl",  "-Mbignum=bpi", "-wle", "print bpi(2000)"]	#计算Π的后2000位
      restartPolicy: Never						#关闭后不自动重启
  backoffLimit: 4								#运行失败后尝试4重新运行

[root@k8s2 pod]# kubectl apply -f job.yml

关于重启策略设置的说明:

  • 如果指定为OnFailure,则job会在pod出现故障时重启容器

    而不是创建pod,failed次数不变

  • 如果指定为Never,则job会在pod出现故障时创建新的pod

    并且故障pod不会消失,也不会重启,failed次数加1

  • 如果指定为Always的话,就意味着一直重启,意味着job任务会重复去执行了

4.5cronjob 控制器

  • Cron Job 创建基于时间调度的 Jobs。

  • CronJob控制器以Job控制器资源为其管控对象,并借助它管理pod资源对象,

  • CronJob可以以类似于Linux操作系统的周期性任务作业计划的方式控制其运行时间点及重复运行的方式。

  • CronJob可以在特定的时间点(反复的)去运行job任务。

示例:

bash 复制代码
[root@k8s2 pod]# vim cronjob.yml
apiVersion: batch/v1
kind: CronJob
metadata:
  name: hello
spec:
  schedule: "* * * * *"
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - name: hello
            image: busybox
            imagePullPolicy: IfNotPresent
            command:
            - /bin/sh
            - -c
            - date; echo Hello from the Kubernetes cluster
          restartPolicy: OnFailure

[root@k8s2 pod]# kubectl apply -f cronjob.yml

五 k8s中的微服务

5.1 什么是微服务

微服务:用控制器来完成集群的工作负载,那么应用如何暴漏出去?需要通过微服务暴漏出去后才能被访问

  • Service是一组提供相同服务的Pod对外开放的接口。

  • 借助Service,应用可以实现服务发现和负载均衡。

  • service默认只支持4层负载均衡能力,没有7层功能。(可以通过Ingress实现)

5.2类型:

微服务类型 作用描述
ClusterIP 默认值,k8s系统给service自动分配的虚拟IP,只能在集群内部访问
NodePort 将Service通过指定的Node上的端口暴露给外部,访问任意一个NodeIP:nodePort都将路由到ClusterIP
LoadBalancer 在NodePort的基础上,借助cloud provider创建一个外部的负载均衡器,并将请求转发到 NodeIP:NodePort,此模式只能在云服务器上使用
ExternalName 将服务通过 DNS CNAME 记录方式转发到指定的域名(通过 spec.externlName 设定

示例:

bash 复制代码
#生成控制器文件并建立控制器
[root@k8s-master ~]# kubectl create deployment timinglee --image myapp:v1  --replicas 2 --dry-run=client -o yaml > timinglee.yaml

#生成微服务yaml追加到已有yaml中
[root@k8s-master ~]# kubectl expose deployment timinglee --port 80 --target-port 80 --dry-run=client -o yaml >> timinglee.yaml

[root@k8s-master ~]# vim timinglee.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: timinglee
  name: timinglee
spec:
  replicas: 2
  selector:
    matchLabels:
      app: timinglee
  template:
    metadata:
      creationTimestamp: null
      labels:
        app: timinglee
    spec:
      containers:
      - image: myapp:v1
        name: myapp
---										#不同资源间用---隔开

apiVersion: v1
kind: Service
metadata:
  labels:
    app: timinglee
  name: timinglee
spec:
  ports:
  - port: 80
    protocol: TCP
    targetPort: 80
  selector:
    app: timinglee

[root@k8s-master ~]# kubectl apply  -f timinglee.yaml
deployment.apps/timinglee created
service/timinglee created

[root@k8s-master ~]# kubectl get services
NAME         TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE
kubernetes   ClusterIP   10.96.0.1       <none>        443/TCP   19h
timinglee    ClusterIP   10.99.127.134   <none>        80/TCP    16s

默认使用iptables调度

bash 复制代码
[root@k8s-master ~]# kubectl get services  -o wide
NAME         TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE    SELECTOR
kubernetes   ClusterIP   10.96.0.1       <none>        443/TCP   19h    <none>
timinglee    ClusterIP   10.99.127.134   <none>        80/TCP    119s   app=timinglee			#集群内部IP 134

#可以在火墙中查看到策略信息
[root@k8s-master ~]# iptables -t nat -nL
KUBE-SVC-I7WXYK76FWYNTTGM  6    --  0.0.0.0/0            10.99.127.134        /* default/timinglee cluster IP */ tcp dpt:80

5.3微服务类型详解

5.3.1 clusterip

特点:

clusterip模式只能在集群内访问,并对集群内的pod提供健康检测和自动发现功能

示例:

bash 复制代码
[root@master hh]# cat clusterip.yml
apiVersion: v1
kind: Service
metadata:
  labels:
    app: clusterip
  name: clusterip
spec:
  ports:
  - port: 80
    protocol: TCP
    targetPort: 80
  selector:
    app: haha
  type: ClusterIP
[root@master hh]#


service创建后集群DNS提供解析
[root@master hh]# kubectl -n kube-system get svc
NAME           TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)                  AGE
kube-dns       ClusterIP   10.96.0.10      <none>        53/UDP,53/TCP,9153/TCP   9d

[root@master service]# dig myappv1.default.svc.cluster.local @10.96.0.10

5.3.2 ClusterIP中的特殊模式headless

headless(无头服务)

对于无头 Services 并不会分配 Cluster IP,kube-proxy不会处理它们, 而且平台也不会为它们进行负载均衡和路由,集群访问通过dns解析直接指向到业务pod上的IP,所有的调度有dns单独完成

示例:

bash 复制代码
[root@master hh]# cat headless.yml
apiVersion: v1
kind: Service
metadata:
  labels:
    app: headless
  name: headless
spec:
  ports:
  - port: 80
    protocol: TCP
    targetPort: 80
  selector:
    app: headless
  type: ClusterIP
  clusterIP: None
[root@master hh]#
[root@master hh]# kubectl get svc
headless    kubernetes  nginx-svc
[root@master hh]# kubectl get svc headless
NAME       TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
headless   ClusterIP   None         <none>        80/TCP    16s
[root@master hh]# ipvsadm -Ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
  -> RemoteAddress:Port           Forward Weight ActiveConn InActConn
TCP  172.17.0.1:30467 rr
  -> 192.168.104.20:443           Masq    1      0          0
TCP  172.17.0.1:31008 rr
  -> 192.168.104.20:80            Masq    1      0          0
TCP  192.168.217.50:80 rr
  -> 192.168.104.20:80            Masq    1      0          0
TCP  192.168.217.50:443 rr
  -> 192.168.104.20:443           Masq    1      0          0
TCP  192.168.217.100:30467 rr
  -> 192.168.104.20:443           Masq    1      0          0
TCP  192.168.217.100:31008 rr
  -> 192.168.104.20:80            Masq    1      0          0
TCP  10.96.0.1:443 rr
  -> 192.168.217.100:6443         Masq    1      4          0
TCP  10.96.0.10:53 rr
  -> 192.168.219.69:53            Masq    1      0          0
  -> 192.168.219.70:53            Masq    1      0          0
。。。

5.3.3 nodeport

通过ipvs暴漏端口从而使外部主机通过master节点的对外ip:<port>来访问pod业务

其访问过程为: nodeport → ClusterIP →pods

示例:

bash 复制代码
[root@master hh]# cat nodeport.yml
apiVersion: v1
kind: Service
metadata:
  labels:
    app: nodeport-service
  name: nodeport-service
spec:
  ports:
  - port: 80
    protocol: TCP
    targetPort: 80
  selector:
    app: nodeport
  type: NodePort
[root@master hh]# kubectl apply -f nodeport.yml
service/nodeport-service created
[root@master hh]# kubectl get svc nodeport-service
NAME               TYPE       CLUSTER-IP       EXTERNAL-IP   PORT(S)        AGE
nodeport-service   NodePort   10.104.136.138   <none>        80:30791/TCP   10s

5.3.4 loadbalancer

云平台会为我们分配vip并实现访问,如果是裸金属主机那么需要metallb来实现ip的分配

loadbalancer → nodeport → ClusterIP →pods

示例:

bash 复制代码
[root@master hh]# cat loadbalancer.yml
---
apiVersion: v1
kind: Service
metadata:
  labels:
    app: balancer-service
  name: balancer-service
spec:
  ports:
  - port: 80
    protocol: TCP
    targetPort: 80
  selector:
    app: balancer
  type: LoadBalancer
[root@master hh]#

5.3.5 metalLB

功能:为LoadBalancer分配vip

官网:Installation :: MetalLB, bare metal load-balancer for Kubernetes

部署:

bash 复制代码
1.设置ipvs模式
[root@master service]# kubectl edit cm -n kube-system kube-proxy
apiVersion: kubeproxy.config.k8s.io/v1alpha1
kind: KubeProxyConfiguration
mode: "ipvs"

ipvs:
  strictARP: true #默认就是不需要改

[root@k8s-master ~]# kubectl -n kube-system get  pods   | awk '/kube-proxy/{system("kubectl -n kube-system delete pods "$1)}'

2.下载部署文件
[root@k8s2 metallb]# wget https://raw.githubusercontent.com/metallb/metallb/v0.13.12/config/manifests/metallb-native.yaml

3.修改文件中镜像地址,与harbor仓库路径保持一致

4.上传镜像到harbor

bash 复制代码
[root@k8s-master ~]# docker pull quay.io/metallb/controller:v0.14.8
[root@k8s-master ~]# docker pull quay.io/metallb/speaker:v0.14.8

[root@k8s-master ~]# docker tag quay.io/metallb/speaker:v0.14.8 reg.timinglee.org/metallb/speaker:v0.14.8
[root@k8s-master ~]# docker tag quay.io/metallb/controller:v0.14.8 reg.timinglee.org/metallb/controller:v0.14.8

[root@k8s-master ~]# docker push reg.timinglee.org/metallb/speaker:v0.14.8
[root@k8s-master ~]# docker push reg.timinglee.org/metallb/controller:v0.14.8

部署服务

bash 复制代码
[root@master service]# kubectl apply -f metallb-native.yaml
[root@master service]# kubectl -n metallb-system get pods
NAME                          READY   STATUS             RESTARTS             AGE
controller-65957f77c8-fc5b5   1/1     Running            1 (20h ago)          23h
speaker-8zslv                 1/1     Running            118 (5m38s ago)      22h
speaker-flrjq                 1/1     Running            67 (<invalid> ago)   3h18m
speaker-ws4fh                 1/1     Running            750 (4m38s ago)      5d18h

配置分配地址段

bash 复制代码
[root@master service]# cat configmap.yml
apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
  name: first-pool    #地址池名称
  namespace: metallb-system
spec:
  addresses:
  - 192.168.217.50-192.168.217.99       #修改为自己本地地址段 

---                                     #个不同的kind中间必须加分割
apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
  name: example
  namespace: metallb-system
spec:
  ipAddressPools:
  - first-pool                           #使用地址池 
[root@master service]#
bash 复制代码
[root@k8s-master ~]# kubectl apply -f configmap.yml
ipaddresspool.metallb.io/first-pool created
l2advertisement.metallb.io/example created


[root@k8s-master ~]# kubectl get services
NAME                TYPE           CLUSTER-IP      EXTERNAL-IP     PORT(S)        AGE
kubernetes          ClusterIP      10.96.0.1       <none>          443/TCP        21h
timinglee-service   LoadBalancer   10.109.36.123   192.168.217.50   80:31595/TCP   9m9s


#通过分配地址从集群外访问服务
[root@reg ~]# curl  192.168.217.50
Hello MyApp | Version: v1 | <a href="hostname.html">Pod Name</a>

5.3.6 externalname

  • 开启services后,不会被分配IP,而是用dns解析CNAME固定域名来解决ip变化问题

  • 一般应用于外部业务和pod沟通或外部业务迁移到pod内时

  • 在应用向集群迁移过程中,externalname在过度阶段就可以起作用了。

  • 集群外的资源迁移到集群时,在迁移的过程中ip可能会变化,但是域名+dns解析能完美解决此问题

示例:

bash 复制代码
[root@master hh]# cat externalname.yml
apiVersion: v1
kind: Service
metadata:
  labels:
    app: haha-service
  name: haha-service
spec:
  selector:
    app: haha
  type: ExternalName
  externalName: www.haha.org

[root@master hh]#

[root@master hh]# kubectl get services haha-service
NAME           TYPE           CLUSTER-IP   EXTERNAL-IP    PORT(S)   AGE
haha-service   ExternalName   <none>       www.haha.org   <none>    12s

测试:

六 k8s下的存储管理

6.1 configmap

6.1.1功能:

  • configMap用于保存配置数据,以键值对形式存储。

  • configMap 资源提供了向 Pod 注入配置数据的方法。

  • 镜像和配置文件解耦,以便实现镜像的可移植性和可复用性。

  • etcd限制了文件大小不能超过1M

6.1.2 创建方式:

  1. 字面值创建
bash 复制代码
[root@master ~]# kubectl create cm lee-config --from-literal  fname=timing --from-literal name=lee
configmap/lee-config created

[root@k8s-master ~]# kubectl describe cm lee-config
Name:         lee-config
Namespace:    default
Labels:       <none>
Annotations:  <none>

Data							#键值信息显示
====
fname:
----
timing
lname:
----
lee

BinaryData
====

Events:  <none> 

2.通过文件创建

bash 复制代码
[root@k8s-master ~]# cat /etc/resolv.conf
# Generated by NetworkManager
nameserver 114.114.114.114

[root@k8s-master ~]# kubectl create cm lee2-config --from-file /etc/resolv.conf
configmap/lee2-config created
[root@k8s-master ~]# kubectl describe cm lee2-config
Name:         lee2-config
Namespace:    default
Labels:       <none>
Annotations:  <none>

Data
====
resolv.conf:
----
# Generated by NetworkManager
nameserver 114.114.114.114


BinaryData
====

Events:  <none>
  1. 通过目录创建
bash 复制代码
[root@k8s-master ~]# mkdir leeconfig
[root@k8s-master ~]# cp /etc/fstab /etc/rc.d/rc.local  leeconfig/
[root@k8s-master ~]# kubectl create cm lee3-config --from-file leeconfig/
configmap/lee3-config created
[root@k8s-master ~]# kubectl describe cm lee3-config
Name:         lee3-config
Namespace:    default
Labels:       <none>
Annotations:  <none>

Data
====
fstab:
----

#
# /etc/fstab
# Created by anaconda on Fri Jul 26 13:04:22 2024
#
# 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.
#
# After editing this file, run 'systemctl daemon-reload' to update systemd
# units generated from this file.
#
UUID=6577c44f-9c1c-44f9-af56-6d6b505fcfa8 /                       xfs     defaults        0 0
UUID=eec689b4-73d5-4f47-b999-9a585bb6da1d /boot                   xfs     defaults        0 0
UUID=ED00-0E42          /boot/efi               vfat    umask=0077,shortname=winnt 0 2
#UUID=be2f2006-6072-4c77-83d4-f2ff5e237f9f none                    swap    defaults        0 0

rc.local:
----
#!/bin/bash
# THIS FILE IS ADDED FOR COMPATIBILITY PURPOSES
#
# It is highly advisable to create own systemd services or udev rules
# to run scripts during boot instead of using this file.
#
# In contrast to previous versions due to parallel execution during boot
# this script will NOT be run after all other services.
#
# Please note that you must run 'chmod +x /etc/rc.d/rc.local' to ensure
# that this script will be executed during boot.

touch /var/lock/subsys/local
mount /dev/cdrom /rhel9


BinaryData
====

Events:  <none>

4.通过yaml文件创建

bash 复制代码
[root@master configmap]# kubectl create configmap test --from-literal name=lee --dry-run=client -o yaml > test.yml
[root@master configmap]# cat test.yml
apiVersion: v1
kind: ConfigMap
metadata:
  name: test
data:
  username: hash
  password: "123"
[root@master configmap]#

[root@master configmap]# kubectl apply -f test.yml
configmap/test created
[root@master configmap]# kubectl describe -f test.yml
Name:         test
Namespace:    default
Labels:       <none>
Annotations:  <none>

Data
====
password:
----
123
username:
----
hash

BinaryData
====

Events:  <none>
[root@master configmap]#

6.2 secrets配置管理

6.2.1 功能介绍

  • Secret 对象类型用来保存敏感信息,例如密码、OAuth 令牌和 ssh key。

  • 敏感信息放在 secret 中比放在 Pod 的定义或者容器镜像中来说更加安全和灵活

  • Pod 可以用两种方式使用 secret:

    • 作为 volume 中的文件被挂载到 pod 中的一个或者多个容器里。

    • 当 kubelet 为 pod 拉取镜像时使用。

  • Secret的类型:

    • Service Account:Kubernetes 自动创建包含访问 API 凭据的 secret,并自动修改 pod 以使用此类型的 secret。

    • Opaque:使用base64编码存储信息,可以通过base64 --decode解码获得原始数据,因此安全性弱。

    • kubernetes.io/dockerconfigjson:用于存储docker registry的认证信息

6.2.2 创建

  1. 从文件创建
bash 复制代码
[root@k8s-master secrets]# echo -n timi> username.txt
[root@k8s-master secrets]# echo -n xixi > password.txt
root@k8s-master secrets]# kubectl create secret generic userlist --from-file username.txt --from-file password.txt
secret/userlist created
[root@master secrets]# kubectl get secrets userlist -o yaml
apiVersion: v1
data:
  password: dGltaQ==
  username: eGl4aQ==
kind: Secret
metadata:
  annotations:
    kubectl.kubernetes.io/last-applied-configuration: |
      {"apiVersion":"v1","data":{"password":"dGltaQ==","username":"eGl4aQ=="},"kind":"Secret","metadata":{"annotations":{},"creationTimestamp":null,"name":"userlist","namespace":"default"},"type":"Opaque"}
  creationTimestamp: "2025-08-16T17:06:05Z"
  name: userlist
  namespace: default
  resourceVersion: "178365"
  uid: 2432fd18-e1fe-4eb2-86e9-a4462126d96d
type: Opaque

编写yaml文件

bash 复制代码
[root@master secrets]# echo -n timi | base64
dGltaQ==
[root@master secrets]# echo -n xixi | base64
eGl4aQ==
[root@master secrets]#

[root@master secrets]# cat userlist.yml
apiVersion: v1
kind: Secret
metadata:
  creationTimestamp: null
  name: userlist
type: Opaque
data:
  username: eGl4aQ==
  password: dGltaQ==
[root@master secrets]#

[root@master secrets]# kubectl describe secrets userlist
Name:         userlist
Namespace:    default
Labels:       <none>
Annotations:  <none>

Type:  Opaque

Data
====
username:  4 bytes
password:  4 bytes
[root@master secrets]#

6.2.3 Secret的使用方法

6.2.3.1.将Secret挂载到Volume中
bash 复制代码
[root@master secrets]# cat nginx-sc.yml
apiVersion: v1
kind: Pod
metadata:
  labels:
    run: ngix-sc
  name: ngix-sc
spec:
  containers:
  - image: nginx
    name: ngix-sc
    volumeMounts:
    - name: secrets
      mountPath: /secret
      readOnly: true
  volumes:
  - name: secrets
    secret:
      secretName: userlist
[root@master secrets]# kubectl apply -f nginx-sc.yml
pod/ngix-sc created
[root@master secrets]# kubectl exec  pods/ngix-sc -it -- /bin/bash
root@ngix-sc:/# cd /secret/
root@ngix-sc:/secret# ls
password  username
root@ngix-sc:/secret#
6.2.3.2.向指定路径映射 secret 密钥
bash 复制代码
#向指定路径映射
[root@k8s-master secrets]# vim pod2.yaml
apiVersion: v1
kind: Pod
metadata:
  labels:
    run: nginx1
  name: nginx1
spec:
  containers:
  - image: nginx
    name: nginx1
    volumeMounts:
    - name: secrets
      mountPath: /secret
      readOnly: true

  volumes:
  - name: secrets
    secret:
      secretName: userlist
      items:
      - key: username
        path: my-users/username

[root@k8s-master secrets]# kubectl apply -f pod2.yaml
pod/nginx1 created
[root@k8s-master secrets]# kubectl exec  pods/nginx1 -it -- /bin/bash
root@nginx1:/# cd secret/
root@nginx1:/secret# ls
my-users
root@nginx1:/secret# cd my-users
root@nginx1:/secret/my-users# ls
username
root@nginx1:/secret/my-users# cat username 
6.2.3.3.将Secret设置为环境变量
bash 复制代码
[root@k8s-master secrets]# vim pod3.yaml
apiVersion: v1
kind: Pod
metadata:
  labels:
    run: busybox
  name: busybox
spec:
  containers:
  - image: busybox
    name: busybox
    command:
    - /bin/sh
    - -c
    - env
    env:
    - name: USERNAME
      valueFrom:
        secretKeyRef:
          name: userlist
          key: username
    - name: PASS
      valueFrom:
        secretKeyRef:
          name: userlist
          key: password
  restartPolicy: Never
bash 复制代码
[root@k8s-master secrets]# kubectl apply -f pod3.yaml
pod/busybox created
[root@k8s-master secrets]# kubectl logs pods/busybox
KUBERNETES_SERVICE_PORT=443
KUBERNETES_PORT=tcp://10.96.0.1:443
HOSTNAME=busybox
MYAPP_V1_SERVICE_HOST=10.104.84.65
MYAPP_V2_SERVICE_HOST=10.105.246.219
SHLVL=1
HOME=/root
MYAPP_V1_SERVICE_PORT=80
MYAPP_V1_PORT=tcp://10.104.84.65:80
MYAPP_V2_SERVICE_PORT=80
MYAPP_V2_PORT=tcp://10.105.246.219:80
MYAPP_V1_PORT_80_TCP_ADDR=10.104.84.65
USERNAME=timinglee
MYAPP_V2_PORT_80_TCP_ADDR=10.105.246.219
KUBERNETES_PORT_443_TCP_ADDR=10.96.0.1
。。。
6.2.3.4.存储docker registry的认证信息

建立私有仓库并上传镜像

bash 复制代码
#建立用于docker认证的secret

[root@k8s-master secrets]# kubectl create secret docker-registry docker-auth --docker-server reg.timinglee.org --docker-username admin --docker-password lee --docker-email timinglee@timinglee.org
secret/docker-auth created
bash 复制代码
[root@master secrets]# cat myapp-sc.yml
apiVersion: v1
kind: Pod
metadata:
  labels:
    run: myapp-sc
  name: myapp-sc
spec:
  containers:
  - image: reg.timinglee.org/test-sc/myapp:v1
    name: myapp-sc
    imagePullSecrets:    #不设定无法下载
    - name: docker-auth
[root@master secrets]#

未设置时

设置后

6.3volumes配置管理

  • 容器中文件在磁盘上是临时存放的,这给容器中运行的特殊应用程序带来一些问题

  • 当容器崩溃时,kubelet将重新启动容器,容器中的文件将会丢失,因为容器会以干净的状态重建。

  • 当在一个 Pod 中同时运行多个容器时,常常需要在这些容器之间共享文件。

  • Kubernetes 卷具有明确的生命周期与使用它的 Pod 相同

  • 卷比 Pod 中运行的任何容器的存活期都长,在容器重新启动时数据也会得到保留

  • 当一个 Pod 不再存在时,卷也将不再存在。

  • Kubernetes 可以支持许多类型的卷,Pod 也能同时使用任意数量的卷。

  • 卷不能挂载到其他卷,也不能与其他卷有硬链接。 Pod 中的每个容器必须独立地指定每个卷的挂载位置。

6.3.1 emptyDir卷

bash 复制代码
[root@master volumes]# cat vo-pod.yml
apiVersion: v1
kind: Pod
metadata:
  labels:
    run: vo-pod
  name: vo-pod
spec:
  containers:
  - image: nginx
    name: nginx
    volumeMounts:
    - mountPath: /usr/share/nginx/html
      name: vo

  - image: busyboxplus
    name: busybox
    command:
    - /bin/sh
    - -c
    - sleep 100000
    volumeMounts:
    - mountPath: /cache
      name: vo

  volumes:
  - name: vo
    emptyDir:
      medium: Memory
      sizeLimit: 100Mi
[root@master volumes]#

[root@master volumes]# kubectl get pods
NAME     READY   STATUS    RESTARTS   AGE
vo-pod   2/2     Running   0          6m16s
[root@master volumes]#

[root@master volumes]# kubectl exec -it pods/vo-pod -c nginx -- /bin/bash
root@vo-pod:/# ls
bin   dev   docker-entrypoint.sh  home  lib64  mnt  proc  run   srv  tmp  var
root@vo-pod:/# cd /usr/share/nginx/html/
root@vo-pod:/usr/share/nginx/html# ls
index.html

[root@master ~]# kubectl exec -it pods/vo-pod -c busybox -- /bin/sh
/ # cd cache/
/cache # ls
/cache # echo hello > index.html
/cache # ls
index.html
/cache # curl localhost
hello
/cache #

6.3.2 hostpath卷

bash 复制代码
[root@master volumes]# cat host-pod.yml
apiVersion: v1
kind: Pod
metadata:
  labels:
    run: host-pod
  name: host-pod
spec:
  containers:
  - image: busyboxplus
    name: busybox
    command:
    - /bin/sh
    - -c
    - sleep 100000
    volumeMounts:
    - mountPath: /cache
      name: vo

  volumes:
  - name: vo
    hostPath:
      path: /data
      type:  DirectoryOrCreate  #当目录不存在时自动创建,有则不管
[root@master volumes]#

测试:
[root@master volumes]# kubectl get pods -o wide
NAME       READY   STATUS    RESTARTS   AGE   IP           NODE    NOMINATED NODE   READINESS GATES
host-pod   1/1     Running   0          15s   10.244.2.7   node2   <none>           <none>

[root@master volumes]# kubectl exec  -it pods/host-pod -- /bin/sh
/ # ls
bin      dev      home     lib64    media    opt      root     sbin     tmp      var
cache    etc      lib      linuxrc  mnt      proc     run      sys      usr
/
在node2上的根目录上自动创建了一个data目录
[root@node2 ~]# ls /
afs  boot  dev  home  lib64  mnt  proc     root  sbin  sys  usr
bin  data  etc  lib   media  opt  rhel9.2  run   srv   tmp  var
起初目录是空的
[root@node2 ~]# cd /data/
[root@node2 data]# ls
[root@node2 data]# touch hh{1..10}
[root@node2 data]# ls
hh1  hh10  hh2  hh3  hh4  hh5  hh6  hh7  hh8  hh9
[root@node2 data]#

[root@master volumes]# kubectl exec  -it pods/host-pod -- /bin/sh
/ # ls
bin      dev      home     lib64    media    opt      root     sbin     tmp      var
cache    etc      lib      linuxrc  mnt      proc     run      sys      usr
/ # cd cache/
/cache # ls   #空的
/cache # ls   #node2上创建文件后
hh1   hh10  hh2   hh3   hh4   hh5   hh6   hh7   hh8   hh9
#当容器停止了node2上的数据仍然存在
[root@master volumes]# kubectl delete -f host-pod.yml --force
Warning: Immediate deletion does not wait for confirmation that the running resource has been terminated. The resource may continue to run on the cluster indefinitely.
pod "host-pod" force deleted
[root@master volumes]# kubectl get pods
No resources found in default namespace.
[root@master volumes]#
#文件仍然存在
[root@node2 data]# ls
hh1  hh10  hh2  hh3  hh4  hh5  hh6  hh7  hh8  hh9
[root@node2 data]#

6.3.3nfs 卷

数据持久化存储和数据互通

bash 复制代码
#部署nfs主机
[root@reg ~]# dnf install nfs-utils -y
[root@reg ~]# systemctl enable --now nfs-server.service

[root@reg ~]# vim /etc/exports
/nfsdata   *(rw,sync,no_root_squash)

[root@reg ~]# exportfs -rv
exporting *:/nfsdata

[root@reg ~]# showmount  -e
Export list for reg.timinglee.org:
/nfsdata *

#在k8s所有节点中安装nfs-utils
[root@k8s-master & node1 & node2  ~]# dnf install nfs-utils -y
bash 复制代码
[root@master volumes]# cat 1-nfs.yml && 2-nfs.yml
apiVersion: v1
kind: Pod
metadata:
  labels:
    run: nfs1  ##nfs2
  name: nfs1   ##nfs2
spec:
  containers:
  - image: nginx
    name: nginx
    volumeMounts:
    - mountPath: /usr/share/nginx/html
      name: vo

  volumes:
  - name: vo
    nfs:
      server: 192.168.217.200
      path: /nfs_data
      
[root@master volumes]# kubectl get pod -o wide
NAME   READY   STATUS    RESTARTS   AGE   IP           NODE    NOMINATED NODE   READINESS GATES
nfs1   1/1     Running   0          21s   10.244.2.8   node2   <none>           <none>
nfs2   1/1     Running   0          14s   10.244.2.9   node2   <none>           <none>
[root@master volumes]# curl 10.244.2.8
<html>
<head><title>404 Not Found</title></head>
<body>
<center><h1>404 Not Found</h1></center>
<hr><center>nginx/1.27.1</center>
</body>
</html>
[root@master volumes]# kubectl exec -it pods/nfs1 -- /bin/bash
root@nfs1:/# ls
bin   dev                  docker-entrypoint.sh  home  lib64  mnt  proc  run   srv  tmp  var
boot  docker-entrypoint.d  etc                   lib   media  opt  root  sbin  sys  usr
root@nfs1:/# ls /
bin   dev                  docker-entrypoint.sh  home  lib64  mnt  proc  run   srv  tmp  var
boot  docker-entrypoint.d  etc                   lib   media  opt  root  sbin  sys  usr
root@nfs1:/# cd /usr/share/nginx/html/
root@nfs1:/usr/share/nginx/html# ls  #空的
#写入数据
[root@reg ~]# cd /nfs_data/
[root@reg nfs_data]# ls
[root@reg nfs_data]# echo hello nfs > index.html
[root@reg nfs_data]# cat index.html
hello nfs
#一定要进去加载一下,否则不会挂载
root@nfs1:/usr/share/nginx/html# ls    #同步
index.html
#再次访问:
[root@master volumes]# curl 10.244.2.8
hello nfs
bash 复制代码
[root@master volumes]# showmount -e 192.168.217.200
Export list for 192.168.217.200:
/nfs_data *  #表示 /nfs_data目录被导出,允许所有客户端(* 表示任意主机)访问。

#一个用于查询 NFS(Network File System)服务器导出共享目录的命令。这个命令会列出 IP 地址为 192.168.217.200 的 NFS 服务器上所有可供客户端挂载的共享目录。

showmount:用于显示 NFS 服务器上的挂载信息。
-e:表示 "export",即显示 NFS 服务器导出的共享目录列表。
192.168.217.200:目标 NFS 服务器的 IP 地址。

6.3.4 PersistentVolume持久卷

PersistentVolume(持久卷,简称PV)

  • pv是集群内由管理员提供的网络存储的一部分。

  • PV也是集群中的一种资源。是一种volume插件,

  • 但是它的生命周期却是和使用它的Pod相互独立的。

  • PV这个API对象,捕获了诸如NFS、ISCSI、或其他云存储系统的实现细节

  • pv有两种提供方式:静态和动态

    • 静态PV:集群管理员创建多个PV,它们携带着真实存储的详细信息,它们存在于Kubernetes API中,并可用于存储使用

    • 动态PV:当管理员创建的静态PV都不匹配用户的PVC时,集群可能会尝试专门地供给volume给PVC。这种供给基于StorageClass

PersistentVolumeClaim(持久卷声明,简称PVC)

  • 是用户的一种存储请求

  • 它和Pod类似,Pod消耗Node资源,而PVC消耗PV资源

  • Pod能够请求特定的资源(如CPU和内存)。PVC能够请求指定的大小和访问的模式持久卷配置

  • PVC与PV的绑定是一对一的映射。没找到匹配的PV,那么PVC会无限期得处于unbound未绑定状态

静态pv实例:

bash 复制代码
#在nfs主机中建立实验目录
[root@reg ~]# mkdir  /nfsdata/pv{1..3}

#编写创建pv的yml文件,pv是集群资源,不在任何namespace中
[root@k8s-master pvc]# vim pv.yml
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv1
spec:
  capacity:
    storage: 5Gi
  volumeMode: Filesystem
  accessModes:
  - ReadWriteOnce
  persistentVolumeReclaimPolicy: Retain
  storageClassName: nfs
  nfs:
    path: /nfsdata/pv1
    server: 172.25.254.250

---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv2
spec:
  capacity:
    storage: 15Gi
  volumeMode: Filesystem
  accessModes:
  - ReadWriteMany
  persistentVolumeReclaimPolicy: Retain
  storageClassName: nfs
  nfs:
    path: /nfsdata/pv2
    server: 172.25.254.250
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv3
spec:
  capacity:
    storage: 25Gi
  volumeMode: Filesystem
  accessModes:
  - ReadOnlyMany
  persistentVolumeReclaimPolicy: Retain
  storageClassName: nfs
  nfs:
    path: /nfsdata/pv3
    server: 172.25.254.250

[root@k8s-master pvc]# kubectl get  pv
NAME   CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM   STORAGECLASS   VOLUMEATTRIBUTESCLASS   REASON   AGE
pv1    5Gi        RWO            Retain           Available           nfs            <unset>                          4m50s
pv2    15Gi       RWX            Retain           Available           nfs            <unset>                          4m50s
pv3    25Gi       ROX            Retain           Available           nfs            <unset>                          4m50s

#建立pvc,pvc是pv使用的申请,需要保证和pod在一个namesapce中
[root@k8s-master pvc]# vim pvc.ym
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pvc1
spec:
  storageClassName: nfs
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi

---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pvc2
spec:
  storageClassName: nfs
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: 10Gi

---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pvc3
spec:
  storageClassName: nfs
  accessModes:
    - ReadOnlyMany
  resources:
    requests:
      storage: 15Gi
[root@k8s-master pvc]# kubectl get pvc
NAME   STATUS   VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS   VOLUMEATTRIBUTESCLASS   AGE
pvc1   Bound    pv1      5Gi        RWO            nfs            <unset>                 5s
pvc2   Bound    pv2      15Gi       RWX            nfs            <unset>                 4s
pvc3   Bound    pv3      25Gi       ROX            nfs            <unset>                 4s

#在其他namespace中无法应用
[root@k8s-master pvc]# kubectl -n kube-system  get pvc
No resources found in kube-system namespace.

在pod中使用pvc

bash 复制代码
[root@k8s-master pvc]# vim pod.yml
apiVersion: v1
kind: Pod
metadata:
  name: timinglee
spec:
  containers:
  - image: nginx
    name: nginx
    volumeMounts:
    - mountPath: /usr/share/nginx/html
      name: vol1
  volumes:
  - name: vol1
    persistentVolumeClaim:
      claimName: pvc1

[root@k8s-master pvc]# kubectl get pods  -o wide
NAME        READY   STATUS    RESTARTS   AGE   IP            NODE        NOMINATED NODE   READINESS GATES
timinglee   1/1     Running   0          83s   10.244.2.54   k8s-node2   <none>           <none>
[root@k8s-master pvc]# kubectl exec -it pods/timinglee -- /bin/bash
root@timinglee:/# curl  localhost
<html>
<head><title>403 Forbidden</title></head>
<body>
<center><h1>403 Forbidden</h1></center>
<hr><center>nginx/1.27.1</center>
</body>
</html>
root@timinglee:/# cd /usr/share/nginx/
root@timinglee:/usr/share/nginx# ls
html
root@timinglee:/usr/share/nginx# cd html/
root@timinglee:/usr/share/nginx/html# ls

[root@reg ~]# echo timinglee > /data/pv1/index.html

[root@k8s-master pvc]# kubectl exec -it pods/timinglee -- /bin/bash
root@timinglee:/# cd /usr/share/nginx/html/
root@timinglee:/usr/share/nginx/html# ls
index.html	

[root@reg pv1]# echo hello pv > index.html
[root@reg pv1]# ls
index.html
[root@reg pv1]#

[root@master volumes]# kubectl exec -it pods/pod -- /bin/bash
root@pod:/# cd /usr/share/nginx/html/
root@pod:/usr/share/nginx/html# ls
index.html
root@pod:/usr/share/nginx/html# curl localhost
hello pv
root@pod:/usr/share/nginx/html# exit

6.4 存储类storageclass

6.4.1 部署NFS Client Provisioner

1.创建sa并授权
bash 复制代码
[root@master volumes]# cat rbac.yml
apiVersion: v1
kind: Namespace
metadata:
  name: nfs-client-provisioner
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: nfs-client-provisioner
  namespace: nfs-client-provisioner
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: nfs-client-provisioner-runner
rules:
  - apiGroups: [""]
    resources: ["nodes"]
    verbs: ["get", "list", "watch"]
  - apiGroups: [""]
    resources: ["persistentvolumes"]
    verbs: ["get", "list", "watch", "create", "delete"]
  - apiGroups: [""]
    resources: ["persistentvolumeclaims"]
    verbs: ["get", "list", "watch", "update"]
  - apiGroups: ["storage.k8s.io"]
    resources: ["storageclasses"]
    verbs: ["get", "list", "watch"]
  - apiGroups: [""]
    resources: ["events"]
    verbs: ["create", "update", "patch"]
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: run-nfs-client-provisioner
subjects:
  - kind: ServiceAccount
    name: nfs-client-provisioner
    namespace: nfs-client-provisioner
roleRef:
  kind: ClusterRole
  name: nfs-client-provisioner-runner
  apiGroup: rbac.authorization.k8s.io
---
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: leader-locking-nfs-client-provisioner
  namespace: nfs-client-provisioner
rules:
  - apiGroups: [""]
    resources: ["endpoints"]
    verbs: ["get", "list", "watch", "create", "update", "patch"]
---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: leader-locking-nfs-client-provisioner
  namespace: nfs-client-provisioner
subjects:
  - kind: ServiceAccount
    name: nfs-client-provisioner
    namespace: nfs-client-provisioner
roleRef:
  kind: Role
  name: leader-locking-nfs-client-provisioner
  apiGroup: rbac.authorization.k8s.io
[root@master volumes]#
查看rbac信息
[root@master volumes]# kubectl -n nfs-client-provisioner get sa
NAME                     SECRETS   AGE
default                  0         15m
nfs-client-provisioner   0         15m
[root@master volumes]#
2.部署应用
bash 复制代码
[root@master volumes]# cat deployment.yml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nfs-client-provisioner
  labels:
    app: nfs-client-provisioner
  namespace: nfs-client-provisioner
spec:
  replicas: 1
  strategy:
    type: Recreate
  selector:
    matchLabels:
      app: nfs-client-provisioner
  template:
    metadata:
      labels:
        app: nfs-client-provisioner
    spec:
      serviceAccountName: nfs-client-provisioner
      containers:
        - name: nfs-client-provisioner
          image: nfs-subdir-external-provisioner:v4.0.2
          volumeMounts:
            - name: nfs-client-root
              mountPath: /persistentvolumes
          env:
            - name: PROVISIONER_NAME
              value: k8s-sigs.io/nfs-subdir-external-provisioner
            - name: NFS_SERVER
              value: 192.168.217.200
            - name: NFS_PATH
              value: /nfs_data
      volumes:
        - name: nfs-client-root
          nfs:
            server: 192.168.217.200
            path: /nfs_data

[root@master volumes]#

[root@master volumes]# kubectl -n nfs-client-provisioner get deployments.apps nfs-client-provisioner
NAME                     READY   UP-TO-DATE   AVAILABLE   AGE
nfs-client-provisioner   1/1     1            1           11m
[root@master volumes]#
3.创建存储类
bash 复制代码
[root@master volumes]# cat class.yml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: nfs-client
provisioner: k8s-sigs.io/nfs-subdir-external-provisioner
parameters:
  archiveOnDelete: "false"
  

[root@master volumes]# kubectl apply -f class.yml
storageclass.storage.k8s.io/nfs-client created
[root@master volumes]# kubectl get storageclasses.storage.k8s.io
NAME         PROVISIONER                                   RECLAIMPOLICY   VOLUMEBINDINGMODE   ALLOWVOLUMEEXPANSION   AGE
nfs-client   k8s-sigs.io/nfs-subdir-external-provisioner   Delete          Immediate           false                  19s

[root@master volumes]# cat class_pvc.yml
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: test-claim
spec:
  storageClassName: nfs-client
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: 1G
[root@master volumes]#
[root@master volumes]# kubectl apply -f class_pvc.yml
persistentvolumeclaim/test-claim created
[root@master volumes]# kubectl get pvc
NAME         STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   VOLUMEATTRIBUTESCLASS   AGE
test-claim   Bound    pvc-615dae92-dff6-426a-8934-8c216140352b   1G         RWX            nfs-client     <unset>                 11s
[root@master volumes]#
5. 创建pvc
bash 复制代码
[root@master storage]# cat class-pvc.yml
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: test-claim
spec:
  storageClassName: nfs-client
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: 1G
[root@master storage]#
[root@master storage]# kubectl apply -f class-pvc.yml
persistentvolumeclaim/test-claim created
5.创建测试pod
bash 复制代码
[root@k8s-master storageclass]# vim pod.yml
kind: Pod
apiVersion: v1
metadata:
  name: test-pod
spec:
  containers:
  - name: test-pod
    image: busybox
    command:
      - "/bin/sh"
    args:
      - "-c"
      - "touch /mnt/SUCCESS && exit 0 || exit 1"
    volumeMounts:
      - name: nfs-pvc
        mountPath: "/mnt"
  restartPolicy: "Never"
  volumes:
    - name: nfs-pvc
      persistentVolumeClaim:
        claimName: test-claim

[root@k8s-master storageclass]# kubectl apply -f pod.yml

[root@reg ~]# ls /data/default-test-claim-pvc-b1aef9cc-4be9-4d2a-8c5e-0fe7716247e2/
SUCCESS
6.设置默认存储类
  • 在未设定默认存储类时pvc必须指定使用类的名称

  • 在设定存储类后创建pvc时可以不用指定storageClassName

bash 复制代码
#一次性指定多个pvc
[root@k8s-master pvc]# vim pvc.yml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pvc1
spec:
  storageClassName: nfs-client
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi

---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pvc2
spec:
  storageClassName: nfs-client
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: 10Gi

---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pvc3
spec:
  storageClassName: nfs-client
  accessModes:
    - ReadOnlyMany
  resources:
    requests:
      storage: 15Gi

设定默认存储类

bash 复制代码
[root@k8s-master storageclass]# kubectl edit sc nfs-client
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  annotations:
    kubectl.kubernetes.io/last-applied-configuration: |
      {"apiVersion":"storage.k8s.io/v1","kind":"StorageClass","metadata":{"annotations":{},"name":"nfs-client"},"parameters":{"archiveOnDelete":"false"},"provisioner":"k8s-sigs.io/nfs-subdir-external-provisioner"}
    storageclass.kubernetes.io/is-default-class: "true"		#设定默认存储类
  creationTimestamp: "2024-09-07T13:49:10Z"
  name: nfs-client
  resourceVersion: "218198"
  uid: 9eb1e144-3051-4f16-bdec-30c472358028
parameters:
  archiveOnDelete: "false"
provisioner: k8s-sigs.io/nfs-subdir-external-provisioner
reclaimPolicy: Delete
volumeBindingMode: Immediate

#测试,未指定storageClassName参数
[root@k8s-master storageclass]# vim pvc.yml
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: test-claim
spec:
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: 1Gi

[root@k8s-master storageclass]# kubectl apply -f pvc.yml
persistentvolumeclaim/test-claim created
[root@k8s-master storageclass]# kubectl get pvc
NAME         STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   VOLUMEATTRIBUTESCLASS   AGE
test-claim   Bound    pvc-b96c6983-5a4f-440d-99ec-45c99637f9b5   1Gi        RWX            nfs-client     <unset>                 7s

6.5statefulset控制器

6.5.1 功能

  • Statefulset是为了管理有状态服务的问题设计的

  • StatefulSet将应用状态抽象成了两种情况:

  • 拓扑状态:应用实例必须按照某种顺序启动。新创建的Pod必须和原来Pod的网络标识一样

  • 存储状态:应用的多个实例分别绑定了不同存储数据。

  • StatefulSet给所有的Pod进行了编号,编号规则是:(statefulset名称)-(序号),从0开始。

  • Pod被删除后重建,重建Pod的网络标识也不会改变,Pod的拓扑状态按照Pod的"名字+编号"的方式固定下来,并且为每个Pod提供了一个固定且唯一的访问入口,Pod对应的DNS记录。

6.5.2 构建方法

bash 复制代码
#建立无头服务
kubectl create service clusterip my-headless-svc \
  --clusterip="None" \
  --tcp=80:8080 \
  --dry-run=client -o yaml > headless-service.yaml
[root@k8s-master statefulset]# vim headless.yml
apiVersion: v1
kind: Service
metadata:
 name: nginx-svc
 labels:
  app: nginx
spec:
 ports:
 - port: 80
   name: web
 clusterIP: None
 selector:
  app: nginx
[root@k8s-master statefulset]# kubectl apply -f headless.yml


#建立statefulset
[root@k8s-master statefulset]# vim statefulset.yml
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: web
spec:
  serviceName: "nginx-svc"
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx
        volumeMounts:
        - name: www
          mountPath: /usr/share/nginx/html

  volumeClaimTemplates:
  - metadata:
      name: www
    spec:
      storageClassName: nfs-client
      accessModes:
      - ReadWriteOnce
      resources:
        requests:
          storage: 1Gi
[root@k8s-master statefulset]# kubectl apply -f statefulset.yml
statefulset.apps/web configured
root@k8s-master statefulset]# kubectl get pods
NAME    READY   STATUS    RESTARTS   AGE
web-0   1/1     Running   0          3m26s
web-1   1/1     Running   0          3m22s
web-2   1/1     Running   0          3m18s


[root@reg nfsdata]# ls /nfsdata/
default-test-claim-pvc-34b3d968-6c2b-42f9-bbc3-d7a7a02dcbac
default-www-web-0-pvc-0390b736-477b-4263-9373-a53d20cc8f9f
default-www-web-1-pvc-a5ff1a7b-fea5-4e77-afd4-cdccedbc278c
default-www-web-2-pvc-83eff88b-4ae1-4a8a-b042-8899677ae854

七 k8s中网络通信与调度

7.1 通信架构

  • k8s通过CNI接口接入其他插件来实现网络通讯。目前比较流行的插件有flannel,calico等

  • CNI插件存放位置:# cat /etc/cni/net.d/10-flannel.conflist

  • 插件使用的解决方案如下

    • 虚拟网桥,虚拟网卡,多个容器共用一个虚拟网卡进行通信。

    • 多路复用:MacVLAN,多个容器共用一个物理网卡进行通信。

    • 硬件交换:SR-LOV,一个物理网卡可以虚拟出多个接口,这个性能最好。

  • 容器间通信:

    • 同一个pod内的多个容器间的通信,通过lo即可实现pod之间的通信

    • 同一节点的pod之间通过cni网桥转发数据包。

    • 不同节点的pod之间的通信需要网络插件支持

  • pod和service通信: 通过iptables或ipvs实现通信,ipvs取代不了iptables,因为ipvs只能做负载均衡,而做不了nat转换

  • pod和外网通信:iptables的MASQUERADE

  • Service与集群外部客户端的通信;(ingress、nodeport、loadbalancer)

flannel网络插件

插件 功能
VXLAN 即Virtual Extensible LAN(虚拟可扩展局域网),是Linux本身支持的一网种网络虚拟化技术。VXLAN可以完全在内核态实现封装和解封装工作,从而通过"隧道"机制,构建出覆盖网络(Overlay Network)
VTEP VXLAN Tunnel End Point(虚拟隧道端点),在Flannel中 VNI的默认值是1,这也是为什么宿主机的VTEP设备都叫flannel.1的原因
Cni0 网桥设备,每创建一个pod都会创建一对 veth pair。其中一端是pod中的eth0,另一端是Cni0网桥中的端口(网卡)
Flannel.1 TUN设备(虚拟网卡),用来进行 vxlan 报文的处理(封包和解包)。不同node之间的pod数据流量都从overlay设备以隧道的形式发送到对端
Flanneld flannel在每个主机中运行flanneld作为agent,它会为所在主机从集群的网络地址空间中,获取一个小的网段subnet,本主机内所有容器的IP地址都将从中分配。同时Flanneld监听K8s集群数据库,为flannel.1设备提供封装数据时必要的mac、ip等网络数据信息

原理

  • 当容器发送IP包,通过veth pair 发往cni网桥,再路由到本机的flannel.1设备进行处理。

  • VTEP设备之间通过二层数据帧进行通信,源VTEP设备收到原始IP包后,在上面加上一个目的MAC地址,封装成一个内部数据帧,发送给目的VTEP设备。

  • 内部数据桢,并不能在宿主机的二层网络传输,Linux内核还需要把它进一步封装成为宿主机的一个普通的数据帧,承载着内部数据帧通过宿主机的eth0进行传输。

  • Linux会在内部数据帧前面,加上一个VXLAN头,VXLAN头里有一个重要的标志叫VNI,它是VTEP识别某个数据桢是不是应该归自己处理的重要标识。

  • flannel.1设备只知道另一端flannel.1设备的MAC地址,却不知道对应的宿主机地址是什么。在linux内核里面,网络设备进行转发的依据,来自FDB的转发数据库,这个flannel.1网桥对应的FDB信息,是由flanneld进程维护的。

  • linux内核在IP包前面再加上二层数据帧头,把目标节点的MAC地址填进去,MAC地址从宿主机的ARP表获取。

  • 此时flannel.1设备就可以把这个数据帧从eth0发出去,再经过宿主机网络来到目标节点的eth0设备。目标主机内核网络栈会发现这个数据帧有VXLAN Header,并且VNI为1,Linux内核会对它进行拆包,拿到内部数据帧,根据VNI的值,交给本机flannel.1设备处理,flannel.1拆包,根据路由表发往cni网桥,最后到达目标容器。

calico网络插件

  • 纯三层的转发,中间没有任何的NAT和overlay,转发效率最好。

  • Calico 仅依赖三层路由可达。Calico 较少的依赖性使它能适配所有 VM、Container、白盒或者混合环境场景。

  • Felix:监听ECTD中心的存储获取事件,用户创建pod后,Felix负责将其网卡、IP、MAC都设置好,然后在内核的路由表里面写一条,注明这个IP应该到这张网卡。同样如果用户制定了隔离策略,Felix同样会将该策略创建到ACL中,以实现隔离。

  • BIRD:一个标准的路由程序,它会从内核里面获取哪一些IP的路由发生了变化,然后通过标准BGP的路由协议扩散到整个其他的宿主机上,让外界都知道这个IP在这里,路由的时候到这里

部署:
bash 复制代码
删除flannel插件
[root@k8s-master ~]# kubectl delete  -f kube-flannel.yml


删除所有节点上flannel配置文件,避免冲突
[root@k8s-master & node1-2 ~]# rm -rf /etc/cni/net.d/10-flannel.conflist


下载部署文件
[root@k8s-master calico]# curl https://raw.githubusercontent.com/projectcalico/calico/v3.28.1/manifests/calico-typha.yaml -o calico.yaml

下载镜像上传至仓库:
[root@k8s-master ~]# docker pull docker.io/calico/cni:v3.28.1
[root@k8s-master ~]# docker pull docker.io/calico/node:v3.28.1
[root@k8s-master ~]# docker pull docker.io/calico/kube-controllers:v3.28.1
[root@k8s-master ~]# docker pull docker.io/calico/typha:v3.28.1

更改yml设置
[root@k8s-master calico]# vim calico.yaml
4835           image: calico/cni:v3.28.1
4835           image: calico/cni:v3.28.1
4906           image: calico/node:v3.28.1
4932           image: calico/node:v3.28.1
5160           image: calico/kube-controllers:v3.28.1
5249         - image: calico/typha:v3.28.1

4970             - name: CALICO_IPV4POOL_IPIP
4971               value: "Never"

4999             - name: CALICO_IPV4POOL_CIDR
5000               value: "10.244.0.0/16"
5001             - name: CALICO_AUTODETECTION_METHOD
5002               value: "interface=eth0"

[root@k8s-master calico]# kubectl apply -f calico.yaml
[root@k8s-master calico]# kubectl -n kube-system get pods
NAME                                       READY   STATUS    RESTARTS       AGE
calico-kube-controllers-6849cb478c-g5h5p   1/1     Running   0              75s
calico-node-dzzjp                          1/1     Running   0              75s
calico-node-ltz7n                          1/1     Running   0              75s
calico-node-wzdnq                          1/1     Running   0              75s
calico-typha-fff9df85f-vm5ks               1/1     Running   0              75s
coredns-647dc95897-nchjr                   1/1     Running   1 (139m ago)   4d7h
coredns-647dc95897-wjbg2                   1/1     Running   1 (139m ago)   4d7h
etcd-k8s-master                            1/1     Running   1 (139m ago)   4d7h
kube-apiserver-k8s-master                  1/1     Running   1 (139m ago)   3d10h
kube-controller-manager-k8s-master         1/1     Running   3 (139m ago)   4d7h
kube-proxy-9g5z2                           1/1     Running   1 (139m ago)   3d10h
kube-proxy-cd5wk                           1/1     Running   1 (139m ago)   3d10h
kube-proxy-mvq4c                           1/1     Running   1 (139m ago)   3d10h
kube-scheduler-k8s-master                  1/1     Running   3 (139m ago)   4d7h



测试:
[root@k8s-master calico]# kubectl run  web --image myapp:v1
pod/web created
[root@k8s-master calico]# kubectl get pods  -o wide
NAME   READY   STATUS    RESTARTS   AGE   IP               NODE        NOMINATED NODE   READINESS GATES
web    1/1     Running   0          5s    10.244.169.129   k8s-node2   <none>           <none>
[root@k8s-master calico]# curl  10.244.169.129
Hello MyApp | Version: v1 | <a href="hostname.html">Pod Name</a>

7.2 k8s调度器

7.2.1 作用:

  • 调度是指将未调度的Pod自动分配到集群中的节点的过程

  • 调度器通过 kubernetes 的 watch 机制来发现集群中新创建且尚未被调度到 Node 上的 Pod

  • 调度器会将发现的每一个未调度的 Pod 调度到一个合适的 Node 上来运行

7.2.2 原理:

  • 创建Pod

    • 用户通过Kubernetes API创建Pod对象,并在其中指定Pod的资源需求、容器镜像等信息。
  • 调度器监视Pod

    • Kubernetes调度器监视集群中的未调度Pod对象,并为其选择最佳的节点。
  • 选择节点

    • 调度器通过算法选择最佳的节点,并将Pod绑定到该节点上。调度器选择节点的依据包括节点的资源使用情况、Pod的资源需求、亲和性和反亲和性等。
  • 绑定Pod到节点

    • 调度器将Pod和节点之间的绑定信息保存在etcd数据库中,以便节点可以获取Pod的调度信息。
  • 节点启动Pod

    • 节点定期检查etcd数据库中的Pod调度信息,并启动相应的Pod。如果节点故障或资源不足,调度器会重新调度Pod,并将其绑定到其他节点上运行。

7.2.3 方法

1.nodename

  • nodeName 是节点选择约束的最简单方法,但一般不推荐

  • 如果 nodeName 在 PodSpec 中指定了,则它优先于其他的节点选择方法

  • 使用 nodeName 来选择节点的一些限制

    • 如果指定的节点不存在。

    • 如果指定的节点没有资源来容纳 pod,则pod 调度失败。

    • 云环境中的节点名称并非总是可预测或稳定的

bash 复制代码
#建立pod文件
[[root@k8s-master scheduler]# kubectl run  testpod  --image myapp:v1 --dry-run=client -o yaml > pod1.yml

#设置调度
[root@k8s-master scheduler]# vim pod1.yml
apiVersion: v1
kind: Pod
metadata:
  labels:
    run: testpod
  name: testpod
spec:
  nodeName: node2
  containers:
  - image: myapp:v1
    name: testpod

#建立pod
[root@k8s-master scheduler]# kubectl apply -f pod1.yml
pod/testpod created

[root@k8s-master scheduler]# kubectl get pods  -o wide
NAME      READY   STATUS    RESTARTS   AGE   IP               NODE        NOMINATED NODE   READINESS GATES
testpod   1/1     Running   0          18s   10.244.169.130   k8s-node2   <none>           <none>

2.Nodeselector(通过标签控制节点)

  • nodeSelector 是节点选择约束的最简单推荐形式

  • 给选择的节点添加标签:

    复制代码
    kubectl label nodes k8s-node1 lab=lee
  • 可以给多个节点设定相同标签

bash 复制代码
#查看节点标签
[root@k8s-master scheduler]# kubectl get nodes --show-labels
NAME         STATUS   ROLES           AGE    VERSION   LABELS
k8s-master   Ready    control-plane   5d3h   v1.30.0   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=k8s-master,kubernetes.io/os=linux,node-role.kubernetes.io/control-plane=,node.kubernetes.io/exclude-from-external-load-balancers=
k8s-node1    Ready    <none>          5d3h   v1.30.0   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=k8s-node1,kubernetes.io/os=linux
k8s-node2    Ready    <none>          5d3h   v1.30.0   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=k8s-node2,kubernetes.io/os=linux


#设定节点标签
[root@k8s-master scheduler]# kubectl label nodes k8s-node1 lab=timinglee
node/k8s-node1 labeled
#取消标签:
kubectl label node k8s-node1 lab-

[root@k8s-master scheduler]# kubectl get nodes k8s-node1 --show-labels
NAME        STATUS   ROLES    AGE    VERSION   LABELS
k8s-node1   Ready    <none>   5d3h   v1.30.0   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=k8s-node1,kubernetes.io/os=linux,lab=timinglee

#调度设置
[root@k8s-master scheduler]# vim pod2.yml
apiVersion: v1
kind: Pod
metadata:
  labels:
    run: testpod
  name: testpod
spec:
  nodeSelector:
    lab: timinglee
  containers:
  - image: myapp:v1
    name: testpod

[root@k8s-master scheduler]# kubectl apply -f pod2.yml
pod/testpod created
[root@k8s-master scheduler]# kubectl get pods  -o wide
NAME      READY   STATUS    RESTARTS   AGE   IP             NODE        NOMINATED NODE   READINESS GATES
testpod   1/1     Running   0          4s    10.244.36.65   k8s-node1   <none>           <none>

7.3 affinity(亲和性)

官方文档 :

将 Pod 指派给节点 | Kubernetes

nodeAffinity节点亲和

  • 那个节点服务指定条件就在那个节点运行

  • requiredDuringSchedulingIgnoredDuringExecution 必须满足,但不会影响已经调度

  • preferredDuringSchedulingIgnoredDuringExecution 倾向满足,在无法满足情况下也会调度pod

    • IgnoreDuringExecution 表示如果在Pod运行期间Node的标签发生变化,导致亲和性策略不能满足,则继续运行当前的Pod。
  • nodeaffinity还支持多种规则匹配条件的配置如

bash 复制代码
#给node1打上标签
[root@master scheduler]# kubectl label nodes node1 disk=haha
node/node1 labeled
[root@master scheduler]# kubectl get nodes -l disk=haha
NAME    STATUS   ROLES    AGE   VERSION
node1   Ready    <none>   8d    v1.30.0

#In
[root@master scheduler]# cat node-p.yml
apiVersion: v1
kind: Pod
metadata:
  name: node-affinity
spec:
  containers:
  - name: nginx
    image: nginx
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
           nodeSelectorTerms:
           - matchExpressions:
             - key: disk
               operator: In
               values:
                 - haha

#node1上有这个标签所以在这个列表值内
#所以运行在node1上
[root@master scheduler]# kubectl get pods -o wide
NAME            READY   STATUS    RESTARTS   AGE   IP                NODE    NOMINATED NODE   READINESS GATES
node-affinity   1/1     Running   0          28s   192.168.166.130   node1   <none>           <none>
[root@master scheduler]#


#NotIn
[root@master scheduler]# cat node-p.yml
apiVersion: v1
kind: Pod
metadata:
  name: node-affinity
spec:
  containers:
  - name: nginx
    image: nginx
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
           nodeSelectorTerms:
           - matchExpressions:
             - key: disk
               operator: NotIn
               values:
                 - haha
#NOTin:label 的值不在列表内
#node2上没有这个标签所有不在列表值内所有会在node2上运行
[root@master scheduler]# kubectl get pods  -o wide
NAME            READY   STATUS    RESTARTS   AGE   IP              NODE    NOMINATED NODE   READINESS GATES
node-affinity   1/1     Running   0          17s   192.168.104.1   node2   <none>           <none>
[root@master scheduler]#

Podaffinity(pod的亲和)

  • 那个节点有符合条件的POD就在那个节点运行

  • podAffinity 主要解决POD可以和哪些POD部署在同一个节点中的问题

  • podAntiAffinity主要解决POD不能和哪些POD部署在同一个节点中的问题。它们处理的是Kubernetes集群内部POD和POD之间的关系。

  • Pod 间亲和与反亲和在与更高级别的集合(例如 ReplicaSets,StatefulSets,Deployments 等)一起使用时,

  • Pod 间亲和与反亲和需要大量的处理,这可能会显著减慢大规模集群中的调度。

bash 复制代码
[root@k8s-master scheduler]# vim example4.yml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  labels:
    app: nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx
      affinity:
        podAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
              - key: app
                operator: In
                values:
                - nginx
            topologyKey: "kubernetes.io/hostname"

[root@k8s-master scheduler]# kubectl get pods  -o wide
NAME                               READY   STATUS    RESTARTS   AGE   IP               NODE        NOMINATED NODE   READINESS GATES
nginx-deployment-658496fff-d58bk   1/1     Running   0          39s   10.244.169.133   k8s-node2   <none>           <none>
nginx-deployment-658496fff-g25nq   1/1     Running   0          39s   10.244.169.134   k8s-node2   <none>           <none>
nginx-deployment-658496fff-vnlxz   1/1     Running   0          39s   10.244.169.135   k8s-node2   <none>           <none>

Podantiaffinity(pod反亲和)

bash 复制代码
[root@master scheduler]# cat antaffinity.yml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  labels:
    app: nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx
      affinity:
        podAntiAffinity:                #反亲和
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
              - key: app
                operator: In
                values:
                - nginx
            topologyKey: "kubernetes.io/hostname"
[root@master scheduler]#

7.4 Taints(污点模式,禁止调度)

  • Taints(污点)是Node的一个属性,设置了Taints后,默认Kubernetes是不会将Pod调度到这个Node上

  • Kubernetes如果为Pod设置Tolerations(容忍),只要Pod能够容忍Node上的污点,那么Kubernetes就会忽略Node上的污点,就能够(不是必须)把Pod调度过去

  • 可以使用命令 kubectl taint 给节点增加一个 taint:

bash 复制代码
$ kubectl taint nodes <nodename> key=string:effect   #命令执行方法
$ kubectl taint nodes node1 key=value:NoSchedule    #创建
$ kubectl describe nodes server1 | grep Taints        #查询
$ kubectl taint nodes node1 key-                  #删除

effect\] 可取值: | effect值 | 解释 | |------------------|----------------------------------------| | NoSchedule | POD 不会被调度到标记为 taints 节点 | | PreferNoSchedule | NoSchedule 的软策略版本,尽量不调度到此节点 | | NoExecute | 如该节点内正在运行的 POD 没有对应 Tolerate 设置,会直接被逐出 | 示例: ```bash [root@master scheduler]# cat tain1.yml apiVersion: apps/v1 kind: Deployment metadata: labels: app: web name: web spec: replicas: 2 selector: matchLabels: app: web template: metadata: labels: app: web spec: containers: - image: nginx name: nginx [root@master scheduler]# kubectl apply -f tain1.yml deployment.apps/web created [root@master scheduler]# [root@master scheduler]# kubectl taint node node1 Taintype=badnode:NoSchedule node/node1 tainted ``` ![](https://i-blog.csdnimg.cn/direct/a767052fd6914811a569d99962bfd32d.png) ```bash 增加容器 [root@master scheduler]# kubectl scale deployment web --replicas 4 deployment.apps/web scaled ``` ![](https://i-blog.csdnimg.cn/direct/5db34bd5e9de4f92ac889dac5a770f20.png) ```bash #去掉node1上的污点标记 [root@master scheduler]# kubectl taint node node1 Taintype- node/node1 untainted #将node2设置为NoExecute即驱赶,将已有的驱赶到其他节点上 [root@master scheduler]# kubectl taint node node2 Taintype=badnode:NoExecute node/node2 tainted [root@master scheduler]# ``` ![](https://i-blog.csdnimg.cn/direct/dc54d54ff4f24d0e86b6b18a1de0a12c.png) **tolerations(污点容忍)** * tolerations中定义的key、value、effect,要与node上设置的taint保持一直: * 如果 operator 是 Equal ,则key与value之间的关系必须相等。 * 如果 operator 是 Exists ,value可以省略 * 如果不指定operator属性,则默认值为Equal。 * 还有两个特殊值: * 当不指定key,再配合Exists 就能匹配所有的key与value ,可以容忍所有污点。 * 当不指定effect ,则匹配所有的effect ```bash #设定节点污点 [root@k8s-master scheduler]# kubectl taint node k8s-node1 name=lee:NoExecute node/k8s-node1 tainted [root@k8s-master scheduler]# kubectl taint node k8s-node2 nodetype=bad:NoSchedule node/k8s-node2 tainted [root@k8s-master scheduler]# vim example7.yml apiVersion: apps/v1 kind: Deployment metadata: labels: app: web name: web spec: replicas: 6 selector: matchLabels: app: web template: metadata: labels: app: web spec: containers: - image: nginx name: nginx #写一种即可 tolerations: #容忍所有污点 - operator: Exists tolerations: #容忍effect为Noschedule的污点 - operator: Exists effect: NoSchedule tolerations: #容忍指定kv的NoSchedule污点 - key: nodetype value: bad effect: NoSchedule ``` ![](https://i-blog.csdnimg.cn/direct/bce91726982e463d9fc9c5a834d8c542.png) ## 八 kubernetes中的认证授权 ### 8.1 kubernetes API 访问控制![](https://i-blog.csdnimg.cn/direct/3c72b4950e1b4ea39d8790fd5eed1f2e.png) Authentication(认证) * 认证方式现共有8种,可以启用一种或多种认证方式,只要有一种认证方式通过,就不再进行其它方式的认证。通常启用X509 Client Certs和Service Accout Tokens两种认证方式。 * Kubernetes集群有两类用户:由Kubernetes管理的Service Accounts (服务账户)和(Users Accounts) 普通账户。k8s中账号的概念不是我们理解的账号,它并不真的存在,它只是形式上存在。 Authorization(授权) * 必须经过认证阶段,才到授权请求,根据所有授权策略匹配请求资源属性,决定允许或拒绝请求。授权方式现共有6种,AlwaysDeny、AlwaysAllow、ABAC、RBAC、Webhook、Node。默认集群强制开启RBAC。 Admission Control(准入控制) * 用于拦截请求的一种方式,运行在认证、授权之后,是权限认证链上的最后一环,对请求API资源对象进行修改和校验。 ![](https://i-blog.csdnimg.cn/direct/b9f3f465311846589d06b6f3fea3c95b.png) **UserAccount与ServiceAccount** * 用户账户是针对人而言的。 服务账户是针对运行在 pod 中的进程而言的。 * 用户账户是全局性的。 其名称在集群各 namespace 中都是全局唯一的,未来的用户资源不会做 namespace 隔离, 服务账户是 namespace 隔离的。 * 集群的用户账户可能会从企业数据库进行同步,其创建需要特殊权限,并且涉及到复杂的业务流程。 服务账户创建的目的是为了更轻量,允许集群用户为了具体的任务创建服务账户 ( 即权限最小化原则 )。 **ServiceAccount** * 服务账户控制器(Service account controller) * 服务账户管理器管理各命名空间下的服务账户 * 每个活跃的命名空间下存在一个名为 "default" 的服务账户 * 服务账户准入控制器(Service account admission controller) * 相似pod中 ServiceAccount默认设为 default。 * 保证 pod 所关联的 ServiceAccount 存在,否则拒绝该 pod。 * 如果pod不包含ImagePullSecrets设置那么ServiceAccount中的ImagePullSecrets 被添加到pod中 * 将挂载于 /var/run/secrets/kubernetes.io/serviceaccount 的 volumeSource 添加到 pod 下的每个容器中 * 将一个包含用于 API 访问的 token 的 volume 添加到 pod 中 **ServiceAccount示例:** ```bash 建立名字为admin的ServiceAccount [root@k8s-master ~]# kubectl create sa timinglee serviceaccount/timinglee created [root@k8s-master ~]# kubectl describe sa timinglee Name: timinglee Namespace: default Labels: Annotations: Image pull secrets: Mountable secrets: Tokens: Events: 建立secrets [root@k8s-master ~]# kubectl create secret docker-registry docker-login --docker-username admin --docker-password 123 --docker-server reg.timinglee.org --docker-email lee@timinglee.org secret/docker-login created [root@k8s-master ~]# kubectl describe secrets docker-login Name: docker-login Namespace: default Labels: Annotations: Type: kubernetes.io/dockerconfigjson Data ==== .dockerconfigjson: 119 bytes 将secrets注入到sa中 imagePullSecrets: - name: docker-auth [root@k8s-master ~]# kubectl edit sa timinglee apiVersion: v1 imagePullSecrets: - name: docker-login kind: ServiceAccount metadata: creationTimestamp: "2024-09-08T15:44:04Z" name: timinglee namespace: default resourceVersion: "262259" uid: 7645a831-9ad1-4ae8-a8a1-aca7b267ea2d [root@k8s-master ~]# kubectl describe sa timinglee Name: timinglee Namespace: default Labels: Annotations: Image pull secrets: docker-login Mountable secrets: Tokens: Events: 建立私有仓库并且利用pod访问私有仓库 在创建pod时会镜像下载会受阻,因为docker私有仓库下载镜像需要认证 [root@k8s-master auth]# vim example1.yml [root@k8s-master auth]# kubectl apply -f example1.yml pod/testpod created [root@k8s-master auth]# kubectl describe pod testpod Warning Failed 5s kubelet Failed to pull image "reg.timinglee.org/lee/nginx:latest": Error response from daemon: unauthorized: unauthorized to access repository: lee/nginx, action: pull: unauthorized to access repository: lee/nginx, action: pull Warning Failed 5s kubelet Error: ErrImagePull Normal BackOff 3s (x2 over 4s) kubelet Back-off pulling image "reg.timinglee.org/lee/nginx:latest" Warning Failed 3s (x2 over 4s) kubelet Error: ImagePullBackOff pod绑定sa [root@k8s-master auth]# vim example1.yml apiVersion: v1 kind: Pod metadata: name: testpod spec: serviceAccountName: timinglee containers: - image: reg.timinglee.org/lee/nginx:latest name: testpod [root@k8s-master auth]# kubectl apply -f example1.yml pod/testpod created [root@k8s-master auth]# kubectl get pods NAME READY STATUS RESTARTS AGE testpod 1/1 Running 0 2s ``` ### 8.2 认证(创建认证用户) #### 8.2.1 创建UserAccount ```bash #建立证书 [root@k8s-master auth]# cd /etc/kubernetes/pki/ [root@master pki]# openssl genrsa -out hello.key 2048 [root@master pki]# openssl req -new -key hello.key -out hello.csr -subj "/CN=hello" [root@master pki]# openssl x509 -req -in hello.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out hello.crt -days 365 Certificate request self-signature ok subject=CN = hello [root@k8s-master pki]# openssl x509 -in timinglee.crt -text -noout Certificate: Data: Version: 1 (0x0) Serial Number: 76:06:6c:a7:36:53:b9:3f:5a:6a:93:3a:f2:e8:82:96:27:57:8e:58 Signature Algorithm: sha256WithRSAEncryption Issuer: CN = kubernetes Validity Not Before: Sep 8 15:59:55 2024 GMT Not After : Sep 8 15:59:55 2025 GMT Subject: CN = timinglee Subject Public Key Info: Public Key Algorithm: rsaEncryption Public-Key: (2048 bit) Modulus: 00:a6:6d:be:5d:7f:4c:bf:36:96:dc:4e:1b:24:64: f7:4b:57:d3:45:ad:e8:b5:07:e7:78:2b:9e:6e:53: 2f:16:ff:00:f4:c8:41:2c:89:3d:86:7c:1b:16:08: 2e:2c:bc:2c:1e:df:60:f0:80:60:f9:79:49:91:1d: 9f:47:16:9a:d1:86:c7:4f:02:55:27:12:93:b7:f4 ... ``` ```bash #建立k8s中的用户 [root@master pki]# kubectl config set-credentials timi --client-certificate /etc/kubernetes/pki/hello.crt --client-key /etc/kubernetes/pki/hello.key --embed-certs=true User "timi" set. [root@master pki]# [root@master pki]# kubectl config view apiVersion: v1 clusters: - cluster: certificate-authority-data: DATA+OMITTED server: https://192.168.217.100:6443 name: kubernetes contexts: - context: cluster: kubernetes user: kubernetes-admin name: kubernetes-admin@kubernetes current-context: kubernetes-admin@kubernetes kind: Config preferences: {} users: - name: kubernetes-admin user: client-certificate-data: DATA+OMITTED client-key-data: DATA+OMITTED - name: timi user: client-certificate-data: DATA+OMITTED client-key-data: DATA+OMITTED #为用户创建集群的安全上下文 [root@master pki]# kubectl config set-context timi@kubernetes --cluster kubernetes --user timi Context "timi@kubernetes" created. [root@master pki]# #切换用户,用户在集群中只有用户身份没有授权 [root@master pki]# kubectl config use-context timi@kubernetes Switched to context "timi@kubernetes". [root@master pki]# kubectl get pods Error from server (Forbidden): pods is forbidden: User "hello" cannot list resource "pods" in API group "" in the namespace "default" [root@master pki]# kubectl config use-context kubernetes-admin@kubernetes Switched to context "kubernetes-admin@kubernetes". [root@master pki]# #切换会集群管理 [root@k8s-master ~]# kubectl config use-context kubernetes-admin@kubernetes Switched to context "kubernetes-admin@kubernetes". #如果需要删除用户 [root@k8s-master pki]# kubectl config delete-user timinglee deleted user timinglee from /etc/kubernetes/admin.conf ``` #### 8.2.2 RBAC(Role Based Access Control) ##### 基于角色访问控制授权:![](https://i-blog.csdnimg.cn/direct/d6f11270d3e548509d47a2666cc7d8e1.png) * 允许管理员通过Kubernetes API动态配置授权策略。RBAC就是用户通过角色与权限进行关联。 * RBAC只有授权,没有拒绝授权,所以只需要定义允许该用户做什么即可 * RBAC的三个基本概念 * Subject:被作用者,它表示k8s中的三类主体, user, group, serviceAccount * Role:角色,它其实是一组规则,定义了一组对 Kubernetes API 对象的操作权限。 * RoleBinding:定义了"被作用者"和"角色"的绑定关系 * RBAC包括四种类型:Role、ClusterRole、RoleBinding、ClusterRoleBinding * Role 和 ClusterRole * Role是一系列的权限的集合,Role只能授予单个namespace 中资源的访问权限。 * ClusterRole 跟 Role 类似,但是可以在集群中全局使用。 * Kubernetes 还提供了四个预先定义好的 ClusterRole 来供用户直接使用 * cluster-amdin、admin、edit、view ##### rrole授权实施 ```bash #生成role的yaml文件 [root@k8s-master rbac]# kubectl create role myrole --dry-run=client --verb=get --resource pods -o yaml > myrole.yml #更改文件内容 [root@k8s-master rbac]# vim myrole.yml apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: creationTimestamp: null name: myrole rules: - apiGroups: - "" resources: - pods verbs: - get - watch - list - create - update - path - delete #创建role [root@k8s-master rbac]# kubectl apply -f myrole.yml [root@k8s-master rbac]# kubectl describe role myrole Name: myrole Labels: Annotations: PolicyRule: Resources Non-Resource URLs Resource Names Verbs --------- ----------------- -------------- ----- pods [] [] [get watch list create update path delete] ``` ```bash #建立角色绑定 [root@k8s-master rbac]# kubectl create rolebinding timinglee --role myrole --namespace default --user hello --dry-run=client -o yaml > rolebinding-myrole.yml [root@master role]# cat rolebinding-myrole.yml apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: name: timi namespace: default #角色绑定必须指定namespace roleRef: apiGroup: rbac.authorization.k8s.io kind: Role name: myrole subjects: - apiGroup: rbac.authorization.k8s.io kind: User name: hello [root@master role]# [root@k8s-master rbac]# kubectl apply -f rolebinding-myrole.yml rolebinding.rbac.authorization.k8s.io/timi created [root@k8s-master rbac]# kubectl get rolebindings.rbac.authorization.k8s.io timi NAME ROLE AGE timi Role/myrole 9s ``` ```bash #切换用户测试授权 [root@k8s-master rbac]# kubectl config use-context timinglee@kubernetes Switched to context "timi@kubernetes". [root@k8s-master rbac]# kubectl get pods No resources found in default namespace. [root@k8s-master rbac]# kubectl get svc #只针对pod进行了授权,所以svc依然不能操作 Error from server (Forbidden): services is forbidden: User "hello" cannot list resource "services" in API group "" in the namespace "default" #切换回管理员 [root@k8s-master rbac]# kubectl config use-context kubernetes-admin@kubernetes Switched to context "kubernetes-admin@kubernetes". ``` ##### clusterrole授权实施 ```bash #建立集群角色 [root@k8s-master rbac]# kubectl create clusterrole myclusterrole --resource=deployment --verb get --dry-run=client -o yaml > myclusterrole.yml [root@master role]# cat myclusterrole.yml apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: myclusterrole rules: - apiGroups: - apps resources: - deployments verbs: - get - list - watch - create - update - path - delete - apiGroups: - "" resources: - pods verbs: - get - list - watch - create - update - path - delete - apiGroups: - "" resources: - services verbs: - get - list - watch - create - update - path - delete [root@master role]# [root@master role]# kubectl describe clusterrole myclusterrole Name: myclusterrole Labels: Annotations: PolicyRule: Resources Non-Resource URLs Resource Names Verbs --------- ----------------- -------------- ----- pods [] [] [get list watch create update path delete] services [] [] [get list watch create update path delete] deployments.apps [] [] [get list watch create update path delete] [root@master role]# #建立集群角色绑定 [root@k8s-master rbac]# kubectl create clusterrolebinding clusterrolebind-myclusterrole --clusterrole myclusterrole --user hello --dry-run=client -o yaml > clusterrolebind-myclusterrole.yml [root@master role]# cat clusterrolebind-myclusterrole.yml apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: clusterrolebind-myclusterrole roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: myclusterrole subjects: - apiGroup: rbac.authorization.k8s.io kind: User name: hello [root@master role]# [root@master role]# kubectl describe clusterrolebindings.rbac.authorization.k8s.io clusterrolebind-myclusterrole Name: clusterrolebind-myclusterrole Labels: Annotations: Role: Kind: ClusterRole Name: myclusterrole Subjects: Kind Name Namespace ---- ---- --------- User hello [root@master role]# #测试: [root@k8s-master rbac]# kubectl get pods -A [root@k8s-master rbac]# kubectl get deployments.apps -A [root@k8s-master rbac]# kubectl get svc -A Error from server (Forbidden): services is forbidden: User "timinglee" cannot list resource "services" in API group "" at the cluster scope ```

相关推荐
tb_first2 小时前
k8sday13数据存储(1.5/2)
linux·运维·服务器·云原生·容器·kubernetes
hhzz4 小时前
Rancher部署的K8S集群服务节点上执行 kubectl 命令
容器·kubernetes·rancher
pwj去战斗吧4 小时前
K8s-Ingress
云原生·kubernetes
AI扶我青云志5 小时前
Milvus 安装和启动指南
人工智能·云原生·eureka·大模型
预判你的代码5 小时前
Docker 快速下载Neo4j 方法记录
docker·容器·neo4j
IT人生--MarkGuo8 小时前
容器云后端存储NFS高可用适配
kubernetes
Insist7539 小时前
K8S-Secret资源对象
云原生·容器·kubernetes
-小末11 小时前
使用docker manifest制作本地多架构镜像
docker·容器·多架构镜像
掘金安东尼13 小时前
Amazon Polly :让文字开口说话的云端实践
人工智能·云原生