Как сделать так, чтобы скрипты запуска cloud-init запускались каждый раз при загрузке моего экземпляра EC2?
У меня есть экземпляр EC2 под управлением AMI на основе Amazon Linux AMI. Как и все такие AMI, он поддерживает систему cloud-init для запуска сценариев запуска на основе пользовательских данных, передаваемых в каждый экземпляр. В данном конкретном случае мой ввод данных пользователя является файлом включения, который содержит несколько других сценариев запуска:
#include
http://s3.amazonaws.com/path/to/script/1
http://s3.amazonaws.com/path/to/script/2
При первой загрузке моего экземпляра скрипт запуска cloud-init запускается правильно. Однако, если я делаю мягкую перезагрузку экземпляра (запустив sudo shutdown -r now
например), экземпляр возвращается без повторного запуска сценария запуска. Если я захожу в системные журналы, я вижу:
Running cloud-init user-scripts
user-scripts already ran once-per-instance
[ OK ]
Это не то, что я хочу - я вижу полезность наличия сценариев запуска, которые запускаются только один раз за время существования экземпляра, но в моем случае они должны запускаться при каждом запуске экземпляра, как обычные сценарии запуска.
Я понимаю, что одно из возможных решений - вручную вставить свои скрипты в rc.local
после запуска в первый раз. Однако это кажется обременительным, поскольку среды cloud-init и rc.d немного отличаются друг от друга, и теперь мне придется отлаживать сценарии при первом запуске и всех последующих запусках отдельно.
Кто-нибудь знает, как я могу сказать cloud-init всегда запускать мои скрипты? Это, безусловно, звучит как то, что разработчики cloud-init рассмотрели бы.
9 ответов
В 11.10, 12.04 и более поздних версиях вы можете добиться этого, заставляя 'scripts-user' всегда запускать '. В /etc/cloud/cloud.cfg вы увидите что-то вроде:
cloud_final_modules:
- rightscale_userdata
- scripts-per-once
- scripts-per-boot
- scripts-per-instance
- scripts-user
- keys-to-console
- phone-home
- final-message
Это может быть изменено после загрузки, или данные конфигурации облака, переопределяющие этот раздел, могут быть вставлены через пользовательские данные. Т.е. в пользовательских данных вы можете указать:
#cloud-config
cloud_final_modules:
- rightscale_userdata
- scripts-per-once
- scripts-per-boot
- scripts-per-instance
- [scripts-user, always]
- keys-to-console
- phone-home
- final-message
Это также может быть "#included", как вы сделали в своем описании. К сожалению, сейчас вы не можете изменить "cloud_final_modules", а только переопределить его. Я надеюсь добавить возможность изменять разделы конфигурации в какой-то момент.
Более подробную информацию об этом можно найти в документе "cloud-config" по адресу http://bazaar.launchpad.net/~cloud-init-dev/cloud-init/trunk/view/head:/doc/examples/cloud-config.txt
Кроме того, вы можете поместить файлы в / var / lib / cloud / scripts / per-boot, и они будут запускаться по пути "scripts-per-boot".
В /etc/init.d/cloud-init-user-scripts
отредактируйте эту строку:
/usr/bin/cloud-init-run-module once-per-instance user-scripts execute run-parts ${SCRIPT_DIR} >/dev/null && success || failure
в
/usr/bin/cloud-init-run-module always user-scripts execute run-parts ${SCRIPT_DIR} >/dev/null && success || failure
Удачи!
cloud-init теперь поддерживает это изначально, см. описания команд runcmd против bootcmd в документации ( http://cloudinit.readthedocs.io/en/latest/topics/examples.html):
"RunCmd":
#cloud-config
# run commands
# default: none
# runcmd contains a list of either lists or a string
# each item will be executed in order at rc.local like level with
# output to the console
# - runcmd only runs during the first boot
# - if the item is a list, the items will be properly executed as if
# passed to execve(3) (with the first arg as the command).
# - if the item is a string, it will be simply written to the file and
# will be interpreted by 'sh'
#
# Note, that the list has to be proper yaml, so you have to quote
# any characters yaml would eat (':' can be problematic)
runcmd:
- [ ls, -l, / ]
- [ sh, -xc, "echo $(date) ': hello world!'" ]
- [ sh, -c, echo "=========hello world'=========" ]
- ls -l /root
- [ wget, "http://slashdot.org", -O, /tmp/index.html ]
"Bootcmd":
#cloud-config
# boot commands
# default: none
# this is very similar to runcmd, but commands run very early
# in the boot process, only slightly after a 'boothook' would run.
# bootcmd should really only be used for things that could not be
# done later in the boot process. bootcmd is very much like
# boothook, but possibly with more friendly.
# - bootcmd will run on every boot
# - the INSTANCE_ID variable will be set to the current instance id.
# - you can use 'cloud-init-per' command to help only run once
bootcmd:
- echo 192.168.1.130 us.archive.ubuntu.com >> /etc/hosts
- [ cloud-init-per, once, mymkfs, mkfs, /dev/vdb ]
также обратите внимание на пример команды "cloud-init-per" в bootcmd. Из его справки:
Usage: cloud-init-per frequency name cmd [ arg1 [ arg2 [ ... ] ]
run cmd with arguments provided.
This utility can make it easier to use boothooks or bootcmd
on a per "once" or "always" basis.
If frequency is:
* once: run only once (do not re-run for new instance-id)
* instance: run only the first boot for a given instance-id
* always: run every boot
Одна из возможностей, хотя и несколько хакерская, состоит в том, чтобы удалить файл блокировки, который использует cloud-init, чтобы определить, был ли уже запущен пользовательский скрипт. В моем случае (Amazon Linux AMI) этот файл блокировки находится в /var/lib/cloud/sem/
и назван user-scripts.i-7f3f1d11
(хеш-часть в конце меняет каждую загрузку). Таким образом, следующий скрипт пользовательских данных, добавленный в конец файла Include, сделает свое дело:
#!/bin/sh
rm /var/lib/cloud/sem/user-scripts.*
Я не уверен, окажет ли это какое-либо неблагоприятное влияние на что-либо еще, но это сработало в моих экспериментах.
Пожалуйста , используйте приведенный ниже скрипт над вашим bash-скриптом.
пример: здесь я печатаю привет мир моему файлу
остановить экземпляр перед добавлением в userdata
скрипт
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." >> /var/tmp/sdksdfjsdlf
--//
Если кто-то хочет сделать это на CDK, вот пример на Python.
Для Windows пользовательские данные имеют специальный persist
тег , но и для Linux, вам нужно использовать MULTIPART данные пользователя для настройки облака иниц первый. Этот пример Linux работал с
cloud-config
(см. справочный блог ) тип детали вместо что требует
cloud-init-per
(см. также cloud-boothook
bootcmd ) вызов, который я не смог проверить (например:
cloud-init-pre always
).
Пример Linux:
# Create some userdata commands
instance_userdata = ec2.UserData.for_linux()
instance_userdata.add_commands("apt update")
# ...
# Now create the first part to make cloud-init run it always
cinit_conf = ec2.UserData.for_linux();
cinit_conf .add_commands('#cloud-config');
cinit_conf .add_commands('cloud_final_modules:');
cinit_conf .add_commands('- [scripts-user, always]');
multipart_ud = ec2.MultipartUserData()
#### Setup to run every time instance starts
multipart_ud.add_part(ec2.MultipartBody.from_user_data(cinit_conf , content_type='text/cloud-config'))
#### Add the commands desired to run every time
multipart_ud.add_part(ec2.MultipartBody.from_user_data(instance_userdata));
ec2.Instance(
self, "myec2",
userdata=multipart_ud,
#other required config...
)
Пример Windows:
instance_userdata = ec2.UserData.for_windows()
# Bootstrap
instance_userdata.add_commands("Write-Output 'Run some commands'")
# ...
# Making all the commands persistent - ie: running on each instance start
data_script = instance_userdata.render()
data_script += "<persist>true</persist>"
ud = ec2.UserData.custom(data_script)
ec2.Instance(
self, "myWinec2",
userdata=ud,
#other required config...
)
Я боролся с этой проблемой почти два дня, перепробовал все решения, которые смог найти, и, наконец, соединив несколько подходов, получил следующее:
MyResource:
Type: AWS::EC2::Instance
Metadata:
AWS::CloudFormation::Init:
configSets:
setup_process:
- "prepare"
- "run_for_instance"
prepare:
commands:
01_apt_update:
command: "apt-get update"
02_clone_project:
command: "mkdir -p /replication && rm -rf /replication/* && git clone https://github.com/awslabs/dynamodb-cross-region-library.git /replication/dynamodb-cross-region-library/"
03_build_project:
command: "mvn install -DskipTests=true"
cwd: "/replication/dynamodb-cross-region-library"
04_prepare_for_west:
command: "mkdir -p /replication/replication-west && rm -rf /replication/replication-west/* && cp /replication/dynamodb-cross-region-library/target/dynamodb-cross-region-replication-1.2.1.jar /replication/replication-west/replication-runner.jar"
run_for_instance:
commands:
01_run:
command: !Sub "java -jar replication-runner.jar --sourceRegion us-east-1 --sourceTable ${TableName} --destinationRegion ap-southeast-1 --destinationTable ${TableName} --taskName -us-ap >/dev/null 2>&1 &"
cwd: "/replication/replication-west"
Properties:
UserData:
Fn::Base64:
!Sub |
#cloud-config
cloud_final_modules:
- [scripts-user, always]
runcmd:
- /usr/local/bin/cfn-init -v -c setup_process --stack ${AWS::StackName} --resource MyResource --region ${AWS::Region}
- /usr/local/bin/cfn-signal -e $? --stack ${AWS::StackName} --resource MyResource --region ${AWS::Region}
Это настройка для процесса межрегиональной репликации DynamoDb.
Другой подход - использовать #cloud-boothook
в вашем скрипте пользовательских данных. Из документов:
Cloud Boothook
- Начинается с #cloud-boothook или Content-Type: text/cloud-boothook.
- Это содержимое является данными загрузочного крючка. Он сохраняется в файле в каталоге /var/lib/cloud, а затем немедленно запускается.
- Это самый ранний доступный "крючок". Нет механизма, позволяющего запускать его только один раз. Багажник должен сам об этом позаботиться. Он предоставляется с идентификатором экземпляра в переменной среды INSTANCE_ID. Используйте эту переменную, чтобы предоставить набор данных загрузчика один раз для каждого экземпляра.
Что мне помогло в Amazon Linux 2, так это удаление семафоров под/var/lib/cloud/instance/sem/
:
sudo rm /var/lib/cloud/instance/sem/config_write_files
sudo rm /var/lib/cloud/instance/sem/config_runcmd
sudo rm /var/lib/cloud/instance/sem/config_scripts_user
Теоретически можно создать cron для периодического удаления этих файлов.