안녕하세요, 렌딧 Engineering 팀의 Sam입니다.

배포를 배포답게 시리즈 마지막 글에서는 앞서 소개했던 문제점 중 5번을 해결한 방법에 대해 적어보려고 합니다.

이번 글과 AWS CodeDeploy > Register On-Premises Instance 문서를 같이 읽으시면 좋습니다.

문제점 5 - IDC 서버

렌딧 서버는 주로 AWS EC2를 사용합니다. 하지만, 대한민국에서는 법률 규제로 인해 주민등록번호 등 민감한 개인 식별정보나 신용 정보는 AWS에 저장할 수 없습니다. 또한, 신용정보평가사 및 신탁 은행 등과의 시스템 연동 역시 전용 회선이 필요합니다. 따라서, 렌딧은 AWS 외에도 IDC의 서버를 사용하고 있습니다. IDC 서버는 CodeDeploy을 통한 배포 설정이 되어있지 않아 mvn packagescp 등의 명령어를 직접 손으로 입력하여 배포하였습니다.

이 귀찮은 문제를 해결하기 위해 IDC에도 CodeDeploy를 도입하였습니다. CodeDeploy agent를 설치하고, 적절한 권한을 가진 사용자의 세션으로 로그인해놓으면 해당 region의 EC2와 같이 CodeDeploy를 사용할 수 있습니다. CodeDeploy agent는 일정 시간마다 AWS에서 polling 하는 방식으로 동작하므로, 대상 서버가 public ip를 가지고 있지 않아도, inbound traffic이 모두 차단되어 있어도 outbound 443/tcp port만 열려있으면 사용할 수 있습니다.

AWS 도움말에서는 다음과 같은 3가지 등록 방법을 소개하고 있습니다.

  • register 명령을 사용하는 방법: 편리한 자동화, 한 개의 instance를 등록하는 데 적합합니다.
  • register-on-premises-instance 명령을 사용하는 방법: 적은 instance를 등록하는 데 적합하며 instance 당 1개의 user 필요합니다.
  • register-on-premises-instance 명령을 STS (Security Token Service)와 함께 사용하는 방법: 많은 instance를 등록하는 데 적합합니다.
    렌딧에서는 마지막 방법을 사용하여 IDC에 CodeDeploy를 구성하였습니다. 이 글에서도 마지막 방법으로 진행합니다.

예시로 serial-ABCDEFG이라는 서버를 ap-northeast-2 지역에 등록시키고, idc-01 태그를 붙여보겠습니다. 서버 식별자를 정할 때는 장비의 사용 목적이 변경될 수 있으므로 admin-01이나 web-01과 같이 정하는 것보다 변하지 않는 값을 사용하는 것이 좋습니다. 서버 식별자는 Name 태그와는 달리 한 번 정하면 deregister 후 다시 등록하지 않으면 변경할 수 없으며, deregister는 몇 시간 이상 소요되기 때문입니다.

User, Role 및 Policy 생성

먼저, 배포에 사용할 Role과 Policy를 생성합니다. 이미 CodeDeploy가 EC2에 설정되어 있다면 Role을 그대로 사용할 수 있습니다.

  1. 다음과 같은 Policy를 생성합니다. 이름은 CodeDeployIDC-Permission으로 하겠습니다.
    1
    {
    2
      "Version": "2012-10-17",
    3
      "Statement": [{
    4
        "Action": [
    5
          "s3:Get*",
    6
          "s3:List*"
    7
        ],
    8
        "Effect": "Allow",
    9
        "Resource": "*"
    10
      }]
    11
    }
  2. 위에서 생성한 CodeDeployIDC-Permission을 가진 Role을 생성합니다. 이름은 CodeDeployIDC-Instance-Profile로 하겠습니다.
  3. 생성된 Role의 ARN이 arn:aws:iam::12345678901:role/CodeDeploy-Instance-Profile이라 가정하겠습니다.
  4. 이번에는 사용자를 생성하는데 필요한 Policy를 생성합니다. 이름은 CodeDeployIDC-User-Profile로 하겠습니다.
    1
    {
    2
      "Version": "2012-10-17",
    3
      "Statement": [{
    4
        "Effect": "Allow",
    5
        "Action": [
    6
          "codedeploy:*",
    7
          "iam:CreateAccessKey",
    8
          "iam:CreateUser",
    9
          "iam:DeleteAccessKey",
    10
          "iam:DeleteUser",
    11
          "iam:DeleteUserPolicy",
    12
          "iam:ListAccessKeys",
    13
          "iam:ListUserPolicies",
    14
          "iam:PutUserPolicy",
    15
          "iam:GetUser",
    16
          "tag:GetTags",
    17
          "tag:GetResources",
    18
          "s3:*",
    19
          "sts:AssumeRole"
    20
        ],
    21
        "Resource": "*"
    22
      }]
    23
    }
  5. 위에서 생성한 CodeDeployIDC-User-Profile을 가진 사용자를 생성합니다. 이름은 CodeDeployIDC-User로 하겠습니다.
  6. Access key를 발급받습니다. 이 사용자는 on-premises instance를 등록하고 STS 토큰을 요청하는 데 사용됩니다.
  7. CodeDeployIDC-Instance-Profile의 trust relationship에 생성한 CodeDeployIDC-User를 추가합니다.

CodeDeploy Agent 및 라이브러리 설치

CodeDeploy agent와 on-premises instance 등록 및 세션 발급에 필요한 각종 도구를 설치합니다.

  1. ruby 및 wget 설치: yum install ruby wget
  2. aws-cli 및 boto 설치: pip install awscli boto3 --user
  3. aws-cli 설정: aws configure (CodeDeployIDC-User의 access token 입력)
  4. CodeDeploy agent 설치:
    1
    $ wget https://aws-codedeploy-ap-northeast-2.s3.amazonaws.com/latest/install
    2
    $ chmod +x ./install
    3
    $ sudo AWS_REGION=ap-northeast-2 ./install auto

STS 토큰 발급 및 설정

  1. 토큰을 업데이트하는 스크립트 /home/lendit/credential-update.py를 작성합니다.
    1
    import boto3
    2
    3
    sts = boto3.client('sts')
    4
    resp = sts.assume_role(
    5
        RoleArn='arn:aws:iam::12345678901:role/CodeDeployIDC-Instance-Profile',
    6
        RoleSessionName='serial-ABCDEFG',
    7
    )
    8
    with open('/home/lendit/.aws/credential-codedeploy', 'w') as f:
    9
        f.write('[default]\n')
    10
        f.write('aws_access_key_id = ' + resp['Credentials']['AccessKeyId'] + '\n')
    11
        f.write('aws_secret_access_key = ' + resp['Credentials']['SecretAccessKey'] + '\n')
    12
        f.write('aws_session_token = ' + resp['Credentials']['SessionToken'] + '\n')
  2. 주기적으로 위 스크립트가 실행되도록 cron에 등록합니다. 세션의 기본 유효시간은 1시간으로 30분 간격으로 등록하면 충분합니다.
  3. CodeDeploy가 토큰을 사용하여 작동하도록 /etc/codedeploy-agent/conf/codedeploy.onpremises.yml 설정을 수정합니다.
    1
    ---
    2
    iam_session_arn: arn:aws:sts::12345678901:assumed-role/CodeDeployIDC-Instance-Profile/serial-ABCDEFG
    3
    aws_credentials_file: /home/lendit/.aws/credential-codedeploy
    4
    region: ap-northeast-2
  4. CodeDeploy agent를 재시작합니다.

서버 등록

  1. 마침내 register-on-premises-instance를 사용하여 instance를 등록합니다. –instance-name은 변경할 수 없습니다.
    1
    $ aws deploy register-on-premises-instance
    2
          --instance-name serial-ABCDEFG
    3
          --iam-session-arn arn:aws:sts::12345678901:assumed-role/CodeDeployIDC-Instance-Profile/serial-ABCDEFG
  2. 등록된 on-premises instance에 태그를 붙입니다. 태그의 값은 EC2 태그와 마찬가지로 언제든지 변경할 수 있습니다.
    1
    $ aws deploy add-tags-to-on-premises-instances
    2
          --instance-names serial-ABCDEFG
    3
          --tags Key=Name,Value=idc-01

배포를 배포답게! 시리즈를 마치며

JSP와 Vue가 섞여있던 프로젝트에서 프론트엔드를 분리했을 때의 배포 과정, 롤백 과정, (2) 편에서 배포 시스템을 설명하며 소개한 Lendit Portal의 다른 기능 등등 아직 할 이야기가 많이 남아있지만, 이 시리즈는 여기서 마칩니다. 렌딧에 입사하고 AWS를 처음 다루게 되어 많은 검색과 삽질을 했지만, 많이 배울 수 있어서 재미있었고, 다른 개발자분들이 잘 사용해주셔서 뿌듯했습니다. 이 글이 누군가에게 도움이 되었으면 좋겠습니다. 감사합니다.