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 仓库
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是什么?
1.4.3 如何使用Dockerfile制作镜像?
编写 Dockerfile
制作镜像docker build -t 镜像名称:标签 Dockerfile所在目录
1.4.4 Dockerfile中常用指令
指令
说明
FROM
指定基础镜像(唯一)
RUN
在构建镜像时执行命令,可以写多条
ADD
把文件拷贝到容器内,如果文件是tar.xx格式,会自动解压
COPY
把文件拷贝到容器内,不会自动解压
ENV
设置启动容器的环境变量
WORKDIR
设置启动容器的默认工作目录(唯一)
CMD
容器默认的启动参数(唯一)
ENTRYPOINT
容器默认的启动命令(唯一)
USER
启动容器使用的用户(唯一)
EXPOSE
使用镜像创建的容器默认监听使用的端口号/协议,不会实际发布端口,只是文档说明
[!CAUTION]
ENTRYPOINT一定会执行,CMD可以通过传递参数覆盖
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 [root@docker ~]# vim myimg/Dockerfile FROM mylinux:latest ENTRYPOINT ["echo" ] CMD ["/bin/ls" , "-l" ] [root@docker ~]# docker build -t img:v2 myimg/ [root@docker ~]# docker run -it --rm img:v2 /bin/ls -l [root@docker ~]# docker run -it --rm img:v2 id id
制作apache镜像 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 [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: [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镜像 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 [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镜像 1 2 3 4 5 6 7 8 9 10 11 12 [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镜像 1 2 3 4 5 6 7 8 9 10 11 12 [root@tomcat ~]# mkdir myimg [root@tomcat ~]# cd myimg [root@tomcat myimg]# [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私有仓库 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 [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 [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
验证测试 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 [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" ]} [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 [root@docker ~]# docker run -itd --rm myhttpd:latest 634766f788d665dbea0a4b39709e0a2cc8624fd99478e9f302c0a5661d76
1.6 部署容器服务 1.6.1 端口映射 容器化带来的问题
新创建容器的IP 地址是随机的
容器在重启后每次IP者都会发生变化
容器服务只有宿主机才能访问
如何才能使用容器对外提供稳定的服务?
端口映射语法: docker run -itd -p [可选IP]:宿主机端口:容器端口 镜像:标签(可有多个)
示例:
1 [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宿主机对象:容器内对象 镜像名:标签
示例:
1 2 3 [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的网络通信模式
使用网络命名空间共享网络 参数是 --network=container:容器名|ID
示例:
1 2 [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
1 2 3 4 5 6 7 8 [root@docker ~ ] 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]
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 [root@docker-0001 ~ ] 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
嵌入脚本:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 [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 主要有两种用法:
运行单个容器的 Pod。”每个 Pod 一个容器”模型是最常见的 Kubernetes 用例; 在这种情况下,可以将 Pod 看作单个容器的包装器,并且 Kubernetes 直接管理 Pod,而不是容器。
运行多个需要协同工作的容器的 Pod。一个pod中运行多个需要互相协作的容器,可以将多个紧密耦合、共享资源且始终在一起运行的容器编排在同一个pod中。
定义: Pod 是 Kubernetes 中最小的可部署单元,表示在集群中运行的一个或多个容器。Pod 中的容器共享相同的网络和存储资源。
特性:
2.1.3 Service ==对外提供稳定的访问地址。在Kubernetes 中,一个服务可能由多个Pod提供支持,Service 可以将这些 Pod 组合起来,对外暴露一个统一的访问入口,实现负载均衡。这样,即使 Pod 挂掉重启,服务的访问地址也不会改变。比如,一个订单服务部署了多个副本,通过 Service 可以让用户始终通过一个固定的地址来访问该服务。==
定义:
Service 是 Kubernetes 中用于定义一组 Pod 的访问策略的对象。它提供了一个稳定的 IP 地址和 DNS 名称,使得外部服务可以访问这组 Pod
类型:
ClusterIP(默认):仅在集群内部可访问
NodePort:通过节点的 IP 和端口对外提供服务
LoadBalancer:集成云服务商的负载均衡器,对外提供服务
ExternalName:通过 CNAME 记录将服务映射到外部服务地址
特性:
2.1.4 Deployment ==用于管理无状态服务的控制器。它定义了Pod 的模板,指定副本数量,并提供滚动更新和重建策略。滚动更新可以按照用户定义的速率逐步替换旧的 Pod,确保服务无中断;重建则是直接停掉所有 Pod 再进行升级,适合非关键任务。例如,对于一个 Web 服务应用,使用 Deployment 可以方便地管理其多个副本的部署和更新。==
定义: Deployment 用于管理无状态应用的部署和更新,确保指定数量的 Pod 副本始终处于运行状态
特点:
2.1.5 StatefulSet ==用于管理需要状态持久化的Pod。每个 Pod 都有一个唯一的标识符,生命周期有序,Pod启动和停止遵循固定模式。适用于数据库(如 MySQL、MongoDB)、消息队列(如 Kafka、RabbitMQ)、分布式存储系统等需要保存状态信息的应用。与 Deployment 管理的无状态服务不同,StatefulSet管理的服务需要保证数据的一致性和持久性。==
定义: StatefulSet 用于管理有状态应用的部署和更新,为每个 Pod 提供稳定的网络标识和存储
特点:
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 中用于将集群资源划分为多个逻辑分区。它提供了一个机制,用于在同一个物理集群中运行多个虚拟集群。==
特性:
2.1.11 Ingress ==Ingress 是 Kubernetes 中用于管理外部访问的对象。它提供了一个统一的入口,用于将外部请求路由到集群内的服务。==
特性:
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 自动补全功能
1 [root@master ~]# source <(kubeadm completion bash|tee /etc/bash_completion.d/kubeadm)
kubectl 命令行工具启用 Bash 自动补全功能
1 [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 1 2 3 [root@harbor ~]# dnf install -y docker-ce [root@harbor ~]# systemctl enable --now docker
2. 导入harbor项目镜像 1 2 3 4 5 6 7 8 9 10 11 [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 [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. 启动项目 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 [root@harbor harbor]# cp harbor.yml.tmpl harbor.yml [root@harbor harbor]# vim harbor.yml 05: hostname: 192.168.10.240 08: 10: 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. 验证项目 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 [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
退出登录
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 [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 [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 [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] ...... [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. 环境配置 1 2 3 4 [root@master ~]# sed '/swap/d' -i /etc/fstab [root@master ~]# swapoff -a [root@master ~]# dnf remove -y firewalld-*
2. 安装软件包 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 [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 [root@master ~]# dnf install -y kubeadm kubelet kubectl containerd.io ipvsadm ipset iproute-tc [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. 配置内核参数 1 2 3 4 5 6 7 8 9 10 11 12 13 [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
如果出现了以下的报错,可以使用modprobe命令加载模块
1 2 3 [root@node01 ~]# modprobe bridge [root@node01 ~]# modprobe nf_conntrack [root@node01 ~]# modprobe br_netfilter
执行命令后就可以了
4. 导入k8s所需的镜像 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 [root@master ~]# dnf install -y docker-ce [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 [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 [root@master ~]# docker login harbor:443 Username: <登录用户> Password: <登录密码> Login Succeeded [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安装 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 [root@master ~]# vim /root/init/init.yaml 13: advertiseAddress: 192.168.10.10 [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. 安装网络插件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 [root@master ~]# ls calico/ calico.tar.xz calico.yaml [root@master calico]# docker load -i calico.tar.xz [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 [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 安装计算节点 获取凭证 1 2 3 4 5 6 7 8 9 10 11 12 [root@master ~]# kubeadm token list TOKEN TTL EXPIRES abcdef.0123456789abcdef 23h 2022-04-12T14:04:34Z [root@master ~]# kubeadm token delete abcdef.0123456789abcdef bootstrap token "abcdef" deleted [root@master ~]# kubeadm token create --ttl=0 --print-join-commandesi kubeadm join 192.168.10.10:6443 --token fhf6gk.bhhvsofvd672yd41 --discovery-token-ca-cert-hash sha256:ea07de5929dab8701c1bddc347155fe51c3fb6efd2ce8a4177f6dc03d5793467 [root@master ~]# openssl x509 -pubkey -in /etc/kubernetes/pki/ca.crt |openssl rsa -pubin -outform der |openssl dgst -sha256 -hex
node安装 1 2 3 4 5 6 7 8 9 10 11 [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:<ca 证书 hash > [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
验证节点状态 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 [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 [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 <资源对象> [选项/参数]命令创建资源
示例:
1 2 3 4 5 6 7 8 9 10 11 12 [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 <pod名> --image=<镜像名:标签>
示例:
1 2 3 4 5 6 7 8 [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 资源类型 [资源名称] [选项/参数]
常用参数
kubectl exec [选项/参数] Pod名称 -- 操作命令
常用参数 -it 分配交互式终端
1 2 3 4 5 6 [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名称:绝对路径]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 [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命令删除==
1 2 3 4 5 6 7 8 9 10 11 [root@master ~]# kubectl delete pods myweb pod "myweb" deleted [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继续排查
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 [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 [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下是事务日志,常用于排错
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 [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 <pod名称> [选项/参数]
没有输出就是没错
1 2 3 [root@master ~]# kubectl -n work logs myhttp [root@master ~]#
2.5.3 资源清单文件管理 常用子命令
子命令
说明
备注
create
创建文件中定义的资源
支持指令式和资源清单文件配置
apply
创建(更新)文件中定义的资源
只支持资源清单文件(声明式)
delete
删除文件中定义的资源
支持指令式和资源清单文件配置
replace
更改/替换资源对象
强制重建 –force
最简单的资源清单文件示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 [root@master metrics]# cat base.yaml --- kind: Pod apiVersion: v1 metadata: name: myweb spec: containers: - name: nginx01 image: myos:nginx status: {} [root@master ~]# cat a.json { "kind" : "Pod" , "apiVersion" : "v1" , "metadata" : {"name" :"myweb" }, "spec" : {"containers" :[{"name" :"nginx" ,"image" :"myos:nginx" }]}, "status" : {} }
命令管理示例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 [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 [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]
示例:
1 2 3 4 5 6 7 8 9 [root@master ~]# kubectl create namespace work --dry-run=client -o yaml apiVersion: v1 kind: Namespace metadata: creationTimestamp: null name: work spec: {} status: {}
获取帮助信息 kubectl explain 资源对象.层级关系
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 [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 示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 [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]
示例:
1 2 3 4 5 6 7 8 [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 字段下。
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 [root@master ~ ] --- kind: Pod apiVersion: v1 metadata: name: mycmd spec: 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
restartPolicy策略
Pod级别配置 :restartPolicy是Pod级别的设置,会作用于Pod内的所有容器,无法单独为某个容器指定不同策略。
与控制器协同 :
Deployment/StatefulSet :通常使用Always,确保应用持续运行。
Job :通常使用OnFailure,任务失败时自动重试。
CronJob :根据需求选择OnFailure或Never。
默认值 : 未显式指定时,默认值为Always。
该策略有以下三个选项
总是重启 [Always]
失败不重启 [Never]
失败就重启 [OnFailure]
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 [root@master ~ ] --- 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)可能设置不同的默认值。
示例:
1 2 3 4 5 6 7 8 9 10 11 [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
典型用途
任务超时控制
优雅关闭应用(如保存状态)
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 [root@master ~]# vim mycmd.yaml --- kind: Pod apiVersion: v1 metadata: name: mycmd spec: terminationGracePeriodSeconds: 0 activeDeadlineSeconds: 60 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
2.6 Pod调度与标签 2.6.1 什么是调度分配? 在 k8s 中,调度是将 Pod 分配到合适的计算节点上,然后对应节点上的Kubelet 运行这些Pod
==kube-scheduler是默认调度器,是集群的核心组件==
2.6.2 调度流程 调度器给一个Pod做调度选择包含两个步骤:筛选和优选
筛选:
首先要筛选出满足pod所有的资源请求的节点,包含cpu,内存,存储,网络,端口号等,如果没有节点能满足 Pod 的需求,Pod 将一直停留在 Pending 状态,直到调度器能够找到合适的节点运行它
优选:
在优选阶段,调度器会根据打分规则,为每一个可调度节点进行打分。选出其中得分最高的节点来运行于Pod。如果存在多个得分最高的节点,调度器会从中随机选取一个
绑定
在确定了某个节点运行Pod之后,调度器将这个调度决定通知给 kube-apiserver,这个过程叫做绑定
2.6.3 Pod定向调度 基于节点名称的调度 在创建 Pod 的过程中,我们可以配置相关的调度规则,从而让 Pod 运行在制定的节点上,给pod添加nodeName标签,让 Pod 运行在制定的节点上
[!CAUTION]
注意:如果标签指定的节点无法运行Pod,它不会迁移到其他节点,将一直等待下去
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 [root@master ~]# vim myhttp.yaml --- kind: Pod apiVersion: v1 metadata: name: myhttp spec: nodeName: node01 containers: - name: apache image: myos:httpd [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对象上的键值对
标签的用途
管理标签语法格式
查看标签:kubectl get 资源类型 [资源名称] --show-labels
设置标签:kubectl label 资源类型 [资源名称] <key>=<value>
删除标签:kubectl label 资源类型 [资源名称] <key>-
使用标签选择:kubectl get 资源类型 -l <key>=<value>
基于标签调度 标签选择运算符
与名称和 UID 不同,标签不支持唯一性。通常,我们希望许多对象携带相同的标签。
通过标签选择运算符,客户端/用户可以识别一组对象。
标签选择运算符可以由多个需求组成。在多个需求的情况下,必须满足所有要求,相当于逻辑与(&&)运算符。
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 [root@master ~]# kubectl label nodes node02 ssd=v1 node/node02 labeled [root@master ~]# kubectl label nodes node03 ssd=v1 node/node03 labeled [root@master ~]# kubectl get nodes --show-labels ...... [root@master ~]# cat ssd.yaml --- kind: Pod apiVersion: v1 metadata: name: ssd labels: hh: jj spec: nodeSelector: 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生命周期?
生命周期有什么用途
生命周期中能做什么
必须操作:启动运行main(主)容器
可选操作:设置容器的初始化方法:(InitContainer)
可选操作:配置容器探针:生命探测(IivenessProbe)、就绪探测(readinessProbe)、启动探测(startupProbe)
可选操作:添加事件处理函数:启动后回调(PostStart)、结束前回调(PreStop)
2.7.2 Init容器
init容器是一种特殊容器,在Pod内主容器启动之前执行,可以依据需求的不同定义多个
init容器可以使用其他镜像,也可以包括一些主容器镜像中不存在的实用工具和安装脚本
init容器的生命是有限的,不能无休止的运行下去,只有在初始化容器执行完成以后才会启动主容器,即只要初始化容器的退出状态代码非0,则不会启动主容器
init容器和普通容器非常像,除了以下三点
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 [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 )) - name: task2 image: myos:latest command : ["sh" ] args: - -c - | ID=${RANDOM} echo "${ID} " sleep 3 exit $((ID%2 )) containers: - name: web image: myos:httpd [root@master ~]# kubectl replace --force -f web1.yaml pod "web1" deleted pod/web1 replaced [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种,分别为:
每次探测都会获得以下三种结果之一:
Success 成功,容器通过了诊断
Failure 失败,容器未通过诊断
Unknown 未知,诊断失败,不会采取任何行动
startupProbe启动探测类型
startupProbe启动探测用于确定容器是否已经启动成功
探测失败:kubelet将杀死容器,依据重启策略执行
如果配置了启动探针,其他所有探针都会被禁用,直到此探针执行成功为止
如果启动探针执行成功,且主容器已经启动,启动探针不会重复执行
如果未定义启动探针,默认为Success状态
使用startupProbe保护慢启动的服务:
基于tcpSocket 的端口检测,只检测端口状态,不需要返回数据,如果端口状态为 Open 则诊断被认为是成功的
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 [root@master ~]# vim web2.yaml ...... spec: containers: - name: web image: myos:httpd startupProbe: initialDelaySeconds: 60 periodSeconds: 10 failureThreshold: 6 tcpSocket: 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
livenessProbe生命探测类型
livenessProbe生命探测用于确定容器是否处于运行状态
livenessProbe在Pod的全部生命周期中运行,如果发现资源无法获取,kubelet将杀死容器,依据重启策略执行
如果没有设置restartPolicy默认使用镜像重建容器
livenssProbe检测失败重启容器的行为会重新激活startupProbe但不会重新执行InitContainer
如果未定义生命探针,默认为Success状态
使用httpGet配置livenessProbe检测
在httpGet的检测中,kubelet目标IP上指定的端口和路径执行HTTP GET请求。如果响应的状态码大于等于200且小于400,则诊断被认为是成功的
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 [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: path: /info.php 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 web2 1/1 Running 1 (11s ago) 171s
readinessProbe就绪探测型
readinessProbe在Pod的生命周期中一直存在,用来探测容器是否准备好提供服务,就绪探测用于确定容器是否已经准备好接收流量。
配置了就绪探测探针,初始的值默认状态为Failure
探测失败:端点控制器将拒绝Pod所提供的服务(这里是等待,并不重建容器)
如果未定义就绪探针,默认为Success状态
使用exec配置readinessProbe检测:
exec选项用来执行自定义命令的检测,通过返回的状态码判断是否成功,如果$?==0则诊断被认为是成功的,其他判断为失败
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 [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} [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.7.4 事件处理函数 postStart和prestop
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 [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
示例:
1 2 3 4 5 6 7 8 lscpu Architecture: x86_64 CPU op-mode(s): 32-bit, 64-bit Byte Order: Little Endian CPU(s): 2 On-line CPU(s) list: 0,1 Thread(s) per core: 2 Core(s) per socket: 1
内存资源类型
memory的约束和请求以字节为单位
你可以使用以下单位来表示内存:E、P、T、G、M、k
你也可以使用对应的2的幂数:Ei、Pi、Ti、Gi、Mi、K!
例如,以下表达式所代表的是相同的值:
1K == 1000
1Ki == 1024
资源配额示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 [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 [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%) ...... spec: nodeName: 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 [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进行配置
资源限额需要与配额共同配置
资源限额必须大于等于资源配额,否则报错
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 [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,回收资源以防止饥饿的过程
Quota资源管理 ResourceQuota 是 Kubernetes 中用于 限制命名空间(Namespace)内资源使用量 的机制,主要解决多租户或团队共享集群时的资源公平性和隔离性问题。其核心作用包括:
防止资源饥饿
避免单个命名空间过度占用集群资源(如 CPU、内存、Pod 数量),影响其他业务。
成本控制
为不同团队或项目分配明确的资源上限,便于核算资源消耗。
优先级管理
通过配额区分高/低优先级业务(例如限制 BestEffort Pod 数量,保障 Guaranteed Pod 资源)。
规范化资源使用
强制要求开发团队在部署时声明资源请求(Requests/Limits),避免资源浪费。
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 [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 [root@master 5.17]# kubectl apply -f resourcequota.yml namespace/test created resourcequota/quota-1 created [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 [root@master 5.17]# cat Burstable.yaml | kubectl -n test apply -f -
2.8 Pod控制器 2.8.1 Pod的定义 Pod是kubernetes的最小管理单元,在kubernetes中,按照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不存在
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 [root@master test ]# vim rs01.yaml --- apiVersion: apps/v1 kind: ReplicaSet metadata: name: rs01 spec: selector: matchExpressions: - key: version operator: Exists replicas: 3 template: 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):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 [root@master test ]# kubectl get rs NAME DESIRED CURRENT READY AGE rs01 3 3 3 7s [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 [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) [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)
1 2 3 4 5 6 [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的所有功能
支持发布的停止、继续
支持滚动升级和回滚版本
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 [root@master test ]# cat pc-deployment.yaml --- kind: Deployment apiVersion: apps/v1 metadata: name: pc-deployment namespace: default spec: replicas: 3 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 [root@master test ]# kubectl get deploy NAME READY UP-TO-DATE AVAILABLE AGE pc-deployment 3/3 3 3 14m [root@master test ]# kubectl get rs NAME DESIRED CURRENT READY AGE pc-deployment-f864b4d67 3 [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命令进行扩缩容。
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 [root@master ~]# kubectl edit deployment pc-deployment deployment.apps/pc-deployment edited [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默认就是滚动更新
1 2 3 4 5 6 7 8 strategy type Recreate RollingUpdate rollingUpdate maxSurge maxUnavailable
重建策略示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 [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 [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 [root@master test ]# kubectl set image deployment pc-deployment test =myos:nginx deployment.apps/pc-deployment image updated [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 [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
滚动更新 添加滚动更新策略(这个为默认策略)
滚动策略示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 [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% replicas: 5 selector: matchLabels: app: test template: metadata: labels: app: test spec: containers: - name: test image: myos:nginx
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 [root@master test ]# kubectl apply -f pc-deployment.yaml deployment.apps/pc-deployment configured [root@master test ]# kubectl set image deployment pc-deployment test =myos:httpd deployment.apps/pc-deployment image updated [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 [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的变化。
1 2 3 4 5 6 7 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
deployment支持版本升级过程中的暂停、继续功能以及版本回退等诸多功能,下面具体来看:
kubectl rollout 命令: 版本升级相关功能,支持下面的选项
示例:
1 2 3 4 5 6 7 8 9 10 [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>
给新版本添加注释信息补充:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 [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
版本回滚 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 [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 [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 [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
回滚到指定的版本 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 [root@master test ]# kubectl rollout history deployment pc-deployment deployment.apps/pc-deployment REVISION CHANGE-CAUSE 2 <none> 7 <none> 8 <none> [root@master test ]# kubectl rollout undo deployment pc-deployment --to-revision=7 deployment.apps/pc-deployment rolled back [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 [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资源滚动更新,否则立即回滚更新操作。这就是所谓的金丝雀发布。==
版本升级,然后暂停示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 [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 [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 [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... [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
新版本确认没问题,进行全部滚动更新:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 [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 [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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 [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。==
$(podname).(headless server name)
FQDN:$(podname).(headless server name).namespace.svc.cluster.local
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 [root@master ~]# vim stssvc.yaml --- kind: Service apiVersion: v1 metadata: name: stssvc spec: type : ClusterIP clusterIP: None selector: app: sts-httpd 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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 [root@master ~]# vim mysts.yaml --- kind: StatefulSet apiVersion: apps/v1 metadata: name: mysts spec: serviceName: stssvc replicas: 3 selector: matchLabels: app: sts-httpd template: metadata: labels: app: sts-httpd spec: containers: - name: web image: myos:httpd [root@master ~]# kubectl apply -f mysts.yaml statefulset.apps/mysts created [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 [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 [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 [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类型的控制器创建。==
特点:
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 [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 [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 [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资源清单文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 apiVersion: batch/v1 kind: Job metadata: name: namespace: labels: controller: job 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 2;done" ]
关于重启策略设置的说明:
如果指定为OnFailure,则job会在pod出现故障时重启容器,而不是创建pod,failed次数不变
如果指定为Never,则job会在pod出现故障时创建新的pod,并且故障pod不会消失,也不会重启,failed次数加1
如果指定为Always的话,就意味着一直重启,意味着job任务会重复去执行了,当然不对,所以不能设置为Always
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 [root@master test ]# vim pc-job.yaml --- kind: Job apiVersion: batch/v1 metadata: name: pc-job spec: manualSelector: true 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 [root@master test ]# kubectl create -f pc-job.yaml job.batch/pc-job created [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 [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运行的总数量和并行数量:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 [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 parallelism: 3 manualSelector: true selector: matchLabels: app: nginx template: ...... [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 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 ......
2.8.8 CronJob(CJ) ==CronJob控制器以Job控制器资源为其管控对象,并借助它管理pod资源对象,Job控制器定义的作业任务在其控制器资源创建之后便会立即执行,但CronJob可以以类似于Linux操作系统的周期性计划任务的方式控制其运行时间点及重复运行的方式。也就是说,CronJob可以在特定的时间点(反复的)去运行job任务。==
CronJob资源清单示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 --- apiVersion: batch/v1 kind: CronJob metadata: name: namespace: labels: controller: cronjob spec: schedule: concurrencyPolicy: failedJobHistoryLimit: successfulJobHistoryLimit: startingDeadlineSeconds: jobTemplate: 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 * * * * <分> <时> <日> <月> <周> 分钟: 值从 0 到 59. 小时: 值从 0 到 23. 日: 值从 1 到 31. 月: 值从 1 到 12. 周: 值从 0 到 6, 0 代表星期日 多个时间可以用逗号隔开; 范围可以用连字符给出;*可以作为通配符; /表示每... concurrencyPolicy: Allow: 允许Jobs并发运行(默认) Forbid: 禁止并发运行,如果上一次运行尚未完成,则跳过下一次运行 Replace: 替换,取消当前正在运行的作业并用新作业替换它
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 [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 [root@master test ]# kubectl create -f pc-cronjob.yaml cronjob.batch/pc-cronjob created [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 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 [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示例:
配置后端服务 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 [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: cpu: 300m --- 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控制器 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 [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 maxReplicas: 4 metrics: - type : Resource resource: name: cpu target: type : Utilization averageUtilization: 50 [root@master ~]# kubectl apply -f myhpa.yaml horizontalpodautoscaler.autoscaling/myhpa created [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 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 [root@master ~]# while sleep 1;do curl -s "http://10.245.1.80/info.php?id=100000" -o /dev/null; done & [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 [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 特点:
服务发现,防止阴滚动升级等因素导致 Pod IP 发生改变而失联,找到提供同一个服务的 Pod。
负载均衡,定义一组 Pod 的访问策略。
svc 与 pod 关系:
pod 在创建时,与资源没有明确关联,通过 service 标签和 pod 标签相匹配来以此关联。
可以通过 endpoints 来查看关联的 pod。
2.9.2 svc的三大特征 服务的自动感知
服务会创建一个clusterlP这个地址对应资源地址,不管Pod 如何变化,服务总能找到对应的Pod,且clusterIP保持不变
服务的负载均衡
如果服务后端对应多个pod,则会通过Iptables/LVS规则将访问的请求最终映射到pod的容器内部,自动在多个容器间实现负载均衡
服务的自动发现
服务创建时会自动在内部DNS上注册域名
域名:<服务名称>.<名称空间>.svc.cluster.local
svc选中pod的逻辑
pod是处于running状态
pod的标签是svc标签的集合(同一个名称空间下)
2.9.3 ClusterIP(集群内部使用) ==默认方式,分配一个稳定的IP地址,即VIP,只能在集群内部访问==
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 [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 [root@master test ]# cat service.yaml --- kind: Service apiVersion: v1 metadata: name: http-test labels: run: http spec: selector: run: http ports: - name: http protocol: TCP port: 80 targetPort: 80 [root@master test ]# kubectl apply -f service.yaml -f server.yaml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 [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> [root@master test ]# kubectl describe svc http-test Name: http-test Namespace: default Labels: run=http Annotations: <none> Selector: run=http Type: ClusterIP IP Family Policy: SingleStack IP Families: IPv4 IP: 10.245.50.225 IPs: 10.245.50.225 Port: <unset > 80/TCP TargetPort: 80/TCP Endpoints: 10.244.58.193:80,10.244.85.219:80 Session Affinity: None Events: <none> [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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 [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 -> 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
域名自动注册 ==域名自动注册就是说,当你创建了一个svc资源的时候,k8s内部会自动的给相对应的svc资源注册一个域名,k8s内部的CoreDNS是能解析该域名的,然后我们就可以在pod中通过该域名来解析相对应的服务了,域名格式为<服务名称>.<名称空间>.svc.cluster.local.==
[!CAUTION]
注意:Node节点是不能解析该域名的,因为节点使用的是宿主机自己的 /etc/resolv.conf,不认识 svc.cluster.local 域名
1 2 3 4 5 6 7 8 9 10 11 12 13 14 [root@master ~]# dnf install -y bind-utils [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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 [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" [root@master test ]# kubectl apply -f client-test.yaml [root@master test ]# kubectl exec -it test-client -- sh / php_host: http-test-57fb9c77d5-djrmd / php_host: http-test-57fb9c77d5-k4znk
补充一(内部交通策略): svc.spec.internalTrafficPolicy 是 Kubernetes 中 Service 对象的一个字段,用于控制来自集群内部源的流量如何被路由。该字段有两个可能的值:Cluster 和 Local。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 [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" ` routes traffic to all endpoints. - `"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.sessionAffinity.clientIP 是 Kubernetes 中 Service 对象的一个字段,用于配置基于客户端 IP 的会话亲和性。会话亲和性是一种机制,它确保来自同一客户端 IP 的请求总是被路由到同一个后端 Pod,从而在多个请求之间保持会话状态。==
ServiceAffinity 默认值是”None”,如果 ServiceAffinity 设置为 “ClientIP”,则该值必须大于 0 且小于等于 86400(1 天)。默认值为 10800(3 小时)。这意味着在默认情况下,来自同一客户端 IP 的请求将在 3 小时内被路由到同一个 Pod。如果在这段时间内没有来自该客户端 IP 的新请求,会话亲和性将不再生效,后续的请求可能会被路由到不同的 Pod。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 [root@master test ]# kubectl explain svc.spec.sessionAffinity KIND: Service VERSION: v1 FIELD: sessionAffinity <string> DESCRIPTION: Supports "ClientIP" and "None" . Used to maintain session affinity. Enable client IP based session affinity. Must be ClientIP or None. Defaults to None. More info: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies Possible enum values: - `"ClientIP" ` is the Client IP based. - `"None" ` - no session affinity.
2.9.4 NodePort(对外暴露应用) NodePort相当于是ClusterIP的一个升级版可以实现ClusterIP的完整功能,在每个节点启用一个端口来暴露服务,可以在集群外部访问,通过NodeIP:NodePort访问端口范围:30000~32767
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 [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 [root@master test ]# vim nodeport.yaml --- kind: Service apiVersion: v1 metadata: name: nodeport spec: type : NodePort selector: app: apache ports: - name: http port: 80 nodePort: 30333 targetPort: 80 [root@master test ]# kubectl apply -f deploy_nodeport.yaml -f nodeport.yaml deployment.apps/nodeprot-deploy configured service/nodeport unchanged
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 [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 Namespace: default Labels: <none> Annotations: <none> Selector: app=apache Type: NodePort IP Family Policy: SingleStack IP Families: IPv4 IP: 10.245.67.236 IPs: 10.245.67.236 Port: http 80/TCP TargetPort: 80/TCP NodePort: http 30333/TCP 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模式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 [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集群外部访问的效果 验证效果:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 [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.externalTrafficPolicy字段是 Kubernetes 中 Service(类型为 NodePort 或 LoadBalancer)的一个重要字段,用于控制 来自集群外部的流量 如何被路由到后端 Pod,该策略和svc.spec.internalTrafficPolicy 有点类似,只是一个是控制外部流量的走向,一个是控制内部流量的走向。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 [root@master 5.28]# kubectl explain svc.spec.externalTrafficPolicy KIND: Service VERSION: v1 FIELD: externalTrafficPolicy <string> DESCRIPTION: externalTrafficPolicy describes how nodes distribute service traffic they receive on one of the Service's "externally-facing" addresses (NodePorts, ExternalIPs, and LoadBalancer IPs). If set to "Local", the proxy will configure the service in a way that assumes that external load balancers will take care of balancing the service traffic between nodes, and so each node will deliver traffic only to the node-local endpoints of the service, without masquerading the client source IP. (Traffic mistakenly sent to a node with no endpoints will be dropped.) The default value, "Cluster", uses the standard behavior of routing to all endpoints evenly (possibly modified by topology and other features). Note that traffic sent to an External IP or LoadBalancer IP from within the cluster will always get "Cluster" semantics, but clients sending to a NodePort from within the cluster may need to take traffic policy into account when picking a node. Possible enum values: - `"Cluster"` # 默认值,将流量路由到所有节点的后端Pod。 - `"Cluster"` routes traffic to all endpoints. - `"Local"` # 值为Local时,外部访问节点的IP:Port的时候,只会被路由到与请求发起的客户端位于同一节点上的后端 Pod,如果该节点没有运行相对应的后端Pod话,请求会失败 - `"Local"` preserves the source IP of the traffic by routing only to endpoints on the same node as the traffic was received on (dropping the traffic if there are no local endpoints).
2.9.5 LoadBalancer(对外暴露应用,适用于公有云) 与NodePort类似,在每个节点启用一个端口来暴露服务。除此之外,K8s请求底层云平台的负载均衡器,把每个[Node IP]:[NodePort]作为后端添加进去
华为云示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 [root@harbor ~]# vim websvc.yaml --- kind: Service apiVersion: v1 metadata: name: websvc annotations: kubernetes.io/elb.class: union kubernetes.io/elb.id: <your_elb_id> kubernetes.io/elb.transparent-client-ip: 'true' 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
2.9.6 ExternalName ==ExternalName 类型的 Service 是 Kubernetes 中用于提供从集群内部访问外部 DNS 名称的一种方式。它的主要目的是简化集群内部应用访问外部服务的过程。当你创建一个类型为 ExternalName 的 Service 时,Kubernetes 会为这个 Service 创建一个 DNS 条目,该条目将解析为指定的外部 DNS 名称。==
[!CAUTION]
与 NodePort 或 LoadBalancer 类型的 Service 不同,ExternalName 类型的 Service 不会暴露任何集群内部的服务到外部网络,而是相反,它让集群内部的服务可以访问外部的服务。
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 [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 [root@master test ]# kubectl apply -f test.yaml -f externalname.yaml [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 [root@master test ]# kubectl exec -it test -- bash [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.7 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
标签子集运算
就绪的状态
没有定义标签选择器
不会创建 同名的endpoints资源对象,需要管理员手动创建
手动关联service示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 [root@master test02]# vim test_service.yaml --- kind: Service apiVersion: v1 metadata: name: nginx spec: type : ClusterIP clusterIP: 10.245.8.8 ports: - targetPort: 80 port: 8888 protocol: TCP --- kind: Endpoints apiVersion: v1 metadata: name: nginx subsets: - addresses: - ip: 192.168.10.12 ports: - port: 80 [root@master test02]# kubectl apply -f test_service.yaml service/nginx created endpoints/nginx created [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 [root@master test02]# kubectl get ep NAME ENDPOINTS AGE kubernetes 192.168.10.10:6443 7d3h nginx 192.168.10.12:80 9s [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 -> 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 [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 参数传递配置信息,该参数可以使用多次
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 [root@master test02]# kubectl create cm literal-config --from-literal=name=lili --from-literal=passwd=123 configmap/literal-config created [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> [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 里面创建一个键值对,键的名字就是文件名,值就是文件的内容
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 [root@master ~]# ls test_cm/ name passwd [root@master ~]# kubectl create cm test-cmdir --from-file=./test_cm/ configmap/test-cmdir created [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> [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 这个参数可以使用多次,你可以使用两次分别指定上个实例中的那两个配置文件,效果就跟指定整个目录是一样的
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 [root@master test02]# kubectl create cm test-config --from-file=test_cm.file configmap/test-config created [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 [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> [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 不会同步更新
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 [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 apiVersion: v1 metadata: name: env-pod spec: restartPolicy: Never containers: - name: test image: myos:nginx command : - sh - -c - env env : - name: USERNAME valueFrom: configMapKeyRef: name: literal-config key: name - name: PASSWORD valueFrom: configMapKeyRef: name: literal-config key: passwd envFrom: - 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 NGINX_PORT_8888_TCP_PORT=8888 PASSWORD=admin 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 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的数据保存在环境变量中,然后通过环境变量的方式引用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 [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 : - 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中的配置文件。每个键值对都会生成一个文件,其中键为文件名,值为文件内容。这样,应用程序就可以根据需要读取配置文件中的配置信息。
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 [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: - name: config-volume mountPath: /etc/config volumes: - name: config-volume 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]命令直接修改内容就可以达到热更新
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 [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 volumes: - name: config-volume configMap: name: log-config [root@master test02]# kubectl exec -it my-nginx-746bd4859b-qw6wp -- cat /etc/config/log_level INFO [root@master test02]# kubectl edit cm log-config configmap/log-config edited [root@master test02]# kubectl exec -it my-nginx-746bd4859b-qw6wp -- cat /etc/config/log_level NOTICE
补充:添加不可改变选项
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 [root@master test02]# kubectl edit cm log-config 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
补充 Pod滚动更新
ConfigMap 更新后,并不会让相应的文件重载。例如,Nginx 在启动时,会加载一次配置文件(配置文件中有 ConfigMap 的相关参数),加载完成后,无论这个配置文件再怎么变化,Nginx 都不会再加载它。==因此需要 ConfigMap 更新后,再滚动更新 Pod。==
可以通过修改 pod annotations 的方式强制触发滚动更新。这里我们在 Deployment.spec.template.metadata.annotations 中添加 version/config字段来实现pod的滚动更新
1 kubectl patch deployment my-nginx --patch '{"spec": {"template": {"metadata": {"annotations":{"version/config": "20250124" }}}}}'
注意:更新 ConfigMap 后:
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 编码格式,可以用于环境变量和数据卷挂载
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 [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 [root@master test02]# vim secret.yaml --- kind: Secret apiVersion: v1 metadata: name: mysecret type : Opaquedata: passwd: MzIx username: aGVoZQ== [root@master test02]# kubectl apply -f secret.yaml secret/mysecret created [root@master test02]# kubectl get secret NAME TYPE DATA AGE mysecret Opaque 2 2m18s [root@master test02]# kubectl describe secrets mysecret Name: mysecret Namespace: default Labels: <none> Annotations: <none> Type: Opaque Data ==== passwd: 3 bytes username: 4 bytes [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 环境变量 作为环境变量(不可以热更新)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 [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: name: mysecret key: username - name: TEST_PASS valueFrom: secretKeyRef: name: mysecret key: passwd [root@master test02]# kubectl apply -f mysecret.yaml deployment.apps/deploy-secret created [root@master test02]# kubectl logs deploy-secret-6c848876bf-dcb2s hehe 321
卷挂载 secret也是支持热更新的和configmap一样,==但是使用secret作为子路径卷挂载的容器不会收到secret更新==
将secret挂载到volume中示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 [root@master test02]# vim secret_volume.yaml --- kind: Pod apiVersion: v1 metadata: name: secret-test spec: volumes: - name: secret secret: secretName: mysecret containers: - name: test image: myos:nginx volumeMounts: - name: secret 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 [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 的相关信息。
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 [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 valueFrom: fieldRef: fieldPath: metadata.name - name: NAMESPACE valueFrom: fieldRef: fieldPath: metadata.namespace - name: POD_IP valueFrom: fieldRef: fieldPath: status.podIP - name: CPU_LIMIT 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 CPU_LIMIT=2 CPU_REQUEST=0 POD_NAME=downward-api-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 信息的情况。
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 [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 的步骤
创建包含 Downward API 信息的 Pod:编写 Pod 的 YAML 配置文件,定义需要注入的环境变量或卷挂载。
使用 kubectl 创建 Pod:使用 kubectl apply -f <pod-config-file.yaml> 命令创建 Pod。
在容器中读取 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 的用法有:
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 [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 mountPath: /test01_volume - name: test02 image: myos:php-fpm volumeMounts: - name: emptydir-volume mountPath: /test02_volume volumes: - name: emptydir-volume emptyDir: {} [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 [root@master test02]# kubectl exec -it test-emptydir-pod -c test01 -- touch /test01_volume/info.txt [root@master test02]# kubectl exec -it test-emptydir-pod -c test02 -- ls /test02_volume info.txt
数据存储落地路径 在kubelet的工作目录,默认为/var/lib/kubelet,会为每个使用了emptyDir:{}的pod创建一个目录,格式为/var/lib/kubelet/pods/{podid}/volumes/kubernetes.io~empty-dir/,所有放在emptyDir中数据,最终都是落在了node的上述路径中的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 [root@node01 3de59b4b-8838-4c79-a036-8c007774d24e]# pwd /var/lib/kubelet/pods/3de59b4b-8838-4c79-a036-8c007774d24e [root@node01 3de59b4b-8838-4c79-a036-8c007774d24e]# tree . . ├── containers │ ├── hello │ │ └── b3ac762c │ └── nginx │ └── 0518943d ├── etc-hosts ├── plugins │ └── kubernetes.io~empty-dir │ ├── emptydir-volume │ │ └── ready │ └── wrapped_kube-api-access-4wqhw │ └── ready └── volumes ├── kubernetes.io~empty-dir │ └── emptydir-volume │ ├── access.log │ ├── error.log │ └── nginx.pid └── kubernetes.io~projected └── kube-api-access-4wqhw ├── ca.crt -> ..data/ca.crt ├── namespace -> ..data/namespace └── token -> ..data/token 12 directories, 11 files [root@node01 3de59b4b-8838-4c79-a036-8c007774d24e]# cd volumes/kubernetes.io~empty-dir/emptydir-volume/ [root@node01 emptydir-volume]# echo hello world >> access.log [root@master ~]# kubectl logs emptydir hello 10.244.219.64 - - [03/Jun/2025:03:07:09 +0000] "GET / HTTP/1.1" 200 19 "-" "curl/7.61.1" 10.244.219.64 - - [03/Jun/2025:03:07:11 +0000] "GET / HTTP/1.1" 200 19 "-" "curl/7.61.1" 10.244.219.64 - - [03/Jun/2025:03:07:14 +0000] "GET / HTTP/1.1" 200 19 "-" "curl/7.61.1" 10.244.219.64 - - [03/Jun/2025:03:07:16 +0000] "GET / HTTP/1.1" 200 19 "-" "curl/7.61.1" 10.244.219.64 - - [03/Jun/2025:03:07:18 +0000] "GET / HTTP/1.1" 200 19 "-" "curl/7.61.1" 10.244.219.64 - - [03/Jun/2025:03:07:20 +0000] "GET / HTTP/1.1" 200 19 "-" "curl/7.61.1" 10.244.219.64 - - [03/Jun/2025:03:07:38 +0000] "GET / HTTP/1.1" 200 19 "-" "curl/7.61.1" hello world
hostPath ==在Kubernetes(k8s)中,HostPath是一种特殊的卷类型,它允许将节点(Node)上的文件或目录直接挂载到Pod中。这种挂载方式使得Pod能够访问宿主机上的文件系统,从而实现了数据的持久化存储,即使Pod被删除或重建,只要宿主机上的文件或目录仍然存在,数据就不会丢失。==
HostPath的配置参数
在Kubernetes中配置HostPath卷时,通常需要指定以下参数:
path:指定宿主机上的目录或文件路径,这是必选字段。
type(可选):指定节点之上存储类型,包括以下几种:
DirectoryOrCreate:如果给定的路径不存在,则创建一个空目录,权限设置为755。
Directory:目录必须存在。
FileOrCreate:如果给定的文件不存在,则创建一个空文件,权限设置为644。
File:文件必须存在。
Socket:UNIX套接字,必须存在。
CharDevice:字符设备,必须存在。
BlockDevice:块设备,必须存在。
HostPath卷适用于以下场景:
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 [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: - 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 ! [root@master ~]# kubectl delete pod web1 pod "web1" deleted [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 服务器上的文件,就像访问本地文件一样==
示例:
1 2 3 4 5 6 7 8 9 10 11 12 [root@harbor ~]# mkdir -p /var/webroot [root@harbor ~]# echo "nfs server" >/var/webroot/index.html [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 [root@node ~]# dnf install -y nfs-utils
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 [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 path: /var/webroot 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 [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的一般流程如下:
创建存储卷:根据需求选择合适的Volume类型,并创建相应的存储卷资源。
挂载存储卷:在Pod的配置文件中指定要挂载的存储卷,并将其挂载到Pod中的容器上。
访问存储卷中的数据:在Pod的容器中,通过挂载路径访问存储卷中的数据。
注意事项
Volume的生命周期与Pod相关,但与容器的生命周期不相关。当Pod被删除时,与其关联的Volume(除非设置为持久化存储)也会被删除。
在使用网络存储或持久化存储时,需要确保存储系统的稳定性和可靠性,以避免数据丢失或损坏。
对于敏感数据的存储,建议使用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与具体的存储实现解耦,提高了可移植性。
工作流程
用户根据需求创建PVC,声明所需的存储资源规格。
Kubernetes根据PVC中的需求寻找合适的PV进行绑定。
如果环境支持动态存储配额,当没有合适的PV可用时,可以根据PVC请求动态创建一个新的PV。
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回收策略
当前,只有 NFS 和 HostPath 支持回收策略。AWS EBS、GCE PD、Azure Disk 和 Cinder 卷支持删除策
PV状态 卷可以处于以下的某种状态:
命令行会显示绑定到 PV 的 PVC 的名称。
示例: 在master部署nfs服务端,node01和node02为客户端
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 yum install -y nfs-common nfs-utils rpcbind mkdir /nfschmod 666 /nfschown nfsnobody /nfsdata [root@master ~]# vim mkdirnfs.sh 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 [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 [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示例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 [root@master test02]# vim pv.yaml --- apiVersion: v1 kind: PersistentVolume metadata: name: nfspv0 spec: capacity: storage: 0.5Gi accessModes: - ReadWriteOnce persistentVolumeReclaimPolicy: Recycle storageClassName: nfs nfs: path: /nfs/0 server: 192.168.10.10 --- apiVersion: v1 kind: PersistentVolume metadata: name: nfspv1 spec: capacity: storage: 1Gi accessModes: - ReadWriteMany persistentVolumeReclaimPolicy: Recycle storageClassName: nfs nfs: path: /nfs/1 server: 192.168.10.10 --- apiVersion: v1 kind: PersistentVolume metadata: name: nfspv2 spec: capacity: storage: 1Gi accessModes: - ReadWriteOnce persistentVolumeReclaimPolicy: Recycle storageClassName: nfs1 nfs: path: /nfs/2 server: 192.168.10.10 --- apiVersion: v1 kind: PersistentVolume metadata: name: nfspv3 spec: capacity: storage: 1Gi accessModes: - ReadWriteOnce persistentVolumeReclaimPolicy: Retain storageClassName: nfs nfs: path: /nfs/3 server: 192.168.10.10 --- apiVersion: v1 kind: PersistentVolume metadata: name: nfspv4 spec: capacity: storage: 1Gi accessModes: - ReadWriteOnce persistentVolumeReclaimPolicy: Recycle storageClassName: nfs nfs: path: /nfs/4 server: 192.168.10.10 --- apiVersion: v1 kind: PersistentVolume metadata: name: nfspv5 spec: capacity: storage: 1Gi accessModes: - ReadWriteOnce persistentVolumeReclaimPolicy: Recycle storageClassName: nfs nfs: path: /nfs/5 server: 192.168.10.10 --- apiVersion: v1 kind: PersistentVolume metadata: name: nfspv6 spec: capacity: storage: 1.5Gi accessModes: - ReadWriteOnce persistentVolumeReclaimPolicy: Recycle storageClassName: nfs nfs: path: /nfs/6 server: 192.168.10.10 --- apiVersion: v1 kind: PersistentVolume metadata: name: nfspv7 spec: capacity: storage: 1Gi accessModes: - ReadWriteOnce persistentVolumeReclaimPolicy: Recycle storageClassName: nfs nfs: path: /nfs/7 server: 192.168.10.10 --- apiVersion: v1 kind: PersistentVolume metadata: name: nfspv8 spec: capacity: storage: 1Gi accessModes: - ReadWriteOnce persistentVolumeReclaimPolicy: Recycle storageClassName: nfs nfs: path: /nfs/8 server: 192.168.10.10 --- apiVersion: v1 kind: PersistentVolume metadata: name: nfspv9 spec: capacity: storage: 1Gi accessModes: - ReadWriteOnce persistentVolumeReclaimPolicy: Recycle storageClassName: nfs nfs: path: /nfs/9 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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 [root@master test02]# vim pvc.yaml --- apiVersion: v1 kind: Service metadata: name: nginx labels: app: nginx spec: ports: - port: 80 name: web clusterIP: None selector: app: nginx --- apiVersion: apps/v1 kind: StatefulSet metadata: name: web spec: selector: matchLabels: app: nginx serviceName: "nginx" replicas: 5 template: metadata: labels: app: nginx spec: containers: - name: nginx image: myos:nginx ports: - containerPort: 80 name: web volumeMounts: - name: www mountPath: /usr/local/nginx/html volumeClaimTemplates: - 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 [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 [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 [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 [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 中的数据不会丢失
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 [root@master ~]# kubectl delete pods web-0 pod "web-0" deleted [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 [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也不会自动释放,需要手动删除
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 [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 [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 > [root@master test ]# kubectl edit pv nfspv3 spec: claimRef: apiVersion: v1 kind: PersistentVolumeClaim name: www-web-0 namespace: default resourceVersion: "619064" uid: 99cea07e-339e-431c-bcb6-c398c884b29c [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 亲和性
[!TIP]
理解本章内容请先了解Pod调度的特性[点我跳转](###2.6 Pod调度与标签)
在 Kubernetes(k8s)中,亲和性(Affinity) 是一种调度机制,用于控制 Pod 如何分配到节点(Node)或与其他 Pod 共存。它分为两类:
Node Affinity (节点亲和性):定义 Pod 倾向于调度到哪些节点。
Pod Affinity/Anti-Affinity (Pod 亲和性/反亲和性):定义 Pod 倾向于与哪些 Pod 运行在相同或不同的节点/拓扑域。
Node Affinity(节点亲和性) 控制 Pod 调度到符合特定标签的节点上,替代了早期的 nodeSelector ,功能更强大。
requiredDuringSchedulingIgnoredDuringExecution (硬亲和性):必须满足条件,否则 Pod 无法调度。
preferredDuringSchedulingIgnoredDuringExecution (软亲和性):优先满足条件,但不强制。
软亲和示例: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 [root@master 6.13]# vim 1.affinity_preferred.yaml --- kind: Pod apiVersion: v1 metadata: name: affinity-preferred spec: containers: - name: affinity image: myos:httpd affinity: nodeAffinity: preferredDuringSchedulingIgnoredDuringExecution: - weight: 1 preference: matchExpressions: - key: domain operator: In values: - henan - beijing [root@master 6.13]# while true ; > do > kubectl apply -f 1.affinity_preferred.yaml > kubectl get pods -o wide > kubectl delete -f 1.affinity_preferred.yaml > done pod/affinity-preferred unchanged NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES affinity-preferred 1/1 Running 0 71s 10.244.140.83 node02 <none> <none> pod "affinity-preferred" deleted pod/affinity-preferred created NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES affinity-preferred 0/1 ContainerCreating 0 0s <none> node02 <none> <none> pod "affinity-preferred" deleted pod/affinity-preferred created NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES affinity-preferred 0/1 ContainerCreating 0 0s <none> node02 <none> <none> pod "affinity-preferred" deleted pod/affinity-preferred created NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES affinity-preferred 0/1 ContainerCreating 0 0s <none> node02 <none> <none> pod "affinity-preferred" deleted pod/affinity-preferred created NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES affinity-preferred 0/1 ContainerCreating 0 0s <none> node02 <none> <none> pod "affinity-preferred" deleted [root@master 6.13]# vim 2.deployment_test.yaml apiVersion: apps/v1 kind: Deployment metadata: labels: app: test name: test spec: replicas: 10 selector: matchLabels: app: test template: metadata: labels: app: test spec: containers: - image: myos:httpd name: myos nodeName: node02 [root@master 6.13]# kubectl apply -f 2.deployment_test.yaml deployment.apps/test created [root@master 6.13]# kubectl replace --force -f 1.affinity_preferred.yaml pod "affinity-preferred" deleted pod/affinity-preferred replaced [root@master 6.13]# kubectl get pods -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES affinity-preferred 1/1 Running 0 1s 10.244.186.212 node03 <none> <none> test-5f4449bccc-24jb8 1/1 Running 0 32s 10.244.140.103 node02 <none> <none> test-5f4449bccc-4rqkx 1/1 Running 0 32s 10.244.140.96 node02 <none> <none> test-5f4449bccc-5cgqf 1/1 Running 0 32s 10.244.140.97 node02 <none> <none> test-5f4449bccc-6kfl7 1/1 Running 0 32s 10.244.140.100 node02 <none> <none> test-5f4449bccc-7rb4m 1/1 Running 0 32s 10.244.140.101 node02 <none> <none> test-5f4449bccc-jlm4v 1/1 Running 0 32s 10.244.140.98 node02 <none> <none> test-5f4449bccc-kq7qb 1/1 Running 0 32s 10.244.140.104 node02 <none> <none> test-5f4449bccc-nnj4s 1/1 Running 0 32s 10.244.140.105 node02 <none> <none> test-5f4449bccc-p2xgh 1/1 Running 0 32s 10.244.140.99 node02 <none> <none> test-5f4449bccc-sn9jd 1/1 Running 0 32s 10.244.140.102 node02 <none> <none> [root@master 6.13]# kubectl label nodes node02 domain=henan node/node02 labeled [root@master 6.13]# kubectl replace --force -f 1.affinity_preferred.yaml pod "affinity-preferred" deleted pod/affinity-preferred replaced [root@master 6.13]# kubectl get pods -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES affinity-preferred 1/1 Running 0 4s 10.244.140.107 node02 <none> <none> test-5f4449bccc-24jb8 1/1 Running 0 107s 10.244.140.103 node02 <none> <none> test-5f4449bccc-4rqkx 1/1 Running 0 107s 10.244.140.96 node02 <none> <none> test-5f4449bccc-5cgqf 1/1 Running 0 107s 10.244.140.97 node02 <none> <none> test-5f4449bccc-6kfl7 1/1 Running 0 107s 10.244.140.100 node02 <none> <none> test-5f4449bccc-7rb4m 1/1 Running 0 107s 10.244.140.101 node02 <none> <none> test-5f4449bccc-jlm4v 1/1 Running 0 107s 10.244.140.98 node02 <none> <none> test-5f4449bccc-kq7qb 1/1 Running 0 107s 10.244.140.104 node02 <none> <none> test-5f4449bccc-nnj4s 1/1 Running 0 107s 10.244.140.105 node02 <none> <none> test-5f4449bccc-p2xgh 1/1 Running 0 107s 10.244.140.99 node02 <none> <none> test-5f4449bccc-sn9jd 1/1 Running 0 107s 10.244.140.102 node02 <none> <none>
硬亲和示例: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 [root@master 6.13]# vim 3.affinity_required.yaml --- kind: Pod apiVersion: v1 metadata: name: affinity-required spec: containers: - name: test image: myos:httpd affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - key: disk operator: In values: - SSD [root@master 6.13]# kubectl apply -f 3.affinity_required.yaml pod/affinity-required unchanged [root@master 6.13]# kubectl get pods -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES affinity-required 0/1 Pending 0 44s <none> <none> <none> <none> [root@master 6.13]# kubectl label nodes node01 disk=SSD node/node01 labeled [root@master 6.13]# kubectl get pods -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES affinity-required 1/1 Running 0 3m2s 10.244.196.141 node01 <none> <none>
Pod Affinity(Pod 亲和性) 让 Pod 倾向于 与满足条件的其他 Pod 部署在同一个拓扑域 (如同一节点、同一可用区)。
典型场景
延迟敏感型应用 :例如前端 Pod 与缓存 Pod 部署在同一节点,减少网络延迟。
数据本地化 :计算 Pod 与数据存储 Pod 同节点,避免跨节点数据传输。
资源复用 :多个 Pod 共享本地资源(如 GPU)。
关键字段
labelSelector:选择目标 Pod 的标签(下面示例是 app=pod-1)。
topologyKey:定义“同一拓扑域”的粒度(如 kubernetes.io/hostname 表示同一节点)。
weight:仅用于软规则,优先级权重。
软亲和示例: ==和Node Affinity的软亲和的概念一样(如果有更好,没有就算了)==
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 [root@master 6.13]# vim 4.podAffinity_preferred.yaml --- kind: Pod apiVersion: v1 metadata: name: pod-affinity spec: containers: - name: test image: myos:httpd affinity: podAffinity: preferredDuringSchedulingIgnoredDuringExecution: - weight: 1 podAffinityTerm: labelSelector: matchExpressions: - key: app operator: In values: - pod-1 topologyKey: kubernetes.io/hostname [root@master 6.13]# kubectl run test --image=myos:httpd [root@master 6.13]# kubectl label pod test app=pod-1 [root@master 6.13]# kubectl get pods -o wide --show-labels NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES LABELS test 1/1 Running 0 17m 10.244.186.217 node03 <none> <none> app=pod-1,run=test [root@master 6.13]# kubectl apply -f 4.podAffinity_preferred.yaml [root@master 6.13]# kubectl get pods -o wide --show-labels NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES LABELS pod-affinity 1/1 Running 0 13m 10.244.186.218 node03 <none> <none> <none> test 1/1 Running 0 17m 10.244.186.217 node03 <none> <none> app=pod-1,run=test
硬亲和示例: ==和Node Affinity的软亲和的概念一样(必须要有,没有就不调度)==
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 [root@master 6.19]# vim pod_affinity.yaml --- kind: Pod apiVersion: v1 metadata: name: pod-affinity spec: containers: - name: test image: myos:httpd affinity: podAffinity: requiredDuringSchedulingIgnoredDuringExecution: - labelSelector: matchExpressions: - key: app operator: In values: - pod-1 topologyKey: kubernetes.io/hostname [root@master 6.19]# kubectl apply -f pod_affinity.yaml [root@master 6.19]# kubectl get pods NAME READY STATUS RESTARTS AGE pod-affinity 0/1 Pending 0 5s [root@master 6.19]# kubectl run test --image=myos:httpd [root@master 6.19]# kubectl label pods test app=pod-1 [root@master 6.19]# kubectl get pods -o wide --show-labels NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES LABELS pod-affinity 1/1 Running 0 84s 10.244.140.108 node02 <none> <none> <none> test 1/1 Running 0 48s 10.244.140.106 node02 <none> <none> app=pod-1,run=test
Pod AntiAffinity(Pod反亲和性) 确保 Pod 不 与特定 Pod 共存在同一节点或拓扑域,提高高可用性,避免资源竞争。
核心参数
requiredDuringScheduling... (硬性):必须满足,否则 Pod 无法调度。
preferredDuringScheduling... (软性):尽量满足,不保证。
labelSelector :匹配要避开哪些 Pod(通过标签)。
topologyKey :定义”同一位置”的范围(如 hostname=节点,zone=可用区)。
软反亲和示例: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 [root@master 6.19]# vim 2.pod_antiaffinity_ruan.yaml --- kind: Pod apiVersion: v1 metadata: name: pod-anti-affinity spec: containers: - name: test image: myos:httpd affinity: podAntiAffinity: preferredDuringSchedulingIgnoredDuringExecution: - weight: 1 podAffinityTerm: labelSelector: matchExpressions: - key: app operator: In values: - pod-2 topologyKey: kubernetes.io/hostname [root@master 6.19]# kubectl run test01 --image=myos:httpd --overrides='{"spec": {"nodeName": "node01"}}' --labels="app=pod-2" pod/test01 created [root@master 6.19]# kubectl run test02 --image=myos:httpd --overrides='{"spec": {"nodeName": "node02"}}' --labels="app=pod-2" pod/test02 created [root@master 6.19]# kubectl run test03 --image=myos:httpd --overrides='{"spec": {"nodeName": "node03"}}' --labels="app=pod-2" pod/test03 created [root@master 6.19]# kubectl apply -f 2.pod_antiaffinity_ruan.yaml pod/pod-anti-affinity created [root@master 6.19]# kubectl get pods -o wide --show-labels NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES LABELS pod-anti-affinity 1/1 Running 0 15s 10.244.140.114 node02 <none> <none> <none> test01 1/1 Running 0 102s 10.244.196.152 node01 <none> <none> app=pod-2 test02 1/1 Running 0 92s 10.244.140.112 node02 <none> <none> app=pod-2 test03 1/1 Running 0 85s 10.244.186.233 node03 <none> <none> app=pod-2
硬反亲和示例: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 [root@master 6.19]# vim 3.pod_antiaffinity_ying.yaml --- kind: Pod apiVersion: v1 metadata: name: pod-antiaffinity-ying spec: containers: - name: test image: myos:httpd affinity: podAntiAffinity: requiredDuringSchedulingIgnoredDuringExecution: - labelSelector: matchExpressions: - key: app operator: In values: - pod-2 topologyKey: kubernetes.io/hostname [root@master 6.19]# kubectl get pods -o wide --show-labels NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES LABELS test01 1/1 Running 0 3h7m 10.244.196.152 node01 <none> <none> app=pod-2 test02 1/1 Running 0 3h7m 10.244.140.112 node02 <none> <none> app=pod-2 test03 1/1 Running 0 3h7m 10.244.186.233 node03 <none> <none> app=pod-2 [root@master 6.19]# kubectl apply -f 3.pod_antiaffinity_ying.yaml [root@master 6.19]# kubectl get pods -o wide --show-labels NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES LABELS pod-antiaffinity-ying 0/1 Pending 0 23s <none> <none> <none> <none> <none> test01 1/1 Running 0 3h15m 10.244.196.152 node01 <none> <none> app=pod-2 test02 1/1 Running 0 3h15m 10.244.140.112 node02 <none> <none> app=pod-2 test03 1/1 Running 0 3h14m 10.244.186.233 node03 <none> <none> app=pod-2 [root@master 6.19]# kubectl label pods test01 app- [root@master 6.19]# kubectl get pods -o wide --show-labels NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES LABELS pod-antiaffinity-ying 1/1 Running 0 10m 10.244.196.144 node01 <none> <none> <none> test01 1/1 Running 0 3h24m 10.244.196.152 node01 <none> <none> <none> test02 1/1 Running 0 3h24m 10.244.140.112 node02 <none> <none> app=pod-2 test03 1/1 Running 0 3h24m 10.244.186.233 node03 <none> <none> app=pod-2
总结
调度策略
匹配标签
操作符
拓扑域支持
调度目标
nodeAffinity
node
In,Notin,Exists,DoesNotExist,Gt,Lt
否
指定主机
podAffinity
Pod
In,Notin,Exists,DoesNotExist
是
Pod与指定Pod同一拓扑域
podAnitAffinity
Pod
In,Notin,Exists,DoesNotExist
是
Pod与指定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:污点标签-
管理污点标签示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 [root@master ~]# kubectl taint node node01 k=v:NoExecute node/node01 tainted [root@master ~]# kubectl taint node node02 k=v:PreferNoSchedule node/node02 tainted [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,这种无视污点标签的调度方式称为容忍,当节点被设置多个污点(Taints)时,Pod 必须满足**所有污点的容忍(Tolerations)**才能被调度到该节点
示例:
1 2 3 4 5 6 7 8 9 spec: tolerations: - operator: "Equal" key: "kl" value: "vl" effect:"NoSchedule" containers: ......
示例二:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 [root@master ~]# vim myphp.yaml ...... spec: tolerations: - operator: Equal key: k value: v1 effect: NoSchedule containers: ...... [root@master ~]# vim myphp.yaml ...... spec: tolerations: - operator: Exists key: k effect: NoSchedule containers: ...... [root@master ~]# vim myphp.yaml ...... spec: tolerations: - operator: Exists key: k effect: "" containers: ...... [root@master ~]# vim myphp.yaml ...... spec: tolerations: - operator: Exists containers: ......
2.11.4 抢占与优先级 优先级是什么?
优先级表示一个Pod相对于其他Pod的重要性。
优先级有什么用?
优先级可以保证重要的Pod被调度运行
如何使用优先级和抢占
配置优先级类PriorityClass
创建Pod时为其设置对应的优先级
PriorityClass简介
PriorityClass是一个全局资源对象,它定义了从优先级类名称到优先级整数值的映射。在优先级类中有两个重要选项,分别是 value 和 preemptionPolicy。
value是一个整数值,值越大,优先级越高,取值范围是0到1000000000之间。
preemptionPolicy 表示在资源不足时候的行为,在队列中等待或者直接抢夺低优先级应用的资源
优先级策略:
描述信息与默认优先级
在PriorityClass中还有两个可选字段,是description和 globalDefault,description用来配置描述性信息,告诉用户优先级的用途,globaIDefault用于设置默认优先级状态,如果没有任何优先级设置Pod 的优先级为0
示例:
创建优先级类和pod设置优先级
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 [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 [root@master ~]# vim php1.yaml ...... spec: nodeSelector: kubernetes.io/hostname: node03 containers: - name: php image: myos:php-fpm resources: requests: cpu: "1200m" [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" [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"
验证非抢占优先:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 [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 [root@master ~]# kubectl delete pod php2 php3 pod "php2" deleted pod "php3" deleted
验证抢占优先:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 [root@master ~]# sed 's,-non,,' -i php?.yaml [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 [root@master ~]# kubectl apply -f php3.yaml pod/php3 created [root@master ~]# kubectl get pods NAME READY STATUS RESTARTS AGE php3 1/1 Running 0 9s [root@master ~]# kubectl apply -f php2.yaml pod/php2 created [root@master ~]# kubectl get pods NAME READY STATUS RESTARTS AGE php2 0/1 Pending 0 3s php3 1/1 Running 0 9s [root@master ~]# kubectl delete pod --all
2.12 用户认证&RBAC授权 2.12.1 用户认证
所有 Kubernetes 集群都有两类用户:由 Kubernetes 管理的服务账户和普通用户。
服务账户是给运行在集群中的 Pod 或其他工作负载使用的身份,用于与 Kubernetes API Server 进行交互。例如,Pod 中的应用程序可能需要通过服务账户读取 ConfigMap、访问其他资源或与 API Server 通信。
普通账户是给集群外部的用户或系统使用的身份,例如管理员、开发者或外部服务(如通过 kubectl 操作集群的用户)。
创建服务账户示例: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 [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 分配权限
1. 创建普通用户证书的配置文件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 [root@master pki]# pwd /etc/kubernetes/pki [root@master pki]# vim devuser.json { "CN" : "devuser" , // Common Name,即用户名,这里设置为devuser "hosts" : [], // 证书可用于哪些主机,空表示不限制主机 "key" : { // 密钥配置 "algo" : "rsa" , // 使用RSA算法 "size" : 2048 // 密钥长度2048位 }, "names" : [ // 证书的详细信息 { "C" : "CN" , // 国家(Country)-中国 "ST" : "BeiJing" , // 州/省(State)-北京 "L" : "BeiJing" , // 城市(Locality)-北京 "O" : "k8s" , // 组织(Organization)-k8s "OU" : "System" // 组织单位(Organizational Unit)-System } ] }
2. 下载证书生成工具 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 wget https://pkg.cfssl.org/R1.2/cfssl_linux-amd64 mv cfssl_linux-amd64 /usr/local/bin/cfsslwget https://pkg.cfssl.org/R1.2/cfssljson_linux-amd64 mv cfssljson_linux-amd64 /usr/local/bin/cfssljsonwget https://pkg.cfssl.org/R1.2/cfssl-certinfo_linux-amd64 mv cfssl-certinfo_linux-amd64 /usr/local/bin/cfssl-certinfocfssl gencert \ -ca=ca.crt \ -ca-key=ca.key \ -profile=kubernetes \ devuser.json \ | cfssljson -bare devuser
3. 设置集群参数 1 2 3 4 5 6 export KUBE_APISERVER="https://192.168.10.100:6443" kubectl config set-cluster kubernetes \ --certificate-authority=ca.crt \ --embed-certs=true \ --server=${KUBE_APISERVER} \ --kubeconfig=devuser.kubeconfig
4. 设置客户端认证参数 1 2 3 4 5 kubectl config set-credentials devuser \ --client-certificate=devuser.pem \ --client-key=devuser-key.pem \ --embed-certs=true \ --kubeconfig=devuser.kubeconfig
5. 设置上下文参数 1 2 3 4 5 kubectl config set-context devuser@kubernetes \ --cluster=kubernetes \ --user=devuser \ --namespace=dev \ --kubeconfig=devuser.kubeconfig
6. 使用上下文 1 2 3 4 5 6 7 8 kubectl create ns dev kubectl create rolebinding devuser-admin-binding --clusterrole=admin --user=devuser --namespace=dev kubectl config use-context devuser@kubernetes --kubeconfig=devuser.kubeconfig cp -f ./devuser.kubeconfig /root/.kube/config
7. 验证普通用户 1 2 3 4 5 6 7 [root@master ~]# kubectl get pods No resources found in dev namespace. [root@master ~]# kubectl get pods -n default Error from server (Forbidden): pods is forbidden: User "devuser" cannot list resource "pods" in API group "" in the namespace "default"
8. 给系统用户创建集群普通用户 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 useradd dve passwd dev mkdir /home/dev/.kubechown -R dev.dev /home/dev/.kubecp /etc/kubernetes/pki/devuser.kubeconfig /home/dev/.kube/configsu - dev [dev@master ~]$ id uid=1001(dev) gid=1001(dev) 组=1001(dev) [dev@master ~]$ kubectl get pods No resources found in dev namespace. [dev@master ~]$ kubectl get pods -n default Error from server (Forbidden): pods is forbidden: User "devuser" cannot list resource "pods" in API group "" in the namespace "default"
角色与授权 如果想访问和管理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)
[!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 资源的访问权限。
自定义角色和角色绑定示例: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 [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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 [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
2.13 Helm包管理工具 2.13.1 Helm的简介 在没使用 helm 之前,向 kubernetes 部署应用,我们要依次部署 deployment、svc 等,步骤较繁琐。 况且随着很多项目微服务化,复杂的应用在容器中部署以及管理显得较为复杂,helm 通过打包的方式,支持发布的版本管理和控制, 很大程度上简化了 Kubernetes 应用的部署和管理。
==Helm 本质就是让 K8s 的应用管理(Deployment、Service 等)可配置,可以通过类似于传递环境变量的方式能动态生成。==通过动态生成 K8s 资源清单文件(deployment.yaml、service.yaml)。然后调用 Kubectl 自动执行 K8s 资源部署。
Helm 是官方提供的类似于 YUM 的包管理器,是部署环境的流程封装。
2.13.2 Helm 中三个重要的概念
**Chart:**Helm 的软件包,采用 tar 格式。类似于 APT 的 DEB 包或者 YUM 的 RPM 包,其包含了一组定义 Kubernetes 资源相关的 YAML 文件。
**Repository(仓库):**Helm 的软件仓库,Repository 本质上是一个 Web 服务器,该服务器保存了一系列的 Chart 软件包以供用户下载,并且提供了一个该 Repository 的 Chart 包的清单文件以供查询。Helm 可以同时管理多个不同的 Repository。
**Release:**使用 helm install 命令在 Kubernetes 集群中部署的 Chart 称为 Release。可以理解为 Helm 使用 Chart 包部署的一个应用实例。一个 chart 通常可以在同一个集群中安装多次。每一次安装都会创建一个新的 release
以 MySQL chart 为例,如果你想在你的集群中运行两个数据库,你可以安装该 chart 两次。每一个数据库都会拥有它自己的 release 和 release name。可以将 release 想象成应用程序发布的版本号。
2.13.3 Helm3 与 Helm2 的区别 Helmv2是C/S 架构,主要分为客户端helm 和服务器端tiller。而由于 RBAC 等权限控制体系的逐渐完善,多租户和安全的需求日益兴起,tiller变得越来越不安全,社区在权限控制领域遇到了极大的阻碍。所以在Helm3 版本中,直接将tiller 这一核心组件移除,helm 直接和kubernetes API 进行通信。直接带来的好处如下:
Helm的架构变的更为简单和灵活
不再需要创建ServiceAccount,直接使用当前环境中的kubeconfig配置
可以直接和kubernetesAPl交互,更为安全
不再需要使用helminit来进行初始化
2.13.4 Helm常用命令
功能分类
命令示例
使用场景
核心参数
基础操作
helm install [RELEASE] [CHART]
部署应用到 K8s 集群(如 helm install myapp bitnami/nginx)
-f values.yaml:自定义配置文件 --set key=value:临时覆盖值 --dry-run:模拟安装不执行
helm upgrade [RELEASE] [CHART]
升级应用版本或配置(如 helm upgrade myapp bitnami/nginx --version 15.3.1)
--version x.y.z:指定 Chart 版本 --reset-values:重置非默认配置值
helm uninstall [RELEASE]
卸载应用(如 helm uninstall myapp -n prod)
--keep-history:保留历史记录 -n NAMESPACE:指定命名空间
Release 管理
helm list
查看已部署的应用列表(如 helm ls -a -n dev)
-a/--all:显示所有状态的 Release -n NAMESPACE:指定命名空间
helm status [RELEASE]
查看应用详细状态(如 helm status myapp -o yaml)
-o json/yaml:输出 JSON/YAML 格式
helm history [RELEASE]
查看应用升级 / 回滚历史(如 helm history myapp --max=5)
--max=10:显示最近 10 次历史
helm rollback [RELEASE] [REVISION]
回滚到指定版本(如 helm rollback myapp 2)
Chart 操作
helm repo add [NAME] [URL]
添加 Chart 仓库(如 helm repo add bitnami https://charts.bitnami.com/bitnami)
--username/--password:认证信息 --force-update:强制更新索引
helm repo update
更新本地仓库索引(获取最新 Charts 列表)
helm search repo [KEYWORD]
搜索仓库中的 Charts(如 helm search repo nginx --versions)
--version x.y.z:指定版本过滤 -l/--versions:显示所有版本
helm pull [CHART]
下载 Chart 到本地(如 helm pull bitnami/nginx --untar)
--untar:解压到当前目录 --destination /path:指定下载路径
配置与调试
helm show values [CHART]
查看 Chart 默认配置(如 helm show values bitnami/nginx)
helm template [RELEASE] [CHART]
本地渲染模板(不部署),用于调试 YAML(如 helm template myapp bitnami/nginx)
-f values.yaml:自定义配置 --show-only templates/deployment.yaml:仅显示特定模板
helm lint [CHART_DIR]
检查 Chart 语法和结构(如 helm lint ./mychart)
插件扩展
helm plugin install [URL]
安装插件(如 helm plugin install https://github.com/databus23/helm-diff)
helm plugin list
列出已安装插件
2.13.5 集群部署Helm & 实操 安装Helm 1 2 3 curl -fsSL -o get_helm.sh https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 chmod 700 get_helm.sh./get_helm.sh
添加chart仓库 当您已经安装好了Helm之后,您可以添加一个chart 仓库。
1 2 3 4 5 6 7 8 9 10 11 helm repo add bitnami https://charts.bitnami.com/bitnami helm search repo bitnami helm repo update helm repo ls helm repo remove <仓库名称>
安装chart示例 1 2 3 4 5 6 7 8 helm search repo apache helm install web bitnami/apache helm install bitnami/apache --generate-name helm list helm show chart bitnami/apache
卸载chart示例 helm v2 版本中,当一个 release 被删除,会保留一条删除记录。而在 Helm 3 中,删除也会移除 release 的记录。 如果你想保留删除记录,使用 helm uninstall --keep-history 。使用 helm list --uninstalled 只会展示使用了 --keep-history 删除的 releasehelm list --all 会展示 Helm 保留的所有 release 记录,包括失败或删除的条目(指定了 --keep-history )
1 2 3 4 5 helm uninstall web --keep-history helm status web
安装前自定义chart 安装过程中有两种方式传递配置数据
--values (或 -f ):使用 YAML 文件覆盖配置。可以指定多次,优先使用最右边的文件
--set:通过命令行的方式对指定项进行覆盖
如果同时使用两种方式,则--set 中的值会被合并到 --values 中,但是 --set 中的值优先级更高。在 --set 中覆盖的内容会被被保存在 ConfigMap 中。可以通过 helm get values <release-name> 来查看指定 release 中 --set 设置的值。也可以通过运行 helm upgrade 并指定 --reset-values 字段来清除 --set 中设置的值
1 2 3 4 5 6 7 8 9 10 11 helm show values bitnami/apache vim values.yaml service: type : NodePort helm install web -f values.yaml bitnami/apache kubectl get svc
下载chart包到本地 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 [root@master 7.2]# helm pull bitnami/apache [root@master 7.2]# ls apache-11.3.18.tgz [root@master 7.2]# tar xf apache-11.3.18.tgz [root@master 7.2]# ls apache apache-11.3.18.tgz [root@master 7.2]# ls apache Chart.lock Chart.yaml README.md values.schema.json charts files templates values.yaml [root@master 7.2]# vim 1.values.yaml image: registry: harbor:443 repository: library/apache tag: 2.4.63 global: security: allowInsecureImages: true service: type : ClusterIP [root@master 7.2]# helm install test -f 1.values.yaml apache/.
helm upgrade升级 当你想升级到 chart 的新版本,或是修改 release 的配置,你可以使用 helm upgrade 命令。Helm 会尝试执行最小侵入式升级。即它只会更新自上次发布以来发生了更改的内容
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 [root@master 7.2]# vim 1.values.yaml image: registry: harbor:443 repository: library/apache tag: 2.4.63 global: security: allowInsecureImages: true service: type : ClusterIP [root@master 7.2]# helm install web -f 1.values.yaml bitnami/apache [root@master 7.2]# kubectl get svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kubernetes ClusterIP 10.245.0.1 <none> 443/TCP 63d web-apache ClusterIP 10.245.134.139 <none> 80/TCP,443/TCP 50s [root@master 7.2]# vim 2.values.yaml service: type : NodePort [root@master 7.2]# helm upgrade -f 2.values.yaml web bitnami/apache [root@master 7.2]# kubectl get svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kubernetes ClusterIP 10.245.0.1 <none> 443/TCP 63d web-apache NodePort 10.245.134.139 <none> 80:31221/TCP,443:32027/TCP 2m [root@master 7.2]# helm get values web USER-SUPPLIED VALUES: service: type : NodePort
helm rollback回滚 现在,假如在一次发布过程中,发生了不符合预期的事情,也很容易通过 helm rollback [RELEASE] [REVISION]命令回滚到之前的发布版本 release 版本其实是一个增量修订(revision)。 每当发生了一次安装、升级或回滚操作,revision 的值就会加1。第一次 revision 的值永远是1。我们可以使用 helm history [RELEASE] 命令来查看一个特定 release 的修订版本号
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 [root@master 7.2]# helm history web REVISION UPDATED STATUS CHART APP VERSION DESCRIPTION 1 Wed Jul 2 16:05:50 2025 superseded apache-11.3.182.4.63 Install complete 2 Wed Jul 2 16:07:39 2025 deployed apache-11.3.182.4.63 Upgrade complete [root@master 7.2]# helm rollback web 1 Rollback was a success! Happy Helming! [root@master 7.2]# kubectl get svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kubernetes ClusterIP 10.245.0.1 <none> 443/TCP 63d web-apache ClusterIP 10.245.134.139 <none> 80/TCP,443/TCP 93m [root@master 7.2]# helm history web REVISION UPDATED STATUS CHART APP VERSION DESCRIPTION 1 Wed Jul 2 16:05:50 2025 superseded apache-11.3.18 2.4.63 Install complete 2 Wed Jul 2 16:07:39 2025 superseded apache-11.3.18 2.4.63 Upgrade complete 3 Wed Jul 2 17:39:17 2025 deployed apache-11.3.18 2.4.63 Rollback to 1
创建一个自己的chart 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 [root@master 7.2]# helm create mychart Creating mychart [root@master 7.2]# ls 1.values.yaml 2.values.yaml apache apache-11.3.18.tgz mychart [root@master 7.2]# tree mychart/ mychart/ ├── charts ├── Chart.yaml ├── templates │ ├── deployment.yaml │ ├── _helpers.tpl │ ├── hpa.yaml │ ├── ingress.yaml │ ├── NOTES.txt │ ├── serviceaccount.yaml │ ├── service.yaml │ └── tests │ └── test-connection.yaml └── values.yaml 3 directories, 10 files [root@master 7.2]# cd mychart/ [root@master mychart]# rm -fr values.yaml templates/* ======================================================= [root@master mychart]# vim templates/NOTES.txt 1、这是一个测试的 myapp chart 2、myapp release 名字:myapp-test-{{ now | date "20060102030405" }}-deploy 3、service 名字:myapp-test-{{ now | date "20060102030405" }}-svc ======================================================= [root@master mychart]# vim templates/svc.yaml apiVersion: v1 kind: Service metadata: labels: app: mychart name: mychart-test-{{ now | date "20060102030405" }}-svc spec: ports: - name: 80-80 port: 80 protocol: TCP targetPort: 80 {{- if eq .Values.service.type "NodePort" }} nodePort: {{ .Values.service.nodeport }} {{- end }} selector: app: mychart type : {{ .Values.service.type | quote }} ======================================================= [root@master mychart]# vim templates/deployment.yaml apiVersion: apps/v1 kind: Deployment metadata: labels: app: mychart name: mychart-test-{{ now | date "20060102030405" }}-deploy spec: replicas: {{.Values.replicaCount}} selector: matchLabels: app: my-dep template: metadata: labels: app: my-dep spec: containers: - image: {{ .Values.image.repository }}:{{ .Values.image.tag }} name: mychart ======================================================= [root@master mychart]# vim values.yaml replicaCount: 5 image: repository: library/myos tag: httpd service: type : NodePort nodeport: 31111 ======================================================== [root@master mychart]# helm install mychart -f values.yaml ../mychart/ NAME: mychart LAST DEPLOYED: Wed Jul 2 21:58:59 2025 NAMESPACE: default STATUS: deployed REVISION: 1 TEST SUITE: None NOTES: 1、这是一个测试的 myapp chart 2、myapp release 名字:myapp-test-20250702095859-deploy 3、service 名字:myapp-test-20250702095859-svc [root@master mychart]# helm ls NAME NAMESPACE REVISION UPDATED STATUS CHART APP VERSION mychart default 1 2025-07-02 21:58:59.099379134 +0800 CSdeployed mychart-0.1.0 1.16.0 test default 1 2025-07-02 18:16:54.973706955 +0800 CSdeployed apache-11.3.18 2.4.63 web default 3 2025-07-02 17:39:17.204102761 +0800 CSdeployed apache-11.3.18 2.4.63 [root@master mychart]# kubectl get svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kubernetes ClusterIP 10.245.0.1 <none> 443/TCP 63d mychart-test-20250702095859-svc NodePort 10.245.144.95 <none> 80:31111/TCP 20s test-apache ClusterIP 10.245.228.226 <none> 80/TCP,443/TCP 3h42m web-apache ClusterIP 10.245.134.139 <none> 80/TCP,443/TCP 5h53m [root@master mychart]# kubectl get deploy NAME READY UP-TO-DATE AVAILABLE AGE mychart-test-20250702095859-deploy 5/5 5 5 33s test-apache 1/1 1 1 3h42m web-apache 1/1 1 1 5h53m [root@master mychart]# kubectl get pods NAME READY STATUS RESTARTS AGE mychart-test-20250702095859-deploy-7f4bf7d6bf-4wz8h 1/1 Running 0 38s mychart-test-20250702095859-deploy-7f4bf7d6bf-fh4wz 1/1 Running 0 38s mychart-test-20250702095859-deploy-7f4bf7d6bf-j7thf 1/1 Running 0 38s mychart-test-20250702095859-deploy-7f4bf7d6bf-ktzp9 1/1 Running 0 38s mychart-test-20250702095859-deploy-7f4bf7d6bf-qw8z7 1/1 Running 0 38s test-apache-5646477d78-r8szb 1/1 Running 1 (77m ago) 3h42m web-apache-7d8c6d8cbb-dllfl 1/1 Running 1 (77m ago) 5h53m
2.14 Ingress-nginx 2.14.1 ingress-nginx是什么? 它是 Kubernetes 的官方入口网关,用 NGINX 实现七层(HTTP/HTTPS)流量管理,==自动将域名和路径路由到集群内不同服务。==
核心功能
统一入口
通过单个IP+端口(80/443)暴露所有HTTP服务,替代多个NodePort/LoadBalancer。
智能路由
根据 域名 (Host)和 路径 (Path)转发请求到后端Service。
自动化
监听K8s API,动态更新NGINX配置,无需手动重启。
解决了什么问题?
痛点 :直接用Service的NodePort或LoadBalancer时:
❌ 无法基于域名路由
❌ 需维护多个IP/端口
❌ 手动管理TLS证书
方案 :Ingress-NGINX 通过声明式配置(YAML)一键搞定。
2.14.2 为什么引入Ingress? 我们说k8s 的服务(service)时,说暴露了service的三种方式ClusterIP、NodePort与LoadBalance,这几种方式都是在service的维度提供的,service的作用体现在两个方面,对集群内部,它不断跟踪pod的变化,更新endpoint中对应pod的对象,提供了ip不断变化的pod的服务发现机制,对集群外部,他类似负载均衡器,可以在集群内外部对pod进行访问。但是,单独用service暴露服务的方式,在实际生产环境中不太合适:
所幸k8s还提供了一种集群维度暴露服务的方式,也就是ingress。==ingress可以简单理解为service的service,他通过独立的ingress对象来制定请求转发的规则,把请求路由到一个或多个service中。这样就把服务与请求规则解耦了,可以从业务维度统一考虑业务的暴露,而不用为每个service单独考虑。==
service通过iptables、ipvs实现。有缺陷:每一组业务都要开启nodePort,这样nodePort越来越多越来越难管理
解决方案:多加一层管理庞大的service
本来nodePort是映射到Service的,现在在往上抽一层,映射到上层,由上层再分发到service,此方案Ingress实现了
而Ingress是K8S中的资源,接口,可以由不同的控制器实现
Ingress-nginx是其中的一种实现,是对nginx的二次开发,来满足Ingress的规范ingress-nginx官方讲解
2.14.3 基于Helm部署ingress-nginx 官方地址: https://github.com/kubernetes/ingress-nginx
确保自己的harbor仓库有这两个镜像,可以在国内的渡渡鸟镜像网站 里下载
控制器镜像(controller) :Ingress-Nginx 的核心组件,负责监听 Kubernetes API 并根据 Ingress 规则配置负载均衡器。它处理所有进入集群的 HTTP/HTTPS 流量,并将请求路由到对应的 Service。
kube-webhook-certgen 镜像(可选) :用于自动生成和管理 Ingress-Nginx 的 admission webhook 所需的 TLS 证书。如果启用了 webhook 验证功能(默认启用),则需要此镜像。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 [root@master 7.6]# helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx [root@master 7.6]# helm pull ingress-nginx/ingress-nginx [root@master 7.6]# ls ingress-nginx-4.12.3.tgz [root@master 7.6]# tar -xf ingress-nginx-4.12.3.tgz [root@master 7.6]# ls ingress-nginx ingress-nginx-4.12.3.tgz 1- 修改global.image.registry=harbor:443 2- 修改image的拉取地方为harbor仓库里的镜像,tag也是 3- 修改 hostNetwork 的值为 true ,共享宿主机网络 4- dnsPolicy的值改为: clusterFirstWithHostNet 5- kind类型更改为:DaemonSet,启冗余作用 6- 关闭所有镜像的 digest 7- ingressClassResource.default=true [root@master 7.6]# kubectl create ns ingress [root@master 7.6]# helm install ingress-nginx -f ingress-nginx/values.yaml -n ingress ingress-nginx/ [root@master 7.6]# kubectl get pods -n ingress -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES ingress-nginx-controller-8lsvd 1/1 Running 0 3m37s 192.168.10.12 node02 <none> <none> ingress-nginx-controller-hhzmz 1/1 Running 0 3m37s 192.168.10.13 node03 <none> <none> ingress-nginx-controller-lqxlj 1/1 Running 0 3m37s 192.168.10.11 node01 <none>
2.14.4 Ingress HTTP 代理访问 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 [root@master 7.6]# vim 1.ingress_http.yaml --- kind: Deployment apiVersion: apps/v1 metadata: name: ingress-http-www1 spec: replicas: 2 selector: matchLabels: hostname: www1 template: metadata: labels: hostname: www1 spec: containers: - name: web image: myos:httpd ports: - containerPort: 80 --- kind: Service apiVersion: v1 metadata: name: ingress-http-www1 spec: ports: - port: 80 targetPort: 80 selector: hostname: www1 --- kind: Ingress apiVersion: networking.k8s.io/v1 metadata: name: ingress-http-www1 spec: ingressClassName: nginx rules: - host: www1.zkbr.com http: paths: - path: / pathType: Prefix backend: service: name: ingress-http-www1 port: number: 80 [root@master 7.6]# kubectl apply -f 1.ingress_http.yaml [root@master 7.6]# kubectl get pods NAME READY STATUS RESTARTS AGE ingress-http-www1-c8bd7b98-6pptq 1/1 Running 0 5s ingress-http-www1-c8bd7b98-r9zg4 1/1 Running 0 6s [root@master 7.6]# kubectl get svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE ingress-http-www1 ClusterIP 10.245.44.237 <none> 80/TCP 15s kubernetes ClusterIP 10.245.0.1 <none> 443/TCP 67d [root@master 7.6]# kubectl get ingress NAME CLASS HOSTS ADDRESS PORTS AGE ingress-http-www1 nginx www1.zkbr.com 80 19s
测试环境要配置host域名解析
访问域名,ingress会根据定义的规则来调用后端的svc响应
1 2 用户 → DNS 解析 → 宿主机 IP:80/443(直接绑定 ingress-nginx-controller Pod) → Nginx 匹配 Ingress 规则 → 转发到后端 Service → 后端 Pod → 响应返回给用户
基于ingress-nginx创建多虚拟主机的访问 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 [root@master 7.6]# vim 2.ingress_http.yaml --- kind: Deployment apiVersion: apps/v1 metadata: name: ingress-http-www2 spec: replicas: 2 selector: matchLabels: hostname: www2 template: metadata: labels: hostname: www2 spec: containers: - name: web image: myos:httpd ports: - containerPort: 80 --- kind: Service apiVersion: v1 metadata: name: ingress-http-www2 spec: ports: - port: 80 targetPort: 80 selector: hostname: www2 --- kind: Ingress apiVersion: networking.k8s.io/v1 metadata: name: ingress-http-www2 spec: ingressClassName: nginx rules: - host: www2.zkbr.com http: paths: - path: / pathType: Prefix backend: service: name: ingress-http-www2 port: number: 80 [root@master 7.6]# kubectl apply -f 2.ingress_http.yaml [root@master 7.6]# kubectl get pods NAME READY STATUS RESTARTS AGE ingress-http-www1-c8bd7b98-6pptq 1/1 Running 0 30m ingress-http-www1-c8bd7b98-r9zg4 1/1 Running 0 30m ingress-http-www2-786b6bcd67-h2gnx 1/1 Running 0 10s ingress-http-www2-786b6bcd67-jxmpj 1/1 Running 0 10s [root@master 7.6]# kubectl get svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE ingress-http-www1 ClusterIP 10.245.44.237 <none> 80/TCP 34m ingress-http-www2 ClusterIP 10.245.60.88 <none> 80/TCP 3m50s kubernetes ClusterIP 10.245.0.1 <none> 443/TCP 67d [root@master 7.6]# kubectl get ingress NAME CLASS HOSTS ADDRESS PORTS AGE ingress-http-www1 nginx www1.zkbr.com 80 34m ingress-http-www2 nginx www2.zkbr.com 80 3m54s
添加解析记录
访问www2.zkbr.com/info.php
2.14.5 Ingress HTTPS 代理访问 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 [root@master 7.7]# openssl req -x509 -sha256 -nodes -days 365 -newkey rsa:2048 -keyout tls.key -out tls.crt -subj "/CN=nginxsvc/O=nginxsvc" Generating a RSA private key ..............+++++ ...................................+++++ writing new private key to 'tls.key' ----- [root@master 7.7]# ls tls.crt tls.key [root@master 7.7]# kubectl create secret tls ingress-nginx-tls --key tls.key --cert tls.crt [root@master 7.7]# vim 1.ingress-nginx-https.yaml apiVersion: apps/v1 kind: Deployment metadata: name: ingress-http-ssl spec: replicas: 2 selector: matchLabels: app: ingress-http-ssl template: metadata: labels: app: ingress-http-ssl spec: containers: - image: myos:httpd name: myos --- apiVersion: v1 kind: Service metadata: name: ingress-http-ssl spec: ports: - port: 80 targetPort: 80 selector: app: ingress-http-ssl --- kind: Ingress apiVersion: networking.k8s.io/v1 metadata: name: ingress-http-ssl annotations: nginx.ingress.kubernetes.io/ssl-redirect: "true" spec: ingressClassName: nginx tls: - hosts: - ssl.zkbr.com secretName: ingress-nginx-tls rules: - host: ssl.zkbr.com http: paths: - path: / pathType: Prefix backend: service: name: ingress-http-ssl port: number: 80 [root@master 7.7]# kubectl apply -f 1.ingress-nginx-https.yaml [root@master 7.7]# kubectl get pods NAME READY STATUS RESTARTS AGE ingress-http-ssl-678cf7dcf6-29p2b 1/1 Running 0 6m6s ingress-http-ssl-678cf7dcf6-wc87h 1/1 Running 0 6m6s ingress-http-www1-c8bd7b98-6pptq 1/1 Running 1 (12h ago) 24h ingress-http-www1-c8bd7b98-r9zg4 1/1 Running 1 (12h ago) 24h ingress-http-www2-786b6bcd67-h2gnx 1/1 Running 1 (12h ago) 24h ingress-http-www2-786b6bcd67-jxmpj 1/1 Running 1 (12h ago) 24h [root@master 7.7]# kubectl get svc,ingress NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE service/ingress-http-ssl ClusterIP 10.245.100.178 <none> 80/TCP 6m29s service/ingress-http-www1 ClusterIP 10.245.44.237 <none> 80/TCP 24h service/ingress-http-www2 ClusterIP 10.245.60.88 <none> 80/TCP 24h service/kubernetes ClusterIP 10.245.0.1 <none> 443/TCP 68d NAME CLASS HOSTS ADDRESS PORTS AGE ingress.networking.k8s.io/ingress-http-ssl nginx ssl.zkbr.com 80, 443 6m29s ingress.networking.k8s.io/ingress-http-www1 nginx www1.zkbr.com 80 24h ingress.networking.k8s.io/ingress-http-www2 nginx www2.zkbr.com 80 24h
测试环境添加域名解析记录
2.14.6 Ingress basicAuth 代理 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 [root@master 7.7]# dnf -y install httpd-tools [root@master 7.7]# htpasswd -c auth hehe [root@master 7.7]# htpasswd auth lili [root@master 7.7]# cat auth hehe:$apr1$.qikKmFZ$9sbloDIxooQ20Phrd0 .pu/ lili:$apr1$H8rRr66e$6dDOrWsGERrQhcrDXpgt80 [root@master 7.7]# kubectl create secret generic ingress-basic-auth --from-file=auth [root@master 7.7]# vim 2.ingress-nginx-auth.yaml apiVersion: apps/v1 kind: Deployment metadata: name: ingress-http-auth spec: replicas: 2 selector: matchLabels: app: ingress-http-auth template: metadata: labels: app: ingress-http-auth spec: containers: - image: myos:httpd name: myos ports: - containerPort: 80 --- apiVersion: v1 kind: Service metadata: name: ingress-http-auth spec: ports: - port: 80 targetPort: 80 selector: app: ingress-http-auth --- apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: ingress-http-auth annotations: nginx.ingress.kubernetes.io/ssl-redirect: "true" nginx.ingress.kubernetes.io/auth-type: basic nginx.ingress.kubernetes.io/auth-secret: ingress-basic-auth nginx.ingress.kubernetes.io/auth-realm: 'Admin Area' spec: ingressClassName: nginx rules: - host: auth.zkbr.com http: paths: - path: / pathType: ImplementationSpecific backend: service: name: ingress-http-auth port: number: 80 tls: - hosts: - auth.zkbr.com secretName: ingress-nginx-tls [root@master 7.7]# kubectl apply -f 2.ingress-nginx-auth.yaml [root@master 7.7]# kubectl get ingress NAME CLASS HOSTS ADDRESS PORTS AGE ingress-http-auth nginx auth.zkbr.com 80, 443 6m59s ingress-http-ssl nginx ssl.zkbr.com 80, 443 44h ingress-http-www1 nginx www1.zkbr.com 80 2d20h ingress-http-www2 nginx www2.zkbr.com 80 2d20h
测试环境添加解析记录
2.14.7 Ingress-nginx 域名重定向 Redirect(重定向):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 [root@master 7.9]# vim 1.ingress-redirect.yaml --- kind: Ingress apiVersion: networking.k8s.io/v1 metadata: name: ingress-redirect annotations: nginx.ingress.kubernetes.io/permanent-redirect: https://www.jd.com spec: ingressClassName: nginx rules: - host: redirect.zkbr.com http:
测试效果
1 2 3 4 5 6 7 8 [root@master 7.9]# echo "192.168.10.13 redirect.zkbr.com" >> /etc/hosts [root@master 7.9]# curl -I redirect.zkbr.com HTTP/1.1 301 Moved Permanently Date: Wed, 09 Jul 2025 10:35:54 GMT Content-Type: text/html Content-Length: 162 Connection: keep-alive Location: https://www.jd.com
2.14.8 Ingress-nginx 地址重写 Rewrite (重写)
作用:重写是指修改请求的路径,但是客户端不会察觉到这个变化,它仅在服务器内部发生。在Kubernetes 中,可以通过Ingress 的注解来配置重写规则
示例:比如你有一个服务部署在/v1路径下,但是你希望用户访问时不需要输入/v1,那么你可以使用重写将请求从根路径/重写到/v1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 [root@master 7.9]# vim 2.ingress-rewrite.yaml apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: ingress-rewrite annotations: nginx.ingress.kubernetes.io/rewrite-target: /info.$1 nginx.ingress.kubernetes.io/use-regex: "true" spec: ingressClassName: nginx rules: - host: rewrite.zkbr.com http: paths: - path: /test.(.*) pathType: ImplementationSpecific backend: service: name: ingress-http-www1 port: number: 80
测试环境添加域名解析
2.14.9 Ingress-nginx 自定义错误页 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 [root@master 7.10]# vim 1.ingress-error.yaml --- kind: Deployment apiVersion: apps/v1 metadata: name: ingress-error spec: replicas: 2 selector: matchLabels: name: error template: metadata: labels: name: error spec: containers: - name: test image: library/errorweb:v1.0 ports: - containerPort: 80 --- kind: Service apiVersion: v1 metadata: name: ingress-error spec: selector: name: error ports: - port: 80 targetPort: 80 --- kind: Ingress apiVersion: networking.k8s.io/v1 metadata: name: ingress-error annotations: nginx.ingress.kubernetes.io/custom-http-errors: "404,500" nginx.ingress.kubernetes.io/default-backend: "ingress-error" spec: rules: - host: error.zkbr.com http: paths: - path: / pathType: Prefix backend: service: name: ingress-http-www1 port: number: 80 [root@master 7.10]# kubectl apppl -f 1.ingress-error.yaml [root@master 7.10]# kubectl get ingress NAME CLASS HOSTS ADDRESS PORTS AGE ingress-error nginx error.zkbr.com 80 15m ingress-http-auth nginx auth.zkbr.com 80, 443 28h ingress-http-ssl nginx ssl.zkbr.com 80, 443 3d ingress-http-www1 nginx www1.zkbr.com 80 4d1h ingress-http-www2 nginx www2.zkbr.com 80 4d ingress-redirect nginx redirect.zkbr.com 80 27h ingress-rewrite nginx rewrite.zkbr.com 80 26h
测试环境添加域名解析记录
2.14.10 Ingress-nginx snippet 自定义配置片段 在 Ingress-nginx 中,snippet 通常指的是通过注解(Annotations)向 Nginx 配置注入的自定义配置片段 。下面从其作用、使用方式、优势与风险等方面详细介绍:
作用 Ingress-nginx 作为 Kubernetes 中常用的 Ingress 控制器,负责将外部流量路由到集群内的服务上。尽管它提供了丰富的默认配置和参数,但在一些复杂场景下,默认配置无法满足需求。这时,snippet 就派上用场了,它允许用户灵活地自定义 Nginx 的配置 ,从而实现更精细的流量控制、安全策略、性能优化等功能 。例如:
自定义重定向 :根据请求的来源、客户端类型等条件,设置特定的重定向规则。
添加自定义 HTTP 头 :在响应中添加自定义的 HTTP 头部信息,用于实现特定的业务逻辑或安全策略。
配置缓存策略 :对某些资源设置缓存规则,提高响应速度。
优势与风险
优势 :极大地增强了 Ingress-nginx 的灵活性和扩展性,能满足多样化的业务需求,而无需修改 Ingress-nginx 的代码或重新部署。
风险 :直接修改 Nginx 配置存在一定的安全风险,如果配置不当,可能会导致服务不可用、安全漏洞等问题。因此,默认情况下,Ingress-nginx 禁止通过注解注入配置片段,需要在 Ingress-nginx 的 ConfigMap 中设置 allow-snippet-annotations: "true" 来开启该功能,并且在使用时需要严格测试和审核配置内容 。
开启注解注入配置片段的权限 当allow-snippet-annotations参数设为 true 时,用户可以在 Ingress 资源的 annotations 中使用以下特殊注解,直接注入 Nginx 配置片段:
nginx.ingress.kubernetes.io/configuration-snippet:注入到 Nginx 服务器(server)块的配置中(适用于路由级别的自定义,如重定向、缓存策略等)。
nginx.ingress.kubernetes.io/server-snippet:注入到 Nginx 全局 http 块的配置中(适用于全局级别的自定义,如日志格式、全局变量等)。
nginx.ingress.kubernetes.io/location-snippet:注入到 Nginx location 块的配置中(适用于特定路径的细节配置)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 [root@master 8.10]# kubectl edit -n ingress cm ingress-nginx-controller data: allow-snippet-annotations: "true" [root@master 8.10]# vim snippet.yaml apiVersion: apps/v1 kind: Deployment metadata: name: snippet spec: replicas: 1 selector: matchLabels: app: snippet template: metadata: labels: app: snippet spec: containers: - name: test image: myos:httpd --- kind: Service apiVersion: v1 metadata: name: snippet spec: ports: - name: 80-80 port: 80 targetPort: 80 protocol: TCP selector: app: snippet type : ClusterIP --- kind: Ingress apiVersion: networking.k8s.io/v1 metadata: name: snippet.zkbr.com annotations: nginx.ingress.kubernetes.io/server-snippet: | set $agentflag 0; if ($http_user_agent ~* "(Firefox)" ) { set $agentflag 1; } if ($agentflag = 1){ return 302 http://www.baidu.com; break ; } if ($http_user_agent ~* "(chrome)" ) { set $agentflag 2; } if ($agentflag = 2){ return 302 http://www.jd.com; break ; } spec: rules: - host: snippet.zkbr.com http: paths: - path: / pathType: Prefix backend: service: name: snippet port: number: 80 [root@master 8.10]# kubectl apply -f snippet.yaml
验证
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 [root@master 8.10]# echo "192.168.10.12 snippet.zkbr.com" >> /etc/hosts [root@master 8.10]# curl snippet.zkbr.com -H 'User-Agent: chrome' -I HTTP/1.1 302 Moved Temporarily Date: Sun, 10 Aug 2025 09:35:43 GMT Content-Type: text/html Content-Length: 138 Connection: keep-alive Location: http://www.jd.com [root@master 8.10]# curl snippet.zkbr.com -H 'User-Agent: Firefox' -I HTTP/1.1 302 Moved Temporarily Date: Sun, 10 Aug 2025 09:36:12 GMT Content-Type: text/html Content-Length: 138 Connection: keep-alive Location: http://www.baidu.com [root@master 8.10]# curl snippet.zkbr.com -I HTTP/1.1 200 OK Date: Sun, 10 Aug 2025 09:36:18 GMT Content-Type: text/html; charset=UTF-8 Content-Length: 23 Connection: keep-alive Last-Modified: Mon, 16 Jan 2023 04:21:54 GMT ETag: "17-5f259ec4c7c80" Accept-Ranges: bytes
2.14.11 Ingress-nginx 配置黑白名单 配置方案
Annotations:只对指定的ingress生效
ConfigMap:全局生效
若是同时配置了Annotations和configmap,一般都是annotations 生效,configmap不生效,因为annotations优先级比configmap高
黑白名单概念
白名单 :仅允许指定 IP 地址(或 IP 段)访问后端服务,其他 IP 被拒绝。
黑名单 :拒绝指定 IP 地址(或 IP 段)访问,其他 IP 被允许。
配置方案建议
黑名单建议使用ConfigMap去配置
白名单建议使用Annotations去配置
Configmap 添加黑名单 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 [root@master 8.10]# kubectl edit -n ingress cm ingress-nginx-controller data: allow-snippet-annotations: "true" block-cidrs: 192.168.10.12 [root@master 8.10]# kubectl get ingress NAME CLASS HOSTS ADDRESS PORTS AGE ingress-error nginx error.zkbr.com 80 30d ingress-http-auth nginx auth.zkbr.com 80, 443 32d ingress-http-ssl nginx ssl.zkbr.com 80, 443 33d ingress-http-www1 nginx www1.zkbr.com 80 34d ingress-http-www2 nginx www2.zkbr.com 80 34d ingress-redirect nginx redirect.zkbr.com 80 32d ingress-rewrite nginx rewrite.zkbr.com 80 31d snippet.zkbr.com nginx snippet.zkbr.com 80 107m [root@master 8.10]# echo "192.168.10.12 www1.zkbr.com" >> /etc/hosts [root@master 8.10]# curl www1.zkbr.com Welcome to The Apache. [root@node01 ~]# echo "192.168.10.13 www1.zkbr.com" >> /etc/hosts [root@node01 ~]# curl www1.zkbr.com Welcome to The Apache. [root@node03 ~]# echo "192.168.10.12 www1.zkbr.com" >> /etc/hosts [root@node03 ~]# curl www1.zkbr.com <html> <head ><title>403 Forbidden</title></head> <body> <center><h1>403 Forbidden</h1></center> <hr><center>nginx</center> </body> </html>
Annotations 添加白名单 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 [root@master 8.10]# vim white.yaml apiVersion: apps/v1 kind: Deployment metadata: name: white-deploy spec: replicas: 1 selector: matchLabels: app: white template: metadata: labels: app: white spec: containers: - image: myos:httpd name: myapp --- apiVersion: v1 kind: Service metadata: labels: app: white name: white-svc spec: ports: - name: http port: 80 protocol: TCP targetPort: 80 selector: app: white type : ClusterIP --- apiVersion: networking.k8s.io/v1 kind: Ingress metadata: annotations: nginx.ingress.kubernetes.io/whitelist-source-range: 192.168.10.13 name: white-zkbr-com labels: app: white spec: rules: - host: white.zkbr.com http: paths: - path: / pathType: Prefix backend: service: name: white-svc port: number: 80 [root@master 8.10]# kubectl apply -f white.yaml [root@master 8.10]# echo "192.168.10.12 white.zkbr.com" >> /etc/hosts [root@master 8.10]# curl white.zkbr.com <html> <head ><title>403 Forbidden</title></head> <body> <center><h1>403 Forbidden</h1></center> <hr><center>nginx</center> </body> </html> [root@node01 ~]# echo "192.168.10.12 white.zkbr.com" >> /etc/hosts [root@node01 ~]# curl white.zkbr.com <html> <head ><title>403 Forbidden</title></head> <body> <center><h1>403 Forbidden</h1></center> <hr><center>nginx</center> </body> </html> ========================================================== [root@node03 ~]# echo "192.168.10.12 white.zkbr.com" >> /etc/hosts [root@node03 ~]# curl white.zkbr.com Welcome to The Apache.
2.14.12 Ingress-nginx 金丝雀部署和灰度发布 在 Kubernetes(K8s)中,金丝雀部署(Canary Deployment) 和灰度发布(Gray Release) 是两种常用的渐进式发布策略,用于在生产环境中安全地验证新版本应用的稳定性。Ingress 作为 K8s 中管理外部流量入口的 API 对象,可通过 Ingress 控制器(如 Nginx Ingress、Traefik 等)的扩展能力实现这两种策略,核心是通过精细化的流量规则将部分流量导向新版本,逐步扩大范围直至全量切换。
核心概念
金丝雀部署 :通常基于流量比例 (如 10%、30%、50%)将部分流量路由到新版本,验证无误后逐步提高比例,最终全量切换。
灰度发布 :更强调精准的用户 / 场景筛选 (如按用户 ID、地域、浏览器类型等),仅让特定群体访问新版本,验证通过后分批扩大覆盖范围。
金丝雀部署示例: 这里我们是把业务web服务的apache改为nginx,企业中一般都是从v1版本升级到v2版本,思路是一样的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 [root@master 8.13]# vim 1.old_ingress.yaml --- apiVersion: apps/v1 kind: Deployment metadata: name: myapp-http spec: replicas: 5 selector: matchLabels: app: myapp-http template: metadata: labels: app: myapp-http spec: containers: - name: http image: myos:httpd ports: - containerPort: 80 --- apiVersion: v1 kind: Service metadata: name: myapp-http-svc spec: selector: app: myapp-http ports: - port: 80 targetPort: 80 --- apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: myapp-http-ingress spec: rules: - host: myapp-web.zkbr.com http: paths: - path: / pathType: Prefix backend: service: name: myapp-http-svc port: number: 80 [root@master 8.13]# kubectl apply -f 1.old_ingress.yaml [root@master 8.13]# echo "192.168.10.12 myapp-web.zkbr.com" >> /etc/hosts [root@master 8.13]# curl myapp-web.zkbr.com Welcome to The Apache. [root@master 8.13]# curl myapp-web.zkbr.com Welcome to The Apache. =======================新版本Ingress==================== [root@master 8.13]# vim 2.new_ingress.yaml --- apiVersion: apps/v1 kind: Deployment metadata: name: myapp-nginx spec: replicas: 5 selector: matchLabels: app: myapp-nginx template: metadata: labels: app: myapp-nginx spec: containers: - name: nginx image: myos:nginx ports: - containerPort: 80 --- apiVersion: v1 kind: Service metadata: name: myapp-nginx-svc spec: selector: app: myapp-nginx ports: - port: 80 targetPort: 80 --- apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: myapp-canary-ingress annotations: nginx.ingress.kubernetes.io/canary: "true" nginx.ingress.kubernetes.io/canary-weight: "10" spec: rules: - host: myapp-web.zkbr.com http: paths: - path: / pathType: Prefix backend: service: name: myapp-nginx-svc port: number: 80 [root@master 8.13]# kubectl apply -f 2.new_ingress.yaml [root@master 8.13]# kubectl get ingress NAME CLASS HOSTS ADDRESS PORTS AGE myapp-canary-ingress nginx myapp-web.zkbr.com 80 18m myapp-http-ingress nginx myapp-web.zkbr.com 80 25m
验证金丝雀部署: 1 2 3 4 5 6 7 8 9 10 [root@master 8.13]# for i in {1..100};do curl myapp-web.zkbr.com >> sum ;done [root@master 8.13]# cat sum | sort | uniq -c 9 Nginx is running ! 91 Welcome to The Apache. [root@master 8.13]# kubectl edit ingress myapp-canary-ingress
1 2 3 4 5 [root@master 8.13]# for i in {1..100};do curl myapp-web.zkbr.com >> sum ;done ^C [root@master 8.13]# cat sum | sort | uniq -c 57 Nginx is running ! 43 Welcome to The Apache.
灰度发布示例: 2.15 Loki日志方案 2.15.1 Loki 核心定位与设计理念 Loki 是由 Grafana Labs 开发的轻量级日志聚合系统 ,专为云原生环境(尤其是 Kubernetes)设计,核心理念是 “只索引元数据,不索引日志内容” ,旨在解决传统日志系统(如 ELK)在大规模集群下存储成本高、查询性能差的问题。
其核心设计原则可总结为:
低成本 :避免对日志全文进行索引,仅通过标签(Labels,如 Pod 名、命名空间、容器名)建立索引,大幅降低存储和计算开销。
云原生友好 :原生支持 Kubernetes 标签,日志与集群资源(Pod、Service)的元数据天然对齐,便于关联查询。
与 Grafana 深度集成 :可直接在 Grafana 中实现 “日志 - 指标 - 监控面板” 的联动分析,无需切换工具。
2.15.2 Loki 的核心组成结构 Loki 的架构是微服务式的,每个组件有明确的职责。你可以根据规模选择全部一起运行(单机模式)或分开独立部署(微服务模式)。
组件
核心职责
关键说明
Promtail
日志采集客户端 (Agent)
1. 以 DaemonSet 形式运行在每个 K8s 节点上。 2. 核心工作 :监控节点上的容器日志文件,从 K8s API 获取 Pod 的元数据(标签) 并附加到日志流上,然后将日志推送给 Loki。
Distributor
日志入口网关
1. 接收来自 Promtail 的日志流。 2. 负责校验数据格式和标签的合法性。 3. 使用一致性哈希 算法,将日志数据分发给后端的多个 Ingester ,实现负载均衡。
Ingester
日志处理与暂存器
1. 最核心、最有状态的组件 。 2. 在内存中构建日志的压缩数据块 (Chunk )。 3. 定期将完整的 Chunk 持久化到后端存储中。 4. 通过副本机制保证数据高可用。
Querier
查询引擎
1. 接收查询请求(如从 Grafana 来)。 2. 先从索引存储 中查找哪些 Chunk 包含目标日志,再从对象存储 中拉取实际的日志数据。 3. 在内存中执行 LogQL 查询,返回结果。
Query Frontend
查询前端 (可选,但推荐)
1. 坐在 Querier 前面,用于提升查询性能。 2. 将大查询拆分成多个小查询并行执行。 3. 提供查询结果缓存。
2.15.3 Loki 与 EFK 的核心区别 EFK 栈指的是 Elasticsearch + Fluentd + Kibana 。它与 Loki 的设计哲学完全不同。
对比维度
Loki
EFK
1. 核心设计哲学
只索引元数据 (标签) ,不对日志正文内容建索引。
对全文进行索引 ,包括所有关键字。
2. 资源消耗 (成本关键)
非常低
非常高
• 索引小 :只存标签,量很小。 • 存储便宜 :日志块(Chunk)可放在廉价对象存储(如S3)。
• 索引巨大 :因为索引了所有内容,索引大小可能接近甚至超过原始日志大小。 • 存储昂贵 :需要高性能的本地SSD来保证Elasticsearch的读写速度。
3. 存储成本
极低
较高
4. 查询方式
使用 LogQL ,类似 Prometheus 的 PromQL。先通过标签筛选日志流,再对内容进行 grep 式的文本搜索。查询思路是:“先缩小范围,再搜索内容” 。
使用 Elasticsearch 的 Query DSL 。功能极其强大,可以对索引过的任何字段进行复杂、快速的聚合、分析和模糊查询。
5. 运维复杂度
相对简单
非常复杂
组件职责单一,架构清晰。存储与计算分离,扩展方便。
Elasticsearch 集群本身的运维是一项专业且繁重的工作(调优、分片、扩容等)。
6. 与 K8s/Prometheus 集成
无缝集成
需要配置
标签模型与 Prometheus 完全一致,在 Grafana 中能实现从监控指标到日志的一键跳转 。
需要额外配置才能实现类似的关联性。
7. 优势
• 成本极致优化 • 云原生友好 • 运维简单 • 与 Prometheus 生态完美融合
• 查询功能强大且灵活 • 全文检索速度快 • 社区成熟,生态庞大
8. 劣势
对日志内容的复杂查询和分析能力较弱。
资源消耗大,总拥有成本高,运维挑战大。
2.15.4 k8s集群部署Loki日志 1 2 3 4 5 6 7 8 9 10 11 12 13 $ helm repo add grafana https://grafana.github.io/helm-charts $ helm repo update $ helm search repo grafana | grep loki $ helm pull grafana/loki-stack . $ tar -xf loki-stack-2.10.2.tgz
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 $ cd loki-stack && ls $ vim values_new.yaml loki: enabled: true image: registry: harbor:443 repository: grafana/loki tag: 2.9 .0 persistence: enabled: true accessModes: - ReadWriteOnce size: 2Gi storageClassName: nfs-client promtail: enabled: true image: registry: harbor:443 repository: grafana/promtail tag: latest defaultVolumes: - name: run hostPath: path: /run/promtail - name: containers hostPath: path: /var/lib/containerd/io.containerd.grpc.v1.cri/containers/ - name: pods hostPath: path: /var/log/pods defaultVolumeMounts: - name: run mountPath: /run/promtail - name: containers mountPath: /var/lib/containerd/io.containerd.grpc.v1.cri/containers/ readOnly: true - name: pods mountPath: /var/log/pods readOnly: true grafana: enabled: true sidecar: image: registry: harbor:443 repository: library/k8s-sidecar tag: 1.19 .2 sha: "" initChownData: enabled: true image: registry: harbor:443 image: registry: harbor:443 repository: grafana/grafana tag: 9.5 .1 service: enabled: true type: NodePort persistence: enabled: true storageClassName: nfs-client accessModes: - ReadWriteOnce size: 1Gi
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 $ kubectl create ns loki $ helm install loki -n loki -f values_new.yaml . [root@master loki-stack]# kubectl get pods -n loki NAME READY STATUS RESTARTS AGE loki-0 1/1 Running 2 (5h48m ago) 46h loki-grafana-78fb7b4475-dhprw 2/2 Running 4 (5h48m ago) 46h loki-promtail-hfc4k 1/1 Running 2 (5h48m ago) 46h loki-promtail-n4mcv 1/1 Running 2 (5h48m ago) 46h loki-promtail-rkzqv 1/1 Running 2 (5h48m ago) 46h loki-promtail-wrhfw 1/1 Running 2 (5h48m ago) 46h [root@master loki-stack]# kubectl get -n loki svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE loki ClusterIP 10.245.253.16 <none> 3100/TCP 46h loki-grafana NodePort 10.245.99.172 <none> 80:30275/TCP 46h loki-headless ClusterIP None <none> 3100/TCP 46h loki-memberlist ClusterIP None <none> 7946/TCP 46h [root@master loki-stack]# kubectl get -n loki deployments NAME READY UP-TO-DATE AVAILABLE AGE loki-grafana 1/1 1 1 46h [root@master loki-stack]# kubectl get -n loki ds NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE loki-promtail 4 4 4 4 4 <none> 46h [root@master loki-stack]# kubectl get -n loki secrets NAME TYPE DATA AGE loki Opaque 1 46h loki-grafana Opaque 3 46h loki-promtail Opaque 1 46h sh.helm.release.v1.loki.v1 helm.sh/release.v1 1 46h
查看Grafana的密码
访问Grafana
验证loki日志体系 1 2 3 4 5 6 7 8 [root@master loki-stack]# kubectl create deploy mynginx --image=nginx:1.27.0 --replicas=2 [root@master loki-stack]# kubectl create svc clusterip mynginx --tcp=80:80 [root@master loki-stack]# kubectl get svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kubernetes ClusterIP 10.245.0.1 <none> 443/TCP 39d myapp ClusterIP 10.245.127.190 <none> 80/TCP 46h mynginx ClusterIP 10.245.90.133 <none> 80/TCP 5s