Important: Complete Installation first.
This quick start assumes OLM, the OSOK MySQL bundle, and OCI authentication are already installed and working in a test or non-production environment.
This example also requires kro. kro is not a general OSOK prerequisite, but it is required for this specific example because the walkthrough exposes the OSOK MySQL
DbSystembehind a singleOsokMysqlSystemAPI.
This quick start is a focused end-to-end example for users who want one concrete workflow from an installed controller to a provisioned OCI resource. It shows how to:
OsokMysqlSystem API with a ResourceGraphDefinitionOsokMysqlSystem custom resourceThis guide intentionally stays narrow. Use Installation, MySQL DB Systems, and Supported Resources for the broader OSOK surface.
This example assumes you already have:
kubectl accesshelm and operator-sdk installed locallyFor this walkthrough, use the MySQL package bundle from Installation:
operator-sdk run bundle ghcr.io/<REPOSITORY_OWNER>/oci-service-operator-mysql-bundle:v2.0.0-alpha
Use one of the authentication paths from Installation:
For this example, the MySQL controller runs in the
oci-service-operator-mysql-system namespace.
Install kro with Helm:
helm install kro oci://registry.k8s.io/kro/charts/kro \
--namespace kro-system \
--create-namespace
Verify the installation:
kubectl get pods -n kro-system
kubectl get resourcegraphdefinitions.kro.run
For a pinned version or a raw-manifest install, use the official kro install guide: Installing kro.
This example assumes kro’s default test-oriented install path. If you run kro
with stricter aggregated RBAC, grant kro access to the resources used here: the
generated OsokMysqlSystem instances, Secrets, and
dbsystems.mysql.oracle.com. See the official kro access-control guide:
Access Control.
This walkthrough assumes OSOK is running on OKE with Instance Principals.
Create a Dynamic Group that matches the Kubernetes worker nodes in the cluster compartment:
Any {instance.compartment.id = '<KUBERNETES_CLUSTER_COMPARTMENT_OCID>'}
Then create policies like:
Allow dynamic-group <OSOK_DYNAMIC_GROUP> to {SUBNET_READ, SUBNET_ATTACH, SUBNET_DETACH, VCN_READ, COMPARTMENT_INSPECT} in compartment id <COMPARTMENT_OCID>
Allow dynamic-group <OSOK_DYNAMIC_GROUP> to manage mysql-family in compartment id <COMPARTMENT_OCID>
Allow dynamic-group <OSOK_DYNAMIC_GROUP> to use tag-namespaces in tenancy
If you need broader scope, adapt those statements for tenancy-wide permissions. For more details, see MySQL DB Systems.
Before applying the kro definition, gather:
COMPARTMENT_OCID: OCI compartment for the MySQL DB SystemSUBNET_OCID: private subnet OCID for the MySQL DB SystemAVAILABILITY_DOMAIN: OCI availability domain for the DB SystemCONFIGURATION_OCID: MySQL configuration OCIDMYSQL_SHAPE_NAME: MySQL DB System shape supported in your tenancyThe checked-in OSOK MySQL sample uses MySQL.2 as an example shape, but you
should replace that with a shape supported in your tenancy.
Save the following file as osok-mysql-rgd.yaml and apply it once as a
cluster-level platform definition:
apiVersion: kro.run/v1alpha1
kind: ResourceGraphDefinition
metadata:
name: osok-mysql-system
spec:
schema:
apiVersion: v1alpha1
kind: OsokMysqlSystem
spec:
compartmentId: string | required=true
subnetId: string | required=true
availabilityDomain: string | required=true
configurationId: string | required=true
mysqlShapeName: string | required=true
mysqlAdminUsername: string | default="dbadmin"
mysqlAdminPassword: string | required=true
mysqlStorageSizeInGBs: integer | default=50
status:
dbSystemName: ${mysqlDbSystem.metadata.name}
dbSystemOCID: ${mysqlDbSystem.status.?status.?ocid.orValue("")}
connectionSecretName: ${mysqlDbSystem.metadata.name}
resources:
- id: mysqlAdminSecret
template:
apiVersion: v1
kind: Secret
metadata:
name: ${schema.metadata.name + "-mysql-admin"}
namespace: ${schema.metadata.namespace}
type: Opaque
stringData:
username: ${schema.spec.mysqlAdminUsername}
password: ${schema.spec.mysqlAdminPassword}
- id: mysqlDbSystem
readyWhen:
- ${mysqlDbSystem.status.?lifecycleState.orValue("") == "ACTIVE"}
template:
apiVersion: mysql.oracle.com/v1beta1
kind: DbSystem
metadata:
name: ${schema.metadata.name}
namespace: ${schema.metadata.namespace}
spec:
compartmentId: ${schema.spec.compartmentId}
displayName: ${schema.metadata.name}
shapeName: ${schema.spec.mysqlShapeName}
subnetId: ${schema.spec.subnetId}
configurationId: ${schema.spec.configurationId}
availabilityDomain: ${schema.spec.availabilityDomain}
adminUsername:
secret:
secretName: ${mysqlAdminSecret.metadata.name}
adminPassword:
secret:
secretName: ${mysqlAdminSecret.metadata.name}
dataStorageSizeInGBs: ${schema.spec.mysqlStorageSizeInGBs}
port: 3306
portX: 33060
Apply it:
kubectl apply -f osok-mysql-rgd.yaml
kubectl get rgd osok-mysql-system
When the ResourceGraphDefinition becomes active, kro has created a new
OsokMysqlSystem API in the cluster.
Save the following as osok-mysql-system.yaml:
apiVersion: kro.run/v1alpha1
kind: OsokMysqlSystem
metadata:
name: mysql-demo
namespace: demo
spec:
compartmentId: ocid1.compartment.oc1..exampleuniqueID
subnetId: ocid1.subnet.oc1..exampleuniqueID
availabilityDomain: <AVAILABILITY_DOMAIN>
configurationId: ocid1.mysqlconfiguration.oc1..exampleuniqueID
mysqlShapeName: MySQL.2
mysqlAdminUsername: dbadmin
mysqlAdminPassword: change-me-for-test-only
mysqlStorageSizeInGBs: 50
Create the namespace, then apply the single instance manifest:
kubectl create namespace demo
kubectl apply -f osok-mysql-system.yaml
For this quick start, the MySQL admin password is supplied on the custom resource for simplicity. For a production-grade design, move the credentials to a pre-created Secret and model that Secret as an external dependency instead of storing the password in the instance spec.
MySQL provisioning takes the longest. Watch the stack with:
kubectl get OsokMysqlSystem -n demo
kubectl get dbsystems.mysql.oracle.com -n demo
kubectl get secrets -n demo
Inspect the DbSystem resource:
kubectl describe dbsystem mysql-demo -n demo
OSOK creates a same-name Secret after the DbSystem becomes active. Inspect
that connection Secret with:
kubectl get secret mysql-demo -n demo
kubectl get secret mysql-demo -n demo -o jsonpath='{.data.PrivateIPAddress}' | base64 --decode && echo
At the end of this flow you should have:
OsokMysqlSystem resource managed by kroDbSystem resource reconciled by OSOKThat Secret is the handoff point for workloads that need to connect to the MySQL DB System later.
Delete the instance first so kro and OSOK can tear down the managed resources:
kubectl delete OsokMysqlSystem mysql-demo -n demo
If you also want to remove the generated API:
kubectl delete rgd osok-mysql-system
If you installed kro only for this example:
helm uninstall kro -n kro-system
After this quick start, continue with: