Docker从入门到掉坑(三):Docker compose技术

本文涉及的产品
云数据库 RDS MySQL Serverless,0.5-2RCU 50GB
云数据库 MongoDB,通用型 2核4GB
简介: Docker从入门到掉坑(三):Docker compose技术

前边的两篇文章里面,我们讲解了基于docker来部署基础的SpringBoot容器,如果阅读本文之前没有相关基础的话,可以点击这里,回看之前的教程。


不知道大家再初次使用docker的时候是否又遇到这种场景,每次部署微服务都是需要执行docker run xxx,docker kill xxx 等命令来操作容器。假设说一个微服务架构里面包含有多个微服务,那么对于每个微服务的部署岂不是都需要手动编写命令来启动和关闭,这样做就会增加运维人员的开发工作量,同时也容易出错。


Docker Compose 编排技术


在前边的文章中,我们讲解了Docker容器化技术的发展,但是随着我们的Docker越来越多的时候,对于容器的管理也是特别麻烦,因此Docker Compose技术也就诞生了。

Docker Compose技术是通过一份文件来定义和运行一系列复杂应用的Docker工具,通过Docker-compose文件来启动多个容器,


网上有很多关于Docker-compose的实战案例,但是都会有些细节地方有所遗漏,所以下边我将通过一个简单的案例一步步地带各位从浅入深地对Docker-compose进行学习。


基于Docker Compose来进行对SpringBoot微服务应用的打包集成


我们还是按照老样子来构建一套基础的SpringBoot微服务项目,首先我们来看看基础版本的项目结构:


网络异常,图片无法展示
|


首先是我们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>
复制代码


然后是java程序的内容代码,这里面有常规的controller,application类,代码如下所示:


启动类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);
    }
}
复制代码


控制器 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配置文件:


server:
  port: 7089
复制代码


接下来便是docker-compose打包时候要用到的配置文件了。这里采用的方式通常都是针对必要的docker容器编写一份dockerfile,然后统一由Docker Compose进行打包管理,假设我们的微服务中需要引用到了MySQL,MongoDB等应用,那么整体架构如下图所示:


网络异常,图片无法展示
|


那么我们先从简单的单个容器入手,看看该如何对SpringBoot做Docker Compose的管理,下边是一份打包SpringBoot进入Docker容器的Dockerfile文件:


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


接着便是加入docker-compose.yml文件的环节了,下边是脚本的内容:


#docker引擎对应所支持的docker-compose文本格式
version: '3'
services:
  #服务的名称
  springboot-docker:
    build:
      context: .
      # 构建这个容器时所需要使用的dockerfile文件
      dockerfile: springboot-dockerfile
    ports:
      # docker容器和宿主机之间的端口映射
      - "7089:7089"
复制代码


docker-compose.ym配置文件有着特殊的规则,通常我们都是先定义version版本号,然后便是列举一系列与容器相关的services内容。


接下来将这份docker服务进行打包,部署到相关的linux服务器上边,这里我采用的是一台阿里云上边购买的服务器来演示。


网络异常,图片无法展示
|


目前该文件还没有进行打包处理,所以没有target目录,因此dockerfile文件构建的时候是不会成功的,因此需要先进行mvn的打包:


mvn package
复制代码


接着便是进行Docker-Compose命令的输入了:


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


你会发现这次输入的命令和之前教程中提及的docker指令有些出入,变成了docker-compose 指令,这条指令是专门针对Docker compose文件所设计的,加入了一个-d的参数用于表示后台运行该容器。由于我们的docker-compose文件中知识编写了对于SpringBoot容器的打包,因此启动的时候只会显示一个docker容器。


为了验证docker-compose指令是否生效,我们可以通过docker–compose ps命令来进行验证。


这里边我们使用 docker logs [容器id] 指令可以进入容器查看日志的打印情况:


docker logs ad83c82b014d
复制代码


网络异常,图片无法展示
|


最后我们通过请求之前写好的接口便会看到相关的响应:


网络异常,图片无法展示
|


基础版本的SpringBoot+Docker compose案例已经搭建好了,还记得我在开头画的那张图片吗:


网络异常,图片无法展示
|


通常在实际开发中,我们所面对的docker容器并不是那么的简单,还有可能会依赖到多个容器,那么这个时候该如何来编写docker compose文件呢?下边我们对原先的SpringBoot项目增加对于MySQLMongoDB的依赖,为了方便下边的场景模拟,这里我们增加两个实体类:


用户类


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;
}
复制代码


汽车类:


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;
}
复制代码


增加对于mongodb,mysql的pom依赖内容


<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>
复制代码


编写相关的dao层:


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;
        }
    }
}
复制代码


在控制器中添加相关的函数入口:


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";
    }
}
复制代码


对原先的docker-compose.yml文件添加相应的内容,主要是增加对于mongodb和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: '%'
复制代码


这里头我尝试将application.yml文件通过不同的profile来进行区分:


网络异常,图片无法展示
|


应上篇文章中有读者问到,不同环境不同配置的指定问题,这里有一种思路,

springboot依旧保持原有的按照profile来识别不同环境的配置,具体打包之后读取的配置,可以通过springboot-dockerfile这份文件的ENTRYPOINT 参数来指定,例如下边这种格式:


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"]
复制代码


最后便是我们的yml配置文件内容,由于配置类docker容器的依赖,所以这里面对于yml的写法不再是通过ip来访问相应的数据库了,而是需要通过service-name的映射来达成目标。


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
复制代码


当相关的代码和文件都整理好了之后,将这份代码发送到服务器上进行打包。


mvn package
复制代码


接着我们便可以进行docker-compose的启动了。


这里有个小坑需要注意一下,由于之前我们已经对单独的springboot容器进行过打包了,所以在执行docker-compose up指令的时候会优先使用已有的容器,而不是重新创建容器。


这个时候需要先将原先的image镜像进行手动删除,再打包操作:


[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
复制代码


删除镜像的命令:


docker rmi 【镜像id】
复制代码


网络异常,图片无法展示
|


此时再重新进行docker-compose指令的打包操作即可:


docker-compose up
复制代码


网络异常,图片无法展示
|


启动之后,可以通过docker-compose自带的一些指令来进行操作,常用的一些指令我都归纳在了下边:


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版本信息
复制代码


最后对相应的接口做检测:


网络异常,图片无法展示
|


网络异常,图片无法展示
|


相关的完整代码我已经上传到了gitee地址,如果有需要的朋友可以前往进行下载。

gitee.com/IdeaHome_ad…


网络异常,图片无法展示
|


实践完毕之后,你可能会觉得有了docker-compose之后,对于多个docker容器来进行管理显得就特别轻松了。


但是往往现实中并没有这么简单,docker-compose存在着一个弊端,那就是不能做跨机器之间的docker容器进行管理。因此随者技术的发展,后边也慢慢出现了一种叫做Kubernetes的技术。Kubernetes(俗称k8s)是一个开源的,用于管理云平台中多个主机上的容器化的应用,Kubernetes的目标是让部署容器化的应用简单并且高效(powerful),Kubernetes提供了应用部署,规划,更新,维护的一种机制。


Kubernetes这类技术对于小白来说入门的难度较高,后边可能会抽空专门来写一篇适合小白阅读的k8s入门文章。

相关实践学习
容器服务Serverless版ACK Serverless 快速入门:在线魔方应用部署和监控
通过本实验,您将了解到容器服务Serverless版ACK Serverless 的基本产品能力,即可以实现快速部署一个在线魔方应用,并借助阿里云容器服务成熟的产品生态,实现在线应用的企业级监控,提升应用稳定性。
云原生实践公开课
课程大纲 开篇:如何学习并实践云原生技术 基础篇: 5 步上手 Kubernetes 进阶篇:生产环境下的 K8s 实践 相关的阿里云产品:容器服务&nbsp;ACK 容器服务&nbsp;Kubernetes&nbsp;版(简称&nbsp;ACK)提供高性能可伸缩的容器应用管理能力,支持企业级容器化应用的全生命周期管理。整合阿里云虚拟化、存储、网络和安全能力,打造云端最佳容器化应用运行环境。 了解产品详情:&nbsp;https://www.aliyun.com/product/kubernetes
目录
相关文章
|
1月前
|
存储 机器学习/深度学习 中间件
快速上手 Elasticsearch:Docker Compose 部署详解
本文介绍了如何使用Docker Compose快速搭建Elasticsearch学习环境。Elasticsearch是一款用于实时搜索和分析的分布式中间件,适用于多种场景,如搜索、日志分析、机器学习等。首先,创建docker网络,拉取最新版8.12.2镜像。接着,编写docker-compose.yml文件,配置单节点集群,设置端口映射、内存限制及数据卷挂载。然后,创建并配置数据卷目录,允许远程访问和跨域。最后,启动服务并验证,通过浏览器访问确认服务运行正常。本文为初学者提供了一个简便的Elasticsearch部署方法。
171 4
快速上手 Elasticsearch:Docker Compose 部署详解
|
1月前
|
关系型数据库 MySQL Java
Docker Compose详细教程(从入门到放弃)
Docker Compose详细教程(从入门到放弃)
186 0
|
1月前
|
Java Go 开发者
Docker容器技术简介及其与Go语言的结合点
【2月更文挑战第23天】本文首先概述了Docker容器技术的核心概念和优势,接着探讨了Go语言与Docker容器技术的结合点。通过阐述Docker的轻量级、可移植性和版本控制等特性,以及Go语言在容器化应用中的优势,本文旨在说明两者结合能够实现更高效、灵活的应用开发和部署。
|
1月前
|
Java Docker 微服务
如何使用Docker和Docker Compose部署微服务
【2月更文挑战第12天】
250 0
|
1天前
|
弹性计算 测试技术 持续交付
Docker技术这些应用场景,你知道吗?
Docker技术这些应用场景,你知道吗?
|
2天前
|
监控 Docker 容器
Docker从入门到精通:Docker log 命令学习
了解 Docker 日志管理对容器监控至关重要。`docker logs` 命令用于查看和管理容器日志,例如,`docker logs &lt;container_name&gt;` 显示容器日志,`-f` 或 `--follow` 实时跟踪日志,`--tail` 显示指定行数,`--timestamps` 添加时间戳,`--since` 按日期筛选。Docker 支持多种日志驱动,如 `syslog`,可通过 `--log-driver` 配置。有效管理日志能提升应用程序的稳定性和可维护性。
5 0
|
6天前
|
机器学习/深度学习 数据挖掘 Docker
《Docker 简易速速上手小册》第1章 Docker 基础入门(2024 最新版)
《Docker 简易速速上手小册》第1章 Docker 基础入门(2024 最新版)
20 1
|
14天前
|
存储 Ubuntu 关系型数据库
Docker从入门到精通:ubuntu系统安装docker
本文介绍了Docker的安装过程。首先,文章简述了Docker的组成部分:镜像(如模板用于创建容器服务)、容器(运行应用的独立实体)和仓库(存储镜像的库)。接着,针对Ubuntu系统,详细阐述了卸载旧版本Docker、更新软件包、安装依赖、添加Docker官方GPG密钥和软件源、安装Docker以及配置用户组的步骤。最后,通过启动Docker、验证安装是否成功(运行`hello-world`镜像)和查看Docker版本来确认安装完成。
53 12
|
22天前
|
Ubuntu Linux Shell
docker学习笔记3,从入门到精通
docker学习笔记3,从入门到精通
|
22天前
|
应用服务中间件 Shell nginx
docker学习笔记2,入门到精通
docker学习笔记2,入门到精通