Optimizing Docker for IoT with Multi-Stage Builds

8a1aabc40d859fcb786eb4d28b95d299?s=47 Tim Perry
December 14, 2017

Optimizing Docker for IoT with Multi-Stage Builds

8a1aabc40d859fcb786eb4d28b95d299?s=128

Tim Perry

December 14, 2017
Tweet

Transcript

  1. Optimizing Docker for IoT with Multi-Stage Builds @pimterry

  2. @pimterry

  3. @pimterry

  4. IoT is still new, and still comes with hard problems

    @pimterry
  5. How do you deploy, monitor and manage IoT applications? @pimterry

  6. @pimterry

  7. Isolation Reproducibility Application Delivery @pimterry Why Docker + IoT?

  8. @pimterry

  9. IoT is still new, and still comes with lots of

    hard problems @pimterry
  10. How do you secure code that’s literally in your users’

    hands? @pimterry
  11. How do you quickly deliver application changes in an inherently

    unreliable environment? @pimterry
  12. @pimterry

  13. Lightweight OS for IoT with Docker Optimised support for 25+

    boards Preconfigured flexible networking support Secure by default throughout Read-only root partition + AUFS data partition @pimterry
  14. @pimterry

  15. @pimterry Moby-based container engine for IoT Fully Docker-compatible 3.5x smaller

    than Docker 10-70x more bandwidth efficient ‘docker pull’ Built for low-memory environments Atomic & durable pulls
  16. Resin.io device stack @pimterry

  17. There’s only so much you can do at the device

    level @pimterry
  18. How can I build smaller images? @pimterry

  19. How can I hide build secrets? @pimterry

  20. How can I keep things out of my image? @pimterry

  21. FROM node:8 ARG NPM_TOKEN RUN echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" >> ~/.npmrc &&

    \ npm whoami RUN mkdir -p /usr/src/app WORKDIR /usr/src/app COPY . . RUN npm install && npm run build && rm ~/.npmrc CMD [ "/usr/local/bin/node", "./built.js" ] @pimterry The Problem
  22. FROM node:8 ARG NPM_TOKEN RUN echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" >> ~/.npmrc &&

    \ npm whoami RUN mkdir -p /usr/src/app WORKDIR /usr/src/app COPY . . RUN npm install && npm run build && rm ~/.npmrc CMD [ "/usr/local/bin/node", "./built.js" ] @pimterry The Problem 700MB image (for an empty project)
  23. FROM node:8 ARG NPM_TOKEN RUN echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" >> ~/.npmrc &&

    \ npm whoami RUN mkdir -p /usr/src/app WORKDIR /usr/src/app COPY . . RUN npm install && npm run build && rm ~/.npmrc CMD [ "/usr/local/bin/node", "./built.js" ] @pimterry The Problem
  24. ➜ docker image inspect 274627c8a3a2 [ { "Id": "sha256:274627c8a3a28d...", ...

    "GraphDriver": { "Data": { "LowerDir": "...", "MergedDir": "/var/lib/docker/overlay2/61038f.../merged", "UpperDir": "/var/lib/docker/overlay2/61038f.../diff", "WorkDir": "/var/lib/docker/overlay2/61038f.../work" }, }, } ] @pimterry The Problem
  25. ➜ sudo cat /var/lib/docker/overlay2/61038f.../diff/root/.npmrc //registry.npmjs.org/:_authToken=<!!!secret token!!!> @pimterry The Problem

  26. ➜ docker history e5936788a37a IMAGE CREATED CREATED BY SIZE e5936788a37a

    2 minutes ago /bin/sh -c #(nop) CMD ["/usr/local/bin/no... 0B 79fad4492e73 2 minutes ago |1 NPM_TOKEN= <!!!!!!!secret token!!!!!!!>... 537kB 373ea011abf1 2 minutes ago /bin/sh -c #(nop) COPY dir:6ffc7f0ac3036c2... 459B 155133064e6a 2 minutes ago /bin/sh -c #(nop) WORKDIR /usr/src/app 0B 2a5d69935132 2 minutes ago |1 NPM_TOKEN= <!!!!!!!secret token!!!!!!!>... 0B 274627c8a3a2 2 minutes ago |1 NPM_TOKEN= <!!!!!!!secret token!!!!!!!>... 125B 8f14bc306cc4 3 minutes ago /bin/sh -c #(nop) ARG NPM_TOKEN 0B 2eeae8debf3d 2 days ago /bin/sh -c #(nop) CMD ["node"] 0B <missing> 2 days ago /bin/sh -c set -ex && for key in 6A0... 4.17MB <missing> 2 days ago /bin/sh -c #(nop) ENV YARN_VERSION=1.3.2 0B <missing> 2 days ago /bin/sh -c ARCH= && dpkgArch="$(dpkg --pri... 56.6MB <missing> 2 days ago /bin/sh -c #(nop) ENV NODE_VERSION=8.9.3 0B <missing> 2 days ago /bin/sh -c set -ex && for key in 94A... 129kB ... @pimterry The Problem
  27. Combine RUN commands @pimterry

  28. @pimterry Combined RUN Limitations Breaks caching Doesn’t help with non-RUN

    commands Doesn’t erase history
  29. docker build --squash @pimterry

  30. @pimterry --squash Limitations Requires ‘experimental’ flag Silent disaster if you

    forget to squash Can be less efficient Doesn’t erase history
  31. Rename previous Dockerfile to Dockerfile.build docker build . -f Dockerfile.build

    Copy build output from image to local file with docker cp Build production Dockerfile ⟶ @pimterry Builder Pattern Dockerfile: FROM node:8-alpine RUN mkdir -p /usr/src/app WORKDIR /usr/src/app COPY ./built.js . CMD [ "/usr/local/bin/node", "./built.js" ]
  32. @pimterry Builder Pattern Limitations Pretty complicated Needs manual steps/wrapper automation

    Encourages different dev/prod images & workflow
  33. Multi-stage Builds @pimterry

  34. @pimterry Build in stages Pull content between stages Multi-stage Builds:

  35. FROM base repeatedly @pimterry

  36. FROM node:latest ... FROM node:alpine ... @pimterry

  37. FROM base as first-stage ... FROM base2 ... FROM first-stage

    ... @pimterry
  38. FROM base as first-stage ... FROM base2 COPY --from=first-stage /a

    /b @pimterry
  39. Bonus: COPY --from=any-image @pimterry

  40. History & layers for each stage are independent @pimterry

  41. The last stage is the end result @pimterry

  42. (Unless it’s not) docker build --target=<stage-name> @pimterry

  43. FROM base as build-stage ... FROM build-stage as test-stage ...

    FROM build-stage @pimterry Change default target:
  44. Watch out: Non-target stages are considered dangling @pimterry

  45. Watch out: --cache-from breaks multi-stage caching @pimterry

  46. FROM node:8 AS build ARG NPM_TOKEN RUN echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" >>

    ~/.npmrc && \ npm whoami RUN mkdir -p /usr/src/app WORKDIR /usr/src/app COPY . . RUN npm install && npm run build CMD [ "/usr/local/bin/node", "./built.js" ] FROM node:8-alpine RUN mkdir -p /usr/src/app WORKDIR /usr/src/app COPY --from=build /usr/src/app/built.js . CMD [ "/usr/local/bin/node", "./built.js" ] @pimterry Fixed Multi-Stage Dockerfile
  47. FROM node:8 AS build ARG NPM_TOKEN RUN echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" >>

    ~/.npmrc && \ npm whoami RUN mkdir -p /usr/src/app WORKDIR /usr/src/app COPY . . RUN npm install && npm run build CMD [ "/usr/local/bin/node", "./built.js" ] FROM node:8-alpine RUN mkdir -p /usr/src/app WORKDIR /usr/src/app COPY --from=build /usr/src/app/built.js . CMD [ "/usr/local/bin/node", "./built.js" ] @pimterry Fixed Multi-Stage Dockerfile 70MB image (90% smaller)
  48. IMAGE CREATED CREATED BY SIZE 0e1999330a57 5 seconds ago /bin/sh

    -c #(nop) CMD ["/usr/local/bin/no... 0B bdd51c6251d5 6 seconds ago /bin/sh -c #(nop) COPY file:19fa00c24fa256... 614kB fadd597467a6 6 seconds ago /bin/sh -c #(nop) WORKDIR /usr/src/app 0B e7bffff20b93 6 seconds ago /bin/sh -c mkdir -p /usr/src/app 0B 144aaf4b1367 5 days ago /bin/sh -c #(nop) CMD ["node"] 0B <missing> 5 days ago /bin/sh -c apk add --no-cache --virtual .b... 4.19MB <missing> 5 days ago /bin/sh -c #(nop) ENV YARN_VERSION=1.3.2 0B <missing> 5 days ago /bin/sh -c addgroup -g 1000 node && ad... 59.6MB <missing> 5 days ago /bin/sh -c #(nop) ENV NODE_VERSION=8.9.3 0B <missing> 12 days ago /bin/sh -c #(nop) CMD ["/bin/sh"] 0B <missing> 12 days ago /bin/sh -c #(nop) ADD file:cb381165dec3689... 3.97MB @pimterry Hide secure credentials: Secrets don’t appear in the history Previous stage layers are separate
  49. @pimterry Take huge images: FROM ekidd/rust-musl-builder ADD . ./ RUN

    sudo chown -R rust:rust /home/rust RUN cargo build --release EXPOSE 8000 CMD ["/home/rust/src/.../rust-server"] Resulting image: 2.3 GB github.com/pimterry/multistage-demos/tree/master/rust-server
  50. @pimterry Build tiny images: FROM ekidd/rust-musl-builder as build ADD .

    ./ RUN sudo chown -R rust:rust /home/rust RUN cargo build --release FROM scratch COPY --from=build /home/rust/src/.../rust-server / EXPOSE 8000 CMD ["/rust-server"] Resulting image: 4.8 MB github.com/pimterry/multistage-demos/tree/master/rust-server
  51. Supported in Docker >= 17.05 @pimterry

  52. Multi-stage builds let you: Control image history Control image size

    Declaratively describe your build @pimterry
  53. Optimizing Docker for IoT with Multi-Stage Builds @pimterry