如何用Docker中的Jenkins构建一个Java应用程序
Jenkins和Docker等新工具的引入有助于提高生产力。在过去的三年里,我们构建软件的方式发生了重大变化。今天,开发人员可以在几个月内创建新的技术并进行部署。
我们可以通过在Docker 容器中运行Jenkins ,实现软件的自动构建、测试和部署。这有利于持续集成和交付。在Docker中包括Jenkins也解决了几个不兼容的问题。
Docker通过将运行Jenkins的任务简化为两个命令:docker pull 和docker run 。
目标
在本教程中,我们将在Docker容器中设置Jenkins。我们还将构建和dockerize一个Java应用程序。
前提条件
- Java、Maven、Git和命令行的基本知识。
- 了解Docker及其命令。
- 一个Java IDE--在本教程中,我们将使用IntelliJ Idea,但你也可以使用你选择的任何IDE。
创建一个演示的Java应用程序
我们将创建一个简单的Java控制台应用程序并对其进行单元测试。这个演示应用程序将只检查一个输入是even 还是odd 。
首先,让我们用IntelliJ IDEA 创建一个新的Maven 项目。
我们将使用以下IntelliJ设置。

我们也可以通过command 行创建一个Maven 项目,使用Maven标准目录布局。
要创建Maven 项目的目录布局,请运行。
$ mkdir -p src/main/java
添加一个pom.xml 文件。
$ touch pom.xml
在我们的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>
<groupId>org.example</groupId>
<artifactId>Java-jenkins-in-docker</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<java.version>1.8</java.version>
</properties>
</project>
为了完成我们的应用配置,我们需要添加JUnit 5 依赖关系,以便编写测试。
让我们更新一下pom.xml 文件,以确保这个依赖关系是存在的。
<dependencies>
<!-- junit 5, unit test -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.3.1</version>
<scope>test</scope>
</dependency>
</dependencies>
开始编写代码
在src/main/java 路径中,让我们创建一个名为Main 的类。它将包含我们简单的控制台应用程序的代码。
在Main 类中,让我们添加main 方法来运行我们的代码。
注意,一些IDE(如NetBeans)通常会自动生成这段代码。
public class Main {
public static void main(String[] args) {
//code will go in here
}
}
接下来,让我们创建一个简单的static 方法,名为checkIfInputIsAnEvenNumber 。
它将检查一个输入是偶数还是奇数。
public static boolean checkIfInputIsAnEvenNumber(int number){
return number % 2 == 0;
}
-
在上面的代码片段中,我们正在创建一个
static方法,以便我们可以编写单元测试。我们想看看Jenkins将如何实现自动化测试。 -
如果输入的
int是偶数或奇数,该方法将分别返回真或假。
下面是Main 类的最终代码。
public class Main {
public static void main(String[] args) {
System.out.println(checkIfInputIsAnEvenNumber(122)); // Testing in the main method
}
public static boolean checkIfInputIsAnEvenNumber(int number){
return number % 2 == 0;
}
}
如果你运行上述代码,输出将是true 。
现在,让我们写一个单元测试来测试我们的checkIfInputIsAnEvenNumber 方法。首先,在src/test/java 路径中,让我们创建一个测试类TestMain 来测试这个方法。
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class TestMain {
@Test
public void testInputIsEven(){
assertTrue(Main.checkIfInputIsAnEvenNumber(23)); // Assertion
}
}
你可以在你的IDE中运行上面的测试。
另外,我们也可以使用Maven 命令在命令行中运行我们所有的单元测试,如下图所示。
$ mvn test
当我们使用23 作为我们的输入数据时,测试失败。

让我们把测试的输入数据改为22 ,并运行Maven 命令。
assertTrue(Main.checkIfInputIsAnEvenNumber(22)); // Assertion

测试通过。在几个步骤中,我们将看到Jenkins如何将这个过程自动化。
在GitHub上托管演示应用程序
我们将把我们的Java 应用程序代码推送到GitHub 。当我们在GitHub上对我们的应用程序做任何修改(提交)时,Jenkins 将远程触发一个post-commit 构建过程。
-
首先,[创建一个新的 GitHub 仓库]。
-
然后打开终端。
-
导航到我们的演示应用程序的目录并运行。
$ git init -b main //To initialize the local repository
- 我们将使用下面的命令添加我们所有的应用程序文件。
$ git add .
- 现在我们可以提交我们的文件了。
$ git commit -m "Added java demo application files"
-
复制GitHub上创建的仓库克隆
URL。 -
然后添加
remote URL,我们将推送本地仓库。
$ git remote add origin <REMOTE_URL>
验证远程 URL 并将我们本地仓库的修改推送到 Github。
$ git remote -v
$ git push origin main
在Docker中设置Jenkins
Docker-in-Docker
当我们在Docker中设置Jenkins时,我们需要记住我们设置的目标:dockerizing of an application 。为了实现这个目标,我们需要执行docker commands ,以及访问其他容器。
为了实现这个功能,我们需要一个Dockerfile ,配置一个Jenkins environment 。它将能够运行Docker命令并管理docker容器。
在任何目录下创建一个Dockerfile ,并在Dockerfile中添加。
from jenkins/jenkins:lts
USER root
RUN apt-get update -qq \
&& apt-get install -qqy apt-transport-https ca-certificates curl gnupg2 software-properties-common
RUN curl -fsSL https://download.docker.com/linux/debian/gpg | apt-key add -
RUN add-apt-repository \
"deb [arch=amd64] https://download.docker.com/linux/debian \
$(lsb_release -cs) \
stable"
RUN apt-get update -qq \
&& apt-get install docker-ce=17.12.1~ce-0~debian -y
RUN usermod -aG docker jenkins
现在让我们使用上面的Dockerfile ,创建一个jenkins-docker image 。
$ docker image build -t jenkins-docker .
为了在命令行中运行我们的Jenkins-docker container ,我们使用下面的代码。
$ docker run -it -p 8080:8080 -p 50000:50000 -v jenkins_home:/var/jenkins_home -v /var/run/docker.sock:/var/run/docker.sock --restart unless-stopped jenkins-docker
-
上面的命令运行我们预先建立的
jenkins-docker image。-p命令将容器的端口8080和50000发布到主机上。 -
我们应该在我们的Jenkins容器中运行Docker命令。然而,在我们的机器中,每次只有一个
Docker daemon在运行。所以我们需要做的是,在我们使用这个参数运行容器时,将我们的container与我们的host machine daemon绑定挂载。-v /var/run/docker.sock:/var/run/docker.sock -
-v jenkins_home:/var/jenkins_home参数在我们的主机上创建一个显式卷。为什么?在我们的初始设置中,我们将配置Jenkins并下载插件。当我们停止/重启/删除我们的容器时,我们需要完整地保存我们的初始设置配置。我们不希望每次停止/重启/删除我们的容器时都要进行这些设置。 -
--restart unless-stopped确保容器总是重新启动,除非使用 命令停止。docker stop <container_name/container_id>
运行上述命令后,访问localhostlocalhost:8080 来设置Jenkins。

我们可以从什么命令returns ,得到admin 的密码。
看看是什么样子的。

我们也可以用下面的命令从/var/jenkins_home/secrets/initialAdminPassword 目录中获得初始管理员密码。
$ docker exec -it <container_name/container_id> /bin/bash
并获得密码。
$ cat /var/jenkins_home/secrets/initialAdminPassword
接下来,我们选择Install suggested plugins 。
Jenkins会自动下载必要的插件。

Jenkins全局配置
首先,我们将在我们的Jenkins控制台配置JDK,Maven, 和Git ,以使Jenkins能够克隆我们的版本库并构建我们的应用程序。
在我们的Jenkins控制台,进入Manage Jenkins 。

在System Configurations 下,点击Global Tool Configuration 。

JDK配置
我们的Jenkins容器自带一个OpenJDK 。要找到它,我们需要进入容器的bash shell ,以获得JAVA_HOME 的路径。
为了得到容器的bash shell ,运行。
$ docker exec -it <container_name/container_id> /bin/bash
然后,如果我们使用的是macOS 或Linux ,我们运行。
echo $JAVA_HOME

Maven配置
我们可以指示Jenkins 从Apache servers ,而不是我们系统中的Maven directory ,下载Maven 。
遵循下图所示的指导原则。

请确保在退出页面前保存配置。
在使用Docker-in-Docker 构建时,我们可能会遇到问题。因此,对Docker-in-Docker 有一个基本的了解可以让我们轻松地调试应用程序。
把它放在一起
到目前为止,我们已经建立了一个简单的演示Java控制台应用程序,在Github上托管了我们的应用程序代码,并在Docker中设置了Jenkins。
现在,让我们把这一切结合起来,使用Jenkins自动构建、测试、docker化,并在每次提交到GitHub上的应用仓库后将我们的应用Docker镜像部署到Docker Hub。
首先,让我们创建一个新的Jenkins项目。

然后选择Freestyle project 。

要配置我们的Freestyle project ,选择GitHub项目并添加项目的URL。

对于我们的Source Code Management (或简称SCM),选择Git ,添加项目的remote Git repository URL ,并将branch field 留空,这样任何分支的提交都会触发我们的整个Jenkins 过程。

对于Build Triggers ,选择Poll SCM ,它检查我们是否做了修改(即新的提交),然后重建我们的项目。Poll SCM 定期检查SCM ,即使版本库中没有任何变化。
我们将通过这个演示应用给Schedule 五颗星,也就是每分钟轮询的cron表达式。

接下来,我们跳过Build Environment 标签。在Build 窗口中,我们将添加两个Invoke top-level Maven targets 步骤。
最后,我们点击apply ,保存我们的Freestyle project 配置。

上述构建步骤会自动运行$ mvn test 和$ mvn install 命令。如果你记得我们之前的步骤,我们手动运行了单元测试的测试命令。
为了测试的目的,让我们构建我们的项目,看看当前的配置是否有效。点击Build Now 。

我们可以在Build History 中查看控制台的输出。

我们的控制台输出应该看起来很像下面的图片。

如果我们提交修改,我们不需要手动点击Build Now 。Jenkins会自动构建我们的Freestyle项目。
构建并将我们的Docker镜像部署到Docker Hub上
我们就快完成了。剩下的就是让我们配置Jenkins来构建我们Java应用程序的Docker镜像,并将该镜像部署到Docker Hub。
为了实现这个目标,我们需要安装一些Jenkins插件。
在Manage Jenkins ,选择Manage Plugins ,在System Configurations ,search ,install ,以下插件。
- docker-build-step
- CloudBees Docker Build and Publish

为了检查插件是否已经安装,让我们回到我们的Freestyle项目配置,在Build 标签,点击Add build step 。
我们将看到Docker Build and Publish 选项。

为了构建一个Docker镜像,我们需要一个Dockerfile来通知docker从哪个基础镜像构建我们的镜像,以及其他与Java相关的配置。我们还需要生成一个JAR(Java ARchive)文件。
在build profile ,导航到pom.xml 文件并添加一个finalName。
这个finalname 将是我们的JAR name 。
<build>
<finalName>java-jenkins-docker</finalName>
</build>
为了生成我们的JAR运行。
$ mvn install
我们可以在项目的target/ 目录中找到我们的JAR。
现在让我们来创建我们的Dockerfile 。
打开terminal ,导航到我们的Java应用程序目录。
$ touch Dockerfile
并在我们的Dockerfile:
FROM openjdk:8
ADD target/java-jenkins-docker.jar java-jenkins-docker.jar
ENTRYPOINT ["java", "-jar","java-jenkins-docker.jar"]
EXPOSE 8080
添加新的文件,然后提交修改到GitHub仓库。这将触发我们配置的Jenkins提交后构建过程。
现在我们可以添加我们的build steps 来构建和部署我们的Java应用的Docker镜像。为此,我们将需要一个Docker Hub account 。
然后,在build step 中设置。
- 存储库名称:
Docker_id/jar_name示例kikiodazie/java-jenkins-docker - 在这个演示中,我们将其余的字段留空,然后
Apply和save。

Jenkins container 为了让Jenkins访问,我们需要通过命令行登录到我们的Docker Hub account 里面,如下图所示。
$ docker exec -it <container_name/container_id> /bin/bash
然后在容器里面,运行Docker login 命令。
$ docker login
为了完成这个过程,输入你的登录凭证。

回到你的项目中,点击Build Now ,然后导航到控制台的输出。输出应该如下图所示。
这意味着我们的镜像已经成功构建并推送到Docker Hub。

结论
在本教程中,我们已经学会了如何在Docker中设置和配置Jenkins。我们还构建并测试了一个Java应用的代码,并将其托管在Github上。
我们让Jenkins访问我们的Docker Hub账户,以执行提交后的构建触发。最后,我们了解了Docker-in-Docker以及如何在Docker容器中构建Docker镜像。