이 연재글은 AWS CloudFormation의 3번째 글입니다.

이번장에서는 Cloudformation을 이용하여 Database를 생성하는 실습을 진행해 보겠습니다. AWS에서는 Provisoned rds와 Serverless rds 두 가지 형태의 Database를 제공하고 있습니다. Provisioned는 고전적인 방식으로 고정된 스펙의 인스턴스를 할당받아 Database를 설치하고 사용하는 방식이며 Serverless는 AWS에서 관리하는 Database를 할당받아 사용하는 방식을 말합니다. Serverless는 사용한 만큼만 지불하면 되고 용량을 확장하거나 축소하는 작업은 AWS에서 자동으로 해주기 때문에 관리자가 신경 써야 할 부분이 줄어들어 편리한 점이 많습니다.

Provisioned RDS

Parameters 항목

DB Cluster 이름, 인스턴스 사양, Root 유저, 비번정보를 입력 받도록 설정합니다.

Mappings 항목

CloudFormation 내에서 사용할 공통 변수를 설정합니다. 계정 별로 사용할 변수를 세팅할 수 있는데 실습에서는 한 계정에만 설치할 것이므로 하나의 설정 정보만 세팅합니다.

Resources 항목

RDSDBClusterParameterGroup (AWS::RDS::DBClusterParameterGroup), RDSDBParameterGroup(AWS::RDS::DBParameterGroup)

Database ParameterGroup을 설정합니다. Cluster용, Database용으로 하나씩 생성합니다. 실습에서는 기본 설정을 이용합니다.

DatabaseSubnetGroup (AWS::RDS::DBSubnetGroup)

Database가 설치될 subnet 리스트를 설정합니다.

AuroraSecurityGroup (AWS::EC2::SecurityGroup)

Database 3306 port에 접근할 수 있도록 Security Group을 생성합니다.

DatabaseCluster (AWS::RDS::DBCluster)

DB Cluster를 생성합니다.

DatabasePrimaryInstance (AWS::RDS::DBInstance)

Master DB 인스턴스를 생성합니다.

DatabaseReplicaInstance (AWS::RDS::DBInstance)

Slave(Replica) DB 인스턴스를 생성합니다.

{
    "AWSTemplateFormatVersion" : "2010-09-09",
  
    "Description" : "AWS CloudFormation Template for Provisoned RDS(MySQL) Cluster",

    "Parameters": {
        "DBClusterIdentifier": {
            "AllowedPattern": "^[a-zA-Z]+[0-9a-zA-Z\\-]*$",
            "ConstraintDescription": "must be between 1 to 60 alphanumeric characters.",
            "Description": "1 to 60 alphanumeric characters or hyphens. First character must be a letter. Can't contain two consecutive hyphens. Can't end with a hyphen.",
            "MaxLength": "60",
            "MinLength": "1",
            "Type": "String"
        },
        "DBInstanceClass": {
            "Description" : "The database instance type",
            "Type": "String",
            "Default": "db.t3.small",
            "AllowedValues" : [ "db.t3.small", "db.t3.medium", "db.r5.large", "db.r5.xlarge", "db.r5.2xlarge", "db.r5.4xlarge"],
            "ConstraintDescription" : "must select a valid database instance type."
        },
        "DatabaseUsername": {
            "AllowedPattern": "[a-zA-Z0-9]+",
            "ConstraintDescription": "must be between 1 to 16 alphanumeric characters.",
            "Description": "The database admin account user name, between 1 to 16 alphanumeric characters.",
            "MaxLength": "16",
            "MinLength": "1",
            "Type": "String",
            "Default": "admin"
        },
        "DatabasePassword": {
            "AllowedPattern": "[a-zA-Z0-9!@#$%^&*()]*",
            "ConstraintDescription": "must be between 8 to 41 alphanumeric characters.",
            "Description": "The database admin account password, between 8 to 41 alphanumeric characters.",
            "MaxLength": "41",
            "MinLength": "8",
            "NoEcho": "true",
            "Type": "String"
        }
    },

    "Mappings": {
        "478069740483": {
            "ap-northeast-2": {
                "VpcId": "vpc-0e1a52b8c1cf1cde3",
                "Subnets": ["subnet-0f1bfff74cead2d23", "subnet-06dfc25b4d7352372"],
                "CidrIp": "172.31.0.0/16"
            }
        }
    },

    "Resources" : {

        "RDSDBClusterParameterGroup": {
            "Type": "AWS::RDS::DBClusterParameterGroup",
            "Properties": {
                "Description": "CloudFormation Sample Aurora Cluster Parameter Group",
                "Family": "aurora-mysql5.7",
                "Parameters": {
                    "time_zone": "Asia/Seoul",
                    "character_set_database": "utf8mb4"
                }
            }
        },

        "RDSDBParameterGroup": {
            "Type": "AWS::RDS::DBParameterGroup",
            "Properties": {
                "Description": "CloudFormation Sample MySQL Parameter Group",
                "Family": "aurora-mysql5.7",
                "Parameters": {
                    "sql_mode": "IGNORE_SPACE",
                    "max_allowed_packet": 1024,
                    "innodb_buffer_pool_size": "{DBInstanceClassMemory*3/4}"
                }
            }
        },

        "DatabaseSubnetGroup": {
            "Type": "AWS::RDS::DBSubnetGroup",
            "Properties": {
                "DBSubnetGroupDescription": "CloudFormation managed DB subnet group.",
                "SubnetIds": {"Fn::FindInMap" : [{ "Ref" : "AWS::AccountId" }, { "Ref" : "AWS::Region" }, "Subnets"]},
            }
        },

        "AuroraSecurityGroup" : {
            "Type" : "AWS::EC2::SecurityGroup",
            "Properties" :
            {
               "GroupDescription" : "Security group for DB Instance",
               "GroupName" : {"Fn::Join" : [ "-", [{ "Ref" : "DBClusterIdentifier"}, "RDBSecurityGroup"]]},
               "VpcId" : {"Fn::FindInMap" : [{ "Ref" : "AWS::AccountId" }, { "Ref" : "AWS::Region" }, "VpcId"]},
               "SecurityGroupIngress" : [{
                   "IpProtocol" : "tcp",
                   "FromPort" : "3306",
                   "ToPort" : "3306",
                   "CidrIp" : {"Fn::FindInMap" : [{ "Ref" : "AWS::AccountId" }, { "Ref" : "AWS::Region" }, "CidrIp"]}
              }]
            }
        },

        "DatabaseCluster" : {
            "DependsOn" : ["RDSDBClusterParameterGroup"],
            "Type": "AWS::RDS::DBCluster",
            "Properties" : {
                "DBClusterIdentifier": {"Ref": "DBClusterIdentifier"},
                "Engine": "aurora-mysql",
                "EngineMode": "provisioned",
                "EngineVersion": "5.7.mysql_aurora.2.09.2",
                "MasterUsername": {"Ref": "DatabaseUsername"},
                "MasterUserPassword": {"Ref": "DatabasePassword"},
                "DBSubnetGroupName": {"Ref": "DatabaseSubnetGroup"},
                "VpcSecurityGroupIds": [{ "Ref": "AuroraSecurityGroup"}],
                "DBClusterParameterGroupName" : { "Ref": "RDSDBClusterParameterGroup"},
                "DeletionProtection": false,
                "StorageEncrypted": false
            },
            "DeletionPolicy": "Delete"
        },

        "DatabasePrimaryInstance" : {
            "DependsOn" : ["RDSDBParameterGroup"],
            "Type" : "AWS::RDS::DBInstance",
            "Properties" : {
                "DBInstanceIdentifier" : {"Fn::Join" : [ "-", [{ "Ref" : "DBClusterIdentifier"}, "PrimaryInstance"]]},
                "Engine": "aurora-mysql",
                "AllowMajorVersionUpgrade": false,
                "AutoMinorVersionUpgrade": true,
                "CopyTagsToSnapshot": false,
                "DBClusterIdentifier": { "Ref" : "DatabaseCluster" }, 
                "DBInstanceClass": { "Ref" : "DBInstanceClass" }, 
                "DBParameterGroupName": { "Ref": "RDSDBParameterGroup"},
                "DBSubnetGroupName": { "Ref": "DatabaseSubnetGroup"}
            },
            "DeletionPolicy": "Delete"
        },

        "DatabaseReplicaInstance" : {
            "DependsOn" : ["RDSDBParameterGroup"],
            "Type" : "AWS::RDS::DBInstance",
            "Properties" : {
                "DBInstanceIdentifier" : {"Fn::Join" : [ "-", [{ "Ref" : "DBClusterIdentifier"}, "ReplicaInstance"]]},
                "Engine": "aurora-mysql",
                "AllowMajorVersionUpgrade": false,
                "AutoMinorVersionUpgrade": true,
                "CopyTagsToSnapshot": false,
                "DBClusterIdentifier": { "Ref" : "DatabaseCluster" }, 
                "DBInstanceClass": { "Ref" : "DBInstanceClass" },
                "DBParameterGroupName": { "Ref": "RDSDBParameterGroup"},
                "DBSubnetGroupName": { "Ref": "DatabaseSubnetGroup"}
            },
            "DeletionPolicy": "Delete"
        }
    }
  }

Serverless RDS

Serverless RDS는 Cluster만 생성하고 Instance는 시스템이 자동으로 관리하기 때문에 Provisoned RDS에 비해 Template 내용이 적습니다. 대부분의 작업내용이 Provisoned RDS와 동일하지만, DB Instance를 생성할 필요가 없어 RDSDBParameterGroup, DatabasePrimaryInstance, DatabaseReplicaInstance 생성 작업은 제거 되었습니다.

{
    "AWSTemplateFormatVersion" : "2010-09-09",
  
    "Description" : "AWS CloudFormation Template Sandbox RDS MySQL Cluster",

    "Parameters": {
        "DBClusterIdentifier": {
            "AllowedPattern": "^[a-zA-Z]+[0-9a-zA-Z\\-]*$",
            "ConstraintDescription": "must be between 1 to 60 alphanumeric characters.",
            "Description": "1 to 60 alphanumeric characters or hyphens. First character must be a letter. Can't contain two consecutive hyphens. Can't end with a hyphen.",
            "MaxLength": "60",
            "MinLength": "1",
            "Type": "String"
        },
        "DatabaseUsername": {
            "AllowedPattern": "[a-zA-Z0-9]+",
            "ConstraintDescription": "must be between 1 to 16 alphanumeric characters.",
            "Description": "The database admin account user name, between 1 to 16 alphanumeric characters.",
            "MaxLength": "16",
            "MinLength": "1",
            "Type": "String",
            "Default": "admin"
        },
        "DatabasePassword": {
            "AllowedPattern": "[a-zA-Z0-9]+",
            "ConstraintDescription": "must be between 8 to 41 alphanumeric characters.",
            "Description": "The database admin account password, between 8 to 41 alphanumeric characters.",
            "MaxLength": "41",
            "MinLength": "8",
            "NoEcho": "true",
            "Type": "String"
        }
    },

    "Mappings": {
        "478069740483": {
            "ap-northeast-2": {
                "VpcId": "vpc-0e1a52b8c1cf1cde3",
                "Subnets": ["subnet-0f1bfff74cead2d23", "subnet-06dfc25b4d7352372"],
                "CidrIp": "172.31.0.0/16"
            }
        }
    },

    "Resources" : {
        "RDSDBClusterParameterGroup": {
            "Type": "AWS::RDS::DBClusterParameterGroup",
            "Properties": {
                "Description": "CloudFormation Sample Aurora Cluster Parameter Group",
                "Family": "aurora-mysql5.7",
                "Parameters": {
                    "time_zone": "Asia/Seoul",
                    "character_set_database": "utf8mb4"
                }
            }
        },

        "DatabaseSubnetGroup": {
            "Type": "AWS::RDS::DBSubnetGroup",
            "Properties": {
                "DBSubnetGroupDescription": "CloudFormation managed DB subnet group.",
                "SubnetIds": {"Fn::FindInMap" : [{ "Ref" : "AWS::AccountId" }, { "Ref" : "AWS::Region" }, "Subnets"]}
            }
        },

        "AuroraSecurityGroup" : {
            "Type" : "AWS::EC2::SecurityGroup",
            "Properties" :
            {
               "GroupDescription" : "Security group for Aurora SampleDB DB Instance",
               "GroupName" : {"Fn::Join" : [ "-", [{ "Ref" : "DBClusterIdentifier"}, "RDBSecurityGroup"]]},
               "VpcId" : {"Fn::FindInMap" : [{ "Ref" : "AWS::AccountId" }, { "Ref" : "AWS::Region" }, "VpcId"]},
               "SecurityGroupIngress" : [{
                   "IpProtocol" : "tcp",
                   "FromPort" : "3306",
                   "ToPort" : "3306",
                   "CidrIp" : {"Fn::FindInMap" : [{ "Ref" : "AWS::AccountId" }, { "Ref" : "AWS::Region" }, "CidrIp"]}
              }]
            }
        },

        "DatabaseCluster" : {
            "DependsOn" : ["RDSDBClusterParameterGroup"],
            "Type": "AWS::RDS::DBCluster",
            "Properties" : {
                "DBClusterIdentifier": {"Ref": "DBClusterIdentifier"},
                "Engine": "aurora-mysql",
                "EngineMode": "serverless",
                "EngineVersion": "5.7.mysql_aurora.2.09.2",
                "MasterUsername": {"Ref": "DatabaseUsername"},
                "MasterUserPassword": {"Ref": "DatabasePassword"},
                "DBSubnetGroupName": {"Ref": "DatabaseSubnetGroup"},
                "VpcSecurityGroupIds": [{ "Ref": "AuroraSecurityGroup"}],
                "DBClusterParameterGroupName" : {"Ref": "RDSDBClusterParameterGroup"},
                "DeletionProtection": false
            },
            "DeletionPolicy": "Delete"
        }
    }
  }
  
연재글 이동[이전글] CloudFormation으로 하는 AWS 인프라관리 – Virtual Private Cloud(VPC) 생성하기