kind（Kubernetes IN Docker）で Feature Gates を設定する方法

# TL;DR

`FeatureGateName=true` は任意の値に置き換えてください。
また、設定が不要なコンポーネントはマニフェストから削除してください。

```bash
$ cat config.yaml
kind: Cluster
apiVersion: kind.sigs.k8s.io/v1alpha3
kubeadmConfigPatches:
- |
  apiVersion: kubeadm.k8s.io/v1beta2
  kind: ClusterConfiguration
  metadata:
    name: config
  apiServer:
    extraArgs:
      "feature-gates": "FeatureGateName=true"
  scheduler:
    extraArgs:
      "feature-gates": "FeatureGateName=true"
  controllerManager:
    extraArgs:
      "feature-gates": "FeatureGateName=true"
- |
  apiVersion: kubeadm.k8s.io/v1beta2
  kind: InitConfiguration
  metadata:
    name: config
  nodeRegistration:
    kubeletExtraArgs:
      "feature-gates": "FeatureGateName=true"

$ kind create cluster --config config.yaml 
```

# はじめに

kind（Kubernetes IN Docker）で Feature Gates を設定する方法を紹介します。
https://github.com/kubernetes-sigs/kind

# 動作確認

kind に渡す設定ファイルを作成します。今回は kube-apiserver と kubelet に `CSIInlineVolume=true` という [Feature Gates](https://kubernetes.io/docs/reference/command-line-tools-reference/feature-gates/) を設定する例となります。

```bash
$ cat config.yaml
kind: Cluster
apiVersion: kind.sigs.k8s.io/v1alpha3
kubeadmConfigPatches:
- |
  apiVersion: kubeadm.k8s.io/v1beta2
  kind: ClusterConfiguration
  metadata:
    name: config
  apiServer:
    extraArgs:
      "feature-gates": "CSIInlineVolume=true"
- |
  apiVersion: kubeadm.k8s.io/v1beta2
  kind: InitConfiguration
  metadata:
    name: config
  nodeRegistration:
    kubeletExtraArgs:
      "feature-gates": "CSIInlineVolume=true"
```

設定ファイルを渡して kind でクラスタを作成します。

```bash
$ kind create cluster --config config.yaml
Creating cluster "kind" ...
 ✓ Ensuring node image (kindest/node:v1.15.0) 🖼
 ✓ Preparing nodes 📦
 ✓ Creating kubeadm config 📜
 ✓ Starting control-plane 🕹️
 ✓ Installing CNI 🔌
 ✓ Installing StorageClass 💾
Cluster creation complete. You can now use the cluster with:

export KUBECONFIG="$(kind get kubeconfig-path --name="kind")"
kubectl cluster-info
```

kind のコンテナ名を確認します。

```bash
$ docker ps
CONTAINER ID        IMAGE                  COMMAND                  CREATED             STATUS              PORTS                                  NAMES
0588eb52ef30        kindest/node:v1.15.0   "/usr/local/bin/entr…"   12 minutes ago      Up 12 minutes       62050/tcp, 127.0.0.1:62050->6443/tcp   kind-control-plane
```

kind コンテナに対してコマンドを実行して kube-apiserver と kubelet に渡されている引数を確認します。

```bash
$ docker exec -it kind-control-plane ps -efww | grep kubelet
root       220     1  3 16:01 ?        00:00:24 /usr/bin/kubelet --bootstrap-kubeconfig=/etc/kubernetes/bootstrap-kubelet.conf --kubeconfig=/etc/kubernetes/kubelet.conf --config=/var/lib/kubelet/config.yaml --container-runtime=remote --container-runtime-endpoint=/run/containerd/containerd.sock --fail-swap-on=false --feature-gates=CSIInlineVolume=true --node-ip=172.17.0.2 --fail-swap-on=false
root       614   597  6 16:02 ?        00:00:43 kube-apiserver --advertise-address=172.17.0.2 --allow-privileged=true --authorization-mode=Node,RBAC --client-ca-file=/etc/kubernetes/pki/ca.crt --enable-admission-plugins=NodeRestriction --enable-bootstrap-token-auth=true --etcd-cafile=/etc/kubernetes/pki/etcd/ca.crt --etcd-certfile=/etc/kubernetes/pki/apiserver-etcd-client.crt --etcd-keyfile=/etc/kubernetes/pki/apiserver-etcd-client.key --etcd-servers=https://127.0.0.1:2379 --feature-gates=CSIInlineVolume=true --insecure-port=0 --kubelet-client-certificate=/etc/kubernetes/pki/apiserver-kubelet-client.crt --kubelet-client-key=/etc/kubernetes/pki/apiserver-kubelet-client.key --kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname --proxy-client-cert-file=/etc/kubernetes/pki/front-proxy-client.crt --proxy-client-key-file=/etc/kubernetes/pki/front-proxy-client.key --requestheader-allowed-names=front-proxy-client --requestheader-client-ca-file=/etc/kubernetes/pki/front-proxy-ca.crt --requestheader-extra-headers-prefix=X-Remote-Extra- --requestheader-group-headers=X-Remote-Group --requestheader-username-headers=X-Remote-User --secure-port=6443 --service-account-key-file=/etc/kubernetes/pki/sa.pub --service-cluster-ip-range=10.96.0.0/12 --tls-cert-file=/etc/kubernetes/pki/apiserver.crt --tls-private-key-file=/etc/kubernetes/pki/apiserver.key
```

Qiita だとスクロール疲れするので雑なシェル芸を噛ますと、想定通り Feature Gates の引数が渡っていることがわかります。

```bash
$ docker exec -it kind-control-plane ps -efww | grep kubelet | tr ' ' '\n'
...

/usr/bin/kubelet
--bootstrap-kubeconfig=/etc/kubernetes/bootstrap-kubelet.conf
--kubeconfig=/etc/kubernetes/kubelet.conf
--config=/var/lib/kubelet/config.yaml
--container-runtime=remote
--container-runtime-endpoint=/run/containerd/containerd.sock
--fail-swap-on=false
--feature-gates=CSIInlineVolume=true # <--- 想定通り Feature Gates の引数が渡っている
--node-ip=172.17.0.2
--fail-swap-on=false

...

kube-apiserver
--advertise-address=172.17.0.2
--allow-privileged=true
--authorization-mode=Node,RBAC
--client-ca-file=/etc/kubernetes/pki/ca.crt
--enable-admission-plugins=NodeRestriction
--enable-bootstrap-token-auth=true
--etcd-cafile=/etc/kubernetes/pki/etcd/ca.crt
--etcd-certfile=/etc/kubernetes/pki/apiserver-etcd-client.crt
--etcd-keyfile=/etc/kubernetes/pki/apiserver-etcd-client.key
--etcd-servers=https://127.0.0.1:2379
--feature-gates=CSIInlineVolume=true # <--- 想定通り Feature Gates の引数が渡っている
--insecure-port=0
--kubelet-client-certificate=/etc/kubernetes/pki/apiserver-kubelet-client.crt
--kubelet-client-key=/etc/kubernetes/pki/apiserver-kubelet-client.key
--kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname
--proxy-client-cert-file=/etc/kubernetes/pki/front-proxy-client.crt
--proxy-client-key-file=/etc/kubernetes/pki/front-proxy-client.key
--requestheader-allowed-names=front-proxy-client
--requestheader-client-ca-file=/etc/kubernetes/pki/front-proxy-ca.crt
--requestheader-extra-headers-prefix=X-Remote-Extra-
--requestheader-group-headers=X-Remote-Group
--requestheader-username-headers=X-Remote-User
--secure-port=6443
--service-account-key-file=/etc/kubernetes/pki/sa.pub
--service-cluster-ip-range=10.96.0.0/12
--tls-cert-file=/etc/kubernetes/pki/apiserver.crt
--tls-private-key-file=/etc/kubernetes/pki/apiserver.key
```

# 備考

今回は kube-apiserver と kubelet に Feature Gates を設定するという流れで進めましたが、TL;DR で挙げている設定ファイルの通りで kube-apiserver, kube-scheduler, kube-controller-manager, kubelet 毎に個別に任意の Feature Gates を設定することが可能です。

また、複数指定したい場合には `"feature-gates": "CSIInlineVolume=true,ServerSideApply=true"` のようにカンマ区切りで指定を行ってください。

# 参考資料

- https://github.com/kubernetes-sigs/kind/issues/563
- https://github.com/kubernetes-sigs/kind/blob/master/site/content/docs/user/quick-start.md#enable-feature-gates-in-your-cluster