Quickstart¶
This walkthrough creates a KubeVirt hosted cluster, attaches its workers to an isolated VLAN, and adds oooi infrastructure for DHCP, DNS, the control-plane proxy, apps ingress, and public API/OAuth DNS.
The names, addresses, registry, and zone below are documentation values. Replace them before applying anything.
Before you begin¶
Complete the prerequisites, including the
NetworkAttachmentDefinition, hosted-cluster secrets, IP plan, and an image
registry that your cluster can pull from. Replace the example release image with
a payload supported by your HyperShift, OpenShift Container Platform, and Red
Hat OpenShift Virtualization versions.
1. Install oooi¶
Install the CRDs and the controller in oooi-system:
export OOOI_IMAGE=registry.example.com/oooi:latest
make install
make deploy IMG="$OOOI_IMAGE"
kubectl -n oooi-system rollout status deployment/oooi-controller-manager --timeout=5m
2. Create the HostedCluster¶
The example follows the structure used by a KubeVirt HostedCluster and NodePool:
spec.servicesis a list of service publishing strategies.- KubeVirt worker networks are configured with
additionalNetworks. attachDefaultNetwork: falseleaves workers with only the VLAN interface.
The service publishing strategy is immutable after creation. On KubeVirt, OAuth
uses a Route; publish it through the proxy external Service
when it must be reachable from outside the VLAN.
apiVersion: hypershift.openshift.io/v1beta1
kind: HostedCluster
metadata:
name: example-hcp
namespace: clusters
spec:
release:
image: registry.example.com/openshift-release:<supported-release>
dns:
baseDomain: clusters.example.com
networking:
clusterNetwork:
- cidr: 10.132.0.0/14
serviceNetwork:
- cidr: 172.31.0.0/16
networkType: OVNKubernetes
platform:
type: KubeVirt
kubevirt: {}
services:
- service: APIServer
servicePublishingStrategy:
type: LoadBalancer
loadBalancer:
hostname: api.example-hcp.clusters.example.com
- service: OAuthServer
servicePublishingStrategy:
type: Route
route:
hostname: oauth.example-hcp.clusters.example.com
- service: Konnectivity
servicePublishingStrategy:
type: Route
route:
hostname: konnectivity.example-hcp.clusters.example.com
- service: Ignition
servicePublishingStrategy:
type: Route
route:
hostname: ignition.example-hcp.clusters.example.com
pullSecret:
name: pull-secret
sshKey:
name: ssh-key
secretEncryption:
type: aescbc
aescbc:
activeKey:
name: etcd-encryption-key
---
apiVersion: hypershift.openshift.io/v1beta1
kind: NodePool
metadata:
name: example-hcp
namespace: clusters
spec:
arch: amd64
clusterName: example-hcp
replicas: 3
management:
autoRepair: true
upgradeType: Replace
platform:
type: KubeVirt
kubevirt:
additionalNetworks:
- name: default/vlan100
attachDefaultNetwork: false
compute:
cores: 4
memory: 16Gi
rootVolume:
type: Persistent
persistent:
size: 50Gi
release:
image: registry.example.com/openshift-release:<supported-release>
3. Apply Infra and attachment¶
Apply Infra and its InfraClusterAttachment as soon as the HostedCluster
object exists. The DHCP, DNS, and proxy services are needed while workers
bootstrap; do not wait for the hosted cluster to become Available.
The attachment is also the association used for shared DNS and proxy routing.
After KubeVirt workers exist, the controller discovers their source addresses
from CAPI Machines. This enables the source-scoped kubernetes.* aliases. The
hostname-SNI alias path uses port 443; the IP-based Kubernetes Service path
uses port 6443 without SNI. Alias discovery does not delay the fully
qualified API route.
apiVersion: hostedcluster.densityops.com/v1alpha1
kind: Infra
metadata:
name: example-hcp
namespace: clusters
spec:
networkConfig:
cidr: 192.0.2.0/24
gateway: 192.0.2.1
networkAttachmentDefinition: vlan100
networkAttachmentNamespace: default
dnsServers:
- 198.51.100.53
- 198.51.100.54
infraComponents:
dhcp:
serverIP: 192.0.2.2
rangeStart: 192.0.2.100
rangeEnd: 192.0.2.199
leaseTime: 1h
dns:
serverIP: 192.0.2.3
proxy:
serverIP: 192.0.2.4
internalProxyService: example-hcp-proxy.clusters.svc.cluster.local
---
apiVersion: hostedcluster.densityops.com/v1alpha1
kind: InfraClusterAttachment
metadata:
name: example-hcp
namespace: clusters
spec:
infraRef:
name: example-hcp
hostedClusterRef:
name: example-hcp
namespace: clusters
dns:
clusterName: example-hcp
baseDomain: clusters.example.com
externalService:
enabled: true
addressPoolName: hosting-public-pool
annotations:
external-dns.alpha.kubernetes.io/hostname: oauth.example-hcp.clusters.example.com.
labels:
external-dns.example.com/publish: "yes"
appsIngress:
enabled: true
metallb:
addressPoolName: apps-pool
ipAddressPoolRange: 192.0.2.200-192.0.2.220
service:
annotations:
external-dns.alpha.kubernetes.io/hostname: "*.apps.example-hcp.clusters.example.com."
labels:
external-dns.example.com/publish: "yes"
4. Watch it converge¶
kubectl -n clusters wait --for=condition=Available \
hostedcluster/example-hcp --timeout=30m
kubectl -n clusters wait --for=condition=Ready \
infra/example-hcp --timeout=30m
kubectl -n clusters get dhcpserver,dnsserver,proxyserver,infra
kubectl -n clusters get infraattachment example-hcp \
-o jsonpath='{.status.appsIngressStatus.phase}{" "}{.status.appsIngressStatus.reason}{" ip="}{.status.appsIngressStatus.externalIP}{"\n"}'
Apps ingress normally progresses through WaitingForHostedClusterNodes,
WaitingForMetalLBCRDs, WaitingForExternalIP, and Ready.
If either wait command times out, inspect the Ready condition and the
apps-ingress status before retrying. The troubleshooting guide
maps the reported reason to corrective actions.
5. Verify from the VLAN¶
Run these from a client attached to the VLAN, not from a management-cluster pod:
dig @192.0.2.3 +short api.example-hcp.clusters.example.com
# 192.0.2.4
dig @192.0.2.3 +short kubernetes.default.svc
# 192.0.2.4 after Machine address propagation
dig @192.0.2.3 +short console-openshift-console.apps.example-hcp.clusters.example.com
# 192.0.2.200
curl -k https://api.example-hcp.clusters.example.com:6443/version
curl -k -o /dev/null -w '%{http_code}\n' \
'https://oauth.example-hcp.clusters.example.com/oauth/authorize?client_id=openshift-challenging-client&response_type=token'
curl -k -o /dev/null -w '%{http_code}\n' \
https://console-openshift-console.apps.example-hcp.clusters.example.com
| Check | Expected result |
|---|---|
API /version |
200 |
| Unauthenticated OAuth request | 401 |
| Console route | 200 |
| Ignition root path | 404 |
| Plain HTTP to konnectivity | 415 |
The OAuth 401, Ignition 404, and konnectivity 415 responses show that the
SNI proxy reached the intended backend. From the pod network, the same HCP
names should resolve to the proxy ClusterIP rather than the VLAN addresses.
6. Publish public DNS¶
Use ExternalDNS or your DNS provider to publish the API, *.apps, and OAuth.
The API record comes from the management-cluster kube-apiserver Service in
the HostedCluster control-plane namespace; *.apps comes from the hosted
oooi-ingress Service; OAuth comes from the attachment's external proxy
Service. See
Public DNS and OAuth publishing for the
ownership model and a hosted-cluster ExternalDNS pattern.
dig +short api.example-hcp.clusters.example.com @<public-resolver>
dig +short console-openshift-console.apps.example-hcp.clusters.example.com @<public-resolver>
dig +short oauth.example-hcp.clusters.example.com @<public-resolver>
The answers should match the current API, apps, and proxy Service VIPs. Use the verification guide for the expected DNS and HTTP results.