상세 컨텐츠

본문 제목

Golden Image Pipeline 구성 - 1. Packer와 Ansible로 이미지 만들기

🔄 CICD

by chnh 2023. 6. 26. 14:07

본문

반응형

들어가며

이 글에서는 Immutable Infrastructure가 무엇인지 이해하고, 이를 구현하기 위한 첫 단계로 Packer와 Ansible을 사용하여 골든 이미지를 만든다. 이후 Github Actions을 사용해 변경사항이 Ansible Playbook에 푸시될 때마다 자동으로 Packer를 실행하는 Golden Image 파이프라인을 구성한 다음, Terraform을 사용하여 변경된 이미지로 인프라를 교체하도록 설정하여 자동화된 Immutable Infra를 구현하는 것을 목표로 한다.

완성하면 요런느낌 ✨

시리즈는 다음과 같이 계획되어있다.

1. Packer와 Ansible로 이미지 만들기

2. GitHub Actions로 이미지 생성 자동화

3. Terraform을 사용하여 AWS에 변경된 인프라 배포

 

Immutable Infrastructure와 Golden Image

AWS를 사용중인 사람이라면 인스턴스를 생성할 때 AMI(Amazon Machine Image)를 사용해봤을 것이다. AMI는 미리 구성된 운영 체제와 설치된 소프트웨어를 포함하고 있는 머신 이미지인데, 덕분에 인스턴스를 시작할 때마다 수동으로 구성하는 절차 없이 빠르게 리소스를 프로비저닝 할 수 있다. 이 AMI에 대한 일종의 마스터 템플릿을 골든 이미지라고 한다.

 

DevOps 관점에서 이미지, 특히 골든 이미지는 변경 불가능한 인프라(Immutable Infrastructure)를 구현하는 데 중요한 역할을 한다. 필요한 구성과 소프트웨어가 모두 포함된 완벽한 골든 이미지를 생성한 다음 해당 이미지를 사용하여 인프라를 생성한다는 아이디어로, 서버에 업데이트 또는 수정이 필요한 경우 이미 사용 중인 서버를 업데이트하는 대신 새로운 이미지를 사용하여 새 서버를 배포하는 방식을 선택한다. 즉 한번 실행된 인스턴스는 변경되지 않고, 변경이 필요한 경우에는 업데이트된 이미지를 가지고 아예 새로운 인스턴스로 교체한다. 때문에 구성 드리프트가 발생할 틈이 없다.

 

https://devopscube.com/immutable-infrastructure

 

이렇게 인프라를 관리하면 다음과 같은 이점이 있다.

 

  1. 일관성: 모든 인스턴스가 동일하므로 구성 드리프트로 인해 문제가 발생할 가능성이 줄어든다.
  2. 효율성: 골든 이미지에는 OS뿐만 아니라 설치 및 구성된 애플리케이션도 포함될 수 있기 때문에 빠르게 부팅할 수 있다.
  3. 신뢰성: 사전에 검증된 이미지를 사용하기때문에 구성 오류가 발생할 가능성이 크게 줄어든다.
  4. 확장성: 확장이 필요할 때 골든 이미지를 사용하여 새로운 동일한 인스턴스를 신속하게 온라인으로 가져올 수 있다.
  5. 복구 용이성: 새 버전에 문제가 있는 경우 이전 골든 이미지를 인스턴스화하여 이전 버전으로 쉽게 되돌릴 수 있고 현재 사용중인 서비스에 문제가 발생하는 경우 빠르게 복원할 수 있다.
  6. IaC의 발전이 Immutable Infra를 구현하는 데에 큰 기여를 했다고 생각한다. 이미지, 인스턴스, 네트워크와 같은 리소스들을 코드로 쫙 작성해서 한번에 테스트하고 승격하고 배포할 수 있게 됐으니 말이다. IaC가 등장하지 않았다면 일관된 방식으로 이런 단계를 반복하는 것은 많은 시간을 들이고, 구현한다 한들 수작업 과정에서 문제가 발생했겠지. ㅎㅎ...끔찍하다... 무튼 ! 이제 이 좋은 도구들을 가지고 자동으로 일관된 환경을 유지해보자.

 

Packer와 Ansible

Packer를 한 마디로 정의하자면 '머신 이미지 빌더'라고 할 수 있을 것 같다. Template을 사용해 여러 플랫폼의 머신 이미지를 생성할 수 있는 Hashicorp의 오픈소스로, AWS Image builder와 유사한 기능을 한다. Packer는 이미지를 만들 때 가상 머신의 특정 시점의 상태만을 저장하는 것이 아니라 이미지가 생성되는 과정도 코드로 저장되기 때문에 유사한 이미지를 반복 생성할 때 도움이 된다.

Template 은 빌드하려는 이미지와 빌드 방법을 정의한 구성파일로, Builder와 Provisioner를 조합하여 구성한다. 초기에는 JSON 형식(.pkr.json)으로 템플릿을 작성했으나 현재는 Terraform과 같은 HCL2를 사용하도록 장려하고 있다. 이 글에서는 HCL로 템플릿을 작성한다.

 

https://hackmd.io/@XeusNguyen/BJtEoB_Ci

  • Builder
    • 빌더는 머신을 생성하고 해당 머신에서 다양한 플랫폼용 이미지를 생성한다. AWS에서 사용할 AMI를 생성할지, Docker 컨테이너 이미지를 생성할지 등을 결정한다.
    • 예시를 살펴보면 좀 더 감이 오는데, 다음은 일반적으로 사용하는 빌더들이다.
      • Amazon EC2 (amazon-ebs, amazon-ebssurrogate, amazon-ebsvolume, amazon-chroot, amazon-instance)
      • Google Compute Engine(googlecompute)
      • Azure Resource Manager(azure-arm)
      • Docker (docker)
      • VMWare(vmware-iso, vmware-vmx)
    • 이외에도 custom builder나 packer 커뮤니티에서 지원하는 빌더를 사용할 수 있다.

 

  • Provisioner
    • 프로비저너는 built-in 또는 third-party 소프트웨어를 사용하여 부팅 후 패키지 설치, 커널 패치, 사용자 생성, 애플리케이션 코드 다운로드 등의 작업을 수행하여 이미지를 원하는 상태로 프로비저닝한다.
    • 다음과 같은 프로비저너들을 사용할 수 있으며, 최신 정보는 공식 문서에서 확인할 수 있다.
      • ansible
      • ansible-local
      • chef-client
      • chef-solo
      • file
      • powershell
      • shell
      • shell-local
      • puppet-masterless
      • puppet-server
      • salt-masterless
      • windows-restart
      • windows-shell

 

  • Post-Processor
    • 포스트 프로세서는 빌드와 프로비저닝 후에 실행된다. 따라서 필수적인 옵션은 아니며, 프로비저닝 이후 생성된 artifact들에 대해 작업을 수행한다. 예를 들어 포트스 프로세서로 compress 를 사용하여 build된 이미지를 tar 파일로 압축해서 저장하거나, docker-push를 사용해 docker 레지스트리에 이미지를 푸시할 수 있다. manifest를 사용하여 artifact에 대한 정보를 json 파일로 출력하는 등의 작업 또한 가능하다.

 

 

 

 

Ansible은 원격으로 서버를 구성하고 관리할 수 있는 RedHat의 오픈소스이다. Ansible은 Python을 기반으로 개발되었으며, YAML 포

맷을 기반으로 인프라를 정의한다. Ansible을 사용하면 관리할 서버에 별도의 데몬을 설치하지 않고 원격으로 접속해서 서버를 구성할 수 있다. Packer로 이미지를 빌드할 때 Ansible을 프로비저너로 사용하면 이미지에 필요한 모든 소프트웨어를 사전에 설치할 수 있고, playbook을 작성해 여러번 재사용할 수 있어 효율적인 인프라 관리가 가능하다.

 

1. Packer, Ansible 설치

macOS를 사용중이라 brew로 설치했다.

# install packer 
brew tap hashicorp/tap
brew install hashicorp/tap/packer

# install ansible
brew install ansible

 

2. Ansible playbook 작성

provisioner로 ansible을 사용할 것이기 때문에 playbook을 미리 작성한다.

playbook은 원격 host에서 실행할 일련의 작업을 yaml로 작성한 스크립트이다. 다음 스크립트는 ec2의 timezone을 서울로 변경하고, 최신 버전으로 git을 설치하고, 특정 버전의 nginx를 설치하고, nginx를 시작한다.

# playbook.yml
- name: Start Playbook
  hosts: all
  become: yes
  tasks:
    - name: Set Asia/Seoul timezone
      timezone:
        name: Asia/Seoul

    - name: Install git
      yum:
        name: git
        state: latest

    - name: Enable extras
      command: amazon-linux-extras install -y nginx1.12
      args:
        creates: /etc/nginx/nginx.conf

    - name: Start Nginx
      ansible.builtin.systemd:
        name: nginx
        state: started
        enabled: yes

 

3. Packer 템플릿 생성

1. packer 템플릿을 저장할 디렉토리를 생성한다.

mkdir packer-practice
cd packer-practice

 

2. 작성한 Packer 템플릿은 다음과 같다.

source_ami는 AMI 카탈로그에서 참조하거나 private으로 사용하는 ami가 있다면 해당 ami를 사용해도 된다. 최신버전의 ami를 사용하고자 하는 경우 filter를 통해 조회 후 적용이 가능하다.

 

EC2 콘솔에서 제공하는 AMI 목록

# packer-aws-linux.pkr.hcl 

locals {
  timestamp = regex_replace(timestamp(), "[- TZ:]", "")
}

source "amazon-ebs" "packer-ami" {
  region          = "ap-northeast-2" # AMI를 생성할 리전 
  ami_name        = "packer-aws-${local.timestamp}"
  source_ami      = "ami-0a0064415cdedc552" # Amazon Linux 2 AMI (HVM)
  vpc_id          = "vpc-id" # AMI가 생성될 VPC
  subnet_id       = "subnet-id" # AMI가 생성될 Subnet
  instance_type   = "t2.micro"
  ssh_interface   = "public_ip"
  ssh_username    = "ec2-user" # Linux ssh user (ubuntu일 경우 admin)
  ami_description = "Nginx with Amazon Linux2"
  assume_role {
    role_arn = "arn:aws:iam::account-id:role/admin"
  }

  tags = { # https://developer.hashicorp.com/packer/plugins/builders/amazon/ebs#build-shared-information-variables
    Base_AMI_ID   = "{{ .SourceAMI }}"
    Base_AMI_Name = "{{ .SourceAMIName }}"
  }
}

build {
  sources = [
    "source.amazon-ebs.packer-ami"
  ]

  provisioner "ansible" {
    playbook_file = "playbook/playbook.yml"
    use_proxy     = "false"
    user          = "ec2-user"

  }
}

 

  • packer
    • packer 블록은 terraform 구성 파일에서 terraform 블록과 유사한 역할을 한다. 구성을 적용하는 데 필요한 최소 요구 사항 ( required_version, required_plugins)과 같은 패커 자체의 동작에 대해 정의한다.
  • source
    • Packer가 새 머신 이미지를 생성하기 위해 소스 이미지를 찾아야하는 위치를 정의한다. build 블록에서 해당 source 블록을 참조하여 builder를 시작할 수 있다. 
  • build
    • build를 어떻게 수행할지에 대해 정의하는 블록이다. 어떤 builder(e.g.VirtualBox, VMware, Amazon EC2)를 사용할지, 어떤 방법으로 프로비저닝 할지 등을 지정한다.
    • provisioner 블록을 포함시켜 ansible 함께 구성한다거나 post-processor를 사용하여 build의 결과물(artifact)을 어떻게 처리할지 정의할 수 있다.
  • variables, locals, data 블록은 terraform에서와 유사한 용법으로 사용된다.

 

3. 구성을 Initialize 한다.

packer init .

init을 실행하면 packer는 구성 파일을 분석해서 필요한 플러그인을 식별하고 설치되어 있지 않다면 필요한 플러그인을 설치한다. 이때 빌더나 프로비저너와 관련된 항목은 관여하지 않고 오로지 플러그인만 확인한다.

 

4. packer 파일의 syntax에 문제가 없는지 확인하고 유효성 검사를 실시한다.

packer fmt packer-aws-linux.pkr.hcl 
packer validate packer-aws-linux.pkr.hcl

 

4. Packer 이미지 빌드

1. pakcer build 명령을 통해 이미지를 빌드해보겠다. build를 실행하면 Packer는 머신 이미지를 생성하고 Provisioner를 실행시킨다.

packer build packer-aws-linux.pkr.hcl

build가 성공한 화면

2. 콘솔에서 결과를 확인해보자.

build 명령 후 출력된 ami-id와 동일한 id의 이미지가 생성된 것을 확인할 수 있다. 태그도 의도했던대로 추가되었다. packer 템플릿에서 packer-aws-${local.timestamp}와 같이 AMI 이름에 생성된 시간이 찍히게 설정해두는데 정상적으로 반영되었다.

 

5. AMI로 EC2 생성

생성한 AMI로 EC2를 시작하여 ansible로 구성한 내역이 잘 들어갔는지 확인해봤다.

모두 정상적으로 실행된 것을 확인할 수 있었다.

 

마치며

packer는 처음 사용해보았는데, terraform을 사용하고 있어서 그런지 무리 없이 사용할 수 있었다. 여기서는 간단히 nginx를 설치하는 등의 작업만 진행했지만 실제로 애플리케이션을 미리 배포하고 구동하는 등의 작업을 playbook으로 실행하고 프로비저닝 해둔다면 auto scaling용 golden ami를 유지 관리하는데 큰 도움이 되겠다고 느꼈다. 꼭 프로덕션용으로 사용하지 않더라도 사내 표준 test base ami를 구성해두면 엔지니어나 개발자가 동일 환경에서 테스트 할 수 있다는 장점도 있을 것으로 보인다.

다음 글에서는 github action을 사용해 packer나 ansible playbook에 변경사항이 있을 때 이를 감지하고 새롭게 이미지를 build하는 방법을 설명해보려 한다.

 

 


참고자료

https://developer.hashicorp.com/packer/tutorials/aws-get-started/aws-get-started-build-image?in=packer%2Faws-get-started

https://www.44bits.io/ko/post/building-docker-image-and-using-packer

https://developer.hashicorp.com/packer/tutorials/aws-get-started/aws-get-started-build-image

반응형

관련글 더보기

댓글 영역