AWS에서 Terraform을 사용한 제로 다운타임 Jenkins 지속적 배포

게시 됨: 2022-03-11

말 그대로 모든 것이 연중무휴 가동되어야 하는 오늘날의 인터넷 세계에서는 안정성이 핵심입니다. 이는 웹사이트의 가동 중지 시간이 거의 0에 가깝고 최신 릴리스를 출시하는 동안 두려운 "찾을 수 없음: 404" 오류 페이지 또는 기타 서비스 중단을 피하는 것으로 해석됩니다.

클라이언트 또는 자신을 위한 새 애플리케이션을 구축하고 애플리케이션을 좋아하는 좋은 사용자 기반을 확보했다고 가정해 보겠습니다. 사용자로부터 피드백을 수집하고 개발자에게 가서 새로운 기능을 빌드하고 애플리케이션을 배포할 준비를 하도록 요청합니다. 준비가 되면 전체 응용 프로그램을 중지하고 새 버전을 배포하거나 수동 개입 없이 사용자에게 새 릴리스를 푸시하는 지루한 작업을 모두 수행할 제로 다운타임 CI/CD 배포 파이프라인을 구축할 수 있습니다.

이 기사에서는 인프라 오케스트레이터로 Terraform을 사용하여 AWS 클라우드에서 Node.js로 구축된 3계층 웹 애플리케이션의 지속적인 배포 파이프라인을 가질 수 있는 방법에 대해 정확히 설명합니다. 지속적 배포 부분에 Jenkins를 사용하고 코드베이스를 호스팅하기 위해 Bitbucket을 사용할 것입니다.

코드 저장소

여기에서 코드를 찾을 수 있는 데모 3계층 웹 애플리케이션을 사용할 것입니다.

리포지토리에는 웹 및 API 계층 모두에 대한 코드가 포함되어 있습니다. 웹 모듈이 내부적으로 데이터베이스에서 현재 시간에 대한 정보를 가져와 웹 계층으로 반환하는 API 계층의 끝점 중 하나를 호출하는 간단한 응용 프로그램입니다.

리포지토리의 구조는 다음과 같습니다.

  • API: API 계층용 코드
  • 웹: 웹 레이어용 코드
  • Terraform: Terraform 을 사용한 인프라 오케스트레이션용 코드
  • Jenkins: CI/CD 파이프라인에 사용되는 Jenkins 서버용 인프라 오케스트레이터용 코드입니다.

이제 배포해야 할 사항을 이해했으므로 AWS에 이 애플리케이션을 배포하기 위해 해야 할 일에 대해 논의한 다음 해당 부분을 CI/CD 파이프라인의 일부로 만드는 방법에 대해 논의하겠습니다.

베이킹 이미지

인프라 오케스트레이터에 Terraform을 사용하고 있으므로 배포하려는 각 계층 또는 애플리케이션에 대해 미리 구운 이미지를 갖는 것이 가장 합리적입니다. 그리고 이를 위해 우리는 Hashicorp의 다른 제품, 즉 Packer를 사용할 것입니다.

Packer는 AWS에 배포하는 데 사용할 Amazon 머신 이미지 또는 AMI를 구축하는 데 도움이 되는 오픈 소스 도구입니다. EC2, VirtualBox, VMware 등과 같은 다양한 플랫폼용 이미지를 빌드하는 데 사용할 수 있습니다.

다음은 Packer 구성 파일( terraform/packer-ami-api.json )을 사용하여 API 계층에 대한 AMI를 생성하는 방법에 대한 스니펫입니다.

 { "builders": [{ "type": "amazon-ebs", "region": "eu-west-1", "source_ami": "ami-844e0bf7", "instance_type": "t2.micro", "ssh_username": "ubuntu", "ami_name": "api-instance {{timestamp}}" }], "provisioners": [ { "type": "shell", "inline": ["mkdir api", "sudo apt-get update", "sudo apt-get -y install npm nodejs-legacy"], "pause_before": "10s" }, { "type": "file", "source" : "../api/", "destination" : "api" }, { "type": "shell", "inline": ["cd api", "npm install"], "pause_before": "10s" } ] }

그리고 AMI를 생성하려면 다음 명령을 실행해야 합니다.

 packer build -machine-readable packer-ami-api.json

이 기사 뒷부분의 Jenkins 빌드에서 이 명령을 실행할 것입니다. 비슷한 방식으로 웹 레이어에도 Packer 구성 파일( terraform/packer-ami-web.json )을 사용할 것입니다.

위의 Packer 구성 파일을 살펴보고 무엇을 하려는지 이해해 보겠습니다.

  1. 앞서 언급했듯이 Packer는 많은 플랫폼용 이미지를 빌드하는 데 사용할 수 있으며 AWS에 애플리케이션을 배포하기 때문에 시작하기 가장 쉬운 빌더인 "amazon-ebs" 빌더를 사용할 것입니다.
  2. 구성의 두 번째 부분은 이미지를 구성하는 데 사용할 수 있는 스크립트 또는 코드 블록과 유사한 프로비저닝 도구 목록을 가져옵니다.
    • 1단계 에서는 실행하려는 명령 집합인 inline 속성을 사용하여 API 폴더를 만들고 이미지에 Node.js를 설치하기 위해 셸 프로비저닝을 실행합니다.
    • 2단계 에서는 파일 프로비저닝 도구를 실행하여 API 폴더에서 인스턴스로 소스 코드를 복사합니다.
    • 3단계 에서는 셸 프로비저닝 도구를 다시 실행하지만 이번에는 스크립트 속성을 사용하여 실행해야 하는 명령이 포함된 파일(terraform/scripts/install_api_software.sh)을 지정합니다.
    • 4단계 에서는 다음 단계에서 설치되는 Cloudwatch에 필요한 인스턴스에 구성 파일을 복사합니다.
    • 5단계 에서는 셸 프로비저닝을 실행하여 AWS Cloudwatch 에이전트를 설치합니다. 이 명령에 대한 입력은 이전 단계에서 복사한 구성 파일입니다. 기사 뒷부분에서 Cloudwatch에 대해 자세히 설명합니다.

따라서 본질적으로 Packer 구성에는 원하는 빌더에 대한 정보와 원하는 이미지 구성 방법에 따라 임의의 순서로 정의할 수 있는 프로비저닝 도구 세트가 포함되어 있습니다.

Jenkins 지속적 배포 설정

다음으로 CI/CD 파이프라인에 사용할 Jenkins 서버를 설정하는 방법을 살펴보겠습니다. 우리는 이것을 설정하기 위해 Terraform과 AWS도 사용할 것입니다.

Jenkins 설정을 위한 Terraform 코드는 jenkins/setup 폴더 안에 있습니다. 이 설정에 대한 몇 가지 흥미로운 점을 살펴보겠습니다.

  1. AWS 자격 증명: AWS 액세스 키 ID와 보안 액세스 키를 Terraform AWS 공급자( instance.tf )에 제공하거나 자격 증명 파일의 위치를 ​​AWS 공급자의 shared_credentials_file 속성에 제공할 수 있습니다.
  2. IAM 역할: Jenkins 서버에서 Packer 및 Terraform을 실행할 것이므로 AWS에서 S3, EC2, RDS, IAM, 로드 밸런싱 및 자동 크기 조정 서비스에 액세스하게 됩니다. 따라서 이러한 서비스에 액세스하기 위해 Packer 및 Terraform용 Jenkins에 대한 자격 증명을 제공하거나 Jenkins 인스턴스를 생성하는 데 사용할 IAM 프로필( iam.tf )을 생성할 수 있습니다.
  3. Terraform 상태: Terraform은 파일의 어딘가에 인프라 상태를 유지해야 하며 S3( backend.tf )를 사용하여 그곳에서 유지하기만 하면 다른 동료와 협업할 수 있고 누구든지 상태를 변경하고 배포할 수 있습니다. 원격 위치에서 유지 관리됩니다.
  4. 공개/개인 키 쌍: 인스턴스와 함께 키 쌍의 공개 키를 업로드해야 Jenkins 인스턴스가 가동되면 ssh할 수 있습니다. Terraform 변수를 사용하여 공개 키의 위치를 ​​지정하는 aws_key_pair 리소스( key.tf )를 정의했습니다.

Jenkins 설정 단계:

1단계: Terraform의 원격 상태를 유지하려면 Terraform에서 사용할 수 있는 버킷을 S3에 수동으로 생성해야 합니다. 이것은 Terraform 외부에서 수행되는 유일한 단계입니다. 아래 명령을 실행하여 AWS 자격 증명을 지정하기 전에 AWS configure 를 실행했는지 확인하십시오.

 aws s3api create-bucket --bucket node-aws-jenkins-terraform --region eu-west-1 --create-bucket-configuration LocationConstraint=eu-west-1

2단계: terraform init 를 실행합니다. 그러면 상태가 초기화되고 S3에 저장되도록 구성하고 AWS 공급자 플러그인을 다운로드합니다.

3단계: terraform apply 를 실행합니다. 이렇게 하면 모든 Terraform 코드를 확인하고 계획을 만들고 이 단계가 완료된 후 생성될 리소스 수를 표시합니다.

4단계: yes 를 입력하면 이전 단계에서 모든 리소스 생성이 시작됩니다. 명령이 완료되면 Jenkins 서버의 공용 IP 주소를 얻을 수 있습니다.

5단계: 개인 키를 사용하여 Jenkins 서버에 SSH로 접속합니다. ubuntu 는 AWS EBS 지원 인스턴스의 기본 사용자 이름입니다. terraform apply 명령에서 반환된 IP 주소를 사용합니다.

 ssh -i mykey [email protected]

6단계: http://34.245.4.73:8080 으로 이동하여 Jenkins 웹 UI를 시작합니다. 비밀번호는 /var/lib/jenkins/secrets/initialAdminPassword 에서 찾을 수 있습니다.

7단계: "추천 플러그인 설치"를 선택하고 Jenkins용 관리자를 생성합니다.

Jenkins와 Bitbucket 간의 CI 파이프라인 설정

  1. 이를 위해 Jenkins에 Bitbucket 플러그인을 설치해야 합니다. Jenkins 관리 → 플러그인 관리 로 이동하고 사용 가능한 플러그인 에서 Bitbucket 플러그인을 설치합니다.
  2. Bitbucket repo 측에서 Settings → Webhooks 로 이동하여 새 webhook을 추가합니다. 이 후크는 저장소의 모든 변경 사항을 Jenkins로 보내고 파이프라인을 트리거합니다.
    Bitbucker를 통해 Jenkins 지속적 배포에 웹훅 추가

이미지 굽기/구축을 위한 Jenkins 파이프라인

  1. 다음 단계는 Jenkins에서 파이프라인을 만드는 것입니다.
  2. 첫 번째 파이프라인은 Packer를 사용하여 애플리케이션의 AMI를 빌드하는 데 사용되는 Freestyle 프로젝트입니다.
  3. Bitbucket 리포지토리에 대한 자격 증명과 URL을 지정해야 합니다.
    bitbucket에 자격 증명 추가
  4. 빌드 트리거를 지정합니다.
    빌드 트리거 구성
  5. 앱 모듈용 AMI를 빌드하기 위한 빌드 단계와 웹 모듈용 AMI를 빌드하기 위한 빌드 단계의 두 가지 빌드 단계를 추가합니다.
    AMI 빌드 단계 추가
  6. 이 작업이 완료되면 Jenkins 프로젝트를 저장할 수 있으며 이제 Bitbucket 리포지토리에 무엇이든 푸시하면 Jenkins에서 새 빌드를 트리거하여 AMI를 생성하고 해당 이미지의 AMI 번호가 포함된 Terraform 파일을 푸시합니다. 빌드 단계의 마지막 두 줄에서 볼 수 있는 S3 버킷.
 echo 'variable "WEB_INSTANCE_AMI" { default = "'${AMI_ID_WEB}'" }' > amivar_web.tf aws s3 cp amivar_web.tf s3://node-aws-jenkins-terraform/amivar_web.tf

Terraform 스크립트를 트리거하는 Jenkins 파이프라인

이제 API 및 웹 모듈용 AMI가 있으므로 전체 애플리케이션을 설정하기 위해 Terraform 코드를 실행하는 빌드를 트리거하고 나중에 이 파이프라인이 서비스 중단 시간 없이 변경 사항을 배포하도록 하는 Terraform 코드의 구성 요소를 살펴보겠습니다.

  1. 애플리케이션을 배포하기 위해 Terraform 코드를 실행할 또 다른 자유형 Jenkins 프로젝트 nodejs-terraform 을 만듭니다.
  2. 먼저 전역 자격 증명 도메인에 "비밀 텍스트" 유형 자격 증명을 생성하여 Terraform 스크립트에 대한 입력으로 사용할 것입니다. Terraform 및 Git 내부에서 RDS 서비스의 비밀번호를 하드 코딩하고 싶지 않기 때문에 Jenkins 자격 증명을 사용하여 해당 속성을 전달합니다.
    Terraform ci cd와 함께 사용할 비밀 만들기
  3. 다른 프로젝트와 유사한 자격 증명 및 URL을 정의해야 합니다.
  4. 빌드 트리거 섹션에서는 이전 프로젝트가 완료될 때 이 프로젝트가 시작되도록 이 프로젝트를 다른 프로젝트와 연결합니다.
    프로젝트를 함께 연결
  5. 그런 다음 바인딩을 사용하여 이전에 프로젝트에 추가한 자격 증명을 구성할 수 있으므로 빌드 단계에서 사용할 수 있습니다.
    바인딩 구성
  6. 이제 이전 프로젝트에서 S3에 업로드한 Terraform 스크립트 파일( amivar_api.tfamivar_web.tf )을 다운로드한 다음 Terraform 코드를 실행하여 AWS에서 전체 애플리케이션을 빌드하는 빌드 단계를 추가할 준비가 되었습니다.
    빌드 스크립트 추가

모든 것이 올바르게 구성된 경우 이제 Bitbucket 리포지토리에 코드를 푸시하면 첫 번째 Jenkins 프로젝트가 트리거되고 두 번째 프로젝트가 트리거되고 애플리케이션이 AWS에 배포되어야 합니다.

AWS용 Terraform 제로 다운타임 구성

이제 이 파이프라인이 다운타임 없이 코드를 배포하도록 하는 Terraform 코드에 대해 논의해 보겠습니다.

첫 번째는 Terraform이 플래그로 create_before_destroy 옵션이 있는 리소스에 대해 이러한 수명 주기 구성 블록을 제공한다는 것입니다.

이제 aws_autoscaling_groupaws_launch_configuration 리소스에서 이 기능을 활용합니다. 따라서 aws_launch_configuration 은 프로비저닝해야 하는 EC2 인스턴스 유형과 해당 인스턴스에 소프트웨어를 설치하는 방법을 구성하고 aws_autoscaling_group 리소스는 AWS Autoscaling 그룹을 제공합니다.

여기서 흥미로운 점은 Terraform의 모든 리소스가 고유한 이름과 유형 조합을 가져야 한다는 것입니다. 따라서 새 aws_autoscaling_groupaws_launch_configuration 에 대해 다른 이름이 없으면 현재 이름을 삭제할 수 없습니다.

Terraform은 aws_launch_configuration 리소스에 name_prefix 속성을 제공하여 이 제약 조건을 처리합니다. 이 속성이 정의되면 Terraform은 모든 aws_launch_configuration 리소스에 고유한 접미사를 추가한 다음 해당 고유 이름을 사용하여 aws_autoscaling_group 리소스를 생성할 수 있습니다.

terraform/autoscaling-api.tf 에서 위의 모든 코드를 확인할 수 있습니다.

 resource "aws_launch_configuration" "api-launchconfig" { name_prefix = "api-launchconfig-" image_ instance_type = "t2.micro" security_groups = ["${aws_security_group.api-instance.id}"] user_data = "${data.template_file.api-shell-script.rendered}" iam_instance_profile = "${aws_iam_instance_profile.CloudWatchAgentServerRole-instanceprofile.name}" connection { user = "${var.INSTANCE_USERNAME}" private_key = "${file("${var.PATH_TO_PRIVATE_KEY}")}" } lifecycle { create_before_destroy = true } } resource "aws_autoscaling_group" "api-autoscaling" { name = "${aws_launch_configuration.api-launchconfig.name}-asg" vpc_zone_identifier = ["${aws_subnet.main-public-1.id}"] launch_configuration = "${aws_launch_configuration.api-launchconfig.name}" min_size = 2 max_size = 2 health_check_grace_period = 300 health_check_type = "ELB" load_balancers = ["${aws_elb.api-elb.name}"] force_delete = true lifecycle { create_before_destroy = true } tag { key = "Name" value = "api ec2 instance" propagate_at_launch = true } }

그리고 가동 중지 시간이 없는 배포의 두 번째 과제는 새 배포가 요청을 수신할 준비가 되었는지 확인하는 것입니다. 일부 상황에서는 새 EC2 인스턴스를 배포하고 시작하는 것만으로는 충분하지 않습니다.

이 문제를 해결하기 위해 aws_launch_configuration 에는 기본 AWS autoscaling user_data 속성을 지원하는 user_data 속성이 있습니다. 이 속성을 사용하면 Autoscaling 그룹의 일부로 새 인스턴스를 시작할 때 실행할 스크립트를 전달할 수 있습니다. 이 예에서는 앱 서버의 로그를 추적하고 시작 메시지가 있을 때까지 기다립니다. HTTP 서버를 확인하고 언제 작동하는지 확인할 수도 있습니다.

 until tail /var/log/syslog | grep 'node ./bin/www' > /dev/null; do sleep 5; done

이와 함께 aws_autoscaling_group 리소스 수준에서 ELB 검사를 활성화할 수도 있습니다. 이렇게 하면 Terraform이 이전 인스턴스를 파괴하기 전에 ELB 검사를 통과하도록 새 인스턴스가 추가되었는지 확인할 수 있습니다. API 계층에 대한 ELB 검사는 다음과 같습니다. /api/status 엔드포인트가 성공을 반환하는지 확인합니다.

 resource "aws_elb" "api-elb" { name = "api-elb" subnets = ["${aws_subnet.main-public-1.id}"] security_groups = ["${aws_security_group.elb-securitygroup.id}"] listener { instance_port = "${var.API_PORT}" instance_protocol = "http" lb_port = 80 lb_protocol = "http" } health_check { healthy_threshold = 2 unhealthy_threshold = 2 timeout = 3 target = "HTTP:${var.API_PORT}/api/status" interval = 30 } cross_zone_load_balancing = true connection_draining = true connection_draining_timeout = 400 tags { Name = "my-elb" } }

요약 및 다음 단계

그래서 이것은 우리를 이 기사의 끝으로 이끕니다. 지금쯤이면 Jenkins 배포 및 Terraform 모범 사례를 사용하여 다운타임이 없는 CI/CD 파이프라인으로 이미 애플리케이션을 배포하고 실행하거나 이 영역을 탐색하고 가능한.

이 기사에서 사용 중인 배포 전략은 Blue-Green 배포라고 하며, 여기에서 현재 설치(Blue)는 새 버전(Green)을 배포 및 테스트하는 동안 라이브 트래픽을 수신한 다음 새 버전이 출시되면 교체합니다. 모두 준비되었습니다. 이 전략 외에도 애플리케이션을 배포하는 다른 방법이 있습니다. 이 문서는 배포 전략 소개에서 잘 설명되어 있습니다. 이제 Jenkins 파이프라인을 구성하는 것처럼 간단하게 다른 전략을 적용할 수 있습니다.

또한 이 기사에서는 API, 웹 및 데이터 계층의 모든 새로운 변경 사항이 호환되므로 새 버전이 이전 버전과 대화하는 것에 대해 걱정할 필요가 없다고 가정했습니다. 그러나 실제로는 항상 그렇지 않을 수도 있습니다. 이 문제를 해결하려면 새 릴리스/기능을 설계하는 동안 항상 이전 버전과의 호환성 계층에 대해 생각하십시오. 그렇지 않으면 해당 상황을 처리하기 위해 배포를 조정해야 합니다.

이 배포 파이프라인에서도 통합 테스트가 누락되었습니다. 테스트를 거치지 않고 최종 사용자에게 릴리스되는 것을 원하지 않기 때문에 이러한 전략을 자신의 프로젝트에 적용할 때가 오면 반드시 염두에 두어야 할 사항입니다.

Terraform이 작동하는 방식과 이 기술을 사용하여 AWS에 배포하는 방법에 대해 자세히 알아보고 싶다면 동료 Toptaler Radoslaw Szalski가 Terraform을 설명하고 다중 구성에 필요한 단계를 보여주는 Terraform AWS Cloud: Sane Infrastructure Management 를 추천합니다. -팀을 위한 환경 및 프로덕션 준비가 된 Terraform 설정

관련: Terraform 대 CloudFormation: 최종 가이드