Eu sou novo docker
e estou tentando entendê-lo um pouco melhor trabalhando com exemplos do zero.
Eu queria começar da forma mais primitiva possível: copiar um arquivo do meu contexto para a raiz da minha imagem do docker e, em seguida, validar a existência do arquivo no contêiner.
$ lsb_release -a
No LSB modules are available.
Distributor ID: Debian
Description: Debian GNU/Linux 9.8 (stretch)
Release: 9.8
Codename: stretch
$ ls -R
.:
demo.txt Dockerfile
$
$ cat Dockerfile
FROM scratch
WORKDIR .
COPY demo.txt /foo
$
$ docker build -t demo:v1 -f ./Dockerfile .
Sending build context to Docker daemon 28.67kB
Step 1/3 : FROM scratch
--->
Step 2/3 : WORKDIR .
---> Using cache
---> 8eb9da711a99
Step 3/3 : COPY demo.txt /foo
---> c57e0e9a316b
Successfully built c57e0e9a316b
Successfully tagged demo:v1
$
$ docker run -it demo:v1 ls -l /foo
docker: Error response from daemon: OCI runtime create failed: container_linux.go:345: starting container process caused "exec: \"ls\": executable file not found in $PATH": unknown.
Eu entendo a essência do problema: FROM scratch
é apenas isso - sem mesmo ls
. Então, isso pode ser corrigido mudando FROM scratch
para algo mais substancial:
$ cat Dockerfile
FROM alpine:3.7
WORKDIR .
COPY demo.txt /foo
$
$ docker build -t demo:v1 -f ./Dockerfile .
Sending build context to Docker daemon 28.67kB
Step 1/3 : FROM alpine:3.7
---> 6d1ef012b567
Step 2/3 : WORKDIR .
---> Using cache
---> 51c22fe2fd60
Step 3/3 : COPY demo.txt /foo
---> Using cache
---> e2c241241653
Successfully built e2c241241653
Successfully tagged demo:v1
$
$ docker run -it demo:v1 ls -l /foo
-rw-r--r-- 1 root root 6 Sep 8 23:42 /foo
Isso me leva à minha pergunta: estou confuso com esse conceito de instalar/executar uma distribuição Linux diferente (alpine) daquela que está no meu host (debian). O que acontece quando executo meu contêiner docker criado a partir de uma imagem incluída FROM alpine:3.7
em seu Dockerfile
? Uma máquina virtual que executa o Alpine Linux foi iniciada no meu PC físico que executa o Debian?
Se eu quisesse fazer algo muito primitivo - como ls
- no meu contêiner docker, como escolher a imagem de base mínima? Eu escolhi alpine aleatoriamente - sem saber exatamente o que estava fazendo - quando alguém precisaria escolher uma distro diferente, como ubuntu, etc.?
As imagens do Docker são como sistemas de arquivos. Uma imagem docker seria um disco que continha uma instalação do Alpine, Ubuntu, Arch Linux... mesmo que seu host seja Debian. (você também pode ter imagens para o Debian, ou versões diferentes delas, mas é mais fácil explicar se forem completamente diferentes)
O truque aqui é que você está executando o mesmo kernel em todos eles. Docker é uma solução de contêiner . Sua máquina baseada em alpine será executada em um kernel Linux distribuído pelo Debian (restringido pelo docker para não ver o mundo exterior). O Alpine funcionará bem com qualquer kernel do Linux novo o suficiente para executar o docker, então simplesmente funciona como se você tivesse uma instalação do Alpine (ou Ubuntu, ou Arch Linux ...).
A desvantagem é que você não pode rodar o BSD ou o Windows lá, já que sua userland não seria capaz de rodar com um kernel Linux. Além disso, obviamente, eles precisam usar uma arquitetura compatível.
Você inicia um novo contêiner que inicializa uma imagem com base em uma instalação alpina, que será muito semelhante à inicialização de uma instalação alpina.
Observe que você pode criar uma instância do docker que faça outra coisa, como iniciar um console bash para você trabalhar, e não iniciar os daemons que a distribuição normalmente executaria na inicialização.
Se você quiser executar
ls
, e apenas isso, poderá criar uma imagem que contenha apenasls
(já que exigiria libc, provavelmente melhor como uma cópia debusybox-static
). isso pode ser um exagero, no entanto.Alpine é usado com bastante frequência para isso, pois é uma distribuição muito pequena.
Se você precisava de algo fornecido por essa distro. Suponha que você queira executar um programa de console cujas dependências foram declaradas como "precisando do pacote X, Y e Z do Ubuntu MN". Portanto, faria sentido executá-lo em uma instância do docker com base em uma imagem "Ubuntu MN".
Como você está trabalhando em um host Debian, você pode se sentir mais confortável trabalhando com imagens Debian, e faria sentido basear suas imagens docker em uma imagem Debian.
Isso equivale à pergunta "Preciso instalar um novo servidor GNU/Linux, qual distribuição devo usar?", que no final das contas será uma escolha pessoal da pessoa (ou equipe) entre várias boas opções.
Eu acho que sua pergunta é realmente “Por que eu preciso disso?” e a resposta simples é “Porque tem que ser independente”. Lembre-se de que, quando um contêiner está em execução, o que quer que esteja nele não pode acessar o sistema de arquivos do host.
Isso é um problema porque muitos programas requerem bibliotecas compartilhadas. Vamos dar uma olhada em
echo
:Assim, mesmo um programa tão simples quanto
echo
não é independente. Depende da libc. Se eu criasse umaFROM scratch
imagem do Docker apenas com oecho
binário, ela não poderia ser executada. Claro que você poderia (re)compilar algum software para ser vinculado estaticamente, mas isso requer tempo.Às vezes, você também depende de outras coisas, como certificados raiz, e deixar uma distribuição estabelecida cuidar disso é mais fácil.
Quando você inicia um contêiner, ele não “executa o sistema operacional do contêiner”. Em vez disso, na maioria dos contêineres mais simples, apenas um único processo é iniciado (conforme definido na linha de comando ou em
Dockerfile
). Os contêineres mais complexos tendem a usar seus próprios sistemas “init”, porque os sistemas init clássicos do sistema operacional geralmente não são adequados para uso em contêineres.Escolher uma imagem de base adequada não é algo que possa ser formalizado. Você precisa pensar nos requisitos e pesquisar o que está disponível. Não há realmente nenhuma maneira de contornar isso. Você também pode ter preferências pessoais. Para este caso particular, talvez as imagens do busybox sejam adequadas e bastante mínimas.
Com um tipo diferente de software de contêiner (LXC/LXD), você pode, de fato, executar uma segunda distribuição Linux inteira, com sistema init regular e outros enfeites, em um contêiner. Isso é preferível à virtualização total se não for necessário tanto isolamento. Ele oferece desempenho muito superior, muito parecido com o Docker.
O isolamento usando namespaces do Linux é suficiente para permitir que muitas distribuições compartilhem o mesmo kernel em execução. Mas é válido pensar nisso como uma máquina virtual leve.