반응형

문제

root 권한이 없는 계정을 통해 ubuntu를 운용하면, code deploy시 생성된 코드의 directory가 'root'로 변경되어 code deploy가 실패하거나(code deploy 후, build 디렉토리 소유자가 root로 변경되었다.), 해당 디렉토리 내의 특정 작업들이 제한된다. 

이에 따라, codedeploy >> appspec.yml >> hooks 부분 script >> 해당 디렉토리에서 추가 작업(ex. log directory 생성)이 있을 경우, code deploy가 실패된다.

메시지
Script at specified location: ./script/start.sh run as user jane failed with exit code 1

왜?? start.sh에는 ecosystem.json을 호출하여 nodejs 코드를 띄워주는데 

ecosystem.json에서 error와 out log를 쌓는 디렉토리를 생성한다.

그런데, /home/jane/build 디렉토리 소유자는 root이므로, 'jane' 권한은 폴더를 만들 수 없기 때문에 실패한다.

 

원리

답은 service codedeploy-agent status에 있다.

이 코드는 아래 Loaded와 같이 /etc/init.d/codedeploy-agent에서 돌아가는데,

/etc/init.d/codedeploy-agent 소유자(그룹)는 2450, users다.

codedeploy-agent

하지만 이런 건 다른 데에도 분명 영향이 있어서 함부로 바꾸면 안된다.

구글링을 통해 찾자.

찾았다.

루트 프로파일 이외의 사용자 프로파일을 사용하여 CodeDeploy 에이전트를 실행하려면 어떻게 해야 하나요?

https://aws.amazon.com/ko/premiumsupport/knowledge-center/codedeploy-agent-non-root-profile/

 

루트 이외의 프로파일로 CodeDeploy 에이전트 실행

6.    다음 명령을 실행하여 어떤 프로세스가 실행 중이며, 어느 사용자가 해당 프로세스를 실행 중인지 확인합니다.

aws.amazon.com

 

해결

ubuntu@ip-192-168-23-61:/etc/init.d$ sudo service codedeploy-agent stop

 

// /etc/init.d/codedeploy/agent에 있는 ""를 "jane"으로 변경. (CodeDeploy agent를 수행할 non-root 계정)

ubuntu@ip-192-168-23-61:/etc/init.d$ sudo sed -i 's/""/"jane"/g' /etc/init.d/codedeploy-agent

 

// 변경 여부 확인. CODEDEPLOY_USER에 "jane"이 추가되었다.

ubuntu@ip-192-168-23-61:/etc/init.d$ vi /etc/init.d/codedeploy-agent

// /opt/codedeploy/agent에 있는 모든 파일들의 소유자/그룹을 jane:jane으로 변경한다.

ubuntu@ip-192-168-23-61:/usr/lib/systemd/system$ sudo chown jane:jane -R /opt/codedeploy-agent

ubuntu@ip-192-168-23-61:/usr/lib/systemd/systemsudo chown jane:jane -R /var/log/aws/

 

// 확인해보자. 진짜 다 바뀌었다.

ubuntu@ip-192-168-23-61:/usr/lib/systemd/system$ cd /opt/codedeploy-agent/

ubuntu@ip-192-168-23-61:/opt/codedeploy-agent$ ll

total 56

drwxr-xr-x 8 jane jane  4096 Jul  5 14:29 ./

drwxr-xr-x 4 root root  4096 Jul  4 15:35 ../

-r--r--r-- 1 jane jane    38 Apr 16  2021 .version

-rw-r--r-- 1 jane jane   353 Mar 23  2021 Gemfile

-rw-r--r-- 1 jane jane 10174 Mar 23  2021 LICENSE

drwxr-xr-x 2 jane jane  4096 Jul  4 15:35 bin/

drwxr-xr-x 2 jane jane  4096 Jul  4 15:35 certs/

-rw-r--r-- 1 jane jane  1158 Mar 23  2021 codedeploy_agent.gemspec

drwxr-xr-x 6 jane jane  4096 Jul  5 18:13 deployment-root/

drwxr-xr-x 4 jane jane  4096 Jul  4 15:35 lib/

drwxr-xr-x 3 jane jane  4096 Jul  4 15:35 state/

drwxr-xr-x 4 jane jane  4096 Jul  4 15:35 vendor/

 

jane@ip-192-168-23-61:/var/log$ cd aws

jane@ip-192-168-23-61:/var/log/aws$ ll

total 12

drwxr-xr-x  3 jane jane   4096 Jul  4 15:35 ./

drwxrwxr-x 10 root syslog 4096 Jul  6 15:25 ../

drwxr-xr-x  2 jane jane   4096 Jul  6 00:00 codedeploy-agent/

 

그리고 /etc/init.d의 codedeploy-agent.service 또한 User를 jane으로 입력해주자. 

(그렇지 않으면, 서비스는 여전히 root로 돈다)

 

이제 codedeploy-agent를 다시 실행하면 된다. (sudo는 빼자)

sudo service codedeploy-agent start
sudo service codedeploy-agent status

 

여기까지 하면, ps aux를 통해 codedeploy-agent가 띄워지는 권한이 root -> jane으로 변경된 것을 확인할 수 있다.

 

 

+ 그리고, appspec.yml에서 hook쪽에 runas가 있다면 이번엔 아래와 같은 에러가 발생한다.
[stderr]su: must be run from a terminal

 

이는 appspec.yml의 hooks 쪽에 있는 runas를 빼주면 된다

(runas가 실행권한과 다르면 상관없는데, agent를 동작시키는 권한과 같을 경우 에러가 발생한다)

 

반응형
반응형

지난 포스팅에 이어, 이번에는 Codedeploy 설정을 진행한다.

1. CodeBuild 설정

2. Codedeploy 설정: 이번 포스팅

3. EC2 Ubuntu 설정

 

 

Codedeploy에서 deploy에 사용할 애플리케이션(Jane-codedeploy)을 생성한다. 

플랫폼은 EC2/온프레미스를 설정하면 끝난다.

 

애플리케이션 생성 후엔 '배포 그룹'에서 실제로 이 code deploy가 어떤 인스턴스에 배포될지 설정한다.

나는 EC2에 배포를 할 것이고, EC2 중에서도 어떤 인스턴스에 배포할지는 인스턴스에 설정된 '태그'를 통해 코드가 배포된다.

(난 Service:Test 태그를 설정했다.)

codedeploy의 배포그룹 구성
코드를 배포할 ec2 인스턴스의 태그

 

 

그리고, codedeploy-agent를 ec2에 설치하면 된다.

나는 ubuntu용 ec2이므로 아래 링크를 참고했고, 들어가보면 윈도우 등 다양한 옵션이 있다.

 

* Codedeploy agent 설치

1. ruby 설치: Codedeploy agent는 ruby로 작성되었으므로 이를 설치한다.

$ sudo apt-get install ruby  // (ubuntu 16.04)

 

2. wget 설치 (agent 설치파일을 들고오기 위해 쓰인다)

$ sudo apt-get install wget

 

3. Codedeploy Agent 설치

# 나는 jane계정을 사용하므로 /home/jane이며, ubuntu 계정을 사용한다면 /home/ubuntu로 가면 된다.

$ cd /home/jane

 

# 설치파일 다운로드 (Seoul region) 

$ wget https://aws-codedeploy-ap-northeast-2.s3.ap-northeast-2.amazonaws.com/latest/install

 

# 실행권한 추가 및 설치

$ chmod +x ./install

$ sudo ./install auto

 

하다가 막힌다면 아래 aws docs를 참고한다.

https://docs.aws.amazon.com/ko_kr/codedeploy/latest/userguide/codedeploy-agent-operations-install-ubuntu.html

 

Ubuntu Server용 CodeDeploy 에이전트 설치 - AWS CodeDeploy

출력을 임시 로그 파일에 쓰는 것은 Ubuntu 20.04에서 install 스크립트를 사용하여 알려진 버그를 해결하는 동안 사용해야 하는 해결 방법입니다.

docs.aws.amazon.com

 

* Codedeploy agent 서비스 확인

$ sudo service codedeploy-agent status

* ubuntu 계정이 아닌, jane과 같이 별도의 root권한이 아닌 계정을 사용한다면 추가 설정이 필요하다. 

그렇지 않으면, codedeploy-agent가 root로 돌기 때문에(위 캡쳐본에 /etc/init.d/codedeploy/agent에서 돌고 있다),

코드가 배포되는 디렉토리가 root가 되어 추후 다른 문제들을 야기시킨다. 이는 별도로 포스팅 했으니 여기 참고!

https://countrymouse.tistory.com/entry/awscodedeployerr

 

[AWS] root 이외 계정에서 codedeploy 실행, codedeploy 'run as user jane failed with exit code 1', '[stderr]su: must be ru

문제 root 권한이 없는 계정을 통해 ubuntu를 운용하면, code deploy시 생성된 코드의 directory가 'root'로 변경되어 code deploy가 실패하거나(code deploy 후, build 디렉토리 소유자가 root로 변경되었다.),..

countrymouse.tistory.com

 

 

그리고, codedeploy 설정을 끝냈으니 codepipeline으로 돌아와서, '배포'부분에 codedeploy에서 만든 deploy를 추가해준다.

입력 아티팩트는 codebuild의 output이었던 BuildArifact이고, 애플리케이션은 codedeploy에서 만들었던 Jane-codedeploy,

배포 그룹은, jane-codedeploy에서 만들어줬던 jane-codedeploy이다.

 

 

그리고, codedeploy에는 appspec.yml이 필요한데, 이는 아래와 같이 작성했다.

appspec.yml

files: source, destination, runas

files은 source: / 내 파일들을 ec2의 destination( /home/jane/build/)로 옮긴다.

즉, build에서 산출된 artifacts의 모든 것들을 ec2의 /home/jane/build/로 옮긴다. (/home/jane/build/ 디렉토리는 생성시켜두자)

그리고, 그 동작은 'jane'이 run 한다.

 

permissions: 

복사한 파일들의 객체들은 모두(pattern: "**") owner/grp을 'jane'으로 준다.

근데, 이미 위에서 말한  "jane과 같이 별도의 root권한이 아닌 계정을 사용한다면 추가 설정이 필요하다. " 를 했으므로,

이 permission은 생략해도 된다.

 

hooks:

deploy는 아래 각 절차(hooks)마다 하나 이상의 스크립트를 통해 변수선언, 환경설정 등을 할 수 있는데

나는 간단한 코드를 올렸으므로 applicationStart에만 code를 실행시키는 간단한 event를 만들었다.

https://docs.aws.amazon.com/ko_kr/codedeploy/latest/userguide/reference-appspec-file-structure-hooks.html

 

start.sh

pm2로 nodejs 코드를 띄우는 sh이다. (pm2 reload ecosystem.json이 메인)

6번째 줄까지는 안 해도 된다. 나는 각 절차가 궁금해서 찍어봤다.

ecosystem.json

/home/jane/build 디렉토리에 있는 index.js를 실행한다. 

코드가 변경되는지 watch==true를 통해 확인하며, error_file, out_file이 쌓이는 위치를 지정해주었다.

 

이 모든 파일은(appspec,yml, scosystem.json, start.sh 등) 코드와 같이 git에 올리면 된다 :)

 

반응형
반응형

 

지난 번에 코드를 ECS Fargate로 배포하는 CI/CD pipeline을 구축했다.

이번에는 ECS Fargate가 아닌, EC2로 배포하는 CI/CD pipeline 구축을 정리해볼 예정이다.

(EC2를 사용하는 곳이 종종 있기 때문이다.)

 

1. CodeBuild 설정: 이번 포스팅

2. Codedeploy 설정

3. EC2 Ubuntu 설정

 

먼저, Codepipeline의 Source까지는 모두 이전 ecs cicd pipeline 포스팅과 동일하므로 생략한다.

* 이전 포스팅: https://countrymouse.tistory.com/entry/awscicd2

 

[AWS] CI/CD pipeline 구축 1. Bitbucket <-> Slack 연동

CI/CD 구축을 위한 단계 0. ECS fargate 생성 (Cluster, Service, task-definition, task) 1-1. Bitbucket에 코드 업로드 (git) 1-2. Bitbucket slack 연동 (PR 알람 노티를 위함) 2. Bitbucket AWS API Gateway..

countrymouse.tistory.com

 

그럼, Codebuild부터 시작해보자.

Codebuild는 별도 메뉴에서 생성하지 않았고, codepipeline를 만들 때 생성했다. (아래 프로젝트 생성)

입력아티팩트는, codepipeline의 source인 s3의 출력아티팩트 값과 동일해야 한다. 

codepipeline의 source부분의 '출력아티팩트'는 'SourceArtifact'로 설정되어 있다.

그리고, 출력아티팩트(BuildArtifact)는 이후 CodeDeploy의 입력아티팩트가 된다.

 

빌드에 사용할 소스와 빌드를 통해 산출되는 아티팩트는 각각 s3의 jane-bucket2에 저장된다.

그리고, build하는 데에 사용되는 buildspec.yml이 필요하다.

 

 

buildspec에는 code deploy에서 배포할 환경이 ubuntu이므로 별도의 docker 작업은 포함되지 않고

n 16.14.0 환경에서 빌드를 수행하게 도와준다.

  • n 은 우분투의 node.js 버전관리 플러그인

 

node_modules

그리고, 소스코드에선 node_modules를 포함하지 않았는데(무거워지니까), codebuild에서 npm install을 통해

코드 실행에 필요한 node_modules들을 생성시켜 준다.

Artifacts

그리고, 해당 빌드를 통해 저장할 파일들을 artifacts: files:에 넣어주는데, 보통 '**/*'로 전체를 artifacts(결과물)로 만들어준다.

(이 artifacts는 codebuild에 있었던 출력아티팩트 저장 위치(s3:jane-bucket2/jane-codepipeline7/BuildAritf/~에 업로드 된다.)

 

Cache

앞으로 해당 코드를 또 수행할 경우, cache를 해 두면 빌드 시간이 굉장히 줄어든다. 그래서 캐시를 전체를 설정한다.

version: 0.2

phases:
install:
commands:
- n 16.14.0
- echo Build starting on `date`
- node --version
build:
commands:
- npm install
- pwd
artifacts:
files:
- '**/*'
cache:
path:
- '**/*'
 

 

buildspec.yml은 다른 코드들과 동일하게 git에 같이 올려서(가장 상위 디렉토리에),

git pr merge가 발생할 때마다 api gateway -> lambda -> s3 를 통해 codebuild가 수행하며 사용된다.

 

다음 포스팅은 상당히 애 먹었던 codedeploy다.

반응형
반응형

상황

AWS의 lambda 안에서 git 명령을 사용하여(execSync를 이용) Run하면,

아래와 같이 '/bin/sh: git: command not found'가 발생한다.

 

2022-07-04T04:21:23.997Z a8ab8b4b-6f54-4775-90a9-1076ba630dc8 INFO proxy: undefined
/bin/sh: git: command not found

 

해결

람다 메인에서 계층에 'Add a layer'로 lambda layer를 추가해준다.

단, 해당 람다와 동일한 리즌에 있는 계층과 연결해야 하므로

아래 github 링크에서 언급한 arn의 리즌을 us-east-1 --> ap-northeast-2(서울)로 바꿔주고 저장한다.

 

 

 

출처

https://github.com/lambci/git-lambda-layer

 

GitHub - lambci/git-lambda-layer: A layer for AWS Lambda that allows your functions to use `git` and `ssh` binaries

A layer for AWS Lambda that allows your functions to use `git` and `ssh` binaries - GitHub - lambci/git-lambda-layer: A layer for AWS Lambda that allows your functions to use `git` and `ssh` binaries

github.com

 

반응형
반응형

Cloud Scheduler: GCP의 Cloud function에 올려둔 코드를 주기적으로 호출해보자.

주기적인 호출(=cron)을 위해서는 GCP의 Cloud schduler 앱을 사용한다.

나는 10분에 한 번씩 cloud function에 있는 특정 함수가 호출되도록 cloud scheduler에서 작업을 추가했다.

 

1. GCP > Cloud Scheduler > 작업 만들기

10분에 한 번씩 cloud function에 있는 특정 함수가 실행되도록 할 것이므로 빈도를 */10으로 설정해준다.

URL은, cloud function에서 만들었던 함수의 trigger되는 URL이다.

(== 내가 Trigger 하고 싶은 cloud function의 특정 함수 URL)

 

 

HTTP 헤더(Content-Type, User-Agent)는 해당 task를 저장하면 자동으로 입력되는 값이다.

인증헤더는 일반적으로 'OIDC 토큰 추가' 를 사용한다. 

https://cloud.google.com/scheduler/docs/http-target-auth?hl=ko 

 

HTTP 대상에 인증 사용  |  Cloud Scheduler 문서  |  Google Cloud

의견 보내기 HTTP 대상에 인증 사용 적절한 사용자 인증 정보가 있는 연결된 서비스 계정을 설정한 경우 Cloud 스케줄러는 인증이 필요한 HTTP 대상을 호출할 수 있습니다. 서비스 계정 설정 Cloud Sche

cloud.google.com

 

그렇게 scheduler를 생성 후 '다음 실행' 동안 기다려보면, scheduler를 통해 함수가 실행되는 것을 확인할 수 있다.

반응형
반응형

* Cloud Function: GCP의 이벤트 기반 서버리스 플랫폼 (= AWS Lambda)

 

오늘은 지난 번에 만든 airflow 특정 DAG의 running 시간을 체크하는 코드를 GCP의 cloud function에서 돌려본다.

언어는 NodeJS를 사용하고, 파일은 index.js, package.json, markdown.json을 사용한다.

이전코드: https://countrymouse.tistory.com/entry/airflowdag

 

Airflow에서 특정 DAG의 실행 시간을 현재시간과 비교하여 slack으로 메시지를 보내주는 코드.

배경 GCP의 airflow에서 특정 DAG의 마지막 batch 시간 start_time을 '현재 시간'과 비교해서, 30분이상 차이가 날 경우, slack으로 알람을 보내줄 것이다. (30분이상 차이가 난다면 배치가 제대로 돌고 있지

countrymouse.tistory.com

 

* GCP cloud function은, AWS lambda와는 다르게 node_modules를 별도로 첨부하지 않는다.

그냥 코드만 올려두고 배포를 실행하면, GCP가 알아서 빌드를 하고 node_modules를 내부적으로 만들어준다.

(그래서 빌드 시간이 더 오래 걸림)

 

 

나는 이전 포스팅의 코드를 그대로 사용하며,

serverless는 이벤트에 의해 코드가 Trigger 되기 때문에 exports handler를 사용한다. 

함수는 export handler 안에서 main을 호출하고 200을 보낸 후 종료된다.

exports.start = async (req, res) => {
await main();
res.send(200);
}

 

그렇게 코드를 배포 후, 로그를 확인해보면 Function이 정상 started 되고 로그도 잘 찍힌 것을 볼 수 있다.
 
반응형
반응형

CI/CD 구축을 위한 단계

  0. ECS fargate 생성 (Cluster, Service, task-definition, task)

1-1. Bitbucket에 코드 업로드 (git)

1-2. Bitbucket <-> slack 연동 (PR 알람 노티를 위함)

2-1. Bitbucket <-> AWS API Gateway 연동 with Webhook

2-2. API gateway -> Lambda 호출

3. Lambda -> S3로 패키지 업로드

4. AWS Codepipeline: S3 -> Codebuild + ECR 연동

5. Codebuild -> Deploy(ECS fargate)

6. ECS <-> LB(ALB) 연동하여 동작 테스트


다 왔다. 이제 마지막 단계인 Deploy 단계이다.

이전 포스팅 (4. Codebuild + ECR 연동)에서 빌드 스테이지까진 만들어 두었으므로, 이번엔 배포 스테이지를 만들어 본다 :)

 

 

이전 포스팅에서 만들어 두었던 코드 파이프라인을 편집해서 Deploy 작업 그룹을 추가한다.

 

작업공급자는, Codedeploy가 아닌 Amazon ECS를 사용한다.

왜냐면, ECS의 fargate에 배포를 할 거고, container를 관리해주는 서비스 자체가 ECS이므로.

 

그래서, Deploy의 입력 아티팩트는, Build에서 저장했던 imagedefinitions.json이 되므로 BuildArtifact로 설정한다.

그리고 클러스터 및 서비스는 이 포스팅 0편에서 다뤘던, ECS의 클러스터/서비스를 선택한다.

이미지 정의 파일은 codebuild에서 buildspec.yml으로 언급한 imagedefinitions.json으로 설정한다.

 

그럼 codepipeline 작업은 다 끝났다.

이제 소스코드가 정말 pr merge 되었을 때 cicd pipeline이 동작하는지 테스트를 해보자.

 

 

1. bitbucket에서 PR Merge를 호출한다.

 

2. slack으로 PR merge에 대한 webhook이 수신된다.

 

 

3. API gateway -> Lambda -> S3에 source 코드가 업로드 되고, Build가 진행된다.

 

 

 

4. Codebuild가 완료된다.

 

 

5. Deploy가 진행된다.

 

 

ECS 컨테이너로 와 보면, 기존엔 컨테이너가 1개 였는데 2개가 되었다가, 다시 1개가 된다.

 

 

해당 ECS의 서비스에서 이벤트를 봐보면,  새로운 task가 실행된 것이다.

(08:38:35, service Jane-service2 has started 1 tasks task d0~~)

 

롤링 업데이트를 포스팅 0번에서 설정했기 때문에,

새로운 컨테이너가 띄워지고 새 컨테이너가 정상 기동되면 이전 컨테이너가 종료된다.

 

그렇게 Deploy까지 완료되고, slack에서 aws chatbot을 통해 결과까지 받아보았다.

이 결과에 대한 리포트는, 코드 파이프라인의 '알림 규칙 생성'에서 만들 수 있다.

 

Succeeded와 Failed만 선택해야 알람이 깔끔하게 온다.

started까지 선택하면, 절차마다 started 라는 알람이 한 번 더 오기 때문에

그냥 결과에 대해서만(succeeded, failed) 설정하는 게 깔끔하다.

 

 

여기까지 ci/cd Pipeline 구축 완료!

반응형
반응형

CI/CD 구축을 위한 단계

  0. ECS fargate 생성 (Cluster, Service, task-definition, task)

1-1. Bitbucket에 코드 업로드 (git)

1-2. Bitbucket <-> slack 연동 (PR 알람 노티를 위함)

2-1. Bitbucket <-> AWS API Gateway 연동 with Webhook

2-2. API gateway -> Lambda 호출 

3. Lambda -> S3로 패키지 업로드

4. AWS Codepipeline: S3 -> Codebuild + ECR 연동

5. Codebuild -> Deploy(ECS fargate)

6. ECS <-> LB(ALB) 연동하여 동작 테스트 


이제 AWS의 Codepipeline을 사용하여 ci/cd pipeline을 만들어보자.

일단 개요를 만드는데, 이 파이프라인에 대한 로그, 아티팩트(결과물)을 남길 위치를 설정한다(고급설정).

나는 소스 코드가 있는 버킷을 아티팩트가 저장될 버킷으로 설정했다.

(어차피 버킷 내 경로는 다르므로)

 

그리고 소스 스테이지를 추가 하는데, s3에서 소스코드를 가져오므로 S3를 소스 공급자로 선택하고,

버킷은 소스코드가 올라가 있는 jane-s3-cicd로 설정한다.

그리고, 객체 키는 lambda에서 s3로 패키지를 업로드 시킬 때 사용했던 파일명을 가져온다. (사진은 임의로 입력한 값)

 // Key: `${repoConfig.projectName}/${repoConfig.repoName}/${repoConfig.branch}.zip`

 

 

이제 빌드 스테이지를 추가해야 하는데, 이건 Codebuild에서 생성할 수 있으므로 Codebuild로 넘어가보자.

 

기본 구성을 만든 후

 

 

소스는 아까 codepipeline에서 언급했던 것처럼, 소스코드가 담겨있는 공급자인 S3, jane-s3-cicd로 설정한다.

 

환경은 어차피 빌드를 할 환경이므로 codebuild에서 제공하는 이미지로 설정한다.

환경은 어차피 빌드를 할 환경이므로 codebuild에서 제공하는 이미지로 설정한다.

 

그리고 build 하는 데에 필요한 buildspec을 정의한다. (아래에서 언급)

 

 

아티팩트(결과물)은 ECR에 저장 + codepipeline으로 다시 이 코드빌드를 가져올 것이기 때문에 아티팩트 없음으로 둔다.

그렇게 빌드 프로젝트를 생성하고, 다시 codepipeline으로 돌아온다.

 

 

그러면, 이제 빌드 스테이지에서 AWS Codebuild를 선택하고, 생성했던 프로젝트 이름을 선택해서 배포 스테이지로 넘어간다.

 

배포 스테이지는 잠깐 건너뛰기 해놓고, 빌드 스테이지를 좀 더 보도록 하자. 

 

빌드 스테이지에서는 크게 두 가지 작업을 해 주어야 한다.

1) ECR(Elastic Container Repository) 생성: build 후 docker image가 저장될 저장소. deploy시 사용됨 

2) buildspec.yml: 빌드 과정을 정의해 놓은 파일

 

먼저 이미지가 저장될 ECR 부터 확인해보자.  (ECR은 ci/cd pipeline 포스팅 0번, ECS 생성에서 이미 만들었다.)

나는 아래 리포지토리를 쓸 것이기 때문에 아래 jane-n-repo 이름을 buildspec.yml에 넣으면 된다.

 

 

buildspec은 크게 install, pre_build, build, post_build 절차로 나뉜다. 

즉, 소스코드를 설치할 때 하는 작업들을 install: commands: 에서 해주고 (ECR 로그인 등)

빌드할 때는 docker build -t로 tagging을 해 주고,

빌드가 끝난 다음에는, docker image를 ECR로 push하는 절차들을 담고 있다.

 

artifacts는 이 build가 끝나면 남는 결과물을 저장하는 것인데, imagedefinitions.json에 위 post_build의 echo값

{name:IMAGE_NAME, imageUri: ECR_PATH:latest} 값이 저장된다.

이 값은 나중에 deploy를 할 때 참고하는 값이 된다.

 

그래서, 이 buildspec.yml까지 bitbucket에 함께 올려놔서, s3로 함께 업로드 시켜두면 된다 :)

그러면, 이제 소스코드가 s3로 업로드 될 때마다 소스 스테이지를 거쳐 빌드 스테이지를 진행하게 되고, 

로그가 남게 된다 :)

 

아까 codepipeline의 아티팩트를 s3에 저장되게 설정했으므로, s3에 해당 codepipeline의 이름을 가진 디렉토리가 생성되었다.

SourceArti/ 는 소스 스테이지에서 사용한 파일들이고,

BuildArtif/는 빌드 후 추출된 artifacts (buildspec.yml에서 설정되어 있던)가 저장되어 있다.

 

실제로 BuildArtif/를 열어보면, zip파일이 있는데

빌드를 할 때마다 생성되며, 아래와 같이 buildpsec.yml에서 언급한 imagedefinitions.json이 남아있다.

 

아래는 SourceArti/ 에 남은 소스 코드들이다. bitbucket에서 다운받은 파일들이 모두 들어가 있다.

 

여기까지 빌드 끝!

다음은 5. Codebuild -> Deploy(ECS fargate) 이다 :)

반응형
반응형

CI/CD 구축을 위한 단계

  0. ECS fargate 생성 (Cluster, Service, task-definition, task)

1-1. Bitbucket에 코드 업로드 (git)

1-2. Bitbucket <-> slack 연동 (PR 알람 노티를 위함)

2-1. Bitbucket <-> AWS API Gateway 연동 with Webhook

2-2. API gateway -> Lambda 호출 

3. Lambda -> S3로 패키지 업로드

4. AWS Codepipeline: S3 -> Codebuild + ECR 연동

5. Codebuild -> Deploy(ECS fargate)

6. ECS <-> LB(ALB) 연동하여 동작 테스트 


이제 bitbucket으로부터 /POST가 올 때 lambda까지 호출하는 절차를 구현했으므로,

Lambda에서 S3로 코드를 올리는 함수를 구현해보자.

 

로컬에서 사용할 함수들을 간단히 정의한 후, npm init -y으로 package.json을 생성했고

index.js와 pkg.json, node_modules를 zip으로 묶어 lambda에 upload 했다.

const AWS = require('aws-sdk');
const s3 = new AWS.S3();
const crypto = require('crypto');
const axios = require('axios');

exports.handler = async (event) => {
    try {
        console.log(`Incoming event: ${JSON.stringify(event)}`);
        const eventBody = JSON.parse(event.body);

일단 사용할 함수들을 정의하고, event를 JSON.stringiy로 찍어본다.

로그를 보면 아래와 같은 event가 들어온다.

 

2022-05-26T22:51:32.181Z 399fc240-aa51-411b-8537-26dbe8d6d578 INFO
{
   "resource":"/",
   "path":"/",
   "httpMethod":"POST",
   "headers":{
      "Accept":"*/*",
      "Content-Type":"application/json; charset=UTF-8",
      "Host":"ㅁㄴㅇㄹㅁㄴxecute-api.ap-northeast-2.amazonaws.com",
      "User-Agent":"Atlassian HttpClient 1.1.0 / Bitbucket-5.10.0 (5010000) / Default",
      "Via":"1.1 localhost (Apache-HttpClient/4.5.5 (cache))",
      "X-Amzn-Trace-Id":"Root=1-62900473-046a3026764d32b66cc4f0c5",
      ”X-Event-Key":"pr:comment:edited",   
      "X-Forwarded-For":"ㅁㄴㅇㄹ.201",
      "X-Forwarded-Port":"443",
      "X-Forwarded-Proto":"https",
      "X-Request-Id":"ㅁㄴㅇㄹㅁ7ffac28c454d"
      "X-Hub-Signature": "sha256=ㅁㄴㅇㄹㅁㅁㄴㅇㄹbㅁㅇㄹa20b",
   },
   "multiValueHeaders":{
      "Accept":[
         "*/*"
      ],
      "Content-Type":[
         "application/json; charset=UTF-8"
      ],
      "Host":[
         "ㅁㄴㄹㅇs.com"
      ],
      "User-Agent":[
         "Atlassian HttpClient 1.1.0 / Bitbucket-5.10.0 (5010000) / Default"
      ],
      "Via":[
         "1.1 localhost (Apache-HttpClient/4.5.5 (cache))"
      ],
      "X-Amzn-Trace-Id":[
         "Root=ㅁㄴㅇㄹㄴㅇㄹㅁㄴㄹㅇ66cc4f0c5"
      ],
      "X-Event-Key":[
         "pr:comment:edited"
      ],
      "X-Forwarded-For":[
         "ㅁㄴㄹㅇㅇㅇ1"
      ],
      "X-Forwarded-Port":[
         "443"
      ],
      "X-Forwarded-Proto":[
         "https"
      ],
      "X-Request-Id":[
         "ㅁㄴㄹㅁ8c454d"
      ]
   },
   "queryStringParameters":null,
   "multiValueQueryStringParameters":null,
   "pathParameters":null,
   "stageVariables":null,
   "requestContext":{
      "resourceId":"ㅁㄹㅇㄹak",
      "resourcePath":"/",
      "httpMethod":"POST",
      "extendedRequestId":"SㅁㄴㄹㅇTA=",
      "requestTime":"26/May/2022:22:51:31 +0000",
      "path":"/prod/",
      "accountId":"ㅁㄴㅇㄹㅁㄴ",
      "protocol":"HTTP/1.1",
      "stage":"prod",
      "domainPrefix":"ㅁㄴㅇㅇㄹh",
      "requestTimeEpoch":ㅁㅇㄹ353,
      "requestId":"ㅁㄴㄹㅇㄹ15ㅁㅇㄹb672d3f5ee",
      "identity":{
         "cognitoIdentityPoolId":null,
         "accountId":null,
         "cognitoIdentityId":null,
         "caller":null,
         "sourceIp":"ㅁㄹㄴㅇㅁㅇㄴ",
         "principalOrgId":null,
         "accessKey":null,
         "cognitoAuthenticationType":null,
         "cognitoAuthenticationProvider":null,
         "userArn":null,
         "userAgent":"Atlassian HttpClient 1.1.0 / Bitbucket-ㅁㄴㄹㅇ(ㅁㄴㄹ) / Default",
         "user":null
      },
      "domainName":"ㅁㄹㄹㅇ2.amazonaws.com",
      "apiId":"ㅁㄹㄴㅇvㅁㄴㄹh"
   },
   "body":"{\"eventKey\":\"pr:comment:edited\",\"date\":\"2022-05-27T07:51:31+0900\",\"actor\":{\"name\":\"jane\",\"emailAddress\":\"

중략.

 

이를 가지고, 헤더를 정규화해주고 (key를 소문자화) 인증서도 체크하고, 코드도 다운받고, 이를 s3로도 올린다.

코드는 아래 링크에 잘 설명되어 있으므로, 자세한 건 이로 대체하고

https://devlog-wjdrbs96.tistory.com/331

        const repoConfig = {
            serverUrl: process.env.BITBUCKET_SERVER_URL, 
            projectName: eventBody.pullRequest.toRef.repository.project.key, 
            repoName: eventBody.pullRequest.toRef.repository.name, 
            branch: eventBody.pullRequest.toRef.displayId, 
            token: process.env.BITBUCKET_TOKEN, 
        };


        // Upload the repository package to S3 bucket
        const s3Upload = await s3.upload({
            Bucket: process.env.S3BUCKET,
            ServerSideEncryption: 'AES256',
            Key: `${repoConfig.projectName}/${repoConfig.repoName}/${repoConfig.branch}.zip`,
            Body: file
        }).promise();
        console.log(s3Upload);

        console.log('Exiting successfully');
        return responseToApiGw(200, 'success');

업로드 하는 부분만 다뤄보자면, key를 event에서 받아온 정보들로 담은 후(projectName, repoName 등)

s3에 zip형태로 올린다.

여기서 말하는 Body: file은, 코드를 다운로드할 때 axios.req로 코드를 bitbucket으로부터 다운받은 뒤,

그의 응답data(=file)를 가져온 부분이다. 

 

 

그러면, 코드에서 언급한 디렉토리가 생성되고 그 아래에 zip파일까지 업로드되는 것을 확인할 수 있다.

 

 

끝.

다음 포스팅은 본격적으로 codepipeline을 생성해보자.

4. AWS Codepipeline: S3 -> Codebuild + ECR 연동

5. Codebuild -> Deploy(ECS fargate)

6. ECS <-> LB(ALB) 연동하여 동작 테스트 

반응형
반응형

CI/CD 구축을 위한 단계

  0. ECS fargate 생성 (Cluster, Service, task-definition, task)

1-1. Bitbucket에 코드 업로드 (git)

1-2. Bitbucket <-> slack 연동 (PR 알람 노티를 위함)

2-1. Bitbucket <-> AWS API Gateway 연동 with Webhook

2-2. API gateway -> Lambda 호출 

3. Lambda -> S3로 패키지 업로드

4. AWS Codepipeline: S3 -> Codebuild + ECR 연동

5. Codebuild -> Deploy(ECS fargate)

6. ECS <-> LB(ALB) 연동하여 동작 테스트 


이번엔 ci/cd를 위한 2-1, 2-2번째 단계로,

2-1. Bitbucket에서 PR merge가 발생했을 때 이를 AWS API Gateway로 던져주는 이벤트를 만들고

2-2. API Gateway에서 Lambda를 호출하는 절차까지 포스팅해본다 :)

 

 

2-1. 먼저, Bitbucket에서 PR merge가 발생했을 때,

Webhooks으로 AWS API Gateway에게 event를 보내는 절차를 진행한다.

이는 bitbucket에서 webhook을 보내줘야 하므로

bitbucket-> 내 Repository -> settings -> Webhooks 에 들어가서 Create webhook을 클릭한다.

어떤 값들이 필요한지 확인해보자.

 

PR merge시에만 api gateway를 호출하면 되므로, Pull request의 Merged를 체크하고, URL만 있으면 된다.

URL은 어디에 있을까?

 

AWS API Gateway앱으로 가보자.

먼저 Restful API를 생성한다.

이름만 넣고 생성.

 

그리고 어떤 path를 통해서 오든, POST를 처리해주기 위해 /POST 메서드를 만들어준다.

그리고 클라이언트로부터 해당 api가 호출될 경우, 별도로 생성해둔 Lambda로 그대로 프록시하도록 설정해준 후,

API 배포를 수행한다.

 

 

그럼 스테이지가 생성되고, URL이 생성된다.

이 URL을, 위의 Bitbucket에서 요구했던 URL에 넣으면 된다 :)

 

그리고, bitbucket에서 해당 URL로 "Test connection"을 하는 옵션도 있는데,

이를 확인하려면 람다 코드에서 x-event-key가 diagnostic:ping일 경우 200을 리턴하는 코드가 있어야 한다.

 

webhook이 올 때 event를 보면,

아래와 같이 header에 X-Event-Key가 실려오기 때문에 이를 기준으로 pr merge, ping등을 체크할 수 있다.

2022-05-26T22:51:32.181Z 399fc240-aa51-411b-8537-26dbe8d6d578 INFO
{
   "resource":"/",
   "path":"/",
   "httpMethod":"POST",
   "headers":{
      "Accept":"*/*",
      "Content-Type":"application/json; charset=UTF-8",
      "Host":"ㅁㄴㅇㄹㅇㅁㄴㄹㄴecute-api.ap-northeast-2.amazonaws.com",
      "User-Agent":"Atlassian HttpClient 1.1.0 / Bitbucket-5.10.0 (5010000) / Default",
      "Via":"1.1 localhost (Apache-HttpClient/4.5.5 (cache))",
      "X-Amzn-Trace-Id":"Root=1ㅁㄴㄹㅇㅁㄹ026764d32b66cc4f0c5",
      ”X-Event-Key":"pr:comment:edited",   // 이건 PR에 댓글을 수정했을 때
      "X-Forwarded-For":"ㅁㅇㄴㄹㅁ01",
      "X-Forwarded-Port":"443",
      "X-Forwarded-Proto":"https",

 

그래서, diagnostic:ping에 대해 응답하는 코드를 넣으면,

이렇게 200ok가 뜨게 된다 :)

 

끝-!

다음은 3. Lambda -> S3로 패키지 업로드 예정 :)

반응형

+ Recent posts