Kubernetes 深入学习(一) —— 入门和集群安装部署
<h2>一、简介</h2><h3>1、Kubernetes 是什么</h3>
<p>Kubernetes 是一个全新的基于容器技术的分布式架构解决方案,是 Google 开源的一个容器集群管理系统,Kubernetes 简称 K8S。</p>
<p>Kubernetes 是一个一站式的完备的分布式系统开发和支撑平台,更是一个开放平台,对现有的编程语言、编程框架、中间件没有任何侵入性。</p>
<p>Kubernetes 提供了完善的管理工具,这些工具涵盖了开发、部署测试、运维监控在内的各个环节。</p>
<p>Kubernetes 具有完备的集群管理能力,包括多层次的安全防护和准入机制、多租户应用支撑能力、透明的服务注册和服务发现机制、内建智能负载均衡器、强大的故障发现和自我修复能力、服务滚动升级和在线扩容能力、可扩展的资源自动调度机制、多粒度的资源配额管理能力。</p>
<p>Kubernetes 官方文档:https://kubernetes.io/zh/</p>
<h3>2、Kubernetes 特性</h3>
<p>① 自我修复</p>
<p>在节点故障时,重新启动失败的容器,替换和重新部署,保证预期的副本数量;杀死健康检查失败的容器,并且在未准备好之前不会处理用户的请求,确保线上服务不中断。</p>
<p>② 弹性伸缩</p>
<p>使用命令、UI或者基于CPU使用情况自动快速扩容和缩容应用程序实例,保证应用业务高峰并发时的高可用性;业务低峰时回收资源,以最小成本运行服务。</p>
<p>③ 自动部署和回滚</p>
<p>K8S采用滚动更新策略更新应用,一次更新一个Pod,而不是同时删除所有Pod,如果更新过程中出现问题,将回滚更改,确保升级不影响业务。</p>
<p>④ 服务发现和负载均衡</p>
<p>K8S为多个容器提供一个统一访问入口(内部IP地址和一个DNS名称),并且负载均衡关联的所有容器,使得用户无需考虑容器IP问题。</p>
<p>⑤ 机密和配置管理</p>
<p>管理机密数据和应用程序配置,而不需要把敏感数据暴露在镜像里,提高敏感数据安全性。并可以将一些常用的配置存储在K8S中,方便应用程序使用。</p>
<p>⑥ 存储编排</p>
<p>挂载外部存储系统,无论是来自本地存储,公有云,还是网络存储,都作为集群资源的一部分使用,极大提高存储使用灵活性。</p>
<p>⑦ 批处理</p>
<p>提供一次性任务,定时任务;满足批量数据处理和分析的场景。</p>
<h2>二、集群架构与组件</h2>
<p>Kubernetes 集群架构以及相关的核心组件如下图所示:一个 Kubernetes 集群一般包含一个 Master 节点和多个 Node 节点,一个节点可以看成是一台物理机或虚拟机。</p>
<p><img src="https://img2018.cnblogs.com/blog/856154/201910/856154-20191023003358108-1816205812.png" alt=""></p>
<h3>1、Master</h3>
<p>Master 是 K8S 的集群控制节点,每个 K8S 集群里需要有一个 Master 节点来负责整个集群的管理和控制,基本上 K8S 所有的控制命令都是发给它,它来负责具体的执行过程。Master 节点通常会占据一个独立的服务器,因为它太重要了,如果它不可用,那么所有的控制命令都将失效。</p>
<p>Master 节点上运行着以下关键组件:</p>
<p>① <strong>kube-apiserver</strong></p>
<p>是集群的统一入口,各组件协调者,以 HTTP Rest 提供接口服务,所有对象资源的增、删、改、查和监听操作都交给 apiserver 处理后再提交给 Etcd 存储。</p>
<p>② <strong>kube-controller-manager</strong></p>
<p>是 K8S 里所有资源对象的自动化控制中心,处理集群中常规后台任务,一个资源对应一个控制器,而 controller-manager 就是负责管理这些控制器的。</p>
<p>③ <strong>kube-scheduler</strong></p>
<p>根据调度算法为新创建的 Pod 选择一个 Node 节点,可以任意部署,可以部署在同一个节点上,也可以部署在不同的节点上。</p>
<p>④ <strong>etcd</strong></p>
<p>是一个分布式的,一致的 key-value 存储,主要用途是共享配置和服务发现,保存集群状态数据,比如 Pod、Service 等对象信息。</p>
<h3>2、Node</h3>
<p>除了 Master,K8S 集群中的其它机器被称为 Node 节点,Node 节点是 K8S 集群中的工作负载节点,每个 Node 都会被 Master 分配一些工作负载,当某个 Node 宕机时,其上的工作负载会被 Master 自动转移到其它节点上去。</p>
<p>每个 Node 节点上都运行着以下关键组件:</p>
<p>① <strong>kubelet</strong></p>
<p>kubelet 是 Master 在 Node 节点上的 Agent(代理),与 Master 密切协作,管理本机运行容器的生命周期,负责 Pod 对应的容器的创建、启停等任务,实现集群管理的基本功能。</p>
<p>② <strong>kube-proxy</strong></p>
<p>在 Node 节点上实现 Pod 网络代理,实现 Kubernetes Service 的通信,维护网络规则和四层负载均衡工作。</p>
<p>③ <strong>docker engine</strong></p>
<p>Docker 引擎,负责本机的容器创建和管理工作。</p>
<p>Node 节点可以在运行期间动态增加到 K8S 集群中,前提是这个节点上已经正确安装、配置和启动了上述关键组件。在默认情况下 kubelet 会向 Master 注册自己,一旦 Node 被纳入集群管理范围,kubelet 就会定时向 Master 节点汇报自身的情况,例如操作系统、Docker 版本、机器的 CPU 和内存情况,以及之前有哪些 Pod 在运行等,这样 Master 可以获知每个 Node 的资源使用情况,并实现高效均衡的资源调度策略。而某个 Node 超过指定时间不上报信息时,会被 Master 判定为“失联”,Node 的状态被标记为不可用(Not Ready),随后 Master 会触发“工作负载大转移”的自动流程。</p>
<h2>三、核心概念</h2>
<p><strong>1、Pod</strong></p>
<p>Pod 是 K8S 中最重要也是最基本的概念,Pod 是最小的部署单元,是一组容器的集合。每个 Pod 都由一个特殊的根容器 Pause 容器,以及一个或多个紧密相关的用户业务容器组成。</p>
<p>Pause 容器作为 Pod 的根容器,以它的状态代表整个容器组的状态。K8S 为每个 Pod 都分配了唯一的 IP 地址,称之为 Pod IP。Pod 里的多个业务容器共享 Pause 容器的IP,共享 Pause 容器挂载的 Volume。</p>
<p><strong>2、Label</strong></p>
<p>标签,附加到某个资源上,用于关联对象、查询和筛选。一个 Label 是一个 key=value 的键值对,key 与 value 由用户自己指定。Label 可以附加到各种资源上,一个资源对象可以定义任意数量的 Label,同一个 Label 也可以被添加到任意数量的资源上。</p>
<p>我们可以通过给指定的资源对象捆绑一个或多个不同的 Label 来实现多维度的资源分组管理功能,以便于灵活、方便地进行资源分配、调度、配置、部署等工作。</p>
<p>K8S 通过 Label Selector(标签选择器)来查询和筛选拥有某些 Label 的资源对象。Label Selector 有基于等式( name=label1 )和基于集合( name in (label1, label2) )的两种方式。</p>
<p><strong>3、ReplicaSet(RC)</strong></p>
<p>ReplicaSet 用来确保预期的 Pod 副本数量,如果有过多的 Pod 副本在运行,系统就会停掉一些 Pod,否则系统就会再自动创建一些 Pod。</p>
<p>我们很少单独使用 ReplicaSet,它主要被 Deployment 这个更高层的资源对象使用,从而形成一整套 Pod 创建、删除、更新的编排机制。</p>
<p><strong>4、Deployment</strong></p>
<div>
<div>Deployment 用于部署无状态应用,Deployment 为 Pod 和 ReplicaSet 提供声明式更新,只需要在 Deployment 描述想要的目标状态,Deployment 就会将 Pod 和 ReplicaSet 的实际状态改变到目标状态。</div>
</div>
<p><strong>5、Horizontal Pod Autoscaler(HPA)</strong></p>
<p>HPA 为 Pod 横向自动扩容,也是 K8S 的一种资源对象。HPA 通过追踪分析 RC 的所有目标 Pod 的负载变化情况,来确定是否需要针对性调整目标 Pod 的副本数量。</p>
<p><strong>6、Service</strong></p>
<p>Service 定义了一个服务的访问入口,通过 Label Selector 与 Pod 副本集群之间“无缝对接”,定义了一组 Pod 的访问策略,防止 Pod 失联。</p>
<p>创建 Service 时,K8S会自动为它分配一个全局唯一的虚拟 IP 地址,即 Cluster IP。服务发现就是通过 Service 的 Name 和 Service 的 ClusterIP 地址做一个 DNS 域名映射来解决的。</p>
<p><strong>7、Namespace</strong></p>
<p>命名空间,Namespace 多用于实现多租户的资源隔离。Namespace 通过将集群内部的资源对象“分配”到不同的Namespace中,形成逻辑上分组的不同项目、小组或用户组。</p>
<p>K8S 集群在启动后,会创建一个名为 default 的 Namespace,如果不特别指明 Namespace,创建的 Pod、RC、Service 都将被创建到 default 下。</p>
<p>当我们给每个租户创建一个 Namespace 来实现多租户的资源隔离时,还可以结合 K8S 的资源配额管理,限定不同租户能占用的资源,例如 CPU 使用量、内存使用量等。</p>
<h2>四、集群搭建 —— 平台规划</h2>
<h3>1、生产环境 K8S 平台规划</h3>
<p>K8S 环境有两种架构方式,单 Master 集群和多 Master 集群,将先搭建起单 Master 集群,再扩展为多 Master 集群。开发、测试环境可以部署单 Master 集群,生产环境为了保证高可用需部署多 Master 集群。</p>
<p><strong>① 单 Master 集群架构</strong></p>
<p>单 Master 集群架构相比于多 Master 集群架构无法保证集群的高可用,因为 master 节点一旦宕机就无法进行集群的管理工作了。单 master 集群主要包含一台 Master 节点,及多个 Node 工作节点、多个 Etcd 数据库节点。</p>
<p>Etcd 是 K8S 集群的数据库,可以安装在任何地方,也可以与 Master 节点在同一台机器上,只要 K8S 能连通 Etcd。</p>
<p><img src="https://img2018.cnblogs.com/blog/856154/201910/856154-20191029230911133-1562198790.png" alt=""></p>
<p><strong>② 多 Master 集群架构</strong></p>
<p>多 Master 集群能保证集群的高可用,相比单 Master 架构,需要一个额外的负载均衡器来负载多个 Master 节点,Node 节点从连接 Master 改成连接 LB 负载均衡器。</p>
<p><img src="https://img2018.cnblogs.com/blog/856154/201910/856154-20191029231927413-1474291721.png" alt=""></p>
<p><strong>③ 集群规划</strong></p>
<p>为了测试,我在本地使用 VMware 创建了几个虚拟机(可通过克隆快速创建虚拟机),一个虚拟机代表一台独立的服务器。</p>
<p><img style="border: 1px solid rgba(0, 0, 0, 1)" src="https://img2018.cnblogs.com/blog/856154/201910/856154-20191031002248686-44613079.png" alt=""></p>
<p>K8S 集群规划如下:</p>
<p>生产环境建议至少两台 Master 节点,LB 主备各一个节点;至少两台以上 Node 节点,根据实际运行的容器数量调整;Etcd 数据库可直接部署在 Master 和 Node 的节点,机器比较充足的话,可以部署在单独的节点上。</p>
<p><img src="https://img2018.cnblogs.com/blog/856154/201910/856154-20191031002330707-1803081307.png" alt=""></p>
<p><strong>④ 服务器硬件配置推荐</strong></p>
<p>测试环境与生产环境服务器配置推荐如下,本地虚拟机的配置将按照本地测试环境的配置来创建虚拟机。</p>
<p><img src="https://img2018.cnblogs.com/blog/856154/201910/856154-20191030010523449-1281148439.png" alt=""></p>
<h3>2、操作系统初始化</h3>
<p>接下来将基于二进制包的方式,手动部署每个组件,来组成 K8S 高可用集群。通过手动部署每个组件,一步步熟悉每个组件的配置、组件之间的通信等,深层次的理解和掌握 K8S。</p>
<p>首先做的是每台服务器的配置初始化,依次按如下步骤初始化。</p>
<p>① 关闭防火墙</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)"># systemctl stop firewalld
# systemctl disable firewalld</span></pre>
</div>
<p>② 关闭 selinux</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)"># #临时生效
# setenforce </span><span style="color: rgba(128, 0, 128, 1)">0</span></pre>
</div>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)"># #永久生效
# </span><span style="color: rgba(0, 0, 255, 1)">sed</span> -i <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">s/enforcing/disabled/</span><span style="color: rgba(128, 0, 0, 1)">'</span> /etc/selinux/config</pre>
</div>
<p>③ 关闭 swap</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)"># #临时关闭
# swapoff </span>-a</pre>
</div>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)"># # 永久生效
# vim </span>/etc/fstab<br># #将 这一行注释掉</pre>
</div>
<p>④ 添加 hosts</p>
<div class="cnblogs_code">
<pre># vim /etc/<span style="color: rgba(0, 0, 0, 1)">hosts
</span><span style="color: rgba(128, 0, 128, 1)">192.168</span>.<span style="color: rgba(128, 0, 128, 1)">31.24</span> k8s-master-<span style="color: rgba(128, 0, 128, 1)">1</span>
<span style="color: rgba(128, 0, 128, 1)">192.168</span>.<span style="color: rgba(128, 0, 128, 1)">31.26</span> k8s-master-<span style="color: rgba(128, 0, 128, 1)">2</span>
<span style="color: rgba(128, 0, 128, 1)">192.168</span>.<span style="color: rgba(128, 0, 128, 1)">31.35</span> k8s-node-<span style="color: rgba(128, 0, 128, 1)">1</span>
<span style="color: rgba(128, 0, 128, 1)">192.168</span>.<span style="color: rgba(128, 0, 128, 1)">31.71</span> k8s-node-<span style="color: rgba(128, 0, 128, 1)">2</span>
<span style="color: rgba(128, 0, 128, 1)">192.168</span>.<span style="color: rgba(128, 0, 128, 1)">31.178</span> k8s-lb-<span style="color: rgba(0, 0, 0, 1)">master
</span><span style="color: rgba(128, 0, 128, 1)">192.168</span>.<span style="color: rgba(128, 0, 128, 1)">31.224</span> k8s-lb-backup</pre>
</div>
<p>⑤ 同步系统时间</p>
<p>各个节点之间需保持时间一致,因为自签证书是根据时间校验证书有效性,如果时间不一致,将校验不通过。</p>
<div class="cnblogs_code">
<pre># #联网情况可使用如下命令<br># ntpdate <span style="color: rgba(0, 0, 255, 1)">time</span>.windows.com<br><br># #如果不能联外网可使用 date 命令设置时间</pre>
</div>
<h2>五、集群搭建 —— 部署Etcd集群</h2>
<h3>1、自签证书</h3>
<p>K8S 集群安装配置过程中,会使用各种证书,目的是为了加强集群安全性。K8S 提供了基于 CA 签名的双向数字证书认证方式和简单的基于 http base 或 token 的认证方式,其中 CA 证书方式的安全性最高。每个K8S集群都有一个集群根证书颁发机构(CA),集群中的组件通常使用CA来验证API server的证书,由API服务器验证kubelet客户端证书等。</p>
<p>证书生成操作可以在master节点上执行,证书只需要创建一次,以后在向集群中添加新节点时只要将证书拷贝到新节点上,并做一定的配置即可。下面就在 <strong>k8s-master-1</strong> 节点上来创建证书,详细的介绍也可以参考官方文档:分发自签名-CA-证书</p>
<p>① K8S 证书</p>
<p>如下是 K8S 各个组件需要使用的证书</p>
<p><img src="https://img2018.cnblogs.com/blog/856154/201911/856154-20191112143250504-2083667283.png" alt=""></p>
<p>② 准备 cfssl 工具</p>
<p>我是使用 cfssl 工具来生成证书,首先下载 cfssl 工具。依次执行如下命令:</p>
<div class="cnblogs_code">
<pre># curl -L https:<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">pkg.cfssl.org/R1.2/cfssl_linux-amd64 -o /usr/local/bin/cfssl</span>
# curl -L https:<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">pkg.cfssl.org/R1.2/cfssljson_linux-amd64 -o /usr/local/bin/cfssljson</span>
# curl -L https:<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">pkg.cfssl.org/R1.2/cfssl-certinfo_linux-amd64 -o /usr/local/bin/cfssl-certinfo</span>
<span style="color: rgba(0, 0, 0, 1)">
# </span><span style="color: rgba(0, 0, 255, 1)">chmod</span> +x /usr/local/bin/cfssl*</pre>
</div>
<h3>2、自签 Etcd SSL 证书</h3>
<p>我们首先为 <strong>etcd </strong>签发一套SSL证书,通过如下命令创建几个目录,<span style="text-decoration: underline">/k8s/etcd/ssl</span> 用于存放 etcd 自签证书,<span style="text-decoration: underline">/k8s/etcd/cfg</span> 用于存放 etcd 配置文件,<span style="text-decoration: underline">/k8s/etcd/bin</span> 用于存放 etcd 执行程序。</p>
<div class="cnblogs_code">
<pre># cd /<span>
# mkdir -p /k8s/etcd/{ssl,cfg,bin}</span></pre>
</div>
<p>进入 etcd 目录:<span style="text-decoration: underline"><br></span></p>
<div class="cnblogs_code">
<pre># cd /k8s/etcd/ssl</pre>
</div>
<p>① 创建 CA 配置文件:<strong>ca-config.json</strong></p>
<p>执行如下命令创建 ca-config.json</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">cat</span> > ca-config.json <<<span style="color: rgba(0, 0, 0, 1)">EOF
{
</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">signing</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">: {
</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">default</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">: {
</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">expiry</span><span style="color: rgba(128, 0, 0, 1)">"</span>: <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">87600h</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">
},
</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">profiles</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">: {
</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">etcd</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">: {
</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">usages</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">: [
</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">signing</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">key encipherment</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">server auth</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">client auth</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">
],
</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">expiry</span><span style="color: rgba(128, 0, 0, 1)">"</span>: <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">87600h</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">
}
}
}
}
EOF</span></pre>
</div>
<p><strong>说明:</strong></p>
<ul>
<li>signing:表示该证书可用于签名其它证书;生成的 ca.pem 证书中 CA=TRUE;</li>
<li>profiles:可以定义多个 profiles,分别指定不同的过期时间、使用场景等参数;后续在签名证书时使用某个 profile;</li>
<li>expiry:证书过期时间</li>
<li>server auth:表示client可以用该 CA 对server提供的证书进行验证;</li>
<li>client auth:表示server可以用该CA对client提供的证书进行验证;</li>
</ul>
<p>② 创建 CA 证书签名请求文件:<strong>ca-csr.json</strong></p>
<p>执行如下命令创建 ca-csr.json:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">cat</span> > ca-csr.json <<<span style="color: rgba(0, 0, 0, 1)">EOF
{
</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">CN</span><span style="color: rgba(128, 0, 0, 1)">"</span>: <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">etcd</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">key</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">: {
</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">algo</span><span style="color: rgba(128, 0, 0, 1)">"</span>: <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">rsa</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">size</span><span style="color: rgba(128, 0, 0, 1)">"</span>: <span style="color: rgba(128, 0, 128, 1)">2048</span><span style="color: rgba(0, 0, 0, 1)">
},
</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">names</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">: [
{
</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">C</span><span style="color: rgba(128, 0, 0, 1)">"</span>: <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">CN</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">ST</span><span style="color: rgba(128, 0, 0, 1)">"</span>: <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Shanghai</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">L</span><span style="color: rgba(128, 0, 0, 1)">"</span>: <span style="color: rgba(128, 0, 0, 1)">"Shanghai</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">O</span><span style="color: rgba(128, 0, 0, 1)">"</span>: <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">etcd</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">OU</span><span style="color: rgba(128, 0, 0, 1)">"</span>: <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">System</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">
}
],
</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">ca</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">: {
</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">expiry</span><span style="color: rgba(128, 0, 0, 1)">"</span>: <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">87600h</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">
}
}
EOF</span></pre>
</div>
<p><strong>说明:</strong></p>
<ul>
<li>CN:Common Name,kube-apiserver 从证书中提取该字段作为请求的用户名 (User Name);浏览器使用该字段验证网站是否合法;</li>
<li>key:加密算法</li>
<li>C:国家</li>
<li>ST:地区</li>
<li>L:城市</li>
<li>O:组织,kube-apiserver 从证书中提取该字段作为请求用户所属的组 (Group);</li>
<li>OU:组织单位</li>
</ul>
<p>③ 生成 CA 证书和私钥</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)"># cfssl gencert </span>-initca ca-csr.json | cfssljson -bare ca</pre>
</div>
<p><img src="https://img2018.cnblogs.com/blog/856154/201911/856154-20191114232231356-1578817713.png" alt=""></p>
<p><strong>说明:</strong></p>
<ul>
<li><strong>ca-key.pem</strong>:CA 私钥</li>
<li><strong>ca.pem</strong>:CA 数字证书</li>
</ul>
<p>④ 创建证书签名请求文件:<strong>etcd-csr.json</strong></p>
<p>执行如下命令创建 etcd-csr.json:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">cat</span> > etcd-csr.json <<<span style="color: rgba(0, 0, 0, 1)">EOF
{
</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">CN</span><span style="color: rgba(128, 0, 0, 1)">"</span>: <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">etcd</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">hosts</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">: [
</span><span style="color: rgba(128, 0, 0, 1)">"192.168.31.24</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(128, 0, 0, 1)">"192.168.31.35</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(128, 0, 0, 1)">"192.168.31.71</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">
],
</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">key</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">: {
</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">algo</span><span style="color: rgba(128, 0, 0, 1)">"</span>: <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">rsa</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">size</span><span style="color: rgba(128, 0, 0, 1)">"</span>: <span style="color: rgba(128, 0, 128, 1)">2048</span><span style="color: rgba(0, 0, 0, 1)">
},
</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">names</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">: [
{
</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">C</span><span style="color: rgba(128, 0, 0, 1)">"</span>: <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">CN</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">ST</span><span style="color: rgba(128, 0, 0, 1)">"</span>: <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">BeiJing</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">L</span><span style="color: rgba(128, 0, 0, 1)">"</span>: <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">BeiJing</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">O</span><span style="color: rgba(128, 0, 0, 1)">"</span>: <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">etcd</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">OU</span><span style="color: rgba(128, 0, 0, 1)">"</span>: <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">System</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">
}
]
}
EOF</span></pre>
</div>
<p><strong>说明:</strong></p>
<ul>
<li><strong>hosts</strong>:需要指定授权使用该证书的 IP 或域名列表,这里配置所有 etcd 的IP地址。</li>
<li><strong>key</strong>:加密算法及长度</li>
</ul>
<p>⑤ 为 etcd 生成证书和私钥</p>
<div class="cnblogs_code">
<pre>cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=etcd etcd-csr.json | cfssljson -bare etcd</pre>
</div>
<p><strong>说明:</strong></p>
<ul>
<li>-ca:指定 CA 数字证书</li>
<li>-ca-key:指定 CA 私钥</li>
<li>-config:CA 配置文件</li>
<li>-profile:指定环境</li>
<li>-bare:指定证书名前缀</li>
</ul>
<p><img src="https://img2018.cnblogs.com/blog/856154/201911/856154-20191114232316392-395705331.png" alt=""></p>
<p><strong>说明:</strong></p>
<ul>
<li><strong>ca-key.pem</strong>:etcd 私钥</li>
<li><strong>ca.pem</strong>:etcd 数字证书</li>
</ul>
<p>证书生成完成,后面部署 Etcd 时主要会用到如下几个证书:</p>
<p><img src="https://img2018.cnblogs.com/blog/856154/201911/856154-20191114232356282-2007896330.png" alt=""></p>
<h3>3、Etcd 数据库集群部署</h3>
<p>etcd 集群采用主从架构模式(一主多从)部署,集群通过选举产生 leader,因此需要部署奇数个节点(3/5/7)才能正常工作。etcd使用raft一致性算法保证每个节点的一致性。</p>
<p><img src="https://img2018.cnblogs.com/blog/856154/201912/856154-20191203223130198-1515895263.png" alt=""></p>
<p>① 下载 etcd</p>
<p>从 github 上下载合适版本的 etcd</p>
<div class="cnblogs_code">
<pre># cd /k8s/<span style="color: rgba(0, 0, 0, 1)">etcd
# </span><span style="color: rgba(0, 0, 255, 1)">wget</span> https:<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">github.com/etcd-io/etcd/releases/download/v3.2.28/etcd-v3.2.28-linux-amd64.tar.gz</span></pre>
</div>
<p>解压:</p>
<div class="cnblogs_code">
<pre># <span style="color: rgba(0, 0, 255, 1)">tar</span> zxf etcd-v3.<span style="color: rgba(128, 0, 128, 1)">2.28</span>-linux-amd64.<span style="color: rgba(0, 0, 255, 1)">tar</span>.gz</pre>
</div>
<p>将 etcd 复制到 /usr/local/bin 下:</p>
<div class="cnblogs_code">
<pre># <span style="color: rgba(0, 0, 255, 1)">cp</span> etcd-v3.<span style="color: rgba(128, 0, 128, 1)">2.28</span>-linux-amd64/{etcd,etcdctl} /k8s/etcd/<span style="color: rgba(0, 0, 0, 1)">bin
# </span><span style="color: rgba(0, 0, 255, 1)">rm</span> -rf etcd-v3.<span style="color: rgba(128, 0, 128, 1)">2.28</span>-linux-amd64*</pre>
</div>
<p>② 创建 etcd 配置文件:<strong>etcd.conf</strong></p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">cat</span> > /k8s/etcd/cfg/etcd.conf <<<span style="color: rgba(0, 0, 0, 1)">EOF
#
ETCD_NAME</span>=etcd-<span style="color: rgba(128, 0, 128, 1)">1</span><span style="color: rgba(0, 0, 0, 1)">
ETCD_DATA_DIR</span>=/k8s/data/<span style="color: rgba(0, 0, 0, 1)">default.etcd
ETCD_LISTEN_PEER_URLS</span>=https:<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">192.168.31.24:2380</span>
ETCD_LISTEN_CLIENT_URLS=https:<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">192.168.31.24:2379</span>
<span style="color: rgba(0, 0, 0, 1)">
#
ETCD_INITIAL_ADVERTISE_PEER_URLS</span>=https:<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">192.168.31.24:2380</span>
ETCD_ADVERTISE_CLIENT_URLS=https:<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">192.168.31.24:2379</span>
ETCD_INITIAL_CLUSTER=etcd-<span style="color: rgba(128, 0, 128, 1)">1</span>=https:<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">192.168.31.24:2380,etcd-2=</span><span style="color: rgba(0, 128, 0, 1); text-decoration: underline">https://192.168.31.35</span><span style="color: rgba(0, 128, 0, 1)">:2380,etcd-3=</span><span style="color: rgba(0, 128, 0, 1); text-decoration: underline">https://192.168.31.71</span><span style="color: rgba(0, 128, 0, 1)">:2380</span>
ETCD_INITIAL_CLUSTER_TOKEN=etcd-<span style="color: rgba(0, 0, 0, 1)">cluster
ETCD_INITIAL_CLUSTER_STATE</span>=<span style="color: rgba(0, 0, 0, 1)">new
#
ETCD_CERT_FILE</span>=/k8s/etcd/ssl/<span style="color: rgba(0, 0, 0, 1)">etcd.pem
ETCD_KEY_FILE</span>=/k8s/etcd/ssl/etcd-<span style="color: rgba(0, 0, 0, 1)">key.pem
ETCD_TRUSTED_CA_FILE</span>=/k8s/etcd/ssl/<span style="color: rgba(0, 0, 0, 1)">ca.pem
ETCD_PEER_CERT_FILE</span>=/k8s/etcd/ssl/<span style="color: rgba(0, 0, 0, 1)">etcd.pem
ETCD_PEER_KEY_FILE</span>=/k8s/etcd/ssl/etcd-<span style="color: rgba(0, 0, 0, 1)">key.pem
ETCD_PEER_TRUSTED_CA_FILE</span>=/k8s/etcd/ssl/<span style="color: rgba(0, 0, 0, 1)">ca.pem
EOF</span></pre>
</div>
<p><strong>说明:</strong></p>
<ul>
<li>ETCD_NAME:etcd在集群中的唯一名称</li>
<li>ETCD_DATA_DIR:etcd数据存放目录</li>
<li>ETCD_LISTEN_PEER_URLS:etcd集群间通讯的地址,设置为本机IP</li>
<li>ETCD_LISTEN_CLIENT_URLS:客户端访问的地址,设置为本机IP</li>
<li></li>
<li>ETCD_INITIAL_ADVERTISE_PEER_URLS:初始集群通告地址,集群内部通讯地址,设置为本机IP</li>
<li>ETCD_ADVERTISE_CLIENT_URLS:客户端通告地址,设置为本机IP</li>
<li>ETCD_INITIAL_CLUSTER:集群节点地址,以 key=value 的形式添加各个 etcd 的地址</li>
<li>ETCD_INITIAL_CLUSTER_TOKEN:集群令牌,用于集群间做简单的认证</li>
<li>ETCD_INITIAL_CLUSTER_STATE:集群状态</li>
<li></li>
<li>ETCD_CERT_FILE:客户端 etcd 数字证书路径</li>
<li>ETCD_KEY_FILE:客户端 etcd 私钥路径</li>
<li>ETCD_TRUSTED_CA_FILE:客户端 CA 证书路径</li>
<li>ETCD_PEER_CERT_FILE:集群间通讯etcd数字证书路径</li>
<li>ETCD_PEER_KEY_FILE:集群间通讯etcd私钥路径</li>
<li>ETCD_PEER_TRUSTED_CA_FILE:集群间通讯CA证书路径</li>
</ul>
<p>③ 创建 etcd 服务:<strong>etcd.service</strong></p>
<p>通过EnvironmentFile指定 <strong>etcd.conf</strong> 作为环境配置文件</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">cat</span> > /k8s/etcd/etcd.service <<<span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">EOF</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">
Description</span>=<span style="color: rgba(0, 0, 0, 1)">Etcd Server
After</span>=<span style="color: rgba(0, 0, 0, 1)">network.target
After</span>=network-<span style="color: rgba(0, 0, 0, 1)">online.target
Wants</span>=network-<span style="color: rgba(0, 0, 0, 1)">online.target
Type</span>=<span style="color: rgba(0, 0, 0, 1)">notify
EnvironmentFile</span>=/k8s/etcd/cfg/<span style="color: rgba(0, 0, 0, 1)">etcd.conf
WorkingDirectory</span>=<span style="color: rgba(0, 0, 0, 1)">${ETCD_DATA_DIR}
ExecStart</span>=/k8s/etcd/bin/<span style="color: rgba(0, 0, 0, 1)">etcd \
</span>--name=<span style="color: rgba(0, 0, 0, 1)">${ETCD_NAME} \
</span>--data-<span style="color: rgba(0, 0, 255, 1)">dir</span>=<span style="color: rgba(0, 0, 0, 1)">${ETCD_DATA_DIR} \
</span>--listen-peer-urls=<span style="color: rgba(0, 0, 0, 1)">${ETCD_LISTEN_PEER_URLS} \
</span>--listen-client-urls=${ETCD_LISTEN_CLIENT_URLS},http:<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">127.0.0.1:2379 \</span>
--initial-advertise-peer-urls=<span style="color: rgba(0, 0, 0, 1)">${ETCD_INITIAL_ADVERTISE_PEER_URLS} \
</span>--advertise-client-urls=<span style="color: rgba(0, 0, 0, 1)">${ETCD_ADVERTISE_CLIENT_URLS} \
</span>--initial-cluster=<span style="color: rgba(0, 0, 0, 1)">${ETCD_INITIAL_CLUSTER} \
</span>--initial-cluster-token=<span style="color: rgba(0, 0, 0, 1)">${ETCD_INITIAL_CLUSTER_TOKEN} \
</span>--initial-cluster-state=<span style="color: rgba(0, 0, 0, 1)">${ETCD_INITIAL_CLUSTER_STATE} \
</span>--cert-<span style="color: rgba(0, 0, 255, 1)">file</span>=<span style="color: rgba(0, 0, 0, 1)">${ETCD_CERT_FILE} \
</span>--key-<span style="color: rgba(0, 0, 255, 1)">file</span>=<span style="color: rgba(0, 0, 0, 1)">${ETCD_KEY_FILE} \
</span>--trusted-ca-<span style="color: rgba(0, 0, 255, 1)">file</span>=<span style="color: rgba(0, 0, 0, 1)">${ETCD_TRUSTED_CA_FILE} \
</span>--peer-cert-<span style="color: rgba(0, 0, 255, 1)">file</span>=<span style="color: rgba(0, 0, 0, 1)">${ETCD_PEER_CERT_FILE} \
</span>--peer-key-<span style="color: rgba(0, 0, 255, 1)">file</span>=<span style="color: rgba(0, 0, 0, 1)">${ETCD_PEER_KEY_FILE} \
</span>--peer-trusted-ca-<span style="color: rgba(0, 0, 255, 1)">file</span>=<span style="color: rgba(0, 0, 0, 1)">${ETCD_PEER_TRUSTED_CA_FILE}
Restart</span>=on-<span style="color: rgba(0, 0, 0, 1)">failure
LimitNOFILE</span>=<span style="color: rgba(128, 0, 128, 1)">65536</span><span style="color: rgba(0, 0, 0, 1)">
WantedBy</span>=multi-<span style="color: rgba(0, 0, 0, 1)">user.target
EOF</span></pre>
</div>
<p>etcd.service 更多的配置以及说明可以通过如下命令查看:</p>
<div class="cnblogs_code">
<pre># /k8s/etcd/bin/etcd --help</pre>
</div>
<p>④ 将 etcd 目录拷贝到另外两个节点</p>
<div class="cnblogs_code">
<pre># <span style="color: rgba(0, 0, 255, 1)">scp</span> -r /k8s root@k8s-node-<span style="color: rgba(128, 0, 128, 1)">1</span>:/k8s<span style="color: rgba(0, 0, 0, 1)">
# </span><span style="color: rgba(0, 0, 255, 1)">scp</span> -r /k8s root@k8s-node-<span style="color: rgba(128, 0, 128, 1)">2</span>:/k8s</pre>
</div>
<p><img src="https://img2018.cnblogs.com/blog/856154/201911/856154-20191115222519900-1199048557.png" alt=""></p>
<p>⑤ 修改两个节点配置文件</p>
<p>修改 k8s-node-1 节点的 /k8s/etcd/cfg/etcd.conf:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">#
ETCD_NAME</span>=etcd-<span style="color: rgba(128, 0, 128, 1)">2</span><span style="color: rgba(0, 0, 0, 1)">
ETCD_LISTEN_PEER_URLS</span>=https:<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">192.168.31.35:2380</span>
ETCD_LISTEN_CLIENT_URLS=https:<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">192.168.31.35:2379</span>
<span style="color: rgba(0, 0, 0, 1)">
#
ETCD_INITIAL_ADVERTISE_PEER_URLS</span>=https:<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">192.168.31.35:2380</span>
ETCD_ADVERTISE_CLIENT_URLS=https:<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">192.168.31.35:2379</span></pre>
</div>
<p>修改 k8s-node-2 节点的 /k8s/etcd/cfg/etcd.conf:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">#
ETCD_NAME</span>=etcd-<span style="color: rgba(128, 0, 128, 1)">2</span><span style="color: rgba(0, 0, 0, 1)">
ETCD_LISTEN_PEER_URLS</span>=https:<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">192.168.31.71:2380</span>
ETCD_LISTEN_CLIENT_URLS=https:<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">192.168.31.71:2379</span>
<span style="color: rgba(0, 0, 0, 1)">
#
ETCD_INITIAL_ADVERTISE_PEER_URLS</span>=https:<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">192.168.31.71:2380</span>
ETCD_ADVERTISE_CLIENT_URLS=https:<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">192.168.31.71:2379</span></pre>
</div>
<p>⑥ 启动 etcd 服务</p>
<p>首先在三个节点将 <strong>etcd.service</strong> 拷贝到 /usr/lib/systemd/system/ 下</p>
<div class="cnblogs_code">
<pre># <span style="color: rgba(0, 0, 255, 1)">cp</span> /k8s/etcd/etcd.service /usr/lib/systemd/system/<span style="color: rgba(0, 0, 0, 1)">
# systemctl daemon</span>-reload</pre>
</div>
<p>在三个节点启动 etcd 服务</p>
<div class="cnblogs_code">
<pre># systemctl start etcd</pre>
</div>
<p>设置开机启动</p>
<div class="cnblogs_code">
<pre># systemctl enable etcd</pre>
</div>
<p>查看 etcd 的日志</p>
<div class="cnblogs_code">
<pre># <span style="color: rgba(0, 0, 255, 1)">tail</span> -f /var/log/messages</pre>
</div>
<p><img src="https://img2018.cnblogs.com/blog/856154/201911/856154-20191115232442732-2092355582.png" alt=""></p>
<p>注意:如果日志中出现连接异常信息,请确认所有节点防火墙是否开放2379,2380端口,或者直接关闭防火墙。</p>
<p>查看 etcd 集群状态</p>
<div class="cnblogs_code">
<pre>/k8s/etcd/bin/<span style="color: rgba(0, 0, 0, 1)">etcdctl \
</span>--ca-<span style="color: rgba(0, 0, 255, 1)">file</span>=/k8s/etcd/ssl/<span style="color: rgba(0, 0, 0, 1)">ca.pem \
</span>--cert-<span style="color: rgba(0, 0, 255, 1)">file</span>=/k8s/etcd/ssl/<span style="color: rgba(0, 0, 0, 1)">etcd.pem \
</span>--key-<span style="color: rgba(0, 0, 255, 1)">file</span>=/k8s/etcd/ssl/etcd-<span style="color: rgba(0, 0, 0, 1)">key.pem \
</span>--endpoints=https:<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">192.168.31.24:2379,</span><span style="color: rgba(0, 128, 0, 1); text-decoration: underline">https://192.168.31.35</span><span style="color: rgba(0, 128, 0, 1)">:2379,</span><span style="color: rgba(0, 128, 0, 1); text-decoration: underline">https://192.168.31.71</span><span style="color: rgba(0, 128, 0, 1)">:2379 \</span>
cluster-health</pre>
</div>
<p><img src="https://img2018.cnblogs.com/blog/856154/201911/856154-20191115235034288-1984020155.png" alt=""></p>
<h2>六、集群搭建 —— 部署Master组件</h2>
<h3>1、自签 ApiServer SSL 证书</h3>
<p>K8S 集群中所有资源的访问和变更都是通过 kube-apiserver 的 REST API 来实现的,首先在 master 节点上部署 kube-apiserver 组件。</p>
<p>我们首先为 <strong>apiserver </strong>签发一套SSL证书,过程与 etcd 自签SSL证书类似。通过如下命令创建几个目录,ssl 用于存放自签证书,cfg 用于存放配置文件,bin 用于存放执行程序,logs 存放日志文件。</p>
<div class="cnblogs_code">
<pre># cd /
# mkdir -p /k8s/kubernetes/{ssl,cfg,bin,logs}</pre>
</div>
<p>进入 kubernetes 目录:</p>
<div class="cnblogs_code">
<pre># cd /k8s/kubernetes/ssl</pre>
</div>
<p>① 创建 CA 配置文件:<strong>ca-config.json</strong></p>
<p>执行如下命令创建 ca-config.json</p>
<div class="cnblogs_code">
<pre>cat > ca-config.json <<<span>EOF
{
"signing"<span>: {
"default"<span>: {
"expiry": "87600h"<span>
},
"profiles"<span>: {
"kubernetes"<span>: {
"usages"<span>: [
"signing"<span>,
"key encipherment"<span>,
"server auth"<span>,
"client auth"<span>
],
"expiry": "87600h"<span>
}
}
}
}
EOF</span></span></span></span></span></span></span></span></span></span></span></span></pre>
</div>
<p>② 创建 CA 证书签名请求文件:<strong>ca-csr.json</strong></p>
<p>执行如下命令创建 ca-csr.json:</p>
<div class="cnblogs_code">
<pre>cat > ca-csr.json <<<span>EOF
{
"CN": "kubernetes"<span>,
"key"<span>: {
"algo": "rsa"<span>,
"size": 2048<span>
},
"names"<span>: [
{
"C": "CN"<span>,
"ST": "Shanghai"<span>,
"L": "Shanghai"<span>,
"O": "kubernetes"<span>,
"OU": "System"<span>
}
],
"ca"<span>: {
"expiry": "87600h"<span>
}
}
EOF</span></span></span></span></span></span></span></span></span></span></span></span></span></pre>
</div>
<p>③ 生成 CA 证书和私钥</p>
<div class="cnblogs_code">
<pre><span># cfssl gencert -initca ca-csr.json | cfssljson -bare ca</span></pre>
</div>
<p>④ 创建证书签名请求文件:<strong>kubernetes-csr.json</strong></p>
<p>执行如下命令创建 kubernetes-csr.json:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">cat</span> > kubernetes-csr.json <<<span style="color: rgba(0, 0, 0, 1)">EOF
{
</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">CN</span><span style="color: rgba(128, 0, 0, 1)">"</span>: <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">kubernetes</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">hosts</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">: [
</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">127.0.0.1</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">10.0.0.1</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">192.168.31.24</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">192.168.31.26</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">192.168.31.35</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">192.168.31.71</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">192.168.31.26</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">192.168.31.26</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">kubernetes</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">kubernetes.default</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">kubernetes.default.svc</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">kubernetes.default.svc.cluster</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">kubernetes.default.svc.cluster.local</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">
],
</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">key</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">: {
</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">algo</span><span style="color: rgba(128, 0, 0, 1)">"</span>: <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">rsa</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">size</span><span style="color: rgba(128, 0, 0, 1)">"</span>: <span style="color: rgba(128, 0, 128, 1)">2048</span><span style="color: rgba(0, 0, 0, 1)">
},
</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">names</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">: [
{
</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">C</span><span style="color: rgba(128, 0, 0, 1)">"</span>: <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">CN</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">ST</span><span style="color: rgba(128, 0, 0, 1)">"</span>: <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">BeiJing</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">L</span><span style="color: rgba(128, 0, 0, 1)">"</span>: <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">BeiJing</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">O</span><span style="color: rgba(128, 0, 0, 1)">"</span>: <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">kubernetes</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">OU</span><span style="color: rgba(128, 0, 0, 1)">"</span>: <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">System</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">
}
]
}
EOF</span></pre>
</div>
<p><strong>说明:</strong></p>
<ul>
<li><strong>hosts</strong>:指定会直接访问 apiserver 的IP列表,一般需指定 etcd 集群、kubernetes master 集群的主机 IP 和 kubernetes 服务的服务 IP,Node 的IP一般不需要加入。</li>
</ul>
<p>⑤ 为 kubernetes 生成证书和私钥</p>
<div class="cnblogs_code">
<pre>cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=kubernetes kubernetes-csr.json | cfssljson -bare kubernetes</pre>
</div>
<h3>2、部署 kube-apiserver 组件</h3>
<p>① 下载二进制包</p>
<p>通过 kubernetes Github 下载安装用的二进制包,我这里使用 <strong>v1.16.2</strong> 版本,server 二进制包已经包含了 master、node 上的各个组件,下载 server 二进制包即可。</p>
<p>考虑到网络问题,可以从百度网盘下载已经准备好的二进制包:链接: 下载离线安装包。</p>
<p>将下载好的 kubernetes-v1.16.2-server-linux-amd64.tar.gz 上传到 /usr/local/src下,并解压:</p>
<div class="cnblogs_code">
<pre># <span style="color: rgba(0, 0, 255, 1)">tar</span> -zxf kubernetes-v1.<span style="color: rgba(128, 0, 128, 1)">16.2</span>-server-linux-amd64.<span style="color: rgba(0, 0, 255, 1)">tar</span>.gz</pre>
</div>
<p>先将 master 节点上部署的组件拷贝到 /k8s/kubernetes/bin 目录下:</p>
<div class="cnblogs_code">
<pre># <span style="color: rgba(0, 0, 255, 1)">cp</span> -p /usr/local/src/kubernetes/server/bin/{kube-apiserver,kube-controller-manager,kube-scheduler} /k8s/kubernetes/bin/<span style="color: rgba(0, 0, 0, 1)">
# </span><span style="color: rgba(0, 0, 255, 1)">cp</span> -p /usr/local/src/kubernetes/server/bin/kubectl /usr/local/bin/</pre>
</div>
<p>② 创建 Node 令牌文件:<strong>token.csv</strong></p>
<p>Master apiserver 启用 TLS 认证后,Node节点 kubelet 组件想要加入集群,必须使用CA签发的有效证书才能与apiserver通信,当Node节点很多时,签署证书是一件很繁琐的事情,因此有了 TLS Bootstrap 机制,kubelet 会以一个低权限用户自动向 apiserver 申请证书,kubelet 的证书由 apiserver 动态签署。因此先为 apiserver 生成一个令牌文件,令牌之后会在 Node 中用到。</p>
<p>生成 token,一个随机字符串,可使用如下命令生成 token:apiserver 配置的 token 必须与 Node 节点 bootstrap.kubeconfig 配置保持一致。</p>
<div class="cnblogs_code">
<pre># <span style="color: rgba(0, 0, 255, 1)">head</span> -c <span style="color: rgba(128, 0, 128, 1)">16</span> /dev/urandom | od -An -t x | <span style="color: rgba(0, 0, 255, 1)">tr</span> -d <span style="color: rgba(128, 0, 0, 1)">'</span> <span style="color: rgba(128, 0, 0, 1)">'</span></pre>
</div>
<p><img src="https://img2018.cnblogs.com/blog/856154/201911/856154-20191120142640146-1408433933.png" alt=""></p>
<p>创建 token.csv,<span style="text-decoration: underline">格式:token,用户,UID,用户组</span></p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">cat</span> > /k8s/kubernetes/cfg/token.csv <<<span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">EOF</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">
bfa3cb7f6f21f87e5c0e5f25e6cfedad,kubelet</span>-bootstrap,<span style="color: rgba(128, 0, 128, 1)">10001</span>,<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">system:node-bootstrapper</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">
EOF</span></pre>
</div>
<p>③ 创建 kube-apiserver 配置文件:<strong>kube-apiserver.conf</strong></p>
<p>kube-apiserver 有很多配置项,可以参考官方文档查看每个配置项的用途:kube-apiserver</p>
<p>注意:踩的一个坑,“\” 后面不要有空格,不要有多余的换行,否则启动失败。</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">cat</span> > /k8s/kubernetes/cfg/kube-apiserver.conf <<<span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">EOF</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">
KUBE_APISERVER_OPTS</span>=<span style="color: rgba(128, 0, 0, 1)">"</span>--etcd-servers=https:<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">192.168.31.24:2379,</span><span style="color: rgba(0, 128, 0, 1); text-decoration: underline">https://192.168.31.35</span><span style="color: rgba(0, 128, 0, 1)">:2379,</span><span style="color: rgba(0, 128, 0, 1); text-decoration: underline">https://192.168.31.71</span><span style="color: rgba(0, 128, 0, 1)">:2379 \</span>
--bind-address=<span style="color: rgba(128, 0, 128, 1)">192.168</span>.<span style="color: rgba(128, 0, 128, 1)">31.24</span><span style="color: rgba(0, 0, 0, 1)"> \
</span>--secure-port=<span style="color: rgba(128, 0, 128, 1)">6443</span><span style="color: rgba(0, 0, 0, 1)"> \
</span>--advertise-address=<span style="color: rgba(128, 0, 128, 1)">192.168</span>.<span style="color: rgba(128, 0, 128, 1)">31.24</span><span style="color: rgba(0, 0, 0, 1)"> \
</span>--allow-privileged=<span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)"> \
</span>--service-cluster-ip-range=<span style="color: rgba(128, 0, 128, 1)">10.0</span>.<span style="color: rgba(128, 0, 128, 1)">0.0</span>/<span style="color: rgba(128, 0, 128, 1)">24</span><span style="color: rgba(0, 0, 0, 1)"> \
</span>--service-node-port-range=<span style="color: rgba(128, 0, 128, 1)">30000</span>-<span style="color: rgba(128, 0, 128, 1)">32767</span><span style="color: rgba(0, 0, 0, 1)"> \
</span>--enable-admission-plugins=<span style="color: rgba(0, 0, 0, 1)">NamespaceLifecycle,LimitRanger,ServiceAccount,ResourceQuota,NodeRestriction \
</span>--authorization-mode=<span style="color: rgba(0, 0, 0, 1)">RBAC,Node \
</span>--enable-bootstrap-token-auth=<span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)"> \
</span>--token-auth-<span style="color: rgba(0, 0, 255, 1)">file</span>=/k8s/kubernetes/cfg/<span style="color: rgba(0, 0, 0, 1)">token.csv \
</span>--kubelet-client-certificate=/k8s/kubernetes/ssl/<span style="color: rgba(0, 0, 0, 1)">kubernetes.pem \
</span>--kubelet-client-key=/k8s/kubernetes/ssl/kubernetes-<span style="color: rgba(0, 0, 0, 1)">key.pem \
</span>--tls-cert-<span style="color: rgba(0, 0, 255, 1)">file</span>=/k8s/kubernetes/ssl/<span style="color: rgba(0, 0, 0, 1)">kubernetes.pem \
</span>--tls-private-key-<span style="color: rgba(0, 0, 255, 1)">file</span>=/k8s/kubernetes/ssl/kubernetes-<span style="color: rgba(0, 0, 0, 1)">key.pem \
</span>--client-ca-<span style="color: rgba(0, 0, 255, 1)">file</span>=/k8s/kubernetes/ssl/<span style="color: rgba(0, 0, 0, 1)">ca.pem \
</span>--service-account-key-<span style="color: rgba(0, 0, 255, 1)">file</span>=/k8s/kubernetes/ssl/ca-<span style="color: rgba(0, 0, 0, 1)">key.pem \
</span>--etcd-cafile=/k8s/etcd/ssl/<span style="color: rgba(0, 0, 0, 1)">ca.pem \
</span>--etcd-certfile=/k8s/etcd/ssl/<span style="color: rgba(0, 0, 0, 1)">etcd.pem \
</span>--etcd-keyfile=/k8s/etcd/ssl/etcd-<span style="color: rgba(0, 0, 0, 1)">key.pem \
</span>--v=<span style="color: rgba(128, 0, 128, 1)">2</span><span style="color: rgba(0, 0, 0, 1)"> \
</span>--logtostderr=<span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)"> \
</span>--log-<span style="color: rgba(0, 0, 255, 1)">dir</span>=/k8s/kubernetes/<span style="color: rgba(0, 0, 0, 1)">logs \
</span>--audit-log-maxage=<span style="color: rgba(128, 0, 128, 1)">30</span><span style="color: rgba(0, 0, 0, 1)"> \
</span>--audit-log-maxbackup=<span style="color: rgba(128, 0, 128, 1)">3</span><span style="color: rgba(0, 0, 0, 1)"> \
</span>--audit-log-maxsize=<span style="color: rgba(128, 0, 128, 1)">100</span><span style="color: rgba(0, 0, 0, 1)"> \
</span>--audit-log-path=/k8s/kubernetes/logs/k8s-<span style="color: rgba(0, 0, 0, 1)">audit.log</span><span style="color: rgba(128, 0, 0, 1)">"
</span>EOF</pre>
</div>
<p><strong>重点配置说明:</strong></p>
<ul>
<li>--etcd-servers:etcd 集群地址</li>
<li>--bind-address:apiserver 监听的地址,一般配主机IP</li>
<li>--secure-port:监听的端口</li>
<li>--advertise-address:集群通告地址,其它Node节点通过这个地址连接 apiserver,不配置则使用 --bind-address</li>
<li>--service-cluster-ip-range:Service 的 虚拟IP范围,以CIDR格式标识,该IP范围不能与物理机的真实IP段有重合。</li>
<li>--service-node-port-range:Service 可映射的物理机端口范围,默认30000-32767</li>
<li>--admission-control:集群的准入控制设置,各控制模块以插件的形式依次生效,启用RBAC授权和节点自管理</li>
<li>--authorization-mode:授权模式,包括:AlwaysAllow,AlwaysDeny,ABAC(基于属性的访问控制),Webhook,RBAC(基于角色的访问控制),Node(专门授权由 kubelet 发出的API请求)。(默认值"AlwaysAllow")。</li>
<li>--enable-bootstrap-token-auth:启用TLS bootstrap功能</li>
<li>--token-auth-file:这个文件将被用于通过令牌认证来保护API服务的安全端口。</li>
<li>--v:指定日志级别,0~8,越大日志越详细</li>
</ul>
<p>④ 创建 apiserver 服务:<strong>kube-apiserver.service</strong></p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">cat</span> > /usr/lib/systemd/system/kube-apiserver.service <<<span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">EOF</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">
Description</span>=<span style="color: rgba(0, 0, 0, 1)">Kubernetes API Server
Documentation</span>=https:<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">github.com/GoogleCloudPlatform/kubernetes</span>
After=<span style="color: rgba(0, 0, 0, 1)">network.target
</span><span style="color: rgba(0, 0, 0, 1)">
EnvironmentFile</span>=-/k8s/kubernetes/cfg/kube-apiserver.conf<span style="color: rgba(0, 0, 0, 1)">
ExecStart</span>=/k8s/kubernetes/bin/kube-<span style="color: rgba(0, 0, 0, 1)">apiserver $KUBE_APISERVER_OPTS<br>
Restart</span>=on-<span style="color: rgba(0, 0, 0, 1)">failure</span><span style="color: rgba(0, 0, 0, 1)">
LimitNOFILE</span>=<span style="color: rgba(128, 0, 128, 1)">65536</span><span style="color: rgba(0, 0, 0, 1)">
WantedBy</span>=multi-<span style="color: rgba(0, 0, 0, 1)">user.target
EOF</span></pre>
</div>
<p>⑤ 启动 kube-apiserver 组件</p>
<p>启动组件</p>
<div class="cnblogs_code">
<pre># systemctl daemon-<span style="color: rgba(0, 0, 0, 1)">reload
# systemctl start kube</span>-<span style="color: rgba(0, 0, 0, 1)">apiserver
# systemctl enable kube</span>-apiserver</pre>
</div>
<p>检查启动状态</p>
<div class="cnblogs_code">
<pre># systemctl status kube-apiserver.service</pre>
</div>
<p>查看启动日志</p>
<div class="cnblogs_code">
<pre># <span style="color: rgba(0, 0, 255, 1)">less</span> /k8s/kubernetes/logs/kube-apiserver.INFO</pre>
</div>
<p>⑥ 将 kubelet-bootstrap 用户绑定到系统集群角色,之后便于 Node 使用token请求证书</p>
<div class="cnblogs_code">
<pre>kubectl create clusterrolebinding kubelet-<span style="color: rgba(0, 0, 0, 1)">bootstrap \
</span>--clusterrole=system:node-<span style="color: rgba(0, 0, 0, 1)">bootstrapper \
</span>--user=kubelet-bootstrap</pre>
</div>
<h3>3、部署 kube-controller-manager 组件</h3>
<p>① 创建 kube-controller-manager 配置文件:<strong>kube-controller-manager.conf</strong></p>
<p>详细的配置可参考官方文档:kube-controller-manager</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">cat</span> > /k8s/kubernetes/cfg/kube-controller-manager.conf <<<span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">EOF</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">
KUBE_CONTROLLER_MANAGER_OPTS</span>=<span style="color: rgba(128, 0, 0, 1)">"</span>--leader-elect=<span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)"> \
</span>--master=<span style="color: rgba(128, 0, 128, 1)">127.0</span>.<span style="color: rgba(128, 0, 128, 1)">0.1</span>:<span style="color: rgba(128, 0, 128, 1)">8080</span><span style="color: rgba(0, 0, 0, 1)"> \
</span>--address=<span style="color: rgba(128, 0, 128, 1)">127.0</span>.<span style="color: rgba(128, 0, 128, 1)">0.1</span><span style="color: rgba(0, 0, 0, 1)"> \
</span>--allocate-node-cidrs=<span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)"> \
</span>--cluster-cidr=<span style="color: rgba(128, 0, 128, 1)">10.244</span>.<span style="color: rgba(128, 0, 128, 1)">0.0</span>/<span style="color: rgba(128, 0, 128, 1)">16</span><span style="color: rgba(0, 0, 0, 1)"> \
</span>--service-cluster-ip-range=<span style="color: rgba(128, 0, 128, 1)">10.0</span>.<span style="color: rgba(128, 0, 128, 1)">0.0</span>/<span style="color: rgba(128, 0, 128, 1)">24</span><span style="color: rgba(0, 0, 0, 1)"> \
</span>--cluster-signing-cert-<span style="color: rgba(0, 0, 255, 1)">file</span>=/k8s/kubernetes/ssl/<span style="color: rgba(0, 0, 0, 1)">ca.pem \
</span>--cluster-signing-key-<span style="color: rgba(0, 0, 255, 1)">file</span>=/k8s/kubernetes/ssl/ca-<span style="color: rgba(0, 0, 0, 1)">key.pem \
</span>--root-ca-<span style="color: rgba(0, 0, 255, 1)">file</span>=/k8s/kubernetes/ssl/<span style="color: rgba(0, 0, 0, 1)">ca.pem \
</span>--service-account-private-key-<span style="color: rgba(0, 0, 255, 1)">file</span>=/k8s/kubernetes/ssl/ca-<span style="color: rgba(0, 0, 0, 1)">key.pem \
</span>--experimental-cluster-signing-duration=<span style="color: rgba(0, 0, 0, 1)">87600h0m0s \
</span>--v=<span style="color: rgba(128, 0, 128, 1)">2</span><span style="color: rgba(0, 0, 0, 1)"> \
</span>--logtostderr=<span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)"> \
</span>--log-<span style="color: rgba(0, 0, 255, 1)">dir</span>=/k8s/kubernetes/<span style="color: rgba(0, 0, 0, 1)">logs</span><span style="color: rgba(128, 0, 0, 1)">"
</span>EOF</pre>
</div>
<p><strong>重点配置说明:</strong></p>
<ul>
<li>--leader-elect:当该组件启动多个时,自动选举,默认true</li>
<li>--master:连接本地apiserver,apiserver 默认会监听本地8080端口</li>
<li>--allocate-node-cidrs:是否分配和设置Pod的CDIR</li>
<li>--service-cluster-ip-range:Service 集群IP段</li>
</ul>
<p>② 创建 kube-controller-manager 服务:<strong>kube-controller-manager.service</strong></p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">cat</span> > /usr/lib/systemd/system/kube-controller-manager.service <<<span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">EOF</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">
Description</span>=<span style="color: rgba(0, 0, 0, 1)">Kubernetes Controller Manager
Documentation</span>=https:<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">github.com/GoogleCloudPlatform/kubernetes</span>
After=<span style="color: rgba(0, 0, 0, 1)">network.target
</span><span style="color: rgba(0, 0, 0, 1)">
EnvironmentFile</span>=/k8s/kubernetes/cfg/kube-controller-<span style="color: rgba(0, 0, 0, 1)">manager.conf
ExecStart</span>=/k8s/kubernetes/bin/kube-controller-<span style="color: rgba(0, 0, 0, 1)">manager $KUBE_CONTROLLER_MANAGER_OPTS<br>
Restart</span>=on-<span style="color: rgba(0, 0, 0, 1)">failure</span><span style="color: rgba(0, 0, 0, 1)">
LimitNOFILE</span>=<span style="color: rgba(128, 0, 128, 1)">65536</span><span style="color: rgba(0, 0, 0, 1)">
WantedBy</span>=multi-<span style="color: rgba(0, 0, 0, 1)">user.target
EOF</span></pre>
</div>
<p>③ 启动 kube-controller-manager 组件</p>
<p>启动组件</p>
<div class="cnblogs_code">
<pre># systemctl daemon-<span style="color: rgba(0, 0, 0, 1)">reload
# systemctl start kube</span>-controller-<span style="color: rgba(0, 0, 0, 1)">manager
# systemctl enable kube</span>-controller-manager</pre>
</div>
<p>检查启动状态</p>
<div class="cnblogs_code">
<pre># systemctl status kube-controller-manager.service</pre>
</div>
<p>查看启动日志</p>
<div class="cnblogs_code">
<pre># <span style="color: rgba(0, 0, 255, 1)">less</span> /k8s/kubernetes/logs/kube-controller-manager.INFO</pre>
</div>
<h3>4、部署 kube-scheduler 组件</h3>
<p>① 创建 kube-scheduler 配置文件:<strong>kube-scheduler.conf</strong></p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">cat</span> > /k8s/kubernetes/cfg/kube-scheduler.conf <<<span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">EOF</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">
KUBE_SCHEDULER_OPTS</span>=<span style="color: rgba(128, 0, 0, 1)">"</span>--leader-elect=<span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)"> \
</span>--master=<span style="color: rgba(128, 0, 128, 1)">127.0</span>.<span style="color: rgba(128, 0, 128, 1)">0.1</span>:<span style="color: rgba(128, 0, 128, 1)">8080</span><span style="color: rgba(0, 0, 0, 1)"> \
</span>--address=<span style="color: rgba(128, 0, 128, 1)">127.0</span>.<span style="color: rgba(128, 0, 128, 1)">0.1</span><span style="color: rgba(0, 0, 0, 1)"> \
</span>--v=<span style="color: rgba(128, 0, 128, 1)">2</span><span style="color: rgba(0, 0, 0, 1)"> \
</span>--logtostderr=<span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)"> \
</span>--log-<span style="color: rgba(0, 0, 255, 1)">dir</span>=/k8s/kubernetes/<span style="color: rgba(0, 0, 0, 1)">logs</span><span style="color: rgba(128, 0, 0, 1)">"
</span>EOF</pre>
</div>
<p>② 创建 kube-scheduler 服务:<strong>kube-scheduler.service</strong></p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">cat</span> > /usr/lib/systemd/system/kube-scheduler.service <<<span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">EOF</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">
Description</span>=<span style="color: rgba(0, 0, 0, 1)">Kubernetes Scheduler
Documentation</span>=https:<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">github.com/GoogleCloudPlatform/kubernetes</span>
After=<span style="color: rgba(0, 0, 0, 1)">network.target
</span><span style="color: rgba(0, 0, 0, 1)">
EnvironmentFile</span>=/k8s/kubernetes/cfg/kube-<span style="color: rgba(0, 0, 0, 1)">scheduler.conf
ExecStart</span>=/k8s/kubernetes/bin/kube-<span style="color: rgba(0, 0, 0, 1)">scheduler $KUBE_SCHEDULER_OPTS<br>
Restart</span>=on-<span style="color: rgba(0, 0, 0, 1)">failure</span><span style="color: rgba(0, 0, 0, 1)">
LimitNOFILE</span>=<span style="color: rgba(128, 0, 128, 1)">65536</span><span style="color: rgba(0, 0, 0, 1)">
WantedBy</span>=multi-<span style="color: rgba(0, 0, 0, 1)">user.target
EOF</span></pre>
</div>
<p>③ 启动 kube-scheduler 组件</p>
<p>启动组件</p>
<div class="cnblogs_code">
<pre># systemctl daemon-<span style="color: rgba(0, 0, 0, 1)">reload
# systemctl start kube</span>-<span style="color: rgba(0, 0, 0, 1)">scheduler
# systemctl enable kube</span>-scheduler</pre>
</div>
<p>查看启动状态</p>
<div class="cnblogs_code">
<pre># systemctl status kube-scheduler.service</pre>
</div>
<p>查看启动日志</p>
<div class="cnblogs_code">
<pre># <span style="color: rgba(0, 0, 255, 1)">less</span> /k8s/kubernetes/logs/kube-scheduler.INFO</pre>
<pre>less /k8s/kubernetes/logs/kube-scheduler.INFO</pre>
</div>
<h3>5、查看集群状态</h3>
<p>① 查看组件状态</p>
<div class="cnblogs_code">
<pre># kubectl get cs</pre>
</div>
<p><img src="https://img2018.cnblogs.com/blog/856154/201911/856154-20191120235101404-1573446834.png" alt=""></p>
<h2>七、集群搭建 —— 部署Node组件</h2>
<h3>1、安装 Docker</h3>
<p>CentOS 安装参考官方文档:https://docs.docker.com/install/linux/docker-ce/centos/</p>
<p>① 卸载旧版本</p>
<div class="cnblogs_code">
<pre># <span style="color: rgba(0, 0, 255, 1)">yum</span> remove docker docker-common docker-selinux</pre>
</div>
<p>② 安装依赖包</p>
<div class="cnblogs_code">
<pre># <span style="color: rgba(0, 0, 255, 1)">yum</span> <span style="color: rgba(0, 0, 255, 1)">install</span> -y <span style="color: rgba(0, 0, 255, 1)">yum</span>-utils device-mapper-persistent-data lvm2</pre>
</div>
<p>③ 安装 Docker 软件包源</p>
<div class="cnblogs_code">
<pre># <span style="color: rgba(0, 0, 255, 1)">yum</span>-config-manager --add-repo https:<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">download.docker.com/linux/centos/docker-ce.repo</span></pre>
</div>
<p>④ 安装 Docker CE</p>
<div class="cnblogs_code">
<pre># <span style="color: rgba(0, 0, 255, 1)">yum</span> <span style="color: rgba(0, 0, 255, 1)">install</span> docker-ce</pre>
</div>
<p>⑤ 启动 Docker 服务</p>
<div class="cnblogs_code">
<pre># systemctl start docker</pre>
</div>
<p>⑥ 设置开机启动</p>
<div class="cnblogs_code">
<pre># systemctl enable docker</pre>
</div>
<p>⑦ 验证安装是否成功</p>
<div class="cnblogs_code">
<pre># docker -v<br># docker info</pre>
</div>
<p><img src="https://img2018.cnblogs.com/blog/856154/201908/856154-20190806005734464-186989792.png" alt=""></p>
<h3>2、Node 节点证书</h3>
<p>① 创建 Node 节点的证书签名请求文件:<strong>kube-proxy-csr.json</strong></p>
<p>首先在 k8s-master-1 节点上,通过颁发的 CA 证书先创建好 Node 节点要使用的证书,先创建证书签名请求文件:kube-proxy-csr.json:</p>
<div class="cnblogs_code">
<pre>cat > kube-proxy-csr.json <<<span>EOF
{
"CN": "system:kube-proxy"<span>,
"hosts"<span>: [],
"key"<span>: {
"algo": "rsa"<span>,
"size": 2048<span>
},
"names"<span>: [
{
"C": "CN"<span>,
"ST": "BeiJing"<span>,
"L": "BeiJing"<span>,
"O": "kubernetes"<span>,
"OU": "System"<span>
}
]
}
EOF</span></span></span></span></span></span></span></span></span></span></span></span></pre>
</div>
<p>② 为 kube-proxy 生成证书和私钥</p>
<div class="cnblogs_code">
<pre>cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=kubernetes kube-proxy-csr.json | cfssljson -bare kube-proxy</pre>
</div>
<p><img src="https://img2018.cnblogs.com/blog/856154/201911/856154-20191127220706167-1455473162.png" alt=""></p>
<p>③ node 节点创建工作目录</p>
<p>在 k8s-node-1 节点上创建 k8s 目录</p>
<div class="cnblogs_code">
<pre># <span style="color: rgba(0, 0, 255, 1)">mkdir</span> -p /k8s/kubernetes/{bin,cfg,logs,ssl}</pre>
</div>
<p>④ 将 k8s-master-1 节点的文件拷贝到 node 节点</p>
<p>将 kubelet、kube-proxy 拷贝到 node 节点上:</p>
<div class="cnblogs_code">
<pre># <span style="color: rgba(0, 0, 255, 1)">scp</span> -r /usr/local/src/kubernetes/server/bin/{kubelet,kube-proxy} root@k8s-node-<span style="color: rgba(128, 0, 128, 1)">1</span>:/k8s/kubernetes/bin/<span style="color: rgba(0, 0, 0, 1)"><br></span></pre>
</div>
<p>将证书拷贝到 k8s-node-1 节点上:</p>
<div class="cnblogs_code">
<pre># <span style="color: rgba(0, 0, 255, 1)">scp</span> -r /k8s/kubernetes/ssl/{ca.pem,kube-proxy.pem,kube-proxy-key.pem} root@k8s-node-<span style="color: rgba(128, 0, 128, 1)">1</span>:/k8s/kubernetes/ssl/<span style="color: rgba(0, 0, 0, 1)"><br></span></pre>
</div>
<h3>3、安装 kubelet</h3>
<p>① 创建请求证书的配置文件:<strong>bootstrap.kubeconfig</strong></p>
<p>bootstrap.kubeconfig 将用于向 apiserver 请求证书,apiserver 会验证 token、证书 是否有效,验证通过则自动颁发证书。</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">cat</span> > /k8s/kubernetes/cfg/bootstrap.kubeconfig <<<span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">EOF</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">
apiVersion: v1
clusters:
</span>-<span style="color: rgba(0, 0, 0, 1)"> cluster:
certificate</span>-authority: /k8s/kubernetes/ssl/<span style="color: rgba(0, 0, 0, 1)">ca.pem
server: https:</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">192.168.31.24:6443</span>
<span style="color: rgba(0, 0, 0, 1)">name: kubernetes
contexts:
</span>-<span style="color: rgba(0, 0, 0, 1)"> context:
cluster: kubernetes
user: kubelet</span>-<span style="color: rgba(0, 0, 0, 1)">bootstrap
name: default
current</span>-<span style="color: rgba(0, 0, 0, 1)">context: default
kind: Config
preferences: {}
users:
</span>- name: kubelet-<span style="color: rgba(0, 0, 0, 1)">bootstrap
user:
token: bfa3cb7f6f21f87e5c0e5f25e6cfedad
EOF</span></pre>
</div>
<p><strong>说明:</strong></p>
<ul>
<li>certificate-authority:CA 证书</li>
<li>server:master 地址</li>
<li>token:master 上 token.csv 中配置的 token</li>
</ul>
<p>② 创建 kubelet 配置文件:<strong>kubelet-config.yml</strong></p>
<p>为了安全性,kubelet 禁止匿名访问,必须授权才可以,通过 kubelet-config.yml 授权 apiserver 访问 kubelet。</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">cat</span> > /k8s/kubernetes/cfg/kubelet-config.yml <<<span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">EOF</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">
kind: KubeletConfiguration
apiVersion: kubelet.config.k8s.io</span>/<span style="color: rgba(0, 0, 0, 1)">v1beta1
address: </span><span style="color: rgba(0, 0, 0, 1)"><span style="color: rgba(128, 0, 128, 1)">0.0.0.0</span></span><span style="color: rgba(0, 0, 0, 1)">
port: </span><span style="color: rgba(128, 0, 128, 1)">10250</span><span style="color: rgba(0, 0, 0, 1)">
readOnlyPort: </span><span style="color: rgba(128, 0, 128, 1)">10255</span><span style="color: rgba(0, 0, 0, 1)">
cgroupDriver: cgroupfs
clusterDNS:
</span>- <span style="color: rgba(128, 0, 128, 1)">10.0</span>.<span style="color: rgba(128, 0, 128, 1)">0.2</span><span style="color: rgba(0, 0, 0, 1)">
clusterDomain: cluster.local
failSwapOn: </span><span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">
authentication:
anonymous:
enabled: </span><span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">
webhook:
cacheTTL: 2m0s
enabled: </span><span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">
x509:
clientCAFile: </span>/k8s/kubernetes/ssl/<span style="color: rgba(0, 0, 0, 1)">ca.pem
authorization:
mode: Webhook
webhook:
cacheAuthroizedTTL: 5m0s
cacheUnauthorizedTTL: 30s
evictionHard:
imagefs.available: </span><span style="color: rgba(128, 0, 128, 1)">15</span>%<span style="color: rgba(0, 0, 0, 1)">
memory.available: 100Mi
nodefs.available: </span><span style="color: rgba(128, 0, 128, 1)">10</span>%<span style="color: rgba(0, 0, 0, 1)">
nodefs.inodesFree: </span><span style="color: rgba(128, 0, 128, 1)">5</span>%<span style="color: rgba(0, 0, 0, 1)">
maxOpenFiles: </span><span style="color: rgba(128, 0, 128, 1)">100000</span><span style="color: rgba(0, 0, 0, 1)">
maxPods: </span><span style="color: rgba(128, 0, 128, 1)">110</span><span style="color: rgba(0, 0, 0, 1)">
EOF</span></pre>
</div>
<p><strong>说明:</strong></p>
<ul>
<li>address:kubelet 监听地址</li>
<li>port:kubelet 的端口</li>
<li>cgroupDriver:cgroup 驱动,与 docker 的 cgroup 驱动一致</li>
<li>authentication:访问 kubelet 的授权信息</li>
<li>authorization:认证相关信息</li>
<li>evictionHard:垃圾回收策略</li>
<li>maxPods:最大pod数</li>
</ul>
<p>③ 创建 kubelet 服务配置文件:<strong>kubelet.conf</strong></p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">cat</span> > /k8s/kubernetes/cfg/kubelet.conf <<<span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">EOF</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">
KUBELET_OPTS</span>=<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">--hostname-override=k8s-node-1 \</span>
--network-plugin=<span style="color: rgba(0, 0, 0, 1)">cni \
</span>--cni-bin-<span style="color: rgba(0, 0, 255, 1)">dir</span>=/opt/cni/<span style="color: rgba(0, 0, 0, 1)">bin \
</span>--cni-conf-<span style="color: rgba(0, 0, 255, 1)">dir</span>=/etc/cni/<span style="color: rgba(0, 0, 0, 1)">net.d \
</span>--cgroups-per-qos=<span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)"> \
</span>--enforce-node-allocatable=<span style="color: rgba(128, 0, 0, 1)">""</span><span style="color: rgba(0, 0, 0, 1)"> \
</span>--kubeconfig=/k8s/kubernetes/cfg/<span style="color: rgba(0, 0, 0, 1)">kubelet.kubeconfig \
</span>--bootstrap-kubeconfig=/k8s/kubernetes/cfg/<span style="color: rgba(0, 0, 0, 1)">bootstrap.kubeconfig \
</span>--config=/k8s/kubernetes/cfg/kubelet-<span style="color: rgba(0, 0, 0, 1)">config.yml \
</span>--cert-<span style="color: rgba(0, 0, 255, 1)">dir</span>=/k8s/kubernetes/<span style="color: rgba(0, 0, 0, 1)">ssl \
</span>--pod-infra-container-image=kubernetes/<span style="color: rgba(0, 0, 0, 1)">pause:latest \
</span>--v=<span style="color: rgba(128, 0, 128, 1)">2</span><span style="color: rgba(0, 0, 0, 1)"> \
</span>--logtostderr=<span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)"> \
</span>--log-<span style="color: rgba(0, 0, 255, 1)">dir</span>=/k8s/kubernetes/logs<span style="color: rgba(128, 0, 0, 1)">"
</span>EOF</pre>
</div>
<p><strong>说明:</strong></p>
<ul>
<li>--hostname-override:当前节点注册到K8S中显示的名称,默认为主机 hostname</li>
<li>--network-plugin:启用 CNI 网络插件</li>
<li>--cni-bin-dir:CNI 插件可执行文件位置,默认在 /opt/cni/bin 下</li>
<li>--cni-conf-dir:CNI 插件配置文件位置,默认在 /etc/cni/net.d 下</li>
<li>--cgroups-per-qos:必须加上这个参数和--enforce-node-allocatable,否则报错 </li>
<li>--kubeconfig:会自动生成 kubelet.kubeconfig,用于连接 apiserver</li>
<li>--bootstrap-kubeconfig:指定 bootstrap.kubeconfig 文件</li>
<li>--config:kubelet 配置文件</li>
<li>--cert-dir:证书目录</li>
<li>--pod-infra-container-image:管理Pod网络的镜像,基础的 Pause 容器,默认是 k8s.gcr.io/pause:3.1</li>
</ul>
<p>④ 创建 kubelet 服务:<strong>kubelet.service</strong></p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">cat</span> > /usr/lib/systemd/system/kubelet.service <<<span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">EOF</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">
Description</span>=<span style="color: rgba(0, 0, 0, 1)">Kubernetes Kubelet
After</span>=<span style="color: rgba(0, 0, 0, 1)">docker.service
Before</span>=<span style="color: rgba(0, 0, 0, 1)">docker.service
EnvironmentFile</span>=/k8s/kubernetes/cfg/<span style="color: rgba(0, 0, 0, 1)">kubelet.conf
ExecStart</span>=/k8s/kubernetes/bin/<span style="color: rgba(0, 0, 0, 1)">kubelet $KUBELET_OPTS
Restart</span>=on-<span style="color: rgba(0, 0, 0, 1)">failure
LimitNOFILE</span>=<span style="color: rgba(128, 0, 128, 1)">65536</span><span style="color: rgba(0, 0, 0, 1)">
WantedBy</span>=multi-<span style="color: rgba(0, 0, 0, 1)">user.target
EOF</span></pre>
</div>
<p>⑤ 启动 kubelet</p>
<div class="cnblogs_code">
<pre># systemctl daemon-reload<br># systemctl start kubelet</pre>
</div>
<p>开机启动:</p>
<div class="cnblogs_code">
<pre># systemctl enable kubelet</pre>
</div>
<p>查看启动日志: </p>
<div class="cnblogs_code">
<pre># tail -f /k8s/kubernetes/logs/kubelet.INFO</pre>
</div>
<p>⑥ master 给 node 授权</p>
<p>kubelet 启动后,还没加入到集群中,会向 apiserver 请求证书,需手动在 k8s-master-1 上对 node 授权。</p>
<p>通过如下命令查看是否有新的客户端请求颁发证书:</p>
<div class="cnblogs_code">
<pre># kubectl get csr</pre>
</div>
<p><img src="https://img2018.cnblogs.com/blog/856154/201911/856154-20191127220530728-425743917.png" alt=""></p>
<p>给客户端颁发证书,允许客户端加入集群:</p>
<div class="cnblogs_code">
<pre># kubectl certificate approve node-csr-FoPLmv3Sr2XcYvNAineE6RpdARf2eKQzJsQyfhk-xf8</pre>
</div>
<p><img src="https://img2018.cnblogs.com/blog/856154/201911/856154-20191127220827044-1194859709.png" alt=""></p>
<p>⑦ 授权成功</p>
<p>查看 node 是否加入集群(此时的 node 还处于未就绪的状态,因为还没有安装 CNI 组件):</p>
<div class="cnblogs_code">
<pre># kubectl get node</pre>
</div>
<p><img src="https://img2018.cnblogs.com/blog/856154/201911/856154-20191127220909837-1664629101.png" alt=""></p>
<p>颁发证书后,可以在 /k8s/kubenetes/ssl 下看到 master 为 kubelet 颁发的证书:</p>
<p><img src="https://img2018.cnblogs.com/blog/856154/201911/856154-20191127221220075-1579437290.png" alt=""></p>
<p>在 /k8s/kubenetes/cfg 下可以看到自动生成的 kubelet.kubeconfig 配置文件:</p>
<p><img src="https://img2018.cnblogs.com/blog/856154/201911/856154-20191127221332515-613675599.png" alt=""></p>
<h3>4、安装 kube-proxy</h3>
<p>① 创建 kube-proxy 连接 apiserver 的配置文件:<strong>kube-proxy.kubeconfig</strong></p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">cat</span> > /k8s/kubernetes/cfg/kube-proxy.kubeconfig <<<span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">EOF</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">
apiVersion: v1
clusters:
</span>-<span style="color: rgba(0, 0, 0, 1)"> cluster:
certificate</span>-authority: /k8s/kubernetes/ssl/<span style="color: rgba(0, 0, 0, 1)">ca.pem
server: https:</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">192.168.31.24:6443</span>
<span style="color: rgba(0, 0, 0, 1)">name: kubernetes
contexts:
</span>-<span style="color: rgba(0, 0, 0, 1)"> context:
cluster: kubernetes
user: kube</span>-<span style="color: rgba(0, 0, 0, 1)">proxy
name: default
current</span>-<span style="color: rgba(0, 0, 0, 1)">context: default
kind: Config
preferences: {}
users:
</span>- name: kube-<span style="color: rgba(0, 0, 0, 1)">proxy
user:
client</span>-certificate: /k8s/kubernetes/ssl/kube-<span style="color: rgba(0, 0, 0, 1)">proxy.pem
client</span>-key: /k8s/kubernetes/ssl/kube-proxy-<span style="color: rgba(0, 0, 0, 1)">key.pem
EOF</span></pre>
</div>
<p>② 创建 kube-proxy 配置文件:<strong>kube-proxy-config.yml</strong></p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">cat</span> > /k8s/kubernetes/cfg/kube-proxy-config.yml <<<span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">EOF</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">
kind: KubeProxyConfiguration
apiVersion: kubeproxy.config.k8s.io</span>/<span style="color: rgba(0, 0, 0, 1)">v1alpha1
address: </span><span style="color: rgba(128, 0, 128, 1)">0.0</span>.<span style="color: rgba(128, 0, 128, 1)">0.0</span><span style="color: rgba(0, 0, 0, 1)">
metrisBindAddress: </span><span style="color: rgba(128, 0, 128, 1)">0.0</span>.<span style="color: rgba(128, 0, 128, 1)">0.0</span>:<span style="color: rgba(128, 0, 128, 1)">10249</span><span style="color: rgba(0, 0, 0, 1)">
clientConnection:
kubeconfig: </span>/k8s/kubernetes/cfg/kube-<span style="color: rgba(0, 0, 0, 1)">proxy.kubeconfig
hostnameOverride: k8s</span>-node-<span style="color: rgba(128, 0, 128, 1)">1</span><span style="color: rgba(0, 0, 0, 1)">
clusterCIDR: </span><span style="color: rgba(128, 0, 128, 1)">10.0</span>.<span style="color: rgba(128, 0, 128, 1)">0.0</span>/<span style="color: rgba(128, 0, 128, 1)">24</span><span style="color: rgba(0, 0, 0, 1)">
mode: ipvs
ipvs:
scheduler: </span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">rr</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">
iptables:
masqueradeAll: </span><span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">
EOF</span></pre>
</div>
<p><strong>说明:</strong></p>
<ul>
<li>metrisBindAddress:采集指标暴露的地址端口,便于监控系统,采集数据</li>
<li>clusterCIDR:集群 Service 网段</li>
</ul>
<p>③ 创建 kube-proxy 配置文件:<strong>kube-proxy.conf</strong></p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">cat</span> > /k8s/kubernetes/cfg/kube-proxy.conf <<<span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">EOF</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">
KUBE_PROXY_OPTS</span>=<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">--config=/k8s/kubernetes/cfg/kube-proxy-config.yml \</span>
--v=<span style="color: rgba(128, 0, 128, 1)">2</span><span style="color: rgba(0, 0, 0, 1)"> \
</span>--logtostderr=<span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)"> \
</span>--log-<span style="color: rgba(0, 0, 255, 1)">dir</span>=/k8s/kubernetes/logs<span style="color: rgba(128, 0, 0, 1)">"
</span>EOF</pre>
</div>
<p>④ 创建 kube-proxy 服务:<strong>kube-proxy.service</strong></p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">cat</span> > /usr/lib/systemd/system/kube-proxy.service <<<span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">EOF</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">
Description</span>=<span style="color: rgba(0, 0, 0, 1)">Kubernetes Proxy
After</span>=<span style="color: rgba(0, 0, 0, 1)">network.target
EnvironmentFile</span>=/k8s/kubernetes/cfg/kube-<span style="color: rgba(0, 0, 0, 1)">proxy.conf
ExecStart</span>=/k8s/kubernetes/bin/kube-<span style="color: rgba(0, 0, 0, 1)">proxy $KUBE_PROXY_OPTS
Restart</span>=on-<span style="color: rgba(0, 0, 0, 1)">failure
LimitNOFILE</span>=<span style="color: rgba(128, 0, 128, 1)">65536</span><span style="color: rgba(0, 0, 0, 1)">
WantedBy</span>=multi-<span style="color: rgba(0, 0, 0, 1)">user.target
EOF</span></pre>
</div>
<p>⑤ 启动 kube-proxy</p>
<p>启动服务:</p>
<div class="cnblogs_code">
<pre># systemctl daemon-reload<br># systemctl start kube-proxy</pre>
</div>
<p>开机启动:</p>
<div class="cnblogs_code">
<pre># systemctl enable kube-proxy</pre>
</div>
<p>查看启动日志: </p>
<div class="cnblogs_code">
<pre># tail -f /k8s/kubernetes/logs/kube-proxy.INFO</pre>
</div>
<h3>5、部署其它Node节点</h3>
<p>部署其它 Node 节点基于与上述流程一致,只需将配置文件中 k8s-node-1 改为 k8s-node-2 即可。</p>
<div class="cnblogs_code">
<pre># kubectl get node -o wide</pre>
</div>
<p><img src="https://img2018.cnblogs.com/blog/856154/201911/856154-20191128224015295-979089341.png" alt=""></p>
<h3>6、部署K8S容器集群网络(Flannel)</h3>
<p>① K8S 集群网络</p>
<p>Kubernetes 项目并没有使用 Docker 的网络模型,kubernetes 是通过一个 CNI 接口维护一个单独的网桥来代替 docker0,这个网桥默认叫 <strong>cni0</strong>。</p>
<p>CNI(Container Network Interface)是CNCF旗下的一个项目,由一组用于配置 Linux 容器的网络接口的规范和库组成,同时还包含了一些插件。CNI仅关心容器创建时的网络分配,和当容器被删除时释放网络资源。</p>
<p>Flannel 是 CNI 的一个插件,可以看做是 CNI 接口的一种实现。Flannel 是针对 Kubernetes 设计的一个网络规划服务,它的功能是让集群中的不同节点主机创建的Docker容器都具有全集群唯一的虚拟IP地址,并让属于不同节点上的容器能够直接通过内网IP通信。</p>
<p>Flannel 网络架构请参考:flannel 网络架构</p>
<p>② 创建 CNI 工作目录</p>
<p>通过给 kubelet 传递 <strong>--network-plugin=cni</strong> 命令行选项来启用 CNI 插件。 kubelet 从 <strong>--cni-conf-dir</strong> (默认是 /etc/cni/net.d)读取配置文件并使用该文件中的 CNI 配置来设置每个 pod 的网络。CNI 配置文件必须与 CNI 规约匹配,并且配置引用的任何所需的 CNI 插件都必须存在于 <strong>--cni-bin-dir</strong>(默认是 /opt/cni/bin)指定的目录。</p>
<p>由于前面部署 kubelet 服务时,指定了 <strong>--cni-conf-dir=/etc/cni/net.d</strong>,<strong>--cni-bin-dir=/opt/cni/bin</strong>,因此首先在node节点上创建这两个目录:</p>
<div class="cnblogs_code">
<pre># <span style="color: rgba(0, 0, 255, 1)">mkdir -p</span> /opt/cni/bin /etc/cni/net.d</pre>
</div>
<p>③ 装 CNI 插件</p>
<p>可以从 github 上下载 CNI 插件:下载 CNI 插件 。</p>
<p>解压到 /opt/cni/bin:</p>
<div class="cnblogs_code">
<pre># <span style="color: rgba(0, 0, 255, 1)">tar</span> zxf cni-plugins-linux-amd64-v0.<span style="color: rgba(128, 0, 128, 1)">8.2</span>.tgz -C /opt/cni/bin/</pre>
</div>
<p><img src="https://img2018.cnblogs.com/blog/856154/201911/856154-20191128220554099-1684002659.png" alt=""></p>
<p>④ 部署 Flannel</p>
<p>可通过此地址下载 flannel 配置文件:下载 kube-flannel.yml</p>
<p>注意如下配置:Network 的地址需与<strong> kube-controller-manager.conf</strong> 中的 <strong>--cluster-cidr=10.244.0.0/16</strong> 保持一致。</p>
<p><img src="https://img2018.cnblogs.com/blog/856154/201911/856154-20191128112822714-1964053859.png" alt=""></p>
<p>在 k8s-master-1 节点上部署 Flannel:</p>
<div class="cnblogs_code">
<pre># kubectl apply -f <span style="color: rgba(0, 128, 0, 1)">kube-flannel.yml</span></pre>
</div>
<p><img src="https://img2018.cnblogs.com/blog/856154/201911/856154-20191128230506906-1309290806.png" alt=""></p>
<p>⑤ 检查部署状态</p>
<p>Flannel 会在 Node 上起一个 Flannel 的 Pod,可以查看 pod 的状态看 flannel 是否启动成功:</p>
<div class="cnblogs_code">
<pre># kubectl get pods -n kube-system -o wide</pre>
</div>
<p><img src="https://img2018.cnblogs.com/blog/856154/201912/856154-20191205005818391-377110510.png" alt=""></p>
<p> Flannel 部署成功后,就可以看 Node 是否就绪:</p>
<div class="cnblogs_code">
<pre># kubectl get nodes -o wide</pre>
</div>
<p><img src="https://img2018.cnblogs.com/blog/856154/201912/856154-20191205010005973-37990797.png" alt=""></p>
<p>在 Node 上查看网络配置,可以看到多了一个 <strong>flannel.1</strong> 的虚拟网卡,这块网卡用于接收 Pod 的流量并转发出去。</p>
<p><img src="https://img2018.cnblogs.com/blog/856154/201912/856154-20191205010303481-754515627.png" alt=""></p>
<p>⑥ 测试创建 Pod</p>
<p>例如创建一个 Nginx 服务:</p>
<div class="cnblogs_code">
<pre># kubectl create deployment web --image=nginx</pre>
</div>
<p><img src="https://img2018.cnblogs.com/blog/856154/201912/856154-20191205010658719-540734499.png" alt=""></p>
<p>查看 Pod 状态:</p>
<p><img src="https://img2018.cnblogs.com/blog/856154/201912/856154-20191205010735690-1937177430.png" alt=""></p>
<p>在对应的节点上可以看到部署的容器:</p>
<p><img src="https://img2018.cnblogs.com/blog/856154/201912/856154-20191205012234493-297995248.png" alt=""></p>
<p>容器创建成功后,再在 Node 上查看网络配置,又多了一块 cni0 的虚拟网卡,cni0 用于 pod 本地通信使用。</p>
<p><img src="https://img2018.cnblogs.com/blog/856154/201912/856154-20191205010844816-242752920.png" alt=""></p>
<p>暴露端口并访问 Nginx:</p>
<div class="cnblogs_code">
<pre># kubectl expose deployment web --port=<span style="color: rgba(128, 0, 128, 1)">80</span> --type=NodePort</pre>
</div>
<p><img src="https://img2018.cnblogs.com/blog/856154/201912/856154-20191205012049946-519639325.png" alt=""></p>
<h3>7、部署内部 DNS 服务</h3>
<p>在Kubernetes集群推荐使用Service Name作为服务的访问地址,因此需要一个Kubernetes集群范围的DNS服务实现从Service Name到Cluster IP的解析,这就是Kubernetes基于DNS的服务发现功能。</p>
<p>① 部署 CoreDNS</p>
<p>下载CoreDNS配置文件:coredns.yaml</p>
<p>注意如下 clusterIP 一定要与 <strong>kube-config.yaml</strong> 中的 <strong>clusterDNS</strong> 保持一致</p>
<p><img src="https://img2018.cnblogs.com/blog/856154/201912/856154-20191206013327734-494461951.png" alt=""></p>
<p>部署 CoreDNS:</p>
<div class="cnblogs_code">
<pre>$ kubectl apply -f coredns.yaml</pre>
</div>
<p><img src="https://img2018.cnblogs.com/blog/856154/201912/856154-20191208235827818-71864912.png" alt=""></p>
<p>② 验证 CoreDNS</p>
<p>创建 busybox 服务:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)"># cat</span> > busybox.yaml <<<span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">EOF</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">
apiVersion: v1
kind: Pod
metadata:
name: busybox
namespace: default
spec:
dnsPolicy: ClusterFirst
containers:
</span>-<span style="color: rgba(0, 0, 0, 1)"> name: busybox
image: busybox:1.28.4
command:
</span>- <span style="color: rgba(0, 0, 255, 1)">sleep</span>
- <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">3600</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">
imagePullPolicy: IfNotPresent
restartPolicy: Always
EOF<br><br># kubectl apply -f busybox.yaml</span></pre>
</div>
<p>验证是否安装成功:</p>
<p><img src="https://img2018.cnblogs.com/blog/856154/201912/856154-20191209011552538-431087460.png" alt=""></p>
<h2>八、集群搭建 —— 部署 Dashboard</h2>
<p>K8S 提供了一个 Web 版 Dashboard,用户可以用 dashboard 部署容器化的应用、监控应用的状态,能够创建和修改各种 K8S 资源,比如 Deployment、Job、DaemonSet 等。用户可以 Scale Up/Down Deployment、执行 Rolling Update、重启某个 Pod 或者通过向导部署新的应用。Dashboard 能显示集群中各种资源的状态以及日志信息。Kubernetes Dashboard 提供了 kubectl 的绝大部分功能。</p>
<h3>1、部署 K8S Dashboard</h3>
<p>通过此地址下载 dashboard yaml文件:kubernetes-dashboard.yaml</p>
<p>下载下来之后,需更新如下内容:通过 Node 暴露端口访问 dashboard</p>
<p><img src="https://img2018.cnblogs.com/blog/856154/201912/856154-20191206010304862-1366726165.png" alt=""></p>
<p>部署 dashboard:</p>
<div class="cnblogs_code">
<pre># kubectl apply -f kubernetes-dashboard.yaml</pre>
</div>
<p>查看是否部署成功:</p>
<p><img src="https://img2018.cnblogs.com/blog/856154/201912/856154-20191206010403304-236700234.png" alt=""></p>
<p>通过 https 访问 dashboard:</p>
<p><img src="https://img2018.cnblogs.com/blog/856154/201912/856154-20191206010519105-236777362.png" alt=""></p>
<h3>2、登录授权</h3>
<p>Dashboard 支持 Kubeconfig 和 Token 两种认证方式,为了简化配置,我们通过配置文件为 Dashboard 默认用户赋予 admin 权限。</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">cat</span> > kubernetes-adminuser.yaml <<<span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">EOF</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">
apiVersion: v1
kind: ServiceAccount
metadata:
name: admin</span>-<span style="color: rgba(0, 0, 0, 1)">user
namespace: kube</span>-<span style="color: rgba(0, 0, 0, 1)">system
</span>---<span style="color: rgba(0, 0, 0, 1)">
apiVersion: rbac.authorization.k8s.io</span>/<span style="color: rgba(0, 0, 0, 1)">v1beta1
kind: ClusterRoleBinding
metadata:
name: admin</span>-<span style="color: rgba(0, 0, 0, 1)">user
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: cluster</span>-<span style="color: rgba(0, 0, 0, 1)">admin
subjects:
</span>-<span style="color: rgba(0, 0, 0, 1)"> kind: ServiceAccount
name: admin</span>-<span style="color: rgba(0, 0, 0, 1)">user
namespace: kube</span>-<span style="color: rgba(0, 0, 0, 1)">system
EOF</span></pre>
</div>
<p>授权:</p>
<div class="cnblogs_code">
<pre># kubectl apply -f kubernetes-adminuser.yaml</pre>
</div>
<p>获取登录的 token:</p>
<div class="cnblogs_code">
<pre># kubectl -n kube-system describe secret $(kubectl -n kube-system get secret | <span style="color: rgba(0, 0, 255, 1)">grep</span> admin-user | <span style="color: rgba(0, 0, 255, 1)">awk</span> <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)"> {print $1}</span><span style="color: rgba(128, 0, 0, 1)">'</span>)</pre>
</div>
<p><img src="https://img2018.cnblogs.com/blog/856154/201912/856154-20191206011211859-592094825.png" alt=""></p>
<p>通过token登录进 dashboard,就可以查看集群的信息:</p>
<p><img src="https://img2018.cnblogs.com/blog/856154/201912/856154-20191206011456607-1992406368.png" alt=""></p>
<h2>九、集群搭建 —— 多 Master 部署</h2>
<h3>1、部署Master2组件</h3>
<p>① 将 k8s-master-1 上相关文件拷贝到 k8s-master-2 上</p>
<p>创建k8s工作目录:</p>
<div class="cnblogs_code">
<pre># <span style="color: rgba(0, 0, 255, 1)">mkdir</span> -p /k8s/kubernetes<span style="color: rgba(0, 0, 0, 1)">
# </span><span style="color: rgba(0, 0, 255, 1)">mkdir</span> -p /k8s/etcd</pre>
</div>
<p>拷贝 k8s 配置文件、执行文件、证书:</p>
<div class="cnblogs_code">
<pre># <span style="color: rgba(0, 0, 255, 1)">scp</span> -r /k8s/kubernetes/{cfg,ssl,bin} root@k8s-master-<span style="color: rgba(128, 0, 128, 1)">2</span>:/k8s/<span style="color: rgba(0, 0, 0, 1)">kubernetes
# </span><span style="color: rgba(0, 0, 255, 1)">cp</span> /k8s/kubernetes/bin/kubectl /usr/local/bin/</pre>
</div>
<p>拷贝 etcd 证书:</p>
<div class="cnblogs_code">
<pre># <span style="color: rgba(0, 0, 255, 1)">scp</span> -r /k8s/etcd/ssl root@k8s-master-<span style="color: rgba(128, 0, 128, 1)">2</span>:/k8s/etcd</pre>
</div>
<p>拷贝 k8s 服务的service文件:</p>
<div class="cnblogs_code">
<pre># <span style="color: rgba(0, 0, 255, 1)">scp</span> /usr/lib/systemd/system/kube-* root@k8s-master-<span style="color: rgba(128, 0, 128, 1)">2</span>:/usr/lib/systemd/system</pre>
</div>
<p>② 修改 k8s-master-2 上的配置文件</p>
<p>修改 kube-apiserver.conf,修改IP为本机IP</p>
<p> </p>
<p><img src="https://img2018.cnblogs.com/blog/856154/201912/856154-20191210231658832-199791846.png" alt=""></p>
<p>③ 启动 k8s-master-2 组件</p>
<p>重新加载配置:</p>
<div class="cnblogs_code">
<pre># systemctl daemon-reload</pre>
</div>
<p>启动 kube-apiserver:</p>
<div class="cnblogs_code">
<pre># systemctl start kube-<span style="color: rgba(0, 0, 0, 1)">apiserver
# systemctl enable kube</span>-apiserver</pre>
</div>
<p>启动 kube-controller-manager:</p>
<div class="cnblogs_code">
<pre># systemctl start kube-controller-<span style="color: rgba(0, 0, 0, 1)">manager
# systemctl enable kube</span>-controller-manager</pre>
</div>
<p>部署 kube-scheduler:</p>
<div class="cnblogs_code">
<pre># systemctl start kube-<span style="color: rgba(0, 0, 0, 1)">scheduler
# systemctl enable kube</span>-scheduler</pre>
</div>
<p>④ 验证</p>
<p><img src="https://img2018.cnblogs.com/blog/856154/201912/856154-20191210233157623-672470319.png" alt=""></p>
<h3>2、部署 Nginx 负载均衡</h3>
<p>为了保证 k8s master 的高可用,将使用 k8s-lb-master 和 k8s-lb-backup 这两台机器来部署负载均衡。这里使用 nginx 做负载均衡器,下面分别在 k8s-lb-master 和 k8s-lb-backup 这两台机器上部署 nginx。</p>
<p>① gcc等环境安装,后续有些软件安装需要这些基础环境</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)"># gcc安装:
# </span><span style="color: rgba(0, 0, 255, 1)">yum</span> <span style="color: rgba(0, 0, 255, 1)">install</span> <span style="color: rgba(0, 0, 255, 1)">gcc</span>-c++<span style="color: rgba(0, 0, 0, 1)">
# PCRE pcre</span>-<span style="color: rgba(0, 0, 0, 1)">devel 安装:
# </span><span style="color: rgba(0, 0, 255, 1)">yum</span> <span style="color: rgba(0, 0, 255, 1)">install</span> -y pcre pcre-<span style="color: rgba(0, 0, 0, 1)">devel
# zlib 安装:
# </span><span style="color: rgba(0, 0, 255, 1)">yum</span> <span style="color: rgba(0, 0, 255, 1)">install</span> -y zlib zlib-<span style="color: rgba(0, 0, 0, 1)">devel
#OpenSSL 安装:
# </span><span style="color: rgba(0, 0, 255, 1)">yum</span> <span style="color: rgba(0, 0, 255, 1)">install</span> -y openssl openssl-devel</pre>
</div>
<p>② 安装nginx</p>
<div class="cnblogs_code">
<pre># rpm -ivh https:<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">nginx.org/packages/rhel/7/x86_64/RPMS/nginx-1.16.1-1.el7.ngx.x86_64.rpm</span></pre>
</div>
<p>③ apiserver 负载配置</p>
<div class="cnblogs_code">
<pre># vim /etc/nginx/nginx.conf</pre>
</div>
<p>增加如下配置:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">stream {
log_format main </span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">$remote_addr $upstream_addr - [$time_local] $status $upstream_bytes_sent</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">;
access_log </span>/var/log/nginx/k8s-<span style="color: rgba(0, 0, 0, 1)">access.log main;
upstream k8s</span>-<span style="color: rgba(0, 0, 0, 1)">apiserver {
server </span><span style="color: rgba(128, 0, 128, 1)">192.168</span>.<span style="color: rgba(128, 0, 128, 1)">31.24</span>:<span style="color: rgba(128, 0, 128, 1)">6443</span><span style="color: rgba(0, 0, 0, 1)">;
server </span><span style="color: rgba(128, 0, 128, 1)">192.168</span>.<span style="color: rgba(128, 0, 128, 1)">31.26</span>:<span style="color: rgba(128, 0, 128, 1)">6443</span><span style="color: rgba(0, 0, 0, 1)">;
}
server {
listen </span><span style="color: rgba(128, 0, 128, 1)">6443</span><span style="color: rgba(0, 0, 0, 1)">;
proxy_pass k8s</span>-<span style="color: rgba(0, 0, 0, 1)">apiserver;
}
}</span></pre>
</div>
<p>④ 启动 nginx</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)"># systemctl start nginx
# systemctl enable nginx</span></pre>
</div>
<h3>3、部署 KeepAlive</h3>
<p>为了保证 nginx 的高可用,还需要部署 keepalive,keepalive 主要负责 nginx 的健康检查和故障转移。</p>
<p>① 分别在 k8s-lb-master 和 k8s-lb-backup 这两台机器上安装 keepalive</p>
<div class="cnblogs_code">
<pre># <span style="color: rgba(0, 0, 255, 1)">yum</span> <span style="color: rgba(0, 0, 255, 1)">install</span> keepalived -y</pre>
</div>
<p>② master 启动 keepalived</p>
<p>修改 k8s-lb-master keepalived 配置文件</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">cat</span> > /etc/keepalived/keepalived.conf <<<span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">EOF</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">
global_defs {
notification_email {
acassen@firewall.loc
failover@firewall.loc
sysadmin@firewall.loc
}
notification_email_from Alexandre.Cassen@firewall.loc
smtp_server </span><span style="color: rgba(128, 0, 128, 1)">127.0</span>.<span style="color: rgba(128, 0, 128, 1)">0.1</span><span style="color: rgba(0, 0, 0, 1)">
smtp_connect_timeout </span><span style="color: rgba(128, 0, 128, 1)">30</span><span style="color: rgba(0, 0, 0, 1)">
router_id NGINX_MASTER
}
vrrp_script check_nginx {
script </span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">/etc/keepalived/check_nginx.sh</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">
}
# vrrp实例
vrrp_instance VI_1 {
state MASTER
interface ens33
virtual_router_id </span><span style="color: rgba(128, 0, 128, 1)">51 </span><span style="color: rgba(0, 0, 0, 1)">
priority </span><span style="color: rgba(128, 0, 128, 1)">100</span><span style="color: rgba(0, 0, 0, 1)">
advert_int </span><span style="color: rgba(128, 0, 128, 1)">1 </span><span style="color: rgba(0, 0, 0, 1)">
authentication {
auth_type PASS
auth_pass </span><span style="color: rgba(128, 0, 128, 1)">1111</span><span style="color: rgba(0, 0, 0, 1)">
}
virtual_ipaddress {
</span><span style="color: rgba(128, 0, 128, 1)">192.168</span>.<span style="color: rgba(128, 0, 128, 1)">31.100</span>/<span style="color: rgba(128, 0, 128, 1)">24</span><span style="color: rgba(0, 0, 0, 1)">
}
track_script {
check_nginx
}
}
EOF</span></pre>
</div>
<p><strong>配置说明:</strong></p>
<ul>
<li>vrrp_script:用于健康检查nginx状态,如果nginx没有正常工作,就会进行故障漂移,使备节点接管VIP,这里通过 shell 脚本来检查nginx状态</li>
<li>state:keepalived 角色,主节点为 MASTER,备节点为 BACKUP</li>
<li>interface:接口,配置本地网卡名,keepalived 会将虚拟IP绑定到这个网卡上</li>
<li>virtual_router_id:#VRRP 路由ID实例,每个实例是唯一的</li>
<li>priority:优先级,备服务器设置90</li>
<li>advert_int:指定VRRP心跳包通告间隔时间,默认1秒</li>
<li>virtual_ipaddress:VIP,要与当前机器在同一网段,keepalived 会在网卡上附加这个IP,之后通过这个IP来访问Nginx,当nginx不可用时,会将此虚拟IP漂移到备节点上。</li>
</ul>
<p>增加 check_nginx.sh 脚本,通过此脚本判断 nginx 是否正常:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">cat ></span> /etc/keepalived/check_nginx.<span style="color: rgba(0, 0, 255, 1)">sh</span> <<<span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">EOF</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">
#</span>!/bin/<span style="color: rgba(0, 0, 0, 1)">bash
count</span>=$(<span style="color: rgba(0, 0, 255, 1)">ps</span> -ef | <span style="color: rgba(0, 0, 255, 1)">grep</span> nginx | e<span style="color: rgba(0, 0, 255, 1)">grep</span> -cv <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">grep|$$</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 0, 255, 1)">if</span> [ <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">$count</span><span style="color: rgba(128, 0, 0, 1)">"</span> -eq <span style="color: rgba(128, 0, 128, 1)">0</span> ];<span style="color: rgba(0, 0, 255, 1)">then</span><span style="color: rgba(0, 0, 0, 1)">
exit </span><span style="color: rgba(128, 0, 128, 1)">1</span><span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)">
exit </span><span style="color: rgba(128, 0, 128, 1)">0</span><span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 0, 255, 1)">fi</span><span style="color: rgba(0, 0, 0, 1)">
EOF</span></pre>
</div>
<p>增加可执行权限:</p>
<div class="cnblogs_code">
<pre># <span style="color: rgba(0, 0, 255, 1)">chmod</span> +x /etc/keepalived/check_nginx.<span style="color: rgba(0, 0, 255, 1)">sh</span></pre>
</div>
<p>启动 keepalived:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)"># systemctl start keepalived
# systemctl enable keepalived</span></pre>
</div>
<p>③ backup 启动 keepalived</p>
<p>修改 k8s-lb-backup keepalived 配置文件</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">cat</span> > /etc/keepalived/keepalived.conf <<<span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">EOF</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">
global_defs {
notification_email {
acassen@firewall.loc
failover@firewall.loc
sysadmin@firewall.loc
}
notification_email_from Alexandre.Cassen@firewall.loc
smtp_server </span><span style="color: rgba(128, 0, 128, 1)">127.0</span>.<span style="color: rgba(128, 0, 128, 1)">0.1</span><span style="color: rgba(0, 0, 0, 1)">
smtp_connect_timeout </span><span style="color: rgba(128, 0, 128, 1)">30</span><span style="color: rgba(0, 0, 0, 1)">
router_id NGINX_BACKUP
}
vrrp_script check_nginx {
script </span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">/etc/keepalived/check_nginx.sh</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">
}
# vrrp实例
vrrp_instance VI_1 {
state BACKUP
interface eno16777736
virtual_router_id </span><span style="color: rgba(128, 0, 128, 1)">51</span><span style="color: rgba(0, 0, 0, 1)">
priority </span><span style="color: rgba(128, 0, 128, 1)">90</span><span style="color: rgba(0, 0, 0, 1)">
advert_int </span><span style="color: rgba(128, 0, 128, 1)">1</span><span style="color: rgba(0, 0, 0, 1)">
authentication {
auth_type PASS
auth_pass </span><span style="color: rgba(128, 0, 128, 1)">1111</span><span style="color: rgba(0, 0, 0, 1)">
}
virtual_ipaddress {
</span><span style="color: rgba(128, 0, 128, 1)">192.168</span>.<span style="color: rgba(128, 0, 128, 1)">31.100</span>/<span style="color: rgba(128, 0, 128, 1)">24</span><span style="color: rgba(0, 0, 0, 1)">
}
track_script {
check_nginx
}
}
EOF</span></pre>
</div>
<p>增加 check_nginx.sh 脚本:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">cat</span> > /etc/keepalived/check_nginx.<span style="color: rgba(0, 0, 255, 1)">sh</span> <<<span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">EOF</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">
#</span>!/bin/<span style="color: rgba(0, 0, 0, 1)">bash
count</span>=$(<span style="color: rgba(0, 0, 255, 1)">ps</span> -ef | <span style="color: rgba(0, 0, 255, 1)">grep</span> nginx | e<span style="color: rgba(0, 0, 255, 1)">grep</span> -cv <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">grep|$$</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 0, 255, 1)">if</span> [ <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">$count</span><span style="color: rgba(128, 0, 0, 1)">"</span> -eq <span style="color: rgba(128, 0, 128, 1)">0</span> ];<span style="color: rgba(0, 0, 255, 1)">then</span><span style="color: rgba(0, 0, 0, 1)">
exit </span><span style="color: rgba(128, 0, 128, 1)">1</span><span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)">
exit </span><span style="color: rgba(128, 0, 128, 1)">0</span><span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 0, 255, 1)">fi</span><span style="color: rgba(0, 0, 0, 1)">
EOF</span></pre>
</div>
<p>增加可执行权限:</p>
<div class="cnblogs_code">
<pre># <span style="color: rgba(0, 0, 255, 1)">chmod</span> +x /etc/keepalived/check_nginx.<span style="color: rgba(0, 0, 255, 1)">sh</span></pre>
</div>
<p>启动 keepalived:</p>
<div class="cnblogs_code">
<pre><span># systemctl start keepalived
# systemctl enable keepalived</span></pre>
</div>
<p>④ 验证负载均衡</p>
<p>keepalived 已经将VIP附加到MASTER所在的网卡上</p>
<p><img src="https://img2018.cnblogs.com/blog/856154/202001/856154-20200105231349220-1660151152.png" alt=""></p>
<p>BACKUP节点上并没有</p>
<p><img src="https://img2018.cnblogs.com/blog/856154/202001/856154-20200105232120068-658127475.png" alt=""></p>
<p>关闭 k8s-lb-master 上的nginx,可看到VIP已经不在了</p>
<p><img src="https://img2018.cnblogs.com/blog/856154/202001/856154-20200105233741081-433366894.png" alt=""></p>
<p>可以看到已经漂移到备节点上了,如果再重启 MASTER 上的 Ngnix,VIP又会漂移到主节点上。</p>
<p><img src="https://img2018.cnblogs.com/blog/856154/202001/856154-20200105233815959-403369443.png" alt=""></p>
<p>访问虚拟IP还是可以访问的</p>
<p><img src="https://img2018.cnblogs.com/blog/856154/202001/856154-20200105233850964-1133520064.png" alt=""></p>
<p>4、Node节点连接VIP</p>
<p>① 修改 node 节点的配置文件中连接 k8s-master 的IP为VIP</p>
<p><img src="https://img2018.cnblogs.com/blog/856154/202001/856154-20200105234648106-1656311814.png" alt=""></p>
<p>② 重启 kubelet 和 kube-proxy</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)"># systemctl restart kubelet
# systemctl restart kube</span>-proxy</pre>
</div>
<p>③ 在 node 节点上访问VIP调用 apiserver 验证</p>
<p>Authorization 的token 是前面生成 token.csv 中的令牌。</p>
<p><img src="https://img2018.cnblogs.com/blog/856154/202001/856154-20200106001143565-1533967509.png" alt=""></p>
<p> </p>
<p>至此,通过二进制方式安装 K8S 集群就算完成了!!</p>
</div>
<div id="MySignature" role="contentinfo">
<div>作者:bojiangzhou</div>
<div>出处:http://www.cnblogs.com/chiangchou/</div>
<div>本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。 </div><br><br>
来源:https://www.cnblogs.com/chiangchou/p/k8s-1.html
頁:
[1]