debootstrap 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251
  1. #!/usr/bin/env bash
  2. set -e
  3. mkimgdeb="$(basename "$0")"
  4. mkimg="$(dirname "$0").sh"
  5. usage() {
  6. echo >&2 "usage: $mkimgdeb rootfsDir suite [debootstrap-args]"
  7. echo >&2 " note: $mkimgdeb meant to be used from $mkimg"
  8. exit 1
  9. }
  10. rootfsDir="$1"
  11. if [ -z "$rootfsDir" ]; then
  12. echo >&2 "error: rootfsDir is missing"
  13. echo >&2
  14. usage
  15. fi
  16. shift
  17. # we have to do a little fancy footwork to make sure "rootfsDir" becomes the second non-option argument to debootstrap
  18. before=()
  19. while [ $# -gt 0 ] && [[ "$1" == -* ]]; do
  20. before+=( "$1" )
  21. shift
  22. done
  23. suite="$1"
  24. if [ -z "$suite" ]; then
  25. echo >&2 "error: suite is missing"
  26. echo >&2
  27. usage
  28. fi
  29. shift
  30. # get path to "chroot" in our current PATH
  31. chrootPath="$(type -P chroot || :)"
  32. if [ -z "$chrootPath" ]; then
  33. echo >&2 "error: chroot not found. Are you root?"
  34. echo >&2
  35. usage
  36. fi
  37. rootfs_chroot() {
  38. # "chroot" doesn't set PATH, so we need to set it explicitly to something our new debootstrap chroot can use appropriately!
  39. # set PATH and chroot away!
  40. PATH='/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin' \
  41. "$chrootPath" "$rootfsDir" "$@"
  42. }
  43. # allow for DEBOOTSTRAP=qemu-debootstrap ./mkimage.sh ...
  44. : ${DEBOOTSTRAP:=debootstrap}
  45. (
  46. set -x
  47. $DEBOOTSTRAP "${before[@]}" "$suite" "$rootfsDir" "$@"
  48. )
  49. # now for some Docker-specific tweaks
  50. # prevent init scripts from running during install/update
  51. echo >&2 "+ echo exit 101 > '$rootfsDir/usr/sbin/policy-rc.d'"
  52. cat > "$rootfsDir/usr/sbin/policy-rc.d" <<-'EOF'
  53. #!/bin/sh
  54. # For most Docker users, "apt-get install" only happens during "docker build",
  55. # where starting services doesn't work and often fails in humorous ways. This
  56. # prevents those failures by stopping the services from attempting to start.
  57. exit 101
  58. EOF
  59. chmod +x "$rootfsDir/usr/sbin/policy-rc.d"
  60. # prevent upstart scripts from running during install/update
  61. (
  62. set -x
  63. rootfs_chroot dpkg-divert --local --rename --add /sbin/initctl
  64. cp -a "$rootfsDir/usr/sbin/policy-rc.d" "$rootfsDir/sbin/initctl"
  65. sed -i 's/^exit.*/exit 0/' "$rootfsDir/sbin/initctl"
  66. )
  67. # shrink a little, since apt makes us cache-fat (wheezy: ~157.5MB vs ~120MB)
  68. ( set -x; rootfs_chroot apt-get clean )
  69. # this file is one APT creates to make sure we don't "autoremove" our currently
  70. # in-use kernel, which doesn't really apply to debootstraps/Docker images that
  71. # don't even have kernels installed
  72. rm -f "$rootfsDir/etc/apt/apt.conf.d/01autoremove-kernels"
  73. # Ubuntu 10.04 sucks... :)
  74. if strings "$rootfsDir/usr/bin/dpkg" | grep -q unsafe-io; then
  75. # force dpkg not to call sync() after package extraction (speeding up installs)
  76. echo >&2 "+ echo force-unsafe-io > '$rootfsDir/etc/dpkg/dpkg.cfg.d/docker-apt-speedup'"
  77. cat > "$rootfsDir/etc/dpkg/dpkg.cfg.d/docker-apt-speedup" <<-'EOF'
  78. # For most Docker users, package installs happen during "docker build", which
  79. # doesn't survive power loss and gets restarted clean afterwards anyhow, so
  80. # this minor tweak gives us a nice speedup (much nicer on spinning disks,
  81. # obviously).
  82. force-unsafe-io
  83. EOF
  84. fi
  85. if [ -d "$rootfsDir/etc/apt/apt.conf.d" ]; then
  86. # _keep_ us lean by effectively running "apt-get clean" after every install
  87. aptGetClean='"rm -f /var/cache/apt/archives/*.deb /var/cache/apt/archives/partial/*.deb /var/cache/apt/*.bin || true";'
  88. echo >&2 "+ cat > '$rootfsDir/etc/apt/apt.conf.d/docker-clean'"
  89. cat > "$rootfsDir/etc/apt/apt.conf.d/docker-clean" <<-EOF
  90. # Since for most Docker users, package installs happen in "docker build" steps,
  91. # they essentially become individual layers due to the way Docker handles
  92. # layering, especially using CoW filesystems. What this means for us is that
  93. # the caches that APT keeps end up just wasting space in those layers, making
  94. # our layers unnecessarily large (especially since we'll normally never use
  95. # these caches again and will instead just "docker build" again and make a brand
  96. # new image).
  97. # Ideally, these would just be invoking "apt-get clean", but in our testing,
  98. # that ended up being cyclic and we got stuck on APT's lock, so we get this fun
  99. # creation that's essentially just "apt-get clean".
  100. DPkg::Post-Invoke { ${aptGetClean} };
  101. APT::Update::Post-Invoke { ${aptGetClean} };
  102. Dir::Cache::pkgcache "";
  103. Dir::Cache::srcpkgcache "";
  104. # Note that we do realize this isn't the ideal way to do this, and are always
  105. # open to better suggestions (https://github.com/docker/docker/issues).
  106. EOF
  107. # remove apt-cache translations for fast "apt-get update"
  108. echo >&2 "+ echo Acquire::Languages 'none' > '$rootfsDir/etc/apt/apt.conf.d/docker-no-languages'"
  109. cat > "$rootfsDir/etc/apt/apt.conf.d/docker-no-languages" <<-'EOF'
  110. # In Docker, we don't often need the "Translations" files, so we're just wasting
  111. # time and space by downloading them, and this inhibits that. For users that do
  112. # need them, it's a simple matter to delete this file and "apt-get update". :)
  113. Acquire::Languages "none";
  114. EOF
  115. echo >&2 "+ echo Acquire::GzipIndexes 'true' > '$rootfsDir/etc/apt/apt.conf.d/docker-gzip-indexes'"
  116. cat > "$rootfsDir/etc/apt/apt.conf.d/docker-gzip-indexes" <<-'EOF'
  117. # Since Docker users using "RUN apt-get update && apt-get install -y ..." in
  118. # their Dockerfiles don't go delete the lists files afterwards, we want them to
  119. # be as small as possible on-disk, so we explicitly request "gz" versions and
  120. # tell Apt to keep them gzipped on-disk.
  121. # For comparison, an "apt-get update" layer without this on a pristine
  122. # "debian:wheezy" base image was "29.88 MB", where with this it was only
  123. # "8.273 MB".
  124. Acquire::GzipIndexes "true";
  125. Acquire::CompressionTypes::Order:: "gz";
  126. EOF
  127. # update "autoremove" configuration to be aggressive about removing suggests deps that weren't manually installed
  128. echo >&2 "+ echo Apt::AutoRemove::SuggestsImportant 'false' > '$rootfsDir/etc/apt/apt.conf.d/docker-autoremove-suggests'"
  129. cat > "$rootfsDir/etc/apt/apt.conf.d/docker-autoremove-suggests" <<-'EOF'
  130. # Since Docker users are looking for the smallest possible final images, the
  131. # following emerges as a very common pattern:
  132. # RUN apt-get update \
  133. # && apt-get install -y <packages> \
  134. # && <do some compilation work> \
  135. # && apt-get purge -y --auto-remove <packages>
  136. # By default, APT will actually _keep_ packages installed via Recommends or
  137. # Depends if another package Suggests them, even and including if the package
  138. # that originally caused them to be installed is removed. Setting this to
  139. # "false" ensures that APT is appropriately aggressive about removing the
  140. # packages it added.
  141. # https://aptitude.alioth.debian.org/doc/en/ch02s05s05.html#configApt-AutoRemove-SuggestsImportant
  142. Apt::AutoRemove::SuggestsImportant "false";
  143. EOF
  144. fi
  145. if [ -z "$DONT_TOUCH_SOURCES_LIST" ]; then
  146. # tweak sources.list, where appropriate
  147. lsbDist=
  148. if [ -z "$lsbDist" -a -r "$rootfsDir/etc/os-release" ]; then
  149. lsbDist="$(. "$rootfsDir/etc/os-release" && echo "$ID")"
  150. fi
  151. if [ -z "$lsbDist" -a -r "$rootfsDir/etc/lsb-release" ]; then
  152. lsbDist="$(. "$rootfsDir/etc/lsb-release" && echo "$DISTRIB_ID")"
  153. fi
  154. if [ -z "$lsbDist" -a -r "$rootfsDir/etc/debian_version" ]; then
  155. lsbDist='Debian'
  156. fi
  157. # normalize to lowercase for easier matching
  158. lsbDist="$(echo "$lsbDist" | tr '[:upper:]' '[:lower:]')"
  159. case "$lsbDist" in
  160. debian)
  161. # updates and security!
  162. 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
  163. (
  164. set -x
  165. sed -i "
  166. p;
  167. s/ $suite / ${suite}-updates /
  168. " "$rootfsDir/etc/apt/sources.list"
  169. echo "deb http://security.debian.org $suite/updates main" >> "$rootfsDir/etc/apt/sources.list"
  170. )
  171. fi
  172. ;;
  173. ubuntu)
  174. # add the updates and security repositories
  175. (
  176. set -x
  177. sed -i "
  178. p;
  179. s/ $suite / ${suite}-updates /; p;
  180. s/ $suite-updates / ${suite}-security /
  181. " "$rootfsDir/etc/apt/sources.list"
  182. )
  183. ;;
  184. tanglu)
  185. # add the updates repository
  186. if [ "$suite" != 'devel' ]; then
  187. (
  188. set -x
  189. sed -i "
  190. p;
  191. s/ $suite / ${suite}-updates /
  192. " "$rootfsDir/etc/apt/sources.list"
  193. )
  194. fi
  195. ;;
  196. steamos)
  197. # add contrib and non-free if "main" is the only component
  198. (
  199. set -x
  200. sed -i "s/ $suite main$/ $suite main contrib non-free/" "$rootfsDir/etc/apt/sources.list"
  201. )
  202. ;;
  203. esac
  204. fi
  205. (
  206. set -x
  207. # make sure we're fully up-to-date
  208. rootfs_chroot sh -xc 'apt-get update && apt-get dist-upgrade -y'
  209. # delete all the apt list files since they're big and get stale quickly
  210. rm -rf "$rootfsDir/var/lib/apt/lists"/*
  211. # this forces "apt-get update" in dependent images, which is also good
  212. mkdir "$rootfsDir/var/lib/apt/lists/partial" # Lucid... "E: Lists directory /var/lib/apt/lists/partial is missing."
  213. )