본문 바로가기

메가존 클라우드 2기 교육/실무 특화

Iac(코드형 인프라) 개념 및 환경 세팅, AWS CLI로 인스턴스 생성

 IaC(코드형 인프라)란?

 코드(스크립트)를 작성 및 실행하여 인프라를 생성, 배포, 수정, 정리하는 것을 의미한다. 이는 서버를 물리적으로 설치하는 등의 하드웨어 측면을 포함하여 운영의 모든 측면을 소프트웨어적으로 생각하는 중대한 사고 전환을 보여준다. 코드형 인프라의 핵심은 서버, 데이터베이스, 네트워크, 로그 파일, 애플리케이션 구성, 문서, 자동화된 테스트, 배포 프로세스 등 거의 모든 것을 코드(스크립트)로 관리할 수 있다.

 처음에는 스크립트로 작성하는 것이 어렵지만, 한 번 작성하면 재사용이 가능하다는 장점이 있다. 즉, 자동화가 가능해진다.

 코드형 인프라 도구로는 애드혹 스크립트(사용자 데이터 - #!/bin/bash), 구성 관리 도구(Ansible - SSH, Python), 서버 템플릿 도구(Docker - 이미지{OS,App}), 오케스트레이션 도구(Kubernetes), 프로비전 도구(Terraform)가 있다.


1. 애드혹 스크립트

 수행할 작업을 단계별로 나누고 배시(Bash)와 같은 언어를 사용하여 각 단계를 코드로 정의하고 작성된 스크립트를 서버에서 수동으로 실행하는 것. 코드를 직접 작성하여 매번 수동으로 맞춤 코드를 작성해야 되기 때문에 간단한 설치에 적합.

ex)

!/bin/bash

yum install -y httpd

......


2. 구성 관리 도구(Configuration)

 셰프, 퍼핏(AWS Opsworks - EC2 구성 관리 도구), 앤서블(이미 설치된 서버에 SSH 혹은 파이썬으로 접근하여 필요한 설치를 자동으로 수행), 솔트스택 등은 모두 구성 관리 도구로써 대상 서버에 소프트웨어를 설치하고 관리하도록 설계되어 있다. 배시 스크립트와 비슷해 보이지만 애드혹 스크립트를 사용할 때와 다른 여러가지 장점이 있다.

  • 1) 코딩 규칙: 구성 관리 도구는 문서화, 파일 레이아웃, 명확하게 이름 붙여진 매개 변수, 시크릿 관리 등을 포함하는 코딩 규칙으로 일관되고 예측 가능한 구조를 제공.
  • 2) 멱등성: 구성 관리 도구는 실행 횟수에 관계없이 설정 파일을 사용하여 소프트웨어가 설치되지 않았을 경우에만 설치하고 소프트웨어가 동작하지 않는 경우에만 동작하도록 한다. 불필요한 자원의 낭비를 줄일 수 있다.
  • 3) 분산형 구조: 애드혹 스크립트는 단일 로컬 머신에서만 실행되도록 설계되어 있지만 앤서블과 같은 구성 관리 도구는 원격의 수많은 서버를 관리하기 위해 특별히 설계 된 것. 관리가 필요한 서버들의 IP를 정리한 hosts 파일을 생성하고 플레이북(스크립트를 정해놓은 파일)을 정의하여 실행.

3. 서버 템플릿 도구

 도커,  패커, 베이그런트와 같은 서버 템플릿 도구는 여러 서버를 시작하고 각각 동일한 코드를 실행하여 서버를 구성하는 기존 방식과 다르게, 운영체제, 소프트웨어, 파일 및 기타 필요한 모든 내용을 포함하고 있는 스냅샷으로 이미지를 생성하여 모든 서버에 이미지를 설치할 수 있다. 실행은 서버에 이미지를 배포하고 할 수 있다.


4. 오케스트레이션 도구

 서버 템플릿 도구는 VM이나 컨테이너를 생성하기에 좋은 도구이지만 관리 부분이 부족하기 때문에 오케스트레이션 도구가 필요하다. 오케스트레이션 도구는 VM과 컨테이너 배포, 효율적 업데이트 및 롤백, 자동 복구(치유), 자동 확장(조정), 로드 밸런싱, 서로 식별하고 통신할 수 있게 서비스 검색 기능을 제공한다. 쿠버네티스를 사용하면 도커 컨테이너를 어떻게 관리할지를 코드로 정의할 수 있다. 오케스트레이션 도구의 종류로는 온-프레미스에서 클러스터를 구축할 수 있는 쿠버네티스, 마라톤/메소스, 도커 스웜, 노마드 등이 있으며 퍼블릭 클라우드에서는 AWS EKS, Azure AKS, GCP GKE가 있다.


5. 프로비전 도구

 프로 비전 도구는 구성 관리, 서버 템플릿, 오케스트레이션을 모두 포함한다.

 구성 관리, 서버 템플릿 및 오케스트레이션 도구가 각 서버에서 실행되는 코드를 정의한다면 테라폼, 클라우드포메이션(AWS에서만 가능해서 SA들은 선호하지 않음), 오픈스택 히트와 같은 프로비전 도구는 서버 자체를 생성한다. 서버 생성만 하는 것이 아니라 사실상 설정도 하고 있어서 인프라에 관한 거의 모든 부분을 프로비저닝을 할 수 있다.


Iac(코드형 인프라) 장점

 수동으로 코드를 변환하지 않아도 되므로 소프트웨어를 효율적으로 배포할 수 있다. IaC(코드형 인프라)는 데브옵스의 일종으로 이를 도입한 조직은 배포 횟수를 200배 늘렸고 오류를 24배 빠르게 개선하며 배포 시간을 2,555배 줄였다.


1. 자급식 배포 

'마법의 명령어'를 알고 있는 소수의 시스템 관리자만 프로덕션 환경에 접속하여 배포를 진행해 왔다. 하지만 인프라를 코드로 정의하면 전체 배포 프로세스를 자동화할 수 있으며 개발자도 필요할 때마다 자체적으로 배포를 진행할 수 있다.


2. 속도와 안정성

배포 프로세스를 자동화하면 사람이 진행하는 것보다 훨씬 빠르게 컴퓨터가 배포를 진행할 수 있다. 자동화된 프로세스는 일관되고 반복 가능하며 수동으로 진행했을 때보다 오류가 적게 발생하기 때문에 안전하다.


3. 문서화 

시스템 관리자뿐만 아니라 누구나 읽을 수 있는 소스 파일로 인프라 상태를 나타낼 수 있다. 즉 모든 사람이 인프라 구조를 이해하고 업무를 볼 수 있도록 한다.


4. 버전 관리

인프라의 변경 내용이 모두 기록된 코드형 인프라 소스 파일을 저장할 수 있으므로 버전을 쉽게 관리할 수 있다. 시스템에 문제가 생겼을 때 문제가 발생한 지점을 찾기가 수월하다. 문제의 내용을 확인한 다음 문제가 없던 이전 코드로 다시 되돌리면 문제가 해결된다. (스냅샷)


5. 유효성 검증

인프라 상태가 코드로 정의되어 있으면 코드가 변경될 때마다 검증을 수행하고 일련의 자동화된 테스트를 실행할 수 있다.


6. 재사용성

인프라를 재사용 가능한 모듈로 패키징할 수 있으므로 모든 제품을 매번 처음부터 배포하는 대신 문서화되고 검증된 모듈로 일관되게 배포할 수 있다.


테라폼이란?

  HashiCorp Terraform은 버전을 지정하고 재사용하고 공유할 수 있는 사람이 읽을 수 있는 구성파일에서 클라우드 및 온프레미스 리소스를 모두 정의할 수 있는 코드형 인프라 도구이다. 그런 다음 일관된 워크플로를 사용하여 수명 주기 동안 모든 인프라를 프로비저닝하고 관리할 수 있다. Terraform은 컴퓨팅, 스토리지 및 네트워킹 리소스와 같은 하위 수준 구성 요소는 물론 DNS 항목 및 SaaS 기능과 같은 상위 수준 구성 요소를 관리할 수 있다.


테라폼의 동작 방식 

  Terraform은 API(응용 프로그래밍 인터페이스)를 통해 클라우드 플랫폼 및 기타 서비스에서 리소스를 생성하고 관리한다. 제공자를 통해 Terraform은 액세스 가능한 API를 통해 거의 모든 플랫폼 또는 서비스와 함께 작동할 수 있다.


  HashiCorp와 Terraform 커뮤니티는 이미 수천 가지 유형의 리소스와 서비스를 관리하기 위해 1700개 이상의 공급자를 작성했으며 이 숫잔느 계속해서 증가하고 있다. Amazon Web Services(AWS), Azure, Google Cloud Platform(GCP), Kubernetes, Helm, GitHu7b, Splunk, DataDog 등을 포함하여 Terraform Registry에서 공개적으로 사용 각능한 모든 공급자를 찾을 수 있다.

핵심 Terraform 워크플로는 세 단계로 구성된다.

  • 쓰기: 여러 클라우드 공급자 및 서비스에 걸쳐 있을 수 있는 리소스를 정의한다. 예를 들어 보안 그룹 및 로드 밸런사가 있는 Virtual Private Cloud(VPC) 네트워크의 가상 머신에 애플리케이션을 배포하기 위한 구성을 생성할 수 있다.
  • 계획: Terraform은 기존 인프라 및 구성을 기반으로 생성, 업데이트 또는 삭제할 인프라를 설명하는 실행 계획을 생성.
  • 적용: 승인 시 Terraform은 리소스 종속성을 고려하여 제안된 작업을 올바른 순서로 수행한다. 예를 들어 VPC의 속성을 업데이트하고 해당 VPC의 가상 머신 수를 변경하면 Terraform은 가상 머신을 확장하기 전에 VPC를 다시 생성한다.

 

 

 


VirtualBox 실습 환경 세팅

CentOS7 VM 생성







설정에서 프로세서 개수 변경 / 네스티드 VT-x/AMD-V 사용하기 체크(중첩된 가상화)

디스크 파일 선택 - CentOS7 선택

오디오 체크 해제

USB 체크 해제

CentOS7 설치

'시작'을 눌러 VM 실행

'Install CentOS7' 엔터

'Continue' 클릭

'I will configure partitioning' 클릭 후에 'Done'
'Click here to create them automatically' 클릭
'/home' 디렉토리 삭제
루트 디렉토리에 용량을 모두 넣어준다.
'Accept Changes' 클릭




ROOT PASSWORD'에서 루트 비밀번호 설정

MobaXterm에서 설정

VBox에서 아이피 확인

MobaXterm에서 SSH 접속

# selinux  설정
vi /etc/selinux/config
// 다음 항목 수정
SELINUX=disabled
//

# 업데이트
yum update -y

스냅샷 생성

현재 CentOS7의 업데이트와 설정이 모두 끝난 순수한 상태를 스냅샷으로 남긴다.

우선, CentOS7의 VM을 poweroff 한다.

스냅샷 생성.

이후에는 디폴트 값 그대로 다음을 누르면 된다.

'문서'파일에 생성된다. 원하는 폴더로 이동시킨다.

AWS CLI

AWS CLI 설치

# awscli 다운로드
curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"

# 여러 패키지 설치
yum install -y unzip wget bash-completion mysql git

# 다운로드 받은 awscli 압축 해제
unzip awscliv2.zip

# 설치 파일 실행
./aws/install

# 자동 완성 설치 후, bash_profile안에 집어넣어서 리붙 시에도 유지하도록 설정
echo "complete -C '/usr/local/bin/aws_completer' aws" >> .bash_profile

# 나가고 난 후 재접속
exit

사용자 추가

IAM에서 사용자를 추가.



사용자 추가 완료.

액세스 키 만들기

생성한 사용자로 접속해서 '액세스 키 만들기' 클릭

CLI 선택 후 다음

'액세스 키 만들기' 클릭

액세스 키 생성 완료.

AWS CLI 자격증명

#발급 받은 액세스 키의 아이디와 키 값, 지역, 포맷을 입력.
aws configure

AWS CLI - VPC와 서브넷 만들기

# VPC 생성. 대역폭은 192.168.0.0/16, 리소스 타입은 vpc, 'Name'키에 'test-vpc'값, json이 아닌 text로 출력 
aws ec2 create-vpc --cidr-block 192.168.0.0/16 --tag-specifications "ResourceType=vpc,Tags=[{Key=Name,Value=test-vpc}]" --output text
VPC생성 완료. 생선된 vpc의 아이디를 기억한다.

# 변수 지정
test_vpc=[생성한 VPC의 아이디]

# 변수 확인
echo $test_vpc

# 서브넷 생성. VPC 아이디 지정, 대역폭, 가용 영역, 태그
aws ec2 create-subnet --vpc-id $test_vpc --cidr-block 192.168.0.0/20 --availability-zone ap-northeast-2a --tag-specifications "ResourceType=subnet,Tags=[{Key=Name,Value=test-pub-2a}]"
파일이 열리는데, ' :q '로 나오면 된다.
# 같은 방법으로 다른 가용 영역 '2b, 2c, 2d'도 추가 (퍼블릭, 프라이빗 나누므로 총 2개씩)
aws ec2 create-subnet --vpc-id $test_vpc --cidr-block 192.168.16.0/20 --availability-zone ap-northeast-2b --tag-specification "ResourceType=subnet,Tags=[{Key=Name,Value=test-pub-2b}]"

aws ec2 create-subnet --vpc-id $test_vpc --cidr-block 192.168.32.0/20 --availability-zone ap-northeast-2c --tag-specification "ResourceType=subnet,Tags=[{Key=Name,Value=test-pub-2c}]"

aws ec2 create-subnet --vpc-id $test_vpc --cidr-block 192.168.48.0/20 --availability-zone ap-northeast-2d --tag-specification "ResourceType=subnet,Tags=[{Key=Name,Value=test-pub-2d}]"

aws ec2 create-subnet --vpc-id $test_vpc --cidr-block 192.168.64.0/20 --availability-zone ap-northeast-2a --tag-specification "ResourceType=subnet,Tags=[{Key=Name,Value=test-pvt-2a}]"

 aws ec2 create-subnet --vpc-id $test_vpc --cidr-block 192.168.80.0/20 --availability-zone ap-northeast-2b --tag-specification "ResourceType=subnet,Tags=[{Key=Name,Value=test-pvt-2b}]"
 
 aws ec2 create-subnet --vpc-id $test_vpc --cidr-block 192.168.96.0/20 --availability-zone ap-northeast-2c --tag-specification "ResourceType=subnet,Tags=[{Key=Name,Value=test-pvt-2c}]"
 
 aws ec2 create-subnet --vpc-id $test_vpc --cidr-block 192.168.112.0/20 --availability-zone ap-northeast-2d --tag-specification "ResourceType=subnet,Tags=[{Key=Name,Value=test-pvt-2d}]"

# 특정 VPC ID를 가지고 거기에 있는 서브넷들을 조회(항목은 가용 영역, 대역폭, 서브넷 이름 태그)
aws ec2 describe-subnets --filters "Name=vpc-id,Values=$test_vpc" --query 'Subnets[*].{AZ:AvailabilityZone,CIDR:CidrBlock,SUBNET:Tags[0].Value}'
생성했던 서브넷들의 원하는 항목들이 나온다.

AWS CLI - 퍼블릭 및 프라이빗 서브넷으로 만들기 

 앞에서 만든 서브넷은 아직 퍼블릭, 프라이빗 지정을 안했다. 그러므로 이제 지정할 차례다.

# 인터넷 게이트웨이 생성
aws ec2 create-internet-gateway --tag-specification "ResourceType=internet-gateway,Tags=[{Key=Name,Value=test-igw}]" --output text
인터넷 게이트웨이 생성 완료. 인터넷 게이트웨이의 아이디를 기억한다.

# 변수 지정
test_igw=[인터넷 게이트웨이 아이디]

# 변수 확인
echo $test_igw

# 인터넷 게이트웨이와 VPC 연결
aws ec2 attach-internet-gateway --vpc-id $test_vpc --internet-gateway-id $test_igw

# 인터넷 게이트웨이 조회(이름과 값으로 필터를 거쳐 조회), table로 출력
aws ec2 describe-internet-gateways --filters "Name=internet-gateway-id,Values=$test_igw" --output table
필터를 거쳐 검색된 게이트웨이를 테이블로 출력.

# 라우팅 테이블 필터를 거쳐 조회
aws ec2 describe-route-tables --filter "Name=vpc-id,Values=$test_vpc"
라우팅 테이블 조회. ID를 기억한다.

# 변수 지정
test_pub_rtb=rtb-00ea81f8d6bdfc354

# 라우팅 정보 생성. (라우팅 테이블 아이디 지정, 대상 지정, 게이트웨이 아이디 지정)
aws ec2 create-route --route-table-id $test_pub_rtb --destination-cidr-block 0.0.0.0/0 --gateway-id $test_igw

# test_pub_rtb 라우팅 테이블에 태그 달기
aws ec2 create-tags --resources $test_pub_rtb --tags "Key=Name,Value=test-pub-rtb"

# test_pub_rtb 라우팅 테이블을 테이블 형태로 출력
aws ec2 describe-route-tables --route-table-ids $test_pub_rtb --output table

# 서브넷 조회(필터를 추가하되 추가로 쿼리문을 이용해 원하는 항목만 출력)
aws ec2 describe-subnets --filters "Name=vpc-id,Values=$test_vpc" --query 'Subnets[*].{ID:SubnetId,CIDR:CidrBlock,TAGS:Tags[0].Value}'
이 정보들을 이용하여 변수 지정을 하여 보다 쉽게 사용.

# 서브넷들 변수 지정
test_pub_2a=[test_pub_2a(192.168.0.0/20)의 아이디]
test_pub_2b=[test_pub_2b(192.168.16.0/20)의 아이디]
test_pub_2c=[test_pub_2c(192.168.32.0/20)의 아이디]
test_pub_2d=[test_pub_2d(192.168.48.0/20)의 아이디]
test_pvt_2a=[test_pvt_2a(192.168.64.0/20)의 아이디]
test_pvt_2b=[test_pvt_2b(192.168.80.0/20)의 아이디]
test_pvt_2c=[test_pvt_2c(192.168.96.0/20)의 아이디]
test_pvt_2d=[test_pvt_2d(192.168.112.0/20)의 아이디]

# 테이블과 서브넷을 명시적 연결
aws ec2 associate-route-table --subnet-id $test_pub_2a --route-table-id $test_pub_rtb
aws ec2 associate-route-table --subnet-id $test_pub_2b --route-table-id $test_pub_rtb
aws ec2 associate-route-table --subnet-id $test_pub_2c --route-table-id $test_pub_rtb
aws ec2 associate-route-table --subnet-id $test_pub_2d --route-table-id $test_pub_rtb

# 퍼블릭 서브넷에 시작할 때 퍼블릭 아이피를 주도록 설정
aws ec2 modify-subnet-attribute --subnet-id $test_pub_2a --map-public-ip-on-launch
aws ec2 modify-subnet-attribute --subnet-id $test_pub_2b --map-public-ip-on-launch
aws ec2 modify-subnet-attribute --subnet-id $test_pub_2c --map-public-ip-on-launch
aws ec2 modify-subnet-attribute --subnet-id $test_pub_2d --map-public-ip-on-launch

# 라우팅 테이블 생성
aws ec2 create-route-table --vpc-id $test_vpc

# 변수 지정 (프라이빗 테이블)
test_pvt_rtb=[생성한 라우팅 테이블의 아이디]

# 태그 달기
aws ec2 create-tags --resources $test_pvt_rtb --tags "Key=Name,Value=test-pvt-rtb"

# 테이블과 서브넷을 명시적 연결
aws ec2 associate-route-table --subnet-id $test_pvt_2a --route-table-id $test_pvt_rtb
aws ec2 associate-route-table --subnet-id $test_pvt_2b --route-table-id $test_pvt_rtb
aws ec2 associate-route-table --subnet-id $test_pvt_2c --route-table-id $test_pvt_rtb
aws ec2 associate-route-table --subnet-id $test_pvt_2d --route-table-id $test_pvt_rtb

# 도메인 주소를 활성화.
aws ec2 modify-vpc-attribute --vpc-id $test_vpc --enable-dns-hostnames

AWS CLI - 키페어, 보안그룹 만들기 

# 키페어 생성. (이름 "test-key" 지정하여 생성한 뒤, 쿼리로 "KeyMaterial"검색하고 text로 출력하여 "test-key.pem"에 저장)
aws ec2 create-key-pair --key-name test-key --query 'KeyMaterial' --output text > test-key.pem

# 키 퍼미션 400으로 변경 (항상 키 파일은 관리자만 ReadOnly)
chmod 400 test-key.pem
키 값이 담긴 'test-key.pem' 생성
실제로 키 값이 들어있다.

# 보안 그룹 생성 (이름은 'test-sg-web, 설명 입력, vpc-id 명시, 리소스타입과 태그 입력)
aws ec2 create-security-group --group-name test-sg-web --description "Security group for HTTP_SSH access" --vpc-id $test_vpc --tag-specification "ResourceType=security-group,Tags=[{Key=Name,Value=test-sg-web}]"
보안 그룹 생성 완료.

# 보안 그룹 변수 지정
test_web_sg=[보안 그룹의 아이디]

# 인바운드 규칙 (유형, 포트, 대역폭)
aws ec2 authorize-security-group-ingress --group-id $test_web_sg --protocol tcp --port 22 --cidr [내아이피]/0
aws ec2 authorize-security-group-ingress --group-id $test_web_sg --protocol tcp --port 80 --cidr [내아이피]/0
aws ec2 authorize-security-group-ingress --group-id $test_web_sg --protocol icmp --port -1 --cidr [내아이피]/0

# 보안 그룹 출력 (테이블로)
aws ec2 describe-security-groups --group-id $test_web_sg --output table

AWS CLI - 볼륨 및 인스턴스 만들기

# mapping.json 생성 및 내용 입력(루트 불륨을 EBS로 사이즈 8, 세컨드 불륨도 EBS 8기가)
vi mapping.json
//
[
    {
        "DeviceName": "/dev/xvda",
        "Ebs": {
            "VolumeSize": 8
        }
    },
    {
        "DeviceName": "/dev/xvdb",
        "Ebs": {
            "VolumeSize": 8
        }
    }
]
//

# 스크립트(사용자 데이터) 생성
vi my_script.txt
//
#!/bin/bash
yum install -y httpd
systemctl enable --now httpd
echo "<h1>Hello AWS CLI</h1>" > /var/www/html/index.html
//

AMI 카탈로그에서 사용할 이미지의 아이디를 기억한다.
# 인스턴스 생성 (이미지 아이딧 선택, 개수는 1개, 인스턴스 타입은 t2.micro, 키 페어는 'test-key'
# 보안 그룹 지정, 서브넷 아이디 지정, 블록 디바이스는 json 파일 참조, 사용자 데이터 지정,
# 리소스 타입, 태그 추가 (볼륨에도 태그를 추가)
aws ec2 run-instances \
--image-id [사용할 이미지 아이디] \
--count 1 \
--instance-type t2.micro \
--key-name test-key \
--security-group-ids $test_web_sg \
--subnet-id $test_pub_2a \
--block-device-mappings file://mapping.json \
--user-data file://my_script.txt \
--tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=test-web}]' 'ResourceType=volume,Tags=[{Key=Name,Value=test-root}]'
인스턴스 생성. 인스턴스 아이디를 기억한다.
콘솔 창에서도 확인 가능하다.

# 인스턴스 아이디 변수 지정
test_iid=[인스턴스 아이디]

# 인스턴스 조회 (퍼블릭 아이피만)
aws ec2 describe-instances --instance-id $test_iid | grep PublicIp

# 인스턴스에 SSH 접속
ssh -i "test-key.pem" ec2-user@[인스턴스 아이피]
퍼블릭 아이피 확인
ssh 접속 확인.

# lsblk로 확인을 하면 xvdb가 마운트가 안됨을 알 수 있다.(자동으로 안됨)
lsblk

# 포멧
sudo mkfs -t xfs /dev/xvdb

# 마운트
sudo mount /dev/xvdb /mnt
마운트가 안됐다.
마운트 정상적으로 완료.

 AWS CLI - 삭제

# 다음 순서로 여태까지의 과정을 정리할 수 있다. (해당 인스턴스에서 접속을 해제한 상태에서 가능)
aws ec2 terminate-instances --instance-id $test_iid
aws ec2 delete-security-group --group-id $test_web_sg
aws ec2 delete-subnet --subnet-id $test_pub_2a
aws ec2 delete-subnet --subnet-id $test_pub_2b
aws ec2 delete-subnet --subnet-id $test_pub_2c
aws ec2 delete-subnet --subnet-id $test_pub_2d
aws ec2 delete-subnet --subnet-id $test_pvt_2a
aws ec2 delete-subnet --subnet-id $test_pvt_2b
aws ec2 delete-subnet --subnet-id $test_pvt_2c
aws ec2 delete-subnet --subnet-id $test_pvt_2d
aws ec2 detach-internet-gateway --internet-gateway-id $test_igw --vpc-id $test_vpc
aws ec2 delete-internet-gateway --internet-gateway-id $test_igw
aws ec2 delete-route-table --route-table-id $test_pvt_rtb
aws ec2 delete-route --route-table-id $test_pub_rtb --destination-cidr-block 0.0.0.0/0
aws ec2 delete-vpc --vpc-id $test_vpc
인스턴스가 종료됐다.