본문 바로가기
IaC/Terraform

[Terraform] 5-1. GitHub와 Atlantis를 연동하여 Terraform 자동화 환경 구축하기

by okms1017 2024. 7. 14.
728x90

✍ Posted by Immersive Builder  Seong

 

 

1. Atlantis 소개 

Atlantis 란?

Atlantis는 깃옵스 방식으로 테라폼 코드의 변경 사항을 자동으로 적용하는 인프라 관리 자동화 도구입니다. 

 

  • GitOps 기반 워크플로우 : Atlantis는 Git 레포지토리와 통합되어 테라폼 코드의 변경 사항을 PR을 통해 관리합니다. 
  • 테라폼 작업 자동화 : PR이 생성되거나 업데이트될 때 Atlantis가 자동으로 플랜을 실행하고, 검토 후 PR이 승인되면 자동으로 변경 사항을 인프라에 반영합니다. 
  • 다중 환경 지원 : 여러 워크스페이스와 환경을 지원하여 개발/스테이징/운영 환경으로 개별 관리가 가능하고, 환경 간에 별도의 설정을 적용하여 격리를 유지할 수 있습니다. 
  • 사용자 및 권한 관리 : GitHub, GitLab, Bitbucket 등 Git 플랫폼과 통합되어 사용자 인증 및 권한 관리를 지원합니다. 
  • 확장성 : 커스텀 워크플로우, 사용자 정의 정책, 다양한 플러그인 등을 통해 기능을 확장할 수 있습니다. 

 

Atlantis 동작 방식

 

Atlantis architecture

 

  1. 개발자가 테라폼 코드를 변경하고 Git 레포지토리에 Push하여 PR을 생성합니다. 
  2. GitHub에서 Webhook을 통해 Atlantis 서버로 데이터를 전송합니다. 
  3. Atlantis 서버는 자동으로 플랜을 실행합니다. 
  4. 생성된 플랜은 PR의 코멘트로 추가됩니다. 
  5. 팀원들이 PR을 리뷰하고 변경 사항을 승인합니다. 
  6. 개발자가 Apply 명령어를 PR의 코멘트로 추가합니다. 
  7. 다시 GitHub에서 Webhook을 통해 Atlantis 서버로 데이터를 전송합니다.
  8. Atlantis 서버가 자동으로 변경 사항을 적용(Apply)하여 인프라에 반영합니다. 
  9. 성공적으로 반영되면 수행 결과가 PR의 코멘트로 추가됩니다.
  10. 팀 관리자는 PR을 완료하고 병합합니다. 

 


2. Atlantis 구축 및 사용 

Atlantis 서버 배포

AWS Cloudformation을 사용하여 Atlantis 템플릿 파일을 넣고 배포합니다.

 

aws cloudformation deploy --template-file t101-atlantis-ec2.yaml --stack-name t101 --parameter-overrides KeyName=$MYKEYNAME SgIngressSshCidr=$(curl -s ipinfo.io/ip)/32 --region ap-northeast-2
aws cloudformation describe-stacks --stack-name t101 --query 'Stacks[*].Outputs[0].OutputValue' --output text

 

Atlantis 서버 배포 확인

Atlantis 서버에는 이미 테라폼과 aws cli가 설치되어 있고, atlantis 바이너리 설치 파일이 압축해제되어 있습니다. 

배포가 완료되면 서버에 접속하여 AWS 자격 증명을 진행합니다. 

 

$ terraform version
Terraform v1.9.2

 

$ aws configure
AWS Access Key ID [None]: AKIA*******
AWS Secret Access Key [None]: imR*******
Default region name [None]: ap-northeast-2
Default output format [None]: 

$ aws s3 ls 

 

./atlantis version
atlantis 0.28.3 (commit: b3dabab) (build date: 2024-06-18T20:13:16Z)

 

Atlantis 서버는 기본 4141 포트를 사용합니다. 

외부에서 접근 가능한 '공인 IP:포트' 주소를 URL 변수로 지정합니다.  

 

URL="http://$(curl -s ipinfo.io/ip):4141"

echo $URL

 

Git Repo & Token 생성  

테라폼 코드를 업로드할 프라이빗 Git 레포지토리를 하나 생성합니다. 

Private Git Repository 생성

그리고 Atlantis 서버에서 Git 레포지토리를 API 호출할 때 필요한 인증 정보인 Git 토큰을 생성합니다.  

Git 토큰 발급 1
Git 토큰 발급 2
Git 토큰 발급 3

발급한 Git 토큰 정보를 환경변수로 지정합니다. (분실하지 않도록 유의!)

 

TOKEN=ghp_**********

 

Webhook Secret 생성

반대로 Git 레포지토리에서 전송한 웹훅이 유효한지 Atlantis 서버에서 검증하기 위해서 웹훅 시크릿을 사용합니다. 

웹훅 시크릿으로 랜덤 문자열을 사용할 수 있고, 최소 24자 이상이어야 합니다. 

 

Webhook Secret 생성

생성한 웹훅 시크릿은 Atlantis 서버에 환경변수로 지정합니다. 

 

SECRET=plpseuqvcdqeboeifzyjczne

 

Webhook 생성

Git 레포지토리에서 Atlantis 서버로 전송할 웹훅을 생성합니다.

webhook 생성 1

웹훅을 트리거할 이벤트로 'Pushes, Pull requests, Pull request reviews, Issue comments' 4개의 옵션을 체크합니다. 특히, 'Issue comments' 옵션을 체크하여야 Atlantis 서버에서 추가한 코멘트에 대한 응답을 받을 수 있습니다. 

webhook 생성 2

생성된 웹훅의 자세한 정보를 확인해보면 ping 통신에 대한 502 에러가 발생하고 있습니다. 

그 이유는 Atlantis를 아직 실행하지 않았기 때문입니다. 

webhook 상세 정보 확인 1
webhook 상세 정보 확인 2

Atlantis 기동

하기 명령어를 실행하여 Atlantis를 기동합니다. 

만약 기동에 실패한다면 환경 변수가 제대로 지정되어 있는지 확인합니다.  

 

$ echo $URL $USERNAME $TOKEN $SECRET $REPO_ALLOWLIST

$ ./atlantis server \
--atlantis-url="$URL" \
--gh-user="$USERNAME" \
--gh-token="$TOKEN" \
--gh-webhook-secret="$SECRET" \
--repo-allowlist="$REPO_ALLOWLIST"

 

새 터미널을 열어 소켓 정보를 조회합니다. 

정상적으로 4141 포트로 리스닝하고 있습니다. 

 

ss -lntp
State         Recv-Q     Send-Q      Local Address:Port      Peer Address:Port     Process              
LISTEN     0              4096                               *:4141                         *:*           users:(("atlantis",pid=2103,fd=7))      

 

변수로 지정한 URL 주소로 웹 페이지 접근합니다. 

 

Atlantis 웹 접속 화면

이전에 웹훅에서 실패했던 ping 통신을 재발송(Redeliver)하면 상태코드 200으로 정상 연결됩니다. 

Response 200 (Redeliver)

 

null 프로바이더 배포 

'atlantis-cicd' 레포지토리를 로컬에 복제한 후 test 브랜치를 생성하여 작업을 시작합니다. 

 

git checkout -b test

 

우선 간단한 테라폼 코드를 다음과 같이 작성한 후 Push하고 PR을 생성합니다. 

 

echo 'resource "null_resource" "example" {}' > main.tf

git add main.tf && git commit -m "add main.tf" && git push origin test

 

PR 생성

PR이 생성될 때 자동으로 변경된 코드의 플랜을 수행하게 됩니다. 

excute auto plan

'Details'를 클릭하여 플랜의 상세 내역을 확인할 수 있습니다. 

plan details 1
plan details 2

Atlantis 서버에서 '.atlantis' 디렉토리를 모니터링하면 플랜이 자동으로 실행됨과 동시에 null 프로바이더를 다운로드하고 플랜을 생성함을 확인할 수 있습니다. 

watch -d tree .atlantis/

이번에는 PR의 코멘트로 atlantis help 명령어를 실행해봅니다. 

명령어 실행 결과가 정상적으로 출력됩니다. 

comment (atlantis help)

하지만 'cat /etc/passwd' 와 같은 보안에 취약할 수 있는 명령어들을 입력하면 실행되지 않고 상태가 잠깁니다. (Locked)

cat /etc/passwd (Locked)

상태가 잠겼을 때에는 Atlantis 웹 페이지에 접속하여 잠김 항목을 직접 선택한 후 해제(Unlock)합니다. 

Atlantis - Locked Item
Atlantis - Discard Plan & Unlock

플랜을 검토하고 이상이 없으면 변경 사항을 적용합니다. 

'atlantis apply -d .' 는 'terraform apply' 를 수행하는 Atlantis 명령어입니다. 

atlantis apply -d .
Apply result submitted as a comment on PR

성공적으로 리소스에 반영되면 PR을 완료하고 병합(Merge)합니다. 

작업을 완료하였으므로 feature branch는 삭제해줍니다. 

Merge PR
Confirm merge
PR merged & closed

'Confirm merge'를 하는 순간 기존 number 디렉토리에 저장되어 있던 캐시 정보가 깨끗이 비워집니다. 

여러 번 테스트한 결과 디렉토리 number는 PR number와 동일하게 증가함을 확인하였습니다. 

watch -d tree .atlantis/
Atlantis PR 작업 내역

$ git checkout main

$ git pull origin main 

 

AWS IAM User 생성

Atlantis 서버는 원격 저장소를 백엔드로 지정합니다. 

이번 실습에서는 Atlantis를 사용하여 IAM User를 생성하고, State는 S3 버킷에 저장되도록 할 예정입니다. 

 

임의의 버킷을 하나 생성합니다. 

 

$ aws s3 mb s3://atlantis-iam-bucket --region ap-northeast-2

make_bucket: atlantis-iam-bucket

$ aws s3 ls | grep atlantis-iam-bucket  

2024-07-14 03:41:27 atlantis-iam-bucket

 

새로운 브랜치(iam)를 따서 IAM User를 생성하는 코드를 작성하고 Push/PR 까지 진행합니다. 

 

$ git checkout -b iam 

$ mkdir iam && cd iam

$ vi main.tf

terraform {
  backend "s3" {
    bucket = "atlantis-iam-bucket"
    key    = "terraform.tfstate"
    region = "ap-northeast-2"
  }
}

resource "aws_iam_user" "myuser" {
  name = "seong"
}

$ git add main.tf && git commit -m "add main.tf" && git push origin iam

 

마찬가지로 PR을 생성한 이후 자동으로 플랜이 실행됩니다. 

여기서 이전과 다른 점은 iam 디렉토리를 지정한 부분입니다. 

 

PR 생성
excute auto plan
plan details

aws 프로바이더를 추가로 다운로드하고 PR number는 3이며, iam 디렉토리 하위에 플랜을 생성합니다. 

watch -d tree .atlantis/

'atlantis apply -d iam' 명령어를 실행하여 State가 지정한 S3 버킷에 생성되는지 확인합니다. 

atlantis apply -d iam

S3 버킷에 State가 정상적으로 저장된 모습입니다. 

 

$ aws s3 ls s3://atlantis-iam-bucket
2024-07-13 19:11:31        853 terraform.tfstate

 

S3 bucket (atlantis-iam-bucket)

IAM User 또한 정상적으로 생성되었습니다. 

IAM User 생성 확인

PR을 병합하고 나면 Atlantis 서버에서 PR 관련된 캐시가 모두 삭제됩니다. 

watch -d tree .atlantis/
Atlantis PR#3 작업 내역

작업을 마무리하고 로컬 레포지토리를 Git 레포지토리와 동기화합니다. 

 

$ git checkout main

$ git pull 

 

AWS IAM User 삭제 

Atlantis는 삭제(destroy) 명령어를 지원하지 않습니다. 

추측컨대, 실 운영 환경에서의 리소스 삭제는 심각한 장애를 초래할 수 있기 때문에 지원하지 않는 것으로 보입니다. 

 

그럼 리소스를 삭제해야 하는 경우 어떻게 할까요?

State를 동기화하는 테라폼의 성질을 이용하여, 실제 리소스와 State는 유지하고 구성 파일에 해당 리소스를 정의하지 않는 방식으로 삭제할 수 있습니다. 

 

위에서 생성한 IAM User를 삭제합니다. 

백엔드만 선언하고 리소스는 정의하지 않습니다. 

 

$ git checkout -b deleteiam 

$ mkdir deleteiam && cd deleteiam

$ vi main.tf

terraform {
  backend "s3" {
    bucket = "atlantis-iam-bucket"
    key    = "terraform.tfstate"
    region = "ap-northeast-2"
  }
}

$ git add main.tf && git commit -m "add main.tf" && git push origin deleteiam

 

IAM User를 삭제하는 PR을 생성하고 플랜을 검토합니다. 

PR 생성
deleteiam plan
deleteiam plan details

'atlantis apply -d deleteiam' 명령어를 실행하여 IAM User를 삭제합니다. 

atlantis apply -d deleteiam

AWS 콘솔에서 IAM User가 삭제된 것을 확인할 수 있습니다. 

IAM User 삭제 확인

PR을 Close하고 작업 브랜치를 삭제합니다. 

PR#4 Closed
watch -d tree .atlantis/
Atlantis PR#4 작업 내역

 

$ git checkout main && cd .. && git pull

 


리소스 삭제

  • CloudFormation Stack & S3 bucket 삭제 
$ aws cloudformation delete-stack --stack-name t101
$ aws s3 rm s3://atlantis-iam-bucket --recursive
$ aws s3 rb s3://atlantis-iam-bucket

 

  • GitHub Repo 삭제

  • Git Token 삭제

 

 


[출처]

1) CloudNet@, T1014 실습 스터디 

2) https://www.runatlantis.io/

 

Atlantis

Atlantis Terraform Pull Request Automation Running Terraform Workflows with Ease

www.runatlantis.io

3) https://www.runatlantis.io/docs/deployment#roll-your-own

 

Deployment | Atlantis

 

www.runatlantis.io

4) https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens#creating-a-fine-grained-personal-access-token

 

Managing your personal access tokens - GitHub Docs

You can use a personal access token in place of a password when authenticating to GitHub in the command line or with the API.

docs.github.com

728x90