"description":"Como o OCI Manager Apps transforma um aplicativo com seu banco de dados e cache em LXCs nativos coordenados: plano, rede privada, gancho de dependência, validação e atualizações transacionais."
},
"header":{
"title":"Aplicações com vários contentores",
"description":"Uma aplicação com seu banco de dados, cache e outros serviços torna-se vários LXCs nativos coordenados, instalados e atualizados como um.",
"section":"OCI manager Apps"
},
"sections":[
{
"id":"intro",
"blocks":[
{
"calloutInfo":{
"title":"Um aplicativo para o usuário, vários LXCs para Proxmox VE",
"body":"Uma definição multi-contentor é uma entrada do catálogo. O instalador cria um LXC nativo por serviço e mantém suas dependências explícitas. Immich, Nextcloud, Paperless-ngx e Tandoor são instalados desta forma."
}
},
{
"mermaid":{
"chartCode":"flowchart LR\n C[\"Docker Compose\"] --> O[\"{{plan}}\"]\n O --> A[\"{{app}}<br/>{{appNet}}\"]\n O --> D[\"{{db}}<br/>{{private}}\"]\n O --> R[\"{{cache}}<br/>{{private}}\"]\n V1[(\"config\")] --> A\n V2[(\"database\")] --> D\n V3[(\"cache\")] --> R\n D --> A\n R --> A",
"labels":{
"plan":"Plano de pilha",
"app":"Aplicação LXC",
"appNet":"LAN + privado",
"db":"PostgreSQL LXC",
"cache":"Valkey LXC",
"private":"privado"
}
}
}
]
},
{
"id":"plan",
"title":"1. O plano, antes de existir qualquer recipiente",
"blocks":[
{
"p":"Nenhum LXC é criado enquanto a definição Compose é interpretada. Um plano completo vem em primeiro lugar, com cada membro, imagem, VMID, rede, caminho, segredo, ordem e verificação de saúde. Se o plano não for consistente, a instalação não começa."
},
{
"table":{
"headers":[
"Elemento do plano",
"Conteúdo",
"Verificado antes de criar qualquer coisa"
],
"rows":[
[
"Membros",
"Aplicação principal, banco de dados, cache, aprendizado de máquina e outras dependências",
"VMIDs exclusivos, papéis conhecidos e exatamente um membro principal"
],
[
"Imagens",
"Referência rolling, arquitetura e digest resolvido de cada serviço",
"Todos existem, suportam a arquitetura e passam pela verificação de integridade do OCI"
],
[
"Rede",
"Ponte privada, sub-rede, endereço fixo por serviço e acesso LAN para o membro principal",
"Sem colisão com pontes ou subredes existentes e sem endereço repetido"
],
[
"Persistência",
"Discos de container, diretórios de host, proprietários e backup",
"Nenhum caminho sobreposto, armazenamento disponível e permissões declaradas"
],
[
"Segredos",
"Senha do banco de dados, chaves do aplicativo e credenciais iniciais",
"Gerado uma vez e dado apenas aos membros que os usam"
],
[
"Ciclo de vida",
"Ordem de início, ordem de parada e uma verificação de funcionamento por membro",
"A aplicação principal começa por último e pára primeiro"
]
]
}
}
]
},
{
"id":"create",
"title":"2. Criação, membro por membro",
"blocks":[
{
"steps":{
"items":[
{
"title":"Reserve cada VMID",
"body":"O inventário Proxmox VE e o registro de instância são verificados. Um CT existente não é adotado e um contrato que ainda pertence a outra instância não é reutilizado."
},
{
"title":"Preparar cada imagem",
"body":"Todas as imagens são resolvidas, baixadas e verificadas antes da criação do primeiro container."
},
{
"title":"Criar cada rootfs",
"body":"Os metadados oficiais do OCI são importados, e a identidade da instância ProxMenux e o papel de membro são adicionados."
},
{
"title":"Rede privada",
"body":"Uma ponte e uma sub-rede são criadas e cada membro recebe seu endereço fixo; apenas o membro principal também recebe a interface LAN."
},
{
"title":"Persistência",
"body":"Cada banco de dados e configuração obtém seu próprio disco de contêiner; somente os dados destinados a serem compartilhados usam diretórios de host."
},
{
"title":"Ambiente",
"body":"Endpoints internos, segredos compartilhados e variáveis de serviço são escritos. O aplicativo atinge suas dependências em seus endereços privados reservados."
},
{
"title":"Registro",
"body":"Cada membro registra sua configuração nativa, as mudanças rootfs que as atualizações têm que reproduzir e sua relação com a pilha."
}
]
}
}
]
},
{
"id":"checks",
"title":"3. Iniciar e verificar cada recipiente",
"intro":"Um LXC criado não significa um serviço pronto. As dependências começam em ordem e cada um passa uma verificação específica para o seu serviço.",
"blocks":[
{
"table":{
"headers":[
"Serviço",
"Verificação",
"O que mostra"
],
"rows":[
[
"PostgreSQL",
"<code>pg_isready</code> no CT com o host, usuário e banco de dados esperados",
"O servidor aceita conexões para o banco de dados configurado"
],
[
"Redis / Valkey",
"<code>redis-cli</code> ou <code>valkey-cli</code> <code>PING</code> contra o seu endereço privado",
"O broker escuta e responde"
],
[
"aprendizado de máquina Immich",
"HTTP <code>GET /ping</code>, mais uma verificação do tempo de execução GPU selecionado",
"O serviço responde e a aceleração solicitada não passou a usar a CPU"
],
[
"Nextcloud",
"<code>GET /status.php</code> com <code>installed=true</code>, <code>maintenance=false</code> e <code>needsDbUpgrade=false</code>",
"A inicialização terminou sem migração pendente"
],
[
"Paperless-ngx / Tandoor",
"HTTP no endereço LAN e porto real do serviço",
"O frontend e suas dependências servem a aplicação"
],
[
"Aplicação principal",
"O ponto final do seu modelo, por exemplo, <code>/api/server/ping</code> em Immich",
"A pilha inteira funciona através da aplicação que usa as dependências"
]
]
}
},
{
"calloutWarning":{
"title":"Em execução não significa funcionando corretamente",
"body":"O estado em execução apenas diz que o processo LXC existe. Quando o serviço oferece uma verificação melhor, é utilizado uma verificação exec ou HTTP com tempo limite. Se um membro para ou falha na verificação, a pilha não é declarada instalada."
}
}
]
},
{
"id":"hook",
"title":"4. O gancho de dependência",
"blocks":[
{
"p":"O gancho é definido apenas no recipiente principal de uma pilha dependente. Proxmox VE mantém o script como um trecho e o executa como o <code>hookscript</code> daquele CT. A receita da pilha não é escrita no script: ela vive em um contrato privado separado."
"p":"O script é o mesmo para cada pilha. VMIDs, nomes, tipos de verificação e timeouts vêm do contrato privado <code>/etc/pve/priv/proxmenux-stack-VMID.json</code> de cada pilha."
},
{
"steps":{
"items":[
{
"title":"O Proxmox VE chama pre-start",
"body":"Antes do início do CT principal, o gancho funciona com o seu VMID e a fase do ciclo de vida."
},
{
"title":"Um bloqueio por pilha",
"body":"<code>flock</code> em <code>/run/lock/proxmenux-stack-VMID.lock</code> impede duas sequências de início ao mesmo tempo."
},
{
"title":"O contrato é lido e validado",
"body":"São exigidos um esquema conhecido, dependências numéricas, rótulos, uma verificação exec, http ou running e um tempo limite positivo."
},
{
"title":"Dependências em ordem",
"body":"Cada CT deve existir; um parado é iniciado e um já em execução não é reiniciado."
},
{
"title":"Esperar o serviço ficar pronto",
"body":"A verificação é executada a cada dois segundos, e o CT também é confirmado para estar ainda em execução."
},
{
"title":"O CT principal começa",
"body":"Quando cada dependência está pronta, termina pré-inicial e o Proxmox VE inicia a aplicação."
}
]
}
},
{
"calloutInfo":{
"title":"O gancho não para dependências",
"body":"As fases pós-início, pré-parar e pós-parar não fazem nada. Parar o CT principal deixa PostgreSQL, Redis, Valkey ou aprendizado de máquina em execução. O gancho ordena o início; ele não transforma vários LXCs em um processo."
}
},
{
"table":{
"headers":[
"Tipo",
"Exemplo",
"Início posterior"
],
"rows":[
[
"Pilha dependente",
"Immich, Nextcloud, aplicação com PostgreSQL",
"O gancho do CT principal inicia as dependências e espera por eles"
],
[
"Conjunto de aplicativos",
"Conjunto de aplicações Arr",
"Nenhum membro principal: cada LXC segue o seu próprio <code>onboot</code>"
],
[
"Aplicação única",
"Jellyfin",
"Proxmox VE inicia esse LXC diretamente"
]
]
}
}
]
},
{
"id":"stack-checks",
"title":"5. Verifica a pilha como um todo",
"blocks":[
{
"list":{
"items":[
"Cada VMID existe, é único e mantém a identidade de instância esperada.",
"Cada contrato pertence à mesma pilha, mantém seu papel e sua configuração nativa gravada.",
"O membro principal é o último em <code>start_order</code> e o primeiro em <code>stop_order</code>.",
"Nenhum membro, volume ou adaptação rootfs está faltando.",
"O gancho aponta para o trecho oficial, seu conteúdo é inalterado e seu contrato corresponde à receita da pilha.",
"As imagens e digests observados correspondem aos arquivos OCI preparados.",
"Dispositivos, perfis GPU, montagens, segredos e endpoints ainda correspondem aos contratos.",
"Após cada dependência passar, o ponto final da aplicação principal verifica a integração entre os membros."
]
}
}
]
},
{
"id":"manage",
"title":"Gerenciando uma pilha instalada",
"intro":"Em <strong>Gerenciar aplicativos OCI instalados</strong>, qualquer membro leva à pilha inteira. O menu oferece <strong>Atualizar todos os contêineres do aplicativo</strong>, <strong>Modificar caminhos e dispositivos extras</strong>, <strong>Recriar todos os contêineres com a configuração salva</strong> e <strong>Remover: excluir o aplicativo e seus contêineres</strong>. Recriar reconstrói cada contêiner da imagem original pelo digest salvo, sem procurar uma mais nova, seguindo os mesmos passos de uma atualização. A opção aparece para pilhas que podem ser atualizadas.",
"blocks":[
{
"steps":{
"items":[
{
"title":"Qualquer membro",
"body":"O contrato dos membros nomeia o VMID principal e a lista completa de membros."
},
{
"title":"A pilha é reprodutível",
"body":"Identificações, contratos, gancho, adaptações e uma repetição coordenada para essa receita são validadas."
},
{
"title":"Primeiro as imagens",
"body":"O aplicativo não é parado até que cada digest tenha sido resolvido, baixado e verificado."
},
{
"title":"Parar e fazer backup do conjunto",
"body":"Os backups nativos verificados são feitos com a pilha parada, então a aplicação e os bancos de dados pertencem ao mesmo momento."
},
{
"title":"Atualizar e verificar cada membro",
"body":"Adaptação, rede, montagens, segredos e dispositivos são novamente aplicados antes do exame de saúde de cada membro."
},
{
"title":"Publicar ou recuperar tudo",
"body":"Os novos contratos só são publicados quando todos os membros passam. Se alguém falhar, cada membro é restaurado."
}
]
}
},
{
"table":{
"headers":[
"Modificar",
"O que muda",
"O que é guardado"
],
"rows":[
[
"Caminhos e dispositivos adicionais",
"Eles são adicionados ou removidos do recipiente de aplicação, que reinicia. Nada é reconstruído.",
"Os dados da aplicação, da sua base de dados e dos outros contentores"
],
[
"O que executa o reconhecimento (Immich)",
"O recipiente de aprendizado de máquina leva a imagem e os dispositivos do CPU ou de um GPU do hospedeiro. Todo o aplicativo é parado e atualizado, como em uma atualização",
"O cache do modelo, a biblioteca e o banco de dados. Se algo falhar, os recipientes anteriores e a escolha anterior são restaurados"
]
]
}
},
{
"calloutWarning":{
"title":"Uma pilha sem uma repetição coordenada não é atualizada",
"body":"Se a preparação de uma pilha não puder ser reproduzida, a atualização é recusada antes de a pilha ser parada. A aplicação continua a correr como está."
}
}
]
},
{
"id":"failure",
"title":"6. Quando algo falha",
"blocks":[
{
"p":"Durante uma instalação, um erro para e remove os contêineres incompletos que a operação criou, e a ponte privada se a operação criou. Uma pilha não é publicada como válida até que toda a sequência termine."
},
{
"p":"Uma atualização é transacional: todas as imagens são preparadas primeiro, depois a pilha pára, cada membro é feito backup e o backup verificado, e só então é substituído cada rootfs. Os membros começam um a um com seu exame de saúde; se um falha, cada membro é restaurado a partir do mesmo conjunto de backups, de modo que o banco de dados eo aplicativo nunca pertencem a momentos diferentes."
"chartCode":"flowchart TB\n P[\"{{prepare}}\"] --> S[\"{{stop}}\"]\n S --> B[\"{{backup}}\"]\n B --> R[\"{{replace}}\"]\n R --> H{\"{{healthy}}\"}\n H -- \"{{yes}}\" --> C[\"{{commit}}\"]\n H -- \"{{no}}\" --> X[\"{{rollback}}\"]",