CI/CD 툴인 Atlantis를 통해 Custom workflow를 통해 인프라를 구성합니다.
Atlantis?
Atlantis는 pull request를 통해 Terraform을 자동화하는 오픈소스입니다.
Terraform 코드를 pull request 하고, comment에 atlantis plan, atlantis apply를 통해 테라폼 코드를 배포할 수 있습니다.
Atlantis의 기본 동작은 아래와 같습니다.
github 또는 gitlab, bitbucket을 통해 PR(pull request)를 보냅니다.
git repo에 설정해노흔 webhook을 통해 atlantis에서 comment에 입력된 명령어를 전달받아 terraform을 수행합니다.
Atlantis 구축해보기
repo는 github을 사용하고, terraform을 통해 인스턴스로 배포하였습니다.
스텝은 아래와 같이 진행하였고,
Docker를 통해 실행하는 것이 아니므로 Github 설정을 위해 인스턴스 생성을 제일 먼저 진행하였습니다.
[ atlantis 인스턴스 생성 > github 설정 > secret key 생성 > atlantis 실행 > github health check > 테라폼 배포 ]
Step 1.
- EC2 생성
[vpc.tf]
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
version = "5.1.2"
name = "ssungz-vpc"
cidr = "10.1.0.0/16"
azs = ["ap-northeast-2a", "ap-northeast-2c"]
private_subnets = ["10.1.1.0/24", "10.1.2.0/24"]
public_subnets = ["10.1.10.0/24", "10.1.20.0/24"]
enable_nat_gateway = false
enable_vpn_gateway = false
tags = {
Terraform = "true"
Environment = "test"
}
}
module "security-group" {
source = "terraform-aws-modules/security-group/aws"
version = "5.1.2"
name = "atlantis-sg"
vpc_id = module.vpc.vpc_id
ingress_with_cidr_blocks = [{
from_port = 22
to_port = 22
protocol = "tcp"
description = "ssh"
cidr_blocks = "61.75.30.162/32"
},
{
from_port = 4141
to_port = 4141
protocol = "tcp"
description = "atlantis portal"
cidr_blocks = "0.0.0.0/0"
}]
egress_with_cidr_blocks = [
{
from_port = 0
to_port = 0
protocol = -1
cidr_blocks = "0.0.0.0/0"
}
]
}
[ec2.tf]
module "ec2_instances" {
source = "terraform-aws-modules/ec2-instance/aws"
version = "5.5.0"
name = "atlantis-srv"
ami = "ami-056a29f2eddc40520"
instance_type = "t2.micro"
subnet_id = module.vpc.public_subnets[0]
key_name = "hoon-office"
vpc_security_group_ids = [module.security-group.security_group_id]
associate_public_ip_address = true
user_data_replace_on_change = true
user_data = "${file("install.sh")}"
}
output "instance_public_ip" {
value = module.ec2_instances.public_ip
}
[install.sh]
해당 코드는 가시다님이 만든 코드를 사용했습니다.
#!/bin/bash
hostnamectl --static set-hostname Atlantis
echo 'alias vi=vim' >> /etc/profile
echo "sudo su -" >> /home/ubuntu/.bashrc
wget -O- https://apt.releases.hashicorp.com/gpg | sudo gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/hashicorp.list
apt update -qq && apt install tree jq unzip zip terraform -y
curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
unzip awscliv2.zip
sudo ./aws/install
wget https://github.com/runatlantis/atlantis/releases/download/v0.28.3/atlantis_linux_amd64.zip -P /root
unzip /root/atlantis_linux_amd64.zip -d /root && rm -rf /root/atlantis_linux_amd64.zip
인스턴스 내부 확인 (user data check)
$ ssh -i ~/.ssh/hoon-office.pem ubuntu@$(terraform output -raw instance_public_ip)
AWS configure 설정
Step 2.
- Github repo 생성
- Atlantis secret 생성
https://www.browserling.com/tools/random-string
- Github Wehook 설정
- webhook trigger 체크
1) Issue comments
2) Pull request reviews
3) Pushes
4) Pull requests
- Atlantis 실행을 위한 설정 값 환경 변수 선언
- Atlantis 실행
./atlantis server \
--atlantis-url="$URL" \
--gh-user="$USERNAME" \
--gh-token="$TOKEN" \
--gh-webhook-secret="$SECRET" \
--repo-allowlist="$REPO_ALLOWLIST" \
--repo-config=/root/repos.yaml
(Atlantis Server Side)
atlantis를 바이너리로 다운받아 실행했기 때문에 repo-config 파일이 따로 없었고,
처음에 PR을 날려 Plan을 돌리면 계속 오류가 발생했고, workflow 허용을 위해 repos.yaml를 사용하였습니다.
ERROR: parsing atlantis.yaml: repo config not allowed to set 'workflow' key: server-side config needs 'allowed_overrides: [workflow]'
## repos.yaml
repos:
- id: /.*/
allowed_overrides: [workflow, apply_requirements, delete_source_branch_on_merge]
allow_custom_workflows: true
- webhook health check
생성된 서버로 webhook이 정상적으로 통신하는지 체크합니다.
최초 서버가 열리지 않아 실패했을 경우 redelivery로 재시도합니다.
- atlantis 웹 페이지 접속 확인
- 원격 레포지토리 clone
$ git clone https://github.com/ssungz789/atlantis-test.git
- feature branch 생성 및 checkout
$ git branch atlantis_test && git checkout atlantis_test
Terraform 인스턴스 배포
단순 인스턴스 2대 생성하는 간단한 구성이지만 테라폼이 아직 익숙지 않아 tf 파일이 지저분합니다..ㅎㅎ
dev와 prod의 코드가 동일하고 서브넷과 environments 변수 값만 달라, dev만 기재했습니다.
atlantis-test ➤ tree git:atlantis_test
.
├── README.md
├── atlantis.yaml
├── environments
│ ├── dev
│ │ ├── dev_main.tf
│ │ └── variables.tf
│ ├── prod
│ │ ├── prod_main.tf
│ │ └── variables.tf
│ └── provider.tf
├── modules
│ └── all_module
│ ├── data.tf
│ ├── main.tf
│ └── variables.tf
├── script
│ └── install.sh
└── variables
├── dev.tfvars
└── prod.tfvars
- atlantis.yaml
version: 3
projects:
- name: dev
dir: environments/dev
workflow: dev-workflow
autoplan:
when_modified: ["*.tf", "../../modules/**/*.tf", "../../variables/dev.tfvars"]
enabled: true
- name: prod
dir: environments/prod
workflow: prod-workflow
autoplan:
when_modified: ["*.tf", "../../modules/**/*.tf", "../../variables/prod.tfvars"]
enabled: true
workflows:
dev-workflow:
plan:
steps:
- init:
extra_args: ["-backend-config=../../variables/dev.tfvars"]
- plan:
extra_args: ["-var-file=../../variables/dev.tfvars"]
apply:
steps:
- run: terraform apply -input=false $PLANFILE
prod-workflow:
plan:
steps:
- init:
extra_args: ["-backend-config=../../variables/prod.tfvars"]
- plan:
extra_args: ["-var-file=../../variables/prod.tfvars"]
apply:
steps:
- run: terraform apply -input=false $PLANFILE
- module/all_module/main.tf
data "template_file" "user_data" {
template = file("../../script/install.sh")
vars = {
name = "ssungz"
env = var.environment
}
}
resource "aws_instance" "web-svr" {
ami = var.ami
instance_type = var.instance_type
key_name = var.key_name
vpc_security_group_ids = [aws_security_group.web-sg.id]
associate_public_ip_address = true
subnet_id = var.subnet_id
user_data = data.template_file.user_data.rendered
tags = {
Name = var.instance_name
}
}
resource "aws_security_group" "web-sg" {
vpc_id = data.aws_vpc.ssungz.id
ingress {
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
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"]
}
}
- variables.tf
variable "environment" {
description = "deployed environment"
type = string
}
variable "ami" {
description = "ami id"
type = string
}
variable "instance_type" {
description = "instance type"
type = string
}
variable "key_name" {
description = "keypair name"
type = string
}
variable "subnet_id" {
description = "pub subnet name"
type = string
}
variable "instance_name" {
description = "instance name"
type = string
}
- environments/dev_main.tf
module "all_module" {
source = "../../modules/all_module"
ami = var.ami
key_name = var.key_name
instance_name = "dev"
instance_type = var.instance_type
subnet_id = "subnet-0c990778d5cb7889d"
environment = "Dev"
}
output "instance_public_ip" {
value = module.all_module.instance_public_ip
}
- dev.tfvars
key_name = "hoon-office"
instance_type = "t2.micro"
ami = "ami-03d9e351318b31439"
- Git Push
코드를 push하면 PR 올라가 Plan을 수행합니다.
$ git add . && git commit -m "init commit" && git push origin atlantis_test
plan / apply 시 오류가 발생하면 PR에서 comment하여 어디서 오류가 발생했는지 확인할 수 있습니다.
(Issue comments 설정으로 인해 표시되는 것으로 보입니다.)
PR을 날리면 진행 상황을 github 에서 볼 수 있고, Atlantis 웹에서도 서버 프롬프트처럼 진행 상황을 확인할 수 있습니다.
- atlantis apply
디렉터리 (디렉토리 별 수행이 필요할 경우 atlantis apply -d dev 로 수행할 수 있고 프로젝트와 workspace별로 배포도 가능합니다.)
https://www.runatlantis.io/docs/using-atlantis
apply가 완료되었고, 인스턴스의 생성과 웹 접근도 잘되는 것을 확인했습니다.
Atlantis는 destory 명령어를 제공하지 않기 때문에 깡통 브런치를 만들어 정리하거나,
원격 저장소에 저장된 state 파일로 작업 디렉토리에서 destroy를 해야합니다.
'Cloud > Terraform' 카테고리의 다른 글
[T101] Terraform 101 Study 7주차 Opentofu (0) | 2024.08.03 |
---|---|
[T101] Terraform 101 Study 7주차 (EKS, Karpenter) (0) | 2024.07.27 |
[T101] Terraform 101 Study 4주차 (0) | 2024.07.05 |
[T101] Terraform 101 Study 실습(5) - for (1) | 2024.07.03 |
[T101] Terraform 101 Study 실습(4) - data resource (0) | 2024.07.02 |