Makefile для DevOps: автоматизация рутины
Makefile для DevOps: автоматизация рутины
В каждом проекте со временем накапливается набор команд, которые нужно запускать регулярно: собрать образ, запустить тесты, задеплоить в staging, почистить старые контейнеры. Эти команды оседают в README, в головах инженеров, в случайных скриптах. Makefile — простой и проверенный временем способ собрать всё в одном месте и сделать команды самодокументируемыми.
Почему Makefile, а не shell-скрипты
Shell-скрипты хороши, но у них есть проблемы: нет стандартного способа перечислить доступные команды, нет встроенных зависимостей между задачами, сложно передавать параметры. Makefile решает всё это из коробки. Плюс make установлен практически на каждом Linux/macOS-сервере без дополнительных зависимостей.
Базовый синтаксис
# Переменные
IMAGE_NAME := my-app
TAG := $(shell git rev-parse --short HEAD)
REGISTRY := registry.example.com
# Цель: имя
# <TAB> команда (именно TAB, не пробелы!)
build:
docker build -t $(IMAGE_NAME):$(TAG) .
push: build
docker tag $(IMAGE_NAME):$(TAG) $(REGISTRY)/$(IMAGE_NAME):$(TAG)
docker push $(REGISTRY)/$(IMAGE_NAME):$(TAG)
.PHONY: build push
Обратите внимание: отступ перед командами — это обязательно символ TAB, не пробелы. Это частая ошибка новичков.
Практичный DevOps Makefile
.DEFAULT_GOAL := help
COMPOSE_FILE := docker-compose.yml
ENV ?= dev
help: ## Показать список команд
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) |
awk 'BEGIN {FS = ":.*?## "}; {printf " 33[36m%-20s 33[0m %s
", $$1, $$2}'
up: ## Запустить окружение
docker compose -f $(COMPOSE_FILE) up -d
down: ## Остановить окружение
docker compose -f $(COMPOSE_FILE) down
logs: ## Показать логи
docker compose -f $(COMPOSE_FILE) logs -f
build: ## Собрать образы
docker compose -f $(COMPOSE_FILE) build --no-cache
test: ## Запустить тесты
docker compose run --rm app pytest -v
migrate: ## Применить миграции БД
docker compose run --rm app python manage.py migrate
lint: ## Проверить код
docker compose run --rm app flake8 .
clean: ## Удалить остановленные контейнеры и тома
docker compose down -v --remove-orphans
docker image prune -f
deploy: build push ## Собрать и задеплоить
helm upgrade --install my-app ./chart
--set image.tag=$(TAG)
--namespace $(ENV)
.PHONY: help up down logs build test migrate lint clean deploy
Трюк с help
Строки с ## комментарий после цели автоматически попадают в вывод make help. Коллеги запускают make help и сразу видят все доступные команды с описаниями — никакого README не нужно.
Условия и параметры
# Передать переменную при запуске: make deploy ENV=production
ENV ?= staging
deploy:
ifeq ($(ENV),production)
@echo "Деплой в PRODUCTION, подтвердите: нажмите Ctrl+C для отмены"
@sleep 5
endif
helm upgrade --install my-app ./chart --namespace $(ENV)
Зависимости между целями
release: lint test build push deploy ## Полный цикл релиза
Запуск make release выполнит все цели по порядку. Если любая из них завершится с ошибкой — цепочка остановится. Это встроенное поведение Make.
Включение внешних файлов
Для больших проектов можно разбить Makefile на части:
include makefiles/docker.mk
include makefiles/k8s.mk
include makefiles/db.mk
Параллельное выполнение
make -j4 lint test build # запустить три цели параллельно
Полезно для независимых задач — например, одновременный запуск линтеров для разных языков.
Makefile не пытается быть универсальным инструментом сборки. Его сила — в простоте и универсальности. Добавьте Makefile в каждый свой проект, и через месяц вы не сможете понять, как работали без него.
Трюк с grep для автогенерации help — просто огонь, сразу добавил к себе в шаблон. Теперь все новые проекты начинаю именно с такого Makefile.