DevOps Project: The Plan

In this project you build a delivery pipeline for a small web API:

  • a CRUD API in Python, in its own GitHub repository;
  • a GitHub Actions pipeline:
    • a push to your own branch runs the fast tests;
    • a pull request runs more: integration tests, security and code inspection tools;
    • a pull request waits for an approver before it can merge;
    • a merge to main builds a container image and deploys it;
  • a deployment on AWS: an EKS cluster, or a set of EC2 instances.

See the finished pipeline at work, one event at a time, in The Pipeline, Step by Step.

The tracks

Choose one of each pair when you start, and follow its steps only.

The API framework

  • Flask: the basic track.
    • Each request runs a plain function, start to finish, in a worker of its own.
    • Plain Python is all it takes.
  • FastAPI: the advanced track.
    • It checks each request against declared types, and generates an OpenAPI page from them.
    • It is built on asyncio: a route may be an async def, run on the server's one event loop.
      • An await hands the loop to another request while this one waits.
      • A blocking call inside an async def (a time.sleep, a synchronous database driver) stops every request.
    • Choose it if you know async/await and the event loop, or want to learn them on the way.

The deployment target

  • EKS: the main track.
    • A Kubernetes cluster: the deployment is a Deployment and a Service, the way most teams ship containers.
    • It costs money from the moment it exists, whether the app runs or not.
    • Created at the start of each working session and destroyed at its end, it costs about $13–$37 in all for 20–30 hours a month over 2–4 months: the computation is in Task 5.
    • Left running, it costs about $200 a month.
  • EC2: a set of instances running the container, for students who find EKS too hard.
    • Fewer parts to learn, and only the instances and the load balancer to pay for.
    • The deploy is your own script, where EKS gives you rollouts and health checks.

The app runs in a container on both targets.

Working alone or in a pair

The project is done alone or by two students sharing one repository. Choose when you start.

  • In both, the way changes reach main changes at Task 3:
    • Tasks 1–2: you push straight to main. Nothing protects it yet, and the empty pipeline is easiest to watch that way.
    • From Task 3 on: main is protected. Every change goes on a branch of its own, through a pull request, and is merged only once its checks pass and it is approved.
  • The difference is who approves.
  • Either way, every branch follows one naming scheme: see Topic branches.
Alone
  • Every issue is assigned to you, and every branch is yours.
  • The approver is pr-reviewer, the GitHub App: it stands in for the second person.
    • It approves after a round of requested changes, and never reads the code.
    • The review is a formality; the checks are what stop a broken change.
  • AWS: your own account.
In a pair
  • One repository: one student creates it, and adds the other as a collaborator with write access.
  • Every task together, both tracks shared: you choose Flask or FastAPI, EKS or EC2, once for the pair.
  • Each issue is assigned to one of you: the assignee owns its branch, so a branch's owner is plain to both.
  • The other student approves: GitHub never lets the author of a pull request approve it.
    • Read the change before approving: this is the review pr-reviewer only imitates.
    • Take turns: one writes a task's change, the other reviews it, and the next task swaps.
    • pr-reviewer is still installed in Task 3, so the pipeline is the same as alone; either approval merges.
  • Never push straight to main after Task 3, even to fix the other's mistake: open a pull request.
  • One AWS account, the repository owner's. The pipeline deploys with its own role, so the other student needs console access only to look: TODO: an IAM user with read-only access, or IAM Identity Center.
  • Split the AWS cost: the cluster's hours are the pair's hours, so one cluster costs no more than alone.

Topic branches

Every change starts as a GitHub issue, and its branch is named <type>/<issue>-<what>.

PartHoldsExample
typefeature for new behaviour of the API, fix for a bug, ci for the pipeline: workflows, Dockerfile, infrastructurefeature
issuethe issue's number12
whata few words, lowercase, joined by hyphenssearch-books
1$ git switch -c feature/12-search-books
2$ git push -u origin feature/12-search-books
  • The number links everything: the branch names the issue, the pull request's description says Closes #12, and the merge closes the issue.
  • The type groups the branches: GitHub's branch list and a workflow's branches: filter can match fix/**.
  • The words say what it is without opening the issue.
  • One issue, one branch, one pull request; the branch is deleted after the merge.

The finished pipeline

The Pipeline, Step by Step plays all of it, one event at a time: a push to a topic branch, a pull request through its checks and its review, the merge and the delivery, and a refused push to main.

EventJobs
Push to your branchlint, unit tests
Pull request to mainlint, unit tests, integration tests, security and code inspection, image build; then the approver's review
Merge to mainimage build and push, deploy, smoke test

The order of the tasks

The tasks build the whole pipeline first, then fill it in.

  • Tasks 1–3: the pipeline, empty.
    • Every job exists from Task 2 on, as a placeholder that prints what it will do.
    • You see each event run its jobs, and the approver gate the merge, before any job does real work.
  • Tasks 4–6: the first complete solution.
    • The placeholders for the image and the deployment become real.
    • A merge to main puts the app on AWS.
  • Tasks 7–10: the details.
    • Each task replaces more placeholders, or makes a working job stricter.
TaskPost
1The Application and Its Repository
2The Pipeline, Empty
3Pull Requests and the Approver
4The Container Image
5The Infrastructure on AWS
6The First Deployment
7Tests on Every Push
8Pull Request Checks
9Security and Code Inspection
10Delivery in Detail
11Clean Up

Before you start

  • A GitHub account.
  • An AWS account, from Task 5 on. The infrastructure costs money while it runs; Task 11 removes it.
  • A Docker Hub account, from Task 4 on.
  • Python 3.12 or newer, Docker and git on your machine.

The site's other material, used along the way:

Translations: