本文是《Docker下,两分钟极速体验Nacos》的续篇,前文我们极速体验了Nacos注册中心、服务提供者、服务消费者,这些应用都对应着不同的Docker容器,今天就来细说这些Docker容器的镜像。
回顾上一章的业务流程
先来回顾一下上一章,整个Docker环境中有哪些容器,提供了什么服务,如下图,请顺着橙色提示框的数字顺序来看请整个流程::
系列文章链接
下面是《Spring Cloud Alibaba实战系列》的所有文章地址:
Nacos环境背后对应的Docker技术
在Docker下搭建一个包含Nacos注册中心、服务提供者、服务消费者的环境,总的来说需要做下面这些事情:
- 制作Nacos镜像;
- 制作服务提供者镜像;
- 制作服务消费者镜像;
- 制作docker-compose.yml文件,将容器编排在一起,然后一次性启动;
接下来我们逐个开发上面提到的内容;
源码下载
如果您不打算写代码,也可以从GitHub上下载本次实战的源码,地址和链接信息如下表所示:
名称
链接
备注
项目主页
https://github.com/zq2599/blog\_demos
该项目在GitHub上的主页
git仓库地址(https)
https://github.com/zq2599/blog\_demos.git
该项目源码的仓库地址,https协议
git仓库地址(ssh)
git@github.com:zq2599/blog_demos.git
该项目源码的仓库地址,ssh协议
这个git项目中有多个文件夹,本章的应用在nacosdemo文件夹下,如下图所示:
制作Nacos镜像
对Nacos镜像的功能要求有以下三点:
包含Nacos server应用;
暴露web管理服务的端口;
容器启动时,Nacos服务能够自动启动;
为了满足以上要求,除了编写Dockerfile文件,还要编写docker-entrypoint.sh文件,在容器创建时执行该文件用于启动Nacos服务;
首先是Dockerfile文件,该文件用于制作Nacos镜像:
# Docker image for Anaconda3-2019.03
# VERSION 0.0.1
# Author: bolingcavalry
### 基础镜像,使用alpine操作系统,openjkd使用8u201
FROM openjdk:8u201-jdk-alpine3.9
#作者
MAINTAINER BolingCavalry <zq2599@gmail.com>
#系统编码
ENV LANG=C.UTF-8 LC_ALL=C.UTF-8
#path
ENV PATH /opt/conda/bin:$PATH
#安装必要的软件
#RUN apt-get update --fix-missing && apt-get install -y wget
RUN apk update && apk add wget
#下载下来的压缩文件名称
ENV NACOS_FILE_NAME nacos-server-1.1.0.tar.gz
#把启动时用到的文件准备好
COPY ./docker-entrypoint.sh /docker-entrypoint.sh
#解压后的文件夹名称
ENV NACOS_FOLDER_NAME nacos
RUN wget https://github.com/alibaba/nacos/releases/download/1.1.0/nacos-server-1.1.0.tar.gz -O ~/$NACOS_FILE_NAME && \
tar -zxf ~/$NACOS_FILE_NAME -C ~/ && \
rm ~/$NACOS_FILE_NAME && \
chmod a+x /docker-entrypoint.sh
ENTRYPOINT ["/docker-entrypoint.sh"]
EXPOSE 8848
从Dockerfile内容中可见,先去nacos的github下载安装包,然后解压,再复制docker-enrypoint.sh到镜像中,最后将nacos的8848端口暴露出来;
再来看看docker-enrypoint.sh文件的内容,该文件在容器启动时会被执行,内容很简单,就是进入nacos的bin目录,执行启动文件,再将start.out输出到控制台:
#!/bin/sh
echo "Starting nacos"n && \
cd ~/nacos/bin && \
./startup.sh -m standalone && \
cd ../logs && \
tail -f start.out
有两个重要信息需要注意:
- nacos的官方参考启动命令是./startup.sh -m standalone,这个命令会将jvm的输出重定向到start.out文件,也就是说nacos的JVM进程是在后台运行的,不会占用控制台(相比之下,spring boot应用使用java -jar启动时会占用控制台),对于docker来说,容器内的进程如果不占用控制台,docker就认为该容器已经结束工作,就会停止该容器,所以,为了避免nacos在docker刚刚启动就退出,需要用tail -f start.out来占领控制台;
- 用tail -f start.out来占领控制台可以避免容器刚刚启动就退出,但也有个弊端,就是容器中有了多个进程,并且nacos进程的PID不是1,所以在执行docker stop命令时,结束进程的信号量不会到nacos进程,而是去了PID等于1的进程,所以nacos进程不会立即退出,只能等到30秒后被强制kill,这个问题最好的解法是修改nacos的startup.sh,让nacos进程始终保持在控制台,不要重定向到后台,但这样就导致Dockerfile不好处理了,每次下载和解压了nacos安装包后,都要用本地的startup.sh去替换原有的,这样做的话,如果nacos升级版本,这边本地的startup.sh也要随之更新,很是麻烦…
构建镜像
Dockerfile和docker-enrypoint.sh文件准备好之后放在同一个目录,执行以下命令构建镜像:
docker build -t bolingcavalry/nacossimpleprovider:1.0-SNAPSHOT
如果您在hub.docker.com已经注册,可以执行以下命令将本地镜像上传到 hub.docker.com ,这样任何人都可以下载使用该镜像了:
docker push bolingcavalry/nacossimpleprovider:1.0-SNAPSHOT
Nacos镜像的制作已经完成,接下来制作一个java应用的镜像:服务提供者;
java应用的父工程
接下来要开发的simple-provider和simple-consumer两个应用都是java应用,为了管理方便,做一个基于maven的父工程,再将simple-provider和simple-consumer以module的形式加入到这个父工程中;
基于maven创建父工程,名为nacosdemo,其pom.xml内容如下:
4.0.0 simpleconsumer simpleprovider org.springframework.boot spring-boot-starter-parent 2.0.5.RELEASE com.bolingcavalry nacosdemo pom 1.0-SNAPSHOT <java.version>1.8</java.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> <spring.boot.version>2.0.5.RELEASE</spring.boot.version> <spring.cloud.version>Finchley.SR1</spring.cloud.version> <spring.cloud.alibaba.version>0.2.2.RELEASE</spring.cloud.alibaba.version> org.springframework.cloud spring-cloud-dependencies ${spring.cloud.version} pom import org.springframework.cloud spring-cloud-alibaba-dependencies ${spring.cloud.alibaba.version} pom import org.apache.maven.plugins maven-compiler-plugin ${java.version} ${project.build.sourceEncoding}
可见这是个普通的父工程,里面对spring cloud和spring cloud alibaba的版本做了控制,避免子工程还要各种指定版本的繁琐操作;
制作服务提供者镜像
simple-provider是个java web应用,使用了spring cloud alibaba的依赖库之后可以使用Nacos的注册发现服务,整个工程的开发步骤如下:
基于maven创建工程,其pom.xml内容如下:
nacosdemo com.bolingcavalry 1.0-SNAPSHOT 4.0.0 simpleprovider org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-starter-alibaba-nacos-discovery org.springframework.boot spring-boot-maven-plugin com.google.cloud.tools jib-maven-plugin 1.3.0 openjdk:8u201-jdk-alpine3.9 bolingcavalry/nacos${project.artifactId}:${project.version} -Xms1g -Xmx1g
上述内容有两点需要注意:
a. 依赖spring-cloud-starter-alibaba-nacos-discovery,这样可以用上spring cloud nacos的服务;
b. 使用了maven插件jib-maven-plugin,用于将应用构建成docker镜像,此插件相关的详情请参考《Docker与Jib(maven插件版)实战》;
2. 配置文件application.properties,配置应用名称和nacos地址,注意这里nacos地址配置的是nacoshost,对应的是后面docker-compose.yml中的link参数:
spring.application.name=simple-provider
spring.cloud.nacos.discovery.server-addr=nacoshost:8848
应用启动类SimpleProviderApplication ,配置了注解EnableDiscoveryClient,用于启动注册发现服务:
package simpleprovider;
import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@EnableDiscoveryClient @SpringBootApplication public class SimpleProviderApplication {
public static void main(String[] args) { SpringApplication.run(SimpleProviderApplication.class, args); }
}
增加一个提供http服务的controller类ProviderController:
package simpleprovider.controller;
import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController;
import java.text.SimpleDateFormat; import java.util.Date;
/**
- @Description: 提供web服务的controller
- @author: willzhao E-mail: zq2599@gmail.com
- @date: 2019/7/28 11:08
*/ @RestController public class ProviderController {
@RequestMapping(value = "/hello/{name}", method = RequestMethod.GET) public String hello(@PathVariable("name") String name){ return "hello " + name + ", " + new SimpleDateFormat("yyyy-mm-dd HH:mm:ss").format(new Date()); }
}
以上就是simple-provider的所有源码了,在pom.xml所在目录执行以下命令,即可构建docker镜像,存入本地仓库:
mvn compile jib:dockerBuild
制作服务消费者镜像
simple-consumer是个java web应用,启动后对外提供http服务,响应的时候,通过nacos取得simple-provider的地址,然后向simple-provider发请求,将响应返回给浏览器:
基于maven创建工程,其pom.xml内容如下:
nacosdemo com.bolingcavalry 1.0-SNAPSHOT 4.0.0 simpleconsumer org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-starter-alibaba-nacos-discovery org.springframework.boot spring-boot-maven-plugin com.google.cloud.tools jib-maven-plugin 1.3.0 openjdk:8u201-jdk-alpine3.9 bolingcavalry/nacos${project.artifactId}:${project.version} -Xms1g -Xmx1g 8080 配置文件application.properties,配置应用名称和nacos地址,注意这里nacos地址配置的是nacoshost,对应的是后面docker-compose.yml中的link参数:
spring.application.name=simple-consumer spring.cloud.nacos.discovery.server-addr=nacoshost:8848
应用启动类SimpleConsumerApplication,配置了注解EnableDiscoveryClient,用于启动注册发现服务:
package com.bolingcavalry.simpleconsumer;
import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@EnableDiscoveryClient @SpringBootApplication public class SimpleConsumerApplication {
public static void main(String[] args) { SpringApplication.run(SimpleConsumerApplication.class, args); }
}
增加一个提供http服务的controller类ConsumerController,通过LoadBalancerClient取得simple-provider的服务地址,然后发请求过去 :
package com.bolingcavalry.simpleconsumer.controller;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cloud.client.ServiceInstance; import org.springframework.cloud.client.loadbalancer.LoadBalancerClient; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate;
import java.text.SimpleDateFormat; import java.util.Date;
/**
- @Description: 提供web服务的controller
- @author: willzhao E-mail: zq2599@gmail.com
- @date: 2019/7/28 11:08
*/ @RestController public class ConsumerController {
@Autowired LoadBalancerClient loadBalancerClient; @RequestMapping("/test") public String test(){ //根据应用名称取得实例对象 ServiceInstance serviceInstance = loadBalancerClient.choose("simple-provider"); //根据实例对象取得地址 String uri = serviceInstance.getUri().toString(); String result = new RestTemplate().getForObject(uri + "/hello/bolingcavalry", String.class); return "provider uri : " + uri + "<br>" + "response :" + result; }
}
以上就是simple-consumer的所有源码了,在pom.xml所在目录执行以下命令,即可构建docker镜像,存入本地仓库:
mvn compile jib:dockerBuild
编写docker-compose.yml
三个镜像都准备好了,接下来是做容器编排,docker-compose.yml内容如下:
version: '2'
services:
nacos:
image: bolingcavalry/nacosserver:0.0.1
container_name: nacos
restart: unless-stopped
ports:
- '8848:8848'
provider:
image: bolingcavalry/nacossimpleprovider:1.0-SNAPSHOT
links:
- nacos:nacoshost
depends_on:
- nacos
restart: unless-stopped
consumer:
image: bolingcavalry/nacossimpleconsumer:1.0-SNAPSHOT
links:
- nacos:nacoshost
container_name: consumer
depends_on:
- nacos
ports:
- '8080:8080'
restart: unless-stopped
上述编排文件,有几点需要注意:
a. 可以用container_name设置容器的名字,但是不要给provider的容器设置名字,因为按照规划provider会启动多个容器,指定名字会导致第二个容器启动时报名字冲突的错误;
b. provider和consumer通过depends_on参数,将自己的启动时间放在了nacos后面;
c. links的作用是将nacos的IP地址,写入provider和consumer的/etc/hosts文件中,这样这两个容器内容的应用可以通过nacoshost来访问nacos了;
启动多个容器
在docker-compose.yml所在目录执行以下命令,即可启动所有容器,并且provider容器会启动6个:
docker-compose up --scale provider=6 -d
至此,整个nacos的docker环境搭建过程已经回顾完毕,在您搭建自己的容器环境时,希望本文能给您一些参考;
欢迎关注我的公众号:程序员欣宸
本文同步分享在 博客“程序员欣宸”(CSDN)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。