Abordagem do sistema para variáveis ​​no Ansible

estilo de código ansible devops

Ei! Meu nome é Denis Kalyuzhny Trabalho como engenheiro no departamento de automação de processos de desenvolvimento. Todos os dias, novas versões de aplicativos são lançadas em centenas de servidores de campanha. E neste artigo compartilho minha experiência de uso do Ansible para esses fins.

Este guia oferece uma maneira de organizar variáveis ​​na implantação. Este guia é destinado àqueles que já usam papéis em seus manuais e leram Melhores Práticas, mas enfrenta problemas semelhantes:

  • Tendo encontrado uma variável no código, é impossível entender imediatamente pelo que ela é responsável;
  • Existem diversas funções e as variáveis ​​precisam estar associadas a um valor, mas isso simplesmente não funciona;
  • Ter dificuldade em explicar aos outros como funciona a lógica das variáveis ​​em seus manuais

Encontramos esses problemas em projetos em nossa empresa, e como resultado chegamos a regras para projetar variáveis ​​em nossos manuais, que até certo ponto resolveram esses problemas.

Abordagem do sistema para variáveis ​​no Ansible

Variáveis ​​em funções

Uma função é um objeto separado do sistema de implantação. Como qualquer objeto do sistema, ele deve possuir uma interface para interação com o restante do sistema. Essa interface são variáveis ​​de função.

Tomemos, por exemplo, o papel api, que instala um aplicativo Java no servidor. Que variáveis ​​ele pode ter?

Abordagem do sistema para variáveis ​​no Ansible

As funções variáveis ​​​​podem ser divididas em 2 tipos de acordo com o tipo:

1. Свойства
    a) независимые от среды
    б) зависимые от среды
2. Связи
    a) слушатели 
    б) запросы внутри системы
    в) запросы в среду

Propriedades variáveis são variáveis ​​que determinam o comportamento de uma função.

Variáveis ​​de consulta - são variáveis ​​cujo valor é utilizado para designar recursos externos à função.

Ouvintes variáveis - são variáveis ​​cujo valor é utilizado para formar variáveis ​​de solicitação.

Por outro lado, 1a, 2a, 2b são variáveis ​​que não dependem do ambiente (hardware, recursos externos, etc.) e podem ser preenchidas com valores padrão na função de padrões. Porém, é impossível preencher variáveis ​​do tipo 1.b e 2.c com valores diferentes de ‘exemplo’, pois elas mudarão de povoamento para povoamento dependendo do ambiente.

Estilo de código

  • O nome da variável deve começar com o nome da função. Isso tornará mais fácil descobrir no futuro qual é a função da variável e pelo que ela é responsável.
  • Ao usar variáveis ​​em funções, você deve seguir o princípio do encapsulamento e usar variáveis ​​definidas na própria função ou nas funções das quais a atual depende.
  • Evite usar dicionários para variáveis. Ansible não permite substituir convenientemente valores individuais em um dicionário.

    Exemplo de uma variável ruim:

    myrole_user:
        login: admin
        password: admin

    Aqui login é a variável independente e senha é a variável dependente. Mas
    já que eles estão combinados em um dicionário, você terá que especificá-lo completamente
    Sempre. O que é muito inconveniente. Melhor assim:

    myrole_user_login: admin
    myrole_user_password: admin

Variáveis ​​em manuais de implantação

Ao compilar um manual de implantação (doravante denominado manual), aderimos à regra de que ele deve ser colocado em um repositório separado. O mesmo que os papéis: cada um em seu próprio repositório git. Isso permite que você entenda que as funções e o playbook são objetos diferentes e independentes do sistema de implantação e que as alterações em um objeto não devem afetar a operação do outro. Isso é conseguido alterando os valores padrão das variáveis.

Ao compilar um playbook, para resumir, é possível substituir os valores padrão das variáveis ​​de função em dois locais: nas variáveis ​​do playbook e nas variáveis ​​de inventário.

mydeploy                        # Каталог деплоя
├── deploy.yml                  # Плейбук деплоя
├── group_vars                  # Каталог переменных плейбука
│   ├── all.yml                 # Файл для переменных связи всей системы
│   └── myapi.yml               # Файл переменных свойств группы myapi
└── inventories                 #
    └── prod                    # Каталог окружения prod
        ├── prod.ini            # Инвентори файл
        └── group_vars          # Каталог для переменных инвентори
            └── myapi           #
                ├── vars.yml    # Средозависимые переменные группы myapi
                └── vault.yml   # Секреты (всегда средозависимы) *

* - Variáveis ​​e cofres

A diferença é que as variáveis ​​do playbook são sempre usadas ao chamar playbooks localizados no mesmo nível que ele. Isso significa que essas variáveis ​​são ótimas para alterar os valores padrão de variáveis ​​independentes do ambiente. Por outro lado, as variáveis ​​de inventário serão usadas apenas para um ambiente específico, o que é ideal para variáveis ​​específicas do ambiente.

É importante observar que a prioridade da variável não permitirá que você substitua as variáveis ​​primeiro nas variáveis ​​do playbook e depois separadamente em um inventário.

Isto significa que já nesta fase é necessário decidir se a variável depende do ambiente ou não e colocá-la no local adequado.

Por exemplo, em um projeto, a variável responsável por habilitar o SSL ficou muito tempo dependente do ambiente, pois não pudemos habilitar o SSL por motivos alheios ao nosso controle em um dos estandes. Depois de corrigirmos esse problema, ele se tornou independente do ambiente e passou para as variáveis ​​do playbook.

Variáveis ​​de propriedade para grupos

Vamos expandir nosso modelo na Figura 1 adicionando dois grupos de servidores com um aplicativo Java diferente, mas com configurações diferentes.

Abordagem do sistema para variáveis ​​no Ansible

Vamos imaginar como será o manual neste caso:

- hosts: myapi
  roles:
    - api

- hosts: bbauth
  roles:
    - auth

- hosts: ghauth
  roles:
    - auth

Temos três grupos no manual, portanto, é imediatamente recomendado criar o mesmo número de arquivos de grupo nas variáveis ​​de inventário group_vars e nas variáveis ​​do manual. Um arquivo de grupo, neste caso, é uma descrição de um componente do aplicativo acima no manual. Ao abrir um arquivo de grupo nas variáveis ​​do playbook, você vê imediatamente todas as diferenças do comportamento padrão das funções instaladas no grupo. Nas variáveis ​​de inventário: diferenças no comportamento do grupo de povoamento para povoamento.

Estilo do código

  • Tente não usar variáveis ​​​​host_vars, pois elas não descrevem o sistema, mas apenas um caso especial, que no futuro levará a perguntas: “Por que este host é diferente dos outros?”, cuja resposta é não sempre fácil de encontrar.

Variáveis ​​de comunicação

No entanto, é disso que se tratam as variáveis ​​de propriedade, mas e as variáveis ​​de comunicação?
A diferença é que devem ter o mesmo significado em grupos diferentes.

No começo foi a ideia use uma construção monstruosa como:
hostvars[groups['bbauth'][0]]['auth_bind_port'], mas eles imediatamente rejeitaram
porque tem desvantagens. Em primeiro lugar, volume. Em segundo lugar, a dependência de um hospedeiro específico do grupo. Em terceiro lugar, antes de iniciar a implantação, é necessário coletar fatos de todos os hosts se não quisermos obter um erro de variável indefinida.

Como resultado, optou-se pela utilização de variáveis ​​de comunicação.

Variáveis ​​de comunicação - são variáveis ​​que pertencem ao manual e são necessárias para conectar objetos do sistema.

Variáveis ​​de comunicação são preenchidas em variáveis ​​gerais do sistema group_vars/all/vars e são formados removendo todas as variáveis ​​de ouvinte de cada grupo e adicionando o nome do grupo do qual o ouvinte foi removido ao início da variável.

Isso garante a uniformidade e a não sobreposição de nomes.

Vamos tentar vincular as variáveis ​​do exemplo acima:

Abordagem do sistema para variáveis ​​no Ansible

Vamos imaginar que temos variáveis ​​que dependem umas das outras:

# roles/api/defaults:
# Переменная запроса
api_auth1_address: "http://example.com:80"
api_auth2_address: "http://example2.com:80"

# roles/auth/defaults:
# Переменная слушатель
auth_bind_port: "20000"

Vamos colocar isso em variáveis ​​comuns group_vars/all/vars todos os ouvintes e adicione o nome do grupo ao título:

# group_vars/all/vars
bbauth_auth_bind_port: "20000"
ghauth_auth_bind_port: "30000"

# group_vars/bbauth/vars
auth_bind_port: "{{ bbauth_auth_bind_port }}"

# group_vars/ghauth/vars
auth_bind_port: "{{ ghauth_auth_bind_port }}"

# group_vars/myapi/vars
api_auth1_address: "http://{{ bbauth_auth_service_name }}:{{ bbauth_auth_bind_port }}"
api_auth2_address: "http://{{ ghauth_auth_service_name }}:{{ ghauth_auth_bind_port }}"

Agora, alterando o valor do conector, teremos certeza de que a solicitação irá para o mesmo local onde está localizada a porta.

Estilo do código

  • Como funções e grupos são objetos de sistema diferentes, eles precisam ter nomes diferentes, então as variáveis ​​de link indicarão com precisão que pertencem a um grupo específico de servidores, e não a uma função no sistema.

Arquivos dependentes do ambiente

As funções podem usar arquivos que diferem de ambiente para ambiente.

Um exemplo de tais arquivos são os certificados SSL. Armazene-os em formato de texto
em uma variável não é muito conveniente. Mas é conveniente armazenar o caminho para eles dentro de uma variável.

Por exemplo, usamos a variável api_ssl_key_file: "/path/to/file".

Como é óbvio que o certificado chave mudará de ambiente para ambiente, esta é uma variável dependente do ambiente, o que significa que deve estar localizada no arquivo
group_vars/myapi/vars inventário de variáveis ​​e contém o valor 'por exemplo'.

A maneira mais conveniente neste caso é colocar o arquivo-chave no repositório do playbook ao longo do caminho
files/prod/certs/myapi.key, então o valor da variável será:
api_ssl_key_file: "prod/certs/myapi.key". A comodidade está no fato de os responsáveis ​​pela implantação do sistema em um estande específico também possuírem um espaço próprio no repositório para armazenar seus arquivos. Ao mesmo tempo, ainda é possível especificar o caminho absoluto para o certificado no servidor, caso os certificados sejam fornecidos por outro sistema.

Vários estandes em um ambiente

Muitas vezes há necessidade de implantar vários estandes quase idênticos no mesmo ambiente com diferenças mínimas. Neste caso, dividimos as variáveis ​​dependentes do ambiente entre aquelas que não mudam dentro deste ambiente e aquelas que mudam. E transferimos este último diretamente para os próprios arquivos de inventário. Após esta manipulação, é possível criar outro inventário diretamente no diretório do ambiente.

Ele reutilizará o inventário group_vars e também poderá redefinir algumas variáveis ​​diretamente para si mesmo.

A estrutura de diretório final do projeto de implantação:

mydeploy                        # Каталог деплоя
├── deploy.yml                  # Плейбук деплоя
├── files                       # Каталог для файлов деплоя
│   ├── prod                    # Католог для средозависимых файлов стенда prod
│   │   └── certs               # 
│   │       └── myapi.key       #
│   └── test1                   # Каталог для средозависимых файлов стенда test1
├── group_vars                  # Каталог переменных плейбука
│   ├── all.yml                 # Файл для переменных связи всей системы
│   ├── myapi.yml               # Файл переменных свойств группы myapi
│   ├── bbauth.yml              # 
│   └── ghauth.yml              #
└── inventories                 #
    ├── prod                    # Каталог окружения prod
    │   ├── group_vars          # Каталог для переменных инвентори
    │   │   ├── myapi           #
    │   │   │   ├── vars.yml    # Средозависимые переменные группы myapi
    │   │   │   └── vault.yml   # Секреты (всегда средозависимы)
    │   │   ├── bbauth          # 
    │   │   │   ├── vars.yml    #
    │   │   │   └── vault.yml   #
    │   │   └── ghauth          #
    │   │       ├── vars.yml    #
    │   │       └── vault.yml   #
    │   └── prod.ini            # Инвентори стенда prod
    └── test                    # Каталог окружения test
        ├── group_vars          #
        │   ├── myapi           #
        │   │   ├── vars.yml    #
        │   │   └── vault.yml   #
        │   ├── bbauth          #
        │   │   ├── vars.yml    #
        │   │   └── vault.yml   #
        │   └── ghauth          #
        │       ├── vars.yml    #
        │       └── vault.yml   #
        ├── test1.ini           # Инвентори стенда test1 в среде test
        └── test2.ini           # Инвентори стенда test2 в среде test

Resumindo

Após organizar as variáveis ​​de acordo com o artigo: cada arquivo de variáveis ​​é responsável por uma tarefa específica. E como o arquivo possui determinadas tarefas, tornou-se possível designar alguém responsável pela correção de cada arquivo. Por exemplo, o desenvolvedor da implantação do sistema passa a ser responsável pelo correto preenchimento das variáveis ​​do playbook, enquanto o administrador cujo estande está descrito no inventário é o responsável direto pelo preenchimento do inventário de variáveis.

As funções tornaram-se sua própria unidade de desenvolvimento com sua própria interface, permitindo que o desenvolvedor da função desenvolvesse capacidades em vez de adaptar a função ao sistema. Este problema dizia especialmente respeito aos papéis comuns de todos os sistemas na campanha.

Os administradores de sistema não precisam mais entender o código de implantação. Tudo o que é exigido deles para uma implantação bem-sucedida é preencher os arquivos de variáveis ​​dependentes do ambiente.

Literatura

  1. Documentação

autor

Kalyuzhny Denis Alexandrovich

Fonte: habr.com

Adicionar um comentário