もくじ
おさらい
#2022/9/23一部修正しました。
#User DataをStackにベタ書きしていましたが、Shell(.sh)ファイルにして読み込むようにしました。
前回の記事はこちらです。
構成図
完成形の構成図は以下のとおりです。
今回やること
DatabaseスタックとComputingスタックのコード解説をします。
AWS CDKコード(Database、Computing)
Databaseスタック(Database_stack.py)
RDS MySQLのデプロイが記述されたスタックです。
- Database_stack.py
from aws_cdk import (
Duration,
Stack,
aws_ec2 as ec2,
aws_rds as rds,
aws_secretsmanager as secretsmanager
)
from constructs import Construct
import aws_cdk as cdk
class DatabaseStack(Stack):
def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None:
super().__init__(scope, construct_id, **kwargs)
##############################################
############### Import Section ###############
##############################################
# Import VPC
wp_vpc = ec2.Vpc.from_lookup(self, "Vpc",
vpc_name="wp-dev-vpc"
)
# Import Security Group(EC2)
wp_sg_ec2 = ec2.SecurityGroup.from_lookup_by_name(self, "SgEC2",
vpc=wp_vpc,
security_group_name="wp-dev-sg-ec2"
)
# Import Security Group(RDS)
wp_sg_rds = ec2.SecurityGroup.from_lookup_by_name(self, "SgRDS",
vpc=wp_vpc,
security_group_name="wp-dev-sg-rds"
)
################################################
############### Database Section ###############
################################################
# DB Subnet Group
wp_dbsubnet = rds.SubnetGroup(self, "DBSubnet",
vpc=wp_vpc,
description="Subnet Group for RDS",
removal_policy=cdk.RemovalPolicy.DESTROY,
subnet_group_name="wp-dev-dbsubnet",
vpc_subnets=ec2.SubnetSelection(
subnet_type=ec2.SubnetType.PRIVATE_ISOLATED
)
)
# DB Instance Engine
wp_dbengine = rds.DatabaseInstanceEngine.mysql(
version=rds.MysqlEngineVersion.VER_5_7_37
)
# Database Credential
wp_secret_rds = rds.DatabaseSecret(self, "RdsSecret",
username="root",
secret_name="wp-dev-secret-rds"
)
wp_credentials_rds = rds.Credentials.from_secret(
secret=wp_secret_rds
)
# RDS
wp_rds = rds.DatabaseInstance(self, "Rds",
vpc=wp_vpc,
engine=wp_dbengine,
instance_type=ec2.InstanceType.of(
ec2.InstanceClass.BURSTABLE3,
ec2.InstanceSize.MICRO
),
instance_identifier="wp-dev-rds",
multi_az=True,
publicly_accessible=False,
removal_policy=cdk.RemovalPolicy.DESTROY,
security_groups=[wp_sg_rds],
subnet_group=wp_dbsubnet,
vpc_subnets=ec2.SubnetSelection(subnet_type=ec2.SubnetType.PRIVATE_ISOLATED),
credentials=wp_credentials_rds,
allocated_storage=20,
backup_retention=Duration.days(0),
delete_automated_backups=True,
deletion_protection=False,
port=3306
)
######################################################
############### Add Allow Rule Section ###############
######################################################
# Add Allow Connection to RDS
wp_rds.connections.allow_from(wp_sg_ec2, ec2.Port.tcp(3306))
コード解説(Database_stack.py)
- 20〜22行目
Networkスタックで作成したVPCをインポートしています。
- 25〜34行目
Networkスタックで作成したEC2とRDSのセキュリティグループをインポートしています。
- 41〜49行目
DBのサブネットグループを作成しています。
- 52〜54行目
DBエンジンを指定しています。MysqlEngineVersion.VER_5_7_37
と記述してMySQLのバージョン5.7.37を使用するように指定しています。
- 57〜63行目
Secrets Managerにシークレットを作成しています。
RDSの管理者ユーザーはroot
にしています。
- 66〜86行目
RDSを作成しています。publicly_accessible
をFalse
としてインターネットアクセスを不可としています。credentials
に先ほど作成したSecrets Managerのシークレットを渡しています。
- 93行目
RDSに対して、EC2との3306ポートでの接続を許可しています。
Computingスタック(Computing_stack.py)
#2022/9/23一部修正。User DataをShell(.sh)ファイルから読み込むように修正しました。
一番最後にデプロイするスタックです。
- Computing_stack.py
from aws_cdk import (
Duration,
Stack,
aws_ec2 as ec2,
aws_elasticloadbalancingv2 as elbv2,
aws_autoscaling as autoscaling,
aws_iam as iam,
)
from constructs import Construct
import aws_cdk as cdk
from aws_cdk.aws_s3_assets import Asset
class ComputingStack(Stack):
def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None:
super().__init__(scope, construct_id, **kwargs)
##############################################
############### Import Section ###############
##############################################
# Import VPC
wp_vpc = ec2.Vpc.from_lookup(self, "Vpc",
vpc_name="wp-dev-vpc"
)
# Import Security Group(EC2)
wp_sg_ec2 = ec2.SecurityGroup.from_lookup_by_name(self, "SgEC2",
vpc=wp_vpc,
security_group_name="wp-dev-sg-ec2"
)
# Import Security Group(EFS)
wp_sg_efs = ec2.SecurityGroup.from_lookup_by_name(self, "SgEFS",
vpc=wp_vpc,
security_group_name="wp-dev-sg-efs"
)
# Import Security Group(RDS)
wp_sg_rds = ec2.SecurityGroup.from_lookup_by_name(self, "SgRDS",
vpc=wp_vpc,
security_group_name="wp-dev-sg-rds"
)
# Import Security Group(ELB)
wp_sg_elb = ec2.SecurityGroup.from_lookup_by_name(self, "SgELB",
vpc=wp_vpc,
security_group_name="wp-dev-sg-elb"
)
# Import Secrurity Group(SSM)
wp_sg_ssm = ec2.SecurityGroup.from_lookup_by_name(self, "SgSSM",
vpc=wp_vpc,
security_group_name="wp-dev-sg-ssm"
)
# Import IAM Role(EC2)
wp_role_ec2 = iam.Role.from_role_name(self, "RoleEC2",
role_name="wp-dev-role-ec2"
)
##############################################
############### Server Section ###############
##############################################
# Upload Shell Script(User Data) to s3
asset = Asset(self, "Asset",
path="Computing/User_data.sh"
)
# Create User Data Instance
wp_userdata = ec2.UserData.for_linux()
# Launch Template
wp_lt = ec2.LaunchTemplate(self, "LaunchTemplate",
launch_template_name="wp-dev-lt",
instance_initiated_shutdown_behavior=ec2.InstanceInitiatedShutdownBehavior.TERMINATE,
security_group=wp_sg_ec2,
instance_type=ec2.InstanceType.of(
instance_class=ec2.InstanceClass.BURSTABLE2,
instance_size=ec2.InstanceSize.MICRO
),
role=wp_role_ec2,
user_data=wp_userdata,
machine_image=ec2.AmazonLinuxImage(
generation=ec2.AmazonLinuxGeneration.AMAZON_LINUX_2
)
# # Set the AMI from EC2 after installed WordPress
# machine_image=ec2.MachineImage.generic_linux(
# {"ap-northeast-1": "ami-XXXXXXXXXXXXXXXXX"}
# )
)
# Grant Read to Role of Template
asset.grant_read(wp_lt.role)
# Download Shell Script(User Data) to local path
wp_local_path = wp_userdata.add_s3_download_command(
bucket=asset.bucket,
bucket_key=asset.s3_object_key,
region="ap-northeast-1"
)
# Add User Data to Template
wp_lt.user_data.add_execute_file_command(
file_path=wp_local_path
)
# Auto Scaling Group
wp_asg = autoscaling.AutoScalingGroup(self, "Asg",
vpc = wp_vpc,
auto_scaling_group_name="wp-dev-asg",
launch_template=wp_lt,
health_check=autoscaling.HealthCheck.elb(
grace=cdk.Duration.seconds(30)
),
vpc_subnets=ec2.SubnetSelection(subnet_type=ec2.SubnetType.PRIVATE_WITH_NAT),
desired_capacity=1,
min_capacity=1,
max_capacity=1
)
#####################################################
############### Load Balancer Section ###############
#####################################################
# Application Load Balancer
wp_alb = elbv2.ApplicationLoadBalancer(self, "Alb",
vpc=wp_vpc,
http2_enabled=False,
ip_address_type=elbv2.IpAddressType.IPV4,
security_group=wp_sg_elb,
internet_facing=True,
load_balancer_name="wp-dev-alb",
vpc_subnets=ec2.SubnetSelection(subnet_type=ec2.SubnetType.PUBLIC)
)
# Target Group
wp_tg = elbv2.ApplicationTargetGroup(self, "TargetGroup",
vpc=wp_vpc,
port=80,
target_group_name="wp-dev-tg",
health_check=elbv2.HealthCheck(
enabled=True,
protocol=elbv2.Protocol.HTTP,
port="80",
path="/readme.html",
timeout=cdk.Duration.seconds(25), # Defaultis 5
unhealthy_threshold_count=5 # Default is 2
),
target_type=elbv2.TargetType.INSTANCE
)
# Add Targets To Auto Scaling Group
wp_tg.add_target(wp_asg)
# Add Listner To ELB
wp_alb.add_listener("AddListener",
port=80,
default_target_groups=[wp_tg]
)
######################################################
############### Add Allow Rule Section ###############
######################################################
# Add Allow Connection to EC2
wp_lt.connections.allow_from(wp_sg_elb, ec2.Port.tcp(80)) # Allow HTTP from ALB
wp_lt.connections.allow_to(wp_sg_ssm, ec2.Port.tcp(443)) # For Acces to EC2 by Sessions Manager
wp_lt.connections.allow_to_any_ipv4(ec2.Port.tcp(80)) # For EC2 installs some packages by User Data. Disable after installing wordPress.
wp_lt.connections.allow_to_any_ipv4(ec2.Port.tcp(443)) # For EC2 installs some packages by User Data. Disable after installing wordPress.
# Add Allow Connection to ELB
wp_alb.connections.allow_from_any_ipv4(ec2.Port.tcp(80))
コード解説(Computing_stack.py)
- 22〜59行目
Networkスタックで作成したVPCとセキュリティグループとIAMロールをインポートしています。 - 66〜68行目
User_data.shファイルをS3にアップロードしています。 - 71行目
Linux用のUserData
インスタンスを生成しています。これをLaunchTemplate
インスタンスに配置して、また、UserDataのShellスクリプトを追加します。 - 74〜86行目
起動テンプレートを作成しています。security_group
でEC2のセキュリティグループを指定しています。instance_type
でインスタンスのタイプ(クラスとサイズ)を指定しています。role
でEC2にIAMロールをアタッチしています。user_data
で先ほどのユーザーデータを渡しています。machine_image
で最新のAmazon Linux 2を指定しています。 - 87〜90行目
ここはまだ使いません。一度Computingスタックのデプロイが完了してWordPressがEC2にインストールされたら、そこからカスタムAMIを作成してから使用します。 - 93行目
S3バケットにアップロードしたUserDataのShellスクリプトを起動テンプレートが読み込めるようにします。 - 96〜100行目
S3にアップロードしたShellファイルをEC2インスタンスのローカルにダウンロードします。 - 103〜105行目
ダウンロードしたUserDataのシェルファイルを読み込むように起動テンプレートに追加します。 - 108〜119行目
Auto Scalingグループを作成しています。desired_capacity
(希望容量)とmin_capacity
(最小容量)とmax_capacity
(最大容量)はいったん1
とします。理由はデプロイされたEC2からカスタムAMIを作成するためです。
- 126〜134行目
ALBを作成しています。
今回はHTTP接続で受け付けるので、http2_enabled
はFalse
にしています。
- 137〜150行目
ターゲットグループを作成しています。health_check
について、ELBとEC2の間はHTTP通信となるので、port
は80
です。path
は/readme.html
としています。
ユーザーデータでEC2にWordPressをインストールしますが、少し時間がかかるのでヘルスチェックのtimeout
を25秒にしています(デフォルトは5秒)。また、ヘルスチェック失敗とみなされる回数(unhealthy_threshold_count
)を5回にしています(デフォルトは2回)。
これらはカスタムAMIを設定したらデフォルトに戻してもいいかもしれません。
- 153行目
ターゲットグループにAuto Scalingグループを追加しています。
- 156〜159行目
ターゲットグループをELBのリスナーに追加しています。
- 166〜169行目
起動テンプレート(EC2)のセキュリティグループに接続許可設定を入れています。
・ELBからの80ポートでの通信許可。
・セッションマネージャーの接続許可。
・EC2からインターネットへHTTP接続許可。
・EC2からインターネットへHTTPS接続許可。
- 172行目
ELBのセキュリティグループに接続許可設定を入れています。
・インターネットに面しているので、すべての接続元から80ポートでの通信を許可。
ユーザーデータの解説
ユーザーデータのShellを解説します。
#/bin/bash
MY_REGION="ap-northeast-1" # Set the AWS Region you deploy
WP_SITENAME="TestBlog" # Web Site Name for WordPress
WP_USERNAME="wpuser" # Admin User Name for WordPress
WP_PASSWORD="*************" # Admin Password for WordPress
WP_EMAIL="test@test.com" # Email Address for WordPress
WP_DBNAME="wordpress" # Database Name for WordPress in MySQL
# Get EFS ID
EFS_ID=$(aws efs describe-file-systems --region ${MY_REGION} | \
grep FileSystemId | cut -d':' -f2 | tr -d '\042 ,')
# Get username, password and hostname of MySQL
DB_USERNAME=$(aws secretsmanager get-secret-value --secret-id wp-dev-secret-rds --region ${MY_REGION} | \
grep SecretString | cut -d'\' -f22 | tr -d '\042')
DB_PASSWORD=$(aws secretsmanager get-secret-value --secret-id wp-dev-secret-rds --region ${MY_REGION} | \
grep SecretString | cut -d'\' -f4 | tr -d '\042')
DB_HOSTNAME=$(aws secretsmanager get-secret-value --secret-id wp-dev-secret-rds --region ${MY_REGION} | \
grep SecretString | cut -d'\' -f18 | tr -d '\042')
# Get DNS name of ELB
ELB_DNSNAME=$(aws elbv2 describe-load-balancers --region ${MY_REGION} | \
grep DNSName | cut -d':' -f2 | tr -d '\042 ,')
# Uninstall mariadb-libs and Install Apache
sudo yum -y remove mariadb-libs \
&& sudo yum -y update \
&& sudo yum -y install httpd \
&& sudo systemctl start httpd \
&& sudo systemctl enable httpd
# install php8.0 and wp-cli
sudo amazon-linux-extras install -y php8.0 \
&& sudo curl -O "https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar" \
&& sudo chmod +x wp-cli.phar \
&& sudo mv wp-cli.phar /usr/local/bin/wp
# Make Document root Directory and mount efs on it
sudo mkdir -p /var/www/html/ \
&& sudo yum install -y amazon-efs-utils \
&& $(echo "mount -t efs -o tls ${EFS_ID}:/ /var/www/html") \
&& sudo yum -y install "https://dev.mysql.com/get/mysql80-community-release-el7-6.noarch.rpm"
# Disable MySQL5.7 and enable MySQL8.0
sudo yum-config-manager --disable mysql80-community \
&& sudo yum-config-manager --enable mysql57-community \
&& sudo rpm --import "https://repo.mysql.com/RPM-GPG-KEY-mysql-2022" \
&& sudo yum -y install mysql-community-client
# Change files permission, download wordpress module and set params of wordpress
sudo chown apache:apache /var/www/html/ \
&& sudo -u apache /usr/local/bin/wp core download --locale=ja --path=/var/www/html \
&& $(echo "sudo -u apache /usr/local/bin/wp core config \
--dbname=$WP_DBNAME --dbuser=$DB_USERNAME --dbpass=$DB_PASSWORD --dbhost=$DB_HOSTNAME \
--path=/var/www/html") \
&& sudo -u apache /usr/local/bin/wp db create --path=/var/www/html \
&& $(echo "sudo -u apache /usr/local/bin/wp core install --url=$ELB_DNSNAME --title=$WP_SITENAME \
--admin_user=$WP_USERNAME --admin_password=$WP_PASSWORD --admin_email=$WP_EMAIL \
--path=/var/www/html")
# Set auto mount setting to /etc/fstab
sudo echo "${EFS_ID}:/ /var/www/html/ efs defaults,_netdev 0 0" | sudo tee -a /etc/fstab >/dev/null
# Restart Apache
sudo systemctl restart httpd
ユーザーデータでWordPressに必要なモジュールを強引にインストールしています。
ユーザーデータで実行していることを大まかに書くと以下のようになります。
- WordPressのサイト名、ユーザー名、パスワード、メールアドレス、データベース名をシェル変数に格納する。
aws cli
を実行して、EFSファイルシステムID、RDSユーザー名、RDSパスワード、RDSホスト名、ELBのDNS名を取得して、シェル変数に格納する。- PHPをインストールする。
wp-cli
をインストールする。- EFSをマウントする。
- Apacheをインストールする。
- EFSをマウントする。
- MySQLクライアントをインストールする。
- WordPressモジュールをマウントしたEFSにダウンロードする。
- wp-config.phpを生成する。
- WordPressデータベースを作成する。
- WordPressをインストールする。
/etc/fstab
にEFSのマウント設定を入れる。- Apacheをリスタートする。
- 3〜8行目
WordPressのサイト名、ユーザー名、パスワード、メールアドレス、データベース名、リージョン名をシェル変数に格納しています。
- 11〜12行目
aws cli
を使用してEFSのIDを取得し、シェル変数EFS_ID
に格納しています。
- 15〜20行目
aws cli
を使用してRDSのユーザー名、パスワード、ホスト名を取得して、それぞれシェル変数DB_USERNAME
、DB_PASSWORD
、DB_HOSTNAME
に格納しています。
- 23〜24行目
aws cli
を使用してELBのDNS名を取得して、シェル変数ELB_DNSNAME
に格納しています。
- 27〜31行目
mariadb-libs
を削除してyum update
をしてApacheをインストールしています。
- 34〜37行目
phpとwp-cli
をインストールしています。
- 40〜43行目
amazon-efs-utils
をインストールしてEFSファイルシステムをマウントポイント/var/www/html/
にマウントしています。
- 46〜49行目
MySQLクライアントをインストールしています。
- 52〜60行目
WordPressモジュールをダウンロードし、wp-config.php
を作成して、RDSにwordpress
データベースを作成しています。これらはwp-cli
を使って行っています。
- 63行目
起動時にEFSをマウントできるようにするため、/etc/fstab
に設定を入れています。
- 66行目
Apacheをリスタートしています。
次回は
すべてのスタックをデプロイして、WordPressが構築されることを確認します。
その後、作成されたEC2からカスタムAMIを作成して、カスタムAMIからEC2が起動されるようにAWS CDKのコードを一部修正します。
コメント