A web application pod proof of concept. Cyan illustrates a blue/green deployment schema using podman, quadlets, and caddy.
graph LR;
A[Client]<-->B[Host];
B[Host]o--oC[Caddy Proxy];
C x-- staging --xD["App (Blue)"];
C<-- prod -->E["App (Green)"];
D<-->Database;
E<-->Database;
Inteded for educational purposes
-
Cyan uses rootless podman which can be installed via Official Installation Instructions
- This Tutorial includes additional instructions for rootless installs
- This Tutorial includes additional instructions for rootless installs
-
Clone the cyan repo
git clone https://github.com/latelatelate/cyan.git- Run
bin/init.shto move quadlet files into systemd folder and finish system configuration.- Feel free to modify and move these manually
- Feel free to modify and move these manually
- Run
-
Notes:
- Requires systemd for quadlet service management
- Uses socket activation for the reverse proxy
- Originally created on a debian host, so these instructions might need to be adapted for your specific environment
Quadlets leverage systemd to manage containers and pods. Besides standard podman commands, we can also use:
systemctl --user status cyan-pod
systemctl --user start cyan-pod
systemctl --user stop cyan-podIndividual services within pods can also be micromanaged:
systemctl --user reload cyan-proxySwapping between app servers theoretically has zero downtime and is achieved by:
- Building a new app image
- If necessary, updating the staging
.containerquadlet file with newImage=build:tag- Reload systemctl w/ changes
systemctl --user daemon-reload
- Reload systemctl w/ changes
- If necessary, updating the staging
- Modifying the
cyan.internal.conffile atvolumes/caddy-etc/conf.d/cyan.internal.conf
Find the snippet in cyan.internal.conf containing:
# Main services on the custom network
https://cyan.internal {
encode gzip zstd
import green
log
}Specify with either import blue or import green
After modification, reload the proxy configuration:
systemctl --user reload cyan-proxySuccess! Traffic is routed through the new app container instead.