kubectlが使えずetcdctlでリソースの削除をした話

# はじめに
これは完全に失敗談です。

# 前提環境
- kubernetes 1.23 (masterはシングルノード)

# やらかした内容
kube-apiserverと同じIPとPortのサービスをデプロイしてしまいました。
※シングルノード構成で「10.0.0.1」がマスターノード(kubernetes APIサーバ)のIPアドレスです。

```yaml:
apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  ports:
  - protocol: TCP
    port: 6443
    targetPort: 443
  externalIPs:
  - 10.0.0.1
```

# 回復策検討と実施
これでkube-apiserverのPodが再起動のループを始めてしまい、kubectlが使えなくなります。
当初かなり焦りましたが、アプリケーションのPodは継続して稼働できているので、うかつな操作をせずに回復策を検討しました。

再起動のループをしているのでAPIへの疎通ができません。また、crictlでコンテナに入ろうとしてもコンテナIDが変わってしまい、長時間は入れません。しばらく考えてkube-apiserverへのアクセスは諦めました。

kubernetesへの知識がある程度あったので、kube-apiserverのデータは全てetcdに保存される事を知っていました。
なので、今度はetcdに直接接続して問題となったServiceリソースの削除を試みました。

https://kubernetes.io/ja/docs/concepts/overview/components/

etcdへの接続はetcdctlというコマンドを使います。
kubernetesのetcdでは証明書やキーが必要となるので、事前にStatic Podのマニフェストを確認しておきます。

```shell-session:
# grep pki /etc/kubernetes/manifests/etcd.yaml
    - --cert-file=/etc/kubernetes/pki/etcd/server.crt
    - --key-file=/etc/kubernetes/pki/etcd/server.key
    - --peer-cert-file=/etc/kubernetes/pki/etcd/peer.crt
    - --peer-key-file=/etc/kubernetes/pki/etcd/peer.key
    - --peer-trusted-ca-file=/etc/kubernetes/pki/etcd/ca.crt
    - --trusted-ca-file=/etc/kubernetes/pki/etcd/ca.crt
    - mountPath: /etc/kubernetes/pki/etcd
      path: /etc/kubernetes/pki/etcd
```

このうち「--cert-file」「--key-file」「-trusted-ca-file」が必要になるようです。
接続先のポート番号についてもマニフェストから確認できます。

```shell-session:
# grep advertise-client-urls /etc/kubernetes/manifests/etcd.yaml 
    kubeadm.kubernetes.io/etcd.advertise-client-urls: https://10.0.0.1:2379
    - --advertise-client-urls=https://10.0.0.1:2379
```

接続先とポート番号を指定して接続してみます。

```shell-session:
# ETCDCTL_API=3 etcdctl endpoint status \
--endpoints=https://[127.0.0.1]:2379 \
--cacert=/etc/kubernetes/pki/etcd/ca.crt \
--cert=/etc/kubernetes/pki/etcd/server.crt \
--key=/etc/kubernetes/pki/etcd/server.key

https://[127.0.0.1]:2379, 63044e73c12f33cd, 3.5.1, 20 MB, true, 86, 270366839
```

無事に接続できるようです。良かった。
問題となったサービスリソースのキーを検索します。昔は「list」というコマンドがあったようなのですが、今は「get /」とするようです。

```shell-session:
# ETCDCTL_API=3 etcdctl get / --prefix --keys-only \
--endpoints=https://[127.0.0.1]:2379 \
--cacert=/etc/kubernetes/pki/etcd/ca.crt \
--cert=/etc/kubernetes/pki/etcd/server.crt \
--key=/etc/kubernetes/pki/etcd/server.key \
 | grep my-service
```

思い切ってキーを削除します。

```shell-session:
# ETCDCTL_API=3 etcdctl del '/registry/services/specs/default/my-service' \
--endpoints=https://[127.0.0.1]:2379 \
--cacert=/etc/kubernetes/pki/etcd/ca.crt \
--cert=/etc/kubernetes/pki/etcd/server.crt \
--key=/etc/kubernetes/pki/etcd/server.key
```

削除後はmasterノードを再起動します。

```shell-session:
# reboot
```

シングル masterは単純に再起動で良いかと思います。今回はなんとかこれで回復はできました。

# おわりに
見事にServiceリソースだけでkubernetesを操作不能に陥らせる事ができました！！自宅だからと言って、admin権限は怖いですね。
kubectlが使えないという、管理者にはかなりインパクトがあるものなので、kubernetes初学者へのトラブルシュート問題に良い内容かなと思いました。

Amazon EKSだと以下のような設定で制限できるようです。

https://docs.aws.amazon.com/ja_jp/eks/latest/userguide/restrict-service-external-ip.html

OpenShiftは以下の設定です。

https://access.redhat.com/documentation/ja-jp/openshift_container_platform/4.10/html/networking/configuring-externalip#restrictions-on-ip-assignment_configuring-externalip