[lxc-devel] [PATCH] Add lxc-cross-debian to create debian containers for non native architectures.

Stéphane Graber stgraber at ubuntu.com
Wed Dec 25 20:47:18 UTC 2013


On Wed, Dec 25, 2013 at 09:38:57PM +0100, Laurent Vivier wrote:
> These containers will use the binfmt kernel module and
> an interpreter to execute binaries inside the container.
> 
> To use it :
> 1- configure correctly binfmt on your system to load the
> interpreter.
> 2- provide the path to the interpreter to the lxc template.
>    The interpreter must be statically linked.
> 3- provide the architecture to install (armhf, mips, m68k, sparc, ...)
> 4- provide the suite to install (stable, lenny, etch, ...)
> 
> For instance, if you want to use qemu as an interpreter, check this:
> 
> If you want to create an m68k containers on your x86_64 system,
> you can use a script to do all the work :
> 
>   git clone git at gitorious.org:qemu-m68k/qemu-m68k.git
>   cd qemu-m68k
>   scripts/debian-create-lxc.sh m68k
> 
> You can change "m68k", but m68k is the one really tested...
> [TIPS: want to create a raspberry-pi container ?
>  scripts/debian-create-lxc.sh raspberrypi ]
> 

Why not do it the same way we do in lxc-ubuntu?

In lxc-ubuntu, foreign architecture containers are completely
transparent, basically if qemu-debootstrap is on the system, it'll be
called automatically and then some tweaks are done to setup multi-arch
post-install and install the few bits that don't work properly under
qemu-user-static (netlink mostly).

I personaly would much rather the feature be added to lxc-debian than
have another template for that which would mostly create some extra
confusion (I already don't like the two Ubuntu templates for that reason
though those are at least entirely different in the way they work and so
vaguely make sense).

> Signed-off-by: Laurent Vivier <laurent at vivier.eu>
> ---
>  configure.ac                  |   1 +
>  templates/Makefile.am         |   3 +-
>  templates/lxc-cross-debian.in | 360 ++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 363 insertions(+), 1 deletion(-)
>  create mode 100755 templates/lxc-cross-debian.in
> 
> diff --git a/configure.ac b/configure.ac
> index 4c5f002..feb1771 100644
> --- a/configure.ac
> +++ b/configure.ac
> @@ -583,6 +583,7 @@ AC_CONFIG_FILES([
>  	templates/lxc-archlinux
>  	templates/lxc-alpine
>  	templates/lxc-plamo
> +	templates/lxc-cross-debian
>  
>  	src/Makefile
>  	src/lxc/Makefile
> diff --git a/templates/Makefile.am b/templates/Makefile.am
> index abdca6f..33939b4 100644
> --- a/templates/Makefile.am
> +++ b/templates/Makefile.am
> @@ -15,4 +15,5 @@ templates_SCRIPTS = \
>  	lxc-archlinux \
>  	lxc-alpine \
>  	lxc-cirros \
> -	lxc-plamo
> +	lxc-plamo \
> +	lxc-cross-debian
> diff --git a/templates/lxc-cross-debian.in b/templates/lxc-cross-debian.in
> new file mode 100755
> index 0000000..a12caa1
> --- /dev/null
> +++ b/templates/lxc-cross-debian.in
> @@ -0,0 +1,360 @@
> +#!/bin/bash
> +#
> +# Some parts from lxc-debian, Daniel Lezcano <daniel.lezcano at free.fr>
> +#
> +# Copy this script to /usr/share/lxc/templates
> +#
> +# and use it with 
> +# lxc-create -t cross-debian -n xxxx  -- --arch xxx --interpreter-path /a/b/c/qemu-xxx
> +#
> +
> +SUITE=${SUITE:-stable}
> +MIRROR=${MIRROR:-http://ftp.debian.org/debian}
> +
> +find_interpreter() {
> +    given_interpreter=$(basename "$1")
> +
> +    if [ ! -d /proc/sys/fs/binfmt_misc/ ] ; then
> +        return 1
> +    fi
> +    for file in /proc/sys/fs/binfmt_misc/* ; do
> +        if [ "$file" = "/proc/sys/fs/binfmt_misc/register" -o \
> +             "$file" = "/proc/sys/fs/binfmt_misc/status" ] ; then
> +            continue
> +        fi
> +        interpreter_path=$(sed -n "/^interpreter/s/interpreter \([^[:space:]]*\)/\1/p" "$file")
> +        interpreter=$(basename $interpreter_path)
> +         
> +        if [ "$given_interpreter" = "$interpreter" ] ; then
> +            echo "$interpreter_path"
> +            return 0
> +        fi
> +    done
> +    return 1
> +}
> +
> +download_debian()
> +{
> +    cache="$1"
> +    arch="$2"
> +
> +    if [ ! -d "$cache/archives-$SUITE-$arch" ]; then
> +        if ! mkdir -p "$cache/archives-$SUITE-$arch" ; then
> +            echo "Failed to create '$cache/archives-$SUITE-$arch' directory"
> +            return 1
> +        fi
> +    fi
> +
> +    echo "Downloading debian $SUITE $arch..."
> +    if ! debootstrap --download-only \
> +                     --no-check-gpg \
> +                     --arch=$arch \
> +                     --include="locales" \
> +                     ${SUITE} "$cache/archives-$SUITE-$arch" \
> +                     ${MIRROR} ; then
> +        echo "ERROR: failed to download to $cache/archives-$SUITE-$arch" 1>&2
> +        exit 1
> +    fi
> +    echo "Download complete."
> +    trap EXIT
> +    trap SIGINT
> +    trap SIGTERM
> +    trap SIGHUP
> +
> +    return 0
> +}
> +
> +copy_debian()
> +{
> +    cache=$1
> +    arch=$2
> +    rootfs=$3
> +
> +    echo -n "Copying rootfs to $rootfs..."
> +    mkdir -p $rootfs
> +    rsync -Ha "$cache/archives-$SUITE-$arch"/ $rootfs/ || return 1
> +    echo "Copy complete."
> +    return 0
> +}
> +
> +install_debian()
> +{
> +    cache="@LOCALSTATEDIR@/cache/lxc/debian"
> +    rootfs="$1"
> +    arch="$2"
> +
> +    mkdir -p @LOCALSTATEDIR@/lock/subsys/
> +    (
> +        if ! flock -x 200 ; then
> +            echo "Cache repository is busy."
> +            return 1
> +        fi
> +
> +        if ! download_debian $cache $arch ; then
> +            echo "Failed to download 'debian base'"
> +            return 1
> +        fi
> +
> +        if ! copy_debian $cache $arch $rootfs ; then
> +            echo "Failed to copy rootfs"
> +            return 1
> +        fi
> +
> +        return 0
> +
> +    ) 200>@LOCALSTATEDIR@/lock/subsys/lxc-cross-debian
> +
> +    return $?
> +}
> +
> +create_root() {
> +
> +    rootfs="$1"
> +    hostname="$2"
> +    interpreter="$3"
> +    arch="$4"
> +    interpreter_path="$5"
> +    include="$6"
> +
> +    if ! install_debian "$rootfs" "$arch" ; then
> +        echo "ERROR: failed to update cache" 1>&2
> +        exit 1
> +    fi
> +
> +    if [ "${include}" = "" ] ; then
> +      include="locales"
> +    else
> +      include="locales,${include}"
> +    fi
> +
> +    # Debian bootstrap
> +
> +    if ! debootstrap --no-check-gpg --foreign \
> +                     --arch=$arch \
> +                     --include="${include}" \
> +                     ${SUITE} "$rootfs" \
> +                     ${MIRROR} ; then
> +        echo "ERROR: failed to debootstrap to $rootfs" 1>&2
> +        exit 1
> +    fi
> +
> +    # adding interpreter binary
> +
> +    if ! cp "$interpreter" "$rootfs/$interpreter_path" ; then
> +        echo "ERROR: failed to copy $interpreter to $rootfs/$interpreter_path" 1>&2
> +        exit 1
> +    fi
> +
> +    # debian bootstrap second stage
> +
> +    chroot "$rootfs" debootstrap/debootstrap --second-stage
> +}
> +
> +configure_debian() {
> +
> +    rootfs="$1"
> +    hostname="$2"
> +    debian_sign="$3"
> +
> +    # set timezone
> +
> +    cat /etc/timezone > "$rootfs/etc/timezone"
> +    chroot $rootfs dpkg-reconfigure -fnoninteractive tzdata
> +
> +    # configuration
> +
> +    cat >> "$rootfs/etc/fstab" <<!EOF
> +# <file system> <mount point>   <type>  <options>       <dump>  <pass>
> +devpts		/dev/pts	devpts	nodev,noexec,nosuid 0	1
> +!EOF
> +
> +    echo "$hostname" > "$rootfs/etc/hostname"
> +    echo "c:2345:respawn:/sbin/getty 38400 console" >> "$rootfs/etc/inittab"
> +
> +    cat >> "$rootfs/etc/network/interfaces" <<!EOF
> +auto eth0
> +iface eth0 inet dhcp
> +!EOF
> +
> +    cat > "$rootfs/etc/apt/sources.list" <<!EOF
> +deb ${MIRROR} ${SUITE} main contrib non-free
> +#deb-src ${MIRROR} ${SUITE} main contrib non-free
> +!EOF
> +
> +    if [ "$debian_sign" != "" ]
> +    then
> +        HOME=/root chroot "$rootfs" gpg --keyserver pgpkeys.mit.edu --recv-key ${debian_sign}
> +        HOME=/root chroot "$rootfs" gpg -a --export ${debian_sign} | chroot "$rootfs"  apt-key add -
> +    fi
> +
> +    chroot "$rootfs" apt-get update
> +
> +    if [ -z "$LANG" ]; then
> +        echo "en_US.UTF-8 UTF-8" > "$rootfs/etc/locale.gen"
> +        chroot $rootfs locale-gen
> +        chroot $rootfs update-locale LANG=en_US.UTF-8
> +    else
> +        echo "$LANG $(echo $LANG | cut -d. -f2)" > "$rootfs/etc/locale.gen"
> +        chroot $rootfs locale-gen
> +        chroot $rootfs update-locale LANG=$LANG
> +    fi
> +
> +    # remove pointless services in a container
> +
> +    if [ -x "$rootfs/usr/sbin/update-rc.d" ] ; then
> +        chroot $rootfs /usr/sbin/update-rc.d -f checkroot.sh remove
> +        chroot $rootfs /usr/sbin/update-rc.d -f umountfs remove
> +        chroot $rootfs /usr/sbin/update-rc.d -f hwclock.sh remove
> +        chroot $rootfs /usr/sbin/update-rc.d -f hwclockfirst.sh remove
> +        chroot $rootfs /usr/sbin/update-rc.d -f module-init-tools remove
> +    fi
> +
> +    echo "root:root" | chroot $rootfs chpasswd
> +    echo "Root password is 'root', please change !"
> +}
> +
> +get_rootfs() {
> +    config="$1/config"
> +    rootfs=$(sed -n "s/^lxc.rootfs[[:space:]]*=[[:space:]]*\(.*\)/\1/p" $config)
> +    if [ "$rootfs" = "" ]
> +    then
> +        echo "$path/rootfs"
> +    else
> +        echo "$rootfs"
> +    fi
> +}
> +
> +create_lxc() {
> +    path="$1"
> +    rootfs="$2"
> +    hostname="$3"
> +
> +    grep -q "^lxc.rootfs" $path/config 2>/dev/null || echo "lxc.rootfs = $rootfs" >> "$path/config"
> +    cat >> "$path/config" <<!EOF
> +lxc.utsname = $hostname
> +
> +lxc.pts=1023
> +lxc.tty=12
> +
> +lxc.cgroup.devices.deny = a
> +lxc.cgroup.devices.allow = c 136:* rwm # pts
> +lxc.cgroup.devices.allow = c 254:0 rwm # rtc
> +lxc.cgroup.devices.allow = c 5:* rwm
> +lxc.cgroup.devices.allow = c 4:* rwm # ttyXX
> +lxc.cgroup.devices.allow = c 1:* rwm
> +lxc.cgroup.devices.allow = b 7:* rwm # loop
> +lxc.cgroup.devices.allow = b 1:* rwm # ram
> +
> +lxc.mount.entry=proc proc proc nodev,noexec,nosuid 0 0
> +lxc.mount.entry=sysfs sys sysfs defaults  0 0
> +
> +!EOF
> +    if [ $? -ne 0 ] ; then
> +        echo "ERROR: failed to create LXC configuration" 1>&2
> +        exit 1
> +    fi
> +}
> +
> +usage()
> +{
> +    cat <<!EOF
> +Usage: $1 --path PATH --name NAME --arch ARCH --interpreter-path QEMU
> +          [--mirror MIRROR][--suite SUITE]
> +
> +    --path is configuration path
> +    --name is container name
> +    --arch is debian architecture
> +    --interpreter-path is path to the interpreter to copy to rootfs
> +    --mirror is URL of debian mirror to use
> +    --suite is debian suite to install
> +    --include is the list of package to add to debootstrap
> +!EOF
> +}
> +
> +options=$(getopt -o hp:n:I:a:s:m:k:i: -l help,rootfs:,path:,name:,interpreter-path:,arch:,suite:,mirror:,deb-sign:,include: -- "$@")
> +if [ $? -ne 0 ]; then
> +        usage $(basename $0)
> +        exit 1
> +fi
> +eval set -- "$options"
> +
> +while true ; do
> +    case "$1" in
> +    -p|--path)
> +        shift
> +        path="$1"
> +        ;;
> +    --rootfs)
> +        shift
> +        rootfs="$1"
> +        ;;
> +    -n|--name)
> +        shift
> +        name="$1"
> +        ;;
> +    -a|--arch)
> +        shift
> +        arch="$1"
> +        ;;
> +    -I|--interpreter-path)
> +        shift
> +        interpreter="$1"
> +        ;;
> +    -s|--suite)
> +        shift
> +        SUITE="$1"
> +        ;;
> +    -m|--mirror)
> +        shift
> +        MIRROR="$1"
> +        ;;
> +    -i|--include)
> +        shift
> +        include="$1"
> +        ;;
> +    -k|--deb-sign)
> +        shift
> +        debian_sign="$1"
> +        ;;
> +    -h|--help)
> +        usage
> +        exit 1
> +        ;;
> +    *)
> +        break
> +        ;;
> +    esac
> +    shift
> +done
> +
> +if [ "$path" = "" -o "$name" = "" -o "$arch" = "" -o "$interpreter" = "" ] ; then
> +    echo "ERROR: missing parameter" 1>&2
> +    usage
> +    exit 1
> +fi
> +
> +if ! type debootstrap ; then
> +    echo "ERROR: 'debootstrap' command is missing" 1>&2
> +    exit 1
> +fi
> +
> +if ! file -b "${interpreter}" |grep -q "statically linked" ; then
> +    echo "ERROR: '${interpreter}' must be statically linked" 1>&2
> +    exit 1
> +fi
> +
> +interpreter_path=$(find_interpreter "$interpreter")
> +if [ $? -ne 0 ] ; then
> +    echo "ERROR: no binfmt interpreter using $(basename $interpreter)" 1>&2
> +    exit 1
> +fi
> +
> +if [ "$rootfs" = "" ] ; then
> +    rootfs=$(get_rootfs $path)
> +fi
> +
> +create_root "$rootfs" "$name" "$interpreter" "$arch" "$interpreter_path" "$include"
> +
> +configure_debian "$rootfs" "$name" "$debian_sign"
> +
> +create_lxc "$path" "$rootfs" "$name"
> -- 
> 1.8.3.2
> 
> _______________________________________________
> lxc-devel mailing list
> lxc-devel at lists.linuxcontainers.org
> http://lists.linuxcontainers.org/listinfo/lxc-devel

-- 
Stéphane Graber
Ubuntu developer
http://www.ubuntu.com
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 836 bytes
Desc: Digital signature
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20131225/642545c1/attachment.pgp>


More information about the lxc-devel mailing list