pod打包原理_DevOps系列Drone的pipeline原理与代码分析

点击?蓝色“ 深入原理”,关注并“设为星标”

技术干货,第一时间推送

需要实现一个工作任务流(task pipeline),基于之前CICD的经验,jenkins pipeline和drone的pipeline进入候选。

drone是基于go的cicd解决方案,github上有1.6万+star,本文简单对比了其和jenkins的区别,重点介绍了drone的pipeline原理,并简单分析了代码。

jenkins 与 drone

对比项

jenkins

drone

pipeline定义

编写jenkinsfile

编写流程yml

运行方式

在一个pod里运行

每一步骤起对应的container,通过挂载volume实现数据共享

运行环境

物理机或者容器环境,包括K8S

docker容器环境

开发语言

java

golang

drone pipeline好处是相对更轻量级,yml定义也相对简洁清晰,按照功能来划分容器,可以方便的实现task的复用,而jenkins则是完全打包到一个镜像,会造成单个镜像体积过大,比如jenkins的单个镜像超过2G。

drone的pipeline,是基于https://github.com/cncd/pipeline 实现的,这里简单分析下其原理。

编译和执行 drone pipeline

要了解一个程序的原理,先从输入输出讲起。

先安装:

go get -u github.com/cncd/pipeline
go install github.com/cncd/pipeline/pipec

然后测试

cd $GOPATH/github.com/cncd/pipeline/samples/sample_1
# ll
total 28
drwxr-xr-x 2 root root 4096 Jan 22 11:44 ./
drwxr-xr-x 13 root root 4096 Jan 22 11:02 ../-rw-r--r-- 1 root root 549 Jan 22 11:02 .env-rw-r--r-- 1 root root 6804 Jan 22 16:30 pipeline.json-rw-r--r-- 1 root root 229 Jan 22 11:02 pipeline.yml-rw-r--r-- 1 root root 138 Jan 22 11:02 README.md
  • pipeline.yml 定义文件

  • pipeline.json 编译后的配置文件

  • .env 环境变量

先来查看pipeline.yml 定义

workspace:
base: /go
path: src/github.com/drone/envsubst
clone:
git:
image: plugins/git
depth: 50
pipeline:
build:
image: golang:1.7
commands:- go get -t ./...- go build- go test -v

上面的yml定义了:

  • 工作目录workspace

  • 初始化工作,git clone仓库,仓库地址在.env里定义

  • 然后是定义pipeline,

    • pipeline下面是step数组,这里只有一个build

    • 使用golang:1.7镜像

    • 构建命令在commands数组里定义

通过pipec compilecompile配置文件:

# pipec compile
Successfully compiled pipeline.yml to pipeline.json

查看编译后的pipeline.json

{"pipeline": [{"name": "pipeline_clone_0","alias": "git","steps": [{"name": "pipeline_clone_0","alias": "git","image": "plugins/git:latest","working_dir": "/go/src/github.com/drone/envsubst","environment": {"CI": "drone","CI_BUILD_CREATED": "1486119586","CI_BUILD_EVENT": "push","CI_BUILD_NUMBER": "6","CI_BUILD_STARTED": "1486119585","CI_COMMIT_AUTHOR": "bradrydzewski","CI_COMMIT_AUTHOR_NAME": "bradrydzewski","CI_COMMIT_BRANCH": "master","CI_COMMIT_MESSAGE": "added a few more test cases for escaping behavior","CI_COMMIT_REF": "refs/heads/master","CI_COMMIT_SHA": "d0876d3176965f9552a611cbd56e24a9264355e6","CI_REMOTE_URL": "https://github.com/drone/envsubst.git","CI_REPO": "drone/envsubst","CI_REPO_LINK": "https://github.com/drone/envsubst","CI_REPO_NAME": "drone/envsubst","CI_REPO_REMOTE": "https://github.com/drone/envsubst.git","CI_SYSTEM": "pipec","CI_SYSTEM_ARCH": "linux/amd64","CI_SYSTEM_LINK": "https://github.com/cncd/pipec","CI_SYSTEM_NAME": "pipec","CI_WORKSPACE": "/go/src/github.com/drone/envsubst","DRONE": "true","DRONE_ARCH": "linux/amd64","DRONE_BRANCH": "master","DRONE_BUILD_CREATED": "1486119586","DRONE_BUILD_EVENT": "push","DRONE_BUILD_LINK": "https://github.com/cncd/pipec/drone/envsubst/6","DRONE_BUILD_NUMBER": "6","DRONE_BUILD_STARTED": "1486119585","DRONE_COMMIT": "d0876d3176965f9552a611cbd56e24a9264355e6","DRONE_COMMIT_AUTHOR": "bradrydzewski","DRONE_COMMIT_BRANCH": "master","DRONE_COMMIT_MESSAGE": "added a few more test cases for escaping behavior","DRONE_COMMIT_REF": "refs/heads/master","DRONE_COMMIT_SHA": "d0876d3176965f9552a611cbd56e24a9264355e6","DRONE_JOB_STARTED": "1486119585","DRONE_REMOTE_URL": "https://github.com/drone/envsubst.git","DRONE_REPO": "drone/envsubst","DRONE_REPO_LINK": "https://github.com/drone/envsubst","DRONE_REPO_NAME": "envsubst","DRONE_REPO_OWNER": "drone","DRONE_REPO_SCM": "git","DRONE_WORKSPACE": "/go/src/github.com/drone/envsubst","PLUGIN_DEPTH": "50"},"volumes": ["pipeline_default:/go"],"networks": [{"name": "pipeline_default","aliases": ["git"]}],"on_success": true,"auth_config": {}}]},{"name": "pipeline_stage_0","alias": "build","steps": [{"name": "pipeline_step_0","alias": "build","image": "golang:1.7","working_dir": "/go/src/github.com/drone/envsubst","environment": {"CI": "drone","CI_BUILD_CREATED": "1486119586","CI_BUILD_EVENT": "push","CI_BUILD_NUMBER": "6","CI_BUILD_STARTED": "1486119585","CI_COMMIT_AUTHOR": "bradrydzewski","CI_COMMIT_AUTHOR_NAME": "bradrydzewski","CI_COMMIT_BRANCH": "master","CI_COMMIT_MESSAGE": "added a few more test cases for escaping behavior","CI_COMMIT_REF": "refs/heads/master","CI_COMMIT_SHA": "d0876d3176965f9552a611cbd56e24a9264355e6","CI_REMOTE_URL": "https://github.com/drone/envsubst.git","CI_REPO": "drone/envsubst","CI_REPO_LINK": "https://github.com/drone/envsubst","CI_REPO_NAME": "drone/envsubst","CI_REPO_REMOTE": "https://github.com/drone/envsubst.git","CI_SCRIPT": "CmlmIFsgLW4gIiRDSV9ORVRSQ19NQUNISU5FIiBdOyB0aGVuCmNhdCA8PEVPRiA+ICRIT01FLy5uZXRyYwptYWNoaW5lICRDSV9ORVRSQ19NQUNISU5FCmxvZ2luICRDSV9ORVRSQ19VU0VSTkFNRQpwYXNzd29yZCAkQ0lfTkVUUkNfUEFTU1dPUkQKRU9GCmNobW9kIDA2MDAgJEhPTUUvLm5ldHJjCmZpCnVuc2V0IENJX05FVFJDX1VTRVJOQU1FCnVuc2V0IENJX05FVFJDX1BBU1NXT1JECnVuc2V0IENJX1NDUklQVAp1bnNldCBEUk9ORV9ORVRSQ19VU0VSTkFNRQp1bnNldCBEUk9ORV9ORVRSQ19QQVNTV09SRAoKZWNobyArICJnbyBnZXQgLXQgLi8uLi4iCmdvIGdldCAtdCAuLy4uLgoKZWNobyArICJnbyBidWlsZCIKZ28gYnVpbGQKCmVjaG8gKyAiZ28gdGVzdCAtdiIKZ28gdGVzdCAtdgoK","CI_SYSTEM": "pipec","CI_SYSTEM_ARCH": "linux/amd64","CI_SYSTEM_LINK": "https://github.com/cncd/pipec","CI_SYSTEM_NAME": "pipec","CI_WORKSPACE": "/go/src/github.com/drone/envsubst","DRONE": "true","DRONE_ARCH": "linux/amd64","DRONE_BRANCH": "master","DRONE_BUILD_CREATED": "1486119586","DRONE_BUILD_EVENT": "push","DRONE_BUILD_LINK": "https://github.com/cncd/pipec/drone/envsubst/6","DRONE_BUILD_NUMBER": "6","DRONE_BUILD_STARTED": "1486119585","DRONE_COMMIT": "d0876d3176965f9552a611cbd56e24a9264355e6","DRONE_COMMIT_AUTHOR": "bradrydzewski","DRONE_COMMIT_BRANCH": "master","DRONE_COMMIT_MESSAGE": "added a few more test cases for escaping behavior","DRONE_COMMIT_REF": "refs/heads/master","DRONE_COMMIT_SHA": "d0876d3176965f9552a611cbd56e24a9264355e6","DRONE_JOB_STARTED": "1486119585","DRONE_REMOTE_URL": "https://github.com/drone/envsubst.git","DRONE_REPO": "drone/envsubst","DRONE_REPO_LINK": "https://github.com/drone/envsubst","DRONE_REPO_NAME": "envsubst","DRONE_REPO_OWNER": "drone","DRONE_REPO_SCM": "git","DRONE_WORKSPACE": "/go/src/github.com/drone/envsubst","HOME": "/root","SHELL": "/bin/sh"},"entrypoint": ["/bin/sh","-c"],"command": ["echo $CI_SCRIPT | base64 -d | /bin/sh -e"],"volumes": ["pipeline_default:/go"],"networks": [{"name": "pipeline_default","aliases": ["build"]}],"on_success": true,"auth_config": {}}]}],"networks": [{"name": "pipeline_default","driver": "bridge"}],"volumes": [{"name": "pipeline_default","driver": "local"}],"secrets": null}

简单分析结构:

  • pipeline 定义了执行的stage,每个stage有一个或者多个step

  • networks、volumes、secrets 分别定义网络、存储和secrets

    • 通过network,实现container互通

    • 通过volumes实现数据共享

最后执行,通过pipec exec

# pipec exec
proc "pipeline_clone_0" started+ git init
Initialized empty Git repository in /go/src/github.com/drone/envsubst/.git/+ git remote add origin https://github.com/drone/envsubst.git+ git fetch --no-tags --depth=50 origin +refs/heads/master:
From https://github.com/drone/envsubst* branch master -> FETCH_HEAD* [new branch] master -> origin/master+ git reset --hard -q d0876d3176965f9552a611cbd56e24a9264355e6+ git submodule update --init --recursive
proc "pipeline_clone_0" exited with status 0
proc "pipeline_step_0" started+ go get -t ./...+ go build+ go test -v=== RUN TestExpand--- PASS: TestExpand (0.00s)=== RUN TestFuzz--- PASS: TestFuzz (0.01s)=== RUN Test_len--- PASS: Test_len (0.00s)=== RUN Test_lower--- PASS: Test_lower (0.00s)=== RUN Test_lowerFirst--- PASS: Test_lowerFirst (0.00s)=== RUN Test_upper--- PASS: Test_upper (0.00s)=== RUN Test_upperFirst--- PASS: Test_upperFirst (0.00s)=== RUN Test_default--- PASS: Test_default (0.00s)
PASS
ok github.com/drone/envsubst 0.009s
proc "pipeline_step_0" exited with status 0

pipeline 原理分析

编译过程

可以形象的理解为 .env+pipeline.yml --> pipeline.json

编译过程不复杂,主要是解析pipeline.yml为Config:

Config struct {
Cache libcompose.Stringorslice
Platform string
Branches Constraint
Workspace Workspace
Clone Containers
Pipeline Containers
Services Containers
Networks Networks
Volumes Volumes
Labels libcompose.SliceorMap}

然后转换为json对应的config:

Config struct {
Stages []*Stage `json:"pipeline"` // pipeline stages
Networks []*Network `json:"networks"` // network definitions
Volumes []*Volume `json:"volumes"` // volume definitions
Secrets []*Secret `json:"secrets"` // secret definitions}

该部分主要代码在pipeline/frontend里

执行过程

我们主要关注执行过程,主要代码在pipeline/backend里。

首先是读取配置文件为backend.Config

config, err := pipeline.Parse(reader)if err != nil {return err}

然后创建执行环境,目前的代码仅docker可用,k8s是空代码。

var engine backend.Engineif c.Bool("kubernetes") {
engine = kubernetes.New(
c.String("kubernetes-namepsace"),
c.String("kubernetes-endpoint"),
c.String("kubernetes-token"),)} else {
engine, err = docker.NewEnv()if err != nil {return err}}

接着开始执行

    ctx, cancel := context.WithTimeout(context.Background(), c.Duration("timeout"))
defer cancel()
ctx = interrupt.WithContext(ctx)return pipeline.New(config,
pipeline.WithContext(ctx),
pipeline.WithLogger(defaultLogger),
pipeline.WithTracer(defaultTracer),
pipeline.WithEngine(engine),).Run()

其中pipeline.NEW创建了Runtime对象;

type Runtime struct {
err error // 错误信息
spec *backend.Config // 配置信息
engine backend.Engine // docker engine
started int64 // 开始时间
ctx context.Context
tracer Tracer
logger Logger}

其中Engine,操作容器的interface,目前仅docker可用。

// Engine defines a container orchestration backend and is used// to create and manage container resources.
type Engine interface {// Setup the pipeline environment.Setup(context.Context, *Config) error// Start the pipeline step.Exec(context.Context, *Step) error// Kill the pipeline step.Kill(context.Context, *Step) error// Wait for the pipeline step to complete and returns// the completion results.Wait(context.Context, *Step) (*State, error)// Tail the pipeline step logs.Tail(context.Context, *Step) (io.ReadCloser, error)// Destroy the pipeline environment.Destroy(context.Context, *Config) error}

关注Run:

// Run starts the runtime and waits for it to complete.func (r *Runtime) Run() error {// 延迟函数,用于销毁docker env
defer func() {
r.engine.Destroy(r.ctx, r.spec)}()// 初始化docker engine
r.started = time.Now().Unix()if err := r.engine.Setup(r.ctx, r.spec); err != nil {return err}// 依次运行stagefor _, stage := range r.spec.Stages {
select {case <-r.ctx.Done():return ErrCancel// 执行case err := <-r.execAll(stage.Steps):if err != nil {
r.err = err}}}return r.err}

重点在于使用errgroup.Group通过协程方式运行step:

// 执行所有stepsfunc (r *Runtime) execAll(procs []*backend.Step) <-chan error {var g errgroup.Group
done := make(chan error)// 遍历执行stepfor _, proc := range procs {// 协程 exec
proc := proc
g.Go(func() error {return r.exec(proc)})}
go func() {
done <- g.Wait()close(done)}()return done}// 执行单个stepfunc (r *Runtime) exec(proc *backend.Step) error {switch {case r.err != nil && proc.OnFailure == false:return nilcase r.err == nil && proc.OnSuccess == false:return nil}// trace日志if r.tracer != nil {
state := new(State)
state.Pipeline.Time = r.started
state.Pipeline.Error = r.err
state.Pipeline.Step = proc
state.Process = new(backend.State) // emptyif err := r.tracer.Trace(state); err == ErrSkip {return nil} else if err != nil {return err}}// docker engine执行if err := r.engine.Exec(r.ctx, proc); err != nil {return err}// 记录日志信息if r.logger != nil {
rc, err := r.engine.Tail(r.ctx, proc)if err != nil {return err}
go func() {
r.logger.Log(proc, multipart.New(rc))
rc.Close()}()}if proc.Detached {return nil}// 等待docker engine执行完成
wait, err := r.engine.Wait(r.ctx, proc)if err != nil {return err}if r.tracer != nil {
state := new(State)
state.Pipeline.Time = r.started
state.Pipeline.Error = r.err
state.Pipeline.Step = proc
state.Process = waitif err := r.tracer.Trace(state); err != nil {return err}}if wait.OOMKilled {return &OomError{
Name: proc.Name,
Code: wait.ExitCode,}} else if wait.ExitCode != 0 {return &ExitError{
Name: proc.Name,
Code: wait.ExitCode,}}return nil}

作者:Jadepeng

-深入原理-  

   知其然并知其所以然    

c2b2d4efdaa77f5a4ffef4cf3d0cd0b8.png

38fdfff18dcb702b4084ff28093899b0.png

63b75787509f751d1f58feaf3c31aa23.gif
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值