Funcionalidades básicas do LXD - sistema de containers no Linux

Funcionalidades básicas do LXD - sistema de containers no Linux

LXD — é um gerenciador de contêineres de próxima geração, assim diz fonte. 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 um pacote snap:

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 aberto

Executando 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✓  core

Apesar 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 lxd

Vamos 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 lxd

Apó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 user1

Vamos 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 lxd

Se 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 lxd

Iniciamos o serviço:

sudo systemctl start lxd

Verificamos o status do serviço:

sudo systemctl status lxd

Armazenamento do LXD ^

Antes de iniciar a inicialização, precisamos entender como o armazenamento está logicamente estruturado no LXD.

Armazenamento (Storage) é composto 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

Funcionalidades básicas do LXD - sistema de containers no Linux

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/alp3

Antes 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 comando

Inicializaçã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 uma tabela comparativa de recursos:

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, veja a resposta do desenvolvedor. 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/s

Vamos conectar o arquivo loopback a um dispositivo loopback livre:

sudo losetup --find --show /mnt/work/lxd/hddpool.img

/dev/loop1

Graç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     512

Da 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/loop1

Vamos 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/hddpool

Inserçã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
EOF

Ativamos 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 root

Para aplicar as alterações, o serviço LXD precisa ser reiniciado:

sudo systemctl restart lxd

Criaçã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:

Funcionalidades básicas do LXD - sistema de containers no Linux

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=none

Verifique 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 0

Perfil 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:

Funcionalidades básicas do LXD - sistema de containers no Linux

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 perfil

Ediçã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 tipo nic conectado ao switch (bridge de rede) lxdbr0
  • root — Dispositivo do tipo disk que utiliza o pool de armazenamento ssdpool

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=ssdpool

De 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=hddpool

Verificando 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 AlpineLinux:

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 na documentação.

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=false

Criaçã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=hddroot

Vamos 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 alp

Alé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 existe

Podemos, é 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.5

Em seguida, precisamos reiniciar o contêiner:

lxc restart alp

Se 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 alp

lxc 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 alp

Acesso 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\/sh

Gerenciamento 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 na documentação.

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 sufixos disponíveis..

Vamos definir um limite de RAM de 256 MB para o contêiner:

lxc config set alp limits.memory 256MB

Além disso, existem outros parâmetros para limitar a memória:

  • limits.memory.enforce
  • limits.memory.hugepages
  • limits.memory.swap
  • limits.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 tipos de limitações:

  • limit.cpu — vincula o contêiner a um ou vários núcleos de CPU
  • limits.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 é atingido
  • limits.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=2GB

Depois 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: 0

Embora 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=hddroot

lxc 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=1000

Vamos 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.img

Se 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 0

Mas 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: 0

Trabalhando 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 snapshot1

A 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 snapshot1

O 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/snapshot1

No 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 PyLXD que fornece uma API para o LXD

ATUALIZAÇÃO 10.04.2020 15:00: Adicionei a navegação

Fonte: habr.com

Compre hospedagem confiável para sites com proteção contra DDoS, servidores VPS VDS 🔥 Compre hospedagem confiável para sites com proteção contra DDoS, servidores VPS VDS | ProHoster