Mrli
别装作很努力,
因为结局不会陪你演戏。
Contacts:
QQ博客园

CICD-Jenkins与Travis

2022/01/13 环境部署
Word count: 4,406 | Reading time: 19min

Jenkins与travis使用

Jenkins与Travis为CI、CD工具,其为Devops模式下的平台组件,能够让我们更好地进行开发测试与交付。由于最近云原生的课上在介绍DevOps和云监控,所以正好趁这个机会体验下Devops的交付流程,之前也有使用过Github Action作为CICD工具,但除了开源项目以外,Github使用比较少。这次呢就可以再熟悉下业内常用的CICD组件了:Jenkins or Travis

DevOps发展背景

传统交付模式的串行及隔离化形成了部门壁垒,降低了工作效率

通过“在云上创建资源”->实现“云服务器自动化管理”->“应用无状态上云”后即可实现应用的持续部署、持续交付,完成一次构建、到处运行的效果

作为一种新式的开发模式,DevOps模式是在互联网应用快速迭代的需要下,结合了开发与运维以及测试流程的一种模式,解决了测试资源匮乏、手工部署工作量大、周期长,环境不统一、资源难管理的问题。

实现的功能

功能架构

DevOps平台典型流程

经典流程

基于镜像容器构筑具备完全一致性的开发测试环境

流程

企业中Devops的解决方案

devops解决方案

建设成果

建设成果

Jenkins使用

Jenkins跟Gitlab一样,是发布了服务包的,为了更好的管理和使用,我这边是直接使用的Jenkins Docker镜像

  1. 拉取Jenkins镜像并运行

docker run -p 8080:8080 -p 50000:50000 --restart=always --name jenkins -v jenkins_home:/var/jenkins_home -v /var/run/docker.sock:/var/run/docker.sock -v $(which docker):$(which docker) jenkins/jenkins:lts

  • --restart=always:能够使我们在重启docker时,自动启动相关容器
  • --name jenkins: 将启动的容器名取作jenkins
  • -v jenkins_home:/var/jenkins_home: 挂载持久化卷
  • -v /var/run/docker.sock:/var/run/docker.sock:为了实现pipeline在另一个容器中运行,侦听docker.sock套接字,并执行挂载
  • -v $(which docker):$(which docker):-v 之后使用$()表示执行命令,这里表示如果在容器上运行which docker, Docker也会在本地计算机上运行“ which container"并将执行结果返回给容器,从而能在Jenkins容器中拿到另一个容器的containerID
  • 最后的jenkins/jenkins:lts:表示要拉取的镜像以及镜像版本

将容器跑起来后需要进入容器,给docker.sock授权:

1
2
3
docker exec -it --user=root jenkins bash
chmod 666 /var/run/docker.sock
ls -ltr /var/run

以上过后就可以在浏览器上登录Jenkins了,这边登录localhost:50000后会有一系列初始化步骤如下

  1. 安装建议的插件
  2. 创建第一个管理员用户
  3. Jenkins实例配置
  4. 开始使用 Jenkins

监听Github仓库变化

选择“新建任务”->“流水线”->勾选Github项目并输入https://github.com/Freedomisgood/iotrfid.git/->构建触发器中选择“GitHub hook trigger for GITScm polling”->流水线选择“Pipeline script”输入:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
pipeline {
agent {
docker {
image 'alpine'
}
}
stages {
stage('pull project') {
// agent { docker 'openjdk:8-jre' }
steps {
// echo 'Hello, JDK'
git credentialsId: 'GithubAccount', url: 'https://github.com/Freedomisgood/iotrfid.git/'
sh 'pwd'
}
}
}
}

然后保存,之后运行就可以等待触发啦,或者点击左侧的“立即构建”

注:可以看到script中有使用到docker,这边需要额外安装Docker的插件Docker PipelineDocker plugin(建议安装的插件中没有),如果不安装会执行失败

注:如果要监听Github仓库的变化,则需要增加Github的凭据:选择“Dashboard”到首页->系统管理->Manage Credendials->Jenkins->全局凭据->左侧的添加凭据,输入Github用户和密码后,留一个之后要使用的Credendials ID

jenkins凭据

参考:怎样用 Jenkins Docker 和 CICD 构建无服务器应用程序

Travis使用

Travis+Docker[+阿里云容器镜像、dockerhub]

travis主要是关注在CI持续集成上,同时与Github关联搭配做的比较号,也能完成持续交付(持续集成的基础上,增加打包构建形成产物)的工作。因此本次就以travis+docker来完成这么一个持续集成与持续交付的步骤:

步骤如下:

  1. travis官网绑定travis和github

  2. Dockerhub创建镜像仓库 or 阿里云开通容器镜像服务

  3. 项目中添加.travis.yml和Dockerfile文件

    1
    2
    3
    4
    5
    6
    7
    FROM python:3.9.1-alpine
    MAINTAINER Mrli 1063052964qq.com
    RUN tar -zcf app.tar.gz .
    ADD app.tar.gz /app
    WORKDIR /app
    RUN pip install -r local_requiements.txt
    ENTRYPOINT python app/main.py
    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
    language: python
    # sudo: enabled or required
    python:
    - "3.9.1"

    branches:
    only:
    - master

    install:
    - pip install -r local_requirements.txt

    before_script:
    - flake8 app tests
    - docker login -u $DOCKERHUB_USERNAME -p $DOCKERHUB_PASSWORD # dockerfile中需要拉取 python-3.9.1:alipine的镜像, 所以需要先docker login

    script:
    - python app/main.py
    - docker build -t nymrli/python:v2 .

    after_success:
    - docker login -u $username -p $password # 这里我们使用环境变量来控制,避免写死。环境变量,在travis的后台settings里面添加
    - docker push
    - echo "success"

    services: # 需要提供docker才能在里面使用docker命令
    - docker
  4. git push提交代码,travis会侦听github仓库变化触发githook从而执行任务

  5. 登录要运行app的服务器,docker-compose up将镜像启动

    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
    32
    33
    34
    35
    version: '2'
    services:
    mysql:
    container_name: mysql1
    image: mysql:5.7
    environment:
    MYSQL_ROOT_PASSWORD: 数据库密码
    ports:
    - "3306:3306"
    volumes:
    - /usr/local/docker/mysql/data:/var/lib/mysql
    - /usr/local/docker/mysql/conf:/etc/mysql
    - /usr/local/docker/mysql/logs:/var/log/mysql
    web:
    container_name: jafir_nginx1
    image: registry.cn-hangzhou.aliyuncs.com/jafir_docker_images/web:latest
    ports:
    - 8080:80
    restart: always
    depends_on:
    - java
    volumes:
    - /mydockerdata/nginx/etc/nginx.conf:/etc/nginx/nginx.conf
    - /mydockerdata/nginx/log/:/var/log/nginx
    java:
    container_name: jafir_gps1
    image: registry.cn-hangzhou.aliyuncs.com/jafir_docker_images/jafir-images:latest
    restart: always
    depends_on:
    - mysql
    ports:
    - 9090:9090
    volumes:
    - /mydockerdata/java/gps/upload:/Users/jafir/Downloads/upload
    - /mydockerdata/arme/out/*.nofoo.cn/*.nofoo.cn.pfx:/Users/jafir/Downloads/upload/cert/*.nofoo.cn.pfx

注:如果需要travis的构建过程中在服务器执行一些命令(如将代码部署发到自己的服务器上),还需要配置生成公钥和私匙,参看:

https://github.com/gopl/ci

Travis CI+Coveralls

  1. 配置Travis

    • 编写.travis.yml
    • 使用 GitHub 账号登陆 Travis CI,Oauth登录授权,获得组织访问权限
    • 设置 Build Status:打开指定仓库的监听状态选项
  2. 配置 Coveralls

    • .travis.yml中添加coveralls内容,如go的库叫goveralls ,python的库叫

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      # GO
      before_install:
      - go get github.com/mattn/goveralls
      script:
      - goveralls -service=travis-ci
      # Python
      install:
      - pip install coveralls
      script:
      - nosetests --with-coverage --cover-package=fanpy
      after_success:
      - coveralls
    • 使用 GitHub 账号登陆 Coverallshttps://travis-ci.org/),Oauth登录授权,获得组织访问权限

    • 设置Coverage Status: 打开指定仓库的监听状态选项

自动部署

通过配合Travis的使用,我们可以实现如下的效果:

  • 测试部署:每次push代码到dev分支,Travis会自动进行单元测试,然后自动的通过SSH将代码部署到对应的开发机器上并重启服务,以保持开发机上始终是最新的版本。
  • 正式部署:决定上线的时候可以将代码push代码到deploy分支上,Travis会自动将代码部署到正式的开发环境。

步入正题,要完成自动部署,首先Travis要能监听Git的变化,然后Travis还需要有权限登录到我们的SSH服务器进行部署:

  1. 配置Travis,让Travis能监听Git的某个分支。
  2. Git某个分支提交之后,Travis能自动发现提交并进行编译。
  3. Travis将编译后的产物通过SSH部署到给我们指定的机器。

Travis添加SSH密钥

通常我们是通过ssh命令加上用户名和密码访问服务器的,虽然理论上我们也可以在travis的命令中写上诸如ssh mofei@zhuwenlong.com -p abc的脚本,但是这样的代码如果提交到了公开的仓库中会有很大的泄露服务器密码的风险,所以我们需要一个别人无法窃取密码或者密钥的方式让Travis登录我们的服务器。

通常的免密登录是基于SSH信任关系的,那么如果我们能把密钥以加密的形式保持在Travis的服务器中,Travis就能登录我们的服务器了。这里我们可以使用Travis的文件加密功能,把我们的密钥进行加密保存。

在这个过程中,我们的密钥首先会被被Travis加密,解密的密钥被存储在Travis中,就是说只有Travis可以进行解密。所以我们可以大胆的把这个加密后的文件上传到github中,不用担心其他人盗用我们的密钥。

▲. 既然我们想要使用Travis加密文件,第一件事情就是在本地安装Travis

  1. 因为travis是用ruby写的,首先得查看是否有ruby,ruby -V

  2. 给ruby换源,gem sources --add https://gems.ruby-china.com/ --remove https://rubygems.org/

  3. 安装travis: sudo gem install travis

  4. 安装好Travis之后,我们需要在命令行中登录Travis: travis login --pro,pro指定的是API网站Travis CI Pro

    注意 Travis 目前有两个官网:travis-ci.comtravis-ci.org. 官方通知后者即将关闭,所以我们使用前者参数为–pro,后者参数为–org。

    1
    2
    3
    4
    5
    6
    7
    8
    Shell completion not installed. Would you like to install it now? |y| y
    We need your GitHub login to identify you.
    This information will not be sent to Travis CI, only to api.github.com.
    The password will not be displayed.

    Try running with --github-token or --auto if you don't want to enter your password anyway.

    Username:

    或者直接:travis login --github-token token --pro, github-token打开github后点击个人头像Github>settings>Personal access tokens> Generate new token > Generate token> Copy Token

    1
    2
    (env38) mrli@VM-4-7-ubuntu:~/remap/cook$ travis login --pro --github-token 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
    Successfully logged in as Freedomisgood!
  5. 生成并加密SSH密钥

    1
    2
    # 在当前目录生成密钥: 这段代码执行完成之后,会在目录中生成2个文件,私钥deploy_rsa和公钥deploy_rsa.pub,前者是用来免密登录服务器时候使用的,后者服务器用来鉴定私钥的有效性的。
    ssh-keygen -t rsa -b 4096 -C 'build@travis-ci.org' -f ./deploy_rsa

    下面的操作得在Github仓库下

    1
    2
    3
    4
    5
    6
    7
    8
    # 使用Travis加密, 注意加上--pro 指定api, 执行完命令,会生成加密文件 id_rsa.enc,.travis.yml 中会被写入解密命令:这几行被自动添加的代码的意思是,在`install`之前执行解开`deploy_rsa.enc`文件的命令并放置到`deploy_rsa`以供使用,其中这里的`$encrypted_137f45644142_key`和`$encrypted_137f45644142_iv`是解开这个文件的两个变量被存储在了`Travis`的服务器上。
    travis encrypt-file deploy_rsa --add --pro
    # 添加信任关系: 这句话的意思是向目标服务器(<ssh-user>@<deploy-host>这里的user和host需要替换成自己服务器的用户名和地址)添加公钥,添加成功之后,所有用该公钥对应的私钥访问服务器都会直接被认证通过。也就是说如果Travis保持了私钥的话,就可以免密的通过ssh登录我们的服务器了。
    ssh-copy-id -i deploy_rsa.pub <ssh-user>@<deploy-host> #
    # 删除敏感文件: 私钥deploy_rsa和公钥deploy_rsa.pub已经完成了他们的使命,我们可以把它删除以免被其他人恶意使用,并把生成的加密文件deploy_rsa.enc和修改后的.travis.yml添加到git中。
    rm -f deploy_rsa deploy_rsa.pub
    # 将修改添加到git中
    git add deploy_rsa.enc .travis.yml

    生成的.travis.yml文件内容

    1
    2
    3
    before_install:
    # 可以看到有两个变量: encrypted_f217180e22ee_key, encrypted_f217180e22ee_iv
    - openssl aes-256-cbc -K $encrypted_f217180e22ee_key -iv $encrypted_f217180e22ee_iv -in id_rsa.enc -out id_rsa -d
    • -in 表示输入文件,即我们要解密的文件
    • -out 表示解密后的文件,这里我们需要手动将路径修改为 ~/.ssh/id_rsa

    ▲.同时travis仓库中也出现了encrypted_f217180e22ee_key, encrypted_f217180e22ee_iv这两个变量

  6. 所有的一切都准备好之后,我们就可以修改.travis.yml文件让travis来进行部署了。

    首先,我们需要在部署之前解密私钥,并使其生效,所以我们添加如下代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    before_install:
    # 一行是解密ssh文件,后面的3行是使ssh密钥生效

    # 把项目下的deploy_rsa.enc加密文件,解密输出到/tmp/deploy_rsa中, 一般是~/.ssh/id_rsa, 不过也跟如何ssh-keygen有关
    - openssl aes-256-cbc -K $encrypted_137f45644142_key -iv $encrypted_137f45644142_iv
    -in deploy_rsa.enc -out /tmp/deploy_rsa -d
    # 开启 ssh-agent,即允许使用 ssh 命令
    - eval "$(ssh-agent -s)"
    # 给予 id_rsa 文件权限,避免警告
    - chmod 600 /tmp/deploy_rsa
    # 将私钥添加到ssh(看其他教程这一步貌似不需要)
    - ssh-add /tmp/deploy_rsa

    after_success:
    # 下面就是一些需要远程登陆的命令啦: scp, ssh,
    - scp -o stricthostkeychecking=no -r target/wx-java-miniapp-0.0.1-SNAPSHOT.jar <ssh-user>@<deploy-host>:/www/wwwroot/travis-app/wx-java-miniapp
    - ssh <ssh-user>@<deploy-host> -o stricthostkeychecking=no "mkdir travis_deploy_success"
    - rsync -az --delete ./dist/* <USERNAME>@<HOST>:<TARGET-PATH>
  7. 上传触发测试, git commit -m "xxx" && git push

    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
    language: node_js
    node_js:
    - lts/*
    branches:
    only:
    - main
    - master
    install:
    - npm install yarn
    before_script:
    # 把项目下的deploy_rsa.enc加密文件,解密输出到/tmp/deploy_rsa中
    - openssl aes-256-cbc -K $encrypted_db2095f63ba3_key -iv $encrypted_db2095f63ba3_iv
    -in deploy_rsa.enc -out /tmp/deploy_rsa -d
    - eval "$(ssh-agent -s)"
    # 给予 id_rsa 文件权限,避免警告
    - chmod 600 /tmp/deploy_rsa
    # 将私钥添加到 ssh
    - ssh-add /tmp/deploy_rsa
    script:
    - yarn build
    after_success:
    - echo "success!"
    services:
    - docker
    addons:
    ssh_known_hosts:
    - 49.235.118.244
    after_success:
    # 非交互方式登录 StrictHostKeyChecking=no
    - ssh mrli@49.235.118.244 -o stricthostkeychecking=no "docker run -d --name cgf -p 3000:80 nymrli/sucsoft-inititializer-front"

参考:

附录:

CICD概念

持续集成:以前是多个开发测试完毕后,才把代码往主分支上合并,可能交叉、冲突,合并之后还可能产生新的问题。而持续集成,就是经常提交代码到主分支,一天可能好几次。并且,自动化地进行单元测试并提供测试报告等,这样的话就能拆分细度颗粒,保证产品能够一步一步地安全可靠地迭代。很多以测试驱动开发的公司就是这样做的。

持续交付:持续集成的基础上,增加打包构建形成产物。

持续部署:持续交付的基础上,增加部署到相应的线上环境。

总而言之,这些持续做的事情,就是为了经常提交代码自动化测试、运行、部署,反馈问题,解决问题,再测试、运行、部署依次循环。持续部署还有个好处,对我们个人网站来说,我们可以直接提交代码,后续一系列过程都是自动化的,就不用管了,它自己自动部署发布。

案例

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
32
33
34
35
language: java
os: linux
dist: xenial
jdk:
- openjdk8
env:
global:
- COMMIT=${TRAVIS_COMMIT::7}
addons:
ssh_known_hosts:
- 39.106.230.88
services:
- docker
script: mvn clean package -Dmaven.test.skip=true
after_success:
- echo "$DOCKER_PASSWORD" | docker login -u "$DOCKER_USERNAME" --password-stdin
- export TAG=`if [ "$TRAVIS_BRANCH" == "main" ]; then echo "latest"; else echo $TRAVIS_BRANCH;
fi`
- export IMAGE_NAME=gongsir0630/wx-java-miniapp
- docker build -t $IMAGE_NAME:$COMMIT .
- docker tag $IMAGE_NAME:$COMMIT $IMAGE_NAME:$TAG
- docker push $IMAGE_NAME:$TAG
- scp -o stricthostkeychecking=no -r target/wx-java-miniapp-0.0.1-SNAPSHOT.jar travis@yzhelp.top:/www/wwwroot/travis-app/wx-java-miniapp
- ssh travis@yzhelp.top -o stricthostkeychecking=no "sh /www/wwwroot/travis-app/wx-java-miniapp/restart.sh"
branches:
only:
- main
notifications:
email:
- gongsir0630@gmail.com
before_install:
- openssl aes-256-cbc -K $encrypted_f217180e22ee_key -iv $encrypted_f217180e22ee_iv
-in id_rsa.enc -out ~/.ssh/id_rsa -d
- chmod 600 ~/.ssh/id_rsa
- echo -e "Host yzhelp.top\n\tStrictHostKeyChecking no\n" >> ~/.ssh/config

My demo:

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
32
33
34
35
36
37
38
39
40
41
42
43
44
language: node_js
node_js:
- lts/*
branches:
only:
- main
- master

install:
- npm install yarn

addons:
ssh_known_hosts:
- 49.235.118.244

env:
global:
- IMAGE_NAME=nymrli/sucsoft-inititializer-front
- IMAGE_TAG=latest
- CONTAINER_NAME=cgf

before_script:
- docker login -u $DOCKERHUB_USERNAME -p $DOCKERHUB_PASSWORD
# 把项目下的deploy_rsa.enc加密文件,解密输出到/tmp/deploy_rsa中
- openssl aes-256-cbc -K $encrypted_db2095f63ba3_key -iv $encrypted_db2095f63ba3_iv
-in deploy_rsa.enc -out /tmp/deploy_rsa -d
- eval "$(ssh-agent -s)"
# 给予 id_rsa 文件权限,避免警告
- chmod 600 /tmp/deploy_rsa
# 将私钥添加到 ssh
- ssh-add /tmp/deploy_rsa

script:
- yarn build

after_success:
- docker build -t ${IMAGE_NAME}:${IMAGE_TAG} .
- docker push ${IMAGE_NAME}:${IMAGE_TAG}
# 停止和删除已有容器,删除已有镜像并运行新镜像
- ssh mrli@49.235.118.244 -o stricthostkeychecking=no "docker stop ${CONTAINER_NAME}; docker rm ${CONTAINER_NAME} ;docker rmi $(docker images -q ${IMAGE_NAME}:${IMAGE_TAG});docker run -d --name ${CONTAINER_NAME} -p 3000:80 ${IMAGE_NAME}:${IMAGE_TAG}"
- echo "success!"

services:
- docker

travis.yml中的宏变量

  • 1
    2
    3
    4
    5
    6
    - $TRAVIS_BRANCH
    - $GH_TOKEN(生成的 Personal access tokens)
    $GIT_NAME(部署时的提交者名称)
    $GIT_EMAIL(部署时的提交者邮箱)
    $CUSTOM_DOMAIN(自定义域名)
    $CUSTOM_PATH(自定义输出目录)

环境变量和非公开环境变量

.travis.ymlenv字段可以定义环境变量。

1
2
3
4
env:
- DB=postgres
- SH=bash
- PACKAGE_VERSION="1.0.*"

然后,脚本内部就使用这些变量了。

而有些环境变量(比如用户名和密码)不能公开,这时可以通过 Travis 网站,写在每个仓库的设置页里面,Travis 会自动把它们加入环境变量。这样一来,脚本内部依然可以使用这些环境变量,但是只有管理员才能看到变量的值。具体操作请看官方文档

Author: Mrli

Link: https://nymrli.top/2021/10/25/CICD-Jenkins与Travis/

Copyright: All articles in this blog are licensed under CC BY-NC-SA 3.0 unless stating additionally.

< PreviousPost
k8s之kube-proxy源码分析
NextPost >
Java Logger
CATALOG
  1. 1. Jenkins与travis使用
    1. 1.1. DevOps发展背景
      1. 1.1.1. 实现的功能
      2. 1.1.2. DevOps平台典型流程
      3. 1.1.3. 企业中Devops的解决方案
      4. 1.1.4. 建设成果
    2. 1.2. Jenkins使用
      1. 1.2.1. 监听Github仓库变化
    3. 1.3. Travis使用
      1. 1.3.1. Travis+Docker[+阿里云容器镜像、dockerhub]
      2. 1.3.2. Travis CI+Coveralls
      3. 1.3.3. 自动部署
        1. 1.3.3.1. Travis添加SSH密钥
  2. 2. 附录:
    1. 2.1. CICD概念
    2. 2.2. 案例
    3. 2.3. travis.yml中的宏变量
    4. 2.4. 环境变量和非公开环境变量