本文主要是講解在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 吧!
本文中覆蓋的內容:
歡迎關注公衆號:非著名開發者,獲取更多精彩內容。