雲原生時代,作為技術人員,如果不瞭解 Docker 和 Kubernetes(K8s),那絕對是技術棧上的一個短板。那麼,什麼是 Docker?什麼又是 Kubernetes?它們之間存在怎樣的關係? 這篇文章,我將透過理論加程式碼實戰的方式,詳細地剖析他們。
1. 原理部分
1.1 Docker
Docker是一個容器化平臺,它允許開發者將應用及其依賴項打包到一個稱為容器的標準化單元中,以便可以在不同環境中快速、可靠地執行這些應用。它的本質是利用作業系統的Cgroups
和Namespace
機制建立出來的隔離環境。
Docker核心概念包括以下幾個:
1.1.1 Docker核心概念
1. 映象:Docker(Image)映象是一個輕量級、獨立、可執行的軟體包,其中包含執行某個應用程式所需的所有內容,包括程式碼、執行時、庫、環境變數和配置檔案。映象是不可變的,基於映象可以建立容器。
2. 容器:容器(Container)是映象的執行例項,它是一個輕量級、獨立的環境,可以在任何支援 Docker的機器上執行。容器與主機系統共享作業系統核心,但在程序空間上是隔離的。
3. Dockerfile:Dockerfile是一個文字檔案,包含構建 Docker映象的所有命令,可以透過 Dockerfile來自動化映象的建立過程。
4. Docker Hub:Docker Hub是一個雲端的 Docker映象儲存庫,使用者可以在上面釋出和獲取映象,它提供了一個平臺來共享和管理映象。
5. 容器編排:Docker Compose是一種定義和執行多容器 Docker應用程式的工具,透過一個docker-compose.yml
檔案,使用者可以定義一個應用包含的所有服務,並使用簡單的命令啟動或停止它們。
Docker容器可以描述成以下全景圖(圖片來自極客張磊):
1.1.2 Docker的優點
Docker的優點可以用一句話總結:一次打包,到處部署
。這個和 JVM一次編譯,到處執行
的設計有著異曲同工之妙,所以說,計算機領域很多思維是相通的!
下圖對比了使用與不使用 Docker的場景:
不使用 Docker:每次將應用部署新主機時,都需要重新安裝一遍應用需要的環境,因為作業系統多,運營商可能也不同,安裝環境是一件痛苦的事情。
使用 Docker:製作 Docker映象時,已經包含應用及其所需的環境,因此可以到任何運營商提供的作業系統上執行。
1.2 Kubernetes
Kubernetes(K8s) 一詞源自希臘,意為“舵手”或“掌舵者”,用於指導和管理船隻的航行。它是一個容器編排平臺,負責自動化容器的部署、擴充套件(水平擴充套件)、負載均衡、以及故障恢復等工作。
Kubernetes的核心概念包括以下幾個:
1.2.1 Kubernetes核心概念
1. 節點:節點(Node)是 Kubernetes叢集中的一臺物理機或虛擬機器,負責執行應用程式的工作負載。每個節點執行一個Kubelet(用於管理節點上的容器)和一個容器執行時(例如Docker)。
2. Pod:Pod是 Kubernetes中最小的可部署單元,通常包含一個或多個容器。Pod中的容器共享網路和儲存,可以協同工作。
3. 控制器:控制器(Controller)負責管理和維護叢集中的Pod和相關的服務。常見的控制器有 ReplicaSet(確保指定數量的Pod副本在執行)、Deployment(管理無狀態應用)和 StatefulSet(管理有狀態應用)。
4. 服務:服務(Service)是一個抽象的方式,用於定義一組Pod的邏輯集合以及訪問這些 Pod的策略。Kubernetes中的服務提供了負載均衡和服務發現功能。
5. 名稱空間:名稱空間(Namespace)用於在同一個 Kubernetes叢集中將資源進行邏輯上的隔離。它允許多個團隊或專案共享一個叢集而不會相互影響。
6. 配置管理:配置管理包含 ConfigMap 和 Secret。ConfigMap 用於儲存非機密的資料,類似配置檔案。Secret用於儲存機密資料,如密碼、OAuth令牌和SSH金鑰。
7. Ingress:Ingress是一個 API物件,管理外部訪問到叢集中服務的 HTTP和 HTTPS路由。它提供負載均衡、SSL終止和基於名稱的虛擬託管等功能。
8. Deployment:Deployment是一個用於管理無狀態應用程式的核心元件。它提供了一種宣告式的方法來管理 Pod和 ReplicaSet,從而實現應用程式的部署、升級和擴縮。
Kubernetes可以描述成以下全景圖(圖片來自極客張磊):
1.2.2 Kubernetes 的工作流程
Kubernetes 的整體工作流程可以總結成下面 6個步驟:
使用者定義和提交檔案:使用者透過 kubectl提交描述檔案,例如 Deployment 和 Service 定義,這些檔案描述了應用副本數量、負責選擇負載均衡的標籤、資源需求等資訊。
API Server 接受請求:API Server處理使用者的物件建立請求,這些請求會被記錄在 etcd 中。
排程器決定 Pod 的位置:Scheduler根據叢集的整體資源使用情況、節點健康狀況、Pod 的資源要求等排程策略,將 Pod安排到叢集中的某個 Node上。
Kubelet 處理並執行Pod:在選定的 Node上,Kubelet呼叫 Container Runtime(例如 Docker 或 Container)啟動容器,並根據請求的規格拉取映象、分配資源、啟動應用。
Kube Proxy 實現服務訪問:叢集中的 Kube Proxy負責為每個 Pod生成虛擬 IP,透過這些 IP,叢集內外部的訪問可以透過 Kubernetes Services 來訪問這些 Pod,進行負載均衡。
狀態保持和自動修復:Kubernetes的控制器會持續監控 Pod和節點的狀態,並在檢測到某些 Pod不可用或節點失效時,自動重啟或排程新的 Pod到健康的節點上。
整個流程如下圖:
1.3 兩者關係
Docker提供容器化機制,用於封裝和打包應用,並利用 CI/CD管道將容器映象推送至中央容器註冊中心(如 Docker Hub 或 Harbor),而 Kubernetes是一個強大的容器編排平臺,負責排程、擴充套件和管理 Docker容器化應用,保證系統高可用、故障自動恢復。
從宏觀上看,兩者關係可以透過下圖來形象的表達:
好了,理論部分講解完後,我們正式進入程式碼實戰部分,俗話說:說起來容易做起來難,對於 Kubernetes叢集還真有點難度,來,上乾貨!!!
2. 程式碼實戰
2.1 目的
開發一個簡單的 Java Web應用,並將其 Docker容器化部署到 Kubernetes叢集中,然後在瀏覽器訪問,展示:Hello world, This is my docker running in Kubernetes!
。
實戰部分基於: Apple M1 Pro電腦
2.2 步驟
整體流程核心步驟有下面 4個:
安裝環境
建立 Web應用
應用 Docker 容器化
部署到 Kubernetes 叢集
瀏覽器驗證
2.2.1 安裝環境
首先,需要確保我們安裝了以下環境:JDK(Java Development Kit), Gradle(Maven),Docker, Kubernetes,下面是我安裝的版本:
JDK 版本:
Gradle版本:
Docker 版本: 直接使用 Mac brew安裝:
brew install kubectl
Kubernetes 版本:
直接使用 Mac brew安裝:
brew install kubectl
注意:Gradle 和 Maven 是專案管理工具,只要安裝一個就OK,自己熟悉哪個就安裝哪個。
2.2.2 建立Java Web應用
1. 建立Gradle(Maven)專案
建立一個 Springboot 專案,整個目錄結構如下:
在src/main/cloud/com/yuanjava/docker
目錄下建立一個新的 Java類,如HelloController.java
:
package com.yuanjava.docker; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; /** * @author 猿java */ @RestController public class HelloController { @GetMapping("/test") public String test() { return "Hello world, This is my docker running in Kubernetes!"; } }
2. Gradle(Maven)打包專案:
Gradle打包指令如下:
gradle build
這裏在 build/libs 目錄下就就會看到打包後的 jar包:
2.2.3 容器化 Java Web應用
1. 編寫Dockerfile:
在專案根目錄下建立一個Dockerfile
:
FROM openjdk:17-jdk-slim COPY build/libs/yuanjava-1.0.jar app.jar ENTRYPOINT ["java","-jar","/app.jar"]
2. 構建Docker映象:
在當前目錄,執行以下命令就可以構建 Docker映象,-t
的作用是給這個映象加一個Tag,也就是起一個好聽的名字。
docker build -t yuanjava:1.0 .
執行完上面的指令之後,就可以在 Docker映象庫看到剛纔打的映象了,可以使用docker images
指令檢視映象:
docker images
也可以透過 Docker的視覺化工具檢視映象:
3. 執行Docker容器
我們可以透過下面的命令來啟動 Docker容器:
docker run -p 9999:9999 yuanjava:1.0
然後,在瀏覽器中訪問http://localhost:9999/test
來測試 Docker是否啟動完成。訪問結果如下圖:
4. 推送 Docker映象到遠端倉庫
這一步是爲了下面將 Docker映象部署到 Kubernetes叢集做準備,推送命令如下:
docker push yuanjava:1.0
推送完之後,遠端倉庫就多了一個映象,如下圖所示:
注意:如果沒有 Docker賬號,需要先建立賬號。
2.2.4 部署到Kubernetes叢集
1. 啟動 Kubernetes叢集
通常來說,我們會使用minikube
命令來啟動和管理 Kubernetes 叢集,安裝和啟動命令如下:
# 安裝 minikube curl -Lo minikube https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64 chmod +x minikube sudo mv minikube /usr/local/bin/ # 啟動 Kubernetes叢集 minikube start
2. 編寫Kubernetes Deployment配置
建立一個deployment.yaml
檔案,內容如下:
apiVersion: apps/v1 # 定義了 Kubernetes API 的版本 kind: Deployment # 宣告這個 Kubernetes 資源的型別 metadata: name: yuanjava # 包含資源的後設資料,Kubernetes 透過 metadata 欄位中的各類資訊來跟蹤和管理資源 spec: replicas: 2 # Pod的副本數量,即 Deployment將執行 2個相同的 Pod selector: matchLabels: app: yuanjava # 告訴 Deployment 如何找到它管理的 Pod template: metadata: labels: app: yuanjava # 定義 Pod 的模板 spec: containers: - name: yuanjava image: yuanjava/yuanjava:1.0 #定義容器使用的映象,Kubernetes 會從容器映象倉庫中拉取它 ports: - containerPort: 9999 # 指定容器內部監聽的應用埠
3. 編寫Kubernetes Service配置
建立一個service.yaml
檔案,內容如下:
apiVersion: v1 kind: Service # 用於描述和管理向外部或者內部暴露應用的網路訪問方式 metadata: name: yuanjava # 包含資源的後設資料資訊 spec: type: LoadBalancer # 定義了服務的型別,LoadBalancer 型別會將服務暴露給外部網路 ports: - port: 80 # 這是對外暴露的埠,表示外部客戶端訪問服務時使用的埠號 targetPort: 9999 # 這是叢集內部目標 Pod 上執行的容器所監聽的埠 selector: app: yuanjava
4. 部署到Kubernetes
我們可以使用kubectl
命令應用配置,命令說明如下:
kubectl apply -f deployment.yaml
:根據 deployment.yaml 檔案中的定義,建立或更新 Kubernetes中相應的 Deployment。 kubectl apply -f service.yaml
:根據 service.yaml 檔案中的內容,Kubernetes會建立一個 Service,並將其與執行 Deployment 中的 Pod關聯起來。
kubectl apply -f deployment.yaml kubectl apply -f service.yaml
5. 驗證部署
使用以下命令檢查 Pods和 Service狀態:
kubectl get pods kubectl get services
如下圖,2個 Pod都處於 Running 狀態:
2.2.5 瀏覽器驗證
最後,我們可以在瀏覽器中訪問,驗證應用是否成功部署到 Kubernetes 叢集,訪問地址是:http://EXTERNAL-IP/hello
,如下圖:
因為,我是本地部署,所以EXTERNAL-IP
地址為<pending>
,即未分配,但是可以透過minikube service yuanjava
檢視 IP地址,如下圖所示:
最後,開啟地址http://192.168.49.2:63213/test
, 見證奇蹟的時刻到了:
因為是本地環境,所以連結最終會跳轉到http://127.0.0.1:63213/test
。
到此,我們就成功地手動將 Docker映象部署到 Kubernetes叢集,並且透過 URL能夠訪問,過程很艱辛,結果還是比較美好。
但是,如果要部署大量的機器,這樣手動操作肯定是不行,因此,我們需要結組一些 CI/CD工具(如 GitHub Actions、GitLab CI/CD 或 Jenkins)實現自動化 Docker映象構建和 Kubernetes 部署。
這裏以 GitHub Actions為例,建立 .github/workflows/auto-deploy.yaml:
name: Auto Deploy Application to Kubernetes on: push: branches: - main jobs: build-and-deploy: runs-on: ubuntu-latest steps: # Step 1: 檢出程式碼 - name: Checkout code uses: actions/checkout@v3 # Step 2: 登入 Docker Registry - name: Log in to Docker Hub run: echo "${{ secrets.DOCKER_PASSWORD }}" | docker login -u "${{ secrets.DOCKER_USERNAME }}" --password-stdin # Step 3: 構建並推送 Docker 映象 - name: Build and push Docker image run: | docker build -t ${{ secrets.DOCKER_USERNAME }}/yuanjava:latest . docker push ${{ secrets.DOCKER_USERNAME }}/yuanjava:latest # Step 4: 設定 kubectl - name: Set up kubectl uses: azure/setup-kubectl@v3 with: version: 'latest' # Step 5: 配置 kubeconfig 檔案 - name: Configure kubeconfig run: | echo "${{ secrets.KUBECONFIG }}" > kubeconfig export KUBECONFIG=kubeconfig # Step 6: 部署到 Kubernetes - name: Apply Kubernetes manifests run: | kubectl apply -f deployment.yaml kubectl apply -f service.yaml
關鍵說明:
配置裡面的 DOCKER_USERNAME, DOCKER_PASSWORD, KUBECONFIG,需在專案的 Settings > Secrets 中配置。
在觸發階段(push 到 main 分支)時,上述流水線將自動完成 Docker 構建、推送,並將應用部署到 Kubernetes。
3. 總結
本文,我們先從理論上分析了 Docker 和 Kubernetes的核心概念以及它們之間的關係。
接著,我們從程式碼實戰的角度,帶大家一步一步實操瞭如何編寫一個簡單的 Java Web應用,並將其 Docker容器化,最後部署到 Kubernetes叢集中,然後在瀏覽器訪問。在實操的期間,因為所有的環境都是本地環境臨時搭建,所以遇到了很多的問題,但最終還是成功了。
透過這次實操,再次說明了做技術不能只停留在理論,實操很重要,實操期間我們可能遇到很多問題,但是,當我們透過各種方式去解決問題的時候,這個過程其實就是對技術更深入的學習和掌握。
個人建議:
Docker是雲原生很重要相對簡單的一個技術基礎,強烈建議掌握並且一定要去實戰。Kubernetes的難度系統會比 Docker大,它為大量容器提供排程、資源管理、彈性伸縮等功能,如果使用的機器數量比較少,Kubernetes其實很難用上,但是,如果可以,還是建議我們創造條件在實際生產環境中去使用和學習 Kubernetes。相信我,這種有壓力的學習效果賊棒!
4. 參考資料
作者:猿java
連結:https://juejin.cn/post/7439656697075875867