commit 88d8062e94e609264d4621ee698fb358ec8cdce9
parent 897557a434b555eccba14f30eb188cfcfcf157db
Author: David Eisinger <[email protected]>
Date: Wed, 2 Oct 2024 09:37:54 -0400
Add Docker references
Diffstat:
4 files changed, 2322 insertions(+), 0 deletions(-)
diff --git a/content/elsewhere/local-docker-best-practices/index.md b/content/elsewhere/local-docker-best-practices/index.md
@@ -4,6 +4,19 @@ date: 2022-05-05T00:00:00+00:00
draft: false
canonical_url: https://www.viget.com/articles/local-docker-best-practices/
featured: true
+references:
+- title: "Ruby on Whales: Dockerizing Ruby and Rails development—Martian Chronicles, Evil Martians’ team blog"
+ url: https://evilmartians.com/chronicles/ruby-on-whales-docker-for-ruby-rails-development
+ date: 2024-10-02T13:37:32Z
+ file: evilmartians-com-6ehmrb.txt
+- title: "Docker: Right for Us. Right for You? | Viget"
+ url: https://www.viget.com/articles/docker-right-for-us-right-for-you-1/
+ date: 2024-10-02T13:37:33Z
+ file: www-viget-com-h4onv6.txt
+- title: "Docker + Rails: Solutions to Common Hurdles | Viget"
+ url: https://www.viget.com/articles/docker-rails-solutions-to-common-hurdles/
+ date: 2024-10-02T13:37:33Z
+ file: www-viget-com-1hyo6b.txt
---
Here at Viget, Docker has become an indispensable tool for local
diff --git a/static/archive/evilmartians-com-6ehmrb.txt b/static/archive/evilmartians-com-6ehmrb.txt
@@ -0,0 +1,1532 @@
+[1]
+[2]Hire Martians
+[3]
+Services
+[4]
+Clients
+[5]
+Products
+[6]
+Open Source
+[7]
+Blog
+[8]
+Events
+[9]
+Podcast
+[10]
+[11]
+[12]
+[13]
+[14]
+[15]Hire Martians
+[16]
+Services
+[17]
+Clients
+[18]
+Products
+[19]
+Open Source
+[20]
+Blog
+[21]
+Events
+[22]
+Podcast
+[23]
+[24]
+[25]
+[26]
+[27]
+7-
+Oct
+8
+Meet us at Rocky Mountain Ruby in Boulder, Colorado!
+[28]Hire Martians
+[29]
+[30]
+[31]
+[32]
+
+Ruby on Whales: Dockerizing Ruby and Rails development
+
+March 15, 2022
+[svg]
+Cover for Ruby on Whales: Dockerizing Ruby and Rails developmentCover for Ruby
+on Whales: Dockerizing Ruby and Rails development
+
+Topics
+
+ • [33]Backend
+ • [34]Full Cycle Software Development
+ • [35]Performance Optimization
+ • [36]Ruby on Rails
+ • [37]Ruby
+ • [38]Docker
+ • [39]PostgreSQL
+ • [40]Node.js
+
+━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+
+ • [svg]
+ Vladimir Dementyev
+
+ Vladimir Dementyev
+
+ Principal Backend Engineer
+
+━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+
+Translations
+
+ • Japanese[41]クジラに乗ったRuby: Evil Martians流Docker+Ruby/Rails開発環境構
+ 築
+ • Chinese[42]骑鲸之路——Docker模式下的Rails开发环境构筑
+
+This post introduces the Docker configuration I use for developing my Ruby on
+Rails projects. This configuration came out of—and then further evolved—during
+[DEL:production:DEL] development at Evil Martians. Read on to learn all the
+details, and feel free to use it, share it, and enjoy!
+
+Notice: This article is regularly updated with the best and latest
+recommendations; for details, take a look at the [43]Changelog.
+
+So, where to start? This has been a pretty long journey: back in the day, I
+used to develop using Vagrant, but its VMs were a bit too heavy for my 4GB RAM
+laptop. In 2017, I decided to make the switch to containers, and this was how I
+first began using Docker. But don’t get the impression that this was an instant
+fix! I was in search of a configuration that was perfect for myself, my team,
+and well, everyone else. And something which was just good enough would not cut
+it. It took quite some time to develop a standard approach (as more formerly
+enshrined with the first release of this article in 2019). Since that first
+iteration of this post revealed my secret to the world, many Rails teams and
+devs have adopted my technique, and actually, they’ve helped to contribute and
+improve it!
+
+With that out of the way, let me just go ahead and present the config itself.
+Along the way, I’ll explain almost every line (because we’ve all had enough of
+those cryptic tutorials that just assume you know stuff).
+
+This post was originally adapted from my talk at RailsConf 2019: [44]
+“Terraforming legacy Rails applications”.
+
+ The source code can be found in the [45]evilmartians/ruby-on-whales
+ repository on GitHub.
+
+Before we get on with it, let’s note that we’ll be using up-to-date software
+versions for this example: Docker Desktop 20.10+ and Docker Compose v2,
+Ruby 3.1.0, PostgreSQL 14, etc.
+
+The bulk of the post consists mostly of annotated code and configuration
+examples, structured as follows:
+
+ • [46]The basics: Dockerfile and docker-compose.yml
+ • [47]Introducing Dip
+ • [48](Micro-)services vs Docker for development
+ • [49]From development to production
+ • [50]Introducing the Ruby on Whales interactive generator
+
+Basic Docker configuration
+
+[51]Dockerfile
+
+The Dockerfile defines our Ruby application’s environment. This environment is
+where we’ll run servers, access the console (rails c), perform tests, do Rake
+tasks, and otherwise interact with our code in any way as developers:
+
+# syntax=docker/dockerfile:1
+
+ARG RUBY_VERSION
+ARG DISTRO_NAME=bullseye
+
+FROM ruby:$RUBY_VERSION-slim-$DISTRO_NAME
+
+ARG DISTRO_NAME
+
+# Common dependencies
+RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
+ --mount=type=cache,target=/var/lib/apt,sharing=locked \
+ --mount=type=tmpfs,target=/var/log \
+ rm -f /etc/apt/apt.conf.d/docker-clean; \
+ echo 'Binary::apt::APT::Keep-Downloaded-Packages "true";' > /etc/apt/apt.conf.d/keep-cache; \
+ apt-get update -qq \
+ && DEBIAN_FRONTEND=noninteractive apt-get install -yq --no-install-recommends \
+ build-essential \
+ gnupg2 \
+ curl \
+ less \
+ git
+
+# Install PostgreSQL dependencies
+ARG PG_MAJOR
+RUN curl -sSL https://www.postgresql.org/media/keys/ACCC4CF8.asc | \
+ gpg --dearmor -o /usr/share/keyrings/postgres-archive-keyring.gpg \
+ && echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/postgres-archive-keyring.gpg] https://apt.postgresql.org/pub/repos/apt/" \
+ $DISTRO_NAME-pgdg main $PG_MAJOR | tee /etc/apt/sources.list.d/postgres.list > /dev/null
+RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
+ --mount=type=cache,target=/var/lib/apt,sharing=locked \
+ --mount=type=tmpfs,target=/var/log \
+ apt-get update -qq && DEBIAN_FRONTEND=noninteractive apt-get -yq dist-upgrade && \
+ DEBIAN_FRONTEND=noninteractive apt-get install -yq --no-install-recommends \
+ libpq-dev \
+ postgresql-client-$PG_MAJOR
+
+# Install NodeJS and Yarn
+ARG NODE_MAJOR
+ARG YARN_VERSION=latest
+RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
+ --mount=type=cache,target=/var/lib/apt,sharing=locked \
+ --mount=type=tmpfs,target=/var/log \
+ apt-get update && \
+ apt-get install -y curl software-properties-common && \
+ curl -fsSL https://deb.nodesource.com/gpgkey/nodesource.gpg.key | apt-key add - && \
+ echo "deb https://deb.nodesource.com/node_${NODE_MAJOR}.x $(lsb_release -cs) main" | tee /etc/apt/sources.list.d/nodesource.list && \
+ apt-get update && \
+ DEBIAN_FRONTEND=noninteractive apt-get install -yq --no-install-recommends nodejs
+RUN npm install -g yarn@$YARN_VERSION
+
+# Application dependencies
+# We use an external Aptfile for this, stay tuned
+COPY Aptfile /tmp/Aptfile
+RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
+ --mount=type=cache,target=/var/lib/apt,sharing=locked \
+ --mount=type=tmpfs,target=/var/log \
+ apt-get update -qq && DEBIAN_FRONTEND=noninteractive apt-get -yq dist-upgrade && \
+ DEBIAN_FRONTEND=noninteractive apt-get install -yq --no-install-recommends \
+ $(grep -Ev '^\s*#' /tmp/Aptfile | xargs)
+
+# Configure bundler
+ENV LANG=C.UTF-8 \
+ BUNDLE_JOBS=4 \
+ BUNDLE_RETRY=3
+
+# Store Bundler settings in the project's root
+ENV BUNDLE_APP_CONFIG=.bundle
+
+# Uncomment this line if you want to run binstubs without prefixing with `bin/` or `bundle exec`
+# ENV PATH /app/bin:$PATH
+
+# Upgrade RubyGems and install the latest Bundler version
+RUN gem update --system && \
+ gem install bundler
+
+# Create a directory for the app code
+RUN mkdir -p /app
+WORKDIR /app
+
+# Document that we're going to expose port 3000
+EXPOSE 3000
+# Use Bash as the default command
+CMD ["/bin/bash"]
+
+This configuration only contains the essentials, and so it can be used as a
+starting point. Let me illustrate what we’re are doing here a bit further. The
+first three lines might look a bit strange:
+
+ARG RUBY_VERSION
+ARG DISTRO_NAME=bullseye
+FROM ruby:$RUBY_VERSION-slim-$DISTRO_NAME
+
+Why not just use FROM ruby:3.1.0, or whatever is the stable Ruby version du
+jour? Well, we’re going this route because we want to make our environment
+configurable from the outside using Dockerfile as a sort of a template:
+
+ • The exact versions of the runtime dependencies are specified in the
+ docker-compose.yml (see below 👇).
+ • The list of apt-installable dependencies is stored in a separate file
+ (also, see below 👇👇).
+
+Additionally, we parameterize the Debian release (bullseye by default) to make
+sure we’re adding the correct sources for our other dependencies (such as
+PostgreSQL).
+
+Alright, now, note that we declare the argument once again after the FROM
+statement:
+
+FROM ruby:$RUBY_VERSION-slim-$DISTRO_NAME
+ARG DISTRO_NAME
+
+That’s the tricky part of how Dockerfiles work: the args are reset after the
+FROM statement. For more details, check out [52]this issue.
+
+Moving on, the rest of the file contains the actual build steps. First, we’ll
+need to manually install some common system dependencies (Git, cURL, etc.), as
+we’re using the slim base Docker image to reduce the size:
+
+# Common dependencies
+RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
+ --mount=type=cache,target=/var/lib/apt,sharing=locked \
+ --mount=type=tmpfs,target=/var/log \
+ apt-get update -qq \
+ && DEBIAN_FRONTEND=noninteractive apt-get install -yq --no-install-recommends \
+ build-essential \
+ gnupg2 \
+ curl \
+ less \
+ git
+
+We’ll explain all the details of installing system dependencies below,
+alongside the application-specific dependencies.
+
+Installing PostgreSQL and NodeJS via apt requires adding their deb package
+repos to the sources list.
+
+Here’s PostgreSQL (based on the [53]official documentation):
+
+ARG PG_MAJOR
+RUN curl -sSL https://www.postgresql.org/media/keys/ACCC4CF8.asc | \
+ gpg --dearmor -o /usr/share/keyrings/postgres-archive-keyring.gpg \
+ && echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/postgres-archive-keyring.gpg] https://apt.postgresql.org/pub/repos/apt/" \
+ $DISTRO_NAME-pgdg main $PG_MAJOR | tee /etc/apt/sources.list.d/postgres.list > /dev/null
+RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
+ --mount=type=cache,target=/var/lib/apt,sharing=locked \
+ --mount=type=tmpfs,target=/var/log \
+ apt-get update -qq && DEBIAN_FRONTEND=noninteractive apt-get -yq dist-upgrade && \
+ DEBIAN_FRONTEND=noninteractive apt-get install -yq --no-install-recommends \
+ libpq-dev \
+ postgresql-client-$PG_MAJOR
+
+Since we aren’t expecting anyone to use this Dockerfile without [54]
+Docker Compose, we don’t provide a default value for the PG_MAJOR argument (the
+same applies to NODE_MAJOR below, and YARN_VERSION further below).
+
+Also, notice that in the code above that the DISTRO_NAME argument which we
+defined at the very beginning of the file comes back into play.
+
+And, we repeat our apt-get ... apt-get clean spell again: we want to make sure
+all the major pieces of our environment are built in an isolated way (this will
+help us to better utilize Docker cache layers when performing upgrades).
+
+For NodeJS (from the [55]NodeSource repo):
+
+ARG NODE_MAJOR
+RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
+ --mount=type=cache,target=/var/lib/apt,sharing=locked \
+ --mount=type=tmpfs,target=/var/log \
+ apt-get update && \
+ apt-get install -y curl software-properties-common && \
+ curl -fsSL https://deb.nodesource.com/gpgkey/nodesource.gpg.key | apt-key add - && \
+ echo "deb https://deb.nodesource.com/node_${NODE_MAJOR}.x $(lsb_release -cs) main" | tee /etc/apt/sources.list.d/nodesource.list && \
+ apt-get update && \
+ DEBIAN_FRONTEND=noninteractive apt-get install -yq --no-install-recommends nodejs
+
+Then, we install Yarn via NPM:
+
+ARG YARN_VERSION=latest
+RUN npm install -g yarn@$YARN_VERSION
+
+So, why are we adding NodeJS and Yarn in the first place? Although Rails 7
+allows you to [56]go Node-less via [57]import maps or precompiled binaries
+(like [58]tailwindcss-rails), these additions increase the chances of
+supporting legacy pipelines or adding modern Webpacker alternatives.
+
+Now it’s time to install the application-specific dependencies:
+
+COPY Aptfile /tmp/Aptfile
+RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
+ --mount=type=cache,target=/var/lib/apt,sharing=locked \
+ --mount=type=tmpfs,target=/var/log \
+ apt-get update -qq && DEBIAN_FRONTEND=noninteractive apt-get -yq dist-upgrade && \
+ DEBIAN_FRONTEND=noninteractive apt-get install -yq --no-install-recommends \
+ $(grep -Ev '^\s*#' /tmp/Aptfile | xargs)
+
+Let’s talk about that Aptfile trick a bit:
+
+COPY Aptfile /tmp/Aptfile
+RUN apt-get install \
+ $(grep -Ev '^\s*#' /tmp/Aptfile | xargs) \
+
+I borrowed this idea from [59]heroku-buildpack-apt, which allows for installing
+additional packages on Heroku. If you’re using this buildpack, you can even
+re-use the same Aptfile for both the local and the production environment.
+
+Our [60]default Aptfile contains only a single package (we’ll use Vim to edit
+the Rails Credentials):
+
+vim
+
+In one of the previous projects I worked on, we generated PDFs using LaTeX and
+[61]TexLive. In a case like that, our Aptfile might look like this:
+
+vim
+# TeX packages
+texlive
+texlive-latex-recommended
+texlive-fonts-recommended
+texlive-lang-cyrillic
+
+By doing this we can keep task-specific dependencies in a separate file, thus
+making our Dockerfile more universal.
+
+With regards to DEBIAN_FRONTEND=noninteractive, I kindly ask you to take a look
+at this [62]answer on Ask Ubuntu.
+
+The --no-install-recommends option helps save some space (and makes our image
+smaller) by disabling the installation of recommended packages. You can see
+more [63]about saving disk space here.
+
+That first (fairly cryptic) part of every RUN statement that installs packages
+also serves the same purpose: it moves out the local repository of retrieved
+package files into a cache that will be preserved between builds. We need this
+magic to be in every RUN statement that installs packages to make sure this
+particular [64]Docker layer doesn’t contain any garbage. It also greatly speeds
+up image build!
+
+[65]RUN --mount is relatively new feature of Docker. Traditionally, every
+package installation step would be appended with a dark spell full of rm and
+truncate commands to clean up temporary files.
+
+The final part of the Dockerfile is mostly devoted to Bundler:
+
+# Configure bundler
+ENV LANG=C.UTF-8 \
+ BUNDLE_JOBS=4 \
+ BUNDLE_RETRY=3 \
+
+# Store Bundler settings in the project's root
+ENV BUNDLE_APP_CONFIG=.bundle
+
+# Uncomment this line if you want to run binstubs without prefixing with `bin/` or `bundle exec`
+# ENV PATH /app/bin:$PATH
+
+# Upgrade RubyGems and install the latest Bundler version
+RUN gem update --system && \
+ gem install bundler
+
+Using LANG=C.UTF-8 sets the default locale to UTF-8. This is an emotional
+setting, as otherwise, Ruby would use US-ASCII for strings—and that’d mean
+waving goodbye to those sweet, sweet emojis! 👋
+
+Setting BUNDLE_APP_CONFIG is required if you’ll use the <root>/.bundle folder
+to store project-specicic Bundler settings (like credentials for private gems).
+The default Ruby image [66]defines this variable so Bundler doesn’t fall back
+to the local config.
+
+Optionally, you can add your <root>/bin folder to the PATH in order to run
+commands without bundle exec. We don’t do this by default, because it could
+break in a multi-project environment (for instance, when you have local gems or
+engines in your Rails app).
+
+Previously, we also had to specify the Bundler version (taking advantage of
+[67]some hacks to make sure it’s picked up by the system). Luckily, since
+Bundler 2.3.0, we no longer need to manually install the version defined in the
+Gemfile.lock (BUNDLED_WITH). Instead, to avoid conflicts, Bundler [68]does this
+for us.
+
+[69]compose.yml
+
+[70]Docker Compose is a tool we can use to orchestrate our containerized
+environment. It allows us to link containers to each other, and to define
+persistent volumes and services.
+
+Below is the compose file for developing a typical Rails application with
+PostgreSQL as the database, and with Sidekiq as the background job processor:
+
+x-app: &app
+ build:
+ context: .
+ args:
+ RUBY_VERSION: '3.2.2'
+ PG_MAJOR: '15'
+ NODE_MAJOR: '18'
+ image: example-dev:1.0.0
+ environment: &env
+ NODE_ENV: ${NODE_ENV:-development}
+ RAILS_ENV: ${RAILS_ENV:-development}
+ tmpfs:
+ - /tmp
+ - /app/tmp/pids
+
+x-backend: &backend
+ <<: *app
+ stdin_open: true
+ tty: true
+ volumes:
+ - ..:/app:cached
+ - bundle:/usr/local/bundle
+ - rails_cache:/app/tmp/cache
+ - node_modules:/app/node_modules
+ - packs:/app/public/packs
+ - packs-test:/app/public/packs-test
+ - history:/usr/local/hist
+ - ./.psqlrc:/root/.psqlrc:ro
+ - ./.bashrc:/root/.bashrc:ro
+ environment: &backend_environment
+ <<: *env
+ REDIS_URL: redis://redis:6379/
+ DATABASE_URL: postgres://postgres:postgres@postgres:5432
+ WEBPACKER_DEV_SERVER_HOST: webpacker
+ MALLOC_ARENA_MAX: 2
+ WEB_CONCURRENCY: ${WEB_CONCURRENCY:-1}
+ BOOTSNAP_CACHE_DIR: /usr/local/bundle/_bootsnap
+ XDG_DATA_HOME: /app/tmp/caches
+ YARN_CACHE_FOLDER: /app/node_modules/.yarn-cache
+ HISTFILE: /usr/local/hist/.bash_history
+ PSQL_HISTFILE: /usr/local/hist/.psql_history
+ IRB_HISTFILE: /usr/local/hist/.irb_history
+ EDITOR: vi
+ depends_on: &backend_depends_on
+ postgres:
+ condition: service_healthy
+ redis:
+ condition: service_healthy
+
+services:
+ rails:
+ <<: *backend
+ command: bundle exec rails
+
+ web:
+ <<: *backend
+ command: bundle exec rails server -b 0.0.0.0
+ ports:
+ - '3000:3000'
+ depends_on:
+ webpacker:
+ condition: service_started
+ sidekiq:
+ condition: service_started
+
+ sidekiq:
+ <<: *backend
+ command: bundle exec sidekiq -C config/sidekiq.yml
+
+ postgres:
+ image: postgres:14
+ volumes:
+ - .psqlrc:/root/.psqlrc:ro
+ - postgres:/var/lib/postgresql/data
+ - history:/user/local/hist
+ environment:
+ PSQL_HISTFILE: /user/local/hist/.psql_history
+ POSTGRES_PASSWORD: postgres
+ ports:
+ - 5432
+ healthcheck:
+ test: pg_isready -U postgres -h 127.0.0.1
+ interval: 5s
+
+ redis:
+ image: redis:6.2-alpine
+ volumes:
+ - redis:/data
+ ports:
+ - 6379
+ healthcheck:
+ test: redis-cli ping
+ interval: 1s
+ timeout: 3s
+ retries: 30
+
+ webpacker:
+ <<: *app
+ command: bundle exec ./bin/webpack-dev-server
+ ports:
+ - '3035:3035'
+ volumes:
+ - ..:/app:cached
+ - bundle:/usr/local/bundle
+ - node_modules:/app/node_modules
+ - packs:/app/public/packs
+ - packs-test:/app/public/packs-test
+ environment:
+ <<: *env
+ WEBPACKER_DEV_SERVER_HOST: 0.0.0.0
+ YARN_CACHE_FOLDER: /app/node_modules/.yarn-cache
+
+volumes:
+ bundle:
+ node_modules:
+ history:
+ rails_cache:
+ postgres:
+ redis:
+ packs:
+ packs-test:
+
+We define six services and two extension fields (x-app and x-backend). [71]
+Extension fields allow us to define common parts of the configuration. We can
+attach YAML anchors to them, and later, embed anywhere in the file.
+
+NOTE: In the end, we don’t use Docker Compose or execute the docker compose up
+command in order to run our application. Instead, we use Dip (see [72]below),
+and thus, the compose.yml file only acts as a services registry. Another
+important thing to note is that we put the compose.yml file into the .dockerdev
+/ folder. This is why we mount the source code as ..:/app and not .:/app.
+Please, keep this in mind if you’re considering using this configuration
+without Dip (which is not recommended).
+
+On that note, let’s go ahead and take a thorough look at each service.
+
+x-app
+
+The main purpose of this extension is to provide all the required information
+to build our application container (as defined in the Dockerfile above):
+
+x-app: &app
+ build:
+ context: .
+ args:
+ RUBY_VERSION: '3.2.2'
+ PG_MAJOR: '15'
+ NODE_MAJOR: '18'
+
+What is the context? The context directory defines the [73]build context for
+Docker. This is something like a working directory for the build process—for
+example, when we execute the COPY command. As this directory is packaged and
+sent to the Docker daemon every time an image is built, it’s better to keep it
+as small as possible. We’re good here, since our context is just the .dockerdev
+folder.
+
+And, as we mentioned earlier, we’ll specify the exact version of our
+dependencies using the args as declared in the Dockerfile.
+
+It’s also a good idea to pay attention to the way we tag images:
+
+image: example-dev:1.0.0
+
+One of the benefits of using Docker for development is the ability to
+automatically synchronize configuration changes across the team. This means the
+only time you need to upgrade the local image version is when you make changes
+to it (or to the arguments or files it relies on). Using example-dev:latest is
+like shooting yourself in the foot.
+
+Keeping an image version also helps work with two different environments
+without any additional hassle. For example, when working on a long-standing
+“chore/upgrade-to-ruby-3” branch, you can easily switch to master and use the
+older image with the older version of Ruby: no need to rebuild anything.
+
+ Rule of thumb: Increase the version number in the image tag every time you
+ change Dockerfile or its arguments (upgrading dependencies, etc.)
+
+Next, we add some common environment variables (those shared by multiple
+services, e.g., Rails and Webpacker):
+
+environment: &env
+ NODE_ENV: ${NODE_ENV:-development}
+ RAILS_ENV: ${RAILS_ENV:-development}
+
+There are several things going on here, but I’d like to focus on just one: the
+X=${X:-smth} syntax. This could be translated as “For X variable within the
+container, if present, use the host machine’s X env variable, otherwise, use
+another value”. Thus, we make it possible to run a service in a different
+environment specified along with a command, e.g., RAILS_ENV=test docker-compose
+up rails.
+
+Note that we’re using a dictionary value (NODE_ENV: xxx) and not a list value (
+- NODE_ENV=xxx) for the environment field. This allows us to re-use the common
+settings (see below).
+
+We also tell Docker to [74]use tmpfs for the /tmp folder within a container—and
+also for the tmp/pids folder of our application. This way, we ensure that no
+server.pid survives a container exit (say goodbye to any “A server is already
+running” errors):
+
+tmpfs:
+ - /tmp
+ - /app/tmp/pids
+
+x-backend
+
+Alright, so now, we’ve finally reached the most interesting part of this post.
+
+This service defines the shared behavior of all Ruby services.
+
+Let’s talk about the volumes first:
+
+x-backend: &backend
+ <<: *app
+ stdin_open: true
+ tty: true
+ volumes:
+ - ..:/app:cached
+ - rails_cache:/app/tmp/cache
+ - bundle:/usr/local/bundle
+ - history:/usr/local/hist
+ - node_modules:/app/node_modules
+ - packs:/app/public/packs
+ - packs-test:/app/public/packs-test
+ - ./.psqlrc:/root/.psqlrc:ro
+ - ./.bashrc:/root/.bashrc:ro
+ - ./.pryrc:/root/.pryrc:ro
+ environment: &backend_environment
+ <<: *env
+ REDIS_URL: redis://redis:6379/
+ DATABASE_URL: postgres://postgres:postgres@postgres:5432
+ WEBPACKER_DEV_SERVER_HOST: webpacker
+ MALLOC_ARENA_MAX: 2
+ WEB_CONCURRENCY: ${WEB_CONCURRENCY:-1}
+ BOOTSNAP_CACHE_DIR: /usr/local/bundle/_bootsnap
+ XDG_DATA_HOME: /app/tmp/caches
+ YARN_CACHE_FOLDER: /app/node_modules/.yarn-cache
+ HISTFILE: /usr/local/hist/.bash_history
+ PSQL_HISTFILE: /usr/local/hist/.psql_history
+ IRB_HISTFILE: /usr/local/hist/.irb_history
+ EDITOR: vi
+ depends_on: &backend_depends_on
+ postgres:
+ condition: service_healthy
+ redis:
+ condition: service_healthy
+volumes:
+ - ..:/app:cached
+ - bundle:/usr/local/bundle
+ - rails_cache:/app/tmp/cache
+ - node_modules:/app/node_modules
+ - packs:/app/public/packs
+ - packs-test:/app/public/packs-test
+ - history:/usr/local/hist
+ - ./.psqlrc:/root/.psqlrc:ro
+ - ./.bashrc:/root/.bashrc:ro
+
+The Docker team is striving to make Docker work faster on MacOS. The latest
+releases (since [75]4.6.0) come with VirtioFS accelerated directory sharing and
+virtualization.framework support. Go check the “Experimental Features” tab in
+the Docker Desktop Preferences. You might find the resulting performance
+improvement to be pretty amazing: ([76]regular actions become ~2x faster)!
+
+The first item in the volumes list mounts the project directory to the /app
+folder within a container using the cached strategy. This cached modifier was
+the key to efficient Docker development on macOS.
+
+Wait, was?
+
+Yeah. Was. That’s because since the release of gRPC FUSE synchronization, it’s
+[77]no longer needed. Still, I decided to keep it for a while, for two reasons:
+first, some of your teammates may still be using older Docker desktop versions,
+and second, I ran some benchmarks and found that using older osxfs file sharing
+could have better performance (but only when using :cached). So, even on modern
+versions of Docker, it could make sense to uncheck the “Use gRPC FUSE for file
+sharing” option inside the preferences menu.
+
+The next line tells our container to use a volume named bundle to store the
+contents of /usr/local/bundle (this is where gems are stored [78]by default).
+By doing this, we persist our gem data across runs: all the volumes defined in
+compose.yml will stay put until we run compose down --volumes.
+
+The following lines have also been dutifully placed in order to nullify the
+“Docker is slow on Mac” curse. We put all the generated files into Docker
+volumes to avoid any heavy disk operations on the host machine:
+
+- rails_cache:/app/tmp/cache
+- node_modules:/app/node_modules
+- packs:/app/public/packs
+- packs-test:/app/public/packs-test
+
+ To give Docker a suitably fast speed on macOS, follow these two rules: use
+ :cached to mount source files (if not using gRPC FUSE), and use volumes for
+ generated content (assets, bundle, etc.).
+
+NOTE: If you’re using Sprockets (or Propshaft), don’t forget to add a dedicated
+volume to store the assets (assets:/app/public/assets). For tailwindcss-rails,
+add something like assets_builds:/app/assets/builds.
+
+We’ll then mount different command line tools configuration files and a volume
+to persist their history:
+
+- history:/usr/local/hist
+- ./.psqlrc:/root/.psqlrc:ro
+- ./.bashrc:/root/.bashrc:ro
+
+Oh, and why is psql in the Ruby container? That’s because it’s used internally
+when you run rails dbconsole.
+
+Pressing onward, our [79].psqlrc file contains the following trick which makes
+it possible to specify the path to the history file via the env variable—thus
+allowing us to specify the path to the history file via the PSQL_HISTFILE env
+variable, or otherwise, fall back to the default $HOME/.psql_history:
+
+\set HISTFILE `[[ -z $PSQL_HISTFILE ]] && echo $HOME/.psql_history || echo $PSQL_HISTFILE`
+
+The .bashrc file allows us to add terminal customizations within a container:
+
+alias be="bundle exec"
+
+Alright, let’s talk about the environment variables:
+
+environment: &backend_environment
+ <<: *env
+ # ----
+ # Service discovery
+ # ----
+ REDIS_URL: redis://redis:6379/
+ DATABASE_URL: postgres://postgres:postgres@postgres:5432
+ WEBPACKER_DEV_SERVER_HOST: webpacker
+ # ----
+ # Application configuration
+ # ----
+ MALLOC_ARENA_MAX: 2
+ WEB_CONCURRENCY: ${WEB_CONCURRENCY:-1}
+ # -----
+ # Caches
+ # -----
+ BOOTSNAP_CACHE_DIR: /usr/local/bundle/_bootsnap
+ # This env variable is used by some tools (e.g., RuboCop) to store caches
+ XDG_DATA_HOME: /app/tmp/cache
+ # Puts the Yarn cache into a mounted volume for speed
+ YARN_CACHE_FOLDER: /app/node_modules/.yarn-cache
+ # ----
+ # Dev tools
+ # ----
+ HISTFILE: /usr/local/hist/.bash_history
+ PSQL_HISTFILE: /usr/local/hist/.psql_history
+ IRB_HISTFILE: /usr/local/hist/.irb_history
+ EDITOR: vi
+
+First of all, we “inherit” variables from the common environment variables (<<:
+*env).
+
+The first group of variables (DATABASE_URL, REDIS_URL, and
+WEBPACKER_DEV_SERVER_HOST) connect our Ruby application to other services.
+
+The DATABASE_URL and WEBPACKER_DEV_SERVER_HOST variables are supported by Rails
+(ActiveRecord and Webpacker respectively) out of the box. Some libraries
+(Sidekiq) also support REDIS_URL, but not all of them: for instance, Action
+Cable must be explicitly configured.
+
+The second group contains some application-wide settings. For example, we
+define MALLOC_ARENA_MAX and WEB_CONCURRENCY to help us keep Ruby memory
+handling in check.
+
+Read more about Ruby memory spells and techniques:
+
+[80]Cables vs. malloc_trim, or yet another Ruby memory usage benchmark
+
+Cables vs. malloc_trim, or yet another Ruby memory usage benchmark
+
+March 19, 2019
+Read also
+
+Also, we have the variables responsible for storing caches in Docker volumes (
+BOOTSNAP_CACHE_DIR, XDG_DATA_HOME, YARN_CACHE_FOLDER).
+
+We use [81]bootsnap to speed up application load time. We store its cache in
+the same volume as the Bundler data. This is because this cache mostly contains
+the gem data, and we want to make sure the cache is reset every time we drop
+the Bundler volume (for instance, during a Ruby version upgrade).
+
+The final group of variables aim to improve the developer experience. HISTFILE:
+/usr/local/hist/.bash_history is the most significant here: it tells Bash to
+store its history in the specified location, thus making it persistent. The
+same goes for PSQL_HISTFILE and IRB_HISTFILE.
+
+NOTE: You need to configure IRB to store history in the specified location. To
+do that, drop these lines into your .irbrc file:
+
+IRB.conf[:HISTORY_FILE] = ENV["IRB_HISTFILE"] if ENV["IRB_HISTFILE"]
+
+Finally, EDITOR: vi is used, for example, by the rails credentials:edit command
+to manage credentials files.
+
+And with that, the only lines in this service we’ve yet to cover are:
+
+stdin_open: true
+tty: true
+
+These lines make this service interactive, that is, they provide a TTY. We need
+this, for example, to run the Rails console or Bash within a container.
+
+This is the same as running a Docker container with the -it option.
+
+rails
+
+The rails server is our default backend service. The only thing it overrides is
+the command to execute:
+
+rails:
+ <<: *backend
+ command: bundle exec rails
+
+This service is meant for executing all the commands needed in development (
+rails db:migrate, rspec, etc.).
+
+web
+
+The web service is meant for launching a web server. It defines the exposed
+ports and the required dependencies to run the app itself.
+
+webpacker
+
+The only thing I want to mention here is the WEBPACKER_DEV_SERVER_HOST: 0.0.0.0
+setting: it makes the Webpack dev server accessible from the outside (it runs
+on localhost by default).
+
+Health checks
+
+When running common Rails commands such as db:migrate, we want to ensure that
+the DB is up and ready to accept connections. How can we tell Docker Compose to
+wait until a dependent service is ready? We can use [82]health checks!
+
+You’ve probably noticed that our depends_on definition isn’t just a list of
+services:
+
+backend:
+ # ...
+ depends_on:
+ postgres:
+ condition: service_healthy
+ redis:
+ condition: service_healthy
+
+postgres:
+ # ...
+ healthcheck:
+ test: pg_isready -U postgres -h 127.0.0.1
+ interval: 5s
+
+redis:
+ # ...
+ healthcheck:
+ test: redis-cli ping
+ interval: 1s
+ timeout: 3s
+ retries: 30
+
+Introducing Dip
+
+If you still think that Docker Compose way is too complicated, there’s a tool
+called [83]Dip (developed by one of my colleages at Evil Martians) which aims
+to make the developer experience even smoother.
+
+[84]Reusable development containers with Docker Compose and Dip
+
+Reusable development containers with Docker Compose and Dip
+
+November 17, 2020
+Read also
+
+[85]Dip is a thin wrapper over docker compose, which provides a switch from
+infrastructure-oriented flow to development-oriented one. The key benefits of
+using Dip are as follows:
+
+ • The ability to define application-specific interactive commands and
+ sub-commands.
+ • The dip provision flow to quickly set up a development environment from
+ scratch.
+ • Support for multiple compose.yml files (including OS-specific
+ configurations).
+
+With Dip in place, to start working on the app locally, you just need to
+execute a few commands:
+
+# Builds a Docker image if none, runs additional commands
+$ dip provision
+# Runs a Rails server with the defined dependencies
+$ dip rails s
+=> Booting Puma
+=> Rails 7.0.2.2 application starting in development
+=> Run `bin/rails server --help` for more startup options
+[1] Puma starting in cluster mode...
+...
+[1] - Worker 0 (PID: 9) booted in 0.0s, phase: 0
+
+Here is our typical [86]dip.yml file:
+
+version: '7.1'
+
+# Define default environment variables to pass
+# to Docker Compose
+environment:
+ RAILS_ENV: development
+
+compose:
+ files:
+ - .dockerdev/compose.yml
+ project_name: example_demo
+
+interaction:
+ # This command spins up a Rails container with the required dependencies (such as databases),
+ # and opens a terminal within it.
+ runner:
+ description: Open a Bash shell within a Rails container (with dependencies up)
+ service: rails
+ command: /bin/bash
+
+ # Run a Rails container without any dependent services (useful for non-Rails scripts)
+ bash:
+ description: Run an arbitrary script within a container (or open a shell without deps)
+ service: rails
+ command: /bin/bash
+ compose_run_options: [ no-deps ]
+
+ # A shortcut to run Bundler commands
+ bundle:
+ description: Run Bundler commands
+ service: rails
+ command: bundle
+ compose_run_options: [ no-deps ]
+
+ # A shortcut to run RSpec (which overrides the RAILS_ENV)
+ rspec:
+ description: Run RSpec commands
+ service: rails
+ environment:
+ RAILS_ENV: test
+ command: bundle exec rspec
+
+ rails:
+ description: Run Rails commands
+ service: rails
+ command: bundle exec rails
+ subcommands:
+ s:
+ description: Run Rails server at http://localhost:3000
+ service: web
+ compose:
+ run_options: [service-ports, use-aliases]
+
+ yarn:
+ description: Run Yarn commands
+ service: rails
+ command: yarn
+ compose_run_options: [ no-deps ]
+
+ psql:
+ description: Run Postgres psql console
+ service: postgres
+ default_args: anycasts_dev
+ command: psql -h postgres -U postgres
+
+ 'redis-cli':
+ description: Run Redis console
+ service: redis
+ command: redis-cli -h redis
+
+provision:
+ - dip compose down --volumes
+ - dip compose up -d postgres redis
+ - dip bash -c bin/setup
+
+Let me explain some bits of this in further detail.
+
+First, the compose section:
+
+compose:
+ files:
+ - .dockerdev/compose.yml
+ project_name: example_demo
+
+Here we should specify the path to our Compose configuration (.dockerdev/
+compose.yml). Accordingly, we can run dip from the project root, and the
+correct configuration will be picked up.
+
+The project_name is important: if we don’t specify it, the folder containing
+the compose.yml file would be used (“dockerdev”), which could lead to
+collisions between different projects.
+
+The rails command is also worth some additional attention:
+
+rails:
+ description: Run Rails commands
+ service: rails
+ command: bundle exec rails
+ subcommands:
+ s:
+ description: Run Rails server at http://localhost:3000
+ service: web
+ compose:
+ run_options: [service-ports, use-aliases]
+
+By default, the dip rails command would call bundle exec rails within a Rails
+container. However, we use the subcommand feature of Dip here to treat dip
+rails s differently:
+
+ • We use the web service, not rails (so, the deps are up).
+ • We expose the service ports (3000 in our case).
+ • We also enable network aliases, so other services can access this container
+ via the web hostname.
+
+Under the hood, this will result in the following Docker Compose command:
+
+docker compose run --rm --service-ports --use-aliases web
+
+Note that it uses run, and not up. This difference makes our server
+terminal-accessible. For example, this means that we can attach a debugger and
+use it without any problems (with the up command the terminal is
+non-interactive).
+
+Interactive provisioning
+
+To learn how to keep configuration under control, check out this “Terraforming
+Rails” series:
+
+[87]Anyway Config: Keep your Ruby configuration sane
+
+Anyway Config: Keep your Ruby configuration sane
+
+April 14, 2020
+[svg]
+Cover for Anyway Config: Keep your Ruby configuration sane
+Read also
+
+For most applications, building an image and setting up a database is not
+enough to start developing: beyond this, some kind of secrets, or credentials,
+or .env files are required. Here, we’ve managed to use Dip to help new
+engineers quickly assemble all these wayfallen parts by providing an
+interactive provision experience.
+
+Let’s consider, for example, that we need to put a .env.development.local file
+with some secret info and also configure RubyGems to download packages from a
+private registry (say, Sidekiq Pro). For this, I’ll write the following
+provision script:
+
+# The command is extracted, so we can use it alone
+configure_bundler:
+ command: |
+ (test -f .bundle/config && cat .bundle/config | \
+ grep BUNDLE_ENTERPRISE__CONTRIBSYS__COM > /dev/null) ||
+ \
+ (echo "Sidekiq ent credentials: "; read -r creds; dip bundle config --local enterprise.contribsys.com $creds)
+
+provision:
+ - (test -f .env.development.local) || (echo "\n\n ⚠️ .env.development.local file is missing\n\n"; exit 1)
+ - dip compose down --volumes
+ - dip configure_bundler
+ - (test -f config/database.yml) || (cp .dockerdev/database.yml.example config/database.yml)
+ - dip compose up -d postgres redis
+ - dip bash -c bin/setup
+
+Below you can see a demonstration of this command running in action:
+
+An interactive Dip provisioning example
+
+Services vs Docker for development
+
+You can use a good ‘ol [88]Makefile to do the same, for sure. However, we’ve
+found that using a dedicated tool (like Dip) to define everything in a
+declarative manner is more efficient.
+
+One more use case for standardizing the development setup is to make it
+possible to run multiple independent services locally. Let me quickly
+demonstrate how we do this with Dip. First, you need to dockerize each
+application (following this post). After that, we need to connect the apps to
+each other. How can we do this? With the help of Docker Compose [89]external
+networks.
+
+We add the following line to the dip.yml for each app:
+
+# ...
+provision:
+ # Make sure the named network exists
+ - docker network inspect my_project > /dev/null 2>&1 || \
+ docker network create my_project
+# ...
+
+Finally, we attach services to this network via aliases in the compose.yml
+files:
+
+# service A: compose.yml
+service:
+ ruby:
+ # ...
+ networks:
+ default:
+ project:
+ aliases:
+ - project-a
+
+networks:
+ project:
+ external:
+ name: my_project
+
+# service B: compose.yml
+service:
+ web:
+ # ...
+ environment:
+ # We can access the service A via its alias defined for the external network
+ SERVICE_URL: http://project-a:3000
+
+networks:
+ project:
+ external:
+ name: my_project
+
+From development to production
+
+So, here’s one of the most popular questions we’ve been asked since launching
+the first version of this article: how to go live with Docker? To answer this,
+we’d need to write a entirely new article… and we will 😉.
+
+For now, let me give a sneak preview of how can we extend the current
+development setup to cover the production environment as well.
+
+First of all, we’re not going to talk about a Docker Compose-based deployment,
+so compose.yml is out. All we need is to update our Docker image to reflect the
+difference between development and production:
+
+ 1. For security reasons, we should execute the code on behalf of the regular,
+ non-root user.
+ 2. We should keep all the required dependencies and artifacts within the image
+ itself; we cannot use volumes (the image should be self-contained).
+ 3. We should keep and copy the source code into a container.
+ 4. The resulting image should be as slim as possible.
+
+To achieve this, we’ll refactor our existing Dockerfile to define multiple
+stages (and to support [90]multi-stage builds). Below is the annotated example:
+
+# syntax=docker/dockerfile:1
+
+ARG RUBY_VERSION
+ARG DISTRO_NAME=bullseye
+
+# Here we add the the name of the stage ("base")
+FROM ruby:$RUBY_VERSION-slim-$DISTRO_NAME AS base
+
+ARG PG_MAJOR
+ARG NODE_MAJOR
+ARG YARN_VERSION
+
+# Common dependencies
+# ...
+# The following lines are exactly the same as before
+# ...
+# ...
+WORKDIR /app
+
+EXPOSE 3000
+CMD ["/bin/bash"]
+
+# Then, we define the "development" stage from the base one
+FROM base AS development
+
+ENV RAILS_ENV=development
+
+# The major difference from the base image is that we may have development-only system
+# dependencies (like Vim or graphviz).
+# We extract them into the Aptfile.dev file.
+COPY Aptfile.dev /tmp/Aptfile.dev
+RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
+ --mount=type=cache,target=/var/lib/apt,sharing=locked \
+ --mount=type=tmpfs,target=/var/log \
+ apt-get update -qq && DEBIAN_FRONTEND=noninteractive apt-get -yq dist-upgrade && \
+ DEBIAN_FRONTEND=noninteractive apt-get install -yq --no-install-recommends \
+ $(grep -Ev '^\s*#' /tmp/Aptfile.dev | xargs)
+
+# The production-builder image is responsible for installing dependencies and compiling assets
+FROM base as production-builder
+
+# First, we create and configure a dedicated user to run our application
+RUN groupadd --gid 1005 my_user \
+ && useradd --uid 1005 --gid my_user --shell /bin/bash --create-home my_user
+USER my_user
+RUN mkdir /home/my_user/app
+WORKDIR /home/my_user/app
+
+# Then, we re-configure Bundler
+ENV RAILS_ENV=production \
+ LANG=C.UTF-8 \
+ BUNDLE_JOBS=4 \
+ BUNDLE_RETRY=3 \
+ BUNDLE_APP_CONFIG=/home/my_user/bundle \
+ BUNDLE_PATH=/home/my_user/bundle \
+ GEM_HOME=/home/my_user/bundle
+
+# Install Ruby gems
+COPY --chown=my_user:my_user Gemfile Gemfile.lock ./
+RUN mkdir $BUNDLE_PATH \
+ && bundle config --local deployment 'true' \
+ && bundle config --local path "${BUNDLE_PATH}" \
+ && bundle config --local without 'development test' \
+ && bundle config --local clean 'true' \
+ && bundle config --local no-cache 'true' \
+ && bundle install --jobs=${BUNDLE_JOBS} \
+ && rm -rf $BUNDLE_PATH/ruby/${RUBY_VERSION}/cache/* \
+ && rm -rf /home/my_user/.bundle/cache/*
+
+# Install JS packages
+COPY --chown=my_user:my_user package.json yarn.lock ./
+RUN yarn install --check-files
+
+# Copy code
+COPY --chown=my_user:my_user . .
+
+# Precompile assets
+# NOTE: The command may require adding some environment variables (e.g., SECRET_KEY_BASE) if you're not using
+# credentials.
+RUN bundle exec rails assets:precompile
+
+# Finally, our production image definition
+# NOTE: It's not extending the base image, it's a new one
+FROM ruby:$RUBY_VERSION-slim-$DISTRO_NAME AS production
+
+# Production-only dependencies
+RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
+ --mount=type=cache,target=/var/lib/apt,sharing=locked \
+ --mount=type=tmpfs,target=/var/log \
+ apt-get update -qq \
+ && apt-get dist-upgrade -y \
+ && DEBIAN_FRONTEND=noninteractive apt-get install -yq --no-install-recommends \
+ curl \
+ gnupg2 \
+ less \
+ tzdata \
+ time \
+ locales \
+ && update-locale LANG=C.UTF-8 LC_ALL=C.UTF-8
+
+# Upgrade RubyGems and install the latest Bundler version
+RUN gem update --system && \
+ gem install bundler
+
+# Create and configure a dedicated user (use the same name as for the production-builder image)
+RUN groupadd --gid 1005 my_user \
+ && useradd --uid 1005 --gid my_user --shell /bin/bash --create-home my_user
+RUN mkdir /home/my_user/app
+WORKDIR /home/my_user/app
+USER my_user
+
+# Ruby/Rails env configuration
+ENV RAILS_ENV=production \
+ BUNDLE_APP_CONFIG=/home/my_user/bundle \
+ BUNDLE_PATH=/home/my_user/bundle \
+ GEM_HOME=/home/my_user/bundle \
+ PATH="/home/my_user/app/bin:${PATH}" \
+ LANG=C.UTF-8 \
+ LC_ALL=C.UTF-8
+
+EXPOSE 3000
+
+# Copy code
+COPY --chown=my_user:my_user . .
+
+# Copy artifacts
+# 1) Installed gems
+COPY --from=production-builder $BUNDLE_PATH $BUNDLE_PATH
+# 2) Compiled assets (by Webpacker in this case)
+COPY --from=production-builder /home/my_user/app/public/packs /home/my_user/app/public/packs
+# 3) We can even copy the Bootsnap cache to speed up our Rails server load!
+COPY --chown=my_user:my_user --from=production-builder /home/my_user/app/tmp/cache/bootsnap* /home/my_user/app/tmp/cache/
+
+CMD ["bundle", "exec", "rails", "server", "-b", "0.0.0.0"]
+
+Introducing the Ruby on Whales interactive generator
+
+As a bonus, our Ruby on Whales [91]repository ships with a Rails template
+(published on [92]Rails Bytes), which can help you quickly adopt Docker for
+development by running a single command (and answering a few questions).
+
+Without further ado, check out the demonstartion below:
+
+An interactive Ruby on Whales installer
+
+You can give it a try by running a single command:
+
+rails app:template LOCATION='https://railsbytes.com/script/z5OsoB'
+
+Radio wave representing wind sounds on Mars
+
+Acknoledgements
+
+I would like to thank:
+
+ • [93]Sergey Ponomarev for sharing performance tips and helping battle-test
+ the initial dockerization attempts.
+ • [94]Mikhail Merkushin for his work on Dip.
+ • [95]Dmitriy Nemykin for helping with the major (v2) upgrade.
+ • [96]Oliver Klee ([97]Brain Gourmets) for continuous PRs with the
+ configuration improvements and actualization.
+
+Radio wave representing wind sounds on Mars
+
+Changelog
+
+2.0.3 (2023-09-21)
+
+ • Upgrade Node.js installation script.
+
+2.0.2 (2022-11-30)
+
+ • Use RUN --mount for caching packages between builds instead of manual
+ cleanup.
+
+2.0.1 (2022-03-22)
+
+ • Replace deprecated apt-key with gpg.
+
+2.0.0 (2022-03-02)
+
+ • Major upgrade and new chapters.
+
+1.1.4 (2021-10-12)
+
+ • Added tmp/pids to tmpfs (to deal with “A server is already running”
+ errors).
+
+1.1.3 (2021-03-30)
+
+ • Updated Dockerfile to mitigate MiniMagic licensing issues. See [98]
+ terraforming-rails#35
+ • Use dictionary to organize environment variables. See [99]
+ terraforming-rails#6
+
+1.1.2 (2021-02-26)
+
+ • Update dependencies versions. See [100]terraforming-rails#28
+ • Allow to use comments in Aptfile. See [101]terraforming-rails#31
+ • Fix path to Aptfile inside Dockerfile. See [102]terraforming-rails#33
+
+1.1.1 (2020-09-15)
+
+ • Use .dockerdev directory as build context instead of project directory. See
+ [103]terraforming-rails#26 for details.
+
+1.1.0 (2019-12-10)
+
+ • Change base Ruby image to slim.
+ • Specify Debian release for Ruby version explicitly and upgrade to buster.
+ • Use standard Bundler path (/usr/local/bundle) instead of /bundle.
+ • Use Docker Compose file format v2.4.
+ • Add health checking to postgres and redis services.
+
+Join our email newsletter
+
+Get all the new posts delivered directly to your inbox. Unsubscribe anytime.
+
+[104][ ]Your email[105][ ]
+Subscribe
+Or [107]subscribe to a feed
+━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+[108][email protected]
+
+United States
+
+[109]+1 888 400 5485
+
+Portugal
+
+[110]+351 308 808 570
+
+Japan
+
+[111]+81 6 6225 1242
+
+ • [112]Contact us
+ • [113]Careers
+ • [114]日本語版
+
+[115]Privacy policy
+[116]Cookie & privacy preferences
+[117]Notice at collection
+
+Designed and developed by Evil Martians
+
+
+References:
+
+[1] https://evilmartians.com/
+[2] https://cal.com/team/evilmartians/exploration
+[3] https://evilmartians.com/services
+[4] https://evilmartians.com/clients
+[5] https://evilmartians.com/products
+[6] https://evilmartians.com/opensource
+[7] https://evilmartians.com/chronicles
+[8] https://evilmartians.com/events
+[9] https://evilmartians.com/devpropulsionlabs
+[10] https://x.com/evilmartians
+[11] https://www.linkedin.com/company/evil-martians
+[12] https://github.com/evilmartians
+[13] https://www.youtube.com/@evil.martians
+[14] https://evilmartians.com/
+[15] https://cal.com/team/evilmartians/exploration
+[16] https://evilmartians.com/services
+[17] https://evilmartians.com/clients
+[18] https://evilmartians.com/products
+[19] https://evilmartians.com/opensource
+[20] https://evilmartians.com/chronicles
+[21] https://evilmartians.com/events
+[22] https://evilmartians.com/devpropulsionlabs
+[23] https://x.com/evilmartians
+[24] https://www.linkedin.com/company/evil-martians
+[25] https://github.com/evilmartians
+[26] https://www.youtube.com/@evil.martians
+[27] https://evilmartians.com/events/evolution-of-real-time-and-anycable-rocky-mountain
+[28] https://cal.com/team/evilmartians/exploration
+[29] https://x.com/evilmartians
+[30] https://www.linkedin.com/company/evil-martians
+[31] https://github.com/evilmartians
+[32] https://www.youtube.com/@evil.martians
+[33] https://evilmartians.com/chronicles?categories=backend
+[34] https://evilmartians.com/chronicles?services=software-development
+[35] https://evilmartians.com/chronicles?services=audit-and-optimization
+[36] https://evilmartians.com/chronicles?skills=rubyonrails
+[37] https://evilmartians.com/chronicles?skills=ruby
+[38] https://evilmartians.com/chronicles?skills=docker
+[39] https://evilmartians.com/chronicles?skills=postgresql
+[40] https://evilmartians.com/chronicles?skills=nodejs
+[41] https://techracho.bpsinc.jp/hachi8833/2022_04_07/116843
+[42] https://xfyuan.github.io/2020/07/dockeerizing-rails-development/
+[43] https://evilmartians.com/chronicles/ruby-on-whales-docker-for-ruby-rails-development#changelog
+[44] https://noti.st/palkan/vhsbxO/terraforming-legacy-rails-applications
+[45] https://github.com/evilmartians/ruby-on-whales
+[46] https://evilmartians.com/chronicles/ruby-on-whales-docker-for-ruby-rails-development#basic-docker-configuration
+[47] https://evilmartians.com/chronicles/ruby-on-whales-docker-for-ruby-rails-development#introducing-dip
+[48] https://evilmartians.com/chronicles/ruby-on-whales-docker-for-ruby-rails-development#services-vs-docker-for-development
+[49] https://evilmartians.com/chronicles/ruby-on-whales-docker-for-ruby-rails-development#from-development-to-production
+[50] https://evilmartians.com/chronicles/ruby-on-whales-docker-for-ruby-rails-development#introducing-the-ruby-on-whales-interactive-generator
+[51] https://github.com/evilmartians/ruby-on-whales/blob/main/example/.dockerdev/Dockerfile
+[52] https://github.com/moby/moby/issues/34129
+[53] https://www.postgresql.org/download/linux/debian/
+[54] https://docs.docker.com/compose/
+[55] https://github.com/nodesource/distributions/blob/master/README.md#debinstall
+[56] https://world.hey.com/dhh/modern-web-apps-without-javascript-bundling-or-transpiling-a20f2755
+[57] https://github.com/WICG/import-maps
+[58] https://github.com/rails/tailwindcss-rails
+[59] https://github.com/heroku/heroku-buildpack-apt
+[60] https://github.com/evilmartians/terraforming-rails/blob/master/examples/dockerdev/.dockerdev/Aptfile
+[61] https://www.tug.org/texlive/
+[62] https://askubuntu.com/a/972528
+[63] http://xubuntugeek.blogspot.com/2012/06/save-disk-space-with-apt-get-option-no.html
+[64] https://docs.docker.com/storage/storagedriver/#images-and-layers
+[65] https://github.com/moby/buildkit/blob/master/frontend/dockerfile/docs/reference.md#run---mount
+[66] https://github.com/docker-library/ruby/issues/129#issue-229195231
+[67] https://github.com/evilmartians/terraforming-rails/pull/24
+[68] https://github.com/rubygems/rubygems/pull/4076
+[69] https://github.com/evilmartians/ruby-on-whales/blob/main/example/.dockerdev/compose.yml
+[70] https://docs.docker.com/compose/
+[71] https://github.com/compose-spec/compose-spec/blob/master/spec.md#extension
+[72] https://evilmartians.com/chronicles/ruby-on-whales-docker-for-ruby-rails-development#introducing-dip
+[73] https://docs.docker.com/compose/compose-file/#context
+[74] https://docs.docker.com/v17.09/engine/admin/volumes/tmpfs/#choosing-the-tmpfs-or-mount-flag
+[75] https://www.docker.com/blog/speed-boost-achievement-unlocked-on-docker-desktop-4-6-for-mac/
+[76] https://twitter.com/palkan_tula/status/1504499523216945167
+[77] https://github.com/docker/for-mac/issues/5402
+[78] https://github.com/infosiftr/ruby/blob/9b1f77c11d663930f4175c683b1c5f268d4d8191/Dockerfile.template#L47
+[79] https://github.com/evilmartians/ruby-on-whales/blob/main/example/.dockerdev/.psqlrc
+[80] https://evilmartians.com/chronicles/cables-vs-malloc_trim-or-yet-another-ruby-memory-usage-benchmark
+[81] https://www.github.com/Shopify/bootsnap
+[82] https://docs.docker.com/compose/compose-file/compose-file-v3/#healthcheck
+[83] https://evilmartians.com/opensource/dip
+[84] https://evilmartians.com/chronicles/reusable-development-containers-with-docker-compose-and-dip
+[85] https://evilmartians.com/opensource/dip
+[86] https://github.com/evilmartians/ruby-on-whales/blob/main/example/dip.yml
+[87] https://evilmartians.com/chronicles/anyway-config-keep-your-ruby-configuration-sane
+[88] https://makefile.site/
+[89] https://docs.docker.com/compose/networking/#use-a-pre-existing-network
+[90] https://docs.docker.com/develop/develop-images/multistage-build/
+[91] https://github.com/evilmartians/ruby-on-whales
+[92] https://railsbytes.com/public/templates/z5OsoB
+[93] https://github.com/sponomarev
+[94] https://github.com/bibendi
+[95] https://github.com/fargelus/
+[96] https://github.com/oliverklee
+[97] https://www.braingourmets.com/
+[98] https://github.com/evilmartians/terraforming-rails/pull/35
+[99] https://github.com/evilmartians/terraforming-rails/pull/6
+[100] https://github.com/evilmartians/terraforming-rails/pull/28
+[101] https://github.com/evilmartians/terraforming-rails/pull/31
+[102] https://github.com/evilmartians/terraforming-rails/issues/33
+[103] https://github.com/evilmartians/terraforming-rails/issues/26
+[107] https://evilmartians.com/chronicles.atom
+[108] mailto:[email protected]
+[109] tel:+18884005485
+[110] tel:+351308808570
+[111] tel:+81662251242
+[112] https://evilmartians.com/contact-us
+[113] https://wellfound.com/company/evilmartians
+[114] https://evilmartians.jp/
+[115] https://evilmartians.com/privacy
+[116] https://evilmartians.com/cookies
+[117] https://evilmartians.com/privacy#notice_at_collection
diff --git a/static/archive/www-viget-com-1hyo6b.txt b/static/archive/www-viget-com-1hyo6b.txt
@@ -0,0 +1,459 @@
+[1] Skip to Main Content
+[2] Viget
+
+ • [3] Work
+ • [4] Services
+ • [5] Articles
+ • [6] Careers
+ • [7] Contact
+ • Open Menu
+
+Navigation
+
+[9] Viget Close
+
+ • Practice
+ • [11] Work
+ • [12] Services
+ • [13] Articles
+
+We’re a full-service digital agency that’s been helping clients make lasting
+change since 1999.
+
+[14] Contact Us
+
+People
+
+ • [15]Company
+ • [16]Careers
+ • [17]Code of Ethics
+ • [18]Diversity & Inclusion
+
+More
+
+ • [19]Pointless Corp.
+ • [20]Explorations
+ • [21]Code at Viget
+
+Featured
+
+[22]
+Read the Article: Building the Future, One Intern at a Time
+
+Newsletter
+
+Building the Future, One Intern at a Time
+
+[23]
+Read the Article: Some Thoughts after a Major Ruby on Rails Upgrade
+
+Article
+
+Some Thoughts after a Major Ruby on Rails Upgrade
+
+Docker + Rails: Solutions to Common Hurdles
+
+[eyJidWNrZXQiOiJ2Z3QtdmlnZXRjb20tYW]
+
+ • [24]Home
+ • [25]Articles
+ • [26]Docker + Rails: Solutions to Common Hurdles
+
+[27] Subscribe (opens in a new window)
+Share
+
+ • [29] Share this page
+ • [30] Share this page
+ • [31] Post this page
+
+Eli Fatsi, Former Development Director
+
+Article Categories: [32] #Code, [33] #Back-end Engineering
+
+Posted on March 30, 2021
+
+ • [34]
+ Share
+ • [35]
+ Share
+ • [36]
+ Post
+
+Tips n Tricks for working with Rails applications in Docker
+
+T i p s n T r i c k s f o r w o r k i n g w i t h R a i l s a p p l i c a t i o
+n s i n D o c k e r
+
+This is the follow on post to an earlier post: [37]Docker: Right for Us. Right
+for You?
+
+Docker has made a number of large problems go away, and replaced them with
+(usually) (hopefully) a smaller set of its own problems. Sometimes it's not
+worth it to make that sacrifice, but it often is for us, and here are the
+tricks we've picked up for working with Rails sites and Docker Compose:
+
+Rails Tips & Tricks [38]#
+
+db host
+
+Trying to docker-ify a Rails app and running into this?
+
+could not connect to server: No such file or directory
+ Is the server running locally and accepting
+ connections on Unix domain socket "/var/run/postgresql/.s.PGSQL.5432"?
+
+Make sure you have a db service defined in docker-compose.yml, and add host: db
+to your database.yml file. All of the services you define in the compose file
+become network-addressable names to each other. So adding host: db tells Rails
+that the hostname of the database server is db, which should point to your
+database service.
+
+━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+
+binding.pry
+
+AKA the one tool I use the most by far I gotta have it. TLDR on the solution:
+[39]https://stackoverflow.com/questions/35211638/
+how-to-debug-a-rails-app-in-docker-with-pry/37264588#37264588
+
+The gist is that you need to add some Docker Compose flags to the service you
+want to run a pry inside, and then docker attach [running_container_name] after
+you've started the service container.
+
+━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+
+save_and_open_page
+
+If you're testing w/ Capybara and want to get a glimpse of your site while a
+feature test is running, then Docker is going to make that difficult for you.
+TLDR - [40]https://github.com/copiousfreetime/launchy/issues/108#
+issuecomment-319644326
+
+And the gist? You need to configure a volume so screenshots saved on the Docker
+container are accessible to the host machine. You then need to rig up guard
+locally to watch the volume directory and automatically open any HTML files
+that land there.
+
+━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+
+cacheing bundler and npm builds
+
+If you don't configure this properly, you could find yourself re-installing
+dependencies every time you spin up your containers. We lean on persisted
+volumes for this, and pick up the log and tmp directories while we're at it.
+like so:
+
+- services
+ - app:
+ entrypoint: ./.docker-config/entrypoint.sh
+ volumes:
+ - ./node_modules:/usr/src/app/node_modules
+ - gems:/usr/local/bundle
+ - log:/usr/src/app/log
+ - tmp:/usr/src/app/tmp
+
+I'll also point out that we prefer to move the dependency installation steps
+into an entrypoint script, instead of within the Dockerfile build definition.
+This means that we're rarely rebuilding our baseline image (unless something
+deeper level changes, like needing an additional runtime available), and our
+dependencies are kept up to date everytime we cycle the containers. Here's an
+example of an entrypoint script for an app with Ruby gems and NPM packages:
+
+#!/bin/bash
+
+set -e
+
+# Ensure PID is reset. This can happen if docker isn't cleanly shut down.
+rm -rf /usr/src/app/tmp/pids
+
+# Verify node_modules are up to date
+yarn install --silent
+
+# Verify gems are up to date
+if ! bundle check > /dev/null; then
+ echo "Gems dependencies are out of date. Installing..."
+ bundle install
+fi
+
+exec "$@"
+
+━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+
+asset performance
+
+Out of the box, sites that run perfectly quickly locally would take a lonnnnng
+time to respond to local asset requests. One solution we settled on was to add
+:delegated to the volume dedicated to syncing the codebase. For example:
+
+app:
+ image: ruby:3.0
+ command: rails server -p 3000 -b '0.0.0.0'
+ ports:
+ - 3000:3000
+ volumes:
+ # Ensure changes to the codebase are picked up by the docker container
+ # The `:delegated` here is a critical config for speedy development
+ - .:/usr/src/app:delegated
+
+ • The docs have a lot to say about this if you want to learn more: [41]http:/
+ /docs.docker.oeynet.com/docker-for-mac/osxfs-caching/
+ • And this StackOverflow answer has a good recap in case that docs link goes
+ down: [42]https://stackoverflow.com/a/63437557/1655658
+
+━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+
+Running bash
+
+This one took me a while to figure out as I was learning the difference between
+a running Docker container and a service and an image and a volume. Once I had
+a clearer picture of how Docker (and Docker Compose) worked, opening up a shell
+into a running thing stopped being tricky. Here's what I've picked up though,
+hopefully these words mean something to you:
+
+If you have a running container based off of a docker-compose service, you can
+bash into it like so:
+
+docker-compose exec [name-of-service] bash
+
+If you have a non-running container based off of a docker-compose service, you
+can bash into it like so:
+
+docker-compose run --rm [name-of-service] bash
+
+The difference here is exec vs run --rm. You can probably guess what each does.
+The --rm flag is important so that the newly spun up containers don't hang
+around forever after you close out of the session.
+
+━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+
+importing database dumps
+
+If you're running something like PostgreSQL within Docker, it becomes a bit
+trickier to psql your way in from a local command line. As is often the case
+with Docker, there are a few ways to get the job done here, and the one that's
+right for you is dependent on what you're trying to do.
+
+One path is to requiring you to expose a port and run commands through that
+port. This can be done in a docker-compose.yml:
+
+services:
+ db:
+ image: mdillon/postgis:11-alpine
+ ports:
+ # Expose Postgres to host networking (your computer) at port 5433.
+ # This prevents conflicts with Postgres installed on your machine
+ # while still allowing the database to be browsed at port 5432
+ - 5433:5432
+ volumes:
+ - db-data:/var/lib/postgresql/data
+
+Another option is to define a new volume within your database service which
+makes a database dump on your local machine accessible to the running Docker
+container. If you had a database dump stored within your project directory at .
+/local/db-backups, you could make it accessible within Docker like so:
+
+services:
+ db:
+ image: mdillon/postgis:11-alpine
+ volumes:
+ - db-data:/var/lib/postgresql/data
+ - ./local/db-backups:/app/db-backups
+
+Then, bash into the running container and have at it with all your usual psql
+moves.
+
+Note: I'm including the db-data volume definition to point out that we always
+have a persistent volume configured for database services so the data persists
+when the containers are wound down.
+
+━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+
+Using local SSH credentials on Docker container
+
+We have a lot of deploy processes that lean on SSH private keys for
+authorization. If you attempt to run one of those commands in a running Docker
+container, it'll fail due to it's sandboxed environment not being able to reach
+out into your local machine for the credentials. This is our Docker Compose
+solution for that:
+
+services:
+ app:
+ ...
+ volumes:
+ - type: bind
+ source: /run/host-services/ssh-auth.sock
+ target: /run/host-services/ssh-auth.sock
+
+The End [43]#
+
+That's about all I could recall now that it's been ~1.5 years since digging in
+on my own Docker journey. Hopefully something in there was useful to you. If
+you have another tip or trick that solved an early challenge, drop a line in
+the comments below!
+
+Related Articles
+
+ • [44]
+ Some Thoughts after a Major Ruby on Rails Upgrade
+
+ Article
+
+ Some Thoughts after a Major Ruby on Rails Upgrade
+
+ Noah Over
+
+ • [45]
+ Setting up a Python Project Using asdf, PDM, and Ruff
+
+ Article
+
+ Setting up a Python Project Using asdf, PDM, and Ruff
+
+ Danny Brown
+
+ • [46]
+ Craft 5: What It Means For Super Table Page Builders
+
+ Article
+
+ Craft 5: What It Means For Super Table Page Builders
+
+ Max Myers
+
+The Viget Newsletter
+
+Nobody likes popups, so we waited until now to recommend our newsletter,
+featuring thoughts, opinions, and tools for building a better digital world.
+[47]Read the current issue.
+
+[48]Subscribe Here (opens in new window)
+
+Site Footer
+
+Have an unsolvable problem or audacious idea?
+
+Let’s get to work
+
+[49] Contact Us [50] [email protected] [51] 703.891.0670
+
+ • Practice
+ • [52]Work
+ • [53]Services
+ • [54]Articles
+
+ • People
+ • [55]Company
+ • [56]Careers
+ • [57]Code of Ethics
+ • [58]Diversity & Inclusion
+
+ • More
+ • [59]Pointless Corp.
+ • [60]Explorations
+ • [61]Code at Viget
+
+Sign Up For Our Newsletter
+
+A curated periodical featuring thoughts, opinions, and tools for building a
+better digital world.
+
+[62] Check it out
+
+Social Links
+
+[63] Viget
+
+ • [64]
+ • [65]
+ • [66]
+ • [67]
+ • [68]
+ • [69]
+
+Viget rhymes with 'dig it'. Click here to hear how we say it.
+
+Office Locations
+
+ • [71]Washington, DC Metro
+ • [72]Durham, NC
+ • [73]Boulder, CO
+ • [74]Chattanooga, TN
+
+© 1999 – 2024 Viget Labs, LLC. [75]Terms [76]Privacy [77]MRF
+
+
+References:
+
+[1] https://www.viget.com/articles/docker-rails-solutions-to-common-hurdles/#content
+[2] https://www.viget.com/
+[3] https://www.viget.com/work/
+[4] https://www.viget.com/services/
+[5] https://www.viget.com/articles/
+[6] https://www.viget.com/careers/
+[7] https://www.viget.com/contact/
+[9] https://www.viget.com/
+[11] https://www.viget.com/work/
+[12] https://www.viget.com/services/
+[13] https://www.viget.com/articles/
+[14] https://www.viget.com/contact/
+[15] https://www.viget.com/about/
+[16] https://www.viget.com/careers/
+[17] https://www.viget.com/code-of-ethics/
+[18] https://www.viget.com/diversity-equity-and-inclusion/
+[19] https://pointlesscorp.com/
+[20] https://explorations.viget.com/
+[21] https://code.viget.com/
+[22] https://www.viget.com/newsletter/building-the-future-one-intern-at-a-time/
+[23] https://www.viget.com/articles/some-thoughts-after-a-major-ruby-on-rails-upgrade/
+[24] https://www.viget.com/
+[25] https://www.viget.com/articles
+[26] https://www.viget.com/articles/docker-rails-solutions-to-common-hurdles/#hero
+[27] http://eepurl.com/gtHqsj
+[29] https://www.facebook.com/sharer/sharer.php?u=https%3A%2F%2Fwww.viget.com%2Farticles%2Fdocker-rails-solutions-to-common-hurdles%2F
+[30] http://www.linkedin.com/shareArticle?mini=true&url=https%3A%2F%2Fwww.viget.com%2Farticles%2Fdocker-rails-solutions-to-common-hurdles%2F
+[31] https://x.com/intent/tweet?text=Tips%20n%20Tricks%20for%20working%20with%20Rails%20applications%20in%20Docker%20https%3A%2F%2Fwww.viget.com%2Farticles%2Fdocker-rails-solutions-to-common-hurdles%2F
+[32] https://www.viget.com/articles/?category=code#results
+[33] https://www.viget.com/articles/?category=back-end-engineering#results
+[34] https://www.facebook.com/sharer/sharer.php?u=https%3A%2F%2Fwww.viget.com%2Farticles%2Fdocker-rails-solutions-to-common-hurdles%2F
+[35] http://www.linkedin.com/shareArticle?mini=true&url=https%3A%2F%2Fwww.viget.com%2Farticles%2Fdocker-rails-solutions-to-common-hurdles%2F
+[36] https://x.com/intent/tweet?text=Tips%20n%20Tricks%20for%20working%20with%20Rails%20applications%20in%20Docker%20https%3A%2F%2Fwww.viget.com%2Farticles%2Fdocker-rails-solutions-to-common-hurdles%2F
+[37] https://www.viget.com/articles/docker-right-for-us-right-for-you-1
+[38] https://www.viget.com/articles/docker-rails-solutions-to-common-hurdles/#rails-tips-tricks
+[39] https://stackoverflow.com/questions/35211638/how-to-debug-a-rails-app-in-docker-with-pry/37264588#37264588
+[40] https://github.com/copiousfreetime/launchy/issues/108#issuecomment-319644326
+[41] http://docs.docker.oeynet.com/docker-for-mac/osxfs-caching/
+[42] https://stackoverflow.com/a/63437557/1655658
+[43] https://www.viget.com/articles/docker-rails-solutions-to-common-hurdles/#the-end
+[44] https://www.viget.com/articles/some-thoughts-after-a-major-ruby-on-rails-upgrade/
+[45] https://www.viget.com/articles/setting-up-a-python-project-using-asdf-pdm-and-ruff/
+[46] https://www.viget.com/articles/craft-5-what-it-means-for-super-table-page-builders/
+[47] https://www.viget.com/newsletter
+[48] http://eepurl.com/gtHqsj
+[49] https://www.viget.com/contact/
+[50] mailto:[email protected]?subject=Hello%2C%20Viget%21
+[51] tel:7038910670
+[52] https://www.viget.com/work/
+[53] https://www.viget.com/services/
+[54] https://www.viget.com/articles/
+[55] https://www.viget.com/about/
+[56] https://www.viget.com/careers/
+[57] https://www.viget.com/code-of-ethics/
+[58] https://www.viget.com/diversity-equity-and-inclusion/
+[59] https://pointlesscorp.com/
+[60] https://explorations.viget.com/
+[61] https://code.viget.com/
+[62] https://www.viget.com/newsletter/
+[63] https://www.viget.com/
+[64] http://x.com/viget
+[65] https://github.com/vigetlabs
+[66] https://dribbble.com/viget
+[67] https://www.instagram.com/viget/
+[68] https://www.linkedin.com/company/viget-labs
+[69] https://vimeo.com/viget/collections
+[71] https://www.viget.com/dc-metro-hq/
+[72] https://www.viget.com/durham/
+[73] https://www.viget.com/boulder/
+[74] https://www.viget.com/chattanooga/
+[75] https://www.viget.com/terms-conditions/
+[76] https://www.viget.com/privacy-policy/
+[77] https://individual.carefirst.com/individuals-families/mandates-policies/machine-readable-file.page
diff --git a/static/archive/www-viget-com-h4onv6.txt b/static/archive/www-viget-com-h4onv6.txt
@@ -0,0 +1,318 @@
+[1] Skip to Main Content
+[2] Viget
+
+ • [3] Work
+ • [4] Services
+ • [5] Articles
+ • [6] Careers
+ • [7] Contact
+ • Open Menu
+
+Navigation
+
+[9] Viget Close
+
+ • Practice
+ • [11] Work
+ • [12] Services
+ • [13] Articles
+
+We’re a full-service digital agency that’s been helping clients make lasting
+change since 1999.
+
+[14] Contact Us
+
+People
+
+ • [15]Company
+ • [16]Careers
+ • [17]Code of Ethics
+ • [18]Diversity & Inclusion
+
+More
+
+ • [19]Pointless Corp.
+ • [20]Explorations
+ • [21]Code at Viget
+
+Featured
+
+[22]
+Read the Article: Building the Future, One Intern at a Time
+
+Newsletter
+
+Building the Future, One Intern at a Time
+
+[23]
+Read the Article: Some Thoughts after a Major Ruby on Rails Upgrade
+
+Article
+
+Some Thoughts after a Major Ruby on Rails Upgrade
+
+Docker: Right for Us. Right for You?
+
+[eyJidWNrZXQiOiJ2Z3QtdmlnZXRjb20tYW]
+
+ • [24]Home
+ • [25]Articles
+ • [26]Docker: Right for Us. Right for You?
+
+[27] Subscribe (opens in a new window)
+Share
+
+ • [29] Share this page
+ • [30] Share this page
+ • [31] Post this page
+
+Eli Fatsi, Former Development Director
+
+Article Categories: [32] #Code, [33] #Back-end Engineering, [34] #Tooling
+
+Posted on March 29, 2021
+
+ • [35]
+ Share
+ • [36]
+ Share
+ • [37]
+ Post
+
+Some reflections from our adventures in Docker
+
+S o m e r e f l e c t i o n s f r o m o u r a d v e n t u r e s i n D o c k e r
+
+Over the last two years, we've been using Docker more and more here at Viget.
+We're a consulting agency so our developers are cycling on and off a wide
+variety of projects, often times building and maintaining multiple distinct
+platforms simultaneously. Docker has provided a handful of specific wins with
+this setup, and brought with it a handful of it's own drawbacks:
+
+Where Docker is Good [38]#
+
+Running multiple versions of dependencies (eg: postgres)
+
+This used to be a large pain every now and then, now it's not. What more can I
+say except thank you, Docker.
+
+New developers onboarding to a project
+
+Now when people onboard to a project with a sprawling tech stack, they don't
+have to become a local devops expert in order to configure their machine to run
+the app locally. We can usually abstract the build and setup to a handful of
+docker-compose calls and they're up and running.
+
+Running non-standard dependencies uniformly on everyone's machine (eg: redis,
+elasticsearch, background worker queues)
+
+Similar to the problem of onboarding new people, onboarding new tech to a
+running team can involve a lot of work if everyone has to update their stack to
+accomodate the new tooling. Docker encapsulates a lot of the initial setup
+complexity.
+
+Where Docker is Frustrating [39]#
+
+While Docker can make it quicker and easier to get complicated architectures
+running on a number of developer (and non-developer) machines, it does so at a
+cost. Docker adds an additional layer of infrastructure between you (the human)
+and an application's runtime, which acts as a hurdle for a lot of standard
+development tools and strategies.
+
+For example, one of the first issues I ran into was seeing that my breakpoints
+were suddenly unusable. When developing in a pure local environment, something
+like binding.pry (Ruby) or breakpoint() (Python) will halt the code
+mid-execution, and expose an interactive REPL for inspection. If you have a
+Rails application running via a docker-compose.yml file, those breakpoints
+hang, but there's shell to interact or continue with.
+
+There is, of course, a solution to this issue (check out the follow on post to
+this: [40]Docker + Rails), but it involves learning a bit about Docker and
+using more than just the basic docker-compose build/up/down commands. Which
+really seems to be the story for most problems we've hit: the solution is out
+there, and the more you know about Docker / Docker Compose, the easier it is to
+find it.
+
+Summary [41]#
+
+For the type of work we do at Viget, working on multiple projects across wildly
+varying tech stacks, leaning on dependencies that range from cutting edge to
+practically abandoned, Docker has brought our team way more upsides than it has
+drawbacks.
+
+I will mention that we haven't experimented with Docker in any of our
+deployment / devops processes. I know this is another selling point for people
+(bringing production / development environments closer together in behavior),
+but we haven't found a use case for that that seems more appealing than our
+current setup (primarily built on Terraform & Ansible).
+
+Whether or not leaning on Docker is right for you is entirely dependent on the
+kinds of projects you're running and what team you have working on them.
+Something I can say with some certainty though is that Docker has the power to
+hide a lot of local devops pain points, so if you and your team are drowning in
+those, maybe give Docker a whirl.
+
+Related Articles
+
+ • [42]
+ Some Thoughts after a Major Ruby on Rails Upgrade
+
+ Article
+
+ Some Thoughts after a Major Ruby on Rails Upgrade
+
+ Noah Over
+
+ • [43]
+ Setting up a Python Project Using asdf, PDM, and Ruff
+
+ Article
+
+ Setting up a Python Project Using asdf, PDM, and Ruff
+
+ Danny Brown
+
+ • [44]
+ Craft 5: What It Means For Super Table Page Builders
+
+ Article
+
+ Craft 5: What It Means For Super Table Page Builders
+
+ Max Myers
+
+The Viget Newsletter
+
+Nobody likes popups, so we waited until now to recommend our newsletter,
+featuring thoughts, opinions, and tools for building a better digital world.
+[45]Read the current issue.
+
+[46]Subscribe Here (opens in new window)
+
+Site Footer
+
+Have an unsolvable problem or audacious idea?
+
+Let’s get to work
+
+[47] Contact Us [48] [email protected] [49] 703.891.0670
+
+ • Practice
+ • [50]Work
+ • [51]Services
+ • [52]Articles
+
+ • People
+ • [53]Company
+ • [54]Careers
+ • [55]Code of Ethics
+ • [56]Diversity & Inclusion
+
+ • More
+ • [57]Pointless Corp.
+ • [58]Explorations
+ • [59]Code at Viget
+
+Sign Up For Our Newsletter
+
+A curated periodical featuring thoughts, opinions, and tools for building a
+better digital world.
+
+[60] Check it out
+
+Social Links
+
+[61] Viget
+
+ • [62]
+ • [63]
+ • [64]
+ • [65]
+ • [66]
+ • [67]
+
+Viget rhymes with 'dig it'. Click here to hear how we say it.
+
+Office Locations
+
+ • [69]Washington, DC Metro
+ • [70]Durham, NC
+ • [71]Boulder, CO
+ • [72]Chattanooga, TN
+
+© 1999 – 2024 Viget Labs, LLC. [73]Terms [74]Privacy [75]MRF
+
+
+References:
+
+[1] https://www.viget.com/articles/docker-right-for-us-right-for-you-1/#content
+[2] https://www.viget.com/
+[3] https://www.viget.com/work/
+[4] https://www.viget.com/services/
+[5] https://www.viget.com/articles/
+[6] https://www.viget.com/careers/
+[7] https://www.viget.com/contact/
+[9] https://www.viget.com/
+[11] https://www.viget.com/work/
+[12] https://www.viget.com/services/
+[13] https://www.viget.com/articles/
+[14] https://www.viget.com/contact/
+[15] https://www.viget.com/about/
+[16] https://www.viget.com/careers/
+[17] https://www.viget.com/code-of-ethics/
+[18] https://www.viget.com/diversity-equity-and-inclusion/
+[19] https://pointlesscorp.com/
+[20] https://explorations.viget.com/
+[21] https://code.viget.com/
+[22] https://www.viget.com/newsletter/building-the-future-one-intern-at-a-time/
+[23] https://www.viget.com/articles/some-thoughts-after-a-major-ruby-on-rails-upgrade/
+[24] https://www.viget.com/
+[25] https://www.viget.com/articles
+[26] https://www.viget.com/articles/docker-right-for-us-right-for-you-1/#hero
+[27] http://eepurl.com/gtHqsj
+[29] https://www.facebook.com/sharer/sharer.php?u=https%3A%2F%2Fwww.viget.com%2Farticles%2Fdocker-right-for-us-right-for-you-1%2F
+[30] http://www.linkedin.com/shareArticle?mini=true&url=https%3A%2F%2Fwww.viget.com%2Farticles%2Fdocker-right-for-us-right-for-you-1%2F
+[31] https://x.com/intent/tweet?text=Some%20reflections%20from%20our%20adventures%20in%20Docker%20https%3A%2F%2Fwww.viget.com%2Farticles%2Fdocker-right-for-us-right-for-you-1%2F
+[32] https://www.viget.com/articles/?category=code#results
+[33] https://www.viget.com/articles/?category=back-end-engineering#results
+[34] https://www.viget.com/articles/?category=tooling#results
+[35] https://www.facebook.com/sharer/sharer.php?u=https%3A%2F%2Fwww.viget.com%2Farticles%2Fdocker-right-for-us-right-for-you-1%2F
+[36] http://www.linkedin.com/shareArticle?mini=true&url=https%3A%2F%2Fwww.viget.com%2Farticles%2Fdocker-right-for-us-right-for-you-1%2F
+[37] https://x.com/intent/tweet?text=Some%20reflections%20from%20our%20adventures%20in%20Docker%20https%3A%2F%2Fwww.viget.com%2Farticles%2Fdocker-right-for-us-right-for-you-1%2F
+[38] https://www.viget.com/articles/docker-right-for-us-right-for-you-1/#where-docker-is-good
+[39] https://www.viget.com/articles/docker-right-for-us-right-for-you-1/#where-docker-is-frustrating
+[40] https://gist.github.com/efatsi/23b42c707ba39a6ae0926c1d1fc71749
+[41] https://www.viget.com/articles/docker-right-for-us-right-for-you-1/#summary
+[42] https://www.viget.com/articles/some-thoughts-after-a-major-ruby-on-rails-upgrade/
+[43] https://www.viget.com/articles/setting-up-a-python-project-using-asdf-pdm-and-ruff/
+[44] https://www.viget.com/articles/craft-5-what-it-means-for-super-table-page-builders/
+[45] https://www.viget.com/newsletter
+[46] http://eepurl.com/gtHqsj
+[47] https://www.viget.com/contact/
+[48] mailto:[email protected]?subject=Hello%2C%20Viget%21
+[49] tel:7038910670
+[50] https://www.viget.com/work/
+[51] https://www.viget.com/services/
+[52] https://www.viget.com/articles/
+[53] https://www.viget.com/about/
+[54] https://www.viget.com/careers/
+[55] https://www.viget.com/code-of-ethics/
+[56] https://www.viget.com/diversity-equity-and-inclusion/
+[57] https://pointlesscorp.com/
+[58] https://explorations.viget.com/
+[59] https://code.viget.com/
+[60] https://www.viget.com/newsletter/
+[61] https://www.viget.com/
+[62] http://x.com/viget
+[63] https://github.com/vigetlabs
+[64] https://dribbble.com/viget
+[65] https://www.instagram.com/viget/
+[66] https://www.linkedin.com/company/viget-labs
+[67] https://vimeo.com/viget/collections
+[69] https://www.viget.com/dc-metro-hq/
+[70] https://www.viget.com/durham/
+[71] https://www.viget.com/boulder/
+[72] https://www.viget.com/chattanooga/
+[73] https://www.viget.com/terms-conditions/
+[74] https://www.viget.com/privacy-policy/
+[75] https://individual.carefirst.com/individuals-families/mandates-policies/machine-readable-file.page