2022年3月24日 星期四

使用 Skaffold 來建立 Kubernetes Debugger 環境 (AWS EKS/ECR)

 


Skaffold 用途


Skaffold 是一個可以加以整合 dev 開發環境跟 kubernetes 環境更貼近的一個開發流程工具,你可以透過 skaffold 來協助你對 kubernetes 做開發測試、logging、甚至 debugging。


基本上 skaffold 的文章都是拿地端環境 (minikube) 來做測試,不過我沒有,我的環境直接就上 EKS (AWS) 來做這件事,所以我應該不用考慮太多地端的問題。


這一篇文章記錄的是將現有專案環境套上 skaffold 的工作流,使得線上線下環境整合得宜。


而這一篇文章直得注意的是,我的專案中有 private repo,我們的專案程式語言是 Golang,裡頭需要用到 private go package,而且 skaffold 也拉的是 private docker image。

而專案中也沒有 k8s manifest ,所以也有涵蓋測試以現有的 k8s namespace 上有的 configmap, secret 和 dependencies pod,直接共享設定和環境,甚至也使用 elb + externaldns 簡略達到對外服務。


Skaffold 專案環境設定


Skaffold 安裝後,是一個指令叫做 skaffold,跑起指令後會讀取專案下的 skaffold.yaml 檔案,可以使用 skaffold init 來產生,或是自己寫也可以,以下是自己寫的內容:

skaffold.yaml:

apiVersion: skaffold/v2beta26
kind: Config
build:
  tagPolicy:
    envTemplate: # build 後會把 docker image 推到 ECR 這時候會用這個 policy 上 tag
      template: "my-staging-latest"
  artifacts: 
  - image: 000000000.dkr.ecr.ap-northeast-1.amazonaws.com/xxxx_your_ecr_repo
    docker:
      dockerfile: skaffold/dev.dockerfile # docker file 位置
      buildArgs: # 如果 docker file 有使用到 env 就可以帶入
        AWS_ACCOUNT_ID: xxxxx
deploy:
  kubectl:
    manifests: # 要套用哪一些 k8s manifest 檔案
      - skaffold/k8s-skaffold-dev.yaml


(我有故意開一個資料夾放 skaffold 有關的特製檔案 dockerfile, ...etc,原因是我的專案中沒有 k8s manifest,k8s manifest 是在 infra 的 project 中統一管理的)

skaffold/dev.docekrfile:

# syntax=docker/dockerfile:1

FROM 00000000.dkr.ecr.ap-northeast-1.amazonaws.com/priv-golang-build:1.17 as builder
# first (build) stage WORKDIR /app COPY . /app RUN CGO_ENABLED=0 go build # final (target) stage FROM 00000000.dkr.ecr.ap-northeast-1.amazonaws.com/priv-alpine:3.14
COPY --from=builder /app/my-program / EXPOSE 8080 ENTRYPOINT [ "./my-program" ]


skaffold/k8s-skaffold-dev.yaml:

---
apiVersion: apps/v1
kind: Deployment
metadata:
 name: my-staging-dev
 namespace: my-staging
 labels:
    group: my-staging-dev
spec:
 replicas: 1
 strategy:
  type: Recreate
  rollingUpdate: null
 selector:
  matchLabels:
   app: my-staging-dev
 template:
  metadata:
   name: my-staging-dev
   labels:
    app: my-staging-dev
  spec:
   containers:
   - image: 00000000.dkr.ecr.ap-northeast-1.amazonaws.com/my-program:my-staging-latest
name: my-staging-dev envFrom: - configMapRef: # 這裡的 config 都在線上直接共用 namespace 上的 configmap,就不用自己在做一份 name: my-config - secretRef: name: my-secret ports: - name: http containerPort: 7070 livenessProbe: httpGet: path: "/is_ready" port: 7070 initialDelaySeconds: 50 periodSeconds: 50 failureThreshold: 50 readinessProbe: httpGet: path: "/is_ready" port: 7070 periodSeconds: 50 failureThreshold: 50 --- apiVersion: v1 kind: Service metadata: name: my-staging-dev-service namespace: my-staging labels: app: my-staging-dev annotations: external-dns.alpha.kubernetes.io/hostname: my-dev.xxxxxxx.com spec: type: LoadBalancer selector: app: my-staging-dev ports: - name: http port: 80 protocol: TCP targetPort: 7070


可以看到上述 k8s 檔案定義兩個資源: deployment 跟 service,這兩個的目的就是讓線上環境直接可以多跟 my-dev 的 domain,直接拿去替換來做測試。

這些檔案其實可以共享原有專案的設定 k8s, dockerfile,這裡分出來的原因是因為 skaffold 需要的 docker, k8s manifest 應該不一定是專案上直接使用的那個描述設定檔案,所以獨立寫一份。


Skaffold work with a lot of private repository


上述的資源都頻繁使用到 Private Repository 的東西,假設你的 manifest 還有使用到 helm 的資源,甚至也需要登入 helm。


這裡有兩個狀況需要說明:


1. deploy, push docker image

這兩個狀況有使用到 aws 上的 ecr, eks 服務,你可以透過下面幾個狀況完成登入:


 aws eks --region ap-northeast-1 update-kubeconfig --name [your eks name]


sudo aws ecr get-login-password \

  --region ap-northeast-1 | sudo docker login \

  --username AWS \

  --password-stdin 000000.dkr.ecr.ap-northeast-1.amazonaws.com


這兩個東西能在執行 skaffold 前先設定一次,就不會出狀況。


2. build docker image

build docker image 使用 private repo 的話,應該要在執行 skaffold 之前使用 go mod vendor ,不要在 docker image 裡面做 go mod download 之類的事情。


Skaffold


然後,一個簡單的指令就能開始進行開發整合:

skaffold dev


如果你的服務第一次可能會產生 fail 的狀況,被 k8s 自動重啟才會好的話,需要更改下指令方式,原因是 skaffold dev 結束後,會幫你刪除資源,此時不要讓他刪除就好:

skaffold dev --no-prune=true --cleanup=false

做這件事之後,只要重複兩次應該就可以進入你的 pod。


或是使用另外一個指令也可以 (似乎只要執行一次就好):


skaffold dev --status-check=false --wait-for-connection=true



Skaffold Debugger with DLV


skaffold 很酷的地方就在於可以幫助你做遠端 debug,他使用的方式是用 dlv,自動幫你跑起來,為了做這個設定,我們需要更改設定描述檔案:


skaffold/dev.docekrfile:

# syntax=docker/dockerfile:1

FROM 00000.dkr.ecr.ap-northeast-1.amazonaws.com/priv-golang-build:1.17 as builder

# first (build) stage

# Build Delve <- 安裝 dlv
RUN go install github.com/go-delve/delve/cmd/dlv@latest

WORKDIR /app
COPY . /app

ENV GOTRACEBACK=all

RUN CGO_ENABLED=0 go build -gcflags="all=-N -l" # 設定此行,才不會讓偵錯模式亂掉

# final (target) stage

FROM 0000.dkr.ecr.ap-northeast-1.amazonaws.com/priv-alpine:3.14
COPY --from=builder /app/my-program / EXPOSE 8080 56268 COPY --from=builder /go/bin/dlv / <- 帶入 dlv # sidecar not work ENV GOTRACEBACK=all # <- 一定要告知這行在最後一個 docker image build stage ENTRYPOINT [ "./my-program" ]


此時,你還需要考慮一個狀況,官方表示如果你的 pod 有 sidecar ,這可能會讓斷點偵錯失敗,所以你需要關閉 sidecar (以下例我關閉 istio)


skaffold/k8s-skaffold-dev.yaml:


--- apiVersion: apps/v1 kind: Deployment metadata: name: my-staging-dev namespace: my-staging labels: group: my-staging-dev spec: replicas: 1 strategy: type: Recreate rollingUpdate: null selector: matchLabels: app: my-staging-dev template: metadata: name: my-staging-dev labels: app: my-staging-dev annotations: sidecar.istio.io/inject: "false" # <- 關閉它


接著,就直接下指令:


skaffold debug --status-check=false --wait-for-connection=false --auto-sync=true --port-forward


就等程式自動開啟來,此時 skaffold debug 會替換你的 dockerfile entrypoint 加上 dlv 模式,就可以開始偵錯。


偵錯工具是使用 vscode,需要寫一個 launch.json 來處理這件事:

.vscode/launch.json:

{
    "configurations": [
        {
            "name": "Skaffold Debug Kuberentes",
            "type": "go",
            "request": "attach",
            "mode": "remote",
            "host": "localhost",
            "port": 56268,
        }
    ]
}


如此用 vscode 打開後,就可以斷點偵錯。


使用 Skaffold 要考量的事情


  • 如果要做 debug ,那麼 pod 需要單一化, replica 可能只有 1。
  • 官方有說要關掉 sidecar,像是我使用的是 istio,而 sidecar 如果有一些設定跟你的軟體有關,有可能會被忽略
  • 可能無法拿來除雲端平台或雲端相關的錯誤


References:

https://skaffold.dev/

沒有留言:

張貼留言

© Mac Taylor, 歡迎自由轉貼。
Background Email Pattern by Toby Elliott
Since 2014