Docker: ๊ฐœ๋ฐœ์—์„œ ํ”„๋กœ๋•์…˜๊นŒ์ง€

์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ปจํ…Œ์ด๋„ˆํ™”๋ฅผ ์œ„ํ•œ ์™„๋ฒฝํ•œ Docker ๊ฐ€์ด๋“œ. Dockerfile, Docker Compose, ๋ฉ€ํ‹ฐ์Šคํ…Œ์ด์ง€ ๋นŒ๋“œ ๋ฐ ํ”„๋กœ๋•์…˜ ๋ฐฐํฌ๋ฅผ ์‹ค์šฉ์ ์ธ ์˜ˆ์ œ์™€ ํ•จ๊ป˜ ์„ค๋ช…ํ•ฉ๋‹ˆ๋‹ค.

๊ฐœ๋ฐœ์—์„œ ํ”„๋กœ๋•์…˜๊นŒ์ง€์˜ Docker ๊ฐ€์ด๋“œ

Docker๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ๊ฐœ๋ฐœ, ํ…Œ์ŠคํŠธ, ๋ฐฐํฌ ๋ฐฉ์‹์„ ๊ทผ๋ณธ์ ์œผ๋กœ ํ˜์‹ ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜๊ณผ ๊ทธ ์˜์กด์„ฑ์„ ํœด๋Œ€ ๊ฐ€๋Šฅํ•œ ์ปจํ…Œ์ด๋„ˆ์— ์บก์Аํ™”ํ•จ์œผ๋กœ์จ Docker๋Š” ์œ ๋ช…ํ•œ "๋‚ด ์ปดํ“จํ„ฐ์—์„œ๋Š” ๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค" ๋ฌธ์ œ๋ฅผ ์ œ๊ฑฐํ•˜๊ณ  ๋ชจ๋“  ํ™˜๊ฒฝ์—์„œ์˜ ์ผ๊ด€์„ฑ์„ ๋ณด์žฅํ•ฉ๋‹ˆ๋‹ค. ์ด ๊ฐ€์ด๋“œ๋Š” ์ฒซ ๋ฒˆ์งธ Dockerfile๋ถ€ํ„ฐ ํ”„๋กœ๋•์…˜ ๋ฐฐํฌ๊นŒ์ง€์˜ ์ „์ฒด ์—ฌ์ •์„ ๋‹ค๋ฃน๋‹ˆ๋‹ค.

2026๋…„์˜ Docker

Docker Desktop 5.x๋Š” ๋„ค์ดํ‹ฐ๋ธŒ containerd ์ง€์›, ์ตœ์ ํ™”๋œ ๋ฆฌ์†Œ์Šค ๊ด€๋ฆฌ, ์›ํ™œํ•œ Kubernetes ํ†ตํ•ฉ์„ ํฌํ•จํ•œ ์ฃผ์š” ์„ฑ๋Šฅ ๊ฐœ์„ ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ๋ฉ€ํ‹ฐ ์•„ํ‚คํ…์ฒ˜ ์ด๋ฏธ์ง€(ARM/x86)๋Š” ์ด์ œ ํ‘œ์ค€ ๊ด€ํ–‰์ด ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

์ปจํ…Œ์ด๋„ˆํ™” ๊ธฐ์ดˆ

์ปจํ…Œ์ด๋„ˆ๋Š” ์ฝ”๋“œ, ๋Ÿฐํƒ€์ž„, ์‹œ์Šคํ…œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๋ฐ ์„ค์ •์„ ํŒจํ‚ค์ง•ํ•˜๋Š” ๊ฒฝ๋Ÿ‰ ์†Œํ”„ํŠธ์›จ์–ด ๋‹จ์œ„์ž…๋‹ˆ๋‹ค. ํ•˜๋“œ์›จ์–ด๋ฅผ ๊ฐ€์ƒํ™”ํ•˜๋Š” ๊ฐ€์ƒ ๋จธ์‹ ๊ณผ ๋‹ฌ๋ฆฌ ์ปจํ…Œ์ด๋„ˆ๋Š” ํ˜ธ์ŠคํŠธ ์‹œ์Šคํ…œ์˜ ์ปค๋„์„ ๊ณต์œ ํ•˜๋ฏ€๋กœ ์‹œ์ž‘์ด ๋น ๋ฅด๊ณ  ๋ฆฌ์†Œ์Šค ์†Œ๋น„๊ฐ€ ์ ์Šต๋‹ˆ๋‹ค.

bash
# terminal
# Docker installation on Ubuntu
sudo apt update
sudo apt install -y docker.io

# Add user to docker group (avoids sudo)
sudo usermod -aG docker $USER

# Verify installation
docker --version
# Docker version 26.1.0, build 1234567

# First container: downloads image and runs
docker run hello-world

์ด ๋ช…๋ น์€ Docker Hub์—์„œ hello-world ์ด๋ฏธ์ง€๋ฅผ ๋‹ค์šด๋กœ๋“œํ•˜๊ณ  ํ™•์ธ ๋ฉ”์‹œ์ง€๋ฅผ ํ‘œ์‹œํ•˜๋Š” ์ปจํ…Œ์ด๋„ˆ๋ฅผ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.

bash
# terminal
# List running containers
docker ps

# List all containers (including stopped)
docker ps -a

# List downloaded images
docker images

# Remove a container
docker rm <container_id>

# Remove an image
docker rmi <image_name>

์ด ๊ธฐ๋ณธ ๋ช…๋ น๋“ค์€ ์ปจํ…Œ์ด๋„ˆ์™€ ์ด๋ฏธ์ง€์˜ ์ˆ˜๋ช… ์ฃผ๊ธฐ๋ฅผ ๊ด€๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

์ฒซ ๋ฒˆ์งธ Dockerfile ์ž‘์„ฑ

Dockerfile์—๋Š” Docker ์ด๋ฏธ์ง€๋ฅผ ๋นŒ๋“œํ•˜๊ธฐ ์œ„ํ•œ ๋ช…๋ น์ด ํฌํ•จ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค. ๊ฐ ๋ช…๋ น์€ ์ตœ์ข… ์ด๋ฏธ์ง€์— ๋ ˆ์ด์–ด๋ฅผ ์ƒ์„ฑํ•˜์—ฌ ์บ์‹ฑ๊ณผ ์žฌ์‚ฌ์šฉ์„ ๊ฐ€๋Šฅํ•˜๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค.

dockerfile
# Dockerfile
# Base image: Node.js 22 on Alpine Linux (lightweight)
FROM node:22-alpine

# Set working directory in the container
WORKDIR /app

# Copy dependency files first (cache optimization)
COPY package*.json ./

# Install dependencies
RUN npm ci --only=production

# Copy source code
COPY . .

# Expose port (documentation)
EXPOSE 3000

# Startup command
CMD ["node", "server.js"]

๋ช…๋ น์˜ ์ˆœ์„œ๋Š” ์บ์‹œ ์ตœ์ ํ™”์— ๋งค์šฐ ์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค. ๋ณ€๊ฒฝ ๋นˆ๋„๊ฐ€ ๋‚ฎ์€ ํŒŒ์ผ(package.json)์€ ์†Œ์Šค ์ฝ”๋“œ๋ณด๋‹ค ๋จผ์ € ๋ณต์‚ฌํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

bash
# terminal
# Build image with a tag
docker build -t my-app:1.0 .

# Run the container
docker run -d -p 3000:3000 --name my-app-container my-app:1.0

# Check logs
docker logs my-app-container

# Access container shell
docker exec -it my-app-container sh

-d ํ”Œ๋ž˜๊ทธ๋Š” ์ปจํ…Œ์ด๋„ˆ๋ฅผ ๋ฐฑ๊ทธ๋ผ์šด๋“œ์—์„œ ์‹คํ–‰ํ•˜๊ณ , -p๋Š” ์ปจํ…Œ์ด๋„ˆ์˜ ํฌํŠธ 3000์„ ํ˜ธ์ŠคํŠธ์˜ ํฌํŠธ 3000์— ๋งคํ•‘ํ•ฉ๋‹ˆ๋‹ค.

Alpine vs Debian

Alpine ์ด๋ฏธ์ง€๋Š” ์ƒ๋‹นํžˆ ์ž‘์Šต๋‹ˆ๋‹ค(Debian์˜ ์•ฝ 120MB์— ๋น„ํ•ด ์•ฝ 5MB). ๊ทธ๋Ÿฌ๋‚˜ glibc ๋Œ€์‹  musl libc๋ฅผ ์‚ฌ์šฉํ•˜๋ฏ€๋กœ ์ผ๋ถ€ ๋„ค์ดํ‹ฐ๋ธŒ ์˜์กด์„ฑ๊ณผ ๋น„ํ˜ธํ™˜์„ฑ์ด ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด Debian ๊ธฐ๋ฐ˜ ์ด๋ฏธ์ง€(node:22-slim)๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ๊ถŒ์žฅ๋ฉ๋‹ˆ๋‹ค.

ํ”„๋กœ๋•์…˜์„ ์œ„ํ•œ ๋ฉ€ํ‹ฐ์Šคํ…Œ์ด์ง€ ๋นŒ๋“œ

๋ฉ€ํ‹ฐ์Šคํ…Œ์ด์ง€ ๋นŒ๋“œ๋Š” ๋นŒ๋“œ ํ™˜๊ฒฝ์„ ๋Ÿฐํƒ€์ž„ ํ™˜๊ฒฝ์—์„œ ๋ถ„๋ฆฌํ•˜์—ฌ ์ตœ์ ํ™”๋œ ํ”„๋กœ๋•์…˜ ์ด๋ฏธ์ง€๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค. ์ตœ์ข… ์ด๋ฏธ์ง€์—๋Š” ํ•„์š”ํ•œ ์•„ํ‹ฐํŒฉํŠธ๋งŒ ํฌํ•จ๋ฉ๋‹ˆ๋‹ค.

dockerfile
# Dockerfile.production
# ============================================
# Stage 1: Build
# ============================================
FROM node:22-alpine AS builder

WORKDIR /app

# Copy and install dependencies (including devDependencies)
COPY package*.json ./
RUN npm ci

# Copy source code
COPY . .

# Build the application (TypeScript, bundling, etc.)
RUN npm run build

# ============================================
# Stage 2: Production
# ============================================
FROM node:22-alpine AS production

# Non-root user for security
RUN addgroup -g 1001 -S nodejs && \
    adduser -S nodejs -u 1001

WORKDIR /app

# Copy only necessary files from builder stage
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/package.json ./

# Switch to non-root user
USER nodejs

# Environment variables
ENV NODE_ENV=production
ENV PORT=3000

EXPOSE 3000

# Startup command
CMD ["node", "dist/server.js"]

์ด ์ ‘๊ทผ ๋ฐฉ์‹์€ ๋นŒ๋“œ ๋„๊ตฌ, devDependencies ๋ฐ ์†Œ์Šค ํŒŒ์ผ์„ ์ œ์™ธํ•˜์—ฌ ์ตœ์ข… ์ด๋ฏธ์ง€ ํฌ๊ธฐ๋ฅผ ํฌ๊ฒŒ ์ค„์ž…๋‹ˆ๋‹ค.

bash
# terminal
# Build with specific file
docker build -f Dockerfile.production -t my-app:production .

# Compare image sizes
docker images | grep my-app
# my-app    production    abc123    150MB
# my-app    1.0           def456    450MB

ํฌ๊ธฐ ๊ฐ์†Œ๋Š” ํ”„๋กœ์ ํŠธ์— ๋”ฐ๋ผ 60-70%์— ๋‹ฌํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ๋ฐฐํฌ ์‹œ๊ฐ„์„ ๊ฐœ์„ ํ•˜๊ณ  ๊ณต๊ฒฉ ํ‘œ๋ฉด์„ ์ค„์ž…๋‹ˆ๋‹ค.

๋กœ์ปฌ ์˜ค์ผ€์ŠคํŠธ๋ ˆ์ด์…˜์„ ์œ„ํ•œ Docker Compose

Docker Compose๋Š” ๋ฉ€ํ‹ฐ ์ปจํ…Œ์ด๋„ˆ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๊ด€๋ฆฌ๋ฅผ ๊ฐ„์†Œํ™”ํ•ฉ๋‹ˆ๋‹ค. YAML ํŒŒ์ผ๋กœ ๋ชจ๋“  ์„œ๋น„์Šค, ๊ตฌ์„ฑ ๋ฐ ์˜์กด์„ฑ์„ ์„ ์–ธํ•ฉ๋‹ˆ๋‹ค.

yaml
# docker-compose.yml
version: "3.9"

services:
  # Main application
  app:
    build:
      context: .
      dockerfile: Dockerfile
    ports:
      - "3000:3000"
    environment:
      - NODE_ENV=development
      - DATABASE_URL=postgresql://postgres:secret@db:5432/myapp
      - REDIS_URL=redis://cache:6379
    volumes:
      # Mount source code for hot-reload
      - ./src:/app/src
      - ./package.json:/app/package.json
    depends_on:
      db:
        condition: service_healthy
      cache:
        condition: service_started
    networks:
      - app-network

  # PostgreSQL database
  db:
    image: postgres:16-alpine
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: secret
      POSTGRES_DB: myapp
    volumes:
      # Data persistence
      - postgres_data:/var/lib/postgresql/data
      # Initialization script
      - ./init.sql:/docker-entrypoint-initdb.d/init.sql
    ports:
      - "5432:5432"
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 5s
      timeout: 5s
      retries: 5
    networks:
      - app-network

  # Redis cache
  cache:
    image: redis:7-alpine
    ports:
      - "6379:6379"
    volumes:
      - redis_data:/data
    command: redis-server --appendonly yes
    networks:
      - app-network

# Named volumes for persistence
volumes:
  postgres_data:
  redis_data:

# Dedicated network for isolation
networks:
  app-network:
    driver: bridge

์„œ๋น„์Šค๋Š” Docker ๋‚ด๋ถ€ ๋„คํŠธ์›Œํฌ๋ฅผ ํ†ตํ•ด ์ด๋ฆ„(db, cache)์œผ๋กœ ํ†ต์‹ ํ•ฉ๋‹ˆ๋‹ค. ํ—ฌ์Šค์ฒดํฌ๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์‹œ์ž‘ ์ „์— ์˜์กด์„ฑ์ด ์ค€๋น„๋˜์—ˆ๋Š”์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.

bash
# terminal
# Start all services
docker compose up -d

# View logs from all services
docker compose logs -f

# Logs from a specific service
docker compose logs -f app

# Stop and remove containers
docker compose down

# Removal including volumes (caution: data loss)
docker compose down -v

# Rebuild after Dockerfile changes
docker compose up -d --build

DevOps ๋ฉด์ ‘ ์ค€๋น„๊ฐ€ ๋˜์…จ๋‚˜์š”?

์ธํ„ฐ๋ž™ํ‹ฐ๋ธŒ ์‹œ๋ฎฌ๋ ˆ์ดํ„ฐ, flashcards, ๊ธฐ์ˆ  ํ…Œ์ŠคํŠธ๋กœ ์—ฐ์Šตํ•˜์„ธ์š”.

์‹œํฌ๋ฆฟ ๋ฐ ํ™˜๊ฒฝ ๋ณ€์ˆ˜ ๊ด€๋ฆฌ

์‹œํฌ๋ฆฟ์˜ ์•ˆ์ „ํ•œ ๊ด€๋ฆฌ๋Š” ํ”„๋กœ๋•์…˜์—์„œ ๋งค์šฐ ์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค. Docker๋Š” ์ƒํ™ฉ์— ๋”ฐ๋ผ ์—ฌ๋Ÿฌ ์ ‘๊ทผ ๋ฐฉ์‹์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

yaml
# docker-compose.override.yml (development only)
version: "3.9"

services:
  app:
    env_file:
      - .env.development
    environment:
      - DEBUG=true

ํ”„๋กœ๋•์…˜์—์„œ๋Š” Docker secrets๊ฐ€ ๋” ๋†’์€ ๋ณด์•ˆ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

yaml
# docker-compose.production.yml
version: "3.9"

services:
  app:
    build:
      context: .
      dockerfile: Dockerfile.production
    secrets:
      - db_password
      - api_key
    environment:
      - NODE_ENV=production
      - DATABASE_PASSWORD_FILE=/run/secrets/db_password
      - API_KEY_FILE=/run/secrets/api_key

secrets:
  db_password:
    file: ./secrets/db_password.txt
  api_key:
    file: ./secrets/api_key.txt

์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ฝ”๋“œ๋Š” ๋งˆ์šดํŠธ๋œ ํŒŒ์ผ์—์„œ ์‹œํฌ๋ฆฟ์„ ์ฝ์Šต๋‹ˆ๋‹ค.

config/secrets.jsjavascript
const fs = require('fs');
const path = require('path');

// Utility function to read Docker secrets
function readSecret(secretName) {
  const secretPath = `/run/secrets/${secretName}`;

  // Check if secret file exists
  if (fs.existsSync(secretPath)) {
    return fs.readFileSync(secretPath, 'utf8').trim();
  }

  // Fallback to classic environment variables
  const envVar = secretName.toUpperCase();
  return process.env[envVar];
}

module.exports = {
  databasePassword: readSecret('db_password'),
  apiKey: readSecret('api_key'),
};

์ด ์ ‘๊ทผ ๋ฐฉ์‹์€ ํ™˜๊ฒฝ ๋ณ€์ˆ˜๋‚˜ Docker ์ด๋ฏธ์ง€์—์„œ ์‹œํฌ๋ฆฟ์ด ๋…ธ์ถœ๋˜๋Š” ๊ฒƒ์„ ๋ฐฉ์ง€ํ•ฉ๋‹ˆ๋‹ค.

Docker ์ด๋ฏธ์ง€ ์ตœ์ ํ™”

์—ฌ๋Ÿฌ ๊ธฐ๋ฒ•์œผ๋กœ ์ด๋ฏธ์ง€ ํฌ๊ธฐ๋ฅผ ์ค„์ด๊ณ  ์„ฑ๋Šฅ์„ ํ–ฅ์ƒ์‹œํ‚ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

dockerfile
# Dockerfile.optimized
FROM node:22-alpine AS base

# Install necessary tools in a single layer
RUN apk add --no-cache \
    dumb-init \
    && rm -rf /var/cache/apk/*

# ============================================
# Stage: Dependencies
# ============================================
FROM base AS deps

WORKDIR /app

# Copy only lock files for caching
COPY package.json package-lock.json ./

# Install with mounted npm cache (BuildKit)
RUN --mount=type=cache,target=/root/.npm \
    npm ci --only=production

# ============================================
# Stage: Builder
# ============================================
FROM base AS builder

WORKDIR /app

COPY package.json package-lock.json ./
RUN --mount=type=cache,target=/root/.npm \
    npm ci

COPY . .
RUN npm run build

# ============================================
# Stage: Production
# ============================================
FROM base AS production

# Image metadata
LABEL maintainer="team@example.com"
LABEL version="1.0"
LABEL description="Production-ready Node.js application"

# Non-root user
RUN addgroup -g 1001 -S nodejs && \
    adduser -S nodejs -u 1001

WORKDIR /app

# Copy production dependencies
COPY --from=deps --chown=nodejs:nodejs /app/node_modules ./node_modules

# Copy build output
COPY --from=builder --chown=nodejs:nodejs /app/dist ./dist
COPY --from=builder --chown=nodejs:nodejs /app/package.json ./

USER nodejs

ENV NODE_ENV=production

# dumb-init as PID 1 for signal handling
ENTRYPOINT ["dumb-init", "--"]
CMD ["node", "dist/server.js"]

dumb-init์„ ์‚ฌ์šฉํ•˜๋ฉด Unix ์‹œ๊ทธ๋„์˜ ์˜ฌ๋ฐ”๋ฅธ ์ฒ˜๋ฆฌ๊ฐ€ ๋ณด์žฅ๋˜์–ด ์ปจํ…Œ์ด๋„ˆ์˜ ๊ทธ๋ ˆ์ด์Šคํ’€ ์…ง๋‹ค์šด์ด ๊ฐ€๋Šฅํ•ด์ง‘๋‹ˆ๋‹ค.

bash
# terminal
# Enable BuildKit for advanced features
export DOCKER_BUILDKIT=1

# Build with cache and detailed output
docker build --progress=plain -t my-app:optimized .

# Analyze image layers
docker history my-app:optimized

# Detailed image inspection
docker inspect my-app:optimized
์ด๋ฏธ์ง€ ๋ณด์•ˆ

์ด๋ฏธ์ง€๋Š” Trivy๋‚˜ Snyk๊ณผ ๊ฐ™์€ ๋„๊ตฌ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ •๊ธฐ์ ์œผ๋กœ ์ทจ์•ฝ์  ์Šค์บ”์„ ์ˆ˜ํ–‰ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๋ฒ ์ด์Šค ์ด๋ฏธ์ง€๋Š” ๋ณด์•ˆ ํŒจ์น˜๋ฅผ ํฌํ•จํ•˜๊ธฐ ์œ„ํ•ด ์ •๊ธฐ์ ์œผ๋กœ ์—…๋ฐ์ดํŠธํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

๊ณ ๊ธ‰ Docker ๋„คํŠธ์›Œํ‚น

Docker๋Š” ๋‹ค์–‘ํ•œ ์‚ฌ์šฉ ์‚ฌ๋ก€๋ฅผ ์œ„ํ•œ ์—ฌ๋Ÿฌ ๋„คํŠธ์›Œํฌ ๋“œ๋ผ์ด๋ฒ„๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

yaml
# docker-compose.networking.yml
version: "3.9"

services:
  # Publicly accessible frontend
  frontend:
    build: ./frontend
    ports:
      - "80:80"
    networks:
      - frontend-network
      - backend-network

  # API accessible only from frontend
  api:
    build: ./api
    networks:
      - backend-network
      - database-network
    # No external ports exposed

  # Isolated database
  database:
    image: postgres:16-alpine
    networks:
      - database-network
    # Accessible only by API

networks:
  frontend-network:
    driver: bridge
  backend-network:
    driver: bridge
    internal: true  # No internet access
  database-network:
    driver: bridge
    internal: true

์ด ๊ตฌ์„ฑ์€ ์ตœ์†Œ ๊ถŒํ•œ ์›์น™์— ๋”ฐ๋ผ ์„œ๋น„์Šค๋ฅผ ๊ฒฉ๋ฆฌํ•ฉ๋‹ˆ๋‹ค. ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋Š” API์—์„œ๋งŒ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

bash
# terminal
# Inspect Docker networks
docker network ls

# Details of a specific network
docker network inspect app-network

# Create a custom network
docker network create --driver bridge --subnet 172.28.0.0/16 custom-network

# Connect a container to an existing network
docker network connect custom-network my-container

๋ณผ๋ฅจ๊ณผ ๋ฐ์ดํ„ฐ ์˜์†์„ฑ

Docker ๋ณผ๋ฅจ์€ ์ปจํ…Œ์ด๋„ˆ ์ˆ˜๋ช… ์ฃผ๊ธฐ๋ฅผ ๋„˜์–ด ๋ฐ์ดํ„ฐ๋ฅผ ๋ณด์กดํ•ฉ๋‹ˆ๋‹ค.

yaml
# docker-compose.volumes.yml
version: "3.9"

services:
  app:
    image: my-app:latest
    volumes:
      # Named volume for persistent data
      - app_data:/app/data
      # Bind mount for development
      - ./uploads:/app/uploads:rw
      # Read-only mount for configuration
      - ./config:/app/config:ro

  backup:
    image: alpine
    volumes:
      # Access same volume for backups
      - app_data:/data:ro
      - ./backups:/backups
    command: |
      sh -c "tar czf /backups/backup-$$(date +%Y%m%d).tar.gz /data"

volumes:
  app_data:
    driver: local
    driver_opts:
      type: none
      device: /path/to/host/data
      o: bind

๋ช…๋ช…๋œ ๋ณผ๋ฅจ๊ณผ ๋ฐ”์ธ๋“œ ๋งˆ์šดํŠธ์˜ ๊ตฌ๋ถ„์€ ์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค. ๋ณผ๋ฅจ์€ Docker๊ฐ€ ๊ด€๋ฆฌํ•˜๊ณ  ๋ฐ”์ธ๋“œ ๋งˆ์šดํŠธ๋Š” ํ˜ธ์ŠคํŠธ ํŒŒ์ผ ์‹œ์Šคํ…œ์„ ์ง์ ‘ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

bash
# terminal
# List volumes
docker volume ls

# Inspect a volume
docker volume inspect app_data

# Remove orphaned volumes
docker volume prune

# Backup a volume
docker run --rm -v app_data:/data -v $(pwd):/backup alpine \
  tar czf /backup/volume-backup.tar.gz /data

ํ”„๋กœ๋•์…˜ ๋ฐฐํฌ

๊ฒฌ๊ณ ํ•œ ๋ฐฐํฌ ์›Œํฌํ”Œ๋กœ์šฐ์—๋Š” ๋นŒ๋“œ, ํ…Œ์ŠคํŠธ ๋ฐ ๋ ˆ์ง€์ŠคํŠธ๋ฆฌ๋กœ์˜ ํ‘ธ์‹œ๊ฐ€ ํฌํ•จ๋ฉ๋‹ˆ๋‹ค.

bash
# deploy.sh
#!/bin/bash
set -e

# Variables
REGISTRY="registry.example.com"
IMAGE_NAME="my-app"
VERSION=$(git describe --tags --always)

echo "Building version: $VERSION"

# Build production image
docker build \
  -f Dockerfile.production \
  -t $REGISTRY/$IMAGE_NAME:$VERSION \
  -t $REGISTRY/$IMAGE_NAME:latest \
  --build-arg BUILD_DATE=$(date -u +"%Y-%m-%dT%H:%M:%SZ") \
  --build-arg VERSION=$VERSION \
  .

# Security scan
echo "Running security scan..."
docker run --rm -v /var/run/docker.sock:/var/run/docker.sock \
  aquasec/trivy image $REGISTRY/$IMAGE_NAME:$VERSION

# Push to registry
echo "Pushing to registry..."
docker push $REGISTRY/$IMAGE_NAME:$VERSION
docker push $REGISTRY/$IMAGE_NAME:latest

echo "Deployment complete: $REGISTRY/$IMAGE_NAME:$VERSION"

์„œ๋ฒ„ ๋ฐฐํฌ๋ฅผ ์œ„ํ•ด ๋ณ„๋„์˜ ํ”„๋กœ๋•์…˜ compose ํŒŒ์ผ์ด ๊ตฌ์„ฑ์„ ์กฐ์ •ํ•ฉ๋‹ˆ๋‹ค.

yaml
# docker-compose.prod.yml
version: "3.9"

services:
  app:
    image: registry.example.com/my-app:latest
    restart: always
    deploy:
      replicas: 3
      resources:
        limits:
          cpus: "0.5"
          memory: 512M
        reservations:
          cpus: "0.25"
          memory: 256M
      update_config:
        parallelism: 1
        delay: 10s
        failure_action: rollback
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 40s
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "3"

์ด ๊ตฌ์„ฑ์€ ์•ˆ์ •์ ์ธ ๋ฐฐํฌ๋ฅผ ์œ„ํ•œ ๋ฆฌ์†Œ์Šค ํ• ๋‹น, ์—…๋ฐ์ดํŠธ ์ „๋žต ๋ฐ ํ—ฌ์Šค์ฒดํฌ๋ฅผ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค.

bash
# terminal
# Production deployment with Docker Compose
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d

# Zero-downtime update (rolling update)
docker compose -f docker-compose.yml -f docker-compose.prod.yml pull
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d --no-deps app

# Rollback if issues occur
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d --no-deps \
  --scale app=0 && \
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d --no-deps app

์ปจํ…Œ์ด๋„ˆ ๋ชจ๋‹ˆํ„ฐ๋ง ๋ฐ ๋””๋ฒ„๊น…

์ปจํ…Œ์ด๋„ˆ ๋ชจ๋‹ˆํ„ฐ๋ง์€ ํ”„๋กœ๋•์…˜์—์„œ ํ•„์ˆ˜์ ์ž…๋‹ˆ๋‹ค.

bash
# terminal
# Real-time statistics for all containers
docker stats

# Statistics for a specific container with custom format
docker stats my-app --format "table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}"

# Inspect processes in a container
docker top my-app

# Real-time Docker events
docker events --filter container=my-app

# Copy files from/to a container
docker cp my-app:/app/logs/error.log ./error.log

์‹ฌ์ธต ๋””๋ฒ„๊น…์„ ์œ„ํ•ด ์—ฌ๋Ÿฌ ๊ธฐ๋ฒ•์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

bash
# terminal
# Interactive shell in a running container
docker exec -it my-app sh

# Execute a single command
docker exec my-app cat /app/config/settings.json

# Start a container in debug mode
docker run -it --rm --entrypoint sh my-app:latest

# Inspect environment variables
docker exec my-app printenv

# Analyze logs with filters
docker logs my-app --since 1h --tail 100 | grep ERROR
yaml
# docker-compose.monitoring.yml
version: "3.9"

services:
  app:
    # ... existing configuration
    labels:
      - "prometheus.scrape=true"
      - "prometheus.port=3000"
      - "prometheus.path=/metrics"

  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.retention.time=15d'

  grafana:
    image: grafana/grafana:latest
    ports:
      - "3001:3000"
    volumes:
      - grafana_data:/var/lib/grafana
    environment:
      - GF_SECURITY_ADMIN_PASSWORD=secret

volumes:
  prometheus_data:
  grafana_data:

์ด ๋ชจ๋‹ˆํ„ฐ๋ง ์Šคํƒ์€ ์ปจํ…Œ์ด๋„ˆ ๋ฉ”ํŠธ๋ฆญ์˜ ์ˆ˜์ง‘๊ณผ ์‹œ๊ฐํ™”๋ฅผ ๊ฐ€๋Šฅํ•˜๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค.

๊ฒฐ๋ก 

Docker๋Š” ๋ชจ๋“  ํ™˜๊ฒฝ์—์„œ์˜ ์ผ๊ด€์„ฑ์„ ๋ณด์žฅํ•˜์—ฌ ๊ฐœ๋ฐœ ์ฃผ๊ธฐ๋ฅผ ํ˜์‹ ํ•ฉ๋‹ˆ๋‹ค. ์ปจํ…Œ์ด๋„ˆํ™”๋Š” ์ด์‹์„ฑ, ๊ฒฉ๋ฆฌ ๋ฐ ์žฌํ˜„์„ฑ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ์ด๋Š” ํ˜„๋Œ€ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์— ํ•„์ˆ˜์ ์ธ ํŠน์„ฑ์ž…๋‹ˆ๋‹ค.

ํ”„๋กœ๋•์…˜์„ ์œ„ํ•œ Docker ์ฒดํฌ๋ฆฌ์ŠคํŠธ

  • โœ… ์ตœ์ ํ™”๋œ ์ด๋ฏธ์ง€๋ฅผ ์œ„ํ•œ ๋ฉ€ํ‹ฐ์Šคํ…Œ์ด์ง€ ๋นŒ๋“œ
  • โœ… ์ปจํ…Œ์ด๋„ˆ ๋‚ด non-root ์‚ฌ์šฉ์ž
  • โœ… ๋ชจ๋“  ์„œ๋น„์Šค์— ํ—ฌ์Šค์ฒดํฌ ๊ตฌ์„ฑ
  • โœ… Docker secrets ๋˜๋Š” ์•ˆ์ „ํ•œ ํ™˜๊ฒฝ ๋ณ€์ˆ˜๋กœ ์‹œํฌ๋ฆฟ ๊ด€๋ฆฌ
  • โœ… ๋ฆฌ์†Œ์Šค ์ œํ•œ(CPU, ๋ฉ”๋ชจ๋ฆฌ) ์ •์˜
  • โœ… ์ค‘์š” ๋ฐ์ดํ„ฐ ์˜์†์„ฑ์„ ์œ„ํ•œ ๋ณผ๋ฅจ
  • โœ… ํŒŒ์ผ ๋กœํ…Œ์ด์…˜ ๊ธฐ๋Šฅ์˜ ์ค‘์•™ ์ง‘์ค‘์‹ ๋กœ๊น…
  • โœ… ๋ฐฐํฌ ์ „ ์ด๋ฏธ์ง€ ๋ณด์•ˆ ์Šค์บ”
  • โœ… ๋‹ค์šดํƒ€์ž„ ์—†๋Š” ์—…๋ฐ์ดํŠธ ์ „๋žต
  • โœ… ์„œ๋น„์Šค ๊ฐ„ ๊ฒฉ๋ฆฌ๋œ ๋„คํŠธ์›Œํฌ

์—ฐ์Šต์„ ์‹œ์ž‘ํ•˜์„ธ์š”!

๋ฉด์ ‘ ์‹œ๋ฎฌ๋ ˆ์ดํ„ฐ์™€ ๊ธฐ์ˆ  ํ…Œ์ŠคํŠธ๋กœ ์ง€์‹์„ ํ…Œ์ŠคํŠธํ•˜์„ธ์š”.

Docker ์ˆ™๋‹ฌ์€ ๋ชจ๋“  ํ˜„๋Œ€ ๊ฐœ๋ฐœ์ž์—๊ฒŒ ๊ธฐ๋ณธ์ ์ธ ๊ธฐ์ˆ ์ž…๋‹ˆ๋‹ค. ๋กœ์ปฌ ํ™˜๊ฒฝ์—์„œ ํ”„๋กœ๋•์…˜ ๋ฐฐํฌ๊นŒ์ง€ Docker๋Š” ์›Œํฌํ”Œ๋กœ์šฐ๋ฅผ ํ‘œ์ค€ํ™”ํ•˜๊ณ  ์šด์˜์„ ๊ฐ„์†Œํ™”ํ•ฉ๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์„œ ์ œ์‹œ๋œ ๊ฐœ๋…๋“ค์€ Kubernetes์™€ ๋Œ€๊ทœ๋ชจ ์ปจํ…Œ์ด๋„ˆ ์˜ค์ผ€์ŠคํŠธ๋ ˆ์ด์…˜์„ ํƒ๊ตฌํ•˜๊ธฐ ์œ„ํ•œ ๊ฒฌ๊ณ ํ•œ ๊ธฐ๋ฐ˜์„ ํ˜•์„ฑํ•ฉ๋‹ˆ๋‹ค.

ํƒœ๊ทธ

#docker
#containerization
#devops
#docker compose
#deployment

๊ณต์œ 

๊ด€๋ จ ๊ธฐ์‚ฌ

์ฒซ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๋ฐฐํฌํ•˜๊ธฐ ์œ„ํ•œ Kubernetes ๊ฐ€์ด๋“œ

Kubernetes: ์ฒซ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋ฐฐํฌํ•˜๊ธฐ

Kubernetes์—์„œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๋ฐฐํฌํ•˜๊ธฐ ์œ„ํ•œ ์‹ค์šฉ ๊ฐ€์ด๋“œ์ž…๋‹ˆ๋‹ค. minikube ์„ค์น˜๋ถ€ํ„ฐ Deployment, Service, ConfigMap๊นŒ์ง€ ๊ตฌ์ฒด์ ์ธ ์˜ˆ์ œ์™€ ํ•จ๊ป˜ ์„ค๋ช…ํ•ฉ๋‹ˆ๋‹ค.

ArgoCD GitOps Kubernetes continuous deployment

ArgoCD์™€ GitOps ์™„๋ฒฝ ๊ฐ€์ด๋“œ 2026: Kubernetes ์ง€์†์  ๋ฐฐํฌ ์ „๋žต๊ณผ ๋ฉด์ ‘ ํ•ต์‹ฌ ์งˆ๋ฌธ

ArgoCD 3.4 GitOps ํŠœํ† ๋ฆฌ์–ผ. Kubernetes ์ง€์†์  ๋ฐฐํฌ, Application CRD, Sync Waves, ๋ฉ€ํ‹ฐํด๋Ÿฌ์Šคํ„ฐ ๊ด€๋ฆฌ, Flux ๋น„๊ต, ๋ฉด์ ‘ ๋นˆ์ถœ ์งˆ๋ฌธ์„ YAML ์˜ˆ์ œ์™€ ํ•จ๊ป˜ ํ•ด์„คํ•ฉ๋‹ˆ๋‹ค.

Terraform Infrastructure as Code ๋ฉด์ ‘ ์ค€๋น„ - HCL ๊ตฌ์„ฑ๊ณผ ํด๋ผ์šฐ๋“œ ํ”„๋กœ๋น„์ €๋‹ ๋‹ค์ด์–ด๊ทธ๋žจ

Terraform ๋ฉด์ ‘ ์งˆ๋ฌธ ์™„๋ฒฝ ๊ฐ€์ด๋“œ 2026: Infrastructure as Code ํ•ต์‹ฌ ์ •๋ฆฌ

Terraform ๋ฉด์ ‘์—์„œ ์ž์ฃผ ์ถœ์ œ๋˜๋Š” ์ƒํƒœ ๊ด€๋ฆฌ, ๋ชจ๋“ˆ, ์›Œํฌ์ŠคํŽ˜์ด์Šค, ํ”„๋กœ๋ฐ”์ด๋”, IaC ๋ชจ๋ฒ” ์‚ฌ๋ก€๋ฅผ ์ฒด๊ณ„์ ์œผ๋กœ ์ •๋ฆฌํ•ฉ๋‹ˆ๋‹ค. 2026๋…„ Terraform 1.14 ๋ฐ HCP Terraform ์ตœ์‹  ๋‚ด์šฉ์„ ๋ฐ˜์˜ํ•˜์˜€์Šต๋‹ˆ๋‹ค.