Created
August 29, 2015 22:17
-
-
Save ralt/492a09d9f9fea64fb28b to your computer and use it in GitHub Desktop.
LAMP template for lxc
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
# Fork of lxc-debian | |
# Highly opinionated. Updates libvirt's default network. | |
# The --ip argument is the last number of the IP | |
# The --mac argument is the last number of the MAC | |
# Detect use under userns (unsupported) | |
for arg in "$@"; do | |
[ "$arg" = "--" ] && break | |
if [ "$arg" = "--mapped-uid" -o "$arg" = "--mapped-gid" ]; then | |
echo "This template can't be used for unprivileged containers." 1>&2 | |
echo "You may want to try the \"download\" template instead." 1>&2 | |
exit 1 | |
fi | |
done | |
# Make sure the usual locations are in PATH | |
export PATH=$PATH:/usr/sbin:/usr/bin:/sbin:/bin | |
MIRROR=${MIRROR:-http://http.debian.net/debian} | |
SECURITY_MIRROR=${SECURITY_MIRROR:-http://security.debian.org/} | |
LOCALSTATEDIR="/var" | |
LXC_TEMPLATE_CONFIG="/usr/share/lxc/config" | |
configure_debian() | |
{ | |
rootfs=$1 | |
hostname=$2 | |
# squeeze only has /dev/tty and /dev/tty0 by default, | |
# therefore creating missing device nodes for tty1-4. | |
for tty in $(seq 1 4); do | |
if [ ! -e $rootfs/dev/tty$tty ]; then | |
mknod $rootfs/dev/tty$tty c 4 $tty | |
fi | |
done | |
# configure the inittab | |
cat <<EOF > $rootfs/etc/inittab | |
id:3:initdefault: | |
si::sysinit:/etc/init.d/rcS | |
l0:0:wait:/etc/init.d/rc 0 | |
l1:1:wait:/etc/init.d/rc 1 | |
l2:2:wait:/etc/init.d/rc 2 | |
l3:3:wait:/etc/init.d/rc 3 | |
l4:4:wait:/etc/init.d/rc 4 | |
l5:5:wait:/etc/init.d/rc 5 | |
l6:6:wait:/etc/init.d/rc 6 | |
# Normally not reached, but fallthrough in case of emergency. | |
z6:6:respawn:/sbin/sulogin | |
1:2345:respawn:/sbin/getty 38400 console | |
c1:12345:respawn:/sbin/getty 38400 tty1 linux | |
c2:12345:respawn:/sbin/getty 38400 tty2 linux | |
c3:12345:respawn:/sbin/getty 38400 tty3 linux | |
c4:12345:respawn:/sbin/getty 38400 tty4 linux | |
p6::ctrlaltdel:/sbin/init 6 | |
p0::powerfail:/sbin/init 0 | |
EOF | |
# symlink mtab | |
[ -e "$rootfs/etc/mtab" ] && rm $rootfs/etc/mtab | |
ln -s /proc/self/mounts $rootfs/etc/mtab | |
# disable selinux in debian | |
mkdir -p $rootfs/selinux | |
echo 0 > $rootfs/selinux/enforce | |
# configure the network using the dhcp | |
cat <<EOF > $rootfs/etc/network/interfaces | |
auto lo | |
iface lo inet loopback | |
auto eth0 | |
iface eth0 inet dhcp | |
EOF | |
# set the hostname | |
cat <<EOF > $rootfs/etc/hostname | |
$hostname | |
EOF | |
# reconfigure some services | |
if [ -z "$LANG" ]; then | |
chroot $rootfs locale-gen en_US.UTF-8 UTF-8 | |
chroot $rootfs update-locale LANG=en_US.UTF-8 | |
else | |
encoding=$(echo $LANG | cut -d. -f2) | |
chroot $rootfs sed -e "s/^# \(${LANG} ${encoding}\)/\1/" \ | |
-i /etc/locale.gen 2> /dev/null | |
chroot $rootfs locale-gen $LANG $encoding | |
chroot $rootfs update-locale LANG=$LANG | |
fi | |
# remove pointless services in a container | |
chroot $rootfs /usr/sbin/update-rc.d -f checkroot.sh disable | |
chroot $rootfs /usr/sbin/update-rc.d -f umountfs disable | |
chroot $rootfs /usr/sbin/update-rc.d -f hwclock.sh disable | |
chroot $rootfs /usr/sbin/update-rc.d -f hwclockfirst.sh disable | |
# generate new SSH keys | |
if [ -x $rootfs/var/lib/dpkg/info/openssh-server.postinst ]; then | |
cat > $rootfs/usr/sbin/policy-rc.d << EOF | |
#!/bin/sh | |
exit 101 | |
EOF | |
chmod +x $rootfs/usr/sbin/policy-rc.d | |
if [ -f $rootfs/etc/init/ssh.conf ]; then | |
mv $rootfs/etc/init/ssh.conf $rootfs/etc/init/ssh.conf.disabled | |
fi | |
rm -f $rootfs/etc/ssh/ssh_host_*key* | |
DPKG_MAINTSCRIPT_PACKAGE=openssh DPKG_MAINTSCRIPT_NAME=postinst chroot $rootfs /var/lib/dpkg/info/openssh-server.postinst configure | |
sed -i "s/root@$(hostname)/root@$hostname/g" $rootfs/etc/ssh/ssh_host_*.pub | |
# Don't allow root login with password | |
sed -i "s/PermitRootLogin yes/PermitRootLogin without-password/" $rootfs/etc/ssh/sshd_config | |
if [ -f "$rootfs/etc/init/ssh.conf.disabled" ]; then | |
mv $rootfs/etc/init/ssh.conf.disabled $rootfs/etc/init/ssh.conf | |
fi | |
rm -f $rootfs/usr/sbin/policy-rc.d | |
fi | |
# set initial timezone as on host | |
if [ -f /etc/timezone ]; then | |
cat /etc/timezone > $rootfs/etc/timezone | |
chroot $rootfs dpkg-reconfigure -f noninteractive tzdata | |
elif [ -f /etc/sysconfig/clock ]; then | |
. /etc/sysconfig/clock | |
echo $ZONE > $rootfs/etc/timezone | |
chroot $rootfs dpkg-reconfigure -f noninteractive tzdata | |
else | |
echo "Timezone in container is not configured. Adjust it manually." | |
fi | |
## LAMP installation. | |
virsh net-update default add ip-dhcp-host \ | |
"<host mac='00:FF:AA:00:00:${mac}' name='${hostname}.lxc' ip='192.168.122.${ip}'/>" \ | |
--live --config | |
echo "192.168.122.${ip} ${hostname}.lxc" >> /etc/hosts | |
cat >> /var/lib/lxc/${hostname}/config <<EOF | |
lxc.network.type = veth | |
lxc.network.flags = up | |
lxc.network.link = virbr0 | |
lxc.network.ipv4 = 0.0.0.0/24 | |
lxc.network.hwaddr = 00:FF:AA:00:00:${mac} | |
EOF | |
chroot $rootfs apt-get update | |
chroot $rootfs apt-get install -y \ | |
apache2 \ | |
php5 \ | |
libapache2-mod-php5 \ | |
php5-xdebug \ | |
php5-gd \ | |
php5-mysql \ | |
unzip \ | |
git | |
chroot $rootfs php -r "readfile('https://getcomposer.org/installer');" | chroot $rootfs php | |
chroot $rootfs mv /composer.phar /usr/bin/composer | |
chroot $rootfs composer global require platformsh/cli:@stable | |
echo "root:root" | chroot $rootfs chpasswd | |
echo -e "\nRoot password is 'root'" | |
return 0 | |
} | |
write_sourceslist() | |
{ | |
local rootfs="$1"; shift | |
local release="$1"; shift | |
local arch="$1"; shift | |
local prefix="deb" | |
if [ -n "${arch}" ]; then | |
prefix="deb [arch=${arch}]" | |
fi | |
cat >> "${rootfs}/etc/apt/sources.list" << EOF | |
${prefix} $MIRROR ${release} main contrib non-free | |
${prefix} $SECURITY_MIRROR ${release}/updates main contrib non-free | |
EOF | |
} | |
configure_debian_systemd() | |
{ | |
path=$1 | |
rootfs=$2 | |
init="$(chroot ${rootfs} dpkg-query --search /sbin/init | cut -d : -f 1)" | |
if [ "$init" = "systemd-sysv" ]; then | |
# only appropriate when systemd is PID 1 | |
echo 'lxc.autodev = 1' >> "$path/config" | |
echo 'lxc.kmsg = 0' >> "$path/config" | |
fi | |
# this only works if we have [email protected] to manipulate | |
if [ -f ${rootfs}/lib/systemd/system/getty\@.service ]; then | |
sed -e 's/^ConditionPathExists=/# ConditionPathExists=/' \ | |
-e 's/After=dev-%i.device/After=/' \ | |
< ${rootfs}/lib/systemd/system/getty\@.service \ | |
> ${rootfs}/etc/systemd/system/getty\@.service | |
fi | |
# just in case systemd is not installed | |
mkdir -p ${rootfs}/{lib,etc}/systemd/system | |
mkdir -p ${rootfs}/etc/systemd/system/getty.target.wants | |
# Fix getty-static-service as debootstrap does not install dbus | |
if [ -e $rootfs//lib/systemd/system/getty-static.service ] ; then | |
sed 's/ getty@tty[5-9].service//g' $rootfs/lib/systemd/system/getty-static.service | sed 's/\(tty2-tty\)[5-9]/\14/g' > $rootfs/etc/systemd/system/getty-static.service | |
fi | |
# This function has been copied and adapted from lxc-fedora | |
rm -f ${rootfs}/etc/systemd/system/default.target | |
touch ${rootfs}/etc/fstab | |
chroot ${rootfs} ln -s /dev/null /etc/systemd/system/udev.service | |
chroot ${rootfs} ln -s /dev/null /etc/systemd/system/systemd-udevd.service | |
chroot ${rootfs} ln -s /lib/systemd/system/multi-user.target /etc/systemd/system/default.target | |
# Make systemd honor SIGPWR | |
chroot ${rootfs} ln -s /lib/systemd/system/halt.target /etc/systemd/system/sigpwr.target | |
# Setup getty service on the 4 ttys we are going to allow in the | |
# default config. Number should match lxc.tty | |
( cd ${rootfs}/etc/systemd/system/getty.target.wants | |
for i in 1 2 3 4 ; do ln -sf ../getty\@.service getty@tty${i}.service; done ) | |
return 0 | |
} | |
cleanup() | |
{ | |
rm -rf $cache/partial-$release-$arch | |
rm -rf $cache/rootfs-$release-$arch | |
} | |
download_debian() | |
{ | |
packages=\ | |
ifupdown,\ | |
locales,\ | |
libui-dialog-perl,\ | |
dialog,\ | |
isc-dhcp-client,\ | |
netbase,\ | |
net-tools,\ | |
iproute,\ | |
openssh-server | |
cache=$1 | |
arch=$2 | |
release=$3 | |
trap cleanup EXIT SIGHUP SIGINT SIGTERM | |
# check the mini debian was not already downloaded | |
mkdir -p "$cache/partial-$release-$arch" | |
if [ $? -ne 0 ]; then | |
echo "Failed to create '$cache/partial-$release-$arch' directory" | |
return 1 | |
fi | |
# download a mini debian into a cache | |
echo "Downloading debian minimal ..." | |
debootstrap --verbose --variant=minbase --arch=$arch \ | |
--include=$packages \ | |
"$release" "$cache/partial-$release-$arch" $MIRROR | |
if [ $? -ne 0 ]; then | |
echo "Failed to download the rootfs, aborting." | |
return 1 | |
fi | |
mv "$1/partial-$release-$arch" "$1/rootfs-$release-$arch" | |
echo "Download complete." | |
trap EXIT | |
trap SIGINT | |
trap SIGTERM | |
trap SIGHUP | |
return 0 | |
} | |
copy_debian() | |
{ | |
cache=$1 | |
arch=$2 | |
rootfs=$3 | |
release=$4 | |
# make a local copy of the minidebian | |
echo -n "Copying rootfs to $rootfs..." | |
mkdir -p $rootfs | |
rsync -Ha "$cache/rootfs-$release-$arch"/ $rootfs/ || return 1 | |
return 0 | |
} | |
install_debian() | |
{ | |
cache="$LOCALSTATEDIR/cache/lxc/debian" | |
rootfs=$1 | |
release=$2 | |
arch=$3 | |
mkdir -p $LOCALSTATEDIR/lock/subsys/ | |
( | |
flock -x 9 | |
if [ $? -ne 0 ]; then | |
echo "Cache repository is busy." | |
return 1 | |
fi | |
echo "Checking cache download in $cache/rootfs-$release-$arch ... " | |
if [ ! -e "$cache/rootfs-$release-$arch" ]; then | |
download_debian $cache $arch $release | |
if [ $? -ne 0 ]; then | |
echo "Failed to download 'debian base'" | |
return 1 | |
fi | |
fi | |
copy_debian $cache $arch $rootfs $release | |
if [ $? -ne 0 ]; then | |
echo "Failed to copy rootfs" | |
return 1 | |
fi | |
return 0 | |
) 9>$LOCALSTATEDIR/lock/subsys/lxc-debian | |
return $? | |
} | |
copy_configuration() | |
{ | |
path=$1 | |
rootfs=$2 | |
hostname=$3 | |
arch=$4 | |
# Generate the configuration file | |
## Create the fstab (empty by default) | |
touch $path/fstab | |
# if there is exactly one veth network entry, make sure it has an | |
# associated hwaddr. | |
nics=`grep -e '^lxc\.network\.type[ \t]*=[ \t]*veth' $path/config | wc -l` | |
if [ $nics -eq 1 ]; then | |
grep -q "^lxc.network.hwaddr" $path/config || sed -i -e "/^lxc\.network\.type[ \t]*=[ \t]*veth/a lxc.network.hwaddr = 00:16:3e:$(openssl rand -hex 3| sed 's/\(..\)/\1:/g; s/.$//')" $path/config | |
fi | |
## Add all the includes | |
echo "" >> $path/config | |
echo "# Common configuration" >> $path/config | |
if [ -e "${LXC_TEMPLATE_CONFIG}/debian.common.conf" ]; then | |
echo "lxc.include = ${LXC_TEMPLATE_CONFIG}/debian.common.conf" >> $path/config | |
fi | |
if [ -e "${LXC_TEMPLATE_CONFIG}/debian.${release}.conf" ]; then | |
echo "lxc.include = ${LXC_TEMPLATE_CONFIG}/debian.${release}.conf" >> $path/config | |
fi | |
## Add the container-specific config | |
echo "" >> $path/config | |
echo "# Container specific configuration" >> $path/config | |
grep -q "^lxc.rootfs" $path/config 2> /dev/null || echo "lxc.rootfs = $rootfs" >> $path/config | |
cat <<EOF >> $path/config | |
lxc.mount = $path/fstab | |
lxc.utsname = $hostname | |
lxc.arch = $arch | |
EOF | |
if [ $? -ne 0 ]; then | |
echo "Failed to add configuration" | |
return 1 | |
fi | |
return 0 | |
} | |
post_process() | |
{ | |
local rootfs="$1"; shift | |
local release="$1"; shift | |
local arch="$1"; shift | |
local hostarch="$1"; shift | |
# Disable service startup | |
cat > ${rootfs}/usr/sbin/policy-rc.d << EOF | |
#!/bin/sh | |
exit 101 | |
EOF | |
chmod +x ${rootfs}/usr/sbin/policy-rc.d | |
# If the container isn't running a native architecture, setup multiarch | |
if [ "${arch}" != "${hostarch}" ]; then | |
mkdir -p ${rootfs}/etc/dpkg/dpkg.cfg.d | |
echo "foreign-architecture ${hostarch}" > ${rootfs}/etc/dpkg/dpkg.cfg.d/lxc-multiarch | |
fi | |
# Write a new sources.list containing both native and multiarch entries | |
> ${rootfs}/etc/apt/sources.list | |
if [ "${arch}" = "${hostarch}" ]; then | |
write_sourceslist ${rootfs} ${release} ${arch} | |
else | |
write_sourceslist ${rootfs} ${release} | |
fi | |
# Re-enable service startup | |
rm ${rootfs}/usr/sbin/policy-rc.d | |
} | |
clean() | |
{ | |
cache="$LOCALSTATEDIR/cache/lxc/debian" | |
if [ ! -e $cache ]; then | |
exit 0 | |
fi | |
# lock, so we won't purge while someone is creating a repository | |
( | |
flock -x 9 | |
if [ $? != 0 ]; then | |
echo "Cache repository is busy." | |
exit 1 | |
fi | |
echo -n "Purging the download cache..." | |
rm --preserve-root --one-file-system -rf $cache && echo "Done." || exit 1 | |
exit 0 | |
) 9>$LOCALSTATEDIR/lock/subsys/lxc-debian | |
} | |
usage() | |
{ | |
cat <<EOF | |
$1 -h|--help -p|--path=<path> [-a|--arch] [-c|--clean] [--mirror=<mirror>] [-r|--release=<release>] [--security-mirror=<security mirror>] | |
arch: the container architecture (e.g. amd64): defaults to host arch | |
release: the debian release (e.g. wheezy): defaults to current stable | |
mirror: debain mirror to use during installation | |
security mirror: debain mirror to use for security updates | |
EOF | |
return 0 | |
} | |
options=$(getopt -o hp:n:a:r:c:i:m: -l arch:,clean,help,mirror:,name:,path:,release:,rootfs:,security-mirror:,ip:,mac: -- "$@") | |
if [ $? -ne 0 ]; then | |
usage $(basename $0) | |
exit 1 | |
fi | |
eval set -- "$options" | |
if which dpkg > /dev/null 2>&1 ; then | |
arch=$(dpkg --print-architecture) | |
else | |
arch=$(uname -m) | |
if [ "$arch" = "i686" ]; then | |
arch="i386" | |
elif [ "$arch" = "x86_64" ]; then | |
arch="amd64" | |
elif [ "$arch" = "armv7l" ]; then | |
arch="armhf" | |
fi | |
fi | |
hostarch=$arch | |
while true | |
do | |
case "$1" in | |
-h|--help) usage $0 && exit 1;; | |
--) shift 1; break ;; | |
-a|--arch) arch=$2; shift 2;; | |
-c|--clean) clean=$2; shift 1;; | |
--mirror) MIRROR=$2; shift 2;; | |
-n|--name) name=$2; shift 2;; | |
-p|--path) path=$2; shift 2;; | |
-i|--ip) ip=$2; shift 2;; | |
-m|--mac) mac=$2; shift 2;; | |
-r|--release) release=$2; shift 2;; | |
--rootfs) rootfs=$2; shift 2;; | |
--security-mirror) SECURITY_MIRROR=$2; shift 2;; | |
*) break ;; | |
esac | |
done | |
if [ -z "$ip" ]; then | |
echo "ip required" | |
exit 1 | |
fi | |
if [ -z "$mac" ]; then | |
echo "mac required" | |
exit 1 | |
fi | |
if [ ! -z "$clean" -a -z "$path" ]; then | |
clean || exit 1 | |
exit 0 | |
fi | |
if [ "$arch" = "i686" ]; then | |
arch=i386 | |
fi | |
if [ "$arch" = "x86_64" ]; then | |
arch=amd64 | |
fi | |
if [ $hostarch = "i386" -a $arch = "amd64" ]; then | |
echo "can't create $arch container on $hostarch" | |
exit 1 | |
fi | |
if [ $hostarch = "armhf" -o $hostarch = "armel" ] && \ | |
[ $arch != "armhf" -a $arch != "armel" ]; then | |
echo "can't create $arch container on $hostarch" | |
exit 1 | |
fi | |
if [ $hostarch = "powerpc" -a $arch != "powerpc" ]; then | |
echo "can't create $arch container on $hostarch" | |
exit 1 | |
fi | |
type debootstrap | |
if [ $? -ne 0 ]; then | |
echo "'debootstrap' command is missing" | |
exit 1 | |
fi | |
if [ -z "$path" ]; then | |
echo "'path' parameter is required" | |
exit 1 | |
fi | |
if [ "$(id -u)" != "0" ]; then | |
echo "This script should be run as 'root'" | |
exit 1 | |
fi | |
current_release=`wget ${MIRROR}/dists/stable/Release -O - 2> /dev/null | head |awk '/^Codename: (.*)$/ { print $2; }'` | |
release=${release:-${current_release}} | |
valid_releases=('squeeze' 'wheezy' 'jessie' 'sid') | |
if [[ ! "${valid_releases[*]}" =~ (^|[^[:alpha:]])$release([^[:alpha:]]|$) ]]; then | |
echo "Invalid release ${release}, valid ones are: ${valid_releases[*]}" | |
exit 1 | |
fi | |
# detect rootfs | |
config="$path/config" | |
if [ -z "$rootfs" ]; then | |
if grep -q '^lxc.rootfs' $config 2> /dev/null ; then | |
rootfs=$(awk -F= '/^lxc.rootfs =/{ print $2 }' $config) | |
else | |
rootfs=$path/rootfs | |
fi | |
fi | |
install_debian $rootfs $release $arch | |
if [ $? -ne 0 ]; then | |
echo "failed to install debian" | |
exit 1 | |
fi | |
configure_debian $rootfs $name | |
if [ $? -ne 0 ]; then | |
echo "failed to configure debian for a container" | |
exit 1 | |
fi | |
copy_configuration $path $rootfs $name $arch | |
if [ $? -ne 0 ]; then | |
echo "failed write configuration file" | |
exit 1 | |
fi | |
configure_debian_systemd $path $rootfs | |
post_process ${rootfs} ${release} ${arch} ${hostarch} | |
if [ ! -z $clean ]; then | |
clean || exit 1 | |
exit 0 | |
fi |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment