O blog da AWS

Streaming de logs de Windows pods do Amazon EKS para Amazon CloudWatch Logs usando Fluentd

Por Marcio Morales, Principal Solutions Architect da Amazon Web Services e
Bruno Gabriel, Cloud Support Engineer do time de Deployment.

 

Containers são um método de virtualização do sistema operacional que permite executar uma aplicação e suas dependências em processos isolados de recursos. Containers permitem que você empacote facilmente o código, as configurações e as dependências de uma aplicação em componentes fáceis de usar, que oferecem consistência de ambiente, eficiência operacional, produtividade do desenvolvedor e controle de versão.

O uso de containers Windows permite que você obtenha todos os benefícios citados anteriormente, mas também migre aplicações legadas em execução em um sistema operacional sem suporte, como o Windows Server 2003, 2008 e 2008 R2, que agora podem expor todo o ambiente a ameaças à segurança e à não conformidade com regras de segurança.

Ao executar containers na AWS, você tem duas opções a fazer. Primeiro, você escolhe se deseja gerenciar servidores. Você escolhe o AWS Fargate se quiser computação serverless para containers e o Amazon EC2 se precisar de controle sobre a instalação, configuração e gerenciamento do seu ambiente computacional. Em segundo lugar, você escolhe qual orquestrador de containers usar: Amazon Elastic Container Service (ECS) ou Amazon Elastic Kubernetes Service (EKS). Confira mais sobre containers aqui.

Enquanto isso, migrar para uma nova plataforma exige ter a ferramenta certa para o trabalho certo. Este blog post aborda como fazer o stream de logs de IIS gerados em pods Windows para o Amazon CloudWatch Logs, como forma de centralizar os logs.

Como vamos alcançar isso?

Pré-requisitos e premissas:

  • Cluster Amazon EKS (1.14 ou mais recente) instalado e funcionando. Passo a passo.
  • Worker Nodes Windows do Amazon EKS em execução. Passo a passo.
  • Worker Nodes Windows do Amazon EKS devem estar sendo executados na versão Windows Server 2019.
  • Instância EC2 Windows para criar uma imagem de container Docker usando a mesma AMI dos worker nodes.
  • Já ter criado o Amazon Elastic Container Registry (ECR). Passo a passo.
  • Correta instalação e configuração da CLI da AWS pelo menos na versão 1.18.17, eksctl e kubectl.

Neste blog, faremos as seguintes tarefas:

  1. Verificar se os worker nodes Windows estão funcionando.
  2. Associar um provedor de OIDC ao cluster Amazon EKS.
  3. Criar a política, a Role e o namespace Kubernetes do IAM para serem usados na conta de serviço.
  4. Criar uma imagem de container Windows contendo IIS e LogMonitor.
  5. Criar uma imagem de container Windows contendo Fluentd
  6. Implantar uma imagem de container Windows contendo Fluentd como um Daemonset
  7. Implantar a imagem de container Windows contendo IIS e LogMonitor.
  8. Acessar os pods de IIS para gerar logs. (Opcional)
  9. Verificar registros no Amazon CloudWatch Logs.

 

1. Verifique se os worker nodes Windows estão funcionando.

1.1 Verifique se seus worker nodes Windows retornam status como Ready. Execute o seguinte comando:

kubectl get nodes -o wide 

1.2 Você deve ter certeza de que seu cluster EKS Windows tenha os pods vpc-admission-webhook e vpc-resource-controller retornados como Running. Execute o seguinte comando:

kubectl get pods -A | grep vpc

2. Associe um provedor de OIDC ao cluster Amazon EKS.

2.1 Para configurar IAM Roles para contas de serviço no Amazon EKS, é necessário associar um OIDC ao cluster Amazon EKS. Verifique se o seu cluster Amazon EKS oferece suporte ao OIDC executando o seguinte comando:

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

Substitua o cluster_name pelo nome do seu cluster do Amazon EKS.

Substitua o region-id pela região na qual seu cluster Amazon EKS foi criado.

Output:

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

2.2 Se você precisar criar a associação com o OIDC, execute o seguinte comando:

eksctl utils associate-iam-oidc-provider  --region region-id --cluster cluster_name --approve
  • Substitua o cluster_name pelo nome do seu cluster do Amazon EKS.
Substitua region-id pela região na qual seu cluster Amazon EKS foi criado
  • Substitua o cluster_name pelo nome do seu cluster do Amazon EKS.
  • Substitua region-id pela região na qual seu cluster Amazon EKS foi criado.

3. Crie a política, a Role e o namespace Kubernetes do IAM para serem usados na conta de serviço.

A conta do serviço IAM deve ter uma política anexada contendo permissões do Amazon CloudWatch, que permita que o cluster de Amazon EKS crie, liste e adicione eventos de log no stream de registros.

3.1 Crie um arquivo JSON contendo a seguinte permissão para ser usado como política do 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 Crie a política do IAM executando o seguinte comando:

aws iam create-policy --policy-name MyPolicy --policy-document file://File Path
  • Substitua MyPolicy pelo nome desejado da sua política.
  • Substitua o File Path pelo caminho do arquivo JSON da política.

3.3 Namespaces de Kubernetes fornecem um escopo para nomes e organizam sua carga de trabalho dentro do cluster. Os nomes dos recursos precisam ser exclusivos em um namespace, mas não em todos os namespaces. Cada recurso no Kubernetes só pode estar em um namespace. Para esta postagem do blog, crie o namespace chamado amazon-cloudwatch com o arquivo YAML abaixo:

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

3.4 Crie a conta de serviço do IAM e anexe a política criada anteriormente. Execute o seguinte comando:

eksctl create iamserviceaccount --cluster cluster_name \
--attach-policy-arn PolicyARN \
--name fluentd-windows --namespace amazon-cloudwatch --approve \
--region region-id
  • Substitua o cluster_name pelo nome do seu cluster do Amazon EKS.
  • Substitua o PolicyARN pelo ARN da sua política.
  • Substitua region-id pela região na qual seu cluster Amazon EKS foi criado.
  • O comando acima só funcionará se você já tiver criado seu cluster Amazon EKS usando eksctl. Se você recebeu a seguinte mensagem: cluster was not created with eksctl, use as instruções nas guias AWS Management Console ou AWS CLI. Além disso, você deve ter a conta de serviço Kubernetes já criada nos namespaces amazon-cloudwatch. Para criar a conta de serviço, execute o seguinte comando:
kubectl create serviceaccount fluentd-windows --namespace amazon-cloudwatch

4. Crie uma imagem de container Windows contendo o LogMonitor

4.1 Para testar a funcionalidade explicada neste blog, crie uma imagem de container Windows contendo o IIS e o LogMonitor. Para obter mais instruções sobre como usar o LogMonitor, acesse o repositório oficial do GitHub.

No exemplo abaixo, há um Dockerfile para criar a imagem do container Windows contendo o IIS e 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
Esse exemplo de configuração do LogMonitorConfig recupera todos os arquivos de log com a extensão .log salvos em C:\inetpub\logs e em subdiretórios, incluindo os logs de acesso do 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

Quando o build for concluído, faça push da imagem para seu ECR registry.

5. Crie uma imagem de container Windows contendo Fluentd

5.1 Para agregar logs de pods Kubernetes, mais especificamente os logs do Docker, usaremos o Windows Server Core como imagem base, o Fluentd RubyGems para analisar e reescrever os logs, e o aws-sdk-cloudwatchlogs RubyGems for Amazon CloudWatch Log para autenticação e comunicação com os serviços da AWS.

O Dockerfile criará a imagem do container contendo todos os requisitos. Esse Dockerfile usa um recurso do Docker chamado Multi Stage Build, reduzindo o tamanho final do container em 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"]

Quando a compilação for concluída, faça push da imagem para seu ECR registry. Se você estiver enfrentando um congelamento durante o processo de build, execute o seguinte comando no servidor de build para desativar o monitoramento em tempo real do Docker durante a fase de compilação.

Set-MpPreference -DisableRealtimeMonitoring $true

5.2 Para configurar o Fluentd, precisamos injetar no container os arquivos fluent.conf e containers.conf. Como estamos usando o Kubernetes, usaremos um objeto chamado ConfigMap para montar os arquivos de configuração diretamente dentro do pod. ConfigMaps permitem fazer o decouple de configuração do aplicação do código fonte. Você pode alterar sua configuração sem ter que reconstruir a imagem do container.

---
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
  • Substitua o valor region-id pela região na qual seu cluster Amazon EKS foi criado.
  • Substitua o valor cluster_name pelo nome do seu cluster Amazon EKS.
kubectl apply -f configmap.yaml

6. Implante uma imagem de container Windows contendo Fluentd como um Daemonset

Um DaemonSet garante que todos (ou alguns) nodes executem uma cópia de um pod. À medida que os nodes são adicionados ao cluster, os pods também são adicionados a eles. Para garantir que todos os worker nodes Windows tenham uma cópia do pod Windows Fluentd, vamos implantar um DaemonSet usando o arquivo de implantação mencionado abaixo.

6.1 Crie um arquivo de implantação usando o seguinte conteúdo:

---
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
  • Substitua a imagem Fluentd-ECRRepository/tag pelo endereço ECR e pela tag gerados na etapa 5.1

6.2 Implante o pod usando o seguinte comando:

kubectl apply -f deploymentfilename.yaml

Você deve garantir que seus pods do Fluentd retornem como Running antes da próxima etapa.

  • Para que os pods atinjam o estado Running, pode levar aproximadamente 7 minutos, devido ao tamanho do container. Você pode verificar o status com este comando:
kubectl get pods -A | grep fluentd

7. Implante uma imagem de container Windows contendo IIS e LogMonitor

A implantação garantirá que o número de réplicas (pods) desejadas, 2 (dois) nesse caso, seja executado nos worker nodes Windows com base no atributo nodeSelector “beta.kubernetes.io/os: windows”. O serviço criará um IP virtual dentro do Kubernetes para balancear a carga do tráfego entre os pods.

7.1 Crie um arquivo de implantação usando o seguinte conteúdo:

---
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
  • Substitua a imagem do IIS-ECRrepository/tag pelo endereço ECR e pela tag gerados na etapa 4.1

7.2 Implante o pod usando o seguinte comando:

kubectl apply -f deploymentfilenameiis.yaml

7. Acesse os pods do IIS para gerar logs (opcional)

7.1 Esta é uma etapa opcional. Você pode esperar que o tráfego original chegue ao container ou forçá-lo a ver rapidamente os resultados. Para gerar logs diretamente no container, execute o seguinte comando:

kubectl -it exec <your_winiis_pod_name> powershell

7.2 De dentro do container, execute o seguinte comando:

curl winiis -UseBasicParsing

8. Verificando logs no Amazon CloudWatch Logs

8.1 Para verificar se os logs foram transmitidos com sucesso para os log streams. Acesse o console do Amazon CloudWatch e clique no log group /EKS/cluster_name/windows e no log stream desejado, que é mapeado para seu pod.

8.2 Como você pode ver, os logs de IIS agora estão sendo transmitidos para o log stream.

Conclusão

Usando o Amazon CloudWatch Logs para centralizar todos os logs de Windows pods, faz com que o administrador identifique rapidamente os problemas da aplicação, obtenha visibilidade operacional e insights para a empresa.

 

Este artigo foi traduzido do Blog da AWS em Inglês.

 


Sobre os autores

Marcio Morales é Principal Solutions Architect da Amazon Web Services. Marcio é um SME global para Windows Containers e ajuda os clientes da AWS a projetar, criar, proteger e otimizar cargas de trabalho de Windows Containers na AWS.

 

 

 

Bruno Gabriel Bruno Gabriel é um Cloud Support Engineer do time de Deployment.

 

 

 

 

Revisores

Bruno Lopes é Senior Solutions Architect no time da AWS LATAM. Trabalha com soluções de TI há mais de 14 anos, tendo em seu portfólio inúmeras experiências em workloads Microsoft, ambientes híbridos e capacitação técnica de clientes como Technical Trainer e Evangelista. Agora atua como um Arquiteto de Soluções, unindo todas as capacidades para desburocratizar a adoção das melhores tecnologias afim de ajudar os clientes em seus desafios diários.

 

 

 

 

Luciano Bernardes trabalha atualmente como Sr Solutions Architect na AWS, especializado em workloads Microsoft. Com 15 anos de experiência no mercado, trabalhou a maior parte em consultoria técnica especializada em Microsoft, em clientes de várias verticais, com demandas voltadas para infraestrutura on-premises e em nuvem. Como SA, trabalha próximo a clientes e parceiros de consultoria em LATAM e US, para apoiá-los em tomadas de decisão e revisão de arquitetura de workoads Microsoft na nuvem AWS.