Tag: Ruby

Multiple UNIX sockets bindings for Puma cluster

If you want to bind Puma to a unix socket, you can do this either by providing a -b options:

$ puma -b unix:///var/run/puma.sock

or using a puma.rb config file and setting the bind options.

# puma.rb config file
# ...config...
bind 'unix://var/run/puma.sock'
# ...config

Unfortunately if you try to bind multiple Pumas to one socket, you might end up with issue similar to this one:

2015/02/16 12:12:22 [error] 2476#0:
 *16083152 upstream timed out (110: Connection timed out) while reading response header from upstream, client: 64.251.13.85, server: app,
  request: "POST /notifier_api/v2/notices HTTP/1.1", upstream: "http://unix:///var/run/puma.sock:/notifier_api/v2/notices", host: "app"
2015/02/16 12:12:23 [error] 2476#0:
 *16083176 upstream timed out (110: Connection timed out) while reading response header from upstream, client: 64.251.10.203, server: app,
  request: "POST /notifier_api/v2/notices HTTP/1.1", upstream: "http://unix:///var/run/puma.sock:/notifier_api/v2/notices", host: "app"
2015/02/16 12:12:23 [error] 2474#0:
 *16083180 upstream timed out (110: Connection timed out) while reading response header from upstream, client: 64.251.13.85, server: app,
  request: "POST /notifier_api/v2/notices HTTP/1.1", upstream: "http://unix:///var/run/puma.sock:/notifier_api/v2/notices", host: "app"
2015/02/16 12:12:43 [error] 2476#0:
 *16083282 upstream timed out (110: Connection timed out) while reading response header from upstream, client: 64.251.13.85, server: app,
  request: "POST /notifier_api/v2/notices HTTP/1.1", upstream: "http://unix:///var/run/puma.sock:/notifier_api/v2/notices", host: "app"
2015/02/16 12:12:43 [error] 2476#0:
 *16083292 upstream timed out (110: Connection timed out) while reading response header from upstream, client: 64.251.13.85, server: app,
  request: "POST /notifier_api/v2/notices HTTP/1.1", upstream: "http://unix:///var/run/puma.sock:/notifier_api/v2/notices", host: "app"
2015/02/16 12:12:43 [error] 2476#0:
 *16083301 upstream timed out (110: Connection timed out) while reading response header from upstream, client: 64.251.13.85, server: app,
  request: "POST /notifier_api/v2/notices HTTP/1.1", upstream: "http://unix:///var/run/puma.sock:/notifier_api/v2/notices", host: "app"
2015/02/16 12:13:33 [error] 2474#0:
 *16083390 upstream timed out (110: Connection timed out) while reading response header from upstream, client: 64.251.13.85, server: app,
  request: "POST /notifier_api/v2/notices HTTP/1.1", upstream: "http://unix:///var/run/puma.sock:/notifier_api/v2/notices", host: "app"
2015/02/16 12:13:33 [error] 2474#0:
 *16083392 upstream timed out (110: Connection timed out) while reading response header from upstream, client: 64.251.13.85, server: app,
  request: "POST /notifier_api/v2/notices HTTP/1.1", upstream: "http://unix:///var/run/puma.sock:/notifier_api/v2/notices", host: "app"
2015/02/16 12:13:33 [error] 2474#0:
 *16083397 upstream timed out (110: Connection timed out) while reading response header from upstream, client: 64.251.13.85, server: app,
  request: "POST /notifier_api/v2/notices HTTP/1.1", upstream: "http://unix:///var/run/puma.sock:/notifier_api/v2/notices", host: "app"
2015/02/16 12:13:33 [error] 2474#0:
 *16083398 upstream timed out (110: Connection timed out) while reading response header from upstream, client: 64.251.13.85, server: app,
  request: "POST /notifier_api/v2/notices HTTP/1.1", upstream: "http://unix:///var/run/puma.sock:/notifier_api/v2/notices", host: "app"
2015/02/16 12:13:43 [error] 2477#0:
 *16083428 upstream timed out (110: Connection timed out) while reading response header from upstream, client: 64.251.13.85, server: app,
  request: "POST /notifier_api/v2/notices HTTP/1.1", upstream: "http://unix:///var/run/puma.sock:/notifier_api/v2/notices", host: "app"

So, what is the reason? Your one and only socket might just not be enough. You can call bind multiple times, to create multiple sockets that your clustered Puma will use:

# ...config...
bind 'unix://var/run/puma1.sock'
bind 'unix://var/run/puma2.sock'
bind 'unix://var/run/puma3.sock'
bind 'unix://var/run/puma4.sock'
bind 'unix://var/run/puma5.sock'
# ...config...

Btw great docs for Puma - it's nowhere mentioned and the easiest place to understand how it works, is in the source code, that looks like that:

 # Bind the server to +url+. tcp:// and unix:// are the only accepted
# protocols.
#
def bind(url)
@options[:binds] << url
end

Gitlab, Ruby private repository, Docker container and safe way to add SSH key for build process only

Note

This is not a tutorial on how to use Docker with SSH keys and private repositories. This is a solution to a particular safety issue when building Docker containers.

I won't get into details on how to create a Dockerfile with a SSH key, etc - there's enough about that in the Internet.

Problem - container stored SSH key

During the Docker build process, we need a SSH key for bundle install. We need it, because we have a Gitlab with private repositories that we want to pull. Since Docker caches this key, it will go to a production environment together with the whole container. It is unsafe. If someone got somehow into the container, he could use this key to get into your repositories.

Solution - during build only, one-time SSH key pair

This issue can be easily solved. To do so, we need to:

  1. Generate SSH key pair
  2. Add it to our Gitlab instance using Gitlab API
  3. Add private key to Docker
  4. Docker b uild (and bundle install)
  5. Remove/revoke our key from Gitlab
  6. Deploy container

There's still going to be a SSH key in the container, but it won't be valid. Since it will be valid only during the build process (which happens on the CI), there's no risk. The key gets revoked before we deploy container.

Code

For Gitlab API communication we will be using Gitlab API gem.

require 'gitlab'
require 'fileutils'

# I assume you have this script in the same place where your Dockerfile

# You can find your private token on the account page of your gitlab user
Gitlab.configure do |config|
  config.endpoint = 'http://your-gitlab/api/v3'
  config.private_token  = 'deploy/docker gitlab user private token'
end

# Add this if you use self signed SSL cert with Gitlab
OpenSSL::SSL::VERIFY_PEER = OpenSSL::SSL::VERIFY_NONE

'ssh-keygen -q -t rsa -f ./id_rsa -N ""'

key = Gitlab.create_ssh_key("docker-#{Time.now.to_s}", File.read('./id_rsa.pub'))

# Note that you should provide some sort of failover to remove key if build fails
system('docker build .')

Gitlab.delete_ssh_key(key.id)

FileUtils.rm('./id_rsa')
FileUtils.rm('./id_rsa.pub')

Copyright © 2025 Closer to Code

Theme by Anders NorenUp ↑