すみません、すごい前置きが長くなってしまったので、ランキングの結果だけ知りたい場合は目次から飛んでください!
「本番環境でやらかしちゃった人 Advent Calendar」をご存知ですか
おはようございます!
自分が好きなアドベントカレンダーに、「本番環境でやらかしちゃった人 Advent Calendar」 というのがあります。インフラエンジニアとして働く自分は、毎年こんなにもリアリティのある やらかしと学びの集大成 を「明日は我が身」だと胃を痛めながらありがたく拝見していました…。
この分野における しくじり先生 がここまで一同に介する機会というのはあまりないことから、大変マニアックながらも知る人ぞ知るアドベントカレンダーという感じになっています。
このアドベントカレンダーのいいところ
ポイントは、しっかりと ポストモーテム の要素がルール化されているところだと思っています。
振り返り、レトロスペクティブ、みなさんが所属されている組織によって呼び方は色々あるんだと思いますが、なにしろ再発防止策というのは本当に大切ですよね…。
過去回を供養しましょう
このアドベントカレンダーは過去4年にわたって開催されており、記事数もかなり多く、どのような年でも悲しみと反省が尽きないものだと身につまされます。2019〜2022 のリンクを置いておきますので、よかったら年末年始のお休み中にでも供養に訪れてみてください。
何度振り返ってみても味があります。あらかじめ、いつもより多めに胃薬を用意しておいたほうがいいと思います。
もっとも注目を浴びた「やらかし」は何だったのか
最近、Qiita API をいくつか触ってみる機会がありましたので、
Qiita API を使って、過去の 「本番環境でやらかしちゃった人 Advent Calendar」の中でもひときわヤバいやらかしが一体どのやらかしだったのか、サクッと求められるのではと思い、やってみました。
以下の手順で出来そうです。
- アドベントカレンダーの記事 ID 一覧を出す
- 記事 ID から Qiita API で「いいね」数を取得する
アドベントカレンダーの記事 ID の一覧を出す
まずは、対象のアドベントカレンダーに投稿された記事の一覧が欲しいところですが、残念ながらそのような API はありませんでした。これは、アドベントカレンダーのページをスクレイピングするしかないかな?と思いきや、Qiita ではスクレイピングが禁止されているようです!
スクレイピングはサーバーの負荷上昇への懸念があるため、Qiitaへのスクレイピングは許可しておりません。
インフラエンジニアとしてはとてもよくわかるところで、ごもっともです。4年分のカレンダーを取得するだけなら負荷もわずかだとは思いますが、何かの間違いで無限ループしたりしないとも限りません。
ここではしっかり丁寧に、ブラウザの「ページを別名で保存…」から HTML を保存します。
続いて、HTML を解析していきます。インフラエンジニアなので、grep
でパースします。grep
は万能なのです。シェルスクリプトを書きましょう。
vim awesome.sh
#!/bin/bash
REGEX='<a href="https://qiita.com/[^/]+/items/[^"]+" class="style-3ki7ar">[^<]+</a>'
grep -oE "$REGEX" 2019.html
なにはともあれ実行です。
$ chmod +x awesome.sh
$ ./awesome.sh
<a href="https://qiita.com/2gt/items/d6906a185109e66df74f" class="style-3ki7ar">いつものように本番作業してたはずなのに</a>
<a href="https://qiita.com/raki/items/8aab477b4d64c7e7610e" class="style-3ki7ar">crontab database ~君がしでかしてくれたもの~</a>
<a href="https://qiita.com/dala00/items/bc03f6522dd20969f481" class="style-3ki7ar">さよなら本番サーバー</a>
...
一見よさそうですが、しかし件数が明らかに多すぎました。数えたら64件もあります。アドベントカレンダーは25日間しかないのにおかしいです。
$ ./awesome.sh | wc -l
64
少し眺めているといくつか重複があるように見えたので、伝家の宝刀 sort | uniq
で対処します。
$ ./awesome.sh | sort | uniq | wc -l
16
よさそうです!ということは、なぜかアドベントカレンダーのページには同じ a
タグが4回も登場しているということなので少し不思議ですが、だいたい動けばオッケーなので気にしません。
※ ところで、2019年は23件の投稿があったので、16件では足りません。これはアドベントカレンダーが、ユーザー自身のブログなど Qiita 外部の記事でも登録できるルールになっているため、そのような場合に grep
にマッチしなかったということでした。今回は Qiita 上の記事についてのみ考慮することにします!
スクリプトを修正して、
-
sort | uniq
を追加しました - 引数として年度を渡せるようにしました
#!/bin/bash
REGEX='<a href="https://qiita.com/[^/]+/items/[^"]+" class="style-3ki7ar">[^<]+</a>'
grep -oE "$REGEX" "$1".html | sort | uniq
ウェブページを保存するときに、2022.html
のようなファイル名にする運用です。
$ ./awesome.sh 2022
<a href="https://qiita.com/mogmet/items/a6166f9bb92eaf33a756" class="style-3ki7ar">Firebaseを使った本番環境で誤ってユーザデータを削除してしまったお話</a>
...
<a href="https://qiita.com/wancom/items/386f182996fe2f1ccaf0" class="style-3ki7ar">「接続できないって言われたんだけど?」「😱😱😱」</a>
動作確認バッチリです。できれば記事 ID の一覧を出力するところまでやっておきたいので、もう少し頑張ります。たとえば上記のような出力から記事 ID だけ欲しいとき、正規表現を使っても良さそうですが、自分はよく cut
を使ってこのようにやってしまいます。
$ ./awesome.sh 2022 | head -1 | cut -d'"' -f2 # URL だけ抜き出す
https://qiita.com/mogmet/items/a6166f9bb92eaf33a756
$ ./awesome.sh 2022 | head -1 | cut -d'"' -f2 | cut -d/ -f6 # さらに ID だけ抜き出す
a6166f9bb92eaf33a756
head
でいくつかサンプルするのは試行錯誤に便利です。最終的にこうなりました!
#!/bin/bash
REGEX='<a href="https://qiita.com/[^/]+/items/[^"]+" class="style-3ki7ar">[^<]+</a>'
grep -oE "$REGEX" "$1".html | sort | uniq | cut -d'"' -f2 | cut -d/ -f6
オッケーです。
$ ./awesome.sh 2022
a6166f9bb92eaf33a756
...
386f182996fe2f1ccaf0
Qiita API から「いいね」数 likes_count
などを取得する
GET /api/v2/items/:item_id
API を使って、記事の情報を取得できます。複数の ID を指定して記事をいくつか一気に引ける API は無いようでしたので、1つずつ引いていきます。
試しに1つやってみます。レスポンスは JSON です。あとでソートしたいので、いいね数 likes_count
、コメント数 comments_count
を含む CSV を jq
コマンド(便利)で出力します。
curl -sS https://qiita.com/api/v2/items/d6906a185109e66df74f \
| jq -r '[ .likes_count, .comments_count, .id, .user.id, .title ] | @csv'
こうなりました。わかりやすいようにユーザー ID やタイトルも出しました。出力も良さそうです。
1454,16,"d6906a185109e66df74f","2gt","いつものように本番作業してたはずなのに"
インフラエンジニアなので、これを新しいスクリプトにしておきます。記事 ID をコマンドライン引数に取るようにしました。
vim amazing.sh
#!/bin/bash
curl -sS https://qiita.com/api/v2/items/"$1" \
| jq -r '[ .likes_count, .comments_count, .id, .user.id, .title ] | @csv'
動作確認 OK です。
$ chmod +x amazing.sh
$ ./amazing.sh 54d31810a5bf853154a2
27,1,"54d31810a5bf853154a2","sakai00kou","タイムリープして本番リリース直前のBIG-IPをぶっ壊した話"
しかし、わかりやすさのためにタイトルを出力するような実装にしてしまったので、動作確認もキリキリしてしょうがないです。
最後に、認証なしだと Qiita API の Rate Limit に当たりそうだったので、QIITA_ACCESS_TOKEN
環境変数でアクセストークンを渡せるようにして、完成としました。
#!/bin/bash
curl -sS https://qiita.com/api/v2/items/"$1" -H "Authorization: Bearer $QIITA_ACCESS_TOKEN" \
| jq -r '[ .likes_count, .comments_count, .id, .user.id, .title ] | @csv'
https://qiita.com/settings/applications から「個人用アクセストークン」を発行して、仕込んでおきます。
$ export QIITA_ACCESS_TOKEN=592...
結果
ここまで作ってきたスクリプトを組み合わせて、実行してみます。シェルスクリプトの醍醐味は、この1つ1つの処理を繋ぎ合わせて欲しい結果を出すところだと感じます。
しかしながら今になって思えば、シェルスクリプトにこのような酷いファイル名をつけていると、いつか本番環境でやらかすことになってしまいそうで非常によくないです。改善が必要だと感じました。
既に HTML ファイルは保存済みです。
$ ls
2019.html 2020.html 2021.html 2022.html amazing.sh awesome.sh
2019〜2022 について awesome.sh
を実行し、その結果を1行ずつ amazing.sh
に渡します。
for year in {2019..2022}; do
./awesome.sh $year | xargs -L1 ./amazing.sh
done | tee result.txt
ッターン!
result.txt
1454,16,"d6906a185109e66df74f","2gt","いつものように本番作業してたはずなのに"
312,4,"b53f38569ebe85de7604","Canon11","「あれ、チュートリアルから始まった」。僕とキャッシュとサイレントリリース。"
550,21,"c0c0feda4e7a8030346f","NACK","cron哀歌~typoを笑うものはtypoに泣く~他"
107,1,"e0b70aff25412655b9ed","Words4MyRegret","1セグメント全断の土曜日"
2500,50,"bc03f6522dd20969f481","dala00","さよなら本番サーバー"
272,6,"04ae78910273d4bc3102","hoshi_kouki","顧客のコンテンツデータを消失させた話"
112,2,"5b8f6e94b21022f6725d","jkr_2255","進まない、マイグレーション"
412,10,"8f4e4834c508052d2ddf","madai0517","データ移行をしただけなのに…(起こってしまったメール誤配信)"
351,1,"5565332c748812f384b9","moriya_snj","稼働中の商用ネットワークでVRRPの切替検証を実施しちゃった話"
145,1,"fda1b30b803652030d61","onigireee","あの夏の抽出件数を僕はまだ忘れていない"
323,40,"08c54072e0d9d0734ccd","potyamaaaa","筋肉マージは辞めよう"
2009,21,"8aab477b4d64c7e7610e","raki","crontab database ~君がしでかしてくれたもの~"
316,11,"3069e79038c961458ba2","sadnessOjisan","思いもよらないものをnpm publishしてしまった話(前任者の顔写真など)"
321,6,"f128ac3475f340724cc8","undoroid","[AWS] Amazon SESのアカウントが止められちゃった話"
103,0,"62f0ad7235a635aa13dd","wordijp","電波調整、ヨシ!"
381,6,"0d80919d98696537ae10","yumamura","VSCodeの操作ミスでGCP Cloud Composerの裏側k8sをお掃除した話"
46,4,"7a025e56578612dd2a79","AkariKanbe","WebサービスのDBに関する失敗談"
917,9,"181052eb85b686783806","HirotoKagotani","管理者用初期化URLを踏んでWebサービスのデータをふっとばした話"
,,,,
44,0,"937a531772c820a8dc5c","Words4MyRegret","1セグメント全断の土曜日 Part2"
117,4,"ee1d507e18f25d00b2f3","Yutaka_Nakano","クラスタのノード欠損を復旧しようとしてクラスタを丸ごと落とした話"
116,5,"774bf3dd84ae2488dea2","glassmonkey","本番環境で動作している(はずの)selectを叩いてサービスを落とした話"
,,,,
52,1,"620e0ea3c7d4168e8321","hoshi_kouki","割れ窓を放置した人の末路"
421,5,"dad1e37ee4127ea7132b","kappa0923","AWSでやらかして3桁万円請求された話"
130,7,"9a811846dd32373ca1d1","karo-jp","コントローラーが故障したraid5ディスク上のDBバックアップはどこに格納されていたか "
84,3,"8fa283d8dc8bb49d81f7","madai0517","とあるテーブルの中身を一括更新した話から学ぶPITR"
333,21,"b2fe3d96dc048ff44207","maip0902","本番環境でsudo yum updateしてサービスが動かなくなった"
,,,,
149,6,"33221dc1abb9202a3471","namizatork","APIの向き先を間違えたままでリリースしたらTwitterトレンドに載った話"
250,7,"75793a4db539f054b28d","nobu0605","本番でTableを1つDeleteしてしまいON DELETE CASCADEでさらに4つTable dataが消えた話"
97,8,"b952df76beeb9aaf2f3d","noritakaIzumi","Crontab を別のユーザのもので上書きしてしまった話"
55,8,"7e2eb9a28d42b3165c83","real_yaruo","ホスティングサーバの/binディレクトリをrm -rfしてしまったおじさんの昔話"
97,0,"47d2144f8c47c46b7313","sakai00kou","知らないシステムを孤立させてしまった話"
35,0,"975afecc8840c2ef5cc3","takoikatakotako","検証環境のURLが入ったメールを本番環境のユーザーに送ってしまった話"
262,1,"1281dc2d3c71c29ab44d","tosiooooooo","惰滅のサーバ"
105,1,"5fa96332dc22029ad44a","tosiooooooo","君の名は"
82,0,"b8120e3ac5b79019e994","tsumita7","S3「 お゛め゛ぇ゛に゛権゛限゛ね゛ぇ゛がら゛!」"
24,0,"cf0d286f6d86127d1c3f","Adachi-ku_Zaiju","本番環境でやらかしちゃったかもしれない話 2021-12-22"
46,0,"f305f031669db2c057ea","DoueKazuna","Auroraをリードレプリカに「えいやっ」したら死んだ話"
22,0,"df9dd25537150323e7a9","EndoMotoki","Azureのインスタンスがデストロイされた話"
9,0,"97acf2f6ac8cd11df518","Words4MyRegret","1セグメント全断の土曜日(Part3)"
169,4,"e49c0c3cf63da306404b","coatly","半角スペースの有無だけでサーバーをぶっ壊しそうになった話"
138,4,"f83788bb45f72d733f43","debu-despot","アンケートサイトを作ったら一部の回答が漏れてた話"
36,0,"f45ddfd67a6fc8e347f8","k-nishigaki","firewall を再起動しただけなのに"
21,1,"5fee2f3a4fb3cede675c","kaizen_nagoya","「配電盤の電源を切る」"
44,1,"9ef3de92a98269fa154f","kimuradesu","転送中のファイルを削除してしまった私の真のやらかし"
81,1,"d7587afdba725b9e7e5f","kyntk","Lambdaの名前を変えただけなのに"
65,2,"362b57ea3e237c837ad6","mogmet","EC2をぶっ飛ばしてデータが全部消えてしまった話"
24,0,"284de4e4fbe84fca2f30","okaits","Linux From Scratchやってるときにやらかした話"
67,0,"604032bf179b6f3b77b6","ramen_2ro","エラーハンドリングをミスって大事故に"
29,1,"3b64a5e9f14b1f0728b8","real_yaruo","テキトーに要件定義した給与システムを使ったら給料が2倍になったり0になった話"
35,1,"0c91fad34e286edf0f70","sakai00kou","ログが1か月間出力されていなかった話"
33,0,"60acf73083732295cb05","sevenc-nanashi","DiscordBot - 設定を無視して一斉Banしてしまい色々大変なことになった話"
15,0,"d9cf14c5a8a30a43bff2","sho7650","デフォルトを信用すると、将来苦しい思いをする話"
386,7,"93fc0cff06c496866f73","tosiooooooo","さばぴょい伝説"
18,0,"d90493d8d58bdb97693d","umeyon","テストメールを多数のアドレスに送ってしまった話"
38,0,"b2466d9190a9f75472c3","yuta-ron","本番環境をサービス提供不能にしてしまいCDパイプラインを重視するようになった話"
17,0,"a6166f9bb92eaf33a756","mogmet","Firebaseを使った本番環境で誤ってユーザデータを削除してしまったお話"
20,0,"1716d89bd70709c89981","nozomu0321","あ、この現象、本やらアドカレで見たところだ!"
7,1,"c82a56aa22e26015938a","oishi-d","夜間作業で本番環境にログインできない!+SES事業者としての受入体制"
60,6,"244845eb86dcc41e7bbf","rompasinai","某年齢制限サービス不具合で全ユーザー向け(?)に本名が晒されました。"
27,1,"54d31810a5bf853154a2","sakai00kou","タイムリープして本番リリース直前のBIG-IPをぶっ壊した話"
15,1,"781b18248f7dee6f2007","sky_t","お客様のデータを先祖返りさせた話"
22,2,"f70f5de5264b6864b574","tooooofu24","Ubuntuをアップグレードしただけなのに..."
76,2,"326150e874dbad694549","tosiooooooo","SHIPPAY✕GAICHU"
73,3,"386f182996fe2f1ccaf0","wancom","「接続できないって言われたんだけど?」「😱😱😱」"
4年分で67件の結果が出ました。途中に ,,,,
という行がいくつか含まれてしまいましたが、これは削除された記事のようでした。ちょっと残念です。ともかくこの結果を使ってランキングを出してみます。
いいね数ランキング TOP 10
CSV の1番目に置いた、いいね数でソートします!
$ sort -t, -k1 -nr result.txt | head -10
2500,50,"bc03f6522dd20969f481","dala00","さよなら本番サーバー"
2009,21,"8aab477b4d64c7e7610e","raki","crontab database ~君がしでかしてくれたもの~"
1454,16,"d6906a185109e66df74f","2gt","いつものように本番作業してたはずなのに"
917,9,"181052eb85b686783806","HirotoKagotani","管理者用初期化URLを踏んでWebサービスのデータをふっとばした話"
550,21,"c0c0feda4e7a8030346f","NACK","cron哀歌~typoを笑うものはtypoに泣く~他"
421,5,"dad1e37ee4127ea7132b","kappa0923","AWSでやらかして3桁万円請求された話"
412,10,"8f4e4834c508052d2ddf","madai0517","データ移行をしただけなのに…(起こってしまったメール誤配信)"
386,7,"93fc0cff06c496866f73","tosiooooooo","さばぴょい伝説"
381,6,"0d80919d98696537ae10","yumamura","VSCodeの操作ミスでGCP Cloud Composerの裏側k8sをお掃除した話"
351,1,"5565332c748812f384b9","moriya_snj","稼働中の商用ネットワークでVRRPの切替検証を実施しちゃった話"
いいね数スゴすぎますね。なんと2019年の1日目と2日目と3日目で TOP 3 を独占でした。「本番環境でやらかしちゃった人 Advent Calendar」リリース当初の衝撃が思い出されます。
コメント数ランキング TOP 10
CSV の2番目に置いた、コメント数でソートします!
$ sort -t, -k2 -nr result.txt | head -10
2500,50,"bc03f6522dd20969f481","dala00","さよなら本番サーバー"
323,40,"08c54072e0d9d0734ccd","potyamaaaa","筋肉マージは辞めよう"
550,21,"c0c0feda4e7a8030346f","NACK","cron哀歌~typoを笑うものはtypoに泣く~他"
333,21,"b2fe3d96dc048ff44207","maip0902","本番環境でsudo yum updateしてサービスが動かなくなった"
2009,21,"8aab477b4d64c7e7610e","raki","crontab database ~君がしでかしてくれたもの~"
1454,16,"d6906a185109e66df74f","2gt","いつものように本番作業してたはずなのに"
316,11,"3069e79038c961458ba2","sadnessOjisan","思いもよらないものをnpm publishしてしまった話(前任者の顔写真など)"
412,10,"8f4e4834c508052d2ddf","madai0517","データ移行をしただけなのに…(起こってしまったメール誤配信)"
917,9,"181052eb85b686783806","HirotoKagotani","管理者用初期化URLを踏んでWebサービスのデータをふっとばした話"
97,8,"b952df76beeb9aaf2f3d","noritakaIzumi","Crontab を別のユーザのもので上書きしてしまった話"
順位と顔ぶれに少し変化がありました。コメントが多くつく記事というのは、少し傾向が違うのかもしれません。ほんとにどれも、読んだだけで冷や汗が出ます。
おわりに
ここまで読んでくださった方はどうもありがとうございました!
私もいつか何か書きたいと思っていたところ、今年は本家「本番環境でやらかしちゃった人 Advent Calendar」の作成者の方はカレンダーの2023を追加されなかったようなのですが、これをリスペクトする有志の方による新しいカレンダーが立ち上がっているようです。
私もこちらに 胃がひっくり返るような思いをした話 について記事を投稿して、カレンダーを盛り立てていければと思っています。思い出すだけでもヒヤヒヤしますが、頑張ります!
あと、ツイキャスを運営するモイ株式会社では、本番環境でやらかしちゃった経験のあるエンジニアを積極採用中ですので、よかったらぜひよろしくお願いいたします