오늘은 MSA 분산 서버를 각각의 dockerfile
을 작성하고 docker-compose
도 적용해 보았습니다.
dockerfile 코드
# 빌드 스테이지 FROM node:20-alpine AS builder WORKDIR /app # 워크스페이스 설정 파일들 복사 COPY package.json yarn.lock turbo.json ./ COPY apps/account/package.json ./apps/account/ COPY packages/common ./packages/common COPY packages/modules ./packages/modules # 의존성 설치 RUN yarn install --frozen-lockfile # account 서비스 소스 복사 COPY apps/account ./apps/account # Protobuf 파일들 복사 COPY packages/common/protobufs ./protobufs # 공통 환경 변수 파일 복사 COPY packages/common/config/.env ./packages/common/config/.env # 빌드 WORKDIR /app/apps/account RUN yarn add glob@^11.0.0 esbuild del-cli --dev RUN NODE_ENV=development yarn build # 실행 스테이지 FROM node:18-alpine AS runner WORKDIR /app # package.json 파일들 복사 COPY package.json yarn.lock turbo.json ./ COPY apps/account/package.json ./apps/account/ COPY packages/common ./packages/common COPY packages/modules ./packages/modules # 빌드된 파일들 복사 COPY --from=builder /app/apps/account/dist ./dist COPY --from=builder /app/protobufs ./protobufs COPY --from=builder /app/packages/common/config/.env ./packages/common/config/.env COPY --from=builder /app/node_modules ./node_modules # 실행 환경 설정 ENV NODE_ENV=production EXPOSE 6020 # 실행 CMD ["node", "dist/account.server.js"]
package.json 코드
{ "name": "account", "version": "1.0.0", "description": "회원서비스 담당 서버", "main": "account.server.js", "type": "module", "scripts": { "account:start": "node dist/account.server.js", "dev": "node src/account.server.js", "build": "node esbuild.config.js", "clean": "del-cli dist" }, "dependencies": { "@peekaboo-ssr/classes": "*", "@peekaboo-ssr/config": "*", "@peekaboo-ssr/events": "*", "@peekaboo-ssr/utils": "*", "@peekaboo-ssr/commands": "*", "@peekaboo-ssr/error": "*", "bcrypt": "^5.1.1", "jsonwebtoken": "^9.0.2", "uuid": "^11.0.3" } }
esbuild.config.js
import { glob } from 'glob'; import * as esbuild from 'esbuild'; try { const entryPoints = await glob('src/**/*.js'); await esbuild.build({ entryPoints, bundle: true, outdir: 'dist', platform: 'node', target: 'node18', format: 'esm', sourcemap: true, external: [ // 실제 사용하는 의존성 '@peekaboo-ssr/classes', '@peekaboo-ssr/config', '@peekaboo-ssr/events', '@peekaboo-ssr/utils', '@peekaboo-ssr/commands', '@peekaboo-ssr/error', 'bcrypt', 'jsonwebtoken', 'uuid', 'fs', 'pidusage', // protobuf 관련 (공통 의존성) 'protobufjs', 'protobufjs/minimal', 'express', 'prom-client', ], define: { 'global.XMLHttpRequest': 'undefined', 'process.env.NODE_ENV': '"production"', }, inject: ['./esm-shims.js'], }); } catch (error) { console.error('Build failed:', error); process.exit(1); }
각각의 서비스 파일안에 dockerfile, esbuild.config.js, package.json
파일들이 각각의 의존성에 맞게 코드를 작성하여 만들어 두었습니다.
docker-compose 코드
services: dedicated: build: context: . dockerfile: apps/game/dedicated/dockerfile image: dedicated_server:latest command: echo "Image build complete, no container execution." distributor: build: context: . dockerfile: apps/distributor/dockerfile container_name: distributor hostname: distributor networks: - peekaboo-network ports: - '6030:6030' - '8030:8030' env_file: - ./packages/common/config/.env game: build: context: . dockerfile: apps/game/master/dockerfile container_name: game hostname: game networks: - peekaboo-network ports: - '6050:6050' - '8050:8050' env_file: - ./packages/common/config/.env depends_on: - distributor volumes: - /var/run/docker.sock:/var/run/docker.sock lobby: build: context: . dockerfile: apps/lobby/dockerfile container_name: lobby hostname: lobby networks: - peekaboo-network ports: - '6010:6010' - '8010:8010' env_file: - ./packages/common/config/.env depends_on: - game account: build: context: . dockerfile: apps/account/dockerfile container_name: account hostname: account networks: - peekaboo-network ports: - '6020:6020' - '8020:8020' env_file: - ./packages/common/config/.env depends_on: - lobby session: build: context: . dockerfile: apps/session/dockerfile container_name: session hostname: session networks: - peekaboo-network ports: - '6040:6040' - '8040:8040' env_file: - ./packages/common/config/.env depends_on: - account gateway: build: context: . dockerfile: apps/gateway/dockerfile container_name: gateway hostname: gateway networks: - peekaboo-network ports: - '6000:6000' - '8000:8000' env_file: - ./packages/common/config/.env depends_on: - session networks: peekaboo-network: driver: bridge
docker-compose.yml
파일은 가장 최상위에 위치해 있으며 해당 파일을 실행하여 한번에 build를 하면서 실행까지하게 만들어두었습니다.데디게이트 서버
는 이미지 빌드만을 실행하고 컨테이너 생성은 중단하였습니다.- MSA에서 실행순서도 맟추어 주었습니다.
트러블 슈팅
도커를 사용하면서 트러블 슈팅을 이번에
최종 프로젝트 브로셔
작성할때 같이notion
에 작성해 보았습니다.
- docker간의 서비스 연결 문제 : docker간의 서비스 연결 문제
- docker 내부에서 컨테이너 생성 : 내부에서 컨테이너 생성
오늘의 회고
이번docker
를 도입할떄 ai의 힘을 많이 빌렸습니다. 빠르게 프로젝트에 적용해보고 싶은 욕심이 있었고 어떻게든 써보고 싶은 마음이 컸던거 같습니다. 그럼에도 불구하고 수많은 시행착오를 격고 여러 구글링을 통해 명령어, 키워드도 찾아보고 어떻게 사용하는지에 대해 어느정도 감을 익혔던 시간이였습니다.지금와서 생각해보니 추후
git action
을 이용하여ci/cd
를 간단하게 구현해 보았습니다. 현재docker-compose
에서데디게이트 서버
파일을 이미지 빌드만하는 것을action
에서 하면 될 거 같다는 생각이 드네요..하핫 그래도 이참에 사용해봤다는 경험이 좋았던거 같습니다. 굿
오늘도 화이팅!