IT STUDY LOG

[SECTION2] <AWS 배포 자동화> DAY 1 LOG 본문

devops bootcamp 4/project log

[SECTION2] <AWS 배포 자동화> DAY 1 LOG

roheerumi 2023. 4. 27. 22:42

#학습 목표

  • WAS를 Docker Image로 빌드하여 컨테이너화
  • 컨테이너화한 WAS를 Registry에 Push
  • 기존에 배포된 Docker Image를 활용/실행
  • mongoDB 기준
  • Docker Compose를 이용해, WAS와 DB를 한 번에 실행
  • WAS 이미지 빌드 및 push 자동화를 구현
  • ECR 서비스를 이용하는 방법을 배웁니다

 

#해결 과제

💡 마일스톤1 - Hello World 서버 컨테이너화

💡 마일스톤2 - docker-compose 작성

💡 마일스톤3 - 이미지 repository push 자동화

 

#과제 항목별 진행 상황

✏️  마일스톤1 - Hello World 서버 컨테이너화

Step 1 : 연습과제: Hello World 서버 작성

1. fastify-cli 설치

$ npm i --global fastify-cli

2. fastify 프로젝트 생성 후 npm install

$ fastify generate helloworld-was
$ npm install

3. 로컬에서 서버 기동 확인

$ npm run start

 

Step 2 : 실전과제: 서버 컨테이너화와 레지스트리로 push

1. Fastify로 만든 Hello World 서버를 컨테이너화하기 위해 Dockerfile을 작성

# file name : ../Devops-04-S2-Team9/helloworld-was/Dockerfile

# 사용할 이미지 지정
FROM node:18-alpine
ENV NODE_ENV=production 

# 이미지 안에 애플리케이션 코드를 넣기 위한 디렉토리 설정
WORKDIR /app

# 가능한 경우(npm@5+) package.json과 package-lock.json을 모두 복사하기 위해 와일드카드를 사용
COPY ["package.json", "package-lock.json*", "./"]

# 이 이미지에는 이미 Node.js와 NPM이 설치되어 있으므로 npm 바이너리로 앱의 의존성을 설치하기만 하면 됨
# 앱 의존성 설치
RUN npm install --production

# 도커 이미지 안에 앱의 소스 코드를 추가
COPY . .

# 앱이 바인딩 되어 있는 포트와 docker 데몬 매핑
#EXPOSE 3000

# 런타임을 정의하는 CMD로 앱 실행 명령어를 정의(서버를 구동하도록 node app.js을 실행하는 기본 npm start을 사용)
CMD npm start

2. tag는 1.0으로 이미지 빌드 후, 로컬에서 실행해 서버가 잘 작동되는지 확인

# 빌드
$ docker build --tag <레파지토리명>/이미지명:tag .

# 이미지가 잘 빌드되었는지 확인
$ docker images

# -p 옵션은 호스트 포트:컨테이너 포트
# -d 옵션은 background 실행
$ docker run --name <컨테이너명> -p 3000:3000 -d <레파지토리명>/이미지명:tag

# 요청 확인
$ curl http://localhost:3000

3. AWS의 액세스 키와 시크릿을 이용해 CLI로 로그인하고, ECR에 이미지를 push

3-1. ECR 프라이빗 리포지토리 생성

  1. Amazon ECR 콘솔(https://console.aws.amazon.com/ecr/repositories) > 탐색 모음에서 리포지토리를 생성할 리전을 선택
  2. 탐색 창에서 리포지토리를 선택
  3. 리포지토리(Repositories) 페이지에서 프라이빗(Private) 탭을 선택한 다음 리포지토리 생성(Create repository)을 선택
  4. 가시성 설정(Visibility settings)에서 프라이빗(Private)이 선택되었는지 확인
  5. 리포지토리 이름(Repository name)에 리포지토리의 고유한 이름을 입력 (리포지토리 이름은 자체적으로 지정할 수 있으며(예: nginx-web-app), 리포지토리를 범주로 그룹화하기 위해 네임스페이스에 추가 가능)
  6. 태그 불변성(Tag immutability)에서 리포지토리의 태그 변경 가능 설정을 선택 (변경 불가능 태그로 구성된 리포지토리는 이미지 태그를 덮어쓰는 것을 방지해 줌, 자세한 내용은 이미지 태그 변경 가능성 섹션을 참조)
  7. 푸시 시 스캔의 경우 기본 스캔을 위해 리포지토리 수준에서 스캔 설정을 지정할 수 있지만 프라이빗 레지스트리 수준에서 스캔 구성을 지정하는 것 권장(프라이빗 레지스트리에서 스캔 설정을 지정하면 고급 스캔 또는 기본 스캔을 사용하고 필터를 정의하여 스캔할 리포지토리를 지정 가능, 자세한 내용은 이미지 스캔 섹션을 참조)
  8. KMS 암호화(KMS encryption)의 경우, AWS Key Management Service를 사용하여 리포지토리에 있는 이미지의 암호화를 활성화할지 선택(자세한 내용은 저장된 데이터 암호화 섹션을 참조)
  9. KMS 암호화가 활성화된 경우 고객 암호화 설정(고급)(Customer encryption settings (advanced))을 선택하여 고유의 KMS 키를 선택 (KMS 키가 클러스터와 동일한 리전에 있어야하며,  AWS KMS 키 생성(Create an key)을 선택하고 AWS KMS 콘솔로 이동하여 고유한 키를 생성)
  10. 리포지토리 생성(Create repository)을 선택
  11. (선택 사항) 생성한 리포지토리를 선택하고 푸시 명령 보기(View push commands)를 선택하여 이미지를 새 리포지토리에 푸시하는 단계 확인 가능 (리포지토리로 이미지를 푸시하는 방법에 대한 자세한 내용은 이미지 푸시 섹션을 참조)

3-2. ECR 프라이빗 리포지토리에 Docker 이미지 푸시

# AWS CLI를 이용해 ECR 레파지토리에 로그인
$ aws ecr get-login-password --region <ECR REGION> | docker login --username AWS --password-stdin 123456789012.dkr.ecr.<ECR REGION>.amazonaws.com

# 필요시 AWS 설정값 설정
$ aws configure

# 로그인 완료 후 이미지 태그를 조회하기 위해 도커 이미지 조회
docker images

# 푸시할 이미지 태그에 태깅
docker tag e9ae3c220b23 123456789012.dkr.ecr.<ECR REGION>.amazonaws.com/<레파지토리명>:<태그>

# ECR 레파지토리로 푸시
docker push 123456789012.dkr.ecr.<ECR REGION>.amazonaws.com/<레파지토리명>:<태그>

3-3. ECR 콘솔 > 레파지토리로 접근해서 정상적으로 push되었는지 확인

 

✏️ 마일스톤2 - docker-compose 작성

Step 1 : 연습과제: mongoDB 도커로 실행하기

1. docker hub에 배포된 mongodb 이미지를 이용해 컨테이너 구동

- 옵션 등 자세한 내용은 docker hub의 mongo 이미지 페이지에서 확인 가능

# 이미지가 없을 경우 레지스트리에서 자동으로 pull
$  docker run --name some-mongo -d mongo:latest

# port 지정해 mongodb 컨테이너 구동
$  docker run --name some-mongo -p 27017:27017 -d mongo:latest 

# 초기화 root user와 password를 지정해 컨테이너 구동
$  docker run -d --name some-mongo -e MONGO_INITDB_ROOT_USERNAME=root -e MONGO_INITDB_ROOT_PASSWORD=secret -p 27017:27017 mongo:latest

2. mongoDB compass를 이용해 실제 컨테이너에 접속 가능한지 확인

3. docker compose 파일을 이용해 컨테이너 실행

version: '3.1'

services:
  mongo: # 서비스명 지정
    image: mongo # 사용할 이미지 지정 (이 경우 태그를 생략하고 latest 버전으로 사용)
    ports: # host 포트와 container 포트를 매핑
      - "27017:27017"
    environment: # 초기 환경 설정
      MONGO_INITDB_ROOT_USERNAME: root
      MONGO_INITDB_ROOT_PASSWORD: secret
    container_name: mongodb # 컨테이너명을 지정

 

Step 2 : 실전과제: 서버와 mongodb를 한꺼번에 docker compose로 실행하기

1. 웹 서버 코드를 변경하여, mongodb와의 접속을 확인

1-1. dotenv 설치 후 .env 파일 생성해 DB 연결 관련 정보 설정

$npm i dotenv
# file name : ../Devops-04-S2-Team9/helloworld-was/.env

DB_HOSTNAME=mongo
DB_USERNAME=root
DB_PASSWORD=secret
DB_DATABASE=devops04
DB_PORT=27017

1-2. fas @fastify/mongodb 설치 후 plugins 디렉토리 하위에 DB 연결 register를 등록하기 위해  mongodb.js 생성

$ npm i @fastify/mongodb
// file name : ../Devops-04-S2-Team9/helloworld-was/plugins/mongodb.js

const fp = require('fastify-plugin')
const { DB_HOSTNAME, DB_USERNAME, DB_PASSWORD, DB_PORT, DB_DATABASE} = process.env

module.exports = fp(async function (fastify, opts) {
  
  fastify.register(require('@fastify/mongodb'), {
    // force to close the mongodb connection when app stopped
    // the default value is false
    forceClose: true,
    url: `mongodb://${DB_USERNAME}:${DB_PASSWORD}@${DB_HOSTNAME}:${DB_PORT}/?authMechanism=DEFAULT`
  })  
})

1-3. 웹 서버에서 실제로 db에 잘 연결되었는지 확인하기 위한 테스트 코드 작성

// file name : ../Devops-04-S2-Team9/helloworld-was/routes/example/index.js

'use strict'

module.exports = async function (fastify, opts) {
  fastify.get('/', async function (request, reply) {
    console.log(`[INFO] Mongo DB Connection Try..`)
    const client = await fastify.mongo.client.connect();
    try {
      if (client !== null) {
        console.log(`[INFO] Mongo DB Connection Success!`)
        console.log(client)
        reply.code(200).send(`Mongo DB Connection Success!`)
      }
    } finally {
      client.close();
    }
  })
}

2. tag 1.1로 웹 서버 이미지를 새롭게 빌드

# 빌드
$ docker build --tag <레파지토리명>/helloworld:1.1 .

# 이미지가 잘 빌드되었는지 확인
$ docker images

3. 컨테이너화 한 웹 서버 이미지와, mongodb를 동시에 실행하기 위한 docker compose 파일 작성

# file name : ../Devops-04-S2-Team9/docker-compose.yml
version: '3.1'

services:
  fastify:
    image: "<레파지토리명>/helloworld:1.1"
    ports:
      - "3000:3000"
    container_name: server
    links:
      - mongo
    restart: on-failure # 서버 기동 실패 시 재기동

  mongo:
    image: mongo
    ports:
      - "27017:27017"
    environment:
      MONGO_INITDB_ROOT_USERNAME: root
      MONGO_INITDB_ROOT_PASSWORD: secret
    container_name: mongodb

4. docker compose up 명령어를 이용해 두 서버가 정상적으로 기동되는지 확인 및 브라우저에 접속해 연결 확인

$ docker compose up

5. docker compose down 명령어를 이용해 기동된 컨테이너 종료

$ docker compose down

 

✏️ 마일스톤3 - 이미지 repository push 자동화

Step 1 : 실전과제: 레지스트리 push 자동화

1. 빌드 자동화를 위한 githubaction yaml 파일 작성

 

Docker 이미지 게시 - GitHub Docs

소개 이 가이드에서는 Docker 빌드를 수행하는 워크플로를 만든 다음, Docker 이미지를 Docker Hub 또는 GitHub Packages에 게시하는 방법을 보여 줍니다. 단일 워크플로를 사용하여 단일 레지스트리 또는

docs.github.com

2. GithubAction을 통해 AWS ECR에 PUSH하기 위한 IAM 역할 생성

3. 이미지 빌드 및 AWS ECR 푸시 자동화를 위한 GithubAction File 작성

# file name : ../Devops-04-S2-Team9/.github/workflows/ecr-build-push.yml
name: build and push docker image to AWS ECR

on:
  push:
    branches: [ "main" ]
  pull_request:
    branches: [ "main" ]
  
env:
  role_arn: ${{ secrets.ECR_ROLE_ARN }}
  aws_region: "ap-northeast-2"
  repository: "레파지토리명"
  aws_image_tag: ${{ github.sha }}
  image_name: "이미지명"

jobs:
  build_and_push_ecs: # job 고유 식별자
    name: "# ECR 로그인 후 도커 이미지 BUILD, ECR에 PUSH"
    runs-on: ubuntu-latest

    permissions:
      id-token: write   # This is required for requesting the JWT
      contents: read    # This is required for actions/checkout

    steps:
      - name: "1. github runner에 레파지토리 체크아웃"
        uses: actions/checkout@v3

      # Before each of the following examples, make sure to include the following:
      - name: "2. AWS credential 설정"
        uses: aws-actions/configure-aws-credentials@v2
        with:
          aws-region: ${{ env.aws_region }}
          role-session-name: GitHubActions
          role-to-assume: ${{ env.role_arn }}

      # Login to Amazon ECR Private, then build and push a Docker image: (cf. public)
      - name: "3. AMAZON ECR 로그인"
        id: login-ecr
        uses: aws-actions/amazon-ecr-login@v1

      - name: "4. 도커 이미지 Build & AMAZON ECR에 도커 이미지 PUSH"
        run: |
          cd ./helloworld-was
          docker build -t ${{ steps.login-ecr.outputs.registry }}/${{ env.repository }}:${{ env.aws_image_tag }} .
          docker push ${{ steps.login-ecr.outputs.registry }}/${{ env.repository }}:${{ env.aws_image_tag }}

 

#TROUBLE SHOOTING LOG

📝 문제 1 : fastify 컨테이너 구동 실패 (exited) (1)

  • 원인 : Dockerfile cmd를 node app.js로 작성했기 때문
  • 해결 방안 : node 명령어를 npm run start로 변경
  • 참고
    1. node 명령어: 단순히 자바스크립트를 서버로 구동하는 명령어
    2. npm start: package.json에의 scripts에서 start로 실행하는 명령어

 

📝 문제 2 : fastify 컨테이너 구동 실패 (exited) (2)

  • 원인 : fastify 노출 포트와 container 구동 시 8000:8000포트로 구동해서 fastify 기본 포트인 3000포트와 불일치
  • 해결 방안 : 3000포트로 컨테이너 구동

 

📝 문제 3 : fastify - mongo db 연동 시 서버 timeout 종료

  • 원인 : mongodb가 로드되기 전에 fastify에서 참조하기 때문에 발생한 현상
  • 해결 방안 : docker-compose.yml에 restart 지시어를 이용해 기동 실패시 재시도하도록 수정

 

📝 문제 4 : mongodb 커넥션 연결 확인

 

📝 문제 5 : 아마존 ECR IAM ROLE 생성

  • 상황 : access key를 이용하지 않고 ECR에 로그인하기 위해 IAM ROLE 생성
  • 해결 방안 : OpenID Connection을 이용해 GitHub Actions 워크플로가 액세스 키, 시크릿 키를 노출하지 않고 AWS(Amazon Web Services)의 리소스에 액세스 가능
  • 해결 과정

1. IAM 자격 증명 공급자 리소스를 생성

2. 웹 자격 증명 또는 OIDC를 위한 역할 생성

  1. 탐색 창에서 역할(Roles)을 선택한 후 역할 생성(Create role)을 선택
  2. 웹 ID(Web Identity) 역할 유형을 선택
  3. 자격 증명 공급자(Identity provider)에서 역할의 IdP를 선택
  4. Audience에서 지정한 자격증명 공급자의 대상 선택
  5. 권한 정책을 사용하기 위한 정책을 선택하거나 정책 생성(Create policy)을 선택하여 새 브라우저 탭을 열고 완전히 새로운 정책을 생성할 수 있음(자세한 내용은 IAM 정책 생성 섹션을 참조)
    • 웹 ID 사용자에게 부여하려는 권한 정책 옆의 확인란을 선택 원할 경우, 여기서 정책을 선택하지 않고 나중에 정책을 만들어서 역할에 연결할 수 있음 (기본적으로 역할은 권한이 없음)
  6. 역할 이름(Role name)에 역할 이름을 입력
    • 역할 이름은 AWS 계정 내에서 고유해야하며 대소문자를 구분하지 않음
    • 다른 AWS 리소스가 역할을 참조할 수 있기 때문에 역할을 생성한 후에는 역할 이름을 편집할 수 없음
  7. 역할에 대한 사용 사례와 권한을 편집하려면 1단계: 신뢰할 수 있는 엔터티 선택(Step 1: Select trusted entities) 또는 2단계: 권한 추가(Step 2: Add permissions) 섹션에서 편집(Edit)을 선택
  8. 역할을 검토한 다음 [Create role]을 선택

3. GitHub OIDC ID 제공자의 역할 구성

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": { # OIDC Role ARN
                "Federated": "arn:aws:iam::123456123456:oidc-provider/token.actions.githubusercontent.com"
            },
            "Action": "sts:AssumeRoleWithWebIdentity",
            "Condition": {
                "StringLike": {
                    "token.actions.githubusercontent.com:sub": "repo:<GitHub 조직명>/<레파지토리명>:*"
                },
                "StringEquals": {
                    "token.actions.githubusercontent.com:aud": "sts.amazonaws.com"
                }
            }
        }
    ]
}
클레임 클레임 유형 설명
aud 사용자 - 기본적으로 리포지토리를 소유하는 organization 같은 리포지토리 소유자의 URL
- 사용자 지정할 수 있는 유일한 클레임도구 키트 명령
- core.getIDToken(audience)을 사용하여 사용자 지정 대상 그룹을 설정 가능
iss 발급자 -OIDC 토큰 발급자: https://token.actions.githubusercontent.com
sub 제목 - 클라우드 공급자가 유효성을 검사할 주체 클레임을 정의
- 이 설정은 액세스 토큰이 예측 가능한 방식으로만 할당되도록 하는 데 필수

4. ECR 로그인을 위한 권한 부여

4-1. ECR Private에 로그인하기 위한 최소 권한 집합

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "GetAuthorizationToken",
      "Effect": "Allow",
      "Action": [
        "ecr:GetAuthorizationToken"
      ],
      "Resource": "*"
    }
  ]
}

4-2. ECR 프라이빗 리포지토리에서 이미지를 가져오기 위한 최소 권한

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowPull",
      "Effect": "Allow",
      "Action": [
        "ecr:BatchGetImage",
        "ecr:GetDownloadUrlForLayer"
      ],
      "Resource": "arn:aws:ecr:<리전>:<AWS 계정 ID>:repository/<레파지토리명>"
    }
  ]
}

4-3. ECR 프라이빗 리포지토리에서 이미지를 푸시하고 풀링하기 위한 최소 권한

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowPushPull",
      "Effect": "Allow",
      "Action": [
        "ecr:BatchGetImage",
        "ecr:BatchCheckLayerAvailability",
        "ecr:CompleteLayerUpload",
        "ecr:GetDownloadUrlForLayer",
        "ecr:InitiateLayerUpload",
        "ecr:PutImage",
        "ecr:UploadLayerPart"
      ],
      "Resource": "arn:aws:ecr:<리전>:<AWS 계정 ID>:repository/<레파지토리명>"
    }
  ]
}

 

#References

node.js 에서 Dockerfile을 이용해 이미지를 만드는 방법

https://docs.docker.com/compose/compose-file/

 

Overview

 

docs.docker.com

https://hub.docker.com/_/mongo

 

mongo - Official Image | Docker Hub

Quick reference Supported tags and respective Dockerfile links Note: the description for this image is longer than the Hub length limit of 25000, so the "Supported tags" list has been trimmed to compensate. See also docker/hub-feedback#238 and docker/roadm

hub.docker.com

https://github.com/fastify/fastify-mongodb

 

GitHub - fastify/fastify-mongodb: Fastify MongoDB connection plugin

Fastify MongoDB connection plugin. Contribute to fastify/fastify-mongodb development by creating an account on GitHub.

github.com

https://stackoverflow.com/questions/31362021/npm-start-vs-node-app-js

 

npm start vs node app.js

I'm extremely new to Node and trying to get my head around app basics. I'm curious as to why these two commands: node app.js --vs-- npm start output the same thing to the console and appear...

stackoverflow.com

https://browsenpm.org/

 

Browsenpm.org | Nodejitsu Inc.

For the full table of contents see below, but first here is a quick cheatsheet of several npm commands: Installing npm back to top curl http://npmjs.org/install.sh | sh Update npm There are several ways you can update npm. curl http://npmjs.org/install.sh

browsenpm.org

https://velog.io/@jeongmin78/CICD-Github-Action-AWS-IAM-Role-%EC%9D%B4%EC%9A%A9%ED%95%B4-%EC%9D%B4%EB%AF%B8%EC%A7%80%EB%A5%BC-ECR%EC%97%90-%EC%98%AC%EB%A6%AC%EA%B8%B0-8n3fmmgn

 

# [CI/CD] Github Action - AWS IAM Role 이용해 이미지를 ECR에 올리기

OIDC를 적용한 Role을 만들어 Github Action으로 ECR 이미지 배포하기

velog.io

https://docs.github.com/ko/actions/deployment/security-hardening-your-deployments/configuring-openid-connect-in-amazon-web-services

 

Amazon Web Services에서 OpenID Connect 구성 - GitHub Docs

워크플로 내에서 OpenID 커넥트를 사용하여 Amazon Web Services로 인증

docs.github.com

https://docs.aws.amazon.com/ko_kr/IAM/latest/UserGuide/id_roles_create_for-idp_oidc.html

 

웹 아이덴티티 또는 OpenID Connect 페더레이션을 위한 역할 생성(콘솔) - AWS Identity and Access Management

Google, Facebook 또는 Amazon Cognito의 OIDC IdP를 사용하는 경우 AWS Management Console에서 IAM IdP를 별도로 생성하지 마세요. 이러한 OIDC ID 제공자는 이미 AWS에 내장되어 있고 용도에 맞게 사용할 수 있습니다

docs.aws.amazon.com

 

Comments