Материал подойдёт разработчикам, которым нужно подготовить локальную среду на Mac с чипом серии M. Мы разберём установку Docker Desktop, проверку архитектуры образов и сборку проекта с MySQL и Redis через Compose, а затем проверим доступность сервисов и сохранение данных.
В Docker Desktop не запускаются контейнеры или приложение не видит базу данных? Быстрый путь: установите Docker Desktop для Apple Silicon, проверьте архитектуру образов, а затем запустите через Docker Compose MySQL, Redis и веб-сервис с правильными адресами и томами.
Подходит: разработчикам, впервые настраивающим контейнерную среду на Mac с чипом серии M, и командам, которым нужно проверить локальную или удалённую среду для разработки. Не подходит: для готового производственного развёртывания. Пример ниже предназначен для локальной разработки и не заменяет настройку защищённой инфраструктуры.
Что нужно проверить до установки Docker на Mac
Docker Desktop на Mac запускает Linux-контейнеры внутри виртуализированной Linux-среды. Это отличается от запуска контейнеров непосредственно в macOS. Для пользователя важны не внутренние механизмы, а два следствия: Docker Desktop должен поддерживаться вашей версией macOS, а образ контейнера должен подходить архитектуре среды исполнения. Условия установки и доступные варианты загрузки сверяйте с документацией Docker Desktop для Mac. Связь виртуализации с запуском Linux на Mac описана в документации Apple о Linux в виртуальной машине.
Как установить Docker Desktop на Mac с чипом серии M и понять, что Docker работает? Скачайте установщик для Apple Silicon с официальной страницы, завершите установку и откройте Docker Desktop. После запуска проверьте в приложении, что движок работает, затем выполните docker version и docker compose version. Первая команда должна показать сведения и о клиенте, и о сервере Docker. Если выводится только информация о клиенте или появляется ошибка подключения, проверьте состояние Docker Desktop, а не переходите сразу к настройке проекта.
До запуска проекта зафиксируйте три условия:
- Docker Desktop установлен и завершил запуск.
- Образ каждого сервиса подходит для используемой архитектуры.
- В Compose-файле заданы адреса сервисов, порты и постоянные тома.
На этом этапе не нужно угадывать требования к ресурсам или выбирать особые параметры запуска. Сначала проверьте поддерживаемую конфигурацию по официальной документации, а затем наблюдайте за фактическим состоянием контейнеров.
Первый запуск: проверяем клиент, сервер и образ
После установки выполните команды в терминале:
docker version
docker compose version
docker run --rm hello-world
Последняя команда загружает и запускает небольшой тестовый контейнер, а затем удаляет его. Успешное сообщение показывает, что клиент смог обратиться к движку, а тот — запустить контейнер. Если ошибка указывает на недоступный сервер, откройте Docker Desktop и дождитесь готовности движка. Если не скачивается образ, проверьте сетевое подключение и повторите команду после устранения причины.
Для проверки архитектуры образа используйте:
docker buildx imagetools inspect mysql:8.4
docker buildx imagetools inspect redis:7
Список платформ в выводе помогает выяснить, какие варианты опубликованы для образа. Общий принцип выбора платформ и сборки образов описан в документации Docker по многоплатформенным образам. Для конкретных образов сверяйтесь с описанием официального образа MySQL и описанием официального образа Redis.
Что делать, если Docker сообщает о несовместимой архитектуре? Сначала найдите сведения о платформах в описании именно этого образа и изучите сообщение запуска. Не добавляйте параметр --platform наугад: принудительный выбор платформы не подтверждает совместимость приложения и может привести к запуску через эмуляцию. Если образ сторонний, уточните у его сопровождающих, публикуется ли сборка для нужной платформы.
Если контейнер завершается, команда
docker psего не покажет. Используйтеdocker ps -aиdocker logs ИМЯ_ИЛИ_ID, чтобы найти остановившийся контейнер и прочитать причину завершения.
Шаг 1. Создаём проект и Compose-конфигурацию
Создайте каталог проекта и внутри него файлы compose.yaml, Dockerfile, package.json и app.js. В Compose каждый сервис получает имя, по которому другие контейнеры могут обращаться к нему в общей сети. Проброс портов нужен для подключения с Mac; тома отделяют сохраняемые данные от жизненного цикла контейнера. Эти роли параметров и сетей описаны в справочнике Compose по настройке сервисов.
Сохраните в compose.yaml такую конфигурацию:
services:
mysql:
image: mysql:8.4
environment:
MYSQL_DATABASE: appdb
MYSQL_USER: app
MYSQL_PASSWORD: ${MYSQL_PASSWORD:?Set MYSQL_PASSWORD in .env}
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD:?Set MYSQL_ROOT_PASSWORD in .env}
ports:
- "127.0.0.1:3306:3306"
volumes:
- mysql_data:/var/lib/mysql
healthcheck:
test:
- CMD-SHELL
- mysqladmin ping -h 127.0.0.1 -uroot -p"$${MYSQL_ROOT_PASSWORD}" --silent
interval: 5s
timeout: 5s
retries: 20
redis:
image: redis:7
command: ["redis-server", "--appendonly", "yes"]
ports:
- "127.0.0.1:6379:6379"
volumes:
- redis_data:/data
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
timeout: 3s
retries: 20
web:
build: .
environment:
PORT: "3000"
MYSQL_HOST: mysql
MYSQL_DATABASE: appdb
MYSQL_USER: app
MYSQL_PASSWORD: ${MYSQL_PASSWORD:?Set MYSQL_PASSWORD in .env}
REDIS_URL: redis://redis:6379
ports:
- "127.0.0.1:3000:3000"
depends_on:
mysql:
condition: service_healthy
redis:
condition: service_healthy
healthcheck:
test:
- CMD
- node
- -e
- "fetch('http://127.0.0.1:3000/health').then(r => process.exit(r.ok ? 0 : 1)).catch(() => process.exit(1))"
interval: 10s
timeout: 5s
retries: 10
volumes:
mysql_data:
redis_data:
В этой конфигурации mysql и redis — имена внутренних сервисов, а не адреса localhost. Поэтому веб-приложение подключается к MySQL по mysql:3306, к Redis по redis:6379. Адрес 127.0.0.1 в публикации портов, напротив, ограничивает доступ к сервисам с самого Mac. Порты 3306, 6379 и 3000 здесь заданы как выбранные значения для проекта: при совпадении с уже занятым портом измените левую часть записи, например 127.0.0.1:3307:3306. Механизм сети Docker Desktop для доступа между хостом и контейнерами объяснён в документации по сетевым возможностям Docker Desktop.
Почему одного depends_on недостаточно? Простое указание порядка запуска не означает, что база уже принимает подключения. Здесь веб-сервис ожидает состояния service_healthy, а MySQL и Redis проверяют свою готовность. Такой подход опирается на описание порядка запуска и проверок готовности в Compose. Он снижает вероятность раннего подключения приложения к ещё не готовой базе, но не исправляет неверный пароль или ошибку в коде.
Шаг 2. Добавляем веб-сервис с проверкой MySQL и Redis
В Dockerfile укажите базовый образ Node.js и установку зависимостей проекта:
FROM node:22-alpine
WORKDIR /app
COPY package.json ./
RUN npm install --omit=dev
COPY app.js ./
EXPOSE 3000
CMD ["node", "app.js"]
В package.json задайте зависимости приложения:
{
"name": "mac-compose-demo",
"private": true,
"version": "1.0.0",
"scripts": {
"start": "node app.js"
},
"dependencies": {
"mysql2": "^3.11.0",
"redis": "^4.7.0"
}
}
Значение version здесь описывает пакет приложения, а не гарантирует версию его зависимостей. Для повторяемых сборок в команде сохраните package-lock.json и используйте npm ci; не полагайтесь на диапазоны зависимостей при строгом требовании воспроизводимости.
В app.js поместите минимальную проверку соединений:
const http = require("node:http");
const mysql = require("mysql2/promise");
const { createClient } = require("redis");
const pool = mysql.createPool({
host: process.env.MYSQL_HOST,
user: process.env.MYSQL_USER,
password: process.env.MYSQL_PASSWORD,
database: process.env.MYSQL_DATABASE,
waitForConnections: true
});
const redis = createClient({ url: process.env.REDIS_URL });
redis.on("error", (error) => console.error("Redis:", error.message));
async function connectWithRetry() {
for (;;) {
try {
await pool.query("SELECT 1");
await redis.connect();
return;
} catch (error) {
console.error("Ожидание баз данных:", error.message);
await new Promise((resolve) => setTimeout(resolve, 2000));
}
}
}
const server = http.createServer(async (_request, response) => {
try {
const [rows] = await pool.query("SELECT 1 AS mysql_ok");
const count = await redis.incr("web:visits");
response.writeHead(200, { "content-type": "application/json" });
response.end(JSON.stringify({
mysql: rows[0].mysql_ok,
redis_counter: count
}));
} catch (error) {
console.error(error);
response.writeHead(503, { "content-type": "application/json" });
response.end(JSON.stringify({ error: "service unavailable" }));
}
});
connectWithRetry().then(() => {
server.listen(Number(process.env.PORT || 3000), "0.0.0.0");
});
Приложение использует имена сервисов Compose, а не localhost: внутри контейнера localhost указывает на сам контейнер. Обработчик /health из этого минимального примера не выделен отдельно, поэтому добавьте его, чтобы проверка готовности из Compose получила корректный ответ. Замените обработчик сервера таким вариантом:
const server = http.createServer(async (request, response) => {
try {
const [rows] = await pool.query("SELECT 1 AS mysql_ok");
await redis.ping();
response.writeHead(200, { "content-type": "application/json" });
response.end(JSON.stringify({
mysql: rows[0].mysql_ok,
redis: "PONG"
}));
} catch (error) {
console.error(error);
response.writeHead(503, { "content-type": "application/json" });
response.end(JSON.stringify({ error: "service unavailable" }));
}
});
Так веб-проверка сообщает о состоянии подключений к обоим хранилищам. Если нужно проверять именно запись в Redis, используйте INCR в отдельном маршруте приложения и проверьте, что значение меняется при повторном запросе. Подключение к MySQL и назначение переменных окружения также сверяйте с инструкцией MySQL по запуску в Docker.
Шаг 3. Задаём локальные пароли и запускаем сервисы
Создайте рядом с compose.yaml файл .env:
MYSQL_PASSWORD=local-app-change-this
MYSQL_ROOT_PASSWORD=local-root-change-this
Это фиктивные значения для локальной проверки. Не переносите их в опубликованный репозиторий и не используйте в производственной среде. Добавьте .env в .gitignore. Для реального окружения применяйте отдельный механизм управления секретами и отдельные учётные данные.
Проверьте собранную конфигурацию и запустите проект:
docker compose config
docker compose up --build -d
docker compose ps
Если проверка конфигурации сообщает, что переменная не задана, проверьте расположение .env и точное написание имён. Если контейнер не стал healthy, запросите журнал соответствующего сервиса:
docker compose logs --tail=100 mysql
docker compose logs --tail=100 redis
docker compose logs --tail=100 web
Не считайте завершение docker compose up доказательством готовности приложения: сопоставьте состояние сервисов в docker compose ps, их журналы и реальный запрос к веб-приложению.
Шаг 4. Проверяем доступ с Mac и сохранение данных
Откройте http://localhost:3000/health в браузере или выполните:
curl -i http://localhost:3000/health
Успешный ответ должен содержать результат запроса к MySQL и ответ Redis. Это проверяет не только публикацию веб-порта, но и внутренние соединения контейнеров. Если страница недоступна, сначала проверьте состояние web и публикацию порта в выводе docker compose ps. Затем смотрите журнал приложения и только после этого проверяйте учётные данные и имена хостов.
Отдельно проверьте Redis из контейнера:
docker compose exec redis redis-cli SET check-key saved
docker compose exec redis redis-cli GET check-key
Для MySQL подключитесь клиентом внутри контейнера:
docker compose exec mysql mysql -uapp -p appdb
Введите значение MYSQL_PASSWORD, а затем проверьте запросом:
SELECT 1;
Том mysql_data хранит файлы MySQL за пределами изменяемого слоя контейнера. redis_data используется Redis с включённым журналированием AOF. Пересоздание контейнера командой docker compose down удаляет контейнеры, но именованные тома по умолчанию сохраняются. Удаление с docker compose down -v удаляет и тома — не выполняйте эту команду, если данные нужно оставить. Для проверки сначала запишите тестовое значение, перезапустите проект и прочитайте его снова. Сохранение данных относится к конфигурации именно этого Compose-проекта и не является резервным копированием.
Типовые сбои и способ отличить причину
Образ не подходит по архитектуре. Проверьте платформы в выводе docker buildx imagetools inspect и сведения сопровождающих образа. Сопоставьте их с текстом ошибки и журналом контейнера. Не меняйте архитектуру принудительно, пока не подтвердите поддержку образа.
Порт уже занят. Смотрите сообщение Docker при запуске и опубликованные порты в docker compose ps. Поменяйте только порт на стороне Mac, например левое значение в 127.0.0.1:3307:3306; внутренний порт сервиса оставьте соответствующим его конфигурации. Если порт изменился у веб-сервиса, открывайте новый адрес в браузере.
Не совпадает пароль или имя базы. Сверьте .env и переменные сервиса web с настройками mysql. Проверьте журналы MySQL и веб-приложения. Если том базы уже создан, изменение переменной и повторный запуск не обязательно переинициализирует существующую базу: изменение начальных параметров не стирает уже записанные данные автоматически.
Сервис ещё не готов. Сопоставьте результат проверки healthcheck с логами. Если MySQL или Redis не стали healthy, сначала устраните причину в соответствующем сервисе. Увеличение ожидания без анализа журналов лишь откладывает обнаружение ошибки.
Данные исчезли после пересоздания. Убедитесь, что сервис подключён к именованному тому и запуск не сопровождался down -v. Проверьте список томов командой docker volume ls. Удалённый том не восстанавливается командой Compose: для важных данных заранее настройте отдельное резервное копирование.
Для локальной разработки опубликованные здесь порты привязаны к loopback-интерфейсу. Не убирайте это ограничение и не используйте демонстрационные пароли, если сервисы становятся доступны из сети.
Как выбрать между локальной и удалённой средой
| Вариант | Оценка для этой задачи | Когда разумно выбирать |
|---|---|---|
| Docker Desktop на собственном Mac | Высокая для ежедневной локальной разработки; состояние и данные остаются под вашим контролем | Проект нужен рядом с редактором кода, а Mac можно оставлять доступным |
| Удалённый Mac | Высокая, если рабочее место требуется отделить от личного компьютера или организовать удалённый доступ | Нужно подключаться к среде извне или не держать рабочий процесс на локальном устройстве |
| Производственный сервер | Низкая для повторения локальной разработки без адаптации | Сервис должен обслуживать пользователей; тогда необходимы отдельные меры безопасности, резервирования и эксплуатации |
Перед переносом проекта на удалённую машину отдельно оцените доступ к портам, хранение секретов, резервное копирование томов и способ удалённого подключения. Если такой вариант подходит, сравните условия на странице удалённого Mac от ZekVPS; сведения о конкретном предложении для региона проверяйте на странице ZekVPS о Mac в Гонконге. Мы не предполагаем здесь никаких характеристик или результатов для удалённых конфигураций.
Контрольный список перед работой над проектом
- [ ] Docker Desktop запущен, а
docker versionпоказывает клиент и сервер. - [ ] Для образов MySQL и Redis проверены опубликованные платформы.
- [ ]
docker compose configзавершается без ошибок переменных. - [ ] Веб-контейнер использует имена сервисов
mysqlиredis, а неlocalhost. - [ ] Веб-страница отвечает, запрос к MySQL проходит, Redis принимает запись и чтение.
- [ ] Томам назначено сохранение данных; команда удаления томов не используется случайно.
- [ ] Тестовые пароли не применяются вне локальной разработки.
Если локальный Mac справляется с проектом и его можно оставить включённым, Docker Desktop — прямой путь к среде разработки без отдельного удалённого рабочего места. Если локальный компьютер неудобно постоянно держать доступным или требуется подключаться к Mac удалённо, изучите подходящую среду ZekVPS; если же задача связана с постоянной производственной нагрузкой, сначала проектируйте производственное развёртывание, а не переносите демонстрационный Compose без изменений.
Перенесите разработку на удалённый Mac
Арендуйте в ZekVPS выделенный Mac mini M4 с полноценной macOS для разработки и тестирования проектов.
Подключайтесь к удалённой машине по SSH или VNC и работайте в привычном окружении.
Чтобы перевести MCP или Agent из демо в ежедневную работу, сначала зафиксируйте облачный Mac со снимками. Смотреть тарифы ZekVPS Mac mini — Разделите лабораторию и рабочий стол — деплой станет спокойнее.