Skip to content

Deploy to Azure Container Apps instead of GKE; smoke-test the image in CI - #8

Merged
mofr merged 3 commits into
winter-33from
azure-container-apps
Oct 13, 2026
Merged

mofr merged 3 commits into
winter-33from
azure-container-apps

Conversation

@mofr

@mofr mofr commented Oct 13, 2026 •

Copy link
Copy Markdown
Contributor

Stacked on #7: it uses the new Dockerfile. Merge #7 first, or merge this into winter-33 before #7.

The GKE deployment is dead. The last deployment, in July 2024, failed. The workflow pushes to gcr.io, which stopped accepting images in March 2025. A GKE cluster with pods and a LoadBalancer service also costs money when idle, and the JSON service account key in the secrets tends to expire or be revoked.

The deployment served two purposes, and this splits them:

  • Check that the app deploys. docker.yml builds the image, starts the container and checks with curl that it answers, on every push. It costs nothing and has nothing to rot.
  • Show how to deploy to a popular cloud. azure.yml deploys to Azure Container Apps (Consumption plan, scales to zero, within the monthly free grant), then checks with curl that the public URL answers. It runs on demand and on the 1st of every month, so a broken cloud setup shows up as a failed workflow within weeks instead of years.

Details:

  • The responses themselves are covered by the pytest suite; the workflows only check that the app starts and answers. The Azure check retries for about 150 seconds to survive a cold start.
  • The image goes to GitHub Container Registry, which is free; Azure Container Registry is not. The package has to be made public once.
  • GitHub Actions signs in to Azure with OpenID Connect. The federated credential trusts only the azure environment of this repository, and the environment allows deployments only from master.
  • The Container Apps environment is created without a Log Analytics workspace, which would be billed separately.
  • gke.yml and deployment.yaml are removed.

Not verified yet: the Azure deployment itself. It needs the one-time setup from the README, run by someone with access to an Azure subscription. docker.yml runs on this PR. Workflows pass actionlint.

🤖 Generated with Claude Code

…n CI

The GKE deployment last succeeded before July 2024 and pushed to gcr.io, which stopped
accepting images in March 2025. A Kubernetes cluster with a load balancer also costs money
when idle.

- docker.yml builds the image, runs it and checks the responses on every push.
- azure.yml deploys to Azure Container Apps on demand and monthly, then runs the same checks
  against the deployed app. The image lives on GitHub Container Registry, and GitHub Actions
  signs in to Azure with OpenID Connect, so there is no long-lived secret to expire.
- smoke_test.sh holds the response checks used by both workflows.
- README documents the one-time Azure and GitHub setup.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@mofr
mofr requested a review from andrey-berenda October 13, 2026 14:28
…est script

The pytest suite already tests the responses. CI only needs to know that the image starts
and the deployment answers.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@mofr
mofr removed the request for review from andrey-berenda October 13, 2026 15:31
@mofr
mofr merged commit b0e723a into winter-33 Oct 13, 2026
9 of 10 checks passed
@mofr
mofr deleted the azure-container-apps branch October 13, 2026 16:11
mofr added a commit that referenced this pull request Oct 13, 2026
* Upgrade to Winter 33 and call setup after route discovery

- Winter 33 import paths: winter.web, winter.django, winter.openapi, winter.application.
- Setup moves from settings.py to AppConfig.ready(), after the URL module is imported.
  Called from settings.py, it ran before the API modules were imported, so exceptions
  marked with @problem got no handler and returned 500.
- An endpoint with a @problem exception shows and tests RFC 7807 responses.
- Route discovery scans only simple_api.api and simple_api.docs; tests move to tests/.
- Fresh Django 5.2 project files, Python 3.11 to 3.13, Poetry 2, current GitHub Actions.
- Dockerfile uses python:3.13-slim and installs only runtime dependencies.
- README: correct paths and gunicorn module, setup order explained, Heroku section removed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Flat layout: simple_api/api.py and simple_api/openapi.py

Winter 33.1 discovers routes in a single module, so the api and docs subpackages
are no longer needed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Regenerate poetry.lock for Winter 33.1.0 with Poetry 2.5.1

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Regenerate poetry.lock from scratch

poetry lock kept versions from the old lock, including greenlet 3.0.3, which
has no Python 3.13 wheels and does not build from source there.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Keep the API example to one endpoint

The @problem endpoint made the first example 22 lines long. The setup order stays
explained in apps.py.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Deploy to Azure Container Apps instead of GKE; smoke-test the image in CI (#8)

* Deploy to Azure Container Apps instead of GKE; smoke-test the image in CI

The GKE deployment last succeeded before July 2024 and pushed to gcr.io, which stopped
accepting images in March 2025. A Kubernetes cluster with a load balancer also costs money
when idle.

- docker.yml builds the image, runs it and checks the responses on every push.
- azure.yml deploys to Azure Container Apps on demand and monthly, then runs the same checks
  against the deployed app. The image lives on GitHub Container Registry, and GitHub Actions
  signs in to Azure with OpenID Connect, so there is no long-lived secret to expire.
- smoke_test.sh holds the response checks used by both workflows.
- README documents the one-time Azure and GitHub setup.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Check the container and the deployment with curl instead of a shell test script

The pytest suite already tests the responses. CI only needs to know that the image starts
and the deployment answers.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
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.

1 participant