mirror of
https://github.com/cheetahlou/CategoryResourceRepost.git
synced 2025-11-03 07:43:44 +08:00
mod
This commit is contained in:
@@ -0,0 +1,131 @@
|
||||
<audio id="audio" title="33 | 知识串联:以购买火车票的流程串联分布式核心技术" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/91/d8/91dcfa77744d954009bc5c8640f7a3d8.mp3"></audio>
|
||||
|
||||
你好,我是聂鹏程。今天,我来继续带你打卡分布式核心技术。
|
||||
|
||||
还记得在专栏之初,我和你分享的“分布式四纵四横知识体系”吗?截止到目前,我已经带你学习了四横和三纵,包括分布式计算、分布式存储与管理、分布式通信、分布式资源池化、分布式协同、分布式调度和分布式追踪与高可用的关键技术(由于分布式追踪、分布式部署虽属于支撑技术,但并不会影响业务的构成,因此我没有在专栏中展开)。
|
||||
|
||||
但学以致用才是最终目的,所以在接下来的模块中,我将通过两篇总结性质的文章,为你串联起前面讲到的核心知识点,看看它们在业务中是如何应用的。
|
||||
|
||||
今天,我就先以购买火车票的流程,带你串联下整个专栏涉及的分布式核心技术吧。
|
||||
|
||||
首先,为方便你理解,并抓住其中涉及的核心技术,我对购买火车票的流程做了一个简化,大致划分为三大核心步骤:
|
||||
|
||||
- 首先,铁路局向购票系统发布火车票;
|
||||
- 然后,用户通过系统查询火车票,找到需要的火车票后购买;
|
||||
- 最后,购票系统给用户响应,完成购票。
|
||||
|
||||
这个流程看似简单,但涉及了我们之前讲过的很多知识。
|
||||
|
||||
这里,我有个小建议,在学习后面的内容前,你可以先自己思考下这个过程涉及了哪些知识点,然后再与我接下来的讲述进行对比,以验证自己对之前内容的掌握程度。这样一来,你可以加深对已掌握知识的理解深度,也可以查漏补缺进而有针对性地复习其他内容。
|
||||
|
||||
那么接下来,我就主要分为三部分进行讲解:铁路局发布火车票、用户查询火车票,以及用户购买火车票。
|
||||
|
||||
## 铁路局发布火车票
|
||||
|
||||
铁路局发布火车票的过程,主要涉及分布式数据存储这一站的知识,包括存储系统三要素、数据分布和数据复制等技术。
|
||||
|
||||
铁路局向购票系统发布火车票的过程,主要是将火车票信息发送到服务器集群进行存储,需要用到[存储系统三要素](https://time.geekbang.org/column/article/168076)的相关技术。其中,铁路局就是存储系统三要素中的“顾客”,火车票存储到具体哪个服务器需要构建数据索引,也就是我们说的三要素中的“导购”,而存储数据的服务器就是三要素中的“货架”。
|
||||
|
||||
由于涉及多个服务器存储火车票信息,不可避免地需要考虑服务器之间数据存储的均衡,以及快速确定火车票信息存储位置以方便后续查询和购买火车票。因此,这里需要用到分布式存储中的[数据分布技术](https://time.geekbang.org/column/article/168940)。
|
||||
|
||||
铁路局按照火车线路将火车票发布到不同的服务器上,即在[数据分片技术](https://time.geekbang.org/column/article/168076)中提到的按照数据范围进行分片。比如,京广线的车票信息存储在京广线服务器集群、京沪线的车票信息存储在京沪线服务器集群等。
|
||||
|
||||
除此之外,为了保证可靠性,也就是当一台服务器故障后,该服务器存储的火车票信息可以恢复或不丢失,通常会进行数据备份。也就是说,同一份数据可能会有多台服务器一起存储,比如京广线的火车票数据存储到服务器A1、A2和A3上,京沪线的火车票数据存储在服务器B1、B2和B3上。
|
||||
|
||||
而数据备份,用到的就是分布式存储中的[数据复制技术](https://time.geekbang.org/column/article/168963)。由于同一份数据被多台服务器存储,自然就需要保证数据的一致性。关于数据一致性,你可以参考[CAP理论](https://time.geekbang.org/column/article/166582)这篇文章。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/b7/15/b7e935602c632312d0f49948708dd915.png" alt="">
|
||||
|
||||
接下来,我们再看看用户查询火车票过程中涉及了哪些相关技术吧。
|
||||
|
||||
## 用户查询火车票
|
||||
|
||||
对于用户查询火车票来说,大致过程是用户向服务器发起查询请求,服务器根据用户请求,通过数据索引,也就是[导购技术](https://time.geekbang.org/column/article/168076),定位到火车票信息存储的位置,然后获取数据返回给用户。
|
||||
|
||||
从这个流程可以看出,**服务器接收用户查询请求是第一步**。
|
||||
|
||||
正常情况下,用户并发请求量比较小,很少会出现服务器能力有限导致系统崩溃的情况。但,遇到节假日或春节,用户请求量通常会非常大,这时如果不采取一定策略的话,大概率会因为服务器能力受限导致系统崩溃。
|
||||
|
||||
而这里的策略,通常就是保证分布式高可靠的[负载均衡](https://time.geekbang.org/column/article/173398)和[流量控制](https://time.geekbang.org/column/article/174495)。比如,每年春运,火车票发布的瞬间,就有大量的用户抢票,如果购票系统后台不使用负载均衡和流量控制的话,服务器一下子就被击垮了。
|
||||
|
||||
除此之外,服务器还不可避免地会出现一些小故障,比如磁盘损坏、网络故障等问题。如何检测服务器故障,以及如何进行故障恢复,就需要用到分布式高可用的[故障隔离](https://time.geekbang.org/column/article/175213)与[故障恢复](https://time.geekbang.org/column/article/175545)的相关技术了。
|
||||
|
||||
接收用户请求后,接下来需要将请求转发至相应的服务器集群,然后再从中选择某一台服务器处理用户请求,也就是获取数据并返回给用户。本质上,这就是在进行数据索引,设计分布式数据存储中的数据分布方式的相关技术。
|
||||
|
||||
以用户查询从北京到上海的火车票信息为例,查询流程如下:
|
||||
|
||||
- 首先,根据查询条件,系统将请求转发至存储京沪线火车票信息的服务器集群中;
|
||||
- 然后,服务器集群再使用一次负载均衡,比如使用轮询算法, 将请求转发至某一台服务器;
|
||||
- 最后,这台服务器将火车票的车次、余票等信息返回给用户。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/c0/34/c00924a0cc8b537cbf5d0bc0c6fb2f34.png" alt="">
|
||||
|
||||
在这个过程中,还可以使用限流算法,比如漏桶、令牌桶等策略来限制用户流量,保证系统高可用。
|
||||
|
||||
除此之外,在存储火车票信息的服务器中,各个服务器的服务单独运行,也就相当于做了一定的故障隔离,比如图中的服务器A1~A3、B1~B3。
|
||||
|
||||
当然,这里**还需要注意一个问题**。在上面的过程中,我一直提到的是服务器集群。既然是集群,就会涉及集群架构、分布式选主等策略。
|
||||
|
||||
以集中式架构Master/Slave为例,服务器集群中会通过分布式选举算法选出一个Master,Master和其他服务器节点之间会维持心跳,并通过心跳来感知服务器节点的存活状态。比如,京广线服务器集群选出的Master节点为A2,A2与A1和A3之间一直维持心跳。
|
||||
|
||||
具体的集群架构原理,你可以参考“第二站:分布式资源管理与负载调度”;而分布式选主的原理,你可以再回顾“第一站:分布式协同与同步”的相关内容。
|
||||
|
||||
当集群中Master节点故障后,会从其他节点中重新选举出一个Master节点,继续为用户提供服务,也就是备升主,以保证服务的可用性。这里,备升主就是一种故障恢复策略。
|
||||
|
||||
最后,我们再来分析下用户购买火车票的过程。
|
||||
|
||||
## 用户购买火车票
|
||||
|
||||
用户购买火车票的过程与用户查询火车票的过程非常相似,**唯一不同的是会造成数据库的变化。**换句话说,用户查询火车票是读请求,而购买火车票相当于一个写请求。那么,写请求又会造成什么新问题呢?
|
||||
|
||||
写请求与读请求的区别是,写请求会造成数据的变化,因此相对于查询火车票,购买火车票的过程涉及的技术问题会多一些,但多出的无非就是数据的一致性问题。谈到数据的一致性,我们就会想到[CAP理论](https://time.geekbang.org/column/article/166582)、[数据复制技术](https://time.geekbang.org/column/article/168963)、[分布式事务](https://time.geekbang.org/column/article/144970)、[分布式锁](https://time.geekbang.org/column/article/145505)等。其实,不仅仅是购买火车票,任何一个简单的购买操作或写操作中,都会涉及这些分布式知识。
|
||||
|
||||
本质上讲,每次购买火车票的操作,就是一个分布式事务,要么执行成功要么执行失败。
|
||||
|
||||
当用户购买了火车票时,该火车票对应时间的车次余票数量必须相应减1,如果减少时发现原先票数就已经为0了,此时就应该提醒用户购买火车票失败,余票为0;同样的,如果票数不为0,则票数应该相应减1,并提示用户购票成功。
|
||||
|
||||
不难看出,购买火车票会改变火车票数据,也不可避免地会存在多个用户同时购买相同路线、相同车次(比如京沪线的T12)的场景。也就是说,这个购买过程存在多个进程同时访问共享资源的问题,因此还要用到分布式锁的相关技术。
|
||||
|
||||
另外,**用户购买火车票的过程还会涉及用户体验、数据一致性和网络故障等问题,因此还涉及C、A、P策略的选择问题。**
|
||||
|
||||
在铁路局发布火车票的流程中,为了保证可靠性,同一数据通常会备份到多个服务器上。当用户购买火车票导致火车票数据改变时,主节点上的数据必须与备节点上的数据保持一致,以防止主节点故障后,备升主,但数据不一致导致业务出错的情况。
|
||||
|
||||
比如,用户A购买2019年10月12日北京到上海的T12的火车票,已购买成功,座位号为3车厢23B。假设主节点和备节点之间数据不一致,主节点上已经减去该火车票,但未在备节点上减去。此时,若主节点故障,备节点升主,用户B此时申请购买相同火车票,系统将3车厢23B火车票又卖给了用户B。等到乘车时,用户A和B就难免“打架”了。
|
||||
|
||||
当然,通常因为网络故障或节点故障等原因导致主节点不能正常工作,才会发生备升主,而备升主其实就是故障恢复策略,而一致性问题涉及的是“第五站:分布式数据存储”的相关技术。
|
||||
|
||||
同时,购买火车票的场景需要快速响应用户,以保证用户体验。因此,通常优先保证系统的可用性,稍微降低对数据一致性的要求,但也必须保证最终一致性。这就是我们平常遇到的,查询火车票时还有余票,但下单后却提示余票为0,无法购买。
|
||||
|
||||
导致这个结果的原因是,下单前你访问的数据库中,数据还未同步,显示有余票;而下单后,数据实现同步了,发现余票数量已经为0,因此提示你无法购买该火车票。
|
||||
|
||||
实现上述策略的方法,通常会采用[半同步复制技术](https://time.geekbang.org/column/article/168963),即将修改后的数据同步到多个备数据库中的某一个或几个后立即响应用户,而不用将数据同步到所有备数据库。
|
||||
|
||||
除此之外,业务量很大的情况下,为了让服务更加健壮、低耦合、便于管理,会根据功能拆分为不同的服务。比如,将整个购票系统拆分为订单系统、支付系统和通知系统,而当购票系统拆分为3个子系统后,子系统之间不可避免地存在信息的交互,子系统之间的交互就会涉及分布式通信的相关知识,比如远程调用RPC、消息队列等。
|
||||
|
||||
如图所示,用户购买火车票后,会首先在订单系统下单,下单成功后会调用支付系统的支付操作进行支付,之后将支付成功的消息存放到消息队列中,通知系统到消息队列中获取消息,最后通知用户购买成功。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/13/d3/1331e8274a370f009c3a3d167af46dd3.png" alt="">
|
||||
|
||||
## 总结
|
||||
|
||||
今天,我主要以购买火车票为例,为你串联了分布式技术在实践中的应用。
|
||||
|
||||
为方便理解,我将购买火车票的模型简化为三个核心步骤,即铁路局发布火车票、用户查询火车票和用户购买火车票。
|
||||
|
||||
其次,我分别与你分析了这三个核心步骤涉及的关键的分布式技术。
|
||||
|
||||
对于铁路局发布火车票这个流程来说,铁路局是数据的生产者,需要将数据发布到服务器进行存储,主要涉及的是分布式数据存储相关技术,对应专栏“第五站:分布式数据存储”的内容。
|
||||
|
||||
对于用户查询火车票来说,主要是读请求,涉及负载均衡、流量控制、集群管理及选主等技术,对应专栏“第一站:分布式协调与同步”“第二站:分布式资源管理与负载调度”和“第六站:分布式高可靠”的内容。
|
||||
|
||||
而对于用户购买火车票来说,是写请求,涉及数据的改变,因此除了用户查询火车票涉及的技术外,还额外涉及一致性、分布式事务、远程通信等技术,对应专栏“第一站:分布式协调与同步”、“第四站:分布式通信技术”、“第五站:分布式数据存储”和第六站:“分布式高可用”等内容。
|
||||
|
||||
最后,我再通过一张思维导图来归纳一下今天的核心知识点吧。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/d1/81/d118439f230cfce4779dbb70c9685181.png" alt="">
|
||||
|
||||
## 思考题
|
||||
|
||||
结合购买火车票这个案例,你能和我分享你身边的应用场景或系统,都涉及或采用了哪些分布式技术吗?
|
||||
|
||||
我是聂鹏程,感谢你的收听,欢迎你在评论区给我留言分享你的观点,也欢迎你把这篇文章分享给更多的朋友一起阅读。我们下期再会!
|
||||
@@ -0,0 +1,484 @@
|
||||
<audio id="audio" title="34 | 搭建一个分布式实验环境:纸上得来终觉浅,绝知此事要躬行" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/8a/7a/8a719b0cd98555919aed407576f9607a.mp3"></audio>
|
||||
|
||||
你好,我是聂鹏程。
|
||||
|
||||
上一讲,我以购买火车票为例,为你串讲了分布式技术的应用,帮助你理解所学分布式技术可以应用到哪些业务中。其实,到目前为止,我们主要是从理论上学习相关的分布式技术。但,“纸上得来终觉浅,绝知此事要躬行”。
|
||||
|
||||
今天,我就以Kubernetes为例,和你一起搭建一个分布式实验环境。我先简单和你说下这篇文章的内容分配:
|
||||
|
||||
- 不会特别详细地讲述搭建过程,而是着重说明搭建的主要步骤以及可能遇到的问题;
|
||||
- 在讲述搭建过程时,串联一下其中涉及的分布式相关知识;
|
||||
- 搭建完Kubernetes集群之后,我会以部署Nginx服务为例,帮助你更直观地体验分布式技术,以巩固、加深对分布式技术的理解。
|
||||
|
||||
话不多说,接下来,我们就一起搭建这个分布式实验环境吧。
|
||||
|
||||
## 搭建目标
|
||||
|
||||
Kubernetes是Google开源的容器集群管理系统,是Borg的开源版本。我在[第9篇文章](https://time.geekbang.org/column/article/148187)中讲解集中式架构时,和你分析过Kubernetes集群属于主从架构的分布式集群。
|
||||
|
||||
Kubernetes集群主要由Master节点和Worker节点组成。Master节点就是中心服务器,负责对集群进行调度管理;Worker节点是真正的工作节点,负责运行业务应用的容器。而容器是一种虚拟化技术,通过限制自身使用的资源来实现资源隔离,可以为应用提供一整套运行环境,从而实现了服务运行环境的隔离,进而实现了故障隔离。你可以回顾下[第30篇文章](https://time.geekbang.org/column/article/175213)中,资源隔离的相关内容。
|
||||
|
||||
接下来,我们明确下这次搭建分布式实验室环境的目标:
|
||||
|
||||
- 搭建一个Kubernetes集群,包括一个Master节点,两个Worker节点;
|
||||
- 在Kubernetes集群上创建一个Nginx服务。
|
||||
|
||||
## 搭建前的准备
|
||||
|
||||
今天我们要搭建的Kubernetes集群,以3台服务器为例,一台作为Master节点,两台作为Worker节点。服务器应具备的条件如下:
|
||||
|
||||
- Ubuntu 16.04操作系统;
|
||||
- 2GB或以上的内存;
|
||||
- 2核CPU或以上;
|
||||
- 服务器间网络连通;
|
||||
- 每台服务器具有唯一的主机名、MAC地址和product_uuid;
|
||||
- 通过执行命令swapoff -a来关闭Swap;
|
||||
- 30GB及以上的磁盘空间;
|
||||
- 具备外网访问权限,以方便获取相关镜像。
|
||||
|
||||
在这次部署中,我采用的机器配置如下:
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/a9/5e/a96f67cb7fc7df583f73c098d377f55e.jpg" alt="">
|
||||
|
||||
准备工作完成后,我们就开始搭建集群吧。
|
||||
|
||||
## Kubernetes集群搭建
|
||||
|
||||
搭建Kubernetes集群的步骤,主要包括安装Docker,安装部署kubeadm、kubelet、kubectl,部署Master节点,部署Worker节点,安装网络插件这几步。
|
||||
|
||||
其中,安装Docker、部署Master节点和Worker节点涉及分布式的,需要在多个节点上部署,比如Docker节点需要在每个Worker节点部署,Master节点若为集群模式,需要在多个节点上配置主备,Worker节点需要与Master节点建立连接等。
|
||||
|
||||
接下来, 我们具体看看如何一步一步搭建出Kubernetes集群吧。
|
||||
|
||||
### 1. 安装Docker
|
||||
|
||||
Kubernetes是一个容器集群管理系统,因此每个Worker节点会运行容器,以实现业务运行环境隔离。我们在每台服务器上采用如下命令安装Docker:
|
||||
|
||||
```
|
||||
apt-get install -y docker.io
|
||||
|
||||
```
|
||||
|
||||
### 2. 安装部署kubeadm、kubelet、kubectl
|
||||
|
||||
kubeadm是Kubernetes社区提供的一个部署工具,该工具将kubelet组件之外的其他组件均采用容器部署,实现了自动化, 避免了手动部署容器的麻烦,简化了部署操作。
|
||||
|
||||
其中,Master节点包括API Server、Scheduler、Cluster State Store(默认etcd)和Control Manager Srever核心组件;Worker节点包括kubelet和kube-proxy核心组件。具体的组件功能和原理,你可以再回顾下[第9篇文章](https://time.geekbang.org/column/article/148187)中的相关内容。
|
||||
|
||||
kubelet组件本身是一个管控容器的组件,需要执行配置容器网络等操作,这些操作需要在宿主机上执行,不采用容器部署。因此,kubelet组件需要单独部署,而不能用kubeadm进行部署。
|
||||
|
||||
除此之外,我们还需要安装一下kubectl组件。这个组件是Kubernetes的命令行工具,通过kubectl可以部署和管理应用,查看资源,创建、删除和更新组件。
|
||||
|
||||
那么,如何部署kubeadm、kubelet和kubectl这三个组件呢?
|
||||
|
||||
apt是Linux下常用的安装管理工具,这里我就采用apt来安装这三个组件。
|
||||
|
||||
首先,我们需要添加Kubernetes源。
|
||||
|
||||
- 你可以通过执行以下语句获取Kubernetes源(需要外网权限):
|
||||
|
||||
```
|
||||
sudo apt-get update && sudo apt-get install -y apt-transport-https curl
|
||||
curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo apt-key add -
|
||||
cat <<EOF | sudo tee /etc/apt/sources.list.d/kubernetes.list
|
||||
deb https://apt.kubernetes.io/ kubernetes-xenial main
|
||||
EOF
|
||||
|
||||
```
|
||||
|
||||
然后使用以下命令更新源:
|
||||
|
||||
```
|
||||
sudo apt-get update
|
||||
|
||||
```
|
||||
|
||||
- 这时,我们就可以使用如下命令来安装kubelet、kubectl和kubeadm了:
|
||||
|
||||
```
|
||||
sudo apt-get install -y kubelet kubeadm kubectl
|
||||
|
||||
```
|
||||
|
||||
- 如果没有外网访问权限,在添加kubernetes源的时候可以执行以下命令来添加阿里云的Kubernetes源:
|
||||
|
||||
```
|
||||
cat <<EOF | sudo tee /etc/apt/sources.list.d/kubernetes.list
|
||||
deb https://mirrors.aliyun.com/kubernetes/apt kubernetes-xenial main
|
||||
EOF
|
||||
|
||||
```
|
||||
|
||||
同样的,使用以下命令来更新源:
|
||||
|
||||
```
|
||||
apt-get update # 忽略gpg的报错信息
|
||||
|
||||
```
|
||||
|
||||
最后,使用如下命令安装kubelet、kubectl和kubeadm:
|
||||
|
||||
```
|
||||
apt-get install -y kubelet kubeadm kubectl --allow-unauthenticated
|
||||
|
||||
```
|
||||
|
||||
安装好这三个组件之后,我们就可以使用kubeadm来一键部署集群节点了。
|
||||
|
||||
首先,我们来部署Master节点。
|
||||
|
||||
### 3. 部署Master节点
|
||||
|
||||
这一步其实就是容器化启动Master节点中的各个组件,直接使用kubeadm工具提供的一条命令kubeadm init即可自动安装。
|
||||
|
||||
kubeadm init这条命令底层其实就是将Master的各个组件,比如API Server、etcd等,以Pod形式(容器集合)运行起来。
|
||||
|
||||
当然了,你可以部署多个Master节点来实现集群的高可用,比如两个Master节点互为主备(你可以回顾下[第31篇文章](https://time.geekbang.org/column/article/175545)中介绍的主备机制)。具体的部署方法,你可以参考[这篇文章](https://kubernetes.io/docs/setup/production-environment/tools/kubeadm/high-availability/)。
|
||||
|
||||
除此之外,etcd组件也可以采用集群方式部署,从而保证了数据不会丢失。etcd采用的是Raft强一致性协议,相关技术你可以再回顾下[第4篇文章](https://time.geekbang.org/column/article/143329)中的相关问题。
|
||||
|
||||
**在本次部署中我以一个Master节点为例为你讲解集群的搭建**,关于Master为集群的方式,各节点上kubernetes配置类似,你可以参考Kubernetes官网。
|
||||
|
||||
在这里,我把192.168.124.49这台机器作为Master节点。在该机器上直接执行kubeadm init,即可完成部署:
|
||||
|
||||
```
|
||||
kubeadm init
|
||||
|
||||
```
|
||||
|
||||
如果有外网访问权限,基本就可以部署成功了。那么,我们可以根据如下信息判断自己是否部署成功:
|
||||
|
||||
```
|
||||
Your Kubernetes control-plane has initialized successfully!
|
||||
|
||||
To start using your cluster, you need to run the following as a regular user:
|
||||
|
||||
mkdir -p $HOME/.kube
|
||||
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
|
||||
sudo chown $(id -u):$(id -g) $HOME/.kube/config
|
||||
|
||||
You should now deploy a pod network to the cluster.
|
||||
Run "kubectl apply -f [podnetwork].yaml" with one of the options listed at:
|
||||
https://kubernetes.io/docs/concepts/cluster-administration/addons/
|
||||
|
||||
Then you can join any number of worker nodes by running the following on each as root:
|
||||
|
||||
kubeadm join 192.168.124.49:6443 --token uv17vd.q3ber8i5knxg4h0x \
|
||||
--discovery-token-ca-cert-hash sha256:c55bd70d346d809e1079565cc1fc1a05f001671cc9f2d02c55bbbc4a00bcc2a3
|
||||
|
||||
```
|
||||
|
||||
可以看到,想要使用集群,需要执行以下命令,执行结束后才可以使用kubectl。
|
||||
|
||||
```
|
||||
mkdir -p $HOME/.kube
|
||||
cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
|
||||
chown $(id -u):$(id -g) $HOME/.kube/config
|
||||
|
||||
```
|
||||
|
||||
如果没有外网访问权限,会报pull image xxxxxx的错误。
|
||||
|
||||
此时不要慌,从报错信息中,我们可以看到哪些镜像拉取不成功。我们可以手动在Docker Hub上寻找相对应的组件及版本,进行拉取,然后再通过Docker打tag,修改为需要的镜像。
|
||||
|
||||
比如,以[ERROR ImagePull]: failed to pull image k8s.gcr.io/kube-apiserver:v1.16.3为例,可以通过以下代码进行拉取。
|
||||
|
||||
```
|
||||
# 可以拉取的相对应的组件和版本
|
||||
docker pull aiotceo/kube-apiserver:v1.16.3
|
||||
# 通过打tag的方式修改为所需要的镜像
|
||||
docker tag aiotceo/kube-apiserver:v1.16.3 k8s.gcr.io/kube-apiserver:v1.16.3
|
||||
|
||||
```
|
||||
|
||||
然后重新执行 kubeadm init即可。
|
||||
|
||||
从以上操作也可以看出,**kubeadm的底层其实就是将容器化组件的操作实现了自动化, 省去了手动部署的麻烦**。
|
||||
|
||||
部署完Master节点后,我们来继续部署Worker节点。
|
||||
|
||||
### 4. 部署Worker节点
|
||||
|
||||
部署Worker节点与部署Master节点类似,都可以通过命令一键部署。这里,我们使用kubeadm提供的kubeadm join命令来进行自动化部署。
|
||||
|
||||
kubeadm join命令的底层与kubeadm init类似,会自动以Pod形式运行Worker节点中需要的组件。不同的是,命令执行后,底层还需要将Worker节点加入到Kubernetes集群中。
|
||||
|
||||
执行kubeadm join命令后(具体命令如下所示),就可以看到Kubernetes集群中的节点信息了。这条命令中需要配置Master节点的IP和Port信息,目的是Worker节点根据IP和Port信息建立连接,并在建立连接的基础上,建立心跳机制。
|
||||
|
||||
具体的心跳机制,你可以参考[第31篇文章](https://time.geekbang.org/column/article/175545)中关于故障恢复的内容。
|
||||
|
||||
到目前为止,Kubernetes的集群已经完成大半了,下面我们继续部署集群。
|
||||
|
||||
根据Master节点部署成功后输出结果的最后几行可以知道,想要加入集群,可以执行kubeadm join命令。我在另外2台机器上都执行了如下命令:
|
||||
|
||||
```
|
||||
kubeadm join 192.168.124.49:6443 --token uv17vd.q3ber8i5knxg4h0x \
|
||||
--discovery-token-ca-cert-hash sha256:c55bd70d346d809e1079565cc1fc1a05f001671cc9f2d02c55bbbc4a00bcc2a3
|
||||
|
||||
```
|
||||
|
||||
这条命令执行后,一个集中式架构的雏形就搭建完成了。接下来,我们需要安装相应的网络插件,以实现Kubernetes集群中Pod之间的通信。
|
||||
|
||||
### 5. 安装网络插件
|
||||
|
||||
网络插件有很多,比如Canal、Flannel、Weave等。不同的插件命令不一致,具体命令可参考官网。
|
||||
|
||||
这里,我以安装Weave插件为例,通过执行以下命令完成安装:
|
||||
|
||||
```
|
||||
sysctl net.bridge.bridge-nf-call-iptables=1
|
||||
|
||||
kubectl apply -f "https://cloud.weave.works/k8s/net?k8s-version=$(kubectl version | base64 | tr -d '\n')"
|
||||
|
||||
```
|
||||
|
||||
### 6. 验证
|
||||
|
||||
到这里,集群就部署完成了,是不是很简单呢?接下来,我就通过获取节点和Pod信息来验证一下集群部署是否成功。
|
||||
|
||||
可以通过刚刚安装的kubectl组件提供的命令查看集群的相关信息。比如,查看节点的运行状态可以通过kubectl get nodes来获得,查看各个组件对应的Pod运行状态可以通过kubectl get pods来获得。命令执行结果,如下所示:
|
||||
|
||||
```
|
||||
kubectl get nodes
|
||||
|
||||
# NAME STATUS ROLES AGE VERSION
|
||||
# vm1-pc Ready master 11h v1.16.3
|
||||
# vm2-pc Ready <none> 11h v1.16.3
|
||||
# vm3-pc Ready <none> 24m v1.16.3
|
||||
|
||||
kubectl get pods --all-namespaces
|
||||
|
||||
# NAMESPACE NAME READY STATUS RESTARTS AGE
|
||||
# kube-system coredns-5644d7b6d9-9dprc 1/1 Running 0 11h
|
||||
# kube-system coredns-5644d7b6d9-ljv5w 1/1 Running 0 11h
|
||||
# kube-system etcd-vm1-pc 1/1 Running 0 11h
|
||||
# kube-system kube-apiserver-vm1-pc 1/1 Running 0 11h
|
||||
# kube-system kube-controller-manager-vm1-pc 1/1 Running 0 11h
|
||||
# kube-system kube-proxy-qpvtb 1/1 Running 0 25m
|
||||
# kube-system kube-proxy-v2xnb 1/1 Running 0 11h
|
||||
# kube-system kube-proxy-wkxzg 1/1 Running 0 11h
|
||||
# kube-system kube-scheduler-vm1-pc 1/1 Running 0 11h
|
||||
# kube-system weave-net-6nj4c 2/2 Running 0 25m
|
||||
# kube-system weave-net-lm6dh 2/2 Running 0 37m
|
||||
# kube-system weave-net-vwnc2 2/2 Running 0 37m
|
||||
|
||||
```
|
||||
|
||||
可以看到,节点全部是Ready状态,各个组件对应的Pod也处于Running状态,表明部署成功。
|
||||
|
||||
### 7. 可能遇到的问题
|
||||
|
||||
如果整个安装失败的话,可以重置,重新安装,即重新kubeadm init
|
||||
|
||||
```
|
||||
kubeadm reset
|
||||
|
||||
```
|
||||
|
||||
部署Worker节点时,pod部署不成功。原因可能是因为没有外网访问权限,镜像拉取不下来,可以通过以下命令查看pod的相关信息:
|
||||
|
||||
```
|
||||
# 检查所有pod是否正常
|
||||
kubectl get pod --all-namespaces -o wide
|
||||
#如果pod处于非running状态,则查看该pod:
|
||||
kubectl describe pod xxxxx -n kube-system
|
||||
|
||||
```
|
||||
|
||||
从错误信息里可以查看到是哪个镜像拉取不下来,与部署Master节点时采用的方式一样,到Docker Hub上手动拉取镜像,并设置Tag即可。
|
||||
|
||||
至此,Kubernetes集群就配置成功了。
|
||||
|
||||
集群环境搭建后,如何验证集群是可用的呢?或者说,如何在集群上运行服务呢?接下来,我就以Nginx服务为例,带你了解如何在Kubernetes集群上进行服务部署。当然,你可以参考这个例子,在Kubernetes集群上部署其他服务。
|
||||
|
||||
## Nginx服务部署
|
||||
|
||||
Kubernetes推荐使用YAML配置文件的方式来创建服务,所以我接下来会使用这种方式部署完成Nginx服务的部署。
|
||||
|
||||
部署Nginx服务这个Demo时,我会创建两个Kubernetes对象(Kubernetes对象是Kubernetes系统中的持久实体,用于表示集群的状态),一个是Deployment,一个是Service:
|
||||
|
||||
- Deployment对象规定Pod创建的相关信息,比如期望创建几个Pod,每个Pod应该部署什么应用等。
|
||||
- Service对象用来给用户访问提供接口。它可以通过Label Selector(标签选择器)来指定可以访问的Pod有哪些。关于Kubernetes对象的相关内容,你可以参考[这篇文章](http://docs.kubernetes.org.cn/232.html)。
|
||||
|
||||
因为Pod是Kubernetes中最小的工作单元,所以Nginx服务都部署在Pod中。下面,我就来创建一个Deployment对象来创建我们期望的Pod状态。
|
||||
|
||||
首先,创建一个YAML配置文件,我将其命名为nginx-deployment.yaml。为将用户请求负载均衡到不同的Pod,减轻单个Pod的访问压力,这里我会创建三个Pod共同运行Nginx服务。
|
||||
|
||||
文件内容如下:
|
||||
|
||||
```
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
labels:
|
||||
app: nginx
|
||||
spec:
|
||||
replicas: 3
|
||||
selector:
|
||||
matchLabels:
|
||||
app: nginx
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: nginx
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx:latest
|
||||
ports:
|
||||
- containerPort: 80
|
||||
|
||||
```
|
||||
|
||||
文件中,replicas字段就是副本数量,也就是Pod数量,设置为3,即创建三个Pod来运行Nginx服务;template字段规定了单个Pod中运行哪些容器,这里运行的是名称为nginx的容器。
|
||||
|
||||
- 创建完配置文件后,通过以下命令就可以将Deployment对象创建成功。
|
||||
|
||||
```
|
||||
kubectl apply -f nginx-deployment.yaml
|
||||
|
||||
```
|
||||
|
||||
执行后,就等待对象的创建,可以通过以下命令来查看创建是否成功。
|
||||
|
||||
```
|
||||
kubectl get deployment
|
||||
|
||||
```
|
||||
|
||||
以下是我创建成功后的输出:
|
||||
|
||||
```
|
||||
NAME READY UP-TO-DATE AVAILABLE AGE
|
||||
nginx-deployment 3/3 3 3 3m17s
|
||||
|
||||
```
|
||||
|
||||
同时,你也可以通过以下命令来查看创建的Pod的信息:
|
||||
|
||||
```
|
||||
kubectl get pod
|
||||
|
||||
```
|
||||
|
||||
以下是我的输出结果:
|
||||
|
||||
```
|
||||
NAME READY STATUS RESTARTS AGE
|
||||
nginx-deployment-59c9f8dff-dtg4w 1/1 Running 0 3m15s
|
||||
nginx-deployment-59c9f8dff-f2hmv 1/1 Running 0 3m15s
|
||||
nginx-deployment-59c9f8dff-lsvdh 1/1 Running 0 3m15s
|
||||
|
||||
```
|
||||
|
||||
创建完deployment之后,我们来创建Service服务。同样是通过配置文件来创建,文件名是nginx-service.yaml,内容如下:
|
||||
|
||||
```
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: nginx-service
|
||||
labels:
|
||||
app: nginx
|
||||
spec:
|
||||
ports:
|
||||
- port: 88
|
||||
targetPort: 80
|
||||
selector:
|
||||
app: nginx
|
||||
type: NodePort
|
||||
|
||||
```
|
||||
|
||||
文件中port属性就是service对外提供的端口。
|
||||
|
||||
同样的,采用kubectl apply命令创建Nginx服务:
|
||||
|
||||
```
|
||||
kubectl apply -f nginx-service.yaml
|
||||
|
||||
```
|
||||
|
||||
执行完成后,可以通过以下命令来查看创建是否成功:
|
||||
|
||||
```
|
||||
kubectl get service
|
||||
|
||||
```
|
||||
|
||||
以下是我的输出结果:
|
||||
|
||||
```
|
||||
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
|
||||
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 12h
|
||||
nginx-service NodePort 10.101.29.9 <none> 88:30755/TCP 5m12s
|
||||
|
||||
```
|
||||
|
||||
现在我们就可以通过访问Nginx服务来查看它是否部署成功了。访问该服务可以通过以下命令:
|
||||
|
||||
```
|
||||
curl 10.101.29.9:88
|
||||
|
||||
```
|
||||
|
||||
结果如下,表明Nginx服务部署成功。
|
||||
|
||||
```
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Welcome to nginx!</title>
|
||||
<style>
|
||||
body {
|
||||
width: 35em;
|
||||
margin: 0 auto;
|
||||
font-family: Tahoma, Verdana, Arial, sans-serif;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Welcome to nginx!</h1>
|
||||
<p>If you see this page, the nginx web server is successfully installed and
|
||||
working. Further configuration is required.</p>
|
||||
|
||||
<p>For online documentation and support please refer to
|
||||
<a href="http://nginx.org/">nginx.org</a>.<br/>
|
||||
Commercial support is available at
|
||||
<a href="http://nginx.com/">nginx.com</a>.</p>
|
||||
|
||||
<p><em>Thank you for using nginx.</em></p>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
```
|
||||
|
||||
在这个过程中,有两个步骤涉及[负载均衡](https://time.geekbang.org/column/article/173398)的相关知识:
|
||||
|
||||
- 一个是创建Deployment时,该Deployment会创建三个Pod,而Pod需要部署到某个Worker节点中,因此会将Pod均衡部署到各个Worker节点中;
|
||||
- 另一个是用户访问,Nginx服务后台三个运行的Pod都可以提供服务,用户访问到来时,可以均衡分布到各个Pod中进行处理。
|
||||
|
||||
到这里,我们搭建的目标就完成了,下面为你留几个实验题,你可以尝试去搭建一下或运行一下,以进一步加深对分布式技术的理解。
|
||||
|
||||
- 实验一:搭建高可用Kubernetes集群,也就是通过etcd实现Master节点以集群模式部署。具体搭建方法可参考[https://kubernetes.io/docs/setup/production-environment/tools/kubeadm/high-availability/](https://kubernetes.io/docs/setup/production-environment/tools/kubeadm/high-availability/);
|
||||
- 实验二:在Kubenetes上部署Cassandra,其中Cassandra作为服务部署到容器中,以学习Cassandra集群的节点发现、集群组件等原理,具体搭建方法可参考[https://kubernetes.io/docs/tutorials/stateful-application/cassandra/](https://kubernetes.io/docs/tutorials/stateful-application/cassandra/);
|
||||
- 实验三:在Kubenetes集群上通过部署一个MySQL服务,体验在Kubenetes集群上如何运行一个单实例有状态应用。具体搭建方法可参考[https://kubernetes.io/docs/tasks/run-application/run-single-instance-stateful-application/](https://kubernetes.io/docs/tasks/run-application/run-single-instance-stateful-application/)。
|
||||
|
||||
好了,整个搭建环境,我就讲到这里。
|
||||
|
||||
其实,到这里,对分布式世界的探索可以说才刚开始,只有动手去实践,你学到的知识才能真正转化为你自己的。加油,赶紧行动起来吧。
|
||||
|
||||
## 总结
|
||||
|
||||
今天,我主要带你学习了搭建分布式实验环境。
|
||||
|
||||
首先,我以Kubernetes为例,介绍了如何搭建 Kubernetes集群环境,其中包括容器、Master节点、Worker节点等配置和安装。
|
||||
|
||||
然后,在搭建好的Kubernetes集群的基础上,我以Nginx服务为例,展示了如何在Kubernetes集群上部署服务。
|
||||
|
||||
其实,今天我演示的Demo只是冰山一角。在Kubernetes中,有很多非常实用的功能,比如Kubernetes可以让服务持续不断运行,当有Pod出现故障时,会自动重启另一个Pod来达到Deployment配置文件中规定的期望状态;还可以自动实现版本更迭等。
|
||||
|
||||
相信通过本讲的学习,你会对分布式技术有更进一步的认知。加油,赶紧行动起来,为你的服务搭建一个分布式实验环境吧。
|
||||
|
||||
我是聂鹏程,感谢你的收听,欢迎你在评论区给我留言分享你的观点,也欢迎你把这篇文章分享给更多的朋友一起阅读。我们下期再会!
|
||||
Reference in New Issue
Block a user