Hateburo: kazeburo hatenablog

SRE / 運用系小姑 / Goを書くPerl Monger

買ってよかった2024

毎年恒例

kazeburo.hatenablog.com

フルリモートを前提としていますが、新宿の支社まで出勤したり出張も何回かあったのでイヤホン新調

悩みに悩んでTechnicsのEAH-AZ80にした。これまで使っていたのがANKERの2019年のモデルだったので隔世の感あり。耳じゃなくて頭の中でクリアな音が鳴る。いい買い物をした。

ただ、一度洗濯機で洗ってしまい充電ケースだけ買い直した。本体は無事だった。充電ケースだけ買えてよかった。Panasonicありがとうございます。

ec-plus.panasonic.jp

会社に行ってリモート会議していると一つ困るのがMacbook Proの高さ。ティッシュ箱が高さちょうどいいのだが不安定。

そこでノートパソコンのスタンドを購入

買ったのはUGREENの折りたたみ式スタンド。安定感抜群だし重さも220gとのことで持ち運びの苦にならない。おすすめ

出社しないときの格好は楽な方がいいわけで、服が基本ユニクロになっていく現象が起きている。

夏はドライEXクルーネックTシャツが着やすかったので、同じものを2着購入。

www.uniqlo.com

秋からは長袖のエアリズムコットンクルーネックTシャツをよく着ている。

ランニング

去年に引き続き今年もハーフマラソン2回、フルマラソン1回、会社のチームで駅伝大会に参加できた。

1月のハイテクハーフマラソンでは90分を切れた。万歳!

秋の横浜マラソンでは30km過ぎからだいぶバテたけど去年よりも20分記録更新!

靴はHOKAのクリフトン9とPUMAのディヴィエイト ニトロ3

ディヴィエイト ニトロ3は横浜マラソンの練習会で試し履きさせてもらって気に入ったので購入。靴選びこういうチャンスがないとムズカシイですね

クリフトン9は普段履き兼ジョグシューズ。YAPC::Hakodateはこれを履いて参加して、ついでに15kmほどランニングできた。出張ランまたチャンスがあればやりたい。

今年のランニング

1年間で合計1821km走れた。毎月100km以上は走っていて、9月には200km、12月も180km頑張った。

平日は仕事始めるまえに7から9kmほど。休日は少し長めに走る時間を確保させてもらってた。

より良いプロダクトをよりよく開発を行うためにはプロダクトビジョンを磨いていく必要があるんじゃないかということで読んだのがこれ。今年読んだ本の中ではおすすめ上位

バーレイザーやWorking backwordsなど話として出すことはあれど読んだことがなかった。結構分厚いがストーリーがあって読みやすい

データベースの運用はマネージドサービスを使うことが多くなり、内部で動いていることを知るチャンスは少なくなる最近ですが、この本をざっと読み、常に机の近くに置いておけば必ず役にたつ、というMySQLの大事なことが本当に盛りだくさんで書かれています。

サイバースペース地政学はぱっと読める。石狩データセンターもでてくるよ

来年は何買うかな~

さくらインターネットではこれからのクラウドをつくるインフラエンジニアを募集しています

ひとつ前の記事では主にソフトウェア開発のポジションを紹介しました。

kazeburo.hatenablog.com

クラウドの開発、GPUを活用した生成AIクラウドサービスの実現にはソフトウェア開発のエンジニアだけではなく、サーバやネットワークの設計開発と運用、石狩をはじめとするデータセンターでの運用を行うエンジニアも結集して取り組むことが必須です。

この記事ではさくらインターネットのインフラを支えるエンジニアのインタビュー記事やオープンしているポジションを紹介します

クラウド基盤

「さくらのVPS」「さくらのクラウド」のインフラを担当する基盤ユニットのメンバーのインタビュー。

sakumaga.sakura.ad.jp

sakumaga.sakura.ad.jp

基盤ユニットでは仮想化ホストとなるサーバ、ネットワーク機器、ストレージなどのクラウドの基盤の設計、開発、運用を行います。

www.sakura.ad.jp

次世代ネットワークインフラ

現在は次の10年を支える次世代ネットワークインフラの設計、実証に取り掛かっています

www.sakura.ad.jp

このプロジェクトに参画しているのはネットワークエンジニアの土屋さんです

sakumaga.sakura.ad.jp

GPU・生成AI向けクラウドサービスのインフラ

GPUをはじめとする大規模計算リソースの基盤構築を行い、AI技術の進化を支えます

www.sakura.ad.jp

JANOG54 Meetingで生成AI向け基盤の構築を紹介しています。

knowledge.sakura.ad.jp

上記の発表を行ったうちの一人、井上喬視さんのインタビュー記事

sakumaga.sakura.ad.jp

高火力PHYに加えて、高火力DOKや生成AI向けサービスなどのプロジェクトを統括する角さんのインタビュー

sakumaga.sakura.ad.jp

サーバエンジニア・データセンターオペレーション

さくらインターネットでは石狩、東京、大阪にデータセンター拠点をおいています。データセンターで勤務しながら、国内最大級のインフラサービスのサーバ構築・保守・運用はもちろん、業務用ツールの開発や業務設計・運用ドキュメントの作成等、幅広く業務を担当していただくのがサーバエンジニア(データセンターオペレーション)です。

www.sakura.ad.jp

データセンター運用を行うチームに属しながら、幅のひろい活躍をしているメンバーもいます。

sakumaga.sakura.ad.jp

ここにあげたポジションも多くあります。カジュアル面談もありますので、ぜひ興味をお持ちの方はご応募いただけると嬉しいです。さくらインターネットでは日本のクラウドサービスを継続に開発し、より進化させるため今後ともさまざま取り組んでいきます

www.sakura.ad.jp

新卒採用もやっています

www.sakura.ad.jp

さくらのTech Dayで喋ったこと、さくらのクラウドの開発の取り組みについて

この記事は「さくらインターネット Advent Calendar 2024」の1日目の記事です

qiita.com

明日からも記事がドシドシあがるのでお楽しみにしてください。

さくらのTech Day開催しました

さくらインターネットではお昼時に「さくらのテックランチ」というオンラインイベントを数ヶ月に一度程度のペースで開催してきました。こちらはさくらのエンジニアが技術について好きに話すというテーマのもと、クラウドの基盤やGPU、開発組織などをテーマにやっています。さくらのTech Dayはさくらのテックランチの拡大版ということで、11/12の午後3時からオンライン開催しました。

sakura-tokyo.connpass.com

当日は多くの方にみていただき、ありがとうございます。アーカイブも公開されていますので、気になる方はぜひご覧ください

さくらのTech Dayで喋ってきた

ガバメントクラウドに向けた開発で江草さんと一緒に話をさせて頂きました。江草さん発表の機密コンピューティングの話も非常に興味深かったのでぜひアーカイブをご覧になっていただけると嬉しいです!

speakerdeck.com

スライドはYAPC::Hakodateの際のスライドを若干アップデートしたものになります。

ガバメントクラウドに向けた開発の取り組み

2024年11月現在、開発チームは4つのストリームアラインドチーム、SRE室、技術開発グループのあわせて6チームに分かれて開発をしています。

APIやデータベースサービス、IaaSの基盤などそれぞれのチームが担当をもち、並列での開発ができるよう進めています。

加えて、エンジニアリングマネージャの体制も強化しております。EMとして入社して頂いた澁谷さん、後藤さんがそれぞれブログ記事を書いてくださっています。

note.com

note.com

開発者の人数が増えていく中、1on1や各チームのミーティングに参加するなどし、課題を抽出、言語化し、開発組織の目指す姿を議論しています。

ガバメントクラウドに向けた開発の進捗についてはデジタル庁のホームページでも公開されています。

www.digital.go.jp

実際の開発にあたり当初の予定から順序を変更しているものがありつつ、開発全体に影響のある遅れはないと状況となります。

もう一つ、今回追加しているスライドが開発の主なトピックです。

この中でも取り上げているαチームによるリリースが11/28にもありました。

www.sakura.ad.jp

この開発においては、さくらのクラウドが開始されて以降開発が続けられてきた既存のAPIを丁寧に確認し、機能の追加を行なったということで大変な偉業であったかと思っています。

こうした基盤となるシステムでは、ニュースリリースはないものの、長く使われてきたIaaSのコアとなるシステムの改善を行っています。Go言語へのリプレイスや設計の見直しを通して信頼性、スケーラビリティの課題をひとつひとつ前に進めております。

既存システムの保守、ガバメントクラウドの技術要件に沿った機能追加でも技術的なチャレンジは多くあり、エンジニアが解くべき良い課題が多々あると考えています。

クラウドをつくるエンジニアを募集しています

開発はスケジュールに沿って行われていますが、今後も行なっていかなければならない機能追加は多くあります。一度開発し、リリースした機能、サービスもお客様のやりたいことを出来るに変えれるようにするため、開発を継続する必要があります。チームに入り、クラウドを作るエンジニアを、仲間になっていただけるエンジニアを募集しています

www.sakura.ad.jp

さくらのクラウドを開発するソフトウェア開発エンジニアのポジションはいくつかあり、APIやIaaSの基盤を扱うエンジニアの他、特定の技術領域に特化したポジションもオープンしています。

ソフトウェア開発エンジニア(監視プラットフォーム)

さくらのクラウドのモニタリングサービス(メトリクス、ログ、アラート、ダッシュボード)を開発し、社内の監視システムの移行やパブリックサービスとしての運用、改善を担当します

www.sakura.ad.jp

リレーショナルデータベースサービス開発エンジニア(バックエンド)

さくらのクラウドにおけるリレーショナルデータベースサービス(RDS)の開発を行います。リレーショナルデータベースに関する知見を高め、クラウド基盤との連携を通し、お客様のDXを支える高い信頼性を実現し、価値提供に繋げることがミッションとなります。

www.sakura.ad.jp

ソフトウェア開発エンジニア(Kubernetes基盤)

さくらインターネットにおいてKubernetesは複数のチームでサービスの基盤として活用されています。Kubernetesを活用し、クラウドに最適化されたアプリケーション実行基盤を作ることがミッションとなります。さくらのクラウドに加えてGPUサービスもその領域になります。

www.sakura.ad.jp

ソフトウェア開発エンジニア(クラウドSDK)

さくらのクラウドではusacloudTerraform Providerといった公式ツールを提供しています。顧客がアプリケーションからさくらのクラウドの各サービスにアクセスするときに使用するSDK、ツールキットなどの開発とメンテナンス、利用促進をおこなうOSSの開発を行います。

www.sakura.ad.jp

ソフトウェア開発エンジニア(バックエンド)

APIやコントロールパネル、IaaSの基盤となる物理機器との連携を行うバックエンドシステムの開発、改善を行うのがバックエンドチームです。インフラ開発エンジニアや特定の領域の問題解決を行うエンジニア、SRE室と協力しながら新しい価値を創造し、お客様、社会のDXを支えることをミッションとしています。

www.sakura.ad.jp

取り組みについてもう少し詳しく聞きたいなど、カジュアル面談も対応できますのでご連絡をいただけると嬉しいです。

SRE室について

さいごにSRE室について。

さくらインターネット クラウド事業本部にてSRE室という組織を2022年7月からやっています。すでに立ち上げてから2年5ヶ月となります。ミッション、ビジョン、バリューや取り組んでいたことは山本さんのブログ記事でも紹介されています。

febc-yamamoto.hatenablog.jp

2年経ち、現在は開発組織がガバメントクラウドにむけた開発に集中しており、SRE室も機能の開発を担当しています。最近の業務としては

  • ガバメントクラウドに向けた新規機能の開発
    • モニタリングサービス(ログ、メトリクス、アラート、ダッシュボード)
    • メッセージキュー
  • さくらのクラウド ロードバランサ・VPCルータなど既存のサービス開発運用
  • CI/CD環境、社内k8sの運用開発
  • OSS(usacloud、Terraform)の開発
  • チーム体制・開発支援
    • チームビルディング
    • CI/CD、IaC導入
  • ソフトウェア開発エンジニア採用
  • Tellus、高火力などの開発参加

このように広がり、当初のSREとして目指したものから大きく変わっています。この変化と成長をしてきた話と、開発のチャレンジを支え、仕組み化するSREをどのように実践していくのかを、来年1月のSRE Kaigiでお話しできればと考えています。

fortee.jp

また、上記の業務のキーワードに興味を持った方がいましたらご連絡いただけると嬉しいです。カジュアル面談しましょう。よろしくお願いします。

YAPC::Hakodate 2024 参加してきました

2週間ほど経ってしまいましたが、2024-10-04〜05に開催された YAPC::Hakodate 2024 に参加してきました。 前回広島は参加できずでしたので、久しぶりで楽しかったです。関係者の皆様、参加者の皆様ありがとうございます!

喋ってきた

前夜祭rejectconでトークの機会をいただいたので、ここ2年ぐらいのチャレンジを紹介させて頂きました

speakerdeck.com

このトークをきっかけに、ガバメントクラウドクラウドに向けた開発や、チームを大きくしていくための取り組みについて様々な方とお話しできました。本当に感謝です。

函館行ってきた

函館は夏に家族旅行で行っていたので、今回2回目

初日は午後の飛行機だったので、函館空港から直接前夜祭の会場入り

2次会は帰りのタイミングが一緒だったFindyさん、helpfeelさんなどなどのグループに混ぜてもらって飲んだり食べたり。これも楽しかった。

本編の日はいい天気

ホテルは函館駅前のプレミアホテル。コナンの映画の聖地でもある

前日夜も食べたのに朝ごはん食べすぎたのでお散歩

本編中の半分ぐらいは弊社さくらインターネットのブース周りをうろうろ。ブースに来てくれた方とお話ししたり、通りがかりの人に話かけたり。

お隣のブースのカケハシさんだったので、941さん、小田中さん(お話しするのは初!)、ogijunさんともいろいろ話せた。

公式懇親会が凄すぎた。toggleさんまじ感謝

3日目は、これもやりたかったことの1つである旅先でのランニング fujiwaraさん、nagayamaさんは函館山登っていてすごい。

立待岬、谷地頭、函館公園函館どつく前、最後にともえ大橋を往復して15km。 ともえ大橋は横浜マラソンの後半の高速道路上をイメージしながら走れた。天気も良くて最高。

走ったあとは、路面電車湯の川温泉まで移動して、日帰り温泉に使って完成してきた

オフラインイベントの体験をもっとたくさんのエンジニアに

いやぁ、ほんと行ってよかった楽しかった。都内や近場のカンファレンスももちろん良いのですが、地方のカンファレンスはまた違う楽しさがあります。もっとたくさんの若手のエンジニアが積極的に参加するよう、背中を押していきたい

行かせてもらった家族にも感謝です。ありがとう。

買ってよかった2023

年末恒例

kazeburo.hatenablog.com

今年は家を買いました。

新築マンションですが、早い時期から相談していたのでオーダーメイドで間取りを変更して、書庫をつくりました。

仕事机まわり

いくつかのプロジェクトで出社しての打ち合わせが多い時期もあったけど、今年も基本リモートワーク。

引っ越して机も買い替えで KANADEMONOさんの リノリウム天板にした

kanademono.design

机の下にはサンワダイレクト ケーブルトレーを設置している。横90cmあるのでいろいろ収められて便利

モニタは Intehill のモバイルモニター U16NAってのものを買った。

16" 4K+ Monitor with P3 98% and 10Bit Color [U16NA]www.intehill.com

そこまで机が広くないところにMacbook 2台置いているので小さめのモニタを探していた。これは16インチとモバイルサイズでありながら画面が16:10の解像度が4Kより少し縦が長い3840x2400でエクセル作業も楽。

しかも、背面が平らな綺麗なデザインでVESAマウント用の穴付き。

ただし、今は入手が不可能になっている。残念

www.amazon.co.jp

アームはコンパクトに畳めそうなもので安価なものを選択

ミーティング用のカメラは蟹バサミクランプで固定

クランプは短いものを使っているのでカメラに対して斜めに顔が映るので、真っ直ぐにも対応ができるようもう少し長いやつに買い換えようかなと思ってる。

会社用Macbook Proからモニタ、カメラ、キーボード、電源とケーブルがたくさん生えていると移動の時に不便なので、Thunerbolt 4の Hub を購入

机の下のケーブルトレーに設置して、Macとはケーブル一本。トラブルなどもなく動くし、スッキリしたのでよかった

ランニング

今年はハーフマラソン2回、フルマラソン1回、会社のチームで駅伝大会に参加できた。

記録はこんな感じ

  • 1/8 ハイテクハーフは 1時間32分
  • 4/16 東日本国際親善マラソン(ハーフ) 1時間36分
  • 10/29 横浜マラソン(フル)は4時間2分
  • 11/19 日本ITチャリティ駅伝(3km) 11分35秒

フルマラソンはまたチャレンジしたいところ!

靴は2足購入

前半が ASICS SUPERBLAST

これで横浜マラソンも走った

後半は ASICS MAGIC SPEED 3

初カーボンプレート入りシューズ。今年も走ったハイテクハーフをこのMAGIC SPEED 3で走る予定。

MAGIC SPEED 3はすごく軽いと感じていて、たまにSUPERBLASTに戻すと、柔らかくて跳ねるけど重いという感覚がある。

フルマラソンを走ったときに、Apple Watchの電池が最後までもたなかったのでApple Watch Ultra 2を購入。シリーズ6からの買い替えでした。ガーミンと悩んで末に、走る時と普段と使い分けるのが面倒だということでこちらに

初見デカっ!と思ったけど、重さは気にならないし画面綺麗だし、まじ電池が長持ちでいい。今年一のライフチェンジングアイテムだった

12月のここまでの走行距離はこんな感じ

去年のエントリからすると1kmあたり14秒ほど速く走ってますね

ポンポさんはNHKで映画を観てから漫画を購入。映像研も好きな漫画ですが、作品をつくる人のストーリーは良い

会社で読書会でふりかえりガイドブックを扱った。同じシリーズのスクラムブートキャンプアジャイルプラクティスガイドブックもおすすめです

さくらインターネットではITパスポートを取得することが推奨されているので勉強しました。プロジェクト管理などの用語はスコシヤクニタッテイル

個人的な今年のおすすめ技術系の本は GitLab本、スタッフエンジニアです。

GitLab本は年明けから読書会をやっていく予定なので楽しみ。チームのパフォーマンスをあげてさくらインターネットで今取り組んでいる大きな2つのプロジェクトをやっていきたい

来年は何買うかな~

ISUCON13のベンチマーカーのDNS水責め攻撃について

この記事はさくらインターネット Advent Calendar 2023の12月3日の記事になります。

先日行われました ISUCON13 の作問を担当しました。参加者の皆様、スタッフの皆様ありがとうございました。

このエントリではISUCON13のDNSに関わる要素とベンチマーカーから行われたDNS水責めについて紹介します。

ISUCON13の問題の講評と解説は以下のエントリーでも行っていますので読んでいただけると嬉しいです

isucon.net

こんいす〜

ISUCON13における名前解決

上記のエントリーにもある通り、今回のISUCONではDNSが問題の一部として出てきます。

これまでポータルから参加者は割り振られたサーバの中から負荷をかけるサーバ1台選択し、ポータルはそのサーバに対して負荷走行を行うことが多くありましたが、今回はサーバ1台を選択したら、ベンチマーカーはそのサーバの UDP/53 で起動しているDNSサーバ(PowerDNS MySQL backendを使用)に対して名前解決のリクエストを行い、得られた結果のIPアドレスに対して接続をし、負荷走行を行います。

もちろん、得られたIPアドレスが参加者に割り振られたサーバでなかった場合、エラーとなります。

ベンチマーカーの実装で利用しているGo言語の標準的なHTTPクライアントでは、TCP接続を行う関数を差し替えることができ、そこで独自の名前解決をいれています。

transport := &http.Transport{
    DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
        // ここに独自の名前解決を実装する
        return dialer.DialContext(ctx, network, addr)
    },
}
return &http.Client{
    Transport: transport,
}

名前解決にはGo言語標準の net.Resolver ではなく、レスポンスコードなどを確認するため、miekg/dns を使っています。

github.com

ISUCON13におけるDNS水責め攻撃

ISUCON13でDNSを導入した狙いの一つがDNS水責め攻撃を負荷走行中に再現することでした。さくらインターネットでもDNS水責め攻撃をうけてさまざまな対策をしています。実際の攻撃や対策の内容は以下のスライドを参考にしてください。

speakerdeck.com

当日のマニュアルにもDNSへの攻撃は記載しています。

負荷走行中、DNSサーバに対していわゆる「DNS水責め攻撃」が行われます。 DNS水責め攻撃はランダムなサブドメインを生成し、大量のアクセスを行うことでDNSサーバの応答に影響を与えることを目的とする攻撃手法です。 ベンチマーカーはDNS水責め攻撃およびスクレイピングを行い、名前解決ができると HTTPS によりアクセスを試みる動作を行います。失敗(NXDOMAINや応答なし)では来ません。

ISUCON13のDNS水責め攻撃はスコアに影響をしない、負荷走行中のサブ要素となっています。あまりにDNS名前解決やスクレイピング的な動作が負荷とならないよう、最大 3000 QPS以上にならないようにしてありました。また、一定のパフォーマンスを満たしている状態で徐々に並列数が上がるようにしてあるため、負荷が上がりすぎず、負荷走行中には通常の名前解決と合わせて最大15万から18万クエリ程度が最大だったと思われます。

DNSおよびDNS水責め攻撃への対応

ISUCON13におけるDNSへの対応ですが、以下のものを想定していました

  1. MySQLスキーマでわざと消してあるインデックスを追加
  2. DNSをアプリケーションのホストと切り離す
  3. TTLおよびPowerDNSのチューニング
  4. MySQLからBINDゾーンファイルバックエンドに切り替え、ワイルドカードレコードを設定
  5. DNSサーバの自作、dnsdistや自作DNSサーバで水責め攻撃の応答を遅延させる

1. インデックスの追加

データベースのログ解析、プロファイリングを行なったチームはインデックスが不足しているのを見つけることができたのではないかと思います。

ISUCON13の初期実装では全体的にインデックスが「ない」状態ですが、こちらもインデックスも欠けています。レコードを管理するrecordsテーブルにおいて、name に対してあるべきインデックスがなく、作成する必要があります。

CREATE TABLE records (
  id                    BIGINT AUTO_INCREMENT,
  domain_id             INT DEFAULT NULL,
  name                  VARCHAR(255) DEFAULT NULL,
  type                  VARCHAR(10) DEFAULT NULL,
  content               VARCHAR(64000) DEFAULT NULL,
  ttl                   INT DEFAULT NULL,
  prio                  INT DEFAULT NULL,
  disabled              TINYINT(1) DEFAULT 0,
  ordername             VARCHAR(255) BINARY DEFAULT NULL,
  auth                  TINYINT(1) DEFAULT 1,
  PRIMARY KEY (id)
) Engine=InnoDB CHARACTER SET 'latin1';

ALTER TABLE records ADD INDEX idx_name (records);

PowerDNSのMySQLバックエンドのスキーマはこちらでも確認できます。

doc.powerdns.com

2. DNSを別サーバに移動する

今回のベンチマーカーではDNSから返すIPを変更することで負荷走行を行うサーバを変更できるので、それを利用してDNSサーバとアプリケーションサーバを別のサーバにする構成変更ができます。制限されているとはいえ、3000 QPSはそれなりの規模であり、負荷を分散する意味は十分にあります。

上記のインデックスと構成変更を行うことで一旦はDNSについては傍においておけるようになっていたのではないかと思います。

3. TTLとPowerDNSの設定

DNSは分散システムであり、キャッシュを適切に利用することで成り立っているシステムです。ところがISUCON13の初期状態ではまったくキャッシュをしないように設定しているため、これを変更するだけでもサーバに対する負荷は削減できます。

(なぜキャッシュを無効化していたかは、DNSの反映を可能な限り高速にするためというストーリーと考えていただけると幸いです)

PowerDNSのゾーン情報はアプリケーションの初期化フェーズでゾーンファイルから読み込まれるので、ここのTTLを変更します。

diff --git a/webapp/pdns/u.isucon.dev.zone b/webapp/pdns/u.isucon.dev.zone
index bd387a1..e1fa270 100644
--- a/webapp/pdns/u.isucon.dev.zone
+++ b/webapp/pdns/u.isucon.dev.zone
@@ -10,7 +10,7 @@ $TTL 3600
 @        0 IN NS ns1.u.isucon.dev.
 @        0 IN A  <ISUCON_SUBDOMAIN_ADDRESS>
 ns1      0 IN A  <ISUCON_SUBDOMAIN_ADDRESS>
-pipe     0 IN A  <ISUCON_SUBDOMAIN_ADDRESS>
+pipe     60 IN A  <ISUCON_SUBDOMAIN_ADDRESS>
 test001  0 IN A  <ISUCON_SUBDOMAIN_ADDRESS>
 
 www              0 IN A  <ISUCON_SUBDOMAIN_ADDRESS>

マニュアルでもDNSの動作としてTTLを設定すれば、適切にキャッシュされるとしていました。

pipe.u.isucon.dev が主にベンチマーカーからアクセスされるドメインであり、これだけTTLを長めに設定すれば十分な効果が得られます。ただ、DNSラウンドロビンなどDNSによる負荷分散を行う場合、この設定をしてしまうと狙った効果が得られなかったかもしれません。

また、PowerDNSの設定ファイル /etc/powerdns/pdns.conf においてもデフォルトから意図的に変更されているものがあり、いくつか変更した方がいい項目があります。

negquery-cache-ttl=0
query-cache-ttl=0

あたりは設定しておいても良さそうです。PowerDNSのパフォーマンスチューニングについては以下のドキュメントが参考になります。自分もよくみました。

doc.powerdns.com

PowerDNSの設定などはデフォルトであるべきものがなかったりと、インフラよりのやや謎解き要素になっていたかもしれません。

4. BIND zone file backendとワイルドカード

さくらのクラウドではDNS水責め攻撃の対策として、MySQL backendからBIND zone file backendに切り替えています。

水責め攻撃ではキャッシュは有効に働きませんので、MySQL backendではDNS問い合わせの都度SQLが発行されてしまい、パフォーマンスに大きな影響がでます。一方BIND zone file backendでは、静的な設定ファイルを読み込むことでPowerDNSのメモリ内だけで処理を行うので、レスポンスが高速になることが期待できます。

ただ、BIND zone file backendでは動的にレコードを追加変更することができません。今回の問題ではユーザが増えるたびにDNSのレコードを追加する必要があるので、このままでは使えません(ユーザ追加の都度ゾーンファイルを読み込み直す対応はあったようです)。

そこで、DNSレコードとして、ワイルドカードレコードを追加しておくことで、ユーザ追加ごとにDNSレコード操作の必要をなくすという手が考えられます。

diff --git a/webapp/pdns/u.isucon.dev.zone b/webapp/pdns/u.isucon.dev.zone
index bd387a1..55a2fe4 100644
--- a/webapp/pdns/u.isucon.dev.zone
+++ b/webapp/pdns/u.isucon.dev.zone
@@ -10,8 +10,9 @@ $TTL 3600
 @        0 IN NS ns1.u.isucon.dev.
 @        0 IN A  <ISUCON_SUBDOMAIN_ADDRESS>
 ns1      0 IN A  <ISUCON_SUBDOMAIN_ADDRESS>
-pipe     0 IN A  <ISUCON_SUBDOMAIN_ADDRESS>
+pipe     60 IN A  <ISUCON_SUBDOMAIN_ADDRESS>
 test001  0 IN A  <ISUCON_SUBDOMAIN_ADDRESS>
+*        60 IN A  <ISUCON_SUBDOMAIN_ADDRESS>

ただこれに行うと、DNS水責め攻撃のクエリに対してもIPアドレスが返ってしまい、マニュアルにもあるHTTPSによるスクレイピング(という設定の余計なアクセス)が発生し、アプリケーションが十分に高速化されていないとかえって負荷になるという状態になります。

5. DNSサーバの自作、あるいは意図的な遅延

DNS水責め攻撃はDoS攻撃手法の一種です。大量のクエリを投げつけて相手のサービスに影響を与えることが狙いだとされています。

水責め攻撃への対応としては攻撃を受け付けるだけの容量、パフォーマンスを備えるというのもありますが、検知してレートリミットをかけたり、フィルタリングする対策も当然あります。

ISUCON13の水責め攻撃でも検知しての対策ができます。名前解決が行われるドイメインは username.u.isucon.dev となっており、usernameが存在していなかった場合、それは水責め攻撃のクエリとみなして、遅延をいれるという手があります。

遅延をいれることで、水責め攻撃のスループットを落とすことでき、DNSサーバのみならず、ベンチマーカー側のCPU負荷をも抑えることができます。他の処理に回せるCPUが増え、結果として全体のスコアが上昇する可能性もありました。

遅延を実現する方法として以下のような方法が考えられます。

  • アプリケーションの中にDNSサーバを組み込み、アプリケーション中でユーザの存在確認をし、遅延をいれる
  • PowerDNSのリモートバックエンドを利用する。アプリケーション中にDNS名前解決用のAPIエンドポイントを追加
  • dnsdistをPowerDNSの前に置き、NXDOMAIN(ドメインが見つからない応答)の場合に遅延挿入

DNSproxyサーバであるdnsdistではさまざまな条件でリクエストやレスポンスに処理を加えることができますが、以下のように設定でレスポンスがNXDOMAINであった場合に遅延挿入が実現できます。dnsdistを起動する前にPowerDNSは1053ポートで動くように変更しておきます。

addLocal("0.0.0.0:53", {reusePort=true,tcpListenQueueSize=4096})
newServer({address="127.0.0.1:1053",useClientSubnet=true,name="backend1"})
addACL("0.0.0.0/0")
addACL("::0/0")

addResponseAction(
  RCodeRule(DNSRCode.NXDOMAIN),
  DelayResponseAction(1000)
)

DNSの遅延をいれることで、DNS水責めの回転数は圧倒的に落ち、名前解決数も減ります。もし、名前解決で使用するCPUがボトルネックになっている場合はスコアが上がるかもしれません。

さくらインターネットの企業賞の条件は、「DNS名前解決数の最も多かったチーム」であり名前解決のパフォーマンスも出しながら、スコアのアップも目指したチームへの賞とさせていただいています。

まとめ

ISUCON13のDNSに関わる要素とベンチマーカーから行われたDNS水責めについて紹介しました。今ではDNSサーバを運用する機会はほぼなく、今後ISUCONでDNSが題材にあがることはないかもしれませんが、DNSはインターネットの大事なコンポーネントであり、参加者の皆様の今後の開発のなんらかの参考になれば幸いです。

resolv.conf におけるDNS名前解決のタイムアウト

resolv.confに記述したDNSサーバへの名前解決のタイムアウトとリトライ回数について改めて確認したので忘備録

CentOS7系およびRocky Linux 9系で確認しています。

/etc/resolv.confDNSサーバが次のように指定されている場合、

nameserver 198.51.100.3
nameserver 198.51.100.4

以下の順で利用されます。

  1. 198.51.100.3タイムアウト 5秒
  2. 198.51.100.45秒 ✖️ 2 ➗ サーバ台数 (小数点切り捨て)のタイムアウト=5 秒

これを2回繰り返し、両方接続不可能な場合、合計20秒間名前解決しようとします。

3台ある場合、

nameserver 198.51.100.3
nameserver 198.51.100.4
nameserver 198.51.100.5
  1. 198.51.100.3タイムアウト 5秒
  2. 198.51.100.45秒 x 2 ➗ サーバ台数 (小数点切り捨て)のタイムアウト=3 秒
  3. 198.51.100.52. の倍のタイムアウト=6秒

これを2回繰り返し、5秒→3秒→6秒を2回の28秒間試行します。

3台以上DNSサーバがある場合は、4台目以降は無視されます。

タイムアウトoptions timeout:N 、試行回数は options attempts:M 指定できます。それぞれ一般的なデフォルトは 5秒2回 となります。

タイムアウトを短くして試行回数を増やすのであれば、次のようにするのが良さそうです。

nameserver 198.51.100.3
nameserver 198.51.100.4
options timeout:1 attempts:3

デフォルト5秒はWebサーバ用途では長すぎるかもしれません。

また、ubuntu 22.04などでは systemd-resolvedが動作しています。こちらではローカルでキャッシュをもっていたり、接続できないDNSサーバにしばらくのアクセスをしない機能があるようです。

straceで確認

2台の場合

11:56:02.580141 socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, IPPROTO_IP) = 4
11:56:02.580190 setsockopt(4, SOL_IP, IP_RECVERR, [1], 4) = 0
11:56:02.580237 connect(4, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("198.51.100.3")}, 16) = 0
11:56:02.580294 poll([{fd=4, events=POLLOUT}], 1, 0) = 1 ([{fd=4, revents=POLLOUT}])
11:56:02.580337 sendto(4, "tC\1\0\0\1\0\0\0\0\0\0\6example\2com\0\0\1\0\1", 30, MSG_NOSIGNAL, NULL, 0) = 30
11:56:02.580428 poll([{fd=4, events=POLLIN}], 1, 5000) = 0 (Timeout)
# 5秒
11:56:07.585638 socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, IPPROTO_IP) = 5
11:56:07.585726 setsockopt(5, SOL_IP, IP_RECVERR, [1], 4) = 0
11:56:07.585766 connect(5, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("198.51.100.4")}, 16) = 0
11:56:07.585807 poll([{fd=5, events=POLLOUT}], 1, 0) = 1 ([{fd=5, revents=POLLOUT}])
11:56:07.585837 sendto(5, "tC\1\0\0\1\0\0\0\0\0\0\6example\2com\0\0\1\0\1", 30, MSG_NOSIGNAL, NULL, 0) = 30
11:56:07.585924 poll([{fd=5, events=POLLIN}], 1, 5000) = 0 (Timeout)
# 5秒
11:56:12.591058 poll([{fd=4, events=POLLOUT}], 1, 0) = 1 ([{fd=4, revents=POLLOUT}])
11:56:12.591182 sendto(4, "tC\1\0\0\1\0\0\0\0\0\0\6example\2com\0\0\1\0\1", 30, MSG_NOSIGNAL, NULL, 0) = 30
11:56:12.591282 poll([{fd=4, events=POLLIN}], 1, 5000) = 0 (Timeout)
# 5秒
11:56:17.596483 poll([{fd=5, events=POLLOUT}], 1, 0) = 1 ([{fd=5, revents=POLLOUT}])
11:56:17.596563 sendto(5, "tC\1\0\0\1\0\0\0\0\0\0\6example\2com\0\0\1\0\1", 30, MSG_NOSIGNAL, NULL, 0) = 30
11:56:17.596629 poll([{fd=5, events=POLLIN}], 1, 5000) = 0 (Timeout)
# 5秒
11:56:22.601778 close(4)                = 0
11:56:22.601824 close(5)                = 0

3台の場合

12:14:57.572565 socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, IPPROTO_IP) = 4
12:14:57.572597 setsockopt(4, SOL_IP, IP_RECVERR, [1], 4) = 0
12:14:57.572626 connect(4, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("198.51.100.3")}, 16) = 0
12:14:57.572682 poll([{fd=4, events=POLLOUT}], 1, 0) = 1 ([{fd=4, revents=POLLOUT}])
12:14:57.572731 sendto(4, "\366\237\1\0\0\1\0\0\0\0\0\0\6example\2com\0\0\1\0\1", 30, MSG_NOSIGNAL, NULL, 0) = 30
12:14:57.572795 poll([{fd=4, events=POLLIN}], 1, 5000) = 0 (Timeout)
# 5秒
12:15:02.577947 socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, IPPROTO_IP) = 5
12:15:02.578009 setsockopt(5, SOL_IP, IP_RECVERR, [1], 4) = 0
12:15:02.578045 connect(5, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("198.51.100.4")}, 16) = 0
12:15:02.578101 poll([{fd=5, events=POLLOUT}], 1, 0) = 1 ([{fd=5, revents=POLLOUT}])
12:15:02.578138 sendto(5, "\366\237\1\0\0\1\0\0\0\0\0\0\6example\2com\0\0\1\0\1", 30, MSG_NOSIGNAL, NULL, 0) = 30
12:15:02.578216 poll([{fd=5, events=POLLIN}], 1, 3000) = 0 (Timeout)
# 3秒
12:15:05.581337 socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, IPPROTO_IP) = 6
12:15:05.581406 setsockopt(6, SOL_IP, IP_RECVERR, [1], 4) = 0
12:15:05.581443 connect(6, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("198.51.100.5")}, 16) = 0
12:15:05.581484 poll([{fd=6, events=POLLOUT}], 1, 0) = 1 ([{fd=6, revents=POLLOUT}])
12:15:05.581512 sendto(6, "\366\237\1\0\0\1\0\0\0\0\0\0\6example\2com\0\0\1\0\1", 30, MSG_NOSIGNAL, NULL, 0) = 30
12:15:05.581568 poll([{fd=6, events=POLLIN}], 1, 6000) = 0 (Timeout)
# 6秒
12:15:11.587696 poll([{fd=4, events=POLLOUT}], 1, 0) = 1 ([{fd=4, revents=POLLOUT}])
12:15:11.587759 sendto(4, "\366\237\1\0\0\1\0\0\0\0\0\0\6sakura\2ad\2jp\0\0\1\0\1", 30, MSG_NOSIGNAL, NULL, 0) = 30
12:15:11.587830 poll([{fd=4, events=POLLIN}], 1, 5000) = 0 (Timeout)
# 5秒
12:15:16.592954 poll([{fd=5, events=POLLOUT}], 1, 0) = 1 ([{fd=5, revents=POLLOUT}])
12:15:16.593015 sendto(5, "\366\237\1\0\0\1\0\0\0\0\0\0\6example\2com\0\0\1\0\1", 30, MSG_NOSIGNAL, NULL, 0) = 30
12:15:16.593077 poll([{fd=5, events=POLLIN}], 1, 3000) = 0 (Timeout)
# 3秒
12:15:19.596238 poll([{fd=6, events=POLLOUT}], 1, 0) = 1 ([{fd=6, revents=POLLOUT}])
12:15:19.596327 sendto(6, "\366\237\1\0\0\1\0\0\0\0\0\0\6example\2com\0\0\1\0\1", 30, MSG_NOSIGNAL, NULL, 0) = 30
12:15:19.596422 poll([{fd=6, events=POLLIN}], 1, 6000) = 0 (Timeout)
# 5秒
12:15:25.602566 close(4)                = 0
12:15:25.602637 close(5)                = 0
12:15:25.602668 close(6)                = 0