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
- 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
You can find Konfig here. Let us know how you're using it and how it can be improved!