Notas sobre tecnologia de contêiner Docker

imagem-20220629215534772

Tecnologia de contêiner Docker

Docker é uma tecnologia de nível de plataforma que cobre uma ampla gama, portanto, antes de começar, certifique-se de ter concluído: capítulo Java SpringBoot (recomenda-se concluir o capítulo SpringCloud antes de começar) tutorial em vídeo e todas as rotas anteriores, caso contrário, o aprendizado será ser muito difícil.Além disso, conhecimentos adicionais recomendados: conhecimentos relacionados a "Rede de Computadores" e "Sistema Operacional". Aprender algo não pode ser feito inteiramente de memória, mas precisa ser compreendido em combinação com o conhecimento básico que você aprendeu. De um modo geral, as coisas que podem ser dominadas apenas pela memória são muitas vezes as mais baratas.

**Site oficial do Docker: **https://www.docker.com

** Preparação antes da aula: ** Configure um servidor Linux de 2C2G ou superior, seja servidor em nuvem ou máquina virtual.

Introdução à tecnologia de contêineres

Com o passar dos tempos, o Docker foi gradativamente entrando no palco da história.Antigamente, se quiséssemos instalar um ambiente, demoraríamos uma tarde ou até um dia inteiro para configurar e instalar diversas peças (como rodar nosso próprio Aplicativo SpringBoot, que pode exigir instalação de bancos de dados, instalação de Redis, instalação de MQ, etc. Leva muito tempo apenas para instalar vários ambientes, o que realmente faz a mentalidade explodir).Com o Docker, nossa implantação de programas e ambientes se tornou muito fácil. , só precisamos empacotar esses ambientes em uma imagem. Ao implantar no servidor, você pode baixar a imagem diretamente para implantação com um clique, não é muito conveniente?

Incluindo os vários componentes que precisamos configurar ao aprender Spring Cloud, podemos encontrar vários problemas ao executar no ambiente do nosso próprio computador (pode não ser possível executar porque os vários ambientes do computador não estão configurados), mas agora só precisamos fazer o download A imagem pode ser executada diretamente. Todos os ambientes estão configurados na imagem e podem ser usados ​​imediatamente.

É realmente tão mágico? Vamos tentar.

Instalação e implantação do ambiente

Em primeiro lugar, devemos configurar primeiro o ambiente Docker (recomenda-se usar o mesmo ambiente que eu, caso contrário você terá que encontrar uma solução sozinho se algo der errado).Aqui usamos:

  • Sistema operacional Ubuntu 22.04

O Docker é dividido em versão comunitária gratuita CE (Community Edition) e versão paga de nível empresarial EE (Enterprise Edition), então escolhemos docker-ce para instalação aqui. Documentação oficial de instalação: https://docs.docker.com/engine/install/ubuntu/

Primeiro instale algumas ferramentas:

sudo apt-get install ca-certificates curl gnupg lsb-release

No entanto, ele foi instalado por padrão no Ubuntu22.04. Em seguida, instale a chave GPG oficial:

sudo mkdir -p /etc/apt/keyrings

curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg

Finalmente, adicione a biblioteca Docker à lista de recursos do apt:

echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

Então atualizamos o apt:

 sudo apt update

Por fim, instale a versão Docker CE:

 sudo apt install docker-ce

Basta aguardar a conclusão da instalação:

imagem-20220630161240162

imagem-20220630161341541

Você pode ver que a versão após a instalação bem-sucedida é 20.10.17. Claro, pode ser uma versão mais recente quando você a instalou. Finalmente, adicionamos o usuário atual ao grupo de usuários do docker, caso contrário, a execução do sudo será necessária sempre que o comando docker for usado, o que é muito problemático:

sudo usermod -aG docker <用户名>

Depois que a configuração for concluída, primeiro saímos do terminal SSH e depois nos reconectamos para entrar em vigor.

Desta forma, nosso ambiente de aprendizagem Docker está configurado. Agora tentaremos implantar um servidor Nginx através do Docker. É muito simples de usar e requer apenas um comando (claro, não importa se você não entende agora, iremos elaborar sobre isso mais tarde):

sudo docker run -d -p 80:80 nginx

imagem-20220630165259663

A primeira opção é baixar a imagem correspondente do armazém de imagens, a velocidade de acesso doméstico é boa e não há necessidade de configurar a fonte da imagem separadamente. Após a conclusão do download, ele será executado em segundo plano. Podemos testá-lo usando um navegador:

imagem-20220630165430159

imagem-20220630165440751

Como você pode ver, o servidor Nginx foi implantado com sucesso, mas na verdade não instalamos o Nginx no Ubuntu, mas construímos o servidor por meio da imagem executando o Docker. A jogabilidade não parece bastante nova? Além de aplicativos simples como o Nginx, também podemos implantar aplicativos complexos por meio do Docker, que explicaremos um por um posteriormente.

De máquinas virtuais a contêineres

Anteriormente, instalamos com sucesso o ambiente de aprendizagem Docker e tivemos uma breve amostra da rápida implantação de aplicativos que o Docker nos trouxe. Antes de entrar oficialmente no estudo, vamos começar com o desenvolvimento do Docker.

Antes do surgimento do Docker, poderia-se dizer que a tecnologia de virtualização dominava. Primeiro, vamos falar sobre por que a tecnologia de virtualização aparece. Sabemos que os servidores são um recurso de hardware indispensável nas empresas. Os servidores também são computadores, mas, diferentemente dos nossos computadores domésticos, as configurações do servidor são muito altas. , a CPU do nosso computador doméstico pode ter apenas 20 núcleos no máximo, e há poucos computadores com mais de 128 G de memória.Um computador doméstico com 64 G de memória pode ser considerado um luxo. Os servidores são diferentes. As CPUs no nível do servidor geralmente têm 12 núcleos. Os servidores podem até ser equipados com várias CPUs ao mesmo tempo, que podem empilhar diretamente até dezenas de núcleos:

imagem-20220630171220207

Nossas CPUs domésticas são geralmente as séries Ryzen da AMD e as séries Core da Intel (como i3 i5 i7 i9), enquanto as CPUs de servidor são geralmente as séries Xeno da Intel. A característica desse tipo de CPU é que ela possui um número muito grande de núcleos:

imagem-20220630172135408

Além disso, as CPUs dos servidores consomem mais energia do que as CPUs domésticas, de modo que as CPUs dos servidores geram calor muito alto. Se você tiver a sorte de estar na sala do computador, ouvirá o som da ventoinha girando violentamente (mas a frequência das CPUs dos servidores não é tão alto quanto o de CPUs domésticas). Alto. Geralmente, jogos em grande escala exigem alta frequência em vez de número de núcleo, e o consumo de energia é relativamente grande, por isso não é adequado para computadores domésticos. Portanto, ao comprar um computador desktop on-line no futuro, se você vir qualquer CPU de "nível i9", não compre., são esses comerciantes inescrupulosos que instalam CPUs de servidor (lixo estrangeiro) eliminadas de servidores estrangeiros em computadores e as vendem para você , então será muito barato e o número de núcleos é comparável ao i9, então você ainda receberá o que pagou)

Os servidores têm muito mais recursos de CPU e memória do que os computadores domésticos, e os projetos de back-end Java que escrevemos acabarão sendo executados nesses servidores. No entanto, há um problema: como os servidores têm recursos de hardware tão abundantes, executaremos este. O pequeno back-end Java parece uma bomba nuclear explodindo mosquitos? Ele pode usar no máximo 5% dos recursos de hardware do servidor. Seria um desperdício executar um servidor tão poderoso.

Portanto, para resolver esta situação em que a taxa de utilização de recursos é de apenas 5% -15%, podemos pensar em uma maneira de dividir este servidor em vários servidores pequenos para uso, e cada servidor pequeno aloca apenas uma parte dos recursos, como dividir um O pequeno servidor possui apenas 2 núcleos de CPU e 4G de memória. Mas devido a problemas de design, nossos computadores só podem executar um sistema operacional ao mesmo tempo, então o que devemos fazer? Neste momento a tecnologia de virtualização começou a crescer.

A virtualização usa software para emular hardware e criar sistemas de computador virtuais. Isso permite que as empresas executem vários sistemas virtuais — ou seja, vários sistemas operacionais e aplicativos — em um único servidor, o que pode levar a economias de escala e maior eficiência. Por exemplo, o VMware, frequentemente usado em nossos computadores, é um software de virtualização de nível civil:

imagem-20220630173915254

Podemos usar o VMware para criar máquinas virtuais. Na verdade, essas máquinas virtuais são executadas com base no software VMware em nosso sistema atual. É claro que o VMware também possui software de virtualização específico do servidor. Com a virtualização, nossos servidores ficam assim:

imagem-20220630174945749

É equivalente a simular muitos computadores através de máquinas virtuais, para que possamos instalar o sistema e implantar nossos aplicativos em múltiplas máquinas virtuais divididas respectivamente, e possamos alocar livremente recursos de hardware e utilizá-los de forma racional. Geralmente nas empresas, diferentes aplicações podem ser implantadas em vários servidores e isoladas, neste caso o uso de máquinas virtuais é muito adequado.

Na verdade, os servidores em nuvem que alugamos da Tencent Cloud e Alibaba Cloud são apenas máquinas virtuais divididas pela tecnologia de virtualização.

Então, agora que as máquinas virtuais são tão convenientes, como os contêineres encontram uma saída? Vamos primeiro dar uma olhada no que é um contêiner.

Contêineres e máquinas virtuais são semelhantes. Ambos podem fornecer encapsulamento e isolamento para aplicativos. Ambos são software. No entanto, o aplicativo executado no contêiner depende do sistema operacional host. Na verdade, ele ainda usa diretamente os recursos do sistema operacional. Claro. Claro, o aplicativo O ambiente físico ainda está isolado, enquanto a máquina virtual simula completamente um computador real, que são diretamente dois computadores diferentes.

imagem-20220630181037698

Portanto, os contêineres são muito mais simples que as máquinas virtuais, e a velocidade de inicialização será muito mais rápida e a sobrecarga será muito menor.

No entanto, a causa raiz do incêndio do contêiner é a ideia do contêiner. Sabemos que se quisermos escrever um projeto Java, como um fórum ou comércio eletrônico, então middleware como banco de dados, fila de mensagens e cache são essenciais, então se Se você deseja implantar um serviço no servidor, na verdade precisa preparar vários ambientes com antecedência. Primeiro instale MySQL, Redis, RabbitMQ e outros aplicativos, configure o ambiente e, em seguida, inicie nosso aplicativo Java. Após todo o processo, muitos tempo é desperdiçado apenas configurando o ambiente. Se for um projeto distribuído em grande escala, muitas máquinas podem precisar ser implantadas, então não temos que fazer isso uma por uma? Demora vários dias para colocar o projeto online, o que é obviamente ridículo.

Os contêineres podem empacotar todo o ambiente. MySQL, Redis, etc. e nossos aplicativos Java podem ser empacotados juntos como um espelho. Quando precisamos implantar serviços, só precisamos baixar diretamente o espelho e executá-lo como fizemos antes. Não há necessidade Nenhuma configuração adicional é necessária. O ambiente em toda a imagem já está configurado e pronto para uso.

[Falha na transferência da imagem do link externo. O site de origem pode ter um mecanismo anti-leeching. Recomenda-se salvar a imagem e carregá-la diretamente (img-4urSvvtK-1657097647526)(https://s2.loli.net/2022/06 /30/NTnU8iSj51CspFw.png )]

Queremos nos concentrar no Docker. Você pode ver que seu ícone é uma baleia. Acima da baleia há muitos contêineres. Cada contêiner é todo o nosso ambiente + aplicativo. O Docker pode empacotar qualquer aplicativo e suas dependências. Um aplicativo leve, portátil e independente. -Container contido que pode ser executado em praticamente qualquer sistema operacional.

Uma breve introdução ao mecanismo de funcionamento dos contêineres

Vamos primeiro dar uma olhada na arquitetura geral do Docker:

imagem-20220630184857540

Na verdade, está dividido em três partes:

  • Cliente Docker: Os comandos docker que usamos antes são todos executados no cliente e as operações serão enviadas ao servidor para processamento.
  • Servidor Docker: O servidor é o corpo principal que inicia o contêiner, geralmente é executado em segundo plano como um serviço e oferece suporte a conexões remotas.
  • Registro: É um armazém que armazena imagens Docker. Assim como o Maven, também pode ser dividido em armazéns públicos e privados. A imagem pode ser baixada do armazém e armazenada localmente.

Quando precisamos implantar um aplicativo e ambiente empacotado no servidor, precisamos apenas baixar a imagem empacotada. Nós a executamos anteriormente:

sudo docker run -d -p 80:80 nginx

Na verdade, depois de inserir este comando:

  1. O cliente Docker envia a operação ao servidor, informando ao servidor que queremos executar a imagem nginx.
  2. O servidor Docker primeiro verifica se existe essa imagem localmente e descobre que não existe.
  3. Então você só pode encontrar e baixar a imagem do repositório público Docker Hub.
  4. O download é concluído e a imagem é salva localmente com sucesso.
  5. O servidor Docker carrega a imagem Nginx, inicia o contêiner e começa a funcionar normalmente (observe que o contêiner é isolado de outros contêineres e de fora e não afeta um ao outro)

Portanto, em todo o processo, o Docker é como um navio de transporte, e o espelho é como um contêiner. O navio de transporte transporta mercadorias de todo o mundo para nossos portos. Depois que as mercadorias chegam ao porto, o Docker não se importa com o que é dentro do contêiner. Basta criar o contêiner e ele funcionará imediatamente. Comparado com nosso ambiente tradicional de instalação e configuração manual, não sei quantos níveis de conveniência ele possui.

No entanto, os contêineres ainda dependem da execução do host host, portanto, geralmente em um ambiente de produção, vários hosts são primeiro criados por meio de virtualização e, em seguida, o Docker é implantado em cada máquina virtual. Nesse caso, a eficiência de operação e manutenção é bastante melhorada .

A partir do próximo capítulo, aprenderemos formalmente as diversas operações do Docker.


Contêineres e imagens

O mais importante para iniciar um container é a imagem, vamos dar uma olhada na introdução da imagem.

Primeira introdução às imagens de contêiner

Primeiro, vamos aprender sobre as operações relacionadas à imagem. Por exemplo, agora queremos baixar uma imagem do warehouse para o local. Aqui usamos a imagem oficial do hello-world:

docker pull hello-world

Basta digitar pullo comando para baixar diretamente na imagem especificada:

imagem-20220701111043417

Você pode ver que há uma frase "Usando tag padrão" na linha acima. Na verdade, o nome de uma imagem é composto de duas partes, uma é e a outra é . Em circunstâncias normais, a convenção é o repositorynome tagda repositoryimagem . tagComo versão, o padrão é mais recente, o que significa a versão mais recente. Então, se você executar a versão especificada:

docker pull 名称:版本

Posteriormente, para comodidade de ensino, utilizaremos diretamente a tag padrão sem especificar a versão.

Após o download da imagem, ela será armazenada localmente. Para iniciar o container desta imagem, basta digitar runo comando como fizemos antes:

docker run hello-world

Claro, se você quiser apenas criá-lo, mas não quiser executá-lo imediatamente, você pode usar createo comando:

docker create hello-world

Você pode ver que tudo começou com sucesso:

imagem-20220701111314331

Após a inicialização, um contêiner será criado automaticamente usando a imagem atual. Podemos inserir pso comando para visualizar a lista de contêineres do contêiner atual:

docker ps -a

Observe que você precisa adicionar um no final -apara visualizar todos os contêineres (outras opções podem ser visualizadas usando -h). Caso contrário, apenas os contêineres em execução no momento serão exibidos e HelloWorld é de uso único e não residente programa como o Nginx, então o contêiner inicia. Depois de imprimir o conteúdo acima, o contêiner parou de funcionar:

imagem-20220701111840091

Você pode ver que a lista de contêineres inclui o hello-world que acabamos de criar e o nginx que criamos antes (observe que a mesma imagem pode criar vários contêineres).Cada contêiner possui um ID de contêiner gerado aleatoriamente escrito na frente, seguido por A criação hora e status atual de execução do contêiner. A última coluna é o nome do contêiner. Ao criar o contêiner, o nome pode ser especificado por nós ou gerado automaticamente. Aqui ele é gerado automaticamente.

Podemos especificar manualmente o nome para iniciar. Ao usar runo comando, --namebasta adicionar parâmetros:

docker run --name=lbwnb hello-world

imagem-20220701125951980

Podemos iniciar manualmente um contêiner parado:

 docker start <容器名称/容器ID>

Observe que precisamos preencher o ID ou nome do contêiner para que o objeto seja iniciado. O ID do contêiner é relativamente longo, então você não precisa escrever tudo, mas apenas metade, mas você deve garantir que o ID do contêiner incompleto inserido é exclusivo.

imagem-20220701124845982

Se quiser parar o container, basta digitar stopo comando:

 docker stop <容器名称/容器ID>

Ou reinicie:

 docker restart <容器名称/容器ID>

imagem-20220701125025173

Se não precisarmos mais usar o contêiner, podemos excluí-lo, mas observe que ele só pode ser excluído quando o contêiner não estiver em execução:

docker rm <容器名称/容器ID>

--rmClaro, se quisermos que o contêiner seja excluído automaticamente após a parada, podemos adicionar parâmetros em tempo de execução :

docker run --rm 镜像名称

imagem-20220701125108834

Após a exclusão, o contêiner não existirá mais. Quando não houver contêiner para o nginx, podemos excluir a imagem local do nginx:

imagem-20220701125204728

Podemos usar imageso comando para verificar quais espelhos estão disponíveis localmente:

docker images

imagem-20220701125514145

Até agora, entendemos o uso simples do Docker. No estudo subsequente, continuaremos a aprender mais maneiras de jogar.

Introdução à estrutura do espelho

Anteriormente, aprendemos sobre as operações básicas do Docker. Na verdade, a base do contêiner é a imagem. Somente com a imagem podemos criar a instância do contêiner correspondente. Então, vamos começar com a estrutura básica da imagem. Vamos dar uma olhada em o que é a imagem, a presença.

Quando empacotamos um projeto, muitas vezes precisamos de um ambiente de sistema operacional básico para que possamos instalar vários softwares dependentes deste sistema operacional, como bancos de dados, caches, etc. Uma imagem básica do sistema como essa é chamada de espelhamento de base, nossos projetos serão será empacotado com base na imagem base no futuro. Claro, não há necessidade de uma imagem base. Ela é apenas baseada no sistema operacional atual para executar comandos simples, como o hello-world que usamos antes.

Geralmente, a imagem base é a versão de distribuição de cada sistema operacional Linux, como Ubuntu que estamos usando, CentOS, Kali, etc. Aqui baixamos a imagem base do CentOS:

docker pull centos

imagem-20220701132622893

Como você pode ver, a imagem base do CentOS foi baixada. Ao contrário de quando usamos um sistema completo, a imagem base do CentOS omite o kernel, então o tamanho é de apenas 272M. Aqui precisamos explicar o mecanismo da imagem base:

imagem-20220701133111829

O sistema operacional Linux consiste em espaço do kernel e espaço do usuário. O espaço do kernel é o núcleo de todo o sistema Linux. Depois que o Linux for iniciado, ele primeiro adicionará o sistema de arquivos. Após a conclusão do carregamento, ele será desinstalado automaticamente. Em seguida, o bootfsO sistema de arquivos do espaço do usuário será carregado.Esta camada Esta é a parte que podemos operar:

  • bootfs contém BootLoader e o kernel Linux. Os usuários não podem fazer nenhuma modificação nesta camada. Depois que o kernel for iniciado, o bootfs será desinstalado automaticamente.
  • rootfs contém as estruturas de diretórios comuns no sistema, incluindo /dev, /proc, /binetc., bem como alguns arquivos e comandos básicos, que é todo o sistema de arquivos que podemos operar após entrar no sistema, incluindo apt que usamos no Ubuntu e CentOS. yum é tudo no espaço do usuário.

A camada inferior da imagem base usará diretamente o kernel do host, ou seja, qualquer que seja a versão do kernel do Ubuntu, então a versão do kernel CentOS na imagem base será, e o rootfs pode executar várias versões diferentes em diferentes containers. Portanto, a imagem base na verdade só tem o rootfs do CentOS, então tem apenas cerca de 300 M de tamanho.É claro que o CentOS contém uma variedade de software básico, que é bastante inchado, e a imagem base de alguns sistemas operacionais é ainda menor que 10 milhões.

Use unameo comando para visualizar a versão atual do kernel:

imagem-20220701135056123

Portanto, não é surpreendente que o Docker possa simular vários ambientes de sistema operacional Linux ao mesmo tempo. Podemos tentar iniciar a imagem base que acabamos de baixar:

docker run -it centos

Observe que você precisa adicionar -itparâmetros para inicialização, o que -isignifica abrir uma interface de entrada padrão no contêiner, -tindicando a alocação de um dispositivo pseudo-tty, que pode suportar login no terminal. Geralmente, esses dois são usados ​​juntos, caso contrário, o contêiner base irá parar automaticamente depois de começar.

imagem-20220701135834325

Você pode ver que pode usar o comando ls para visualizar todos os arquivos no diretório raiz, mas muitos comandos não estão disponíveis, nem mesmo claros. Vamos dar uma olhada na versão do kernel:

imagem-20220701140018095

Você pode ver que a versão do kernel é a mesma (isso também é uma desvantagem. Se o software tiver requisitos para a versão do kernel, use o Docker para enviá-lo diretamente). Podemos sair do terminal de contêiner digitando. Você pode ver que o contêiner exittambém parou:

[Falha na transferência da imagem do link externo. O site de origem pode ter um mecanismo anti-leeching. Recomenda-se salvar a imagem e carregá-la diretamente (img-T9dw38Wp-1657097647529)(https://s2.loli.net/2022/07 /01/u5MQnWVihlbkyx1.png )]

Claro, também podemos iniciá-lo novamente. Observe que ele deve ser adicionado ao iniciar a -ientrada no contêiner para interação, caso contrário ele será executado em segundo plano:

imagem-20220701140706977

Com base na imagem base, podemos instalar vários softwares nesta base. Quase todas as imagens são construídas instalando e configurando o software necessário com base na imagem base:

imagem-20220701143105247

Cada vez que um software é instalado, ele é colocado em camadas sobre a imagem base, usando uma estrutura em camadas para que vários contêineres possam montar livremente essas diferentes camadas. Por exemplo, vários contêineres agora precisam usar o CentOS. A imagem base e o software rodando nele é diferente, a estrutura hierárquica é muito legal neste momento. Só precisamos salvar uma imagem base localmente, e ela pode ser montada e usada por vários contêineres diferentes. Não parece muito flexível?

Vemos que além desses softwares, há também uma camada de contêiner gravável no topo. Para que serve isso? Por que está colocado no topo?

Sabemos que todos os espelhos serão empilhados para formar um sistema de arquivos unificado. Se houver arquivos no mesmo local em camadas diferentes, os arquivos da camada superior substituirão os arquivos da camada inferior. No final, o que fazemos see é um sistema de arquivos sobreposto. Quando precisamos modificar os arquivos no contêiner, na verdade não modificamos a imagem diretamente, mas a modificamos na camada superior do contêiner (a camada superior geralmente é chamada de camada do contêiner e as seguintes são todas as camadas espelhadas). Isso afeta o imagem a seguir, caso contrário será difícil compartilhar a imagem com vários contêineres. Portanto, cada operação é a seguinte:

  • Leitura de arquivo: para ler um arquivo, o Docker pesquisará de cima para baixo e abrirá o arquivo quando for encontrado.
  • Criação e modificação de arquivos: A criação de novos arquivos será adicionada diretamente à camada do contêiner. A modificação dos arquivos pesquisará os arquivos em cada imagem de cima para baixo. Se encontrados, eles serão copiados para a camada do contêiner e depois modificados.
  • Excluindo arquivos: A exclusão de arquivos também pesquisará os arquivos em cada imagem de cima para baixo. Uma vez encontrados, os arquivos da imagem não serão excluídos diretamente, mas a operação de exclusão será marcada na camada do contêiner.

Em outras palavras, a maioria das operações que realizamos nos arquivos de todo o contêiner são realizadas na camada superior do contêiner. Não podemos interferir em todos os arquivos da camada de imagem abaixo, o que protege bem a integridade da imagem. Somente fazendo isso podemos vários contêineres sejam compartilhados e usados.

Construir imagem

Já entendemos a estrutura das imagens Docker. Na verdade, todos os aplicativos comumente usados ​​​​têm imagens correspondentes. Só precisamos baixar essas imagens e usá-las. Não precisamos instalá-las manualmente. No máximo, precisamos realizar algumas operações especiais.configuração. Claro, se você encontrar alguns aplicativos impopulares, a imagem pode não ser fornecida. Nesse caso, precisamos instalá-la manualmente. Em seguida, veremos como construir nossa própria imagem Docker. Existem duas maneiras de construir uma imagem, uma é usar commitcomandos e a outra é usar Dockerfile. Vejamos primeiro a primeira.

Aqui vamos dar um exemplo simples, por exemplo, agora queremos instalar o ambiente Java na imagem base do Ubuntu e empacotá-lo como uma nova imagem (esta nova imagem é uma imagem do sistema Ubuntu contendo o ambiente Java)

Primeiro iniciamos a imagem do Ubuntu e depois usamos yumo comando (semelhante ao apt) para instalar o ambiente Java. O primeiro é runo comando:

docker pull ubuntu

imagem-20220701151405640

Então comece:

imagem-20220701151433520

Use diretamente o comando apt para instalar o ambiente Java. Atualize-o antes de fazer isso. Por ser uma instalação mínima, não há pacotes de software locais:

imagem-20220701151600847

Então digite:

apt install openjdk-8-jdk

Aguarde a conclusão da instalação:

imagem-20220701152018041

Desta forma, concluímos a instalação do ambiente Java, e então podemos sair desta imagem e construí-la como uma nova imagem:

imagem-20220701152130041

Use commito comando para salvar o contêiner como uma nova imagem:

docker commit 容器名称/ID 新的镜像名称

imagem-20220701152302171

imagem-20220701152418060

Você pode ver que o tamanho da imagem após a instalação do software é muito maior que o tamanho original, então podemos iniciar diretamente um contêiner do sistema operacional Ubuntu com um ambiente Java através desta imagem. Porém, embora este método seja altamente customizado, não é oficialmente recomendado pelo Docker. Nesse caso, o usuário não sabe como a imagem é construída ou se há um backdoor nela, e a eficiência de construção é muito baixa. Se você deseja construir imagens para vários sistemas operacionais ao mesmo tempo, não é necessário construí-las uma por uma? Como usuários comuns, seria melhor usarmos o Dokcerfile.

Vamos dar uma olhada em como usar o Dockerfile para criar uma imagem do sistema Ubuntu com um ambiente Java. Primeiro, crie um novo Dockerfilearquivo chamado:

touch Dockerfile

Então vamos editar. DockerfileInternamente precisamos escrever uma variedade de instruções para informar ao Docker as informações relevantes sobre nossa imagem:

FROM <基础镜像>

Primeiro, precisamos usar a instrução FROM para selecionar a imagem base da imagem atual (deve começar com esta instrução). Aqui podemos usá-la diretamente ubuntucomo imagem base. Claro, se não precisarmos de nenhuma imagem base, nós pode usá-lo diretamente scratchpara construir do zero. Aqui não há mais demonstrações.

Após a conclusão da configuração básica da imagem, precisamos executar o comando no contêiner para instalar o ambiente Java. Aqui precisamos usar RUNo comando:

RUN apt update
RUN apt install -y openjdk-8-jdk

Após cada instrução ser executada, uma nova camada de imagem é gerada.

OK, agora nosso Dockerfile está concluído e só precisamos concluir a construção uma vez:

docker build -t <镜像名称> <构建目录>

Após a execução, o Docker procurará o arquivo Dockerfile no diretório de construção e, em seguida, começará a executar as instruções no Dockerfile em sequência:

imagem-20220701155443170

Cada etapa do processo de construção é listada de forma muito clara. Há três instruções no total correspondentes às três etapas. Esperamos um pouco para instalar. Todas as informações de log durante o processo de instalação serão impressas diretamente no console (observe que o Docker A construção da imagem é armazenada em cache. Mecanismo, mesmo se você sair no meio do caminho e depois construir novamente, cada camada da imagem que foi construída antes será usada diretamente. A menos que o arquivo Dockerfile seja modificado e reconstruído, desde que uma determinada camada altere sua parte superior camada O cache de construção será invalidado, é claro que pullhaverá um mecanismo semelhante quando incluído)

imagem-20220701155812315

Finalmente, a instalação foi bem-sucedida e aparecerá localmente:

imagem-20220701155847721

Você pode ver que o tamanho da instalação é o mesmo que fizemos antes, porque as coisas feitas são exatamente as mesmas. Podemos historyvisualizar o histórico de construção usando o comando:

imagem-20220701160128689

Você pode ver que as duas camadas superiores são o conteúdo que geramos usando o comando apt, que são usadas diretamente como as duas camadas de espelhos no espelho atual. Cada espelho tem seu próprio ID e espelhos diferentes têm tamanhos diferentes. A imagem que geramos manualmente commitatravés do comando não possui este registro:

imagem-20220701160406891

Se você encontrar uma imagem com um ID de imagem ausente, geralmente é uma imagem baixada do Docker Hub. Esse problema ocorrerá, mas não é um grande problema. Use a imagem que construímos para criar um contêiner e você poderá experimentar diretamente o contêiner com ambiente Java:

imagem-20220701161546279

Aprenderemos gradualmente sobre outros comandos do Dockerfile no estudo subsequente.

Publique a imagem no repositório remoto

Anteriormente, aprendemos como construir uma imagem Docker. Podemos publicar nossa própria imagem no Docker Hub. Assim como um repositório remoto Git, podemos fazer upload de nossa própria imagem aqui: https://hub.docker.com/repositories , se você não não tem uma conta, cadastre-se primeiro.

imagem-20220701164609666

Clique em Criar Armazém no canto superior direito e preencha as informações:

imagem-20220701164939268

Após a conclusão da criação, temos um armazém de imagens públicas. Podemos fazer upload da imagem local. Antes de fazer o upload, precisamos modificar o nome da imagem para ficar mais padronizado. Aqui usamos o comando para renomear tag:

docker tag ubuntu-java-file:latest 用户名/仓库名称:版本

Aqui alteramos a versão para a versão 1.0 em vez do padrão mais recente.

imagem-20220701165231001

Após a conclusão da modificação, uma nova imagem local será criada com o nome que nós mesmos definimos. Então precisamos fazer login localmente:

imagem-20220701165446859

Após o login bem-sucedido, podemos fazer upload:

docker push nagocoler/ubuntu-java:1.0

imagem-20220701165744647

Haha, ainda é um pouco estressante fazer upload de 500 milhões de coisas. Se for muito lento, você pode fazer um espelho mais simples novamente. Após a conclusão do upload, abra o warehouse e você verá que já existe uma versão 1.0:

imagem-20220701165920060

imagem-20220701170053250

Observe que o repositório público pode ser pesquisado e baixado, então aqui excluímos todas as imagens locais e baixamos a imagem que acabamos de carregar. Aqui buscamos primeiro, searchbasta usar o comando:

docker search nagocoler/ubuntu-java

imagem-20220701170253126

Podemos usar o comando pull para baixá-lo:

docker pull nagocoler/ubuntu-java:1.0

imagem-20220701171148334

A imagem carregada é compactada, portanto o conteúdo baixado é menor. Tente executar:

imagem-20220701171253440

Claro, você também pode pedir a seus colegas que tentem baixar sua própria imagem em outras máquinas para ver se ela funciona normalmente.

O Docker Hub também pode construir servidores privados por si só, mas não entrarei em detalhes aqui. Neste ponto, algumas operações básicas sobre contêineres e espelhos foram explicadas.

Combate prático: use IDEA para construir a imagem do programa SpringBoot

Aqui criamos um novo projeto SpringBoot. Agora esperamos usar o Docker para implantar rapidamente nosso projeto SpringBoot no servidor onde o Docker está instalado. Podemos empacotá-lo como uma imagem Docker.

imagem-20220701173902376

Primeiro crie um projeto e deixe-o rodar. Se ele funcionar normalmente, não haverá problema. Em seguida, precisamos empacotá-lo como uma imagem Docker. Crie um novo Dockerfile aqui:

FROM ubuntu
RUN apt update && apt install -y openjdk-8-jdk

Primeiro, construímos uma imagem do sistema com um ambiente Java baseado no ubuntu. Em seguida, primeiro a conectamos ao nosso servidor Docker para construção. Como o IDEA vem com seu próprio plug-in Docker, clicamos diretamente no botão executar no canto superior esquerdo e selecione a segunda opção . Construir imagem para Dockerfile" :

imagem-20220701203741495

[Falha na transferência da imagem do link externo. O site de origem pode ter um mecanismo anti-leeching. Recomenda-se salvar a imagem e carregá-la diretamente (img-jVuXrtKl-1657097647534)(https://s2.loli.net/2022/07 /01/FAcME5yxZPD1aoz.png )]

Aqui precisamos configurar o servidor Docker, que é o Docker que instalamos no servidor Ubuntu. Aqui preenchemos as informações relacionadas ao servidor. Nossa primeira escolha é modificar algumas configurações do Docker e habilitar o acesso remoto do cliente:

sudo vim /etc/systemd/system/multi-user.target.wants/docker.service 

Depois de aberto, adicione destaques:

imagem-20220701202846707

Após a conclusão da modificação, reinicie o serviço Docker. Se for um servidor em nuvem, lembre-se de abrir a porta de conexão TCP 2375:

sudo systemctl daemon-reload
sudo systemctl restart docker.service 

Agora prossiga para configurá-lo no IDEA:

imagem-20220701203318098

Preencha o endereço IP do nosso servidor Docker na URL da API do mecanismo:

tcp://IP:2375

Depois que a conexão bem-sucedida for exibida, significa que a configuração está correta, clique em Salvar e comece a construir em nosso servidor Docker:

imagem-20220701203518930

Finalmente construído com sucesso:

imagem-20220701204815069

Como você pode ver, a imagem que acabamos de construir já existe no servidor Docker:

imagem-20220701204900943

No entanto, o nome não é especificado. Aqui nós o reconfiguramos:

imagem-20220701204955570

imagem-20220701205053642

Reconstrua-o e será nosso nome personalizado:

imagem-20220701205402607

imagem-20220701205350004

Vamos criar um contêiner e testá-lo:

[Falha na transferência da imagem do link externo. O site de origem pode ter um mecanismo anti-leeching. Recomenda-se salvar a imagem e carregá-la diretamente (img-Wzeb7k1S-1657097647536)(https://s2.loli.net/2022/ 07/01/8xPUg7qmVzXF9nN.png)]

Ok, agora que o ambiente básico está configurado, precisamos empacotar nosso projeto SpringBoot e executá-lo quando o contêiner for iniciado. Abra o Maven e execute o comando de empacotamento:

imagem-20220701205630885

Então precisamos editar o Dockerfile e colocar nele o pacote jar que construímos:

COPY target/DockerTest-0.0.1-SNAPSHOT.jar app.jar

Aqui você precisa usar o comando COPY para copiar o arquivo para a imagem. O primeiro parâmetro é o arquivo local que queremos copiar. O segundo parâmetro é o local do arquivo armazenado na imagem Docker. Como ainda não aprendemos o gerenciamento de armazenamento, nós o inserimos diretamente aqui. app.jarBasta salvá-lo diretamente no caminho padrão.

Então precisamos especificar para executar nosso programa Java na inicialização. Isso é feito usando o comando CMD:

FROM ubuntu
RUN apt update && apt install -y openjdk-8-jdk
COPY target/DockerTest-0.0.1-SNAPSHOT.jar app.jar
CMD java -jar app.jar
# EXPOSE 8080

O comando CMD pode definir o comando a ser executado após o contêiner ser iniciado. EXPOSE pode especificar a porta que o contêiner precisa expor. No entanto, ainda não aprendemos conhecimento relacionado à rede, então não o usaremos por enquanto Aqui especificamos o comando para iniciar o projeto Java. Após a conclusão da configuração, reconstrua:

imagem-20220701210438145

Você pode ver que novas etapas apareceram no histórico:

imagem-20220701213513862

Então inicie nossa imagem, podemos operá-la diretamente no IDEA sem digitar comandos, o que é um pouco cansativo:

imagem-20220701210845768

imagem-20220701210908997

Após a inicialização, você pode ver as informações de log da inicialização do contêiner à direita:

imagem-20220701210946261

imagem-20220701211029119

Mas descobrimos que não podemos acessá-lo diretamente após a inicialização. Por que isso acontece? Isso ocorre porque a rede dentro do container está isolada da rede externa.Se quisermos acessar o servidor no container, precisamos vincular a porta correspondente ao host e deixar o host também abrir esta porta para que possamos nos conectar o recipiente:

docker run -p 8080:8080 -d springboot-test:1.0

Isso -prepresenta a ligação de porta, que vincula a porta do contêiner Docker à porta do host, de modo que a porta 8080 do contêiner possa ser acessada através da porta 8080 do host (apresentaremos o gerenciamento da rede do contêiner em detalhes posteriormente), o parâmetro indica operação em segundo -dplano . Claro, também pode ser configurado diretamente no IDEA:

imagem-20220701211536598

Após configurar, clique para recriar o container:

imagem-20220701211701640

Após a nova execução, podemos acessar com êxito o projeto SpringBoot em execução no contêiner:

imagem-20220701211753962

Claro, para conveniência futura, podemos enviá-lo diretamente para o Docker Hub. Aqui criamos um novo armazém público:

imagem-20220701212330425

Desta vez usaremos o IDEA para demonstrar como fazer o upload da imagem diretamente, basta clicar:

imagem-20220701212458851

Em seguida, precisamos configurar nossas informações relacionadas ao Docker Hub:

[Falha na transferência da imagem do link externo. O site de origem pode ter um mecanismo anti-leeching. Recomenda-se salvar a imagem e carregá-la diretamente (img-74osw1mZ-1657097647538)(https://s2.loli.net/2022/07 /01/tMcD2kzNwW9J7d3.png)]

imagem-20220701212731276

OK, a configuração do armazém de espelho remoto está concluída, basta enviá-lo diretamente e aguardar a conclusão do envio.

imagem-20220701212902977

Você pode ver que nossa imagem apareceu no armazém remoto e também pode vê-la de forma síncrona no IDEA:

imagem-20220701213026214

Dessa forma, concluímos o empacotamento do projeto SpringBoot em uma imagem Docker usando IDEA.


Gerenciamento de rede de contêineres

**Nota:** O estudo desta seção requer o domínio de alguns conhecimentos do curso "Redes de Computadores".

Anteriormente, aprendemos algumas operações básicas de contêineres e espelhos e aprendemos como criar contêineres por meio de espelhos, construir contêineres sozinhos, enviar para armazéns remotos, etc. Nesta parte, discutiremos o gerenciamento de rede de contêineres.

Tipo de rede de contêiner

Após a instalação do Docker, ele criará três redes em nosso host. Use network lso comando para visualizar:

docker network ls

imagem-20220702161742741

Você pode ver que existem bridgetrês tipos hostde rede nonepor padrão: , a imagem padrão do Ubuntu não possui nenhum software, então instalamos antecipadamente os que precisaremos para a rede:

docker run -it ubuntu
apt update
apt install net-tools iputils-ping curl

Isto está instalado, saímos diretamente e construímos como uma nova imagem:

docker commit lucid_sammet ubuntu-net

imagem-20220702170441267

OK, poderemos usá-lo em um momento.

  • **nenhuma rede:** Esta rede não possui outras redes, exceto uma rede de loopback local. Podemos especificar essa rede ao criar o contêiner.

    Aqui --networkos parâmetros são usados ​​para especificar a rede:

    docker run -it --network=none ubuntu-net
    

    Após entrar, podemos verificar diretamente a rede atual:

    ifconfig
    

    Você pode ver que existe apenas um lodispositivo de rede de loopback local:

    imagem-20220702170000617

    Portanto, este contêiner não pode se conectar à Internet:

    imagem-20220702170531312

    Pode-se dizer que a operação "verdadeira" de uma única máquina é absolutamente segura. Ninguém pode acessá-la. É bom salvar algumas senhas.

  • **rede de ponte:** O tipo de rede usado pelo contêiner por padrão. Esta é a rede de ponte e o tipo de rede mais usado:

    Na verdade, quando verificarmos as informações de rede no host host, encontraremos um dispositivo de rede chamado docker0:

    [Falha na transferência da imagem do link externo. O site de origem pode ter um mecanismo anti-leeching. Recomenda-se salvar a imagem e carregá-la diretamente (img-TKTERH5x-1657097647539)(https://s2.loli.net/2022/07 /02/jDKSIriXec96uhy.png )]

    Este dispositivo de rede é um dispositivo virtual criado automaticamente quando o Docker é instalado. Para que ele é usado? Podemos dar uma olhada na situação dentro do container criado por padrão:

    docker run -it ubuntu-net
    

    imagem-20220702172532004

    Você pode ver que o endereço da interface de rede do contêiner é 172.17.0.2. Na verdade, esta é a rede virtual criada pelo Docker. É como se o contêiner tivesse um cabo de rede virtual separado conectado à rede virtual criada pelo Docker, e o A rede docker0 é realmente usada como Na função de uma ponte, uma extremidade é sua própria sub-rede virtual e a outra extremidade é a rede do host.

    A topologia da rede é semelhante à seguinte:

    imagem-20220702173005750

    Ao adicionar essa ponte, podemos gerenciar e controlar a rede de contêineres. Podemos usar network inspecto comando para visualizar as informações de configuração da ponte docker0:

    docker network inspect bridge
    

    imagem-20220702173431530

    A sub-rede configurada aqui é 172.17.0.0, a máscara de sub-rede é 255.255.0.0 e o gateway é 172.17.0.1, que é o dispositivo de rede virtual docker0, portanto o container que criamos acima tem o endereço 172.17 atribuído nesta sub-rede.

    Posteriormente também explicaremos como gerenciar e controlar redes de contêineres.

  • **rede host:** Quando o contêiner estiver conectado a esta rede, ele compartilhará a rede do host, e a configuração da rede é exatamente a mesma:

    docker run -it --network=host ubuntu-net
    

    Você pode ver que a lista de redes e a lista de hosts são iguais. Gostaria de saber se você notou que até o nome do host é exatamente o mesmo que fora:

    [A transferência da imagem do link externo falhou. O site de origem pode ter um mecanismo anti-leeching. Recomenda-se salvar a imagem e carregá-la diretamente (img-vr5IpDLe-1657097647540)(https://s2.loli.net/2022/ 07/02/cRAQtIxV4D9byCu.png)]

    Contanto que o host possa se conectar à Internet, ele também pode ser usado diretamente dentro do contêiner:

    imagem-20220702171041631

    Neste caso, usando diretamente a rede do host, basicamente não há perda no desempenho de transmissão, e podemos abrir portas diretamente, etc., sem qualquer ponte:

     apt install -y systemctl nginx
     systemctl start nginx
    

    Depois de instalar o Nginx, você pode acessá-lo diretamente sem abrir nenhuma porta:

    imagem-20220702171550979

    É muito mais conveniente do que uma rede em ponte.

Podemos escolher razoavelmente esses três usos de rede de acordo com a situação real.

Rede definida pelo usuário

Além das três redes que apresentamos anteriormente, também podemos personalizar nossa própria rede e permitir que o contêiner se conecte a essa rede.

O Docker fornece três drivers de rede por padrão: bridge, overlay, macvlan. Drivers diferentes correspondem a drivers de dispositivos de rede diferentes e implementam funções diferentes. Por exemplo, o tipo de ponte é na verdade o mesmo da rede de ponte que apresentamos anteriormente.

Podemos network createexperimentar usando:

docker network create --driver bridge test

Aqui criamos uma rede bridge chamada test:

imagem-20220702180837819

Você pode ver que um novo dispositivo de rede foi adicionado. Este será o gateway responsável pela nossa rede de contêineres. É igual ao docker0 anterior:

docker network inspect test

imagem-20220702181150667

Aqui criamos um novo contêiner usando esta rede:

 docker run -it --network=test ubuntu-net

imagem-20220702181252137

O endereço IP atribuído com sucesso está nesta rede. Observe que diferentes redes estão isoladas. Podemos criar outro contêiner para tentar:

imagem-20220702181808792

Você pode ver que diferentes redes estão isoladas umas das outras e não podem se comunicar. Claro, também conectamos este contêiner à rede à qual outro contêiner pertence:

docker network connect test 容器ID/名称

imagem-20220702182050204

Isso conecta uma nova rede:

imagem-20220702182146049

Você pode ver que um novo dispositivo de rede foi adicionado ao contêiner e está conectado à nossa própria rede definida. Agora que os dois contêineres estão na mesma rede, eles podem executar ping um no outro:
imagem-20220702182310008

Os outros dois tipos de redes não serão apresentados aqui. Eles são usados ​​para comunicação multi-host. Atualmente, aprendemos a usá-los apenas em uma única máquina.

rede entre contêineres

Vamos primeiro dar uma olhada na comunicação de rede entre contêineres. Na verdade, já demonstramos o ping antes. Agora criamos dois contêineres do Ubuntu:

docker run -it ubuntu-net

Primeiro obtenha as informações de rede de um dos contêineres:

[A transferência da imagem do link externo falhou. O site de origem pode ter um mecanismo anti-leeching. Recomenda-se salvar a imagem e carregá-la diretamente (img-cGnoTjG7-1657097647542)(https://s2.loli.net/2022/ 07/02/yTEcg4l2kASBnQu.png)]

Podemos executar ping neste contêiner diretamente de outro contêiner:

[Falha na transferência da imagem do link externo. O site de origem pode ter um mecanismo anti-leeching. Recomenda-se salvar a imagem e carregá-la diretamente (img-IOOtmkLm-1657097647542)(/Users/nagocoler/Library/Application Support/typora-user -imagens/image-20220702175444713.png)]

Você pode ver que é possível executar ping diretamente porque ambos os contêineres usam a rede bridge e estão na mesma sub-rede, para que possam acessar um ao outro.

Podemos nos comunicar entre contêineres diretamente através do endereço IP do contêiner, desde que os dois contêineres estejam na mesma rede. Embora isso seja mais conveniente, na maioria dos casos, o endereço IP após a implantação do contêiner é atribuído automaticamente (é claro Também pode ser --ipespecificado manualmente, mas ainda é inconveniente.) Não podemos saber o endereço IP antecipadamente, então existe um método que possa ser mais flexível?

Podemos usar o servidor DNS fornecido pelo Docker. É como um servidor DNS real e pode resolver nomes de domínio. É muito simples de usar. Só precisamos fornecer um nome quando o contêiner for iniciado. Podemos acessar esse nome diretamente. Eventualmente, será resolvido para o endereço IP do contêiner correspondente, mas observe que só terá efeito em nossa rede definida pelo usuário e a rede padrão não funcionará:

docker run -it --name=test01 --network=test ubuntu-net
docker run -it --name=test02 --network=test ubuntu-net

Em seguida, basta executar ping diretamente no nome da outra parte:

imagem-20220702192457354

Você pode ver que o nome será resolvido automaticamente para o endereço IP correspondente, então você não precisa se preocupar com a incerteza do IP.

Claro, também podemos permitir que dois contêineres compartilhem a mesma rede ao mesmo tempo. Observe que o compartilhamento aqui é compartilhar diretamente o mesmo dispositivo de rede. Os dois contêineres compartilham um endereço IP e só precisam ser especificados ao criar:

docker run -it --name=test01 --network=container:test02 ubuntu-net

Aqui, a rede é especificada como a rede de um contêiner, de modo que os dois contêineres usem a mesma rede:

imagem-20220702200711351

Você pode ver que os endereços IP dos dois contêineres e os endereços Mac das placas de rede são exatamente iguais. Suas redes agora são compartilhadas. Neste momento, ao acessar o host local no contêiner, é você e os outros.

Podemos instalar o Nginx no contêiner 1 e acessá-lo no contêiner 2:

 apt install -y systemctl nginx
 systemctl start nginx

imagem-20220702201348722

Acessou com sucesso o servidor Nginx em outro contêiner.

Rede externa de contêiner

Anteriormente introduzimos a comunicação de rede entre contêineres, a seguir veremos a comunicação entre o contêiner e a rede externa.

Primeiro, vamos ver como o contêiner acessa a Internet. Nas três redes padrão, apenas o modo compartilhado e o modo bridge podem se conectar à rede externa. O modo compartilhado, na verdade, usa diretamente o equipamento de rede do host para se conectar à Internet. Aqui, vamos dar uma olhada principalmente no modo bridge.

Através do estudo anterior, aprendemos que o modo bridge na verdade cria uma rede virtual separada, permite que o contêiner esteja nesta rede virtual e, em seguida, se conecta ao mundo externo através da ponte. Então, como o pacote de dados chega ao host host de a rede dentro do container e depois enviar para a Internet? Na verdade, o mais importante em todo o processo é confiar no NAT (Network Address Translation) para traduzir o endereço e, em seguida, usar o endereço IP do host para enviar o pacote de dados.

Aqui complementaremos o NAT aprendido no curso "Redes de Computadores":

Na verdade, o NAT é frequentemente visto em nossas vidas, por exemplo, se quisermos acessar um recurso na Internet e nos comunicarmos com o servidor, precisamos enviar o pacote de dados, e o servidor também precisa enviar o pacote de dados de volta . Nós Você pode saber o endereço IP do servidor, ou pode se conectar diretamente, porque o endereço IP do servidor está exposto na Internet, mas nossa LAN é diferente. É limitada apenas à nossa casa. Por exemplo, se nós conectar-se ao roteador em casa, você pode obter um endereço IP, mas descobrirá que esse IP da rede pública não pode nos acessar diretamente, porque esse endereço IP é apenas um endereço IP de rede local, comumente conhecido como IP da intranet. Como não podemos ser acessado da rede pública, então o servidor Como o pacote de dados é enviado para nós?

imagem-20220702230700124

Na verdade, o NAT é usado aqui para nos ajudar a comunicar com servidores na Internet. Através do NAT, o endereço IP da rede local pode ser mapeado para o endereço IP da rede pública correspondente. Uma extremidade do dispositivo NAT é conectada ao externo rede e a outra extremidade está conectada à rede interna. Todos os dispositivos na Internet, quando quisermos nos comunicar com a rede externa, podemos enviar o pacote de dados para o dispositivo NAT, que mapeará o endereço de origem do pacote de dados para seu endereço na rede externa, para que o servidor possa descobri-lo, possa estabelecer comunicação diretamente com ele. Quando o servidor envia dados de volta, eles também são entregues diretamente ao dispositivo NAT e, em seguida, encaminhados para o dispositivo de intranet correspondente de acordo com o mapeamento de endereço (é claro, porque o endereço IP público é limitado, a combinação de IP + porta é geralmente usado na forma de ANPT)

Portanto, se você abrir o Baidu e pesquisar o IP diretamente, descobrirá que esse endereço IP não é o local, mas o endereço público do dispositivo NAT:

imagem-20220702231458928

Na verdade, os roteadores em nossas casas geralmente possuem funções NAT, e o modo NAT está ativado por padrão.Incluindo nossa comunidade, existe também um dispositivo NAT para conversão, para que seu computador possa viajar no mundo da Internet. É claro que o NAT também pode proteger o equipamento da rede interna de ser exposto diretamente à rede pública, o que também será mais seguro. Somente quando iniciamos ativamente uma conexão é que outras pessoas podem saber sobre nós.

Claro, o mesmo se aplica ao nosso Docker. Na verdade, se você deseja enviar pacotes de dados da intranet para a Internet, você precisa passar por um conjunto de processos como este:

imagem-20220702232449520

Desta forma, a rede interna utilizada pelo contêiner Docker pode se comunicar com a rede externa.

Mas há um problema com isso. Se confiarmos apenas no NAT, o mundo exterior só poderá nos conhecer quando entrarmos em contato ativo com o mundo exterior. Mas agora podemos implantar alguns serviços em nossos contêineres e precisar que o mundo exterior se conecte ativamente a nós. … O que devemos fazer neste momento?

Podemos configurar o mapeamento de portas diretamente no contêiner. Lembra que implantamos o servidor Nginx na primeira lição?

docker run -d -p 80:80 nginx

Os parâmetros aqui -psão, na verdade, configurações de mapeamento de portas. O mapeamento de portas pode mapear a porta que o contêiner precisa para fornecer serviços externos para a porta do host host. Dessa forma, quando a porta correspondente do host host é acessada de fora, ele será encaminhado diretamente para o contêiner. A porta está mapeada. A regra é que 宿主端口:容器端口o que está configurado aqui é mapear a porta 80 do container para a porta 80 do host host.

[Falha na transferência da imagem do link externo. O site de origem pode ter um mecanismo anti-leeching. Recomenda-se salvar a imagem e carregá-la diretamente (img-XK4pgE2W-1657097647543)(https://s2.loli.net/2022/ 07/02/WQzEVTwePNaHYgG.png )]

Assim que a porta de monitoramento 80 do host receber um pacote de dados, ele será encaminhado diretamente para o contêiner correspondente. Assim, após configurar o mapeamento de portas, normalmente podemos acessar os serviços no contêiner de fora:

imagem-20220630165440751

Também podemos entrar diretamente docker pspara visualizar o mapeamento da porta:

imagem-20220702233831651

Até agora, isso é tudo para a parte de rede do contêiner. Claro, esta é apenas a operação da rede do contêiner em uma única máquina. Em cursos futuros, aprenderemos mais sobre a configuração de rede em vários hosts.


Gerenciamento de armazenamento de contêineres

Anteriormente introduzimos o gerenciamento de rede de contêineres, agora sabemos como configurar a rede de contêineres e alguns princípios relacionados. Outra parte importante é o armazenamento em contêineres. Nesta seção teremos uma compreensão aprofundada do gerenciamento de armazenamento em contêineres.

Armazenamento persistente de contêiner

Sabemos que depois que o contêiner é criado, os arquivos que criamos e modificamos no contêiner são realmente salvos e operados na camada superior do contêiner pelo mecanismo de camadas do contêiner. Para proteger as imagens de cada camada abaixo de serem modificadas, então há é um recurso CopyOnWrite. Mas isso também fará com que os dados sejam perdidos quando o contêiner for destruído.Quando destruímos o contêiner e criamos um novo contêiner, todos os dados são perdidos e voltamos diretamente ao local onde o sonho começou.

Em alguns casos, podemos querer armazenar determinados arquivos de forma persistente no contêiner, em vez de armazená-los uma única vez. Aqui, um volume de dados é usado.

Antes de começarmos, vamos preparar a imagem a ser utilizada no experimento:

docker run -it ubuntu
apt update && apt install -y vim

Em seguida, empacote-o em uma imagem que usaremos mais tarde:

docker commit 

Podemos permitir que o contêiner salve o arquivo no host host, de modo que, mesmo que o contêiner seja destruído, o arquivo será retido no host host. Na próxima vez que um contêiner for criado, o arquivo correspondente ainda poderá ser lido no host. hospedar. Como fazer isso? Basta especificá-lo quando o contêiner for iniciado:

mkdir test

Agora criamos um novo testdiretório no diretório do usuário, criamos um arquivo nele e escrevemos algum conteúdo:

vim test/hello.txt

Então podemos montar o diretório ou arquivo no host host em um diretório no contêiner:

docker run -it -v ~/test:/root/test ubuntu-volume

Um novo parâmetro é usado aqui -vpara especificar a montagem do arquivo. Aqui, o diretório de teste que acabamos de criar é montado no caminho /root/test do contêiner.

imagem-20220703105256049

Desta forma, podemos acessar diretamente os arquivos do host host no contêiner. Claro, se editarmos os arquivos no diretório de montagem, será equivalente a editar os dados do host host:

vim /root/test/test.txt  

imagem-20220703105626105

No diretório correspondente do host host, você pode acessar diretamente o arquivo que acabamos de criar.

A seguir, vamos destruir o contêiner e ver se os dados montados ainda podem ser retidos quando o contêiner não existir mais:

imagem-20220703105847329

Pode-se observar que mesmo que destruamos o container, os arquivos na máquina host ainda existem e não serão afetados, desta forma, na próxima vez que criarmos uma nova imagem, ainda poderemos utilizar esses arquivos salvos externamente.

Por exemplo, se agora quisermos implantar um servidor Nginx para proxy de nosso front-end, podemos salvar diretamente a página front-end na máquina host e, em seguida, permitir que o Nginx no contêiner acesse-a por meio da montagem. mesmo que a imagem Nginx seja atualizada posteriormente, ela precisará ser reinstalada. A criação não afetará nossa página front-end. Vamos testar aqui. Primeiro carregamos o modelo de front-end no servidor:

scp Downloads/moban5676.zip 192.168.10.10:~/

Em seguida, descompacte-o no servidor:

unzip moban5676.zip

Então podemos iniciar o contêiner:

docker run -it -v ~/moban5676:/usr/share/nginx/html/ -p 80:80 -d nginx

Aqui montaremos o diretório descompactado no diretório de site padrão do Nginx no contêiner /usr/share/nginx/html/(uma vez que é montado no nível superior, ele substituirá os arquivos originais na camada espelhada), para que o Nginx faça proxy diretamente do front end que armazenamos no host host. Página, é claro, não se esqueça de mapear a porta para o host host. A imagem que usamos aqui é a imagem oficial do nginx.

Agora entramos no container e iniciamos o serviço Nginx:

systemctl start nginx

Em seguida, acesse-o pelo navegador para ver se o proxy foi bem-sucedido:

imagem-20220703111937254

Você pode ver que nossa página front-end é proxy direta. Claro, se quisermos escrever uma configuração personalizada, podemos usar o mesmo método.

Observe que se -vnão especificarmos um diretório no host para montagem ao usar parâmetros, o Docker criará automaticamente um diretório e copiará o conteúdo do caminho correspondente no contêiner para o diretório criado automaticamente e, finalmente, montará no contêiner, este é o volume de dados gerenciado pelo Docker (volume gerenciado pelo docker). Vamos tentar:

docker run -it -v /root/abc ubuntu-volume

Observe que especificamos apenas o caminho de montagem aqui e não especificamos o diretório correspondente do host. Continue a criar:

imagem-20220703112702067

Após a criação, você pode ver rootque há um novo abcdiretório no diretório, então onde ele está localizado especificamente no host host? Aqui ainda podemos usar inspecto comando:

docker inspect bold_banzai 

[Falha na transferência da imagem do link externo. O site de origem pode ter um mecanismo anti-leeching. Recomenda-se salvar a imagem e carregá-la diretamente (img-zANxbiPe-1657097647544)(https://s2.loli.net/2022/07 /03/zFotAfeBpcRjKWN.png )]

Você pode ver que Sorce aponta para /var/libum diretório em . Podemos entrar neste diretório para criar um novo arquivo. Lembre-se de aumentar as permissões antes de entrar. Se as permissões forem baixas, você não poderá entrar:

imagem-20220703114333446

Vamos criar um novo documento de texto:

imagem-20220703114429831

Na verdade, é o mesmo de antes e também pode ser visto no contêiner.É claro que, após a exclusão do contêiner, os dados ainda serão retidos. Quando não precisarmos mais usar o volume de dados, podemos excluí-lo:

imagem-20220703145011638

Claro, às vezes, por conveniência, você pode não precisar montar um diretório diretamente, mas apenas transferir alguns arquivos do host para o contêiner. Aqui podemos usar o comando para concluir cp:

imagem-20220703115648195

Este comando suporta a cópia de arquivos do host host para o contêiner ou a cópia de arquivos do contêiner para o host host. O método de uso é semelhante ao comando que vem com o Linux cp.

Compartilhamento de dados de contêiner

Anteriormente, montamos os arquivos no host host diretamente no contêiner por meio da montagem, para que o contêiner possa acessar diretamente os arquivos no host host, e os arquivos no host host não serão limpos quando o contêiner for excluído.

Vamos dar uma olhada em como realizar o compartilhamento de dados entre contêineres. Na verdade, de acordo com nossas ideias anteriores, podemos criar um diretório público no host host, para que todos esses contêineres que precisam ser compartilhados possam montar esse diretório público:

docker run -it -v ~/test:/root/test ubuntu-volume

imagem-20220703141840532

Como a mesma área da máquina host está montada, o conteúdo pode ser acessado diretamente em ambos os contêineres. Claro, também podemos montar o diretório de outro contêiner e especificar diretamente o diretório a ser usado para montar este contêiner ao iniciar o contêiner:

docker run -it -v ~/test:/root/test --name=data_test ubuntu-volume
docker run -it --volumes-from data_test ubuntu-volume

Aqui você usa para --volumes-fromespecificar outro contêiner (este tipo de contêiner é usado para fornecer volumes de dados para outros contêineres, geralmente o chamamos de contêiner de volume de dados)

imagem-20220703142849845

Pode-se observar que o conteúdo montado no contêiner do volume de dados também existe no contêiner atual. Claro, mesmo que o contêiner do volume de dados seja excluído neste momento, isso não afetará este lado, porque este lado equivale a herdar o contêiner de volume de dados. O volume de dados é fornecido, portanto, essencialmente, os dois contêineres montam o mesmo diretório para obter o compartilhamento de dados.

Embora os dados possam ser transferidos entre contêineres através do método acima, isso é inconveniente. Às vezes, queremos apenas compartilhar entre contêineres e não queremos que a função do host participe diretamente do compartilhamento. Então, precisamos encontrar uma maneira melhor. Na verdade, podemos colocar os dados completamente no contêiner e compartilhar diretamente os dados empacotados no contêiner com outros contêineres construindo um contêiner.Claro, ainda é essencialmente um volume de dados gerenciado pelo Docker, embora ainda não esteja completamente separado do host. Mas a portabilidade é muito maior.

Vamos escrever um Dockerfile:

FROM ubuntu
ADD moban5676.tar.gz /usr/share/nginx/html/
VOLUME /usr/share/nginx/html/

Aqui usamos um novo comando ADD, que é semelhante ao comando COPY. Ele também pode copiar arquivos para o contêiner, mas pode descompactar automaticamente os arquivos compactados. Aqui você só precisa preencher os arquivos compactados e o seguinte VOLUME O A diretiva -vcria um ponto de montagem no contêiner, assim como fizemos com os parâmetros:

cd test
tar -zcvf moban5676.tar.gz *
mv moban5676.tar.gz ..
cd ..

Então construímos diretamente:

docker build -t data .

imagem-20220703153109650

Agora vamos executar um contêiner e ver:

[Falha na transferência da imagem do link externo. O site de origem pode ter um mecanismo anti-leeching. Recomenda-se salvar a imagem e carregá-la diretamente (img-V1YYSZM6-1657097647545)(https://s2.loli.net/2022/07 /03/SUg32jlwMcY7Btp.png )]

Você pode ver que todos os arquivos foram descompactados automaticamente (exceto que os nomes dos arquivos em chinês estão ilegíveis, mas isso não importa).Saímos do contêiner e podemos ver que os volumes de dados que precisamos usar foram adicionados ao lista de volumes de dados:

imagem-20220703153514730

imagem-20220703153542739

Este local é na verdade o local onde os dados são armazenados no host atual, mas é gerenciado pelo Docker em vez de personalizado por nós. Agora podemos criar um novo contêiner e herdar diretamente:

docker run -p 80:80 --volumes-from=data_test -d nginx

Visite o servidor Nginx e você poderá ver o proxy bem-sucedido:

imagem-20220703111937254

Desta forma, podemos colocar os dados no contêiner para compartilhamento. Não precisamos especificar deliberadamente o ponto de montagem do host. Em vez disso, o Docker os gerencia sozinho, de modo que, mesmo que o host seja migrado, ele ainda possa ser implantado rapidamente.


Gerenciamento de recursos de contêiner

Já concluímos o estudo de vários módulos principais do Docker e, por fim, vamos dar uma olhada em como gerenciar os recursos do contêiner.

Operações de controle de contêineres

Antes de começarmos, ainda precisamos adicionar alguns outros comandos de contêiner que não mencionamos antes.

Em primeiro lugar, quando nosso projeto SpringBoot está em execução, como podemos verificar as informações do log de saída?

docker logs test

Use o comando aqui logpara imprimir as informações de log no contêiner:

imagem-20220701221210083

Claro, você também pode adicionar -fparâmetros para imprimir continuamente informações de registro.

imagem-20220701215617022

Agora nosso contêiner foi iniciado, mas e se quisermos entrar no contêiner para monitorá-lo? Podemos attachanexar o comando ao terminal do comando de inicialização do contêiner:

docker attach 容器ID/名称

imagem-20220701215829492

Observe que agora você mudou para o terminal no contêiner. Se quiser sair, você precisa pressionar Ctrl+P primeiro e depois Ctrl+Q para sair do terminal. Você não pode usar Ctrl+C diretamente para encerrar, o que irá diretamente encerrar o programa em execução no programa Docker.Java.

imagem-20220701220018207

Depois de sair, o contêiner ainda está em execução.

Também podemos usar execcomandos para iniciar um novo terminal no contêiner ou executar comandos no contêiner:

docker exec -it test bash

-itA operação é igual ao runcomando, após executar aqui, um novo terminal será criado (claro que o programa original ainda está rodando normalmente) e iremos interagir em um novo terminal:

imagem-20220701220601732

Claro, você também pode simplesmente executar um comando no contêiner:

imagem-20220701220909626

Após a execução, um novo terminal será aberto no contêiner para executar o comando e gerar os resultados.

Também aprendemos a operação de parada do contêiner anteriormente e stopparamos o contêiner digitando um comando. No entanto, essa operação não irá parar imediatamente, mas esperará que o contêiner lide com as consequências. Então, como podemos encerrar o contêiner à força? Podemos usar killo comando diretamente, o que equivale a enviar o sinal SIGKILL ao processo para forçá-lo a terminar.

docker kill test

Comparado com stopo pedido, killnão é tão gentil.

Às vezes, podemos querer apenas que o contêiner pare de funcionar temporariamente, em vez de encerrá-lo diretamente. Esperamos retomar a execução do contêiner em algum momento no futuro. Neste momento, podemos usar o comando para pausar pauseo contêiner:

docker pause test

Após pausar o contêiner, o programa para temporariamente de funcionar e não consegue responder às solicitações enviadas pelo navegador:

imagem-20220701222537737

imagem-20220701222243900

Neste momento, a magia do amor está girando em círculos. Podemos restaurá-la ao funcionamento usando unpauseo comando:

docker unpause test

Após retomar a operação, a resposta foi bem-sucedida instantaneamente.

[Falha na transferência da imagem do link externo. O site de origem pode ter um mecanismo anti-leeching. Recomenda-se salvar a imagem e carregá-la diretamente (img-mQep9X8o-1657097647547)(https://s2.loli.net/2022/ 07/01/g2b8mxVz1i7WJop.png)]

gerenciamento de recursos físicos

Para um contêiner, em alguns casos, podemos não querer que ele ocupe todos os recursos do sistema para ser executado. Queremos alocar apenas uma parte dos recursos para o contêiner. Por exemplo, apenas 2G de memória são alocados para o contêiner. Apenas um é permitido usar no máximo 2G e não é permitido ocupar mais recursos.Com mais memória, precisamos limitar os recursos do contêiner.

docker run -m 内存限制 --memory-swap=内存和交换分区总共的内存限制 镜像名称

O -mparâmetro é o limite de uso da memória física do contêiner, --memory-swapmas o limite da soma da memória e das partições swap. Ambos são por padrão -1, o que significa que não há limite (se apenas os parâmetros forem especificados no início -m, então o limite da memória swap é igual ao limite do uso da memória física do contêiner. Para mantê-lo consistente, memória + swap é igual ao -mdobro do tamanho) Por padrão, é o mesmo que a máquina host, que tem Memória 2G. Agora podemos tentar limitar a memória do contêiner a 100M, incluindo 50M de memória física e 50M de memória swap. Tente iniciá-lo. Programa SpringBoot:

docker run -it -m 50M --memory-swap=100M nagocoler/springboot-test:1.0

Como você pode ver, não foi possível iniciar devido à memória insuficiente:

imagem-20220702104653971

É claro que, além das restrições de memória, também podemos limitar os recursos da CPU. Por padrão, todos os contêineres podem usar os recursos da CPU igualmente. Podemos ajustar os pesos da CPU de diferentes contêineres (o padrão é 1024) para atender à demanda. Para alocar recursos, você precisa usar -copções aqui ou você pode inserir o nome completo --cpu-share:

docker run -c 1024 ubuntu
docker run -c 512 ubuntu

A proporção de peso da CPU do contêiner aqui é de 16 para 8, que é de 2 para 1 (observe que isso só terá efeito quando houver vários contêineres).Então, quando os recursos da CPU estiverem escassos, os recursos serão alocados de acordo com esse peso Claro, se os recursos da CPU não forem escassos, neste caso ainda há uma chance de usar todos os recursos da CPU.

Aqui usamos uma ferramenta de teste de estresse para verificar:

docker run -c 1024 --name=cpu1024 -it ubuntu
docker run -c 512 --name=cpu512 -it ubuntu

Em seguida, entramos no contêiner para instalar stressas ferramentas de teste de estresse:

apt update && apt install -y stress

Em seguida, iniciamos as ferramentas de teste de estresse em ambos os contêineres, gerando quatro processos para calcular continuamente a raiz quadrada de números aleatórios:

stress -c 4

Em seguida, entramos no topo para ver o status da CPU (lembre-se de encerrar o contêiner rapidamente após ler isto, caso contrário a CPU ficará cheia e travada):

imagem-20220702114126128

Pode-se observar que contêineres com peso alto recebem mais recursos de CPU, enquanto contêineres com peso baixo recebem apenas metade dos recursos de CPU.

Claro, também podemos limitar diretamente o número de CPUs usadas pelo contêiner:

docker run -it --cpuset-cpus=1 ubuntu

--cpuset-cpusA opção pode ser diretamente limitada à execução na CPU especificada. Por exemplo, se nosso host tiver uma CPU de 2 núcleos, podemos dividir as duas CPUs 0 e 1 para uso do Docker. Após a restrição, apenas os recursos da CPU 1 será usado:

imagem-20220702115538699

Percebe-se que os quatro processos utilizam apenas 25% da CPU cada, e o total é 100%, o que significa que só podem ocupar o uso total de uma CPU. Se você quiser alocar múltiplas CPUs, separe-as com vírgulas:

docker run -it --cpuset-cpus=0,1 ubuntu

Isso usará estas duas CPUs:

imagem-20220702115818344

Claro, você também pode usá-lo diretamente --cpuspara limitar o número de recursos da CPU usados:

docker run -it --cpus=1 ubuntu

imagem-20220702120329140

Depois que o limite for definido como 1, apenas os recursos fornecidos por uma CPU poderão ser usados, portanto, apenas os recursos de uma CPU serão carregados aqui. É claro que existem --cpu-period somas mais sofisticadas --cpu-quota, que não serão apresentadas aqui.

Finalmente, vamos dar uma olhada nas limitações do desempenho de leitura e gravação de IO do disco. Primeiro usamos ddo comando para testar a velocidade de leitura e gravação do disco:

dd if=/dev/zero of=/tmp/1G bs=4k count=256000 oflag=direct

Você não precisa esperar a execução terminar, apenas Ctrl+C para encerrá-la no meio:

imagem-20220702121839871

Você pode ver que a velocidade atual de leitura e gravação é de 86,4 MB/s e podemos limitá-la por meio dos parâmetros --device-read/write-bpse .--device-read/write-iops

Vamos falar sobre a diferença primeiro:

  • bps: A quantidade de dados lidos e gravados por segundo.
  • iops: Número de IOs por segundo.

Por uma questão de intuição, aqui usamos diretamente BPS como condição de restrição:

docker run -it --device-write-bps=/dev/sda:10MB ubuntu

Como o sistema de arquivos do contêiner está ativado /dev/sda, limitaremos /dev/sda:10MBa velocidade de gravação em /dev/sda a apenas 10 MB/s. Vamos testá-lo:

imagem-20220702122557288

Você pode ver que a velocidade atual é de apenas 10 MB.

Monitoramento de contêineres

Finalmente, vamos dar uma olhada em como monitorar o status de execução do contêiner em tempo real. Agora queremos ser capazes de monitorar o uso de recursos do contêiner. O que devemos fazer?

Podemos usar statso comando para monitorar:

docker stats

imagem-20220702153236692

Vários status do contêiner podem ser monitorados em tempo real, incluindo uso de memória, uso de CPU, E/S de rede, E/S de disco e outras informações. Claro, se limitarmos o uso de memória:

docker run -d -m 200M nagocoler/springboot-test:1.0

Você pode ver as limitações muito claramente:

[A transferência da imagem do link externo falhou. O site de origem pode ter um mecanismo anti-leeching. Recomenda-se salvar a imagem e carregá-la diretamente (img-Gzph9e4m-1657097647548)(https://s2.loli.net/2022/ 07/02/CGc6T4iYyN7PD51.png)]

Além de utilizar statscomandos para monitorar a situação em tempo real, você também pode utilizar topcomandos para visualizar os processos no contêiner:

docker top 容器ID/名称

imagem-20220702153957780

Claro, você também pode carregar alguns parâmetros, os parâmetros específicos pssão consistentes com os parâmetros de comando do Linux, então não vou apresentá-los aqui.

Mas será este tipo de monitorização demasiado primitivo? Existe um painel web que pode ser usado para monitoramento e gerenciamento em tempo real? alguns.

Precisamos implantar um aplicativo de painel de gerenciamento web Docker separadamente. Geralmente, os mais comuns são: Portainer. Aqui podemos implantar este aplicativo diretamente através da imagem Docker. Após pesquisar, descobrimos que o endereço de manutenção da versão mais recente é: https:/ /hub .docker.com/r/portainer/portainer-ce

CE é uma versão comunitária gratuita e, claro, existe uma versão comercial BE. Aqui apenas instalamos a versão comunitária diretamente. O tutorial oficial de instalação do Linux: https://docs.portainer.io/start/install/server/docker/ linux, incluindo alguns preparativos necessários antes da instalação.

Primeiro precisamos criar um volume de dados para o Portainer usar:

docker volume create portainer_data

Em seguida, instale e inicie através do comando oficial:

docker run -d -p 8000:8000 -p 9443:9443 --name portainer --restart=always -v /var/run/docker.sock:/var/run/docker.sock -v portainer_data:/data portainer/portainer-ce:latest

Observe que duas portas precisam ser abertas aqui, uma é a porta 8000 e a outra é a porta 9443.

imagem-20220702155450772

OK, a inicialização foi bem-sucedida. Podemos fazer login diretamente no painel de back-end: https://IP:9443/. O acesso HTTPS é necessário aqui. O navegador pode avisar que não é seguro, apenas ignore:

imagem-20220702155637366

imagem-20220702155703962

Depois de entrar, precisamos nos registrar. Aqui só precisamos digitar a senha duas vezes. O nome de usuário padrão é admin. Após preencher, podemos começar a usá-lo:

[Falha na transferência da imagem do link externo. O site de origem pode ter um mecanismo anti-leeching. Recomenda-se salvar a imagem e carregá-la diretamente (img-KyKabWKY-1657097647549)(https://s2.loli.net/2022/ 07/02/P1JIKaMCl7guYoz.png)]

Clique em Começar para entrar na página de gerenciamento. Podemos ver que há atualmente um servidor Docker local em execução:

imagem-20220702160328972

Podemos clicar para entrar e gerenciá-lo detalhadamente, mas a única desvantagem é que não há chinês, o que é bastante incômodo. Você também pode usar a versão não oficial em chinês: https://hub.docker.com/r/6053537/ portainer-ce.


Orquestração de contêiner independente

Por fim, vamos explicar o Docker-Compose, que pode orquestrar nossos contêineres. Por exemplo, agora queremos implantar vários tipos de serviços em um host, incluindo bancos de dados, filas de mensagens, aplicativos SpringBoot, ou queremos construir um cluster MySQL. Nesse caso, precisamos criar vários contêineres para concluir, mas esperamos alcançar a implantação com um clique, o que devemos fazer neste momento? Estamos prestes a usar a orquestração de contêineres para permitir que vários contêineres sejam implantados de acordo com nossa própria orquestração.

**Documento oficial: **https://docs.docker.com/get-started/08_using_compose/, é definitivamente impossível apresentar todas as configurações no vídeo tutorial, então se seus amigos quiserem saber mais configurações, se você tiver mais necessidades, você pode consultar diretamente a documentação oficial.

começo rápido

No ambiente Linux precisamos primeiro instalar o plug-in:

sudo apt install docker-compose-plugin

Em seguida, entre docker compose versionpara verificar se a instalação foi bem-sucedida.

[Falha na transferência da imagem do link externo. O site de origem pode ter um mecanismo anti-leeching. Recomenda-se salvar a imagem e carregá-la diretamente (img-vKLsI2Fz-1657097647549)(https://s2.loli.net/2022/ 07/03/5XDiAMpgW9aqUGJ.png )]

Aqui tomamos a implantação do projeto SpringBoot como exemplo. Continuamos a usar o projeto SpringBoot empacotado anteriormente. Agora queremos implantar este projeto SpringBoot ao mesmo tempo que implantamos um servidor MySQL e um servidor Redis. Neste momento, o todo o ambiente completo para a execução do nosso projeto SpringBoot, primeiro obtenha a imagem correspondente:

docker pull mysql/mysql-server
docker pull redis

Em seguida, precisamos instalar o DockerCompose localmente, baixar o endereço: https://github.com/docker/compose/releases, baixar a versão correspondente ao nosso computador e depois configurá-lo no IDEA:

imagem-20220703175103531

Após a conclusão do download, altere o caminho do arquivo executável do Docker Compose para o caminho onde você armazenou o arquivo executável que acabou de baixar. Para Windows, basta definir o caminho diretamente. Após o download para MacOS, você precisa realizar as seguintes operações:

mv 下载的文件名称 docker-compose
sudo chmod 777 docker-compose
sudo mv docker-compose /usr/local/bin

Após a conclusão da configuração, ele pode ser usado normalmente, caso contrário não será executado.Então podemos começar a escrever o arquivo docker-compose.yml no IDEA.

imagem-20220703180206437

Clique no botão "Sincronizar com a janela da ferramenta de serviço" no canto superior direito aqui, para que você possa verificar a situação abaixo daqui a pouco.

Vamos configurar este arquivo do zero agora. Agora temos que criar três serviços, um é o servidor MySQL, um é o servidor Redis e o outro é o servidor SpringBoot. Três contêineres são necessários para serem executados separadamente. Primeiro, escrevemos estes três Serve:

version: "3.9"  #首先是版本号,别乱写,这个是和Docker版本有对应的
services:   #services里面就是我们所有需要进行编排的服务了
  spring:   #服务名称,随便起
    container_name: app_springboot  #一会要创建的容器名称
  mysql:
    container_name: app_mysql
  redis:
    container_name: app_redis

Desta forma, configuramos os três serviços e os nomes dos contêineres correspondentes que serão criados posteriormente. Em seguida, precisamos especificar as imagens correspondentes a esses contêineres. O primeiro é nosso aplicativo SpringBoot. Também podemos atualizar o aplicativo posteriormente. e modificações, então aqui precisamos construir a imagem do Dockerfile antes de implantar:

spring:
  container_name: app_springboot
  build: .  #build表示使用构建的镜像,.表示使用当前目录下的Dockerfile进行构建

Vamos modificar o Dockerfile aqui e alterar a imagem básica para uma imagem que empacotou o ambiente JDK:

FROM adoptopenjdk/openjdk8
COPY target/DockerTest-0.0.1-SNAPSHOT.jar app.jar
CMD java -jar app.jar

Depois, há os outros dois serviços.Os outros dois serviços precisam usar as imagens correspondentes para iniciar o contêiner:

mysql:
  container_name: app_mysql
  image: mysql/mysql-server:latest  #image表示使用对应的镜像,这里会自动从仓库下载,然后启动容器
redis:
  container_name: app_redis
  image: redis:latest

Ainda não acabou, ainda precisamos mapear a porta do projeto SpringBoot e o último arquivo de configuração simples do docker-compose está concluído:

version: "3.9"  #首先是版本号,别乱写,这个是和Docker版本有对应的
services:   #services里面就是我们所有需要进行编排的服务了
  spring:   #服务名称,随便起
    container_name: app_springboot  #一会要创建的容器名称
    build: .
    ports:
    - "8080:8080"
  mysql:
    container_name: app_mysql
    image: mysql/mysql-server:latest
  redis:
    container_name: app_redis
    image: redis:latest

Agora podemos implantar diretamente com um clique. Clicamos no botão de implantação abaixo:

imagem-20220703182541976

imagem-20220703182559020

Ver Running 4/4 significa que a implantação foi bem-sucedida. Vamos ao servidor para ver a situação:

imagem-20220703182657205

Como você pode ver, de fato foram criados 3 containers de acordo com nossa configuração, e todos estão rodando e podem ser acessados ​​normalmente:

imagem-20220703182958392

Se quisermos encerrar, basta clicar em Parar:

imagem-20220703183240400

Claro, se não precisarmos mais deste ambiente, podemos clicar diretamente no botão abaixo para baixar todo o arranjo. Neste caso, os recipientes correspondentes também serão limpos:

imagem-20220703183730693

imagem-20220703183807157

Observe que ao implantar usando docker-compose, uma nova rede personalizada será criada automaticamente e todos os contêineres serão conectados a esta rede personalizada:

imagem-20220703210431690

Esta rede também usa bridge como driver por padrão:

imagem-20220703210531073

Desta forma, concluímos uma configuração simples para implantar todo o nosso ambiente.

Implantar o projeto completo

Anteriormente aprendemos como usá-lo docker-composepara implantação simples, mas simplesmente iniciamos o serviço. Agora vamos conectar esses serviços. O primeiro é o projeto SpringBoot. Primeiro introduzimos dependências:

<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>

<dependency>
   <groupId>mysql</groupId>
   <artifactId>mysql-connector-java</artifactId>
</dependency>

Depois configure a fonte de dados, espere, como saberemos qual é a senha padrão do banco de dados? Então, vamos configurar o serviço MySQL primeiro:

mysql:
  container_name: app_mysql
  image: mysql/mysql-server:latest
  environment:   #这里我们通过环境变量配置MySQL的root账号和密码
    MYSQL_ROOT_HOST: '%'   #登陆的主机,这里直接配置为'%'
    MYSQL_ROOT_PASSWORD: '123456.root'    #MySQL root账号的密码,别设定得太简单了
    MYSQL_DATABASE: 'study'    #在启动时自动创建的数据库
    TZ: 'Asia/Shanghai'    #时区
  ports:
  - "3306:3306"    #把端口暴露出来,当然也可以不暴露,因为默认所有容器使用的是同一个网络

Para configuração detalhada do MySQL, consulte: https://registry.hub.docker.com/_/mysql

Em seguida, concluímos a configuração da fonte de dados:

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://app_mysql:3306/study   #地址直接输入容器名称,会自动进行解析,前面已经讲过了
    username: root
    password: 123456.root

Então vamos escrever um código de teste. Aqui usamos JPA para interação:

<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

<dependency>
   <groupId>org.projectlombok</groupId>
   <artifactId>lombok</artifactId>
</dependency>
@Data
@AllArgsConstructor
@NoArgsConstructor
@Entity
@Table(name = "db_account")
public class Account {
    
    

    @Column(name = "id")
    @Id
    long id;

    @Column(name = "name")
    String name;

    @Column(name = "password")
    String password;
}
@Repository
public interface AccountRepository extends JpaRepository<Account, Long> {
    
    

}
@RestController
public class MainController {
    
    

    @Resource
    AccountRepository repository;

    @RequestMapping("/")
    public String hello(){
    
    
        return "Hello World!";
    }

    @GetMapping("/get")
    public Account get(@RequestParam("id") long id){
    
    
        return repository.findById(id).orElse(null);
    }

    @PostMapping("/post")
    public Account get(@RequestParam("id") long id,
                       @RequestParam("name") String name,
                       @RequestParam("password") String password){
    
    
        return repository.save(new Account(id, name, password));
    }
}

A seguir, vamos modificar o arquivo de configuração:

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://app_mysql:3306/study
    username: root
    password: 123456.root
  jpa:
    database: mysql
    show-sql: true
    hibernate:
      ddl-auto: update   #这里自动执行DDL创建表,全程自动化,尽可能做到开箱即用

Agora que o código está escrito, podemos empacotar o projeto. Preste atenção ao executar nosso comando de empacotamento abaixo e não teste porque ele não consegue se conectar ao banco de dados:

mvn package -DskipTests

Depois de regenerar o pacote jar, modificamos a configuração do docker-compose. Como a velocidade de inicialização do MySQL é relativamente lenta, temos que esperar um pouco até que sua inicialização seja concluída. Se o projeto SpringBoot falhar ao iniciar devido à falha na conexão ao banco de dados, iremos reiniciar:

spring:   #服务名称,随便起
  container_name: app_springboot  #一会要创建的容器名称
  build: .
  ports:
  - "8080:8080"
  depends_on:  #这里设置一下依赖,需要等待mysql启动后才运行,但是没啥用,这个并不是等到启动完成后,而是进程建立就停止等待
  - mysql
  restart: always  #这里配置容器停止后自动重启

Em seguida, excluímos a imagem construída automaticamente anteriormente e aguardamos a reconstrução:

imagem-20220703215050497

Agora vamos reimplantar o docker-compos:

imagem-20220703215133786

Quando todos os três serviços estão azuis, significa que estão funcionando normalmente. Agora vamos testar:

imagem-20220703215211999

A seguir, vamos tentar passar dados para o banco de dados:

[Falha na transferência da imagem do link externo. O site de origem pode ter um mecanismo anti-leeching. Recomenda-se salvar a imagem e carregá-la diretamente (img-DoU5gNOr-1657097647552)(https://s2.loli.net/2022/07 /03/nVEURiAe7qjworl.png )]

imagem-20220703215245757

Você pode ver que a resposta foi bem-sucedida, vamos fazer uma solicitação:

[A transferência da imagem do link externo falhou. O site de origem pode ter um mecanismo anti-leeching. Recomenda-se salvar a imagem e carregá-la diretamente (img-ObN2Plzy-1657097647552)(https://s2.loli.net/2022/ 07/03/uB6rYDCSbLXmOPE.png )]

Desta forma, nosso projeto e o MySQL são basicamente implantados automaticamente.

A seguir, vamos configurar o Redis:

<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

Em seguida, configure as informações de conexão:

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://app_mysql:3306/study
    username: root
    password: 123456.root
  jpa:
    database: mysql
    show-sql: true
    hibernate:
      ddl-auto: update
  redis:
    host: app_redis
//再加两个Redis操作进来
@Resource
StringRedisTemplate template;

@GetMapping("/take")
public String take(@RequestParam("key") String key){
    
    
    return template.opsForValue().get(key);
}

@PostMapping("/put")
public String  put(@RequestParam("key") String key,
                   @RequestParam("value") String value){
    
    
    template.opsForValue().set(key, value);
    return "操作成功!";
}

Finalmente, vamos configurar o arquivo de configuração docker-compose:

redis:
  container_name: app_redis
  image: redis:latest
  ports:
  - "6379:6379"

OK, vamos reimplantá-lo como antes e depois testar:

imagem-20220703220941562

[Falha na transferência da imagem do link externo. O site de origem pode ter um mecanismo anti-leeching. Recomenda-se salvar a imagem e carregá-la diretamente (img-gyIt9iTW-1657097647553)(https://s2.loli.net/2022/07 /03/1SRG8EDtx5Oqr2M.png )]

Desta forma, concluímos a configuração de todo o ambiente + aplicação. Ao implantar todo o projeto, só precisamos utilizar o arquivo de configuração docker-compose para iniciá-lo. Isso facilita muito nossa operação e permite que ele seja utilizado fora de a Caixa. Podemos até usar uma plataforma dedicada para realizar configurações únicas em vários hosts ao mesmo tempo e implantar rapidamente em larga escala, mas deixaremos isso para cursos futuros.

Acho que você gosta

Origin blog.csdn.net/qq_25928447/article/details/125643311
Recomendado
Clasificación