Skip to content

docs: migrate 12-factor tutorials to Canonical Kubernetes - #2882

Merged
lengau merged 35 commits into
canonical:mainfrom
florentianayuwono:docs/migrate-tutorials-ck8s-isd-4330
Oct 22, 2026
Merged

lengau merged 35 commits into
canonical:mainfrom
florentianayuwono:docs/migrate-tutorials-ck8s-isd-4330

Conversation

@florentianayuwono

@florentianayuwono florentianayuwono commented Sep 15, 2026 •

Copy link
Copy Markdown
Contributor

Follow-up to the how-to guide migration (#2743), addressing review feedback that the 12-factor tutorials still required MicroK8s. This migrates the tutorials to use Canonical Kubernetes (CK8s) exclusively — no MicroK8s alternative.

Changes

Shared tutorial setup (docs/reuse/tutorial/setup_stable.rst, setup_edge.rst)

  • Install and bootstrap Canonical Kubernetes (sudo snap install k8s --classic, sudo k8s bootstrap).
  • Enable local-storage (replaces MicroK8s' hostpath-storage) and ingress.
  • Enable the built-in load-balancer with an L2 address pool set to the VM's own IP. Unlike the MicroK8s ingress add-on, the CK8s ingress is fronted by a LoadBalancer service, so a load balancer is required for the ingress to be assigned a reachable address. The tutorial curl commands resolve the ingress host to that address ($INGRESS_IP) instead of the loopback address.
  • Register the cluster as a Juju cloud (sudo k8s config | juju add-k8s ...), since CK8s isn't autodetected like MicroK8s.
  • Set up a small local OCI registry on localhost:5000. CK8s has no bundled registry addon, so the tutorials run the static distribution registry as a systemd service and point containerd at it through /etc/containerd/hosts.d/localhost:5000/hosts.toml.

Six framework tutorials and their task.yaml tests (Django, Express, FastAPI, Flask, Go, Spring Boot)

  • Push the rock to the local registry with rockcraft.skopeo copy to localhost:5000, per reviewer request to use a registry rather than driving containerd directly in a tutorial.
  • Reference the charm resource image with the localhost:5000/ prefix.
  • Update prose and juju status sample output for CK8s.

How-to (docs/howto/manage-web-app-charms/integrate-web-app-charm.rst)

  • Make the load-balancer step Canonical K8s-only, consistent with migrating the 12-factor docs off MicroK8s.

Spread (spread.yaml)

  • The docs/tutorial/code/ suite deploys the same local registry and containerd hosts.d config so the executable tutorial tests exercise the documented registry workflow on CK8s.
  • Enable the load balancer and curl the ingress address directly, matching the documented commands (removing the earlier test-only loopback DNAT workaround).

Validation

  • Docs build passes with sphinx-build --fail-on-warning.
  • ⚠️ The task.yaml files are executable spread integration tests. They require a full Multipass + CK8s setup and a self-hosted docs runner, so their end-to-end execution on CK8s — particularly ingress via nginx-ingress-integrator — should be validated in CI.

  • I've followed the contribution guidelines.
  • I've signed the CLA.
  • I've successfully run make lint && make test.
  • I've added or updated any relevant documentation.
  • In documents I changed, I added a meta description if one was missing.
  • I've updated the relevant release notes.

Replace MicroK8s with Canonical Kubernetes throughout the 12-factor app
tutorials so they use CK8s exclusively, following up on the how-to guide
migration.

- Rework the shared tutorial setup (setup_stable.rst, setup_edge.rst) to
  install and bootstrap Canonical Kubernetes (k8s snap), enable
  local-storage and ingress, and register the cluster as a Juju cloud with
  'juju add-k8s' before bootstrapping.
- Replace the MicroK8s registry push (rockcraft.skopeo copy to
  localhost:32000) with a direct containerd image import ('ctr images
  import') and reference the image without the registry prefix in the six
  framework tutorials and their task.yaml tests.
- Update prose and 'juju status' sample output accordingly.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI lite review requested due to automatic review settings September 15, 2026 15:21
Remove the MicroK8s options from the manage-web-app-charms how-to
guides so they use Canonical Kubernetes exclusively, matching the
tutorial migration.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The six executable task suites still provision MicroK8s, so their CK8s commands cannot run until the test environment is updated.

Get a fresh assessment by requesting another Copilot review.

Pull request overview

Migrates the 12-factor tutorials from MicroK8s to Canonical Kubernetes.

Changes:

  • Updates shared CK8s setup and Juju registration.
  • Replaces registry pushes with containerd image imports.
  • Refreshes tutorial instructions and sample output.
File summaries
File Summary
docs/tutorial/kubernetes-charm-spring-boot.rst Updates Spring Boot tutorial for CK8s.
docs/tutorial/kubernetes-charm-go.rst Updates Go tutorial for CK8s.
docs/tutorial/kubernetes-charm-flask.rst Updates Flask tutorial for CK8s.
docs/tutorial/kubernetes-charm-fastapi.rst Updates FastAPI tutorial for CK8s.
docs/tutorial/kubernetes-charm-express.rst Updates Express tutorial for CK8s.
docs/tutorial/kubernetes-charm-django.rst Updates Django tutorial for CK8s.
docs/tutorial/code/spring-boot/task.yaml Uses CK8s containerd image import; requires CK8s test-suite provisioning.
docs/tutorial/code/go/task.yaml Uses CK8s containerd image import; requires CK8s test-suite provisioning.
docs/tutorial/code/flask/task.yaml Uses CK8s containerd image import; requires CK8s test-suite provisioning.
docs/tutorial/code/fastapi/task.yaml Uses CK8s containerd image import; requires CK8s test-suite provisioning.
docs/tutorial/code/expressjs/task.yaml Uses CK8s containerd image import; requires CK8s test-suite provisioning.
docs/tutorial/code/django/task.yaml Uses CK8s containerd image import; requires CK8s test-suite provisioning.
docs/reuse/tutorial/setup_stable.rst Defines stable CK8s setup and Juju registration.
docs/reuse/tutorial/setup_edge.rst Defines edge CK8s setup and Juju registration.
Review details
  • Files reviewed: 16/16 changed files
  • Comments generated: 6
  • Review effort level: Lite

💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread docs/tutorial/code/django/task.yaml Resolved
Comment thread docs/tutorial/code/expressjs/task.yaml Resolved
Comment thread docs/tutorial/code/fastapi/task.yaml Resolved
Comment thread docs/tutorial/code/flask/task.yaml Resolved
Comment thread docs/tutorial/code/go/task.yaml Resolved
The tutorial task.yaml files now import images with the k8s snap's
containerd (`ctr`) and deploy to a `k8s-cloud` Juju cloud, but the
shared docsuite only provisioned MicroK8s. Give docs/tutorial/code/ a
dedicated suite that installs the k8s snap, configures the docker.io
containerd mirror, bootstraps Canonical Kubernetes, enables
local-storage, ingress and the load balancer, and registers k8s-cloud
with Juju. The how-to code suite stays on MicroK8s.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@florentianayuwono

Copy link
Copy Markdown
Contributor Author

Addressed in 49c61e3: the six tutorial task suites no longer inherit the MicroK8s docsuite. docs/tutorial/code/ now has a dedicated spread suite that provisions Canonical Kubernetes instead:

  • installs the k8s snap (1.34-classic/stable) so /snap/k8s/current/bin/ctr is available for the image imports;
  • writes the docker.io pull-through mirror to /etc/containerd/hosts.d/docker.io/hosts.toml (the k8s snap's containerd path) before bootstrap;
  • k8s bootstrap, then enables local-storage, ingress, and the load-balancer (with an L2 pool set to the node IP — required because CK8s exposes ingress via a LoadBalancer service, unlike the MicroK8s host-port addon);
  • registers the cluster as the k8s-cloud Juju cloud (k8s config | juju add-k8s k8s-cloud --client) and bootstraps dev-controller on it.

The how-to code suite (docs/howto/code/) intentionally stays on MicroK8s, as those task.yaml files still use MicroK8s.

Note: these suites are manual: true and I can't run them locally, so CI validation is needed — in particular the nginx-ingress-integrator + Cilium ingress path.

Comment thread docs/tutorial/code/django/task.yaml Resolved
Comment thread docs/reuse/tutorial/setup_edge.rst Resolved
Comment thread docs/reuse/tutorial/setup_stable.rst Resolved

@alithethird alithethird left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Left some nitpicks, overall looking good.

Comment thread spread.yaml Resolved
florentianayuwono and others added 2 commits October 1, 2026 11:56
Address PR review feedback: avoid driving containerd directly (`ctr
images import`) in the 12-factor tutorials. Instead deploy a small
local OCI registry at localhost:5000 and push rocks to it with
`rockcraft.skopeo copy`, mirroring the previous MicroK8s registry flow.

- setup_stable.rst / setup_edge.rst: add a "Set up a local registry"
  section (distribution registry binary as a systemd service +
  containerd hosts.d config for localhost:5000).
- tutorial task.yaml (6 frameworks): push rocks via skopeo and
  reference images as localhost:5000/<app>:<ver>.
- tutorial .rst (6 frameworks): update prose/bullets to the registry
  and skopeo workflow.
- spread.yaml tutorial suite: deploy the registry + hosts.d config in
  place of the docker.io pull-through mirror block.
- Trim heading underlines in the setup files (review nitpick).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…s-ck8s-isd-4330

# Conflicts:
#	docs/howto/manage-web-app-charms/use-web-app-charm.rst

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment thread docs/tutorial/code/django/task.yaml Resolved
Comment thread docs/tutorial/code/expressjs/task.yaml Resolved
Comment thread docs/tutorial/code/fastapi/task.yaml Resolved
Comment thread docs/tutorial/code/flask/task.yaml Resolved
Comment thread docs/tutorial/code/go/task.yaml Resolved
Comment thread spread.yaml Resolved

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment thread docs/reuse/tutorial/setup_edge.rst Resolved
Comment thread docs/reuse/tutorial/setup_stable.rst Resolved
The Canonical Kubernetes ingress is fronted by a LoadBalancer service
rather than binding the node's host ports like the MicroK8s add-on, so
the documented `curl ... 127.0.0.1` requests could not reach the app.

Enable the built-in load balancer with an L2 address pool set to the
VM's own IP in the tutorial setup, and resolve the ingress host to that
address (`$INGRESS_IP`) instead of the loopback address. Align the
spread tutorial suite with the documented commands and drop the
test-only loopback DNAT workaround.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Address the critical command error and moderate registry exposure findings.

1 open finding
2 resolved since last review

🧠 Review effort: Lite

Comment thread docs/reuse/tutorial/setup_edge.rst Resolved
Comment thread docs/reuse/tutorial/setup_edge.rst Resolved
Comment thread docs/reuse/tutorial/setup_stable.rst Resolved
Comment thread docs/reuse/tutorial/setup_stable.rst Resolved
florentianayuwono and others added 2 commits October 12, 2026 20:12
Co-authored-by: florentianayuwono <76247368+florentianayuwono@users.noreply.github.com>
Signed-off-by: florentianayuwono <76247368+florentianayuwono@users.noreply.github.com>
Remove a stray trailing double-backtick from a `curl` command in the
Spring Boot tutorial so the rendered code block can be copied verbatim.
Add a release note for the 12-factor tutorial migration to Canonical
Kubernetes.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Comment thread docs/reuse/tutorial/setup_edge.rst Resolved
florentianayuwono and others added 9 commits October 21, 2026 15:04
Co-authored-by: Erin Conley <erin.conley@canonical.com>
Signed-off-by: florentianayuwono <76247368+florentianayuwono@users.noreply.github.com>
Co-authored-by: Erin Conley <erin.conley@canonical.com>
Signed-off-by: florentianayuwono <76247368+florentianayuwono@users.noreply.github.com>
Co-authored-by: Erin Conley <erin.conley@canonical.com>
Signed-off-by: florentianayuwono <76247368+florentianayuwono@users.noreply.github.com>
Co-authored-by: Erin Conley <erin.conley@canonical.com>
Signed-off-by: florentianayuwono <76247368+florentianayuwono@users.noreply.github.com>
Increase the Multipass VM memory from 4 GB to 8 GB in the shared setup
pages and the per-framework prerequisites to avoid pack errors and I/O
timeouts during the build steps. Add "balancer('s)?" to the custom
wordlist so the load-balancer prose passes the documentation linter.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Django 6.1 requires PostgreSQL 15 or later, but the tutorial deployed
postgresql-k8s from the 14/edge channel (PostgreSQL 14), so the charm's
startup migration failed with "PostgreSQL 15 or later is required".
Deploy postgresql-k8s from 16/stable to match the Django requirement,
consistent with the upstream 12-factor Django tutorial.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The registry setup inlined the full Kubernetes manifest as a heredoc,
which made the shared setup pages long and hard to read. Move the
manifest into docs/reuse/tutorial/registry.yaml and download it with
curl in the tutorial, mirroring the upstream 26.04 tutorials. Add a
readiness probe so the registry rollout wait reflects actual readiness.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

@erinecon erinecon left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was able to successfully complete the Django tutorial 🎉

For the other tutorials with a dedicated section to integrate with a database -- I'm consistently seeing a lack of response from the visitors endpoint.

florentianayuwono and others added 3 commits October 22, 2026 00:47
Avoids the ca-certificates.crt staging conflict (canonical/rockcraft#1261)
that occurs with the ubuntu@24.04 base.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…tstrap

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The cilium ingress class treats the ImplementationSpecific path / as an
exact match, so endpoints other than / returned an empty 404. Use the /.*
pattern and give the ingress more time to come up.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@florentianayuwono
florentianayuwono force-pushed the docs/migrate-tutorials-ck8s-isd-4330 branch from 17f29b3 to 05cf1ed Compare October 22, 2026 04:08
florentianayuwono and others added 3 commits October 22, 2026 14:19
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: florentianayuwono <76247368+florentianayuwono@users.noreply.github.com>
… 16 in clearsession

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@lengau
lengau requested review from erinecon and lengau October 22, 2026 16:57
Comment thread docs/release-notes/charmcraft-4.5.rst Resolved
Comment thread docs/release-notes/charmcraft-4.5.rst Resolved

@lengau lengau left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM from a technical perspective. Once @erinecon has approved I can land this and cherry-pick it onto the 4.5 hotfix branch.

@erinecon erinecon left a comment •

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've successfully tested the Flask and Go tutorials 🎉

Since the FastAPI tutorial is using the experimental 26.04 base (due to canonical/rockcraft#1261 preventing the rock from being packed with the "stable" base), I want to hold off on my approval until I've tested locally (or if the Spread test succeeds). TBD in a couple of hours

@erinecon erinecon left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Spread tests are all passing 🎉

Co-authored-by: Alex Lowe <alex.lowe@canonical.com>
Signed-off-by: florentianayuwono <76247368+florentianayuwono@users.noreply.github.com>
@lengau

lengau commented Oct 22, 2026

Copy link
Copy Markdown
Collaborator

The only things the most recent commit could change have succeeded, so I'm bypassing the requirements and merging so I can cherry-pick this onto the hotfix branch.

@lengau
lengau merged commit 0885c6a into canonical:main Oct 22, 2026
19 of 21 checks passed
lengau pushed a commit that referenced this pull request Oct 22, 2026
Follow-up to the how-to guide migration (#2743), addressing review feedback that the 12-factor **tutorials** still required MicroK8s. This migrates the tutorials to use Canonical Kubernetes (CK8s) **exclusively** — no MicroK8s alternative.

## Changes

**Shared tutorial setup** (`docs/reuse/tutorial/setup_stable.rst`, `setup_edge.rst`)
- Install and bootstrap Canonical Kubernetes (`sudo snap install k8s --classic`, `sudo k8s bootstrap`).
- Enable `local-storage` (replaces MicroK8s' `hostpath-storage`) and `ingress`.
- Enable the built-in `load-balancer` with an L2 address pool set to the VM's own IP. Unlike the MicroK8s ingress add-on, the CK8s ingress is fronted by a `LoadBalancer` service, so a load balancer is required for the ingress to be assigned a reachable address. The tutorial `curl` commands resolve the ingress host to that address (`$INGRESS_IP`) instead of the loopback address.
- Register the cluster as a Juju cloud (`sudo k8s config | juju add-k8s ...`), since CK8s isn't autodetected like MicroK8s.
- Set up a small local OCI registry on `localhost:5000`. CK8s has no bundled `registry` addon, so the tutorials run the static `distribution` registry as a systemd service and point containerd at it through `/etc/containerd/hosts.d/localhost:5000/hosts.toml`.

**Six framework tutorials and their `task.yaml` tests** (Django, Express, FastAPI, Flask, Go, Spring Boot)
- Push the rock to the local registry with `rockcraft.skopeo copy` to `localhost:5000`, per reviewer request to use a registry rather than driving containerd directly in a tutorial.
- Reference the charm resource image with the `localhost:5000/` prefix.
- Update prose and `juju status` sample output for CK8s.

**How-to** (`docs/howto/manage-web-app-charms/integrate-web-app-charm.rst`)
- Make the load-balancer step Canonical K8s-only, consistent with migrating the 12-factor docs off MicroK8s.

**Spread** (`spread.yaml`)
- The `docs/tutorial/code/` suite deploys the same local registry and containerd `hosts.d` config so the executable tutorial tests exercise the documented registry workflow on CK8s.
- Enable the load balancer and curl the ingress address directly, matching the documented commands (removing the earlier test-only loopback DNAT workaround).

## Validation

- Docs build passes with `sphinx-build --fail-on-warning`.
- ⚠️ The `task.yaml` files are executable spread integration tests. They require a full Multipass + CK8s setup and a self-hosted docs runner, so their end-to-end execution on CK8s — particularly ingress via `nginx-ingress-integrator` — should be validated in CI.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants