如何在 AWS 上的自定义 AMI(CentOS) 上设置 cloud-init?

36

在AWS实例中定义用户数据似乎非常有用,可以执行各种引导操作。不幸的是,由于PCI原因,我必须使用一个未从提供的AMI之一派生的自定义CentOS AMI,因此cloud-init尚未安装和配置。我只想设置主机名并运行一个小的bash脚本。如何使其工作?


在这个上下文中,“PCI”是什么意思? - JDS
@JDS它是指由支付卡行业数据安全委员会制定的准则。遵守这些准则是处理信用卡付款所必需的,并且通常是一种强有力的安全标准。 - whereswalden
whereswalden:好的,谢谢。得注意一下“行话”,你懂的。 - JDS
4个回答

55

cloud-init是一个非常强大但文档不完善的工具。即使安装成功,也有很多默认激活的模块会覆盖您在AMI上已经定义的内容。以下是从头开始进行最小设置的说明:

说明

  1. Install cloud-init from a standard repository. If you're worried about PCI, you probably don't want to use AWS's custom repositories.

    # rpm -Uvh https://download.fedoraproject.org/pub/epel/6/x86_64/epel-release-6-8.noarch.rpm
    # yum install cloud-init
    
  2. Edit /etc/cloud/cloud.cfg, a yaml file, to reflect your desired configuration. Below is a minimal configuration with documentation for each module.

    #If this is not explicitly false, cloud-init will change things so that root
    #login via ssh is disabled. If you don't want it to do anything, set it false.
    disable_root: false
    
    #Set this if you want cloud-init to manage hostname. The current
    #/etc/hosts file will be replaced with the one in /etc/cloud/templates.
    manage_etc_hosts: true
    
    #Since cloud-init runs at multiple stages of boot, this needs to be set so
    #it can log in all of them to /var/log/cloud-init.
    syslog_fix_perms: null
    
    #This is the bit that makes userdata work. You need this to have userdata
    #scripts be run by cloud-init.
    datasource_list: [Ec2]
    datasource:
      Ec2:
        metadata_urls: ['http://169.254.169.254']
    
    #modules that run early in boot
    cloud_init_modules:
     - bootcmd  #for running commands in pre-boot. Commands can be defined in cloud-config userdata.
     - set-hostname  #These 3 make hostname setting work
     - update-hostname
     - update-etc-hosts
    
    #modules that run after boot
    cloud_config_modules:
     - runcmd  #like bootcmd, but runs after boot. Use this instead of bootcmd unless you have a good reason for doing so.
    
    #modules that run at some point after config is finished
    cloud_final_modules:
     - scripts-per-once  #all of these run scripts at specific events. Like bootcmd, can be defined in cloud-config.
     - scripts-per-boot
     - scripts-per-instance
     - scripts-user
     - phone-home  #if defined, can make a post request to a specified url when done booting
     - final-message  #if defined, can write a specified message to the log
     - power-state-change  #can trigger stuff based on power state changes
    
    system_info:
      #works because amazon's linux AMI is based on CentOS
      distro: amazon
    
  3. If there is a defaults.cfg in /etc/cloud/cloud.cfg.d/, delete it.

  4. To take advantage of this configuration, define the following userdata for new instances:

    #cloud-config
    hostname: myhostname
    fqdn: myhostname.mydomain.com
    runcmd:
     - echo "I did this thing post-boot"
     - echo "I did this too"
    

    You can also simply run a bash script by replacing #cloud-config with #!/bin/bash and putting the bash script in the body, but if you do, you should remove all of the hostname-related modules from cloud_init_modules.

附加说明

请注意,这是一个最基本的配置,cloud-init 能够管理用户、ssh密钥、挂载点等。请参考以下文献获取有关特定功能的更多文档。

总的来说,似乎 cloud-init 根据指定的模块执行操作。一些模块(如“disable-ec2-metadata”)只需指定即可执行操作;其他一些(如“runcmd”)仅在其参数被指定时(无论是在 cloud.cfg 中还是在云配置 userdata 中)才会执行操作。下面的大部分文档只告诉您每个模块可能的参数,而不告诉您模块的名称。但是默认的 cloud.cfg 应该有完整的模块列表供您使用。我发现禁用一个模块的最好方法就是从列表中删除它。

在某些情况下,“rhel” 可能比 “amazon” 更适合“distro”标记。 我还没有弄清楚什么情况下要这样做。


参考文献


1
你想要编辑的配置文件实际上是 /etc/cloud/cloud.cfg,而不是 /etc/cloud.cfg。我把所有的更改都应用到了 /etc/cloud.cfg(包括将 disable_root 设置为 false),创建了一个 AMI,但随后被锁定,无法访问从该 AMI 创建的任何实例以及我的原始实例,因为 cloud-init 实际上并没有读取那个文件。 - Paul Zielinski
1
没错!已编辑。 - whereswalden
1
@whereswalden 谢谢。我找了又找,猜到可能是这样,但我找不到任何明确说明要使用安装了cloud-init的AMI构建的东西。虽然很多地方都有暗示,但免费提供的Centos AMI似乎没有它,而且似乎我需要重新制作。抱歉我说话有点啰嗦,但我喝了一些啤酒。 - JDS
@TennisSmith:不是这样的,正如答案所说,脚本应该作为userdata提供给新实例。我相信如果你正在寻找的是AMI中独立运行的内容,有一种方法可以实现,但我忘记了如何操作,而且我已经无法访问编写此答案时使用的环境了。 - whereswalden
谢谢!这正是我需要的! - Dan Herman
显示剩余3条评论

12
Here is a brief tutorial on how to run scripts during startup using cloud-init on AWS EC2 (CentOS).
This tutorial explains:
- how to set the configuration file /etc/cloud/cloud.cfg - what the cloud path /var/lib/cloud/scripts looks like - the script files under the cloud path using an example, and - how to check if the script files are executed during the instance's startup Configuration file The configuration file below is for AWS CentOS6. For Amazon Linux, see here.
# cat /etc/cloud/cloud.cfg
manage_etc_hosts: localhost
user: root
disable_root: false
ssh_genkeytypes: [ rsa, dsa ]

cloud_init_modules:
 - resizefs
 - update_etc_hosts
 - ssh

cloud_final_modules:
 - scripts-per-once
 - scripts-per-boot
 - scripts-per-instance
 - scripts-user

目录树

这是云路径/var/lib/cloud/scripts的目录结构:

# cd /var/lib/cloud/scripts
# tree `pwd`
/var/lib/cloud/scripts
├── per-boot
│     └── per-boot.sh
├── per-instance
│     └── per-instance.sh
└── per-once
       └── per-once.sh

脚本文件内容

以下是示例脚本文件的内容。
这些文件必须在用户root下。请参阅创建引导脚本中的我的方法。

# cat /var/lib/cloud/scripts/per-boot/per-boot.sh
#!/bin/sh
echo per-boot: `date` >> /tmp/per-xxx.txt

# cat /var/lib/cloud/scripts/per-instance/per-instance.sh
#!/bin/sh
echo per-instance: `date` >> /tmp/per-xxx.txt

# cat /var/lib/cloud/scripts/per-once/per-once.sh   
#!/bin/sh
echo per-once: `date` >> /tmp/per-xxx.txt

执行结果

在初始启动的情况下

# cat /tmp/per-xxx.txt
per-once: 1 January 3, 2013 Thursday 17:30:16 JST 
per-boot: 1 January 3, 2013 Thursday 17:30:16 JST 
per-instance: 1 January 3, 2013 Thursday 17:30:16 JST

在重新启动的情况下
# cat /tmp/per-xxx.txt
per-once: 1 January 3, 2013 Thursday 17:30:16 JST 
per-boot: 1 January 3, 2013 Thursday 17:30:16 JST 
per-instance: 1 January 3, 2013 Thursday 17:30:16 JST 
per-boot: 1 January 3, 2013 Thursday 17:32:24 JST

在AMI中启动的情况下
# cat /tmp/per-xxx.txt
per-once: 1 January 3, 2013 Thursday 17:30:16 JST 
per-boot: 1 January 3, 2013 Thursday 17:30:16 JST 
per-instance: 1 January 3, 2013 Thursday 17:30:16 JST 
per-boot: 1 January 3, 2013 Thursday 17:32:24 JST 
per-boot: 1 January 3, 2013 Thursday 17:44:08 JST

参考文献
检查了在cloud-init(CentOS6)中运行脚本的时间(已翻译)


1
我发现你必须确保在shell脚本上设置可执行权限。 - JamesP

6
扩展之前的答案,为了创建一个启用cloud-init且能够实际执行您的CloudFormation脚本的CentOS AMI,您可以尝试以下步骤:
  1. 启动一个带更新的市场CentOS AMI - 确保存在cloud-initsudo yum install -y cloud-init
  2. rm -rf /var/lib/cloud/data
  3. rm -rf /var/lib/cloud/instance
  4. rm -rf /var/lib/cloud/instances/*
  5. /etc/cloud/cloud.cfg替换为上面答案中的配置,但确保设置distro: rhel
  6. 添加CloudFormation助手(http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/cfn-helper-scripts-reference.html)
  7. 从此实例创建AMI镜像
我曾经花了很长时间才弄清楚为什么我的UserData没有被调用,直到意识到市场中的映像通常只运行一次每个实例的UserData,并且当然它们已经运行过。删除已执行的指示符以及在cloud.cfg文件中更改distro: rhel就可以解决问题。
对于好奇的人,distro:值应与/usr/lib/python2.6/site-packages/cloudinit/distros中的python脚本之一相对应。结果发现我启动的AMI没有amazon.py,因此您需要为CentOS使用rhel。根据您启动的AMI和cloud-init的版本,可能会有所不同。

在引用其他答案时,将其链接起来会很有帮助。顺序可能会改变。 - Steve Buzonas

0

感谢在这里已经对cloud-init进行了如此多的澄清!然而,至少就AWS而言,有一个问题需要强调:

默认情况下,在启动EC2实例时,用户数据脚本和cloud-init指令只运行第一次引导周期

换句话说,它们不会在随后的引导中运行。这并不总是被提及,我怀疑这会引起混淆。更多信息:

{{link2:cloud-init必须确定当前引导是否为新实例的第一次引导,以便应用适当的配置。在实例的第一次引导中,它应该运行所有“每个实例”配置,而在随后的引导中,它应该仅运行“每个引导”配置。}}


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接