Rails Configuration in Kubernetes

Please forgive my strong opinion here: while we built Cloud 66 with Rails and ran it in production in containers and I can tell you with conviction, I dislike Docker's developer experience just as much as I like RoR's. The contrast couldn't be starker.

For almost all of us who build Rails applications, Docker is an excellent solution in production but a pain in the neck when it comes to the development environment.

If you're wondering, here is a short list of the challenges of running Rails in Docker:

  • What's the best way to build an image? Every developer for themselves? A base image shared with the team?
  • Where to put the code? In the container or mounted as a volume? (BTW, is it only me, or do you have to google Docker Volume syntax every time?)
  • Issues with Node.js and compiling assets, webpacker, and running webpacker development server in containers.
  • Docker Compose failing to shutdown containers randomly and consuming four times more resources compared to running Rails web and Sidekiq outside containers.
  • Have a private gem? Welcome to the nightmare of SSH key sharing inside of a container image.

On the other hand, using containers (specifically with Kubernetes) for Rails makes deployments significantly faster, more reliable, and lets you rollback deployments with little effort or stress.

Many everyday operational tasks like backups, scaling, load balancing, and creating a stack per branch improved significantly with Kubernetes (I admit there is a steep learning curve involved, but I guess, "no pain, no gain!").

For these reasons, we decided to stay with the native Rails development experience while running our non-development environments on Kubernetes. This presented a new challenge around configuration management for Rails. In Rails, configuration usually lives in YAML files, while in Kubernetes configuration settings are split one per file (using ConfigMaps or Secrets). We wanted a solution that could load configuration settings from a single YAML file or multiple individual files seamlessly.

The result of this effort is a new open-source Gem called Konfig. Here is what Konfig can do:

  • Load configuration from YAML or single-value files and present them to the application in a `Settings.foo.bar` syntax.
  • Verify configuration against strongly-typed validation before the application starts.
  • Support environment variable overrides (very useful in containerized environments)
  • Support JSON as a native data type inside YAML.
  • Support ERB with its YAML configuration files.

Configuration (most likely as an initializer)

if %w(development test).include?($environment)
    Konfig.configuration.mode = :yaml
    Konfig.configuration.workdir = File.join($rails_root, 'config', 'settings')

    # set the konfig log level to WARN
    konfig_logger = ActiveSupport::Logger.new(STDOUT)
    konfig_logger.level = ::Logger::WARN
    Konfig.configuration.logger = konfig_logger
else
    Konfig.configuration.mode = :directory
    # this is mounted by Kubernetes from ConfigMap or Secrets
    Konfig.configuration.workdir = "/app/config/settings/configuration"
end

Konfig.configuration.schema do
    required(:foo).schema do
        required(:bar).filled(:string)
        required(:buzz).filled(:string)
        required(:fuzz).filled(:boolean)
    end
end

Usage:

puts Settings.foo.bar

You can find Konfig here. Let us know how you're using it and how it can be improved!

Related Articles:

Try Cloud 66 for Free, No credit card required