¿Cómo puedo hacer que los scripts de inicio de cloud-init se ejecuten cada vez que se inicia mi instancia de EC2?

Tengo una instancia de EC2 que ejecuta una AMI basada en Amazon Linux AMI. Al igual que todas las AMI de este tipo, admite el sistema cloud-init para ejecutar scripts de inicio basados ​​en los datos de usuario pasados ​​en cada instancia. En este caso particular, mi entrada de datos de usuario pasa a ser un archivo de inclusión que genera muchos otros scripts de inicio:

#include http://s3.amazonaws.com/path/to/script/1 http://s3.amazonaws.com/path/to/script/2 

La primera vez que reinicio mi instancia, el script de inicio de cloud-init se ejecuta correctamente. Sin embargo, si hago un reinicio suave de la instancia (ejecutando sudo shutdown -r now , por ejemplo), la instancia vuelve a aparecer sin ejecutar el script de inicio la segunda vez. Si voy a los registros del sistema, puedo ver:

 Running cloud-init user-scripts user-scripts already ran once-per-instance [ OK ] 

Esto no es lo que quiero, puedo ver la utilidad de tener scripts de inicio que solo se ejecutan una vez por vida de la instancia, pero en mi caso deberían ejecutarse cada vez que se inicia la instancia, como las secuencias de comandos normales de inicio.

Me doy cuenta de que una posible solución es hacer que mis scripts se rc.local manualmente en rc.local después de ejecutarse la primera vez. Esto parece engorroso, sin embargo, dado que los entornos cloud-init y rc.d son sutilmente diferentes y ahora tendría que depurar los scripts en el primer lanzamiento y en todos los lanzamientos posteriores por separado.

¿Alguien sabe cómo puedo decirle a cloud-init que siempre ejecute mis scripts? Esto ciertamente suena como algo que los diseñadores de cloud-init habrían considerado.

En 11.10, 12.04 y posteriores, puede lograr esto haciendo que ‘el usuario de scripts’ ejecute ‘siempre’. En /etc/cloud/cloud.cfg verás algo como:

 cloud_final_modules: - rightscale_userdata - scripts-per-once - scripts-per-boot - scripts-per-instance - scripts-user - keys-to-console - phone-home - final-message 

Esto puede modificarse después del arranque, o los datos de configuración de nube que reemplazan esta estrofa pueden insertarse a través de datos de usuario. Es decir, en datos de usuario puede proporcionar:

 #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 

Eso también puede ser ‘#incluido’ como lo ha hecho en su descripción. Lamentablemente, en este momento, no puedes modificar los ‘cloud_final_modules’, solo anularlos. Espero agregar la capacidad de modificar secciones de configuración en algún momento.

Hay un poco más de información sobre esto en el documento de configuración de la nube en http://bazaar.launchpad.net/~cloud-init-dev/cloud-init/trunk/view/head:/doc/examples/cloud-config .TXT

Alternativamente, puede colocar los archivos en / var / lib / cloud / scripts / per-boot, y se ejecutarán mediante la ruta ‘scripts-per-boot’.

En /etc/init.d/cloud-init-user-scripts , edite esta línea:

 /usr/bin/cloud-init-run-module once-per-instance user-scripts execute run-parts ${SCRIPT_DIR} >/dev/null && success || failure 

a

  /usr/bin/cloud-init-run-module always user-scripts execute run-parts ${SCRIPT_DIR} >/dev/null && success || failure 

Buena suerte !

Una posibilidad, aunque algo hackish, es eliminar el archivo de locking que usa cloud-init para determinar si el script de usuario ya se ejecutó o no. En mi caso (Amazon Linux AMI), este archivo de locking se encuentra en /var/lib/cloud/sem/ y se llama user-scripts.i-7f3f1d11 (la parte hash al final cambia cada arranque). Por lo tanto, la siguiente secuencia de comandos de datos de usuario agregada al final del archivo de inclusión hará el truco:

 #!/bin/sh rm /var/lib/cloud/sem/user-scripts.* 

No estoy seguro de si esto tendrá algún efecto adverso en alguna otra cosa, pero ha funcionado en mis experimentos.

cloud-init admite esto ahora de forma nativa; consulte las descripciones del comando runcmd vs bootcmd en la documentación ( http://cloudinit.readthedocs.io/en/latest/topics/examples.html#run-commands-on-first-boot ):

“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 ] 

también tenga en cuenta el ejemplo del comando “cloud-init-per” en bootcmd. De su ayuda:

 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 

Luché con este tema durante casi dos días, probé todas las soluciones que pude encontrar y finalmente, combinando varios enfoques, llegó con lo siguiente:

 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} 

Esta es la configuración para el proceso de replicación entre regiones de DynamoDb.