IT STUDY LOG
[SECTION2] <AWS 배포 자동화> DAY 1 LOG 본문
#학습 목표
- 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 프라이빗 리포지토리 생성
- Amazon ECR 콘솔(https://console.aws.amazon.com/ecr/repositories) > 탐색 모음에서 리포지토리를 생성할 리전을 선택
- 탐색 창에서 리포지토리를 선택
- 리포지토리(Repositories) 페이지에서 프라이빗(Private) 탭을 선택한 다음 리포지토리 생성(Create repository)을 선택
- 가시성 설정(Visibility settings)에서 프라이빗(Private)이 선택되었는지 확인
- 리포지토리 이름(Repository name)에 리포지토리의 고유한 이름을 입력 (리포지토리 이름은 자체적으로 지정할 수 있으며(예: nginx-web-app), 리포지토리를 범주로 그룹화하기 위해 네임스페이스에 추가 가능)
- 태그 불변성(Tag immutability)에서 리포지토리의 태그 변경 가능 설정을 선택 (변경 불가능 태그로 구성된 리포지토리는 이미지 태그를 덮어쓰는 것을 방지해 줌, 자세한 내용은 이미지 태그 변경 가능성 섹션을 참조)
- 푸시 시 스캔의 경우 기본 스캔을 위해 리포지토리 수준에서 스캔 설정을 지정할 수 있지만 프라이빗 레지스트리 수준에서 스캔 구성을 지정하는 것 권장(프라이빗 레지스트리에서 스캔 설정을 지정하면 고급 스캔 또는 기본 스캔을 사용하고 필터를 정의하여 스캔할 리포지토리를 지정 가능, 자세한 내용은 이미지 스캔 섹션을 참조)
- KMS 암호화(KMS encryption)의 경우, AWS Key Management Service를 사용하여 리포지토리에 있는 이미지의 암호화를 활성화할지 선택(자세한 내용은 저장된 데이터 암호화 섹션을 참조)
- KMS 암호화가 활성화된 경우 고객 암호화 설정(고급)(Customer encryption settings (advanced))을 선택하여 고유의 KMS 키를 선택 (KMS 키가 클러스터와 동일한 리전에 있어야하며, AWS KMS 키 생성(Create an key)을 선택하고 AWS KMS 콘솔로 이동하여 고유한 키를 생성)
- 리포지토리 생성(Create repository)을 선택
- (선택 사항) 생성한 리포지토리를 선택하고 푸시 명령 보기(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 파일 작성
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로 변경
- 참고
- node 명령어: 단순히 자바스크립트를 서버로 구동하는 명령어
- 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 커넥션 연결 확인
- 원인 : fastify.mongo 객체 사용법이 생소
- 해결 방안 : fastify mongo 객체 레퍼런스 참고
📝 문제 5 : 아마존 ECR IAM ROLE 생성
- 상황 : access key를 이용하지 않고 ECR에 로그인하기 위해 IAM ROLE 생성
- 해결 방안 : OpenID Connection을 이용해 GitHub Actions 워크플로가 액세스 키, 시크릿 키를 노출하지 않고 AWS(Amazon Web Services)의 리소스에 액세스 가능
- 해결 과정
2. 웹 자격 증명 또는 OIDC를 위한 역할 생성
- 탐색 창에서 역할(Roles)을 선택한 후 역할 생성(Create role)을 선택
- 웹 ID(Web Identity) 역할 유형을 선택
- 자격 증명 공급자(Identity provider)에서 역할의 IdP를 선택
- Audience에서 지정한 자격증명 공급자의 대상 선택
- 권한 정책을 사용하기 위한 정책을 선택하거나 정책 생성(Create policy)을 선택하여 새 브라우저 탭을 열고 완전히 새로운 정책을 생성할 수 있음(자세한 내용은 IAM 정책 생성 섹션을 참조)
- 웹 ID 사용자에게 부여하려는 권한 정책 옆의 확인란을 선택 원할 경우, 여기서 정책을 선택하지 않고 나중에 정책을 만들어서 역할에 연결할 수 있음 (기본적으로 역할은 권한이 없음)
- 역할 이름(Role name)에 역할 이름을 입력
- 역할 이름은 AWS 계정 내에서 고유해야하며 대소문자를 구분하지 않음
- 다른 AWS 리소스가 역할을 참조할 수 있기 때문에 역할을 생성한 후에는 역할 이름을 편집할 수 없음
- 역할에 대한 사용 사례와 권한을 편집하려면 1단계: 신뢰할 수 있는 엔터티 선택(Step 1: Select trusted entities) 또는 2단계: 권한 추가(Step 2: Add permissions) 섹션에서 편집(Edit)을 선택
- 역할을 검토한 다음 [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/
https://hub.docker.com/_/mongo
https://github.com/fastify/fastify-mongodb
https://stackoverflow.com/questions/31362021/npm-start-vs-node-app-js
https://docs.aws.amazon.com/ko_kr/IAM/latest/UserGuide/id_roles_create_for-idp_oidc.html
'devops bootcamp 4 > project log' 카테고리의 다른 글
[SECTION2] <AWS 배포 자동화> DAY 4 LOG (0) | 2023.05.02 |
---|---|
[SECTION2] <AWS 배포 자동화> DAY 3 LOG (0) | 2023.05.01 |
[SECTION2] <AWS 배포 자동화> DAY 2 LOG (0) | 2023.04.28 |
[SECTION1] <WAS, Web Server 실습> 회고 (0) | 2023.04.05 |
[SECTION 1] <WAS, Web Server 실습> Introduction (0) | 2023.04.03 |