Reference — Example Lesson#
A complete lesson that puts everything together, modelled on Kubernetes 101: the learner instruments the TODO app that post-create.sh deployed, and every action is checked in the container, proven in Grail and solvable by automation.
One page here, one page per step in your training
To keep the template short this lesson is a single page, so its solutions are merged into one. In your training, give each section its own page (its own nav entry): the app treats a page as a step, and resume and the nightly test work step by step. Kubernetes 101 splits exactly these sections into three pages.
1. Deploy the Dynatrace Operator#
The operator manages everything Dynatrace runs in the cluster. Install it with Helm, in the terminal:
helm install dynatrace-operator oci://public.ecr.aws/dynatrace/dynatrace-operator \
--create-namespace --namespace dynatrace --atomic
2. Deploy the DynaKube#
The DynaKube tells the operator what to monitor. It was generated for you when this step opened (the step's STEP_SETUP ran generateDynakube, from the framework defaults). Look at it, then apply it:
Open your cluster in Dynatrace:
dt-app|dynatrace.kubernetes|Open the Kubernetes app
3. Restart the application#
The TODO app was started by post-create.sh before the webhook existed, so it is not instrumented yet. Restart it so its new pods pass through the webhook:
kubectl rollout restart deployment -n todoapp
kubectl rollout status deployment -n todoapp --timeout=180s
4. Prove it in Grail#
The DynaKube you applied turned on the log module: every pod log in your cluster now flows to Grail, with no change to any application. Open the TODO app and add a todo if you like — then fetch your cluster's logs:
fetch logs, from:now()-15m
| filter endsWith(k8s.cluster.name, "{{DT_SESSION_ID}}") // correct
// | filter k8s.cluster.name == "{{DT_SESSION_ID}}" // never matches
| filter k8s.namespace.name == "todoapp"
| fields timestamp, k8s.namespace.name, k8s.pod.name, content
| sort timestamp desc
| limit 20
The endsWith(k8s.cluster.name, "{{DT_SESSION_ID}}") line scopes the query to your cluster — in a workshop, everyone shares one tenant. The k8s.namespace.name == "todoapp" line then narrows to the app you are teaching: the point is that your application's logs arrive, not that Dynatrace can see its own components in the dynatrace namespace. The cluster name ends with your session id, so the commented-out == line never matches — select a line and toggle its comment (Ctrl/Cmd+/) to try it.
That is a complete interactive step
Content, a check in the container, a check in Grail, a quiz, and a solution that lets the platform replay it — every night, and every time a learner resumes. Copy this page as the starting point of your first real lesson.