After spending about three days to get this thing going and create my own CentOS distro, I would like to share this tutorial with everyone interested building his own virtual machine into the AWS cloud.
Long story short, here we go:
1. Create new Amazon 64-bit t1.micro instance
Use all defaults. We’ll use this instance to create our own machine. Since it has YUM, it suits perfectly into our scenario.
- Launch it
- Connect to it via SSH
- Apply all updates
# sudo yum -y update
# sudo yum install -y MAKEDEV
2. Create the disk for the new system
2.1 From AWS console go to “Volumes” and create a new volume of 50GB
2.2 Attach this volume to your micro instance as /dev/sdg
Note: Create in same zone as your running instance!
Note: Inside the instance would be seen as /dev/xvdg disk device
2.3 Partition the new volume
- Start fdisk
# fdisk /dev/xvdg
- Press ‘p’
Disk /dev/xvdg: 53.7 GB, 53687091200 bytes
255 heads, 63 sectors/track, 6527 cylinders
Units = cylinders of 16065 * 512 = 8225280 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0xd5e37c4b
Device Boot Start End Blocks Id System
Command (m for help):
- Press
- ‘n’ – Create new partition
- ‘p’ – Create primary partition
- ’1′ – Create first partition
- ENTER – Start with 1st cylinder
- ENTER – End at last cylinder
- Overview again the configuration with ‘p’
Disk /dev/xvdg: 53.7 GB, 53687091200 bytes
255 heads, 63 sectors/track, 6527 cylinders
Units = cylinders of 16065 * 512 = 8225280 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0xd5e37c4b
Device Boot Start End Blocks Id System
/dev/xvdg1 1 6527 52428096 83 Linux
Command (m for help):
- Press ‘w’ to commit changes
- sync – to sync with disk
- Create new ext4 filesystem on the new /dev/xvdg1 device
# mkfs.ext4 /dev/xvdg1
mke2fs 1.42 (29-Nov-2011)
Filesystem label=
OS type: Linux
Block size=4096 (log=2)
Fragment size=4096 (log=2)
Stride=0 blocks, Stripe width=0 blocks
3276800 inodes, 13107024 blocks
655351 blocks (5.00%) reserved for the super user
First data block=0
Maximum filesystem blocks=4294967296
400 block groups
32768 blocks per group, 32768 fragments per group
8192 inodes per group
Superblock backups stored on blocks:
32768, 98304, 163840, 229376, 294912, 819200, 884736, 1605632, 2654208,
4096000, 7962624, 11239424
Allocating group tables: done
Writing inode tables: done
Creating journal (32768 blocks): done
Writing superblocks and filesystem accounting information: done
2.4 Mount the new partition into the system so we can prepare it
# mkdir /mnt/image/
# mount /dev/xvdg1 /mnt/image
Populate the partition with a clean system
2.5 Create the special devices
# mkdir -p /mnt/image/{dev,etc,proc,sys}
# /sbin/MAKEDEV -v -d /mnt/image/dev -x console
# /sbin/MAKEDEV -v -d /mnt/image/dev -x null
# /sbin/MAKEDEV -v -d /mnt/image/dev -x zero
# mount --bind -t proc proc /mnt/image/proc
# mount --bind -t sysfs sysfs /mnt/image/sys
# mount --bind /dev/ /mnt/image/dev/
2.6 Install YUM
# mkdir -p /mnt/image/var/lib/rpm
# rpm --rebuilddb --root=/mnt/image
# rpm --import --root=/mnt/image http://mirror.centos.org/centos/RPM-GPG-KEY-CentOS-6
# wget http://mirror.centos.org/centos/6.2/os/x86_64/Packages/centos-release-6-2.el6.centos.7.x86_64.rpm
# rpm -ivh --root=/mnt/image/ --nodeps centos-release-6-2.el6.centos.7.x86_64.rpm
# vim /mnt/image/etc/yum.conf
[main]
cachedir=/mnt/image/var/cache/yum/$basearch/$releasever
keepcache=0
debuglevel=2
logfile=/mnt/image/var/log/yum.log
exactarch=1
obsoletes=1
gpgcheck=1
plugins=1
installonly_limit=5
bugtracker_url=http://bugs.centos.org/set_project.php?project_id=16&ref=http://bugs.centos.org/bug_report_page.php?category=yum
distroverpkg=centos-release
# yum -c /mnt/image/etc/yum.conf --installroot=/mnt/image install -y rpm-build yum openssh-server dhclient
2.7 – Configure the networking on the new machine
Note: eth0 must be configured to automatically get its address via dhcp
# cat > /mnt/image/etc/sysconfig/network-scripts/ifcfg-eth0 <<EOF
DEVICE=eth0
BOOTPROTO=dhcp
ONBOOT=yes
TYPE=Ethernet
USERCTL=yes
PEERDNS=yes
IPV6INIT=no
EOF
# cat > /mnt/image/etc/sysconfig/network <<EOF
NETWORKING=yes
HOSTNAME=MyCentOS
NOZEROCONF=yes
NETWORKING_IPV6=no
IPV6INIT=no
IPV6_ROUTER=no
IPV6_AUTOCONF=no
IPV6FORWARDING=no
IPV6TO4INIT=no
IPV6_CONTROL_RADVD=no
EOF
# cat /etc/resolv.conf > /mnt/image/etc/resolv.conf
Note: This will copy host’s resolv.conf to new instance. In my case, the resolv.conf looks like this:
# cat /etc/resolv.conf
; generated by /sbin/dhclient-script
search us-west-2.compute.internal
nameserver 172.16.0.23
2.8 – Configure the disk system
# cat > /mnt/image/etc/fstab <<EOF
/dev/xvde1 / ext4 defaults,noatime 1 1
tmpfs /dev/shm tmpfs defaults 0 0
devpts /dev/pts devpts gid=5,mode=620 0 0
sysfs /sys sysfs defaults 0 0
proc /proc proc defaults 0 0
EOF
2.9 CHROOT into the new system and start installing
# chroot /mnt/image/ su -
# yum -y groupinstall base core
Disable SELinux
# vim /etc/sysconfig/selinux
SELINUX=disabled
Create a password for root, just in case
# passwd
2.10 Configure your SSH access on the future host
In this step we are going to create a new user ec2-user in the Amazon images spirit
# adduser ec2-user
# passwd ec2-user
And change the password to something, even your mamma cannot remember.
Next we will enable the system to retrieve automatically the PEM public key pair defined in the AWS console so you can login with your AWS key pairs.
Open /etc/rc.d/ec2-user-key and append the following snippet:
# vim /etc/rc.d/ec2-user-key
#!/bin/bash
# chkconfig: 2345 99 10
# description: Retrieve Amazon PEM key for the ec2-user
# processname: Amazon
# Retrieve Amazon PEM key for ec2-user
EC2_SSH_KEY=`curl -m 20 --fail --silent http://169.254.169.254/2011-05-01/meta-data/public-keys/0/openssh-key`
if [ $? -eq 0 ];
then
EC2_USER="ec2-user"
EC2_SSH_DIR="/home/$EC2_USER/.ssh"
if [ ! -f "$EC2_SSH_DIR/authorized_keys" ];
then
if [ ! -d "$EC2_SSH_DIR" ];
then
action "Creating $EC2_SSH_DIR"
mkdir -p $EC2_SSH_DIR
chown $EC2_USER:$EC2_USER $EC2_SSH_DIR
chmod 700 $EC2_SSH_DIR
fi
EC2_DESTFILE="$EC2_SSH_DIR/authorized_keys"
action "One time only - copying public PEM file to $EC2_DESTFILE"
echo $EC2_SSH_KEY > $EC2_DESTFILE
chmod 600 $EC2_DESTFILE
chown $EC2_USER:$EC2_USER $EC2_DESTFILE
success "OK"
fi
else
echo "Failed to retrieve the SSH PEM key from key server"
failure ""
fi
To clarify some things above. The URL http://169.254.169.254 is accessible only from within Amazon machines, not from your browser. Also, the URL is hardcoded, doesn’t matter what zone you are located in (not tested).
The script tries to retrieve the public key and save it to authorized_keys, if the file does not already exists. Along the way checks if the directory tree exists, if not, tries to create it and assign appropriate rights, so SSH wouldn’t crap itself.
Now make the script executable
# chmod +x /etc/rc.d/ec2-user-key
# chkconfig --add ec2-user-key
One more thing on security. Edit your /etc/ssh/sshd_config and set the following things:
PermitRootLogin no
PermitEmptyPasswords no
GSSAPIAuthentication no # most likely you won't be needing that
2.11 Configure the grub bootloader
# cd /boot/
# mv initramfs-2.6.32-220.13.1.el6.x86_64.img old_initramfs-2.6.32-220.13.1.el6.x86_64.img
# mkinitrd --force initramfs-2.6.32-220.13.1.el6.x86_64.img 2.6.32-220.13.1.el6.x86_64
# cat > /boot/grub/grub.conf <<EOF
default=0
timeout=3
title EC2
root (hd0,0)
kernel /boot/vmlinuz-2.6.32-220.13.1.el6.x86_64 ro root=/dev/xvde1 rd_NO_PLYMOUTH selinux=0 console=hvc0 loglvl=all sync_console console_to_ring earlyprintk=xen nomodeset
initrd /boot/initramfs-2.6.32-220.13.1.el6.x86_64.img
EOF
# ln -s /boot/grub/grub.conf /boot/grub/menu.lst
# ln -s /boot/grub/grub.conf /etc/grub.conf
# exit
2.12 Unmount the whole thing
# sync
# umount /mnt/image/dev
# umount /mnt/image/proc
# umount /mnt/image/sys
# umount /mnt/image/
Note: if it doesn’t work, use “lsof | grep /mnt” and kill remaining processes
Note: cat /etc/mtab to see if everything is unmounted
3 Take a snapshot of the drive from the AWS console
4 Create a new image from the snapshot
Please select appropriate Kernel ID. For my image I choose aki-f837bac8 (zone us-west-2)
Note: Kernel ID depends on: AWS zone (us, eu, west, east etc.), boot disk (hd00 ebs, or hd0 from S3 back-end)and architecture (i386 or x86_64).
The list of kernels can be consulted here: http://docs.amazonwebservices.com/AWSEC2/latest/UserGuide/UserProvidedkernels.html#AmazonKernelImageIDs
Note: For the root device, choose /dev/sda and NOT the default /dev/sda1!
5 Launch the image into a new t1.micro instance and check the results
Note: Use same kernel as defined above in step 4
Note: You need to authenticate with SSH and key created above!
References:
This tutorial was built using the following tutorials
1. http://paranumeral.blogspot.com/2012/01/centos-62-64bit-aws-ec2-ami-creation.html – Thanks for the detailed info!
2. http://davidatenney.wordpress.com/2012/04/12/creating-your-own-centos-6-2-x86_64-ebs-ami-on-amazon-with-pv-grub-kernel/ – Thanks for answering
3. http://blog.bashton.com/2012/how-to-make-your-own-centos-6-amis/
4. http://www.practicalclouds.com/content/guide/
5. http://amazonaws.michael–martinez.com/
6. http://www.coderchris.com/linux/how-to-mount-an-amazon-ebs-disk-as-a-drive-in-linux-centos/2011/01/31
7. http://wiki.sysconfig.org.uk/display/howto/Build+your+own+Core+CentOS+5.x+AMI+for+Amazon+EC2
8. http://lists.centos.org/pipermail/centos/2011-January/105377.html
9. http://www.ioncannon.net/system-administration/1205/installing-cent-os-5-5-on-ec2-with-the-cent-os-5-5-kernel/
Edit:
Fixed step 2.12 - SSH access using Amazon’s PEM keys