paint-brush
Micro-DevOps com Systemd: sobrecarregue qualquer servidor Linux comumby@tylerjl
5,185
5,185

Micro-DevOps com Systemd: sobrecarregue qualquer servidor Linux comum

Tyler6m2023/09/05
Read on Terminal Reader

Orquestradores de contêineres como o Kubernetes oferecem uma variedade estonteante de recursos, mas você precisa pagar o custo da complexidade adicional? Saiba como os recursos modernos do systemd podem ajudar a gerenciar a segurança e a resiliência de cargas de trabalho no Linux para serem executadas com eficiência em uma escala menor.
featured image - Micro-DevOps com Systemd: sobrecarregue qualquer servidor Linux comum
Tyler HackerNoon profile picture
0-item
1-item

Plataformas como Kubernetes, Nomad ou qualquer plataforma como serviço (Paas) hospedada na nuvem oferecem uma variedade de recursos poderosos. Do dimensionamento de cargas de trabalho ao gerenciamento de segredos e estratégias de implantação, esses orquestradores de carga de trabalho são otimizados para ajudar a dimensionar a infraestrutura de diferentes maneiras.


Mas será que as operadoras sempre precisam pagar o custo da escalabilidade máxima? Às vezes, o custo da complexidade e da abstração supera seus benefícios. Em vez disso, muitos construtores passam a confiar em arquiteturas de implantação radicalmente simples para facilitar o gerenciamento. Dois servidores virtuais privados por trás de um balanceador de carga são uma pilha drasticamente mais simples de gerenciar em comparação com um amplo cluster de microsservidores em uma frota de hosts de contêineres. Isso pode começar a render dividendos quando houver menos peças móveis para depurar quando surgirem problemas ou atualizar quando chegar a hora de mantê-las.


A base para muitas distribuições modernas do Linux é o systemd e ele vem com um forte conjunto de recursos que muitas vezes são comparáveis aos orquestradores de contêineres ou sistemas PaaS. Neste artigo, exploraremos como você pode aproveitar os recursos mais recentes do systemd para obter muitas das habilidades desses outros grandes sistemas sem a dor de cabeça do gerenciamento e sobrecarregar qualquer servidor Linux comum para ser uma plataforma de aplicativos muito capaz.

Uma cartilha do sistema

Em um único host, escrever um arquivo systemd .service é uma maneira ideal de executar um processo gerenciado. Na maioria das vezes, você nem precisa alterar o aplicativo: o systemd oferece suporte a uma variedade de tipos diferentes de serviços e pode se adaptar de acordo.


Por exemplo, considere este .service simples que define como executar um serviço web simples:


 [Unit] Description=a simple web service [Service] ExecStart=/usr/bin/python3 -m http.server 8080


Lembre-se dos padrões para serviços systemd: ExecStart= deve ser um caminho absoluto, os processos não devem se bifurcar em segundo plano e pode ser necessário definir variáveis de ambiente necessárias com a opção Environment= .


Quando colocado em um arquivo como /etc/systemd/system/webapp.service , isso cria um serviço que você pode controlar com systemctl :


  • systemctl start webapp iniciará o processo.
  • systemctl status webapp exibirá se o serviço está em execução, seu tempo de atividade e saída de stderr e stdout , bem como o ID do processo e outras informações.
  • systemctl stop webapp encerrará o serviço.


Além disso, toda a saída impressa em stderr e stdout será agregada por journald e acessível através do diário do sistema (com journalctl ) ou direcionada especificamente usando o sinalizador --unit :


 journalctl --unit webapp


Como o journald gira e gerencia seu armazenamento por padrão, a coleta de logs por meio do diário é uma boa estratégia para gerenciar o armazenamento de logs.


O restante deste artigo explorará opções para aprimorar um serviço como este.

Segredos

Orquestradores de contêineres como o Kubernetes oferecem suporte à capacidade de injetar segredos com segurança: valores extraídos de armazenamentos de dados seguros e expostos a cargas de trabalho em execução. Dados confidenciais, como chaves de API ou senhas, exigem tratamento diferente das variáveis de ambiente ou arquivos de configuração para evitar exposição não intencional.


Aopção LoadCredential= systemd suporta o carregamento de valores confidenciais de arquivos no disco e a exposição deles a serviços em execução de maneira segura. Assim como as plataformas hospedadas que gerenciam segredos remotamente, o systemd tratará as credenciais de maneira diferente dos valores como variáveis de ambiente para garantir que sejam mantidas seguras.


Para injetar um segredo em um serviço systemd, comece colocando um arquivo contendo o valor secreto em um caminho no sistema de arquivos. Por exemplo, para expor uma chave de API a uma unidade .service , crie um arquivo em /etc/credstore/api-key para persistir o arquivo durante as reinicializações ou em /run/credstore/api-key para evitar a persistência do arquivo permanentemente (o path pode ser arbitrário, mas o systemd tratará esses caminhos credstore como padrões). Em ambos os casos, o arquivo deve ter permissões restritas usando um comando como chmod 400 /etc/credstore/api-key .


Na seção [Service] do arquivo .service , defina a opção LoadCredential= e passe para ela dois valores separados por dois pontos ( : ): o nome da credencial e seu caminho. Por exemplo, para chamar nosso arquivo /etc/credstore/api-key “token”, defina a seguinte opção de serviço systemd:


 LoadCredential=token:/etc/credstore/api-key


Quando o systemd inicia seu serviço, o segredo é exposto ao serviço em execução em um caminho no formato ${CREDENTIALS_DIRECTORY}/token onde ${CREDENTIALS_DIRECTORY} é uma variável de ambiente preenchida pelo systemd. O código do seu aplicativo deve ler cada segredo definido dessa forma para uso em bibliotecas ou códigos que exigem valores seguros, como tokens de API ou senhas. Por exemplo, em Python, você pode ler este segredo com um código como o seguinte:


 from os import environ from pathlib import Path credentials_dir = Path(environ["CREDENTIALS_DIRECTORY"]) with Path(credentials_dir / "token").open() as f: secret = f.read().strip()


Você pode então usar a variável secret com o conteúdo do seu segredo para qualquer biblioteca que possa exigir um token de API ou senha.

Reinicia

Outra capacidade de orquestradores como o Nomad é a capacidade de reiniciar automaticamente uma carga de trabalho que travou. Seja devido a um erro de aplicativo não tratado ou alguma outra causa, reiniciar aplicativos com falha é um recurso muito útil que geralmente é a primeira linha de defesa ao projetar um aplicativo para ser resiliente.


A opção Restart= systemd controla se o systemd reiniciará automaticamente um processo em execução. Existem vários valores potenciais para esta opção, mas para serviços básicos, a configuração on-failure é adequada para satisfazer a maioria dos casos de uso.


Outra configuração a ser considerada ao configurar a reinicialização automática é a opção RestartSec= , que determina quanto tempo o systemd esperará antes de iniciar o serviço novamente. Normalmente, esse valor deve ser personalizado para evitar reiniciar serviços com falha em um loop apertado e potencialmente consumir muito tempo de CPU gasto na reinicialização de processos. Um valor curto que não espere muito , como 5s geralmente é suficiente.


Opções como RestartSec= que aceitam períodos de duração ou valores baseados em tempo podem analisar uma variedade de formatos como 5min 10s ou 1hour dependendo de suas necessidades. Consulte o manual do systemd.time para obter informações adicionais.


Finalmente, duas outras opções determinam o quão agressivamente o systemd tentará reiniciar as unidades com falha antes de finalmente desistir. StartLimitIntervalSec= e StartLimitBurst= controlarão a frequência com que uma unidade pode iniciar dentro de um determinado período de tempo. Por exemplo, as seguintes configurações:


 StartLimitBurst=5 StartLimitIntervalSec=10


Só permitirá que uma unidade tente arrancar no máximo 5 vezes durante um período de 10 segundos. Se o serviço configurado tentar inicializar pela sexta vez em um período de 10 segundos, o systemd irá parar de tentar reiniciar a unidade e marcá-la como com failed .


Combinando todas essas configurações, você pode incluir as seguintes opções para sua unidade .service configurar reinicializações automáticas:


 [Unit] StartLimitBurst=5 StartLimitIntervalSec=10 [Service] Restart=on-failure RestartSec=1


Esta configuração irá reiniciar um serviço se ele falhar - ou seja, ele sair inesperadamente, como com um código de saída diferente de zero - depois de esperar um segundo e irá parar de tentar reiniciar o serviço se ele tentar iniciar mais de cinco vezes ao longo do curso de 10 segundos.

Endurecimento de serviço

Um dos principais benefícios da execução em um contêiner é o sandbox de segurança. Ao segmentar um processo de aplicativo do sistema operacional subjacente, quaisquer vulnerabilidades que possam estar presentes no serviço são muito mais difíceis de serem escaladas para um comprometimento total. Tempos de execução como o Docker conseguem isso por meio de uma combinação de cgroups e outras primitivas de segurança.


Você pode ativar várias opções do systemd para impor restrições semelhantes que podem ajudar a proteger um host subjacente contra comportamento imprevisível da carga de trabalho:


  • ProtectSystem= pode restringir o acesso de gravação a caminhos confidenciais do sistema, como /boot e /usr . A documentação desta opção enumera todas as opções disponíveis, mas de modo geral, definir esta opção como full é um padrão razoável para proteger esses caminhos do sistema de arquivos.
  • ProtectHome= pode definir os diretórios /home , /root e /run/user como somente leitura com a configuração read-only ou, quando definido como true , montá-los no sistema de arquivos do serviço como diretórios vazios. A menos que seu aplicativo tenha uma necessidade específica de acessar esses diretórios, definir isso como true pode proteger o sistema com segurança contra acesso ilegítimo a esses diretórios.
  • PrivateTmp= mantém /tmp e /var/tmp separados para o serviço configurado para que os arquivos temporários desse serviço e de outros processos permaneçam privados. A menos que haja um motivo convincente para os processos compartilharem informações por meio de arquivos temporários, esta é uma opção útil para ativar.
  • NoNewPrivileges= é outra maneira segura e direta de proteger um serviço, garantindo que o processo executado não possa elevar seus privilégios. Se você não tiver certeza sobre a capacidade de usar outras opções de proteção, essa geralmente é uma das menos problemáticas de ativar.


A página de manual do systemd.exec é um recurso útil para explorar as diferentes opções que se aplicam a cargas de trabalho executáveis, como serviços.

Leitura adicional

As páginas de manual do projeto systemd são extensas e úteis para aprender sobre todas as opções disponíveis para executar seus próprios aplicativos. Esteja você executando um serviço persistente como um servidor web ou uma unidade .timer periódica para substituir um cron job, a documentação do systemd pode oferecer orientações úteis.