1. 概述
功能需求
目前的流水线的功能比较简单,对于有服务实例的仓库,只需要实现maven 构建、测试、打包镜像。对于私有maven仓库,实现自动mvn deploy
技术选取
1. kaniko
使用kaniko来打包镜像,kaniko 是一种在容器或 Kubernetes 集群内从 Dockerfile 构建容器镜像的工具。kaniko 不依赖于 Docker 守护进程,而是完全在用户空间中执行 Dockerfile 中的每个命令。这使得在无法轻松或安全地运行 Docker 守护程序的环境中构建容器镜像成为可能,例如标准的 Kubernetes 集群。
kaniko是谷歌开发的,墙内获取比较麻烦,此外,命令参数较多,使用繁琐。因此考虑搭建自己的kaniko仓库,自定义kaniko镜像。使得其他仓库流水线可以方便地使用"kaniko"命令即可打包镜像。
2. gitlab-ci
我们没有使用jenkins等工具,选择使用了gitlab提供的GitLab CI。GitLab CI 是 GitLab 内置的进行持续集成的工具,只需要在仓库根目录下创建 .gitlab-ci.yml 文件,并配置 GitLab Runner;每次提交的时候,GitLab 将自动识别到 .gitlab-ci.yml 文件,并且使用 Gitlab Runner 执行该脚本。
2. 实现
gitlab-ci.yml
技术文档参考The gitlab-ci.yml file | GitLab
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| stages: - test - build
test_job: stage: test image: name: docker.nju.edu.cn/maven:3.8.7-eclipse-temurin-8-alpine allow_failure: true script: mvn test
build: stage: build image: name: gcr.io/kaniko-project/executor:v1.9.0-debug entrypoint: [""] script: - /kaniko/executor --context "${CI_PROJECT_DIR}" --dockerfile "${CI_PROJECT_DIR}/Dockerfile" --destination "${CI_REGISTRY_IMAGE}:${CI_COMMIT_TAG}" rules: - if: $CI_COMMIT_TAG
build_job: stage: build image: name: script: - kaniko
|
上面的build任务为gitlab文档中的kaniko使用方法,而build_job中的写法则是我们希望达成的。
自定义kaniko
自己打镜像,即使用Dockerfile COPY一些可执行文件到基础kaniko镜像。
这里,自己写一个kaniko脚本,封装kaniko的几个必选参数,可以通过环境变量获取一些仓库或者流水线的信息,如:${PIPELINE_ID}
。为了执行它,也拷贝了一个bash(直接从ubuntu拷贝的二进制文件),注意如果文件是通过window上传的,会丢失可执行权限,需要在Dockerfile里面写明chmod等。
Dockerfile
kaniko打镜像同样依据Dockerfile,这里给出一个简单的maven项目例子
1 2 3 4 5 6 7 8 9 10
| FROM maven:main AS MAVEN_BUILD COPY . /build WORKDIR /build RUN mvn package -Dmaven.test.skip=true
FROM openjdk:8-jdk-alpine COPY --from=MAVEN_BUILD /build/target/*.jar /app/app.jar WORKDIR /app CMD ["java", "-jar", "app.jar", "-Duser.timezone=Asia/Shanghai"] EXPOSE 8081
|
这里不多的值得说的就是设置时区。博客和GPT会看到一种写法:RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && echo "Asia/Shanghai" > /etc/time
这是从宿主机拷贝时间,但可能会不存在这个文件,因此最简单的方法是用java加参数。
自动maven deploy私人仓库
1 2 3 4 5 6 7 8 9 10
| stages: - deploy
deploy: stage: deploy image: maven:main script: - mkdir -p /root/.m2 - echo -ne '<?xml version="1.0" encoding="UTF-8"?>\n<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"\n xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"\n xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd">\n <servers>\n <server>\n <id>lx-nju</id>\n <username>maven</username>\n <password>maven</password>\n </server>\n </servers>\n <mirrors>\n <mirror>\n <id>alimaven</id>\n <name>aliyun maven</name>\n <url>http://maven.aliyun.com/nexus/content/groups/public/</url>\n <mirrorOf>central</mirrorOf>\n </mirror>\n\n <mirror>\n <id>CN</id>\n <name>OSChina Central</name>\n <url>http://maven.oschina.net/content/groups/public/</url>\n <mirrorOf>central</mirrorOf>\n </mirror>\n\n <mirror>\n <id>nexus</id>\n <name>internal nexus repository</name>\n <url>http://repo.maven.apache.org/maven2</url>\n <mirrorOf>central</mirrorOf>\n </mirror>\n\n </mirrors>\n</settings>\n' > /root/.m2/settings.xml - mvn deploy
|
Springboot项目pom配置
需要注意配置mainClass,否则会报错no main manifest attribute,in app.jar
。解决这个问题无疑需要添加插件。一开始我使用maven-jar-plugin
插件如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| <plugin> <groupId>org.apache.maven.plugins</groupId><artifactId>maven-jar-plugin</artifactId> <version>3.3.0</version> <configuration> <archive> <manifest> <addCasspath>true</addclasspath> <classpathPrefix>ib/</classpathPrefix> <mainclass>org.lexiang.order.OrderApplication</mainclass> </manifest> </archive> </configuration> </plugin>
|
但在springboot项目中使用这个插件,报了NoclassDefFoundError
的错,经过研究,使用spring-boot-maven-plugin
可以解决这个问题。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <version>${spring.boot.version}</version> <configuration> <mainClass>org.lexiang.order.OrderApplication</mainClass> </configuration> <executions> <execution> <goals> <goal>repackage</goal> </goals> </execution> </executions> </plugin>
|