从接触云原生开始,也过去有很久了,今年由于项目的原因,又荒废了,最近为了CKA的认证考试,所以打算继续整理和学习相关云原生的知识图谱。最近在腾讯云上搭建自己的Kubernetes集群,本文就是记录和整理一下,将自己的一些服务切换到Cloud Native 服务的过程和一些思考,欢迎交流。
整体我会从两个方面来阐述:
- Image:云原生应用的打包格式,Cloud Native的应用的运行实体都是一个个基础的Image,这里主要讨论Container Image Registry的选择,Image本身的知识不过多介绍,有兴趣可以看一下浅析Docker基于overlay的存储结构。
- Chart:云原生应用的部署、配置和管理。这里主要介绍Chart如何管理的问题,即Chart Registry的选择。至于Chart本身这里不过过多介绍。
Image
Image作为云原生应用的打包格式,它包含了一个应用运行所需的所有依赖:代码、运行时、库、环境变量和配置文件。
之前在容器化技术之Linux Namespace中,分析过Linux为了支持虚拟化在内核引入了Namespace和Cgroups,也在浅析Docker基于overlay的存储结构一文中介绍了Docker如何存储和管理Image的,这里不在深入介绍Image了。
Image流行的很多原因,最直观的来说:Image是打包和运行应用程序的好方法。随着云原生技术的发展,容器化越来越成为行业首选的技术方案,我觉得有一个很好的对Image的概括:
Build Once, Run anywhere.
Image让交付更简单,一次构建,可以在任何地方部署,依托registery,可以方便的进行发布和回滚,基于虚拟化环境,可以做到更好的环境隔离和高度的可移植性。如下:一个简单容器的构建和交付流程图:
如下是我的一个read it later服务器Dockerfile的内容,很简单吧
1 | FROM ubuntu:24.04 |
Image Registry
搭建自己的云原生服务,首先要解决的就是Image在存放在哪里,既然我们想对外提供服务,那么肯定是要选择公开的,稳定的Registry,针对我们个人开发者来说,能够免费就再好不过了。在此基础上,我希望的是最好能够保持功能独立,不过度整合其他的服务,不然如果需要迁移,可能耦合太多东西,会很麻烦。
Docker Hub
Docker Hub 是 Docker 官方提供的公共镜像仓库服务,它是目前全球最权威、规模最大的容器镜像集中地,你可以将其理解为一个类似于 GitHub 的代码托管平台,但专门用于存储和管理 Docker 镜像,准确说是符合OCI标准的Image。
Docker Hub提供公共和私有仓库,用于存储 Container Image,并支持在不同用户或团队之间共享,提供大量高质量的官方镜像(如 Ubuntu, Nginx, Redis)和社区贡献的镜像,作为开发和部署的基石。
如果我们的Docker dameon没有配置镜像加速地址的话,默认docker push/pull针对没有地址的Image的操作,都是直接去访问Docker Hub。如下可以看到本机docker dameon的默认Registry配置:
1 | $ sudo docker info |
如下是Docker Hub我的Image仓库列表:
Docker Hub针对个人免费的认证用户的限制相对比较严格,根据Docker Hub usage and limits,只能有1个Private Repo,拉取限制(200 次/6 小时)
| User type | Pull rate limit per 6 hours | Number of public repositories | Number of private repositories |
|---|---|---|---|
| Business (authenticated) | Unlimited | Unlimited | Unlimited |
| Team (authenticated) | Unlimited | Unlimited | Unlimited |
| Pro (authenticated) | Unlimited | Unlimited | Unlimited |
| Personal (authenticated) | 200 | Unlimited | Up to 1 |
| Unauthenticated users | 100 per IPv4 address or IPv6 /64 subnet | Not applicable | Not applicable |
Github Packages(GHCR)
GitHub Packages 是 GitHub 提供的一项软件包托管服务,它能让你在同一个地方管理源代码和软件包,支持多种主流的包管理工具,该说不说,GitHub是真的强,从Code Repo,到CI/CD,再到各个其他Pages,Packages的托管,只有你想不到的,很难他没有你想要的一些托管服务。
如下是目前Github支持的包管理registry的类型:
| 包管理工具类型 | 主要应用语言 |
|---|---|
npm registry |
Node.js 的包管理器 |
Container registry |
容器镜像的注册中心 |
Apache Maven registry |
Java 的项目管理和构建工具 |
RubyGems registry |
Ruby的包管理器 |
NuGet registry |
.NET的包管理器 |
Gradle registry |
Java (基于 Maven) 的构建自动化工具 |
这里我们只关注Container Registry,最开始Github针对容器镜像的Registry称为:Docker Registry,后来随着发展以及OCI标准的发展,改为了Container Registry,目前支持两类的容器镜像格式:
GitHub Package的管理页面在主页的Packages页签,例如我的:https://github.com/walkerdu?tab=packages
具体Container Registry的操作可以参考官方Docs,如下是push image的操作:
1 | ~> sudo docker tag 21e8325b0d3f ghcr.io/walkerdu/beancount |
目前看到GitHub的针对Container Registry的整体限制有两个:
- The Container registry has a 10 GB size limit for each layer.
- The Container registry has a 10 minute timeout limit for uploads.
GHCR 的存储和数据传输(下载/拉取)计入 GitHub Packages 的整体配额。公共镜像(public repositories)免费且无配额限制;私有镜像(private)有免费额度,超出后需付费或被阻塞(如果无有效支付方式)。配额因计划而异(免费、Pro、Team、Enterprise),具体见 About billing for GitHub Packages。像我是GitHub Free(大部分都是)用户,Private Image的额度就是500MB的存储和1GB的传输量。
针对Private Image,通过GitHub Actions的拉取不会算入传输流量的开销:
| Hosted | Self-Hosted | |
|---|---|---|
Access using a GITHUB_TOKEN |
Free | Free |
| Access using a personal access token | Free | Paid |
Tencent Image Registry
腾讯云的Image Registry提供了免费的个人版实例
每个用户的个人版镜像相关配额默认如下表,基本上针对个人开发者来说够用了
| 配额项 | 默认值 |
|---|---|
| 单地域下镜像命名空间 | 10 |
| 单地域下镜像仓库 | 广州地域 500,其他地域 100 |
| 单镜像下镜像版本 | 100 |
Helm Chart
介绍了Image和Image Registry后,我们再看一下Chart:预定义的 Kubernetes 资源包的集合,它描述了如何部署一个复杂应用。介绍Chart的前提,我们要先知道Helm,
Helm 是 Kubernetes 的包管理器(Package Manager),类似于 Linux 的 apt 或 yum,但专为 K8s 应用设计。
Chart 是 Helm 的“包”单位:一个预配置的 Kubernetes 资源模板集合,用于简化复杂应用的部署、管理和升级。
简单说:Chart 就像一个“应用安装包”,包含 YAML 文件、模板和默认值,帮助你一键部署如 WordPress、MySQL 或微服务集群,而无需手动编写所有 Kubernetes manifest。如下是官方文档针对Chart的定义:
Helm uses a packaging format called charts. A chart is a collection of files that describe a related set of Kubernetes resources. A single chart might be used to deploy something simple, like a memcached pod, or something complex, like a full web app stack with HTTP servers, databases, caches, and so on.
这里我们不详细介绍Chart的结构和细节,以下主要介绍Chart如何管理的问题,即Chart Registry的介绍和选择。
介绍Chart Registry选择之前,我们先看一下Helm是如何定义和组织Chart Registry的。这里Helm支持两种方式来进行Chart包的管理:
- Chart Repo:基于任何能够提供 YAML 和 tar 文件并响应 GET 请求的 HTTP 服务器。
- OCI Registry:从Helm3 开始的一项重大演进:将 Helm Chart 当作容器镜像一样存储在 OCI 注册表中(如 Docker Hub、GitHub Container Registry、Harbor 等),彻底改变了传统 Chart 仓库的托管方式。
Chart Repo
Helm提供了自建Chart仓库的能力。一个Chart仓库本质上是:一个HTTP服务,通过默认主页index.yaml文件提供可以选Chart包列表。从 Helm 2.2.0 版本开始,支持对仓库的客户端 SSL 认证。其他认证协议可以通过插件形式提供。
Chart仓库的组成部分包括:
- 一个
index.yaml的特殊文件:包含本仓库中所有Chart包的索引信息。 - Chart包集:通常来说,
index.yaml中列出的所有Chart包也托管于同仓库中,当然也可以不托管在一起。 - provenance 文件(.prov 后缀):可选的安全文件,包含 Chart 的数字签名和校验和(用 GPG 签名生成),防止 Chart 被篡改、伪造或中间人攻击,尤其适合企业级或开源共享场景。和我们常见的软件的.sig签名防串改类似。
例如,https://example.com/charts的仓库目录结构可能如下:
1 | charts/ |
上面示例中,index.yaml中应该包括一个 Alpine chart包,以及它的下载地址:https://example.com/charts/alpine-0.1.2.tgz,当然Chart包可以不和index.yaml托管在一个仓库,但是在一起是常见做法,且比较简单。如下示例:
1 | apiVersion: v1 |
由于 Chart 仓库可以是任何能够提供 YAML 和 tar 文件并响应 GET 请求的 HTTP 服务器,因此在托管自己的 Chart 仓库时,有大量选项。例如,可以使用 Google Cloud Storage (GCS) 桶、Amazon S3 桶、GitHub Pages,或者甚至创建自己的 Web 服务器。
OCI registry
从Helm3 开始,可以将 Helm Chart 当作容器镜像一样存储在 OCI 注册表中(如 Docker Hub、GitHub Container Registry、Harbor 等),彻底改变了传统 Chart 仓库的托管方式。Helmv3.8.0开始默认支持OCI注册表来管理Chart包。
我们先简单的了解一下什么是OCI,OCI(Open Container Initiative)开放容器标准是容器镜像格式、运行时、镜像分发的行业统一标准,让所有容器工具(Docker、containerd、Podman、CRI-O…)都能“说同一种语言”。OCI 的三大核心规范(Spec)
| 规范 | 官方名称 | 主要内容 | 参考实现 |
|---|---|---|---|
| Image | OCI Image Specification | 镜像层、manifest、config、层文件系统(rootfs) | Docker、BuildKit、containerd |
| Runtime | OCI Runtime Specification | create / start / kill / delete 等生命周期命令 | runC(Go 实现) crun(C 实现) |
| Distribution | OCI Distribution Specification | 镜像/artifact 的 上传/下载 API(/v2/) | Docker Registry v2、Harbor、GHCR、ECR、ACR |
我之前写的浅析Docker基于overlay的存储结构就是Docker符合OCI规范的Image的设计。关于三大核心规范官方GITHUB有接口规范的定义,可以参考;
Artifact(2019年发起,2024年正式加入OCI Distribution Spec)让 OCI 注册表能存任意二进制(Chart、WASM、SBOM、签名等),Helm OCI 正是基于此。Helm 3.8+ 让你把 Chart 当成 Docker 镜像一样,推送到 Docker Hub 或 GHCR,用 helm push 和 helm install oci://… 替代传统仓库,彻底告别 index.yaml 和静态服务器!
针对OCI Registry,Helm指令有些特殊限制:
helm push只能推送已经打包好的Chart包的.tgz格式文件,所以推送OCI Chart Registry的时候需要先进行helm package打包操作helm push向OCI registry推送Chart包的时候,必须指定oci://协议.
支持 oci:// 协议的helm subcommand如下,可以参考官方文档,后面在接受Chart Registry的GHCR使用的时候会列出使用过程
helm pullhelm pushhelm showhelm templatehelm installhelm upgrade
Chart Registry
下面开始介绍Helm Chart包的Chart Repo和OCI Registry两种包管理方式的具体实践。
GitHub Pages + Chart Releaser
Helm官方提供了通过 Chart Releaser Action 自动发布Chart包到Github Pages来实现Chart Repo的功能,我们来先看一下是如何操作的。
在你的GitHub组织下创建一个Git仓库。可以将其命名为
helm-charts,当然其他名称也可以接受。所有Chart包的资源都可以放在主分支的根目录下的/charts目录中。当然目录可以通过构建Action的构建参数指定,一般不用修改。还应该创建另一个分支
gh-pages用于发布Chart。这个分支的更改会通过Chart发布操作自动创建。同时可以创建一个gh-branch分支并添加README.md文件,当然这个不是必须的。在
helm-charts主分支创建 GitHub Actions workflow,可以命名为:.github/workflows/release.yml,内容如下:
1 | name: Release Charts |
这里需要配置一个GITHUB的PAT,用于Chart Releaser Action通过GitHub API来操作当前的helm-charts。默认我们提交主分支的charts目录包,就会自动触发Action,然后进行Chart的发布,Chart Releaser Action会调用Chart Releaser工具进行Chart包的上传和index.yaml文件的发布:
- 上传Chart Package到
gh-pages分支(可以通过pages_branch参数修改默认发布分支),默认是通过tag的方式进行发布,可以通过packages_with_index: true来修改发布方式为直接上传tgz Chart包到gh-pages发布分支,而不是通过tag来进行发布 - 更新
gh-pages发布分支的index.yaml,包含Chart Repo里最新的Chart包信息:版本号,签名,下载地址信息;
如下是我自己基于GitHub Pages搭建自己的Chart Repo仓库的过程:
- 在
helm-charts主分支的charts目录提交wecom-read-it-later服务的Chart包信息;
- 主分支下参考 Chart Releaser Action 的说明,创建了GitHub Actions workflow,上面的提交后触发了对应Action如下:
- 参考Chart Releaser+GitHub Page手册,添加Chart Repo,并进行Chart包的安装,如下:
1 | $ helm repo add walkerdu-charts https://walkerdu.github.io/helm-charts |
然后就可以通过Chart Repo来进行我们服务的安装啦,如下:
1 | $ helm install wecom-read-it-later walkerdu-charts/wecom-read-it-later --version 0.0.0-master.68e6cb48 |
这里需要注意,默认情况下是通过GitHub Release的方式进行Chart包的发布的,如果需要将Chart包托管到和index.yaml同样的目录,需要通过 Chart Releaser Action 的参数来修改:
packages_with_index: When you set this totrue, it will upload chart packages directly into publishing branch.
例如我的仓库的Actions配置如下:
1 | ... |
如下 Chart Releaser Action 会自动将Chart包发布托管到和index.yaml同样的目录:
Chart Releaser的README中也介绍了Usage with a private repository的情况下,必须要开启packages_with_index参数,将Chart包托管到和index.yaml同样的目录,否则Helm不支持拉取Chart包的时候的二次认证。
When using this tool on a private repository, helm is unable to download the chart package files. When you give Helm your username and password it uses it to authenticate to the repository (the index file). The index file then tells Helm where to get the tarball. If the tarball is hosted in some other location (Github Releases in this case) then it would require a second authentication (which Helm does not support). The solution is to host the files in the same place as your index file and make the links relative paths so there is no need for the second authentication.
#123 solve this by adding a
--packages-with-indexflag to the upload and index commands.
由于Chart包基本都涉及到一些重要的安全信息,例如key,token之类的,所以helm-charts仓库基本都要设置为私有的,设置后,GitHub Pages会自动失效,因为针对Free 账户,GitHub Pages不提供限制访问的功能,所以需要通过Helm Repo add的时候添加GitHub的PAT来认证私有Chart Repo的访问权限,如下:
1 | $ helm repo add walkerdu-charts https://x-access-token:[email protected]/walkerdu/helm-charts/gh-pages |
GitHub Packages(GHCR)
前面我们了解到,从Helm3 开始,可以将 Helm Chart 当作容器镜像一样存储在 OCI 注册表中(如 Docker Hub、GitHub Container Registry、Harbor 等),彻底改变了传统 Chart 仓库的托管方式。Helm v3.8.0开始默认支持OCI注册表来管理Chart包。
那么我们就可以在存储Image的Image Registry中进行Helm Chart包的托管,这里我们也介绍一下GitHub Packages服务支持的 Container Registry来进行Chart包的托管体验,如下:
1 | # 登录 |
如下推送完Chart包到GHCR中后,可以看到Chart包的列表如下:
ChartMuseum Repository Server
ChartMuseum是一个开源的 Helm Chart 仓库服务器(Helm Chart Repository Server),专为存储、管理和分发 Helm Charts 设计。它本质上是一个轻量级、Lightweight 的 HTTP 服务器,支持 Helm 官方的 Chart 仓库协议(index.yaml + .tgz 文件),让你轻松搭建私有或公开的 Helm Chart 注册表,类似 Docker Hub 但专为 Kubernetes 部署模板服务。
一句话定位:ChartMuseum = “Helm Charts 的 Docker Registry” —— 简单、可靠、可自托管。
按照ChartMuseum的介绍,目前ChartMuseum的后端存储已经支持如下的云服务商的存储,保证高可用和数据的可靠性:
ChartMuseum is an open-source Helm Chart Repository server written in Go (Golang), with support for cloud storage backends, including Google Cloud Storage, Amazon S3, Microsoft Azure Blob Storage, Alibaba Cloud OSS Storage, Openstack Object Storage, Oracle Cloud Infrastructure Object Storage, Baidu Cloud BOS Storage, Tencent Cloud Object Storage, DigitalOcean Spaces, Minio, and etcd.
例如官方文档支持Tencent的COS作为后端存储:
1 | chartmuseum --debug --port=8080 \ |
如下我们直接通过ChartMusuem Container来体验一下本地存储Chart包的托管模式:
1 | # Docker 运行 |
然后直接添加localhost:8080为Chart Repo,进行Chart包的管理:
1 | $ helm repo add chartmuseum-charts http://localhost:8080/ |
上面直接用curl进行Chart包的上传,是因为Helm 3针对非OCI协议,默认不支持push,我们可以用ChartMuseum提供的额外的插件:helm-push plugin,通过ChartMuseum的HTTP API进行Chart包的上传:
1 | $ helm cm-push wecom-read-it-later chartmuseum-charts |
我一开始会好奇作为Helm CLI,为什么需要额外的插件,而不是内置针对HTTP Chart Repo进行包的上传呢,Grok了一下,原来是有原因的:
Helm push (V3)能直接用于 OCI 是因为它复用了 Docker Registry 的标准协议并被官方内置;而 ChartMuseum 用的是自定义 HTTP API,官方不负责实现,所以只能靠插件
这里主要还是历史包袱:Helm 2 时代的“Chart Repo = 静态 HTTP 服务器”
- Helm 1/2 时代(2016–2019),Chart 仓库默认是静态文件服务器(如 GitHub Pages、S3 静态网站、Nginx)。
- 这些服务器 只支持 GET(下载 index.yaml 和 .tgz),天然不支持 POST/PUT 上传,HTTP push 协议碎片化,每个仓库实现都不一样。
- 因此,Helm CLI 从设计之初就只实现了 pull 逻辑(helm search, helm install),从未内置 push。
Image Registry和Chart Registry的选择
关于Image Registry的简单对比如下:目前我用的是Docker Hub,后面GitHub Pacages也同时用起来,如果需要多的Private Image,会考虑使用云厂商的服务,例如Tencent的,限制会少点。
| 方案 | 公有Image | 私有Image | 推荐度 |
|---|---|---|---|
| Docker Hub | 免费,无限制 | 免费1个,大小无限制 | ⭐⭐⭐⭐⭐ |
| GitHub Packages(GHCR) | 免费,无限制 | 免费个数不限,但是有总的500MB的存储和1GB的传输量 | ⭐⭐⭐⭐ |
| Tencent Image Registry | 个人版本,单镜像下镜像版本最多100个 | 和公有限制一样 | ⭐⭐⭐ |
关于Helm Chart Registry的简单对比总结如下:目前我选择用的是GitHub Pages,其实应该描述为GitHub Repo更为准确,因为Repo设置为Private后,Free Plan不支持Pages,不过不耽误Helm Chart包的托管了,其他的或多或少有点限制,相比GitHub Pages基本无大小和数量限制,且提供了可靠的存储,不用太操心。
| 方案 | 成本 | 易用性 |
|---|---|---|
| GitHub Pages | 免费,无限制 | ⭐⭐⭐⭐⭐ |
| GitHub Packages (GHCR) | public无限制,免费个人:private的个数不限,但是有总的500MB的存储和1GB的传输量 | ⭐⭐⭐⭐ |
| ChartMuseum | 自托管,存储可托管到云厂商的存储服务,都有些免费额度 | ⭐⭐ |
Artifact Hub
Artifact Hub 是一个开源的 Web 应用,专为云原生生态设计,由 CNCF(Cloud Native Computing Foundation,云原生计算基金会) 孵化项目维护(2024 年 5 月升级为 Incubating 阶段)。 它的核心定位是云原生包的发现、安装和发布平台,类似于 npm.js(JavaScript 包)或 PyPI(Python 包)的“应用商店”,但专注于 Kubernetes 和云原生工具。
它不直接托管文件(不像 Docker Hub 存储镜像),而是一个分布式元数据搜索引擎:从全球各种仓库(如 GitHub Pages、OCI 注册表)聚合包的元数据,提供统一的搜索、浏览和安装入口。 主要定位与功能
“为什么 Artifact Hub 不像 Docker Hub 那样做成『存储 + 搜索』一体?”
这正是 CNCF 生态设计哲学 和 现实工程权衡 的核心冲突。
容器镜像时代(2013-2015):
- Docker Hub 是先行者
- 当时没有成熟的分布式存储方案
- 集中式是最快的方式
云原生时代(2018+):
- 已有成熟的 Git、GitHub Pages、S3
- OCI 标准让任何地方都能托管镜像
- 不需要重新发明轮子
这里我感觉想的很美好,但是有点鸡肋,也许对K8s社区有一定的促进作用,但是现实做的好的产品都是一整套服务的,针对企业级用户不可能使用Artifact Hub来进行应用的管理的,针对ToC个人开发者倒是有很好的推广和交流。
