<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Adam Niedzielski</title>
    <description>The latest articles on DEV Community by Adam Niedzielski (@adamniedzielski).</description>
    <link>https://dev.to/adamniedzielski</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F842411%2F5ee446a0-251f-4731-9916-9ba425175165.jpeg</url>
      <title>DEV Community: Adam Niedzielski</title>
      <link>https://dev.to/adamniedzielski</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/adamniedzielski"/>
    <language>en</language>
    <item>
      <title>Docker for skeptics</title>
      <dc:creator>Adam Niedzielski</dc:creator>
      <pubDate>Wed, 18 May 2022 08:29:47 +0000</pubDate>
      <link>https://dev.to/adamniedzielski/docker-for-skeptics-3eod</link>
      <guid>https://dev.to/adamniedzielski/docker-for-skeptics-3eod</guid>
      <description>&lt;p&gt;I first tried Docker in early 2014. I was working for a software agency. We were looking for ways to move away from manually configuring virtual servers for Ruby on Rails application deployments that involved copy-pasting a bunch of commands from a runbook. Back then Kubernetes wasn’t released to the public yet (&lt;a href="https://cloudplatform.googleblog.com/2015/07/Kubernetes-V1-Released.html"&gt;1.0 got released in 2015&lt;/a&gt;), Docker Compose was called Fig, and &lt;a href="https://www.docker.com/blog/welcoming-the-orchard-and-fig-team/"&gt;people were thinking that they could use Fig for running applications in production&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I tried to get a Ruby on Rails app running in production where the container is monitored and automatically restarted, so an equivalent of tools like systemd. I looked into using Docker for running the database and found the topic of persistence immature at that time. As having persistent data storage for my database was very important for me, I got scared there.&lt;/p&gt;

&lt;p&gt;I also looked into how I can make sure that the containers are started in the right order, so, for example, Postgres gets started before the app server automatically, in case of a complete server restart.&lt;/p&gt;

&lt;p&gt;The limitations I discovered at that time made me skeptical about Docker. If I had been working at Google, I probably would have started contributing to Kubernetes in my “20% learning time”. Instead I gave a recommendation to my colleagues to &lt;a href="https://blog.sundaycoding.com/blog/2014/03/10/learn-chef-its-hard-but-worth-it/"&gt;use Chef&lt;/a&gt; and decided to stay away from Docker.&lt;/p&gt;

&lt;p&gt;In the next few years I avoided Docker and responded to the proposals involving Docker with the grumpy “how about not?” approach. The first time I looked at it without skepticism was when &lt;a href="https://twitter.com/amrAbdelwahab"&gt;Amr Abdelwahab&lt;/a&gt; delivered the talk “&lt;a href="https://www.youtube.com/watch?v=ua6tJ9d8pE8"&gt;The broken promise of dockerization in the land of Ruby&lt;/a&gt;” in early 2020 at Ruby User Group Berlin. Amr gave a compelling presentation that showed me that Docker improved a lot over the course of the last couple of years.&lt;/p&gt;

&lt;p&gt;What I took away from the presentation was the use of multi stage builds to avoid repetition in Dockerfiles, improved performance in development, and a Makefile to hide the complexity of long commands. I encourage you to check out the &lt;a href="https://github.com/amrabdelwahab/docker-ruby-boilerplate"&gt;repository with the boilerplate&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It still took me over a year to come back to this idea. It happened for the first time in a hobby project called &lt;a href="https://github.com/adamniedzielski/happy_news"&gt;Happy News&lt;/a&gt; where I played with a &lt;a href="https://github.com/adamniedzielski/happy_news/pull/33/files"&gt;strange GitHub Actions workflow&lt;/a&gt;: the image is first built and then the tests run inside the container. This was more to prove a point about the holy grail of dev / CI / prod parity than anything else. GitHub Actions are convenient for running tests without explicitly using Docker anyway.&lt;/p&gt;

&lt;p&gt;What I’m especially proud of is using the same Dockerfile to &lt;a href="https://github.com/adamniedzielski/happy_news/pull/35/files"&gt;deploy to Heroku using their Container Registry&lt;/a&gt;. I currently see it as the best deployment method for bootstrapping products, because it allows you to transition to Kubernetes easily, if you happen to start earning some money and actually having traffic.&lt;/p&gt;

&lt;p&gt;We use the same approach to Docker in development at my team at Marley Spoon. My team is called RAMEN and we develop an internal tool for our colleagues from the Culinary department. This tool enables them to create new recipes and plan weekly menus for Marley Spoon and Dinnerly customers.&lt;/p&gt;

&lt;p&gt;The system has an API part which is a monolithic Ruby on Rails application. In development we need a few services running - app server, Postgres, &lt;a href="https://github.com/que-rb/que"&gt;Que&lt;/a&gt;, and &lt;a href="https://mailcatcher.me/"&gt;MailCatcher&lt;/a&gt;. We use Docker Compose for making sure that they’re running properly. My colleague Bogdan also got exposed to Amr’s ideas and introduced a Makefile to our Docker setup. This makes it easier for other team members to start the containers without being exposed to the complexity of the Docker Compose commands. As we’re a cross-functional team with some folks focusing on the frontend I see a big advantage of this.&lt;/p&gt;

&lt;p&gt;You can see a good overview of the setup in an &lt;a href="https://github.com/adamniedzielski/sidekiq-staged_push-demo"&gt;open source application that I created&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# Dockerfile&lt;/span&gt;

&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;ruby:3.1.0-alpine&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;dev&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;apk add build-base postgresql-dev tzdata git bash
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; BUNDLE_PATH=/bundle \&lt;/span&gt;
    BUNDLE_BIN=/bundle/bin \
    GEM_HOME=/bundle
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; PATH="${BUNDLE_BIN}:${PATH}"&lt;/span&gt;

&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;dev&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;ci&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; Gemfile Gemfile.lock ./&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;bundle &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . ./&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# docker-compose.yml&lt;/span&gt;

&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;3.9"&lt;/span&gt;
&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;web&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;context&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.&lt;/span&gt;
      &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dev&lt;/span&gt;
    &lt;span class="na"&gt;env_file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;.env.dev&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;BUNDLE_ENTERPRISE__CONTRIBSYS__COM&lt;/span&gt;
    &lt;span class="na"&gt;stdin_open&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="na"&gt;tty&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bundle exec rails server --binding 0.0.0.0&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;.:/app"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;bundle:/bundle&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;3000:3000"&lt;/span&gt;
    &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;db&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;redis&lt;/span&gt;
  &lt;span class="na"&gt;sidekiq&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;context&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.&lt;/span&gt;
      &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dev&lt;/span&gt;
    &lt;span class="na"&gt;env_file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;.env.dev&lt;/span&gt;
    &lt;span class="na"&gt;stdin_open&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="na"&gt;tty&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bundle exec sidekiq&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;.:/app"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;bundle:/bundle&lt;/span&gt;
    &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;db&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;redis&lt;/span&gt;
  &lt;span class="na"&gt;db&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;healthcheck/postgres:alpine"&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;POSTGRES_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;db_password&lt;/span&gt;
  &lt;span class="na"&gt;redis&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;redis:6-alpine"&lt;/span&gt;
    &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;redis-server&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;6379:6379"&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;redis:/data"&lt;/span&gt;
&lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;bundle&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;redis&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight make"&gt;&lt;code&gt;&lt;span class="c"&gt;# Makefile
&lt;/span&gt;
&lt;span class="nl"&gt;build&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
    docker-compose build

&lt;span class="nl"&gt;bundle&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
    docker-compose run &lt;span class="nt"&gt;--rm&lt;/span&gt; web bundle &lt;span class="nb"&gt;install&lt;/span&gt;

&lt;span class="nl"&gt;dbsetup&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
    docker-compose run &lt;span class="nt"&gt;--rm&lt;/span&gt; web bundle &lt;span class="nb"&gt;exec &lt;/span&gt;rails db:setup

&lt;span class="nl"&gt;server&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
    docker-compose run &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;--service-ports&lt;/span&gt; web

&lt;span class="nl"&gt;console&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
    docker-compose run &lt;span class="nt"&gt;--rm&lt;/span&gt; web bundle &lt;span class="nb"&gt;exec &lt;/span&gt;rails console

&lt;span class="nl"&gt;sidekiq&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
    docker-compose run &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;--service-ports&lt;/span&gt; sidekiq

&lt;span class="nl"&gt;rubocop&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
    docker-compose run &lt;span class="nt"&gt;--rm&lt;/span&gt; web bundle &lt;span class="nb"&gt;exec &lt;/span&gt;rubocop

&lt;span class="nl"&gt;bash&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
    docker-compose run &lt;span class="nt"&gt;--rm&lt;/span&gt; web bash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# .env.dev&lt;/span&gt;

&lt;span class="nv"&gt;DATABASE_USERNAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;postgres
&lt;span class="nv"&gt;DATABASE_PASSWORD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;db_password
&lt;span class="nv"&gt;DATABASE_HOST&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;db
&lt;span class="nv"&gt;REDIS_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;redis://redis:6379/1
&lt;span class="nv"&gt;USE_STAGED_PUSH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true
&lt;/span&gt;&lt;span class="nv"&gt;USE_SIDEKIQ_ENTERPRISE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://github.com/adamniedzielski/sidekiq-staged_push"&gt;sidekiq-staged_push&lt;/a&gt; is my first Ruby gem that uses Docker for development. The configuration doesn’t differ much, but I’m including the link here for reference. I got so excited with how easy the setup becomes for open source contributors that I &lt;a href="https://github.com/mperham/sidekiq/pull/5183"&gt;submitted a PR to Sidekiq to add it&lt;/a&gt;. Sadly it didn’t get accepted, but I finally learned &lt;a href="https://stackoverflow.com/questions/2145590/what-is-the-purpose-of-phony-in-a-makefile"&gt;what this “.PHONY” in a Makefile means&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;What followed was a&lt;a href="https://github.com/adamniedzielski/signal_nudge/"&gt; simple Telegram bot that nudges people to switch to Signal&lt;/a&gt;. Setting up dev and prod was a matter of copying over and adjusting the 4 files included above. I also noticed that it’s about 100 times faster to set up a new Ruby version on my machine if I use Docker and not rbenv. That’s when I realized that using Docker for Ruby became my new default approach and I changed from a Docker skeptic to a Docker preacher.&lt;/p&gt;

&lt;p&gt;One thing that I haven’t figured out yet is how to &lt;a href="https://github.com/amrabdelwahab/docker-ruby-boilerplate/issues/2"&gt;investigate gem source code&lt;/a&gt; with this setup. I’ve also recently &lt;a href="https://github.com/adamniedzielski/my-messaging-app"&gt;started playing around&lt;/a&gt; with converting the same approach to the land of Elixir. If you have ideas how to improve it please help me!&lt;/p&gt;

</description>
      <category>docker</category>
      <category>kubernetes</category>
      <category>ruby</category>
      <category>elixir</category>
    </item>
  </channel>
</rss>
