All.devops docker.reference
Code Organization#
- A repo or directory is "runnable" if it can build a Docker container and run
its code inside the container
- E.g.,
//helpers,//amp,//orange,optimizer
- E.g.,
-
Each directory that is runnable contains the files:
changelog.txt: store the changelogdevops: dir with all the Docker files needed to build and run a container
-
There are several types of
devopsdirs- A
devopswhere a container is built, run, and released- E.g.,
//amp,//helpers,//optimizer,//sports_analytics,//tutorials,//demo
- E.g.,
- A
devopswhere a container from a different dir is run- E.g.,
//orangereusescmampcontainer
- E.g.,
- A
Devops to Build, Run, Release a Container#
-
An example of the
devopsstructure is:> cd //amp > tree.sh -p devops devops/ |-- compose/ | `-- __init__.py |-- debug/ | `-- repo_compare.sh* |-- docker_build/ | |-- create_users.sh* | |-- dev.Dockerfile | |-- etc_sudoers | |-- fstab | |-- install_cprofile.sh | |-- install_dind.sh* | |-- install_jupyter_extensions.sh* | |-- install_os_packages.sh* | |-- install_python_packages.sh* | |-- poetry.lock | |-- poetry.toml | |-- prod.Dockerfile | `-- pyproject.toml |-- docker_run/ | |-- aws_credentials.sh | |-- bashrc | |-- entrypoint.sh* | |-- run_jupyter_server.sh* | |-- setenv.sh | `-- test_setup.sh* |-- env/ | `-- default.env |-- notebooks/ ... |-- test/ ... `-- __init__.py -
The organization of a
devopsdir is:compose: Docker compose filesdocker_build: everything related to building a Docker imagedocker_run: everything related to running a Docker imageenv: Docker env files
-
Optional dirs are:
debug: scripts to debug and maintain the dirnotebooks: notebooks used to analyze, monitor Docker container- E.g.,
Master_buildmeister_dashboard.ipynb
- E.g.,
test: everything needed to test a Docker image
Detailed Description of Files#
-
In
devops/compose/devops/compose/docker-compose.yml: file generated by theinvokesystem based on templates, to run a Docker compose
-
In
devops/docker_build/create_users.sh: create the needed users inside the containerdev.Dockerfile: Dockerfile to build the dev version of the containeretc_sudoers: give sudo permission to all users inside the containerfstab: mount S3 and other dir in the container- TODO(gp): Not sure if it's needed anymore
install_cprofile.sh: install the packages needed to profile Python code inside the repoinstall_dind.sh: install docker-in-docker- This workflow has been superseded by Docker sibling approach
install_jupyter_extensions.sh: install Jupyterinstall_os_packages.sh: install the OS packages using the declarative approachos_packages/: directory containing individual OS package install scripts and configurationinstall_python_packages.sh: install the Python packages withpoetryandpippoetry.lock: file generated bypoetryafter the list of packages has been generated- This tracks what packages were installed by
poetry
- This tracks what packages were installed by
poetry.toml: configuration file forpoetryprod.Dockerfile: Dockerfile to build the prod version of the containerpyproject.toml: list of package specification forpoetry
-
In
devops/docker_run/aws_credentials.sh: loads the AWS credentials in the corresponding env vars- TODO(gp): This seems useless
bashrc: basic configuration for bash shells inside a containerentrypoint.sh: configures the container on startup- E.g., run
setenv.sh, start dind, setup Git to run inside the container
- E.g., run
run_jupyter_server.sh: start Jupyter notebooksetenv.sh: configure the virtual environment inside the container- E.g., update
$PATHand$PYTHONPATH - This is equivalent to
setenv.shfor the thin environment
- E.g., update
test_setup.sh: check that the major systems are configured properly- E.g.,
AWS,gspread, ... - TODO(gp): Probably obsolete
- E.g.,
-
In
env/default.env: set some variables and their defaults- TODO(gp): This seems useless
Declarative OS Package Installation#
- The OS package installation system uses a declarative approach where packages are defined in configuration files rather than hardcoded in scripts
- This approach provides better modularity, maintainability, and allows easy customization of which packages to install
Structure#
-
/devops/docker_build/install_os_packages.sh: main script that orchestrates the installation process- Reads the list of packages to install from
os_packages/os_packages.txt - Executes individual install scripts located in the
os_packages/directory - Each install script is responsible for installing a specific set of OS packages (e.g., AWS CLI, GitHub CLI, GraphViz)
- Reads the list of packages to install from
-
devops/docker_build/os_packages/: directory containing individual OS package install scripts and configuration- Install scripts follow the naming convention
install_os_<package_name>.sh os_packages.txt: declarative list of install scripts to execute, one script per line- Lines starting with
#are treated as comments - Empty lines are ignored
- Lines starting with
- Available install scripts:
install_os_aws_cli.sh: install AWS CLI V2 for multiple architecturesinstall_os_github_cli.sh: install GitHub CLI and configure repositoryinstall_os_graphviz.sh: install GraphViz and related packages for diagram generationinstall_os_publishing_tools.sh: install publishing tools like PlantUML
- Install scripts follow the naming convention
How to Disable or Enable OS Packages#
-
Edit
/devops/docker_build/os_packages/os_packages.txtto control which packages are installed:- Disable a package: add
#at the beginning of the line - Enable a package: remove the
#from the beginning of the lineinstall_os_publishing_tools.sh # install_os_aws_cli.sh <- disabled install_os_github_cli.sh install_os_graphviz.sh
- Disable a package: add
-
Important: While it is technically possible to build the Docker image without any OS packages listed in
os_packages.txt(by commenting out all entries), be aware that certainhelpersmodules depend on specific OS packages to function correctly. Disabling required packages may cause runtime errors when those modules attempt to execute system-level operations. For example:hs3.pyfunctions likecopy_file_to_s3()andcopy_data_from_s3_to_local_dir()use AWS CLI commands (aws s3 cp,aws s3 sync) which require theawsclipackage (installed viainstall_os_aws_cli.sh)lib_tasks_gh.pyfunctions likegh_create_pr(),gh_workflow_list(), andgh_issue_title()use GitHub CLI commands (gh pr create,gh workflow list,gh issue view) which require theghpackage (installed viainstall_os_github_cli.sh)
Always verify that the functionality you need will work with your selected package configuration.
How to Add a New OS Package Install Script#
-
Create a new install script following the naming convention:
- File name:
devops/docker_build/os_packages/install_os_<package_name>.sh - Make the script executable:
chmod +x install_os_<package_name>.sh - The script should handle package installation and any necessary configuration
- File name:
-
Add the script to the package list:
- Edit
/devops/docker_build/os_packages/os_packages.txt - Add a new line with the script name:
install_os_publishing_tools.sh install_os_aws_cli.sh install_os_github_cli.sh install_os_graphviz.sh install_os_<package_name>.sh
- Edit
-
Example script structure:
#!/usr/bin/env bash # # Install <package_name>. # apt-get install $APT_GET_OPTS <package_dependencies> # Add installation commands here echo "<PACKAGE_NAME> VERSION="$(<package_name> --version) -
The
APT_GET_OPTSenvironment variable is available and contains standard apt-get options (-y --no-install-recommends)
Devops to Run an Already Existing Container#
- To reuse a Docker container from a different dir / repo,
devopsdoesn't need thedocker_builddir> tree.sh -p devops devops/ |-- debug/ | `-- repo_compare.sh* |-- docker_build/ | `-- prod.Dockerfile -> ../../amp/devops/docker_build/prod.Dockerfile |-- docker_run/ | |-- aws_credentials.sh ... `-- env/ `-- default.env
Docker Invoke Flow#
docker_bash#
-
When
invoke docker_bashis called, it generates a Docker compose command line like:> IMAGE=623860924167.dkr.ecr.eu-north-1.amazonaws.com/sports_analytics:dev \ docker compose \ --file /Users/saggese/src/sports_analytics1/devops/compose/docker-compose.yml \ --env-file devops/env/default.env \ run \ --rm \ --name saggese.sports_analytics.app.sports_analytics1.20240817_113504 \ --user $(id -u):$(id -g) \ app \ bash -
The Docker compose file is generated programmatically based on the command line options
version: '3' services: base_app: cap_add: - SYS_ADMIN environment: - AM_AWS_ACCESS_KEY_ID=$AM_AWS_ACCESS_KEY_ID ... - AM_ENABLE_DIND=0 - AM_FORCE_TEST_FAIL=$AM_FORCE_TEST_FAIL - AM_HOST_NAME=gpmac.local - AM_HOST_OS_NAME=Darwin - AM_HOST_USER_NAME=saggese - AM_HOST_VERSION=22.3.0 - AM_REPO_CONFIG_CHECK=True # Use inferred path for `repo_config.py`. - AM_REPO_CONFIG_PATH= - AM_TELEGRAM_TOKEN=$AM_TELEGRAM_TOKEN ... - CK_TELEGRAM_TOKEN=$CK_TELEGRAM_TOKEN # TODO(Vlad): consider removing, locally we use our personal tokens from files and # inside GitHub actions we use the `GH_TOKEN` environment variable. - GH_ACTION_ACCESS_TOKEN=$GH_ACTION_ACCESS_TOKEN # Inside GitHub Actions we use `GH_TOKEN` environment variable, # see https://cli.github.com/manual/gh_auth_login. - GH_TOKEN=$GH_ACTION_ACCESS_TOKEN # This env var is used by GH Action to signal that we are inside the CI. - CI=$CI image: ${IMAGE} restart: "no" volumes: # TODO(gp): We should pass the value of $HOME from dev.Dockerfile to here. # E.g., we might define $HOME in the env file. - ~/.aws:/home/.aws - ~/.config/gspread_pandas/:/home/.config/gspread_pandas/ - ~/.config/gh:/home/.config/gh # Use sibling-container approach. - /var/run/docker.sock:/var/run/docker.sock # Mount `amp` when it is used as supermodule. app: extends: base_app volumes: - ../../:/app linter: extends: base_app volumes: - /Users/saggese/src/sports_analytics1:/src - ../../:/app working_dir: /src environment: - MYPYPATH jupyter_server: command: devops/docker_run/run_jupyter_server.sh environment: - PORT=${PORT} extends: app network_mode: ${NETWORK_MODE:-bridge} ports: # TODO(gp): Rename `AM_PORT`. - "${PORT}:${PORT}" jupyter_server_test: command: jupyter notebook -h 2>&1 >/dev/null environment: - PORT=${PORT} extends: app network_mode: ${NETWORK_MODE:-bridge} ports: - "${PORT}:${PORT}" networks: default: name: main_network
docker_jupyter#
- This invokes a different service
- To disable vim go to Settings -> Enable Vim Mode
- We use the percent format for Jupytext