Docker 和 Kubernetes 入门到精通:运维工程师的实战笔记 (近5万字)

文章目录

  • 1. Docker
    • 1.1 Docker是什么?
      • 1.1.1 容器服务原理
    • 1.2 Docker的三大概念
      • 1.2.1 镜像
      • 1.2.2 容器
      • 1.2.3 仓库
      • 1.2.4 总结
    • 1.3 Docker常用命令
      • 1.3.1 镜像常用命令
      • 1.3.2 容器常用命令
    • 1.4 Dockerfile
      • 1.4.1 commit的局限
      • 1.4.2 Dockerfile是什么?
      • 1.4.3 如何使用Dockerfile制作镜像?
      • 1.4.4 Dockerfile中常用指令
        • 制作apache镜像
        • 制作nginx镜像
        • 制作php-fpm镜像
        • 制作tomcat镜像
    • 1.5 Registry仓库
      • 1.5.1 私有仓库概述
      • 1.5.2 部署registry私有仓库
        • 验证测试
    • 1.6 部署容器服务
      • 1.6.1 端口映射
      • 1.6.2 映射容器卷
      • 1.6.3 容器网络通信
    • 1.7 Docker-compose
      • 1.7.1 微服务概述
      • 1.7.2 微服务治理
        • docker compose 子命令
      • 1.7.3 微服务编排
        • compose语法
  • 2. Kubernetes
    • 2.1 k8s核心概念
      • 2.1.1 Node
      • 2.1.2 Pod
      • 2.1.3 Service
      • 2.1.4 Deployment
      • 2.1.5 StatefulSet
      • 2.1.6 DaemonSet
      • 2.1.7 Job&CronJob
      • 2.1.8 ConfigMap&Secret
      • 2.1.9 PV&PVC
      • 2.1.10 Namespace
      • 2.1.11 Ingress
    • 2.2 k8s核心组件
      • 2.2.1 Master 组件
        • API Server
        • etcd
        • Controller Manager
        • Scheduler
        • Calico
        • Metrics-server
      • 2.2.2 Node 组件
        • kubelet
        • kube-proxy
        • 容器运行时(如 Docker、containerd、CRI-O)
      • 2.2.3 日志与监控组件
        • Fluentd/Elasticsearch/Kibana (EFK)
        • Prometheus
        • Grafana
    • 2.3 k8s集群管理工具
      • 2.3.1 集群服务端口
      • 2.3.2 kubeadm集群管理工具
      • 2.3.3 kubectl集群命令行工具
      • 2.3.4 命令的自动补全操作
    • 2.4 k8s集群部署示例
      • 2.4.1 所需主机清单
      • 2.4.2 部署Harbor仓库
        • 1. 部署docker
        • 2. 导入harbor项目镜像
        • 3. 启动项目
        • 4. 验证项目
        • 5. Harbor仓库管理
      • 2.4.3 安装控制节点
        • 1. 环境配置
        • 2. 安装软件包
        • 3. 配置内核参数
        • 4. 导入k8s所需的镜像
        • 5. master安装
        • 6. 安装网络插件
      • 2.4.4 安装计算节点
        • 获取凭证
        • node安装
        • 验证节点状态
    • 2.5 k8s资源对象与管理
      • 2.5.1 kubectl子命令使用
        • 查看系统命名空间
        • 创建Pod资源对象
        • Pod状态分析
        • Pod管理命令
      • 2.5.2 排错三剑客
        • get子命令
        • describe子命令
        • log子命令
      • 2.5.3 资源清单文件管理
        • 常用子命令
        • 命令管理示例
      • 2.5.4 模板与帮助信息
        • 生成模板
        • 获取帮助信息
      • 2.5.6 多容器Pod
        • 管理多容器Pod
      • 2.5.7 Pod自定义任务
        • 自定义命令
        • restartPolicy策略
        • terminationGracePeriodSeconds 宽限期策略
        • activeDeadlineSeconds 策略
    • 2.6 Pod调度与标签
      • 2.6.1 什么是调度分配?
      • 2.6.2 调度流程
      • 2.6.3 Pod定向调度
        • 基于节点名称的调度
      • 2.6.4 Pod标签调度
        • Pod标签管理
        • 基于标签调度
    • 2.7 Pod生命周期与资源管理
      • 2.7.1 Pod生命周期概述
      • 2.7.2 Init容器
      • 2.7.3 容器探针
        • 探针类型
        • startupProbe启动探测类型
        • livenessProbe生命探测类型
        • readinessProbe就绪探测型
      • 2.7.4 事件处理函数
        • postStart和prestop
      • 2.7.5 Pod资源管理
        • 资源配额
        • 资源限额
        • 节点压力驱逐
        • Quota资源管理
    • 2.8 Pod控制器
      • 2.8.1 Pod的定义
      • 2.8.2 什么是Pod控制器
      • 2.8.3 ReplicaSet(RS)
      • 2.8.4 Deployment(Deploy)
        • 扩缩容
        • 镜像更新
        • 滚动更新
        • 版本管理
        • 版本回滚
        • 回滚到指定的版本
        • 金丝雀部署
      • 2.8.5 Statefulset(sts)
        • StatefulSet特点
      • 2.8.6 DaemonSet(DS)
      • 2.8.7 Job
      • 2.8.8 CronJob(CJ)
      • 2.8.9 HPA
        • 配置后端服务
        • 部署HPA控制器
        • 测试
    • 2.9 Service详解
      • 2.9.1 Service 存在的意义?
      • 2.9.2 svc的三大特征
      • 2.9.3 ClusterIP(集群内部使用)
        • 补充一:
        • 补充二:
      • 2.9.4 NodePort(对外暴露应用)
        • 补充
      • 2.9.5 Ingress
        • 为什么引入Ingress?
        • Ingress 是什么?
        • Ingerss安装
      • 2.9.6 LoadBalancer(对外暴露应用,适用于公有云)
      • 2.9.7 ExternalName
      • 2.9.8 Endpoints与Service和Pod间的关联
    • 2.10 k8s存储
      • 2.10.1 概述
      • 2.10.2 k8s支持的卷类型
      • 2.10.3 临时存储
      • 2.10.4 持久存储
      • 2.10.5 ConfigMap
        • 用途:
        • 与Secret的区别
        • 通过目录创建
        • 通过文件创建
        • 环境变量
        • 命令行参数
        • 卷挂载
        • 热更新
        • 补充
        • CM的优势
      • 2.10.6 Secret
        • Secret特性
        • Secret的类型
        • Opaque类型 (使用最多)
        • 在pod中使用secret
          • 环境变量
          • 卷挂载
      • 2.10.7 Downward API
        • Downward API 的两种注入方式
          • 环境变量
          • 卷挂载
        • Downward API 支持的字段
        • 使用 Downward API 的步骤
      • 2.10.8 Volume
        • 定义与用途
        • emptyDir
        • hostPath
        • NFS
        • 使用流程
        • 注意事项
      • 2.10.9 PV/PVC
        • PV(Persistent Volume)
        • 访问模式
        • PVC(Persistent Volume Claim)
        • 工作流程
        • PV与PVC的关系
        • PV与PVC的关联条件
        • PV回收策略
        • PV状态
        • 部署pv示例
        • 创建服务与pvc示例
    • 2.11 Pod调度策略管理
      • 2.11.1 污点概述
      • 2.11.2 管理污点标签
      • 2.11.3 容忍策略
      • 2.11.4 抢占与优先级
        • PriorityClass简介
    • 2.12 用户认证&RBAC授权
      • 2.12.1 用户认证
        • 创建服务账户示例:
        • 创建普通账户的步骤:
        • 角色与授权
      • 2.12.2 RBAC授权
        • 角色(Role)
        • 集群角色(ClusterRole)
        • 角色绑定(RoleBinding)
        • 集群角色绑定(ClusterRoleBinding)
        • 资源对象角色与作用域
        • 资源对象权限
        • 主体(Subject)

1. Docker

1.1 Docker是什么?

[!IMPORTANT]

Docker是一个开源的容器化平台,Docker的容器中没有系统,它可以帮助你把应用程序和它们的依赖打包成一个“容器”,这样你就可以在任何地方运行这个容器,而不用担心环境的不同。想象一下,你有一个应用程序,它在你的开发环境中运行得很好,但当你把它部署到生产环境时,可能会遇到各种问题,因为两个环境的配置不一样。Docker可以帮助你解决这个问题。

简单来说Docker就像是一个神奇的盒子,你可以把程序和它需要的东西(比如它喜欢吃的食物、玩具等)一起打包进这个盒子里。这样,不管你把这个盒子搬到哪里,只要那里有电脑,你的程序都能在里面正常运行,而且不会和其他程序打架抢东西。

总的来说,Docker就是帮你把程序和它需要的东西打包好,让程序在任何地方都能快速、安全、方便地运行。

1.1.1 容器服务原理

什么时上帝进程?

简单来说就是系统创建之处产生的第一个进程
特点:

  • 没有父进程,PID==1

  • 是所有程序的根进程

  • 上帝进程死亡系统实例也就关闭了

容器的上帝进程

  • 容器的启动进程就是上帝进程

  • 如果容器的启动进程关闭等同于容器关闭

  • 上帝进程无法在后台执行

  • 容器的启动进程必须放在前台执行

1.2 Docker的三大概念

1.2.1 镜像

  • 镜像是一个只读的模板,包含了创建Docker容器所需的所有文件和配置。镜像可以包含操作系统、应用程序、库、环境变量等。镜像是容器的基础,容器是镜像的运行实例。

  • 定义:镜像是一个包含了所有必要文件和配置的只读模板,用于创建Docker容器。

  • 创建:镜像可以通过Dockerfile文件构建,Dockerfile文件定义了镜像的构建步骤。

1.2.2 容器

  • 容器是镜像的运行实例。容器是一个轻量级的、独立的执行环境,它可以运行一个或多个进程。容器与虚拟机不同,它们共享主机操作系统的内核,而不是包含完整的操作系统。

  • 定义:容器是镜像的运行实例,提供一个隔离的执行环境。

  • 运行:容器可以通过docker run命令从镜像启动。

1.2.3 仓库

  • 仓库是存储和分发镜像的地方。Docker仓库可以是公共的(如Docker Hub),也可以是私有的。仓库允许用户上传、下载和共享镜像。

  • 定义:仓库是存储和分发镜像的集合。

  • 访问:可以通过docker pull和docker push命令从仓库下载和上传镜像。

1.2.4 总结

  • 镜像:只读模板,包含创建容器所需的所有文件和配置。

  • 容器:镜像的运行实例,提供一个隔离的执行环境。

  • 仓库:存储和分发镜像的地方。

1.3 Docker常用命令

1.3.1 镜像常用命令

镜像管理命令 说明
docker version 常看服务器与客户端版本
docker info 查看docker服务配置信息
docker images 查看本机镜像
docker pull 镜像名:标签 下载镜像
docker save 镜像名:标签 -0 文件名 备份镜像为tar包
docker load -i 备份文件名称 导入备份的镜像文件
docker history 镜像名:标签 常看镜像的制作历史
docker rmi 镜像名:标签 删除镜像(必须先删除该镜像启动的所有的容器)
docker tag 镜像id:标签 镜像名:新的标签 创建新的镜像名和标签

1.3.2 容器常用命令

容器管理命令 说明
docker run -it(d) 镜像名:标签 创建容器
docker ps -a[q] 查看所有容器的信息/id
docker inspect 镜像名|容器名 查看(镜像/容器)的详细信息
docker [start|stop|restart] 容器 id 启动、停止、重启容器
docker exec -it 容器id 启动命令 在容器内执行命令
docker logs 容器id 查看容器日志
docker cp 路径1 路径2 拷贝文件:路径模式(本机路径、容器id/路径)
docker rm [-f] 容器id 删除容器/强制删除
docker commit 容器id 新镜像名:新标签名 把容器制作成镜像

1.4 Dockerfile

1.4.1 commit的局限

很容易制作简单的镜像,但碰到复杂的情况就十分不方便

例如碰到下面的情况:

  • 需要设置默认的启动命令

  • 需要设置环境变量

  • 需要指定镜像开放某些特定的端口

Dockerfile就是解决以上问题的方法

1.4.2 Dockerfile是什么?

  • Dockerfile是一种更强大的镜像制作方式

  • 编写类似脚本的 Dockerfile文件,通过该文件制作镜像

1.4.3 如何使用Dockerfile制作镜像?

  1. 编写 Dockerfile

  2. 制作镜像docker build -t 镜像名称:标签 Dockerfile所在目录

1.4.4 Dockerfile中常用指令

指令 说明
FROM 指定基础镜像(唯一)
RUN 在构建镜像时执行命令,可以写多条
ADD 把文件拷贝到容器内,如果文件是tar.xx格式,会自动解压
COPY 把文件拷贝到容器内,不会自动解压
ENV 设置启动容器的环境变量
WORKDIR 设置启动容器的默认工作目录(唯一)
CMD 容器默认的启动参数(唯一)
ENTRYPOINT 容器默认的启动命令(唯一)
USER 启动容器使用的用户(唯一)
EXPOSE 使用镜像创建的容器默认监听使用的端口号/协议,不会实际发布端口,只是文档说明

[!CAUTION]

ENTRYPOINT一定会执行,CMD可以通过传递参数覆盖

# ENTRYPOINT 与 CMD 执行方式为 ${ENTRYPOINT} ${@-${CMD}}
[root@docker ~]# vim myimg/Dockerfile 
FROM mylinux:latest
ENTRYPOINT ["echo"]
CMD  ["/bin/ls", "-l"]

# 创建镜像
[root@docker ~]# docker build -t img:v2 myimg/

# CMD 做为参数传递,在容器内执行了 echo '/bin/ls -l'
[root@docker ~]# docker run -it --rm img:v2 
/bin/ls -l

# CMD 被替换,在容器内执行了 echo id
[root@docker ~]# docker run -it --rm img:v2 id
id
制作apache镜像
[root@docker ~]# mkdir apache
[root@docker ~]# tar -czf apache/httpd.tar.gz index.html info.php
[root@docker ~]# docker cp httpd:/etc/httpd/conf.modules.d/00-mpm.conf apache/00-mpm.conf
[root@docker ~]# vim apache/00-mpm.conf
11: LoadModule mpm_prefork_module ... ... # 去掉注释 
23: # LoadModule mpm_event_module ... ... # 注释配置 
[root@docker ~]# ls apache/
00-mpm.conf  Dockerfile  httpd.tar.gz
[root@docker ~]# vim apache/Dockerfile
FROM mylinux:latest
RUN  dnf install -y httpd php && dnf clean all
COPY 00-mpm.conf /etc/httpd/conf.modules.d/00-mpm.conf
ADD  myweb.tar.gz /var/www/html/
ENV  LANG=C
WORKDIR /var/www/html/
EXPOSE 80/tcp
CMD  ["/usr/sbin/httpd", "-DFOREGROUND"]

[root@docker ~]# docker build -t myhttpd:latest apache
制作nginx镜像
[root@docker ~]# ls nginx/
Dockerfile  nginx-1.22.1.tar.gz
[root@docker ~]# vim nginx/Dockerfile
# 第一阶段编译程序
FROM mylinux:latest as builder
ADD  nginx-1.22.1.tar.gz /
WORKDIR /nginx-1.22.1
RUN  dnf install -y openssl-devel pcre-devel gcc make
RUN  ./configure --prefix=/usr/local/nginx --with-pcre --with-http_ssl_module
RUN  make
RUN  make install
RUN  echo 'Nginx is running !' >/usr/local/nginx/html/index.html

# 第二阶段最终镜像
FROM mylinux:latest
RUN  dnf install -y pcre openssl && dnf clean all
COPY --from=builder /usr/local/nginx /usr/local/nginx
ENV  PATH=${PATH}:/usr/local/nginx/sbin
WORKDIR /usr/local/nginx
EXPOSE 80/tcp
CMD  ["nginx", "-g", "daemon off;"]

[root@docker ~]# docker build -t mynginx:latest nginx
制作php-fpm镜像
[root@docker ~]# mkdir php
[root@docker ~]# vim php/Dockerfile
FROM mylinux:latest
RUN  dnf install -y php-fpm && dnf clean all && \
     mkdir -p /run/php-fpm && \
     chown -R nobody.nobody /run/php-fpm /var/log/php-fpm && \
     sed -ri 's,^(listen =).*,\1 127.0.0.1:9000,' /etc/php-fpm.d/www.conf
USER nobody
EXPOSE 9000/tcp
CMD ["/usr/sbin/php-fpm", "--nodaemonize"]

[root@docker ~]# docker build -t php-fpm:latest php
制作tomcat镜像
[root@tomcat ~]# mkdir myimg
[root@tomcat ~]# cd myimg
[root@tomcat myimg]# # 拷贝 apache-tomcat.tar.gz 到该目录中
[root@tomcat myimg]# vim Dockerfile 
FROM myos:8.5
RUN  yum install -y java-1.8.0-openjdk && yum clean all
ADD  apache-tomcat.tar.gz /usr/local/
WORKDIR /usr/local/apache-tomcat/webapps
EXPOSE 8080
CMD ["/usr/local/apache-tomcat/bin/catalina.sh", "run"]
[root@tomcat myimg]# docker build -t myos:tomcat .
[root@tomcat myimg]# docker run -itd -p 8080:8080 myos:tomcat

1.5 Registry仓库

1.5.1 私有仓库概述

  • 私有仓库是存储者 docker image 的仓库

  • 管理了一个 docker 集群,在所有节点维护镜像的一致性是一个非常麻烦繁琐的任务,使用公共仓库,我们又无法控制仓库中的镜像、版本等数据,私有仓库就是解决这些问题的最佳方法

  • 用户只需要维护私有仓库里面的镜像即可,docker 客户端可以通过私有仓库创建容器服务

  • 主流仓库有 docker Registry 和 [vmware Harbor](###2.4.2 部署Harbor仓库)

  • Registry 提供了仓库的核心功能,包括分层传输机制、WEB接口等功能

  • Habor 是在 Registry 上进行了相应的企业级扩展,从而获得了更加广泛的应用,这些新的企业级特性包括:提供WEB界面,优化用户体验,支持登陆、搜索功能,区分公有、私有镜像,以及基于角色的访问控制,集成日志审计、支持水平扩展等功能

1.5.2 部署registry私有仓库

#在registry上安装私有仓库
[root@registry ~]# dnf install -y docker-distribution
# 启动私有仓库,并设置开机自启动
[root@registry ~]# systemctl enable --now docker-distribution

#客户端配置
[root@docker ~]# vim /etc/hosts
192.168.88.30   registry
# 修改配置文件
[root@docker ~]# vim /etc/docker/daemon.json
{
    "registry-mirrors": ["http://registry:5000", "其他镜像仓库"], #镜像仓库地址
    "insecure-registries":["registry:5000"] #信任仓库地址
}
# 重启服务生效
[root@docker ~]# systemctl restart docker
[root@docker ~]# docker info

# 给镜像设置标签,上传本地mylinux:latest镜像到registry:5000的library路径中取名为mylinux:latest
[root@docker ~]# docker tag mylinux:latest registry:5000/library/mylinux:latest
#查看本地镜像
[root@docker-0002 ~]# docker images
REPOSITORY                  TAG       IMAGE ID       CREATED       SIZE
registry:5000/library/mylinux   latest    38e93535adaa   2 days ago    249MB
# 上传镜像
[root@docker ~]# docker push registry:5000/library/mylinux:latest
验证测试
#验证测试
#查看镜像名称:curl http://仓库IP:5000/v2/_catalog
#查看镜像标签:curl http://仓库IP:5000/v2/镜像路径/tags/list
#使用易读格式:python3 -m json.tool
# 查看仓库中所有镜像的名称
[root@docker ~]# curl http://registry:5000/v2/_catalog
{"repositories":["img/mylinux","library/myhttpd","library/mynginx","library/php-fpm"]}

# 易读格式显示镜像名称
[root@docker-0002 ~]# curl -s http://registry:5000/v2/_catalog |python3 -m json.tool
{
    "repositories": [
        "img/mylinux",
        "library/myhttpd",
        "library/mynginx",
        "library/php-fpm"
    ]
}

# 查看某一镜像的所有标签
[root@docker ~]# curl http://registry:5000/v2/img/mylinux/tags/list
{"name":"img/mylinux","tags":["latest","8"]}

#使用registry仓库创建容器
#删除所有容器
[root@docker ~]# docker rm -f $(docker ps -aq)
#删除所有镜像
[root@docker ~]# docker rmi $(docker images --format='{{.Repository}}:{{.Tag}}')

#下载镜像
[root@docker ~]# docker pull registry:5000/img/mylinux:latest
2b7cd6d88a7665dbea0a4b3d99478e9f302c0a5661d7676d6d3bd3cb6d181

#library 是默认路径,可以省略路径地址
[root@docker ~]# docker run -itd --rm myhttpd:latest
634766f788d665dbea0a4b39709e0a2cc8624fd99478e9f302c0a5661d76

1.6 部署容器服务

1.6.1 端口映射

容器化带来的问题

  • 新创建容器的IP 地址是随机的

  • 容器在重启后每次IP者都会发生变化

  • 容器服务只有宿主机才能访问

如何才能使用容器对外提供稳定的服务?

  • 容器端口可以与宿主机的端口进行映射绑定

  • 从而把宿主机变成对应的服务,不用关心容器的IP地址

  • 每个端口都只能和一个容器绑定

端口映射语法:
docker run -itd -p [可选IP]:宿主机端口:容器端口 镜像:标签(可有多个)

示例:

[root@docker ~]# docker run -itd --rm --name web -p 80:80 -p 443:443 myos:nginx

1.6.2 映射容器卷

Docker可以映射宿主机文件或目录到容器中

  • 目标对象不存在就自动创建

  • 目标对象存在就直接覆盖掉

  • 多个容器可以映射同一个目标对象来达到数据共享的目的

启动容器时,使用 -v 映射参数(可有多个)
docker run -itd -v宿主机对象:容器内对象 镜像名:标签

示例:

[root@docker ~]# docker run -itd --rm --name web -p 80:80 \
                   -v /root/conf:/usr/local/nginx/conf \
                   -v /var/webroot:/usr/local/nginx/html myos:nginx

1.6.3 容器网络通信

docker的网络通信模式

  • bridge 模式,默认模式

  • host模式,与宿主机共享网络

  • none模式,无网络模式

  • container模式,共享其他容器的网络命名空间

  • 自定义网络,自由创建桥接网络或者overlay网络

使用网络命名空间共享网络
参数是 --network=container:容器名|ID

示例:

[root@docker ~]# docker run -itd --rm --name php --network=container:web \
                   -v /var/webroot:/usr/local/nginx/html myos:php-fpm

1.7 Docker-compose

1.7.1 微服务概述

微服务并不是一种技术,而是架构思想、它以容器技术为载体,演进出的一种以软件运行环境、产品、研发、运营为一体全新模式。

  • 在微服务架构中每个微服务一般都会包含多个容器实例。

  • 如果每个微服务都要手动管理,那么效率之低、维护量之大可想而知。为了解决编排部署的问题,docker 公司推出了docker Compose 工具

  • Compose是一个用于定义和运行多容器的应用的工具。

  • 使用Compose,可以在一个文件中配置多个容器服务,然后使用一个简单的命令就可以轻松、高效地管理配置中引用的所有容器服务。

1.7.2 微服务治理

创建项目服务
项目文件 docker-compose.yaml

# 创建项目文件
[root@docker ~]# vim docker-compose.yaml
name: myweb                       #项目名称
version: "3"                      #语法格式版本
services:                         #关键字,定义服务
  websvc:                         #服务名称
    container_name: nginx         #容器名称
    image: myos:nginx             #创建容器使用的镜像
docker compose 子命令

docker compose [-f xxx.yaml | -p project] 子命令

指令 说明
up 创建项目并启动容器
ls 列出可以管理的项目
images 列出项目使用的镜像
ps 显示项目中容器的状态
logs 查看下项目中容器的日志
start/stop/restart 启动项目/停止项目/重启项目
down 删除项目容器及网络

1.7.3 微服务编排

  • Compose项目是Docker官方的开源项目,负责实现容器集群的快速编排,在Compose中有两个核心概念,分别是服务和项目

  • 服务(service):一个应用的容器,实际上可以包括若干运行相同镜像的容器实例。

  • 项目(project):由一组关联的应用容器组成的一个完整业务单元,在docker-compose.yaml文件中定义

compose语法
指令 说明
networks 配置容器连接的网络
container_name 指定容器名称
depends_on 服务依赖关系 services_[started、healthy、completed_successfully]
command 覆盖容器启动后默认执行的命令
environment 设置环境变量
image 指定为镜像名称或镜像 ID
network_mode 设置网络模式
restart 容器保护策略[always、no、on-failure]
ports 暴露端口信息
volumes 数据卷,支持 [volume、bind、tmpfs、npipe]

示例:

[root@docker-0001 ~]# cat docker-compose.yml 
name: myweb 
version: "3" 
services:
  websvc:
    container_name: nginx
    image: myos:nginx
    environment:
      - "TZ=Asia/Shanghai"
    ports:
      - 80:80
    volumes:
      - type: bind
        source: /var/webroot
        target: /usr/local/nginx/html
      - type: bind
        source: /root/conf/conf
        target: /usr/local/nginx/conf
  phpsvc:
    container_name: php
    image: myos:php-fpm
    network_mode: "service:websvc"
    restart: always
    volumes:
      - type: bind
        source: /var/webroot
        target: /usr/local/nginx/html

嵌入脚本:

[root@docker-0001 ~]# cat docker-script.yaml 
name: mycmd
version: "3"
services:
  shell:
    container_name: mycmd
    image: myos:8.5
    command:
      - sh
      - -c
      - |
        for i in {1..5}
        do
           sleep 1
           echo "$${i} ${HOSTNAME} && $${HOSTNAME}"
        done
[root@docker-0001 ~]# docker compose -p mycmd logs
mycmd  | 1 docker-0001 && aff93cf09d53
mycmd  | 2 docker-0001 && aff93cf09d53
mycmd  | 3 docker-0001 && aff93cf09d53
mycmd  | 4 docker-0001 && aff93cf09d53
mycmd  | 5 docker-0001 && aff93cf09d53

2. Kubernetes

[!IMPORTANT]

kubernetes是一个开源的容器集群管理系统,用于管理云平台中多个主机上的容器化的应用,kubernetes的目标是让部署容器化的应用简单且高效,可以实现容器集群的自动化部署、自动扩缩容、自动维护等功能。

Kubernetes 这个名字源于希腊语,意为“舵手”或“飞行员”。K8s 这个缩写是因为 K 和 s 之间有 8 个字符的关系。 Google 在 2014 年开源了 Kubernetes 项目。 Kubernetes 建立在 Google 大规模运行生产工作负载十几年经验的基础上, 结合了社区中最优秀的想法和实践。

2.1 k8s核心概念

2.1.1 Node

代表集群中的一个节点,可以是物理服务器或虚拟机,负责运行 Pod

2.1.2 Pod

pod(容器组)是Kubernetes中的最小的可部署单元。一个 Pod 中可以运行一个或多个容器,这些容器共享网络命名空间、存储资源等,Pod 中的服务可以互相访问,但Pod 与 Pod 之间是隔离的。可以把 Pod 理解为一个逻辑上的“主机”,容器则是在这个“主机”上运行的具体程序,也可以理解为pod是一个容器组。

Kubernetes 集群中的 Pod 主要有两种用法:

  1. 运行单个容器的 Pod。"每个 Pod 一个容器"模型是最常见的 Kubernetes 用例; 在这种情况下,可以将 Pod 看作单个容器的包装器,并且 Kubernetes 直接管理 Pod,而不是容器。

  2. 运行多个需要协同工作的容器的 Pod。一个pod中运行多个需要互相协作的容器,可以将多个紧密耦合、共享资源且始终在一起运行的容器编排在同一个pod中。

定义:
Pod 是 Kubernetes 中最小的可部署单元,表示在集群中运行的一个或多个容器。Pod 中的容器共享相同的网络和存储资源。

特性:

  • 每个 Pod 都有一个唯一的 IP 地址。

  • Pod 中的容器共享网络命名空间和存储卷。

  • Pod 是短暂的,应用程序的生命周期通常由更高层次的 Kubernetes 对象(如 Deployment)管理。

2.1.3 Service

对外提供稳定的访问地址。在Kubernetes 中,一个服务可能由多个Pod提供支持,Service 可以将这些 Pod 组合起来,对外暴露一个统一的访问入口,实现负载均衡。这样,即使 Pod 挂掉重启,服务的访问地址也不会改变。比如,一个订单服务部署了多个副本,通过 Service 可以让用户始终通过一个固定的地址来访问该服务。

定义:

Service 是 Kubernetes 中用于定义一组 Pod 的访问策略的对象。它提供了一个稳定的 IP 地址和 DNS 名称,使得外部服务可以访问这组 Pod

类型:

  • ClusterIP(默认):仅在集群内部可访问

  • NodePort:通过节点的 IP 和端口对外提供服务

  • LoadBalancer:集成云服务商的负载均衡器,对外提供服务

  • ExternalName:通过 CNAME 记录将服务映射到外部服务地址

特性:

  • 提供负载均衡和服务发现

  • 定义了一组 Pod 的访问策略

  • Service 为 Pod 提供了一个稳定的访问地址,即使 Pod 的 IP 地址发生变化,Service 的访问地址保持不变

2.1.4 Deployment

用于管理无状态服务的控制器。它定义了Pod 的模板,指定副本数量,并提供滚动更新和重建策略。滚动更新可以按照用户定义的速率逐步替换旧的 Pod,确保服务无中断;重建则是直接停掉所有 Pod 再进行升级,适合非关键任务。例如,对于一个 Web 服务应用,使用 Deployment 可以方便地管理其多个副本的部署和更新。

定义:
Deployment 用于管理无状态应用的部署和更新,确保指定数量的 Pod 副本始终处于运行状态

特点:

  • 支持滚动更新和回滚,可以平滑地更新应用版本

  • 可以定义 Pod 的副本数量,自动管理 Pod 的创建和销毁

  • 与 Service 结合使用,实现应用的高可用和负载均衡

2.1.5 StatefulSet

用于管理需要状态持久化的Pod。每个 Pod 都有一个唯一的标识符,生命周期有序,Pod启动和停止遵循固定模式。适用于数据库(如 MySQL、MongoDB)、消息队列(如 Kafka、RabbitMQ)、分布式存储系统等需要保存状态信息的应用。与 Deployment 管理的无状态服务不同,StatefulSet管理的服务需要保证数据的一致性和持久性。

定义:
StatefulSet 用于管理有状态应用的部署和更新,为每个 Pod 提供稳定的网络标识和存储

特点:

  • 每个 Pod 有一个唯一的序号和稳定的网络标识,便于管理有状态应用

  • 与 PersistentVolume 结合使用,实现数据的持久化存储

  • 支持有序的部署和更新,确保有状态应用的稳定运行

2.1.6 DaemonSet

确保每个节点都运行一个Pod。通常用于集群范围的服务,比如日志收集(如 Fluentd)、监控服务(如Prometheus Node Exporter)等。它可以保证在集群的每个节点上都有一个特定的Pod 在运行,以便收集节点的日志信息或监控节点的状态。

定义:
DaemonSet 确保集群中的每个节点都运行一个 Pod 副本,常用于部署日志收集、监控等系统级服务

特点:
自动在新加入的节点上创建 Pod 副本,确保每个节点都有相应的服务运行

2.1.7 Job&CronJob

***Job:***用于运行一次性任务,直到任务完成为止

***CronJob:***用于定时运行任务,类似于 Linux 的 cron 任务

***特点:***可以定义任务的执行频率和策略,自动管理任务的执行和重试

2.1.8 ConfigMap&Secret

ConfigMap:
用于存储配置数据,如环境变量、配置文件等,可以被 Pod 以文件或环境变量的形式使用

Secret:
用于存储敏感数据,如密码、密钥、证书等,可以被 Pod 以文件或环境变量的形式使用

特点:将配置和敏感数据与应用程序分离,提高应用程序的可移植性和安全性.

2.1.9 PV&PVC

***PersistentVolume(PV):***集群资源,用于提供持久化存储,可以是本地磁盘、网络存储等

***PersistentVolumeClaim(PVC):***用户对存储的请求,声明所需的存储容量、访问模式等,由 Kubernetes 自动匹配合适的 PV

***特点:***实现数据的持久化存储,与 Pod 的生命周期解耦,确保数据不会因 Pod 的重启或删除而丢失

2.1.10 Namespace

Namespace(命名空间) 是 Kubernetes 中用于将集群资源划分为多个逻辑分区。它提供了一个机制,用于在同一个物理集群中运行多个虚拟集群。

特性:

  • 提供资源的逻辑隔离。

  • 支持资源配额和限制。

  • 可以为不同的团队或项目分配独立的 Namespace。

2.1.11 Ingress

Ingress 是 Kubernetes 中用于管理外部访问的对象。它提供了一个统一的入口,用于将外部请求路由到集群内的服务。

特性:

  • 提供负载均衡、SSL 终止和名称虚拟主机。

  • 支持路径和主机名的路由规则。

  • 可以集成第三方 Ingress 控制器,如 Nginx、Traefik 和 HAProxy。

2.2 k8s核心组件

2.2.1 Master 组件

API Server

是集群的统一入口,提供了 RESTful API 接口,用于与 Kubernetes 集群进行交互。它处理所有的管理操作,包括创建、更新、删除和查询资源。
***作用:***接收来自用户和其他组件的请求,是操作 Kubernetes 资源的必经之路,保障集群的安全性和合法性。例如,开发人员通过kubectl命令行工具与 API Server 交互来部署应用。


etcd

是一个分布式键值存储系统,用于存储 Kubernetes 集群的所有数据。它是 Kubernetes 的“大脑”,存储了集群的状态和配置信息。
***作用:***存储如节点信息、Pod 定义、服务配置等数据。任何集群状态的改变都会记录在 etcd 中,是集群数据的核心存储,确保数据在节点间同步,维持集群的状态稳定


Controller Manager

Controller Manager 是 Kubernetes 的控制器管理器,负责运行各种控制器,实现集群自动化管理。负责监控集群的状态并确保实际状态与期望状态一致。
***作用:***通过 ReplicaSet 控制器保证 Pod 副本数稳定。不同的控制器协同工作,让集群根据用户定义自动调整状态,像自动修复故障 Pod,保证应用正常运行。


Scheduler

Scheduler 是 Kubernetes 的调度器,负责将 Pod 分配到适当的节点上运行。它根据资源需求、节点状态和调度策略进行决策
***作用:***依据节点资源(CPU、内存等)、亲和性和 Pod 服务质量要求,合理安排 Pod 运行节点,提高资源利用率,保证 Pod 正常运行


Calico

Calico 是开源容器网络插件,是一个纯三层的虚拟网络,它没有复用docker的docker0网桥,而是自己实现的,calico网络不对数据包进行额外封装,不需要NAT和端口映射。
***作用:***网络通信:为 Pod 分配 IP 地址,使 Pod 能通信,不管是否在同一节点。支持 IPIP 和 BGP 模式,前者简单适用于简单场景,后者高效适用于大规模复杂场景。
***网络安全:***通过定义策略控制 Pod 流量,基于标签设置规则,防止恶意访问。在多租户环境中能隔离租户应用,避免干扰和风险。


Metrics-server

Metrics-Server是集群核心监控数据的聚合器,提供节点和Pod的资源使用情况的信息,包括CPU 和内存的指标。
***作用:***通过kubelet 获取 node 和 Pod的CPU,内存等监控数据。为调度器、弹性控制器、以及Dashboard等UI组件提供数据来源

2.2.2 Node 组件

kubelet

Kubelet 是 Kubernetes 的节点代理,负责在节点上运行和管理容器。它确保节点上的容器按照期望状态运行
***作用:***Kubelet 与 API Server 通信,获取 Pod 的配置信息,并确保容器按照配置信息运行。


kube-proxy

Kube-proxy 是 Kubernetes 的网络代理,负责处理节点上的网络流量。它确保服务的网络连接和负载均衡
***作用:***就像节点内的 “流量调度员”。例如在一个微服务架构的集群中,有多个相同服务的 Pod,kube-proxy 会把对这个服务的请求均匀地分配到这些 Pod 上,确保服务的高可用性和高效性。在服务更新或 Pod 数量变化时,也能及时调整请求分配策略。


容器运行时(如 Docker、containerd、CRI-O)

是容器运行的实际执行者,包括镜像下载、容器创建和启动,以及生命周期管理。
***作用:***按照 kubelet 的要求,获取镜像并创建容器,提供资源隔离和限制,保证容器在规定资源范围内运行,是容器运行的基础。

2.2.3 日志与监控组件

Fluentd/Elasticsearch/Kibana (EFK)

日志收集和可视化系统.
***作用:***Fluentd负责将日志从Pod中收集并发送到Elasticsearch,Elasticsearch存储和索引日志数据,Kibana用于日志的可视化和分析,帮助用户快速定位问题和了解应用的运行状态.


Prometheus

开源的监控和告警系统.
***作用:***专门为Kubernetes环境设计,能够收集和存储时间序列数据,提供强大的查询和告警功能,监控集群的资源使用情况、性能指标、应用状态等,帮助用户及时发现和处理问题.


Grafana

开源的可视化平台.
***作用:***常与Prometheus结合使用,提供丰富的图表和仪表盘功能,帮助用户直观地展示监控数据,进行数据分析和可视化,提高监控的可视化效果和用户体验.

k8s架构图-ProcessOn

2.3 k8s集群管理工具

2.3.1 集群服务端口

软件 端口范围 用途
api-server 6443 所有组件接口服务
etcd 2379-2380 核心数据库
kube-scheduler 10259 调度服务
kube-container-manager 10257 控制器管理服务
kubelet 10250 节点代理服务
kube-proxy 10256 网络通讯与负载均衡

2.3.2 kubeadm集群管理工具

kubeadm 是一个用于快速初始化 Kubernetes 集群的工具。它能够自动化完成许多复杂的集群初始化步骤。

命令选项 描述
init 初始化k8s控制平面节点
join 用于将节点加入到已初始化的 Kubernetes 集群
upgrade apply 用于升级 Kubernetes 集群的版本
upgrade node 用于升级集群中的节点
config 用于管理集群配置
token 用于管理集群加入令牌
reset 用于重置节点状态

2.3.3 kubectl集群命令行工具

kubectl 是 Kubernetes 集群的命令行工具,用于与 Kubernetes API Server 进行交互。它可以完成几乎所有与集群资源管理相关的操作。例如,用户可以使用 kubectl 创建、删除、更新和查看各种 Kubernetes 资源,如 Pod、Service、Deployment、StatefulSet 等。

命令选项 描述
cluster-info 显示集群的相关配置信息
api-resources 查看当前服务器上的所有的资源对象
api-versions 查看当前服务器上的所有资源对象的版本
config 管理当前节点上的认证信息

2.3.4 命令的自动补全操作

kubeadm 命令行工具启用 Bash 自动补全功能

[root@master ~]# source <(kubeadm completion bash|tee /etc/bash_completion.d/kubeadm)

kubectl 命令行工具启用 Bash 自动补全功能

[root@master ~]# source <(kubectl completion bash|tee /etc/bash_completion.d/kubectl)

2.4 k8s集群部署示例

2.4.1 所需主机清单

主机名 IP地址 最低配置
harbor 192.168.10.240 2C/4G
master 192.168.10.10 2C/4G
node01 192.168.10.11 2C/2G
node02 192.168.10.12 2C/2G
node03 192.168.10.13 2C/2G

2.4.2 部署Harbor仓库

1. 部署docker
# 安装部署 docker,事先准备好yum源
[root@harbor ~]# dnf install -y docker-ce
[root@harbor ~]# systemctl enable --now docker
2. 导入harbor项目镜像
# 导入 harbor 项目镜像,链接: https://pan.baidu.com/s/1388UkBJ754E_kNt2XM6jHw?pwd=1234 提取码: 1234
[root@harbor ~]# tar -zxf harbor-v2.9.2.tgz -C /usr/local/
[root@harbor ~]# cd /usr/local/harbor
[root@harbor harbor]# docker load -i harbor.v2.9.2.tar.gz

# 创建 https 证书
[root@harbor harbor]# mkdir tls
[root@harbor harbor]# openssl genrsa -out tls/cert.key 2048
[root@harbor harbor]# openssl req -new -x509 -days 3652 -key tls/cert.key -out tls/cert.crt \
                                  -subj "/C=CN/ST=BJ/L=BJ/O=Tedu/OU=NSD/CN=harbor" \
                                  -addext "subjectAltName = IP:192.168.10.240"
3. 启动项目
# 修改配置文件
[root@harbor harbor]# cp harbor.yml.tmpl harbor.yml
[root@harbor harbor]# vim harbor.yml
05:    hostname: 192.168.10.240
08:    # http:
10:      # port: 80
17:    certificate: /usr/local/harbor/tls/cert.crt
18:    private_key: /usr/local/harbor/tls/cert.key
36:    harbor_admin_password: <登录密码>

# 预安装环境检查,生成项目文件
[root@harbor harbor]# /usr/local/harbor/prepare

# 创建并启动项目
[root@harbor harbor]# docker compose -f docker-compose.yml up -d

# 添加开机自启动
[root@harbor harbor]# chmod 0755 /etc/rc.d/rc.local
[root@harbor harbor]# echo "/usr/bin/docker compose -p harbor start" >>/etc/rc.d/rc.local
4. 验证项目
# 查看项目
[root@harbor ~]# docker compose ls -a
NAME                STATUS              CONFIG FILES
harbor              running(9)          /usr/local/harbor/docker-compose.yml

# 查看容器状态
[root@harbor ~]# docker compose -p harbor ps
NAME                COMMAND                  SERVICE       STATUS
harbor-core         "/harbor/entrypoint.…"   core          running (healthy)
harbor-db           "/docker-entrypoint.…"   postgresql    running (healthy)
harbor-jobservice   "/harbor/entrypoint.…"   jobservice    running (healthy)
harbor-log          "/bin/sh -c /usr/loc…"   log           running (healthy)
harbor-portal       "nginx -g 'daemon of…"   portal        running (healthy)
nginx               "nginx -g 'daemon of…"   proxy         running (healthy)
redis               "redis-server /etc/r…"   redis         running (healthy)
registry            "/home/harbor/entryp…"   registry      running (healthy)
registryctl         "/home/harbor/start.…"   registryctl   running (healthy)
5. Harbor仓库管理
容器管理命令 说明
docker login 登录私用镜像仓库
docker logout 退出登录
#=======================Harbor仓库管理示例=====================
# 在带有docker软件的设备上面做管理实验
# 添加主机配置
[root@docker ~]# vim /etc/hosts
192.168.10.240  harbor

# 添加私有仓库配置
[root@docker ~]# vim /etc/docker/daemon.json
{
    "registry-mirrors": ["https://harbor:443", "其他镜像仓库"],
    "insecure-registries":["harbor:443", "其他镜像仓库"]
}
[root@docker ~]# systemctl restart docker

# 登录 harbor 仓库
[root@docker ~]# docker login harbor:443
Username: <登录用户>
Password: <登录密码>
... ...
Login Succeeded
# 认证信息记录文件
[root@docker ~]# cat /root/.docker/config.json 
{
    "auths": { ... ... }
}
# 退出登录
[root@docker ~]# docker logout harbor:443
Removing login credentials for harbor:443

# =====================上传镜像到harbor仓库中==================
# 设置标签
[root@docker ~]# docker tag myos:httpd harbor:443/private/httpd:latest
# 没有登录上传失败
[root@docker ~]# docker push harbor:443/private/httpd:latest
65dbea0a4b39: Preparing 
unauthorized: unauthorized to access repository ......

# 登录成功后才可以上传
[root@docker ~]# docker login harbor:443
Username: <登录用户>
Password: <登录密码>

Login Succeeded
# 上传成功
[root@docker ~]# docker push harbor:443/private/httpd:latest
The push refers to repository [harbor:443/private/httpd]
......

# 上传镜像到 library 项目
[root@docker ~]# docker tag myos:latest harbor:443/library/myos:latest
# 没有权限上传失败
[root@docker ~]# docker push harbor:443/library/myos:latest
The push refers to repository [harbor:443/library/myos]
65dbea0a4b39: Preparing 
unauthorized: unauthorized to access repository: 
......

# 赋权后重新上传镜像
[root@docker ~]# docker push harbor:443/library/myos:latest
The push refers to repository [harbor:443/library/myos]
......

2.4.3 安装控制节点

1. 环境配置
# 禁用 firewall 和 swap
[root@master ~]# sed '/swap/d' -i /etc/fstab
[root@master ~]# swapoff -a
[root@master ~]# dnf remove -y firewalld-*
2. 安装软件包
[root@master ~]# vim /etc/hosts
192.168.10.240  harbor
192.168.10.10   master
192.168.10.11   node01
192.168.10.12   node02
192.168.10.13   node03

# 配置好yum仓库(要有docker和k8s相关的软件包),然后安装部署k8s所需的软件包
[root@master ~]# dnf install -y kubeadm kubelet kubectl containerd.io ipvsadm ipset iproute-tc
# 导出containerd默认的配置文件,然后进行修改
[root@master ~]# containerd config default >/etc/containerd/config.toml
[root@master ~]# vim /etc/containerd/config.toml
61:     sandbox_image = "harbor:443/k8s/pause:3.9"
125:    SystemdCgroup = true
154 行新插入:
        [plugins."io.containerd.grpc.v1.cri".registry.mirrors."docker.io"]
          endpoint = ["https://192.168.10.240:443"]
        [plugins."io.containerd.grpc.v1.cri".registry.mirrors."harbor:443"]
          endpoint = ["https://192.168.10.240:443"]
        [plugins."io.containerd.grpc.v1.cri".registry.configs."192.168.10.240:443".tls]
          insecure_skip_verify = true
[root@master ~]# systemctl enable --now kubelet containerd
3. 配置内核参数
# 加载内核模块
[root@master ~]# vim /etc/modules-load.d/containerd.conf
br_netfilter
xt_conntrack
[root@master ~]# systemctl start systemd-modules-load.service 

# 设置内核参数
[root@master ~]# vim /etc/sysctl.d/99-kubernetes-cri.conf
net.ipv4.ip_forward = 1
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.netfilter.nf_conntrack_max = 1000000
[root@master ~]# sysctl -p /etc/sysctl.d/99-kubernetes-cri.conf
4. 导入k8s所需的镜像
# 先安装docker
[root@master ~]# dnf install -y docker-ce
# 配置docker指定镜像仓库,要事先准备好harbor仓库
[root@master ~]# vim /etc/docker/daemon.json 
{
    "registry-mirrors":["https://harbor:443"],
    "insecure-registries":["harbor:443"]
}
[root@master ~]# systemctl enable --now docker
[root@master ~]# docker info

# 查看当前k8s所需的镜像
[root@master ~]# kubeadm config images list
I0506 11:49:25.749132    9305 version.go:256] remote version is much newer: v1.33.0; falling back to: stable-1.29
registry.k8s.io/kube-apiserver:v1.29.15
registry.k8s.io/kube-controller-manager:v1.29.15
registry.k8s.io/kube-scheduler:v1.29.15
registry.k8s.io/kube-proxy:v1.29.15
registry.k8s.io/coredns/coredns:v1.11.1
registry.k8s.io/pause:3.9
registry.k8s.io/etcd:3.5.10-0

# 登录仓库,并上传k8s所需的镜像
[root@master ~]# docker login harbor:443 
Username: <登录用户>
Password: <登录密码>
Login Succeeded

# https://download.csdn.net/download/hehe228/90768818,在该地址有k8s-v1.29.2版本所需要的初始化镜像tar包
[root@master ~]# docker load -i init/v1.29.2.tar.xz
[root@master ~]# docker images|while read i t _;do
    [[ "${t}" == "TAG" ]] && continue
    [[ "${i}" =~ ^"harbor:443/".+ ]] && continue
    docker tag ${i}:${t} harbor:443/k8s/${i##*/}:${t}
    docker push harbor:443/k8s/${i##*/}:${t}
    docker rmi ${i}:${t} harbor:443/k8s/${i##*/}:${t}
done
5. master安装
# 可以参考https://download.csdn.net/download/hehe228/90768834,事先准备好初始化的yaml文件,也可以使用kubeadm命令生成一个初始化yaml文件,
[root@master ~]# vim /root/init/init.yaml
13:  advertiseAddress: 192.168.10.10 # 修改成master主机的IP地址

# 测试系统环境
[root@master ~]# kubeadm init --config=init/init.yaml --dry-run 2>error.log
# 如果文件为空,说明测试初始化没有什么大问题
[root@master ~]# cat error.log

# 主控节点初始化
[root@master ~]# rm -rf error.log /etc/kubernetes/tmp
# 这里即使上一步测试没有问题,有的时候这一步也可能出错,一般都是环境的问题
[root@master ~]# kubeadm init --config=init/init.yaml |tee init/init.log

# 管理授权
[root@master ~]# mkdir -p $HOME/.kube
[root@master ~]# sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
[root@master ~]# sudo chown $(id -u):$(id -g) $HOME/.kube/config

# 验证安装结果
[root@master ~]# kubectl get nodes
NAME     STATUS     ROLES           AGE   VERSION
master   NotReady   control-plane   19s   v1.29.2
6. 安装网络插件
# https://download.csdn.net/download/hehe228/90769045,该地址有calico所需的镜像,https://download.csdn.net/download/hehe228/90769052,该地址有calico所需的yaml文件
[root@master ~]# ls calico/
calico.tar.xz  calico.yaml

[root@master calico]# docker load -i calico.tar.xz
# 上传到harbor仓库
[root@master calico]# docker images|while read i t _;do
    [[ "${t}" == "TAG" ]] && continue
    [[ "${i}" =~ ^"harbor:443/".+ ]] && continue
    docker tag ${i}:${t} harbor:443/plugins/${i##*/}:${t}
    docker push harbor:443/plugins/${i##*/}:${t}
    docker rmi ${i}:${t} harbor:443/plugins/${i##*/}:${t}
done

# 安装calico网络插件
# 修改yaml文件,指定到harbor仓库
[root@master calico]# sed -ri 's,^(\s*image: )(.*/)?(.+),\1harbor:443/plugins/\3,' calico.yaml

[root@master calico]# kubectl apply -f calico.yaml
[root@master calico]# kubectl get nodes
NAME     STATUS   ROLES           AGE   VERSION
master   Ready    control-plane   23m   v1.29.2

2.4.4 安装计算节点

获取凭证
# 查看 token
[root@master ~]# kubeadm token list
TOKEN                     TTL         EXPIRES                
abcdef.0123456789abcdef   23h         2022-04-12T14:04:34Z
# 删除 token
[root@master ~]# kubeadm token delete abcdef.0123456789abcdef
bootstrap token "abcdef" deleted
# 创建 token,并记录返回的命令
[root@master ~]# kubeadm token create --ttl=0 --print-join-command
kubeadm join 192.168.10.10:6443 --token fhf6gk.bhhvsofvd672yd41 --discovery-token-ca-cert-hash sha256:ea07de5929dab8701c1bddc347155fe51c3fb6efd2ce8a4177f6dc03d5793467
# 获取 hash 值 [1、在创建 token 时候显示  2、使用 openssl 计算]
[root@master ~]# openssl x509 -pubkey -in /etc/kubernetes/pki/ca.crt |openssl rsa -pubin -outform der |openssl dgst -sha256 -hex
node安装
# https://github.com/adminzhh/ansible_playbook/,该地址是使用ansible自动化来部署node节点的,要在master节点上面执行。
[root@node ~]# 控制节点(安装步骤 1)
[root@node ~]# 控制节点(安装步骤 2)
[root@node ~]# 控制节点(安装步骤 3)

[root@node ~]# kubeadm join 192.168.10.10:6443 --token <你的token> --discovery-token-ca-cert-hash sha256:
#------------------------ 在 master 节点上验证---------------------------
[root@master ~]# kubectl get nodes
NAME        STATUS   ROLES           AGE   VERSION
master      Ready    control-plane   76m   v1.29.2
node01   Ready    <none>          61s   v1.29.2
验证节点状态
# 验证节点工作状态
[root@master ~]# kubectl get nodes
NAME        STATUS   ROLES           AGE   VERSION
master      Ready    control-plane   99m   v1.29.2
node01   Ready    <none>          23m   v1.29.2
node02   Ready    <none>          57s   v1.29.2
node03   Ready    <none>          57s   v1.29.2

# 验证容器工作状态,kube-system空间里的pod都是集群的核心pod,状态必须都得为Running运行状态
[root@master ~]# kubectl -n kube-system get pods
NAME                                      READY   STATUS    RESTARTS   AGE
calico-kube-controllers-fc945b5f7-p4xnj   1/1     Running   0          77m
calico-node-6s8k2                         1/1     Running   0          59s
calico-node-bxwdd                         1/1     Running   0          59s
calico-node-d5g6x                         1/1     Running   0          77m
calico-node-sjngw                         1/1     Running   0          24m
coredns-844c6bb88b-89lzt                  1/1     Running   0          59m
coredns-844c6bb88b-qpbvk                  1/1     Running   0          59m
etcd-master                               1/1     Running   0          70m
kube-apiserver-master                     1/1     Running   0          70m
kube-controller-manager-master            1/1     Running   0          70m
kube-proxy-5xjzw                          1/1     Running   0          59s
kube-proxy-9mbh5                          1/1     Running   0          59s
kube-proxy-g2pmp                          1/1     Running   0          99m
kube-proxy-l7lpk                          1/1     Running   0          24m
kube-scheduler-master                     1/1     Running   0          70m

2.5 k8s资源对象与管理

[!IMPORTANT]

k8s中把可以创建或配置的应用和服务称为资源对象,我们在集群中创建的Pod、负载均衡、存储、网络服务等都是资源对象,对象可以看作是资源的运行实例

如何创建资源对象?

  • 简单资源对象可以使用kubectI直接创建

  • 高级资源对象需要使用资源清单文件创建

2.5.1 kubectl子命令使用

子命令 说明 备注
run 创建pod对象 创建即运行,没有停止的概念
create 创建资源对象 不能创建pod
get 查看资源对象的状态信息 可选参数:-o 显示格式
describe 查询资源对象的属性信息
logs 查看容器的报错信息 可选参数:-c容器名称
exec 在某一个容器内执行特定的命令 可选参数:-c容器名称
cp 在容器和宿主机之间拷贝文件/目录 可选参数:-c容器名称
delete 删除资源对象 可选参数:-f文件名称

kubectl create <资源对象> [选项/参数]命令创建资源

示例:

# 创建名称空间资源对象
[root@master ~]# kubectl create namespace work
namespace/work created

# 查看名称空间
[root@master ~]# kubectl get namespaces
NAME              STATUS   AGE
default           Active   39h
kube-node-lease   Active   39h
kube-public       Active   39h
kube-system       Active   39h
work              Active   11s
查看系统命名空间

查看命名空间中的资源对象:kubectl get pods -n kube-system

  • default 默认的命名空间,不声明命名空间的Pod都在这里

  • kube-node-lease为高可用提供心跳监视的命名空间

  • kube-public 公共数据,所有用户都可以读取它

  • kube-system 系统服务对象所使用的命名空间

k8s系统核心服务都运行在kube-system名称空间中

创建Pod资源对象

kubectl -n <命名空间名> run --image=<镜像名:标签>

示例:

# 在 work 名称空间创建 Pod
[root@master ~]# kubectl -n work run myhttp --image=myos:httpd
pod/myhttp created

# 查询资源对象
[root@master ~]# kubectl -n work get pods -o wide
NAME     READY   STATUS    RESTARTS   AGE   IP           NODE
myhttp   1/1     Running   0          3s    10.244.2.2   node02
Pod状态分析

Pod的status字段是一个PodStatus的对象,Pod对象总是应该处于其生命进程中以下几个相位(phase)之一

  • Pending Pod创建过程中,但它尚未被调度完成

  • Running Pod中所有容器都已经被创建成功,正在运行

  • Completed Pod所有容器都已经成功终止,并不会被重启

  • Failed Pod中的所有容器中至少有一个容器退出是非0状态

  • Unknown 无法正常获取到Pod对象的状态信息,一般防火墙和网络的问题

Pod管理命令

kubectl get 资源类型 [资源名称] [选项/参数]

常用参数

  • 参数 [-o name] 只显示名字

  • 参数 [-o wide] 显示更加的详细信息

  • 参数 [-o yaml/json] 以yaml/json格式显示资源对象

kubectl exec [选项/参数] Pod名称 -- 操作命令

常用参数 -it 分配交互式终端

# 在容器内执行命令,--是一个分隔符,防止命令参数被误解析
[root@master ~]# kubectl exec -it myweb -- ls 
index.html  info.php

[root@master ~]# kubectl exec -it myweb -- bash
[root@myweb html]# ifconfig eth0

kubectl cp [选项/参数] 原文件 目标文件

pod的路径格式为: [Pod名称:绝对路径]

# 与容器进行文件或目录传输
[root@master ~]# kubectl cp myweb:/etc/yum.repos.d /root/aaa
tar: Removing leading `/' from member names
[root@master ~]# tree /root/aaa
/root/aaa
├── local.repo
├── Rocky-AppStream.repo
├── Rocky-BaseOS.repo
└── Rocky-Extras.repo

0 directories, 4 files
[root@master ~]# kubectl -n work cp /etc/passwd myhttp:/root/mima
[root@master ~]# kubectl -n work exec -it myhttp -- ls /root/
mima

kubectl delete [选项/参数] 资源类型 资源名称

集群中所有资源都可用delete命令删除

# 删除资源对象
[root@master ~]# kubectl delete pods myweb 
pod "myweb" deleted

# 删除 work 名称空间下所有 Pod 对象
[root@master ~]# kubectl -n work delete pods --all
pod "myhttp" deleted

# 删除名称空间
[root@master ~]# kubectl delete namespaces work 
namespace "work" deleted

2.5.2 排错三剑客

get子命令

kubectl get 资源类型 [资源名称] [选项/参数]

主要看状态是否正常,不正常使用describe、logs继续排查

# 查看 Pod 资源对象
[root@master ~]# kubectl get pods
NAME     READY   STATUS    RESTARTS   AGE
myweb    1/1     Running   0          10m
# 查看名称空间
[root@master ~]# kubectl get namespaces 
NAME              STATUS   AGE
default           Active   39h
kube-node-lease   Active   39h
kube-public       Active   39h
kube-system       Active   39h
# 查看名称空间中的 Pod 信息
[root@master ~]# kubectl -n kube-system get pods
NAME                             READY   STATUS    RESTARTS      AGE
etcd-master                      1/1     Running   0             39h
kube-apiserver-master            1/1     Running   0             39h
kube-controller-manager-master   1/1     Running   0             39h
kube-scheduler-master            1/1     Running   0             39h
describe子命令

kubectl describe 资源类型 [资源名称] [选项/参数]

Events下是事务日志,常用于排错

# 查看资源对象的配置信息
[root@master ~]# kubectl -n work describe pod myhttp
Name:             myhttp
Namespace:        work
Priority:         0
Service Account:  default
Node:             node02/192.168.10.12
... ...
Events:
  Type    Reason     Age   From               Message
  ----    ------     ----  ----               -------
  Normal  Scheduled  7s    default-scheduler  Successfully assigned work/myhttp to node02
  Normal  Pulling    6s    kubelet            Pulling image "myos:httpd"
  Normal  Pulled     2s    kubelet            Successfully pulled image "myos:httpd" in 4.495s (4.495s including waiting)
  Normal  Created    2s    kubelet            Created container myhttp
  Normal  Started    2s    kubelet            Started container myhttp
log子命令

kubectl logs [选项/参数]

没有输出就是没错

# 查看容器日志
[root@master ~]# kubectl -n work logs myhttp
[root@master ~]# 

2.5.3 资源清单文件管理

常用子命令
子命令 说明 备注
create 创建文件中定义的资源 支持指令式和资源清单文件配置
apply 创建(更新)文件中定义的资源 只支持资源清单文件(声明式)
delete 删除文件中定义的资源 支持指令式和资源清单文件配置
replace 更改/替换资源对象 强制重建 --force

最简单的资源清单文件示例:

[root@master metrics]# cat base.yaml
---                     #yaml文件起始标志  
kind: Pod               #当前创建资源的类型
apiVersion: v1          #当前资源对应的版本
metadata:               #属性信息,元数据
  name: myweb           #属性信息,资源的名称
spec:                   #资源的特性描述(规约)
  containers:           #容器资源特征描述
  - name: nginx01       #容器的名称
    image: myos:nginx   #启动容器使用的镜像
status: {}              #资源状态,运行后自动生成

#json格式
[root@master ~]# cat a.json 
{
        "kind": "Pod",
        "apiVersion": "v1",
        "metadata": {"name":"myweb"},
        "spec": {"containers":[{"name":"nginx","image":"myos:nginx"}]},
        "status": {}
}
命令管理示例
# 创建资源对象
[root@master ~]# kubectl create -f myweb.yaml 
# 使用资源清单文件删除
[root@master ~]# kubectl delete -f myweb.yaml
# 创建资源对象
[root@master ~]# kubectl apply -f myweb.yaml 
pod/myweb created
# 更新资源对象
[root@master ~]# kubectl apply -f myweb.yaml 
pod/myweb configured
# 强制重建资源对象
[root@master ~]# kubectl replace --force -f myweb.yaml 
pod "myweb" deleted
pod/myweb created

# 拓展提高
# 与 kubectl apply -f myweb.yaml 功能相同
[root@master metrics]# for i in web{1..5}
> do
> sed -r "s/myweb/$i/" base.yaml
> done | kubectl apply -f -
pod/web1 created
pod/web2 created
pod/web3 created
pod/web4 created
pod/web5 created
[root@master metrics]# kubectl get pods
NAME   READY   STATUS    RESTARTS   AGE
web1   1/1     Running   0          21s
web2   1/1     Running   0          21s
web3   1/1     Running   0          21s
web4   1/1     Running   0          20s
web5   1/1     Running   0          20s

2.5.4 模板与帮助信息

生成模板

资源对象模板使用create生成,生成模板命令:[--dry-run=client -o yaml|json]

示例:

# 获取资源对象模板
[root@master ~]# kubectl create namespace work --dry-run=client -o yaml
apiVersion: v1
kind: Namespace
metadata:
  creationTimestamp: null
  name: work
spec: {}
status: {}
获取帮助信息

kubectl explain 资源对象.层级关系

示例:

# 查询帮助信息
[root@master ~]# kubectl explain Pod.metadata
KIND:       Pod
VERSION:    v1

FIELD: metadata <ObjectMeta> ... ...

  namespace <string>
    Namespace defines the space within which each name must be unique. An empty
    namespace is equivalent to the "default" namespace, but "default" is the
    canonical representation. Not all objects are required to be scoped to a
    namespace - the value of this field for those objects will be empty.

[root@master ~]# kubectl explain Pod.metadata.namespace

2.5.6 多容器Pod

示例:

[root@master ~]# cat mynginx.yaml 
---
kind: Pod
apiVersion: v1
metadata:
  name: mynginx
  namespace: default
spec:
  containers:              #容器资源是个数组
  - name: nginx            
    image: myos:nginx
  - name: php              #容器的名称唯一,不能重复
    image: myos:php-fpm    #镜像
管理多容器Pod

受多容器配置影响,以下命令需要使用 <-c 容器名>来指定容器
受影响命令:[logs exec cp]

示例:

# 查看日志
[root@master ~]# kubectl logs mynginx -c nginx

# 执行命令
[root@master ~]# kubectl exec -it mynginx -c nginx -- ls /root

# 拷贝文件
[root@master ~]# kubectl cp mynginx:/etc/php-fpm.conf /root/php.conf -c php

2.5.7 Pod自定义任务

自定义命令

创建 Pod 时,可以为其设置启动时要执行的自定义命令,如果配置了自定义命令,那么镜像中自带的默认启动命令将不再执行。

自定义命令设置在command字段下,如果命令有参数,需要填写在args 字段下。

示例:

[root@master ~]# cat mycmd.yaml 
---
kind: Pod
apiVersion: v1
metadata:
  name: mycmd
spec:
  containers:
  - name: http
    image: myos:httpd
    command: ["sh"]       #调用sh命令
    args:                 #设置命令参数
    - -c                  #读取脚本命令
    - |                   #多行字符串格式
      id=${RANDOM}        #脚本指令,注意缩进
      for i in {1..8}
      do 
        echo "${id}: Hello World"
        sleep 6
      done
restartPolicy策略
  1. Pod级别配置
    restartPolicy是Pod级别的设置,会作用于Pod内的所有容器,无法单独为某个容器指定不同策略。
  2. 与控制器协同
    • Deployment/StatefulSet:通常使用Always,确保应用持续运行。
    • Job:通常使用OnFailure,任务失败时自动重试。
    • CronJob:根据需求选择OnFailureNever
  3. 默认值
    未显式指定时,默认值为Always
  4. 该策略有以下三个选项
  • 总是重启 [Always]
  • 失败不重启 [Never]
  • 失败就重启 [OnFailure]

示例:

[root@master ~]# cat mycmd.yaml 
---
kind: Pod
apiVersion: v1
metadata:
  name: mycmd
spec:
  terminationGracePeriodSeconds: 0
  restartPolicy: OnFailure    #失败就重启
  containers:
  - name: http
    image: myos:httpd
    command: ["sh"]
    args:
    - -c
    - |
      id=${RANDOM}   #取随机数
      for i in {1..8}
      do 
        echo "${id}: Hello World"
        sleep 6
      done
      exit $(($id%2))       #退出返回非0,表示失败
terminationGracePeriodSeconds 宽限期策略

1. 核心作用

当 Pod 需要被删除或终止时(如手动删除、滚动更新、节点驱逐等),terminationGracePeriodSeconds 定义了:

  • 宽限期(Grace Period):Kubernetes 在强制杀死容器前,等待容器正常停止的最长时间(单位为秒)。
  • 用途:确保容器有足够时间完成清理操作(如保存状态、关闭连接、释放资源等)。

2. 默认值

  • 默认 30 秒:若未显式设置,Kubernetes 会使用默认值 30
  • 特殊场景:某些控制器(如 Job)可能设置不同的默认值。

示例:

[root@master ~]# cat mycmd.yaml
---
kind: Pod
apiVersion: v1
metadata:
  name: mycmd
spec:
  terminationGracePeriodSeconds: 0  # 设置宽限期
  restartPolicy: OnFailure
  containers:
  ......
activeDeadlineSeconds 策略

1. 核心作用

  • 硬性超时机制:当 Pod 运行时间超过 activeDeadlineSeconds 指定的秒数时,Kubernetes 会立即终止该 Pod 及其所有容器,无论任务是否完成。
  • 适用场景
    • 批处理任务(Job/CronJob)需要限制最大执行时间。
    • 防止因代码逻辑错误(如死循环)导致 Pod 长期占用资源。

为了防止Pod循环死锁,设置Pod运行的最长时间,默认永久,时间到期后会向 Pod 发送 signal,如果 Pod 无法结束就把他强制关闭,并且设置为 Error 状态

2. 与 terminationGracePeriodSeconds 的区别

策略 activeDeadlineSeconds terminationGracePeriodSeconds
作用 限制 Pod 总运行时间 控制 Pod 终止时的优雅退出时间
触发条件 Pod 运行时间 > 设定值 Pod 被删除或终止时
终止行为 立即强制终止(SIGKILL 先发送 SIGTERM,超时后发送 SIGKILL
典型用途 任务超时控制 优雅关闭应用(如保存状态)

示例:

[root@master ~]# vim mycmd.yaml
---
kind: Pod
apiVersion: v1
metadata:
  name: mycmd
spec:
  terminationGracePeriodSeconds: 0
  activeDeadlineSeconds: 60      # 可以执行的最大时长为60s
  restartPolicy: OnFailure
  containers:
  ......

#验证
[root@master ~]# kubectl get pods -w
NAME    READY   STATUS    RESTARTS   AGE
mycmd   1/1     Running   0          7s
mycmd   0/1     Error     0          62s   #60秒左右Pod状态为Error

2.6 Pod调度与标签

2.6.1 什么是调度分配?

在 k8s 中,调度是将 Pod 分配到合适的计算节点上,然后对应节点上的Kubelet 运行这些Pod

kube-scheduler是默认调度器,是集群的核心组件

2.6.2 调度流程

调度器给一个Pod做调度选择包含两个步骤:筛选和优选

  1. 筛选:

首先要筛选出满足pod所有的资源请求的节点,包含cpu,内存,存储,网络,端口号等,如果没有节点能满足 Pod 的需求,Pod 将一直停留在 Pending 状态,直到调度器能够找到合适的节点运行它

  1. 优选:

在优选阶段,调度器会根据打分规则,为每一个可调度节点进行打分。选出其中得分最高的节点来运行于Pod。如果存在多个得分最高的节点,调度器会从中随机选取一个

  1. 绑定

在确定了某个节点运行Pod之后,调度器将这个调度决定通知给 kube-apiserver,这个过程叫做绑定

2.6.3 Pod定向调度

基于节点名称的调度

在创建 Pod 的过程中,我们可以配置相关的调度规则,从而让 Pod 运行在制定的节点上,给pod添加nodeName标签,让 Pod 运行在制定的节点上

[!CAUTION]

注意:如果标签指定的节点无法运行Pod,它不会迁移到其他节点,将一直等待下去

示例:

[root@master ~]# vim myhttp.yaml 
---
kind: Pod
apiVersion: v1
metadata:
  name: myhttp
spec:
  nodeName: node01     # 基于节点名称进行调度
  containers:
  - name: apache
    image: myos:httpd

#查看是否运行在node01节点上
[root@master ~]# kubectl get pods -o wide
NAME      READY   STATUS    RESTARTS   AGE   IP           NODE
myhttp    1/1     Running   0          3s    10.244.1.6   node01

2.6.4 Pod标签调度

Pod标签管理

标签的什么

标签(Labels)是附加到Kubernetes对象上的键值对

标签的用途

  • k8s在创建、删除、修改资源对象的时候可以使用标签来确定要修改的资源对象。在 Pod 调度的任务中,使用标签可以更加灵活的完成调度任务。

  • 标签可以在创建时附加到对象,也可以在创建之后随时添加和修改。标签可以用于组织和选择对象的子集

管理标签语法格式

  • 查看标签:kubectl get 资源类型 [资源名称] --show-labels

  • 设置标签:kubectl label 资源类型 [资源名称] =

  • 删除标签:kubectl label 资源类型 [资源名称] -

  • 使用标签选择:kubectl get 资源类型 -l =

基于标签调度

标签选择运算符

  • 与名称和 UID 不同,标签不支持唯一性。通常,我们希望许多对象携带相同的标签。

  • 通过标签选择运算符,客户端/用户可以识别一组对象。

  • 标签选择运算符可以由多个需求组成。在多个需求的情况下,必须满足所有要求,相当于逻辑与(&&)运算符。

示例:

#给node主机配置标签
[root@master ~]# kubectl label nodes node02 ssd=v1
node/node02 labeled
[root@master ~]# kubectl label nodes node03 ssd=v1
node/node03 labeled
#查看node主机的标签情况
[root@master ~]# kubectl get nodes --show-labels
......
#编写资源子清单文件
[root@master ~]# cat ssd.yaml 
---
kind: Pod
apiVersion: v1
metadata:
  name: ssd
  labels:     #给Pod配置标签
    hh: jj    #定义标签,可以多个
spec:
  nodeSelector:         #在Pod.spec中声明标签选择运算符
    ssd: v1             #选择的标签
  containers:
  - name: mynginx
    image: myos:nginx

#验证结果
[root@master ~]# kubectl apply -f ssd.yaml 
pod/ssd created
[root@master ~]# kubectl get pods -o wide 
NAME   READY   STATUS    RESTARTS   AGE   IP               NODE        NOMINATED NODE   READINESS GATES
http   1/1     Running   0          21m   10.244.147.17    node02   <none>           <none>
ssd    1/1     Running   0          15s   10.244.243.218   node03   <none>           <none>

2.7 Pod生命周期与资源管理

2.7.1 Pod生命周期概述

什么是Pod生命周期?

  • Pod对象自从其创建开始至终止的时间范围称为生命周期

  • 在这段时间中Pod处在不同的状态并可以执行相关操作

生命周期有什么用途

  • 复杂服务运行时有启动顺序、依赖关系等,我们可以在生命周期中配置相关性,解决启动关系依赖等问题

  • 容器服务在启动前或运行过程中需要做的相关操作,例如:配置文件生成、数据预加载、依赖服务的检测、安装等

生命周期中能做什么

  • 必须操作:启动运行main(主)容器

  • 可选操作:设置容器的初始化方法:(InitContainer)

  • 可选操作:配置容器探针:生命探测(IivenessProbe)、就绪探测(readinessProbe)、启动探测(startupProbe)

  • 可选操作:添加事件处理函数:启动后回调(PostStart)、结束前回调(PreStop)

2.7.2 Init容器

  • init容器是一种特殊容器,在Pod内主容器启动之前执行,可以依据需求的不同定义多个

  • init容器可以使用其他镜像,也可以包括一些主容器镜像中不存在的实用工具和安装脚本

  • init容器的生命是有限的,不能无休止的运行下去,只有在初始化容器执行完成以后才会启动主容器,即只要初始化容器的退出状态代码非0,则不会启动主容器

init容器和普通容器非常像,除了以下三点

  • 顺序执行,每个容器都必须等待上一个容器成功完成执行,如果init容器失败,kubelet会不断地重启该init容器,直到该容器成功为止

  • 如果Pod对应的restartPolicy值为Never,并且Pod的init容器失败,则k8s会将整个Pod状态设置为失败,主容器将无法执行。

  • 它们必须全部运行到生命周期结束

示例:

[root@master ~]# vim web1.yaml
---
kind: Pod
apiVersion: v1
metadata:
  name: web1
spec:
  restartPolicy: Never       # 任务失败不重启
  initContainers:            # 定义初始化任务
  - name: task1              # 如果初始化任务失败,主容器不会启动
    image: myos:latest       # 初始化可以使用不同的镜像
    command: ["sh"]          # 任务,一般用脚本实现
    args:                    # 任务
    - -c                     # 任务
    - |                      # 任务
      ID=${RANDOM}           # 任务
      echo "${ID}"           # 任务
      sleep 3                # 任务
      exit $((ID%2))         # 状态 0 成功,非0失败,如果失败会重新执行初始化
  - name: task2
    image: myos:latest
    command: ["sh"]
    args:
    - -c
    - |
      ID=${RANDOM}
      echo "${ID}"
      sleep 3
      exit $((ID%2))
  containers:
  - name: web
    image: myos:httpd

# 重建Pod
[root@master ~]# kubectl replace --force -f web1.yaml
pod "web1" deleted 
pod/web1 replaced

# 必须所有init容器都成功,否则Pod创建失败
# 初始化任务失败,因为设置的restartPolicy策略是从不重启,所以一旦一个init容器失败,后面的init容器和主容器都不会运行
[root@master ~]# kubectl get pods -w
NAME   READY   STATUS       RESTARTS        AGE
web1   0/1     Init:0/2          0          1s
web1   0/1     Init:1/2          0          3s
web1   0/1     Init:Error        0          5s

2.7.3 容器探针

什么是容器探针(可选配置)

是由kubelet对容器执行的定期诊断和检查

探针类型
  • startupProbe(启动探测):探测目标应用是否已经启动正常

  • livenessProbe(生命探测):探测容器是否能够正常运行

  • readinessProbe(就绪探测):指示容器是否准备好为请求提供服务

容器探针的检查方式共有4种,分别为:

  • exec:在容器内执行指定命令进行检测

  • httpGet:使用HTTP协议诊断服务状态

  • tcpSocket:对指定的IP地址上的端口执行TCP检查

  • gRPC:使用gRPC健康检查协议进行检测,多用于检测用户自己开发的程序或返回状态

每次探测都会获得以下三种结果之一:

  • Success 成功,容器通过了诊断

  • Failure 失败,容器未通过诊断

  • Unknown 未知,诊断失败,不会采取任何行动

startupProbe启动探测类型
  • startupProbe启动探测用于确定容器是否已经启动成功

  • 探测失败:kubelet将杀死容器,依据重启策略执行

  • 如果配置了启动探针,其他所有探针都会被禁用,直到此探针执行成功为止

  • 如果启动探针执行成功,且主容器已经启动,启动探针不会重复执行

  • 如果未定义启动探针,默认为Success状态

使用startupProbe保护慢启动的服务:

基于tcpSocket 的端口检测,只检测端口状态,不需要返回数据,如果端口状态为 Open 则诊断被认为是成功的

示例:

# 用于检测容器启动过程中依赖的某个重要服务,启动成功后结束
[root@master ~]# vim web2.yaml
......
spec:
  containers:
  - name: web
    image: myos:httpd
    startupProbe:                   # 启动探针
      initialDelaySeconds: 60       # 首次检查延时
      periodSeconds: 10             # 检查间隔
      failureThreshold: 6           # 可失败的次数
      tcpSocket:                    # 使用 tcp 协议检测
        port: 80                    # 端口号

[root@master ~]# kubectl apply -f web2.yaml 
pod/web2 created
[root@master ~]# kubectl get pods -w
NAME   READY   STATUS      RESTARTS      AGE
web2   0/1     Running     0             7s
web2   0/1     Running     1 (1s ago)    31s
web2   0/1     Running     1 (10s ago)   40s
web2   1/1     Running     1 (11s ago)   70s #60s+10s后检测到容器的80端口,该容器就会成功创建
livenessProbe生命探测类型
  • livenessProbe生命探测用于确定容器是否处于运行状态

  • livenessProbe在Pod的全部生命周期中运行,如果发现资源无法获取,kubelet将杀死容器,依据重启策略执行

  • 如果没有设置restartPolicy默认使用镜像重建容器

  • livenssProbe检测失败重启容器的行为会重新激活startupProbe但不会重新执行InitContainer

  • 如果未定义生命探针,默认为Success状态

使用httpGet配置livenessProbe检测

在httpGet的检测中,kubelet目标IP上指定的端口和路径执行HTTP GET请求。如果响应的状态码大于等于200且小于400,则诊断被认为是成功的

示例:

# 判断某个核心资源是否可用,在 Pod 的全部生命周期中(重启)
[root@master ~]# vim web2.yaml
......
spec:
  containers:
  - name: web
    image: myos:httpd
    startupProbe:
      initialDelaySeconds: 60
      periodSeconds: 10
      failureThreshold: 6
      tcpSocket:
        port: 80
    livenessProbe:                # 定义存活探针
      timeoutSeconds: 3           # 服务影响超时
      httpGet:                    # 使用 HTTP 协议检测
        path: /info.php           # 请求的 URL 路径
        port: 80                  # 服务端口号
        
[root@master ~]# kubectl get pods -w
NAME   READY   STATUS       RESTARTS   AGE
web2   0/1     Running      0          2s
web2   1/1     Running      0          71s
web2   0/1     Running      1 (1s ago)   101s  #如果中途info.php文件被删除,它会检测失败重建容器
web2   1/1     Running      1 (11s ago)   171s #很明显执行了启动指针,然后重建容器成功
readinessProbe就绪探测型
  • readinessProbe在Pod的生命周期中一直存在,用来探测容器是否准备好提供服务,就绪探测用于确定容器是否已经准备好接收流量。

  • 配置了就绪探测探针,初始的值默认状态为Failure

  • 探测失败:端点控制器将拒绝Pod所提供的服务(这里是等待,并不重建容器)

  • 如果未定义就绪探针,默认为Success状态

使用exec配置readinessProbe检测:

exec选项用来执行自定义命令的检测,通过返回的状态码判断是否成功,如果$?==0则诊断被认为是成功的,其他判断为失败

# 附加条件检测,在 Pod 的全部生命周期中(禁止调用,不重启)
[root@master ~]# vim web2.yaml
......   
     readinessProbe:              # 定义就绪探针
      failureThreshold: 3         # 失败确认次数
      periodSeconds: 10           # 检测间隔
      exec:                       # 执行命令进行检测
        command:                  # 检测命令
        - sh
        - -c
        - |
          read ver </var/www/html/version.txt
          if (( ${ver:-0} > 2 ));then
             res=0
          fi
          exit ${res:-1}          # 版本大于 2 成功,否则失败

[root@master ~]# kubectl replace --force -f web2.yaml 
pod/web2 created
[root@master ~]# kubectl get pods -w
NAME   READY   STATUS    RESTARTS   AGE
web2   0/1     Running   0          5s
web2   1/1     Running   0          20s
web2   0/1     Running   0          40s # 检测到版本不大于2,就一直等着,不会重启

2.7.4 事件处理函数

postStart和prestop
  • postStart是一个生命周期钩子函数,它与InitContainer不同,postStart是在主容器创建之后被调用。这可以用于执行主容器的初始化工作,通常用于确保容器对外提供服务之前已经完全准备好

  • prestop也是一个生命周期钩子函数,它在容器被停止之前被调用。这可以用于执行清理工作,通常用于确保资源被正确释放,避免数据丢失或损坏

示例:

# 在主容器启动之后或结束之前执行的附加操作
[root@master ~]# vim web3.yaml
---
kind: Pod
apiVersion: v1
metadata:
  name: web3
spec:
  containers:
  - name: web
    image: myos:httpd
    lifecycle:                    # 定义启动后事件处理函数
      postStart:
        exec:
          command:
          - sh
          - -c
          - |
            echo "Hello World" |tee -a /tmp/web.log
            sleep 10
      preStop:                    # 定义关闭前事件处理函数
        exec:
          command:
          - sh
          - -c
          - |
            echo "Bay~" |tee -a /tmp/web.log
            sleep 10

[root@master ~]# kubectl apply -f web3.yaml 
pod/web3 created
[root@master ~]# kubectl exec -it web3 -- bash
[root@web3 html]# cat /tmp/web.log 
Hello World

# 在其他终端执行
[root@master ~]# kubectl delete pods web3
pod "web3" deleted

[root@web3 html]# cat /tmp/web.log 
Hello World
Bay~

2.7.5 Pod资源管理

资源配额

当多个应用共享固定节点数目的集群时,人们担心某些应用无法获得足够的资源,从而影响到其正常运行,我们需要设定一些规则,用来保证应用能获得其运行所需资源

CPU资源类型

  • CPU资源的约束和请求以毫核(m)为单位。在 k8s中1m是最小的调度单元,CPU的一个核心可以看作1000m

  • 如果你有2颗CPU,且每CPU为4核心,那么你的CPU资源总量就是8000m

示例:

lscpu 
Architecture:        x86_64
CPU op-mode(s):      32-bit, 64-bit
Byte Order:          Little Endian
CPU(s):              2   #2个cpu
On-line CPU(s) list: 0,1
Thread(s) per core:  2   #每个核心2个线程数
Core(s) per socket:  1   #每个插槽1个核心

内存资源类型

memory的约束和请求以字节为单位

你可以使用以下单位来表示内存:E、P、T、G、M、k

你也可以使用对应的2的幂数:Ei、Pi、Ti、Gi、Mi、K!

例如,以下表达式所代表的是相同的值:

1K == 1000

1Ki == 1024

资源配额示例:

[root@master ~]# vim app.yaml
---
kind: Pod
apiVersion: v1
metadata:
  name: app
spec:
  containers:
  - name: web
    image: myos:httpd
    resources:                  # 配置资源策略
      requests:                 # 配额策略
        cpu: 1500m              # 计算资源配额
        memory: 600Mi           # 内存资源配额

[root@master ~]# kubectl apply -f app.yaml
pod/app created

[root@master ~]# kubectl describe pods app
......
    Ready:          True
    Restart Count:  0
    Requests:
      cpu:          1500m
      memory:       600Mi
  
# 当查看pod被分配到的节点的信息,会看到配额占用了1500m,但实际cpu并没有这么高,只是被该pod占用了
[root@master ~]# kubectl describe node node03
......
Allocated resources:
  (Total limits may be over 100 percent, i.e., overcommitted.)
  Resource           Requests     Limits
  --------           --------     ------
  cpu                1850m (92%)  0 (0%)
  memory             670Mi (39%)  170Mi (10%)
  ephemeral-storage  0 (0%)       0 (0%)
  hugepages-1Gi      0 (0%)       0 (0%)
  hugepages-2Mi      0 (0%)       0 (0%)
  
# 该node节点只有2000m,如果再到此节点的pod的配额超过了150m,那么pod会起不来
......
  spec:
  nodeName: node03 # 指定pod调度node03上面,做实验
  containers: 
  - name: test-resource
    image: myos:httpd
    resources: 
      requests: 
        cpu: 200m
        memory: 100Mi
[root@master ~]# kubectl apply -f test-resources.yaml
pod/app created
# 会发现调度到node03的pod创建不出来
[root@master ~]# kubectl get pods -w
NAME            READY   STATUS     RESTARTS      AGE
app             1/1     Running    0             11m
test-resource   0/1     OutOfcpu   0             94s
        
资源限额

为什么要使用资源限额?

  • 限额策略是为了防止某些应用对节点资源过度使用,而配置的限制性策略,限额与配额相反,它不检查节点资源的剩余情况,只限制应用对资源的最大使用量

  • 资源限额使用 limits进行配置

  • 资源限额需要与配额共同配置

  • 资源限额必须大于等于资源配额,否则报错

示例:

[root@master ~]# vim app.yaml
---
kind: Pod
apiVersion: v1
metadata:
  name: app
spec:
  containers:
  - name: web
    image: myos:httpd
    resources:                  # 配置资源策略
      limits:                   # 限额策略
        cpu: 600m               # 计算资源限额
        memory: 800Mi           # 内存资源限额
        
[root@master ~]# kubectl apply -f app.yaml 
pod/app created

# 资源清单文件没有设置资源配额,但是查看资源策略时却有,这表明限额和配额同时存在的,资源清单文件中不指定配额的话,默认与限额一致 
[root@master ~]# kubectl describe pods app
......
Ready:          True
    Restart Count:  0
    Limits:
      cpu:     800m
      memory:  600Mi
    Requests:
      cpu:        800m
      memory:     600Mi
节点压力驱逐

kubelet监控集群节点的内存、磁盘空间和文件系统等资源,当这些资源中的其中一个或者多个出现消耗瓶颈的时候,kubelet会主动终止一个或多个Pod,回收资源以防止饥饿的过程

  • 在节点压力驱逐过程期间,kubelet将所选Pod的状态设置为Failed并终止Pod的运行

  • 一般来说,k8s在出发节点压力驱逐时会考虑QoS类别,进行如下处理

    • Guaranteed(保障型):这类Pod有稳定(配限额相等单独配置限额)的资源限额/配额,通常情况下最不易被驱逐
    • Burstable(爆发型):配额值小于限额值或单独配置配额时,它们在节点资源不足时可能会被驱逐
    • BestEffort(尽量型):没有设置配限额的Pod,被认为是首选被驱逐的对象
Quota资源管理

ResourceQuota 是 Kubernetes 中用于 限制命名空间(Namespace)内资源使用量 的机制,主要解决多租户或团队共享集群时的资源公平性和隔离性问题。其核心作用包括:

  1. 防止资源饥饿
    • 避免单个命名空间过度占用集群资源(如 CPU、内存、Pod 数量),影响其他业务。
  2. 成本控制
    • 为不同团队或项目分配明确的资源上限,便于核算资源消耗。
  3. 优先级管理
    • 通过配额区分高/低优先级业务(例如限制 BestEffort Pod 数量,保障 Guaranteed Pod 资源)。
  4. 规范化资源使用
    • 强制要求开发团队在部署时声明资源请求(Requests/Limits),避免资源浪费。

示例:

[root@master 5.17]# cat resourcequota.yml 
---
kind: Namespace
apiVersion: v1
metadata: 
  name: test
spec: {}

---
kind: ResourceQuota  # 全局资源限额对象
apiVersion: v1
metadata: 
  name: quota-1      # 规则名称
  namespace: test    # 规则作用的名称空间
spec:               
  hard:              # 创建强制规则
    pods: 3          # 限制创建资源对象总量
  scopes:            # 配置服务质量的类型
  - BestEffort       # 可选(NotBestEffort)适用于有明确资源设置的Pod,包括Guaranteed和Burstable类型的Pod。

[root@master 5.17]# kubectl apply -f resourcequota.yml 
namespace/test created
resourcequota/quota-1 created
# 查看test名称空间的属性
[root@master 5.17]# kubectl describe namespaces test 
Name:         test
Labels:       kubernetes.io/metadata.name=test
Annotations:  <none>
Status:       Active

Resource Quotas
  Name:    quota-1
  Scopes:  BestEffort
  * Matches all pods that do not have resource requirements set. These pods have a best effort quality of service.
  Resource  Used  Hard
  --------  ---   ---
  pods      0     3

No LimitRange resource.

=======================创建pod,验证效果========================
[root@master 5.17]# cat app.yaml 
---
kind: Pod
apiVersion: v1
metadata: 
  name: app1
  namespace: test
spec: 
  containers:
  - name: app1
    image: myos:httpd
[root@master 5.17]# sed 's/app1/app11/' app.yaml | kubectl apply -f -
pod/app11 created
[root@master 5.17]# sed 's/app1/app12/' app.yaml | kubectl apply -f -
pod/app12 created
[root@master 5.17]# sed 's/app1/app13/' app.yaml | kubectl apply -f -
Error from server (Forbidden): error when creating "STDIN": pods "app13" is forbidden: exceeded quota: quota-1, requested: pods=1, used: pods=3, limited: pods=3
[root@master 5.17]# kubectl describe namespaces test 
Name:         test
Labels:       kubernetes.io/metadata.name=test
Annotations:  <none>
Status:       Active

Resource Quotas
  Name:    quota-1
  Scopes:  BestEffort
  * Matches all pods that do not have resource requirements set. These pods have a best effort quality of service.
  Resource  Used  Hard
  --------  ---   ---
  pods      3     3

No LimitRange resource.

[root@master 5.17]# cat Burstable.yaml 
---
kind: Pod
apiVersion: v1
metadata: 
  name: burstable
spec: 
  containers: 
  - name: burstable
    image: myos:httpd
    resources: 
      limits:  
        cpu: 80m
        memory: 80Mi
# 在test名称空间创建一个Guaranteed类型的pod,就可以创建
[root@master 5.17]# cat Burstable.yaml | kubectl -n test apply -f -

2.8 Pod控制器

2.8.1 Pod的定义

Pod是kubernetes的最小管理单元,在kubernetes中,按照pod的创建方式可以将其分为两类:

  • 自主式pod:kubernetes直接创建出来的Pod,这种pod删除后就没有了,也不会重建

  • 控制器创建的pod:kubernetes通过控制器创建的pod,这种pod删除了之后还会自动重建

2.8.2 什么是Pod控制器

[!IMPORTANT]

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

在kubernetes中,有很多类型的pod控制器,每种都有自己的适合的场景,常见的Pod控制器有下面这些:

  • ReplicationController(RC):比较原始的pod控制器,已经被废弃,由ReplicaSet替代

  • ReplicaSet(RS):保证副本数量一直维持在期望值,并支持pod数量扩缩容,镜像版本升级

  • Deployment:通过控制ReplicaSet来控制Pod,并支持滚动升级、回退版本

  • Horizontal Pod Autoscaler(HPA):可以根据集群负载自动水平调整Pod的数量,实现削峰填谷

  • DaemonSet:在集群中的指定Node上运行且仅运行一个副本,一般用于守护进程类的任务(比如部署node_exporter)

  • Job:它创建出来的pod只要完成任务就立即退出,不需要重启或重建,用于执行一次性任务

  • Cronjob:它创建的Pod负责周期性任务控制,不需要持续后台运行

  • StatefulSet:管理有状态应用

2.8.3 ReplicaSet(RS)

ReplicaSet的主要作用是保证一定数量的pod正常运行,它会持续监听这些Pod的运行状态,一旦Pod发生故障,就会重启或重建。同时它还支持对pod数量的扩缩容和镜像版本的升降级。

RS在标签选择器上,除了可以定义键值对的选择形式,还支持selector.matchExpressions字段,可以提供多种选择。目前支持的操作包括:

  • In:label的值在某个列表中

  • Notln:label的值不在某个列表中

  • Exists:某个label存在

  • DoesNotExist:某个label不存在

示例:

[root@master test]# vim rs01.yaml 
---
apiVersion: apps/v1
kind: ReplicaSet
metadata: 
  name: rs01
spec: 
  selector: # 选择器,它的作用是建立pod控制器和pod之间的关联关系,采用的Label Selector机制
            # 在pod模板上定义label,在控制器上定义选择器,就可以表明当前控制器能管理哪些pod了
#   matchLabels:        #开启标签匹配
#     app: nginx        #和哪个key:value匹配
    matchExpressions:   #开启匹配运算符
      - key: version
        operator: Exists  #意思是key是version的就匹配
  replicas: 3    #指定副本数量,其实就是当前rs创建出来的pod的数量,默认为1
  template:      #模板,就是当前控制器创建pod所使用的模板
    metadata: 
      labels: 
        app: nginx
        version: v1
    spec: 
      containers: 
      - name: nginx
        image: myos:nginx
        ports: 
        - containerPort: 80
[root@master test]# kubectl apply -f rs01.yaml 
replicaset.apps/rs created

验证ReplicaSet(RS):

# 查看rs
# DESIRED:期望副本数量  
# CURRENT:当前副本数量  
# READY:已经准备好提供服务的副本数量
[root@master test]# kubectl get rs
NAME   DESIRED   CURRENT   READY   AGE
rs01     3         3         3       7s

# 查看当前控制器创建出来的pod
# 这里发现控制器创建出来的pod的名称是在控制器名称后面拼接了-xxxxx随机码
[root@master test]# kubectl get pods --show-labels
NAME            READY   STATUS    RESTARTS   AGE   LABELS
rs01-9t866        1/1     Running   0          24s   app=nginx,version=v1
rs01-pr6vg        1/1     Running   0          24s   app=nginx,version=v1
rs01-xpthg        1/1     Running   0          24s   app=nginx,version=v1

# 使用scale命令实现扩缩容, 后面--replicas=n直接指定目标数量即可
[root@master test]# kubectl scale rs rs01 --replicas=2 
replicaset.apps/rs scaled
[root@master test]# kubectl get pods --show-labels
NAME            READY   STATUS    RESTARTS   AGE     LABELS
rs01-pr6vg        1/1     Running   0          2m20s   app=nginx,version=v1
rs01-xpthg        1/1     Running   0          2m20s   app=nginx,version=v1

# 镜像升级,查看镜像
[root@master test]# kubectl get rs -o wide 
NAME   DESIRED   CURRENT   READY   AGE     CONTAINERS   IMAGES       SELECTOR
rs01     3         3         3       5m17s   nginx        myos:nginx   app in (lili,spring-k8s)
# kubectl set image rs rs名称 容器=镜像版本 -n namespace
[root@master test]# kubectl set image rs rs01 nginx=myos:latest
replicaset.apps/rs image updated
# 再次查看,发现镜像版本已经变更了
[root@master test]# kubectl get rs -o wide 
NAME   DESIRED   CURRENT   READY   AGE     CONTAINERS   IMAGES        SELECTOR
rs     3         3         3       7m10s   nginx        myos:latest   app in (lili,spring-k8s)

删除ReplicaSet(RS)

# 使用kubectl delete命令会删除此RS以及它管理的Pod
# 在kubernetes删除RS前,会将RS的replicasclear调整为0,等待所有的Pod被删除后,在执行RS对象的删除
[root@master test]# kubectl delete rs rs01
replicaset.apps "rs01" deleted
[root@master test]# kubectl get pods
No resources found in default namespace.

2.8.4 Deployment(Deploy)

为了更好的解决服务编排的问题,kubernetes在V1.2版本开始,引入了Deployment控制器。值得一提的是,这种控制器并不直接管理pod,而是通过管理ReplicaSet来简介管理Pod,即:Deployment管理ReplicaSet,ReplicaSet管理Pod。所以Deployment比ReplicaSet功能更加强大。

Deployment主要功能有下面几个:

  • 支持ReplicaSet的所有功能

  • 支持发布的停止、继续

  • 支持滚动升级和回滚版本

示例:

[root@master test]# cat pc-deployment.yaml 
---
kind: Deployment
apiVersion: apps/v1
metadata: 
  name: pc-deployment
  namespace: default
spec:
  replicas: 3       # 副本数量
  selector:         # 选择器,通过它指定该控制器管理哪些pod
    matchLabels:    # Labels匹配规则
      app: test
  template:         # 模板,当副本数量不足时,会根据下面的模板创建pod副本
    metadata: 
      labels: 
        app: test
    spec: 
      containers: 
      - name: test
        image: myos:nginx
# 创建deployment
[root@master test]# kubectl apply -f pc-deployment.yaml 
deployment.apps/pc-deployment 

# 查看deployment控制器
[root@master test]# kubectl get deploy
NAME            READY   UP-TO-DATE   AVAILABLE   AGE
pc-deployment   3/3     3            3           14m

# 查看replicaset控制器
[root@master test]# kubectl get rs
NAME                      DESIRED   CURRENT   READY   AGE
pc-deployment-f864b4d67   3         

# 查看pod是否创建
[root@master test]# kubectl get pods 
NAME                            READY   STATUS    RESTARTS   AGE
pc-deployment-f864b4d67-6prdd   1/1     Running   0          13m
pc-deployment-f864b4d67-dt8p8   1/1     Running   0          13m
pc-deployment-f864b4d67-hklm2   1/1     Running   0          13m
扩缩容

deployment控制器的扩缩容和replicaset的方法一样,都可以进行edit在线编辑和使用scale命令进行扩缩容。

示例:

# 可以通过edit命令进入编辑器里改replicas的值
[root@master ~]# kubectl edit deployment pc-deployment 
deployment.apps/pc-deployment edited

# 使用scale命令扩缩容
[root@master ~]# kubectl get deployment
NAME            READY   UP-TO-DATE   AVAILABLE   AGE
pc-deployment   5/5     5            5           4h21m
[root@master ~]# kubectl scale deploy pc-deployment --replicas=3
deployment.apps/pc-deployment scaled
[root@master ~]# kubectl get deployment
NAME            READY   UP-TO-DATE   AVAILABLE   AGE
pc-deployment   3/3     3            3           4h23m
镜像更新

deployment支持两种更新策略:重建更新 和 滚动更新 ,可以通过 strategy 指定策略类型,支持两个属性。

deployment默认就是滚动更新

strategy  #指定新的Pod替换旧的Pod的策略, 支持两个属性:
  type    #指定策略类型,支持两种策略
    Recreate       #在创建出新的Pod之前会先杀掉所有已存在的Pod
    RollingUpdate  #滚动更新,就是杀死一部分,就启动一部分,在更新过程中,存在两个版本Pod
#------//如果配置了滚动更新,就需要配置下面的-----
  rollingUpdate    #当type为RollingUpdate时生效,用于为RollingUpdate设置参数,支持两个属性:
    maxSurge       # 用来指定在升级过程中可以超过期望的Pod的最大数量,默认为25%。
    maxUnavailable #用来指定在升级过程中不可用Pod的最大数量,默认为25%。

重建策略示例:

[root@master test]# cat pc-deployment.yaml 
---
kind: Deployment
apiVersion: apps/v1
metadata: 
  name: pc-deployment
  namespace: default
spec:
  strategy:          # 设置更新策略
    type: Recreate   # 重建策略
  replicas: 5
  selector: 
    matchLabels: 
      app: test
  template: 
    metadata: 
      labels: 
        app: test
    spec: 
      containers: 
      - name: test
        image: myos:httpd
# 更新资源清单文件
[root@master test]# kubectl apply -f pc-deployment.yaml 
deployment.apps/pc-deployment configured
 
 # 查看现在的镜像是myos:httpd
[root@master test]# kubectl get deploy -o wide 
NAME            READY   UP-TO-DATE   AVAILABLE   AGE     CONTAINERS   IMAGES       SELECTOR
pc-deployment   5/5     5            5           4h46m   test         myos:httpd   app=test

# 更新镜像为myos:nginx
[root@master test]# kubectl set image deployment pc-deployment test=myos:nginx
deployment.apps/pc-deployment image updated

# 更新镜像前进行实时监控pod,会发现直接把老的pod干掉了,重新创建的pod
[root@master ~]# kubectl get pods -w
NAME                             READY   STATUS    RESTARTS   AGE
pc-deployment-6dfdccd467-2vcqn   1/1     Running   0          58s
pc-deployment-6dfdccd467-52vwg   1/1     Running   0          58s
pc-deployment-6dfdccd467-6t5zh   1/1     Running   0          58s
pc-deployment-6dfdccd467-dqqbb   1/1     Running   0          58s
pc-deployment-6dfdccd467-ff4ww   1/1     Running   0          58s
pc-deployment-6dfdccd467-dqqbb   1/1     Terminating   0          83s
pc-deployment-6dfdccd467-2vcqn   1/1     Terminating   0          83s
pc-deployment-6dfdccd467-52vwg   1/1     Terminating   0          83s
pc-deployment-f864b4d67-8r5v8    0/1     Pending       0          0s
pc-deployment-f864b4d67-zlf2d    0/1     Pending       0          0s
pc-deployment-f864b4d67-7b7jf    0/1     Pending       0          0s
pc-deployment-f864b4d67-kkg44    0/1     Pending       0          0s
pc-deployment-f864b4d67-zlf2d    0/1     Pending       0          0s
pc-deployment-f864b4d67-p7xw9    0/1     ContainerCreating   0          0s
pc-deployment-f864b4d67-8r5v8    0/1     ContainerCreating   0          0s
pc-deployment-f864b4d67-7b7jf    0/1     ContainerCreating   0          0s
pc-deployment-f864b4d67-kkg44    0/1     ContainerCreating   0          0s
pc-deployment-f864b4d67-zlf2d    0/1     ContainerCreating   0          0s
pc-deployment-f864b4d67-p7xw9    0/1     ContainerCreating   0          1s
pc-deployment-f864b4d67-kkg44    1/1     Running             0          3s
pc-deployment-f864b4d67-p7xw9    1/1     Running             0          3s
pc-deployment-f864b4d67-7b7jf    1/1     Running             0          3s
pc-deployment-f864b4d67-zlf2d    1/1     Running             0          4s
pc-deployment-f864b4d67-8r5v8    1/1     Running             0          5s

# 查看镜像已经更新为myos:nginx
[root@master test]# kubectl get deploy -o wide 
NAME            READY   UP-TO-DATE   AVAILABLE   AGE     CONTAINERS   IMAGES       SELECTOR
pc-deployment   5/5     5            5           4h46m   test         myos:nginx   app=test
滚动更新

添加滚动更新策略(这个为默认策略)

滚动策略示例:

[root@master test]# vim pc-deployment.yaml 
---
kind: Deployment
apiVersion: apps/v1
metadata: 
  name: pc-deployment
  namespace: default
spec:
  strategy:   # 更新策略
    type: RollingUpdate  # 滚动更新策略
    rollingUpdate:       # 滚动更新
      maxSurge: 25%      # 最大额外可以存在的副本数,可以为百分比,也可以为整数
      maxUnavailable: 25%  # 最大不可用状态的 Pod 的最大值,可以为百分比,也可以为整数
  replicas: 5
  selector: 
    matchLabels: 
      app: test
  template: 
    metadata: 
      labels: 
        app: test
    spec: 
      containers: 
      - name: test
        image: myos:nginx
# 更新资源清单文件
[root@master test]# kubectl apply -f pc-deployment.yaml 
deployment.apps/pc-deployment configured

# 更新镜像为myos:httpd
[root@master test]# kubectl set image deployment pc-deployment test=myos:httpd
deployment.apps/pc-deployment image updated

# 更新镜像前进行实时监控pod,结果是先创建一个新的pod再干掉一个老的
[root@master ~]# kubectl get pods -w
NAME                            READY   STATUS    RESTARTS   AGE
pc-deployment-f864b4d67-5r2q2   1/1     Running   0          99s
pc-deployment-f864b4d67-6mzq6   1/1     Running   0          99s
pc-deployment-f864b4d67-jw24s   1/1     Running   0          102s
pc-deployment-f864b4d67-kv8sq   1/1     Running   0          102s
pc-deployment-f864b4d67-zcx54   1/1     Running   0          102s
pc-deployment-6dfdccd467-vvqrr   0/1     Pending   0          0s
pc-deployment-6dfdccd467-hjs6c   0/1     Pending   0          0s
pc-deployment-f864b4d67-kv8sq    1/1     Terminating   0          104s
pc-deployment-6dfdccd467-hjs6c   0/1     Pending       0          0s
pc-deployment-6dfdccd467-hjs6c   0/1     ContainerCreating   0          1s
pc-deployment-f864b4d67-kv8sq    1/1     Terminating         0          105s
pc-deployment-6dfdccd467-vvqrr   0/1     ContainerCreating   0          1s
pc-deployment-f864b4d67-5r2q2    1/1     Terminating         0          105s
pc-deployment-f864b4d67-6mzq6    1/1     Terminating         0          105s
pc-deployment-f864b4d67-5r2q2    0/1     Terminating         0          105s
pc-deployment-f864b4d67-6mzq6    0/1     Terminating         0          105s
pc-deployment-6dfdccd467-h7znq   1/1     Running             0          2s
pc-deployment-f864b4d67-jw24s    1/1     Terminating         0          108s
pc-deployment-f864b4d67-5r2q2    0/1     Terminating         0          105s
pc-deployment-f864b4d67-jw24s    1/1     Terminating         0          109s
pc-deployment-6dfdccd467-c2nrp   1/1     Running             0          2s
pc-deployment-f864b4d67-6

# 查看镜像已经更新为myos:httpd
[root@master test]# kubectl get deployment -o wide 
NAME            READY   UP-TO-DATE   AVAILABLE   AGE    CONTAINERS   IMAGES       SELECTOR
pc-deployment   5/5     5            5           5h8m   test         myos:httpd   app=test
版本管理

当我们进行deployment版本升级时,因为它是操作replicaset控制器的,可以查看一下rs的变化。

root@master test]# kubectl get rs
NAME                       DESIRED   CURRENT   READY   AGE
pc-deployment-575cdc66c8   0         0         0       47m
pc-deployment-6dfdccd467   5         5         5       41m
pc-deployment-f864b4d67    0         0         0       5h27m
# 为什么旧的RS依然存在呢?
# 这就是deployment的特殊的功能,它管控这replicaset来进行版本回退的操作。

deployment支持版本升级过程中的暂停、继续功能以及版本回退等诸多功能,下面具体来看:

kubectl rollout 命令: 版本升级相关功能,支持下面的选项

  • status : 显示当前升级状态

  • history: 显示升级历史记录

  • pause: 暂停版本升级过程

  • resume: 继续已经暂停的版本升级过程

  • restart : 重启版本升级过程

  • undo: 回滚到上一级版本(可以使用 --to-revision 回滚到指定版本)

示例:

# 查看升级回退的状态
[root@master test]# kubectl rollout status deployment pc-deployment 
deployment "pc-deployment" successfully rolled out
# 查看历史版本
[root@master test]# kubectl rollout history deployment pc-deployment 
deployment.apps/pc-deployment 
REVISION  CHANGE-CAUSE
2         <none>
6         <none>
7         <none>

给新版本添加注释信息补充:

# 查看历史版本
[root@master ~]# kubectl rollout history deployment mydeploy
deployment.apps/mydeploy 
REVISION  CHANGE-CAUSE
1         <none>

# 添加注释信息
[root@master ~]# kubectl annotate deployments mydeploy kubernetes.io/change-cause="httpd.v1"
deployment.apps/mydeploy annotated

[root@master ~]# kubectl rollout history deployment mydeploy
deployment.apps/mydeploy 
REVISION  CHANGE-CAUSE
1         httpd.v1

# 更新资源清单文件
[root@master ~]# vim mydeploy.yaml
# 在创建容器的镜像下面添加
        imagePullPolicy: Always

[root@master ~]# kubectl apply -f mydeploy.yaml
deployment.apps/mydeploy patched

# 更新版本信息
[root@master ~]# kubectl annotate deployments mydeploy kubernetes.io/change-cause="add imagePullPolicy"
deployment.apps/mydeploy annotated

[root@master ~]# kubectl rollout history deployment mydeploy
deployment.apps/mydeploy 
REVISION  CHANGE-CAUSE
1         httpd.v1
2         add imagePullPolicy
版本回滚
# 查看当前版本是myos:nginx02
[root@master test]# kubectl get deployment pc-deployment -owide
NAME            READY   UP-TO-DATE   AVAILABLE   AGE     CONTAINERS   IMAGES       SELECTOR
pc-deployment   5/5     5            5           5h35m   test         myos:nginx02  app=test

# 返回到上一个版本
[root@master test]# kubectl rollout undo deployment pc-deployment 
deployment.apps/pc-deployment rolled back

# 再次查看,版本变成myos:nginx01
[root@master test]# kubectl get deployment pc-deployment -owide
NAME            READY   UP-TO-DATE   AVAILABLE   AGE     CONTAINERS   IMAGES       SELECTOR
pc-deployment   5/5     5            5           5h35m   test         myos:nginx01   app=test

# 查看rs信息,发现rs中的pod的位置也变成到老的版本中
[root@master test]# kubectl get rs
NAME                       DESIRED   CURRENT   READY   AGE
pc-deployment-575cdc66c8   0         0         0       56m
pc-deployment-6dfdccd467   0         0         0       50m
pc-deployment-f864b4d67    5         5         5       5h36m
回滚到指定的版本
# 查看历史版本
[root@master test]# kubectl rollout history deployment pc-deployment 
deployment.apps/pc-deployment 
REVISION  CHANGE-CAUSE
2         <none> #nginx03版
7         <none> #nginx02版
8         <none> #nginx01版,当前所在的版本

# 回滚到指定的版本
[root@master test]# kubectl rollout undo deployment pc-deployment --to-revision=7
deployment.apps/pc-deployment rolled back

# 查看版本为myos:nginx02
[root@master test]# kubectl get deployment pc-deployment -owide
NAME            READY   UP-TO-DATE   AVAILABLE   AGE     CONTAINERS   IMAGES       SELECTOR
pc-deployment   5/5     5            5           5h56m   test         myos:nginx02  app=test

# rs中的pod也换了位置
[root@master test]# kubectl get rs
NAME                       DESIRED   CURRENT   READY   AGE
pc-deployment-575cdc66c8   0         0         0       76m
pc-deployment-6dfdccd467   5         5         5       71m
pc-deployment-f864b4d67    0         0         0       5h56m
金丝雀部署

Deployment控制器支持控制更新过程中的控制,如“暂停(pause)”或“继续(resume)”更新操作。

比如有一批新的Pod资源创建完成后立即暂停更新过程,此时,仅存在一部分新版本的应用,主体部分还是旧的版本。然后,再筛选一小部分的用户请求路由到新版本的Pod应用,继续观察能否稳定地按期望的方式运行。确定没问题之后再继续完成余下的Pod资源滚动更新,否则立即回滚更新操作。这就是所谓的金丝雀发布。

版本升级,然后暂停示例:

# 查看当前的rs信息和镜像版本
[root@master test]# kubectl get rs -o wide 
NAME                       DESIRED   CURRENT   READY   AGE     CONTAINERS   IMAGES       SELECTOR
pc-deployment-6dfdccd467   0         0         0       9m27s   test         myos:nginx01  app=test,pod-template-hash=6dfdccd467
pc-deployment-f864b4d67    3         3         3       11m     test         myos:nginx02  app=test,pod-template-hash=f864b4d67

# 将nginx02升级到nginx03,升级一个,然后暂停升级
[root@master test]# kubectl set image deployment pc-deployment test=myos:nginx03 && kubectl rollout pause deployment pc-deployment 
deployment.apps/pc-deployment image updated
deployment.apps/pc-deployment paused

# 查看更新状态(新开一个终端,会占用前台)
# 监控更新的过程,可以看到已经新增了一个资源,但是并未按照预期的状态去删除一个旧的资源,就是因为使用了pause暂停命令
[root@master test]# kubectl rollout status deployment pc-deployment 
Waiting for deployment "pc-deployment" rollout to finish: 1 out of 3 new replicas have been updated...

# 发现nginx03版本更新了一个,老版本还在
[root@master test]# kubectl get rs -o wide 
NAME                       DESIRED   CURRENT   READY   AGE   CONTAINERS   IMAGES         SELECTOR
pc-deployment-6dfdccd467   0         0         0       11m   test         myos:nginx01     app=test,pod-template-hash=6dfdccd467
pc-deployment-79bf44dbdc   1         1         1       18s   test         myos:nginx03   app=test,pod-template-hash=79bf44dbdc
pc-deployment-f864b4d67    3         3         3       13m   test         myos:nginx02     app=test,pod-template-hash=f864b4d67

新版本确认没问题,进行全部滚动更新:

# 确认新版本没问题,继续滚动更新
[root@master test]# kubectl rollout resume deployment pc-deployment 
deployment.apps/pc-deployment resumed

# 查看版本全部更新成功
[root@master test]# kubectl get rs -o wide 
NAME                       DESIRED   CURRENT   READY   AGE   CONTAINERS   IMAGES         SELECTOR
pc-deployment-6dfdccd467   0         0         0       13m   test         myos:httpd     app=test,pod-template-hash=6dfdccd467
pc-deployment-79bf44dbdc   3         3         3       2m    test         myos:php-fpm   app=test,pod-template-hash=79bf44dbdc
pc-deployment-f864b4d67    0         0         0       15m   test         myos:nginx     app=test,pod-template-hash=f864b4d67

# 最后出现successfully成功代表更新成功
[root@master test]# kubectl rollout status deployment pc-deployment 
Waiting for deployment "pc-deployment" rollout to finish: 1 out of 3 new replicas have been updated...
Waiting for deployment "pc-deployment" rollout to finish: 1 out of 3 new replicas have been updated...
Waiting for deployment spec update to be observed...
Waiting for deployment spec update to be observed...
Waiting for deployment "pc-deployment" rollout to finish: 1 out of 3 new replicas have been updated...
Waiting for deployment "pc-deployment" rollout to finish: 1 out of 3 new replicas have been updated...
Waiting for deployment "pc-deployment" rollout to finish: 2 out of 3 new replicas have been updated...
Waiting for deployment "pc-deployment" rollout to finish: 2 out of 3 new replicas have been updated...
Waiting for deployment "pc-deployment" rollout to finish: 2 out of 3 new replicas have been updated...
Waiting for deployment "pc-deployment" rollout to finish: 1 old replicas are pending termination...
Waiting for deployment "pc-deployment" rollout to finish: 1 old replicas are pending termination...
deployment "pc-deployment" successfully rolled out

删除deployment

[root@master test]# kubectl get rs
NAME                       DESIRED   CURRENT   READY   AGE
pc-deployment-6dfdccd467   0         0         0       28m
pc-deployment-79bf44dbdc   3         3         3       16m
pc-deployment-f864b4d67    0         0         0       30m

[root@master test]# kubectl delete deployment pc-deployment 
deployment.apps "pc-deployment" deleted

[root@master test]# kubectl get rs
No resources found in default namespace.

[root@master test]# kubectl get deploy
No resources found in default namespace. 

2.8.5 Statefulset(sts)

StatefulSet 是一个用于运行有状态应用程序的 Kubernetes 控制器。它为每个 Pod 分配了一个唯一的标识符,也称作稳定的网络标识符(Stable Network Identifier, 简称为 SNI)。这个唯一的标识符允许 StatefulSet 根据指定的顺序(例如按照字母顺序或者按照时间戳)来创建和更新 Pod,并在每次更新时保证这些 Pod 的稳定性。这使得有状态应用程序(如数据库)的部署和扩展变得更加容易。

StatefulSet特点

Deployment、ReplicationController 是为无状态服务而设计的,它们中 pod 的名称、主机名、存储都是不稳定的,且 pod 的启动、销毁顺序随机。管理的 pod 具有如下特点:

  • **唯一性:**对于包含 N 个副本的 StatefulSet,每个 pod 会被分配一个 [0,N)范围内的唯一序号。
  • **顺序性:**StatefulSet 中 pod 的启动、更新、销毁默认都是按顺序进行的。
  • **稳定的网络身份标识:**pod 的主机名、DNS 地址不会随着 pod 被重新调度而发生变化。
  • **稳定的持久化存储:**当 pod 被重新调度后,仍然能挂载原有的 PersistentVolume,保证了数据的完整性和一致性。

在Deployment中,与之对应的服务是service,而在StatefulSet中与之对应的headless service。

  • headless service,即无头服务,与service的区别就是它没有Cluster IP,解析它的名称时将返回该Headless Service对应的全部Pod的Endpoint列表。

  • 除此之外,StatefulSet在Headless Service的基础上又为StatefulSet控制的每个Pod副本创建了一个DNS域名,这个域名的格式为:

$(podname).(headless server name)

FQDN:$(podname).(headless server name).namespace.svc.cluster.local

示例:

# 创建无头服务
[root@master ~]# vim stssvc.yaml 
---
kind: Service
apiVersion: v1
metadata:
  name: stssvc          # 服务名称
spec:
  type: ClusterIP
  clusterIP: None       # 设置 IP 为 None
  selector:
    app: sts-httpd      # 设置 Pod 标签
  ports:
  - protocol: TCP
    port: 80
    targetPort: 80

[root@master ~]# kubectl apply -f stssvc.yaml 
service/stssvc created

[root@master ~]# kubectl get services stssvc
NAME         TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)   AGE
stssvc       ClusterIP   None          <none>        80/TCP    51s
# 创建sts控制器
[root@master ~]# vim mysts.yaml
---
kind: StatefulSet       # 资源对象类型
apiVersion: apps/v1
metadata:
  name: mysts           # 控制器名称
spec:
  serviceName: stssvc   # 新增 headless 服务名称
  replicas: 3
  selector:
    matchLabels:
      app: sts-httpd    # 修改标签防止冲突
  template:
    metadata:
      labels:
        app: sts-httpd  # 修改标签防止冲突
    spec:
      containers:
      - name: web
        image: myos:httpd
        
# statefulset 主要解决了 Pod 创建顺序的问题
# statefulset 主要解决了访问指定 Pod 的问题
[root@master ~]# kubectl apply -f mysts.yaml 
statefulset.apps/mysts created

# pod是一个一个创建的
[root@master ~]# kubectl get pods
NAME      READY   STATUS    RESTARTS   AGE
mysts-0   1/1     Running   0          3s
mysts-1   1/1     Running   0          2s
mysts-2   1/1     Running   0          1s

# 所有 Pod IP 地址
[root@master ~]# host stssvc.default.svc.cluster.local 10.245.0.10
Using domain server:
Name: 10.245.0.10
Address: 10.245.0.10#53
Aliases: 
stssvc.default.svc.cluster.local has address 10.244.1.81
stssvc.default.svc.cluster.local has address 10.244.2.82
stssvc.default.svc.cluster.local has address 10.244.3.83

# 单个 Pod IP 地址
[root@master ~]# host mysts-0.stssvc.default.svc.cluster.local 10.245.0.10
Using domain server:
Name: 10.245.0.10
Address: 10.245.0.10#53
Aliases: 
mysts-0.stssvc.default.svc.cluster.local has address 10.244.1.81

# 删除sts控制器
[root@master ~]# kubectl delete -f mysts.yaml -f stssvc.yaml
statefulset.apps "mysts" deleted
service "stssvc" deleted

2.8.6 DaemonSet(DS)

DaemonSet 类型的控制器可以保证在集群中的每一台(或指定)节点上都运行一个副本,一般适用于日志收集、节点监控等场景。 也就是说,如果一个Pod提供的功能是节点级别的,那么这类Pod就适合使用DaemonSet类型的控制器创建。

特点:

  • 每当向集群中添加一个节点时,指定的Pod副本也将添加到该节点上。

  • 当节点从集群中移除时,Pod也就被垃圾回收了。

示例:

[root@master test]# vim pc-daemonset.yaml 
---
kind: DaemonSet
apiVersion: apps/v1
metadata: 
  name: pc-daemonset
spec: 
  selector: 
    matchLabels: 
      app: nginx
  template: 
    metadata: 
      labels: 
        app: nginx 
    spec: 
      containers: 
      - name: nginx
        image: myos:export
[root@master test]# kubectl apply -f pc-daemonset.yaml 
daemonset.apps/pc-daemonset created

# 查看daemonset
[root@master test]# kubectl get ds
NAME           DESIRED   CURRENT   READY   UP-TO-DATE   AVAILABLE   NODE SELECTOR   AGE
pc-daemonset   2         2         2       2            2           <none>          8s

# 查看pod,发现在每个Node上都运行一个pod,master节点有NoSchedule不调度污点所以master节点没添加该pod
[root@master test]# kubectl get pods -o wide 
NAME                 READY   STATUS    RESTARTS   AGE   IP              NODE         NOMINATED NODE   READINESS GATES
pc-daemonset-8qn25   1/1     Running   0          14s   10.244.85.237   node01   <none>           <none>
pc-daemonset-s565r   1/1     Running   0          14s   10.244.58.220   node02   <none>           <none>

2.8.7 Job

Job负责批处理任务,专门用来执行一次的任务,它保证批处理任务的一个或多个pod成功结束

Job特点:

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

  • 当成功结束的Pod达到指定的数量时,Job将完成任务。

Job资源清单文件:

apiVersion: batch/v1    # 版本号
kind: Job               # 类型       
metadata:               # 元数据
  name:                 # 名称 
  namespace:            # 所属命名空间 
  labels:               # 标签
    controller: job
spec:                   # 详情描述
  completions: 1        # 指定job需要成功运行Pods的次数。默认值: 1 (就是说:pod成功运行的次数)
  parallelism: 1        # 指定job在任一时刻应该并发运行Pods的数量。默认值: 1(多少pod可以并行执行)
  activeDeadlineSeconds: 30 # 指定job可运行的时间期限,超过时间还未结束,系统将会尝试进行终止。
  backoffLimit: 6       # 指定job失败后进行重试的次数。默认是6
  manualSelector: true  # 是否可以使用selector选择器选择pod,默认是false
  selector:             # 选择器,通过它指定该控制器管理哪些pod
    matchLabels:        # Labels匹配规则
      app: counter-pod
    matchExpressions:   # Expressions匹配规则
      - {key: app, operator: In, values: [counter-pod]}
  template:             # 模板,当副本数量不足时,会根据下面的模板创建pod副本
    metadata:
      labels:
        app: counter-pod
    spec:
      restartPolicy: Never  # 重启策略只能设置为Never或者OnFailure
      containers:
      - name: counter
        image: busybox:1.30
        command: ["bin/sh","-c","for i in 9 8 7 6 5 4 3 2 1; do echo $i;sleep 2;done"]

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

  • 如果指定为OnFailure,则job会在pod出现故障时重启容器,而不是创建pod,failed次数不变

  • 如果指定为Never,则job会在pod出现故障时创建新的pod,并且故障pod不会消失,也不会重启,failed次数加1

  • 如果指定为Always的话,就意味着一直重启,意味着job任务会重复去执行了,当然不对,所以不能设置为Always

示例:

[root@master test]# vim pc-job.yaml 
---
kind: Job
apiVersion: batch/v1
metadata: 
  name: pc-job
spec: 
  manualSelector: true   #允许使用selector选择器选择pod
  selector: 
    matchLabels: 
      app: nginx
  template: 
    metadata: 
      labels: 
        app: nginx
    spec: 
      restartPolicy: Never
      containers: 
      - name: nginx
        image: myos:nginx
        command: 
        - sh
        - -c
        - |
          for i in {1..9}
          do
          echo $i
          sleep 3
          done
# 创建job
[root@master test]# kubectl create -f pc-job.yaml 
job.batch/pc-job created
# 实时查看job
[root@master test]# kubectl get job -w
NAME     COMPLETIONS   DURATION   AGE
pc-job   0/1           8s         8s
pc-job   0/1           34s        34s
pc-job   1/1           34s        34s
# 通过观察pod状态可以看到,pod在运行完毕任务后,就会变成Completed状态
[root@master test]# kubectl get pods -w
NAME           READY   STATUS    RESTARTS   AGE
pc-job-dg252   0/1     Pending   0          0s
pc-job-dg252   0/1     Pending   0          0s
pc-job-dg252   0/1     ContainerCreating   0          0s
pc-job-dg252   0/1     ContainerCreating   0          1s
pc-job-dg252   1/1     Running             0          3s
pc-job-dg252   0/1     Completed           0          30s
pc-job-dg252   0/1     Completed           0          32s

修改Pod运行的总数量和并行数量:

# 直接更新是不行的,对于同一个Job,这是不可变参数
[root@master test]# kubectl delete -f pc-job.yaml
job.batch "pc-job" deleted

[root@master test]# vim pc-job.yaml 
---
kind: Job
apiVersion: batch/v1
metadata: 
  name: pc-job
spec: 
  completions: 6  # 指定job需要成功运行Pods的次数为6
  parallelism: 3  # 指定job并发运行Pods的数量为3
  manualSelector: true
  selector: 
    matchLabels: 
      app: nginx
  template: 
    ......
# 创建job
[root@master test]# kubectl create -f pc-job.yaml
job.batch/pc-job created

# 创建并执行查看结果
[root@master test]# kubectl get pods -w
NAME           READY   STATUS    RESTARTS   AGE
pc-job-xnzjh   0/1     Pending   0          0s
pc-job-xrv5p   0/1     Pending   0          0s
pc-job-xd6qv   0/1     Pending   0          0s
pc-job-xnzjh   0/1     Pending   0          0s
pc-job-xd6qv   0/1     Pending   0          0s
pc-job-xnzjh   0/1     ContainerCreating   0          0s
pc-job-xd6qv   0/1     ContainerCreating   0          0s
pc-job-xrv5p   0/1     ContainerCreating   0          1s
pc-job-xd6qv   1/1     Running             0          3s
pc-job-xnzjh   1/1     Running             0          3s
pc-job-xrv5p   1/1     Running             0          3s  # 从这里可以看见并发创建了3个pod
pc-job-xd6qv   0/1     Completed           0          30s
pc-job-xrv5p   0/1     Completed           0          30s
pc-job-xnzjh   0/1     Completed           0          30s
pc-job-xd6qv   0/1     Completed           0          31s # completed字样代表执行成功
......

2.8.8 CronJob(CJ)

CronJob控制器以Job控制器资源为其管控对象,并借助它管理pod资源对象,Job控制器定义的作业任务在其控制器资源创建之后便会立即执行,但CronJob可以以类似于Linux操作系统的周期性计划任务的方式控制其运行时间点及重复运行的方式。也就是说,CronJob可以在特定的时间点(反复的)去运行job任务。

CronJob资源清单示例:

---
apiVersion: batch/v1       # 版本号
kind: CronJob              # 类型       
metadata:                  # 元数据
  name:                    # CJ名称 
  namespace:               # 所属命名空间 
  labels:                  #标签
    controller: cronjob
spec:                      # 详情描述
  schedule:       # cron格式的作业调度运行时间点,用于控制任务在什么时间执行
  concurrencyPolicy:         # 并发执行策略,用于定义前一次作业运行尚未完成时是否以及如何运行后一次的作业
  failedJobHistoryLimit:     # 为失败的任务执行保留的历史记录数,默认为1
  successfulJobHistoryLimit: # 为成功的任务执行保留的历史记录数,默认为3
  startingDeadlineSeconds:   # 启动作业错误的超时时长
  jobTemplate:               # job控制器模板,用于为cronjob控制器生成job对象;下面其实就是job的定义
    metadata:
    spec:
      completions: 1
      parallelism: 1
      activeDeadlineSeconds: 30
      backoffLimit: 6
      manualSelector: true
      selector:
        matchLabels:
          app: counter-pod
        matchExpressions: # 规则
          - {key: app, operator: In, values: [counter-pod]}
      template:
        metadata:
          labels:
            app: counter-pod
        spec:
          restartPolicy: Never 
          containers:
          - name: counter
            image: busybox:1.30
            command: ["bin/sh","-c","for i in 9 8 7 6 5 4 3 2 1; do echo $i;sleep 20;done"]
--------------------------------------------------------------
需要重点解释的几个选项:
schedule: cron表达式,用于指定任务的执行时间
    */1    *      *    *     *
    <>  <>  <>  <> <>

    分钟: 值从 059.
    小时: 值从 023.
    日: 值从 131.
    月: 值从 112.
    周: 值从 06, 0 代表星期日
    多个时间可以用逗号隔开; 范围可以用连字符给出;*可以作为通配符; /表示每...
    
concurrencyPolicy:
    Allow:   允许Jobs并发运行(默认)
    Forbid:  禁止并发运行,如果上一次运行尚未完成,则跳过下一次运行
    Replace: 替换,取消当前正在运行的作业并用新作业替换它

示例:

[root@master test]# vim pc-cronjob.yaml 
---
kind: CronJob
apiVersion: batch/v1
metadata: 
  name: pc-cronjob
  labels: 
    controller: cronjob
spec: 
  schedule: "*/1 * * * *"   #指定执行任务周期,这里是每分钟
  jobTemplate: 
    metadata: 
    spec: 
      template:
        spec: 
          restartPolicy: Never
          containers: 
          - name: latest
            image: myos:latest
            command:    # 执行的任务
            - sh
            - -c
            - | 
              for i in {1..5}
              do
                echo $i
                sleep 6
              done
# 创建cronjob
[root@master test]# kubectl create -f pc-cronjob.yaml 
cronjob.batch/pc-cronjob created

# 实时查看cj的信息,是按照我们定义的每分钟执行
[root@master test]# kubectl get cj -w
NAME         SCHEDULE      SUSPEND   ACTIVE   LAST SCHEDULE   AGE
pc-cronjob   */1 * * * *   False     0        <none>          2s
pc-cronjob   */1 * * * *   False     1        0s              10s
pc-cronjob   */1 * * * *   False     2        0s              70s
pc-cronjob   */1 * * * *   False     3        0s              2m10s
pc-cronjob   */1 * * * *   False     2        10s             2m20s
pc-cronjob   */1 * * * *   False     2        10s             2m20s
pc-cronjob   */1 * * * *   False     3        0s              3m10s
pc-cronjob   */1 * * * *   False     4        0s              4m10s

# 实时查看job
root@master ~]# kubectl get job -w
NAME                  COMPLETIONS   DURATION   AGE
pc-cronjob-28954353   0/1           9s         9s
pc-cronjob-28954353   0/1           10s        10s
pc-cronjob-28954353   1/1           10s        10s # 一个任务已完成
pc-cronjob-28954354   0/1           10s        10s
pc-cronjob-28954354   0/1           11s        11s
pc-cronjob-28954354   1/1           11s        11s

# 发现pod是一个pod一个pod的创建,执行完一个才能创建下一个pod
[root@master test]# kubectl get pods -w
NAME                        READY   STATUS    RESTARTS   AGE
pc-cronjob-28954353-xn2tw   0/1     Pending             0          0s
pc-cronjob-28954353-xn2tw   0/1     Pending             0          0s
pc-cronjob-28954353-xn2tw   0/1     ContainerCreating   0          0s
pc-cronjob-28954353-xn2tw   0/1     ContainerCreating   0          1s
pc-cronjob-28954353-xn2tw   1/1     Running             0          2s
pc-cronjob-28954353-xn2tw   0/1     Completed           0          8s
pc-cronjob-28954353-xn2tw   0/1     Completed           0          9s
pc-cronjob-28954354-q2zcn   0/1     Pending             0          0s
pc-cronjob-28954354-q2zcn   0/1     Pending             0          0s
pc-cronjob-28954354-q2zcn   0/1     ContainerCreating   0          0s
pc-cronjob-28954354-q2zcn   0/1     ContainerCreating   0          1s
pc-cronjob-28954354-q2zcn   1/1     Running             0          3s
pc-cronjob-28954354-q2zcn   0/1     Completed           0          9s
pc-cronjob-28954354-q2zcn   0/1     Completed           0          10s

2.8.9 HPA

HPA(Horizontal Pod Autoscaling)Pod 水平自动伸缩,是根据Pod占用CPU的比率,到达一定的阀值,会触发伸缩机制,Kubernetes 有一个 HPA 的资源,HPA 可以根据 CPU 利用率自动伸缩一个 Replication Controller、 Deployment 或者Replica Set 中的 Pod 数量。

HPA的规则:

  • 定义pod的时候必须要有资源限制,否则HPA无法进行监控

  • 扩容时即时的,只要超过阀值会立刻扩容,不是立刻扩容到最大副本数。他会在最小值和最大值之间波动。如果扩容的数量满足了需求,不会在扩容

  • 缩容时缓慢的。如果业务的峰值较高,回收的策略太积极的话,可能会产生业务的崩溃。缩容的速度比较慢,扩容比较快

HPA监控的是CPU,根据CPU自动扩缩容。扩的比较快(要立即满足需求),缩的比较慢(方式业务崩溃),周期性的检测Pod的CPU使用率,默认30s检测一次

HPA示例:

配置后端服务
# 为 Deploy 模板添加资源配额
[root@master ~]# vim mycluster.yaml 
---
kind: Deployment
apiVersion: apps/v1
metadata:
  name: mydeploy
spec:
  replicas: 1
  selector:
    matchLabels:
      app: deploy-httpd
  template:
    metadata:
      labels:
        app: deploy-httpd
    spec:
      containers:
      - name: web
        image: myos:httpd
        resources:           # 为该资源设置配额
          requests:          # HPA 控制器会根据配额使用情况伸缩集群
            cpu: 300m        # CPU 配额

---
kind: Service
apiVersion: v1
metadata:
  name: websvc
spec:
  type: ClusterIP
  clusterIP: 10.245.1.80
  selector:
    app: deploy-httpd
  ports:
  - protocol: TCP
    port: 80
    targetPort: 80

[root@master ~]# kubectl replace --force -f mycluster.yaml
deployment.apps/mydeploy replaced
service/websvc replaced

# 验证服务
[root@master ~]# kubectl top pods
NAME                       CPU(cores)   MEMORY(bytes)   
mydeploy-b4f9dc786-w4x2z   6m           18Mi            

[root@master ~]# curl -s http://10.245.1.80/info.php
<pre>
Array
(
    [REMOTE_ADDR] => 10.244.219.64
    [REQUEST_METHOD] => GET
    [HTTP_USER_AGENT] => curl/7.61.1
    [REQUEST_URI] => /info.php
)
php_host:   mydeploy-b4f9dc786-w4x2z
1229
部署HPA控制器
[root@master ~]# vim myhpa.yaml 
---
kind: HorizontalPodAutoscaler
apiVersion: autoscaling/v2
metadata: 
  name: myhpa
spec: 
  behavior:                     # 配置控制器参数
    scaleDown:                  # 缩容参数
      stabilizationWindowSeconds: 60  # 窗口稳定期
  scaleTargetRef:               # 监控的资源对象
    kind: Deployment            # 资源对象类型
    apiVersion: apps/v1         # 版本
    name: mydeploy              # 资源对象名称
  minReplicas: 1                # Pod副本最小值
  maxReplicas: 4                # Pod副本最大值
  metrics:                       # 定义度量资源
  - type: Resource              # 检测资源对象类型
    resource:                     # 目标定义
      name: cpu                   # 资源名称
      target:                     # 参考指标
        type: Utilization       # 类型
        averageUtilization: 50  # 被使用资源占配额的百分比
        
[root@master ~]# kubectl apply -f myhpa.yaml 
horizontalpodautoscaler.autoscaling/myhpa created

# 刚刚创建 unknown 是正常现象,最多等待 60s 就可以正常获取数据
[root@master ~]# kubectl get horizontalpodautoscalers
NAME    REFERENCE             TARGETS         MINPODS   MAXPODS   REPLICAS
myhpa   Deployment/mydeploy   <unknown>/50%   1         5         0

[root@master ~]# kubectl get horizontalpodautoscalers
NAME    REFERENCE             TARGETS   MINPODS   MAXPODS   REPLICAS
myhpa   Deployment/mydeploy   0%/50%    1         5         3
测试
# 终端 1 访问提高负载
[root@master ~]# while sleep 1;do curl -s "http://10.245.1.80/info.php?id=100000" -o /dev/null; done &
# 终端 2 监控 HPA 变化
[root@master ~]# kubectl get hpa -w
NAME    REFERENCE             TARGETS   MINPODS   MAXPODS   REPLICAS   AGE
myhpa   Deployment/mydeploy   0%/50%    1         3         1          1m
myhpa   Deployment/mydeploy   31%/50%   1         3         1          2m
myhpa   Deployment/mydeploy   70%/50%   1         3         1          2m15s
myhpa   Deployment/mydeploy   72%/50%   1         3         2          2m30s
myhpa   Deployment/mydeploy   36%/50%   1         3         2          2m45s
myhpa   Deployment/mydeploy   55%/50%   1         3         2          3m
myhpa   Deployment/mydeploy   58%/50%   1         3         3          3m15s
myhpa   Deployment/mydeploy   39%/50%   1         3         3          3m30s
... ...
myhpa   Deployment/mydeploy   66%/50%   1         3         3          5m

# 如果 60s 内平均负载小于标准值,就会自动缩减集群规模
[root@master ~]# kubectl get hpa -w
NAME    REFERENCE             TARGETS   MINPODS   MAXPODS   REPLICAS   AGE
myhpa   Deployment/mydeploy   52%/50%   1         3         3          13m
myhpa   Deployment/mydeploy   44%/50%   1         3         3          13m15s
myhpa   Deployment/mydeploy   38%/50%   1         3         3          13m30s
myhpa   Deployment/mydeploy   35%/50%   1         3         3          13m45s
myhpa   Deployment/mydeploy   28%/50%   1         3         3          14m
... ...
myhpa   Deployment/mydeploy   8%/50%    1         3         3          18m30s
myhpa   Deployment/mydeploy   13%/50%   1         3         2          18m45s
myhpa   Deployment/mydeploy   12%/50%   1         3         2          19m
myhpa   Deployment/mydeploy   28%/50%   1         3         1          19m15s
myhpa   Deployment/mydeploy   15%/50%   1         3         1          19m30s
myhpa   Deployment/mydeploy    0%/50%   1         3         1          20m

2.9 Service详解

2.9.1 Service 存在的意义?

引入 Service 主要是解决 Pod 的动态变化,通过创建 Service,可以为一组具有相同功能的容器应用提供一个统一的入口地址,并且将请求负载分发到后端的各个容器应用上。

若提供服务的容器应用是分布式,所以存在多个 pod 副本,而 Pod 副本数量可能在运行过程中动态改变,比如水平扩缩容,或者服务器发生故障 Pod 的 IP 地址也有可能发生变化。当 pod 的地址端口发生改变后,客户端再想连接访问应用就得人工干预,很麻烦,这时就可以通过 service 来解决问题。

概念:
Service 主要用于提供网络服务,通过 Service 的定义,能够为客户端应用提供稳定的访问地址(域名或 IP 地址)和负载均衡功能,以及屏蔽后端 Endpoint 的变化,是 K8s 实现微服务的核心资源。

svc 特点:

  1. 服务发现,防止阴滚动升级等因素导致 Pod IP 发生改变而失联,找到提供同一个服务的 Pod。
  2. 负载均衡,定义一组 Pod 的访问策略。

svc 与 pod 关系:

  1. pod 在创建时,与资源没有明确关联,通过 service 标签和 pod 标签相匹配来以此关联。
  2. 可以通过 endpoints 来查看关联的 pod。

2.9.2 svc的三大特征

服务的自动感知

服务会创建一个clusterlP这个地址对应资源地址,不管Pod 如何变化,服务总能找到对应的Pod,且clusterIP保持不变

服务的负载均衡

如果服务后端对应多个pod,则会通过Iptables/LVS规则将访问的请求最终映射到pod的容器内部,自动在多个容器间实现负载均衡

服务的自动发现

服务创建时会自动在内部DNS上注册域名

域名:<服务名称>.<名称空间>.svc.cluster.local

svc选中pod的逻辑

  1. pod是处于running状态

  2. pod的标签是svc标签的集合(同一个名称空间下)

2.9.3 ClusterIP(集群内部使用)

默认方式,分配一个稳定的IP地址,即VIP,只能在集群内部访问

示例:

# 创建Deployment,pod的副本数为2
[root@master test]# cat server.yaml 
---
kind: Deployment
apiVersion: apps/v1
metadata: 
  name: http-test
  labels: 
    run: http
spec:
  replicas: 2
  selector:
    matchLabels:
      run: http
  template:
    metadata:
      labels:
        run: http
    spec:
      containers:
      - name: http
        image: myos:httpd

# 创建service
[root@master test]# cat service.yaml 
---
kind: Service
apiVersion: v1
metadata: 
  name: http-test     # 定义service的名称
  labels:             # 定义Service的标签,用于与其他资源关联
    run: http
spec:                 # 定义Service的规格
  selector:           # 定义Service选择器,用于选择具有特定标签的Pod
    run: http
  ports:              # 定义Service的端口配置
    - name: http      # 定义多个端口的话需要为每个端口指定名字
      protocol: TCP   # 指定协议为TCP,不填的话默认 TCP
      port: 80        # service 暴露的端口
      targetPort: 80  # 关联的 Pod 的端口,不填的话默认和 port 字段相同
                      # 也可以定义端口别名,但是已有的pod要有端口别名才能被service调用
# 创建以上两个资源清单文件
[root@master test]# kubectl apply -f service.yaml -f server.yaml
# 查看标签为run=http的pod详细信息和ip地址
[root@master test]# kubectl get pods -owide -l run=http
NAME                         READY   STATUS    RESTARTS   AGE   IP              NODE         NOMINATED NODE   READINESS GATES
http-test-57fb9c77d5-djrmd   1/1     Running   0          12m   10.244.58.193   node02   <none>           <none>
http-test-57fb9c77d5-k4znk   1/1     Running   0          12m   10.244.85.219   node01   <none>           <none>

# 查看service的属性信息
[root@master test]# kubectl describe svc http-test 
Name:              http-test      # Service的名称
Namespace:         default
Labels:            run=http       # Service自己的标签
Annotations:       <none>
Selector:          run=http       # 选择run=http标签的pod
Type:              ClusterIP      # Service的类型
IP Family Policy:  SingleStack
IP Families:       IPv4
IP:                10.245.50.225  # Service的IP地址
IPs:               10.245.50.225
Port:              <unset>  80/TCP
TargetPort:        80/TCP
Endpoints:         10.244.58.193:80,10.244.85.219:80  # Endpoints是Service的实际后端,即流量最终被转发到的地方。
Session Affinity:  None
Events:            <none>

# 访问serviceIP有负载的效果
[root@master test]# curl -s http://10.245.50.225/info.php | grep php_host
php_host:       http-test-57fb9c77d5-djrmd
[root@master test]# curl -s http://10.245.50.225/info.php | grep php_host
php_host:       http-test-57fb9c77d5-k4znk
# 查看ipvs规则
[root@master test]# ipvsadm -Ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
  -> RemoteAddress:Port           Forward Weight ActiveConn InActConn
TCP  10.245.0.1:443 rr
  -> 192.168.10.10:6443          Masq    1      6          0         
TCP  10.245.0.10:53 rr
  -> 10.244.235.207:53            Masq    1      0          0         
  -> 10.244.235.208:53            Masq    1      0          0         
TCP  10.245.0.10:9153 rr
  -> 10.244.235.207:9153          Masq    1      0          0         
  -> 10.244.235.208:9153          Masq    1      0          0         
TCP  10.245.50.225:80 rr  # 自动添加的规则,以下是后端pod的IP
  -> 10.244.58.193:80             Masq    1      0          0         
  -> 10.244.85.219:80             Masq    1      0          0         
UDP  10.245.0.10:53 rr
  -> 10.244.235.207:53            Masq    1      0          0         
  -> 10.244.235.208:53            Masq    1      0          0  

#-------------------------------------------------------
# 这些规则是由 Kubernetes 的 kube-proxy 组件自动管理的。kube-proxy 负责维护 ipvs 规则,以确保流量能够正确地从 Service 转发到后端的 Pod。

域名自动注册:

# 安装工具软件包
[root@master ~]# dnf install -y bind-utils
# 查看 DNS 服务地址
[root@master test]# kubectl -n kube-system get service kube-dns 
NAME       TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)                  AGE
kube-dns   ClusterIP   10.245.0.10   <none>        53/UDP,53/TCP,9153/TCP   5d22h
# 域名解析测试
[root@master test]# host http-test.default.svc.cluster.local. 10.245.0.10
Using domain server:
Name: 10.245.0.10
Address: 10.245.0.10#53
Aliases: 

http-test.default.svc.cluster.local has address 10.245.50.225
# 创建pod充当客户端来测试
[root@master test]# vim client-test.yaml 
---
apiVersion: v1
kind: Pod
metadata:
  name: test-client
spec:
  containers:
    - name: client-test
      image: myos:latest
      command:
        - sleep
        - "3600"
# 创建pod
[root@master test]# kubectl apply -f client-test.yaml 
# 进入容器里,用域名来访问实现负载的效果
[root@master test]# kubectl exec -it test-client -- sh
/ # curl -s  http-test.default.svc.cluster.local./info.php | grep host
php_host:       http-test-57fb9c77d5-djrmd
/ # curl -s  http-test.default.svc.cluster.local./info.php | grep host
php_host:       http-test-57fb9c77d5-k4znk
补充一:

svc.spec.internalTrafficPolicy 是 Kubernetes 中 Service 对象的一个字段,用于控制来自集群内部源的流量如何被路由。该字段有两个可能的值:Cluster 和 Local。

  • 当设置为 Cluster(默认值)时,Kubernetes 会选择所有的服务端点来处理集群内部的流量。

  • 当设置为 Local 时,kube-proxy 仅对集群内部流量使用节点本地端点。这意味着流量只会被路由到与请求发起的客户端位于同一节点上的后端 Pod。这种策略通常用于优化性能,减少跨节点的网络流量,从而降低延迟和提高可靠性。

[root@master test]# kubectl explain svc.spec.internalTrafficPolicy
KIND:       Service
VERSION:    v1

FIELD: internalTrafficPolicy <string>

DESCRIPTION:
    InternalTrafficPolicy describes how nodes distribute service traffic they 
    receive on the ClusterIP. If set to "Local", the proxy will assume that pods
    only want to talk to endpoints of the service on the same node as the pod,
    dropping the traffic if there are no local endpoints. The default value,
    "Cluster", uses the standard behavior of routing to all endpoints evenly
    (possibly modified by topology and other features).
    
    Possible enum values:
         # 默认值为Cluster。node和pod都可以发送请求
     - `"Cluster"` routes traffic to all endpoints.  
         # 值为Local 只有pod可以发送请求,node发送请求报错
     - `"Local"` routes traffic only to endpoints on the same node as the client
    pod (dropping the traffic if there are no local endpoints).
补充二:

svc.spec.sessionAffinityConfig.clientIP 是 Kubernetes 中 Service 对象的一个字段,用于配置基于客户端 IP 的会话亲和性。会话亲和性是一种机制,它确保来自同一客户端 IP 的请求总是被路由到同一个后端 Pod,从而在多个请求之间保持会话状态。

如果 ServiceAffinity 设置为 “ClientIP”,则该值必须大于 0 且小于等于 86400(1 天)。默认值为 10800(3 小时)。这意味着在默认情况下,来自同一客户端 IP 的请求将在 3 小时内被路由到同一个 Pod。如果在这段时间内没有来自该客户端 IP 的新请求,会话亲和性将不再生效,后续的请求可能会被路由到不同的 Pod。

[root@master test]# kubectl explain svc.spec.sessionAffinityConfig.clientIP
KIND:       Service
VERSION:    v1

FIELD: clientIP <ClientIPConfig>

DESCRIPTION:
    clientIP contains the configurations of Client IP based session affinity.
    ClientIPConfig represents the configurations of Client IP based session
    affinity.
    
FIELDS:
  timeoutSeconds        <integer>
    timeoutSeconds specifies the seconds of ClientIP type session sticky time.
    The value must be >0 && <=86400(for 1 day) if ServiceAffinity == "ClientIP".
    Default value is 10800(for 3 hours).

2.9.4 NodePort(对外暴露应用)

NodePort相当于是ClusterIP的一个升级版可以实现ClusterIP的完整功能,在每个节点启用一个端口来暴露服务,可以在集群外部访问,通过NodeIP:NodePort访问端口范围:30000~32767

示例:

# 创建deployment,pod副本数为3
[root@master test]# vim deploy_nodeport.yaml 
---
kind: Deployment
apiVersion: apps/v1
metadata: 
  name: nodeprot-deploy
spec:
  replicas: 3
  selector: 
    matchLabels:
      app: apache
  template: 
    metadata: 
      labels: 
        app: apache
    spec: 
      containers: 
      - name: http
        image: myos:httpd
# 创建service的nodeport类型,实现集群外部访问
[root@master test]# vim nodeport.yaml 
---
kind: Service
apiVersion: v1
metadata: 
  name: nodeport
spec: 
  type: NodePort       # NodePort类型
  selector: 
    app: apache
  ports: 
  - name: http         # 定义多个端口的话需要为每个端口指定名字
    port: 80           # clusterIP 暴露的端口
    nodePort: 30333    # 集群的每台机器上要暴露的端口,不填的话由系统随机指定 (30000-32767)
    targetPort: 80     # 关联的 Pod 的端口,不指定的话默认和 port 字段相同

# 创建以上资源清单文件
[root@master test]# kubectl apply -f deploy_nodeport.yaml -f nodeport.yaml 
deployment.apps/nodeprot-deploy configured
service/nodeport unchanged
[root@master test]# kubectl get pods -o wide
NAME                               READY   STATUS    RESTARTS   AGE   IP              NODE         NOMINATED NODE   READINESS GATES
nodeprot-deploy-644c7f78f7-6czqq   1/1     Running   0          26m   10.244.85.197   node01   <none>           <none>
nodeprot-deploy-644c7f78f7-7pm4n   1/1     Running   0          26m   10.244.58.196   node02   <none>           <none>
nodeprot-deploy-644c7f78f7-r4lkx   1/1     Running   0          26m   10.244.58.195   node02   <none>           <none>

[root@master test]# kubectl describe svc nodeport 
Name:                     nodeport      # service名称
Namespace:                default
Labels:                   <none>
Annotations:              <none>
Selector:                 app=apache    # 选择app=apache标签的pod
Type:                     NodePort
IP Family Policy:         SingleStack
IP Families:              IPv4
IP:                       10.245.67.236 # 访问集群内部的IP
IPs:                      10.245.67.236
Port:                     http  80/TCP  # 端口名为http,svc暴露的端口
TargetPort:               80/TCP        # 关联pod的端口
NodePort:                 http  30333/TCP # 端口名为http,集群节点暴露的端口
                            # 以下3个IP是关联pod的ip
Endpoints:                10.244.58.195:80,10.244.58.196:80,10.244.85.197:80
Session Affinity:         None
External Traffic Policy:  Cluster
Events:                   <none>

查看ipvs规则发现每个节点的每张物理网卡都自动绑定了30333端口,而且还有rr算法轮询的效果,是NAT模式

[root@master test]# ipvsadm -Ln | grep :30333 -A 3
TCP  172.17.0.1:30333 rr
  -> 10.244.58.195:80             Masq    1      0          0         
  -> 10.244.58.196:80             Masq    1      0          0         
  -> 10.244.85.197:80             Masq    1      0          0               
TCP  192.168.10.10:30333 rr
  -> 10.244.58.195:80             Masq    1      0          1         
  -> 10.244.58.196:80             Masq    1      0          2         
  -> 10.244.85.197:80             Masq    1      0          1                
TCP  10.244.235.192:30333 rr
  -> 10.244.58.195:80             Masq    1      0          0         
  -> 10.244.58.196:80             Masq    1      0          0         
  -> 10.244.85.197:80             Masq    1      0          0  
  .....       
[root@node01 ~]# ipvsadm -Ln | grep :30333 -A 3       
TCP  192.168.10.11:30333 rr
  -> 10.244.58.195:80             Masq    1      0          0         
  -> 10.244.58.196:80             Masq    1      0          0         
  -> 10.244.85.197:80             Masq    1      0          0         
TCP  192.168.122.1:30333 rr
  -> 10.244.58.195:80             Masq    1      0          0         
  -> 10.244.58.196:80             Masq    1      0          0         
  -> 10.244.85.197:80             Masq    1      0          0         
TCP  10.244.85.192:30333 rr
  -> 10.244.58.195:80             Masq    1      0          0         
  -> 10.244.58.196:80             Masq    1      0          0         
  -> 10.244.85.197:80             Masq    1      0          0 
  ......        
[root@node02 ~]# ipvsadm -Ln | grep :30333 -A 3
TCP  192.168.10.12:30333 rr
  -> 10.244.58.195:80             Masq    1      0          0         
  -> 10.244.58.196:80             Masq    1      0          0         
  -> 10.244.85.197:80             Masq    1      0          0         
TCP  192.168.122.1:30333 rr
  -> 10.244.58.195:80             Masq    1      0          0         
  -> 10.244.58.196:80             Masq    1      0          0         
  -> 10.244.85.197:80             Masq    1      0          0         
TCP  10.244.58.192:30333 rr
  -> 10.244.58.195:80             Masq    1      0          0         
  -> 10.244.58.196:80             Masq    1      0          0         
  -> 10.244.85.197:80             Masq    1      0          0 
  ......       

访问每个节点的30333端口实现k8s集群外部访问的效果
验证效果:

[root@master test]# curl -s http://192.168.10.10:30333/info.php | grep host
php_host:       nodeprot-deploy-644c7f78f7-r4lkx
[root@master test]# curl -s http://192.168.10.10:30333/info.php | grep host
php_host:       nodeprot-deploy-644c7f78f7-6czqq
[root@master test]# curl -s http://192.168.10.10:30333/info.php | grep host
php_host:       nodeprot-deploy-644c7f78f7-7pm4n
[root@master test]# curl -s http://192.168.10.11:30333/info.php | grep host
php_host:       nodeprot-deploy-644c7f78f7-6czqq
[root@master test]# curl -s http://192.168.10.11:30333/info.php | grep host
php_host:       nodeprot-deploy-644c7f78f7-7pm4n
[root@master test]# curl -s http://192.168.10.11:30333/info.php | grep host
php_host:       nodeprot-deploy-644c7f78f7-r4lkx
[root@master test]# curl -s http://192.168.10.12:30333/info.php | grep host
php_host:       nodeprot-deploy-644c7f78f7-6czqq
[root@master test]# curl -s http://192.168.10.12:30333/info.php | grep host
php_host:       nodeprot-deploy-644c7f78f7-7pm4n
[root@master test]# curl -s http://192.168.10.12:30333/info.php | grep host
php_host:       nodeprot-deploy-644c7f78f7-r4lkx
补充

svc.spec.internalTrafficPolicysvc.spec.externalTrafficPolicy字段

  • ClusterIP类型

    • internalTrafficPolicy
      • Cluster
        1. 集群节点的工具可以通过虚拟IP进行访问
        2. 集群内部的Pod可以通过虚拟IP进行访问
      • Local:
        1. 仅仅只支持集群内部的Pod可以通过虚拟IP进行访问
  • NodePort 类型

    • internalTrafficPolicy
      • Cluster:
        1. 集群节点的工具可以通过虚拟IP进行访问
        2. 集群内部的Pod可以通过虚拟IP进行访问
      • Local:
        1. 仅仅只支持集群内部的Pod可以通过虚拟IP进行访问
    • externalTrafficPolicy
      • Cluster:
        1. 集群以外的客户端可以通过节点的物理网卡的物理IP对应的端口进行访问
        2. 集群节点的工具可以通过节点的物理网卡的物理IP对应的端口进行访问
      • Local:
        1. 仅仅只支持集群节点的工具可以通过节点的物理网卡的物理对应的端口进行访问

2.9.5 Ingress

为什么引入Ingress?

我们说k8s 的服务(service)时说暴露了service的三种方式ClusterIP、NodePort与LoadBalance,这几种方式都是在service的维度提供的,service的作用体现在两个方面,对集群内部,它不断跟踪pod的变化,更新endpoint中对应pod的对象,提供了ip不断变化的pod的服务发现机制,对集群外部,他类似负载均衡器,可以在集群内外部对pod进行访问。但是,单独用service暴露服务的方式,在实际生产环境中不太合适:

  • ClusterIP的方式只能在集群内部访问。

  • NodePort方式的话,测试环境使用还行,当有几十上百的服务在集群中运行时,NodePort的端口管理是灾难。

  • LoadBalance方式受限于云平台,且通常在云平台部署ELB还需要额外的费用。

所幸k8s还提供了一种集群维度暴露服务的方式,也就是ingress。ingress可以简单理解为service的service,他通过独立的ingress对象来制定请求转发的规则,把请求路由到一个或多个service中。这样就把服务与请求规则解耦了,可以从业务维度统一考虑业务的暴露,而不用为每个service单独考虑。

service是四层的负载均衡方案,Ingress是七层负载均衡访问方案

service通过iptables、ipvs实现。有缺陷:每一组业务都要开启nodePort,这样nodePort越来越多越来越难管理

解决方案:多加一层管理庞大的service

  • 本来nodePort是映射到Service的,现在在往上抽一层,映射到上层,由上层再分发到service,此方案Ingress实现了

  • 而Ingress是K8S中的资源,接口,可以由不同的控制器实现

  • Ingress-nginx是其中的一种实现,是对nginx的二次开发,来满足Ingress的规范ingress-nginx官方讲解

Ingress 是什么?
  • Ingress 公开了从集群外部到集群内服务的 HTTP 和 HTTPS 路由。 流量路由由 Ingress 资源上定义的规则控制。

  • Ingress 控制器通常由负载均衡器来实现(Nginx、HAProxy)

Ingerss安装
  • Ingress服务由(规则+控制器)组成

  • 规则负责制定策略,控制器负责执行

  • 如果没有控制器,单独设置规则无效

获取控制器镜像及资源文件

地址: https://github.com/kubernetes/ingress-nginx

示例:

# 验证后端服务
[root@master ~]# kubectl get pods,services 
NAME       READY   STATUS    RESTARTS   AGE
pod/web1   1/1     Running   0          35m

NAME                 TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)
service/kubernetes   ClusterIP   10.245.0.1    <none>        443/TCP
service/websvc       ClusterIP   10.245.1.80   <none>        80/TCP
service/mysvc        NodePort    10.245.3.88   <none>        80:30080/TCP

[root@master ~]# curl http://10.245.1.80
Welcome to The Apache.

# 查询 ingress 控制器类名称
[root@master ~]# kubectl get ingressclasses.networking.k8s.io 
NAME    CONTROLLER             PARAMETERS   AGE
nginx   k8s.io/ingress-nginx   <none>       5m7s

[root@master ~]# vim mying.yaml 
---
kind: Ingress
apiVersion: networking.k8s.io/v1
metadata: 
  name: mying
spec: 
  ingressClassName: nginx  # 使用的类名称
  rules:                   # ingress规则定义
  - host: nsd.tedu.cn      # 域名定义,没有可以不写
    http:                  # 协议
      paths:               # 访问的路径定义
      - path: /            # 访问的url路径
        pathType: Prefix   # 路径的类型[Exact(精准)、Prefix(宽松]
        backend:           # 定义后端服务
          service:         # 服务声明
            name: websvc   # 服务名称
            port:          # 端口号声明
              number: 80   # 访问服务的端口号
              
[root@master ~]# kubectl apply -f mying.yaml 
ingress.networking.k8s.io/mying created
[root@master ~]# kubectl get ingress
NAME    CLASS   HOSTS         ADDRESS        PORTS
mying   nginx   nsd.tedu.cn   192.168.10.11   80
[root@master ~]# curl -H "Host: nsd.tedu.cn" http://192.168.10.11
Welcome to The Apache.

2.9.6 LoadBalancer(对外暴露应用,适用于公有云)

与NodePort类似,在每个节点启用一个端口来暴露服务。除此之外,K8s请求底层云平台的负载均衡器,把每个[Node IP]:[NodePort]作为后端添加进去

华为云示例:

# 使用 CCE 集群服务调用 ELB 负载均衡
[root@harbor ~]# vim websvc.yaml
---
kind: Service
apiVersion: v1
metadata:
  name: websvc
  annotations:
    kubernetes.io/elb.class: union           # ELB 类型
    kubernetes.io/elb.id: <your_elb_id>      # ELB 服务的 ID
    kubernetes.io/elb.transparent-client-ip: 'true'  # 获取客户端 IP
spec:
  type: LoadBalancer
  selector:
    app: xk8s-httpd
  ports:
  - name: websvc
    protocol: TCP
    port: 80
    targetPort: webport

[root@harbor ~]# kubectl apply -f websvc.yaml 
service/websvc created

[root@harbor ~]# kubectl get service 
NAME         TYPE           CLUSTER-IP       EXTERNAL-IP
kubernetes   ClusterIP      10.245.0.1       <none>
websvc       LoadBalancer   10.245.209.220   139.9.168.100,192.168.0.250
# 使用浏览器访问公网IP 139.9.168.100,就能访问集群内的pod

2.9.7 ExternalName

ExternalName 类型的 Service 是 Kubernetes 中用于提供从集群内部访问外部 DNS 名称的一种方式。它的主要目的是简化集群内部应用访问外部服务的过程。当你创建一个类型为 ExternalName 的 Service 时,Kubernetes 会为这个 Service 创建一个 DNS 条目,该条目将解析为指定的外部 DNS 名称。

与 NodePort 或 LoadBalancer 类型的 Service 不同,ExternalName 类型的 Service 不会暴露任何集群内部的服务到外部网络,而是相反,它让集群内部的服务可以访问外部的服务。

示例:

[root@master test]# cat test.yaml 
---
kind: Pod
apiVersion: v1
metadata: 
  name: test
spec: 
  containers: 
  - name: test
    image: myos:httpd
[root@master test]# cat externalname.yaml 
---
kind: Service
apiVersion: v1
metadata: 
  name: externalname
spec: 
  type: ExternalName # 类型
  externalName: www.baidu.com # 外部的dns

# 创建以上两个资源清单
[root@master test]# kubectl apply -f test.yaml -f externalname.yaml
# 查看svc信息
[root@master test]# kubectl get svc
NAME           TYPE           CLUSTER-IP   EXTERNAL-IP     PORT(S)   AGE
externalname   ExternalName   <none>       www.baidu.com   <none>    11m
kubernetes     ClusterIP      10.245.0.1   <none>          443/TCP   6d17h
# 进入测试pod
[root@master test]# kubectl exec -it test -- bash
# 访问名为externalname的svc的域名,得到是百度的响应就成功
[root@test html]# ping externalname.default.svc.cluster.local.
PING www.a.shifen.com (103.235.47.188) 56(84) bytes of data.
64 bytes from 103.235.47.188 (103.235.47.188): icmp_seq=1 ttl=127 time=228 ms
64 bytes from 103.235.47.188 (103.235.47.188): icmp_seq=2 ttl=127 time=236 ms
64 bytes from 103.235.47.188 (103.235.47.188): icmp_seq=3 ttl=127 time=231 ms
^C
--- www.a.shifen.com ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2008ms
rtt min/avg/max/mdev = 228.156/231.495/235.757/3.170 ms

2.9.8 Endpoints与Service和Pod间的关联

Kubernetes中的Service,它定义了一组Pods的逻辑集合和一个用于访问它们的策略。一个Service的目标Pod集合通常是由LabelSelector来决定的

Endpoints是一组实际服务的端点集合。一个Endpoint是一个可被访问的服务端点,即一个状态为running的pod的可访问端点。一般Pod都不是一个独立存在,所以一组Pod的端点合在一起称为EndPoints。只有被ServiceSelector匹配选中并且状态为Running的Pod才会被加入到和Service同名的Endpoints 中。

Service与endpoints的关联关系如下:

  • 有定义标签选择器

    • 自动创建一个同名的endpoints资源对象
      • 匹配对象:当前名称空间的pod
        1. 标签子集运算
        2. 就绪的状态
  • 没有定义标签选择器

    • 不会创建同名的endpoints资源对象,需要管理员手动创建
      • 匹配对象
        1. 管理员填写的端点信息,灵活性强

手动关联service示例:

[root@master test02]# vim test_service.yaml 
---
kind: Service
apiVersion: v1
metadata: 
  name: nginx
spec: 
  type: ClusterIP  # service的类型
  clusterIP: 10.245.8.8 # 固定service的IP
  ports: 
    - targetPort: 80 # 访问后端服务器的端口
      port: 8888     # service暴露的端口
      protocol: TCP  # 使用的协议
---
kind: Endpoints
apiVersion: v1
metadata:
  name: nginx # 必须要和关联的service的名一样
subsets:  # 定义关联的后端服务器的IP
  - addresses:
      - ip: 192.168.10.12 # 关联集群中的nodeIP,可以是多个
    ports:
      - port: 80 # 访问nodeIP的80端口

# 创建资源清单文件
[root@master test02]# kubectl apply -f test_service.yaml 
service/nginx created
endpoints/nginx created

# 查看svc信息
[root@master test02]# kubectl get svc
NAME         TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)    AGE
kubernetes   ClusterIP   10.245.0.1   <none>        443/TCP    7d3h
nginx        ClusterIP   10.245.8.8   <none>        8888/TCP   6s

# 查看endpoints信息
[root@master test02]# kubectl get ep
NAME         ENDPOINTS             AGE
kubernetes   192.168.10.10:6443   7d3h
nginx        192.168.10.12:80     9s

# 查看ipvs规则,自动添加的规则
[root@master test02]# ipvsadm -Ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
  -> RemoteAddress:Port           Forward Weight ActiveConn InActConn
TCP  10.245.0.1:443 rr
  -> 192.168.10.10:6443          Masq    1      6          0         
TCP  10.245.0.10:53 rr
  -> 10.244.235.207:53            Masq    1      0          0         
  -> 10.244.235.208:53            Masq    1      0          0         
TCP  10.245.0.10:9153 rr
  -> 10.244.235.207:9153          Masq    1      0          0         
  -> 10.244.235.208:9153          Masq    1      0          0         
TCP  10.245.8.8:8888 rr  # svcIP的规则,自动绑定后端ip,实现轮询效果
  -> 192.168.10.12:80            Masq    1      0          0         
UDP  10.245.0.10:53 rr
  -> 10.244.235.207:53            Masq    1      0          0         
  -> 10.244.235.208:53            Masq    1      0          0       
 
# 验证访问svc的8888端口,得到的是自己关联的后端的80端口的信息
[root@master test02]# curl 10.245.8.8:8888
wo shi hehe   

2.10 k8s存储

2.10.1 概述

在Kubernetes(K8s)中,存储系统是一个关键的组成部分,用于管理容器化应用的数据持久性和共享性。K8s的存储分类可以从多个维度进行理解,但主要分为两大类:临时存储和持久存储。关于元数据和真实数据的分类,虽然这两个概念在存储系统中普遍存在,但在K8s的存储分类中,它们并不是直接用于分类存储类型的标准。不过,可以从K8s存储类型如何管理和使用这些数据的角度来探讨。

2.10.2 k8s支持的卷类型

  • 持久卷:持久卷是集群中的存储资源,就像他的名字一样,在里面存储的数据不会随着 Pod 的删除而丢失。
  • 临时卷:有些应用程序需要额外的存储,但并不关心数据在重启后是否仍然可用。卷会遵从 Pod的生命周期,与Pod一起创建和删除。
  • 投射卷:它允许您将多个现有卷源映射到同一个目录。通过将这些不同类型的卷源组合成一个统一的卷,可以更方便地管理和使用这些资源

2.10.3 临时存储

**EmptyDir:**EmptyDir是一种在Pod中创建的空目录,用于在容器之间共享文件。它的数据存储在Pod所在节点的本地磁盘上,当Pod被删除时,数据也会被删除。这种存储方式适用于需要临时存储数据的场景,如缓存数据。在这种情况下,元数据(如目录结构、文件属性等)和真实数据(文件内容)都是临时的,与Pod的生命周期绑定。

2.10.4 持久存储

PersistentVolume (PV) 和 PersistentVolumeClaim (PVC):

PV是由管理员配置的存储资源,而PVC是用户请求的存储资源。PVC允许用户抽象地请求存储资源,而不需要关心具体的存储后端。PV和PVC的结合使用,可以动态地分配和释放存储资源,用于持久化存储真实数据。元数据(如PV和PVC的配置信息)存储在K8s的etcd数据库中,而真实数据则存储在配置的存储后端(如NFS、Ceph等)上。

NFS:

NFS卷将网络文件系统(NFS)挂载到容器中,允许跨多个Pod和节点共享数据。元数据(如NFS文件系统的目录结构、文件权限等)和真实数据都存储在NFS服务器上,实现了数据的持久化和共享。

ConfigMap 和 Secret:

虽然ConfigMap和Secret主要用于挂载配置文件和密钥到容器中,但它们也可以视为一种存储形式。这些资源对象的元数据(如配置项的名称、值等)和真实数据(配置文件内容、密钥值等)都存储在K8s的etcd数据库中。不过,它们的主要用途是配置和安全性,而非大规模的数据存储。

StatefulSet:

StatefulSet是一种用于管理有状态应用的控制器,它确保每个Pod都有稳定的标识和顺序。StatefulSet通常会为每个Pod分配一个独特的持久卷(通过PVC实现),以存储Pod的持久化数据。在这种情况下,元数据(如StatefulSet的配置、Pod的标识等)存储在K8s的etcd数据库中,而真实数据则存储在分配的持久卷上。

总结

在K8s中,元数据和真实数据的存储和管理是通过不同的机制实现的。元数据通常存储在K8s的etcd数据库中,用于管理集群的状态和配置。而真实数据则根据所选的存储类型(如PV、PVC、NFS等)存储在相应的存储后端上。通过合理配置和使用这些存储类型,K8s能够提供灵活、可靠的数据存储解决方案,满足各种应用场景的需求。

2.10.5 ConfigMap

K8s(Kubernetes)中的ConfigMap是一种用于存储配置数据的API对象,它属于Kubernetes中的核心对象。ConfigMap的主要作用是将应用程序的配置信息与容器镜像分离,以便在不重新构建镜像的情况下进行配置的修改和更新。

用途:

ConfigMap用于存储键值对形式的配置数据,这些数据可以包括环境变量、命令行参数、配置文件等。它提供了一种集中管理和传递配置信息的机制,使得应用程序能够从ConfigMap中获取配置数据,从而在不修改容器镜像的前提下,动态地修改应用程序的配置参数。

与Secret的区别

ConfigMap主要用于存储非敏感的配置数据,如应用程序的配置文件、环境变量等,而Secret则用于存储敏感的数据,如密码、密钥等。Secret提供了更高的安全性和访问控制机制。

通过命令行管理ConfigMap示例:

使用文字值创建,利用 --from-literal 参数传递配置信息,该参数可以使用多次

[root@master test02]# kubectl create cm literal-config --from-literal=name=lili --from-literal=passwd=123
configmap/literal-config created

# 查看指定cm的属性信息
[root@master test02]# kubectl describe cm literal-config
Name:         literal-config
Namespace:    default
Labels:       <none>
Annotations:  <none>

Data
====
name:
----
lili
passwd:
----
123

BinaryData
====

Events:  <none>

# 使用YAML格式输出
[root@master test02]# kubectl get cm literal-config -o yaml
apiVersion: v1
data:
  name: lili
  passwd: "123"
kind: ConfigMap
metadata:
  creationTimestamp: "2025-01-24T03:33:55Z"
  name: literal-config
  namespace: default
  resourceVersion: "391106"
  uid: 860618b7-3177-4f8c-ac83-62d458da7dc0
通过目录创建

–from-file 指定在目录下的所有文件都会被用在 ConfigMap 里面创建一个键值对,键的名字就是文件名,值就是文件的内容

示例:

# 以下两个文件里分别有两个值
[root@master ~]# ls test_cm/
name  passwd

# 通过目录创建
[root@master ~]# kubectl create cm test-cmdir --from-file=./test_cm/
configmap/test-cmdir created

# 查看configmap
[root@master ~]# kubectl get cm
NAME               DATA   AGE
kube-root-ca.crt   1      9d
literal-config     2      35m
test-cmdir         2      10s

# 查看属性信息
[root@master ~]# kubectl describe cm test-cmdir 
Name:         test-cmdir
Namespace:    default
Labels:       <none>
Annotations:  <none>

Data
====
name:
----
haha
lili

passwd:
----
1234
6789


BinaryData
====

Events:  <none>

# 通过yaml格式输出
[root@master ~]# kubectl get cm test-cmdir -o yaml
apiVersion: v1
data:
  name: |
    haha
    lili
  passwd: |
    1234
    6789
kind: ConfigMap
metadata:
  creationTimestamp: "2025-01-24T04:09:00Z"
  name: test-cmdir
  namespace: default
  resourceVersion: "394234"
  uid: 35476d3a-3ac0-4409-b04d-06936cde7d1e
通过文件创建

–from-file 参数只要指定为一个文件就可以从单个文件中创建 ConfigMap。–from-file 这个参数可以使用多次,你可以使用两次分别指定上个实例中的那两个配置文件,效果就跟指定整个目录是一样的

示例:

[root@master test02]# kubectl create cm test-config --from-file=test_cm.file 
configmap/test-config created

# 查看configmap
[root@master test02]# kubectl get cm
NAME               DATA   AGE
kube-root-ca.crt   1      9d
literal-config     2      108m
test-cmdir         2      73m
test-config        1      6s

# 查看cm的属性信息
[root@master test02]# kubectl describe cm test-config 
Name:         test-config
Namespace:    default
Labels:       <none>
Annotations:  <none>

Data
====
test_cm.file:
----
name=hehe
passwd=123456


BinaryData
====

Events:  <none>

# 以yaml的格式输出指定cm
[root@master test02]# kubectl get cm test-config -o yaml
apiVersion: v1
data:
  test_cm.file: |
    name=hehe
    passwd=123456
kind: ConfigMap
metadata:
  creationTimestamp: "2025-01-24T05:22:24Z"
  name: test-config
  namespace: default
  resourceVersion: "396512"
  uid: bfa82c9a-2aea-425e-8623-fe26aee5473b
环境变量

**环境变量:**可以将ConfigMap中的数据设置为Pod中容器的环境变量。这样,容器在启动时就可以从环境变量中获取配置信息。

**注意:**使用该 ConfigMap 挂载的 Env 不会同步更新

示例:

[root@master test02]# vim config-env.yaml 
---
kind: ConfigMap
apiVersion: v1
metadata: 
  name: literal-config
data: 
  name: hehe     # 定义数据
  passwd: admin
---
kind: ConfigMap
apiVersion: v1
metadata:
  name: env-config
data: 
  log_level: INFO
---
kind: Pod   # 定义Pod
apiVersion: v1
metadata: 
  name: env-pod
spec: 
  restartPolicy: Never   # 重启策略 永不
  containers: 
  - name: test
    image: myos:nginx
    command:   # 启动命令 打印容器内部env环境变量
    - sh
    - -c
    - env
    env:       # 定义env 为容器内部添加环境变量
    - name: USERNAME  # 环境变量名字
      valueFrom:      # 值来源
        configMapKeyRef:       # 值来源于configmap
          name: literal-config # 值来源的configmap类别的名字
          key: name            # key的名字/字段
    - name: PASSWORD
      valueFrom: 
        configMapKeyRef:
          name: literal-config
          key: passwd
    envFrom:     # 直接引入名为env-config的configmap
    - configMapRef: 
        name: env-config
#创建以上资源清单文件
[root@master test02]# kubectl apply -f config-env.yaml 
configmap/literal-config unchanged
configmap/env-config unchanged
pod/env-pod created        

[root@master test02]# kubectl get pods
NAME      READY   STATUS      RESTARTS   AGE
env-pod   0/1     Completed   0          5m10s

[root@master test02]# kubectl logs env-pod 
NGINX_PORT_8888_TCP=tcp://10.245.8.8:8888
NGINX_PORT_8888_TCP_ADDR=10.245.8.8
HOSTNAME=env-pod
USERNAME=hehe     # literal-config添加的环境变量
NGINX_PORT_8888_TCP_PORT=8888
PASSWORD=admin    # literal-config添加的环境变量
KUBERNETES_PORT_443_TCP_PROTO=tcp
KUBERNETES_PORT_443_TCP_ADDR=10.245.0.1
NGINX_PORT=tcp://10.245.8.8:8888
KUBERNETES_PORT=tcp://10.245.0.1:443
PWD=/usr/local/nginx/html
HOME=/root
NGINX_SERVICE_PORT=8888
KUBERNETES_SERVICE_PORT_HTTPS=443
KUBERNETES_PORT_443_TCP_PORT=443
KUBERNETES_PORT_443_TCP=tcp://10.245.0.1:443
SHLVL=1
NGINX_PORT_8888_TCP_PROTO=tcp
KUBERNETES_SERVICE_PORT=443
log_level=INFO    # env-config添加的环境变量
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/local/nginx/sbin
KUBERNETES_SERVICE_HOST=10.245.0.1
NGINX_SERVICE_HOST=10.245.8.8
_=/usr/bin/env
命令行参数

命令行参数:将ConfigMap中的数据作为命令行参数传递给容器中的应用程序。这通常需要先将ConfigMap的数据保存在环境变量中,然后通过环境变量的方式引用。

[root@master test02]# vim cm-command.yaml 
---
kind: Pod
apiVersion: v1
metadata: 
  name: cm-command-pod
spec: 
  restartPolicy: Never
  containers: 
  - name: test
    image: myos:nginx
    command: 
    - sh
    - -c
    - |
      echo ${USERNAME} ${PASSWORD}
    env:    # 这里引用上面案例的configmap
    - name: USERNAME
      valueFrom: 
        configMapKeyRef: 
          name: literal-config
          key: name
    - name: PASSWORD
      valueFrom: 
        configMapKeyRef: 
          name: literal-config
          key: passwd
  
[root@master test02]# kubectl apply -f cm-command.yaml 
pod/cm-command-pod created

[root@master test02]# kubectl logs cm-command-pod 
hehe admin
卷挂载

卷挂载:ConfigMap可以作为卷挂载到Pod中,使得容器可以直接读取ConfigMap中的配置文件。每个键值对都会生成一个文件,其中键为文件名,值为文件内容。这样,应用程序就可以根据需要读取配置文件中的配置信息。

示例:

[root@master test02]# vim cm-volume.yaml 
---
apiVersion: v1
kind: Pod
metadata:
 name: cm-volume-pod
spec:
 restartPolicy: Never
 containers:
  - name: test
    image: myos:nginx
    volumeMounts: # volume挂载
    - name: config-volume    # 挂载下面指定的 volume
      mountPath: /etc/config # 挂载到的目录(容器内路径,该目录下,文件名就是键名,文件内容就是键值)
      subPath: 
 volumes:
  - name: config-volume    # volume 名称
    configMap:             # 来自configmap
     name: literal-config  # 上边的示例已经定义过
     
[root@master test02]# kubectl apply -f cm-volume.yaml 
pod/cm-volume-pod created

[root@master test02]# kubectl exec -it cm-volume-pod -- bash

[root@cm-volume-pod html]# ls /etc/config
name  passwd
# 这种方式创建的是链接文件 热更新
[root@cm-volume-pod html]# cat /etc/config/name
hehe[root@cm-volume-pod html]# cat /etc/config/passwd
admin[root@cm-volume-pod html]# ls -l /etc/config
total 0
lrwxrwxrwx 1 root root 11 Jan 24 07:16 name -> ..data/name
lrwxrwxrwx 1 root root 13 Jan 24 07:16 passwd -> ..data/passwd
热更新

通过kubectl edit configmap [configmap name]命令直接修改内容就可以达到热更新

示例:

[root@master test02]# cat cm-update.yaml 
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: log-config
  namespace: default
data:
  log_level: INFO
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-nginx
spec:
  replicas: 1
  selector: 
    matchLabels: 
      run: my-nginx
  template:
    metadata:
      labels:
        run: my-nginx
    spec:
      containers:
        - name: nginx
          image: myos:nginx
          ports:
            - containerPort: 80
          volumeMounts:
            - name: config-volume
              mountPath: /etc/config # 容器内这个目录下会有 log_level 这个文件,内容为 INFO
      volumes:
        - name: config-volume
          configMap:
            name: log-config
# 查看容器里的文件
[root@master test02]# kubectl exec -it my-nginx-746bd4859b-qw6wp -- cat /etc/config/log_level
INFO
# 使用edit命令修改cm的键值为NOTICE
[root@master test02]# kubectl edit cm log-config 
configmap/log-config edited
# 过个10秒再次查看容器里的文件,文件里的配置修改了
[root@master test02]# kubectl exec -it my-nginx-746bd4859b-qw6wp -- cat /etc/config/log_level
NOTICE

补充:添加不可改变选项

# 添加不可改变选项 immutable: true 
[root@master test02]# kubectl edit cm log-config 
# Please edit the object below. Lines beginning with a '#' will be ignored,
# and an empty file will abort the edit. If an error occurs while saving this file will be
# reopened with the relevant failures.
#
apiVersion: v1
data:
  log_level: NOTICE
immutable: true  # 添加不可改变选项
kind: ConfigMap
metadata:
  annotations:
    kubectl.kubernetes.io/last-applied-configuration: |
      {"apiVersion":"v1","data":{"log_level":"INFO"},"kind":"ConfigMap","metadata":{"annotations":{},"name":"log-config","namespace":"default"}}
  creationTimestamp: "2025-01-24T08:56:32Z"
  name: log-config
  namespace: default
  resourceVersion: "429330"
  uid: 4e138ec5-861f-4205-8611-69c00035e973
# 添加之后就无法进行热更新了
# 添加之后是不可逆的 需要重新创建一个cm
补充

Pod滚动更新

ConfigMap 更新后,并不会让相应的文件重载。例如,Nginx 在启动时,会加载一次配置文件(配置文件中有 ConfigMap 的相关参数),加载完成后,无论这个配置文件再怎么变化,Nginx 都不会再加载它。因此需要 ConfigMap 更新后,再滚动更新 Pod。

可以通过修改 pod annotations 的方式强制触发滚动更新。这里我们在 Deployment.spec.template.metadata.annotations 中添加 version/config字段来实现pod的滚动更新

kubectl patch deployment my-nginx --patch '{"spec": {"template": {"metadata": {"annotations":{"version/config": "20250124" }}}}}'

注意:更新 ConfigMap 后:

  • 使用该 ConfigMap 挂载的 Env 不会同步更新

  • 使用该 ConfigMap 挂载的 Volume 中的数据需要一段时间(实测大概10秒)才能同步更新

CM的优势
  • **配置解耦:**将配置信息与容器镜像解耦,使得配置可以在不重新构建镜像的情况下进行修改和管理。
  • **动态更新:**ConfigMap中的配置可以在运行时动态更新,而不需要重新启动应用程序。
  • **版本控制:**ConfigMap中的配置可以使用版本控制系统进行管理,随时回滚到之前的版本。
  • **共享和复用:**ConfigMap可以被多个应用程序共享和复用,提高了配置的一致性和可维护性。

综上所述,K8s ConfigMap是Kubernetes中用于存储和管理配置数据的重要组件,它提供了灵活的配置管理方式,使得应用程序的配置更加清晰、易于管理和更新。

2.10.6 Secret

K8s(Kubernetes)中的Secret是一种用于保存敏感信息的资源对象,如密码、OAuth令牌、ssh密钥等。这些信息如果直接放在Pod的定义中或镜像中,可能会带来安全风险,因为Pod的定义和镜像都可能被存储在版本控制系统中,或者被不同的用户访问。通过使用Secret,可以更安全地管理这些敏感信息。

Secret特性
  • **安全性:**Secret中的信息被加密存储(实际上是Base64编码,但Kubernetes社区通常称之为加密),以减少敏感信息泄露的风险。
  • **灵活性:**Secret可以以多种方式被Pod使用,包括作为环境变量、挂载到Pod中的卷中的文件,或者在kubelet为Pod拉取镜像时使用。
  • **可重用性:**多个Pod可以引用同一个Secret,从而避免在多个地方重复存储相同的敏感信息。
Secret的类型
  • Opaque:这是默认的Secret类型,用于存储任意格式的敏感信息。数据以Base64编码的形式存储在Secret中。

  • kubernetes.io/service-account-token:由Kubernetes自动创建,用于Pod与API Server之间的通信认证。

  • kubernetes.io/dockerconfigjson:用于存储私有Docker Registry的认证信息。

  • kubernetes.io/tls:用于存储TLS证书和私钥,以便Pod能够使用SSL/TLS协议进行安全通信。

  • kubernetes.io/basic-auth:用于存储基本认证信息,如用户名和密码

Opaque类型 (使用最多)

Opaque和configMap很像, 数据是一个 map 类型,要求 value 是 base64 编码格式,可以用于环境变量和数据卷挂载

示例:

# 使用base64编码
[root@master test02]# echo -n hehe | base64
aGVoZQ==
[root@master test02]# echo -n 321 | base64
MzIx
# 解码
[root@master test02]# echo -n aGVoZQ== | base64 -d
hehe
# 使用加密后的用户名和密码创建 Secret
[root@master test02]# vim secret.yaml 
---
kind: Secret
apiVersion: v1
metadata: 
  name: mysecret
type: Opaque
data: 
  passwd: MzIx
  username: aGVoZQ==

[root@master test02]# kubectl apply -f secret.yaml 
secret/mysecret created
# 查看secret
[root@master test02]# kubectl get secret
NAME       TYPE     DATA   AGE
mysecret   Opaque   2      2m18s
# 查看secret属性信息
[root@master test02]# kubectl describe secrets mysecret 
Name:         mysecret
Namespace:    default
Labels:       <none>
Annotations:  <none>

Type:  Opaque

Data
====
passwd:    3 bytes
username:  4 bytes
# 以yaml的格式输出指定的secret
[root@master test02]# kubectl get secrets mysecret -o yaml
apiVersion: v1
data:
  passwd: MzIx
  username: aGVoZQ==
kind: Secret
metadata:
  annotations:
    kubectl.kubernetes.io/last-applied-configuration: |
      {"apiVersion":"v1","data":{"passwd":"MzIx","username":"aGVoZQ=="},"kind":"Secret","metadata":{"annotations":{},"name":"mysecret","namespace":"default"},"type":"Opaque"}
  creationTimestamp: "2025-01-24T15:57:16Z"
  name: mysecret
  namespace: default
  resourceVersion: "442203"
  uid: 9bff22dc-aaf2-4c1f-af1f-d4bda8accd8c
type: Opaque
在pod中使用secret
环境变量

作为环境变量(不可以热更新)

[root@master test02]# vim mysecret.yaml 
---
kind: Deployment
apiVersion: apps/v1
metadata: 
  name: deploy-secret
spec: 
  replicas: 3
  selector: 
    matchLabels: 
      app: secret
  template: 
    metadata: 
      labels: 
        app: secret
    spec: 
      containers: 
      - name: test
        image: myos:httpd
        command: 
        - sh
        - -c
        - |
          echo ${TEST_USER} ${TEST_PASS}
        env:     # 添加环境变量
        - name: TEST_USER    # 环境变量名
          valueFrom:         # 值来源
            secretKeyRef:    # 从 Secret 中获取
              name: mysecret # Secret 的名字,在以上示例已定义
              key: username  # Secret 中的键名,上面案例定义的
        - name: TEST_PASS
          valueFrom: 
            secretKeyRef: 
              name: mysecret
              key: passwd
[root@master test02]# kubectl apply -f mysecret.yaml 
deployment.apps/deploy-secret created

# secret中的键值会自动的解码
[root@master test02]# kubectl logs deploy-secret-6c848876bf-dcb2s 
hehe 321
卷挂载

secret也是支持热更新的和configmap一样,但是使用secret作为子路径卷挂载的容器不会收到secret更新

将secret挂载到volume中示例:

[root@master test02]# vim secret_volume.yaml 
---
kind: Pod
apiVersion: v1
metadata: 
  name: secret-test
spec: 
  volumes:        # 创建一个卷
  - name: secret  # 卷名
    secret:       # 卷使用的方案
      secretName: mysecret  # 来自于上面案例创建的 mysecret
  containers: 
  - name: test
    image: myos:nginx
    volumeMounts:       # 卷挂载
    - name: secret      # 挂载的是上面声明的 secrets
      mountPath: /data  # 挂载的目录(容器内目录)
      readOnly: true    # 只读 

[root@master test02]# kubectl apply -f secret_volume.yaml 
pod/secret-test created

[root@master test02]# kubectl get pods
NAME          READY   STATUS    RESTARTS   AGE
secret-test   1/1     Running   0          4s

# 进入容器
[root@master test02]# kubectl exec -it secret-test -- bash
[root@secret-test html]# cd /data
# Opaque Secret 中的用户名和密码都已经挂载进来了
[root@secret-test data]# ls
passwd  username
# 查看内容,发现内容已经自动被解码
[root@secret-test data]# cat passwd 
321[root@secret-test data]# cat username 
hehe

注意事项:

  • 当使用Secret时,应确保Pod有足够的权限来访问这些Secret。

  • Secret中的信息虽然被加密(实际上是Base64编码),但应尽量避免将过于敏感的信息存储在Kubernetes集群中,以防止潜在的泄露风险。

  • 定期检查并更新Secret中的敏感信息,以确保系统的安全性。

2.10.7 Downward API

在Kubernetes(k8s)中,Downward API 是一种特殊类型的 API,它允许 Pod 中的容器获取关于 Pod 本身及其所在环境的元数据信息。这些信息可以通过两种方式注入到容器内部:环境变量和卷挂载(Volume Mounts)。

Downward API 的两种注入方式
环境变量

环境变量是 Downward API 注入信息到容器的常用方式,适用于单个变量的情况。通过 Downward API,可以将 Pod 的 IP 地址、名称、命名空间等基本信息以环境变量的形式注入到容器内部。这样,容器内的应用程序就可以通过读取这些环境变量来获取 Pod 的相关信息。

示例:

[root@master test02]# vim test_downwardapi.yaml 
kind: Pod
apiVersion: v1
metadata: 
  name: downward-api-pod
spec: 
  restartPolicy: Never
  containers: 
  - name: test
    image: myos:nginx
    env:   # 定义容器的环境变量
    - name: POD_NAME # POD_NAME 环境变量,其值来源于 Pod 的元数据字段 metadata.name
      valueFrom: 
        fieldRef: 
          fieldPath: metadata.name
    - name: NAMESPACE
      valueFrom: 
        fieldRef: 
          fieldPath: metadata.namespace
    - name: POD_IP
      valueFrom: 
        fieldRef: 
          fieldPath: status.podIP
    - name: CPU_LIMIT  # CPU_LIMIT 环境变量,其值来源于 Pod 的资源限制字段 limits.cpu
      valueFrom: 
        resourceFieldRef: 
          resource: limits.cpu
    - name: CPU_REQUEST
      valueFrom: 
        resourceFieldRef: 
          resource: requests.cpu
          
[root@master test02]# kubectl apply -f test_downwardapi.yaml 
pod/downward-api-pod created

[root@master test02]# kubectl get pods
NAME               READY   STATUS    RESTARTS   AGE
downward-api-pod   1/1     Running   0          9s
secret-test        1/1     Running   0          5h58m

# 验证效果
[root@master test02]# kubectl exec -it downward-api-pod -- env
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/local/nginx/sbin
HOSTNAME=downward-api-pod
POD_IP=10.244.85.215         # 得到的pod的IP地址
CPU_LIMIT=2                  # 限制2个cpu
CPU_REQUEST=0                # 0代表没有进行配额的设置
POD_NAME=downward-api-pod    # pod的名称
NAMESPACE=default
NGINX_PORT_8888_TCP_PORT=8888
KUBERNETES_SERVICE_PORT_HTTPS=443
NGINX_PORT_8888_TCP_ADDR=10.245.8.8
NGINX_PORT=tcp://10.245.8.8:8888
KUBERNETES_PORT_443_TCP_PROTO=tcp
KUBERNETES_PORT_443_TCP_PORT=443
NGINX_SERVICE_HOST=10.245.8.8
NGINX_PORT_8888_TCP=tcp://10.245.8.8:8888
KUBERNETES_SERVICE_PORT=443
KUBERNETES_PORT=tcp://10.245.0.1:443
KUBERNETES_PORT_443_TCP=tcp://10.245.0.1:443
KUBERNETES_PORT_443_TCP_ADDR=10.245.0.1
NGINX_SERVICE_PORT=8888
NGINX_PORT_8888_TCP_PROTO=tcp
KUBERNETES_SERVICE_HOST=10.245.0.1
TERM=xterm
HOME=/root
卷挂载

是将 Pod 的信息生成为文件,并通过卷挂载的方式将这些文件注入到容器内部。这种方式适用于需要批量处理或复杂查询 Pod 信息的情况。

示例:

[root@master test02]# vim test_downwardapi02.yaml 
---
kind: Pod
apiVersion: v1
metadata: 
  name: test-volume-pod
  labels: 
    app: volume
spec: 
  restartPolicy: Never
  containers:
  - name: test
    image: myos:nginx
    resources: 
      limits: 
        cpu: 1
        memory: 400Mi
      requests:
        cpu: 1
        memory: 300Mi
    volumeMounts: 
    - name: downwardapi-volume
      mountPath: /podinfo
  volumes: 
  - name: downwardapi-volume
    downwardAPI: 
      items: 
      - path: "labels"  # 挂载的文件路径
        fieldRef: 
          fieldPath: metadata.labels  # 引用的字段路径
      - path: "name"
        fieldRef: 
          fieldPath: metadata.name
      - path: "namespace"
        fieldRef:
          fieldPath: metadata.namespace
      - path: "uid"
        fieldRef:
          fieldPath: metadata.uid
      - path: "cpuRequest"
        resourceFieldRef:
          containerName: test     # 引用的容器名称
          resource: requests.cpu  # 引用的资源字段
      - path: "memoryRequest"     # 挂载的文件路径
        resourceFieldRef:
          containerName: test
          resource: requests.memory
      - path: "cpuLimit"
        resourceFieldRef:
          containerName: test
          resource: limits.cpu
      - path: "memoryLimit"
        resourceFieldRef:
          containerName: test
          resource: limits.memory
          
[root@master test02]# kubectl get pods
NAME               READY   STATUS    RESTARTS   AGE
downward-api-pod   1/1     Running   0          136m
secret-test        1/1     Running   0          8h
test-volume-pod    1/1     Running   0          8m2s
# 进入容器验证效果
[root@master test02]# kubectl exec -it test-volume-pod -- bash
[root@test-volume-pod html]# cd /podinfo 
[root@test-volume-pod podinfo]# ls
cpuLimit    labels       memoryRequest  namespace
cpuRequest  memoryLimit  name           uid
[root@test-volume-pod podinfo]# cat name
test-volume-pod[root@test-volume-pod podinfo]# cat labels
app="volume"
Downward API 支持的字段

Downward API 支持的字段包括但不限于:

  • spec.nodeName:宿主机名字

  • status.hostIP:宿主机 IP

  • metadata.name:Pod 的名字

  • metadata.namespace:Pod 的 Namespace

  • status.podIP:Pod 的 IP

  • spec.serviceAccountName:Pod 的 Service Account 的名字

  • metadata.uid:Pod 的 UID

  • metadata.labels[‘’]:指定 的 Label 值

  • metadata.annotations[‘’]:指定 的 Annotation 值

  • metadata.labels:Pod 的所有 Label

  • metadata.annotations:Pod 的所有 Annotation

使用 Downward API 的步骤
  1. 创建包含 Downward API 信息的 Pod:编写 Pod 的 YAML 配置文件,定义需要注入的环境变量或卷挂载。

  2. 使用 kubectl 创建 Pod:使用 kubectl apply -f 命令创建 Pod。

  3. 在容器中读取 Downward API 注入的信息:进入 Pod 的容器内部,通过环境变量或文件来读取注入的信息。

通过以上步骤,你可以在 Kubernetes 中使用 Downward API 来获取 Pod 的相关信息,并将其注入到容器内部,以满足应用程序的需求。

2.10.8 Volume

K8s(Kubernetes)中的Volume(存储卷)是一种用于在Pod中持久存储数据的机制,它为Pod中的容器提供了一个共享的存储空间

定义与用途

**定义:**在K8s中,Volume是一种抽象的概念,用于提供Pod中容器的持久化存储。它允许将数据存储在Pod的生命周期之外,以便在容器重启、迁移或重新调度时保留数据。

用途:

  • 数据持久化:将数据存储在Volume中,确保容器重启后数据仍然存在。

  • 数据共享:Volume可以连接到Pod中的一个或多个容器,使它们能够共享相同的数据。

  • 数据备份和恢复:使用Volume来备份和还原应用程序的数据。

  • 数据迁移和复制:将Volume从一个Pod迁移到另一个Pod,或将Volume复制到其他地方。

kubernets支持的卷的类型

官网:https://kubernetes.io/zh/docs/concepts/storage/volumes/

k8s支持的卷的类型如下:

awsElasticBlockStore 、azureDisk、azureFile、cephfs、cinder、configMap、csidownwardAPI、emptyDir、fc (fibre channel)、flexVolume、flocker、gcePersistentDisk、gitRepo (deprecated)、glusterfs、hostPath、iscsi、local、nfs、persistentVolumeClaim、projected、portworxVolume、quobyte、rbd、scaleIO、secret、storageos、vsphereVolume

emptyDir

当 Pod 被分配给节点时,首先创建 emptyDir 卷,并且只要该 Pod 在该节点上运行,该卷就会存在。正如卷的名字所述,它最初是空的。该卷可以挂载到 Pod 每个容器中的相同或不同路径上,并且每个容器都可以读取和写入 emptyDir 卷中的文件。当出于任何原因从节点中删除 Pod 时, emptyDir 中的数据将被永久删除。

***注意:***容器崩溃不会从节点中移除 pod,因此 emptyDir 卷中的数据在容器时是安全的

emptyDir 的用法有:

  • 存放临时文件,例如用于基于磁盘的归并排序;

  • 用作长时间计算崩溃恢复时的检查点,供容器崩溃后恢复使用;

  • Web 服务器容器提供数据时,保存内容管理器容器提取的文件;

示例:

[root@master test02]# vim test_emptydir.yaml 
---
kind: Pod
apiVersion: v1
metadata: 
  name: test-emptydir-pod
spec: 
  containers: 
  - name: test01
    image: myos:nginx
    volumeMounts: 
    - name: emptydir-volume      # 通过哪个 volume 挂载
      mountPath: /test01_volume  # 挂载到容器的哪个目录下
  - name: test02
    image: myos:php-fpm
    volumeMounts: 
    - name: emptydir-volume
      mountPath: /test02_volume
  volumes: 
  - name: emptydir-volume  # volume 名称
    emptyDir: {}           # volume 类型
    
[root@master test02]# kubectl apply -f test_emptydir.yaml 
pod/test-emptydir-pod created

[root@master test02]# kubectl get pods
NAME                READY   STATUS    RESTARTS   AGE
test-emptydir-pod   2/2     Running   0          4s

# 在容器1的挂载目录下,创建一个 info.txt 文件
[root@master test02]# kubectl exec -it test-emptydir-pod -c test01 -- touch /test01_volume/info.txt
# 查看容器2的 /test02_volume挂载目录,会出现info.txt,实现了共享emptydir卷
[root@master test02]# kubectl exec -it test-emptydir-pod -c test02 -- ls /test02_volume
info.txt
hostPath

在Kubernetes(k8s)中,HostPath是一种特殊的卷类型,它允许将节点(Node)上的文件或目录直接挂载到Pod中。这种挂载方式使得Pod能够访问宿主机上的文件系统,从而实现了数据的持久化存储,即使Pod被删除或重建,只要宿主机上的文件或目录仍然存在,数据就不会丢失。

HostPath的配置参数

在Kubernetes中配置HostPath卷时,通常需要指定以下参数:

  • path:指定宿主机上的目录或文件路径,这是必选字段。

  • type(可选):指定节点之上存储类型,包括以下几种:

  • DirectoryOrCreate:如果给定的路径不存在,则创建一个空目录,权限设置为755。

  • Directory:目录必须存在。

  • FileOrCreate:如果给定的文件不存在,则创建一个空文件,权限设置为644。

  • File:文件必须存在。

  • Socket:UNIX套接字,必须存在。

  • CharDevice:字符设备,必须存在。

  • BlockDevice:块设备,必须存在。

HostPath卷适用于以下场景:

  • 需要Pod直接访问宿主机上的特定文件或目录,例如访问Docker内部机制或系统文件。

  • 在某些特定场景下,如运行管理任务的系统级Pod资源,需要访问节点上的特定资源。

示例:

[root@master ~]# vim web1.yaml
---
kind: Pod
apiVersion: v1
metadata:
  name: web1
spec:
  volumes:                              # 卷定义
  - name: logdata                       # 卷名称
    hostPath:                           # 资源类型
      path: /var/weblog                 # 宿主机路径
      type: DirectoryOrCreate           # 目录不存在就创建
  containers:
  - name: nginx
    image: myos:nginx
    volumeMounts:                       # mount 卷
    - name: logdata                     # 卷名称
      mountPath: /usr/local/nginx/logs  # 容器内路径
      
[root@master ~]# kubectl apply -f web1.yaml 
pod/web1 created
[root@master ~]# kubectl get pods -o wide
NAME   READY   STATUS    RESTARTS   AGE     IP             NODE
web1   1/1     Running   0          45m     10.244.2.16    node02

[root@master ~]# curl http://10.244.2.16/
Nginx is running !

# 删除Pod ,日志数据也不会丢失
[root@master ~]# kubectl delete pod web1
pod "web1" deleted

# 来到 node 上查看日志
[root@node02 ~]# cat /var/weblog/access.log 
10.244.0.0 - - [27/Jun/2022:02:00:12 +0000] "GET / HTTP/1.1" 200 19 "-" "curl/7.29.0"
NFS

k8s 中允许将 nfs 存储以卷的方式挂载到你的 Pod 中。在删除 Pod 时,nfs 存储卷会被卸载(umount),而不是被删除。nfs 卷可以在不同节点的 Pod 之间共享数据。

NFS卷的用途

NFS最大的功能就是在不同节点的不同Pod中共享读写数据。本地 NFS 的客户端可以透明地读写位于远端 NFS 服务器上的文件,就像访问本地文件一样

示例:

# 创建共享目录,并部署测试页面,这里harbor主机充当nfs服务器
[root@harbor ~]# mkdir -p /var/webroot
[root@harbor ~]# echo "nfs server" >/var/webroot/index.html

# 部署 NFS 服务
[root@harbor ~]# dnf install -y nfs-utils
[root@harbor ~]# vim /etc/exports
/var/webroot    192.168.0.0/16(rw,no_root_squash)
[root@harbor ~]# systemctl enable --now nfs-server.service
#----------------------------------------------------------#
# 所有 node 节点都要安装 nfs 软件包
[root@node ~]# dnf install -y nfs-utils
[root@master day05]# vim web1.yaml 
---
kind: Pod
apiVersion: v1
metadata: 
  name: test
spec: 
  volumes:
  - name: logdata
    hostPath:
      path: /var/weblog
      type: DirectoryOrCreate
  - name: website
    nfs: 
      server: 192.168.10.240   # nfs服务器的地址
      path: /var/webroot       # nfs共享的目录
  containers:
  - name: web
    image: myos:nginx
    volumeMounts:
    - name: logdata
      mountPath: /usr/local/nginx/logs
    - name: website
      mountPath: /usr/local/nginx/html # 映射到容器中的路径

[root@master ~]# kubectl apply -f web1.yaml 
pod/web1 created

[root@master day05]# kubectl get pods -owide
NAME   READY   STATUS    RESTARTS   AGE   IP              NODE        NOMINATED NODE   READINESS GATES
test   1/1     Running   0          9s    10.244.147.29   node02   <none>           <none>

[root@master day05]# curl 10.244.147.29
nfs server

# 在nfs服务器上添加数据
[root@harbor ~]# echo love you k8s >> /var/webroot/index.html 

[root@master day05]# curl 10.244.147.29
nfs server
love you k8s
使用流程

使用K8s Volume的一般流程如下:

  1. 创建存储卷:根据需求选择合适的Volume类型,并创建相应的存储卷资源。

  2. 挂载存储卷:在Pod的配置文件中指定要挂载的存储卷,并将其挂载到Pod中的容器上。

  3. 访问存储卷中的数据:在Pod的容器中,通过挂载路径访问存储卷中的数据。

注意事项
  1. Volume的生命周期与Pod相关,但与容器的生命周期不相关。当Pod被删除时,与其关联的Volume(除非设置为持久化存储)也会被删除。

  2. 在使用网络存储或持久化存储时,需要确保存储系统的稳定性和可靠性,以避免数据丢失或损坏。

  3. 对于敏感数据的存储,建议使用Secret或ConfigMap等机制来保护数据安全。

[!CAUTION]

总之,K8s Volume是K8s中非常重要的一个概念,它为Pod中的容器提供了持久化存储和数据共享的能力。通过合理使用不同类型的Volume和正确的配置方法,可以确保应用程序的稳定性和可靠性。

2.10.9 PV/PVC

在Kubernetes(K8s)中,PV(Persistent Volume)和PVC(Persistent Volume Claim)是两个重要的概念,用于管理集群中的持久化存储资源。以下是对PV和PVC的详细解析:

PV(Persistent Volume)

定义与功能:

  • PV是Kubernetes中用于表示持久化存储资源的API对象。它是一块网络存储,独立于Pod存在,可以是云提供商的存储、NFS、iSCSI、本地存储等多种类型。

  • 管理员负责创建PV,并配置其细节,如容量、访问模式(ReadWriteOnce、ReadOnlyMany、ReadWriteMany)、存储类别等。

  • PV有自己的生命周期,包括可用(Available)、绑定(Bound)、释放(Released)、回收(Retained)等状态。

访问模式
  • ReadWriteOnce(RWO):单个节点读写模式,即卷可以被一个节点以读写方式挂载。

  • ReadOnlyMany(ROX):多个节点只读模式,即卷可以被多个节点以只读方式挂载。

  • ReadWriteMany(RWX):多个节点读写模式,即卷可以被多个节点以读写方式挂载。

  • ReadWriteOncePod(RWOP):卷可以被单个 Pod 以读写方式挂载。 如果你想确保整个集群中只有一个 Pod 可以读取或写入该 PVC, 请使用 ReadWriteOncePod 访问模式。这只支持 CSI 卷以及需要 Kubernetes 1.22 以上版本。

PVC(Persistent Volume Claim)

定义与功能:

  • PVC是用户对PV的存储请求。用户在PVC中定义存储的大小、访问模式等需求,而不需要指定具体的PV。

  • 当PVC被创建时,Kubernetes会尝试将其与满足其要求的PV进行绑定。如果没有合适的PV可以绑定,PVC将处于Pending状态,直到有合适的PV可用或动态创建一个新的PV为止。

  • PVC的存在使得Pod与具体的存储实现解耦,提高了可移植性。

工作流程
  1. 用户根据需求创建PVC,声明所需的存储资源规格。

  2. Kubernetes根据PVC中的需求寻找合适的PV进行绑定。

  3. 如果环境支持动态存储配额,当没有合适的PV可用时,可以根据PVC请求动态创建一个新的PV。

  4. Pod在定义中引用PVC,当Pod被调度到节点上时,PV会被挂载到Pod指定的路径上,供Pod使用。

PV与PVC的关系
  • PV和PVC之间的关系是一种动态的匹配和绑定关系。PVC声明了对存储资源的需求,而PV则是提供这些资源的实际载体。

  • 当PVC被创建时,Kubernetes会尝试将其与满足其要求的PV进行绑定。匹配的过程是根据PVC的标签选择器和PV的标签进行匹配,只有匹配成功的PV才能被绑定到PVC。

  • 一旦绑定成功,Pod可以通过PVC访问PV提供的存储资源。如果没有合适的PV可以绑定,PVC将处于Pending状态,直到有合适的PV可用为止。

PV与PVC的关联条件
  • **存储类一致:**如果 PV 指定了存储类,PVC 必须请求相同的存储类,除非 PVC 不指定存储类。
  • **访问模式兼容:**PVC 请求的访问模式必须与 PV 支持的访问模式兼容。
  • **容量足够:**PVC 请求的存储容量不能超过 PV 的容量。
  • **选择器匹配:**如果 PV 定义了选择器(标签),PVC 必须匹配这些选择器才能绑定。
  • **绑定策略:**PV 可以指定绑定策略(动态分配或静态绑定),PVC 必须满足这些策略要求。
  • **状态要求:**PVC 必须处于待处理状态(Pending),PV 必须处于可用状态(Available),才能成功绑定。

简而言之,PVC 必须符合 PV 的要求,才能成功绑定并使用 PV 提供的持久化存储。

PV回收策略
  • Retain(保留):手动回收

  • Recycle(回收):基本擦除( 相当于执行了 rm -rf /thevolume/* ),然后 PV 状态重置为Available供重新绑定。

  • Delete(删除):关联的存储资产(例如 AWS EBS、GCE PD、Azure Disk 和 OpenStack Cinder 卷)将被删除

当前,只有 NFS 和 HostPath 支持回收策略。AWS EBS、GCE PD、Azure Disk 和 Cinder 卷支持删除策

PV状态

卷可以处于以下的某种状态:

  • Available(可用):一块空闲资源还没有被任何声明绑定

  • Bound(已绑定):卷已经被声明绑定

  • Released(已释放):声明被删除,但是资源还未被集群重新声明

  • Failed(失败):该卷的自动回收失败

命令行会显示绑定到 PV 的 PVC 的名称。

示例:
在master部署nfs服务端,node01和node02为客户端

yum install -y nfs-common nfs-utils rpcbind
# 使用master上面的主机作nfs磁盘分享
mkdir /nfs
chmod 666 /nfs
chown nfsnobody /nfsdata # 没有nfsnobody 使用nobody

[root@master ~]# vim mkdirnfs.sh 
#!/bin/bash
for i in {0..9}
do
        mkdir /nfs/$i
        echo "$i" > /nfs/$i/index.html
        echo "/nfs/$i *(rw,no_root_squash,no_all_squash,sync)" >> /etc/exports
done
# 执行上面的脚本,查看/nfs
[root@master ~]# tree /nfs
/nfs
├── 0
│   └── index.html
├── 1
│   └── index.html
├── 2
│   └── index.html
├── 3
│   └── index.html
├── 4
│   └── index.html
├── 5
│   └── index.html
├── 6
│   └── index.html
├── 7
│   └── index.html
├── 8
│   └── index.html
└── 9
    └── index.html

10 directories, 10 files

root@master ~]# cat /etc/exports
/nfs/0 *(rw,no_root_squash,no_all_squash,sync)
/nfs/1 *(rw,no_root_squash,no_all_squash,sync)
/nfs/2 *(rw,no_root_squash,no_all_squash,sync)
/nfs/3 *(rw,no_root_squash,no_all_squash,sync)
/nfs/4 *(rw,no_root_squash,no_all_squash,sync)
/nfs/5 *(rw,no_root_squash,no_all_squash,sync)
/nfs/6 *(rw,no_root_squash,no_all_squash,sync)
/nfs/7 *(rw,no_root_squash,no_all_squash,sync)
/nfs/8 *(rw,no_root_squash,no_all_squash,sync)
/nfs/9 *(rw,no_root_squash,no_all_squash,sync)
[root@master ~]# showmount -e localhost
Export list for localhost:
/nfs/9 *
/nfs/8 *
/nfs/7 *
/nfs/6 *
/nfs/5 *
/nfs/4 *
/nfs/3 *
/nfs/2 *
/nfs/1 *
/nfs/0 *
[root@master ~]# cat /nfs/9/index.html 
9

# 验证nfs共享
[root@node01 ~]# mkdir /testnfs
[root@node01 ~]# mount master:/nfs/9 /testnfs
[root@node01 ~]# tree /testnfs/
/testnfs/
└── index.html

0 directories, 1 file
[root@node01 ~]# cat /testnfs/index.html 
9
[root@node01 ~]# umount /testnfs 
[root@node01 ~]# tree /testnfs/
/testnfs/

0 directories, 0 files
部署pv示例
[root@master test02]# vim pv.yaml 
---
apiVersion: v1
kind: PersistentVolume
metadata:
 name: nfspv0     # pv 名字
spec:
 capacity:        # 容量
  storage: 0.5Gi  # 存储空间
 accessModes:     # 存储模式
  - ReadWriteOnce # 单个节点读写模式,即卷可以被一个节点以读写方式挂载
 persistentVolumeReclaimPolicy: Recycle # 持久卷回收策略
 storageClassName: nfs   # 存储类的名字
 nfs:
  path: /nfs/0           # nfs共享路径
  server: 192.168.10.10 # nfs服务器地址
--- 
apiVersion: v1
kind: PersistentVolume
metadata:
 name: nfspv1     # pv 名字
spec:
 capacity:        # 容量
  storage: 1Gi    # 存储空间
 accessModes:     # 存储模式
  - ReadWriteMany # 多个节点读写模式,即卷可以被多个节点以读写方式挂载
 persistentVolumeReclaimPolicy: Recycle  # 持久卷回收策略
 storageClassName: nfs # 存储类的名字
 nfs:
  path: /nfs/1         # nfs共享路径
  server: 192.168.10.10
--- 
apiVersion: v1
kind: PersistentVolume
metadata:
 name: nfspv2     # pv 名字
spec:
 capacity:        # 容量
  storage: 1Gi    # 存储空间
 accessModes:     # 存储模式
  - ReadWriteOnce # 单个节点读写模式,即卷可以被一个节点以读写方式挂载
 persistentVolumeReclaimPolicy: Recycle # 持久卷回收策略
 storageClassName: nfs1 # 存储类的名字
 nfs:
  path: /nfs/2          # nfs共享路径
  server: 192.168.10.10
--- 
apiVersion: v1
kind: PersistentVolume
metadata:
 name: nfspv3  # pv 名字
spec:
 capacity:     # 容量
  storage: 1Gi # 存储空间
 accessModes:  # 存储模式
  - ReadWriteOnce # 单个节点读写模式,即卷可以被一个节点以读写方式挂载
 persistentVolumeReclaimPolicy: Retain # 持久卷回收策略
 storageClassName: nfs  # 存储类的名字
 nfs:
  path: /nfs/3          # nfs共享路径
  server: 192.168.10.10
--- 
apiVersion: v1
kind: PersistentVolume
metadata:
 name: nfspv4  # pv 名字
spec:
 capacity:     # 容量
  storage: 1Gi # 存储空间
 accessModes:  # 存储模式
  - ReadWriteOnce # 单个节点读写模式,即卷可以被一个节点以读写方式挂载
 persistentVolumeReclaimPolicy: Recycle # 持久卷回收策略
 storageClassName: nfs # 存储类的名字
 nfs:
  path: /nfs/4         # nfs共享路径
  server: 192.168.10.10
--- 
apiVersion: v1
kind: PersistentVolume
metadata:
 name: nfspv5  # pv 名字
spec:
 capacity:     # 容量
  storage: 1Gi # 存储空间
 accessModes:  # 存储模式
  - ReadWriteOnce # 单个节点读写模式,即卷可以被一个节点以读写方式挂载
 persistentVolumeReclaimPolicy: Recycle # 持久卷回收策略
 storageClassName: nfs # 存储类的名字
 nfs:
  path: /nfs/5         # nfs共享路径
  server: 192.168.10.10
--- 
apiVersion: v1
kind: PersistentVolume
metadata:
 name: nfspv6    # pv 名字
spec:
 capacity:       # 容量
  storage: 1.5Gi # 存储空间
 accessModes:    # 存储模式
  - ReadWriteOnce # 单个节点读写模式,即卷可以被一个节点以读写方式挂载
 persistentVolumeReclaimPolicy: Recycle #持久卷回收策略
 storageClassName: nfs # 存储类的名字
 nfs:
  path: /nfs/6 # nfs共享路径
  server: 192.168.10.10
--- 
apiVersion: v1
kind: PersistentVolume
metadata:
 name: nfspv7 # pv 名字
spec:
 capacity: #容量
  storage: 1Gi #存储空间
 accessModes: #存储模式
  - ReadWriteOnce #单个节点读写模式,即卷可以被一个节点以读写方式挂载
 persistentVolumeReclaimPolicy: Recycle #持久卷回收策略
 storageClassName: nfs # 存储类的名字
 nfs:
  path: /nfs/7 # nfs共享路径
  server: 192.168.10.10
--- 
apiVersion: v1
kind: PersistentVolume
metadata:
 name: nfspv8 # pv 名字
spec:
 capacity: #容量
  storage: 1Gi #存储空间
 accessModes: #存储模式
  - ReadWriteOnce #单个节点读写模式,即卷可以被一个节点以读写方式挂载
 persistentVolumeReclaimPolicy: Recycle #持久卷回收策略
 storageClassName: nfs # 存储类的名字
 nfs:
  path: /nfs/8 # nfs共享路径
  server: 192.168.10.10
--- 
apiVersion: v1
kind: PersistentVolume
metadata:
 name: nfspv9      # pv 名字
spec:
 capacity:         # 容量
  storage: 1Gi     # 存储空间
 accessModes:      # 存储模式
  - ReadWriteOnce  # 单个节点读写模式,即卷可以被一个节点以读写方式挂载
 persistentVolumeReclaimPolicy: Recycle # 持久卷回收策略
 storageClassName: nfs # 存储类的名字
 nfs:
  path: /nfs/9         # nfs共享路径
  server: 192.168.10.10
  
[root@master test02]# kubectl apply -f pv.yaml 
persistentvolume/nfspv0 created
persistentvolume/nfspv1 created
persistentvolume/nfspv2 created
persistentvolume/nfspv3 created
persistentvolume/nfspv4 created
persistentvolume/nfspv5 created
persistentvolume/nfspv6 created
persistentvolume/nfspv7 created
persistentvolume/nfspv8 created
persistentvolume/nfspv9 created
[root@master test02]# kubectl get pv
NAME     CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM   STORAGECLASS   VOLUMEATTRIBUTESCLASS   REASON   AGE
nfspv0   512Mi      RWO            Recycle          Available           nfs            <unset>                          13s
nfspv1   1Gi        RWX            Recycle          Available           nfs            <unset>                          13s
nfspv2   1Gi        RWO            Recycle          Available           nfs1           <unset>                          13s
nfspv3   1Gi        RWO            Retain           Available           nfs            <unset>                          13s
nfspv4   1Gi        RWO            Recycle          Available           nfs            <unset>                          13s
nfspv5   1Gi        RWO            Recycle          Available           nfs            <unset>                          13s
nfspv6   1536Mi     RWO            Recycle          Available           nfs            <unset>                          13s
nfspv7   1Gi        RWO            Recycle          Available           nfs            <unset>                          13s
nfspv8   1Gi        RWO            Recycle          Available           nfs            <unset>                          13s
nfspv9   1Gi        RWO            Recycle          Available           nfs            <unset>                          13s
创建服务与pvc示例

本案例是基于StatefuSet控制器的方式创建的pvc:

[root@master test02]# vim pvc.yaml 
---
apiVersion: v1
kind: Service  # service类
metadata:
 name: nginx
 labels:
  app: nginx
spec:
 ports:
 - port: 80
   name: web
# clusterIP模式没有vip的话,ipvs工作方式就没有负载均衡
# 无头服务专门给StatefulSet使用
 clusterIP: None  # 没有vip
 selector:
  app: nginx
---
apiVersion: apps/v1
kind: StatefulSet # 有状态服务,数据可能发生改变的服务,如mysql
metadata:
 name: web
spec:
 selector:
  matchLabels:
   app: nginx
 serviceName: "nginx" # 匹配无头服务service nginx
 replicas: 5
 template:
  metadata:
   labels:
    app: nginx
  spec:
   containers:
   - name: nginx
     image: myos:nginx
     ports: # 定义端口
     - containerPort: 80 # 端口80 
       name: web         # 端口名称web
     volumeMounts:       # 卷绑定
     - name: www         # 卷名
       mountPath: /usr/local/nginx/html # 卷挂载路径
 volumeClaimTemplates:   # pvc模版
 - metadata:
    name: www
   spec:
    accessModes: [ "ReadWriteOnce" ]    # 单节点读取
    storageClassName: "nfs" # 存储类
    resources:
     requests:
      storage: 1Gi # 存储期望
      
[root@master test02]# kubectl apply -f pvc.yaml 
service/nginx unchanged
statefulset.apps/web configured

# 查看 pvc,每个Pod有一个pvc,是一个一个创建的
[root@master test02]# kubectl get pvc
NAME        STATUS   VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS   VOLUMEATTRIBUTESCLASS   AGE
www-web-0   Bound    nfspv5   1Gi        RWO            nfs            <unset>                 8m20s
www-web-1   Bound    nfspv7   1Gi        RWO            nfs            <unset>                 6m24s
www-web-2   Bound    nfspv8   1Gi        RWO            nfs            <unset>                 6m20s
www-web-3   Bound    nfspv4   1Gi        RWO            nfs            <unset>                 6m16s
www-web-4   Bound    nfspv3   1Gi        RWO            nfs            <unset>                 6m10s

# 查看pv
[root@master test02]# kubectl get pv
NAME     CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM               STORAGECLASS   VOLUMEATTRIBUTESCLASS   REASON   AGE
nfspv0   512Mi      RWO            Recycle          Available                       nfs            <unset>                          53m
nfspv1   1Gi        RWX            Recycle          Available                       nfs            <unset>                          53m
nfspv2   1Gi        RWO            Recycle          Available                       nfs1           <unset>                          53m
nfspv3   1Gi        RWO            Retain           Bound       default/www-web-4   nfs            <unset>                          53m
nfspv4   1Gi        RWO            Recycle          Bound       default/www-web-3   nfs            <unset>                          53m
nfspv5   1Gi        RWO            Recycle          Bound       default/www-web-0   nfs            <unset>                          53m
nfspv6   1536Mi     RWO            Recycle          Available                       nfs            <unset>                          53m
nfspv7   1Gi        RWO            Recycle          Bound       default/www-web-1   nfs            <unset>                          53m
nfspv8   1Gi        RWO            Recycle          Bound       default/www-web-2   nfs            <unset>                          53m
nfspv9   1Gi        RWO            Recycle          Available                       nfs            <unset>                          53m

# 获取podIP
[root@master test02]# kubectl get pods -o wide
NAME                READY   STATUS    RESTARTS   AGE     IP              NODE         NOMINATED NODE   READINESS GATES
web-0               1/1     Running   0          10m     10.244.58.193   node02   <none>           <none>
web-1               1/1     Running   0          8m15s   10.244.85.193   node01   <none>           <none>
web-2               1/1     Running   0          8m11s   10.244.85.194   node01   <none>           <none>
web-3               1/1     Running   0          8m7s    10.244.58.195   node02   <none>           <none>
web-4               1/1     Running   0          8m1s    10.244.85.195   node01   <none>           <none>
[root@master test02]# curl 10.244.58.193
5
# 在 NFS 服务器的 /nfs/5 目录中添加数据,然后通过 nginx 来访问
[root@master test02]# echo hehe >>  /nfs/5/index.html 
[root@master test02]# curl 10.244.58.193
5
hehe

注意:StatefulSet是有序部署的,当有Pod的PVC没有绑定到一个PV,就会处于Pending状态,后序的Pod也没法创建了

尝试删除 Pod,pv 中的数据不会丢失

[root@master ~]# kubectl delete pods web-0
pod "web-0" deleted
# 删除pod后,重新创建的pod读取nfs数据,更改后的数据依然存在,pod级别数据持久化
[root@master ~]# kubectl get pods -owide
NAME    READY   STATUS    RESTARTS   AGE     IP              NODE         NOMINATED NODE   READINESS GATES
web-0   1/1     Running   0          15s     10.244.58.196   node02   <none>           <none>
web-1   1/1     Running   0          5h24m   10.244.85.193   node01   <none>           <none>
web-2   1/1     Running   0          5h23m   10.244.85.194   node01   <none>           <none>
web-3   1/1     Running   0          5h23m   10.244.58.195   node02   <none>           <none>
web-4   1/1     Running   0          5h23m   10.244.85.195   node01   <none>           <none>
[root@master ~]# curl 10.244.58.196 
5
hehe

# pod内部互相访问使用域名
# 域名格式
#podName.headlessSvcName.nsName.svc.cluster.local.
#statefulSetName-num.headlessSvcName.nsName.svc.cluster.local.
[root@master test]# kubectl exec -it web-0 -- bash
[root@web-0 html]# curl http://web-1.nginx.default.svc.cluster.local.
7
[root@web-0 html]# curl http://web-2.nginx.default.svc.cluster.local.
8

删除 StatefulSet 后,pvc 不会自动删除,pv也不会自动释放,需要手动删除

# 删除 StatefulSet 后,pvc 仍然存在
[root@master test]# kubectl delete statefulset web
statefulset.apps "web" deleted
[root@master test]# kubectl get pvc
NAME        STATUS   VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS   VOLUMEATTRIBUTESCLASS   AGE
www-web-0   Bound    nfspv5   1Gi        RWO            nfs            <unset>                 5h53m
www-web-1   Bound    nfspv7   1Gi        RWO            nfs            <unset>                 5h51m
www-web-2   Bound    nfspv8   1Gi        RWO            nfs            <unset>                 5h51m
www-web-3   Bound    nfspv4   1Gi        RWO            nfs            <unset>                 5h51m
www-web-4   Bound    nfspv3   1Gi        RWO            nfs            <unset>                 5h50m
# 删除 pvc 后,pv 没有自动释放
[root@master test]# kubectl delete pvc --all
persistentvolumeclaim "www-web-0" deleted
persistentvolumeclaim "www-web-1" deleted
persistentvolumeclaim "www-web-2" deleted
persistentvolumeclaim "www-web-3" deleted
persistentvolumeclaim "www-web-4" deleted
[root@master test]# kubectl get pv
NAME     CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM               STORAGECLASS   VOLUMEATTRIBUTESCLASS   REASON   AGE
nfspv0   512Mi      RWO            Recycle          Available                       nfs            <unset>                          6h38m
nfspv1   1Gi        RWX            Recycle          Available                       nfs            <unset>                          6h38m
nfspv2   1Gi        RWO            Recycle          Available                       nfs1           <unset>                          6h38m
nfspv3   1Gi        RWO            Retain           Released    default/www-web-4   nfs            <unset>                          6h38m
nfspv4   1Gi        RWO            Recycle          Released    default/www-web-3   nfs            <unset>                          6h38m
nfspv5   1Gi        RWO            Recycle          Released    default/www-web-0   nfs            <unset>                          6h38m
nfspv6   1536Mi     RWO            Recycle          Available                       nfs            <unset>                          6h38m
nfspv7   1Gi        RWO            Recycle          Failed      default/www-web-1   nfs            <unset>                          6h38m
nfspv8   1Gi        RWO            Recycle          Released    default/www-web-2   nfs            <unset>                          6h38m
nfspv9   1Gi        RWO            Recycle          Available                       nfs            <unset>            

# 手动释放pv
[root@master test]# kubectl edit pv nfspv3
# 将下面的 spec.claimRef 删除
	spec:
	  claimRef:
	    apiVersion: v1
	    kind: PersistentVolumeClaim
	    name: www-web-0
	    namespace: default
	    resourceVersion: "619064"
	    uid: 99cea07e-339e-431c-bcb6-c398c884b29c
# 再次查看 pv nfspv3已经得到释放
[root@master test]# kubectl get pv
NAME     CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM               STORAGECLASS   VOLUMEATTRIBUTESCLASS   REASON   AGE
nfspv0   512Mi      RWO            Recycle          Available                       nfs            <unset>                          6h41m
nfspv1   1Gi        RWX            Recycle          Available                       nfs            <unset>                          6h41m
nfspv2   1Gi        RWO            Recycle          Available                       nfs1           <unset>                          6h41m
nfspv3   1Gi        RWO            Retain           Available                       nfs            <unset>                          6h41m
nfspv4   1Gi        RWO            Recycle          Released    default/www-web-3   nfs            <unset>                          6h41m
nfspv5   1Gi        RWO            Recycle          Released    default/www-web-0   nfs            <unset>                          6h41m
nfspv6   1536Mi     RWO            Recycle          Available                       nfs            <unset>                          6h41m
nfspv7   1Gi        RWO            Recycle          Failed      default/www-web-1   nfs            <unset>                          6h41m
nfspv8   1Gi        RWO            Recycle          Released    default/www-web-2   nfs            <unset>                          6h41m
nfspv9   1Gi        RWO            Recycle          Available                       nfs            <unset>                   

2.11 Pod调度策略管理

2.11.1 污点概述

什么是污点

污点(Taint)是使节点与Pod产生排斥的一类规则

污点策略是如何实现

污点策略通过嵌合在键值对上的污点标签进行声明

污点标签

  • 尽量不调度:PreferNoSchedule 尽量避免将 Pod 调度到该节点,但不是强制的。

  • 不会被调度:NoSchedule 新创建的 Pod 不会被调度到该节点,已在节点上运行的 Pod 不受影响。

  • 驱逐节点:NoExecute 不仅新创建的 Pod 不会被调度到该节点,已在节点上运行的且无法容忍该污点的 Pod 会被驱逐

2.11.2 管理污点标签

污点标签必须绑定在键值对上,格式为:key=value:[污点标签]

  • 查看污点标签:kubectl describe nodes [节点名字]

  • 设置污点标签:kubectl taint node [节点名字] key=value:污点标签

  • 删除污点标签:kubectl taint node [节点名字] key=value:污点标签-

管理污点标签示例:

# NoExecute 会删除节点上的 Pod
[root@master ~]# kubectl taint node node01 k=v:NoExecute
node/node01 tainted

# node02 设置污点策略 PreferNoSchedule
[root@master ~]# kubectl taint node node02 k=v:PreferNoSchedule
node/node02 tainted

# node03 设置污点策略 NoSchedule
[root@master ~]# kubectl taint node node03 k=v:NoSchedule
node/node03 tainted

[root@master ~]# kubectl describe nodes |grep Taints
Taints:             node-role.kubernetes.io/control-plane:NoSchedule
Taints:             k=v:NoExecute
Taints:             k=v:PreferNoSchedule
Taints:             k=v:NoSchedule

# 删除污点
[root@master ~]# kubectl taint node node-000{1..3} k-
[root@master ~]# kubectl describe nodes |grep Taints
Taints:             node-role.kubernetes.io/control-plane:NoSchedule
Taints:             <none>
Taints:             <none>
Taints:             <none>

2.11.3 容忍策略

容忍策略是什么?

容忍刚好与污点相反,某些时候我们需要在有污点的节点上运行Pod,这种无视污点标签的调度方式称为容忍

示例:

#配置容忍策略示例
spec:
tolerations:             # 定义容忍策略
- operator: "Equal"      # 匹配方式,必选(Equal,Exists)
  key: "kl"              # 设置键值对的key,为空时代表任意键值对
  value: "vl"            # 设置values 的值
  effect:"NoSchedule"   # 设置容忍的标签,为空时代表所有污点标签
containers:
......

示例二:

# 容忍 k=v1:NoSchedule 污点
[root@master ~]# vim myphp.yaml
......
spec:
  tolerations:
  - operator: Equal      # 完全匹配键值对
    key: k               # 键
    value: v1            # 值
    effect: NoSchedule   # 污点标签
  containers:
......

# 容忍 k=*:NoSchedule 污点
[root@master ~]# vim myphp.yaml
......
spec:
  tolerations:
  - operator: Exists     # 部分匹配,存在即可
    key: k               # 键
    effect: NoSchedule   # 污点标签
  containers:
......

# 容忍键为k的所有node上的污点
[root@master ~]# vim myphp.yaml 
......
spec:
  tolerations:
  - operator: Exists     # 模糊匹配
    key: k               # 键
    effect: ""           # 设置空或删除,代表所有污点标签
  containers:
......

2.11.4 抢占与优先级

优先级是什么?

优先级表示一个Pod相对于其他Pod的重要性。

优先级有什么用?

优先级可以保证重要的Pod被调度运行

如何使用优先级和抢占

  • 配置优先级类PriorityClass
  • 创建Pod时为其设置对应的优先级
PriorityClass简介
  • PriorityClass是一个全局资源对象,它定义了从优先级类名称到优先级整数值的映射。在优先级类中有两个重要选项,分别是 value 和 preemptionPolicy。
  • value是一个整数值,值越大,优先级越高,取值范围是0到1000000000之间。
  • preemptionPolicy 表示在资源不足时候的行为,在队列中等待或者直接抢夺低优先级应用的资源

优先级策略:

  • 抢占策略:PreemptLowerPriority
  • 非抢占策略:Never

描述信息与默认优先级

在PriorityClass中还有两个可选字段,是description和 globalDefault,description用来配置描述性信息,告诉用户优先级的用途,globaIDefault用于设置默认优先级状态,如果没有任何优先级设置Pod 的优先级为0

示例:

创建优先级类和pod设置优先级

[root@master ~]# vim mypriority.yaml
---
kind: PriorityClass
apiVersion: scheduling.k8s.io/vl
metadata:
  name: high-non             # 优先级名称
preemptionPolicy: Never      # 策略:非抢占
value: 1000                  # 优先级

---
kind: PriorityClass
apiVersion: scheduling.k8s.io/v1
metadata:
  name: low-non
preemptionPolicy: Never
value: 500

---
kind: PriorityClass
apiVersion: scheduling.k8s.io/v1
metadata:
  name: high
preemptionPolicy: PreemptLowerPriority
value: 1000

---
kind: PriorityClass
apiVersion: scheduling.k8s.io/vl
metadata:
  name: low                             # 优先级名称
preemptionPolicy: PreemptLowerPriority  # 策略:抢占
value: 500                              # 优先级

[root@master ~]# kubectl apply -f mypriority.yaml 
[root@master ~]# kubectl get priorityclasses.scheduling.k8s.io  
NAME                      VALUE        GLOBAL-DEFAULT   AGE
high                      1000         false            8s
high-non                  1000         false            24m
low                       500          false            8s
low-non                   500          false            24m
system-cluster-critical   2000000000   false            21d
system-node-critical      2000001000   false            21d

# 无优先级的pod
[root@master ~]# vim php1.yaml 
......
spec:
  nodeSelector:
    kubernetes.io/hostname: node03
  containers:
  - name: php
    image: myos:php-fpm
    resources:
      requests:
        cpu: "1200m"
  
# 低优先级 Pod
[root@master ~]# vim php2.yaml 
......
spec:
  nodeSelector:
    kubernetes.io/hostname: node03
  priorityClassName: low-non      # 优先级名称
  containers:
  - name: php
    image: myos:php-fpm
    resources:
      requests:
        cpu: "1200m"
        
# 高优先级 Pod
[root@master ~]# vim php3.yaml 
......
spec:
  nodeSelector:
    kubernetes.io/hostname: node03
  priorityClassName: high-non     # 优先级名称
  containers:
  - name: php
    image: myos:php-fpm
    resources:
      requests:
        cpu: "1200m"

验证非抢占优先:

[root@master ~]# kubectl apply -f php1.yaml 
pod/php1 created
[root@master ~]# kubectl apply -f php2.yaml 
pod/php2 created
[root@master ~]# kubectl apply -f php3.yaml 
pod/php3 created
[root@master ~]# kubectl get pods
NAME   READY   STATUS    RESTARTS   AGE
php1   1/1     Running   0          9s
php2   0/1     Pending   0          6s
php3   0/1     Pending   0          4s
[root@master ~]# kubectl delete pod php1
pod "php1" deleted
[root@master ~]# kubectl get pods
NAME   READY   STATUS    RESTARTS   AGE
php2   0/1     Pending   0          20s
php3   1/1     Running   0          18s

# 清理实验 Pod
[root@master ~]# kubectl delete pod php2 php3
pod "php2" deleted
pod "php3" deleted

验证抢占优先:

# 替换优先级策略,?是匹配任意字符
[root@master ~]# sed 's,-non,,' -i php?.yaml

# 默认优先级 Pod
[root@master ~]# kubectl apply -f php1.yaml 
pod/php1 created
[root@master ~]# kubectl get pods
NAME   READY   STATUS    RESTARTS   AGE
php1   1/1     Running   0          6s

# 高优先级 Pod
[root@master ~]# kubectl apply -f php3.yaml
pod/php3 created

# php1被抢占
[root@master ~]# kubectl get pods
NAME   READY   STATUS    RESTARTS   AGE
php3   1/1     Running   0          9s

# 低优先级 Pod
[root@master ~]# kubectl apply -f php2.yaml
pod/php2 created

# 由于php2的优先级低于php3,所以是Pending状态
[root@master ~]# kubectl get pods
NAME   READY   STATUS    RESTARTS   AGE
php2   0/1     Pending   0          3s
php3   1/1     Running   0          9s

# 清理实验 Pod
[root@master ~]# kubectl delete pod --all

2.12 用户认证&RBAC授权

2.12.1 用户认证

  • 所有 Kubernetes 集群都有两类用户:由 Kubernetes 管理的服务账户和普通用户。
  • 服务账户是给运行在集群中的 Pod 或其他工作负载使用的身份,用于与 Kubernetes API Server 进行交互。例如,Pod 中的应用程序可能需要通过服务账户读取 ConfigMap、访问其他资源或与 API Server 通信。
  • 普通账户是给集群外部的用户或系统使用的身份,例如管理员、开发者或外部服务(如通过 kubectl 操作集群的用户)。
创建服务账户示例:
[root@master ~]# vim admin-user.yaml
---
kind: ServiceAccount
apiVersion: v1
metadata:
  name: kube-admin
  namespace: kubernetes-dashboard

[root@master ~]# kubectl -n kubernetes-dashboard get sa
NAME                   SECRETS   AGE
default                0         26m
kubernetes-dashboard   0         26m

[root@master ~]# kubectl apply -f admin-user.yaml 
serviceaccount/kube-admin created
[root@master ~]# kubectl -n kubernetes-dashboard get serviceaccounts 
NAME                   SECRETS   AGE
default                0         16m
kube-admin             0         11s
kubernetes-dashboard   0         16m
创建普通账户的步骤:

普通账户的创建本质是通过外部认证机制向 Kubernetes 注册用户身份,核心步骤为:

  • 生成用户证书(或 Token)。
  • 配置 kubeconfig。
  • 通过 RBAC 分配权限
角色与授权

如果想访问和管理kubernetes集群,就要对身份以及权限做验证,kubernetes 支持的鉴权模块有 Node、RBAC、ABAC、Webhook API

  • Node:一种特殊用途的鉴权模式,专门对kubelet发出的请求进行鉴权
  • RBAC:是一种基于组织中用户的角色来控制资源使用的方法
  • ABAC:基于属性的访问控制,是一种通过将用户属性与权限组合在一起像用户授权的方法
  • Webhook:是一个HTTP回调。

2.12.2 RBAC授权

K8s RBAC(Role-Based Access Control,基于角色的访问控制)是K8s中用于控制用户对集群资源访问权限的一种机制。RBAC允许管理员通过定义角色(Role)和角色绑定(RoleBinding)来管理用户对集群资源的访问权限

角色(Role)与集群角色(ClusterRole)
在 Kubernetes 中,RBAC(Role-Based Access Control)的核心概念包括角色(Role)和集群角色(ClusterRole),它们用于定义对资源的访问权限。

角色(Role)
  • Role 是一种 Kubernetes 对象,它定义了对特定命名空间内资源的一组权限。这些权限可以是创建、读取、更新和删除等操作,针对特定的 API 资源对象(如 Pod、Service、ConfigMap 等)。
  • Role 对象只在特定命名空间内生效,即它所定义的权限只适用于该命名空间内的资源。
集群角色(ClusterRole)
  • ClusterRole 也是一种 Kubernetes 对象,它与 Role 类似,但是作用范围更广泛,可以应用于整个集群,而不限于特定的命名空间。
  • ClusterRole 定义了对集群范围内资源的权限,可以包括所有命名空间中的资源,也可以包括集群级别的资源,例如节点、命名空间等。

[!IMPORTANT]

总的来说,角色(Role)和集群角色(ClusterRole)的作用是相似的,都用于定义对 Kubernetes 资源的访问权限。它们的区别主要在于作用范围:Role 仅作用于特定命名空间内的资源,而 ClusterRole 则作用于整个集群的资源

角色绑定(RoleBinding)与集群角色绑定(ClusterRoleBinding)
在 Kubernetes 中,角色绑定(RoleBinding)和集群角色绑定(ClusterRoleBinding)用于将用户、组或服务账户与角色或集群角色关联起来,从而赋予它们相应的权限。它们的作用是将权限分配给特定的实体,使其能够执行定义在角色或集群角色中的操作。

角色绑定(RoleBinding)

RoleBinding 是一种 Kubernetes 对象,用于将特定的角色(Role)与特定的用户、组或服务账户绑定在一起,从而赋予它们在某个命名空间内的资源上执行操作的权限。

集群角色绑定(ClusterRoleBinding)

ClusterRoleBinding 类似于 RoleBinding,但是作用范围更广泛,它用于将集群角色(ClusterRole)与用户、组或服务账户绑定在一起,赋予它们在整个集群范围内执行操作的权限。

资源对象角色与作用域
资源对象 描述 作用域
ServiceAccount 服务账号,为 Pod 中运行的进程提供了一个身份 单一名称空间
Role 角色,包含一组代表相关权限的规则 单一名称空间
ClusterRole 角色,包含一组代表相关权限的规则 全集群
RoleBinding 将权限赋予用户,Role、ClusterRole 均可使用 单一名称空间
ClusterRoleBinding 将权限赋予用户,只可以使用 ClusterRole 全集群
资源对象权限
create delete deletecollection get list patch update watch
创建 删除 删除集合 获取属性 获取列表 补丁 更新 监控
主体(Subject)

在 Kubernetes 中,主体(Subject)是指具有身份的实体,通常是用户、组或服务账户。主体通过角色绑定(RoleBinding)或集群角色绑定(ClusterRoleBinding)与角色或集群角色关联在一起,从而获取对 Kubernetes 资源的访问权限。

主体可以是以下几种类型之一:

  • **用户:**Kubernetes 可以与外部身份验证服务集成,以允许使用基于用户名和密码的身份验证机制登录到集群中。登录成功后,用户将被视为主体,并根据其被分配的角色获得相应的权限。
  • **组:**在某些情况下,将一组用户组织在一起,并为整个组分配权限可能更为方便。在 Kubernetes 中,可以通过角色绑定或集群角色绑定将组与角色或集群角色关联起来,从而将权限分配给组内的所有成员。
  • **服务账户:**服务账户是 Kubernetes 中的一种特殊类型的身份,用于表示正在运行的容器或 Pod。它们通常用于实现应用程序和其他 Kubernetes 部署的自动化任务。通过为服务账户分配适当的角色或集群角色,可以确保它们具有执行所需操作所需的权限。

总之,主体在 Kubernetes 中代表了具有身份的实体,通过角色绑定或集群角色绑定与角色或集群角色相关联,从而获得对 Kubernetes 资源的访问权限。

自定义角色和角色绑定示例:

[root@master ~]# vim myrole.yaml 
---
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata: 
  name: myrole
  namespace: default
rules:         # 定义规则
- apiGroups:   # 资源对象所属组信息
  - ""         # 分组信息
  resources:   # 要设置权限的资源对象
  - pods       # 授权资源对象名称
  verbs:       # 权限设置
  - get        # 权限
  - list       # 权限

# 给上面创建的服务账户授普通用户权限
---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata: 
  name: kube-admin-role
  namespace: default
roleRef:   # 关联权限
  apiGroup: rbac.authorization.k8s.io  # 角色对象组
  kind: Role   # 角色对象
  name: myrole # 角色名称
subjects:      # 授权信息
- kind: ServiceAccount  # 账号资源对象
  name: kube-admin      # 账号名称
  namespace: kubernetes-dashboard      # 账号所在的名称空间
  
[root@master ~]# kubectl apply -f myrole.yaml 
role.rbac.authorization.k8s.io/myrole created
rolebinding.rbac.authorization.k8s.io/kube-admin-role created
[root@master ~]# kubectl describe role myrole 
Name:         myrole
Labels:       <none>
Annotations:  <none>
PolicyRule:
  Resources  Non-Resource URLs  Resource Names  Verbs
  ---------  -----------------  --------------  -----
  pods       []         servi        []              [get list]
[root@master ~]# kubectl describe rolebindings.rbac.authorization.k8s.io 
Name:         kube-admin-role
Labels:       <none>
Annotations:  <none>
Role:
  Kind:  Role
  Name:  myrole
Subjects:
  Kind            Name        Namespace
  ----            ----        ---------
  ServiceAccount  kube-admin  kubernetes-dashboard
# 查看集群角色,cluster-admin集群角色相当于Linux中的root管理员
[root@master ~]# kubectl get clusterrole | grep cluster-admin
cluster-admin                           2025-01-14T11:22:09Z
[root@master ~]# vim admin-user.yaml 
---
kind: ServiceAccount
apiVersion: v1
metadata: 
  name: kube-admin
  namespace: kubernetes-dashboard

# 给上面创建的服务账户授管理员权限
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata: 
  name: kube-admin-role
roleRef:                   # 关联权限
  apiGroup: rbac.authorization.k8s.io  # 角色对象组
  kind: ClusterRole        # 角色对象
  name: cluster-admin      # 角色名称
subjects:                  # 授权信息
- kind: ServiceAccount     # 账号资源对象
  name: kube-admin         # 账号名称
  namespace: kubernetes-dashboard      # 账号所在的名称空间
  
[root@master ~]# kubectl apply -f admin-user.yaml 
serviceaccount/kube-admin unchanged
clusterrolebinding.rbac.authorization.k8s.io/kube-admin-role created

[root@master ~]# kubectl describe clusterrole | grep -A 9 cluster-admin
Name:         cluster-admin
Labels:       kubernetes.io/bootstrapping=rbac-defaults
Annotations:  rbac.authorization.kubernetes.io/autoupdate: true
PolicyRule:
  Resources  Non-Resource URLs  Resource Names  Verbs
  ---------  -----------------  --------------  -----
  *.*        []                 []              [*]
             [*]                []              [*]
             
[root@master ~]# kubectl describe clusterrolebindings.rbac.authorization.k8s.io | grep -A 8 kube-admin
Name:         kube-admin-role
Labels:       <none>
Annotations:  <none>
Role:
  Kind:  ClusterRole
  Name:  cluster-admin
Subjects:
  Kind            Name        Namespace
  ----            ----        ---------
  ServiceAccount  kube-admin  kubernetes-dashboard

你可能感兴趣的:(运维,linux,docker,容器,kubernetes,云原生,kubelet)