在 AWS 中,user_data 是否在 cloud-init 之前执行?
in AWS, is user_data executed before cloud-init?
我使用 terraform 创建一个 EC2 实例,我使用 user_data
在 /var/lib/cloud/scripts/per-once
中放置一个文件。这没有执行 - 我现在的问题是:cloud-init 运行 在 user_data
之前吗?
===编辑===
对 Dude0001 非常有帮助的答案的更长回复:
我已经尝试了以下方法,现在 - 这是我的 user_data
:
#!/bin/bash
cat >/var/lib/cloud/scripts/per-once/install_mysql <<!
#cloud-config
package_update: true
packages:
- mysql-server
!
cat >>/root/.bashrc <<!
set -o vi
unalias -a
alias ll='ls -lp'
!
cat >>/home/admin/.bashrc <<!
set -o vi
unalias -a
alias ll='ls -lp'
!
cat /root/.vimrc <<!
set t_ti= t_te=
set compatible
set expandtab ts=2 sw=2 ai
!
cat >/home/admin/.vimrc <<!
set t_ti= t_te=
set compatible
set expandtab ts=2 sw=2 ai
!
这将按预期创建所有文件(我真的很守旧,不喜欢 vim 的大部分新功能)。创建实例后我尝试重新启动:没有 mysqld。我更改了权限,chmod 755 /var/lib/cloud/scripts/per-once/install_mysql,然后重新启动:也没有结果(我更改权限的原因是 python 代码显示云- init 仅查找可执行文件)。
===编辑===
对我上面的user_data
的一些解释:
这种结构可能会让一些人感到困惑,因为它不太常见:
cat >/some/path/to/a/file <<!
...
!
cat
是一个简单地从标准输入读取并写入标准输出的命令,它通常与重定向 <
和 >
一起使用。在上面的构造中,我将任何输出定向到一个文件 /some/path/to/a/file
。另一部分,涉及 <<!
和 !
被称为 here 文档 ,我怀疑它起源于大型机上使用的 JCL 语言,但是这真的很有用。意思是 阅读以下行,直到结束标记 (此处:!
,但它可以是任何字符串)。所以,总而言之,它说 创建一个包含以下内容的文件:....
第一个文件 /var/lib/cloud/scripts/per-once/install_mysql
包含:
#cloud-config
package_update: true
packages:
- mysql-server
我希望这应该告诉 cloud-init 更新包存储库并安装 mysql-server
- 这不会发生。
接下来的 4 个文件只是 root
和 admin
用户环境中的一些设置;基本上,我创建了一个 .vimrc
并向 .bashrc
添加了几行以确保某些设置符合我的喜好。
文件都创建好了,但是#cloud-config
的那个好像根本就没动过。我昨天做了一些实验,将这个文件放在 /var/lib/cloud/scripts/
下的不同目录中,但看起来很像当 cloud-init 读取目录时这些文件不存在。通读cloud-init
的源码,好像是运行经历了10个阶段——user_data
是在第5阶段抓取的,应该是第7阶段读取的。我也可以看到它似乎需要设置执行权限位;然而,这是重启后日志中的内容:
2019-10-02 08:06:52,884 - handlers.py[DEBUG]: start: modules-final/config-scripts-per-boot: running config-scripts-per-boot with frequency always
2019-10-02 08:06:52,884 - helpers.py[DEBUG]: Running config-scripts-per-boot using lock (<cloudinit.helpers.DummyLock object at 0x7f677362acc0>)
2019-10-02 08:06:52,885 - util.py[DEBUG]: Running command ['/var/lib/cloud/scripts/per-boot/install_mysql'] with allowed return codes [0] (shell=False, capture=False)
2019-10-02 08:06:52,887 - util.py[WARNING]: Failed running /var/lib/cloud/scripts/per-boot/install_mysql [-]
2019-10-02 08:06:52,887 - util.py[DEBUG]: Failed running /var/lib/cloud/scripts/per-boot/install_mysql [-]
Traceback (most recent call last):
File "/usr/lib/python3/dist-packages/cloudinit/util.py", line 1992, in subp
env=env, shell=shell)
File "/usr/lib/python3.7/subprocess.py", line 775, in __init__
restore_signals, start_new_session)
File "/usr/lib/python3.7/subprocess.py", line 1522, in _execute_child
raise child_exception_type(errno_num, err_msg, err_filename)
OSError: [Errno 8] Exec format error: b'/var/lib/cloud/scripts/per-boot/install_mysql'
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/usr/lib/python3/dist-packages/cloudinit/util.py", line 835, in runparts
subp(prefix + [exe_path], capture=False)
File "/usr/lib/python3/dist-packages/cloudinit/util.py", line 2000, in subp
stderr="-" if decode else b"-")
cloudinit.util.ProcessExecutionError: Exec format error. Missing #! in script?
Command: ['/var/lib/cloud/scripts/per-boot/install_mysql']
Exit code: -
Reason: [Errno 8] Exec format error: b'/var/lib/cloud/scripts/per-boot/install_mysql'
Stdout: -
Stderr: -
2019-10-02 08:06:52,897 - cc_scripts_per_boot.py[WARNING]: Failed to run module scripts-per-boot (per-boot in /var/lib/cloud/scripts/per-boot)
2019-10-02 08:06:52,898 - handlers.py[DEBUG]: finish: modules-final/config-scripts-per-boot: FAIL: running config-scripts-per-boot with frequency always
2019-10-02 08:06:52,898 - util.py[WARNING]: Running module scripts-per-boot (<module 'cloudinit.config.cc_scripts_per_boot' from '/usr/lib/python3/dist-packages/cloudinit/config/cc_scripts_per_boot.py'>) failed
2019-10-02 08:06:52,898 - util.py[DEBUG]: Running module scripts-per-boot (<module 'cloudinit.config.cc_scripts_per_boot' from '/usr/lib/python3/dist-packages/cloudinit/config/cc_scripts_per_boot.py'>) failed
Traceback (most recent call last):
File "/usr/lib/python3/dist-packages/cloudinit/stages.py", line 800, in _run_modules
freq=freq)
File "/usr/lib/python3/dist-packages/cloudinit/cloud.py", line 54, in run
return self._runners.run(name, functor, args, freq, clear_on_fail)
File "/usr/lib/python3/dist-packages/cloudinit/helpers.py", line 187, in run
results = functor(*args)
File "/usr/lib/python3/dist-packages/cloudinit/config/cc_scripts_per_boot.py", line 41, in handle
util.runparts(runparts_path)
File "/usr/lib/python3/dist-packages/cloudinit/util.py", line 842, in runparts
% (len(failed), len(attempted)))
RuntimeError: Runparts: 1 failures in 1 attempted commands
因此,它绝对不喜欢文件的格式 - 它想要查看 #!...
或者可能是二进制可执行文件。
我现在将更详细地尝试 Dude0001 的建议。
===编辑===
最后,按照 Dude0001 的建议,使用 multipart/mixed 格式是有效的:
Content-Type: multipart/mixed; boundary="//"
MIME-Version: 1.0
--//
Content-Type: text/cloud-config; charset="us-ascii"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment; filename="cloud-config.txt"
#cloud-config
package_update: yes
package_upgrade: all
packages:
- mariadb-server
- apt-file
--//
Content-Type: text/x-shellscript; charset="us-ascii"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment; filename="userdata.txt"
#!/bin/bash
cat >>/root/.bashrc <<!
set -o vi
unalias -a
alias ll='ls -lp'
!
cat >>/home/admin/.bashrc <<!
set -o vi
unalias -a
alias ll='ls -lp'
!
cat /root/.vimrc <<!
set t_ti= t_te=
set compatible
set expandtab ts=2 sw=2 ai
!
cat >/home/admin/.vimrc <<!
set t_ti= t_te=
set compatible
set expandtab ts=2 sw=2 ai
!
--//
仅指定 #cloud-config
似乎行不通,但这种方式行之有效。至少对我来说。在当下。
简答:
A user_data
值设置为 shell 脚本将导致给定的 shell 脚本在 cloud-init 的最后阶段成为 运行(我相信在您引用的一次性文件夹中的 cloud-init 指令之后。
如果您想在 EC2 user_data
属性 中使用自定义 cloud-init 指令和 shell 脚本,您需要使用 multipart/mixed mime 格式 https://aws.amazon.com/premiumsupport/knowledge-center/execute-user-data-ec2/
长答案:
user_data
可以保存要通过 EC2 元数据、脚本或 cloud-init 指令读取的原始数据。此外,您可以将其设置为 multipart/mixed mime 类型并提供其中的每一个。
如果user_data
是原始数据,可以在EC2实例中用curl命令获取。由调用命令来解释数据,可以是用户选择的任何内容。
[ec2-user ~]$ curl http://169.254.169.254/latest/user-data
如果user_data
是一个脚本(比如第一行的#!/bin/bash
),那么在cloud-init的最后阶段运行作为cloud-init的一个步骤https://cloudinit.readthedocs.io/en/latest/topics/boot.html#final.
如果user_data
是一个cloud-init指令(例如第一行的#cloud-config
),它是运行作为指定的cloud-init指令。
来自 https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/user-data.html#user-data-cloud-init
"To pass cloud-init directives to an instance with user_data
... enter your cloud-init directive text in the user_data
text."
像这样
#cloud-config
repo_update: true
repo_upgrade: all
packages:
- httpd
- mariadb-server
runcmd:
- [ sh, -c, "amazon-linux-extras install -y lamp-mariadb10.2-php7.2 php7.2" ]
- systemctl start httpd
- sudo systemctl enable httpd
- [ sh, -c, "usermod -a -G apache ec2-user" ]
- [ sh, -c, "chown -R ec2-user:apache /var/www" ]
- chmod 2775 /var/www
- [ find, /var/www, -type, d, -exec, chmod, 2775, {}, \; ]
- [ find, /var/www, -type, f, -exec, chmod, 0664, {}, \; ]
- [ sh, -c, 'echo "<?php phpinfo(); ?>" > /var/www/html/phpinfo.php' ]
此处描述了 multipart/mixed mime 格式 https://aws.amazon.com/premiumsupport/knowledge-center/execute-user-data-ec2/,示例
Content-Type: multipart/mixed; boundary="//"
MIME-Version: 1.0
--//
Content-Type: text/cloud-config; charset="us-ascii"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment; filename="cloud-config.txt"
#cloud-config
cloud_final_modules:
- [scripts-user, always]
--//
Content-Type: text/x-shellscript; charset="us-ascii"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment; filename="userdata.txt"
#!/bin/bash
/bin/echo "Hello World" >> /tmp/testfile.txt
--//
我使用 terraform 创建一个 EC2 实例,我使用 user_data
在 /var/lib/cloud/scripts/per-once
中放置一个文件。这没有执行 - 我现在的问题是:cloud-init 运行 在 user_data
之前吗?
===编辑===
对 Dude0001 非常有帮助的答案的更长回复:
我已经尝试了以下方法,现在 - 这是我的 user_data
:
#!/bin/bash
cat >/var/lib/cloud/scripts/per-once/install_mysql <<!
#cloud-config
package_update: true
packages:
- mysql-server
!
cat >>/root/.bashrc <<!
set -o vi
unalias -a
alias ll='ls -lp'
!
cat >>/home/admin/.bashrc <<!
set -o vi
unalias -a
alias ll='ls -lp'
!
cat /root/.vimrc <<!
set t_ti= t_te=
set compatible
set expandtab ts=2 sw=2 ai
!
cat >/home/admin/.vimrc <<!
set t_ti= t_te=
set compatible
set expandtab ts=2 sw=2 ai
!
这将按预期创建所有文件(我真的很守旧,不喜欢 vim 的大部分新功能)。创建实例后我尝试重新启动:没有 mysqld。我更改了权限,chmod 755 /var/lib/cloud/scripts/per-once/install_mysql,然后重新启动:也没有结果(我更改权限的原因是 python 代码显示云- init 仅查找可执行文件)。
===编辑===
对我上面的user_data
的一些解释:
这种结构可能会让一些人感到困惑,因为它不太常见:
cat >/some/path/to/a/file <<!
...
!
cat
是一个简单地从标准输入读取并写入标准输出的命令,它通常与重定向 <
和 >
一起使用。在上面的构造中,我将任何输出定向到一个文件 /some/path/to/a/file
。另一部分,涉及 <<!
和 !
被称为 here 文档 ,我怀疑它起源于大型机上使用的 JCL 语言,但是这真的很有用。意思是 阅读以下行,直到结束标记 (此处:!
,但它可以是任何字符串)。所以,总而言之,它说 创建一个包含以下内容的文件:....
第一个文件 /var/lib/cloud/scripts/per-once/install_mysql
包含:
#cloud-config
package_update: true
packages:
- mysql-server
我希望这应该告诉 cloud-init 更新包存储库并安装 mysql-server
- 这不会发生。
接下来的 4 个文件只是 root
和 admin
用户环境中的一些设置;基本上,我创建了一个 .vimrc
并向 .bashrc
添加了几行以确保某些设置符合我的喜好。
文件都创建好了,但是#cloud-config
的那个好像根本就没动过。我昨天做了一些实验,将这个文件放在 /var/lib/cloud/scripts/
下的不同目录中,但看起来很像当 cloud-init 读取目录时这些文件不存在。通读cloud-init
的源码,好像是运行经历了10个阶段——user_data
是在第5阶段抓取的,应该是第7阶段读取的。我也可以看到它似乎需要设置执行权限位;然而,这是重启后日志中的内容:
2019-10-02 08:06:52,884 - handlers.py[DEBUG]: start: modules-final/config-scripts-per-boot: running config-scripts-per-boot with frequency always
2019-10-02 08:06:52,884 - helpers.py[DEBUG]: Running config-scripts-per-boot using lock (<cloudinit.helpers.DummyLock object at 0x7f677362acc0>)
2019-10-02 08:06:52,885 - util.py[DEBUG]: Running command ['/var/lib/cloud/scripts/per-boot/install_mysql'] with allowed return codes [0] (shell=False, capture=False)
2019-10-02 08:06:52,887 - util.py[WARNING]: Failed running /var/lib/cloud/scripts/per-boot/install_mysql [-]
2019-10-02 08:06:52,887 - util.py[DEBUG]: Failed running /var/lib/cloud/scripts/per-boot/install_mysql [-]
Traceback (most recent call last):
File "/usr/lib/python3/dist-packages/cloudinit/util.py", line 1992, in subp
env=env, shell=shell)
File "/usr/lib/python3.7/subprocess.py", line 775, in __init__
restore_signals, start_new_session)
File "/usr/lib/python3.7/subprocess.py", line 1522, in _execute_child
raise child_exception_type(errno_num, err_msg, err_filename)
OSError: [Errno 8] Exec format error: b'/var/lib/cloud/scripts/per-boot/install_mysql'
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/usr/lib/python3/dist-packages/cloudinit/util.py", line 835, in runparts
subp(prefix + [exe_path], capture=False)
File "/usr/lib/python3/dist-packages/cloudinit/util.py", line 2000, in subp
stderr="-" if decode else b"-")
cloudinit.util.ProcessExecutionError: Exec format error. Missing #! in script?
Command: ['/var/lib/cloud/scripts/per-boot/install_mysql']
Exit code: -
Reason: [Errno 8] Exec format error: b'/var/lib/cloud/scripts/per-boot/install_mysql'
Stdout: -
Stderr: -
2019-10-02 08:06:52,897 - cc_scripts_per_boot.py[WARNING]: Failed to run module scripts-per-boot (per-boot in /var/lib/cloud/scripts/per-boot)
2019-10-02 08:06:52,898 - handlers.py[DEBUG]: finish: modules-final/config-scripts-per-boot: FAIL: running config-scripts-per-boot with frequency always
2019-10-02 08:06:52,898 - util.py[WARNING]: Running module scripts-per-boot (<module 'cloudinit.config.cc_scripts_per_boot' from '/usr/lib/python3/dist-packages/cloudinit/config/cc_scripts_per_boot.py'>) failed
2019-10-02 08:06:52,898 - util.py[DEBUG]: Running module scripts-per-boot (<module 'cloudinit.config.cc_scripts_per_boot' from '/usr/lib/python3/dist-packages/cloudinit/config/cc_scripts_per_boot.py'>) failed
Traceback (most recent call last):
File "/usr/lib/python3/dist-packages/cloudinit/stages.py", line 800, in _run_modules
freq=freq)
File "/usr/lib/python3/dist-packages/cloudinit/cloud.py", line 54, in run
return self._runners.run(name, functor, args, freq, clear_on_fail)
File "/usr/lib/python3/dist-packages/cloudinit/helpers.py", line 187, in run
results = functor(*args)
File "/usr/lib/python3/dist-packages/cloudinit/config/cc_scripts_per_boot.py", line 41, in handle
util.runparts(runparts_path)
File "/usr/lib/python3/dist-packages/cloudinit/util.py", line 842, in runparts
% (len(failed), len(attempted)))
RuntimeError: Runparts: 1 failures in 1 attempted commands
因此,它绝对不喜欢文件的格式 - 它想要查看 #!...
或者可能是二进制可执行文件。
我现在将更详细地尝试 Dude0001 的建议。
===编辑===
最后,按照 Dude0001 的建议,使用 multipart/mixed 格式是有效的:
Content-Type: multipart/mixed; boundary="//"
MIME-Version: 1.0
--//
Content-Type: text/cloud-config; charset="us-ascii"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment; filename="cloud-config.txt"
#cloud-config
package_update: yes
package_upgrade: all
packages:
- mariadb-server
- apt-file
--//
Content-Type: text/x-shellscript; charset="us-ascii"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment; filename="userdata.txt"
#!/bin/bash
cat >>/root/.bashrc <<!
set -o vi
unalias -a
alias ll='ls -lp'
!
cat >>/home/admin/.bashrc <<!
set -o vi
unalias -a
alias ll='ls -lp'
!
cat /root/.vimrc <<!
set t_ti= t_te=
set compatible
set expandtab ts=2 sw=2 ai
!
cat >/home/admin/.vimrc <<!
set t_ti= t_te=
set compatible
set expandtab ts=2 sw=2 ai
!
--//
仅指定 #cloud-config
似乎行不通,但这种方式行之有效。至少对我来说。在当下。
简答:
A user_data
值设置为 shell 脚本将导致给定的 shell 脚本在 cloud-init 的最后阶段成为 运行(我相信在您引用的一次性文件夹中的 cloud-init 指令之后。
如果您想在 EC2 user_data
属性 中使用自定义 cloud-init 指令和 shell 脚本,您需要使用 multipart/mixed mime 格式 https://aws.amazon.com/premiumsupport/knowledge-center/execute-user-data-ec2/
长答案:
user_data
可以保存要通过 EC2 元数据、脚本或 cloud-init 指令读取的原始数据。此外,您可以将其设置为 multipart/mixed mime 类型并提供其中的每一个。
如果user_data
是原始数据,可以在EC2实例中用curl命令获取。由调用命令来解释数据,可以是用户选择的任何内容。
[ec2-user ~]$ curl http://169.254.169.254/latest/user-data
如果user_data
是一个脚本(比如第一行的#!/bin/bash
),那么在cloud-init的最后阶段运行作为cloud-init的一个步骤https://cloudinit.readthedocs.io/en/latest/topics/boot.html#final.
如果user_data
是一个cloud-init指令(例如第一行的#cloud-config
),它是运行作为指定的cloud-init指令。
来自 https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/user-data.html#user-data-cloud-init
"To pass cloud-init directives to an instance with user_data
... enter your cloud-init directive text in the user_data
text."
像这样
#cloud-config
repo_update: true
repo_upgrade: all
packages:
- httpd
- mariadb-server
runcmd:
- [ sh, -c, "amazon-linux-extras install -y lamp-mariadb10.2-php7.2 php7.2" ]
- systemctl start httpd
- sudo systemctl enable httpd
- [ sh, -c, "usermod -a -G apache ec2-user" ]
- [ sh, -c, "chown -R ec2-user:apache /var/www" ]
- chmod 2775 /var/www
- [ find, /var/www, -type, d, -exec, chmod, 2775, {}, \; ]
- [ find, /var/www, -type, f, -exec, chmod, 0664, {}, \; ]
- [ sh, -c, 'echo "<?php phpinfo(); ?>" > /var/www/html/phpinfo.php' ]
此处描述了 multipart/mixed mime 格式 https://aws.amazon.com/premiumsupport/knowledge-center/execute-user-data-ec2/,示例
Content-Type: multipart/mixed; boundary="//"
MIME-Version: 1.0
--//
Content-Type: text/cloud-config; charset="us-ascii"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment; filename="cloud-config.txt"
#cloud-config
cloud_final_modules:
- [scripts-user, always]
--//
Content-Type: text/x-shellscript; charset="us-ascii"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment; filename="userdata.txt"
#!/bin/bash
/bin/echo "Hello World" >> /tmp/testfile.txt
--//