K8S从懵圈到熟练:认证与调度

本文涉及的产品
容器服务 Serverless 版 ACK Serverless,317元额度 多规格
容器服务 Serverless 版 ACK Serverless,952元额度 多规格
容器镜像服务 ACR,镜像仓库100个 不限时长
简介: 不知道大家有没有意识到一个现实,就是大部分时候,我们已经不像以前一样,通过命令行,或者可视窗口来使用一个系统了。现在我们上微博、或者网购,操作的其实不是眼前这台设备,而是一个又一个集群。 通常,这样的集群拥有成百上千个节点,每个节点是一台物理机或虚拟机。

不知道大家有没有意识到一个现实,就是大部分时候,我们已经不像以前一样,通过命令行,或者可视窗口来使用一个系统了。现在我们上微博、或者网购,操作的其实不是眼前这台设备,而是一个又一个集群。

通常,这样的集群拥有成百上千个节点,每个节点是一台物理机或虚拟机。集群一般远离用户,坐落在数据中心。为了让这些节点互相协作,对外提供一致且高效的服务,集群需要操作系统。Kubernetes就是这样的操作系统。

比较Kubernetes和单机操作系统,Kubernetes相当于内核,它负责集群软硬件资源管理,并对外提供统一的入口,用户可以通过这个入口来使用集群,和集群沟通。

而运行在集群之上的程序,与普通程序有很大的不同。这样的程序,是“关在笼子里”的程序。它们从被制作,到被部署,再到被使用,都不寻常。我们只有深挖根源,才能理解其本质。

“关在笼子里”的程序

代码

我们使用go语言写了一个简单的web服务器程序app.go,这个程序监听在2580这个端口。通过http协议访问这个服务的根路径,服务会返回“This is a small app for kubernetes...”字符串。


package main

import (

        "github.com/gorilla/mux"

        "log"

        "net/http"

)

func about(w http.ResponseWriter, r *http.Request) {

        w.Write([]byte("This is a small app for kubernetes...\n"))

}

func main() {

        r := mux.NewRouter()

        r.HandleFunc("/", about)

        log.Fatal(http.ListenAndServe("0.0.0.0:2580", r))

}


使用go build命令编译这个程序,产生app可执行文件。这是一个普通的可执行文件,它在操作系统里运行,会依赖系统里的库文件。


# ldd app

linux-vdso.so.1 => (0x00007ffd1f7a3000)

libpthread.so.0 => /lib64/libpthread.so.0 (0x00007f554fd4a000)

libc.so.6 => /lib64/libc.so.6 (0x00007f554f97d000)

/lib64/ld-linux-x86-64.so.2 (0x00007f554ff66000)


“笼子”

为了让这个程序不依赖于操作系统自身的库文件,我们需要制作容器镜像,即隔离的运行环境。Dockerfile是制作容器镜像的“菜谱”。我们的菜谱就只有两个步骤,下载一个centos的基础镜像,把app这个可执行文件放到镜像中/usr/local/bin目录中去。


FROM centos

ADD app /usr/local/bin

地址

制作好的镜像存再本地,我们需要把这个镜像上传到镜像仓库里去。这里的镜像仓库,相当于应用商店。我们使用阿里云的镜像仓库,上传之后镜像地址是:


registry.cn-hangzhou.aliyuncs.com/kube-easy/app:latest

镜像地址可以拆分成四个部分:仓库地址/命名空间/镜像名称:镜像版本。显然,镜像上边的镜像,在阿里云杭州镜像仓库,使用的命名空间是kube-easy,镜像名:版本是app:latest。至此,我们有了一个可以在Kubernetes集群上运行的,“关在笼子里”的小程序。

得其门而入

入口

Kubernetes作为操作系统,和普通的操作系统一样,有API的概念。有了API,集群就有了入口;有了API,我们使用集群,才能得其门而入。Kubernetes的API被实现为运行在集群节点上的组件API Server。这个组件是典型的web服务器程序,通过对外暴露http(s)接口来提供服务。

这里我们创建一个阿里云Kubernetes集群。登录集群管理页面,我们可以看到API Server的公网入口。


API Server 内网连接端点: https://xx.xxx.xxx.xxx:6443


双向数字证书验证

阿里云Kubernetes集群API Server组件,使用基于CA签名的双向数字证书认证来保证客户端与api server之间的安全通信。这句话很绕口,对于初学者不太好理解,我们来深入解释一下。

从概念上来讲,数字证书是用来验证网络通信参与者的一个文件。这和学校颁发给学生的毕业证书类似。在学校和学生之间,学校是可信第三方CA,而学生是通信参与者。如果社会普遍信任一个学校的声誉的话,那么这个学校颁发的毕业证书,也会得到社会认可。参与者证书和CA证书可以类比毕业证和学校的办学许可证。

这里我们有两类参与者,CA和普通参与者;与此对应,我们有两种证书,CA证书和参与者证书;另外我们还有两种关系,证书签发关系,以及信任关系。这两种关系至关重要。

我们先看签发关系。如下图,我们有两张CA证书,三个参与者证书。其中最上边的CA证书,签发了两张证书,一张是中间的CA证书,另一张是右边的参与者证书;中间的CA证书,签发了下边两张参与者证书。这六张证书以签发关系为联系,形成了树状的证书签发关系图。

然而,证书以及签发关系本身,并不能保证可信的通信可以在参与者之间进行。以上图为例,假设最右边的参与者是一个网站,最左边的参与者是一个浏览器,浏览器相信网站的数据,不是因为网站有证书,也不是因为网站的证书是CA签发的,而是因为浏览器相信最上边的CA,也就是信任关系。

理解了CA(证书),参与者(证书),签发关系,以及信任关系之后,我们回过头来看“基于CA签名的双向数字证书认证”。客户端和API Server作为通信的普通参与者,各有一张证书。而这两张证书,都是由CA签发,我们简单称它们为集群CA和客户端CA。客户端信任集群CA,所以它信任拥有集群CA签发证书的API Server;反过来API Server需要信任客户端CA,它才愿意与客户端通信。

阿里云Kubernetes集群,集群CA证书,和客户端CA证书,实现上其实是一张证书,所以我们有这样的关系图。

KubeConfig文件

登录集群管理控制台,我们可以拿到KubeConfig文件。这个文件包括了客户端证书,集群CA证书,以及其他。证书使用base64编码,所以我们可以使用base64工具解码证书,并使用openssl查看证书文本。

  • 首先,客户端证书的签发者CN是集群id c0256a3b8e4b948bb9c21e66b0e1d9a72,而证书本身的CN是子账号252771643302762862。

Certificate:

    Data:

        Version: 3 (0x2)

        Serial Number: 787224 (0xc0318)

    Signature Algorithm: sha256WithRSAEncryption

        Issuer: O=c0256a3b8e4b948bb9c21e66b0e1d9a72, OU=default, CN=c0256a3b8e4b948bb9c21e66b0e1d9a72

        Validity

            Not Before: Nov 29 06:03:00 2018 GMT

            Not After : Nov 28 06:08:39 2021 GMT

        Subject: O=system:users, OU=, CN=252771643302762862

  • 其次,只有在API Server信任客户端CA证书的情况下,上边的客户端证书才能通过API Server的验证。kube-apiserver进程通过client-ca-file这个参数指定其信任的客户端CA证书,其指定的证书是/etc/kubernetes/pki/apiserver-ca.crt。这个文件实际上包含了两张客户端CA证书,其中一张和集群管控有关系,这里不做解释,另外一张如下,它的CN与客户端证书的签发者CN一致。

Certificate:

    Data:

        Version: 3 (0x2)

        Serial Number: 787224 (0xc0318)

    Signature Algorithm: sha256WithRSAEncryption

        Issuer: O=c0256a3b8e4b948bb9c21e66b0e1d9a72, OU=default, CN=c0256a3b8e4b948bb9c21e66b0e1d9a72

        Validity

            Not Before: Nov 29 06:03:00 2018 GMT

            Not After : Nov 28 06:08:39 2021 GMT

        Subject: O=system:users, OU=, CN=252771643302762862


  • 再次,API Server使用的证书,由kube-apiserver的参数tls-cert-file决定,这个参数指向证书/etc/kubernetes/pki/apiserver.crt。这个证书的CN是kube-apiserver,签发者是c0256a3b8e4b948bb9c21e66b0e1d9a72,即集群CA证书。

Certificate:

    Data:

        Version: 3 (0x2)

        Serial Number: 2184578451551960857 (0x1e512e86fcba3f19)

    Signature Algorithm: sha256WithRSAEncryption

        Issuer: O=c0256a3b8e4b948bb9c21e66b0e1d9a72, OU=default, CN=c0256a3b8e4b948bb9c21e66b0e1d9a72

        Validity

            Not Before: Nov 29 03:59:00 2018 GMT

            Not After : Nov 29 04:14:23 2019 GMT

        Subject: CN=kube-apiserver


  • 最后,客户端需要验证上边这张API Server的证书,因而KubeConfig文件里包含了其签发者,即集群CA证书。对比集群CA证书和客户端CA证书,发现两张证书完全一样,这符合我们的预期。

Certificate:

    Data:

        Version: 3 (0x2)

        Serial Number: 786974 (0xc021e)

    Signature Algorithm: sha256WithRSAEncryption

        Issuer: C=CN, ST=ZheJiang, L=HangZhou, O=Alibaba, OU=ACS, CN=root

        Validity

            Not Before: Nov 29 03:59:00 2018 GMT

            Not After : Nov 24 04:04:00 2038 GMT

        Subject: O=c0256a3b8e4b948bb9c21e66b0e1d9a72, OU=default, CN=c0256a3b8e4b948bb9c21e66b0e1d9a72


访问

理解了原理之后,我们可以做一个简单的测试。我们以证书作为参数,使用curl访问api server,并得到预期结果。


# curl --cert ./client.crt --cacert ./ca.crt --key ./client.key https://xx.xx.xx.xxx:6443/api/

{

  "kind": "APIVersions",

  "versions": [

    "v1"

  ],

  "serverAddressByClientCIDRs": [

    {

      "clientCIDR": "0.0.0.0/0",

      "serverAddress": "192.168.0.222:6443"

    }

  ]

}


择优而居

两种节点,一种任务

如开始所讲,Kubernetes是管理集群多个节点的操作系统。这些节点在集群中的角色,却不必完全一样。Kubernetes集群有两种节点,master节点和worker节点。

这种角色的区分,实际上就是一种分工:master负责整个集群的管理,其上运行的以集群管理组件为主,这些组件包括实现集群入口的api server;而worker节点主要负责承载普通任务。

在Kubernetes集群中,任务被定义为pod这个概念。pod是集群可承载任务的原子单元。pod被翻译成容器组,其实是意译,因为一个pod实际上封装了多个容器化的应用。原则上来讲,被封装在一个pod里边的容器,应该是存在相当程度的耦合关系。

择优而居

调度算法需要解决的问题,是替pod选择一个舒适的“居所”,让pod所定义的任务可以在这个节点上顺利地完成。

为了实现“择优而居”的目标,Kubernetes集群调度算法采用了两步走的策略:第一步,从所有节点中排除不满足条件的节点,即预选;第二步,给剩余的节点打分,最后得分高者胜出,即优选。

下边,我们使用文章开始的时候制作的镜像,创建一个pod,并通过日志来具体分析一下,这个pod怎么样被调度到某一个集群节点。

Pod配置

首先,我们创建pod的配置文件,配置文件格式是json。这个配置文件有三个地方比较关键,分别是镜像地址,命令以及容器的端口。


{

    "apiVersion": "v1",

    "kind": "Pod",

    "metadata": {

        "name": "app"

    },

    "spec": {

        "containers": [

            {

                "name": "app",

                "image": "registry.cn-hangzhou.aliyuncs.com/kube-easy/app:latest",

                "command": [

                    "app"

                ],

                "ports": [

                    {

                        "containerPort": 2580

                    }

                ]

            }

        ]

    }

}


日志级别

集群调度算法被实现为运行在master节点上的系统组件,这一点和api server类似。其对应的进程名是kube-scheduler。kube-scheduler支持多个级别的日志输出,但社区并没有提供详细的日志级别说明文档。查看调度算法对节点进行筛选、打分的过程,我们需要把日志级别提高到10,即加入参数--v=10。


kube-scheduler --address=127.0.0.1 --kubeconfig=/etc/kubernetes/scheduler.conf --leader-elect=true --v=10


创建Pod

使用curl,以证书和pod配置文件等作为参数,通过POST请求访问api server的接口,我们可以在集群里创建对应的pod。


# curl -X POST -H 'Content-Type: application/json;charset=utf-8' --cert ./client.crt --cacert ./ca.crt --key ./client.key https://47.110.197.238:6443/api/v1/namespaces/default/pods -d@app.json


预选

预选是Kubernetes调度的第一步,这一步要做的事情,是根据预先定义的规则,把不符合条件的节点过滤掉。不同版本的Kubernetes所实现的预选规则有很大的不同,但基本的趋势,是预选规则会越来越丰富。

比较常见的两个预选规则是PodFitsResourcesPred和PodFitsHostPortsPred。前一个规则用来判断,一个节点上的剩余资源,是不是能够满足pod的需求;而后一个规则,检查一个节点上某一个端口是不是已经被其他pod所使用了。

下图是调度算法在处理测试pod的时候,输出的预选规则的日志。这段日志记录了预选规则CheckVolumeBindingPred 的执行情况。某些类型的存储卷(PV),只能挂载到一个节点上,这个规则可以过滤掉不满足pod对PV需求的节点。

从app的编排文件里可以看到,pod对存储卷并没有什么需求,所以这个条件并没有过滤掉节点。

优选

调度算法的第二个阶段是优选阶段。这个阶段,kube-scheduler会根据节点可用资源及其他一些规则,给剩余节点打分。

目前,CPU和内存是调度算法考量的两种主要资源,但考量的方式并不是简单的,剩余CPU、内存资源越多,得分就越高。

日志记录了两种计算方式:LeastResourceAllocation和BalancedResourceAllocation。前一种方式计算pod调度到节点之后,节点剩余CPU和内存占总CPU和内存的比例,比例越高得分就越高;第二种方式计算节点上CPU和内存使用比例之差的绝对值,绝对值越大,得分越少。

这两种方式,一种倾向于选出资源使用率较低的节点,第二种希望选出两种资源使用比例接近的节点。这两种方式有一些矛盾,最终依靠一定的权重来平衡这两个因素。

除了资源之外,优选算法会考虑其他一些因素,比如pod与节点的亲和性,或者如果一个服务有多个相同pod组成的情况下,多个pod在不同节点上的分散程度,这是保证高可用的一种策略。

得分

最后,调度算法会给所有的得分项乘以它们的权重,然后求和得到每个节点最终的得分。因为测试集群使用的是默认调度算法,而默认调度算法把日志中出现的得分项所对应的权重,都设置成了1,所以如果按日志里有记录得分项来计算,最终三个节点的得分应该是29,28和29。

之所以会出现日志输出的得分和我们自己计算的得分不符的情况,是因为日志并没有输出所有的得分项,猜测漏掉的策略应该是NodePreferAvoidPodsPriority,这个策略的权重是10000,每个节点得分10,所以才得出最终日志输出的结果。

结束语

在这篇文章中,我们以一个简单的容器化web程序为例,着重分析了客户端怎么样通过Kubernetes集群API Server认证,以及容器应用怎么样被分派到合适节点这两件事情。

在分析过程中,我们弃用了一些便利的工具,比如kubectl,或者控制台。我们用了一些更接近底层的小实验,比如拆解KubeConfig文件,再比如分析调度器日志来分析认证和调度算法的运作原理。希望这些对大家进一步理解Kubernetes集群有所帮助。

 

相关实践学习
通过Ingress进行灰度发布
本场景您将运行一个简单的应用,部署一个新的应用用于新的发布,并通过Ingress能力实现灰度发布。
容器应用与集群管理
欢迎来到《容器应用与集群管理》课程,本课程是“云原生容器Clouder认证“系列中的第二阶段。课程将向您介绍与容器集群相关的概念和技术,这些概念和技术可以帮助您了解阿里云容器服务ACK/ACK Serverless的使用。同时,本课程也会向您介绍可以采取的工具、方法和可操作步骤,以帮助您了解如何基于容器服务ACK Serverless构建和管理企业级应用。 学习完本课程后,您将能够: 掌握容器集群、容器编排的基本概念 掌握Kubernetes的基础概念及核心思想 掌握阿里云容器服务ACK/ACK Serverless概念及使用方法 基于容器服务ACK Serverless搭建和管理企业级网站应用
目录
相关文章
|
6月前
|
Kubernetes 网络协议 网络安全
提升你的云技能:深入了解CKA认证之k8s升级秘籍!
提升你的云技能:深入了解CKA认证之k8s升级秘籍!
107 0
|
4月前
|
缓存 运维 监控
运维之道:从故障响应到系统优化的实战之旅
在信息技术飞速发展的今天,高效、可靠的系统运维已成为企业IT部门的核心任务。本文将通过一系列真实案例分析,深入探讨运维团队如何从日常的故障响应出发,逐步过渡到系统性能的深度优化。我们将一起探索运维的最佳实践,包括自动化工具的应用、性能监控的重要性以及如何构建一个弹性和高可用性的系统架构。文章旨在为读者提供一套完整的运维解决方案,帮助他们在面对复杂多变的技术环境时,能够迅速定位问题并实施有效的解决策略。
237 0
|
6月前
|
存储 Kubernetes 数据安全/隐私保护
总结归纳Kubernetes | 一站式速查知识,助您轻松驾驭容器编排技术(配置与密码安全)
总结归纳Kubernetes | 一站式速查知识,助您轻松驾驭容器编排技术(配置与密码安全)
92 0
|
6月前
|
运维 Kubernetes 网络协议
总结归纳Kubernetes | 一站式速查知识,助您轻松驾驭容器编排技术(服务治理与网络访问)
总结归纳Kubernetes | 一站式速查知识,助您轻松驾驭容器编排技术(服务治理与网络访问)
64 0
|
机器学习/深度学习 弹性计算 运维
企业运维训练营之云上网络原理与实践课程 - 第二讲 负载均衡CLB(下)- 常见问题与解决思路
课程目标 了解负载均衡CLB的产品功能 了解负载均衡CLB的底层架构与相关技术 掌握负载均衡CLB的最佳实践 熟知负载均衡CLB的常见问题与解决思路
企业运维训练营之云上网络原理与实践课程 - 第二讲  负载均衡CLB(下)- 常见问题与解决思路
|
SQL 监控 API
阿里云应用监控(ARMS)过程全解析:我们在乎用户每一秒的体验
在互联网高速发展的时代,应用成为连接企业与用户的载体,网络购物,实时交易,游戏娱乐,办公邮件等各种各样的使用场景中,网络响应无延迟,用户访问无槽点成为企业应用能否在激烈的市场竞争中脱颖而出的重要影响因素。
9705 5
|
域名解析 Kubernetes 负载均衡
Knative 化繁为简之道:应用部署与访问
Knative 是一款基于 Kubernetes 之上的开源 Serverless 框架,其目标之一是降低用户服务部署及使用门槛。在此基础上提还供了其它丰富的功能,如自动扩缩容,路由流量,金丝雀发布以及事件驱动等。在本文中,我们首先 Kubernetes 上部署应用并进行服务访问,然后在 Knative 中部署同样的服务并访问,以此对比来看Knative如何降低了服务部署及访问的门槛。
6153 0
Knative 化繁为简之道:应用部署与访问
|
弹性计算 Kubernetes 前端开发
K8s 从懵圈到熟练-集群伸缩原理
阿里云 K8s 集群的一个重要特性,是集群的节点可以动态的增加或减少。有了这个特性,集群才能在计算资源不足的情况下扩容新的节点,同时也可以在资源利用率降低的时候,释放节点以节省费用。这篇文章,我们将讨论阿里云 K8s 集群扩容与缩容的实现原理。理解实现原理,在遇到问题的时候,我们就可以高效地排查并定位原因。
K8s 从懵圈到熟练-集群伸缩原理
|
存储 JSON Kubernetes
K8S从懵圈到熟练:认证与调度
不知道大家有没有意识到一个现实,就是大部分时候,我们已经不像以前一样,通过命令行,或者可视窗口来使用一个系统了。现在我们上微博、或者网购,操作的其实不是眼前这台设备,而是一个又一个集群。 通常,这样的集群拥有成百上千个节点,每个节点是一台物理机或虚拟机。
K8S从懵圈到熟练:认证与调度
下一篇
无影云桌面