Otimização de serviços Go no Kubernetes

  • Thread starter Thread starter ClaudioGodoy
  • Start date Start date
C

ClaudioGodoy

As configurações do ambiente execução do Go podem impactar o comportamento de um serviço em determinado ambiente.

Entender quais configurações são necessárias é imprescindível quando se trata de garantir o máximo de eficiência e desempenho de um serviço escrito em Go.

A configuração GOMAXPROCS dita o comportamento do serviço em relação ao consumo de CPU.

  • GOMAXPROCS: Número de "threads" disponíveis para o processo. O valor padrão é o número de núcleos da CPU.

Neste artigo, vou descrever minha experiência em busca da configuração ideal para um serviço hospedado no Kubernetes.



GOMAXPROCS e goroutines​


A documentação oficial do Go refere-se às goroutines como sendo um tipo de thread mais leve. Embora correta, essa afirmação pode gerar confusões.

Uma goroutine é uma estrutura usada pelo runtime da qual conterá uma pilha de execução, onde o próprio runtime pode gerenciar sua execução. Para gerenciar a execução das rotinas, o Go implementa três componentes principais, introduzidos por Dmitry Vyukov em seu trabalho Scalable Go Scheduler Design Doc.

De forma resumida, são eles:

  • G (Goroutine): Contem a pilha de execução e todos artefatos necessários para execução, descrito na documentação como um tipo de thread, porém muito mais leve.
  • M (Thread): M é a forma mais comum de referenciar uma thread do sistema operacional.
  • P (Processor): Processor é uma estrutura lógica introduzida por Dmitry Vyukov para minimizar o número de goroutines que ficam em estado de espera quando uma thread M realiza uma operação bloqueante, conhecida como sys-call. O Processor implementa uma série de algoritmos que controlam a distribuição das rotinas entre as threads disponíveis. Vale lembrar que existe uma relação íntima entre um Processor P e uma Thread M, porque o P é uma estrutura lógica e, no fim do dia, vai rodar no contexto de uma Thread M.

A variável de ambiente GOMAXPROCS controla o número máximo de Processors disponíveis para nossa aplicação. Para facilitar o entendimento deste ponto, o GOMAXPROCS não controla o número máximo de Threads M disponíveis para nossa aplicação. Em outras palavras, ela não inclui o número de threads bloqueadas, realizando uma chamada para o kernel do SO.

Em outras palavras, o GOMAXPROCS controla o número máximo de threads que podem ser processadas de forma concorrente pelo processador do sistema operacional. O valor padrão é o número de núcleos do processador do host da aplicação.

Definir um valor muito alto para o GOMAXPROCS pode gerar um grande número de trocas de contexto da CPU.



GOMAXPROCS no Kubernetes​


O GOMAXPROCS ainda não tem uma integração nativa com os limites definidos para um pod do Kubernetes.

Por exemplo, um pod com limite de 1 núcleo de CPU, rodando em um node com 64 núcleos de CPU, vai acabar tendo o valor padrão GOMAXPROCS=64.

A empresa Uber realizou uma série de estudos em relação aos comportamentos desse desencontro de configurações no repositório do Github: uber-go/automaxprocs.

Configurar GOMAXPROCS na definição do contêiner​


Para resolver esse problema, podemos configurar a variável nas especificações do contêiner da seguinte forma:

Code:
env:
- name: GOMAXPROCS
  valueFrom:
    resourceFieldRef:
      resource: limits.cpu

Imagine uma imagem docker, contendo a seguinte aplicação:

Code:
package main

import (
    "fmt"
    "runtime"
    "runtime/debug")

func main() {
    fmt.Printf("GOMAXPROCS: %d\n", runtime.GOMAXPROCS(0))
}

Podemos aplicar a configuração mencionada anteriormente na definição de um contêiner, como no exemplo abaixo:

Code:
apiVersion: v1
kind: Pod
metadata:
  name: app
spec:
  containers:
  - name: app
    image: imagem-da-aplicação    
    resources:
      limits:
        cpu: 1500m
    env:
    - name: GOMAXPROCS
      valueFrom:
        resourceFieldRef:
          resource: limits.cpu

O resultado que você terá, ao conferir os logs de execução deste pod é:

Code:
GOMAXPROCS: 2

Conclusão​


Entender a relação entre o número de núcleos da CPU, threads e goroutines é essencial para elevar o nível de eficiência do seu serviço Go. Este artigo pode servir de base para que você realize e aprofunde seus estudos sobre o assunto proposto, para que possa ter uma grande consciência da estratégia que pretende utilizar, mudando ou não a configuração GOMAXPROCS.

Continue reading...
 
Back
Top