入門(mén)實(shí)踐丨如何在K3s上部署Web應(yīng)用程序
直播預(yù)告
在本文中,我們將使用Flask和JavaScript編寫(xiě)的、帶有MongoDB數(shù)據(jù)庫(kù)的TODO應(yīng)用程序,并學(xué)習(xí)如何將其部署到Kubernetes上。這篇文章是針對(duì)初學(xué)者的,如果你之前沒(méi)有深度接觸過(guò)Kubernetes集群,也不要擔(dān)心!
我們將使用K3s,這是一個(gè)輕量級(jí)的Kubernetes發(fā)行版,非常適合快速入門(mén)。但首先讓我們談?wù)勎覀兿胍獙?shí)現(xiàn)的目標(biāo)。
首先,我將介紹示例應(yīng)用程序。這其實(shí)已經(jīng)簡(jiǎn)化了許多細(xì)節(jié),但它說(shuō)明了常見(jiàn)的用例。然后我們將熟悉了解容器化應(yīng)用程序的過(guò)程。在我們繼續(xù)之前,我會(huì)討論我們?nèi)绾问褂萌萜鱽?lái)讓我們的開(kāi)發(fā)更加輕松,特別是如果我們?cè)谝粋€(gè)團(tuán)隊(duì)中工作,或者是當(dāng)我們?cè)谝粋€(gè)新的環(huán)境中工作時(shí),希望減輕開(kāi)發(fā)人員的負(fù)擔(dān)。
一旦我們將應(yīng)用程序容器化,下一步就是將它們部署到Kubernetes上。雖然我們可以手動(dòng)創(chuàng)建服務(wù)、Ingress和網(wǎng)關(guān),但我們可以使用Knative以在任何時(shí)候都支持我們的應(yīng)用程序。
設(shè)置應(yīng)用程序
我們將使用一個(gè)簡(jiǎn)單的TODO應(yīng)用程序來(lái)演示前端、REST API后端和MongoDB協(xié)同工作。這要?dú)w功于Prashant Shahi提出的這個(gè)例子。我做了一些小改動(dòng),純粹是為了教學(xué)的目的:
https://github.com/prashant-shahi
首先,git clone代碼庫(kù):
git clone https://github.com/benjamintanweihao/Flask-MongoDB-K3s-KNative-TodoApp接下來(lái),我們將檢查目錄,了解情況:
cd Flask-MongoDB-K3s-KNative-TodoApptree
該文件夾結(jié)構(gòu)是一個(gè)典型的Flask應(yīng)用程序。Entry point是app.py,它還包含REST APIs。Templates文件夾包含了將被渲染成HTML的文件:
打開(kāi) app.py,我們可以看到所有的主要部分:
├── app.py├── requirements.txt├── static│ ├── assets│ │ ├── style.css│ │ ├── twemoji.js│ │ └── twemoji.min.js└── templates├── index.html└── update.html
從上面的代碼段,您可以看到應(yīng)用程序需要MongoDB作為數(shù)據(jù)庫(kù)。使用lists()方法,您可以看到如何定義路由(即@ app.route(“/ list”))、如何從MongoDB獲取數(shù)據(jù),以及模板是如何呈現(xiàn)的示例。
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.camp2016todos = db.todoapp = Flask(__name__)title = "TODO with Flask"@app.route("/list")def lists ():#Display the all Taskstodos_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 Trueapp.run(host='0.0.0.0', port=port, debug=debug)
這里需要注意的另一件事是使用了MONGO_HOST和MONGO_PORT的環(huán)境變量和Flask相關(guān)的環(huán)境變量。其中,最重要的是debug。當(dāng)變量設(shè)置為T(mén)rue時(shí),F(xiàn)lask服務(wù)器會(huì)在檢測(cè)到和發(fā)生更改時(shí)自動(dòng)重新加載。這在開(kāi)發(fā)過(guò)程中特別方便,也是我們要充分利用的特性。
用Docker容器開(kāi)發(fā)
在處理應(yīng)用程序時(shí),我曾經(jīng)花費(fèi)大量時(shí)間設(shè)置環(huán)境并安裝所有依賴項(xiàng)。在那之后,我可以通過(guò)添加新功能來(lái)啟動(dòng)和運(yùn)行。然而,這僅僅描述了一個(gè)理想的場(chǎng)景,對(duì)嗎?
你有多少次回到你已經(jīng)開(kāi)發(fā)的應(yīng)用程序(比如六個(gè)月前),卻發(fā)現(xiàn)自己正在慢慢陷入依賴項(xiàng)地獄?依賴項(xiàng)通常是一個(gè)靈活的目標(biāo),除非您采取措施鎖定對(duì)象,否則您的應(yīng)用程序可能無(wú)法正常工作。解決這個(gè)問(wèn)題的方法之一是將所有依賴項(xiàng)打包到Docker容器中。
Docker帶來(lái)的另一件特性是自動(dòng)化。這意味著不再需要復(fù)制和粘貼命令,也不再需要設(shè)置數(shù)據(jù)庫(kù)之類(lèi)的東西。
Docker化 Flask程序
以下是Dockerfile:
FROM alpine:3.7COPY . /appWORKDIR /appRUN 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 5000ENTRYPOINT ["python"]
我們從一個(gè)最小的(在大小和功能方面)基礎(chǔ)鏡像開(kāi)始。然后,應(yīng)用程序的內(nèi)容進(jìn)入容器中的/app目錄。接下來(lái),我們執(zhí)行一系列命令來(lái)安裝Python、Nginx web server和Flask應(yīng)用程序的所有需求。這些正是在新系統(tǒng)上設(shè)置應(yīng)用程序所需的步驟。
您可以這樣構(gòu)建Docker容器:
docker build -t <yourusername>/todo-app .你將看到這樣如下輸出:
# ...Successfully built c650af8b7942Successfully tagged benjamintanweihao/todo-app:latest
那 MongoDB 呢?
您是否應(yīng)該經(jīng)歷為MongoDB創(chuàng)建Dockerfile的相同過(guò)程?在此之前,已經(jīng)有人做過(guò)這樣的嘗試,具體演示請(qǐng)查看案例鏈接:https://hub.docker.com/_/mongo.不過(guò)現(xiàn)在您有兩個(gè)容器,其中Flask容器依賴于MongoDB容器。
一種方法是先啟動(dòng)MongoDB容器,然后啟動(dòng)Flask容器。但是,假設(shè)您想添加緩存并決定引入Redis容器。那么啟動(dòng)每個(gè)容器的過(guò)程會(huì)很快變枯燥繁瑣。解決方案是Docker Compose,這是一個(gè)允許您定義和運(yùn)行多個(gè)Docker容器的工具,正符合我們當(dāng)前面臨的情況。
Docker Compose
以下是Docker compose文件,docker-compose.yaml:
services:flaskapp:build: .image: benjamintanweihao/todo-app:latestports:5000:5000container_name: flask-appenvironment:MONGO_HOST=mongoMONGO_PORT=27017networks:todo-netdepends_on:mongovolumes:.:/app # <---mongo:image: mvertes/alpine-mongoports:27017:27017networks:todo-netnetworks::driver: bridge
即使您不熟悉Docker Compose,這里的YAML文件也并不復(fù)雜。讓我們看一下重要的部分。
在最開(kāi)頭,這個(gè)文件定義了由flaskapp和mongo組成的服務(wù),以及指定橋接連接的網(wǎng)絡(luò)。這將創(chuàng)建一個(gè)網(wǎng)絡(luò)連接,以便服務(wù)中定義的容器可以相互通信。
每個(gè)服務(wù)都定義鏡像、端口映射和前面定義的網(wǎng)絡(luò)。在flaskapp中也定義了環(huán)境變量(請(qǐng)查看app.py,看看它們是否確實(shí)是相同的)。
我想提醒您注意flask應(yīng)用程序中指定的volume。我們?cè)谶@里所做的是將主機(jī)的當(dāng)前目錄(應(yīng)該是包含app.py的項(xiàng)目目錄)映射到容器的/app目錄 我們?yōu)槭裁匆@樣做?回想一下,在Dockerfile中,我們將app復(fù)制到/app目錄中,如下所示:
COPY . /app假設(shè)你想對(duì)應(yīng)用程序做一個(gè)更改。你不可能輕易改變?nèi)萜髦械腶pp.py。通過(guò)對(duì)本地目錄的映射,你基本上是在用你目錄中的本地副本覆蓋容器中的app.py。因此,假設(shè)Flask應(yīng)用程序處于調(diào)試模式(如果你在這一點(diǎn)上沒(méi)有改變?nèi)魏螙|西的話,它就是調(diào)試模式),當(dāng)你啟動(dòng)容器并做出改變時(shí),渲染的輸出會(huì)反映出這個(gè)改變。
但是,重要的是要意識(shí)到容器中的app.py仍然是舊版本,您仍然需要記住構(gòu)建新鏡像(希望您已將CI/CD設(shè)置為自動(dòng)執(zhí)行此操作)
讓我們看看這是怎么回事。運(yùn)行以下命令:
docker-compose up接下來(lái)你將看到:
Creating network "flask-mongodb-k3s-knative-todoapp_my-net" with driver "bridge"Creating flask-mongodb-k3s-knative-todoapp_mongo_1 ... doneCreating flask-app ... doneAttaching to flask-mongodb-k3s-knative-todoapp_mongo_1, flask-app# ... more output truncatedflask-app | * Serving Flask app "app" (lazy loading)flask-app | * Environment: productionflask-app | WARNING: Do not use the development server in a production environment.flask-app | Use a production WSGI server instead.flask-app | * Debug mode: onflask-app | * Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)flask-app | * Restarting with statmongo_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
現(xiàn)在開(kāi)始在瀏覽器中訪問(wèn):http://localhost:5000

如果你看到這個(gè),恭喜你!Flask和Mongo在一起正常工作了。您可以隨意使用應(yīng)用程序來(lái)感受它。
現(xiàn)在讓我們對(duì)應(yīng)用程序標(biāo)題中的app.py做一個(gè)小小的改動(dòng):
index d322672..1c447ba 100644--- a/app.py+++ b/app.py-heading = "tOdO Reminder"+heading = "TODO Reminder!!!!!"
保存文件并重新加載應(yīng)用程序:

完成后,您可以輸入以下命令:
docker-compose down將應(yīng)用程序部署到Kubernetes上
截至目前,我們已將我們的應(yīng)用程序及其支持服務(wù)(現(xiàn)在只是MongoDB)容器化。我們?nèi)绾伍_(kāi)始將我們的應(yīng)用程序部署到Kubernetes?
在此之前,讓我們安裝Kubernetes。為此,我選擇了K3s,因?yàn)樗前惭bKubernetes和啟動(dòng)和運(yùn)行的最簡(jiǎn)單方法。
curl -sfL https://get.k3s.io | INSTALL_K3S_EXEC="server --no-deploy=traefik" sh -s -過(guò)一會(huì)兒,你就可以安裝 Kubernetes了:
[] Finding release for channel stable[] Using v1.20.6+k3s1 as release[] Downloading hash https://github.com/k3s-io/k3s/releases/download/v1.20.6+k3s1/sha256sum-amd64.txt[] systemd: Starting k3s
驗(yàn)證是否已正確設(shè)置K3s:
% kubectl get noNAME STATUS ROLES AGE VERSIONartemis Ready control-plane,master 2m53s v1.20.6+k3s1
MongoDB
有多種方法可以完成這一操作。您可以使用我們創(chuàng)建的鏡像,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.localTo 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 -- bashThen, 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構(gòu)建在Kubernetes之上,使得開(kāi)發(fā)人員可以很容易地部署和運(yùn)行應(yīng)用程序,而不必知道Kubernetes的很多細(xì)節(jié)。
Knative由兩部分組成:Serving和Eventing。在本節(jié)中,我們將討論Serving部分。使用Knative Serving,您可以在幾秒鐘內(nèi)創(chuàng)建可彈性伸縮的、安全的和無(wú)狀態(tài)的服務(wù),這就是我們需要對(duì)TODO應(yīng)用程序做的!在此之前,我們先安裝Knative:
以下說(shuō)明基于:
https://knative.dev/docs/install/install-serving-with-yaml/
kubectl apply -f https://github.com/knative/serving/releases/download/v0.22.0/serving-crds.yamlkubectl apply -f https://github.com/knative/serving/releases/download/v0.22.0/serving-core.yamlkubectl apply -f https://github.com/knative/net-istio/releases/download/v0.22.0/istio.yamlkubectl apply -f https://github.com/knative/net-istio/releases/download/v0.22.0/net-istio.yaml
這設(shè)置了Knative和istio。你可能想知道為什么我們需要Istio。原因是Knative需要一個(gè)Ingress Controller,使其可以執(zhí)行流量分發(fā)(例如, Todo應(yīng)用程序的版本1和版本2需要同時(shí)運(yùn)行)和自動(dòng)HTTP請(qǐng)求重試。
Istio有替代方案嗎?或許可以考慮Gloo(https://docs.solo.io/gloo-edge/master/installation/knative/)。但當(dāng)前不支持Traefik,這就是為什么我們?cè)诎惭bK3s時(shí)必須禁用它。由于Istio是默認(rèn)且最受支持的,我們將使用它。
現(xiàn)在等待所有的knative-serving 的pod運(yùn)行:
kubectl get pods --namespace knative-serving -wNAME READY STATUS RESTARTS AGEcontroller-57956677cf-2rqqd 1/1 Running 0 3m39swebhook-ff79fddb7-mkcrv 1/1 Running 0 3m39sautoscaler-75895c6c95-2vv5b 1/1 Running 0 3m39sactivator-799bbf59dc-t6v8k 1/1 Running 0 3m39sistio-webhook-5f876d5c85-2hnvc 1/1 Running 0 44snetworking-istio-6bbc6b9664-shtd2 1/1 Running 0 44s
設(shè)置自定義域
默認(rèn)情況下,Knative Serving使用example.com作為默認(rèn)域。如果您按照說(shuō)明設(shè)置了K3s,則應(yīng)該安裝負(fù)載均衡器。這意味著通過(guò)一些設(shè)置,您可以使用sslip.io之類(lèi)的DNS服務(wù)創(chuàng)建自定義域。
sslip.io是一種服務(wù),當(dāng)使用帶有嵌入式IP地址的主機(jī)名進(jìn)行查詢時(shí),它會(huì)返回該IP地址。例如,192.168.0.1.sslip.io等URL將指向192.168.0.1。這是極好的服務(wù),你不必去買(mǎi)你自己的域名。
繼續(xù)并應(yīng)用以下manifest:
kubectl apply -f https://storage.googleapis.com/knative-nightly/serving/latest/serving-default-domain.yaml如果您打開(kāi) serving-default-domain. yaml,您需要在 spec 中注意到以下內(nèi)容:
# other parts truncatedspec:serviceAccountName: controllercontainers:name: default-domaimage: ko://knative.dev/serving/cmd/default-domainargs: ["-magic-dns=sslip.io"]
這將啟用您將在下一步中需要使用的DNS。
測(cè)試是否一切正常
下載kn二進(jìn)制文件。您可以查閱鏈接:https://knative.dev/development/client/install-kn/。一定要重命名二進(jìn)制文件 kn然后把它放在$PATH的某個(gè)地方。一旦解決了這個(gè)問(wèn)題,就繼續(xù)創(chuàng)建示例Hello World服務(wù)。我已經(jīng)將benjamintanweihao/helloworld python鏡像推送到Docker Hub:
kn service create helloworld-python --image=docker.io/benjamintanweihao/helloworld-python --env TARGET="Python Sample v1"這將產(chǎn)生以下輸出:
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 ready29.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服務(wù):
kn service list -A如果有kubectl,這就變成:
kubectl get ksvc -A要?jiǎng)h除服務(wù),只需執(zhí)行以下操作:
kn service delete helloworld-python 如果您還沒(méi)有這樣做,請(qǐng)確保TODO應(yīng)用程序鏡像已推送到DockerHub。記住用DockerHub ID替換{username}:
docker push {username}/todo-app:latest推送鏡像后,可以使用kn命令創(chuàng)建TODO服務(wù)。記住用DockerHub ID替換{username}:
kn service create todo-app --image=docker.io/{username}/todo-app --env MONGO_HOST="mongodb-release.default.svc.cluster.local" 如果一切運(yùn)行順利,你將看到:
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 ready4.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
現(xiàn)在訪問(wèn)http://todo-app.default.192.168.86.26.sslip.io (或者在上一個(gè)輸出的最后一行的內(nèi)容)您應(yīng)該可以看到應(yīng)用程序!現(xiàn)在我們來(lái)看看Knative為你做了什么。KNative僅需一行命令就可以為您啟動(dòng)一個(gè)服務(wù),并且為您提供了一個(gè)可以從集群訪問(wèn)的URL。
我對(duì)Knative的了解僅僅停留于表面,但我希望這個(gè)教程可以激勵(lì)你更多地了解它!當(dāng)我開(kāi)始看Knative的時(shí)候,我不太明白它做了什么。希望這個(gè)例子能讓我們看到Knative的驚人之處和它的便利性。
結(jié) 論
在本文中,我們簡(jiǎn)要介紹了使用Python構(gòu)建的web應(yīng)用程序并需要MongoDB,并學(xué)習(xí)了如何:
使用Docker容器化TODO應(yīng)用程序
使用Docker減輕依賴項(xiàng)
使用Docker進(jìn)行開(kāi)發(fā)
使用Docker Compose打包多個(gè)容器
安裝K3s
安裝Knative(Serving)和Istio
使用Helm署MongoDB
使用Knative部署TODO應(yīng)用程序
雖然將應(yīng)用程序遷移到 Kubernetes 當(dāng)然不是一項(xiàng)簡(jiǎn)單的任務(wù),但是將應(yīng)用程序容器化通常會(huì)讓你成功一半。當(dāng)然,本文還有很多東西沒(méi)有涉及,比如安全性和可擴(kuò)展性。
K3s 是輕量級(jí)的Kubernetes發(fā)行版,可以極其輕松地使用筆記本/臺(tái)式機(jī)測(cè)試和運(yùn)行 Kubernetes 工作負(fù)載,對(duì)個(gè)人開(kāi)發(fā)者來(lái)說(shuō)十分友好。同時(shí),也十分適合企業(yè)在邊緣端大規(guī)模部署集群。
當(dāng)我開(kāi)始研究 Knative 的時(shí)候,我并不十分理解它的作用。希望這個(gè)例子能夠幫助我們理解 Knative 的魅力及其帶來(lái)的便利。實(shí)際上,Knative 的亮點(diǎn)之一就是“在幾秒鐘之內(nèi)就能啟動(dòng)一個(gè)可擴(kuò)展的、安全的、無(wú)狀態(tài)的服務(wù)”。
我將在以后的文章中更多地介紹 Knative,并更深入地探討其核心特征。我希望你能通過(guò)這篇教程,將它們應(yīng)用到你的應(yīng)用程序中!
推薦閱讀
AutoK3s+k3d,K3s部署體驗(yàn)再升級(jí)!
掃碼添加k3s中文社區(qū)助手
加入官方中文技術(shù)社區(qū)
官網(wǎng):https://k3s.io

