NetBox のオンプレから Amazon ECS 移植を CDK で実現する

チームの管理情報を溜めていたオンプレ基盤で動く NetBox を Amazon Elastic Container Service へ AWS Cloud Development Kit を用いて移植しました。 今まで NetBox をオンプレで動かしていた際には以下のような運用の難しさがありました。

  • DB も Docker コンテナによって管理されており、冗長化もなかったため DB コンテナが落ちてしまうとサービス提供できなくなる可能性があった
  • Docker Compose で動かしているので、サービスの作り直しを実施するとそれまでのログが削除される
  • そもそも NetBox を動かしている場所で法定停電があり、定期的に NetBox のサービスがとまっていた

NetBox を AWS へともっていくことでオンプレ運用時に発生していた手間を簡素化し、メンテナンス等も一部 AWS にマネージしてもらえることで運用の省力化を達成できました。 さらに、 AWS Cloud Development Kit を利用することでデプロイも簡素化できました。

今回はその様子や管理における粒度等といった細かい意思決定について共有します。

目次

自己紹介

こんにちは、イノベーションセンターの福田です。

普段はクラウド関連の調査・開発や Infrastructure as Code (IaC) の推進等に従事しています。

チームでオペレータが手動で実施していた NetBox 管理をパブリッククラウドに移行して運用の省力化をしつつ、 IaC 化することでデプロイがしやすい基盤を整えたのでその方法を共有します。

用語

今回の移植対象である NetBox やその移植先である Amazon Web Services (AWS) で使用しているコンポーネント、 IaC や AWS における IaC の実装としての AWS Cloud Development Kit (CDK) 等用語がいくつも飛びでます。 ここではそれぞれがどういったものかといったことについて説明します。

NetBox

NetBox とは NetBox Labs が作成・提供している OSS データセンター管理ツールです。 OSS であるため、一定の環境さえ提供できれば今回紹介する AWS や Microsoft Azure, Google Cloud といったクラウドでも動かせます。

NetBox OSS 4.0.8

データセンター上のさまざまなものを管理できますが、一例を上げれば以下のようなネットワークの資産を管理できます。

  • IP アドレス
  • ラッキング情報
  • VLAN 管理

この他にもさまざまなものを管理できますが、データセンターに設置した機器の情報等を一括で管理できます。

Apache License, Version 2.0 で提供されている1ため、 OSS として利用することもできますが、 NetBox Labs では NetBox Cloud というクラウドサービスを提供しています。

NetBox Cloud - NetBox Labs

NetBox を NetBox Labs にマネージしてもらえるため、運用の省力化やセキュティの迅速な対応、信頼性の確保といったものをまかせることも可能です。

データベース

NetBox はデータベースとして RDB である PostgreSQL のバージョン 12 以上を利用します2。 NetBox を移植するにあたってはこの PostgreSQL をパブリッククラウド上に用意する必要があります。

幸い、 AWS は Amazon Relational Database Service (RDS) のサービスとして Amazon Aurora というサービスにて PostgreSQL を提供しています。

Amazon Aurora(高性能マネージドリレーショナルデータベース)| AWS

簡単に PostgreSQL 互換の RDB をデプロイでき、セキュリティパッチやバージョンアップグレード、スケーラビリティの確保などを AWS に管理してもらうことができます。

Amazon Aurora には Aurora Standard と Aurora I/O 最適化といったプランがあります。 Aurora I/O 最適化は I/O 要求があるようなアプリケーションに向いていると AWS は述べており、今回の NetBox は社内利用であることからそこまで I/O 要求がないことを考えて Aurora Standard を採用しました。

I/O が少量~中程度のアプリケーションで費用対効果が高い選択肢は Aurora 標準です。I/O が大量のアプリケーションでは Aurora I/O 最適化により、料金パフォーマンスの向上、予測可能な料金の利用、最大 40% のコスト削減が可能です。

参考: AWS が Amazon Aurora I/O 最適化をリリース

他にも Amazon RDS for PostgreSQL というサービスがある3のですが、 Amazon Aurora は Amazon RDS for PostgreSQL に対して以下のメリットがあります。

  • ストレージの自動スケーリング
  • ストレージのサイジングが不要
  • ストレージの地理的分散
  • マスターへの障害発生時にレプリカを昇格可能
    • Amazon RDS for PostgreSQL の場合はスタンバイ状態のものを別途用意する必要あり

さらに、 NetBox では管理されるものが DC の IP アドレスや VLAN タグ等であることから利用されるデータ量はかなり少ないことが予想されます。 一般にデータ量や IO が少なければ料金的には Amazon Aurora の方が安いです。 Amazon Aurora と Amazon RDS for PostgreSQL における料金表は次の通りです^InstanceSize

比較項目 Amazon Aurora Amazon RDS for PostgreSQL
インスタンス料金 0.113 USD/時間・インスタンス 0.129 USD/時間・インスタンス
I/O料金 0.24 USD/100万リクエスト -
ストレージ料金 0.12 USD/月・GB 0.138 USD/月・GB

稼働時間を 730 時間 (1ヶ月)、利用する容量を 100 GB とし、 I/O は100万リクエストとします。 また、 Amazon Aurora ではリードレプリカを2つ、 Amazon RDS for PostgreSQL ではスタンバイを2つ用意すると仮定します。 この仮定のもと計算すると次のようになります。

サービス名 金額
Amazon Aurora 0.113 USD/時間・インスタンス * 3インスタンス * 730 時間 + 0.24 USD/100万リクエスト * 1 100万リクエスト + 0.12 USD/月・GB * 1ヶ月 * 100 GB = 259.71 USD
Amazon RDS for PostgreSQL 0.129 USD/時間・インスタンス * 3インスタンス * 730 時間 + 0.138 USD/月・GB * 1ヶ月 * 100 GB = 296.31 USD

表をみると Amazon RDS for PostgreSQL は 296.31 USD で Amazon Aurora の 259.71 USD よりも高くなっていることがわかります。

そのため、料金・運用の省力化の観点から今回は Amazon Aurora を採用しました。

Aurora は他にも MySQL 互換のものも提供しており、また MySQL にも Amazon RDS for MySQL があります。 さらに RDS ではエンタープライズで人気のデータベースである Oracle Database を提供する Amazon RDS for Oracle や Microsoft が開発する SQL Server を提供する Amazon RDS for SQL Server もあり、さまざまな RDB の中から要件にあった RDB を選択・利用しつつ運用の省力化を実現できます。

キャッシュ

NetBox ではキャッシュも Redis というソフトウェアを利用します4

Redis とはインメモリーで動作するキーバリューデータベースです。 インメモリで動作するため非常に高速に読み書きできます。

こちらもパブリッククラウドで動作させるにあたっては別途用意しなければなりませんが、 AWS には Amazon ElastiCache というキャッシュサーバーを提供するサービスがあり、その中で Redis を提供しています。

Redis 用 Amazon ElastiCache(キャッシュ管理・操作)| AWS

こちらを利用すれば AWS 側で可用性やスケーラビリティ・セキュリティの確保してもらうことができ、ユーザーは Redis を利用することに集中できます。 VM を使い自前で用意するといったことも可能ですが、運用にあたってはアップデートやセキュリティパッチの適用、スケーラビリティ・可用性の確保といった運用を減らしたかったので ElastiCache を利用しました。

ElastiCache では Memcached も提供されている5ため、ニーズに合わせたキャッシュサーバーを利用できます。

Docker コンテナサービス

AWS では Amazon Elastic Container Service (ECS) という Docker コンテナサービスを提供しています。

Amazon ECS(Docker コンテナを実行および管理)| AWS

ECS を利用すると Docker コンテナのデプロイや管理、スケーリングを容易に実現できます。

ECS には Docker コンテナを動かす環境によって次の形式が存在しています。

  • Amazon EC2 起動タイプモデル
    • AWS の仮想マシン提供サービスである Amazon EC2 の上で Docker コンテナを動かす形式
    • Docker コンテナではなく仮想マシンに対して課金されるので仮想マシンが存在しつづけている限りは課金されつづける
    • 通称 ECS on EC2
  • AWS Fargate 起動タイプモデル
    • ECS on EC2 では Docker を実行する VM 環境を自前で管理するが、 Fargate は Docker コンテナの実行環境を AWS が管理してくれる
    • コンテナに課金されるため、コンテナが起動した分だけ課金される
    • Amazon EC2 起動タイプモデルよりも後に開発された
    • 通称 Fargate

ECS on EC2 は Docker コンテナ環境として仮想マシンを動かすため、地理分散させるときなどには仮想マシンへの配慮も必要となり、管理が煩雑になります。 一方、 Fargate であれば Docker コンテナを動かす環境は AWS が管理するため、ユーザーは Docker コンテナのことだけを考えればよくなり運用の省力化を狙えます。

仮想マシンの管理は煩雑になることが多いため、なるべく省きたいと考え、 ECS を利用して構築することにしました。 さらに幸いなことに、 NetBox は Docker Compose による構築方法も提供しています6。 そのため、そちらを参考にして今回の実装を進めました。 また今回はコンテナのみ動かせればよく、 GPU も利用しないため、 VM の管理を回避しなければならない ECS on EC2 ではなく Fargate を選択しました。

Elastic Load Balancing

Elastic Load Balancing はロードバランサーを提供するサービスです。

Elastic Load Balancing(複数のターゲットにわたる着信トラフィックの分配)| AWS

用途によって4種類のロードバランサーが提供されています。

  • Application Load Balancer (ALB)
    • L7 ロードランサー
    • HTTP/HTTPS リクエストのロードバランシングを提供
  • Network Load Balancer (NLB)
    • L4 ロードバランサー
    • 静的 IP を割り振ることができる
  • Classic Load Balancer (CLB)
    • 名前にある通りクラシックな環境向けのロードバランサー
    • Amazon EC2 の中でもクラシックな EC2-Classic な環境向けであり、 EC2-Classic Networking の環境は販売終了しているため7、現代で触る機会はまずない
  • Gateway Load Balancer (GLB)
    • L3 ロードバランサー
    • ファイアウォール、侵入検知、ディープパケットインスペクションといった機能を提供する際に利用する

一般に Web サーバーを提供するのであれば ALB を提供すれば十分です。 ALB を利用すれば簡単に HTTPS 通信を提供できるだけでなく、 HTTP から HTTPS へのリダイレクトも実装できます。 ただし、 ALB では固定 IP アドレスを利用できません。 そのためもし固定 IP アドレスを利用する場合、 ALB の前段に NLB を用意しなければなりません。 NLB は固定 IP アドレスを提供できるので、 NLB -> ALB とすることで固定 IP アドレスを提供しつつ HTTPS 通信を提供できます。

NetBox はパスワードによる認証があり、 HTTP による通信は避けたい状況があります。 そのため HTTPS 通信を提供する必要がありました。 ALB を利用すれば AWS Certificate Manager という証明書管理サービスを通して証明書を取得・設定し、 HTTPS 通信を提供できるため、 ALB を通してサービス提供するようにしました。

AWS Secrets Manager

AWS が提供するシークレット情報を管理するサービスです。

AWS Secrets Manager(シークレットのローテーション、管理、取得)| AWS

複数人でシークレットを共有したり、シークレットの閲覧・編集権限をロールベースで制御したり可能です。 シークレット情報はこちらに入れておけばアプリケーションにシークレット情報を含めずとも利用可能になり、安全にシークレット情報を利用できます。

シークレット情報は AWS がマネージする AWS Managed Key というもので暗号化されていますが、より強固なセキュリティが欲しい場合は Customer Managed Key (CMK) という顧客管理の鍵で暗号化もできます。

今回は表立ってでてくるというわけではないですが、利用する PostgreSQL や Redis のシークレット情報を保管・参照するといった用途で利用します。

Infrastructure as Code と AWS Cloud Development Kit

近年、インフラの構成時に設定していた細かい部分の暗黙知を減らしたり、デプロイ対応を平準化する目的でインフラをコード化する Infrastructure as Code (IaC) というパラダイムが普及しています。 有名なものでは HasiCorp が提供する Terraform といったものがあります。

Terraform by HashiCorp

AWS では IaC の実装として AWS Cloud Development Kit (CDK) というものを IaC の実装の1つとして提供しています。

オープンソースの開発フレームワーク - AWS クラウド開発キット - AWS

CDK はコードを TypeScript/Golang/Python/Java といったプログラミング言語で記述でき、新しく言語を覚えずとも慣れた言語でインフラコードを記述できます。 また、そのまま言語のライブラリなど各種エコシステムを利用できるため、既存の OSS インフラに乗って容易に拡張ができます。 たとえば TypeScript を利用すれば npm を利用してさまざまなコミュニティが提供するパッケージへアクセスできます。 このように既存の言語を利用できるため、簡単に始めることができます。

以下の例は TypeScript で仮想マシンを AWS 上に用意している様子です。

import * as ec2 from 'aws-cdk-lib/aws-ec2';

new ec2.Instance(this, "Instance", {
  // 各種コンポーネントを動かすのひ必要となる Virtual Private Cloud (VPC)
  // 詳細は https://aws.amazon.com/jp/vpc/
  vpc,
  // 仮想マシンを t2.small で作成
  instanceType: ec2.InstanceType.of(
    ec2.InstanceClass.T2,
    ec2.InstanceSize.SMALL,
  ),
  // OS は Amazon Linux 2023 を利用
  machineImage: ec2.MachineImage.latestAmazonLinux2023(),
});

このように各種言語が既知であれば簡単にどのようなインフラ構成をしているのかを読めるところが CDK の特徴です。

CDK は背後で AWS CloudFormation という AWS の提供する構成管理ツールを利用しています。 各種言語で記載されたインフラの情報を元として CloudFormation が解釈できる形式にし、 CloudFormation を呼び出すことでインフラの管理を実現しています。

アーキテクチャ

アーキテクチャとしては RDS と ElastiCache を配置し、NetBox からクライアントとして接続するという一般的な構成にしました。

NetBox は Docker イメージ内にバックエンドとフロントエンドを含めているため、1つの Docker コンテナを動かせばバックエンドとフロントエンドの両方を提供できます。

この環境を CDK で実装していきます。

実装

流れとしては次のようになります。

  1. RDS の設置
  2. ElastiCache の設置
  3. NetBox の設置

RDS

RDS では強力な権限を持つ管理者ユーザーが存在しており、そのユーザーのパスワードとして強力なものを設置します。

import * as sm from 'aws-cdk-lib/aws-secretsmanager';

// 自動生成されるパスワードにおいて使わない文字を設定
//
// 詳しくは https://docs.aws.amazon.com/ja_jp/AmazonRDS/latest/AuroraUserGuide/CHAP_Limits.html#RDS_Limits.Constraints
const excludeCharacters = ':@/" \'';
const masterPassword = new sm.Secret(this, "DbMasterPassword", {
  // AWS Secrets Manager というシークレット管理サービスにシークレット情報を保存するnode、見たときにわかりやすい名前をつけておく
  secretName: `/Netbox/DbMasterPassword`,
  // 
  generateSecretString: {
    excludeCharacters,
    passwordLength: 64,
    // 英大文字小文字、記号、数字を1つずつ含めるようにする
    requireEachIncludedType: true,
    // 管理者ユーザー名は `postgresAdmin` 固定
    secretStringTemplate: JSON.stringify({ username: "postgresAdmin" }),
    // ここで生成するパスワードは `password` というキーで登録する
    generateStringKey: 'password',
  },
});

次にこのパスワードを利用して RDS を生成するようにします。

import * as cdk from 'aws-cdk-lib';
import * as rds from 'aws-cdk-lib/aws-rds';
import * as ec2 from 'aws-cdk-lib/aws-ec2';

new rds.DatabaseCluster(this, "Db", {
  // RDS サービス上の DB 識別子
  clusterIdentifier: "NetBoxDb",
  // PostgreSQL の 12.16 を利用
  engine: rds.DatabaseCluster.auroraPostgres({
    version: rds.AuroraPostrgesEngineVersion.VER_12_16,
  }),
  // 管理者パスワードを設定
  credentials: rds.Credentials.fromSecret(masterPassword),
  // ストレージ暗号化を有効化
  storageEncrypted: true,
  // マスター DB を動かす仮想マシンの CPU サイズやメモリーを設定
  writer: rds.ClusterInstance.provisioned("DbClusterWriter", {
    instanceType: ec2.InstanceType.of(
      ec2.InsetanceClass.T4G,
      ecc2.InstanceSize.LARGE,
    ),
  }),
  // リードレプリカを設置
  readers: [
    rds.ClusterInstance.provisioned("DbClusterReader", {
      instanceType: ec2.InstanceType.of(
        ec2.InsetanceClass.T4G,
        ecc2.InstanceSize.LARGE,
      ),
    }),
  ],
});

最後に管理者パスワードは定期的に更新するようにします。

import * as sm from 'aws-cdk-lib/aws-secretsmanager';

new sm.SecretRotation(this, "PasswordRotation", {
  application: sm.SecretRotationApplication.POSTGRES_ROTATION_SINGLE_USER,
  // 保存先
  secret: masterPassword,
  target: db,
  // 1度実行したら1週間後に実行
  automaticallyAfter: cdk.Duration.days(7),
  // パスワードに入れない文字を設定
  excludeCharacters,
});

ここまでくれば psql 等の PostgreSQL クライアントで接続できます。

RDS が用意ができたら NetBox が接続するために必要な環境を RDS 内に作ります。 詳しくは ドキュメント に記載があるのでそちらを参照してください。 簡易には次のスクリプトを流せば準備完了です。

CREATE DATABASE netbox;
CREATE USER netbox WITH PASSWORD '<パスワード>';
ALTER DATABASE netbox OWNER TO netbox;

用意したら設定したパスワードを AWS Secrets Manager へ保存しておきます。 こうすることで NetBox のデプロイ時にシークレット情報を直接埋め込むことなくシークレット情報を参照できます。

私たちは既存の NetBox 環境があるので、上記設定で必要な初期化を PostgreSQL に行ったのち、以下の手順でバックアップを取得し復元作業を実施しました。

# 事前にユーザーにバックアップと復元計画を周知した上で既存 PostgreSQL から pg_dump で論理バックアップ
docker-compose exec postgresql pg_dump -h postgres -U netbox netbox > db.dump
# RDS へ接続できる仮想マシンを用意し、 db.dump をコピーしてその上で実行する
# ダンプファイルから復元して netbox データベースを再構築
# コマンド実行後にパスワードの入力を求められるので事前に RDS の管理者アカウントのパスワードを AWS Secrets Manager から取得しておく必要がある
psql -h "<RDS のマスター DB エンドポイント>" -U postgresAdmin -W < db.dump

ElastiCache

Redis にはアクセス制御機能があり、ユーザーに対してさまざまな制御を実現しています。 Redis 版 ElastiCache にもこの機能があり、ユーザーを管理する機能 があります。 まず各種アクセス権限を設定したユーザーがおり、それをまとめておくユーザーグループがあるという状態です。

ユーザーグループには default というユーザー名を持つユーザーを必ず含めなければなりません。 そこで default ユーザーを作成し、 Redis に紐付けるユーザーグループへその default ユーザーを追加します。

import * as sm from 'aws-cdk-lib/aws-secretsmanager';
import * as ec from 'aws-cdk-lib/aws-elasticache';

const defaultUserPassword = new sm.Secret(this, "DefaultUserPassword", {
  secretName: "/Netbox/RedisMasterPassword",
  generateSecretString: {
    secretStringTemplate: JSON.stringify({ username: "default" }),
    // ここで生成するパスワードは `password` というキーで登録する
    generateStringKey: "password",
    // Redis AUTH コマンドによる認証によって使用できる記号は制限されている
    //
    // https://docs.aws.amazon.com/ja_jp/AmazonElastiCache/latest/red-ug/auth.html#auth-overview
    excludeCharacters: "@%*()_+=~`{}|[]\\:\";'?,./",
  },
});
const defaultUser = new ec.CfnUser(this, "DefaultUser", {
  // ユーザーにつける ID は英小文字とハイフンしか受け付けない
  userId: "netbox",
  userName: "default",
  engine: "redis",
  // 全ての権限つける
  accessString: "on ~* &* +@all",
  // 生成したパスワードを使用するように設定
  passwords: defaultUserPassword.secretValueFromJson("password").unsafeUnwrap(),
});
const userGroup = new ec.CfnUserGroup(this, "UserGroup", {
  // ユーザーグループにつける ID は英小文字とハイフンしか受け付けない
  userGorupId: "netbox",
  engine: "redis",
  userIds: [defaultUser.userId],
});

ユーザーグループがあれば Redis を構成できます。

new ec.CfnReplicationGroup(this, "ReplicationGroup", {
  replicationGroupDescription: "netbox",
  engine: "redis",
  // バージョンは 6.2 を使用
  engineVersion: "6.2",
  // Redis を動かす仮想マシンの CPU サイズやメモリーを設定
  cacheNodeType: "cache.t5g.medium",
  // Redis にはさまざまな設定項目があるが、ベストプラクティスのつまったデフォルトのものを利用する
  cacheParameterGroup: "default.redis6",
  numNodeGroups: 1,
  replicasPerNodeGroup: 2,
  // TLS 有効化
  atRestEncryptionEnabled: true,
  transitEncryptionEnabled: true,
  // 作成したユーザーグループを紐付け
  userGroupIds: [userGroup.userGroupId],
  // 地理分散して冗長性確保
  multiAzEnabled: true,
  // 英小文字とハイフンしか受け付けない
  replicationGropId: "netbox",
});

NetBox

NetBox は Docker Compose 化したものがあるので、そちらを参照にして進めます。

GitHub - netbox-community/netbox-docker: 🐳 Docker Image of NetBox

起動に必要となる環境変数は configuration/configuration.py にあります8。 以上の情報をもとにサービスを構築していきます。

ECS は以下の関係図式のようなコンポーネントで成り立っています。

タスクとは1つまたは複数の Docker コンテナの集合でタスク内の Docker コンテナは同一ホストで実行されます。 タスク定義は名前の通りタスクの定義を持つもので、タスク定義の内容を元にタスクが起動されます。 サービスはタスクを管理するもので指定された数のタスク数を維持してくれます。 タスクが失敗したら同じタスク定義を元に別のタスクを起動しなおしたりといったことを実施してくれます。 クラスターはサービスやタスクをまとめるための論理的なグループです。

本来は1つ1つのコンポーネントを記載していくのですが、現在の CDK では ALB を配置し、その後ろに ECS コンポーネントを配置するだけであれば ApplicationLoadBalancedFargateService という各種 ECS コンポーネント + Application Load Balancer をまとめてデプロイしてくれるパーツを提供しています。 そのため、今回はこちらを利用して Docker コンテナの実行環境や実行時に必要な情報を設定するのみとして実装量を減らしました。

import * as ecs from 'aws-cdk-lib/aws-ecs';
import * as et from 'aws-cdk-lib/aws-ecs-patterns';

const dbSecretName = "<PostgreSQL のシークレット情報を持つ AWS Secrets Manager のシークレット名>";
new et.ApplicationLoadBalancedFargateService(this, "NetBox", {
  // チームでしか利用しないため、アクセス頻度も低くそこまで CPU は必要ないので 0.5 vCPU を利用
  cpu: 512,
  // チームでしか利用しないため、アクセス頻度も低くそこまでメモリーも必要ないので 1024 MB に設定
  memoryLimitMiB: 1024,
  // 環境は安定して利用できる Linux/x85_64 で動かす
  runtimePlatform: {
    cpuArchitecture: ecs.CpuArchitecture.X86_64,
    operatingSystemFamily: ecs.OperatingSystemFamily.LINUX,
  },
  taskImageOptions: {
    // Docker Compose 化された NetBox で指定された Docker リポジトリーを使用
    //
    // https://github.com/netbox-community/netbox-docker/blob/0c99ff8b5663db3e0db5a45660cebda9f917508b/docker-compose.yml#L3
    image: ecs.ContainerImage.fromRegistry("netboxcommunity/netbox:latest"),
    // NetBox は 8080 番ポートで動作している
    containerPort: 8080,
    environment: {
      DB_HOST: "<設置した RDS のマスター DB の持つエンドポイント>",
      DB_NAME: "netbox",
      DB_PORT: "5432",
      REDIS_HOST: "<設置した ElastiCache のプライマリーエンドポイント>",
      REDIS_PORT: "6379",
      // SSL は入れておくことを推奨
      REDIS_SSL: "True",
    },
    // パスワードや秘密鍵等秘密にしておきたい環境変数はこちらに設定
    secrets: {
      DB_USER: ecs.Secret.fromSecretsManager(dbSecretName, "username"),
      DB_PASSWORD: ecs.Secret.fromSecretsManager(dbSecretName, "password"),
      // さまざまなハッシュに利用される値
      // こちらも事前に AWS Secrets Manager に登録しておくこと
      // 詳しくは以下の URL を参照
      //
      // https://netboxlabs.com/docs/netbox/en/stable/configuration/required-parameters/#secret_key
      SECRET_KEY: ecs.Secret.fromSecretsManager("<シークレットキーを保管する AWS Secrets Manger のシークレット名>", "<シークレットの参照キー>"),
      REDIS_PASSWORD: ecs.Secret.fromSecretsManager("<設置した Redis の default ユーザーのパスワードを持つ AWS Secrets Manager のシークレット名>", "password"),
    },
  },
});

これらをまとめた上でデプロイすると NetBox 環境が生成されます。

注意点

少し高度な内容になります。

CloudFormation では管理の単位をスタックという単位で行っています9。 スタックによってさまざまなリソースを一括で作成・更新・削除でき、削除に失敗すれば元の状態へロールバックしてくれます。 このスタックに変更を加えると場合によってはリソースを削除・再生成するという手順を踏むことがあります。 挙動次第では DB やキャッシュというものの削除・再作成まで走ることがあります。 何度作り直しても問題ない仮想マシンや ECS クラスター等のリソースであればそこまで困ることはありません。 ですが、あまりにも1つのスタックにまとめすぎると何かのパラメータ変更と共にスタックの全てが再作成されることもあり得ます。 場合によっては識別子が一意でなければならない影響で作成に失敗し、ロールバックしようとするも削除されていないものが残っていてにっちもさっちもいかなくなるということも起こります。

特に RDS や ElastiCache は削除や変更は慎重におこなうべきものであり、つくりあげた環境が削除や更新もできなくなるという状況は避けたいです。 そこで CloudFormation スタックを1つで管理はせずに小さく RDS / ElastiCache / NetBox でわけて管理しています。 CloudFormation には Nested Stack という機能10もあり、スタックは1つにまとめつつもサブのスタックとして管理もできます。 しかし、Nested Stack があるからと言っても更新頻度が頻繁なものとそうでないものを混ぜると更新頻度の高いものにひっぱられて一斉に更新されることがあります。 Web サーバーは更新頻度が高い一方、 RDS や ElastiCache の更新頻度は高くないことが予想されるため、全てを一緒に含めてしまうと運用が複雑化してしまいます。 このように無用な混乱や運用の複雑化を避けるため、 RDS や ElastiCache 等ステートフルなリソースは個別のスタックで管理すると楽になることがあります。

まとめ

ここまででオンプレにあった NetBox を AWS へ CDK を利用して移植する方法について記載してきました。 幸い NetBox が PostgreSQL や Redis を利用しており、 AWS にこれらをマネージするサービスがあったため、それらを利用して簡単にサービスを構築し、運用も省力化可能になりました。

今後は RDS や ElastiCache のアップグレードにおいてインプレースでのアップグレードに対応しているのでそちらを試したり、バックアップの定期取得をおこなったりといったことを計画しており、これらを CDK といった IaC と実体との整合性を考慮しながら実施していこうと思います。

© NTT Communications Corporation 2014