Tsuzu's Notes

主にIT系備忘録

ICTトラブルシューティングコンテスト2021 夏の陣で優勝しました

500年ぶりにブログを書いている。

ICTトラブルシューティングコンテスト2021 夏の陣(ICTSC2021 Summer)というコンテストに2年前から一緒に出ているメンバーで参加して優勝しました。 優勝チームで誰もエントリを書かないのは流石に申し訳ないので500年ぶりにブログエントリを更新しています。 f:id:tsuyochi23182:20210830001106p:plain

謝辞

チーム名のmis1yakudoは元々traPという東工大のサークルに存在する #traP1yakudo から影響を受けて作成された、我々が以前所属していたサークル MIS.W に存在する文化です。 #mis1yakudo (メンバーは元々MIS.Wに所属していましたがすでに引退した老人から構成されています。)

この文化を作成された方にこの場を借りてお礼を申し上げます。

作戦など

特に作戦とかはなくて各自が解きたいものを好き勝手に解いていく感じでした。

私は主にDocker/Kubernetes担当で、前回の本線まではKubernetes問題は全て解けていたのに前回Cephに敗北したので一敗です。

1日目

とりあえずコンテスト開始と同時に冒頭にKubernetes問題(クラウドネイティブは難しい…)を見つける。ぱっと見400点問題で重そうで、もう一問Kubernetes問題を見つける。確か「Webサイトにアクセスできない」?

Webサイトにアクセスできない

Ingress500系エラーが出ている時はとりあえずIngressコントローラのログを見ろと古事記にも書いてあるので見ると以下のようなエラーが出ている。エラー文をGoogle先生に貼っつけるとproxy_buffer_sizeが足りないみたいな話が出てくる。NGINX Ingress Controllerのannotations一覧 からそれっぽいやつ (nginx.ingress.kubernetes.io/proxy-buffer-size)を見つけて適用する。

2021/08/28 01:05:48 [error] 34#34: *1715942 upstream sent too big header while reading response header from upstream, client: 10.244.0.0, server: _, request: "GET / HTTP/2.0", upstream: "http://10.244.1.9:443/", host: "192.168.13.101"

こんな感じ

    nginx.ingress.kubernetes.io/proxy-buffer-size: "8"
+    nginx.ingress.kubernetes.io/proxy-buffer-size: "32k"
    kubernetes.io/ingress.allow-http: "false"

ingress.yamlをapplyして再度接続してみると 400 The plain HTTP request was sent to HTTPS port と言われる。エラー内容の通りでIngress Controllerからバックエンドへの接続がバックエンドはHTTPSのポートなのにIngress ControllerはHTTPで繋ぎに行っているのが問題。再度それっぽいannotation(nginx.ingress.kubernetes.io/backend-protocol) を探してきて変更。

    nginx.ingress.kubernetes.io/proxy-buffer-size: "32k"
+    nginx.ingress.kubernetes.io/backend-protocol: "HTTPS"
    kubernetes.io/ingress.allow-http: "false"

提出したらちゃんと終了条件を満たしているはずなのに50/200しか貰えていなくてどうして!?と言って@musaprgに何も言わずに解いてもらおうとしたりした。流石にこれで減点はあり得ないだろうと思い運営に問い合わせたら間違いだったっぽくてちゃんと満点がもらえた。早めに気づけて良かった。

dockerをインストールしただけなのに...

Docker/Kubernetes担当の仕事を果たしに次の軽めの問題に取り掛かる。

サーバのアドレスが 172.16.0.0/12 にある時点で全てを察したが最初Dockerを落としてはいけないとあって、bridgeをdocker network rmしても良いのかなぁ、とか思って別の何かをやっていたら最終的に起動しておけば良い、に変わっていたので /etc/docker/daemon.json に以下を書いてDockerを再起動した。

{
        "bip": "172.15.0.1/16"
}

テレワーク推進部

実はWireguard担当でもあったので取り掛かった。クライアントの秘密鍵、ドコ・・・!?と言って運営に質問を投げてしまった、恥ずかしい。@musaprgが以前VyOSでWireguardを構築していたらしく保存されているパスを教えてくれた。私がやるべきではなかったのでは? defaultフォルダにあったのでそもそもコンフィグに秘密鍵を設定する必要なさそうだということがわかり、以下だけで良かった。

set interfaces wireguard wg01 address 10.0.0.3/31
set interfaces wireguard wg01 port 51820
set interfaces wireguard wg01 peer server allowed-ips '10.0.0.2/32'
set interfaces wireguard wg01 peer server address '192.168.1.2'
set interfaces wireguard wg01 peer server port '51820'
set interfaces wireguard wg01 peer server pubkey 'EwxEvuDmdaKslWndieq1BFsNQgWjtyWKPe+kCDSc+nQ='
commit
save

キャッシュサーバ立てたけど

今回のコンテストで一番勉強になった問題だった。

最近のUbuntuとかだと systemd-resolved が127.0.0.53で動いているのでそれを /etc/resolv.conf で参照するようにしている。しかし、今回は /etc/resolv.conf に外部のnameserverが直接指定されていてそれに対して飛んでいってしまう、というのが問題だった。

そもそもgethostbynameが呼ばれた時にDNSサーバに問い合わせをしているのって誰なんだっけ?ということを調べるとglibcが行なっているらしい。さらに調べるとglibc/etc/resolv.conf を参照するかどうかを /etc/nsswitch.conf に頼っているらしく、最初これにDNS serverの指定とか書けないかなぁ、とか思っていたけど@_iy4がresolveを指定するとsystemd-resolvedを見るっぽいことを見つけてくれた :pray:

余談 https://blog.katio.net/page/nsswitch 絶対作問したの絶対@onokatio_先生でしょ

ンジンエックス

@_iy4が解いていたのに参加して一緒に色々試す。

trac.nginx.org

こういうチケットがあってh2cとHTTP/1.1を同時に80でlistenできないのでは?となって色々悩んでいた。が、結局nginxをビルドしなくともlisten 80でいけるんじゃね?となって試したらいけたので拍子抜けしてしまった。

クラウドネイティブは難しい…

そろそろやるかと手をつけ始める。

aptで落ちているのでこういうのは大体ネットワークでは?とあたりをつける。VMにログインしてみてインターネットに繋がらないのを確認。トポロジー図を見てホスト側に生えてるブリッジデバイス(br-ex)までパケットが到達しているかをまず判別して原因の切り分けをする。VMからインターネットにpingを飛ばしてbr-exでtcpdumpすると普通に来ている。おや?と sysctlnet.ipv4.ip_forward を見ると0になってる。これをいつもの手順で1にするとクラスタが作成できた。

これで200点ももらえて良いのだろうか、と思いながらとりあえず提出して200点が得られて良かった。

後半パートに取り掛かる。Pod間が通信できていないのでCNIが原因っぽいな、とあたりをつける。kube-systemのCalicoのPodが起動していないのがわかる。セキュリティグループで通信が弾かれていそうだなぁとエスパーする。

CalicoのドキュメントのSystem requirementsを見るとTCP 179番ポートが必要であることがわかる。Security Groupを確認するとTCP 179番ポートが許可されていないのでこれを以下のコマンドで追加すると通信できるようになる。

openstack security group rule create --protocol tcp  --dst-port 179 worker
openstack security group rule create --protocol tcp  --dst-port 179 worker

あとでCalicoのPodをdescribeするとBGPが通信できていないよ、というEventが出ているのでそれで判別できるのだけれどそれに気づかずログだけ見ていたので気づかなかった。

これを提出した時点で1日目が残り3分ぐらいで、2000点ぐらいだったはず。

1日目の終わり

2日間で問題が共通であることを知らなかったのでリングフィットアドベンチャーをやったり「サイダーのように言葉が湧き上がる」を観たりしていた。 その日の終わりに食べた醤油ラーメン。シンプルだけど美味しかった。

f:id:tsuyochi23182:20210830022520j:plain
1日目の終わりに食べた醤油ラーメン

2日目

前述の通り2日間で問題が共通であることを知らなかったので今日は何が出るかな〜と思いながらコンテストが始まったら問題が共通だった。チームのSlackで問題ごとにチャンネルを切っていて、アーカイブしたチャンネルを急いで戻す作業をした。

1日目でサーバ問題はほとんど解いていてあとサーバ気象予報20点分とパケットが通らない!だけだった。ネットワークをやらなきゃいけなくなってしまった。

20点分に時間を割くのは時間の無駄そうということで放置。「アドレスが配布できない」に手を付ける。

アドレスが配布できない

とりあえずclient側でnetplanで dhcp6: true を設定する。(結果的にどうやらこれはManagement Flagが返って来れば設定は要らなかったっぽい?)

tcpdumpで通信を見てみるとdhclientからmulticastのICMPパケットが飛んでいる。このパケットなんだろうと@minoeru_xavierに相談するとSLAACでは? と言われる。色々調べるとDHCPv6はRAでManagement Flagがtrueの時のみ動くのでDHCP serverでRAを応答してもらう必要があるらしい。すでにDHCP serverにはradvdがインストール済みだったのでradvdでManagement Flagをtrueで返すようにしてsystemctl enable --nowしたら動くようになった。

これを解いたあたりで昼になって唯一手持ちの問題がない人になったので昼食を買いに行く担当になりました。カレーとナンを食べた。インドカレーは店によって違いが少なくクオリティが安定しがち。

パケットが通らない!

SRv6わかる気がしないがやるか・・・と言いつつ@_iy4と二人で取り掛かる。

pingをC11からC21に飛ばそうとするがRouter1の内部でまず落ちている。VRFでルーティングした後にSRv6に載せているのでどちらに原因があるかまず確かめるためにSRv6の前にeth0に流すルールを加えてみるがeth0に流れてこない。

これは net.ipv4.ip_forwardが0では?と確認するとやはり0だった。色々試すとipv6のforwardingも有効化する必要がありそうだった。

ここら辺まで提出すると100点ぐらいもらえた。

C21でtcpdumpをしてみるとpingが到達してreplyも返している。しかしRouter1のeth1には流れてこないのでどうやらFRRで経路を広報してやらんといけないらしい。SRv6のprefixを広報してやればいいのかなぁとか言って色々試したが結局わからなかった。FRRの資料少なすぎてわからん・・・難しい。

おわりに

ICTSCはもう終わりかもと前回の本選で言われていてマジか〜と思っていたのに今回開催され、更にそこで優勝することができ嬉しかった。(欲を言えば賞金が欲しかったけど・・・)

次もあれば参加したいモチベーションはあるがメンバー全員が卒論/修論を書かなければならず大丈夫だろうか・・・。

運営の皆様お疲れ様でした。楽しいコンテストをありがとうございました!