互联网软件的开发和发布,已经造成了一套标准流程,最重要的组成部分就是持续集成(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 和 Strider 是开源软件,Travis 和 Codeship 对于开源项目能够无偿使用。它们都会将构建和测试,在一次运行中执行完成。
构建完成,就要进行第二轮测试。若是第一轮已经涵盖了全部测试内容,第二轮能够省略,固然,这时构建步骤也要移到第一轮测试前面。
第二轮是全面测试,单元测试和集成测试都会跑,有条件的话,也要作端对端测试。全部测试以自动化为主,少数没法自动化的测试用例,就要人工跑。
须要强调的是,新版本的每个更新点都必须测试到。若是测试的覆盖率不高,进入后面的部署阶段后,极可能会出现严重的问题。
经过了第二轮测试,当前代码就是一个能够直接部署的版本(artifact)。将这个版本的全部文件打包( tar filename.tar * )存档,发到生产服务器。
生产服务器将打包文件,解包成本地的一个目录,再将运行路径的符号连接(symlink)指向这个目录,而后从新启动应用。这方面的部署工具备Ansible,Chef,Puppet等。
一旦当前版本发生问题,就要回滚到上一个版本的构建结果。最简单的作法就是修改一下符号连接,指向上一个版本的目录。
从 GitLab 8.0 开始,GitLab CI 就已经集成在 GitLab 中,咱们只要在项目中添加一个.gitlab-ci.yml
文件,而后添加一个 Runner,便可进行持续集成。 并且随着 GitLab 的升级,GitLab CI 变得愈来愈强大。
一次 Pipeline 其实至关于一次构建任务,里面能够包含多个流程,如安装依赖、运行测试、编译、部署测试服务器、部署生产服务器等流程。
任何提交或者 Merge Request 的合并均可以触发 Pipeline,以下图所示:
+------------------+ +----------------+
| | trigger | |
| Commit / MR +---------->+ Pipeline |
| | | |
+------------------+ +----------------+
复制代码
Stages 表示构建阶段,说白了就是上面提到的流程。咱们能够在一次 Pipeline 中定义多个 Stages,这些 Stages 会有如下特色:
所以,Stages 和 Pipeline 的关系就是:
+--------------------------------------------------------+
| |
| Pipeline |
| |
| +-----------+ +------------+ +------------+ |
| | Stage 1 |---->| Stage 2 |----->| Stage 3 | |
| +-----------+ +------------+ +------------+ |
| |
+--------------------------------------------------------+
复制代码
Jobs 表示构建工做,表示某个 Stage 里面执行的工做。咱们能够在 Stages 里面定义多个 Jobs,这些 Jobs 会有如下特色:
因此,Jobs 和 Stage 的关系图就是:
+------------------------------------------+
| |
| Stage 1 |
| |
| +---------+ +---------+ +---------+ |
| | Job 1 | | Job 2 | | Job 3 | |
| +---------+ +---------+ +---------+ |
| |
+------------------------------------------+
复制代码
理解了上面的基本概念以后,有没有以为少了些什么东西 —— 由谁来执行这些构建任务呢?
答案就是 GitLab Runner 了!
想问为何不是 GitLab CI 来运行那些构建任务?
通常来讲,构建任务都会占用不少的系统资源 (譬如编译代码),而 GitLab CI 又是 GitLab 的一部分,若是由 GitLab CI 来运行构建任务的话,在执行构建任务的时候,GitLab 的性能会大幅降低。
GitLab CI 最大的做用是管理各个项目的构建状态,所以,运行构建任务这种浪费资源的事情就交给 GitLab Runner 来作拉!
由于 GitLab Runner 能够安装到不一样的机器上,因此在构建任务运行期间并不会影响到 GitLab 的性能
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
复制代码
项目
–>设置
–>CI/CD
–>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 # 使用的基础镜像
复制代码
在项目工程下编写.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 分红五个阶段:
注意:设置 Job.only 后,只有当 develop 分支和 master 分支有提交的时候才会触发相关的 Jobs。
节点说明:
全部操做完成后 push 代码到服务器,查看是否成功:
passed 表示执行成功
gitlab-ci-multi-runner unregister --name "名称"
复制代码
gitlab-ci-multi-runner list
复制代码
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
复制代码
# 定义阶段
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
复制代码
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
复制代码
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
复制代码
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
复制代码
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
复制代码
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+】,本群专用于学习交流技术、分享面试机会,拒绝广告,我也会在群内不按期答题、探讨。