Rails on Docker: Kamal Proxy Health Checks with Cloud Load Balancers
In setting up a multi-instance development environment where multiple developers can deploy their own instance of an app to a single development VM, I stumbled across this issue.
Kamal Proxy is a load balancer, designed to be exposed to the Internet. However, in my client's situation, the server itself (on which Kamal Proxy was running) was behind a GCP Cloud Load Balancer.
GCP Load Balancers (and, presumably, similar products such as AWS) don't allow a hostname to be specified when performing a healthcheck. They ping the server on a given port, and expect an HTTP 200/OK
response.
If Kamal Proxy is running a single app, or an app that is running on a wildcard hostname, then this isn't a problem. However, as in this case, you may have multiple, isolated apps each with its own specific hostname, e.g:
httpx://appname-dev-mary.example.com
httpx://appname-dev-john.example.com
- ...
When the Cloud Load Balancer pinged the server without specifying a host, Kamal Proxy would respond with a 404. The result is that the server was never shown as healthy, so the Cloud Load Balancer would not route any traffic to it.
Workaround
Without a solution directly in Kamal Proxy itself, I worked around the problem by installing a small app directly into Kamal Proxy that simply returned a 200/OK
response when hit with /up
.
The app uses a customised nginx
container, which can be built use the following Dockerfile. For simplicity, you can create this on your target (deployment) server:
# Dockerfile
FROM nginx
RUN <<EOF
echo "server {
listen 80;
server_name localhost;
location /up {
default_type text/plain;
return 200 "OK";
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}" > /etc/nginx/conf.d/default.conf
EOF
Next, build and deploy the image into Kamal Proxy:
$ docker build --tag nginx-healthcheck .
$ docker run -d -it --network kamal --name gcp-healthcheck-1 nginx-healthcheck
$ docker exec -it kamal-proxy kamal-proxy deploy gcp-healthcheck-1 --target gcp-healthcheck-1:80 --health-check-path "/up" --deploy-timeout 5s
You can check the app is working correctly by sending a request to localhost on your server:
$ curl http://localhost/up
# OK
With the health-check app running, your Cloud Load Balancer will now see the server as healthy and start routing traffic to it.
👋 Thanks for reading - I hope you enjoyed this post. If you find it helpful and want to support further writing and tutorials like this one, please consider supporting my work with a coffee!
Support ☕️