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字段。整个拉取路径可以拆成几个阶段:
- Pod创建请求:用户提交Deployment,Pod模板中
image字段写的是demo/myapp:latest。 - ImageStream解析:OpenShift的ImageStream解析器根据
lookupPolicy,在demo命名空间中找到myapp:latest对应的ImageStreamTag。 - 镜像ID替换:准入控制器把Pod的
image字段替换为image-registry.openshift-image-registry.svc:5000/demo/myapp@sha256:abc123...。 - 凭证注入:kubelet使用Pod绑定的ServiceAccount,通过内部Registry的认证接口获取临时拉取凭证。
- 镜像拉取:kubelet从内部Registry拉取镜像层,节点上完成解压和容器启动。
这条路径的关键在于:Pod最终拉取的是内部Registry的地址,而不是外部仓库。即使原始镜像来自Docker Hub,OpenShift也会先把它导入到内部Registry,再由内部Registry分发到各节点。这样做的好处是拉取速度快、不依赖外网、便于统一治理。
docker.io/library/nginx:latest),且没有对应的ImageStream,OpenShift会尝试直接从外部拉取。但生产环境建议统一走内部Registry,以便做镜像扫描、签名和配额控制。架构关系可视化
下面这张图把内部Registry、ImageStream、ImageStreamTag以及拉取路径放在一起,帮助建立整体认知。图中左侧是外部镜像源,中间是OpenShift内部对象,右侧是节点上的容器运行时。
实际验证:观察一次完整的解析过程
理论讲完,用一条命令验证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。这是排障时第一个要检查的点。
image字段为内部Registry地址。正确做法是让OpenShift通过ImageStream自动解析,否则会失去标签追踪和自动触发部署的能力。架构小结
把本节内容串起来看:内部Registry是存储层,ImageStream是逻辑视图层,ImageStreamTag是标签到镜像ID的映射层,而镜像拉取路径则是从Pod定义到节点拉取的执行链路。四者分工明确,共同支撑了OpenShift“构建一次、多处部署、统一治理”的镜像管理模型。理解这条链路后,后续学习镜像导入、构建触发、镜像清理和配额管理时,就能清楚每个操作作用在哪个环节上。