Blog

Verwendung von systemd zur Orchestrierung von cloud-init mit cfn-signal in AWS CloudFormation

Mark van Holsteijn

Aktualisiert Oktober 20, 2025
4 Minuten

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.

Contact

Let’s discuss how we can support your journey.