Modernin ohjelmistoprojektin odotetaan olevan yksikkö- ja integraatiotestattu, koodikattavuuden kartoitettu, muutosten katselmoitu, ja koodin siirtyvän automaattisesti versiohallinnasta ajoon beta- ja tuotantoklustereille. Milloin? No versiohallintaan puskettaessa tietysti. Versiohallintalähtöistä, automoitua lähdekoodin ja tuotteenhallintaa GitHubista Traviksen syövereihin ja ajoon Amazonin Elastic Beanstalkiin. Tule kuulemaan hiljaista tietoa tämän vuosituhannen ohjelmistokehityksen perustyökaluista.
5. Docker at Shopify
• Serving production traffic for over a year
• Gives developers more freedom (and responsibility) around
how applications are being ran
• Offers super fast and reliable deploys1
1
When it works. Warstories are plenty
7. Dockerfiles
Super simple (Dockerfile is essentially shell commands + a
handful of specialized instructions)
Super efficient (Detects changes and skips things that have not
changed)
Super inefficient and complicated for anything nontrivial
8. Simple, eh?
FROM ruby:2.1.3
RUN apt-get update
RUN apt-get upgrade
RUN apt-get install -qq -y libmysqlclient-dev
RUN useradd --create-home rails_is_fun
WORKDIR /home/rails_is_fun
COPY . /home/rails_is_fun
RUN bundle install
CMD bundle exec unicorn -p 3000
10. FROM ruby:2.1.3
RUN apt-get update
RUN apt-get upgrade
RUN apt-get install -qq -y libmysqlclient-dev
RUN useradd --create-home rails_is_fun
WORKDIR /home/rails_is_fun
COPY Gemfile /home/rails_is_fun/Gemfile
COPY Gemfile.lock /home/rails_is_fun/Gemfile.lock
RUN bundle install
COPY . /home/rails_is_fun
CMD bundle exec unicorn -p 3000
Even with this you have no concept of Gem cache, so any
changes to Gemfile essentially means building them from
scratch
Not to mention the burden of apt-get update/upgrade
11.
12. What about secrets?
When building application containers its pretty common to
have secrets (API keys, DB urls etc.).
Dockerfile doesn't support any temporary/external files. While
building you can't use VOLUMES either
No real solutions. Everything is bad (secrets in plaintext in
layers, external wrappers etc)
14. The things we needed
• Performance (not only for building a lot of containers but
building a single one quickly)
• Security. We handle money and peoples private
information, so having a secure system is kinda important
• Coordinator for builds
16. Our custom builder Locutus is a Golang app that:
• Listens to webhooks (every commit to GH triggers a build)
• Builds containers
• Pushes to our private registry
• Has a webgui showing the build step-by-step
• Sends hooks about build statuses
• Scales horizontally, eg. we can have N builders
17. The interesting part, building
We replaced Dockerfiles with prepare, precompile and
compile scripts (vanilla Bash)
These scripts actually represent a different build phase and live
in the app repo
Each phase does a docker run with the script and saves the
container with docker commit.
18. prepare
is used for your system packages (eg. apt-get install
libmysqlclient-dev).
In this phase we only copy the script itself to our baseimage
We cache this image based on the md5sum of the prepare
During Shopify builds we really rarely have to run this phase
since system level depencies doesn't change often.
19. precompile
is used for application dependencies and asset building (eg.
bundle install)
Used for creating a folder of wanted artifacts. Only thing that is
persisted is /artifacts
Run on every build, but build upon the previous cache of the
specific app (and branch). So we never end up pulling all gems
because of a Gemfile change etc.
20. compile
is used for moving the generated arfifacts to correct folders
(eg. .bundle) and finishing setup
Run on every build, but super fast since we are just doing
bunch of mv and rm (to cleanup caches etc. to slim the image)
The result of this phase is the final image we actually can
deploy to be run.
21. Build times around 40-50 seconds thanks to heavy caching
Containers never see the keys that are used for decrypting
secrets