Docker и Swift: контейнеризация приложений на iOS и macOS

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Введение: зачем Swift-разработчику Docker?

Многие разработчики на Swift (особенно в мире iOS и macOS) привыкли к Xcode, симуляторам и ручному управлению зависимостями. Однако с ростом популярности серверного Swift (например, фреймворка Vapor) и необходимостью автоматизации тестирования и деплоя, контейнеризация становится обязательным навыком.

Docker позволяет упаковать ваше Swift-приложение вместе со всеми зависимостями в изолированный контейнер. Это гарантирует, что код будет работать одинаково на локальной машине, на сервере CI/CD и в production. В этой статье мы разберём, как создать Docker-образ для Swift-проекта, оптимизировать его размер и запустить в контейнере.

1. Основы Docker для Swift: Dockerfile и мультистадийная сборка

Стандартный подход к контейнеризации Swift-приложений — использование мультистадийной сборки. Это позволяет разделить этап компиляции (где нужны тяжёлые инструменты Swift) и этап выполнения (где достаточно только скомпилированного бинарника).

Рассмотрим типичный проект на Swift, например, сервер на Vapor. Создадим файл Dockerfile в корне проекта:

# Первый этап: сборка
FROM swift:5.9-focal AS build

WORKDIR /app

# Копируем файлы Package.swift и Package.resolved для кэширования зависимостей
COPY Package.swift Package.resolved ./
RUN swift package resolve

# Копируем исходный код
COPY . .

# Сборка в режиме release
RUN swift build -c release --static-swift-stdlib

# Второй этап: минимальный образ для выполнения
FROM ubuntu:focal

WORKDIR /app

# Копируем собранный бинарник из первого этапа
COPY --from=build /app/.build/release/Run .

# Открываем порт (по умолчанию Vapor использует 8080)
EXPOSE 8080

# Запускаем приложение
CMD ["./Run"]

Важные моменты:

  • --static-swift-stdlib — статическая линковка стандартной библиотеки Swift уменьшает зависимости на этапе выполнения.
  • Копирование Package.swift до исходников — это трюк для кэширования слоя зависимостей Docker.
  • Базовый образ ubuntu:focal — лёгкий, но содержит все необходимые системные библиотеки.

2. Сборка и запуск контейнера

Теперь, когда Dockerfile готов, соберём образ и запустим контейнер:

# Сборка образа
docker build -t my-swift-app .

# Запуск контейнера в фоне с пробросом порта
docker run -d -p 8080:8080 --name swift-container my-swift-app

# Проверка логов
docker logs swift-container

# Тестирование (если приложение — HTTP сервер)
curl http://localhost:8080/

Если вы используете Swift Package Manager без Vapor, бинарник может называться иначе. В таком случае измените путь в COPY --from=build и CMD. Узнать точное имя бинарника можно командой:

swift build -c release --show-bin-path

2.1. Работа с .dockerignore

Чтобы не копировать в контекст сборки лишние файлы (например, .git, build/), создайте .dockerignore:

.git
.gitignore
.build/
*.xcodeproj
*.xcworkspace
.DS_Store
README.md

3. Docker Compose для Swift-приложений

Если ваш проект использует базу данных (PostgreSQL, Redis) или несколько микросервисов, удобно использовать docker-compose.yml. Пример для Vapor + PostgreSQL:

version: '3.8'

services:
  app:
    build: .
    ports:
      - "8080:8080"
    environment:
      - DATABASE_HOST=db
      - DATABASE_PORT=5432
      - DATABASE_NAME=vapor_db
      - DATABASE_USER=vapor_user
      - DATABASE_PASSWORD=secret
    depends_on:
      - db

  db:
    image: postgres:15
    environment:
      POSTGRES_DB: vapor_db
      POSTGRES_USER: vapor_user
      POSTGRES_PASSWORD: secret
    ports:
      - "5432:5432"
    volumes:
      - postgres_data:/var/lib/postgresql/data

volumes:
  postgres_data:

Запуск одной командой:

docker-compose up -d

Этот подход особенно полезен для локальной разработки и CI/CD, так как вся инфраструктура поднимается автоматически.

4. Оптимизация размера образа для Swift

Образы Swift могут быть большими (гигабайты на этапе сборки). Вот несколько рекомендаций по уменьшению финального размера:

  • Используйте --static-swift-stdlib — как показано выше, это уменьшает количество динамических библиотек.
  • Выбирайте минимальный базовый образ: вместо ubuntu:focal можно попробовать swift:5.9-slim (существуют community-образы) или alpine с дополнительными настройками, но это требует больше ручной работы.
  • Удаляйте кэш SPM на этапе сборки: добавьте RUN rm -rf /app/.build/repositories после копирования исходников.
  • Используйте .dockerignore — исключайте ненужные файлы, как описано выше.

Пример компактного Dockerfile для production (только бинарник):

FROM swift:5.9-focal AS build
WORKDIR /app
COPY . .
RUN swift build -c release --static-swift-stdlib && \
    rm -rf /app/.build/repositories

FROM ubuntu:focal
WORKDIR /app
COPY --from=build /app/.build/release/Run .
EXPOSE 8080
CMD ["./Run"]

После сборки проверьте размер:

docker images | grep my-swift-app

Хороший результат — 50–100 МБ для production образа.

Заключение

Контейнеризация Swift-приложений с помощью Docker — это не только про серверный Swift. Вы можете упаковывать CLI-инструменты, бэкенды для iOS-приложений и даже тестовые окружения для macOS-проектов. Главное преимущество — воспроизводимость окружения и лёгкий деплой.

Начните с простого Dockerfile, добавьте .dockerignore

Блоги

Рекомендаций по книгам