😑

#05 AWS CDKの準備(2023.10)

2024/10/22に公開

1. はじめに

CDKを使ってRDSまわりの環境を準備した時のメモです。
RDS構築に必要なネットワーク関連のStackを記載しています。
CDKで構築した環境の上に、SAMとCloud9使い、動作確認用のアプリを構築しています。

セットアップ対象のバージョン

  • nvm 0.39.0
  • python 3.11.2
  • pip 23.2.1
  • aws-cli 1.29.20
  • cdk 2.82.0

今回、WSL2からコマンドを実行しています。
WSL2を初めて構築する人は参考にしてください。
#01 WSLを使った作業環境を準備した時のメモ(2023.10)

First CDK

AWS CDKはじめてのアプリを参照してデプロイ環境を整えます。

command
$ aws sts get-caller-identity --query 'Account' --output text // 操作アカウントの確認
$ aws configure get region // リージョン確認 
$ npm install -g aws-cdk
$ cdk --version
$ mkdir my-project
$ cd my-project
$ cdk init app --language python
$ tree -L 1
.
├── README.md
├── app.py
├── cdk.json
├── my_project
│   ├── __init__.py
│   └── my_project_stack.py
├── requirements-dev.txt
├── requirements.txt
├── source.bat
└── tests
$ source .venv/bin/activate
$ python -m pip install -r requirements.txt

CDKスクリプト作成

my_project_stack.py を書き換えます。
サンプルの内容

  • RDSのインストールと、RDSをデプロイする先のネットワークをセットアップします。

  • サブネットは、PRIVATE_WITH_NATを選択しています。
     - PUBLIC: 公開用パブリックセグメント利用

    • PRIVATE_WITH_NAT: プライベートセグメントだが、NATを通して外部に接続可能
    • PRIVATE_ISOLATED: プライベートセグメント(外部接続不可) ※今回未使用
  • max_azs=2 … AZゾーンを二つにデプロイします。

  • nat_gateways=1 … NATGATEWAYを有効にしています。

  • サブネットマスク: /24 ※AWS/16を推奨

  • セキュリティグループ: EC2,RDSのそれぞれ作成

  • cdk.RemovalPolicy.DESTROY: CDKコマンドから削除を許可する。

  • allow_all_outbound=False : 全てのアウトバンドをブロックする

  • DBにはアカウント:rootでアクセスします。パスワードは、AWSマネージド画面から設定する

  • STACKファイル(my_project_stack.py)

python
from aws_cdk import (
    Duration,
    aws_ec2 as ec2,
    aws_rds as rds
)
from constructs import Construct
import aws_cdk as cdk

class RdsStack(cdk.Stack):

    def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None:
        super().__init__(scope, construct_id, **kwargs)

        #------------------------------
        # region CONSTRUCT COMMON TAG
        cdk.Tags.of(scope).add("owner", "SAM SUN")
        cdk.Tags.of(scope).add("start", "20230101")
        cdk.Tags.of(scope).add("end", "20231231")
        cdk.Tags.of(scopt).add("purpose", "FIRST CDK")
        #------------------------------
        # endregion

        #------------------------------
        #region VPC & Subnet
        my_vpc = ec2.Vpc(self, "Vpc",
            vpc_name="my-vpc",
            cidr="10.0.0.0/16",
            max_azs=2,
            nat_gateways=1,
            subnet_configuration=[
                ec2.SubnetConfiguration(
                    name="my-public",
                    cidr_mask=24,
                    subnet_type=ec2.SubnetType.PUBLIC
                ),
                ec2.SubnetConfiguration(
                    name="my-private",
                    cidr_mask=24,
                    subnet_type=ec2.SubnetType.PRIVATE_WITH_NAT
                )
            ]
        # DB Subnet
        my_dbsubnet = rds.SubnetGroup(self, "DBSubnet",
            vpc=my_vpc,
            description="Subnet Group for RDS",
            removal_policy=cdk.RemovalPolicy.DESTROY,
            subnet_group_name="my-private",
            vpc_subnets=ec2.SubnetSelection(
                subnet_type=ec2.SubnetType.PRIVATE_WITH_NAT
            )
        )
        #endregion
        #------------------------------

        #------------------------------
        # region Security Group
        Security Group(EC2)
        my_sg_ec2 = ec2.SecurityGroup(self, "SgEC2",
            vpc=my_vpc,
            security_group_name="my-sg-ec2",
            description="Security Group for EC2",
            allow_all_outbound=False
        )
        # Security Group(RDS)
        my_sg_rds = ec2.SecurityGroup(self, "SgRDS",
            vpc=my_vpc,
            security_group_name="my-sg-rds",
            description="Security Group for RDS",
            allow_all_outbound=False
        )
        #------------------------------
        # endregion Security Group

        #------------------------------
        # region DB SETUP

        #DB Instance Engine
        my_dbengine = rds.DatabaseInstanceEngine.mysql(
            version=rds.MysqlEngineVersion.VER_8_0_28
        )
        
        # Database Credential
        my_secret_rds = rds.DatabaseSecret(self, "RdsSecret",
            username="root",
            secret_name="my-secret-rds"
        )
        my_credentials_rds = rds.Credentials.from_secret(
            secret=my_secret_rds
        )
        # RDS
        my_rds = rds.DatabaseInstance(self, "Rds",
            vpc=my_vpc,
            engine=my_dbengine,
            instance_type=ec2.InstanceType.of(
                ec2.InstanceClass.BURSTABLE2,
                ec2.InstanceSize.SMALL
            ),
            instance_identifier="my-rds",
            multi_az=True,
            publicly_accessible=False,
            removal_policy=cdk.RemovalPolicy.DESTROY,
            security_groups=[my_sg_rds],
            subnet_group=my_dbsubnet,
            vpc_subnets=ec2.SubnetSelection(subnet_type=ec2.SubnetType.PRIVATE_WITH_NAT),
            credentials=my_credentials_rds,
            allocated_storage=20,
            backup_retention=Duration.days(0),
            delete_automated_backups=True,
            deletion_protection=False,
            port=3306
        )

        # Add Allow Connection to RDS
        my_rds.connections.allow_from(my_sg_ec2, ec2.Port.tcp(3306))
        #------------------------------
        # endregion
  • デプロイコマンド
command
$ cdk bootstrap
---
Bootstrapping environment aws://************/ap-northeast-1...
Trusted accounts for deployment: (none)
Trusted accounts for lookup: (none)
Using default execution policy of 'arn:aws:iam::aws:policy/AdministratorAccess'. Pass '--cloudformation-execution-policies' to customize.
CDKToolkit: creating CloudFormation changeset...
Environment aws://************/ap-northeast-1 bootstrapped.
---
$ cdk deploy

動作確認用アプリケーション作成

mysqlを利用した時のメモをアップしました。
作業メモ#05 mysql準備

動作確認用アプリケーション(Lamda)作成

my_project_stack.py を書き換えます。

import json
import sys
import os
import mysql.connector

DB_USER = "user"
DB_PASSWORD = "123456789Aa#"
DB_HOST = "xxxxxxxx.xxxxxxxxxxxx.ap-northeast-1.rds.amazonaws.com"
DB_NAME = "sample"
DB_PORT = "3306"

conn = mysql.connector.connect(
	host=DB_HOST,
	port=DB_PORT,
	user=DB_USER,
	password=DB_PASSWORD,
	database=DB_NAME
)
cur = conn.cursor(buffered=True)

def lambda_handler(event, context):

	try:
		sql_str = 'select * from student;'
		cur.execute(sql_str)
		cur.close()
		res = {"message": "Success"}
		return {
			'statusCode': 200,
			'body': "Success"
		}
	except Exception as e:
		cur.close()
		print(e)
		return {
			'statusCode': 403,
			'body': "No Stuent found"
		}

Discussion