En los dos artículos anteriores, explicamos el contenedor SpringBoot basado en la implementación de la ventana acoplable. Si no hay una base relevante antes de leer este artículo, puede revisar el tutorial anterior.
Docker de la entrada a la trampa (2): compile microservicios SpringBoot basados en Docker
No sé si se encontró con este escenario la primera vez que utilizó Docker. Cada vez que implementa un microservicio, debe ejecutar comandos como Docker run xxx, Docker kill xxx para operar el contenedor. Suponiendo que un sistema se basa en varios contenedores de la ventana acoplable, ¿la implementación de cada contenedor de la ventana acoplable necesita escribir comandos manualmente para iniciarse y apagarse? Hacerlo aumentará la carga de trabajo de desarrollo del personal de operación y mantenimiento y también es propenso a errores.
Tecnología de orquestación de Docker Compose
En el artículo anterior, explicamos el desarrollo de la tecnología de contenedorización de Docker, pero a medida que tenemos más y más Docker , la gestión de contenedores también es particularmente problemática, por lo que nació la tecnología Docker Compose .
La tecnología Docker Compose es una herramienta de Docker que define y ejecuta una serie de aplicaciones complejas a través de un archivo e inicia varios contenedores a través de un archivo Docker-compose. Hay muchos casos reales de Docker-compose en Internet, pero se omiten algunos detalles. Entonces, a continuación, lo llevaré paso a paso para aprender a componer Docker desde lo más superficial a lo más profundo a través de un caso simple.
Basado en Docker Compose para empaquetar e integrar aplicaciones de microservicio SpringBoot
Creemos un proyecto básico de microservicio SpringBoot como de costumbre. Primero, echemos un vistazo a la estructura del proyecto de la versión básica:
El primero es el contenido de configuración de nuestro archivo pom:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.sise.idea</groupId>
<artifactId>springboot-docker</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>spring-boot-docker</name>
<url>http://maven.apache.org</url>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.3.RELEASE</version>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.18</version>
</dependency>
</dependencies>
<build>
<finalName>springboot-docker</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
Luego está el código de contenido del programa Java. Hay clases de aplicación y controlador convencionales. El código es el siguiente:
Solicitud de clase de inicio
package com.sise.docker;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* @author idea
* @data 2019/11/20
*/
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class);
}
}
Controlador DockerController
package com.sise.docker.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author idea
* @data 2019/11/20
*/
@RestController
@RequestMapping(value = "/docker")
public class DockerController {
@GetMapping(value = "/test")
public String test(){
System.out.println("=========docker test=========");
return "this is docker test";
}
}
archivo de configuración yml:
server:
port: 7089
El siguiente es el archivo de configuración que se usa cuando se empaqueta docker-compose. El método utilizado aquí suele ser escribir un archivo docker para el contenedor de la ventana acoplable necesario, y luego empaquetarlo y administrarlo mediante Docker Compose. Suponiendo que nuestros microservicios necesitan hacer referencia a aplicaciones como MySQL y MongoDB, la arquitectura general se muestra en la siguiente figura:
Así que comencemos con un contenedor simple y veamos cómo administrar SpringBoot con Docker Compose. A continuación se muestra un Dockerfile que empaqueta SpringBoot en el contenedor Docker:
#需要依赖的其他镜像
FROM openjdk:8-jdk-alpine
# Spring Boot应用程序为Tomcat创建的默认工作目录。作用是在你的主机”/var/lib/docker”目录下创建一个临时的文件,并且链接到容器中#的”/tmp”目录。
VOLUME /tmp
#是指将原先的src文件 添加到我们需要打包的镜像里面
ADD target/springboot-docker.jar app.jar
#设置镜像的时区,避免出现8小时的误差
ENV TZ=Asia/Shanghai
#容器暴露的端口号 和SpringBoot的yml文件暴露的端口号要一致
EXPOSE 7089
#输入的启动参数内容 下边这段内容相当于运行了java -Xms256m -Xmx512m -jar app.jar
ENTRYPOINT ["java","-Xms256m","-Xmx512m","-jar","app.jar"]
Luego está el enlace para agregar el archivo docker-compose.yml. A continuación se muestra el contenido del script:
#docker引擎对应所支持的docker-compose文本格式
version: '3'
services:
#服务的名称
springboot-docker:
build:
context: .
# 构建这个容器时所需要使用的dockerfile文件
dockerfile: springboot-dockerfile
ports:
# docker容器和宿主机之间的端口映射
- "7089:7089"
El archivo de configuración docker-compose.ym tiene reglas especiales. Por lo general, primero definimos el número de versión y luego enumeramos una serie de servicios relacionados con el contenedor.
A continuación, empaquete este servicio de la ventana acoplable e impleméntelo en el servidor Linux correspondiente. Aquí utilizo un servidor comprado en Alibaba Cloud para demostrarlo.
En la actualidad, el archivo no se ha empaquetado, por lo que no hay un directorio de destino, por lo que cuando se crea el archivo dockerfile, no tendrá éxito, por lo que primero debe empaquetar mvn:
mvn package
Lo siguiente es la entrada del comando Docker-Compose:
[root@izwz9ic9ggky8kub9x1ptuz springboot-docker]# docker-compose up -d
Starting springboot-docker_springboot-docker_1 ... done
[root@izwz9ic9ggky8kub9x1ptuz springboot-docker]#
Verá que el comando ingresado esta vez es algo diferente del comando docker mencionado en el tutorial anterior. Se convierte en el comando docker-compose. Este comando está diseñado específicamente para el archivo de composición de Docker, agregando un parámetro -d para Indica que el contenedor se está ejecutando en segundo plano. Dado que el conocimiento en nuestro archivo docker-compose escribe el empaquetado del contenedor SpringBoot, solo se mostrará un contenedor docker al iniciar.
Para verificar si el comando docker-compose tiene efecto, podemos verificarlo a través del comando docker - compose ps .
Aquí usamos el comando docker logs [container id] para ingresar al contenedor y ver la impresión del registro:
docker logs ad83c82b014d
Finalmente, veremos la respuesta relevante a través de la interfaz escrita antes de la solicitud:
Se ha construido la versión básica del caso de composición SpringBoot + Docker. Recuerda la imagen que dibujé al principio:
Por lo general, en el desarrollo real, el contenedor de la ventana acoplable al que nos enfrentamos no es tan simple y puede depender de varios contenedores, entonces, ¿cómo escribir el archivo de composición de la ventana acoplable en este momento?
A continuación agregamos la dependencia de MySQL y MongoDB al proyecto SpringBoot original . Para facilitar la simulación del siguiente escenario, aquí agregamos dos clases de entidad:
Clase de usuario
package com.sise.docker.domain;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author idea
* @data 2019/11/23
*/
@AllArgsConstructor
@NoArgsConstructor
@Data
public class User {
private Integer id;
private String username;
}
Categoría de automóvil:
package com.sise.docker.domain;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.data.annotation.Id;
/**
* @author idea
* @data 2019/11/23
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Car {
@Id
private Integer id;
private String number;
}
Aumente el contenido de dependencia de pom para mongodb y mysql
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.21</version>
</dependency>
Escribe la capa de dao relevante:
package com.sise.docker.dao;
import com.sise.docker.domain.Car;
import org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.stereotype.Repository;
/**
* @author idea
* @data 2019/11/23
*/
@Repository
public interface CarDao extends MongoRepository<Car, Integer> {
}
package com.sise.docker.dao;
import com.sise.docker.domain.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Repository;
import java.sql.ResultSet;
import java.sql.SQLException;
/**
* @author idea
* @data 2019/11/23
*/
@Repository
public class UserDao {
@Autowired
private JdbcTemplate jdbcTemplate;
public void insert() {
String time = String.valueOf(System.currentTimeMillis());
String sql = "insert into t_user (username) values ('idea-" + time + "')";
jdbcTemplate.update(sql);
System.out.println("==========执行插入语句==========");
}
class UserMapper implements RowMapper<User> {
@Override
public User mapRow(ResultSet resultSet, int i) throws SQLException {
User unitPO = new User();
unitPO.setId(resultSet.getInt("id"));
unitPO.setUsername(resultSet.getString("username"));
return unitPO;
}
}
}
Agregue la entrada de función relevante en el controlador:
package com.sise.docker.controller;
import com.sise.docker.dao.CarDao;
import com.sise.docker.dao.UserDao;
import com.sise.docker.domain.Car;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Random;
/**
* @author idea
* @data 2019/11/20
*/
@RestController
@RequestMapping(value = "/docker")
public class DockerController {
@Autowired
private UserDao userDao;
@Autowired
private CarDao carDao;
@GetMapping(value = "/insert-mongodb")
public String insertMongoDB() {
Car car = new Car();
car.setId(new Random().nextInt(15000000));
String number = String.valueOf(System.currentTimeMillis());
car.setNumber(number);
carDao.save(car);
return "this is insert-mongodb";
}
@GetMapping(value = "/insert-mysql")
public String insertMySQL() {
userDao.insert();
return "this is insert-mysql";
}
@GetMapping(value = "/test2")
public String test() {
System.out.println("=========docker test222=========");
return "this is docker test";
}
}
Agregue el contenido correspondiente al archivo docker-compose.yml original, principalmente para aumentar los módulos dependientes para mongodb y mysql,
#docker引擎对应所支持的docker-compose文本格式
version: '3'
services:
#服务的名称
springboot-docker:
container_name: docker-springboot
build:
context: .
dockerfile: springboot-dockerfile
ports:
- "7089:7089"
depends_on:
- mongodb
mongodb:
#容器的名称
container_name: docker-mongodb
image: daocloud.io/library/mongo:latest
ports:
- "27017:27017"
mysql:
#镜像的版本
image: mysql:5.7
container_name: docker-mysql
ports:
- 3309:3306
environment:
MYSQL_DATABASE: test
MYSQL_ROOT_PASSWORD: root
MYSQL_ROOT_USER: root
MYSQL_ROOT_HOST: '%'
Aquí trato de distinguir el archivo application.yml a través de diferentes perfiles:
En respuesta a la pregunta de un lector en el artículo anterior, hay una forma de pensar sobre la designación de diferentes configuraciones en diferentes entornos. Springboot aún mantiene el perfil original para identificar la configuración de diferentes entornos. La configuración leída después de un paquete específico se puede leer a través de springboot- El parámetro ENTRYPOINT de este archivo dockerfile se especifica, por ejemplo, el siguiente formato:
FROM openjdk:8-jdk-alpine
VOLUME /tmp
ADD target/springboot-docker.jar springboot-docker.jar
#设置镜像的时区,避免出现8小时的误差
ENV TZ=Asia/Shanghai
EXPOSE 7089
#这里可以通过-D参数在对jar打包运行的时候指定需要读取的配置问题
ENTRYPOINT ["java","-Xms256m","-Xmx512m","-Dspring.profiles.active=prod","-jar","springboot-docker.jar"]
El último es el contenido de nuestro archivo de configuración yml. Debido a la dependencia del contenedor docker de la clase de configuración, el método de escritura para yml ya no es acceder a la base de datos correspondiente a través de ip, sino lograr el objetivo a través del mapeo del nombre del servicio.
application-prod.yml
server:
port: 7089
spring:
data:
mongodb:
uri: mongodb://mongodb:27017
database: test
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://mysql:3306/test?useUnicode=true&characterEncoding=UTF-8
username: root
password: root
Cuando se clasifican el código y los archivos relevantes, el código se envía al servidor para empaquetarlo.
mvn package
Entonces podemos comenzar a docker-compose.
Hay un pequeño pozo al que prestar atención. Dado que hemos empaquetado un contenedor springboot separado antes, daremos prioridad al uso del contenedor existente en lugar de recrear el contenedor al ejecutar el comando docker-compose up.
En este momento, debe eliminar manualmente la imagen de la imagen original antes de empaquetar:
[root@izwz9ic9ggky8kub9x1ptuz springboot-docker]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
springboot-docker latest 86f32bd9257f 4 hours ago 128MB
<none> <none> 411616c3d7f7 2 days ago 679MB
<none> <none> 77044e3ad9c2 2 days ago 679MB
<none> <none> 5d9328dd1aca 2 days ago 679MB
springbootmongodocker_springappserver latest 36237acf08e1 3 days ago 695MB
Comando para eliminar espejo:
docker rmi 【镜像id】
En este punto, puede volver a empaquetar la instrucción docker-compose:
docker-compose up
Después del inicio, puede utilizar algunas instrucciones que vienen con docker-compose para operar. Algunas de las instrucciones más utilizadas se resumen a continuación:
docker-compose [Comando]
Commands:
build 构建或重建服务
bundle 从compose配置文件中产生一个docker绑定
config 验证并查看compose配置文件
create 创建服务
down 停止并移除容器、网络、镜像和数据卷
events 从容器中接收实时的事件
exec 在一个运行中的容器上执行一个命令
help 获取命令的帮助信息
images 列出所有镜像
kill 通过发送SIGKILL信号来停止指定服务的容器
logs 从容器中查看服务日志输出
pause 暂停服务
port 打印绑定的公共端口
ps 列出所有运行中的容器
pull 拉取并下载指定服务镜像
push Push service images
restart 重启YAML文件中定义的服务
rm 删除指定已经停止服务的容器
run 在一个服务上执行一条命令
scale 设置指定服务运行容器的个数
start 在容器中启动指定服务
stop 停止已运行的服务
top 显示各个服务容器内运行的进程
unpause 恢复容器服务
up 创建并启动容器
version 显示Docker-Compose版本信息
Finalmente, verifique la interfaz correspondiente:
He subido el código completo correspondiente a la dirección del albergue y los amigos que lo necesitan pueden descargarlo.
Dirección de código: https://gitee.com/IdeaHome_admin/wfw
Después de la práctica, es posible que sienta que con docker-compose es particularmente fácil administrar varios contenedores de la ventana acoplable.
Pero a menudo no es tan simple en la realidad. Docker-compose tiene un inconveniente, es decir , no puede administrar contenedores de docker entre máquinas .
Por lo tanto, con el desarrollo de la tecnología, una tecnología llamada Kubernetes ha aparecido lentamente detrás . Kubernetes (comúnmente conocido como k8s) es un código abierto que se utiliza para administrar aplicaciones en contenedores en múltiples hosts en la plataforma en la nube. El objetivo de Kubernetes es hacer que la implementación de aplicaciones en contenedores sea simple y eficiente. Kubernetes proporciona implementación y planificación de aplicaciones , Actualizar y mantener un mecanismo.
Las tecnologías como Kubernetes son difíciles de iniciar para Xiaobai, y puedo tomarme el tiempo para escribir un artículo introductorio de k8s adecuado para que Xiaobai lo lea.