| #!/usr/bin/env bash |
| set -e |
| |
| mkimgdeb="$(basename "$0")" |
| mkimg="$(dirname "$0").sh" |
| |
| usage() { |
| echo >&2 "usage: $mkimgdeb rootfsDir suite [debootstrap-args]" |
| echo >&2 " note: $mkimgdeb meant to be used from $mkimg" |
| exit 1 |
| } |
| |
| rootfsDir="$1" |
| if [ -z "$rootfsDir" ]; then |
| echo >&2 "error: rootfsDir is missing" |
| echo >&2 |
| usage |
| fi |
| shift |
| |
| # we have to do a little fancy footwork to make sure "rootfsDir" becomes the second non-option argument to debootstrap |
| |
| before=() |
| while [ $# -gt 0 ] && [[ "$1" == -* ]]; do |
| before+=( "$1" ) |
| shift |
| done |
| |
| suite="$1" |
| if [ -z "$suite" ]; then |
| echo >&2 "error: suite is missing" |
| echo >&2 |
| usage |
| fi |
| shift |
| |
| # get path to "chroot" in our current PATH |
| chrootPath="$(type -P chroot || :)" |
| if [ -z "$chrootPath" ]; then |
| echo >&2 "error: chroot not found. Are you root?" |
| echo >&2 |
| usage |
| fi |
| |
| rootfs_chroot() { |
| # "chroot" doesn't set PATH, so we need to set it explicitly to something our new debootstrap chroot can use appropriately! |
| |
| # set PATH and chroot away! |
| PATH='/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin' \ |
| "$chrootPath" "$rootfsDir" "$@" |
| } |
| |
| # allow for DEBOOTSTRAP=qemu-debootstrap ./mkimage.sh ... |
| : ${DEBOOTSTRAP:=debootstrap} |
| |
| ( |
| set -x |
| $DEBOOTSTRAP "${before[@]}" "$suite" "$rootfsDir" "$@" |
| ) |
| |
| # now for some Docker-specific tweaks |
| |
| # prevent init scripts from running during install/update |
| echo >&2 "+ echo exit 101 > '$rootfsDir/usr/sbin/policy-rc.d'" |
| cat > "$rootfsDir/usr/sbin/policy-rc.d" <<-'EOF' |
| #!/bin/sh |
| |
| # For most Docker users, "apt-get install" only happens during "docker build", |
| # where starting services doesn't work and often fails in humorous ways. This |
| # prevents those failures by stopping the services from attempting to start. |
| |
| exit 101 |
| EOF |
| chmod +x "$rootfsDir/usr/sbin/policy-rc.d" |
| |
| # prevent upstart scripts from running during install/update |
| ( |
| set -x |
| rootfs_chroot dpkg-divert --local --rename --add /sbin/initctl |
| cp -a "$rootfsDir/usr/sbin/policy-rc.d" "$rootfsDir/sbin/initctl" |
| sed -i 's/^exit.*/exit 0/' "$rootfsDir/sbin/initctl" |
| ) |
| |
| # shrink a little, since apt makes us cache-fat (wheezy: ~157.5MB vs ~120MB) |
| ( set -x; rootfs_chroot apt-get clean ) |
| |
| # this file is one APT creates to make sure we don't "autoremove" our currently |
| # in-use kernel, which doesn't really apply to debootstraps/Docker images that |
| # don't even have kernels installed |
| rm -f "$rootfsDir/etc/apt/apt.conf.d/01autoremove-kernels" |
| |
| # Ubuntu 10.04 sucks... :) |
| if strings "$rootfsDir/usr/bin/dpkg" | grep -q unsafe-io; then |
| # force dpkg not to call sync() after package extraction (speeding up installs) |
| echo >&2 "+ echo force-unsafe-io > '$rootfsDir/etc/dpkg/dpkg.cfg.d/docker-apt-speedup'" |
| cat > "$rootfsDir/etc/dpkg/dpkg.cfg.d/docker-apt-speedup" <<-'EOF' |
| # For most Docker users, package installs happen during "docker build", which |
| # doesn't survive power loss and gets restarted clean afterwards anyhow, so |
| # this minor tweak gives us a nice speedup (much nicer on spinning disks, |
| # obviously). |
| |
| force-unsafe-io |
| EOF |
| fi |
| |
| if [ -d "$rootfsDir/etc/apt/apt.conf.d" ]; then |
| # _keep_ us lean by effectively running "apt-get clean" after every install |
| aptGetClean='"rm -f /var/cache/apt/archives/*.deb /var/cache/apt/archives/partial/*.deb /var/cache/apt/*.bin || true";' |
| echo >&2 "+ cat > '$rootfsDir/etc/apt/apt.conf.d/docker-clean'" |
| cat > "$rootfsDir/etc/apt/apt.conf.d/docker-clean" <<-EOF |
| # Since for most Docker users, package installs happen in "docker build" steps, |
| # they essentially become individual layers due to the way Docker handles |
| # layering, especially using CoW filesystems. What this means for us is that |
| # the caches that APT keeps end up just wasting space in those layers, making |
| # our layers unnecessarily large (especially since we'll normally never use |
| # these caches again and will instead just "docker build" again and make a brand |
| # new image). |
| |
| # Ideally, these would just be invoking "apt-get clean", but in our testing, |
| # that ended up being cyclic and we got stuck on APT's lock, so we get this fun |
| # creation that's essentially just "apt-get clean". |
| DPkg::Post-Invoke { ${aptGetClean} }; |
| APT::Update::Post-Invoke { ${aptGetClean} }; |
| |
| Dir::Cache::pkgcache ""; |
| Dir::Cache::srcpkgcache ""; |
| |
| # Note that we do realize this isn't the ideal way to do this, and are always |
| # open to better suggestions (https://github.com/docker/docker/issues). |
| EOF |
| |
| # remove apt-cache translations for fast "apt-get update" |
| echo >&2 "+ echo Acquire::Languages 'none' > '$rootfsDir/etc/apt/apt.conf.d/docker-no-languages'" |
| cat > "$rootfsDir/etc/apt/apt.conf.d/docker-no-languages" <<-'EOF' |
| # In Docker, we don't often need the "Translations" files, so we're just wasting |
| # time and space by downloading them, and this inhibits that. For users that do |
| # need them, it's a simple matter to delete this file and "apt-get update". :) |
| |
| Acquire::Languages "none"; |
| EOF |
| |
| echo >&2 "+ echo Acquire::GzipIndexes 'true' > '$rootfsDir/etc/apt/apt.conf.d/docker-gzip-indexes'" |
| cat > "$rootfsDir/etc/apt/apt.conf.d/docker-gzip-indexes" <<-'EOF' |
| # Since Docker users using "RUN apt-get update && apt-get install -y ..." in |
| # their Dockerfiles don't go delete the lists files afterwards, we want them to |
| # be as small as possible on-disk, so we explicitly request "gz" versions and |
| # tell Apt to keep them gzipped on-disk. |
| |
| # For comparison, an "apt-get update" layer without this on a pristine |
| # "debian:wheezy" base image was "29.88 MB", where with this it was only |
| # "8.273 MB". |
| |
| Acquire::GzipIndexes "true"; |
| Acquire::CompressionTypes::Order:: "gz"; |
| EOF |
| |
| # update "autoremove" configuration to be aggressive about removing suggests deps that weren't manually installed |
| echo >&2 "+ echo Apt::AutoRemove::SuggestsImportant 'false' > '$rootfsDir/etc/apt/apt.conf.d/docker-autoremove-suggests'" |
| cat > "$rootfsDir/etc/apt/apt.conf.d/docker-autoremove-suggests" <<-'EOF' |
| # Since Docker users are looking for the smallest possible final images, the |
| # following emerges as a very common pattern: |
| |
| # RUN apt-get update \ |
| # && apt-get install -y <packages> \ |
| # && <do some compilation work> \ |
| # && apt-get purge -y --auto-remove <packages> |
| |
| # By default, APT will actually _keep_ packages installed via Recommends or |
| # Depends if another package Suggests them, even and including if the package |
| # that originally caused them to be installed is removed. Setting this to |
| # "false" ensures that APT is appropriately aggressive about removing the |
| # packages it added. |
| |
| # https://aptitude.alioth.debian.org/doc/en/ch02s05s05.html#configApt-AutoRemove-SuggestsImportant |
| Apt::AutoRemove::SuggestsImportant "false"; |
| EOF |
| fi |
| |
| if [ -z "$DONT_TOUCH_SOURCES_LIST" ]; then |
| # tweak sources.list, where appropriate |
| lsbDist= |
| if [ -z "$lsbDist" -a -r "$rootfsDir/etc/os-release" ]; then |
| lsbDist="$(. "$rootfsDir/etc/os-release" && echo "$ID")" |
| fi |
| if [ -z "$lsbDist" -a -r "$rootfsDir/etc/lsb-release" ]; then |
| lsbDist="$(. "$rootfsDir/etc/lsb-release" && echo "$DISTRIB_ID")" |
| fi |
| if [ -z "$lsbDist" -a -r "$rootfsDir/etc/debian_version" ]; then |
| lsbDist='Debian' |
| fi |
| # normalize to lowercase for easier matching |
| lsbDist="$(echo "$lsbDist" | tr '[:upper:]' '[:lower:]')" |
| case "$lsbDist" in |
| debian) |
| # updates and security! |
| if curl -o /dev/null -s --head --fail "http://security.debian.org/dists/$suite/updates/main/binary-$(rootfs_chroot dpkg --print-architecture)/Packages.gz"; then |
| ( |
| set -x |
| sed -i " |
| p; |
| s/ $suite / ${suite}-updates / |
| " "$rootfsDir/etc/apt/sources.list" |
| echo "deb http://security.debian.org $suite/updates main" >> "$rootfsDir/etc/apt/sources.list" |
| ) |
| fi |
| ;; |
| ubuntu) |
| # add the updates and security repositories |
| ( |
| set -x |
| sed -i " |
| p; |
| s/ $suite / ${suite}-updates /; p; |
| s/ $suite-updates / ${suite}-security / |
| " "$rootfsDir/etc/apt/sources.list" |
| ) |
| ;; |
| tanglu) |
| # add the updates repository |
| if [ "$suite" != 'devel' ]; then |
| ( |
| set -x |
| sed -i " |
| p; |
| s/ $suite / ${suite}-updates / |
| " "$rootfsDir/etc/apt/sources.list" |
| ) |
| fi |
| ;; |
| steamos) |
| # add contrib and non-free if "main" is the only component |
| ( |
| set -x |
| sed -i "s/ $suite main$/ $suite main contrib non-free/" "$rootfsDir/etc/apt/sources.list" |
| ) |
| ;; |
| esac |
| fi |
| |
| ( |
| set -x |
| |
| # make sure we're fully up-to-date |
| rootfs_chroot sh -xc 'apt-get update && apt-get dist-upgrade -y' |
| |
| # delete all the apt list files since they're big and get stale quickly |
| rm -rf "$rootfsDir/var/lib/apt/lists"/* |
| # this forces "apt-get update" in dependent images, which is also good |
| |
| mkdir "$rootfsDir/var/lib/apt/lists/partial" # Lucid... "E: Lists directory /var/lib/apt/lists/partial is missing." |
| ) |