Blog de Amazon Web Services (AWS)

Transmisión de registros desde pods de Windows desde Amazon EKS a Amazon CloudWatch Logs mediante Fluentd

Por Marcio Morales, arquitecto principal de soluciones de Amazon Web Services y
Bruno Gabriel, ingeniero de soporte en la nube del equipo de implementación.

 

Los contenedores son un método de virtualización del sistema operativo que permite ejecutar una aplicación y sus dependencias en procesos de recursos aislados. Los contenedores permiten empaquetar fácilmente el código, las configuraciones y las dependencias de una aplicación en componentes fáciles de usar que proporcionan coherencia ambiental, eficiencia operativa, productividad de los desarrolladores y control de versiones.

El uso de contenedores de Windows permite obtener todas las ventajas mencionadas anteriormente, pero también migrar aplicaciones antiguas que se ejecutan en un sistema operativo no compatible, como Windows Server 2003, 2008 y 2008 R2, lo que ahora puede exponer a todo el entorno a amenazas de seguridad y al incumplimiento de las normas de seguridad.

Al ejecutar contenedores en AWS, tiene dos opciones para elegir. En primer lugar, debe elegir si desea administrar los servidores. Elija AWS Fargate si desea una infraestructura sin servidor para contenedores y Amazon EC2 si necesita controlar la instalación, la configuración y la administración de su entorno informático. En segundo lugar, elige qué orquestador de contenedores usar: Amazon Elastic Container Service (ECS) o Amazon Elastic Kubernetes Service (EKS). Obtenga más información sobre los contenedores aquí.

Mientras tanto, la migración a una nueva plataforma requiere contar con la herramienta adecuada para el trabajo correcto. En esta entrada de blog se explica cómo transmitir los registros de IIS generados en los pods de Windows a Amazon CloudWatch Logs, como una forma de centralizar los registros.

¿Cómo vamos a lograrlo?

Prerrequisitos y premisas:

  • Amazon EKS Cluster (1.14 o posterior) instalado y en ejecución. Paso a paso.
  • Nodos de Amazon EKS Windows Worker en ejecución. Paso a paso.
  • Los nodos Windows Worker de Amazon EKS deben ejecutar la versión Windows Server 2019.
  • Instancia EC2 de Windows para crear una imagen de contenedor de Docker utilizando la misma AMI que los nodos de trabajo.
  • Ya ha creado el Amazon Elastic Container Registry (ECR). Paso a paso.
  • Instalación y configuración correctas de la CLI de AWS al menos en las versiones 1.18.17, eksctl y kubectl.

 

En este blog, realizaremos las siguientes tareas:

  1. Compruebe que los nodos de trabajo de Windows funcionan.
  2. Asocie un proveedor de OIDC al clúster Amazon EKS.
  3. Cree la política de IAM, el rol y el espacio de nombres de Kubernetes para usarlos en la cuenta de servicio.
  4. Cree una imagen de contenedor de Windows que contenga IIS y LogMonitor.
  5. Crea una imagen de contenedor de Windows que contenga Fluentd
  6. Implemente una imagen de contenedor de Windows que contenga Fluentd como Daemonset
  7. Implemente la imagen del contenedor de Windows que contiene IIS y LogMonitor.
  8. Acceda a los pods de IIS para generar registros. (Opcional)
  9. Compruebe los registros en Amazon CloudWatch Logs.

#1 Compruebe que los nodos de trabajo de Windows estén en funcionamiento.

1.1 Compruebe que los nodos de trabajo de Windows devuelvan el estado Listo. Ejecute el siguiente comando:

kubectl get nodes -o wide 

1.2 Debe asegurarse de que su clúster de Windows de EKS tenga los pods vpc-admission-webhook y vpc-resource-controller devueltos como En ejecución. Ejecute el siguiente comando:

kubectl get pods -A | grep vpc

#2 Asocie un proveedor de OIDC al clúster Amazon EKS.

2.1 Para configurar las funciones de IAM para las cuentas de servicio de Amazon EKS, debe asociar un OIDC al clúster de Amazon EKS. Compruebe que su clúster de Amazon EKS admite OIDC mediante la ejecución del siguiente comando:

aws eks describe-cluster --name cluster_name --region region-id --query "cluster.identity.oidc.issuer" --output text

Sustituya cluster_name por el nombre de su clúster de Amazon EKS.

Sustituya el identificador de región por la región en la que se creó el clúster de Amazon EKS.

Salida:

https://oidc.eks.region-id.amazonaws.com/id/EXAMPLED539D4633E53DE1B716D3041E

2.2 Si necesita crear la asociación con el OIDC, ejecute el siguiente comando:

eksctl utils associate-iam-oidc-provider  --region region-id --cluster cluster_name --approve
  • Sustituya cluster_name por el nombre de su clúster de Amazon EKS.
  • Sustituya el identificador de región por la región en la que se creó el clúster de Amazon EKS.

#3 Cree la política de IAM, el rol y el espacio de nombres de Kubernetes para usarlos en la cuenta de servicio.

La cuenta de servicio de IAM debe tener una política adjunta que contenga los permisos de Amazon CloudWatch que permita al clúster de Amazon EKS crear, enumerar y añadir eventos de registro al flujo de registro.

3.1 Cree un archivo JSON que contenga el siguiente permiso para usarlo como política de IAM:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "EKSFluentdCW",
            "Effect": "Allow",
            "Action": [
                "logs:CreateLogStream",
                "logs:DescribeLogGroups",
                "logs:DescribeLogStreams",
                "logs:CreateLogGroup",
                "logs:PutLogEvents"
            ],
            "Resource": "*"
        }
    ]
}
JSON

3.2 Cree la política de IAM mediante la ejecución del siguiente comando:

aws iam create-policy --policy-name MyPolicy --policy-document file://File Path
  • Sustituya MyPolicy por el nombre deseado de su política.
  • Reemplace File Path por la ruta del archivo JSON de la política.

3.3 Los espacios de nombres de Kubernetes proporcionan un ámbito para los nombres y organizan la carga de trabajo dentro del clúster. Los nombres de los recursos deben ser únicos en un espacio de nombres, pero no en todos los espacios de nombres. Cada recurso de Kubernetes solo puede estar en un espacio de nombres. Para esta entrada de blog, crea el espacio de nombres llamado amazon-cloudwatch con el siguiente archivo YAML:

apiVersion: v1
kind: Namespace
metadata:
  name: amazon-cloudwatch
kubectl apply -f namespace.yaml

3.4 Cree la cuenta de servicio de IAM y adjunte la política creada anteriormente. Ejecute el siguiente comando:

eksctl create iamserviceaccount --cluster cluster_name \
--attach-policy-arn PolicyARN \
--name fluentd-windows --namespace amazon-cloudwatch --approve \
--region region-id
  • Sustituya cluster_name por el nombre de su clúster de Amazon EKS. 
  • Sustituya PolicYarn por el ARN de su política.
  • Sustituya el identificador de región por la región en la que se creó el clúster de Amazon EKS. 
  • El comando anterior solo funcionará si ya ha creado su clúster de Amazon EKS con eksctl.  Si recibió el siguiente mensaje: el clúster no se creó con eksctl, siga las instrucciones de las pestañas AWS Management Console o AWS CLI.  Además, debes tener la cuenta de servicio de Kubernetes ya creada en los espacios de nombres de amazon-cloudwatch.  Para crear la cuenta de servicio, ejecute el siguiente comando:
kubectl create serviceaccount fluentd-windows --namespace amazon-cloudwatch

#4 Cree una imagen de contenedor de Windows que contenga LogMonitor

4.1 Para probar la funcionalidad explicada en este blog, cree una imagen de contenedor de Windows que contenga IIS y LogMonitor. Para obtener más instrucciones sobre cómo usar LogMonitor, visita el repositorio oficial de GitHub.

En el ejemplo siguiente, hay un Dockerfile para crear la imagen del contenedor de Windows que contiene IIS y LogMonitor.

FROM mcr.microsoft.com/windows/servercore/iis:windowsservercore-ltsc2019

#Set powershell as default shell
SHELL ["powershell", "-NoLogo", "-Command", "$ErrorActionPreference = 'Stop'; $ProgressPreference = 'SilentlyContinue';"]

#Add X-Forward-For Header to IIS Default website log
RUN Add-WebConfigurationProperty -pspath 'MACHINE/WEBROOT/APPHOST' -filter "system.applicationHost/sites/siteDefaults/logFile/customFields" -name "." "-value @{logFieldName='X-Forwarded-For';sourceName='X-Forwarded-For';sourceType='RequestHeader'}" 

#Add STDOUT LogMonitor binary and config in json format
COPY LogMonitor.exe LogMonitorConfig.json 'C:\LogMonitor\'
WORKDIR /LogMonitor

SHELL ["C:\\LogMonitor\\LogMonitor.exe", "powershell.exe"]
ENTRYPOINT C:\ServiceMonitor.exe w3svc;

LogMonitorConfig.json
Este ejemplo de configuración de LogMonitorConfig recupera todos los archivos de registro con la extensión.log guardados en C:\inetpub\logs y en los subdirectorios, incluidos los registros de acceso a IIS.

{
  "LogConfig": {
    "sources": [
      {
        "type": "EventLog",
        "startAtOldestRecord": true,
        "eventFormatMultiLine": false,
        "channels": [
          {
            "name": "system",
            "level": "Error"
          }
        ]
      },
      {
        "type": "File",
        "directory": "c:\\inetpub\\logs",
        "filter": "*.log",
        "includeSubdirectories": true
      },
      {
        "type": "ETW",
        "providers": [
          {
            "providerName": "IIS: WWW Server",
            "ProviderGuid": "3A2A4E84-4C21-4981-AE10-3FDA0D9B0F83",
            "level": "Information"
          },
          {
            "providerName": "Microsoft-Windows-IIS-Logging",
            "ProviderGuid": "7E8AD27F-B271-4EA2-A783-A47BDE29143B",
            "level": "Information",
            "keywords": "0xFF"
          }
        ]
      }
    ]
  }
}
JSON

Cuando la compilación esté completa, inserte la imagen en su registro de ECR.

#5 Crea una imagen de contenedor de Windows que contenga Fluentd

5.1 Para agregar los registros de pods de Kubernetes, más específicamente los registros de Docker, utilizaremos Windows Server Core como imagen base, Fluentd RubyGems para analizar y reescribir los registros y aws-sdk-cloudwatchlogs RubyGems para Amazon CloudWatch Log para la autenticación y la comunicación con los servicios de AWS.

El Dockerfile creará la imagen del contenedor que contiene todos los requisitos. Este Dockerfile utiliza una función de Docker llamada Multietapa Build, que reduce el tamaño final del contenedor en aproximadamente 600 MB.

FROM mcr.microsoft.com/windows/servercore:ltsc2019 as base

RUN powershell -Command "Set-ExecutionPolicy Bypass -Scope Process -Force; iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))"

RUN choco install -y ruby --version 3.1.2.1 --params "'/InstallDir:C:\ruby31'" \
&& choco install -y msys2 --version 20220904.0.0 --params "'/NoPath /NoUpdate /InstallDir:C:\ruby31\msys64'"
RUN refreshenv \
&& ridk install 3 \
&& echo gem: --no-document >> C:\ProgramData\gemrc \
&& gem install oj -v 3.13.22 \
&& gem install json -v 2.6.2 \
&& gem install rexml -v 3.2.5 \
&& gem install fluentd -v 1.15.3 \
&& gem install win32-service -v 2.3.2 \
&& gem install win32-ipc -v 0.7.0 \
&& gem install win32-event -v 0.6.3 \
&& gem install aws-sdk-cloudwatchlogs \
&& gem install fluent-plugin-concat \
&& gem install fluent-plugin-rewrite-tag-filter \
&& gem install fluent-plugin-multi-format-parser \
&& gem install fluent-plugin-cloudwatch-logs \
&& gem sources --clear-all

RUN powershell -Command "Remove-Item -Force C:\ruby31\lib\ruby\gems\3.1.0\cache\*.gem; Remove-Item -Recurse -Force 'C:\ProgramData\chocolatey'"

FROM mcr.microsoft.com/windows/servercore:ltsc2019
COPY --from=base /ruby31 /ruby31
RUN setx /M PATH "C:\ruby31\bin;%PATH%"
CMD ["powershell", "-command", "fluentd"]

Cuando la compilación esté completa, inserte la imagen en su registro de ECR. Si se bloquea el proceso de compilación, ejecute el siguiente comando en el servidor de compilación para deshabilitar la supervisión en tiempo real de Docker durante la fase de compilación.

Set-MpPreference -DisableRealtimeMonitoring $true

5.2 Para configurar Fluentd, necesitamos inyectar los archivos fluent.conf y containers.conf en el contenedor. Como usamos Kubernetes, utilizaremos un objeto llamado ConfigMap para montar los archivos de configuración directamente dentro del pod. ConfigMaps permite desacoplar la configuración de la aplicación de código fuente. Puede cambiar la configuración sin tener que reconstruir la imagen del contenedor.

---
apiVersion: v1
kind: ConfigMap
metadata:
  name: fluentd-windows
  namespace: amazon-cloudwatch
  labels:
    k8s-app: fluentd-windows
data:
  AWS_REGION: region-id
  CLUSTER_NAME: cluster_name
  fluent.conf: | <match fluent.**> @type null </match>

    @include containers.conf
  containers.conf: | <source> @type tail @id in_tail_container_logs path /var/log/containers/*.log exclude_path ["/var/log/containers/fluentd*"] pos_file /var/log/fluentd-containers.log.pos tag k8s.* read_from_head true <parse> @type "json" time_format %Y-%m-%dT%H:%M:%S.%NZ </parse> </source>
    
    <filter **>
      @type record_transformer
      @id filter_containers_stream_transformer
      <record>
        stream_name ${tag_parts[4]}
      </record>
    </filter>
    
      <match k8s.**>
        @type cloudwatch_logs
        @id out_cloudwatch_logs_containers
        region "#{ENV.fetch('AWS_REGION')}"
        log_group_name "/EKS/#{ENV.fetch('CLUSTER_NAME')}/Windows"
        log_stream_name_key stream_name
        remove_log_stream_name_key true
        auto_create_stream true
        <buffer>
          flush_interval 5
          chunk_limit_size 2m
          queued_chunks_limit_size 32
          retry_forever true
        </buffer>
      </match>
YAML
  • Sustituya el valor region-id por la región en la que se creó el clúster de Amazon EKS.
  • Sustituya el valor cluster_name por el nombre de su clúster de Amazon EKS. 
kubectl apply -f configmap.yaml

#6 Implemente una imagen de contenedor de Windows que contenga Fluentd como Daemonset

Un DaemonSet garantiza que todos (o algunos) nodos ejecuten una copia de un pod. A medida que se agregan nodos al clúster, también se les agregan pods. Para asegurarnos de que todos los nodos de trabajo de Windows tengan una copia del pod de Windows Fluentd, implementaremos un DaemonSet mediante el archivo de implementación que se menciona a continuación.

6.1 Cree un archivo de despliegue con el siguiente contenido:

---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: fluentd-windows
  namespace: amazon-cloudwatch
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: fluentd-windows
  namespace: amazon-cloudwatch
rules:
- apiGroups:
  - ""
  resources:
  - pods
  - namespaces
  verbs:
  - get
  - list
  - watch
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: fluentd-windows
roleRef:
  kind: ClusterRole
  name: fluentd-windows
  apiGroup: rbac.authorization.k8s.io
subjects:
- kind: ServiceAccount
  name: fluentd-windows
  namespace: amazon-cloudwatch
---
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: fluentd-windows
  namespace: amazon-cloudwatch
  labels:
    k8s-app: fluentd-windows
spec:
  selector:
    matchLabels:
      name: fluentd-windows
  template:
    metadata:
      labels:
        name: fluentd-windows
    spec:
      serviceAccount: fluentd-windows
      serviceAccountName: fluentd-windows
      # Because the fluentd requires to write on /etc/fluentd/ but we 
      # mount the config using a configmap which is read-only, 
      # this initContainer needs to be used to copy 
      # from Read Only folder to Read Write folder.
      initContainers:
      - name: copy-fluentd-config
        image: mcr.microsoft.com/windows/servercore:ltsc2019
        command: ['powershell', '-command', 'cp /etc/temp/*.conf /etc/fluent/']
        volumeMounts:
        - name: fluentdconftemp
          mountPath: /etc/temp/
        - name: fluentdconf
          mountPath: /etc/fluent
      containers:
      - name: fluentd-windows
        image: Fluentd-ECRrepository/tag
        env:
          - name: AWS_REGION
            valueFrom:
              configMapKeyRef:
                name: fluentd-windows
                key: AWS_REGION
          - name: CLUSTER_NAME
            valueFrom:
              configMapKeyRef:
                name: fluentd-windows
                key: CLUSTER_NAME
        resources:
          limits:
            memory: 2Gi
          requests:
            cpu: 100m
            memory: 1Gi
        volumeMounts:
        - name: fluentdconftemp
          mountPath: /etc/temp/
        - name: fluentdconf
          mountPath: /etc/fluent
        - name: varlog
          mountPath: /var/log
        - name: varlibdockercontainers
          mountPath: C:\ProgramData\Docker\containers
          readOnly: true
      nodeSelector:
        beta.kubernetes.io/os: windows
      terminationGracePeriodSeconds: 30
      volumes:
      - name: fluentdconftemp
        configMap:
          name: fluentd-windows
      - name: varlog
        hostPath:
          path: C:\var\log
      - name: varlibdockercontainers
        hostPath:
          path: C:\ProgramData\Docker\containers
      - name: fluentdconf
        emptyDir: {}
YAML
  • Sustituya la imagen del repositorio/etiqueta de FluentD-ECR por la dirección ECR y la etiqueta generadas en el paso 5.1

6.2 Despliegue el pod mediante el siguiente comando:

kubectl apply -f deploymentfilename.yaml

Debes asegurarte de que tus pods de Fluentd vuelvan a estar en ejecución antes del siguiente paso.

  • Las cápsulas pueden tardar aproximadamente 7 minutos en llegar al estado de funcionamiento, debido al tamaño del recipiente.  Puede comprobar el estado con este comando:
kubectl get pods -A | grep fluentd

#7 Implemente una imagen de contenedor de Windows que contenga IIS y LogMonitor

La implementación garantizará que el número deseado de réplicas (pods) (pods) (pods), 2 (dos) en este caso, se ejecuten en los nodos de trabajo de Windows basándose en el atributo nodeSelector «beta.kubernetes.io/os: windows». El servicio creará una IP virtual en Kubernetes para equilibrar la carga de tráfico entre los pods.

7.1 Cree un archivo de despliegue con el siguiente contenido:

---
apiVersion: v1
kind: Service
metadata:
  name: winiis
  labels:
    run: winiis
spec:
  ports:
  - port: 80
    protocol: TCP
  selector:
    run: winiis
---
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    run: winiis
  name: winiis
  namespace: default
spec:
  replicas: 2
  revisionHistoryLimit: 10
  selector:
    matchLabels:
      run: winiis
  strategy:
    rollingUpdate:
      maxSurge: 25%
      maxUnavailable: 25%
    type: RollingUpdate
  template:
    metadata:
      labels:
        run: winiis
    spec:
      containers:
      - image: IIS-ECRrepository/tag
        imagePullPolicy: IfNotPresent
        name: winiis
      dnsPolicy: ClusterFirst
      nodeSelector:
        beta.kubernetes.io/os: windows
      restartPolicy: Always
      terminationGracePeriodSeconds: 30
  • Sustituya la imagen del IIS-ECRepository/tag por la dirección ECR y la etiqueta generadas en el paso 4.1
7.2 Despliegue el pod mediante el siguiente comando:

kubectl apply -f deploymentfilenameiis.yaml

#7 Acceda a los pods de IIS para generar registros (opcional)

7.1 Este es un paso opcional. Puede esperar a que el tráfico original llegue al contenedor u obligarlo a ver rápidamente los resultados. Para generar registros directamente en el contenedor, ejecute el siguiente comando:

kubectl -it exec <your_winiis_pod_name> powershell

7.2 Desde el interior del contenedor, ejecute el siguiente comando:

curl winiis -UseBasicParsing

#8 Comprobación de registros en Amazon CloudWatch Logs

8.1 Para comprobar que los registros se transmitieron correctamente a los flujos de registro. Vaya a la consola de Amazon CloudWatch y haga clic en el grupo de registros /EKS/CLUSTER_NAME/Windows y en el flujo de registro deseado, que se asigna a su pod.

8.2 Como puede ver, los registros de IIS ahora se transmiten al flujo de registros.

Conclusión

Al utilizar Amazon CloudWatch Logs para centralizar todos los registros de los pods de Windows, el administrador puede identificar rápidamente los problemas de las aplicaciones, obtener visibilidad operativa y obtener información para la empresa.

 

Este artículo fue traducido del Blog da AWS en Inglés.

 


Acerca de los autores

Marcio Morales es el arquitecto principal de soluciones en Amazon Web Services.  Marcio es una pyme global dedicada a Windows Containers y ayuda a los clientes de AWS a diseñar, crear, proteger y optimizar las cargas de trabajo de Windows Container en AWS.

 

 

 

Bruno Gabriel Bruno Gabriel es un ingeniero de soporte en la nube del equipo de implementación.

 

 

 

 

Revisores

Bruno Lopes es un arquitecto de soluciones sénior en el equipo de AWS LATAM. Lleva más de 14 años trabajando con soluciones de TI, y en su cartera cuenta con numerosas experiencias en cargas de trabajo de Microsoft, entornos híbridos y formación técnica para clientes como Technical Trainer y Evangelista. Ahora actúa como arquitecto de soluciones, combinando todas las capacidades para reducir la burocracia en la adopción de las mejores tecnologías a fin de ayudar a los clientes a superar sus desafíos diarios.

 

 

 

 

Luciano Bernardes trabaja actualmente como arquitecto de soluciones sénior en AWS y se especializa en cargas de trabajo de Microsoft. Con 15 años de experiencia en el mercado, trabajó principalmente en consultoría técnica especializada en Microsoft, con clientes de diversos sectores, con demandas centradas en la infraestructura local y en la nube. Como SA, trabaja en estrecha colaboración con clientes y socios consultores en Latinoamérica y EE. UU. para apoyarlos en la toma de decisiones y la revisión de la arquitectura de las cargas de trabajo de Microsoft en la nube de AWS.

 

 

 

 

JuanMa Silva quien es arquitecto de soluciones con especialidad en Microsoft para México y MCO. Cuenta con 15 años de experiencia en la industria de IT, en posiciones de Sysadmin, consultor para ayudar a migrar clientes a la nube y modernización de aplicaciones, soporte aplicaciones de misión crítica basados en tecnología Microsoft.