写点什么

Docker 下的 Nacos 环境开发

作者:程序员欣宸
  • 2022 年 10 月 04 日
    广东
  • 本文字数:8804 字

    阅读完需:约 29 分钟

Docker下的Nacos环境开发

欢迎访问我的 GitHub

这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos


  • 本文是《Docker下,两分钟极速体验Nacos》的续篇,前文我们极速体验了 Nacos 注册中心、服务提供者、服务消费者,这些应用都对应着不同的 Docker 容器,今天就来细说这些 Docker 容器的镜像。

回顾上一章的业务流程

  • 先来回顾一下上一章,整个 Docker 环境中有哪些容器,提供了什么服务,如下图,请顺着橙色提示框的数字顺序来看请整个流程::

系列文章链接

  • 下面是《Spring Cloud Alibaba 实战系列》的所有文章地址:


  1. 《Docker下,两分钟极速体验Nacos》

  2. 《Docker下的Nacos环境开发》

  3. 《Docker下,两分钟极速体验Nacos配置中心》

  4. 《Docker下Nacos配置应用开发》

Nacos 环境背后对应的 Docker 技术

  • 在 Docker 下搭建一个包含 Nacos 注册中心、服务提供者、服务消费者的环境,总的来说需要做下面这些事情:


  1. 制作 Nacos 镜像;

  2. 制作服务提供者镜像;

  3. 制作服务消费者镜像;

  4. 制作 docker-compose.yml 文件,将容器编排在一起,然后一次性启动;


  • 接下来我们逐个开发上面提到的内容;

源码下载

  • 如果您不打算写代码,也可以从 GitHub 上下载本次实战的源码,地址和链接信息如下表所示:



  • 这个 git 项目中有多个文件夹,本章的应用在 nacosdemo 文件夹下,如下图所示:

制作 Nacos 镜像

  • 对 Nacos 镜像的功能要求有以下三点:


  1. 包含 Nacos server 应用;

  2. 暴露 web 管理服务的端口;

  3. 容器启动时,Nacos 服务能够自动启动;


  • 为了满足以上要求,除了编写 Dockerfile 文件,还要编写 docker-entrypoint.sh 文件,在容器创建时执行该文件用于启动 Nacos 服务;

  • 首先是 Dockerfile 文件,该文件用于制作 Nacos 镜像:


# Docker image for Anaconda3-2019.03# VERSION 0.0.1# Author: bolingcavalry
### 基础镜像,使用alpine操作系统,openjkd使用8u201FROM openjdk:8u201-jdk-alpine3.9
#作者MAINTAINER BolingCavalry <zq2599@gmail.com>
#系统编码ENV LANG=C.UTF-8 LC_ALL=C.UTF-8#pathENV PATH /opt/conda/bin:$PATH
#安装必要的软件#RUN apt-get update --fix-missing && apt-get install -y wgetRUN 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-entrypoint.sh 到镜像中,最后将 nacos 的 8848 端口暴露出来;再来看看 docker-entrypoint.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-entrypoint.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 内容如下:


<?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>    <modules>        <module>simpleconsumer</module>        <module>simpleprovider</module>    </modules>
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.5.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent>
<groupId>com.bolingcavalry</groupId> <artifactId>nacosdemo</artifactId> <packaging>pom</packaging> <version>1.0-SNAPSHOT</version>
<properties> <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> </properties>
<!-- 依赖声明 --> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring.cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-alibaba-dependencies</artifactId> <version>${spring.cloud.alibaba.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>
<!--编译管理 jdk版本和字符集编码--> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>${java.version}</source> <target>${java.version}</target> <encoding>${project.build.sourceEncoding}</encoding> </configuration> </plugin> </plugins> </build></project>
复制代码


  • 可见这是个普通的父工程,里面对 spring cloud 和 spring cloud alibaba 的版本做了控制,避免子工程还要各种指定版本的繁琐操作;

制作服务提供者镜像

  • simple-provider 是个 java web 应用,使用了 spring cloud alibaba 的依赖库之后可以使用 Nacos 的注册发现服务,整个工程的开发步骤如下:

  • 基于 maven 创建工程,其 pom.xml 内容如下:


<?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">    <parent>        <artifactId>nacosdemo</artifactId>        <groupId>com.bolingcavalry</groupId>        <version>1.0-SNAPSHOT</version>    </parent>    <modelVersion>4.0.0</modelVersion>
<artifactId>simpleprovider</artifactId>
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> </dependencies>
<build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> <!--使用jib插件--> <plugin> <groupId>com.google.cloud.tools</groupId> <artifactId>jib-maven-plugin</artifactId> <version>1.3.0</version> <configuration> <!--from节点用来设置镜像的基础镜像,相当于Docerkfile中的FROM关键字--> <from> <!--使用openjdk官方镜像,tag是8u201-jdk-alpine3.9,表示镜像的操作系统是alpine3.9,装好了jdk8u201--> <image>openjdk:8u201-jdk-alpine3.9</image> </from> <to> <!--镜像名称和tag,使用了mvn内置变量${project.artifactId}和${project.version}--> <image>bolingcavalry/nacos${project.artifactId}:${project.version}</image> </to> <!--容器相关的属性--> <container> <!--jvm内存参数--> <jvmFlags> <jvmFlag>-Xms1g</jvmFlag> <jvmFlag>-Xmx1g</jvmFlag> </jvmFlags> </container> </configuration> </plugin> </plugins> </build></project>
复制代码


  • 上述内容有两点需要注意:a. 依赖 spring-cloud-starter-alibaba-nacos-discovery,这样可以用上 spring cloud nacos 的服务;b. 使用了 maven 插件 jib-maven-plugin,用于将应用构建成 docker 镜像,此插件相关的详情请参考《Docker与Jib(maven插件版)实战》

  • 配置文件 application.properties,配置应用名称和 nacos 地址,注意这里 nacos 地址配置的是 nacoshost ,对应的是后面 docker-compose.yml 中的 link 参数:


spring.application.name=simple-providerspring.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@SpringBootApplicationpublic 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 */@RestControllerpublic 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 内容如下:


<?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">    <parent>        <artifactId>nacosdemo</artifactId>        <groupId>com.bolingcavalry</groupId>        <version>1.0-SNAPSHOT</version>    </parent>    <modelVersion>4.0.0</modelVersion>
<artifactId>simpleconsumer</artifactId>
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> </dependencies>
<build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> <!--使用jib插件--> <plugin> <groupId>com.google.cloud.tools</groupId> <artifactId>jib-maven-plugin</artifactId> <version>1.3.0</version> <configuration> <!--from节点用来设置镜像的基础镜像,相当于Docerkfile中的FROM关键字--> <from> <!--使用openjdk官方镜像,tag是8u201-jdk-alpine3.9,表示镜像的操作系统是alpine3.9,装好了jdk8u201--> <image>openjdk:8u201-jdk-alpine3.9</image> </from> <to> <!--镜像名称和tag,使用了mvn内置变量${project.artifactId}和${project.version}--> <image>bolingcavalry/nacos${project.artifactId}:${project.version}</image> </to> <!--容器相关的属性--> <container> <!--jvm内存参数--> <jvmFlags> <jvmFlag>-Xms1g</jvmFlag> <jvmFlag>-Xmx1g</jvmFlag> </jvmFlags> <!--要暴露的端口--> <ports> <port>8080</port> </ports> </container> </configuration> </plugin> </plugins> </build></project>
复制代码


  • 配置文件 application.properties,配置应用名称和 nacos 地址,注意这里 nacos 地址配置的是 nacoshost ,对应的是后面 docker-compose.yml 中的 link 参数:


spring.application.name=simple-consumerspring.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@SpringBootApplicationpublic 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 */@RestControllerpublic 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 环境搭建过程已经回顾完毕,在您搭建自己的容器环境时,希望本文能给您一些参考;

欢迎关注 InfoQ:程序员欣宸

学习路上,你不孤单,欣宸原创一路相伴...


发布于: 刚刚阅读数: 2
用户头像

搜索"程序员欣宸",一起畅游Java宇宙 2018.04.19 加入

前腾讯、前阿里员工,从事Java后台工作,对Docker和Kubernetes充满热爱,所有文章均为作者原创,个人Github:https://github.com/zq2599/blog_demos

评论

发布
暂无评论
Docker下的Nacos环境开发_Docker_程序员欣宸_InfoQ写作社区