#+author: ChiefKemist
#+title: Devops From Scratch (Automated Basics)
#+date: <2023-05-14 Sun>
* Recap
*** What we did
+ Created a [[https://www.djangoproject.com/][Django]] TODO application
+ Provisionned a VM on AWS
+ Installed the required Python dependencies on VM
+ Uploaded Application Code to VM
+ Ran Dabase Migrtions on VM
+ Setup [[http://supervisord.org/][Supervisord]] for Running, Monitoring, and Keeping Alive the Application
*** Pain Points
+ Complicated hand-written commands
+ Not easily repeatable accross machines
+ Keeping the process alive
+ Machine Dependencies
*** Better Tools and Processes
+ Use code as it is familiar
+ Use configuration files for reuseability
+ Use code and configuration to provision at scale
+ Portable infrastructure through containers
+ Container runtime for building and shipping applications
+ Use container scheduler for bin packing
+ Use container scheduler for scale
* Automation
*** Infrastructure (VMs / Fabric)
#+name: .gitignore
#+begin_src init :tangle devops-from-scratch/automated-basics/.gitignore :mkdirp yest :cache no
*.pem
*.tar
#+end_src
#+name: pyproject.toml
#+begin_src toml :tangle devops-from-scratch/automated-basics/pyproject.toml :mkdirp yes :cache no
[tool.poetry]
name = "devops-from-scratch-automated-basics"
version = "0.1.0"
description = "DevOps from Scratch (Automated Basics)"
authors = ["user "]
license = "MIT"
readme = "README.md"
#packages = [{include = "devops_from_scratch_automated_basics"}]
[tool.poetry.dependencies]
#python = "^3.10.6"
python = "^3.11"
sortedcontainers = "^2.4.0"
httpx = "^0.23.0"
invoke = "2.0.0"
fabric = "^3.0.1"
paramiko = "^3.1.0"
typer = { version = "^0.9.0", extras = ["all"] }
#[tool.poetry.group.dev.dependencies]
#black = "^23.3.0"
#pytest = "7.3.1"
[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
#+end_src
#+name config.ini
#+begin_src ini :tangle devops-from-scratch/automated-basics/config.ini :mkdirp yes :cache no
[ubuntu.main]
host = ubuntu@15.222.44.100
app_dir = /home/ubuntu
pem_file = ubuntu-main.pem
#+end_src
#+name fabfile.py
#+begin_src python :tangle devops-from-scratch/automated-basics/fabfile.py :mkdirp yes :cache no
import configparser
from fabric import (
Connection, Config, task,
)
# Read the configuration file
config = configparser.ConfigParser()
config.read('config.ini')
def get_server_connection(server, config=config):
# Define the hosts where the tasks will be executed
host = config.get(server, 'host')
# Set the path to the .pem file
pem_file = config.get(server, 'pem_file')
# Create a configuration for the connection
fab_config = Config(
overrides={
'connect_kwargs': {
'key_filename': pem_file
}
})
# Create a connection
conn = Connection(host, config=fab_config)
return conn
# Ubuntu Main Configuration Section Name
#server = 'ubuntu.main'
# Define the directory of the application
#app_dir = config.get(server, 'app_dir')
@task
def machine_info(ctx, server):
# Define the server connection
with get_server_connection(server) as conn:
conn.run('uname')
conn.run('uname -n')
conn.run('uname -v')
conn.run('uname -m')
conn.run('lsb_release -a')
conn.run('whoami')
conn.run('date')
#+end_src
*** Testing (CI / PyTest)
Another time...
*** Deployment (CD / Docker)
We will take the following steps to Prepare our initial Virtual Machine:
+ Acquire VM
+ Install Docker
+ Setup Docker Swarm
+ Mark it as Master Node
**** Install Docker
#+begin_src bash
# Update your existing list of packages
sudo apt update
# Install a few prerequisite packages which let `apt` use packages over HTTPS
sudo apt install apt-transport-https ca-certificates curl software-properties-common
# Add the GPG key for the official Docker repository to your system
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
# Add the Docker repository to APT sources
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
# Update your existing list of packages again for the addition to be recognized
sudo apt update
# Make sure you are about to install from the Docker repo instead of the default Ubuntu repo
apt-cache policy docker-ce
# Install Docker
sudo apt install docker-ce
# Check that it’s running
sudo systemctl status docker
#+end_src
#+name fabfile.py
#+begin_src python :tangle devops-from-scratch/automated-basics/fabfile.py :mkdirp yes :cache no
@task
def install_docker(ctx, server):
# Define the server connection
with get_server_connection(server) as conn:
# Update your existing list of packages
conn.sudo('apt update')
# Install a few prerequisite packages which let `apt` use packages over HTTPS
#with settings(prompts={'Do you want to continue [Y/n]?':'Y'}):
conn.sudo('apt install apt-transport-https ca-certificates curl software-properties-common')
# Add the GPG key for the official Docker repository to your system
conn.run('curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg')
# Add the Docker repository to APT sources
conn.run('echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null')
# Update your existing list of packages again for the addition to be recognized
conn.sudo('apt update')
# Make sure you are about to install from the Docker repo instead of the default Ubuntu repo
conn.run('apt-cache policy docker-ce')
# Install Docker
conn.sudo('apt install docker-ce')
# Check that it’s running
conn.sudo('systemctl status docker')
#+end_src
Note that the `install_docker` function is not idenpotent and may fail on certain steps during execution.
**** Configure Docker
Docker without `sudo`
#+begin_src bash
# Add your username to the docker group
sudo usermod -aG docker ${USER}
#+end_src
Apply the new group membership, log out of the server and back in (Optional?)
#+begin_src bash
su - ${USER}
groups
#+end_src
#+name fabfile.py
#+begin_src python :tangle devops-from-scratch/automated-basics/fabfile.py :mkdirp yes :cache no
@task
def configure_docker(ctx, server):
# Define the server connection
with get_server_connection(server) as conn:
# Add your username to the docker group
conn.sudo('usermod -aG docker ${USER}')
#conn.run('su - ${USER}')
conn.run('groups')
#+end_src
**** Check Docker's Installation
Check whether [[https://hub.docker.com/_/hello-world][Docker Hub]]'s images are accessible and can be downloaded
#+begin_src bash
docker run hello-world
#+end_src
#+name fabfile.py
#+begin_src python :tangle devops-from-scratch/automated-basics/fabfile.py :mkdirp yes :cache no
@task
def check_docker(ctx, server):
# Define the server connection
with get_server_connection(server) as conn:
conn.run('docker run hello-world')
#+end_src
* Containers
*** What? (Definition)
**** English
A container is a lightweight, standalone, executable software package that includes everything needed to run a piece of software, including the code, a runtime, libraries, environment variables, and config files. Containers are designed to provide a consistent and reproducible environment, which can be easily ported across different host systems. They isolate the application process from the host, providing an added layer of security, and allow for resource control, providing efficiency.
**** Français
Un conteneur est un package logiciel léger, autonome et exécutable qui comprend tout ce qui est nécessaire pour exécuter un logiciel, y compris le code, un environnement d'exécution, des bibliothèques, des variables d'environnement et des fichiers de configuration. Les conteneurs sont conçus pour fournir un environnement cohérent et reproductible, qui peut être facilement porté sur différents systèmes hôtes. Ils isolent le processus d'application de l'hôte, offrant une couche supplémentaire de sécurité, et permettent un contrôle des ressources, offrant une efficacité.
*** Why? (Benefit)
**** English
Containers are used because they offer several benefits. Firstly, they ensure that applications run the same regardless of where and when they are run, increasing deployment speed and reliability. Secondly, they provide process isolation by keeping applications and their dependencies separate from each other, thus improving security. Thirdly, they utilize system resources efficiently, which reduces infrastructure costs.
**** Français
Les conteneurs sont utilisés parce qu'ils offrent plusieurs avantages. Premièrement, ils garantissent que les applications fonctionnent de la même manière, quel que soit le lieu et le moment où elles sont exécutées, ce qui augmente la vitesse et la fiabilité du déploiement. Deuxièmement, ils fournissent une isolation des processus en gardant les applications et leurs dépendances séparées les unes des autres, améliorant ainsi la sécurité. Troisièmement, ils utilisent efficacement les ressources du système, ce qui réduit les coûts d'infrastructure.
*** When? (LXC vs Docker)
**** English
+ LXC (LinuX Containers) is an operating system-level virtualization method that allows running multiple isolated Linux systems (containers) on a single control host. LXC uses the Linux kernel cgroups and namespace functionality to isolate software applications and their environment from the underlying system. LXC was first released in 2008 and was the first, most complete implementation of Linux container manager. It's considered "lighter" than a VM because it runs at the OS level, not the hardware level.
+ Docker is an open-source platform used to automate the deployment, scaling, and management of applications by packaging them into containers. Docker, introduced in 2013, expanded on LXC's approach to providing a standardized interface for developers and sysadmins to develop, package, and run applications agnostic to the operating environment. Docker has since replaced LXC as the predominant containerization technology.
**** Français
+ LXC (LinuX Containers) est une méthode de virtualisation au niveau du système d'exploitation qui permet d'exécuter plusieurs systèmes Linux isolés (conteneurs) sur un seul hôte de contrôle. LXC utilise les fonctionnalités cgroups et namespace du noyau Linux pour isoler les applications logicielles et leur environnement du système sous-jacent. LXC a été lancé pour la première fois en 2008 et a été la première implémentation la plus complète du gestionnaire de conteneurs Linux. Il est considéré comme "plus léger" qu'une VM car il fonctionne au niveau du système d'exploitation, et non au niveau du matériel.
+ Docker est une plateforme open source utilisée pour automatiser le déploiement, la mise à l'échelle et la gestion des applications en les emballant dans des conteneurs. Docker, introduit en 2013, a développé l'approche de LXC en fournissant une interface standardisée pour les développeurs et les administrateurs système afin de développer, empaqueter et exécuter des applications indépendamment de l'environnement d'exploitation. Docker a depuis remplacé LXC comme la principale technologie de conteneurisation.
*** Docker Ecosystem
**** English
This diagram covers a few basic concepts in Docker:
#+begin_src mermaid
graph TB
DockerEngine(Docker Engine)
DockerEngine -- Runs --> DockerContainer
DockerEngine -- Builds --> DockerImage
DockerFile(Dockerfile: Recipe for Images) -- Defines --> DockerImage
DockerHub(Docker Hub: Public Repository) -- Stores and Shares --> DockerImage
DockerContainer(Docker Container: Tiny, stand-alone, executable package)
DockerImage(Docker Image: Blueprints for Containers) -- Creates --> DockerContainer
subgraph "Analogy: Construction"
DockerFile -- "Architect's Plan" --> DockerImage
DockerImage -- "Pre-fab house parts" --> DockerContainer
end
#+end_src
1. Docker Engine: The runtime that builds and runs containers. This is like a construction company that builds and maintains houses.
2. Dockerfile: A text document that contains all the commands a user could call on the command line to assemble an image. It's like an architect's plan for a house.
3. Docker Image: The blueprint of Docker that represents a specific instance of a container. It's like the pre-fabricated parts of a house, ready to be constructed.
4. Docker Container: An executable package that includes everything needed to run an application. This is like the constructed house, based on the pre-fab parts (Image).
5. Docker Hub: A cloud-based registry service which allows you to link to code repositories, build your images, and test them. This is like a public display or storage of various pre-fab house parts, which can be used by any construction company (Docker Engine).
**** Français
#+begin_src mermaid
graph TB
DockerEngine(Moteur Docker)
DockerEngine -- Exécute --> DockerContainer
DockerEngine -- Construit --> DockerImage
DockerFile(Fichier Docker: Recette pour les Images) -- Définit --> DockerImage
DockerHub(Hub Docker: Dépôt Public) -- Stocke et Partage --> DockerImage
DockerContainer(Conteneur Docker: Petit paquet exécutable autonome)
DockerImage(Image Docker: Plans pour les Conteneurs) -- Crée --> DockerContainer
subgraph "Analogie: Construction"
DockerFile -- "Plan d'architecte" --> DockerImage
DockerImage -- "Pièces préfabriquées de maison" --> DockerContainer
end
#+end_src
Ce diagramme couvre quelques concepts de base de Docker :
1. Docker Engine (Moteur Docker): L'environnement d'exécution qui construit et exécute des conteneurs. C'est comme une entreprise de construction qui construit et entretient des maisons.
2. Dockerfile (Fichier Docker): Un document texte qui contient toutes les commandes qu'un utilisateur pourrait appeler sur la ligne de commande pour assembler une image. C'est comme le plan d'un architecte pour une maison.
3. Docker Image (Image Docker): Le plan de Docker qui représente une instance spécifique d'un conteneur. C'est comme les pièces préfabriquées d'une maison, prêtes à être construites.
4. Docker Container (Conteneur Docker): Un paquet exécutable qui comprend tout ce qui est nécessaire pour exécuter une application. C'est comme la maison construite, basée sur les pièces préfabriquées (Image).
5. Docker Hub (Hub Docker): Un service de registre basé sur le cloud qui vous permet de lier à des dépôts de code, de construire vos images et de les tester. C'est comme une présentation publique ou un stockage de diverses pièces de maison préfabriquées, qui peuvent être utilisées par n'importe quelle entreprise de construction (Moteur Docker).
*** Docker Swarm
**** English
Docker Swarm is a container orchestration tool built into Docker. It allows you to manage multiple Docker hosts (nodes) and deploy services to them in a way that makes them act like a single, large host. The nodes in a Swarm communicate with each other to ensure they maintain the desired state of the services you deploy.
Let's break it down with an analogy. Think of Docker Swarm as a conductor of an orchestra, where each musician (Docker host/node) has a specific part to play (task), and the conductor (Docker Swarm) ensures everyone plays in harmony to create beautiful music (smoothly running application).
Here's a Mermaid diagram that illustrates how Docker Swarm works:
#+begin_src mermaid
graph LR
DockerSwarm(Docker Swarm: Orchestra Conductor)
DockerHost1(Docker Host 1: Musician 1)
DockerHost2(Docker Host 2: Musician 2)
DockerHost3(Docker Host 3: Musician 3)
DockerSwarm --> DockerHost1
DockerSwarm --> DockerHost2
DockerSwarm --> DockerHost3
subgraph ServicesOnHost1
Service1a(Service 1: Musical Score 1)
Service1b(Service 2: Musical Score 2)
end
subgraph ServicesOnHost2
Service2a(Service 1: Musical Score 1)
Service2b(Service 2: Musical Score 2)
end
subgraph ServicesOnHost3
Service3a(Service 1: Musical Score 1)
Service3b(Service 2: Musical Score 2)
end
DockerHost1 --> ServicesOnHost1
DockerHost2 --> ServicesOnHost2
DockerHost3 --> ServicesOnHost3
#+end_src
In this diagram:
+ Docker Swarm: This is the conductor of the orchestra, managing how each Docker host (or node) runs its services.
+ Docker Hosts (1, 2, 3): The musicians in the orchestra, each one playing their part according to the musical scores (services).
+ Services (1, 2): The musical scores, representing the different tasks that each Docker host needs to perform. Each Docker host might run multiple services, just like a musician might play different parts in the same piece of music.
The arrows represent Docker Swarm managing the Docker hosts, like a conductor directing the musicians in an orchestra, and each Docker host managing its services, like a musician following their musical scores.
**** Français
Docker Swarm est un outil d'orchestration de conteneurs intégré à Docker. Il permet de gérer plusieurs hôtes Docker (nœuds) et de déployer des services sur ceux-ci de manière à ce qu'ils agissent comme un seul et grand hôte. Les nœuds d'un Swarm communiquent entre eux pour garantir qu'ils maintiennent l'état souhaité des services que vous déployez.
Prenons une analogie. Pensez à Docker Swarm comme à un chef d'orchestre, où chaque musicien (hôte Docker/nœud) a une partie spécifique à jouer (tâche), et le chef d'orchestre (Docker Swarm) veille à ce que tout le monde joue en harmonie pour créer une belle musique (application fonctionnant sans accroc).
Voici un diagramme Mermaid qui illustre comment fonctionne Docker Swarm:
#+begin_src mermaid
graph LR
DockerSwarm(Docker Swarm: Chef d'Orchestre)
DockerHost1(Hôte Docker 1: Musicien 1)
DockerHost2(Hôte Docker 2: Musicien 2)
DockerHost3(Hôte Docker 3: Musicien 3)
DockerSwarm --> DockerHost1
DockerSwarm --> DockerHost2
DockerSwarm --> DockerHost3
subgraph ServicesOnHost1
Service1a(Service 1: Partition Musicale 1)
Service1b(Service 2: Partition Musicale 2)
end
subgraph ServicesOnHost2
Service2a(Service 1: Partition Musicale 1)
Service2b(Service 2: Partition Musicale 2)
end
subgraph ServicesOnHost3
Service3a(Service 1: Partition Musicale 1)
Service3b(Service 2: Partition Musicale 2)
end
DockerHost1 --> ServicesOnHost1
DockerHost2 --> ServicesOnHost2
DockerHost3 --> ServicesOnHost3
#+end_src
Dans ce diagramme :
+ Docker Swarm: Il s'agit du chef d'orchestre, qui gère la façon dont chaque hôte Docker (ou nœud) exécute ses services.
+ Hôtes Docker (1, 2, 3): Ce sont les musiciens de l'orchestre, chacun jouant son rôle en fonction des partitions musicales (services).
+ Services (1, 2): Ce sont les partitions musicales, représentant les différentes tâches que chaque hôte Docker doit accomplir. Chaque hôte Docker peut exécuter plusieurs services, tout comme un musicien peut jouer différentes parties dans la même pièce musicale.
Les flèches représentent Docker Swarm qui gère les hôtes Docker, comme un chef d'orchestre dirigeant les musiciens dans un orchestre, et chaque hôte Docker gère ses services, comme un musicien suivant ses partitions musicales.
*** Django Application Stack on Docker
**** English
Here's a high-level overview:
1. First, create a Dockerfile for your Django application, which will be the blueprint for building a Docker image of your application.
2. Then, define a Docker Compose file (docker-compose.yml), which will describe the multi-container Docker environment for your application. This file will include the configuration for your Django application container and Postgres database container.
3. Finally, use Docker Compose commands to build and start your application.
#+begin_src mermaid
graph TB
DockerComposeFile[Docker Compose File]
DockerComposeCommand{Docker Compose Commands}
DockerComposeFile --> DjangoAppDockerFile[Django App Dockerfile]
DjangoAppDockerFile --> DjangoAppImage[Django App Docker Image]
DjangoAppImage --> DjangoAppContainer[Django App Container]
DockerComposeFile --> PostgresImage[Postgres Docker Image]
PostgresImage --> PostgresContainer[Postgres Container]
DockerComposeCommand --> DockerComposeFile
DjangoAppContainer -- Connects To --> PostgresContainer
subgraph "Application Stack"
DjangoAppContainer -- "Django Application" --> PostgresContainer
end
#+end_src
This diagram represents the process:
+ Docker Compose File: This file contains the multi-container Docker configuration for your application.
+ Docker Compose Commands: These are the commands you run to start your Docker Compose environment.
+ Django App Dockerfile: This is the Dockerfile for your Django application. It's like a blueprint for creating a Docker image of your Django application.
+ Django App Docker Image: This is the Docker image created from the Dockerfile for your Django application.
+ Django App Container: This is the Docker container running your Django application. It is created from the Docker image of your Django application.
+ Postgres Docker Image: This is the Docker image for the Postgres database. It's usually downloaded from Docker Hub.
+ Postgres Container: This is the Docker container running your Postgres database. It is created from the Postgres Docker image.
The arrow labeled "Connects To" between the Django App Container and Postgres Container nodes indicates that the Django application connects to the Postgres database.
The subgraph titled "Application Stack" represents your complete application stack including the Django application and the Postgres database.
**** Français
Voici un aperçu de haut niveau :
1. Premièrement, créez un Dockerfile pour votre application Django, qui sera le plan pour construire une image Docker de votre application.
2. Ensuite, définissez un fichier Docker Compose (docker-compose.yml), qui décrira l'environnement Docker multi-conteneur pour votre application. Ce fichier inclura la configuration pour votre conteneur d'application Django et le conteneur de base de données Postgres.
3. Enfin, utilisez les commandes Docker Compose pour construire et démarrer votre application.
#+begin_src mermaid
graph TB
DockerComposeFile[Fichier Docker Compose]
DockerComposeCommand{Commandes Docker Compose}
DockerComposeFile --> DjangoAppDockerFile[Dockerfile App Django]
DjangoAppDockerFile --> DjangoAppImage[Image Docker App Django]
DjangoAppImage --> DjangoAppContainer[Conteneur App Django]
DockerComposeFile --> PostgresImage[Image Docker Postgres]
PostgresImage --> PostgresContainer[Conteneur Postgres]
DockerComposeCommand --> DockerComposeFile
DjangoAppContainer -- Se Connecte à --> PostgresContainer
subgraph "Pile d'Applications"
DjangoAppContainer -- "Application Django" --> PostgresContainer
end
#+end_src
Ce diagramme représente le processus :
+ Fichier Docker Compose : Ce fichier contient la configuration Docker multi-conteneur pour votre application.
+ Commandes Docker Compose : Ce sont les commandes que vous exécutez pour démarrer votre environnement Docker Compose.
+ Dockerfile App Django : C'est le Dockerfile pour votre application Django. C'est comme un plan pour créer une image Docker de votre application Django.
+ Image Docker App Django : C'est l'image Docker créée à partir du Dockerfile pour votre application Django.
+ Conteneur App Django : C'est le conteneur Docker exécutant votre application Django. Il est créé à partir de l'image Docker de votre application Django.
+ Image Docker Postgres : C'est l'image Docker pour la base de données Postgres. Elle est généralement téléchargée depuis Docker Hub.
+ Conteneur Postgres : C'est le conteneur Docker exécutant votre base de données Postgres. Il est créé à partir de l'image Docker Postgres.
La flèche étiquetée "Se Connecte à" entre les nœuds Conteneur App Django et Conteneur Postgres indique que l'application Django se connecte à la base de données Postgres.
Le sous-graphique intitulé "Pile d'Applications" représente votre pile d'applications complète, incluant l'application Django et la base de données Postgres.
** Demo
*** Build Local Container
Add the Dockerfile to the project:
#+name Dockerfile
#+begin_src bash :tangle devops-from-scratch/automated-basics/mytodos_app/Dockerfile :mkdirp yes :cache no
FROM python:3.10.6-bullseye
ENV PIP_NO_CACHE_DIR=True
ENV POETRY_VIRTUAL_ENVS_CREATE=False
RUN pip install -U \
pip \
setuptools \
wheel \
poetry
WORKDIR /app
RUN useradd -m -r user && \
chown user /app
COPY pyproject.toml poetry.lock ./
#RUN poetry install --no-dev
#RUN poetry install --no-dev && \
# rm -rf ~/.cache/pypoetry/{cache,artifacts}
RUN poetry export --without-hashes -f requirements.txt -o requirements.txt
RUN pip3 install -r requirements.txt
COPY . .
RUN python3 manage.py migrate
USER user
EXPOSE 8000/tcp
CMD ["python3", "manage.py", "runserver", "0.0.0.0:8000"]
#+end_src
Build the container:
#+begin_src bash
docker build -t mytodos:v1 ./
#+end_src
*** Export Docker Image Archive (.tar file)
#+begin_src bash
docker save mytodos:v1 > mytodos_app_container.tar
#+end_src
*** Upload Docker Image Archive and Install
#+name fabfile.py
#+begin_src python :tangle devops-from-scratch/automated-basics/fabfile.py :mkdirp yes :cache no
@task
def upload_and_install_docker_archive(ctx, server, docker_archive_src, docker_archive_dest):
# Define the server connection
with get_server_connection(server) as conn:
# Upload Docker Image Archive
conn.put(docker_archive_src, docker_archive_dest)
# Install Docker Archive
conn.run(f'docker load -i {docker_archive_dest}')
#+end_src
*** Run Docker Image
#+name fabfile.py
#+begin_src python :tangle devops-from-scratch/automated-basics/fabfile.py :mkdirp yes :cache no
@task
def remote_exec(ctx, server, cmd):
# Define the server connection
with get_server_connection(server) as conn:
# Execute Command
conn.run(cmd)
#+end_src
*** Fix Any Imcompatibilities
The Fix is:
#+begin_src bash
docker buildx build --platform linux/amd64 -t mytodos:v1 ./
#+end_src
#+begin_src bash
docker save mytodos:v2 > mytodos_app_container.tar
#+end_src
*** Docker Swarm
#+begin_src bash
fab remote-exec ubuntu.main "docker swarm init"
#+end_src
#+name config.ini
#+begin_src ini :tangle devops-from-scratch/automated-basics/config.ini :mkdirp yes :cache no
[ubuntu.worker1]
host = ubuntu@35.183.61.2
app_dir = /home/ubuntu
pem_file = ubuntu-worker1.pem
token = SWMTKN-1-24z5u12objl9qho41g77nhkmds8qpjoea9ta8mpvfvwxojjs3y-2k5buiwingdxa97zalxu2076b 172.26.7.85:2377
#+end_src
#+name config.ini
#+begin_src ini :tangle devops-from-scratch/automated-basics/config.ini :mkdirp yes :cache no
[ubuntu.worker2]
host = ubuntu@15.156.205.168
app_dir = /home/ubuntu
pem_file = ubuntu-worker2.pem
token = SWMTKN-1-24z5u12objl9qho41g77nhkmds8qpjoea9ta8mpvfvwxojjs3y-2k5buiwingdxa97zalxu2076b 172.26.7.85:2377
#+end_src
#+begin_src bash
docker swarm join --token SWMTKN-1-24z5u12objl9qho41g77nhkmds8qpjoea9ta8mpvfvwxojjs3y-2k5buiwingdxa97zalxu2076b 172.26.7.85:2377
docker node ls # Check Swarm Cluster
docker stack deploy -c docker-compose.yaml mytodos # Deploy Stack with service name mytodos
#+end_src
*** My Django TODOs App Stack
#+begin_src python
DATABASES = {
"default": {
"ENGINE": os.environ.get("SQL_ENGINE", "django.db.backends.sqlite3"),
"NAME": os.environ.get("SQL_DATABASE", BASE_DIR / "db.sqlite3"),
"USER": os.environ.get("SQL_USER", "user"),
"PASSWORD": os.environ.get("SQL_PASSWORD", "password"),
"HOST": os.environ.get("SQL_HOST", "localhost"),
"PORT": os.environ.get("SQL_PORT", "5432"),
}
}
#+end_src
#+name docker-compose.yaml
#+begin_src yaml :tangle devops-from-scratch/automated-basics/docker-compose.yaml :mkdirp yes :cache no
services:
db:
image: postgres
volumes:
- ./data/db:/var/lib/postgresql/data
environment:
- POSTGRES_DB=postgres
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=postgres
web:
image: mytodos:v3
#build: .
command: python manage.py runserver 0.0.0.0:8000
#volumes:
# - .:/code
ports:
- "8000:8000"
environment:
- POSTGRES_NAME=postgres
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=postgres
depends_on:
- db
#+end_src
* TODOs
*** TODO Recap of Previous Tutorial
**** TODO Review Application Functionality
**** TODO Provision VM
**** TODO Introduce Fabric
**** TODO Redeploy and Watch Application in a not so manual way using Fabric
**** TODO Scale Up with MultiProcessing
**** TODO Introduce Load Balancer
**** TODO Introduce K6, and Load Test
*** TODO Docker Containers
**** TODO Test local application against Containerized PostgreSQL
**** TODO Integration testing with Containerized PostgreSQL
**** TODO Create Dockerfile for Application
**** TODO Introduce Docker Compose for Multi Container Application
*** TODO Provision Docker
**** TODO Single Machine and Small
**** TODO Build Application and Export Docker Tar File
**** TODO Upload Docker Container Tar File and Run it with Containerized PostgreSQL
**** TODO Make PostgreSQL Persistent
**** TODO Load Testing with K6
*** TODO Scaling Out vs Scaling Up
**** TODO Scale Up with MultiProcessing on Bigger Machine
**** TODO Introduce Load Balancer (Containerized)
**** TODO Scale out with Docker Swarm
**** TODO Introduction to Portainer
**** TODO Load Testing with K6
* Miscellaneous
+ [[https://hub.docker.com/][Docker Hub]]
+ [[https://www.digitalocean.com/community/tutorials/how-to-install-and-use-docker-on-ubuntu-22-04][How to Install and Use Docker on Ubuntu 22:04]]
+ [[https://www.fabfile.org/][Fabric: Simple, Pythonic remote execution and deployment]]
+ [[https://docs.docker.com/samples/django/][Django Samples]]
+ [[https://www.youtube.com/watch?v=6IYKdiRpTi4][Docker Swarm Tutorial]]
+ [[https://www.youtube.com/watch?v=HSIJ24-yc3g][Docker Swarm Deploy Service]]
+ [[https://docs.portainer.io/start/install-ce/server/swarm/linux][Install Portainer CE with Docker Swarm on Linux]]
+ [[https://www.youtube.com/watch?v=7ywriX10RPo][Docker Swarm + NGINX Proxy Manager]]
+ [[https://nginxproxymanager.com][NGINX Proxy Manager]]