Um contêiner leve com systemd-nspawn não possui todo o isolamento de um contêiner com lxc (Linux Contéiners). O contêiner com systemd-nspawn possui isolamento de sistema de arquivos (equivalente ao chroot), isolamento de memória e isolamento de processos (processos de um contêiner não veem processos do host ou de outros contêiners). Porém, os contêiners compartilham a rede do hospedeiro. Portanto, o endereço IP e o espaço de portas é compartilhado entre todos.
A ativação automática via sockets do systemd funciona da seguinte forma:
- Configuramos o systemd para ficar escutando uma determinada porta, através de um arquivo de unidade especial terminado em .socket.
- Quando uma conexão é iniciada por um cliente nesta porta, o systemd ativa a unidade homônima com a extensão .service e repassa o socket para o serviço. Normalmente, esse serviço é configurado de tal forma que ele seja concluído ao fim da conexão com o cliente, de forma que não fique um serviço rodando sempre em segundo plano. Porém, isto não é obrigatório.
Este recurso também pode ser usado para carregar um contêiner com systemd-nspawn. Vamos colocar o seguinte cenário: eu tenho um contêiner criado com o objetivo de isolar um servidor JBoss. Enquanto eu não precisar do servidor JBoss, eu gostaria também de não precisar carregar o contêiner. E quando eu quisesse acessar algum serviço do JBoss, o systemd deveria carregar o contêiner, carregar o JBoss interno e repassar o socket do host ao contêiner. Este, por sua vez, passaria o socket para o serviço JBoss que foi inciado.
Vamos à implementação:
No host, é necessário criar duas unidades:
- /etc/systemd/system/meuconteiner.socket É o que define a porta em que o systemd irá escutar para ativar o contêiner.
[Unit] Description=Socket for my Container [Socket] ListenStream=2223É possível escutar mais de uma porta no host, de forma que o systemd vai repassar os sockets correspondentes para o contêiner. Daí, o contêiner poderá repassá-los, por sua vez, aos serviços corretos internamente.
[Unit] Description=Inicializador do conteiner [Service] ExecStart=/usr/bin/systemd-nspawn -jbD/archimage --bind=/var/cache/pacman/pkg KillMode=process
O contêiner será inicializado automaticamente quando uma conexão for estabelecida na porta 2223 (utilizarei esta porta para evitar conflitar com a porta 22, em que pode estar rodando o sshd do próprio host).
Agora vamos configurar o contêiner. Primeiro, precisamos acessá-lo manualmente com o comando abaixo:
sudo systemd-nspawn -jbD archimage --bind=/var/cache/pacman/pkg
Os parãmetros foram detalhados no post anterior. Eu loguei com o usuário root, para fazer as configurações necessárias. Irei apenas criar um serviço sshd para exemplificar.
Novamente, criaremos duas unidades do systemd: uma para definir o socket a escutar e a outra para executar o serviço sshd.
- /etc/systemd/system/sshd.socket Define a porta do contêiner que o systemd interno deverá escutar.
[Unit] Description=SSH Socket for Per-Connection Servers [Socket] ListenStream=2223 Accept=yes
[Unit] Description=SSH Per-Connection Server for %I [Service] ExecStart=-/usr/bin/sshd -i StandardInput=socket
Pronto. Ao executarmos
ssh -p 2223 localhost
o systemd do host vai iniciar o contêiner, passar o socket para o systemd interno e este, por sua vez, iniciará o serviço sshd, que receberá o socket e fará a conexão.
Eu reparei que mesmo depois de fechar a conexão ssh, o contêiner não foi terminado. Precisarei investigar mais este detalhe, pois gostaria que ele fosse terminado também. Versões futuras do systemd vão resolver este problema, permitindo que contêiners ociosos sejam terminados, da mesma forma que um laptop pode ser hibernado pelo systemd quando ele está em um estado ocioso.
No próximo post, pretendo configurar uma instância do JBoss AS 7 no contêiner e pretendo associar vários sockets externos aos serviços fornecidos internamente pelo JBoss através do contêiner.