在本文中,我們將使用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/install/install-serving-with-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/master/installation/knative/)。但當前不支援Traefik,這就是為什麼我們在安裝K3s時必須禁用它。由於Istio是預設且最受支援的,我們將使用它。
現在等待所有的knative-serving 的pod執行:
kubectl 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,並更深入地探討其核心特徵。我希望你能通過這篇教程,將它們應用到你的應用程式中!