Site icon MatrixFlow

ノーコードAIプラットフォームを支える技術(インフラ構成管理編)

はじめに

株式会社MatrixFlowでは、ウェブブラウザから機械学習を実行できるノーコードツールであるMatrixFlowの開発を行っています。前回の記事ではMatrixFlowのAWS上の実行基盤について紹介しました。本記事では、実行基盤の構成管理と利用しているツール・運用方法について紹介します。

目次

構成管理ツールの種類と選定理由

MatrixFlowでは構成管理ツールに、AWSが公式に提供しているAWS CDKを採用しています。CDKはTypeScriptやPythonなど様々な言語でリソースを定義・記述でき、コマンド1つで複数のAWSリソースをデプロイ・展開することができます。CDKの細かい内容に関しては、実践!AWS CDK #1 導入実践!AWS CDK #26 Version 2などの記事をご覧ください。

CDKを採用した理由は、TypeScriptやPythonなどのプログラミング言語で記述でき、かつAWSが公式に提供しているからです。その他の理由としては、開発者が記述していないリソースでも必要な場合はデフォルトで作成してくれる点です。特にIAMロールは、明示的に宣言しない場合に必要最低限の権限を付与して作成されます。これは、コードとして記述されていないリソースは作成されてしまいますが、実行に必要なリソースを自動生成してくれるため便利な機能だと思っています。

AWSが提供している構成管理ツールは他に、CloudFormationというのもあります。CloudFormationではyamlファイルでリソースを定義・記述できます。このyamlフォーマットは全てのリソースを記述する必要があり、煩雑なため採用していないです。CDKもデプロイする時にはCloudFormationのyamlフォーマットに変換されてデプロイされますが、yamlファイルを作成することを意識しないので良いなと思っています。

そのほかの有名な構成管理ツールとしてTerraformがありますが、こちらもMatrixFlowでの利用は考えていないです。理由はMatrixFlowの実行環境はAWSのみで、AzureやGCPなどマルチクラウドで展開していないためです。AWSしか使っていない場合は、Terraformを利用してもメリットを享受できないなと思い採用していないです。

CDKでの構成管理について

CDKでデプロイ・構成管理をする1つのまとまりはStackという単位になっています。CDKでデプロイした後は、以下の図のようにAWSのCloudFormationから内容を確認できます。このStackの中には、いくつかのAWSリソースが含まれています。加えて、削除するときもこのStackで管理しているリソースをまとめて削除できます。

MatrixFlowでは利用しているAWSリソースのほとんど全てをCDKで管理しており、記事執筆時点で計83個のStackを作成・管理しています。

前回の記事で述べたようにMatrixFlowでは開発環境・ドッグフーディング環境・本番環境の3つの環境が存在しています。そして、1つの環境では10個程度のStackを連携させることで稼働しています。以下の図は、1つの環境で作成・利用している複数個のStackを簡略化して点線の枠で囲んで表しています。点線で囲われていないリソースは、CDKで管理していないことを表しています。この図のように複数個のStackを連携させることで、MatrixFlowは稼働しています。

CDKで管理していないリソースは、RDSやRoute53やS3などです。管理していない主な理由は、CDKで管理するメリットがあまりないと判断したためです。AWSのリソースのうち、頻繁に更新があるリソース・そのリソース同士を組み合わせるのが難しい時・そもそもリソースを作成するのが大変な時に、CDKなどで構成管理をするとメリットが大きくなると思っています。Route53やRDSなどは頻繁に更新することがなく、さらにECSやLambdaなどと比べると作成方法や他リソースとの組み合わせが簡単です。これらの理由から一部のリソースは管理せず、構成管理をした方がメリットが大きいリソースのみCDKで管理しています。

Stack分割の粒度は、関連があるAWSリソースが1つのStackになるようにしています。例えば、サービスというStackにはECSとALBが合わせてデプロイされるようになっています。これは、MatrixFlowが動いているECSをデプロイする時には、ALBが必要だからです。

一方でまとまりを意識しすぎて大きくなりすぎる場合は、Stackを分割してARNやリソース名を参照することで関連を持たせるようにしています。例えば、サービスとリリースパイプラインの2つのStackはどちらも関連が強くありますが、2つになるように分けています。そして、パイプラインの方からサービス内のECSのリソース名を参照して動くようにしています。WAFに関してもALBと合わせて同じStackにしても良いのですが、WAFに関しては他のALBとも共有して利用できるため、WAF単体でStackを作成しています。

CDKの実装について

CDKのフォルダ構成は以下のようになっており、CDKのバージョン2系で初期化した時に自動生成される構成をそのまま採用しています。また、CDKはTypeScriptで実装しています。

.
├── cdk.json
├── package.json
├── package-lock.json
├── tsconfig.json
├── bin/
│   └── index.ts
└── lib/
    ├── index.ts
    └── stacks/
        └── {各種stack}.ts
 

binフォルダにはStackのインスタンスがまとめて定義されたindex.tsを、libフォルダにStackごとのコンストラクタやインターフェイス・パラメータ定数がファイルごとに作成してあります。libフォルダ内で定義されているファイルでは、Stackのコンストラクタを一部修正して以下のようなクラスを作成しています。パラメータごとに複数作成できるように、パラメータ定義のインターフェイスを作成して、各環境用のパラメータ定数を定義しています。

import {
  App, 
  Stack,
  StackProps,
} from 'aws-cdk-lib';

interface IEcsStack {
  /** 開発・ドッグフーディング・本番環境を区別するための値 */
  environment: "dev" | "dogfooding" | "prod"
  suffix: string
  /** そのほかの値は省略 */
}

/** 開発環境用のパラメータ */
export const devEcsParams: IEcsStack = {
  environment: "dev",
  suffix: "dev"
  /** そのほかのdev環境用の値は省略 */
}

/** ドッグフーディング環境用のパラメータ */
export const dogfoodingEcsParams: IEcsStack = {
  environment: "dogfooding",
  suffix: "dogfooding"
  /** そのほかのdogfooding環境用の値は省略 */
}

/** 本番環境用のパラメータ */
export const prodEcsParams: IEcsStack = {
  environment: "prod",
  suffix: "prod"
  /** そのほかのprod環境用の値は省略 */
}

export class EcsStack extends Stack {
  constructor(scope: App, id: string, params: IEcsStack, props?: StackProps) {
    super(scope, id, props);
    /** 細かい実装は省略 */
  }
}
 

bin/index.tsは以下のようなコードになっています。EcsStackの引数に環境ごとのパラメータを入れるだけで、異なる設定の環境を作成できます。TypeScriptのインターフェイスを使うと、間違った値を指定した時にエディタがエラーを教えてくれたり、デプロイする前にもエラーが発生したりするので事前に気づくことができます。ただ、コンストラクタに独自の引数を設定する方法は、他のCDKの記事などでは見かけることはないです。公式の方法だと、ContextValuesの利用を推奨しているそうですが、インターフェイスを使って引数を入れた方が便利だと思っているのでこの方法を使っています。

import { App } from 'aws-cdk-lib';
import * as lib from '../lib';

const app = new App();

// 各環境のECSを構築するStack
new lib.EcsStack(app, "MatrixFlowEcs-dev1", lib.devEcsParams);
new lib.EcsStack(app, "MatrixFlowEcs-dogfooding", lib.dogfoodingEcsParams);
new lib.EcsStack(app, "MatrixFlowEcs-beta", lib.prodEcsParams);
 

AWSではリソースに一意の名前をつける必要があり、同じStackを違うパラメータでデプロイする際には名前が一意になるようにしています。具体的には、environmentとsuffixパラメータを付与しています。environmentは環境そのものの名前を表しており、MatrixFlowでは開発・ドッグフーディング・本番環境のいずれかを表します。suffixは同じ環境の中での識別子となっており、RDSやS3などのリソースは共有しつつ、ECSなどの稼働部分のみに変更を加えたい時に利用します。environmentとsuffix、および各環境とリソースの関係は以下の図のとおりで、ここでは開発環境にHotFix環境を追加した状態を表しています。

HotFix環境を用意するのは本番環境ですぐに修正が必要なバグが出て、その変更内容をテストしてリリースする時です。通常のリリースでは開発環境でテストを行ったりするのですが、HotFixの場合はその変更内容を開発環境へとデプロイするのが難しいため一時的に用意したHotFix環境でテストします。このようにすることで、同じリソースを共有しつつテストの実行や一時的に変更が必要な環境でテストをすることができます。

CDKを運用する際の注意点と気がついたこと

CDKではTypeScriptやPythonなど様々な言語を使って記述することができます。そのため、AWSのリソースを指定する際にfor文やif文などの制御構文を利用することができます。しかし、それらの制御構文は利用せずに書くようにしています。理由は、それらの制御構文を利用するとコードを読む際に処理の流れを考える必要が出てきて可読性が低くなるためです。

CDKを運用していてよかった点は、別の環境が必要になった時にパラメータを変えるだけで同じ環境をすぐに用意できる点です。MatrixFlowを稼働させるには多くのAWSリソースを利用・連携させる必要があるため、人手でマネージメントコンソールから設定をしていった場合、必ず失敗してしまうと思います。その点、CDKの場合はコードが間違っていなければ同じ設定で毎回デプロイが実行されます。また、不要になった場合もCloudFormationのStackごと削除でき、使っていないリソースが残ることはほとんどないのも良い点です。

CDKを運用していて大変だった点は、設定したい内容のパラメータを探すことです。公式ドキュメントをみたら大体載っているのですが、どのパラメータが対応しているのかがわからないことがあり大変だったりします。特に、1回の反映に少し時間がかかるECSなどはデバックをするのが大変です。また、これは良いことでもあるのですが、CDKの開発のスピードが早く大体1週間でバージョンがあがります。すると、たまにバージョンをあげるとメソッドが使えなくなっていたりすることがあります。2年ほど使っていて1回くらいしか起きたことがなく、あまり影響はないですが大変な点として挙げます。

おわりに

MatrixFlowのAWSで展開しているリソースの構成管理についてまとめました。CDKを利用することで、迅速にインフラ構築をできるので良いです。