<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Runner on 维克的博客</title><link>https://blogs.wuvikr.top/tags/runner/</link><description>Recent content in Runner on 维克的博客</description><generator>Hugo</generator><language>en</language><lastBuildDate>Fri, 30 Oct 2026 15:59:39 +0800</lastBuildDate><atom:link href="https://blogs.wuvikr.top/tags/runner/index.xml" rel="self" type="application/rss+xml"/><item><title>从零开始：生产级 GitLab CI 流水线搭建</title><link>https://blogs.wuvikr.top/posts/%E4%BB%8E%E9%9B%B6%E5%BC%80%E5%A7%8B%E7%94%9F%E4%BA%A7%E7%BA%A7gitlab-ci%E6%B5%81%E6%B0%B4%E7%BA%BF%E6%90%AD%E5%BB%BA/</link><pubDate>Fri, 30 Oct 2026 15:59:39 +0800</pubDate><guid>https://blogs.wuvikr.top/posts/%E4%BB%8E%E9%9B%B6%E5%BC%80%E5%A7%8B%E7%94%9F%E4%BA%A7%E7%BA%A7gitlab-ci%E6%B5%81%E6%B0%B4%E7%BA%BF%E6%90%AD%E5%BB%BA/</guid><description>&lt;p>本文以 Go 语言为案例，从「只做编译和测试」的最小流水线起步，逐步加上&lt;strong>构建镜像&lt;/strong>、&lt;strong>镜像漏洞扫描&lt;/strong>、&lt;strong>推送 Harbor&lt;/strong>，最终形成五阶段完整生产级 GitLab CI 流水线。&lt;/p>
&lt;p>下面是本文的实践路线：&lt;/p>
&lt;ol>
&lt;li>最小流水线，只做 &lt;code>build&lt;/code> 和 &lt;code>test&lt;/code> 阶段，让流水线先简单跑通。&lt;/li>
&lt;li>增加 &lt;code>build-image&lt;/code> 阶段，用 Kaniko 按 Dockerfile 构建镜像。&lt;/li>
&lt;li>增加 &lt;code>scan-image&lt;/code> 阶段，用 Trivy 扫描镜像，有高危漏洞直接退出。&lt;/li>
&lt;li>增加 &lt;code>push&lt;/code> 阶段，扫描通过后把镜像推送并打上标签。&lt;/li>
&lt;li>&lt;strong>汇总&lt;/strong>：给出完整五阶段配置与生产最佳实践。&lt;/li>
&lt;/ol>
&lt;p>&lt;strong>为什么从头开始一点点增加步骤&lt;/strong>?&lt;/p>
&lt;p>直接给出五十行的完整配置的话，出了问题根本不知道是编译、测试、构建、扫描还是推送哪一个阶段的问题。先跑通最小闭环，每次增加一个阶段，有问题能立即验证，排查范围小，人也更有把握。&lt;/p>
&lt;p>日常编写构建生产级别的流水线也建议这样一步步验证。&lt;/p>
&lt;h1 id="1-最小流水线验证">1. 最小流水线验证&lt;/h1>
&lt;p>先只配置两个 job：&lt;/p>
&lt;ul>
&lt;li>&lt;code>build&lt;/code> 阶段：负责编译校验，确认代码能否正常打包、是否存在语法错误。&lt;/li>
&lt;li>&lt;code>test&lt;/code> 阶段：运行单元测试。&lt;/li>
&lt;/ul>
&lt;p>这两个 job 是整个 CI 的基础，也是触发最频繁、最需要优先跑稳的部分。&lt;/p>
&lt;h2 id="11-为什么先只做这两步">1.1 为什么先只做这两步&lt;/h2>
&lt;ul>
&lt;li>&lt;strong>反馈最快&lt;/strong>：编译和单元测试在同一个代码环境中运行，几秒内就能拿到结果，不依赖镜像仓库或外部服务。&lt;/li>
&lt;li>&lt;strong>成本最低&lt;/strong>：不构建镜像、不推送，也不做扫描，任何分支的提交都可以安全触发，不会产生多余产物。&lt;/li>
&lt;li>&lt;strong>优先保障代码质量&lt;/strong>：如果编译或测试失败，就不应继续执行后续的构建、扫描和推送步骤。&lt;/li>
&lt;/ul>
&lt;h2 id="13-写配置文件">1.3 写配置文件&lt;/h2>
&lt;p>在项目根目录新建 &lt;code>.gitlab-ci.yml&lt;/code>，写入下面内容：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#f92672">stages&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - &lt;span style="color:#ae81ff">build&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - &lt;span style="color:#ae81ff">test&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#f92672">variables&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">GO_VERSION&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;1.26&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e"># 国内须覆盖 GOPROXY，否则 go mod download 走 proxy.golang.org 会超时&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">GOPROXY&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;https://goproxy.cn,direct&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">GOSUMDB&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;sum.golang.google.cn&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e"># 镜像仓库地址&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">HARBOR_REGISTRY&lt;/span>: &lt;span style="color:#ae81ff">example.harbor.com&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">IMAGE_NAME&lt;/span>: &lt;span style="color:#ae81ff">${HARBOR_REGISTRY}/devops/web-demo&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e"># 缓存目录&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">GOMODCACHE&lt;/span>: &lt;span style="color:#ae81ff">$CI_PROJECT_DIR/.cache/go-mod&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">GOCACHE&lt;/span>: &lt;span style="color:#ae81ff">$CI_PROJECT_DIR/.cache/go-build&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e"># ============================================================================&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e"># Stage 1: 服务构建&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e"># ============================================================================&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#f92672">build&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">stage&lt;/span>: &lt;span style="color:#ae81ff">build&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">image&lt;/span>: &lt;span style="color:#ae81ff">${HARBOR_REGISTRY}/devops/golang:1.26-trixie&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">variables&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">GOFLAGS&lt;/span>: &lt;span style="color:#e6db74">&amp;#34;-mod=mod&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">cache&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">key&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">files&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - &lt;span style="color:#ae81ff">go.sum&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">paths&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - &lt;span style="color:#ae81ff">.cache/go-mod/&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - &lt;span style="color:#ae81ff">.cache/go-build/&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">untracked&lt;/span>: &lt;span style="color:#66d9ef">true&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">before_script&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - &lt;span style="color:#ae81ff">mkdir -p ${GOMODCACHE} ${GOCACHE}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">script&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - &lt;span style="color:#ae81ff">go mod download&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - &lt;span style="color:#ae81ff">go build -o /dev/null ./... &lt;/span> &lt;span style="color:#75715e"># 只验证能否编译，不产出二进制&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - &lt;span style="color:#ae81ff">go vet ./...&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e"># ============================================================================&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e"># Stage 2: 单元测试&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e"># ============================================================================&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#f92672">test&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">stage&lt;/span>: &lt;span style="color:#ae81ff">test&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">image&lt;/span>: &lt;span style="color:#ae81ff">${HARBOR_REGISTRY}/devops/golang:1.26-trixie&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">cache&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">key&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">files&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - &lt;span style="color:#ae81ff">go.sum&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">paths&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - &lt;span style="color:#ae81ff">.cache/go-mod/&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - &lt;span style="color:#ae81ff">.cache/go-build/&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">untracked&lt;/span>: &lt;span style="color:#66d9ef">true&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">before_script&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - &lt;span style="color:#ae81ff">mkdir -p ${GOMODCACHE} ${GOCACHE}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">script&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - &lt;span style="color:#ae81ff">go mod download&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - &lt;span style="color:#ae81ff">go test -v -race -coverprofile=coverage.out ./...&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">artifacts&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">when&lt;/span>: &lt;span style="color:#ae81ff">always&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">expire_in&lt;/span>: &lt;span style="color:#ae81ff">1&lt;/span> &lt;span style="color:#ae81ff">week&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">reports&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">coverage_report&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">coverage_format&lt;/span>: &lt;span style="color:#ae81ff">cobertura&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">path&lt;/span>: &lt;span style="color:#ae81ff">coverage.out&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">paths&lt;/span>:
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> - &lt;span style="color:#ae81ff">coverage.out&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>配置说明&lt;/strong>：&lt;/p></description></item><item><title>GitLab CICD 入门指南</title><link>https://blogs.wuvikr.top/posts/gitlab-cicd-%E5%9F%BA%E7%A1%80%E5%85%A5%E9%97%A8/</link><pubDate>Thu, 22 Oct 2026 10:02:12 +0800</pubDate><guid>https://blogs.wuvikr.top/posts/gitlab-cicd-%E5%9F%BA%E7%A1%80%E5%85%A5%E9%97%A8/</guid><description>&lt;h1 id="1-简介">1. 简介&lt;/h1>
&lt;h2 id="11-gitlab-cicd-是什么">1.1 GitLab CI/CD 是什么？&lt;/h2>
&lt;p>GitLab CI/CD 可以理解为：GitLab 内置的持续集成 / 持续交付引擎。代码提交、合并请求、打标签或定时触发后，GitLab 会根据项目里的 &lt;code>.gitlab-ci.yml&lt;/code> 生成流水线，再交给 GitLab Runner 去执行构建、测试、扫描、打包、部署等任务。&lt;/p>
&lt;p>它的整体架构可以分成三层：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th style="text-align: center">&lt;strong>层级&lt;/strong>&lt;/th>
&lt;th style="text-align: center">&lt;strong>组件&lt;/strong>&lt;/th>
&lt;th style="text-align: center">&lt;strong>作用&lt;/strong>&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td style="text-align: center">控制层&lt;/td>
&lt;td style="text-align: center">GitLab Server&lt;/td>
&lt;td style="text-align: center">解析 &lt;code>.gitlab-ci.yml&lt;/code>，生成 Pipeline，分发 Job，展示结果&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: center">执行层&lt;/td>
&lt;td style="text-align: center">GitLab Runner&lt;/td>
&lt;td style="text-align: center">从 GitLab 拉取任务，执行脚本，回传日志和产物&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: center">环境层&lt;/td>
&lt;td style="text-align: center">Executor&lt;/td>
&lt;td style="text-align: center">决定 Job 跑在哪里，比如 Docker、Kubernetes、Shell、SSH 等&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>一个典型的 Gitlab CICD 执行链路如下所示：&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-plaintext" data-lang="plaintext">&lt;span style="display:flex;">&lt;span>开发者 push 代码或创建 MR，触发流水线
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ↓
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>GitLab 读取仓库里的 .gitlab-ci.yml，生成 Pipeline 流水线作业
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ↓
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Runner 轮询到待执行的任务，领取 Job
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ↓
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>Job 的 Tag 会和 Executor 的 Tag 进行匹配，匹配成功后，Executor 去执行 Job
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ↓
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>执行完成后，日志、制品、状态回传到 GitLab，在页面上可以查看结果
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="12-如何定义流水线">1.2 如何定义流水线？&lt;/h2>
&lt;p>一个流水线本质上由两个核心概念构成：&lt;/p></description></item></channel></rss>