Skip to content

FEAT: build updates to support multi-architecture image builds and hardware encoding #1225

Open
@mrjoshuap

Description

@mrjoshuap

These are some ideas and notes to make it easier to track and solicit feedback. Please do not think this is a PR. This is sort of like requirements documentation. I'd like a little discussion on these before I make a file PR and work on it there. I've implemented most of this already, but it's not consumable by others... I'm grabbing some of the things that worked for me and putting them here. There's gonna be spelling errors and things that are seemingly misplaced or not make total sense because I'm pulling from several different prototypes I went through getting my successful deployment.

In short, to get a viable hardware encoding enabled multi-arch image consistently built while simultaneously supporting a number of different deployment patterns, we're gonna need an overhaul to the build system.

Make is already there, so I used it. I also used ArgoCD, and can make arguments for others as well. Personally, I find using this approach to make very convenient and handy at forcing better development habits for myself.

I'd love some thoughts and feedback.

MediaCMS Build Configurations

These are stored as build/[build-config].env and contain variables that alter the build behavior and resulting image outputs.

Supported builds configurations include:

  • full - Builds a typical full MediaCMS image runtime and frontend (not started)
  • frontend - Builds the NodeJS image needed for independent frontend development or generating static content
  • all-in-one - Builds a "kitchen sink" deployment without the garbage disposal

MediaCMS Deployment Configurations

These are stored as deploy/[container-runtime]/[deployment-config].env and contain variables that alter the deployment patterns behavior and resulting image outputs.

Supported deployment types are:

  • local_install - no containers, only useful for python and node development
  • simple - The main container runs migrations, web, uwsgi, celery_beat, celery_workers (celery_short and celery_long services), exposed on port 80 supported by redis and postgres database, accessed as http://localhost
  • simple-letsencrypt - simple with a ssl certificate through letsencrypt service, accessed as https://mediacms.io
  • advanced - Separate containers run migrations, mediacms_web (web and uwsgi), celery_beat, celery_workers (celery_short and celery_long services), exposed on port 80 supported by redis and postgres database, accessed as http://localhost
  • advanced-letsencrypt - advanced with a ssl certificate through letsencrypt service, accessed as https://mediacms.io
  • advanced-reverse-proxy - advanced with reverse proxy, accessed as http://mediacms.io
  • advanced-reverse-proxy-letsencrypt - advanced-reverse-proxy with a ssl certificate through letsencrypt service, accessed as https://mediacms.io
  • advanced-hardware-encoder - Separate containers run migrations, web, uwsgi, celery_beat, celery_short, and celery_long services, exposed on port 80 supported by redis and postgres database, accessed as http://localhost

It is common to copy one of the examples and customize the environment variables appropriately.

MediaCMS Services

  • SERVICES = db redis migration celery celery_workers celery_beat celery_long celery_short frontend uwsgi web

  • db - PostgreSQL for most application components

  • redis - In memory cache for task queues migration celery_beat celery_long celery_short uwsgi frontend web

  • migration - is a Django container meant to handle database migration scripts to accomodate application changes and to collect static content, is typically one-shot started after the databases, but before the rest of the components. It can safely be run after all services on online, running and operational.

  • celery - Task executor service comprised of three different services listening to different queues: beat, long, short

    • Can be built, deployed and managed as a single unit, celery_workers but is actually broken into celery_beat, celery_short, celery_long
  • celery_beat - Watches for heartbeats and other internal application messages posting to the beat queue, ideally always the first celery worker to start and last to stop

  • celery_long - Primarily pulls trancoding jobs from the long queue and performing the encoding

  • celery_short - Watches for short tasks, such as sending e-mail and other light transcoding needs

  • frontend - NodeJS application found in ./frontend/, it generates the front end application as static content or run as a server for development/debugging of the web interface

  • uwsgi - is mainly the backend service for the web front end handling API calls and typical web application needs

  • web - an nginx server that serves static content and proxies requests to the uwsgi service

NOTE: It should be common practice to run all of the celery workers in a single deployment container, however, scalable deployments would separate the workers accordingly
NOTE: Currently celery_long workers are the only workers that will utilize hardware encoding, if specified hardware and the image supports it

MediaCMS Environment Variables

MediaCMS is managed either by a local_settings.py file or set using one or more environment variables.

NOTE: mediacms/cms/settings.py is configured to prioritize local_settings.py however, ALL settings can be configured with MEDIACMS_* environment variables.

A brief list environment variables is:

MEDIACMS_ACCOUNT_AUTHENTICATION_METHOD
MEDIACMS_ACCOUNT_EMAIL_CONFIRMATION_EXPIRE_DAYS
MEDIACMS_ACCOUNT_EMAIL_REQUIRED
...
... a WHOLE bunch of them.  all of them.  from settings.py.  all safe typed and have sane defaults.  currently overridden by local_settings.py if present.
...
MEDIACMS_ADMINS_NOTIFICATIONS_MEDIA_ADDED
MEDIACMS_ADMINS_NOTIFICATIONS_MEDIA_ENCODED
MEDIACMS_ADMINS_NOTIFICATIONS_MEDIA_REPORTED

Directory Structure

  • ./Makefile: Main entry point.
  • ./build/:
    • ./build/Makefile.[component]: Generic component makefiles.
    • ./build/Makefile.[component].[runtime]: Runtime-specific makefiles.
    • ./build/[build-config].env: Build environment files (e.g., all-in-one, full, frontend).
    • ./build/scripts/:
      • Shell scripts (e.g., setup_env.sh, setup_precommit.sh).
  • ./deploy/:
    • ./deploy/kubectl/: Deployments and runtime files for kubernetes based runtimes
    • ./deploy/kubectl/[deployment-config].env: Deployment environment variables for full builds for kubernetes based runtimes (simple, advanced, etc)
    • ./deploy/docker/: Deployments and runtime files for docker/podman based runtimes
    • ./deploy/docker/[deployment-config].env: Deployment environment variables for simple builds for docker/podman based runtimes
      ./deploy/local_install/: Configurations related to the local_install deployment
    • ./deploy/local_install/.env: Deployment environment files for the local_install (not really supported)

Makefile Structure

  • Indentation: Use tabs (not spaces) for indentation, as required by GNU Make.
  • Component Makefiles: Organize targets into logically grouped files:
    • Makefile.admin: Administrative tasks (e.g., shells, container operations).
    • Makefile.build: Build-related tasks (e.g., compiling, container images).
    • Makefile.deploy: Deployment tasks (e.g., service deployment).
    • Makefile.development: Development tools and workflows.
    • Makefile.frontend: Frontend Node.js application tasks.
    • Makefile.manage: Application management (e.g., Django static content, operations and python shell).
    • Makefile.legacy: Deprecated targets for backward compatibility.
  • Container Runtime Variants: Include runtime-specific makefiles (e.g., Makefile.build.kubectl, Makefile.build.docker) where applicable.

Makefile Environment Variables (Makefile Format)

The important ones are:

  • CONTAINER_RUNTIME=kubectl
  • BUILD=full
  • DEPLOYMENT=simple
# Shared Configuration, represents start up order
CONTAINER_RUNTIME ?= kubectl
BUILD_DIR = build
DEPLOY_DIR = deploy/$(CONTAINER_RUNTIME)

# Can be full or simple
BUILDS = full all-in-one frontend
BUILD ?= full
BUILD_ENV ?= $(BUILD_DIR)/env/$(BUILD).env

BUILD_ENV_ALL_IN_ONE_DOCKERFILE ?= $(BUILD_DIR)/Dockerfile
BUILD_ENV_FRONTEND_DOCKERFILE ?= $(BUILD_DIR)/Dockerfile-multi-arch-frontend
BUILD_ENV_FULL_DOCKERFILE ?= $(BUILD_DIR)/Dockerfile-multi-arch
BUILD_ENV_DOCKERFILE ?= $(BUILD_ENV_FULL_DOCKERFILE)

# Can be advanced or simple
DEPLOYMENT_TYPES ?= simple simple-reverse-proxy advanced advanced-reverse-proxy advanced-hardware-encoding
DEPLOYMENT_TYPE ?= advanced
DEPLOYMENT_LOCAL_INSTALL ?= deploy/local_install/.env
DEPLOYMENT ?= $(DEPLOY_DIR)/.env.$(DEPLOYMENT_TYPE)

# Should be overriden by the deployment
SERVICES ?= db redis migration celery celery_beat celery_long celery_short uwsgi frontend web

# CLI Configuration
CLI_ENV = cli-tool/.env

# Kubectl
NAMESPACE = mediacms
KUBECTL = kubectl -n $(NAMESPACE)
K8S_DIR = $(BUILD_DIR)/$(CONTAINER_RUNTIME)

# Docker/Podman
COMPOSE_FILE = docker-compose-multi-arch.yaml

As a prototype of my thoughts, here is an initial just checked out now what example:

$ make
MediaCMS Build System
Usage: make [target]

Available Categories:
  build             - Build-related tasks
  deploy            - Deployment tasks
  manage            - Application management
  development       - Development tools and workflows
  frontend          - Frontend Node.js application tasks
  admin             - Administrative tasks
  workflows         - Show common workflow examples
  legacy            - Deprecated targets

Use 'make help-[category]' for details, e.g., 'make help-workflows'
$ make help-workflows
MediaCMS Workflows

Development Workflow:
  make feature NAME=[name]      - Create a feature branch
    Ex: make feature NAME=new-feature
  make bug NAME=[name]          - Create a bug branch
    Ex: make bug NAME=fix-login
  make changed-from BRANCH=[branch] - Show changed files from a branch
    Ex: make changed-from BRANCH=main
  make diff BRANCH=[branch]     - Show diff from a branch
    Ex: make diff BRANCH=main
  make squash BRANCH=[branch]   - Squash commits into a new branch (WARNING: modifies git history)
    Ex: make squash BRANCH=feat/new-feature
  make report-bug               - Print bug report template
  make request-feature          - Print feature request template
  make submit-pr                - Print pull request template

Build Workflow:
  make build-[config]           - Build the specified configuration
    Ex: make build-full
    Note: For kubectl, this creates a suspended build job. Use 'start-build' to resume it.
  make start-build CONFIG=[config] - Start the build job (kubectl only)
    Ex: make start-build CONFIG=full
  make logs-build CONFIG=[config]  - Monitor build logs
    Ex: make logs-build CONFIG=full
  make status-build CONFIG=[config] - Check build status
    Ex: make status-build CONFIG=full

Deployment Workflow:
  make setup-deploy TYPE=[type] - Configure deployment settings
    Ex: make setup-deploy TYPE=production
  make deploy-[config]         - Deploy a specific configuration, (`simple`, `advanced`, etc)
    Ex: make deploy-simple
  make status-deploy CONFIG=[config] - Check deployment status
    Ex: make status-deploy CONFIG=simple
  make restart-[deployment]        - Restart a deployment
    Ex: make restart-advanced
  make stop-[deployment]           - Stop a deployment
    Ex: make stop-advanced

Management Workflow:
  make setup-cli                - Configure the CLI tool
  make migration                - Run database migrations
  make createsuperuser          - Create a superuser
  make collectstatic            - Collect static files
  make loaddata FILE=[file]     - Load data from a JSON file
    Ex: make loaddata FILE=fixtures/data.json
  make logs-[service]           - View logs for a service
    Ex: make logs-web
  make shell-[service]           - Open a shell to a service
    Ex: make shell-web
  make shell-admin           - Open a python shell to a service
    Ex: make shell-admin SERVICE=web

Testing Workflow:
  make test-all                 - Run all tests
  make test-[test]              - Run a specific test
    Ex: make test-pytest
  make test-pytest              - Run tests for python runtime
  make test-kubectl             - Run tests for kubectl runtime
  make test-docker              - Run tests for docker runtime
  make test-podman              - Run tests for podman runtime

Convenience Targets:
  make build-and-deploy         - Build all configurations and deploy all services
  make clean-all                - Clean all build and deployment artifacts

Functionally I need this so I have more control over volume mounts... seems a long way around to get it.

LOL!

Looking forward to hearing some opinions.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions