BuildPack:无需编写 Dockerfile,新一代的企业镜像打包工具
公众号关注 「奇妙的 Linux 世界」
设为「星标」,每天带你玩转 Linux !
过去的工作中,我们使用微服务、容器化以及服务编排构建了技术平台。为了提升开发团队的研发效率,我们同时还提供了 CICD 平台,用来将代码快速的部署到 Openshift(企业级的 Kubernetes) 集群。
部署的第一步就是应用程序的容器化,持续集成的交付物从以往的 jar 包、webpack 等变成了容器镜像。容器化将软件代码和所需的所有组件(库、框架、运行环境)打包到一起,进而可以在任何环境任何基础架构上一致地运行,并与其他应用“隔离”。
我们的代码需要从源码到编译到最终可运行的镜像,甚至部署,这一切在 CICD 的流水线中完成。最初,我们在每个代码仓库中都加入了三个文件,也通过项目生成器(类似 Spring Initializer)在新项目中注入:
•Jenkinsfile.groovy:用来定义 Jenkins 的 Pipeline,针对不同的语言还会有多种版本•Manifest YAML:用于定义 Kubernetes 资源,也就是工作负载及其运行的相关描述•Dockerfile:用于构建对象
这个三个文件也需要在工作中不断的演进,起初项目较少(十几个)的时候我们基础团队还可以去各个代码仓库去维护升级。随着项目爆发式的增长,维护的成本越来越高。我们对 CICD 平台进行了迭代,将“Jenkinsfile.groovy”和 “manifest YAML”从项目中移出,变更较少的 Dockerfile 就保留了下来。
随着平台的演进,我们需要考虑将这唯一的“钉子户” Dockerfile 与代码解耦,必要的时候也需要对 Dockerfile 进行升级。因此调研了一下 buildpacks,就有了今天的这篇文章。
什么是 Dockerfile
Docker 通过读取 Dockerfile 中的说明自动构建镜像。Dockerfile 是一个文本文件,包含了由 Docker 可以执行用于构建镜像的指令。我们拿之前用于测试 Tekton 的 Java 项目[1]的 Dockerfile 为例:
FROM openjdk:8-jdk-alpine
RUN mkdir /app
WORKDIR /app
COPY target/*.jar /app/app.jar
ENTRYPOINT ["sh", "-c", "java -Xmx128m -Xms64m -jar app.jar"]
镜像分层
你可能会听过 Docker 镜像包含了多个层。每个层与 Dockerfile 中的每个命令对应,比如 RUN
、COPY
、ADD
。某些特定的指令会创建一个新的层,在镜像构建过程中,假如某些层没有发生变化,就会从缓存中获取。
在下面的 Buildpack 中也同样通过镜像分层和 cache 来加速镜像的构建。
什么是 Buildpack
BuildPack[2] 是一个程序,它能将源代码转换成容器镜像的并可以在任意云环境中运行。通常 buildpack 封装了单一语言的生态工具链。适用于 Java、Ruby、Go、NodeJs、Python 等。
Builder 是什么?
一些 buildpacks 按顺序组合之后就是 builder,除了 buildpacks, builder 中还加入了 生命周期[3] 和 stack 容器镜像。
stack 容器镜像由两个镜像组成:用于运行 buildpack 的镜像 build image,以及构建应用镜像的基础镜像 run image。如上图,就是 builder 中的运行环境。
Buildpack 的工作方式
每个 buildpack 运行时都包含了两个阶段:
1. 检测阶段
通过检查源代码中的某些特定文件/数据,来判断当前 buildpack 是否适用。如果适用,就会进入构建阶段;否则就会退出。比如:
•Java maven 的 buildpack 会检查源码中是否有 pom.xml
•Python 的 buildpack 会检查源码中是否有 requirements.txt
或者 setup.py
文件•Node buildpack 会查找 package-lock.json
文件。
2. 构建阶段
在构建阶段会进行如下操作:
1.设置构建环境和运行时环境2.下载依赖并编译源码(假如需要的话)3.设置正确的 entrypoint 和启动脚本。
比如:
•Java maven buildpack 在检查到有 pom.xml
文件之后,会执行 mvn clean install -DskipTests
•Python buildpack 检查到有 requrements.txt
之后,会执行 pip install -r requrements.txt
•Node build pack 检查到有 package-lock.json
后执行 npm install
BuildPack 上手
那到底如何在没有 Dockerfile 的情况下使用 builderpack 构建镜像的。看了上面这些,大家基本上也都能了解到这个核心就在 buildpack 的编写和使用的。
其实现在有很多开源的 buildpack 可以用,没有特定定制的情况下无需自己手动编写。比如下面的几个大厂开源并维护的 Buildpacks:
•Heroku Buildpacks[4]•Google Buildpacks[5]•Paketo[6]
但是正式详细介绍开源的 buildpacks 之前,我们还是通过自己创建 buildpack 的方式来深入了解 Buildpacks 的工作方式。测试项目呢,我们还是用测试 Tekton 的 Java 项目[7]。
下面所有的内容都提交到了 Github[8] 上,可以访问:https://github.com/addozhang/buildpacks-sample 获取相关代码。
最终的目录buildpacks-sample
结构如下:
├── builders
│ └── builder.toml
├── buildpacks
│ └── buildpack-maven
│ ├── bin
│ │ ├── build
│ │ └── detect
│ └── buildpack.toml
└── stacks├── build│ └── Dockerfile├── build.sh└── run└── Dockerfile
创建 buildpack
pack buildpack new examples/maven \--api 0.5 \--path buildpack-maven \--version 0.0.1 \--stacks io.buildpacks.samples.stacks.bionic
看下生成的 buildpack-maven
目录:
buildpack-maven
├── bin
│ ├── build
│ └── detect
└── buildpack.toml
各个文件中都是默认的初试数据,并没有什么用处。需要添加些内容:
bin/detect
:
#!/usr/bin/env bash
if [[ ! -f pom.xml ]]; thenexit 100
fi
plan_path=$2
cat >> "${plan_path}" <<EOL
[[provides]]
name = "jdk"
[[requires]]
name = "jdk"
EOL
bin/build
:
#!/usr/bin/env bash
set -euo pipefail
layers_dir="$1"
env_dir="$2/env"
plan_path="$3"
m2_layer_dir="${layers_dir}/maven_m2"
if [[ ! -d ${m2_layer_dir} ]]; thenmkdir -p ${m2_layer_dir}echo "cache = true" > ${m2_layer_dir}.toml
fi
ln -s ${m2_layer_dir} $HOME/.m2
echo "---> Running Maven"
mvn clean install -B -DskipTests
target_dir="target"
for jar_file in $(find "$target_dir" -maxdepth 1 -name "*.jar" -type f); docat >> "${layers_dir}/launch.toml" <<EOL
[[processes]]
type = "web"
command = "java -jar ${jar_file}"
EOLbreak;
done
buildpack.toml
:
api = "0.5"
[buildpack]id = "examples/maven"version = "0.0.1"
[[stacks]]id = "com.atbug.buildpacks.example.stacks.maven"
创建 stack
构建 Maven 项目,首选需要 Java 和 Maven 的环境,我们使用 maven:3.5.4-jdk-8-slim
作为 build image 的 base 镜像。应用的运行时需要 Java 环境即可,因此使用 openjdk:8-jdk-slim
作为 run image 的 base 镜像。
在 stacks
目录中分别创建 build
和 run
两个目录:
build/Dockerfile
FROM maven:3.5.4-jdk-8-slim
ARG cnb_uid=1000
ARG cnb_gid=1000
ARG stack_id
ENV CNB_STACK_ID=${stack_id}
LABEL io.buildpacks.stack.id=${stack_id}
ENV CNB_USER_ID=${cnb_uid}
ENV CNB_GROUP_ID=${cnb_gid}
# Install packages that we want to make available at both build and run time
RUN apt-get update && \apt-get install -y xz-utils ca-certificates && \rm -rf /var/lib/apt/lists/*
# Create user and group
RUN groupadd cnb --gid ${cnb_gid} && \useradd --uid ${cnb_uid} --gid ${cnb_gid} -m -s /bin/bash cnb
USER ${CNB_USER_ID}:${CNB_GROUP_ID}
run/Dockerfile
FROM openjdk:8-jdk-slim
ARG stack_id
ARG cnb_uid=1000
ARG cnb_gid=1000
LABEL io.buildpacks.stack.id="${stack_id}"
USER ${cnb_uid}:${cnb_gid}
然后使用如下命令构建出两个镜像:
export STACK_ID=com.atbug.buildpacks.example.stacks.maven
docker build --build-arg stack_id=${STACK_ID} -t addozhang/samples-buildpacks-stack-build:latest ./build
docker build --build-arg stack_id=${STACK_ID} -t addozhang/samples-buildpacks-stack-run:latest ./run
创建 Builder
有了 buildpack 和 stack 之后就是创建 Builder 了,首先创建 builder.toml
文件,并添加如下内容:
[[buildpacks]]
id = "examples/maven"
version = "0.0.1"
uri = "../buildpacks/buildpack-maven"
[[order]]
[[order.group]]
id = "examples/maven"
version = "0.0.1"
[stack]
id = "com.atbug.buildpacks.example.stacks.maven"
run-image = "addozhang/samples-buildpacks-stack-run:latest"
build-image = "addozhang/samples-buildpacks-stack-build:latest"
然后执行命令,注意这里我们使用了 --pull-policy if-not-present
参数,就不需要将 stack 的两个镜像推送到镜像仓库了:
pack builder create example-builder:latest --config ./builder.toml --pull-policy if-not-present
测试
有了 builder 之后,我们就可以使用创建好的 builder 来构建镜像了。
这里同样加上了 --pull-policy if-not-present
参数来使用本地的 builder 镜像:
# 目录 buildpacks-sample 与 tekton-test 同级,并在 buildpacks-sample 中执行如下命令
pack build addozhang/tekton-test --builder example-builder:latest --pull-policy if-not-present --path ../tekton-test
如果看到类似如下内容,就说明镜像构建成功了(第一次构建镜像由于需要下载 maven 依赖耗时可能会比较久,后续就会很快,可以执行两次验证下):
...
===> EXPORTING
[exporter] Adding 1/1 app layer(s)
[exporter] Reusing layer 'launcher'
[exporter] Reusing layer 'config'
[exporter] Reusing layer 'process-types'
[exporter] Adding label 'io.buildpacks.lifecycle.metadata'
[exporter] Adding label 'io.buildpacks.build.metadata'
[exporter] Adding label 'io.buildpacks.project.metadata'
[exporter] Setting default process type 'web'
[exporter] Saving addozhang/tekton-test...
[exporter] *** Images (0d5ac1158bc0):
[exporter] addozhang/tekton-test
[exporter] Adding cache layer 'examples/maven:maven_m2'
Successfully built image addozhang/tekton-test
启动容器,会看到 spring boot 应用正常启动:
docker run --rm addozhang/tekton-test:latest. ____ _ __ _ _/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \\\/ ___)| |_)| | | | | || (_| | ) ) ) )' |____| .__|_| |_|_| |_\__, | / / / /=========|_|==============|___/=/_/_/_/:: Spring Boot :: (v2.2.3.RELEASE)...
总结
其实现在有很多开源的 buildpack 可以用,没有特定定制的情况下无需自己手动编写。比如下面的几个大厂开源并维护的 Buildpacks:
•Heroku Buildpacks[9]•Google Buildpacks[10]•Paketo[11]
上面几个 buildpacks 库内容比较全面,实现上会有些许不同。比如 Heroku 的执行阶段使用 Shell 脚本,而 Paketo 使用 Golang。后者的扩展性较强,由 Cloud Foundry 基金会支持,并拥有由 VMware 赞助的全职核心开发团队。这些小型模块化的 buildpack,可以通过组合扩展使用不同的场景。
当然还是那句话,自己上手写一个会更容易理解 Buildpack 的工作方式。
引用链接
[1]
测试 Tekton 的 Java 项目: https://github.com/addozhang/tekton-test
[2]
BuildPack: https://buildpacks.io/
[3]
生命周期: https://buildpacks.io/docs/concepts/components/lifecycle/
[4]
Heroku Buildpacks: https://github.com/heroku/
[5]
Google Buildpacks: https://github.com/GoogleCloudPlatform/buildpacks
[6]
Paketo: https://github.com/paketo-buildpacks
[7]
测试 Tekton 的 Java 项目: https://github.com/addozhang/tekton-test
[8]
Github: https://github.com/addozhang/buildpacks-sample
[9]
Heroku Buildpacks: https://github.com/heroku/
[10]
Google Buildpacks: https://github.com/GoogleCloudPlatform/buildpacks
[11]
Paketo: https://github.com/paketo-buildpacks
本文转载自:「云原生指北」,原文:https://tinyurl.com/jwr782uz,版权归原作者所有。欢迎投稿,投稿邮箱: editor@hi-linux.com。
你可能还喜欢
点击下方图片即可阅读
一个 TCP 连接可以发多少个 HTTP 请求?99% 的人可能都不知道!
点击上方图片,『美团|饿了么』外卖红包天天免费领
更多有趣的互联网新鲜事,关注「奇妙的互联网」视频号全了解!
BuildPack:无需编写 Dockerfile,新一代的企业镜像打包工具相关推荐
- Kubernetes学习笔记三:Docker安装,Docker使用,编写Dockerfile,制作容器镜像,上传docker镜像
文章目录 Docker的安装 Docker的使用:docker run命令 查看本地存在的镜像:docker images命令 编写Dockerfile,制作容器镜像 docker build制作镜像 ...
- 镜像打包工具clonezilla
镜像打包工具clonezilla clonezilla 百度云盘链接:https://pan.baidu.com/s/1LOEPqNE9O0Z4QJmNExlgeA 提取码:zlso 使用方法: 1. ...
- [Linux] 编写Dockerfile文件自动构建镜像
Dockerfile是一个文本文件,按顺序包含构建给定镜像所需的所有命令 Docker通过读取Dockerfile中的指令自动构建图像 . Dockerfile遵循特定的格式和指令集,您可以在Dock ...
- dockerfile 编写php,[Linux]编写Dockerfile文件自动构建镜像
Dockerfile是一个文本文件,按顺序包含构建给定镜像所需的所有命令 Docker通过读取Dockerfile中的指令自动构建图像 . Dockerfile遵循特定的格式和指令集,您可以在Dock ...
- 通过dockerfile构建jar包镜像
1.编写Dockerfile文件 # 指定基础镜像 FROM java:8 # 维护者信息 MAINTAINER key "422943393@qq.com" # 拷贝jar包 A ...
- dockerfile构建镜像的命令_编写Dockerfile的最佳实践
虽然 Dockerfile 简化了镜像构建的过程,并且把这个过程可以进行版本控制,但是很多人构建镜像的时候,都有一种冲动--把可能用到的东西都打包到镜像中.这种不正当的 Dockerfile 使用也会 ...
- 再见 Dockerfile,拥抱新型镜像构建技术 Buildpacks
作者:米开朗基杨,方阗 云原生正在吞并软件世界,容器改变了传统的应用开发模式,如今研发人员不仅要构建应用,还要使用 Dockerfile 来完成应用的容器化,将应用及其依赖关系打包,从而获得更可靠的产 ...
- 编写 DockerFile
编写 DockerFile 本节内容简介 在前面的实验中我们多次用到的 Dockerfile,在本实验里我们将通过完成一个实例来学习Dockerfile的编写. 本节中,我们需要依次完成下面几项任务: ...
- linux alpine 用dockerfile创建的ssh镜像
1.下载alpine镜像 [root@docker43 ~]# docker pull alpine Using default tag: latest Trying to pull reposito ...
最新文章
- CocoaPods使用 主要带图。转载。
- 读《大规模敏捷开发实践》
- ASP.NET MVC以ModelValidator为核心的Model验证体系: ModelValidator
- 第六章---机器学习与数据建模
- wxWidgets:wxTreeEvent类用法
- 【Linux】一步一步学Linux——apt-get命令(278)
- 一位嵌入式工程师,硬核单片机编程思想!
- alpine登陆mysql_如何构建一个php7-alpine的docker镜像
- Linux的文件系统及文件缓存知识点整理
- 担心在机场丢行李?这个日本AI能帮你到处找包
- mysql5.7 主从复制的正常切换【转】
- 一次让你搞懂Android应用签名
- 学计算机二级的免费软件,计算机二级MS模拟软件
- [随心译]2017.8.5-你家毛茸茸的宠物的荤粮正在加速气候变化
- S3C2440-裸机篇-01 | JZ2440开发板快速上手
- 利用python爬取豆瓣电影top250
- Annotation-specified bean name.. for bean class ...
- 分享125个ASP源码,总有一款适合你
- URL 参数编解码详解
- 这个项目获2022世界物联网博览会三新成果奖!
热门文章
- Android攻城狮Handler简介
- ps2015安装guideguide参考线辅助工具
- jquery事件绑定的问题Uncaught TypeError: $(...).live is not a function at HTMLDocument.<anonymous> (ex5_
- 数据库实践LAB大纲 02 检索
- App测试流程及测试点(个人整理版)-转
- 解决win7 anaconda 安装 Failed to create menus
- 【QA单】柿饼派及柿饼M3模块相关QA(持续更新....)
- i5 1240p使用perf避坑指南
- 【C语言】C语言实现按照考试成绩的等级输出百分制分数段
- 未来两周目标计划---C++ and Disassembly(不积跬步无以至千里,不积小流无以成江海)