7

入门实践丨如何在K3s上部署Web应用程序

 3 years ago
source link: http://dockone.io/article/2434376
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

入门实践丨如何在K3s上部署Web应用程序


在本文中,我们将使用Flask和JavaScript编写的、带有MongoDB数据库的TODO应用程序,并学习如何将其部署到Kubernetes上。这篇文章是针对初学者的,如果你之前没有深度接触过Kubernetes集群,也不要担心!

我们将使用K3s,这是一个轻量级的Kubernetes发行版,非常适合快速入门。但首先让我们谈谈我们想要实现的目标。

首先,我将介绍示例应用程序。这其实已经简化了许多细节,但它说明了常见的用例。然后我们将熟悉了解容器化应用程序的过程。在我们继续之前,我会讨论我们如何使用容器来让我们的开发更加轻松,特别是如果我们在一个团队中工作,或者是当我们在一个新的环境中工作时,希望减轻开发人员的负担。

一旦我们将应用程序容器化,下一步就是将它们部署到Kubernetes上。虽然我们可以手动创建服务、Ingress和网关,但我们可以使用Knative以在任何时候都支持我们的应用程序。

设置应用程序

我们将使用一个简单的TODO应用程序来演示前端、REST API后端和MongoDB协同工作。这要归功于Prashant Shahi提出的这个例子。我做了一些小改动,纯粹是为了教学的目的:

https://github.com/prashant-shahi

首先,git clone代码库:
git clone https://github.com/benjamintanweihao/Flask-MongoDB-K3s-KNative-TodoApp


接下来,我们将检查目录,了解情况:
cd Flask-MongoDB-K3s-KNative-TodoApp
tree


该文件夹结构是一个典型的Flask应用程序。Entry point是app.py,它还包含REST APIs。Templates文件夹包含了将被渲染成HTML的文件:

打开 app.py,我们可以看到所有的主要部分:
├── app.py
├── requirements.txt
├── static
│   ├── assets
│   │   ├── style.css
│   │   ├── twemoji.js
│   │   └── twemoji.min.js
└── templates
├── index.html
└── update.html


从上面的代码段,您可以看到应用程序需要MongoDB作为数据库。使用lists()方法,您可以看到如何定义路由(即@ app.route(“/ list”))、如何从MongoDB获取数据,以及模板是如何呈现的示例。
mongodb_host = os.environ.get('MONGO_HOST', 'localhost')
mongodb_port = int(os.environ.get('MONGO_PORT', '27017'))
client = MongoClient(mongodb_host, mongodb_port)
db = client.camp2016
todos = db.todo 

app = Flask(__name__)
title = "TODO with Flask"

@app.route("/list")
def lists ():
#Display the all Tasks
todos_l = todos.find()
a1="active"
return render_template('index.html',a1=a1,todos=todos_l,t=title,h=heading)

if __name__ == "__main__":
env = os.environ.get('APP_ENV', 'development')
port = int(os.environ.get('PORT', 5000))
debug = False if env == 'production' else True
app.run(host='0.0.0.0', port=port, debug=debug)


这里需要注意的另一件事是使用了MONGO_HOST和MONGO_PORT的环境变量和Flask相关的环境变量。其中,最重要的是debug。当变量设置为True时,Flask服务器会在检测到和发生更改时自动重新加载。这在开发过程中特别方便,也是我们要充分利用的特性。

用Docker容器开发

在处理应用程序时,我曾经花费大量时间设置环境并安装所有依赖项。在那之后,我可以通过添加新功能来启动和运行。然而,这仅仅描述了一个理想的场景,对吗?

你有多少次回到你已经开发的应用程序(比如六个月前),却发现自己正在慢慢陷入依赖项地狱?依赖项通常是一个灵活的目标,除非您采取措施锁定对象,否则您的应用程序可能无法正常工作。解决这个问题的方法之一是将所有依赖项打包到Docker容器中。

Docker带来的另一件特性是自动化。这意味着不再需要复制和粘贴命令,也不再需要设置数据库之类的东西。

Docker化 Flask程序

以下是Dockerfile:
FROM alpine:3.7
COPY . /app
WORKDIR /app

RUN apk add --no-cache bash git nginx uwsgi uwsgi-python py2-pip \
&& pip2 install --upgrade pip \
&& pip2 install -r requirements.txt \
&& rm -rf /var/cache/apk/*

EXPOSE 5000
ENTRYPOINT ["python"]


我们从一个最小的(在大小和功能方面)基础镜像开始。然后,应用程序的内容进入容器中的/app目录。接下来,我们执行一系列命令来安装Python、Nginx web server和Flask应用程序的所有需求。这些正是在新系统上设置应用程序所需的步骤。

您可以这样构建Docker容器:
% docker build -t <yourusername>/todo-app .


你将看到这样如下输出:
# ...
Successfully built c650af8b7942
Successfully tagged benjamintanweihao/todo-app:latest


那 MongoDB 呢?

您是否应该经历为MongoDB创建Dockerfile的相同过程?在此之前,已经有人做过这样的尝试,具体演示请查看案例链接:https://hub.docker.com/_/mongo.不过现在您有两个容器,其中Flask容器依赖于MongoDB容器。

一种方法是先启动MongoDB容器,然后启动Flask容器。但是,假设您想添加缓存并决定引入Redis容器。那么启动每个容器的过程会很快变枯燥繁琐。解决方案是Docker Compose,这是一个允许您定义和运行多个Docker容器的工具,正符合我们当前面临的情况。

Docker Compose

以下是Docker compose文件,docker-compose.yaml:
services:
flaskapp:
build: .
image: benjamintanweihao/todo-app:latest
ports:
  - 5000:5000
container_name: flask-app
environment:
  - MONGO_HOST=mongo
  - MONGO_PORT=27017
networks:
  - todo-net
depends_on:
  - mongo
volumes:
  - .:/app # <--- 
mongo:
image: mvertes/alpine-mongo
ports:
  - 27017:27017
networks:
  - todo-net

networks:
todo-net:
driver: bridge


即使您不熟悉Docker Compose,这里的YAML文件也并不复杂。让我们看一下重要的部分。

在最开头,这个文件定义了由flaskapp和mongo组成的服务,以及指定桥接连接的网络。这将创建一个网络连接,以便服务中定义的容器可以相互通信。

每个服务都定义镜像、端口映射和前面定义的网络。在flaskapp中也定义了环境变量(请查看app.py,看看它们是否确实是相同的)。

我想提醒您注意flask应用程序中指定的volume。我们在这里所做的是将主机的当前目录(应该是包含app.py的项目目录)映射到容器的/app目录 我们为什么要这样做?回想一下,在Dockerfile中,我们将app复制到/app目录中,如下所示:
COPY . /app


假设你想对应用程序做一个更改。你不可能轻易改变容器中的app.py。通过对本地目录的映射,你基本上是在用你目录中的本地副本覆盖容器中的app.py。因此,假设Flask应用程序处于调试模式(如果你在这一点上没有改变任何东西的话,它就是调试模式),当你启动容器并做出改变时,渲染的输出会反映出这个改变。

但是,重要的是要意识到容器中的app.py仍然是旧版本,您仍然需要记住构建新镜像(希望您已将CI/CD设置为自动执行此操作)

让我们看看这是怎么回事。运行以下命令:
docker-compose up


接下来你将看到:
Creating network "flask-mongodb-k3s-knative-todoapp_my-net" with driver "bridge"
Creating flask-mongodb-k3s-knative-todoapp_mongo_1 ... done
Creating flask-app                                 ... done
Attaching to flask-mongodb-k3s-knative-todoapp_mongo_1, flask-app

... more output truncated

flask-app   |  * Serving Flask app "app" (lazy loading)
flask-app   |  * Environment: production
flask-app   |    WARNING: Do not use the development server in a production environment.
flask-app   |    Use a production WSGI server instead.
flask-app   |  * Debug mode: on
flask-app   |  * Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)
flask-app   |  * Restarting with stat
mongo_1     | 2021-05-15T15:41:37.993+0000 I NETWORK  [listener] connection accepted from 172.23.0.1:48844 #2 (2 connections now open)
mongo_1     | 2021-05-15T15:41:37.993+0000 I NETWORK  [conn2] received client metadata from 172.23.0.1:48844 conn2: { driver: { name: "PyMongo", version: "3.11.4" }, os: { type: "Linux", name: "", architecture: "x86_64", version: "5.8.0-53-generic" }, platform: "CPython 2.7.15.final.0" }
flask-app   |  * Debugger is active!
flask-app   |  * Debugger PIN: 183-021-098


现在开始在浏览器中访问:http://localhost:5000

图片

如果你看到这个,恭喜你!Flask和Mongo在一起正常工作了。您可以随意使用应用程序来感受它。

现在让我们对应用程序标题中的app.py做一个小小的改动:
index d322672..1c447ba 100644
--- a/app.py
+++ b/app.py
-heading = "tOdO Reminder"
+heading = "TODO Reminder!!!!!"


保存文件并重新加载应用程序:

图片

完成后,您可以输入以下命令:
docker-compose down

将应用程序部署到Kubernetes上

截至目前,我们已将我们的应用程序及其支持服务(现在只是MongoDB)容器化。我们如何开始将我们的应用程序部署到Kubernetes?

在此之前,让我们安装Kubernetes。为此,我选择了K3s,因为它是安装Kubernetes和启动和运行的最简单方法。
% curl -sfL https://get.k3s.io | INSTALL_K3S_EXEC="server --no-deploy=traefik"  sh -s -


过一会儿,你就可以安装 Kubernetes了:
[INFO]  Finding release for channel stable
[INFO]  Using v1.20.6+k3s1 as release
[INFO]  Downloading hash https://github.com/k3s-io/k3s/releases/download/v1.20.6+k3s1/sha256sum-amd64.txt

truncated ...

[INFO]  systemd: Starting k3s


验证是否已正确设置K3s:
% kubectl get no
NAME      STATUS   ROLES                  AGE     VERSION
artemis   Ready    control-plane,master   2m53s   v1.20.6+k3s1


MongoDB

有多种方法可以完成这一操作。您可以使用我们创建的镜像,MongoDB operator或Helm:
helm install mongodb-release bitnami/mongodb --set architecture=standalone --set auth.enabled=false
Please be patient while the chart is being deployed 

MongoDB(R) can be accessed on the following DNS name(s) and ports from within your cluster:

mongodb-release.default.svc.cluster.local

To connect to your database, create a MongoDB(R) client container:

kubectl run --namespace default mongodb-release-client --rm --tty -i --restart='Never' --env="MONGODB_ROOT_PASSWORD=$MONGODB_ROOT_PASSWORD" --image docker.io/bitnami/mongodb:4.4.6-debian-10-r0 --command -- bash

Then, run the following command:
mongo admin --host "mongodb-release"

To connect to your database from outside the cluster execute the following commands:

kubectl port-forward --namespace default svc/mongodb-release 27017:27017 &
mongo --host 127.0.0.1


安装Knative和Istio

在本文中,我们将使用Knative。Knative构建在Kubernetes之上,使得开发人员可以很容易地部署和运行应用程序,而不必知道Kubernetes的很多细节。

Knative由两部分组成:Serving和Eventing。在本节中,我们将讨论Serving部分。使用Knative Serving,您可以在几秒钟内创建可弹性伸缩的、安全的和无状态的服务,这就是我们需要对TODO应用程序做的!在此之前,我们先安装Knative:

以下说明基于:

https://knative.dev/docs/insta ... yaml/
kubectl apply -f https://github.com/knative/serving/releases/download/v0.22.0/serving-crds.yaml
kubectl apply -f https://github.com/knative/serving/releases/download/v0.22.0/serving-core.yaml
kubectl apply -f https://github.com/knative/net-istio/releases/download/v0.22.0/istio.yaml
kubectl apply -f https://github.com/knative/net-istio/releases/download/v0.22.0/net-istio.yaml


这设置了Knative和istio。你可能想知道为什么我们需要Istio。原因是Knative需要一个Ingress Controller,使其可以执行流量分发(例如, Todo应用程序的版本1和版本2需要同时运行)和自动HTTP请求重试。

Istio有替代方案吗?或许可以考虑Gloo(https://docs.solo.io/gloo-edge ... tive/)。但当前不支持Traefik,这就是为什么我们在安装K3s时必须禁用它。由于Istio是默认且最受支持的,我们将使用它。

现在等待所有的knative-serving 的pod运行:

k
ubectl get pods --namespace knative-serving -w
NAME                                READY   STATUS    RESTARTS   AGE
controller-57956677cf-2rqqd         1/1     Running   0          3m39s
webhook-ff79fddb7-mkcrv             1/1     Running   0          3m39s
autoscaler-75895c6c95-2vv5b         1/1     Running   0          3m39s
activator-799bbf59dc-t6v8k          1/1     Running   0          3m39s
istio-webhook-5f876d5c85-2hnvc      1/1     Running   0          44s
networking-istio-6bbc6b9664-shtd2   1/1     Running   0          44s


设置自定义域

默认情况下,Knative Serving使用example.com作为默认域。如果您按照说明设置了K3s,则应该安装负载均衡器。这意味着通过一些设置,您可以使用sslip.io之类的DNS服务创建自定义域。

sslip.io是一种服务,当使用带有嵌入式IP地址的主机名进行查询时,它会返回该IP地址。例如,192.168.0.1.sslip.io等URL将指向192.168.0.1。这是极好的服务,你不必去买你自己的域名。

继续并应用以下manifest:
kubectl apply -f https://storage.googleapis.com/knative-nightly/serving/latest/serving-default-domain.yaml


如果您打开 serving-default-domain. yaml,您需要在 spec 中注意到以下内容:
# other parts truncated     
spec:
serviceAccountName: controller
containers:
    - name: default-doma
      image: ko://knative.dev/serving/cmd/default-domain
      args: ["-magic-dns=sslip.io"]


这将启用您将在下一步中需要使用的DNS。

测试是否一切正常

下载kn二进制文件。您可以查阅链接:https://knative.dev/development/client/install-kn/。一定要重命名二进制文件 kn然后把它放在$PATH的某个地方。一旦解决了这个问题,就继续创建示例Hello World服务。我已经将benjamintanweihao/helloworld python镜像推送到Docker Hub:
% kn service create helloworld-python --image=docker.io/benjamintanweihao/helloworld-python --env TARGET="Python Sample v1"


这将产生以下输出:
Creating service 'helloworld-python' in namespace 'default':

0.037s The Route is still working to reflect the latest desired specification.
0.099s Configuration "helloworld-python" is waiting for a Revision to become ready.
29.277s ...
29.314s Ingress has not yet been reconciled.
29.446s Waiting for load balancer to be ready
29.605s Ready to serve.

Service 'helloworld-python' created to latest revision 'helloworld-python-00001' is available at URL:
http://helloworld-python.default.192.168.86.26.sslip.io


输入以下代码即可列出所有命名空间中所有已部署的Knative服务:
% kn service  list -A


如果有kubectl,这就变成:
% kubectl get ksvc -A


要删除服务,只需执行以下操作:
kn service delete helloworld-python # or kubectl delete ksvc helloworld-python


如果您还没有这样做,请确保TODO应用程序镜像已推送到DockerHub。记住用DockerHub ID替换{username}:
% docker push {username}/todo-app:latest


推送镜像后,可以使用kn命令创建TODO服务。记住用DockerHub ID替换{username}:
kn service create todo-app --image=docker.io/{username}/todo-app --env MONGO_HOST="mongodb-release.default.svc.cluster.local" 


如果一切运行顺利,你将看到:
Creating service 'todo-app' in namespace 'default':

0.022s The Route is still working to reflect the latest desired specification.
0.085s Configuration "todo-app" is waiting for a Revision to become ready.
4.586s ...
4.608s Ingress has not yet been reconciled.
4.675s Waiting for load balancer to be ready
4.974s Ready to serve.

Service 'todo-app' created to latest revision 'todo-app-00001' is available at URL:
http://todo-app.default.192.168.86.26.sslip.io


现在访问http://todo-app.default.192.168.86.26.sslip.io (或者在上一个输出的最后一行的内容)您应该可以看到应用程序!现在我们来看看Knative为你做了什么。KNative仅需一行命令就可以为您启动一个服务,并且为您提供了一个可以从集群访问的URL。

我对Knative的了解仅仅停留于表面,但我希望这个教程可以激励你更多地了解它!当我开始看Knative的时候,我不太明白它做了什么。希望这个例子能让我们看到Knative的惊人之处和它的便利性。

在本文中,我们简要介绍了使用Python构建的web应用程序并需要MongoDB,并学习了如何:
  • 使用Docker容器化TODO应用程序
  • 使用Docker减轻依赖项
  • 使用Docker进行开发
  • 使用Docker Compose打包多个容器
  • 安装K3s
  • 安装Knative(Serving)和Istio
  • 使用Helm署MongoDB
  • 使用Knative部署TODO应用程序
虽然将应用程序迁移到 Kubernetes 当然不是一项简单的任务,但是将应用程序容器化通常会让你成功一半。当然,本文还有很多东西没有涉及,比如安全性和可扩展性。

K3s 是轻量级的Kubernetes发行版,可以极其轻松地使用笔记本/台式机测试和运行 Kubernetes 工作负载,对个人开发者来说十分友好。同时,也十分适合企业在边缘端大规模部署集群。

当我开始研究 Knative 的时候,我并不十分理解它的作用。希望这个例子能够帮助我们理解 Knative 的魅力及其带来的便利。实际上,Knative 的亮点之一就是“在几秒钟之内就能启动一个可扩展的、安全的、无状态的服务”。

我将在以后的文章中更多地介绍 Knative,并更深入地探讨其核心特征。我希望你能通过这篇教程,将它们应用到你的应用程序中!

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK