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

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

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

ISUCON10予選で再起動試験失敗で敗退しました

TL:DR

  • 教訓: レギュレーションはちゃんと読み込もう(N回目)

はじめに

3000点以上を取っていたのに再起動試験でコケて予選敗退した悔しさの勢いでブログを書いています。ultra_fast_gopherという学生3人チームで参加していました。

やったこと

一週間前

とりあえず去年惨敗したからちゃんと練習しようとISUCON9予選を解き直した。環境をAWSのEC2上に構築したが4台ぐらい用意したら結構な金額が取られてしまった、かなしい。

New Relicも試したがTransportを入れ替えたりcontextを取り回したりしてもなぜか外部API呼び出しが可視化されなかったのとpprofで別段困らなかったのでpprofとmysqldumpslowの二刀流のみで戦うことにした。

高速化もいろいろ実践したが重要なのは事前準備だなぁと思いながら、phpMyAdmin、netdata、WireGuard(VPN、今回は外から直接叩けないため)、GitHub Actionのself-hosted runner(CD用)などを諸々セットアップするためのスクリプトを用意した。

当日

〜開始

今回は集まらず全員がDiscordでリモートで参加したが実際に集まるよりモニターが多いという点でめちゃくちゃ快適だった。来年もリモートで良さそう。

開始時間が遅くなったのでのんびりおにぎりとペットボトルとラムネをコンビニに買い出しに行った。ちょうど雨に降られて悲しかった。

開始〜初動

Bad Gateway!?インスタンス一覧見られない!?となっていたがチームメイトがGistの方からインスタンスのアドレスを見つけてきてくれたので事前準備したスクリプトを一人一台流し込んでセットアップした。(GitHub Actions のrunnnerはnode1のみ)

(そういえばVPNが51820ポートでListenしていたがつながらなかったのでNTPのポートなら行けるだろ、と思って試したらいけてしまったが良かったのだろうか・・・。)

とりあえずpprofとslow query logの設定のみ行いベンチマーク実行。インスタンスが同じなのを忘れて最初pprofを見てJSONエンコードが遅い??となっていてDebugの無効化などをしたがそもそもMySQLの方が圧倒的にCPUを食っていたのでnode2にMySQLを移行し、再度実行。この時node1からのログインをphpMyAdminでサクッと可能にできて楽だった。

slow query logを見てぽちぽちインデックスを追加する(~16:00ぐらい?)

この時点で1000ぐらいだったと思う。大体ここら辺でチームメイトがUser-Agentの対応を実装してくれていた。展開できるものはそもそも正規表現を使わずstrings.Contains等にしておいた。多分負荷がそもそも高くなかったのでこの時はそこまで影響がなかった。

中盤

椅子のサイズから納入できる物件をレコメンドする関数が時間を圧倒的に使っているがインデックスの追加でどう早くするかわからなくなる。

全てのpopularityを負の値にするのとDESC>ASCのORDER BYでMySQL 8ならインデックスがDESC, ASCで貼れるのを思いつく。前者はバグらせた時がやばそうだったのでとりあえずnode2をMySQL8にあげてquery cacheの設定を削除。再びベンチマークをかけるがかなりスコアが下がってしまった。やっぱダメじゃんと言ってnode3にDBを移行。

ここでDB分割すればよくね?と思ってnode1とnode3の分割を実行。大体300ぐらい上がった気がする。(1500ぐらい?)

やっぱりMySQLが依然重いのでインメモリ戦略に切り替える。大体のデータはpopularityとidでソートしたうえで最初の数百件ぐらい持ってくれば良いだろうという考えからbtreeライブラリを持ってきて物件の全データをbtree(今回は2分木)で展開し、10000件以上アクセスしたらやばそうなのでDBにフォールバックするようにした。

感想戦を見ているとそもそもpopularityとidに適切にインデックスを貼るだけで良かったらしく無駄だったっぽいので真似はしないでください。

大体これで1800~ぐらいには行っていた気がする。

中盤〜終盤

ここらへんでslow queryが検索でばらけていてよくわからなくなってくる。検索がやばそうだけどとりあえずpprofに立ち戻るとNazotteがかなり時間を消費していることがわかる。 N+1が起こっているのとこれは別にDBでやる必要全くないな、ということでGoでのチェックに切り替える。

凸多角形の内外判定をしてくれる関数をインターネットで漁りまくりGoに書き換える作業を行う。

実装が下手。修正してとりあえず投げるがverifyで無限に落ちる。始点と終点が同一なのがやばそうということで調整するがそれでもダメ。DBでの結果と比較して間違っているものを落としてきた。いい感じのツールがわからないのでチームメイトに投げると可視化してくれた。↓これでバグるのは流石に実装が悪そうということになるがバグが見つからない。

f:id:tsuyochi23182:20200913013221p:plain

(言いがかりの可能性も高いが)参考実装が間違ってるのでは?と別の実装を持ってきて始点終点を調整するとようやく通る。(私の実装が間違っていた説も高いです。)

これで大体3000弱ぐらいのスコアが出るようになったので不要なものを片っ端から無効化して上振れを狙って何度かベンチマークを回し3200以上が出たので再起動試験等をしつつ残り20分は椅子を温めました。

結果

そもそも再起動後にベンチマーカーが動作するだけでなくデータも保持されている必要があることを知らなかった。ISUCON初心者ですか???

それでもDBへのフォールバック機能があるためデータが読み込みのみであれば問題なかったが書き込まれるとインメモリを使ってしまうため20件以上書き込まれるとフォールバックされず死んでしまう。

座標の方についてはデータはDBから参照しているため問題なく動いていたはずである。というわけでおそらくだが↓のPRをmergeすれば予選突破できていたと思う。

github.com

もはや何の意味もないスコアだが、かなり健闘をできていた方だったはずなので本当に悔しい。

総評

毎度毎度ドキュメントちゃんと読もうな?っていう反省をしている気がする。8の本戦では負荷をあげる機能に気がつかなかった。

今回はちゃんと準備していてパフォーマンスも完璧だっただけに本当に悔しい。GitHub ActionsとWireGuardは最高だった。事前準備はとても重要です。そしてドキュメントはディスプレイに穴が開くほど読みましょう・・・。

また挑戦するために来年開催されることを願っています。そして来年こそは必ず本戦に出場して入賞を目指します。

謝辞

運営の方々、楽しいコンテストをありがとうございました。本当にお疲れ様でした。

Wantedlyサマーインターン

書くの苦手だけど書こうかな〜と言っていたらTwitterを捕捉されてしまい引くに引けなくなったので3年ぶりぐらいにブログを更新しています。(案の定なかなか書けず完全に乗り遅れました。早く書けるようになりたい。)

先日、3週間のWantedlyのサマーインターンにインフラコースで参加してきました。

WHY

ブログやイベントなどでWantedlyは早くからGoやKubernetesを本番投入しまくっているらしい、というのを知ったのがWantedlyに興味を持ったきっかけでした。それから時は流れ、今年のサマーインターンの募集を見て面白そうだな〜、と思い応募しました。

余談としてインフラとバックエンドどちらのコースにするかはかなり悩んだのですが、インターンコースの こんな人は是非 のところ見て自分じゃん、と思いインフラに決めました。

WHAT

取り組んだ内容

今回参加したインターンは、初日に課題を決定して3週間かけて一つの課題を解決するもので、私は「毎日機械学習のモデルの学習をKubernetesの上で行いDockerのイメージのビルドからデプロイまで行う仕組みを作る」という課題を選択しました。

課題を進める過程で必要な要件や仕様をミーティング等を通じて洗い出し、調査し、構築を進めました。 最終的にはWantedlyKubernetesクラスタ上で既に稼働している Argo というWorkflow Engineに乗っかる形にし、機械学習、Dockerイメージビルド、デプロイをそれぞれ一つのジョブとして順番に実行する仕組みを採用しました。単にYAMLを書けばいいのかな?と思っていたらデプロイ処理をKubernetes側から行うのはWantedlyで過去あまり行われてきたことはないようで、一部のコンベンションの変更等が必要になり、関連するツール等の変更も行いました。

PoCができた段階で再びミーティングを行い、workflowのYAMLってほとんどテンプレート化できるしYAMLを自動生成できたら保守面でもユーザ面でも嬉しいよね、という話になり残り一週間を切った段階で新しいリポジトリが生えてツールを作ることになった時には結構焦りました。

そんなこんなで元々問題提起されていたプロジェクトになんとか導入を終え、バタバタしたり何度もリトライしながら最終日の成果発表直前にプロダクション環境で無事学習からデプロイまでが行われることを確認できました。 また、そのプロジェクトの agatanさん に作成した仕組みを喜んでもらえたので非常に嬉しかったです。

文字に起こすと大したことはないのですが途中では悩むことが多く、メンターの munisystemさん に質問したり、指摘されたりしながらなんとか進められました。ありがとうございました!

より技術的な話も書きたいのですがここに書くと長くなるので後述します。

カルチャーなど

非同期コミュニケーション

印象的だったのはSlackのチャンネルに流れるのはお知らせや通知等が主で、多くの作業やコミュニケーションがGitHubをベースとして非同期で行われていることでした。最初は戸惑いましたがSlackでメンションを飛ばす時の申し訳なさなどが軽減されて慣れてくると快適に思えるようになり良かったです。

Infra Squad

Wantedlyではチームの小単位をsquadと呼んでいるそうです。

今回私が取り組んだ課題は元々アプリケーションのsquadから上がっていた課題でした。Infra squadが他squadとコミュニケーションが取れていることで開発チーム側が抱えるそういった課題を協力しつつ解決しやすくなっているのが良いな、と感じました。

Wantedlyでのインフラの歴史も聞く事ができ、どういった経緯やモチベーションでその技術を採用してきたか、なども聞けて面白かったです。これから来る技術の見極め力の高さに驚きました。

社内勉強会

週1回のペースで社内勉強会が開催されており私は Microservices MondayGophers Code Reading Party に何度か参加しました。面白かったです。社外の人でも参加できるらしいので興味ある人は参加してみてはいかがでしょうか(?)

ご飯

画像はモーメントにまとめたので良かったらみてください。 UXが良いと言われるカレーうどん屋、白金台のファミレス、美味しい和食屋など色々行けました。どれも美味しかったです。普段の学生街に比べて断然相場は高いですが・・・。 twitter.com

終わりに

(2日以上||就業型)インターンに参加したのは初めてでなおかつKubernetesを業務として触った事はなかったのですが無事成果を出す事ができてホッとしています。

ところで、ちょうどKubernetesのRBAC周りをちょうどインターンのArgo周りで触っていたおかげでインターン期間中にあった ICTSCの300点問題 を即解答する事ができ5位だったので非常にラッキーでした。閑話休題

周りにいる社員さんも優秀な方ばかりで刺激を沢山受けました。Infra Squadのリーダーの koudaiiiさん と1on1をさせてもらったり、他squadの方とランチに行かせてもらったりと交流を行う機会が豊富にあり、色々なお話を聞けて面白かったです。

これからも頑張っていくぞ!と思える最高のインターンでした!

オリジナルTシャツと修了証
Tシャツ欲しいな〜と思ってたらもらえたカッコイイTシャツと修了証。良い

技術的な話のおまけ

せっかく色々調査したので共有しよう、という事で軽く書いておきます。

ツール群

Kubernetes上でDockerイメージをビルドするための機構としていくつかのツールを検討したのですが最終的にはDocker in Dockerを採用しました。ある程度詳細に調査したツール群に軽く触れておこうと思います。

kaniko

  • Google謹製のDockerイメージビルドツール
  • デーモンを持たずkanikoのコンテナ単体でビルドする事ができる
  • 特殊な権限がいらない(privilegedやseccomp、AppArmorなどのsecurity contextを変更する必要なし)
    • Dockerfileをパースしてkanikoと同じ環境でRUNを直接実行する
    • (詳細な調査はできていないがS3などのcredentialが読めてしまうリスクが高くなりそう)
  • contextとしてローカルのファイル以外にS3、GCS、Gitなどが指定できる
  • cacheを勝手にやってくれる

といった特徴がありました。pullからpushまでを勝手にやってくれるのはかなり便利で今回のような一連のworkflowの一部のjobとしてのビルドやCIで使うには便利そうという印象を受けました。 マルチステージビルドも対応しているのですが並列で動作しなかったりそのステージを跨ぐ度にアーカイブする処理が入ったりでパフォーマンス的には余りよくありませんでした。

BuildKit

  • moby謹製のビルドツール
  • Docker 18.06からDOCKER_BUILDKIT=1で使えるやつ
  • 高速、セキュア、高機能
  • 並列化やキャッシュをいい感じにやってくれる
  • buildkitのデーモンを起動する事で使える
  • rootless対応(experimental)

非常に便利なツールで良い感じでした。 rootlessで使いたかったものの、私が試したところだとKubernetes上でrootlessではうまく動作しませんでした。seccomp周りの設定っぽいですが具体的な理由までは調査できませんでした。

dind(Docker in Docker(Kubernetes))

  • Dockerのコンテナ内でDockerを動かす
  • rootlessのイメージもあるが何れにしてもprivilegedが必要
  • いつものDockerが使える

なんてことはない普通のDockerをコンテナ内で動かすものです。普段普通はDockerでイメージをビルドする上、CI等でもDockerでイメージをビルドするため安心感があります。デフォルトではHTTPSで通信するデーモンが起動するためHTTPで利用するためには環境変数でDOCKER_TLS_CERTDIRを空で上書きする必要がある点に注意が必要です。ただしrootlessであってもprivilegedは必須です。

dindを採用した理由

これらのツールを調査した上でdindを採用した最も大きな理由はWantedlyで採用されているイメージレジストリquay.ioManifest V2, Schema 2 という最新のイメージの仕様に対応していなかったためです。 ここら辺のissueに詳細があります。

github.com

quay.ioに問い合わせをしたところ数週間以内に対応予定とのことでしたが記事執筆時点ではまだ対応されていないようです。上に挙げたツールの中で古い仕様にも未だ対応しているのはDocker in Dockerだけでした。(DockerでもManifest V2, Schema 1はdeprecatedだから早くイメージレジストリの方を対応してもらってね。みたいなメッセージが出ます。)

最も大きな理由は身も蓋もない感じとなってしまったのですがそれ以外の理由もあります。

Wantedlyでは開発者の負担を減らすためにDockerイメージのビルドなどもCLIツールで共通化され、Wantedlyのコンベンションに沿った仕組みを搭載しています。これらのツールは当然Dockerを想定しているため他のツールを利用する場合には新しいビルド手法も保守していく必要が出てきます。

また、Argoではsidecarとしてメインのjobの他で同時にコンテナを動かして置くことができるようで、公式に dindを行うexample がありました。同一コンテナ内に設置するのは少し抵抗がありましたがsidecarにできるとかなりスッキリします。

privilegedをつけることと以上のことを天秤にかけた結果dindを採用するに至りました。

フェルマー方程式/Fermat(JOI2007春合宿Day2 2nd)

春合宿まで精進するぞって言ってし始めたんですが結構始めから躓いていて早速数学に苦しめられています。(本選参加記はもう少ししたら書きます。)
Fermatも理解できず、LatteMaltaさん(@latte0119)に教えていただいてなんとか理解できたので忘れないうちに備忘録としてまとめます。(ありがとうございました!)

続きを読む

JOI2016/2017予選参加記

最後ということもあり書きます。
JOI(情報オリンピック)は中一から参加しているのでもう5回目ですね・・・。時の流れは早いものです。 前回、前々回は本選まで出場していますがいずれもそこで敗退しています。

事前準備

今回はいくつかのツールを事前に準備して挑みました。具体的には

  • テストケースの入力ファイルをダウンロードフォルダから自動分配するプログラム
  • 計5つのテストケースを実行ファイルの標準入力に渡して実行し結果をファイルに書き込むプログラム
  • 一度提出したファイルとローカルに残っているファイルが一致しているかを確認するプログラム。(結果として公式が公開した答えと自分の答えを照合するプログラムにもなりました。)

などです。
5回目にもかかわらず少し戸惑いながらなんとかログインなどの準備を完了しました。

1

JOI予選1問目恒例ifでとく感じの問題ですね。a≠0なので0を基準に考えればOKです。 gist.github.com

2

これもJOI予選2問目恒例forと配列でとく感じの問題ですね。 問題を勘違いしてやたらバグらせてしまったのが反省。 gist.github.com

3

O(NMD)で問題ないので3重ループしてあげれば終わりです。 gist.github.com

4

ここで難易度が急上昇してビビる。とりあえず問題を読んでDPっぽいなとは思いつつまだ思いつかなかったので飛ばして5問目。

5

UnionFindとかDBS/BFSとか考えて微妙にうまくいかなかったのでなにも考えず遡るようにsetでゴリ押すコードを書いたらケース5でも5sほどで終わってしまいまぁいいかとなってそのまま4問目へ。

gist.github.com

4

考察をすると最終的な状態を決めてしまえばどれを移動すればいいかが求められることがわかる。最終的な状態の並び方はM!通りあるがこれをbitDPで処理してやるとO(2M)で良いことがわかる。また、状態を確定させた時にいくつを移動しなければならないかはあらかじめ累積和を利用して数えておくことができる。

gist.github.com

6

ダイクストラに寒暖の異なる部屋に入れるまでの時間、寒暖、経過時間を持っておけば良さそうだとは思ったがそれでうまくいく確証と計算量がオーバーしそうな予感がしてそのまま終了。

結果

まだ結果は出ていませんが自分で答え合わせをした感じだと解いたところは全部あっていたようで5完(500/600点)でした。今回は各ツールのおかげでだいぶスムーズにできました。
本選にも出場できると思うので後悔の無いよう頑張ろうと思います。

カード情報が盗まれたっ!と一人で慌てていた話

自戒も含めて文字に起こす。

12月4日の夜、私はデビットカードを利用している銀行のインターネットバンキングにログインした。 そこで異変にすぐ気づいた。使った記憶がないのに口座から1万円以上なくなっていたのだ。慌てた私は不正利用の可能性を考えそこですぐにデビットを停止した。

f:id:tsuyochi23182:20161205191248p:plain
見てみると12月3日の夜にVisaデビットで13800円が利用されていた。しかし私は全く利用した記憶がない。いよいよ不正利用の可能性が高まってきたぞと思いつつ利用明細を開くとAmazonからの請求になっていた。 盗んだカードをAmazonで利用したりするだろうかと疑問に思いつつ自身のAmazonアカウントの注文履歴を見てもそのような請求は見当たらなかった。まぁAmazonなら返金される可能性も高いだろうし安心だと考えとりあえずAmazonのカスタマーサポートに連絡をしてみようかと考えていた時ふと13800円とはなんの金額なんだろう、と気になったのだ。 まぁそう簡単にヒットするわけはないだろうと思いつつGoogleで「Amazon 13800」と検索。しかしここで予想を裏切られた。f:id:tsuyochi23182:20161205184811p:plain
あっ...(全てを理解

時を遡ること数ヶ月前、国内のAmazon CloudDriveの無制限プランが解放された。無制限のクラウドドライブを渇望していた私は一目散に無料体験に飛びついた。その際にカード情報を登録する必要があったのだが、とりえあず入力して使ってみたところ思ったよりも速度が出ず実用的でないと判断したため金を払う価値はないと感じた。私はここでなぜか体験プランの終了を押すのではなく支払い情報から自分のカードを無くしておくことで体験プランが終了すると同時に支払いが完了せず勝手に終了してくれるだろうと考えていた。

しかし実際にアクセスしてみると実際支払いが完了していた。どうやらAmazonで普段買い物をするために登録しているカード情報から支払いが行われてしまったらしい。まさかそっちの情報から引き落とされるなど予想だにしていなかった。

仕方ないのでAmazon CloudDriveのカスタマーサポートに連絡をしてこの旨を伝えた。 このメールを送ったのは確か23時過ぎで、まぁ翌朝には返信が来るだろうと期待していた。しかしなんと日付が変わって2時ごろに返信があった。 まさか夜中にサポートしてくれるとは思っていなかったためとても驚いた。
結果はOK。 f:id:tsuyochi23182:20161205190328p:plain
ありがとうAmazon!今後はこのようなことがないように気をつけます...。

(今回事実に気づく前に一度銀行の方にも連絡を入れてしまい慌てて、なんでもありませんでしたという謝罪の連絡を入れ直したりしており、多くの人にご迷惑をおかけしてしまいました。この場を借りて謝罪を述べさせていただきます。申し訳ございませんでした。)

Node.JSでアンプをLAN経由で操作

以前家の今のテレビを父が購入した際、なぜか導入されたMarantz NR1604。(今や家で使う人は私一人のみでとてももったいない。)
NR1604はDLNAやネットワークラジオ、AirPlay*1などの機能をサポートしており、その一環としてLAN経由で操作ができるようになっている。
f:id:tsuyochi23182:20160709181127p:plain:w400

このNR1604は、音質などに疎い私には十分満足のいくものであり、よく家に一人でいる時にAirPlayを利用して音楽を流している。
NR1604は電源が切れている状態でもLANには接続されており、AirPlayで再生を開始すると自動的に電源が入るようになっている。
しかし、AirPlayが終了した後でも自動で電源が切れる機能はないため、私はよく電源を切り忘れてしまう。アンプは非常に熱を持つためよろしくない。

そこでタイマーをセットして指定された時間になるとLAN経由で電源を落とすスクリプトを軽く組んでみた。以下、開発環境。

まず、LAN上に存在するNR1604のIPアドレスを取得するところから始めた。プログラム内にIPアドレスを直で書いてもよかったが、度々変動していることもあったので動的に取得させることにした。
LAN上のデバイスのIPアドレスを取得する方法として、DLNAでよく使われているUPnPなどがあるが、今回はAppleが開発したBonjourを用いた。(そもそもUPnPをNR1604をサポートしているかは不明)
Node.JSにはBonjourを利用するなんとマルチプラットフォームなライブラリを公開してくれている人がいたため、開発はスムーズに行うことができた。Node.JSなんでもライブラリあってすごい!本当に。
Bonjourを利用して、サービスの種類をAirPlay(raop.tcp)にしてやると、以下のようにデバイスの情報が取得でき、その中にIPアドレスも書いてある。
f:id:tsuyochi23182:20160709183705p:plain:w400

あとは指定の時間にCronJobが発火するようにセットし、このIPアドレスに対してHTTPリクエストを送信するだけで電源を切ることができた。
今更ながらリクエストを送るために今回は既存をライブラリを用いたがそのライブラリが古いものであったため、バグ修正に追われて自分で書いた方が圧倒的に楽だったのが残念。
(一応コードを貼り付けますが基本的にmarantz-avrがおかしいのでまともに動きません。)
これで気兼ねなくMarantzアンプを使えるようになったので満足。

Marantz NR1604 Auto PowerOff

*1:Appleが開発した、iOS/macOSなどでサポートされているLAN経由で音声、ビデオなどを転送する機能。