OpenShift

OpenShift 4 概念 – OpenShift是如何通过ImageStream访问Image的

有关ImageStream的入门操作可参见:
OpenShift 4 Hands-on Lab (3) – 用ImageStream操作Image

ImageStream基本概念


容器镜像存储在镜像注册表中(如Docker Hub、Quay.io),我们可以从那里手动pull出来单独运行,也可以在Kubernetes集群中运行。Kubernetes没有内置的镜像注册表,然而这在企业环境中又是通常是需要的。Red Hat OpenShift 提供了一个开箱即用的集成注册表来填补这一空白,并引入了一个名为ImageStream的新的Kubernetes对象资源,以“Kubernetes-native方式”管理镜像。

ImageStream 是为了支持不同用途而设计的。但在一开始,某些内容可能会让人感到困惑。技术上来说,ImageStream只是一个包含其它实际镜像元数据的Kubernetes对象资源,它可以指向OpenShift管理内部的镜像(是在OpenShift集群内部Registry中的镜像),ImageStream还可以指向外部Registry中的镜像。对于镜像,ImageStream是一个额外的抽象层,它提供了一些额外的功能(例如轮询更新、缓存)。由于 ImageStream 可以有两种不同的行为方式,如果我们区分它们是指向内部镜像还是指向外部镜像,就更容易理解它们。

OpenShift在生成ImageStream的时候,过程大致如下:

  • OpenShift将提供的Image访问地址当做ImageStreamTag,并通过访问地址获得Image其它元数据,其中包括ImageStreamImage信息。
  • OpenShift将ImageStreamTag和ImageStreamStream作为重要元数据保存在这个Image对应的ImageStream。
  • 在OpenShift中,ImageStreamImage是ImageStream中的一个具体的镜像版本。它是一个对象,用于表示一个特定的容器镜像,包括镜像名称、版本号、镜像标签等信息。每个ImageStream都可以包含多个ImageStreamImage对象,表示不同的镜像版本。
  • 当用户使用OpenShift部署应用程序时,可以通过引用ImageStreamImage来指定所需的容器镜像版本。这样,即使在ImageStream中更新了镜像版本,应用程序也可以自动更新到最新的版本。
  • 在OpenShift中,ImageStreamImage对象是自动创建的。当用户推送新的镜像版本到ImageStream时,OpenShift将创建一个新的ImageStreamImage对象来表示这个镜像版本。用户还可以使用OpenShift的命令行工具或Web界面来查看和管理ImageStreamImage对象。
  • 总之,ImageStreamImage是OpenShift中用于表示容器镜像版本的一个对象。它可以帮助用户更加方便和灵活地管理容器镜像,实现容器镜像的版本控制和生命周期管理。

OpenShift应用在使用ImageStream的时候,缺省过程大致如下:

  • OpenShift根据应用提供的ImageStreamTag在ImageStream中找到对应的ImageStreamImage
  • OpenShift然后使用ImageStreamImage从远程Registry上pull image,这样就可以使用该Image了

以上是缺省生成ImageStream和通过ImageStream使用Image的过程。除此以外可以定制ImageStream的配置,可以实现以下定制过程:

  • OpenShift每次使用的Image的时候不使用本地ImageStream中的ImageStreamImage,而是每次都用ImageStreamTag从外部获取一次ImageStreamImage,然后再根据获取到的ImageStreamImage来pull image。
  • OpenShift在使用ImageStreamImage获取image的时候,缺省每次都从外部pull image,但也可以选择使用已经pull到本地的Image。

来自ChatGPT的说明


Internal Image 和 Internal Registry


首先,下面是关于存储在Internal Registry中的Image的一些注意事项:

  • 对于OpenShift内部Registry上的Image,ImageStream和内部Registry是紧密耦合的。例如,is-project命名空间中名为myimage-internal的ImageStream会被映射到image-registry.openshift-image-registry.svc:5000/is-project/myimage-internal。
  • 如果我们运行一个新的Build,其中output指定的是ImageStream类型,那么镜像将被存储在OpenShift内部注册表中。(【引用#1】如果Build的output指定的是DockerImage类型,那么OpenShift会使用push操作将Image推送到标准的Docker Registry,缺省是DockerHub)。另外同理适用于指定Builder Image的来源。
  • 如果我们使用Push方法手动向Internal Image Registry推送一个Image,OpenShift会自动创建一个匹配的ImageStream并指向这个Internal Image Registry中的Image。
  • 如果我们删除了ImageStream,在OpenShift内部的Registry中保存的Image存储也会被删除。
  • 为了保持OpenShift内部Registry的使用空间,你需要定期Prune Images。在这个过程中,那些在Build或Deployment中被使用到的内部Image会自动保留。

External Image 和 External Registry


在OpenShift之外的External Registry保存了外部Image。ImageStream和External Registry、以及其上的外部Image的关系如下:

  • 我们可以用 oc import-image 或 oc tag 命令创建一个指向外部 Image 的 ImageStream,或者直接用 yaml 创建一个 ImageStream。
  • 一个ImageStream中的标签可以指向来自完全不同的Registry和Repository的Image。通过这种有效的方法可以将相关的外部Image关联起来,而Image可存储在不同的Registry存储库中。
  • 作为一个抽象层,ImageStream隐藏了Image的实际来源。当构建和部署引用一个ImageStream时,我们可以通过修改ImageStream中的URL来改变正在使用的Image,而不是逐一编辑OpenShift的每个构建和部署。
  • 当创建或更新Image时,ImageStream 通过其唯一的 sha256 ID(而非Tag)来引用Image 。这使得当外部Registry变化标签的时候,其使用的Image可保持稳定性。
  • ImageStream 可以定期(15 分钟)监控外部标记是否发生变化(通过–scheduled=true)。
  • 我们可以在内部Registry中缓存外部Image(通过–reference-policy=local)。这真的很有用,因为它可以使部署更加快速,并且不受外部Registry可用性的影响。
  • 无论是内部还是外部Image,在ImageStream更新时会自动触发相关的BuildConfigs和DeploymentConfigs。

确定ImageStream指向的目标和方式


我们可以把OpenShift的ImageStream看成是一个指向Image的指针(当然还有一些其它元数据),ImageStream指向的Image目标可以是多种不同类型的目标,它们包括:

  1. 指向一个external image
  2. 通过pullthrough 指向一个被缓存的external image
  3. 总是使用一个external image的URL
  4. 指向一个internal image
  5. 指向另一个ImageStream

指向External Image


我们可以用以下“oc import-image”命令或“oc tag”命令创建一个存放在is-project下的ImageStream,并让它指向外部Image,这个外部Image就是docker.io/balazsszeti/hello:sleeper。

$ oc new-project is-project
$ oc import-image is-project/myimage-ref-source:mytag --from="docker.io/balazsszeti/hello:sleeper" --confirm
$ oc tag docker.io/balazsszeti/hello:sleeper is-project/myimage-ref-source:mytag

然后我们可以查看这个ImageStream和ImageStreamTag的详细信息。

$ oc get is myimage-ref-source -oyaml
apiVersion: image.openshift.io/v1
kind: ImageStream
metadata:
  annotations:
    openshift.io/image.dockerRepositoryCheck: "2020-04-12T08:18:25Z"
  creationTimestamp: "2020-04-12T08:18:25Z"
  generation: 1
  name: myimage-ref-source
  namespace: is-project
  resourceVersion: "2844402"
  selfLink: /apis/image.openshift.io/v1/namespaces/is-project/imagestreams/myimage-ref-source
  uid: 2e541461-7c96-11ea-8ba8-0a580a81000f
spec:
  lookupPolicy:
    local: false
  tags:
  - from:
      kind: DockerImage
      name: docker.io/balazsszeti/hello:sleeper
    importPolicy: {}
    name: mytag
    referencePolicy:
      type: Source
status:
  dockerImageRepository: image-registry.openshift-image-registry.svc:5000/is-project/myimage-ref-source
  publicDockerImageRepository: default-route-openshift-image-registry.apps.cluster-beijing-78c7.beijing-78c7.example.opentlc.com/is-project/myimage-ref-source
  tags:
  - items:
    - created: "2020-04-12T08:18:25Z"
      dockerImageReference: docker.io/balazsszeti/hello@sha256:42957024b43e121a210a1b3a8a44f497233a2385f7ef48227a6866afdb9b8e1b
      generation: 1
      image: sha256:42957024b43e121a210a1b3a8a44f497233a2385f7ef48227a6866afdb9b8e1b
    tag: mytag
 
$ oc get istag
NAME                       IMAGE REF                                                                                             UPDATED
myimage-ref-source:mytag   docker.io/balazsszeti/hello@sha256:42957024b43e121a210a1b3a8a44f497233a2385f7ef48227a6866afdb9b8e1b   10 minutes ago
  1. 名为mytag的ImageSteamTag指向的是DockerImage类型的External Image,外部镜像URL(名称+Tag)是docker.io/balazsszeti/hello:sleeper。
  2. 这个ImageSteam的mytag实际指向的外部Image是由dockerImageReference所指的sha256 ID,即下面的字符串。在生成ImageStreamTag的时候,OpenShift是使用了docker.io/balazsszeti/hello:sleeper获得的该Image的sha256 ID。这样当在build或deployment命令中引用ImageStreamTag来创建Pod的时候,实际上使用的是用sha256 ID代表的Image。
 docker.io/balazsszeti/hello@sha256:42957024b43e121a210a1b3a8a44f497233a2385f7ef48227a6866afdb9b8e1b
  1. 每次通过ImageSteamTag使用Image的时候使用的是source类型的referencePolicy,即直接从source访问Image。因此如果当时网络不通,则无法访问该Image。
  2. 当生成ImageSteamTag指向sha256 ID后,修改升级外部镜像的tag就不再影响ImageSteamTag了,也就是说即便把外部镜像从“docker.io/balazsszeti/hello:sleeper”改为“docker.io/balazsszeti/hello:sleeper-v1”,只要sha256 ID没有变化,OpenShift的ImageSteamTag就可以继续使用该外部镜像。
  3. 当外部镜像发生变化后需要自动更新ImageSteamTag对应的sha256 ID,那么可以使用oc tag或oc import-image命令的“–scheduled=true”参数,它会在YAML中设置“importPolicy: {scheduled: true}”。
  4. ImageStream是OpenShift扩展的对象,因此OpenShift的Build、DeploymentConfig等对象可以直接使用它。如果需要在Kubernetes的RC、Pod等对象使用ImageStream,可执行以下的命令启动ImageStream的本地名称查询功能。该命令将把ImageStream的Imagestream. spec.lookupPolicy.local设为true。
$ oc set image-lookup IMAGESTREAM

以下是一个例子:如果为名为hello的ImageStream启用了“本地名称”查询功能,那么在pod或其他资源中使用“–image=hello”就可以访问到ImageSteamTag指向的Image,而不是来自上游Image Registry。如果没有启动本地名称查询功能,那么使用“–image=hello”访问Image的时候会出问题,例如下面的“ErrImagePull”错误。

$ oc import-image is-project/hello:latest --from="openshift/hello-openshift:latest" --confirm
$ oc run hello --image=hello
$ oc get pod -w
NAME    READY   STATUS              RESTARTS   AGE
hello   0/1     ContainerCreating   0          5s
hello   0/1     ErrImagePull        0          10s

下面在使用了“set image-lookup”后就可通过“–image=hello”使用该镜像了。

$ oc delete pod hello
$ oc set image-lookup hello 
$ oc run hello --image=hello
$ oc get pod -w
NAME    READY   STATUS              RESTARTS   AGE
hello   0/1     ContainerCreating   0          4s
hello   1/1     Running             0          7s

通过pullthrough指向External Image


为了让ImageStream使用pullthrough特性,我们可以在“oc import-image”或“oc tag”命令中设置“–reference-policy=local”参数。

$ oc import-image is-project/myimage-ref-local:mytag --from="docker.io/balazsszeti/hello:sleeper" --confirm --reference-policy=local
$ oc tag docker.io/balazsszeti/hello:sleeper is-project/myimage-ref-local:mytag --reference-policy=local

“–reference-policy=local”参数会将ImageStream的referencePolicy.local设置成true。

。。。
  referencePolicy:
    type: Local
。。。

当“–reference-policy=local”的时候,StreamImage使用的是pullthrough方式指向External Image,那么:

  1. 根据Image生成Pod的时候会使用Internal Registry上的已经被pulled through的Image。例如:
image-registry.openshift-image-registry.svc:5000/is-project/myimage-ref-local@sha256:42957024b43e121a210a1b3a8a44f497233a2385f7ef48227a6866afdb9b8e1b
1
  1. 当Image第一次被使用的时后,它会先从External Registry被获取到然后缓存到Internal Registry缓存中。所以在第一次使用Image的时后还不会脱机工作,但一旦缓存后,就再也不需要联系外部注册表了。即使将映像从外部注册表中删除,它也会保留在Internal Registry缓存中。
  2. 和“指向External Image”一样,当External Image的Tag变化后,并且如果sha256 ID还有效,那么不会影响继续使用ImageStreamTag访问镜像,而且Internal Registry的Image缓存也不会更新。当使用手动更新或使用“–scheduled=true”定时更新,更新后的External Image会更新至内部缓存的Image。
  3. 由于Pod是采用Internal Registry sha256 ID获取被缓存的Image,因此如果缓存Image没有被清除,那么Pod就可以被rollback。

每次通过External Image的URL地址获取镜像


通过使用oc tag命令的–reference=true参数可创建一个ImageStream。在通过它运行Pod的时候,OpenShift不会直接使用ImageStreamTag的sha256 ID来找到对应的Image,而是每次都是通过指定的External Image名称和对应的Tag去找那个对应特定的Image。下面我们对比使用–reference=true参数和–reference=false(缺省情况)的区别。

  1. 首先使用–reference=true参数创建一个ImageStream,然后查看是否有ImageStreamTag对象。可以看到当使用–reference=true参数的时候,OpenShift并不会记录Image的sha256 ID信息(不过OpenShift实际是通过sha256 ID识别被使用的Image)。此时OpenShift每次都要通过解析External Image的URL地址(即docker.io/openshift/hello-openshift:latest)解析实际使用的Image(其实最终还要解析到Image的sha256 ID,只不过不在OpenShift的ImageStreamTag中记录该sha256 ID信息了)。因此这种情况是每次访问Image都做一次外部镜像地址的全解析。
$ oc tag docker.io/openshift/hello-openshift:latest is-project/hello-1:latest --reference=true
$ oc get istag hello-1:latest
Error from server (NotFound): imagestreamtags.image.openshift.io "hello-1:latest" not found
123
  1. 首先使用–reference=false参数创建一个ImageStream,然后查看是否有ImageStreamTag对象。可以看到当使用–reference=false参数的时候,OpenShift会为之创建与ImageStream对应的ImageStreamTag。OpenShift以后实际上是通过ImageStreamTag中记录的sha256 ID定位该Image的,因此从External Image的URL地址解析到Image的sha256 ID过程OpenShift只做一次。
$ oc tag docker.io/openshift/hello-openshift:latest is-project/hello-2:latest --reference=false
$ oc get istag hello-2:latest
NAME             IMAGE REF                                                                                                     UPDATED
hello-2:latest   docker.io/openshift/hello-openshift@sha256:aaea76ff622d2f8bcb32e538e7b3cd0ef6d291953f3e7c9f556c1ba5baf47e2e   5 seconds ago
1234

当我们使用–reference=true的ImageStream的时候,具有以下特性:

  1. 在这种情况下由于ImageStream没有记录ImageSteamTag的sha256 ID信息,因此每次都要使用以下External Image的URL找到该Image。
docker.io/openshift/hello-openshift:latest
1
  1. 在这种情况下即便使用了–reference-policy=local选项,也无法使用缓存,因此不能离线运行。
  2. 由于每次OpenShift都通过External Image的URL找到所用Tag对应的Image来运行Pod,因此当External Image的Tag升级后,OpenShift可以立即感知出来这种变化,并用新的Tag对应的Image生成Pod。在–reference=false的时候,应配合Pod的“imagePullPolicy: Always”参数一起使用,以让Pod每次都pull到最新的Image。
  3. 在这种情况时,Pod直接使用的是External Image URL获取的Image,而不是用ImageStream配置中的Internal Registry URL(即image-registry.openshift-image-registry.svc:5000/is-project/myimage-reference:mytag)获取Image。
  4. 由于Pod是直接通过External Image的URL获得Image,例如docker.io/openshift/hello-openshift:latest,因此即便rollback,Pod获取到的Image可能会没有变化,因此rollback就不会奏效。

使用Internal Image


通过OpenShift的build生成的Image会推送到 Internal Registry上,OpenShift同时会为其生成ImageStream。

$ oc new-app php~https://github.com/liuxiaoyu-git/php-helloworld
 
$ oc get istag php-helloworld:latest
NAME                    IMAGE REFERENCE                                                                                                                                      UPDATED
php-helloworld:latest   image-registry.openshift-image-registry.svc:5000/is-project/php-helloworld@sha256:759da8784d4c130ff6784fa375141fa65b949ac12eae4f98d537241cb49d6060   3 minutes ago
12345

使用这种方式的ImageStream有以下特性:

  1. 同使用pullthrough方式相同,Pod使用的是Internal Registry和sha256 ID的组合方式访问到Image。
image-registry.openshift-image-registry.svc:5000/is-project/php-helloworld@sha256:759da8784d4c130ff6784fa375141fa65b949ac12eae4f98d537241cb49d6060
1
  1. 由于Image就在OpenShift的internal registry中,因此可以离线访问到。
  2. 当通过build过程更新了Image的Tag后,Pod会使用更新后的Image。
  3. 其它特性和上面介绍的pullthrough的情况相同。