以 K8s 为核心的云原生技术已经成为业界企业数字化转型的事实标准技术栈,但到了真正开始落地的时候,技术人员就会发现遇到一点问题能在网上查到的都是一些碎片化的知识,很难系统的解决实际应用发布和部署问题。在学习 K8s 过程中我们常会遇到几个问题:

  • 选择多
    K8s 系统是一套专注容器应用管理的集群系统。初学者会发现其实 K8s 的组件并不是特别多,为什么给人的印象就是特别难安装呢? 因为我们基础软硬件设施并不能保证装完就是最优的配置,当你作为用户在做一份新的集群部署的方案的时候,需要做很多选择题才能调优到最优解。当前很多书籍资料只是介绍当前最流行的开源解决方案,至于这个方案是否能在你的系统里面跑的更好是不承担责任的。这个给系统运维人员带来的痛苦是非常巨大的。

  • 排错难
    当前 K8s 社区提供了各种各样的 K8s 运维工具,每种工具都不是简单的几行代码就能熟悉,用户需要投入很大的精力来学习和试用。因为各种底层系统的多样性,你会遇到各种各样的问题。任何一个点出了问题,你都需要排错。而且 K8s 集群的版本是每个季度有一个大版本的更新。对于企业用户来说怎么才能在保证业务没有影响的情况下平滑更新 K8s 组件呢? 头疼的问题就是这么出来的。一旦发生不可知问题,如何排错和高效的解决问题呢?

  • 场景多
    随着应用场景的多样性,在应对突发流量的时候,K8s 编排系统是作为一种弹性伸缩的工具,快速提高进程的数量来承载流量。实际情况下很多应用集群为了管理和优化上的考虑,都是多套集群系统。多套集群在多个区域部署,给运维带来了不少麻烦。 这也是多场景下多集群治理带来的烦恼。所以,本专栏作者将秉着布道云原生技术的信心带着大家来一起探讨 K8s 落地的各项技术细节和实际的决策思路,让 K8s 的用户可以从容自如的应对落地容器集群编排技术。

专栏特色

  • 实践场景出发,按照使用者的角度来梳理技术细节
  • 通过完整的知识体系为 K8s 用户提供使用依据
  • 各章节切入点考虑普适性,让更多人能有理有据的选择适合自己的问题解决方案

专栏结构

作者在编写此系列文章的过程中,尽量避免复制网络上大量的参考资料,通过自身搭建的系统帮助大家从零搭建起生产落地的经验

  • 第一部分,K8s 组件的优化和安装步骤过程中需要注意的问题。 这部分虽然有很多书都讲得很细了,但是大部分书籍都是讲过程,不讲为什么是这样设计的。这一部分致力于为大家扫清这部分的学习障碍。

  • 第二部分,配置。 一套复杂的系统最重要的就是熟悉各种组件的配置信息。配置错了,怎么调试也不会有好结果,所以这块的各个选项的理解也是很重要的。作者将尽量通过 kubectl 来描述这部分内容。

  • 第三部分,对网络流量的架构分享。 这部分内容是很多用户特别关心的地方。因为每一种网络方案还要涉及配置,压测,选择,还是有很多需要注意的地方。

  • 第四部分,业务应用的存储选择。 网络存储方案发展至今,最出名的就是 Ceph 了。但是如果你不合时宜的直接上马 Ceph 方案,马上就会掉入运维 Ceph 的坑里面。其实容器存储的方案有很多种,你要结合你的需求,选择合适的方案。使用 K8s 来编排管理各种存储也是业务发展的必然。

  • 第五部分,K8s 集群的安全配置工作。 安装 K8s 算是一个中型大小的任务,对 K8s 的安全配置和调优更是业务重心。这方面的开源工具层出不穷,我们需要学习和掌握核心方法论,梳理出自己需要的工具链,来应对容器安全落地实践。

你将获得什么

  • 全方位理解 K8s 组件的原理技术和内部实现
  • 结合云原生开源思想,掌握分布式系统的组合过程
  • 通过实际场景问题解决案例,熟练使用架构思维模型

作者简介

avatar

肖德时,企业级容器技术实践者/布道师。Docker/Kubernetes/Openshift 等多项国际顶级开源项目社区开发者。前容器云创业公司数人云 CTO。目前对容器技术、Kubernetes 等分布式系统等领域进行技术研究和布道。

适宜人群

  • 运维开发工程师
  • 系统工程师
  • DevOps工程师
  • 想掌握容器技术的 IT 工程师
  • 各应用阶段的企业员工和技术爱好者

订阅须知

  • 本专栏为图文内容,共计 32 篇。每周更新一篇,预计 2020 年更新完毕。
  • 本专栏为虚拟产品,一经付费概不退款,敬请谅解。
  • 本专栏可在 GitChat 服务号、App 及网页端 gitbook.cn 上购买,一端购买,多端阅读。

订阅福利

  • 订购本专栏可获得专属海报(在 GitChat 服务号领取),分享专属海报每成功邀请一位好友购买,即可获得 25% 的返现奖励,多邀多得,上不封顶,立即提现。
  • 提现流程:在 GitChat 服务号中点击「我-我的邀请-提现」。
  • 订阅本专栏后,即可加入 专属交流群,服务号会自动弹出入群二维码和暗号。如果你没有收到那就先关注微信服务号「GitChat」,或者加我们的小助手「xiangcode」咨询。(入群方式可查看 第 4 篇 文末说明)。
课程内容
开篇词:为什么我们要学习 Kubernetes 技术

Kubernetes 是谷歌开源的分布式容器编排和资源管理系统。因为它的英文术语字数太长,社区专门给它定义了一个缩写单词:K8s。从 2014 年发布至今,已经成为 GitHub 社区中最炙手可热的开源项目。因为以 K8s 为核心的云原生技术已经成为业界企业数字化转型的事实标准技术栈。国内企业纷纷效仿并开始计划落地 K8s 集群作为企业应用发布的默认基础设施,但是具体怎么落实这项云原生技术其实并没有特别好实施的工具,大部分情况下我们必须结合现有企业的实际情况来落地企业应用。当然,这个说起来容易,真正开始落地的时候,技术人员就会发现遇到一点问题能在网上查到的都是一些碎片化的知识,很难系统的解决实际应用发布和部署问题。所以,笔者借着这个场景机会,秉着布道云原生技术的信心带着大家来一起探讨 K8s 落地的各项技术细节和实际的决策思路,让 K8s 的用户可以从容自如的应对落地容器集群编排技术。

在学习 K8s 技术之前,我想给大家梳理下当前社区在学习 K8s 过程中遇到的几个问题:

选择多: K8s 系统是一套专注容器应用管理的集群系统,它的组件一般按功能分别部署在主控节点(master node)和计算节点(agent node)。对于主控节点,主要包含有 etcd 集群,controller manager 组件,scheduler 组件,api-server 组件。对于计算节点,主要包含 kubelet 组件和 kubelet-proxy 组件。初学者会发现其实 K8s 的组件并不是特别多,为什么给人的印象就是特别难安装呢? 这里需要特别强调的是,即使到了 2020 年,我们基础软硬件设施并不能保证装完就是最优的配置,仍然需要系统工程师解决一些兼容性问题。所以当你把这些 K8s 系统组件安装到物理机、虚拟机中,并不能保证就是最优的部署配置。因为这个原因,当你作为用户在做一份新的集群部署的方案的时候,需要做很多选择题才能调优到最优解。

另外,企业业务系统的发布,并不止依赖于 K8s,它还需要包括网络、存储等。我们知道容器模型是基于单机设计的,当初设计的时候并没有考虑大规模的容器在跨主机的情况下通信问题。Pod 和 Pod 之间的网络只定义了接口标准,具体实现还要依赖第三方的网络解决方案。一直发展到今天,你仍然需要面对选择,选择适合的网络方案和网络存储。

这里特别强调的是,目前容器网络并没有完美方案出现,它需要结合你的现有环境和基础硬件的情况来做选择。但是,当前很多书籍资料只是介绍当前最流行的开源解决方案,至于这个方案是否能在你的系统里面跑的更好是不承担责任的。这个给系统运维人员带来的痛苦是非常巨大的。一直到现在,我遇到很多维护 K8s 系统的开发运维还是对这种选择题很头疼。是的,开源社区的方案是多头驱动并带有竞争关系的,我们不能拍脑袋去选择一个容器网络之后就不在关心它的发展的。今天的最优网络方案可能过半年就不是最优的了。同理这种问题在应对选择容器存储解决方案过程中也是一样的道理。

排错难: 当前 K8s 社区提供了各种各样的 K8s 运维工具,有 ansible 的,dind 容器化的,有 mac-desktop 桌面版本的,还有其他云原生的部署工具。每种工具都不是简单的几行代码就能熟悉,用户需要投入很大的精力来学习和试用。因为各种底层系统的多样性,你会遇到各种各样的问题,比如容器引擎 Docker 版本低,时间同步组件 ntp 没有安装,容器网络不兼容底层网络等。任何一个点出了问题,你都需要排错。加上企业的系统环境本来就很复杂,很多场景下都是没有互联网可以查资料的,对排错来说即使所有的日志都收集起来做分析也很难轻易的排错。

你可能会觉得这是公司的基础设施没有建设好,可以考虑专家看看。用户倒是想解决这个问题,但是不管是商业方案还是开源方案都只是片面的考虑到 K8s 核心组件的排错,而真正企业关心的应用容器,集群,主机,网络,存储,监控日志,持续集成发布等方面的排错实践就只能靠自己摸索,你很难系统的学习到。还有,K8s 集群的版本是每个季度有一个大版本的更新。对于企业用户来说怎么才能在保证业务没有影响的情况下平滑更新 K8s 组件呢? 头疼的问题就是这么出来的。一旦发生不可知问题,如何排错和高效的解决问题呢。这就是本系列专栏和大家探讨的问题。

场景多: 在早期的应用编排场景,主要是为了削峰填谷,高效利用浪费的主机资源。一个不公开的企业运维秘密就是生产中主机资源平均利用率不超过 20%。这不是因为运维傻,这是因为如果遇到峰值,主机系统需要能平滑应对,需要给业务容量留有余地。因为容器的引入让原来主机仅可以部署 3-4 个进程的系统,现在可以充分利用容器进程隔离的技术在主机上部署 20-40 个进程系统,并且各自还不受影响。这就是容器应用的最大好处。

社区里面有好学习的技术架构师也曾经说过,在介绍什么是容器时,就拿租房过程中,单身公寓和打隔断的群租房来对比形容,特别形象。随着应用场景的多样性,在应对突发流量的时候,K8s 编排系统就是作为一种弹性伸缩的工具,快速提高进程的数量来承载流量。在解决微服务应用编排上,除了传统的微服务部署需求之外,还有混合部署需要的 Service Mesh 技术也对 K8s 提出了流量编排的新要求。另外还有 Serverless 场景下的 FaaS 框架 Openfaas 也对 K8s 带来了新的机会和应用难点。还有很多有状态中间件服务,如数据库集群等也都在大量的迁入到 K8s 集群中,并获得了很好的实践反馈。

现在 K8s 已经成为企业数字化转型中应用发布管理的标准基础设施方案,面对这么多场景需求,我们用一套容器编排集群能把所有需求都解决吗?显然是不可以的。实际情况下很多应用集群为了管理和优化上的考虑,都是多套集群系统。多套集群在多个区域部署,给运维带来了不少麻烦。这也是多场景下多集群治理带来的烦恼。

以上这些问题仅仅是笔者这些年来遇到的客户在和我交流过程中积累下的重点问题。实际过程中,我们还会遇到很多不是 K8s 的问题,但是我们为了落地容器编排集群不得不做的折中选择。这些都是我们在学习过程中需要注意和总结的经验。

专栏特色

  • 从实践场景出发,按照使用者的角度来梳理技术细节
  • 通过完整的知识体系为 K8s 用户提供了使用依据
  • 各章节切入点考虑普适性,让更多人能有理有据的选择适合自己的问题解决方案

你将获得什么

通过本专栏的学习,你将全方位的理解 K8s 组件的原理技术,并结合云原生开源思想,学习到分布式系统的组合过程。为了解决日常场景中可能的问题,你也可以分章节获得独家的实践理解和解决思路过程,让你可以推演并学习到一些架构思维模型。并且笔者也给大家精选了 K8s 组件的详细讲解,可以让好奇的使用者,不断可以知道这些组件的原理,还能知道它们内部的实现,让你可以更准确的把握这些组件,相信你也有机会参与 K8s 的开发并写出更多的组件代替它们。

专栏结构

我在编写此系列文章的过程中,尽量避免复制网络上大量的参考资料,通过自身搭建的系统帮助大家从零搭建起生产落地的经验。因为涉及的内容太分散,容易让读者失焦。所以我重点把生产落地中遇到的几个点给大家重点讲讲。

第一部分,我就想讲讲 K8s 组件的优化和安装步骤过程中需要注意的问题。这块,有很多书都讲的很细了。但是我看到的书籍中都是讲过程,不讲为什么是这样设计的。这个在当年我学习 K8s 的过程中就非常的痛苦。这次我想帮大家扫清这块的学习障碍。

第二部分,我们需要讲配置。一套复杂的系统本身最重要的就是熟悉各种组件的配置信息。配置错了,大家怎么调试也不会有好的结果,所以这块的各个选项的理解也是很重要的。虽然这块很枯燥但是还必须掌握。我尽量通过 kubectl 来描述这块的内容。

第三部分,是对网络流量的架构分享,我想这块的内容也是大量 用户特别关心的地方。因为每一种网络方案还要涉及配置,压测,选择,还是有很多需要注意的地方。

第四部分,就是业务应用的存储选择了。网络存储方案发展至今,最出名的就是 Ceph 了。但是如果你不合时宜的直接上马 Ceph 方案,马上就会掉入运维 Ceph 的坑里面。其实容器存储的方案有很多种,你要结合你的需求,选择合适的方案。当然在企业内部,大家都需要有那种统一的方案,什么需求都可以支持的容器存储,我觉得这个就是对业务理解的偏差。更友好的选择应该是专项专用。这样肯定会带来各种各样的方案,这个也不是不能解决,使用 K8s 来编排管理各种存储也是业务发展的必然。

第五部分,我们会重点关注 K8s 集群的安全配置工作。安装 K8s 算是一个中型大小的任务,对 K8s 的安全配置和调优更是业务重心。这方面的开源工具层出不穷,我们需要学习和掌握核心方法论,梳理出自己需要的工具链,来应对容器安全落地实践。

最后,我希望大家能尽快入坑 K8s 的学习过程中,让我们一起探讨和落地安全可靠的 K8s 容器应用编排集群。

专栏作者

肖德时,企业级容器技术的布道师,Docker/Kubernetes/Openshift 等多项国际顶级开源项目社区开发者。前容器云创业公司数人云 CTO。目前对容器技术、Kubernetes 等分布式系统等领域进行技术研究和布道。

适宜人群

所有运维开发工程师、系统工程师、DevOps 工程师和想掌握容器和技术的IT工程师都可以来看一看。因为云原生技术已经在中国企业广泛应用,我们需要学习并更深入的学习这项技术,为了更准确的理解这项技术,本系列最适合各应用阶段的企业员工和技术爱好者。

重新认识 Kubernetes 的核心组件

本篇我们开始介绍 Kubernetes 的核心组件,为了方便大家提前在脑中建立起完整的 Kubernetes 架构印象,笔者整理出核心组件的介绍如下:

  • kube-apiserver,提供了 Kubernetes 各类资源对象(Pod、RC、Service 等)的增删改查及 watch 等 HTTP REST 接口,是整个系统的管理入口。
  • kube-controller-manager,作为集群内部的管理控制中心,负责集群内的 Node、Pod 副本、服务端点(Endpoint)、命名空间(Namespace)、服务账号(ServiceAccount)、资源定额(ResourceQuota)等对象管理。
  • kube-scheduler,集群调度器,提供了策略丰富,弹性拓扑能力。调度实现专注在业务可用性、性能和容量等能力上。
  • kube-proxy,提供南北向流量负载和服务发现的反向代理。
  • kubelet,是工作节点的管理 Pod 的控制程序,专门来调度启动 Pod 容器组。
  • etcd,是集群数据集中存储的分布式键值服务,用来存储 Kubernetes 集群中所有数据和状态的数据库。
  • cni-plugins,容器网络工作组维护的标准网络驱动如 fannel、ptp、host-local、portmap、tuning、vlan、sample、dhcp、ipvlan、macvlan、loopback、bridge 等网络插件供业务需求使用。这层 Overlay 网络只能包含一层,无法多层网络的互联互通。
  • runc,运行单个容器的容器运行时进程,遵循 OCI(开放容器标准)。
  • cri-o,容器运行时管理进程,类似 Docker 管理工具 containerd,国内业界普遍使用 containerd。

我们可以用如下一张架构设计图更能深刻理解和快速掌握 Kubernetes 的核心组件的布局:

1-1-k8s-arch

通过以上的介绍,核心组件的基本知识就这么多。从最近几年落地 Kubernetes 云原生技术的用户反馈来看,大家仍然觉得这套系统太复杂,不太好管理,并且随时担心系统给业务带来致命性的影响。

那么 Kubernetes 的组件是为分布式系统设计的,为什么大家还是担心它会影响业务系统的稳定性呢?从笔者接触到的用户来讲,业界并没有统一的可以直接参考的解决方案。大家在落地过程中,只能去摸石头过河,一点一点总结经验并在迭代中不断地改进实施方案。因为业务规模的不同,Kubernetes 实施的架构也完全不同,你很难让基础设施的一致性在全部的商业企业 IT 环境中保持一致性。业务传播的都是最佳实践,在 A 用户这里可以行的通,不代表在 B 用户可以实施下去。

当然,除了客观的限制因素之外,我们应用 Kubernetes 的初衷是尽量的保持企业的 IT 基础设施的一致性,并随着企业业务需求的增长而弹性扩展。毕竟 Kubernetes 是谷歌基于内部 Borg 应用管理系统成功经验的基础之上开源的容器编排系统,它的发展积累了整个业界的经验精华,所以目前企业在做数字转型的阶段,都在无脑的切换到这套新的环境中,生怕技术落后影响了业务的发展。

本篇的目的是让大家从企业的角度更深刻的理解 Kubernetes 的组件,并能用好他们,所以笔者准备从一下几个角度来分析:

  • 主控节点组件的使用策略
  • 工作节点组件的使用策略
  • 工作节点附加组件的使用策略

主控节点组件的使用策略

从刚接手维护 Kubernetes 集群的新用户角度考虑,一般第一步要做的就是遵循安装文档把集群搭建起来。世面上把集群安装的工具分为两类,

一类是学习用集群安装工具:

  • Docker Desktop for Kubernetes
  • Kind(Kubernetes IN Docker)
  • Minikube
  • MicroK8s

另一类是生产级别的安装工具:

  • kubeadm
  • kops
  • kubespray
  • Rancher Kubernetes Engine(RKE)
  • K3s

其中 kubeadm 和 RKE 采用了容器组件的形式来安装 Kubernetes 集群。虽然采用系统容器来运行集群环境对主机系统的侵入性降低了,但运维的维护成本会线性增加。维护过容器应用的用户都会知道,容器技术主要是对运行进程的隔离,它并不是为系统进程设计的隔离工具,容器的生命周期都很短,随时可以失败。当容器进程出错的时候,隔离环境很难还原故障的环境。常用的办法就是通过重启容器来忽略故障,期望能快速排除故障。

但是往往这种潜在的小问题,就是让你很烦恼并对长时间无法重现感到烦恼。那么对于系统进程,Linux 是有对应的系统维护工具 Systemd 来维护的。它的生态足够完善,并能在多种 Linux 环境中保持行为一致。当出现问题的时候,运维可以直接登录主机快速排查系统日志来定位排错。根据这样的经验积累,笔者推荐生产环境还是采用原生进程的方式来维护 Kubernetes 的组件,让运维可以集中精力在集群架构上多做冗余优化。

接下来我们重新理解 etcd 集群的架构。根据 Kubernetes 官方文档的参考资料介绍,通常按照 etcd 集群的拓扑模型可以分为两类生产级别的 Kubernetes 集群。

栈式 etcd 集群拓扑:

kubeadm-ha-topology-stacked-etcd

独立式 etcd 集群拓扑:

kubeadm-ha-topology-external-etcd

参考上面的架构图,我们可以看到 etcd 集群的部署方式影响着 Kubernetes 集群的规模。在落地实践中因为采购的机器都是高性能大内存的刀片服务器,业务部门的期望是能充分的把这些资源利用上,并不期望用这些机器来跑集群控制管理组件。

当遇到这种情况,很多部署方案会采用第一种方案,是把主机节点、工作节点和 etcd 集群都放在一起复用资源。从高可用架构来讲,高度的应用密度集合并不能给用户带来无限制的好处。试想当出现节点宕机后这种架构的隐患是业务应用会受到极大的影响。所以通常的高可用架构经验是工作节点一定要和主控节点分开部署。在虚拟化混合环境下,主控节点可以使用小型的虚拟机来部署最为合适。当你的基础设施完全采用物理机的时候,直接使用物理机来部署主控节点是很浪费的,建议在现有物理机集群中先使用虚拟化软件虚拟化一批中小型虚拟机来提供管理节点的管理资源。开源的管理系统有 OpenStack,商业的方案是 VMware vSphere,按需求丰俭由人即可。

除了以上标准的部署解决方案,社区还提供了单机模式部署的 K3s 集群模式。把核心组件直接绑定为一个单体二进制文件,这样的好处就是这个系统进程只有一个,非常容易管理和恢复集群。在纯物理机环境,使用这种单点集群架构来部署应用,我们可以通过冗余部署多套集群的方式来支持应用的高可用和容灾。下图就是 K3s 的具体架构图:

k3s-arch

K3s 本来是为嵌入式环境提供的精简 Kubernetes 集群,但是这个不妨碍我们在生产实践中灵活运用。K3s 提供的是原生 Kubernetes 的所有稳定版本 API 接口,在 x86 集群下可以发挥同样的编排容器业务的能力。

工作节点组件的使用策略

在工作节点上默认安装的组件是 kubelet 和 kube-proxy。在实际部署的过程中,kubelet 是有很多配置项需要调优的,这些参数会根据业务需求来调整,并不具备完全一样的配置方案。让我们再次认识一下 kubelet 组件,对于 kubelet,它是用来启动 Pod 的控制管理进程。虽然 kubelet 总体启动容器的工作流程,但是具体的操作它是依赖主机层面的容器引擎来管理的。对于依赖的容器引擎,我们可以选择的组件有 containerd、ori-o 等。Kubernetes 默认配置的组件是 cri-o。但是业界实际落地部署最多的还是 containerd,因为它的部署量巨大,很多潜在的问题都会被第一时间解决。containerd 是从 docker 引擎抽离出来的容器管理工具,用户具备长期的使用经验,这些经验对于运维和管理容器会带来很多潜在的使用信心。

containerd-plugin

对于容器实例的维护,我们常用的命令行工具是 Docker,在切换到 containerd 之后,命令行工具就切换为 ctr 和 crictl。很多时候用户无法搞清楚这两个工具的用处,并和 Docker 混为一谈。

Docker 可以理解为单机上运行容器的最全面的开发管理工具,这个不用多介绍,大家都了解。ctr 是 containerd 的客户端级别的命令行工具,主要的能力是管理运行中的容器。crictl 这个工具是管理 CRI 运行时环境的,在上图中是操作 cri-containerd 的组件。它的功能主要聚焦在 Pod 层面的镜像加载和运行。

还请大家注意下 Docker、ctr、crictl 三者细节实现上的差别。举个例子,Docker 和 ctr 确实都是管理主机层面的镜像和容器的,但是他们都有自己独立的管理目录,所以你即使是同样的加载镜像的操作,在主机存储的文件位置也是不同的,他们的镜像层无法复用。而 crictl 是操作 Pod 的,它并不是直接操作镜像的进程,一般把命令发送给对应的镜像管理程序,比如 containerd 进程。

另外一个组件是 kube-proxy,它是面向 Service 对象概念的南北向反向代理服务。通过对接 Endpoint 对象,可以按照均衡策略来负载流量。另外为了实现集群全局的服务发现机制,每一个服务都会定义全局唯一的名字,也就是 Service 的名字。这个名字可以通过附加的组件 coredns 来实现集群内的名字解析,也就是服务发现。对于流量的负载,Kubernetes 是通过 iptables 或 IPVS(IP Virtual Server)来实现。

在正常的集群规模下,Service 并不会超过 500 个,但是华为容器技术团队做了一个极限压测,发现了 iptables 在实现反向代理的时候出现的性能瓶颈。试验验证了,当 Service 增加到足够大的时候,Service 规则增加对于 iptables 是 O(n) 的复杂度,而切换到 IPVS 却是 O(1)。压测结果如下:

ipvs-iptables-perf

目前 Kubernetes 默认反向代理激活模块为 IPVS 模式,iptables 和 IPVS 都是基于 Linux 的子模块 netfilter,他们的相同点就是做反向代理,但是还是有以下 3 点区别需要知道:

  • IPVS 提供大规模集群扩展性和高性能
  • IPVS 提供更丰富的负载均衡算法(最小负载、最小链接数、基于局部性调度、加权轮叫等等)
  • IPVS 支持服务器健康检查和网络重试机制

当 IPVS 在处理流量的 packet filtering、SNAT 和 masquerade 需求时,仍然需要使用 iptables 的扩展包工具 ipset 配置固定数量的转换规则,不会像 iptables 模式下随着服务和 Pod 的增加而线性写入规则导致系统的计算 CPU 负载加大,影响集群的处理能力。

以下表单是 IPVS 模式需要使用的 ipset 规则:

set namemembersusage
KUBE-CLUSTER-IPAll service IP + portMark-Masq for cases that masquerade-all=true or clusterCIDR specified
KUBE-LOOP-BACKAll service IP + port + IPmasquerade for solving hairpin purpose
KUBE-EXTERNAL-IPservice external IP + portmasquerade for packages to external IPs
KUBE-LOAD-BALANCERload balancer ingress IP + portmasquerade for packages to load balancer type service
KUBE-LOAD-BALANCER-LOCALLB ingress IP + port with externalTrafficPolicy=localaccept packages to load balancer with externalTrafficPolicy=local
KUBE-LOAD-BALANCER-FWload balancer ingress IP + port with loadBalancerSourceRangespackage filter for load balancer with loadBalancerSourceRanges specified
KUBE-LOAD-BALANCER-SOURCE-CIDRload balancer ingress IP + port + source CIDRpackage filter for load balancer with loadBalancerSourceRanges specified
KUBE-NODE-PORT-TCPnodeport type service TCP portmasquerade for packets to nodePort(TCP)
KUBE-NODE-PORT-LOCAL-TCPnodeport type service TCP port with externalTrafficPolicy=localaccept packages to nodeport service with externalTrafficPolicy=local
KUBE-NODE-PORT-UDPnodeport type service UDP portmasquerade for packets to nodePort(UDP)
KUBE-NODE-PORT-LOCAL-UDPnodeport type service UDP port with externalTrafficPolicy=localaccept packages to nodeport service with externalTrafficPolicy=local

另外,IPVS 模式会在以下场景下降级使用 iptables 模式:

  • kube-proxy 启动的同时启用 --masquerade-all=true
  • kube-proxy 启动时指定 Pod 层网段
  • Load Balancer 类型的 Service
  • NodePort 类型的 Service
  • 配置了 externalIP 的 Service

工作节点附加组件的使用策略

提到附加的组件,一般常识是这些组件可有可无,锦上添花的能力而已。但是在 Kubernetes 集群中,这些附加组件是不得不安装的,不然整个集群就是一套鸡肋的展览品。Kubernetes 官方把这些附加组件分为以下五类:

  • 网络和网络策略
  • 服务发现
  • 可视化管理
  • 基础设施
  • 遗留组件

大家看到标题,基本上就能理解这些组件的用处。我这里还是从实用的角度和大家一起重新认识一下这些组件,为之后的使用提供经验参考。

1. 网络和网络策略

对于网络,我们主要指容器网络。注意在 Kubernetes 集群里面,是有两层虚拟网络的。一说虚拟网络,就会有丢包率,这个是以往虚拟化环境不可想象的问题。为了提高或者说规避这方面的棘手问题,我们会放弃所有官方的方案,采用传统的网络方案来支持。当然传统的网络方案大都不是为 Kubernetes 网络设计的,需要做很多自定义适配工作来完善体验。在不理想的传统方案之外,容器网络方案中最流行的有 Calico、Cilium、Flannel、Contiv 等等。采用这些方案之后,随着业务流量的增加一定会带来网络丢包的情况。网络丢包带来的问题是业务处理能力的降低,为了恢复业务实例的处理能力,我们常规的操作是水平扩展容器实例数。注意,正是因为实例数的增加反而会提高业务处理能力,让运维人员忽略容器网络带来的性能损耗。另外,Kubernetes 在业务实践中还参考了主流网络管理的需求设计,引入了 Network Policies。这些策略定义了 Pod 之间的连通关系,方便对业务容器组的安全网络隔离。当然笔者在实践中发现,这些策略完全依赖容器网络的实现能力,依赖性强,只能作为试验品体验,但是在实际业务中,还没有看到实际的能力优势。

2. 服务发现

目前提供的能力就是给 Pod 提供 DNS 服务,并引入了域名的定义规则。官方认可的只有 CoreDNS。注意 ,这个服务发现只能在集群内部使用。不推荐直接暴露给外部服务,集群对外暴露的服务仍然是 IP 和端口。外部 DNS 可以灵活的指定这个固定 IP 来让业务在全局服务发现。

3. 可视化管理

官方提供了 Dashboard,这是官方提供的标准管理集群的 web 界面,很多开发集成测试环境,使用它就可以满足业务管理的需求。这个可选安装。

4. 基础设施

官方提供了 KubeVirt,是可以让 Kubernetes 运行虚拟机的附加组件,默认运行在裸机群集上。从目前的实践经验来看,这种能力还属于试验性的能力,一般很少人使用。

5. 遗留组件

对于很多老版本的 Kubernetes,有很多历史遗留的组件可以选用,所以官方把这些可选的组件都保留了下来,帮助用户在迁移集群版本的过程中可以继续发挥老集群的能力。一般很少人使用。

通过三个纬度的介绍,我相信大家对 Kubernetes 的核心组件有了更深入的理解。在生产实践中,为了标准化运维模型,我们对 Kubernetes 的组件可以按照业务需求定义一个基线模型,有选择的使用这些组件,相信一定可以规避很多兼容性的问题。在笔者遇到的大部分的 Kubernetes 集群故障案例中,大部分就是对组件的错用或者误用,让问题变的更复杂,更难以复现。

当然,云端的 Kubernetes 可以彻底解决基线的问题,我相信未来用户会越来越容易的使用到靠谱的 Kubernetes 集群环境。只是你一定要记住,我们只是把运维 Kubernetes 的难题交给专业的云服务开发者而已。

深入理解 Kubernets 的编排对象

Kubernetes 系统是一套分布式容器应用编排系统,当我们用它来承载业务负载时主要使用的编排对象有 Deployment、ReplicaSet、StatefulSet、DaemonSet 等。读者可能好奇的地方是 Kubernetes 管理的对象不是 Pod 吗?为什么不去讲讲如何灵活配置 Pod 参数。其实这些对象都是对 Pod 对象的扩展封装。并且这些对象作为核心工作负载 API 固化在 Kubernetes 系统中了。所以 ,我们有必要认真的回顾和理解这些编排对象,依据生产实践的场景需要,合理的配置这些编排对象,让 Kubernetes 系统能更好的支持我们的业务需要。本文会从实际应用发布的场景入手,分析和梳理具体场景中需要考虑的编排因素,并整理出一套可以灵活使用的编排对象使用实践。

常规业务容器部署策略

策略一:强制运行不少于 2 个容器实例副本

在应对常规业务容器的场景之下,Kubernetes 提供了 Deployment 标准编排对象,从命令上我们就可以理解它的作用就是用来部署容器应用的。Deployment 管理的是业务容器 Pod,因为容器技术具备虚拟机的大部分特性,往往让用户误解认为容器就是新一代的虚拟机。从普通用户的印象来看,虚拟机给用户的映象是稳定可靠。如果用户想当然地把业务容器 Pod 也归类为稳定可靠的实例,那就是完全错误的理解了。容器组 Pod 更多的时候是被设计为短生命周期的实例,它无法像虚拟机那样持久地保存进程状态。因为容器组 Pod 实例的脆弱性,每次发布的实例数一定是多副本,默认最少是 2 个。

部署多副本示例:

apiVersion: apps/v1kind: Deploymentmetadata:  name: nginx-deployment  labels:    app: nginxspec:  replicas: 2  selector:    matchLabels:      app: nginx  template:    metadata:      labels:        app: nginx    spec:      containers:      - name: nginx        image: nginx:1.7.9        ports:        - containerPort: 80
策略二:采用节点亲和,Pod 间亲和/反亲和确保 Pod 实现高可用运行

当运维发布多个副本实例的业务容器的时候,一定需要仔细注意到一个事实。Kubernetes 的调度默认策略是选取最空闲的资源主机来部署容器应用,不考虑业务高可用的实际情况。当你的集群中部署的业务越多,你的业务风险会越大。一旦你的业务容器所在的主机出现宕机之后,带来的容器重启动风暴也会即可到来。为了实现业务容错和高可用的场景,我们需要考虑通过 Node 的亲和性和 Pod 的反亲和性来达到合理的部署。这里需要注意的地方是,Kubernetes 的调度系统接口是开放式的,你可以实现自己的业务调度策略来替换默认的调度策略。我们这里的策略是尽量采用 Kubernetes 原生能力来实现。

为了更好地理解高可用的重要性,我们深入探讨一些实际的业务场景。

首先,Kubernetes 并不是谷歌内部使用的 Borg 系统,大部分中小企业使用的 Kubernetes 部署方案都是人工扩展的私有资源池。当你发布容器到集群中,集群不会因为资源不够能自动扩展主机并自动负载部署容器 Pod。即使是在公有云上的 Kubernetes 服务,只有当你选择 Serverlesss Kubernetes 类型时才能实现资源的弹性伸缩。很多传统企业在落地 Kubernetes 技术时比较关心的弹性伸缩能力,目前只能折中满足于在有限静态资源的限制内动态启停容器组 Pod,实现类似的业务容器的弹性。用一个不太恰当的比喻就是房屋中介中,从独立公寓变成了格子间公寓,空间并没有实质性扩大。在实际有限资源的情况下,Kubernetes 提供了打标签的功能,你可以给主机、容器组 Pod 打上各种标签,这些标签的灵活运用,可以帮你快速实现业务的高可用运行。

其次,实践中你会发现,为了高效有效的控制业务容器,你是需要资源主机的。你不能任由 Kubernetes 调度来分配主机启动容器,这个在早期资源充裕的情况下看不到问题。当你的业务复杂之后,你会部署更多的容器到资源池中,这个时间你的业务运行的潜在危机就会出现。因为你没有管理调度资源,导致很多关键业务是运行在同一台服务器上,当主机宕机发生时,让你很难处理这种灾难。所以在实际的业务场景中,业务之间的关系需要梳理清楚,设计单元化主机资源模块,比如 2 台主机为一个单元,部署固定的业务容器组 Pod,并且让容器组 Pod 能足够分散的运行在这两台主机之上,当任何一台主机宕机也不会影响到主体业务,实现真正的高可用。

主机亲和性示例

pods/pod-with-node-affinity.yaml apiVersion: v1kind: Podmetadata:  name: with-node-affinityspec:  affinity:    nodeAffinity:      requiredDuringSchedulingIgnoredDuringExecution:        nodeSelectorTerms:        - matchExpressions:          - key: kubernetes.io/e2e-az-name            operator: In            values:            - e2e-az1            - e2e-az2      preferredDuringSchedulingIgnoredDuringExecution:      - weight: 1        preference:          matchExpressions:          - key: another-node-label-key            operator: In            values:            - another-node-label-value  containers:  - name: with-node-affinity    image: gcr.azk8s.cn/google-samples/node-hello:1.0

目前有两种类型的节点亲和,分别为 requiredDuringSchedulingIgnoredDuringExecution 和 preferredDuringSchedulingIgnoredDuringExecution。你可以视它们为“硬”和“软”,意思是,前者指定了将 pod 调度到一个节点上必须满足的规则,后者指定调度器将尝试执行但不能保证的偏好。

Pod 间亲和与反亲和示例

使用反亲和性避免单点故障例子:

     affinity:        podAntiAffinity:          requiredDuringSchedulingIgnoredDuringExecution:            - labelSelector:                matchExpressions:                  - key: "app"                    operator: In                    values:                    - zk              topologyKey: "kubernetes.io/hostname"

意思是以主机的 hostname 命名空间来调度,运行打了标签键 "app" 并含有 "zk" 值的 Pod 在不同节点上部署。

策略三:使用 preStop Hook 和 readinessProbe 保证服务平滑更新不中断

我们部署应用之后,接下来会做的工作就是服务更新的操作。如何保证容器更新的时候,业务不中断是最重要的关心事项。参考示例:

apiVersion: apps/v1kind: Deploymentmetadata:  name: nginxspec:  replicas: 1  selector:    matchLabels:      component: nginx  template:    metadata:      labels:        component: nginx    spec:      containers:      - name: nginx        image: xds2000/nginx-hostname        ports:        - name: http          hostPort: 80          containerPort: 80          protocol: TCP        readinessProbe:          httpGet:            path: /            port: 80            httpHeaders:            - name: X-Custom-Header              value: Awesome          initialDelaySeconds: 15          timeoutSeconds: 1        lifecycle:          preStop:            exec:              command: ["/bin/bash", "-c", "sleep 30"]

给 Pod 里的 container 加 readinessProbe(就绪检查),通常是容器完全启动后监听一个 HTTP 端口,kubelet 发送就绪检查探测包,正常响应说明容器已经就绪,然后修改容器状态为 Ready,当 Pod 中所有容器都 Ready 之后这个 Pod 才会被 Endpoint Controller 加进 Service 对应 Endpoint IP:Port 列表,然后 kube-proxy 再更新节点转发规则,更新完了即便立即有请求被转发到新的 Pod 也能保证能够正常处理连接,避免了连接异常。

给 Pod 里的 container 加 preStop hook,让 Pod 真正销毁前先 sleep 等待一段时间,留点时间给 Endpoint controller 和 kube-proxy 更新 Endpoint 和转发规则,这段时间 Pod 处于 Terminating 状态,即便在转发规则更新完全之前有请求被转发到这个 Terminating 的 Pod,依然可以被正常处理,因为它还在 sleep,没有被真正销毁。

策略四:通过泛域名转发南北向流量范式

常规集群对外暴露一个公网 IP 作为流量入口(可以是 Ingress 或 Service),再通过 DNS 解析配置一个泛域名指向该 IP(比如 *.test.foo.io),现希望根据请求中不同 Host 转发到不同的后端 Service。比如 a.test.foo.io 的请求被转发到 my-svc-a,b.test.foo.io 的请求转发到 my-svc-b。当前 Kubernetes 的 Ingress 并不原生支持这种泛域名转发规则,我们需要借助 Nginx 的 Lua 编程能力解决实现泛域名转发。

Nginx proxy 示例(proxy.yaml):

apiVersion: apps/v1kind: Deploymentmetadata:  labels:    component: nginx  name: proxyspec:  replicas: 1  selector:    matchLabels:      component: nginx  template:    metadata:      labels:        component: nginx    spec:      containers:      - name: nginx        image: "openresty/openresty:centos"        ports:        - name: http          containerPort: 80          protocol: TCP        volumeMounts:        - mountPath: /usr/local/openresty/nginx/conf/nginx.conf          name: config          subPath: nginx.conf      - name: dnsmasq        image: "janeczku/go-dnsmasq:release-1.0.7"        args:          - --listen          - "127.0.0.1:53"          - --default-resolver          - --append-search-domains          - --hostsfile=/etc/hosts          - --verbose      volumes:      - name: config        configMap:          name: configmap-nginx---apiVersion: v1kind: ConfigMapmetadata:  labels:    component: nginx  name: configmap-nginxdata:  nginx.conf: |-    worker_processes  1;    error_log  /error.log;    events {        accept_mutex on;        multi_accept on;        use epoll;        worker_connections  1024;    }    http {        include       mime.types;        default_type  application/octet-stream;        log_format  main  '$time_local $remote_user $remote_addr $host $request_uri $request_method $http_cookie '                          '$status $body_bytes_sent "$http_referer" '                          '"$http_user_agent" "$http_x_forwarded_for" '                          '$request_time $upstream_response_time "$upstream_cache_status"';        log_format  browser '$time_iso8601 $cookie_km_uid $remote_addr $host $request_uri $request_method '                          '$status $body_bytes_sent "$http_referer" '                          '"$http_user_agent" "$http_x_forwarded_for" '                          '$request_time $upstream_response_time "$upstream_cache_status" $http_x_requested_with $http_x_real_ip $upstream_addr $request_body';        log_format client '{"@timestamp":"$time_iso8601",'                          '"time_local":"$time_local",'                          '"remote_user":"$remote_user",'                          '"http_x_forwarded_for":"$http_x_forwarded_for",'                          '"host":"$server_addr",'                          '"remote_addr":"$remote_addr",'                          '"http_x_real_ip":"$http_x_real_ip",'                          '"body_bytes_sent":$body_bytes_sent,'                          '"request_time":$request_time,'                          '"status":$status,'                          '"upstream_response_time":"$upstream_response_time",'                          '"upstream_response_status":"$upstream_status",'                          '"request":"$request",'                          '"http_referer":"$http_referer",'                          '"http_user_agent":"$http_user_agent"}';        access_log  /access.log  main;        sendfile        on;        keepalive_timeout 120s 100s;        keepalive_requests 500;        send_timeout 60000s;        client_header_buffer_size 4k;        proxy_ignore_client_abort on;        proxy_buffers 16 32k;        proxy_buffer_size 64k;        proxy_busy_buffers_size 64k;        proxy_send_timeout 60000;        proxy_read_timeout 60000;        proxy_connect_timeout 60000;        proxy_cache_valid 200 304 2h;        proxy_cache_valid 500 404 2s;        proxy_cache_key $host$request_uri$cookie_user;        proxy_cache_methods GET HEAD POST;        proxy_redirect off;        proxy_http_version 1.1;        proxy_set_header Host                $http_host;        proxy_set_header X-Real-IP           $remote_addr;        proxy_set_header X-Forwarded-For     $proxy_add_x_forwarded_for;        proxy_set_header X-Forwarded-Proto   $scheme;        proxy_set_header X-Frame-Options     SAMEORIGIN;        server_tokens off;        client_max_body_size 50G;        add_header X-Cache $upstream_cache_status;        autoindex off;        resolver      127.0.0.1:53 ipv6=off;        server {            listen 80;            location / {                set $service  '';                rewrite_by_lua '                    local host = ngx.var.host                    local m = ngx.re.match(host, "(.+).test.foo.io")                    if m then                        ngx.var.service = "my-svc-" .. m[1]                    end                ';                proxy_pass http://$service;            }        }    }

用 Service 的示例(service.yaml):

apiVersion: v1kind: Servicemetadata:  labels:    component: nginx  name: service-nginxspec:  type: LoadBalancer  ports:  - name: http    port: 80    targetPort: http  selector:    component: nginx

用 Ingress 的示例(ingress.yaml):

apiVersion: extensions/v1beta1kind: Ingressmetadata:  name: ingress-nginxspec:  rules:  - host: "*.test.foo.io"    http:      paths:      - backend:          serviceName: service-nginx          servicePort: 80        path: /

有状态业务容器部署策略

StatefulSet 旨在与有状态的应用及分布式系统一起使用。为了理解 StatefulSet 的基本特性,我们使用 StatefulSet 部署一个简单的 Web 应用。

创建一个 StatefulSet 示例(web.yaml):

apiVersion: v1kind: Servicemetadata:  name: nginx  labels:    app: nginxspec:  ports:  - port: 80    name: web  clusterIP: None  selector:    app: nginx---apiVersion: apps/v1kind: StatefulSetmetadata:  name: webspec:  serviceName: "nginx"  replicas: 2  selector:    matchLabels:      app: nginx  template:    metadata:      labels:        app: nginx    spec:      containers:      - name: nginx        image: gcr.azk8s.cn/google_containers/nginx-slim:0.8        ports:        - containerPort: 80          name: web        volumeMounts:        - name: www          mountPath: /usr/share/nginx/html  volumeClaimTemplates:  - metadata:      name: www    spec:      accessModes: [ "ReadWriteOnce" ]      resources:        requests:          storage: 1Gi

注意 StatefulSet 对象运行的特点之一就是,StatefulSet 中的 Pod 拥有一个唯一的顺序索引和稳定的网络身份标识。这个输出最终将看起来像如下样子:

kubectl get pods -l app=nginxNAME      READY     STATUS    RESTARTS   AGEweb-0     1/1       Running   0          1mweb-1     1/1       Running   0          1m

很多文档在提及 Statefulset 对象的概念时,用户容易望文生义,常常把挂盘的容器实例当成有状态实例。这是不准确的解释。在 Kubernetes 的世界里,有稳定的网络身份标识的容器组才是有状态的应用。例如:

for i in 0 1; do kubectl exec web-$i -- sh -c 'hostname'; doneweb-0web-1

另外,我们使用 kubectl run 运行一个提供 nslookup 命令的容器。通过对 Pod 的主机名执行 nslookup,你可以检查他们在集群内部的 DNS 地址。示例如下:

kubectl run -i --tty --image busybox:1.28 dns-test --restart=Never --rm   nslookup web-0.nginxServer:    10.0.0.10Address 1: 10.0.0.10 kube-dns.kube-system.svc.cluster.localName:      web-0.nginxAddress 1: 10.244.1.6nslookup web-1.nginxServer:    10.0.0.10Address 1: 10.0.0.10 kube-dns.kube-system.svc.cluster.localName:      web-1.nginxAddress 1: 10.244.2.6
策略五:灵活运用 StatefulSet 的 Pod 管理策略

通常对于常规分布式微服务业务系统来说,StatefulSet 的顺序性保证是不必要的。这些系统仅仅要求唯一性和身份标志。为了加快这个部署策略,我们通过引入 .spec.podManagementPolicy 解决。

Parallel pod 管理策略告诉 StatefulSet 控制器并行的终止所有 Pod,在启动或终止另一个 Pod 前,不必等待这些 Pod 变成 Running 和 Ready 或者完全终止状态。示例如下:

apiVersion: v1kind: Servicemetadata:  name: nginx  labels:    app: nginxspec:  ports:  - port: 80    name: web  clusterIP: None  selector:    app: nginx---apiVersion: apps/v1kind: StatefulSetmetadata:  name: webspec:  serviceName: "nginx"  podManagementPolicy: "Parallel"  replicas: 2  selector:    matchLabels:      app: nginx  template:    metadata:      labels:        app: nginx    spec:      containers:      - name: nginx        image: gcr.azk8s.cn/google_containers/nginx-slim:0.8        ports:        - containerPort: 80          name: web        volumeMounts:        - name: www          mountPath: /usr/share/nginx/html  volumeClaimTemplates:  - metadata:      name: www    spec:      accessModes: [ "ReadWriteOnce" ]      resources:        requests:          storage: 1Gi

业务运维类容器部署策略

在我们部署 Kubernetes 扩展 DNS、Ingress、Calico 能力时需要在每个工作节点部署守护进程的程序,这个时候需要采用 DaemonSet 来部署系统业务容器。默认 DaemonSet 采用滚动更新策略来更新容器,可以通过执行如下命令确认:

kubectl get ds/<daemonset-name> -o go-template='{{.spec.updateStrategy.type}}{{"\n"}}'
RollingUpdate

在日常工作中,我们对守护进程只需要执行更换镜像的操作:

kubectl set image ds/<daemonset-name> <container-name>=<container-new-image>

查看滚动更新状态确认当前进度:

kubectl rollout status ds/<daemonset-name>

当滚动更新完成时,输出结果如下:

daemonset "<daemonset-name>" successfully rolled out

此外,我们还有一些定期执行脚本任务的需求,这些需求可以通过 Kubernetes 提供的 CronJob 对象来管理,示例如下:

apiVersion: batch/v1beta1kind: CronJobmetadata:  name: hellospec:  schedule: "*/1 * * * *"  successfulJobsHistoryLimit: 0  failedJobsHistoryLimit: 0  jobTemplate:    spec:      template:        spec:          containers:          - name: hello            image: busybox            args:            - /bin/sh            - -c            - date; echo Hello from the Kubernetes cluster          restartPolicy: OnFailure

总结

本文从实际业务需求出发,带着读者一起梳理了 Kubernetes 的工作负载定义的稳定版本的编排对象 Deployment、StatefulSet、DaemonSet、CronJob。所有提供的资料都是来自行业分享的实践经验的总结,去掉了很多文档的繁琐或者不正确的介绍,帮助读者正确建立合理的编排对象的使用策略。当然,除了这些核心编排对象之外,Kubernetes 还提供了扩展接口,我们通过 Operator 编程框架就可以自定义需要的编排对象,把自己的运维经验用代码规范起来,让你的持续发布的流程更加方便快捷。

DevOps 场景下落地 K8s 的困难分析
微服务应用场景下落地 K8s 的困难分析
解决 K8s 落地难题的方法论提炼
练习篇:K8s 核心实践知识掌握
容器引擎 containerd 落地实践
K8s 集群安装工具 kubeadm 的落地实践
南北向流量组件 IPVS 的落地实践
东西向流量组件 Calico 的落地实践
服务发现 DNS 的落地实践
练习篇:K8s 集群配置测验
理解对方暴露服务的对象 Ingress 和 Service
应用网关 Openresty 对接 K8s 实践
Service 层 iptable 和 IPVS 的选择实践
容器网络的落地实践
应用流量的优雅无损切换实践
练习篇:应用流量无损切换技术测验
使用 Rook 构建生产可用存储环境实践
有状态应用的默认特性落地分析
案例:分布式 MySQL 集群工具 Vitess 实践分析
存储对象 PV、PVC、Storage Classes 的管理落地实践
K8s 集群中存储对象灾备的落地实践
练习篇:有状态应用的部署技术测验
安全审计扫描工具的活学活用
Pod 中 securityContext 配置分析
RBAC 管理实践
镜像仓库的安全配置
混沌工程技术在 K8s 生产安全中的应用
练习篇:K8s 安全生产技术测验
结语:日积硅步终将活用 K8s 编排集群技术

阅读全文: http://gitbook.cn/gitchat/column/5f27b043f84af80b7669f79b

Logo

开源、云原生的融合云平台

更多推荐