Skip to content

prsmalley/ansible-playground

Repository files navigation

ansible-playground

Lint License: MIT

Status: Live at https://flaskapp.prsmalley.dev/health — flaskapp is served via a Cloudflare Tunnel running on the EC2 (provisioned by terraform-flaskapp-infra). A Grafana dashboard of app metrics is live at https://grafana.prsmalley.dev. The cluster itself stays SG-restricted to the operator IP.

This repo owns cluster bootstrap and per-release deploys: it installs k3s on a fresh EC2 host and ships Kubernetes manifests to the cluster via an ephemeral self-hosted GitHub Actions runner (ARC). It's one of three repos that together build, provision, and deploy a Flask app to a k3s cluster on AWS EC2:

  • flaskapp-docker-practice — builds and publishes the container image to GHCR.
  • terraform-flaskapp-infra — provisions the EC2 host.
  • ansible-playground — bootstraps k3s and deploys the app via ephemeral self-hosted runners (ARC) running inside the cluster.

See ARCHITECTURE.md for the full design.

flowchart LR
    A[flaskapp-docker-practice] -->|CI + release| GHCR[(GHCR)]
    B[terraform-flaskapp-infra] -.provisions.-> EC2
    C[ansible-playground] -.bootstraps.-> k3s
    C --> Runner
    subgraph EC2[AWS EC2]
        subgraph k3s[k3s cluster]
            Runner[ARC runner pod] -->|kubectl apply| APP[flaskapp pods]
        end
    end
    GHCR -.image pull.-> APP
Loading

Repo layout

.
├── ansible.cfg                 # Ansible defaults
├── inventory.ini.example       # Copy to inventory.ini for bootstrap
├── requirements.yml            # Ansible collections (community.docker — used by legacy/ only)
├── bootstrap-k3s.yml           # k3s install playbook (one-time per cluster)
├── manifests/                  # K8s manifests applied per deploy
│   ├── deployment.yaml
│   ├── service.yaml
│   └── ingress.yaml
├── setup/                      # One-time cluster bootstrap (not redeployed)
│   └── rbac.yaml               # ServiceAccount + Role + RoleBinding for deploy-runner
├── monitoring/                 # Prometheus + Grafana (applied once, not per deploy)
│   ├── prometheus.yaml
│   └── grafana.yaml
├── legacy/                     # Archived from earlier architecture iterations
├── .github/workflows/
│   ├── lint.yml                # ansible-lint on every PR
│   └── deploy.yml              # ARC ephemeral runner runs kubectl apply
├── BOOTSTRAP.md                # One-time cluster setup runbook
└── ARCHITECTURE.md             # End-to-end design across all three repos

Deploying (per-release)

After the one-time cluster setup, deploys are triggered from the CLI or the GitHub Actions UI:

gh workflow run deploy.yml \
  -f image_tag=sha-abc1234 \
  -f dry_run=false

Set dry_run=true to preview what would be applied without making changes — useful for verifying a new image tag before a real deploy.

What happens when you trigger it:

  1. release.yml in flaskapp-docker-practice has already published a multi-arch OCI image to GHCR.
  2. Operator triggers deploy.yml via workflow_dispatch (the command above).
  3. ARC's listener detects the queued job and spawns an ephemeral self-hosted runner pod inside the k3s cluster.
  4. The runner pod authenticates to the K8s API via its mounted ServiceAccount token, sed-substitutes the image tag into manifests/deployment.yaml, and runs kubectl apply -f manifests/.
  5. k3s reconciles to the declared state; flaskapp pods come up and reach Ready when their readiness probes pass.
  6. kubectl rollout status blocks until the new pods are Ready; the runner pod terminates.

Initial cluster setup

One-time procedure for spinning up a new cluster from scratch. See BOOTSTRAP.md for the full operator runbook (~30 minutes end to end, assuming AWS / GitHub / SSH credentials are already in place). After bootstrap, deploys are workflow-triggered as above.

Monitoring

The cluster runs Prometheus (collects metrics) and Grafana (charts them), both in the monitoring namespace. The app exposes a /metrics endpoint, Prometheus scrapes it every 30 seconds, and Grafana reads from Prometheus and serves a dashboard at https://grafana.prsmalley.dev.

These live in monitoring/ rather than manifests/ on purpose: they're set up once when the cluster is built, not re-applied on every app deploy. Install steps are in BOOTSTRAP.md.

Linting

ansible-lint runs on every PR via lint.yml at the production profile. To run locally:

pip install ansible-lint
ansible-galaxy collection install -r requirements.yml
ansible-lint

Legacy (legacy/)

Archived files from earlier architecture iterations. Kept for portfolio depth and as Ansible examples; not part of the active deploy.

  • site.yml — early Ansible learning playbook. Multipass VM config (packages, user, templated config, nginx). Demonstrates templates, handlers, multi-module orchestration.
  • deploy-flaskapp.yml — Ansible-based deploy that pulled the image and ran it via Docker on a single host. Replaced by the K8s deploy. Demonstrates the community.docker collection.
  • inventory-runner.ini — inventory used by the old deploy.yml workflow when the runner was the deploy target (Multipass VM era).

License

MIT — see LICENSE.

About

Ansible bootstrap for k3s install + K8s manifests for Flask container deploy. Workflows run on ephemeral self-hosted runners managed by ARC inside the k3s cluster. Part of a three-repo CI/CD/CD design - see ARCHITECTURE.md

Topics

Resources

License

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages