【问题标题】:makefile - build mulitple files with multiple targetsmakefile - 使用多个目标构建多个文件
【发布时间】:2016-04-16 15:28:53
【问题描述】:

我有一个包含多个 Dockerfile 的项目,每个 Dockerfile 都位于我用作 Docker 映像标记的命名目录中。我需要为每个 Dockerfile 构建、测试和推送每个 Docker 映像。我需要做什么才能使用 GNU Make 进行以下工作?

# BUILDS needs to be a list of directories, not a list of Dockerfiles
BUILDS  := $(wildcard */Dockerfile)
VERSION := $(shell git rev-parse --short=12 --verify HEAD)
DOCKER_REPO_URL := quay.io/reponame

define docker_build =
$(1):
    @echo "Building $$@"
    docker build -t $$@ --force-rm $$@
endef

define docker_test =
$(1):
    @echo "Testing $$@"
    docker inspect $$@
    docker run --rm $$@ help
endef

define docker_push =
$(1):
    @echo "Pushing $$@"
    docker tag $$@ $(DOCKER_REPO_URL):$$@-$(VERSION)
    docker push $(DOCKER_REPO_URL):$$@-$(VERSION)
    docker tag $$@ $(DOCKER_REPO_URL):$$@
    docker push $(DOCKER_REPO_URL):$$@
endef

.PHONY: all build test release clean

all: build test release

build: $(BUILDS)
$(foreach build,$(BUILDS),$(eval $(call docker_build,$(build))))

test: $(BUILDS)
$(foreach test,$(BUILDS),$(eval $(call docker_test,$(test))))

release:
$(foreach image,$(BUILDS),$(eval $(call docker_push,$(image))))

【问题讨论】:

  • 我对docker不熟悉。如果我有foo/Dockerfile 并执行docker build -t foo --force-rm foo,它会生成一个文件吗?如果是这样,该文件的名称是什么?它位于何处?
  • 您好 Beta,图像(文件)位于 docker 主机上,取决于使用的存储驱动程序。 stackoverflow.com/questions/19234831/…
  • 你的意思是一般情况下你不知道文件会去哪里或者叫什么名字吗?在这种情况下,有效 makefile 很容易,但有效 则不然。
  • 我的意思是 makefile 不应该依赖于特定的 docker 配置。主机可以在远程机器上,并且可以使用多个存储驱动程序。 docker build 可以使用目录名称' -t ${PWD##*/} '标记图像。 docker run 和 push 也可以引用目录名。

标签: makefile gnu-make dockerfile


【解决方案1】:

我不确定这是你想要的,但是......

首先考虑BUILD 变量。如果我们有三个 Dockerfile:

foo/Dockerfile
bar/Dockerfile
baz/Dockerfile

那么我们希望BUILDS 包含foo bar baz

这里有几个尝试:

BUILDS := $(wildcard */Dockerfile) # this is foo/Dockerfile bar/Dockerfile baz/Dockerfile

BUILDS := $(dir $(wildcard */Dockerfile)) # this is foo/ bar/ baz/

BUILDS  := $(patsubst %/,%, $(dir $(wildcard */Dockerfile))) # this is foo bar baz

粗鲁但有效。

现在是规则。通常,规则的目标是规则构建的文件的名称。在这种情况下,我们必须打破这个约定,因为我们不知道图像文件的名称是什么。所以如果目录是foo/,我们可以有一个规则叫做build_foo

build_foo:
    @echo "Building foo"
    @echo docker build -t foo --force-rm foo

由于我们不想为每个可能的目录编写规则,我们将使用自动变量并创建一个pattern rule

build_%:
    @echo "Building $$@"
    @echo docker build -t $* --force-rm $*

现在 "make build_foowill work correctly. And we could write abuild` 构建所有这些规则:

build: $(addprefix build_,$(BUILDS))

但这不是完全正确的方法。我们想要构建,然后测试,然后按这个顺序推送每个图像。所以我们想要这样的东西:

push_foo: test_foo

test_foo: build_foo

我们可以使用模式规则来做到这一点:

test_%: build_%
    ...

push_%: test_%
    ...

release: $(addprefix push_,$(BUILDS))

现在“make release”会做所有事情。 (如果你把release:作为makefile中的第一条规则,它将是默认规则,“make”就足够了。)

【讨论】:

  • 多么出色的答案。谢谢你。我很欣赏这些解释并展示了这两种流程。我确实实施了推荐的流程。
  • 此解决方案还允许使用一次性规则,例如“make build_1.2-node5.10-onbuild”——任何可能的方式来完成选项卡?
  • @Tom:制表符补全?你的意思是当你编辑makefile时,标签会自动处理?这取决于您的文本编辑器。
【解决方案2】:

就像@Beta,我不明白你为什么要构建所有图像,然后 测试所有图像,然后推送所有图像,而不是 构建、测试和推送每个图像;后一种方法很适合 到一个更简单、更正常的 makefile。

如果您有理由必须采用第一种方式,那么您需要一个 生成文件是这样的:

# Assuming each subdirectory `foobar` containing a Dockerfile
# is where we `docker build` the image `foobar` 
IMAGES  := $(patsubst %/,%,$(dir $(wildcard */Dockerfile)))
BUILD_TARGS = $(patsubst %,build_%,$(IMAGES))
TEST_TARGS = $(patsubst %,test_%,$(IMAGES))
PUSH_TARGS = $(patsubst %,push_%,$(IMAGES))

VERSION := 1 # $(shell git rev-parse --short=12 --verify HEAD)
DOCKER_REPO_URL := quay.io/reponame

define docker_build =
build_$(1):
    @echo "Building $(1)"
    #docker build -t $(1) --force-rm $(1)
endef

define docker_test =
test_$(1):
    @echo "Testing $(1)"
    #docker inspect $(1)
    #docker run --rm $(1) help
endef

define docker_push =
push_$(1):
    @echo "Pushing $(1)"
    #docker tag $(1) $(DOCKER_REPO_URL):$(1)-$(VERSION)
    #docker push $(DOCKER_REPO_URL):$(1)-$(VERSION)
    #docker tag $$@ $(DOCKER_REPO_URL):$(1)
    #docker push $(DOCKER_REPO_URL):$(1)
endef

.PHONY: all build test release clean $(IMAGES) $(BUILD_TARGS) $(TEST_TARGS) $(PUSH_TARGS)

all: build test release

build: $(BUILD_TARGS)
test: $(TEST_TARGS)
release: $(PUSH_TARGS)

$(foreach image,$(IMAGES),$(eval $(call docker_build,$(image))))
$(foreach image,$(IMAGES),$(eval $(call docker_test,$(image))))
$(foreach image,$(IMAGES),$(eval $(call docker_push,$(image))))

显然,取消注释 docker 命令以使其运行,并恢复 VERSION 的正确定义。

就目前而言,它会给你这样的:

$ make
Building foo
#docker build -t foo --force-rm foo
Building bar
#docker build -t bar --force-rm bar
Testing foo
#docker inspect foo
#docker run --rm foo help
Testing bar
#docker inspect bar
#docker run --rm bar help
Pushing foo
#docker tag foo quay.io/reponame:foo-1 
#docker push quay.io/reponame:foo-1 
#docker tag push_foo quay.io/reponame:foo
#docker push quay.io/reponame:foo
Pushing bar
#docker tag bar quay.io/reponame:bar-1 
#docker push quay.io/reponame:bar-1 
#docker tag push_bar quay.io/reponame:bar
#docker push quay.io/reponame:bar

【讨论】:

  • 感谢迈克的回答。没错,但我接受了 Beta,因为它是第一个,而且我认为它也能更进一步地传达一种理解
  • @Tom 我非常同意。除非有什么不寻常的原因,否则你会这样做。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-08-16
  • 2012-12-04
  • 2021-08-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多