maven和docker
Docker是其中的新热点之一。 与传统虚拟机相比,它具有一组不同的技术和思想,并通过容器的思想实现了相似但同时又有所不同的东西:几乎所有VM都具有强大的功能,但速度更快,并且还具有许多有趣的附加功能。
在本文中,我假设您已经对Docker有所了解,并且知道如何与之交互。 如果不是这种情况,我可以建议您从以下链接开始:
- http://www.docker.io/gettingstarted
- http://coreos.com/docs/launching-containers/building/getting-started-with-docker/
- http://robknight.org.uk/blog/2013/05/drupal-on-docker/
我对该主题的个人贡献是向您展示可能的工作流程 ,使您可以从Maven作业中启动和停止Docker容器。
我研究此功能的原因是为了帮助使用Maven构建的Java项目中的测试和集成测试 。 这个问题是众所周知的:您的代码与外部系统和服务交互。
测试这些交互的常用策略是:
- 在内存服务器中; 用Java实现,通常很快,但是很多时候它们的局限性在于它们不是真实的东西
- 存根服务层,以提供所需的接口。
- 真实的外部流程
付出很多努力才能落实到位。 最完整的一种,即使用适当的外部服务的那种,会给与隔离有关的问题带来问题:想象一下您正在与数据库进行交互,并且在其他人访问相同资源的同时执行读/写操作。 同样,您可能会找到正确的工作流程,这些工作流程涉及创建单独的模式等等,但是,这又是额外的工作,而且通常不是很简单的活动。
如果我们能拥有与这些外部系统相同的机会,但完全隔离,那不是很好吗? 如果我也增加报价,您会怎么看?
Docker是为我们提供这一机会的工具。
在测试套件的开头,您可以使用所需的所有服务启动一组Docker容器,并在结束时将其拆解。 您的Maven工作可以成为这些服务的唯一使用者,并且需要所有隔离功能。 您可以在Dockerfile
的帮助下轻松编写所有脚本,最后,它们只不过是一系列顺序的命令行调用。
让我们看看如何启用所有这些功能。
您必须使用Linux或需要传统VM的帮助来托管Docker服务器进程。
这是官方文档指南,向您展示如何在不同的Linux发行版中进行安装: http : //docs.docker.io/en/latest/installation/
相反,这是一个非常快速的指南,显示了如何在MacOSX上进行安装: http : //blog.javabien.net/2014/03/03/setup-docker-on-osx-the-no-brainer-way /
准备就绪并安装Docker后,需要应用特定的配置 。
在最新版本的Docker中,默认情况下仅通过Unix套接字公开其远程API。 尽管我们可以使用正确的代码与它们进行交互,但我发现通过HTTP与API进行交互要容易得多。 为此,您必须将特定标志传递给Docker守护进程,以使其也监听HTTP。
我正在使用Fedora,要修改的配置文件是/usr/lib/systemd/system/docker.service
。
[Unit]
Description=Docker Application Container Engine
Documentation=http://docs.docker.io
After=network.target
[Service]
ExecStart=/usr/bin/docker -d -H tcp://127.0.0.1:4243 -H unix:///var/run/docker.sock
Restart=on-failure
[Install]
WantedBy=multi-user.target
与默认值相比,唯一的修改是添加了-H tcp://127.0.0.1:4243
。
现在,在重新加载systemd
脚本并重新启动服务之后,我有了一个Docker守护程序,该守护程序向我展示了一个不错的REST API ,可以用curl
戳一下。
sudo systemctl daemon-reload
sudo systemctl restart docker
curl http://127.0.0.1:4243/images/json # returns a json in output
您可能还希望此配置能够在将来的Docker rpm更新中保留下来。 为了实现这一点,您必须将刚刚修改的文件复制到可以保留rpm更新的位置。 在systemd
实现此目标的正确方法是:
sudo cp /usr/lib/systemd/system/docker.service /etc/systemd/system
Ubuntu ,则必须配置其他文件。 查看此页面: http : //blog.trifork.com/2013/12/24/docker-from-a-distance-the-remote-api/
现在,我们拥有与Docker轻松交互所需的一切。
Maven Docker插件 。 不幸的是,事实并非如此。 还没有这样的插件 ,或者至少我不知道它。 我正在考虑编写一个,但是目前我已经借助GMaven插件,一些Groovy代码和Java库Rest-assuredSwift解决了我的问题。
启动Docker容器的代码
import com.jayway.restassured.RestAssured
import static com.jayway.restassured.RestAssured.*
import static com.jayway.restassured.matcher.RestAssuredMatchers.*
import com.jayway.restassured.path.json.JsonPath
import com.jayway.restassured.response.Response
RestAssured.baseURI = "http://127.0.0.1"
RestAssured.port = 4243
// here you can specify advance docker params, but the mandatory one is the name of the Image you want to use
def dockerImageConf = '{"Image":"${docker.image}"}'
def dockerImageName = JsonPath.from(dockerImageConf).get("Image")
log.info "Creating new Docker container from image $dockerImageName"
def response = with().body(dockerImageConf).post("/containers/create")
if( 404 == response.statusCode ) {
log.info "Docker image not found in local repo. Trying to dowload image '$dockerImageName' from remote repos"
response = with().parameter("fromImage", dockerImageName).post("/images/create")
def message = response.asString()
//odd: rest api always returns 200 and doesn't return proper json. I have to grep
if( message.contains("404") ) fail("Image $dockerImageName NOT FOUND remotely. Abort. $message}")
log.info "Image downloaded"
// retry to create the container
response = with().body(dockerImageConf).post("/containers/create")
if( 404 == response.statusCode ) fail("Unable to create container with conf $dockerImageConf: ${response.asString()}")
}
def containerId = response.jsonPath().get("Id")
log.info "Container created with id $containerId"
// set the containerId to be retrieved later during the stop phase
project.properties.setProperty("containerId", "$containerId")
log.info "Starting container $containerId"
with().post("/containers/$containerId/start").asString()
def ip = with().get("/containers/$containerId/json").path("NetworkSettings.IPAddress")
log.info "Container started with ip: $ip"
System.setProperty("MONGODB_HOSTNAME", "$ip")
System.setProperty("MONGODB_PORT", "27017")
阻止他们的人
import com.jayway.restassured.RestAssured
import static com.jayway.restassured.RestAssured.*
import static com.jayway.restassured.matcher.RestAssuredMatchers.*
RestAssured.baseURI = "http://127.0.0.1"
RestAssured.port = 4243
def containerId = project.properties.getProperty('containerId')
log.info "Stopping Docker container $containerId"
with().post("/containers/$containerId/stop")
log.info "Docker container stopped"
if( true == ${docker.remove.container} ){
with().delete("/containers/$containerId")
log.info "Docker container deleted"
}
我对docker run
功能的实现,如此处的官方API文档所述: http : //docs.docker.io/en/latest/reference/api/docker_remote_api_v1.9/#inside-docker -跑
我要解决的特定问题是如何将Docker容器的ID从Maven阶段传播到另一个阶段
// set the containerId to be retrieved later during the stop phase project.properties.setProperty("containerId", "$containerId")
我还公开了一些Maven属性,可用于与API交互:
-
docker.image
–您要旋转的图像的名称 -
docker.remove.container
–如果设置为false,则告诉Maven不要从文件系统中删除已停止的容器(在作业完成后用于检查Docker容器)
例如
mvn verify -Ddocker.image=pantinor/fuse -Ddocker.remove.container=false
您可以在此处找到完整的工作示例。 有人告诉我,有时我的语法着色器脚本会吃一些关键字或更改单词的大小写,因此,如果要复制和粘贴,可能是从Github裁剪的一个更好的主意。
这是使用命令mvn verify
运行Maven构建时输出的一部分:
...
[INFO] --- gmaven-plugin:1.4:execute (start-docker-images) @ gmaven-docker ---
[INFO] Creating new Docker container from image {"Image":"pantinor/centos-mongodb"}
log4j:WARN No appenders could be found for logger (org.apache.http.impl.conn.BasicClientConnectionManager).
log4j:WARN Please initialize the log4j system properly.
[INFO] Container created with id 5283d970dc16bd7d64ec08744b5ecec09b57d9a81162826e847666b8fb421dbc
[INFO] Starting container 5283d970dc16bd7d64ec08744b5ecec09b57d9a81162826e847666b8fb421dbc
[INFO] Container started with ip: 172.17.0.2
...
[INFO] --- gmaven-plugin:1.4:execute (stop-docker-images) @ gmaven-docker ---
[INFO] Stopping Docker container 5283d970dc16bd7d64ec08744b5ecec09b57d9a81162826e847666b8fb421dbc
[INFO] Docker container stopped
[INFO] Docker container deleted
...
如果您有任何疑问或建议,请随时告诉我!
完整的Maven`pom.xml`也可以在这里找到: https ://raw.githubusercontent.com/paoloantinori/gmaven_docker/master/pom.xml
<!--?xml version="1.0"?-->
<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>
<artifactid>gmaven-docker</artifactid>
<groupid>paolo.test</groupid>
<version>1.0.0-SNAPSHOT</version>
<name>Sample Maven Docker integration</name>
<description>See companion blogpost here: </description>
<build>
<plugins>
<plugin>
<groupid>org.codehaus.gmaven</groupid>
<artifactid>gmaven-plugin</artifactid>
<version>1.4</version>
<configuration>
<providerselection>2.0</providerselection>
</configuration>
<executions>
<execution>
<id>start-docker-images</id>
<phase>test</phase>
<goals>
<goal>execute</goal>
</goals>
<configuration>
<source><!--[CDATA[
import com.jayway.restassured.RestAssured
import static com.jayway.restassured.RestAssured.*
import static com.jayway.restassured.matcher.RestAssuredMatchers.*
RestAssured.baseURI = "http://127.0.0.1"
RestAssured.port = 4243
// here you can specify advance docker params, but the mandatory one is the name of the Image you want to use
def dockerImage = '{"Image":"pantinor/centos-mongodb"}'
log.info "Creating new Docker container from image $dockerImage"
def response = with().body(dockerImage).post("/containers/create")
if( 404 == response.statusCode ) {
log.info "[INFO] Docker Image not found. Downloading from Docker Registry"
log.info with().parameter("fromImage", "pantinor/centos-mongodb").post("/images/create").asString()
log.info "Image downloaded"
}
// retry to create the container
def containerId = with().body(dockerImage).post("/containers/create").path("Id")
log.info "Container created with id $containerId"
// set the containerId to be retrieved later during the stop phase
project.properties.setProperty("containerId", "$containerId")
log.info "Starting container $containerId"
with().post("/containers/$containerId/start").asString()
def ip = with().get("/containers/$containerId/json").path("NetworkSettings.IPAddress")
log.info "Container started with ip: $ip"
System.setProperty("MONGODB_HOSTNAME", "$ip")
System.setProperty("MONGODB_PORT", "27017")
]]-->
</configuration>
</execution>
<execution>
<id>stop-docker-images</id>
<phase>post-integration-test</phase>
<goals>
<goal>execute</goal>
</goals>
<configuration>
<source><!--[CDATA[
import com.jayway.restassured.RestAssured
import static com.jayway.restassured.RestAssured.*
import static com.jayway.restassured.matcher.RestAssuredMatchers.*
RestAssured.baseURI = "http://127.0.0.1"
RestAssured.port = 4243
def containerId = project.properties.getProperty('containerId')
log.info "Stopping Docker container $containerId"
with().post("/containers/$containerId/stop")
log.info "Docker container stopped"
with().delete("/containers/$containerId")
log.info "Docker container deleted"
]]-->
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupid>com.jayway.restassured</groupid>
<artifactid>rest-assured</artifactid>
<version>1.8.1</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
参考:在Someday Never Comes博客上,我们的JCG合作伙伴 Paolo Antinori 与Maven和Docker进行了集成测试 。
翻译自: https://www.javacodegeeks.com/2014/03/integration-testing-with-maven-and-docker.html
maven和docker