「DNS浸透言うな」について整理

DNS浸透言うな」を知った経緯

レジストラホスティングサービスのDNSサーバからAWSのRoute53にDNSサーバを置き換える仕事があった。

その時DNSサーバ移行に際して気をつけることを調べていた時にちょうどGMOクラウドサポートのtwitterが炎上しており自分もよくわからなかったので調べた。

GMOクラウド_サポート on Twitter / X

DNS浸透言うな」の本質

DNS浸透待ちですって言ってるやつだいたいただの設定ミスだよね。

嘘ついてることになるし、DNSの理解が深まらないのでそう言うのやめようと言うこと。

DNSサーバ(権威サーバ)引越しの正しい手順」

基本的にDNSがよくわかる教科書の引越し手順を読めばいい。今回はレジストラの移行に伴うところだけ以下手順では追記している。 

1. ゾーンデータの移行

最初にDNSサーバ上で動くサービスを止めずに移行先DNSサーバ上で動くように移行を進める必要がある。

  1. ゾーンデータのレコードのキャッシュを短くする(86,400s -> 300s)
  2. ゾーンデータのレコードの向き先を移行先のものにする

上記2つの対応をすることで、既存のキャッシュが切れたタイミングでサービスの移行は完了する。

1.の対応をしないと並行運用期間が長くなり、メンテ時間内に収まらないといった不都合が起きる。

上記1.の例で言えばメンテ期間を3hもらっているとしたら、元のキャッシュだと1日古いサービスへのキャッシュが残ることになり間に合わない。よって、キャッシュを5分にして変更後1日経つのを待ってキャッシュ5分の設定が反映されるようにする。

※ 移行先レコードのTTLを短くしておくことで移行元に戻さなければいけない不具合があったときにすぐ戻せるようにしておくといい。

 

並行運用期間から完了までの時間

移行元ゾーンデータのTTL値に依存する。

 

2. レジストラを変更

権威サーバは移行元のままで、レジストラを変更する。

レジストラによっては2週間近くかかることもあるが、実際にはレジストラ移転の承認をしてもらってから2,3日で完了した。

 

移行申請してから完了までの時間

何度か経験のある人からしてもだいたい2,3日で完了するとのこと。

また移行元レジストラに承認してもらう必要があるので、コミュニケーションコストが+1週間程度かかった。

 

3. 権威サーバの移行

  1. 親子ともにNSレコードを変更する
  2. グルーレコードの設定を変更する

グルーレコード=サブドメインが親ゾーンと異なるネームサーバーを使用している場合に、そのサブドメインのネームサーバーのIPアドレスを指定するために使用される。

並行運用期間から完了までの時間

移行元権威サーバのNSレコードのTTL値 + TLDTTL値に依存する。

つまり「DNS浸透言うな」はどう言うことか

DNSサーバ移転にかかる期間は想定できるし、サービスを止めないように移行もできる。

DNSの返答がうまくいっておらず、DNS浸透がまだ完了してない?と思ったときは、以下の可能性が考えられる。

  1. TTLの設定を短くできておらず、新旧のサーバーにリクエストがまだランダムに振り分けられてしまっている。

  2. 移行元ゾーンデータの向き先が移行先になっておらず、移行完了後に削除されてもいない。この場合、ネームサーバが変わってもキャッシュが残り続けたまま、移行元の情報で上書きをし続けることでなかなか移行先を向いてくれないことがある。

    このようなケースは非協力なDNS運用者によって引き起こされることがある。

    ただ、GMOの炎上の件は移行元のDNS運用者もGMO自身だったこともスレッドで言及されており言い訳できない状況となっていた。

    S (ツイートはスレッド全体をご確認ください) on Twitter / X

勉強の参考にさせていただいたサイト

  • DNS設定の確認方法
    • 再帰検索要求」と「非再帰検索要求」を使い分ける

      非再起要求・再起要求

       

    • 各権威サーバの設定は非再起検索要求で確認。

    • 権威サーバかどうか(AAフラグがあるかつTTLがカウントダウンされてないことを確認)

    • キャッシュサーバの応答確認は再起検索要求で確認。

ドメイン移管作業から学んだこと

ドメイン移管とは?

ドメイン移管という言葉は曖昧で、最初この作業を始めるときに知識がない状態の自分が使っていた言葉だが、きちんと意味わけすると2つの意味があった。

  1. ネームサーバの変更
  2. レジストラの変更

今回は結論から言うとレジストラの変更作業を行なった。

ネームサーバの変更とは

ネームサーバは特定のドメインDNS管理を行うサーバのことで、自由に設定できる。

AWS Route53のネームサーバを使えばそこのネームサーバの機能が使えるし、お名前.comのネームサーバを使えばそこの機能が使える。

レジストラの変更とは

レジストラとはドメイン管理業者のことであり、ドメイン所有者の代わりにドメインの管理を行う。レジストラになるには申請が必要で以下サイトで一覧確認できる程度には事業者は少ない。

指定事業者一覧 | JPドメイン名の登録 | JPRS

つまり、ドメインの管理事業者を変更すると言うこと。

なぜレジストラの変更が必要だったか

  1. 先方の用意したドメイン管理サービスではZone Apexが使えないが、Zone Apexでドメインを使えるようにしたいと言う要望だった
  2. 先方の用意したドメイン管理サービスではネームサーバの変更がポリシー上許容されていなかった。よってRoute53のネームサーバを設定するだけと言うわけには行かなかった。
  3. 管理されているドメインはそのサービス経由で取得されたこともあってレジストラがそのサービス会社となっていた。

Zone Apex・・・example.comなどwww.example.comのような~.がつかないドメインのこと。以下記事にもあるようにRoute53のエイリアスレコードのような仕組みがあればZone Apexをwebサーバに向けることもできるが、そのような機能を持たないDNSサービスもあるためwebサーバに向けるドメインサブドメインしか使えないと言うことがある。

blog.serverworks.co.jp

どのように対応したか

  1. 今回はレジストラの変更先としてDoレジを使用した。手順は以下URLの通り。

    『Doレジ』 レジストラ変更

  2. DoレジからネームサーバをRoute53のものに変更。
  3. 元のドメイン管理サービスに登録されていたメールサーバ情報なども移管して完了

最後に

作業自体はたいしたことはしていないが、先方の使っているドメイン管理サービスがあまり聞かないものだったため先方のドメインレジストラや契約状況の確認などに時間がかかった。。。

1ヶ月はなんだかんだかかる作業と見込んだ方がいいと思う。

go1.20で追加されたCancelとWaitDelayを理解する

なぜこの記事を書くのか?

ただGoでDaggerを使いたかっただけなのだが、daggerを実行しようとするとversion1.20を必要とされた。

その際に以下のようなエラーが出たのでCancelWaitDelayとはなんなのか気になり調べることにした。

...dagger.io/dagger@v0.7.3/internal/engineconn/session.go:152:8: proc.Cancel undefined (type *exec.Cmd has no field or method Cancel)
...dagger.io/dagger@v0.7.3/internal/engineconn/session.go:156:8: proc.WaitDelay undefined (type *exec.Cmd has no field or method WaitDelay)
note: module requires Go 1.20

参考にした記事

Go 1.20で入ったexec.CommandのCancelとWaitDelayで外部コマンドを正しく終了させる - ぽよメモ

Cancelとは

簡単に言うと、他のContextがDoneされた際に呼び出される関数。

exec.CommandContext関数内で呼び出され、デフォルトではprocess killコマンドが呼び出されるようになっている。

Process Killコマンド以外にも自由に設定することができ、以下のようにすればいい。

sampleCtx, sampleCancel := context.WithCancel(ctx)

proc = exec.CommandContext(sampleCtx, xx, yy)

proc.Cancel = 好きなClose処理

WaitDelayとは

上記のCancelが失敗したとされるまでの猶予時間。これを過ぎるとProcess.Killコマンドが実行される。

Dagger sessionでは5分が設定されていた。

proc.WaitDelay = 300 * time.Second

InnnoDBのトランザクションの扱いについてまとめた

トランザクション分離レベル

ファントムリード

3度SELECTを行い件数を取得するTx1クエリがあるとする。

Tx1クエリのCommit前に、1行別のTx2クエリトランザクションによってCommitされたとする。

Tx1クエリトランザクションでは、このトランザクション内で他のTx2クエリのCommit後、取得するレコードの数が増えてしまう。

このように他のトランザクションCommitによって読み込むレコードの数が増減することを言う。

 

ダーティリード

ファントムリードとは違い、他変更トランザクションがCommitされる前にすでに影響を他トランザクションに及ぼすことを言う。

この場合、変更された状態のものを他トランザクションは取得するがRollbackされることでその変更はなくなるといった不整合が起きたりする。

ファントムリードとは違い、1つのレコードに対して別トランザクションが競合した際にどうなるかについての挙動を指している。

 

ファジーリード(ノンリピータブルリード)

ファントムリードと影響を受けるタイミングは同じで、別トランザクションのCommit以降に影響を受ける。

ファントムリードとは違い、1つのレコードに対して別トランザクションが競合した時にどうなるかについての挙動を指している。(ファントムリードは複数レコードに対する影響を指している。)

1つのレコードを取得するトランザクションがCommitされる前に別トランザクションからのCommitで変更されたとき、その変更後のデータを取得してしまうと言う影響のこと。

 

MVCCとは?

最新のレコードが更新されると、未CommitのトランザクションにはUNDOログという古い更新前レコードを参照させる仕組みのこと。

 

MCCCによって何が解決する?

ファントムリード、ファジーリード、ダーティリードの発生を防げる。

 

ノンロッキングリードとロッキングリードとは?

ノンロッキングリードでは、最新のレコードにロックがかかっていたとしても古いレコードにMVCCの仕組みによってアクセスできるためロックの競合が起きない。

ロッキングリードは、DELETEやUPDATEクエリを発行する際に起こり、最新のデータへアクセスする必要があるためロックの競合は起きる。

これらはトランザクション分離レベルにおけるREPEATABLE-READとREAD-COMMITEDにおいて実行されるクエリの種類によって変化する。

 

ロッキングリードの注意点

- 自身がロックしているレコードは最新のものが見えるため過去の状態のレコードを期待していると予期せぬ結果が返ってくる。

- つまり、SELECT(ノンロッキング)-> UPDATE(ロッキング)-> SELECT(ロッキング)だと最初と最後の見ているレコードのバージョンが違うことになる。

 

MVCCが適用されないロッキングリードではどうやってファントムリードを防ぐのか?

ロッキングリード時は、ダーティリードとファジーリードはそもそも同一レコードに対してロックがかかっているので他トランザクションは実行できないため防がれる。

ただし、ファントムリードはロックしている行以外が差し込まれた時にクエリの実行される範囲によっては影響を受けるというものであるためロックだけでは防げない。

MVCCも機能しないロッキングリードではこうした場合、ネクスキーロックによってファントムリードを防ぐ。

 

ネクスキーロックとは

特定範囲の行に対するトランザクションがあった時、各行の次に挿入される予定のレコード部分をロックする。

ここにレコードが現状存在するわけではないが、あると仮定してロックがされる。これによって新しいレコードが挿入できなくなり、ファントムリードは防がれる。

 

 

 

DNS周りの理解を深める

ネームサーバとDNSサーバーの違いは?

同じ。ドメインIPアドレスを変換して紐づけるためのもの。

権威DNSサーバとDNSコンテンツサーバの違いは?

同じ。フルサービスリゾルバやキャッシュサーバのように名前解決先を探す役割ではなく、自分の知っているゾーン内でのみ回答するという機能・役割のDNSサーバ。

プライマリサーバとセカンダリサーバの違いは?

プライマリに自分でレコードの設定を行い、その設定をコピーして同じ動きをするようにするのがセカンダリサーバ。

どうやって同期する?

方法は主に3つあるが、使用しているDNSサーバソフトウェアによって変わる。

  • ゾーン更新通知(NOTIFY)とゾーン転送要求(AXFR,IXFR)を利用する
  • ファイルを転送する
  • RDBを利用する

DNSサーバソフトウェア色々(参考

  • BIND(古くからあり脆弱性も多くなってきているが実績は多い。Dynamic DNS、view、DNSSEC、IPv6の対応などのほぼすべての機能が揃っている)
  • NSD(権威DNSサーバに特化)
  • unbound(DNSキャッシュサーバに特化)
  • dnsdist(DNSのロードバランシングや暗号化ができる。古くなったBINDから別ソフトウェアに移行する際、キャッシュDNSサーバと権威DNSサーバをまとめた一つの構成となっているBINDと同じ構成を作るためにdnsdistを他のソフトウェアと組み合わせて使用したりする)

Dynamic DNS

view

名前解決クエリの結果を、接続先によって見せ方を変えるようなことができる。(参照

DNSSEC

DNSキャッシュサーバが応答されたレコードに付与された鍵と署名のセットを見て、正しいDNSサーバからの応答なのかをチェックする技術。これによって、DNSキャッシュポイズニングによる偽装サイトへの誘導などを防ぐことができる。(参照

SOAレコードとは?

ゾーン転送に関する情報を持っており、プライマリサーバからセカンダリサーバへのゾーン転送をするために必要。

Zone ApexとCNAMEレコードの制約

そもそもZone Apexとはexample.comというゾーンの頂点であるexmample.comというネイキッドドメインのことを指す。

このZoneApexにはCNAMEレコードを登録できない。つまり、ネイキッドドメインへのアクセスをALBのdns名にCNAMEで紐づける。といったことができない。

理由としては、ZoneApexのドメイン名はそのゾーン内の他のMXレコードやNSレコードへ名前解決する必要があるのでCNAMEを設定するとこれら他のレコードへの名前解決ができなくなる可能性がある。

困るのは、ZoneApexドメインIPアドレスが一意ではないロードバランサーなどへのCNAMEレコードを設定したい場合である。一意なIPアドレスであれば、AレコードでZoneApexを指定すればいいのだがそうでない場合、エイリアスレコードが使用可能なサービスへの移行を考える必要がある。

エイリアスレコードを使えるサービス

参照

monologの仕組みを調べてみた

LaravelやSynfonyなどPHP系のフレームワークに組み込まれているmonologの仕組みを理解しようとしたら思っていた以上にHandlerというlogの出力先の設定などをするためのものが元々用意されているのがいいなとおもた。

 

抑えるべき概念

- Handler

- Formatter

- Logger

- Processer

 

Handler

log出力先、出力するlog levelなどのlogに関する設定を決める。

DynamoDBHandler, RedisHandler, ElasticSearchHandlerなどが用意されている。

出力されたlogのハンドリングが主な責務。

 

Formatter

HandlerにアタッチすることでHandlerから出力されるlogのフォーマットをカスタマイズできる。

JsonFormatterやElasticSearchFormatterなどが存在する。デフォルトはlineFormatterとなっている。

 

Logger

logを出力するチャンネルを作成し、Handlerを紐づけてlogをHandler通りに出力してもらうようになっている。

例えば、dbChannelをnew Loggerで作成し、このチャンネルのlogはDynamoDBHandler,RedisHandle,MongodbHandlerでハンドリングする。というようなイメージ。

 

Processer

出力するlogに情報(データ)を追加することができる。

GitProcesserは現在のブランチ情報をlogに追加してくれる。MemoryUsageProcesserは現在のメモリ使用量をlogに付与してくれる。などなど

 

公式のdoc

monolog/doc/02-handlers-formatters-processors.md at main · Seldaek/monolog · GitHub

AWSアクセスキーの管理方法のベストプラクティスの勉強とGo SDK実装のコードリーディング

結論

アプリ実行用サーバにアクセスキーを持たせて、各AWSリソースにアクセスするやり方はできるだけ避ける。

代わりにIAM RoleやSTSを使って一時的な認証を行うことができるのでそちらでできるだけ代替する。

具体的には、以下の順に推奨されていた。

  1. EC2かECSならそれぞれ専用のIAM Roleを使用する
  2. 1が無理なら.aws/credencialsか.aws/configに設定する
  3. EC2以外のマシンなどで実行する場合などは、環境変数で持たせるといい

AWS アクセスキーを管理するためのベストプラクティス - AWS アカウント管理

どうやるか

EC2のインスタンスプロファイルによってEC2内のアプリケーションに一時的な認証情報を付与する。

つまりインスタンスプロファイルに必要なアクセス権限を持たせたIAM Roleを紐づければいい。

AWS CDKとAWS CLIはこの認証情報をインスタンスプロファイルから自動的に取得してくれる。

メリット

  • アクセスキーの管理がIAM Roleで一元化できる。
  • Credencialsのローテートが不要になる。

AWS CDKとAWS CLIはどうやって認証情報を取得するのか(Golang

認証情報を取得するのは以下のコード実行時らしい。

https://aws.github.io/aws-sdk-go-v2/docs/configuring-sdk/#loading-aws-shared-configuration


cfg, err := config.LoadDefaultConfig(context.TODO()) if err != nil { log.Fatalf("failed to load configuration, %v", err) }

**config.LoadDefaultConfigGetting StartedのWrite Codeを参考にする)**

  • 第1引数: context.TODO()
    • context.Context型を引数にとるメソッドに渡したいcontextの実装がない or 決まっていない場合に暫定的にcontext.Contextを渡すためだけに使用している。(参照
  • 第2引数: config.WithRegion("us-west-2")
    • LoadOptionsFunc型(…func(*LoadOptions) error)を引数に取る
      • config.LoadDefaultConfigは実行されると、LoadOptions構造体のfieldに関数の実装を差し込むLoadOptionsFunc型の関数を返す。(この時点で実行はまだされない)
      • その後、LoadOptionsFunc型の関数はloopによって空のLoadOptions構造体を引数に受け、「LoadOptions構造体のfieldに関数の実装を差し込む」が実行される。
      • 上記流れでLoadOptions構造体に関数がセットされる。
      • LoadOptions構造体はConfigs型に埋め込まれる
      • 外部の設定値をConfigにロードする
      • ConfigsはAWSResolverによってaws.Configに変換され、これが返り値となる。
  • あとは****imds**** packageaws.Configを渡せばそのfield通りにEC2内のデータを取得できる。
    • imds package内のIAMInfo構造体でinstance profileの値は受け取ってそう
    • 時間があればこの先もそのうち調べてみる

参照記事・コード

https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/config#LoadDefaultConfig

https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/credentials/ec2rolecreds

https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/feature/ec2/imds#IAMInfo

https://github.com/aws/aws-sdk-go-v2/blob/feature/ec2/imds/v1.13.3/feature/ec2/imds/api_client.go

AWS CDKとAWS CLIがcredencialsを読み込む順番

どうやら以下順番で認証情報の読み込みを行うらしい(参照

  1. 環境変数

    1, AWS_ACCESS_KEY_IDAWS_SECRET_ACCESS_KEYAWS_SESSION_TOKEN

    2, AWS_WEB_IDENTITY_TOKEN_FILE

    • これは、以下記事のように外部IDプロバイダを使用した認証を行う際に設定される値。
    • GithubのID Providerで認証を行う例(記事
    • おそらくTokenが含まれたfileのパスか、Token文字列そのものが設定されると思われる。
  2. 設定ファイル

    1, .aws/credencialsを確認

    2, .aws/configを確認

  3. ECSの場合、タスク用のIAM Roleを確認

  4. EC2の場合、インスタンス用IAM Roleを確認