Quickstart
From an empty namespace to a registered, measured service in about ten minutes.
This walk-through installs the platform with its bundled PostgreSQL, registers one service, has the Kubernetes agent report on it, and pushes a health report for something that is not in Kubernetes at all. It uses port-forwarding so you do not need DNS or TLS yet. Install covers the production values.
You need kubectl and helm pointed at a cluster running Kubernetes 1.23 or newer, with a default storage class.
1. Install the platform
helm repo add itops https://charts.mlops.hu
helm repo update
helm install itops itops/itops --version 2.0.0 -n itops --create-namespace \
--set ingress.enabled=false --set uiIngress.enabled=false \
--set secretEnv.ITOPS_JWT_SECRET="$(openssl rand -hex 32)" \
--set secretEnv.ITOPS_SECURITY_OPERATOR_API_KEY="$(openssl rand -hex 32)"
kubectl -n itops rollout status deploy/itops-core --timeout=600s
The first start runs the database migrations. The startup probe allows ten minutes for that; on a small cluster it takes about one.
The operator API key you just generated is what agents and scripts authenticate with. Keep it at hand:
export KEY=$(kubectl -n itops get secret itops-secrets -o jsonpath='{.data.ITOPS_SECURITY_OPERATOR_API_KEY}' | base64 -d)
2. Open the UI
kubectl -n itops port-forward svc/itops-ui 8081:80 &
kubectl -n itops port-forward svc/itops-core 8080:8080 &
export API=http://localhost:8080
Open http://localhost:8081 and sign in as admin with the password Password123!. Change it now, from the user menu: the platform seeds this password on every fresh install and it is on every checklist for a reason.
The UI needs to know where the API is. With port-forwarding, both are on localhost and the default works. With ingress you set ui.apiUrl; see Install.
3. Register a service
The catalogue belongs to the server. A service exists because you said so, in one HTTP call:
curl -s -X POST $API/api/v1/operator/register \
-H "X-API-Key: $KEY" -H "Content-Type: application/json" -d '{
"nodeId": "acme/shop/prod/eu-1",
"name": "orders-api",
"namespace": "shop",
"displayName": "Orders API",
"criticality": "high",
"slaGroup": "checkout-flow",
"ownership": { "team": "Commerce", "onCall": "commerce-oncall" },
"source": "manual"
}'
The response is {"success":true,"serviceId":"…","created":true}. Reload the UI: the tree now has acme › shop › prod › eu-1, and under it orders-api with the status UNKNOWN, because nobody has reported on it yet. Registering services lists every field the body can carry.
nodeId is the first four levels of the service's path. Pick real names: they are the identity of everything the service will ever report, and the tree in the UI is built from them.
4. Let the agent report on it
The Kubernetes agent asks the core which services belong to its node, looks each one up as a Deployment, StatefulSet or DaemonSet, and reports replica counts. For this walk-through, give it something to find:
kubectl create namespace shop
kubectl -n shop create deployment orders-api --image=nginx:alpine --replicas=2
Then deploy the agent into the same namespace with the node id you registered under. The manifest in the agent repository has a ServiceAccount, a Role with get on workloads, a RoleBinding and the Deployment; the only lines to edit are the three environment variables:
kubectl -n shop create secret generic itops-agentv2 --from-literal=api-key="$KEY"
curl -sO https://raw.githubusercontent.com/balazspuskas/itops-agentv2/main/deploy/kubernetes.yaml
# edit: ITOPS_URL=http://itops-core.itops.svc:8080 ITOPS_NODE_ID=acme/shop/prod/eu-1 ITOPS_WATCH=true
kubectl -n shop apply -f kubernetes.yaml
Within 30 seconds the agent posts its first status report and orders-api turns OPERATIONAL with 2/2 ready and the image it found. Scale the deployment to zero and it turns DOWN; scale it to one and it reads DEGRADED. The agent page explains the rules, the RBAC and what the agent will never do.
5. Push a report for something outside Kubernetes
Anything that can send one HTTP request can be a service. The push endpoints create the service on first sight, so there is nothing to register first:
curl -s -X POST $API/api/v1/health/report \
-H "X-API-Key: $KEY" -H "Content-Type: application/json" -d '{
"path": "acme/shop/prod/dc-budapest/galera-node-1",
"status": "OPERATIONAL",
"message": "wsrep_cluster_size=3, wsrep_ready=ON",
"criticality": "critical",
"slaGroup": "orders-database"
}'
A second node, dc-budapest, appears in the tree with the Galera node under it. Put that curl in a cron line on the database host and the service stays green. Stop the cron and it turns UNKNOWN after two minutes and OUTAGE after five: silence is never mistaken for health. The other two push endpoints, for storage and backups, work the same way and are on the push API page.
6. Where the SLA numbers come from
Every five minutes the platform snapshots every service's status. Once a group has a few snapshots, the SLA dashboard shows its uptime, its error budget in minutes and whether the month is on track. The SLA plugin needs a licence key; without one the dashboard tab is hidden and the snapshots still accumulate, so nothing is lost when a key is added later. See SLA measurement and Licence and plugins.
What to do next
- Put the two
curlcalls into the places they belong: registration into a Helm post-install hook or the CI job that deploys the service, the health push into cron on the host. - Read Install before exposing the platform: ingress, TLS, CORS, an external database and the one cluster setting that breaks rate limiting.
- Hand out accounts through users, groups and roles. Give visitors the read-only viewer role.