Kubernetes 容器编排技术(基于kubernetes的容器云平台)

Kubernetes 容器编排技术
Kubernetes 容器编排
前言
知识扩展
早在 2015 年 5 月,Kubernetes 在 Google 上的搜索热度就已经超过了 Mesos 和 Docker Swarm

Kubernetes 容器编排

前言

知识扩展

2015 年5 月,Kubernetes 在Google 上的搜索热度超过了Mesos 和Docker Swarm,从此在容器编排引擎领域领先竞争对手数十英里。就这样。

目前,AWS、Azure、Google、阿里云、腾讯云等主流公有云都提供了基于Kubernetes的容器服务,Rancher、CoreOS、IBM、Mirantis、Oracle、Red Hat、VMWare等众多厂商也在提供基于Kubernetes的容器服务。积极发展。为Kubernetes推广基于容器CaaS或PaaS服务的容器服务。 Kubernetes 可以说是当今容器行业最炙手可热的明星。

Google 在其数据中心运行了超过20 亿个容器,Google 10 年前就开始使用容器技术。最初,Google 开发了一个名为Borg(现在的Omega)的系统来调度这些大量的容器和工作负载。经过多年的经验积累,谷歌决定重写这套容器管理系统,并将其贡献给开源社区,让全世界都能从中受益。这个项目就是Kubernetes。简单来说,Kubernetes 是Google Omega 的开源版本。与很多基础设施开发路线先工程实践、后方法论不同,Kubernetes 项目的理论基础远早于工程实践。当然,这要归功于Google 于2015 年4 月发布的Borg。 Borg系统长期以来被誉为谷歌内部最强大的“秘密武器”。虽然有些夸张,但这种说法并不是吹牛。这是因为与Spanner 和BigTable 等更高级别的项目相比,Borg 负责整个Google 基础设施的核心依赖关系。在Google发表的基础设施系统论文中,Borg项目在整个基础设施技术栈中排名垫底。

由于这个位置,Borg 是最不可能被Google 开源的项目。幸运的是,得益于Docker 项目和容器技术的流行,Docker 项目终于能够以不一样的方式与开源社区见面。这是一个Kubernetes 项目。

因此,相比“小”Docker公司和Mesos社区的“旧瓶新酒”,Kubernetes项目从一开始就幸运地达到了别人难以达到的高度。在成长阶段,项目的几乎所有核心功能都将源自Borg/Omega系统的设计和经验。更重要的是,在开源社区的实施过程中,整个社区的集体努力极大地改进了这些功能,并修复了Borg 系统中遗留的许多缺陷和问题。

因此,它在刚发布时被批评为“成熟”,但随着时间的推移,社区很快就认识到了Docker 技术堆栈的“不成熟”和Mesos 社区的“陈旧”。以下以Borg系统为指导。这体现了一种独特的“进步”和“诚信”意识,这是基础设施领域开源项目赖以生存的核心价值观。

什么是编排

一个正在运行的Linux容器可以分为两个部分。

1.容器静态视图

一组联合挂载在/var/lib/docker/aufs/mnt 的rootfs。这部分被称为“容器镜像”。

2. 容器的动态视图

由Namespace+Cgroup组成的隔离环境。这部分称为“容器运行时”。

作为一名开发人员,我并不真正关心容器运行时的差异。在整个“开发、测试、发布”的过程中,真正承载容器信息的是容器镜像,而不是容器运行时。这也是容器技术圈在Docker项目成功后立即转向“容器编排”这一“上层建筑”的主要原因。作为云服务提供商或基础设施提供商,您所需要的只是发送Docker 镜像的能力。通过将用户提交的容器作为容器运行,您可以成为这个高度活跃的容器生态系统的家园,从您的节点上积累整个容器技术堆栈的价值。

更重要的是,从这个托管点一直回到Docker镜像的生产者和用户,整条路径上的每个服务节点如CI/CD、监控、安全、网络、存储等都有盈利空间。这个逻辑是所有云计算提供商热衷于容器技术的一个关键原因。通过容器镜像,您可以直接将容器镜像与潜在用户(即开发人员)关联起来。

从一个开发者、单一容器镜像,到无数开发者、庞大的容器集群,容器技术实现了从“容器”到“容器云”的飞跃,真正得到了市场和生态的认可。就这样,容器从开发者手中的小工具一跃成为云计算领域的绝对中流砥柱,“容器编排”技术可以定义容器在容器中组织和管理的规范。云计算领域处于领先地位。容器技术领域“头把交椅”。

最流行的容器编排工具:

1.Docker Compose+Swarm组合

2. Google和RedHat共同领导的Kubernetes项目

google与k8s

2014年6月,基础设施领域的领头羊谷歌突然行动起来,正式宣布一个名为Kubernetes的项目诞生。这个项目不仅拯救了当时的CoreOS和RedHat,还再次改变了整个容器市场,类似于Docker项目的诞生。这个时期也是Docker生态初创公司的春天,围绕Docker项目的很多网络、存储、监控、CI/CD、甚至UI项目都在启动。一个非常成功的开源和商业初创公司。

2014年到2015年,容器社区整体非常活跃。在令人兴奋的繁荣背后,更多的担忧也浮现出来。最重要的负面情绪是对Docker 商业化策略的各种担忧。事实上,很多从业者都知道,Docker项目现在已经成为Docker公司的商业产品。开源只是Docker吸引一批开发者的重要方式。但多年来,开源社区的商业化其实也是基于类似的理念。只是是否引人注目、是否不耐烦的问题。

大多数人真正感到沮丧的是,Docker 在Docker 开源项目的发展中始终保持着绝对的权力和话语权,而对其他玩家(CoreOS、RedHat 等)没有真正的控制权,这是我尝试过的。很多时候都是用我自己的行动。谷歌和微软)的物质利益。所以此时大家的抱怨已经不再是在GitHub上抱怨那么简单了。

许多容器领域的资深玩家都听说过,当Docker 项目首次出现时,Google 也开源了其长期存在的内部和经过生产测试的Linux 容器。 lmctfy(让我为您容器化)。然而,面对Docker项目的迅速崛起,这个对用户不友好的Google容器项目却无力抵抗。于是,谷歌不甘示弱,表达了与Docker合作的愿望。我们决定关闭该项目并与Docker 合作推广中立的容器运行时库作为Docker 项目的核心依赖项。不过,Docker并没有同意这个提议,这显然削弱了自己的地位,不久之后就发布了容器运行时库Libcontainer。这种草率的、公司驱动的、带着战略考虑的重构,是Libcontainer长期被社区诟病代码可读性和可维护性差的一个关键原因。

此时,Docker公司在容器运行时层面的强硬立场,以及Docker项目在快速迭代过程中的不稳定和频繁变更,开始引起社区的抱怨。这种情绪在2015 年达到了顶峰,当时容器领域的其他几位参与者开始讨论Docker 项目的“死亡”。 “砍”的方法也很经典,建立中立的基础。因此,2015年6月22日,由Docker公司牵头,与CoreOS、Google、RedHat等公司合作,Docker公司捐赠了Libcontainer,该项目将由完全中立的基金会管理,随后命名为RunC项目。宣布将进行更改。作为基础,我们将共同制定一套容器和镜像的标准和规范。这套标准和规范就是OCI(开放容器倡议)。 OCI 的提议旨在将容器运行时和镜像实现与Docker 项目完全分离。这一方面增加了Docker在容器技术领域的主导地位,另一方面也让其他玩家可以在不依赖Docker项目的情况下构建自己的平台层功能。

但OCI的成立是这些集装箱厂商为了自身利益干涉而妥协的结果。 Docker虽然是OCI的创始人和创始成员,但它在OCI的技术进步或标准制定中发挥的作用不大,也没有动力去积极推动这些所谓的标准。这也是OCI组织至今效率低下的根本原因。 OCI 无法破坏Docker 在容器领域的主导地位,因此Google 和RedHat 等公司发起了第二次尝试。

Docker之所以不担心OCI威胁,是因为Docker项目是容器生态系统事实上的标准,并且其维护的Docker社区足够庞大。但当这场战斗转移到容器之上的平台层,即PaaS层时,Docker的竞争优势迅速扩大。 Google 和RedHat 等成熟公司在该领域拥有深厚的技术专业知识,但CoreOS 等初创公司也广泛使用Etcd 等开源基础设施项目。然而,Docker公司只有一个Swarm。

因此,Google、RedHat等开源基础设施领域的玩家联合发起了一个名为CNCF(云原生计算基金会)的基金会。这个基金会的目的很明确:我们要建立一个基于Kubernetes 项目、由开源基础设施厂商主导、以独立基金会方式运营的平台级社区,以对抗以Docker 为核心的容器。核心公司。商业生态。

为了围绕Kubernetes 项目创建“护城河”,CNCF 社区必须确保至少两件事。 Kubernetes项目必须能够在容器编排领域获得显着的竞争优势。创建一个Kubernetes 项目来覆盖足够的场景。 CNCF 社区如何解决Kubernetes 项目在编排领域的竞争力问题:

在容器编排领域,Kubernetes项目要面临来自Docker公司和Mesos社区的双重压力。 Swarm 和Mesos 实际上从两个不同的方向讲述了最好的故事。 Swarm 非常适合与Docker 生态系统无缝集成,而Mesos 非常适合调度和管理大型集群。

这两个方向也是大多数人在运行容器集群管理项目时最简单的两个出发点。因此,Kubernetes项目继续在这两个方向上做文章是不明智的。

Kubernetes选择的响应方法是:Borg

大多数k8s 项目都源自Borg 和Omega 系统的内部功能。这些功能属于k8s项目、Pod、Sidecar等功能和设计模式。

这就解释了为什么Kubernetes发布后,很多人抱怨它的设计理念太“超前”。 Kubernetes项目的基本功能并不是少数工程师凭空想出的,而是由Google众多工程师积累和完善的。在集装箱化基础设施领域拥有多年的工作经验。这是Kubernetes 项目从一开始就避免与Swarm 和Mesos 社区同质化的重要方式。

CNCF接下来的任务是如何通过技术手段将这些先进理念落地到开源社区中,并培育一个认可这些理念的生态系统。

RedHat发挥了关键作用。当时,Kubernetes 团队规模很小,可供投资的工程能力非常有限。这正是RedHat的强项。 RedHat是全球为数不多的真正理解开源社区运营和项目开发本质的合作伙伴之一。 RedHat与Google建立合作伙伴关系,不仅保证了RedHat对Kubernetes项目的影响力,也正式开启了容器编排领域的“三国”局面。

Mesos 社区和容器技术之间的关系更像是借势,而不是该领域的实际参与者和领导者。这一事实,再加上其所属的Apache 社区固有的封闭性,导致Mesos 社区在容器编排领域拥有最成熟的技术,但创新却很少。

从一开始,Docker 就优先考虑解决来自Kubernetes 项目的竞争。一方面,我们一直强调“Docker Native”的“重要性”,另一方面,我们也多次与k8s项目发生直接冲突。本次大赛的发展很快就超出了Docker的预期。 Kubernetes 项目并没有与Swarm 项目竞争,因此“Docker Native”的说辞并没有那么致命。相反,k8s项目新颖的设计理念和吸引力很快创建了自己的容器编排和管理生态系统。

随着GitHub 上Kubernetes 项目的各种指标开始起飞,Swarm 项目远远落后了。

CNCF社区如何解决第二个问题:

CNCF 社区在加入容器监控事实上的标准Prometheus 项目后,迅速将容器生态系统中的一组知名工具和项目添加到其成员项目中,包括Fluentd、OpenTracing 和CNI。看到CNCF社区对用户展现出的巨大吸引力,很多企业和创业团队开始专门针对CNCF社区而不是Docker制定推广策略。

2016年,Docker公布了一项让所有人震惊的计划。这个想法是放弃现有的Swarm 项目,并将所有容器编排和集群管理功能合并到Docker 项目中。 Docker公司已经意识到,Swarm项目此时唯一的竞争优势就是它与Docker项目的无缝集成。那么如何将Swarm 合并到您的Docker 项目中来充分利用这一点呢?从工程角度来看,这种做法风险很大。虽然内置的容器编排、集群管理和负载均衡功能当然可以将Docker 项目的边界扩展到完整的PaaS 项目,但这种变化带来的技术复杂性和维护困难可能会对您的Docker 项目产生负面影响。 撤销。然而,在当时的普遍环境下,选择Docker 也可能是高风险的。

k8s响应策略:

它朝相反的方向发展,并开始在整个社区推广“民主化”的建筑。简而言之,Kubernetes 项目为开发人员公开了可扩展的插件机制,并鼓励用户在从API 到容器运行时的每一层使用Code is。参与了Kubernetes 项目的各个阶段。

这一变化对Kubernetes 项目的影响是立竿见影的,很快整个容器社区就出现了一些基于Kubernetes API 和扩展接口的二次创新,包括:

将目前非常流行的微服务治理项目Istio、广泛采用的有状态应用部署框架Operator、Ceph等重量级产品封装成简单易用的容器存储插件Rook等开源创业项目。

在鼓励二次创新的整体氛围下,k8s社区自2016年以来经历了前所未有的发展。更重要的是,与之前仅限于“打包发布”的PaaS 路线不同,这次蓬勃发展的容器社区完全以Kubernetes 项目为中心。面对Kubernetes 社区的崛起和壮大,Docker 也必须面对一个大赌注出了问题的现实。但在早前拒绝了微软的大手笔收购后,Docker其实已经没有多少回旋的余地,而是选择逐渐放弃开源社区,专注于自身的商业转型。

因此,从2017年开始,Docker公司将把Docker项目的容器运行时部分Containerd捐赠给CNCF社区,标志着Docker项目已全面升级为PaaS平台,并随后宣布更名为Docker公司。当你将Docker 项目委托给Moby 并依靠社区来维护时,Docker 的商业产品就拥有了Docker 商标。

这些Docker 措施背后的含义非常明确。通过将Docker 项目更名为Moby,我们完全放弃了在开源社区与Kubernetes 生态系统的竞争,转而帮助那些原本属于Docker 的人。您的社区变成您自己的客户。 2017年10月,Docker出人意料地宣布将把Kubernetes项目纳入其旗舰产品Docker企业版中,结束了近两年的“编排战争”。

2018年1月30日,RedHat宣布将以2.5亿美元收购CoreOS。

2018 年3 月28 日,这一切争议的始作俑者Docker CTO Solomon Hikes 宣布辞职,曾经风起云涌的容器技术世界终于尘埃落定。

在短短几年内,容器技术世界发生了很多变化,其中很多实际上是有意义的。和Docker一样,通过运营开源社区取得巨大成功的初创公司必然面临来自整个云计算行业的竞争和围攻。这个行业的专有性对于像Docker 这样的科技初创公司来说本质上是不友好的。在这种情况下,接受微软过高的收购价格在大多数人看来是一个非常谨慎和现实的选择。不过,Solomon Hikes身上却有一丝理想主义的影子,他不想“依赖他人”,必须带领Docker公司顶住来自整个云计算行业的压力。

但Docker的最终战术是创建一个高度封闭的技术生态系统,将开源项目和商业产品紧密联系起来。这实际上违背了Docker项目与开发者保持密切关系的初衷。相比之下,Kubernetes 社区采取了更为良性的做法,接手了Docker 项目未竟的事业,构建一个以开发者为中心、相对民主、开放的容器生态系统。这就是为什么Kubernetes 项目的成功实际上是不可避免的。

很难想象如果Docker 首先选择与Kubernetes 社区合作,今天的容器生态系统会是什么样子。但我们可以肯定的是,Docker这五年来的变化,以及Solomon Hikes本人的传奇经历,都在云计算的悠久历史上留下了浓墨重彩的一笔。

概括:

容器技术的兴起源于PaaS技术的普及。

Docker公司发布的Docker项目是一个里程碑。

Docker项目通过“容器镜像”解决了打包应用程序的根本问题。

容器本身没有价值,有价值的是“容器编排”。这也正是容器技术生态中爆发了一场关于“容器编排”的“战争”的原因。而这场战争以Kubernetes 项目和CNCF 社区的胜利而告终。

容器编排之战

一. kubernetes简要概述

Kubernetes 是一个开源系统,用于自动部署、扩展和管理容器化应用程序,将组成应用程序的容器组合成逻辑单元,以便于管理和服务发现。同时,我们汇集了社区的最佳想法和实践。

容器化应用的总趋势是越来越多的单体应用向研发靠拢,从早期的单服务裸机部署转向使用容器进行托管服务。运维、测试等IT职能部门的人们逐渐转向云端进行开发,不再只强调单一的一致性问题主导一切。

事实上,容器是自Linux 内核诞生以来就存在的一个产品,当时资源限制要求程序员创建容器并编写代码来调用Linux 内核内的Cgroup 和NameSpace。底层的文件系统和宿主机是同一个文件系统,所以操作容器内的文件就和修改宿主机的文件系统一样,这使得容器的适用性大大降低,后来随着Docker的出现解决了这个问题。 docker 创建一种称为图像的东西并将其分层。这样,当用户启动容器时,镜像的副本会在读/写层挂载到容器中,而容器可见的底层文件系统只是内容。因此,镜像提供的好处远不止于此,比如在生产、测试、研发三种环境中,应用程序会因为环境不一致而很快崩溃。使用镜像,您不必担心环境问题,因为所有服务都在容器内运行,并且为容器运行提供的底层文件系统是镜像的。只要使用同一个镜子,就会是一样的。运维、测试、研发人员不再需要担心应用程序的运行环境,Docker Engine 提供了许多简单易用的指令。控制容器大大加快了程序员的时间。它还提供了相应的镜像和公共仓库来存储它们,让容器技术得以快速发展。

容器快速发展五年后,Google 发布了容器编排系统kubernetes。这解决了跨多个主机调度容器的问题,并提供了可靠的API接口来控制容器执行并保持容器始终启动。融合故障自愈、服务发现等新特性,容器行业从此向容器编排发起猛烈进攻。

1.1 kubernetes 功能简介

服务发现和负载均衡

Kubernetes 可以使用DNS 名称或其自己的IP 地址公开容器。当进入容器的流量较大时,Kubernetes可以负载均衡和分配网络流量,常用的DNS插件用于:集群内的服务发现和容器通信。负载均衡器使用集群中的服务资源向外界公开服务,同时还提供入口插件接口,向外界公开服务存储和编排。

Kubernetes 允许您自动挂载您选择的存储系统,包括本地存储和公共云提供商。集群中的应用程序可能需要持久化数据,如果容器被删除,数据就会丢失,因此需要稳定的外部存储。集群非常重要。常见为k8提供存储能力的集群有: heketi+glusterfs、Rook+Ceph、阿里云OSS等。集群内存储类允许您直接从自动存储集群申请存储空间。部署和回滚

Kubernetes 可用于描述已部署容器的所需状态。这允许实际状态以受控的速率改变到期望状态。例如,您可以自动化Kubernetes 创建用于部署的新容器,或者删除或替换现有容器。大多数情况下,控制k8s集群中容器的状态都是采用yaml资源配置列表的形式,方便记忆和识别,并且可以自动完成装箱计算。

Kubernetes 允许您指定每个容器所需的CPU 和内存(RAM)。当容器指定其资源请求时,Kubernetes 可以做出更好的决策来管理容器的资源。这与Linux操作系统中的资源配额有关。并发访问往往会导致某些容器无限期地独占主机资源,导致其他服务容器争夺运行和提供服务所需的资源,从而导致大规模瘫痪和自愈。

Kubernetes 会重新启动失败的容器、替换容器、杀死不响应用户定义的运行状况检查的容器,并仅在Pod 创建期间成功检测到After all 探针时才为Pod 提供服务,直到准备就绪为止。否则,k8s 中的容器将受到kubelet 和kube-apiserver 的双重管理。

ubelet上报容器运行状态, api-server向etcd中请求期望状态, 比较后让其达到期望的状态, 从而保证其功能/服务容器永久保持在线密钥与配置管理
  Kubernetes 允许你存储和管理敏感信息, 例如密码、OAuth 令牌和 ssh 密钥.  你可以在不重建容器镜像的情况下部署和更新密钥和应用程序配置, 也无需在堆栈配置中暴露密钥. 对于无状态应用(exam: nginx)的配置文件, 可以使用k8s中的configmap资源进行存储, 但对于密码和某些私密信息而言就可以使用secret资源进行存储, 从而避免频繁更改和私密信息泄漏的风险

1.2 Kubernetes架构及组件

一个 Kubernetes 集群由一组被称作节点的机器组成。这些节点上运行 Kubernetes 所管理的容器化应用。集群具有至少一个工作节点。工作节点托管作为应用负载的组件的 Pod 。控制平面管理集群中的工作节点和 Pod 。 为集群提供故障转移和高可用性,这些控制平面一般跨多主机运行,集群跨多个节点运行。

kube-apiserver
API 服务器是 Kubernetes 控制面的组件, 该组件公开了 Kubernetes API。 API 服务器是 Kubernetes 控制面的前端。Kubernetes API 服务器的主要实现是 kube-apiserver。 kube-apiserver 设计上考虑了水平伸缩,也就是说,它可通过部署多个实例进行伸缩。 你可以运行 kube-apiserver 的多个实例,并在这些实例之间平衡流量。etcd
etcd 是兼具一致性和高可用性的键值数据库,可以作为保存 Kubernetes 所有集群数据的后台数据库。你的 Kubernetes 集群的 etcd 数据库通常需要有个备份计划。要了解 etcd 更深层次的信息,请参考 etcd 文档。kube-scheduler
控制平面组件,负责监视新创建的、未指定运行节点(node)的 Pods,选择节点让 Pod 在上面运行。调度决策考虑的因素包括单个 Pod 和 Pod 集合的资源需求、硬件/软件/策略约束、亲和性和反亲和性规范、数据位置、工作负载间的干扰和最后时限。

/etc/kubernetes/manifests/kube-scheduler.yaml 中添加参数

–node-monitor-grace-period=10s:该参数指定了 Node 节点的监控宽限期(Grace Period),即在节点变为不可用之前的时间间隔。在这段时间内,调度器不会将 Pod 从该节点上驱逐,以便给应用程序一些时间来完成正在进行的任务并正常关闭。在这个例子中,宽限期为 10 秒。–node-monitor-period=3s:该参数指定了 Node 节点的监控周期,即调度器检查节点状态的时间间隔。在这个例子中,监控周期为 3 秒。–node-startup-grace-period=20s:该参数指定了新节点加入集群后的启动宽限期(Grace Period),即在节点完全准备好之前的时间间隔。在这段时间内,调度器不会将 Pod 调度到该节点上,以便给节点一些时间来初始化和准备运行 Pod。在这个例子中,启动宽限期为 20 秒。–pod-eviction-timeout=10s:该参数指定了 Pod 的驱逐超时时间,即在调度器决定驱逐一个 Pod 之后,等待该 Pod 关闭的时间间隔。如果 Pod 在这段时间内没有正常关闭,调度器将强制终止该 Pod。在这个例子中,驱逐超时时间为 10 秒。

kube-controller-manager
运行控制器进程的控制平面组件。从逻辑上讲,每个控制器都是一个单独的进程, 但是为了降低复杂性,它们都被编译到同一个可执行文件,并在一个进程中运行。这些控制器包括:
节点控制器(Node Controller):负责在节点出现故障时进行通知和响应任务控制器(Job controller):监测代表一次性任务的 Job 对象,然后创建 Pods 来运行这些任务直至完成端点控制器(Endpoints Controller):填充端点(Endpoints)对象(即加入 Service 与 Pod)服务帐户和令牌控制器(Service Account & Token Controllers):为新的命名空间创建默认帐户和 API 访问令牌 cloud-controller-manager
云控制器管理器是指嵌入特定云的控制逻辑的 控制平面组件。 云控制器管理器使得你可以将你的集群连接到云提供商的 API 之上, 并将与该云平台交互的组件同与你的集群交互的组件分离开来。cloud-controller-manager 仅运行特定于云平台的控制回路。 如果你在自己的环境中运行 Kubernetes,或者在本地计算机中运行学习环境, 所部署的环境中不需要云控制器管理器。与 kube-controller-manager 类似,cloud-controller-manager 将若干逻辑上独立的 控制回路组合到同一个可执行文件中,供你以同一进程的方式运行。 你可以对其执行水平扩容(运行不止一个副本)以提升性能或者增强容错能力。下面的控制器都包含对云平台驱动的依赖:
节点控制器(Node Controller):用于在节点终止响应后检查云提供商以确定节点是否已被删除路由控制器(Route Controller):用于在底层云基础架构中设置路由服务控制器(Service Controller):用于创建、更新和删除云提供商负载均衡器 Node 组件: 节点组件在每个节点上运行,维护运行的 Pod 并提供 Kubernetes 运行环境。kubelet
一个在集群中每个节点(node)上运行的代理。 它保证容器(containers)都 运行在 Pod 中。kubelet 接收一组通过各类机制提供给它的 PodSpecs,确保这些 PodSpecs 中描述的容器处于运行状态且健康。 kubelet 不会管理不是由 Kubernetes 创建的容器。kube-proxy
kube-proxy 是集群中每个节点上运行的网络代理, 实现 Kubernetes 服务(Service) 概念的一部分。kube-proxy 维护节点上的网络规则。这些网络规则允许从集群内部或外部的网络会话与 Pod 进行网络通信。如果操作系统提供了数据包过滤层并可用的话,kube-proxy 会通过它来实现网络规则。否则, kube-proxy 仅转发流量本身。容器运行时(Container Runtime)
容器运行环境是负责运行容器的软件。Kubernetes 支持容器运行时,例如 Docker、 containerd、CRI-O 以及 Kubernetes CRI (容器运行环境接口) 的其他任何实现。插件(Addons)
插件使用 Kubernetes 资源实现集群功能。 因为这些插件提供集群级别的功能,插件中命名空间域的资源属于 kube-system 命名空间。下面描述众多插件中的几种。有关可用插件的完整列表,请参见 插件(Addons)。
DNS
尽管其他插件都并非严格意义上的必需组件,但几乎所有 Kubernetes 集群都应该 有集群 DNS, 因为很多示例都需要 DNS 服务。集群 DNS 是一个 DNS 服务器,和环境中的其他 DNS 服务器一起工作,它为 Kubernetes 服务提供 DNS 记录。Kubernetes 启动的容器自动将此 DNS 服务器包含在其 DNS 搜索列表中。Web 界面(仪表盘)
Dashboard 是 Kubernetes 集群的通用的、基于 Web 的用户界面。 它使用户可以管理集群中运行的应用程序以及集群本身并进行故障排除。容器资源监控
容器资源监控 将关于容器的一些常见的时间序列度量值保存到一个集中的数据库中,并提供用于浏览这些数据的界面。集群层面日志
集群层面日志 机制负责将容器的日志数据 保存到一个集中的日志存储中,该存储能够提供搜索和浏览接口 ELK/Loki+grafana

1.3 Kubernetes核心概念

Master

Master节点主要负责资源调度(Scheduler),控制副本(Replication Controller),和提供统一访问集群的入口(API Server)。—核心节点也是管理节点

Node

Node是Kubernetes集群架构中运行Pod的服务节点(亦叫agent或minion)。Node是Kubernetes集群操作的单元,用来承载被分配Pod的运行,是Pod运行的宿主机,由Master管理,并汇报容器状态给Master,当Node节点宕机(NotReady状态)时,该节点上运行的Pod会被自动地转移到其他节点上。
Kubelet组件—Kubelet会从API Server接收Pod的创建请求,启动和停止容器,监控容器运行状态并汇报给Kubernetes API Server。
KuberProxy组件–实现将客户端请求流量转发到内部的pod资源。
Docker Engine(docker)组件,Docker引擎,负责本机的容器创建和管理工作;

Node IP

Node节点的IP地址,是Kubernetes集群中每个节点的物理网卡的IP地址,是真是存在的物理网络,所有属于这个网络的服务器之间都能通过这个网络直接通信;

Pod

Pod直译是豆荚,可以把容器想像成豆荚里的豆子,把一个或多个关系紧密的豆子包在一起就是豆荚(一个Pod)。处于同一个Pod中的容器共享同样的存储空间(Volume,卷或存储卷)、网络命名空间IP地址和Port端口。在k8s中我们不会直接操作容器,而是把容器包装成Pod再进行管理
运行于Node节点上,Pod是k8s进行创建、调度和管理的最小单位.一个Pod可以包含一个容器或者多个相关容器。
 
Pod 就是 k8s 世界里的\”应用\”;而一个应用,可以由多个容器组成。

pause容器

每个Pod中都有一个pause容器,pause容器做为Pod的网络接入点,Pod中其他的容器会使用容器映射模式启动并接入到这个pause容器。

Pod IP

Pod的IP地址,是Docker Engine根据docker0网桥的IP地址段进行分配的,通常是一个虚拟的二层网络,位于不同Node上的Pod能够彼此通信,需要通过Pod IP所在的虚拟二层网络进行通信,而真实的TCP流量则是通过Node IP所在的物理网卡流出的

资源限制:

每个Pod可以设置限额的计算机资源有CPU和Memory;

Pod Volume:

Docker Volume对应Kubernetes中的Pod Volume;数间共享数据。

Event

是一个事件记录,记录了事件最早产生的时间、最后重复时间、重复次数、发起者、类型,以及导致此事件的原因等信息。Event通常关联到具体资源对象上,是排查故障的重要参考信息

Endpoint(pod IP+容器Port)

标识服务进程的访问点;

ReplicaSet

确保任何指定的Pod副本数量,并提供声明式更新等功能。

Deployment

Deployment是一个更高层次的API对象,它管理ReplicaSets和Pod,并提供声明式更新等功能。
官方建议使用Deployment管理ReplicaSets,而不是直接使用ReplicaSets,这就意味着可能永远不需要直接操作ReplicaSet对象,因此Deployment将会是使用最频繁的资源对象。

RC-Replication Controller

Replication Controller用来管理Pod的副本,保证集群中存在指定数量的Pod副本。集群中副本的数量大于指定数量,则会停止指定数量之外的多余pod数量,反之,则会启动少于指定数量个数的容器,保证数量不变。Replication Controller是实现弹性伸缩、动态扩容和滚动升级的核心。

Service

Service定义了Pod的逻辑集合和访问该集合的策略,是真实服务的抽象。它有暴露内部服务的端口到外网的能力。Service提供了一个统一的服务访问入口以及服务代理和发现机制,用户不需要了解后台Pod是如何运行。
一个service定义了访问pod的方式,就像单个固定的IP地址和与其相对应的DNS名之间的关系。
service分为两种ip地址:
一种是对内的叫Cluster IP ,一种是对外的叫external ip:
实现对外ip方式分为:
1.NodePort:通过每个 Node 上的 IP 和内部服务暴露出来的静态端口实现暴露服务。
2.LoadBalancer:使用云提供商的负载均衡器.
3.ingress:使用负载均衡软件实现负载均衡,向外部暴露服务。

Cluster IP

Service的IP地址,特性:
专门给集群内部资源访问的。在k8s集群里面部署的容器,用内部的容器可以访问这个ip地址。但是集群外面的机器不能访问。
1.无法被Ping通;
2.Node IP、Pod IP、Cluster IP之间的通信,采用的是Kubernetes自己设计的一种编程方式的特殊的路由规则,与IP路由有很大的不同

Label—标签与Selectors—选择器

 Kubernetes中的任意API对象都是通过Label进行标识,Label的实质是一系列的K/V键值对。Label是Replication Controller和Service运行的基础,二者通过Label来进行关联Node上运行的Pod。
 比如,你可能创建了一个\”tier\”和“app”标签,通过Label(tier=frontend, app=myapp)来标记前端Pod容器,使用 Label(tier=backend, app=myapp)标记后台Pod。然后可以使用 Selectors 选择带有特定Label的Pod,并且将 Service 或者 Replication Controller 应用到上面。
一个label是一个被附加到资源上的键/值对,譬如附加到一个Pod上,为它传递一个用户自定的并且可识别的属性。

注:Node、Pod、Replication Controller和Service等都可以看作是一种\”资源对象\”,几乎所有的资源对象都可以通过Kubernetes提供的kubectl工具执行增、删、改、查等操作并将其保存在etcd中持久化存储。

二. 常用镜像库

官方 Docker Hub 仓库:
官方 Kubernetes 镜像通常存放在 Docker Hub 上。地址通常是 k8s.gcr.io/<镜像名>:<标签>,但实际上你需要通过 Docker Hub 访问这些镜像,例如 docker pull k8s.gcr.io/<镜像名>:<标签>。 阿里云容器镜像服务(Aliyun Docker Registry):
阿里云的 Kubernetes 镜像仓库地址格式一般为 registry.cn-hangzhou.aliyuncs.com/google_containers/<镜像名>:<标签>。例如,对于 kube-apiserver 镜像,地址可能是 registry.cn-hangzhou.aliyuncs.com/google_containers/kube-apiserver:<标签>。 腾讯云容器镜像服务(Tencent Cloud Container Registry):
腾讯云的 Kubernetes 镜像仓库地址格式可能类似于 ccr.ccs.tencentyun.com/tke-market/<镜像名>:<标签>。 华为云容器镜像服务(Huawei Cloud Container Registry):
华为云的 Kubernetes 镜像仓库地址格式可能类似于 swr.cn-north-4.myhuaweicloud.com/<镜像名>:<标签>。

三.集群部署方式

方式1. minikube
Minikube是一个工具,可以在本地快速运行一个单点的Kubernetes,尝试Kubernetes或日常开发的用户使用。不能用于生产环境。
官方地址:https://kubernetes.io/docs/setup/minikube/

方式2. kubeadm
Kubeadm也是一个工具,提供kubeadm init和kubeadm join,用于快速部署Kubernetes集群。
官方地址:https://kubernetes.io/docs/reference/setup-tools/kubeadm/kubeadm/

方式3. 直接使用epel-release yum源,缺点就是版本较低 1.5方式4. 二进制包

四.Kubeadm 方式部署集群

kubeadm部署官方文档
kubeadm部署k8s高可用集群的官方文档
主节点CPU核数必须是 ≥2核且内存要求必须≥2G,否则k8s无法启动

主机名地址角色配置kub-k8s-master1192.168.75.100主节点2核2G 50Gkub-k8s-master2192.168.75.101主节点2核2G 50Gkub-k8s-master3192.168.75.102主节点2核2G 50Gkub-k8s-node1192.168.75.103工作节点1核2G 50Gkub-k8s-node2192.168.75.104工作节点/haproxy1核2G 50G

4.1 安装docker[集群]

过程请查看docker安装部分

# 一键安装docker脚本
curl -L https://gitea.beyourself.org.cn/newrain001/shell-project/raw/branch/master/os/get-docker-latest.sh | sh

4.2 获取镜像[集群]

获取镜像版本方法

kubeadm config images list –kubernetes-version=<版本号>

谷歌镜像[由于国内网络原因,无法下载,后续将采用阿里云镜像代替]

# 官方,不采用
docker pull k8s.gcr.io/kube-apiserver:v1.22.0
docker pull k8s.gcr.io/kube-proxy:v1.22.0
docker pull k8s.gcr.io/kube-controller-manager:v1.22.0
docker pull k8s.gcr.io/kube-scheduler:v1.22.0
docker pull k8s.gcr.io/etcd:3.5.0-0
docker pull k8s.gcr.io/pause:3.5
docker pull k8s.gcr.io/coredns/coredns:v1.8.4

4.3 阿里仓库下载[集群]

在线下载

docker pull registry.cn-hangzhou.aliyuncs.com/google_containers/kube-controller-manager:v1.22.0
docker pull registry.cn-hangzhou.aliyuncs.com/google_containers/kube-proxy:v1.22.0
docker pull registry.cn-hangzhou.aliyuncs.com/google_containers/kube-apiserver:v1.22.0
docker pull registry.cn-hangzhou.aliyuncs.com/google_containers/kube-scheduler:v1.22.0
docker pull registry.cn-hangzhou.aliyuncs.com/google_containers/coredns:v1.8.4
docker pull registry.cn-hangzhou.aliyuncs.com/google_containers/etcd:3.5.0-0
docker pull registry.cn-hangzhou.aliyuncs.com/google_containers/pause:3.5
# 下载完了之后需要将aliyun下载下来的所有镜像打成k8s.gcr.io/kube-controller-manager:v1.22.0这样的tag
docker tag registry.cn-hangzhou.aliyuncs.com/google_containers/kube-controller-manager:v1.22.0 k8s.gcr.io/kube-controller-manager:v1.22.0
docker tag registry.cn-hangzhou.aliyuncs.com/google_containers/kube-proxy:v1.22.0 k8s.gcr.io/kube-proxy:v1.22.0
docker tag registry.cn-hangzhou.aliyuncs.com/google_containers/kube-apiserver:v1.22.0 k8s.gcr.io/kube-apiserver:v1.22.0
docker tag registry.cn-hangzhou.aliyuncs.com/google_containers/kube-scheduler:v1.22.0 k8s.gcr.io/kube-scheduler:v1.22.0
docker tag registry.cn-hangzhou.aliyuncs.com/google_containers/coredns:v1.8.4 k8s.gcr.io/coredns/coredns:v1.8.4
docker tag registry.cn-hangzhou.aliyuncs.com/google_containers/etcd:3.5.0-0 k8s.gcr.io/etcd:3.5.0-0
docker tag registry.cn-hangzhou.aliyuncs.com/google_containers/pause:3.5 k8s.gcr.io/pause:3.5
# 可以清理掉aliyun的镜像标签
docker rmi -f `docker images –format {{.Repository}}:{{.Tag}} | grep aliyun`

离线安装[适用于当前网络环境不佳的情况]

[root@localhost ~]# ls
anaconda-ks.cfg application.tar.xz kube-1.22.0.tar.xz
[root@localhost ~]# tar xvf application.tar.xz
[root@localhost ~]# tar xvf kube-1.22.0.tar.xz
[root@localhost ~]# cd application/images && ls *.tar | xargs -i docker load -i {} && cd ../.. && cd kube-1.22.0/images && ls *.tar | xargs -i docker load -i {} && cd ../.. && docker images | wc -l

4.4 集群部署[集群]

cat >> /etc/hosts <<EOF
192.168.75.100 kub-k8s-master1
192.168.75.101 kub-k8s-master2
192.168.75.102 kub-k8s-master3
192.168.75.103 kub-k8s-node1
192.168.75.104 kub-k8s-node2
EOF
制作本地解析,修改主机名。相互解析

4.5 集群环境配置[集群]

1.关闭防火墙:
# systemctl disable firewalld –now
2.禁用SELinux:
# setenforce 0
3.编辑文件/etc/selinux/config,将SELINUX修改为disabled,如下:
# sed -i \’s/SELINUX=enforcing/SELINUX=disabled/\’ /etc/sysconfig/selinux
SELINUX=disabled
4.时间同步
# timedatectl set-timezone Asia/Shanghai
# yum install -y ntpsec
# ntpdate time.windows.com
# hwclock –systohc
5.配置静态ip

4.6 关闭系统Swap[集群]

Kubernetes 1.8开始要求关闭系统的Swap,如果不关闭,默认配置下kubelet将无法启动。

方法一: 通过kubelet的启动参数–fail-swap-on=false更改这个限制。方法二: 关闭系统的Swap。
1.关闭swap分区
# swapoff -a
修改/etc/fstab文件,注释掉SWAP的自动挂载,使用free -m确认swap已经关闭。
2.注释掉swap分区:
# sed -i \’s/.*swap.*/#&/\’ /etc/fstab
# free -m
total used free shared buff/cache available
Mem: 3935 144 3415 8 375 3518
Swap: 0 0 0

4.7 安装Kubeadm包[集群]

配置官方源[需翻墙]
# cat <<EOF > /etc/yum.repos.d/kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=https://packages.cloud.google.com/yum/repos/kubernetes-el7-x86_64/
enabled=1
gpgcheck=0
EOF
配置阿里云源
cat <<EOF > /etc/yum.repos.d/kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=https://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-x86_64/
enabled=1
gpgcheck=1
repo_gpgcheck=1
gpgkey=https://mirrors.aliyun.com/kubernetes/yum/doc/yum-key.gpg https://mirrors.aliyun.com/kubernetes/yum/doc/rpm-package-key.gpg
EOF

所有节点:
1.安装依赖包及常用软件包
# yum install -y conntrack ntpdate ntp ipvsadm ipset jq iptables curl sysstat libseccomp wget vim net-tools git iproute lrzsz bash-completion tree bridge-utils unzip bind-utils gcc
2.安装对应版本
# yum install -y kubelet-1.22.0-0.x86_64 kubeadm-1.22.0-0.x86_64 kubectl-1.22.0-0.x86_64
3.加载ipvs相关内核模块
# cat <<EOF > /etc/modules-load.d/ipvs.conf
ip_vs
ip_vs_lc
ip_vs_wlc
ip_vs_rr
ip_vs_wrr
ip_vs_lblc
ip_vs_lblcr
ip_vs_dh
ip_vs_sh
ip_vs_nq
ip_vs_sed
ip_vs_ftp
ip_vs_sh
nf_conntrack_ipv4
ip_tables
ip_set
xt_set
ipt_set
ipt_rpfilter
ipt_REJECT
ipip
EOF
4.配置:
配置转发相关参数,否则可能会出错
# cat <<EOF > /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-iptables=1
net.bridge.bridge-nf-call-ip6tables=1
net.ipv4.ip_forward=1
net.ipv4.tcp_tw_recycle=0
vm.swappiness=0
vm.overcommit_memory=1
vm.panic_on_oom=0
fs.inotify.max_user_instances=8192
fs.inotify.max_user_watches=1048576
fs.file-max=52706963
fs.nr_open=52706963
net.ipv6.conf.all.disable_ipv6=1
net.netfilter.nf_conntrack_max=2310720
EOF
5.使配置生效
# sysctl –system
6.如果net.bridge.bridge-nf-call-iptables报错,加载br_netfilter模块
# modprobe br_netfilter
# modprobe ip_conntrack
# sysctl -p /etc/sysctl.d/k8s.conf
7.查看是否加载成功
# lsmod | grep ip_vs

4.8 配置启动kubelet[集群]

1.配置kubelet使用pause镜像
获取docker的cgroups
配置变量:
[root@k8s-master ~]# DOCKER_CGROUPS=`docker info |grep \’Cgroup\’ | awk \’ NR==1 {print $3}\’`
[root@k8s-master ~]# echo $DOCKER_CGROUPS
cgroupfs
2.配置kubelet的cgroups
# cat >/etc/sysconfig/kubelet<<EOF
KUBELET_EXTRA_ARGS=\”–cgroup-driver=$DOCKER_CGROUPS –pod-infra-container-image=k8s.gcr.io/pause:3.5\”
EOF

启动
# systemctl daemon-reload
# systemctl enable kubelet && systemctl restart kubelet
在这里使用 # systemctl status kubelet,你会发现报错误信息;
10月 11 00:26:43 node1 systemd[1]: kubelet.service: main process exited, code=exited, status=255/n/a
10月 11 00:26:43 node1 systemd[1]: Unit kubelet.service entered failed state.
10月 11 00:26:43 node1 systemd[1]: kubelet.service failed.
运行 # journalctl -xefu kubelet 命令查看systemd日志才发现,真正的错误是:
unable to load client CA file /etc/kubernetes/pki/ca.crt: open /etc/kubernetes/pki/ca.crt: no such file or directory
#这个错误在运行kubeadm init 生成CA证书后会被自动解决,此处可先忽略。
#简单地说就是在kubeadm init 之前kubelet会不断重启。

4.9 配置master节点[master]

! 4.9.1 和 4.9.2 所有master都执行,4.9.3 只有master1执行

4.9.1 安装haproxy

也可以选择lvs、nginx等

[root@localhost ~]# yum install -y haproxy
[root@localhost ~]# vim /etc/haproxy/haproxy.cfg
# Global settings section
global
log 127.0.0.1 local2 # 设置日志记录,使用本地系统日志服务
chroot /var/lib/haproxy # 设置 HAProxy 的运行环境为隔离模式
pidfile /var/run/haproxy.pid # 指定 HAProxy 进程的 PID 文件位置
maxconn 4000 # 设置最大并发连接数
user haproxy # 指定 HAProxy 运行的用户
group haproxy # 指定 HAProxy 运行的用户组
daemon # 以守护进程模式运行
# 开启状态统计的 UNIX 套接字
stats socket /var/lib/haproxy/stats
# Default settings section
defaults
mode tcp # 默认使用 TCP 模式,适用于非 HTTP 流量
log global # 使用全局日志设置
option tcplog # 开启 TCP 日志记录
timeout connect 10s # 连接超时设置
timeout client 1m # 客户端超时
timeout server 1m # 服务器超时
retries 3 # 连接重试次数
frontend stats
mode http
bind *:9000 # 监听 9000 端口,用于访问统计页面
stats enable # 启用统计报告
stats uri /haproxy_stats # 设置统计报告的 URI,可以通过 http://<your-ip>:9000/haproxy_stats 访问
stats realm HAProxy\\ Statistics # 设置认证弹窗的标题
stats auth admin:password # 设置访问统计页面的用户名和密码,这里为 admin 和 password,您应该设置一个更安全的密码
stats admin if TRUE # 如果设置为 TRUE,允许在页面上进行管理操作
# Kubernetes Master 节点的前端配置
frontend kubernetes-frontend
bind *:6443 # 监听 8443 端口,用于 Kubernetes API
default_backend kubernetes-backend # 默认后端设置为 kubernetes-backend
# Kubernetes Master 节点的后端配置
backend kubernetes-backend
balance roundrobin # 使用轮询算法进行负载均衡
option tcp-check # 开启 TCP 检查以检测服务器健康状态
server master1 kub-k8s-master1:6443 check # Kubernetes Master 节点1
server master2 kub-k8s-master2:6443 check # Kubernetes Master 节点2
server master3 kub-k8s-master3:6443 check # Kubernetes Master 节点3
[root@localhost ~]# systemctl start haproxy

4.9.2 配置keepalived

[root@kub-k8s-master1 ~]# yum install -y keepalived
! Configuration File for keepalived
global_defs {
router_id directory1 # master2、3记得修改
}
vrrp_instance VI_1 {
state MASTER # master2、3记得修改
interface ens33
virtual_router_id 80
priority 100
advert_int 1
authentication {
auth_type PASS
auth_pass 1111
}
virtual_ipaddress {
192.168.75.104/24 # 网段的vip,也是4.9.3初始化步骤中的 –control-plane-endpoint
}
}
启动并开机启动

4.9.3 初始化集群

注意事项:

1、–control-plane-endpoint “192.168.75.104:8443” 指的是vip的地址和haproxy的端口2、–pod-network-cidr=10.244.0.0/16 可以默认,如果修改,所有pod的网段均会修改3、–apiserver-advertise-address=192.168.75.100 指的是当前master1 的ip地址4、–apiserver-cert-extra-sans=后面指的是所有master的ip和vip地址

运行初始化过程如下:
[root@kub-k8s-master1 ~]# kubeadm init –kubernetes-version=v1.22.0 –control-plane-endpoint \”192.168.75.104:8443\” –pod-network-cidr=10.244.0.0/16 –apiserver-advertise-address=192.168.75.100 –apiserver-cert-extra-sans=192.168.75.104,192.168.75.100,192.168.75.101,192.168.75.102,192.168.75.101 –upload-certs
[init] Using Kubernetes version: v1.22.0
[preflight] Running pre-flight checks
[WARNING SystemVerification]: this Docker version is not on the list of validated versions: 25.0.0. Latest validated version: 20.10
[preflight] Pulling images required for setting up a Kubernetes cluster
[preflight] This might take a minute or two, depending on the speed of your internet connection
[preflight] You can also perform this action in beforehand using \’kubeadm config images pull\’
[certs] Using certificateDir folder \”/etc/kubernetes/pki\”
[certs] Generating \”ca\” certificate and key
[certs] Generating \”apiserver\” certificate and key
[certs] apiserver serving cert is signed for DNS names [kub-k8s-master1 kubernetes kubernetes.default kubernetes.default.svc kubernetes.default.svc.cluster.local] and IPs [10.96.0.1 192.168.75.100 192.168.75.104 192.168.75.101 192.168.75.102]
[certs] Generating \”apiserver-kubelet-client\” certificate and key
[certs] Generating \”front-proxy-ca\” certificate and key
[certs] Generating \”front-proxy-client\” certificate and key
[certs] Generating \”etcd/ca\” certificate and key
[certs] Generating \”etcd/server\” certificate and key
[certs] etcd/server serving cert is signed for DNS names [kub-k8s-master1 localhost] and IPs [192.168.75.100 127.0.0.1 ::1]
[certs] Generating \”etcd/peer\” certificate and key
[certs] etcd/peer serving cert is signed for DNS names [kub-k8s-master1 localhost] and IPs [192.168.75.100 127.0.0.1 ::1]
[certs] Generating \”etcd/healthcheck-client\” certificate and key
[certs] Generating \”apiserver-etcd-client\” certificate and key
[certs] Generating \”sa\” key and public key
[kubeconfig] Using kubeconfig folder \”/etc/kubernetes\”
[endpoint] WARNING: port specified in controlPlaneEndpoint overrides bindPort in the controlplane address
[kubeconfig] Writing \”admin.conf\” kubeconfig file
[endpoint] WARNING: port specified in controlPlaneEndpoint overrides bindPort in the controlplane address
[kubeconfig] Writing \”kubelet.conf\” kubeconfig file
[endpoint] WARNING: port specified in controlPlaneEndpoint overrides bindPort in the controlplane address
[kubeconfig] Writing \”controller-manager.conf\” kubeconfig file
[endpoint] WARNING: port specified in controlPlaneEndpoint overrides bindPort in the controlplane address
[kubeconfig] Writing \”scheduler.conf\” kubeconfig file
[kubelet-start] Writing kubelet environment file with flags to file \”/var/lib/kubelet/kubeadm-flags.env\”
[kubelet-start] Writing kubelet configuration to file \”/var/lib/kubelet/config.yaml\”
[kubelet-start] Starting the kubelet
[control-plane] Using manifest folder \”/etc/kubernetes/manifests\”
[control-plane] Creating static Pod manifest for \”kube-apiserver\”
[control-plane] Creating static Pod manifest for \”kube-controller-manager\”
[control-plane] Creating static Pod manifest for \”kube-scheduler\”
[etcd] Creating static Pod manifest for local etcd in \”/etc/kubernetes/manifests\”
[wait-control-plane] Waiting for the kubelet to boot up the control plane as static Pods from directory \”/etc/kubernetes/manifests\”. This can take up to 4m0s
[apiclient] All control plane components are healthy after 5.013483 seconds
[upload-config] Storing the configuration used in ConfigMap \”kubeadm-config\” in the \”kube-system\” Namespace
[kubelet] Creating a ConfigMap \”kubelet-config-1.22\” in namespace kube-system with the configuration for the kubelets in the cluster
[upload-certs] Storing the certificates in Secret \”kubeadm-certs\” in the \”kube-system\” Namespace
[upload-certs] Using certificate key:
1d241f4bdfbfb37444221397b0e3522c8096f54d4b301cc49b69ddc735bee9c1
[mark-control-plane] Marking the node kub-k8s-master1 as control-plane by adding the labels: [node-role.kubernetes.io/master(deprecated) node-role.kubernetes.io/control-plane node.kubernetes.io/exclude-from-external-load-balancers]
[mark-control-plane] Marking the node kub-k8s-master1 as control-plane by adding the taints [node-role.kubernetes.io/master:NoSchedule]
[bootstrap-token] Using token: hxyvvg.bzvpv2h1ag28g79m
[bootstrap-token] Configuring bootstrap tokens, cluster-info ConfigMap, RBAC Roles
[bootstrap-token] configured RBAC rules to allow Node Bootstrap tokens to get nodes
[bootstrap-token] configured RBAC rules to allow Node Bootstrap tokens to post CSRs in order for nodes to get long term certificate credentials
[bootstrap-token] configured RBAC rules to allow the csrapprover controller automatically approve CSRs from a Node Bootstrap Token
[bootstrap-token] configured RBAC rules to allow certificate rotation for all node client certificates in the cluster
[bootstrap-token] Creating the \”cluster-info\” ConfigMap in the \”kube-public\” namespace
[kubelet-finalize] Updating \”/etc/kubernetes/kubelet.conf\” to point to a rotatable kubelet client certificate and key
[addons] Applied essential addon: CoreDNS
[endpoint] WARNING: port specified in controlPlaneEndpoint overrides bindPort in the controlplane address
[addons] Applied essential addon: kube-proxy
Your Kubernetes control-plane has initialized successfully!
To start using your cluster, you need to run the following as a regular user:
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
Alternatively, if you are the root user, you can run:
export KUBECONFIG=/etc/kubernetes/admin.conf
You should now deploy a pod network to the cluster.
Run \”kubectl apply -f [podnetwork].yaml\” with one of the options listed at:
https://kubernetes.io/docs/concepts/cluster-administration/addons/
You can now join any number of the control-plane node running the following command on each as root:
# 其他控制节点执行
kubeadm join 192.168.75.104:8443 –token hxyvvg.bzvpv2h1ag28g79m \\
–discovery-token-ca-cert-hash sha256:19a140207276157f923d7f962b5a51e93006f282180b439ad5c1390acaea722c \\
–control-plane –certificate-key 1d241f4bdfbfb37444221397b0e3522c8096f54d4b301cc49b69ddc735bee9c1
Please note that the certificate-key gives access to cluster sensitive data, keep it secret!
As a safeguard, uploaded-certs will be deleted in two hours; If necessary, you can use
\”kubeadm init phase upload-certs –upload-certs\” to reload certs afterward.
Then you can join any number of worker nodes by running the following on each as root:
# 其他node节点执行
kubeadm join 192.168.75.104:8443 –token hxyvvg.bzvpv2h1ag28g79m \\
–discovery-token-ca-cert-hash sha256:19a140207276157f923d7f962b5a51e93006f282180b439ad5c1390acaea722c

上面记录了完成的初始化输出的内容,根据输出的内容基本上可以看出手动初始化安装一个Kubernetes集群所需要的关键步骤。
其中有以下关键内容:
[kubelet] 生成kubelet的配置文件”/var/lib/kubelet/config.yaml”
[certificates]生成相关的各种证书
[kubeconfig]生成相关的kubeconfig文件
[bootstraptoken]生成token记录下来,后边使用kubeadm join往集群中添加节点时会用到

配置使用kubectl
如下操作在master节点操作
[root@kub-k8s-master1 ~]# rm -rf $HOME/.kube
[root@kub-k8s-master1 ~]# mkdir -p $HOME/.kube
[root@kub-k8s-master1 ~]# cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
[root@kub-k8s-master1 ~]# chown $(id -u):$(id -g) $HOME/.kube/config
查看node节点
[root@k8s-master ~]# kubectl get nodes
NAME STATUS ROLES AGE VERSION
k8s-master NotReady master 2m41s v1.22.0

4.10 配置使用网络插件[master]

# 版本差异 https://projectcalico.docs.tigera.io/archive/v3.22/getting-started/kubernetes/requirements
#> 部署calico网络插件
[root@kub-k8s-master1 ~]# curl -L https://docs.projectcalico.org/v3.22/manifests/calico.yaml -O
# 修改 文件
4205 – name: IP_AUTODETECTION_METHOD
4206 value: \”interface=ens33\”
[root@kub-k8s-master1 ~]# kubectl apply -f calico.yaml
[root@kub-k8s-master1 ~]# kubectl get pod -A -w
NAMESPACE NAME READY STATUS RESTARTS AGE
kube-system calico-kube-controllers-7c87c5f9b8-q2vth 1/1 Running 0 36s
kube-system calico-node-5th7f 1/1 Running 0 36s
kube-system calico-node-g996t 1/1 Running 0 36s
kube-system calico-node-n94v5 1/1 Running 0 36s
kube-system calico-node-qjmth 1/1 Running 0 36s
kube-system calico-node-rfzf7 1/1 Running 0 36s
kube-system coredns-78fcd69978-5sg7p 1/1 Running 0 11m
kube-system coredns-78fcd69978-btws4 1/1 Running 0 11m
kube-system etcd-kub-k8s-master1 1/1 Running 6 11m
kube-system etcd-kub-k8s-master2 1/1 Running 0 10m
kube-system etcd-kub-k8s-master3 1/1 Running 0 10m
kube-system kube-apiserver-kub-k8s-master1 1/1 Running 6 11m
kube-system kube-apiserver-kub-k8s-master2 1/1 Running 2 (10m ago) 11m
kube-system kube-apiserver-kub-k8s-master3 1/1 Running 0 10m
kube-system kube-controller-manager-kub-k8s-master1 1/1 Running 10 (10m ago) 11m
kube-system kube-controller-manager-kub-k8s-master2 1/1 Running 0 11m
kube-system kube-controller-manager-kub-k8s-master3 1/1 Running 0 10m
kube-system kube-proxy-4bsm2 1/1 Running 0 8m2s
kube-system kube-proxy-bb5rd 1/1 Running 0 10m
kube-system kube-proxy-bnxj5 1/1 Running 0 8m4s
kube-system kube-proxy-cfm5m 1/1 Running 0 11m
kube-system kube-proxy-smhd6 1/1 Running 0 11m
kube-system kube-scheduler-kub-k8s-master1 1/1 Running 10 (10m ago) 11m
kube-system kube-scheduler-kub-k8s-master2 1/1 Running 0 11m
kube-system kube-scheduler-kub-k8s-master3 1/1 Running 0 10m

calico 原理

基于 BGP 的网络连接:Calico 使用 BGP 协议在 Kubernetes 集群中的节点之间建立网络连接。每个节点运行一个 Calico 代理【daemonset】,该代理负责与其他节点建立 BGP 连接,并将容器的网络流量路由到正确的目的地。网络策略:Calico 提供了一种灵活的网络策略模型,允许定义容器之间的访问规则。可以使用标签来标识容器,并使用网络策略来控制容器之间的流量。IP 地址管理:Calico 自动管理 Kubernetes 集群中的 IP 地址分配。它使用 IPIP 隧道技术将容器的 IP 地址映射到节点的 IP 地址,以确保容器之间的通信。网络隔离:Calico 提供了网络隔离功能,允许将不同的容器和应用程序隔离到不同的网络中。

flannel 原理

网络覆盖:Flannel 在 Kubernetes 集群中的每个节点上运行一个守护进程,该守护进程使用 VXLAN。网络配置:Flannel 守护进程负责配置虚拟网络,并将容器的网络流量路由到正确的目的地。IP 地址分配:Flannel 自动管理 Kubernetes 集群中的 IP 地址分配。它使用 IPAM(IP Address Management)模块来分配 IP 地址,并将它们分配给容器。网络策略:Flannel 支持网络策略,允许定义容器之间的访问规则。可以使用网络策略来控制容器之间的流量,以确保应用程序的安全性。

4.11 加入集群[node]

注意,如果丢失了初始化命令,可以通过一下命令重新生成

kubeadm token create –print-join-command

master 节点[master2、master3]

[root@kub-k8s-master1 ~]# kubeadm join 192.168.75.104:8443 –token hxyvvg.bzvpv2h1ag28g79m –discovery-token-ca-cert-hash sha256:19a140207276157f923d7f962b5a51e93006f282180b439ad5c1390acaea722c –control-plane –certificate-key 1d241f4bdfbfb37444221397b0e3522c8096f54d4b301cc49b69ddc735bee9c1

node 节点

配置node节点加入集群:
如果报错开启ip转发:
# sysctl -w net.ipv4.ip_forward=1
在所有node节点操作,此命令为初始化master成功后返回的结果
# kubeadm join 192.168.75.104:8443 –token hxyvvg.bzvpv2h1ag28g79m \\
–discovery-token-ca-cert-hash sha256:19a140207276157f923d7f962b5a51e93006f282180b439ad5c1390acaea722c

4.12 后续检查[master]

各种检测:
1.查看pods:
[root@kub-k8s-master1 ~]# kubectl get pod -A -w
NAMESPACE NAME READY STATUS RESTARTS AGE
kube-system calico-kube-controllers-7c87c5f9b8-q2vth 1/1 Running 0 36s
kube-system calico-node-5th7f 1/1 Running 0 36s
kube-system calico-node-g996t 1/1 Running 0 36s
kube-system calico-node-n94v5 1/1 Running 0 36s
kube-system calico-node-qjmth 1/1 Running 0 36s
kube-system calico-node-rfzf7 1/1 Running 0 36s
kube-system coredns-78fcd69978-5sg7p 1/1 Running 0 11m
kube-system coredns-78fcd69978-btws4 1/1 Running 0 11m
kube-system etcd-kub-k8s-master1 1/1 Running 6 11m
kube-system etcd-kub-k8s-master2 1/1 Running 0 10m
kube-system etcd-kub-k8s-master3 1/1 Running 0 10m
kube-system kube-apiserver-kub-k8s-master1 1/1 Running 6 11m
kube-system kube-apiserver-kub-k8s-master2 1/1 Running 2 (10m ago) 11m
kube-system kube-apiserver-kub-k8s-master3 1/1 Running 0 10m
kube-system kube-controller-manager-kub-k8s-master1 1/1 Running 10 (10m ago) 11m
kube-system kube-controller-manager-kub-k8s-master2 1/1 Running 0 11m
kube-system kube-controller-manager-kub-k8s-master3 1/1 Running 0 10m
kube-system kube-proxy-4bsm2 1/1 Running 0 8m2s
kube-system kube-proxy-bb5rd 1/1 Running 0 10m
kube-system kube-proxy-bnxj5 1/1 Running 0 8m4s
kube-system kube-proxy-cfm5m 1/1 Running 0 11m
kube-system kube-proxy-smhd6 1/1 Running 0 11m
kube-system kube-scheduler-kub-k8s-master1 1/1 Running 10 (10m ago) 11m
kube-system kube-scheduler-kub-k8s-master2 1/1 Running 0 11m
kube-system kube-scheduler-kub-k8s-master3 1/1 Running 0 10m
2.查看节点:
[root@kub-k8s-master1 ~]# kubectl get nodes
NAME STATUS ROLES AGE VERSION
kub-k8s-master Ready master 43m v1.22.0
kub-k8s-node1 Ready <none> 6m46s v1.22.0
kub-k8s-node2 Ready <none> 6m37s v1.22.0
到此集群配置完成

4.13 集群数据库相关操作

由k8s集群数据库文档单独记录

kubernetes命令自动补全

[root@kub-k8s-master1 ~]# yum install -y epel-release bash-completion
[root@kub-k8s-master1 ~]# source /usr/share/bash-completion/bash_completion
[root@kub-k8s-master1 ~]# source <(kubectl completion bash)
[root@kub-k8s-master1 ~]# echo \”source <(kubectl completion bash)\” >> ~/.bashrc
# 输入kubectl, 双击Tab
[root@kub-k8s-master1 ~]# kubectl
alpha apply certificate convert delete edit get options proxy scale uncordon
annotate attach cluster-info cordon describe exec kustomize patch replace set version
api-resources auth completion cp diff explain label plugin rollout taint wait
api-versions autoscale config create drain expose logs port-forward run top
[root@kub-k8s-master1 ~]# kubectl

错误整理

#> 如果集群初始化失败:(每个节点都要执行)
$ kubeadm reset -f; ipvsadm –clear; rm -rf ~/.kube
$ systemctl restart kubelet
#> 如果忘记token值
$ kubeadm token create –print-join-command
$ kubeadm init phase upload-certs –upload-certs

五.可视化部署

5.1 官方Dashboard
5.1.1 部署Dashboard

# kubectl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/v2.4.0/aio/deploy/recommended.yaml
# kubectl edit svc kubernetes-dashboard -n kubernetes-dashboard
# 注意将 type: ClusterIP 改为 type: NodePort
# kubectl get svc -A |grep kubernetes-dashboard
kubernetes-dashboard dashboard-metrics-scraper ClusterIP 10.107.179.10 <none> 8000/TCP 38s
kubernetes-dashboard kubernetes-dashboard NodePort 10.110.18.72 <none> 443:32231/TCP 38s

5.1.2 创建访问账号

#创建访问账号,准备一个yaml文件; vi dash.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: admin-user
namespace: kubernetes-dashboard

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: admin-user
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: cluster-admin
subjects:
– kind: ServiceAccount
name: admin-user
namespace: kubernetes-dashboard

# kubectl apply -f dash.yaml

5.1.3 获取访问令牌

#获取访问令牌
# kubectl -n kubernetes-dashboard get secret $(kubectl -n kubernetes-dashboard get sa/admin-user -o jsonpath=\”{.secrets[0].name}\”) -o go-template=\”{{.data.token | base64decode}}\”
eyJhbGciOiJSUzI1NiIsImtpZCI6ImhRa2Q3UDFGempzb3VneVdUS0R0dk50SHlwUHExc0tuT21SOTdWQkczaG8ifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJrdWJlcm5ldGVzLWRhc2hib2FyZCIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VjcmV0Lm5hbWUiOiJhZG1pbi11c2VyLXRva2VuLXBmbGxyIiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZXJ2aWNlLWFjY291bnQubmFtZSI6ImFkbWluLXVzZXIiLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC51aWQiOiJlNDk0MTFhYS0zOTVhLTRkYzUtYmZmYS04MDZiODE3M2VmZWEiLCJzdWIiOiJzeXN0ZW06c2VydmljZWFjY291bnQ6a3ViZXJuZXRlcy1kYXNoYm9hcmQ6YWRtaW4tdXNlciJ9.k3Gd9vRF6gP3Zxy89x14y4I2RCGn232bGLo9A5iEmeMl6BRPdJXZPbwy9fm3OT6ZjVc7LHiRgArczjZuCU3Sis4tIA_24A55h74WQE_JTeiZ5XnSGRknYQRHFSqyBrTaTxgDJb-O-DHol8GQLQjr6gIPzppHc-RhWhUFFNnPVP1nr2MLFBkvIT_qAcbHP6McFf2N6IsYwVFuvyIO77qWcmyFlgSr8a3A0INEJYB2bFPRL82rNc41c0TsUwOguQbJYrDA9lBqVpSff_7Uk_-7ycabZclbZX1HPz2-F59LQW7NWQy7biZw5b25AZaXAG3kL3SDuRRBoMNS92MmDFsVyA

5.1.4 浏览器访问

任意节点ip+端口[上面查看到为32231]
url https://192.168.246.216:32231/
使用token登录

5.2 kuboard 部署

[root@kube-master ~]# kubectl apply -f https://addons.kuboard.cn/kuboard/kuboard-v3.yaml
[root@kube-master ~]# kubectl get pod -n kuboard
NAME READY STATUS RESTARTS AGE
kuboard-agent-2-5c54dcb98f-4vqvc 1/1 Running 24 (7m50s ago) 16d
kuboard-agent-747b97fdb7-j42wr 1/1 Running 24 (7m34s ago) 16d
kuboard-etcd-ccdxk 1/1 Running 16 (8m58s ago) 16d
kuboard-etcd-k586q 1/1 Running 16 (8m53s ago) 16d
kuboard-questdb-bd65d6b96-rgx4x 1/1 Running 10 (8m53s ago) 16d
kuboard-v3-5fc46b5557-zwnsf 1/1 Running 12 (8m53s ago) 16d
[root@kube-master ~]# kubectl get svc -n kuboard
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kuboard-v3 NodePort 10.96.25.125 <none> 80:30080/TCP,10081:30081/TCP,10081:30081/UDP 16d

5.3 kubesphere 部署

官方文档:

在 Kubernetes 上最小化安装 KubeSphere

# 运行安装
[root@kube-master ~]# kubectl apply -f https://github.com/kubesphere/ks-installer/releases/download/v3.4.0/kubesphere-installer.yaml
[root@kube-master ~]# kubectl apply -f https://github.com/kubesphere/ks-installer/releases/download/v3.4.0/cluster-configuration.yaml
# 查看日志
[root@kube-master ~]# kubectl logs -n kubesphere-system $(kubectl get pod -n kubesphere-system -l \’app in (ks-install, ks-installer)\’ -o jsonpath=\'{.items[0].metadata.name}\’) -f

卸载kubesphere

[root@kube-master ~]# curl https://raw.githubusercontent.com/kubesphere/ks-installer/release-3.4/scripts/kubesphere-delete.sh | sh

5.4 rainbond 部署

Rainbond

[root@kube-master ~]# curl -o install.sh https://get.rainbond.com && bash ./install.sh
RO
[root@kube-master ~]# helm repo add rainbond https://openchart.goodrain.com/goodrain/rainbond
[root@kube-master ~]# helm repo update
[root@kube-master ~]# kubectl create namespace rbd-system

卸载

如果是基于helm创建的rainbond
[root@kube-master ~]# helm uninstall rainbond -n rbd-system
如果是基于官方脚本创建的rainbond
# Delete PVC
[root@kube-master ~]# kubectl get pvc -n rbd-system | grep -v NAME | awk \'{print $1}\’ | xargs kubectl delete pvc -n rbd-system
# Delete PV
[root@kube-master ~]# kubectl get pv | grep rbd-system | grep -v NAME | awk \'{print $1}\’ | xargs kubectl delete pv
# Delete CRD
[root@kube-master ~]# kubectl delete crd componentdefinitions.rainbond.io \\
helmapps.rainbond.io \\
rainbondclusters.rainbond.io \\
rainbondpackages.rainbond.io \\
rainbondvolumes.rainbond.io \\
rbdcomponents.rainbond.io \\
servicemonitors.monitoring.coreos.com \\
thirdcomponents.rainbond.io \\
rbdabilities.rainbond.io \\
rbdplugins.rainbond.io \\
servicemeshclasses.rainbond.io \\
servicemeshes.rainbond.io \\
-n rbd-system
# Delete NAMESPACE
[root@kube-master ~]# kubectl delete ns rbd-system

六.集群常用指令

6.1 基础控制指令

# 查看对应资源: 状态
$ kubectl get <SOURCE_NAME> -n <NAMESPACE> -o wide
# 查看对应资源: 事件信息
$ kubectl describe <SOURCE_NAME> <SOURCE_NAME_RANDOM_ID> -n <NAMESPACE>
# 查看pod资源: 日志
$ kubectl logs -f <SOURCE_NAME_RANDOM_ID> [CONTINER_NAME] -n <NAMESPACE>
# 创建资源: 根据资源清单
$ kubectl apply[or create] -f <SOURCE_FILENAME>.yaml
# 删除资源: 根据资源清单
$ kubectl delete -f <SOURCE_FILENAME>.yaml
# 修改资源: 根据反射出的etcd中的配置内容, 生产中不允许该项操作, 且命令禁止
$ kubectl edit <SOURCE_NAME> <SOURCE_NAME_RANDOM_ID> -n <NAMESPACE>

6.2 命令实践

# 查看node状态
$ kubectl get node # -o wide 显示更加详细的信息
# 查看service对象
$ kubectl get svc
# 查看kube-system名称空间内的Pod
$ kubectl get pod -n kube-system
# 查看所有名称空间内的pod
$ kubectl get pod -A
# 查看集群信息
$ kubectl cluster-info
# 查看各组件信息
$ kubectl -s https://api-server:6443 get componentstatuses
# 查看各资源对象对应的api版本
$ kubectl explain pod
# 查看帮助信息
$ kubectl explain deployment
$ kubectl explain deployment.spec
$ kubectl explain deployment.spec.replicas

6.3 备注

问题一 查看各组件信息,可能会发现错误
$ kubectl -s https://192.168.96.143:6443 get componentstatuses
Warning: v1 ComponentStatus is deprecated in v1.19+
NAME STATUS MESSAGE ERROR
scheduler Unhealthy Get \”http://127.0.0.1:10251/healthz\”: dial tcp 127.0.0.1:10251: connect: connection refused
controller-manager Unhealthy Get \”http://127.0.0.1:10252/healthz\”: dial tcp 127.0.0.1:10252: connect: connection refused
etcd-0 Healthy {\”health\”:\”true\”}
问题一解决
$ vim /etc/kubernetes/manifests/kube-scheduler.yaml
10 spec:
11 containers:
12 – command:
13 – kube-scheduler
14 – –authentication-kubeconfig=/etc/kubernetes/scheduler.conf
15 – –authorization-kubeconfig=/etc/kubernetes/scheduler.conf
16 – –bind-address=127.0.0.1
17 – –kubeconfig=/etc/kubernetes/scheduler.conf
18 – –leader-elect=true
19 – –port=0 # 将此行注释或删除

$ vim /etc/kubernetes/manifests/kube-controller-manager.yaml
10 spec:
11 containers:
12 – command:
13 – kube-controller-manager
14 – –allocate-node-cidrs=true
15 – –authentication-kubeconfig=/etc/kubernetes/controller-manager.conf
16 – –authorization-kubeconfig=/etc/kubernetes/controller-manager.conf
17 – –bind-address=127.0.0.1
18 – –client-ca-file=/etc/kubernetes/pki/ca.crt
19 – –cluster-cidr=10.244.0.0/16
20 – –cluster-name=kubernetes
21 – –cluster-signing-cert-file=/etc/kubernetes/pki/ca.crt
22 – –cluster-signing-key-file=/etc/kubernetes/pki/ca.key
23 – –controllers=*,bootstrapsigner,tokencleaner
24 – –kubeconfig=/etc/kubernetes/controller-manager.conf
25 – –port=0 # 将此行注释或删除

$ systemctl restart kubelet

$ kubectl -s https://192.168.96.143:6443 get componentstatuses
Warning: v1 ComponentStatus is deprecated in v1.19+
NAME STATUS MESSAGE ERROR
scheduler Healthy ok
controller-manager Healthy ok
etcd-0 Healthy {\”health\”:\”true\”}

七.Yaml语法解析

YAML是一个类似 XML、JSON 的标记性语言。它强调以数据为中心,并不是以标识语言为重点。因而YAML本身的定义比较简单,号称\”一种人性化的数据格式语言\”。

YAML的语法比较简单,主要有下面几个:
1、大小写敏感
2、使用缩进表示层级关系
3、缩进不允许使用tab,只允许空格( 低版本限制 )
4、缩进的空格数不重要,只要相同层级的元素左对齐即可
5、\’#\’表示注释
YAML支持以下几种数据类型:
1、纯量:单个的、不可再分的值
2、对象:键值对的集合,又称为映射(mapping)/ 哈希(hash) / 字典(dictionary)
3、数组:一组按次序排列的值,又称为序列(sequence) / 列表(list)
补充说明:
1、书写yaml切记: 后面要加一个空格
2、如果需要将多段yaml配置放在一个文件中,中间要使用—分隔

举个例子,通过声明式配置yaml 创建名称空间

$ vim namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
name: webserver

$ kubectl apply -f namespace.yaml
# 如果通过命令行创建
$ kubectl create namespace webserver
# 删除名称空间[注意,这将删除名称空间下的所有资源]
$ kubectl delete namespace webserver

八.k8s中的Pod

Pod 是可以在 Kubernetes 中创建和管理的、最小的可部署的计算单元; Pod 中会启动一个或一组紧密相关的业务容器, 各个业务容器相当于_Pod_ 中的各个进程, 此时就可以将_Pod_ 作为虚拟机看待; 在创建 Pod 时会启动一个init容器, 用来初始化存储和网络, 其余的业务容器都将在init容器启动后启动, 业务容器共享init容器的存储和网络; Pod 只是一个逻辑单元, 并不是真实存在的“主机”, 这种类比主机的概念可以更好的符合现有互联网中几乎所有的虚拟化设计; 像之前运行在虚拟机中的 nginx、mysql、php均可以使用对应的镜像运行出对应的容器在Pod中, 来类比虚拟机中运行这三者;

对于 Pod 而言, 在运行的过程中, k8s为了控制其生命周期的状态, 增加了_容器探测指针_、资源限额、期望状态保持、多容器结合、安全策略设定、控制器受管、故障处理策略 等; Pod在平时是不能够被单独创建的, 而是需要使用控制器对其创建, 这样可以时刻保持Pod的期望状态;

在Kubernetes中所有的资源均可通过命令行参数或者资源清单[yaml/json]的方式进行创建和修改, 但由于Kubernetes属于声明式资源控制集群, 故大多管理Kubernetes集群的方式采用资源配置清单; 资源配置清单可以很好的追溯资源的原型和详细的配置, 且配合版本控制能够完成更好的溯源、回滚、发布等操作; 所以课程中所有的资源创建和修改都会采用资源配置清单的方式, 除部分命令行操作以外;

Pod API 对象

问题:

通过yaml文件创建pod的时候里面有容器,这个文件里面到底哪些属性属于 Pod 对象,哪些属性属于 Container?

解决:

共同特征是,它们描述的是\”机器\”这个整体,而不是里面运行的\”程序\”。

容器可选的设置属性包括:

Pod是 k8s 项目中的最小编排单位。将这个设计落实到 API 对象上,容器(Container)就成了 Pod 属性里一个普通的字段。

把 Pod 看成传统环境里的\”虚拟机机器\”、把容器看作是运行在这个\”机器\”里的\”用户程序\”,那么很多关于 Pod 对象的设计就非常容易理解了。
凡是调度、网络、存储,以及安全相关的属性,基本上是 Pod 级别的

比如:
配置这个\”机器\”的网卡(即:Pod 的网络定义)
配置这个\”机器\”的磁盘(即:Pod 的存储定义)
配置这个\”机器\”的防火墙(即:Pod 的安全定义)
这台\”机器\”运行在哪个服务器之上(即:Pod 的调度)

kind:指定了这个 API 对象的类型(Type),是一个 Pod,根据实际情况,此处资源类型可以是Deployment、Job、Ingress、Service等。
metadata:包含Pod的一些meta信息,比如名称、namespace、标签等信息.
spec:specification of the resource content 指定该资源的内容,包括一些container,storage,volume以及其他Kubernetes需要的参数,以及诸如是否在容器失败时重新启动容器的属性。可在特定Kubernetes API找到完整的Kubernetes Pod的属性。
ˌ
specification—–>[spesɪfɪˈkeɪʃn]

\”name\”: 容器名称
\”image\”: 容器镜像
\”command\”: 容器启动指令
\”args\”: 指令参数
\”workingDir\”: 工作目录
\”ports\”: 容器端口
\”env\”: 环境变量
\”resource\”: 资源限制
\”volumeMounts\”: 卷挂载
\”livenessProbe\”: 存活探针
\”readinessProbe\”: 就绪探针
\”startupProbe\”: 启动探针
\”livecycle\”: 钩子函数
\”terminationMessagePath\”: 容器的异常终止消息的路径,默认在 /dev/termination-log
\”imagePullPolicy\”: 镜像拉去策略
\”securityContext\”: 安全上下文
\”stdin\”: 给容器分配标准输入,类似docker run -i
\”tty\”: 分配终端

8.1 创建一个pod

$ vim nginx.yaml
apiVersion: v1
kind: Pod
metadata:
name: nginx
labels:
app: nginx
spec:
containers:
– name: nginx
image: nginx:1.16.1
ports:
– containerPort: 80

$ kubectl apply -f nginx.yaml

8.2 pod是如何被创建的

step1: kubectl 向 k8s api server 发起一个create pod 请求step2: k8s api server接收到pod创建请求后,不会去直接创建pod;而是生成一个包含创建信息的yaml。step3: apiserver 将刚才的yaml信息写入etcd数据库。step4: scheduler 查看 k8s api,判断:pod.spec.Node == null,若为null,表示这个Pod请求是新来的,需要创建;因此先进行调度计算,找到最适合的node。并更新数据库step5: node节点上的Kubelet通过监听数据库更新,发现有新的任务与自己的node编号匹配,则进行任务创建

8.3 创建一个单容器pod

$ vim mysql.yaml
apiVersion: v1
kind: Pod
metadata:
name: mysql
labels:
name: mysql
spec:
restartPolicy: OnFailure
containers:
– name: mysql
image: mysql:5.7
imagePullPolicy: IfNotPresent
env:
– name: MYSQL_ROOT_PASSWORD
value: \”123456\”
resources:
limits:
memory: \”1024Mi\”
cpu: \”1000m\”
ports:
– containerPort: 3306
nodeSelector:
kubernetes.io/hostname: kub-k8s-node2
# nodeName: kub-k8s-node2
$ kubectl apply -f mysql.yaml
# 字段解析
restartPolicy:
pod 重启策略,可选参数有:
1、Always:Pod中的容器无论如何停止都会自动重启
2、OnFailure: Pod中的容器非正常停止会自动重启
3、Never: Pod中的容器无论怎样都不会自动重启
imagePullPolicy:
镜像拉取策略,可选参数有:
1、Always:总是重新拉取
2、IfNotPresent:默认,如果本地有,则不拉取
3、Never:只是用本地镜像,从不拉取
nodeSelector:
节点选择器:可以指定node的标签,查看标签指令:
nodeName:
节点名称: 可以指定node的名称进行调度
$ kubectl get node –show-labels

8.4 创建一个多容器pod

#vim lnmp.yaml

apiVersion: v1
kind: Pod
metadata:
name: nginx-many
namespace: default
labels:
app: nginx
spec:
restartPolicy: OnFailure
containers:
– name: nginx
image: nginx:1.16.1
imagePullPolicy: IfNotPresent
ports:
– containerPort: 80
– name: php-fpm
image: php:8.0-fpm-alpine
imagePullPolicy: IfNotPresent
ports:
– containerPort: 9000
$ kubectl apply -f lnmp.yaml

# 字段解析
nodeSelector:
节点选择器:可以指定node的标签,查看标签指令:
$ kubectl get node –show-labels

8.4.1 配置节点标签

添加标签
kubectl label nodes node3 name=value
删除标签
kubectl label nodes node3 name-

8.5 Pod容器的交互
8.5.1 创建pod,并做本地解析

vim host-alias.yaml

apiVersion: v1
kind: Pod
metadata:
name: centos
labels:
name: centos
spec:
containers:
– name: centos
image: centos:7
command:
– \”tail\”
– \”-f\”
– \”/dev/null\”
hostAliases:
– ip: \”192.168.100.128\”
hostnames:
– \”master\”
– \”k8s-master\”
– \”apiserver\”
# 字段解析
command:
启动容器时执行的指令,类似于docker run -it 镜像 tail -f /dev/null
hostAliases:
在容器中的/etc/hosts文件中配置本地解析

8.5.2 pod共享进程

[root@kub-k8s-master prome]# kubectl delete -f pod.yml
pod \”website\” deleted
[root@kub-k8s-master prome]# vim pod.yml #修改如下。最好是提前将镜像pull下来。

apiVersion: v1
kind: Pod
metadata:
name: website
labels:
app: website
spec:
shareProcessNamespace: true #共享进程名称空间
containers:
– name: test-web
image: daocloud.io/library/nginx
ports:
– containerPort: 80
– name: busybox
image: daocloud.io/library/busybox
stdin: true
tty: true

2.创建
[root@kub-k8s-master prome]# kubectl apply -f pod.yml
pod/website created

1. 定义了 shareProcessNamespace=true
表示这个 Pod 里的容器要共享进程(PID Namespace)如果是false则为不共享。
2. 定义了两个容器:
一个 nginx 容器
一个开启了 tty 和 stdin 的 busybos 容器
在 Pod 的 YAML 文件里声明开启它们俩,等同于设置了 docker run 里的 -it(-i 即 stdin,-t 即 tty)参数。此 Pod 被创建后,就可以使用 shell 容器的 tty 跟这个容器进行交互了。

我们通过kubectl可以查看到
# kubectl exec -it website -c busybox — ps aux
PID USER TIME COMMAND
1 root 0:00 /pause
7 root 0:00 nginx: master process nginx -g daemon off;
35 101 0:00 nginx: worker process
36 root 0:00 sh
66 root 0:00 ps aux

在busybox中可以查看到nginx的进程

8.5.2 pod共用宿主机namespace

刚才的都是pod里面容器的Namespace,并没有和本机的Namespace做共享,接下来我们可以做与本机的Namespace共享,可以在容器里面看到本机的进程。
[root@kub-k8s-master prome]# kubectl delete -f pod.yml
pod \”website\” deleted
[root@kub-k8s-master prome]# vim pod.yml #修改如下

apiVersion: v1
kind: Pod
metadata:
name: website
labels:
app: website
spec:
hostNetwork: true #共享宿主机网络
hostIPC: true #共享ipc通信
hostPID: true #共享宿主机的pid
containers:
– name: test-web
image: daocloud.io/library/nginx
ports:
– containerPort: 80
– name: busybos
image: daocloud.io/library/busybox
stdin: true
tty: true
创建pod
[root@kub-k8s-master prome]# kubectl apply -f pod.yml
pod/website created

定义了共享宿主机的 Network、IPC 和 PID Namespace。这样,此 Pod 里的所有容器,会直接使用宿主机的网络、直接与宿主机进行 IPC 通信、看到宿主机里正在运行的所有进程。

8.6 钩子函数lifecycle

kubernetes 在主容器启动之后和删除之前提供了两个钩子函数:

post start:容器创建之后执行,如果失败会重启容器pre stop:容器删除之前执行,执行完成之后容器将成功删除,在其完成之前会阻塞删除容器的操作钩子函数有三种定义方式第一种 exec 执行指令第二种 在容器中请求端口第三种 向容器发起http请求

lifecycle:
postStart:
exec:
command:
– cat
– /etc/hosts

lifecycle:
postStart:
tcpSocket:
port: 8080

lifecycle:
postStart:
httpGet:
path: / # URI地址
port: 80 # 端口号
host: 192.168.75.100 # 主机地址
scheme: HTTP

一个钩子函数的示例

$ cat nginx-lifecycle.yaml
apiVersion: v1
kind: Pod
metadata:
name: nginx-lifecycle
namespace: default
labels:
app: nginx
spec:
containers:
– name: nginx-lifecycle
image: nginx:1.16.1
ports:
– containerPort: 80
protocol: TCP
lifecycle:
postStart:
exec:
command: [\”/bin/sh\”, \”-c\”, \”echo \'<h1>this is a nginx-lifecycle test page</h1>\’ > /usr/share/nginx/html/index.html\”]
preStop:
exec:
command: [\”/usr/sbin/nginx\”, \”-s\”, \”quit\”]
$ kubectl apply -f nginx-lifecycle.yaml
pod/pod-lifecycle created

九.容器监控检查及恢复机制

在 k8s 中,可以为 Pod 里的容器定义一个健康检查\”探针\”(Probe)。kubelet 就会根据这个 Probe 的返回值决定这个容器的状态,而不是直接以容器是否运行(来自 Docker 返回的信息)作为依据。这种机制,是生产环境中保证应用健康存活的重要手段。

9.1 命令模式探针

Kubernetes 文档中的例子:

[root@kub-k8s-master1 ~]# cd prome/
[root@kub-k8s-master prome]# vim test-liveness-exec.yaml

apiVersion: v1
kind: Pod
metadata:
labels:
test: liveness
name: test-liveness-exec
spec:
containers:
– name: liveness
image: daocloud.io/library/nginx
args:
– /bin/sh
– -c
– touch /tmp/healthy; sleep 30; rm -rf /tmp/healthy; sleep 50
livenessProbe: #探针,健康检查
exec: #类型
command: #命令
– cat
– /tmp/healthy
initialDelaySeconds: 5 #健康检查,在容器启动5s后开始执行
periodSeconds: 5 #每5s执行一次
它在启动之后做的第一件事是在/tmp目录下创建了一个healthy文件,以此作为自己已经正常运行的标志。而30s过后,它会把这个文件删除掉。
与此同时,定义了一个这样的 livenessProbe(健康检查)。它的类型是 exec,它会在容器启动后,在容器里面执行一句我们指定的命令,比如:\”cat /tmp/healthy\”。这时,如果这个文件存在,这条命令的返回值就是 0,Pod就会认为这个容器不仅已经启动,而且是健康的。这个健康检查,在容器启动5s后开始执行(initialDelaySeconds: 5),每5s执行一次(periodSeconds: 5)。

创建Pod:

[root@kub-k8s-master prome]# kubectl apply -f test-liveness-exec.yaml
pod/test-liveness-exec created

查看 Pod 的状态:

[root@kub-k8s-master prome]# kubectl get pod
NAME READY STATUS RESTARTS AGE
nginx-configmap 1/1 Running 0 16h
nginx-pod 1/1 Running 0 12h
test-liveness-exec 1/1 Running 0 75s

由于已经通过了健康检查,这个 Pod 就进入了 Running 状态。

然后30 s 之后,再查看一下 Pod 的 Events:

[root@kub-k8s-master prome]# kubectl describe pod test-liveness-exec

发现,这个 Pod 在 Events 报告了一个异常:

Events:
Type Reason Age From Message
—- —— —- —- ——-
Warning Unhealthy 54s (x9 over 3m34s) kubelet, kub-k8s-node1 Liveness probe failed: cat: /tmp/healthy: No such file or directory

这个健康检查探查到 /tmp/healthy 已经不存在了,所以它报告容器是不健康的。那么接下来会发生什么呢?

再次查看一下这个 Pod 的状态:

[root@kub-k8s-master prome]# kubectl get pod test-liveness-exec
NAME READY STATUS RESTARTS AGE
test-liveness-exec 1/1 Running 4 5m19s

这时发现,Pod 并没有进入 Failed 状态,而是保持了 Running 状态。这是为什么呢?

RESTARTS 字段从 0 到 1 的变化,就明白原因了:这个异常的容器已经被 Kubernetes 重启了。在这个过程中,Pod 保持 Running 状态不变。
#注
k8s 中并没有 Docker 的 Stop 语义。所以如果容器被探针检测到有问题,查看状态虽然看到的是 Restart,但实际却是重新创建了容器。

这个功能就是 Kubernetes 里的Pod 恢复机制,也叫 restartPolicy。它是 Pod 的 Spec 部分的一个标准字段(pod.spec.restartPolicy),默认值是 Always,即:任何时候这个容器发生了异常,它一定会被重新创建。

小提示:

Pod 的恢复过程,永远都是发生在当前节点上,而不会跑到别的节点上去。事实上,一旦一个 Pod 与一个节点(Node)绑定,除非这个绑定发生了变化(pod.spec.node 字段被修改),否则它永远都不会离开这个节点。这也就意味着,如果这个宿主机宕机了,这个 Pod 也不会主动迁移到其他节点上去。

9.2 http get方式探针

[root@kub-k8s-master prome]# vim liveness-httpget.yaml

apiVersion: v1
kind: Pod
metadata:
name: liveness-httpget-pod
namespace: default
spec:
containers:
– name: liveness-exec-container
image: daocloud.io/library/nginx
imagePullPolicy: IfNotPresent
ports:
– name: http
containerPort: 80
livenessProbe: #探针,健康检查
httpGet:
port: http
path: /index.html
initialDelaySeconds: 1
periodSeconds: 3

创建该pod

[root@kub-k8s-master prome]# kubectl create -f liveness-httpget.yaml
pod/liveness-httpget-pod created

查看当前pod的状态

[root@kub-k8s-master prome]# kubectl describe pod liveness-httpget-pod

Liveness: http-get http://:http/index.html delay=1s timeout=1s period=3s #success=1 #failure=3

登陆容器

测试将容器内的index.html删除掉
[root@kub-k8s-master prome]# kubectl exec -it liveness-httpget-pod /bin/bash
root@liveness-httpget-pod:/# mv /usr/share/nginx/html/index.html index.html
root@liveness-httpget-pod:/# command terminated with exit code 137
可以看到,当把index.html移走后,这个容器立马就退出了。

此时,查看pod的信息

[root@kub-k8s-master prome]# kubectl describe pod liveness-httpget-pod

Normal Killing 49s kubelet, kub-k8s-node2 Container liveness-exec-container failed liveness probe, will be restarted
Normal Pulled 49s kubelet, kub-k8s-node2 Container image \”daocloud.io/library/nginx\” already present on machine

看输出,容器由于健康检查未通过,pod会被杀掉,并重新创建

[root@kub-k8s-master prome]# kubectl get pods
NAME READY STATUS RESTARTS AGE
lifecycle-demo 1/1 Running 1 34h
liveness-httpget-pod 1/1 Running 1 5m42s
#restarts 为 1

重新登陆容器,发现index.html又出现了,证明容器是被重拉了。

[root@kub-k8s-master prome]# kubectl exec -it liveness-httpget-pod /bin/bash
root@liveness-httpget-pod:/# cat /usr/share/nginx/html/index.html

9.3 POD 的恢复策略

Pod 恢复策略:
可以通过设置 restartPolicy,改变 Pod 的恢复策略。一共有3种:
1. Always: 在任何情况下,只要容器不在运行状态,就自动重启容器;
2. OnFailure: 只在容器 异常时才自动重启容器;
3. Never: 从来不重启容器。
实际使用时,需要根据应用运行的特性,合理设置这三种恢复策略。

十.投射数据卷 Projected Volume

注:Projected Volume 是 Kubernetes v1.11 之后的新特性

10.1 什么是Projected Volume

在 k8s 中,有几种特殊的 Volume,它们的意义不是为了存放容器里的数据,\”而是为容器提供预先定义好的数据。\”
从容器的角度来看,这些 Volume 里的信息仿佛是被 k8s \”投射\”(Project)进入容器当中的。

10.2 k8s 支持的 Projected Volume 方式

Secret
ConfigMap
Downward API

十一.Secret 实现

11.1 secret  详解

secret用来保存小片敏感数据的k8s资源,例如密码,token,或者秘钥。这类数据当然也可以存放在Pod或者镜像中,但是放在Secret中是为了更方便的控制如何使用数据,并减少暴露的风险。

用户可以创建自己的secret,系统也会有自己的secret。
Pod需要先引用才能使用某个secret

Pod使用secret方式:

作为volume的一个域被一个或多个容器挂载

內建的Secrets:

由ServiceAccount创建的API证书附加的秘钥k8s自动生成的用来访问apiserver的Secret,所有Pod会默认使用这个Secret与apiserver通信

创建自己的Secret:

方式1:使用kubectl create secret命令
方式2:yaml文件创建Secret

11.2 secret 使用

创建Secret

假如某个Pod要访问数据库,需要用户名密码,现在我们分别设置这个用户名和密码
Secret 对象要求这些数据必须是经过 Base64 转码的,以免出现明文密码显示的安全隐患。

创建一个secret.yaml文件,内容用base64编码:明文显示容易被别人发现,这里先转码。
[root@kub-k8s-master1 ~]# echo -n \’admin\’ | base64
YWRtaW4=
[root@kub-k8s-master1 ~]# echo -n \’1f2d1e2e67df\’ | base64
MWYyZDFlMmU2N2Rm

创建一个secret.yaml文件,内容用base64编码

[root@kub-k8s-master prome]# vim secret.yml

apiVersion: v1
kind: Secret
metadata:
name: mysecret
type: Opaque #模糊
data:
username: YWRtaW4=
password: MWYyZDFlMmU2N2Rm

创建:

[root@kub-k8s-master prome]# kubectl apply -f secret.yml
secret/mysecret created

解析Secret中内容,还是经过编码的—需要解码

查看secret
[root@kub-k8s-master1 ~]# kubectl get secrets
NAME TYPE DATA AGE
default-token-7vc82 kubernetes.io/service-account-token 3 30h
mysecret Opaque 2 6s
查看secret详细信息
[root@kub-k8s-master prome]# kubectl get secret mysecret -o yaml
apiVersion: v1
data:
password: MWYyZDFlMmU2N2Rm
username: YWRtaW4=
kind: Secret
metadata:
creationTimestamp: \”2019-10-21T03:07:56Z\”
name: mysecret
namespace: default
resourceVersion: \”162855\”
selfLink: /api/v1/namespaces/default/secrets/mysecret
uid: 36bcd07d-92eb-4755-ac0a-a5843ed986dd
type: Opaque

手动base64解码方式:

[root@kub-k8s-master1 ~]# echo \’MWYyZDFlMmU2N2Rm\’ | base64 –decode

11.3 使用Secret

secret可以作为数据卷挂载或者作为环境变量暴露给Pod中的容器使用,也可以被系统中的其他资源使用。

一个Pod中引用Secret的列子:

创建一个Secret,多个Pod可以引用同一个Secret
修改Pod的定义,在spec.volumes[]加一个volume,给这个volume起个名字,spec.volumes[].secret.secretName记录的是要引用的Secret名字

[root@kub-k8s-master prome]# vim pod_use_secret.yaml
apiVersion: v1
kind: Pod
metadata:
name: mypod
spec:
containers:
– name: testredis
image: daocloud.io/library/redis
volumeMounts: #挂载一个卷
– name: foo #这个名字需要与定义的卷的名字一致
mountPath: \”/etc/foo\” #挂载到容器里哪个目录下,随便写
readOnly: true
volumes: #数据卷的定义
– name: foo #卷的名字这个名字自定义
secret: #卷是直接使用的secret。
secretName: mysecret #调用刚才定义的secret

创建:
[root@kub-k8s-master prome]# kubectl apply -f pod_use_secret.yaml
pod/mypod created
[root@kub-k8s-master prome]# kubectl exec -it mypod /bin/bash
root@mypod:/data# cd /etc/foo/
root@mypod:/etc/foo# ls
password username
root@mypod:/etc/foo# cat password
1f2d1e2e67df

结果中看到,保存在 Etcd 里的用户名和密码信息,已经以文件的形式出现在了容器的 Volume 目录里。
而这个文件的名字,就是 kubectl create secret 指定的 Key,或者说是 Secret 对象的 data 字段指定的 Key。

每一个被引用的Secret都要在spec.volumes中定义
如果Pod中的多个容器都要引用这个Secret那么每一个容器定义中都要指定自己的volumeMounts,但是Pod定义中声明一次spec.volumes就好了。

映射secret key到指定的路径

可以控制secret key被映射到容器内的路径,利用spec.volumes[].secret.items来修改被映射的具体路径

[root@kub-k8s-master prome]# kubectl delete -f pod_use_secret.yaml
pod \”mypod\” deleted
[root@kub-k8s-master prome]# vim pod_use_secret.yaml

apiVersion: v1
kind: Pod
metadata:
name: mypod
spec:
containers:
– name: testredis
image: daocloud.io/library/redis
volumeMounts:
– name: foo
mountPath: \”/etc/foo\”
readOnly: true
volumes:
– name: foo
secret:
secretName: mysecret
items: #定义一个items
– key: username #将那个key重新定义到那个目录下
path: my-group/my-username #相对路径,相对于/etc/foo的路径

2.创建
[root@kub-k8s-master prome]# kubectl apply -f pod_use_secret.yaml
pod/mypod created
3.从volume中读取secret的值
[root@kub-k8s-master prome]# kubectl exec -it mypod /bin/bash
root@mypod:/data# cd /etc/foo/my-group
root@mypod:/etc/foo/my-group# ls
my-username
root@mypod:/etc/foo/my-group# cat my-username
admin
root@mypod:/etc/foo/my-group#

username被映射到了文件/etc/foo/my-group/my-username而不是/etc/foo/username,而password没有被使用,这种方式每个key的调用需要单独用key像username一样调用

被挂载的secret内容自动更新

也就是如果修改一个Secret的内容,那么挂载了该Secret的容器中也将会取到更新后的值,但是这个时间间隔是由kubelet的同步时间决定的。

1.设置base64加密
[root@kub-k8s-master prome]# echo qianfeng | base64
cWlhbmZlbmcK
2.将admin替换成qianfeng
[root@kub-k8s-master prome]# vim secret.yml

apiVersion: v1
kind: Secret
metadata:
name: mysecret
type: Opaque
data:
username: cWlhbmZlbmcK #修改为qianfeng的base64加密后的
password: MWYyZDFlMmU2N2Rm

1.创建
[root@kub-k8s-master prome]# kubectl apply -f secret.yml
Warning: kubectl apply should be used on resource created by either kubectl create –save-config or kubectl apply
secret/mysecret configured
2.连接pod容器
[root@kub-k8s-master prome]# kubectl exec -it mypod /bin/bash
root@mypod:/data# cd /etc/foo/my-group
root@mypod:/etc/foo/my-group# ls
my-username
root@mypod:/etc/foo/my-group# cat my-username
qianfeng

以环境变量的形式使用Secret

[root@kub-k8s-master prome]# kubectl delete -f pod_use_secret.yaml
pod \”mypod\” deleted
[root@kub-k8s-master prome]# vim pod_use_secret.yaml

apiVersion: v1
kind: Pod
metadata:
name: mypod
spec:
containers:
– name: testredis
image: daocloud.io/library/redis
env: #定义环境变量
– name: SECRET_USERNAME #创建新的环境变量名称
valueFrom:
secretKeyRef: #调用的key是什么
name: mysecret #变量的值来自于mysecret
key: username #username里面的值
2.创建使用secret的pod容器
[root@kub-k8s-master prome]# kubectl apply -f pod_use_secret.yaml
pod/mypod created
3.连接
[root@kub-k8s-master prome]# kubectl exec -it mypod /bin/bash
root@mypod:/data# echo $SECRET_USERNAME #打印一下定义的变量
qianfeng

11.4 实战案例

1.创建数据库用户的密码secret
[root@kub-k8s-master test]# echo -n \’QianFeng@123!\’ | base64
UWlhbkZlbmdAMTIzIQ==
[root@kub-k8s-master test]# cat secret.yml
apiVersion: v1
kind: Secret
metadata:
name: mysql-secret
type: Opaque
data:
password: UWlhbkZlbmdAMTIzIQ==

[root@kub-k8s-master test]# kubectl apply -f secret.yml
2.创建数据库并使用secret
[root@kub-k8s-master test]# cat mysql.yaml
apiVersion: v1
kind: Pod
metadata:
name: my-mysql
spec:
containers:
– name: mysql
image: daocloud.io/library/mysql:5.7
ports:
– containerPort: 3306
env:
– name: MYSQL_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: mysql-secret
key: password
[root@kub-k8s-master test]# kubectl apply -f myslq.yaml
[root@kub-k8s-master test]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
my-mysql 1/1 Running 0 2m47s 10.244.2.13 node2 <none> <none>
测试:
[root@kub-k8s-master test]# mysql -uroot -p\’QianFeng@123!\’ -h 10.244.2.13 -P3306

十二.ConfigMap祥解

ConfigMap与 Secret 类似,用来存储配置文件的kubernetes资源对象,所有的配置内容都存储在etcd中。

与 Secret 的区别:

ConfigMap 保存的是不需要加密的、应用所需的配置信息。
ConfigMap 的用法几乎与 Secret 完全相同:可以使用 kubectl create configmap 从文件或者目录创建 ConfigMap,也可以直接编写 ConfigMap 对象的 YAML 文件。

12.1 创建ConfigMap的方式

创建ConfigMap的方式有4种:
命令行方式
方式1:通过直接在命令行中指定configmap参数创建,即–from-literal
方式2:通过指定文件创建,即将一个配置文件创建为一个ConfigMap,–from-file=<文件>
方式3:通过指定目录创建,即将一个目录下的所有配置文件创建为一个ConfigMap,–from-file=<目录>
配置文件方式
方式4:事先写好标准的configmap的yaml文件,然后kubectl create -f 创建

12.2 通过命令行参数创建

创建命令:

[root@kub-k8s-master prome]# kubectl create configmap test-configmap –from-literal=user=admin –from-literal=pass=1122334
configmap/test-configmap created

结果如下面的data内容所示:

[root@kub-k8s-master prome]# kubectl get configmap test-configmap -o yaml
apiVersion: v1
data:
pass: \”1122334\”
user: admin
kind: ConfigMap
metadata:
creationTimestamp: \”2019-10-21T07:48:15Z\”
name: test-configmap
namespace: default
resourceVersion: \”187590\”
selfLink: /api/v1/namespaces/default/configmaps/test-configmap
uid: 62a8a0d0-fab9-4159-86f4-a06aa213f4b1

12.3 通过指定文件创建

编辑文件server.conf内容如下:

[root@kub-k8s-master prome]# vim server.conf
server {
listen 80;
server_name localhost;
location / {
root /var/www/html;
index index.html index.htm;
}
}

创建(可以有多个–from-file):

[root@kub-k8s-master prome]# kubectl create configmap test-config2 –from-file=server.conf
configmap/test-config2 created

结果如下面data内容所示:

[root@kub-k8s-master prome]# kubectl get configmap test-config2 -o yaml
apiVersion: v1
data:
server.conf: |
server {
listen 80;
server_name localhost;
localtion / {
root /var/www/html;
index index.html index.htm;
}
}
kind: ConfigMap
metadata:
creationTimestamp: \”2019-10-21T08:01:43Z\”
name: test-config2
namespace: default
resourceVersion: \”188765\”
selfLink: /api/v1/namespaces/default/configmaps/test-config2
uid: 790fca12-3900-4bf3-a017-5af1070792e5

通过指定文件创建时,configmap会创建一个key/value对,key是文件名,value是文件内容。

12.4 指定目录创建

configs 目录下的config-1和config-2内容如下所示:

[root@kub-k8s-master prome]# mkdir config
[root@kub-k8s-master prome]# cd config/
[root@kub-k8s-master config]# vim config1
aaa
bbb
c=d
[root@kub-k8s-master config]# vim config2
eee
fff
h=k

创建:

[root@kub-k8s-master config]# cd ..
[root@kub-k8s-master prome]# kubectl create configmap test-config3 –from-file=./config
configmap/test-config3 created

结果下面data内容所示:

[root@kub-k8s-master prome]# kubectl get configmap test-config3 -o yaml
apiVersion: v1
data:
config1: |
aaa
bbb
c=d
config2: |
eee
fff
h=k
kind: ConfigMap
metadata:
creationTimestamp: \”2019-10-21T08:20:42Z\”
name: test-config3
namespace: default
resourceVersion: \”190420\”
selfLink: /api/v1/namespaces/default/configmaps/test-config3
uid: 6e00fded-80a8-4297-aeb3-4c48795e6eb9

指定目录创建时,configmap内容中的各个文件会创建一个key/value对,key是文件名,value是文件内容。

12.5 通过yaml文件创建

yaml文件内容如下: 注意其中一个key的value有多行内容时的写法

[root@kub-k8s-master prome]# vim configmap.yaml

apiVersion: v1
kind: ConfigMap
metadata:
name: test-config4
namespace: default
data:
cache_host: memcached-gcxt
cache_port: \”11211\”
cache_prefix: gcxt
my.cnf: |
[mysqld]
log-bin = mysql-bin
haha = hehe

创建:

[root@kub-k8s-master prome]# kubectl apply -f configmap.yaml
configmap/test-config4 created

结果如下面data内容所示:

[root@kub-k8s-master prome]# kubectl get configmap test-config4 -o yaml
apiVersion: v1
data:
cache_host: memcached-gcxt
cache_port: \”11211\”
cache_prefix: gcxt
my.cnf: |
[mysqld]
log-bin = mysql-bin
haha = hehe
kind: ConfigMap
metadata:
annotations:
kubectl.kubernetes.io/last-applied-configuration: |
{\”apiVersion\”:\”v1\”,\”data\”:{\”cache_host\”:\”memcached-gcxt\”,\”cache_port\”:\”11211\”,\”cache_prefix\”:\”gcxt\”,\”my.cnf\”:\”[mysqld]\\nlog-bin = mysql-bin\\nhaha = hehe\\n\”},\”kind\”:\”ConfigMap\”,\”metadata\”:{\”annotations\”:{},\”name\”:\”test-config4\”,\”namespace\”:\”default\”}}
creationTimestamp: \”2019-10-21T08:30:24Z\”
name: test-config4
namespace: default
resourceVersion: \”191270\”
selfLink: /api/v1/namespaces/default/configmaps/test-config4
uid: 2a8cd6e7-db2c-4781-b005-e0b76d26394b

查看configmap的详细信息:

# kubectl describe configmap

12.6 使用ConfigMap

使用ConfigMap的方式,一种是通过环境变量的方式,直接传递pod,另一种是使用volume的方式挂载入到pod内

示例ConfigMap文件:

[root@kub-k8s-master prome]# vim config-map.yml

apiVersion: v1
kind: ConfigMap
metadata:
name: config-map
namespace: default
data:
special.how: very
special.type: charm
创建
[root@kub-k8s-master prome]# kubectl apply -f config-map.yml
configmap/config-map created

12.6.1 通过变量使用

(1) 使用valueFrom、configMapKeyRef、name、key指定要用的key:

1.设置指定变量的方式
[root@kub-k8s-master prome]# vim testpod.yml

apiVersion: v1
kind: Pod
metadata:
name: dapi-test-pod
spec:
containers:
– name: test-container
image: daocloud.io/library/nginx
env: #专门在容器里面设置变量的关键字
– name: SPECIAL_LEVEL_KEY #这里的-name,是容器里设置的新变量的名字
valueFrom:
configMapKeyRef:
name: config-map #这里是来源于哪个configMap
key: special.how #configMap里的key
– name: SPECIAL_TYPE_KEY
valueFrom:
configMapKeyRef:
name: config-map
key: special.type
restartPolicy: Never
创建pod
[root@kub-k8s-master prome]# kubectl apply -f testpod.yml
pod/dapi-test-pod created

测试:

[root@kub-k8s-master prome]# kubectl exec -it dapi-test-pod /bin/bash
root@dapi-test-pod:/# echo $SPECIAL_TYPE_KEY
charm

(2) 通过envFrom、configMapRef、name使得configmap中的所有key/value对儿  都自动变成环境变量:

[root@kub-k8s-master prome]# kubectl delete -f testpod.yml
pod \”dapi-test-pod\” deleted
[root@kub-k8s-master prome]# cp testpod.yml testpod.yml.bak
[root@kub-k8s-master prome]# vim testpod.yml

apiVersion: v1
kind: Pod
metadata:
name: dapi-test-pod
spec:
containers:
– name: test-container
image: daocloud.io/library/nginx
envFrom:
– configMapRef:
name: config-map
restartPolicy: Never

这样容器里的变量名称直接使用configMap里的key名:

[root@kub-k8s-master prome]# kubectl apply -f testpod.yml
pod/dapi-test-pod created.
[root@kub-k8s-master prome]# kubectl exec -it dapi-test-pod /bin/bash
root@dapi-test-pod:/# env
HOSTNAME=dapi-test-pod
NJS_VERSION=0.3.3
NGINX_VERSION=1.17.1
KUBERNETES_PORT_443_TCP_PROTO=tcp
KUBERNETES_PORT_443_TCP_ADDR=10.96.0.1
PKG_RELEASE=1~stretch
KUBERNETES_PORT=tcp://10.96.0.1:443
PWD=/
special.how=very
HOME=/root
KUBERNETES_SERVICE_PORT_HTTPS=443
KUBERNETES_PORT_443_TCP_PORT=443
KUBERNETES_PORT_443_TCP=tcp://10.96.0.1:443
TERM=xterm
SHLVL=1
KUBERNETES_SERVICE_PORT=443
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
special.type=charm
KUBERNETES_SERVICE_HOST=10.96.0.1
_=/usr/bin/env

12.6.2 作为volume挂载使用

(1) 把1.4中test-config4所有key/value挂载进来:

[root@kub-k8s-master prome]# kubectl delete -f testpod.yml
pod \”dapi-test-pod\” deleted
[root@kub-k8s-master prome]# vim volupod.yml

apiVersion: v1
kind: Pod
metadata:
name: nginx-configmap
spec:
containers:
– name: nginx-configmap
image: daocloud.io/library/nginx
volumeMounts:
– name: config-volume4
mountPath: \”/tmp/config4\”
volumes:
– name: config-volume4
configMap:
name: test-config4

创建pod
[root@kub-k8s-master prome]# kubectl apply -f volupod.yml
pod/nginx-configmap created

进入容器中/tmp/config4查看:

[root@kub-k8s-master prome]# kubectl exec -it nginx-configmap /bin/bash
root@nginx-configmap:/# ls /tmp/config4/
cache_host cache_port cache_prefix my.cnf
root@nginx-configmap:/# cat /tmp/config4/cache_host
memcached-gcxt
root@nginx-configmap:/#

可以看到,在config4文件夹下以每一个key为文件名,value为内容,创建了多个文件。

12.7 实战案例

创建configmap
[root@kub-k8s-master configmap]# vim configmap.yaml

apiVersion: v1
kind: ConfigMap
metadata:
name: nginx-server-conf
namespace: default
data:
index.html: |
Hello, cloud computing
Hello, Mr. Wang

[root@kub-k8s-master configmap]# kubectl get configmap
NAME DATA AGE
nginx-server-conf 2 7s
[root@kub-k8s-master configmap]# kubectl get configmap nginx-server-conf -o yaml
使用configmap
[root@kub-k8s-master configmap]# vim pod.yaml

apiVersion: v1
kind: Pod
metadata:
name: test-webapp
spec:
containers:
– name: nginx-app
image: daocloud.io/library/nginx
ports:
– containerPort: 80
volumeMounts:
– name: nginx-volume
mountPath: \”/usr/share/nginx/html\”
volumes:
– name: nginx-volume
configMap:
name: nginx-server-conf
[root@kub-k8s-master configmap]# kubectl apply -f pod.yaml
[root@kub-k8s-master configmap]# kubectl get pod
NAME READY STATUS RESTARTS AGE
test-webapp 1/1 Running 0 6s
[root@kub-k8s-master configmap]# kubectl exec -it test-webapp /bin/bash
root@test-webapp:/# cd /usr/share/nginx/html/
root@test-webapp:/usr/share/nginx/html# ls
index.html
root@test-webapp:/usr/share/nginx/html# cat index.html
Hello, cloud computing
Hello, Mr. Wang
[root@kub-k8s-master configmap]# curl 10.244.2.25
Hello, cloud computing
Hello, Mr. Wang

十三.Downward API

Downward API
用于在容器中获取 POD 的基本信息,kubernetes原生支持
Downward API提供了两种方式用于将 POD 的信息注入到容器内部:
1.环境变量:用于单个变量,可以将 POD 信息直接注入容器内部。
2.Volume挂载:将 POD 信息生成为文件,直接挂载到容器内部中去。

13.1 目前 Downward API 支持的字段

1. 使用 fieldRef 可以声明使用:
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[\'<KEY>\’] – 指定 <KEY> 的 Label 值
metadata.annotations[\'<KEY>\’] – 指定 <KEY> 的 Annotation 值
metadata.labels – Pod 的所有 Label
metadata.annotations – Pod 的所有 Annotation
上面这个列表的内容,随着 Kubernetes 项目的发展肯定还会不断增加。所以这里列出来的信息仅供参考,在使用 Downward API 时,还是要记得去查阅一下官方文档。
所有基本信息可以使用下面的方式去查看(describe方式看不出来):
[root@kub-k8s-master configmap]# kubectl get pod test-webapp -o yaml
apiVersion: v1
kind: Pod
metadata:
annotations:
kubectl.kubernetes.io/last-applied-configuration: |
{\”apiVersion\”:\”v1\”,\”kind\”:\”Pod\”,\”metadata\”:{\”annotations\”:{},\”name\”:\”test-webapp\”,\”namespace\”:\”default\”},\”spec\”:{\”containers\”:[{\”image\”:\”daocloud.io/library/nginx\”,\”name\”:\”nginx-app\”,\”volumeMounts\”:[{\”mountPath\”:\”/usr/share/nginx/html\”,\”name\”:\”nginx-volume\”}]}],\”volumes\”:[{\”configMap\”:{\”name\”:\”nginx-server-conf\”},\”name\”:\”nginx-volume\”}]}}
creationTimestamp: \”2021-02-21T09:44:51Z\”
name: test-webapp
namespace: default
resourceVersion: \”270687\”
selfLink: /api/v1/namespaces/default/pods/test-webapp
uid: ed92d685-f800-464f-95dc-d6aa5f92fc9c
……

13.2 实战案例

使用fieldRef获取 POD 的基本信息,以环境变量的方式实现

[root@kub-k8s-master prome]# vim test-env-pod.yml

apiVersion: v1
kind: Pod
metadata:
name: test-env-pod
namespace: kube-system
spec:
containers:
– name: test-env-pod
image: daocloud.io/library/nginx
env:
– name: POD_NAME #第一个环境变量的名字
valueFrom: #使用valueFrom方式设置
fieldRef: #关联一个字段metadata.name
fieldPath: metadata.name #这个字段从当前运行的pod详细信息查看
– name: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
– name: POD_IP
valueFrom:
fieldRef:
fieldPath: status.podIP

注意: POD 的 name 和 namespace 属于元数据,是在 POD 创建之前就已经定下来了的,所以使用 metadata 获取就可以了,但是对于 POD 的 IP 则不一样,因为POD IP 是不固定的,POD 重建了就变了,它属于状态数据,所以使用 status 去获取。

创建上面的 POD:

[root@kub-k8s-master prome]# kubectl apply -f test-env-pod.yml
pod/test-env-pod created

POD 创建成功后,查看:

[root@kub-k8s-master prome]# kubectl exec -it test-env-pod /bin/bash -n kube-system
root@test-env-pod:/# env | grep POD
POD_NAME=test-env-pod
POD_NAMESPACE=kube-system
POD_IP=10.244.1.35
root@test-env-pod:/#

Volume挂载

通过Downward API将 POD 的 Label、等信息通过 Volume 以文件的形式挂载到容器的某个文件中去,然后在容器中打印出该文件的值来验证。

[root@kub-k8s-master prome]# vim test-volume-pod.yaml

apiVersion: v1
kind: Pod
metadata:
name: test-volume-pod
namespace: kube-system
labels:
k8s-app: test-volume
node-env: test
spec:
containers:
– name: test-volume-pod-container
image: daocloud.io/library/nginx
volumeMounts:
– name: podinfo
mountPath: /etc/podinfo
volumes:
– name: podinfo
downwardAPI:
items:
– path: \”labels\”
fieldRef:
fieldPath: metadata.labels

创建上面的 POD :

[root@kub-k8s-master prome]# kubectl apply -f test-volume-pod.yaml pod/test-volume-pod created
[root@kub-k8s-master prome]# kubectl get pod -n kube-system
[root@k8s-master prome]# kubectl exec -it test-volume-pod /bin/bash -n kube-system

Secret、ConfigMap,以及 Downward API 这三种 Projected Volume 定义的信息,大多还可以通过环境变量的方式出现在容器里。但是,通过环境变量获取这些信息的方式,不具备自动更新的能力。一般情况下,建议使用 Volume 文件的方式获取这些信息。

十四.ServiceAccount详解

官方文档地址:https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/

k8s中提供了良好的多租户认证管理机制,如RBAC、ServiceAccount还有各种Policy等。

什么是 Service Account ?

Pod里的进程也可以与 apiserver 联系。 当它们在联系 apiserver 的时候,它们就会被认证为一个特定的 Service Account。

使用场景:

Service Account它并不是给kubernetes集群的用户使用的,而是给pod里面的进程使用的,它为pod提供必要的身份认证。—-专门为pod里面的进程和apiserver通信提供认证的。

Service account与User account区别:

1. User account是为人设计的,而service account则是为Pod中的进程调用Kubernetes API或其他外部服务而设计的
2. User account是跨namespace的,而service account则是仅局限它所在的namespace;
3. 每个namespace都会自动创建一个default service account
4. Token controller检测service account的创建,并为它们创建secret

14.1 Service Account应用示例

Service Account(服务账号)示例

因为平时系统会使用默认service account,我们不需要自己创建,感觉不到service account的存在,本实验是使用自己手动创建的service account

1、创建serviceaccount
[root@kub-k8s-master1 ~]# kubectl create serviceaccount mysa
serviceaccount/mysa created
2、查看mysa
[root@kub-k8s-master1 ~]# kubectl describe sa mysa
Name: mysa
Namespace: default
Labels: <none>
Annotations: <none>
Image pull secrets: <none>
Mountable secrets: mysa-token-cknwf
Tokens: mysa-token-cknwf
Events: <none>
3、查看mysa自动创建的secret
[root@kub-k8s-master1 ~]# kubectl get secret
NAME TYPE DATA AGE
db-user-pass Opaque 2 11h
default-token-6svwp kubernetes.io/service-account-token 3 4d23h
mysa-token-cknwf kubernetes.io/service-account-token 3 76s
mysecret Opaque 2 11h
mysecret-01 Opaque 2 6h58m
pass Opaque 1 7h6m
user Opaque 1 7h7m
4、使用mysa的sa资源配置pod
[root@kub-k8s-master1 ~]# cd prome/
[root@kub-k8s-master prome]# vim mysa-pod.yaml

apiVersion: v1
kind: Pod
metadata:
name: nginx-pod
labels:
app: my-pod
spec:
containers:
– name: my-pod
image: daocloud.io/library/nginx
ports:
– name: http
containerPort: 80
serviceAccountName: mysa #指定serviceaccount的名称

5、导入
[root@kub-k8s-master prome]# kubectl apply -f mysa-pod.yaml
pod/nginx-pod created
6、查看
[root@kub-k8s-master prome]# kubectl describe pod nginx-pod
7、查看使用的token和secret(使用的是mysa的token)
[root@kub-k8s-master prome]# kubectl get pod nginx-pod -o jsonpath={\”.spec.volumes\”}
]]
[root@kub-k8s-master prome]#

十五.RBAC 详解(基于角色的访问控制)

在Kubernetes中,授权有ABAC(基于属性的访问控制)、RBAC(基于角色的访问控制)、Webhook、Node、AlwaysDeny(一直拒绝)和AlwaysAllow(一直允许)这6种模式。
RBAC基于角色的访问控制–全拼Role-Based Access Control—-做权限控制的,顾名思义就是通过给角色赋予相应的权限,从而使得该角色具有访问相关资源的权限。
k8s里面有两种用户,一种是User,一种就是service account(服务使用的账号)。
User account是为人设计的属于用户账户(个人使用的账号),此外User Account是跨Namespace的,而ServiceAccount则是仅局限它所在的Namespace。
在RABC API中,通过如下的步骤进行授权:
1)定义角色:在定义角色时会指定此角色对于资源的访问控制的规则;
2)绑定角色:将主体与角色进行绑定,对用户进行访问授权。

在K8s中这些资源分属于两个级别,名称空间(role/rolebinding)和集群级别(clusterrole/clusterrolebinding)这两个都是标准的K8s资源,可以直接定义。
Role与ClusterRole
Role普通角色:一个Role对象只能用于授予对某一单一命名空间中资源的访问权限,普通角色只是在当前的名称空间生效。
ClusterRole集群角色:整个Kubernetes集群范围内有效的角色则通过ClusterRole对象实现,可以访问整个集群资源。
简介
role/ClusterRole:
1、允许的操作,如get,list,update,create,delete等权限
2、允许操作的对象,如pod,svc等资源

rolebinding:将哪个用户绑定到哪个role上
clusterrolebinding:绑定到集群角色上
如果使用clusterrolebinding绑定到clusterrole上,表示绑定的用户拥有所有namespace的权限

#这里面有哪些重要的东西:role,clusterrole,binding,账号。

15.1 创建k8s账号与RBAC授权使用

创建账号
1、创建私钥
[root@kub-k8s-master1 ~]# (umask 077; openssl genrsa -out soso.key 2048)
Generating RSA private key, 2048 bit long modulus
………………………….+++
……………………..+++
e is 65537 (0x10001)
用此私钥创建一个csr(证书签名请求)文件
[root@kub-k8s-master1 ~]# openssl req -new -key soso.key -out soso.csr -subj \”/CN=soso\” # 这个地方是用户名
拿着私钥和请求文件生成证书
[root@kub-k8s-master1 ~]# openssl x509 -req -in soso.csr -CA /etc/kubernetes/pki/ca.crt -CAkey /etc/kubernetes/pki/ca.key -CAcreateserial -out soso.crt -days 365
Signature ok
subject=/CN=soso
Getting CA Private Key
生成账号
[root@kub-k8s-master1 ~]# kubectl config set-credentials soso –client-certificate=soso.crt –client-key=soso.key –embed-certs=true
User \”soso\” set.
3、设置上下文环境–指的是创建这个账号的环境在当前名称空间中
[root@kub-k8s-master1 ~]# kubectl config set-context soso@kubernetes –cluster=kubernetes –user=soso
Context \”soso@kubernetes\” created.
查看当前的工作上下文
[root@kub-k8s-master1 ~]# kubectl config view
apiVersion: v1
clusters:
– cluster:
certificate-authority-data: DATA+OMITTED
server: https://192.168.75.100:6443
….
4、切换用户(切换上下文)
[root@kub-k8s-master1 ~]# kubectl config use-context soso@kubernetes
Switched to context \”soso@kubernetes\”.
验证是否已经切换到了新的上下文
[root@kub-k8s-master1 ~]# kubectl config current-context
soso@kubernetes
5.测试(还未赋予权限)
[root@kub-k8s-master1 ~]# kubectl get pod
Error from server (Forbidden): pods is forbidden: User \”soso\” cannot list resource \”pods\” in API group \”\” in the namespace \”default\”

创建一个角色(role)—设置权限
1.切回管理帐号先
[root@kub-k8s-master1 ~]# kubectl config use-context kubernetes-admin@kubernetes
Switched to context \”kubernetes-admin@kubernetes\”.
创建角色(命令):
[root@kub-k8s-master1 ~]# kubectl create role role-reader –verb=get,list,watch –resource=pod,svc
role.rbac.authorization.k8s.io/role-reader created
–verb: 相当于是权限
–resource:给什么资源使用
yaml文件方式:
[root@kub-k8s-master1 ~]# vim role.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: role-reader
rules: #定义规则
– apiGroups: [\”\”] #表示当前pod使用核心的APIserver组,默认用\”\”表示就可以
resources: [\”pods\”,\”svc\”]
verbs: [\”get\”, \”list\”, \”watch\”, \”create\”, \”update\”, \”delete\”] #[\”*\”]表示所有权限
[root@kub-k8s-master1 ~]# kubectl apply -f role.yaml
role.rbac.authorization.k8s.io/role-reader created
[root@kub-k8s-master1 ~]# kubectl get roles
NAME AGE
role-reader 30s
[root@kub-k8s-master1 ~]# kubectl describe role role-reader
Name: role-reader
Labels: <none>
Annotations: kubectl.kubernetes.io/last-applied-configuration:
{\”apiVersion\”:\”rbac.authorization.k8s.io/v1beta1\”,\”kind\”:\”Role\”,\”metadata\”:{\”annotations\”:{},\”name\”:\”role-reader\”,\”namespace\”:\”default\”},\”…
PolicyRule:
Resources Non-Resource URLs Resource Names Verbs
——— —————– ————– —–
pods [] [] [get list watch create update delete]
svc [] [] [get list watch create update delete]
2.绑定用户soso(上面创建的用户),绑定用户到role-reader
[root@kub-k8s-master1 ~]# kubectl create rolebinding myrole-binding –role=role-reader –user=soso
rolebinding.rbac.authorization.k8s.io/myrole-binding created
yaml文件方式:
[root@k8s-master ~]# vim role-binding.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: myrolebind
subjects: #定义对那个主体进行操作,有三种Subjects:Service Account、User Account、Groups
– kind: User
name: soso
apiGroup: rbac.authorization.k8s.io
roleRef: #定义使用哪个角色
kind: Role
name: role-reader
apiGroup: rbac.authorization.k8s.io
[root@k8s-master ~]# kubectl apply -f role-binding.yaml
rolebinding.rbac.authorization.k8s.io/myrolebind created
[root@k8s-master ~]# kubectl get rolebinding
NAME AGE
myrolebind 25s
3.切换用户
[root@kub-k8s-master1 ~]# kubectl config use-context soso@kubernetes
Switched to context \”soso@kubernetes\”.
4.查看权限(只授权了default名称空间pod和svc的get,list,watch权限)
[root@kub-k8s-master1 ~]# kubectl get pod
NAME READY STATUS RESTARTS AGE
lifecycle-demo 1/1 Running 1 22h
mypod 1/1 Running 0 8h
nginx-configmap 1/1 Running 0 4h29m
nginx-pod 1/1 Running 0 39m
[root@kub-k8s-master1 ~]# kubectl get pod -n kube-system #无权访问kube-system
Error from server (Forbidden): pods is forbidden: User \”soso\” cannot list resource \”pods\” in API group \”\” in the namespace \”kube-system\”
[root@kub-k8s-master1 ~]# kubectl delete pod nginx-pod #无权限删除
Error from server (Forbidden): pods \”nginx-pod\” is forbidden: User \”soso\” cannot delete resource \”pods\” in API group \”\” in the namespace \”default\”
5.切换用户
[root@kub-k8s-master1 ~]# kubectl config use-context kubernetes-admin@kubernetes
Switched to context \”kubernetes-admin@kubernetes\”.
实验二,绑定用户到集群角色
6.删除soso账号之前绑定的rolebinding
[root@kub-k8s-master1 ~]# kubectl delete rolebinding myrolebind
rolebinding.rbac.authorization.k8s.io \”myrolebind\” deleted
7.创建clusterrole #可以访问全部的namespace
[root@kub-k8s-master1 ~]# kubectl create clusterrole myclusterrole –verb=get,list,watch –resource=pod,svc
clusterrole.rbac.authorization.k8s.io/myclusterrole created
yaml文件方式:
[root@kub-k8s-master1 ~]# vim clusterrole.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: myclusterrole
rules:
– apiGroups:
– \”\”
resources:
– pods
verbs:
– get
– list
– watch
[root@kub-k8s-master1 ~]# kubectl apply -f clusterrole.yaml
[root@kub-k8s-master1 ~]# kubectl get clusterrole
8.绑定集群角色到用户soso
[root@kub-k8s-master1 ~]# kubectl create clusterrolebinding my-cluster-rolebinding –clusterrole=myclusterrole –user=soso
clusterrolebinding.rbac.authorization.k8s.io/my-cluster-rolebinding created
yaml文件方式:
[root@kub-k8s-master1 ~]# vim clusterrolebinding.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: my-cluster-rolebinding
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: myclusterrole
subjects:
– apiGroup: rbac.authorization.k8s.io
kind: User
name: soso
[root@kub-k8s-master1 ~]# kubectl apply -f clusterrolebinding.yaml
[root@kub-k8s-master1 ~]# kubectl get clusterrolebinding
9.切换账号
[root@kub-k8s-master1 ~]# kubectl config use-context soso@kubernetes
Switched to context \”soso@kubernetes\”.
10.查看权限 查看kube-system空间的pod
[root@kub-k8s-master1 ~]# kubectl get pod -n kube-system
NAME READY STATUS RESTARTS AGE
coredns-5644d7b6d9-sm8hs 1/1 Running 0 5d
coredns-5644d7b6d9-vddll 1/1 Running 0 5d
etcd-kub-k8s-master 1/1 Running 0 5d

注意:11.切换为管理员用户
[root@kub-k8s-master1 ~]# kubectl config use-context kubernetes-admin@kubernetes

15.2 设置上下文和账户切换

设置工作上下文(前提得有用户)

[root@kub-k8s-master1 ~]# kubectl config set-context soso@kubernetes –cluster=kubernetes –user=soso
Context \”soso@kubernetes\” created.

查看当前的工作上下文

[root@kub-k8s-master1 ~]# kubectl config view
apiVersion: v1
clusters:
– cluster:
….

切换上下文(切换用户)

[root@kub-k8s-master1 ~]# kubectl config use-context soso@kubernetes
Switched to context \”soso@kubernetes\”.

切换为管理员用户

[root@kub-k8s-master prome]# kubectl config use-context kubernetes-admin@kubernetes
Switched to context \”kubernetes-admin@kubernetes\”.

查看某个资源类型是由哪个apiserver版本提供

[root@kub-k8s-master1 ~]# kubectl explain ClusterRole

十六.Deployment 资源详解

如果Pod出现故障,对应的服务也会挂掉,所以Kubernetes提供了一个Deployment的概念 ,目的是让Kubernetes去管理一组Pod的副本,也就是副本集 ,这样就能够保证一定数量的副本一直可用,不会因为某一个Pod挂掉导致整个服务挂掉。

Deployment 还负责在 Pod 定义发生变化时,对每个副本进行滚动更新(Rolling Update)。

使用yaml创建Deployment
k8s deployment资源创建流程:
1. 用户通过 kubectl 创建 Deployment。
2. Deployment 创建 ReplicaSet。
3. ReplicaSet 创建 Pod。

对象的命名方式是:子对象的名字 = 父对象名字 + 随机字符串或数字

Deployment是一个定义及管理多副本应用(即多个副本 Pod)的新一代对象,与Replication Controller相比,它提供了更加完善的功能,使用起来更加简单方便。

16.1 deployment 实例

例1:
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
selector:
matchLabels:
app: nginx
replicas: 2
template:
metadata:
labels:
app: nginx
spec:
containers:
– name: nginx
image: nginx:1.7.9
ports:
– containerPort: 80

例2:在上面yaml的基础上添加了volume
[root@kub-k8s-master prome]# vim deployment.yaml
apiVersion: apps/v1 #注意版本号
kind: Deployment
metadata:
name: nginx-deployment
spec:
selector: #属性,选择器
matchLabels:
app: nginx
replicas: 2 #管理的副本个数
template: #模板属性
metadata: #对pod的描述
labels:
app: nginx
spec:
volumes: #定义共享卷
– name: nginx-vol
emptyDir: {}
containers:
– name: nginx
image: daocloud.io/library/nginx
ports:
– containerPort: 80
volumeMounts: #定义挂载卷
– mountPath: \”/usr/share/nginx/html\”
name: nginx-vol

16.2 文件说明

创建Deployment:
将上述的YAML文件保存为deployment.yaml,然后创建Deployment:
[root@kub-k8s-master prome]# kubectl apply -f deployment.yaml
deployment.apps/nginx-deployment created
检查Deployment的列表:启动之后需要创建时间比较长
通过 kubectl get 命令检查这个 YAML 运行起来的状态:
[root@kub-k8s-master prome]# kubectl get deployments
NAME READY UP-TO-DATE AVAILABLE AGE
nginx-deployment 2/2 2 2 2m22s
[root@kub-k8s-master prome]# kubectl get pods -l app=nginx
NAME READY STATUS RESTARTS AGE
nginx-deployment-59c4b86474-2llrt 1/1 Running 0 2m51s
nginx-deployment-59c4b86474-n2r2m 1/1 Running 0 2m51s
在这里加上了一个 -l 参数,即获取所有匹配 app: nginx 标签的 Pod。需要注意的是,在命令行中,所有 key-value 格式的参数,都使用\”=\”而非\”:\”表示。
删除Deployment:
[root@k8s-master ~]# kubectl delete deployments nginx-deployment
deployment \”nginx-deployment\” deleted
或者
[root@k8s-master ~]# kubectl delete -f deployment.yaml

16.3 deployment可用字段

replicas: 1 # 声明副本数目
revisionHistoryLimit: 3 # 保留历史版本
selector: # 选择器

十七.Service 服务

k8s 内部域名访问方式
…svc.cluster.local

17.1 创建service

1.创建一个depl
[root@kub-k8s-master prome]# vim nginx-depl.yml
apiVersion: apps/v1
kind: Deployment
metadata:
name: dep01
spec:
selector:
matchLabels:
app: web
replicas: 2
template:
metadata:
labels:
app: web
spec:
containers:
– name: testnginx9
image: daocloud.io/library/nginx
ports:
– containerPort: 80
[root@kub-k8s-master prome]# kubectl apply -f nginx-depl.yml
deployment.apps/nginx-deployment created
2. 创建service并且以nodePort的方式暴露端口给外网:
[root@kub-k8s-master prome]# vim nginx_svc.yaml
apiVersion: v1
kind: Service
metadata:
name: mysvc
spec:
type: NodePort #类型
ports:
– port: 8080
nodePort: 30001
targetPort: 80
selector: #选择器
app: web

[root@kub-k8s-master prome]# kubectl apply -f nginx_svc.yaml
service/mysvc created
3.测试
[root@kub-k8s-master prome]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 5d18h
mysvc NodePort 10.100.166.208 <none> 8080:30001/TCP 21s

17.2 页面请求测试
17.3 pod内部请求测试

# 进入docker容器
[root@kube-node1 ~]# docker exec -it b4 /bin/bash
# 请求
root@dep01-694c5dbcd-ccdsv:/# curl mysvc.default.svc.cluster.local:8080
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
body {
width: 35em;
margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif;
}
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href=\”http://nginx.org/\”>nginx.org</a>.<br/>
Commercial support is available at
<a href=\”http://nginx.com/\”>nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>

17.4 端口解析

# 解析
port
port是暴露在cluster ip上的端口,port提供了集群内部客户端访问service的入口,即clusterIP:port。
nodeport
nodePort 提供了集群外部客户端访问 Service 的一种方式,nodePort 提供了集群外部客户端访问 Service 的端口,通过 nodeIP:nodePort 提供了外部流量访问k8s集群中service的入口。
targetPort
targetPort是pod的端口,从port和nodePort来的流量经过kube-proxy流入到后端pod的targetPort上,最后进入容器。
containerPort
containerPort是pod内部容器的端口,targetPort映射到containerPort。

17.5 kube-proxy 使用ipvs

[root@master ~]# kubectl get configmap kube-proxy -n kube-system -o yaml > kube-proxy-configmap.yaml
[root@master ~]# sed -i \’s/mode: \”\”/mode: \”ipvs\”/\’ kube-proxy-configmap.yaml
[root@master ~]# kubectl apply -f kube-proxy-configmap.yaml
[root@master ~]# rm -f kube-proxy-configmap.yaml
[root@master ~]# kubectl get pod -n kube-system | grep kube-proxy | awk \'{system(\”kubectl delete pod \”$1\” -n kube-system\”)}\’
后续请求时,可以发现已经通过了算法进行调度
[root@kube-master ~]# ipvsadm -L -n
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
-> RemoteAddress:Port Forward Weight ActiveConn InActConn
TCP 172.17.0.1:30001 rr
-> 10.244.1.34:80 Masq 1 0 0
-> 10.244.2.31:80 Masq 1 0 0
TCP 192.168.96.143:30001 rr
-> 10.244.1.34:80 Masq 1 0 12
-> 10.244.2.31:80 Masq 1 0 12

十八.k8s服务暴露

18.1 ClusterIP

此类型会提供一个集群内部的虚拟IP(与Pod不在同一网段),以供集群内部的pod之间通信使用。ClusterIP也是Kubernetes service的默认类型。

18.2 NodePort

外网client—>nodeIP+nodePort—>podIP+PodPort

为每个节点暴露一个端口,通过nodeip + nodeport可以访问这个服务,同时服务依然会有cluster类型的ip+port。内部通过clusterip方式访问,外部通过nodeport方式访问。

18.3 loadbalance

LoadBalancer在NodePort基础上,K8S可以请求底层云平台创建一个负载均衡器,将每个Node作为后端,进行服务分发。

18.4 Ingress

Ingress是一种HTTP方式的路由转发机制,为K8S服务配置HTTP负载均衡器,通常会将服务暴露给K8S群集外的客户端。

十九.Ingress 暴露服务

要理解ingress,需要区分两个概念,ingress和ingress-controller:

ingress对象:
指的是k8s中的一个api对象,一般用yaml配置。作用是定义请求如何转发到service的规则,可以理解为配置模板。

ingress-controller:
具体实现反向代理及负载均衡的程序,对ingress定义的规则进行解析,根据配置的规则来实现请求转发。

简单来说,ingress-controller才是负责具体转发的组件,通过各种方式将它暴露在集群入口,外部对集群的请求流量会先到ingress-controller,而ingress对象是用来告诉ingress-controller该如何转发请求,比如哪些域名哪些path要转发到哪些服务等等。

service 的表现形式为IP:PORT,即工作在第四层传输层(TCP/IP层),对于不同的URL地址经常对应用不同的后端服务或者虚拟服务器,这些应用层的转发机制仅通过kubernetes的service机制是无法实现的,这种情况我么可以使用ingress策略定义和一个具体的ingress Controller.

Ingress提供七层负载均衡能力,可以通过 Ingress 配置提供外部可访问的 URL、负载均衡、SSL、基于名称的虚拟主机等。作为集群流量接入层,Ingress 的高可靠性显得尤为重要。

19.1 ingress详解

这个负载均衡是基于nginx七层反向代理来实现的,ingress工作原理如下图:
外部客户端通过访问负载均衡器,然后调度到service上,然后在调度到IngressController,IngressController通过Ingress规则(域名或虚拟主机)访问到后端pod,而在Ingress规则当中对应的主机是由service分组来设定的,可以看到,这幅图有2种service,最上面的service是用来对外提供服务的,而下面2个service仅仅是用来分pod组的
Kubernetes 并没有自带 Ingress Controller,实际上ingress-controller只是一个统称,具体实现有多种,需要自己单独安装,目前,由k8s维护的ingress-controller只有google云的GCE与ingress-nginx两个,常用的是 Ingress-nginx Controller.

Ingress 一般由三个组件组成:
1. Nginx 反向代理负载均衡器
2. Ingress Controller 可以理解为控制器,它通过不断的跟 Kubernetes API 交互,实时获取后端 Service、Pod 等的变化,比如新增、删除等,然后结合 Ingress 定义的规则生成配置,然后动态更新上边的 Nginx 负载均衡器,并刷新使配置生效,来达到服务自动发现的作用。
3. Ingress 则是定义规则,通过它定义某个域名的请求过来之后转发到集群中指定的 Service。它可以通过 Yaml 文件定义,可以给一个或多个 Service 定义一个或多个 Ingress 规则。

19.2 如何创建 Ingress 资源

Ingress 中的spec字段是Ingress资源的核心组成部分,主要包含以下3个字段:
rules:用于定义当前Ingress资源的转发规则列表;由rules定义规则,或没有匹配到规则时,所有的流量会转发到由backend定义的默认后端。backend:默认的后端,用于服务那些没有匹配到任何规则的请求;定义Ingress资源时,必须要定义backend或rules两者之一,该字段用于让负载均衡器指定一个全局默认的后端。tls:TLS配置,目前仅支持通过默认端口443提供服务,如果要配置指定的列表成员指向不同的主机,则需要通过SNI TLS扩展机制来支持该功能。

19.3 部署 Ingress 控制器(Nginx)
19.3.1 下载ingress controller

[root@k8s-master ~]# cd /mnt/
[root@k8s-master mnt]# wget https://codeload.github.com/kubernetes/ingress-nginx/tar.gz/refs/tags/controller-v1.3.1
[root@k8s-master mnt]# tar xf ingress-nginx-controller-v1.3.1.tar.gz
[root@k8s-master mnt]# cd ingress-nginx-controller-v1.3.1/deploy/static/provider/cloud
[root@k8s-master cloud]# ls
deploy.yaml kustomization.yaml
[root@k8s-master cloud]# cd
[root@k8s-master ~]# vim deploy.yaml #修改配置文件
找到已下apiserver的版本:
# 390行修改
kind: DaemonSet #将原来的Deployment修改为DaemonSet
# 415行下边添加
hostNetwork: true #添加此配置

需要修改的地方:

kind: DaemonSet:

官方原始文件使用的是deployment,replicate 为 1,这样将会在某一台节点上启动对应的nginx-ingress-controller pod。外部流量访问至该节点,由该节点负载分担至内部的service。测试环境考虑防止单点故障,改为DaemonSet然后删掉replicate ,配合亲和性部署在制定节点上启动nginx-ingress-controller pod,确保有多个节点启动nginx-ingress-controller pod,后续将这些节点加入到外部硬件负载均衡组实现高可用性。

hostNetwork: true:

添加该字段,暴露nginx-ingress-controller pod的服务端口(80)

19.3.2 创建ingress-controller

[root@k8s-master ~]# kubectl apply -f deploy.yaml
查看ingress-controller资源
[root@k8s-master ~]# kubectl get pods -n ingress-nginx
NAME READY STATUS RESTARTS AGE
nginx-ingress-controller-s8vnl 1/1 Running 0 98m
nginx-ingress-controller-ztxz4 1/1 Running 0 97m
[root@k8s-master cloud]# kubectl get ingressclass
NAME CONTROLLER PARAMETERS AGE
nginx k8s.io/ingress-nginx <none> 38m

测试ingress

创建两个应用和service

[root@k8s-master ~]# vim my-apache.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-apache
spec:
selector:
matchLabels:
run: my-apache
replicas: 2
template:
metadata:
labels:
run: my-apache
spec:
containers:
– name: my-apache
image: daocloud.io/library/httpd:2.4
ports:
– containerPort: 80

apiVersion: v1
kind: Service
metadata:
name: my-apache
labels:
run: my-apache
spec:
#type: NodePort
ports:
– port: 80
targetPort: 80
#nodePort: 30002
selector:
run: my-apache

[root@k8s-master ~]# cat my-nginx.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-nginx
spec:
selector:
matchLabels:
run: my-nginx
replicas: 2
template:
metadata:
labels:
run: my-nginx
spec:
containers:
– name: my-nginx
image: daocloud.io/library/nginx:1.7.9
ports:
– containerPort: 80

apiVersion: v1
kind: Service
metadata:
name: my-nginx
labels:
run: my-nginx
spec:
#type: NodePort
ports:
– port: 80
targetPort: 80
#nodePort: 30001
selector:
run: my-nginx

创建pod和service
[root@k8s-master ~]# kubectl apply -f my-apache.yaml
[root@k8s-master ~]# kubectl apply -f my-nginx.yaml
查看资源
[root@k8s-master ~]# kubectl get pods
NAME READY STATUS RESTARTS AGE
my-apache-d49c8b95c-8z8l9 1/1 Running 0 125m
my-apache-d49c8b95c-d9q5s 1/1 Running 0 125m
my-nginx-5fdc96f9b4-bmf6s 1/1 Running 0 124m
my-nginx-5fdc96f9b4-qfw8c 1/1 Running 0 124m
[root@k8s-master ~]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 20d
my-apache NodePort 10.99.178.186 <none> 80/TCP 125m
my-nginx NodePort 10.97.171.188 <none> 80/TCP 124m

配置ingress转发文件

[root@k8s-master ~]# cat ingress-test.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: test-ingress
namespace: default
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
ingressClassName: nginx
rules: #定义转发规则
– host: test.apache.ingress #指定域名方式
http:
paths:
– path: / #指定访问的路径
pathType: Prefix #定义路径的类型
backend: #定义转发后端的服务
service: #定义转发的service
name: my-apache
port:
number: 80
– host: test.nginx.ingress
http:
paths:
– path: /
pathType: Prefix
backend:
service:
name: my-nginx
port:
number: 80

[root@k8s-master ~]# kubectl apply -f ingress-test.yaml
[root@k8s-master ~]# kubectl get ingress
NAME CLASS HOSTS ADDRESS PORTS AGE
test-ingress <none> test.apache.ingress,test.nginx.ingress 80 119m

19.3.3 修改ingress转发类型

[root@k8s-master ~]# kubectl edit svc ingress-nginx-controller -n ingress-nginx
# 将type修改为NodePort

nginx-ingress-controller运行在node1,node2两个节点上。

如果网络中有dns服务器,在dns中把这两个域名映射到nginx-ingress-controller运行的任意一个节点上,如果没有dns服务器只能修改host文件了。

任意一个节点上操作:(客户端解析)

我这里有两个节点部署了控制器,ip分别为172.16.229.5,172.16.229.6 ,如果有多个,可以随便选。

在wind电脑设置本地解析

172.16.229.5 test.nginx.ingress
172.16.229.6 test.apache.ingress

二十.企业级镜像仓库Harbor

如果资源不足可以在3个节点中的任意节点部署,也可以在单独的一台中部署。

20.1 上传harbor安装包并安装

$ tar xf harbor-offline-installer-v2.5.3.tgz
$ cp harbor.yml.tmpl harbor.yml
$ vim harbor.yml
hostname: 192.168.246.217
# http related config
http:
# port for http, default is 80. If https enabled, this port will redirect to https port
port: 80
# 注释所有https的内容
$ ./install.sh

20.2 浏览器访问

默认账号:admin 默认密码:Harbor12345

20.3 k8s使用harbor仓库

# 两台node节点执行
[root@kub-k8s-node1 ~]# vim /etc/docker/daemon.json #不存在则创建
{ \”insecure-registries\”:[\”192.168.246.168\”] }
重启docker:
[root@kub-k8s-node1 ~]# systemctl restart docker

20.4 上传镜像到仓库

$ docker login http://192.168.246.168
页面中创建用户及仓库
$ docker pull newrain857/show
$ docker tag newrain857/show 192.168.246.168/os-image/show:latest

20.5 创建secret.yaml文件

$ cat ~/.docker/config.json |base64 -w 0
ewoJImF1dGhzIjogewoJCSIxOTIuMTY4Ljk2LjIxNyI6IHsKCQkJImF1dGgiOiAiZUdsaGIyMXBibWM2VVhFeE1URXhNVEU9IgoJCX0KCX0KfQ==
创建 secret.yaml 文件
apiVersion: v1
kind: Secret
metadata:
name: login
type: kubernetes.io/dockerconfigjson
data:
.dockerconfigjson: ewoJImF1dGhzIjogewoJCSIxOTIuMTY4Ljk2LjIxNyI6IHsKCQkJImF1dGgiOiAiZUdsaGIyMXBibWM2VVhFeE1URXhNVEU9IgoJCX0KCX0KfQ==

$ kubectl apply -f secret.yaml
或者
kubectl create secret docker-registry –docker-server=192.168.246.168 –docker-username=admin –docker-password=Harbor12345 –docker-email=admin@newrain.com

20.6 k8s-pod 使用镜像

vim harbor-pod.yml

apiVersion: apps/v1
kind: Deployment
metadata:
name: show
spec:
replicas: 2
selector:
matchLabels:
app: show
template:
metadata:
labels:
app: show
spec:
containers:
– name: show
image: 192.168.246.168/os-image/show:latest
ports:
– containerPort: 80
imagePullSecrets:
– name: login

apiVersion: v1
kind: Service
metadata:
name: show-service
spec:
type: NodePort
selector:
app: show
ports:
– port: 80
targetPort: 80
nodePort: 32000
# 浏览器访问最终的结果

二十一.水平扩展/收缩与滚动更新

21.1 水平扩展/收缩
21.1.1 创建一个deployment


apiVersion: v1
kind: Namespace
metadata:
name: dep01
labels:
name: dep01

apiVersion: apps/v1
kind: Deployment
metadata:
namespace: nginx-dep
name: myapp
spec:
replicas: 2
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
– name: nginx
image: nginx:1.16.1
ports:
– containerPort: 80

21.1.2 通过声明方式扩展

[root@kub-k8s-master prome]# kubectl get deploy
NAME READY UP-TO-DATE AVAILABLE AGE
dep01 2/2 2 2 4h41m
我们将dep01的副本数量变成4个,现在2个
[root@kub-k8s-master prome]# vim deployment.yaml #修改如下内容
将replicas: 2
修改为:
replicas: 4

创建上节儿的:dep01

[root@kub-k8s-master prome]# kubectl apply -f deployment.yaml –record
deployment.apps/dep01 configured
–record kubectl apply 每次更新应用时 Kubernetes 都会记录下当前的配置,保存为一个 revision(版次),这样就可以回滚到某个特定 revision。

检查nginx-deployment 创建后的状态信息:

[root@kub-k8s-master prome]# kubectl get deploy
NAME READY UP-TO-DATE AVAILABLE AGE
dep01 4/4 4 4 4h53m

返回结果中四个状态字段含义:

DESIRED:
如果有就表示用户期望的 Pod 副本个数(spec.replicas 的值);
CURRENT:
当前处于 Running 状态的 Pod 的个数;
UP-TO-DATE:
当前处于最新版本的 Pod 的个数,所谓最新版本指的是 Pod 的 Spec 部分与 Deployment 里 Pod 模板里定义的完全一致;
AVAILABLE:
当前已经可用的 Pod 的个数,即:既是 Running 状态,又是最新版本,并且已经处于 Ready(健康检查正确)状态的 Pod 的个数。只有这个字段,描述的才是用户所期望的最终状态。

21.1.3 通过edit方式收缩

[root@kub-k8s-master prome]# kubectl get deploy
NAME READY UP-TO-DATE AVAILABLE AGE
dep01 4/4 4 4 4h59m
将dep01的副本将4变为3个
[root@kub-k8s-master prome]# kubectl edit deployment/dep01
# reopened with the relevant failures.
#
apiVersion: apps/v1

spec:
progressDeadlineSeconds: 600
replicas: 3 #将这里原来的4改为3
revisionHistoryLimit: 10
selector:
matchLabels:

保存退出,vim的方式
[root@kub-k8s-master prome]# kubectl edit deployment/dep01
deployment.apps/dep01 edited

21.2 滚动更新

概念:

将一个集群中正在运行的多个 Pod 版本,交替地逐一升级的过程,就是\”滚动更新\”。

21.2.1 进行版本的升级

创建一个新的deploy
[root@kub-k8s-master prome]# cp nginx-depl.yml nginx-depl02.yml
[root@kub-k8s-master prome]# vim nginx-depl02.yml
apiVersion: apps/v1
kind: Deployment
metadata:
name: dep02 #注意修改
spec:
selector:
matchLabels:
app: web1
replicas: 2
template:
metadata:
name: testnginx9
labels:
app: web1
spec:
containers:
– name: testnginx9
image: daocloud.io/library/nginx:1.14 #注意修改
ports:
– containerPort: 80
[root@kub-k8s-master prome]# kubectl apply -f nginx-depl02.yml
deployment.apps/dep02 created
[root@kub-k8s-master prome]# kubectl get pods
NAME READY STATUS RESTARTS AGE
dep01-58f6d4d4cb-997jw 1/1 Running 0 16m
dep01-58f6d4d4cb-g6vtg 1/1 Running 0 5h32m
dep01-58f6d4d4cb-k6z47 1/1 Running 0 5h32m
dep02-78dbd944fc-47czr 1/1 Running 0 44s
dep02-78dbd944fc-4snsj 1/1 Running 0 25s
将nginx的版本从1.14升级到1.16
[root@kub-k8s-master prome]# kubectl edit deployment/dep02
# Please edit the object below. Lines beginning with a \’#\’ will be ignored,
# and an empty file will abort the edit. If an error occurs while saving this file will be

spec:
containers:
– image: daocloud.io/library/nginx:1.16 #将这里原来的nginx:1.14修改为nginx:1.16
imagePullPolicy: Always
name: testnginx9
ports:
– containerPort: 80

保存退出,vim的方式
[root@kub-k8s-master prome]# kubectl edit deployment/dep02
deployment.apps/dep01 edited

这时可以通过查看 Deployment 的 Events,看到这个\”滚动更新\”的流程

[root@kub-k8s-master prome]# kubectl describe deployment dep02

Events:
Type Reason Age From Message
—- —— —- —- ——-
Normal ScalingReplicaSet 50s deployment-controller Scaled up replica set dep02-846bf8775b to 2
Normal ScalingReplicaSet 9s deployment-controller Scaled up replica set dep02-58f8d5678 to 1
Normal ScalingReplicaSet 8s deployment-controller Scaled down replica set dep02-846bf8775b to 1
Normal ScalingReplicaSet 8s deployment-controller Scaled up replica set dep02-58f8d5678 to 2
Normal ScalingReplicaSet 5s deployment-controller Scaled down replica set dep02-846bf8775b to 0

如此交替进行,新 ReplicaSet 管理的 Pod 副本数,从 0 个变成 1 个,再变成 2 个,最后变成 3 个。而旧的 ReplicaSet 管理的 Pod 副本数则从 3 个变成 2 个,再变成 1 个,最后变成 0 个。这样,就完成了这一组 Pod 的版本升级过程。

21.2.2 验证

[root@kub-k8s-master prome]# kubectl get pods
NAME READY STATUS RESTARTS AGE
dep02-78dbd944fc-69t8x 1/1 Running 0 11h
dep02-78dbd944fc-7cn86 1/1 Running 0 11h
[root@kub-k8s-master prome]# kubectl exec -it dep02-78dbd944fc-69t8x /bin/bash
root@dep02-78dbd944fc-69t8x:/# nginx -v
nginx version: nginx/1.16.1
root@dep02-78dbd944fc-69t8x:/# exit

21.2.3 滚动更新的好处

在升级刚开始的时候,集群里只有 1 个新版本的 Pod。如果这时,新版本 Pod 有问题启动不起来,那么\”滚动更新\”就会停止,从而允许开发和运维人员介入。而在这个过程中,由于应用本身还有两个旧版本的 Pod 在线,所以服务并不会受到太大的影响。

21.3 版本回滚
21.3.1 查看版本历史

[root@kub-k8s-master prome]# kubectl rollout history deployment/dep02
deployment.apps/dep02
REVISION CHANGE-CAUSE
1 <none>
2 <none>

21.3.2 回滚到以前的旧版本:

把整个 Deployment 回滚到上一个版本:

[root@kub-k8s-master prome]# kubectl rollout undo deployment/dep02
deployment.apps/dep02 rolled back

查看回滚状态

[root@kub-k8s-master prome]# kubectl rollout status deployment/dep02
deployment \”dep02\” successfully rolled out

验证:

[root@kub-k8s-master prome]# kubectl get pods
NAME READY STATUS RESTARTS AGE
dep02-8594cd6447-pqtxk 1/1 Running 0 55s
dep02-8594cd6447-tt4h4 1/1 Running 0 51s
[root@kub-k8s-master prome]# kubectl exec -it dep02-8594cd6447-tt4h4 /bin/bash
root@dep02-8594cd6447-tt4h4:/# nginx -v
nginx version: nginx/1.14.2

21.3.3 回滚到更早之前的版本

使用 kubectl rollout history 命令查看每次 Deployment 变更对应的版本。
[root@kub-k8s-master prome]# kubectl rollout history deployment/dep02
deployment.apps/dep02
REVISION CHANGE-CAUSE
2 <none>
3 <none>
#默认配置下,Kubernetes 只会保留最近的几个 revision,可以在 Deployment 配置文件中通过 revisionHistoryLimit: 属性增加 revision 数量。

由于在创建这个 Deployment 的时候,指定了–record 参数,会将创建这些版本时执行的 kubectl 时文件中的配置,都会记录下来。

查看每个版本对应的 Deployment 的 API 对象的细节:

[root@kub-k8s-master prome]# kubectl rollout history deployment/dep02 –revision=3
deployment.apps/dep02 with revision #3
Pod Template:
Labels: app=web1
pod-template-hash=8594cd6447
Containers:
testnginx9:
Image: daocloud.io/library/nginx:1.14
Port: 80/TCP
Host Port: 0/TCP
Environment: <none>
Mounts: <none>
Volumes: <none>

2.在 kubectl rollout undo 命令行最后,加上要回滚到的指定版本的版本号,就可以回滚到指定版本了。

[root@kub-k8s-master prome]# kubectl rollout undo deployment/dep02 –to-revision=2
deployment.apps/dep02 rolled back

验证:

[root@kub-k8s-master prome]# kubectl get pods
NAME READY STATUS RESTARTS AGE
dep02-78dbd944fc-8nvxl 1/1 Running 0 86s
dep02-78dbd944fc-sb9sj 1/1 Running 0 88s
[root@kub-k8s-master prome]# kubectl exec -it dep02-78dbd944fc-8nvxl /bin/bash
root@dep02-78dbd944fc-8nvxl:/# nginx -v
nginx version: nginx/1.16.1

课后了解

你听说过金丝雀发布(Canary Deployment)和蓝绿发布(Blue-Green Deployment)吗?你能说出它们是什么意思吗?

二十二.DeamonSet详解

22.1 何为DaemonSet

介绍DaemonSet我们先来思考一个问题:相信大家都接触过监控系统比如zabbix,监控系统需要在被监控机安装一个agent,安装agent通常会涉及到以下几个场景:

– 所有节点都必须安装agent以便采集监控数据
– 新加入的节点需要配置agent,手动或者运行脚本

k8s中经常涉及到在node上安装部署应用,它是如何解决上述的问题的呢?答案是DaemonSet。DaemonSet守护进程简称DS,适用于在所有节点或部分节点运行一个daemon守护进程。

DaemonSet 的主要作用,是让你在 k8s 集群里,运行一个 Daemon Pod。

这个 Pod 有如下三个特征:

这个 Pod 运行在 k8s 集群里的每一个节点(Node)上;每个节点上只有一个这样的 Pod 实例;当有新的节点加入 Kubernetes 集群后,该 Pod 会自动地在新节点上被创建出来;而当旧节点被删除后,它上面的 Pod 也相应地会被回收掉。

举例:

各种网络插件的 Agent 组件,都必须运行在每一个节点上,用来处理这个节点上的容器网络;

各种存储插件的 Agent 组件,也必须运行在每一个节点上,用来在这个节点上挂载远程存储目录,操作容器的 Volume 目录;

各种监控组件和日志组件,也必须运行在每一个节点上,负责这个节点上的监控信息和日志搜集。

22.2 DaemonSet 的 API 对象的定义

所有node节点分别下载镜像

# docker pull daocloud.io/daocloud/fluentd-elasticsearch:1.20
# docker pull daocloud.io/daocloud/fluentd-elasticsearch:v2.2.0

fluentd-elasticsearch 镜像功能:

通过 fluentd 将每个node节点上面的Docker 容器里的日志转发到 ElasticSearch 中。

编写daemonset配置文件

Yaml文件内容如下

[root@k8s-master ~]# mkdir set
[root@k8s-master ~]# cd set/
[root@k8s-master set]# vim fluentd-elasticsearch.yaml # DaemonSet 没有 replicas 字段
apiVersion: apps/v1
kind: DaemonSet #创建资源的类型
metadata:
name: fluentd-elasticsearch
namespace: kube-system
labels:
k8s-app: fluentd-logging
spec:
selector:
matchLabels:
name: fluentd-elasticsearch
template:
metadata:
labels:
name: fluentd-elasticsearch
spec:
tolerations: #容忍污点
– key: node-role.kubernetes.io/master #污点
effect: NoSchedule #描述污点的效应
containers:
– name: fluentd-elasticsearch
image: daocloud.io/daocloud/fluentd-elasticsearch:1.20
resources: #限制使用资源
limits: #定义使用内存的资源上限
memory: 200Mi
requests: #实际使用
cpu: 100m
memory: 200Mi
volumeMounts:
– name: varlog
mountPath: /var/log
– name: varlibdockercontainers
mountPath: /var/lib/docker/containers
readOnly: true
volumes:
– name: varlog
hostPath: #定义卷使用宿主机目录
path: /var/log
– name: varlibdockercontainers
hostPath:
path: /var/lib/docker/containers

DaemonSet 没有 replicas 字段

selector :

选择管理所有携带了 name=fluentd-elasticsearch 标签的 Pod。

Pod 的模板用 template 字段定义:

定义了一个使用 fluentd-elasticsearch:1.20 镜像的容器,而且这个容器挂载了两个 hostPath 类型的 Volume,分别对应宿主机的 /var/log 目录和 /var/lib/docker/containers 目录。

fluentd 启动之后,它会从这两个目录里搜集日志信息,并转发给 ElasticSearch 保存。这样,通过 ElasticSearch 就可以很方便地检索这些日志了。Docker 容器里应用的日志,默认会保存在宿主机的 /var/lib/docker/containers/{{. 容器 ID}}/{{. 容器 ID}}-json.log 文件里,这个目录正是 fluentd 的搜集目标。

DaemonSet 如何保证每个 Node 上有且只有一个被管理的 Pod ?

DaemonSet Controller,首先从 Etcd 里获取所有的 Node 列表,然后遍历所有的 Node。这时,它就可以很容易地去检查,当前这个 Node 上是不是有一个携带了 name=fluentd-elasticsearch 标签的 Pod 在运行。

检查结果有三种情况:

1. 没有这种 Pod,那么就意味着要在这个 Node 上创建这样一个 Pod;指定的 Node 上创建新 Pod 用 nodeSelector,选择 Node 的名字即可。
2. 有这种 Pod,但是数量大于 1,那就说明要把多余的 Pod 从这个 Node 上删除掉;删除节点(Node)上多余的 Pod 非常简单,直接调用 Kubernetes API 就可以了。
3. 正好只有一个这种 Pod,那说明这个节点是正常的。

tolerations:

DaemonSet 还会给这个 Pod 自动加上另外一个与调度相关的字段,叫作 tolerations。这个字段意思是这个 Pod,会\”容忍\”(Toleration)某些 Node 的\”污点\”(Taint)。

tolerations 字段,格式如下:

apiVersion: v1
kind: Pod
metadata:
name: with-toleration
spec:
tolerations:
– key: node.kubernetes.io/unschedulable #污点的key
operator: Exists #将会忽略value;只要有key和effect就行
effect: NoSchedule #污点的作用

含义是:\”容忍\”所有被标记为 unschedulable\”污点\”的 Node;\”容忍\”的效果是允许调度。可以简单地把\”污点\”理解为一种特殊的 Label。

正常情况下,被标记了 unschedulable\”污点\”的 Node,是不会有任何 Pod 被调度上去的(effect: NoSchedule)。可是,DaemonSet 自动地给被管理的 Pod 加上了这个特殊的 Toleration,就使得这些 Pod 可以忽略这个限制,保证每个节点上都会被调度一个 Pod。如果这个节点有故障的话,这个 Pod 可能会启动失败,DaemonSet 会始终尝试下去,直到 Pod 启动成功。

DaemonSet 的\”过人之处\”,其实就是依靠 Toleration 实现的

DaemonSet 是一个控制器。在它的控制循环中,只需要遍历所有节点,然后根据节点上是否有被管理 Pod 的情况,来决定是否要创建或者删除一个 Pod。

更多种类的Toleration

可以在 Pod 模板里加上更多种类的 Toleration,从而利用 DaemonSet 实现自己的目的。

比如,在这个 fluentd-elasticsearch DaemonSet 里,给它加上了这样的 Toleration:

tolerations:
– key: node-role.kubernetes.io/master
effect: NoSchedule

这是因为在默认情况下,Kubernetes 集群不允许用户在 Master 节点部署 Pod。因为,Master 节点默认携带了一个叫作node-role.kubernetes.io/master的\”污点\”。所以,为了能在 Master 节点上部署 DaemonSet 的 Pod,就必须让这个 Pod\”容忍\”这个\”污点\”。

22.3 DaemonSet实践
22.3.1 创建 DaemonSet 对象

[root@k8s-master set] # kubectl create -f fluentd-elasticsearch.yaml

DaemonSet 上一般都加上 resources 字段,来限制它的 CPU 和内存使用,防止它占用过多的宿主机资源。

创建成功后,如果有 3 个节点,就会有 3 个 fluentd-elasticsearch Pod 在运行

[root@k8s-master set]# kubectl get pod -n kube-system -l name=fluentd-elasticsearch
NAME READY STATUS RESTARTS AGE
fluentd-elasticsearch-6lmnb 1/1 Running 0 21m
fluentd-elasticsearch-9fd7k 1/1 Running 0 21m
fluentd-elasticsearch-vz4n4 1/1 Running 0 21m

22.3.2 查看 DaemonSet 对象

[root@k8s-master set]# kubectl get ds -n kube-system fluentd-elasticsearch
NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE
fluentd-elasticsearch 3 3 3 3 3 <none> 22m

注:k8s 里比较长的 API 对象都有短名字,比如 DaemonSet 对应的是 ds,Deployment 对应的是 deploy。

22.3.3 DaemonSet 版本管理

[root@k8s-master set]# kubectl rollout history daemonset fluentd-elasticsearch -n kube-system
daemonset.apps/fluentd-elasticsearch
REVISION CHANGE-CAUSE
1 <none>

22.3.4 DaemonSet 的容器镜像版本到 v2.2.0

[root@k8s-master set]# kubectl set image ds/fluentd-elasticsearch fluentd-elasticsearch=daocloud.io/daocloud/fluentd-elasticsearch:v2.2.0 –record -n=kube-system
daemonset.apps/fluentd-elasticsearch image updated

这个 kubectl set image 命令里,第一个 fluentd-elasticsearch 是 DaemonSet 的名字,第二个 fluentd-elasticsearch 是容器的名字。

–record 参数:

升级使用到的指令会自动出现在 DaemonSet 的 rollout history 里面,如下所示:

[root@k8s-master set]# kubectl rollout history daemonset fluentd-elasticsearch -n kube-system
daemonset.apps/fluentd-elasticsearch
REVISION CHANGE-CAUSE
1 <none>
2 kubectl set image ds/fluentd-elasticsearch fluentd-elasticsearch=daocloud.io/daocloud/fluentd-elasticsearch:v2.2.0 –record=true –namespace=kube-system

有了版本号,也就可以像 Deployment 一样,将 DaemonSet 回滚到某个指定的历史版本了。

二十三.离线业务编排详解

23.1 在线业务和离线业务

在线业务

Deployment、StatefulSet以及 DaemonSet 这三个编排概念的共同之处是:它们主要编排的对象,都是\”在线业务\”,即:Long Running Task(长作业)。比如常用的 Nginx、Tomcat,以及 MySQL 等等。这些应用一旦运行起来,除非出错或者停止,它的容器进程会一直保持在 Running 状态。

离线业务

也可以叫做Batch Job(计算业务)。这种业务在计算完成后就直接退出了,如果用 Deployment 来管理这种业务,会发现 Pod 会在计算结束后退出,然后被 Deployment Controller 不断地重启

23.2 Job解析

什么是Job?

一个用来描述离线业务的 API 对象

Job API 对象的定义

# vim job.yaml
apiVersion: batch/v1
kind: Job
metadata:
name: pi
spec:
template:
spec:
containers:
– name: pi
image: resouer/ubuntu-bc
command: [\”sh\”, \”-c\”, \”echo \’scale=10000; 4*a(1)\’ | bc -l \”]
restartPolicy: Never
backoffLimit: 4

Job 对象不要求定义一个 spec.selector 来描述要控制哪些 Pod

spec.template 字段为Pod 模板

23.3 运行程序

echo “scale=10000; 4*a(1)” | bc -l

bc 命令:

是 Linux 里的\”计算器\”

-l 表示:是要使用标准数学库

a(1):  是调用数学库中的 arctangent 函数,计算 atan(1)。这是什么意思呢?

数学知识回顾:

tan(π/4) = 1。所以,4*atan(1)正好就是π,也就是 3.1415926…。
这是一个计算π值的容器。通过 scale=10000,指定了输出的小数点后的位数是 10000。在我的计算机上,这个计算大概用时 1 分 54 秒。

23.4 创建Job

# kubectl create -f job.yaml

查看 Job 对象

# kubectl describe jobs/pi
Name: pi
Namespace: default
Selector: controller-uid=c2db599a-2c9d-11e6-b324-0209dc45a495
Labels: controller-uid=c2db599a-2c9d-11e6-b324-0209dc45a495
job-name=pi
Annotations: <none>
Parallelism: 1
Completions: 1
..
Pods Statuses: 0 Running / 1 Succeeded / 0 Failed
Pod Template:
Labels: controller-uid=c2db599a-2c9d-11e6-b324-0209dc45a495
job-name=pi
Containers:

Volumes: <none>
Events:
FirstSeen LastSeen Count From SubobjectPath Type Reason Message
——— ——– —– —- ————- ——– —— ——-
1m 1m 1 {job-controller } Normal SuccessfulCreate Created pod: pi-rq5rl

为了避免不同 Job 对象所管理的 Pod 发生重合,Job 对象在创建后,它的 Pod 模板,被自动加上了一个 controller-uid=< 一个随机字符串 > 这样的 Label。而这个 Job 对象本身,则被自动加上了这个 Label 对应的 Selector,保证了 Job 与它所管理的 Pod 之间的匹配关系。

这种自动生成的 Label 对用户并不友好,不太适合推广到 Deployment 等长作业编排对象上。

Pod 进入了 Running 状态说明它正在计算 Pi 的值

# kubectl get pods
NAME READY STATUS RESTARTS AGE
pi-rq5rl 1/1 Running 0 10s

几分钟后计算结束,这个 Pod 就会进入 Completed 状态

# kubectl get pods
NAME READY STATUS RESTARTS AGE
pi-rq5rl 0/1 Completed 0 4m

离线计算的 Pod 永远都不应该被重启

实现方式是在 Pod 模板中定义 restartPolicy=Never

事实上restartPolicy 在 Job 对象里只允许被设置为 Never 和 OnFailure;而在 Deployment 对象里,restartPolicy 则只允许被设置为 Always。

查看 Pod 日志

# kubectl logs pi-rq5rl //可以看到计算得到的 Pi 值已经被打印了出来
3.141592653589793238462643383279…

离线作业失败处理方式

离线作业失败后 Job Controller 就会不断地尝试创建一个新 Pod,这个尝试肯定不能无限进行下去。所以,在 Job 对象的 spec.backoffLimit 字段里定义了重试次数为 4(即,backoffLimit=4,默认值是 6)

Job Controller 重新创建 Pod 的间隔是呈指数增加的,即下一次重新创建 Pod 的动作会分别发生在 10 s、20 s、40 s …后。

如果restartPolicy=OnFailure,离线作业失败后,Job Controller 就不会去尝试创建新的 Pod。但是,它会不断地尝试重启 Pod 里的容器。

# kubectl get pods
NAME READY STATUS RESTARTS AGE
pi-55h89 0/1 ContainerCreating 0 2s
pi-tqbcz 0/1 Error 0 5s

可以看到,这时候会不断地有新 Pod 被创建出来。

spec.activeDeadlineSeconds 字段:

当一个 Job 的 Pod 运行结束后,它会进入 Completed 状态。但是,如果这个 Pod 因为某种原因一直不肯结束呢?

在 Job 的 API 对象里,有一个 spec.activeDeadlineSeconds 字段可以设置最长运行时间,比如:

spec:

backoffLimit: 5

activeDeadlineSeconds: 100

一旦运行超过了 100 s,这个 Job 的所有 Pod 都会被终止。并且,你可以在 Pod 的状态里看到终止的原因是 reason: DeadlineExceeded。

以上,就是一个 Job API 对象最主要的概念和用法

23.5 并行作业

离线业务之所以被称为 Batch Job,是因为它们可以以\”Batch\”,也就是并行的方式去运行。

负责并行控制的参数有两个:

spec.parallelism:

定义一个 Job 在任意时间最多可以启动多少个 Pod 同时运行;

spec.completions:

定义 Job 至少要完成的 Pod 数目,即 Job 的最小完成数。

在之前计算 Pi 值的 Job 里,添加这两个参数:

注意:本例只是为了演示 Job 的并行特性,实际用途不大。不过现实中确实存在很多需要并行处理的场景。比如批处理程序,每个副本(Pod)都会从任务池中读取任务并执行,副本越多,执行时间就越短,效率就越高。这种类似的场景都可以用 Job 来实现。

apiVersion: batch/v1
kind: Job
metadata:
name: pi
spec:
parallelism: 2
completions: 4
template:
spec:
containers:
– name: pi
image: resouer/ubuntu-bc
command: [\”sh\”, \”-c\”, \”echo \’scale=5000; 4*a(1)\’ | bc -l \”]
restartPolicy: Never
backoffLimit: 4

这样,我们就指定了这个 Job 最大的并行数是 2,而最小的完成数是 4。

创建这个 Job 对象:

# kubectl create -f job.yaml

这个 Job 其实也维护了两个状态字段,即 DESIRED 和 SUCCESSFUL,如下所示:

# kubectl get job //注意,最新版本的子段已经不一样了
NAME DESIRED SUCCESSFUL AGE
pi 4 0 3s

其中,DESIRED 的值,正是 completions 定义的最小完成数。

这个 Job 首先创建了两个并行运行的 Pod 来计算 Pi:

# kubectl get pods
NAME READY STATUS RESTARTS AGE
pi-5mt88 1/1 Running 0 6s
pi-gmcq5 1/1 Running 0 6s

而在 40 s 后,这两个 Pod 相继完成计算。

这时可以看到,每当有一个 Pod 完成计算进入 Completed 状态时,就会有一个新的 Pod 被自动创建出来,并且快速地从 Pending 状态进入到 ContainerCreating 状态:

# kubectl get pods
NAME READY STATUS RESTARTS AGE
pi-gmcq5 0/1 Completed 0 40s
pi-84ww8 0/1 Pending 0 0s
pi-5mt88 0/1 Completed 0 41s
pi-62rbt 0/1 Pending 0 0s
# kubectl get pods
NAME READY STATUS RESTARTS AGE
pi-gmcq5 0/1 Completed 0 40s
pi-84ww8 0/1 ContainerCreating 0 0s
pi-5mt88 0/1 Completed 0 41s
pi-62rbt 0/1 ContainerCreating 0 0s

紧接着,Job Controller 第二次创建出来的两个并行的 Pod 也进入了 Running 状态:

# kubectl get pods
NAME READY STATUS RESTARTS AGE
pi-5mt88 0/1 Completed 0 54s
pi-62rbt 1/1 Running 0 13s
pi-84ww8 1/1 Running 0 14s
pi-gmcq5 0/1 Completed 0 54s

最终,后面创建的这两个 Pod 也完成了计算,进入了 Completed 状态。

这时,由于所有的 Pod 均已经成功退出,这个 Job 也就执行完了,所以你会看到它的 SUCCESSFUL 字段的值变成了 4:

# kubectl get pods
NAME READY STATUS RESTARTS AGE
pi-5mt88 0/1 Completed 0 5m
pi-62rbt 0/1 Completed 0 4m
pi-84ww8 0/1 Completed 0 4m
pi-gmcq5 0/1 Completed 0 5m
# kubectl get job
NAME DESIRED SUCCESSFUL AGE
pi 4 4 5m

Job Controller工作原理总结

Job Controller 控制的对象,直接就是 Pod。Job Controller 在控制循环中进行的调谐(Reconcile)操作,是根据实际在 Running 状态 Pod 的数目、已经成功退出的 Pod 的数目,以及 parallelism、completions 参数的值共同计算出在这个周期里,应该创建或者删除的 Pod 数目,然后调用 Kubernetes API 来执行这个操作。

在上面计算 Pi 值的这个例子中,当 Job 一开始创建出来时,实际处于 Running 状态的 Pod 数目 =0,已经成功退出的 Pod 数目 =0,而用户定义的 completions,也就是最终用户需要的 Pod 数目 =4。

所以,在这个时刻,需要创建的 Pod 数目 = 最终需要的 Pod 数目 – 实际在 Running 状态 Pod 数目 – 已经成功退出的 Pod 数目 = 4 – 0 – 0= 4。也就是说,Job Controller 需要创建 4 个 Pod 来纠正这个不一致状态。

可是,又定义了这个 Job 的 parallelism=2 规定了每次并发创建的 Pod 个数不能超过 2 个。所以,Job Controller 会对前面的计算结果做一个修正,修正后的期望创建的 Pod 数目应该是:2 个。

这时候,Job Controller 就会并发地向 kube-apiserver 发起两个创建 Pod 的请求。

类似地,如果在这次调谐周期里,Job Controller 发现实际在 Running 状态的 Pod 数目,比 parallelism 还大,那么它就会删除一些 Pod,使两者相等。

综上所述,Job Controller 实际上控制了,作业执行的并行度,以及总共需要完成的任务数这两个重要参数。而在实际使用时,你需要根据作业的特性,来决定并行度(parallelism)和任务数(completions)的合理取值。

23.6 Job控制器CronJob

Job 对象:CronJob

CronJob 描述的是定时任务,CronJob 是一个 Job 对象的控制器(Controller)

CronJob的 API 对象,如下所示:

# vim ./cronjob.yaml
apiVersion: batch/v1beta1
kind: CronJob
metadata:
name: hello
spec:
schedule: \”*/1 * * * *\”
jobTemplate:
spec:
template:
spec:
containers:
– name: hello
image: daocloud.io/library/busybox
args:
– /bin/sh
– -c
– date; echo Hello from the Kubernetes cluster
restartPolicy: OnFailure

CronJob 与 Job 的关系,同 Deployment 与 Pod 的关系一样。

CronJob 是一个专门用来管理 Job 对象的控制器。它创建和删除 Job 的依据,是 schedule 字段定义的、一个标准的Unix Cron格式的表达式。

比如:

*/1 * * * *
分钟、小时、日、月、星期

这个 Cron 表达式里 _/1 中的 _ 表示从 0 开始,/ 表示\”每\”,1 表示偏移量。所以,它的意思就是:从 0 开始,每 1 个时间单位执行一次。

本例表示从当前开始,每分钟执行一次

这里要执行的内容,就是 jobTemplate 定义的 Job 。

这个 CronJob 对象在创建 1 分钟后,就会有一个 Job 产生了,如下所示:

# kubectl create -f ./cronjob.yaml
cronjob \”hello\” created

一分钟后

# kubectl get jobs
NAME DESIRED SUCCESSFUL AGE
hello-4111706356 1 1 2s

此时,CronJob 对象会记录下这次 Job 执行的时间:新版本中在describe里显示执行时间

# kubectl get cronjob hello
NAME SCHEDULE SUSPEND ACTIVE LAST-SCHEDULE
hello */1 * * * * False 0 Thu, 6 Sep 2018 14:34:00 -070

spec.concurrencyPolicy 字段:

由于定时任务的特殊性,很可能某个 Job 还没有执行完,另外一个新 Job 就产生了。这时候,可以通过 spec.concurrencyPolicy 字段来定义具体的处理策略。

concurrencyPolicy=Allow

默认情况,表示这些 Job 可以同时存在;

concurrencyPolicy=Forbid

表示不会创建新的 Pod,该创建周期被跳过;

concurrencyPolicy=Replace

表示新产生的 Job 会替换旧的、没有执行完的 Job。

spec.startingDeadlineSeconds 字段:

如果某一次 Job 创建失败,这次创建就会被标记为\”miss\”。当在指定的时间窗口内,miss 的数目达到 100 时,那么 CronJob 会停止再创建这个 Job。

这个时间窗口,可以由 spec.startingDeadlineSeconds 字段指定。比如 startingDeadlineSeconds=200,意味着在过去 200 s 里,如果 miss 的数目达到了 100 次,那么这个 Job 就不会被创建执行了。

二十四.k8s之共享存储pv&pvc

24.1 存储资源管理

在基于k8s容器云平台上,对存储资源的使用需求通常包括以下几方面:

1.应用配置文件、密钥的管理;
2.应用的数据持久化存储;
3.在不同的应用间共享数据存储;

k8s的Volume抽象概念就是针对这些问题提供的解决方案,k8s的volume类型非常丰富,从临时目录、宿主机目录、ConfigMap、Secret、共享存储(PV和PVC)。

k8s支持Volume类型包括以下几类:

1.临时空目录(随着Pod的销毁而销毁)
emptyDir:
2.配置类(将配置文件以Volume的形式挂载到容器内)
ConfigMap:将保存在ConfigMap资源对象中的配置文件信息挂载到容器内的某个目录下
Secret:将保存在Secret资源对象中的密码密钥等信息挂载到容器内的某个文件中。
downwardAPI:将downwardAPI的数据以环境变量或文件的形式注入容器中。
gitErpo:将某Git代码库挂载到容器内的某个目录下
3.本地存储类
hostpath:将宿主机的目录或文件挂载到容器内进行使用。
local:Kubernetes从v1.9版本引入,将本地存储以PV的形式提供给容器使用,并能够实现存储空间的管理。
4.共享存储
PV(Persistent Volume):将共享存储定义为一种“持久存储卷”,可以被多个容器应用共享使用。
PVC(Persistent Volume Claim):用户对存储资源的一次“申请”,PVC申请的对象是PV,一旦申请成功,应用就能够像使用本地目录一样使用共享存储了。

共享存储主要用于多个应用都能够使用存储资源,例如NFS存储、光纤存储Glusterfs共享文件系统等,在k8s系统中通过PV/StorageClass和PVC来完成定义,并通过volumeMount挂载到容器的目录或文件进行使用。

ConfigMap、Secret、emptyDir、hostPath等属于临时性存储,当pod被调度到某个节点上时,它们随pod的创建而创建,临时占用节点存储资源,当pod离开节点时,存储资源被交还给节点,pod一旦离开这个节点,存储就失效,不具备持久化存储数据的能力。与此相反,持久化存储拥有独立的生命周期,具备持久化存储能力,其后端一般是独立的存储系统如NFS、iSCSI、cephfs、glusterfs等。

pv与pvc

Kubernetes中的node代表计算资源,而PersistentVolume(PV)则代表存储资源,它是对各种诸如NFS、iSCSI、云存储等各种存储后端所提供存储块的统一抽象从而提供网络存储,通过它屏蔽低层实现细节。与普通volume不同,PV拥有完全独立的生命周期。
因为PV表示的是集群能力,它是一种集群资源,所以用户(通常是开发人员)不能直接使用PV,就像不能直接使用内存一样,需要先向系统申请,而 PVC 就是请求存储资源的。
Kubernetes通过PersistentVolumeClaim(PVC)代理用户行为,用户通过对PVC的操作实现对PV申请、使用、释放等操作,PVC是用户层面的资源。
PV 和 PVC 可以将 pod 和数据卷解耦,pod 不需要知道确切的文件系统或者支持它的持久化引擎。

Kubernetes的共享存储供应模式包括静态和动态两种模式

静态模式
静态PV由系统管理员负责创建、提供、维护,系统管理员为用户屏蔽真正提供存储的后端及其实现细节,普通用户作为消费者,只需通过PVC申请、使用此类资源。
动态模式:
集群管理员无须手工创建PV,而是通过对Storage Class的设置对后端存储进行描述,“storage class”可以理解成某种具体的后端存储,标记为某种“类型(Class)”,此时要求PVC对存储的类型进行声明,系统将自动完成PV的创建与PVC的绑定。如果用户的PVC中“storage class”的值为\”\”,则表示不能为此PVC动态创建PV。

PV与PVC的绑定

用户创建包含容量、访问模式等信息的PVC,向系统请求存储资源。系统查找已存在PV或者监控新创建PV,如果与PVC匹配则将两者绑定。如果PVC创建动态PV,则系统将一直将两者绑定。PV与PVC的绑定是一一对应关系,不能重复绑定。如果系统一直没有为PVC找到匹配PV,则PVC无限期维持在\”unbound\”状态,直到系统找到匹配PV。实际绑定的PV容量可能大于PVC中申请的容量。

24.2 持久卷pv的类型

PV 持久卷是用插件的形式来实现的。Kubernetes 目前支持以下插件

– GCEPersistentDisk
– AWSElasticBlockStore
– AzureFile
– AzureDisk
– FC (Fibre Channel)
– Flexvolume
– Flocker
– NFS
– iSCSI
– RBD (Ceph Block Device)
– CephFS
– Cinder (OpenStack block storage)
– Glusterfs
– VsphereVolume
– Quobyte Volumes
– HostPath (Single node testing only – local storage is not supported in any way and WILL NOT WORK in a multi-node cluster)
– Portworx Volumes
– ScaleIO Volumes
– StorageOS

24.5 实验mysql基于NFS共享存储实现持久化存储
24.5.1 安装NFS

机器准备

k8s-master 制作nfs服务端作为共享文件系统
[root@k8s-master ~]# yum install -y nfs-utils rpcbind
[root@k8s-master ~]# mkdir /mnt/data #制作共享目录
[root@k8s-master ~]# vim /etc/exports
/mnt/data 192.168.122.0/24(rw,no_root_squash)
[root@k8s-master ~]# systemctl start rpcbind nfs-server #启动服务
# 使配置生效
[root@k8s-master ~]# exportfs -r
# 检查配置是否生效
[root@k8s-master ~]# exportfs
集群中的工作节点都需要安装客户端工具,主要作用是节点能够驱动 nfs 文件系统
只安装,不启动服务
# yum install -y nfs-utils
# showmount -e master_ip
# mount -t nfs master_ip:/mnt/data /mnt/data
master节点
制作pv.yaml(一般这个动作由 kubernetes 管理员完成,也就是我们运维人员)
[root@k8s-master ~]# mkdir /k8s/mysql -p
[root@k8s-master ~]# cd /k8s/mysql/
[root@k8s-master mysql]# vim pv.yaml
apiVersion: v1
kind: PersistentVolume #类型定义为pv
metadata:
name: my-pv #pv的名字
labels: #定义标签
type: nfs #类型为nfs
spec:
nfs: # 存储类型,需要与底层实际的存储一致,这里采用 nfs
server: 192.168.122.24 # NFS 服务器的 IP
path: \”/mnt/data\” # NFS 上共享的目录
capacity: #定义存储能力
storage: 3Gi #指定存储空间
accessModes: #定义访问模式
– ReadWriteMany #读写权限,允许被多个Node挂载
persistentVolumeReclaimPolicy: Retain #定义数据回收策略,这里是保留

24.5.2 PV参数详解

1.存储能力(Capacity)

描述存储设备的能力,目前仅支持对存储空间的设置(storage=xx)。

2.访问模式(Access Modes)

对PV进行访问模式的设置,用于描述用户应用对存储资源的访问权限。访问模式如下:

ReadWriteOnce:读写权限,并且只能被单个pod挂载。
ReadOnlyMany:只读权限,允许被多个pod挂载。
ReadWriteMany:读写权限,允许被多个pod挂载。
某些PV可能支持多种访问模式,但PV在挂载时只能使用一种访问模式,多种访问模式不能同时生效。

3.persistentVolumeReclaimPolicy定义数据回收策略

目前支持如下三种回收策略:

保留(Retain):保留数据,需要手工处理。
回收空间(Recycle):警告: 回收策略 Recycle 已被废弃。取而代之的建议方案是使用动态供应。如果下层的卷插件支持,回收策略 Recycle 会在卷上执行一些基本的 擦除(rm -rf /thevolume/*)操作,之后允许该卷用于新的 PVC 申领
删除(Delete):会将 PersistentVolume 对象从 Kubernetes 中移除,同时也会从外部基础设施(如 AWS EBS、GCE PD、Azure Disk 或 Cinder 卷)中移除所关联的存储资产。 就是把云存储一起删了。

目前,只有 NFS 和 HostPath 两种类型的存储设备支持 “Recycle” 策略; AWS EBS、 GCE PD、Azure Disk 和 Cinder volumes 支持 “Delete” 策略。

4.storageClassName存储类别(Class)

PV可以设定其存储的类型(Class),通过 storageClassName参数指定一个 StorageClass 资源对象的名称。
具有特定“类别”的 PV 只能与请求了该“类别”的 PVC 进行绑定。未设定 “类别” 的 PV 则只能与不请求任何 “类别” 的 PVC 进行绑定。
相当于一个标签。

24.5.3 创建pv

[root@k8s-master mysql]# kubectl apply -f pv.yaml
persistentvolume/my-pv created
[root@k8s-master mysql]# kubectl get pv

PV 生命周期的各个阶段(Phase)

某个 PV 在生命周期中,可以处于以下4个阶段之一:

– Available:可用状态,还未与某个 PVC 绑定。
– Bound:已与某个 PVC 绑定。
– Released:释放,绑定的 PVC 已经删除,但没有被集群回收存储空间 。
– Failed:自动资源回收失败。

pv创建成功目前属于可用状态,还没有与pvc绑定,那么现在创建pvc

创建pvc

[root@k8s-master mysql]# vim mysql-pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim #定义类型为PVC
metadata:
name: mypvc #声明pvc的名称,当做pod的卷使用时会用到
spec:
accessModes: #定义访问pvc的模式,与pv拥有一样的模式
– ReadWriteMany #读写权限,允许被多个pod挂载
resources: #声明可以请求特定数量的资源,目前仅支持 request.storage 的设置,即存储空间大小。
requests:
storage: 3Gi #定义空间大小
selector: #PV选择条件,标签选择器,通过标签选择
matchLabels:
type: \”nfs\” #选择pv类型的nfs

注意:当我们申请pvc的容量大于pv的容量是无法进行绑定的。
创建pvc
[root@k8s-master mysql]# kubectl apply -f mysql-pvc.yaml
persistentvolumeclaim/mypvc created
[root@k8s-master mysql]# kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
mypvc Bound my-pv 3Gi RWX pv-nfs 37s

status状态

– Available (可用): 表示可用状态,还未被任何PVC绑定
– Bound (已绑定):已经绑定到某个PVC
– Released (已释放):对应的PVC已经删除,但资源还没有被集群收回
– Failed:PV自动回收失败

Kubernetes中会自动帮我们查看pv状态为Available并且根据声明pvc容量storage的大小进行筛选匹配,同时还会根据AccessMode进行匹配。如果pvc匹配不到pv会一直处于pending状态。

24.5.4 mysql使用pvc持久卷

创建secret
[root@k8s-master mysql]# echo -n \’QianFeng@123!\’ | base64
UWlhbkZlbmdAMTIzIQ==
[root@k8s-master mysql]# vim mysql-secret.yaml
apiVersion: v1
data:
password: UWlhbkZlbmdAMTIzIQ==
kind: Secret
metadata:
annotations:
name: my-pass
type: Opaque
[root@k8s-master mysql]# kubectl apply -f mysql-secret.yaml
secret/my-pass created
[root@k8s-master mysql]# kubectl get secret
NAME TYPE DATA AGE
default-token-24c52 kubernetes.io/service-account-token 3 6d22h
my-pass Opaque 1 69s
创建myslq-pod文件
[root@k8s-master mysql]# cat mysql-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-mysql
spec:
replicas: 1
selector:
matchLabels:
app: mysql
template:
metadata:
labels:
app: mysql
spec:
containers:
– name: my-mysql
image: daocloud.io/library/mysql:5.7
ports:
– containerPort: 3306
env:
– name: MYSQL_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: my-pass
key: password
volumeMounts:
– name: mysql-data
mountPath: /var/lib/mysql
volumes:
– name: mysql-data
persistentVolumeClaim: #绑定pvc
claimName: mypvc #指定对应的pvc名字
[root@k8s-master mysql]# kubectl apply -f mysql-deployment.yaml
[root@k8s-master mysql]# kubectl get pod
NAME READY STATUS RESTARTS AGE
my-mysql-5474b6885f-c5dmp 1/1 Running 0 8s
[root@k8s-master mysql]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
my-mysql-5474b6885f-c5dmp 1/1 Running 0 40s 10.244.2.5 k8s-node2 <none> <none>
测试
[root@k8s-master ~]# cd /mnt/data/
[root@k8s-master data]# ll
总用量 188484
-rw-r—– 1 polkitd ssh_keys 56 11月 8 21:49 auto.cnf
-rw——- 1 polkitd ssh_keys 1680 11月 8 21:49 ca-key.pem
-rw-r–r– 1 polkitd ssh_keys 1112 11月 8 21:49 ca.pem
-rw-r–r– 1 polkitd ssh_keys 1112 11月 8 21:49 client-cert.pem
-rw——- 1 polkitd ssh_keys 1680 11月 8 21:49 client-key.pem
-rw-r—– 1 polkitd ssh_keys 688 11月 8 21:57 ib_buffer_pool
-rw-r—– 1 polkitd ssh_keys 79691776 11月 8 21:59 ibdata1
-rw-r—– 1 polkitd ssh_keys 50331648 11月 8 21:59 ib_logfile0
-rw-r—– 1 polkitd ssh_keys 50331648 11月 8 21:49 ib_logfile1
-rw-r—– 1 polkitd ssh_keys 12582912 11月 8 22:00 ibtmp1
drwxr-x— 2 polkitd ssh_keys 4096 11月 8 21:49 mysql
drwxr-x— 2 polkitd ssh_keys 8192 11月 8 21:49 performance_schema
-rw——- 1 polkitd ssh_keys 1680 11月 8 21:49 private_key.pem
-rw-r–r– 1 polkitd ssh_keys 452 11月 8 21:49 public_key.pem
-rw-r–r– 1 polkitd ssh_keys 1112 11月 8 21:49 server-cert.pem
-rw——- 1 polkitd ssh_keys 1676 11月 8 21:49 server-key.pem
drwxr-x— 2 polkitd ssh_keys 8192 11月 8 21:49 sys

24.6 动态绑定pv

StorageClass 相当于一个创建 PV 的模板,用户通过 PVC 申请存储卷,StorageClass 通过模板自动创建 PV,然后和 PVC 进行绑定。

StorageClass创建动态存储卷流程

集群管理员预先创建存储类(StorageClass);用户创建使用存储类的持久化存储声明(PVC:PersistentVolumeClaim);存储持久化声明通知系统,它需要一个持久化存储(PV: PersistentVolume);系统读取存储类的信息;系统基于存储类的信息,在后台自动创建PVC需要的PV;用户创建一个使用PVC的Pod;Pod中的应用通过PVC进行数据的持久化;而PVC使用PV进行数据的最终持久化处理。

StorageClass支持的动态存储插件:

NFS Provisioner 是一个自动配置卷程序,它使用现有的和已配置的 NFS 服务器来支持通过持久卷声明动态配置 Kubernetes 持久卷。

注意:k8s 1.21版本中创建pvc时nfs-provisioner会报错

E0903 08:00:24.858523 1 controller.go:1004] provision “default/test-claim” class “managed-nfs-storage”: unexpected error getting claim reference: selfLink was empty, can’t make reference

解决方法:
修改 /etc/kubernetes/manifests/kube-apiserver.yaml文件
增加 – –feature-gates=RemoveSelfLink=false

spec:
containers:
– command:
– kube-apiserver
– –feature-gates=RemoveSelfLink=false # 增加这行
– –advertise-address=172.24.0.5
– –allow-privileged=true
– –authorization-mode=Node,RBAC
– –client-ca-file=/etc/kubernetes/pki/ca.crt

24.6.1 配置nfs-provisioner授权

创建ServiceAccount、ClusterRole、ClusterRoleBinding等,为nfs-client-provisioner授权

# rbac.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: nfs-client-provisioner
# replace with namespace where provisioner is deployed
namespace: default

kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: nfs-client-provisioner-runner
rules:
– apiGroups: [\”\”]
resources: [\”persistentvolumes\”]
verbs: [\”get\”, \”list\”, \”watch\”, \”create\”, \”delete\”]
– apiGroups: [\”\”]
resources: [\”persistentvolumeclaims\”]
verbs: [\”get\”, \”list\”, \”watch\”, \”update\”]
– apiGroups: [\”storage.k8s.io\”]
resources: [\”storageclasses\”]
verbs: [\”get\”, \”list\”, \”watch\”]
– apiGroups: [\”\”]
resources: [\”events\”]
verbs: [\”create\”, \”update\”, \”patch\”]

kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: run-nfs-client-provisioner
subjects:
– kind: ServiceAccount
name: nfs-client-provisioner
# replace with namespace where provisioner is deployed
namespace: default
roleRef:
kind: ClusterRole
name: nfs-client-provisioner-runner
apiGroup: rbac.authorization.k8s.io

kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: leader-locking-nfs-client-provisioner
# replace with namespace where provisioner is deployed
namespace: default
rules:
– apiGroups: [\”\”]
resources: [\”endpoints\”]
verbs: [\”get\”, \”list\”, \”watch\”, \”create\”, \”update\”, \”patch\”]

kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: leader-locking-nfs-client-provisioner
subjects:
– kind: ServiceAccount
name: nfs-client-provisioner
# replace with namespace where provisioner is deployed
namespace: default
roleRef:
kind: Role
name: leader-locking-nfs-client-provisioner
apiGroup: rbac.authorization.k8s.io

24.6.2 部署nfs-client-provisioner

# nfs-provisioner.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nfs-client-provisioner
labels:
app: nfs-client-provisioner
# replace with namespace where provisioner is deployed
namespace: default #与RBAC文件中的namespace保持一致
spec:
replicas: 1
selector:
matchLabels:
app: nfs-client-provisioner
strategy:
type: Recreate
selector:
matchLabels:
app: nfs-client-provisioner
template:
metadata:
labels:
app: nfs-client-provisioner
spec:
serviceAccountName: nfs-client-provisioner
containers:
– name: nfs-client-provisioner
image: quay.io/external_storage/nfs-client-provisioner:latest
volumeMounts:
– name: nfs-client-root
mountPath: /persistentvolumes
env:
– name: PROVISIONER_NAME
value: gxf-nfs-storage #provisioner名称,请确保该名称与 nfs-StorageClass.yaml文件中的provisioner名称保持一致
– name: NFS_SERVER
value: 10.24.X.X #NFS Server IP地址
– name: NFS_PATH
value: /home/nfs/1 #NFS挂载卷
volumes:
– name: nfs-client-root
nfs:
server: 10.24.X.X #NFS Server IP地址
path: /home/nfs/1 #NFS 挂载卷

# 部署
[root@kube-master ~]# kubectl apply -f rbac.yaml
[root@kube-master ~]# kubectl apply -f nfs-provisioner.yaml
[root@kube-master ~]# kubectl get pod
NAME READY STATUS RESTARTS AGE
nfs-client-provisioner-75bfdbdcd8-5mqbv 1/1 Running 0 4m24s

24.6.3 创建StorageClass

# nfs-StorageClass.yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: managed-nfs-storage
provisioner: gxf-nfs-storage #这里的名称要和provisioner配置文件中的环境变量PROVISIONER_NAME保持一致
reclaimPolicy: Retain # 默认为delete
parameters:
archiveOnDelete: \”true\” # false表示pv被删除时,在nfs下面对应的文件夹也会被删除,true正相反

24.6.4 deployment 动态挂载

部署一个有2个副本的deployment,挂载共享目录

创建pvc,“storageClassName\”为上面创建的\”managed-nfs-storage”,即指定动态创建PV的模板文件的名字。
# test-pvclaim.yaml
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: test-claim
spec:
accessModes:
– ReadWriteMany
resources:
requests:
storage: 100Mi
storageClassName: managed-nfs-storage

[root@kube-master ~]# kubectl apply -f test-pvclaim.yaml

deployment部署

# test-deploy.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: test-deploy
labels:
app: test-deploy
namespace: default #与RBAC文件中的namespace保持一致
spec:
replicas: 2
selector:
matchLabels:
app: test-deploy
strategy:
type: Recreate
selector:
matchLabels:
app: test-deploy
template:
metadata:
labels:
app: test-deploy
spec:
containers:
– name: test-pod
image: busybox:1.24
command:
– \”/bin/sh\”
args:
– \”-c\”
# – \”touch /mnt/SUCCESS3 && exit 0 || exit 1\” #创建一个SUCCESS文件后退出
– touch /mnt/SUCCESS5; sleep 50000
volumeMounts:
– name: nfs-pvc
mountPath: \”/mnt\”
# subPath: test-pod-3 # 子路径 (这路基代表存储卷下面的test-pod子目录)
volumes:
– name: nfs-pvc
persistentVolumeClaim:
claimName: test-claim #与PVC名称保持一致

24.6.5 statefulset 动态挂载

# test-sts-1.yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: test-sts
labels:
k8s-app: test-sts
spec:
serviceName: test-sts-svc
replicas: 3
selector:
matchLabels:
k8s-app: test-sts
template:
metadata:
labels:
k8s-app: test-sts
spec:
containers:
– image: busybox:1.24
name: test-pod
command:
– \”/bin/sh\”
args:
– \”-c\”
# – \”touch /mnt/SUCCESS3 && exit 0 || exit 1\” #创建一个SUCCESS文件后退出
– touch /mnt/SUCCESS5; sleep 50000
imagePullPolicy: IfNotPresent
volumeMounts:
– name: nfs-pvc
mountPath: \”/mnt\”
volumeClaimTemplates:
– metadata:
name: nfs-pvc
spec:
accessModes: [\”ReadWriteMany\”]
storageClassName: managed-nfs-storage
resources:
requests:
storage: 20Mi

[root@kube-master ~]# kubectl apply -f test-sts-1.yaml
[root@kube-master ~]# kubectl get sts
NAME READY AGE
test-sts 3/3 4m46s

二十五.StatefulSet有状态的应用

在Kubernetes系统中,Pod的管理对象RC、Deployment、DaemonSet都是面向无状态的服务,它们所管理的Pod的IP、名字,启停顺序等都是随机的。但现实中有很多服务是有状态的,特别是一些复杂的中间件集群。例如Mysql集群、MongoDB集群、Kafka集群、Zookeeper集群等,这些都有一个共同的特点:

1.每个节点都有固定的ID,通过这个ID,集群中的成员可以相互发现并且通信。
2.集群的规模是比较固定的,集群规模不能随意改动。
3.集群里的每个节点都是有状态的,通常会持久化数据到永久存储中。
4.如果磁盘损坏,集群里的某个节点就无法正常运行,集群功能受损。

StatefulSet本质上是Deployment的一种变体,在v1.9版本中已成为GA版本,它为了解决有状态服务的问题,它所管理的Pod拥有固定的Pod名称,启停顺序的,如果使用RC/Deployment控制副本的方式实现有状态的集群那么pod的名字是没有办法控制的因为是随机产生,同时也无法为每一个pod确定唯一不变的ID号,为了解决这个问题,在k8s中引用了新的资源对象即StatefulSet。在StatefulSet中,Pod名字称为网络标识(hostname),还必须要用到共享存储。
1.StatefulSet里的每个Pod都有稳定、唯一的网络标识,可以用来发现集群中的其他成员,比如说StatefulSet的名字是N,那么第一个pod的名字就是N-0,第二个就是N-1,以此类推
2.StatefulSet控制的pod副本的启停顺序是受控制的,操作第n个pod,前面的n-1的pod已经是运行且准备好的状态。
3.StatefulSet里的pod采用稳定的持久化存储卷,通过PV/PVC来实现,删除Pod时默认不会删除与StatefulSet相关的存储卷(目的是为了保证数据的安全性)
所以,StatefulSet的核心功能,就是通过某种方式,记录这些状态,然后在Pod被重新创建时,能够为新Pod恢复这些状态.

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

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

$(podname).(headless server name)

25.1 StatefulSet实现Pod的存储状态

通过PVC机制来实现存储状态管理

在StatefulSet对象中除了定义PodTemplate还会定义一个volumeClaimTemplates凡是被这个StatefulSet管理的Pod都会声明一个对应的PVC,这个PVC的定义就来自于 volumeClaimTemplates这个模板字段,这个PVC的名字,会被分配一个与这个Pod完全一致的编号。
把一个Pod比如N-0删除之后,这个Pod对应的PVC和PV并不会被删除,而这个Volume 里已经写入的数据,也依然会保存在远程存储服务里
StatefulSet在重新创建N-0这个pod的时候.它声明使用的PVC的名字还是叫作:N-0 这个PVC的定义,还是来自于PVC模板(volumeClaimTemplates)这是StatefulSet创建 Pod的标准流程
Kubernetes为它查找名叫N-0的PVC时,就会直接找到旧Pod遗留下来的同名的 PVC进而找到跟这个PVC绑定在一起的PV.这样新的Pod就可以挂载到旧Pod对应的那个Volume并且获取到保存在Volume里的数据.通过这种方式Kubernetes的StatefulSet就实现了对应用存储状态的管理

25.2 StatefulSet 的应用特点

StatefulSet的核心功能就是,通过某种方式记录应用状态,在Pod被重建的时候,通过这种方式还可以恢复原来的状态。StatefulSet由以下几个部分组成:
Headless Service 用于定义网络标识(DNS)volumeClaimTemplates  用于创建PVStatefulSet  用于定义具体应用 稳定且有唯一的网络标识符 当节点挂掉,既pod重新调度后其PodName和HostName不变,基于Headless Service来实现稳定且持久的存储  当节点挂掉,既pod重新调度能访问到相同的持久化存储,基于PVC实现有序、平滑的扩展、部署 即Pod是有顺序的,在部署或者扩展的时候要依据定义的顺序依次进行(即从0到N-1,在下一个Pod运行之前所有之前的Pod必须都是Running和Ready状态),基于init containers来实现。有序、平滑的收缩、删除 既Pod是有顺序的,在收缩或者删除的时候要依据定义的顺序依次进行(既从N-1到0,既倒序)。**拓扑状态。**是应用多个实例之间的不完全对等的关系,这些应用实例是必须按照定义的顺序启动的。例如主应用A先于从应用B启动,如果把A和B删掉,应用还要按照先启动主应用A再启动从应用B,且创建的应用必须和原来的应用的网络标识一样(既PodName和HostName)。这样他们就可以按照原来的顺序创建了。存储状态。应用实例分别绑定了不同的数据存储,Pod A第一次读到的数据要和10分钟后读到的数据,是同一份。哪怕这期间Pod A被重建。这种典型的例子就是数据库应用的多个存储实例。

25.3 实战1-通过StatefulSet创建nginx

由于statefulSet在创建的时候使用的是动态绑定pvc,而动态绑定pvc是需要插件支持的,一下实验通过手动创建pv在通过StatefulSet自动实现pvc申请持久卷。

1.创建基于NFS的pv持久卷
[root@k8s-master ~]# mkdir /mnt/data-1 #创建共享目录
[root@k8s-master ~]# mkdir /mnt/data-2
[root@k8s-master ~]# vim /etc/exports
/mnt/data-1 172.16.229.*/24 (rw,sync,insecure,no_subtree_check,no_root_squash)
/mnt/data-2 172.16.229.*/24 (rw,sync,insecure,no_subtree_check,no_root_squash)
[root@k8s-master ~]# systemctl restart nfs
[root@k8s-master ~]# mkdir /k8s/nginx #创建工作目录
[root@k8s-master ~]# cd /k8s/nginx/
[root@k8s-master nginx]# vim nginx-pv.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv-0
labels:
type: pv-0
spec:
capacity:
storage: 5Gi
accessModes:
– ReadWriteMany
persistentVolumeReclaimPolicy: Recycle
nfs:
server: 172.16.229.143
path: \”/mnt/data-1\”

apiVersion: v1
kind: PersistentVolume
metadata:
name: pv-1
labels:
type: pv-1
spec:
capacity:
storage: 5Gi
accessModes:
– ReadWriteMany
persistentVolumeReclaimPolicy: Recycle
nfs:
server: 172.16.229.143
path: \”/mnt/data-2\”

2.创建pv
[root@k8s-master nginx]# kubectl apply -f nginx-pv.yaml
persistentvolume/nginx-pv created
[root@k8s-master nginx]# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
my-pv 3Gi RWX Recycle Bound default/mypvc pv-nfs 6d16h
pv-0 5Gi RWX Recycle Available 43s
pv-1 5Gi RWX Recycle Available 43s
3.创建 service的Headless Service无头服务
[root@k8s-master nginx]# vim nginx-service.yaml
apiVersion: v1
kind: Service
metadata:
name: nginx #service的名字
labels:
app: nginx
spec:
ports:
– port: 80
name: web
clusterIP: None #采用什么类型的service,这里采用的是Headless Service
selector:
app: nginx
[root@k8s-master nginx]# kubectl apply -f nginx-service.yaml
service/nginx created
[root@k8s-master nginx]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 12d
nginx ClusterIP None <none> 80/TCP 5s
4.创建StatefulSet,通过StatefulSet创建pvc实现自动绑定已经创建好的pv
[root@k8s-master nginx]# cat nginx-web.yaml
apiVersion: apps/v1 #选择apiserver的版本
kind: StatefulSet #定义pod类型
metadata:
name: nginx-web
spec:
serviceName: \”nginx\” #定义属于哪个Headless Service
replicas: 2 #定义pod的副本
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
– name: nginx
image: daocloud.io/library/nginx
ports:
– containerPort: 80
name: web
volumeMounts: #定义卷的挂载到pod的路径
– name: nginx-data
mountPath: /usr/share/nginx/html
volumeClaimTemplates: #pvc的申请模版,会自动创建pvc并与pv绑定。从而实现各pod有专用存储(我们使用的是静态创建pv)
– metadata:
name: nginx-data #pvc的名字
spec:
accessModes: [\”ReadWriteMany\”]
resources:
requests:
storage: 5Gi #指定每一个持久卷的大小
[root@k8s-master nginx]# kubectl apply -f nginx-web.yaml
查看statefulset
[root@k8s-master nginx]# kubectl get statefulset nginx-web
NAME READY AGE
nginx-web 2/2 19m
查看service
[root@k8s-master nginx]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
nginx ClusterIP None <none> 80/TCP 18m

为什么需要volumeClaimTemplate?
对于有状态的副本集都会用到持久存储,对于分布式系统来讲,它的最大特点是数据是不一样的,所以各个节点不能使用同一存储卷,每个节点有自已的专用存储,但是如果在Deployment中的Pod template里定义的存储卷,是所有副本集共用一个存储卷,数据是相同的,因为是基于模板来的 ,而statefulset中每个Pod都要自已的专有存储卷,所以statefulset的存储卷就不能再用Pod模板来创建了,于是statefulSet使用volumeClaimTemplate,称为卷申请模板,它会为每个Pod生成不同的pvc,并绑定pv, 从而实现各pod有专用存储。这就是为什么要用volumeClaimTemplate的原因。

25.3.1 顺序创建 Pod

对于一个拥有 N 个副本的 StatefulSet,Pod 被部署时是按照 {0 …… N-1} 的序号顺序创建的。在终端中使用 kubectl get 检查输出。这个输出最终将看起来像下面的样子

[root@k8s-master nginx]# kubectl get -l app=\’nginx\’ pods
NAME READY STATUS RESTARTS AGE
nginx-web-0 1/1 Running 0 18m
nginx-web-1 1/1 Running 0 6m34s

请注意在 nginx-web-0 Pod 处于 Running和Ready 状态后 nginx-web-1 Pod 才会被启动

如同 StatefulSets 概念中所提到的,StatefulSet 中的 Pod 拥有一个具有黏性的、独一无二的身份标志。这个标志基于 StatefulSet 控制器分配给每个 Pod 的唯一顺序索引。Pod 的名称的形式为<statefulset name>-<ordinal index>。webStatefulSet 拥有两个副本,所以它创建了两个 Pod:nginx-web-0和nginx-web-1。

25.3.2 使用稳定的网络身份标识

每个 Pod 都拥有一个基于其顺序索引的稳定的主机名。使用[kubectl exec](https://kubernetes.io/zh/docs/reference/generated/kubectl/kubectl-commands/#exec)在每个 Pod 中执行hostname

[root@k8s-master nginx]# for i in 0 1; do kubectl exec nginx-web-$i — /bin/bash -c \’hostname\’; done
nginx-web-0
nginx-web-1

25.3.3 查看statefulset创建的pod的存储

获取 nginx-web-0 和 nginx-web-1 的 PersistentVolumeClaims。

[root@k8s-master nginx]# kubectl get pvc -l app=\’nginx\’
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
nginx-data-nginx-web-0 Bound pv-0 5Gi RWX 53m
nginx-data-nginx-web-1 Bound pv-1 5Gi RWX 53m

StatefulSet 控制器创建了两个 PersistentVolumeClaims,绑定到两个 PersistentVolumes。由于这里我们使用的是手动创建pv,所有的 PersistentVolume 都是手动创建自动的绑定。

NGINX web 服务器默认会加载位于 /usr/share/nginx/html/index.html 的 index 文件。StatefulSets spec 中的 volumeMounts 字段保证了 /usr/share/nginx/html 文件夹由一个 PersistentVolume 支持。

将 Pod 的主机名写入它们的index.html文件并验证 NGINX web 服务器使用该主机名提供服务。

[root@k8s-master nginx]# kubectl exec -it nginx-web-0 /bin/bash
root@nginx-web-0:/# echo nginx-web-0 >> /usr/share/nginx/html/index.html
root@nginx-web-0:/# exit
exit
[root@k8s-master nginx]# kubectl exec -it nginx-web-1 /bin/bash
root@nginx-web-1:/# echo nginx-web-1 >> /usr/share/nginx/html/index.html
root@nginx-web-1:/# exit
exit
[root@k8s-master nginx]# kubectl get pods -l app=\’nginx\’ -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-web-0 1/1 Running 0 84m 10.244.2.24 k8s-node2 <none> <none>
nginx-web-1 1/1 Running 0 84m 10.244.1.26 k8s-node1 <none> <none>
[root@k8s-master nginx]# curl -s http://10.244.2.24
nginx-web-0
[root@k8s-master nginx]# curl -s http://10.244.1.26
nginx-web-1

25.3.4 说明

请注意,如果你看见上面的 curl 命令返回了 403 Forbidden 的响应,你需要像这样修复使用 `volumeMounts`挂载的目录的权限:
# for i in 0 1; do kubectl exec web-$i — chmod 755 /usr/share/nginx/html; done

将StatefulSet创建的 所有 Pod删除

[root@k8s-master nginx]# kubectl delete -f nginx-web.yaml
statefulset.apps \”nginx-web\” deleted

在使用StatefulSet重新创建的所有Pod

[root@k8s-master nginx]# kubectl apply -f nginx-web.yaml
statefulset.apps/nginx-web created
[root@k8s-master nginx]# kubectl get pods -l app=\’nginx\’ -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-web-0 1/1 Running 0 14s 10.244.2.25 k8s-node2 <none> <none>
nginx-web-1 1/1 Running 0 12s 10.244.1.27 k8s-node1 <none> <none>

我们发现pod的IP地址发生了变化,接下来再次访问

[root@k8s-master nginx]# curl -s http://10.244.2.25
nginx-web-0
[root@k8s-master nginx]# curl -s http://10.244.1.27
nginx-web-1

虽然 nginx-web-0 和 nginx-web-1 被重新调度了,但它们仍然继续监听各自的主机名,因为和它们的 PersistentVolumeClaim 相关联的 PersistentVolume 被重新挂载到了各自的 volumeMount 上。不管 nginx-web-0 和 nginx-web-1 被调度到了哪个节点上,它们的 PersistentVolumes 将会被挂载到合适的挂载点上。

25.3.5 扩容/缩容 StatefulSet

扩容/缩容 StatefulSet 指增加或减少它的副本数。这通过更新 replicas 字段完成.如果是手动创建的pv,那么需要提前将pv创建好了。

25.4 实战2-通过StatefulSet来管理有状态的服务msyql

[root@k8s-master ~]# mkdir /mnt/data-3
[root@k8s-master ~]# vim /etc/exports
/mnt/data-3 172.16.229.*/24 (rw,sync,insecure,no_subtree_check,no_root_squash)
[root@k8s-master ~]# systemctl restart nfs
[root@k8s-master ~]# mkdir /k8s/application/mysql
[root@k8s-master mysql]# cat mysql.pv.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: mysql-pv
labels:
type: mysql-pv
spec:
capacity:
storage: 5Gi
accessModes:
– ReadWriteMany
persistentVolumeReclaimPolicy: Recycle
nfs:
server: 172.16.229.143
path: \”/mnt/data-3\”

[root@k8s-master mysql]# cat mysql-svc.yaml
apiVersion: v1
kind: Service
metadata:
name: mysql
labels:
app: mysql
spec:
ports:
– port: 3306
name: mysql
clusterIP: None
selector:
app: mysql

[root@k8s-master mysql]# cat mysql-server.yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: mysql
spec:
serviceName: \”mysql\”
replicas: 1
selector:
matchLabels:
app: mysql
template:
metadata:
labels:
app: mysql
spec:
containers:
– name: mysql
image: daocloud.io/library/mysql:5.7
ports:
– containerPort: 3306
env:
– name: MYSQL_ROOT_PASSWORD
value: \”Qfedu@123\”
volumeMounts:
– name: mysql-data
mountPath: /var/lib/mysql
volumeClaimTemplates:
– metadata:
name: mysql-data
spec:
accessModes: [\”ReadWriteMany\”]
resources:
requests:
storage: 5Gi

[root@k8s-master mysql]# kubectl apply -f mysql.pv.yaml
persistentvolume/mysql-pv created
[root@k8s-master mysql]# kubectl apply -f mysql-svc.yaml
service/mysql created
[root@k8s-master mysql]# kubectl apply -f mysql-server.yaml
statefulset.apps/mysql created
[root@k8s-master mysql]# kubectl get pv | grep mysql
mysql-pv 5Gi RWX Recycle Bound default/mysql-data-mysql-0 7m28s
[root@k8s-master mysql]# kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
mysql-data-mysql-0 Bound mysql-pv 5Gi RWX 7m16s
[root@k8s-master mysql]# kubectl get pods -l app=\’mysql\’ -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
mysql-0 1/1 Running 0 6m22s 10.244.1.28 k8s-node1 <none> <none>
[root@k8s-master mysql]# kubectl exec -it mysql-0 /bin/bash
root@mysql-0:/# mysql -uroot -p\’Qfedu@123\’
mysql: [Warning] Using a password on the command line interface can be insecure.
Welcome to the MySQL monitor. Commands end with ; or \\g.

mysql> show databases;
+——————–+
| Database |
+——————–+
| information_schema |
| mysql |
| performance_schema |
| sys |
+——————–+
4 rows in set (0.01 sec)
mysql> create database test1;
Query OK, 1 row affected (0.00 sec)
mysql> exit
Bye
root@mysql-0:/# exit
exit
[root@k8s-master mysql]# cd /mnt/data-3/
[root@k8s-master data-3]# ll
总用量 188484
-rw-r—– 1 polkitd ssh_keys 56 11月 15 16:24 auto.cnf
-rw——- 1 polkitd ssh_keys 1676 11月 15 16:24 ca-key.pem
-rw-r–r– 1 polkitd ssh_keys 1112 11月 15 16:24 ca.pem
-rw-r–r– 1 polkitd ssh_keys 1112 11月 15 16:24 client-cert.pem
-rw——- 1 polkitd ssh_keys 1680 11月 15 16:24 client-key.pem
-rw-r—– 1 polkitd ssh_keys 1353 11月 15 16:25 ib_buffer_pool
-rw-r—– 1 polkitd ssh_keys 79691776 11月 15 16:25 ibdata1
-rw-r—– 1 polkitd ssh_keys 50331648 11月 15 16:25 ib_logfile0
-rw-r—– 1 polkitd ssh_keys 50331648 11月 15 16:24 ib_logfile1
-rw-r—– 1 polkitd ssh_keys 12582912 11月 15 16:25 ibtmp1
drwxr-x— 2 polkitd ssh_keys 4096 11月 15 16:25 mysql
drwxr-x— 2 polkitd ssh_keys 8192 11月 15 16:25 performance_schema
-rw——- 1 polkitd ssh_keys 1680 11月 15 16:24 private_key.pem
-rw-r–r– 1 polkitd ssh_keys 452 11月 15 16:24 public_key.pem
-rw-r–r– 1 polkitd ssh_keys 1112 11月 15 16:24 server-cert.pem
-rw——- 1 polkitd ssh_keys 1680 11月 15 16:24 server-key.pem
drwxr-x— 2 polkitd ssh_keys 8192 11月 15 16:25 sys
drwxr-x— 2 polkitd ssh_keys 20 11月 15 16:29 test1
[root@k8s-master mysql]# kubectl delete -f mysql-server.yaml
statefulset.apps \”mysql\” deleted
[root@k8s-master mysql]# cd /mnt/data-3/
[root@k8s-master data-3]# ll
总用量 176196
-rw-r—– 1 polkitd ssh_keys 56 11月 15 16:24 auto.cnf
-rw——- 1 polkitd ssh_keys 1676 11月 15 16:24 ca-key.pem
-rw-r–r– 1 polkitd ssh_keys 1112 11月 15 16:24 ca.pem
-rw-r–r– 1 polkitd ssh_keys 1112 11月 15 16:24 client-cert.pem
-rw——- 1 polkitd ssh_keys 1680 11月 15 16:24 client-key.pem
-rw-r—– 1 polkitd ssh_keys 688 11月 15 16:32 ib_buffer_pool
-rw-r—– 1 polkitd ssh_keys 79691776 11月 15 16:32 ibdata1
-rw-r—– 1 polkitd ssh_keys 50331648 11月 15 16:32 ib_logfile0
-rw-r—– 1 polkitd ssh_keys 50331648 11月 15 16:24 ib_logfile1
drwxr-x— 2 polkitd ssh_keys 4096 11月 15 16:25 mysql
drwxr-x— 2 polkitd ssh_keys 8192 11月 15 16:25 performance_schema
-rw——- 1 polkitd ssh_keys 1680 11月 15 16:24 private_key.pem
-rw-r–r– 1 polkitd ssh_keys 452 11月 15 16:24 public_key.pem
-rw-r–r– 1 polkitd ssh_keys 1112 11月 15 16:24 server-cert.pem
-rw——- 1 polkitd ssh_keys 1680 11月 15 16:24 server-key.pem
drwxr-x— 2 polkitd ssh_keys 8192 11月 15 16:25 sys
drwxr-x— 2 polkitd ssh_keys 20 11月 15 16:29 test1
[root@k8s-master data-3]# cd –
/k8s/application/mysql
[root@k8s-master mysql]# kubectl apply -f mysql-server.yaml
statefulset.apps/mysql created
[root@k8s-master mysql]# kubectl exec -it mysql-0 /bin/bash
root@mysql-0:/# mysql -uroot -p\’Qfedu@123\’

mysql> show databases;
+——————–+
| Database |
+——————–+
| information_schema |
| mysql |
| performance_schema |
| sys |
| test1 |
+——————–+
5 rows in set (0.01 sec)
mysql>

25.5 k8s中无状态服务和有状态服务部署的区别?

无状态:

1. pod命名:pod名由资源名+随机的字符串组成;
2. 数据存储:多个实例pod可以共享相同的持久化数据,存储不是必要条件;
3. 扩缩容:可以随意扩缩容某个pod,不会指定某个pod进行扩缩容;
4. 启停顺序:因为pod名的序号是随机串,无启停顺序之分;
5. 无状态k8s资源:ReplicaSet、ReplicationController、Deployment、DaemonSet、Job等资源;
6. 无状态服务:tomcat、nginx等;

有状态
这里假设有N个pod;

1. pod命名:pod名由statefulset资源名+有序的数字组成(0,1,2…N-1),且pod有特定的网络标识;
2. 数据存储:有状态的服务对应实例需要有自己的独立持久卷存储;
3. 扩缩容:扩缩容不可随意,缩容是从数字最大的开始递减,扩容是在原有pod序号基础上递增1。
4. 启停顺序:pod启停是有顺序的,启动时,先启动pod序号为0的,然后依次递增至N-1;停止时,先停止pod序号为最大的N-1,然后依次递减至0;
5. 有状态k8s资源:StatefulSet资源;
6. 有状态服务:Kafka、ZooKeeper、MySql、MongoDB以及一些需要保存日志的应用等服务;

#以上关于Kubernetes 容器编排技术的相关内容来源网络仅供参考,相关信息请以官方公告为准!

原创文章,作者:CSDN,如若转载,请注明出处:https://www.sudun.com/ask/92635.html

Like (0)
CSDN的头像CSDN
Previous 2024年6月27日
Next 2024年6月27日

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注