diff --git a/content/elsewhere/local-docker-best-practices/index.md b/content/elsewhere/local-docker-best-practices/index.md index 1517dae..36423ec 100644 --- 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 new file mode 100644 index 0000000..f681474 --- /dev/null +++ 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 /.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 /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]chronicles@evilmartians.com + +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:chronicles@evilmartians.com +[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 new file mode 100644 index 0000000..acad174 --- /dev/null +++ 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] hello@viget.com [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:hello@viget.com?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 new file mode 100644 index 0000000..14b93df --- /dev/null +++ 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] hello@viget.com [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:hello@viget.com?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