반응형

문제

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에 올리면 된다 :)

 

반응형
반응형

상황

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

 

반응형
반응형

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) 연동하여 동작 테스트 


이번엔 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로 패키지 업로드 예정 :)

반응형
반응형

 

CI/CD 구축을 위한 단계

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

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

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

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

3. API gateway -> Lambda 호출 

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

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

6. Codebuild -> Deploy(ECS fargate)

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

 

CI/CD 구축을 위한 첫 번째 절차는, 일단 코드를 배포하기 위한 타겟, 즉 컨테이너를 생성하는 것이다.

이 컨테이너를 관리해주는 게 ECS(Elastic Container Service)인데, ECS 앱을 통해 컨테이너를 생성한다.

컨테이너를 생성하기 위해서는, 

1) 클러스터 생성

2) 작업 정의(Task-definition) 생성

3) 서비스(Service) 생성이 필요하다.

 

 

1) 클러스터 생성

EC2가 아닌 Fargate를 생성할 예정이므로, 네트워킹 전용을 고른다.

 

나중에 태그 값으로 생성 리소스들을 확인해보기 위해 태그도 붙인다.

 

 

벌써 클러스터 생성이 완료되었다. 클러스터 생성은 큰 어려움이 없다.

 

 

2) 작업 정의(Task-definition) 생성

이제는 이 클러스터 내 컨테이너가 어떤 이미지를 쓰고, 자원들은 얼마나 쓸 것인지 정의하는

작업 정의(Task-Definition)을 생성해야 한다. 왼쪽 메뉴에서 '작업 정의'를 클릭한다. 

역시 Fargate를 생성할 예정이므로 Fargate를 선택한다.

 

이름, IAM, 컨테이너 정의를 수행한다.

(이미지를 불러오려면 미리 ECR에 이미지를 push 해놨어야 한다.)

앞으로 이 작업 정의를 사용하는 컨테이너는 위 이미지의 태그값을 사용한다. (그러니 신중하게 하자... 변경하려면 다시 생성해야 한다)

 

ECR에 미리 push해 둔 이미지 파일들. URI를 복사해서 위 컨테이너 이미지 링크로 넣는다.

 

그리고 작업정의를 이어서 진행한다.

가벼운 코드이므로 작업 크기는 작게 잡았다.

 

작업 정의도 완료되었다.

 

3) 서비스(Service) 생성

다음은 서비스 생성이다.

서비스란 클러스터의 앞단에서 이 클러스터 내 컨테이너를 몇 개 띄울 것인지, scaling은 어떻게 할 것인지(optional),

로드밸런싱 등의 설정을 하는 곳이다.

역시 FARGATE로 시작할 예정이므로 FARGATE로 설정하고, 작업 정의는 2)에서 정의했던 정의를 들고온다.

클러스터는 1)에서 정의한 클러스터를 들고온다.

 

그리고 패키지가 업데이트될 때마다 롤링업데이트를 진행할 예정으로, 롤링 업데이트를 설정하고

(롤링 업데이트: 패키지 배포시마다 컨테이너 전체에 업데이트를 한 번에 진행하는 게 아니라, 컨테이너를 하나씩 업데이트해서 서비스 중단이 없게끔 하는 업데이트 방식)

 

로드 밸런싱을 설정한다.

로드 밸런서는 당장은 설정하지 않아도 문제없으나, 결국엔 패키지가 제대로 배포되었는지 확인을 위해(curl을 통해 확인)

LB까지 설정을 진행한다.  

여기서 로드 밸런싱할 컨테이너는 2)에서 만든 작업정의의 컨테이너이므로 Jane-test:3002가 맵핑되어 있다.

이제 로드밸런서에 80포트를 통해 3002번 포트에게 req가 오면, 그걸 Jane-target-grp3이라는 대상으로 보낸다.

 

사실 :3002를 direct로 사용한다기 보다는, 보통 path를 통해 curl 확인을 하므로 코드에서 설정한 http 경로를 입력한다.

예를들어, 나는 코드에서 아래와 같이 /jane_으로 get을 했을 경우 이렇게 프린트 하라고 정의했기 때문에, 경로를 /jane_으로 입력해준다.

(평가순서는, :80을 사용하는 다른 서비스도 많기 때문에 /path로 분기를 하는데, 어떤 path를 먼저 볼 것인지 하는 우선순위이다.)

 

이렇게 다 설정을 하면, 서비스도 생성이 완료되었다.

 

 

그리고 서비스에 따라 작업(컨테이너)이 생성되었다.

끝!

반응형
반응형

현상

AWS codebuild를 통해 빌드를 하고 있는데, buildspec.yml을 읽는 과정에서 아래와 같은 에러가 발생했다.

 

[Container] 2022/05/31 20:23:56 Running command docker build -t $IMAGE_NAME .
Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?

[Container] 2022/05/31 20:23:58 Command did not exit successfully docker build -t $IMAGE_NAME . exit status 1
[Container] 2022/05/31 20:23:58 Phase complete: INSTALL State: FAILED
[Container] 2022/05/31 20:23:58 Phase context status code: COMMAND_EXECUTION_ERROR Message: Error while executing command: docker build -t $IMAGE_NAME .. Reason: exit status 1

 

 

해결

다행히 말 그대로 도커 데몬에 연결할 수 없다는 것인데, aws의 troubleshooting 사이트에 올라와 있는 에러였다.

https://docs.aws.amazon.com/ko_kr/codebuild/latest/userguide/troubleshooting.html

 

똑같이 따라가본다.

Codebuild > 프로젝트 > 내 프로젝트 > 환경 편집 > 이미지 재정의

 

난 가장 하단에 '권한이 있음'을 체크하지 않았었다.

이부분을 체크해준다.

 

 

그리고 다시 빌드를 했더니, 돌아간다 :)

 

끝 

(또 다른 이슈 발생,,,)

반응형
반응형

ec2를 pc에서 접속 시 public ip와 인증서(.pem)을 통해 ssh로 접속하는데, 접속이 안될 때가 있다.

(휴대폰 테더링을 통해 pc를 인터넷에 연결하면 그렇다.)

이 경우.. 아래와 같이 도메인으로 접속해보면 된다.

 

Before

ssh -i [인증서].pem -p [접속할 local port] ec2-user@[접속할 ec2의 public ip]

 

After

ssh -i [인증서].pem -p 20022 ec2-user@[접속할 ec2의 도메인 주소]

반응형
반응형

이전 포스팅에서, cloudwatch에서 알람이 발생할 때마다 SNS, 그리고 lambda를 trigger해서 slack으로 알람을 보냈다.

* 이전 포스팅:

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

 

[AWS] cloudwatch, lambda를 통해 slack으로 알람 보내기 (Nodejs)

Cloudwatch에서 알람 조건을 만들어서, 해당 알람이 발생할 때 slack으로 notify하는 환경을 만들어보자. 1. AWS > CloudWatch > 경보 생성 1) 지표 선택 2) 지표에서 원하는 tracking값 및 임계치 선택 (알람..

countrymouse.tistory.com

 

그런데, alarm으로 받는 text의 포맷이 아래와 같이 가독성이 떨어진다. 그래서, 보다 가독성 있게 수정해보기로 한다.

 

위 가독성 없는 텍스트를 아래 포맷으로 수정한다.

 

코드는 아래 티스토리 블로거 향로님의 코드를 사용하였고 아래는 한줄한줄 해석을 달아보았다. 

(직접 하나씩 만들어보려고 했는데, 반나절의 삽질을 해도 원하는 대로 잘 안돼서 다른 분의 코드를 사용하기로 했다...)

https://jojoldu.tistory.com/586

 

CloudWatch 이상 지표를 슬랙 알람으로 받기 (feat. SNS, Lambda)

AWS 서비스를 이용하면 CloudWatch를 통해 서비스의 이상 지표를 손쉽게 확인할 수 있습니다. 이를테면 다음과 같은 경우인데요. 평소보다 로드밸런서로 들어오는 요청양이 2배이상 높다거나 RDS의 C

jojoldu.tistory.com

 

향로님 코드에 cause부분과 관련된 코드는 삭제하였고(나는 '원인'을 '설명'으로 대체했다), 일부분에 주석을 추가하였다.

// 구성 -> 환경변수로 webhook을 받도록 합니다.
const ENV = process.env
if (!ENV.webhook) throw new Error('Missing environment variable: webhook')

const webhook = ENV.webhook;
const https = require('https')

const statusColorsAndMessage = {
    ALARM: {"color": "danger", "message":"위험"},
    INSUFFICIENT_DATA: {"color": "warning", "message":"데이터 부족"},
    OK: {"color": "good", "message":"정상"}
}


// 1. 여기서부터 시작 
exports.handler = async (event) => {
    await exports.processEvent(event);
}


// 2. processEvent로 넘어간다.
exports.processEvent = async (event) => {
    console.log('Event:', JSON.stringify(event))
    const snsMessage = event.Records[0].Sns.Message;
    console.log('SNS Message:', snsMessage);
    const postData = exports.buildSlackMessage(JSON.parse(snsMessage))  // 3. buildSlackMessage
    await exports.postSlack(postData, webhook);
}

// snsMessage는 이전 포스팅 값들이 담겨 있는데, 참고해보자.


// 3. buildSlackMessage(위 jane_bot2에서 출력한 snsMessage를 받아와서 각각 처리한다.)
exports.buildSlackMessage = (data) => {
    const newState = statusColorsAndMessage[data.NewStateValue];  // 위 jane_bot2 기준 ALARM
    const oldState = statusColorsAndMessage[data.OldStateValue];     // 위 jane_bot2 기준 OK
    const executeTime = exports.toYyyymmddhhmmss(data.StateChangeTime); // 2022-05-23T03:16:02.297+0000
    const description = data.NewStateReason;  // 기존 코드엔 description, cause가 둘 다 있지만 나는 description에 대체했다.
    return {
        attachments: [
            {
                title: `[${data.AlarmName}]`,
                color: newState.color,
                fields: [
                    {
                        title: 'Timestamp',
                        value: executeTime
                    },
                    {
                        title: 'Alarm',
                        value: description
                    },
                    {
                        title: 'Previous Status',
                        value: oldState.message,
                        short: true
                    },
                    {
                        title: 'Current Status',
                        value: `*${newState.message}*`,
                        short: true
                    },
                    {
                        title: 'Link',
                        value: exports.createLink(data)
                    }
                ]
            }
        ]
    }
}

// CloudWatch 알람 바로 가기 링크
exports.createLink = (data) => {
    return `https://console.aws.amazon.com/cloudwatch/home?region=${exports.exportRegionCode(data.AlarmArn)}#alarm:alarmFilter=ANY;name=${encodeURIComponent(data.AlarmName)}`;
}

exports.exportRegionCode = (arn) => {
    return  arn.replace("arn:aws:cloudwatch:", "").split(":")[0];
}


// 타임존 UTC -> KST (2022-05-23T03:16:02.297+0000 --> 2022-05-23 13:14:48)
exports.toYyyymmddhhmmss = (timeString) => {

    if(!timeString){
        return '';
    }

    const kstDate = new Date(new Date(timeString).getTime() + 32400000);

    function pad2(n) { return n < 10 ? '0' + n : n }

    return kstDate.getFullYear().toString()
        + '-'+ pad2(kstDate.getMonth() + 1)
        + '-'+ pad2(kstDate.getDate())
        + ' '+ pad2(kstDate.getHours())
        + ':'+ pad2(kstDate.getMinutes())
        + ':'+ pad2(kstDate.getSeconds());
}  

exports.postSlack = async (message, slackUrl) => {
    return await request(exports.options(slackUrl), message);
}

exports.options = (slackUrl) => {
    const {host, pathname} = new URL(slackUrl);
    return {
        hostname: host,
        path: pathname,
        method: 'POST',
        headers: {
            'Content-Type': 'application/json'
        },
    };
}

function request(options, data) {

    return new Promise((resolve, reject) => {
        const req = https.request(options, (res) => {
            res.setEncoding('utf8');
            let responseBody = '';

            res.on('data', (chunk) => {
                responseBody += chunk;
            });

            res.on('end', () => {
                resolve(responseBody);
            });
        });

        req.on('error', (err) => {
            console.error(err);
            reject(err);
        });

        req.write(JSON.stringify(data));
        req.end();
    });
}

 

완성!

반응형
반응형

Cloudwatch에서 알람 조건을 만들어서, 해당 알람이 발생할 때 slack으로 notify하는 환경을 만들어보자.

 

 

1. AWS > CloudWatch > 경보 생성

1) 지표 선택

 

2) 지표에서 원하는 tracking값 및 임계치 선택 (알람 발생을 위해 임계값은 1로 설정)

 

3) SNS 연동 (이전 포스팅에서 만든 jane-test-sns2)

 - 이전 포스팅: https://countrymouse.tistory.com/entry/awsslack

 

[AWS] Lambda와 slack 연동하기 (Nodejs)

1. slack에서 app을 생성한다.  1) slack에 접속하여 메뉴 중 앱 > 앱 디렉터리를 클릭하면 된다. https://api.slack.com/apps?new_app=1 2) 그럼 아래 창이 나오는데, 오른쪽 상단의 '구축'을 누르면 된다. 3)..

countrymouse.tistory.com

+ 여기서 알림을 추가하고, 정상 상태에서도 알람이 발생하도록 정상 알림도 추가한다.

 

 

4) 이름 설정 후 미리보기, 경보 생성

5) 경보가 생성되었음. 데이터가 5분 주기므로 기다려보기.

 

 

2. SNS 앱 확인

 : 아까 cloudwatch에서 연동해둔 SNS(jane-test-sns2)를 보니 lambda가 구독되어 있다.

그렇다면, cloudwatch에 알람이 발생하면, SNS를 통해, Lambda가 트리거 될 것이다.

 

3. lambda의 index.json 코드

cloudwatch를 통해 event를 받을 때 어떤 값들이 수신되는지 확인해봤다. // console.log(message)

exports.handler = async (event, context) => {
    //console.log('Received event:', JSON.stringify(event, null, 2));
    const message = event.Records[0].Sns.Message;
    console.log('From SNS:', message);
    return message;
};

 

4. Slack

 (jane_bot2 is testing은 무시하시길... 테스트 했던 console log다.)

그럼 예상했던 대로, cloudwatch에서 생성한 알람이 발생할 때마다 SNS를 호출시키고, 그게 lambda를 호출시켜서, slack으로 온다.

EC2 -> Cloudwatch -> SNS -> Lambda -> slack 구조다.

 

그럼 다음 포스팅에서는 이 text들을 예쁘게 만들어보기로 한다. (삽질 엄청했다....)

반응형

+ Recent posts