Rails Kamal
WIP
TODO notes
kamal traefik reboot
- deploy lock
- SSL / Traefik SSL with Kamal 1
-
Traefik will be replaced by Kamal Proxy in Kamal v2 2
- from VM ```sh docker ps –format “table \t\t\t”
sudo docker logs -f container-id
- or remote
```sh
kamal app logs -f
- ssh limitations: if you have remote machines with different users than root and they are vary
-
dokku, cap rover, capistrano
-
What Kamal offers
- zero-downtime deploys,
- rolling restarts,
- asset bridging,
- local and remote builds,
- auxiliary services management,
- nice shortcuts for managing Docker in production. Kamal is Linux-flavour and language agnostic and will deploy any containerized application since it’s just a wrapper around Docker. (c)3
Kamal is built around
- SSH,
- Docker, and
- Traefik,
and is designed to deploy to servers that are already behind a load balancer by default. It can bootstrap your servers with Docker so you can use Kamal without a dedicated provisioning tool, but it’s not designed to do more than that.
Limitations
- can’t run multiple applications on the same server.
- No state of your servers beyond your configuration file. (Because of this, you cannot count on Kamal to decommission containers if you delete them from the config) 3
- cannot auto-provision servers dynamically like Docker native Swarm mode
- -> it doesn’t support auto-scaling.
- No Load Balancer.
Terminology
- service: A name for the service you are about to deploy, a name of your application
- server: A virtual or physical host running your application image
- role: A server role assigned to a group of servers running the same command like web or job
- accessory: A long-running auxiliary service like a database or microservice with an independent lifecycle
- volumes: A local mount directory or a Docker volume
- directory: A local mount directory that’s created automatically
Workflow / TLDR
gem install kamal
kamal init # init kamal deploy yaml.
# Configure deploy.yml
# Configure ENV to include KAMAL_REGISTRY_PASSWORD(i.e. your Docker Hub) and RAILS_MASTER_KEY
kamal setup # 1st build and deploy, but can be
kamal deploy # deploy update versions
kamal setup
:
- ensures Docker is installed,
- pushes your environment from the environment files,
- boots accessories like databases,
-
runs deploy step
kamal deploy
- starts a build process that will build the application out of a Dockerfile and push the image to the container registry with a service label and tag of the git commit (this label is required)
- boots Traefik on the hosts with the web role
- starts a new health check container named healthcheck-[SERVICE]-[VERSION] on port 3999 and checks if it passes the health check
- removes the health check container and checks for stale containers
- issues the application boot process with the new version
- removes old containers and images.
health check cord
A cord file is created inside .kamal/cords/[SERVICE]-web-[HASH]
on the host.
The container is started with this cord file mounted inside at /tmp/kamal-cord
and the health check is modified to check for this file.
docker run (...)
--volume $(pwd)/.kamal/cords/[SERVICE]-web-[HASH]:/tmp/kamal-cord
--health-cmd "(...) && (stat /tmp/kamal-cord/cord > /dev/null || exit 1)"
- the requested version is tagged in Docker as :latest
- new assets are extracted
- asset volumes are synced
- old version is renamed to [OLD_VERSION]replaced[HASH]
- a cord is tied
- the new version is run
- the old version cord is cut
- the old version is stopped
- assets are cleaned up
Hooks
- pre-connect: Before connecting to hosts
- pre-traefik-reboot: Before rebooting Traefik
- post-traefik-reboot: After rebooting Traefik
- docker-setup: After Docker is installed during bootstrap
- pre-build: Before the image is built
- pre-deploy: Before the application boot
- post-deploy: After the application finished booting
Deploy config
service: kamal-test
image: friendlyantz/kamal-test
servers:
- 192.168.1.46
registry:
username: friendlyantz
password:
- DOCKER_ACCESS_TOKEN=${DOCKER_ACCESS_TOKEN} # fetch from ENV
env:
secret:
- RAILS_MASTER_KEY # fetch from .env (avoid .env at all costs)
clear:
RAILS_LOG_TO_STDOUT: true # optional
DNS
nslookup -q=ns yourwebsite.io
Server Roles
- web
- job
- scheduler Kamal will only ever run one instance for each role on each specified host. If you need parallelism like running more workers on a single host, you have to implement it inside your image.
deploy.yaml
# config/deploy.yml
...
servers:
web:
hosts:
- 165.232.112.197
port: "3000:3000"
cmd: bin/rails server
labels:
traefik.http.routers.pinned-web.rule: Host(`pinnedjobs.com`)
traefik.http.routers.pinned-web-secure.entrypoints: websecure
traefik.http.routers.pinned-web-secure.rule: Host(`pinnedjobs.com`)
traefik.http.routers.pinned-web-secure.tls: true
traefik.http.routers.pinned-web-secure.tls.certresolver: letsencrypt
options:
network: "private"
options for CPU / Memory
# config/deploy.yml
...
servers:
web:
hosts:
- 165.232.112.197
options:
cpus: 1.5
cpuset-cpus: "0,3"
memory: 1g
memory-swap: 1.5g # actual swap is 0.5g,since 'mempry-swap' in this case is the amount of combined memory and swap that can be used.
# We can also pass swappiness per container group by passing --memory-swappiness, from 0 (off) to 100 (maximum swappiness). Swap has to be enabled and exist for this to work.
Always leave a little bit of dedicated resources unassigned for debugging remotely
Builder
kamal deploy -- version=[TAG]
kamal app images
ENV
# config/deploy.yml
...
env:
clear:
KEY_NAME: "value"
secret:
- TOP_SECRET
kamal envify
kamal envify --skip-push
An initial kamal setup does push the initial environment automatically, however, subsequent kamal deploy won’t push any environment variables on its own.
kamal env push
kamal app boot # redeploying to get env vars
Leave a comment