4. Docker: контейнеры

Docker — контейнеризация приложений. Упаковываешь код со всеми зависимостями в изолированный контейнер, который одинаково работает везде.
Зачем Docker?
Заголовок раздела «Зачем Docker?»Без Docker: С Docker:───────────────────── ─────────────────────"У меня работает!" Работает везде одинаковоРазные версии Node Версия зафиксирована в DockerfileРучная установка deps Deps внутри образа"Обнови Python на сервере" Просто поменяй образОсновные концепции
Заголовок раздела «Основные концепции»Image (образ) — шаблон, blueprintContainer — запущенный экземпляр образаDockerfile — инструкции для создания образаRegistry — хранилище образов (Docker Hub, GHCR)Layer — слой кэширования в образеVolume — персистентное хранилище данныхNetwork — сеть между контейнерамиDockerfile: базовый
Заголовок раздела «Dockerfile: базовый»# DockerfileFROM node:20-alpine # базовый образ
WORKDIR /app # рабочая директория
# Копируем package файлы (кэширование слоёв)COPY package.json package-lock.json ./
# Устанавливаем зависимостиRUN npm ci --only=production
# Копируем исходный кодCOPY . .
# Переменные окруженияENV NODE_ENV=productionENV PORT=3000
# Открываем портEXPOSE 3000
# Команда запускаCMD ["node", "src/index.js"]Dockerfile для Next.js (оптимизированный)
Заголовок раздела «Dockerfile для Next.js (оптимизированный)»# Dockerfile# Stage 1: DependenciesFROM node:20-alpine AS depsRUN apk add --no-cache libc6-compatWORKDIR /app
COPY package.json package-lock.json ./RUN npm ci
# Stage 2: BuilderFROM node:20-alpine AS builderWORKDIR /appCOPY --from=deps /app/node_modules ./node_modulesCOPY . .
ENV NEXT_TELEMETRY_DISABLED 1
RUN npm run build
# Stage 3: RunnerFROM node:20-alpine AS runnerWORKDIR /app
ENV NODE_ENV productionENV NEXT_TELEMETRY_DISABLED 1
RUN addgroup --system --gid 1001 nodejsRUN adduser --system --uid 1001 nextjs
COPY --from=builder /app/public ./publicCOPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
USER nextjs
EXPOSE 3000ENV PORT 3000
CMD ["node", "server.js"]Multi-stage builds: зачем?
Заголовок раздела «Multi-stage builds: зачем?»Без multi-stage:Образ = code + node_modules (dev) + build toolsРазмер: ~800MB
С multi-stage:Финальный образ = только production файлыРазмер: ~100MB
Плюсы:✅ Меньший размер✅ Меньше поверхность атаки✅ Быстрее деплой.dockerignore
Заголовок раздела «.dockerignore»node_modules.next.git.gitignore*.md.env*!.env.examplenpm-debug.logDockerfiledocker-compose*.ymlcoverage/.nyc_output/Основные команды Docker
Заголовок раздела «Основные команды Docker»# Сборка образаdocker build -t myapp:latest .docker build -t myapp:v1.2.3 .docker build --no-cache -t myapp:latest . # без кэша
# Запуск контейнераdocker run myapp:latest # в foregrounddocker run -d myapp:latest # в фоне (detached)docker run -p 3000:3000 myapp:latest # с маппингом портовdocker run -e NODE_ENV=production myapp # с env varsdocker run -v /data:/app/data myapp # с volumedocker run --name mycontainer myapp # с именем
# Просмотр контейнеровdocker ps # запущенныеdocker ps -a # все включая остановленныеdocker stats # метрики в реальном времени
# Работа с контейнеромdocker exec -it CONTAINER_ID bash # войти в контейнерdocker logs CONTAINER_ID # логиdocker logs -f CONTAINER_ID # логи в реальном времениdocker stop CONTAINER_ID # остановитьdocker rm CONTAINER_ID # удалить
# Образыdocker images # список образовdocker rmi IMAGE_ID # удалить образdocker pull nginx:alpine # скачать образdocker push myapp:latest # запушить в registry
# Очисткаdocker system prune # удалить неиспользуемоеdocker system prune -a # удалить всё неиспользуемоеVolumes — персистентные данные
Заголовок раздела «Volumes — персистентные данные»# Создание volumedocker volume create mydata
# Использованиеdocker run -v mydata:/app/data myappdocker run -v $(pwd)/data:/app/data myapp # bind mount
# Просмотрdocker volume lsdocker volume inspect mydataNetworks
Заголовок раздела «Networks»# Создание сетиdocker network create mynet
# Запуск в сетиdocker run --network mynet --name api myappdocker run --network mynet --name db postgres
# Теперь контейнеры видят друг друга по имени:# api может подключиться к db через hostname "db"Публикация в Docker Hub
Заголовок раздела «Публикация в Docker Hub»# Логинdocker login
# Тег для Docker Hubdocker tag myapp:latest username/myapp:latestdocker tag myapp:latest username/myapp:v1.0.0
# Pushdocker push username/myapp:latestdocker push username/myapp:v1.0.0GitHub Container Registry (GHCR)
Заголовок раздела «GitHub Container Registry (GHCR)»# Логин через GitHub tokenecho $GITHUB_TOKEN | docker login ghcr.io -u USERNAME --password-stdin
# Тегdocker tag myapp:latest ghcr.io/username/myapp:latest
# Pushdocker push ghcr.io/username/myapp:latestАвтоматическая сборка в GitHub Actions
Заголовок раздела «Автоматическая сборка в GitHub Actions»name: Build and Push Docker
on: push: branches: [main] tags: ['v*.*.*']
jobs: docker: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4
- name: Docker meta id: meta uses: docker/metadata-action@v5 with: images: ghcr.io/${{ github.repository }} tags: | type=ref,event=branch type=semver,pattern={{version}} type=sha,prefix=sha-
- name: Login to GHCR uses: docker/login-action@v3 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push uses: docker/build-push-action@v5 with: context: . push: true tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} cache-from: type=gha cache-to: type=gha,mode=maxКлючевые моменты
Заголовок раздела «Ключевые моменты»- Docker упаковывает приложение со всеми зависимостями
- Dockerfile — инструкции сборки образа
- Multi-stage builds уменьшают размер финального образа
.dockerignoreисключает ненужные файлы из контекста- Volumes — для персистентных данных (БД и т.д.)
- Networks — для коммуникации между контейнерами
- GHCR — бесплатный registry для GitHub проектов
Интерактивный пример
Заголовок раздела «Интерактивный пример»Структура Dockerfile — нажимай на слои, чтобы узнать их назначение: