본문 바로가기

Infrastructure

Kubernetes Makefile을 읽으며 배우는 GNU make

GNU make

GNU make는 복잡한 빌드 과정을 하나의 진입점(entry point)으로 관리하는 도구이다.

make 명령을 실행하면 현재 디렉터리의 Makefile(또는 makefile)을 읽고, 정의된 규칙에 따라 필요한 명령을 실행한다.

 

실습

"Hello, World"를 출력하는 간단한 Go 프로그램을 작성한다.

다음과 같은 Makefile을 작성한 후 make를 이용하여 프로그램을 실행하고 빌드해본다.

https://github.com/vvalvvizal/Go_study/tree/master/makefile

run:
	go run main.go

build:
	go build -o bin/main main.go

clean:
	rm -rf bin

 

실행 결과

$ make run
go run main.go
Hello, World!

$ make build
go build -o bin/main main.go

 


Makefile의 문법

앞에서 간단한 Makefile을 직접 작성해봤다면, 이번에는 Kubernetes 오픈소스의 Makefile을 살펴보자.

Kubernetes는 빌드, 테스트, 린트(Lint), 릴리즈 등 다양한 작업을 모두 Makefile을 통해 관리한다.

 

Makefile에서는 $(변수), define, ifeq, ifdef와 같은 간단한 문법이 자주 사용된다.

 

1. $(변수)$$

  • $(WHAT)

Makefile 변수를 참조하는 문법이다. 

all:
	hack/make-rules/build.sh $(WHAT)

 

make all WHAT=cmd/kubelet

 

예를 들어 WHAT에 kubelet을 넣고 실행하면 실제로는 아래 명령이 실행된다.

 

hack/make-rules/build.sh cmd/kubelet

 

  • $$

쉘(Bash)에게 $를 그대로 전달하기 위한 문법이다. 

echo "$$HOME"

 

 

  1. Make가 $$$ 하나로 변환한다.
  2. Bash가 $HOME을 환경변수로 해석한다.
  3. HOME 환경변수의 값이 출력된다.

 

2. define

define여러 줄의 문자열(매크로)을 정의하는 문법이다.

아래는 ALL_HELP_INFO라는 변수에 여러 줄의 도움말을 저장한다.

define ALL_HELP_INFO
# Build code.
#
# Args:
#   WHAT: Directory or Go package names to build.
#
# Example:
#   make all
#   make all WHAT=cmd/kubelet
endef

 

 

define 내부의 내용은 모두 문자열로 저장되며, #도 주석이 아니라 문자열의 일부가 된다.

일반적인 Makefile에서는 다음과 같이 Make 변수로 사용할 수 있다.

all:
	printf '%s\n' "$(ALL_HELP_INFO)"

 

Kubernetes Makefile에서는 다음과 같은 설정이 존재한다.

.EXPORT_ALL_VARIABLES:

.EXPORT_ALL_VARIABLESMakefile에서 정의한 모든 변수를 환경변수(export)로 전달하는 설정​이다.

따라서 define으로 정의한 ALL_HELP_INFO도 Bash에서 환경변수처럼 사용할 수 있다.

all:
	echo "$$ALL_HELP_INFO"

 

3. ifeq

ifeq두 값이 같은지 비교하는 조건문이다.

 

ifeq (arg1, arg2)

... else

... endif

ifeq ($(PRINT_HELP),y)
all:
	echo "$$ALL_HELP_INFO"
else
all:
	hack/make-rules/build.sh $(WHAT)
endif

위 코드는 다음과 같은 의미이다.

if PRINT_HELP == "y"
    도움말 출력
else
    build.sh 실행

즉, PRINT_HELP 변수의 값이 y이면 도움말을 출력하고, 그렇지 않으면 실제 빌드 스크립트를 실행한다.

 

 

4. ifdef / ifndef

ifdefifndef변수가 정의되어 있는지 여부를 확인하는 조건문이다.

ifdef KUBE_GOFLAGS

$(info KUBE_GOFLAGS is now deprecated. Please use GOFLAGS instead.)

ifndef GOFLAGS
    GOFLAGS := $(KUBE_GOFLAGS)
    unexport KUBE_GOFLAGS
else
    $(error Both KUBE_GOFLAGS and GOFLAGS are set. Please use just GOFLAGS)
endif

endif

동작 과정은 다음과 같다.

  1. KUBE_GOFLAGS가 정의되어 있는지 확인한다.
  2. 정의되어 있다면 안내 메시지를 출력한다.

 

GOFLAGS가 정의되어 있지 않다면  KUBE_GOFLAGS의 값을 GOFLAGS로 복사한다.

GOFLAGS := $(KUBE_GOFLAGS)

그리고  KUBE_GOFLAGS를 자식 프로세스에 환경변수로 전달하지 않도록 한다.

unexport KUBE_GOFLAGS

 

 

반대로 GOFLAGS도 이미 정의되어 있다면 Make를 즉시 종료하고 에러를 출력한다.

$(error Both KUBE_GOFLAGS and GOFLAGS are set. Please use just GOFLAGS)

 

 

 

 

Kubernetes 빌드

Kubernetes의 루트 디렉터리에서 다음 명령을 실행하면 Makefile의 all 타겟이 실행된다.

make all

 

실제로 빌드가 수행되면 다음과 같이 각 컴포넌트가 컴파일되는 과정을 확인할 수 있다.

+++ Building go targets for darwin/arm64

k8s.io/kubernetes/cmd/kube-apiserver
k8s.io/kubernetes/cmd/kube-controller-manager
k8s.io/kubernetes/cmd/kube-scheduler
k8s.io/kubernetes/cmd/kubelet
k8s.io/kubernetes/cmd/kubectl
...

 

그리고 빌드가 완료되면 _output/bin 디렉터리에 실행 가능한 바이너리가 생성된다.

ls _output/bin

kube-apiserver
kube-controller-manager
kube-scheduler
kubelet
kubectl
kubeadm
kube-proxy
...

 

 

쿠버네티스를 구성하는 핵심 컴포넌트들의 실행 파일이 make all 한 번에 빌드된 것을 확인할 수 있다. 

 

 

 

'Infrastructure' 카테고리의 다른 글

쿠버네티스 코드베이스 읽어보기  (0) 2026.06.28