镜像管理

介绍openshift的镜像管理方案。

刚接触docker时,第一个接触到的应该就是镜像了,docker之所以如此火热,个人认为一大部分原因就是这个镜像的提出,极大的促进了DevOps推广和软件复用的能力。

而openshift对镜像的管理非常强大,直到写这篇blog,我才真正意识到这点,甚至犹豫是不是要放到开发实战篇后再来写镜像管理

简要说下openshift里使用镜像的情况:

  • 首先openshift可以利用任何实现了Docker registry API的镜像仓,比如,Vmware的Harbor项目,Docker hub以及集成镜像仓( integrated registry)
  • 集成镜像仓,openshift内部的,可以动态生成,自动让用户编译的镜像有地方存, 其次它还负责通知openshift镜像的变动,然后openshift会根据策略去决定编译其他依赖镜像还是部署应用
  • 第三方镜像, 可通过命令oc import-image <stream>来实时获取镜像tag信息并转换成镜像流,继而触发后续的编译或者部署。
  • 当然oc new-app也支持直接从第三方镜像仓或者本地镜像里启动一个应用

文末有安装集成镜像仓的说明,先介绍image Streams 和 istag的概念和应用场景。

镜像管理

  • openshift基于docker的image概念又延伸出了Image Streams和Image Stream Tags概念

    默认openshift项目下会有一些镜像流,是供自带模板里用的,所以想加速部署模板的话,可以在改这里,通过istag指向本地镜像仓。

    oc get is -n openshift
    oc get istag -n openshift
    

    image,通俗讲就是对应用运行依赖(库,配置,运行环境)的一个打包。docker pull push, 就是操作的镜像。 为什么openshift还要抽象出is和istag呢,主要是为了打通集成编译和部署环节(bc和dc),原生API就支持了DevOps理念。后面会细讲bc和dc

    is,开发人员可以理解成git的分支,每个分支都会编译很多临时版本出来,这个就是对应到is~=分支和istag~=版本号。 其实is和istag只是记录了一些映射关系,并不会存放实际镜像数据,比如is里记录了build后要output的镜像仓地址和所有tags,而istag里又记录了具体某个tag与image(可能是存于外部镜像仓,也能是某个is)的关系, 利用此实现了bc/dc和镜像的解耦。

    这里通过部署jenkins服务,来初步了解下具体的含义,

    • 创建ci项目
    oc new-project ci
    # 先拉取必要镜像
    docker pull openshift/jenkins-1-centos7
    
    #通过模板部署,下面一条命令就可以创建一个临时的jenkins服务的
    #oc new-app jenkins-ephemeral
    #跑之前我们先来注意几点
    
    • 更改默认的is

    先来查看默认的is

    oc get template jenkins-ephemeral -n openshift -o json
    ...
    "triggers": [
      {
          "imageChangeParams": {
              "automatic": true,
              "containerNames": [
                  "jenkins"
              ],
              "from": {
                  "kind": "ImageStreamTag",
                  "name": "${JENKINS_IMAGE_STREAM_TAG}",
                  "namespace": "${NAMESPACE}"
              },
              "lastTriggeredImage": ""
          },
          "type": "ImageChange"
      },
      {
          "type": "ConfigChange"
      }
      ]
    ...
    {
        "name": "NAMESPACE",
        "displayName": "Jenkins ImageStream Namespace",
        "description": "The OpenShift Namespace where the Jenkins ImageStream resides.",
        "value": "openshift"
    },
    {
        "name": "JENKINS_IMAGE_STREAM_TAG",
        "displayName": "Jenkins ImageStreamTag",
        "description": "Name of the ImageStreamTag to be used for the Jenkins image.",
        "value": "jenkins:latest"
    }
    ...
    

    可以看到默认模板里部署jenkins时,会从openshfit的namespace里拉取jenkins:latest的镜像, 去openshift项目里找找看,确实存在对应的is

    ➜  ~ oc get is -n openshift | grep jenkins
    jenkins      170.16.131.234:5000/openshift/jenkins      latest,1                     2 days ago
    
    ➜  ~ oc get istag -n openshift | grep jenkins:latest
    jenkins:latest      openshift/jenkins-1-centos7@sha256:ab590529e20470e53c1d4b6b970de5d4fd357d864320735a75c1df0e2fffde07       2 days ago   sha256:ab590529e20470e53c1d4b6b970de5d4fd357d864320735a75c1df0e2fffde07
    

    上面的命令的输出,有两个点要阐述下,

    • is里的170.16.131.234:5000/openshift/jenkins是个可有可无的地址,一般系统会填写集成镜像仓的地址
    • 而istag里openshift/jenkins-1-centos7@sha256:ab则是指明了对应tag的镜像来源,

    这样的话,默认执行oc new-app jenkins-ephemeral的话,会从docker.io那里拉取镜像 openshift/jenkins-1-centos7@sha256:ab590529e20470e53c1d4b6b970de5d4fd357d864320735a75c1df0e2fffde07

    为了加速部署,我们把刚才pull下来的镜像,push到集成镜像仓里

    #添加用户
    #htpasswd /etc/origin/master/htpasswd xxp
    docker tag openshift/jenkins-1-centos7 hub2.300.cn/openshift/jenkins
    docker login -u developer -p `oc whoami -t` hub2.300.cn
    docker push hub2.300.cn/openshift/jenkins
    

    push完后再来看istag的变化,由以前的openshift/jenkins-1-centos变为了170.16.131.234:5000/openshift/jenkins

    ➜  ~ oc get istag -n openshift | grep jenkins:latest
    jenkins:latest      170.16.131.234:5000/openshift/jenkins@sha256:dc0f434a492d11d6ae13711e77f87303a06a8fc0fb3a97ae327a4b88c33435b6   21 hours ago   sha256:dc0f434a492d11d6ae13711e77f87303a06a8fc0fb3a97ae327a4b88c33435b6
    
    

    这里是push到集成镜像仓后,系统会自动更新对应的is和istag, 为了加速部署,更改istag还可以通过import-image从私有镜像仓(harbor)里来完成

    这样再来部署jenkins应用就快速多了

    还有很多更细致的东西,比如如何周期同步第三方镜像仓等等,有需要的查看官文

安装独立镜像仓

可以通过openshift-ansible一次性安装OK,这里使用CLI安装,正好可以串一下openshift里的各种概念的使用场景。

部署

oc project default
oc adm policy add-scc-to-user privileged system:serviceaccount:default:registry
oc label node 192.168.56.102 registry=true
#使用hostPath存储,要在node上放行权限,默认registry用的1001 userID, 不然后续挂载进去的volume,没有写权限
sudo chown 1001:root /home/registry
#注意要指定使用的镜像版本,默认是拉取最新的, 一定要指明``--volume``参数,不然deploy,不会挂载主机目录,官文这里遗漏了。
oc adm registry --images="openshift/origin-docker-registry:v1.4.1" --selector="registry=true" --mount-host="/home/registry" --service-account=registry --volume='/registry'```

中间莫名出现部署``error``的状态,重新部署了下``oc deploy docker-registry --retry``

### 加密镜像仓

- 先拿到serviceIP
```bash
[root@node0 master]# oc get svc docker-registry
NAME              CLUSTER-IP       EXTERNAL-IP   PORT(S)    AGE
docker-registry   170.16.132.252   <none>        5000/TCP   1h
  • 创建自签名证书,如果已经有了,跳过此步
oc adm ca create-server-cert \
    --signer-cert=/etc/origin/master/ca.crt \
    --signer-key=/etc/origin/master/ca.key \
    --signer-serial=/etc/origin/master/ca.serial.txt \
    --hostnames='hub.example.com,docker-registry.default.svc.cluster.local,170.16.132.252:5000' \
    --cert=/etc/secrets/registry.crt \
    --key=/etc/secrets/registry.key
  • 创建secret, secret是专门来保存敏感信息的,比如密码,sshkey,token信息等等

    更详细的介绍可以查看这里

    oc secrets new registry-secret \
        /etc/secrets/registry.crt \
        /etc/secrets/registry.key
    
  • 绑定secret到serviceaccount

oc secrets link registry registry-secret
oc secrets link default  registry-secret
  • 更新dc,添加volume,把新创建的secret挂进去
oc volume dc/docker-registry --add --type=secret \
    --name=docker-registry --secret-name=registry-secret -m /etc/secrets

如果想移除的话,如下

oc volume dc/docker-registry --remove --name=docker-registry
  • 更新环境变量
oc set env dc/docker-registry \
    REGISTRY_HTTP_TLS_CERTIFICATE=/etc/secrets/registry.crt \
    REGISTRY_HTTP_TLS_KEY=/etc/secrets/registry.key
  • 更新健康监测 HTTP->HTTPS
oc patch dc/docker-registry -p '{"spec": {"template": {"spec": {"containers":[{
    "name":"registry",
    "livenessProbe":  {"httpGet": {"scheme":"HTTPS"}}
  }]}}}}'
oc patch dc/docker-registry -p '{"spec": {"template": {"spec": {"containers":[{
      "name":"registry",
      "readinessProbe":  {"httpGet": {"scheme":"HTTPS"}}
    }]}}}}'
  • 验证是否OK
[root@node0 master]# oc logs dc/docker-registry  | grep tls
time="2017-03-02T16:01:41.113619323Z" level=info msg="listening on :5000, tls" go.version=go1.7.4 instance.id=594aa09b-4540-4e38-a85a-851261cd1254

添加路由

oc create route passthrough registry --service=docker-registry --hostname=hub2.300.cn

登录镜像仓

oc policy add-role-to-user admin developer -n default
oc login -u developer
docker login -u developer -p `oc whoami -t` hub2.300.cn
  • push镜像
docker push hub2.300.cn/default/busybox

安装镜像仓console

  • oc利用官方模板安装
oc create -n default -f https://raw.githubusercontent.com/openshift/openshift-ansible/master/roles/openshift_hosted_templates/files/v1.4/origin/registry-console.yaml
oc create route passthrough --service registry-console \
    --hostname hub3.300.cn \
    -n default
oc new-app -n default --template=registry-console \
    -p OPENSHIFT_OAUTH_PROVIDER_URL="https://192.168.31.100:8443" \
    -p REGISTRY_HOST=$(oc get route docker-registry -n default --template='{{ .spec.host }}') \
    -p COCKPIT_KUBE_URL=$(oc get route registry-console -n default --template='https://{{ .spec.host }}')
  • 登录浏览器打开https://hub3.300.cn/registry,使用已有的账户登录,比如这里是默认的developer和developer。 界面