Этот гайд поможет вам с нуля собрать полностью рабочее окружение для практической разработки:
- Поймёте базовые команды Docker.
- Поднимете PostgreSQL в контейнере.
- Создадите приложение на Go, которое пишет данные в базу через HTTP-запрос.
- Настроите volume для сохранения данных после удаления контейнеров.
- Сделаете сборку приложения в продакшен-стиле: multi-stage build, запуск не от root-пользователя.
💻 Для практики можете использовать онлайн-песочницу iximiuz labs playground, если Docker локально не установлен.
📝 Рекомендуемый клиент для локальной работы с базой: DBeaver
📦 Официальный образ Postgres с документацией:
https://hub.docker.com/_/postgres
Автор урока: Олег Козырев
🔗 Следующий шаг — разберись с Docker Compose!
👉 Урок по Docker Compose: автоматизация запуска приложения и базы
Там мы:
- Поднимаем Postgres и приложение в одной команде.
- Настраиваем
.envпеременные. - Подключаем миграции через Goose.
- И делаем проект готовым для продакшена!
Источник изображения: kinsta.com
Запускаем тестовый контейнер:
docker run hello-worldDocker скачает тестовый образ и выведет сообщение, если всё настроено правильно.
Посмотреть список запущенных контейнеров:
docker psПосмотреть список локальных образов:
docker imagesdocker run -d -p 80:80 --name mynginx nginxТеперь можно открыть в браузере: http://localhost
➡️ Nginx успешно работает!
docker run -d -p 5432:5432 --name mypostgres -e POSTGRES_USER=demo -e POSTGRES_PASSWORD=demo postgres:15Теперь база доступна на порту 5432, и пользователь demo готов к работе.
Если в контейнере есть bash:
docker exec -it mypostgres bashЕсли bash нет, используем sh:
docker exec -it mypostgres shpsql -U demo -d postgres-U demo— имя пользователя-d postgres— база данных по умолчанию
Полезные команды внутри psql:
- Посмотреть все таблицы:
\dt - Посмотреть все объекты:
\d - Посмотреть все базы данных:
\l
Создаём таблицу пользователей:
CREATE TABLE users (
id SERIAL PRIMARY KEY,
username text,
email text,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);Добавляем данные вручную:
INSERT INTO users (username, email) VALUES ('alice', '[email protected]');Проверяем данные:
SELECT * FROM users;Останавливаем контейнер:
docker stop mypostgresУдаляем контейнер и образ:
docker rmi -f mypostgresЧистим все неиспользуемые ресурсы Docker:
docker system prune
⚠️ Внимание: команда очистит все остановленные контейнеры, образы и кэш.
- Устанавливаем Docker Desktop: https://www.docker.com/products/docker-desktop/
- Устанавливаем DBeaver для подключения к базе: https://dbeaver.io/download/
Приложение принимает POST-запрос с username и email и сохраняет их в таблицу users.
Добавляем файл .dockerignore, чтобы не попадали лишние файлы в контейнер при сборке.
Создаём multi-stage Dockerfile:
- Этап сборки — heavy image с Go SDK.
- Этап финальный — лёгкий продакшен образ на Alpine.
- Собираем статически слинкованный бинарник.
Собираем приложение под нужную архитектуру:
docker build --platform linux/amd64 -t my-go-server:v1.0.0 .Для полной пересборки без кэша:
docker build --no-cache --platform linux/amd64 -t my-go-server:v1.0.0 .Проверяем архитектуру образа:
docker image inspect my-go-server:v1.0.0 --format='{{.Architecture}}/{{.Os}}'Запускаем приложение:
docker run -p 8080:8080 my-go-server:v1.0.0Создаём сеть:
docker network create app-networkЗапускаем Postgres в сети:
docker run -d --name mypostgres --network app-network -p 5432:5432 -e POSTGRES_USER=demo -e POSTGRES_PASSWORD=demo postgres:15Запускаем приложение в той же сети:
docker run -p 8080:8080 --network app-network my-go-server:v1.0.0Создаём новое подключение:
- Хост:
localhost - Порт:
5432 - База данных:
postgres - Пользователь:
demo - Пароль:
demo
Создаём таблицу в DBeaver:
CREATE TABLE users (
id SERIAL PRIMARY KEY,
username text,
email text,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);Отправляем POST-запрос для создания пользователя:
curl -X POST http://localhost:8080/users \
-H "Content-Type: application/json" \
-d '{"username": "alice", "email": "[email protected]"}'Проверяем в базе: пользователь появился 🎉
Останавливаем контейнер с базой:
docker stop mypostgresСтартуем снова:
docker start mypostgres✅ Данные остаются!
Удаляем контейнер:
docker rm mypostgresЗапускаем заново без volume — ❌ данные пропали.
Запускаем контейнер с volume:
docker run -d --name mypostgres --network app-network -p 5432:5432 \
-e POSTGRES_USER=demo \
-e POSTGRES_PASSWORD=demo \
-v pgdata:/var/lib/postgresql/data \
postgres:15Создаём таблицу, добавляем пользователя через curl.
Останавливаем контейнер:
docker stop mypostgresЗапускаем снова:
docker start mypostgres✅ Данные сохранены!
Добавляем запуск приложения не от root-пользователя:
# Создаём непривилегированного пользователя и группу
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
# Меняем владельца файлов на созданного пользователя
RUN chown -R appuser:appgroup /app
# Переключаемся на непривилегированного пользователя
USER appuserТеперь контейнер безопасно работает без root-доступа.
Если вам понравился урок — заглядывайте:
- Telegram канал — полезные заметки, лайф стайл и обсуждение волнующих вопросов в мире IT.
- YouTube канал — технические и софтовые видео про разработку.
Когда мы собираем образ Docker, он строится как "слоёный пирог".
Каждая инструкция в Dockerfile — это новый слой. Например:
FROM— базовый слой образа (например, Alpine или Go SDK).COPY,RUN— добавляют новые слои с изменениями.CMD,EXPOSE— тоже фиксируются как слои.
Docker кэширует слои: если что-то в слое не поменялось, он просто использует уже готовый слой, чтобы ускорить сборку.
Если собирать Go-приложение без multi-stage build:
- Внутри финального образа окажется весь SDK Go, исходные файлы и временные артефакты.
- Это раздувает размер образа и может нести лишние риски безопасности (внутри много лишнего).
Multi-stage build позволяет:
- В одной стадии использовать тяжёлый образ для сборки (например,
golang:1.23.1). - А во второй стадии взять только результат сборки — бинарный файл — и положить его в минимальный образ (например,
alpine:3.21.3).
Такой подход даёт сразу несколько плюсов:
✅ Минимальный размер финального образа.
✅ Нет лишних инструментов внутри контейнера.
✅ Быстрая и надёжная сборка.
✅ Повышенная безопасность (меньше attack surface).
Автор: Олег Козырев
