AWSTemplateFormatVersion: "2010-09-09" Description: "buield default vpc" Parameters: KeyNamePublic: Description: "Name of an existing EC2 KeyPair to enable SSH access to the public instances" Type: "AWS::EC2::KeyPair::KeyName" ConstraintDescription: "must be the name of an existing EC2 KeyPair." KeyNamePrivate: Description: "Name of an existing EC2 KeyPair to enable SSH access to the private instances" Type: "AWS::EC2::KeyPair::KeyName" ConstraintDescription: "must be the name of an existing EC2 KeyPair." # 1. t2.micro 는 프리티어일 경우만 사용 한다. # 2. 되도록 최신 세대를 사용하도록한다. # 3. 되도록 작은 인스턴스로 시작한다. InstanceType: Description: "WebServer EC2 instance type" Type: "String" Default: "t4g.micro" AllowedValues: - "t2.micro" - "t4g.micro" - "t4g.small" - "t4g.medium" ConstraintDescription: "must be a valid EC2 instance type." # ex) 사무실 IP or 개인 PC IP SSHLocation: Description: "The IP address range that can be used to SSH to the EC2 instances" Type: "String" MinLength: "9" MaxLength: "18" Default: "0.0.0.0/0" AllowedPattern: "(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})/(\\d{1,2})" ConstraintDescription: "must be a valid IP CIDR range of the form x.x.x.x/x." # db.t2.micro 는 프리티어일 경우 DBClass: Description: "Database instance class" Type: "String" Default: "db.t3.micro" AllowedValues: - "db.t2.micro" # 프리티어 가능한 경우만. - "db.t3.micro" - "db.t3.small" - "db.t3.medium" - "db.t3.large" - "db.m6g.large" ConstraintDescription: "must select a valid database instance type." DBName: Description: "database and database admin name" Type: "String" MinLength: "1" MaxLength: "64" AllowedPattern: "[a-zA-Z][a-zA-Z0-9]*" ConstraintDescription: "must begin with a letter and contain only alphanumeric characters." DBPassword: Description: "database admin account password" Type: "String" MinLength: "8" MaxLength: "41" AllowedPattern: "[a-zA-Z0-9]*" ConstraintDescription: "must contain only alphanumeric characters." Mappings: AWSInstanceType: # t4 세대는 arm 기반 t2.micro: Ami: "ami-086a3601cd7a83590" t4g.micro: Ami: "ami-0c4151369dfab2351" t4g.small: Ami: "ami-0c4151369dfab2351" t4g.medium: Ami: "ami-0c4151369dfab2351" Resources: VPC: Type: "AWS::EC2::VPC" Properties: CidrBlock: "10.0.0.0/16" InstanceTenancy: "default" EnableDnsSupport: true EnableDnsHostnames: true Tags: - Key: Name Value: !Join ["", [!Ref AWS::StackName, "-VPC"]] - Key: Application Value: !Ref AWS::StackName InternetGateway: Type: "AWS::EC2::InternetGateway" Properties: Tags: - Key: Name Value: !Join ["", [!Ref AWS::StackName, "-Gateway"]] - Key: Application Value: !Ref AWS::StackName InternetGatewayAttach: Type: "AWS::EC2::VPCGatewayAttachment" Properties: VpcId: !Ref VPC InternetGatewayId: !Ref InternetGateway SubnetPrivateA: Type: "AWS::EC2::Subnet" Properties: CidrBlock: "10.0.0.0/24" AvailabilityZone: "ap-northeast-2a" VpcId: !Ref VPC Tags: - Key: "Name" Value: !Join ["", [!Ref AWS::StackName, "-Subnet-A-private"]] - Key: Application Value: !Ref AWS::StackName SubnetPrivateC: Type: "AWS::EC2::Subnet" Properties: CidrBlock: "10.0.1.0/24" AvailabilityZone: "ap-northeast-2c" VpcId: !Ref VPC Tags: - Key: "Name" Value: !Join ["", [!Ref AWS::StackName, "-Subnet-C-private"]] - Key: Application Value: !Ref AWS::StackName SubnetPublicA: Type: "AWS::EC2::Subnet" Properties: CidrBlock: "10.0.100.0/24" AvailabilityZone: "ap-northeast-2a" VpcId: !Ref VPC MapPublicIpOnLaunch: true Tags: - Key: "Name" Value: !Join ["", [!Ref AWS::StackName, "-Subnet-A-public"]] - Key: Application Value: !Ref AWS::StackName SubnetPublicC: Type: "AWS::EC2::Subnet" Properties: CidrBlock: "10.0.101.0/24" AvailabilityZone: "ap-northeast-2c" VpcId: !Ref VPC MapPublicIpOnLaunch: true Tags: - Key: "Name" Value: !Join ["", [!Ref AWS::StackName, "-Subnet-C-public"]] - Key: Application Value: !Ref AWS::StackName DBSubnetGroup: Type: "AWS::RDS::DBSubnetGroup" Properties: DBSubnetGroupDescription: "db subnet group" SubnetIds: [!Ref SubnetPrivateA, !Ref SubnetPrivateC] Tags: - Key: "Name" Value: !Join ["", [!Ref AWS::StackName, "-DBSubnetGroup"]] - Key: Application Value: !Ref AWS::StackName # NetworkAclId: !Ref NACLPrivate RouteTablePrivate: Type: "AWS::EC2::RouteTable" Properties: VpcId: !Ref VPC Tags: - Key: "Name" Value: !Join ["", [!Ref AWS::StackName, "-RouteTable-Private"]] - Key: Application Value: !Ref AWS::StackName RouteTablePublic: Type: "AWS::EC2::RouteTable" Properties: VpcId: !Ref VPC Tags: - Key: "Name" Value: !Join ["", [!Ref AWS::StackName, "-RouteTable-Public"]] - Key: Application Value: !Ref AWS::StackName SubNetRoutePrivateA: Type: "AWS::EC2::SubnetRouteTableAssociation" Properties: RouteTableId: !Ref RouteTablePrivate SubnetId: !Ref SubnetPrivateA SubNetRoutePrivateC: Type: "AWS::EC2::SubnetRouteTableAssociation" Properties: RouteTableId: !Ref RouteTablePrivate SubnetId: !Ref SubnetPrivateC SubNetRoutePublicA: Type: "AWS::EC2::SubnetRouteTableAssociation" Properties: RouteTableId: !Ref RouteTablePublic SubnetId: !Ref SubnetPublicA SubNetRoutePublicC: Type: "AWS::EC2::SubnetRouteTableAssociation" Properties: RouteTableId: !Ref RouteTablePublic SubnetId: !Ref SubnetPublicC InternetGatewayRoutePublic: Type: "AWS::EC2::Route" Properties: DestinationCidrBlock: "0.0.0.0/0" RouteTableId: !Ref RouteTablePublic GatewayId: !Ref InternetGateway # NatGateway 설정을 하지 않으면 private subnet 에 있는 인스턴스들이 업데이트가 안된다. EIP: DependsOn: InternetGatewayAttach Type: "AWS::EC2::EIP" Properties: Domain: "vpc" NatGateway: Type: "AWS::EC2::NatGateway" Properties: Tags: - Key: "Name" Value: !Join ["", [!Ref AWS::StackName, "-NatGateway"]] - Key: Application Value: !Ref AWS::StackName AllocationId: !GetAtt ["EIP", "AllocationId"] ConnectivityType: "public" SubnetId: !Ref SubnetPublicC NatGatewayRoutePrivate: Type: "AWS::EC2::Route" Properties: DestinationCidrBlock: "0.0.0.0/0" RouteTableId: !Ref RouteTablePrivate NatGatewayId: !Ref NatGateway SecurityGroupBastion: Type: AWS::EC2::SecurityGroup Properties: GroupName: !Join ["", [!Ref AWS::StackName, "-Bastion"]] GroupDescription: Bastion Security Group VpcId: !Ref VPC Tags: - Key: Name Value: !Join ["", [!Ref AWS::StackName, "-Bastion"]] - Key: Application Value: !Ref AWS::StackName SecurityGroupIngress: - IpProtocol: tcp FromPort: "22" ToPort: "22" CidrIp: !Ref SSHLocation SecurityGroupELB: Type: AWS::EC2::SecurityGroup Properties: GroupName: !Join ["", [!Ref AWS::StackName, "-ELB"]] GroupDescription: Access to the load balancer that sits in front of ECS VpcId: !Ref VPC SecurityGroupIngress: # Allow access from anywhere to our ECS services - CidrIp: 0.0.0.0/0 IpProtocol: -1 Tags: - Key: Name Value: !Join ["", [!Ref AWS::StackName, "-LoadBalancers"]] - Key: Application Value: !Ref AWS::StackName SecurityGroupWeb: Type: "AWS::EC2::SecurityGroup" Properties: GroupName: !Join ["", [!Ref AWS::StackName, "-Web"]] GroupDescription: "Web Security Group" VpcId: !Ref VPC Tags: - Key: "Name" Value: !Join ["", [!Ref AWS::StackName, "-Web"]] - Key: Application Value: !Ref AWS::StackName SecurityGroupIngress: - SourceSecurityGroupId: !Ref SecurityGroupELB IpProtocol: -1 - SourceSecurityGroupId: !Ref SecurityGroupBastion IpProtocol: "tcp" FromPort: "22" ToPort: "22" SecurityGroupDB: Type: "AWS::EC2::SecurityGroup" Properties: GroupName: !Join ["", [!Ref AWS::StackName, "-DB"]] GroupDescription: "DB Security Group" VpcId: !Ref VPC Tags: - Key: "Name" Value: !Join ["", [!Ref AWS::StackName, "-DB"]] - Key: Application Value: !Ref AWS::StackName SecurityGroupIngress: - IpProtocol: "tcp" FromPort: "3306" ToPort: "3306" SourceSecurityGroupId: !Ref SecurityGroupWeb # Load Balancer setting ApplicationLoadBalancer: Type: "AWS::ElasticLoadBalancingV2::LoadBalancer" Properties: Name: !Ref AWS::StackName Subnets: - !Ref SubnetPublicA - !Ref SubnetPublicC SecurityGroups: - !Ref SecurityGroupELB Tags: - Key: Name Value: !Join ["", [!Ref AWS::StackName, " LoadBalancers"]] - Key: Application Value: !Ref AWS::StackName ALBTargetGroup: Type: "AWS::ElasticLoadBalancingV2::TargetGroup" Properties: Name: !Ref AWS::StackName # aws 에서 healthcheck 할 파일. 각자 상황에 따라 설정해준다. HealthCheckPath: "/healthcheck.html" HealthCheckIntervalSeconds: 30 HealthCheckTimeoutSeconds: 5 HealthyThresholdCount: 2 Port: 80 Protocol: "HTTP" UnhealthyThresholdCount: 5 VpcId: !Ref VPC LoadBalancerListenerHTTP: Type: AWS::ElasticLoadBalancingV2::Listener Properties: LoadBalancerArn: !Ref ApplicationLoadBalancer Port: 80 Protocol: HTTP DefaultActions: - Type: forward TargetGroupArn: !Ref ALBTargetGroup # LoadBalancerListenerHTTPS: # Type: AWS::ElasticLoadBalancingV2::Listener # Properties: # LoadBalancerArn: !Ref ApplicationLoadBalancer # Port: 443 # Protocol: HTTPS # Certificates: # - CertificateArn: !Ref LBCertificateArn # DefaultActions: # - Type: forward # TargetGroupArn: !Ref ALBTargetGroup # private ec2 자원에 접근하기 위한 ec2 Bastion: Type: "AWS::EC2::Instance" DependsOn: "InternetGateway" Properties: Tags: - Key: "Name" Value: !Join ["", [!Ref AWS::StackName, "-Bastion"]] - Key: Application Value: !Ref AWS::StackName # 2021.6.29 기준 월 4200원 수준 InstanceType: "t4g.nano" ImageId: "ami-0c4151369dfab2351" # 개발단계에서 프리티어 사용 가능 할 경우. # InstanceType: "t2.micro" # ImageId: "ami-086a3601cd7a83590" KeyName: !Ref KeyNamePublic NetworkInterfaces: - GroupSet: - !Ref SecurityGroupBastion AssociatePublicIpAddress: true DeviceIndex: "0" SubnetId: !Ref SubnetPublicA UserData: Fn::Base64: !Sub | #!/bin/bash sudo yum -y update sudo amazon-linux-extras install -y php8.0 sudo yum install -y htop nmap git #ec2 초기 설정. EC2Instance: Type: "AWS::EC2::Instance" DependsOn: "InternetGateway" # Metadata: # AWS::CloudFormation::Init: Properties: Tags: - Key: "Name" Value: !Join ["", [!Ref AWS::StackName, "-web"]] - Key: Application Value: !Ref AWS::StackName ImageId: # 기본 linux ami !FindInMap ["AWSInstanceType", !Ref InstanceType, "Ami"] InstanceType: !Ref InstanceType NetworkInterfaces: - GroupSet: - !Ref SecurityGroupWeb DeviceIndex: "0" SubnetId: !Ref SubnetPrivateA KeyName: !Ref KeyNamePrivate UserData: Fn::Base64: !Sub | #!/bin/bash # yum update sudo yum -y update # php, nginx, git 설치 sudo amazon-linux-extras install -y nginx1 php8.0 sudo yum install -y git mariadb-server php-{pear,cgi,common,curl,mbstring,gd,mysqlnd,gettext,bcmath,json,xml,fpm,intl,zip} # nginx 시작 프로그램 등록. sudo systemctl enable nginx service nginx start # php-fpm 설정. php-fpm 실행 권한이 변경된다. sudo sed -i 's|;*user = apache|user = nginx|g' /etc/php-fpm.d/www.conf sudo sed -i 's|;*group = apache|group = nginx|g' /etc/php-fpm.d/www.conf #시작 프로그램 등록 sudo systemctl enable php-fpm service php-fpm restart # group 권한 추가 usermod -a -G nginx ec2-user >> /home/ec2-user/install.log # php.ini 설정변경 ( 기본 설정이 다를 수 있으니 확인이 필요하다.) # configure php # sudo sed -i 's|;*post_max_size = 8M|post_max_size = 50M|g' /etc/php.ini # sudo sed -i 's|;*upload_max_filesize = 2M|upload_max_filesize = 10M|g' /etc/php.ini # sudo sed -i 's|;*max_file_uploads = 20|max_file_uploads = 20|g' /etc/php.ini # 시간대 변경 timedatectl set-timezone Asia/Seoul >> /home/ec2-user/install.log # 디렉토리 권한 변경 sudo chmod 711 /home/ec2-user sudo usermod -a -G nginx ec2-user # Back up nginx config sudo cp -R /etc/nginx /etc/nginx-backup # nginx.conf 파일 수정 cat << EOF > /etc/nginx/nginx.conf # For more information on configuration, see: # * Official English Documentation: http://nginx.org/en/docs/ # * Official Russian Documentation: http://nginx.org/ru/docs/ user nginx; worker_processes auto; error_log /var/log/nginx/error.log; pid /run/nginx.pid; # Load dynamic modules. See /usr/share/doc/nginx/README.dynamic. include /usr/share/nginx/modules/*.conf; events { worker_connections 1024; } http { log_format main '\$remote_addr - \$remote_user [\$time_local] "\$request" ' '\$status \$body_bytes_sent "\$http_referer" ' '"\$http_user_agent" "\$http_x_forwarded_for"'; access_log /var/log/nginx/access.log main; sendfile on; tcp_nopush on; tcp_nodelay on; keepalive_timeout 65; types_hash_max_size 4096; include /etc/nginx/mime.types; default_type application/octet-stream; # Load modular configuration files from the /etc/nginx/conf.d directory. # See http://nginx.org/en/docs/ngx_core_module.html#include # for more information. include /etc/nginx/conf.d/*.conf; server { listen 80; server_name example.com; root /home/ec2-user/www/public; add_header X-Frame-Options "SAMEORIGIN"; add_header X-Content-Type-Options "nosniff"; index index.php; charset utf-8; location / { try_files \$uri \$uri/ /index.php?\$query_string; } location = /favicon.ico { access_log off; log_not_found off; } location = /robots.txt { access_log off; log_not_found off; } error_page 404 /index.php; location ~ \.php$ { fastcgi_pass unix:/var/run/php-fpm/www.sock; fastcgi_param SCRIPT_FILENAME \$realpath_root\$fastcgi_script_name; include fastcgi_params; } location ~ /\.(?!well-known).* { deny all; } } } EOF service nginx start # composer 설치 # cd /home/ec2-user # curl -sS https://getcomposer.org/installer | php # mv composer.phar /usr/local/bin/composer # ln -s /usr/local/bin/composer /usr/bin/composer >> /home/ec2-user/install.log #laravel 설치 # composer global require laravel/installer >> /home/ec2-user/install.log # 소스 내려받기 # git clone https://id:password@github.com/mycompany/ExampleServer.git www # chown -R nginx.nginx /home/ec2-user/www # sudo chmod -R 775 storage bootstrap/cache # RDS instance MasterDB: Type: "AWS::RDS::DBInstance" Properties: DBName: !Ref DBName AllocatedStorage: 20 DBInstanceClass: !Ref DBClass Engine: "MariaDB" MasterUsername: !Ref DBName MasterUserPassword: !Ref DBPassword # 2021.07.01 현시점 최소 20G VPCSecurityGroups: [!GetAtt ["SecurityGroupDB", "GroupId"]] DBSubnetGroupName: !Ref DBSubnetGroup Tags: - Key: "Name" Value: !Join ["", [!Ref AWS::StackName, "-db Master"]] - Key: Application Value: !Ref AWS::StackName # ReplicaDB: # Type: "AWS::RDS::DBInstance" # Properties: # SourceDBInstanceIdentifier: !Ref MasterDB, # DBInstanceClass: !Ref DBClass # Tags: # - Key: "Name" # Value: !Join ["", [!Ref AWS::StackName, "-db Slave"]] # - Key: Application # Value: !Ref AWS::StackName Bucket: Type: AWS::S3::Bucket Properties: AccessControl: PublicRead Outputs: EndpointAddress: Description: DB cluster endpoint address Value: !GetAtt MasterDB.Endpoint.Address EndpointPort: Description: DB cluster endpoint port Value: !GetAtt MasterDB.Endpoint.Port S3BucketName: Description: S3 Bucket name Value: !Ref Bucket