성능 분석이란?

성능 테스트

  • 실제 트래픽 상황에서의 정상 동작 하는지를 파악.

부하 테스트

  • 리소스 병목 탐색, 어플리케이션 버그 탐색
  • 이벤트 상황과 같은 순간 트래픽 최대치, 한계치를 탐색
  • 신규 스펙 장비에서 MYSQL 설정 최적화 탐색

스트레스 테스트

  • 장기간 부하 발생에 대한 한계치를 탐색, 예외 동작 상황 확인
  • Graceful Shutdown 정상 동작 확인
  • 데이터베이스 failover 상황, 자동 복구, 예외 동작 상황 확인
  • 외부 요인(PG사)의 밀릴, 예외 상황 동작 확인

성능 분석을 하는 이유

  • 서비스 및 서비스 시스템의 성능을 확인하기 위해 실사용 환경과 비슷한 환경에서 테스트를 진행하는 것
    • 어느 정도의 부하를 견딜 수 있는지
    • 한계치에서 병목이 생기는 지점 찾기
    • 자원을 효율적으로 사용할 수 있다.
    • 메모리 누수, 오류, 오버플로우 발생하는지 확인 가능
    • 최악의 상황에서 어떤 동작을 하는지 확인 가능
    • 장애 조치와 복구의 동작을 확인 가능
  • 구글 리서치 자료에 따르면 3초 이상 걸리면 53%의 사용자가 떠나며, 표시 속도가 1초에서 7초로 늘어난다면 이탈률은 113%가 된다고 함
 

Mobile conversion rate statistics - Think with Google

Google’s Official Mobile Marketing Publication. Mobile conversion rates are lower than on desktop. Get more mobile data on Think with Google.

www.thinkwithgoogle.com

→ 웹 성능테스트를 통해 사용자에게 빠른 서비스를 제공할 수 있어야 한다.

성능 테스트의 기대 효과

  1. 시스템 측면 : 주요 성능 결함 및 구조적 위험 우선 식별, 운영시스템 성능 최적화, 유지보수 비용 및 시
    스템 비용 최소화 등
  2. 사용자 측면 : 서비스의 품질 개선, 응답시간 향상을 통한 업무 효율 증대, 사용자 만족도 향상 등

nGrinder

 

nGrinder

Please post questions in Discussions not Issues. nGrinder 3.5.5-p1 version is now available. Check the changes at here. nGrinder is a platform for stress tests that enables you to execute script creation, test execution, monitoring, and result report gener

naver.github.io

  • nGrinder는 네이버에서 성능 측정 목적으로 jython(JVM위에서 파이썬이 동작)으로 개발 된 오픈소
    스 프로젝트이며,2011년에 공개 됨.
  • 실제 서비스에 투입 되기 전, 실제와 같은 환경을 만들어 놓고 서버가 사용자를
    얼마 만큼 수용할 수 있는지를 실험 할 때 사용
  • 언어로는 jython (java + python)과 Groovy(자바 호환)를 지원.

Controller

  • 퍼포먼스 테스팅(부하테스트)를 위해 웹 인터페이스를 제공
  • 테스트 프로세스를 체계화
  • 테스트 결과를 수집해 통계로 보여줌

Agent

  • Controller의 명령을 받아 실행.
  • target이 된 머신에 프로세스와 스레드를 실행시켜 부하를 발생.
  • 대상 시스템의 CPU와 Memory를 모니터링.

Target

  • 부하 테스트를 받는 머신

설치 방법

  • controller와 agent를 둘다 설치해야 한다.

docker를 사용하는 방법

  • 운영 체제별 도커 데스크탑 설치
 

Docker Desktop: The #1 Containerization Tool for Developers | Docker

Docker Desktop is collaborative containerization software for developers. Get started and download Docker Desktop today on Mac, Windows, or Linux.

www.docker.com

  • 우분투 도커 설치
 

Ubuntu 에 Docker 설치

실행환경 Ubuntu 22.04 Docker 설치방법 1. 우분투 시스템 패키지 업데이트 sudo apt-get update 2. 필요한 패키지 설치 sudo apt-get install apt-transport-https ca-certificates curl gnupg-agent software-properties-common 3. Docker의

haengsin.tistory.com

  • controller 설치 및 실행
# docker hub에서 이미지 가져오기
docker pull ngrinder/controller

# 가져온 이미지로 실행하기
docker run -d -v ~/ngrinder-controller:/opt/ngrinder-controller --name controller -p 80:80 -p 16001:16001 -p 12000-12009:12000-12009 ngrinder/controller
  • agent 설치 및 실행
# docker hub에서 이미지 가져오기
docker pull ngrinder/agent

# 가져온 이미지로 실행하기
# 위에서 생성했던 controller와 연결
docker run -d --name agent1 --link controller:controller ngrinder/agent

직접 파일을 다운로드해서 실행시키는 방법

이 경우에 자바를 사용해야한다.

  • 자바 다운로드
 

Java를 설치하는 방법은 무엇입니까?

 

www.java.com

  • nGrinder 다운로드
    • 받고싶은 버전(보통은 최신버전)을 선택하여 .war 파일을 다운 받는다.
 

Releases · naver/ngrinder

enterprise level performance testing solution. Contribute to naver/ngrinder development by creating an account on GitHub.

github.com

  • 다음 받은 후 해당 명령어를 실행한다.
# java -Djava.io.tmpdir=[임시파일을 저장할 경로] -jar [다운받은 war 파일 이름] --port=[열고싶은 포트번호]
# 예시
java -Djava.io.tmpdir=/Users/choi/Desktop/test/ -jar ngrinder-controller-3.5.8.war --port=8301
  • 정상적으로 실행 되었다면 localhost:[지정한 포트]로 접속할 시 nGrinder 메인 화면이 뜬다

  • 초기 관리자 ID와 password는 둘다 admin이다.
  • 접속 후 오른쪽 상단의 admin 클릭 후 에이전트 다운로드 클릭 해서 압축 파일 다운로드

  • 다운받은 압축 파일을 압축해제 해제
# tar -xvf [다운받은 압축파일]
tar -xvf ngrinder-agent-3.5.8-localhost.tar
  • 압축 해제 후 run_agent.sh 실행 (윈도우는 .bat)

./run_agent.sh
  • 이렇게 실행할 경우 agent도 틀어짐

사용 방법

 

에이전트 테스트에 사용할 에이전트의 개수
스크립트 사용자가 해당 주소로 부하를 걸 스크립트를 보여줌
RHEAD 선택된 스크립트의 내용을 보여주는 페이지로 이동
테스트 대상 서버 테스트를 진행할 서버의 리스트를 보여주며, 추가버튼 클릭 시 테스트 받을 서버를 늘릴 수 있다.
테스트 기간 해당 테스트를 얼마만큼 실행할 지 설정하는 항목
실행 횟수 사용자가 설정한 쓰레드당 몇 번의 테스트를 실행할 것인지를 지정
Ramp-Up 점차 부하를 가할 수 있는 기능. 부하를 가할 때, 가상 사용자 수를 늘리는 것 이 아닌 process나 thread를 늘린다
초기 개수 처음 시작 시 가상사용자의 수를 설정
초기 대기 시간 테스트를 언제부터 실행시킬 지 설정
증가 단위 해당 process/thread를 몇 개씩 증가시킬지를 설정
Ramp_Up 주기 상승 시간을 설정

  • 여러가지 스크립트를 작성해서 각 기능에 대해 얼마나 부하가 걸리는지 확인할 수 있다.
// 로그인 페이지 예시 스크립트
@Test
public void testLogin() {
    // 메인 페이지 요청
    HTTPResponse mainPageResponse = request.GET("http://example.com/main")

    // 로그인 요청
    Map<String, Object> loginParams = [username: "user", password: "pass"]
    HTTPResponse loginResponse = request.POST("http://example.com/login", loginParams)

    // 로그인 응답 확인
    assertThat(loginResponse.statusCode, is(200))

}

 

EC2 구성하기

key pair 생성

ec2를 접속할 때 사용할 key pair를 생성한다.

ssh-keygen -t rsa -b 4069 -C {자신의 메일} -f "./my_key" -N ""
# rsa 방식으로 4096비트를 사용해서 생성한다.

생성 완료되면

Generating public/private rsa key pair.
Your identification has been saved in ./my_key
Your public key has been saved in ./my_key.pub
The key fingerprint is:
SHA256:[여러 문자] [입력한 메일 주소]
The key's randomart image is:
+---[RSA 4069]----+
|+O               |
|++@+.   . .      |
|oo.+   + o +     |
|=.  o o +        |
|E..  . .So       |
|o* .   ..        |
|..* ..+.         |
|.. o.  o         |
|   .o .          |
+----[SHA256]-----+

이런 메세지가 뜨면서 파일 두개가 생성된다.
my_key와 my_key.pub이 생성되는데 .pub가 공개키, my_key 암호키 즉 쌍(pair)으로 생성된다.

생성한 키페어를 테라폼 파일에 등록한다.

# ec2.tf
resource "aws_key_pair" "ec2_key" {
    key_name = "my_key"
  public_key = "my_key.pub 안에 있는 내용 전부 붙여넣기" or file("./my_key.pub")
}

ec2.tf를 만들어 주고 위의 코드를 작성해서 생성한 키페어를 테라폼에서 사용할 수 있게 만들어준다.

ec2 생성

aws_instance를 사용해서 쉽게 생성할 수 있다.

#ec2.tf

resource "aws_instance" "real_ec2" {
    ami = "ami-0ba5cd124d7a79612"
    #ubuntu 20.04의 ami
    instance_type = "t3.small"
    security_groups = [aws_security_group.realPublicSG.id]
    subnet_id = aws_subnet.realPublicSubnet.id
    key_name = aws_key_pair.ec2_key.key_name
    root_block_device { #볼륨 설정 가능
            volume_size = 200
            volume_type = "gp3"
    }
    tags ={ #이름 설정 가능
            Name = "real_ec2"
    }
}
  • 인스턴스 타입을 설정할 수 있다.
  • ami의 경우 콘솔에서 ami검색을 통해 id를 확인할 수 있다.

작성완료 했으면 terraform plan 후 apply
(apply는 실제 환경에 적용이기 때문에 자주하지 않더라고 plan은 자주해서 오류를 바로바로 잡아내자!)

RDS 생성

rds.tf 파일을 생성한 후 아래와 같이 작성한다.

#rds.tf

#rds의 서브넷 그룹으로 사용할 subnet들 미리 지정
resource "aws_db_subnet_group" "realDBSubnetGroup" {
  name = "test"
  subnet_ids = [
    aws_subnet.realPrivateSubnet1.id,
    aws_subnet.realPrivateSubnet2.id,
    aws_subnet.realPublicSubnet.id,
  ]
  tags = {
    "Name" = "real-db-subnet-group"
  }
}

resource "aws_db_instance" "real_rds" { 
    allocated_storage = 50
    max_allocated_storage = 80
    skip_final_snapshot = true
    vpc_security_group_ids = [aws_security_group.realPrivateSG.id ]
        #보안그룹 지정.
    db_subnet_group_name = aws_db_subnet_group.realDBSubnetGroup.name
        #서브넷 그룹 지정.
    publicly_accessible = true
    engine = "mariadb"
    engine_version = "10.6.8"
    instance_class = "db.t3.small"
    db_name = "testDB"
    username = "admin"
    password = "testtest"
    tags = {
        "Name" = "realDB"
    }
}

생성완료 후 terraform plan 후 apply로 적용을 한다.

구성도

이런 형태로 구축하려고 한다.

서브넷 추가하기

#vpc.tf
#퍼블릭 서브넷
resource "aws_subnet" "realPublicSubnet" {
    vpc_id = aws_vpc.realVPC.id
    cidr_block = "10.0.0.0/24"
    availability_zone = "ap-northeast-2c"
    tags = {
        Name = "real-public-subnet"
    }
}
#프라이빗 서브넷
resource "aws_subnet" "realPrivateSubnet1" {
    vpc_id = aws_vpc.realVPC.id
    cidr_block = "10.0.1.0/24"
    availability_zone = "ap-northeast-2c"
    tags = {
        Name ="real-private-subnet1"
    }
}
resource "aws_subnet" "realPrivateSubnet2" {
    vpc_id = aws_vpc.realVPC.id
    cidr_block = "10.0.2.0/24"
    availability_zone = "ap-northeast-2a"
    tags = {
        Name = "real-private-subnet2"
    }
}
  • vpc_id 부분에 바로전에 만들었던 vpc의 id를 추가하면 쉽게 서브넷을 추가할 수 있다.
  • 아까 만들었던 이름을 사용해서 id를 쉽게 얻을 수 있다. (aws_vpc.testVPC.id)

IGW (인터넷 게이트웨이 추가)

# vpc.tf
resource "aws_internet_gateway" "realIGW" {
    vpc_id = aws_vpc.realVPC.id
    tags = {
        Name="real-IGW"
    }
}
  • vpc에서 외부와 통신할 수 있게 public subnet과 통신한 인터넷 게이트웨이를 추가한다.

라우팅 테이블 작성

#vpc.tf
##라우팅 테이블 만들기
#퍼블릭 라우팅 테이블
resource "aws_route_table" "realPublicRoute" {
    vpc_id = aws_vpc.realVPC.id
    route {
        cidr_block = "0.0.0.0/0"
        gateway_id = aws_internet_gateway.realIGW.id 
            #퍼블릭 서브넷에 인터넷 게이트웨이 연결.
    }
    tags = {
    Name = "real-public-route"
    }
}
#프라이빗 라우팅 테이블
resource "aws_route_table" "realPrivateRoute" {
    vpc_id = aws_vpc.realVPC.id
    tags = {
        Name = "real-private-route"
    }
}

#퍼블릭 라우팅 테이블 연결
resource "aws_route_table_association" "realPublicRTAssociation"{
    subnet_id = aws_subnet.realPublicSubnet.id
    route_table_id = aws_route_table.realPublicRoute.id
}

#프라이빗 라우팅 테이블 연결
resource "aws_route_table_association" "realPrivateRTAssociation1"{
    subnet_id = aws_subnet.realPrivateSubnet1.id
    route_table_id = aws_route_table.realPrivateRoute.id
}
resource "aws_route_table_association" "realPrivateRTAssociation2"{
    subnet_id = aws_subnet.realPrivateSubnet2.id
    route_table_id = aws_route_table.realPrivateRoute.id
}
  • 다음으로는 라우팅 테이블을 만들어 서브넷과 연결시켜준다.

보안그룹 생성

#vpc.tf
#퍼블릭 보안 그룹
resource "aws_security_group" "realPublicSG" {
    vpc_id = aws_vpc.realVPC.id
    name = "real public SG"
    description = "real public SG"
    tags = {
        Name = "real pulbic SG"
    }

}
#퍼블릭 보안 그룹 규칙
resource "aws_security_group_rule" "realPublicSGRulesHTTPingress" {
    type = "ingress"
    from_port = 80
    to_port=80
    protocol = "TCP"
    cidr_blocks = ["0.0.0.0/0"]
    security_group_id = aws_security_group.realPublicSG.id
    lifecycle{
        create_before_destroy = true
    }
}
resource "aws_security_group_rule" "realPublicSGRulesSSHingress" {
    type = "ingress"
    from_port = 22
    to_port= 22
    protocol = "TCP"
    cidr_blocks=["0.0.0.0/0"]
    security_group_id = aws_security_group.realPublicSG.id
    lifecycle{
        create_before_destroy = true
    }
}
resource "aws_security_group_rule" "realPublicSGRulesALLegress" {
    type = "egress"
    from_port = 0
    to_port= 0
    protocol = "ALL"
    cidr_blocks=["0.0.0.0/0"]
    security_group_id = aws_security_group.realPublicSG.id
    lifecycle{
        create_before_destroy = true
    }
}
#프라이빗 보안 그룹
resource "aws_security_group" "realPrivateSG" {
    vpc_id = aws_vpc.realVPC.id
    name = "real private SG"
    description = "real private SG"
    tags = {
        Name = "real private SG"
    }
}
#프라이빗 보안 그룹 규칙
resource "aws_security_group_rule" "realPrivateSGRulesRDSingress" {
    type = "ingress"
    from_port = 3306
    to_port=3306
    protocol = "TCP"
    security_group_id = aws_security_group.realPrivateSG.id
    source_security_group_id = aws_security_group.realPublicSG.id
    lifecycle{
        create_before_destroy = true
    }
}
resource "aws_security_group_rule" "realPrivateSGRulesRDSegress" {
    type = "egress"
    from_port = 3306
    to_port= 3306
    protocol = "TCP"
    security_group_id = aws_security_group.realPrivateSG.id
    source_security_group_id = aws_security_group.realPublicSG.id
    lifecycle{
        create_before_destroy = true
    }
}
  • public, private subnet의 보안그룹을 생성해준다.

    • public의 경우 ssh접속을 위한 22번 포트와, http 통신을 위한 80를 ingress로, 열어주고 외부로 나가는 egress는 모든 트래픽을 열어준다.
    • private의 경우 rds를 연결하기 위해 3306포트를 열어준다.
  • 테라폼에서는 보안 그룹을 설정할 때 인라인으로 규칙을 추가해주거나 aws_security_group_rule
    으로 추가할 수 있음.

  • 만약 인라인으로 지정하게 된다면 한 보안 그룹에서 인바운드로 허용하는 액세스원이 다른 보안 그룹인 경우 이를 지정 할 수 없다.
    => aws_security_group_rule를 사용해서 추가해주자.

  • ingress는 외부에서 내부로 들어오는 트래픽, egress는 내부에서 외부로 나가는 트래픽을 의미.

계획 및 적용

$ terraform plan
.
.
.
Plan: 16 to add, 0 to change, 0 to destroy.
$ terraform apply

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

aws_security_group.testPrivateSG: Creating...
aws_route_table.testPrivateRoute2: Creating...
aws_route_table.testPrivateRoute1: Creating...
aws_route_table.testPublicRoute: Creating...
aws_security_group.testPublicSG: Creating...
aws_route_table.testPrivateRoute2: Creation complete after 1s [id=rtb-01a08a475c7aa3b6e]
aws_route_table_association.testPrivateRTAssociation2: Creating...
aws_route_table.testPrivateRoute1: Creation complete after 1s [id=rtb-0de978f9daf7138da]
aws_route_table_association.testPrivateRTAssociation1: Creating...
aws_route_table_association.testPrivateRTAssociation2: Creation complete after 0s [id=rtbassoc-04c687539b690b97e]
aws_route_table_association.testPrivateRTAssociation1: Creation complete after 0s [id=rtbassoc-00c81afb4d66a4afb]
aws_route_table.testPublicRoute: Creation complete after 1s [id=rtb-0b21780b54c48f288]
aws_route_table_association.testPublicRTAssociation: Creating...
aws_route_table_association.testPublicRTAssociation: Creation complete after 1s [id=rtbassoc-015a0b583a70d3d07]
aws_security_group.testPrivateSG: Creation complete after 2s [id=sg-0564ca6903f5d71ae]
aws_security_group.testPublicSG: Creation complete after 2s [id=sg-0d17e51c8f54212ee]
aws_security_group_rule.testPublicSGRulesHTTPegress: Creating...
aws_security_group_rule.testPrivateSGRulesRDSegress: Creating...
aws_security_group_rule.testPublicSGRulesHTTPSingress: Creating...
aws_security_group_rule.testPublicSGRulesHTTPSegress: Creating...
aws_security_group_rule.testPublicSGRulesHTTPingress: Creating...
aws_security_group_rule.testPublicSGRulesSSHegress: Creating...
aws_security_group_rule.testPrivateSGRulesRDSingress: Creating...
aws_security_group_rule.testPublicSGRulesSSHingress: Creating...
aws_security_group_rule.testPublicSGRulesSSHegress: Creation complete after 1s [id=sgrule-1099277773]
aws_security_group_rule.testPrivateSGRulesRDSegress: Creation complete after 1s [id=sgrule-640893485]
aws_security_group_rule.testPublicSGRulesHTTPSingress: Creation complete after 1s [id=sgrule-1338266698]
aws_security_group_rule.testPrivateSGRulesRDSingress: Creation complete after 1s [id=sgrule-3779608258]
aws_security_group_rule.testPublicSGRulesHTTPingress: Creation complete after 2s [id=sgrule-768399379]
aws_security_group_rule.testPublicSGRulesHTTPSegress: Creation complete after 2s [id=sgrule-292654581]
aws_security_group_rule.testPublicSGRulesSSHingress: Creation complete after 3s [id=sgrule-1743733607]
aws_security_group_rule.testPublicSGRulesHTTPegress: Creation complete after 3s [id=sgrule-3303017385]

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

이런식으로 적용이 완료 되면 성공이다.

테라폼 사용하기

테라폼 공식 레퍼런스
-> 필요한 프로바이더, 리소스등을 어떻게 사용하는 상세히 나와 있다. 모르는거 있을때 참고하면 좋다.

테라폼 작동방식

  • 프로바이더 작성
    무엇을 사용해서 인프라를 구성할 지 결정 ex)aws, 구글 클라우드 서비스 등

  • 초기화 (init)
    terraform init 명령어를 통해 테라폼을 사용할 수 있게 기본적인 설정 진행.

  • 리소스 작성
    사용하고 싶은 리소스를 .tf 파일로 작성

  • 계획 / 적용 (plan/apply)
    terraform plan : 작성한 리소스들이 실제로 어떻게 만들어질지 예측 및 결과를 보여줌 (실제로 만드는 건 아님)
    terraform apply : 작성한 리소스들을 바탕으로 실제 인프라를 생성함.

프로바이더 작성

사용하게 될 서비스(aws, gcp, docker 등)를 작성

  • 사용할 디렉토리 생성.
    mkdir [디렉토리 이름]
$ mkdir test
  • 그 후 .tf파일 작성
#main.tf (파일명은 상관 없음. 본인이 알아볼 수 있는 이름으로 설정.)
terraform {
  required_version = "1.2.9" #자신의 terraform 버전으로 설정.

  required_providers {
      aws = { #사용할 프로바이더 aws 설정.
          source = "hashicorp/aws" #저장 경로.
          version = "~> 3.0" #버전 설정
      }
  }
}

provider "aws" {
  region     = "ap-northeast-2" #aws의 리전 설정. ap-northeast-2는 서울리전.
  # access_key 와 secert_key 를 파일에 적어서 구현할 수는 있지만 권장안함.
  # 보안이슈로 파일에 직접적는것보단 export나 위의 configure로 설정하는 것이 좋음.
  # access_key = "my-access-key"
  # secret_key = "my-secret-key"
}

key 부분은 전 게시물에 aws CLI를 설정했으면 넘어가도 된다.
(그리고 파일에 key값을 명시하는 것은 보안상의 이유로 추천하지 않는다.)

초기화

  • 파일 작성 후 terraform init 명령어를 통해 초기화
    $ terraform init
    Terraform has been successfully initialized!
    ##블라블라 하면서 초록색으로 성공적으로 초기화되었습니다! 뜸

    만약 프로바이더의 tf파일의 변경사항이 있을 경우
    terraform init -upgrade 해주면 마지막 변경사항으로 업데이트 된다.

리소스 작성

EC2, RDS 등 실제로 구축하게 될 자원들을 작성해야한다.

  • vpc 생성으로 테스트 해보기
#vpc.tf
resource "aws_vpc" "testVPC"{
    cidr_block = "10.0.0.0/16"
    enable_dns_hostnames = true
}

리소스의 경우 .tf 형식을 가지며 한파일에 전부 있어도 되고, 여러개로 나눠져있어도 된다. (위의 vpc.tf파일의 내용이 제일 위에 만들었던 provider.tf에 있어도 상관없음.
보통은 사용하는 aws 리소스별로 나눈다.

계획/적용

계획 (plan) : 실제 적용하기 전 코드를 테스트 해보는 것.
적용 (apply) : 실제 환경에 적용한다.

  • 위의 vpc.tf 파일을 생성했으면 plan 명령어를 사용한다.
$ terraform plan

Terraform used the selected providers to generate the following execution plan.
Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # aws_vpc.testVPC will be created
  + resource "aws_vpc" "testVPC" {
      + arn                                  = (known after apply)
      + cidr_block                           = "10.0.0.0/16"
      + default_network_acl_id               = (known after apply)
      + default_route_table_id               = (known after apply)
      + default_security_group_id            = (known after apply)
      + dhcp_options_id                      = (known after apply)
      + enable_classiclink                   = (known after apply)
      + enable_classiclink_dns_support       = (known after apply)
      + enable_dns_hostnames                 = (known after apply)
      + enable_dns_support                   = true
      + id                                   = (known after apply)
      + instance_tenancy                     = "default"
      + ipv6_association_id                  = (known after apply)
      + ipv6_cidr_block                      = (known after apply)
      + ipv6_cidr_block_network_border_group = (known after apply)
      + main_route_table_id                  = (known after apply)
      + owner_id                             = (known after apply)
      + tags_all                             = (known after apply)
    }

Plan: 1 to add, 0 to change, 0 to destroy.

이런식으로 출력되면 plan이 성공한 것이다.

👉 이런 에러가 출력될때가 있다.
An argument or block definition is required here. To set an argument, use the │ equals sign "=" to introduce the argument value.
이건 복사/붙여넣기로 파일 만들었을때 문자가 깨져서 나오는 것이므로 .tf파일을 직접 손으로 작성한 후 다시 plan할 것.

  • plan에 성공했을 경우 apply 명령어를 사용한다.
$ terraform apply

Terraform used the selected providers to generate the following execution plan.
Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # aws_vpc.testVPC will be created
  + resource "aws_vpc" "testVPC" {
      + arn                                  = (known after apply)
      + cidr_block                           = "10.0.0.0/16"
      + default_network_acl_id               = (known after apply)
      + default_route_table_id               = (known after apply)
      + default_security_group_id            = (known after apply)
      + dhcp_options_id                      = (known after apply)
      + enable_classiclink                   = (known after apply)
      + enable_classiclink_dns_support       = (known after apply)
      + enable_dns_hostnames                 = (known after apply)
      + enable_dns_support                   = true
      + id                                   = (known after apply)
      + instance_tenancy                     = "default"
      + ipv6_association_id                  = (known after apply)
      + ipv6_cidr_block                      = (known after apply)
      + ipv6_cidr_block_network_border_group = (known after apply)
      + main_route_table_id                  = (known after apply)
      + owner_id                             = (known after apply)
      + tags_all                             = (known after apply)
    }

Plan: 1 to add, 0 to change, 0 to destroy.

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes #yes를 입력해줘야 한다.

aws_vpc.testVPC: Creating...
aws_vpc.testVPC: Creation complete after 2s [id=vpc-0d827147de6c34491]

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

apply의 경우 실제로 환경에 적용하는 것이므로 진짜 적용할건지 한번 더 물어본다. yes를 입력해주면 적용한다.

여기까지 완료했으면 aws console을 가서 확인해보자. 새로운 vpc가 생긴것을 확인할 수 있다.


내용을 변경하고 싶으면 내용 변경 후 plan->apply를 반복하면 된다.

#vpc.tf
resource "aws_vpc" "testVPC"{
    cidr_block = "10.0.0.0/16"
    enable_dns_hostnames = true
    tags = {
        Name="testVPC" # 콘솔에서 표시될 이름 지정
    }
}

이렇게 이름을 추가 후 plan/apply를 한 후 콘솔을 확인해보면 이름이 지정된 것을 확인할 수 있다.

AWS CLI 설치

AWS CLI 설치 페이지

  • 위 페이지에서 나에게 맞는 운영체제의 AWS CLI 설치 가능.
    (mac의 경우 GUI installer의 pkg를 사용하여 쉽게 설치 가능)
  • 설치 후 경로와 버전 확인
$ which aws
/usr/local/bin/aws

$ aws --version
aws-cli/2.7.31 Python/3.9.11 Darwin/21.5.0 exe/x86_64 prompt/off

제대로 경로와 버전이 뜬다면 설치 성공.

AWS CLI 사용을 위한 설정

  • key pair 생성 및 .csv 파일을 이용한 키 페어 가져오기
  1. AWS 콘솔에 로그인 후 IAM 서비스로 이동.
  2. 서비스 이동 후 왼쪽의 users 탭 클릭.
  3. 키페어 생성할 user name 클릭.
  4. Security credentials(보안 자격) 탭 클릭.
  5. create access key 버튼 클릭.

중요! 단 한번만 생성할때 secret access key와 .csv파일을 다운받을 수 있음. 꼭 복사해두거나 저장해둘것.

$ aws configure
AWS Access Key ID [None]: 
AWS Secret Access Key [None]: 
Default region name [None]: ap-northeast-2
Default output format [None]: json
  • 총 4개의 설정값이 나옴.
  • 위에서 얻은 access key id와 key를 입력.
  • Default region name은 기본으로 사용할 리전을 입력 (서울의 경우 ap-northeast-2)
  • Default output format은 기본 출력 형식 (json, yaml, yaml-stream, text, table 중 하나나 선택, 지정안하면 json이 기본값)
  • aws configure list 명령어 사용하면 현재 구성정보 한눈에 확인 가능.

여기까지 하면 테라폼 설치 전 AWS CLI 환경 설정 완료!

테라폼이란?

모든 클라우드에서 인프라 자동화
모든 환경에서 리소스를 프로비저닝, 변경 및 버전 관리합니다.
출처 : https://www.terraform.io (공식홈페이지 소개글)

IaC

Infrastructure as Code, 코드로써의 인프라

즉 코드로써의 인프라는 인프라를 이루는 서버, 미들웨어 그리고 서비스 등 인프라 구성요소들을 코드를 통해 구축하는 것이다.
terraform은 이 IaC의 하나로 인프라를 코드로 구축하기 때문에 작성하기 편리하고, 재사용과 유지보수가 쉽다는 장점을 가진다.

구성요소

테라폼은 다음 5가지의

  • provider
    테라폼으로 생성할 인프라의 종류(aws, 구글 클라우드 서비스 등)

      required_providers {
          aws = { #사용할 프로바이더 aws 설정.
              source = "hashicorp/aws" #저장 경로.
              version = "~> 3.0" #버전 설정
          }
    
  • resource
    테라폼으로 생성할 인프라의 자원(aws라고 하면 ec2, vpc 등)
    .tf의 확장자로 생성한다.

    #create a vpc
    resource "aws_vpc" "example" { 
    #"aws_vpc"는 사용하는 자원, "example"은 내가 사용할 이름을 뜻한다.
    #여기서 "example"이란 이름은 내가 테라폼에서 사용하는 이름으로, aws 콘솔에서 표시되는 이름이 아니다.
        tags = {
            Name = "example" #aws 콘솔에 표시되는 이름은 이렇게 tags안의 Name으로 설정한다.    
        }
        cidr_block = "10.0.0.0/16"
        #이외에도 수많은 인자 존재.
    }
  • state
    테라폼을 통해 생성한 자원의 상태 (file로 저장됨)

    {
      "version": 4,
      "terraform_version": "1.2.9",
      "serial": 222,
      "lineage": "9xxxxxxx-fxxx-e80b-2c13-a1bf59bd6bb2",
      "outputs": {},
      "resources": [
            {...},
            {...},
        ]
    }

    단, state는 현재 인프라의 상태가 아닌 테라폼으로 생성한 결과다. (만약 콘솔이나 다른작업으로 상태가 바뀌어도 state는 업데이트 되지 않는다.)

  • output
    테라폼으로 만든 자원을 변수 형태로 state에 저장하는 것을 의미

    resource "aws_vpc" "default" {
        cidr_block = "10.0.0.0/16"
        # cidr_block 이외에도 수많은 인자가 존재합니다.
    }
    
    output "vpc_id"{
        value = aws_vpc.default.id
    }
    
    output "cidr_block"{
        value = aws_vpc.default.cidr_block
    }
  • module
    고통적으로 활용할 수 있는 코드를 문자 그대로 모듈 형태로 정의하는 것을 의미
    (재사용성이 높음)

    module "vpc" {
        source = "../_modules/vpc" ##리소스 코드 경로
    
        cidr_block = "10.0.0.0/16"
    } 
  • remote
    다른 경로의 state를 참조함.

    data "terraform_remote_state" "vpc" {
        backend = "remote"
    
        config = {
            bucket = "terraform-s3-bucket"
            region = "ap-northeast-2"
            key = "terraform/vpc/terraform.tfstate"
        }
    }

주요 명령어

terraform init
테라폼 명령어 사용을 위해 각종 설정을 진행 (git init과 비슷한 역할)
terraform plan
테라폼으로 작성한 코드가 실제로 어떻게 만들어질지에 대한 예측 결과를 보여줌
terraform apply
테라폼 코드로 실제 인프라를 생성하는 명령어.
terrafrom import
이미 만들어진 자원을 테라폼 state 파일로 옮겨주는 명령어.
terraform state
테라폼 state를 다루는 명령어. 하위 명령어로 mv, push 등이 있음
terraform destroy
생성된 자원들 state 파일 모두 삭제하는 명령어. (백업 등의 설정을 안해놨으면 한번에 지워지니 조심해서 사용할 것.)

+ Recent posts