Day 20 - 常見問題與建議 (1)

本日共賞

希望你知道

沒有人希望有錯誤發生!尤其是新手上路,更容易在錯誤發生後,因為無法修復而導致一連串挫折就放棄。而在 k8s 這樣龐大的系統下,新手發生錯誤的機會又特別高。因此,在接下來的幾天時間,我將列出 k8s 常見的錯誤與建議修正方法。

錯誤的發生往往是忽略一些小細節,通常 k8s 都會提示你錯誤是什麼


錯誤的容器名稱或權限不足

第一種常見的問題是當 k8s 嘗試建立 Pod 的時候找不到指定的映像檔 (image) 或者指到一個僅供私人存取的映像檔 (private image),毫無疑問,肯定是無法順利建立 Pod。

極有可能是打錯字…

舉個例子,透過 run 指令建立一個 Pod 並指定一個不存在的映像檔 james/idontexist:v1.0

$ kubectl run doesnotexist --image=james/idontexist:v1.0

然後查看 Pod 狀態

$ kubectl get pods
NAME                            READY     STATUS             RESTARTS   AGE
doesnotexist-688bcb554f-gtj4z   0/1       ImagePullBackOff   0          8s

你可能會看到 ErrImagePull 與 ImagePullBackOff 兩種狀態,皆表示無法取得映像檔

這時候你會發現 Pod 的狀態不是 Running 表示 Pod 並沒有正常運作,接著查看詳細資料內 Event 的內容

$ kubectl describe pods doesnotexist-688bcb554f-gtj4z   <=== 名字要記得換掉喔
Events:
  Type     Reason                 Age                From               Message
  ----     ------                 ----               ----               -------
  Normal   Scheduled              40s                default-scheduler  Successfully assigned doesnotexist-688bcb554f-gtj4z to minikube
  Normal   SuccessfulMountVolume  40s                kubelet, minikube  MountVolume.SetUp succeeded for volume "default-token-ld9jt"
  Normal   Pulling                23s (x2 over 39s)  kubelet, minikube  pulling image "james/idontexist:v1.0"
  Warning  Failed                 18s (x2 over 35s)  kubelet, minikube  Failed to pull image "james/idontexist:v1.0": rpc error: code = Unknown desc = Error response from daemon: repository james/idontexist not found: does not exist or no pull access
  Warning  FailedSync             6s (x4 over 35s)   kubelet, minikube  Error syncing pod
  Normal   BackOff                6s (x2 over 34s)   kubelet, minikube  Back-off pulling image "james/idontexist:v1.0"

k8s 會提示你 Failed to pull image "james/idontexist:v1.0",當你看到這個錯誤訊息就是 k8s 在告訴你它沒辦法取得 james/idontexist:v1.0 這個映像檔。

那麼問題來了,到底是什麼原因造成 k8s 無法取得映像檔?有以下幾種可能

  1. 映像檔不存在
  2. k8s 沒有權限取得映像檔 (pull image)
  3. 映像檔存在,但是 tag 不正確

映像檔不存在有可能是打錯字造成的,如果想測試映像檔是否存在,可以用 docker 來測試,例如

$ docker pull james/idontexist:v1.0

如何安裝 docker 請參考 docker 官網

另一種情況是映像檔存在但是指定的 tag 不存在,這時候還可以把 tag 移除試試看。

$ docker pull james/idontexist

如果使用 docker pull 也無法抓取則很有可能映像檔真的不存在。但如果使用上述指令可以抓取但是 k8s 卻無法正確取得就有可能是權限不足的問題。

針對權限不足的狀況,可以利用 Day 17 - Secret 提到的方法新增 Secret。

還有一種可能性,就是映像檔可能不是放在 Dockerhub

由於 k8s 會將 Dockerhub 當作預設的映像檔儲存區 (repository registry),因此,當你指定 james/idontexist 時,k8s 會認定你是使用 Dockerhub。所以,如果你是使用像 AWS GCRGoogle Container Registry 這些非 Dockerhub 的儲存區時,請務必將完整的 url 填入。例如 gcr.io/james/idontexist

k8s 對於無權限存取或是映像檔不存在皆以 ErrImagePull 表示


驗證錯誤

還記得 Day 9 - yaml 討論過的 yaml 格式嗎? 由於部署 k8s 物件需要撰寫 yaml 描述物件,常常一不小心就可能會打錯字或格式錯誤。另外一種常見的情況是 yaml 格式沒錯誤,但是指定的版本 (apiVersion) 並不支援某個設定或是放錯位置。撇除 yaml 格式錯誤的問題,我們來看一下底下的 error-obj.yaml

# error-obj.yaml

---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
    name: err-obj-nginx
spec:
    replicas: 3
    template:
      metadata:
        labels: 
          app: nginx
      spec:
        containers:
        - name: nginx
          image: nginx  <=== ports 應該與這裡對齊
        ports:          <=== 放錯位置了
        - containerPort: 80

以 yaml 格式來說,上面的例子並沒有任何錯誤。但當我們部署到 k8s 中

$ kubectl apply -f error-obj.yaml
error: error validating "error-obj.yaml": error validating data: ValidationError(Deployment.spec.template.spec): unknown field "ports" in io.k8s.api.core.v1.PodSpec; if you choose to ignore these errors, turn validation off with --validate=false

unknown field "ports" 這邊會發現 v1.PodSpec 並不認得 ports 這個設定,其實不是沒有而是位置放錯了,正確寫法為

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
    name: err-obj-nginx
spec:
    replicas: 3
    template:
      metadata:
        labels: 
          app: nginx
      spec:
        containers:
        - name: nginx
          image: nginx
          ports:    <=== 修正這裡
          - containerPort: 80

還有一種錯誤原因為指定的版本 (apiVersion) 並不支援某個設定,例如底下的 error-unknown.yaml

# error-unknown.yaml

---
apiVersion: v1
kind: Deployment   <=== v1 並無支援 Deployment 物件
metadata:
    name: err-unknown-nginx
spec:
    replicas: 3
    template:
      metadata:
        labels: 
          app: nginx
      spec:
        containers:
        - name: nginx
          image: nginx
          ports:
          - containerPort: 80

就 yaml 格式上來看,error-unknown.yaml 沒有任何錯誤,部署到 k8s 後會看到以下錯誤

$ kubectl apply -f error-unknown.yaml
error: error validating "error-unknown.yaml": error validating data: unknown object type schema.GroupVersionKind{Group:"", Version:"v1", Kind:"Deployment"}; if you choose to ignore these errors, turn validation off with --validate=false

unknown object type schema 這邊 v1 並沒有 Deployment 這個物件,因此部署就會發生錯誤。

版本支援的物件請參考 官方網站 的說明

建議方案

為避免上述錯誤,除了多加練習撰寫 yaml 之外,也可多多參考官方文件說明。另外 kubectl 有提供 --dry-run 供測試使用。如果要測試修正過後的 error-obj.yaml

$ kubectl create -f error.obj.yaml --dry-run
deployment "err-obj-nginx" created (dry run)

如果 yaml 存在錯誤就可以從這裡知道,如果沒有錯誤也不會真的部屬到 k8s 中。

kubectl --dry-run 只有在能連到 k8s 下才能使用

本文與部署檔案同步發表於 https://jlptf.github.io/ironman2018-day20/