본문 바로가기
IaC/Terraform

[Terraform] 1-2. Cloud-init을 활용하여 EC2 웹 서버 배포하기

by okms1017 2024. 6. 16.
728x90

✍ Posted by Immersive Builder  Seong

 

 

1. 실습 소개 

이번 실습에서는 테라폼을 사용하여 간단한 EC2 웹 서버를 배포합니다. 

실습은 AWS 환경에서 진행하며, 작업 순서는 하기와 같습니다. 

 

  • Step1. 기본 VPC(default) 구성 확인 
  • Step2. 최신 AMI 이미지(Ubuntu) 가져오기 
  • Step3. Cloud-init Script 작성
  • Step4. 테라폼 구성파일 작성
  • Step5. EC2 웹 서버 배포 및 접속 테스트 

 


▶ 테라폼 기본 사용법

 

[AEWS2] 8-1. What is Terraform ?

✍ Posted by Immersive Builder  Seong  1. Terraform 기본 개념  Terraform 이란?HashiCorp에서 공개한 인프라스트럭처를 코드로 프로비저닝하고 관리할 수 있는 오픈소스 도구입니다.  HCL(HashiCorp Configuration

okms1017.tistory.com

 


2. EC2 웹 서버 배포하기 

Step1. 기본 VPC 구성 확인

AWS 계정 생성 시 기본으로 구성되는 VPC/Subnet에 EC2 웹서버를 배포할 예정입니다. 

따라서 기본 디폴트 VPC와 Subnet 정보를 사전에 확인합니다. 

 

  • VPC(default-vpc)

default-vpc(172.31.0.0/16)

$ aws ec2 describe-vpcs --filter 'Name=isDefault,Values=true' | jq '.Vpcs[0].VpcId'
"vpc-7dac0a16"

 

$ aws ec2 describe-vpcs --filter 'Name=isDefault,Values=true' | jq '.Vpcs[0].Tags[0].Value'
"default-vpc"

 

$ aws ec2 describe-vpcs --filter 'Name=isDefault,Values=true' | jq '.Vpcs[0].CidrBlock'
"172.31.0.0/16"

 

  • Subnets

default-subnets

$ aws ec2 describe-subnets --filter 'Name=vpc-id,Values=vpc-7dac0a16' --output table

|+-----------------------------+----------------------------------------------------------------+|

||  SubnetId                    |  subnet-3b06d650                                                       ||

||  CidrBlock                   |  172.31.0.0/20                                                              ||

|+-----------------------------+----------------------------------------------------------------+|

||  SubnetId                    |  subnet-c752dfbc                                                        ||

||  CidrBlock                   |  172.31.16.0/20                                                            ||

|+-----------------------------+----------------------------------------------------------------+|

||  SubnetId                    |  subnet-4dcbcf01                                                        ||

||  CidrBlock                   |  172.31.32.0/20                                                            ||

|+-----------------------------+----------------------------------------------------------------+|

||  SubnetId                    |  subnet-90aef7cc                                                         ||

||  CidrBlock                   |  172.31.48.0/20                                                            ||

|+-----------------------------+----------------------------------------------------------------+|

 

Step2. 최신 AMI 이미지 가져오기 

배포할 EC2 웹 서버의 OS는 Ubuntu로 진행합니다. 

 

데이터 소스를 정의하여 AWS에서 제공하는 최신 Ubuntu AMI 이미지를 가져올 수 있습니다. 

데이터 소스란 테라폼으로 정의되지 않은 외부 리소스 또는 저장된 정보를 테라폼 내에서 참조하기 위해 사용합니다. 

 


        data "aws_ami" "ubuntu" {
        most_recent = true

        filter {
            name   = "name"
            values = ["ubuntu/images/hvm-ssd-gp3/ubuntu-noble-24.04-amd64-server-*"]
        }

        filter {
            name   = "virtualization-type"
            values = ["hvm"]
        }

        owners = ["099720109477"] # Canonical
        }
       

 

$ terraform plan && terraform apply -auto-approve

data.aws_ami.ubuntu: Reading...
data.aws_ami.ubuntu: Read complete after 0s [id=ami-07d1d85660ec58cd3]

Apply complete! Resources: 0 added, 0 changed, 0 destroyed.

 

$ terraform console

> data.aws_ami.ubuntu.id
"ami-07d1d85660ec58cd3"

 

Step3. Cloud-init Script 작성 

Cloud-init 은 클라우드 환경에서 인스턴스(VM)를 프로비저닝할 때 호스트 이름, 네트워크 인터페이스, 인증키 설정 등의 초기 설정과 각 인스턴스의 구성 어플리케이션 설치를 자동화하는 도구입니다. 

 

테라폼으로 인스턴스를 프로비저닝하는 경우, Cloud-init 을 사용함으로써 각 인스턴스에 필요한 어플리케이션과 해당 종속성을 자동으로 설치 및 구성하여 시간을 절약하고 대규모로 유지 관리할 수 있습니다. 

 

Cloud-init 은 대부분의 리눅스 배포판과 주요 클라우드 제공업체에서 사용이 가능한 표준 구성 지원 도구입니다. 

 

여기서는 패스워드 없이 인스턴스에 접속하도록 하는 SSH 인증키 설정과 Apache 웹 서버 설치 구성을 스크립트 형식으로 user_data에 넣어 전달할 것입니다. 

 

  • 로컬 SSH 인증키 생성

로컬 터미널에서 인스턴스에 접속할 SSH 인증키를 생성합니다. 

키 생성 명령어 인자 값으로 이메일 주소를 입력하고 패스워드 문자열은 공란으로 넘어갑니다. 

 

$ ssh-keygen -t rsa -C "okms1017@gmail.com" -f ./tf-cloud-init

Generating public/private rsa key pair.
Enter passphrase (empty for no passphrase): 
Enter same passphrase again: 
Your identification has been saved in ./tf-cloud-init
Your public key has been saved in ./tf-cloud-init.pub
The key fingerprint is:
SHA256:xYSERep1i72r9ANukd+U6UlZoxsNohymbKMmbmDphqk okms1017@gmail.com
The key's randomart image is:
+---[RSA 3072]----+
|       =+..      |
|      ...o       |
|      . . +      |
|     . .o=... o  |
|  .  ..+S+o. O . |
|.o    = *  .O .  |
|+o   o o.+.= +   |
|ooo o  .o.o.=    |
|Eo.o   ...o.     |
+----[SHA256]-----+

 

명령어 실행 이후 'tf-cloud-init'와 'tf-cloud-init.pub' 2개의 파일이 생성된 것을 확인합니다. 

 

  • Cloud-init Script 작성 

scripts 디렉토리를 생성하고 하위 경로에 'add-ssh-apache.yaml' 스크립트 파일을 작성합니다. 

 

 # add-ssh-apache.yaml
 
        groups:
          - ubuntu: [root,sys]
          - devops

        # Add users to the system. Users are added after groups are added.
        users:
          - default
          - name: okms1017
            gecos: okms1017
            shell: /bin/bash
            primary_group: devops
            sudo: ALL=(ALL) NOPASSWD:ALL
            groups: users, admin
            lock_passwd: false
            ssh_authorized_keys:
              - ssh-rsa AAAAB3NzaC1yc2EAAAADAQ***************************= okms1017@gmail.com

        # Downloads the apache2 package
        packages:
          - apache2

        runcmd:
          - echo "Welcome to Seong's Skill Builder!" > /var/www/html/index.html
:
 

 

  • groups : ubuntu, devops 그룹을 생성하고 ubuntu 그룹에 root, sys 시스템 계정을 포함합니다 
  • users : 패스워드 없이 접속이 가능하고 관리자 권한의 명령어를 수행할 수 있는 시스템 계정을 생성합니다 
  • users[1].ssh_authorized_keys : 로컬에서 생성한 SSH 공개키를 입력합니다 
  • packages : 어플리케이션 패키지를 다운로드하고 실행합니다. ex) apache2 
  • runcmd : 인스턴스 부팅 이후 수행할 명령어를 입력합니다. Apache 메인 페이지 문구를 변경합니다. 

 

Step4. 테라폼 구성파일 작성 

EC2 웹 서버를 배포하기 위한 테라폼 구성파일을 작성합니다. 

 

구성파일(*.tf)은 테라폼과 프로바이더 버전을 명시한 terraform.tf 파일, 데이터 소스와 리소스를 정의한 main.tf 파일, 변수 값을 전달하는 variables.tf 파일로 구성됩니다.

 

그리고 user_data를 전달하기 위한 add-ssh-apache.yaml 파일이 있습니다. 

 

./deploy/
├── instance
│   ├── main.tf
│   ├── terraform.tf
│   └── variables.tf
└── scripts
    └── add-ssh-apache.yaml

 

1) terraform.tf : 테라폼과 프로바이더(aws) 버전 명시 

 

 
        terraform {
          required_providers {
            aws = {
              source  = "hashicorp/aws"
              version = "~> 5.47.0"
            }
          }

          required_version = "~> 1.1"
        }
 

 

  • terraform.required_providers.aws.version : 프로바이더(aws) 버전은 v5.47.0을 포함한 v5.47.x을 허용하나, v5.x는 허용하지 않음
  • terraform.required_version : 테라폼 버전은 v1.1을 포함한 v1.x를 허용하나, v2.0 이상은 허용하지 않음

 

2) main.tf : 데이터 소스와 리소스, Output 블록 정의 

 


        provider "aws" {
          region = var.region
        }

        data "aws_ami" "ubuntu" {
          most_recent = true

          filter {
            name   = "name"
            values = ["ubuntu/images/hvm-ssd-gp3/ubuntu-noble-24.04-amd64-server-*"]
          }

          filter {
            name   = "virtualization-type"
            values = ["hvm"]
          }

          owners = ["099720109477"] # Canonical
        }

        data "aws_vpc" "default-vpc" {
          filter {
            name   = "tag:Name"
            values = ["default-vpc"]
          }
        }

        data "aws_subnet" "default-subnet" {
          filter {
            name   = "tag:Name"
            values = ["default-subnet01"]
          }
        }
 
        resource "aws_security_group" "web_sg" {
          name   = "web-sg"
          vpc_id = data.aws_vpc.default-vpc.id
        }

        resource "aws_security_group_rule" "sg_22" {
          type              = "ingress"
          from_port         = 22
          to_port           = 22
          protocol          = "tcp"
          cidr_blocks       = ["0.0.0.0/0"]
          security_group_id = aws_security_group.web_sg.id
        }

        resource "aws_security_group_rule" "sg_80" {
          type              = "ingress"
          from_port         = 80
          to_port           = 80
          protocol          = "tcp"
          cidr_blocks       = ["0.0.0.0/0"]
          security_group_id = aws_security_group.web_sg.id
        }

        resource "aws_security_group_rule" "sg_00" {
          type              = "egress"
          from_port         = 0
          to_port           = 0
          protocol          = "-1"
          cidr_blocks       = ["0.0.0.0/0"]
          security_group_id = aws_security_group.web_sg.id
        }

        resource "aws_instance" "web" {
          ami                         = data.aws_ami.ubuntu.id
          instance_type               = "t3.medium"
          subnet_id                   = data.aws_subnet.default-subnet.id
          vpc_security_group_ids      = [aws_security_group.sg_22.id, aws_security_group.sg_80.id]
          associate_public_ip_address = true
          user_data                   = file("../scripts/add-ssh-apache.yaml")

          tags = {
            Name = "Apache-WEB"
          }
        }

        output "public_ip" {
          value = aws_instance.web.public_ip
        }
 

 

  • var.region : variables.tf 파일에서 선언한 region 변수 값 (ap-northeast-2) 
  • data.aws_ami.ubuntu : 'name'과 'virtualization-type'으로 필터링하여 가장 최신의 Ubuntu AMI 이미지를 가져옴 
  • data.aws_vpc.default-vpc : vpc 태그 값으로 필터링하여 기본 디폴트 VPC 정보를 가져옴 
  • data.aws_subnet.default-subnet : subnet 태그 값으로 필터링하여 기본 Subnet 정보를 가져옴 
  • aws_security_group.web_sg : 신규 웹 서버에 연결할 보안 그룹 
  • aws_security_group_rule.sg_22 :  신규 웹 서버에 접속(SSH)하기 위한 22(inbound) 보안 그룹
  • aws_security_group_rule.sg_80 : 신규 웹 서비스(HTTP)를 위한 80(inbound) 보안 그룹
  • aws_security_group_rule.sg_00 : 웹 서버에 대한 Any(Outbound) 보안 그룹 
  • sg 보안그룹 규칙은 추가·삭제·변경을 고려하여 별도 리소스 블록으로 분리 선언함 
  • aws_instance.web : 디폴트 VPC/Subnet 정보와 AMI 이미지를 받아 보안그룹, user_data 정보와 함께 생성되는 인스턴스 리소스 
  • output.public_ip : 리소스 프로비저닝 이후 할당되는 속성 값(public ip)을 화면 또는 파일로 출력함 

 

3) variables.tf : 변수 선언 

 


        variable "region" {
          description = "The region that terraform deploys web-instance"
          default     = "ap-northeast-2"
        }
 

 

  • variable.region.description : region 변수의 부연 설명 
  • variable.region.default : region 변수의 실제 값으로, 리전명이 기입됨  ex) ap-northeast-2, us-east-1 

 

Step5. EC2 웹 서버 배포 및 접속 테스트 

작업 공간을 초기화합니다. 

 

$ terraform init

 

./.terraform
└── providers
    └── registry.terraform.io
        └── hashicorp
            └── aws
                └── 5.47.0
                    └── linux_amd64
                        └── terraform-provider-aws_v5.47.0_x5

 

구성파일 작성을 완료하면 실행계획을 생성하고 이상이 없을 시 EC2 웹 서버를 배포합니다. 

 

$ terraform plan && terraform apply -auto-approve 

aws_security_group.sg_80: Creation complete after 1s [id=sg-097fb967c94d7b31f]
aws_security_group.sg_22: Creation complete after 2s [id=sg-0e5f40e72878481c1]

aws_instance.web: Creation complete after 12s [id=i-03ff38cb92ce3af40]

 

Apply complete! Resources: 3 added, 0 changed, 0 destroyed.

 

서버가 배포되고 어플리케이션이 실행되기까지 약 3분 정도가 소요됩니다. 

지정한 default-subnet01(172.31.0.0/24) 네트워크 대역에 서버(172.31.1.57/32)가 배포되고, 외부 접속이 가능한 공인 IP(43.200.7.34/32)를 할당받았습니다. 

 

EC2 WEB 인스턴스 배포 (Apache-WEB)
SG 보안그룹 생성 완료

 

  • SSH 접속 테스트 

로컬에서 해당 웹 서버로 패스워드 없이 SSH 접속을 시도합니다. 

새로 생성한 계정(okms1017)으로 정상 접속이 되고 있습니다. 

 

$ terraform output -raw public_ip

43.200.7.34

 

$ ssh okms1017@$(terraform output -raw public_ip) -i ./tf-cloud-init

Welcome to Ubuntu 24.04 LTS (GNU/Linux 6.8.0-1009-aws x86_64)

IPv4 address for ens5: 172.31.1.57

 

  • WEB 서비스 테스트 

Apache 서비스 또한 user_data로 전달한 문구가 정상적으로 출력되는 모습입니다. 

 

$ while true; do curl --connect-timeout 1  http://43.200.7.34:80/ ; echo "------------------------------"; date; sleep 1; done

Sun Jun 15 :16:26 KST 2024
Welcome to Seong's Skill Builder!

 

실습을 완료하고 리소스를 반납합니다. 

 

$ terraform destroy -auto-approve

 

 


[출처]

1) https://developer.hashicorp.com/terraform/tutorials/provision/cloud-init

 

Provision infrastructure with Cloud-Init | Terraform | HashiCorp Developer

Deploy preconfigured infrastructure with Terraform using the Cloud-Init tool.

developer.hashicorp.com

2) https://cloudinit.readthedocs.io/en/latest/reference/examples.html

 

Cloud config examples - cloud-init 24.1.7 documentation

Previous Module reference

cloudinit.readthedocs.io

728x90