Jaeilit

Git action + CICD (EC2 + ECR) 본문

TIL

Git action + CICD (EC2 + ECR)

Jaeilit 2024. 8. 30. 14:27
728x90

git action 으로 cicd 를 하는 방법에 대해서 기술하려고 합니다.

 

본 내용은 nestjs 서버 배포의 내용을 하기 때문에 ec2에 배포를 하려고했고 프론트 배포는 spa 같은 경우엔 s3 에 정적으로 배포를 해도 되니 ec2 인스턴스를 사용할 일은 없을 것 같고, nextjs 를 사용한다면 버셀에서 워낙 잘되있기에 해당사항이 없을 수도 있습니다.

 

깃 액션에서 수행할 테스크는 로컬에서 하는 방식과 동일합니다.

2. aws 로그인을 하고

2. 도커 이미지를 빌드와 ecr에 푸쉬하고

3. ec2 원격으로 접속한 뒤에

4. 이미지를 불러와서 docker copose 해당 서비스를 해주시면 됩니다.

 

깃 액션에서도 동일하게 수행하면 됩니다.

 

1. aws 인증

- name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v1
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          aws-region: ${{secrets.AWS_REGION }}

 

유저 생성 관련해서는 검색만해도 안내해주는 블로그 글이 많기 때문에 생략합니다. 대신에 나중에 권한 정책에 관한 부분을 다루겠습니다.

access key id 와 secret access key 는 aws iam 에서 유저를 생성하면서 같이 발급하게 되는데 다시 확인하기 어렵기 때문에 발급받고 따로 메모하시거나 저장해두시기 바랍니다. 

 

2. ecr 로그인

- name: Login to Amazon ECR
        id: login-ecr
        uses: aws-actions/amazon-ecr-login@v1
        with:
          mask-password: 'true'

ecr push

 

ecr 로그인 하는 액션인데 로컬이라면서 사진에 나와 있는 aws ecr~~ 명령어를 aws cli 로 입력해야합니다. 해당 액션은 위에 aws 인증이 되어있다면 명령어 기입없이 편하게 수행됩니다. mask password 는 패스워드 마스킹하는 옵션입니다. 넣는 것을 권장합니다 넣지 않는다면 깃 액션이 돌 때 확인해보시면 에러는 뜨지 않으나 경고는 뜹니다.

 

3. 태그명

이번거는 태그 명에 대한 액션인데요 따로 사용하시는게 있으시다면 패스해도 됩니다. 저 같은 경우에는 깃 커밋 해시의 앞자리 6자리로 지정해뒀습니다.

 - name: Set short_sha variable
        id: git_sha
        run: echo "SHORT_SHA=$(echo $GITHUB_SHA | cut -c1-6)" >> $GITHUB_OUTPUT

 

4. ecr 푸쉬

ecr 로그인도 했고 태그명도 정해졌고 이제 푸쉬만 하면되는데요 먼저 이미지를 빌드하고 tag 명을 바꿔주고 docker push 하도록 하면 됩니다. 

아래 run 에서 수행하는 커맨더는 ecr 푸쉬 명령에서 소개하는 순서로 빌드하고 태그명 바꿔주고 푸쉬하는 순서인데요

빌드하고 태그를 바꿔주지않고 처음부터 docker build 할 때 부터 태그 달아서 하셔도 됩니다.

 

steps 는 steps.액션.outputs.변수명 이렇게 사용하는건데 공식문서에 설명이 잘되어있기 때문에 공식문서를 보시는 것을 추천합니다.

간단히 설명드리자면

steps 는 액션의 steps을 이야기하는것이고

git_sha 는 해당 액션에서 지정해둔 id 입니다. 위에 보시면 id를 지정해둔 것이 이제는 보이실 겁니다.

outputs 는 기본 메서드라고 생각하시면 되고 당연히 다른 메서드들도 다양하게 존재합니다.

SHORT_SHA 는 변수명인데요 위에서 echo 명령어로 SHORT_SHA 변수에 값을 할당하는 것이 이제는 보이실겁니다.

 

저도 이런것들을 전부 외우고 있진않고 필요한 부분에 대해서 방법이 없을까 하면서 공식문서를 이리저리 뒤져보는 편입니다. 그래서 자세한 설명이 좀 부족할 수 있으니 공식문서를 보시는 것을 추천드립니다.

다른 사용 예시)

더보기

name: SetUp
        runs-on: ubuntu-latest
        steps:
            - name: setMode
              id: setMode
              run: |
                  if [ "${{ github.event_name == 'push' }}" == "true" ]; then
                    #  릴리즈나 디벨롭 브랜치에 머지 & 푸쉬 할때 stage 배포를 위한 (환경)변수 설정
                    echo "mode=STAGE" >> $GITHUB_OUTPUT
                  elif [ "${{ github.event_name }}" == "pull_request" ] && [ "${{ startsWith(github.head_ref, 'feat/') || startsWith(github.head_ref,'fix/') }}" == "true" ]; then
                    #  feat/ fix/ 브랜치에 pr 올릴떄 dev 배포를 위한 (환경)변수 설정
                    echo "mode=QA" >> $GITHUB_OUTPUT
                  else
                    exit 1
                  fi

        outputs:
            mode: ${{ steps.setMode.outputs.mode }}

 build:
        name: ${{ needs.setup.outputs.mode }} Build & Push
        needs: setup
        runs-on: ubuntu-latest

git action

다른 사용 예시로는 steps.setMode.outputs.mode 이 변수 값으로 어떤 환경에서 빌드하고 배포했는지 체크를 해두었습니다.

 

 - name: ECR push
        run: |
          docker build -t ${{ secrets.IMAGE_NAME }} .
          docker tag ${{ secrets.IMAGE_NAME }}:latest ${{ secrets.AWS_ECR_ADDRESS }}:${{ steps.git_sha.outputs.SHORT_SHA }}
          docker push ${{ secrets.AWS_ECR_ADDRESS }}:${{ steps.git_sha.outputs.SHORT_SHA }}

 

5. ec2 배포

host 는 ec2로 진입가능한 퍼블릭 ip 입니다.

ec2에서 탄력적 ip를 생성할 수 있는데요, 생성 후에 인스턴스에 할당을 해주어야 합니다. 그리고 또 인바운드 규칙으로 해당 ip로 진입가능하도록 포트설정을 해주어야합니다. 이런 설정 방법도 찾아보면 해당 글이 많으니 따로 설정을 다루진 않겠습니다.

 

username, key, port 는 전부 ec2 ssh에 관한 내용입니다.

cd ~/.ssh

퍼블릭키 생성을 위해서 해당 디렉토리로 이동합니다. 디렉토리가 존재하지 않다면 생성해주세요,

https://docs.github.com/ko/authentication/connecting-to-github-with-ssh/generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent

 

새 SSH 키 생성 및 ssh-agent에 추가 - GitHub Docs

SSH(Secure Shell Protocol)를 사용하여 GitHub.com의 리포지토리에서 데이터에 액세스하고 쓸 수 있습니다. SSH를 통해 연결할 때 로컬 머신에서 프라이빗 키 파일을 사용하여 인증합니다. 자세한 내용은 "

docs.github.com

디렉토리를 생성 후에 위 링크에 따라서 진행하면서 로컬에 ssh key를 생성하고 레포지토리에도 ssh key를 입력하면 되는데 핵심은 git email 주소 입력을 까먹지 않아야합니다. 무작정 엔터만 막 치다가는 mac의 id되거나 이상한 id가 생성됩니다. 제가 로컬에서 접속하는 것이 아니고 깃 액션이 깃에서 ec2 ssh로 접속하는 것이기 때문에 git email 계정에 대한 인증키를 생성하는 것입니다.

vscod extenstion

생성 후에 vscode 익스텐션 중에  vscode 에서 ssh 을 접속할 수 있게 해주는 remote-ssh 를 설치하고 설정을 마친 뒤에 vscode 에서 ssh를 접속하면 ec2 인스턴스로 진입하게 됩니다.

 

IdentityFile 은 .pem 확장자로 된 키 페어가 있을텐데 없다면 생성하시면 됩니다.

 

ssh 을 vscode로 성공적으로 진입했다면 디렉토리에는 보이는게 없을 텐데

 

vscode 중앙 상단 부에 저 부분을 클릭하면 루트 디렉토리로 이동하는 경로를 안내할 겁니다. remote-ssh 로 접속한 초기에는 어디로 가야할지 몰라서 아무것도 나오지 않는 것입니다. 설정한다면 remote-ssh 의 ssh 네이밍 밑으로 디렉토리로 바로가기도 생깁니다.

 

ssh 접속 후에 루트 디렉토리에서도 동일하게 .ssh 디렉토리가 있습니다. 여기 안에 authorized_keys 가 있을텐데요 ssh 접속을 위해서 아까 생성한 publick 의 약자인 .pub 확장자로 끝나는 퍼블릭 키를 복붙해주면 됩니다. 내용 중에 끝 부분이 id 인데요 이게 git 이메일 주소랑 다르면 git email 주소로 다시 생성해야합니다.

 

ssh 생성 후에 커맨더로 ssh에 전송하는 방법이 있기는 하지만 아직 ssh 접속이 되는지 확인조차 안된 단계라서 저는 눈으로 보고 직접 입력하는게 편해서 복붙하는 방법을 택했습니다.

ssh 설정은 끝났고 이제 scripts 부분인데요 ssh 루트에서 apps 디렉토리를 만들어주고 docker compose 파일도 생성하고 내용 작성하고 나서 준비를 끝내고 다시 scripts 부분으로 와서 aws login 을 보면 아까 인증을 했는데 왜 로그인하지? 라고 생각 할 수도 있는데 지금은 환경이 다릅니다. 아까는 깃 액션 백그라운드에서 aws 인증을 한 것이고 지금은 ssh 환경으로 접속해서 ecr에서 이미지를 풀 받기 위해서 aws cli을 사용하기 위한 aws login 입니다. aws login 이 시크릿은 아까 위에서도 했던 아래 사진입니다.

ecr push

 

아마 Ec2 인스턴스 초기에는 아무것도 설치가 되어있지 않을텐데

공식문서 보고 따라 설치하면 됩니다. 설치도 아마 압축파일로 되실텐데 압축을 풀기 위해 .taz 을 푸는 툴도 설치해야 할 겁니다.

aws cli 설치하는 김에 나중에 사용 할 도커 컴포즈도 설치해주면 좋을거 같네요

https://docs.aws.amazon.com/ko_kr/cli/latest/userguide/getting-started-install.html

 

최신 버전의 AWS CLI설치 또는 업데이트 - AWS Command Line Interface

이전 버전에서 업데이트하는 경우 unzip 명령을 실행하면 기존 파일을 덮어쓸지 묻는 메시지가 표시됩니다. 스크립트 자동화와 같은 경우에 이러한 프롬프트를 건너뛰려면 unzip에 대한 -u 업데이

docs.aws.amazon.com

https://docs.docker.com/engine/install/ubuntu/

 

Install Docker Engine on Ubuntu

Jumpstart your client-side server applications with Docker Engine on Ubuntu. This guide details prerequisites and multiple methods to install Docker Engine on Ubuntu.

docs.docker.com

 

image pull

 

aws 로그인 후에 이미지를 깃 해시 태그를 달아서 푸쉬했으니 깃 해시 달린 이미지를 풀 해야합니다. 어떻게 동적으로 할까 고민을 많이 했는데 스크립트로 변수를 할당하는 방법을 사용했습니다. 먼저 .env 에 BACKEND_TAG=latest 환경변수를 만들어줍니다. 사실 빈 값을 넣어도 되고 latest를 기본 값으로 사용하고싶으시면 latest를 넣어도 됩니다. 

 

scripts 실행 시 아까 해시 step 의 hash 값을 동적으로 할당해주면 됩니다.

- name: EC2 pull at ECR image
        uses: appleboy/ssh-action@master
        with:
          host: ${{ secrets.SSH_HOST }}
          username: ${{ secrets.SSH_USERNAME }}
          key: ${{ secrets.SSH_KEY }}
          port: ${{ secrets.SSH_PORT }}
          script: |
            cd ~/apps
            ${{ secrets.AWS_LOGIN }}
            BACKEND_TAG=${{ steps.git_sha.outputs.SHORT_SHA }} docker compose up -d server

 

 

이렇게 cicd 파이프라인을 git action 으로 구성할 수 있습니다. 그리고 돌려보면 아마 당연히 에러가 날 겁니다. 이유는 ecr 접근 권한 정책에 대해서 설정을 안해줬거든요.

 

ecr 콘솔에서 왼쪽 목록에서 정책을 들어가서 정책 생성을 해주면 되는데 눈에 익지 않는 이상 많이 어지러울 겁니다. 저도 아직은 많이 어지럽네요, 저는 공식문서에 내용을 많이 참고했는데요,

https://docs.aws.amazon.com/ko_kr/AmazonECR/latest/userguide/security_iam_id-based-policy-examples.html

 

Amazon Elastic Container Registry 자격 증명 기반 정책 예제 - 아마존 ECR

이 페이지에 작업이 필요하다는 점을 알려 주셔서 감사합니다. 실망시켜 드려 죄송합니다. 잠깐 시간을 내어 설명서를 향상시킬 수 있는 방법에 대해 말씀해 주십시오.

docs.aws.amazon.com

하나의 레포지토리 Access 라는 부분을 그대로 가져와서 사용했습니다.

필요한 Access 권한은 이미지를 푸쉬할때 필요한 GetAuthorizationToken

그리고 이미지를 pull 받을 때 필요한 권한들입니다. 

리소스 부분에 레포지토리/레포지토리네임이랑 지역(리즌)이 아마 서울이 아니면 아래와 다를 수도 있으니 참고해주시고 설정해주시면 됩니다.

{
	"Version": "2012-10-17",
	"Statement": [
		{
			"Sid": "ListImagesInRepository",
			"Effect": "Allow",
			"Action": [
				"ecr:ListImages"
			],
			"Resource": "arn:aws:ecr:ap-northeast-2:084828582995:레포지토리/레포지토리네임"
		},
		{
			"Sid": "GetAuthorizationToken",
			"Effect": "Allow",
			"Action": [
				"ecr:GetAuthorizationToken"
			],
			"Resource": "*"
		},
		{
			"Sid": "ManageRepositoryContents",
			"Effect": "Allow",
			"Action": [
				"ecr:BatchCheckLayerAvailability",
				"ecr:GetDownloadUrlForLayer",
				"ecr:GetRepositoryPolicy",
				"ecr:DescribeRepositories",
				"ecr:ListImages",
				"ecr:DescribeImages",
				"ecr:BatchGetImage",
				"ecr:InitiateLayerUpload",
				"ecr:UploadLayerPart",
				"ecr:CompleteLayerUpload",
				"ecr:PutImage"
			],
			"Resource": "arn:aws:ecr:ap-northeast-2:084828582995:레포지토리/레포지토리네임"
		}
	]
}

 

 

git action 으로 cicd 파이프라인을 구성해봤습니다. 더 고도화를 해본다면 ecr 에 이미지를 쌓아 둘수 없으니 라이프 사이클도 정책도 정의하면 좋고 ec2에도 이미지를 계속 풀 받고 사용하지 않는 이미지들을 정리를 해주지 않으면 메모리를 먹으니 주기적으로 정리해주는 장치를 해두면 좋겠네요! 

 

.ps

그리고 배포가 끝난 뒤에 주로 사용하는 메신저에 웹훅 개념으로 연결해서 결과를 알림으로 받을 수도 있습니다.

지금은 아니지만 전에는 텔레그렘으로 결과를 받도록 했었습니다.

#  - name: Send to Depoly Result Chatbot
#           uses: appleboy/telegram-action@master
#           with:
#               to: ${{ secrets.TELEGRAM_CD_CHANNEL_ID }}
#               token: ${{ secrets.TELEGRAM_CD_TOKEN }}
#               format: markdown
#               message: |

#                   ${{ needs.setup.outputs.mode }} 배포 ${{ needs.build.result }}

#                   작성자: ${{ github.actor }}

#                   버전: ${{ needs.build.outputs.git_sha }}

#                   ${{ steps.link.outputs.link }}

#                   ${{ github.event.pull_request.body }}

 

728x90