⭐⭐⭐ Spring Boot 项目实战 ⭐⭐⭐ Spring Cloud 项目实战
《Dubbo 实现原理与源码解析 —— 精品合集》 《Netty 实现原理与源码解析 —— 精品合集》
《Spring 实现原理与源码解析 —— 精品合集》 《MyBatis 实现原理与源码解析 —— 精品合集》
《Spring MVC 实现原理与源码解析 —— 精品合集》 《数据库实体设计合集》
《Spring Boot 实现原理与源码解析 —— 精品合集》 《Java 面试题 + Java 学习指南》

摘要: 原创出处 suo.im/5ZMDjf 「网络」欢迎转载,保留摘要,谢谢!


🙂🙂🙂关注**微信公众号:【芋道源码】**有福利:

  1. RocketMQ / MyCAT / Sharding-JDBC 所有源码分析文章列表
  2. RocketMQ / MyCAT / Sharding-JDBC 中文注释源码 GitHub 地址
  3. 您对于源码的疑问每条留言将得到认真回复。甚至不知道如何读源码也可以请教噢
  4. 新的源码解析文章实时收到通知。每周更新一篇左右
  5. 认真的源码交流微信群。

Docker和Spring Boot是非常流行的组合,我们将利用GitLab CI的优势,并在应用程序服务器上自动构建,推送和运行Docker镜像。

GitLab CI

Gitlab CI/CD服务是GitLab的一部分,每当开发人员将代码推送到GitLab存储库时,它都会在所需的环境中构建,测试和存储最新的更改。

选择GitLab CI的一些主要原因:

  1. 易于学习,使用和可扩展

  2. 维护容易

  3. 整合容易

  4. CI完全属于GitLab存储库的一部分

  5. 良好的Docker集成

  6. 镜像托管(Container registry)-基本上是你自己的私有Docker Hub

  7. 从成本上来说,GitLab CI是一个很好的解决方案。每个月你有2000分钟的免费构建时间,对于某些项目来说,这是绰绰有余的

我把 Docker 的教程和文章整理成了 PDF,关注微信关注号 Java后端,回复 666 就能下载了。

为什么GitLab CI超越Jenkins

这无疑是一个广泛讨论的话题,但是在本文中,我们将不深入探讨该话题。GitLab CI和Jenkins都有优点和缺点,它们都是功能非常强大的工具。

那为什么选择GitLab?

如前所述,CI完全是GitLab存储库的一部分,这意味着不需要安装它,并且维护最少。yml脚本完成后,你便或多或少地完成了所有工作。

对于小型项目使用Jenkins,你就必须自己设置和配置所有内容。通常,你还需要一台专用的Jenkins服务器,这也需要额外的成本和维护。

使用GitLab CI 前提条件

如果需要与这些前提条件有关的任何帮助,我已提供相应指南的链接。

  1. 你已经在GitLab上推送了Spring Boot项目

  2. 你已在应用程序服务器上安装了Docker(指南)

  3. 你具有Docker镜像的镜像托管(在本指南中将使用Docker Hub)

  4. 你已经在服务器上生成了SSH RSA密钥(指南)

你要创建什么

你将创建 Dockerfile.gitlab-ci.yml, 它们将自动用于:

  1. 构建应用程序Jar文件

  2. 构建Docker镜像

  3. 将镜像推送到Docker存储库

  4. 在应用程序服务器上运行镜像

基本项目信息

本文的Spring Boot应用程序是通过Spring Initializr生成的。这是一个基于Java 8或Java11构建的Maven项目。后面,我们将介绍Java 8和Java 11对Docker镜像有什么影响。

Docker文件

让我们从Dockerfile开始。

FROM maven:3.6.3-jdk-11-slim AS MAVEN_BUILD
#FROM maven:3.5.2-jdk-8-alpine AS MAVEN_BUILD FOR JAVA 8
ARG SPRING_ACTIVE_PROFILE
MAINTAINER Jasmin
COPY pom.xml /build/
COPY src /build/src/
WORKDIR /build/

RUN mvn clean install -Dspring.profiles.active=$SPRING_ACTIVE_PROFILE && mvn package -B -e -Dspring.profiles.active=$SPRING_ACTIVE_PROFILE
FROM openjdk:11-slim
#FROM openjdk:8-alpine FOR JAVA 8
WORKDIR /app
COPY --from=MAVEN_BUILD /build/target/appdemo-*.jar /app/appdemo.jar
ENTRYPOINT ["java", "-jar", "appdemo.jar"]

与该Dockerfile相关的知识很少。

Java版本

让我们从Docker的角度看一下Java 8和11之间的区别。长话短说:这是Docker镜像的大小和部署时间。

基于Java 8构建的Docker镜像将明显小于基于Java 11的镜像。 这也意味着Java 8项目的构建和部署时间将更快。

Java 8-构建时间:约4分钟,镜像大小为 约180 MB

Java 11-构建时间: 约14分钟,镜像大小约为480 MB

注意: 在实际应用中,这些数字可能会有所不同。

Docker镜像

正如在前面示例中已经看到的那样,由于Java版本的缘故,我们在应用程序镜像大小和构建时间方面存在巨大差异。其背后的实际原因是在Dockerfile中使用了Docker镜像。

如果我们再看一下Dockerfile,那么 Java 11镜像很大的真正原因是因为它包含了没有经过验证/测试的open-jdk:11镜像的Alpine版本

如果你不熟悉OpenJDK镜像版本,建议你阅读OpenJDK Docker官方文档。在这里,你可以找到有关每个OpenJDK版本的镜像的说明。

备注:动态的变量

ENTRYPOINT中,与环境相关的属性,我们只能写死,如下:

ENTRYPOINT [ “ java”,“ -Dspring.profiles.active = development”,“ -jar”,“ appdemo.jar” ]

为了使它动态,你希望将其简单地转换为:

ENTRYPOINT [ “ java”,“ -Dspring.profiles.active = $ SPRINT_ACTIVE_PROFILE”,“ -jar”,“ appdemo.jar” ]
以前,这是不可能的,但是幸运的是,这将在.gitlab-ci.yml中通过 ARG SPRING_ACTIVE_PROFILE修复。

gitlab-ci.yml

在编写此文件之前,要准备的东西很少。基本上,我们想要实现的是,只要推送代码,就会在相应的环境上自动部署。

创建.env文件和分支

我们首先需要创建包含与环境相关的分支和.env文件。 每个分支实际上代表我们的应用程序将运行的环境。

我们将在三个不同的环境中部署我们的应用程序:开发,测试和生产( development, QA, and production )。这意味着我们需要创建三个分支。

我们的dev,QA和prod应用程序将在不同的服务器上运行,并且将具有不同的Docker容器标签,端口和SSH密钥。这就要求我们的 gitlab-ci.yml文件将要是动态的 ,通过为我们拥有的每个环境创建.env文件来解决该问题。

.develop.env .qa.env .master.env

重要说明: 命名这些文件时,有一个简单的规则:使用GitLab分支来命名,因此文件名应如下所示: 。$ BRANCH_NAME.env

例如,这是.develop.env文件。

export SPRING_ACTIVE_PROFILE='development'
export DOCKER_REPO='username/demo_app:dev'
export APP_NAME='demo_app_dev'
export PORT='8080'
export SERVER_IP='000.11.222.33'
export SERVER_SSH_KEY="$DEV_SSH_PRIVATE_KEY"

与.env文件有关的重要说明:

SPRING_ACTIVE_PROFILE :不言自明,我们要使用哪些Spring应用程序属性。 DOCKER_REPO :这是Docker镜像的存储库;在这里,我们唯一需要注意的是Docker image TAG ,对于每种环境,我们将使用不同的标签,这意味着我们将使用 devqaprod 标签。

我们的Docker中心看起来像这样。

新知图谱, 使用 GitLab CI 和 Docker 自动部署 Spring Boot 应用

如你所见,存在一个带有三个不同标签的存储库,每当将代码推送到GitLab分支上时,每个标签(应用程序版本)都会被更新。

  • APP_NAME: 此属性非常重要,它是对容器的命名。 如果你未设置此属性,则Docker将为你的容器随机命名。这可能是一个问题,因为你将无法以干净的方式停止运行容器。
  • 端口: 这是我们希望运行Docker容器的端口。
  • SERVER_IP: 应用程序使用的服务器IP。通常,每个环境都将位于不同的服务器上。
  • SERVER_SSH_KEY: 这是我们已经在每台服务器上生成的SSH密钥。 $DEV_SSH_PRIVATE_KEY实际上是来自GitLab存储库的变量。

创建GitLab变量

最后需要做的是创建GitLab变量。

打开你的GitLab存储库,然后转到: Settings -> CI/CD 。在 Variables 部分中, 添加新变量:

  • DOCKER_USER: 用于访问Docker Hub或其他镜像托管的用户名
  • DOCKER_PASSWORD: 用于访问镜像托管的密码
  • $ ENV_SSH_PRIVATE_KEY: 先前在服务器上生成的SSH私钥。

SSH KEY的重要说明:

你需要复制完整的密钥值,包括: ----- BEGIN RSA PRIVATE KEY -----和----- END RSA PRIVATE KEY -----

最后,你的GitLab变量应如下所示。

新知图谱, 使用 GitLab CI 和 Docker 自动部署 Spring Boot 应用

创建gitlab-ci.yml文件

最后,让我们创建将所有内容放在一起的文件。

services:
- docker:19.03.7-dind
stages:
- build jar
- build and push docker image
- deploy
build:
image: maven:3.6.3-jdk-11-slim
stage: build jar
before_script:
- source .${CI_COMMIT_REF_NAME}.env
script:
- mvn clean install -Dspring.profiles.active=$SPRING_ACTIVE_PROFILE && mvn package -B -e -Dspring.profiles.active=$SPRING_ACTIVE_PROFILE
artifacts:
paths:
- target/*.jar
docker build:
image: docker:stable
stage: build and push docker image
before_script:
- source .${CI_COMMIT_REF_NAME}.env
script:
- docker build --build-arg SPRING_ACTIVE_PROFILE=$SPRING_ACTIVE_PROFILE -t $DOCKER_REPO .
- docker login -u $DOCKER_USER -p $DOCKER_PASSWORD docker.io
- docker push $DOCKER_REPO
deploy:
image: ubuntu:latest
stage: deploy
before_script:
- 'which ssh-agent || ( apt-get update -y && apt-get install openssh-client -y )'
- eval $(ssh-agent -s)
- echo "$SSH_PRIVATE_KEY" | tr -d 'r' | ssh-add -
- mkdir -p ~/.ssh
- chmod 700 ~/.ssh
- echo -e "Host *ntStrictHostKeyChecking nonn" > ~/.ssh/config
- source .${CI_COMMIT_REF_NAME}.env
script:
- ssh root@$SERVER "docker login -u $DOCKER_USER -p $DOCKER_PASSWORD docker.io; docker stop $APP_NAME; docker system prune -a -f; docker pull $DOCKER_REPO; docker container run -d --name $APP_NAME -p $PORT:8080 -e SPRING_PROFILES_ACTIVE=$SPRING_ACTIVE_PROFILE $DOCKER_REPO; docker logout"

让我们解释一下这里发生了什么:

services:
- docker:19.03.7-dind

这是一项服务,使我们可以在Docker中使用Docker。在Docker中运行Docker通常不是一个好主意,但是对于此用例来说,这是完全可以的,因为我们将构建镜像并将其推送到存储库中。

stages:
- build jar
- build and push docker image
- deploy

对于每个gitlab-ci.yml文件,必须首先定义执行步骤。脚本将按照步骤定义的顺序执行。

在每个步骤,我们都必须添加以下部分: before_script: - source .${CI_COMMIT_REF_NAME}.env

这只是预先加载之前创建的 env. files, 文件。根据正在运行的分支来自动注入变量。(这就是为什么我们必须使用分支名称来命名.env文件的原因)

这些是我们部署过程中的执行步骤。

新知图谱, 使用 GitLab CI 和 Docker 自动部署 Spring Boot 应用

如你所见,,有三个带有绿色复选标记的圆圈,这表示所有步骤均已成功执行。

build:
image: maven:3.6.3-jdk-11-slim
stage: build jar
before_script:
- source .${CI_COMMIT_REF_NAME}.env
script:
- mvn clean install -Dspring.profiles.active=$SPRING_ACTIVE_PROFILE && mvn package -B -e -Dspring.profiles.active=$SPRING_ACTIVE_PROFILE
artifacts:
paths:
- target/*.jar

这是执行第一步骤代码的一部分,构建了一个jar文件,该文件可以下载。这实际上是一个可选步骤,仅用于演示构建jar并从GitLab下载它是多么容易。

第二步骤是在Docker存储库中构建并推送Docker镜像。

docker build:
image: docker:stable
stage: build and push docker image
before_script:
- source .${CI_COMMIT_REF_NAME}.env
script:
- docker build --build-arg SPRING_ACTIVE_PROFILE=$SPRING_ACTIVE_PROFILE -t $DOCKER_REPO .
- docker login -u $DOCKER_USER -p $DOCKER_PASSWORD docker.io
- docker push $DOCKER_REPO

这一步骤,我们不得不使用docker:19.03.7-dind服务。如你所见,我们使用的是最新的稳定版本的Docker,我们只是在为适当的环境构建镜像,然后对Dockerhub进行身份验证并推送镜像。

我们脚本的最后一部分是:

deploy:
image: ubuntu:latest
stage: deploy
before_script:
- 'which ssh-agent || ( apt-get update -y && apt-get install openssh-client -y )'
- eval $(ssh-agent -s)
- echo "$SSH_PRIVATE_KEY" | tr -d 'r' | ssh-add -
- mkdir -p ~/.ssh
- chmod 700 ~/.ssh
- echo -e "Host *ntStrictHostKeyChecking nonn" > ~/.ssh/config
- source .${CI_COMMIT_REF_NAME}.env
script:
- ssh root@$SERVER "docker stop $APP_NAME; docker system prune -a -f; docker pull $DOCKER_REPO; docker container run -d --name $APP_NAME -p $PORT:8080 -e SPRING_PROFILES_ACTIVE=$SPRING_ACTIVE_PROFILE $DOCKER_REPO"

在此步骤中,我们使用Ubuntu Docker镜像,因此我们可以SSH到我们的应用程序服务器并运行一些Docker命令。其中的部分代码 before_script大部分来自官方文档,但是,当然,我们可以对其进行一些调整以满足我们的需求。为不对私钥进行验证,添加了以下代码行:

- echo -e "Host *ntStrictHostKeyChecking nonn" > ~/.ssh/config

你也可以参考指南验证私钥。 如你在最后阶段的脚本部分中所见,我们正在执行一些Docker命令。

  1. 停止正在运行的Docker容器: docker stop $APP_NAME。(这就是我们要在.env文件中定义 APP_NAME的原因 )

  2. 删除所有未运行的Docker镜像 docker system prune -a -f。这实际上不是强制性的,但我想删除服务器上所有未使用的镜像。

  3. 拉取最新版本的Docker镜像(该镜像是在上一个阶段中构建并推送的)。

  4. 最后,使用以下命令运行Docker镜像:

docker container run -d --name $APP_NAME -p $PORT:8080 -e SPRING_PROFILES_ACTIVE=$SPRING_ACTIVE_PROFILE $DOCKER_REPO

文章目录
  1. 1. GitLab CI
    1. 1.1. 为什么GitLab CI超越Jenkins
    2. 1.2. Docker文件
  2. 2. Java版本
  3. 3. Docker镜像
    1. 3.1. 备注:动态的变量
    2. 3.2. gitlab-ci.yml
    3. 3.3. 创建GitLab变量
    4. 3.4. 创建gitlab-ci.yml文件