🦔

Private Host Zoneを使ってDB接続情報の`.env`運用から脱却する

2024/03/24に公開

TL;DR

  • .envを利用した設定値の管理は結構つらいので可能な限り控えるべき
  • 各環境がVPCで隔離されている場合はDBのエンドポイントをPrivate Host ZoneのCNAMEレコードに登録することでアプリケーションがアクセスするDBのhost名を固定値にできる
  • DBのhost名だけでなく、VPCエンドポイントを付与できるリソースはPrivate Host Zoneを経由することで.envから設定値を排除できる

モチベーション

DB接続情報のような、環境毎に異なる設定値を管理する方法として.envが広く普及している。

しかし、筆者はこう考えている。

.envを使うくらいなら環境毎に異なる設定値が存在しないよう最大限努力すべきである、と。

アプリケーションの設定値の管理というと、Twelve-Factor Appが思い浮かぶが、該当の項では下記のように述べられている。

アプリケーションは設定を名前付きのグループ(しばしば“環境”と呼ばれる)にまとめることがある。グループは、Rails におけるdevelopmenttestproduction環境のように、デプロイの名前を取って名付けられる。この方法はうまくスケールしない。アプリケーションのデプロイが増えるにつれて、新しい環境名(stagingqa)が必要になる。さらにプロジェクトが拡大すると、開発者はjoes-stagingのような自分用の環境を追加する。結果として設定が組み合わせ的に爆発し、アプリケーションのデプロイの管理が非常に不安定になる。

III. 設定 | The Twelve-Factor App (日本語訳)

言及されているグループという概念について、資料中では明言されていないものの、筆者は.envもこれに該当すると解釈している。加えて、CakePHPなどのフレームワークで利用するapp_local.phpも同様である。

本稿では.env管理される設定値の代表例としてDB接続情報を取り上げ、これを.env運用から脱却させる方法を紹介する。

前提条件

  • アプリケーションをデプロイする環境がVPCレベルで隔離されていること

.envの管理は結構つらい

環境数に応じて管理するファイルが増えがち

1つのサービスを運用する場合でも、本番環境、リモート開発環境、ローカル開発環境、QA環境、E2Eテスト環境など利用する環境は複数あるのが一般的である。

場合によっては少ない人数で複数のサービスの運用をしなければならないケースもあり、サービス数 × 環境数分の.envを管理するのは非常にコストがかかる。

.envを編集する機会は意外と多い

今日のWebアプリケーション開発において、.envが塩漬けになることは珍しい。

新しい機能のためにS3を追加するときや、新しいサブシステムのためのlambdaやAPI Gaytewayを追加するときには.envを編集することになる。

設定値の増減に合わせて修正する箇所が多くなりがち

たとえば1項目.envに追加するだけでも、最低3箇所は変更しなければならない。

  • 各環境毎の.env
  • リポジトリにコミットする.env.sample
  • 環境変数を読み込むアプリケーションのコード

これは、たいへん手間である。

ミスオペを誘発しやすい

上記の通り、一度の修正でこれだけ多くの変更オペレーションを伴うということは、それだけ人手によるミスが紛れ込みやすいということである。

  • タイポ
  • ファイルの保存先を間違える
  • 修正漏れ

運悪く本番環境の.envでミスが混入してしまうと、それだけで障害となる。

Private Host Zoneを使おう

DBのエンドポイントをPrivate Host ZoneのCNAMEレコードに登録することでアプリケーションがアクセスするDBのhost名を固定値にできる。

Private Host Zoneとは

プライベートホストゾーンは、Amazon VPCサービスで作成する1つ以上のVPC内のドメインとそのサブドメインへのDNSクエリに対し、Amazon Route 53がどのように応答するかに関する情報を保持するコンテナです。

プライベートホストゾーンの使用 - Amazon Route 53

平たく言えば、VPC内でのみ名前解決してくれる君である。

サンプルコード

サンプルとしてTerraformとdocker composeの例を示す。

private-host-zone.tf
# DBへの接続用Private hosted zone
resource "aws_route53_zone" "aurora_mysql" {
    name = "db.example.com"

    vpc {
        vpc_id = aws_vpc.your_vpc.id
        vpc_region = "ap-northeast-1"
    }
    force_destroy = false
    comment = "Managed by Terraform"
}

# Private Host ZoneのCNAMEレコードwiterエンドポイント用
resource "aws_route53_record" "aurora_mysql_writer" {
    name = "writer.db.example.com"
    zone_id = aws_route53_zone.aurora_mysql.id
    type = "CNAME"
    records = [
        aws_rds_cluster.your_cluster.endpoint
    ]
    ttl = 60
}

# Private Host ZoneのCNAMEレコードreaderエンドポイント用
resource "aws_route53_record" "aurora_mysql_reader" {
    name = "reader.db.example.com"
    zone_id = aws_route53_zone.aurora_mysql.id
    type = "CNAME"
    records = [
        aws_rds_cluster_endpoint.your.endpoint,
    ]
    ttl = 60
}

docker composeを利用して開発環境を構築している場合は下記のようにaliasをつける。

mysql:
    image: mysql
    aliases:
      - writer.db.example.com
      - reader.db.example.com

まとめ

  • .env運用はつらいのでなくしていこう
    • Twelve-Factor Appにもちゃんとそう書いてある
  • 環境毎にVPCが隔離されていればPrivate Host Zoneを使ってDBのhost名を固定値で運用できる
  • VPCエンドポイントを作成できるリソースであれば同様のアプローチを使うことができる
ランサーズ株式会社

Discussion