本文主要是讲解在Kubernates集群上运行基于微服务的应用程序。html
原文内容来自Learn Kubernetes in Under 3 Hours: A Detailed Guide to Orchestrating Containers前端
这个项目只有一个功能:在Web浏览器上输入一个句子,而后会计算句子所表达的情绪。java
从技术的角度看,这个应用程序包含3个微服务,每一个都包含特定的功能:node
咱们能够经过微服务之间的数据流来描述这种交互:python
如今就克隆这个代码库:github.com/heqingbao/k…,接下来咱们要作更精彩的东西。nginx
咱们须要启动所需的3个服务,这里从前端应用程序开始。git
为了运行React应用程序,须要先在计算机上安装NodeJS和NPM,安装好这些后,在终端中进入目录sa-frontend,而后运行以下命令:github
npm install
复制代码
该命令会将 React 应用程序的全部 Javascript 依赖都下载到文件夹 node_modules 中(package.json 文件中定义了全部依赖)。在全部依赖都解决后,运行以下命令:web
npm start
复制代码
这样就能够了!咱们运行了 React 应用程序,如今能够经过默认端口 localhost:3000 访问该应用程序了。你能够自由修改代码,并从浏览器中观察即时效果。spring
为了搭建产品环境,咱们须要创建应用程序的静态网页,并经过网络服务器提供服务。
首先在终端中进入目录sa-frontend,而后运行以下命令:
npm run build
复制代码
该命令会在项目的文件目录中生成一个名叫“build”的文件夹。该文件夹内包含了 ReactJS 应用程序所需的全部静态文件。
首先安装并启动 Nginx 网络服务器。而后将 sa-frontend/build 目录内的文件移动到 [nginx安装目录]/html。
如此一来,咱们就能够经过 [nginx安装目录]/html/index.html 来访问 index.html 文件了,而它是 Nginx 服务的默认文件。
默认状况下,Nginx 网络服务器会监听端口 80。你能够经过修改 [nginx安装目录]/conf/nginx.conf 文件中的 server.listen 字段来指定不一样的端口。
打开浏览器,并访问端口 80,能够看到 ReactJS 应用程序加载成功。
在输入框Type your sentence
中输入句子,而后点击SEND,发现没什么反应。由于它会向http://localhost:8080/sentiment
发送请求,接下来咱们就部署这个服务。
为了编译sa-webapp项目,必须安装JDK8和Maven,并设置它们的环境变量。设置好后继续操做。
在终端中进入sa-webapp目录,并运行以下命令:
mvn install
复制代码
该命令会在目录 sa-webapp 中生成一个名叫 target 的文件夹。target 文件夹内有打包好的 Java 应用程序包:’sentiment-analysis-web-0.0.1-SNAPSHOT.jar’。
进入target目录,并经过以下命令启动应用程序:
java -jar sentiment-analysis-web-0.0.1-SNAPSHOT.jar
复制代码
启动失败,能够看到以下异常信息:
org.springframework.beans.factory.BeanCreationException: 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}")
private String saLogicApiUrl;
@PostMapping("/sentiment")
public SentimentDto sentimentAnalysis(@RequestBody SentenceDto sentenceDto) {
RestTemplate restTemplate = new RestTemplate();
return restTemplate.postForEntity(
saLogicApiUrl + "/analyse/sentiment", sentenceDto, SentimentDto.class).getBody();
}
}
复制代码
在 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应用程序运行时定义的值初始化该属性,如此一来String网络应用程序就能够知道在运行时把信息传递到哪里了。
为了简单起见,咱们假设在localhost:5000上运行Python应用程序。
运行以下命令,而后咱们再部署最后一个服务:Python应用程序。
java -jar sentiment-analysis-web-0.0.1-SNAPSHOT.jar --sa.logic.api.url=http://localhost:5000
复制代码
为了启动 Python 应用程序,首先咱们须要安装 Python3 和 pip,以及设置它们的环境变量。(若是本机只有python2,建议使用virtualenv配置python多环境)
在终端中进入 sa-logic/sa目录,而后运行以下命令:
python -m pip install -r requirements.txt
python -m textblob.download_corpora
复制代码
注意:若是是新安装的python3,而且经过virtualenv建立的pytho> n3环境,在执行
python -m textblob.download_corpora
的时候可能会报如下错误:
(venv) ➜ sa git:(master) ✗ python -m textblob.download_corpora
[nltk_data] Error loading brown: <urlopen error [SSL:
[nltk_data] CERTIFICATE_VERIFY_FAILED] certificate verify failed:
[nltk_data] unable to get local issuer certificate (_ssl.c:1051)>
[nltk_data] Error loading punkt: <urlopen error [SSL:
[nltk_data] CERTIFICATE_VERIFY_FAILED] certificate verify failed:
[nltk_data] unable to get local issuer certificate (_ssl.c:1051)>
[nltk_data] Error loading wordnet: <urlopen error [SSL:
[nltk_data] CERTIFICATE_VERIFY_FAILED] certificate verify failed:
[nltk_data] unable to get local issuer certificate (_ssl.c:1051)>
[nltk_data] Error loading averaged_perceptron_tagger: <urlopen error
[nltk_data] [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify
[nltk_data] failed: unable to get local issuer certificate
[nltk_data] (_ssl.c:1051)>
[nltk_data] Error loading conll2000: <urlopen error [SSL:
[nltk_data] CERTIFICATE_VERIFY_FAILED] certificate verify failed:
[nltk_data] unable to get local issuer certificate (_ssl.c:1051)>
[nltk_data] Error loading movie_reviews: <urlopen error [SSL:
[nltk_data] CERTIFICATE_VERIFY_FAILED] certificate verify failed:
[nltk_data] unable to get local issuer certificate (_ssl.c:1051)>
Finished.
复制代码
解决方式是执行一下这个文件:/Applications/Python\ 3.7/Install\ Certificates.command
,此文件经过Finder->应用程序
里面可以找到,双击就行。
在利用 Pip 安装好依赖后,咱们就能够经过运行以下命令启动应用程序了:
python sentiment_analysis.py
* Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)
复制代码
这意味着应用程序已经启动,并在 localhost 的端口 5000 上监听 HTTP 请求了。
到此,若是一切顺利,那么在浏览器上访问:http://localhost:3000/
,而后在输入框中输入句子,应该就可以显示情绪值了。
后面将介绍如何在Docker容器内启动这些服务,由于这是在Kubernetes集群内运行这些服务的前提条件。
Kubernetes 是容器管理平台。可想而知咱们须要容器去管理它们。可是容器是什么?Docker 官方文档的最佳答案以下:
容器映像是轻量级的、独立的、可执行软件包,包含全部可运行的东西:代码、运行时、系统工具、系统库、设置。对于基于 Linux 和 Windows 的应用,不论环境如何,容器化的软件均可以照常运行。
这意味着容器能够在任何计算机上运行,甚至是在产品服务器上,都没有任何差异。
为了更形象地描述,让咱们来对比一下 React 应用程序在虚拟机上和容器内运行的状况。
经过虚拟机提供 React 静态文件
使用虚拟机的缺点包括:
经过容器提供 React 静态文件
使用容器的优势包括:
Docker 容器最基本的组件是.dockerfile。该 Dockerfile 文件最基本的组成是容器镜像,咱们将经过下列一系列说明,介绍如何建立一个符合应用程序需求的容器镜像。
在开始定义 Dockerfile 以前,让咱们先回想一下使用 Nginx 服务 React 静态文件的步骤:
在下一节中,你会注意到建立容器与创建本地 React 的过程很是类似。
前端 Dockerfile 的创建只有两个步骤。这是由于 Nginx 团队为咱们提供了基本的 Nginx 映像,咱们能够直接利用。这两个步骤以下:
转换成的Dockerfile以下所示:
FROM nginx
COPY build /usr/share/nginx/html
复制代码
这个文件是可读的,咱们能够归纳为:
从 Nginx 映像开始(无论里面是什么)。将 build 目录复制到映像的 nginx/html 目录中。而后就行了!
你可能在想,我应该将 build 文件复制到哪儿呢?例如:/usr/share/nginx/html。很是简单:在 Docker Hub 的 Nginx 映像文档中有记载。
在推送映像以前,咱们须要一个容器注册来托管映像。Docker Hub 是一个免费的云容器服务,咱们将使用它来作演示。接下来有 3 个任务须要完成:
安装 Docker CE;
注册 Docker Hub;
在终端中运行以下命令登陆:
docker login -u="$DOCKER_USERNAME" -p="$DOCKER_PASSWORD"
复制代码
或者执行:
docker login
复制代码
而后使用交互模式填写用户名和密码。
在完成上述任何后,请进入目录 sa-frontend。而后运行以下命令(请用你的 docker hub 用户名替换 $DOCKER 用户名,例如:heqingbao/sentiment-analysis-frontend)。
[root@VM_0_3_centos sa]# docker build -f Dockerfile -t heqingbao/sentiment-analysis-frontend .
Sending build context to Docker daemon 1.768 MB
Step 1/2 : FROM nginx
Trying to pull repository docker.io/library/nginx ...
sha256:d59a1aa7866258751a261bae525a1842c7ff0662d4f34a355d5f36826abc0341: Pulling from docker.io/library/nginx
f17d81b4b692: Pull complete
82dca86e04c3: Pull complete
046ccb106982: Pull complete
Digest: sha256:d59a1aa7866258751a261bae525a1842c7ff0662d4f34a355d5f36826abc0341
Status: Downloaded newer image for docker.io/nginx:latest
---> 62f816a209e6
Step 2/2 : COPY build /usr/share/nginx/html
---> 8284804168aa
Removing intermediate container f74eb32d3c46
Successfully built 8284804168aa
复制代码
注意:若是是部署在远端服务器上,必定要把App.js里面的
http://localhost:8080/sentiment
的localhost
换成远端主机的IP或容器的IP。
如今咱们能够删掉 -f Dockerfile 了,由于咱们已经在包含 Dockerfile 的目录中了。
咱们可使用 docker push 命令来推送映像:
[root@VM_0_3_centos sa]# docker push heqingbao/sentiment-analysis-frontend
The push refers to a repository [docker.io/heqingbao/sentiment-analysis-frontend]
a5a0d2defc6a: Pushed
ad9ac0e6043b: Mounted from library/nginx
6ccbee34dd10: Mounted from library/nginx
237472299760: Mounted from library/nginx
latest: digest: sha256:eb5adb74d0685e267771d5bcdc536015a8cb58fe88c1860d10f13d2994d3c063 size: 1158
复制代码
请确认映像已成功地被推送到 docker hub 代码库。
如今任何人均可以获取 heqingbao/sentiment-analysis-frontend 中的映像并运行:
docker pull heqingbao/sentiment-analysis-frontend
docker run -d -p 80:80 heqingbao/sentiment-analysis-frontend
复制代码
Docker 容器已经处于运行状态了!
访问:http://yourid:80,试试看,你如今应该能够访问React应用程序了。
刚才咱们看到创建 SA-Frontend 的映像很是慢,很差意思,应该是超级慢。这是由于咱们必须将创建过程当中的环境文件发送给 Docker 服务。更具体地来讲,创建过程当中的环境文件指的是在创建映像的时候,全部会用到的 Dockerfile 目录中的数据。
以咱们的例子来讲,SA-Frontend 文件包括以下文件夹:
sa-frontend:
| .dockerignore
| Dockerfile
| package.json
| README.md
+---build
+---node_modules
+---public
\---src
复制代码
可是咱们只须要 build 文件夹。上传其余的文件会浪费时间。咱们能够经过删除其余目录来节约时间。这就须要用到 .dockerignore。你可能以为这与 .gitignore 很类似,例如你能够全部想要忽略的目录都添加到 .dockerignore,以下所示:
node_modules
src
public
复制代码
这个 .dockerignore 文件应该与 Dockerfile 在同一文件夹中。如今创建映像文件只须要几秒钟了。
这里使用了自定义容器镜像的方式,可是若是咱们的镜像只使用一次(例如写一些测试Demo),大可没必要建立本身的镜像而后再传到仓库,咱们能够直接使用官方的Nginx镜像便可。
下载nginx镜像:
docker pull docker.io/nginx
复制代码
启动nginx容器:
docker run -d -p 80:80 --name mynginx \
--volume "$PWD/html":/usr/share/nginx/html \
docker.io/nginx
复制代码
上面命令的各个参数含义以下:
而后把前面sa-frontend项目编译后的build目录里的全部文件拷贝到当前路径下的html目录里。
访问:http://yourid:80,试试看,你如今应该也能够访问React应用程序。
在 sa-webapp 中打开 Dockerfile:
FROM openjdk:8-jdk-alpine
# Environment Variable that defines the endpoint of sentiment-analysis python api.
ENV SA_LOGIC_API_URL http://localhost:5000
ADD target/sentiment-analysis-web-0.0.1-SNAPSHOT.jar /
EXPOSE 8080
CMD ["java", "-jar", "sentiment-analysis-web-0.0.1-SNAPSHOT.jar", "--sa.logic.api.url=${SA_LOGIC_API_URL}"]
复制代码
关键字 ENV 在 Docker 容器内声明了环境变量。这可让咱们在启动容器的时候为情感分析 API 提供 URL。
另外,关键字 EXPOSE 提供了一个端口,供咱们之后访问。可是等等,咱们在 SA-Frontend 的时候没有作这一步,说得很对!这个端口仅用于文档,换句话说就是这个端口是用来向阅读 Dockerfile 的人提供信息的。
你应该已经掌握了建立和推送容器映像。
建立镜像:
[root@VM_0_3_centos sa-webapp]# docker build -f Dockerfile -t heqingbao/sentiment-analysis-web-app .
Sending build context to Docker daemon 20.49 MB
Step 1/5 : FROM openjdk:8-jdk-alpine
Trying to pull repository docker.io/library/openjdk ...
sha256:b18e45570b6f59bf80c15c78d7f0daff1e18e9c19069c323613297057095fda6: Pulling from docker.io/library/openjdk
4fe2ade4980c: Pull complete
6fc58a8d4ae4: Pull complete
ef87ded15917: Pull complete
Digest: sha256:b18e45570b6f59bf80c15c78d7f0daff1e18e9c19069c323613297057095fda6
Status: Downloaded newer image for docker.io/openjdk:8-jdk-alpine
---> 97bc1352afde
Step 2/5 : ENV SA_LOGIC_API_URL http://localhost:5000
---> Running in c3be1ec16ac4
---> ab213d1b2ce1
Removing intermediate container c3be1ec16ac4
Step 3/5 : ADD target/sentiment-analysis-web-0.0.1-SNAPSHOT.jar /
---> 5d1ebdbf659d
Removing intermediate container 7e5b7519d9e3
Step 4/5 : EXPOSE 8080
---> Running in e428a3388798
---> 0893bf90a104
Removing intermediate container e428a3388798
Step 5/5 : CMD java -jar sentiment-analysis-web-0.0.1-SNAPSHOT.jar --sa.logic.api.url=${SA_LOGIC_API_URL}
---> Running in 065ac2e61dbd
---> cba14182f49f
Removing intermediate container 065ac2e61dbd
复制代码
启动容器:
[root@VM_0_3_centos sa-webapp]# docker run -d -p 8080:8080 -e SA_LOGIC_API_URL='http://x.x.x.x:5050' heqingbao/sentiment-analysis-web-app
b3ab99abecd7a97f091e2362b4eee870037e562347f3996a9a1a2669ca60c651
复制代码
上传到仓库:
[root@VM_0_3_centos sa-webapp]# docker push heqingbao/sentiment-analysis-web-app
The push refers to a repository [docker.io/heqingbao/sentiment-analysis-web-app]
4e1c5d0784bf: Pushed
ed6f0bd39121: Mounted from library/openjdk
0c3170905795: Mounted from library/openjdk
df64d3292fd6: Mounted from library/openjdk
latest: digest: sha256:be20fe12c184b6c4d2032141afe9b8cc092a9a083f1cf0a7dc8f73c4b1ebbaf8 size: 1159
复制代码
sa-logic 的 Dockerfile:
FROM python:3.6.6-alpine
COPY sa /app
WORKDIR /app
RUN pip3 install -r requirements.txt && \
python3 -m textblob.download_corpora
EXPOSE 5000
ENTRYPOINT ["python3"]
CMD ["sentiment_analysis.py"]
复制代码
如今你已是 Docker 达人了。
构建容器镜像:
docker build -f Dockerfile -t heqingbao/sentiment-analysis-logic .
复制代码
运行Docker容器:
docker run -d -p 5050:5000 heqingbao/sentiment-analysis-logic
复制代码
1.运行sa-logic容器,并配置监听端口5050:
docker run -d -p 5050:5000 heqingbao/sentiment-analysis-logic
复制代码
2.运行 sa-webapp 容器,并配置监听端口 8080(由于咱们改变了 Python 应用监听的端口,因此咱们须要重写环境变量 SA_LOGIC_API_URL):
$ docker run -d -p 8080:8080 -e SA_LOGIC_API_URL='http://x.x.x.x:5050' heqingbao/sentiment-analysis-web-app
复制代码
3.运行 sa-frontend 容器:
docker run -d -p 80:80 heqingbao/sentiment-analysis-frontend
复制代码
而后就能够了。在浏览器中打开 x.x.x.x:80。
本节中,咱们学习了 Dockerfile,如何使用它建立映像,以及推送映像到 Docker注册目录的命令。另外,咱们探讨了如何经过忽略没用的文件,减小须要发送的创建过程当中的环境文件。最后咱们从容器上运行了应用程序。
接下来,咱们介绍为何要使用 Kubernetes?咱们将在下面深刻介绍 Kubernetes,这里我想给你留个智力问答题。
我向你保证我没有夸大其词,读完本文你会问“为何咱们不称它为 Supernetes?”
从容器启动微服务后,咱们有一个问题,让咱们经过以下问答的形式具体描述这个问题:
问:咱们怎么扩大或缩小容器?
答:咱们启动另一个容器。
问:咱们如何在容器间分摊负荷?若是当前服务器的负荷达到最大,那咱们是否须要另一个服务器?咱们如何最大化硬件使用率?
答:唔......呃......(让我搜一下)
问:若是在打更新补丁的时候,不影响到全部的服务?若是服务出了问题,如何才能返回以前能正常工做的版本?
Kubernetes 能够解决以上全部问题(以及更多问题!)。我能够用一句话总结 Kubernetes:“Kubernetes 是容器控制平台,能够抽象全部的底层基础设施(容器运行用到的基础设施)。”
咱们对容器控制平台有个模糊的概念。在本文后续部分,咱们将看看它的实际应用,可是这是第一次咱们提到“底层基础设施的抽象”,因此咱们来详细看看这个概念。
Kubernetes 经过一个简单的 API 提供底层基础设施的抽象,咱们能够向该 API 发送请求。这些请求可让 Kubernetes 尽最大能力应对。例如,能够简单地要求“Kubernetes 添加映像 x 的 4 个容器。”而后 Kubernetes 会找出使用中的节点,并在内添加新的容器。
这对开发人员来讲意味着什么?意味着开发人员不须要在乎节点的数目,也不须要在乎从哪里运行容器以及如何与它们交流。开发人员不须要管理硬件优化,或担忧节点关闭(它们将遵循墨菲法则),由于新的节点会添加到 Kubernetes 集群。同时 Kubernetes 会在其余运行的节点中添加容器。Kubernetes 会发挥最大的做用。
在上图中咱们看到了一些新东西:
就介绍这么多,跟深刻的介绍会致使咱们分心,咱们能够等到后面一点再介绍,有一些有用的资源,好比官方文档,或者阅读 Marko Lukša 的著做《Kubernetes in Action》。
Kubernetes 另一个深刻人心的点是:它标准化了云服务提供商。这是一个很大胆的宣言,咱们经过以下例子来具体看一看:
好比,有一个 Azure、Google 云平台或其余云服务提供商的专家,他担任了一个搭建在全新的云服务提供商的项目。这可能引发不少后果,好比说:他可能没法在截止期限内完成;公司可能须要招聘更多相关的人员,等等。
相对的,Kubernetes 就没有这个问题。由于不管是哪家云服务提供商,你均可以在上面运行相同的命令。你能够以既定的方式向 API 服务器发送请求。Kubernetes 会负责抽象,并实装这家云服务商。
停一秒钟仔细想一下,这是极其强有力的功能。对公司来讲,这意味着他们不须要绑定到一家云服务商。他们能够计算别家云服务商的开销,而后转移到别家。他们依旧能够保留原来的专家,保留原来的人员,他们还能够花更少的钱。
说了这么多,在下一节中让咱们来实际使用 Kubernetes。
咱们创建了微服务在容器上运行,虽然颇为坎坷,但仍是能够工做的。咱们还提到这种解决方案不具备伸缩性和弹性,而 Kubernetes 能够解决这些问题。在本文的后续章节,咱们会将各个服务转移到由 Kubernetes 管理的容器中,如图所示。
在本文中,咱们将使用 Minikube 进行本地调试,尽管全部东西都是运行在 Azure 和 Google 云平台中的。(我这里是部署在腾讯云上的)
请参阅安装 Minikube 的官方文档:
k8smeetup.github.io/docs/tasks/
在Mac上安装minikube:
curl -Lo minikube https://storage.googleapis.com/minikube/releases/v0.18.0/minikube-darwin-amd64 && chmod +x minikube && sudo mv minikube /usr/local/bin/
复制代码
在Mac上安装kubectl,Kubectl 是向 Kubernetes API 服务器发送请求的客户端:
curl -LO https://storage.googleapis.com/kubernetes-release/release/v1.12.2/bin/darwin/amd64/kubectl && chmod +x kubectl && sudo mv kubectl /usr/local/bin/
复制代码
或者:
curl -LO https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/darwin/amd64/kubectl && chmod +x kubectl && sudo mv kubectl /usr/local/bin/
复制代码
一般状况下,上面命令下载都会很慢,甚至根本就没法下载。有两个办法:
启动minikube:
minikube start
复制代码
第一次启动的时候会自动下载Minikube ISO
,根据网络状况时间会比较久。
注意:有可能会安装失败,好比出现下面的错误:
➜ Desktop minikube start There is a newer version of minikube available (v0.30.0). Download it here: https://github.com/kubernetes/minikube/releases/tag/v0.30.0 To disable this notification, run the following: minikube config set WantUpdateNotification false Starting local Kubernetes cluster... Starting VM... Downloading Minikube ISO 89.51 MB / 89.51 MB [==============================================] 100.00% 0s E1111 20:06:02.564775 4725 start.go:116] Error starting host: Error creating host: Error with pre-create check: "VBoxManage not found. Make sure VirtualBox is installed and VBoxManage is in the path". Retrying. E1111 20:06:02.567379 4725 start.go:122] Error starting host: Error creating host: Error with pre-create check: "VBoxManage not found. Make sure VirtualBox is installed and VBoxManage is in the path" ================================================================================ An error has occurred. Would you like to opt in to sending anonymized crash information to minikube to help prevent future errors? To opt out of these messages, run the command: minikube config set WantReportErrorPrompt false ================================================================================ Please enter your response [Y/n]: 复制代码
意思是说没有找到VboxManager命令,须要先安装VirtualBox。安装完VirtualBox后,再执行
minikube start
时不会从新下载Minikube ISO
。
在启动后,运行 kubectl get nodes
命令能够获得以下结果:
➜ Desktop kubectl get nodes
NAME STATUS ROLES AGE VERSION
minikube NotReady <none> 18m v1.6.0
复制代码
注意:我这里在安装1.12.2版本后,执行
kubectl get nodes
报错:➜ Desktop kubectl get nodes Error from server (NotAcceptable): unknown (get nodes) 复制代码
后面从新安装了
1.8.7
版本就没有这个问题。
Minikube 提供给咱们的 Kubernetes 集群只有一个节点,可是记住咱们并不在意有多少个节点,Kubernetes 会负责抽象,对咱们来讲深刻掌握 Kubernetes 并不重要。
下面咱们将介绍 Kubernetes 的第一个资源:Pod。
我大爱容器,相信如今你也很喜欢容器。那为何 Kubernetes 给咱们最小的可部署计算单元 Pod 呢?Pod是干什么的?由一个或一组容器组成的 Pod 能够共享相同的运行环境。
可是咱们真的须要在一个 Pod 内运行两个容器吗?呃……通常来讲,只会运行一个容器,咱们的例子中也是这样的。可是有些状况下,好比两个容器须要共享卷,或它们之间是经过跨进程的交流方式交流的,又或者它们被绑到一块儿,那么就可使用 Pod。Pod 的另外一个特征是:若是咱们但愿使用其余 Rke 等技术的话,咱们能够作到不依赖 Docker 容器。
总的来讲,Pod 的主要属性包括(如上图所示):
注:容器有个本身独立的文件系统,尽管他们能够经过 Kubernetes 的资源卷共享数据。
更多详细内容,请参阅相关的官方文档:
以下是咱们的第一个 pod sa-frontend 的清单文件,咱们会对文件内容进行逐一解释。
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
复制代码
#1 kind:指定咱们想建立的 Kubernetes 资源的类型。这里是 Pod。
#2 name:定义该资源的名字。咱们在这里命名为 sa-frontend。
#3 spec:该对象定义了资源应有的状态。Pod Spec 中最重要的属性是容器的数组。
#4 image:是指咱们但愿在本 Pod 中启动的容器的映像。
#5 name:Pod 中容器中惟一的名字。
#6 containerPort:是指容器监听的端口号。这只是为了提供文档信息(即使没有这个端口也不会影响访问)。
建立 SA Frontend 的 Pod
你能够在 resource-manifests/sa-frontend-pod.yaml 中找到上述 Pod 的定义。你能够在终端中进入该文件夹,或在命令行输入完整的路径。而后执行以下命令:
kubectl create -f sa-frontend-pod.yaml
pod "sa-frontend" created
复制代码
能够经过以下命令确认 Pod:
kubectl get pods
NAME READY STATUS RESTARTS AGE
sa-frontend 1/1 Running 0 7s
复制代码
若是该 Pod 还处于容器生成中的状态的话,你能够在运行命令的时候加入参数 --watch,当 Pod 进入运行状态的时候,终端会显示信息。
从外部访问应用程序
为了从外部访问应用程序,咱们须要建立服务类型的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 -f sa-frontend-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 资源的部署一节中改善这个方案。
总结 Pod
提供静态文件的 Nginx 网络服务器在另个不一样的 Pod 内运行。如今咱们有两个问题:
Kubernetes 提供了服务类型的资源。在下一节中咱们将详细介绍。
Kubernetes 服务资源能够做为一组提供相同服务的 Pod 的入口。这个资源肩负发现服务和平衡 Pod 之间负荷的重任,如图 16 所示。
在 Kubernetes 集群内,咱们拥有提供不一样服务的 Pod(前端、Spring 网络应用和 Flask Python 应用程序)。因此这里的问题是:服务如何知道该处理哪一个 Pod?例如:它如何生成这些 Pod 的终端列表?
这个问题能够用标签来解决,具体分两个步骤:
下列视图看起来更清晰:
咱们能够看到 Pod 都贴着标签“app: sa-frontend”,服务用这个标签找到目标 Pod。
标签
标签提供了一种简单的方法用于管理Kubernetes资源。它们有一对键值表示,且能够用于全部资源。按照图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
复制代码
咱们看到了一个警告(在应用的时候,而非建立,明白了)。在第二行咱们看到部署了 pod “sa-frontend”和 “sa-frontend2”。咱们能够过滤想要查看的 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 已经贴上了标签,咱们准备好经过服务找到它们了。让咱们定义 LoadBalancer 类型的服务,如图 18 所示。
服务的定义
LoadBalancer 服务的 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
复制代码
#1 kind:服务;
#2 type:指定类型,咱们选择 LoadBalancer,由于咱们想平衡 Pod 之间的负荷;
#3 ports:指定服务获取请求的端口;
#4 protocol:定义交流;
#5 targetPort:能够未来访的请求转发到这个端口;
#6 selector:包含选择pod属性的对象;
#7 app:sa-frontend定义了哪一个是目标 Pod,只有拥有标签“app: sa-frontend”的才是目标 Pod。
经过运行以下命令建立服务:
kubectl create -f service-sa-frontend-lb.yaml
service "sa-frontend-lb" created
复制代码
能够经过运行以下命令检查的服务的状态:
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
复制代码
External-IP 处于 pending 状态(不用再等了,这个状态不会变的)。这是由于咱们使用的是 Minikube。若是咱们在 Azure 或 Google 云服务上运行,那么咱们能够获得一个公开的 IP,那么全世界均可以访问咱们的服务了。
尽管如此,Minikube 也不会置咱们于不顾,它提供一个很是有用的本地调试命令,以下所示:
minikube service sa-frontend-lb
Opening kubernetes service default/sa-frontend-lb in default browser...
复制代码
这能够在浏览器中打开指向该服务的 IP。服务受到请求后,会将请求转发给其中一个 Pod(不用理会是哪一个)。经过利用服务做为访问入口,这种抽象可让咱们看到并将多个 Pod 当成一个来交互。
服务的总结
在本节中,咱们介绍了给资源贴标签,在服务中使用标签做为选择器,咱们还定义并建立了一个 LoadBalancer 的服务。这知足了咱们但愿伸缩应用程序规模的需求(只需加入新的贴了标签的 Pod),并经过将服务做为访问入口在 Pod 之间作负载均衡。
Kubernetes 部署能够帮助每个应用程序的生命都保持相同的一点:那就是变化。此外,只有挂掉的应用程序才会一尘不变,不然,新的需求会源源不断地涌现,更多代码会被开发出来、打包以及部署。这个过程当中的每一步都有可能出错。
部署资源能够自动化应用程序从一版本升迁到另外一版本的过程,并保证服务不间断,若是有意外发生,它可让咱们迅速回滚到前一个版本。
部署实践
如今咱们有两个 Pod 和一个服务开放,并且它们之间有负载均衡(如图 19 所示)。咱们提到过现有的 Pod 还远远不够完美。须要分开管理每个 Pod(建立、更新、删除和监视他们的状况)。快速更新和迅速回滚根本不可能!这样是不行的,部署 Kubernetes 资源能够解决这里的每一个问题。
在继续下面的内容以前,让咱们复述下咱们的目标,经过概述可让咱们更好的理解部署资源的清单文件的定义。咱们想要的是:
在下一节中,咱们能够将这些需求反映到部署的定义中。
部署的定义
以下资源定义的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
复制代码
#1 kind:部署;
#2 replicas:是部署 Spec 对象的一个属性,定义了咱们想运行多少的 Pod。因此是 2;
#3 type:指定从当前版本升迁到下个版本的时候,部署使用的策略。此处的策略 RollingUpdate 能够保证部署期间服务不间断;
#4 maxUnavailable:是 RollingUpdate 对象的一个属性,定义了在升级的时候,最大容许中止的 Pod 数量(与但愿的状态相比)。对咱们的部署来讲,咱们有 2 个副本,这意味着在一个 Pod 中止后,咱们还会有另一个 Pod 运行,因此能够保证应用程序可访问;
#5 maxSurge:是 RollingUpdate 对象的另外一个属性,定义了添加到部署的最大 Pod 数量(与但愿的状态相比)。对咱们的部署来讲,这意味着在向新版本迁移的时候,咱们能够加一个 Pod,那么咱们能够同时拥有个 3 个 Pod;
#6 template:指定 Pod 的模板,部署在建立新 Pod 的时候,会用到该模板。极可能这个很是类似的 Pod 会当即吸引你;
#7 app: sa-frontend:根据模板建立的 Pod 将被贴上该标签;
#8 imagePullPolicy:当设置成 Always 的时候,每一次新部署都会从新获取容器映像。
坦白来讲,这一堆的文本让我更糊涂了,因此仍是让咱们来看个例子:
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,两个是由部署建立的,而另外两个是咱们手动建立的。经过 kubectl delete pod 命令删除其中一个手动建立的 Pod。
练习:删除其中一个部署建立的 Pod,看看结果怎样。在阅读以下的解释前,请先想一想缘由。
解释:删除一个 Pod 后,部署注意到当前的状态(只有 1 个 Pod 在运行)与但愿的状态(2 个 Pod 处于运行状态),因此它会再启动一个 Pod。
那么,除了保持但愿的状态外,使用部署还有什么好处?让咱们先来看看好处。
好处 1:采用零停机时间部署(Zero-downtime)
产品经理带着新的需求来找咱们,说客户想要在前端加一个绿色的按钮。开发者写好了代码后,只需提供给咱们同样必须的东西,容器映像 rinormaloku/sentiment-analysis-frontend:green。而后就该咱们了,咱们须要采用零停机时间部署,这项工做很难吗?让咱们试试看!
编辑 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,它会打开浏览器。咱们能够看到按钮 SEND 已更新了。
“RollingUpdate”背后的状况
在咱们应用了新的部署后,Kubernetes 会将新状态与旧的相比。在咱们的例子中,新状态须要两个 rinormaloku/sentiment-analysis-frontend:green 映像的 Pod。这与当前的运行状态不一样,因此 Kubernetes 会执行 RollingUpdate。
这里的 RollingUpdate 会根据咱们指定的规格执行,也就是“maxUnavailable: 1″和“maxSurge: 1″。这意味着部署须要终止一个 Pod,而且仅能够运行一个新的 Pod。这个过程会不断重复,一直到全部的 Pod被替换(如图 21 所示)。
咱们继续介绍第二个好处。
声明:出于娱乐的目的,下面的部分我按照小说的形式来书写。
好处2:回滚到前一个状态
产品经理跑进办公室说,他遇到一个大麻烦!
产品经理大喊道:“产品环境中的应用程序有一个很关键的 bug!!须要立刻回滚到前一个版本”。
你冷静地看着他,眼睛都没有眨一下,就转向了心爱的终端,而后开始敲:
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
复制代码
你看了一眼前一个部署,而后问产品经理:“上个版本不少 bug,那前一个版本运行得很完美吗?”
产品经理吼道:“是啊,你没听见我说嘛?!”
你没理他,你知道该如何处理,因而你开始敲:
kubectl rollout undo deployment sa-frontend --to-revision=1
deployment "sa-frontend" rolled back
复制代码
而后,你轻轻地刷新了页面,以前的修改全都不见了!
产品经理瞠目结舌地看着你。
你拯救了你们!
完
我知道……这是个很无聊的故事。在 Kubernetes 出现以前,这个故事挺好的,更加戏剧化,让人高度紧张,并且这种状态持续了很长时间。那段旧时光仍是很美好的!
大多数的命令都自带说明,只是有一些细节你须要本身搞清楚。为何第一个版本中字段 CHANGE-CAUSE 的值为 ,而同时第二次改版的时候,CHANGE-CAUSE 的值为“kubectl.exe apply –filename=sa-frontend-deployment-green.yaml –record=true”。
你应该能够发现这是由于在应用新的映像的时候,咱们用到了标志符 --record。
在下一节中,咱们将使用以前全部的概念,完成整个架构。
如今咱们学习了完成架构的全部必须的资源,所以这一节会很是快。图 22 中灰色的部分是须要作的事情。让咱们从底部开始:部署 sa-logic 的部署。
部署 SA-Logic
在终端中进入资源清单文件所在的目录,而后运行以下命令:
kubectl apply -f sa-logic-deployment.yaml --record
deployment "sa-logic" created
复制代码
SA-Logic 的部署会建立三个 Pod(Pod 上运行着咱们的 Python 应用)。该命令还会给Pod 贴上 app: sa-logic 的标签。有了这个标签,咱们就能从 SA-Logic 服务中利用选择器来选择这些 Pod。请花点时间打开 sa-logic-deployment.yaml,查看其内容。
这里的概念都是同样的,所以咱们能够直接讲解下一个资源:SA-Logic 服务。
SA Logic 服务
首先来解释下为何须要该服务。咱们的 Java 应用(在 SA-WebApp 部署的 Pod 中运行)依赖于 Python 应用提供的情感分析。但如今,与咱们在本地运行一切服务时的情况不一样,咱们并无一个单一的 Python 应用监听着某个端口,咱们只有两个 Pod,若是须要,咱们能够有更多的 Pod。
这就是为何须要“服务”为一组提供相同功能的 Pod 提供访问入口。这就是说,咱们能够利用 SA-Logic 服务做为全部 SA-Logic Pod 的访问入口。
运行以下命令:
kubectl apply -f service-sa-logic.yaml
service "sa-logic" created
复制代码
更新后的应用程序状态:如今咱们有两个 Pod 在运行(包含 Python 应用程序),而且 SA-Logic 服务提供了访问入口,该访问入口将在 SA-WebApp 的 Pod 中使用。
如今须要部署 SA-WebApp Pod,咱们须要用到部署资源。
SA-WebApp 部署
咱们已经学过了部署,尽管这个部署会用到更多的特性。打开 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 属性。咱们猜想它定义了环境变量 SA_LOGIC_API_URl,值为在 Pod 内的值为 http://sa-logic。但为何要初始化成 http://sa-logic,sa-logic 到底是什么?
咱们先来介绍下 kube-dns。
KUBE-DNS
Kubernetes 有个特殊的 Pod 叫作 kube-dns。默认状况下,全部 Pod 都用它做为 DNS 服务器。kube-dns 的一个重要属性就是它为每一个创建的访问都建立一条 DNS 记录。
这就是说当咱们建立 sa-logic 服务时,它会得到一个 IP 地址。它的名字会加入到 kube-dns 中(和它的 IP 地址一块儿)。这样全部 Pod 都可以把 sa-logic 翻译成 SA-Logic 服务的 IP 地址。
好,如今能够继续了:
SA WebApp 部署(续)
运行如下命令:
kubectl apply -f sa-web-app-deployment.yaml --record
deployment "sa-web-app" created
复制代码
完了。剩下的工做就是经过 LoadBalancer 服务将 SA-WebApp Pod 暴露到外部。LoadBalancer 服务提供了 SA-WebApp Pod 的访问入口,这样 React 应用程序就能发送 HTTP 请求了。
SA-WebApp 服务
打开 service-sa-web-app-lb.yaml 文件,能够看到内容仍是挺熟悉的。
因此咱们能够运行以下命令:
kubectl apply -f service-sa-web-app-lb.yaml
service "sa-web-app-lb" created
复制代码
这样架构就完成了。但还有一点不完美的地方。在部署 SA-Frontend Pod 以后,容器映像指向了 http://localhost:8080/sentiment 处的 SA-WebApp。但如今咱们须要将其更新为 SA-WebApp LoadBalancer 的 IP 地址(其做用是 SA-WebApp Pod 的访问入口)。
修补该不完美是个快速复习一切的绝佳机会(若是能不参照如下的指南独立完成更好)。下面咱们开始:
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 |
|-------------|----------------------|-----------------------------|
复制代码
analyzeSentence() {
fetch('http://192.168.99.100:31691/sentiment', { /* shortened for brevity */})
.then(response => response.json())
.then(data => this.setState(data));
}
复制代码
构建静态文件 npm build (须要先切换到 sa-front-end 目录);
构建容器映像:
docker build -f Dockerfile -t $DOCKER_USER_ID/sentiment-analysis-frontend:minikube .
将映像推送到 Docker hub:
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 吧!
本文中覆盖的内容:
欢迎关注公众号:非著名开发者,获取更多精彩内容。