创建 AWS EC2 实例时 userdata 的一些知识

我们在初始一个 AWS EC2 实例时,可以通过 user data 让 EC2 第一次启动后做些事情,可以放置 shell script 或 cloud-init 指令。在控制台设置 user data 可用明文文本,由 awscli 创建时可使用一个文件,或者通过 API 用 base64 编码的内容。

下面是 user data 被执行时需知晓的一些知识

  1. 是脚本时必须以 #! 开始,俗称 Shebang, 如 #!/bin/bash
  2. user data是以 root 身份执行,所以不要用 sudo, 当然创建的目录或文件的 owner 也是 root,需要 ec2-user 用户访问的话需要 chmod 修改文件权限,或者直接用 chown ec2-user:ec2-user -R abc 修改文件的所有者()
  3. 脚本不能交互,有交互时必须想办法跳过用户输入,如 apt install -y xzy, 带个  -y 标记
  4. 如果脚本中需访问 AWS 资源,权限由 Instance Profile 所指定的 IAM role 决定
  5. user data 中的脚本会被存储在  /var/lib/cloud/instances/<instance-id>/user-data.txt 文件中,因此也可以从这里验证 user data 是否设置正确。或者在 EC2 实例上访问 http://169.254.169.254/latest/user-data 也能看到 user data 的内容。并且在 EC2 实例初始化后不被删除,所以以此实例为基础来创建一个新的 AMI 需把它删除了
  6. user data 的大小限制为 16 KB, 指 base64 编码前的大小
  7. cloud-init 的输出日志在 /var/log/cloud-init-output.log, 它会捕获 cloud-init 控制台的输出内容

user data 的内容通常在创建好实例后,还得等一会才完全生效,马上用 SSH 登陆新创建后的实例一般还看不到效果,有可能得等分把钟。

下面是在一个选择了 Ubuntu 18.04 镜像的 EC2 实例的 user data 样例 -- 用来安装 Python 3.9, 并创建虚拟环境, 安装 Python 组件
 1#!/bin/bash
 2apt update
 3apt install awscli -y
 4apt install software-properties-common -y
 5add-apt-repository ppa:deadsnakes/ppa -y
 6apt update
 7apt install python3.9 python3.9-venv -y
 8python3.9 -m venv /home/ec2-user/py3.9-venv
 9/home/ec2-user/py3.9-venv/bin/pip install boto3 pymongo psycopg2-binary
10chown -R ec2-user:ec2-user /home/ec2-user/py3.9-venv

脚本的内容会存储在 EC2 实例上,但它执行的控制台输出却没地方找,如果脚本执行过程中有问题就难以诊断了,这里有个办法可记录下 user data 中脚本执行的控制台输出,需在 user data 中加上一行,最后把调试也打开
1#!/bin/bash -ex
2exec > >(tee /var/log/user-data.log|logger -t user-data -s 2>/dev/console) 2>&1
3apt update
4......

对,你没有看错,上面的 exec > >(... 两个大括号之间有空格

这样就能在实例的 /var/log/user-data.log 中看到所有 user data 中脚本执行的控制台输出了,错在哪一步也就能有的放矢的修正。

比如本人有一次发现 user data 中的脚本没发生作用,就查看 /var/log/user-data.log 中的内容发现错误信息
E: Could not get lock /var/lib/dpkg/lock-frontend - open (11: Resource temporarily unavailable)
E: Unable to acquire the dpkg frontend lock (/var/lib/dpkg/lock-frontend), is another process using it?
原来是还有一个进程也在执行 apt,继而 Google 到 apt-daily.service 启动的时候在干那事,查看它的日志
$ journalctl -u apt-daily.service
-- Logs begin at Thu 2021-09-09 07:38:01 UTC, end at Sat 2021-09-18 17:29:41 UTC. --
Sep 18 17:23:21 lin-0aff3d81.mstarext.com systemd[1]: Starting Daily apt download activities...
Sep 18 17:23:46 lin-0aff3d81.mstarext.com systemd[1]: Started Daily apt download activities.
所以在 user data 需要避开它,我们的 user data 可以加一个  sleep 30 进行延迟执行,30 不够的话就 sleep 60

已启动 EC2 实例的 user data 只允许在实例停止后被修改,但修改后的内容并不会在重启后得到执行,只不过是更新了一下文件 /var/lib/cloud/instances/<instance-id>/user-data.txt 中内容,无用功,难道是在掩盖现场?

How can I utilize user data to automatically run a script with every restart of my Amazon EC2 Linux instance?(user data 设计为只在第一次初始化 EC2 实例时起作用,有人曲折的方式让实例每次重启时也执行 user data 中的内容,个人觉得没多大意义,每次启动执行何不做成一个服务呢?)

关于 cloud-init 指令式的 user data

要在 user data 中放置 cloud-init 指令, 需在 user data 的第一行写上 #cloud-config。cloud-init 的用户可比脚本复杂了,它能做一些更高级的事情。这里就不深究,暂时还感觉不到非得用 cloud init 指令不成的境地,下面只看一个节选自官方的例子
 1#cloud-config
 2repo_update: true
 3repo_upgrade: all
 4
 5packages:
 6 - httpd
 7 - mariadb-server
 8
 9runcmd:
10 - [ sh, -c, "amazon-linux-extras install -y lamp-mariadb10.2-php7.2 php7.2" ]
11 - systemctl start httpd
12 - sudo systemctl enable httpd
13 - [ sh, -c, "usermod -a -G apache ec2-user" ]
14 - [ sh, -c, "chown -R ec2-user:apache /var/www" ]
15 - chmod 2775 /var/www
16 - [ find, /var/www, -type, d, -exec, chmod, 2775, {}, \; ]
17 - [ find, /var/www, -type, f, -exec, chmod, 0664, {}, \; ]
18 - [ sh, -c, 'echo "<?php phpinfo(); ?>" > /var/www/html/phpinfo.php' ]

它是 YAML 文件格式,比起用脚本可难看多了, runcmd 中一会用 sh 加中括号参数的形式,一会又直接 shell 脚本。个人认为上面的 sudo 是不需要的。

cloud-init 指令应该与系统中的 cloud-init 命令是相关的,EC2 实例中可以看到一个配置文件 /etc/cloud/cloud.cfg.

#cloud-config 形式的执行输出到控制台的内容会写到 /var/log/cloud-init-out.log 文件中,希望它记录下更多 cloud-init 执行时调试信息的话,在 user data 的  cloud-init 指令中加上一句
1output : { all : '| tee -a /var/log/cloud-init-output.log' }

链接:

  1. Work with instance user data
  2. Logging user-data Script Output on EC2 Instances
  3. Run commands on your Linux instance at launch
  4. How to Fix 'E: Could not get lock /var/lib/dpkg/lock/log' Error in Ubuntu Linux
  5. How can I utilize user data to automatically run a script with every restart of my Amazon EC2 Linux instance?
永久链接 https://yanbin.blog/launch-aws-ec2-instance-userdata-knowledge/, 来自 隔叶黄莺 Yanbin's Blog
[版权声明] 本文采用 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 进行许可。