EKSのノードのKubeletが突然Unauthorizedでクラスタ接続不可となった時の調査メモ


ある日開発環境のEKSのマネージドノードが突然クラスタに参加不可の状態になりました。

単純に自分の理解が足りてなかっただけなのですが、調査方法等のTipsを残しておきたいと思います。

事象

開発環境で開発をしていたら、ノードが全てNot Readyになっていました。

kubelet[14180]: E0106 06:58:01.678158   14180 reflector.go:178] k8s.io/kubernetes/pkg/kubelet/config/apiserver.go:46: Failed to list *v1.Pod: Unauthorized
kubelet[14180]: E0106 06:58:02.269813   14180 controller.go:136] failed to ensure node lease exists, will retry in 7s, error: Unauthorized
kubelet[14180]: E0106 07:00:31.476224   14180 kubelet_node_status.go:92] Unable to register node "ip-xxx-xxx-xxx-xxx.ap-northeast-1.compute.internal" with API server: Unauthorized
kubelet[14180]: E0106 07:11:24.484369   14180 reflector.go:178] k8s.io/client-go/informers/factory.go:135: Failed to list *v1.CSIDriver: Unauthorized

原因

今回の原因は画面にもメッセージに出ている通り、aws-authのConfigMapの不備でした。

以下のURLにもあるような、マネージドノードのロールマッピングが手違いで消えてしまったのが原因でした。

apiVersion: v1
kind: ConfigMap
metadata:
  name: aws-auth
  namespace: kube-system
data:
  mapRoles: |
    - rolearn: <ARN of instance role (not instance profile)>
      username: system:node:{{EC2PrivateDNSName}}
      groups:
        - system:bootstrappers
        - system:nodes

調査メモ

調査や調整に使えるTipsができたので、書いておきます。

Unauthorisedということで権限が問題なのですが、どこで権限を取得しているかをまず確認します。

kubeletは以下の設定ファイルを見ています。

ここを見るとわかるのですがaws-iam-authenticatorを使ってトークンを取得して、そのトークンを clusters.cluster.server で定義されているサーバーに投げて認証しています。

$ cat /var/lib/kubelet/kubeconfig
apiVersion: v1
kind: Config
clusters:
- cluster:
    certificate-authority: /etc/kubernetes/pki/ca.crt
    server: https://xxxxxxxxxxxx.yyy.ap-northeast-1.eks.amazonaws.com
  name: kubernetes
contexts:
- context:
    cluster: kubernetes
    user: kubelet
  name: kubelet
current-context: kubelet
users:
- name: kubelet
  user:
    exec:
      apiVersion: client.authentication.k8s.io/v1alpha1
      command: /usr/bin/aws-iam-authenticator
      args:
        - "token"
        - "-i"
        - "YourClusterName"
        - --region
        - "ap-northeast-1"

この動きをコマンドで追っていきます。これができるとチェックの手間が省けます。

# END_POINT= </var/lib/kubelet/kubeconfigのclusters.cluster.serverを設定>
# 以下で取得も可能
CLUSTER_NAME=YourClusterName
REGION=ap-northeast-1
END_POINT=$(aws eks describe-cluster --region ${REGION} --name ${CLUSTER_NAME} | jq -r '.cluster.endpoint')

# Kubeletが認証に使っているトークンを取得
# aws-iam-authenticatorは引数のクラスタ名やRegionが間違っていてもトークン取得できるので注意してください
USER_TOKEN=$(/usr/bin/aws-iam-authenticator token -i ${CLUSTER_NAME}--region ${REGION} --token-only)


# 認証処理を投げてみる
curl -XPOST --insecure ${END_POINT}/apis/authentication.k8s.io/v1/tokenreviews \
-H "Authorization: Bearer ${USER_TOKEN}" \
-H "Content-Type: application/json" \
-d "{\"apiVersion\": \"authentication.k8s.io/v1\",\"kind\": \"TokenReview\",\"spec\": {\"token\": \"${USER_TOKEN}\"}}"

認証失敗すると以下のようにUnauthorizedのエラーが出ます。

{
  "kind": "Status",
  "apiVersion": "v1",
  "metadata": {

  },
  "status": "Failure",
  "message": "Unauthorized",
  "reason": "Unauthorized",
  "code": 401
}

成功すると以下のようなメッセージが返ります。


{
  "kind": "TokenReview",
  "apiVersion": "authentication.k8s.io/v1",
  "metadata": {
    "creationTimestamp": null,
    "managedFields": [
      {
        "manager": "curl",
        "operation": "Update",
        "apiVersion": "authentication.k8s.io/v1",
        "time": "2021-01-06T07:29:12Z",
        "fieldsType": "FieldsV1",
        "fieldsV1": {"f:spec":{"f:token":{}}}
      }
    ]
  },
  "spec": {
    "token": "k8s-aws-v1.xxxxx"
  },
  "status": {
    "authenticated": true,
    "user": {
      "username": "system:node:ip-xxx-xxx-xxx-xxx.ap-northeast-1.compute.internal",
      "uid": "heptio-authenticator-aws:123456789012:Axxxxxxxxxx",
      "groups": [
        "system:bootstrappers",
        "system:nodes",
        "system:authenticated"
      ],
      "extra": {
        "accessKeyId": ["Axxxxxx"]
      }
    },
    "audiences": [
      "https://kubernetes.default.svc"
    ]
  }
}