Create a docker image based on debootstrap(-ed) debian jessie, suitable to run odoo
#!/bin/bash | |
# debootstrap distro parameter | |
suite="jessie" | |
# docker build tag | |
tag="odoo-deboot:latest" | |
# override if you want to build to a non-temp directory | |
dir= | |
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' \ | |
chroot "$rootfsDir" "$@" | |
} | |
delDir= | |
if [ -z "$dir" ]; then | |
dir="$(mktemp -d ${TMPDIR:-/var/tmp}/docker-mkimage.XXXXXXXXXX)" | |
delDir=1 | |
fi | |
rootfsDir="$dir/rootfs" | |
( set -x; mkdir -p "$rootfsDir" ) | |
debootstrap --arch=amd64 --variant=minbase $suite $rootfsDir | |
cp requirements.txt "$rootfsDir/tmp" | |
# 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 | |
echo "deb http://security.debian.org $suite/updates main" >> "$rootfsDir/etc/apt/sources.list" | |
( | |
set -x | |
sed -i "s/ $suite main$/ $suite main contrib non-free/" "$rootfsDir/etc/apt/sources.list" | |
) | |
( | |
set -x | |
# make sure we're fully up-to-date | |
rootfs_chroot sh -xc 'apt-get update && apt-get dist-upgrade -y' | |
rootfs_chroot sh -xc 'apt-get install -y --fix-missing \ | |
apt-utils \ | |
python2.7 \ | |
python2.7-dev \ | |
python-dev \ | |
python-setuptools \ | |
libevent-dev \ | |
libsasl2-dev \ | |
libldap2-dev \ | |
libyaml-dev \ | |
libxml2 \ | |
libxml2-dev \ | |
libxslt-dev \ | |
zlib1g-dev \ | |
libjpeg-dev \ | |
libfreetype6-dev \ | |
libpng12-dev \ | |
libpq-dev \ | |
npm \ | |
wkhtmltopdf' | |
rootfs_chroot sh -xc 'ln -s /usr/bin/nodejs /usr/bin/node' | |
rootfs_chroot sh -xc 'npm install -dd -g less less-plugin-clean-css' | |
rootfs_chroot sh -xc 'easy_install pip' | |
rootfs_chroot sh -xc 'pip install -r /tmp/requirements.txt --no-cache-dir' | |
rootfs_chroot sh -xc 'apt-get clean' | |
rootfs_chroot sh -xc 'rm -rf /var/lib/apt/lists/*' | |
rootfs_chroot sh -xc 'npm cache clean' | |
) | |
# Docker mounts tmpfs at /dev and procfs at /proc so we can remove them | |
rm -rf "$rootfsDir/dev" "$rootfsDir/proc" | |
mkdir -p "$rootfsDir/dev" "$rootfsDir/proc" | |
compression='xz' | |
tarFile="$dir/rootfs.tar${compression:+.$compression}" | |
touch "$tarFile" | |
( | |
set -x | |
tar --numeric-owner --create --auto-compress --file "$tarFile" --directory "$rootfsDir" --transform='s,^./,,' . | |
) | |
echo >&2 "+ cat > '$dir/Dockerfile'" | |
cat > "$dir/Dockerfile" <<EOF | |
FROM scratch | |
ADD $(basename "$tarFile") / | |
EOF | |
# if our generated image has a decent shell, let's set a default command | |
for shell in /bin/bash /usr/bin/fish /usr/bin/zsh /bin/sh; do | |
if [ -x "$rootfsDir/$shell" ]; then | |
( set -x; echo 'CMD ["'"$shell"'"]' >> "$dir/Dockerfile" ) | |
break | |
fi | |
done | |
( set -x; rm -rf "$rootfsDir" ) | |
if [ "$tag" ]; then | |
( set -x; docker build -t "$tag" "$dir" ) | |
elif [ "$delDir" ]; then | |
# if we didn't specify a tag and we're going to delete our dir, let's just build an untagged image so that we did _something_ | |
( set -x; docker build "$dir" ) | |
fi | |
if [ "$delDir" ]; then | |
( set -x; rm -rf "$dir" ) | |
fi |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment