ICTSC2024 二次予選 Writeup
こちらは、JSNOGアドベントカレンダー15日目の記事です。
前回(1週間以上前)の記事は、@tuna2134さんの「BIRD2でBGPを張る話」です。
私もHomeNOCと接続するためにBIRD2を使ったのですが、BIRDの情報が少なくて苦しいのとても共感できました。
はじめに
少し遅くなってしまいましたが、こちらはICTSC2024二次予選のWriteupです。
ICTSCとは、学生向けのインフラのトラブルシューティングコンテストで、ネットワークやサーバーなど運営が事前に用意したトラブルを解決する能力を競うコンテストです。
ICTSCへの参加は3回目で、分野としては主にKubernetesなどを触ってます。最近はKubernetesの問題が多くなってきており嬉しいです。
つい先日発表があり、無事にICTSC2024二次予選を通過できました🎉 チームメンバーに感謝!
今回の予選の中ではいくつかの問題に手をつけたのですが、完全に解けた問題は1つだけだったので精進が必要ですね。ネットワーク周りの知識をもっとつけたい...
ICTSC2024二次予選の他の問題は以下で公開されてます。
[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のネットワークを疑い設定を見て見ました。
問題環境の設定ファイルとは違いますが、以下が該当の部分です。
実際のファイルの設定は以下のようになっていました。
# 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の内部ネットワークに吸われて通信が外まで届いてないわけですね。
解決方法
- クラスターをリセットする
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
- 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に登録してあげると良いらしいです。
該当のファイルは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