openshift4.11 acm with hypershift on baremetal

本文介绍,在openshift4.11上,装 ACM 组件以后,然后通过hypershift的方式,来部署一个单worker节点openshift4.11的控制面托管的集群,在部署的过程中,我们模拟离线的网络环境,并且禁止DHCP,只用静态IP。

This document, will describe how to deploy a single worker node cluster using hypershift, on a ocp 4.11 hub cluster with ACM. During the deployment process, we simulate an offline network environment, and Disable DHCP, only use static IP.

控制面托管(hypershift)模式,之所以诱人,是因为他能够让控制面变成一个namespace,然后托管到中心控制面集群上,这样就能把多个集群的控制面集中到一个中心集群上,能大大提高master节点的计算密度,节约master节点的成本。并且能够把集群master节点的运行维护工作,交给专业团队运维的控制面集群,作为最终用户,只要关心worker节点的运行和维护,而worker节点的运行维护相对来说,是非常简单的。

The control plane hosting (hypershift) mode is attractive because it can turn the control plane into a namespace and then host it on the central cluster, so that the control planes of multiple clusters can be concentrated on one central cluster, which can greatly increase the computing density of the master node and save the cost of the master node. And the operation and maintenance of the master node can be handed over to the central cluster operated by a professional team. As an end user, you only need to care about the operation and maintenance of the worker node, and the operation and maintenance of the worker node is relatively simple.

对比SNO,compact cluster这种master/worker混合部署的方案,hypershift通过剥离控制面业务负载,到中心集群,防止work load对master的不利影响,比如用户部署了一个UPF这种极度消耗CPU的应用,就会无意间影响master,从而让整个集群垮掉。而hypershift就从方案层面,避免了这种情况。而从中心集群的角度来说,他的业务负载种类比较单一,就能刚好的有针对性的优化和运维。

Compared with the master/worker combind deployment mod of SNO and compact cluster, hypershift removes the control plane work load and transfers it to the central cluster to prevent the adverse impact of work load on the master. For example, if a user deploys an application that consumes CPU such as UPF, It will inadvertently affect the master, causing the entire cluster to collapse. And hypershift avoids this situation from the architecture level. From the perspective of the central cluster, its work load type is relatively simple and consistent, and it can be optimized for operation and maintenance by focusing on the control plan.

本次实验,整个流程如下:

  1. 在openshift4上安装ACM组件。
  2. 在ACM上配置cluster, infra env等配置。
  3. MCE通过网络 redfish 协议启动kvm
  4. kvm自动开始集群安装,但是由于kvm+redfish的限制,安装过程中的重启,需要手动停止kvm,配置由硬盘启动,然后再手动启动kvm。
  5. 集群安装完成,保存集群登录信息

In this experiment, the whole process is as follows:

  1. Install the ACM component on openshift4.
  2. Configure cluster, infra env and other configurations on ACM.
  3. MCE starts kvm through network redfish protocol
  4. Kvm automatically starts the cluster installation, but due to the limitation of kvm+redfish, the restart during the installation process requires manually stopping kvm, configuring it to start from the hard disk, and then manually starting kvm.
  5. The cluster installation is complete, save the cluster login information

本次实验的部署架构图: >The deployment architecture diagram of this experiment:

本次实验的网络架构,和服务器, kvm部属架构,是依托之前的一个未完成的实验,工厂模式,虽然工厂模式实验的网络模型比较复杂,但是我们就不重复配置环境了。如果想了解IPI模式如何部署集群,可以参考上述文档。

The network architecture of this experiment, as well as the server and kvm deployment architecture, are based on a previous unfinished experiment, Factory Mode, although the network model of the factory mode experiment is more complicated , but we will not repeat the configuration environment. If you want to know how to deploy clusters in IPI mode, you can refer to the above documents.

参考资料:

reference:

  • https://cloud.redhat.com/blog/how-to-build-bare-metal-hosted-clusters-on-red-hat-advanced-cluster-management-for-kubernetes
  • https://cloud.redhat.com/blog/a-guide-to-red-hat-hypershift-on-bare-metal

静态变量 / static variable

根据factory的安装过程,我们弄了一个 3 node IPI 模式安装的 openshift, 是一个 ipi 的 compact cluster. 我们把这个集群作为hub集群,里面要装ACM组件。

According to the installation process of the factory, we have installed openshift in 3 node IPI mode, which is an ipi compact cluster. We use this cluster as a hub cluster, and ACM components must be installed in it.

以下的参数,是我们用这个hub集群,通过hypershift创建出来新集群的参数,新集群只有1个worker节点。

The following parameters are the parameters of the new cluster created by using this hub cluster through hypershift. The new cluster has only one worker node.

# on helper

# 做一些配置参数定义
INSTALL_IMAGE_REGISTRY=quaylab.infra.wzhlab.top:8443
# PULL_SECRET='{"auths":{"registry.redhat.io": {"auth": "ZHVtbXk6ZHVtbXk=","email": "noemail@localhost"},"registry.ocp4.redhat.ren:5443": {"auth": "ZHVtbXk6ZHVtbXk=","email": "noemail@localhost"},"'${INSTALL_IMAGE_REGISTRY}'": {"auth": "'$( echo -n 'admin:redhatadmin' | openssl base64 )'","email": "noemail@localhost"}}}'
PULL_SECRET=$(cat /data/pull-secret.json)

ACM_DEMO_CLUSTER=edge01

SNO_BASE_DOMAIN=wzhlab.top
SNO_IP=192.168.12.33
SNO_GW=192.168.12.1
SNO_NETMAST=255.255.255.0
SNO_NETMAST_S=24
SNO_HOSTNAME=edge-worker-01
SNO_IF=enp1s0
SNO_IF_MAC=52:54:00:20:a2:01
SNO_DNS=192.168.77.11
SNO_DISK=/dev/vda
SNO_CORE_PWD=redhat

另外,要说明的是,我们发现参考材料里面,对dns的配置不需要那么搞,至少对于单一worker节点来说,apps都指向这个worker节点就可以,api,api-int的域名指向并不重要,因为我们的实验,通过nodeport暴露API server,然后ip地址和端口号被静态的写入了kubelet的配置。

In addition, it should be noted that we found that in the reference materials, the configuration of dns does not need to be so, at least for a single worker node, apps can all point to this worker node, and the domain names of api and api-int are not important. Because of our experiment, the API server is exposed through nodeport, and then the ip address and port number are statically written into the kubelet configuration.

部署ACM / deploy ACM

接下来,我们就部署ACM,我们用最简单的部署模式。

Next, we deploy ACM, we use the simplest deployment mode.

# install operator Advanced Cluster Management for Kubernetes

cat << EOF > ${BASE_DIR}/data/install/acm.subscript.ns.yaml
apiVersion: v1
kind: Namespace
metadata:
  name: open-cluster-management
EOF
oc create -f ${BASE_DIR}/data/install/acm.subscript.ns.yaml

cat << EOF > ${BASE_DIR}/data/install/acm.subscript.yaml
---
apiVersion: operators.coreos.com/v1
kind: OperatorGroup
metadata:
  name:  open-cluster-management-wzh
  namespace: open-cluster-management
spec:
  targetNamespaces:
    - open-cluster-management
---
apiVersion: operators.coreos.com/v1alpha1
kind: Subscription
metadata:
  name: advanced-cluster-management
  namespace: open-cluster-management
spec:
  sourceNamespace: openshift-marketplace
  source: redhat-operators
  channel: release-2.6
  installPlanApproval: Automatic
  name: advanced-cluster-management
EOF

oc create -f ${BASE_DIR}/data/install/acm.subscript.yaml

# RHACM create the MultiClusterHub resource

cat << EOF > ${BASE_DIR}/data/install/acm.mch.mch.yaml
apiVersion: operator.open-cluster-management.io/v1
kind: MultiClusterHub
metadata:
  name: multiclusterhub
  namespace: open-cluster-management
spec: {}
EOF
oc create -f ${BASE_DIR}/data/install/acm.mch.mch.yaml

oc patch mce multiclusterengine --type=merge -p '{"spec":{"overrides":{"components":[{"name":"hypershift-preview","enabled": true}]}}}'

# wait here until you can see the local-cluster
oc get ManagedCluster -A
# NAME            HUB ACCEPTED   MANAGED CLUSTER URLS                  JOINED   AVAILABLE   AGE
# local-cluster   true           https://api.factory.wzhlab.top:6443   True     True        5h22m

cat << EOF > ${BASE_DIR}/data/install/managed-cluster-addon.yaml
apiVersion: addon.open-cluster-management.io/v1alpha1
kind: ManagedClusterAddOn
metadata:
  name: hypershift-addon
  namespace: local-cluster
spec:
  installNamespace: open-cluster-management-agent-addon
EOF
oc create --save-config -f ${BASE_DIR}/data/install/managed-cluster-addon.yaml
# oc delete -f ${BASE_DIR}/data/install/managed-cluster-addon.yaml

oc get managedclusteraddons -A
# NAMESPACE       NAME                          AVAILABLE   DEGRADED   PROGRESSING
# local-cluster   application-manager           True
# local-cluster   cert-policy-controller        True
# local-cluster   cluster-proxy                 True
# local-cluster   config-policy-controller      True
# local-cluster   governance-policy-framework   True
# local-cluster   hypershift-addon              True
# local-cluster   iam-policy-controller         True
# local-cluster   work-manager                  True


装好了是这样,我们能看到装了2个operator, ACM和MCE

This is how it is installed, we can see that 2 operators, ACM and MCE are installed

我们可以通过webUI访问ACM:

We can access ACM through webUI:

https://console-openshift-console.apps.factory.wzhlab.top/multicloud/infrastructure/clusters/managed

可以看到,默认有一个local-cluster,类型是hub,这个就是我们这个装了ACM的集群。

As you can see, there is a local-cluster by default, the type is hub, and this is our cluster with ACM installed.

点击进去,就能看到这个cluster的详细信息。

Click into it, you can see the detailed information of this cluster.

以及这个cluster包含的节点。

And the nodes contained in this cluster.

这个集群装的ACM插件。

The ACM addon installed in this cluster.

新版本的ACM还有一个cluster set的概念,用来分类cluster.

The new version of ACM also has a concept of cluster set, which is used to classify clusters.

在ACM概览页面,能看到这个ACM管理的多云环境。

On the ACM overview page, you can see the multi-cloud environment managed by this ACM.

其他的链接,都没有内容,页面是空的。

Other links have no content and the page is empty.

用hypershift模式部署集群 / Deploy the cluster using hypershift

有过部署assisted install service,并通过AIS来部署SNO的经验,那么通过ACM,用hypershift的模式来部署,就容易理解了,整个过程一样,都是配置ACM里面的assisted install service,然后定义infr env,调用BMC API,来直接挂载iso,并启动主机。不同的地方,以前的实验,之后是定义一个 ClusterDeployment, 现在要定义一个 HostedCluster,这个hosted cluster会帮助我们创建 cluster deployment 。

Having deployed assisted install service and deploying SNO through AIS, it is easy to understand through ACM and deploying in hypershift mode. The whole process is the same as that of configuring assisted install service in ACM, and then defining infr env , call the BMC API to directly mount the iso and start the host. The difference is that in the previous experiment, after above steps, we define a ClusterDeployment, now we need to define a HostedCluster. This hosted cluster will help us create a cluster deployment.

setup ACM for agent service

ACM 2.6 UI 是完全支持hypershift的,但是,我们现在的实验,是为了项目上能定制,所以有些配置要用命令行完成。

ACM 2.6 UI fully supports hypershift, but our current experiment is for project customization, so some configurations need to be done using the command line.

本文就是手动创建yaml,然后一步一步的做,更深入的理解一下hypershift的过程。

This article is to manually create yaml, and then do it step by step to understand the process of hypershift more deeply.


oc project open-cluster-management

oc get hiveconfig hive -n multicluster-engine -o yaml
# ......
# spec: {}
# status:
#   aggregatorClientCAHash: b30ffa769079a2ac0e37e40172084089
#   conditions:
#   - lastProbeTime: "2023-01-13T09:10:10Z"
#     lastTransitionTime: "2023-01-13T09:10:10Z"
#     message: Hive is deployed successfully
#     reason: DeploymentSuccess
#     status: "True"
#     type: Ready
#   configApplied: true
#   observedGeneration: 1

oc patch provisioning provisioning-configuration --type merge -p '{"spec":{"watchAllNamespaces": true }}'

oc get provisioning provisioning-configuration -o yaml
# ......
# spec:
#   preProvisioningOSDownloadURLs: {}
#   provisioningMacAddresses:
#   - 52:54:00:20:a1:01
#   - 52:54:00:20:a1:02
#   - 52:54:00:20:a1:03
#   provisioningNetwork: Disabled
#   provisioningOSDownloadURL: http://192.168.77.11:8080/rhcos-openstack.x86_64.qcow2.gz?sha256=506bb66f8cb407c74061a8201f13e7b1edd44000d944be85eb7a4df7058dcb79
#   watchAllNamespaces: true
# ......

cat << EOF > ${BASE_DIR}/data/install/acm.ocp.release.yaml
apiVersion: hive.openshift.io/v1
kind: ClusterImageSet
metadata:
  name: openshift-v4.11.21
  namespace: multicluster-engine
spec:
  releaseImage: ${INSTALL_IMAGE_REGISTRY}/openshift/release-images:4.11.21-x86_64
EOF
oc create -f ${BASE_DIR}/data/install/acm.ocp.release.yaml
# oc delete -f ${BASE_DIR}/data/install/acm.ocp.release.yaml

oc get ClusterImageSet
# NAME                 RELEASE
# openshift-v4.11.21   quaylab.infra.wzhlab.top:8443/openshift/release-images:4.11.21-x86_64

cat << EOF > ${BASE_DIR}/data/install/acm.cm.asc.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: assisted-service-config
  namespace: multicluster-engine
  labels:
    app: assisted-service
data:
  LOG_LEVEL: "debug"
EOF
oc create -f ${BASE_DIR}/data/install/acm.cm.asc.yaml
# oc delete -f ${BASE_DIR}/data/install/acm.cm.asc.yaml

openshift-install version
# openshift-install 4.11.21
# built from commit d3fb15afdbf1558344ea88a1e134c8e9a011440f
# release image quay.io/openshift-release-dev/ocp-release@sha256:860cc37824074671c4cf76e02d224d243e670d2298e6dab8923ee391fbd0ae1c
# release architecture amd64

openshift-install coreos print-stream-json | jq .architectures.x86_64.artifacts.metal.release -r
# 411.86.202210041459-0

VAR_COREOS_VERSION=`openshift-install coreos print-stream-json | jq .architectures.x86_64.artifacts.metal.release -r`

# the config of CA is important here.
# assisted service will not use cluster's CA config
cat << EOF > ${BASE_DIR}/data/install/acm.mirror.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: hyper1-mirror-config
  namespace: multicluster-engine
  labels:
    app: assisted-service
data:
  ca-bundle.crt: |
$( cat /etc/crts/infra.wzhlab.top.crt | sed 's/^/    /g' )
  registries.conf: |
    unqualified-search-registries = ["registry.access.redhat.com", "docker.io"]

    [[registry]]
      prefix = ""
      location = "quay.io/openshift-release-dev/ocp-release"
      mirror-by-digest-only = true

      [[registry.mirror]]
        location = "${INSTALL_IMAGE_REGISTRY}/openshift/release-images"

    [[registry]]
      prefix = ""
      location = "quay.io/openshift-release-dev/ocp-v4.0-art-dev"
      mirror-by-digest-only = true

      [[registry.mirror]]
        location = "${INSTALL_IMAGE_REGISTRY}/openshift/release"

---
EOF
oc create -f ${BASE_DIR}/data/install/acm.mirror.yaml
# oc delete -f ${BASE_DIR}/data/install/acm.mirror.yaml

cat << EOF > ${BASE_DIR}/data/install/acm.agentservicecofnig.yaml
apiVersion: agent-install.openshift.io/v1beta1
kind: AgentServiceConfig
metadata:
  name: agent
  namespace: multicluster-engine
  ### This is the annotation that injects modifications in the Assisted Service pod
  annotations:
    unsupported.agent-install.openshift.io/assisted-service-configmap: "assisted-service-config"
###
spec:
  databaseStorage:
    accessModes:
      - ReadWriteOnce
    resources:
      requests:
        storage: 40Gi
  filesystemStorage:
    accessModes:
      - ReadWriteOnce
    resources:
      requests:
        storage: 40Gi
  ### This is a ConfigMap that only will make sense on Disconnected environments
  mirrorRegistryRef:
    name: "hyper1-mirror-config"
  ###
  osImages:
    - openshiftVersion: "4.11"
      version: "$VAR_COREOS_VERSION"
      url: "http://192.168.77.11:8080/rhcos-live.x86_64.iso"
      rootFSUrl: "http://192.168.77.11:8080/rhcos-live-rootfs.x86_64.img"
      cpuArchitecture: x86_64
EOF
oc create -f ${BASE_DIR}/data/install/acm.agentservicecofnig.yaml
# oc delete -f ${BASE_DIR}/data/install/acm.agentservicecofnig.yaml

# oc get pod -n multicluster-engine -o json | jq .items[].metadata.name -r | xargs -I DEMO oc logs -n multicluster-engine --prefix=true DEMO | grep 'failed to add release image '

# wait here to see all the status is True
oc get AgentServiceConfig/agent -n multicluster-engine -o yaml  
# ......
# status:
#   conditions:
#   - lastTransitionTime: "2023-01-13T01:38:25Z"
#     message: AgentServiceConfig reconcile completed without error.
#     reason: ReconcileSucceeded
#     status: "True"
#     type: ReconcileCompleted
#   - lastTransitionTime: "2023-01-13T01:40:25Z"
#     message: All the deployments managed by Infrastructure-operator are healthy.
#     reason: DeploymentSucceeded
#     status: "True"
#     type: DeploymentsHealthy

# stop here, and wait the assisted-service pod run into ok status
oc get pod -n multicluster-engine | grep assisted
# assisted-image-service-0                               1/1     Running   0               4m38s
# assisted-service-764cd98cf7-2r2db                      2/2     Running   1 (2m59s ago)   4m40s

create the infra env

infra env这个概念比较古怪,他的意思是,一组相同的主机共享的配置,共享什么配置呢?主要是网络参数配置,启动盘ISO的定制化配置等等。

The concept of infra env is rather weird. What it means is that a group of identical hosts share configurations. What configurations are shared? Mainly network parameter configuration, customized configuration of boot disk ISO, etc.


oc create ns ${ACM_DEMO_CLUSTER}
oc project ${ACM_DEMO_CLUSTER}


cat << EOF > ${BASE_DIR}/data/install/acm.managed.secret.yaml
apiVersion: v1
kind: Secret
metadata:
  name: assisted-deployment-pull-secret
  namespace: ${ACM_DEMO_CLUSTER}
stringData:
  .dockerconfigjson: '$PULL_SECRET'
EOF
oc create -f ${BASE_DIR}/data/install/acm.managed.secret.yaml
# oc delete -f ${BASE_DIR}/data/install/acm.managed.secret.yaml


cat << EOF > ${BASE_DIR}/data/install/acm.nmsc.yaml
apiVersion: agent-install.openshift.io/v1beta1
kind: NMStateConfig
metadata:
 name: ${ACM_DEMO_CLUSTER}
 namespace: ${ACM_DEMO_CLUSTER}
 labels:
   nmstate-conf-cluster-name: ${ACM_DEMO_CLUSTER}
spec:
 config:
   interfaces:
     - name: ${SNO_IF}
       type: ethernet
       state: up
       ipv4:
         enabled: true
         address:
           - ip: ${SNO_IP}
             prefix-length: ${SNO_NETMAST_S}
         dhcp: false
   dns-resolver:
     config:
       server:
         - ${SNO_DNS}
   routes:
     config:
       - destination: 0.0.0.0/0
         next-hop-address: ${SNO_GW}
         next-hop-interface: ${SNO_IF}
         table-id: 254
 interfaces:
   - name: "${SNO_IF}" 
     macAddress: ${SNO_IF_MAC}
EOF
oc create -f ${BASE_DIR}/data/install/acm.nmsc.yaml
# oc delete -f ${BASE_DIR}/data/install/acm.nmsc.yaml

oc get NMStateConfig/${ACM_DEMO_CLUSTER} -n ${ACM_DEMO_CLUSTER}
# NAME     AGE
# edge01   3h30m


cat << EOF > ${BASE_DIR}/data/install/acm.infraenv.yaml
apiVersion: agent-install.openshift.io/v1beta1
kind: InfraEnv
metadata:
  name: ${ACM_DEMO_CLUSTER}
  namespace: ${ACM_DEMO_CLUSTER}
  labels:
    agentclusterinstalls.extensions.hive.openshift.io/location: ${ACM_DEMO_CLUSTER}
    networkType: static
spec:
  agentLabels:
    'agentclusterinstalls.extensions.hive.openshift.io/location': ${ACM_DEMO_CLUSTER}
  additionalNTPSources:
    - 192.168.77.11
  # clusterRef:
  #   name: ${ACM_DEMO_CLUSTER}
  #   namespace: ${ACM_DEMO_CLUSTER}-${ACM_DEMO_CLUSTER}
  sshAuthorizedKey: "$(< ~/.ssh/id_rsa.pub)"
  pullSecretRef:
    name: assisted-deployment-pull-secret
  # ignitionConfigOverride: '${VAR_IGNITION}'
  nmStateConfigLabelSelector:
    matchLabels:
      nmstate-conf-cluster-name: ${ACM_DEMO_CLUSTER}
  # imageType: "full-iso"
EOF
oc create -f ${BASE_DIR}/data/install/acm.infraenv.yaml
# oc delete -f ${BASE_DIR}/data/install/acm.infraenv.yaml

oc get infraenv/${ACM_DEMO_CLUSTER} -n ${ACM_DEMO_CLUSTER} -o yaml 
# additionalNTPSources:
#   - 192.168.77.11
# agentLabels:
#   agentclusterinstalls.extensions.hive.openshift.io/location: edge01
# cpuArchitecture: x86_64
# ipxeScriptType: DiscoveryImageAlways
# nmStateConfigLabelSelector:
#   matchLabels:
#     infraenvs.agent-install.openshift.io: edge01
# pullSecretRef:
#   name: pullsecret-edge01
# sshAuthorizedKey: ssh-rsa .....

oc get infraenv/${ACM_DEMO_CLUSTER} -n ${ACM_DEMO_CLUSTER} -o json | jq .status
# {
#   "agentLabelSelector": {
#     "matchLabels": {
#       "infraenvs.agent-install.openshift.io": "edge01"
#     }
#   },
#   "bootArtifacts": {
#     "initrd": "https://assisted-image-service-multicluster-engine.apps.factory.wzhlab.top/images/c70485f3-0b12-437f-9efe-85b17f0c627f/pxe-initrd?api_key=eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJpbmZyYV9lbnZfaWQiOiJjNzA0ODVmMy0wYjEyLTQzN2YtOWVmZS04NWIxN2YwYzYyN2YifQ.rrkRFxLVcMjEw16W3brxl_YCxHtJtUu-h0KMHcvj3DO701_ZPUM6cDg765Q02CviGSNcSTmu0ic5g06AkU0Zzg&arch=x86_64&version=4.11",
#     "ipxeScript": "https://assisted-service-multicluster-engine.apps.factory.wzhlab.top/api/assisted-install/v2/infra-envs/c70485f3-0b12-437f-9efe-85b17f0c627f/downloads/files?api_key=eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJpbmZyYV9lbnZfaWQiOiJjNzA0ODVmMy0wYjEyLTQzN2YtOWVmZS04NWIxN2YwYzYyN2YifQ.3j_oKrmfOVQn85v2S3laLojUKaCTRqgkv_aSBPo-z_7k8-n2swb2m9aNT3uPr3CEstV4UVurkYwShtawFed0Cg&file_name=ipxe-script",
#     "kernel": "https://assisted-image-service-multicluster-engine.apps.factory.wzhlab.top/boot-artifacts/kernel?arch=x86_64&version=4.11",
#     "rootfs": "https://assisted-image-service-multicluster-engine.apps.factory.wzhlab.top/boot-artifacts/rootfs?arch=x86_64&version=4.11"
#   },
#   "conditions": [
#     {
#       "lastTransitionTime": "2023-01-13T03:15:17Z",
#       "message": "Image has been created",
#       "reason": "ImageCreated",
#       "status": "True",
#       "type": "ImageCreated"
#     }
#   ],
#   "createdTime": "2023-01-13T03:15:16Z",
#   "debugInfo": {
#     "eventsURL": "https://assisted-service-multicluster-engine.apps.factory.wzhlab.top/api/assisted-install/v2/events?api_key=eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJpbmZyYV9lbnZfaWQiOiJjNzA0ODVmMy0wYjEyLTQzN2YtOWVmZS04NWIxN2YwYzYyN2YifQ.W_KCQgx4SwgbErK6eiyh7EmxPb9L8KKawXLOWPgBoPxVPH79QXq5wb-X5DT48b6qBlk3xk-F7MCT_bEG1f30Ww&infra_env_id=c70485f3-0b12-437f-9efe-85b17f0c627f"
#   },
#   "isoDownloadURL": "https://assisted-image-service-multicluster-engine.apps.factory.wzhlab.top/images/c70485f3-0b12-437f-9efe-85b17f0c627f?api_key=eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJpbmZyYV9lbnZfaWQiOiJjNzA0ODVmMy0wYjEyLTQzN2YtOWVmZS04NWIxN2YwYzYyN2YifQ.4FqFWSqfYijmGGWAKopqHIiKghDZBZ2NAqTY1hmUhwNfTzuKlFLZ2pDZAevAxtmf7aN96-6UCeNewIfqoLzPVQ&arch=x86_64&type=minimal-iso&version=4.11"
# }

# VAR_ISO=`oc get infraenv ${ACM_DEMO_CLUSTER} -n ${ACM_DEMO_CLUSTER} -o jsonpath={.status.isoDownloadURL}`

# cd /data/install/
# wget --no-check-certificate -O acm.demo1.iso $VAR_ISO

定义好了infra env,我们就能在ACM的web界面上看到啦。

After defining the infra env, we can see it on the ACM web interface.

infra env的详细信息,似乎没什么有用的,就是一些普通的配置。

The details of infra env seem to be of little use, just some common configurations.

在infra env的host配置里面,我们看到,现在还没有一个主机添加进来。

In the host configuration of infra env, we see that no host has been added yet.

add host to infra env

我们接下来要做的,就是给infra env添加主机,从web界面上看,大概有3种添加方法,一个是手动挂载discovery ISO,然后在infra env里面自动发现,一个是通过web界面,配置BMC等参数,来添加host,最后一种,是通过上传yaml配置文件来完成导入host的操作。

The next thing we need to do is to add hosts to the infra env. From the web interface, there are about 3 ways to add them. One is to manually mount the discovery ISO, and then automatically discover it in the infra env. The other is to configure it through the web interface, by adding BMC and other parameters to add the host, and the last one is to complete the operation of importing the host by uploading the yaml configuration file.

本文是通过命令行的方式来添加,那么就类似界面上最后一种,通过上传yaml的方式来导入host。

This article is added through the command line, so it is similar to the last one on the web interface, importing host by uploading yaml.

# lets confirm that the metal3 component is ready
# then we can use ocp to manage the baremetal
oc get pod -A | grep metal3
# openshift-machine-api                              metal3-8666f4cf4d-2bkfb                                           5/5     Running     5               12h
# openshift-machine-api                              metal3-image-cache-8jhtr                                          1/1     Running     1               13h
# openshift-machine-api                              metal3-image-cache-9jfs7                                          1/1     Running     1               13h
# openshift-machine-api                              metal3-image-cache-fl545                                          1/1     Running     1               13h
# openshift-machine-api                              metal3-image-customization-868d87999b-x2mnw                       1/1     Running     1               13h


cat << EOF > ${BASE_DIR}/data/install/acm.demo.secret.bmc.yaml
apiVersion: v1
kind: Secret
metadata:
  name: ${ACM_DEMO_CLUSTER}-bmc-master-01
  namespace: ${ACM_DEMO_CLUSTER}
data:
  password: $(echo password | base64)
  username: $(echo admin | base64)
type: Opaque
EOF
oc create -f ${BASE_DIR}/data/install/acm.demo.secret.bmc.yaml
# oc delete -f ${BASE_DIR}/data/install/acm.demo.secret.bmc.yaml

cat << EOF > ${BASE_DIR}/data/install/acm.demo.bmh.master.yaml
apiVersion: metal3.io/v1alpha1
kind: BareMetalHost
metadata:
  name: ${ACM_DEMO_CLUSTER}-${SNO_HOSTNAME}
  namespace: ${ACM_DEMO_CLUSTER}
  labels:
    infraenvs.agent-install.openshift.io: "${ACM_DEMO_CLUSTER}"
  annotations:
    ## Disable the Introspection
    inspect.metal3.io: disabled
    ## Set Static Hostname
    bmac.agent-install.openshift.io/hostname: "${SNO_HOSTNAME}"
    ## Set Static Role, auto-assign?
    bmac.agent-install.openshift.io/role: "worker"
spec:
  online: true
  bmc:
    address: redfish-virtualmedia://192.168.77.101:8000/redfish/v1/Systems/$(cat /data/install/vm.list.* | grep ocp4-ipi-edge-master-01 | awk '{print $1}')
    credentialsName: ${ACM_DEMO_CLUSTER}-bmc-master-01
    disableCertificateVerification: true
  bootMACAddress: $(cat /data/install/mac.list.* | grep ocp4-ipi-edge-master-01 | awk '{print $2}')
  automatedCleaningMode: disabled
EOF
oc create -f ${BASE_DIR}/data/install/acm.demo.bmh.master.yaml
# oc delete -f ${BASE_DIR}/data/install/acm.demo.bmh.master.yaml

oc get BareMetalHost/${ACM_DEMO_CLUSTER}-${SNO_HOSTNAME} -n ${ACM_DEMO_CLUSTER} -o yaml
# ......
# metadata:
#   annotations:
#     bmac.agent-install.openshift.io/hostname: edge-worker-01
#     bmac.agent-install.openshift.io/role: worker
#     inspect.metal3.io: disabled
#   creationTimestamp: "2023-01-18T15:08:22Z"
#   finalizers:
#   - baremetalhost.metal3.io
#   generation: 2
#   labels:
#     infraenvs.agent-install.openshift.io: edge01
#   name: edge01-edge-worker-01
#   namespace: edge01
#   resourceVersion: "111945"
#   uid: b21c5b31-c28c-4b43-b8c1-a0ba80581e60
# spec:
#   automatedCleaningMode: disabled
#   bmc:
#     address: redfish-virtualmedia://192.168.77.101:8000/redfish/v1/Systems/a176e428-fea7-43ff-95c7-a927514227ed
#     credentialsName: edge01-bmc-master-01
#     disableCertificateVerification: true
#   bootMACAddress: 52:54:00:20:a2:01
#   image:
#     format: live-iso
#     url: https://assisted-image-service-multicluster-engine.apps.factory.wzhlab.top/images/2e9fa857-17c6-493f-8030-b4cb2b736fd1?api_key=eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJpbmZyYV9lbnZfaWQiOiIyZTlmYTg1Ny0xN2M2LTQ5M2YtODAzMC1iNGNiMmI3MzZmZDEifQ.yF_UwtoDKWdc6dYUkcYpNDOWzLt_jVS1ZSqU-SLzZq4QZwt6v7x5Hl8azM3S9THX0xi0K-ert3gqVLbNV62s9Q&arch=x86_64&type=minimal-iso&version=4.11
#   online: true
# ......

配置完成以后,在web界面上,就能看到这个主机啦,其实在openshift的界面里面,也能看到这个baremetal,我们看到系统正在试图配置这个主机。

After the configuration is complete, you can see this host on the web interface. In fact, you can also see this baremetal in the openshift interface. We can see that the system is trying to configure this host.

其实在目标kvm上,是启动了一个定制的coreos live cd,启动了以后,运行了一个服务,他会搜集本机的信息,然后上报,上述操作顺利的话,我们就能在界面上看到主机信息更新了。

In fact, on the target kvm, a customized coreos live cd is started. After starting, a service is run, which will collect the information of the machine and then report it. If the above operations are successful, we can see the host on the interface. The information has been updated.

这里面的host,在后台对应的是agent的配置,我们可以通过命令行查看agent对应的详细信息。

The host here corresponds to the configuration of the agent CR in the background, and we can view the detailed information corresponding to the agent through the command line.


oc get agent -n ${ACM_DEMO_CLUSTER}
# NAME                                   CLUSTER   APPROVED   ROLE     STAGE
# a176e428-fea7-43ff-95c7-a927514227ed             true       worker

oc get agent/a176e428-fea7-43ff-95c7-a927514227ed -n ${ACM_DEMO_CLUSTER} -o yaml 
# ......
# metadata:
#   annotations:
#     inventory.agent-install.openshift.io/version: "0.1"
#   creationTimestamp: "2023-01-18T15:11:47Z"
#   finalizers:
#   - agent.agent-install.openshift.io/ai-deprovision
#   generation: 2
#   labels:
#     agent-install.openshift.io/bmh: edge01-edge-worker-01
#     agent-install.openshift.io/clusterdeployment-namespace: ""
#     agentclusterinstalls.extensions.hive.openshift.io/location: edge01
#     infraenvs.agent-install.openshift.io: edge01
#     inventory.agent-install.openshift.io/cpu-architecture: x86_64
#     inventory.agent-install.openshift.io/cpu-virtenabled: "false"
#     inventory.agent-install.openshift.io/host-isvirtual: "true"
#     inventory.agent-install.openshift.io/host-manufacturer: RedHat
#     inventory.agent-install.openshift.io/host-productname: KVM
#     inventory.agent-install.openshift.io/storage-hasnonrotationaldisk: "false"
#   name: a176e428-fea7-43ff-95c7-a927514227ed
#   namespace: edge01
#   resourceVersion: "117085"
#   uid: c410d01b-1bdb-4ade-b5e6-630aadf634b3
# spec:
#   approved: true
#   hostname: edge-worker-01
#   role: worker
# ......

begin to create new cluster - control plan

准备工作都做好了,我们开始创建一个hypershift管理的,托管控制面的新集群。

With everything in place, we started creating a new hypershift-managed cluster hosting the control plane.


cat << EOF > ${BASE_DIR}/data/install/capi-role-${ACM_DEMO_CLUSTER}.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: capi-provider-role
  namespace: ${ACM_DEMO_CLUSTER}
rules:
- apiGroups:
  - agent-install.openshift.io
  resources:
  - agents
  verbs:
  - '*'
EOF
oc create --save-config -f ${BASE_DIR}/data/install/capi-role-${ACM_DEMO_CLUSTER}.yaml

# nodepool -> config -> config map -> machine config
# we have container image cache, so we add customize config through machine config
cat << EOF > ${BASE_DIR}/data/install/hyper.mirror.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: hyper-mirror-config
  namespace: ${ACM_DEMO_CLUSTER}
data:
  config: |
$( cat /data/ocp4/99-worker-container-registries.yaml | sed 's/^/    /g' )

---
EOF
oc create -f ${BASE_DIR}/data/install/hyper.mirror.yaml
# oc delete -f ${BASE_DIR}/data/install/hyper.mirror.yaml


cat << EOF > ${BASE_DIR}/data/sno/install.images.bu
variant: openshift
version: 4.9.0
metadata:
  labels:
    machineconfiguration.openshift.io/role: master
  name: 99-zzz-master-install-images
storage:
  files:
    - path: /etc/containers/registries.conf.d/base.registries.conf
      overwrite: true
      contents:
        inline: |
          unqualified-search-registries = ["registry.access.redhat.com", "docker.io"]
          short-name-mode = ""

          [[registry]]
            prefix = ""
            location = "quay.io/openshift-release-dev/ocp-release"
            mirror-by-digest-only = true

            [[registry.mirror]]
              location = "${INSTALL_IMAGE_REGISTRY}/openshift/release-images"

          [[registry]]
            prefix = ""
            location = "quay.io/openshift-release-dev/ocp-v4.0-art-dev"
            mirror-by-digest-only = true

            [[registry.mirror]]
              location = "${INSTALL_IMAGE_REGISTRY}/openshift/release"

EOF
butane ${BASE_DIR}/data/sno/install.images.bu > ${BASE_DIR}/data/sno/disconnected/99-zzz-master-install-images.yaml

cat << EOF > ${BASE_DIR}/data/install/hyper.mirror.main.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: hyper-mirror-main-config
  namespace: ${ACM_DEMO_CLUSTER}
data:
  config: |
$( cat ${BASE_DIR}/data/sno/disconnected/99-zzz-master-install-images.yaml | sed 's/^/    /g' )

---
EOF
oc create -f ${BASE_DIR}/data/install/hyper.mirror.main.yaml



cat << EOF > ${BASE_DIR}/data/install/hosted-cluster-${ACM_DEMO_CLUSTER}.yaml 
---
apiVersion: hypershift.openshift.io/v1alpha1
kind: HostedCluster
metadata:
  name: ${ACM_DEMO_CLUSTER}
  namespace: ${ACM_DEMO_CLUSTER}
  labels:
    "cluster.open-cluster-management.io/clusterset": 'default'
spec:
  release:
    image: ${INSTALL_IMAGE_REGISTRY}/openshift/release-images:4.11.21-x86_64
  pullSecret:
    name: pullsecret-cluster-${ACM_DEMO_CLUSTER}
  sshKey:
    name: sshkey-cluster-${ACM_DEMO_CLUSTER}
  networking:
    podCIDR: 10.132.0.0/14
    serviceCIDR: 172.31.0.0/16
    machineCIDR: 192.168.12.0/24
    networkType: OpenShiftSDN
  platform:
    type: Agent
    agent:
      agentNamespace: ${ACM_DEMO_CLUSTER}
  infraID: ${ACM_DEMO_CLUSTER}
  dns:
    baseDomain: '$SNO_BASE_DOMAIN'
  services:
  - service: APIServer
    servicePublishingStrategy:
        nodePort:
          address: 192.168.12.23
          port: 30000
        type: NodePort
  - service: OAuthServer
    servicePublishingStrategy:
      type: Route
  - service: OIDC
    servicePublishingStrategy:
      type: Route
  - service: Konnectivity
    servicePublishingStrategy:
      type: Route
  - service: Ignition
    servicePublishingStrategy:
      type: Route
---
apiVersion: v1
kind: Secret
metadata:
  name: pullsecret-cluster-${ACM_DEMO_CLUSTER}
  namespace: ${ACM_DEMO_CLUSTER}
stringData:
  '.dockerconfigjson': '$PULL_SECRET'
type: kubernetes.io/dockerconfigjson
---
apiVersion: v1
kind: Secret
metadata:
  name: sshkey-cluster-${ACM_DEMO_CLUSTER}
  namespace: ${ACM_DEMO_CLUSTER}
stringData:
  id_rsa.pub: '$(< ~/.ssh/id_rsa.pub)'
---
apiVersion: hypershift.openshift.io/v1alpha1
kind: NodePool
metadata:
  name: 'nodepool-${ACM_DEMO_CLUSTER}-01'
  namespace: ${ACM_DEMO_CLUSTER}
spec:
  clusterName: ${ACM_DEMO_CLUSTER}
  config:
    - name: hyper-mirror-config
    - name: hyper-mirror-main-config
  replicas: 1
  management:
    autoRepair: false
    upgradeType: InPlace
  platform:
    type: Agent
    agent:
      agentLabelSelector:
        matchLabels: {}
  release:
    image: ${INSTALL_IMAGE_REGISTRY}/openshift/release-images:4.11.21-x86_64
---
apiVersion: cluster.open-cluster-management.io/v1
kind: ManagedCluster
metadata:
  labels:
    cloud: hypershift
    name: ${ACM_DEMO_CLUSTER}
    cluster.open-cluster-management.io/clusterset: 'default'
  name: ${ACM_DEMO_CLUSTER}
spec:
  hubAcceptsClient: true
---
apiVersion: agent.open-cluster-management.io/v1
kind: KlusterletAddonConfig
metadata:
  name: ${ACM_DEMO_CLUSTER}
  namespace: ${ACM_DEMO_CLUSTER}
spec:
  clusterName: ${ACM_DEMO_CLUSTER}
  clusterNamespace: ${ACM_DEMO_CLUSTER}
  clusterLabels:
    cloud: ai-hypershift
  applicationManager:
    enabled: true
  policyController:
    enabled: true
  searchCollector:
    enabled: true
  certPolicyController:
    enabled: true
  iamPolicyController:
    enabled: true
EOF

oc create --save-config -f ${BASE_DIR}/data/install/hosted-cluster-${ACM_DEMO_CLUSTER}.yaml 
# oc delete -f ${BASE_DIR}/data/install/hosted-cluster-${ACM_DEMO_CLUSTER}.yaml 

oc get HostedCluster -A
# NAMESPACE   NAME     VERSION   KUBECONFIG                PROGRESS   AVAILABLE   PROGRESSING   MESSAGE
# edge01      edge01             edge01-admin-kubeconfig   Partial    True        False         The hosted control plane is available

oc get HostedCluster/${ACM_DEMO_CLUSTER} -n ${ACM_DEMO_CLUSTER} -o yaml | yq .spec
# autoscaling: {}
# clusterID: 8c0fb18c-22dd-4fb9-a2a6-420ee19d9f8a
# controllerAvailabilityPolicy: SingleReplica
# dns:
#   baseDomain: wzhlab.top
# etcd:
#   managed:
#     storage:
#       persistentVolume:
#         size: 4Gi
#       type: PersistentVolume
#   managementType: Managed
# fips: false
# infraID: edge01
# infrastructureAvailabilityPolicy: SingleReplica
# issuerURL: https://kubernetes.default.svc
# networking:
#   clusterNetwork:
#     - cidr: 10.132.0.0/14
#   machineNetwork:
#     - cidr: 192.168.12.0/24
#   networkType: OVNKubernetes
#   serviceNetwork:
#     - cidr: 172.31.0.0/16
# olmCatalogPlacement: management
# platform:
#   agent:
#     agentNamespace: edge01
#   type: Agent
# pullSecret:
#   name: pullsecret-cluster-edge01
# release:
#   image: quaylab.infra.wzhlab.top:8443/openshift/release-images:4.11.21-x86_64
# services:
#   - service: APIServer
#     servicePublishingStrategy:
#       nodePort:
#         address: 192.168.12.23
#         port: 30000
#       type: NodePort
#   - service: OAuthServer
#     servicePublishingStrategy:
#       type: Route
#   - service: OIDC
#     servicePublishingStrategy:
#       type: Route
#   - service: Konnectivity
#     servicePublishingStrategy:
#       type: Route
#   - service: Ignition
#     servicePublishingStrategy:
#       type: Route
# sshKey:
#   name: sshkey-cluster-edge01

oc get clusterdeployment -A
# NAMESPACE       NAME     INFRAID                                PLATFORM          REGION   VERSION   CLUSTERTYPE   PROVISIONSTATUS   POWERSTATE   AGE
# edge01-edge01   edge01   39d863f0-57f8-4ff4-a2b5-61e3e654c4db   agent-baremetal            4.11.21                 Provisioned                    122m

oc get clusterdeployment/${ACM_DEMO_CLUSTER} -n ${ACM_DEMO_CLUSTER}-${ACM_DEMO_CLUSTER} -o yaml | yq .spec
# baseDomain: wzhlab.top
# clusterInstallRef:
#   group: extensions.hive.openshift.io
#   kind: AgentClusterInstall
#   name: edge01
#   version: v1beta1
# clusterMetadata:
#   adminKubeconfigSecretRef:
#     name: admin-kubeconfig
#   clusterID: 28c54029-b032-4c48-8486-deb1dabe8ea8
#   infraID: 28c54029-b032-4c48-8486-deb1dabe8ea8
# clusterName: edge01
# controlPlaneConfig:
#   servingCertificates: {}
# installed: true
# platform:
#   agentBareMetal:
#     agentSelector: {}
# pullSecretRef:
#   name: pull-secret

oc get AgentClusterInstall -A
# NAMESPACE       NAME     CLUSTER   STATE
# edge01-edge01   edge01   edge01    adding-hosts

oc get AgentClusterInstall/${ACM_DEMO_CLUSTER} -n ${ACM_DEMO_CLUSTER}-${ACM_DEMO_CLUSTER} -o yaml | yq .spec
# clusterDeploymentRef:
#   name: edge01
# ignitionEndpoint:
#   caCertificateReference:
#     name: ignition-server-ca-cert
#     namespace: edge01-edge01
#   url: https://ignition-server-edge01-edge01.apps.factory.wzhlab.top
# networking:
#   userManagedNetworking: true
# provisionRequirements:
#   controlPlaneAgents: 3

oc get agent -n ${ACM_DEMO_CLUSTER}
# NAME                                   CLUSTER   APPROVED   ROLE     STAGE
# a176e428-fea7-43ff-95c7-a927514227ed             true       worker

oc get agent/a176e428-fea7-43ff-95c7-a927514227ed -n ${ACM_DEMO_CLUSTER} -o yaml | yq .spec
# approved: true
# clusterDeploymentName:
#   name: edge01
#   namespace: edge01-edge01
# hostname: edge-worker-01
# ignitionEndpointTokenReference:
#   name: agent-user-data-nodepool-edge01-01-e3fdfbf8
#   namespace: edge01-edge01
# machineConfigPool: ignition
# role: worker


# wait here, and check the control plan creation.
oc get pod -n ${ACM_DEMO_CLUSTER}-${ACM_DEMO_CLUSTER}
# NAME                                             READY   STATUS    RESTARTS   AGE
# capi-provider-87b88465c-zgrx2                    1/1     Running   0          10m
# catalog-operator-7dcf86576f-vffl6                2/2     Running   0          7m33s
# certified-operators-catalog-7b4bdcb679-25gls     1/1     Running   0          7m39s
# cluster-api-5984dc678b-46ms7                     1/1     Running   0          10m
# cluster-autoscaler-5cd6b96d55-nzw4x              1/1     Running   0          9m33s
# cluster-network-operator-547f6988f4-6q2f2        1/1     Running   0          7m49s
# cluster-policy-controller-857bf8594f-9dhhj       1/1     Running   0          7m56s
# cluster-version-operator-85f5fd968f-rhchm        1/1     Running   0          7m55s
# community-operators-catalog-f6d797bc-87f9k       1/1     Running   0          7m38s
# control-plane-operator-65444fdff8-fzhvb          1/1     Running   0          10m
# etcd-0                                           1/1     Running   0          9m36s
# hosted-cluster-config-operator-cb8bd76f7-wvtfl   1/1     Running   0          7m41s
# ignition-server-57fbf98b8b-wvkv2                 1/1     Running   0          9m26s
# ingress-operator-594bdd5d6d-2t6kw                2/2     Running   0          7m46s
# konnectivity-agent-67bd878b88-bwxcp              1/1     Running   0          9m35s
# konnectivity-server-764ffdb8fd-xgxqq             1/1     Running   0          9m36s
# kube-apiserver-7f85bd5d7f-cvd7r                  3/3     Running   0          9m34s
# kube-controller-manager-7bd7ff884f-2c4jr         1/1     Running   0          6m35s
# kube-scheduler-68858b678d-jlpmx                  1/1     Running   0          8m30s
# machine-approver-c6b6f6ff8-jh445                 1/1     Running   0          9m33s
# oauth-openshift-5bb59d5596-55mtw                 2/2     Running   0          6m15s
# olm-operator-949f6f76b-r8kkz                     2/2     Running   0          7m32s
# openshift-apiserver-5ddbbd9847-n2824             2/2     Running   0          6m35s
# openshift-controller-manager-7cdd5bcc7b-p7kfb    1/1     Running   0          7m56s
# openshift-oauth-apiserver-8c76cb9b9-t9nts        1/1     Running   0          7m58s
# packageserver-58d5b997b9-wdn58                   2/2     Running   0          7m32s
# redhat-marketplace-catalog-85748dc79-tl8sr       1/1     Running   0          7m38s
# redhat-operators-catalog-74849cb9d6-9bg49        1/1     Running   0          7m38s


oc get pod -n ${ACM_DEMO_CLUSTER}-${ACM_DEMO_CLUSTER} | tail -n +2 | wc -l
# 28

配置导入以后,我们就能看到多了一个集群edge01, 类型是hosted.

After the configuration is imported, we can see that there is an additional cluster edge01, the type is hosted.

安装过程稍微有一点时间,期间,我们能看到集群状态,nodepool状态有所变化。

The installation process takes a little while. During this period, we can see the status of the cluster and the status of the nodepool has changed.

我们还能看到hub集群上,有了一个edge01-edge01的namespace,里面有集群控制面的pod,其中就有我们熟悉的etcd, api-server

We can also see that on the hub cluster, there is an edge01-edge01 namespace, which contains the pods of the cluster control plane, including the familiar etcd and api-server

import the hosted cluster

经过一段时间,新集群就安装成功了,但是页面上提示,需要手动导入。我们复制页面上的命令,并到helper上,运行这2个命令,他们是登录到hosted control plan,然后配置一些CR进去

After a period of time, the new cluster is installed successfully, but the page prompts that it needs to be imported manually. We copy the commands on the page, and run these two commands on the helper node, they log in to the hosted control plan, and then configure some CR/CRD into it

# on helper

# copy past the 1st command
oc login https://192.168.12.23:30000 -u kubeadmin -p z2I9i-BZF8L-sYvUC-47c7x

# copy past the 2nd command
# it is too large, we will omit most of them
echo "Ci0tLQphc............" | base64 -d | oc create -f - || test $? -eq 0 && sleep 2 && echo "Ci0tLQphcGlWZ............" | base64 -d | oc apply -f - || echo "VGhlIGNsdXN..............." | base64 -d
# namespace/open-cluster-management-agent created
# serviceaccount/klusterlet created
# clusterrole.rbac.authorization.k8s.io/klusterlet created
# clusterrole.rbac.authorization.k8s.io/open-cluster-management:klusterlet-admin-aggregate-clusterrole created
# clusterrolebinding.rbac.authorization.k8s.io/klusterlet created
# Warning: would violate PodSecurity "restricted:latest": allowPrivilegeEscalation != false (container "klusterlet" must set securityContext.allowPrivilegeEscalation=false), unrestricted capabilities (container "klusterlet" must set securityContext.capabilities.drop=["ALL"]), runAsNonRoot != true (pod or container "klusterlet" must set securityContext.runAsNonRoot=true), seccompProfile (pod or container "klusterlet" must set securityContext.seccompProfile.type to "RuntimeDefault" or "Localhost")
# deployment.apps/klusterlet created
# secret/bootstrap-hub-kubeconfig created
# klusterlet.operator.open-cluster-management.io/klusterlet created

# lets decode the first 2 base64 content, the 3rd one is just a message.

我们很好奇到底导入了什么东西,那让我们解码看看。第一个导入hosted control plan的yaml是一个CRD。

We are curious about what is being imported, so let's decode it. The first yaml imported into the hosted control plan is a CRD.

---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  name: klusterlets.operator.open-cluster-management.io
spec:
  conversion:
    strategy: None
  group: operator.open-cluster-management.io
  names:
    kind: Klusterlet
    listKind: KlusterletList
    plural: klusterlets
    singular: klusterlet
  scope: Cluster
  preserveUnknownFields: false
  versions:
    - name: v1
      schema:
        openAPIV3Schema:
          description: Klusterlet represents controllers to install the resources for a managed cluster. When configured, the Klusterlet requires a secret named bootstrap-hub-kubeconfig in the agent namespace to allow API requests to the hub for the registration protocol. In Hosted mode, the Klusterlet requires an additional secret named external-managed-kubeconfig in the agent namespace to allow API requests to the managed cluster for resources installation.
          type: object
          properties:
            apiVersion:
              description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
              type: string
            kind:
              description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
              type: string
            metadata:
              type: object
            spec:
              description: Spec represents the desired deployment configuration of Klusterlet agent.
              type: object
              properties:
                clusterName:
                  description: ClusterName is the name of the managed cluster to be created on hub. The Klusterlet agent generates a random name if it is not set, or discovers the appropriate cluster name on OpenShift.
                  type: string
                deployOption:
                  description: DeployOption contains the options of deploying a klusterlet
                  type: object
                  properties:
                    mode:
                      description: 'Mode can be Default or Hosted. It is Default mode if not specified In Default mode, all klusterlet related resources are deployed on the managed cluster. In Hosted mode, only crd and configurations are installed on the spoke/managed cluster. Controllers run in another cluster (defined as management-cluster) and connect to the mangaged cluster with the kubeconfig in secret of "external-managed-kubeconfig"(a kubeconfig of managed-cluster with cluster-admin permission). Note: Do not modify the Mode field once it''s applied.'
                      type: string
                externalServerURLs:
                  description: ExternalServerURLs represents the a list of apiserver urls and ca bundles that is accessible externally If it is set empty, managed cluster has no externally accessible url that hub cluster can visit.
                  type: array
                  items:
                    description: ServerURL represents the apiserver url and ca bundle that is accessible externally
                    type: object
                    properties:
                      caBundle:
                        description: CABundle is the ca bundle to connect to apiserver of the managed cluster. System certs are used if it is not set.
                        type: string
                        format: byte
                      url:
                        description: URL is the url of apiserver endpoint of the managed cluster.
                        type: string
                namespace:
                  description: 'Namespace is the namespace to deploy the agent. The namespace must have a prefix of "open-cluster-management-", and if it is not set, the namespace of "open-cluster-management-agent" is used to deploy agent. Note: in Detach mode, this field will be **ignored**, the agent will be deployed to the namespace with the same name as klusterlet.'
                  type: string
                nodePlacement:
                  description: NodePlacement enables explicit control over the scheduling of the deployed pods.
                  type: object
                  properties:
                    nodeSelector:
                      description: NodeSelector defines which Nodes the Pods are scheduled on. The default is an empty list.
                      type: object
                      additionalProperties:
                        type: string
                    tolerations:
                      description: Tolerations is attached by pods to tolerate any taint that matches the triple <key,value,effect> using the matching operator <operator>. The default is an empty list.
                      type: array
                      items:
                        description: The pod this Toleration is attached to tolerates any taint that matches the triple <key,value,effect> using the matching operator <operator>.
                        type: object
                        properties:
                          effect:
                            description: Effect indicates the taint effect to match. Empty means match all taint effects. When specified, allowed values are NoSchedule, PreferNoSchedule and NoExecute.
                            type: string
                          key:
                            description: Key is the taint key that the toleration applies to. Empty means match all taint keys. If the key is empty, operator must be Exists; this combination means to match all values and all keys.
                            type: string
                          operator:
                            description: Operator represents a key's relationship to the value. Valid operators are Exists and Equal. Defaults to Equal. Exists is equivalent to wildcard for value, so that a pod can tolerate all taints of a particular category.
                            type: string
                          tolerationSeconds:
                            description: TolerationSeconds represents the period of time the toleration (which must be of effect NoExecute, otherwise this field is ignored) tolerates the taint. By default, it is not set, which means tolerate the taint forever (do not evict). Zero and negative values will be treated as 0 (evict immediately) by the system.
                            type: integer
                            format: int64
                          value:
                            description: Value is the taint value the toleration matches to. If the operator is Exists, the value should be empty, otherwise just a regular string.
                            type: string
                registrationImagePullSpec:
                  description: RegistrationImagePullSpec represents the desired image configuration of registration agent. quay.io/open-cluster-management.io/registration:latest will be used if unspecified.
                  type: string
                workImagePullSpec:
                  description: WorkImagePullSpec represents the desired image configuration of work agent. quay.io/open-cluster-management.io/work:latest will be used if unspecified.
                  type: string
            status:
              description: Status represents the current status of Klusterlet agent.
              type: object
              properties:
                conditions:
                  description: 'Conditions contain the different condition statuses for this Klusterlet. Valid condition types are: Applied: Components have been applied in the managed cluster. Available: Components in the managed cluster are available and ready to serve. Progressing: Components in the managed cluster are in a transitioning state. Degraded: Components in the managed cluster do not match the desired configuration and only provide degraded service.'
                  type: array
                  items:
                    description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path .status.conditions.  For example, type FooStatus struct{     // Represents the observations of a foo's current state.     // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\"     // +patchMergeKey=type     // +patchStrategy=merge     // +listType=map     // +listMapKey=type     Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n     // other fields }"
                    type: object
                    required:
                      - lastTransitionTime
                      - message
                      - reason
                      - status
                      - type
                    properties:
                      lastTransitionTime:
                        description: lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed.  If that is not known, then using the time when the API field changed is acceptable.
                        type: string
                        format: date-time
                      message:
                        description: message is a human readable message indicating details about the transition. This may be an empty string.
                        type: string
                        maxLength: 32768
                      observedGeneration:
                        description: observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance.
                        type: integer
                        format: int64
                        minimum: 0
                      reason:
                        description: reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty.
                        type: string
                        maxLength: 1024
                        minLength: 1
                        pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$
                      status:
                        description: status of the condition, one of True, False, Unknown.
                        type: string
                        enum:
                          - "True"
                          - "False"
                          - Unknown
                      type:
                        description: type of condition in CamelCase or in foo.example.com/CamelCase. --- Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be useful (see .node.status.conditions), the ability to deconflict is important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt)
                        type: string
                        maxLength: 316
                        pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$
                generations:
                  description: Generations are used to determine when an item needs to be reconciled or has changed in a way that needs a reaction.
                  type: array
                  items:
                    description: GenerationStatus keeps track of the generation for a given resource so that decisions about forced updates can be made. The definition matches the GenerationStatus defined in github.com/openshift/api/v1
                    type: object
                    properties:
                      group:
                        description: group is the group of the resource that you're tracking
                        type: string
                      lastGeneration:
                        description: lastGeneration is the last generation of the resource that controller applies
                        type: integer
                        format: int64
                      name:
                        description: name is the name of the resource that you're tracking
                        type: string
                      namespace:
                        description: namespace is where the resource that you're tracking is
                        type: string
                      resource:
                        description: resource is the resource type of the resource that you're tracking
                        type: string
                      version:
                        description: version is the version of the resource that you're tracking
                        type: string
                observedGeneration:
                  description: ObservedGeneration is the last generation change you've dealt with
                  type: integer
                  format: int64
                relatedResources:
                  description: RelatedResources are used to track the resources that are related to this Klusterlet.
                  type: array
                  items:
                    description: RelatedResourceMeta represents the resource that is managed by an operator
                    type: object
                    properties:
                      group:
                        description: group is the group of the resource that you're tracking
                        type: string
                      name:
                        description: name is the name of the resource that you're tracking
                        type: string
                      namespace:
                        description: namespace is where the thing you're tracking is
                        type: string
                      resource:
                        description: resource is the resource type of the resource that you're tracking
                        type: string
                      version:
                        description: version is the version of the thing you're tracking
                        type: string
      served: true
      storage: true
      subresources:
        status: {}
status:
  acceptedNames:
    kind: ""
    plural: ""
  conditions: []
  storedVersions: []

第二个yaml是这样的,这个是配置了一个新的namespace,然后部署了一个klusterlet应用和配置,这个到底是啥,作者暂时也说不出。

The second yaml is like this. This is to configure a new namespace, and then deploy a klusterlet application and configuration. What exactly is this, the author can’t say for the time being.


---
apiVersion: v1
kind: Namespace
metadata:
  annotations:
    workload.openshift.io/allowed: "management"
  name: "open-cluster-management-agent"

---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: klusterlet
  namespace: "open-cluster-management-agent"

---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: klusterlet
rules:
- apiGroups: [""]
  resources: ["secrets", "configmaps", "serviceaccounts"]
  verbs: ["create", "get", "list", "update", "watch", "patch", "delete"]
- apiGroups: ["coordination.k8s.io"]
  resources: ["leases"]
  verbs: ["create", "get", "list", "update", "watch", "patch"]
- apiGroups: ["authorization.k8s.io"]
  resources: ["subjectaccessreviews"]
  verbs: ["create"]
- apiGroups: [""]
  resources: ["namespaces"]
  verbs: ["create", "get", "list", "watch","delete"]
- apiGroups: [""]
  resources: ["nodes"]
  verbs: ["get", "list", "watch"]
- apiGroups: ["", "events.k8s.io"]
  resources: ["events"]
  verbs: ["create", "patch", "update"]
- apiGroups: ["apps"]
  resources: ["deployments"]
  verbs: ["create", "get", "list", "update", "watch", "patch", "delete"]
- apiGroups: ["rbac.authorization.k8s.io"]
  resources: ["clusterrolebindings", "rolebindings"]
  verbs: ["create", "get", "list", "update", "watch", "patch", "delete"]
- apiGroups: ["rbac.authorization.k8s.io"]
  resources: ["clusterroles", "roles"]
  verbs: ["create", "get", "list", "update", "watch", "patch", "delete", "escalate", "bind"]
- apiGroups: ["apiextensions.k8s.io"]
  resources: ["customresourcedefinitions"]
  verbs: ["create", "get", "list", "update", "watch", "patch", "delete"]
- apiGroups: ["operator.open-cluster-management.io"]
  resources: ["klusterlets"]
  verbs: ["get", "list", "watch", "update", "patch", "delete"]
- apiGroups: ["operator.open-cluster-management.io"]
  resources: ["klusterlets/status"]
  verbs: ["update", "patch"]
- apiGroups: ["work.open-cluster-management.io"]
  resources: ["appliedmanifestworks"]
  verbs: ["list", "update", "patch"]

---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: open-cluster-management:klusterlet-admin-aggregate-clusterrole
  labels:
    rbac.authorization.k8s.io/aggregate-to-admin: "true"
rules:
- apiGroups: ["operator.open-cluster-management.io"]
  resources: ["klusterlets"]
  verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: klusterlet
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: klusterlet
subjects:
- kind: ServiceAccount
  name: klusterlet
  namespace: "open-cluster-management-agent"

---
kind: Deployment
apiVersion: apps/v1
metadata:
  name: klusterlet
  namespace: "open-cluster-management-agent"
  labels:
    app: klusterlet
spec:
  replicas: 1
  selector:
    matchLabels:
      app: klusterlet
  template:
    metadata:
      annotations:
        target.workload.openshift.io/management: '{"effect": "PreferredDuringScheduling"}'
      labels:
        app: klusterlet
    spec:
      serviceAccountName: klusterlet
      tolerations:
      - key: "node-role.kubernetes.io/infra"
        value: ""
        effect: "NoSchedule"
        operator: "Exists"
      containers:
      - name: klusterlet
        image: registry.redhat.io/multicluster-engine/registration-operator-rhel8@sha256:183dc28f1991ad2aa2fcb987d217fc63863909497ae9291b14a96079640463d3
        imagePullPolicy: IfNotPresent
        args:
          - "/registration-operator"
          - "klusterlet"
          - "--disable-leader-election"
        livenessProbe:
          httpGet:
            path: /healthz
            scheme: HTTPS
            port: 8443
          initialDelaySeconds: 2
          periodSeconds: 10
        readinessProbe:
          httpGet:
            path: /healthz
            scheme: HTTPS
            port: 8443
          initialDelaySeconds: 2

---
apiVersion: v1
kind: Secret
metadata:
  name: "bootstrap-hub-kubeconfig"

  namespace: "open-cluster-management-agent"

type: Opaque
data:
  kubeconfig: "YXBpVmVyc2............"

---
apiVersion: operator.open-cluster-management.io/v1
kind: Klusterlet
metadata:
  name: klusterlet
spec:
  deployOption:
    mode: Default
  registrationImagePullSpec: "registry.redhat.io/multicluster-engine/registration-rhel8@sha256:52efbbbd9deef8517ea2c96b1d4756c154ebf342a6331603c6942cf0a64ee133"
  workImagePullSpec: "registry.redhat.io/multicluster-engine/work-rhel8@sha256:3e1a592361dc8176dae1eb5d2bc82bd3aabb6e370add47ae84325ddeb00d661c"
  clusterName: "edge01"
  namespace: "open-cluster-management-agent"
  nodePlacement:
    tolerations:
    - key: "node-role.kubernetes.io/infra"
      value: ""
      effect: "NoSchedule"
      operator: "Exists"

导入配置以后,我们就能看到集群导入成功啦。

After importing the configuration, we can see that the cluster was imported successfully.

cluster set页面上也都是正常的标志。

There are also normal signs on the cluster set page.

集群详细页面上也都是正常的标志。

The cluster details page is also full of normal signs.

新集群的host页面,也有了一个新的worker节点。

The host page of the new cluster also has a new worker node.

集群详细信息的插件页面上,也都是正常的标志。

On the addon page of the cluster details, there are also normal signs.

我们登录到新装的edge01集群的管理页面看看。

Let's log in to the management page of the newly installed edge01 cluster to see.

新的edge01集群,是不能自行升级的,提示这是一个特殊的hosted集群。

The new edge01 cluster cannot be upgraded by itself, suggesting that this is a special hosted cluster.

回想一下,在ACM界面里面,edge01是hosted类型。

Recall that in the ACM interface, edge01 is a hosted type.

我们简单的看看,这个hosted control plan的资源消耗。

Let's take a brief look at the resource consumption of this hosted control plan.

我们看一下这个control plan里面都些什么pod。

Let's take a look at what pods are in this control plan.

cli login into the hosted cluster

接下来,我们通过命令行来登录到新的edge01集群,看看命令行上,这个新的集群有什么特殊的地方。

Next, we log in to the new edge01 cluster through the command line, and see what is special about this new cluster on the command line.

oc extract -n ${ACM_DEMO_CLUSTER} secret/${ACM_DEMO_CLUSTER}-admin-kubeconfig --to=- > ${BASE_DIR}/data/install/kubeconfig-${ACM_DEMO_CLUSTER}

# approve the worker node, if the node can't import
# under normal situation, this is no needed.
oc --kubeconfig=${BASE_DIR}/data/install/kubeconfig-${ACM_DEMO_CLUSTER} get csr | grep -v Approved
oc --kubeconfig=${BASE_DIR}/data/install/kubeconfig-${ACM_DEMO_CLUSTER} get csr -ojson | jq -r '.items[] | select(.status == {} ) | .metadata.name' | xargs oc --kubeconfig=${BASE_DIR}/data/install/kubeconfig-${ACM_DEMO_CLUSTER} adm certificate approve


oc --kubeconfig=${BASE_DIR}/data/install/kubeconfig-${ACM_DEMO_CLUSTER} get co
# NAME                                       VERSION   AVAILABLE   PROGRESSING   DEGRADED   SINCE   MESSAGE
# console                                    4.11.21   True        False         False      6h22m
# csi-snapshot-controller                    4.11.21   True        False         False      6h24m
# dns                                        4.11.21   True        False         False      6h23m
# image-registry                             4.11.21   True        False         False      6h23m
# ingress                                    4.11.21   True        False         False      6h39m
# insights                                   4.11.21   True        False         False      6h25m
# kube-apiserver                             4.11.21   True        False         False      6h40m
# kube-controller-manager                    4.11.21   True        False         False      6h40m
# kube-scheduler                             4.11.21   True        False         False      6h40m
# kube-storage-version-migrator              4.11.21   True        False         False      6h24m
# monitoring                                 4.11.21   True        False         False      6h20m
# network                                    4.11.21   True        False         False      6h24m
# openshift-apiserver                        4.11.21   True        False         False      6h40m
# openshift-controller-manager               4.11.21   True        False         False      6h40m
# openshift-samples                          4.11.21   True        False         False      6h23m
# operator-lifecycle-manager                 4.11.21   True        False         False      6h40m
# operator-lifecycle-manager-catalog         4.11.21   True        False         False      6h40m
# operator-lifecycle-manager-packageserver   4.11.21   True        False         False      6h40m
# service-ca                                 4.11.21   True        False         False      6h25m
# storage                                    4.11.21   True        False         False      6h25m

oc --kubeconfig=${BASE_DIR}/data/install/kubeconfig-${ACM_DEMO_CLUSTER} get node
# NAME             STATUS   ROLES    AGE   VERSION
# edge-worker-01   Ready    worker   17h   v1.24.6+5658434

oc --kubeconfig=${BASE_DIR}/data/install/kubeconfig-${ACM_DEMO_CLUSTER} get mcp
# error: the server doesn't have a resource type "mcp"

oc --kubeconfig=${BASE_DIR}/data/install/kubeconfig-${ACM_DEMO_CLUSTER} get mc
# error: the server doesn't have a resource type "mc"

oc --kubeconfig=${BASE_DIR}/data/install/kubeconfig-${ACM_DEMO_CLUSTER} get all -o wide -n openshift-ingress
# NAME                                 READY   STATUS    RESTARTS   AGE     IP              NODE             NOMINATED NODE   READINESS GATES
# pod/router-default-bb569f544-cknjw   1/1     Running   0          6h41m   192.168.12.33   edge-master-01   <none>           <none>

# NAME                              TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)                   AGE     SELECTOR
# service/router-internal-default   ClusterIP   172.31.152.115   <none>        80/TCP,443/TCP,1936/TCP   6h41m   ingresscontroller.operator.openshift.io/deployment-ingresscontroller=default

# NAME                             READY   UP-TO-DATE   AVAILABLE   AGE     CONTAINERS   IMAGES                                                                                                                   SELECTOR
# deployment.apps/router-default   1/1     1            1           6h41m   router       quay.io/openshift-release-dev/ocp-v4.0-art-dev@sha256:e0dc935b7825a800e32eac69fafa2d238e1d6eb2f344cdf29345cb1123c26a22   ingresscontroller.operator.openshift.io/deployment-ingresscontroller=default

# NAME                                       DESIRED   CURRENT   READY   AGE     CONTAINERS   IMAGES                                                                                                                   SELECTOR
# replicaset.apps/router-default-bb569f544   1         1         1       6h41m   router       quay.io/openshift-release-dev/ocp-v4.0-art-dev@sha256:e0dc935b7825a800e32eac69fafa2d238e1d6eb2f344cdf29345cb1123c26a22   ingresscontroller.operator.openshift.io/deployment-ingresscontroller=default,pod-template-hash=bb569f544


oc --kubeconfig=${BASE_DIR}/data/install/kubeconfig-${ACM_DEMO_CLUSTER} get pod -A | wc -l
# 56

oc --kubeconfig=${BASE_DIR}/data/install/kubeconfig-${ACM_DEMO_CLUSTER} get clusterversion
# NAME      VERSION   AVAILABLE   PROGRESSING   SINCE   STATUS
# version   4.11.21   True        False         6h35m   Cluster version is 4.11.21


post operation

装完了,我们为了方便做实验,我们对集群节点做点配置。虽然减低了集群的安全性,但是做实验吗,无所谓了。

After the installation is complete, we will configure the cluster nodes for the convenience of experiments. Although the security of the cluster is reduced, it doesn't matter if you only do the experiment.


# on helper

# VAR_CLUSTER=edge01
# oc get secret/$VAR_CLUSTER-keypair -n $VAR_CLUSTER --template='{{index .data "id_rsa.key" | base64decode}}' > ${BASE_DIR}/data/install/edge.key

# chmod 600 ${BASE_DIR}/data/install/edge.key

# ssh -i ${BASE_DIR}/data/install/edge.key core@192.168.12.33

cat > ${BASE_DIR}/data/install/crack.txt << EOF

echo redhat | sudo passwd --stdin root

sudo sed -i "s|^PasswordAuthentication no$|PasswordAuthentication yes|g" /etc/ssh/sshd_config
sudo sed -i "s|^PermitRootLogin no$|PermitRootLogin yes|g" /etc/ssh/sshd_config
sudo sed -i "s|^#ClientAliveInterval 180$|ClientAliveInterval 1800|g" /etc/ssh/sshd_config

sudo systemctl restart sshd

sudo sh -c 'echo "export KUBECONFIG=/etc/kubernetes/static-pod-resources/kube-apiserver-certs/secrets/node-kubeconfigs/localhost.kubeconfig" >> /root/.bashrc'

sudo sh -c 'echo "RET=\\\`oc config use-context system:admin\\\`" >> /root/.bashrc'

EOF

for i in 33
do
  ssh core@192.168.12.$i < ${BASE_DIR}/data/install/crack.txt
done


for i in 33
do
  sshpass -p 'redhat' ssh-copy-id root@192.168.12.$i
done


ssh root@192.168.12.33

end