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:
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
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:
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:
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:
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:
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:
É 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.
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:
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:
- O cliente Docker envia a operação ao servidor, informando ao servidor que queremos executar a imagem nginx.
- O servidor Docker primeiro verifica se existe essa imagem localmente e descobre que não existe.
- Então você só pode encontrar e baixar a imagem do repositório público Docker Hub.
- O download é concluído e a imagem é salva localmente com sucesso.
- 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 pull
o comando para baixar diretamente na imagem especificada:
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 repository
nome tag
da repository
imagem . tag
Como 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 run
o 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 create
o comando:
docker create hello-world
Você pode ver que tudo começou com sucesso:
Após a inicialização, um contêiner será criado automaticamente usando a imagem atual. Podemos inserir ps
o comando para visualizar a lista de contêineres do contêiner atual:
docker ps -a
Observe que você precisa adicionar um no final -a
para 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:
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 run
o comando, --name
basta adicionar parâmetros:
docker run --name=lbwnb hello-world
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.
Se quiser parar o container, basta digitar stop
o comando:
docker stop <容器名称/容器ID>
Ou reinicie:
docker restart <容器名称/容器ID>
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>
--rm
Claro, 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 镜像名称
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:
Podemos usar images
o comando para verificar quais espelhos estão disponíveis localmente:
docker images
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
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:
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 bootfs
O 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
,/bin
etc., 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 uname
o comando para visualizar a versão atual do kernel:
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 -it
parâmetros para inicialização, o que -i
significa abrir uma interface de entrada padrão no contêiner, -t
indicando 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.
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:
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 exit
també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 -i
entrada no contêiner para interação, caso contrário ele será executado em segundo plano:
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:
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 commit
comandos 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 yum
o comando (semelhante ao apt) para instalar o ambiente Java. O primeiro é run
o comando:
docker pull ubuntu
Então comece:
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:
Então digite:
apt install openjdk-8-jdk
Aguarde a conclusão da instalação:
Desta forma, concluímos a instalação do ambiente Java, e então podemos sair desta imagem e construí-la como uma nova imagem:
Use commit
o comando para salvar o contêiner como uma nova imagem:
docker commit 容器名称/ID 新的镜像名称
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 Dockerfile
arquivo chamado:
touch Dockerfile
Então vamos editar. Dockerfile
Internamente 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 ubuntu
como imagem base. Claro, se não precisarmos de nenhuma imagem base, nós pode usá-lo diretamente scratch
para 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 RUN
o 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:
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 pull
haverá um mecanismo semelhante quando incluído)
Finalmente, a instalação foi bem-sucedida e aparecerá localmente:
Você pode ver que o tamanho da instalação é o mesmo que fizemos antes, porque as coisas feitas são exatamente as mesmas. Podemos history
visualizar o histórico de construção usando o comando:
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 commit
através do comando não possui este registro:
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:
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.
Clique em Criar Armazém no canto superior direito e preencha as informações:
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.
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:
Após o login bem-sucedido, podemos fazer upload:
docker push nagocoler/ubuntu-java:1.0
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:
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, search
basta usar o comando:
docker search nagocoler/ubuntu-java
Podemos usar o comando pull para baixá-lo:
docker pull nagocoler/ubuntu-java:1.0
A imagem carregada é compactada, portanto o conteúdo baixado é menor. Tente executar:
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.
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" :
[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:
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:
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:
Finalmente construído com sucesso:
Como você pode ver, a imagem que acabamos de construir já existe no servidor Docker:
No entanto, o nome não é especificado. Aqui nós o reconfiguramos:
Reconstrua-o e será nosso nome personalizado:
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:
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.jar
Basta 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:
Você pode ver que novas etapas apareceram no histórico:
Então inicie nossa imagem, podemos operá-la diretamente no IDEA sem digitar comandos, o que é um pouco cansativo:
Após a inicialização, você pode ver as informações de log da inicialização do contêiner à direita:
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 -p
representa 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 -d
plano . Claro, também pode ser configurado diretamente no IDEA:
Após configurar, clique para recriar o container:
Após a nova execução, podemos acessar com êxito o projeto SpringBoot em execução no contêiner:
Claro, para conveniência futura, podemos enviá-lo diretamente para o Docker Hub. Aqui criamos um novo armazém público:
Desta vez usaremos o IDEA para demonstrar como fazer o upload da imagem diretamente, basta clicar:
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)]
OK, a configuração do armazém de espelho remoto está concluída, basta enviá-lo diretamente e aguardar a conclusão do envio.
Você pode ver que nossa imagem apareceu no armazém remoto e também pode vê-la de forma síncrona no IDEA:
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 ls
o comando para visualizar:
docker network ls
Você pode ver que existem bridge
três tipos host
de rede none
por 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
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
--network
os 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
lo
dispositivo de rede de loopback local:Portanto, este contêiner não pode se conectar à Internet:
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
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:
Ao adicionar essa ponte, podemos gerenciar e controlar a rede de contêineres. Podemos usar
network inspect
o comando para visualizar as informações de configuração da ponte docker0:docker network inspect bridge
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:
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:
É 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 create
experimentar usando:
docker network create --driver bridge test
Aqui criamos uma rede bridge chamada test:
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
Aqui criamos um novo contêiner usando esta rede:
docker run -it --network=test ubuntu-net
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:
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/名称
Isso conecta uma nova rede:
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:
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 --ip
especificado 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:
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:
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
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?
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:
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:
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 -p
sã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:
Também podemos entrar diretamente docker ps
para visualizar o mapeamento da porta:
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 test
diretó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 -v
para especificar a montagem do arquivo. Aqui, o diretório de teste que acabamos de criar é montado no caminho /root/test do contêiner.
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
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:
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:
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 -v
nã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:
Após a criação, você pode ver root
que há um novo abc
diretório no diretório, então onde ele está localizado especificamente no host host? Aqui ainda podemos usar inspect
o 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/lib
um 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:
Vamos criar um novo documento de texto:
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:
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
:
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
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-from
especificar 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)
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 -v
cria 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 .
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:
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:
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 log
para imprimir as informações de log no contêiner:
Claro, você também pode adicionar -f
parâmetros para imprimir continuamente informações de registro.
Agora nosso contêiner foi iniciado, mas e se quisermos entrar no contêiner para monitorá-lo? Podemos attach
anexar o comando ao terminal do comando de inicialização do contêiner:
docker attach 容器ID/名称
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.
Depois de sair, o contêiner ainda está em execução.
Também podemos usar exec
comandos para iniciar um novo terminal no contêiner ou executar comandos no contêiner:
docker exec -it test bash
-it
A operação é igual ao run
comando, 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:
Claro, você também pode simplesmente executar um comando no contêiner:
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 stop
paramos 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 kill
o comando diretamente, o que equivale a enviar o sinal SIGKILL ao processo para forçá-lo a terminar.
docker kill test
Comparado com stop
o pedido, kill
nã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 pause
o 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:
Neste momento, a magia do amor está girando em círculos. Podemos restaurá-la ao funcionamento usando unpause
o 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 -m
parâmetro é o limite de uso da memória física do contêiner, --memory-swap
mas 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 -m
dobro 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:
É 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 -c
opçõ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 stress
as 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):
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-cpus
A 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:
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:
Claro, você também pode usá-lo diretamente --cpus
para limitar o número de recursos da CPU usados:
docker run -it --cpus=1 ubuntu
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 dd
o 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:
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-bps
e .--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:10MB
a velocidade de gravação em /dev/sda a apenas 10 MB/s. Vamos testá-lo:
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 stats
o comando para monitorar:
docker stats
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 stats
comandos para monitorar a situação em tempo real, você também pode utilizar top
comandos para visualizar os processos no contêiner:
docker top 容器ID/名称
Claro, você também pode carregar alguns parâmetros, os parâmetros específicos ps
sã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.
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:
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:
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 version
para 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:
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.
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:
Ver Running 4/4 significa que a implantação foi bem-sucedida. Vamos ao servidor para ver a situação:
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:
Se quisermos encerrar, basta clicar em Parar:
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:
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:
Esta rede também usa bridge como driver por padrão:
Desta forma, concluímos uma configuração simples para implantar todo o nosso ambiente.
Implantar o projeto completo
Anteriormente aprendemos como usá-lo docker-compose
para 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:
Agora vamos reimplantar o docker-compos:
Quando todos os três serviços estão azuis, significa que estão funcionando normalmente. Agora vamos testar:
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 )]
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:
[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.