ansible devops codestyle
Olá! Meu nome é Trabalho como engenheiro no departamento de automação de processos de desenvolvimento. Todos os dias, novas versões de aplicativos são implantadas em centenas de servidores da empresa. Neste artigo, compartilho minha experiência no uso do Ansible para esses fins.
Este guia oferece uma maneira de organizar variáveis no deployment. Este guia é destinado a aqueles que já utilizam roles em seus playbooks e leram , mas estão enfrentando problemas semelhantes:
- Ao encontrar uma variável no código, não é possível entender imediatamente para que ela serve;
- Existem várias roles, e as variáveis precisam ser associadas a um único valor, mas não conseguimos fazer isso;
- Há dificuldades em explicar a outros como a lógica das variáveis em seus playbooks está estruturada.
Enfrentamos esses problemas em projetos em nossa empresa, o que nos levou a desenvolver regras para a formatação de variáveis em nossos playbooks, que de certa forma resolveram essas questões.

Variáveis em roles
Uma role é um objeto separado do sistema de deployment. Como qualquer objeto do sistema, deve ter uma interface de interação com o restante do sistema. Essa interface é composta pelas variáveis da role.
Vamos pegar, por exemplo, a role api, que instala um aplicativo Java em um servidor. Quais variáveis ela pode ter?

As variáveis da role podem ser divididas em 2 tipos:
1. Propriedades
a) independentes do ambiente
b) dependentes do ambiente
2. Relações
a) ouvintes
b) solicitações dentro do sistema
c) solicitações ao ambiente
Variáveis de propriedades são aquelas que definem o comportamento da role.
Variáveis de solicitação são aquelas cujos valores são utilizados para representar recursos externos, em relação à role.
Variáveis ouvintes são aquelas cujos valores são usados para formar as 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 no defaults da role. No entanto, variáveis dos tipos 1b e 2c não podem ser preenchidas com valores diferentes de 'example', pois elas variarão de stand para stand dependendo do ambiente.
Estilo de código
- O nome da variável deve sempre começar com o nome da role. Isso facilitará, no futuro, entender de qual role a variável se origina e para que ela serve.
- Ao usar variáveis em roles, você deve seguir rigorosamente o princípio da encapsulação e usar variáveis definidas na própria role ou em roles das quais a atual depende.
Tente evitar o uso de dicionários para variáveis. O Ansible não permite substituir facilmente valores individuais em um dicionário.
Exemplo de uma variável ruim:
myrole_user: login: admin password: adminAqui, login é uma variável independente, enquanto password é dependente. Mas
como estão agrupadas em um dicionário, você terá que especificá-la completamente
todas as vezes. O que é muito inconveniente. Melhor assim:myrole_user_login: admin myrole_user_password: admin
Variáveis em playbooks de deployment
Ao elaborar um playbook de deployment (daqui em diante, chamado de playbook), seguimos a regra de que ele deve estar em um repositório separado. Assim como as roles: cada uma em seu próprio repositório git. Isso permite que se compreenda que roles e playbook são objetos independentes do sistema de deployment, e mudanças em um objeto não devem afetar o funcionamento do outro. Isso é alcançado mudando os valores padrão das variáveis.
Ao elaborar o playbook, em termos gerais, existe a possibilidade de sobrepor os valores padrão das variáveis da role em dois lugares: nas variáveis do playbook e nas variáveis de inventário.
mydeploy # Diretório de deployment
├── deploy.yml # Playbook de deployment
├── group_vars # Diretório das variáveis do playbook
│ ├── all.yml # Arquivo para variáveis que conectam todo o sistema
│ └── myapi.yml # Arquivo das variáveis específicas do grupo myapi
└── inventories #
└── prod # Diretório do ambiente prod
├── prod.ini # Arquivo de inventário
└── group_vars # Diretório para variáveis de inventário
└── myapi #
├── vars.yml # Variáveis independentes do grupo myapi
└── vault.yml # Segredos (sempre independentes) ** —
A diferença é que as variáveis do playbook são usadas sempre que callamos playbooks que estão no mesmo nível. Portanto, essas variáveis são ideais para alterar os valores padrão de variáveis que não dependem do ambiente. Por outro lado, as variáveis do inventário serão usadas apenas para um ambiente específico, o que é perfeito para variáveis dependentes do ambiente.
É importante notar que a prioridade das variáveis não permitirá que você sobreponha variáveis primeiro nas variáveis do playbook e depois separadamente em um inventário.
Isso significa que já neste estágio é necessário decidir se a variável é dependente do ambiente ou não e colocá-la no local apropriado.
Por exemplo, em um projeto, a variável que controla a ativação do SSL foi por muito tempo dependente do ambiente, uma vez que não conseguimos ativar o SSL por razões alheias a nós em um dos ambientes. Depois que resolvemos esse problema, ela se tornou independente do ambiente e foi movida para as variáveis do playbook.
Variáveis de propriedades para grupos
Vamos expandir nosso modelo na figura 1, adicionando 2 grupos de servidores com um aplicativo Java diferente, mas com configurações distintas.

Vamos imaginar como seria o playbook nesse caso:
- hosts: myapi
roles:
- api
- hosts: bbauth
roles:
- auth
- hosts: ghauth
roles:
- authTemos três grupos no playbook, então imediatamente é recomendável criar o mesmo número de arquivos de grupo em group_vars nas variáveis do inventário e do playbook. Um arquivo de grupo, nesse caso, é a descrição de um componente do aplicativo no playbook. Ao abrir o arquivo de grupo nas variáveis do playbook, você imediatamente vê todas as diferenças em relação ao comportamento padrão dos papéis instalados para o grupo. Nas variáveis do inventário: diferenças no comportamento do grupo de um ambiente para outro.
Estilo de Código
- Tente não usar variáveis host_vars, pois elas não descrevem o sistema, mas apenas um caso específico, o que a longo prazo levará a perguntas como: "Por que este host é diferente dos outros?", uma resposta que nem sempre é fácil de encontrar.
Variáveis de ligação
No entanto, isso diz respeito às variáveis de propriedades, mas como devemos proceder com as variáveis de ligação?
A diferença é que elas devem ter o mesmo valor em diferentes grupos.
No início havia de usar uma construção monstruosa do tipo:
hostvars[groups['bbauth'][0]]['auth_bind_port'], mas essa ideia foi prontamente descartada
pois ela apresenta desvantagens. Primeiro, a complexidade. Segundo, a dependência de um host específico no grupo. Terceiro, é necessário coletar informações de todos os hosts antes do início do deployment, se não quisermos obter um erro de variável indefinida.
No final, foi decidido usar variáveis de ligação.
Variáveis de ligação — são variáveis que pertencem ao playbook e são necessárias para a conexão de objetos do sistema.
As variáveis de conexão são preenchidas em variáveis comuns do sistema. group_vars/all/vars e são formadas pela extração de todas as variáveis dos ouvintes de cada grupo, adicionando no início o nome do grupo de onde o ouvinte foi extraído.
Dessa forma, assegura-se a uniformidade e a não sobreposição de nomes.
Vamos tentar conectar as variáveis do exemplo acima:

Vamos supor 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"Extrairemos para variáveis comuns group_vars/all/vars todos os ouvintes e adicionaremos no nome o nome do grupo:
# 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, ao alterar o valor do conector, teremos certeza de que a solicitação irá se referir ao local onde está a porta.
Estilo de Código
- Como funções e grupos são objetos diferentes do sistema, é necessário que eles tenham nomes diferentes, assim as variáveis de conexão mostrarão claramente que pertencem a um grupo específico de servidores, e não a uma função no sistema.
Arquivos dependentes do ambiente
Nas funções, podem ser utilizados arquivos que variam de ambiente para ambiente.
Um exemplo desses arquivos são os certificados SSL. Armazená-los em texto
na variável não é muito conveniente. Porém, é mais prático armazenar o caminho até eles dentro da variável.
Por exemplo, usamos a variável api_ssl_key_file: "/path/to/file".
Como é evidente que o certificado da chave mudará de ambiente para ambiente, essa é uma variável dependente do ambiente e, portanto, deve ficar no arquivo
group_vars/myapi/vars inventário de variáveis, e conter o valor 'por exemplo'.
É mais conveniente, nesse caso, colocar o arquivo da chave no repositório do playbook no caminho
files/prod/certs/myapi.key, então o valor da variável será:
api_ssl_key_file: "prod/certs/myapi.key". A conveniência está no fato de que as pessoas responsáveis pela implantação do sistema em um ambiente específico também têm seu espaço reservado no repositório para armazenar seus arquivos. Ao mesmo tempo, permanece a possibilidade de indicar um caminho absoluto até o certificado no servidor, caso os certificados sejam fornecidos por outro sistema.
Vários ambientes na mesma configuração
Frequentemente surge a necessidade de implantar vários estandes praticamente idênticos em um ambiente com mínimas diferenças. Nesse caso, separamos as variáveis dependentes do ambiente em aquelas que permanecem inalteradas dentro desse ambiente e aquelas que mudam. E colocamos as últimas diretamente nos arquivos de inventário. Após essa manipulação, é possível criar mais um inventário diretamente no diretório do ambiente.
Ele irá reutilizar os group_vars do inventário, além de ter a capacidade de redefinir algumas variáveis diretamente para si.
A estrutura final dos diretórios para o projeto de implantação:
mydeploy # Diretório de implantação
├── deploy.yml # Playbook de implantação
├── files # Diretório para arquivos de implantação
│ ├── prod # Diretório para arquivos dependentes do ambiente do estande prod
│ │ └── certs #
│ │ └── myapi.key #
│ └── test1 # Diretório para arquivos dependentes do ambiente do estande test1
├── group_vars # Diretório de variáveis do playbook
│ ├── all.yml # Arquivo para variáveis aplicáveis a todo o sistema
│ ├── myapi.yml # Arquivo de variáveis de propriedade do grupo myapi
│ ├── bbauth.yml #
│ └── ghauth.yml #
└── inventories #
├── prod # Diretório do ambiente prod
│ ├── group_vars # Diretório para variáveis do inventário
│ │ ├── myapi #
│ │ │ ├── vars.yml # Variáveis dependentes do grupo myapi
│ │ │ └── vault.yml # Segredos (sempre dependentes do ambiente)
│ │ ├── bbauth #
│ │ │ ├── vars.yml #
│ │ │ └── vault.yml #
│ │ └── ghauth #
│ │ ├── vars.yml #
│ │ └── vault.yml #
│ └── prod.ini # Inventário do estande prod
└── test # Diretório do ambiente test
├── group_vars #
│ ├── myapi #
│ │ ├── vars.yml #
│ │ └── vault.yml #
│ ├── bbauth #
│ │ ├── vars.yml #
│ │ └── vault.yml #
│ └── ghauth #
│ ├── vars.yml #
│ └── vault.yml #
├── test1.ini # Inventário do estande test1 no ambiente test
└── test2.ini # Inventário do estande test2 no ambiente testResumo
Após organizar as variáveis de acordo com o artigo: cada arquivo de variáveis responde por uma tarefa específica. E uma vez que um arquivo tem tarefas específicas, é possível designar alguém responsável pela correção de cada arquivo. Por exemplo, o desenvolvedor da implantação do sistema torna-se responsável pela correção do preenchimento das variáveis do playbook, enquanto o administrador cujo ambiente é descrito nas variáveis de inventário fica encarregado do preenchimento destas variáveis.
Os papéis tornaram-se uma unidade independente de desenvolvimento com sua própria interface, permitindo que o desenvolvedor do papel criasse funcionalidades, em vez de adaptar o papel ao sistema. Essa questão era especialmente relevante para os papéis comuns a todos os sistemas na empresa.
Os administradores de sistemas não precisam mais entender o código de implantação. Tudo o que se exige deles para uma implantação bem-sucedida é preencher os arquivos de variáveis dependentes do ambiente.
Literatura
Autor:
Kalyuzhny Denis Alexandrovich
Fonte: habr.com
