Using Sinatra in Kubernetes
A short tale about host interfaces
Posted by Harry Lascelles on November 8, 2023
Here at Silvercat we make pragmatic use of microservices, and recently we were deploying a very simple Sinatra application to Kubernetes. The app was little more than this, launched with Rack:
require "sinatra"
class App < Sinatra::Base
set :public_folder, "./public"
get "/" do
redirect "/index.html"
end
end
run App
The app performed perfectly on a developer machine. We then deployed it to Kubernetes, and correctly assigned a Service to the app, and also an Ingress.
However, when we loaded it in the browser, we were faced with:
Bad Gateway
Since we were pretty sure the Service and Ingress were right, we gained shell access to the
pod, and ran netstat
to see what addresses the app was listening on:
# netstat -lntp
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 127.0.0.1:80 0.0.0.0:* LISTEN 7/rackup -p 80
There was our problem. The application was only listening on 127.0.0.0
, the address of the
loopback interface, and not on the externally accessible 0.0.0.0
address.
The solution was to tell Sinatra (which uses the Rack webserver) to bind to all interfaces, by adding the following ENV variable to the deployment:
env:
- name: RACK_ENV
value: production
We could now see the app was listening on the correct interface:
# netstat -lntp
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN 7/rackup -p 80
By default, when running a Sinatra app, it runs with a RACK_ENV
of development
. This is useful
as it provides detailed error messages and other debugging information.
However it also means the app binds to the localhost
interface, which is not available outside of
the pod in a Kubernetes environment. Changing the RACK_ENV
to production
tells Rack under the
hood to bind to all interfaces, and everything works as expected.
This is somewhat equivalent to running:
rackup --host 0.0.0.0
Note, we found that changing the Sinatra APP_ENV
to production
was not sufficient.
Now all is resolved, onwards and upwards with Sinatra!