+135 410 16684Mon. - Fri. 10:00-22:00

AWS折腾记——为EC2制作镜像

AWS折腾记——为EC2制作镜像

AWS折腾记——为EC2制作镜像

Amazon web services 在近期启用了悉尼区的服务器,公司终于可以下决心使用了。但在准备开启第一个EC2实例的时候,发现没有CentOS的官方AMI(即EC2可用的镜像)可以选择,只有社区制作的第三方AMI。而对于有洁癖的我们来说,使用第三方镜像是不放心,所以这次我的任务是制作自己的AMI。 制作自己的AMI,我使用了2种方法—— 一是将现有的系统打包成镜像上传;二是在现有系统上制作新的镜像并上传,即官方说的『Creating an Instance Store-Backed AMI From a Loopback』。可惜,虽然2种方法都令EC2成功运行,但启动的时候会将内核变成其他。截至写本文时亦找不到解决办法,是以为记。

一、转换现有系统并上传

这种方法是将VM上的系统用Amazon提供的命令行工具压缩成image并上传到S3,在注册成AMI。具体做法如下:

1. 安装ec2 ami tools 和ec2 api tools

通过以下命令安装ec2 ami tools

1
yum install http://s3.amazonaws.com/ec2-downloads/ec2-ami-tools.noarch.rpm

对于ec2 api tools,可以参考官方文档安装 『Setting Up the Amazon EC2 Command Line Tools

2. 更改系统配置

首先是将ifcfg-eth0里的HWADDRESS去掉,并加上“NM_Controlled no”。然后是安装cloud-init(直接用yum安装)并根据官方修改配置。

3. 获得密钥并打包

在AWS网站上,account里的『Security Credentials』找到『X.509』,生成key.pem和cert.pem,然后用以下命令生成image文件并打包

1
ec2-bundle-vol -c cert.pem -k pk.pem -u <account-id> -d /image -e /image -r x86_64 –no-inherit

其中是帐号的id,-d后面是指镜像放置的目录,-e代表忽略的目录——这个一定要加上,否则会进入死循环。

4. 上传

account里的『access key』找到找到access key id 和secret access key,然后用以下命令上传到S3

1
ec2-upload-bundle -b <bucket name> -m /image/image.manifest.xml -a <access id> -s <secret access id>

5. 注册AMI

1
2
3
4
5
6
7
#设置变量,以便直接运行ec2-register
export EC2_HOME=/usr/local/ec2/ec2-api/
export PATH=$PATH:$EC2_HOME/bin
export AWS_ACCESS_KEY=&amp;lt;access key>
export AWS_SECRET_KEY=&amp;lt;secret key>
ec2-register <bucket name>/image.manifest.xml –region ap-southeast-2

这里,要用–region表明上传到哪个地区,ap-southeast-2是表示悉尼区。

二、现有系统新建系统并上传

这个做法的具体原理是:先在原有系统新建一个镜像文件,然后将其mount到某个目录,利用yum groupinstall Base将系统安装到该镜像文件,最后将文件打包并上传。 在镜像文件里安装系统的做法可以参考以下文章:『Create your own CentOS AMI Image (S3 Backed) 』、『Build your own Core CentOS 5.x AMI for Amazon EC2』 其中打包和上传的命令如下

1
2
3
4
5
6
7
8
#打包
#–image指镜像文件的位置
#–destination指打包后文件的位置
ec2-bundle-image –image /image/ami-centos5.8-64bit-base-img –prefix ami-centos5.8-64bit-base –cert cert-.pem –privatekey pk.pem –user <account-id> –destination /home/public
/EC2AMIFILES –arch x86_64
#上传
ec2-upload-bundle -b crazysalesamistore -m /home/public/EC2AMIFILES/ami-centos5.8-64bit-base.manifest.xml -a <access-key> -s <secret-key>

上传完毕后,可以按照第一种方法注册AMI,也可以在AWS后台使用图形界面注册。

在AWS EC2中创建不含Marketplace code的CentOS6 AMI

参考资料:
https://www.caseylabs.com/remove-the-aws-marketplace-code-from-a-centos-ami/
http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/storage_expand_partition.html

背景介绍:
在AWS EC2中,从Marketplace里面可以很方便的选择最新的CentOS6的官方Minimal版本的AMI,来创建Instance。
但是这里面却埋了一个大坑,那就是,所有基于Marketplace里面的AMI所创建的Instance,都会带有一个Marketplace code。
它会导致你无法通过为现有根分区所在的EBS Volume创建Snapshot和新的Volume的方式来对其扩容。
在Detach了现有的根分区所在的Volume后,将无法再次将其Attach到Instance当中,在Attach新的Volume时也会遇到相同的报错:

Client.OperationNotPermitted:
'vol-xxxxxxx' with Marketplace codes may not be attached as a secondary device.

这个Marketplace code,顾名思义,应该就是为了保护一些付费的AMI不被随意的克隆,但不知道为什么没有对费用为$0的CentOS6 AMI做单独的处理。
上面的限制,主要影响到的是,默认创建好的CentOS6 Instance的EBS Volume只有8G,即使在创建时指定了50G的EBS Volume,创建后的根分区空间也只有8G。这样的大小是无法满足线上需求的,只能对其进行扩容,而因为有上面的Marketplace code的限制,又使扩容变得很艰难。
还好最终我通过参考上面的两篇文章,从官方的CentOS6 AMI中移除了Marketplace code,并成功的对根分区进行了扩容并创建了相应的AMI。

具体步骤:
1. 从现有的CentOS6 AMI中移除Marketplace code
1.1 从AWS的Marketplace搜索CentOS6 AMI,并创建一个根分区所在的EBS Volume为8G(默认大小)的Instance;
1.2 在AWS EC2 web console中,再创建一个新的大小为8G的EBS Volume;
1.3 将新创建的EBS Volume Attach到Instance上,通常会默认识别为/dev/xvdj(HVM版本的AMI会识别为/dev/xvdf);
1.4 通过SSH登陆到Instance,并通过dd克隆根分区所在的EBS Volume(HVM版本的AMI会将根目录所在的EBS Volume识别为/dev/xvda):

dd bs=65536 if=/dev/xvde of=/dev/xvdj

1.5 当克隆完成以后,关闭Instance;
1.6 Detach现有根分区所在的EBS Volume;
1.7 Detach新创建的EBS Volume,并重新Attach到Instance,作为/dev/sda(HVM版本的AMI需要指定为/dev/sda1);
1.8 启动Instance;
1.9 在确认Instance正常启动后,在EC2 web console中右键点击Instance,并选择Create Image,即可创建一个新的不含Marketplace code的CentOS6 AMI了,我一般将其命名为official_centos6_x86_64_minimal_ebs8g。

2. 将现有的AMI根分区所在的EBS Volume扩容为50G,并创建新的AMI official_centos6_x86_64_minimal_ebs50g
2.1 基于AMI official_centos6_x86_64_minimal_ebs8g创建一个Instance;
2.2 为Instance所在的EBS Volume创建一个Snapshot;
2.3 创建一个新的大小为50G的Volume,并包含刚刚创建的Snapshot;
2.4 将新创建的Volume Attach到Instance,作为第二块EBS Volume,默认会识别为/dev/xvdj(HVM版本的AMI会识别为/dev/xvdf);
2.5 在Instance上对第二块EBS Volume进行扩容,详细步骤如下(HVM版本的AMI会将根目录所在的EBS Volume识别为/dev/xvda):

[root@ip-172-17-4-12 ~]# parted /dev/xvdj
GNU Parted 2.1
Using /dev/xvdj
Welcome to GNU Parted! Type 'help' to view a list of commands.
(parted) unit s
(parted) print
Model: Xen Virtual Block Device (xvd)
Disk /dev/xvdj: 104857600s
Sector size (logical/physical): 512B/512B
Partition Table: msdos

Number  Start  End        Size       Type     File system  Flags
 1      2048s  16777215s  16775168s  primary  ext4         boot

(parted) rm 1
(parted) mkpart primary 2048s 100%
(parted) print
Model: Xen Virtual Block Device (xvd)
Disk /dev/xvdj: 104857600s
Sector size (logical/physical): 512B/512B
Partition Table: msdos

Number  Start  End         Size        Type     File system  Flags
 1      2048s  104857599s  104855552s  primary  ext4

(parted) set 1 boot on
(parted) print
Model: Xen Virtual Block Device (xvd)
Disk /dev/xvdj: 104857600s
Sector size (logical/physical): 512B/512B
Partition Table: msdos

Number  Start  End         Size        Type     File system  Flags
1      2048s  104857599s  104855552s  primary  ext4         boot

(parted) quit
Information: You may need to update /etc/fstab.

[root@ip-172-17-4-12 ~]# e2fsck -f /dev/xvdj1
e2fsck 1.41.12 (17-May-2010)
Superblock needs_recovery flag is clear, but journal has data.
Run journal anyway<y>? yes

/dev/xvdj1: recovering journal
Pass 1: Checking inodes, blocks, and sizes
Pass 2: Checking directory structure
Pass 3: Checking directory connectivity
Pass 4: Checking reference counts
Pass 5: Checking group summary information

/dev/xvdj1: ***** FILE SYSTEM WAS MODIFIED *****
/dev/xvdj1: 18425/524288 files (0.2% non-contiguous), 243772/2096896 blocks

[root@ip-172-17-4-12 ~]# lsblk
NAME    MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
xvde    202:0    0   8G  0 disk
└─xvde1 202:1    0   8G  0 part /
xvdj    202:80   0  50G  0 disk
└─xvdj1 202:     0  50G  0 part

[root@ip-172-17-4-12 ~]# resize2fs /dev/xvdj1
resize2fs 1.41.12 (17-May-2010)
Resizing the filesystem on /dev/xvdj1 to 13106944 (4k) blocks.
The filesystem on /dev/xvdj1 is now 13106944 blocks long.

2.6 关闭Instance;
2.7 Detach现有根分区所在的EBS Volume;
2.8 Detach扩容后的第二块EBS Volume,并重新Attach到Instance,作为/dev/sda(HVM版本的AMI需要指定为/dev/sda1);
2.9 启动Instance;
2.10 在确认Instance正常启动后,在EC2 web console中右键点击Instance,并选择Create Image,即可创建一个新的根分区大小为50G的CentOS6 AMI了,我一般将其命名为official_centos6_x86_64_minimal_ebs50g。

PS: 自己制作的镜像,如果要支持创建时自定义指定SSH Public Key,需要确保/etc/rc.local中包含以下代码,同时将镜像中的/root/.ssh/authorized_keys文件删除。

#!/bin/sh
#
# This script will be executed *after* all the other init scripts.
# You can put your own initialization stuff in here if you don't
# want to do the full Sys V style init stuff.

touch /var/lock/subsys/local

# set a random pass on first boot
if [ -f /root/firstrun ]; then
  dd if=/dev/urandom count=50|md5sum|passwd --stdin root
  passwd -l root
  rm /root/firstrun
fi

if [ ! -d /root/.ssh ]; then
  mkdir -m 0700 -p /root/.ssh
  restorecon /root/.ssh
fi

# Get the root ssh key setup
ReTry=0
while [ ! -f /root/.ssh/authorized_keys ] && [ $ReTry -lt 10 ]; do
  sleep 2
  curl -f http://169.254.169.254/latest/meta-data/public-keys/0/openssh-key > /root/.ssh/pubkey
  if [ 0 -eq 0 ]; then
    mv /root/.ssh/pubkey /root/.ssh/authorized_keys
  fi
  ReTry=$[Retry+1]
done
chmod 600 /root/.ssh/authorized_keys && restorecon /root/.ssh/authorized_keys

制作自己的AMI

亚马逊AMI market上提供的AMI有的含有Market Code(不允许将跟分区作为第二分区挂载到别的实例上进去修改根分区内容),有的AMI不支持特定实例类型(例如CentOS.org提供的AMI就不支持c3系列)。因此有必要自己做几个AMI。制作方法和步骤如下:
(1)自己在合适的位置安装一个精简版的CentOS,尽量最简化安装,因为一会要把系统文件内容全部同步到亚马逊上去。还可以直接将虚拟机import进AWS。安装的时候使用/dev/sda1 跟分区,/dev/sda1 swap分区,不要用LVM。例子里我们是创建1GB的vmware磁盘,不要使用动态扩展,尽量使用一次性分配。然后分区/dev/sda1 768M,剩下的MB全部给swap,此处大小要是2的n次方,否则会出现partition doesn’t end with disk boundry类似的警告提示。
(2)第二部将系统内容弄到AWS上去,此处提供两个方案:
首先在一台配置到aws tools的机器(推荐Amazon AMI创建的的EC2),创建一块25GB的EBS volume,并attach,然后格式化分区,17GB/, 8GB swap,精确到MB,然后

mkfs.ext4 /dev/xvdj1;
e2label /dev/xvdj1 /  (打上标签);
mkswap -L swap /dev/xvdj2(也打上标签),
#然后挂载
mount /dev/xvdj1 /ebs(自己创建一个挂载点)

使用rsync将刚才安装的虚拟机文件系统同步到上述挂载点中

rsync -avx -e "ssh -i xxxxx.pem" /* user@ip:/ebs/

使用ec2-import-volume命令将虚拟机磁盘导入到AWS的一块EBS磁盘中。命令如下:

ec2-import-volume centos6.5.disk -f raw -b <your_s3_bucket_name> \
 --region ap-southeast-1 -O <access_key_id> -W <secret_access_key> \
 -o <access_key_id> -w <secret_access_key>

-f是指定格式:kvm、xen的是raw, vmware的是vmdk,hyper-v的是vhd(vhdx格式好想目前还不支持,需要转成vhd格式),-b是指定与你导入地区一致的s3 bucket,因为此过程是通过S3中转的。然后你会得到一块磁盘,attach到上述Amazon AMI的EC2上,然后把内容复制出来。
(3)进入到/ebs中修改你刚才同步过来的文件系统
检查/etc/grub.conf,主要是默认会采用uuid来指定分区,但是复制的vm uuid是不一样的,因此要改成LABEL形式,如下:

default=0
timeout=5
splashimage=(hd0,0)/boot/grub/splash.xpm.gz
hiddenmenu
title CentOS (2.6.32-358.el6.x86_64)
root (hd0,0)
kernel /boot/vmlinuz-2.6.32-358.el6.x86_64 ro root=LABEL=/ console=ttyS0
initrd /boot/initramfs-2.6.32-358.el6.x86_64.img

修改/etc/sysconfig/network-scripts/ifcfg-eth0(注意安装虚拟机得时候只要一个网卡),修改成如下

DEVICE=eth0
BOOTPROTO=dhcp
ONBOOT=yes

修改/etc/fatab,改成用LABEL挂载,如下:(none开头的保持默认)

LABEL=/ / ext4 defaults 1 1
LABEL=swap swap swap defaults 0 0

( 4)将改好的EBS磁盘做成snapshot快照,并获取snapshot ID备用
(5)在上述工作机上用ec2-register命令注册AMI

ec2-register -n "CentOS6.4" -d "CentOS6.4" -a x86_64 -K pk-xxxxxx.pem \
 -C cert-mod.pem --root-device-name /dev/sda1 \
 -b /dev/sda=snap-a40edf49:25:true --kernel aki-503e7402 \
 --region ap-southeast-1

这一步骤需要你账号的X.509 Certificates密钥对(在security credential里面找),还需要kernel id,具体该使用哪个aki,详见这个官方文档链接
AmazonKernelImageIDs
(6)试着基于自己创建的AMI创建一个实例,然后进去自定义一下,再在实例上右键注册成自己个性化的AMI(我一般会装上CloudWatch然后写个README说这个AMI是我做的cloudwatch别删除)
至此完毕

将虚拟机导入到AWS

 

借助ec2-import-instance我们可以将我们的虚拟机导入到AWS中的EC2实例里。

~$ ec2-import-instance centos6.4-vm01.disk -f raw -p Linux -t m3.large \
 -a x86_64 -b &lt;your_s3_bucket&gt; --region ap-southeast-1 -O &lt;access_key_id&gt; \
 -W &lt;secret_access_key&gt; -o &lt;access_key_id&gt; -w &lt;secret_access_key&gt;

(参数为多个字母的都是双-,不知为啥我的总显示为多-,不管有几个-。例如–region参数。请注意,后同)
然后可以用ec2-describe-conversion-tasks命令查看导入过程。ec2-cancel-conversion-task命令取消导入任务。

~$ ec2-describe-conversion-tasks --region ap-southeast-1 import-i-ffm0xv71 -O xxxx -W

xxxxx(import-i-ffm0xv71这个是ec2-import-instance任务的任务ID,在命令输出结果中有)
如果都顺利的话,你能到指定的region得到一台EC2实例,默认是关机状态。
在这个过程中需要注意如下事项:
导入虚拟机的时候,虚拟机只支持一个DHCP的网卡,多网卡的删掉,光驱可以删掉(至少不能让他挂载这ISO的状态导入),如果有多个磁盘,此命令只能导入跟分区所在的虚拟磁盘(注意不是指根分区所在的分区)其他的磁盘用下面介绍的ec2-import-volume导入
导入的虚拟机配置类型是有限制的(Linux虚拟机是有限制的,Windows虚拟机时无限制)
We support importing Windows instances into any instance type. Linux instances can be imported into the following instance types:m3.xlarge、m3.2xlarge、hi1.4xlarge、hs1.8xlarge、cc1.4xlarge、cg1.4xlarge、cc2.8xlarge、cr1.8xlarge
如果将导入的虚拟机右键注册成AMI(如果你使用的默认的AKI)将会导致你的AMI只能创建指定类型的EC2实例,需要选择合适的AKI,见上一篇文档的分享链接。

用ec2-import-volume将虚拟机磁盘导入AWS EBS

~$ ec2-import-volume centos6.4.disk -f raw -b &lt;your-s3-bucket&gt; \
 --region ap-southeast-1 -O xxxx -W xxxxxxxx -o xxxx -w xxxxxx

使用ec2-describe-conversion-tasks查看导入的进度,完成之后你会得到一个状态为Available的EBS Volume。