譯者 | 李睿
審校 | 重樓
雖然使用傳統(tǒng)的docker build命令構(gòu)建Docker鏡像的方法簡(jiǎn)單直接,但在處理由多個(gè)組件組成的復(fù)雜應(yīng)用程序時(shí),這個(gè)過程可能會(huì)變得繁瑣且容易出錯(cuò)。這就是Docker Bake發(fā)揮重要作用的地方——它是一個(gè)強(qiáng)大而靈活的工具,用于組織多階段和并行的鏡像構(gòu)建。
本文將介紹Docker Bake的功能、其相對(duì)于傳統(tǒng)方法的優(yōu)勢(shì),以及在各種開發(fā)場(chǎng)景中使用Docker Bake的實(shí)際示例。
什么是Docker Bake?
Docker Bake是BuildKit的一個(gè)功能,允許用戶使用配置文件組織和自動(dòng)化 Docker鏡像構(gòu)建過程。
Docker Bake的主要優(yōu)勢(shì)包括:
- 聲明性語(yǔ)法??梢允褂肏CL(HashiCorp配置語(yǔ)言)、JSON或YAML(Docker Compose文件)來描述所需的結(jié)果,而不是在腳本中使用多個(gè)命令。
- 并行構(gòu)建。BuildKit在可能的情況下自動(dòng)并行執(zhí)行鏡像構(gòu)建。
- 緩存重用。在不同構(gòu)建之間高效利用緩存。
- 分組和有針對(duì)性的構(gòu)建。能夠定義圖像組并僅構(gòu)建當(dāng)前所需的目標(biāo)。
- 變量和繼承。在構(gòu)建目標(biāo)之間具有強(qiáng)大的變量系統(tǒng)和屬性繼承功能。
- CI/CD 集成。輕松集成到持續(xù)集成和交付管道中。
Bake文件剖析
以下了解Bake文件的主要組成部分:
1.變量
變量允許定義可以在配置的不同部分使用的值,并且可以在運(yùn)行時(shí)輕松地重新定義:
Shell
variable "TAG" {
default = "latest"
}
variable "DEBUG" {
default = "false"
}
變量可以通過字符串插值在配置的其他部分使用:${TAG}。
2.組
群組允許將多個(gè)目標(biāo)組合在一起,然后同時(shí)構(gòu)建:
Shell
group "default" {
targets = ["app", "api"]
}
group "backend" {
targets = ["api", "database"]
}
3.目標(biāo)
目標(biāo)是構(gòu)建的主要單元,每個(gè)目標(biāo)定義一個(gè)Docker鏡像:
Shell
target "app" {
dockerfile = "Dockerfile.app"
context = "./app"
tags = ["myorg/app:${TAG}"]
args = {
DEBUG = "${DEBUG}"
}
platforms = ["linux/amd64", "linux/arm64"]
}
主要目標(biāo)參數(shù):
- dockerfile——dockerfile的路徑
- context——構(gòu)建上下文
- tags——圖像的標(biāo)簽
- args ——傳遞給Dockerfile的參數(shù)
- platforms——用于多平臺(tái)構(gòu)建的平臺(tái)
- target——在Dockerfile中進(jìn)行多階段構(gòu)建的目標(biāo)
- output——在哪里輸出構(gòu)建結(jié)果
- cache-from和cache-to——cache設(shè)置
4.繼承
Bake最強(qiáng)大的功能之一是繼承參數(shù)的能力:
Shell
target "base" {
context = "."
args = {
BASE_IMAGE = "node:16-alpine"
}
}
target "app" {
inherits = ["base"]
dockerfile = "app/Dockerfile"
tags = ["myapp/app:latest"]
}
應(yīng)用程序目標(biāo)將繼承基中的所有參數(shù),并用自己的參數(shù)覆蓋或補(bǔ)充它們。
5.函數(shù)
在HashiCorp配置語(yǔ)言(HCL)中,可以定義函數(shù)以實(shí)現(xiàn)更靈活的配置:
Shell
function "tag" {
params = [name, version]
result = ["${name}:${version}"]
}
target "app" {
tags = tag("myapp/app", "v1.0.0")
}
安裝和設(shè)置
Docker Bake是BuildKit的一部分,BuildKit是一個(gè)用于構(gòu)建Docker鏡像的現(xiàn)代引擎。從Docker 23.0開始,BuildKit默認(rèn)是啟用的,所以大多數(shù)用戶不需要額外的配置。但是,如果用戶使用的是舊版本的Docker或想要確保BuildKit已經(jīng)激活,請(qǐng)按照以下的說明進(jìn)行操作。
檢查Docker版本
確保有最新版本的Docker(23.0或更高版本)??梢允褂妹顧z查版本:
Plain Text
docker --version
如果Docker版本過時(shí)了,請(qǐng)按照官方文檔進(jìn)行更新。
激活BuildKit(適用于舊Docker版本)
對(duì)于低于Docker23.0的版本,需要手動(dòng)激活BuildKit。這可以通過以下方式之一完成:
通過環(huán)境變量:
Shell
export DOCKER_BUILDKIT=1
Plain Text
In the Docker configuration file: Edit the ~/.docker/config.json file and add the following parameters:
JSON
{
"features": {
"buildkit": true
}
}
- 通過命令行:當(dāng)使用docker build或docker buildx bake命令時(shí),可以明確指定使用BuildKit:
Shell
DOCKER_BUILDKIT=1 docker buildx bake
安裝Docker Buildx
Docker Buildx是Docker CLI的擴(kuò)展,它為構(gòu)建鏡像提供了額外的功能,包括對(duì)多平臺(tái)構(gòu)建的支持。從Docker 20.10開始,Buildx包含在Docker中,但為了獲得完整的功能,建議確保已經(jīng)安裝并激活它。
(1)檢查Buildx安裝
Shell
docker buildx version
如果沒有安裝Buildx,可以按照下面的說明操作。
(2)安裝Buildx
- For Linux:
Shell
mkdir -p ~/.docker/cli-plugins
curl -sSL https://github.com/docker/buildx/releases/latest/download/buildx-linux-amd64 -o ~/.docker/cli-plugins/docker-buildx
chmod +x ~/.docker/cli-plugins/docker-buildx
- For macOS (using Homebrew):
Shell
brew install docker-buildx
(3)創(chuàng)建和使用Buildx生成器
在默認(rèn)情況下,Docker使用內(nèi)置的構(gòu)建器,但為了獲得完整的功能,建議創(chuàng)建一個(gè)新的構(gòu)建器:
Shell
1 docker buildx create --use --name my-builder
檢查構(gòu)建器是否處于激活狀態(tài):
Shell
1 docker buildx ls
Docker Bake基礎(chǔ)知識(shí)
配置文件
Docker Bake使用的配置文件可以用HCL(默認(rèn))、JSON或YAML格式編寫。以下是這些文件的標(biāo)準(zhǔn)名稱:
- docker-bake.hcl
- docker-bake.json
也可以使用帶有一些擴(kuò)展的docker-compose.yml。
HCL文件結(jié)構(gòu)
典型的Docker Bake配置文件的結(jié)構(gòu)如下:
Shell
// Defining variables
variable "TAG" {
default = "latest"
}
// Defining groups
group "default" {
targets = ["app", "api"]
}
// Defining common settings
target "docker-metadata-action" {
tags = ["user/app:${TAG}"]
}
// Defining build targets
target "app" {
inherits = ["docker-metadata-action"]
dockerfile = "Dockerfile.app"
context = "./app"
}
target "api" {
inherits = ["docker-metadata-action"]
dockerfile = "Dockerfile.api"
context = "./api"
}
執(zhí)行構(gòu)建
從默認(rèn)組構(gòu)建所有目標(biāo):
Plain Text
docker buildx bake
建立一個(gè)特定的目標(biāo)或組:
Plain Text
1 docker buildx bake app
傳遞變量:
Plain Text
TAG=v1.0.0 docker buildx bake
實(shí)例
示例1:簡(jiǎn)單的多組件應(yīng)用程序
假設(shè)有一個(gè)由Web前端、API和數(shù)據(jù)庫(kù)服務(wù)組成的應(yīng)用程序。以下是一個(gè)docker-bake.hcl文件可能的樣子:
Shell
variable "TAG" {
default = "latest"
}
group "default" {
targets = ["frontend", "api", "db"]
}
group "services" {
targets = ["api", "db"]
}
target "base" {
context = "."
args = {
BASE_IMAGE = "node:16-alpine"
}
}
target "frontend" {
inherits = ["base"]
dockerfile = "frontend/Dockerfile"
tags = ["myapp/frontend:${TAG}"]
args = {
API_URL = "http://api:3000"
}
}
target "api" {
inherits = ["base"]
dockerfile = "api/Dockerfile"
tags = ["myapp/api:${TAG}"]
args = {
DB_HOST = "db"
DB_PORT = "5432"
}
}
target "db" {
context = "./db"
dockerfile = "Dockerfile"
tags = ["myapp/db:${TAG}"]
}
示例2:多平臺(tái)構(gòu)建
Docker Bake的一個(gè)強(qiáng)大的方面是易于建立多平臺(tái)構(gòu)建:
Shell
variable "TAG" {
default = "latest"
}
group "default" {
targets = ["app-all"]
}
target "app" {
dockerfile = "Dockerfile"
tags = ["myapp/app:${TAG}"]
}
target "app-linux-amd64" {
inherits = ["app"]
platforms = ["linux/amd64"]
}
target "app-linux-arm64" {
inherits = ["app"]
platforms = ["linux/arm64"]
}
target "app-all" {
inherits = ["app"]
platforms = ["linux/amd64", "linux/arm64"]
}
示例3:不同的開發(fā)環(huán)境
Docker Bake可以很容易地管理不同環(huán)境(例如,開發(fā)、測(cè)試和生產(chǎn))的構(gòu)建。為此,可以使用通過命令行覆蓋的變量:
Shell
variable "ENV" {
default = "dev"
}
group "default" {
targets = ["app-${ENV}"]
}
target "app-base" {
dockerfile = "Dockerfile"
args = {
BASE_IMAGE = "node:16-alpine"
}
}
target "app-dev" {
inherits = ["app-base"]
tags = ["myapp/app:dev"]
args = {
NODE_ENV = "development"
DEBUG = "true"
}
}
target "app-stage" {
inherits = ["app-base"]
tags = ["myapp/app:stage"]
args = {
NODE_ENV = "production"
API_URL = "https://api.stage.example.com"
}
}
target "app-prod" {
inherits = ["app-base"]
tags = ["myapp/app:prod", "myapp/app:latest"]
args = {
NODE_ENV = "production"
API_URL = "https://api.example.com"
}
}
要為特定環(huán)境構(gòu)建鐿像,使用以下命令:
Plain Text
ENV=prod docker buildx bake
高級(jí)Docker Bake功能
矩陣構(gòu)建
Docker Bake允許根據(jù)參數(shù)組合定義創(chuàng)建多個(gè)構(gòu)建變體的矩陣:
Shell
variable "REGISTRY" {
default = "docker.io/myorg"
}
target "matrix" {
name = "app-${platform}-${version}"
matrix = {
platform = ["linux/amd64", "linux/arm64"]
version = ["1.0", "2.0"]
}
dockerfile = "Dockerfile"
tags = ["${REGISTRY}/app:${version}-${platform}"]
platforms = ["${platform}"]
args = {
VERSION = "${version}"
}
}
這段代碼將為平臺(tái)和版本的每種組合創(chuàng)建四個(gè)鏡像變體??梢允褂靡粋€(gè)命令來構(gòu)建它們。
使用外部文件和函數(shù)
Docker Bake允許使用外部文件和函數(shù)進(jìn)行更靈活的配置:
Shell
// Import variables from a JSON file
variable "settings" {
default = {}
}
function "tag" {
params = [name, tag]
result = ["${name}:${tag}"]
}
target "app" {
dockerfile = "Dockerfile"
tags = tag("myapp/app", "v1.0.0")
args = {
CONFIG = "${settings.app_config}"
}
}
然后可以傳遞一個(gè)settings文件:
Plain Text
docker buildx bake --file settings.json
與Docker Compose集成
Docker Bake可以與Docker Compose集成,這對(duì)現(xiàn)有項(xiàng)目來說特別方便:
YAML
# docker-compose.yml
services:
app:
build:
context: ./app
dockerfile: Dockerfile
args:
VERSION: "1.0"
image: myapp/app:latest
api:
build:
context: ./api
dockerfile: Dockerfile
image: myapp/api:latest
Shell
# docker-bake.hcl
target "default" {
context = "."
dockerfile-inline = <<EOT
FROM docker/compose:1.29.2
WORKDIR /app
COPY docker-compose.yml .
RUN docker-compose build
EOT
}
條件邏輯
對(duì)于更復(fù)雜的場(chǎng)景,可以使用條件邏輯:
Shell
variable "DEBUG" {
default = "false"
}
target "app" {
dockerfile = "Dockerfile"
tags = ["myapp/app:latest"]
args = {
DEBUG = "${DEBUG}"
EXTRA_PACKAGES = DEBUG == "true" ? "vim curl htop" : ""
}
}
在CI/CD中使用Docker Bake
Docker Bake非常適合在CI/CD管道中使用。以下是一個(gè)與GitHub Actions集成的例子,在Docker Hub中使用秘密(secrets)進(jìn)行安全認(rèn)證:
Shell
# .github/workflows/build.yml
name: Build and Publish
on:
push:
branches: [main]
tags: ['v*']
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Login to DockerHub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Docker Metadata
id: meta
uses: docker/metadata-action@v4
with:
images: myapp/app
tags: |
type=ref,event=branch
type=ref,event=pr
type=semver,pattern={{version}}
- name: Build and push
uses: docker/bake-action@v2
with:
files: |
./docker-bake.hcl
targets: app
push: true
set: |
*.tags=${{ steps.meta.outputs.tags }}
調(diào)試和監(jiān)視構(gòu)建
Docker Bake為調(diào)試構(gòu)建過程提供了幾個(gè)有用的選項(xiàng):
查看配置而不構(gòu)建:
Plain Text
docker buildx bake –print
詳細(xì)的日志:
Plain Text
docker buildx bake --progress=plain
導(dǎo)出為JSON進(jìn)行分析:
Plain Text
docker buildx bake --print | jq
與其他工具的比較
Docker Bake vs. Docker Compose
特性 | Docker Bake | Docker Compose |
主要目的 | 構(gòu)建鏡像 | 容器管理 |
并行構(gòu)建 | 自動(dòng) | 有限 |
矩陣構(gòu)建 | 是 | 否 |
繼承 | 強(qiáng)大的系統(tǒng) | 有限 (擴(kuò)展) |
多平臺(tái) | 集成 | 否 |
配置格式 | HCL, JSON | YAML |
Docker Bake vs. Build Scripts
特性 | Docker Bake | Bash/scripts |
聲明性 | 高 | 低 |
維護(hù)的復(fù)雜性 | 低 | 高 |
可重用性 | 簡(jiǎn)單 | 復(fù)雜 |
并行性 | 自動(dòng) | 人工 |
CI/CD集成 | 簡(jiǎn)單的 | 需要努力 |
最佳實(shí)踐
將目標(biāo)組織成邏輯組:
Shell
group "all" {
targets = ["app", "api", "worker"]
}
group "backend" {
targets = ["api", "worker"]
}
將繼承用于常見設(shè)置:
Shell
target "common" {
context = "."
args = {
BASE_IMAGE = "node:16-alpine"
}
}
target "app" {
inherits = ["common"]
dockerfile = "app/Dockerfile"
}
將復(fù)雜的配置組織到多個(gè)文件中:
Shell
docker buildx bake \
-f ./common.hcl \
-f ./development.hcl \
app-dev
使用變量實(shí)現(xiàn)靈活性:
Shell
variable "REGISTRY" {
default = "docker.io/myorg"
}
target "app" {
tags = ["${REGISTRY}/app:latest"]
}
為復(fù)雜的構(gòu)建場(chǎng)景應(yīng)用矩陣:
Shell
target "matrix" {
matrix = {
env = ["dev", "prod"]
platform = ["linux/amd64", "linux/arm64"]
}
name = "app-${env}-${platform}"
tags = ["myapp/app:${env}-${platform}"]
}
常見問題及解決方法
問題1:緩存沒有被有效使用
解決方案
正確地組織Dockerfile,把那些變化不太頻繁的層放在文件的開頭:
Dockerfile
FROM node:16-alpine
# First copy only dependency files
COPY package.json package-lock.json ./
RUN npm install
# Then copy the source code
COPY . .
問題2:環(huán)境變量沖突
解決方案
在Docker Bake中使用顯式值:
Shell
target "app" {
args = {
NODE_ENV = "production"
}
}
問題3:難以調(diào)試構(gòu)建
解決方案
使用詳細(xì)的日志和檢查:
Plain Text
docker buildx bake --progress=plain --print app
結(jié)論
Docker Bake提供了一種強(qiáng)大、靈活和聲明性的方法來組織Docker鏡像構(gòu)建。它解決了團(tuán)隊(duì)在使用傳統(tǒng)構(gòu)建方法時(shí)面臨的許多問題,特別是在復(fù)雜的多組件項(xiàng)目中。
Docker Bake的主要優(yōu)點(diǎn):
- 聲明式方法
- 高效的緩存使用
- 并行和多平臺(tái)構(gòu)建
- 強(qiáng)大的變量和繼承系統(tǒng)
- 與CI/CD管道的出色集成
在工作流程中實(shí)施Docker Bake可以顯著簡(jiǎn)化和加快映像構(gòu)建過程,特別是對(duì)于使用微服務(wù)架構(gòu)或復(fù)雜多組件應(yīng)用程序的團(tuán)隊(duì)而言。
有用的資源
- Official Docker Bake documentation
- BuildKit GitHub repository
- HCL documentation
- Docker Buildx GitHub repository
原文標(biāo)題:Docker Bake: A Modern Approach to Container Building,作者:Suleiman Dibirov