Docker,微服务,持续交付是编程世界中当前最流行的话题。在一个环境中,包含几十个微服务相互通信,这似乎是非常重要的自动化测试、创建和部署进程。Docker是一个优秀的微服务解决方案,因为它可以创建和运行隔离的容器服务。
今天我将演示如何创建一个基础的持续交付管道,使用流行的软件自动化工具Jenkins,做一个微服务示例。
微服务示例
在我进入这篇文章的核心主题前,我先说说关于架构和用于微服务示例创建的工具。示例应用包含两个微服务示例互相通信(账户,客户),一个发现服务器(Eureka),和一个API网关(Zuul)。使用Spring Boot和Spring Cloud架构来实施。资源代码可以在GitHub上获得。Spring Cloud支持微服务发现和网关在box外——我们只要在maven项目配置文件(pom.xml)中定义正确的相关性。
下面的图片说明了采用解决方案架构是可见的。客户,账户REST API服务,发现服务,和网关正在隔离的Docker容器内运行。网关是微服务系统的入口。与所有其他服务相互作用。在发现服务中,代理请求选择微服务搜索它的地址。在每个账户或客户微服务存在多个实例的情况下,请求用Ribbon 和Feign 客户端负载均衡。
账户和客户服务在启动后,把它们自己登记到发现服务中。它们之间也有相互作用的可能性——例如,如果我们想要发现和返回所有客户的账户细节。
Dockerfiles
示例资源代码中的每个服务都有一个Docker镜像创建定义的Dockerfile。这非常的简单。这是Dockerfile的账户服务。我们用OpenJDK作为基础镜像。来自目标的JAR文件增加到镜像中,然后使用 java -jar 命令运行。服务在端口2222运行,在外部公开。
[mw_shl_code=text,true]FROM openjdk
MAINTAINER Piotr Minkowski <
piotr.minkowski@gmail.com>
ADD target/account-service.jar account-service.jar
ENTRYPOINT ["java", "-jar", "/account-service.jar"]
EXPOSE 2222[/mw_shl_code]
还必须在JAR清单里设置主要类别。我们在pom.xml模式中使用spring-boot-maven-plugin 来实现。下面可以看到片段。我们也设置创建 finalName 中断目标JAR文件版本号。Dockerfile和Maven创建定义和所有其他微服务非常类似。
[mw_shl_code=xml,true]<build>
<finalName>account-service</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>1.5.2.RELEASE</version>
<configuration>
<mainClass>pl.piomin.microservices.account.Application</mainClass>
<addResources>true</addResources>
</configuration>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>[/mw_shl_code]
Jenkins管道
为了我们的微服务,使用管道插件来创建持续交付。设置Jenkins,除了标准插件,还需要Docker管道插件CloudBees。下面图中有4个管道定义可以看看。
这是发现服务用Groovy语言写的管道定义。执行需要5歩。在Checkout步骤里,把变更pull到项目远程Git资源库中。然后用MVN安装命令创建项目,Maven版本从 pom.xml读取。在镜像步骤,从发现服务Dockerfile创建一个Docker镜像,然后把镜像push到本地注册表。在第四步,用公开的默认端口运行创建的镜像,连接docker容器主机名可见。最后,账户管道从no wait选项开始,意味着资源管道结束,将不会等待账户管道执行完成。
[mw_shl_code=text,true]node {
withMaven(maven:'maven') {
stage('Checkout') {
git url: 'https://github.com/piomin/sample-spring-microservices.git', credentialsId: 'github-piomin', branch: 'master'
}
stage('Build') {
sh 'mvn clean install'
def pom = readMavenPom file:'pom.xml'
print pom.version
env.version = pom.version
}
stage('Image') {
dir ('discovery-service') {
def app = docker.build "localhost:5000/discovery-service:${env.version}"
app.push()
}
}
stage ('Run') {
docker.image("localhost:5000/discovery-service:${env.version}").run('-p 8761:8761 -h discovery --name discovery')
}
stage ('Final') {
build job: 'account-service-pipeline', wait: false
}
}
}[/mw_shl_code]
账户管道非常类似。主要的区别是,在第四步里,账户服务容器连接到发现容器。我们需要去连接那些容器,因为 account-service 把自己登记到发现服务中,并且必须使用主机名连接它。
[mw_shl_code=text,true]node {
withMaven(maven:'maven') {
stage('Checkout') {
git url: 'https://github.com/piomin/sample-spring-microservices.git', credentialsId: 'github-piomin', branch: 'master'
}
stage('Build') {
sh 'mvn clean install'
def pom = readMavenPom file:'pom.xml'
print pom.version
env.version = pom.version
}
stage('Image') {
dir ('account-service') {
def app = docker.build "localhost:5000/account-service:${env.version}"
app.push()
}
}
stage ('Run') {
docker.image("localhost:5000/account-service:${env.version}").run('-p 2222:2222 -h account --name account --link discovery')
}
stage ('Final') {
build job: 'customer-service-pipeline', wait: false
}
}
}[/mw_shl_code]
类似管道也为客户和网关服务定义了。在每个微服务上,作为Jenkinsfile,在主项目目录里它们是可见的。每个镜像在管道执行期间创建,也push到本地Docker注册表里。为了使本地注册表在我们的主机上,需要pull并且运行Docker资源库镜像,在pull或push的时候,也使用注册表地址作为一个镜像名称前缀。本地注册表在它默认的5000端口公开。你可以看到push镜像到本地注册表的列表,通过调用REST API,例如http://localhost:5000/v2/_catalog.
[mw_shl_code=bash,true]docker run -d --name registry -p 5000:5000 registry[/mw_shl_code]
测试
需要在 discovery-service-pipeline上发动创建。这个管道将不仅为发现服务运行创建,也调用启动下一管道在最后创建(account-service-pipeline) 。
同样的规则也配置在 account-service-pipeline ,调用customer-service-pipeline 和为customer-service-pipeline, 调用gateway-service-pipeline.
因此,在所有的管道结束,你可以通过使用docker ps 命令,检查运行docker容器列表。应该看到5个容器:本地注册表和4个微服务。
还可以检查每个容器的日志,通过运行docker logs 命令——例如docker logs account。如果一切运行顺利,应该可以调用一个服务http://localhost:2222/accounts 或者通过Zuul网关http://localhost:8765/account/account.
[mw_shl_code=text,true]CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
fa3b9e408bb4 localhost:5000/gateway-service:1.0-SNAPSHOT "java -jar /gatewa..." About an hour ago Up About an hour 0.0.0.0:8765->8765/tcp gateway
cc9e2b44fe44 localhost:5000/customer-service:1.0-SNAPSHOT "java -jar /custom..." About an hour ago Up About an hour 0.0.0.0:3333->3333/tcp customer
49657f4531de localhost:5000/account-service:1.0-SNAPSHOT "java -jar /accoun..." About an hour ago Up About an hour 0.0.0.0:2222->2222/tcp account
fe07b8dfe96c localhost:5000/discovery-service:1.0-SNAPSHOT "java -jar /discov..." About an hour ago Up About an hour 0.0.0.0:8761->8761/tcp discovery
f9a7691ddbba registry "/entrypoint.sh /e..." About an hour ago Up About an hour 0.0.0.0:5000->5000/tcp registry[/mw_shl_code]
结论
我已经展示了使用Docker和Jenkins的微服务的持续交付环境的基础示例。你可以轻松的发现所提出的解决方案的局限性。例如,我们在它们之间互相连接Docker容器来通信,或者所有的工具和微服务在同样的机器上运行。
来源:weixin
作者:博云