In diesem Blog zeigen wir Ihnen, wie Sie eine systemd cfn-signal-Einheit konfigurieren, um einen erfolgreichen oder fehlgeschlagenen Abschluss des cloud-init-Laufs zu signalisieren.
Wir verwalten seit 2017 ein Dutzend Autoscaling-Gruppen für Instanzen von ECS-Clustern mit CloudFormation. Um eine Instanz einer virtuellen Maschine für einen bestimmten Cluster zu konfigurieren, verwenden wir cloud-init. Mit cloud-init konfigurieren wir den ECS-Agent und konfigurieren die Protokollierung für die richtigen Protokollgruppen. Das bedeutet, dass wir eine cloud-init-Konfiguration als Benutzerdaten in unsere CloudFormation-Vorlage eingebettet haben. Das funktioniert meistens ziemlich gut, aber manchmal, wenn die cloud-init-Konfiguration einen Fehler aufweist, wurde trotzdem ein Erfolgssignal gesendet, so dass wir eine falsch konfigurierte virtuelle Maschine haben.
Mit systemd können wir eine cfn-signal-Einheit konfigurieren, um den Status von cloud-init zu melden, nachdem cloud-init ausgeführt wurde. Das können Sie auch tun! Wir werden es Ihnen zeigen:
- wie man den erfolgreichen Abschluss von cloud-init feststellt
- wie man cfn-signal von cloud-init abhängig macht
- wie man die cfn-Signaleinheit definiert
- integrieren Sie dies in die CloudFormation-Vorlage
Feststellung des erfolgreichen Abschlusses von cloud-init
Um festzustellen, ob cloud-init ohne Fehler abgeschlossen wurde, verwenden wir den folgenden Befehl:
cloud-init status --wait
Es wird einer der folgenden Status gedruckt:
status: not started
status: running
status: done
status: error - done
status: error - running
status: degraded done
status: degraded running
status: disabled
Der Befehl, um cfn-signal dazu zu bringen, den korrekten Fertigstellungsstatus zu melden, sieht also wie folgt aus:
cloud-init status --wait | grep -q '^status: done$'
/opt/aws/bin/cfn-signal \
--stack "$STACK_NAME" \
--resource "$LOGICAL_RESOURCE_ID" \
--region "$AWS_REGION" \
--exit-code $?
Wie man cfn-signal von cloud-init abhängig macht
Alle systemd-Einheiten, die zusammen die cloud-init-Funktionalität bereitstellen, werden durch das cloud-init.target gruppiert.
cloud-init.target
├─cloud-config.service
├─cloud-final.service
├─cloud-init-local.service
└─cloud-init.service
Um cfn-signal nach Abschluss von cloud-init auszuführen, definieren wir den Dienst als abhängig von cloud-init.target, indem wir die Anweisungen Wants und After in der systemd Service Unit verwenden:
Description=Signal completion of cloud-init to CFN
Wants=cloud-init.target
After=cloud-init.target
Wir verwenden Wants anstelle von Required, denn wir möchten, dass das Programm läuft, auch wenn cloud-init fehlschlägt.
Die Diensteinheit cfn-signal
Die gesamte cfn-signal-Diensteinheit sieht nun wie folgt aus:
#
# When cloud-init completed successfully, report this to CFN
# using cfn-signal
#
[Unit]
Description=Signal completion of cloud-init to CFN
Want=cloud-init.target
After=cloud-init.target
[Service]
Type=oneshot
ExecStart=/bin/sh -xc '\
cloud-init status --wait | grep -q '^status: done$'; \
/opt/aws/bin/cfn-signal \
--stack "$STACK_NAME" \
--resource "$LOGICAL_RESOURCE_ID" \
--region "$AWS_REGION" \
--exit-code $? \
'
[Install]
WantedBy=default.target
Sie können diesen Dienst in Ihr eigenes VM-Basis-Image einbinden oder ihn in Ihre CloudFormation-Vorlage aufnehmen.
Integration in die CloudFormation-Vorlage
Der cfn-signal-Dienst wird in die CloudFormation-Vorlage integriert, indem der Stack-Name, die Region und die logische Ressourcenkennung als Umgebungsvariablen in die systemd-Dienstüberschreibungskonfiguration für den cfn-signal-Dienst in /etc/systemd/system/cfn-signal.service.d/override.conf aufgenommen werden:
ClusterASGLaunchTemplate:
Type: AWS::EC2::LaunchTemplate
Properties:
LaunchTemplateName: ClusterASG
LaunchTemplateData:
ImageId: !Ref AL2023
InstanceType: t3.micro
IamInstanceProfile:
Arn: !GetAtt 'InstanceProfile.Arn'
UserData: !Base64
Fn::Sub: |
#cloud-config
write_files:
- path: /etc/systemd/system/cfn-signal.service.d/override.conf
permissions: '0644'
content: |
[Service]
Environment="AWS_REGION=${AWS::Region}"
Environment="STACK_NAME=${AWS::StackName}"
Environment="LOGICAL_RESOURCE_ID=ClusterASG"
runcmd:
- sudo systemctl daemon-reload
- sudo systemctl enable --now --no-block cfn-signal
Wenn cloud-init die Datei nicht erstellen kann, kann cfn-signal keine erfolgreiche Fertigstellung melden und CloudFormation wartet mit einer Zeitüberschreitung auf das Signal. Wenn cloud-init cfn-signal erfolgreich konfigurieren kann und in einem anderen Teil von cloud-init Fehler aufgetreten sind, wird unmittelbar nach Abschluss von cloud-init ein Fail-Signal gesendet.
Demonstration
Um eine vollständige Demonstration zu sehen, setzen Sie den Beispielstapel ein:
curl -sS -o sample.yaml \
https://gist.github.com/mvanholsteijn/a85dc4355985477a0aec2aeb3c27eab1
aws cloudformation deploy \
--stack-name cfn-signal \
--template-file ./sample.yaml \
--capabilities CAPABILITY_IAM \
--parameter-overrides ExitCode=0
Nun ahmen Sie eine Aktualisierung mit einem Fehler in den runcmd-Skripten nach, indem Sie einen ExitCode gleich 1 angeben:
aws cloudformation deploy \
--stack-name cfn-signal \
--template-file ./sample.yaml \
--capabilities CAPABILITY_IAM \
--parameter-overrides ExitCode=1
Dadurch wird ein Fehlersignal an CloudFormation gemeldet und die Aktualisierung abgebrochen.
Fazit
Mit systemd können wir eine cfn-signal-Einheit konfigurieren, die den Status von cloud-init meldet, nachdem cloud-init ausgeführt wurde. Dadurch können wir CloudFormation nur dann benachrichtigen, wenn cloud-init die Instanz erfolgreich initialisiert hat. Dadurch wird die Wahrscheinlichkeit von falsch konfigurierten Instanzen in unserer Umgebung verringert.
Bild von WikimediaImages von Pixabay
Verfasst von

Mark van Holsteijn
Mark van Holsteijn is a senior software systems architect at Xebia Cloud-native solutions. He is passionate about removing waste in the software delivery process and keeping things clear and simple.
Unsere Ideen
Weitere Blogs
Contact



