вторник, 19 мая 2020 г.

Sequential multiple builds with packer

Packer is a great tool to prepare and build virtual machines, but it lacks a feature to build multiple output formats from the same VM in a single run.
For example vagrant, ova, and vhd in one run.
Fortunately, this can be accomplished by writing multiple packer.json files. Which is done the way, that initial VM is provisioned once in the first step, and then kept running. All consequential steps just attach to existing VM and export in desired format, possibly with extra provisioning (vagrant).

The key is to keep VM until the last step. This can be done with "virtualbox-vm" builder and
"keep_registered": true
# content of build.sh
packer setup-vm.json # import ova, or build from iso
packer export-ova.json # export and keep vm
packer export-vhd.json # still keep
packer make-vagrant.json # export and destroy VM

First json: 
 "builders": [
    {
      "type": "virtualbox-ovf",
      "source_path": "base.ova",
      "vm_name": "build-vm",
      "keep_registered": true // this is important
      ...
    },
  ]

Last json: 
"builders": [
  {
    "type": "virtualbox-vm",
    "vm_name": "build-vm", // same vm name
    "keep_registered": false // finally destroy vm
    ...
  }
],

пятница, 8 мая 2020 г.

Tips for writing dockerfiles

Sharing here some insights I got while working on reducing size of the image

Docker images Copy-On-Write based, and new layer created for each RUN command. So, that means clean up step must be in the same RUN as the main operation

Wrong:
RUN apt-get install my-package # will add a package and also cache to layer
RUN apt-get clean # will remove cache from resulting image, but will not reduce the size

Right:
RUN apt-get install my-package && apt-get clean # will add only package itself

docker history command can be used to show a detailed breakdown of the size of layers.


When there is a lot going on in dockerfile grouping all to singe RUN reduces readability a lot. To help with that you can split commands over several RUNs and perform cleanup after each of them.

# installing build dependencies
RUN apt-get install build_dep1 build_dep2 && apt-get clean

# installing runtime dependencies
RUN apt-get install runtime_dep1 runtime_dep2 && apt-get clean



Or use inline comments in single RUN.

RUN apt-get install \

`# installing build dependencies` \
  build_dep1 `# required for ...` && \
  build_dep2 `# also required for build` && \
`# installing runtime dependecies` \ 
 runtime_dep1 `# needed for this` \
 runtime_dep2 `# needed for that` && \
 apt-get clean `# clean up`


Note that I commented why each dependency is required. This will help a lot when it's time to migrate to a new base image. Like update from ubuntu 16 to ubuntu 18