Partilhar via


Tutorial: Persistir dados numa aplicação de contentor usando volumes no VS Code

Neste tutorial, você aprenderá a persistir dados em um aplicativo de contêiner. Quando executas ou atualizas, os dados ainda estão disponíveis. Existem dois tipos principais de volumes usados para persistir dados. Este tutorial centra-se em volumes nomeados .

Você também aprenderá sobre montagens de ligação, que controlam o ponto de montagem exato no host. Você pode usar montagens de ligação para persistir dados, mas também pode adicionar mais dados em contêineres. Ao trabalhar em um aplicativo, você pode usar uma montagem de ligação para montar o código-fonte no contêiner para permitir que ele veja as alterações de código, responda e permita que você veja as alterações imediatamente.

Este tutorial também apresenta as camadas de imagens, o cache de camadas e as compilações multietapa.

Neste tutorial, você aprenderá a:

  • Compreenda os dados entre contêineres.
  • Armazene dados usando volumes nomeados.
  • Use suportes de ligação.
  • Ver camada de imagem.
  • Dependências de cache.
  • Compreenda as compilações de vários estágios.

Pré-requisitos

Este tutorial continua o tutorial anterior, Criar e compartilhar um aplicativo de contêiner com o Visual Studio Code. Comece por aquele, que inclui pré-requisitos.

Compreender os dados entre contêineres

Nesta seção, você iniciará dois contêineres e criará um arquivo em cada um. Os arquivos criados em um contêiner não estão disponíveis em outro.

  1. Inicie um contêiner de ubuntu usando este comando:

    docker run -d ubuntu bash -c "shuf -i 1-10000 -n 1 -o /data.txt && tail -f /dev/null"
    

    Este comando inicia e invoca dois comandos usando &&. A primeira parte escolhe um único número aleatório e escreve-o para /data.txt. O segundo comando é observar um arquivo para manter o contêiner em execução.

  2. No VS Code, no Container Explorer, clique com o botão direito do mouse no contêiner ubuntu e selecione Anexar Shell.

    A captura de tela mostra a extensão Ferramentas de Contêiner com um contêiner selecionado e um menu de contexto com Anexar Shell selecionado.

    Um terminal é aberto que está executando um shell no contêiner do Ubuntu.

  3. Execute o seguinte comando para ver o conteúdo do arquivo /data.txt.

    cat /data.txt
    

    O terminal mostra um número entre 1 e 10000.

    Para usar a linha de comando para ver esse resultado, obtenha a ID do contêiner usando o comando docker ps e execute o seguinte comando.

    docker exec <container-id> cat /data.txt
    
  4. Inicie outro contêiner ubuntu.

    docker run -d ubuntu bash -c "shuf -i 1-10000 -n 1 -o /data.txt && tail -f /dev/null"
    
  5. Use este comando para examinar o conteúdo da pasta.

    docker run -it ubuntu ls /
    

    Não deve haver nenhum ficheiro data.txt porque este foi escrito no espaço temporário apenas para o primeiro contentor.

  6. Selecione esses dois contêineres do Ubuntu. Clique com o botão direito do mouse e selecione Remover. Na linha de comando, você pode removê-los usando o comando docker rm -f.

Persista os seus dados de tarefas utilizando volumes nomeados

Por padrão, o aplicativo todo armazena seus dados em uma base de dados SQLite no /etc/todos/todo.db. Banco de dados SQLite é um banco de dados relacional que armazena dados em um único arquivo. Esta abordagem funciona para pequenos projetos.

Você pode manter o único arquivo no host. Quando você o disponibiliza para o próximo contêiner, o aplicativo pode continuar de onde parou. Ao criar um volume e anexar, ou montar, à pasta em que os dados estão armazenados, você pode manter os dados. O contêiner grava no arquivo todo.db e esses dados persistem no host no volume.

Para esta seção, use um volume chamado. O Docker mantém o local físico do volume no disco. Consulte o nome do volume e o Docker fornece os dados corretos.

  1. Crie um volume usando o comando docker volume create.

    docker volume create todo-db
    
  2. Em CONTAINERS, selecione de introdução e clique com o botão direito do mouse. Selecione Parar para parar o contêiner do aplicativo.

    Para parar o contêiner da linha de comando, use o comando docker stop.

  3. Inicie o contêiner início usando o comando a seguir.

    docker run -dp 3000:3000 -v todo-db:/etc/todos getting-started
    

    O parâmetro volume especifica o volume a montar e a sua localização, /etc/todos.

  4. Atualize o navegador para recarregar o aplicativo. Se você fechou a janela do navegador, vá para http://localhost:3000/. Adicione alguns itens à sua lista de tarefas.

    Captura de tela mostra o aplicativo de exemplo com vários itens adicionados à lista.

  5. Remova o contêiner introdução para o aplicativo todo. Clique com o botão direito do rato no contentor no Explorador de Contentores e selecione Remover ou use os comandos docker stop e docker rm.

  6. Inicie um novo contêiner usando o mesmo comando:

    docker run -dp 3000:3000 -v todo-db:/etc/todos getting-started
    

    Este comando monta o mesmo disco que antes. Atualize o navegador. Os itens que você adicionou ainda estão na sua lista.

  7. Remova novamente o recipiente de início.

Volumes nomeados e montagens bind, discutidos abaixo, são os principais tipos de volumes suportados por uma instalação padrão do motor Docker.

Propriedade Volumes nomeados Montagens de Ligação
Localização do anfitrião Docker escolhe Você controla
Exemplo de montagem (usando -v) meu-volume:/usr/local/data /caminho/para/dados:/usr/local/data
Preenche o novo volume com o conteúdo do contêiner Sim Não
Suporta drivers de volume Sim Não

Há muitos plugins de driver de volume disponíveis para suportar NFS, SFTP, NetApp e muito mais. Esses plug-ins são especialmente importantes para executar contêineres em vários hosts em um ambiente clusterizado, como Swarm ou Kubernetes.

Se você quer saber onde o Docker realmente armazena seus dados, execute o seguinte comando.

docker volume inspect todo-db

Observe a saída, semelhante a este resultado.

[
    {
        "CreatedAt": "2019-09-26T02:18:36Z",
        "Driver": "local",
        "Labels": {},
        "Mountpoint": "/var/lib/docker/volumes/todo-db/_data",
        "Name": "todo-db",
        "Options": {},
        "Scope": "local"
    }
]

O Mountpoint é o local real onde os dados são armazenados. Na maioria dos computadores, você precisa de acesso root para acessar esse diretório do host.

Usar suportes de ligação

Com bind monta, você controla o ponto de montagem exato no host. Essa abordagem persiste dados, mas geralmente é usada para fornecer mais dados em contêineres. Pode usar um vínculo para montar o código-fonte no contêiner, permitindo que este veja as alterações de código, responda a elas e que você as veja de imediato.

Para executar seu contêiner para dar suporte a um fluxo de trabalho de desenvolvimento, siga as seguintes etapas:

  1. Remova todos os recipientes getting-started.

  2. Na pasta app, execute o seguinte comando.

    docker run -dp 3000:3000 -w /app -v ${PWD}:/app node:lts-alpine sh -c "yarn install && yarn run dev"
    

    Este comando contém os seguintes parâmetros.

    • -dp 3000:3000 O mesmo que antes. Execute no modo desanexado e crie um mapeamento de porta.
    • -w /app Diretório de trabalho dentro do contêiner.
    • -v ${PWD}:/app" Bind monta o diretório atual do host no contêiner no diretório /app.
    • node:lts-alpine A imagem a ser usada. Esta imagem é a imagem base para seu aplicativo do Dockerfile.
    • sh -c "yarn install && yarn run dev" Um comando. Ele inicia um shell usando sh e executa yarn install para instalar todas as dependências. Em seguida, ele corre yarn run dev. Se olhares para o package.json, o script dev está a começar nodemon.
  3. Você pode assistir os logs usando docker logs.

    docker logs -f <container-id>
    
    $ nodemon src/index.js
    [nodemon] 2.0.20
    [nodemon] to restart at any time, enter `rs`
    [nodemon] watching path(s): *.*
    [nodemon] watching extensions: js,mjs,json
    [nodemon] starting `node src/index.js`
    Using sqlite database at /etc/todos/todo.db
    Listening on port 3000
    

    Quando vir a entrada final nesta lista, a aplicação está em execução.

    Quando terminar de assistir aos registros, selecione qualquer tecla na janela do terminal ou selecione Ctrl+C em uma janela externa.

  4. No VS Code, abra src/static/js/app.js. Altere o texto do botão "Adicionar Item" na linha 109.

    - {submitting ? 'Adding...' : 'Add Item'}
    + {submitting ? 'Adding...' : 'Add'}
    

    Guarde as suas alterações.

  5. Atualize o navegador. Você deve ver a mudança.

    Captura de tela mostra o aplicativo de exemplo com o novo texto no botão.

  6. Retire o node:lts-alpine recipiente.

  7. Na pasta app, execute o seguinte comando para remover a pasta node_modules criada nas etapas anteriores.

    rm -r node_modules
    

Ver camadas de imagem

Você pode olhar para as camadas que compõem uma imagem. Execute o comando docker image history para ver o comando que foi usado para criar cada camada dentro de uma imagem.

  1. Use docker image history para ver as camadas na imagem de início que criou anteriormente no tutorial.

    docker image history getting-started
    

    O seu resultado deve ser semelhante a este.

    IMAGE               CREATED             CREATED BY                                      SIZE                COMMENT
    a78a40cbf866        18 seconds ago      /bin/sh -c #(nop)  CMD ["node" "/app/src/ind…   0B                  
    f1d1808565d6        19 seconds ago      /bin/sh -c yarn install --production            85.4MB              
    a2c054d14948        36 seconds ago      /bin/sh -c #(nop) COPY dir:5dc710ad87c789593…   198kB               
    9577ae713121        37 seconds ago      /bin/sh -c #(nop) WORKDIR /app                  0B                  
    b95baba1cfdb        13 days ago         /bin/sh -c #(nop)  CMD ["node"]                 0B                  
    <missing>           13 days ago         /bin/sh -c #(nop)  ENTRYPOINT ["docker-entry…   0B                  
    <missing>           13 days ago         /bin/sh -c #(nop) COPY file:238737301d473041…   116B                
    <missing>           13 days ago         /bin/sh -c apk add --no-cache --virtual .bui…   5.35MB              
    <missing>           13 days ago         /bin/sh -c #(nop)  ENV YARN_VERSION=1.21.1      0B                  
    <missing>           13 days ago         /bin/sh -c addgroup -g 1000 node     && addu…   74.3MB              
    <missing>           13 days ago         /bin/sh -c #(nop)  ENV NODE_VERSION=12.14.1     0B                  
    <missing>           13 days ago         /bin/sh -c #(nop)  CMD ["/bin/sh"]              0B                  
    <missing>           13 days ago         /bin/sh -c #(nop) ADD file:e69d441d729412d24…   5.59MB   
    

    Cada uma das linhas representa uma camada na imagem. A saída mostra a base na parte inferior com a camada mais recente na parte superior. Usando essas informações, você pode ver o tamanho de cada camada, ajudando a diagnosticar imagens grandes.

  2. Várias das linhas estão truncadas. Se você adicionar o parâmetro --no-trunc, obterá a saída completa.

    docker image history --no-trunc getting-started
    

Dependências de cache

Quando uma camada muda, todas as camadas a jusante também têm de ser recriadas. Aqui está novamente o Dockerfile:

FROM node:lts-alpine
WORKDIR /app
COPY . .
RUN yarn install --production
CMD ["node", "/app/src/index.js"]

Cada comando no Dockerfile se torna uma nova camada na imagem. Para minimizar o número de camadas, você pode reestruturar seu Dockerfile para oferecer suporte ao cache de dependências. Para aplicações baseadas em Node.js, essas dependências são definidas no ficheiro package.json.

A abordagem é copiar apenas esse arquivo primeiro, instalar as dependências e, seguida, copiar todo o resto. O processo só recria as dependências do fio se houver uma alteração no package.json.

  1. Atualize o Dockerfile para primeiro copiar no package.json, instalar dependências e só então copiar o resto. Aqui está o novo arquivo:

    FROM node:lts-alpine
    WORKDIR /app
    COPY package.json yarn.lock ./
    RUN yarn install --production
    COPY . .
    CMD ["node", "/app/src/index.js"]
    
  2. Crie uma nova imagem usando docker build.

    docker build -t getting-started .
    

    Você deve ver resultados como os seguintes:

    Sending build context to Docker daemon  219.1kB
    Step 1/6 : FROM node:lts-alpine
    ---> b0dc3a5e5e9e
    Step 2/6 : WORKDIR /app
    ---> Using cache
    ---> 9577ae713121
    Step 3/6 : COPY package* yarn.lock ./
    ---> bd5306f49fc8
    Step 4/6 : RUN yarn install --production
    ---> Running in d53a06c9e4c2
    yarn install v1.17.3
    [1/4] Resolving packages...
    [2/4] Fetching packages...
    info fsevents@1.2.9: The platform "linux" is incompatible with this module.
    info "fsevents@1.2.9" is an optional dependency and failed compatibility check. Excluding it from installation.
    [3/4] Linking dependencies...
    [4/4] Building fresh packages...
    Done in 10.89s.
    Removing intermediate container d53a06c9e4c2
    ---> 4e68fbc2d704
    Step 5/6 : COPY . .
    ---> a239a11f68d8
    Step 6/6 : CMD ["node", "/app/src/index.js"]
    ---> Running in 49999f68df8f
    Removing intermediate container 49999f68df8f
    ---> e709c03bc597
    Successfully built e709c03bc597
    Successfully tagged getting-started:latest
    

    Todas as camadas foram reconstruídas. Esse resultado é esperado porque você alterou o Dockerfile.

  3. Faça uma alteração no src/static/index.html. Por exemplo, altere o título para dizer "The Awesome Todo App".

  4. Crie a imagem do Docker agora usando docker build novamente. Desta vez, sua saída deve parecer um pouco diferente.

    Sending build context to Docker daemon  219.1kB
    Step 1/6 : FROM node:lts-alpine
    ---> b0dc3a5e5e9e
    Step 2/6 : WORKDIR /app
    ---> Using cache
    ---> 9577ae713121
    Step 3/6 : COPY package* yarn.lock ./
    ---> Using cache
    ---> bd5306f49fc8
    Step 4/6 : RUN yarn install --production
    ---> Using cache
    ---> 4e68fbc2d704
    Step 5/6 : COPY . .
    ---> cccde25a3d9a
    Step 6/6 : CMD ["node", "/app/src/index.js"]
    ---> Running in 2be75662c150
    Removing intermediate container 2be75662c150
    ---> 458e5c6f080c
    Successfully built 458e5c6f080c
    Successfully tagged getting-started:latest
    

    Como você está usando o cache de compilação, ele deve ser muito mais rápido.

Construções em vários estágios

Compilações de múltiplos estágios são uma ferramenta incrivelmente poderosa para ajudar na criação de uma imagem por meio de várias fases. Existem várias vantagens para eles:

  • Separe as dependências de tempo de compilação das dependências de tempo de execução
  • Reduza o tamanho geral da imagem enviando apenas o que seu aplicativo precisa para ser executado

Esta secção fornece breves exemplos.

Exemplo de Maven/Tomcat

Quando você cria aplicativos baseados em Java, um JDK é necessário para compilar o código-fonte em bytecode Java. Esse JDK não é necessário na produção. Você pode estar usando ferramentas como Maven ou Gradle para ajudar a criar o aplicativo. Essas ferramentas também não são necessárias na sua imagem final.

FROM maven AS build
WORKDIR /app
COPY . .
RUN mvn package

FROM tomcat
COPY --from=build /app/target/file.war /usr/local/tomcat/webapps 

Este exemplo usa um estágio, build, para executar a compilação Java real usando o Maven. A segunda etapa, a partir de "FROM tomcat", copia ficheiros da etapa build. A imagem final é apenas o último estágio que está sendo criado, que pode ser substituído usando o parâmetro --target.

Exemplo de React

Ao criar aplicativos React, você precisa de um ambiente Node para compilar o código JavaScript, folhas de estilo Sass e muito mais em HTML, JavaScript e CSS estáticos. Se você não estiver fazendo renderização do lado do servidor, nem precisará de um ambiente Node para a compilação de produção.

FROM node:lts-alpine AS build
WORKDIR /app
COPY package* yarn.lock ./
RUN yarn install
COPY public ./public
COPY src ./src
RUN yarn run build

FROM nginx:alpine
COPY --from=build /app/build /usr/share/nginx/html

Este exemplo usa uma imagem node:lts-alpine para executar a compilação, o que maximiza o cache de camada, e depois copia a saída para um contentor nginx .

Limpar recursos

Guarde tudo o que fez até agora para continuar esta série de tutoriais.

Próximos passos

Você aprendeu sobre as opções para persistir dados para aplicativos de contêiner.

O que pretende fazer a seguir?