AWS EKSの運用は実家の猫に任せてお前は先に行け

どうも、僕です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242714/a4972451-89ba-0d7f-b01e-30cb68fd4fed.png)

# CatOps
DevOps が Developers を運用に引きずり込む為のマインドセットだとすれば CapOps は猫を運用に引きずり込む為のマタタビセットと言えるでしょう。今回は実家でおやつを食べてばかりのあいつにも手伝ってもらいます。彼のおもちゃの半分ぐらいは私がAWSをこねくり回して得た給与によって供給されているのでこれは当然の義務とも言えます。

# Amazon Web Services Cat-Like Interface (AWS CLI)
作業用のIAMロールを作ります。仕事じゃないのでアクセス許可は雑です。

- AmazonEC2ContainerRegistryFullAccess
- AmazonEC2FullAccess
- AWSCloudFormationFullAccess
- [EksAllAccess](https://eksctl.io/usage/minimum-iam-policies/)
- [IamLimitedAccess](https://eksctl.io/usage/minimum-iam-policies/)

Amazon Linux 2 の EC2 インスタンスを作ってIAMロールを付けたら必要そうなものをインストールしておきます。

```bash
sudo yum update -y
sudo yum install jq

### Python
PYVER="python3.8"
sudo yum remove -y python3
sudo amazon-linux-extras enable ${PYVER}
sudo yum install -y ${PYVER}
PY_VENV_PATH="${HOME}/.${PYVER}-venv"
/usr/bin/${PYVER} -m venv ${PY_VENV_PATH}
echo "source ${PY_VENV_PATH}/bin/activate" >> ${HOME}/.bashrc
echo 'export PS1="[\u \W]\\$ "' >> ${HOME}/.bashrc
. ~/.bashrc

### AWS CLI
pip install --upgrade pip
pip install --upgrade awscli
aws --version

### Docker
sudo yum install -y docker
sudo systemctl enable docker
sudo systemctl start docker
sudo usermod -aG docker $(whoami)

### kubectl + aws-iam-authenticator
curl -sLo kubectl https://s3.us-west-2.amazonaws.com/amazon-eks/1.22.6/2022-03-09/bin/linux/amd64/kubectl
curl -sLo aws-iam-authenticator https://s3.us-west-2.amazonaws.com/amazon-eks/1.21.2/2021-07-05/bin/linux/amd64/aws-iam-authenticator
mkdir -p $HOME/bin
export PATH=$PATH:$HOME/bin
echo 'export PATH=$PATH:$HOME/bin' >> ~/.bashrc
chmod +x ./kubectl
chmod +x ./aws-iam-authenticator
mv ./kubectl $HOME/bin/kubectl
mv ./aws-iam-authenticator $HOME/bin/aws-iam-authenticator

### eksctl
curl -sL "https://github.com/weaveworks/eksctl/releases/latest/download/eksctl_$(uname -s)_amd64.tar.gz" | tar xz -C /tmp
sudo mv /tmp/eksctl /usr/local/bin

sudo reboot
```

EKSクラスターを作ります。簡単ですね。簡単ということにしておきましょう。

```bash
mkdir -p ~/meow && cd $_
cat <<EOF > cluster.yaml
apiVersion: eksctl.io/v1alpha5
kind: ClusterConfig
metadata:
  name: cluster-1
  region: ap-northeast-1
nodeGroups:
  - name: ng-1
    instanceType: t3.medium
    desiredCapacity: 1
    volumeSize: 20
EOF
eksctl create cluster -f cluster.yaml
```

# ネコと和解せよ

意外とご存じない方も多いのですが、猫は kubectl コマンドを入力するのが得意ではありません。その原因としては、一般に猫は人間のように個々の指を独立させて異なるキーを押すことが得意でないためとされています。しかし、これは正しくありません。実際の理由は、市販されているキーボードのキーの配置間隔が多くのイエネコの肉球の面積に比べて非常に狭い為です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242714/258b9b45-05fe-0fb2-6dee-53b72cf08f58.png)


そこで今回は [Cat User Interface (CUI)](https://store.line.me/stickershop/product/1015712/en) を実装することで kubectl コマンドを使わないEKSクラスターの制御を実現します。残念ながら私には CUI を新規に開発する技術も根性もありません。そこで GitHub で公開されている [kubecraftadmin](https://github.com/erjadi/kubecraftadmin) という素晴らしいソフトウェアの力を借ります。

これによりEKSクラスターの制御がスティーブ (またはアレックス) の制御に変換されるため、入力デバイスは市販のキーボードに限定されなくなります。ポッドは豚に、レプリカセットは牛に、サービスは鶏に、デプロイメントは馬として表示されます。鶏も含め、いずれも猫の獲物としては若干大きい動物ですが、そこは我慢して受け入れます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242714/8b484399-f05c-9001-27ff-d85bee12c8a7.png)

大まかな仕組みとしては Windows 10上で動作する Bedrock Edition のクライアントから /wsserver コマンドを介してEKSクラスター上のポッドに接続し、このポッドを介してコントロールプレーンのAPIを叩きます。

ここで困った点として /wsserver での接続時に認証がありません。さすがにこれを受け入れるセキュリティポリシーはオスの三毛猫よりも稀な存在です。そのためポッドにはサービスはアタッチできません。さらに前述した通り猫は kubectl コマンドが得意ではありません。つまり kubectl port-foward を介して /wsserver を実行するのは良い解決策とは言えません。面倒ですが、仕方ないので今回はポッドに2個目のコンテナを追加して猫だけが通れるバックドア、つまり Cat flap を作ります。

# 猫に小判を与える

ネットワーク構成は以下のようになります。まだまだ経験の浅い若者 (たしか3~4歳) の技量不足を補いつつ成長を促す為にはこの程度の小細工は避けられないでしょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242714/d5bab59e-c243-8a36-852b-128c13e3654a.png)

Cat flap用のコンテナをビルドし、EKSクラスターでプルできるようにECRにプッシュしておきます。
```bash
cd ~/meow
NAME=catflap
REGION=ap-northeast-1

cat <<EOF > Dockerfile.${NAME}
FROM amazonlinux
RUN yum -y install sudo iproute jq
RUN yum -y install https://downloads.remote.it/remoteit/v4.14.1/remoteit-4.14.1-1.x86_64.rpm
RUN yum clean all; rm /etc/remoteit/config.json
STOPSIGNAL SIGRTMIN+3
CMD ["/usr/sbin/init"]
EOF

docker build --rm --no-cache -t ${NAME} -f Dockerfile.${NAME} .
aws --region ${REGION} ecr create-repository --repository-name ${NAME}
REPO="$(aws sts get-caller-identity | jq -r '.Account').dkr.ecr.${REGION}.amazonaws.com"
docker tag ${NAME}:latest ${REPO}/${NAME}:latest
aws --region ${REGION} ecr get-login-password | docker login --username AWS --password-stdin ${REPO}
docker push ${REPO}/${NAME}:latest
```

そして kubecraftadmin と Cat flap を組み合わせた猫用のポッドをEKSにデプロイします。kubecraftadminのイメージは Docker Hub に転がっているので、そこから直接持ってきます。消費されるリソースは猫の額ほどしかありませんので requests や limits を気にする必要はないでしょう。
```bash
cd ~/meow
AWS_ID=123456789000
REGION=ap-northeast-1
CLUSTER=cluster-1
NAME=kubectl4cat

cat <<EOF > ${NAME}.yml
apiVersion: v1
kind: Pod
metadata:
  name: ${NAME}
spec:
  containers:
  - name: kubecraftadmin
    image: erjadi/kubecraftadmin
    imagePullPolicy: Always
    volumeMounts:
    - mountPath: /.kube/config
      subPath: config
      name: kubeconfig
  - name: catflap
    image: ${AWS_ID}.dkr.ecr.${REGION}.amazonaws.com/catflap
    imagePullPolicy: Always
    volumeMounts:
    - mountPath: /tmp
      name: tmp
    - mountPath: /run
      name: run
    - mountPath: /sys/fs/cgroup
      name: cgroup
      readOnly: true
  volumes:
  - name: kubeconfig
    secret:
      secretName: kubeconfig
  - name: tmp
    emptyDir: {}
  - name: run
    emptyDir: {}
  - name: cgroup
    hostPath:
      path: /sys/fs/cgroup
      type: Directory
EOF

aws eks update-kubeconfig --region ${REGION} --name ${CLUSTER}
kubectl create secret generic kubeconfig --from-file=${HOME}/.kube/config -o yaml --dry-run=client | kubectl apply -f -
kubectl apply -f ${NAME}.yml
kubectl get pod
```

無事に猫用ポッドがデプロイできたことを確認したら、次に猫用ポッドに接続します。Cat flap コンテナから登録用コードを取り出します。

```bash
kubectl exec -it ${NAME} -c catflap -- cat /etc/remoteit/config.json | jq -r ".device.claim"
```

remote.it の [Windows用のデスクトップアプリ](https://www.remote.it/download?platform-list=windows&products=desktop) をインストールしたら、コードを入力し Cat flap をデバイスとして登録します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242714/28f6ed32-6e95-aa55-fafe-28c50596d82c.png)

kubecraftadmin はTCP 8000ポートをリッスンしています。そこで、登録した remote.it デバイスにサービスを追加し、Cap flapコンテナ内の TCP 8000 ポートに接続できるようにします。ご存じの通り同一ポッド内のコンテナは localhost としてアクセス可能です。したがって、これで kubecraftadmin へのアクセスが可能になります。なお、ここでいうサービスはK8sのサービスとは全く別物です。猫も杓子もサービスという単語を使うのはややこしいですね。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242714/6a187f8d-afd6-e718-4dbc-d9c569dbbde7.png)

サービスを追加したら Setup の項目からルーティングを no default に設定します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242714/a712b9ad-fc30-37cf-88b3-bd83693feddd.png)

そのあと Connect の項目からルーティングを Proxy only に変更し ADD TO NETWORK します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242714/0fe996fa-345a-70b4-64fb-6a0befedc2ea.png)

最後に表示されるポート番号だけが重要なのでメモしておきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242714/25ff8012-5681-5652-e2c3-818b829005d1.png)

一応、接続確認だけしておきましょう。適当にブラウザか何かで以下のURLにアクセスします。****** の部分にはポート番号を入力します。上記の例だと 33023 ですね。

```
http://127.0.0.1:******/ws
```

ブラウザ側に [HTTP 400 - Bad Request](https://http.cat/400) が返れば成功です。コンテナ側のログ見ると以下のようにWSでおｋ的な感じのエラーが残っているはずです。

```bash
kubectl logs -f kubectl4cat kubecraftadmin

Listening on port 8000
2022/07/10 04:06:47 error upgrading request: websocket: the client is not using the websocket protocol: 'upgrade' token not found in 'Connection' header
```

猫の手を借りてみる

今回初めて知ったのですが、Mincraft Bedrock Edition クライアントのような UWP アプリはデフォルトでは localhost へのアクセスが許可されません。

https://bugs.mojang.com/browse/MCPE-123297

ネットワーク構成の図でも示したように Bedrock Edition クライアントは localhost 上の remote.it クライアントを経由します。以下のコマンドを実行し Bedrock Edition クライアントが localhost にアクセスできるように設定します。

```powershell
PS> Get-AppxPackage | Select-String "minecraft"
Microsoft.MinecraftUWP_1.19.202.0_x64__8wekyb3d8bbwe

PS> CheckNetIsolation LoopbackExempt -a -n="Microsoft.MinecraftUWP_1.19.202.0_x64__8wekyb3d8bbwe"
OK.
```

Bedrock Edition のクライアントを起動し、チート機能をオンにしたワールドを新規作成します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242714/02296357-48b1-aafd-75ed-23eca041b8bb.png)

そして /wsserver コマンドを実行します。***** の部分は先ほどと同様にメモしたポート番号を入力します。ちなみに localhost を指定すると何故か失敗する。

```
/wsserver 127.0.0.1:*****/ws
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242714/4e3ed4d9-003c-9bd1-bcc3-4d1058c8230b.png)

接続に成功したのでご褒美としてダイヤの剣と気持ちばかりのTNTを貰いました。次に k8s リソースを司る家畜を召喚するために init コマンドを実行します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242714/98ef0b5e-9e31-82b8-6c91-258e318df67b.png)

接続切れちゃったぞ。ガッデム乳山なんでやねん。

```bash
kubectl logs -f kubectl4cat kubecraftadmin

Listening on port 8000
2022/07/10 04:06:47 error upgrading request: websocket: the client is not using the websocket protocol: 'upgrade' token not found in 'Connection' header
Player has entered!
Player has disconnected
2022/07/10 04:20:05 event response: malformed properties JSON: unexpected end of JSON input (payload: {"body":{"message":"init","receiver":"","sender":"ryaguchi","type":"chat"},"header":{"eventName":"PlayerMessage","messagePurpose":"event","version":16842752}}
)
Player has entered!
2022/07/10 04:20:10 event response: malformed properties JSON: unexpected end of JSON input (payload: {"body":{"message":"KubeCraftAdmin","receiver":"ryaguchi","sender":"External","type":"title"},"header":{"eventName":"PlayerMessage","messagePurpose":"event","version":16842752}}
)
Player has disconnected
```

あー、はいはい。なるほどね。わからん。


https://github.com/Sandertv/mcwss/blob/v1.2.0/player.go#L516

MCWSSがJSONを構造体に放り込もうとしてコケたようにも見えなくもない訳でもない。バージョンによって Bedrock Edition クライアントが吐き出す JSON が多少変わってるのかも知れないけど、たぶん探し方が悪いのか公式っぽい公開情報が見つけられません。

# そもそも吾輩は猫である
そろそろ晩御飯の準備をする時間なので今回は諦めることにしました。だって今夜は焼き肉。冷静に考えたら常時MTTBでラリってる奴にEKSクラスター預けるのも中々にリスキーなので失敗して良かったのかも知れません。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242714/c6fb63e6-9813-6860-8596-2a90d9a1789f.png)

kubecraftadmin の類似ソフトウェアとしては  [kubedoom](https://github.com/storax/kubedoom) があります。こっちも少し試したのですが、受け取った .kube/config を使って kubectl を叩くようなので、そのまま実行すると aws eks get-token が実行できないと怒ります。環境変数でIAMユーザのキーを渡すなり、EKSワーカーノードにIAMロールを割り当てるなり、とりあえず認証できるように細工が必要です。

まぁ、最大の問題は DOOM は難しいのでチートしてもすぐ倒されてしまうことです。

# 掃除は大切

```bash
eksctl delete cluster -f cluster.yaml
```