本系列文章描述了离线环境下以 UPI
(User Provisioned Infrastructure) 模式安装 Openshift Container Platform
(OCP) 4.4.5 的步骤,我的环境是 VMware ESXI
虚拟化,也适用于其他方式提供的虚拟机或物理主机。离线资源包括安装镜像、所有样例 Image Stream
和 OperatorHub
中的所有 RedHat Operators。
本系列采用静态 IP 的方式安装 OCP
集群,如果你可以随意分配网络,建议采用 DHCP
的方式。
1. 离线环境
单独准备一台节点用来执行安装任务和离线资源准备,这台节点最好具备魔法上网的能力,以便可以同时访问内外网,我们称这台节点为基础节点。
除此之外还需要部署一个私有镜像仓库,以供 OCP 安装和运行时使用,要求支持 version 2 schema 2 (manifest list),我这里选择的是 Quay 3.3
。镜像仓库需要部署在另外一台节点,因为需要用到 443
端口,与后面的负载均衡端口冲突。
2. 准备离线安装介质
获取版本信息
目前最新的 OCP 版本是 4.4.5,可以从这里下载客户端:
解压出来的二进制文件放到基础节点的 $PATH
下,看下版本信息:
创建内部镜像仓库
内部镜像仓库用于存放部署 OCP 集群所需的镜像,仓库本身使用 Quay
部署。Quay 包含了几个核心组件:
- 数据库 : 主要存放镜像仓库的元数据(非镜像存储)
- Redis : 存放构建日志和Quay的向导
- Quay : 作为镜像仓库
- Clair : 提供镜像扫描功能
首先修改镜像仓库节点的主机名:
$ hostnamectl set-hostname registry.openshift4.example.com
master1.aa.bb.com
。
接着安装 podman
:
$ yum install -y podman
先创建一个 Pod
,用来共享 Network Namespace:
🐳 → podman pod create --name quay -p 443:8443
安装 Mysql 数据库:
$ mkdir -p /data/quay/lib/mysql
$ chmod 777 /data/quay/lib/mysql
$ export MYSQL_CONTAINER_NAME=quay-mysql
$ export MYSQL_DATABASE=enterpriseregistrydb
$ export MYSQL_PASSWORD=<PASSWD>
$ export MYSQL_USER=quayuser
$ export MYSQL_ROOT_PASSWORD=<PASSWD>
$ podman run \
--detach \
--restart=always \
--env MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD} \
--env MYSQL_USER=${MYSQL_USER} \
--env MYSQL_PASSWORD=${MYSQL_PASSWORD} \
--env MYSQL_DATABASE=${MYSQL_DATABASE} \
--name ${MYSQL_CONTAINER_NAME} \
--privileged=true \
--pod quay \
-v /data/quay/lib/mysql:/var/lib/mysql/data:Z \
registry.access.redhat.com/rhscl/mysql-57-rhel7
安装 Redis:
$ mkdir -p /data/quay/lib/redis
$ chmod 777 /data/quay/lib/redis
$ podman run -d --restart=always \
--pod quay \
--privileged=true \
--name quay-redis \
-v /data/quay/lib/redis:/var/lib/redis/data:Z \
registry.access.redhat.com/rhscl/redis-32-rhel7
获取 Red Hat Quay v3 镜像的访问权:
$ podman login -u="redhat+quay" -p="O81WSHRSJR14UAZBK54GQHJS0P1V4CLWAJV1X2C4SD7KO59CQ9N3RE12612XU1HR" quay.io
参考: https://access.redhat.com/solutions/3533201
配置 Quay:
$ podman run --privileged=true \
--name quay-config \
--pod quay \
--add-host mysql:127.0.0.1 \
--add-host redis:127.0.0.1 \
--add-host clair:127.0.0.1 \
-d quay.io/redhat/quay:v3.3.0 config icloudnative.io
这一步会启动一个配置 Quay 的进程,打开浏览器访问:https://registry.openshift4.example.com,用户名/密码为:quayconfig/icloudnative.io
:
选择新建配置,然后设置数据库:
设置超级管理员:
下一个界面要设置两个地方,一个是 Server configuration 的 Server Hostname
,另一个是 Redis Hostname
,SSL 不用设置,后面直接通过命令行配置:
配置检查通过后,就可以保存下载下来:
最后会导出一个 quay-config.tar.gz
,将其上传到 Quay 所在的服务器,解压到配置文件目录:
$ mkdir -p /data/quay/config
$ mkdir -p /data/quay/storage
$ cp quay-config.tar.gz /data/quay/config/
$ cd /data/quay/config/
$ tar zxvf quay-config.tar.gz
生成自签名证书:
# 生成私钥
$ openssl genrsa -out ssl.key 1024
根据私钥生成证书申请文件 csr
:
$ openssl req -new -key ssl.key -out ssl.csr
这里根据命令行向导来进行信息输入:
Common Name 可以输入:*.yourdomain.com
,这种方式可以生成通配符域名证书。
使用私钥对证书申请进行签名从而生成证书:
$ openssl x509 -req -in ssl.csr -out ssl.cert -signkey ssl.key -days 3650
这样就生成了有效期为 10 年的证书文件,对于自己内网服务使用足够。
或者你也可以一步到位:
$ openssl req \
-newkey rsa:2048 -nodes -keyout ssl.key \
-x509 -days 3650 -out ssl.cert -subj \
"/C=CN/ST=Shanghai/L=Shanghai/O=IBM/OU=IBM/CN=*.openshift4.example.com"
证书搞定了之后,还需要修改 config.yaml
,将协议修改为 https
:
PREFERRED_URL_SCHEME: https
然后停止 quay-config:
$ podman stop quay-config
最后一步才是部署 Quay:
$ podman run --restart=always \
--sysctl net.core.somaxconn=4096 \
--privileged=true \
--name quay-master \
--pod quay \
--add-host mysql:127.0.0.1 \
--add-host redis:127.0.0.1 \
--add-host clair:127.0.0.1 \
-v /data/quay/config:/conf/stack:Z \
-v /data/quay/storage:/datastorage:Z \
-d quay.io/redhat/quay:v3.3.0
安装成功后,将自签名的证书复制到默认信任证书路径:
$ cp ssl.cert /etc/pki/ca-trust/source/anchors/ssl.crt
$ update-ca-trust extract
现在可以通过 podman login
命令来测试仓库的连通性,看到如下字样即表示安装成功(也可以通过浏览器访问 Web UI):
🐳 → podman login registry.openshift4.example.com
Username: admin
Password: ********
Login Succeeded
如果使用 Docker 登录,需要将证书复制到 docker 的信任证书路径:
$ mkdir -p /etc/docker/certs.d/registry.openshift4.example.com
$ cp ssl.cert /etc/docker/certs.d/registry.openshift4.example.com/ssl.crt
$ systemctl restart docker
下载镜像文件
准备拉取镜像权限认证文件。 从 Red Hat OpenShift Cluster Manager
站点的
Pull Secret 页面下载 registry.redhat.io
的 pull secret
。
# 把下载的 txt 文件转出 json 格式,如果没有 jq 命令,通过 epel 源安装
$ cat ./pull-secret.txt | jq . > pull-secret.json
$ yum install epel-release
$ yum install jq
JSON 内容如下:
{
"auths": {
"cloud.openshift.com": {
"auth": "b3BlbnNo...",
"email": "you@example.com"
},
"quay.io": {
"auth": "b3BlbnNo...",
"email": "you@example.com"
},
"registry.connect.redhat.com": {
"auth": "NTE3Njg5Nj...",
"email": "you@example.com"
},
"registry.redhat.io": {
"auth": "NTE3Njg5Nj...",
"email": "you@example.com"
}
}
}
把本地仓库的用户密码转换成 base64
编码:
$ echo -n 'admin:password' | base64 -w0
cm9vdDpwYXNzd29yZA==
然后在 pull-secret.json
里面加一段本地仓库的权限。第一行仓库域名和端口,第二行是上面的 base64
,第三行随便填个邮箱:
"auths": {
...
"registry.openshift4.example.com": {
"auth": "cm9vdDpwYXNzd29yZA==",
"email": "you@example.com"
},
...
设置环境变量:
$ export OCP_RELEASE="4.4.5-x86_64"
$ export LOCAL_REGISTRY='registry.openshift4.example.com'
$ export LOCAL_REPOSITORY='ocp4/openshift4'
$ export PRODUCT_REPO='openshift-release-dev'
$ export LOCAL_SECRET_JSON='/root/pull-secret.json'
$ export RELEASE_NAME="ocp-release"
- OCP_RELEASE : OCP 版本,可以在
这个页面查看。如果版本不对,下面执行
oc adm
时会提示image does not exist
。 - LOCAL_REGISTRY : 本地仓库的域名和端口。
- LOCAL_REPOSITORY : 镜像存储库名称,使用
ocp4/openshift4
。 PRODUCT_REPO
和RELEASE_NAME
都不需要改,这些都是一些版本特征,保持不变即可。- LOCAL_SECRET_JSON : 密钥路径,就是上面
pull-secret.json
的存放路径。
在 Quay 中创建一个组织(Organization
)ocp4
用来存放同步过来的镜像。
最后一步就是同步镜像,这一步的动作就是把 quay
官方仓库中的镜像同步到本地仓库,如果失败了可以重新执行命令,整体内容大概 5G
。
$ oc adm -a ${LOCAL_SECRET_JSON} release mirror \
--from=quay.io/${PRODUCT_REPO}/${RELEASE_NAME}:${OCP_RELEASE} \
--to=${LOCAL_REGISTRY}/${LOCAL_REPOSITORY} \
--to-release-image=${LOCAL_REGISTRY}/${LOCAL_REPOSITORY}:${OCP_RELEASE}
oc adm release mirror
命令执行完成后会输出下面类似的信息,保存下来,将来会用在 install-config.yaml
文件中:
imageContentSources:
- mirrors:
- registry.openshift4.example.com/ocp4/openshift4
source: quay.io/openshift-release-dev/ocp-release
- mirrors:
- registry.openshift4.example.com/ocp4/openshift4
source: quay.io/openshift-release-dev/ocp-v4.0-art-dev
本地镜像仓库缓存好镜像之后,通过 tag/list
接口查看所有 tag,如果能列出来一堆就说明是正常的:
这里需要创建一个 OAuth access token
来访问 Quay
的 API,创建过程如下:
- 浏览器登录 Red Hat Quay,选择一个组织(
Organization
),例如ocp4
。 - 在左侧导航中选择
Applications
图标。 - 选择
Create New Application
,输入 Application 的名字然后回车。 - 选择你新创建的 Application,在左侧导航栏中选择
Generate Token
。 - 选择相应的权限,然后点击
Generate Access Token
。 - 再次确认你设置的权限,然后点击
Authorize Application
。 - 保管好生成的 token。
Quay 的 API 文档可以参考这里: Appendix A: Red Hat Quay Application Programming Interface (API)。
Quay 中也能看到所有的镜像:
提取 openshift-install 命令
为了保证安装版本一致性,需要从镜像库中提取 openshift-install
二进制文件,不能直接从
https://mirror.openshift.com/pub/openshift-v4/clients/ocp/4.4.5 下载,不然后面会有 sha256
匹配不上的问题。
# 这一步需要用到上面的 export 变量
$ oc adm release extract \
-a ${LOCAL_SECRET_JSON} \
--command=openshift-install \
"${LOCAL_REGISTRY}/${LOCAL_REPOSITORY}:${OCP_RELEASE}"
如果提示 error: image dose not exist
,说明拉取的镜像不全,或者版本不对。
把文件移动到 $PATH
并确认版本:
$ chmod +x openshift-install
$ mv openshift-install /usr/local/bin/
$ openshift-install version
openshift-install 4.4.5
built from commit 15eac3785998a5bc250c9f72101a4a9cb767e494
release image registry.openshift4.example.com/ocp4/openshift4@sha256:4a461dc23a9d323c8bd7a8631bed078a9e5eec690ce073f78b645c83fb4cdf74
3. 准备 Image Stream 样例镜像
准备一个镜像列表,然后使用 oc image mirror
将镜像同步到私有仓库中:
cat sample-images.txt | while read line; do
target=$(echo $line | sed 's/registry.redhat.io/registry.openshift4.example.com/')
oc image mirror -a ${LOCAL_SECRET_JSON} $line $target
done
如果之前装过 OCP 4.4.5,把 openshift-cluster-samples-operator
项目下 cluster-samples-operator
Pod 的 /opt/openshift
目录同步出来,简单 grep 一下就都有了完整的镜像列表。
完整列表参考 这里。
同步过程中如果遇到报错,可根据报错信息到 Quay
中创建相应的 Organization
,不用中断任务。这里给出一个参考,需要创建以下的 Organization:
rhscl
jboss-datavirt-6
3scale-amp21
3scale-amp22
3scale-amp23
3scale-amp24
3scale-amp25
3scale-amp26
jboss-eap-6
devtools
openshift3
rhpam-7
rhdm-7
jboss-amq-6
jboss-datagrid-7
jboss-datagrid-6
jboss-webserver-3
amq-broker-7
jboss-webserver-5
redhat-sso-7
openjdk
redhat-openjdk-18
fuse7
dotnet
4. 准备 OperatorHub 离线资源
首先在 Quay 中创建一个 devinfra
项目,然后构建 RedHat Operators 的 catalog image
, 保存为 registry.openshift4.example.com/devinfra/redhat-operators:v1
。
$ oc adm catalog build \
-a ${LOCAL_SECRET_JSON} \
--appregistry-endpoint https://quay.io/cnr \
--from=registry.redhat.io/openshift4/ose-operator-registry:v4.4 \
--appregistry-org redhat-operators \
--to=registry.openshift4.example.com/devinfra/redhat-operators:v1
这个 catalog image 相当于 RedHat Operators
的一个目录,通过 catalog image
可以找到 RedHat Operators
的所有镜像。而且 catalog image 使用 sha256 digest
来引用镜像,能够确保应用有稳定可重复的部署。
然后使用 catalog image 同步 RedHat Operators
的所有镜像到私有仓库:
$ oc adm catalog mirror \
-a ${LOCAL_SECRET_JSON} \
registry.openshift4.example.com/devinfra/redhat-operators:v1 \
registry.openshift4.example.com
如果执行过程中遇到 project not found
之类的错误,可根据报错信息到 Quay 中创建相应的项目,不用中断任务。
这里还会遇到一个 bug,执行到最后会有如下的报错信息:
...
I0409 08:04:48.342110 11331 mirror.go:231] wrote database to /tmp/db-225652515/bundles.db
W0409 08:04:48.347417 11331 mirror.go:258] errors during mirroring. the full contents of the catalog may not have been mirrored: couldn't parse image for mirroring (), skipping mirror: invalid reference format
I0409 08:04:48.385816 11331 mirror.go:329] wrote mirroring manifests to redhat-operators-manifests
先来看看有哪些 Operators:
$ sqlite3 /tmp/db-225652515/bundles.db 'select * from related_image'|grep '^|'
随便挑一个 Operator,查看其 ClusterServiceVersion
的 spec.relatedImages
字段内容:
$ cat /tmp/cache-943388495/manifests-698804708/3scale-operator/3scale-operator-9re7jpyl/0.5.0/3scale-operator.v0.5.0.clusterserviceversion.yaml
...
spec:
replaces: 3scale-operator.v0.4.2
relatedImages:
- name: apicast-gateway-rhel8
image: registry.redhat.io/3scale-amp2/apicast-gateway-rhel8@sha256:21be62a6557846337dc0cf764be63442718fab03b95c198a301363886a9e74f9
- name: backend-rhel7
image: registry.redhat.io/3scale-amp2/backend-rhel7@sha256:ea8a31345d3c2a56b02998b019db2e17f61eeaa26790a07962d5e3b66032d8e5
- name: system-rhel7
image: registry.redhat.io/3scale-amp2/system-rhel7@sha256:93819c324831353bb8f7cb6e9910694b88609c3a20d4c1b9a22d9c2bbfbad16f
- name: zync-rhel7
image: registry.redhat.io/3scale-amp2/zync-rhel7@sha256:f4d5c1fdebe306f4e891ddfc4d3045a622d2f01db21ecfc9397cab25c9baa91a
- name: memcached-rhel7
image: registry.redhat.io/3scale-amp2/memcached-rhel7@sha256:ff5f3d2d131631d5db8985a5855ff4607e91f0aa86d07dafdcec4f7da13c9e05
- name: redis-32-rhel7
value: registry.redhat.io/rhscl/redis-32-rhel7@sha256:a9bdf52384a222635efc0284db47d12fbde8c3d0fcb66517ba8eefad1d4e9dc9
- name: mysql-57-rhel7
value: registry.redhat.io/rhscl/mysql-57-rhel7@sha256:9a781abe7581cc141e14a7e404ec34125b3e89c008b14f4e7b41e094fd3049fe
- name: postgresql-10-rhel7
value: registry.redhat.io/rhscl/postgresql-10-rhel7@sha256:de3ab628b403dc5eed986a7f392c34687bddafee7bdfccfd65cecf137ade3dfd
...
可以看到 relatedImages
列表中有些条目的键是 value
而不是 image
,这就是问题所在! 那些没有 image 的条目在反序列化时会将 image 的值当成空字符串 ""
:
$ sqlite3 /tmp/db-225652515/bundles.db 'select * from related_image where operatorbundle_name="3scale-operator.v0.5.0"'
registry.redhat.io/3scale-amp2/zync-rhel7@sha256:f4d5c1fdebe306f4e891ddfc4d3045a622d2f01db21ecfc9397cab25c9baa91a|3scale-operator.v0.5.0
registry.redhat.io/3scale-amp2/memcached-rhel7@sha256:ff5f3d2d131631d5db8985a5855ff4607e91f0aa86d07dafdcec4f7da13c9e05|3scale-operator.v0.5.0
|3scale-operator.v0.5.0
registry.redhat.io/3scale-amp2/apicast-gateway-rhel8@sha256:21be62a6557846337dc0cf764be63442718fab03b95c198a301363886a9e74f9|3scale-operator.v0.5.0
registry.redhat.io/3scale-amp2/backend-rhel7@sha256:ea8a31345d3c2a56b02998b019db2e17f61eeaa26790a07962d5e3b66032d8e5|3scale-operator.v0.5.0
registry.redhat.io/3scale-amp2/3scale-rhel7-operator@sha256:2ba16314ee046b3c3814fe4e356b728da6853743bd72f8651e1a338e8bbf4f81|3scale-operator.v0.5.0
registry.redhat.io/3scale-amp2/system-rhel7@sha256:93819c324831353bb8f7cb6e9910694b88609c3a20d4c1b9a22d9c2bbfbad16f|3scale-operator.v0.5.0
从上面的输出可以看到键为 value
的那几个条目都反序列化失败了,具体的讨论参考:
bundle validate should validate that there are no empty relatedImages。
这里给出一个临时解决方案,先打开另外一个窗口,然后回到原来的窗口执行命令:
$ oc adm catalog mirror \
-a ${LOCAL_SECRET_JSON} \
registry.openshift4.example.com/devinfra/redhat-operators:v1 \
registry.openshift4.example.com
然后迅速切到下一个窗口,查找最新的 manifest 缓存目录:
$ ls -l /tmp/cache-*/
根据日期判断最新的缓存目录,假设是 /tmp/cache-320634009
,然后将所有的 value
替换为 image
:
$ sed -i "s/value: registry/image: registry/g" $(egrep -rl "value: registry" /tmp/cache-320634009/)
同步完成后会产生 redhat-operators-manifests
目录,下面有两个文件:
- imageContentSourcePolicy.yaml : 定义了一个
ImageContentSourcePolicy
对象,该对象可以配置节点将其对官方 Operator manifests 中镜像的引用改为对本地镜像仓库中镜像的引用。 - mapping.txt : 包含了所有的源镜像在本地镜像仓库中的映射位置。
oc image mirror
命令可以引用该文件进一步修改镜像配置。
然而目前这么做还是有问题
1800674: 同步出来的镜像 manifest digest
不对,导致后面离线安装 Operator 时会报镜像无法获取的错误。
暂时可以使用上面 bugzilla 链接里给出的临时解决方案,先安装 skopeo:
$ yum install -y golang gpgme-devel libassuan-devel btrfs-progs-devel device-mapper-devel
$ git clone https://github.com/containers/skopeo
$ cd skopeo
$ make binary-local
$ mv skopeo /usr/local/bin/
从 pull-secret.json
中解码 quay.io
、registry.redhat.io
和 registry.access.redhat.com
的用户名密码,然后通过下面的命令认证:
$ skopeo login -u <quay.io_user> -p <quay.io_psw> quay.io
$ skopeo login -u <registry.redhat.io_user> -p <registry.redhat.io_psw> registry.redhat.io
$ skopeo login -u <registry.access.redhat.com_user> -p <registry.access.redhat.com_psw> registry.access.redhat.com
最后同步镜像的 manifest digest:
cat redhat-operators-manifests/mapping.txt | while read line; do
origin=$(echo $line | cut -d= -f1)
target=$(echo $line | cut -d= -f2)
if [[ "$origin" =~ "sha256" ]]; then
tag=$(echo $origin | cut -d: -f2 | cut -c -8)
skopeo copy --all docker://$origin docker://$target:$tag
else
skopeo copy --all docker://$origin docker://$target
fi
done
不得不说,OCP 的安装真是个浩大的工程,这洋洋洒洒的一大篇也只是准备了离线资源,这只是安装的一小步,还有很长的步骤要写,心理素质不过关的同学切勿随意模仿。
5. 参考资料
- 离线部署 Openshift Container Platform 4.3 - 1: 准备离线资源
- Chapter 9. Using Operator Lifecycle Manager on restricted networks