
Part 3에서는 현대 풀스택 개발의 핵심인 Docker와 Kubernetes 환경 설정을 다룹니다. 특히 M3/M4 맥북의 성능 특성을 고려하여 Docker Desktop 대신 Minikube를 활용한 경량화된 개발 환경을 구축합니다.
Docker Desktop은 M3/M4 맥북에서 메모리와 CPU를 많이 사용하는 단점이 있습니다. Minikube를 활용하면 더 효율적인 환경을 구축할 수 있습니다.
# Minikube 설치
version: '3.8'
services:
frontend:
build: ./frontend
ports:
- "3000:3000"
volumes:
- ./frontend:/app
- /app/node_modules
environment:
- CHOKIDAR_USEPOLLING=true
command: npm start
backend:
build: ./backend
ports:
- "8000:8000"
volumes:
- ./backend:/app
environment:
- DATABASE_URL=postgresql://user:password@db:5432/dbname
depends_on:
- db
db:
image: postgres:15-alpine
environment:
- POSTGRES_DB=dbname
- POSTGRES_USER=user
- POSTGRES_PASSWORD=password
volumes:
- postgres_data:/var/lib/postgresql/data
volumes:
postgres_data:
apiVersion: apps/v1
kind: Deployment
metadata:
name: fullstack-app
spec:
replicas: 2
selector:
matchLabels:
app: fullstack-app
template:
metadata:
labels:
app: fullstack-app
spec:
containers:
- name: frontend
image: my-frontend:latest
ports:
- containerPort: 3000
- name: backend
image: my-backend:latest
ports:
- containerPort: 8000
env:
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: db-secret
key: url
---
apiVersion: v1
kind: Service
metadata:
name: fullstack-service
spec:
selector:
app: fullstack-app
ports:
- name: frontend
port: 80
targetPort: 3000
- name: backend
port: 8080
targetPort: 8000
type: LoadBalancer
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: fullstack-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
rules:
- host: fullstack.local
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: fullstack-service
port:
number: 80
- path: /api
pathType: Prefix
backend:
service:
name: fullstack-service
port:
number: 8080
apiVersion: skaffold/v2beta29
kind: Config
metadata:
name: fullstack-app
build:
artifacts:
- image: my-frontend
context: ./frontend
docker:
dockerfile: Dockerfile
- image: my-backend
context: ./backend
docker:
dockerfile: Dockerfile
deploy:
kubectl:
manifests:
- k8s/*.yaml
portForward:
- resourceType: service
resourceName: fullstack-service
port: 80
localPort: 3000
name: CI/CD Pipeline
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main ]
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [18.x, 20.x]
python-version: [3.11, 3.12]
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
- name: Setup Python
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
cd frontend && npm ci
cd ../backend && pip install -r requirements.txt
- name: Run tests
run: |
cd frontend && npm test
cd ../backend && pytest
build-and-deploy:
needs: test
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@v4
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push Docker images
run: |
docker build -t ${{ secrets.DOCKERHUB_USERNAME }}/frontend:latest ./frontend
docker build -t ${{ secrets.DOCKERHUB_USERNAME }}/backend:latest ./backend
docker push ${{ secrets.DOCKERHUB_USERNAME }}/frontend:latest
docker push ${{ secrets.DOCKERHUB_USERNAME }}/backend:latest

version: '3.8'
services:
prometheus:
image: prom/prometheus:latest
ports:
- "9090:9090"
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
- prometheus_data:/prometheus
command:
- '--config.file=/etc/prometheus/prometheus.yml'
- '--storage.tsdb.path=/prometheus'
grafana:
image: grafana/grafana:latest
ports:
- "3000:3000"
volumes:
- grafana_data:/var/lib/grafana
environment:
- GF_SECURITY_ADMIN_PASSWORD=admin
node-exporter:
image: prom/node-exporter:latest
ports:
- "9100:9100"
volumes:
- /proc:/host/proc:ro
- /sys:/host/sys:ro
- /:/rootfs:ro
command:
- '--path.procfs=/host/proc'
- '--path.sysfs=/host/sys'
- '--collector.filesystem.mounted-points'
volumes:
prometheus_data:
grafana_data:
global:
scrape_interval: 15s
scrape_configs:
- job_name: 'docker'
static_configs:
- targets: ['host.docker.internal:9323']
- job_name: 'node-exporter'
static_configs:
- targets: ['node-exporter:9100']
- job_name: 'minikube'
static_configs:
- targets: ['minikube:8443']
scheme: https
tls_config:
insecure_skip_verify: true
# Frontend FROM node:18-alpine as frontend-builder WORKDIR /app COPY package*.json ./ RUN npm ci COPY . . RUN npm run build FROM nginx:alpine COPY --from=frontend-builder /app/dist /usr/share/nginx/html COPY nginx.conf /etc/nginx/nginx.conf # Backend FROM python:3.12-slim as backend-builder WORKDIR /app COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt COPY . . FROM python:3.12-slim WORKDIR /app COPY --from=backend-builder /usr/local/lib/python3.12/site-packages /usr/local/lib/python3.12/site-packages COPY --from=backend-builder /app . CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
컨테이너와 오케스트레이션 환경 구축의 핵심 포인트:
이제 Part 4에서는 보안 설정과 전체 환경의 최적화를 다룰 예정입니다.