建木持续集成平台简单使用教程

猎隼丶止戈 猎隼丶止戈 | 450 | 2023-03-17

前言

建木持续集成平台基于建木,致力于为国内开发者与 DevOps 人员提供极致用户体验,提升开发、上线、运维的效率,让软件用户专注于提供业务价值。

以下为简略文档,详细文档请访问:官方文档

部署

前置条件

资源和系统要求

建议操作系统:Ubuntu 21.04(macOS Monterey)

建议配置:

  • CPU:2C
  • 内存:8GB
  • 磁盘:100GB

软件版本要求

安装建木

下载 docker-compose

注意:Mysql​ 版本需要 8.0​ 以上

wget https://gitee.com/jianmu-dev/jianmu-deploy/raw/master/docker-compose.yml

示例如下(修改成自己的配置):

version: '3'
services:
  jianmu-mysql:
    container_name: jianmu-mysql
    image: mysql:8
    command: --init-connect='SET NAMES utf8' --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci --max-connections=1200 --max-user-connections=1000
    ports:
      - "3306:3306"
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: 123456
      MYSQL_DATABASE: "jianmu"
      TZ: "Asia/Shanghai"
    volumes:
      - /home/jianmu/mysql/conf:/etc/mysql
      - /home/jianmu/mysql/data:/var/lib/mysql
      - /home/jianmu/mysql/mysql-files:/var/lib/mysql-files
  ci-server:
    image: docker.jianmuhub.com/jianmu/jianmu-server:v2.7.0
    container_name: jianmu-ci-server
    restart: always
    environment:
      SPRING_PROFILES_ACTIVE: dev
      SPRING_DATASOURCE_URL: jdbc:mysql://jianmu-mysql:3306/jianmu?useUnicode=true&characterEncoding=utf8&useSSL=false&allowPublicKeyRetrieval=true
      SPRING_DATASOURCE_USERNAME: root
      SPRING_DATASOURCE_PASSWORD: 123456
      JIANMU_WORKER_SECRET: worker-secret
      JIANMU_API_ADMINPASSWD: 123456
#      REGISTRY_AK:
#      REGISTRY_SK:
    volumes:
      - /home/jianmu/ci:/ci
    depends_on:
      - jianmu-mysql
    entrypoint: ["wait-for-it.sh", "jianmu-mysql:3306", "-t", "0", "--", "java", "-Duser.timezone=Asia/Shanghai",  "-jar", "jianmu-server.jar"]
  worker:
    image: docker.jianmuhub.com/jianmu/jianmu-worker-docker:v1.0.9
    container_name: jianmu-worker
    restart: always
    environment:
      JIANMU_SRV_ADDRESS: http://ci-server:8081
      JIANMU_SRV_SECRET: worker-secret
      JIANMU_WORKER_ID: worker1
      # 可同时并发的容器数量,默认为2
      JIANMU_WORKER_CAPACITY: 5
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    depends_on:
      - jianmu-mysql
      - ci-server
  web:
    image: docker.jianmuhub.com/jianmu/jianmu-ui:v2.7.0
    container_name: jianmu-web
    ports:
      - "8088:80"
#      - "443:443"
    restart: always
    depends_on:
      - jianmu-mysql
      - ci-server

networks:
  default:
    ipam:
      config:
        - subnet: 10.1.0.0/16

启动

docker-compose up -d

访问 http://localhost​,默认用户名密码为 admin/123456​ 。

CentOS 7.9 安装注意事项

在 CentOS 7.9 安装过程需要替换自带的 docker。

以 root 权限登陆系统,更新系统并安装 docker。

更新操作系统,CentOS 7.9 最新版本:

yum -y update

移除 CentOS 默认的 docker 版本:

yum remove docker \
                  docker-client \
                  docker-client-latest \
                  docker-common \
                  docker-latest \
                  docker-latest-logrotate \
                  docker-logrotate \
                  docker-engine

配置 docker 官方 yum 仓库:

yum install -y yum-utils
yum-config-manager \
    --add-repo \
    https://download.docker.com/linux/centos/docker-ce.repo

安装 docker 并启动:

yum install docker-ce docker-ce-cli containerd.io
systemctl enable docker
systemctl restart docker

安装 docker-compose:

curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose

配置 docker-compose 权限:

chmod +x /usr/local/bin/docker-compose

验证环境可用性如下:

[root@localhost ~]# docker version
Client: Docker Engine - Community
 Version:           20.10.11
 API version:       1.41
 Go version:        go1.16.9
 Git commit:        dea9396
 Built:             Thu Nov 18 00:38:53 2021
 OS/Arch:           linux/amd64
 Context:           default
 Experimental:      true

Server: Docker Engine - Community
 Engine:
  Version:          20.10.11
  API version:      1.41 (minimum version 1.12)
  Go version:       go1.16.9
  Git commit:       847da18
  Built:            Thu Nov 18 00:37:17 2021
  OS/Arch:          linux/amd64
  Experimental:     false
 containerd:
  Version:          1.4.12
  GitCommit:        7b11cfaabd73bb80907dd23182b9347b4245eb5d
 runc:
  Version:          1.0.2
  GitCommit:        v1.0.2-0-g52b36a2
 docker-init:
  Version:          0.19.0
  GitCommit:        de40ad0

[root@localhost ~]# docker-compose version
docker-compose version 1.29.2, build 5becea4c
docker-py version: 5.0.0
CPython version: 3.7.10
OpenSSL version: OpenSSL 1.1.0l  10 Sep 2019

使用示例

平台部署

了解缓存

版本说明:v2.7.0 开始支持

注意:只在 docker worker 支持

可在 global​ 段落下定义 cache​ ,已定义的 cache​ 可以在节点中引用,用于解决项目级缓存问题。

  • 同一项目中 cache 唯一
  • 项目之间的 cache 相互隔离,避免资源冲突
  • DSL 中删除某个 cache 配置时,该 cache 会被清除
  • 项目删除时,会清除项目的所有 cache

语法:

global:
  # 定义单个缓存
  cache: m2
  
 

global:
  # 定义多个缓存
  cache:
    - m1
    - m2

pipiline:
   maven_build: 
     type: maven_build:1.4.0-jdk11
     # 在节点中引用缓存,可引用多个
     cache:
       # 表示容器中的 /.m1/repository 目录会挂载到缓存m1上
       m1: /.m1/repository
       m2: /.m2/repository
     param:
       mvn_action: test
       workspace: ${git_clone.git_path}
   shell: 
     image: alpine:3.13.6
     # 可在shell节点中引用缓存
     cache:
       m2: /xxx
     script:
       - ls -l /xxx

了解 Docker 数据卷

数据卷 是一个可供一个或多个容器使用的特殊目录,它绕过 UFS,可以提供很多有用的特性:

  • 数据卷 可以在容器之间共享和重用
  • 对 数据卷 的修改会立马生效
  • 对 数据卷 的更新,不会影响镜像
  • 数据卷 默认会一直存在,即使容器被删除

注意:数据卷 的使用,类似于 Linux 下对目录或文件进行 mount,镜像中的被指定为挂载点的目录中的文件会复制到数据卷中(仅数据卷为空时会复制)。

编译节点说明

  1. maven 节点在 1.4.0​ 版本开始支持修改 local_repo_path​​ (本地仓库路径) 默认值为 /.m2/repository​​
  2. nodejs 节点在 1.6.0​ 版本开始支持修改 cache_path​​ (本地缓存目录) 默认值为 /cache​​

建木 DSL 配置

name: 演示环境
description: 演示环境

# 每天早上6:00自动触发
trigger:
  type: cron
  schedule: 0 0 6 * * ?

global:
  enabled:
    value: true
    mutable: true
  param:
    git_ref: master
    demo_git_ref: dev
    git_frame_url: http://192.168.0.2:10080/test/frame.git
    git_demo_url: http://192.168.0.2:10080/test/demo.git
    mvn_rep: http://192.168.0.125:9888/repository/maven-public/
    img_name: 192.168.0.88/library/demo-api
    img_tag: latest
  cache: m2

pipeline:

  # 同步git上的最新代码
  frame:
    type: git_clone:1.2.1
    param:
      remote_url: ${global.git_frame_url}
      ref: ${global.demo_git_ref}
      username: ((gogs.username))
      password: ((gogs.password))
  demo:
    type: git_clone:1.2.1
    param:
      remote_url: ${global.git_demo_url}
      ref: ${global.git_ref}
      username: ((gogs.username))
      password: ((gogs.password))

  # maven编译打包
  maven_frame_build:
    type: maven_build:1.4.0-jdk8
    cache:
      m2: /home/jianmu/maven
    param:
      mvn_action: clean install -Dmaven.test.skip=true
      workspace: ${frame.git_path}
      maven_public_url: ${global.mvn_rep}
      local_repo_path: /home/jianmu/maven
  maven_demo_build:
    type: maven_build:1.4.0-jdk8
    cache:
      m2: /home/jianmu/maven
    param:
      mvn_action: clean install -Dmaven.test.skip=true
      workspace: ${demo.git_path}
      maven_public_url: ${global.mvn_rep}
      local_repo_path: /home/jianmu/maven
  
  # 构建docker镜像并推送到私仓
  docker_build:
    type: docker_image_build:1.1.0
    param:
      registry_address: 192.168.0.88
      docker_username: ((harbor.username))
      docker_password: ((harbor.password))
      image_name: ${global.img_name}
      image_tag: ${global.img_tag}
      docker_file: Dockerfile
      docker_build_path: .
      workspace: ${demo.git_path}/demo-api

  # 连接81服务器,执行部署命令
  deploy_demo:
    type: ssh_cmd:1.0.1
    param:
      ssh_ip: 192.168.0.81
      ssh_user: root
      ssh_private_key: ((81_ssh.ssh_pk))
      ssh_cmd: >-
        cd /home/demo-api 
        && 
        docker images 
        &&
        docker-compose down 
        && 
        docker pull ${global.img_name}:${global.img_tag} 
        && 
        docker-compose up -d 
        && 
        docker ps -a | grep demo 
        && 
        echo done

关键说明:

  1. 在全局变量定义了一个 maven 缓存,变量名为 m2

    global:
      cache: m2
    
  2. 新增一个 maven build 节点,使用缓存 m2

    pipeline:
    
      # maven编译打包
      maven_frame_build:
        type: maven_build:1.4.0-jdk8
        cache:
          m2: /home/jianmu/maven
        param:
          local_repo_path: /home/jianmu/maven
    

    注意:local_repo_path 默认路径为 /.m2/repository

  3. 上方完整示例执行时,将会把 jar 包下载在容器内部的 /home/jianmu/maven​ 目录下,宿主机路径为 docker 启动默认生成的数据卷(volume)

    可以使用以下命令查看:

    docker volume ls
    
  4. 第二次执行时,建木将把宿主机上的数据卷(volume)重新挂载到容器内部的 /home/jianmu/maven​ 目录

文档自动更新

了解 Webhook

Webhook​ 可以接收外部事件来触发项目执行

定义语法:

trigger:
  type: webhook
  param:
    - name:  xxx
      type: STRING
      exp: $.xxx.xxx
  auth: 
    token: ${trigger.xxx}
    value: ((xxx.xxx))
  only: (${trigger.xxx} == "xxx")
  • type:此处固定填 webhook​,必填

  • param:触发器参数,非必填

    • name:参数名称,必填

    • type:参数类型,支持类型:STRING​ 、NUMBER​ 、BOOL​ 、SECRET​,必填

    • exp:提取参数表达式,必填

      注意事项:v2.2.4​ 开始,若 type​ 为 STRING​ 时,提取的值将被自动 trim​ 处理,解决 yml​ 语法带来的转义冲突

  • auth:认证鉴权,非必填

  • token:Webhook 请求携带的认证鉴权数据,如:请求头中的 Authorization​ 、Token​ 等,auth​ 存在时必填,支持运算表达式和字符串模版

  • value:用于校验 token​ 值,相同则验证成功,必须是密钥类型,auth​ 存在时必填

  • only:匹配规则,结果为 true​ 时触发流程,当前只可引用触发器参数,暂不支持引用全局参数,非必填。

示例:

trigger:
  type: webhook
  param:
    - name: gitee_token
      type: SECRET
      exp: $.header.X-Gitee-Token
    - name: pr_state
      type: STRING
      exp: $.body.json.pull_request.state
    - name: pr_target_branch
      type: STRING
      exp: $.body.json.target_branch
  auth:
    token: ${trigger.gitee_token}
    value: ((gitee.webhook_token))
  only: ((${trigger.pr_target_branch} == "wap" || ${trigger.pr_target_branch} == "web") && ${trigger.pr_state} == "merged")

可使用 JsonPath​ 从 Webhook​ 请求中提取触发器参数,以供项目 DSL​ 中引用

根据提取的位置不同,提取规则如下:

  • Header:$.header.xxx,http header

  • Query:$.query.xxx,http querystring

  • Body:

    • JSON:$.body.json.xxx,http body,Content-Type为application/json
    • Form 表单:$.body.form.xxx,http body,Content-Type为application/x-www-form-urlencoded
    • Text:$.body.text,http body,Content-Type为text/plain
Webhook:
payload: {
  "header": {
    "content-length": "149",
    "postman-token": "2ffe99d2-ff91-4546-a029-a9da8093c1ff",
    "x-nginx-proxy": "true",
    "connection": "close",
    "content-type": "application/json",
    "x-client-type": "jianmu",
    "accept": "*/*"
  },
  "query": {
    "phone": "10086"
  },
  "body": {
    "json": {
      "token": "yWi6XOJQ2VQ9BKSUnlK9jTiHY",
      "user": [
        {
          "name": "Jerry",
          "age": 3
        },
        {
          "name": "Tom",
          "age": 5
        }
      ]
    }
  }
}

提取示例:

trigger:
  type: webhook
  param:
    - name: type
      type: STRING
      exp: $.header.x-client-type
    - name: phone
      type: STRING
      exp: $.query.phone
    - name: token
      type: STRING
      exp: $.body.json.token
    - name: name
      type: STRIING
      exp: $.body.json.user[0].name
    - name: age
      type: NUMBER
      exp: $.body.json.user[0].age
  • 表达式 $.header.x-client-type​ 的结果是 jianmu
  • 表达式 $.query.phone​ 的结果是 10086
  • 表达式 $.body.json.token​ 的结果是 yWi6XOJQ2VQ9BKSUnlK9jTiHY
  • 表达式 $.body.json.user[0].name​ 的结果是 Jerry
  • 表达式 $.body.json.user[0].age​ 的结果是 3

在项目 DSL​​ 中添加以上定义,保存后会自动生成 Webhook​​ 链接。

​​

Gogs Webhook 配置

建木 DSL 配置

name: 文档更新
description: 文档更新

# 生成webhook
trigger:
  type: webhook
  param:
    - name: event
      type: STRING
      exp: $.header.X-Gogs-Event
  only: (${trigger.event} === "push")

pipeline:
  # 连接81服务器,调用同步frame-doc仓库数据
  deploy_demo:
    type: ssh_cmd:1.0.1
    param:
      ssh_ip: 192.168.0.81
      ssh_user: root
      ssh_private_key: ((81_ssh.ssh_pk))
      ssh_cmd: >-
        cd /home/frame-doc/docsify/frame-doc 
        && 
        git pull 
        && 
        echo done

执行结果

附录

机器间免密登录

ssh​ 命令用于远程登录上 Linux 主机。常用格式:

ssh [-l login_name] [-p port] [user@]hostname

环境信息

配置两台机器,操作系统都为 Centos7​ ,信息如下:

  • Server A: 192.168.9.100
  • Server B: 192.168.9.110

免密码登录原理

图解,server A 免登录到 server B:

  1. A​ 上生成公钥私钥。
  2. 将公钥拷贝给 server B​ ,要重命名成 authorized_keys​ (从英文名就知道含义了)
  3. Server A​ 向 Server B​ 发送一个连接请求。
  4. Server B​ 得到 Server A​ 的信息后,在 authorized_key​ 中查找,如果有相应的用户名和 IP​ ,则随机生成一个字符串,并用 Server A​ 的公钥加密,发送给 Server A​ 。
  5. Server A​ 得到 Server B​ 发来的消息后,使用私钥进行解密,然后将解密后的字符串发送给 Server B​ 。Server B​ 进行和生成的对比,如果一致,则允许免登录。
  6. 得到 server B​ 发来的消息后,会使用私钥进行解析,然后将机密后的字符串发给 server B​ 。
  7. 接收到机密后的字符串会跟先前生成的字符串进行对比,如果一致就允许免密登陆。

Linux 下生成密钥

查看 ssh-keygen 的命令手册。

man ssh-keygen

  1. 通过命令生成一对密钥,执行之后会在用户的根目录生成一个 .ssh​ 的文件夹。

    ssh-keygen -t rsa
    

  2. 用户的根目录查看生成的 .ssh​ 文件夹

  3. 查看 .ssh​ 文件下生成的文件

    文件介绍:

    • authorized_keys:存放远程免密登录的公钥,主要通过这个文件记录多台机器的公钥
    • id_rsa:生成的私钥文件
    • id_rsa.pub:生成的公钥文件
    • know_hosts:已知的主机公钥清单

免密登陆设置

总之:A 要免密码登录到 B,B 首先要拥有 A 的公钥,然后 B 要做一次加密验证。对于非对称加密,公钥加密的密文不能公钥解开,只能私钥解开。

手工复制粘贴的方式
  1. 拷贝本地主机的公钥到远程主机

    scp id_rsa.pub
    
  2. 将本地主机的公钥加到远程主机的授权列表 .ssh/authorized_keys

    若不存在,手动创建

    cat id_rsa.pub >> authorized_keys
    
  3. .ssh​ 目录的权限必须是 700

    chmod 700 .ssh
    
  4. 授权列表 authorized_keys​ 的权限必须是 600

    chmod 600 authorized_keys
    
通过 ssh-copy-id 的方式

命令:

ssh-copy-id -i ~/.ssh/id_rsa.pub [romte_ip]

例如:

ssh-copy-id -i ~/.ssh/id_rsa.pub 192.168.9.110

结果如图:

ssh-copy-id​ 命令可以把本地主机的公钥复制到远程主机的 authorized_keys​ 文件上,ssh-copy-id​ 命令也会给远程主机的用户主目录(home)和 /.ssh​ , 和 /.ssh/authorized_keys​ 设置合适的权限。

文章标签: CI/CD
推荐指数:

真诚点赞 诚不我欺~

建木持续集成平台简单使用教程

点赞 收藏 评论

关于作者

猎隼丶止戈
猎隼丶止戈

这个人很懒~

等级 LV3

粉丝 13

获赞 28

经验 363