Containerizing Rails: Techniques, Pitfalls, & Best Practices

Containerizing Rails: Techniques, Pitfalls, & Best Practices

Presentation by Daniel Azuma at RailsConf 2018. Covers best practices for designing Docker images for Rails applications. More information at http://daniel-azuma.com/railsconf2018

E06aa8f63d2a1753a2b352bc1cabbde2?s=128

Daniel Azuma

April 18, 2018
Tweet

Transcript

  1. None
  2. Containerizing Rails Techniques, Pitfalls, & Best Practices Daniel Azuma Google

    Cloud Platform
  3. Daniel Azuma http://daniel-azuma.com/ @danielazuma

  4. None
  5. Languages, Optimizations, and Libraries

  6. Languages, Optimizations, and Libraries

  7. None
  8. Basic concepts

  9. Håkan Dahlström, "Multicolored Containers" Source: https://www.flickr.com/photos/dahlstroms/3144199355 License: https://creativecommons.org/licenses/by/2.0/

  10. Isolation

  11. None
  12. • File system

  13. • File system • CPU allocation

  14. • File system • CPU allocation • Memory

  15. • File system • CPU allocation • Memory • Network

    stack
  16. • File system • CPU allocation • Memory • Network

    stack • Users
  17. • File system • CPU allocation • Memory • Network

    stack • Users • Processes
  18. • File system • CPU allocation • Memory • Network

    stack • Users • Processes
  19. • File system • CPU allocation • Memory • Network

    stack • Users • Processes != VM
  20. • File system • CPU allocation • Memory • Network

    stack • Users • Processes Linux host
  21. • File system • CPU allocation • Memory • Network

    stack • Users • Processes CPU arch Linux kernel Linux host:
  22. • File system • CPU alloc • Memory • Network

    stack • Users • Processes CPU arch Linux kernel Linux host: • File system • CPU alloc • Memory • Network stack • Users • Processes • File system • CPU alloc • Memory • Network stack • Users • Processes
  23. Images

  24. None
  25. Dockerfile

  26. # Rails Dockerfile FROM ruby WORKDIR /opt/app COPY . /opt/app

    RUN bundle install ENV PORT=8080 CMD ["bundle", "exec", "rails", "server"]
  27. # Rails Dockerfile FROM ruby WORKDIR /opt/app COPY . /opt/app

    RUN bundle install ENV PORT=8080 CMD ["bundle", "exec", "rails", "server"]
  28. # Rails Dockerfile FROM ruby WORKDIR /opt/app COPY . /opt/app

    RUN bundle install ENV PORT=8080 CMD ["bundle", "exec", "rails", "server"]
  29. # Rails Dockerfile FROM ruby WORKDIR /opt/app COPY . /opt/app

    RUN bundle install ENV PORT=8080 CMD ["bundle", "exec", "rails", "server"]
  30. # Rails Dockerfile FROM ruby WORKDIR /opt/app COPY . /opt/app

    RUN bundle install ENV PORT=8080 CMD ["bundle", "exec", "rails", "server"]
  31. # Rails Dockerfile FROM ruby WORKDIR /opt/app COPY . /opt/app

    RUN bundle install ENV PORT=8080 CMD ["bundle", "exec", "rails", "server"]
  32. Which base image?

  33. FROM ruby FROM alpine FROM phusion/baseimage ??? Tip #1: Read

    and understand your base image
  34. Size does matter

  35. apt-get install

  36. RUN apt-get clean \ && rm -f /var/lib/apt/lists/*_*

  37. RUN apt-get update -y RUN apt-get install -y -q mypackage

    RUN apt-get clean \ && rm -f /var/lib/apt/lists/*_*
  38. RUN apt-get update -y \ && apt-get install -y -q

    mypackage \ && apt-get clean \ && rm -f /var/lib/apt/lists/*_*
  39. FROM base-image

  40. FROM base-image RUN apt-get update

  41. FROM base-image RUN apt-get update RUN apt-get install mypkg

  42. FROM base-image RUN apt-get update RUN apt-get install mypkg RUN

    apt-get clean
  43. FROM base-image RUN apt-get update \ && apt-get install mypkg

    \ && apt-get clean Tip #2: Combine install commands with cleanup
  44. RUN apt-get install build-essential \ && bundle install

  45. http://artgallery.msfc.nasa.gov/4459.html Source: NASA

  46. FROM my-base AS builder COPY . /app RUN apt-get install

    build-essential \ && bundle install --deployment FROM my-base COPY --from=builder /app /app
  47. FROM my-base AS builder COPY . /app RUN apt-get install

    build-essential \ && bundle install --deployment FROM my-base COPY --from=builder /app /app
  48. FROM my-base AS builder COPY . /app RUN apt-get install

    build-essential \ && bundle install --deployment FROM my-base COPY --from=builder /app /app Ruby base image
  49. FROM my-base AS builder COPY . /app RUN apt-get install

    build-essential \ && bundle install --deployment FROM my-base COPY --from=builder /app /app Ruby base image App source
  50. FROM my-base AS builder COPY . /app RUN apt-get install

    build-essential \ && bundle install --deployment FROM my-base COPY --from=builder /app /app Ruby base image App source Compilers, etc.
  51. FROM my-base AS builder COPY . /app RUN apt-get install

    build-essential \ && bundle install --deployment FROM my-base COPY --from=builder /app /app Ruby base image App source Vendored gems Compilers, etc.
  52. FROM my-base AS builder COPY . /app RUN apt-get install

    build-essential \ && bundle install --deployment FROM my-base COPY --from=builder /app /app Ruby base image Ruby base image App source Vendored gems Compilers, etc.
  53. FROM my-base AS builder COPY . /app RUN apt-get install

    build-essential \ && bundle install --deployment FROM my-base COPY --from=builder /app /app Ruby base image Ruby base image App source Vendored gems Compilers, etc. App w/vendored gems
  54. FROM my-base AS builder COPY . /app RUN apt-get install

    build-essential \ && bundle install --deployment FROM my-base COPY --from=builder /app /app Ruby base image Ruby base image App source Vendored gems Compilers, etc. App w/vendored gems
  55. FROM my-base AS builder COPY . /app RUN apt-get install

    build-essential \ && bundle install --deployment FROM my-base COPY --from=builder /app /app Ruby base image App w/vendored gems Tip #3: Use a separate build stage
  56. Gerd Rohs, "Image in the image" https://pixabay.com/en/image-in-the-image-mobile-phone-2204798/ CC0

  57. None
  58. RUN echo "en_US.UTF-8 UTF-8" > /etc/locale.gen \ && locale-gen en_US.UTF-8

    ENV LANG en_US.UTF-8 ENV LANGUAGE en_US:en Tip #4: Set the system locale
  59. "Australia in a Nutshell" (Sign contents modified) Photo by: Kristian

    Thøgersen Source: https://www.flickr.com/photos/flottenheimer/5534636006 License: https://creativecommons.org/licenses/by/2.0/
  60. Tip #5: Create an unprivileged user # After building your

    app... RUN adduser -s /bin/sh -u 1001 -G root \ -h /app -S -D rails \ && chown -R rails /app USER rails
  61. # Exec form CMD ["bundle", "exec", "rails", "s"] # Shell

    form CMD bundle exec rails s
  62. # Exec form CMD ["bundle", "exec", "rails", "s"] # Shell

    form CMD bundle exec rails s Tip #6: Prefer exec form for CMD
  63. # Exec form CMD ["bundle", "exec", "rails", "s"] # Shell

    form CMD exec bundle exec rails s Tip #7: Prefix shell form with the “exec” keyword
  64. ONBUILD

  65. # Base image... ONBUILD COPY . /app ONBUILD RUN bundle

    install # ...
  66. # Base image... ONBUILD COPY . /app ONBUILD RUN bundle

    install # ... # App image... FROM base-image # (implicit commands)
  67. # Base image... ONBUILD COPY . /app ONBUILD RUN bundle

    install # ... # App image... FROM base-image # (implicit commands) Tip #8: Avoid ONBUILD
  68. Containers in production

  69. Containers == isolation

  70. Containers == isolation ⟹ predictability

  71. docker run --cpus=2.0 --memory=200M ... Tip #9: Always specify resource

    constraints
  72. apiVersion: v1 kind: Pod spec: containers: - name: rails resources:

    requests: memory: "100Mi" cpu: 0.5 limits: memory: "200Mi" cpu: 1.0 ...
  73. None
  74. None
  75. None
  76. Tip #10: Avoid preforking in a container ???

  77. Tip #11: Scale by adding containers

  78. https://pixabay.com/en/logs-timber-wood-logging-lumber-690888/ CC0

  79. None
  80. None
  81. ENV RAILS_LOG_TO_STDOUT=true Tip #12: Log to STDOUT or an external

    agent
  82. • Optimizing for size/performance • Container contents • Containers in

    production
  83. http://daniel-azuma.com/railsconf2018

  84. Thank you! http://daniel-azuma.com/ @danielazuma http://daniel-azuma.com/railsconf2018