DevOps全链路实战 | 第 6 天:GitLab CI 流水线搭建:.gitlab-ci.yml 与 Kaniko 无守护进程构建

第 6/18 天

引言

K8s

在前面五天里,我们完成了从 K8s 集群搭建、Harbor 私有镜像仓库部署、GitLab CE 代码托管平台安装到 GitLab Runner 的 K8s Executor 与 RBAC 权限配置。基础设施层已经就绪,今天我们将打通整个 DevOps 管道中最关键的一环——CI(持续集成)流水线。

CI 流水线的核心使命是:当开发者向代码仓库推送代码(或发起 Merge Request)时,系统能够自动完成代码检出、依赖安装、单元测试、镜像构建、镜像推送这一系列动作,无需任何人工干预。本篇将围绕 GitLab CI 的声明式配置文件 .gitlab-ci.yml 展开,并使用 Kaniko 这一无守护进程(daemonless)的镜像构建工具替代传统 Docker-in-Docker 方案,在 K8s Executor 环境下实现安全、高效、无特权的容器镜像构建与推送。

核心概念:GitLab CI 与 Kaniko

GitLab CI 工作模型

GitLab CI 采用声明式配置,所有流水线行为都定义在仓库根目录的 .gitlab-ci.yml 文件中。其核心概念包括:

  • Pipeline(流水线):一次代码推送触发的完整构建流程,由多个阶段(Stage)组成
  • Stage(阶段):流水线的逻辑分组,同一 Stage 内的 Job 并行执行,不同 Stage 之间串行
  • Job(作业):最小执行单元,由 script 中的一组 shell 命令构成
  • Runner:执行 Job 的计算资源,我们已在第 5 天配置了 K8s Executor 类型的 Runner

为什么选择 Kaniko

传统容器镜像构建依赖 Docker 守护进程(dockerd),在 K8s Pod 中运行 Docker 需要 DinD(Docker-in-Docker)模式,这要求容器拥有 privileged: true 特权模式,带来严重的安全隐患:

  • 特权容器可访问宿主机内核能力,存在容器逃逸风险
  • DinD 需要额外运行 dockerd 进程,资源开销大
  • 构建缓存管理复杂,跨 Job 难以共享

Kaniko 由 Google 开源,其工作原理是直接在非特权容器内解析 Dockerfile、逐条执行构建指令,最终将分层文件系统打包为镜像并推送到 Registry。整个过程不依赖任何守护进程,也不需要特权模式,天然契合 K8s Executor 的安全模型。

Kaniko 工作原理

💻 代码示例

# Kaniko 构建镜像的完整命令

/kaniko/executor

–context=git://github.com/myorg/myapp.git

–dockerfile=Dockerfile

–destination=registry.stellardata.top/myapp:$(git rev-parse –short HEAD)

–cache=true

–cache-repo=registry.stellardata.top/myapp/cache

Kaniko executor 接收三个核心参数:--context 指定构建上下文来源,--dockerfile 指定构建文件路径,--destination 指定目标镜像仓库地址。同时支持 --cache 启用分层缓存,大幅加速重复构建。

实战步骤:搭建完整 CI 流水线

步骤一:准备应用代码与 Dockerfile

首先在 GitLab 仓库中准备一个简单的 Go 应用及其 Dockerfile:

💻 代码示例

// main.go – 简单的 HTTP 服务

package main

 

import (

"fmt"

"net/http"

"os"

)

 

func main() {

http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {

fmt.Fprintf(w, "Hello from StellarData DevOps Pipeline! Version: %s", os.Getenv("APP_VERSION"))

})

http.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {

w.WriteHeader(http.StatusOK)

w.Write([]byte("OK"))

})

fmt.Println("Server starting on :8080")

http.ListenAndServe(":8080", nil)

}

对应的 Dockerfile 采用多阶段构建,最小化最终镜像体积:

💻 代码示例

# Dockerfile – 多阶段构建

FROM golang:1.21-alpine AS builder

WORKDIR /app

COPY go.mod go.sum ./

RUN go mod download

COPY . .

RUN CGO_ENABLED=0 go build -o myapp -ldflags="-s -w" main.go

 

FROM alpine:3.18

RUN apk add –no-cache ca-certificates

WORKDIR /app

COPY –from=builder /app/myapp .

ENV APP_VERSION=1.0.0

EXPOSE 8080

ENTRYPOINT ["./myapp"]

步骤二:编写 .gitlab-ci.yml

在仓库根目录创建 .gitlab-ci.yml,定义完整的流水线。这里采用 image 指定 Kaniko 官方镜像作为构建容器,通过变量复用 Harbor 仓库凭据:

💻 代码示例

# .gitlab-ci.yml – GitLab CI 流水线定义

stages:

– test

– build

– push

 

variables:

HARBOR_REGISTRY: "registry.stellardata.top"

IMAGE_NAME: "$HARBOR_REGISTRY/devops/myapp"

IMAGE_TAG: "$CI_COMMIT_SHORT_SHA"

 

# 单元测试阶段

unit-test:

stage: test

image: golang:1.21-alpine

script:

– go test -v -race -coverprofile=coverage.out ./…

– go tool cover -func=coverage.out

artifacts:

paths:

– coverage.out

expire_in: 1 week

rules:

– if: $CI_PIPELINE_SOURCE == "merge_request_event"

– if: $CI_COMMIT_BRANCH == "main"

 

# Kaniko 镜像构建与推送阶段

build-and-push:

stage: build

image:

name: gcr.io/kaniko-project/executor:debug

entrypoint: [""]

script:

– mkdir -p /kaniko/.docker

– |

cat > /kaniko/.docker/config.json << EOF

{

"auths": {

"$HARBOR_REGISTRY": {

"username": "$HARBOR_USER",

"password": "$HARBOR_PASSWORD"

}

}

}

EOF

– >

/kaniko/executor

–context="$CI_PROJECT_DIR"

–dockerfile="Dockerfile"

–destination="$IMAGE_NAME:$IMAGE_TAG"

–destination="$IMAGE_NAME:latest"

–cache=true

–cache-repo="$HARBOR_REGISTRY/devops/cache"

–cache-ttl=24h

rules:

– if: $CI_COMMIT_BRANCH == "main"

步骤三:配置 Harbor 仓库凭据

在 GitLab 项目的 Settings → CI/CD → Variables 中,添加以下变量,确保流水线能够通过 Harbor 认证:

💻 代码示例

# 在 GitLab CI/CD Variables 中配置(类型为 Variable,勾选 Masked)

HARBOR_REGISTRY=registry.stellardata.top

HARBOR_USER=ci-robot

HARBOR_PASSWORD=<your-harbor-password-or-token>

注意:HARBOR_PASSWORD 务必勾选 Masked 选项,避免在日志中明文泄露。同时建议在 Harbor 中为 CI 创建专用的 Robot Account,授予对应项目的推送权限,而非使用管理员账号。

步骤四:配置 Runner 命名空间与 RBAC

回顾第 5 天的 Runner 配置,确保 Runner 所在命名空间对 Kaniko 镜像有拉取权限。如果 Harbor 仓库是私有仓库,需要预先创建 ImagePullSecret:

💻 代码示例

# 创建 Harbor 仓库登录凭据 Secret

kubectl create secret docker-registry harbor-credentials

–docker-server=registry.stellardata.top

–docker-username=ci-robot

–docker-password=<password>

–namespace=gitlab-runner

 

# 验证 Secret 创建成功

kubectl get secret harbor-credentials -n gitlab-runner -o yaml

步骤五:触发流水线并验证

代码推送至 main 分支后,GitLab 将自动触发流水线。可在 GitLab 项目的 CI/CD → Pipelines 页面查看执行状态。构建成功后,登录 Harbor 控制台即可看到推送的镜像:

💻 代码示例

# 查看 Harbor 仓库中的镜像

curl -u "ci-robot:<password>"

https://registry.stellardata.top/api/v2.0/projects/devops/repositories/myapp/artifacts

 

# 验证镜像 Tag

{

"name": "myapp",

"tags": [

{"name": "a3f5c2b", "size": 12451840},

{"name": "latest", "size": 12451840}

]

}

常见问题

Q1:Kaniko 构建报错 “no space left on device”

Kaniko 的工作目录位于容器内,构建大型镜像时会占用大量临时空间。K8s Executor 默认的临时存储配额可能不足。解决方法是在 .gitlab-ci.yml 中为 Job 挂载更大的 emptyDir,或在 Runner 的 ConfigMap 中调整 emptyDir.sizeLimit:

💻 代码示例

# 在 Runner ConfigMap 的 [runners.kubernetes] 段中配置

[runners.kubernetes]

ephemeral_storage_limit = "10Gi"

ephemeral_storage_request = "5Gi"

Q2:镜像推送 Harbor 报 401 Unauthorized

通常原因是 /kaniko/.docker/config.json 中的凭据配置错误,或 Harbor 的 Robot Account 权限不足。排查步骤:

  1. 确认 GitLab CI Variables 中的 HARBOR_USER 和 HARBOR_PASSWORD 值正确
  2. 确认 Harbor 中该用户/Robot 对目标项目有 推送到仓库 权限
  3. 在 Kaniko 命令前添加 cat /kaniko/.docker/config.json 调试输出,确认 JSON 格式无误
  4. 注意 Harbor 如果启用了 OIDC,需要使用 Robot Account 的 Token 而非用户密码

Q3:构建缓存不生效,每次都全量构建

Kaniko 的缓存基于镜像分层哈希匹配。如果 Dockerfile 中频繁使用 COPY . . 且上下文文件变动较多,缓存命中率会很低。优化方法:

  • 在 COPY . . 之前先 COPY go.mod go.sum 并执行 go mod download,让依赖下载层被缓存
  • 使用 --cache-repo 指定独立的缓存仓库
  • 使用 --cache-ttl 控制缓存有效期

总结

今天我们完成了 GitLab CI 流水线的搭建,掌握了以下要点:

  • GitLab CI 的声明式配置模型:Pipeline → Stage → Job 三层结构
  • Kaniko 无守护进程构建的核心优势:安全(无需特权)、高效(分层缓存)、云原生(契合 K8s)
  • .gitlab-ci.yml 的完整编写:包含单元测试、镜像构建、镜像推送三个阶段
  • Harbor 凭据的安全管理:通过 GitLab CI Variables 注入,使用 Robot Account 降权

至此,从代码提交到镜像推送到 Harbor 的 CI 闭环已经打通。但这只是 DevOps 管道的前半段——镜像虽然构建出来了,但还没有部署到 K8s 集群。自动部署这一环需要 Argo CD 的 GitOps 能力来实现,这将在后续篇章中展开。

下期预告

明天我们将进入第 7 天:Harbor 镜像版本管理与 Tag 策略配置。当 CI 流水线不断推送大量镜像后,如何管理这些镜像的版本、如何制定合理的 Tag 策略、如何清理冗余镜像避免空间膨胀,都是生产环境必须解决的问题。敬请期待。

系列大纲

  1. 全链路架构总览:从代码提交到灰度发布的完整管道设计
  2. K8s 集群与网络基础:kubeadm 离线部署与 Cilium 网络插件
  3. Harbor 私有镜像仓库部署与验证:Helm 安装与镜像推送
  4. GitLab CE 自托管部署:代码仓库与项目管理平台
  5. GitLab Runner 配置:K8s Executor 与 RBAC 权限设置
  6. GitLab CI 流水线搭建:.gitlab-ci.yml 与 Kaniko 无守护进程构建(今天)
  7. Harbor 镜像版本管理与 Tag 策略配置
  8. Argo CD 部署与 GitOps 配置仓库创建
  9. CI 与 CD 联动:从代码提交到部署的全自动闭环
  10. Prometheus 部署:kube-prometheus-stack 与 ServiceMonitor 指标采集
  11. Grafana 看板搭建:K8s 标准面板与自定义业务看板
  12. 告警规则与 Alertmanager:钉钉/邮件通知渠道配置
  13. Argo Rollouts 安装与基础概念:CRD 与 Rollout 资源定义
  14. 金丝雀发布实战:setWeight 流量分割与 pause 步骤
  15. AnalysisTemplate 指标分析与自动回滚机制
  16. 全链路端到端演示:代码提交→构建→灰度→监控→告警完整流程
  17. 生产化加固要点:Harbor/GitLab/Argo CD 高可用方案
  18. 供应链安全闭环:Trivy 扫描 + cosign 签名 + Kyverno 验签
微信二维码
© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享
评论 抢沙发
头像 - 恒星
欢迎您留下宝贵的见解!
提交
头像 - 恒星

昵称

取消
昵称表情代码图片快捷回复

    暂无评论内容