Random IT Notes

As stated above.

Building Custom CentOS 6.4 LiveCD With Ansible

| Comments

Preface

LiveCDs can be a big help when you need to do a task that cannot be executed from within OS for some reason. Resizing partitions, changing passwords and firmware updates – is just the beginning of the list. It is especially welcome when your machine cannot boot from network ( PXE ).

There are lot of very good options like Knoppix, Parted Magic or LiveCDs of popular distributions. But there are cases when you need custom LiveCD with your scripts, packages and preinstalled software. Ability to modify root partition using writable snapshot on livecd, allows you to add software and modify configuration on the fly.

LiveCD can be easily flashed to usb or converted to pxe images. Hence, there are plenty of ways to get into your custom livecd. Booting LiveCD from network can be a great way to bootstrap your hardware before os installation.

The standard procedure of LiveCD creation requires livecd-tools package together with kickstart file that defines packages list and post installation instructions. All postinstall is done using shell scripts which is far from ideal. Tools like Ansible are better suitable for doing exactly that. Defining system state with DSL language is much cleaner and easily maintanable.

In the next guide I will show how to create your own custom Centos 6.4 LiveCD using step by step instructions.

Installing Requirements

  1. CentOS 6.X machine.
  2. livecd-tools, git, python-argparse and ansible – all packages available in EPEL repository.

Install EPEL repository if not already installed:

# yum -y install http://ftp.nluug.nl/pub/os/Linux/distr/fedora-epel/6/i386/epel-release-6-8.noarch.rpm

Install required packages:

# yum -y install livecd-tools git ansible python-argparse

Clone livecd-ansible repository:

$ git clone https://github.com/GR360RY/livecd-ansible.git

Building LiveCD the old way

Let start from getting basic livecd configuration kickstart file for CentOS created by Fabian Arrotin (arrfab):

wget http://people.centos.org/arrfab/CentOS6/LiveCD-DVD/centos6-liveCD-desktop.cfg

livecd-creator tool from livecd-tools package is used to create custom livecd.

The example follows:

# livecd-creator -c centos6-liveCD-desktop.cfg -f centos6-desktop

This will create centos6-desktop.iso as defined in centos6-liveCD-desktop.cfg. The file has standard kickstart format and contains two main parts:

Services configuration and packages selection:

This part is straightforward. Language, Keyboard and Services are self explanatory. The same goes for package list – contains yum groups and individual packages.

centos6-liveCD-desktop.cfg
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
lang en_US.UTF-8
keyboard us
timezone US/Eastern
auth --useshadow --enablemd5
selinux --enforcing
firewall --enabled --service=mdns
repo --name=base    --baseurl=file://REPOPATH

xconfig --startxonboot
part / --size 4096 --fstype ext4
services --enabled=NetworkManager --disabled=network,sshd


%packages
syslinux
kernel
@base
@core
@basic-desktop
...

Postinstallation stage:

Here the real mess is starting. Triple variable escaping is a pain:

%post

## default LiveCD user
LIVECD_USER="centoslive"

########################################################################
# Create a sub-script so the output can be captured
# Must change "$" to "\$" and "`" to "\`" to avoid shell quoting
########################################################################
cat > /root/post-install << EOF_post
#!/bin/bash

echo ###################################################################
echo ## Creating the livesys init script
echo ###################################################################

Here is an example code snipplet from centos6-liveCD-desktop.cfg

centos6-liveCD-desktop.cfg
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
%post
...

. /etc/init.d/functions

if ! strstr "\\\`cat /proc/cmdline\\\`" liveimg || [ "\\\$1" != "start" ]; then
      exit 0
      fi

      if [ -e /.liveimg-configured ] ; then
            configdone=1
            fi


            exists() {
                  which \\\$1 >/dev/null 2>&1 || return
                      \\\$*
                      }

...

Maintaining your bash code is not convenient in such a way. The same goes for catching output or just plain debugging. As as result adding your own software to the livecd becomes a laborious task ( in case you need more then just adding an individual package)

Building LiveCD with Ansible

Taking into acount problems presented with the conventional way of creating LiveCD, Ansible was the obvious choise for the task. Ansible supports “chroot path” as transport for playbooks which is ideal for Postinstallation Stage.

Before I get into the details let see how the new build procedure works.

$ cd livecd-ansible
$ ./generate_config.py centos6-mini.yml
$ sudo -s
# livecd-creator -c centos6-mini.ks --cache=cache -f centos6-mini

This will create centos6-mini.iso with only the basic packages.

Before we used kickstart config file to “define” the livecd. From now on we will use Ansible playbook for this purpose.

generate_config.py accepts playbook file as an argument and will generate kickstart config file. All postinstallation steps will be done using Ansible.

Check out the Project Structure below:

Project Structure
1
2
3
4
5
6
7
8
9
10
11
12
13
14
.
├── roles                         # Ansible roles folder
│   ├── livecd-pre-common
│   ├── centos-sshd-service       # sshd service role
│   ├── epel-repo                 # epel repo setup
│   ├── livecd-pxe-common
│   ├── livecd-post-common
│   └── livecd-isolinux-common
├── templates
│   └── centos6-mini.ks.j2        # Basic config template
├── centos6-mini.yml              # Ansible Playbook for %post stage
├── generate_config.py            # Config file Generator
├── README.md
└── Vagrantfile

centos6-mini.yml will be used to generate config file and run postinstall:

centos-mini.yml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
- hosts: post
  connection: chroot
  sudo: false
  gather_facts: false

  vars:
    cdlabel: centos6-mini
    hostname: centos6-mini
    livecd_user: centos
    root_password: centos

    # Not used directly in playbook. Used by generate_config.py to create basic kickstart file.

    lang: en_US.UTF-8
    keyboard: us
    timezone: Asia/Jerusalem

    repos_list:
      - { name: 'a-base', baseurl: 'http://mirror.isoc.org.il/pub/centos/6/os/$basearch' }
      - { name: 'a-updates', baseurl: 'http://mirror.isoc.org.il/pub/centos/6/updates/$basearch' }

    # End of variable block used by generate_config.py

  roles:
    - livecd-pre-common
    # Add your ansible roles here
    # - epel-repo
    # - centos-sshd-service
    - livecd-pxe-common
    - livecd-post-common


- hosts: post-nochroot
  connection: local
  sudo: false
  gather_facts: false

  roles:
    - livecd-isolinux-common

You can copy or modify centos6-mini.yml to create your own livecd by adding your ansible roles below livecd-pre-common. Update lang, keyboard, timezone and repos_list to match your location to reduce download times. If you move livecd-ansible folder to a new location, rerun generate_config.py for every playbook file.

Debugging your Ansible Playbooks

livecd-creator has a hidden fiature – you can get get into the livecd chroot environmnet following the %post stage and before squashfs and iso are created. Let execute livecd-creator with “-l” parameter:

# livecd-creator -l -c centos6-mini.ks --cache=cache -f centos6-mini

You will be dropped to shell after playbook execution:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
...
PLAY [post-nochroot] **********************************************************

TASK: [livecd-isolinux-common | Get CDLABEL from isolinux.cfg] ****************
ok: [127.0.0.1]

TASK: [livecd-isolinux-common | Deploy modified isolinux.cfg] *****************
changed: [127.0.0.1]

PLAY RECAP ********************************************************************
/var/tmp/imgcreate-sP_xxo/install_root : ok=10   changed=7    unreachable=0    failed=0
127.0.0.1                  : ok=2    changed=1    unreachable=0    failed=0

Launching shell. Exit to continue.
----------------------------------
bash-4.1#

Open another terminal session and go to livecd-ansible directory. You can modify ansible playbooks directly and rerun playbook as follows:

ansible-playbook -i auto_gen_ansible_hosts-centos6-mini centos6-mini.yml

This procedure can be repeated as many times as you need till you satisfied with the result. Go back to chroot environment and exit the shell with Ctrl+D. It will complete the build procedure. …

Testing Everething with Vagrant

If you don’t have CentOS 6 laying around, you can create livecd using virtualbox and vagrant. Let assume that all the above componenets are already installed. The repository includes Vagrantfile together with provisioning part done with ansible ( Just make sure to install ansible on your machine prior to spinning up the VM).

Vagrantfile
1
2
3
4
5
6
7
8
9
10
11
12
13
# -*- mode: ruby -*-
# vi: set ft=ruby :

VAGRANTFILE_API_VERSION = "2"
Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
  config.vm.box = "centos64-x64"
  config.vm.box_url = "https://github.com/2creatives/vagrant-centos/releases/download/v0.1.0/centos64-x86_64-20131030.box"
  config.vm.provision "ansible" do |ansible|
    ansible.playbook = "livecd-builder.yml"
    ansible.tags = "host_setup_livecd"
    ansible.sudo = true
  end
end

The above setup will configure VM with all the requirements for building livecd. ansible.tags = "host_setup_livecd" line will only run “setup” tasks from livecd-builder.yml playbook.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
$ vagrant up
Bringing machine 'default' up with 'virtualbox' provider...
[default] Importing base box 'centos64-x64'...
[default] Matching MAC address for NAT networking...
[default] Setting the name of the VM...
[default] Clearing any previously set forwarded ports...
[default] Creating shared folders metadata...
[default] Clearing any previously set network interfaces...
[default] Preparing network interfaces based on configuration...
[default] Forwarding ports...
[default] -- 22 => 2222 (adapter 1)
[default] Booting VM...
[default] Waiting for machine to boot. This may take a few minutes...
[default] Machine booted and ready!
[default] Mounting shared folders...
[default] -- /vagrant
[default] Running provisioner: ansible...

PLAY [all] ********************************************************************

GATHERING FACTS ***************************************************************
ok: [default]

TASK: [epel-repo | Make sure EPEL repo is available] **************************
changed: [default]

TASK: [epel-repo | Verify existence of RPM-GPG-KEY-EPEL-6] ********************
changed: [default]

TASK: [livecd-builder-requirements | Install required packages] ***************
changed: [default] => (item=screen,git,ansible,livecd-tools,python-argparse)

TASK: [Get livecd-ansible repository] *****************************************
skipping: [default]

PLAY RECAP ********************************************************************
default                    : ok=4    changed=3    unreachable=0    failed=0

If you already tested your own livecd and want to build it using vagrant, modify the Vagrantfile as in the next example: * Remove ansible.tags line * Add ansible.extra_vars line with the name of your custom playbook without extension.

Vagrantfile
1
2
3
4
5
6
7
8
9
10
11
12
13
# -*- mode: ruby -*-
# vi: set ft=ruby :

VAGRANTFILE_API_VERSION = "2"
Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
  config.vm.box = "centos64-x64"
  config.vm.box_url = "https://github.com/2creatives/vagrant-centos/releases/download/v0.1.0/centos64-x86_64-20131030.box"
  config.vm.provision "ansible" do |ansible|
    ansible.playbook = "livecd-builder.yml"
    ansible.extra_vars = { livecd_label: "centos6-modified" }
    ansible.sudo = true
  end
end

livecd-builder will look for centos6-modified.yml and create livecd based on roles included in your file. The resulting iso file will be placed in the source directory.

Comments