spring cloud 与 docker-compose构建微服务


    1. 前言
    1. 目录结构
    1. 配置文件的修改
    1. Dockerfile文件
    1. bash脚本
    1. docker-compose.yml文件
    1. 编译与运行
    1. 综上

1. 前言


上一篇文章中讲了服务注册中心eureka-server、服务消费者service-a、服务提供者service-b,service-aservice-b在服务注册中心中进行服务注册之后,service-a成功调用service-b的服务,整个应用比较简单,没有太多的难点,而spring cloud应用打进docker中运行也是十分简单的,因此这次再次尝试了下将三个服务都使用docker运行起来,之前两次也试过编写dockerfile文件,然后打成镜像,运行容器,但是当时的方法一直是将整个源码都打入到镜像中,然后运行容器的时候,需要去官网下载jar包,应用编译打包才能运行起来,太过耗时,因此这次经过多次尝试以后,成功将打包出来的jar文件打进镜像中,打包镜像、启动容器都十分简单快捷,无需那么多的操作,这次就把这些过程完全的讲述一遍。

2. 目录结构


如上次一般,这次依然是要展示一下当前工程的目录结构,在上个应用的基础上,就只是增加了几个模块的Dockerfile文件和docker-compose.yml文件和一个用来打出镜像的bash脚本,整体目录如下:

.
├── build.gradle
├── buildDockerImage.sh
├── docker-compose.yml
├── eureka-server
│   ├── Dockerfile
│   ├── build.gradle
│   └── src
│       └── main
│           ├── java
│           │   └── cn
│           │       └── com
│           │           └── enreka
│           │               └── EurekaServerApplication.java
│           └── resources
│               └── bootstrap.yml
├── gradlew
├── gradlew.bat
├── service-a
│   ├── Dockerfile
│   ├── build.gradle
│   └── src
│       └── main
│           ├── java
│           │   └── cn
│           │       └── com
│           │           └── devh
│           │               ├── A1ServiceApplication.java
│           │               ├── controllers
│           │               │   └── AServiceController.java
│           │               └── fegin
│           │                   └── ServiceBClient.java
│           └── resources
│               ├── application.yml
│               └── bootstrap.yml
├── service-b
│   ├── Dockerfile
│   ├── build.gradle
│   └── src
│       └── main
│           ├── java
│           │   └── cn
│           │       └── com
│           │           └── devh
│           │               ├── B1ServiceApplication.java
│           │               └── controllers
│           │                   └── ServiceB1Controller.java
│           └── resources
│               ├── application.yml
│               └── bootstrap.yml
├── settings.gradle
└── zuul
    ├── build.gradle
    └── src
        └── main
            ├── java
            │   └── cn
            │       └── com
            │           └── zuul
            │               └── ZuulApplication.java
            └── resources

每个模块的Dockerfile文件都在该模块的根目录下面,便于在执行这个Dockerfile文件时方便找到build中的jar包,buildDockerImage.sh脚本用于执行脚本对每个模块中的Dockerfile文件执行然后打出镜像。

docker-compose.yml放在根目录下,便于在所有镜像都完成后直接根据镜像启动容器运行。

3. 配置文件的修改


如果使用docker来运行的话,配置文件也要做一定程度的修改,具体如下:

eureka-server 中的bootstrap.yml:
增加配置:

---
spring:
  profiles: docker
  application:
    name: eureka-server

server:
  port: 8761

eureka:
  instance:
    hostname: eureka-server
    prefer-ip-address: true
    lease-expiration-duration-in-seconds: 30
    lease-renewal-interval-in-seconds: 30
  client:
    registerWithEureka: false
    fetchRegistry: false
    serviceUrl:
            defaultZone: http://eureka-server:8761/eureka/
  server:
    enable-self-preservation: false

service-a中的bootstrap.yml,
增加配置:

---
spring:
  profiles: docker
  application:
    name: service-a

server:
    port: 8080

eureka:
  instance:
    hostname: service-a
    prefer-ip-address: true
  client:
    registerWithEureka: true
    fetchRegistry: true
    serviceUrl:
            defaultZone: http://eureka-server:8761/eureka/

service-b中的bootstrap.yml:
增加配置:

---
spring:
  profiles: docker
  application:
    name: service-b

server:
  port: 8070

eureka:
  instance:
    hostname: service-b
    prefer-ip-address: true
  client:
    registerWithEureka: true
    fetchRegistry: true
    serviceUrl:
            defaultZone: http://eureka-server:8761/eureka/

以上就是每一个一个配置选项,以这个环境运行后,就是docker容器之间进行通讯。

4. Dockerfile文件

除去以上给每个服务新的配置以外,我们还需要将三个服务打到镜像中的Dockerfile脚本,具体如下:

4.1 eureka server Dockerfile文件


FROM java:8

RUN mkdir /app
WORKDIR /app
COPY build/libs/eureka-server.jar /app
ENTRYPOINT ["java", "-Djava.security.egd=file:/dev/./urandom", "-jar", "/app/eureka-server.jar", "--spring.profiles.active=docker"]
EXPOSE 8761

Dockerfile中的指令不做描述,不明白的可以找一篇基础文章看看,以前编写Dockerfile的时候,都是将整个应用的源码全部copy到基础镜像之中,这样造成的问题就是运行容器的时候需要相当长的时间去下载依赖和编译jar,但是直接将已经编译打包好的jar copy到容器中是最为方便的,之前也是这样尝试过,但是一直是失败的,可能原因是选择的目录不正确的原因。

4.2 service-a、service-b Dockerfile文件


service-a和service-b的Dockerfile文件类似,如下:

service-a:

FROM java:8

RUN mkdir /app
WORKDIR /app
COPY build/libs/service-a.jar /app
ENTRYPOINT ["java", "-Djava.security.egd=file:/dev/./urandom", "-jar", "/app/service-a.jar", "--spring.profiles.active=docker"]
EXPOSE 8080

service-b:

FROM java:8

RUN mkdir /app
WORKDIR /app
COPY build/libs/service-b.jar /app
ENTRYPOINT ["java", "-Djava.security.egd=file:/dev/./urandom", "-jar", "/app/service-b.jar", "--spring.profiles.active=docker"]
EXPOSE 8070

对外暴露的端口分别是8080和8070,同时都指定了运行的profile为docker,即我们新编写的配置文件。

5. bash脚本


bash脚本的用途是分别执行几个module下的Dockerfile文件,分别打成镜像,不用一个个手工执行,文件如下:

buildDockerImage.sh:

#!/usr/bin/env bash

set -eo pipefail

modules=( eureka-server service-a service-b )

for module in "${modules[@]}"; do
    docker build -t "microservice/${module}:latest" ${module}
done

使用十分简单,就是使用docker build分别执行每个模块下的Dockerfile文件,然后简便起见,直接就是最新的版本,其实按照规范一点,最好是每次打出的镜像的版本号都是依次递增的,这样回退是十分方便的。

6. docker-compose.yml文件


最后就是docker-compose.yml文件,这个脚本中的内容是一次性启动这几个服务,并实现服务之间的通信,脚本内容如下:

docker-compose.yml:

eureka-server:
  image: microservice/eureka-server
  hostname: eureka-server
  ports:
    - "8761:8761"

service-b:
  image:  microservice/service-b
  ports:
    - "8070:8070"
  links:
    - "eureka-server"

service-a:
  image:  microservice/service-a
  ports:
    - "8080:8080"
  links:
    - "eureka-server"
    - "service-b"

三个服务,使用的image都是先前打出的镜像,然后容器内的端口映射物理机上的端口,最后是通信的模块,这因此也是先前的配置文件之中不再是指定的ip,而是服务名, 这里就可以通过服务名来找到对应的服务。

7. 编译与运行


我们执行脚本buildDockerImage.sh,控制台输出如下:

如此几步新的镜像就已经成功打成功了,可以执行命令docker images查看镜像,如下:

这就是成功打出的三个镜像,现在根据这三个镜像运行对应的容器即可,在工程目录下,执行命令docker-compose up -d启动容器并运行,并执行docker ps查看运行起来的容器,如下图:

三个服务已经成功启动,现在访问http://192.168.99.100:8761/,这是先前docker配置的本地的ip192.168.99.100,8761对应的服务为eureka,访问如下:

两个服务已经注册成功,访问对应的service-a提供的服务http://192.168.99.100:8080/,得到如下结果:

与上文相同,三个服务在docker容器中运行正常,相互之间通信也正常。

8. 综上


将三个服务使用docker运行起来,实现互相之间的通信,并不是十分的困难,是一个使用docker构建整套微服务的开始,后续将有更多的docker来参与服务的构建运行,下一篇文章讲述上篇文章中提到的几个服务注册注解的使用与不同。

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐