본문 바로가기

카카오테크 부트캠프

[KTB]IaC : Terraform과 Ansible을 사용한 인프라 구축

│ Error: creating RDS DB Instance (terraform-20240807052148484400000003): InvalidVPCNetworkStateFault: Cannot create a publicly accessible DBInstance.  The specified VPC does not support DNS resolution, DNS hostnames, or both. Update the VPC and then try again
│ 	status code: 400, request id: e527579a-4d6b-4d14-ba51-72391e808779
│
│   with aws_db_instance.tripdb,
│   on main.tf line 131, in resource "aws_db_instance" "tripdb":
│  131: resource "aws_db_instance" "tripdb" {
│

테라폼을 apply하는에 이런 오류 발생..! 뭔가 하고 지피티에게 물어봤다.vpc를 제대로 설정하라는 것 같은데

일단 오늘의 오류를 해결하기 위해 제가 공부한 것들을 순차적으로 적어내려가겠습니다.


1. DNS란?

더보기

InvalidVPCNetworkStateFault: Cannot create a publicly accessible DBInstance.  The specified VPC does not support DNS resolution, DNS hostnames, or both. Update the VPC and then try again
│  status code: 400, request id: e527579a-4d6b-4d14-ba51-72391e808779

먼저 오류 원인은 지정된 VPC가 DNS해상도 또는 호스트 이름을 지원하지 않기 때문에 공개적으로 접근 가능한 RDS를 만들 수 없다는 것인데요, 제 부족한 CS지식으로 하나하나 차근히 알아가겠습니다.

 

DNS(Domain Name System) : 도메인 이름 시스템으로 인터넷에서 사용하는 "전화번호부"같은 역할을 합니다. 

예를 들어 www.google.com과 같은 도메인 이름을 사용한다면,  실제 사이트에 접근하기 위해는 142.250.190.14와 같은 ip주소가 필요합니다.

 

DNS의 역할

- 도메인 네임을 IP주소로 변환 : 도메인 이름은 사람이 기억하기 쉽고 IP주소는 컴퓨터가 통신할 때 사용하는 숫자이므로 DNS가 도메인 네임을 기억하면 그에 맞는 IP주소를 찾아 웹서버에 알리고 그럼 웹 서버가 우리에게 해당 페이지를 보여주는 것입니다.

 

2. DNS 해상도(DNS resolution)

도메인 이름을 IP 주소로 변환하는 과정

AWS VPC 내에서도 비슷한 개념이 적용됩니다. VPC 내부의 자원들이 서로 통신할 때, DNS 해상도가 활성화 되어있으면 다른 자원의 도메인 이름을 사용해 통신할 수 있습니다. 예를 들어서 VPC 내의 EC2인스턴스가 특정 도메인 이름으로 설정된 RDS에 접근하려 하면 DNS해상도를 통해 올바른 ip주소로 변환되어 통신이 이루어진다.

 

AWS VPC에서 enable_dns_support와 enable_dns_hostnames 옵션은 다음과 같은 역할을 합니다:

  • enable_dns_support: 이 옵션이 활성화되면, VPC 내의 인스턴스가 도메인 이름을 IP 주소로 변환하는 DNS 해상도를 사용할 수 있게 됩니다.
  • enable_dns_hostnames: 이 옵션이 활성화되면, VPC 내에서 생성된 인스턴스에 DNS 호스트 이름이 할당됩니다. 즉, 인스턴스에 도메인 이름이 부여되어 해당 인스턴스를 도메인 이름으로 참조할 수 있게 됩니다.

VPC에서 DNS 해상도가 비활성화되어 있으면, 도메인 이름을 사용하여 다른 자원에 접근할 수 없으므로, RDS와 같은 서비스가 제대로 동작하지 않을 수 있습니다. 이 때문에 DNS 해상도와 DNS 호스트 이름을 활성화하여 VPC 내부의 자원들이 올바르게 통신할 수 있도록 설정해 주어야 합니다.

 

그래서 수정코드는 다음과 같다.

 

provider "aws" {
  region = "ap-northeast-2"
}

variable "instance_count" {
  default = 1
}

variable "instance_type" {
  default = "t3.micro"
}

resource "aws_vpc" "main" {
  cidr_block = "192.169.0.0/16"
  enable_dns_support   = true
  enable_dns_hostnames = true
  tags = {
    Name = "main-vpc"
  }
}

resource "aws_subnet" "main_2a" {
  vpc_id                  = aws_vpc.main.id
  cidr_block              = "192.169.1.0/24"
  availability_zone       = "ap-northeast-2a"
  map_public_ip_on_launch = true

  tags = {
    Name = "main-subnet-2a"
  }
}

resource "aws_subnet" "main_2c" {
  vpc_id                  = aws_vpc.main.id
  cidr_block              = "192.169.2.0/24"
  availability_zone       = "ap-northeast-2c"
  map_public_ip_on_launch = true

  tags = {
    Name = "main-subnet-2c"
  }
}

resource "aws_subnet" "db_2a" {
  vpc_id                  = aws_vpc.main.id
  cidr_block              = "192.169.3.0/24"
  availability_zone       = "ap-northeast-2a"
  map_public_ip_on_launch = false

  tags = {
    Name = "db-subnet-2a"
  }
}

resource "aws_subnet" "db_2c" {
  vpc_id                  = aws_vpc.main.id
  cidr_block              = "192.169.4.0/24"
  availability_zone       = "ap-northeast-2c"
  map_public_ip_on_launch = false

  tags = {
    Name = "db-subnet-2c"
  }
}

resource "aws_internet_gateway" "main" {
  vpc_id = aws_vpc.main.id

  tags = {
    Name = "main-igw"
  }
}

resource "aws_route_table" "main" {
  vpc_id = aws_vpc.main.id

  route {
    cidr_block = "0.0.0.0/0"
    gateway_id = aws_internet_gateway.main.id
  }

  tags = {
    Name = "main-route-table"
  }
}

resource "aws_route_table_association" "main_2a" {
  subnet_id      = aws_subnet.main_2a.id
  route_table_id = aws_route_table.main.id
}

resource "aws_route_table_association" "main_2c" {
  subnet_id      = aws_subnet.main_2c.id
  route_table_id = aws_route_table.main.id
}

resource "aws_security_group" "main" {
  vpc_id = aws_vpc.main.id

  ingress {
    from_port   = 22
    to_port     = 22
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }

  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }

  tags = {
    Name = "main-sg"
  }
}

resource "aws_instance" "web" {
  count         = var.instance_count
  ami           = "ami-062cf18d655c0b1e8"
  instance_type = var.instance_type
  key_name      = "kakao-tech-bootcamp"
  subnet_id     = element([aws_subnet.main_2a.id, aws_subnet.main_2c.id], count.index % 2)
  vpc_security_group_ids = [aws_security_group.main.id]

  tags = {
    Name = "jenkins-${count.index}"
  }
}

resource "aws_db_instance" "tripdb" {
  allocated_storage    = 20
  engine               = "postgres"
  engine_version       = "16"
  instance_class       = "db.t2.micro"
  username             = "admin"
  password             = "1248"
  parameter_group_name = "default.postgres13"
  skip_final_snapshot  = true
  publicly_accessible  = true

  vpc_security_group_ids = [aws_security_group.main.id]
  db_subnet_group_name   = aws_db_subnet_group.main.name
}

resource "aws_db_subnet_group" "main" {
  name       = "main-db-subnet-group"
  subnet_ids = [aws_subnet.db_2a.id, aws_subnet.db_2c.id]

  tags = {
    Name = "main-db-subnet-group"
  }
}

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

요금이 걱정돼서.. t3.medium -> t3.micro

enable_dns_support   = true
  enable_dns_hostnames = true

이거 두 개를 추가해줬다.

 

근데 또 오류가........?

Error: creating RDS DB Instance (terraform-20240807064140341500000001): InvalidParameterCombination: RDS does not support creating a DB instance with the following combination: DBInstanceClass=db.t2.micro, Engine=postgres, EngineVersion=16.3, LicenseModel=postgresql-license. For supported combinations of instance class and database engine version, see the documentation.
│ 	status code: 400, request id: 4ae350ef-d6c9-488f-b0b6-9c3294350124
│
│   with aws_db_instance.tripdb,
│   on main.tf line 132, in resource "aws_db_instance" "tripdb":
│  132: resource "aws_db_instance" "tripdb" {
│

이건그냥 버전 문제였습니다. db를 db.t3.micro로 해서 해결! ㅎㅎ이아니라 ㄷ또남

╷
│ Error: creating RDS DB Instance (terraform-20240807065142683500000001): InvalidParameterValue: MasterUsername admin cannot be used as it is a reserved word used by the engine
│ 	status code: 400, request id: e1aa5e13-cea9-4227-9919-eea352a0b4b4
│
│   with aws_db_instance.tripdb,
│   on main.tf line 132, in resource "aws_db_instance" "tripdb":
│  132: resource "aws_db_instance" "tripdb" {
│

db 유저 이름을 변경하라는......알아서할게!! 그래도 변경해줘야죠.. 

패스워드도 8자리이상으로 변경하고 parameter_group_name제거해주고... (이건 지피티가 써준건데 역시 너무 의존하면 안되는!!)

 

완료!

이제 Ansible파일에 들어갈 ip주소를 hosts.ini에 추가해주기 위해 

 terraform output -json instance_ips | jq -r '.[]' >> hosts.ini

명령어를 입력해준다

그럼 자동으로 hosts.ini파일에 퍼블리 도메인이 작성되고 앤서블 코드에서 웹 서버 도메인을 말할 때 hosts에 써있는 것을 보고 관련 프로비저닝을 실행할 때 해줍니다.

 

또 지피티에게 부탁한 Ansibl코드는 다음과 같습니다.

 

- playbook.yml

- hosts: webservers
  become: yes

  tasks:
    - name: Install Java
      apt:
        name: default-jdk
        state: present

- name: Install Node.js and npm
      apt:
        name: nodejs
        state: present

- name: Update db servers
  hosts: databases
  become: true

  tasks:
    - name: Ensure postgresql is at the latest version
      ansible.builtin.yum:
        name: postgresql
        state: latest
    - name: Ensure that postgresql is started
      ansible.builtin.service:
        name: postgresql
        state: started

 

ansible-playbook -i hosts.ini playbook.yml

이 명령어를 실행시키면 playbook에 있는 내용이 Host.in에있는 ip주소에 설치해주는데 또 오류가 발생

 

들여쓰기는 잘 못 한 것 같다. 그리고 hosts.ini에 [webservers] / [databases]를 명시해주지 않아서 인 것 같아 수정

근데도 또 오류...

fatal: [3.34.252.48]: UNREACHABLE! => {"changed": false, "msg": "Failed to connect to the host via ssh: no such identity: /Users/goorm/Downloads/kakaotechbootcamp.pem: No such file or directory\r\nec2-user@3.34.252.48: Permission denied (publickey).", "unreachable": true}

 

아 이건 제가 잘못했네요..

 

- ansibel.cfg

[defaults]
inventory = hosts.ini
private_key_file = ~/Downloads/kakao-tech-bootcamp.pem
host_key_checking = False
remote_user = ubuntu

키이름에 -가 빠져있엇고 remote_user가 ec2로 되어있어ㅅ서 그랬던 거였습니다.

근데 또 오류가 났어요.. node를 설치하는 부분에서

 

지긋지긋하네요

fatal: [3.34.252.48]: FAILED! => {"changed": false, "msg": "No package matching 'nodejs' is available"}

들여쓰기 수정해서 해결.

 

fatal: [terraform-20240807065657717900000001.czu4uiymcah9.ap-northeast-2.rds.amazonaws.com]: UNREACHABLE! => {"changed": false, "msg": "Failed to connect to the host via ssh: ssh: connect to host terraform-20240807065657717900000001.czu4uiymcah9.ap-northeast-2.rds.amazonaws.com port 22: Operation timed out", "unreachable": true} PLAY RECAP ********************************************************************************** 3.34.252.48 : ok=4 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 terraform-20240807065657717900000001.czu4uiymcah9.ap-northeast-2.rds.amazonaws.com : ok=0 changed=0 unreachable=1 failed=0 skipped=0 rescued=0 ignored=0

 

이런 오류가 나기 시작.........

나는 ec2인스턴스 그러니까 원격 서버안에 RDS가 있다고 이해를 했었는데 그게 아니었습니다. 그래서 RDS는 Ansible작업이 필요하지 않다는 점!! 왜냐면 Ansible은 원격 서버 안에 설치해줄 것들을 yaml파일로 정리해주는 것이기 때문이에요. 그래서 과감하게 그 코드를 없애줍니다.

 

- playbook.yml

- hosts: webservers
  become: yes

  tasks:
    - name: Update apt cache
      apt:
        update_cache: yes

    - name: Install Java
      apt:
        name: default-jdk
        state: present

    - name: Install Node.js and npm
      apt:
        name: nodejs

 

 

이제완료~!~!

 

ㅎㅎㅎ이제 원격 서버에 자바랑 node.js가 설치됐는지 확인해볼게요

ssh -i ~/Downloads/kakao-tech-bootcamp.pem ubuntu@3.34.252.48

원격 서버에 접속한 다음

 

ㅎㅎㅎ 자바도 잘 설치가 되었네요.. 근데 npm은 설치안돼서 걍 수기로 설치했어요..

sudo apt install npm

 

그리고 RDS로 한 번 접근해볼게요

 

sudo apt install postgresql postgresql-contrib

이걸로 postgresql을 설치해줍니다

psql -h <RDS 엔드포인트> -U <사용자 이름> -d <데이터베이스 이름>

근데 계속 연결이 안되는 이슈...... 바로 인바운드 규칙을 고치지 않아서였다!!! ㅎㅎㅎ

보안그룹에 8080, 5432(postgresql)을 추가해준다.

이름을 알 수 없다고 연결이 안돼서 걍 지워버렸습니다........... 오늘은 여기까지 하고 다음 게시물에서 계속...