AWS

CFNカスタムリソースでRDS DBの初期セットアップ

スポンサーリンク
CFNカスタムリソースでRDSのDBを初期化する AWS
スポンサーリンク
スポンサーリンク

CloudFormationカスタムリソースを使って、RDS DBの初期セットアップを実行する

CloudFormationでRDSリソース作成時に、DBの初期化(DBやテーブルの作成、テストレコードの追加等)も併せて実行することを考えます。
今回はCloudFormationカスタムリソースを使用して、DBを初期化します。

構築する環境

Diagram of initialize RDS DB with CFN Custom Resource.

主に4つのリソースを作成します。

1つ目はRDS DBインスタンスです。
今回はMySQLタイプのDBインスタンスを作成します。

2つ目はEC2インスタンスです。
DBインスタンスに接続するクライアントとして使用します。
インスタンスは最新版のAmazon Linux 2です。

3つ目はLambda関数です。
この関数がCloudFormationスタック作成時に自動的に実行されるように、CloudFormationカスタムリソースに関数を関連づけます。
この関数の働きは、DBインスタンスを初期化することです。
関数のランタイム環境はPython3.8です。

4つ目はSSMパラメータストアです。
Lambda関数で実行するSQL文を、パラメータストアに文字列として保存します。
なお初期化処理で実行する内容は、以下のAWS公式ページを参考にしました。

ステップ 3: MySQL データベースにデータを入力する - アマゾン ウェブ サービス
テーブルが作成されたので、事前に設定された SQL スクリプトを使用して、最初の MySQL テーブルを接続、入力、および実行する方法を学びます。

CloudFormationテンプレートファイル

上記の構成をCloudFormationで構築します。
以下のURLにCloudFormationテンプレートを配置しています。

awstut-fa/062 at main · awstut-an-r/awstut-fa
Contribute to awstut-an-r/awstut-fa development by creating an account on GitHub.

テンプレートファイルのポイント解説

SSMパラメータストア

Resources: SQLParameter: Type: AWS::SSM::Parameter Properties: Name: !Ref Prefix Type: String Value: | CREATE database tutorial; USE tutorial; CREATE TABLE planet (id INT UNSIGNED AUTO_INCREMENT, name VARCHAR(30), PRIMARY KEY(id)); INSERT INTO planet (name) VALUES ("Mercury"); INSERT INTO planet (name) VALUES ("Venus"); INSERT INTO planet (name) VALUES ("Earth"); INSERT INTO planet (name) VALUES ("Mars"); INSERT INTO planet (name) VALUES ("Jupiter"); INSERT INTO planet (name) VALUES ("Saturn"); INSERT INTO planet (name) VALUES ("Uranus"); INSERT INTO planet (name) VALUES ("Neptune");
Code language: YAML (yaml)

Lambda関数で実行するSQL文を、SSMパラメータストアに登録します。
DBおよびテーブルを作成後、テストレコードを保存します。

CloudFormationカスタムリソース

まずカスタムリソースで実行するLambda関数を確認します。

Resources: Function: Type: AWS::Lambda::Function Properties: Environment: Variables: DB_ENDPOINT_ADDRESS: !Ref DBInstanceEndpointAddress DB_ENDPOINT_PORT: !Ref MySQLPort DB_PASSWORD: !Ref DBMasterUserPassword DB_USER: !Ref DBMasterUsername REGION: !Ref AWS::Region SQL_PARAMETER: !Ref SQLParameter Code: ZipFile: | import boto3 import cfnresponse import mysql.connector import os db_endpoint_port = os.environ['DB_ENDPOINT_PORT'] db_endpoint_address = os.environ['DB_ENDPOINT_ADDRESS'] db_password = os.environ['DB_PASSWORD'] db_user = os.environ['DB_USER'] region = os.environ['REGION'] sql_parameter = os.environ['SQL_PARAMETER'] CREATE = 'Create' response_data = {} def lambda_handler(event, context): try: if event['RequestType'] == CREATE: client = boto3.client('ssm', region_name=region) response = client.get_parameter(Name=sql_parameter) sql_statements = response['Parameter']['Value'] conn = mysql.connector.connect( host=db_endpoint_address, port=db_endpoint_port, user=db_user, password=db_password ) cur = conn.cursor() for sql in sql_statements.splitlines(): print(sql) cur.execute(sql) cur.close() conn.commit() cfnresponse.send(event, context, cfnresponse.SUCCESS, response_data) except Exception as e: print(e) cfnresponse.send(event, context, cfnresponse.FAILED, response_data) FunctionName: !Sub "${Prefix}-function" Handler: index.lambda_handler Layers: - !Ref LambdaLayer Runtime: !Ref Runtime Role: !GetAtt FunctionRole.Arn Timeout: !Ref Timeout VpcConfig: SecurityGroupIds: - !Ref FunctionSecurityGroup SubnetIds: - !Ref PrivateSubnet
Code language: YAML (yaml)

Lambda関数で実行するコードをインライン表記で定義します。
詳細につきましては、以下のページをご確認ください。

cfnresponseモジュールを使用して、関数をLambda-backedカスタムリソースとして実装します。
詳細につきましては、以下のページをご確認ください。

今回の構成では、Lambda関数をVPC内に配置します。
VpcConfigプロパティで、関数を配置するサブネットや、適用するセキュリティグループを指定します。

実行するコードの内容ですが、以下の通りです。

  1. CloudFormationテンプレートで定義した環境変数を、os.environにアクセスして取得する。
  2. Boto3でSSM用クライアントオブジェクトを作成する。
  3. クライアントオブジェクトを使用して、SSMパラメータストアにアクセスし、先述のSQL文を取得する。
  4. MySQLクライアントを使用してDBインスタンスに接続し、SQLを1文ずつ実行する。

DBインスタンス接続用のMySQLクライアントですが、MySQL Connectorを使用します。

MySQL :: MySQL Connector/Python Developer Guide

今回はLambdaレイヤーとしてMySQL Connectorパッケージを用意します。
Lambdaレイヤーの詳細につきましては、以下のページをご確認ください。

ちなみに関数用のIAMロールは以下の通りです。

Resources: FunctionRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Action: sts:AssumeRole Principal: Service: - lambda.amazonaws.com ManagedPolicyArns: - arn:aws:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole Policies: - PolicyName: GetSSMParameterPolicy PolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Action: - ssm:GetParameter Resource: - !Sub "arn:aws:ssm:${AWS::Region}:${AWS::AccountId}:parameter/${SQLParameter}"
Code language: YAML (yaml)

AWS管理ポリシーであるAWSLambdaVPCAccessExecutionRoleに加えて、SSMパラメータストアからパラメータを取得する権限を与えます。

続いてCloudFormationカスタムリソース本体を確認します。

Resources: CustomResource: Type: Custom::CustomResource Properties: ServiceToken: !Ref FunctionArn
Code language: YAML (yaml)

先述のLambda関数を指定します。

VPCエンドポイント

今回の構成では、複数のVPCエンドポイントを作成します。
ここではSSM用VPCエンドポイントに注目します。

Resources: SSMEndpoint: Type: AWS::EC2::VPCEndpoint Properties: PrivateDnsEnabled: true SecurityGroupIds: - !Ref EndpointSecurityGroup ServiceName: !Sub "com.amazonaws.${AWS::Region}.ssm" SubnetIds: - !Ref PrivateSubnet VpcEndpointType: Interface VpcId: !Ref VPC
Code language: YAML (yaml)

VPC内に配置したLambda関数からSSMパラメータストアに接続するために、このVPCエンドポイントを使用します。
VPCエンドポイントを作成することで、インターネットを経由することなく、VPC外のAWSリソースに接続することができます。

(参考)RDS DBインスタンス

Resources: DBInstance: Type: AWS::RDS::DBInstance Properties: AllocatedStorage: !Ref DBAllocatedStorage AvailabilityZone: !Sub "${AWS::Region}${AvailabilityZone}" DBInstanceClass: !Ref DBInstanceClass DBInstanceIdentifier: dbinstance DBSubnetGroupName: !Ref DBSubnetGroup Engine: !Ref DBEngine EngineVersion: !Ref DBEngineVersion MasterUsername: !Ref DBMasterUsername MasterUserPassword: !Ref DBMasterUserPassword VPCSecurityGroups: - !Ref DBSecurityGroup
Code language: YAML (yaml)

DBインスタンスです。
CloudFormationカスタムリソースを使用して、DBを初期化するためには、特別な設定は不要です。

(参考)EC2インスタンス

Resources: Instance: Type: AWS::EC2::Instance Properties: IamInstanceProfile: !Ref InstanceProfile ImageId: !Ref ImageId InstanceType: !Ref InstanceType NetworkInterfaces: - DeviceIndex: 0 SubnetId: !Ref PrivateSubnet GroupSet: - !Ref InstanceSecurityGroup UserData: !Base64 | #!/bin/bash -xe yum update -y yum install -y mariadb
Code language: YAML (yaml)

ユーザーデータでインスタンスの初期化処理を定義します。
EC2インスタンスからMySQLタイプのDBインスタンスに接続するために、MySQLクライアントパッケージをインストールします。
詳細につきましては、以下のページをご確認ください。

環境構築

CloudFormationを使用して、本環境を構築し、実際の挙動を確認します。

事前準備

事前準備として、Lambdaレイヤー用のデプロイパッケージを用意します。
具体的には、以下のコマンドを実行します。

$ mkdir python $ pip3 install mysql-connector-python -t ./python $ zip -r layer.zip python
Code language: Bash (bash)

作成したデプロイパッケージをS3バケットにアップロードします。

CloudFormationスタックを作成し、スタック内のリソースを確認する

CloudFormationスタックを作成します。
スタックの作成および各スタックの確認方法については、以下のページをご確認ください。

各スタックのリソースを確認した結果、今回作成された主要リソースの情報は以下の通りです。

  • RDS DBインスタンス:dbinstance
  • RDS DBインスタンスのエンドポイント:dbinstance.cl50iikpthxs.ap-northeast-1.rds.amazonaws.com
  • EC2インスタンス:i-0b3d18375ca48f06b
  • Lambda関数:fa-062-function
  • SSMパラメータストアのパラメータ:fa-062

AWS Management Consoleから各リソースを確認します。
まずCloudFormationカスタムリソースを確認します。

Result of Custom Resource 1.
Result of Custom Resource 2.

Lambda関数とカスタムリソース本体が正常に作成されていることがわかります。

次にSSMパラメータストアに保存されている値を確認します。

SSM Parameter Store.

DB初期化用のSQL文が保存されていることがわかります。

動作確認

準備が整いましたので、実際の動作を確認します。

Lambda関数の実行結果

まずLambda関数の実行結果を、CloudWatch Logsのロググループで確認します。

Result of Custom Resource 3.

ログから、SSMパラメータストアに保存されていたSQLが、1文ずつ実行されていることがわかります。
つまりDB初期化処理が正常に実行されたということです。

そしてCloudFormationカスタムリソースとして、関数が「SUCCESS」を返していることもわかります。
つまり関数はカスタムリソースとして正常に動作したということです。

EC2インスタンス

次にEC2インスタンスに接続し、DBの初期化状況を確認します。

インスタンスへのアクセスはSSM Session Managerを使用します。

% aws ssm start-session --target i-0b3d18375ca48f06b Starting session with SessionId: root-0b2e1f71f0d2aea98 sh-4.2$
Code language: Bash (bash)

SSM Session Managerの詳細につきましては、以下のページをご確認ください。

まずユーザーデータによるEC2インスタンスの初期化処理の実行状況を確認します。

sh-4.2$ yum list installed | grep mariadb mariadb.aarch64 1:5.5.68-1.amzn2 @amzn2-core mariadb-libs.aarch64 1:5.5.68-1.amzn2 installed sh-4.2$ mysql -V mysql Ver 15.1 Distrib 5.5.68-MariaDB, for Linux (aarch64) using readline 5.1
Code language: Bash (bash)

正常にMySQLクライアントパッケージがインストールされていることがわかります。

このクライアントパッケージを使用して、DBインスタンスに接続します。

sh-4.2$ mysql -h dbinstance.cl50iikpthxs.ap-northeast-1.rds.amazonaws.com -P 3306 -u testuser -p Enter password: Welcome to the MariaDB monitor. Commands end with ; or \g. Your MySQL connection id is 20 Server version: 8.0.28 Source distribution Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others. Type 'help;' or '\h' for help. Type '\c' to clear the current input statement. MySQL [(none)]>
Code language: Bash (bash)

正常に接続することができました。

DBを選択します。

MySQL [(none)]> USE tutorial; Reading table information for completion of table and column names You can turn off this feature to get a quicker startup with -A Database changed MySQL [tutorial]>
Code language: Bash (bash)

DBを変更することができました。
CloudFormationカスタムリソースによる初期化処理が正常に実行されているということです。

最後に全レコードを取得します。

MySQL [tutorial]> select * from planet; +----+---------+ | id | name | +----+---------+ | 1 | Mercury | | 2 | Venus | | 3 | Earth | | 4 | Mars | | 5 | Jupiter | | 6 | Saturn | | 7 | Uranus | | 8 | Neptune | +----+---------+ 8 rows in set (0.01 sec)
Code language: Bash (bash)

テストレコードが返ってきました。
正常にCREATE TABLE文やINSERT文が実行されたということです。

以上の通り、CloudFormationカスタムリソースによって、DBインスタンスを初期化することができました。

まとめ

CloudFormationカスタムリソースを使用して、DBを初期化する方法をご紹介しました。

タイトルとURLをコピーしました