class: center, middle # Development Re-bundling in Dockerland --- class: center, middle ![docker-bundle](http://i.imgur.com/Om332Av.jpg) --- class: middle, center ![docker-text](http://i.imgur.com/bGCr2WO.jpg) --- class: middle, center
Reuse
Cross platform
Orchestration
Deployment
Specification of application dependencies
Build Environment Caching
Networking
Utilization
Idempotent Builds
Lightweight footprint
--- class: middle, center ![bundler-text](http://i.imgur.com/ewZJSgI.jpg) --- class: middle, center
Gem publication
Dependency upgrades
Fetch and save dependencies
Dependency grouping Grouped
Context execution
--- # A Simple Dockerfile ```Dockerfile FROM ruby WORKDIR /app ADD . /app RUN bundle install ``` --- # A Rails Dockerfile ```Dockerfile FROM ruby:2.2.0 RUN apt-get update -qq && apt-get install -y build-essential libpq-dev nodejs RUN mkdir /myapp WORKDIR /myapp ADD Gemfile /myapp/Gemfile ADD Gemfile.lock /myapp/Gemfile.lock RUN bundle install ADD . /myapp ``` Source: [docs.docker.com/compose/rails/](https://docs.docker.com/compose/rails/) --- # A simple workflow ```bash # build the image docker build -t myapp . # run the container docker run -it myapp ruby ... # edit files vi ... # rebuild container docker build -t myapp . # run the container again docker run -it myapp ruby ... ``` --- # Changes to code? Sorted. ```bash Step 1 : FROM ruby:2.2.0 Step 2 : RUN apt-get update -qq && apt-get install -y build-essential... ---> Using cache Step 3 : RUN mkdir /myapp ---> Using cache Step 4 : WORKDIR /myapp ---> Using cache Step 5 : ADD Gemfile /myapp/Gemfile ---> Using cache Step 6 : ADD Gemfile.lock /myapp/Gemfile.lock ---> Using cache Step 7 : RUN bundle install ---> Using cache ``` ```bash Step 8 : ADD . /myapp ✅ # fast, local operation ``` --- class: center, middle ![happypath](http://i.imgur.com/ty2zRAk.jpg) --- class: center, middle # How about updating a gem?
(read 'gem' -> app dependency)
--- # Start over. ```diff # Gemfile.lock - rails (4.2.7) - actionmailer (= 4.2.7) - ... - activesupport (= 4.2.7) + rails (4.2.7.1) + actionmailer (= 4.2.7.1) + ... + activesupport (= 4.2.7.1) ``` ```bash # using cached layers Step 1 : FROM ruby:2.2.0 ... # cache invalidated Step 6 : ADD Gemfile.lock /myapp/Gemfile.lock # starting over... Step 7 : RUN bundle install 🐢 ``` --- class: center, middle # How about system packages?
--- # Same story... ```diff FROM ruby:2.2.0 -RUN apt-get update -qq && apt-get install -y build-essential +RUN apt-get update -qq && apt-get install -y build-essential cowsay ``` ```bash Step 1 : FROM ruby:2.2.0 ---> bc5beaf30723 Step 2 : RUN apt-get update -qq && apt-get install -y build-essential cowsay # all other layers need to be rebuilt Step 7 : RUN bundle install 🐢 ``` --- ```bash RUN bundle install ``` ![turtle](http://i.imgur.com/mHFaYEG.jpg) --- # This is a shared problem ```bash pip install -r requirements.txt => ./vendor ``` ```bash npm install => ./node_modules ``` ```bash yarn pack --filename deps.tar.gz ``` ```bash glide install => ./vendor ``` ```bash nuget install -outputdirectory c:\vendor ``` ```bash cargo build => ./target/.. ``` --- class: center, middle # What did everyone else do? --- class: center, middle ![community](http://www.idahostatesman.com/news/local/community/boise/9lvht/picture60423826/ALTERNATES/FREE_640/rendering)
Figure 1. " container community "
--- # A note on docker-compose ```yml # docker-compose.yml myapp: build: ./ environment: - RAILS_ENV=development links: - database ports: - 3000:3000 database: image: image:version volumes: - app_database_volume:/some/path expose: ... ``` --- # Attempt #1 ```yml # docker-compose.yml myapp: ... environment: - BUNDLE_PATH=/bundle volumes_from: - bundle-cache bundle-cache: image: busybox volumes: - /bundle ```
✅ Persist gem changes between container runs
❌ Still needs to run bundle install during build
Source: [bradgessler.com/articles/docker-bundler](http://bradgessler.com/articles/docker-bundler/) --- class: center, middle
--- # Attempt #2, one step further ```diff # Dockerfile ADD Gemfile /myapp/Gemfile ADD Gemfile.lock /myapp/Gemfile.lock -RUN bundle install 🚨 ``` ```diff # docker-compose.yml myapp: ... + command: ./script/start.sh environment: - BUNDLE_PATH=/.bundle volumes_from: - bundle-cache bundle-cache: ... ``` ```bash # start.sh bundle check || bundle install bundle exec rails s -b 0.0.0.0 ``` Source: [CookiesHQ](https://cookieshq.co.uk/posts/common-problems-when-starting-with-docker-and-rails/), [Fabiano B](https://medium.com/@fbzga/how-to-cache-bundle-install-with-docker-7bed453a5800#.oh0mh8otb), [My entrypoint implementation](https://github.com/charlieegan3/docker-bundler-caching) --- class: center, middle
--- class: center, middle
--- class: center, middle ![community](http://www.idahostatesman.com/news/local/community/boise/9lvht/picture60423826/ALTERNATES/FREE_640/rendering) --- # Gemfile.tip Add more Gemfiles: ```Dockerfile ADD Gemfile /var/www/yourapp/ ADD Gemfile.lock /var/www/yourapp/ RUN bundle install ... ADD Gemfile.tip /var/www/yourapp/ RUN bundle install 🚨 ``` Source: [Philipp Steiner](http://stackoverflow.com/a/28030597/1510063) --- class: center, middle
--- # docker-(r)sync ```yml # docker-compose.yml ... volumes: myapp-bundle-sync: external: true ``` ```yml # docker-sync.yml syncs: myapp-bundle-sync: src: './bundle' dest: '/bundle' sync_host_ip: 'localhost' sync_host_port: 10873 ``` ```bash docker-sync-stack start ``` Source: [Jesal Gadhia](http://jes.al/2016/09/setting-up-a-rails-development-environment-using-docker/) --- class: center, middle ![messy containers](http://i.imgur.com/JwZ2eLn.jpg) --- # Remember this? ```Dockerfile FROM ruby WORKDIR /app ADD . /app RUN bundle install ``` --- class: center, middle # Lets get back to basics ![containers](https://media.giphy.com/media/wYmlB9oJH5zW0/giphy.gif) --- class: middle ### Remember this? Our simple Dockerfile ```diff FROM ruby ADD Gemfile /myapp/Gemfile ADD Gemfile.lock /myapp/Gemfile.lock RUN bundle install ADD . /myapp ``` --- class: middle # In an ideal world... ```diff FROM ruby ADD Gemfile /myapp/Gemfile ADD Gemfile.lock /myapp/Gemfile.lock +ADD ./bundle.tar.gz / RUN bundle install ADD . /myapp ``` --- class: middle # We'd have a command... ```bash docker run -it -v "$(pwd):/app" myapp command-to-cache-bundle ``` That creates... ```bash 📦 bundle.tar.gz 📦 ``` --- class: middle # Let's make it. ```bash # bundlecache script '#!/bin/sh tar -zcf /app/bundle.tar.gz /usr/local/bundle ``` ```bash # placed somewhere in the path /usr/local/bin/bundlecache ``` ```bash # made executable chmod +x /usr/local/bin/bundlecache ``` --- class: middle # Bringing it all together ```diff FROM ruby:2.2.0 RUN apt-get update -qq && apt-get install -y build-essential libpq-dev nodejs RUN mkdir /myapp WORKDIR /myapp ADD Gemfile /myapp/Gemfile ADD Gemfile.lock /myapp/Gemfile.lock +ADD ./bundle.tar.gz / RUN bundle install +RUN printf '#!/bin/sh\ntar -zcf /app/bundle.tar.gz /usr/local/bundle\n' + > /usr/local/bin/bundlecache +RUN chmod +x /usr/local/bin/bundlecache ADD . /myapp ``` Introducing... ```bash docker run -it -v "$(pwd):/app" myapp bundlecache ``` --- # 'bundlecache' * Simple Dockerfile change * Manual caching * No extra compose service * Cache easily cleared ```bash rm bundle.tar.gz && touch bundle.tar.gz && docker build ``` Example Repo: [github.com/charlieegan3/bundlecache](https://github.com/charlieegan3/bundlecache) --- class: middle, center Find me online at [charlieegan3.com](http://charlieegan3.com); email [charlie.egan@unboxed.co](mailto:c@egan.co).