├── .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 |
--------------------------------------------------------------------------------