sakura-cloud-controller-managerのデプロイ&トラブルシューティング

さくらのクラウドKubernetesを利用する場合、マネージドなLBなどを扱うためのcloud-controller-manager実装として sakura-cloud-controller-managerというものがあります。

febc-yamamoto.hatenablog.jp

こちらのデプロイでよく引っかかる点やトラブルシューティング方法についてメモを発掘したので整理がてら残しておきます。

さくらのクラウド上のリソースの構成

Kubernetesクラスタで利用するさくらのクラウド上のリソースの構成は以下の点に注意が必要です。

  • ネットワーク構成
  • スイッチ(ルータ)への@k8sタグ設定

ネットワーク構成

sakura-cloud-controller-managerはサーバをスイッチ+ルータ or スイッチに接続する必要があります。
共有セグメント(共有回線)はサポートしていませんので注意してください。

スイッチ(ルータ)への@k8sタグ設定

sakura-cloud-controller-managerはさくらのクラウドAPIで取得したスイッチ(ルータ)の情報を元にどのスイッチを利用するかを決定します。
その際に@k8sタグの付けられたリソースかで判定を行なっています。

タグの付け忘れにご注意ください。

クラスタの構成

次にKubernetesクラスタの構成についてですが、以下2点に注意する必要があります。

順番にみていきます。

--cloud-providerオプションを適切に指定すること

Kubernetesでexternalなcloud-controller-managerを起動するには--cloud-providerオプションを適切に指定する必要があります。

参考: Kubernetes Cloud Controller Manager - Kubernetes

具体的には以下2点です。

  • kubeletのパラメータとして--cloud-provider=externalを指定しておくこと
  • kube-apiserverとkube-controller-managerには--cloud-providerを指定しないこと

kubeadmを利用してクラスタのデプロイを行う場合は/var/lib/kubelet/kubeadm-flags.envファイルや/etc/default/kubeletファイルなどで適切に指定しましょう。

参考: Installing kubeadm - Kubernetes

Nodeに付与されるtaintsへの対応

先ほどの参考ドキュメントに書いてありますが、kubeletに--cloud-provider=externalを指定するとノードに対し以下のようなtaintsが設定されます。

- apiVersion: v1
  kind: Node
  spec:
    taints:
    - effect: NoSchedule
      key: node.cloudprovider.kubernetes.io/uninitialized
      value: "true"

sakura-cloud-controller-managerをデプロイする際はこのtaintsに対するtolerationsが適切に設定されている必要があります。

# tolerationsの設定例
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: sakura-cloud-controller-manager
  namespace: kube-system
spec:
  replicas: 1
  template:
    spec:
      containers:
      - name: sakura-cloud-controller-manager
        image: "sacloud/sakura-cloud-controller-manager:0.3.0"
      tolerations:
        # tolerationsを設定しておく
        - key: node.cloudprovider.kubernetes.io/uninitialized
          value: "true"
          effect: NoSchedule

もしkubeletに--cloud-providerを指定しなかったらどうなるの?

この場合でもsakura-cloud-controller-managerのデプロイ自体はうまくいくように見えます。
が、ノードのExternalIPが適切に設定されないため、type: LoadBalancerなサービスを作ってもロードバランサが作成されるものの実サーバが登録されないという状態になります。

serviceは作成されるけど、、、

f:id:febc_yamamoto:20190603165301p:plain

実サーバは登録されていない

f:id:febc_yamamoto:20190603165314p:plain

クラスタ内のノード名とさくらのクラウド上でのサーバ名が一致していること

これはsakura-cloud-controller-manager側の制約です。
sakura-cloud-controller-managerではノード名を条件にさくらのクラウドAPIで対象サーバの情報を取得しています。
このため、ノード名とサーバ名が異なるとうまくいきません。

kubeletの--hostname-overrideオプションなどで適切に設定しましょう。 (kubeadmの場合は--node-nameフラグが利用できます。 参考: kubeadm init - Kubernetes)

もしノード名とサーバ名が違うとどうなるの?

sakura-cloud-controller-managerでのノード情報の取得が行えず、ノードのtaintsが残り続けます。
このためPodを起動しようとしてもPendingのままとなります。
(まずkube-dnsなどの主要コンポーネントについてもPendingのままとなっているはずです。)

# Podを起動しようとしてもPendingのまま
$ kubectl get pod
NAME                     READY   STATUS    RESTARTS   AGE
nginx-7cdbd8cdc9-8g8mw   0/1     Pending   0          3m1s

# 主要コンポーネントもPendingのまま
$ kubectl get pod -n kube-system --selector k8s-app=kube-dns
NAME                        READY   STATUS    RESTARTS   AGE
kube-dns-58bd5b8dd7-swrlg   0/3     Pending   0          7m39s

さらにこの状態だとノードのInternalIP/ExternalIPが設定されず、kubectl logsを実行するとError from server: no preferred addresses found; known addresses: []などというエラーになります。

sakura-cloud-controller-managerのデプロイ

DeploymentまたはDaemonSetとしてデプロイしてください。
(現在Helmでデプロイする場合はDaemonSetのみサポートしています)

Deploymentとする場合のマニフェスト例は以下の通りです。 (この例はRKEを使ってデプロイしたKubernetesクラスタを利用しています。tolerationsやnodeSelectorsは環境に応じて適切に指定してください)

apiVersion: v1
kind: Secret
metadata:
  name: sakuracloud-api-keys
  namespace: kube-system
type: Opaque
data:
  access-token: "<APIアクセストークン>"
  access-token-secret: "<APIアクセスシークレット>"
---
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: sakura-cloud-controller-manager
  namespace: kube-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  annotations:
    rbac.authorization.kubernetes.io/autoupdate: "true"
  name: system:sakura-cloud-controller-manager
rules:
  - apiGroups:
      - ""
    resources:
      - events
    verbs:
      - create
      - patch
      - update
  - apiGroups:
      - ""
    resources:
      - nodes
    verbs:
      - '*'
  - apiGroups:
      - ""
    resources:
      - nodes/status
    verbs:
      - patch
  - apiGroups:
      - ""
    resources:
      - services
    verbs:
      - list
      - patch
      - update
      - watch
  - apiGroups:
      - ""
    resources:
      - services/status
    verbs:
      - list
      - patch
      - update
      - watch
  - apiGroups:
      - ""
    resources:
      - serviceaccounts
    verbs:
      - create
  - apiGroups:
      - ""
    resources:
      - persistentvolumes
    verbs:
      - get
      - list
      - update
      - watch
  - apiGroups:
      - ""
    resources:
      - configmaps
    verbs:
      - get
      - list
      - watch
  - apiGroups:
      - ""
    resources:
      - endpoints
    verbs:
      - create
      - get
      - list
      - watch
      - update
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
  name: system:sakura-cloud-controller-manager
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: system:sakura-cloud-controller-manager
subjects:
  - kind: ServiceAccount
    name: sakura-cloud-controller-manager
    namespace: kube-system
---

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  labels:
    k8s-app: cloud-controller-manager
  name: sakura-cloud-controller-manager
  namespace: kube-system
spec:
  replicas: 1
  selector:
    matchLabels:
      k8s-app: cloud-controller-manager
  template:
    metadata:
      labels:
        k8s-app: cloud-controller-manager
    spec:
      dnsPolicy: Default
      hostNetwork: true
      serviceAccountName: sakura-cloud-controller-manager
      containers:
      - name: sakura-cloud-controller-manager
        image: "sacloud/sakura-cloud-controller-manager:0.3.0"
        resources:
          requests:
            cpu: 100m
            memory: 128Mi
          limits:
            cpu: 256m
            memory: 256Mi
        command:
          - /usr/local/bin/sakura-cloud-controller-manager
          - --cloud-provider=sakuracloud
          - --allocate-node-cidrs=false
          - --configure-cloud-routes=false
        env:
          - name: SAKURACLOUD_ACCESS_TOKEN
            valueFrom:
              secretKeyRef:
                name: sakuracloud-api-keys
                key: access-token
          - name: SAKURACLOUD_ACCESS_TOKEN_SECRET
            valueFrom:
              secretKeyRef:
                name: sakuracloud-api-keys
                key: access-token-secret
          - name: SAKURACLOUD_ZONE
            value: "<対象ゾーン(is1a or is1b or tk1a)>"
          - name: SAKURACLOUD_CLUSTER_ID
            value: "default"
      tolerations:
        - key: node.cloudprovider.kubernetes.io/uninitialized
          value: "true"
          effect: NoSchedule

        - key: "CriticalAddonsOnly"
          operator: "Exists"
      nodeSelector:
        node-role.kubernetes.io/controlplane: "true"

実行時によくあるトラブル

よくあるトラブルとして、Serviceを作成してもいつまでもPendingのままというものがあります。

この場合、sakura-cloud-controller-managerのログを参照すると何かヒントが得られることがあります。

# ログの参照例
$ kubectl logs -f -n kube-system sakura-cloud-controller-managerのpod名

私も引っかかったことがあるのは次のようなエラーメッセージが出るケースです。

Improper request. The parameters of the specified error or input rule violation. Please check your entries.\n実サーバのIPアドレス(xxx.xxx.xxx.xxx)は同一ネットワークである必要があります

このケースは@k8sタグのついたスイッチ+ルータが複数存在している場合に発生します。
複数のスイッチ+ルータを使い分けたい場合はそれぞれのスイッチに対し識別用のタグを付与した上でk8s.usacloud.jp/router-selectorアノテーションをServiceに対して指定するようにすればOKです。

おまけ: RKEでデプロイするTerraformモジュール

こちらにTerraformのさくらのクラウドプロバイダー+RKEプロバイダーでクラスタ構築やsakura-cloud-controller-managerのデプロイまで行うモジュールを置いておきます。

github.com

以上です。