
LXD — é um gerenciador de contêineres de próxima geração, assim diz . Ele oferece uma interface de usuário semelhante a máquinas virtuais, mas utilizando contêineres Linux em vez disso.
O núcleo LXD — é um daemon privilegiado (um serviço executado com permissões root), que fornece uma API REST através de um socket unix local, bem como pela rede, se a configuração apropriada for instalada. Clientes, como a ferramenta de linha de comando fornecida com o LXD, enviam solicitações através dessa API REST. Isso significa que, não importa se você está acessando o host local ou remoto, tudo funciona da mesma maneira.
Neste artigo, não abordaremos em detalhes os conceitos do LXD, nem discutiremos todas as possibilidades disponíveis na documentação, incluindo a implementação recente nas últimas versões do LXD, que dá suporte a máquinas virtuais QEMU em paralelo com contêineres. Em vez disso, vamos aprender apenas as funcionalidades básicas de gerenciamento de contêineres — configurar pools de armazenamento, rede, iniciar contêineres, aplicar limites de recursos e considerar como usar snapshots, para que você possa ter uma noção básica sobre o LXD e utilizar contêineres no Linux.
Para informações completas, consulte a fonte oficial:
Navegação
Instalação do LXD
Instalação do LXD em distribuições Ubuntu
Na distribuição Ubuntu 19.10, o pacote lxd tem uma tradução para :
apt search lxd
lxd/eoan 1:0.7 all
Pacote transitório - lxd -> snap (lxd)Isso significa que dois pacotes serão instalados, um de sistema e o outro como um pacote snap. A instalação de dois pacotes no sistema pode criar um problema em que o pacote de sistema pode se tornar órfão se o pacote snap for removido pelo gerenciador de pacotes snap.
Encontrar pacote lxd no repositório snap pode ser feito com o seguinte comando:
snap find lxd
Nome Versão Resumo
lxd 3.21 Gerenciador de containers de sistema e API
lxd-demo-server 0+git.6d54658 Sessões de demonstração de software online usando LXD
nova ocata Serviço de Computação OpenStack (nova)
nova-hypervisor ocata Serviço de Computação OpenStack - Hipervisor KVM (nova)
distrobuilder 1.0 Criador de imagens para LXC e LXD
fabrica 0.1 Construa snaps apontando um formulário da web para...
satellite 0.1.2 Plataforma avançada e escalável de inteligência de código abertoExecutando o comando list podemos confirmar que o pacote lxd ainda não está instalado:
snap list
Nome Versão Rev Tracking Publicador Notas
core 16-2.43.3 8689 stable canonical✓ coreApesar de LXD ser um pacote snap, ele deve ser instalado através do pacote de sistema lxd, que criará no sistema o grupo correspondente, as utilidades necessárias em /usr/bin etc.
sudo apt update
sudo apt install lxdVamos verificar se o pacote está instalado como pacote snap:
snap list
Nome Versão Rev Tracking Publicador Notas
core 16-2.43.3 8689 stable canonical✓ core
lxd 3.21 13474 stable/... canonical✓ -Instalação do LXD em distribuições Arch Linux
Para instalar o pacote LXD no sistema, é necessário executar os seguintes comandos, o primeiro atualiza a lista de pacotes disponíveis no repositório, e o segundo instala o pacote:
sudo pacman -Syyu && sudo pacman -S lxdApós a instalação do pacote, para gerenciar o LXD como um usuário comum, é necessário adicioná-lo ao grupo do sistema lxd:
sudo usermod -a -G lxd user1Vamos verificar se o usuário user1 foi adicionado ao grupo lxd:
id -Gn user1
user1 adm dialout cdrom floppy sudo audio dip video plugdev netdev lxdSe o grupo lxd não aparecer na lista, será necessário ativar novamente a sessão do usuário. Para isso, saia e entre novamente no sistema com o mesmo usuário.
Ativamos em systemd o carregamento do serviço LXD na inicialização do sistema:
sudo systemctl enable lxdIniciamos o serviço:
sudo systemctl start lxdVerificamos o status do serviço:
sudo systemctl status lxdArmazenamento do LXD
Antes de iniciar a inicialização, precisamos entender como o armazenamento está logicamente estruturado no LXD.
Armazenamento (Storage) por um ou mais Storage Pool que utiliza um dos sistemas de arquivos suportados como ZFS, BTRFS, LVM ou diretórios comuns. Cada Storage Pool é dividido em volumes (Storage Volume) que contêm imagens, contêineres ou dados para outros fins.
- Imagens — são distribuições especialmente reunidas sem o núcleo Linux e disponíveis a partir de fontes externas
- Contêineres — são distribuições implantadas a partir de imagens, prontas para uso
- Snapshots — são capturas do estado dos contêineres às quais se pode retornar

Para gerenciar o armazenamento no LXD, usa-se o comando lxc storage cuja ajuda pode ser obtida informando a chave — lxc storage --help
O seguinte comando exibe uma lista de todos Storage Pool no armazenamento LXD:
lxc storage list
+---------+-------------+--------+--------------------------------+---------+
| NOME | DESCRIÇÃO | DRIVER | FONTE | EM USO |
+---------+-------------+--------+--------------------------------+---------+
| hddpool | | btrfs | /dev/loop1 | 2 |
+---------+-------------+--------+--------------------------------+---------+
| ssdpool | | btrfs | /var/lib/lxd/disks/ssdpool.img | 4 |
+---------+-------------+--------+--------------------------------+---------+Para visualizar a lista de todos Storage Volume no escolhido Storage Pool usa-se o comando lxc storage volume list:
lxc storage volume list hddpool
+-------+----------------------------------+-------------+---------+
| TIPO | NOME | DESCRIÇÃO | EM USO |
+-------+----------------------------------+-------------+---------+
| imagem | ebd565585223487526ddb3607f515... | | 1 |
+-------+----------------------------------+-------------+---------+lxc storage volume list ssdpool
+-----------+----------------------------------+-------------+---------+
| TIPO | NOME | DESCRIÇÃO | EM USO |
+-----------+----------------------------------+-------------+---------+
| contêiner | alp3 | | 1 |
+-----------+----------------------------------+-------------+---------+
| contêiner | jupyter | | 1 |
+-----------+----------------------------------+-------------+---------+
| imagem | ebd565585223487526ddb3607f515... | | 1 |
+-----------+----------------------------------+-------------+---------+Além disso, se durante a criação foi escolhida o sistema de arquivos BTRFS, é possível obter a lista de Storage Pool subvolumes Storage Volume ou na interpretação do BTRFS utilizando as ferramentas desse sistema de arquivos: sudo btrfs subvolume list -p /var/lib/lxd/storage-pools/hddpoolID 257 gen 818 parent 5 top level 5 path images/ebd565585223487526ddb3607f5156e875c15a89e21b61ef004132196da6a0a3
sudo btrfs subvolume list -p /var/lib/lxd/storage-pools/ssdpool
ID 257 gen 1820 parent 5 top level 5 path images/ebd565585223487526ddb3607f5156e875c15a89e21b61ef004132196da6a0a3
ID 260 gen 1819 parent 5 top level 5 path containers/jupyter
ID 263 gen 1820 parent 5 top level 5 path containers/alp3Antes de criar e usar contêineres, é necessário realizar a inicialização geral do LXD, que cria e configura a rede, bem como o armazenamento. Isso pode ser feito manualmente com os comandos padrão do cliente, que estão disponíveis na lista através do comandoInicialização do LXD
lxc --help ou através do assistente de inicialização lxd init lxd init respondendo a algumas perguntas.
Escolha do sistema de arquivos para o Storage Pool
Durante a inicialização, o LXD faz algumas perguntas, incluindo a definição do tipo de sistema de arquivos para o padrão Storage Pool. Por padrão, o sistema de arquivos BTRFS é escolhido para ele. Alterar para outro sistema de arquivos após a criação não será possível. Para escolher o sistema de arquivos, é proposto :
Recurso
Diretório
Btrfs
LVM
ZFS
CEPH
Armazenamento de imagens otimizado
não
sim
sim
sim
sim
Criação otimizada de instâncias
não
sim
sim
sim
sim
Criação otimizada de instantâneas
não
sim
sim
sim
sim
Transferência otimizada de imagens
não
sim
não
sim
sim
Transferência otimizada de instâncias
não
sim
não
sim
sim
Copia ao escrever
não
sim
sim
sim
sim
Baseado em blocos
não
não
sim
não
sim
Clonagem instantânea
não
sim
sim
sim
sim
Driver de armazenamento utilizável dentro de um contêiner
sim
sim
não
não
não
Restaurar de instantâneas anteriores (não as mais recentes)
sim
sim
sim
não
sim
Cotas de armazenamento
sim(*)
sim
sim
sim
não
Inicialização da rede e do Storage Pool com o assistente
O próximo comando que veremos oferece configurar os principais componentes do LXD respondendo a perguntas simples através do assistente de inicialização.
Execute o comando lxc init e insira as respostas para as perguntas após o sinal de dois pontos, conforme mostrado no exemplo abaixo ou altere-as conforme suas condições:
lxd init
Você gostaria de usar clustering LXD? (sim/não) [padrão=não]:
Você deseja configurar um novo pool de armazenamento? (sim/não) [padrão=sim]:
Nome do novo pool de armazenamento [padrão=padrão]: ssdpool
Nome do backend de armazenamento a ser utilizado (lvm, btrfs, dir) [padrão=btrfs]:
Criar um novo pool BTRFS? (sim/não) [padrão=sim]:
Você gostaria de usar um dispositivo de bloco existente? (sim/não) [padrão=não]:
Tamanho em GB do novo dispositivo loop (mínimo de 1GB) [padrão=15GB]: 10GB
Você gostaria de conectar-se a um servidor MAAS? (sim/não) [padrão=não]:
Você gostaria de criar uma nova ponte de rede local? (sim/não) [padrão=sim]:
Como deve ser chamada a nova ponte? [padrão=lxdbr0]:
Qual endereço IPv4 deve ser usado? (notação CIDR, “auto” ou “nenhum”) [padrão=auto]: 10.0.5.1/24
Você gostaria que o LXD fizesse NAT no tráfego IPv4 na sua ponte? [padrão=sim]:
Qual endereço IPv6 deve ser usado? (notação CIDR, “auto” ou “nenhum”) [padrão=auto]: nenhum
Você gostaria que o LXD estivesse disponível na rede? (sim/não) [padrão=não]:
Você gostaria que imagens em cache obsoletas fossem atualizadas automaticamente? (sim/não) [padrão=sim] não
Você gostaria que um YAML "lxd init" pré-configurado fosse impresso? (sim/não) [padrão=não]: Criação de um Storage Pool adicional
Na etapa anterior, criamos Storage Pool a qual chamamos de ssdpool e cujo arquivo foi localizado em meu sistema no endereço /var/lib/lxd/disks/ssdpool.img. Este endereço do sistema de arquivos corresponde ao SSD físico no meu PC.
Nos próximos passos, para expandir a compreensão do papel que Storage Pool tem no armazenamento, criaremos um segundo Storage Pool que será fisicamente localizado em outro tipo de disco, em um HDD. O problema é que o LXD não permite criar Storage Pool fora do endereço /var/lib/lxd/disks/ e mesmo links simbólicos não funcionarão, . Podemos contornar essa limitação ao inicializar/formatar Storage Pool especificando o valor como um dispositivo de bloco em vez de um caminho para o arquivo de loopback usando isso na chave source.
Portanto, antes da criação Storage Pool é necessário determinar o arquivo loopback ou a partição existente em seu sistema de arquivos que ele usará. Para isso, criaremos e usaremos um arquivo que limitaremos a um tamanho de 10GB:
dd if=/dev/zero of=/mnt/work/lxd/hddpool.img bs=1MB count=10000
10000+0 registros lidos
10000+0 registros gravados
10000000000 bytes (10 GB, 9,3 GiB) copiados, 38,4414 s, 260 MB/sVamos conectar o arquivo loopback a um dispositivo loopback livre:
sudo losetup --find --show /mnt/work/lxd/hddpool.img
/dev/loop1Graças à chave --show a execução do comando retorna na tela o nome do dispositivo ao qual o nosso arquivo loopback foi conectado. Se necessário, podemos exibir uma lista de todos os dispositivos ocupados desse tipo para garantir a correção de nossas ações:
losetup -l
NOME SIZELIMIT OFFSET AUTOCLEAR RO ARQUIVO DO BACK-END DIO LOG-SEC
/dev/loop1 0 0 0 0 /mnt/work/lxd/hddpool.img 0 512
/dev/loop0 0 0 1 0 /var/lib/lxd/disks/ssdpool.img 0 512Da lista, pode-se observar que no dispositivo /dev/loop1 o arquivo loopback está conectado /mnt/work/lxd/hddpool.img, e no dispositivo /dev/loop0 o arquivo loopback está conectado /var/lib/lxd/disks/ssdpool.img que corresponde ao padrão Storage Pool.
O próximo comando cria uma nova Storage Pool no LXD com base no arquivo loopback recém-preparado. O LXD formatará o arquivo loopback /mnt/work/lxd/hddpool.img no dispositivo /dev/loop1 para o sistema de arquivos BTRFS:
lxc storage create hddpool btrfs size=10GB source=/dev/loop1Vamos exibir a lista de todos Storage Pool na tela:
lxc storage list
+---------+-------------+--------+--------------------------------+---------+
| NOME | DESCRIÇÃO | DRIVER | FONTE | USADO POR |
+---------+-------------+--------+--------------------------------+---------+
| hddpool | | btrfs | /dev/loop1 | 0 |
+---------+-------------+--------+--------------------------------+---------+
| ssdpool | | btrfs | /var/lib/lxd/disks/ssdpool.img | 0 |
+---------+-------------+--------+--------------------------------+---------+Aumento do tamanho do Storage Pool
Após a criação Storage Pool, se necessário, ele pode ser expandido. Para Storage Pool baseado no sistema de arquivos BTRFS, execute os seguintes comandos:
sudo truncate -s +5G /mnt/work/lxd/hddpool.img
sudo losetup -c /dev/loop1
sudo btrfs filesystem resize max /var/lib/lxd/storage-pools/hddpoolInserção automática de arquivo loopback na slot do dispositivo loopback
Temos um pequeno problema, ao reiniciar o sistema host, o arquivo /mnt/work/lxd/hddpool.img "sairá" do dispositivo /dev/loop1 e o serviço LXD falhará durante a inicialização, pois não o encontrará neste dispositivo. Para resolver esse problema, é necessário criar um serviço do sistema que irá conectar esse arquivo ao dispositivo /dev/loop1 durante a inicialização do sistema host.
Vamos criar um arquivo do tipo serviço em /etc/systemd/system/ para o sistema de inicialização SystemD:
cat << EOF | sudo tee -a /etc/systemd/system/lxd-hddpool.service
[Unit]
Description=Losetup LXD Storage Pool (hddpool)
After=local-fs.target
[Service]
Type=oneshot
ExecStart=/sbin/losetup /dev/loop1 /mnt/work/lxd/hddpool.img
RemainAfterExit=true
[Install]
WantedBy=local-fs.target
EOFAtivamos o serviço:
sudo systemctl enable lxd-hddpool
Criado link simbólico /etc/systemd/system/local-fs.target.wants/lxd-hddpool.service → /etc/systemd/system/lxd-hddpool.service.Após reiniciar o sistema anfitrião, verifique o status do serviço:
systemctl status lxd-hddpool.service
● lxd-hddpool.service - Losetup LXD Storage Pool (hddpool)
Carregado: carregado (/etc/systemd/system/lxd-hddpool.service; habilitado; predefinição do fornecedor: desabilitada)
Ativo: ativo (saído) desde Qua 2020-04-08 03:43:53 MSK; 1min 37s atrás
Processo: 711 ExecStart=/sbin/losetup /dev/loop1 /mnt/work/lxd/hddpool.img (código=saído, status=0/SUCESSO)
PID principal: 711 (código=saído, status=0/SUCESSO)
abr 08 03:43:52 manjaro systemd[1]: Iniciando Losetup LXD Storage Pool (hddpool)...
abr 08 03:43:53 manjaro systemd[1]: Concluído Losetup LXD Storage Pool (hddpool).Da saída, podemos garantir que o estado do serviço é ativo, embora a execução do nosso script de um único comando tenha terminado, isso nos permitiu fazer a opção RemainAfterExit=true.
Segurança. Privilégios de contêineres
Como todos os processos do contêiner são, na verdade, executados de forma isolada no sistema anfitrião usando seu núcleo, para maior proteção do acesso dos processos do contêiner ao sistema anfitrião, o LXD oferece privilégio aos processos, onde:
Contêineres privilegiados — são contêineres em que os processos com UID e GID correspondem ao mesmo proprietário que no sistema anfitrião. Por exemplo, um processo executado em um contêiner com UID igual a 0 tem os mesmos direitos de acesso que um processo do sistema anfitrião com UID igual a 0. Em outras palavras, o usuário root no contêiner possui todos os direitos não apenas no contêiner, mas também no sistema anfitrião se conseguir sair do espaço de nome isolado do contêiner.
Contêineres não privilegiados — são contêineres em que os processos pertencem ao proprietário UID e GID com números de 0 a 65535, mas para o sistema anfitrião, o proprietário é mascarado pelo bit SubUID e SubGID adicionados, respectivamente. Por exemplo, um usuário com UID=0 no contêiner será notado no sistema anfitrião como
SubUID + UID. Isso protege o sistema anfitrião, pois, se algum processo no contêiner conseguir sair de seu espaço de nome isolado, ele poderá interagir com o sistema anfitrião apenas como um processo com UID/GID desconhecido e muito elevado.
Por padrão, contêineres recém-criados têm status não privilegiado e, portanto, devemos definir SubUID e SubGID.
Vamos criar dois arquivos de configuração nos quais definiremos a máscara para SubUID e SubGID, respectivamente:
sudo touch /etc{/subuid,/subgid}
sudo usermod --add-subuids 1000000-1065535 root
sudo usermod --add-subgids 1000000-1065535 rootPara aplicar as alterações, o serviço LXD precisa ser reiniciado:
sudo systemctl restart lxdCriação de um switch virtual de rede
Como inicializamos a rede anteriormente com o assistente de inicialização lxd init e criamos um dispositivo de rede lxdbr0, nesta seção, vamos apenas conhecer a rede no LXD e como criar um switch virtual (bridge) usando o comando do cliente.
O diagrama a seguir mostra como o switch (bridge) conecta o host e os contêineres à rede:

Os contêineres podem interagir através da rede com outros contêineres ou com o host em que esses contêineres estão sendo executados. Para isso, é necessário vincular as interfaces de rede dos contêineres ao switch virtual. Primeiro, criaremos o switch, e as interfaces de rede do contêiner serão vinculadas em capítulos posteriores, depois que o próprio contêiner for criado.
O seguinte comando cria um switch com uma sub-rede 10.0.5.0/24 e um endereço IPv4 10.0.5.1/24, além de ativar ipv4.nat para que os contêineres possam acessar a internet através do host usando o serviço NAT:
lxc network create lxdbr0 ipv4.address=10.0.5.1/24 ipv4.nat=true ipv6.address=noneVerifique a lista de dispositivos de rede disponíveis no LXD:
lxc network list
+--------+----------+---------+-------------+---------+
| NOME | TIPO | GERENCIADO | DESCRIÇÃO | USADO POR |
+--------+----------+---------+-------------+---------+
| eno1 | físico | NÃO | | 0 |
+--------+----------+---------+-------------+---------+
| lxdbr0 | bridge | SIM | | 0 |
+--------+----------+---------+-------------+---------+Você também pode verificar a criação do dispositivo de rede usando a ferramenta padrão do sistema Linux — ip link ou ip addr:
ip addr
1: lo: mtu 65536 qdisc noqueue estado DESCONHECIDO grupo padrão qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 escopo host lo
lft válido para sempre lft preferido para sempre
inet6 ::1/128 escopo host
lft válido para sempre lft preferido para sempre
2: eno1: mtu 1500 qdisc fq_codel estado UP grupo padrão qlen 1000
link/ether bc:ee:7b:5a:6b:44 brd ff:ff:ff:ff:ff:ff
altname enp0s25
inet6 fe80::9571:11f3:6e0c:c07b/64 escopo link noprefixroute
lft válido para sempre lft preferido para sempre
3: lxdbr0: mtu 1500 qdisc noqueue estado UP grupo padrão qlen 1000
link/ether c2:38:90:df:cb:59 brd ff:ff:ff:ff:ff:ff
inet 10.0.5.1/24 escopo global lxdbr0
lft válido para sempre lft preferido para sempre
inet6 fe80::c038:90ff:fedf:cb59/64 escopo link
lft válido para sempre lft preferido para sempre
5: veth3ddab174@if4: mtu 1500 qdisc noqueue master lxdbr0 estado UP grupo padrão qlen 1000
link/ether ca:c3:5c:1d:22:26 brd ff:ff:ff:ff:ff:ff link-netnsid 0Perfil de configuração
Cada contêiner no LXD possui sua própria configuração e pode expandi-la com configurações globalmente declaradas chamadas perfis de configuração. A aplicação de perfis de configuração ao container segue um modelo em cascata, o exemplo a seguir demonstra isso:

Neste exemplo, três perfis foram criados no sistema LXD: padrão, hddpool e hostfs. Todos os três perfis foram aplicados a um container que possui uma configuração local (zona cinza). O perfil padrão possui um dispositivo root cujo parâmetro pool é igual a ssdpool, mas graças ao modelo em cascata de aplicação de configuração, podemos aplicar ao container o perfil hddpool cujo parâmetro pool substituindo esse mesmo parâmetro do perfil padrão e o container receberá a configuração do dispositivo root com o parâmetro pool igual a hddpool, enquanto o perfil hostfs simplesmente adiciona um novo dispositivo ao container.
Para ver a lista de perfis de configuração disponíveis, use o seguinte comando:
lxc profile list
+---------+---------+
| NAME | USED BY |
+---------+---------+
| padrão | 1 |
+---------+---------+
| hddroot | 0 |
+---------+---------+
| ssdroot | 1 |
+---------+---------+A lista completa de comandos disponíveis para trabalhar com perfis pode ser obtida adicionando a chave --help:
lxc profile --help
Descrição:
Gerenciar perfis
Uso:
lxc profile [comando]
Comandos Disponíveis:
add Adicionar perfis a instâncias
assign Atribuir conjuntos de perfis a instâncias
copy Copiar perfis
create Criar perfis
delete Deletar perfis
device Gerenciar dispositivos de instância
edit Editar configurações de perfil como YAML
get Obter valores para chaves de configuração de perfil
list Listar perfis
remove Remover perfis de instâncias
rename Renomear perfis
set Definir chaves de configuração de perfil
show Mostrar configurações de perfil
unset Desmarcar chaves de configuração de perfilEdição do perfil
O perfil de configuração padrão padrão não possui configuração de adaptador de rede para o container e todos os containers recém-criados não possuem rede, para eles é necessário criar dispositivos de rede locais (dedicados) com um comando separado, mas podemos criar no perfil de configuração um dispositivo de rede global que será compartilhado entre todos os containers que utilizam esse perfil. Assim, imediatamente após o comando de criação de um novo container, eles terão rede com acesso à internet. Nesse processo, não há restrições; sempre podemos mais tarde criar um dispositivo de rede local, caso haja necessidade.
O próximo comando adicionará ao perfil de configuração o dispositivo eth0 do tipo nic conectado à rede lxdbr0:
lxc profile device add default eth0 nic network=lxdbr0 name=eth0É importante notar que, como realmente adicionamos um dispositivo ao perfil de configuração, se tivéssemos especificado um endereço IP estático para o dispositivo, todos os contêineres que aplicarem esse perfil compartilharão o mesmo endereço IP. Caso haja a necessidade de criar um contêiner com um endereço IP estático dedicado, deve-se criar a configuração da interface de rede no nível do contêiner (configuração local) com o parâmetro de endereço IP, e não no nível do perfil.
Vamos verificar o perfil:
lxc profile show default
config: {}
description: Perfil LXD padrão
devices:
eth0:
name: eth0
network: lxdbr0
type: nic
root:
path: \/
pool: ssdpool
type: disk
name: default
used_by: []Neste perfil, podemos ver que para todos os novos contêineres criados, dois dispositivos (devices) serão criados:
eth0— Dispositivo do tiponicconectado ao switch (bridge de rede)lxdbr0root— Dispositivo do tipodiskque utiliza o pool de armazenamentossdpool
Criação de novos perfis
Para utilizar anteriormente criados Storage Pool contêineres, criaremos um perfil de configuração ssdroot no qual adicionaremos um dispositivo do tipo disk com ponto de montagem / (root) usando o anteriormente criado Storage Pool — ssdpool:
lxc profile create ssdroot
lxc profile device add ssdroot root disk path=\/ pool=ssdpoolDe forma similar, criamos um dispositivo do tipo disk, mas nesse caso utilizando Storage Pool — hddpool:
lxc profile create hddroot
lxc profile device add hddroot root disk path=\/ pool=hddpoolVerificando os perfis de configuração:
lxc profile show ssdroot
config: {}
description: ""
devices:
root:
path: \/
pool: ssdpool
type: disk
name: ssdroot
used_by: []lxc profile show hddroot
config: {}
description: ""
devices:
root:
path: \/
pool: hddpool
type: disk
name: hddroot
used_by: []Repositório de imagens
Os contêineres são criados a partir de imagens que são distribuições especialmente construídas que não possuem um kernel Linux. Portanto, antes de iniciar um contêiner, ele deve ser descarregado a partir dessa imagem. As imagens são provenientes de um repositório local, onde as imagens são carregadas de repositórios externos.
Repositórios de imagens remotas
Por padrão, o LXD está configurado para obter imagens de três fontes remotas:
- ubuntu: (para imagens estáveis do Ubuntu)
- ubuntu-daily: (para imagens diárias do Ubuntu)
- images: (para uma variedade de outras distribuições)
lxc remote list
+-----------------+------------------------------------------+--------+--------+
| NOME | URL | PÚBLICO | ESTÁTICO |
+-----------------+------------------------------------------+--------+--------+
| imagens | https://images.linuxcontainers.org | SIM | NÃO |
+-----------------+------------------------------------------+--------+--------+
| local (padrão) | unix:// | NÃO | SIM |
+-----------------+------------------------------------------+--------+--------+
| ubuntu | https://cloud-images.ubuntu.com/releases | SIM | SIM |
+-----------------+------------------------------------------+--------+--------+
| ubuntu-daily | https://cloud-images.ubuntu.com/daily | SIM | SIM |
+-----------------+------------------------------------------+--------+--------+Por exemplo, o repositório ubuntu: possui as seguintes imagens:
lxc image -c dasut list ubuntu: | head -n 11
+----------------------------------------------+--------------+----------+------------+
| DESCRIÇÃO | ARQUITETURA | TAMANHO | TIPO |
+----------------------------------------------+--------------+----------+------------+
| ubuntu 12.04 LTS amd64 (release) (20150728) | x86_64 | 153.72MB | CONTÊINER |
+----------------------------------------------+--------------+----------+------------+
| ubuntu 12.04 LTS amd64 (release) (20150819) | x86_64 | 152.91MB | CONTÊINER |
+----------------------------------------------+--------------+----------+------------+
| ubuntu 12.04 LTS amd64 (release) (20150906) | x86_64 | 154.69MB | CONTÊINER |
+----------------------------------------------+--------------+----------+------------+
| ubuntu 12.04 LTS amd64 (release) (20150930) | x86_64 | 153.86MB | CONTÊINER |
+----------------------------------------------+--------------+----------+------------+Para exibir um número limitado de colunas, usamos a opção -c com os parâmetros dasut, e também limitamos a extensão da lista com o comando head.
Para exibir a lista de imagens, há a filtragem disponível. O seguinte comando mostrará uma lista de todas as arquiteturas disponíveis do distribuidor :
lxc image -c ldast list images:alpine/3.11
+------------------------------+--------------------------------------+--------------+
| ALIAS | DESCRIÇÃO | ARQUITETURA |
+------------------------------+--------------------------------------+--------------+
| alpine/3.11 (3 mais) | Alpine 3.11 amd64 (20200220_13:00) | x86_64 |
+------------------------------+--------------------------------------+--------------+
| alpine/3.11/arm64 (1 mais) | Alpine 3.11 arm64 (20200220_13:00) | aarch64 |
+------------------------------+--------------------------------------+--------------+
| alpine/3.11/armhf (1 mais) | Alpine 3.11 armhf (20200220_13:00) | armv7l |
+------------------------------+--------------------------------------+--------------+
| alpine/3.11/i386 (1 mais) | Alpine 3.11 i386 (20200220_13:01) | i686 |
+------------------------------+--------------------------------------+--------------+
| alpine/3.11/ppc64el (1 mais) | Alpine 3.11 ppc64el (20200220_13:00) | ppc64le |
+------------------------------+--------------------------------------+--------------+
| alpine/3.11/s390x (1 mais) | Alpine 3.11 s390x (20200220_13:00) | s390x |
+------------------------------+--------------------------------------+--------------+Repositório de imagens local
Para começar a usar o contêiner, é necessário adicionar uma imagem do repositório global ao local. local:. O repositório local está vazio agora, e podemos confirmar isso com o comando lxc image list. Se o método list não especificar um repositório, o repositório local será usado por padrão — local:
lxc image list local:
+-------+-------------+--------+-------------+--------------+------+------+
| ALIAS | FINGERPRINT | PUBLIC | DESCRIÇÃO | ARQUITETURA | TIPO | TAMANHO |
+-------+-------------+--------+-------------+--------------+------+------+O gerenciamento de imagens no repositório é feito pelos seguintes métodos:
Time
Descrição
lxc image alias
Gerenciar aliases de imagens
lxc image copy
Copiar imagens entre servidores
lxc image delete
Excluir imagens
lxc image edit
Editar propriedades da imagem
lxc image export
Exportar e baixar imagens
lxc image importar
Importar imagens para o armazenamento de imagens
lxc image info
Mostrar informações úteis sobre as imagens
lxc image list
Listar imagens
lxc image refresh
Atualizar imagens
lxc image show
Mostrar propriedades da imagem
Estamos copiando a imagem do repositório global para o local. images::
lxc image copy images:alpine/3.11/amd64 local: --alias=alpine3
Imagem copiada com sucesso!Vamos listar todas as imagens disponíveis agora no repositório local. local::
lxc image -c lfdatsu list local:
+---------+--------------+------------------------------------+--------------+
| ALIAS | FINGERPRINT | DESCRIÇÃO | ARQUITETURA |
+---------+--------------+------------------------------------+--------------+
| alpine3 | 73a3093d4a5c | Alpine 3.11 amd64 (20200220_13:00) | x86_64 |
+---------+--------------+------------------------------------+--------------+Configuração do LXD
Além do modo interativo, o LXD também suporta um modo não interativo de configuração, onde a configuração é especificada em um arquivo YAML, um formato especial que permite definir toda a configuração de uma vez, evitando a execução de muitos comandos interativos que foram discutidos acima neste artigo, incluindo a configuração de rede, criação de perfis de configuração, etc. Não vamos abordar essa área aqui, você pode se familiarizar com isso .
O próximo comando interativo lxc config que vamos examinar permite definir a configuração. Por exemplo, para evitar que as imagens carregadas no repositório local sejam atualizadas automaticamente a partir dos repositórios globais, podemos ativar esse comportamento com o seguinte comando:
lxc config set images.auto_update_cached=falseCriação e gerenciamento de contêiner
Para criar um contêiner, usamos o comando lxc init ao qual são passados os valores repositório:imagem e então o identificador desejado para o contêiner. O repositório pode ser especificado como local. local: assim como qualquer repositório global. Se o repositório não for especificado, por padrão, o repositório local é utilizado para procurar a imagem. Se a imagem for especificada de um repositório global, a imagem será primeiro carregada no repositório local e, em seguida, usada para criar o contêiner.
Vamos executar o seguinte comando para criar nosso primeiro contêiner:
lxc init alpine3 alp --storage=hddpool --profile=default --profile=hddrootVamos analisar a ordem das chaves do comando que usamos aqui:
alpine3— Especifica um alias (apelido) para a imagem que foi previamente carregada no repositório local. Se o alias não tiver sido criado para essa imagem, sempre podemos referir a imagem pelo seu Fingerprint que é exibido na tabela.alp— É definida uma identificação para o contêiner--storage— Esta chave indica em qual Storage Pool o contêiner será criado--profile— Estas chaves aplicam em cascata a configuração do contêiner a partir dos perfis de configuração previamente criados.
Iniciamos o contêiner, que começa a iniciar o sistema init da distribuição:
lxc start alpAlém disso, podemos usar o comando lxc launch que permite combinar os comandos lxc init e lxc start em uma única operação.
Verificamos o estado do contêiner:
lxc list -c ns46tb
+------+---------+------------------+------+-----------+--------------+
| NAME | STATE | IPV4 | IPV6 | TYPE | STORAGE POOL |
+------+---------+------------------+------+-----------+--------------+
| alp | RUNNING | 10.0.5.46 (eth0) | | CONTAINER | hddpool |
+------+---------+------------------+------+-----------+--------------+Verificamos a configuração do contêiner:
lxc config show alp
architecture: x86_64
config:
image.architecture: amd64
image.description: Alpine 3.11 amd64 (20200326_13:39)
image.os: Alpine
image.release: "3.11"
image.serial: "20200326_13:39"
image.type: squashfs
volatile.base_image: ebd565585223487526ddb3607f5156e875c15a89e21b61ef004132196da6a0a3
volatile.eth0.host_name: vethb1fe71d8
volatile.eth0.hwaddr: 00:16:3e:5f:73:3e
volatile.idmap.base: "0"
volatile.idmap.current: '[{"Isuid":true,"Isgid":false,"Hostid":1000000,"Nsid":0,"Maprange":65536},{"Isuid":false,"Isgid":true,"Hostid":1000000,"Nsid":0,"Maprange":65536}]'
volatile.idmap.next: '[{"Isuid":true,"Isgid":false,"Hostid":1000000,"Nsid":0,"Maprange":65536},{"Isuid":false,"Isgid":true,"Hostid":1000000,"Nsid":0,"Maprange":65536}]'
volatile.last_state.idmap: '[{"Isuid":true,"Isgid":false,"Hostid":1000000,"Nsid":0,"Maprange":65536},{"Isuid":false,"Isgid":true,"Hostid":1000000,"Nsid":0,"Maprange":65536}]'
volatile.last_state.power: RUNNING
devices:
root:
path: \/
pool: hddpool
type: disk
ephemeral: false
profiles:
- default
- hddroot
stateful: false
description: ""Na seção profiles podemos verificar que este contêiner usa dois perfis de configuração — padrão e hddroot. Na seção devices podemos encontrar apenas um dispositivo, já que o dispositivo de rede foi criado no nível do perfil. padrão. Para ver todos os dispositivos utilizados pelo contêiner, é necessário adicionar a chave --expanded:
lxc config show alp --expanded
architecture: x86_64
config:
image.architecture: amd64
image.description: Alpine 3.11 amd64 (20200326_13:39)
image.os: Alpine
image.release: "3.11"
image.serial: "20200326_13:39"
image.type: squashfs
volatile.base_image: ebd565585223487526ddb3607f5156e875c15a89e21b61ef004132196da6a0a3
volatile.eth0.host_name: vethb1fe71d8
volatile.eth0.hwaddr: 00:16:3e:5f:73:3e
volatile.idmap.base: "0"
volatile.idmap.current: '[{"Isuid":true,"Isgid":false,"Hostid":1000000,"Nsid":0,"Maprange":65536},{"Isuid":false,"Isgid":true,"Hostid":1000000,"Nsid":0,"Maprange":65536}]'
volatile.idmap.next: '[{"Isuid":true,"Isgid":false,"Hostid":1000000,"Nsid":0,"Maprange":65536},{"Isuid":false,"Isgid":true,"Hostid":1000000,"Nsid":0,"Maprange":65536}]'
volatile.last_state.idmap: '[{"Isuid":true,"Isgid":false,"Hostid":1000000,"Nsid":0,"Maprange":65536},{"Isuid":false,"Isgid":true,"Hostid":1000000,"Nsid":0,"Maprange":65536}]'
volatile.last_state.power: RUNNING
devices:
eth0:
name: eth0
network: lxdbr0
type: nic
root:
path: \
pool: hddpool
type: disk
ephemeral: false
profiles:
- default
- hddroot
stateful: false
description: ""Configuração de endereço IP estático
Se tentarmos definir um endereço IP para o dispositivo de rede eth0 com o comando lxc config device set alp destinado à configuração do contêiner, receberemos um erro informando que o dispositivo não existe, pois o dispositivo eth0 que está sendo utilizado pelo contêiner pertence ao perfil padrão:
lxc config device set alp eth0 ipv4.address 10.0.5.5
Error: O dispositivo não existePodemos, é claro, definir um endereço IP estático para eth0 o dispositivo no perfil, mas ele será único para todos os contêineres que utilizarem esse perfil. Portanto, vamos adicionar um dispositivo dedicado para o contêiner:
lxc config device add alp eth0 nic name=eth0 nictype=bridged parent=lxdbr0 ipv4.address=10.0.5.5Em seguida, precisamos reiniciar o contêiner:
lxc restart alpSe agora olharmos para a configuração do contêiner, não precisamos aplicar a opção --expanded para ver o dispositivo de rede eth0, pois o criamos no nível do contêiner e ele sobrescreveu esse mesmo dispositivo do perfil. padrão:
lxc config show alp
architecture: x86_64
config:
image.architecture: amd64
image.description: Alpine 3.11 amd64 (20200326_13:39)
image.os: Alpine
image.release: "3.11"
image.serial: "20200326_13:39"
image.type: squashfs
volatile.base_image: ebd565585223487526ddb3607f5156e875c15a89e21b61ef004132196da6a0a3
volatile.eth0.host_name: veth2a1dc59d
volatile.eth0.hwaddr: 00:16:3e:0e:e2:71
volatile.idmap.base: "0"
volatile.idmap.current: '[{"Isuid":true,"Isgid":false,"Hostid":1000000,"Nsid":0,"Maprange":65536},{"Isuid":false,"Isgid":true,"Hostid":1000000,"Nsid":0,"Maprange":65536}]'
volatile.idmap.next: '[{"Isuid":true,"Isgid":false,"Hostid":1000000,"Nsid":0,"Maprange":65536},{"Isuid":false,"Isgid":true,"Hostid":1000000,"Nsid":0,"Maprange":65536}]'
volatile.last_state.idmap: '[{"Isuid":true,"Isgid":false,"Hostid":1000000,"Nsid":0,"Maprange":65536},{"Isuid":false,"Isgid":true,"Hostid":1000000,"Nsid":0,"Maprange":65536}]'
volatile.last_state.power: RUNNING
devices:
eth0:
ipv4.address: 10.0.5.5
name: eth0
nictype: bridged
parent: lxdbr0
type: nic
root:
path: \/
pool: hddpool
type: disk
ephemeral: false
profiles:
- default
- hddroot
stateful: false
description: ""Remoção do contêiner
O comando para remover o contêiner é lxc delete, mas antes de excluir o contêiner, ele deve ser parado com o comando lxc stop:
lxc stop alplxc list
+------+---------+-------------------+------+-----------+-----------+
| NAME | STATE | IPV4 | IPV6 | TYPE | SNAPSHOTS |
+------+---------+-------------------+------+-----------+-----------+
| alp | STOPPED | 10.0.5.10 (eth0) | | CONTAINER | 0 |
+------+---------+-------------------+------+-----------+-----------+Depois de garantir que o estado do contêiner está STOPPED, ele pode ser removido com Storage Pool:
lxc delete alpAcesso ao contêiner
Para executar comandos no contêiner diretamente, ignorando conexões de rede, use o comando lxc exec que executa comandos no contêiner sem iniciar um shell do sistema. Se você precisar executar um comando no shell, usando padrões de shell, como variáveis, redirecionamentos de arquivos (pipe) etc., será necessário iniciar explicitamente o shell e passar o comando como argumento, por exemplo:
lxc exec alp -- \/bin\/sh -c "echo $HOME"O comando utilizou um caractere especial de escape para o caractere especial $ para que a variável $HOME não seja interpretada na máquina host, mas seja interpretada apenas dentro do contêiner.
Além disso, é possível iniciar o modo interativo do shell e, após concluir, encerrar a sessão pressionando a tecla de atalho CTRL+D:
lxc exec alp -- \/bin\/shGerenciamento de recursos do contêiner
No LXD, é possível gerenciar os recursos do contêiner usando um conjunto específico de configurações. A lista completa dos parâmetros de configuração do contêiner pode ser encontrada .
Limitação de recursos de RAM
Parâmetro limits.memory limita a quantidade de RAM disponível para o contêiner. O valor deve ser um número seguido de um dos .
Vamos definir um limite de RAM de 256 MB para o contêiner:
lxc config set alp limits.memory 256MBAlém disso, existem outros parâmetros para limitar a memória:
limits.memory.enforcelimits.memory.hugepageslimits.memory.swaplimits.memory.swap.priority
Time lxc config show permite exibir toda a configuração do contêiner, incluindo as limitações de recursos que foram aplicadas:
lxc config show alp
architecture: x86_64
config:
image.architecture: amd64
image.description: Alpine 3.11 amd64 (20200220_13:00)
image.os: Alpine
image.release: "3.11"
image.serial: "20200220_13:00"
image.type: squashfs
limits.memory: 256MB
volatile.base_image: 73a3093d4a5ce0148fd84b95369b3fbecd19a537ddfd2e2d20caa2eef0e8fd60
volatile.eth0.host_name: veth75b6df07
volatile.eth0.hwaddr: 00:16:3e:a1:e7:46
volatile.idmap.base: "0"
volatile.idmap.current: '[]'
volatile.idmap.next: '[]'
volatile.last_state.idmap: '[]'
volatile.last_state.power: RUNNING
devices: {}
ephemeral: false
profiles:
- default
stateful: false
description: ""Limitação de recursos de CPU
Para limitar os recursos de CPU, existem vários :
limit.cpu— vincula o contêiner a um ou vários núcleos de CPUlimits.cpu.allowance— gerencia tanto as cotas do planejador CFS, quando o limite de tempo é atingido, quanto um mecanismo universal de compartilhamento de recursos da CPU, quando um valor percentual é atingidolimits.cpu.priority— prioridade do planejador, quando múltiplas instâncias, compartilhando um conjunto de processadores, recebem a mesma porcentagem de processadores
lxc config set alp limits.cpu.allowance 40%lxc config show alp
architecture: x86_64
config:
image.architecture: amd64
image.description: Alpine 3.11 amd64 (20200220_13:00)
image.os: Alpine
image.release: "3.11"
image.serial: "20200220_13:00"
image.type: squashfs
limits.cpu.allowance: 40%
limits.memory: 256MB
volatile.base_image: 73a3093d4a5ce0148fd84b95369b3fbecd19a537ddfd2e2d20caa2eef0e8fd60
volatile.eth0.host_name: veth75b6df07
volatile.eth0.hwaddr: 00:16:3e:a1:e7:46
volatile.idmap.base: "0"
volatile.idmap.current: '[]'
volatile.idmap.next: '[]'
volatile.last_state.idmap: '[]'
volatile.last_state.power: RUNNING
devices: {}
ephemeral: false
profiles:
- default
stateful: false
description: ""Limitação de espaço em disco
Além das limitações como limits.read, limits.write também podemos limitar a quantidade de espaço em disco consumido pelo contêiner (funciona apenas com ZFS ou BTRFS):
lxc config device set alp root size=2GBDepois de definir, no parâmetro devices.root.size podemos verificar a limitação aplicada:
lxc config show alp
...
devices:
root:
path: \/
pool: hddpool
size: 2GB
type: disk
ephemeral: false
profiles:
- default
- hddroot
stateful: false
description: ""Para visualizar as cotas de disco utilizadas, podemos obter da comando lxc info:
lxc info alp
...
Resources:
Processes: 5
Disk usage:
root: 1.05GB
CPU usage:
CPU usage (in seconds): 1
Memory usage:
Memory (current): 5.46MB
Network usage:
eth0:
Bytes received: 802B
Bytes sent: 1.59kB
Packets received: 4
Packets sent: 14
lo:
Bytes received: 0B
Bytes sent: 0B
Packets received: 0
Packets sent: 0Embora tenhamos definido um limite de 2GB para o dispositivo raiz do container, utilitários de sistema como df não verão esse limite. Para isso, vamos realizar um pequeno teste e descobrir como isso funciona.
Vamos criar 2 novos containers idênticos no mesmo Storage Pool (hddpool):
lxc init alpine3 alp1 --storage=hddpool --profile=default --profile=hddroot
lxc init alpine3 alp2 --storage=hddpool --profile=default --profile=hddrootlxc list
+------+---------+------------------+------+-----------+-----------+
| NAME | STATE | IPV4 | IPV6 | TYPE | SNAPSHOTS |
+------+---------+------------------+------+-----------+-----------+
| alp1 | RUNNING | 10.0.5.46 (eth0) | | CONTAINER | 0 |
+------+---------+------------------+------+-----------+-----------+
| alp2 | RUNNING | 10.0.5.30 (eth0) | | CONTAINER | 0 |
+------+---------+------------------+------+-----------+-----------+Em um dos containers vamos criar um arquivo de 1GB:
lxc exec alp1 -- dd if=\/dev\/urandom of=file.img bs=1M count=1000Vamos confirmar que o arquivo foi criado:
lxc exec alp1 -- ls -lh
total 1000M
-rw-r--r-- 1 root root 1000.0M Mar 27 10:16 file.imgSe olharmos no segundo container e verificarmos a existência do arquivo no mesmo lugar, esse arquivo não estará lá, como esperado, pois os containers são criados em seus próprios Storage Volume neste mesmo Storage Pool:
lxc exec alp2 -- ls -lh
total 0Mas vamos comparar os valores que o comando retorna df em ambos os containers:
lxc exec alp1 -- df -hT
Filesystem Type Size Used Available Use% Mounted on
\/dev\/loop1 btrfs 9.3G 1016.4M 7.8G 11% \/
...lxc exec alp2 -- df -hT
Filesystem Type Size Used Available Use% Mounted on
\/dev\/loop1 btrfs 9.3G 1016.4M 7.8G 11% \/
...O dispositivo /dev/loop1 montado como partição raiz é Storage Pool que esses containers usam, portanto eles compartilham seu espaço entre si.
Estatísticas de consumo de recursos
Para visualizar as estatísticas de uso de recursos do container, você pode usar o comando:
lxc info alp
Name: alp
Location: none
Remote: unix:\/\/
Architecture: x86_64
Created: 2020\/04\/08 18:05 UTC
Status: Running
Type: container
Profiles: default, hddroot
Pid: 19219
Ips:
eth0: inet 10.0.5.5 veth2a1dc59d
eth0: inet6 fe80::216:3eff:fe0e:e271 veth2a1dc59d
lo: inet 127.0.0.1
lo: inet6 ::1
Resources:
Processes: 5
Disk usage:
root: 495.62kB
CPU usage:
CPU usage (in seconds): 1
Memory usage:
Memory (current): 4.79MB
Network usage:
eth0:
Bytes received: 730B
Bytes sent: 1.59kB
Packets received: 3
Packets sent: 14
lo:
Bytes received: 0B
Bytes sent: 0B
Packets received: 0
Packets sent: 0Trabalhando com snapshots
No LXD existe a possibilidade de criar snapshots e restaurar o estado do container a partir deles.
Para criar um snapshot, execute o seguinte comando:
lxc snapshot alp snapshot1A comando lxc snapshot não possui uma opção list, portanto, para visualizar a lista de snapshots, você deve usar o comando que exibe informações gerais sobre o contêiner:
lxc info alp
...
...
Snapshots:
snapshot1 (tirado em 2020/04/08 18:18 UTC) (sem estado)Para restaurar o contêiner a partir de um snapshot, você pode usar o comando lxc restore especificando o contêiner do qual a restauração será feita e o nome do snapshot:
lxc restore alp snapshot1O próximo comando serve para excluir um snapshot. Observe que a sintaxe do comando é diferente de todos os outros; aqui, você deve incluir uma barra direta após o nome do contêiner. Se a barra for omitida, o comando de exclusão do snapshot será interpretado como um comando de exclusão do contêiner!
lxc delete alp/snapshot1No exemplo acima, consideramos os chamados snapshots sem estado. O LXD também possui outro tipo de snapshots — com estado, nos quais o estado atual de todos os processos no contêiner é mantido. Com os snapshots com estado, existem várias funções interessantes e úteis.
E mais?
- Para desenvolvedores Python, está disponível o módulo que fornece uma API para o LXD
ATUALIZAÇÃO 10.04.2020 15:00: Adicionei a navegação
Fonte: habr.com
