commit 95584b666a2ab15295de65e11d55f2a156eead18 Author: Mayne0213 Date: Sun Nov 23 23:40:15 2025 +0900 INIT(deploy): portfolio project setup Add complete portfolio project including: - Next.js application in services/nextjs - Docker configurations for dev and prod - Kubernetes deployment manifests with kustomize - ArgoCD application configuration - GitHub Actions workflow for automated builds diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..35589ea --- /dev/null +++ b/.dockerignore @@ -0,0 +1,70 @@ +# Dependencies +node_modules +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Next.js +.next/ +out/ +build/ + +# Production +dist/ + +# Environment variables +.env +.env.local +.env.development.local +.env.test.local +.env.production.local + +# Logs +logs +*.log + +# Misc +.DS_Store +*.pem + +# Vercel +.vercel + +# TypeScript +*.tsbuildinfo +next-env.d.ts + +# Git +.git +.gitignore + +# Docker +Dockerfile* +docker-compose* +.dockerignore + +# Documentation +README.md +*.md + +# Scripts +scripts/ +deploy/ + +# Coverage +coverage/ +.nyc_output/ + +# IDE +.vscode/ +.idea/ +*.swp +*.swo +*~ + +# OS +.DS_Store +Thumbs.db + +# Trunk +.trunk diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..f35d0c1 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,77 @@ +name: Build Docker Image + +on: + push: + branches: [main] + tags: + - 'v*' + workflow_dispatch: + +env: + REGISTRY: ghcr.io + IMAGE_NAME: ${{ github.repository }} + +jobs: + build-and-push: + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + + outputs: + image-tag: ${{ steps.meta.outputs.tags }} + image-digest: ${{ steps.build.outputs.digest }} + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Log in to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Lowercase repository name + id: lowercase + run: | + echo "repo=$(echo ${{ github.repository }} | tr '[:upper:]' '[:lower:]')" >> $GITHUB_OUTPUT + + - name: Extract metadata (tags, labels) + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.REGISTRY }}/${{ steps.lowercase.outputs.repo }} + tags: | + type=ref,event=branch + type=ref,event=pr + type=semver,pattern={{version}} + type=semver,pattern={{major}}.{{minor}} + type=sha,prefix={{branch}}- + type=raw,value=latest,enable={{is_default_branch}} + + - name: Build and push Docker image + id: build + uses: docker/build-push-action@v5 + with: + context: ./services/nextjs + file: ./deploy/docker/Dockerfile.prod + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max + + - name: Display image information + run: | + echo "βœ… Image built and pushed successfully!" + echo "πŸ“¦ Image tags:" + echo "${{ steps.meta.outputs.tags }}" + echo "πŸ”– Digest: ${{ steps.build.outputs.digest }}" + echo "" + echo "πŸš€ ArgoCD will automatically detect and deploy this new image" + echo " Monitor deployment at your ArgoCD dashboard" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..e2879a5 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,45 @@ +name: CI + +on: + push: + branches: [main, develop] + pull_request: + branches: [main, develop] + +jobs: + lint-and-build: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + cache: 'npm' + cache-dependency-path: services/nextjs/package-lock.json + + - name: Install dependencies + working-directory: services/nextjs + run: npm ci + + - name: Run ESLint + working-directory: services/nextjs + run: npm run lint + + - name: Build Next.js application + working-directory: services/nextjs + run: npm run build + env: + NEXT_TELEMETRY_DISABLED: 1 + + - name: Check build output + working-directory: services/nextjs + run: | + if [ ! -d ".next" ]; then + echo "Build failed: .next directory not found" + exit 1 + fi + echo "βœ… Build completed successfully" diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1ddb3f6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,52 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +node_modules +services/nextjs/node_modules +services/nextjs/.pnp +services/nextjs/.pnp.js +services/nextjs/.yarn/install-state.gz + +# testing +services/nextjs/coverage + +# next.js +services/nextjs/.next/ +services/nextjs/out/ + +# production +services/nextjs/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* +.pnpm-debug.log* + +# env files +.env* +!.env.example + +# vercel +.vercel + +# typescript +*.tsbuildinfo +next-env.d.ts + +# IDE +.vscode +.idea +*.swp +*.swo +*~ + +# OS +Thumbs.db + +# trunk +.trunk diff --git a/deploy/argocd/application.yaml b/deploy/argocd/application.yaml new file mode 100644 index 0000000..eb268ab --- /dev/null +++ b/deploy/argocd/application.yaml @@ -0,0 +1,38 @@ +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: portfolio + namespace: argocd + finalizers: + - resources-finalizer.argocd.argoproj.io +spec: + project: default + + source: + repoURL: https://github.com/Mayne0213/portfolio.git + targetRevision: main + path: deploy/k8s/overlays/prod + + destination: + server: https://kubernetes.default.svc + namespace: portfolio + + syncPolicy: + automated: + prune: true # λ§€λ‹ˆνŽ˜μŠ€νŠΈμ—μ„œ 제거된 λ¦¬μ†ŒμŠ€ μžλ™ μ‚­μ œ + selfHeal: true # ν΄λŸ¬μŠ€ν„°μ—μ„œ μˆ˜λ™ λ³€κ²½ μ‹œ μžλ™ 볡ꡬ + allowEmpty: false + + syncOptions: + - CreateNamespace=true # namespaceκ°€ μ—†μœΌλ©΄ μžλ™ 생성 + - PrunePropagationPolicy=foreground + - PruneLast=true + + retry: + limit: 5 + backoff: + duration: 5s + factor: 2 + maxDuration: 3m + + revisionHistoryLimit: 10 diff --git a/deploy/docker/Dockerfile.dev b/deploy/docker/Dockerfile.dev new file mode 100644 index 0000000..6a25e7e --- /dev/null +++ b/deploy/docker/Dockerfile.dev @@ -0,0 +1,26 @@ +# Development Dockerfile for Portfolio Next.js application +FROM node:20-alpine AS base + +# Install dependencies for development +RUN apk add --no-cache libc6-compat curl + +WORKDIR /app + +# Copy package files +COPY package.json package-lock.json* ./ + +# Install all dependencies (including dev dependencies) +RUN npm ci + +# Copy source code +COPY . . + +# Expose port +EXPOSE 3000 + +# Health check +HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ + CMD curl -f http://localhost:3000 || exit 1 + +# Default command (can be overridden in docker-compose) +CMD ["npm", "run", "dev"] diff --git a/deploy/docker/Dockerfile.prod b/deploy/docker/Dockerfile.prod new file mode 100644 index 0000000..fdad135 --- /dev/null +++ b/deploy/docker/Dockerfile.prod @@ -0,0 +1,56 @@ +# Multi-stage build for Portfolio Next.js application +FROM node:20-alpine AS base + +# Install dependencies only when needed +FROM base AS deps +# Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed. +RUN apk add --no-cache libc6-compat curl +WORKDIR /app + +# Install dependencies based on the preferred package manager +COPY package.json package-lock.json* ./ +RUN npm ci + +# Rebuild the source code only when needed +FROM base AS builder +WORKDIR /app +COPY --from=deps /app/node_modules ./node_modules +COPY . . + +# Build the application +ENV NEXT_TELEMETRY_DISABLED=1 +RUN npm run build + +# Production image, copy all the files and run next +FROM base AS runner +WORKDIR /app + +RUN apk add --no-cache curl + +ENV NODE_ENV=production +ENV NEXT_TELEMETRY_DISABLED=1 + +RUN addgroup --system --gid 1001 nodejs +RUN adduser --system --uid 1001 nextjs + +# Set the correct permission for prerender cache +RUN mkdir .next +RUN chown nextjs:nodejs .next + +# Automatically leverage output traces to reduce image size +# https://nextjs.org/docs/advanced-features/output-file-tracing +COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./ +COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static + +USER nextjs + +EXPOSE 3000 + +ENV PORT=3000 +ENV HOSTNAME=0.0.0.0 + +# Health check +HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ + CMD curl -f http://localhost:3000 || exit 1 + +CMD ["node", "server.js"] diff --git a/deploy/docker/docker-compose.dev.yml b/deploy/docker/docker-compose.dev.yml new file mode 100644 index 0000000..e19d7d9 --- /dev/null +++ b/deploy/docker/docker-compose.dev.yml @@ -0,0 +1,27 @@ +services: + # Development Portfolio Next.js Application + app: + build: + context: ../../services/nextjs + dockerfile: ../../deploy/docker/Dockerfile.dev + container_name: portfolio-app-dev + restart: unless-stopped + labels: + kompose.namespace: portfolio + ports: + - 3005:3000 + environment: + - NODE_ENV=development + - WATCHPACK_POLLING=true + networks: + - portfolio-network + volumes: + - ../../services/nextjs:/app + - /app/node_modules + - /app/.next + command: npm run dev + +networks: + portfolio-network: + driver: bridge + name: portfolio-network-dev diff --git a/deploy/docker/docker-compose.yml b/deploy/docker/docker-compose.yml new file mode 100644 index 0000000..7e32644 --- /dev/null +++ b/deploy/docker/docker-compose.yml @@ -0,0 +1,22 @@ +services: + # Production Portfolio Next.js Application + app: + image: portfolio-app + build: + context: ../../services/nextjs + dockerfile: ../../deploy/docker/Dockerfile.prod + container_name: portfolio-app-prod + restart: unless-stopped + labels: + kompose.namespace: portfolio + ports: + - 3005:3000 + environment: + - NODE_ENV=production + networks: + - portfolio-network + +networks: + portfolio-network: + driver: bridge + name: portfolio-network-prod diff --git a/deploy/k8s/base/deployment.yaml b/deploy/k8s/base/deployment.yaml new file mode 100644 index 0000000..d18b079 --- /dev/null +++ b/deploy/k8s/base/deployment.yaml @@ -0,0 +1,51 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: portfolio-app + labels: + app: portfolio-app +spec: + replicas: 1 + selector: + matchLabels: + app: portfolio-app + strategy: + type: RollingUpdate + rollingUpdate: + maxUnavailable: 1 + maxSurge: 1 + template: + metadata: + labels: + app: portfolio-app + spec: + containers: + - name: portfolio-app + image: ghcr.io/mayne0213/portfolio:latest + imagePullPolicy: Always + ports: + - containerPort: 3000 + protocol: TCP + env: + - name: NODE_ENV + value: production + resources: + requests: + memory: "100Mi" + cpu: "50m" + limits: + memory: "200Mi" + cpu: "150m" + livenessProbe: + httpGet: + path: / + port: 3000 + initialDelaySeconds: 30 + periodSeconds: 10 + readinessProbe: + httpGet: + path: / + port: 3000 + initialDelaySeconds: 5 + periodSeconds: 5 + restartPolicy: Always diff --git a/deploy/k8s/base/kustomization.yaml b/deploy/k8s/base/kustomization.yaml new file mode 100644 index 0000000..e0af853 --- /dev/null +++ b/deploy/k8s/base/kustomization.yaml @@ -0,0 +1,14 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +resources: + - deployment.yaml + - service.yaml + +commonLabels: + app.kubernetes.io/name: portfolio + app.kubernetes.io/component: web + +images: + - name: ghcr.io/mayne0213/portfolio + newTag: latest diff --git a/deploy/k8s/base/service.yaml b/deploy/k8s/base/service.yaml new file mode 100644 index 0000000..4e5e3ff --- /dev/null +++ b/deploy/k8s/base/service.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: Service +metadata: + name: portfolio-service + labels: + app: portfolio-app +spec: + type: ClusterIP + ports: + - name: http + port: 80 + targetPort: 3000 + protocol: TCP + selector: + app: portfolio-app diff --git a/deploy/k8s/overlays/prod/deployment-patch.yaml b/deploy/k8s/overlays/prod/deployment-patch.yaml new file mode 100644 index 0000000..b0e9a67 --- /dev/null +++ b/deploy/k8s/overlays/prod/deployment-patch.yaml @@ -0,0 +1,19 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: portfolio-app + labels: + environment: production +spec: + replicas: 1 + template: + spec: + containers: + - name: portfolio-app + resources: + requests: + memory: "100Mi" + cpu: "50m" + limits: + memory: "200Mi" + cpu: "150m" diff --git a/deploy/k8s/overlays/prod/kustomization.yaml b/deploy/k8s/overlays/prod/kustomization.yaml new file mode 100644 index 0000000..6bdd415 --- /dev/null +++ b/deploy/k8s/overlays/prod/kustomization.yaml @@ -0,0 +1,18 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +namespace: portfolio + +resources: + - ../../base + +commonLabels: + environment: production + +# 이미지 νƒœκ·Έ μ„€μ • +images: + - name: ghcr.io/mayne0213/portfolio + newTag: latest + +patchesStrategicMerge: + - deployment-patch.yaml diff --git a/scripts/common.sh b/scripts/common.sh new file mode 100755 index 0000000..4bec043 --- /dev/null +++ b/scripts/common.sh @@ -0,0 +1,173 @@ +#!/bin/bash + +# Portfolio 슀크립트 곡톡 μœ ν‹Έλ¦¬ν‹° ν•¨μˆ˜λ“€ +# λͺ¨λ“  Portfolio μŠ€ν¬λ¦½νŠΈμ—μ„œ μ‚¬μš©ν•  수 μžˆλŠ” 곡톡 κΈ°λŠ₯듀을 μ •μ˜ + +set -e +# shopt -s inherit_errexit + +# 곡톡 슀크립트의 μ ˆλŒ€ 경둜 기반 디렉토리 μƒμˆ˜ +# ν•¨μˆ˜ 호좜 μ»¨ν…μŠ€νŠΈμ— 따라 BASH_SOURCE 해석이 λ‹¬λΌμ§ˆ 수 μžˆμœΌλ―€λ‘œ +# λ‘œλ“œ μ‹œμ μ— κ³ μ •ν•΄ μ‹ λ’° κ°€λŠ₯ν•œ 루트λ₯Ό κ³„μ‚°ν•œλ‹€ +PORTFOLIO_SCRIPTS_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PORTFOLIO_ROOT="$(dirname "${PORTFOLIO_SCRIPTS_DIR}")" + +# 색상 μ •μ˜ +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +# λ‘œκΉ… ν•¨μˆ˜λ“€ +log_info() { + echo -e "${GREEN}[INFO]${NC} ${1}" +} + +log_warn() { + echo -e "${YELLOW}[WARN]${NC} ${1}" +} + +log_error() { + echo -e "${RED}[ERROR]${NC} ${1}" +} + +log_debug() { + echo -e "${BLUE}[DEBUG]${NC} ${1}" +} + +# 경둜 계산 ν•¨μˆ˜ +get_portfolio_root() { + # λ‘œλ“œ μ‹œμ μ— κ³ μ •λœ 루트 경둜 λ°˜ν™˜ + echo "${PORTFOLIO_ROOT}" +} + +get_mayne_root() { + # portfolio 루트λ₯Ό κΈ°μ€€μœΌλ‘œ mayne 루트 경둜 계산 + local portfolio_root + portfolio_root="$(get_portfolio_root)" + dirname "$(dirname "${portfolio_root}")" +} + +# 확인 ν•¨μˆ˜ +confirm_action() { + local message="${1}" + local default="${2:-N}" + + if [[ "${default}" == "Y" ]]; then + read -p "${message} (Y/n): " -n 1 -r + echo + if [[ ${REPLY} =~ ^[Nn]$ ]]; then + return 1 + fi + return 0 + else + read -p "${message} (y/N): " -n 1 -r + echo + if [[ ${REPLY} =~ ^[Yy]$ ]]; then + return 0 + fi + return 1 + fi +} + +# ν•„μˆ˜ 디렉토리 확인 +check_required_dirs() { + local portfolio_root + portfolio_root="$(get_portfolio_root)" + local dirs=("${@}") + + for dir in "${dirs[@]}"; do + if [[ ! -d "${portfolio_root}/${dir}" ]]; then + log_error "ν•„μˆ˜ 디렉토리가 μ—†μŠ΅λ‹ˆλ‹€: ${dir}" + exit 1 + fi + done +} + +# ν•„μˆ˜ 파일 확인 +check_required_files() { + local portfolio_root + portfolio_root="$(get_portfolio_root)" + local files=("${@}") + + for file in "${files[@]}"; do + if [[ ! -f "${portfolio_root}/${file}" ]]; then + log_error "ν•„μˆ˜ 파일이 μ—†μŠ΅λ‹ˆλ‹€: ${file}" + exit 1 + fi + done +} + +# Docker κ΄€λ ¨ μœ ν‹Έλ¦¬ν‹° +docker_cleanup_portfolio() { + log_info "Portfolio κ΄€λ ¨ Docker λ¦¬μ†ŒμŠ€ 정리 쀑..." + + # μ»¨ν…Œμ΄λ„ˆ 쀑지 및 μ‚­μ œ + docker-compose -p portfolio -f deploy/docker/docker-compose.yml down --remove-orphans 2>/dev/null || true + docker-compose -p portfolio -f deploy/docker/docker-compose.dev.yml down --remove-orphans 2>/dev/null || true + docker ps -aq --filter "name=portfolio" | xargs -r docker rm -f 2>/dev/null || true + + # 이미지 μ‚­μ œ + docker images --filter "reference=portfolio*" -q | xargs -r docker rmi -f 2>/dev/null || true + docker images --filter "reference=*portfolio*" -q | xargs -r docker rmi -f 2>/dev/null || true + + # λ³Όλ₯¨ μ‚­μ œ + docker volume ls -q --filter "name=portfolio" | xargs -r docker volume rm -f 2>/dev/null || true + + # μ‹œμŠ€ν…œ 정리 + docker system prune -f + + log_info "Docker λ¦¬μ†ŒμŠ€ 정리 μ™„λ£Œ" +} + +# ν™˜κ²½ λ³€μˆ˜ λ‘œλ“œ +load_env_file() { + local portfolio_root + portfolio_root="$(get_portfolio_root)" + local env_file="${portfolio_root}/.env" + + if [[ -f "${env_file}" ]]; then + log_info "ν™˜κ²½ λ³€μˆ˜ 파일 λ‘œλ“œ: ${env_file}" + # shellcheck source=/dev/null + source "${env_file}" + return 0 + else + log_warn "ν™˜κ²½ λ³€μˆ˜ 파일이 μ—†μŠ΅λ‹ˆλ‹€: ${env_file}" + return 1 + fi +} + +# μ—λŸ¬ 처리 +handle_error() { + local exit_code=$? + log_error "슀크립트 μ‹€ν–‰ 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€ (μ’…λ£Œ μ½”λ“œ: ${exit_code})" + exit "${exit_code}" +} + +# 슀크립트 μ‹œμž‘ μ‹œ 곡톡 μ„€μ • +setup_script() { + # μ—λŸ¬ λ°œμƒ μ‹œ μžλ™μœΌλ‘œ handle_error ν•¨μˆ˜ 호좜 + trap handle_error ERR + + # 슀크립트 λ””λ ‰ν† λ¦¬λ‘œ 이동 + local portfolio_root + portfolio_root="$(get_portfolio_root)" + cd "${portfolio_root}" + + log_info "슀크립트 μ‹œμž‘: $(basename "${BASH_SOURCE[1]}")" +} + +# 슀크립트 μ’…λ£Œ μ‹œ 정리 +cleanup_script() { + local exit_code=$? + if [[ "${exit_code}" -eq 0 ]]; then + log_info "슀크립트 μ™„λ£Œ: $(basename "${BASH_SOURCE[1]}")" + else + log_error "슀크립트 μ‹€νŒ¨: $(basename "${BASH_SOURCE[1]}") (μ’…λ£Œ μ½”λ“œ: ${exit_code})" + fi + exit "${exit_code}" +} + +# 슀크립트 μ’…λ£Œ μ‹œ 정리 ν•¨μˆ˜ 등둝 +trap cleanup_script EXIT diff --git a/scripts/docker-build.sh b/scripts/docker-build.sh new file mode 100755 index 0000000..70a3055 --- /dev/null +++ b/scripts/docker-build.sh @@ -0,0 +1,93 @@ +#!/bin/bash + +# Portfolio Docker λΉŒλ“œ 및 μ‹€ν–‰ 슀크립트 +# 곡톡 μœ ν‹Έλ¦¬ν‹° ν•¨μˆ˜ λ‘œλ“œ +source "$(dirname "${BASH_SOURCE[0]}")/common.sh" + +# 슀크립트 μ„€μ • +setup_script + +log_info "πŸš€ Portfolio Docker λΉŒλ“œ 및 μ‹€ν–‰ μ‹œμž‘..." + +# ν•„μˆ˜ 디렉토리 및 파일 확인 +log_info "πŸ“ 폴더 ꡬ쑰 확인 쀑..." +check_required_dirs "services/nextjs" "deploy/docker" + +log_info "πŸ“„ ν•„μˆ˜ 파일 확인 쀑..." +check_required_files "deploy/docker/docker-compose.yml" "deploy/docker/docker-compose.dev.yml" "deploy/docker/Dockerfile.prod" "deploy/docker/Dockerfile.dev" + +log_info "βœ… 폴더 ꡬ쑰 및 ν•„μˆ˜ 파일 확인 μ™„λ£Œ!" + +# ν™˜κ²½ 선택 +echo "" +log_info "🎯 μ‹€ν–‰ν•  ν™˜κ²½μ„ μ„ νƒν•˜μ„Έμš”:" +echo "1) 개발 ν™˜κ²½ (Development)" +echo "2) ν”„λ‘œλ•μ…˜ ν™˜κ²½ (Production)" +echo "3) λΉŒλ“œλ§Œ (Build Only)" +read -p "선택 (1-3): " -n 1 -r +echo + +case ${REPLY} in + 1) + log_info "πŸ”§ 개발 ν™˜κ²½ λΉŒλ“œ 및 μ‹€ν–‰ 쀑..." + cd deploy/docker + docker-compose -p portfolio -f docker-compose.dev.yml build --no-cache + docker-compose -p portfolio -f docker-compose.dev.yml up -d + PORTFOLIO_ROOT=$(get_portfolio_root) + cd "${PORTFOLIO_ROOT}" + ENV_TYPE="development" + COMPOSE_FILE_PATH="deploy/docker/docker-compose.dev.yml" + ;; + 2) + log_info "🏭 ν”„λ‘œλ•μ…˜ ν™˜κ²½ λΉŒλ“œ 및 μ‹€ν–‰ 쀑..." + cd deploy/docker + docker-compose -p portfolio -f docker-compose.yml build --no-cache + docker-compose -p portfolio -f docker-compose.yml up -d + PORTFOLIO_ROOT=$(get_portfolio_root) + cd "${PORTFOLIO_ROOT}" + ENV_TYPE="production" + COMPOSE_FILE_PATH="deploy/docker/docker-compose.yml" + ;; + 3) + log_info "πŸ”¨ 이미지 λΉŒλ“œλ§Œ μ‹€ν–‰ 쀑..." + cd deploy/docker + log_info " - 개발 이미지 λΉŒλ“œ 쀑..." + docker-compose -p portfolio -f docker-compose.dev.yml build --no-cache + log_info " - ν”„λ‘œλ•μ…˜ 이미지 λΉŒλ“œ 쀑..." + docker-compose -p portfolio -f docker-compose.yml build --no-cache + PORTFOLIO_ROOT=$(get_portfolio_root) + cd "${PORTFOLIO_ROOT}" + log_info "βœ… λΉŒλ“œ μ™„λ£Œ! μ‹€ν–‰ν•˜λ €λ©΄ λ‹€μ‹œ 이 슀크립트λ₯Ό μ‹€ν–‰ν•˜κ³  ν™˜κ²½μ„ μ„ νƒν•˜μ„Έμš”." + exit 0 + ;; + *) + log_error "잘λͺ»λœ μ„ νƒμž…λ‹ˆλ‹€." + exit 1 + ;; +esac + +# μ„œλΉ„μŠ€ μƒνƒœ 확인 +echo "" +log_info "⏳ μ„œλΉ„μŠ€ μ‹œμž‘ λŒ€κΈ° 쀑..." +sleep 10 + +echo "" +log_info "πŸ“Š μ„œλΉ„μŠ€ μƒνƒœ 확인:" +docker-compose -p portfolio -f "${COMPOSE_FILE_PATH}" ps + +echo "" +log_info "πŸ” μ»¨ν…Œμ΄λ„ˆ 둜그 확인:" +echo " - μ• ν”Œλ¦¬μΌ€μ΄μ…˜ 둜그: docker-compose -p portfolio -f ${COMPOSE_FILE_PATH} logs -f app" + +echo "" +log_info "🌐 접속 URL:" +echo " - μ• ν”Œλ¦¬μΌ€μ΄μ…˜: http://localhost:3005" + +echo "" +log_info "βœ… Docker λΉŒλ“œ 및 μ‹€ν–‰ μ™„λ£Œ!" +echo "" +log_info "πŸ“‹ μœ μš©ν•œ λͺ…λ Ήμ–΄:" +echo " - μ„œλΉ„μŠ€ 쀑지: docker-compose -p portfolio -f ${COMPOSE_FILE_PATH} down" +echo " - 둜그 확인: docker-compose -p portfolio -f ${COMPOSE_FILE_PATH} logs -f" +echo " - μ„œλΉ„μŠ€ μž¬μ‹œμž‘: docker-compose -p portfolio -f ${COMPOSE_FILE_PATH} restart" +echo "" diff --git a/scripts/docker-cleanup.sh b/scripts/docker-cleanup.sh new file mode 100755 index 0000000..0dfe5b8 --- /dev/null +++ b/scripts/docker-cleanup.sh @@ -0,0 +1,38 @@ +#!/bin/bash + +# Portfolio Docker λ¦¬μ†ŒμŠ€ 정리 슀크립트 +# 곡톡 μœ ν‹Έλ¦¬ν‹° ν•¨μˆ˜ λ‘œλ“œ +source "$(dirname "${BASH_SOURCE[0]}")/common.sh" + +# 슀크립트 μ„€μ • +setup_script + +log_info "🧹 Portfolio Docker λ¦¬μ†ŒμŠ€ 정리 μ‹œμž‘..." + +# ν˜„μž¬ μ‹€ν–‰ 쀑인 Portfolio κ΄€λ ¨ μ»¨ν…Œμ΄λ„ˆ 확인 +log_info "πŸ“‹ ν˜„μž¬ μ‹€ν–‰ 쀑인 Portfolio κ΄€λ ¨ μ»¨ν…Œμ΄λ„ˆ:" +docker ps -a --filter "name=portfolio" --format "table {{.Names}}\t{{.Status}}\t{{.Image}}" + +echo "" +if confirm_action "⚠️ λͺ¨λ“  Portfolio κ΄€λ ¨ μ»¨ν…Œμ΄λ„ˆ, 이미지, λ³Όλ₯¨μ„ μ‚­μ œν•˜μ‹œκ² μŠ΅λ‹ˆκΉŒ?"; then + : # 계속 μ§„ν–‰ +else + log_info "μž‘μ—…μ΄ μ·¨μ†Œλ˜μ—ˆμŠ΅λ‹ˆλ‹€." + exit 0 +fi + +echo "" +log_info "πŸ›‘ μ»¨ν…Œμ΄λ„ˆ 쀑지 및 μ‚­μ œ 쀑..." + +# Docker 정리 μ‹€ν–‰ +docker_cleanup_portfolio + +log_info "βœ… 정리 μ™„λ£Œ!" +echo "" +log_info "πŸ“Š μ •λ¦¬λœ λ¦¬μ†ŒμŠ€:" +echo " - Portfolio κ΄€λ ¨ μ»¨ν…Œμ΄λ„ˆ: μ‚­μ œλ¨" +echo " - Portfolio κ΄€λ ¨ 이미지: μ‚­μ œλ¨" +echo " - Portfolio κ΄€λ ¨ λ³Όλ₯¨: μ‚­μ œλ¨" +echo " - μ‚¬μš©ν•˜μ§€ μ•ŠλŠ” Docker λ¦¬μ†ŒμŠ€: 정리됨" +echo "" +log_info "πŸš€ 이제 './scripts/docker-build.sh' 슀크립트λ₯Ό μ‹€ν–‰ν•˜μ—¬ μž¬λΉŒλ“œν•˜μ„Έμš”!" diff --git a/services/nextjs b/services/nextjs new file mode 160000 index 0000000..527de6b --- /dev/null +++ b/services/nextjs @@ -0,0 +1 @@ +Subproject commit 527de6b0f3be17645dedbb94b217a8a922b02380