BLOG

In-House 구축 에이전트에서 AWS CodeDeploy와 AWS CodePipeline으로 마이그레이션 하기
작성일: 2018-04-11

저는 구축 및 개발 경험을 전문으로 하는 회사인 Woot 의 개발자, Charles Fort입니다. Woot는 원래 일일 거래 웹 사이트로서 시작하였고, 2004년에 설립되어 2010년 아마존에 인수되었습니다.  https://www.woot.com

 

우리가 개발한 구축 툴인 Troop에서 웹 프론트엔드의 구축 부분을 AWS CodeDeploy 및 AWS Codepipeline으로 이동했습니다. 이 마이그레이션에서는 CodeDeploy 에이전트가 설치된 새로운 고객용 EC2 웹 서버를 런칭하고 AWS CloudFormation을 사용하여 CodeDeploy및 Codepipeline 리소스를 생성 및 관리했습니다. 마이그레이션이 완료된 후에 우리는 구현 중 발생하는 HTTP 500 오류가 최대 50%까지 감소했음을 발견했습니다.

 

이 블로그 포스트에서는 다음 내용에 대해 알려드리겠습니다.

 

  • 왜 우리가 AWS 구축 툴을 선택했는지
  • Woot 시스템의 아키텍쳐 다이어그램
  • 마이그레이션 프로젝트의 개요
  • 마이그레이션 프로젝트의 결과

 

The Old and Busted

우리는 사내 구축 시스템을 자동화를 기반으로 구축할 수 있고, 소유하거나 유지할 필요가 없는 시스템으로 교체하고 싶었습니다. 우리는 이미 충분히 좋지 않은 건축시스템을 소유하고 유지하고 있었습니다. 우리는 구축 파이프 라인을 위해 추가 인프라를 운영하고 싶지 않았습니다.

 

간단히 말해, 우리는 클라우드 서비스를 원했습니다. 모든 인프라가 AWS에 있기 때문에, CodeDeploy가 우리의 구축 에이전트를 대체하기에 적합한 환경이었습니다. CodePipeline은 클라우드의 자동화 조정 역할을 하며, 우리의 마법사는 CodeDeploy에 무엇을 언제 할지를 알려 줍니다.

 

아키텍쳐 개요

여기 Woot 웹 프론트 엔드의 아키텍처를 공유 드립니다.

 

 

 

프로젝트 개요

우리의 프로젝트는 5개의 웹 프론트 엔드를 마이그레이션하는 것이었고 이는 매일 평균 1200만개의 요청을 처리해야 하는 것이었습니다. 물론 저희 고객들을 위해 사이트를 계속 운영하면서 말이죠.

그래서 다음과 같이 진행하였습니다.

 

  • 몇 가지 배포 스크립트를 작성했습니다.
  • CodeDeploy를 지원하는 새로운 EC2 웹 서버 군을 런칭했습니다.
  • CloudFormation에 정의된 CodeDeploy 및 CodePipeline 구성을 위한 구축 파이프 라인을 생성했습니다.
  • 새로운 Fleet 를 실시간 상황에 도입했습니다.

 

배포 스크립트

이전 배포 시스템은 웹 서버를 중지하거나 시작하지 않았습니다. 대신 서버가 실행되는 동안 서버 아래에서 구축물을 교체하려고 했습니다. 그건 정말 좋지 않은 생각이었습니다.

 

우리는 몇개의 배포 스크립트를 Powershell에 작성했는데, 이 스크립트는 CodeDeploy가 실행하여 IIS웹 서버를 중지 및 시작하기 위한 것입니다. 이러한 스크립트는 고객 트래픽을 처리하는 동안 웹 서버를 중지시키고 싶지 않았으므로 CodeDeploy의 Elastic Load Balancing (ELB)지원과 함께 작동합니다.

 

New Fleet
우리의 Fleet 은 Amazon EC2에서 운영되고 있기 때문에 CodeDeploy 에이전트가 이미 설치된 상태에서 Web Fleet 에 맞는 AMI(Amazon  Machine Image)를 구축했습니다. Fleet 의 관점에서 보면 이런 일은 우리가 해야 할 일의 대부분을 차지합니다. 에이전트가 설치된 CodeDeploy는 우리의 배포 스크립트를 사용하여 웹 프로젝트를 구축할 수 있습니다.

 

AWS Cloudformation Deployment Pipeline

우리는 구축하려는 각 웹 프로젝트에 대해 구축 파이프 라인과 몇 개의 CodeDeploy 구성 (CodeDeploy 애플리케이션와 적어도 하나의 CodeDeploy 구축 그룹)이 필요했기 때문에 이 구성을 위하여 AWS CloudFormation을 사용하기로 했습니다. 우리의 구축 시스템( TeamCity)은 버젼 관리 시스템으로부터 읽고 아마존 S3에 쓸 수 있습니다. TeamCity에서 간단한 빌드를 수행하여 AWS CloudFormation 템플릿을 S3으로 밀어 넣어 AWS Cloutformation에 배포되는 파이프 라인을 촉진했습니다. 이것이 CodePipeline과 CodeDeploy 리소스를 생성합니다. 이제 우리는 인프라 변경에 대해 코드 리뷰를 할 수 있습니다. 코드 변경과 마찬가지로 시간 변화에 따른 인프라 변경도 추적할 수 있습니다.

 

실시간 상황

우리의 웹 플릿은 Classic Load Balancer 의 뒷단에서 운영 중에 있습니다. CodeDeploy를 선택하면, 우리는 새로운 ELB기능을 사용할 수 있습니다. 예를 들어 elastic load balancer를 통해 CodeDeploy는 배치되어있는 동안에 인터넷 트래픽이 인스턴스로 라우팅 되지 않도록 할 수 있습니다. 해당 인스턴스에 대한 배포가 완료되면 인스턴스를 트래픽에 사용할 수 있게 됩니다.

우리는  CodeDeploy 에이전트와 함께 새로운 호스트를 런칭했고 ELB 지원 기능 없이 배포했습니다. 그리고 나서 우리는 천천히, 수동으로 우리의 Fleet에 도입하여 통계를 모니터링합니다. 우리는 새 시스템을 도입한 후에 기존 시스템을 로드 밸런서에서 천천히 제거하여 CodeDeploy가 Fleet을 완벽하게 지원할 수 있도록 했습니다. 이렇게 마이그레이션을 수행하면 사이트에 다운 타임이 발생하지 않습니다.

한가지 재미 있는 사항이 있습니다. 우리가 로드 밸런서에 새 Fleet의 2/3를 배치했을 때,  CodeDeploy 구축을 유발했지만, 이번에는 ELB 기능을 켜놓았습니다. 이로 인해 CodeDeploy는 나머지 시스템을 로드 밸런서에 배치했으며, 설정해야할 버튼이 조금 덜 있었습니다.

 

AWS CloudFormation example

아래는 우리의 웹 프로젝트를 위해 AWS 구성을 관리할 때 쓰는 AWS CloudFormation 템플릿의 간단한 예시입니다. 이는 구축 파이프라인에 배치되었으며 이는 웹프로젝트 자체와 유사합니다.

 

Parameters:

  CodePipelineBucket:

    Type: String

  CodePipelineRole:

    Type: String

  CodeDeployRole:

    Type: String

  CodeDeployBucket:

    Type: String

Resources:

 

  ### Woot.Example deployment configuration ###

  ExampleDeploymentConfig:

    Type: ‘AWS::CodeDeploy::DeploymentConfig’

    Properties:

      MinimumHealthyHosts:

        Type: FLEET_PERCENT

        Value: ’66’ # Let’s keep 2/3 of the fleet healthy at any point

 

  #Woot.Example CodeDeploy application

  WootExampleApplication:

    Type: “AWS::CodeDeploy::Application”

    Properties:

      ApplicationName: “Woot.Example”

 

  #Woot.Example CodeDeploy deployment groups

  WootExampleDeploymentGroup:

    DependsOn: “WootExampleApplication”

    Type: “AWS::CodeDeploy::DeploymentGroup”

    Properties:

      ApplicationName: “Woot.Example”

      DeploymentConfigName: !Ref “ExampleDeploymentConfig” # use the deployment configuration we created

      DeploymentGroupName: “Woot.Example.Main”

      AutoRollbackConfiguration:

        Enabled: true

        Events:

          – DEPLOYMENT_FAILURE # this makes the deployment rollback when the deployment hits the failure threshold

          – DEPLOYMENT_STOP_ON_REQUEST # this makes the deployment rollback if you hit the stop button

      LoadBalancerInfo:

        ElbInfoList:

          – Name: “WootExampleInternal” # this is the ELB the hosts live in, they will be added and removed from here

      DeploymentStyle:

        DeploymentOption: “WITH_TRAFFIC_CONTROL” # this tells CodeDeploy to actually add/remove the hosts from the ELB

      Ec2TagFilters:

        –

          Key: “Name”

          Value: “exampleweb*” # deploy to all machines named like exampleweb001.yourdomain.com, etc

          Type: “KEY_AND_VALUE”

      ServiceRoleArn: !Sub “arn:aws:iam::${AWS::AccountId}:role/${CodeDeployRole}” # this is the IAM role CodeDeploy in your account should use

 

  #Woot.Example CodePipeline

  WootExampleDeploymentPipeline:

    DependsOn: “WootExampleDeploymentGroup”

    Type: “AWS::CodePipeline::Pipeline”

    Properties:

      RoleArn: !Sub “arn:aws:iam::${AWS::AccountId}:role/${CodePipelineRole}” # this is the IAM role CodePipeline in your account should use

      Name: “Woot.Example” # name of the pipeline

      ArtifactStore:

        Type: S3

        Location: !Ref “CodePipelineBucket” # the bucket CodePipeline uses in your account to shuffle artifacts around

      Stages:

        –

          Name: Source # one S3 source stage

          Actions:

            –

              Name: SourceAction

              ActionTypeId:

                Category: Source

                Owner: AWS

                Version: 1

                Provider: S3

              OutputArtifacts:

                –

                  Name: SourceOutput

              Configuration:

                S3Bucket: !Ref “CodeDeployBucket” # the S3 bucket your builds go into (needs to be versioned)

                S3ObjectKey: “Woot.Example/Woot.Example-Release.zip” # build artifact path for this application

              RunOrder: 1

        –

          Name: Deploy # one deploy stage that triggers the CodeDeploy deployment

          Actions:

            –

              Name: DeployAction

              ActionTypeId:

                Category: Deploy

                Owner: AWS

                Version: 1

                Provider: CodeDeploy

              InputArtifacts:

                –

                  Name: SourceOutput

              Configuration:

                ApplicationName: “Woot.Example”

                DeploymentGroupName: “Woot.Example.Main”

              RunOrder: 2

 

Appspec.yml example

이것은 우리의 주요 웹 프론트 엔드(Woot.Web.Retail)에 사용되는 appspec.yml파일입니다. appspec.yml파일은 CodeDeploy가 어디에 파일을 저장하고 배포 스크립트를 실행해야 하는지를 알려 줍니다.

 

version: 0.0

os: windows

files:

  – source: /

    destination: C:\Woot\Woot.Web.Retail\Initial

 

hooks:

  BeforeInstall:

    – location: bin\Deployment\stopWebsite.ps1

    – location: bin\Deployment\clearWebsiteDeployment.ps1

  AfterInstall:

    – location: bin\Deployment\startWebsite.ps1

 

배포 스크립트

서버 런칭 인프라에서는 빌드 결과의 초기 배치에 CodeDeploy를 사용하지 않으므로 CodeDeploy가 파일을 덮어쓰지 않습니다. (서비스는 이에 대해 전혀 알지 못함.) 이는 장점 혹은 단점이 될 수 있습니다: 장/단점이 될 수 있는 이유는 CodeDeploy는 파일을 덮어쓰지 않기 때문에 좋고, clearWebsiteDeployment.ps1: 과 같은 구축 스크립트를 준비해야하기 때문에 나쁩니다.

 

 

 

 

 

 

우리의 스크립트에 CodeDeploy 환경 변수($env:APPLICATION_NAME)가 사용되었음을 알 수 있습니다. CodeDeploy 응용 프로그램의 이름은 IIS웹 사이트의 이름이기도 합니다. 이렇게 하면 여러 웹 사이트에 동일한 배포 스크립트를 사용할 수 있습니다.

 

The new hotness

지금 우리는 CodeDeploy를 운영하고 있고 그 결과에 매우 만족합니다. 예전 구축 툴인 Troop은 우리에게 릴리스하는 방법에 대한 통제권을 주지 않았습니다. 이제 인스턴스 레벨 별로 구축을 확인할 수 있으며, 자동화를 통해 얻을 수 있는 이점이 큽니다.

마이그레이션 후 배포하는 동안 고객에게 제공된 HTTP500 오류가 50% 감소했습니다. 우리는 배치하는 동안 1시간 분량의 시간을 검토하여 CodeDeploy로 마이그레이션 하기 전과 후의 평균 카운트를 비교했습니다. 이 수치들은 우리의 예전 구축 체계가 심하게 망가졌다는 것을 보여 줍니다.

 

이 그래프에서는 시간 경과에 따른 배포 오류를 요약하여 보여 주고 CodeDeploy를 기존 사내 구축 에이전트(Troop)와 비교하여 보여 줍니다.

 

 

우리는 AWS 구축 도구에 전체 계정 크로스 어카운트 프로세스를 구현할 계획입니다. 하나의 AWS 계정과 우리의 다양한 환경에서 CodeDeploy를 제어하고, 테스트를 트리거하고, 진행하는 동안 다음 환경으로 이동할 것입니다. 우리가 직접 만든 툴로 그런 걸 만드는 건 힘든 일이죠. CodeDeploy, CodePipeline 및 AWS  CloudFormation 덕분에 우리는 이제 더 편하게 살 수 있습니다!

 

원문 URL: https://aws.amazon.com/ko/blogs/devops/migrating-from-an-in-house-deployment-agent-to-aws-codedeploy-and-aws-codepipeline/

** 메가존 TechBlog는 AWS BLOG 영문 게재글중에서 한국 사용자들에게 유용한 정보 및 콘텐츠를 우선적으로 번역하여 내부 엔지니어 검수를 받아서, 정기적으로 게재하고 있습니다. 추가로 번역및 게재를 희망하는 글에 대해서 관리자에게 메일 또는 SNS페이지에 댓글을 남겨주시면, 우선적으로 번역해서 전달해드리도록 하겠습니다.