应我们的要求,Habr 创建了一个中心
Kubernetes 我们很高兴将第一份出版物放入其中。 订阅!
Kubernetes 很简单。 为什么银行付给我很多钱让我从事这个领域的工作,而任何人都可以在短短几个小时内掌握这项技术?
如果你怀疑 Kubernetes 能学这么快,我建议你自己尝试一下。 也就是说,掌握了本材料后,您将能够在 Kubernetes 集群中运行基于微服务的应用程序。 我可以保证这一点,因为我在这里使用的方法与我教客户如何使用 Kubernetes 的方法相同。 本指南与其他指南有何不同? 其实很多事情。 因此,这些材料中的大多数都是从简单事物的解释开始的 - Kubernetes 的概念和 kubectl 命令的功能。 这些文章的作者假设读者熟悉应用程序开发、微服务和 Docker 容器。 我们会走另一条路。 首先,我们来谈谈如何在计算机上运行基于微服务的应用程序。 然后我们将研究为每个微服务构建容器映像。 之后,我们将熟悉 Kubernetes,并分析在 Kubernetes 管理的集群中基于微服务的应用程序的部署。
这种方法,以及对 Kubernetes 的渐进方法,将让普通人深入了解正在发生的事情,以便了解 Kubernetes 中一切的安排是多么简单。 Kubernetes 当然是一项简单的技术,只要想掌握它的人都知道它在哪里以及如何使用。
现在,事不宜迟,让我们开始工作并讨论我们将使用的应用程序。
实验性应用程序
我们的应用程序将只执行一项功能。 它以一句话作为输入,然后利用文本分析工具对这句话进行情感分析,获得该句子的作者对某一对象的情感态度的评估。
这就是该应用程序的主窗口的样子。
情感分析 Web 应用程序
从技术角度来看,该应用程序由三个微服务组成,每个微服务解决一组特定的任务:
- SA-Frontend 是一个 Nginx Web 服务器,提供 React 静态文件。
- SA-WebApp 是一个用 Java 编写的 Web 应用程序,用于处理来自前端的请求。
- SA-Logic 是一个执行文本情感分析的 Python 应用程序。
值得注意的是,微服务并不是孤立存在的。 他们贯彻了“职责分离”的思想,但同时,他们也需要彼此互动。
应用程序中的数据流
在上图中,您可以看到系统的编号阶段,说明了应用程序中的数据流。 让我们把它们分解一下:
- 浏览器向服务器请求文件
index.html
(这又会加载 React 应用程序包)。 - 用户与应用程序交互,这会导致调用基于 Spring 的 Web 应用程序。
- Web 应用程序将解析文本的请求转发到 Python 应用程序。
- Python 应用程序分析文本的情绪并将结果作为对请求的响应返回。
- Spring 应用程序向 React 应用程序发送响应(反过来,React 应用程序向用户显示解析文本的结果)。
所有这些应用程序的代码都可以找到
在本地机器上运行基于微服务的应用程序
为了使应用程序正常工作,我们需要启动所有三个微服务。 让我们从其中最漂亮的部分开始——前端应用程序。
▍设置 React 进行本地开发
为了运行 React 应用程序,您需要在计算机上安装 Node.js 框架和 NPM。 安装完所有这些后,使用终端进入项目文件夹 sa-frontend
并运行以下命令:
npm install
通过在文件夹中执行此命令 node_modules
React应用程序的依赖项将被加载,其记录在文件中 package.json
。 将依赖项下载到同一文件夹后,运行以下命令:
npm start
就这样。 React 应用程序现已运行,可以通过浏览器地址进行访问 localhost:3000
。 您可以更改他的代码中的某些内容。 您将立即在浏览器中看到这些更改的效果。 这要归功于所谓的模块“热”更换。 正因为如此,前端开发变成了一种简单而愉快的体验。
▍为生产准备 React 应用程序
为了实际使用 React 应用程序,我们需要将其转换为一组静态文件,并使用 Web 服务器将它们提供给客户端。
要构建 React 应用程序,请再次使用终端,导航到该文件夹 sa-frontend
并运行以下命令:
npm run build
这将在项目文件夹中创建一个目录 build
。 它将包含 React 应用程序工作所需的所有静态文件。
▍使用 Nginx 提供静态文件
首先,您需要安装并运行 Nginx Web 服务器。 sa-frontend/build
到文件夹 [your_nginx_installation_dir]/html
.
通过这种方法,在 React 应用程序的组装过程中生成的文件 index.html
将于 [your_nginx_installation_dir]/html/index.html
。 默认情况下,Nginx 服务器在访问该文件时会发出该文件。 服务器配置为侦听端口 80
,但您可以通过编辑文件来自定义它 [your_nginx_installation_dir]/conf/nginx.conf
.
现在打开浏览器并转到 localhost:80
。 您将看到 React 应用程序页面。
由 Nginx 服务器提供服务的 React 应用程序
如果您现在在字段中输入内容 Type your sentence
并按下按钮 Send
- 什么都不会发生。 但是,如果您查看控制台,您可以看到错误消息。 为了准确了解这些错误发生的位置,让我们分析应用程序代码。
▍前端应用程序代码分析
查看文件的代码 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。 假定该地址是等待此类请求的应用程序。
2.发送到应用程序的请求正文。 以下是请求正文示例:
{
sentence: "I like yogobella!"
}
3.当接收到对请求的响应时,组件的状态被更新。 这会导致组件重新渲染。 如果我们收到数据(即包含输入数据和计算出的文本分数的 JSON 对象),我们将输出组件 Polarity
只要满足条件。 我们是这样描述该组件的:
const polarityComponent = this.state.polarity !== undefined ?
<Polarity sentence={this.state.sentence}
polarity={this.state.polarity}/> :
null;
该代码似乎工作得很好。 无论如何,这里出了什么问题? 如果您假设在应用程序尝试发送 POST 请求的地址处,还没有任何东西可以接受和处理该请求,那么您是绝对正确的。 即,处理到达该地址的请求 http://localhost:8080/sentiment
,我们需要运行一个基于Spring的Web应用程序。
我们需要一个可以接受 POST 请求的 Spring 应用程序
▍搭建基于Spring的Web应用
为了部署 Spring 应用程序,您需要 JDK8 和 Maven 以及正确配置的环境变量。 安装完所有这些后,您可以继续我们的项目。
▍将应用程序打包成jar文件
使用终端导航至文件夹 sa-webapp
并输入以下命令:
mvn install
在文件夹中执行此命令后 sa-webapp
将创建目录 target
。 这是 Java 应用程序所在的位置,打包在 jar 文件中,由文件表示 sentiment-analysis-web-0.0.1-SNAPSHOT.jar
.
▍启动Java应用程序
转到文件夹 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}"
对我们来说,这里最重要的是提到无法澄清含义 sa.logic.api.url
。 我们来分析一下发生错误的代码。
▍Java应用程序代码分析
这是发生错误的代码片段。
@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();
}
}
- 在S
entimentController
有一个字段saLogicApiUrl
。 它的值由属性设置sa.logic.api.url
. - 行
saLogicApiUrl
与值连接/analyse/sentiment
。 它们一起形成一个地址,用于调用执行文本分析的微服务。
▍设置属性值
在Spring中,属性值的默认来源是文件 application.properties
,可以在以下位置找到 sa-webapp/src/main/resources
。 但使用它并不是设置属性值的唯一方法。 您还可以使用以下命令来执行此操作:
java -jar sentiment-analysis-web-0.0.1-SNAPSHOT.jar --sa.logic.api.url=WHAT.IS.THE.SA.LOGIC.API.URL
该属性的值应指向我们的 Python 应用程序的地址。
通过配置它,我们告诉 Spring Web 应用程序需要去哪里执行文本解析请求。
为了不让我们的生活变得复杂,我们将决定 Python 应用程序将在以下位置提供: localhost:5000
并尽量不要忘记它。 因此,启动 Spring 应用程序的命令将如下所示:
java -jar sentiment-analysis-web-0.0.1-SNAPSHOT.jar --sa.logic.api.url=http://localhost:5000
我们的系统缺少 Python 应用程序
现在我们只需运行 Python 应用程序,系统就会按预期工作。
▍设置Python应用程序
为了运行 Python 应用程序,您必须安装 Python 3 和 Pip,并且必须正确设置相应的环境变量。
▍安装依赖
转到项目文件夹 sa-logic/sa
并运行以下命令:
python -m pip install -r requirements.txt
python -m textblob.download_corpora
▍应用程序启动
安装依赖项后,我们就可以运行应用程序了:
python sentiment_analysis.py
执行此命令后,我们将被告知以下内容:
* Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)
这意味着应用程序正在运行并等待请求 localhost:5000/
▍代码研究
让我们看一下 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='0.0.0.0', port=5000) #6
- 对象初始化
Flask
. - 指定向其发出 POST 请求的地址。
- 检索属性
sentence
来自请求正文。 - 匿名对象初始化
TextBlob
并获取值polarity
对于请求正文中收到的第一个提案(在我们的例子中,这是提交用于分析的唯一提案)。 - 返回响应,其正文包含报价文本和为其计算的指标
polarity
. - 启动 Flask 应用程序,该应用程序可在
0.0.0.0:5000
(您还可以使用以下形式的构造来访问它localhost:5000
).
现在组成应用程序的微服务正在运行。 他们被设置为彼此互动。 这是此阶段工作的应用程序图的样子。
组成应用程序的所有微服务都处于健康状态
现在,在我们继续之前,在浏览器中打开 React 应用程序并尝试用它解析一些句子。 如果一切都正确完成 - 按下按钮后 Send
您将在文本框下方看到分析结果。
在下一节中,我们将讨论如何在 Docker 容器中运行微服务。 为了准备应用程序在 Kubernetes 集群中运行,这是必要的。
Docker 容器
容器镜像是一个轻量级的、独立的、可执行的包,其中包含一个应用程序,其中包括运行它所需的一切:应用程序代码、运行时环境、系统工具和库、设置。 容器化程序可以在 Linux 和 Windows 环境中使用,并且无论基础设施如何,都将始终以相同的方式工作。
这意味着容器可以在任何计算机上运行,包括生产服务器,并且在任何环境中,其中包含的应用程序都将以相同的方式工作。
为了探索容器的功能并将其与其他运行应用程序的方式进行比较,让我们看一下使用虚拟机和容器提供 React 应用程序的示例。
▍使用虚拟机提供 React 应用程序的静态文件
尝试使用虚拟机来组织静态文件的维护,我们会遇到以下缺点:
- 资源利用效率低下,因为每个虚拟机都是一个完整的操作系统。
- 平台依赖性。 在某些本地计算机上有效的方法可能不适用于生产服务器。
- 虚拟机解决方案的缓慢且资源密集型扩展。
Nginx Web 服务器提供在虚拟机中运行的静态文件
如果使用容器来解决类似的问题,那么与虚拟机相比,可以看出以下优势:
- 资源的高效利用:使用Docker与操作系统一起工作。
- 平台独立性。 开发人员可以在自己的计算机上运行的容器可以在任何地方运行。
- 通过使用镜像层进行轻量级部署。
Nginx Web 服务器提供在容器中运行的静态文件
我们仅在几个方面比较了虚拟机和容器,但即使这样也足以了解容器的优势。
▍为 React 应用构建容器镜像
Docker 容器的基本构建块是文件 Dockerfile
。 在此文件的开头,记录了容器的基本映像,然后包含一系列指令,指示如何创建满足应用程序需求的容器。
在我们开始使用该文件之前 Dockerfile
,记住我们为了准备 React 应用程序的文件以上传到 Nginx 服务器而做了什么:
- 构建 React 应用程序包(
npm run build
). - 启动 Nginx 服务器。
- 复制目录的内容
build
从项目文件夹sa-frontend
到服务器文件夹nginx/html
.
下面您可以看到创建容器与在本地计算机上执行的上述操作之间的相似之处。
▍为 SA 前端应用程序准备 Dockerfile
须包含的说明 Dockerfile
申请 SA-Frontend
,仅由两支球队组成。 事实上Nginx开发团队已经准备好了一个基本的
- 您需要将 Nginx 镜像作为镜像的基础。
- 文件夹内容
sa-frontend/build
需要复制到图片文件夹nginx/html
.
如果我们从这个描述转到文件 Dockerfile
,那么它将如下所示:
FROM nginx
COPY build /usr/share/nginx/html
正如您所看到的,这里的一切都非常简单,而文件的内容甚至变得非常可读和易于理解。 该文件告诉系统拍摄图像 nginx
及其已有的所有内容,然后复制目录的内容 build
到目录 nginx/html
.
在这里您可能有一个关于我如何知道从文件夹中复制文件的确切位置的问题 build
,即路径从哪里来 /usr/share/nginx/html
。 事实上,这里也没有什么复杂的。 事实上,相关信息可以在
▍组装镜像并上传到存储库
在我们使用完成的图像之前,我们需要将其提交到图像存储库。 为此,我们将使用免费的基于云的图像托管平台 Docker Hub。 在这个阶段的工作中,您需要完成以下工作:
- 要安装
码头工人 . - 在 Docker Hub 站点上注册。
- 通过在终端中运行以下命令来登录您的帐户:
docker login -u="$DOCKER_USERNAME" -p="$DOCKER_PASSWORD"
现在您需要使用终端转到目录 sa-frontend
并在那里运行以下命令:
docker build -f Dockerfile -t $DOCKER_USER_ID/sentiment-analysis-frontend .
这里和下面的类似命令 $DOCKER_USER_ID
应替换为您在 Docker Hub 上的用户名。 例如,这部分命令可能如下所示: rinormaloku/sentiment-analysis-frontend
.
在这种情况下,可以通过从中删除来缩短该命令 -f Dockerfile
,因为我们执行此命令的文件夹已经有此文件。
为了将完成的图像发送到存储库,我们需要以下命令:
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_USER_ID/sentiment-analysis-frontend
现在容器正在运行,我们可以通过创建我们需要的其他镜像来继续工作。 但在我们继续之前,让我们先了解一下设计 80:80
,它是在运行图像的命令中找到的,可能看起来很混乱。
- 第一个数字
80
是主机(即本地计算机)的端口号。 - 第二个号码
80
是请求应重定向到的容器的端口。
考虑下图。
转发端口
系统从端口转发请求 <hostPort>
到港口 <containerPort>
。 也就是访问端口 80
计算机被重定向到某个端口 80
容器。
自进港以来 80
在本地计算机上打开,您可以从此计算机访问该应用程序 localhost:80
。 如果您的系统不支持 Docker,您可以在 Docker 虚拟机上运行应用程序,其地址如下所示 <docker-machine ip>:80
。 要找出Docker虚拟机的IP地址,可以使用命令 docker-machine ip
.
此时,一旦前端应用程序容器成功启动,您应该能够在浏览器中打开其页面。
▍.dockerignore 文件
构建应用程序映像 SA-Frontend
,我们可以注意到这个过程非常慢。 这是因为镜像构建上下文必须发送到 Docker 守护进程。 表示构建上下文的目录作为命令的最后一个参数给出 docker build
。 在我们的例子中,该命令的末尾有一个点。 这会导致以下结构包含在程序集上下文中:
sa-frontend:
| .dockerignore
| Dockerfile
| package.json
| README.md
+---build
+---node_modules
+---public
---src
但在这里的所有文件夹中,我们只需要一个文件夹 build
。 下载其他任何东西都是浪费时间。 您可以通过告诉 Docker 忽略哪些目录来加快构建速度。 为了做到这一点,我们需要一个文件 .dockerignore
。 您,如果您熟悉该文件 .gitignore
,这个文件的结构可能看起来很熟悉。 它列出了映像构建系统可以忽略的目录。 在我们的例子中,该文件的内容如下所示:
node_modules
src
public
文件 .dockerignore
必须与文件位于同一文件夹中 Dockerfile
。 现在图像的组装将需要几秒钟。
现在让我们处理 Java 应用程序的图像。
▍为Java应用程序构建容器镜像
您知道吗,并且您已经了解了创建容器映像所需的一切。 这就是为什么本节会很短。
打开文件 Dockerfile
,位于项目文件夹中 sa-webapp
。 如果您阅读此文件的文本,那么在其中您只会遇到两个以关键字开头的新结构 ENV
и EXPOSE
:
ENV SA_LOGIC_API_URL http://localhost:5000
…
EXPOSE 8080
关键词 ENV
允许您在 Docker 容器内声明环境变量。 特别是,在我们的示例中,它允许您设置 URL 来访问执行文本分析的应用程序的 API。
关键词 EXPOSE
允许你告诉 Docker 打开一个端口。 我们将在使用应用程序时使用此端口。 在这里你可以看到 Dockerfile
申请 SA-Frontend
没有这样的命令。 这仅用于文档目的,换句话说,此构造是供读者使用的 Dockerfile
.
构建图像并将其推送到存储库看起来与前面的示例完全相同。 如果你对自己的能力还不是很有信心,可以在文件中找到相应的命令 README.md
在文件夹中 sa-webapp
.
▍为Python应用程序构建容器镜像
如果你看一下文件的内容 Dockerfile
在文件夹中 sa-logic
在那里你不会发现任何新东西。 您也应该熟悉用于构建映像并将其推送到存储库的命令,但是,与我们其他应用程序的情况一样,它们可以在文件中找到 README.md
在文件夹中 sa-logic
.
▍测试容器化应用程序
你能相信一些你没有测试过的东西吗? 我也不能。 让我们测试一下我们的容器。
- 让我们启动应用程序容器
sa-logic
并将其配置为侦听端口5050
:docker run -d -p 5050:5000 $DOCKER_USER_ID/sentiment-analysis-logic
- 让我们启动应用程序容器
sa-webapp
并将其配置为侦听端口8080
。 此外,我们需要通过重新分配环境变量来设置Python应用程序侦听来自Java应用程序的请求的端口SA_LOGIC_API_URL
:$ docker run -d -p 8080:8080 -e SA_LOGIC_API_URL='http://<container_ip or docker machine ip>:5000' $DOCKER_USER_ID/sentiment-analysis-web-app
要了解如何查找容器或 Docker VM 的 IP 地址,请参阅文件
让我们启动应用程序容器 sa-frontend
:
docker run -d -p 80:80 $DOCKER_USER_ID/sentiment-analysis-frontend
现在一切准备就绪,可以在浏览器中导航到该地址 localhost:80
并测试该应用程序。
请注意,如果您更改端口 sa-webapp
,或者如果您正在运行 Docker VM,则需要编辑该文件 App.js
从文件夹 sa-frontend
通过更改方法中的IP地址或端口号 analyzeSentence()
通过替换当前信息而不是过时的数据。 之后,您需要重新组装图像并使用它。
这就是我们的应用程序图现在的样子。
微服务在容器中运行
总结:为什么需要 Kubernetes 集群?
我们刚刚审查了文件 Dockerfile
,讨论了如何构建镜像并将其推送到 Docker 存储库。 此外,我们还学习了如何使用该文件加速图像的组装 .dockerignore
。 因此,我们的微服务现在运行在 Docker 容器中。 在这里你可能有一个完全合理的问题:为什么我们需要 Kubernetes。 这个问题的答案将集中在本材料的第二部分。 同时,请考虑以下问题:
假设我们的文本分析 Web 应用程序已在全球范围内流行。 每分钟都有数以百万计的请求向他提出。 这意味着微服务 sa-webapp
и sa-logic
将会承受巨大的压力。 如何扩展运行微服务的容器?
来源: habr.com