若是谁均可以在三个小时内学会Kubernetes,银行为什么要为这么简单的东西付一大笔钱?
若是你心存疑虑,我建议你不妨跟着我试一试!在完成本文的学习后,你就能在Kubernetes集群上运行基于微服务的应用程序。我之因此能保证这一点,是由于我就是这么向客户介绍Kubernetes的。 html
这份指南与其余文章有何不一样之处?
至关多!大多数指南是从Kubernetes概念和kubectl命令这类简单的东西开始的。它们假定读者熟悉应用程序开发、微服务和Docker容器。
而在咱们这篇文章中,步骤是: 前端
按部就班的过程为普通人掌握Kubernetes的 简易性提供了必要的深度。没错,当你了解使用Kubernetes的上下文时,一切就变得很简单了。废话很少说,接下来看看咱们所要构建的东西。 java
该应用程序只有一个功能:它以一个句子做为输入,使用文本分析计算出句子的情感值。 node
图1. 情绪分析Web应用程序
从技术角度来看,这个应用程序由三个微服务组成。每一个微服务都具备一个特定功能: python
微服务不是孤立存在的,它们能够实现“关注点分离”,但仍是须要互相交互,理解这一点很是重要。 nginx
图2. 情绪分析WebApp中的数据流
经过展现数据在它们之间的流动方式是对这种交互最好的说明: git
全部这些应用程序的代码均可以在 本仓库中找到。建议读者如今将代码克隆下来,由于咱们即将一块儿构建出很棒的东西。 github
咱们须要启动全部三项服务。就从最具吸引力的前端应用程序开始吧。 web
要启动React应用程序,你须要在计算机上安装NodeJS和NPM。安装完后,使用终端定位到sa-frontend目录。输入如下命令: docker
npm install
这会下载该React应用程序依赖的全部JavaScript,并将其放置在node_modules文件夹中(依赖关系定义在package.json文件中)。在处理完全部依赖关系后,执行下一条命令:
npm start
搞定!咱们的React应用程序启动了,默认状况下你能够经过localhost:3000对其进行访问。你可随意修改代码并当即在浏览器上看到效果,这是经过模块热替换(Hot Module Replacement)实现的,从而大大下降了前端开发的难度!
在生产环境中,咱们须要将应用程序构建成静态文件,并使用Web服务器提供访问。
要构建该React应用程序,请在终端中定位到sa-frontend目录。而后执行如下命令:
npm run build
这会在项目树中生成一个名为build的文件夹。该文件夹包含了咱们的ReactJS应用程序所需的全部静态文件。
安装并启动Nginx服务器( 指南)。而后将sa-frontend/build文件夹的内容移到[nginx安装目录]/html中。
这样,生成的index.html文件能够在[nginx安装目录]/html/index.html(这是Nginx的默认访问文件)中访问到。
默认状况下,Nginx服务器会监听80端口。可经过修改[nginx安装目录]/conf/nginx.conf文件中的server.listen参数来指定其余端口。
使用浏览器打开localhost:80,ReactJS应用程序将会出现。
图3. Nginx提供的React应用程序页面
在“Type your sentence.”字段中进行输入,而后按下Send按钮,将返回一个404错误(可在浏览器控制台中查看)。但为何会这样呢?咱们来看一下代码。
在App.js文件中咱们能够看到,按下Send按钮会触发analyzeSentence方法。该方法的代码以下所示(使用#注释的各行在脚本下方进行说明):
analyzeSentence() { fetch('http://localhost:8080/sentiment', { // #1 method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ sentence: this.textField.getValue()})// #2 }) .then(response => response.json()) .then(data => this.setState(data)); // #3 }
#1:进行POST调用的URL(应用程序会监听该URL的调用)。
#2:发送给应用程序的请求主体,以下所示:
{ sentence: “I like yogobella!” }
#3:使用响应来更新组件状态。这会触发组件的从新渲染。在收到的数据(即包含输入句子及其情感值的JSON对象)后,以下定义的polarityComponent组件的条件获得了知足,从而进行了显示:
const polarityComponent = this.state.polarity !== undefined ? <Polarity sentence={this.state.sentence} polarity={this.state.polarity}/> : null;
一切看起来都是对的。那么咱们遗漏什么了?若是你猜是由于咱们没有设置任何东西来监听localhost:8080,那么恭喜你,答对了!咱们须要启动Spring Web应用程序来监听该端口!
图4. Spring WebApp微服务缺失
要启动该Spring应用程序,你须要安装JDK8和Maven(还须要设置其环境变量)。安装完成后,咱们继续进行下一步。
在终端中定位到sa-webapp目录并输入如下命令:
maven install
这将在sa-webapp目录中生成一个名为target的文件夹。咱们的Java应用程序会被打包成'sentiment-analysis-web-0.0.1-SNAPSHOT.jar'放在target文件夹中。
定位到target目录并使用如下命令启动该应用程序:
java -jar sentiment-analysis-web-0.0.1-SNAPSHOT.jar
糟糕!咱们碰到了一个错误。应用程序启动失败了,惟一的线索是堆栈跟踪中的异常信息:
Error creating bean with name 'sentimentController': Injection of autowired dependencies failed; nested exception is java.lang.IllegalArgumentException: Could not resolve placeholder 'sa.logic.api.url' in value "${sa.logic.api.url}"
这里面的重点信息是SentimentController中的 sa.logic.api.url
占位符。咱们来看一下!
@CrossOrigin(origins = "*") @RestController public class SentimentController { @Value("${sa.logic.api.url}") // #1 private String saLogicApiUrl; @PostMapping("/sentiment") public SentimentDto sentimentAnalysis( @RequestBody SentenceDto sentenceDto) { RestTemplate restTemplate = new RestTemplate(); return restTemplate.postForEntity( saLogicApiUrl "/analyse/sentiment", // #2 sentenceDto, SentimentDto.class) .getBody(); } }
sa.logic.api.url
参数定义。
Spring默认的参数源是application.properties(位于 sa-webapp/src/main/resources中)。可是,它不是定义参数的惟一方法,也可使用前面的命令来实现:
java -jar sentiment-analysis-web-0.0.1-SNAPSHOT.jar --sa.logic.api.url=SA.LOGIC.API的路径
这个参数要使用咱们的Python应用程序运行的地址进行初始化,这样就可让Spring Web应用程序知道在运行时要将消息转发到哪里。
为了简单起见,咱们将在localhost:5000上运行Python应用程序。请记住这一点!
运行以下命令,咱们就能够转移到最后一个服务——Python应用程序。
java -jar sentiment-analysis-web-0.0.1-SNAPSHOT.jar --sa.logic.api.url=http://localhost:5000
要启动Python应用程序,咱们须要安装Python3和Pip(还须要设置其环境变量)。
在终端中定位到sa-logic/sa目录( 仓库)并输入如下命令:
python -m pip install -r requirements.txt python -m textblob.download_corpora
使用Pip安装依赖项后,咱们便可经过执行如下命令来启动应用程序:
python sentiment_analysis.py * Running on http://localhost:5000/ (Press CTRL+C to quit)
如今,咱们的应用程序已经启动并在localhost的5000端口上监听HTTP请求。
咱们来检查一下代码以了解SA Logic这个Python应用程序中发生了什么。
from textblob import TextBlob from flask import Flask, request, jsonify app = Flask(__name__) #1 @app.route("/analyse/sentiment", methods=['POST']) #2 def analyse_sentiment(): sentence = request.get_json()['sentence'] #3 polarity = TextBlob(sentence).sentences[0].polarity #4 return jsonify( #5 sentence=sentence, polarity=polarity ) if __name__ == '__main__': app.run(host='localhost', port=5000) #6
这些服务被设置为相互通讯。在继续前请从新打开前端localhost:80,并尝试输入句子!
图6. 微服务架构完成了
在下一节中,咱们将继续介绍如何在Docker容器中启动服务,由于这是在Kubernetes集群中运行服务的先决条件。
Kubernetes是一个容器编排器。很显然,咱们须要容器以便对其进行编排。那么什么是容器?这从Docker的文档中能够获得最好的回答。
容器镜像是一个轻量级、独立的可执行软件包,它包含了软件运行所需的所有内容:代码、运行时、系统工具、系统库、设置等等。不管是基于Linux或是Windows的应用,容器化的软件在任何环境中都能一样运行。
这意味着容器能够在包括生产服务器的任何计算机上无差异地运行。
为了便于说明,咱们来比较一下如何使用虚拟机与容器来提供咱们的React应用程序服务。
使用虚拟机的缺点:
图7. 虚拟机上的提供静态文件访问的Nginx Web服务器
图8. 容器中提供静态文件访问的Nginx Web服务器
这些是使用容器最显着的特色和好处。有关容器的更多信息,可阅读 Docker文档。
Docker容器的基础构建块是Dockerfile。Dockerfile开头是一个基础容器镜像,后面是有关构建知足应用程序需求的新容器镜像的一系列指令。
在开始定义Dockerfile以前,回忆一下使用Nginx提供静态文件访问的步骤:
在下一节中,你会发现建立一个容器与在本地React设置中所作的极其相似。
SA-Frontend的Dockerfile是执行两个步骤的指令。Nginx团队已经提供了一个Nginx 基础镜像,咱们只要在它的基础上进行构建便可。这两个步骤是:
转换成Dockerfile看起来是这样的:
FROM nginx COPY build /usr/share/nginx/html
是否是很酷?这个文件具备很好的可读性,复述起来就是:
从Nginx镜像开始(无论那些家伙在里面作了什么),将build目录复制到镜像中的nginx/html目录。完成!
你可能会问,怎么知道build文件要复制到哪里?也就是 /usr/share/nginx/html
。很简单,查看Docker Hub上Nginx 镜像的文档。
在推送镜像以前,咱们须要一个容器Registry来托管容器。Docker Hub是一个免费的云容器服务,咱们将用其进行演示。在继续以前,你有三项任务:
docker login -u="$DOCKER_USERNAME" -p="$DOCKER_PASSWORD"
完成上述任务后,定位到sa-frontend目录。而后执行如下命令(请把$DOCKER_ID_USER替换成你的Docker Hub用户名,好比rinormaloku/sentiment-analysis-frontend):
docker build -f Dockerfile -t $DOCKER_ID_USER/sentiment-analysis-frontend .
-f Dockerfile
能够省略,由于咱们正处在包含Dockerfile的目录中。
要推送镜像,请使用docker push命令:
docker push $DOCKER_USER_ID/sentiment-analysis-frontend
在你的Docker Hub仓库中验证一下镜像是否已推送成功。
如今,任何人均可以拉取并运行 $DOCKER_USER_ID/sentiment-analysis-frontend
中的镜像:
docker pull $DOCKER_USER_ID/sentiment-analysis-frontend docker run -d -p 80:80 $DOCKER_ID_USER/sentiment-analysis-frontend
咱们的Docker容器运行起来了!
在继续以前,须要说明一下易发生混淆的80:80:
图9. 宿主机到容器的端口映射
它将<宿主机端口>映射到<容器端口>上。这意味着对宿主机80端口的请求将被映射到容器的80端口中,如图9所示。
因为这个端口运行在宿主机(你的计算机)的80端口上,所以能够经过localhost:80进行访问。若是没有原生的Docker支持,你能够经过<docker-machine ip>:80打开该应用程序。执行 docker-machine ip
可得到docker-machine的IP地址。
动手试试!你应该可以经过它访问到咱们的React应用程序了。
正如咱们以前所见,构建SA-Frontend的镜像速度很慢,很是很是慢。这是由于构建时须要将构建的上下文发送给Docker守护进程。更具体地说,构建上下文表示的是Dockerfile所在目录中构建镜像所需的全部数据。在咱们的示例中,SA前端包含如下文件夹:
sa-frontend: | .dockerignore | Dockerfile | package.json | README.md ---build ---node_modules ---public \---src
但咱们须要的惟一数据都在build文件夹中,上传其余内容是在浪费时间。咱们能够经过忽略其余目录来减小构建时间。这就是 .dockerignore
的做用。对你而言这个文件应该不陌生,由于它与 .gitignore
相似,只要将全部想忽略的目录都添加到 .dockerignore
文件中便可,其内容以下所示:
node_modules src public
.dockerignore
文件应与Dockerfile位于同一文件夹中。如今构建镜像只须要几秒钟。
接下来继续说明Java应用程序。
你猜怎么了!你几乎已经学会了建立容器镜像的全部内容!所以这个部分很是短。
打开sa-webapp中Dockerfile,你会发现有两个新的关键字:
ENV SA_LOGIC_API_URL http://localhost:5000 … EXPOSE 8080
ENV
关键字在Docker容器中声明了一个环境变量。这让咱们能够在启动容器时指定情绪分析API的URL。
其次, EXPOSE
关键字暴露了咱们稍后要访问的端口。可是,咱们在SA-Frontend的Dockerfile中并无这么作。好问题!缘由是它仅用于文档目的,换句话说,它仅仅是为了给阅读Dockerfile的人提供相关信息用的。
你对容器镜像的构建和推送应该比较熟悉了。若是遇到任何困难,请阅读sa-webapp目录中的README.md文件。
sa-logic的Dockerfile中没有新的关键字。如今你能够称本身为Docker专家了。
要构建和推送容器镜像,请阅读sa-logic目录中的README.md。
你会信任没有通过测试的东西吗?我是不会。接下来让咱们对这些容器进行一轮测试。
docker run -d -p 5050:5000 $DOCKER_ID_USER/sentiment-analysis-logic
docker run -d -p 8080:8080 $DOCKER_USER_ID/sentiment-analysis-web-app -e SA_LOGIC_API_URL='http://localhost:5050'
docker run -d -p 80:80 $DOCKER_ID_USER/sentiment-analysis-frontend
完成!打开你的浏览器访问localhost:80吧。
注意:若是你修改了sa-webapp的端口,或者使用的是docker-machine ip,则须要修改sa-frontend中App.js文件的analyzeSentence方法,以便重新的IP或端口获取数据。而后你须要从新构建,并使用更新后的镜像。
图10. 运行在容器中的微服务
在本节中,咱们了解了Dockerfile、如何使用它来构建镜像以及将其推送到Docker Registry的命令。此外,咱们还学会了如何经过忽略无用文件以减小发送给构建上下文的文件数量。最后,咱们在容器中将应用程序运行了起来。那么为何要使用Kubernetes?这一点将在下一节中深刻探讨,你不妨先思考一下:
我能够保证且绝不夸张地说,到文章结束时,你会问本身:“为什么咱们不称它为Supernetes(译者注:借用Superman)?”
图11. Supernetes
本文从开始到如今,已经覆盖了如此多的背景和知识。你可能担忧如今会是最难的部分,但实际上它最简单。学习Kubernetes之因此使人生畏,惟一缘由是由于“其余的一切”,而咱们已经很好地学完了这些东西。
在从容器启动咱们的微服务以后,咱们碰到了一个问题,下面咱们以问答形式进一步阐述它:
问:如何进行容器扩展?
答:再启动一个。
问:如何在它们之间分担负载?若是服务器已经使用到极限,而且咱们的容器须要另外一台服务器,该怎么办?如何计算最佳的硬件利用率?
答:啊……呃……(我Google一下)。
问:如何在不影响任何内容的状况下进行更新?若是须要,该如何回退到可工做的版本?
Kubernetes解决了全部这些问题(以及其余更多问题!)。我能够用一句话来介绍Kubernetes:“Kubernetes是一个容器编排器,它对底层基础设施(容器运行的地方)进行了抽象。”
咱们对容器编排器有一个模糊的概念。后文的实践中会看到它,但这是咱们第一次说到“对底层基础设施进行抽象”,这点须要作个详细说明。
Kubernetes提供了一套简单的用于发送请求的API,对底层基础设施进行抽象。Kubernetes会尽其最大能力来知足这些请求。好比说,能够简单地请求“Kubernetes启动4个x镜像的容器”,而后Kubernetes会找出利用率不足的节点,在这些节点中启动新的容器(参见图12)。
图12. 发送到API服务器的请求
对开发者这意味着什么?意味着他无须关心节点的数量、容器在哪启动以及它们之间如何通讯。他无须处理硬件优化,也无须担忧节点会宕机(根据墨菲定律,节点必定会宕机),由于他能够将新节点添加到Kubernetes集群中。Kubernetes会在正常运转的节点上启动容器。它会尽其最大能力来作到这一点。
在图12中,咱们能够看到一些新的东西:
对Kubernetes的介绍到此为止,再深刻的话会分散咱们的关注点,若有须要有大量有用的资源可供你们学习,好比官方文件(困难模式)或是Marko Lukša编写的《Kubernetes in Action》。
Kubernetes强力推进的另外一个项是,将云服务提供商(CSP)标准化。这是一个大胆的声明,咱们用一个例子来讲明:
– 某个Azure、Google云平台或其余CSP的专家在一个全新的CSP中开展项目,但他对此毫无经验。这会形成不少后果,好比可能会错过最后期限;公司可能须要购买更多资源等等。
但这对Kubernetes根本不是问题。由于不管是哪一个CSP,发送给API服务器的执行命令都是相同的。你以声明方式从API服务器请求所须要的东西,Kubernetes抽象并实现了CSP的对该请求的动做。
很显然,这是个很是强大的功能。对于公司来讲,这意味着不须要与CSP捆绑在一块儿。只须要计算出在另外一个CSP上的支出,而后就能够进行迁移。专业知识、资源都还在,并且能够更便宜!
说了这么多,下一节咱们将把Kubernetes付诸实践。
把微服务运行在容器中,虽然行得通,可是设置过程至关繁琐。咱们还提到这个解决方案不具备可扩展性和弹性,而Kubernetes则可解决这些问题。本文后续部分,咱们会把服务迁移成图13所示的最终结果,由Kubernetes来编排容器。
图13. 运行在Kubernetes管理的集群中的微服务
本文将使用Minikube进行本地调试,不过全部东西在Azure和Google云平台中均可正常工做。
请按照官方文档来安装 Minikube。在Minikube安装过程当中,还将安装Kubectl。这是向Kubernetes API服务器发出请求的客户端。
执行命令 minikube start
来启动Minikube,完成后执行 kubectl get nodes
可得到以下输出:
kubectl get nodes NAME STATUS ROLES AGE VERSION minikube Ready <none> 11m v1.9.0
Minikube提供了一个只有一个节点的Kubernetes集群,但别忘了咱们并不关心节点的数量,Kubernetes已经将其抽象掉了,而且这对学习Kubernetes并不重要。在下一节中,咱们将从咱们的第一个Kubernetes资源——Pod开始。
我喜欢容器,如今你也喜欢容器。那么为何Kubernetes决定把Pod看成最小的可部署计算单元呢?Pod是作什么的?Pod能够由一个甚至是一组共享相同运行环境的容器组成。
有须要在一个Pod中运行两个容器么?通常会像本示例所作的这样,一个Pod中只运行一个容器。可是,若是两个容器须要共享数据卷,或者它们须要进行进程间通讯,或者以其余方式紧密耦合,用Pod就能作到。另一点,Pod可让咱们不受Docker容器的限制,若是须要的话,咱们可使用其余技术来实现,好比 Rkt。
图14. Pod属性
总结来讲,Pod的主要属性是(如图14所示):
*容器拥有独立的文件系统,不过它们可使用Kubernetes资源卷来共享数据。
对于咱们来讲这些信息已经足够了,若是你想了解更多,请查看 官方文档。
如下是第一个Pod sa-front的清单(manifest)文件,后面是对各个要点的解释。
apiVersion: v1 kind: Pod # 1 metadata: name: sa-frontend # 2 spec: # 3 containers: - image: rinormaloku/sentiment-analysis-frontend # 4 name: sa-frontend # 5 ports: - containerPort: 80 # 6
上述Pod定义在 resource-manifests/sa-frontend-pod.yaml
文件中。你能够在终端中定位其所在目录,或者在命令行中提供完整路径。而后执行如下命令:
kubectl create pod -f sa-frontned-pod.yaml pod "sa-frontend" created
要检查Pod是否正在运行,请执行如下命令:
kubectl get pods NAME READY STATUS RESTARTS AGE sa-frontend 1/1 Running 0 7s
若是其状态还是ContainerCreating,则可使用 --watch
参数执行上述命令,以便在Pod处于Running状态时得到更新信息。
为了从外部访问应用程序,正常来讲,咱们须要建立一个Service类型的Kubernetes资源(这是后文的内容),但为了快速调试,咱们使用另外一个方法,也就是端口转发:
kubectl port-forward sa-frontend-pod 88:80 Forwarding from 127.0.0.1:88 -> 80
在浏览器中打开127.0.0.1:88便可访问咱们的React应用程序。
咱们说过Kubernetes的主要功能之一是可扩展性,为了说明这一点,咱们来运行另外一个Pod。为此,建立另外一个Pod资源,其定义以下:
apiVersion: v1 kind: Pod metadata: name: sa-frontend2 # The only change spec: containers: - image: rinormaloku/sentiment-analysis-frontend name: sa-frontend ports: - containerPort: 80
经过执行如下命令来建立新的Pod:
kubectl create pod -f sa-frontned-pod2.yaml pod "sa-frontend2" created
经过执行如下命令验证第二个Pod是否正在运行:
kubectl get pods NAME READY STATUS RESTARTS AGE sa-frontend 1/1 Running 0 7s sa-frontend2 1/1 Running 0 7s
如今有两个Pod在运行了!
注意:这不是最终的解决方案,它的问题不少。咱们将在Kubernetes的Deployment资源环节对此进行改进。
提供静态文件服务的Nginx Web服务器运行在两个不一样的Pod中。如今有两个问题:
图15. 服务之间的负载平衡
Kubernetes为此提供了Service资源。咱们在下一节对其进行说明。
Kubernetes的Service资源为提供相同功能服务的一组Pod充当入口。如图16所示,此类资源负责发现服务和负载平衡,任务繁重。
图16. Kubernetes Service维护着IP地址
咱们的Kubernetes集群是由多个具备不一样功能性服务的Pod组成的(前端、Spring WebApp和Flask Python应用程序)。那么问题来了,Service如何知道要定位到哪些Pod?也就是说,它如何生成Pod的端点列表?
答案是,经过标签(Labels)来实现的,这是一个两步过程:
使用图片更易于理解:
图17. 带有标签的Pod及其清单
能够看到,Pod被打上了“app:sa-frontend”标签,而Service也使用同一标签来定位Pod。
标签为组织Kubernetes资源提供了一种简单的方法。它们的表现形式为键值对,能够应用于全部资源。修改Pod的清单文件以匹配此前图17中所示的示例。
完成修改后保存文件,并使用如下命令来应用:
kubectl apply -f sa-frontend-pod.yaml Warning: kubectl apply should be used on resource created by either kubectl create --save-config or kubectl apply pod "sa-frontend" configured kubectl apply -f sa-frontend-pod2.yaml Warning: kubectl apply should be used on resource created by either kubectl create --save-config or kubectl apply pod "sa-frontend2" configured
这里出现了一个警告(apply与create的不一样)。下一行能够看到“sa-frontend”和“sa-frontend2”Pod配置好了。咱们能够经过过滤要显示的Pod来验证Pod是否已打上标签:
kubectl get pod -l app=sa-frontend NAME READY STATUS RESTARTS AGE sa-frontend 1/1 Running 0 2h sa-frontend2 1/1 Running 0 2h
验证Pod被打上标签的另外一种方法是在上述命令中附加 --show-labels
参数。这将显示每一个Pod的全部标签。
好极了!咱们的Pod都被打上标签了,接下来能够用咱们的Service来定位它们了。下面开始定义如图18所示的LoadBalancer类型的Service。
图18. 使用LoadBalancer Service实现负载平衡
Loadbalancer Service的YAML定义以下所示:
apiVersion: v1 kind: Service # 1 metadata: name: sa-frontend-lb spec: type: LoadBalancer # 2 ports: - port: 80 # 3 protocol: TCP # 4 targetPort: 80 # 5 selector: # 6 app: sa-frontend # 7
请执行如下命令建立该Service:
kubectl create -f service-sa-frontend-lb.yaml service "sa-frontend-lb" created
可经过执行如下命令来检查Service的状态:
kubectl get svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE sa-frontend-lb LoadBalancer 10.101.244.40 <pending> 80:30708/TCP 7m
外部IP处于pending状态(不用等了,它不会变的)。这是由于咱们使用的是Minikube。若是咱们在Azure或GCP这样的云提供商中执行该操做,将得到一个公网IP,以便在全球范围内访问咱们的服务。
尽管如此,咱们不会所以受阻,Minikube为本地调试提供了一个有用的命令,执行如下命令:
minikube service sa-frontend-lb Opening kubernetes service default/sa-frontend-lb in default browser...
这会在你的浏览器中打开Service的IP地址。在Service收到请求后,它会将其转发给其中一个Pod(哪个可有可无)。这种抽象让咱们可使用Service做为入口将多个Pod做为一个单元来看待和交互。
本节咱们介绍了标签资源,将它们用于Service的筛选器,同时咱们定义并建立了一个LoadBalancer Service。这知足了咱们扩展应用程序的需求(只需添加新的打上标签的Pod)并使用Service做用入口实现Pod之间的负载均衡。
应用程序是在不断变化的,Kubernetes的Deployment负责保证这些应用的一致。惟有那些死掉的应用程序才不会改变,不然新的需求会出现,更多的代码会被发布、打包并部署。在这个过程的每一步中,均可能犯错误。
Deployment资源能够自动迁移应用程序版本,实现零停机,而且能够在失败时快速回滚到前一版本。
目前,咱们有两个Pod和一个用于暴露这两个Pod并在它们之间作负载均衡的Service(参见图19)。咱们以前说过,单独部署这些Pod并不是理想的方案。它要求每一个Pod进行单独管理(建立、更新、删除及监控其健康情况)。快速更新和回滚更是不可能!这是没法接受的,而Kubernetes的Deployment资源则解决了这些问题。
图19. 当前状态
在继续以前,我说明咱们想要实现的目标,由于这将为咱们提供一个总览,让咱们可以理解Deployment资源的清单定义。咱们想要的是:
app: sa-frontend
标签,以便sa-frontend-lb Service能发现这些服务
在下一节中,咱们将把这些需求转化成一个Deployment定义。
实现上述全部需求的YAML资源定义:
apiVersion: extensions/v1beta1 kind: Deployment # 1 metadata: name: sa-frontend spec: replicas: 2 # 2 minReadySeconds: 15 strategy: type: RollingUpdate # 3 rollingUpdate: maxUnavailable: 1 # 4 maxSurge: 1 # 5 template: # 6 metadata: labels: app: sa-frontend # 7 spec: containers: - image: rinormaloku/sentiment-analysis-frontend imagePullPolicy: Always # 8 name: sa-frontend ports: - containerPort: 80
app: sa-frontend
是使用该模板建立出来的Pod所使用的标签。
老实说,即使是我也会被这堆文字弄糊涂,咱们直接用这个示例入手:
kubectl apply -f sa-frontend-deployment.yaml deployment "sa-frontend" created
跟以前同样,咱们来验证一下一切是否正常:
kubectl get pods NAME READY STATUS RESTARTS AGE sa-frontend 1/1 Running 0 2d sa-frontend-5d5987746c-ml6m4 1/1 Running 0 1m sa-frontend-5d5987746c-mzsgg 1/1 Running 0 1m sa-frontend2 1/1 Running 0 2d
如今运行了4个Pod,有两个是由Deployment部署的,另外两个是咱们手动建立的。可以使用命令kubectl delete pod <Pod名>
来删除手动建立的那两个。
练习:删除Deployment部署的一个Pod,看看会发生什么。并在阅读下面的解释以前思考一下缘由。
说明:删除一个Pod后Deployment将发现当前状态(运行着1个Pod)与预期状态不一样(运行着2个Pod),所以它会再启动一个Pod。
除了保证预期状态以外,Deployment还有什么好处?下面咱们一一来看下它的优势:
产品经理提出了一项新的需求:客户但愿在前端有一个绿色按钮。开发人员发布完代码,而后提供了咱们惟一须要的东西,即容器镜像 rinormaloku/sentiment-analysis-frontend:green
。如今轮到咱们了,做为DevOps,咱们必须实现零停机时间部署,前面的努力值得么?让咱们拭目以待!
编辑 deploy-frontend-pods.yaml
文件,修改容器镜像来引用新的镜像:rinormaloku/sentiment-analysis-frontend:green
。保存并执行如下命令:
kubectl apply -f deploy-frontend-green-pods.yaml --record deployment "sa-frontend" configured
咱们可使用如下命令检查滚动部署的状态:
kubectl rollout status deployment sa-frontend Waiting for rollout to finish: 1 old replicas are pending termination... Waiting for rollout to finish: 1 old replicas are pending termination... Waiting for rollout to finish: 1 old replicas are pending termination... Waiting for rollout to finish: 1 old replicas are pending termination... Waiting for rollout to finish: 1 old replicas are pending termination... Waiting for rollout to finish: 1 of 2 updated replicas are available... deployment "sa-frontend" successfully rolled out
从输出可知,部署工做已经完成。它完成的方式是这样的,副本被逐一替换。这意味着应用程序始终处于运行状态。在继续以前,咱们确认一下更新是否生效。
在浏览器上查看更新的内容。执行与以前使用过的同一命令 minikube service sa-frontend-lb
打开浏览器,咱们能够看到该按钮已更新。
图20. 绿色按钮
在应用新的Deployment后,Kubernetes会对新旧状态进行比较。在咱们的示例中,新状态请求两个使用rinormaloku/sentiment-analysis-frontend:green
的Pod。这与当前运行状态不一样,所以它会启用RollingUpdate。
图21. RollingUpdate替换Pod
RollingUpdate会根据咱们指定的规则进行操做,即“maxUnavailable: 1”和“maxSurge: 1”。这意味着部署时只能终止一个Pod,而且只能启动一个新的Pod。该过程会不断重复直到全部的Pod都被更换(见图21)。
接下来看看优势#2。
声明:下一部分以小说形式写成,仅供娱乐。
产品经理跑进你的办公室,说他有大麻烦了!
“生产环境的应用程序里有一个严重错误!当即恢复到前一个版本!”,产品经理大叫道。
你心里毫无波澜,眼睛眨都没眨一下。你切换到终端应用,输入:
kubectl rollout history deployment sa-frontend deployments "sa-frontend" REVISION CHANGE-CAUSE 1 <none> 2 kubectl.exe apply --filename=sa-frontend-deployment-green.yaml --record=true
你看了一眼上述Deployment,而后问产品经理:“最新版本有问题,而前一个版本工做正常?”
“是的,你在听我说话吗?!”产品经理尖叫起来。
你无视他的存在,内心很清楚要作什么,而后开始输入:
kubectl rollout undo deployment sa-frontend --to-revision=1 deployment "sa-frontend" rolled back
当你刷新页面后,最近的修改被撤消了!
产品经理惊得下巴都掉到了地上。
你成了今天的英雄!
剧终!
没错……好无聊的小说。在Kubernetes出现以前,现实要精彩得多,更富戏剧性、强度也更高,持续的时间也更长。一段美好的旧时光!
大部分命令都是一目了然的,但有个细节须要你本身解读。为何第一个版本的 CHANGE-CAUSE
是<none>,而第二个版本的 CHANGE-CAUSE
是“kubectl.exe apply –filename=sa-frontend-deployment-green.yaml –record=true
”?
若是你的答案是:咱们在应用新镜像时使用了 --record
标示,那么恭喜你,回答正确!
在下一节中,咱们将使用到目前为止学到的概念来完成整个架构。
咱们已经学习了完成架构所需的所有资源,所以这部分会很快。在图22中,咱们将全部仍然须要作的事情灰化了。让咱们从最下面开始:部署sa-logic Deployment。
图22. 当前应用程序状态
在终端中定位到resource-manifests目录并执行如下命令:
kubectl apply -f sa-logic-deployment.yaml --record deployment "sa-logic" created
SA-Logic Deployment建立了三个Pod(运行着Python应用程序容器),并给它们打上了 app: sa-logic
标签。该标签让咱们可以使用SA-Logic Service中的筛选器来定位它们。请花点时间打开文件sa-logic-deployment.yaml
查看其内容。
因为使用的概念相同,所以无需多言来看看下一项:Service SA-Logic。
这里须要说明一下为何咱们须要这项Service。咱们的Java应用程序(运行在SA-WebApp Deployment的Pod中)依赖于Python应用程序完成的情绪分析。可是,与以前所有在本地运行不一样,如今咱们再也不是使用单一一个Python应用程序监听一个端口,而是两个甚至更多。
这就是为何咱们须要一个Service“做为提供相同功能服务的一组Pod的入口”。这意味着咱们可使用Service SA-Logic做为全部SA-Logic Pod的入口。
执行如下命令:
kubectl apply -f service-sa-logic.yaml --record service "sa-logic" created
更新后的应用程序状态:咱们运行了2个Pod(包含Python应用程序),而且有一个SA-Logic Service做为即将在SA-WebApp Pod中使用的入口。
图23. 更新后的应用程序状态
如今咱们须要使用Deployment资源来部署SA-WebApp Pod。
咱们对Deployment已经很是熟悉,不过此处仍是有一个新功能。若是你打开 sa-web-app-deployment.yaml
文件,你会发现这部分是新的:
- image: rinormaloku/sentiment-analysis-web-app imagePullPolicy: Always name: sa-web-app env: - name: SA_LOGIC_API_URL value: "http://sa-logic" ports: - containerPort: 8080
咱们感兴趣的是env属性是作什么的?咱们推测它是在Pod中声明环境变量SA_LOGIC_API_URL的值为“ http://sa-logic”。但为何咱们将它初始化为 http://sa-logic,什么是sa-logic?
这里须要介绍一下kube-dns。
Kubernetes有一个特殊的Pod kube-dns。默认状况下,全部Pod都会将其做为DNS服务器。kube-dns一个重要特性是它会为每一个新建的Service建立一条DNS记录。
这意味着当咱们建立Service sa-logic时,它得到了一个IP地址。它的名字(与IP一块儿)会被添加到kube-dns记录中。这使得全部的Pod可以将sa-logic转换为SA-Logic Service的IP地址。
好的,咱们继续:
执行如下命令:
kubectl apply -f sa-web-app-deployment.yaml --record deployment "sa-web-app" created
完成。剩下的是使用LoadBalancer Service对外暴露SA-WebApp Pod。以便让咱们的React应用程序能够向做为SA-WebApp Pod入口的Service发送HTTP请求。
打开 service-sa-web-app-lb.yaml
文件,能够看到一切都很熟悉。
无须多想,执行如下命令:
kubectl apply -f sa-web-app-deployment.yaml deployment "sa-web-app" created
整个架构完成了。不过还有一点没完善。在部署SA-Frontend Pod时,容器镜像将SA-WebApp指向了 http://localhost:8080/sentiment。可是如今咱们须要将其更新为指向SA-WebApp LoadBalancer(充当SA-WebApp Pod的入口)的IP地址。
解决这个问题让咱们有机会再次快速地把从代码到部署的全部内容过一遍(若是你不是遵循如下指南,而是单独作这件事,可能会更有效)。让咱们开始吧:
minikube service list |-------------|----------------------|-----------------------------| | NAMESPACE | NAME | URL | |-------------|----------------------|-----------------------------| | default | kubernetes | No node port | | default | sa-frontend-lb | http://192.168.99.100:30708 | | default | sa-logic | No node port | | default | sa-web-app-lb | http://192.168.99.100:31691 | | kube-system | kube-dns | No node port | | kube-system | kubernetes-dashboard | http://192.168.99.100:30000 | |-------------|----------------------|-----------------------------|
sa-frontend/src/App.js
文件中使用SA-WebApp Loadbalancer的IP地址:
analyzeSentence() { fetch('http://192.168.99.100:31691/sentiment', { /* shortened for brevity */}) .then(response => response.json()) .then(data => this.setState(data)); }
npm build
构建静态文件(须要定位到sa-frontend目录)
docker build -f Dockerfile -t $DOCKER_USER_ID/sentiment-analysis-frontend:minikube .
docker push $DOCKER_USER_ID/sentiment-analysis-frontend:minikube
sa-frontend-deployment.yaml
文件以使用新的镜像。kubectl apply -f sa-frontend-deployment.yaml
刷新一下浏览器,或者再次执行 minikube service sa-frontend-lb
。如今输入一个句子试试!
Kubernetes对团队和项目都很是有益,它简化了部署、可扩展性和弹性,让咱们可以使用任意的底层基础设施。从如今开始,我要称它为Supernetes!你以为如何?
咱们在这个系列中介绍的内容:
原文连接: Learn Kubernetes in Under 3 Hours: A Detailed Guide to Orchestrating Containers (翻译: 梁晓勇)