- Se você já usa imagens Docker como unidade de implantação, o Nix pode ser um ponto de entrada para experimentar builds determinísticos e fixação de dependências sem mudar drasticamente o workflow
- Um
docker build comum tende a depender do estado da internet pública, como repositórios Ubuntu ou downloads externos, o que dificulta reproduzir a mesma imagem algum tempo depois
- O Nix conhece as dependências necessárias no nível de pacote, então com
dockerTools.buildLayeredImage é possível configurar o reenvio apenas das camadas alteradas
- O exemplo baseado em Go
douglas-adams-quotes mostra que é possível gerar o binário com pkgs.buildGoModule e carregá-lo para implantação como uma imagem Docker comum com nix build .#docker e docker load < ./result
- Se vários serviços em um monorepo compartilham camadas e o cache do Nix, é possível reduzir tempo e custo de build em reproduções de imagens de commits antigos e em implantações repetidas
Por que acoplar o Nix ao build de imagens Docker
- O Nix reúne três características: gerenciador de pacotes, linguagem e sistema operacional
- Você escreve instruções de build de pacotes na linguagem Nix
- O gerenciador de pacotes Nix pode, com base nessas instruções, criar software, ferramentas, imagens NixOS, imagens de contêiner e mais
- Não é fácil introduzir o Nix em pipelines CI/CD já existentes
- O Nix é diferente da forma com que muitos desenvolvedores estão acostumados
- A barreira de adoção é grande, a menos que seja um ambiente novo, como uma startup recém-criada ou um homelab
- Organizações que já usam Docker podem começar aplicando o Nix ao build de imagens de contêiner para experimentar sem quebrar muito o fluxo atual de implantação
Onde o build do Docker oscila
- Docker e conteinerização foram amplamente adotados, e imagens Docker são usadas como um formato de pacote universal de fato na internet
- Plataformas como Fly.io, Railway e Render usam imagens Docker em vez de imagens arbitrárias de VM ou programas Linux em tarball
- Builds com Docker nem sempre são determinísticos
- A maioria dos Dockerfiles vistos na internet costuma funcionar, mas a pequena porcentagem que falha pode virar problema operacional
- Um dos maiores pontos frágeis é que o build do Docker acessa a internet pública
- Isso é necessário para baixar pacotes de repositórios Ubuntu
- Quando você precisa recriar a mesma imagem depois, é difícil restaurar exatamente o estado dos repositórios Ubuntu daquela época
- O Ubuntu 18.04 está próximo do fim do suporte em seu respectivo ciclo, então você pode só descobrir o que dependia dessa versão depois que ocorrer uma falha
- Adicionar pacotes a uma imagem Docker de forma simples pode gerar desperdício de espaço
- Executar
apt-get upgrade no começo do build pode substituir arquivos dentro da imagem de contêiner
- Os arquivos anteriores à substituição podem permanecer na camada, acumulando-se como cópias sombreadas
Como o Nix lida com camadas Docker
- O Nix conhece de antemão as dependências necessárias e pode dividi-las no menor número possível de camadas Docker
- Alterar uma linha de código não exige que
apt ou npm reinstalem dependências
- Só o mínimo que realmente mudou precisa ser refletido na atualização da imagem
dockerTools é um conjunto de ferramentas para colocar pacotes Nix e suas dependências dentro de imagens Docker
- As imagens Docker criadas com Nix se dividem em dois grandes modos
- Imagem sem camadas: coloca o programa, dependências e itens extras como certificados raiz TLS em uma única pasta e os expõe como uma imagem de camada única
- Imagem em camadas: coloca cada dependência em uma camada separada da imagem, permitindo enviar apenas o que realmente mudou
- O próprio Docker também tem armazenamento baseado em conteúdo, mas é difícil aproveitá-lo bem apenas com
docker build
- As imagens em camadas do Nix usam camadas por pacote, então dependências como
glibc só precisam ser enviadas uma vez
- Porém, se essa biblioteca precisar de modificação, essa camada terá de ser reenviada
Exemplo de serviço Go: Douglas Adams Quotes
- O serviço de exemplo é um programa em Go que fornece citações de Douglas Adams
- Projetos com módulos Go definem o pacote Nix com
pkgs.buildGoModule
bin = pkgs.buildGoModule {
pname = "douglas-adams-quotes";
inherit version;
src = ./.;
vendorHash = null;
};
- Essa definição usa o template de módulos Go para configurar o compilador Go, o compilador C para CGo e o download de dependências externas
pname é o nome do pacote
version é gerada automaticamente a partir do commit Git do serviço
src = ./.; usa o código-fonte do diretório de trabalho atual
- Se não houver dependências além da biblioteca padrão,
vendorHash = null pode ser usado
- Se houver dependências externas, você pode especificar o hash de todas elas ou usar
gomod2nix
- O pacote é compilado com o seguinte comando
nix build .#bin
- A imagem Docker em camadas pode ser criada com
dockerTools.buildLayeredImage
docker = pkgs.dockerTools.buildLayeredImage {
name = "registry.fly.io/douglas-adams-quotes";
tag = "latest";
config.Cmd = "${bin}/bin/douglas-adams-quotes";
};
- Ao apontar
config.Cmd para o binário compilado do servidor, os itens necessários são copiados junto
glibc e outros componentes necessários para execução também são incluídos
- Pacotes extras, como certificados raiz de CA, podem ser adicionados em
contents
docker = pkgs.dockerTools.buildLayeredImage {
name = "registry.fly.io/douglas-adams-quotes";
tag = "latest";
contents = with pkgs; [ cacert ];
config.Cmd = "${bin}/bin/douglas-adams-quotes";
};
- A imagem é criada e carregada no daemon do Docker com os seguintes comandos
nix build .#docker
docker load < ./result
- Ao abrir com
dive, cada camada adiciona pacotes diferentes do nixpkgs, e no fim os itens são vinculados simbolicamente à raiz da imagem
O efeito de compartilhar camadas em um monorepo
- Se houver vários serviços no mesmo repositório, as imagens Docker criadas com Nix podem compartilhar camadas
- Isso acontece sem configuração extra
- Fazer isso só com Docker costuma levar à criação de várias imagens base comuns, que podem incluir ferramentas e conteúdo desnecessários para alguns serviços
- Como exemplo, o repositório
Xe/x é um monorepo com 10 anos de side projects, experimentos e ferramentas
- Vários projetos são implantados em cerca de três plataformas
- O trabalho vinha sendo feito para convergir para uma base comum de implantação usando imagens Docker
- Ao enviar a atualização de um serviço, parte da atualização compartilhada com a maioria dos outros serviços também é enviada
- Essa abordagem economiza tempo e dinheiro em vários projetos
O que é difícil acompanhar apenas com cache do Docker
- Em teoria, usando cache do Docker, também seria possível construir imagens com eficiência comparável à do Nix
- Mas obter o mesmo efeito com a abordagem do Docker pode tornar as etapas de build difíceis de manter
- É preciso instalar bibliotecas compartilhadas em camadas separadas
- É necessário reativar a pilha de rede durante o build, o que enfraquece a reprodutibilidade
- Também é preciso reajustar caminho de busca, flags do compilador e configurações relacionadas a CGo
dockerTools.buildLayeredImage do Nix lida por você com os detalhes de colocar pacotes no contêiner, simplificando a configuração
Reprodução de momentos passados e cache do Nix
- Uma das maiores vantagens do Nix é poder recompilar exatamente o software de um ponto específico no passado
- Quando for necessário reproduzir depois um bug em um ambiente específico de cliente, será possível recriar a mesma imagem Docker
- O build do pacote serve para fixar todo o estado do software e das dependências em um momento específico
XeDN é um projeto desenvolvido há anos, e a versão de 14 meses atrás pode ser compilada com o seguinte comando
nix build github:Xe/x/567fdc2#xedn-docker
- Esse comando compila o alvo
xedn-docker de um commit específico do repositório GitHub Xe/x
- Ao carregá-lo no daemon do Docker, é gerada uma imagem com os mesmos bytes daquela época
- O exemplo inclui até o Go 1.19
- Reproduzir o mesmo resultado apenas com builds Docker comuns pode exigir muito custo de armazenamento
- O cache do Nix armazena a saída de comandos Nix para evitar recompilar depois
- Assim, não é preciso recompilar pacotes como
nokogiri em cada notebook de desenvolvedor
- É possível receber o resultado já compilado na nuvem
- O Garnix é usado no CI de todos os projetos com flakes e informa o status de build a cada commit
- Configurações de máquinas de homelab também são compiladas com Garnix, de modo que, nas atualizações noturnas, a configuração mais recente é obtida do cache do Garnix em vez de ser recompilada localmente
Conclusão como artefato de implantação
- O Nix pode ser considerado um construtor de imagens Docker melhor do que o construtor de imagens do Docker
- O Nix faz você especificar o resultado, em vez de listar manualmente os detalhes do procedimento para chegar até ele
- Em ambientes que já usam Docker, começar pelo build de imagens Docker com Nix é um caminho realista de adoção
- Imagens Docker criadas com Nix podem compartilhar camadas entre várias partes de um monorepo
- Graças ao cache binário, não é necessário recompilar código que já foi compilado no passado
- O artefato final continua sendo uma imagem de contêiner comum, implantável em plataformas como AWS, Google Cloud e Fly.io
1 comentários
Opiniões no Hacker News
Tentei gostar do Nix várias vezes, mas agora sinto que preciso desistir
Tenho 2 sistemas rodando Nix e morro de medo de mexer neles; no passado, os dois já quebraram e precisei reinstalar tudo do zero. Em teoria, o Nix é idempotente e determinístico, mas, na prática, você precisa entender determinístico em relação a quê; se não conhecer a fundo o comportamento de todas as partes de que depende, acaba encontrando resultados estranhos, erros bizarros e nada úteis, ou então situação sem nenhum feedback
A documentação, mesmo quando completa e tecnicamente correta, é terrivelmente obscura, e os tutoriais só te levam até 80% do caminho. Se você sai dali, precisa construir por conta própria, e isso exige anos de conhecimento acumulado e frustração, algo que não quero mais encarar. A vantagem do Docker é justamente o caos em si: sabendo mais ou menos shell e o gerenciador de pacotes da distribuição, dá para montar quase qualquer coisa; quando algo dá errado, ferramentas com décadas de história tornam o diagnóstico e a correção muito mais fáceis
O Nix é como o Emacs: com paciência e conhecimento profundo e visionário, dá para fazer qualquer coisa, mas parece que as únicas opções são mergulhar completamente nele ou observá-lo de longe
Se você fizer build duas vezes seguidas na mesma máquina, o resultado sai igual; mas, com o tempo, quando o gerenciador de pacotes e as tags das imagens base forem atualizados, o resultado de um rebuild um mês depois será completamente diferente. Quando a equipe gerencia uns 40 contêineres, consertar contêineres vira uma parte grande do trabalho
Então, em teoria, o Nix deveria ser perfeito, mas ele é diferente demais: ferramentas de fornecedores não rodam no Nix e, quando aparece um erro, é difícil encontrar rapidamente uma solução na web. Por causa dessa frustração, criei https://www.stablebuild.com, que oferece builds determinísticos baseados em Docker sobre contêineres Ubuntu, Debian e Alpine. Ele é composto por um cache pull-through imutável do Docker Hub, cópias completas diárias dos repositórios de pacotes do Ubuntu/Debian/Alpine, cópias diárias de PPAs populares e do índice PyPI, além de um cache imutável de arquivos/URLs arbitrários
Na prática, é fácil de usar, fácil de depurar e tem ampla compatibilidade de software; nos contêineres migrados para o StableBuild, houve 0 problemas causados por não determinismo
Já gastei tempo tentando entender por que
nix developnão se comporta como a documentação diz e como fazê-lo se comportar daquele jeito. A documentação diz que, por padrão, você entra no ambiente de tempo de build, mas o padrão real não é isolado; os nomes das opções de linha de comando também são confusos, então tive de extrair conhecimento do código-fonteTambém gostaria que os edge cases que quebram a reprodutibilidade fossem tratados de forma mais explícita. Código de ponto flutuante é sensível à ordem das operações, e o estado pode vazar por causa da preempção do sistema operacional; se nem o óbvio for explicitado, as pessoas acabam dando um tiro no próprio pé
Usando a configuração “Erase your darlings”, dá para eliminar a maior parte do estado não reprodutível fora das contas de usuário. É meio trabalhoso, mas o que no NixOS não é trabalhoso?
https://grahamc.com/blog/erase-your-darlings/
Não me preocupo com o interior das contas de usuário, e não gosto do Home Manager
Docker é ok, mas no Mac o desempenho não é grande coisa; já o Nix é bom porque torna trivial instalar e fazer as coisas funcionarem do mesmo jeito em várias máquinas. A documentação do Nix é péssima, mas o ChatGPT-4 foi excelente para resolver problemas com Nix
Tenho a impressão de que 90% dos problemas com Nix surgiram porque eu estava tentando fazer coisas que não eram do “jeito Nix”
Já usei praticamente todos os gerenciadores de pacotes de distribuições e, mesmo sem fazer nada especial, consegui quebrar todos eles de alguma forma. O Fedora Kinoite tem aguentado bem adicionar/remover camadas, atualizações diárias e até rebases a partir do Silverblue. Pessoalmente, acho que o rpm-ostree vai acabar substituindo o Nix
Vejo o Nix e o NixOS em um estado parecido com o do git antes do GitHub.
A ideia básica se apoia em ciência da computação mais séria do que as abordagens existentes, como SVN ou Docker, e o encanamento interno ainda tem problemas, mas não é pior; porém as ferramentas e a documentação voltadas ao usuário ainda não estão no nível de adoção mainstream.
Com o lançamento do flox, isso pode ter mudado: https://flox.dev. É quase fluido, e, vindo de ex-DE Shaw, isso nem surpreende.
O Nix não faz muito sentido sem flakes e
nix-command, mas ambos estão documentados como experimentais e vêm desativados por padrão. A documentação está melhorando, mas ainda é insuficiente, e nixlang é incrível quando você aprende bem, mas não é aceitável como barreira de entrada para o mainstream.nix-env -iA foona prática quase nunca é o comportamento que você quer, então o Nix está mais para uma arma secreta de empresas que conseguem bancar expertise interna do que para o gerenciador de pacotes do jeito que ele é anunciado.O flox reduz a barreira para “experimentar uma vez e imediatamente ter uma experiência melhor”. O Nix/NixOS, ou algo parecido, acabará empurrando o Docker para trás como o Git fez com o Subversion, mas isso não vai acontecer até surgir um momento tipo GitHub — e então vai acontecer de uma vez.
A maioria das reclamações sobre Nix nesta thread está tecnicamente errada, mas é totalmente compreensível e, mais importante, não é culpa do usuário. Sei que está diminuindo a geração que conheceu um mundo sem git/GitHub, mas seria bom ouvir Linus explicando, diante de pessoas que passaram pelos critérios iniciais de contratação do Google, por que elas deveriam se interessar por uma ferramenta que parece complicada.
https://youtu.be/MjIPv8a0hU8?si=QC0UnHXRdMpp2tI4
Estamos construindo isso há algum tempo, e gostaríamos de ouvir se há algo que deveríamos priorizar para torná-lo melhor.
O git é assim, mas o Nix ainda não é, então não tenho tanta certeza de que um momento tipo GitHub vá ajudar.
Este post do blog deixa de explicar por que camadas Docker compartilhadas são úteis.
O motivo é cache: quanto mais imagens compartilham as mesmas camadas, mais coisas podem ser cacheadas, tornando a inicialização de contêineres mais rápida.
O Docker é fraco nisso porque, para obter os benefícios de cache, ao criar uma imagem você precisa produzir camadas o mais iguais possível às camadas existentes. Por exemplo, se você executar
apt-get install python3hoje e não houver novas atualizações, deveria sair exatamente a mesma camada de ontem, mas camadas Docker são cacheadas por hash de arquivo, então todos os arquivos precisam ser exatamente iguais, inclusive metadados como hora de criação.O Nix já armazena dependências por hash, então, com a mesma versão e a mesma configuração, a camada sempre será a mesma.
Dependências normalmente formam um grafo, não uma árvore, então isso fica inconveniente rapidamente. Ferramentas alternativas como Nix, e talvez Bazel, não têm essa restrição e conseguem mapear o grafo de dependências para camadas Docker para obter cache granular. É algo que não dá para expressar com Dockerfile.
apt-getnão é invalidada automaticamente.Ela só muda se você usar
--no-cacheou se houver alguma alteração em uma camada anterior.Passei os últimos 2 ou 3 dias tentando fazer build de imagens Docker no Darwin, e este texto parece o universo tirando sarro de mim
Nix é claramente a melhor ferramenta para o objetivo desejado, mas tem alguns cantos escuros e abandonados que sugam a alma. Eu gosto dele, mas às vezes me sinto como o Morty arrastado para as aventuras do Rick na terra dos compiladores
Docker, em essência, é mais uma prisão para binários Linux. No Linux, você compila o binário, coloca no contêiner e pronto; o código Nix também é simples. Se você consegue compilar o código, criar o contêiner é só mais uma etapa
Mas Docker exige binários Linux e, no Mac, o Docker Desktop sobe uma VM Linux, faz todo o trabalho dentro dela e depois esconde esse fato. O Nix não faz isso, então há duas opções
A primeira é fazer compilação cruzada, mas isso exige que até a glibc consiga ser compilada de forma cruzada; mesmo que a maioria das dependências da comunidade funcione, alguns pacotes podem ter autores que não se preocuparam com cross-compilation. Além disso, o Hydra, que preenche o cache padrão do Nix, não faz builds com compilação cruzada, então você pode compilar por muito tempo e acabar falhando
A segunda é conectar um builder Linux ao Mac e enviar para ele os trabalhos de build
x86_64-linux. Pode ser uma máquina física, uma VM ou até um contêiner Docker com NixOSA opção 1 parece o jeito correto, mas a 2 é mais prática. Os problemas que você está enfrentando provavelmente vêm de tentar a opção 1, e isso exige bastante experiência não só com Nix, mas também com cross-compilation. Seria bom se o Hydra também fizesse builds de cross-compilation de Darwin para Linux, fornecendo cache e evitando quebras, mas isso também aumentaria os custos. Parece melhor tentar a solução 2
Acho que havia uma solução oficial: https://ryantm.github.io/nixpkgs/builders/special/darwin-bui...
Em algumas stacks, a compilação cruzada não é muito robusta, então compilamos de forma cruzada
{aarch64,amd64} x {linux,darwin}com Docker. Estamos rodando vários Dockers sobre Darwin aarch64 para compilar tudo, e a experiência foi boahttps://github.com/gytis-ivaskevicius/high-quality-nix-conte...
Há um ou dois bugs, mas parecem ser principalmente relacionados a volumes, e ele lida bem com builds de imagens Docker. A parte mais problemática é a velocidade, porque a combinação do hardware com o fato de o Docker precisar emular uma VM Linux acaba dando um baque
A experiência de criar uma imagem Docker para uma aplicação Java com Nix não foi muito boa
Depois que o
gradle2nixfoi descontinuado, não parece haver uma alternativa clara para aplicações Java baseadas em Gradle. Certa vez pedi a um amigo para tentar criar a menor imagem Docker possível de uma aplicação Spring Boot simples, e o resultado feito com Nix ficou com o dobro do tamanho da imagem feita sem NixO código pode ser visto aqui: https://github.com/jossephus/Docker_challenge/blob/main/flak...
Entram juntos o zulu e o JDK que o gradle inclui como argumento
jdk. Se você olhar ogradleGenno nixpkgs, dá para entender o que isso significa. Desculpa pelogradle2nix; estou trabalhando em uma melhoria menos “gambiarra”Era mais ou menos assim: https://gist.github.com/takeda/17b6b645ad4758d5aaf472b84447b...
Fiz link de tudo com musl, compilei o Python desativando todos os pacotes que a aplicação não usava e removi partes não utilizadas do boto3/botocore. Só boto3/botocore já passa de 100 MB
Pacotes Nix, por padrão, miram o sistema operacional NixOS, então em situações comuns, com bastante espaço em disco, a expectativa é que todos os recursos estejam ativados. Por isso muitas dependências desnecessárias vêm junto. Já as imagens Alpine são projetadas para Docker, e o objetivo é desativar recursos extras nos pacotes, então o resultado é menor
Para criar imagens pequenas, é preciso desligar o que não é necessário com
override. Por exemplo, o zulu inclui coisas como alsa, fontconfig, freetype, xorg, cups, gtk, cairo e ffmpeg. Seu amigo extraiu cuidadosamente só os arquivos necessários e os colocou no contêiner, enquanto do lado do Nix foi incluído o pacote zulu inteiro com todas as dependênciasPrimeiro corrija para incluir apenas um JDK; depois dá para reduzir ainda mais o tamanho do JDK com os métodos acima. Usar
openjdk_headlesstalvez seja mais simpleshttps://github.com/NixOS/nixpkgs/blob/master/pkgs/developmen...
https://github.com/GoogleContainerTools/jib/tree/master/jib-...
openjdk_headlesspula as dependências de GTK e X que o Spring não precisaEm vez do Zulu, mudei para usar como base o build headless do OpenJDK do Nixpkgs, removendo dependências de bibliotecas de GUI, e criei um JRE mínimo customizado com
pkgs.jre_minimalejlinkO tamanho da imagem ficou em 161 MB, um pouco maior que a imagem
demo_jlink. Isso porque o JRE fica com cerca de 90 MB ao incluir todos os módulos realmente necessários para executar a aplicação. A chamada ajdepsnoDockerfile_jlinknão detectava todos os módulos e estava criando um JRE apenas comjava.base. Se eu colocar apenasjava.baseno meu JRE mínimo, o tamanho do JRE fica em cerca de 50 MB, e a imagem de contêiner quebrada fica com 117 MB no PodmanTambém removi o
copyToRootincorreto na chamada adockerTools.buildImage. Só o contexto de string deconfig.Cmdjá coloca o app na imagem, mas o código anterior estava copiando o app mais uma vez. Além disso, troquei paradockerTools.buildLayeredImage, colocando cada store path em uma camada de imagem separada; isso ajuda na escalabilidade de espaço ao compartilhar dependências entre várias imagens de contêiner, mas não afeta um experimento com uma única imagemO que resta é, em grande parte, otimizar o tamanho do JRE. A
glibcé a próxima maior dependência, com cerca de 30 MB, aparentemente porque o Nixpkgs compila a glibc para oferecer suporte a muitos locales e codificações de caracteres. Não sei se é possível, nem se seria prático, separar isso em uma derivation ou output à parte para tornar a escolha opcional. Se várias imagens forem criadas comdockerTools.buildLayeredImage, partindo do pressuposto de que usam o mesmo commit do Nixpkgs, a glibc e o restante das dependências serão todos compartilhadoshttps://github.com/max-privatevoid/hackernews-docker-challen...
Se você já adotou Nix, ótimo; seria bom ver o gerenciamento declarativo de pacotes, como Nix ou Guix, se espalhar mais
Se você já usa Docker, mas quer introduzir Nix gradualmente, também existe a abordagem desta apresentação: https://youtu.be/l17oRkhgqHE
Em vez de migrar de uma vez a configuração e o build de contêineres para Nix, dá para manter o Dockerfile e fazer com que ele construa a configuração do Nix. A maior desvantagem é que não se aproveitam as camadas de forma alguma; a vantagem é poder mudar o Dockerfile gradualmente enquanto se reutiliza a infraestrutura ou a automação Docker existente
[1] https://mitchellh.com/writing/nix-with-dockerfiles
Mas me parece que boa parte dessa não reprodutibilidade vem do fato de não haver garantia de que uma das camadas do Docker continuará disponível. Nesse caso, também fico curioso se no Nix há garantia de que as versões dos pacotes permanecerão no repositório para sempre
Meio do nada, mas a ilustradora Annie Ruygt, que fez as imagens destes slides, também criou os logotipos e a arte de marca de duas startups da YC: RethinkDB (rethinkdb.com) e Pachyderm (pachyderm.io)
Trabalhei na Pachyderm, que foi fundada por ex-engenheiros da RethinkDB. O trabalho dela é realmente muito bom
Há relativamente pouco tempo, por recomendação da equipe de infraestrutura, passei mais ou menos meio dia tentando criar imagens-base de CI com Nix; a imagem ficou enorme e algumas partes não funcionavam por problemas de linkagem
O que me irritou especialmente foi que, ao criar imagens multi-arquitetura, ele tentava realmente executar alguma coisa de outra arquitetura, e só dava suporte à virtualização de hardware do qemu. A máquina de build e a workstation são VMs, então não têm isso, embora tenham
binfmt-misc. Se ele tivesse feito fork/exec de ummkdirarm64 para criar/tmp, teria funcionado; mas uma camada Docker é apenas um arquivo tar, então dá para criar o diretório assimecho "tmp uid=0 gid=0 time=0 mode=0755 type=dir" | bsdtar -cf - @-É bem provável que essa camada exata já exista em algum lugar, então talvez o usuário nem precisasse baixá-la
Toda vez que tento Nix, fico com a sensação de que, em mais alguns meses, talvez dê para usá-lo regularmente. O nixpkgs tem quase todos os pacotes que quero e instala bem na workstation. Mas uma exigência como “preciso de bash, python, build-essential e Bazel” não parece ser o objetivo de um builder de imagens Docker. Para colocar um único binário Go em uma imagem Docker, não preciso de Nix. Basta pegar distroless e colocar a aplicação em um arquivo tar, e isso vira um contêiner. Pessoalmente uso
rules_ocido Bazel; internamente ele faz mais ou menos isso, com só um pouco mais de inteligência para construir binários para várias arquiteturas, criar o YAML do índice de imagens e fazer push para o registryClaro, os arquivos de build do pacote precisam dar suporte a isso. Também não me parece correto dizer que o qemu só dá suporte à virtualização de hardware. Dá para usar qemu até em arquiteturas nas quais só é possível emulação por software
Um exemplo mínimo é abordado aqui: https://discourse.nixos.org/t/how-do-i-get-a-shell-nix-with-...
Não quero chegar a dizer que deveria ser tão simples quanto usar
pkgCross, mas fico curioso sobre quais problemas concretos você encontrou no processo normalhttps://nix.dev/tutorials/cross-compilation.html
Já a usamos em alguns projetos por anos, mas, se o tamanho for um problema, é possível criar imagens bem pequenas contendo apenas o que foi especificado, do modo mencionado no texto
[1] https://hub.docker.com/r/nixos/nix/tags
Usei até musl; ele construiu em CI, de forma rápida e consistente, as menores imagens que já consegui com qualquer ferramenta, e o cache também funcionou bem. Não entendo muito bem o que teria sido executado
buildFHSEnvpara dar suporte a algum binário estranho de terceirosParece que o Nix precisa de mais textos explicando como fazer uma transição suave, por partes, a partir de sistemas existentes
Como engenheiro de plataforma, eu gostaria de gostar do Nix, mas ele não é fácil para todo mundo
Acho que a experiência do desenvolvedor ainda é bem ruim. Por exemplo, prefiro a experiência do devbox, porque dá para adicionar pacotes com algo como
devbox add python@3.11Ao olhar um
flake.nixde 120 linhas, também fica difícil dizer exatamente que é “mais fácil”https://github.com/Xe/douglas-adams-quotes/blob/main/flake.n...
O flake linkado define como compilar um binário Go, define uma imagem Docker que usa esse binário como ponto de entrada e também define um módulo NixOS que cria um serviço systemd para executar o binário Go. Além disso, inclui até um teste NixOS que verifica se esse módulo NixOS cria o serviço systemd conforme esperado
O framework de testes do NixOS é bem impressionante, e os testes rodam dentro de uma VM QEMU executando NixOS junto. O que tem relação com o artigo linkado é apenas a definição do binário Go e da imagem Docker, além de parte do boilerplate ao redor
Além disso, muitas linhas correspondem 1:1 à descrição inline do serviço systemd, então elas não desapareceriam independentemente do sistema usado. Também há ali uma forma elegante de declarar vários sistemas com overrides
Esse exemplo está mais para “vamos fazer uma coisa trivial como faríamos em um projeto grande e sério”, e, se fosse tratado como algo pontual, acho que poderia ser reduzido para cerca de 40 linhas
Esse binário e esse arquivo de configuração são a coisa mais próxima de um “flat pack” que se pode obter no Linux, e este exemplo mostra bem o que deixamos passar entre a floresta e as árvores
O Guix também tem uma opção Docker fácil e boa chamada
guix pack -f dockerO Guix também tem a vantagem de usar uma linguagem já existente, Guile/Scheme, em vez de uma linguagem própria dedicada
[1] https://guix.gnu.org/manual/en/html_node/Invoking-guix-pack....