使用 TypeScript 编写 Dockerfile 和 CI 管道。
Trellis 是一款便携式 CI/CD 工具。借助 Trellis,您可以在 TypeScript 中定义 Dockerfile 和 CI/CD 管道,并在任何地方(本地或托管平台上)运行它们。
首先,使用brew install deno
(或类似的)安装Deno。
其次,安装 Trellis CLI:
deno install
--allow-run=docker
--allow-net
--allow-write
--allow-env
--allow-read
https://deno.land/x/[email protected]/cli.ts
运行trellis --help
来验证您的安装:
>>> trellis --help
Usage: trellis build mod.ts
Options:
-V, --version output the version number
-h, --help display help for command
Commands:
ls [file] List all Images and Tasks available in a
TypeScript module
preview [options] [file] Generate a Dockerfile defined in a TypeScript
module
build [options] [file] Build an Image defined in a TypeScript module
run [options] [file] Run a Task defined in a TypeScript module
help [command] display help for command
导出任何Image
以启用 Dockerfile 生成和使用 Trellis 构建镜像。
例如,要定义安装了一些有用实用程序的 Ubuntu 映像,您可以编写以下mod.ts
文件:
import { Image } from "https://deno.land/x/[email protected]/mod.ts" ;
const UBUNTU_VERSION = "20.04" ;
export const buildStage = Image . from ( `ubuntu: ${ UBUNTU_VERSION } ` )
. workDir ( "/root" )
. aptInstall ( [
"curl" ,
"jq" ,
"git" ,
] ) ;
运行trellis ls mod.ts
列出可构建的图像:
>>> trellis ls mod.ts
Images:
- buildStage (trellis build --target buildStage)
我们可以使用trellis preview mod.ts --target buildStage
预览生成的 Dockerfile:
>>> trellis preview --target buildStage
# syntax=docker/dockerfile:1.4
FROM ubuntu:20.04 AS stage-0
WORKDIR /root
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked --mount=type=cache,target=/var/lib/apt,sharing=locked apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends curl git jq
我们可以使用trellis build --target buildStage
构建镜像:
>>> trellis build --target buildStage
[+] Building 0.6s (11/11) FINISHED
= > [internal] load build definition from Dockerfile 0.0s
= > = > transferring dockerfile: 335B 0.0s
= > [internal] load .dockerignore 0.0s
= > = > transferring context: 2B 0.0s
= > resolve image config for docker.io/docker/dockerfile:1.4 0.2s
= > CACHED docker-image://docker.io/docker/dockerfile:1.4@sha256:9ba7531bd80fb0a858632727cf7a112fbf 0.0s
= > [internal] load build definition from Dockerfile 0.0s
= > [internal] load .dockerignore 0.0s
= > [internal] load metadata for docker.io/library/ubuntu:20.04 0.2s
= > [stage-0 1/3] FROM docker.io/library/ubuntu:20.04@sha256:35ab2bf57814e9ff49e365efd5a5935b6915ee 0.0s
= > CACHED [stage-0 2/3] WORKDIR /root 0.0s
= > CACHED [stage-0 3/3] RUN --mount=type=cache,target=/var/cache/apt,sharing=locked --mount=type=c 0.0s
= > exporting to image 0.0s
= > = > exporting layers 0.0s
= > = > writing image sha256:17f750ba9a4becf38ce4d584d0de4793bfd6a8139674c3b332cdcdf6525ea8d9 0.0s
= > = > naming to docker.io/trellis/db112e211de238c035a9fd3bbcbd5c417aafc5ee96a8c24d99d4caf81a759903 0.0s
√ Build: trellis/db112e211de238c035a9fd3bbcbd5c417aafc5ee96a8c24d99d4caf81a759903
从 TypeScript 模块导出任何函数,以支持使用 Trellis 执行任务。
例如,要定义 CI 管道来验证我们的命令行实用程序是否已成功安装,您可以编写以下tasks.ts
文件:
import { build , Image , run } from "https://deno.land/x/[email protected]/mod.ts" ;
import { buildStage } from "./mod.ts" ;
export default async function runChecks ( ) {
await build ( buildStage ) ;
const checkCurl = Image . from ( buildStage ) . run (
"curl --help" ,
) ;
const checkJq = Image . from ( buildStage ) . run (
"jq --help" ,
) ;
const checkGit = Image . from ( buildStage ) . run (
"git --help" ,
) ;
await Promise . all ( [
run ( checkCurl ) ,
run ( checkJq ) ,
run ( checkGit ) ,
] ) ;
}
运行trellis ls tasks.ts
列出可执行任务:
>>> trellis ls tasks.ts
Tasks:
- default (trellis run tasks.ts)
我们可以使用trellis run tasks.ts
在本地执行任务:
>>> trellis run tasks.ts
[+] Building 1.1s (13/13) FINISHED
= > [internal] load build definition from Dockerfile 0.0s
= > = > transferring dockerfile: 335B 0.0s
= > [internal] load .dockerignore 0.0s
= > = > transferring context: 2B 0.0s
= > resolve image config for docker.io/docker/dockerfile:1.4 0.5s
= > [auth] docker/dockerfile:pull token for registry-1.docker.io 0.0s
= > CACHED docker-image://docker.io/docker/dockerfile:1.4@sha256:9ba7531bd80fb0a858632727cf7a112fbf 0.0s
= > [internal] load .dockerignore 0.0s
= > [internal] load build definition from Dockerfile 0.0s
= > [internal] load metadata for docker.io/library/ubuntu:20.04 0.3s
= > [auth] library/ubuntu:pull token for registry-1.docker.io 0.0s
= > [stage-0 1/3] FROM docker.io/library/ubuntu:20.04@sha256:35ab2bf57814e9ff49e365efd5a5935b6915ee 0.0s
= > CACHED [stage-0 2/3] WORKDIR /root 0.0s
= > CACHED [stage-0 3/3] RUN --mount=type=cache,target=/var/cache/apt,sharing=locked --mount=type=c 0.0s
= > exporting to image 0.0s
= > = > exporting layers 0.0s
= > = > writing image sha256:17f750ba9a4becf38ce4d584d0de4793bfd6a8139674c3b332cdcdf6525ea8d9 0.0s
= > = > naming to docker.io/trellis/adf8a603d1ab539848d89f68491e1b9213c1ca498f3f68d871e1b59c4c7de601 0.0s
√ Build: trellis/adf8a603d1ab539848d89f68491e1b9213c1ca498f3f68d871e1b59c4c7de601
√ Run: git --help
√ Run: jq --help
√ Run: curl --help
Trellis 可以通过trellis.config.ts
文件进行配置,其基本语义是根据 Vite 建模的。
trellis.config.ts
应包含一个由defineConfig
调用组成的默认导出,如下所示:
import { defineConfig } from "https://deno.land/x/[email protected]/mod.ts" ;
export default defineConfig ( {
engine : "docker" ,
} ) ;
Trellis 将使用最接近的trellis.config.ts
,首先在当前工作目录中查找,然后在每个后续父目录中查找。
Trellis 与 depot.dev 兼容,可用于实现零配置的云加速构建。运行 Depot 安装( brew install depot/tap/depot
或类似的,然后是depot login
),然后定义一个trellis.config.ts
,如下所示:
import { defineConfig } from "https://deno.land/x/[email protected]/mod.ts" ;
export default defineConfig ( {
engine : {
type : "depot" ,
project : "${YOUR_PROJECT_ID}" ,
} ,
} ) ;
从那里开始,所有 Trellis 构建都将通过 Depot 运行。
Trellis 在 Deno 上运行,使其成为 GitHub Actions 上的一步安装:
name : CI
on :
push :
branches : [ main ]
pull_request :
branches : [ main ]
env :
DOCKER_BUILDKIT : 1
jobs :
build :
name : " Build "
runs-on : ubuntu-latest
steps :
- uses : actions/checkout@v3
- name : " Install Deno "
uses : denoland/setup-deno@v1
with :
deno-version : " 1.25.2 "
- name : " Install Trellis "
working-directory : ./trellis
run : deno install --allow-run=docker --allow-net --allow-write --allow-env --allow-read https://deno.land/x/[email protected]/cli.ts
- name : " Build the image "
working-directory : ./examples/typescript
run : trellis build trellis/mod.ts
Trellis 的动机来自以下观察,这些观察来自维护大型容器化 CI/CD 系统的经验。
Dockerfile 很难维护。随着时间的推移,大型系统往往会积累具有相似子部分但没有共享抽象的 Dockerfile 集合。
高效的Dockerfile 很难编写。编写一个可最大程度地缓存且占用空间最小的 Dockerfile 需要大量的专业知识。例如,对于apt-get install
,Docker 文档建议如下:
RUN apt-get update && apt-get install -y
# Be sure to sort dependencies to maximize cacheability.
bzr
cvs
git
mercurial
subversion
# Clear the apt cache to minimize disk size.
&& rm -rf /var/lib/apt/lists/ *
CI/CD迭代循环太慢。编写新的 GitHub Actions 管道、Jenkinsfile 等的常见工作流程是提交、推送、等待系统确认您的更改,然后等待您的任务失败——连续数十次甚至数百次。使用现有的 CI 解决方案,您编写的代码要在不熟悉的系统上运行,不受您的控制,并且没有一流的开发工作流程。
CI/CD 系统产生了显着的锁定。将 Jenkinsfiles 或 YAML 文件移植到 GitHub Actions(反之亦然)需要处理特定于平台的抽象。
Trellis 通过一些重要的设计决策解决了这些问题。
首先:使用 Trellis,您可以在 TypeScript 中定义 Dockerfile 和 CI/CD 管道。这为您提供了“完整”编程语言的强大功能,同时保留了声明性 API。使用 TypeScript,我们可以获得以下好处:
apt-get install
步骤。deno.land
导入 Slack 客户端一样简单。第二: Trellis 使本地执行成为一流的原语。 CI/CD 不应该感觉像是一个完全独立的系统;它应该感觉像运行代码。 Trellis 构建于 Deno 之上,并且高度可移植。您可以在本地运行trellis build
,就像在 GitHub Actions 或其他地方运行一样。通过这种方式,Trellis 从 Earthly 和 Dagger 等工具中获得了灵感。
Trellis 有一些尚未实现的理想目标:
Trellis 既是一个库,也是一个命令行界面。使用 Trellis,您可以从 TypeScript 模块导出Image
定义和可运行函数(称为“任务”),然后通过trellis
CLI 执行它们。
trellis preview
生成在 TypeScript 模块中定义的 Dockerfile。
Usage: trellis preview [options] [file]
Generate a Dockerfile defined in a TypeScript module
Options:
-t, --target < TARGET > Image to build within the TypeScript module
-h, --help display help for command
trellis build
构建在 TypeScript 模块中定义的图像。
Usage: trellis build [options] [file]
Build an Image defined in a TypeScript module
Options:
-t, --target < TARGET > Image to build within the TypeScript module
--push Whether to push the image to a remote registry
-h, --help display help for command
trellis ls
列出 TypeScript 模块中可用的所有图像和任务。
Usage: trellis ls [options] [file]
List all Images and Tasks available in a TypeScript module
Options:
-h, --help display help for command
trellis run
运行 TypeScript 模块中定义的任务。
Run a Task defined in a TypeScript module
Options:
-t, --target <TARGET> Task to run within the TypeScript module
-h, --help display help for command
./examples
目录演示了 Trellis 的各种用例。 Trellis 非常灵活,可以单独用于为其他系统生成 Dockerfile,或定义整个 CI/CD 管道。
rocket
:Rocket 框架之上的 Rust 网络服务器。利用trellis preview
通过 Fly.io 演示多阶段构建和部署。ruff
:Rust 命令行工具。演示高效的构建和 CI 检查。runc
:Linux 开发容器。演示使用 Trellis 生成工件并将其复制回主机。turborepo
:Turborepo 自己的 Docker 示例,经过修改以使用 Trellis 生成 Dockerfile。typescript
:TypeScript monorepo。演示高效的构建和 CI 检查,以及合并常量(如 TypeScript 工作区列表)。wasm
:“你好,世界!”编译为 Wasm 并在 Wasmtime 上测试的 Rust 二进制文件。 Trellis 构建于 Deno 之上,Deno 作为单个二进制可执行文件分发,没有外部依赖项。使用 Deno 意味着在任何地方安装 Trellis 都和deno install ...
一样简单——没有package.json
,没有npm install
,也没有 TypeScript 转译步骤。
与 Nixpacks 类似,Trellis 生成 Dockerfile。这简化了 Trellis 的实施,但也使用户能够利用 Trellis 单独生成 Dockerfile,而不是作为完整的 CI/CD 解决方案。
trellis build
和trellis run
依赖于 Docker,并假设 Docker 守护进程可在本地访问。
麻省理工学院