Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
LXC CentOS template. Work with Ubuntu if the yum package is installed
#!/bin/bash
#
# template script for generating CentOS container for LXC
#
#
# lxc: linux Container library
# Authors:
# Daniel Lezcano <daniel.lezcano@free.fr>
# Ramez Hanna <rhanna@informatiq.org>
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#Configurations
arch=$(arch)
release=6
cache_base=/var/cache/lxc/centos/$arch
default_path=/var/lib/lxc
root_password=password
# is this centos?
[ -f /etc/redhat-release ] && is_centos=true
if [ "$arch" = "i686" ]; then
arch=i386
fi
configure_centos()
{
# disable selinux in centos
mkdir -p $rootfs_path/selinux
echo 0 > $rootfs_path/selinux/enforce
# configure the network using the dhcp
cat <<EOF > ${rootfs_path}/etc/sysconfig/network-scripts/ifcfg-eth0
DEVICE=eth0
BOOTPROTO=dhcp
ONBOOT=yes
TYPE=Ethernet
USERCTL=yes
PEERDNS=yes
IPV6INIT=no
DHCP_HOSTNAME="$(if [ -x /etc/hostname ] && [ ! -z `cat /etc/hostname` ] ; then cat /etc/hostname ; else hostname ; fi )"
EOF
# set the dns
cat > ${rootfs_path}/etc/resolv.conf << END
# Google public DNS
nameserver 8.8.8.8
nameserver 8.8.4.4
END
# set the hostname
cat <<EOF > ${rootfs_path}/etc/sysconfig/network
NETWORKING=yes
HOSTNAME=${name}
EOF
# set minimal hosts
cat <<EOF > $rootfs_path/etc/hosts
127.0.0.1 localhost $name
::1 localhost localhost.localdomain localhost6 localhost6.localdomain6
EOF
sed -i 's|.sbin.start_udev||' ${rootfs_path}/etc/rc.sysinit
sed -i 's|.sbin.start_udev||' ${rootfs_path}/etc/rc.d/rc.sysinit
# don't mount devpts, for pete's sake
sed -i 's/^.*dev.pts.*$/#\0/' ${rootfs_path}/etc/rc.sysinit
sed -i 's/^.*dev.pts.*$/#\0/' ${rootfs_path}/etc/rc.d/rc.sysinit
sed -i 's/^.*dev.pts.*$/#\0/' ${rootfs_path}/etc/rc.d/rc.sysinit
sed -i 's/^.*dev.pts.*$/#\0/' ${rootfs_path}/etc/rc.d/rc.sysinit
sed -i 's/^.*dev.pts.*$/#\0/' ${rootfs_path}/etc/rc.d/rc.sysinit
sed -i 's/^#baseurl/baseurl/g' ${rootfs_path}/etc/yum.repos.d/CentOS-Base.repo
sed -i "s/\$releasever/$release.$releaseminor/g" ${rootfs_path}/etc/yum.repos.d/CentOS-Base.repo
sed -i '115,126s/^/#/' ${rootfs_path}/etc/rc.d/init.d/halt
chroot ${rootfs_path} chkconfig udev-post off
chroot ${rootfs_path} chkconfig network on
dev_path="${rootfs_path}/dev"
rm -rf $dev_path
mkdir -p $dev_path
mknod -m 666 ${dev_path}/null c 1 3
mknod -m 666 ${dev_path}/zero c 1 5
mknod -m 666 ${dev_path}/random c 1 8
mknod -m 666 ${dev_path}/urandom c 1 9
mkdir -m 755 ${dev_path}/pts
mkdir -m 1777 ${dev_path}/shm
mknod -m 666 ${dev_path}/tty c 5 0
mknod -m 666 ${dev_path}/tty0 c 4 0
mknod -m 666 ${dev_path}/tty1 c 4 1
mknod -m 666 ${dev_path}/tty2 c 4 2
mknod -m 666 ${dev_path}/tty3 c 4 3
mknod -m 666 ${dev_path}/tty4 c 4 4
mknod -m 600 ${dev_path}/console c 5 1
mknod -m 666 ${dev_path}/full c 1 7
mknod -m 600 ${dev_path}/initctl p
mknod -m 666 ${dev_path}/ptmx c 5 2
echo "setting root passwd to $root_password"
echo "root:$root_password" | chroot $rootfs_path chpasswd
# specifying this in the initial packages doesn't always work.
# echo "installing centos-release package"
# if [ ! -f ${rootfs_path}/etc/resolv.conf ]; then
# cp /etc/resolv.conf ${rootfs_path}/etc/resolv.conf
# remove_resolv=1
# fi
# chroot ${rootfs_path} rpm --rebuilddb
# chroot ${rootfs_path} yum --releasever=${release} -y install centos-release
# if [ ! -z $remove_resolv ]; then
# rm ${rootfs_path}/etc/resolv.conf
# fi
# silence some needless startup errors
touch ${rootfs_path}/etc/fstab
# give us a console on /dev/console
sed -i 's/ACTIVE_CONSOLES=.*$/ACTIVE_CONSOLES="\/dev\/console \/dev\/tty[1-4]"/' \
${rootfs_path}/etc/sysconfig/init
return 0
}
download_centos()
{
# check the mini centos was not already downloaded
INSTALL_ROOT=$cache/partial
mkdir -p $INSTALL_ROOT
if [ $? -ne 0 ]; then
echo "Failed to create '$INSTALL_ROOT' directory"
return 1
fi
# download a mini centos into a cache
echo "Downloading centos minimal ..."
YUM="yum --installroot $INSTALL_ROOT -y --nogpgcheck"
PKG_LIST="yum initscripts passwd rsyslog vim-minimal dhclient chkconfig"
PKG_LIST="$PKG_LIST rootfiles policycoreutils centos-release openssh-server avahi"
PKG_LIST="$PKG_LIST openssh-clients sudo plymouth"
MIRRORLIST_URL="http://mirrorlist.centos.org/?release=$release&arch=$arch&repo=os"
DOWNLOAD_OK=no
for trynumber in 1 2 3; do
[ $trynumber != 1 ] && echo "Trying again..."
MIRROR_URL=$(curl -s -S -f "$MIRRORLIST_URL" | head -n2 | tail -n1)
if [ $? -ne 0 ] || [ -z "$MIRROR_URL" ]; then
echo "Failed to get a mirror"
continue
fi
RELEASE_URL="$MIRROR_URL/Packages/centos-release-$release-$releaseminor.el6.centos.10.$arch.rpm"
echo "Fetching from $RELEASE_URL"
curl -f "$RELEASE_URL" > $INSTALL_ROOT/centos-release-$release-$releaseminor.centos.$arch.rpm
if [ $? -ne 0 ]; then
echo "Failed to download centos release rpm"
continue
fi
DOWNLOAD_OK=yes
break
done
if [ $DOWNLOAD_OK != yes ]; then
echo "Aborting"
return 1
fi
mkdir -p $INSTALL_ROOT/var/lib/rpm
rpm --root $INSTALL_ROOT --initdb
rpm --root $INSTALL_ROOT -ivh $INSTALL_ROOT/centos-release-$release-$releaseminor.centos.$arch.rpm
$YUM install $PKG_LIST
chroot $INSTALL_ROOT rm -f /var/lib/rpm/__*
chroot $INSTALL_ROOT rpm --rebuilddb
if [ $? -ne 0 ]; then
echo "Failed to download the rootfs, aborting."
return 1
fi
mv "$INSTALL_ROOT" "$cache/rootfs"
echo "Download complete."
return 0
}
copy_centos()
{
# make a local copy of the minicentos
echo -n "Copying rootfs to $rootfs_path ..."
#cp -a $cache/rootfs-$arch $rootfs_path || return 1
# i prefer rsync (no reason really)
mkdir -p $rootfs_path
rsync -a $cache/rootfs/ $rootfs_path/
return 0
}
update_centos()
{
# chroot $cache/rootfs yum -y update
echo "Updates disabled"
}
install_centos()
{
mkdir -p /var/lock/subsys/
(
flock -x 200
if [ $? -ne 0 ]; then
echo "Cache repository is busy."
return 1
fi
echo "Checking cache download in $cache/rootfs ... "
if [ ! -e "$cache/rootfs" ]; then
download_centos
if [ $? -ne 0 ]; then
echo "Failed to download 'centos base'"
return 1
fi
else
echo "Cache found. Updating..."
update_centos
if [ $? -ne 0 ]; then
echo "Failed to update 'centos base', continuing with last known good cache"
else
echo "Update finished"
fi
fi
echo "Copy $cache/rootfs to $rootfs_path ... "
copy_centos
if [ $? -ne 0 ]; then
echo "Failed to copy rootfs"
return 1
fi
return 0
) 200>/var/lock/subsys/lxc
return $?
}
copy_configuration()
{
mkdir -p $config_path
cat <<EOF >> $config_path/config
lxc.utsname = $name
lxc.tty = 4
lxc.pts = 1024
lxc.rootfs = $rootfs_path
lxc.mount = $config_path/fstab
# uncomment the next line to run the container unconfined:
#lxc.aa_profile = unconfined
#cgroups
lxc.cgroup.devices.deny = a
# /dev/null and zero
lxc.cgroup.devices.allow = c 1:3 rwm
lxc.cgroup.devices.allow = c 1:5 rwm
# consoles
lxc.cgroup.devices.allow = c 5:1 rwm
lxc.cgroup.devices.allow = c 5:0 rwm
lxc.cgroup.devices.allow = c 4:0 rwm
lxc.cgroup.devices.allow = c 4:1 rwm
# /dev/{,u}random
lxc.cgroup.devices.allow = c 1:9 rwm
lxc.cgroup.devices.allow = c 1:8 rwm
lxc.cgroup.devices.allow = c 136:* rwm
lxc.cgroup.devices.allow = c 5:2 rwm
# rtc
lxc.cgroup.devices.allow = c 254:0 rwm
EOF
cat <<EOF > $config_path/fstab
proc proc proc nodev,noexec,nosuid 0 0
sysfs sys sysfs defaults 0 0
EOF
if [ $? -ne 0 ]; then
echo "Failed to add configuration"
return 1
fi
return 0
}
clean()
{
if [ ! -e $cache ]; then
exit 0
fi
# lock, so we won't purge while someone is creating a repository
(
flock -x 200
if [ $? != 0 ]; then
echo "Cache repository is busy."
exit 1
fi
echo -n "Purging the download cache for Centos-$release-$releaseminor..."
rm --preserve-root --one-file-system -rf $cache && echo "Done." || exit 1
exit 0
) 200>/var/lock/subsys/lxc
}
usage()
{
cat <<EOF
usage:
$1 -n|--name=<container_name>
[-p|--path=<path>] [-c|--clean] [-R|--release=<Centos_release>] [-A|--arch=<arch of the container>]
[-h|--help]
Mandatory args:
-n,--name container name, used to as an identifier for that container from now on
Optional args:
-p,--path path to where the container rootfs will be created, defaults to /var/lib/lxc. The container config will go under /var/lib/lxc in that case
-c,--clean clean the cache
-R,--release Centos release for the new container. if the host is Fedora, then it will defaultto the host's release.
-m,--release-minor Minor release number for the new containar
-A,--arch NOT USED YET. Define what arch the container will be [i686,x86_64]
-h,--help print this help
EOF
return 0
}
options=$(getopt -o hp:n:cR:m: -l help,path:,name:,clean,release:,releaseminor: -- "$@")
if [ $? -ne 0 ]; then
usage $(basename $0)
exit 1
fi
eval set -- "$options"
while true
do
case "$1" in
-h|--help) usage $0 && exit 0;;
-p|--path) path=$2; shift 2;;
-n|--name) name=$2; shift 2;;
-c|--clean) clean=$2; shift 2;;
-R|--release) release=$2; shift 2;;
-m|--releaseminor) releaseminor=$2; shift 2;;
--) shift 1; break ;;
*) break ;;
esac
done
if [ ! -z "$clean" -a -z "$path" ]; then
clean || exit 1
exit 0
fi
needed_pkgs=""
type yum >/dev/null 2>&1
if [ $? -ne 0 ]; then
needed_pkgs="yum $needed_pkgs"
fi
type curl >/dev/null 2>&1
if [ $? -ne 0 ]; then
needed_pkgs="curl $needed_pkgs"
fi
if [ -n "$needed_pkgs" ]; then
echo "Missing commands: $needed_pkgs"
echo "Please install these using \"sudo apt-get install $needed_pkgs\""
exit 1
fi
if [ -z "$path" ]; then
path=$default_path
fi
if [ -z "$release" ]; then
if [ "$is_centos" ]; then
release=$(cat /etc/centos-release |awk '/^Fedora/ {print $3}')
else
echo "This is not a centos host and release missing, defaulting to 6.4. use -R|--release to specify release"
release=6
fi
fi
if [ -z "$releaseminor" ]; then
releaseminor=4
fi
if [ "$(id -u)" != "0" ]; then
echo "This script should be run as 'root'"
exit 1
fi
rootfs_path=$path/rootfs
config_path=$default_path/$name
cache=$cache_base/$release
revert()
{
echo "Interrupted, so cleaning up"
lxc-destroy -n $name
# maybe was interrupted before copy config
rm -rf $path/$name
rm -rf $default_path/$name
echo "exiting..."
exit 1
}
trap revert SIGHUP SIGINT SIGTERM
copy_configuration
if [ $? -ne 0 ]; then
echo "failed write configuration file"
exit 1
fi
install_centos
if [ $? -ne 0 ]; then
echo "failed to install centos"
exit 1
fi
configure_centos
if [ $? -ne 0 ]; then
echo "failed to configure centos for a container"
exit 1
fi
if [ ! -z $clean ]; then
clean || exit 1
exit 0
fi
echo "container rootfs and config created"
@dewrit

This comment has been minimized.

Copy link

dewrit commented Jan 22, 2013

centos-release-6-3.el6.centos.9.i386.rpm gives error 404
because i386 arch package no more exist so fetching error.

@ville761

This comment has been minimized.

Copy link

ville761 commented Mar 5, 2013

The script seems to overwrite the host's resolv.conf file. I just commented that part out, as the dhclient-script will generate the file in the container anyway. Then it works fine on Ubuntu (if using lxcbr0).

@bryanwb

This comment has been minimized.

Copy link

bryanwb commented Apr 19, 2013

i recommend making the following changes

at https://gist.github.com/hagix9/3514296#file-lxc-centos-L57 add

    cat <<"EOF" > ${rootfs_path}/etc/sysconfig/network-scripts/ifcfg-eth0
DEVICE=eth0
BOOTPROTO=dhcp
ONBOOT=yes
TYPE=Ethernet
USERCTL=yes
PEERDNS=yes
IPV6INIT=no
DHCP_HOSTNAME="$(if [ -x /etc/hostname ] && [ ! -z `cat /etc/hostname` ] ; then cat /etc/hostname ; else hostname ; fi )"
EOF

this way you can discover the new ip by checking /var/lib/misc/dnsmasq.leases

and install the extra packages
openssh-clients openssl sudo curl

@hagix9

This comment has been minimized.

Copy link
Owner Author

hagix9 commented May 1, 2013

Thank you
It has been changed to incorporate the opinions of everyone

@tenforward

This comment has been minimized.

Copy link

tenforward commented May 22, 2013

It is better to append '-L' option for curl command (ex. Line 172), because a mirror server may return code 301 (, 302, or 303).

curl -f -L "$RELEASE_URL" > $INSTALL_ROOT/centos-release-$release-$releaseminor.centos.$arch.rpm
@peterhoeg

This comment has been minimized.

Copy link

peterhoeg commented Aug 19, 2013

I've tried this template but it looks like the rpm db is not being rebuilt correctly. 'rpm -qa' gives no output as rpm thinks nothing is installed. Any ideas?

@wkharold

This comment has been minimized.

Copy link

wkharold commented Sep 11, 2013

Very nice! Worked first time on my Ubuntu 13.04 box. One problem: uname -a says:

Linux centos 3.8.0-30-generic #44-Ubuntu SMP Thu Aug 22 20:52:24 UTC 2013 x86_64 x86_64 x86_64 GNU/Linux

instead of what my Vagrant CentOS 6.x box says:

Linux localhost.localdomain 2.6.32-279.22.1.el6.x86_64 #1 SMP Wed Feb 6 03:10:46 UTC 2013 x86_64 x86_64 x86_64 GNU/Linux

This prevents me from installing EPEL (which checks for RedHat 6.x) from installing and getting access to all the additional EPEL goodies.

@clvx

This comment has been minimized.

Copy link

clvx commented Sep 17, 2013

This template is really good; however, the container gets broken after updating initscripts from the base repo. I don't know why this is happening, but after the update and following the reboot it freezes saying /etc/fstab is mounted.

Also, if I check in the rootfs/var/log/messages, I found this errors:
Sep 16 19:03:14 localhost init: tty (/dev/tty2) main process (359) terminated with status 1
Sep 16 19:03:14 localhost init: tty (/dev/tty2) main process ended, respawning
Sep 16 19:03:14 localhost init: tty (/dev/tty3) main process (360) terminated with status 1
Sep 16 19:03:14 localhost init: tty (/dev/tty3) main process ended, respawning
Sep 16 19:03:14 localhost /sbin/mingetty[362]: tty2: cannot open tty: Operation not permitted
Sep 16 19:03:14 localhost init: tty (/dev/tty4) main process (361) terminated with status 1
Sep 16 19:03:14 localhost init: tty (/dev/tty4) main process ended, respawning
Sep 16 19:03:14 localhost /sbin/mingetty[363]: tty3: cannot open tty: Operation not permitted
Sep 16 19:03:14 localhost /sbin/mingetty[364]: tty4: cannot open tty: Operation not permitted

@levsh

This comment has been minimized.

Copy link

levsh commented Sep 29, 2013

rpm db fix

 mkdir -p $INSTALL_ROOT/var/lib/rpm
 rpm --root $INSTALL_ROOT  --initdb
 rpm --root $INSTALL_ROOT -ivh $INSTALL_ROOT/centos-release-$release-$releaseminor.centos.$arch.rpm
 $YUM install $PKG_LIST
+cp $INSTALL_ROOT/root/.rpmdb/Packages $INSTALL_ROOT/var/lib/rpm/
 chroot $INSTALL_ROOT rm -f /var/lib/rpm/__*
 chroot $INSTALL_ROOT rpm --rebuilddb
@dminnich

This comment has been minimized.

Copy link

dminnich commented Nov 3, 2013

fix init scripts after a yum upgrade...

comment

/sbin/start_udev

in /var/lib/lxc/CONTAINER/rootfs/etc/rc.d/rc.sysinit.

@xeon22

This comment has been minimized.

Copy link

xeon22 commented Nov 28, 2013

I am also getting the error reported by clvx about mingetty respawning. Any ideas?

@erikh

This comment has been minimized.

Copy link

erikh commented Dec 31, 2013

on ubuntu 12.04, you'll need to patch the getopt line to include the rootfs long argument.

@higkoo

This comment has been minimized.

Copy link

higkoo commented Jan 8, 2014

df -Th

df: no file systems processed

@higkoo

This comment has been minimized.

Copy link

higkoo commented Jan 8, 2014

How to create '$cache/rootfs/' ?

@zakalibit

This comment has been minimized.

Copy link

zakalibit commented May 8, 2014

To allow nfs mounts from the container I had to add the following to the config

lxc.aa_profile = unconfined
lxc.cgroup.devices.allow = b 7:* rwm
lxc.cgroup.devices.allow = c 10:237 rwm

@masahirosmanhuts

This comment has been minimized.

Copy link

masahirosmanhuts commented Jun 19, 2014

getting 404 error form curl.
any ideas?

@buksy

This comment has been minimized.

Copy link

buksy commented Jun 26, 2014

Did some changes to get the container working on 14.04. Also changed the default minor version to be 5
Also changed the looping do be done for all the sites in the mirror list till a working one is found.
https://gist.github.com/buksy/76574f8d4296b081bf65

@vrastogi

This comment has been minimized.

Copy link

vrastogi commented Jul 25, 2014

Be warned that it may not work with Fedora and similar (rpm-based) hosts. For me it overwrote many critical parts of the host such as the rpm db, libc,..., effectively making the host unusable.

@briceburg

This comment has been minimized.

Copy link

briceburg commented Aug 10, 2014

Forked to support 6.5-11.1 final release

https://gist.github.com/briceburg/17417af388a464251638

@anumercian

This comment has been minimized.

Copy link

anumercian commented May 22, 2015

This script is not working for Ubuntu 14.04, so I used @buksy's script from the link provided above. The script starts to work but gives this error:

curl: (7) Failed to connect to 6.5 is not a valid and current release or hasnt been released yet port 80: Connection timed out.

I am trying to install Centos LXC on Ubuntu 14.04 Virtual Machine (on Virtual Box). Any help is much appreciated.

@huegelc

This comment has been minimized.

Copy link

huegelc commented Oct 12, 2015

Take a look at this scripts: https://github.com/tpokorra/lxc-scripts

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.