持续集成

背景

对于CI/CD心痒一段时间了,现在了解一下具体过程

简介

Continuous Integration,持续集成,简称CI.
Continuous Delivery,持续交付,简称CD.
Continuous Deployment,持续部署,也简称CD.

通常写代码到使用的流程是:
功能分支写代码->编译->测试->合并到主分支->build主分支->deploy主分支.
手动做当然没有任何问题,如果自动做,就体现了持续性,也更有效率一些.
如果进行得快(5分钟完成),有可能会间接提高代码质量.
但如果项目编译一次半小时,测试一次几小时,则CI完全沦为摆设.

完全可以在自己的本地写一套bash脚本,
然后减少到把过程减少到一条命令.
或者想控制流程,写成像git一样的一套命令.

CI的想法可能比个人想法更加成熟

  • 监测git仓库状态的改变
  • 使用yaml语法来做流程相关配置和控制.
  • 有时提供GUI的页面
    • 显示过程
    • 进行配置和管理

但本质来看依然不变,还是过程的自动化

主流产品

市面上虽然有多种CI工具,但主体思想大同小异:

  • 什么情况下做什么事情.
  • 另外需要指定一个执行用的机器称为runner

要解决的一些基础问题也雷同

  • 如何加密环境变量中的需要保密信息
  • 如何通过runner访问部署用的机器
  • 如果想在测试成功后做分支合并,需要权限
  • 等等

市面上虽然有多种CI工具,但都大同小异,各有特色,
但种类不下20种,会让人觉得搞这么多有意思吗.

  • Travis CI 和Github集成较好,市占率也最高
  • Gitlab CI Gitlab官方集成
  • Jenkins
  • Circle CI
  • 等等

Travis CI

Travis CI和Github集成较好,不是Github也可以,只是配置麻烦一些.
打开 travis-ci.org, 使用Github登陆,对想监听的仓库打对勾.就能使用了

使用时需要配置文件 .travis.yml.
配置文件举例

1
2
3
4
language: python    # 运行环境需要python
sudo: required # 需要sudo权限
install: sudo pip install foo # 需要安装一些东西
script: test.py # 需要搞一些测试

如果一个配置关键字有多条内容,则通常用列表来写

1
2
3
install:
- command1
- command2

需要注意的是

  • install后面的多个命令,一个失败后脚本不继续进行
  • script后的多个命令,一个失败后脚本依然继续进行,
    如果不想,则可以写成 script: command1 && command2

另外Travis提供基于ruby的cli工具(是看不起我python吗).
使用 gem install travis 就可以安装,然后就能使用 travis 命令

声明周期相关关键字

  1. before~install~(如果install是用一个x.sh脚本安装,但该脚本没有权限,此时给权限比较合适)
  2. install
  3. before~script~
  4. script
  5. aftersuccess or afterfailure
  6. before~deploy~
  7. deploy
  8. after~deploy~
  9. after~script~

默认脚本

install和script一般会有默认值,
具体默认值是什么,可以依据项目的语言来自动判断

java项目

1
2
3
4
5
6
7
language: java

jdk:
- oraclejdk8

install: mvn install -DskipTests=true -Dmaven.javadoc.skip=true
script: mvn test

node项目

1
2
3
4
5
6
language: node_js
node_js:
- "8"

install: npm install
script: npm test

php项目

1
2
3
4
5
language: php
php:
- 5.6
before_script: composer require phpunit/phpunit
script: phpunit Test.php

环境变量

使用env定义yaml内部使用的环境变量

1
2
3
4
5
env:
- DB=postgres
- SH=bash

script: $SH ./something

有些环境变量不能公开,有两种办法

  1. 在Travis网站上设置变量名和值,然后直接在配置文件中使用环境变量.
    (虽然只有登陆才能看见,但有可能还是担心明文存在网站上不好)

  2. 使用travis提供的工具加密

    1
    travis encrypt SOMEVAR=secretvalue

    加密后得到一段文字 secure: ".... encrypted data ....",
    包含了变量名和变量值.
    然后在配置中使用

    1
    2
    3
    4
    5
    6
    env:
    global:
    - secure: ".... encrypted data ...."


    script: echo $SOMEVAR

    还可以使用

    1
    travis encrypt SOMEVAR=secretvalue --add

    来自动添加加密的环境变量到配置文件中.

加密文件

假设开发用机器是A,通常会登陆部署端的服务器B,因此有一对ssh秘钥.A的公钥放在B上.

当runner测试完成,有可能需要登陆到部署端的服务器B,然后执行一些命令.
由于runner可能是公用的,因此有一种方案是,让runner使用A的私钥去登陆B.

但为了安全A的私钥不能直接给runner,需要加密一下.
使用

1
travis encrypt-file ~/.ssh/id_rsa --add
  • 生成一个id~rsa~.enc文件,需要把该文件放在git项目中.
  • 配置文件被更改了,在before~install字段添加了配置~ openssl ...

使用以下配置文件可使runner能够登陆到B上做操作

1
2
3
4
5
6
7
8
9
10
11
12
13
language: php
php:
- 5.6

before_install:
- openssl aes-256-cbc -K $encrypted_830d3b21a25d_key -iv $encrypted_830d3b21a25d_iv -in id_rsa.enc -out ~/.ssh/id_rsa -d # 将id_rsa_enc解密到了runner上的~/.ssh/id_rsa文件
- chmod 600 ~/.ssh/id_rsa # 有可能出现权限问题,于是先改一下
- echo -e "Host x.x.x.x\n\tStrictHostKeyChecking no\n" >> ~/.ssh/config
before_script: composer require phpunit/phpunit
script: phpunit Test.php

after_success:
- ssh [email protected] 'cd /var/www && git pull' # 为了部署专门新建一个用户,显得规范
  1. A的公钥放在B上

    1
    2
    3
    ssh-copy-id [email protected]:~/.ssh/authorized_keys
    # 相当于
    cat ~/.ssh/id_rsa.pub >> ssh:[email protected]:~/.ssh/authorized_keys

TODO 部署

目前没有中文文档,考虑阅读效率以后再完善,
现在暂时只给个例子

1
2
3
4
5
6
deploy:
provider: pages
skip_cleanup: true
github_token: $GITHUB_TOKEN # Set in travis-ci.org dashboard
on:
branch: master

其他

  • 如果想跳过自动构建,可以在 commit message 里加上[ci skip]

  • 如果想在Github的README中显示一个高大上的build:passing标志,可以在md中加一个来自travis的图片链接

    1
    [![Build Status](https://travis-ci.org/michaelliao/openweixin.svg?branch=master)](https://travis-ci.org/michaelliao/openweixin)

参考

  1. 阮一峰的初步介绍
  2. 主流CI工具
  3. 阮一峰关于Travis的简单介绍
  4. 廖雪峰的简单介绍
  5. Travis官方文档
  6. 文件加密功能的用途