AI Infra 训练营
总览
  • Day 1 · 集群起步 + CNI
  • Day 2 · 控制面 + etcd
  • Day 3 · CRD + Operator + Webhook
  • Day 4 · 存储深度
  • Day 5 · 卷扩容 + 安全
  • Day 6 · 调度 + 可观测
  • Day 7 · Harbor + ArgoCD + Mesh
  • Day 8 · AI Infra
  • Day 9 · Triton + GPU
  • Day 10 · MIG + HPA + 量化
  • Day 11 · AI Agent 端到端
  • Day 12 · 灾备
  • Day 13 · Operator + 联邦 + Mesh + RAG
  • Day 14 · CKA / CKS + 总结
  • LLM 训练手册
  • RAG + Agent 手册
  • 推理优化手册
  • 上下文工程手册
  • Agent 开发手册
  • 面试深度复盘
  • 训练 v2 深度手册
  • 心智模型
  • 看懂命令输出
  • 容器网络底层
  • K8s 网络深入
  • DNS 全套
  • 故障排查方法论
  • 心智模型
  • 容器挂载完整指南
  • K8s Volumes 大全
  • PV/PVC/CSI 深入
  • NFS 深入
  • 分布式存储概览
  • 故障排查 runbook
HiHuo 主站
GitHub
总览
  • Day 1 · 集群起步 + CNI
  • Day 2 · 控制面 + etcd
  • Day 3 · CRD + Operator + Webhook
  • Day 4 · 存储深度
  • Day 5 · 卷扩容 + 安全
  • Day 6 · 调度 + 可观测
  • Day 7 · Harbor + ArgoCD + Mesh
  • Day 8 · AI Infra
  • Day 9 · Triton + GPU
  • Day 10 · MIG + HPA + 量化
  • Day 11 · AI Agent 端到端
  • Day 12 · 灾备
  • Day 13 · Operator + 联邦 + Mesh + RAG
  • Day 14 · CKA / CKS + 总结
  • LLM 训练手册
  • RAG + Agent 手册
  • 推理优化手册
  • 上下文工程手册
  • Agent 开发手册
  • 面试深度复盘
  • 训练 v2 深度手册
  • 心智模型
  • 看懂命令输出
  • 容器网络底层
  • K8s 网络深入
  • DNS 全套
  • 故障排查方法论
  • 心智模型
  • 容器挂载完整指南
  • K8s Volumes 大全
  • PV/PVC/CSI 深入
  • NFS 深入
  • 分布式存储概览
  • 故障排查 runbook
HiHuo 主站
GitHub

make / Makefile —— 任务编排

一句话定义

make 是经典的 任务运行器 —— 你写 Makefile 描述"目标 → 依赖 → 命令",make 按依赖图执行。运维 / DevOps 场景里,Makefile 是替代 shell 脚本 / package.json scripts 的轻量统一入口——make deploy、make test、make logs。

典型场景

  • 项目入口:make deploy / make rollback
  • K8s 部署套路:make apply make diff make port-forward
  • 替代复杂的 if/else 脚本
  • CI 流水线的入口(Jenkins / GitHub Actions / GitLab CI 都能 make ...)

Makefile 在运维场景里不为了"增量编译"——是为了"一致的命令入口"。无论 macOS / Linux / Windows WSL 上,make deploy 都跑一样的东西。


最小 Makefile

# Makefile
.PHONY: help install deploy clean

help:
	@echo "Targets: install, deploy, clean"

install:
	pip install -r requirements.txt

deploy: install
	kubectl apply -f manifests/

clean:
	rm -rf build/

用法:

make                              # 默认跑第一个 target(help)
make help
make install
make deploy                       # 自动先跑依赖 install
make clean

三大元素

target: prerequisites
<TAB>command
<TAB>command

命令前必须是 TAB 不是空格 —— 这是 Makefile 最大坑。


变量

VERSION := v1.2.3                # 立即赋值(=== 一次性求值)
KUBECONFIG ?= ~/.kube/config      # 默认值(环境没设才用)
NAMESPACE = production            # 延迟赋值(每次用时求值)

deploy:
	@echo "Deploying $(VERSION) to $(NAMESPACE)"
	kubectl --kubeconfig=$(KUBECONFIG) -n $(NAMESPACE) apply -f deploy.yaml
make deploy                                       # 用默认值
make deploy VERSION=v1.2.4                        # 命令行覆盖
NAMESPACE=staging make deploy                     # 环境变量覆盖

?= 是 make 的一大杀器——配合 K8s 多环境 / 多集群非常顺手。

Make 内置变量

变量含义
$@当前 target 名
$<第一个依赖
$^所有依赖
$?比 target 新的依赖
$(MAKE)make 本身(递归用)
%.png: %.svg                     # 模式规则
	inkscape -e $@ $<

模式规则在编译类场景常用、运维 Makefile 少见。


.PHONY —— 必加

.PHONY: deploy
deploy:
	kubectl apply -f deploy.yaml

.PHONY 告诉 make:"这不是个真文件,每次都跑命令"。没 .PHONY 时:

deploy:                              # 没 .PHONY
	kubectl apply -f deploy.yaml

# 如果当前目录恰好有个文件叫 `deploy`:
$ make deploy
make: 'deploy' is up to date.        # ❌ 不跑了!

Makefile 里所有任务式 target 都要 .PHONY:

.PHONY: help install deploy clean test apply diff

真实运维 Makefile 模板

# Makefile
.PHONY: help install lint test build push deploy diff rollback logs status \
        clean port-forward exec

# === 变量 ===
SHELL := /bin/bash
.SHELLFLAGS := -eu -o pipefail -c

PROJECT  := my-app
VERSION  ?= $(shell git describe --tags --always --dirty)
REGISTRY ?= registry.example.com
IMAGE    := $(REGISTRY)/$(PROJECT):$(VERSION)
NAMESPACE ?= default
KUBECTL  := kubectl -n $(NAMESPACE)

# === 帮助 ===
help: ## 列出所有 target
	@awk 'BEGIN {FS = ":.*##"} /^[a-zA-Z_-]+:.*##/ {printf "  \033[36m%-15s\033[0m %s\n", $$1, $$2}' $(MAKEFILE_LIST)

# === 开发 ===
install: ## 装依赖
	pip install -r requirements.txt

lint: ## 静态检查
	flake8 src/
	black --check src/

test: ## 跑测试
	pytest tests/

# === 容器 ===
build: ## 构建 docker 镜像
	docker build -t $(IMAGE) .

push: build ## 推镜像
	docker push $(IMAGE)

# === K8s 部署 ===
diff: ## 看部署 diff
	helm diff upgrade $(PROJECT) ./chart \
	  -n $(NAMESPACE) \
	  --set image.tag=$(VERSION)

deploy: ## 部署
	helm upgrade --install $(PROJECT) ./chart \
	  -n $(NAMESPACE) --create-namespace \
	  --set image.tag=$(VERSION) \
	  --wait --timeout 5m

rollback: ## 回滚到上一版
	helm rollback $(PROJECT) -n $(NAMESPACE)

# === 运维 ===
status: ## 看 pod 状态
	$(KUBECTL) get pod,svc,ing -l app=$(PROJECT)

logs: ## 看日志
	$(KUBECTL) logs -l app=$(PROJECT) --tail=200 -f --prefix

port-forward: ## 端口转发到本地 8080
	$(KUBECTL) port-forward svc/$(PROJECT) 8080:80

exec: ## 进 pod
	$(KUBECTL) exec -it deploy/$(PROJECT) -- bash

clean: ## 清本地构建
	rm -rf build/ dist/ *.egg-info

用法:

make help                                       # 列所有
make deploy VERSION=v2.0.0                      # 部署 v2.0.0
make deploy NAMESPACE=staging                   # 部署到 staging
make logs                                        # 看日志
make port-forward                                # 端口转发
make rollback                                    # 回滚

Makefile 上的几个最佳实践

1. SHELL := /bin/bash + pipefail

SHELL := /bin/bash
.SHELLFLAGS := -eu -o pipefail -c

默认 make 用 /bin/sh 不支持 pipefail 等 bash 特性。这两行让你的命令在出错时立刻退出(set -e 等价)。

2. @ 抑制 echo

deploy:
	@echo "deploying ..."          # 只打印 "deploying ...",不打印 echo 命令本身
	kubectl apply -f ...            # 打印命令本身 + 输出

只在 @echo 这种自显示提示性命令前加 @。其它命令保留显示(看得到跑了啥)。

3. 自描述 help

help: ## 列出所有 target
	@awk 'BEGIN {FS = ":.*##"} /^[a-zA-Z_-]+:.*##/ {printf "  \033[36m%-15s\033[0m %s\n", $$1, $$2}' $(MAKEFILE_LIST)

deploy: ## 部署应用
	...

clean: ## 清理
	...

make help 自动列出所有带 ## 注释的 target——一致的项目入口。

4. 默认 target 是 help

把 help 放在第一个 target,或显式:

.DEFAULT_GOAL := help

make 不带参数时跑 help。避免不小心跑了破坏性的 deploy。


多 target 依赖图

.PHONY: ci
ci: lint test build

lint:
	...

test:
	...

build:
	...
make ci                                          # 自动跑 lint → test → build

并行:

make -j4 ci                                      # 并行跑(小心副作用)

-j 加速但要注意 target 之间不能写同一个文件 / 资源。


子 Makefile

build-frontend:
	$(MAKE) -C frontend build                    # 在 frontend/ 目录跑 make build

build-backend:
	$(MAKE) -C backend build

build: build-frontend build-backend

-C dir 切目录后跑。monorepo 友好。


实战:K8s 多环境部署

.PHONY: dev staging prod

ENV ?= dev

deploy-%:
	@echo "Deploying to $*"
	helm upgrade --install $(PROJECT) ./chart \
	  -n $* --create-namespace \
	  -f values-base.yaml \
	  -f values-$*.yaml

dev: deploy-dev
staging: deploy-staging
prod: deploy-prod
make dev                                        # 部署 dev
make staging
make prod                                       # 生产

% 是通配 target,$* 是匹配的部分。


常见踩坑

坑 1:用空格而不是 TAB

deploy:
    kubectl apply -f ...                          # 4 空格
# Makefile:5: *** missing separator. Stop.

命令前必须 TAB。编辑器设:

" .vimrc 给 Makefile 不展开 tab
autocmd FileType make setlocal noexpandtab
# .editorconfig
[Makefile]
indent_style = tab

坑 2:没 .PHONY 又有同名文件

deploy:
	kubectl apply -f ...
touch deploy
make deploy
# make: 'deploy' is up to date.

所有非编译 target 加 .PHONY。

坑 3:$ 变量被 make 吃了

list-pods:
	for p in $(kubectl get pods -o name); do echo $p; done
$ make list-pods
# 报错:未定义变量 p

shell 里的 $ 在 Makefile 里要 $$:

list-pods:
	for p in $$(kubectl get pods -o name); do echo $$p; done

或者写成单行 / 用 here-doc:

list-pods:
	@kubectl get pods -o name

坑 4:多行 shell 不在同一个 shell

target:
	cd /tmp
	ls                                # ❌ ls 不是在 /tmp 里跑

每行 make 起一个新 shell。多行要用 && 或 ; 或 \ 连:

target:
	cd /tmp && ls

# 或
target:
	cd /tmp; \
	ls

或者用 .ONESHELL(GNU make 4+):

.ONESHELL:
target:
	cd /tmp
	ls

坑 5:变量 = vs :=

VERSION = $(shell git describe)        # 每次用都跑一次 git
VERSION := $(shell git describe)       # 一次性跑

:= 求值一次。= 延迟求值。Shell 命令在 = 下会被反复跑——慢且不一致。默认用 :=。

坑 6:?= 没生效

NAMESPACE ?= default
NAMESPACE=prod make deploy

环境变量传给 make 不会自动传给 make 子进程的 shell。要 export:

export NAMESPACE

或者只在命令里用 $(NAMESPACE)(make 变量),不依赖环境传给子 shell。

坑 7:make 没装

$ make
-bash: make: command not found
apt install -y build-essential                  # Ubuntu
yum groupinstall -y "Development Tools"          # CentOS

K8s 节点上一般没装,CI 镜像通常有。

坑 8:循环 / 条件难写

deploy:
	if [ "$$ENV" = "prod" ]; then \
	  echo "deploying prod"; \
	else \
	  echo "non-prod"; \
	fi

Makefile 的 shell 嵌入复杂条件反直觉——\ 连行 + $$ 转义。

复杂逻辑外移到 shell 脚本:

deploy:
	./scripts/deploy.sh $(ENV)

Makefile 作"入口",shell 脚本做"实际逻辑"。

坑 9:make clean 删了不该删的

clean:
	rm -rf $(BUILD_DIR)/*

如果 BUILD_DIR 没设、变成 rm -rf /*,灾难。

防御:

BUILD_DIR := build

clean:
	@test -n "$(BUILD_DIR)" || (echo "BUILD_DIR not set"; exit 1)
	rm -rf $(BUILD_DIR)

坑 10:make 不显示彩色 / 进度

make 默认输出黑白。脚本里用 ANSI 着色:

deploy:
	@echo -e "\033[32m✓ Deployed\033[0m"

或者用 printf。


替代方案

工具优点适合
make普及 / 简单 / 无依赖通用入口
just现代 / 更易写替代 make 的新工具
task (Taskfile.yml)YAML 格式 / 现代不想用 Makefile 语法
npm scriptsJS 生态Node 项目
纯 shell 脚本灵活复杂逻辑

实际中make 仍是 K8s / DevOps 项目的事实标准入口——所有人都能跑、不需要装额外工具。


关联命令

  • git —— Makefile 里 git describe 拿版本号
  • docker —— make build && make push
  • kubectl —— Makefile 里 kubectl 命令
  • helm —— make deploy 包装 helm upgrade
  • just / task —— make 的现代替代品
在 GitHub 上编辑此页