learn.lianglianglee.com/专栏/Kubernetes 从上手到实践/03 宏观认识:整体架构.md.html
2022-05-11 19:04:14 +08:00

649 lines
23 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<!-- saved from url=(0046)https://kaiiiz.github.io/hexo-theme-book-demo/ -->
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1.0, user-scalable=no">
<link rel="icon" href="/static/favicon.png">
<title>03 宏观认识:整体架构.md.html</title>
<!-- Spectre.css framework -->
<link rel="stylesheet" href="/static/index.css">
<!-- theme css & js -->
<meta name="generator" content="Hexo 4.2.0">
</head>
<body>
<div class="book-container">
<div class="book-sidebar">
<div class="book-brand">
<a href="/">
<img src="/static/favicon.png">
<span>技术文章摘抄</span>
</a>
</div>
<div class="book-menu uncollapsible">
<ul class="uncollapsible">
<li><a href="/" class="current-tab">首页</a></li>
</ul>
<ul class="uncollapsible">
<li><a href="../">上一级</a></li>
</ul>
<ul class="uncollapsible">
<li>
<a href="/专栏/Kubernetes 从上手到实践/01 开篇: Kubernetes 是什么以及为什么需要它.md.html">01 开篇: Kubernetes 是什么以及为什么需要它.md.html</a>
</li>
<li>
<a href="/专栏/Kubernetes 从上手到实践/02 初步认识Kubernetes 基础概念.md.html">02 初步认识Kubernetes 基础概念.md.html</a>
</li>
<li>
<a class="current-tab" href="/专栏/Kubernetes 从上手到实践/03 宏观认识:整体架构.md.html">03 宏观认识:整体架构.md.html</a>
</li>
<li>
<a href="/专栏/Kubernetes 从上手到实践/04 搭建 Kubernetes 集群 - 本地快速搭建.md.html">04 搭建 Kubernetes 集群 - 本地快速搭建.md.html</a>
</li>
<li>
<a href="/专栏/Kubernetes 从上手到实践/05 动手实践:搭建一个 Kubernetes 集群 - 生产可用.md.html">05 动手实践:搭建一个 Kubernetes 集群 - 生产可用.md.html</a>
</li>
<li>
<a href="/专栏/Kubernetes 从上手到实践/06 集群管理:初识 kubectl.md.html">06 集群管理:初识 kubectl.md.html</a>
</li>
<li>
<a href="/专栏/Kubernetes 从上手到实践/07 集群管理:以 Redis 为例-部署及访问.md.html">07 集群管理:以 Redis 为例-部署及访问.md.html</a>
</li>
<li>
<a href="/专栏/Kubernetes 从上手到实践/08 安全重点 认证和授权.md.html">08 安全重点 认证和授权.md.html</a>
</li>
<li>
<a href="/专栏/Kubernetes 从上手到实践/09 应用发布:部署实际项目.md.html">09 应用发布:部署实际项目.md.html</a>
</li>
<li>
<a href="/专栏/Kubernetes 从上手到实践/10 应用管理:初识 Helm.md.html">10 应用管理:初识 Helm.md.html</a>
</li>
<li>
<a href="/专栏/Kubernetes 从上手到实践/11 部署实践:以 Helm 部署项目.md.html">11 部署实践:以 Helm 部署项目.md.html</a>
</li>
<li>
<a href="/专栏/Kubernetes 从上手到实践/12 庖丁解牛kube-apiserver.md.html">12 庖丁解牛kube-apiserver.md.html</a>
</li>
<li>
<a href="/专栏/Kubernetes 从上手到实践/13 庖丁解牛etcd.md.html">13 庖丁解牛etcd.md.html</a>
</li>
<li>
<a href="/专栏/Kubernetes 从上手到实践/14 庖丁解牛controller-manager.md.html">14 庖丁解牛controller-manager.md.html</a>
</li>
<li>
<a href="/专栏/Kubernetes 从上手到实践/15 庖丁解牛kube-scheduler.md.html">15 庖丁解牛kube-scheduler.md.html</a>
</li>
<li>
<a href="/专栏/Kubernetes 从上手到实践/16 庖丁解牛kubelet.md.html">16 庖丁解牛kubelet.md.html</a>
</li>
<li>
<a href="/专栏/Kubernetes 从上手到实践/17 庖丁解牛kube-proxy.md.html">17 庖丁解牛kube-proxy.md.html</a>
</li>
<li>
<a href="/专栏/Kubernetes 从上手到实践/18 庖丁解牛Container Runtime Docker.md.html">18 庖丁解牛Container Runtime Docker.md.html</a>
</li>
<li>
<a href="/专栏/Kubernetes 从上手到实践/19 Troubleshoot.md.html">19 Troubleshoot.md.html</a>
</li>
<li>
<a href="/专栏/Kubernetes 从上手到实践/20 扩展增强Dashboard.md.html">20 扩展增强Dashboard.md.html</a>
</li>
<li>
<a href="/专栏/Kubernetes 从上手到实践/21 扩展增强CoreDNS.md.html">21 扩展增强CoreDNS.md.html</a>
</li>
<li>
<a href="/专栏/Kubernetes 从上手到实践/22 服务增强Ingress.md.html">22 服务增强Ingress.md.html</a>
</li>
<li>
<a href="/专栏/Kubernetes 从上手到实践/23 监控实践:对 K8S 集群进行监控.md.html">23 监控实践:对 K8S 集群进行监控.md.html</a>
</li>
<li>
<a href="/专栏/Kubernetes 从上手到实践/24 总结.md.html">24 总结.md.html</a>
</li>
</ul>
</div>
</div>
<div class="sidebar-toggle" onclick="sidebar_toggle()" onmouseover="add_inner()" onmouseleave="remove_inner()">
<div class="sidebar-toggle-inner"></div>
</div>
<script>
function add_inner() {
let inner = document.querySelector('.sidebar-toggle-inner')
inner.classList.add('show')
}
function remove_inner() {
let inner = document.querySelector('.sidebar-toggle-inner')
inner.classList.remove('show')
}
function sidebar_toggle() {
let sidebar_toggle = document.querySelector('.sidebar-toggle')
let sidebar = document.querySelector('.book-sidebar')
let content = document.querySelector('.off-canvas-content')
if (sidebar_toggle.classList.contains('extend')) { // show
sidebar_toggle.classList.remove('extend')
sidebar.classList.remove('hide')
content.classList.remove('extend')
} else { // hide
sidebar_toggle.classList.add('extend')
sidebar.classList.add('hide')
content.classList.add('extend')
}
}
function open_sidebar() {
let sidebar = document.querySelector('.book-sidebar')
let overlay = document.querySelector('.off-canvas-overlay')
sidebar.classList.add('show')
overlay.classList.add('show')
}
function hide_canvas() {
let sidebar = document.querySelector('.book-sidebar')
let overlay = document.querySelector('.off-canvas-overlay')
sidebar.classList.remove('show')
overlay.classList.remove('show')
}
</script>
<div class="off-canvas-content">
<div class="columns">
<div class="column col-12 col-lg-12">
<div class="book-navbar">
<!-- For Responsive Layout -->
<header class="navbar">
<section class="navbar-section">
<a onclick="open_sidebar()">
<i class="icon icon-menu"></i>
</a>
</section>
</header>
</div>
<div class="book-content" style="max-width: 960px; margin: 0 auto;
overflow-x: auto;
overflow-y: hidden;">
<div class="book-post">
<p id="tip" align="center"></p>
<div><h1>03 宏观认识:整体架构</h1>
<p>工欲善其事,必先利其器。本节我们来从宏观上认识下 K8S 的整体架构,以便于后续在此基础上进行探索和实践。</p>
<h2>C/S 架构</h2>
<p>从更高层来看K8S 整体上遵循 C/S 架构,从这个角度来看,可用下面的图来表示其结构:</p>
<pre><code> +-------------+
| |
| | +---------------+
| | +-----&gt; | Node 1 |
| Kubernetes | | +---------------+
+-----------------+ | Server | |
| CLI | | | | +---------------+
| (Kubectl) |-----------&gt;| ( Master ) |&lt;------+-----&gt; | Node 2 |
| | | | | +---------------+
+-----------------+ | | |
| | | +---------------+
| | +-----&gt; | Node 3 |
| | +---------------+
+-------------+
</code></pre>
<p>左侧是一个官方提供的名为 <code>kubectl</code> 的 CLI Command Line Interface工具用于使用 K8S 开放的 API 来管理集群和操作对象等。</p>
<p>右侧则是 K8S 集群的后端服务及开放出的 API 等。根据上一节的内容,我们知道 Node 是用于工作的机器,而 Master 是一种角色Role表示在这个 Node 上包含着管理集群的一些必要组件。具体组件的详细介绍参考第 11 小节对各组件的详细剖析。</p>
<p>当然在这里,只画出了一个 Master在生产环境中为了保障集群的高可用我们通常会部署多个 Master 。</p>
<h2>Master</h2>
<p>下面我们来逐层分解, 首先是 Master 这里我们只介绍其管理集群的相关组件。Master 是整个 K8S 集群的“大脑”,与大脑类似,它有几个重要的功能:</p>
<ul>
<li>接收:外部的请求和集群内部的通知反馈</li>
<li>发布:对集群整体的调度和管理</li>
<li>存储:存储</li>
</ul>
<p>这些功能,也通过一些组件来共同完成,通常情况下,我们将其称为 control plane 。如下图所示:</p>
<pre><code>+----------------------------------------------------------+
| Master |
| +-------------------------+ |
| +-------&gt;| API Server |&lt;--------+ |
| | | | | |
| v +-------------------------+ v |
| +----------------+ ^ +--------------------+ |
| | | | | | |
| | Scheduler | | | Controller Manager | |
| | | | | | |
| +----------------+ v +--------------------+ |
| +------------------------------------------------------+ |
| | | |
| | Cluster state store | |
| | | |
| +------------------------------------------------------+ |
+----------------------------------------------------------+
</code></pre>
<p>它主要包含以下几个重要的组成部分。</p>
<h3>Cluster state store</h3>
<p>存储集群所有需持久化的状态,并且提供 watch 的功能支持,可以快速的通知各组件的变更等操作。</p>
<p>因为目前 Kubernetes 的存储层选择是 etcd ,所以一般情况下,大家都直接以 etcd 来代表集群状态存储服务。即:将所有状态存储到 etcd 实例中。</p>
<p>刚才我们说 Master 相当于是 K8S 集群的大脑更细化来看etcd 则是大脑中的核心,为什么这么说?可以参考后面详细剖析的章节,本章我们先从更高的层次来看集群的整体架构。</p>
<p>你可能会问, etcd 是必须的吗就目前而言etcd 是必须的,这主要是 Kubernetes 的内部实现。</p>
<p>而早在 2014 年左右,社区就一直在提议将存储层抽象出来,后端的实际存储作为一种插件化的存在。<a href="https://github.com/kubernetes/kubernetes/issues/1957">呼声</a>比较大的是另一种提供 k/v 存储功能的 <a href="https://www.consul.io/">Consul</a></p>
<p>不过得益于 etcd 的开发团队较为活跃,而且根据 K8S 社区的反馈做了相当大的改进,并且当时 K8S 团队主要的关注点也不在此,所以直到现在 etcd 仍不是一个可选项。</p>
<p>如果现在去看下 Kubernetes 的源代码,你会发现存储层的代码还比较简洁清晰,后续如果有精力也许将此处插件化也不是不可能。</p>
<h3>API Server</h3>
<p>这是整个集群的入口,类似于人体的感官,接收外部的信号和请求,并将一些信息写入到 etcd 中。</p>
<p>实际处理逻辑比三次握手简单的多:</p>
<ul>
<li>请求 API Server :“嗨,我有些东西要放到 etcd 里面”</li>
<li>API Server 收到请求:“你是谁?我为啥要听你的”</li>
<li>从请求中拿出自己的身份凭证一般是证书“是我啊你的master给我把这些东西放进去”</li>
<li>这时候就要看是些什么内容了,如果这些内容 API Server 能理解,那就放入 etcd 中 “好的 master 我放进去了”;如果不能理解,“抱歉 master 我理解不了”</li>
</ul>
<p>可以看到,它提供了认证相关的功能,用于判断是否有权限进行操作。当然 API Server 支持多种认证方法,不过一般情况下,我们都使用 x509 证书进行认证。</p>
<p>API Server 的目标是成为一个极简的 server只提供 REST 操作,更新 etcd ,并充当着集群的网关。至于其他的业务逻辑之类的,通过插件或者在其他组件中完成。关于这部分的详细实现,可以参考后面的 API Server 剖析相关章节。</p>
<h3>Controller Manager</h3>
<p>Controller Manager 大概是 K8S 集群中最繁忙的部分,它在后台运行着许多不同的控制器进程,用来调节集群的状态。</p>
<p>当集群的配置发生变更,控制器就会朝着预期的状态开始工作。</p>
<h3>Scheduler</h3>
<p>顾名思义Scheduler 是集群的调度器,它会持续的关注集群中未被调度的 Pod ,并根据各种条件,比如资源的可用性,节点的亲和性或者其他的一些限制条件,通过绑定的 API 将 Pod 调度/绑定到 Node 上。</p>
<p>在这个过程中,调度程序一般只考虑调度开始时, Node 的状态,而不考虑在调度过程中 Node 的状态变化 (比如节点亲和性等,截至到目前 v1.11.2 也暂未加入相关功能的稳定特性)</p>
<h2>Node</h2>
<p>Node 的概念我们在上节已经提过了,这里不再过多赘述,简单点理解为加入集群中的机器即可。</p>
<p>那 Node 是如何加入集群接受调度,并运行服务的呢?这都要归功于运行在 Node 上的几个核心组件。我们先来看下整体结构:</p>
<pre><code>+--------------------------------------------------------+
| +---------------------+ +---------------------+ |
| | kubelet | | kube-proxy | |
| | | | | |
| +---------------------+ +---------------------+ |
| +----------------------------------------------------+ |
| | Container Runtime (Docker) | |
| | +---------------------+ +---------------------+ | |
| | |Pod | |Pod | | |
| | | +-----+ +-----+ | |+-----++-----++-----+| | |
| | | |C1 | |C2 | | ||C1 ||C2 ||C3 || | |
| | | | | | | | || || || || | |
| | | +-----+ +-----+ | |+-----++-----++-----+| | |
| | +---------------------+ +---------------------+ | |
| +----------------------------------------------------+ |
+--------------------------------------------------------+
</code></pre>
<h3>Kubelet</h3>
<p>Kubelet 实现了集群中最重要的关于 Node 和 Pod 的控制功能,如果没有 Kubelet 的存在,那 Kubernetes 很可能就只是一个纯粹的通过 API Server CRUD 的应用程序。</p>
<p>K8S 原生的执行模式是操作应用程序的容器,而不像传统模式那样,直接操作某个包或者是操作某个进程。基于这种模式,可以让应用程序之间相互隔离,互不影响。此外,由于是操作容器,所以应用程序可以说和主机也是相互隔离的,毕竟它不依赖于主机,在任何的容器运行时(比如 Docker上都可以部署和运行。</p>
<p>我们在上节介绍过 PodPod 可以是一组容器也可以包含存储卷K8S 将 Pod 作为可调度的基本单位, 分离开了构建时和部署时的关注点:</p>
<ul>
<li>构建时,重点关注某个容器是否能正确构建,如何快速构建</li>
<li>部署时,关心某个应用程序的服务是否可用,是否符合预期,依赖的相关资源是否都能访问到</li>
</ul>
<p>这种隔离的模式,可以很方便的将应用程序与底层的基础设施解耦,极大的提高集群扩/缩容,迁移的灵活性。</p>
<p>在前面,我们提到了 Master 节点的 <code>Scheduler</code> 组件,它会调度未绑定的 Pod 到符合条件的 Node 上,而至于最终该 Pod 是否能运行于 Node 上,则是由 <code>Kubelet</code> 来裁定的。关于 Kubelet 的具体原理,后面有详细剖析的章节。</p>
<h3>Container runtime</h3>
<p>容器运行时最主要的功能是下载镜像和运行容器,我们最常见的实现可能是 <a href="https://www.docker.com/">Docker</a> , 目前还有其他的一些实现,比如 <a href="https://github.com/rkt/rkt">rkt</a>, <a href="https://github.com/kubernetes-sigs/cri-o">cri-o</a></p>
<p>K8S 提供了一套通用的容器运行时接口 CRI (Container Runtime Interface), 凡是符合这套标准的容器运行时实现,均可在 K8S 上使用。</p>
<h3>Kube Proxy</h3>
<p>我们都知道,想要访问某个服务,那要么通过域名,要么通过 IP。而每个 Pod 在创建后都会有一个虚拟 IPK8S 中有一个抽象的概念,叫做 <code>Service</code> <code>kube-proxy</code> 便是提供一种代理的服务,让你可以通过 <code>Service</code> 访问到 Pod。</p>
<p>实际的工作原理是在每个 Node 上启动一个 <code>kube-proxy</code> 的进程,通过编排 <code>iptables</code> 规则来达到此效果。深入的解析,在后面有对应的章节。</p>
<h2>总结</h2>
<p>本节中,我们了解到了 K8S 的整体遵循 C/S 架构,集群的 Master 包含着几个重要的组成部分,比如 <code>API Server</code>, <code>Controller Manager</code> 等。</p>
<p>而 Node 上,则运行着三个必要的组件 <code>kubelet</code>, <code>container runtime</code> (一般是 Docker), <code>kube-proxy</code></p>
<p>通过所有组件的分工协作,最终实现了 K8S 对容器的编排和调度。</p>
<p>完成了这节的学习,那我们就开始着手搭建一个属于我们自己的集群吧。</p>
</div>
</div>
<div>
<div style="float: left">
<a href="/专栏/Kubernetes 从上手到实践/02 初步认识Kubernetes 基础概念.md.html">上一页</a>
</div>
<div style="float: right">
<a href="/专栏/Kubernetes 从上手到实践/04 搭建 Kubernetes 集群 - 本地快速搭建.md.html">下一页</a>
</div>
</div>
</div>
</div>
</div>
</div>
<a class="off-canvas-overlay" onclick="hide_canvas()"></a>
</div>
<script defer src="https://static.cloudflareinsights.com/beacon.min.js/v652eace1692a40cfa3763df669d7439c1639079717194" integrity="sha512-Gi7xpJR8tSkrpF7aordPZQlW2DLtzUlZcumS8dMQjwDHEnw9I7ZLyiOj/6tZStRBGtGgN6ceN6cMH8z7etPGlw==" data-cf-beacon='{"rayId":"70997230b8393d60","version":"2021.12.0","r":1,"token":"1f5d475227ce4f0089a7cff1ab17c0f5","si":100}' crossorigin="anonymous"></script>
</body>
<!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-NPSEEVD756"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag() {
dataLayer.push(arguments);
}
gtag('js', new Date());
gtag('config', 'G-NPSEEVD756');
var path = window.location.pathname
var cookie = getCookie("lastPath");
console.log(path)
if (path.replace("/", "") === "") {
if (cookie.replace("/", "") !== "") {
console.log(cookie)
document.getElementById("tip").innerHTML = "<a href='" + cookie + "'>跳转到上次进度</a>"
}
} else {
setCookie("lastPath", path)
}
function setCookie(cname, cvalue) {
var d = new Date();
d.setTime(d.getTime() + (180 * 24 * 60 * 60 * 1000));
var expires = "expires=" + d.toGMTString();
document.cookie = cname + "=" + cvalue + "; " + expires + ";path = /";
}
function getCookie(cname) {
var name = cname + "=";
var ca = document.cookie.split(';');
for (var i = 0; i < ca.length; i++) {
var c = ca[i].trim();
if (c.indexOf(name) === 0) return c.substring(name.length, c.length);
}
return "";
}
</script>
</html>