在實作練習 Kubernetes 時,在實現那些進階的操作像是負載均衡、滾動更新、安全與監控… 等概念,我們都會不斷不斷的圍繞在 Kubernetes 三兄弟 - Pod, Service, Deployment 身邊不斷的為其設定,來實現以上操作,所以接下來我們將會對這三兄弟 (一鄉情願的自稱?) 來點初步的了解。
首先要來介紹的是 Pod ,在我們前幾天的 Kubernetes 組件 介紹文章中也有提到,Pod 是 Node(節點) 中最小的單位,等於我們所使用的容器們都會被放置 Pod 裡管理。通常一個 Pod 裡只會有一個容器( 也可以有一個以上的容器),接下來我們將會實作一個簡單的容器來練習。
基本上只要是 Container-based 的服務,都可以拿來讓 Kubernetes Pod 佈署,而不僅限於 Docker Container,只是因為現代容器趨勢依舊是被 Docker 掌握著加上其方便性以及穩定性,所以我們將會使用 Docker 處理各種容器建構。
來寫一個簡單的 Golang API Server(如果沒有 Golang 的同學可以直接往下到建立 Pod 環節) :
package main
import (
	"github.com/gin-gonic/gin"
	"net/http"
)
func main() {
	router := gin.Default()
	router.GET("/", func(c *gin.Context) {
		c.JSON(http.StatusOK, gin.H{"data": "Hello foo"})
	})
	router.Run()
}
輸入 go run main.go 運行起這個服務後,為一個點開 localhost:8080 即可出現 Hello foo 字樣的簡易 API Server。
FROM golang:1.18-rc-alpine as builder
WORKDIR /
COPY . .
RUN go mod tidy
RUN go build -o main
// 多階段建構
FROM alpine:3.15.0-rc.4
WORKDIR /
COPY --from=builder /main .
EXPOSE 8080
ENTRYPOINT ["./main"]
以上就是我們簡單產生出一個 docker image 的設定檔,接下來我們就可以用他來建構一個 Container image。
docker build -t foo . 
來 run 一下看看
docker run --rm -p 8080:8080 -it -d foo

成功出現 Hello foo ~
當我們把 Image 推送到遠端的 Image 存取服務的倉庫時,我們就可以使用 Image 與 tag 拉下我們想使用的指定 Image,這也是容器化的其中一個便利性,當然 Kubernetes 也是採用一樣的方式來取得要運作的容器服務。
docker build -t mikehsu0618/foo . 
docker push mikehsu0618/foo
這裡簡單的將 docker image 推到我個人的 docker hub 庫中,docker 相關操作就不稍贅述,如需練習可以直接拉取這裡已經建理好的 Image 即可。
接下來我們將要試著把這個服務用 Kubernetes 部署起來!
首先我們需要本地建立設定檔,讓我們接下來的指令去運行到指定檔案。
// pod.yaml
apiVersion: v1
kind: Pod
metadata:
 name: foo
 labels:
   app: foo
spec:
 containers:
   - name: foo
     image: mikehsu0618/foo
     ports:
       - containerPort: 8080
apiVersion : 代表目前該元件在 Kubernetes 中的版本號。metadata.name : 表示這個 Pod 的名稱metadata.labels : Lables 標籤 是附加到 Kubernetes 對象 ( 例如 Pods, Service, Deployment) 上的 Key-Value 組合。旨在用於把對用戶有意義且相關的對象分組,但不直接對核心系統有直接關聯,如此一來,我們即可使用 選擇器(Selector) 選取指定 Lables 運作。spec : 在 spec 我們可以看到在 Docker 很熟悉的 Image 設定配置,此處可以用來設定一個或多個 Container。containers.name : 設定 Container 名稱。containers.image : Image 的路徑,此處為 Docker Hub 的拉取路徑。containers.ports : containerPort 代表的是這個 Container 只能開放哪些 port 來允許外部資源的存取。所以這邊根據我們的應用程式以及 dockerfile 設定的 8080 port 來指定 8080 端口。通常在 Kubernetes 基本上所有指令的建立都可以使用兩種指令:
// 使用 -f 參數去指定設定檔路徑並且建立 pod
kubectl apply -f pod.yaml
// or
kubectl create -f pod.yaml
以上兩個指令都可以用來建立資源,差別在於 create 只能用來創建還未存在的資源,而 apply 可以資源在已經存在的情況下,查看設定是否有異動並且更新。
接著查看 Pods 列表:
kubectl get pods

並且可以查看 Pod 更詳細的資訊:
kubectl describe pod foo
=========================
foo:
    Container ID:   docker://fc02b6801a4b6c62cb0aed77f4480bb41f297476c034a0d75fa079ea7e883cb4
    Image:          mikehsu0618/foo
    Image ID:       docker-pullable://mikehsu0618/foo@sha256:41cd860bd4c9ce86271bb5c864599bb2ca32b3c3a377afb12d2142fa12a87b1d
    Port:           8080/TCP
    Host Port:      0/TCP
    State:          Running
      Started:      Tue, 21 Jun 2022 17:13:30 +0800
    Ready:          True
    Restart Count:  0
    Environment:    <none>
    Mounts:
      /var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-v84zl (ro)
Conditions:
  Type              Status
  Initialized       True 
  Ready             True 
  ContainersReady   True 
  PodScheduled      True 
Volumes:
  kube-api-access-v84zl:
    Type:                    Projected (a volume that contains injected data from multiple sources)
    TokenExpirationSeconds:  3607
    ConfigMapName:           kube-root-ca.crt
    ConfigMapOptional:       <nil>
    DownwardAPI:             true
QoS Class:                   BestEffort
Node-Selectors:              <none>
Tolerations:                 node.kubernetes.io/not-ready:NoExecute op=Exists for 300s
                             node.kubernetes.io/unreachable:NoExecute op=Exists for 300s
Events:
  Type    Reason     Age    From               Message
  ----    ------     ----   ----               -------
  Normal  Scheduled  2m37s  default-scheduler  Successfully assigned default/foo to docker-desktop
  Normal  Pulling    2m37s  kubelet            Pulling image "mikehsu0618/foo"
  Normal  Pulled     2m33s  kubelet            Successfully pulled image "mikehsu0618/foo" in 3.777043918s
  Normal  Created    2m33s  kubelet            Created container foo
  Normal  Started    2m32s  kubelet            Started container foo
來看一下 kubectl port-forward 指令:
kubectl port-forward TYPE/NAME [options] [LOCAL_PORT:]REMOTE_PORT
port-forward 可以使我們本地的端口轉發到指定的 Kubernetes 集群端口中,這麼一來我們就可以用本地的 localhost 去映射到我們建立起來的 Kubernetes 服務中了。
於是我們讓啟動的 Pod 的 8080端口 轉發到本地的 8080端口:
kubectl port-forward pod/foo 8080:8080
// 執行後
=======================================
Forwarding from 127.0.0.1:8080 -> 808080
Forwarding from [::1]:8080 -> 8080
沒意外的話我們使用 curl 去存取 localhost:8080 可以得到一樣的回覆

如此一來我們已經使用了 Kubernetes 運行了第一個容器了囉,到後面隨著使用情境的複雜化,要處理的問題也會越來越難,但所有的操作不外乎完全圍繞在 Pods 身上,可以說是非常核心的觀念,在初期也是會不斷的與之接觸。不難發現,我們用 Kubernetes 模擬了日常 Docker 流的一個 Container 使用方式,相信平常就有在用 Docker 的人會感到非常親切,可以說 Docker 是容器化技術的入門磚也不過分。
千呼萬喚始出來!鐵人賽系列「從異世界歸來發現只剩自己不會 Kubernetes」同名改編作品出版了!
感謝所有交流指教的各路英雄,也感謝願意點閱文章的各位,如果能幫助到任何人都將會是我的榮幸。
本書內容改編自第 14 屆 iThome 鐵人賽 DevOps 組的優選系列文章《從異世界歸來發現只剩自己不會 Kubernetes》。此書是一本綜合性的指南,針對想要探索認識 Kubernetes 的技術人員而生。無論是初涉此領域的新手,還是已有深厚經驗的資深工程師,本書都能提供你所需的知識和技能。
「這本書不僅提供了豐富的範例程式碼和操作指南,讓身為工程師的我們能實際操作來加深認知;更重要的是,它教會我如何從後端工程師的角度去思考和應用 Kubernetes。從容器的生命週期、資源管理到部署管理,每一章都與我們的日常開發工作息息相關。」
──── 雷N │ 後端工程師 / iThome 鐵人賽戰友
天瓏連結: 從異世界歸來發現只剩自己不會 Kubernetes:初心者進入雲端世界的實戰攻略!
相關文章:
相關程式碼同時收錄在:
https://github.com/MikeHsu0618/2022-ithelp/tree/master/Day6
在練習期間有遇到下載 gin 遇到連不到 host 的問題
後來透過這個方式處理
https://github.com/gin-gonic/gin/issues/2421#issuecomment-1005742334
感謝提供資訊~
感謝版主提供海量知識才是 ?
版主您好,
我把
RUN go mod tidy
改成
RUN go mod init main
RUN go mod tidy -e
不然會出錯。
不好意思~ 這邊因為 golang 不是重點部分
省略了一些細節導致不夠明確
通常 go mod init 都是在專案產生時就輸入了
所以在 dockerfile 不用特地執行
感謝您的求知精神
請問metadata.lables.app 是不是給pod 一個隨便的名字? 可以和metadata.name 不相同?
沒錯 label 可以自由命名或是重複,可以讓我們很容易的對資源分群