├── .gitignore ├── slide.pdf ├── clock ├── app.rb ├── Gemfile ├── Dockerfile ├── views │ └── index.erb └── Gemfile.lock ├── README.md └── terraform └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .envrc 2 | .direnv 3 | -------------------------------------------------------------------------------- /slide.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nacyot/deploy_clock/HEAD/slide.pdf -------------------------------------------------------------------------------- /clock/app.rb: -------------------------------------------------------------------------------- 1 | require 'sinatra' 2 | 3 | get '/' do 4 | erb :index, :locals => {:hostname => `hostname`} 5 | end 6 | -------------------------------------------------------------------------------- /clock/Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | 3 | git_source(:github) {|repo_name| "https://github.com/#{repo_name}" } 4 | 5 | gem 'sinatra' 6 | -------------------------------------------------------------------------------- /clock/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ruby:2.6 2 | 3 | WORKDIR /app 4 | ADD ./Gemfile ./Gemfile.lock /app/ 5 | RUN bundle install 6 | ADD . /app 7 | CMD ruby ./app.rb -o 0.0.0.0 8 | -------------------------------------------------------------------------------- /clock/views/index.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 |

Clock

4 |
Hostname: <%= hostname %>
5 |
Time: <%= Time.now.iso8601 %>
6 | 7 | -------------------------------------------------------------------------------- /clock/Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | mustermann (1.0.3) 5 | rack (2.0.7) 6 | rack-protection (2.0.5) 7 | rack 8 | sinatra (2.0.5) 9 | mustermann (~> 1.0) 10 | rack (~> 2.0) 11 | rack-protection (= 2.0.5) 12 | tilt (~> 2.0) 13 | tilt (2.0.9) 14 | 15 | PLATFORMS 16 | ruby 17 | 18 | DEPENDENCIES 19 | sinatra 20 | 21 | BUNDLED WITH 22 | 1.17.2 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 아마존 웹 서비스에 웹 서버 배포 2 | 3 | ## 프로젝트 4 | 5 | * `./clock` - 간단한 Sinatra 웹서버 애플리케이션 6 | 7 | ``` 8 | # 서버 실행 9 | $ ruby ./app.rb 10 | ``` 11 | 12 | ## Dockerize 13 | 14 | * Dockerfile: Docker 이미지를 만들기 위한 파일 15 | * Dockerfile은 프로젝트의 루트 디렉터리에 배치 16 | 17 | Dockerfile 예제 18 | 19 | ``` 20 | FROM ruby:2.6 21 | 22 | WORKDIR /app 23 | ADD ./Gemfile ./Gemfile.lock /app/ 24 | RUN bundle install 25 | ADD . /app 26 | CMD ruby ./app.rb -o 0.0.0.0 27 | ``` 28 | 29 | 다섯가지만 알면 바로 시작 가능! 30 | 31 | * FROM - 베이스 이미지를 지정 32 | * WORKDIR - 작업 디렉터리를 지정 33 | * ADD - 도커 이미지에 파일을 추가 34 | * RUN - 이미지 기반으로 명령어를 실행 35 | * CMD - 이미지의 기본 명령어 지정 36 | 37 | ``` 38 | # 도커 이미지 빌드 39 | $ docker build -t nacyot/clock . 40 | 41 | # 도커 컨테이너 실행 42 | $ docker run -it -p 80:4567 nacyot/clock:latest 43 | 44 | # 도커 허브 로그인 45 | $ docker login 46 | 47 | # 도커 허브에 이미지 업로드 48 | $ docker push nacyot/clock:latest 49 | ``` 50 | 51 | 도커에 대해서 더 자세한 내용은 다음 글들을 참고해주세요. 52 | 53 | * [도커(Docker) 튜토리얼 : 깐 김에 배포까지 | 44bits.io](https://www.44bits.io/ko/post/easy-deploy-with-docker) 54 | * [초보를 위한 도커 안내서 - 도커란 무엇인가?](https://subicura.com/2017/01/19/docker-guide-for-beginners-1.html) 55 | 56 | ## AWS 배포 준비 57 | 58 | * [아마존 웹서비스 커맨드라인 인터페이스(AWS CLI) 기초 | 44bits.io](https://www.44bits.io/ko/post/aws_command_line_interface_basic) 59 | * [커맨드라인 JSON 프로세서 jq : 기초 문법과 작동원리 | 44bits.io](https://www.44bits.io/ko/post/cli_json_processor_jq_basic_syntax) 60 | 61 | ## 배포를 위한 변수값 준비 62 | 63 | VPC, Subnet, AMI 등 배포 작업에 필요한 변수들을 먼저 준비. AWS 계정 기본 상태(Default VPC)를 사용한다고 가정. 64 | 65 | ```sh 66 | # 기본 VPC의 ID 값 67 | VPC_ID=$(aws ec2 describe-vpcs | jq -r '.Vpcs[].VpcId') 68 | 69 | # 기본 VPC에 속한 Subnet들의 ID 70 | SUBNET_IDS=$(aws ec2 describe-subnets | jq -r '.Subnets | map(.SubnetId) | join(" ")') 71 | FIRST_SUBNET_ID=$(echo $SUBNET_IDS | cut -d' ' -f1) 72 | SECOND_SUBNET_ID=$(echo $SUBNET_IDS | cut -d' ' -f2) 73 | THIRD_SUBNET_ID=$(echo $SUBNET_IDS | cut -d' ' -f3) 74 | 75 | # 기본 보안 그룹 76 | DEFAULT_SG_ID=$(aws ec2 describe-security-groups --group-name=default | jq -r '.SecurityGroups[].GroupId') 77 | 78 | # Ubuntu AMI 79 | UBUNTU_AMI_ID='ami-0b5edf72c627a56c9' 80 | 81 | # DOMAIN 82 | DOMAIN='44bits.io' 83 | DOMAIN_EC2='clock.44bits.io' 84 | DOMAIN_ELB='clockl1.44bits.io' 85 | ``` 86 | 87 | ## EC2 용 보안 그룹 생성 88 | 89 | ec2web: 22, 80 포트 인바운트를 열어주는 보안 그룹 작성 90 | 91 | ```sh 92 | # 비어있는 보안 그룹을 생성 93 | aws ec2 create-security-group \ 94 | --group-name ec2web \ 95 | --description ec2web \ 96 | --vpc-id "${VPC_ID}" 97 | 98 | # 방금 생성한 보안 그룹의 ID를 EC2WEB_SG_ID 변수에 저장 99 | EC2WEB_SG_ID=$(aws ec2 describe-security-groups --group-name=ec2web | jq -r '.SecurityGroups[].GroupId') 100 | 101 | # 22번 포트 오픈 규칙 추가(SSH) 102 | aws ec2 authorize-security-group-ingress \ 103 | --group-id $EC2WEB_SG_ID \ 104 | --protocol tcp \ 105 | --port 22 \ 106 | --cidr 0.0.0.0/0 107 | 108 | # 80번 포트 오픈 규칙 추가(HTTP) 109 | aws ec2 authorize-security-group-ingress \ 110 | --group-id $EC2WEB_SG_ID \ 111 | --protocol tcp \ 112 | --port 80 \ 113 | --cidr 0.0.0.0/0 114 | ``` 115 | 116 | ## 첫 번째 EC2 인스턴스 생성 117 | 118 | 웹 서버 배포용 첫 EC2 인스턴스 생성 119 | 120 | * SSH 키는 미리 설정해두어야함(EC2 -> Key Pairs) 121 | * 앞서 생성한 EC2용 보안 그룹을 사용 122 | * 외부에서 접속 가능하도록 퍼블릭 IP를 할당해줌(`--associate-public-ip-address`) 123 | * `key-name`은 AWS 계정에 설정된 SSH 키를 지정 124 | 125 | ```sh 126 | aws ec2 run-instances \ 127 | --image-id $UBUNTU_AMI_ID \ 128 | --instance-type t2.small \ 129 | --security-group-ids $EC2WEB_SG_ID $DEFAULT_SG_ID \ 130 | --subnet-id $FIRST_SUBNET_ID \ 131 | --associate-public-ip-address \ 132 | --key-name nacyot \ 133 | --tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=Web01}]' 134 | 135 | # Web01 서버 IP와 ID를 변수로 저장 136 | WEB01_IP=$(aws ec2 describe-instances --filters "Name=tag:Name,Values=Web01" | jq -r '.Reservations[].Instances[].PublicIpAddress') 137 | WEB01_ID=$(aws ec2 describe-instances --filters "Name=tag:Name,Values=Web01" | jq -r '.Reservations[].Instances[].InstanceId') 138 | 139 | # 1~2분 정도 기다린 후 SSH 접속 140 | ssh ubuntu@$WEB01_IP 141 | 142 | # Docker 설치 143 | curl -s https://get.docker.com/ | sudo sh 144 | 145 | # 도커로 nacyot/clock 이미지 실행하기 146 | sudo docker run -d -p 80:4567 nacyot/clock:latest 147 | ``` 148 | 149 | ## Route 53으로 도메인 연결하기(1) 150 | 151 | A Record 추가: $DOMAIN_EC2, EC2 IP 152 | 153 | ## ELB 용 보안 그룹 작성 154 | 155 | ```sh 156 | aws ec2 create-security-group \ 157 | --group-name elbweb \ 158 | --description elbweb \ 159 | --vpc-id "${VPC_ID}" 160 | 161 | ELBWEB_SG_ID=$(aws ec2 describe-security-groups --group-name=elbweb | jq -r '.SecurityGroups[].GroupId') 162 | 163 | # HTTP 접속 용 규칙 추가 164 | aws ec2 authorize-security-group-ingress \ 165 | --group-id $ELBWEB_SG_ID \ 166 | --protocol tcp \ 167 | --port 80 \ 168 | --cidr 0.0.0.0/0 169 | 170 | # HTTPS 접속 용 규칙 추가 171 | aws ec2 authorize-security-group-ingress \ 172 | --group-id $ELBWEB_SG_ID \ 173 | --protocol tcp \ 174 | --port 443 \ 175 | --cidr 0.0.0.0/0 176 | ``` 177 | 178 | ## 로드 밸런서 작성 179 | 180 | ```sh 181 | # 로드 밸런서 생성 182 | aws elbv2 create-load-balancer \ 183 | --name web-load-balancer \ 184 | --subnets $FIRST_SUBNET_ID $SECOND_SUBNET_ID $THIRD_SUBNET_ID \ 185 | --security-groups $ELBWEB_SG_ID $DEFAULT_SG_ID 186 | 187 | LB_ARN=$(aws elbv2 describe-load-balancers --name web-load-balancer | jq -r '.LoadBalancers[0] | .LoadBalancerArn') 188 | 189 | # 타깃 그룹 생성 190 | aws elbv2 create-target-group \ 191 | --name web-target-group \ 192 | --protocol HTTP --port 80 --vpc-id "${VPC_ID}" 193 | 194 | TG_ARN=$(aws elbv2 describe-target-groups --name web-target-group | jq -r '.TargetGroups[0] | .TargetGroupArn') 195 | 196 | # 로드 밸런서에 타깃 그룹을 리스너로 등록 197 | aws elbv2 create-listener \ 198 | --protocol HTTP --port 80 \ 199 | --load-balancer-arn="${LB_ARN}" \ 200 | --default-actions "Type=forward,TargetGroupArn=${TG_ARN}" 201 | 202 | # Web01 서버를 타겟 그룹에 등록 203 | aws elbv2 register-targets --target-group-arn $TG_ARN --targets Id=$WEB01_ID 204 | 205 | # 로드 밸런서의 DNS 주소 출력 206 | aws elbv2 describe-load-balancers --name web-load-balancer | jq -r '.LoadBalancers[0].DNSName' 207 | ``` 208 | 209 | ## Route 53으로 도메인 연결하기(2) 210 | 211 | A Record(alias) 추가: $DOMAIN_ELB, ELB DNSName 212 | 213 | 214 | ## ACM을 사용해 인증서 발급 215 | 216 | 웹 콘솔에서 작업 후 변수로 저장 217 | 218 | ```sh 219 | ACM_ARN=$(aws acm list-certificates | jq -r ".CertificateSummaryList[] | select(.DomainName == \"*.$DOMAIN\") | .CertificateArn") 220 | ``` 221 | 222 | ## 로드 밸런서에 HTTPS 용 리스너 추가 223 | 224 | ```sh 225 | aws elbv2 create-listener \ 226 | --protocol HTTPS --port 443 \ 227 | --load-balancer-arn="${LB_ARN}" \ 228 | --default-actions "Type=forward,TargetGroupArn=${TG_ARN}" \ 229 | --certificates "CertificateArn=${ACM_ARN}" \ 230 | --ssl-policy ELBSecurityPolicy-2016-08 231 | ``` 232 | 233 | ## 두 번째 인스턴스 작업 234 | 235 | ```sh 236 | aws ec2 run-instances \ 237 | --image-id $UBUNTU_AMI_ID \ 238 | --instance-type t2.small \ 239 | --security-group-ids $EC2WEB_SG_ID $DEFAULT_SG_ID \ 240 | --subnet-id $FIRST_SUBNET_ID \ 241 | --associate-public-ip-address \ 242 | --key-name nacyot \ 243 | --tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=Web02}]' 244 | 245 | WEB02_IP=$(aws ec2 describe-instances --filters "Name=tag:Name,Values=Web02" | jq -r '.Reservations[].Instances[].PublicIpAddress') 246 | WEB02_ID=$(aws ec2 describe-instances --filters "Name=tag:Name,Values=Web02" | jq -r '.Reservations[].Instances[].InstanceId') 247 | 248 | ssh ubuntu@$WEB02_IP 249 | curl -s https://get.docker.com/ | sudo sh 250 | sudo docker run -d -p 80:4567 nacyot/clock:latest 251 | ``` 252 | 253 | 두 번째 인스턴스도 타깃그룹에 등록 254 | 255 | ```sh 256 | aws elbv2 register-targets --target-group-arn $TG_ARN --targets Id=$WEB02_ID 257 | ``` 258 | 259 | ## 생성한 리소스 삭제 260 | 261 | * ELB, EC2 리소스 정리 262 | * Route53 Record, ACM 인증서는 웹콘솔에서 삭제 263 | 264 | ```sh 265 | aws elbv2 delete-load-balancer --load-balancer-arn=$LB_ARN 266 | aws elbv2 delete-target-group --target-group-arn=$TG_ARN 267 | aws ec2 terminate-instances --instance-id=$WEB01_ID 268 | aws ec2 terminate-instances --instance-id=$WEB02_ID 269 | aws ec2 delete-security-group --group-name ec2web 270 | aws ec2 delete-security-group --group-name elbweb 271 | ``` 272 | 273 | # 더 공부하기 274 | 275 | * [아마존 엘라스틱 컨테이너 서비스(ECS)와 도커(Docker)로 시작하는 컨테이너 오케스트레이션 | 44bits.io](https://www.44bits.io/ko/post/container-orchestration-101-with-docker-and-aws-elastic-container-service) 276 | -------------------------------------------------------------------------------- /terraform/README.md: -------------------------------------------------------------------------------- 1 | # 테라폼 튜토리얼 2 | 3 | ## 테라폼 기초 4 | ### Requirements 5 | * 노트북 6 | * AWS 계정 7 | 8 | ### 테라폼은? 9 | * Infrastructure as Code 10 | * **코드로 클라우드 리소스들을 관리** <- 핵심 11 | 12 | ### 주요 개념 13 | * 프로비저닝(Provisioning) 14 | * 프로바이더(Provider) 15 | * 리소스(Resource) 16 | * 데이터(Data) 17 | * HCL(Hashicorp Configuration Language) 18 | * 계획(Plan) 19 | * 적용(Apply) 20 | 21 | ### HCL 22 | * Hashicorp Configuration Language 23 | * 테라폼에서 사용하는 공식 언어 24 | * JSON 호환 25 | * 리소스 정의 순서는 무관 (의존성 자동 파악) 26 | 27 | 기본적인 형태 28 | ``` 29 | resource "RESOURCE_TYPE" "RESOURCE_NAME" { 30 | } 31 | ``` 32 | 33 | 인자값 지정 34 | ``` 35 | resource "RESOURCE_TYPE" "RESOURCE_NAME" { 36 | ARG1 = "value" 37 | ARG2 = "value" 38 | } 39 | ``` 40 | 41 | 참조 42 | ``` 43 | resource "A" "B" { } 44 | resource "Y" "Z" { 45 | ARG1 = A.B.ATTR 46 | } 47 | ``` 48 | 49 | ### 매뉴얼 읽기 50 | * [AWS: aws_instance - Terraform by HashiCorp](https://www.terraform.io/docs/providers/aws/r/instance.html) 51 | 52 | ## 튜토리얼 아키텍처 53 | 54 | ## Iteration 1: EC2 Instance 55 | 56 | ### 환경 준비 57 | * AWS 액세스 키를 생성해주세요. 58 | * [아마존 웹 서비스 IAM 사용자의 액세스 키 발급 및 관리 | 44bits.io](https://www.44bits.io/ko/post/publishing_and_managing_aws_user_access_key) 59 | * 액세스 키는 안전하게 보관해주세요. 60 | * 테라폼을 설치해주세요. 61 | * [Download Terraform - Terraform by HashiCorp](https://www.terraform.io/downloads.html) 62 | * [Terraform – Getting Started – Install Terraform on Windows, Linux and Mac OS](https://www.vasos-koupparis.com/terraform-getting-started-install/) 63 | 64 | macOS(homebrew) 65 | ``` 66 | $ brew install terraform 67 | ``` 68 | 69 | 설치 확인 70 | ``` 71 | $ terraform version 72 | ``` 73 | 74 | ### 테라폼 프로젝트 준비 75 | 다음 명령어들을 차례대로 실행하고, 결과를 확인합니다. 76 | 77 | ``` 78 | ## 프로젝트 디렉터리 생성 79 | $ mkdir terraform-clock 80 | $ cd terraform-clock 81 | 82 | ## 첫 plan 명령어 83 | $ terraform plan 84 | 85 | ## ec2.tf 파일 생성 86 | $ touch ec2.tf 87 | 88 | ## 테라폼 프로젝트 초기화(실제로는 아무일도 일어나지 않음) 89 | ## init 실행시 사용중인 프로바이더나 모듈을 가져옵니다 90 | $ terraform init 91 | 92 | ## 다시 plan 93 | $ terraform plan 94 | 95 | ## 아무것도 없는 상태에서 apply 96 | $ terraform apply 97 | Apply complete! Resources: 0 added, 0 changed, 0 destroyed. 98 | 99 | # apply해서 state 파일이 만들어집니다 100 | $ cat terraform.tfstate 101 | ``` 102 | 103 | ### AWS 프로바이더 셋업 104 | `configuration.tf` 파일을 만들고 다음 내용을 추가합니다. 105 | 106 | ``` 107 | provider "aws" { 108 | region = "ap-northeast-2" 109 | access_key = "my-access-key" 110 | secret_key = "my-secret-key" 111 | } 112 | ``` 113 | 114 | 다음 명령어들을 차례대로 실행하고, 결과를 확인합니다. 115 | 116 | ``` 117 | $ terraform plan 118 | 119 | # aws provider 설치, .terraform 디렉터리 생성 120 | $ terrafrom init 121 | 122 | $ terraform plan 123 | 124 | # 설치된 provider 정보도 함께 보여줍니다 125 | $ terraform version 126 | ``` 127 | 128 | ### 첫 테라폼 코드: EC2 인스턴스 예제 129 | `ec2.tf` 파일을 작성하고 다음 내용을 추가합니다. 130 | 131 | ``` 132 | resource "aws_instance" "clock" { 133 | ami = "ami-0b5edf72c627a56c9" # Ubuntu AMI 134 | instance_type = "t2.micro" 135 | 136 | tags = { 137 | Name = "HelloTerraform" 138 | } 139 | } 140 | ``` 141 | 142 | 다음 명령어들을 차례대로 실행하고, 결과를 확인합니다. 143 | 144 | ``` 145 | ## plan 명령어를 실행하면, 생성될 EC2 인스턴스의 정보를 보여줍니다. 146 | $ terraform plan 147 | 148 | ## 실제로 EC2 인스턴스를 생성합니다. 중간에 정말 생성하냐는 질문에 yes를 입력해줍니다. 149 | $ terraform apply 150 | ``` 151 | 152 | 생성된 EC2의 IP를 받아오기 위해 `output` 문법을 사용해봅니다. 153 | 154 | `output.tf` 파일을 만들고 다음 내용을 작성합니다. 155 | 156 | ``` 157 | output "public_ip" { 158 | value = aws_instance.clock.public_ip 159 | } 160 | ``` 161 | 162 | 다시 `terraform apply`를 실행하면 맨 아래에 output에 추가한 내용이 출력됩니다. 이 IP로 SSH에 접속해봅니다. 163 | 164 | ``` 165 | ## 접속 안 됨... 166 | $ ssh ubuntu@ 167 | ``` 168 | 169 | 지금까지 작성한 프로젝트의 구조를 살펴봅니다. 170 | 171 | ``` 172 | $ tree 173 | . 174 | ├── configuration.tf 175 | ├── ec2.tf 176 | ├── output.tf 177 | ├── terraform.tfstate 178 | └── terraform.tfstate.backup 179 | ``` 180 | 181 | ### 인스턴스에 SSH 접속하기 182 | 웹콘솔에 접속해서 생성된 EC2 인스턴스의 정보를 확인해봅니다. 시큐리티 그룹을 확인해보면 SSH 포트가 열려있지 않은 것을 확인할 수 있습니다. 또한 SSH 접속을 위한 키 페어 설정도 필요합니다. 183 | 184 | `ec2.tf` 파일에 다음 내용을 추가합니다. 위치는 무관하지만 `aws_instance` 리소스 앞에 추가하는 것을 권장합니다. 185 | 186 | ``` 187 | resource "aws_security_group" "ec2" { 188 | name = "terraform-ec2-sg" 189 | description = "allow ssh" 190 | 191 | ingress { 192 | from_port = 22 193 | to_port = 22 194 | protocol = "tcp" 195 | cidr_blocks = ["0.0.0.0/0"] 196 | } 197 | 198 | egress { 199 | from_port = 0 200 | to_port = 0 201 | protocol = "-1" 202 | cidr_blocks = ["0.0.0.0/0"] 203 | } 204 | } 205 | ``` 206 | 207 | `terraform apply`로 시큐리티 그룹을 생성합니다. 208 | 209 | 키페어 추가를 위해 다음 내용을 `ec2.tf`에 추가합니다. 210 | ``` 211 | resource "aws_key_pair" "admin" { 212 | key_name = "admin" 213 | public_key = "${file("~/.ssh/id_rsa.pub")}" 214 | } 215 | ``` 216 | 217 | 이 때 `public_key`의 경로는 자신이 사용하는 SSH키의 퍼블릭 키 경로가 되어야합니다. 218 | 219 | `terraform apply`로 키 페어를 추가해줍니다. 220 | 221 | `ec2.tf`에서 처음에 작성 `aws_instance.clock` 리소스의 내용을 다음과 같이 변경합니다. `vpc_security_group_ids`와 `key_name`이 추가되었습니다. 222 | 223 | ``` 224 | resource "aws_instance" "clock" { 225 | ami = "ami-0b5edf72c627a56c9" 226 | instance_type = "t2.micro" 227 | vpc_security_group_ids = [aws_security_group.ec2.id] 228 | key_name = aws_key_pair.admin.key_name 229 | 230 | tags = { 231 | Name = "HelloTerraform" 232 | } 233 | } 234 | ``` 235 | 236 | 다시 `terraform apply`을 실행합니다. 237 | 238 | 이제 SSH에 접속이 가능할 것입니다. 239 | 240 | ``` 241 | # 1~2분 정도 기다린 후 SSH 접속 242 | $ ssh ubuntu@$ 243 | ``` 244 | 245 | ### 도커 설치 및 컨테이너 실행 246 | ``` 247 | # Docker 설치 248 | $ curl -s https://get.docker.com/ | sudo sh 249 | 250 | # 도커로 nacyot/clock 이미지 실행하기 251 | $ sudo docker run -d -p 80:4567 nacyot/clock:latest 252 | 253 | $ curl 0.0.0.0:80 254 | ``` 255 | 256 | ### 관련 리소스 목록 257 | * [Provider: AWS - Terraform by HashiCorp](https://www.terraform.io/docs/providers/aws/index.html) 258 | * [AWS: aws_security_group - Terraform by HashiCorp](https://www.terraform.io/docs/providers/aws/r/security_group.html) 259 | * [AWS: aws_instance - Terraform by HashiCorp](https://www.terraform.io/docs/providers/aws/r/instance.html) 260 | 261 | ### 추가 과제 262 | * 여기까지 적용된 `terraform.tfstate` 내용을 살펴봅니다. 263 | * 앞서 실행한 도커 컨테이너는 외부에서 접속이 어렵습니다. 접속 가능하도록 해보세요. 264 | * 힌트: 시큐리티 그룹에서 도커가 사용하는 포트를 열어줘야합니다. 265 | * AWS 프로바이더 설정시 직접 값을 입력하는 대신 환경변수로 지정해보세요. 266 | * 힌트: [Provider: AWS - Terraform by HashiCorp](https://www.terraform.io/docs/providers/aws/index.html) 267 | * 현재 IP에서만 SSH 접속이 가능하도록 sceurity group을 수정해보세요. 268 | * 힌트: My IP 같은 서비스를 사용해 자신의 퍼블릭 IP를 확인하고 CIDR로 지정해보세요. 269 | * `terraform destroy` 후 다시 `terraform apply` 해보세요. 270 | * 단, 도커 컨테이너는 SSH 접속해서 다시 실행해줘야합니다. 271 | 272 | ## Iteration 2: Elastic Load Balancer 273 | ### Security Group 274 | `elb.tf` 파일을 작성하고 시큐리티 그룹을 추가합니다. 275 | ``` 276 | resource "aws_security_group" "lb" { 277 | name = "terraform-elb-sg" 278 | description = "allow http" 279 | 280 | ingress { 281 | from_port = 80 282 | to_port = 80 283 | protocol = "tcp" 284 | cidr_blocks = ["0.0.0.0/0"] 285 | } 286 | 287 | egress { 288 | from_port = 0 289 | to_port = 0 290 | protocol = "-1" 291 | cidr_blocks = ["0.0.0.0/0"] 292 | } 293 | } 294 | ``` 295 | 296 | ### VPC, Subnet 데이터 가져오기 297 | ELB 리소스를 생성하기 위해서는 VPC와 서브넷 정보가 필요합니다. AWS 프로바이더의 data를 사용해서 이 정보들을 가져옵니다. 298 | 299 | `elb.tf`에 다음 내용을 추가합니다. 300 | 301 | ``` 302 | data "aws_vpc" "default" { 303 | default = true 304 | } 305 | 306 | data "aws_subnet_ids" "default" { 307 | vpc_id = data.aws_vpc.default.id 308 | } 309 | ``` 310 | 311 | ### 로드 밸런서 작성 312 | 로드 밸런서 리소스를 작성합니다. 앞서 생성한 시큐리티 그룹을 사용합니다. 또한 위에서 data로 가져온 서브넷 아이디를 지정합니다. 313 | 314 | `elb.tf`에 다음 내용을 추가합니다. 315 | 316 | ``` 317 | resource "aws_lb" "clock" { 318 | name = "clock-lb" 319 | internal = false 320 | load_balancer_type = "application" 321 | security_groups = [aws_security_group.lb.id] 322 | subnets = data.aws_subnet_ids.default.ids 323 | enable_deletion_protection = false 324 | } 325 | ``` 326 | 327 | 생성된 로드 밸런서를 웹 콘솔에서 확인해봅니다. 328 | 329 | 로드 밸런서에 접속할 수 있는 주소를 출력하기 위해 `output.tf`에 다음 내용을 추가해줍니다. 330 | 331 | ``` 332 | output "elb_address" { 333 | value = aws_lb.clock.dns_name 334 | } 335 | ``` 336 | 337 | 이 주소에 접속해봅니다. 아직은 앞서 확인한 clock 서버에 연결되지 않습니다. 338 | 339 | ### 타깃 그룹 작성 340 | 타깃 그룹을 생성합니다. 341 | 342 | `elb.tf`에 다음 내용을 추가합니다. 343 | 344 | ``` 345 | resource "aws_lb_target_group" "clock" { 346 | name = "clock" 347 | port = 80 348 | protocol = "HTTP" 349 | vpc_id = data.aws_vpc.default.id 350 | } 351 | ``` 352 | 353 | ### 로드 밸런서 리스너 작성 354 | 로드 밸런서와 타깃 그룹을 연결하는 리스너를 연결합니다. 355 | 356 | `elb.tf`에 다음 내용을 추가합니다. 357 | 358 | ``` 359 | resource "aws_lb_listener" "clock_http" { 360 | load_balancer_arn = aws_lb.clock.arn 361 | port = "80" 362 | protocol = "HTTP" 363 | 364 | default_action { 365 | type = "forward" 366 | target_group_arn = aws_lb_target_group.clock.arn 367 | } 368 | } 369 | ``` 370 | 371 | ### 타깃 그룹에 타깃 추가 372 | 타깃 그룹에 앞서 생성한 인스턴스를 연결합니다. 373 | 374 | `elb.tf`에 다음 내용을 추가합니다. 375 | 376 | ``` 377 | resource "aws_lb_target_group_attachment" "clock" { 378 | target_group_arn = aws_lb_target_group.clock.arn 379 | target_id = aws_instance.clock.id 380 | port = 80 381 | } 382 | ``` 383 | 384 | 이제 다시 로드 밸런서의 주소에 접속해봅니다. 385 | 386 | ### 리소스 목록 387 | * [AWS: aws_lb - Terraform by HashiCorp](https://www.terraform.io/docs/providers/aws/r/lb.html) 388 | * [AWS: aws_lb_listener - Terraform by HashiCorp](https://www.terraform.io/docs/providers/aws/r/lb_listener.html) 389 | * [AWS: aws_lb_target_group - Terraform by HashiCorp](https://www.terraform.io/docs/providers/aws/r/lb_target_group.html) 390 | 391 | ### 추가 과제 392 | * 인스턴스를 하나 더 만들고 타깃그룹에 연결해봅니다. 로드 밸런서가 두 인스턴스에 모두 연결해주는지 확인해봅니다. 393 | * VPC와 서브넷 정보를 common.tf 파일로 분리하고, `ec2.tf`의 인스턴스에서 명시적으로 subnet을 지정해봅니다. 394 | * [AWS: aws_instance - Terraform by HashiCorp](https://www.terraform.io/docs/providers/aws/r/instance.html)에서 서브넷 지정 방법을 참고합니다. 395 | * ELB에서 직접 HTTPS 처리하는 것이 가능합니다. 이 때 어떤 리소스들이 필요한지 구상해봅니다. 396 | * 공인인증서를 사용해 HTTPS 연결을 하려면 직접 구매한 도메인이 필요합니다. 397 | --------------------------------------------------------------------------------