도커 공부하다가 찾은 귀여운 이미지
펭귄이랑 고래는 찍먹해봤는데, 곰돌이는 ... 만져볼 일 없지 않을까..?🤔
CSR로 개발된 우리 회사 서비스가 SEO에 취약한 점이 있어, 곧 Next.js를 이용한 SSR 마이그레이션
이 있을 예정이다.
그 전에, 앞으로의 SSR 프로젝트를 더욱 간편하게 시작하기 위해, Next.js 템플릿
을 만들었다.
특히 SSR 프로젝트는 S3로 호스팅할 수 없기 때문에
(S3는 정적인 웹사이트만 호스팅할 수 있다), 기존에 하던 빌드 방식과는 다른 방식이 필요했다.
그래서 선택한 것이 바로 도커
!
(사실 내가 선택한 건 아니고 우리 CTO님이 선택해주셨다.)
여기서 잠깐, 도커란?
도커란 컨테이너 기반의 오픈소스 가상화 플랫폼이다.
프로젝트에서 실행하는 다양한 실행 환경, 프로그램들을 컨테이너로 추상화해서 빌드와 관리를 간편하게 하고, 어디서든 실행 가능하게 해준다.
초보를 위한 도커 안내서
기존 회사 프로젝트에서 CI/CD는 Github Actions
를 이용하고 있었고, 여기저기 구글링해보니 AWS 코드 파이프라인은 설정이 너무 어렵다
는 경고가 있었다. (nightmare..?)
그래서 친숙한 Github Actions
를 사용하기로 결정했다😓.
Github Actions
,Docker
를 활용해서Next.js
프로젝트를 자동으로 빌드하자!
Dockerfile
을 루트 위치에 만들기.https://github.com/vercel/next.js/tree/canary/examples/with-docker
Dockerfile
에서 사용하는 요 커맨드들을 가지고 어떻게 이미지를 만들건지 적어주면 된다.
command | 설명 |
---|---|
FROM | base 이미지 설정 |
WORKDIR | 작업 디렉터리 설정 |
RUN | 이미지 빌드 시 커맨드 실행 |
ENTRYPOINT | 이미지 실행 시 항상 실행되야 하는 커맨드 설정 |
CMD | 이미지 실행 시 디폴트 커맨드 또는 파라미터 설정 |
EXPOSE | 컨테이너가 리스닝할 포트 및 프로토콜 설정 |
COPY/ADD | 이미지의 파일 시스템으로 파일 또는 디렉터리 복사 |
ENV | 환경 변수 설정 |
ARG | 빌드 시 넘어올 수 있는 인자 설정 |
NodeJS 16(Alpine Linux Project 기반)을 base image로 한다.
그 과정 중에 필요한 라이브러리가 사라지는 이슈가 있으니, 추가해주기 위해 libc6-compat
패키지를 추가해준다. (자세한 내용은 여기를 확인하세요)
다했으면 app 디렉토리로 이동하기~!
FROM node:16-alpine AS deps
RUN apk add --no-cache libc6-compat
WORKDIR /app
먼저 ~~~.lock
파일을 복사해주고,
사용하는 패키지(yarn/npm/pnpm
)를 사용해 Dependencies를 설치해준다.
만약 관련 파일이 없다면 exit 1
로 빌드를 중지한다.
COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml* ./
RUN \
if [ -f yarn.lock ]; then yarn install --frozen-lockfile; \
elif [ -f package-lock.json ]; then npm ci; \
elif [ -f pnpm-lock.yaml ]; then yarn global add pnpm && pnpm i --frozen-lockfile; \
else echo "Lockfile not found." && exit 1; \
잠깐, 레이어를 왜 세 개나 만드나요?🤔
이미지 용량 최소화를 위해서 입니다!
도커 이미지는 레이어를 축적해서 만들어지는데요! 이 때 이전 이미지의 레이어와 현재 빌드하는 이미지의 레이어가 같다면 재사용합니다.(캐싱)
따라서 역할별 레이어를 세분화해서 바뀌지 않는 부분은 재사용하게 해서 빌드 시 사용되는 리소스를 최소화하는 것입니다~
첫 번째 레이어와 같이 Node.js 16
을 base image로 한다.
app에 있는 모든 파일(.dockerignore
에 지정된 파일 제외)을 복사한다.
이 때 Next.js에서 원격으로 데이터 수집을 못하게 하고싶다면 telemetry 서버를 꺼준다.
FROM node:16-alpine AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
ENV NEXT_TELEMETRY_DISABLED 1
RUN yarn build
이젠 말 안해도 아는 Node.js 16
base image로 하기
app 디렉토리로 이동
Next.js 원격 데이터 수집 싫으면 하지말라고 말해주기
sudo 없이 로컬에서 이미지를 컨테이너에서 실행할 수 있도록 add group, add user!
파일들 몽땅 복사!
FROM node:16-alpine AS runner
WORKDIR /app
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs
COPY --from=builder /app/public ./public
Next.js에서는 빌드할 때 무려 자동으로 이미지 사이즈를 줄여준다 !
빌드 과정에서 standalone
이라는 폴더를 만들고, 코드에서 사용한 import
, request
들을 분석해서 꼭 필요한 친구들만 요 폴더에 넣은 뒤 빌드한다고 한다. (자세한 내용은 여기에서 확인)
그 기능을 사용해보자 ~!
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
USER nextjs
3000번 포트를 사용한다고 설정한다.(포트가 1024번보다 낮으면 에러뜰 수 있다)
그리고 컨테이너가 실행되었을 때 실행되는 명령어를 정의해준다.
EXPOSE 3000
ENV PORT 3000
CMD ["node", "server.js"]
docker build -t (이미지 태그명)
docker run (<옵션>) <이미지 식별자> (<명령어>) (<인자>)
루트 디렉토리에 .dockerignore
파일을 만들어준다.
나는 AWS ECR
repository에서 env 설정을 해주기 때문에 .env
도 포함시켰다.
Dockerfile
.dockerignore
node_modules
npm-debug.log
README.md
.next
.git
Footer
.env
/** @type {import('next').NextConfig} */
const nextConfig = {
reactStrictMode: true,
experimental: { appDir: true }, // Next.js v13 app directory 사용 여부
output: 'standalone', // 여기서 standalone 폴더를 사용한다고 해준다.
};
export default nextConfig;
루트 디렉토리에 .github
폴더를 만들고, workflow
폴더를 만든 뒤 안에 yaml 확장자로 파일을 만들어준다!
깃헙 repository에서 actions 설정을 하면 자동으로 폴더가 생긴다.
name: Deploy to Amazon ECR
on:
push:
branches:
- main # main 브랜치에 push할 때
pull_request:
branches:
- main # main 브랜치에 PR할 때 실행한다.
env: # 사용할 env
AWS_REGION:
ECR_REGISTRY:
ECR_REPOSITORY:
jobs:
deploy:
name: Deploy
runs-on: ubuntu-latest
environment: production
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Configure AWS credentials # AWS 로그인
uses: aws-actions/configure-aws-credentials@v1
with: # AWS 로그인에 필요한 정보들. 얘네는 Github Secret에 설정해야 한다.
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ${{ env.AWS_REGION }}
- name: Login to Amazon ECR # ECR에도 로그인
id: login-ecr
uses: aws-actions/amazon-ecr-login@v1
- name: Build, tag, and push image to Amazon ECR # 이미지 빌드하고 ECR push
id: build-image
env:
ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
# 아래처럼 docker 커맨드를 실행한다.
run: |
docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:latest .
docker push $ECR_REGISTRY/$ECR_REPOSITORY:latest
echo "image=$ECR_REGISTRY/$ECR_REPOSITORY:latest"
요렇게 해주면 자동으로 빌드 / 이미지 생성 / AWS ECR push까지 끝 ~!
요렇게해서 Next.js 템플릿에 CI/CD를 무사히 추가했다.
사실 나는 프론트엔드다보니, 도커에 대해서 정말 1도 모르고 있었다.(고래라는 거 밖에 몰랐음)
이 기회에 도커가 뭔지 개념도 이해하고, 직접 이미지도 말아보고 컨테이너도 실행해볼 수 있었다.
사실 CI/CD에 대해서도 큰 이해가 없었고, 그냥 있는 코드를 몇 군데 수정하는데 지나지 않았었는데,
이번 기회에 직접 처음부터 CI/CD에 대해 공부하고, 어떤 툴을 사용할지 고민하고, 직접 코드를 작성하면서 확실히 이해하게 되었다.
(나름 재미있을지도 ?)
그리고 AWS는 아직도 무섭다..😓
끝