안녕하세요! 오늘은 Vue.js 프론트엔드 애플리케이션을 Docker와 Kubernetes를 이용해 배포하는 방법에 대해 알아볼 예정이다! 처음부터 차근차근 진행하면서 각 단계별로 필요한 파일들을 상세히 설명할거니 끝까지 함께 따라와보자!
먼저 Vue.js 프론트엔드 애플리케이션을 위한 Dockerfile을 작성해보자!
# 빌드 단계 Node.js 이미지를 사용하여 Vue.js 애플리케이션 빌드
FROM node:16 as build
WORKDIR /app # 컨테이너 내부의 작업 디렉토리 설정
COPY package*.json ./ # package.json과 package-lock.json 먼저 복사
RUN npm install # 의존성 설치
COPY . . # 소스 코드 전체 복사
RUN npm run build # Vue.js 애플리케이션 빌드 실행
# 실행 단계 빌드된 파일을 Nginx로 서빙
FROM nginx:alpine # 경량화된 Nginx 이미지 사용
# 빌드 단계에서 생성된 dist 폴더를 Nginx의 웹 루트 디렉토리로 복사
COPY --from=build /app/dist /usr/share/nginx/html
# Nginx 설정 파일 복사
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80 # 80번 포트 노출
CMD ["nginx", "-g", "daemon off;"] # Nginx 서버 실행 명령
이 Dockerfile은 멀티 스테이지 빌드라는 기법을 사용한다!
이렇게 하면 빌드 도구나 소스 코드가 최종 이미지에 포함되지 않아서 이미지 크기가 작아지고 보안성도 좋아진다!
FROM node:16 as build Node.js 16 버전 이미지를 사용하고, 이 단계에 "build"라는 이름을 붙인다WORKDIR /app 컨테이너 안에서 작업할 기본 디렉토리를 지정한다 (일반 컴퓨터에서 cd 명령어로 디렉토리 이동하는 것과 비슷함)COPY package*.json ./ 의존성 파일을 먼저 복사한다COPY . . 첫 번째 점은 내 컴퓨터의 현재 폴더, 두 번째 점은 컨테이너의 현재 작업 폴더(/app)를 의미한다이렇게 package*.json을 먼저 복사하고 npm install을 실행한 다음 소스 코드를 복사하는 이유는 Docker의 레이어 캐싱 기능을 활용하기 위해서다! 의존성 설치는 시간이 오래 걸리지만 자주 변경되지 않으니까, 소스 코드가 변경되어도 이 단계는 캐시를 사용할 수 있어서 빌드 시간이 단축된다!
이제 Nginx 설정 파일을 작성해보자!
server {
listen 80; # 80번 포트에서 HTTP 요청 대기
server_name localhost; # 서버 이름 설정
root /usr/share/nginx/html; # 웹 루트 디렉토리 설정
index index.html; # 기본 인덱스 파일 설정
location / {
try_files $uri $uri/ /index.html; # SPA를 위한 URL 리다이렉션 설정
}
# 백엔드 API 프록시 설정
location /api {
proxy_pass http://backend-app:8080; # /api 요청을 백엔드로 전달
proxy_set_header Host $host; # 원본 호스트 헤더 유지
proxy_set_header X-Real-IP $remote_addr; # 클라이언트 IP 정보 전달
}
}
이 설정 파일은 Nginx가 두 가지 중요한 역할을 수행하도록 해준다!
/api로 시작하는 요청을 백엔드 서비스(backend-app)로 전달한다특히 try_files $uri $uri/ /index.html; 설정은 SPA(Single Page Application)에서 매우 중요하다! Vue Router 같은 클라이언트 측 라우팅을 사용할 때, 모든 URL이 서버에서 처리되지 않고 index.html로 돌아가서 클라이언트 측에서 라우팅이 처리되도록 해준다.
이제 Kubernetes에 애플리케이션을 배포하기 위한 설정 파일을 작성해보자!
apiVersion: apps/v1 # Kubernetes API 버전
kind: Deployment # 리소스 유형 Deployment
metadata:
name: vue-login # 배포 이름
labels:
app: vue-login # 리소스 식별 라벨
spec:
replicas: 1 # 실행할 Pod 복제본 수
selector:
matchLabels:
app: vue-login # 이 Deployment가 관리할 Pod 선택기
template:
metadata:
labels:
app: vue-login # Pod에 적용할 라벨
spec:
containers:
- name: vue-login # 컨테이너 이름
image: ${DOCKER_REGISTRY}/vue-login:latest # 사용할 이미지
ports:
- containerPort: 80 # 컨테이너가 노출하는 포트
resources:
limits: # 리소스 사용 상한선
cpu: "0.5" # 최대 0.5 CPU 코어
memory: "512Mi" # 최대 512MB 메모리
requests: # 리소스 요청 (최소 보장)
cpu: "0.2" # 최소 0.2 CPU 코어
memory: "256Mi" # 최소 256MB 메모리
livenessProbe: # 컨테이너 생존 확인 설정
httpGet:
path: / # 확인할 HTTP 경로
port: 80 # 확인할 포트
initialDelaySeconds: 30 # 시작 후 검사 시작까지 대기 시간
periodSeconds: 10 # 검사 주기
readinessProbe: # 컨테이너 준비 상태 확인 설정
httpGet:
path: / # 확인할 HTTP 경로
port: 80 # 확인할 포트
initialDelaySeconds: 5 # 시작 후 검사 시작까지 대기 시간
periodSeconds: 5 # 검사 주기
이 파일은 쿠버네티스에 Vue.js 프론트엔드 애플리케이션을 어떻게 배포할지 정의한다!
livenessProbe 컨테이너가 살아있는지 확인하고, 응답이 없으면 재시작한다readinessProbe 컨테이너가 요청을 처리할 준비가 됐는지 확인하고, 준비되지 않았으면 트래픽을 보내지 않는다apiVersion: v1 # Kubernetes API 버전
kind: Service # 리소스 유형 Service
metadata:
name: vue-login # 서비스 이름
spec:
selector:
app: vue-login # 트래픽을 전달할 Pod 선택기
ports:
- port: 80 # 서비스 포트
targetPort: 80 # 컨테이너 포트
type: NodePort # 서비스 유형 NodePort
이 파일은 배포된 Pod에 접근하기 위한 서비스를 정의한다!
app: vue-login 라벨을 가진 Pod들을 서비스 대상으로 선택한다NodePort는 각 노드의 IP와 특정 포트를 통해 서비스에 접근할 수 있게 해준다이제 작성한 파일들을 GitLab 저장소에 푸시해보자!
git remote -v
이 명령어를 실행하면 origin이라는 이름으로 GitHub 주소만 나올 거다.
git remote add gitlab https://gitlab.com/rmsals2020/vue-login.git
여기서 gitlab은 remote 이름이고, 원하는 이름으로 바꿔도 되지만 나중에 기억하기 쉽게 이렇게 두는 게 좋다!
git add .
git commit -m "CI/CD 파이프라인 설정 추가"
git push gitlab main
main 대신 master를 쓰는 경우도 있으니, 브랜치 이름 확인은 아래 명령어로 확인해보자!
git branch
이제 GitLab으로 돌아가보면 "CI/CD 파이프라인 설정 추가"라고 커밋 메시지로 쓴 내용이 push된 걸 확인할 수 있다!
GitLab CI/CD 파이프라인을 설정하면서 여러 오류가 발생할 수 있다. 몇 가지 일반적인 오류와 해결 방법을 살펴보자!
$ docker push $DOCKER_REGISTRY/$IMAGE_NAME:$CI_COMMIT_SHORT_SHA
The push refers to repository [localhost:5000/vue-login]
Get "http://localhost:5000/v2/": dial tcp [::1]:5000: connect: connection refused
원인 CI/CD 파이프라인에서 Docker 이미지를 로컬 레지스트리(localhost:5000)로 푸시하려고 시도했으나, 이 레지스트리가 존재하지 않거나 접근할 수 없다!
해결 방법 DOCKER_REGISTRY 변수를 로컬 레지스트리 주소에서 GitLab 레지스트리 주소(registry.gitlab.com/사용자명/프로젝트명)로 변경하자!
$ docker push $DOCKER_REGISTRY/$IMAGE_NAME:$CI_COMMIT_SHORT_SHA
denied: requested access to the resource is denied
원인 GitLab 레지스트리에 이미지를 푸시하려고 했으나, 레지스트리에 접근 권한이 없다!
해결 방법 CI/CD 스크립트에 Docker 로그인 단계를 추가하자!
- echo "$CI_REGISTRY_PASSWORD" | docker login -u "$CI_REGISTRY_USER" --password-stdin $CI_REGISTRY
이렇게 GitLab이 자동으로 제공하는 인증 변수를 사용하면 레지스트리에 로그인할 수 있다!
error: unknown command "sh" for "kubectl"
원인 kubectl 명령어가 제대로 실행되지 않거나 Kubernetes 클러스터 접속을 위한 인증 정보가 없다!
해결 방법
1. 다른 베이스 이미지(alpine:latest) 사용하기
2. kubectl 직접 설치하기
3. 쉘 스크립트 블록으로 명령어 실행하기
deploy:
stage: deploy
image: alpine:latest
script:
- apk add --no-cache bash curl
- |
# 배포 스크립트
set -e
# kubectl 설치
curl -LO "https://dl.k8s.io/release/v1.27.0/bin/linux/amd64/kubectl"
chmod +x kubectl
mv kubectl /usr/local/bin/
# 나머지 배포 명령...
CI/CD 파이프라인에서 로컬 미니큐브 클러스터에 접근하려고 할 때 경로 불일치나 네트워크 접근 제한 등의 문제가 발생할 수 있다!
해결 방법 CI/CD 파이프라인에서는 이미지 빌드와 레지스트리 푸시까지만 수행하고, 실제 배포는 로컬에서 수동으로 처리하자!
stages:
- build
- package
# 여기에 build, package 단계 설정...
로컬 환경에서 미니큐브를 이용해 배포해보자!
배포 파일에서 이미지 이름 수정하기
image: registry.gitlab.com/rmsals2020/vue-login/vue-login:latest
쿠버네티스에 배포하기
kubectl apply -f kubernetes/frontend-deployment.yaml
kubectl apply -f kubernetes/frontend-service.yaml
서비스 확인하기
kubectl get pods
minikube service vue-login
이렇게 하면 브라우저가 열리면서 배포된 Vue.js 애플리케이션에 접속할 수 있다!
오늘은 Vue.js 프론트엔드 애플리케이션을 Docker와 Kubernetes를 사용해 배포하는 방법을 알아봤다! 멀티 스테이지 빌드로 효율적인 Docker 이미지를 만들고, Kubernetes 배포 구성으로 안정적인 운영 환경을 구성했다! GitLab CI/CD 연동 과정에서 발생할 수 있는 다양한 오류들과 해결 방법도 살펴봤다!
CI/CD 파이프라인을 구축하면 코드 변경사항을 자동으로 테스트하고 배포할 수 있어서 개발 생산성이 크게 높아진다! 다음에는 백엔드 애플리케이션을 배포하고 프론트엔드와 연동하는 방법에 대해 알아보자!
궁금한 점이나 의견이 있으면 댓글로 남겨주세요! 다음 글에서 만나요! 👋