Docker from entry to drop (3): too many containers, optimized orchestration

    In the previous two articles, we explained the SpringBoot container based on docker deployment. If there is no relevant foundation before reading this article, you can review the previous tutorial.

Docker from entry to pit

Docker from entry to pitfall (2): build SpringBoot microservices based on Docker

    I don't know if you encountered this scenario when you first used docker. Every time you deploy a microservice, you need to execute commands such as docker run xxx, docker kill xxx to operate the container. Assuming that a system relies on multiple docker containers, does the deployment of each docker container need to manually write commands to start and shut down? Doing so will increase the development workload of operation and maintenance personnel and is also prone to errors.

Docker Compose orchestration technology

In the previous article, we explained the development of Docker containerization technology, but as we have more and more Docker , the management of containers is also particularly troublesome, so Docker Compose technology was born.

Docker Compose technology is a Docker tool that defines and runs a series of complex applications through a file, and starts multiple containers through a Docker-compose file. There are many actual cases of Docker-compose on the Internet, but some details are omitted. , So below, I will take you step by step to learn Docker-compose from the shallower to the deeper through a simple case.

Based on Docker Compose to package and integrate SpringBoot microservice applications

Let's build a basic SpringBoot microservice project as usual. First, let's take a look at the project structure of the basic version:

The first is the configuration content of our pom file:

 

<?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>

Then there is the content code of the java program. There are conventional controller and application classes. The code is as follows:

Start class Application

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);
    }
}

Controller 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";
    }
}

yml configuration file:

server:
  port: 7089

Next is the configuration file used when docker-compose is packaged. The method used here is usually to write a dockerfile for the necessary docker container, and then package and manage it by Docker Compose. Assuming that our microservices need to reference applications such as MySQL and MongoDB, the overall architecture is shown in the following figure:

So let's start with a simple single container and see how to manage SpringBoot with Docker Compose. Below is a Dockerfile that packages SpringBoot into the Docker container:

 

#需要依赖的其他镜像
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"]

Then there is the link to add the docker-compose.yml file. Below is the content of the script:

#docker引擎对应所支持的docker-compose文本格式
version: '3'
services:

  #服务的名称
  springboot-docker:
    build:
      context: .
      # 构建这个容器时所需要使用的dockerfile文件
      dockerfile: springboot-dockerfile
    ports:
      # docker容器和宿主机之间的端口映射
      - "7089:7089"

The docker-compose.ym configuration file has special rules. Usually we first define the version number, and then list a series of container-related services.

Next, package this docker service and deploy it to the relevant linux server. Here I use a server purchased from Alibaba Cloud to demonstrate.

At present, the file has not been packaged, so there is no target directory, so when the dockerfile file is built, it will not be successful, so you need to package mvn first:

 

mvn package

Next is the input of the Docker-Compose command:

[root@izwz9ic9ggky8kub9x1ptuz springboot-docker]# docker-compose up -d
Starting springboot-docker_springboot-docker_1 ... done
[root@izwz9ic9ggky8kub9x1ptuz springboot-docker]# 

You will find that the command entered this time is somewhat different from the docker command mentioned in the previous tutorial. It becomes the docker-compose command. This command is specifically designed for the Docker compose file, adding a -d parameter for Indicates that the container is running in the background. Since the knowledge in our docker-compose file writes the packaging of the SpringBoot container, only one docker container will be displayed when starting.

In order to verify whether the docker-compose command takes effect, we can verify it through the docker--compose ps command.

Here we use the  docker logs [container id]  command to enter the container to view the log printing:

docker logs ad83c82b014d

 

 

Finally, we will see the relevant response through the interface written before the request:  

 

The basic version of the SpringBoot+Docker compose case has been built. Remember the picture I drew at the beginning:

 

Usually in actual development, the docker container we are facing is not so simple, and it may depend on multiple containers, so how to write the docker compose file at this time?

Below we add dependency on MySQL and MongoDB to the original SpringBoot project . In order to facilitate the simulation of the following scenario, here we add two entity classes:

User class

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;
}

Automobile category:

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;
}

Increase the pom dependency content for mongodb and 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>

Write the relevant dao layer:

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;
        }
    }
}

Add the relevant function entry in the controller:

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";
    }
}

Add the corresponding content to the original docker-compose.yml file, mainly to increase the dependent modules for mongodb and 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: '%'

Here I try to distinguish the application.yml file through different profiles:

 

In response to a reader's question in the previous article, there is a way of thinking about the designation of different configurations in different environments. Springboot still maintains the original profile to identify the configuration of different environments. The configuration read after specific packaging can be read through springboot- The ENTRYPOINT parameter of this dockerfile file is specified, for example, the following format:

 

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"]

The last is the content of our yml configuration file. Due to the dependency of the configuration class docker container, the writing method for yml is no longer to access the corresponding database through ip, but to achieve the goal through service-name mapping.

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&amp;characterEncoding=UTF-8
             username: root
             password: root

When the relevant code and files are sorted out, the code is sent to the server for packaging.

mvn package

Then we can start docker-compose.

There is a small pit to pay attention to. Since we have packaged a separate springboot container before, we will give priority to using the existing container instead of recreating the container when executing the docker-compose up command.


At this time, you need to manually delete the original image image before packaging:

[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

Command to delete mirror:

docker rmi 【镜像id】


At this point, you can re-pack the docker-compose instruction:

docker-compose up

 

After startup, you can use some instructions that come with docker-compose to operate. Some of the commonly used instructions are summarized below:

 

docker-compose [Command]

 

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版本信息

Finally, check the corresponding interface:

I have uploaded the relevant complete code to the gitee address, and friends who need it can download it.

Code address: https://gitee.com/IdeaHome_admin/wfw

After practice, you may feel that with docker-compose, it is particularly easy to manage multiple docker containers.

But it is often not so simple in reality. docker-compose has a drawback, that is , it cannot manage docker containers across machines .

Therefore, with the development of technology, a technology called Kubernetes has slowly appeared behind . Kubernetes (commonly known as k8s) is an open source, used to manage containerized applications on multiple hosts in the cloud platform. The goal of Kubernetes is to make the deployment of containerized applications simple and efficient. Kubernetes provides application deployment and planning , Update and maintain a mechanism.

Technologies such as Kubernetes are difficult for Xiaobai to get started, and I may take the time to write a k8s introductory article suitable for Xiaobai to read.

 

 

 

Guess you like

Origin blog.csdn.net/bj_chengrong/article/details/103234077