2. OpenShift镜像仓库架构解析

在OpenShift中,镜像仓库并不是一个孤立的外部依赖,而是被深度集成进平台内部的一等公民。理解OpenShift镜像仓库架构,核心要抓住四个对象:内部Registry、ImageStream、ImageStreamTag以及它们与镜像拉取路径之间的关系。很多初学者会把ImageStream简单理解为“镜像的别名”,这会导致后续在构建、部署和治理时出现理解偏差。本节从实际对象和拉取路径出发,把这套架构讲清楚。

内部Registry:集群自带的镜像存储服务

OpenShift默认在openshift-image-registry命名空间中部署一个内部镜像仓库,通常以image-registry这个Deployment和Service形式存在,对外暴露的地址形如image-registry.openshift-image-registry.svc:5000。它本质上是一个符合Docker Registry V2规范的仓库服务,但被OpenShift接管了认证与路由。

内部Registry有两个关键特性。第一,它按命名空间隔离:每个项目(Namespace)在Registry中对应一个独立的仓库路径前缀,例如image-registry.openshift-image-registry.svc:5000/myproject/myapp。第二,它与集群认证打通:Pod拉取镜像时使用ServiceAccount绑定的system:image-puller权限,而不是在Secret里硬编码用户名密码。这意味着镜像拉取凭证由OpenShift自动注入,运维人员不需要手动维护imagePullSecrets。

内部Registry的存储后端可以是emptyDir(仅测试)、PVC、S3、GCS、Swift等。生产环境通常配置对象存储,因为镜像层是只读且可共享的,对象存储比块存储更经济。可以通过以下命令查看当前Registry的存储配置:

oc get configs.imageregistry.operator.openshift.io cluster -o yaml
# 关注 spec.storage 字段,例如:
# spec:
#   storage:
#     s3:
#       bucket: ocp-image-registry
#       region: cn-north-1

ImageStream:镜像的逻辑视图与标签追踪器

ImageStream是OpenShift对镜像的一种抽象。它本身不存储镜像数据,而是记录“某个镜像仓库路径下有哪些标签、这些标签当前指向哪个镜像ID”。可以把它理解为一个镜像标签的观察窗口。当外部镜像仓库中的标签发生变化时,ImageStream可以自动或手动同步这个变化。

ImageStream最重要的价值在于解耦构建与部署。假设构建流水线每次推送镜像时都生成新标签,如果Deployment直接引用具体标签,就需要不断修改YAML。而使用ImageStream后,Deployment可以引用ImageStreamTag,当ImageStreamTag指向的镜像ID更新时,OpenShift可以触发一次新的部署(通过ImageChange触发器)。

创建一个ImageStream的示例如下:

apiVersion: image.openshift.io/v1
kind: ImageStream
metadata:
  name: myapp
  namespace: demo
spec:
  lookupPolicy:
    local: true

其中lookupPolicy.local: true表示当Pod引用demo/myapp:latest这类短名称时,OpenShift会优先在本地ImageStream中解析,而不是去外部Registry查找。这是内部镜像拉取路径能够生效的关键配置。

ImageStreamTag与ImageStreamImage:标签与镜像ID的映射

ImageStreamTag是ImageStream下的具体标签对象,例如myapp:latest、myapp:v1.2。它记录了该标签当前指向的镜像ID(一个sha256摘要)。而ImageStreamImage则是以镜像ID为索引的对象,格式为myapp@sha256:...。两者的关系是:ImageStreamTag是“可变的指针”,ImageStreamImage是“不可变的快照”。

查看一个ImageStreamTag的实际指向:

oc get is myapp -n demo -o yaml
# 输出中 status.tags 部分类似:
# status:
#   tags:
#   - tag: latest
#     items:
#     - dockerImageReference: image-registry.openshift-image-registry.svc:5000/demo/myapp@sha256:abc123...
#       image: sha256:abc123...

这里可以看到,ImageStreamTag最终解析出的dockerImageReference指向内部Registry的地址。这就是OpenShift内部镜像拉取路径的起点。

镜像拉取路径:从Pod到内部Registry的完整链路

当Pod引用一个ImageStreamTag时,OpenShift的准入控制器会介入,把ImageStreamTag解析为具体的镜像ID,并改写Pod的image字段。整个拉取路径可以拆成几个阶段:

  1. Pod创建请求:用户提交Deployment,Pod模板中image字段写的是demo/myapp:latest。
  2. ImageStream解析:OpenShift的ImageStream解析器根据lookupPolicy,在demo命名空间中找到myapp:latest对应的ImageStreamTag。
  3. 镜像ID替换:准入控制器把Pod的image字段替换为image-registry.openshift-image-registry.svc:5000/demo/myapp@sha256:abc123...。
  4. 凭证注入:kubelet使用Pod绑定的ServiceAccount,通过内部Registry的认证接口获取临时拉取凭证。
  5. 镜像拉取:kubelet从内部Registry拉取镜像层,节点上完成解压和容器启动。

这条路径的关键在于:Pod最终拉取的是内部Registry的地址,而不是外部仓库。即使原始镜像来自Docker Hub,OpenShift也会先把它导入到内部Registry,再由内部Registry分发到各节点。这样做的好处是拉取速度快、不依赖外网、便于统一治理。

如果Pod直接引用外部镜像(如docker.io/library/nginx:latest),且没有对应的ImageStream,OpenShift会尝试直接从外部拉取。但生产环境建议统一走内部Registry,以便做镜像扫描、签名和配额控制。

架构关系可视化

下面这张图把内部Registry、ImageStream、ImageStreamTag以及拉取路径放在一起,帮助建立整体认知。图中左侧是外部镜像源,中间是OpenShift内部对象,右侧是节点上的容器运行时。

外部镜像源 Docker Hub / Quay 私有 Harbor 等 docker.io/library/nginx 内部 Registry image-registry.openshift- image-registry.svc:5000 按 Namespace 隔离仓库路径 存储后端:S3 / PVC / GCS 认证:ServiceAccount 自动注入 ImageStream demo/myapp lookupPolicy.local: true ImageStreamTag myapp:latest → sha256:abc123... 节点容器运行时 CRI-O / containerd kubelet 拉取镜像层 解压 → 启动容器 Pod image: demo/myapp:latest (准入控制器改写后) 导入/同步 标签追踪 解析 镜像ID 启动 拉取路径关键点: Pod 引用短名称 → ImageStream 解析 → ImageStreamTag 映射到 sha256 → 准入控制器改写为内部 Registry 地址 → kubelet 凭 ServiceAccount 拉取

实际验证:观察一次完整的解析过程

理论讲完,用一条命令验证ImageStreamTag到内部Registry地址的映射关系:

# 查看 ImageStreamTag 的详细信息
oc describe istag myapp:latest -n demo

# 输出关键行示例:
# Docker Image: image-registry.openshift-image-registry.svc:5000/demo/myapp@sha256:abc123...
# Image: sha256:abc123...
# Created: 2 minutes ago

再查看Pod实际使用的镜像地址,可以看到它已经被改写为内部Registry地址:

oc get pod myapp-7d9f8b6c5-xxxxx -n demo -o jsonpath='{.spec.containers[0].image}'
# 输出:
# image-registry.openshift-image-registry.svc:5000/demo/myapp@sha256:abc123...

如果发现Pod的image字段仍然是demo/myapp:latest,说明ImageStream解析没有生效,通常是因为lookupPolicy.local未开启,或者Pod所在的命名空间没有对应的ImageStream。这是排障时第一个要检查的点。

不要手动修改Pod的image字段为内部Registry地址。正确做法是让OpenShift通过ImageStream自动解析,否则会失去标签追踪和自动触发部署的能力。

架构小结

把本节内容串起来看:内部Registry是存储层,ImageStream是逻辑视图层,ImageStreamTag是标签到镜像ID的映射层,而镜像拉取路径则是从Pod定义到节点拉取的执行链路。四者分工明确,共同支撑了OpenShift“构建一次、多处部署、统一治理”的镜像管理模型。理解这条链路后,后续学习镜像导入、构建触发、镜像清理和配额管理时,就能清楚每个操作作用在哪个环节上。