Note: I had trouble creating this image from a VM based on a similar image hosted on VMWare, so that is why I decided to build this in vagrant. Also, vagrant can be run from anywhere, which makes this a much more portable solution.
I won't go through the installation process for vagrant and VirtualBox as it's different for each platform, but both of these need to be installed prior to the next step.
We need to create a Vagrantfile to start our instance. This will be a very simple instance, but feel free to use any that you already have available.
Vagrant.configure("2") do |c|
c.vm.box = "rhel65-1.0.0"
c.vm.box_url = "http://example.com/rhel65/1.0.0/rhel65-1.0.0.box"
c.vm.hostname = "default-rhel65-100.vagrantup.com"
c.vm.synced_folder ".", "/vagrant", disabled: true
c.vm.provider :virtualbox do |p|
end
end
Make sure you change the url to point to your .box file.
Now, open a terminal to the directory where you saved the Vagrantfile we just created and type:
vagrant up
This will create the VM and start it up based on the settings from the Vagrantfile. Please refer to the vagrant docs for additional configuration options, but none are needed for creating the docker image.
Next, we'll need to login on the vagrant image by issuing the command:
vagrant ssh
My instance logs me in as the vagrant user with sudoer powers. We will need sudo or root access to create the base image. This is required to access and read all of the system files that need to be copied to the image.
We need to install docker so our base image can actually be stored and pushed to a repo after it's created. You'll notice in the script later that docker commands are called from the script to commit the image with the name provided. We'll install the EPEL and then docker-io.
sudo rpm -ivh http://mirror.pnl.gov/epel/6/i386/epel-release-6-8.noarch.rpm
sudo yum install -y docker-io
Inside the vagrant image, we need to clone the docker repo and cd to the docker/contrib directory:
sudo yum install -y git # I had to install git first as it wasn't in the image I'm using
git clone https://github.com/docker/docker.git
cd docker/contrib
This is where the magic script is stored. I have added it here in case it breaks in the future, but always try the version in the repo as long as it exists. The file we need is mkimage-yum.sh
#!/usr/bin/env bash
#
# Create a base CentOS Docker image.
#
# This script is useful on systems with yum installed (e.g., building
# a CentOS image on CentOS). See contrib/mkimage-rinse.sh for a way
# to build CentOS images on other systems.
usage() {
cat <eoopts basename="" name="">
OPTIONS:
-y <yumconf> The path to the yum config to install packages from. The
default is /etc/yum.conf.
EOOPTS
exit 1
}
# option defaults
yum_config=/etc/yum.conf
while getopts ":y:h" opt; do
case $opt in
y)
yum_config=$OPTARG
;;
h)
usage
;;
\?)
echo "Invalid option: -$OPTARG"
usage
;;
esac
done
shift $((OPTIND - 1))
name=$1
if [[ -z $name ]]; then
usage
fi
#--------------------
target=$(mktemp -d --tmpdir $(basename $0).XXXXXX)
set -x
mkdir -m 755 "$target"/dev
mknod -m 600 "$target"/dev/console c 5 1
mknod -m 600 "$target"/dev/initctl p
mknod -m 666 "$target"/dev/full c 1 7
mknod -m 666 "$target"/dev/null c 1 3
mknod -m 666 "$target"/dev/ptmx c 5 2
mknod -m 666 "$target"/dev/random c 1 8
mknod -m 666 "$target"/dev/tty c 5 0
mknod -m 666 "$target"/dev/tty0 c 4 0
mknod -m 666 "$target"/dev/urandom c 1 9
mknod -m 666 "$target"/dev/zero c 1 5
yum -c "$yum_config" --installroot="$target" --setopt=tsflags=nodocs \
--setopt=group_package_types=mandatory -y groupinstall Core
yum -c "$yum_config" --installroot="$target" -y clean all
cat > "$target"/etc/sysconfig/network <<EOF
NETWORKING=yes
HOSTNAME=localhost.localdomain
EOF
# effectively: febootstrap-minimize --keep-zoneinfo --keep-rpmdb
# --keep-services "$target". Stolen from mkimage-rinse.sh
# locales
rm -rf "$target"/usr/{{lib,share}/locale,{lib,lib64}/gconv,bin/localedef,sbin/build-locale-archive}
# docs
rm -rf "$target"/usr/share/{man,doc,info,gnome/help}
# cracklib
rm -rf "$target"/usr/share/cracklib
# i18n
rm -rf "$target"/usr/share/i18n
# sln
rm -rf "$target"/sbin/sln
# ldconfig
rm -rf "$target"/etc/ld.so.cache
rm -rf "$target"/var/cache/ldconfig/*
version=
if [ -r "$target"/etc/redhat-release ]; then
version="$(sed 's/^[^0-9\]*\([0-9.]\+\).*$/\1/' "$target"/etc/redhat-release)"
fi
if [ -z "$version" ]; then
echo >&2 "warning: cannot autodetect OS version, using '$name' as tag"
version=$name
fi
tar --numeric-owner -c -C "$target" . | docker import - $name:$version
docker run -i -t $name:$version echo success
rm -rf "$target"
We'll now execute this script which will create a docker image and commit it to our local docker instance, but first we need to start our docker instance.
sudo service docker start
sudo ./mkimage-yum.sh rhel
The script will now copy all of the data it needs to a tmp directory, create the image, commit the image using the name provided, in this case rhel, and the version from /etc/redhat-release, and then clean up the tmp directory. Once this is complete, we'll see our docker image in our local docker instance. The last few lines of output show the image id and "name":
Then we can see it in our local list of images, the "name" is made of the REPOSITORY and TAG:
We can also see this in our containers list:
We'll now push it to a private registry just to show the basics of that interaction. We'll use our server at ipfacecobld26 on port 5000, but first we need to commit it with our registry as part of the REPOSITORY:
We used the CONTAINER ID from the previous step to commit the image with the REPOSITORY set as ipfacecobld26:5000/rhel and the tag as 6.5. This returns the IMAGE ID of the new image. We can now push this image to our server: