📝

ICTSC2024 二次予選 Writeup

2024/12/17に公開

こちらは、JSNOGアドベントカレンダー15日目の記事です。
前回(1週間以上前)の記事は、@tuna2134さんの「BIRD2でBGPを張る話」です。
私もHomeNOCと接続するためにBIRD2を使ったのですが、BIRDの情報が少なくて苦しいのとても共感できました。

はじめに

少し遅くなってしまいましたが、こちらはICTSC2024二次予選のWriteupです。
ICTSCとは、学生向けのインフラのトラブルシューティングコンテストで、ネットワークやサーバーなど運営が事前に用意したトラブルを解決する能力を競うコンテストです。

ICTSCへの参加は3回目で、分野としては主にKubernetesなどを触ってます。最近はKubernetesの問題が多くなってきており嬉しいです。

つい先日発表があり、無事にICTSC2024二次予選を通過できました🎉 チームメンバーに感謝!

今回の予選の中ではいくつかの問題に手をつけたのですが、完全に解けた問題は1つだけだったので精進が必要ですね。ネットワーク周りの知識をもっとつけたい...

ICTSC2024二次予選の他の問題は以下で公開されてます。
https://blog.icttoracon.net/2024/12/14/ictsc2024pr/

[ISI] Welcome to Kubernetes

KubesprayでKubernetesを構築する問題です。ちょうどKubesprayを触ろうとしてたので、競技で学べてよかったです。

この問題は以下の3点が主な原因でした。

  • Kubernetes内部ネットワークで利用しているサブネットと、Docker registoryがあるサブネットが被っていた
  • KubernetesからDNSサーバーへの名前解決の設定がされていなかった
  • Containerdでプライベートレジストリの設定を追加していなかった

解決できた!と思ってたら他のトラブルが発生!の繰り返しで結構時間を取られてしまいました。
あと、提供されているVMのスペックのせいなのかansibleの流し込みに時間がかなり時間がかかってしまい、設定を変更してからデプロイするまでに数分かかってしまうので苦しかったです...

サブネットが被ってる!?

とりあえず、NginxのPodは起動するということでPod内に入って状況を確認してみました。
競技中に初めて知ったのですが、kubectl debugというのを使うと起動中のPodをデバッグができるので便利でした。

kubectl debug nginx-.... -it --image=nicolaka/netshoot

netshootはネットワーク系のデバッグをするためのイメージで、digとかmtrとかネットワークのデバッグに便利なツールが一通り入ってるようです。
これを使って、10.233.90.120のDocker Registryにpingを飛ばしてみたのですがそもそも疎通しません...

ホストからは疎通できるのでおかしいなと思い、k8sのネットワークを疑い設定を見て見ました。
問題環境の設定ファイルとは違いますが、以下が該当の部分です。
https://github.com/kubernetes-sigs/kubespray/blob/master/inventory/sample/group_vars/k8s_cluster/k8s-cluster.yml#L76

実際のファイルの設定は以下のようになっていました。

 # Kubernetes internal network for services, unused block of space.
 kube_service_addresses: 10.233.0.0/18
 # internal network. When used, it will assign IP
 # addresses from this range to individual pods.
 # This network must be unused in your network infrastructure!
 kube_pods_subnet: 10.233.64.0/18

あっ!Docker Registry(10.233.90.120)とサブネットがかぶってますね。
どおりで、10.233.90.120にpingを飛ばそうとしてもk8sの内部ネットワークに吸われて通信が外まで届いてないわけですね。

解決方法

  1. クラスターをリセットする
    Kubernetes内部ネットワークのサブネットが変更するため、まずは既存のクラスターを一度リセットします。
cd /home/user/kubespray && \
python3 -m venv venv && \
source venv/bin/activate
ansible-playbook -i inventory/mycluster/hosts.yaml --become --become-user=root --ask-pass --ask-become-pass reset.yml
  1. k8s-cluster.ymlを編集する
    /home/user/kubespray/inventory/mycluster/group_vars/k8s_cluster/k8s-cluster.ymlを以下のように変更します。

まず、IPサブネットを10.233.0.0/18から10.230.0.0/18に変更します。

# Kubernetes internal network for services, unused block of space.
< kube_service_addresses: 10.233.0.0/18
> kube_service_addresses: 10.230.0.0/18
# internal network. When used, it will assign IP
# addresses from this range to individual pods.
# This network must be unused in your network infrastructure!
< kube_pods_subnet: 10.233.64.0/18
> kube_pods_subnet: 10.230.64.0/18

この状態でデプロイしたところ、今度は別の問題が発生してしまいました...

ContainerdでImageをPullできない!?

目標のPodを起動して見たのですが、今度は以下のような状況でした。

 Events:
   Type     Reason     Age                From               Message
   ----     ------     ----               ----               -------
   Normal   Scheduled  17s                default-scheduler  Successfully assigned default/someapp-cm44g to node1
   Normal   BackOff    15s (x2 over 16s)  kubelet            Back-off pulling image "docker-registry.hideichi.as.a.service.com/someapp:latest"
   Warning  Failed     15s (x2 over 16s)  kubelet            Error: ImagePullBackOff
   Normal   Pulling    2s (x2 over 17s)   kubelet            Pulling image "docker-registry.hideichi.as.a.service.com/someapp:latest"
   Warning  Failed     2s (x2 over 17s)   kubelet            Failed to pull image "docker-registry.hideichi.as.a.service.com/someapp:latest": failed to pull and unpack image "docker-registry.hideichi.as.a.service.com/someapp:latest": failed to resolve reference "docker-registry.hideichi.as.a.service.com/someapp:latest": failed to do request: Head "https://docker-registry.hideichi.as.a.service.com/v2/someapp/manifests/latest": dial tcp 10.233.90.120:443: connect: connection refused
   Warning  Failed     2s (x2 over 17s)   kubelet            Error: ErrImagePull

ErrImagePullと出ていて、どうやらhttps://docker-registry.hideichi.as.a.service.com/v2/someapp/manifests/latestに接続できないようです。
今回のプライベートレジストリはそもそもhttpにしか対応していないので、httpsではなくhttpで接続してほしい...

調べて見たところ、今回のkubesprayで使ってるk8sのコンテナランタイムあるcontainerdの問題のようです。
Containerdはデフォルトで、httpsで接続しようとするのでmirrorに登録してあげると良いらしいです。
https://fingerease.work/archives/k8s_trouble_pulling_images/

該当のファイルはkubespray上で以下のように編集できます。
registriesにdocker-registry.hideichi.as.a.service.comを追加してあげます。

> containerd_registries_mirrors:
>   - prefix: docker-registry.hideichi.as.a.service.com
>     mirrors:
>       - host: http://docker-registry.hideichi.as.a.service.com
>         capabilities: ["pull", "resolve"]
>         skip_verify: true

今度こそ動くかな...と思ったらまた別の問題が発生しました

PodからDNSサーバーへの名前解決ができない

無事にImageのPullはできるようになったのですが、今度はPodが起動してもエラーになって正常終了しなくなってしまいました。
検証用イメージではおそらく

curl -X GET http://docker-registry.hideichi.as.a.service.com/v2/_catalog

のような感じでチェックしてるんだと思いますが、これが通らないとエラーになるようです。

再びkubectl debugで調べてみたところ、

dig docker-registry.hideichi.as.a.service.com

でも名前解決ができないことがある...?ようでした。
以下のようにDNSの設定を更新してあげると良さそうです。

/home/user/kubespray/inventory/mycluster/group_vars/k8s_cluster/k8s-cluster.ymlに以下のDNS設定を追加しました。

> nodelocaldns_external_zones:
> - zones:
>   - coredns.hideichi.as.a.service.com
>   - docker-registry.hideichi.as.a.service.com
>   nameservers:
>   - 10.244.10.130

動作チェック

3度目の正直で、最後にクラスターを再構築します。

cd /home/user/kubespray && \
python3 -m venv venv && \
source venv/bin/activate
ansible-playbook -i inventory/mycluster/hosts.yaml --become --become-user=root --ask-pass --ask-become-pass cluster.yml

以下のように当該Podをデプロイして、status_code=0で正常にJobが終了することが確認できました。
めでたしめでたし

cd /home/user/kubespray/inventory/mycluster/artifacts/
./kubectl.sh create job someapp --image=docker-registry.hideichi.as.a.service.com/someapp:latest
job.batch/someapp created
./kubectl.sh get jobs
NAME      STATUS     COMPLETIONS   DURATION   AGE
someapp   Complete   1/1           8s         24s
./kubectl.sh get pods
NAME                     READY   STATUS      RESTARTS   AGE
nginx-7584b6f84c-pzvr6   1/1     Running     0          74m
someapp-r9pnw            0/1     Completed   0          58s
sudo apt install yq
./kubectl.sh get pods someapp-r9pnw -oyaml | yq ".status.containerStatuses[].state.terminated.exitCode"
0

Discussion