Blog

Verwenden Sie CloudWatch LogGroups für die EC2-Protokollierung

Joris Conijn

Aktualisiert Oktober 15, 2025
6 Minuten

Sie können sich vor dem Verlust von Protokollen auf Amazon EC2 schützen, indem Sie CloudWatch Logs verwenden. Konfigurieren Sie den CloudWatch Agent so, dass er Ihre Protokolle in eine LogGroup streamt. Dies schützt Sie vor dem Verlust von Protokollen. Zum Beispiel, wenn die Instanz durch Autoscaling ersetzt wird. Sie sind auch vor Manipulationen der Protokolle geschützt. Ein Angreifer, der sich Zugang zu Ihrem System verschafft hat, kann die Protokolle entfernen. Aber die Protokolle in der LogGroup enthalten die ursprünglichen Protokollzeilen.

Vorbereitungen

Wir benötigen die folgenden Ressourcen:

 InstanceSecurityGroup:
   Type: AWS::EC2::SecurityGroup
   Properties:
     GroupDescription: Security group for the test instance
     VpcId: "{{resolve:ssm:/landingzone/vpc/vpc-id}}"
     SecurityGroupEgress:
       - Description: Allow outbound connectivity to port 443.
         IpProtocol: tcp
         FromPort: 443
         ToPort: 443
         CidrIp: 0.0.0.0/0

 Role:
   Type: AWS::IAM::Role
   Properties:
     PermissionsBoundary: !Sub arn:aws:iam::${AWS::AccountId}:policy/landingzone-workload-permissions-boundary
     AssumeRolePolicyDocument:
       Version: 2012-10-17
       Statement:
         - Effect: Allow
           Action: sts:AssumeRole
           Principal:
             Service: ec2.amazonaws.com
     ManagedPolicyArns:
       - arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore
       - arn:aws:iam::aws:policy/AmazonInspector2ManagedCispolicy
       - arn:aws:iam::aws:policy/CloudWatchAgentServerPolicy

 InstanceProfile:
   Type: AWS::IAM::InstanceProfile
   Properties:
     Roles:
       - !Ref Role

 LogGroup:
   Type: AWS::Logs::LogGroup
   Properties:
     LogGroupName: !Sub ${AWS::StackName}-Instance
     RetentionInDays: !!int 365
  • InstanceSecurityGroup, erlaubt eine ausgehende Verbindung auf 443. Der CloudWatch Agent muss die Protokolle an den CloudWatch Endpunkt streamen.
  • Rolle, enthält die Richtlinien, die es der Instanz erlauben, die Protokolle an die LogGroup zu senden.
  • InstanceProfile, das Profil, das verwendet wird, um die Rolle mit den EC2-Instances zu verbinden.
  • LogGroup, die LogGroup, in der die Protokolle gespeichert werden sollen.

Einrichten der EC2-Instanz

In meinem letzten Blog habe ich darüber geschrieben, wie Sie eine EC2-Instanz mit Infrastructure as Code erstellen können. Wir werden mit diesem Beispiel fortfahren. Anstelle einer einzelnen Instanz werden wir eine autoskalierende Gruppe erstellen.

Zunächst benötigen wir ein LaunchTemplate. Dieses LaunchTemplate enthält einige Metadaten namens AWS::CloudFormation::Init. Die Metadaten enthalten so genannte configSets und Konfigurationsblöcke. Hier ist ein Beispiel, das wir verwenden werden:

 LaunchTemplate:
   Type: AWS::EC2::LaunchTemplate
   Metadata:
     AWS::CloudFormation::Init:
       configSets:
         default:
           - CloudFormationInit
           - CloudWatchLogs
       CloudFormationInit:
         files:
           /etc/cfn/cfn-hup.conf:
             owner: root
             group: root
             mode: 000400
             content: !Sub |-
               [main]
               stack=${AWS::StackId}
               region=${AWS::Region}
           /etc/cfn/hooks.d/cfn-auto-reloader.conf:
             owner: root
             group: root
             mode: 000400
             content: !Sub |-
               [cfn-auto-reloader-hook]
               triggers=post.update
               path=Resources.LaunchTemplate.Metadata.AWS::CloudFormation::Init
               action=/opt/aws/bin/cfn-init -v --stack ${AWS::StackName} --region ${AWS::Region} --resource LaunchTemplate
               runas=root
         services:
           sysvinit:
             cfn-hup:
               enabled: true
               ensureRunning: true
               files:
                 - /etc/cfn/cfn-hup.conf
                 - /etc/cfn/hooks.d/cfn-auto-reloader.conf
       CloudWatchLogs:
         packages:
           yum:
             amazon-cloudwatch-agent: []
         files:
           /opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.d/file_amazon-cloudwatch-agent.json:
             owner: root
             group: root
             mode: 000400
             content: !Sub |-
               {
                 "agent": {
                   "region": "${AWS::Region}",
                   "logfile": "/opt/aws/amazon-cloudwatch-agent/logs/amazon-cloudwatch-agent.log",
                   "debug": false
                 },
                 "logs": {
                   "logs_collected": {
                     "files": {
                       "collect_list": [
                         {
                           "file_path": "/var/log/user-data.log",
                           "log_group_name": "${LogGroup}",
                           "log_stream_name": "{instance_id}/user-data.log"
                         },
                         {
                           "file_path": "/var/log/cfn-hup.log",
                           "log_group_name": "${LogGroup}",
                           "log_stream_name": "{instance_id}/cfn-hup.log"
                         },
                         {
                           "file_path": "/opt/aws/amazon-cloudwatch-agent/logs/amazon-cloudwatch-agent.log",
                           "log_group_name": "${LogGroup}",
                           "log_stream_name": "{instance_id}/amazon-cloudwatch-agent.log"
                         }
                       ]
                     }
                   },
                   "log_stream_name": "default_log_stream"
                 },
                 "metrics": {
                   "append_dimensions": {
                     "AutoScalingGroupName": "${!aws:AutoScalingGroupName}"
                   },
                   "metrics_collected": {
                     "disk": {
                       "measurement": [
                         "used_percent"
                       ],
                       "metrics_collection_interval": 60,
                       "resources": [
                         "/"
                       ]
                     }
                   }
                 }
               }
         services:
           sysvinit:
             amazon-cloudwatch-agent:
               enabled: true
               ensureRunning: true
               files:
                 - /opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.d/file_amazon-cloudwatch-agent.json
   Properties:
     LaunchTemplateData:
       BlockDeviceMappings:
         - DeviceName: /dev/xvda
           Ebs:
             DeleteOnTermination: !!bool true
             Encrypted: !!bool true
             KmsKeyId: !GetAtt EncryptionKey.Arn
             VolumeSize: !!int 32
             VolumeType: gp2
       DisableApiTermination: !!bool true
       IamInstanceProfile:
         Arn: !GetAtt InstanceProfile.Arn
       ImageId: "{{resolve:ssm:/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2}}"
       InstanceType: t3.micro
       SecurityGroupIds:
         - !Ref InstanceSecurityGroup
       MetadataOptions:
         HttpTokens: required
         InstanceMetadataTags: enabled
       UserData:
         Fn::Base64: !Sub |-
           #!/bin/bash -x
           /opt/aws/bin/cfn-init --stack ${AWS::StackName} --region ${AWS::Region} --resource LaunchTemplate
           /opt/aws/bin/cfn-signal -e $? --stack ${AWS::StackName} --region ${AWS::Region} --resource AutoScalingGroup

CloudFormationInit

Der CloudFormationInit enthält eine Konfiguration. Diese Konfiguration wird vom cfn-init-Agenten verwendet. Sie wird mit den AMIs AmazonLinux2 und AmazonLinux2023 vorinstalliert. Unter dem Abschnitt Dateien können Sie sehen, dass wir 2 Dateien definieren:

  • /etc/cfn/cfn-hup.conf
  • /etc/cfn/hooks.d/cfn-auto-reloader.conf

Die Syntax ist recht erklärend. Sie legen einen Eigentümer, eine Gruppe, einen Modus und den Inhalt der Datei fest. Der Inhalt der Datei teilt der EC2-Instanz mit, wo sie die Metadaten finden kann. Beim ersten Start sind diese in den Benutzerdaten enthalten. Aber Sie können die Metadaten im Laufe der Zeit ändern. Dadurch wird sichergestellt, dass die laufenden Instanzen prüfen, ob ein Update verfügbar ist.

Zusammenfassend lässt sich sagen, dass dadurch sichergestellt wird, dass die laufenden EC2-Instanzen mit den angegebenen Metadaten synchron bleiben.

CloudWatchLogs

Die Amazon Linux AMIs werden nicht mit dem CloudWatch Agent geliefert. Sie müssen den Agent zuerst installieren. Sie können dies tun, indem Sie das Paket amazon-cloudwatch-agent unter Pakete bereitstellen.

Jetzt ist es an der Zeit, den Agenten so zu konfigurieren, dass er die Protokolldateien an CloudWatch LogGroups streamt. Wir haben 3 Dateien definiert, die vom Agenten überwacht werden sollen:

  • /var/log/user-data.log
  • /var/log/cfn-hup.log
  • /opt/aws/amazon-cloudwatch-agent/logs/amazon-cloudwatch-agent.log

Sie können diese Liste nach Ihren eigenen Bedürfnissen erweitern. In diesem Beispiel wird jede EC2-Instanz 3 Streams haben. 1 pro Datei und die Instanz-ID wird als Präfix verwendet, Beispiel: i-0000000000/user-data.log.

Sie können auch Messungen an der Instanz selbst vornehmen und diese als CloudWatch Metrik darstellen. In diesem Beispiel erfassen wir einen Prozentsatz des belegten Speicherplatzes des Root-Volumes.

Wir haben jetzt den Agenten konfiguriert. Aber wir müssen auch sicherstellen, dass er läuft. Wenn wir Änderungen an der Konfigurationsdatei vornehmen, müssen wir ihn auch neu starten. Wir tun dies, indem wir auf die Konfigurationsdatei verweisen. Jetzt weiß cfn-init, wenn es die Konfiguration ändert, dass es auch den Agenten neu starten muss.

Benutzerdaten

Die Benutzerdaten stellen sicher, dass die Konfiguration übernommen wird. Da wir cfn-init so konfigurieren, dass die Konfiguration überprüft wird, bleibt sie von nun an synchron. Als Zweites sendet es ein Signal an die AutoScalingGroup-Ressource. Wenn das cfn-init erfolgreich ausgeführt wurde, wird ein Erfolg gemeldet, andernfalls ein Fehler.

Das heißt, wenn die Konfiguration fehlschlägt, erhält die Autoscaling-Gruppe einen Fehler. Infolgedessen wird der Stack zurückgesetzt.

AutoScalingGroup

Die Autoscaling-Gruppe sieht wie folgt aus:

  AutoScalingGroup:
    Type: AWS::AutoScaling::AutoScalingGroup
    CreationPolicy:
      ResourceSignal:
        Timeout: PT5M
    UpdatePolicy:
      AutoScalingRollingUpdate:
        MinInstancesInService: 1
        MaxBatchSize: 1
        PauseTime: PT5M
        WaitOnResourceSignals: !!bool true
    Properties:
      LaunchTemplate:
        LaunchTemplateId: !GetAtt LaunchTemplate.LaunchTemplateId
        Version: !GetAtt LaunchTemplate.LatestVersionNumber
      MinSize: !!int 1
      MaxSize: !!int 2
      DesiredCapacity: !!int 1
      VPCZoneIdentifier:
        - "{{resolve:ssm:/landingzone/vpc/private-subnet-1-id}}"
        - "{{resolve:ssm:/landingzone/vpc/private-subnet-2-id}}"

Wir werden 2 Richtlinien definieren, eine für die Erstellung und eine für die Aktualisierung. Wenn das in den Benutzerdaten konfigurierte Signal fehlschlägt, schlägt die Ressource fehl und löst einen Rollback aus. Wenn die Instanz erfolgreich konfiguriert wurde, wird sie fortgesetzt. In der Gruppe für die automatische Skalierung können Sie die Anzahl der benötigten Instanzen festlegen. In diesem Beispiel ist das Maximum 2 und das Minimum 1. Dadurch wird sichergestellt, dass mindestens eine EC2-Instanz läuft. Wenn eine Ersatzinstanz benötigt wird, wird zunächst eine neue Instanz hinzugefügt, und wenn diese erfolgreich bereitgestellt wird, wird die alte Instanz entfernt. Aus diesem Grund ist der Höchstwert auf 2 gesetzt, damit wir die alte Instanz problemlos ersetzen können.

Fazit

Die Konfiguration einer LogGroup zum Streamen Ihrer Protokolldateien ist nicht schwer. Mit CloudFormation Init können Sie Anwendungen installieren und konfigurieren. In unserem Beispiel haben wir den CloudWatch-Agenten konfiguriert, der die Protokolle streamen wird.

Wenn Sie mit Autoscaling-Ereignissen und/oder bösartigen Angreifern zu tun haben. Sie können den Verlust von Protokollen verhindern, indem Sie Ihre Anwendungsprotokolldateien zentralisieren.

Foto von Pixabay

Verfasst von

Joris Conijn

Joris is the AWS Practise CTO of the Xebia Cloud service line and has been working with the AWS cloud since 2009 and focussing on building event-driven architectures. While working with the cloud from (almost) the start, he has seen most of the services being launched. Joris strongly believes in automation and infrastructure as code and is open to learning new things and experimenting with them because that is the way to learn and grow.

Contact

Let’s discuss how we can support your journey.