<?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>维克的博客</title><link>https://blogs.wuvikr.top/</link><description>Recent content 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/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>Trivy 使用笔记：从安装到生产实践</title><link>https://blogs.wuvikr.top/posts/trivy%E4%BD%BF%E7%94%A8%E7%AC%94%E8%AE%B0/</link><pubDate>Mon, 26 Oct 2026 10:41:08 +0800</pubDate><guid>https://blogs.wuvikr.top/posts/trivy%E4%BD%BF%E7%94%A8%E7%AC%94%E8%AE%B0/</guid><description>&lt;h1 id="1-trivy-是什么">1. Trivy 是什么&lt;/h1>
&lt;ul>
&lt;li>&lt;strong>官方网站：&lt;/strong> &lt;a href="https://trivy.dev/">https://trivy.dev/&lt;/a>&lt;/li>
&lt;li>&lt;strong>开源仓库：&lt;/strong> &lt;a href="https://github.com/aquasecurity/trivy">https://github.com/aquasecurity/trivy&lt;/a>&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>Trivy&lt;/strong>（读作 “tri-vee”）是由 &lt;strong>Aqua Security&lt;/strong> 主导开发并开源的&lt;a href="https://trivy.dev/">统一安全扫描工具&lt;/a>，名称取自 “Trigger”，寓意快速触发安全检查。它面向整个软件供应链，可用于扫描：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>容器镜像与文件系统&lt;/strong>&lt;/li>
&lt;li>&lt;strong>Git 仓库与依赖项&lt;/strong>&lt;/li>
&lt;li>&lt;strong>IaC 配置&lt;/strong>，如 Dockerfile、Terraform 和 Kubernetes 清单&lt;/li>
&lt;li>&lt;strong>SBOM&lt;/strong>&lt;/li>
&lt;li>&lt;strong>Kubernetes 集群及资源&lt;/strong>&lt;/li>
&lt;/ul>
&lt;p>借助统一的命令行入口，Trivy 能够发现：&lt;/p>
&lt;ul>
&lt;li>操作系统及应用依赖中的已知漏洞（CVE）&lt;/li>
&lt;li>容器、Kubernetes 和 IaC 配置错误&lt;/li>
&lt;li>硬编码密码、令牌等泄露密钥&lt;/li>
&lt;li>依赖项的许可证合规风险&lt;/li>
&lt;/ul>
&lt;p>Trivy 的优势在于将多种安全检查整合到一套工具中，并可轻松接入 CI/CD、DevSecOps 和 Kubernetes 等工作流，以结构化报告输出扫描结果。这样无需为不同类型的安全风险维护多套独立工具，就能在代码构建、制品发布和部署运行等阶段持续开展安全检查。&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-text" data-lang="text">&lt;span style="display:flex;">&lt;span>trivy &amp;lt;扫描目标&amp;gt; ─► 镜像 / 文件系统 / Git / IaC / SBOM / K8s
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ─► 漏洞 + 配置错误 + 密钥 + 许可证风险报告
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h1 id="2-为什么我们需要它">2. 为什么我们需要它&lt;/h1>
&lt;h2 id="21-供应链安全是真实风险">2.1 供应链安全是真实风险&lt;/h2>
&lt;p>服务的容器镜像不是凭空造的，它由一层层基础镜像 + 一堆依赖库叠加而成。任何一个基础镜像里的老漏洞（比如某个 glibc 漏洞）、任何一个 &lt;code>npm install&lt;/code> 下来的依赖带 CVE，都会&lt;strong>原封不动带进生产环境&lt;/strong>。&lt;/p></description></item><item><title>Kaniko 从入门到实践</title><link>https://blogs.wuvikr.top/posts/kaniko%E4%BB%8E%E5%85%A5%E9%97%A8%E5%88%B0%E5%AE%9E%E8%B7%B5/</link><pubDate>Sat, 24 Oct 2026 11:28:31 +0800</pubDate><guid>https://blogs.wuvikr.top/posts/kaniko%E4%BB%8E%E5%85%A5%E9%97%A8%E5%88%B0%E5%AE%9E%E8%B7%B5/</guid><description>&lt;h1 id="1-kaniko-是什么">1. Kaniko 是什么&lt;/h1>
&lt;p>&lt;strong>Kaniko&lt;/strong> 是一个由 Google 开源的，在&lt;strong>容器或 Kubernetes 集群内部&lt;/strong>，直接根据 Dockerfile 构建容器镜像的工具。&lt;/p>
&lt;p>它最核心的特点只有一个：&lt;strong>不依赖 Docker daemon（守护进程）&lt;/strong>，所有构建动作都在用户态（userspace）完成。这意味着你不需要在集群里跑一个 Docker 服务，也不需要挂载 &lt;code>/var/run/docker.sock&lt;/code>，就可以构建出标准镜像。&lt;/p>
&lt;h1 id="2-kaniko-解决了什么问题">2. Kaniko 解决了什么问题&lt;/h1>
&lt;p>在 Kaniko 出现之前，CI 里构建镜像的主流做法是 &lt;strong>Docker-in-Docker（DinD）&lt;/strong>。即在 CI Job 里再起一个 Docker 守护进程，用它去 build。这套方案在 Kubernetes 集群里有两个致命痛点：&lt;/p>
&lt;ol>
&lt;li>&lt;strong>需要 privileged 特权模式&lt;/strong>：dind 服务容器要操控内核特性，必须给 Pod 开 &lt;code>privileged: true&lt;/code>。这是很严重的安全隐患——容器可能逃逸到宿主机，很多安全策略严格的集群直接禁止。&lt;/li>
&lt;li>&lt;strong>性能差&lt;/strong>：每个 Job 都额外拉起一个完整 Docker daemon，启动慢、占用高，多任务并发时更是灾难。&lt;/li>
&lt;/ol>
&lt;p>Kaniko 就是为了解决这个问题而生的：&lt;strong>不用特权、不用 daemon，在标准 K8s 集群里就能安全构建镜像。&lt;/strong>&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>问题&lt;/th>
&lt;th>DinD（旧方案）&lt;/th>
&lt;th>Kaniko&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>需要 privileged 特权&lt;/td>
&lt;td>✅ 必须&lt;/td>
&lt;td>❌ 不需要&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>依赖 Docker daemon&lt;/td>
&lt;td>✅ 必须&lt;/td>
&lt;td>❌ 完全不需要&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>挂载 docker.sock&lt;/td>
&lt;td>✅ 需要&lt;/td>
&lt;td>❌ 不需要&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>安全性&lt;/td>
&lt;td>有容器逃逸风险&lt;/td>
&lt;td>纯用户态，安全&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>性能开销&lt;/td>
&lt;td>每个 Job 起 daemon，慢&lt;/td>
&lt;td>轻量，快&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>Kaniko 的核心价值用一句话来说就是：&lt;strong>在不安全、不方便跑 Docker daemon 的环境（尤其是 K8s 集群）里，仍然能安全、快速地构建镜像。&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><item><title>JavaScript 基础知识</title><link>https://blogs.wuvikr.top/posts/javascript%E5%9F%BA%E7%A1%80%E7%9F%A5%E8%AF%86/</link><pubDate>Tue, 20 Oct 2026 17:13:23 +0800</pubDate><guid>https://blogs.wuvikr.top/posts/javascript%E5%9F%BA%E7%A1%80%E7%9F%A5%E8%AF%86/</guid><description>&lt;h1 id="1-javascript-是什么">1. JavaScript 是什么&lt;/h1>
&lt;p>JavaScript（简称 JS）是一门&lt;strong>动态类型、多范式&lt;/strong>的编程语言。它最初为浏览器而生，如今已覆盖浏览器、服务器（Node.js）、桌面应用、移动端等环境。&lt;/p>
&lt;p>几个关键特征：&lt;/p>
&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;li>&lt;strong>基于原型&lt;/strong>：对象继承通过原型链实现，而非传统类继承（ES6 提供了 &lt;code>class&lt;/code> 语法糖）。&lt;/li>
&lt;/ul>
&lt;blockquote>
&lt;p>&lt;strong>注意&lt;/strong>：JavaScript 与 Java 是两门完全不同的语言，仅名字有历史渊源关系。&lt;/p>
&lt;/blockquote>
&lt;hr>
&lt;h1 id="2-运行环境">2. 运行环境&lt;/h1>
&lt;p>写 JS 代码前，需要一个运行环境。&lt;/p>
&lt;h2 id="21-浏览器环境">2.1 浏览器环境&lt;/h2>
&lt;p>打开任意浏览器（Chrome / Edge / Firefox），按 &lt;code>F12&lt;/code> 打开&lt;strong>开发者工具&lt;/strong>，切换到 &lt;strong>Console&lt;/strong>（控制台）标签，即可直接输入并运行 JS 代码：&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-javascript" data-lang="javascript">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#a6e22e">a&lt;/span>&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#e6db74">&amp;#34;zhangsan&amp;#34;&lt;/span>;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#a6e22e">console&lt;/span>.&lt;span style="color:#a6e22e">log&lt;/span>(&lt;span style="color:#e6db74">&amp;#34;Hello &amp;#34;&lt;/span>,&lt;span style="color:#a6e22e">a&lt;/span>);
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>也可以在 HTML 文件中通过 &lt;code>&amp;lt;script&amp;gt;&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-html" data-lang="html">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&amp;lt;!DOCTYPE html&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&amp;lt;&lt;span style="color:#f92672">html&lt;/span>&amp;gt;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&amp;lt;&lt;span style="color:#f92672">body&lt;/span>&amp;gt;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &amp;lt;&lt;span style="color:#f92672">script&lt;/span>&amp;gt;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">console&lt;/span>.&lt;span style="color:#a6e22e">log&lt;/span>(&lt;span style="color:#e6db74">&amp;#34;页面加载时执行&amp;#34;&lt;/span>);
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &amp;lt;/&lt;span style="color:#f92672">script&lt;/span>&amp;gt;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&amp;lt;/&lt;span style="color:#f92672">body&lt;/span>&amp;gt;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&amp;lt;/&lt;span style="color:#f92672">html&lt;/span>&amp;gt;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="22-nodejs-环境">2.2 Node.js 环境&lt;/h2>
&lt;p>&lt;a href="https://nodejs.org/">Node.js&lt;/a> 是一个免费、开源、跨平台的 JavaScript 运行时环境，它让开发人员能够创建服务器、 Web 应用、命令行工具和脚本。&lt;/p>
&lt;p>安装后，在终端执行：&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-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>node -v &lt;span style="color:#75715e"># 查看版本&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>node hello.js &lt;span style="color:#75715e"># 运行文件&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&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-javascript" data-lang="javascript">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">// hello.js
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>&lt;span style="color:#a6e22e">console&lt;/span>.&lt;span style="color:#a6e22e">log&lt;/span>(&lt;span style="color:#e6db74">&amp;#34;Hello from Node.js&amp;#34;&lt;/span>);
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>
&lt;blockquote>
&lt;p>&lt;strong>注意&lt;/strong>：浏览器和 Node.js 共享 ECMAScript 核心语法，但宿主 API 不同。浏览器有 &lt;code>window&lt;/code>、&lt;code>document&lt;/code>；Node.js 有 &lt;code>fs&lt;/code>、&lt;code>path&lt;/code>、&lt;code>process&lt;/code>。&lt;/p></description></item><item><title>Gitlab 备份恢复和版本升级</title><link>https://blogs.wuvikr.top/posts/gitlab%E5%A4%87%E4%BB%BD%E6%81%A2%E5%A4%8D%E5%92%8C%E7%89%88%E6%9C%AC%E5%8D%87%E7%BA%A7/</link><pubDate>Mon, 12 Oct 2026 19:32:50 +0800</pubDate><guid>https://blogs.wuvikr.top/posts/gitlab%E5%A4%87%E4%BB%BD%E6%81%A2%E5%A4%8D%E5%92%8C%E7%89%88%E6%9C%AC%E5%8D%87%E7%BA%A7/</guid><description>&lt;h1 id="1-gitlab-数据备份">1. gitlab 数据备份&lt;/h1>
&lt;h2 id="11-数据备份">1.1 数据备份&lt;/h2>
&lt;p>GitLab 提供一条命令 &lt;code>gitlab-backup create&lt;/code>，运行命令后会产出一个压缩 tar 归档，包含数据库、Git 仓库与 LFS、CI/CD artifacts、Wiki、附件上传、Pages、注册表等。但它&lt;strong>不包含&lt;/strong>配置文件、密钥、Redis 队列和对象存储，这些要另行处理。&lt;/p>
&lt;p>官方提示至少必须单独备份 &lt;code>/etc/gitlab/gitlab-secrets.json&lt;/code>（内含数据库加密密钥，丢失后无法解密两步验证和 CI 安全变量）与 &lt;code>/etc/gitlab/gitlab.rb&lt;/code>。建议连同 &lt;code>/etc/gitlab/trusted-certs&lt;/code>一起备份，避免整机恢复时出现中间人警告。&lt;/p>
&lt;h2 id="12-执行备份">1.2 执行备份&lt;/h2>
&lt;p>Omnibus 包安装的环境下，可以直接调用官方封装命令：&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-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>gitlab-backup create
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>归档默认生成在 &lt;code>/var/opt/gitlab/backups/&lt;/code>，文件名一般是这个样子： &lt;code>1730000000_2026_10_12_17.5_gitlab_backup.tar&lt;/code>。&lt;/p>
&lt;p>可以在 &lt;code>/etc/gitlab/gitlab.rb&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-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e"># 备份保留7天&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>gitlab_rails&lt;span style="color:#f92672">[&lt;/span>&lt;span style="color:#e6db74">&amp;#39;backup_keep_time&amp;#39;&lt;/span>&lt;span style="color:#f92672">]&lt;/span> &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">604800&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>gitlab_rails&lt;span style="color:#f92672">[&lt;/span>&lt;span style="color:#e6db74">&amp;#39;backup_path&amp;#39;&lt;/span>&lt;span style="color:#f92672">]&lt;/span> &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#e6db74">&amp;#34;/var/opt/gitlab/backups&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>修改后执行 &lt;code>gitlab-ctl reconfigure&lt;/code>。&lt;/p>
&lt;p>需要排除某类数据时用 &lt;code>SKIP&lt;/code>，例如跳过 CI 输出日志：&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-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>gitlab-backup create SKIP&lt;span style="color:#f92672">=&lt;/span>builds,artifacts
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="13-生产备份策略">1.3 生产备份策略&lt;/h2>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>策略&lt;/th>
&lt;th>做法&lt;/th>
&lt;th>为什么&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;strong>定时执行&lt;/strong>&lt;/td>
&lt;td>用 cron 每天/每周触发 &lt;code>gitlab-backup create&lt;/code>&lt;/td>
&lt;td>备份频率决定最多可丢失多少数据（RPO）&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>异地/对象存储&lt;/strong>&lt;/td>
&lt;td>把 tar 归档拷贝到另一台机器或对象存储（支持 S3 等 remote 配置）&lt;/td>
&lt;td>单机磁盘故障时本地归档同样会丢&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>配置密钥单独备&lt;/strong>&lt;/td>
&lt;td>定期打包 &lt;code>/etc/gitlab&lt;/code> 到加密存储&lt;/td>
&lt;td>密钥丢失=数据库无法解密，归档也无意义&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>定期恢复演练&lt;/strong>&lt;/td>
&lt;td>在测试机用备份实际恢复一次&lt;/td>
&lt;td>没验证能恢复的备份等于没有备份&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;div class="alert alert-tip" role="alert">
&lt;p class="alert-title">提示：&lt;/p></description></item><item><title>Gitlab 入门和生产部署实践</title><link>https://blogs.wuvikr.top/posts/gitlab%E5%85%A5%E9%97%A8%E5%92%8C%E7%94%9F%E4%BA%A7%E9%83%A8%E7%BD%B2%E5%AE%9E%E8%B7%B5/</link><pubDate>Mon, 12 Oct 2026 14:51:37 +0800</pubDate><guid>https://blogs.wuvikr.top/posts/gitlab%E5%85%A5%E9%97%A8%E5%92%8C%E7%94%9F%E4%BA%A7%E9%83%A8%E7%BD%B2%E5%AE%9E%E8%B7%B5/</guid><description>&lt;h1 id="1-什么是-gitlab">1. 什么是 GitLab&lt;/h1>
&lt;p>GitLab 是一款开源的 Git 代码托管平台，支持私有化部署，被众多企业用于搭建内部代码仓库。与 GitHub 主打开源协作不同，GitLab 更聚焦于代码托管和研发流程自动化，原生提供 CI/CD、代码评审、权限管控、镜像仓库、日志审计等能力。&lt;/p>
&lt;p>GitLab 主要分为两个版本：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>CE 社区版&lt;/strong>：免费开源，基本能满足 95% 中小企业的生产需求（本文案例）；&lt;/li>
&lt;li>&lt;strong>EE 企业版&lt;/strong>：付费使用，提供高可用架构、高级安全扫描、集群权限管控以及官方技术支持，比较适合大厂落地。&lt;/li>
&lt;/ul>
&lt;p>总之，对企业来说，GitLab 的核心价值主要体现在四个方面：&lt;strong>代码内网私有化存储、研发流程标准化、流水线自动化、操作可审计追溯&lt;/strong>。&lt;/p>
&lt;h1 id="2-三种部署方式选型">2. 三种部署方式（选型）&lt;/h1>
&lt;ol>
&lt;li>&lt;strong>Omnibus 包部署（生产首选）&lt;/strong>：官方推荐的标准生产部署方案，以一键安装包形式提供，支持主流 Linux 发行版。内置 PostgreSQL、Redis、Gitaly、Web、Sidekiq 等核心组件，部署路径清晰、运维成本较低、稳定性较高，是生产环境中最稳妥的选择。&lt;/li>
&lt;li>&lt;strong>K8s 混合部署（不推荐新人尝试）&lt;/strong>：面向大规模高并发场景时，官方推荐采用混合架构，将 Web、Sidekiq 等无状态组件部署在 Kubernetes 中，便于弹性扩缩；Gitaly、数据库、缓存等有状态服务则保留在虚拟机或独立节点上部署，以保证存储和数据的稳定性。不建议新手直接使用 Kubernetes 全组件部署 GitLab。有状态组件在容器环境中的存储管理、备份恢复和升级复杂度较高，生产风险较大。&lt;/li>
&lt;li>&lt;strong>Docker 单容器部署（仅测试用）&lt;/strong>：Docker 单容器部署启动快、上手简单，但在数据持久化、备份恢复、版本升级和高可用方面能力有限，仅适合本地测试、功能验证或演示环境，不建议用于生产。&lt;/li>
&lt;/ol>
&lt;h1 id="3-安装-gitlab">3. 安装 GitLab&lt;/h1>
&lt;p>&lt;a href="https://docs.gitlab.com/omnibus/installation/index.html">官方安装文档&lt;/a>&lt;/p>
&lt;h2 id="31-前置硬件系统要求">3.1 前置硬件&amp;amp;系统要求&lt;/h2>
&lt;ul>
&lt;li>仅支持 linux，最低配置：4核8G（10人以内小团队），生产推荐 8 核 16G 及以上；&lt;/li>
&lt;li>关闭 SELinux 或 AppArmor，否则可能影响服务正常运行；&lt;/li>
&lt;li>存储优先用 SSD；不建议用 NFS、EFS、Azure Files 这类网络文件系统放仓库数据；&lt;/li>
&lt;li>开放端口：80(http)、443(https)、22(ssh)。&lt;/li>
&lt;/ul>
&lt;h2 id="32-添加-apt-源安装">3.2 添加 apt 源安装&lt;/h2>
&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-bash" data-lang="bash">&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>sudo apt update &lt;span style="color:#f92672">&amp;amp;&amp;amp;&lt;/span> sudo apt upgrade -y
&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>sudo apt install -y curl openssh-server ca-certificates tzdata
&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"># 添加 GitLab 官方 APT 源&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>sudo curl --location &lt;span style="color:#e6db74">&amp;#34;https://packages.gitlab.com/install/repositories/gitlab/gitlab-ce/script.deb.sh&amp;#34;&lt;/span> | sudo bash
&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"># 安装 GitLab 社区版（默认启动初始化）&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>sudo apt install gitlab-ce -y
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="33-离线包无网络环境使用">3.3 离线包（无网络环境使用）&lt;/h2>
&lt;p>&lt;a href="https://packages.gitlab.com/gitlab/gitlab-ce">官方离线包下载地址&lt;/a>&lt;/p></description></item><item><title>ArgoCD 入门和生产实践指南</title><link>https://blogs.wuvikr.top/posts/argocd%E5%85%A5%E9%97%A8%E5%92%8C%E7%94%9F%E4%BA%A7%E5%AE%9E%E8%B7%B5%E6%8C%87%E5%8D%97/</link><pubDate>Sat, 10 Oct 2026 09:33:28 +0800</pubDate><guid>https://blogs.wuvikr.top/posts/argocd%E5%85%A5%E9%97%A8%E5%92%8C%E7%94%9F%E4%BA%A7%E5%AE%9E%E8%B7%B5%E6%8C%87%E5%8D%97/</guid><description>&lt;h1 id="1-什么是-argocd">1. 什么是 ArgoCD&lt;/h1>
&lt;p>&lt;a href="https://argo-cd.readthedocs.io/en/stable/">ArgoCD&lt;/a> 是 CNCF 毕业级项目，面向 Kubernetes 的&lt;strong>声明式 GitOps 持续交付工具&lt;/strong>。&lt;/p>
&lt;p>GitOps 核心思想：&lt;strong>Git 仓库作为应用配置的唯一可信源&lt;/strong>。ArgoCD 持续监听 Git 仓库内定义的应用目标状态，对比集群内资源的实际运行状态，自动完成应用部署、版本更新与故障回滚。&lt;/p>
&lt;p>ArgoCD 原生支持多种配置编排方式：Helm Chart、Kustomize、Jsonnet、Ksonnet，以及原生 YAML/JSON Kubernetes 资源清单。&lt;/p>
&lt;h1 id="2-架构原理">2. 架构原理&lt;/h1>
&lt;p>ArgoCD 以 Kubernetes 控制器形态运行，持续对应用做状态巡检：&lt;/p>
&lt;ol>
&lt;li>拉取 Git 仓库中声明的&lt;strong>期望状态（Target State）&lt;/strong>&lt;/li>
&lt;li>采集集群内资源&lt;strong>实际运行状态（Live State）&lt;/strong>&lt;/li>
&lt;li>对比两者差异，不一致时标记为 &lt;code>OutOfSync&lt;/code>（不同步）&lt;/li>
&lt;li>可视化展示资源差异，支持&lt;strong>手动同步 / 自动同步&lt;/strong>，将集群状态收敛到 Git 定义的目标状态&lt;/li>
&lt;/ol>
&lt;p>Git 仓库中修改清单后，ArgoCD 会感知变更并同步至目标集群，实现“改 Git 即发布”。&lt;/p>
&lt;p>具体工作流程参见下面的图片：
&lt;img alt="argocd工作流程图" loading="lazy" src="https://blogs.wuvikr.top/posts/argocd%E5%85%A5%E9%97%A8%E5%92%8C%E7%94%9F%E4%BA%A7%E5%AE%9E%E8%B7%B5%E6%8C%87%E5%8D%97/argocd%E5%B7%A5%E4%BD%9C%E6%B5%81%E7%A8%8B%E7%A4%BA%E6%84%8F%E5%9B%BE.png">&lt;/p>
&lt;p>&lt;strong>核心组件&lt;/strong>:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th style="text-align: center">组件&lt;/th>
&lt;th style="text-align: center">核心作用&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td style="text-align: center">&lt;code>argocd-server&lt;/code>&lt;/td>
&lt;td style="text-align: center">ArgoCD API 服务，提供 REST API、Web UI，处理认证鉴权&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: center">&lt;code>argocd-repo-server&lt;/code>&lt;/td>
&lt;td style="text-align: center">Git 仓库管理服务，缓存仓库代码、渲染 Helm/Kustomize 生成K8s清单&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: center">&lt;code>argocd-application-controller&lt;/code>&lt;/td>
&lt;td style="text-align: center">应用控制器，持续做状态比对，执行同步、健康检查&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: center">&lt;code>argocd-applicationset-controller&lt;/code>&lt;/td>
&lt;td style="text-align: center">ApplicationSet控制器，批量、模板化管理多环境/多集群 Application&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: center">&lt;code>argocd-redis&lt;/code>&lt;/td>
&lt;td style="text-align: center">缓存组件，存储仓库缓存、应用状态信息&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: center">&lt;code>argocd-dex-server&lt;/code>&lt;/td>
&lt;td style="text-align: center">可选，Dex 身份代理，对接OIDC、LDAP等外部身份源&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: center">&lt;code>argocd-notifications-controller&lt;/code>&lt;/td>
&lt;td style="text-align: center">可选，通知控制器，同步状态变更推送至邮件、Slack、Webhook等&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h1 id="3-部署">3. 部署&lt;/h1>
&lt;p>部署前建议查阅&lt;a href="https://argo-cd.readthedocs.io/en/stable/operator-manual/installation/#supported-versions">官方版本兼容矩阵&lt;/a>，选择与K8s集群匹配的ArgoCD版本。&lt;/p></description></item><item><title>Kubespray 部署和维护 K8s 集群指南</title><link>https://blogs.wuvikr.top/posts/kubespray%E9%83%A8%E7%BD%B2%E5%92%8C%E7%BB%B4%E6%8A%A4k8s%E9%9B%86%E7%BE%A4%E6%8C%87%E5%8D%97/</link><pubDate>Fri, 09 Oct 2026 11:34:13 +0800</pubDate><guid>https://blogs.wuvikr.top/posts/kubespray%E9%83%A8%E7%BD%B2%E5%92%8C%E7%BB%B4%E6%8A%A4k8s%E9%9B%86%E7%BE%A4%E6%8C%87%E5%8D%97/</guid><description>&lt;h1 id="1-简介">1. 简介&lt;/h1>
&lt;p>Kubespray 是 Kubernetes SIGs 官方维护的部署项目，本质是一套经过大量生产环境验证的 Ansible Playbook、Inventory 模板和角色集合。它不发明新的集群模型，而是把 kubeadm 引导、etcd 集群组建、CNI 安装、证书轮换、节点扩缩容与升级这些容易出错的步骤固化为可重复执行的代码。&lt;/p>
&lt;h2 id="11-kubespray-解决了什么问题">1.1 Kubespray 解决了什么问题&lt;/h2>
&lt;p>手动搭一个生产集群的工作量并不仅仅只是简单把 K8s 集群跑起来，而在于：&lt;/p>
&lt;ul>
&lt;li>etcd 节点集群的证书、成员管理、快照备份；&lt;/li>
&lt;li>多控制平面节点下 apiserver 的负载均衡与前端证书 SAN；&lt;/li>
&lt;li>kubelet 配置（cgroup 驱动、预留资源、镜像垃圾回收、最大 Pod 数）；&lt;/li>
&lt;li>CNI 插件与内核网络参数、iptables/nftables 后端的匹配；&lt;/li>
&lt;li>证书到期轮换、跨小版本顺序升级、节点下线清理。&lt;/li>
&lt;/ul>
&lt;p>这些工作做一次可以靠文档，做十次就得靠自动化。Kubespray 的价值就在于它把这些工程细节沉淀成了一套&lt;strong>符合生产环境最佳实践的可重复执行代码&lt;/strong>，你只需要在默认值的基础上少量改动，而不是自己编写和维护大量的脚本。&lt;/p>
&lt;h2 id="12-和其他工具对比">1.2 和其他工具对比&lt;/h2>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>工具&lt;/th>
&lt;th>Kubespray&lt;/th>
&lt;th>kubeadm&lt;/th>
&lt;th>Rancher&lt;/th>
&lt;th>OpenShift(OKD)&lt;/th>
&lt;th>kops&lt;/th>
&lt;th>KubeKey(kk)&lt;/th>
&lt;th>k3s&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;strong>核心定位&lt;/strong>&lt;/td>
&lt;td>Ansible驱动，生产级离线部署K8s集群&lt;/td>
&lt;td>K8s官方最小化集群引导工具&lt;/td>
&lt;td>企业级K8s全生命周期管理平台&lt;/td>
&lt;td>红帽出品，基于K8s的企业容器平台&lt;/td>
&lt;td>AWS专用K8s集群部署工具&lt;/td>
&lt;td>KubeSphere社区开源部署工具，中文友好&lt;/td>
&lt;td>Rancher出品轻量K8s发行版&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>底层引擎&lt;/strong>&lt;/td>
&lt;td>Ansible&lt;/td>
&lt;td>Go原生二进制&lt;/td>
&lt;td>容器+WebUI&lt;/td>
&lt;td>容器/Operator&lt;/td>
&lt;td>Go，调用AWS API&lt;/td>
&lt;td>Go二进制，封装kubeadm&lt;/td>
&lt;td>裁剪K8s单二进制&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>适用环境&lt;/strong>&lt;/td>
&lt;td>裸金属、VMware、私有云、离线环境&lt;/td>
&lt;td>裸金属/虚拟机/云主机，在线为主&lt;/td>
&lt;td>公有云、私有云、边缘，多集群统一管理&lt;/td>
&lt;td>企业私有/混合云，偏向生产企业&lt;/td>
&lt;td>&lt;strong>仅限AWS&lt;/strong>公有云&lt;/td>
&lt;td>裸机/虚拟机，私有化离线场景&lt;/td>
&lt;td>低配服务器、边缘、开发/小生产&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>离线部署&lt;/strong>&lt;/td>
&lt;td>✅ 完美支持，可打包离线介质&lt;/td>
&lt;td>⚠️ 支持但麻烦，需要手动下载镜像/二进制&lt;/td>
&lt;td>✅ 支持离线airgap安装&lt;/td>
&lt;td>✅ 官方提供离线方案&lt;/td>
&lt;td>❌ 依赖AWS云API，不适合离线&lt;/td>
&lt;td>✅原生支持打包离线包&lt;/td>
&lt;td>✅离线包&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>集群维护&lt;/strong>&lt;/td>
&lt;td>Ansible playbook升级、扩容、替换节点；版本升级稳定&lt;/td>
&lt;td>仅引导集群，&lt;strong>不负责后续集群运维&lt;/strong>，升级操作繁琐&lt;/td>
&lt;td>Web界面一键升级、扩缩容，多集群统一监控告警&lt;/td>
&lt;td>Operator管理集群，自动化运维能力强&lt;/td>
&lt;td>集群升级、节点替换由kops命令管理&lt;/td>
&lt;td>一键HA、扩容升级，可配套KubeSphere控制台&lt;/td>
&lt;td>一行命令运维，嵌入式etcd HA&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>高可用&lt;/strong>&lt;/td>
&lt;td>✅ 内置etcd三副本、控制面HA方案&lt;/td>
&lt;td>需要手动搭建HA（负载均衡+多master）&lt;/td>
&lt;td>✅ 自动支持HA，内置负载均衡方案&lt;/td>
&lt;td>✅ 默认HA架构&lt;/td>
&lt;td>✅ AWS上自动搭建控制面HA&lt;/td>
&lt;td>✅一键HA，内置负载均衡&lt;/td>
&lt;td>✅内置HA（嵌入式etcd）&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>网络插件&lt;/strong>&lt;/td>
&lt;td>内置Calico/Flannel/Cilium等多种CNI可选&lt;/td>
&lt;td>仅提供基础CNI对接，需自行部署CNI&lt;/td>
&lt;td>内置多种CNI，UI可视化配置&lt;/td>
&lt;td>默认OVN-Kubernetes，也可Calico&lt;/td>
&lt;td>默认Calico，可自定义CNI&lt;/td>
&lt;td>Calico等CNI可选&lt;/td>
&lt;td>Flannel默认，支持替换CNI&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>存储支持&lt;/strong>&lt;/td>
&lt;td>对接本地卷、Ceph、Longhorn等，ansible角色集成&lt;/td>
&lt;td>仅提供CSI标准，存储需要自己部署&lt;/td>
&lt;td>内置存储管理，支持Longhorn等&lt;/td>
&lt;td>内置CSI+本地存储方案&lt;/td>
&lt;td>对接AWS EBS等云存储&lt;/td>
&lt;td>对接Longhorn、Ceph、本地存储&lt;/td>
&lt;td>支持本地存储、CSI&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>版本适配&lt;/strong>&lt;/td>
&lt;td>紧跟K8s社区版本，滞后一小段时间&lt;/td>
&lt;td>K8s官方同步发布&lt;/td>
&lt;td>支持多K8s版本，统一纳管不同版本集群&lt;/td>
&lt;td>OKD版本独立，和上游K8s有差异&lt;/td>
&lt;td>紧跟AWS支持的K8s版本&lt;/td>
&lt;td>跟随上游kubeadm&lt;/td>
&lt;td>Rancher维护稳定版本&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>学习成本&lt;/strong>&lt;/td>
&lt;td>中等，需要懂Ansible&lt;/td>
&lt;td>低，上手简单，适合测试集群&lt;/td>
&lt;td>低，WebUI友好，运维门槛低&lt;/td>
&lt;td>高，体系庞大，有专属概念&lt;/td>
&lt;td>中等，熟悉AWS即可快速上手&lt;/td>
&lt;td>中等，中文文档友好&lt;/td>
&lt;td>很低，上手极快&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>典型场景&lt;/strong>&lt;/td>
&lt;td>裸金属机房、私有化项目、需要批量部署大量集群、等保离线环境&lt;/td>
&lt;td>实验室、POC测试、小规模简单集群&lt;/td>
&lt;td>多集群管理、混合云、需要统一权限/审计的企业&lt;/td>
&lt;td>大型企业生产环境，需要完整容器安全、合规能力&lt;/td>
&lt;td>AWS云上快速搭建销毁K8s集群&lt;/td>
&lt;td>中小企业裸机/VM离线部署，可选KubeSphere控制台&lt;/td>
&lt;td>资源有限、边缘、小生产集群&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>缺点&lt;/strong>&lt;/td>
&lt;td>Ansible串行执行，大规模集群部署慢；无WebUI；故障排查需要Ansible基础&lt;/td>
&lt;td>没有集群生命周期管理，生产HA搭建工作量大；缺少监控日志等附加组件&lt;/td>
&lt;td>资源开销大；底层K8s被封装，深度定制复杂；商业版收费&lt;/td>
&lt;td>闭源企业版昂贵；架构重，资源消耗高；学习曲线陡峭&lt;/td>
&lt;td>只能跑在AWS，无法裸金属/其他云使用&lt;/td>
&lt;td>依赖kubeadm；底层封装，深度排障需要kubeadm知识；KubeSphere额外耗资源&lt;/td>
&lt;td>裁剪部分组件，重型大规模生产不推荐；和标准K8s有细微差异&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;strong>国内中小企业热度&lt;/strong>&lt;/td>
&lt;td>⭐⭐⭐ 更多集成商使用，中小企业自用偏少&lt;/td>
&lt;td>⭐⭐⭐⭐⭐ 最高，几乎所有运维都学这个&lt;/td>
&lt;td>⭐⭐⭐⭐ 多集群场景热门&lt;/td>
&lt;td>⭐⭐ 大型企业为主，中小企业很少用&lt;/td>
&lt;td>⭐ 国内几乎不用&lt;/td>
&lt;td>⭐⭐⭐⭐ 私有化中小企业热门&lt;/td>
&lt;td>⭐⭐⭐⭐ 小集群/边缘场景热门&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h1 id="2-底层架构和设计">2. 底层架构和设计&lt;/h1>
&lt;p>&lt;strong>关键核心设计&lt;/strong>：&lt;/p></description></item><item><title>Harbor 私有镜像仓库安装部署和最佳实践</title><link>https://blogs.wuvikr.top/posts/harbor%E7%A7%81%E6%9C%89%E9%95%9C%E5%83%8F%E4%BB%93%E5%BA%93%E5%AE%89%E8%A3%85%E9%83%A8%E7%BD%B2%E5%92%8C%E6%9C%80%E4%BD%B3%E5%AE%9E%E8%B7%B5/</link><pubDate>Sat, 03 Oct 2026 11:32:44 +0800</pubDate><guid>https://blogs.wuvikr.top/posts/harbor%E7%A7%81%E6%9C%89%E9%95%9C%E5%83%8F%E4%BB%93%E5%BA%93%E5%AE%89%E8%A3%85%E9%83%A8%E7%BD%B2%E5%92%8C%E6%9C%80%E4%BD%B3%E5%AE%9E%E8%B7%B5/</guid><description>&lt;h1 id="1-简介">1. 简介&lt;/h1>
&lt;p>Harbor 是 CNCF 托管、VMware 开源的企业级容器镜像仓库，基于 Docker Registry 二次开发。除基础镜像存储能力外，还提供 Web UI、细粒度RBAC权限、镜像漏洞扫描、垃圾回收、镜像复制同步、审计日志等企业级能力，是 Kubernetes 集群离线部署、容器镜像统一管理的核心组件。&lt;/p>
&lt;p>&lt;strong>适用场景&lt;/strong>：企业内网私有镜像托管、Kubespray 离线集群镜像分发、CI/CD流水线镜像仓库。&lt;/p>
&lt;p>&lt;strong>官方参考文档&lt;/strong>：&lt;/p>
&lt;ul>
&lt;li>Harbor 官方安装指南：&lt;a href="https://goharbor.io/docs/2.8.0/install-config/">https://goharbor.io/docs/2.8.0/install-config/&lt;/a>&lt;/li>
&lt;li>Harbor HTTPS 证书配置文档：&lt;a href="https://goharbor.io/docs/main/install-config/configure-https/">https://goharbor.io/docs/main/install-config/configure-https/&lt;/a>&lt;/li>
&lt;li>Harbor 版本生命周期与版本策略：&lt;a href="https://goharbor.io/docs/main/install-config/versioning/">https://goharbor.io/docs/main/install-config/versioning/&lt;/a>&lt;/li>
&lt;li>Harbor 备份恢复官方文档：&lt;a href="https://goharbor.io/docs/main/administration/backup-restore/">https://goharbor.io/docs/main/administration/backup-restore/&lt;/a>&lt;/li>
&lt;li>Harbor 生产环境最佳实践：&lt;a href="https://www.cncf.io/blog/2026/02/24/making-harbor-production-ready-essential-considerations-for-deployment/">https://www.cncf.io/blog/2026/02/24/making-harbor-production-ready-essential-considerations-for-deployment/&lt;/a>&lt;/li>
&lt;li>Harbor 官方 Trivy 文档：https://goharbor.io/docs/2.14.0/scan/&lt;/li>
&lt;li>Trivy 官方文档：&lt;a href="https://aquasecurity.github.io/trivy/">https://aquasecurity.github.io/trivy/&lt;/a>&lt;/li>
&lt;/ul>
&lt;h1 id="2-前置环境准备">2. 前置环境准备&lt;/h1>
&lt;h2 id="21-harbor版本选型指南">2.1 Harbor版本选型指南&lt;/h2>
&lt;p>当前最新版已经到 v2.15.x，很多人会直接选用最新版，但&lt;strong>不建议生产直接上最新大版本&lt;/strong>。&lt;/p>
&lt;p>Harbor版本命名规则：&lt;code>主版本.次版本.补丁号&lt;/code>，例如 &lt;code>2.10.2&lt;/code>、&lt;code>2.15.0&lt;/code>&lt;/p>
&lt;ul>
&lt;li>&lt;strong>奇数小版本（2.11、2.13、2.15）&lt;/strong>：新特性版本，引入新功能、重构模块；bug相对多，适合测试体验，&lt;strong>不推荐生产直接使用&lt;/strong>。&lt;/li>
&lt;li>&lt;strong>偶数小版本（2.10、2.12、2.14）&lt;/strong>：长期维护稳定分支，重点修复漏洞，新增功能保守，&lt;strong>生产首选&lt;/strong>。&lt;/li>
&lt;/ul>
&lt;p>举例：&lt;/p>
&lt;ul>
&lt;li>&lt;code>v2.15.x&lt;/code>：新特性版，适合尝鲜、学习测试；&lt;/li>
&lt;li>&lt;code>v2.14.x&lt;/code>：当前最新稳定偶数版；&lt;/li>
&lt;li>&lt;code>v2.10.x&lt;/code>：久经大规模生产验证，bug少，社区问题资料极多，如果不需要新版本的一些新特性的话，推荐使用。&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>企业生产环境&lt;/strong>：优先选择&lt;strong>偶数LTS稳定分支&lt;/strong>，发布至少3个月以上，积累足够补丁。不要选用刚发布的 &lt;code>.0&lt;/code> 首版（例如 &lt;code>2.15.0&lt;/code>），这类版本会存在较多未发现的bug。&lt;/p>
&lt;h2 id="22-基础依赖">2.2 基础依赖&lt;/h2>
&lt;p>Harbor 基于 Docker Compose V2 编排运行，提前准备依赖环境：&lt;/p>
&lt;ul>
&lt;li>Docker Engine：20.10+&lt;/li>
&lt;li>Docker Compose V2&lt;/li>
&lt;li>系统架构：amd64 / arm64 均可支持（OrbStack、VMware虚拟机均可正常运行）&lt;/li>
&lt;li>硬件资源：测试环境≥2核4G；生产环境≥4核8G，推荐SSD存储提升镜像读写性能&lt;/li>
&lt;/ul>
&lt;h1 id="3-harbor-离线安装流程">3. Harbor 离线安装流程&lt;/h1>
&lt;p>离线包稳定性高于在线安装，内网、离线集群优先选用。本文部署以&lt;code>v2.14.4&lt;/code>版本为例。&lt;/p></description></item><item><title>Go 语言中的 Slice 类型</title><link>https://blogs.wuvikr.top/posts/go%E8%AF%AD%E8%A8%80%E4%B8%AD%E7%9A%84slice%E7%B1%BB%E5%9E%8B/</link><pubDate>Wed, 30 Sep 2026 15:55:22 +0800</pubDate><guid>https://blogs.wuvikr.top/posts/go%E8%AF%AD%E8%A8%80%E4%B8%AD%E7%9A%84slice%E7%B1%BB%E5%9E%8B/</guid><description>&lt;p>Slice（切片）是 Go 语言内置的&lt;strong>可变长度序列&lt;/strong>，属于引用类型，基于底层数组封装实现，&lt;strong>解决数组长度固定、数组传参完整拷贝开销大的问题&lt;/strong>，是 Go 开发最常用的数据结构之一。&lt;/p>
&lt;h1 id="1-slice-的基础特性">1. Slice 的基础特性&lt;/h1>
&lt;ul>
&lt;li>&lt;strong>可变长度&lt;/strong>：长度不固定，可通过 &lt;code>append&lt;/code> 动态追加元素。&lt;/li>
&lt;li>&lt;strong>底层数组共享&lt;/strong>：切片本身不存储实际元素，元素存放在底层数组；多个切片可以指向同一个底层数组，修改会互相影响。&lt;/li>
&lt;li>&lt;strong>len 与 cap 分离&lt;/strong>：&lt;code>len&lt;/code>代表切片当前可用元素个数；&lt;code>cap&lt;/code>代表底层数组允许的最大容量。&lt;/li>
&lt;li>&lt;strong>零值特性&lt;/strong>：切片零值为 &lt;code>nil&lt;/code>，&lt;code>nil slice&lt;/code> len=0、cap=0；&lt;code>nil slice&lt;/code> 和空切片 &lt;code>[]int{}&lt;/code> 不相等。&lt;/li>
&lt;li>&lt;strong>引用语义&lt;/strong>：切片变量存储的是结构体头信息，赋值、传参拷贝的是切片头，不是底层数组。&lt;/li>
&lt;li>&lt;strong>不可直接比较&lt;/strong>：切片不能使用 &lt;code>==&lt;/code> 判断两个切片内容是否相等，仅允许和 &lt;code>nil&lt;/code> 做相等判断。&lt;/li>
&lt;/ul>
&lt;div class="alert alert-tip" role="alert">
&lt;p class="alert-title">友情提示&lt;/p>
&lt;p>上述特性与 Slice 的底层实现紧密关联，建议结合第 4 节&amp;quot;Slice 底层实现原理&amp;quot;章节阅读，以获得更深入的理解。&lt;/p>
&lt;/div>
&lt;h1 id="2-slice-的四种初始化方式">2. Slice 的四种初始化方式&lt;/h1>
&lt;p>切片必须初始化，&lt;code>nil&lt;/code>切片可以读取、append，但不能直接对索引位置赋值。&lt;/p>
&lt;h2 id="21-声明未初始化nil-slice">2.1 声明未初始化（nil slice）&lt;/h2>
&lt;p>仅定义变量，底层数组指针为 nil，len=0、cap=0。&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-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">// 声明nil切片，未分配底层数组&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">var&lt;/span> &lt;span style="color:#a6e22e">a&lt;/span> []&lt;span style="color:#66d9ef">string&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#a6e22e">fmt&lt;/span>.&lt;span style="color:#a6e22e">Println&lt;/span>(&lt;span style="color:#a6e22e">a&lt;/span> &lt;span style="color:#f92672">==&lt;/span> &lt;span style="color:#66d9ef">nil&lt;/span>) &lt;span style="color:#75715e">// true&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#a6e22e">fmt&lt;/span>.&lt;span style="color:#a6e22e">Println&lt;/span>(len(&lt;span style="color:#a6e22e">a&lt;/span>), cap(&lt;span style="color:#a6e22e">a&lt;/span>)) &lt;span style="color:#75715e">// 0 0&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">// a[0] = &amp;#34;hello&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">// 未初始化的切片直接赋值会引发 panic&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">// panic: index out of range&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="22-字面量初始化空切片带初始值">2.2 字面量初始化（空切片/带初始值）&lt;/h2>
&lt;p>直接初始化，分配底层数组，支持空切片和预设元素。&lt;/p></description></item><item><title>Go 语言中的 Map 类型</title><link>https://blogs.wuvikr.top/posts/go%E8%AF%AD%E8%A8%80%E4%B8%AD%E7%9A%84map%E7%B1%BB%E5%9E%8B/</link><pubDate>Mon, 28 Sep 2026 13:01:24 +0800</pubDate><guid>https://blogs.wuvikr.top/posts/go%E8%AF%AD%E8%A8%80%E4%B8%AD%E7%9A%84map%E7%B1%BB%E5%9E%8B/</guid><description>&lt;p>Map 是 Go 语言内置的&lt;strong>键值对（key-value）哈希集合&lt;/strong>，属于引用类型，核心作用是通过唯一键快速索引对应值，查询、插入、删除的平均时间复杂度均为&lt;code>O(1)&lt;/code>，是 Go 开发中最常用的数据结构之一。&lt;/p>
&lt;h1 id="1-map-的基础特性">1. Map 的基础特性&lt;/h1>
&lt;ul>
&lt;li>&lt;strong>键唯一性&lt;/strong>：同一个 Map 中，key 唯一，重复插入会覆盖旧值。&lt;/li>
&lt;li>&lt;strong>键类型限制&lt;/strong>：key 必须是&lt;strong>可比较类型&lt;/strong>（int、string、bool、结构体等），切片、map、函数不可作为 key。&lt;/li>
&lt;li>&lt;strong>无序性&lt;/strong>：Map 不存储键值对插入顺序，遍历顺序随机（Go 1.12+ 每次遍历顺序不一致）。&lt;/li>
&lt;li>&lt;strong>零值特性&lt;/strong>：Map 零值为 &lt;code>nil&lt;/code>，&lt;code>nil map&lt;/code> 无法写入数据，仅可读取。&lt;/li>
&lt;li>&lt;strong>引用类型&lt;/strong>：赋值、传参仅传递引用，多个变量指向同一个底层数据&lt;/li>
&lt;/ul>
&lt;h1 id="2-map-的四种初始化方式">2. Map 的四种初始化方式&lt;/h1>
&lt;p>Go 语言 Map 必须初始化后才能写入数据，未初始化的&lt;code>nil map&lt;/code>直接写入会触发运行时 panic。以下是所有合法初始化方式。&lt;/p>
&lt;h2 id="21-声明未初始化nil-map">2.1 声明未初始化（&lt;code>nil map&lt;/code>）&lt;/h2>
&lt;p>仅定义变量，未分配内存，零值为&lt;code>nil&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-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#f92672">package&lt;/span> &lt;span style="color:#a6e22e">main&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">import&lt;/span> &lt;span style="color:#e6db74">&amp;#34;fmt&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:#66d9ef">func&lt;/span> &lt;span style="color:#a6e22e">main&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e">// 声明nil map，未初始化&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">var&lt;/span> &lt;span style="color:#a6e22e">m&lt;/span> &lt;span style="color:#66d9ef">map&lt;/span>[&lt;span style="color:#66d9ef">string&lt;/span>]&lt;span style="color:#66d9ef">int&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">fmt&lt;/span>.&lt;span style="color:#a6e22e">Println&lt;/span>(&lt;span style="color:#a6e22e">m&lt;/span> &lt;span style="color:#f92672">==&lt;/span> &lt;span style="color:#66d9ef">nil&lt;/span>) &lt;span style="color:#75715e">// 输出：true&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">// m[&amp;#34;a&amp;#34;] = 1&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e">// 报错：panic: assignment to entry in nil map&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="22-字面量初始化空-map带初始值">2.2 字面量初始化（空 Map/带初始值）&lt;/h2>
&lt;p>直接初始化并分配内存，支持空 Map 和预设键值对，最常用。&lt;/p></description></item><item><title>Go 语言中的字符串类型</title><link>https://blogs.wuvikr.top/posts/go%E8%AF%AD%E8%A8%80%E4%B8%AD%E7%9A%84%E5%AD%97%E7%AC%A6%E4%B8%B2%E7%B1%BB%E5%9E%8B/</link><pubDate>Fri, 25 Sep 2026 10:01:44 +0800</pubDate><guid>https://blogs.wuvikr.top/posts/go%E8%AF%AD%E8%A8%80%E4%B8%AD%E7%9A%84%E5%AD%97%E7%AC%A6%E4%B8%B2%E7%B1%BB%E5%9E%8B/</guid><description>&lt;p>Go 原生支持字符串类型，关键字&lt;code>string&lt;/code>用于表示字符串数据类型。&lt;/p>
&lt;h1 id="1-底层实现">1. 底层实现&lt;/h1>
&lt;p>Go 中的&lt;code>string&lt;/code>实际上是一个&lt;code>struct&lt;/code>类型，其本身并不真正存储字符串数据，仅是由一个指向底层数组的指针和字符串的长度字段组成，可以在标准库的&lt;code>reflect&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-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">// $GOROOT/src/reflect/value.go&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">// StringHeader是一个string的运行时表示&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">type&lt;/span> &lt;span style="color:#a6e22e">StringHeader&lt;/span> &lt;span style="color:#66d9ef">struct&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">Data&lt;/span> &lt;span style="color:#66d9ef">uintptr&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">Len&lt;/span> &lt;span style="color:#66d9ef">int&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>由于&lt;code>string&lt;/code>类型实际上只是一个对底层存储的引用，因此实际传参的开销非常小，另外在生成&lt;code>string&lt;/code>变量时，会同时存储字节长度&lt;code>len&lt;/code>，所以无论字符串长度怎么变化，获取长度的时间都是&lt;code>O(1)&lt;/code>。&lt;/p>
&lt;p>&lt;strong>底层实现图解&lt;/strong>：&lt;/p>
&lt;p>&lt;img alt="string的底层实现" loading="lazy" src="https://blogs.wuvikr.top/posts/go%E8%AF%AD%E8%A8%80%E4%B8%AD%E7%9A%84%E5%AD%97%E7%AC%A6%E4%B8%B2%E7%B1%BB%E5%9E%8B/string%E7%9A%84%E5%BA%95%E5%B1%82%E5%AE%9E%E7%8E%B0.png">&lt;/p>
&lt;p>&lt;strong>三个必须记住的特性&lt;/strong>：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>特性&lt;/th>
&lt;th>含义&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>不可变&lt;/td>
&lt;td>任何“修改”操作都会产生新字符串，原串不变&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>UTF-8 编码&lt;/td>
&lt;td>源码中的字符串字面量默认是 UTF-8&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>零值是 &lt;code>&amp;quot;&amp;quot;&lt;/code>&lt;/td>
&lt;td>不是 nil，&lt;code>var s string&lt;/code> 是空串&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&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-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">var&lt;/span> &lt;span style="color:#a6e22e">s&lt;/span> &lt;span style="color:#66d9ef">string&lt;/span> = &lt;span style="color:#e6db74">&amp;#34;Hello&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">// s[0] = &amp;#39;h&amp;#39; // 编译错误：cannot assign to s[0]&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#a6e22e">b&lt;/span> &lt;span style="color:#f92672">:=&lt;/span> []byte(&lt;span style="color:#a6e22e">s&lt;/span>) &lt;span style="color:#75715e">// 想修改就转成切片（会复制一份）&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>
&lt;blockquote>
&lt;p>注意：&lt;code>len(s)&lt;/code>返回的是字节数，不是字符数。中文、emoji 等多字节字符会让两者不相等。&lt;/p>
&lt;/blockquote>
&lt;h1 id="2-字面量与引号">2. 字面量与引号&lt;/h1>
&lt;p>Go 中初始化字符串变量，需要在双引号&lt;code>&amp;quot;&lt;/code>中定义值，单引号&lt;code>'&lt;/code>用于单个字符（ 以及 runes ）。&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-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">var&lt;/span> &lt;span style="color:#a6e22e">Name&lt;/span> &lt;span style="color:#66d9ef">string&lt;/span> = &lt;span style="color:#e6db74">&amp;#34;zhangsan&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#a6e22e">age&lt;/span> &lt;span style="color:#f92672">:=&lt;/span> &lt;span style="color:#e6db74">&amp;#34;18&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">var&lt;/span> &lt;span style="color:#a6e22e">a&lt;/span> &lt;span style="color:#a6e22e">char&lt;/span> = &lt;span style="color:#e6db74">&amp;#39;a&amp;#39;&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:#a6e22e">s1&lt;/span> &lt;span style="color:#f92672">:=&lt;/span> &lt;span style="color:#e6db74">&amp;#34;hello\nworld&amp;#34;&lt;/span> &lt;span style="color:#75715e">// 双引号：支持 \n \t \&amp;#34; \\ 等转义&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">// hello&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">// world&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:#a6e22e">s2&lt;/span> &lt;span style="color:#f92672">:=&lt;/span> &lt;span style="color:#e6db74">`hello\nworld`&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">// 输出：&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">// hello\nworld&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:#a6e22e">s3&lt;/span> &lt;span style="color:#f92672">:=&lt;/span> &lt;span style="color:#e6db74">&amp;#34;&amp;#34;&lt;/span> &lt;span style="color:#75715e">// 空串，len == 0&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Go 中没有也不支持三引号的写法，使用反引号来表示原生字面量。由于所见即所得，原样保留，可跨行的特点，常用于&lt;code>SQL/JSON/正则表达式&lt;/code>中&lt;/p></description></item><item><title>Go 语言中的循环控制</title><link>https://blogs.wuvikr.top/posts/go%E8%AF%AD%E8%A8%80%E4%B8%AD%E7%9A%84%E5%BE%AA%E7%8E%AF%E6%8E%A7%E5%88%B6/</link><pubDate>Wed, 23 Sep 2026 10:36:31 +0800</pubDate><guid>https://blogs.wuvikr.top/posts/go%E8%AF%AD%E8%A8%80%E4%B8%AD%E7%9A%84%E5%BE%AA%E7%8E%AF%E6%8E%A7%E5%88%B6/</guid><description>&lt;p>Go 语言只有一种循环构造方式，即 for 循环（&lt;strong>没有 while 或 do-while&lt;/strong>），类似于 C、Java 和 C# 之类的编程语言中的 for 循环。使用分号&lt;code>;&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-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">for&lt;/span> &lt;span style="color:#a6e22e">初始化语句&lt;/span>; &lt;span style="color:#a6e22e">条件表达式&lt;/span>; &lt;span style="color:#a6e22e">后置语句&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">循环体&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>执行顺序&lt;/strong>&lt;/p>
&lt;ol>
&lt;li>初始化语句：在循环开始前执行，且只执行一次（如 &lt;code>i := 0&lt;/code>）。&lt;/li>
&lt;li>条件表达式：在每次迭代前求值。如果为&lt;code>true&lt;/code>，进入循环体；如果为&lt;code>false&lt;/code>，循环直接结束。&lt;/li>
&lt;li>循环体：条件为真时执行。&lt;/li>
&lt;li>后置语句：在每次循环体执行结束后执行（如 &lt;code>i++&lt;/code>），然后回到第 2 步重新判断条件。&lt;/li>
&lt;/ol>
&lt;h1 id="1-基本语法">1. 基本语法&lt;/h1>
&lt;p>&lt;strong>for循环的三个组件&lt;/strong>：&lt;/p>
&lt;ul>
&lt;li>在第一次迭代之前执行的初始语句（可选）。&lt;/li>
&lt;li>在每次迭代之前计算的条件表达式，该条件为false时，循环会停止退出。&lt;/li>
&lt;li>在每次迭代结束时执行的后处理语句（可选）。&lt;/li>
&lt;/ul>
&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-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">func&lt;/span> &lt;span style="color:#a6e22e">main&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">sum&lt;/span> &lt;span style="color:#f92672">:=&lt;/span> &lt;span style="color:#ae81ff">0&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">for&lt;/span> &lt;span style="color:#a6e22e">i&lt;/span> &lt;span style="color:#f92672">:=&lt;/span> &lt;span style="color:#ae81ff">1&lt;/span>; &lt;span style="color:#a6e22e">i&lt;/span> &lt;span style="color:#f92672">&amp;lt;=&lt;/span> &lt;span style="color:#ae81ff">100&lt;/span>; &lt;span style="color:#a6e22e">i&lt;/span>&lt;span style="color:#f92672">++&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">sum&lt;/span> &lt;span style="color:#f92672">+=&lt;/span> &lt;span style="color:#a6e22e">i&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:#a6e22e">fmt&lt;/span>.&lt;span style="color:#a6e22e">Println&lt;/span>(&lt;span style="color:#e6db74">&amp;#34;sum of 1..100 is&amp;#34;&lt;/span>, &lt;span style="color:#a6e22e">sum&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h1 id="2-只使用表达式的-for-循环">2. 只使用表达式的 for 循环&lt;/h1>
&lt;p>某些编程语言中，可以使用 while 关键字编写循环，而 Go 没有 while 关键字。 但是可以改用 for 循环实现相同的效果。&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-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#f92672">package&lt;/span> &lt;span style="color:#a6e22e">main&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">import&lt;/span> (
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#e6db74">&amp;#34;fmt&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#e6db74">&amp;#34;math/rand&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#e6db74">&amp;#34;time&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>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">func&lt;/span> &lt;span style="color:#a6e22e">main&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">var&lt;/span> &lt;span style="color:#a6e22e">num&lt;/span> &lt;span style="color:#66d9ef">int64&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">rand&lt;/span>.&lt;span style="color:#a6e22e">Seed&lt;/span>(&lt;span style="color:#a6e22e">time&lt;/span>.&lt;span style="color:#a6e22e">Now&lt;/span>().&lt;span style="color:#a6e22e">Unix&lt;/span>())
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">for&lt;/span> &lt;span style="color:#a6e22e">num&lt;/span> &lt;span style="color:#f92672">!=&lt;/span> &lt;span style="color:#ae81ff">5&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">num&lt;/span> = &lt;span style="color:#a6e22e">rand&lt;/span>.&lt;span style="color:#a6e22e">Int63n&lt;/span>(&lt;span style="color:#ae81ff">15&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">fmt&lt;/span>.&lt;span style="color:#a6e22e">Println&lt;/span>(&lt;span style="color:#a6e22e">num&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;/code>&lt;/pre>&lt;/div>&lt;h1 id="3-无限循环和-break">3. 无限循环和 break&lt;/h1>
&lt;p>更进一步，当 for 循环连表达式都不编写的情况就是无限循环，这时想要退出循环必须使用 break 关键字。&lt;/p></description></item><item><title>K8s MetaLB 详解</title><link>https://blogs.wuvikr.top/posts/k8smetalb%E8%AF%A6%E8%A7%A3/</link><pubDate>Wed, 16 Sep 2026 13:59:57 +0800</pubDate><guid>https://blogs.wuvikr.top/posts/k8smetalb%E8%AF%A6%E8%A7%A3/</guid><description>&lt;p>在 Kubernetes 集群中，Service 是核心资源之一，其中 LoadBalancer 类型服务是业务对外暴露的核心方式。但原生 K8s 仅为公有云集群（AWS、阿里云、腾讯云等）提供 LoadBalancer 实现，裸金属、本地机房、私有部署的 K8s 集群默认不支持 LoadBalancer 类型 Service，只能依赖 NodePort、Ingress 实现流量暴露，存在端口散乱、运维复杂、无统一入口、负载均衡能力薄弱等问题。&lt;/p>
&lt;p>&lt;a href="https://metallb.io/">MetalLB&lt;/a> 正是这样一款专为私有/裸金属K8s集群设计的开源负载均衡组件，完全开源免费、轻量无侵入，完美补齐了私有 K8s 集群无原生 LoadBalancer 的短板，是本地机房 K8s 生产集群的标配组件。&lt;/p>
&lt;hr>
&lt;h1 id="1-项目介绍">1. 项目介绍&lt;/h1>
&lt;h2 id="11-项目定位与核心作用">1.1 项目定位与核心作用&lt;/h2>
&lt;p>MetalLB 是 Kubernetes官方认可的负载均衡解决方案，专为非云环境（裸金属服务器、虚拟机、本地私有集群）设计。其核心作用是：为集群内 LoadBalancer 类型的 Service 自动分配外部静态 IP，实现四层流量负载均衡，替代原生 NodePort 的低效暴露方式，让私有集群拥有与公有云一致的 Service 负载均衡能力。&lt;/p>
&lt;p>&lt;strong>核心价值：&lt;/strong>&lt;/p>
&lt;ul>
&lt;li>统一业务暴露方式：与公有云 K8s 开发规范对齐，无需适配 NodePort 特殊逻辑。&lt;/li>
&lt;li>稳定四层负载均衡：支持 TCP/UDP 协议，适配后端服务、数据库、中间件等各类业务。&lt;/li>
&lt;li>简化运维：固定服务外部 IP，无需节点端口映射，流量入口统一可控。&lt;/li>
&lt;li>轻量高性能：无复杂依赖，资源占用极低，不侵入 K8s 核心组件。&lt;/li>
&lt;/ul>
&lt;h2 id="12-架构原理">1.2 架构原理&lt;/h2>
&lt;p>MetalLB 采用控制器 + 转发节点的分布式架构，整体由两大核心组件组成，全部运行在集群内部，以 Pod 形式部署。&lt;/p>
&lt;h3 id="121-controller控制器组件">1.2.1 Controller（控制器组件）&lt;/h3>
&lt;p>全局唯一的调度组件，默认单副本部署，核心职责：&lt;/p>
&lt;ul>
&lt;li>监听集群内所有 LoadBalancer 类型的 Service 资源变更。&lt;/li>
&lt;li>维护 IP 地址池，为新的 LoadBalancer Service 自动分配可用外部 IP。&lt;/li>
&lt;li>回收废弃 Service 的 IP 资源，避免 IP 泄露与占用。&lt;/li>
&lt;li>校验 IP 分配合法性，保证全局唯一性。&lt;/li>
&lt;/ul>
&lt;h3 id="122-speaker转发组件">1.2.2 Speaker（转发组件）&lt;/h3>
&lt;p>集群节点级组件，默认每个集群节点部署一个Pod（DaemonSet部署），核心职责：&lt;/p></description></item><item><title>Pod 网络故障排查</title><link>https://blogs.wuvikr.top/posts/pod%E7%BD%91%E7%BB%9C%E6%95%85%E9%9A%9C%E6%8E%92%E6%9F%A5/</link><pubDate>Sat, 12 Sep 2026 15:51:54 +0800</pubDate><guid>https://blogs.wuvikr.top/posts/pod%E7%BD%91%E7%BB%9C%E6%95%85%E9%9A%9C%E6%8E%92%E6%9F%A5/</guid><description>&lt;p>Kubernetes 的网络是一个多层虚拟网络体系，流量从客户端到后端 Pod 的路径涉及多个组件。排查网络问题时，如果&amp;quot;东戳一下西戳一下&amp;quot;没有章法，可能需要耗费大量时间。如何最快的定位网络问题的根源？记住常见的具体现象和应对办法，遵循&lt;strong>从内到外&lt;/strong>的原则（先确认&amp;quot;自己没问题&amp;quot;，再确认&amp;quot;中间没问题&amp;quot;，最后确认&amp;quot;外面没问题&amp;quot;）或许能够帮你节约大量的时间，。&lt;/p>
&lt;p>下面按照从&lt;strong>具体访问现象&lt;/strong>出发的思路，由浅入深展开。&lt;/p>
&lt;hr>
&lt;h1 id="1-第一层pod-自身状态确认">1. 第一层：Pod 自身状态确认&lt;/h1>
&lt;p>&lt;strong>现象：Pod 完全无法访问，任何请求都无响应&lt;/strong>
这是最基础的一层。在排查任何网络问题之前，先确认 Pod 本身是否健康运行。&lt;/p>
&lt;h2 id="11-检查-pod-状态">1.1 检查 Pod 状态&lt;/h2>
&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-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>kubectl get pods -o wide
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>关注以下状态：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>状态&lt;/th>
&lt;th>含义&lt;/th>
&lt;th>可能原因&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>Running&lt;/code>&lt;/td>
&lt;td>正常运行&lt;/td>
&lt;td>正常&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>Pending&lt;/code>&lt;/td>
&lt;td>等待调度&lt;/td>
&lt;td>资源不足、节点亲和性不匹配&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>CrashLoopBackOff&lt;/code>&lt;/td>
&lt;td>反复崩溃重启&lt;/td>
&lt;td>应用启动失败、配置错误&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>OOMKilled&lt;/code>&lt;/td>
&lt;td>内存超限被杀&lt;/td>
&lt;td>&lt;code>resources.limits.memory&lt;/code> 设置过低&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>ImagePullBackOff&lt;/code>&lt;/td>
&lt;td>镜像拉取失败&lt;/td>
&lt;td>镜像名错误、仓库认证失败&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>ContainerCreating&lt;/code>&lt;/td>
&lt;td>容器创建中&lt;/td>
&lt;td>CNI 插件分配 IP 失败、存储卷挂载失败&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>如果 Pod 不在 &lt;code>Running&lt;/code> 状态，网络问题只是表象，根因在 Pod 本身：&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-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e"># 查看 Pod 详细事件&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>kubectl describe pod &amp;lt;pod-name&amp;gt; -n &amp;lt;namespace&amp;gt;
&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>kubectl logs &amp;lt;pod-name&amp;gt; -n &amp;lt;namespace&amp;gt;
&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>kubectl logs &amp;lt;pod-name&amp;gt; -n &amp;lt;namespace&amp;gt; --previous
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="12-确认-pod-有-ip-地址">1.2 确认 Pod 有 IP 地址&lt;/h2>
&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-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>kubectl get pods -o wide
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>如果 Pod 状态是 &lt;code>Running&lt;/code> 但 &lt;strong>IP 为空&lt;/strong>，说明 CNI 插件没有成功分配 IP。此时需要检查：&lt;/p></description></item><item><title>K8s 集群升级</title><link>https://blogs.wuvikr.top/posts/k8s%E9%9B%86%E7%BE%A4%E5%8D%87%E7%BA%A7/</link><pubDate>Wed, 09 Sep 2026 14:26:14 +0800</pubDate><guid>https://blogs.wuvikr.top/posts/k8s%E9%9B%86%E7%BE%A4%E5%8D%87%E7%BA%A7/</guid><description>&lt;p>Kubernetes 集群的大版本（跨次要版本）升级是一项复杂且高风险的运维操作。为了确保业务连续性，必须遵循严格的规范。以下是升级的核心原则、标准流程以及可能遇到的关键问题。&lt;/p>
&lt;h1 id="1-大版本升级的核心原则">1. 大版本升级的核心原则&lt;/h1>
&lt;ol>
&lt;li>&lt;strong>逐级递进，严禁跨版本&lt;/strong>：Kubernetes 不支持跨多个次要版本直接升级（例如不能从 1.24 直接升级到 1.26），必须按照&lt;code>1.24 → 1.25 → 1.26&lt;/code>的顺序逐版本进行。每一跳都需要完成控制面和节点的升级与验证。&lt;/li>
&lt;li>&lt;strong>组件版本兼容策略&lt;/strong>：
&lt;ul>
&lt;li>&lt;strong>控制面&lt;/strong>：&lt;code>kube-apiserver&lt;/code> 版本最高且需最先升级，&lt;code>controller-manager&lt;/code> 和 &lt;code>scheduler&lt;/code> 版本应小于或等于 &lt;code>apiserver&lt;/code>。&lt;/li>
&lt;li>&lt;strong>kubelet&lt;/strong>：节点上的 &lt;code>kubelet&lt;/code> 版本必须小于或等于 &lt;code>apiserver&lt;/code>，且不能低于 &lt;code>apiserver&lt;/code> 的前一个次要版本（例如 apiserver 为 v1.28 时，kubelet 可以是 v1.28 或 v1.27）。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;strong>先升级控制面，再升级节点&lt;/strong>：必须先完成 Master 节点（控制平面）的升级，确认稳定后再分批升级 Worker 节点。&lt;/li>
&lt;li>&lt;strong>第三方组件兼容性检查&lt;/strong>：升级前必须确认 CNI 插件（如 Calico/Cilium）、CoreDNS、kube-proxy 以及 Ingress 控制器等是否支持目标版本。通常建议先升级 CNI，CoreDNS 则与 K8s 版本强绑定，需查阅官方对应表。&lt;/li>
&lt;/ol>
&lt;h1 id="2-标准升级流程">2. 标准升级流程&lt;/h1>
&lt;ol>
&lt;li>&lt;strong>升级前准备&lt;/strong>：
&lt;ul>
&lt;li>检查集群健康状态，确保所有节点为 Ready，无 CrashLoopBackOff 的 Pod。&lt;/li>
&lt;li>&lt;strong>备份关键数据&lt;/strong>：必须备份 etcd 数据、证书文件及核心配置文件，以防升级失败导致数据丢失。&lt;/li>
&lt;li>检查并处理废弃 API（Deprecated APIs），高版本可能会移除旧版 API，导致 HPA 或 Ingress 等资源失效。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;strong>升级控制面&lt;/strong>：升级 &lt;code>kubeadm&lt;/code>，执行 &lt;code>kubeadm upgrade apply&lt;/code>，依次更新 &lt;code>kube-apiserver&lt;/code>、&lt;code>kube-controller-manager&lt;/code> 和 &lt;code>kube-scheduler&lt;/code>。&lt;/li>
&lt;li>&lt;strong>升级工作节点&lt;/strong>：
&lt;ul>
&lt;li>腾空节点（Cordon &amp;amp; Drain），将 Pod 驱逐到其他节点。&lt;/li>
&lt;li>升级节点的 &lt;code>kubeadm&lt;/code>、&lt;code>kubelet&lt;/code> 和 &lt;code>kubectl&lt;/code>。&lt;/li>
&lt;li>解除节点保护（Uncordon），使其重新加入调度。&lt;/li>
&lt;li>建议在生产环境中逐个节点离线升级后再上线，避免全部同时升级。&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ol>
&lt;h1 id="3-升级过程中可能遇到的问题">3. 升级过程中可能遇到的问题&lt;/h1>
&lt;ol>
&lt;li>&lt;strong>废弃 API 导致业务失效&lt;/strong>：高版本 K8s 会废弃旧版 API。例如升级到 1.22+ 时，&lt;code>networking.k8s.io/v1beta1&lt;/code> 的 Ingress 将不再可用；升级到 1.18+ 后，HPA 的 &lt;code>apiVersion&lt;/code> 必须切换为 &lt;code>apps/v1&lt;/code>，否则功能会失效。&lt;/li>
&lt;li>&lt;strong>容器运行时变更&lt;/strong>：从 Kubernetes 1.24 开始，不再支持将 Docker 作为内置容器运行时。若从 1.22/1.23 升级，必须提前将节点运行时从 Docker 迁移到 containerd，否则升级会失败。&lt;/li>
&lt;li>&lt;strong>节点排水（Drain）失败与 PDB 冲突&lt;/strong>：在驱逐 Pod 时，如果配置了 Pod Disruption Budget (PDB) 且当前可用副本数达到下限，Pod 将无法被驱逐，导致节点升级超时或失败。&lt;/li>
&lt;li>&lt;strong>网络连通性中断&lt;/strong>：如果 Pod 通过 &lt;code>LoadBalancer&lt;/code> 类型的 Service 访问同节点上的另一个 Pod，且 Service 的 &lt;code>externalTrafficPolicy&lt;/code> 设置为 &lt;code>Local&lt;/code>，在节点轮转升级后，两个 Pod 可能不再位于同一节点，导致网络不通。&lt;/li>
&lt;li>&lt;strong>自定义配置被覆盖&lt;/strong>：如果曾通过非产品化方式（如直接登录节点修改 kubelet 配置、打开 SWAP 分区等）更改过节点配置，升级过程中这些自定义配置可能会被重置或覆盖，甚至导致升级失败。&lt;/li>
&lt;li>&lt;strong>磁盘空间不足&lt;/strong>：升级过程需要下载软件包并可能产生临时文件。如果节点磁盘水位过高（建议预留至少 20% 空间），可能导致 Pod 被驱逐或升级失败。&lt;/li>
&lt;li>&lt;strong>Cgroup v1 兼容性问题&lt;/strong>：在较新的版本（如 1.36）中，kubelet 默认仅支持 Cgroup v2。如果节点操作系统仍在使用 Cgroup v1，可能会导致 kubelet 启动失败或节点 NotReady。&lt;/li>
&lt;/ol></description></item><item><title>Kubeadm 部署高可用 K8s 集群</title><link>https://blogs.wuvikr.top/posts/kubeadm%E9%83%A8%E7%BD%B2%E9%AB%98%E5%8F%AF%E7%94%A8k8s%E9%9B%86%E7%BE%A4/</link><pubDate>Wed, 09 Sep 2026 10:54:51 +0800</pubDate><guid>https://blogs.wuvikr.top/posts/kubeadm%E9%83%A8%E7%BD%B2%E9%AB%98%E5%8F%AF%E7%94%A8k8s%E9%9B%86%E7%BE%A4/</guid><description>&lt;p>&lt;a href="https://kubernetes.io/zh/docs/setup/production-environment/tools/kubeadm/">kubeadm 安装官方文档&lt;/a>&lt;/p>
&lt;h1 id="0-引言">0. 引言&lt;/h1>
&lt;p>Kubernetes 高可用架构的核心在于保障 API Server 的访问连续性与 etcd 的数据可靠性。通常通过部署多 Master 节点并前置负载均衡来实现，本方案采用 &lt;code>kube-vip&lt;/code> 承担此角色。为节省资源，本文将负载均衡与 etcd 集群复用部署于 Master 节点；&lt;strong>生产环境请务必将其独立部署，以确保集群稳定性。&lt;/strong>&lt;/p>
&lt;p>本文基于 Ubuntu 24.04 环境编写。若使用其他 Linux 发行版，请根据实际情况自行调整相关命令。&lt;/p>
&lt;hr>
&lt;h1 id="1-环境架构">1. 环境架构&lt;/h1>
&lt;h2 id="11-角色划分">1.1 角色划分&lt;/h2>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>主机名&lt;/th>
&lt;th style="text-align: center">IP&lt;/th>
&lt;th style="text-align: center">角色&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>master01&lt;/td>
&lt;td style="text-align: center">192.168.69.11&lt;/td>
&lt;td style="text-align: center">master01、Etcd-node01、kube-vip 节点&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>master02&lt;/td>
&lt;td style="text-align: center">192.168.69.12&lt;/td>
&lt;td style="text-align: center">master02、Etcd-node02、kube-vip 节点&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>master03&lt;/td>
&lt;td style="text-align: center">192.168.69.13&lt;/td>
&lt;td style="text-align: center">master03、Etcd-node03、kube-vip 节点&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>worker01&lt;/td>
&lt;td style="text-align: center">192.168.69.21&lt;/td>
&lt;td style="text-align: center">worker01&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>worker02&lt;/td>
&lt;td style="text-align: center">192.168.69.22&lt;/td>
&lt;td style="text-align: center">worker02&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>worker03&lt;/td>
&lt;td style="text-align: center">192.168.69.23&lt;/td>
&lt;td style="text-align: center">worker03&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>虚拟IP&lt;/td>
&lt;td style="text-align: center">10.0.0.100&lt;/td>
&lt;td style="text-align: center">kube-vip 提供&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h2 id="12-拓扑图">1.2 拓扑图&lt;/h2>
&lt;p>&lt;img alt="k8s集群架构拓扑" loading="lazy" src="https://blogs.wuvikr.top/posts/kubeadm%E9%83%A8%E7%BD%B2%E9%AB%98%E5%8F%AF%E7%94%A8k8s%E9%9B%86%E7%BE%A4/%E6%8B%93%E6%89%91%E5%9B%BE.png">&lt;/p>
&lt;hr>
&lt;h1 id="2-操作系统初始化">2. 操作系统初始化&lt;/h1>
&lt;h2 id="21-linux-基础配置">2.1 Linux 基础配置&lt;/h2>
&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-bash" data-lang="bash">&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>ufw disable
&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"># 关闭 swap&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>swapoff -a &lt;span style="color:#f92672">&amp;amp;&amp;amp;&lt;/span> sysctl -w vm.swappiness&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#ae81ff">0&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>sed -ri &lt;span style="color:#e6db74">&amp;#39;s/.*swap.*/#&amp;amp;/&amp;#39;&lt;/span> /etc/fstab
&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"># 配置主机名和 hosts&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>hostnamectl set-hostname &amp;lt;hostname&amp;gt;
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>cat &amp;gt;&amp;gt; /etc/hosts &lt;span style="color:#e6db74">&amp;lt;&amp;lt; EOF
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#e6db74">192.168.69.11 master01.wuvikr.top master01
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#e6db74">192.168.69.12 master02.wuvikr.top master02
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#e6db74">192.168.69.13 master03.wuvikr.top master03
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#e6db74">192.168.69.21 worker01.wuvikr.top worker01
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#e6db74">192.168.69.22 worker02.wuvikr.top worker02
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#e6db74">192.168.69.23 worker03.wuvikr.top worker03
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#e6db74">192.168.69.100 kubeapi.wuvikr.top kubeapi
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#e6db74">EOF&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"># 配置 limit&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>ulimit -SHn &lt;span style="color:#ae81ff">65535&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"># 持久化 limit 配置（重新登录后生效）&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>cat &amp;gt;&amp;gt; /etc/security/limits.conf &lt;span style="color:#e6db74">&amp;lt;&amp;lt; EOF
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#e6db74">* soft nofile 65536
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#e6db74">* hard nofile 131072
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#e6db74">* soft nproc 65535
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#e6db74">* hard nproc 655350
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#e6db74">* soft memlock unlimited
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#e6db74">* hard memlock unlimited
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#e6db74">* soft core unlimited
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#e6db74">* hard core unlimited
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#e6db74">EOF&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="22-时间同步">2.2 时间同步&lt;/h2>
&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-bash" data-lang="bash">&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>hwclock -s
&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>timedatectl set-timezone Asia/Shanghai
&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"># 确保时间同步服务已启用（Ubuntu 24.04 默认已启用）&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>systemctl enable --now systemd-timesyncd
&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>timedatectl status
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>
&lt;blockquote>
&lt;p>Ubuntu 24.04 内置的 &lt;code>systemd-timesyncd&lt;/code> 提供轻量级 NTP 客户端功能，支持自动同步、漂移补偿和状态查询。如需更高精度的时间同步（如金融、日志审计场景），建议改用 &lt;code>chrony&lt;/code> 替代 &lt;code>systemd-timesyncd&lt;/code>。&lt;/p></description></item><item><title>Terraform 状态文件管理</title><link>https://blogs.wuvikr.top/posts/terraform%E7%8A%B6%E6%80%81%E6%96%87%E4%BB%B6%E7%AE%A1%E7%90%86/</link><pubDate>Fri, 04 Sep 2026 16:08:53 +0800</pubDate><guid>https://blogs.wuvikr.top/posts/terraform%E7%8A%B6%E6%80%81%E6%96%87%E4%BB%B6%E7%AE%A1%E7%90%86/</guid><description>&lt;h1 id="1-为什么要把-state-搬到云上">1. 为什么要把 State 搬到「云上」&lt;/h1>
&lt;p>State 文件是 Terraform 的「账本」：它记录云上到底有哪些资源、各自的属性。本地存放 State 有三个硬伤：&lt;/p>
&lt;ul>
&lt;li>绑定机器：State 在 A 电脑上，B 电脑 apply 就会&amp;quot;失忆&amp;quot;，容易把资源重建一遍。&lt;/li>
&lt;li>无法协作：两个人同时改同一个环境，互相覆盖，状态文件乱套。&lt;/li>
&lt;li>安全隐患：本地文件易丢，而且里面可能含有明文密码等敏感信息，因此也不建议上传到 gitlab 等代码托管平台。&lt;/li>
&lt;/ul>
&lt;p>解决办法就是 Terraform 的 &lt;strong>Backend 机制&lt;/strong>：&lt;strong>用阿里云 OSS 存 State（集中、加密、可回滚），用 Tablestore 提供加锁（防止多人并发操作冲突）&lt;/strong>。&lt;/p>
&lt;h1 id="2-工作原理">2. 工作原理&lt;/h1>
&lt;p>每次执行 Terraform 命令，OSS Backend 都会走「加锁 → 干活 → 放锁」三步：&lt;/p>
&lt;pre tabindex="0">&lt;code class="language-mermaid" data-lang="mermaid">flowchart LR
A[terraform apply] --&amp;gt; B{从 Tablestore 拿 LockID}
B -- 已存在 --&amp;gt; C[报错：有人在操作]
B -- 不存在 --&amp;gt; D[生成并存入 LockID]
D --&amp;gt; E[读写 OSS 里的 State 文件]
E --&amp;gt; F[释放 LockID]
&lt;/code>&lt;/pre>&lt;ul>
&lt;li>&lt;strong>有锁&lt;/strong> → 说明有人正在操作，或上次操作异常退出，直接报错&lt;/li>
&lt;li>&lt;strong>无锁&lt;/strong> → 自动生成 LockID 存入 Tablestore，操作完成后更新 OSS 中的 State，再释放锁&lt;/li>
&lt;/ul>
&lt;h1 id="3-第一步创建存储--锁资源">3. 第一步：创建「存储 + 锁」资源&lt;/h1>
&lt;p>在正式使用前，要先准备一个 OSS Bucket 和一个 Tablestore 实例/表。它们本身也是云资源，所以也可以用 Terraform 自己建。&lt;/p></description></item><item><title>Terraform 云上存量资源导入</title><link>https://blogs.wuvikr.top/posts/terraform%E4%BA%91%E4%B8%8A%E8%B5%84%E6%BA%90%E5%AF%BC%E5%85%A5/</link><pubDate>Thu, 03 Sep 2026 16:24:03 +0800</pubDate><guid>https://blogs.wuvikr.top/posts/terraform%E4%BA%91%E4%B8%8A%E8%B5%84%E6%BA%90%E5%AF%BC%E5%85%A5/</guid><description>&lt;h1 id="1-为什么要导入云上资源">1. 为什么要&amp;quot;导入&amp;quot;云上资源？&lt;/h1>
&lt;p>很多团队都有这样的经历：最开始用控制台点几下、或者用 CLI 脚本把资源建好了，等资源越攒越多、想标准化管理时，发现这些资源&amp;quot;游离&amp;quot;在 Terraform 之外。&lt;/p>
&lt;p>Terraform 的管理逻辑很简单：&lt;strong>它只认自己 state 文件里记录的资源&lt;/strong>。你在控制台创建的资源，Terraform 根本&amp;quot;看不见&amp;quot;，也不会去动它们。所以要把存量资源管起来，第一步就是&amp;quot;导入&amp;quot;（import）——把云上已有的资源登记进 Terraform 的 state，再补上对应的配置代码。&lt;/p>
&lt;p>导入成功后，这些资源和 Terraform 新建的资源享受完全一样的待遇：能 plan、能 apply、能 diff、能进 CI/CD。&lt;/p>
&lt;h1 id="2-先搞清楚两个概念config-和-state">2. 先搞清楚两个概念：config 和 state&lt;/h1>
&lt;p>这是最容易绕晕的地方，务必先分清：&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th style="text-align: center">概念&lt;/th>
&lt;th style="text-align: center">是什么&lt;/th>
&lt;th style="text-align: center">谁来写&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td style="text-align: center">&lt;strong>&lt;code>.tf&lt;/code> 配置（config）&lt;/strong>&lt;/td>
&lt;td style="text-align: center">声明&amp;quot;想要什么&amp;quot;，是给人看的声明式代码&lt;/td>
&lt;td style="text-align: center">手写，或工具自动生成&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align: center">&lt;strong>&lt;code>terraform.tfstate&lt;/code>（state）&lt;/strong>&lt;/td>
&lt;td style="text-align: center">记录&amp;quot;现在实际是什么&amp;quot;，含真实属性（可能有敏感信息）&lt;/td>
&lt;td style="text-align: center">由 apply / import 自动生成&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>关键点：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>导入只会写 state，不会自动写 config&lt;/strong>（除非你用 &lt;code>-generate-config-out&lt;/code> 或 Terraformer）。&lt;/li>
&lt;li>所以完整流程 = &lt;strong>导入进 state + 补齐 config + plan 核对一致&lt;/strong>，三步缺一不可。&lt;/li>
&lt;li>state 含敏感信息，&lt;strong>永远不要提交到 git&lt;/strong>（后面最佳实践会讲远程存储）。&lt;/li>
&lt;/ul>
&lt;p>整体流程可以先看这张图：&lt;/p>
&lt;pre tabindex="0">&lt;code class="language-mermaid" data-lang="mermaid">flowchart TD
A[盘点存量资源: 类型 / ID / 地域] --&amp;gt; B[环境准备: Terraform + provider + RAM 凭证]
B --&amp;gt; C{资源量多少?}
C --&amp;gt;|少| D[import block / terraform import]
C --&amp;gt;|多| E[aliterraformer 批量导入]
D --&amp;gt; F[生成 config + state]
E --&amp;gt; F
F --&amp;gt; G[terraform plan 核对无漂移]
G --&amp;gt; H[清理只读属性 + 纳入 git + OSS 远程 state]
&lt;/code>&lt;/pre>&lt;h1 id="3-三种导入方式怎么选">3. 三种导入方式怎么选&lt;/h1>
&lt;p>&lt;a href="https://help.aliyun.com/zh/terraform/import-resources-into-terraform#63744434ecsis">阿里云官方文档&lt;/a>提供了三种方式，直接放对比表：&lt;/p></description></item><item><title>Terraform 内置函数</title><link>https://blogs.wuvikr.top/posts/terraform%E5%86%85%E7%BD%AE%E5%87%BD%E6%95%B0/</link><pubDate>Thu, 03 Sep 2026 16:06:29 +0800</pubDate><guid>https://blogs.wuvikr.top/posts/terraform%E5%86%85%E7%BD%AE%E5%87%BD%E6%95%B0/</guid><description>&lt;p>写 Terraform 配置的时候，你肯定遇到过这些场景：&lt;/p>
&lt;ul>
&lt;li>想把几个字符串拼起来&lt;/li>
&lt;li>想从一个列表里取第一个元素&lt;/li>
&lt;li>想根据条件选不同的值&lt;/li>
&lt;li>想读取一个 JSON 配置文件&lt;/li>
&lt;/ul>
&lt;p>Terraform 内置了 &lt;strong>100+ 个函数&lt;/strong>，覆盖了字符串处理、集合操作、类型转换、编码解码等各种需求。掌握它们，你的 HCL 代码会从&amp;quot;能跑&amp;quot;变成&amp;quot;优雅&amp;quot;。&lt;/p>
&lt;p>本文挑出&lt;strong>最常用的 20 个函数&lt;/strong>，每个都配上可直接运行的 demo，最后附上最佳实践。&lt;/p>
&lt;hr>
&lt;h1 id="1-字符串函数">1. 字符串函数&lt;/h1>
&lt;h2 id="11-format--格式化字符串">1.1. &lt;code>format()&lt;/code> — 格式化字符串&lt;/h2>
&lt;p>类似其他语言的 &lt;code>sprintf&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-hcl" data-lang="hcl">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">locals&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> env &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#e6db74">&amp;#34;prod&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> region &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#e6db74">&amp;#34;cn-hangzhou&amp;#34;&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"> # 输出: &amp;#34;prod-cn-hangzhou-01&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span> name &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#66d9ef">format&lt;/span>(&lt;span style="color:#e6db74">&amp;#34;%s-%s-01&amp;#34;&lt;/span>, &lt;span style="color:#66d9ef">local&lt;/span>.&lt;span style="color:#66d9ef">env&lt;/span>, &lt;span style="color:#66d9ef">local&lt;/span>.&lt;span style="color:#66d9ef">region&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="12-join--用分隔符拼接列表">1.2. &lt;code>join()&lt;/code> — 用分隔符拼接列表&lt;/h2>
&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-hcl" data-lang="hcl">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">locals&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> tags &lt;span style="color:#f92672">=&lt;/span> [&lt;span style="color:#e6db74">&amp;#34;web&amp;#34;, &amp;#34;nginx&amp;#34;, &amp;#34;frontend&amp;#34;&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"> # 输出: &amp;#34;web,nginx,frontend&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span> csv &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#66d9ef">join&lt;/span>(&lt;span style="color:#e6db74">&amp;#34;,&amp;#34;&lt;/span>, &lt;span style="color:#66d9ef">local&lt;/span>.&lt;span style="color:#66d9ef">tags&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="13-split--按分隔符拆分字符串">1.3. &lt;code>split()&lt;/code> — 按分隔符拆分字符串&lt;/h2>
&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-hcl" data-lang="hcl">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">locals&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> str &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#e6db74">&amp;#34;10.0.0.1,10.0.0.2,10.0.0.3&amp;#34;&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"> # 输出: [&amp;#34;10.0.0.1&amp;#34;, &amp;#34;10.0.0.2&amp;#34;, &amp;#34;10.0.0.3&amp;#34;]
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span> ips &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#66d9ef">split&lt;/span>(&lt;span style="color:#e6db74">&amp;#34;,&amp;#34;&lt;/span>, &lt;span style="color:#66d9ef">local&lt;/span>.&lt;span style="color:#66d9ef">str&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="14-replace--替换子串">1.4. &lt;code>replace()&lt;/code> — 替换子串&lt;/h2>
&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-hcl" data-lang="hcl">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">locals&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> name &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#e6db74">&amp;#34;my-project-dev&amp;#34;&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"> # 输出: &amp;#34;my-project-prod&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span> new &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#66d9ef">replace&lt;/span>(&lt;span style="color:#66d9ef">local&lt;/span>.&lt;span style="color:#66d9ef">name&lt;/span>, &lt;span style="color:#e6db74">&amp;#34;dev&amp;#34;, &amp;#34;prod&amp;#34;&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="15-trimprefix--trimsuffix--去掉前缀后缀">1.5. &lt;code>trimprefix()&lt;/code> / &lt;code>trimsuffix()&lt;/code> — 去掉前缀/后缀&lt;/h2>
&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-hcl" data-lang="hcl">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">locals&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> domain &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#e6db74">&amp;#34;www.example.com&amp;#34;&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"> # 输出: &amp;#34;example.com&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span> base &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#66d9ef">trimprefix&lt;/span>(&lt;span style="color:#66d9ef">local&lt;/span>.&lt;span style="color:#66d9ef">domain&lt;/span>, &lt;span style="color:#e6db74">&amp;#34;www.&amp;#34;&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="16-upper--lower--title--大小写转换">1.6. &lt;code>upper()&lt;/code> / &lt;code>lower()&lt;/code> / &lt;code>title()&lt;/code> — 大小写转换&lt;/h2>
&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-hcl" data-lang="hcl">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">locals&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> env &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#e6db74">&amp;#34;production&amp;#34;&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"> # 输出: &amp;#34;PRODUCTION&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span> tag &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#66d9ef">upper&lt;/span>(&lt;span style="color:#66d9ef">local&lt;/span>.&lt;span style="color:#66d9ef">env&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;hr>
&lt;h1 id="2-集合函数列表--映射--集合">2. 集合函数（列表 / 映射 / 集合）&lt;/h1>
&lt;h2 id="21-length--获取长度">2.1. &lt;code>length()&lt;/code> — 获取长度&lt;/h2>
&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-hcl" data-lang="hcl">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">locals&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> zones &lt;span style="color:#f92672">=&lt;/span> [&lt;span style="color:#e6db74">&amp;#34;cn-hangzhou-a&amp;#34;, &amp;#34;cn-hangzhou-b&amp;#34;, &amp;#34;cn-hangzhou-c&amp;#34;&lt;/span>]
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> count &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#66d9ef">length&lt;/span>(&lt;span style="color:#66d9ef">local&lt;/span>.&lt;span style="color:#66d9ef">zones&lt;/span>)&lt;span style="color:#75715e"> # 输出: 3
&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;/code>&lt;/pre>&lt;/div>&lt;h2 id="22-element--按索引取值会循环">2.2. &lt;code>element()&lt;/code> — 按索引取值（会循环）&lt;/h2>
&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-hcl" data-lang="hcl">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">locals&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> zones &lt;span style="color:#f92672">=&lt;/span> [&lt;span style="color:#e6db74">&amp;#34;a&amp;#34;, &amp;#34;b&amp;#34;&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"> # 索引 2 超出长度，自动取模 → 回到索引 0
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e"> # 输出: &amp;#34;a&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span> zone &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#66d9ef">element&lt;/span>(&lt;span style="color:#66d9ef">local&lt;/span>.&lt;span style="color:#66d9ef">zones&lt;/span>, &lt;span style="color:#ae81ff">2&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>
&lt;blockquote>
&lt;p>💡 和直接 &lt;code>local.zones[2]&lt;/code> 不同，&lt;code>element()&lt;/code> 不会报错，适合循环创建资源时分配可用区。&lt;/p></description></item><item><title>Terrafrom 基础知识</title><link>https://blogs.wuvikr.top/posts/terraform%E5%9F%BA%E7%A1%80/</link><pubDate>Thu, 03 Sep 2026 12:37:52 +0800</pubDate><guid>https://blogs.wuvikr.top/posts/terraform%E5%9F%BA%E7%A1%80/</guid><description>&lt;h1 id="1-简介">1. 简介&lt;/h1>
&lt;p>&lt;a href="https://developer.hashicorp.com/terraform/intro">Terraform&lt;/a> 是一款&lt;strong>开源的“基础设施即代码（Infrastructure as Code，IaC）”工具&lt;/strong>，由 HashiCorp 公司开发（创始人为 Mitchell Hashimoto）。它的核心作用是用人类可读的配置文件来安全、高效地构建、变更和版本化管理云上和本地的各类基础设施。&lt;/p>
&lt;p>它的主要特点包括：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>基础设施即代码&lt;/strong>：用高阶配置语法描述基础设施，便于版本控制、复用和共享。&lt;/li>
&lt;li>&lt;strong>声明式语言&lt;/strong>：用户只需描述期望的基础设施最终状态，工具自动处理依赖关系和部署顺序；配置语言使用 HCL（也支持 JSON）。&lt;/li>
&lt;li>&lt;strong>执行计划&lt;/strong>：在真正执行前会生成操作计划，避免意外变更。&lt;/li>
&lt;li>&lt;strong>资源图&lt;/strong>：构建资源依赖图并并行处理相互独立的资源。&lt;/li>
&lt;li>&lt;strong>多云支持&lt;/strong>：可与 AWS、Azure、Google Cloud、阿里云等主流云平台集成。&lt;/li>
&lt;/ul>
&lt;p>典型的工作流程包括初始化（&lt;code>terraform init&lt;/code>）、生成计划（&lt;code>terraform plan&lt;/code>）和应用（&lt;code>terraform apply&lt;/code>）三个阶段。Terraform 侧重于定义和管理基础设施结构，常与 Ansible 等侧重于配置管理的工具搭配使用。&lt;/p>
&lt;h1 id="2-核心概念">2. 核心概念&lt;/h1>
&lt;h2 id="21-基础架构即代码-iac">2.1. 基础架构即代码 (IaC)&lt;/h2>
&lt;p>Terraform 允许使用配置文件（而非手动操作或脚本）来定义和管理基础设施。这使基础设施可以像应用程序代码一样进行版本控制、测试和协作。&lt;/p>
&lt;h2 id="22--hcl">2.2 HCL&lt;/h2>
&lt;p>HCL 全称 HashiCorp Configuration Language，是 Terraform 使用的一种特定于域的专用语言，专为基础设施管理设计：&lt;/p>
&lt;ul>
&lt;li>语法简单易学，比 JSON 和 ARM 模板更直观&lt;/li>
&lt;li>支持变量、条件表达式和函数&lt;/li>
&lt;li>提供类型安全和 IntelliSense 支持（在 VS Code 等编辑器中）&lt;/li>
&lt;/ul>
&lt;h2 id="23-providers">2.3. Providers&lt;/h2>
&lt;p>&lt;a href="https://registry.terraform.io/browse/providers">Provider&lt;/a> 是云厂商提供并维护的一个能与云厂商 API 进行交互的插件，其中包含相关资源和数据源对象。Terraform 可以通过 Provider 来管理基础设施。常用的 Providers 有：&lt;/p>
&lt;ul>
&lt;li>AzureRM：管理 Azure 资源&lt;/li>
&lt;li>AWS：管理 AWS 服务&lt;/li>
&lt;li>Kind：使用 Kind 管理 kubernetes 集群&lt;/li>
&lt;li>Docker：管理容器资源&lt;/li>
&lt;li>其他支持的云平台和 SaaS 服务&lt;/li>
&lt;/ul>
&lt;h2 id="24-状态文件-state-file">2.4. 状态文件 (State File)&lt;/h2>
&lt;p>Terraform 会跟踪基础设施的当前状态，包括，组件配置，资源之间的依赖关系等，通过比对配置文件与状态文件，确定需要实施哪些变更。&lt;/p></description></item></channel></rss>