GitLab 持續集成

互聯網軟件的開發和發佈,已經造成了一套標準流程,最重要的組成部分就是持續集成(Continuous integration,簡稱CI)。javascript

持續集成

持續集成指的是,頻繁地(一天屢次)將代碼集成到主幹。它的好處主要有兩個:java

  • 快速發現錯誤。每完成一點更新,就集成到主幹,能夠快速發現錯誤,定位錯誤也比較容易。
  • 防止分支大幅偏離主幹。若是不是常常集成,主幹又在不斷更新,會致使之後集成的難度變大,甚至難以集成。

Martin Fowler 說過,"持續集成並不能消除 Bug,而是讓它們很是容易發現和改正。"node

持續集成

持續集成強調開發人員提交了新代碼以後,馬上進行構建、(單元)測試。根據測試結果,咱們能夠肯定新代碼和原有代碼可否正確地集成在一塊兒。linux

與持續集成相關的,還有兩個概念,分別是持續交付和持續部署。nginx

持續交付

持續交付(Continuous delivery)指的是,頻繁地將軟件的新版本,交付給質量團隊或者用戶,以供評審。若是評審經過,代碼就進入生產階段。git

持續交付能夠看做持續集成的下一步。它強調的是,無論怎麼更新,軟件是隨時隨地能夠交付的。github

持續交付

持續交付在持續集成的基礎上,將集成後的代碼部署到更貼近真實運行環境的「類生產環境」(production-like environments)中。好比,咱們完成單元測試後,能夠把代碼部署到鏈接數據庫的 Staging 環境中更多的測試。若是代碼沒有問題,能夠繼續手動部署到生產環境中。面試

持續部署

持續部署(continuous deployment)是持續交付的下一步,指的是代碼經過評審之後,自動部署到生產環境。redis

持續部署的目標是,代碼在任什麼時候刻都是可部署的,能夠進入生產階段。spring

持續部署的前提是能自動化完成測試、構建、部署等步驟。

持續部署

持續集成的操做流程

根據持續集成的設計,代碼從提交到生產,整個過程有如下幾步。

提交

流程的第一步,是開發者向代碼倉庫提交代碼。全部後面的步驟都始於本地代碼的一次提交(commit)。

測試(第一輪)

代碼倉庫對 commit 操做配置了鉤子(hook),只要提交代碼或者合併進主幹,就會跑自動化測試。

測試的種類:

  • 單元測試:針對函數或模塊的測試
  • 集成測試:針對總體產品的某個功能的測試,又稱功能測試
  • 端對端測試:從用戶界面直達數據庫的全鏈路測試

第一輪至少要跑單元測試。

構建

經過第一輪測試,代碼就能夠合併進主幹,就算能夠交付了。

交付後,就先進行構建(build),再進入第二輪測試。所謂構建,指的是將源碼轉換爲能夠運行的實際代碼,好比安裝依賴,配置各類資源(樣式表、JS腳本、圖片)等等。

經常使用的構建工具以下:

  • Jenkins
  • Travis
  • Codeship
  • Strider

Jenkins 和 Strider 是開源軟件,Travis 和 Codeship 對於開源項目能夠無償使用。它們都會將構建和測試,在一次運行中執行完成。

測試(第二輪)

構建完成,就要進行第二輪測試。若是第一輪已經涵蓋了全部測試內容,第二輪能夠省略,固然,這時構建步驟也要移到第一輪測試前面。

第二輪是全面測試,單元測試和集成測試都會跑,有條件的話,也要作端對端測試。全部測試以自動化爲主,少數沒法自動化的測試用例,就要人工跑。

須要強調的是,新版本的每個更新點都必須測試到。若是測試的覆蓋率不高,進入後面的部署階段後,極可能會出現嚴重的問題。

部署

經過了第二輪測試,當前代碼就是一個能夠直接部署的版本(artifact)。將這個版本的全部文件打包( tar filename.tar * )存檔,發到生產服務器。

生產服務器將打包文件,解包成本地的一個目錄,再將運行路徑的符號連接(symlink)指向這個目錄,而後從新啓動應用。這方面的部署工具備Ansible,Chef,Puppet等。

回滾

一旦當前版本發生問題,就要回滾到上一個版本的構建結果。最簡單的作法就是修改一下符號連接,指向上一個版本的目錄。

使用 GitLab 持續集成

從 GitLab 8.0 開始,GitLab CI 就已經集成在 GitLab 中,咱們只要在項目中添加一個.gitlab-ci.yml文件,而後添加一個 Runner,便可進行持續集成。 並且隨着 GitLab 的升級,GitLab CI 變得愈來愈強大。

概念

Pipeline

一次 Pipeline 其實至關於一次構建任務,裏面能夠包含多個流程,如安裝依賴、運行測試、編譯、部署測試服務器、部署生產服務器等流程。

任何提交或者 Merge Request 的合併均可以觸發 Pipeline,以下圖所示:

+------------------+           +----------------+
| |  trigger  | |
| Commit / MR +---------->+ Pipeline |
| |           | |
+------------------+           +----------------+

複製代碼
Stages

Stages 表示構建階段,說白了就是上面提到的流程。咱們能夠在一次 Pipeline 中定義多個 Stages,這些 Stages 會有如下特色:

  • 全部 Stages 會按照順序運行,即當一個 Stage 完成後,下一個 Stage 纔會開始
  • 只有當全部 Stages 完成後,該構建任務 (Pipeline) 纔會成功
  • 若是任何一個 Stage 失敗,那麼後面的 Stages 不會執行,該構建任務 (Pipeline) 失敗

所以,Stages 和 Pipeline 的關係就是:

+--------------------------------------------------------+
| |
| Pipeline |
| |
| +-----------+ +------------+ +------------+ |
| |  Stage 1  |---->|   Stage 2  |----->|   Stage 3  | |
| +-----------+ +------------+ +------------+ |
| |
+--------------------------------------------------------+
複製代碼
Jobs

Jobs 表示構建工做,表示某個 Stage 裏面執行的工做。咱們能夠在 Stages 裏面定義多個 Jobs,這些 Jobs 會有如下特色:

  • 相同 Stage 中的 Jobs 會並行執行
  • 相同 Stage 中的 Jobs 都執行成功時,該 Stage 纔會成功
  • 若是任何一個 Job 失敗,那麼該 Stage 失敗,即該構建任務 (Pipeline) 失敗

因此,Jobs 和 Stage 的關係圖就是:

+------------------------------------------+
| |
| Stage 1 |
| |
| +---------+ +---------+ +---------+ |
| |  Job 1  | |  Job 2  | |  Job 3  | |
| +---------+ +---------+ +---------+ |
| |
+------------------------------------------+
複製代碼

基於 Docker 安裝 GitLab Runner

GitLab Runner 簡介

理解了上面的基本概念以後,有沒有以爲少了些什麼東西 —— 由誰來執行這些構建任務呢?

答案就是 GitLab Runner 了!

想問爲何不是 GitLab CI 來運行那些構建任務?

通常來講,構建任務都會佔用不少的系統資源 (譬如編譯代碼),而 GitLab CI 又是 GitLab 的一部分,若是由 GitLab CI 來運行構建任務的話,在執行構建任務的時候,GitLab 的性能會大幅降低。

GitLab CI 最大的做用是管理各個項目的構建狀態,所以,運行構建任務這種浪費資源的事情就交給 GitLab Runner 來作拉!

由於 GitLab Runner 能夠安裝到不一樣的機器上,因此在構建任務運行期間並不會影響到 GitLab 的性能

基於 Docker 安裝 GitLab Runner

準備目錄結構

目錄結構

docker-compose.yml

version: '2' 
services:
    gitlab:
        image: twang2218/gitlab-ce-zh:10.5
        restart: always
        hostname: '10.3.50.160'
        container_name: gitlab
        environment:
          TZ: 'Asia/Shanghai'
          GITLAB_OMNIBUS_CONFIG: |
            external_url 'http://10.3.50.160:8080'
            gitlab_rails['gitlab_shell_ssh_port'] = 2222
            unicorn['port'] = 8888
            nginx['listen_port'] = 8080
        ports:
          - '8080:8080'
          - '8443:443'
          - '2222:22'
        volumes:
            - /etc/localtime:/etc/localtime
            - ./conf:/etc/gitlab
            - ./data/logs:/var/log/gitlab
            - ./data/data:/var/opt/gitlab
    gitlab-runner:
        image: gitlab/gitlab-runner
        restart: always
        hostname: gitlab-runner
        container_name: gitlab-runner
        extra_hosts:
            - git.imlcs.top:10.3.50.160
        depends_on:
            - gitlab
        volumes:
            - /etc/localtime:/etc/localtime
            - ./runner:/etc/gitlab-runner
            - /var/run/docker.sock:/var/run/docker.sock
複製代碼

GitLab CI 地址與令牌參數

項目–>設置–>CI/CD–>Runner 設置

查找位置

註冊 Runner

方法一

docker exec -it gitlab-runner  gitlab-runner register -n \
  --url http://10.3.50.160:8080/ \
  --registration-token cpR4sgBCsZ-TJUpJVz9t \
  --description "dockersock" \
  --docker-privileged=true \
  --docker-pull-policy="if-not-present" \
  --docker-image "docker:latest" \
  --docker-volumes /var/run/docker.sock:/var/run/docker.sock \
  --docker-volumes /root/m2:/root/.m2 \
  --executor docker 
複製代碼

方法二

docker exec -it gitlab-runner gitlab-runner register

# 輸入 GitLab 地址
Please enter the gitlab-ci coordinator URL (e.g. https://gitlab.com/):
http://10.3.50.160:8888/

# 輸入 GitLab Token
Please enter the gitlab-ci token for this runner:
1Lxq_f1NRfCfeNbE5WRh

# 輸入 Runner 的說明
Please enter the gitlab-ci description for this runner:
能夠爲空

# 設置 Tag,能夠用於指定在構建規定的 tag 時觸發 ci
Please enter the gitlab-ci tags for this runner (comma separated):
deploy

# 這裏選擇 true ,能夠用於代碼上傳後直接執行
Whether to run untagged builds [true/false]:
true

# 這裏選擇 false,能夠直接回車,默認爲 false
Whether to lock Runner to current project [true/false]:
false

# 選擇 runner 執行器,這裏咱們選擇的是 shell
Please enter the executor: virtualbox, docker+machine, parallels, shell, ssh, docker-ssh+machine, kubernetes, docker, docker-ssh:
#shell
docker                                 # 使用 docker 做爲輸出模式
Please enter the default Docker image (e.g. ruby:2.1):
alpine:latest                          # 使用的基礎鏡像
複製代碼

使用 Runner

.gitlab-ci.yml

在項目工程下編寫.gitlab-ci.yml配置文件:
示例一,找一個springboot的簡單項目

image: docker-maven:alpine
services:
  - redis:3-alpine

#Maven 阿里雲鏡像
#before_script:
# - echo -e "<?xml version=\""1.0\"" encoding=\""UTF-8\""?><settings xmlns=\""http://maven.apache.org/SETTINGS/1.0.0\"" xmlns:xsi=\""http://www.w3.org/2001/XMLSchema-instance\"" xsi:schemaLocation=\""http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd\""><mirrors><mirror><id>alimaven</id><name>aliyun maven</name><url>http://maven.aliyun.com/nexus/content/groups/public/</url><mirrorOf>central</mirrorOf></mirror></mirrors></settings>" > ~/.m2/settings.xml

# 定義 stages
stages:
# - test
  - build

# 定義 jobs
#test app:
# stage: test
# script:
# - echo "I am test job"
# - mvn test

# 定義 job
build app:
  stage: build
  script:
    - mvn  -Dmaven.test.skip=true clean package docker:build
複製代碼

示例二

stages:
  - install_deps
  - test
  - build
  - deploy_test
  - deploy_production

cache:
  key: ${CI_BUILD_REF_NAME}
  paths:
    - node_modules/
    - dist/

# 安裝依賴
install_deps:
  stage: install_deps
  only:
    - develop
    - master
  script:
    - npm install

# 運行測試用例
test:
  stage: test
  only:
    - develop
    - master
  script:
    - npm run test

# 編譯
build:
  stage: build
  only:
    - develop
    - master
  script:
    - npm run clean
    - npm run build:client
    - npm run build:server

# 部署測試服務器
deploy_test:
  stage: deploy_test
  only:
    - develop
  script:
    - pm2 delete app || true
    - pm2 start app.js --name app

# 部署生產服務器
deploy_production:
  stage: deploy_production
  only:
    - master
  script:
    - bash scripts/deploy/deploy.sh
複製代碼

上面的配置把一次 Pipeline 分紅五個階段:

  • 安裝依賴(install_deps)
  • 運行測試(test)
  • 編譯(build)
  • 部署測試服務器(deploy_test)
  • 部署生產服務器(deploy_production)

注意:設置 Job.only 後,只有當 develop 分支和 master 分支有提交的時候纔會觸發相關的 Jobs。

節點說明:

  • stages:定義構建階段,這裏只有一個階段 deploy
  • deploy:構建階段 deploy 的詳細配置也就是任務配置
  • script:須要執行的 shell 腳本
  • only:這裏的 master 指在提交到 master 時執行
  • tags:與註冊 runner 時的 tag 匹配

測試集成效果

全部操做完成後 push 代碼到服務器,查看是否成功:

passed 表示執行成功

其它命令

  • 刪除註冊信息
gitlab-ci-multi-runner unregister --name "名稱"
複製代碼
  • 查看註冊列表
gitlab-ci-multi-runner list
複製代碼

附:項目配置 Dockerfile 案例

FROM openjdk:8-jre

MAINTAINER Lusifer <topsale@vip.qq.com>

ENV APP_VERSION 1.0.0-SNAPSHOT
ENV DOCKERIZE_VERSION v0.6.1
RUN wget https://github.com/jwilder/dockerize/releases/download/$DOCKERIZE_VERSION/dockerize-linux-amd64-$DOCKERIZE_VERSION.tar.gz \
    && tar -C /usr/local/bin -xzvf dockerize-linux-amd64-$DOCKERIZE_VERSION.tar.gz \
    && rm dockerize-linux-amd64-$DOCKERIZE_VERSION.tar.gz

RUN mkdir /app

COPY myshop-service-user-provider-$APP_VERSION.jar /app/app.jar
ENTRYPOINT ["dockerize", "-timeout", "5m", "-wait", "tcp://192.168.10.131:3306", "java", "-Djava.security.egd=file:/dev/./urandom", "-jar", "/app/app.jar"]

EXPOSE 8501
複製代碼

持續集成實戰用戶管理服務

部署通用模塊項目

先將全部被依賴項目(通用模塊項目)部署到 Nexus,爲項目建立一個deploy.bat文件,示例代碼以下:

cd ..
cd myshop-dependencies
call mvn deploy

cd ..
cd myshop-commons
call mvn deploy

cd ..
cd myshop-commons-domain
call mvn deploy

cd ..
cd myshop-commons-mapper
call mvn deploy

cd ..
cd myshop-commons-dubbo
call mvn deploy

cd ..
cd myshop-static-backend
call mvn deploy

cd ..
cd myshop-service-user-api
call mvn deploy
複製代碼

持續集成依賴管理項目

因爲咱們全部項目的父工程都是依賴於myshop-dependencies,因此咱們持續集成的第一步是將該項目進行持續集成,在項目目錄建立.gitlab-ci.yml文件,代碼以下:

stages:
  - deploy

deploy:
  stage: deploy
  script:
    - /usr/local/maven/apache-maven-3.5.3/bin/mvn clean install
複製代碼

持續集成服務提供者

gitlab-ci.yml

# 定義階段
stages:
  - build
  - push
  - run
  - clean

build:
  stage: build
  script:
    - /usr/local/maven/apache-maven-3.5.3/bin/mvn clean package
    - cp target/myshop-service-user-provider-1.0.0-SNAPSHOT.jar docker
    - cd docker
    - docker build -t 192.168.10.133:5000/myshop-service-user-provider:v1.0.0 .

push:
  stage: push
  script:
    - docker push 192.168.10.133:5000/myshop-service-user-provider:v1.0.0

run:
  stage: run
  script:
    - cd docker
    - docker-compose down
    - docker-compose up -d

clean:
  stage: clean
  script:
    - docker image prune -f
複製代碼

Dockerfile

FROM openjdk:8-jre
MAINTAINER Lusifer <topsale@vip.qq.com>

ENV APP_VERSION 1.0.0-SNAPSHOT
ENV DOCKERIZE_VERSION v0.6.1
RUN wget https://github.com/jwilder/dockerize/releases/download/$DOCKERIZE_VERSION/dockerize-linux-amd64-$DOCKERIZE_VERSION.tar.gz \
    && tar -C /usr/local/bin -xzvf dockerize-linux-amd64-$DOCKERIZE_VERSION.tar.gz \
    && rm dockerize-linux-amd64-$DOCKERIZE_VERSION.tar.gz

RUN mkdir /app

COPY myshop-service-user-provider-$APP_VERSION.jar /app/app.jar
ENTRYPOINT ["dockerize", "-timeout", "5m", "-wait", "tcp://192.168.10.131:3306", "java", "-Djava.security.egd=file:/dev/./urandom", "-jar", "/app/app.jar"]

EXPOSE 8501 22222 20881
複製代碼

docker-compose.yml

version: '3.1'
services:
  myshop-service-user-provider:
    image: 192.168.10.133:5000/myshop-service-user-provider:v1.0.0
    container_name: myshop-service-user-provider
    ports:
      - 8501:8501
      - 22222:22222
      - 20881:20881

networks:
  default:
    external:
      name: dubbo
複製代碼

持續集成服務消費者

gitlab-ci.yml

stages:
  - build
  - push
  - run
  - clean

build:
  stage: build
  script:
    - /usr/local/maven/apache-maven-3.5.3/bin/mvn clean package
    - cp target/myshop-service-user-consumer-1.0.0-SNAPSHOT.jar docker
    - cd docker
    - docker build -t 192.168.10.133:5000/myshop-service-user-consumer:v1.0.0 .

push:
  stage: push
  script:
    - docker push 192.168.10.133:5000/myshop-service-user-consumer:v1.0.0

run:
  stage: run
  script:
    - cd docker
    - docker-compose down
    - docker-compose up -d

clean:
  stage: clean
  script:
    - docker image prune -f
複製代碼

Dockerfile

FROM openjdk:8-jre
MAINTAINER Lusifer <topsale@vip.qq.com>

ENV APP_VERSION 1.0.0-SNAPSHOT
ENV DOCKERIZE_VERSION v0.6.1
RUN wget https://github.com/jwilder/dockerize/releases/download/$DOCKERIZE_VERSION/dockerize-linux-amd64-$DOCKERIZE_VERSION.tar.gz \
    && tar -C /usr/local/bin -xzvf dockerize-linux-amd64-$DOCKERIZE_VERSION.tar.gz \
    && rm dockerize-linux-amd64-$DOCKERIZE_VERSION.tar.gz

RUN mkdir /app

COPY myshop-service-user-consumer-$APP_VERSION.jar /app/app.jar
ENTRYPOINT ["dockerize", "-timeout", "5m", "-wait", "tcp://192.168.10.131:20881", "java", "-Djava.security.egd=file:/dev/./urandom", "-jar", "/app/app.jar"]

EXPOSE 8601 8701
複製代碼

docker-compose.yml

version: '3.1'
services:
  myshop-service-user-consumer:
    image: 192.168.10.133:5000/myshop-service-user-consumer:v1.0.0
    container_name: myshop-service-user-consumer
    ports:
      - 8601:8601
      - 8701:8701

networks:
  default:
    external:
      name: my_net複製代碼

以爲不錯請點贊支持,歡迎留言或進個人我的羣855801563領取【架構資料專題目合集90期】、【BATJTMD大廠JAVA面試真題1000+】,本羣專用於學習交流技術、分享面試機會,拒絕廣告,我也會在羣內不按期答題、探討。

相關文章
相關標籤/搜索