真香,GitLab 和 Docker自動化部署SpringBoot應用

Docker和Spring Boot是很是流行的組合,咱們將利用GitLab CI的優點,並在應用程序服務器上自動構建,推送和運行Docker鏡像。css

GitLab CI

Gitlab CI/CD服務是GitLab的一部分。開發人員將代碼推送到GitLab存儲庫時,GitLab CI就會在用戶指定的環境中自動構建,測試和存儲最新的代碼更改。java

選擇GitLab CI的一些主要緣由:git

  1. 易於學習,使用和可擴展spring

  2. 維護容易docker

  3. 整合容易apache

  4. CI徹底屬於GitLab存儲庫的一部分ubuntu

  5. 良好的Docker集成bash

  6. 鏡像託管(Container registry)-基本上是你本身的私有Docker Hub服務器

  7. 從成本上來講,GitLab CI是一個很好的解決方案。每月你有2000分鐘的免費構建時間,對於某些項目來講,這是綽綽有餘的app

爲何GitLab CI超越Jenkins

這無疑是一個普遍討論的話題,可是在本文中,咱們將不深刻探討該話題。GitLab CI和Jenkins都有優勢和缺點,它們都是功能很是強大的工具。

那爲何選擇GitLab?

如前所述,Gitlab CI是GitLab存儲庫的一部分,這就意味着當咱們有了GitLab後,就不須要再安裝Gitlab CI,也不須要額外維護。而且只須要編寫一個.gitlab-ci.yml文件(下文會詳細說明),你便完成了全部CI工做。

對於小型項目使用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 8ARG SPRING_ACTIVE_PROFILEMAINTAINER JasminCOPY 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_PROFILEFROM openjdk:11-slim#FROM openjdk:8-alpine FOR JAVA 8WORKDIR /appCOPY --from=MAVEN_BUILD /build/target/appdemo-*.jar /app/appdemo.jarENTRYPOINT ["java", "-jar", "appdemo.jar"]

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,對於每種環境,咱們將使用不一樣的標籤,這意味着咱們將使用dev,qa 和prod 標籤。

咱們的Docker中心看起來像這樣。

真香,GitLab 和 Docker自動化部署SpringBoot應用


如你所見,存在一個帶有三個不一樣標籤的存儲庫,每當將代碼推送到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 和 Docker自動化部署SpringBoot應用


建立gitlab-ci.yml文件

最後,讓咱們建立將全部內容放在一塊兒的文件。

services:  - docker:19.03.7-dindstages:  - build jar  - build and push docker image  - deploybuild:  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/*.jardocker 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_REPOdeploy:  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 *\n\tStrictHostKeyChecking no\n\n" > ~/.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 和 Docker自動化部署SpringBoot應用


如你所見,,有三個帶有綠色複選標記的圓圈,這表示全部步驟均已成功執行。

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 *\n\tStrictHostKeyChecking no\n\n" > ~/.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 *\n\tStrictHostKeyChecking no\n\n" > ~/.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=$SPR

相關文章
相關標籤/搜索