Terraform과 함께 AWS 구축하기(feat. Minecraft)

maintain·2021년 9월 15일
0

계기

최근에 친구들과 함께 Minecraft를 즐기고 있습니다. 얼마간은 소싯적 Minecraft 해본 친구 한 명이 기억을 되살려 친구의 개인 PC에 서버를 구축해서 게임을 했습니다. 하지만 개인 PC라는 특성 상 친구가 강의를 듣거나 기타 작업을 할 때만 굉장히 방해가 됩니다. 또한 전기세와 발열 문제도 있고요. 그런고로 24시간 유지가 힘들었고, 게임하고 싶을 때 그 친구를 오매불망 기다려야 했습니다. 그래서! 24시간 하고 싶을 때 게임하고, 겸사겸사 공부도 할 겸해서 AWS에 서버를 구축하게 되었습니다.

계획

최우선 목표는 빠르게 24시간 게임을 할 수 있는 환경을 구축하는 것입니다. 저야 겸사겸사 이것저것 붙이면서 공부하는 목적도 있지만 그건 저한테만 중요한 목적입니다. 그래서 먼저 관리기능이나, 비용 최적화 등은 나중에 추가하고 서버에 올라가서 24시간 작동만 하는 것을 목표로 했습니다.

위 사항을 반영해 만든 설계 초안은 다음과 같습니다.

  • 친구 몇 명 정도만 사용할 것이기에 굉장히 좋은 성능은 필요가 없습니다. 거기에 더해 비용을 줄이기 위해 인스턴스는 하나만 띄우기로 했습니다.
  • 편리하게 서버를 띄우기 위해 itzg의 docker 이미지를 이용했습니다.
  • 나중에 추가될 수 있는 기능 간 통신은 편의성 + 인스턴스 1개의 제약 때문에 docker-compose를 이용하기로 결정했습니다.
  • ssh, 마인크래프트 포트만 인바운드로 그 외에는 인터넷과 연결할 http와 https만 아웃바운드로 보안 정책을 설정합니다.
  • Terraform을 이용합니다. 개인적으로 최근에 관심이 있던 기술이라 꼭 써보고 싶었습니다.
    - 보안 및 싱크 등의 이유로 상태를 저장하는 tfstate파일은 S3같은 외부 저장소를 사용할 것이 권장되지만, 혼자 작업할 것이기 때문에 git을 이용합니다.

테라폼 설치 및 간단하게 살펴보기

Terraform이 뭐죠? 이걸 쓰는 이유는 뭐죠?

테라폼은 Hashicorp 사에서 개발한 인프라 관리 도구입니다. 인프라를 코드로 관리하는 IoC(Infrastructure as Code) 개념의 대표주자 중 하나로 선언적 언어로 인프라를 정의하고 관리할 수 있습니다. 코드로 인프라가 관리됨으로써, 버전 관리 시스템 안에 인프라가 들어오고, 사용하는 인프라가 한 곳에 모여 관리가 용이해진다는 장점이 있습니다. 또한 개인적으로는 웹 콘솔을 사용하는 GUI 방식과 비교했을 때 더 배우기 쉽다고 느꼈습니다. 웹 콘솔을 통하면 어떤 설정을 어디서 찾아야할지 막막한 부분이 있는데, terraform의 경우 provider 문서를 보면 대충 설정이 어디 있을지 감이 잡히기 때문에 그렇게 느껴졌습니다.

개념

  1. 프로바이더
    외부 인프라 서비스 테라폼을 연결해주는 모듈입니다. 외부 서비스와 연동하기 위해선 기본적으로 해당 프로바이더가 필요합니다.
  2. 리소스
    프로바디어가 제공하는 조작 가능한 자원의 최소 단위입니다.
  3. HCL(Hashicorp Configuration Language)
    테라폼에서 사용하는 설정 언어입니다.
  4. 계획
    작성한 설정이 실제 적용 가능한지 확인해보는 것이 계획입니다. 반복적으로 파일을 수정하면서 terraform plan 명령을 통해 계획을 확인하면서 인프라를 작성하고 마지막으로 terraform apply 명령을 통해 작성된 인프라를 반영하게 됩니다.
  5. tfstate
    반영된 인프라의 상태 정보를 가진 파일입니다. 계획을 적용할때마다 생성됩니다.

설치

설치 방법은 여러 가지가 있지만 저는 tfenv 방식을 선택했습니다. 여기를 참조하면 좋을 것 같습니다.

Terraform 파일 작성

1. 프로바이더 파일 작성


# AWS 인증키 보안 자격 증명(IAM) 페이지에서 얻을 수 있습니다.
variable "mc_aws_access_key" {
    type = string
}

# 위와 마찬가지로 보안 자격 증명 페이지에서 얻을 수 있습니다.
variable "mc_aws_secret_key" {
    type = string
}

# SSH 접속을 위한 인증 파일을 연동해준다.
resource "aws_key_pair" "mc_admin" {
    key_name = "mc_admin"
    public_key = file({파일 경로})
}

provider "aws" {
    access_key = var.mc_aws_access_key
    secret_key = var.mc_aws_secret_key
    region = "ap-northeast-2"  # 서울
}

1. VPC 작성

resource "aws_vpc" "mcVPC" {
    cidr_block = "10.0.0.0/16"

    tags = {
        name = "mcVPC"
    }
}

resource "aws_subnet" "mcPublicSubnet" {
    vpc_id = aws_vpc.mcVPC.id
    cidr_block = "10.0.0.0/24"
    availability_zone = "ap-northeast-2c"
    tags = {
        name = "mcPublicSubnet"
    }
}

resource "aws_internet_gateway" "mcIGW" {
    vpc_id = aws_vpc.mcVPC.id
    tags = {
        name = "mcIGW"
    }
}

resource "aws_route_table" "mcPublicRoute" {
    vpc_id = aws_vpc.mcVPC.id
    route {
        cidr_block = "0.0.0.0/0"
        gateway_id = aws_internet_gateway.mcIGW.id
    }
    tags = {
        name = "mcPublicRoute"
    }
}

resource "aws_route_table_association" "mcPublicRTA" {
    subnet_id = aws_subnet.mcPublicSubnet.id
    route_table_id = aws_route_table.mcPublicRoute.id
}

resource "aws_security_group" "mcPublicSG" {
    vpc_id = aws_vpc.mcVPC.id
    name = "mcPublicSG"
    description = "Game User Group"
}

# SSH 접속용
resource "aws_security_group_rule" "mcSSHSGRIngress" {
    type = "ingress"
    description = "Allow SSH port from all"
    from_port = 22
    to_port = 22
    protocol = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
    security_group_id = aws_security_group.mcPublicSG.id
    lifecycle {
        create_before_destroy = true
    }
}

# Minecraft 게임용 포트. 혹시 몰라서 인바운드 아웃바운드를 둘 다 열었다.
resource "aws_security_group_rule" "mcPublicSGRIngress" {
    type = "ingress"
    from_port = 25565
    to_port = 25565
    protocol = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
    security_group_id = aws_security_group.mcPublicSG.id
    lifecycle {
        create_before_destroy = true
    }
}
resource "aws_security_group_rule" "mcPublicSGREgress" {
    type = "egress"
    from_port = 25565
    to_port = 25565
    protocol = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
    security_group_id = aws_security_group.mcPublicSG.id
    lifecycle {
        create_before_destroy = true
    }
}

# 인스턴스 내부에서 패키지 설치 및 파일 다운로드를 위한 http
resource "aws_security_group_rule" "mcPublicSGRHTTPegress" {
  type = "egress"
  from_port = 80
  to_port = 80
  protocol = "TCP"
  cidr_blocks = [ "0.0.0.0/0" ]
  security_group_id = aws_security_group.mcPublicSG.id
  lifecycle {
    create_before_destroy = true
  }
}

# 인스턴스 내부에서 패키지 설치 및 파일 다운로드를 위한 https
resource "aws_security_group_rule" "mcPublicSGRHTTPSegress" {
  type = "egress"
  from_port = 443
  to_port = 443
  protocol = "TCP"
  cidr_blocks = [ "0.0.0.0/0" ]
  security_group_id = aws_security_group.mcPublicSG.id
  lifecycle {
    create_before_destroy = true
  }
}

2. 인스턴스 작성

인스턴스를 작성하기 전에 사용할 AMI와 인스턴스 타입을 미리 알아둘 필요가 있습니다. 인스턴스 타입은 공식 문서에서 볼 수 있습니다. AMI를 확인하는 방법은 여러 가지가 있는데 저는 AWS 콘솔에서 인스턴스를 생성하는 창에서 선택했습니다.

resource "aws_instance" "mcEC2" {
    ami = "{AMI}"  # 원하는 AMI를 사용. 저는 Ubuntu 18.04를 사용했습니다.
    instance_type = "{인스턴스 타입}"  # 원하는 인스턴스 타입을 사용합니다.
    vpc_security_group_ids = [
        aws_security_group.mcPublicSG.id
    ]
    subnet_id = aws_subnet.mcPublicSubnet.id
    key_name = aws_key_pair.mc_admin.key_name
    root_block_device {
        delete_on_termination = false  # 게임 데이터 유지를 위한 볼륨 유지 옵션
    }
}


# 고정 IP
resource "aws_eip" "mcIP" {
    instance = aws_instance.mcEC2.id
    vpc = true
    depends_on = [
      aws_internet_gateway.mcIGW
    ]
}

참조

0개의 댓글