美文网首页工作生活
构建基于Springboot+Docker+Jenkins+Gi

构建基于Springboot+Docker+Jenkins+Gi

作者: wxb2dyj | 来源:发表于2019-07-01 17:47 被阅读0次

    最近在做基于Docker+Jenkins+Github+Maven的持续集成环境,目的是自动化构建springboot项目并发布到生产环境。小公司没有自动化构建系统,项目发布过程是这样的:

    1、在本地写好代码、测试,测试通过后打成war包或jar包;
    2、将war包或jar包拷贝到云服务器上;
    3、重启服务。

    整个过程,特别是第2、3步靠手工操作可能会引入错误(比如命令行下不小心可能会删掉某个文件),不利于项目的可靠性。为此,想起上家公司采用了Jenkins实现了项目的自动化构建,不需要本地打包、上传,因此干脆就做一回运维,花点时间自己搭建一个持续集成系统,以实现:
    只需本地写好代码并完成测试,然后将代码push到Github,后面的编译、打包、发布等工作交由持续集成系统自动完成。

    恩,理想很丰满,现实嘛骨感。以前工作中都是使用现成,第一次自己搭建踩了无数的坑。好在经过一段时间的学习、实操,今天终于初步达成了目标。在此,要对Google搜索同学提出表扬。

    由于比较忙,先大致说一下整个过程。

    本地开发环境:
    操作系统:Ubuntu 16.04
    Java开发集成环境:Intellij idea 2019.1.3(Ultimate Edition)
    JDK版本:1.8.0_191
    数据库:Mongodb 4.0.10、Redis 3.0.6
    Maven:3.6.1
    Tomcat:8.5.41

    云服务器环境:
    操作系统:阿里云ECS,Ubuntu 16.04
    JDK版本:使用镜像openjdk8,目前版本1.8.0_212
    数据库:Mongodb 4.0.10、Redis 3.0.6
    Maven:3.6.1
    Tomcat:2.1.1,Springboot自带

    大致步骤(后面有时间再补充):
    注意:我的账户默认是root,因此下面的命令均不需要sudo前缀
    第1步:安装Docker(按照官网安装即可)
    (1)apt-get update
    (2)apt-get install apt-transport-https ca-certificates curl gnupg-agent software-properties-common
    (3)curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
    (4)add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
    (5)apt-get update
    (6)apt-get install docker-ce docker-ce-cli containerd.io
    安装好后,运行“docker run hello-world”看是否安装正确。

    第2步:安装Jenkins
    这里通过Jenkins镜像来安装。注意:Jenkins镜像版本越高越好,否则可能会出现某些插件不兼容的问题。安装过程见我的另一篇博客https://www.jianshu.com/p/15c1addd1733。这篇博客中的汉化方法有些问题,重启Jenkins后会出现部分中文部分英文的情况,问题还没有找到。在此建议先不要汉化,因为很多问题可以在google上搜索,汉化后搜索结果的质量你懂的。

    第3步:配置Jenkins和Github
    这一步的目的是,当我们将本地项目push到Github后,会主动触发Jenkins从Github上拉取该项目,然后运行配置好的脚本。具体配置过程见我的另一篇博客https://www.jianshu.com/p/29d2a339a57a

    第4步:配置Springboot项目
    这一步主要是在项目pom.xml中引入dockerfile-maven-plugin插件,在该插件中可以指定项目打包后的名称、版本以及本地JAR包的位置。另外,还需要在项目根目录下编写Dockerfile以及build.sh、run.sh脚本。
    Dockerfile:用于构建Springboot项目的镜像
    build.sh:包含编译Dockerfile的命令
    run.sh:用于基于项目镜像启动Docker容器,即运行项目
    注意:这三个文件在项目根目录下,与src同级,见下图


    image.png

    4.1 引入dockerfile-maven-plugin插件
    4.1.1 在pom.xml的plugins下插入一下配置:
    <plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
    <configuration>

    <mainClass>com.company.testproject.TestProjectApplication</mainClass>
    <layout>ZIP</layout>
    </configuration>
    <executions>
    <execution>
    <goals>
    <goal>repackage</goal>
    </goals>
    </execution>
    </executions>
    </plugin>

            <plugin>
                <groupId>com.spotify</groupId>
                <artifactId>dockerfile-maven-plugin</artifactId>
                <version>1.4.10</version>
                <configuration>
                    <repository>testproject</repository>
                    <tag>20190628-1.0</tag>
                    <buildArgs>
                        <JAR_FILE>/target/testproject-0.0.1-SNAPSHOT.jar</JAR_FILE>
                    </buildArgs>
                </configuration>
            </plugin>
    

    注意:如果你原来将项目打包为war包,那么需要将pox.xml中的<packaging>war</packaging>注释掉(如果有的话)
    4.1.2 配置Tomcat
    由于在本地开发及测试时使用的是单独安装的Tomcat,没有使用Springboot自带的Tomcat,因此在pom.xml中是将该自带的Tomcat依赖去掉了的,即原配置如下:

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <exclusions>-->
                <exclusion>-->
                    <groupId>org.springframework.boot</groupId>-->
                    <artifactId>spring-boot-starter-tomcat</artifactIyincid>-->
                </exclusion>-->
            </exclusions>-->
        </dependency>
    

    由于阿里云内存空间有限,为了减少不必要的内存开销,同时启动/停止项目均通过容器来实现,不需要显式执行Tomcat的startup.sh脚本来启动项目,因此没必要单独安装Tomcat。故将上面的依赖修改为:

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    

    同时,因为使用内置的Tomcat,因此还要增加:

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
        </dependency>
    

    4.2 编写Dockerfile
    Dockerfile用于构建项目镜像。在项目的根目录下新建文件Dockerfile,内容如下:

    FROM openjdk:8
    ARG JAR_FILE
    RUN apt-get update
    RUN apt-get install vim -y
    RUN echo "Asia/Shanghai" > /etc/timezone
    RUN dpkg-reconfigure -f noninteractive tzdata
    RUN mkdir /testproject
    ADD ${JAR_FILE} /testproject
    EXPOSE 9081
    ENTRYPOINT ["java","-jar","/testproject/testproject-0.0.1-SNAPSHOT.jar"]

    这里,我没有使用宿主机中安装的JDK,而是基于基础镜像openjdk:8来构建项目镜像。
    这里简要说明该Dockerfile的内容:
    第1行:表示当前要构建的镜像的基础镜像是openjdk:8
    第2行:设置环境变量,暂未指定值,在后面指定
    第3、4行:可选。这两行目的是更新并安装vim,这两行主要是为了安装vim,因为基础镜像中除了jdk之外,没有其他软件可用。为了容器启动后可用对某些配置进行编辑或查看(当然也可以用cat命令查看,但我习惯了用vim)。如果你不需要vim,可以不安装
    第5、6行:设置容器中的时区为东八区(北京时间)。容器的默认时区不是东八区,因此需要进行设置。需要注意的是,通过这两行设置后的时间也早于当前时间8小时。网上有方法是在启动容器时,加上参数-v /etc/localtime:/etc/localtime -v /etc/timezone:/etc/timezone,但我没有验证此方法。
    第7行:在阿里云服务器上创建目录testproject,用于保存testproject-0.0.1-SNAPSHOT.jar
    第8行:将环境变了JAR_FILE指向/testproject
    第9行:将容器的端口9081暴露到容器外,即在宿主机可以访问该端口(即访问服务的端口)
    第10行:指定容器启动时执行的程序及参数。这里表示在容器启动后就发布项目,如果不想立即发布项目,可以替换为其他命令,比如ENTRYPOINT ["ls", "-l", "/testproject"]

    4.3 编写build.sh
    这里不多说,见下面的代码:

    mvn clean
    mvn package -DskipTests
    docker rmi -f testproject:20190628-1.0
    mvn Dockerfile:build
    docker images

    其中,第3行表示删除上一个版本的镜像(可选,如果要考虑回滚的话就不要删除);第四行是根据Dockerfile重新生成新镜像。

    4.4 编写run.sh
    这个脚本主要是用于启动容器,代码如下:

    docker ps -a

    aa-remove-unknown

    docker stop ruleparser
    docker rm -f ruleparser
    docker run -d --name ruleparser --network jids --network-alias jids -p 9081:9081 -v /var/jenkins_home/workspace/java_tale/data:/data -v /etc/localtime:/etc/localtime -v /etc/timezone:/etc/timezone ruleparser:20190628-1.0

    这里要稍微解释一下:
    第2行:防止第3行命令执行后出现”cannot stop container”的情况
    第3、4行:停止前一版本的容器,并删除该容器(不删的话,第5行的docker run命令会因存在相同名称的容器而失败)
    第5行:启动容器。参数说明如下:
    --name:指定了容器名称;
    --network:指定该容器所在的网桥,该网桥要与后文的mongodb容器所在网桥相同,否则容器不能访问mongod;
    --network-alias:表示网桥的别名,如果没有别名的话不需要此参数;
    -p:端口映射,格式为"-p 宿主机端口:容器端口";
    -v /var/jenkins_home/workspace/java_tale/data:/data:表示将宿主机的目录/var/jenkins_home/workspace/java_tale/data映射到容器目录/data。宿主机目录存放的是项目需要的数据,在项目中位于data目录下,data目录与src目录同级,见第4步附图。这样配置后,需要在项目的properties文件中,制定数据路径,如下图:


    image.png

    -v /etc/localtime:/etc/localtime -v /etc/timezone:/etc/timezone:指定容器中的时区,主要是方便查看日志
    ruleparser:20190628-1.0:镜像名称及标签,注意:名称及标签要与pom.xml中的配置相同。

    第5步:mongodb容器及配置主从节点
    参考博客https://outmanzzq.github.io/2019/01/30/docker-mongo-replica/
    这里贴出我的配置过程。

    5.1 运行mongodb replica的三个容器
    docker run -dit -p 27000:27017 --name mongo-master -v /server/data/mongodb-docker/db/master:/data/db --network jids --network-alias jids mongo mongod --replSet rs

    docker run -dit -p 27001:27017 --name mongo-slave-1 -v /server/data/mongodb-docker/db/slave-1:/data/db --network jids --network-alias jids mongo mongod --replSet rs

    docker run -dit -p 27002:27017 --name mongo-slave-2 -v /server/data/mongodb-docker/db/slave-2:/data/db --network jids --network-alias jids mongo mongod --replSet rs

    其中:
    -p 27000:27017 表示映射宿主机27000端口到容器的27017端口
    --name mongo-master 表示容器名为mongo-master
    -v /server/data/mongodb-docker/db/master:/data/db 表示映射宿主机目录到mongodb容器/data/db目录 (mongodb容器运行时的默认目录),两个目录可以自定义
    --net jids 指定容器网络为local-mongo-cluster
    mongo 容器使用的mongodb镜像
    mongod --replSet rs 执行mongod命令,将该实例(容器)添加到名为rs的副本集

    5.2 进入mongo-master容器的mongo命令行
    docker exec -it mongo-master mongo
    (进入容器的命令是docker exec -it mongo-master /bin/bash,不要搞混)

    5.3 配置主从节点
    use jids
    (1)确定主从配置
    config = {
    "_id" : "rs",
    "members" : [
    {
    "_id" : 0,
    "host" : "mongo-master:27017"
    },
    {
    "_id" : 1,
    "host" : "mongo-slave-1:27017"
    },
    {
    "_id" : 2,
    "host" : "mongo-slave-2:27017"
    }
    ]
    }
    (2)初始化
    rs.initiate(config)
    (3)切换到MASTER节点(如果切换后还是SECONDARY,可能由于MASTER连接问题,多尝试几次即可)
    rs.config()

    5.4 从宿主机导入数据到容器
    5.4.1 首先进入容器并在/root下创建data文件夹
    docker exec -it mongo-master /bin/bash
    mkdir /root/data
    5.4.2 然后退出容器回到宿主机,复制数据到容器。命令格式为:docker cp 数据文件夹 容器名:容器内保存数据的目录。例如:
    //假设在宿主机中保存bson和json文件的jids文件夹在/root/jids/data下
    docker cp /root/jids/data/jids mongo-master:/root/data

    然后就可以验证主从节点是否有效了(没有包括更多验证,如某个节点宕机),附常用命令:
    (1)创建数据库
    use 数据库名
    (2)创建集合
    db.createCollection("集合名")
    (3)显示所有数据库
    show dbs
    注意:当使用show dbs时,会提示Error: listDatabases failed:..."errmsg" : "not master and slaveOk=false"。此时执行一下命令rs.slaveOk()即可
    (4)显示所有集合
    show collections
    (5)显示集合中的所有文档
    db.集合名.find()

    第6步:在项目中配置mongodb和redis
    修改项目的properties文件,根据宿主机及容器情况配置mongodb和redis的IP和Port
    6.1 配置mongodb
    spring.data.mongodb.uri=mongodb://172.17.0.1:27000,172.17.0.1:27001,172.17.0.1:27002/jids?replicaSet=rs
    这里的172.17.0.1是宿主机中输入ifconfig后,docker0对应的虚拟IP地址。这个地址被所有容器共享,相当于容器组成的虚拟网络的localhost。注意,不要使用127.0.0.1来试图使用宿主机的服务,因为容器所在的虚拟网络与宿主机所在的网络是隔断的。

    mongodb.png

    6.2 配置redis
    redis.host=172.17.0.1
    redis.port=6379
    这里的host同6.1中的IP。

    redis.png

    第7步:访问项目
    这里有个坑。假如测试项目的controller如下


    controller.png

    要访问的功能模块如下:


    method.png

    如果将war包扔在tomcat的webapps中,那么访问该模块时需要采用"IP:PORT/testproject/test_parser?"这样的URI。然而,由于我们在Docker中使用的是Springboot内置的Tomcat,访问的URI变为了"IP:PORT/test_parser?"。这一点需要注意。

    到此,整个构建过程基本完成了。过程这么长,可以想象遇到了多少坑。上面的流程都是在填完坑后的总结,其中的心酸就不提了。还有很多细节还可以优化,只有待后面熟悉Docker和Jenkins后再做打算了。

    相关文章

      网友评论

        本文标题:构建基于Springboot+Docker+Jenkins+Gi

        本文链接:https://www.haomeiwen.com/subject/qlbgcctx.html