Notas sobre tecnología de contenedores Docker

imagen-20220629215534772

Tecnología de contenedores Docker

Docker es una tecnología a nivel de plataforma que cubre una amplia gama, por lo que antes de comenzar, asegúrese de haber completado: el capítulo de Java SpringBoot (se recomienda completar el capítulo de SpringCloud antes de comenzar), el video tutorial y todas las rutas anteriores; ser muy dificil Además, conocimientos adicionales recomendados: conocimientos relacionados con "Redes informáticas" y "Sistemas operativos". Aprender algo no se puede hacer enteramente de memoria, sino que debe entenderse en combinación con los conocimientos básicos que se han aprendido. En términos generales, las cosas que se pueden dominar únicamente con la memoria suelen ser las más baratas.

**Sitio web oficial de Docker: **https://www.docker.com

**Preparación antes de la clase: **Configurar un servidor Linux de 2C2G o superior, ya sea servidor en la nube o máquina virtual.

Introducción a la tecnología de contenedores

Con el desarrollo de los tiempos, Docker ha ido entrando paulatinamente en la etapa de la historia: en el pasado, si quisiéramos instalar un entorno, nos tomaría una tarde o incluso un día entero configurar e instalar varias partes (como ejecutar el nuestro). Aplicación SpringBoot, que puede requerir Instalar bases de datos, instalar Redis, instalar MQ, etc. Se necesita mucho tiempo solo para instalar varios entornos, lo que realmente hace que la mentalidad explote) Con Docker, la implementación de nuestro programa y entorno se ha vuelto muy fácil. , solo necesitamos empaquetar estos entornos en una sola imagen. Al implementar en el servidor, puede descargar la imagen directamente para implementarla con un solo clic, ¿no es muy conveniente?

Incluyendo los diversos componentes que necesitamos configurar al aprender Spring Cloud, podemos encontrar varios problemas al ejecutar en el entorno de nuestra propia computadora (es posible que no se pueda ejecutar porque los distintos entornos en la computadora no están configurados), pero ahora solo necesitamos descargar La imagen se puede ejecutar directamente, todos los entornos están configurados en la imagen y se pueden usar de inmediato.

¿Es realmente tan mágico? Hagamos un intento.

Instalación e implementación del entorno.

En primer lugar, primero debemos configurar el entorno Docker (se recomienda usar el mismo entorno que yo, de lo contrario, si algo sale mal, tendrá que encontrar una solución usted mismo). Aquí usamos:

  • Sistema operativo Ubuntu 22.04

Docker se divide en la versión comunitaria gratuita CE (Community Edition) y la versión paga de nivel empresarial EE (Enterprise Edition), por lo que elegimos docker-ce para la instalación aquí. Documentación oficial de instalación: https://docs.docker.com/engine/install/ubuntu/

Primero instale algunas herramientas:

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

Sin embargo, se instaló de forma predeterminada en Ubuntu22.04. Luego instale la clave 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, agregue la biblioteca Docker a la lista de recursos aptos:

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

Luego actualizamos apt:

 sudo apt update

Finalmente instale la versión Docker CE:

 sudo apt install docker-ce

Solo espera a que se complete la instalación:

imagen-20220630161240162

imagen-20220630161341541

Puede ver que la versión después de una instalación exitosa es 20.10.17. Por supuesto, puede ser una versión más nueva cuando la instaló. Finalmente, agregamos el usuario actual al grupo de usuarios de Docker, de lo contrario, se requiere la ejecución de sudo cada vez que se usa el comando Docker, lo cual es muy problemático:

sudo usermod -aG docker <用户名>

Una vez completada la configuración, primero salimos del terminal SSH y luego nos volvemos a conectar para que surta efecto.

De esta forma ya tenemos configurado nuestro entorno de aprendizaje Docker, ahora intentaremos implementar un servidor Nginx a través de Docker, es muy sencillo de usar y solo requiere un comando (claro, no importa si no lo entiendes). ahora, lo explicaremos más adelante):

sudo docker run -d -p 80:80 nginx

imagen-20220630165259663

La primera opción es descargar la imagen correspondiente del almacén de imágenes, la velocidad de acceso nacional es buena y no es necesario configurar la fuente de la imagen por separado. Una vez completada la descarga, se ejecutará en segundo plano, podemos probarlo usando un navegador:

imagen-20220630165430159

imagen-20220630165440751

Como puede ver, el servidor Nginx se implementó con éxito, pero de hecho no instalamos Nginx en Ubuntu, sino que construimos el servidor a través de la imagen que ejecuta Docker. ¿No parece la jugabilidad bastante novedosa? Además de aplicaciones sencillas como Nginx, también podemos implementar aplicaciones complejas a través de Docker, que explicaremos una a una más adelante.

De máquinas virtuales a contenedores

Anteriormente instalamos con éxito el entorno de aprendizaje de Docker y probamos brevemente la rápida implementación de aplicaciones que nos trajo Docker. Antes de ingresar oficialmente al estudio, comencemos con el desarrollo de Docker.

Antes de la aparición de Docker, se podía decir que la tecnología de virtualización dominaba. Primero, hablemos de por qué aparece la tecnología de virtualización. Sabemos que los servidores son una instalación de hardware indispensable en las empresas. Los servidores también son computadoras, pero a diferencia de nuestras computadoras domésticas, las configuraciones del servidor son muy altas. La CPU de nuestra computadora doméstica puede tener solo 20 núcleos como máximo, y hay pocas computadoras con más de 128 G de memoria. Una computadora doméstica con 64 G de memoria puede considerarse un lujo. Los servidores son diferentes. Las CPU a nivel de servidor suelen tener 12 núcleos. Los servidores pueden incluso estar equipados con varias CPU al mismo tiempo, que pueden apilar directamente hasta docenas de núcleos:

imagen-20220630171220207

Nuestras CPU domésticas son generalmente la serie Ryzen de AMD y la serie Core de Intel (como i3 i5 i7 i9), mientras que las CPU de servidor son generalmente la serie Xeno de Intel. La característica de este tipo de CPU es que tiene una gran cantidad de núcleos:

imagen-20220630172135408

Además, las CPU de los servidores consumen más energía que las CPU domésticas, por lo que las CPU de los servidores generan mucho calor. Si tiene la suerte de haber estado en la sala de ordenadores, oirá el sonido del ventilador de refrigeración girando violentamente (pero la frecuencia de las CPU de los servidores (no es tan alto como el de las CPU domésticas). Alto. Generalmente, los juegos a gran escala requieren una alta frecuencia en lugar de un número de núcleos, y el consumo de energía es relativamente grande, por lo que no es adecuado para computadoras domésticas. Por lo tanto, al comprar una computadora de escritorio en línea en el futuro, si ve alguna CPU de "nivel i9", no la compre. Son estos comerciantes sin escrúpulos quienes instalan CPU de servidor (basura extranjera) eliminadas de servidores extranjeros en las computadoras y se las venden. , por lo que será muy económico y la cantidad de núcleos es comparable a la del i9, por lo que aún obtendrás lo que pagas)

Los servidores tienen muchos más recursos de CPU y memoria que las computadoras domésticas, y los proyectos back-end de Java que escribimos eventualmente se ejecutarán en estos servidores. Sin embargo, hay un problema. Dado que los servidores tienen recursos de hardware tan abundantes, ejecutaremos este. ¿El pequeño backend de Java se siente como una bomba nuclear que hace estallar mosquitos? Puede que sólo utilice el 5% de los recursos de hardware del servidor como máximo, lo que sería un desperdicio ejecutar un servidor tan potente.

Por lo tanto, para resolver esta situación en la que la tasa de utilización de recursos es solo del 5% al ​​15%, ¿podemos pensar en una manera de dividir este servidor en varios servidores pequeños para su uso, y cada servidor pequeño solo asigna una parte de los recursos? como dividir un El servidor pequeño solo tiene 2 núcleos de CPU y 4G de memoria. Pero debido a problemas de diseño, nuestras computadoras solo pueden ejecutar un sistema operativo al mismo tiempo, entonces, ¿qué debemos hacer? En esta época la tecnología de virtualización comenzó a auge.

La virtualización utiliza software para emular hardware y crear sistemas informáticos virtuales. Esto permite a las empresas ejecutar múltiples sistemas virtuales (es decir, múltiples sistemas operativos y aplicaciones) en un solo servidor, lo que puede generar economías de escala y eficiencias mejoradas. Por ejemplo, VMware, que se utiliza a menudo en nuestras computadoras, es un software de virtualización de nivel civil:

imagen-20220630173915254

Podemos usar VMware para crear máquinas virtuales. Estas máquinas virtuales en realidad se ejecutan en función del software VMware en nuestro sistema actual. Por supuesto, VMware también tiene un software de virtualización específico del servidor. Con la virtualización, nuestros servidores se ven así:

imagen-20220630174945749

Equivale a simular muchas computadoras a través de máquinas virtuales, de modo que podamos instalar el sistema e implementar nuestras aplicaciones en múltiples máquinas virtuales divididas respectivamente, y podamos asignar libremente recursos de hardware y usarlos de manera racional. Generalmente en las empresas se pueden implementar diferentes aplicaciones en varios servidores y aislarlas, en este caso el uso de máquinas virtuales es muy adecuado.

De hecho, los servidores en la nube que alquilamos de Tencent Cloud y Alibaba Cloud son solo máquinas virtuales divididas por tecnología de virtualización.

Entonces, ahora que las máquinas virtuales son tan convenientes, ¿cómo encuentran una salida los contenedores? Primero echemos un vistazo a qué es un contenedor.

Los contenedores y las máquinas virtuales son similares. Ambos pueden proporcionar encapsulación y aislamiento para aplicaciones. Ambos son software. Sin embargo, la aplicación que se ejecuta en el contenedor depende del sistema operativo host. De hecho, todavía utiliza directamente los recursos del sistema operativo. Por supuesto, la aplicación El entorno físico todavía está aislado, mientras que la máquina virtual simula completamente una computadora real, que son directamente dos computadoras diferentes.

imagen-20220630181037698

Por lo tanto, los contenedores son mucho más simples que las máquinas virtuales, la velocidad de inicio será mucho más rápida y la sobrecarga será mucho menor.

Sin embargo, la causa principal del incendio de contenedores es su idea de contenedor. Sabemos que si queremos escribir un proyecto Java, como un foro o un comercio electrónico, entonces el middleware como la base de datos, la cola de mensajes y el caché son esenciales, por lo que si Si desea implementar un servicio en el servidor, en realidad necesita preparar varios entornos con anticipación. Primero instale MySQL, Redis, RabbitMQ y otras aplicaciones, configure el entorno y luego inicie nuestra aplicación Java. Después de todo el proceso, muchos Se pierde tiempo simplemente configurando el entorno. Si se trata de un proyecto distribuido a gran escala, es posible que sea necesario implementar muchas máquinas, entonces, ¿no tenemos que hacerlo una por una? Se necesitan varios días para poner el proyecto en funcionamiento, lo que obviamente es ridículo.

Los contenedores pueden empaquetar todo el entorno. MySQL, Redis, etc. y nuestras aplicaciones Java se pueden empaquetar juntas como un espejo. Cuando necesitamos implementar servicios, solo necesitamos descargar directamente el espejo y ejecutarlo como lo hicimos antes. No es necesario No se requiere configuración adicional. El entorno en toda la imagen ya está configurado y listo para usar desde el primer momento.

[Error en la transferencia de la imagen del enlace externo. El sitio de origen puede tener un mecanismo anti-leeching. Se recomienda guardar la imagen y cargarla directamente (img-4urSvvtK-1657097647526)(https://s2.loli.net/2022/06 /30/NTnU8iSj51CspFw.png )]

En lo que queremos centrarnos es en Docker. Puedes ver que su icono es una ballena. Encima de la ballena hay muchos contenedores. Cada contenedor es nuestro entorno completo + aplicación. Docker puede empaquetar cualquier aplicación y sus dependencias. Un sistema liviano, portátil y autónomo. -Contenedor contenido que puede ejecutarse en casi cualquier sistema operativo.

Una breve introducción al mecanismo de funcionamiento de los contenedores.

Primero echemos un vistazo a la arquitectura general de Docker:

imagen-20220630184857540

En realidad, está dividido en tres partes:

  • Cliente Docker: todos los comandos de Docker que usamos antes se ejecutan en el cliente y las operaciones se enviarán al servidor para su procesamiento.
  • Servidor Docker: el servidor es el cuerpo principal que inicia el contenedor, generalmente se ejecuta en segundo plano como un servicio y admite conexiones remotas.
  • Registro: Es un almacén que almacena imágenes de Docker, al igual que Maven, también se puede dividir en almacenes públicos y privados, las imágenes se pueden descargar desde el almacén y almacenar localmente.

Cuando necesitamos implementar una aplicación y un entorno empaquetados en el servidor, solo necesitamos descargar la imagen empaquetada, que ejecutamos anteriormente:

sudo docker run -d -p 80:80 nginx

De hecho, después de ingresar este comando:

  1. El cliente Docker envía la operación al servidor y le dice que queremos ejecutar la imagen nginx.
  2. El servidor Docker primero verifica si existe esta imagen localmente y descubre que no la hay.
  3. Entonces solo podrá buscar y descargar la imagen desde el repositorio público Docker Hub.
  4. La descarga se completa y la imagen se guarda exitosamente localmente.
  5. El servidor Docker carga la imagen de Nginx, inicia el contenedor y comienza a ejecutarse normalmente (tenga en cuenta que el contenedor está aislado de otros contenedores y del exterior y no se afecta entre sí)

Por lo tanto, en todo el proceso, Docker es como un barco de transporte y el espejo es como un contenedor. El barco de transporte transporta mercancías de todo el mundo a nuestros puertos. Una vez que las mercancías llegan al puerto, a Docker no le importa lo que sea. dentro del contenedor Simplemente cree el contenedor y funcionará fuera de la caja. En comparación con nuestro entorno tradicional de instalación y configuración manual, no sé cuántos niveles de conveniencia tiene.

Sin embargo, los contenedores aún dependen de la ejecución del host, por lo que generalmente en un entorno de producción, primero se crean varios hosts mediante la virtualización y luego se implementa Docker en cada máquina virtual, lo que mejora enormemente la eficiencia de operación y mantenimiento. . .

A partir del próximo capítulo, aprenderemos formalmente las diversas operaciones de Docker.


Contenedores e imágenes

Lo más importante para iniciar un contenedor es la imagen, echemos un vistazo a la introducción de la imagen.

Primera introducción a las imágenes de contenedores.

Primero, aprendamos sobre las operaciones relacionadas con la imagen. Por ejemplo, ahora queremos descargar una imagen del almacén al local. Aquí usamos la imagen oficial de hello-world:

docker pull hello-world

Simplemente ingrese pullel comando para descargar directamente a la imagen especificada:

imagen-20220701111043417

Puede ver que hay una oración "Usar etiqueta predeterminada" en la línea anterior. De hecho, el nombre de una imagen se compone de dos partes, una es y la otra es. En circunstancias normales, la convención es el nombre de repositoryla tagimagen repository. Como tagversión, la predeterminada es la última, lo que significa la última versión. Entonces, si ejecuta la versión especificada:

docker pull 名称:版本

Más adelante, para facilitar la enseñanza, usaremos directamente la etiqueta predeterminada sin especificar la versión.

Después de descargar la imagen, se almacenará localmente. Para iniciar el contenedor de esta imagen, simplemente ingrese runel comando tal como lo hicimos antes:

docker run hello-world

Por supuesto, si solo desea crearlo pero no desea ejecutarlo inmediatamente, puede usar createel comando:

docker create hello-world

Puedes ver que se inició correctamente:

imagen-20220701111314331

Después del inicio, se creará automáticamente un contenedor usando la imagen actual. Podemos ingresar psel comando para ver la lista de contenedores del contenedor actual:

docker ps -a

Tenga en cuenta que debe agregar uno al final -apara ver todos los contenedores (se pueden ver otras opciones usando -h). De lo contrario, solo se mostrarán los contenedores actualmente en ejecución y HelloWorld es de un solo uso y no es residente. programa como Nginx, por lo que el contenedor se inicia. Después de imprimir el contenido anterior, el contenedor dejó de ejecutarse:

imagen-20220701111840091

Puede ver que la lista de contenedores incluye el hello-world que acabamos de crear y el nginx que creamos antes (tenga en cuenta que la misma imagen puede crear varios contenedores). Cada contenedor tiene una ID de contenedor generada aleatoriamente escrita en el frente, seguida de La creación hora y estado de ejecución actual del contenedor. La última columna es el nombre del contenedor. Al crear el contenedor, el nombre puede ser especificado por nosotros o generado automáticamente. Aquí se genera automáticamente.

Podemos especificar manualmente el nombre para comenzar. Cuando use runel comando, --namesimplemente agregue parámetros:

docker run --name=lbwnb hello-world

imagen-20220701125951980

Podemos iniciar manualmente un contenedor detenido:

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

Tenga en cuenta que debemos completar el ID o el nombre del contenedor para que se inicie el objeto. El ID del contenedor es relativamente largo, por lo que no necesita escribir todo, sino solo la mitad, pero debe asegurarse que el ID del contenedor incompleto que ingresa es único.

imagen-20220701124845982

Si desea detener el contenedor, simplemente ingrese stopel comando:

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

O reiniciar:

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

imagen-20220701125025173

Si ya no necesitamos usar el contenedor, podemos eliminarlo, pero tenga en cuenta que solo se puede eliminar cuando el contenedor no se está ejecutando:

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

--rmPor supuesto, si queremos que el contenedor se elimine automáticamente después de detenerse, podemos agregar parámetros en tiempo de ejecución :

docker run --rm 镜像名称

imagen-20220701125108834

Después de la eliminación, el contenedor ya no existirá. Cuando no hay un contenedor para nginx, podemos eliminar la imagen local de nginx:

imagen-20220701125204728

Podemos usar imagesel comando para verificar qué espejos están actualmente disponibles localmente:

docker images

imagen-20220701125514145

Hasta ahora hemos entendido el uso simple de Docker, en estudios posteriores continuaremos aprendiendo más formas de jugar.

Introducción a la estructura espejo.

Anteriormente aprendimos sobre las operaciones básicas de Docker. De hecho, la piedra angular del contenedor es la imagen. Solo con la imagen podemos crear la instancia de contenedor correspondiente. Así que comencemos con la estructura básica de la imagen. Echemos un vistazo a lo que es la imagen La presencia.

Cuando empaquetamos un proyecto, a menudo necesitamos un entorno de sistema operativo básico para que podamos instalar varios software dependientes en este sistema operativo, como bases de datos, cachés, etc. Una imagen básica del sistema como esta se llama base Mirroring, nuestros proyectos En el futuro, se empaquetará en función de la imagen base. Por supuesto, no hay necesidad de una imagen base. Simplemente se basa en el sistema operativo actual para ejecutar comandos simples, como el hello-world que usamos antes.

Generalmente la imagen base es la versión de distribución de cada sistema operativo Linux, como Ubuntu que estemos usando, CentOS, Kali, etc. Aquí descargamos la imagen base de CentOS:

docker pull centos

imagen-20220701132622893

Como puede ver, la imagen base de CentOS ha sido descargada. A diferencia de cuando usamos un sistema completo, la imagen base de CentOS omite el kernel, por lo que el tamaño es de solo 272 M. Aquí debemos explicar el mecanismo de la imagen base:

imagen-20220701133111829

El sistema operativo Linux consta de espacio del kernel y espacio de usuario. El espacio del kernel es el núcleo de todo el sistema Linux. Después de que Linux se inicie, primero agregará el sistema de archivos. Una vez completada la carga, lo desinstalará automáticamente. Luego, el bootfsSe cargará el sistema de archivos del espacio de usuario. Esta capa Esta es la parte que podemos operar nosotros mismos:

  • bootfs contiene BootLoader y el kernel de Linux. Los usuarios no pueden realizar ninguna modificación en esta capa. Una vez iniciado el kernel, bootfs se desinstalará automáticamente.
  • rootfs contiene las estructuras de directorios comunes en el sistema, incluidos /dev, /proc, /binetc., así como algunos archivos y comandos básicos, que es el sistema de archivos completo que podemos operar después de ingresar al sistema, incluido apt que usamos en Ubuntu y CentOS. yum es todo en el espacio del usuario.

La capa inferior de la imagen base usará directamente el kernel del host, es decir, cualquiera que sea su versión del kernel de Ubuntu, entonces la versión del kernel de CentOS en la imagen base será, y rootfs puede ejecutar múltiples versiones diferentes en diferentes contenedores. Por lo tanto, la imagen base en realidad solo tiene los rootfs de CentOS, por lo que solo tiene un tamaño de aproximadamente 300 M. Por supuesto, CentOS contiene una variedad de software básico, que está bastante inflado, y la imagen base de algunos sistemas operativos es incluso menor que 10M.

Utilice unameel comando para ver la versión actual del kernel:

imagen-20220701135056123

Por tanto, no es de extrañar que Docker pueda simular múltiples entornos de sistema operativo Linux al mismo tiempo, podemos intentar iniciar la imagen base que acabamos de descargar:

docker run -it centos

Tenga en cuenta que debe agregar -itparámetros para el inicio, lo que -isignifica abrir una interfaz de entrada estándar en el contenedor, -tlo que indica la asignación de un dispositivo pseudo-tty, que puede admitir el inicio de sesión del terminal. Generalmente, estos dos se usan juntos; de lo contrario, el contenedor base se detendrá automáticamente. después de comenzar.

imagen-20220701135834325

Puede ver que puede usar el comando ls para ver todos los archivos en el directorio raíz, pero muchos comandos no están disponibles, ni siquiera claros, echemos un vistazo a la versión del kernel:

imagen-20220701140018095

Puede ver que la versión del kernel es la misma (esta también es la desventaja. Si el software tiene requisitos para la versión del kernel, entonces use Docker para enviarlo directamente). Podemos salir de la terminal del contenedor escribiendo. Puede ver que el El contenedor exittambién se detuvo:

[Error en la transferencia de la imagen del enlace externo. El sitio de origen puede tener un mecanismo anti-leeching. Se recomienda guardar la imagen y cargarla directamente (img-T9dw38Wp-1657097647529)(https://s2.loli.net/2022/07 /01/u5MQnWVihlbkyx1.png )]

Por supuesto, también podemos iniciarlo nuevamente, tenga en cuenta que debe agregarse al comenzar -ia ingresar al contenedor para la interacción, de lo contrario se ejecutará en segundo plano:

imagen-20220701140706977

Según la imagen base, podemos instalar varios programas sobre esta base. Casi todas las imágenes se crean instalando y configurando el software requerido sobre la base de la imagen base:

imagen-20220701143105247

Cada vez que se instala una pieza de software, se coloca sobre la imagen base, utilizando una estructura en capas para que múltiples contenedores puedan ensamblar libremente estas diferentes capas. Por ejemplo, ahora varios contenedores necesitan usar CentOS. La imagen base, y El software que se ejecuta en él es diferente, la estructura jerárquica es muy buena en este momento. Solo necesitamos guardar una imagen base localmente y puede ser ensamblada y utilizada por múltiples contenedores diferentes. ¿No se siente muy flexible?

Vemos que además de este software, también hay una capa contenedora de escritura en la parte superior. ¿Para qué sirve esto? ¿Por qué se coloca en la parte superior?

Sabemos que todos los espejos se apilarán para formar un sistema de archivos unificado. Si hay archivos en la misma ubicación en diferentes capas, entonces los archivos de la capa superior sobrescribirán los archivos de la capa inferior. Al final, lo que Ver es un sistema de archivos superpuesto. Cuando necesitamos modificar los archivos en el contenedor, en realidad no modificamos la imagen directamente, sino que la modificamos en la capa superior del contenedor (la superior generalmente se llama capa contenedora y las siguientes son todas capas espejo). siguiente imagen; de lo contrario, será difícil compartir la imagen con varios contenedores. Entonces cada operación es la siguiente:

  • Lectura de archivos: para leer un archivo, Docker buscará de arriba a abajo y luego abrirá el archivo cuando lo encuentre.
  • Creación y modificación de archivos: la creación de nuevos archivos se agregará directamente a la capa contenedora. La modificación de archivos buscará archivos en cada imagen de arriba a abajo. Si los encuentra, se copiarán a la capa contenedora y luego se modificarán.
  • Eliminar archivos: eliminar archivos también buscará archivos en cada imagen de arriba a abajo. Una vez encontrados, los archivos de la imagen no se eliminarán directamente, pero la operación de eliminación se marcará en la capa contenedora.

En otras palabras, la mayoría de las operaciones que realizamos en los archivos de todo el contenedor se realizan en la capa superior del contenedor. No podemos interferir con todos los archivos de la capa de imagen que se encuentran debajo, lo que protege bien la integridad de la imagen. Sólo así podemos Se pueden compartir y utilizar varios contenedores.

Construir imagen

Ya hemos entendido la estructura de las imágenes de Docker. De hecho, todas las aplicaciones de uso común tienen imágenes correspondientes. Solo necesitamos descargar estas imágenes y luego usarlas. No necesitamos instalarlas manualmente. Como máximo, necesitamos realizar algunas operaciones especiales configuración. Por supuesto, si encuentra algunas aplicaciones impopulares, es posible que no se proporcione la imagen. En este caso, debemos instalarla manualmente. Luego veremos cómo crear nuestra propia imagen de Docker. Hay dos formas de crear una imagen, una es usar commitcomandos y la otra es usar Dockerfile, veamos la primera primero.

Aquí tomaremos un ejemplo simple. Por ejemplo, ahora queremos instalar el entorno Java en la imagen base de Ubuntu y empaquetarlo como una nueva imagen (esta nueva imagen es una imagen del sistema Ubuntu que contiene el entorno Java).

Primero iniciamos la imagen de Ubuntu y luego usamos yumel comando (similar a apt) para instalar el entorno Java. El primero es runel comando:

docker pull ubuntu

imagen-20220701151405640

Entonces empezar:

imagen-20220701151433520

Utilice directamente el comando apt para instalar el entorno Java. Actualícelo antes de hacer esto. Debido a que es una instalación mínima, no hay paquetes de software locales:

imagen-20220701151600847

Luego ingresa:

apt install openjdk-8-jdk

Espere a que se complete la instalación:

imagen-20220701152018041

De esta manera, hemos completado la instalación del entorno Java y luego podemos salir de esta imagen y construirla como una nueva imagen:

imagen-20220701152130041

Utilice commitel comando para guardar el contenedor como una nueva imagen:

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

imagen-20220701152302171

imagen-20220701152418060

Puede ver que el tamaño de la imagen después de instalar el software es mucho mayor que nuestro tamaño original, por lo que podemos iniciar directamente un contenedor del sistema operativo Ubuntu con un entorno Java a través de esta imagen. Sin embargo, aunque este método es altamente personalizado, Docker no lo recomienda oficialmente. En este caso, el usuario no sabe cómo se construye la imagen o si tiene una puerta trasera, y la eficiencia de construcción es demasiado baja. Si quieres crear imágenes para varios sistemas operativos al mismo tiempo, ¿no tienes que crearlas una por una? Como usuarios normales, sería mejor para nosotros utilizar Dokcerfile.

Veamos cómo usar Dockerfile para crear una imagen del sistema Ubuntu con un entorno Java. Primero, cree un nuevo Dockerfilearchivo llamado:

touch Dockerfile

Luego editemos. DockerfileInternamente necesitamos escribir una variedad de instrucciones para decirle a Docker la información relevante sobre nuestra imagen:

FROM <基础镜像>

Primero, necesitamos usar la instrucción FROM para seleccionar la imagen base de la imagen actual (debe comenzar con esta instrucción). Aquí podemos usarla directamente ubuntucomo imagen base. Por supuesto, si no necesitamos ninguna imagen base, Puede usarlo directamente scratchpara construir desde cero. Aquí no hay más demostraciones.

Una vez completada la configuración básica de la imagen, debemos ejecutar el comando en el contenedor para instalar el entorno Java, aquí debemos usar RUNel comando:

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

Después de ejecutar cada instrucción, se genera una nueva capa de imagen.

Bien, ahora nuestro Dockerfile está terminado y solo necesitamos completar la compilación una vez:

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

Después de la ejecución, Docker buscará el archivo Dockerfile en el directorio de compilación y luego comenzará a ejecutar las instrucciones en el Dockerfile en secuencia:

imagen-20220701155443170

Cada paso del proceso de compilación se enumera muy claramente. Hay tres instrucciones en total correspondientes a los tres pasos. Esperamos un momento para instalar. Toda la información de registro durante el proceso de instalación se imprimirá directamente en la consola (tenga en cuenta que Docker La creación de la imagen se almacena en caché. Mecanismo, incluso si sale a la mitad y luego vuelve a compilar, cada capa de la imagen que se haya creado antes se usará directamente. A menos que el archivo Dockerfile se modifique y reconstruya, siempre que una determinada capa cambie su parte superior. capa El caché de compilación será invalidado, por supuesto, pullhabrá un mecanismo similar cuando se incluya)

imagen-20220701155812315

Finalmente la instalación es exitosa y aparecerá localmente:

imagen-20220701155847721

Puedes ver que el tamaño de la instalación es el mismo que hicimos antes, porque las cosas hechas son exactamente iguales. Podemos historyver el historial de compilación usando el comando:

imagen-20220701160128689

Puede ver que las dos capas superiores son el contenido que generamos usando el comando apt, que se usan directamente como las dos capas de espejos en el espejo actual. Cada espejo tiene su propia ID y diferentes espejos tienen diferentes tamaños. La imagen que generamos manualmente commitmediante el comando no tiene este registro:

imagen-20220701160406891

Si encuentra una imagen cuyo ID de imagen falta, generalmente es una imagen descargada de Docker Hub. Este problema ocurrirá, pero no es un gran problema. Utilice la imagen que creamos para crear un contenedor y podrá experimentar directamente el contenedor con el entorno Java:

imagen-20220701161546279

Aprenderemos gradualmente sobre otros comandos de Dockerfile en el estudio posterior.

Publicar la imagen en el repositorio remoto.

Anteriormente aprendimos cómo crear una imagen de Docker. Podemos publicar nuestra propia imagen en Docker Hub. Al igual que un repositorio remoto de Git, podemos cargar nuestra propia imagen aquí: https://hub.docker.com/repositories, si no No tienes una cuenta, regístrate primero.

imagen-20220701164609666

Haga clic en Crear almacén en la esquina superior derecha y complete la información:

imagen-20220701164939268

Una vez completada la creación, tenemos un almacén de imágenes público. Podemos cargar la imagen local. Antes de cargarla, debemos modificar el nombre de la imagen para que esté más estandarizado. Aquí usamos el comando para volver a etiquetar tag:

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

Aquí cambiamos la versión a la versión 1.0 en lugar de la última versión predeterminada.

imagen-20220701165231001

Una vez completada la modificación, se creará una nueva imagen local con el nombre que definimos nosotros mismos. Luego debemos iniciar sesión localmente:

imagen-20220701165446859

Después de iniciar sesión correctamente, podemos cargar:

docker push nagocoler/ubuntu-java:1.0

imagen-20220701165744647

Jaja, todavía es un poco estresante subir 500 millones de cosas, si es demasiado lento, puedes volver a hacer un espejo más simple. Una vez completada la carga, abra el almacén y podrá ver que ya existe la versión 1.0:

imagen-20220701165920060

imagen-20220701170053250

Tenga en cuenta que se puede buscar y descargar en el repositorio público, por lo que aquí eliminamos todas las imágenes locales y descargamos la imagen que acabamos de cargar. Aquí buscamos primero, searchsolo use el comando:

docker search nagocoler/ubuntu-java

imagen-20220701170253126

Podemos usar el comando pull para descargarlo:

docker pull nagocoler/ubuntu-java:1.0

imagen-20220701171148334

La imagen cargada está comprimida, por lo que el contenido descargado es más pequeño. Intenta ejecutar:

imagen-20220701171253440

Por supuesto, también puedes pedirles a tus compañeros que intenten descargar tu propia imagen en otras máquinas para ver si funciona normalmente.

Docker Hub también puede construir servidores privados por sí solo, pero no entraré en detalles aquí. Hasta ahora, se han explicado algunas operaciones básicas sobre contenedores y espejos.

Combate práctico: use IDEA para crear la imagen del programa SpringBoot

Aquí creamos un nuevo proyecto SpringBoot. Ahora esperamos usar Docker para implementar rápidamente nuestro proyecto SpringBoot en el servidor donde está instalado Docker. Podemos empaquetarlo como una imagen de Docker.

imagen-20220701173902376

Primero cree un proyecto y déjelo ejecutar. Si puede ejecutarse normalmente, no habrá ningún problema. Luego debemos empaquetarlo como una imagen de Docker. Cree un nuevo Dockerfile aquí:

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

Primero, construimos una imagen del sistema con un entorno Java basado en Ubuntu. Luego, primero lo conectamos a nuestro servidor Docker para su construcción. Dado que IDEA viene con su propio complemento Docker, hacemos clic directamente en el botón Ejecutar en la esquina superior izquierda y seleccione la segunda opción "Crear imagen para Dockerfile" :

imagen-20220701203741495

[Error en la transferencia de la imagen del enlace externo. El sitio de origen puede tener un mecanismo anti-leeching. Se recomienda guardar la imagen y cargarla directamente (img-jVuXrtKl-1657097647534)(https://s2.loli.net/2022/07 /01/FAcME5yxZPD1aoz.png )]

Aquí necesitamos configurar el servidor Docker, que es el Docker que instalamos en el servidor Ubuntu. Aquí completamos la información relacionada con el servidor. Nuestra primera opción es modificar algunas configuraciones de Docker y habilitar el acceso remoto del cliente:

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

Una vez abierto, agregue aspectos destacados:

imagen-20220701202846707

Una vez completada la modificación, reinicie el servicio Docker, si es un servidor en la nube, recuerde abrir el puerto de conexión TCP 2375:

sudo systemctl daemon-reload
sudo systemctl restart docker.service 

Ahora procede a configurarlo en IDEA:

imagen-20220701203318098

Complete la dirección IP de nuestro servidor Docker en la URL de la API del motor:

tcp://IP:2375

Después de que se muestre la conexión exitosa, significa que la configuración es correcta, haga clic en Guardar y luego comience a construir en nuestro servidor Docker:

imagen-20220701203518930

Finalmente construido con éxito:

imagen-20220701204815069

Como puede ver, la imagen que acabamos de crear ya existe en el servidor Docker:

imagen-20220701204900943

Sin embargo no se especifica el nombre, aquí lo reconfiguramos:

imagen-20220701204955570

imagen-20220701205053642

Reconstrúyelo y será nuestro nombre personalizado:

imagen-20220701205402607

imagen-20220701205350004

Creemos un contenedor y probémoslo:

[La transferencia de la imagen del enlace externo falló. El sitio de origen puede tener un mecanismo anti-leeching. Se recomienda guardar la imagen y cargarla directamente (img-Wzeb7k1S-1657097647536)(https://s2.loli.net/2022/ 01/07/8xPUg7qmVzXF9nN.png )]

Bien, ahora que el entorno básico está configurado, necesitamos empaquetar nuestro proyecto SpringBoot y ejecutarlo cuando se inicie el contenedor. Abra Maven y ejecute el comando de empaquetado:

imagen-20220701205630885

Luego necesitamos editar el Dockerfile y colocar el paquete jar que creamos en él:

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

Aquí debe usar el comando COPY para copiar el archivo a la imagen. El primer parámetro es el archivo local que queremos copiar. El segundo parámetro es la ubicación del archivo almacenado en la imagen de Docker. Como aún no hemos aprendido a administrar el almacenamiento, Lo ingresamos directamente aquí, app.jarsimplemente guárdelo directamente en la ruta predeterminada.

Luego debemos especificar ejecutar nuestro programa Java al inicio, esto se hace usando el 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

El comando CMD puede configurar el comando que se ejecutará después de que se inicie el contenedor. EXPOSE puede especificar el puerto que el contenedor necesita exponer. Sin embargo, aún no hemos aprendido conocimientos relacionados con la red, por lo que no lo usaremos por el momento. Aquí especificamos el comando para iniciar el proyecto Java. Una vez completada la configuración, reconstruya:

imagen-20220701210438145

Puedes ver que han aparecido nuevos pasos en el historial:

imagen-20220701213513862

Luego inicie nuestra imagen, podemos operarla directamente en IDEA sin escribir comandos, lo cual es un poco agotador:

imagen-20220701210845768

imagen-20220701210908997

Después del inicio, puede ver la información de registro del inicio del contenedor a la derecha:

imagen-20220701210946261

imagen-20220701211029119

Pero descubrimos que no podemos acceder directamente a él después del inicio. ¿A qué se debe esto? Esto se debe a que la red dentro del contenedor está aislada de la red externa. Si queremos acceder al servidor en el contenedor, debemos vincular el puerto correspondiente al host y dejar que el host también abra este puerto para que podamos conectarnos. al contenedor:

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

Esto -prepresenta el enlace de puerto, que vincula el puerto en el contenedor Docker al puerto del host, de modo que se pueda acceder al puerto 8080 del contenedor a través del puerto 8080 del host (presentaremos la administración de la red del contenedor en detalle más adelante), el parámetro indica operación en segundo -dplano Por supuesto, también se puede configurar directamente en IDEA:

imagen-20220701211536598

Después de la configuración, haga clic para recrear el contenedor:

imagen-20220701211701640

Después de volver a ejecutar, podemos acceder con éxito al proyecto SpringBoot que se ejecuta en el contenedor:

imagen-20220701211753962

Por supuesto, para mayor comodidad en el futuro, podemos enviarlo directamente a Docker Hub, aquí creamos un nuevo almacén público:

imagen-20220701212330425

Esta vez usaremos IDEA para demostrar cómo cargar la imagen directamente, simplemente haga clic en:

imagen-20220701212458851

A continuación, debemos configurar la información relacionada con nuestro Docker Hub:

[Error en la transferencia de la imagen del enlace externo. El sitio de origen puede tener un mecanismo anti-leeching. Se recomienda guardar la imagen y cargarla directamente (img-74osw1mZ-1657097647538)(https://s2.loli.net/2022/07 /01/tMcD2kzNwW9J7d3.png )]

imagen-20220701212731276

Bien, la configuración del almacén espejo remoto está completa, simplemente empújelo directamente y espere a que se complete el envío.

imagen-20220701212902977

Puede ver que nuestra imagen ha aparecido en el almacén remoto y también puede verla sincrónicamente en IDEA:

imagen-20220701213026214

De esta manera, hemos completado el empaquetado del proyecto SpringBoot en una imagen de Docker usando IDEA.


Gestión de redes de contenedores

**Nota:** El estudio de esta sección requiere dominar algunos conocimientos del curso "Redes de Computadoras".

Anteriormente, aprendimos algunas operaciones básicas de contenedores y espejos, y aprendimos cómo crear contenedores a través de espejos, construir contenedores nosotros mismos, enviar a almacenes remotos, etc. En esta parte, discutiremos la gestión de red de contenedores.

Tipo de red de contenedores

Después de instalar Docker, creará tres redes en nuestro host. Use network lsel comando para ver:

docker network ls

imagen-20220702161742741

Puede ver que hay bridgetres tipos hostde red nonede forma predeterminada: , la imagen predeterminada de Ubuntu no tiene ningún software, por lo que hemos instalado los que necesitaremos para la red de antemano:

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

Esto está instalado, salimos directamente y lo construimos como una nueva imagen:

docker commit lucid_sammet ubuntu-net

imagen-20220702170441267

Bien, podremos usarlo en un momento.

  • **ninguna red:** Esta red no tiene otras redes excepto una red de loopback local. Podemos especificar esta red al crear el contenedor.

    Aquí --networklos parámetros se utilizan para especificar la red:

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

    Después de ingresar, podemos verificar directamente la red actual:

    ifconfig
    

    Puede ver que solo hay un lodispositivo de red de loopback local:

    imagen-20220702170000617

    Entonces este contenedor no puede conectarse a Internet:

    imagen-20220702170531312

    Se puede decir que la operación "verdadera" de una sola máquina es absolutamente segura, nadie puede acceder a ella y es bueno guardar algunas contraseñas.

  • **red puente:** El tipo de red utilizado por el contenedor de forma predeterminada. Esta es la red puente y el tipo de red más utilizado:

    De hecho, cuando verificamos la información de la red en el host, encontraremos un dispositivo de red llamado docker0:

    [Error en la transferencia de la imagen del enlace externo. El sitio de origen puede tener un mecanismo anti-leeching. Se recomienda guardar la imagen y cargarla directamente (img-TKTERH5x-1657097647539)(https://s2.loli.net/2022/07 /02/jDKSIriXec96uhy.png )]

    Este dispositivo de red es un dispositivo virtual que se crea automáticamente cuando se instala Docker ¿Para qué se utiliza? Podemos echar un vistazo a la situación dentro del contenedor creado por defecto:

    docker run -it ubuntu-net
    

    imagen-20220702172532004

    Puede ver que la dirección de la interfaz de red del contenedor es 172.17.0.2. De hecho, esta es la red virtual creada por Docker. Es como si el contenedor tuviera un cable de red virtual separado conectado a la red virtual creada por Docker, y el La red docker0 en realidad se usa como En la función de puente, un extremo es su propia subred virtual y el otro extremo es la red del host.

    La topología de la red es similar a la siguiente:

    imagen-20220702173005750

    Al agregar dicho puente, podemos administrar y controlar la red de contenedores. Podemos usar network inspectel comando para ver la información de configuración del puente docker0:

    docker network inspect bridge
    

    imagen-20220702173431530

    La subred configurada aquí es 172.17.0.0, la máscara de subred es 255.255.0.0 y la puerta de enlace es 172.17.0.1, que es el dispositivo de red virtual docker0, por lo que el contenedor que creamos anteriormente es la dirección 172.17 asignada en esta subred 0.2.

    Más adelante también explicaremos cómo gestionar y controlar las redes de contenedores.

  • **red de host:** Cuando el contenedor está conectado a esta red, compartirá la red del host y la configuración de la red es exactamente la misma:

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

    Puede ver que la lista de redes y la lista de hosts son las mismas. Me pregunto si habrá notado que incluso el nombre de host es exactamente el mismo que afuera:

    [La transferencia de la imagen del enlace externo falló. El sitio de origen puede tener un mecanismo anti-leeching. Se recomienda guardar la imagen y cargarla directamente (img-vr5IpDLe-1657097647540)(https://s2.loli.net/2022/ 07/02/cRAQtIxV4D9byCu.png )]

    Siempre que el host pueda conectarse a Internet, también se puede utilizar directamente dentro del contenedor:

    imagen-20220702171041631

    En este caso, al utilizar directamente la red del host, básicamente no hay pérdida en el rendimiento de la transmisión y podemos abrir puertos directamente, etc., sin ningún puente:

     apt install -y systemctl nginx
     systemctl start nginx
    

    Después de instalar Nginx, puedes acceder a él directamente sin abrir ningún puerto:

    imagen-20220702171550979

    Es mucho más conveniente que una red puenteada.

Podemos elegir razonablemente estos tres usos de la red según la situación real.

Red definida por el usuario

Además de las tres redes que presentamos anteriormente, también podemos personalizar nuestra propia red y dejar que el contenedor se conecte a esta red.

Docker proporciona tres controladores de red de forma predeterminada: bridge, overlay, macvlan. Diferentes controladores corresponden a diferentes controladores de dispositivos de red e implementan diferentes funciones. Por ejemplo, el tipo de puente es en realidad el mismo que la red de puente que presentamos anteriormente.

Podemos network createprobarlo usando:

docker network create --driver bridge test

Aquí creamos una red puente llamada prueba:

imagen-20220702180837819

Puedes ver que se ha añadido un nuevo dispositivo de red, este será el gateway responsable de nuestra red de contenedores, es igual que el docker0 anterior:

docker network inspect test

imagen-20220702181150667

Aquí creamos un nuevo contenedor usando esta red:

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

imagen-20220702181252137

La dirección IP asignada exitosamente está dentro de esta red. Tenga en cuenta que las diferentes redes están aisladas. Podemos crear otro contenedor para probar:

imagen-20220702181808792

Puedes ver que diferentes redes están aisladas entre sí y no pueden comunicarse, por supuesto, también conectamos este contenedor a la red a la que pertenece otro contenedor:

docker network connect test 容器ID/名称

imagen-20220702182050204

Esto conecta una nueva red:

imagen-20220702182146049

Puede ver que se ha agregado un nuevo dispositivo de red al contenedor y está conectado a nuestra propia red definida. Ahora que los dos contenedores están en la misma red, pueden hacer ping entre sí:
imagen-20220702182310008

Los otros dos tipos de redes no se presentarán aquí, se utilizan para la comunicación entre múltiples hosts y actualmente solo aprendemos a usarlos en una sola máquina.

red entre contenedores

Primero echemos un vistazo a la comunicación de red entre contenedores. De hecho, ya hemos demostrado el ping antes. Ahora creamos dos contenedores de Ubuntu:

docker run -it ubuntu-net

Primero obtenga la información de red de uno de los contenedores:

[La transferencia de la imagen del enlace externo falló. El sitio de origen puede tener un mecanismo anti-leeching. Se recomienda guardar la imagen y cargarla directamente (img-cGnoTjG7-1657097647542)(https://s2.loli.net/2022/ 02/07/yTEcg4l2kASBnQu.png )]

Podemos hacer ping a este contenedor directamente desde otro contenedor:

[Error en la transferencia de la imagen del enlace externo. El sitio de origen puede tener un mecanismo anti-leeching. Se recomienda guardar la imagen y cargarla directamente (img-IOOtmkLm-1657097647542)(/Users/nagocoler/Library/Application Support/typora-user -imagenes/imagen-20220702175444713 .png)]

Puede ver que puede hacer ping directamente porque ambos contenedores usan la red puente y están en la misma subred, por lo que pueden acceder entre sí.

Podemos comunicarnos entre contenedores directamente a través de la dirección IP del contenedor, siempre que los dos contenedores estén en la misma red. Aunque esto es más conveniente, en la mayoría de los casos, la dirección IP después de que se implementa el contenedor se asigna automáticamente (por supuesto También se puede --ipespecificar manualmente, pero sigue siendo un inconveniente). No podemos conocer la dirección IP de antemano, entonces, ¿existe algún método que pueda ser más flexible?

Podemos usar el servidor DNS proporcionado por Docker. Es como un servidor DNS real y puede resolver nombres de dominio. Es muy simple de usar. Solo necesitamos dar un nombre cuando se inicia el contenedor. Podemos acceder directamente a este nombre. Eventualmente se resolverá en la dirección IP del contenedor correspondiente, pero tenga en cuenta que solo tendrá efecto en nuestra red definida por el usuario y la red predeterminada no funcionará:

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

Luego simplemente haga ping directamente al nombre de la otra parte:

imagen-20220702192457354

Puede ver que el nombre se resolverá automáticamente en la dirección IP correspondiente, por lo que no tiene que preocuparse por la incertidumbre de la IP.

Por supuesto, también podemos permitir que dos contenedores compartan la misma red al mismo tiempo. Tenga en cuenta que compartir aquí es compartir directamente el mismo dispositivo de red. Los dos contenedores comparten una dirección IP y solo deben especificarse al crear:

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

Aquí, la red se especifica como la red de un contenedor, de modo que los dos contenedores usan la misma red:

imagen-20220702200711351

Puede ver que las direcciones IP de los dos contenedores y las direcciones Mac de las tarjetas de red son exactamente las mismas. Sus redes ahora están compartidas. En este momento, cuando accede al localhost en el contenedor, es usted y otros.

Podemos instalar Nginx en el contenedor 1 y luego acceder a él en el contenedor 2:

 apt install -y systemctl nginx
 systemctl start nginx

imagen-20220702201348722

Accedí exitosamente al servidor Nginx en otro contenedor.

Red externa de contenedores

Anteriormente presentamos la comunicación de red entre contenedores, ahora veamos la comunicación entre el contenedor y la red externa.

Primero, veamos cómo el contenedor accede a Internet. En las tres redes predeterminadas, solo el modo compartido y el modo puente pueden conectarse a la red externa. El modo compartido en realidad utiliza directamente el equipo de red del host para conectarse a Internet. Aquí veamos principalmente en el modo puente.

A través del estudio anterior, aprendimos que el modo puente en realidad crea una red virtual separada, permite que el contenedor esté en esta red virtual y luego se conecta al mundo exterior a través del puente. Entonces, ¿cómo llega el paquete de datos al host desde ¿La red dentro del contenedor? y luego enviarla a Internet. De hecho, lo más crítico en todo el proceso es confiar en NAT (traducción de direcciones de red) para traducir la dirección y luego usar la dirección IP del host para enviar el paquete de datos.

Aquí complementaremos la NAT aprendida en el curso "Redes de Computadoras":

De hecho, NAT se ve a menudo en nuestras vidas. Por ejemplo, si queremos acceder a un recurso en Internet y comunicarnos con el servidor, debemos enviar el paquete de datos y el servidor también debe enviar el paquete de datos de regreso. . Nosotros podemos conocer la dirección IP del servidor, o podemos conectarnos directamente, porque la dirección IP del servidor está expuesta en Internet, pero nuestra LAN es diferente. Solo se limita a nuestra casa. Por ejemplo, si Conéctese al enrutador en casa. Puede obtener una dirección IP, pero encontrará que esta red pública IP no puede acceder directamente a nosotros, porque esta dirección IP es solo una dirección IP de red de área local, comúnmente conocida como IP de intranet. Se puede acceder desde la red pública, luego el servidor ¿Cómo se nos envía el paquete de datos?

imagen-20220702230700124

De hecho, NAT se utiliza aquí para ayudarnos a comunicarnos con servidores en Internet. A través de NAT, la dirección IP de la red de área local se puede asignar a la dirección IP de la red pública correspondiente. Un extremo del dispositivo NAT está conectado al externo red y el otro extremo está conectado a la red interna. Todos los dispositivos en Internet, cuando queremos comunicarnos con la red externa, podemos enviar el paquete de datos al dispositivo NAT, que asignará la dirección de origen del paquete de datos a su dirección en la red externa, para que el servidor pueda descubrirlo y pueda establecer comunicación con él directamente. Cuando el servidor devuelve datos, también se entrega directamente al dispositivo NAT y luego se reenvía al dispositivo de intranet correspondiente de acuerdo con la asignación de direcciones (por supuesto, debido a que la dirección IP pública es limitada, la combinación de puerto IP + es generalmente utilizado en forma de ANPT)

Entonces, si abre Baidu y busca IP directamente, encontrará que esta dirección IP no es la local, sino la dirección pública del dispositivo NAT:

imagen-20220702231458928

De hecho, los enrutadores de nuestros hogares generalmente tienen funciones NAT y el modo NAT está activado de forma predeterminada. Incluyendo nuestra comunidad, también hay un dispositivo NAT para conversión, para que su computadora pueda viajar en el mundo de Internet. Por supuesto, NAT también puede proteger el equipo de la red interna para que no quede expuesto directamente a la red pública, lo que también será más seguro: solo cuando iniciamos activamente una conexión otros podrán saber de nosotros.

Por supuesto, lo mismo ocurre con nuestro Docker. De hecho, si desea enviar paquetes de datos desde la intranet a Internet, debe pasar por una serie de procesos como este:

imagen-20220702232449520

De esta forma, la red interna utilizada por el contenedor Docker puede comunicarse con la red externa.

Pero hay un problema con esto. Si confiamos únicamente en NAT, el mundo exterior sólo podrá conocernos cuando contactemos activamente con el mundo exterior. Pero ahora podemos implementar algunos servicios en nuestros contenedores y necesitar que el mundo exterior se conecte activamente con nosotros. ¿Qué debemos hacer en este momento?

Podemos configurar el mapeo de puertos directamente en el contenedor. ¿Recuerdas que implementamos el servidor Nginx en la primera lección?

docker run -d -p 80:80 nginx

Los parámetros aquí -pson en realidad configuraciones de mapeo de puertos. El mapeo de puertos puede mapear el puerto que el contenedor necesita para proporcionar servicios externos al puerto del host host. De esta manera, cuando se accede al puerto correspondiente del host host desde el exterior, será reenviado directamente al contenedor y el puerto está mapeado. La regla es que 宿主端口:容器端口lo que se configura aquí es mapear el puerto 80 del contenedor al puerto 80 del host host.

[La transferencia de la imagen del enlace externo falló. El sitio de origen puede tener un mecanismo anti-leeching. Se recomienda guardar la imagen y cargarla directamente (img-XK4pgE2W-1657097647543)(https://s2.loli.net/2022/ 02/07/WQzEVTwePNaHYgG.png )]

Una vez que el puerto de monitoreo 80 del host recibe un paquete de datos, se reenviará directamente al contenedor correspondiente. Entonces, después de configurar el mapeo de puertos, normalmente podemos acceder a los servicios en el contenedor desde el exterior:

imagen-20220630165440751

También podemos entrar directamente docker pspara ver el mapeo de puertos:

imagen-20220702233831651

Hasta ahora, eso es todo para la parte de red del contenedor. Por supuesto, esto es solo la operación de la red del contenedor en una sola máquina. En cursos futuros, aprenderemos más sobre la configuración de la red en múltiples hosts.


Gestión de almacenamiento de contenedores

Anteriormente presentamos la gestión de red de contenedores, ahora sabemos cómo configurar la red de contenedores y algunos principios relacionados. Otra parte importante es el almacenamiento de contenedores, en esta sección comprenderemos en profundidad la gestión del almacenamiento de contenedores.

Almacenamiento persistente de contenedores

Sabemos que después de crear el contenedor, los archivos que creamos y modificamos en el contenedor en realidad se guardan y operan en la capa superior del contenedor mediante el mecanismo de capas del contenedor. Para proteger las imágenes de cada capa inferior para que no se modifiquen, hay es una característica de CopyOnWrite. Pero esto también hará que los datos se pierdan cuando se destruya el contenedor. Cuando destruimos el contenedor y creamos un contenedor nuevo, se pierden todos los datos y volvemos directamente al lugar donde comenzó el sueño.

En algunos casos, es posible que queramos almacenar ciertos archivos de manera persistente en el contenedor en lugar de almacenarlos una sola vez. En este caso, se utiliza un volumen de datos.

Antes de comenzar, preparemos la imagen que se utilizará en el experimento:

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

Luego empaquetelo en una imagen que usaremos más adelante:

docker commit 

Podemos dejar que el contenedor guarde el archivo en el host, de modo que incluso si el contenedor se destruye, el archivo se conservará en el host. La próxima vez que se cree un contenedor, el archivo correspondiente aún se puede leer desde el host. anfitrión. ¿Cómo hacerlo? Simplemente especifíquelo cuando se inicie el contenedor:

mkdir test

Ahora creamos un nuevo testdirectorio en el directorio de usuario, luego creamos un archivo en él y escribimos algo de contenido:

vim test/hello.txt

Luego podemos montar el directorio o archivo en el host en un directorio en el contenedor:

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

Aquí se utiliza un nuevo parámetro -vpara especificar el montaje del archivo. Aquí, el directorio de prueba que acabamos de crear se monta en la ruta /root/test del contenedor.

imagen-20220703105256049

De esta manera, podemos acceder directamente a los archivos en el host en el contenedor. Por supuesto, si editamos los archivos en el directorio de montaje, es equivalente a editar los datos del host:

vim /root/test/test.txt  

imagen-20220703105626105

En el directorio correspondiente del host, puede acceder directamente al archivo que acabamos de crear.

A continuación, destruyamos el contenedor y veamos si los datos montados aún se pueden conservar cuando el contenedor ya no exista:

imagen-20220703105847329

Se puede ver que incluso si destruimos el contenedor, los archivos en la máquina host aún existen y no se verán afectados, de esta manera, la próxima vez que creemos una nueva imagen, aún podremos usar estos archivos guardados afuera.

Por ejemplo, si ahora queremos implementar un servidor Nginx para representar nuestro front-end, podemos guardar directamente la página de front-end en la máquina host y luego permitir que Nginx en el contenedor acceda a ella mediante el montaje. Incluso si la imagen de Nginx se actualiza más adelante, es necesario volver a instalarla y la creación no afectará nuestra página de inicio. Probémoslo aquí: primero cargamos la plantilla de front-end en el servidor:

scp Downloads/moban5676.zip 192.168.10.10:~/

Luego descomprímelo en el servidor:

unzip moban5676.zip

Entonces podemos iniciar el contenedor:

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

Aquí montaremos el directorio descomprimido en el directorio del sitio predeterminado de Nginx en el contenedor /usr/share/nginx/html/(dado que está montado en el nivel superior, reemplazará los archivos originales en la capa espejo), de modo que Nginx representará directamente la interfaz que almacenamos. en el host host. Página, por supuesto, no olvide asignar el puerto al host host. La imagen que usamos aquí es la imagen oficial de nginx.

Ahora ingresamos al contenedor e iniciamos el servicio Nginx:

systemctl start nginx

Luego acceda a él a través del navegador para ver si el proxy tiene éxito:

imagen-20220703111937254

Puede ver que nuestra página de inicio está proxy directamente. Por supuesto, si queremos escribir una configuración personalizada, podemos usar el mismo método.

Tenga en cuenta que si -vno especificamos un directorio en el host para montar cuando usamos parámetros, Docker creará automáticamente un directorio y copiará el contenido de la ruta correspondiente en el contenedor al directorio creado automáticamente y finalmente lo montará En el contenedor, Este es el volumen de datos administrado por Docker (volumen administrado por Docker), intentémoslo:

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

Tenga en cuenta que aquí solo especificamos la ruta de montaje y no especificamos el directorio correspondiente del host. Continúe creando:

imagen-20220703112702067

Después de la creación, puede ver rootque hay un nuevo abcdirectorio debajo del directorio, entonces, ¿dónde está ubicado específicamente en el host? Aquí todavía podemos usar inspectel comando:

docker inspect bold_banzai 

[Error en la transferencia de la imagen del enlace externo. El sitio de origen puede tener un mecanismo anti-leeching. Se recomienda guardar la imagen y cargarla directamente (img-zANxbiPe-1657097647544)(https://s2.loli.net/2022/07 /03/zFotAfeBpcRjKWN.png )]

Puedes ver que Sorce apunta a /var/libun directorio en . Podemos ingresar a este directorio para crear un nuevo archivo. Recuerda aumentar los permisos antes de ingresar. Si los permisos son bajos, no podrás ingresar:

imagen-20220703114333446

Creemos un nuevo documento de texto:

imagen-20220703114429831

De hecho, es lo mismo que antes y también se puede ver en el contenedor. Por supuesto, una vez que se elimina el contenedor, los datos aún se conservan. Cuando ya no necesitemos utilizar el volumen de datos, podemos eliminarlo:

imagen-20220703145011638

Por supuesto, a veces, por conveniencia, es posible que no necesite montar un directorio directamente, sino simplemente transferir algunos archivos desde el host al contenedor. Aquí podemos usar el comando para completar cp:

imagen-20220703115648195

Este comando admite copiar archivos del host al contenedor, o copiar archivos del contenedor al host. El método de uso es similar al comando que viene con Linux cp.

Intercambio de datos de contenedores

Anteriormente, montamos los archivos en el host directamente en el contenedor mediante el montaje, de modo que el contenedor pueda acceder directamente a los archivos en el host y los archivos en el host no se limpiarán cuando se elimine el contenedor.

Echemos un vistazo a cómo implementar el intercambio de datos entre contenedores. De hecho, de acuerdo con nuestras ideas anteriores, podemos crear un directorio público en el host host, de modo que todos los contenedores que deben compartirse puedan montar este directorio público:

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

imagen-20220703141840532

Dado que está montada la misma área en la máquina host, se puede acceder directamente al contenido en ambos contenedores. Por supuesto, también podemos montar el directorio de otro contenedor y especificar directamente el directorio que se utilizará para montar este contenedor al iniciar el contenedor:

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

Aquí se utiliza para --volumes-fromespecificar otro contenedor (este tipo de contenedor se utiliza para proporcionar volúmenes de datos a otros contenedores, generalmente lo llamamos contenedor de volúmenes de datos)

imagen-20220703142849845

Se puede ver que el contenido montado en el contenedor del volumen de datos también existe en el contenedor actual. Por supuesto, incluso si el contenedor del volumen de datos se elimina en este momento, no afectará este lado, porque este lado equivale a heredar el contenedor de volumen de datos. El volumen de datos se proporciona, por lo que esencialmente los dos contenedores montan el mismo directorio para lograr el intercambio de datos.

Aunque los datos se pueden transferir entre contenedores a través del método anterior, es inconveniente. A veces solo queremos compartir entre contenedores y no queremos que la función del host participe directamente en el intercambio, entonces necesitamos encontrar una mejor manera. De hecho, podemos poner los datos completamente en el contenedor y compartir directamente los datos empaquetados en el contenedor con otros contenedores construyendo un contenedor. Por supuesto, sigue siendo esencialmente un volumen de datos administrado por Docker, aunque todavía no está completamente separado. desde el host Pero la portabilidad es mucho mayor.

Escribamos un Dockerfile:

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

Aquí usamos un nuevo comando AGREGAR, que es similar al comando COPIAR. También puede copiar archivos al contenedor, pero puede descomprimir automáticamente los archivos comprimidos. Aquí solo necesita completar los archivos comprimidos y el siguiente VOLUMEN El La directiva -vcrea un punto de montaje en el contenedor tal como lo hicimos con los parámetros:

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

Luego construimos directamente:

docker build -t data .

imagen-20220703153109650

Ahora ejecutemos un contenedor y veamos:

[Error en la transferencia de la imagen del enlace externo. El sitio de origen puede tener un mecanismo anti-leeching. Se recomienda guardar la imagen y cargarla directamente (img-V1YYSZM6-1657097647545)(https://s2.loli.net/2022/07 /03/SUg32jlwMcY7Btp.png )]

Puede ver que todos los archivos se han descomprimido automáticamente (excepto los nombres de los archivos chinos están confusos, pero no importa), salimos del contenedor y podemos ver que los volúmenes de datos que necesitamos usar se han agregado al lista de volúmenes de datos:

imagen-20220703153514730

imagen-20220703153542739

Esta ubicación es en realidad la ubicación donde se almacenan los datos en el host actual, pero Docker la administra en lugar de personalizarla nosotros. Ahora podemos crear un nuevo contenedor y heredar directamente:

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

Visite el servidor Nginx y podrá ver el proxy exitoso:

imagen-20220703111937254

De esta manera, podemos poner los datos en el contenedor para compartirlos. No necesitamos especificar deliberadamente el punto de montaje del host. En cambio, Docker lo administra por sí mismo, de modo que incluso si el host se migra, aún puede desplegarse rápidamente.


Gestión de recursos de contenedores

Ya hemos completado el estudio de varios módulos principales de Docker, finalmente, echemos un vistazo a cómo administrar los recursos del contenedor.

Operaciones de control de contenedores

Antes de comenzar, todavía necesitamos agregar algunos otros comandos de contenedor que no hemos mencionado antes.

En primer lugar, cuando nuestro proyecto SpringBoot se está ejecutando, ¿cómo podemos verificar la información del registro de salida?

docker logs test

Utilice el comando aquí logpara imprimir la información de registro en el contenedor:

imagen-20220701221210083

Por supuesto, también puede agregar -fparámetros para imprimir continuamente información de registro.

imagen-20220701215617022

Ahora nuestro contenedor se ha iniciado, pero ¿qué sucede si queremos ingresar al contenedor para monitorearlo? Podemos attachadjuntar el comando a la terminal del comando de inicio del contenedor:

docker attach 容器ID/名称

imagen-20220701215829492

Tenga en cuenta que ahora ha cambiado a la terminal en el contenedor. Si desea salir, primero debe presionar Ctrl+P y luego Ctrl+Q para salir de la terminal. No puede usar Ctrl+C directamente para terminar, lo que directamente finalizar el programa que se ejecuta en Docker.Programa Java.

imagen-20220701220018207

Después de salir, el contenedor sigue ejecutándose.

También podemos usar execcomandos para iniciar una nueva terminal en el contenedor o ejecutar comandos en el contenedor:

docker exec -it test bash

-itLa operación es la misma que runel comando, después de ejecutar aquí, se creará una nueva terminal (por supuesto, el programa original todavía se ejecuta normalmente) e interactuaremos en una nueva terminal:

imagen-20220701220601732

Por supuesto, también puedes simplemente ejecutar un comando en el contenedor:

imagen-20220701220909626

Después de la ejecución, se abrirá una nueva terminal en el contenedor para ejecutar el comando y generar los resultados.

También aprendimos anteriormente la operación de detención del contenedor, y stoplo detuvimos ingresando un comando. Sin embargo, esta operación no se detendrá inmediatamente, sino que esperará a que el contenedor maneje las consecuencias. Entonces, ¿cómo podemos terminar el contenedor por la fuerza? Podemos usar killel comando directamente, lo que equivale a enviar la señal SIGKILL al proceso para forzar su finalización.

docker kill test

Comparado con stopel orden, killno es tan gentil.

A veces es posible que simplemente queramos que el contenedor deje de ejecutarse temporalmente en lugar de finalizarlo directamente. Esperamos reanudar la ejecución del contenedor en algún momento en el futuro. En este momento, podemos usar el comando para pausar el contenedor pause:

docker pause test

Después de pausar el contenedor, el programa deja de ejecutarse temporalmente y no puede responder a las solicitudes enviadas por el navegador:

imagen-20220701222537737

imagen-20220701222243900

En este momento la magia del amor está dando vueltas en círculos, podemos restablecerla a su funcionamiento usando unpauseel comando:

docker unpause test

Después de reanudar la operación, la respuesta fue exitosa al instante.

[La transferencia de la imagen del enlace externo falló. El sitio de origen puede tener un mecanismo anti-leeching. Se recomienda guardar la imagen y cargarla directamente (img-mQep9X8o-1657097647547)(https://s2.loli.net/2022/ 01/07/g2b8mxVz1i7WJop.png )]

gestión de recursos físicos

Para un contenedor, en algunos casos es posible que no queramos que ocupe todos los recursos del sistema para ejecutarse. Solo queremos asignar una parte de los recursos al contenedor. Por ejemplo, solo se asignan 2G de memoria al contenedor. Solo un Se permite utilizar un máximo de 2G y no se permite ocupar más recursos. Con más memoria, debemos limitar los recursos del contenedor.

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

El -mparámetro es el límite en el uso de la memoria física del contenedor, --memory-swappero el límite en la suma de la memoria y las particiones de intercambio. Ambas son por defecto -1, lo que significa que no hay límite (si solo se especifican los parámetros al principio). -m, entonces el límite de la memoria de intercambio es el mismo que el límite del uso de memoria física del contenedor. Para mantener la coherencia, la memoria + el intercambio equivalen al -mdoble del tamaño) De forma predeterminada, es el mismo que el de la máquina host, que tiene Memoria 2G. Ahora podemos intentar limitar la memoria del contenedor a 100 M, incluyendo 50 M de memoria física y 50 M de memoria de intercambio. Intente iniciarlo. Programa SpringBoot:

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

Como puede ver, no pudo iniciarse debido a memoria insuficiente:

imagen-20220702104653971

Por supuesto, además de las restricciones de memoria, también podemos limitar los recursos de la CPU. De forma predeterminada, todos los contenedores pueden usar los recursos de la CPU por igual. Podemos ajustar los pesos de la CPU de diferentes contenedores (el valor predeterminado es 1024) para satisfacer la demanda. Para asignar recursos, usted necesita usar -clas opciones aquí, o puede ingresar el nombre completo --cpu-share:

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

La relación de peso de la CPU del contenedor aquí es de 16 a 8, que es de 2 a 1 (tenga en cuenta que solo tendrá efecto cuando hay varios contenedores), luego, cuando los recursos de la CPU sean escasos, los recursos se asignarán de acuerdo con este peso. Por supuesto, si los recursos de la CPU no son escasos, todavía existe la posibilidad de utilizar todos los recursos de la CPU.

Aquí utilizamos una herramienta de prueba de estrés para verificar:

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

Luego ingresamos al contenedor para instalar stresslas herramientas de prueba de estrés:

apt update && apt install -y stress

Luego iniciamos las herramientas de prueba de estrés en ambos contenedores, generando cuatro procesos para calcular continuamente la raíz cuadrada de números aleatorios:

stress -c 4

Luego ingresamos a top para ver el estado de la CPU (recuerde cerrar el contenedor rápidamente después de leer esto, de lo contrario la CPU estará llena y atascada):

imagen-20220702114126128

Se puede ver que a los contenedores con alto peso se les asignan más recursos de CPU, mientras que a los contenedores con bajo peso solo se les asigna la mitad de los recursos de CPU.

Por supuesto, también podemos limitar directamente la cantidad de CPU utilizadas por el contenedor:

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

--cpuset-cpusLa opción se puede limitar directamente a ejecutarse en la CPU especificada. Por ejemplo, si nuestro host tiene una CPU de 2 núcleos, podemos dividir las dos CPU 0 y 1 para que Docker las use. Después de la restricción, solo los recursos de la CPU 1 se utilizará:

imagen-20220702115538699

Se puede ver que los cuatro procesos solo usan el 25% de la CPU cada uno, y el total es el 100%, lo que significa que solo pueden ocupar el uso completo de una CPU. Si desea asignar varias CPU, sepárelas con comas:

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

Esto utilizará estas dos CPU:

imagen-20220702115818344

Por supuesto, también puede usarlo directamente --cpuspara limitar la cantidad de recursos de CPU utilizados:

docker run -it --cpus=1 ubuntu

imagen-20220702120329140

Una vez que el límite se establece en 1, solo se pueden usar los recursos proporcionados por una CPU, por lo que aquí solo se cargan los recursos de una CPU. Por supuesto, hay --cpu-period sumas más sofisticadas --cpu-quota, que no se presentarán aquí.

Finalmente, echemos un vistazo a las limitaciones en el rendimiento de lectura y escritura de E/S del disco. Primero usamos ddel comando para probar la velocidad de lectura y escritura del disco:

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

No es necesario esperar a que finalice la ejecución, solo Ctrl+C para finalizarla a la mitad:

imagen-20220702121839871

Puede ver que la velocidad actual de lectura y escritura es de 86,4 MB/s y podemos limitarla mediante los parámetros --device-read/write-bpsy .--device-read/write-iops

Hablemos primero de la diferencia:

  • bps: la cantidad de datos leídos y escritos por segundo.
  • iops: número de IO por segundo.

Por intuición, aquí utilizamos directamente BPS como condición de restricción:

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

Debido a que el sistema de archivos del contenedor está /dev/sdaactivado, limitaremos /dev/sda:10MBla velocidad de escritura en /dev/sda a solo 10 MB/s. Probémoslo:

imagen-20220702122557288

Puede ver que la velocidad actual es de sólo unos 10 MB.

Monitoreo de contenedores

Finalmente, echemos un vistazo a cómo monitorear el estado de ejecución del contenedor en tiempo real. Ahora queremos poder monitorear el uso de recursos del contenedor. ¿Qué debemos hacer?

Podemos usar statsel comando para monitorear:

docker stats

imagen-20220702153236692

Se pueden monitorear varios estados del contenedor en tiempo real, incluido el uso de memoria, uso de CPU, E/S de red, E/S de disco y otra información. Por supuesto, si limitamos el uso de memoria:

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

Puedes ver las limitaciones muy claramente:

[La transferencia de la imagen del enlace externo falló. El sitio de origen puede tener un mecanismo anti-leeching. Se recomienda guardar la imagen y cargarla directamente (img-Gzph9e4m-1657097647548)(https://s2.loli.net/2022/ 02/07/CGc6T4iYyN7PD51.png )]

Además de usar statscomandos para monitorear la situación en tiempo real, también puede usar topcomandos para ver los procesos en el contenedor:

docker top 容器ID/名称

imagen-20220702153957780

Por supuesto, también puede llevar algunos parámetros. Los parámetros específicos psson consistentes con los parámetros del comando en Linux, por lo que no los presentaré aquí.

¿Pero es este tipo de seguimiento demasiado primitivo? ¿Existe un panel web que pueda utilizarse para seguimiento y gestión en tiempo real? alguno.

Necesitamos implementar una aplicación del panel de administración web Docker por separado. Generalmente, las más comunes son: Portainer. Aquí podemos implementar esta aplicación directamente a través de la imagen de Docker. Después de buscar, encontramos que la dirección de mantenimiento de la última versión es: https:/ /hub.docker.com/r/portainer/portainer-ce

CE es una versión comunitaria gratuita y, por supuesto, hay una versión comercial BE. Aquí simplemente instalamos la versión comunitaria directamente. El tutorial oficial de instalación de Linux: https://docs.portainer.io/start/install/server/docker/ Linux, incluidos algunos preparativos necesarios antes de la instalación.

Primero necesitamos crear un volumen de datos para que lo utilice Portainer:

docker volume create portainer_data

Luego instale y comience mediante el 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

Tenga en cuenta que aquí es necesario abrir dos puertos, uno es el puerto 8000 y el otro es el puerto 9443.

imagen-20220702155450772

OK, el inicio fue exitoso. Podemos iniciar sesión directamente en el panel backend: https://IP:9443/. Se requiere acceso HTTPS aquí. El navegador puede indicar que no es seguro, simplemente ignórelo:

imagen-20220702155637366

imagen-20220702155703962

Después de ingresar, debemos registrarnos. Aquí solo necesitamos ingresar la contraseña dos veces. El nombre de usuario predeterminado es admin. Después de completarlo, podemos comenzar a usarlo:

[La transferencia de la imagen del enlace externo falló. El sitio de origen puede tener un mecanismo anti-leeching. Se recomienda guardar la imagen y cargarla directamente (img-KyKabWKY-1657097647549)(https://s2.loli.net/2022/ 02/07/P1JIKaMCl7guYoz.png )]

Haga clic en Comenzar para ingresar a la página de administración. Podemos ver que actualmente hay un servidor Docker local ejecutándose:

imagen-20220702160328972

Podemos hacer clic para entrar y gestionarlo en detalle, pero la única desventaja es que no hay chino, lo cual es bastante incómodo, también puedes usar la versión china no oficial: https://hub.docker.com/r/6053537/ portainer-ce.


Orquestación de contenedores independiente

Finalmente, expliquemos Docker-Compose, que puede orquestar nuestros contenedores. Por ejemplo, ahora queremos implementar muchos tipos de servicios en un host, incluidas bases de datos, colas de mensajes, aplicaciones SpringBoot, o queremos construir un clúster MySQL. En este caso, necesitamos crear varios contenedores para completar, pero esperamos Lograr la implementación con un solo clic, ¿qué debemos hacer en este momento? Estamos a punto de utilizar la orquestación de contenedores para permitir la implementación de varios contenedores de acuerdo con nuestra propia orquestación.

**Documento oficial: **https://docs.docker.com/get-started/08_using_compose/, definitivamente es imposible presentar todas las configuraciones en el video tutorial, así que si sus amigos quieren saber más configuraciones, si Si tiene más necesidades, puede consultar directamente la documentación oficial.

Inicio rápido

En el entorno Linux primero debemos instalar el complemento:

sudo apt install docker-compose-plugin

Luego ingrese docker compose versionpara verificar si la instalación fue exitosa.

[La transferencia de la imagen del enlace externo falló. El sitio de origen puede tener un mecanismo anti-leeching. Se recomienda guardar la imagen y cargarla directamente (img-vKLsI2Fz-1657097647549)(https://s2.loli.net/2022/ 03/07/5XDiAMpgW9aqUGJ.png )]

Aquí tomamos la implementación del proyecto SpringBoot como ejemplo. Continuamos usando el proyecto SpringBoot previamente empaquetado. Ahora queremos implementar este proyecto SpringBoot al mismo tiempo que implementamos un servidor MySQL y un servidor Redis. En este momento, el Entorno completo para que se ejecute nuestro proyecto SpringBoot, primero obtenga la imagen correspondiente:

docker pull mysql/mysql-server
docker pull redis

A continuación, necesitamos instalar DockerCompose localmente, descargar la dirección: https://github.com/docker/compose/releases, descargar la versión correspondiente a nuestra computadora y luego configurarla en IDEA:

imagen-20220703175103531

Una vez completada la descarga, cambie la ruta del archivo ejecutable de Docker Compose a la ruta donde almacenó el archivo ejecutable que acaba de descargar. Para Windows, simplemente configure la ruta directamente. Después de descargar para MacOS, debe realizar las siguientes operaciones:

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

Una vez completada la configuración, se puede usar normalmente, de lo contrario no se ejecutará, luego podemos comenzar a escribir el archivo docker-compose.yml en IDEA.

imagen-20220703180206437

Haga clic en el botón "Sincronizar con la ventana de la herramienta de servicio" en la esquina superior derecha aquí, para que pueda verificar la situación a continuación más adelante.

Configuraremos este archivo desde cero ahora. Ahora tenemos que crear tres servicios, uno es el servidor MySQL, uno es el servidor Redis y el otro es el servidor SpringBoot. Se necesitan tres contenedores para ejecutarse por separado. Primero, escribimos estos tres servicios:

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

De esta manera hemos configurado los tres servicios y los nombres de los contenedores correspondientes que se crearán más adelante, luego debemos especificar las imágenes correspondientes a estos contenedores, la primera es nuestra aplicación SpringBoot, también podremos actualizar la aplicación más adelante y modificaciones, entonces aquí necesitamos construir la imagen desde Dockerfile antes de implementarla:

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

Modifiquemos el Dockerfile aquí y cambiemos la imagen básica a una imagen que haya empaquetado el entorno JDK:

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

Luego están los otros dos servicios, los otros dos servicios necesitan usar las imágenes correspondientes para iniciar el contenedor:

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

Aún no ha terminado, todavía necesitamos mapear el puerto del proyecto SpringBoot y completar el último archivo de configuración simple de Docker-Compose:

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

Ahora podemos implementar directamente con un clic, hacemos clic en el botón de implementación a continuación:

imagen-20220703182541976

imagen-20220703182559020

Ver Ejecutando 4/4 significa que la implementación ha sido exitosa, vayamos al servidor para ver la situación:

imagen-20220703182657205

Como puede ver, de hecho se crean 3 contenedores de acuerdo con nuestra configuración, todos se están ejecutando y se puede acceder a ellos normalmente:

imagen-20220703182958392

Si queremos finalizar, sólo debemos hacer clic en Detener:

imagen-20220703183240400

Por supuesto, si ya no necesitamos este entorno, podemos hacer clic directamente en el botón de abajo para bajar todo el arreglo, en este caso también se limpiarán los contenedores correspondientes:

imagen-20220703183730693

imagen-20220703183807157

Tenga en cuenta que al implementar usando Docker-Compose, se creará automáticamente una nueva red personalizada y todos los contenedores se conectarán a esta red personalizada:

imagen-20220703210431690

Esta red también utiliza bridge como controlador de forma predeterminada:

imagen-20220703210531073

De esta manera, hemos completado una configuración simple para implementar todo nuestro entorno.

Implementar el proyecto completo

Anteriormente aprendimos cómo usarlo docker-composepara una implementación simple, pero simplemente iniciamos el servicio, ahora conectemos estos servicios. El primero es el proyecto SpringBoot. Primero introducimos dependencias:

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

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

Luego configura la fuente de datos, espera, ¿cómo sabemos cuál es la contraseña predeterminada de la base de datos? Entonces, primero configuremos el servicio MySQL:

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 una configuración detallada de MySQL, consulte: https://registry.hub.docker.com/_/mysql

Luego completamos la configuración de la fuente de datos:

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

Luego escribamos un código de prueba, aquí usamos JPA para la interacción:

<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 continuación modifiquemos el archivo de configuración:

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创建表,全程自动化,尽可能做到开箱即用

Ahora que el código está escrito, podemos empaquetar el proyecto. Preste atención a ejecutar nuestro comando de empaquetado a continuación y no lo pruebe porque no puede conectarse a la base de datos:

mvn package -DskipTests

Después de regenerar el paquete jar, modificamos la configuración de docker-compose. Debido a que la velocidad de inicio de MySQL es relativamente lenta, tenemos que esperar un rato hasta que se complete el inicio. Si el proyecto SpringBoot no se inicia debido a una falla en la conexión a la base de datos, reiniciaremos:

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

Luego eliminamos la imagen creada previamente automáticamente y esperamos a que se reconstruya:

imagen-20220703215050497

Ahora volvamos a implementar docker-compos:

imagen-20220703215133786

Cuando los tres servicios están en azul, significa que se están ejecutando normalmente. Ahora probémoslo:

imagen-20220703215211999

A continuación, intentemos pasar datos a la base de datos:

[Error en la transferencia de la imagen del enlace externo. El sitio de origen puede tener un mecanismo anti-leeching. Se recomienda guardar la imagen y cargarla directamente (img-DoU5gNOr-1657097647552)(https://s2.loli.net/2022/07 /03/nVEURiAe7qjworl.png )]

imagen-20220703215245757

Puedes ver que la respuesta es exitosa, hagamos una solicitud:

[La transferencia de la imagen del enlace externo falló. El sitio de origen puede tener un mecanismo anti-leeching. Se recomienda guardar la imagen y cargarla directamente (img-ObN2Plzy-1657097647552)(https://s2.loli.net/2022/ 03/07/uB6rYDCSbLXmOPE.png )]

De esta manera, nuestro proyecto y MySQL se implementan básicamente de forma automática.

A continuación configuremos Redis:

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

Luego configure la información de conexión:

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, configuremos el archivo de configuración docker-compose:

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

Bien, volvamos a implementarlo como antes y luego probemos:

imagen-20220703220941562

[Error en la transferencia de la imagen del enlace externo. El sitio de origen puede tener un mecanismo anti-leeching. Se recomienda guardar la imagen y cargarla directamente (img-gyIt9iTW-1657097647553)(https://s2.loli.net/2022/07 /03/1SRG8EDtx5Oqr2M.png )]

De esta manera hemos completado la configuración de todo el entorno + aplicación, al implementar todo el proyecto solo necesitamos usar el archivo de configuración docker-compose para iniciarlo, esto facilita enormemente nuestra operación y permite usarlo fuera de la caja. Incluso podemos usar una plataforma dedicada para realizar una configuración única en múltiples hosts al mismo tiempo e implementarla rápidamente a gran escala, pero lo dejaremos para cursos futuros.

Supongo que te gusta

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