AWS CDK(Python)だけでWordPressサイトをデプロイする(ELB,EC2,EFS,RDS)

もくじ

おさらい

#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_accessibleFalseとしてインターネットアクセスを不可としています。
    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_enabledFalseにしています。
     
  • 137〜150行目
    ターゲットグループを作成しています。
    health_checkについて、ELBとEC2の間はHTTP通信となるので、port80です。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に必要なモジュールを強引にインストールしています。

ユーザーデータで実行していることを大まかに書くと以下のようになります。

  1. WordPressのサイト名、ユーザー名、パスワード、メールアドレス、データベース名をシェル変数に格納する。
  2. aws cliを実行して、EFSファイルシステムID、RDSユーザー名、RDSパスワード、RDSホスト名、ELBのDNS名を取得して、シェル変数に格納する。
  3. PHPをインストールする。
  4. wp-cliをインストールする。
  5. EFSをマウントする。
  6. Apacheをインストールする。
  7. EFSをマウントする。
  8. MySQLクライアントをインストールする。
  9. WordPressモジュールをマウントしたEFSにダウンロードする。
  10. wp-config.phpを生成する。
  11. WordPressデータベースを作成する。
  12. WordPressをインストールする。
  13. /etc/fstabにEFSのマウント設定を入れる。
  14. Apacheをリスタートする。
  • 3〜8行目
    WordPressのサイト名、ユーザー名、パスワード、メールアドレス、データベース名、リージョン名をシェル変数に格納しています。
     
  • 11〜12行目
    aws cliを使用してEFSのIDを取得し、シェル変数EFS_IDに格納しています。
     
  • 15〜20行目
    aws cliを使用してRDSのユーザー名、パスワード、ホスト名を取得して、それぞれシェル変数DB_USERNAMEDB_PASSWORDDB_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のコードを一部修正します。

1 2 3

この記事が気に入ったら
フォローしてね!

よかったらシェアしてね!

コメント

コメントする

コメントは日本語で入力してください。(スパム対策)

CAPTCHA

もくじ
閉じる