上一次我们使用Gitea搭建了自己的Git版本控制系统,可以用来管理自己的代码
还需要一个自动构建工具来解放生产力,这里我推荐使用Drone来搭建CI/CD持续集成,持续部署平台 你懂的.png
为什么选用Gitea+Drone呢?因为这两款软件都是基于go编写的,运行过程中可谓十分轻量级,对于资源的占用都是很少的,并且都可以基于docker来安装,安装部署起来十分方便
相对来说主流的Gitlab+Jenkins一套下来对于资源的占用,个人是很难能流畅运行的,至少我的机器连个Gitlab都跑不起来,更别提使用Java编写的Jenkins了。。。 汗.png
废话少说,直接开搞~ 滑稽.png
这里我的服务器环境使用的是最新的CentOS7 x64系统,只安装了一个docker


1 安装Gitea

Gitea的安装过程详见:https://ffis.me/experience/1960.html
这里就不做过多的阐述了

2 安装Drone

新版的Drone可以直接使用OAuto2和drone进行通信,无缝集成,配置完成后只需要得到gitea的授权即可进入drone平台,连账户和密码都不用输了

2.1 Gitea创建OAuth2应用程序

我们进入Gitea-->点击右上角头像-->设置-->应用-->管理OAuth2应用程序
来创建一个OAuth2应用程序
2020-05-10T07:30:25.png

这里的重定向URL是授权成功后跳转到drone的地址,根据自己的drone地址来创建
2020-05-10T07:33:05.png

创建成功后就可以拿到客户端ID和客户端密钥了,这里可以先记录下来,我们后边会用到,因为一旦离开这个页面就没法再查看密钥了,只能重新生成

2.2 创建共享密钥

这里我们还需要创建一个共享密钥来供drone和docker-runner通信使用
我们可以使用openssl生成共享密钥:

$ openssl rand -hex 16
da7fb75c68106b563100bec5ce166b72

2.3 安装Docker Compose

这里我使用Docker Compose来部署Drone
Docker Compose是用来批量创建和管理Docker容器的,只需要配置好yml配置文件,就可以批量管理Docker容器
安装过程可参考:https://www.runoob.com/docker/docker-compose.html

2.4 编写docker-compose.yml文件

这里我们通过使用 Docker Compose 来构建并启动 Drone和 Docker Runner,编写 docker-compose.yml 文件

$ mkdir drone
$ cd drone
$ vim docker-compose.yml

在配置文件中,我们设置 docker-compose.yml 的格式为 3 号版本,定义以下两个docker服务。

  • Drone Server:使用drone/drone:1版本镜像,将 drone 容器的 80 端口映射到宿主机的 7079 端口。映射容器内 /data 目录到宿主机的 /data/drone 目录,以便 drone 可以保留数据。配置服务自动重新启动,并配置构建 drone 所需的环境变量。
  • Docker Runner:使用 drone/drone-runner-docker:1 版本镜像,将 docker 启动句柄挂载到容器 /var/run/docker.sock 文件中,以便 drone 可以使用 docker-runner 来执行镜像构建任务。环境变量中需要配置 drone server 的端口协议以及共享密钥,以便与 server 进行通信。

具体配置可参考如下配置:

version: '3'
services:
  # 容器名称
  fan-drone-server:
    # 构建所使用的镜像
    image: drone/drone:1
    # 映射容器内80端口到宿主机的7079端口
    ports:
      - 7079:80
    # 映射容器内/data目录到宿主机的/data/drone目录
    volumes:
      - /data/drone:/data
    # 容器随docker自动启动
    restart: always
    environment:
      # Gitea 服务器地址
      - DRONE_GITEA_SERVER=https://git.ffis.me
      # Gitea OAuth2客户端ID
      - DRONE_GITEA_CLIENT_ID=aaaaaaaaaaa-8888-8888-8888-fffffffffffff
      # Gitea OAuth2客户端密钥
      - DRONE_GITEA_CLIENT_SECRET=aaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbffffffff
      # drone的共享密钥
      - DRONE_RPC_SECRET=asdfsadfasdfsadfsadfasdf
      # drone的主机名
      - DRONE_SERVER_HOST=drone.ffis.me
      # 外部协议方案
      - DRONE_SERVER_PROTO=https
      # 创建管理员账户,这里对应为gitea的用户名
      - DRONE_USER_CREATE=username:noisky,admin:true

  fan-docker-runner:
    image: drone/drone-runner-docker:1
    ports:
      - 7080:3000
    restart: always
    depends_on:
      - fan-drone-server
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    environment:
      # 用于连接到Drone服务器的协议。该值必须是http或https。
      - DRONE_RPC_PROTO=https
      # 用于连接到Drone服务器的主机名
      - DRONE_RPC_HOST=drone.ffis.me
      # Drone服务器进行身份验证的共享密钥,和上面设置一样
      - DRONE_RPC_SECRET=asdfsadfasdfsadfsadfasdf
      # 限制运行程序可以执行的并发管道数。运行程序默认情况下执行2个并发管道。
      - DRONE_RUNNER_CAPACITY=2
      # docker runner 名称
      - DRONE_RUNNER_NAME=fan-docker-runner-1

2.5 构建drone和runner

docker-compose up -d
-d 为后台运行

这里我的drone使用的域名访问,是因为我配置了nginx反向代理,如果你没有配置也可以使用ip:端口的形式访问,一定要配置好域名再进行构建,不然会报错
贴出我的drone的nginx反向代理配置,供参考

location / {
  proxy_pass http://127.0.0.1:7079/;
      proxy_set_header Host $host;
      proxy_set_header X-Real-IP $remote_addr;
      proxy_set_header REMOTE-HOST $remote_addr;
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}

2.6 打开drone

到这里如果没有出问题的话drone就构建完成了,我们在浏览器输入配置的drone地址,可看到自动跳转到gitea的授权页面
2020-05-10T07:51:43.png

这里我们进行授权后就跳转到drone的页面了,只需授权一次即可,往后我们登录了gitea就直接进入drone了
进入drone会看到未激活的仓库,我们点击激活它
2020-05-10T07:53:25.png

激活后回到gitea,我们会看到仓库已经自动配置好了web钩子
2020-05-10T07:54:47.png

我们可以进去测试钩子,一般是没问题的
2020-05-10T07:55:18.png

到这里我们的drone就安装完毕了,激活仓库后,我们向仓库推送一个commit,gitea就会发送消息通知drone去干活啦~

3 配置Drone自动构建

drone的安装过程还是很简单的,下面我们来进行drone构建的配置
drone支持不同环境的不同管道配置,这里我使用Docker Pipelines管道配置
简单来说就是drone的每一步操作都在一个临时的drone的容器中进行,容器操作完会自动销毁,容器之间会共享当前的工作目录

这里我还是构建一个springboot工程,下面介绍两种部署方式:

3.1 本地部署

本地部署主要步骤:

  1. drone拉取最新的代码到当前工作目录
  2. drone创建maven/grade容器,通过指定的构建工具maven/grade等将代码编译/打包为jar包,然后将jar包和Dockerfile文件复制到挂载的宿主机共享目录中
  3. drone创建ssh容器,在容器中通过ssh连接到宿主机,通过定义好的命令,到jar和Dockerfile目录中将jar包制作构建为镜像,然后通过镜像创建应用容器并运行,期间会删除之前老的应用程序容器和镜像
  4. drone创建钉钉通知容器,将构建成功/失败的消息通过钉钉机器人通知到管理员

至此本地构建完毕,这个构建步骤是我自己搞的,其优点是所有操作都在本机执行,构建的应用不用经过网络传输,整体部署的速度很快,其缺点是只能单机部署,无法进行分布式多机器部署,如果自己个人单机使用的话推荐使用这种方式,我的测试demo,整体部署流程下来只用了12s,速度还是很快的
2020-05-10T08:13:22.png

编写drone部署的文件
在仓库的根目录下创建.drone.yml配置文件
配置文件参考如下配置:

# drone 本地构建
kind: pipeline
type: docker
name: MyHelloWorld

# drone构建步骤
steps:
  # 1.maven打包
  - name: maven compile
    pull: if-not-exists
    image: maven:ibmjava-alpine
    volumes:
      # maven构建缓存
      - name: cache
        path: /root/.m2
      # 挂载宿主机的目录
      - name: data
        path: /home
    commands:
      # 开始打包maven工程
      - cd demo
      - mvn clean package -Dmaven.test.skip=true
      # 将打包后的文件复制到宿主机映射目录
      - cp target/*.jar /home
      - cp ../Dockerfile /home

  # 2.使用ssh访问主机制作镜像并运行
  - name: ssh commands
    pull: if-not-exists
    image: appleboy/drone-ssh:1.5.7
    settings:
      host: 0.0.0.0
      username: root
      password:
        # 从drone仓库配置中秘密空间读取密码
        from_secret: ssh_password
      port: 22
      script:
        - echo =======暂停容器=======
        - docker stop `docker ps -a | grep springdemo | awk '{print $1}' `
        - echo =======暂停旧容器和镜像=======
        - docker rm -f `docker ps -a | grep springdemo | awk '{print $1}' `
        - docker rmi `docker images | grep springdemo | awk '{print $3}' `
        - echo =======开始构建新镜像=======
        - cd /data/drone/helloDemo
        - docker build -t springdemo:v1 .
        - echo =======开始部署应用=======
        - docker run -d -p 8188:8180 --name springdemo springdemo:v1
        - echo =======清理构建文件=======
        - rm -rf *
        - echo =======部署成功=======

  # 3.钉钉通知
  - name: dingTalk notification
    pull: if-not-exists
    image: guoxudongdocker/drone-dingtalk:latest
    settings:
      token:
        from_secret: dingtalk_token
      type: markdown
      message_color: true
      message_pic: true
      sha_link: true
    when:
      status: [failure, success]

# 挂载的主机卷,可以映射到docker容器中
volumes:
  # maven构建缓存
  - name: cache
    host:
      # path: /tmp/cache/.m2
      path: /var/lib/cache
  # maven构建后与宿主机通信的共享目录
  - name: data
    host:
      path: /data/drone/helloDemo

# drone执行触发器
trigger:
  branch:
    - master

文件编写完成后,git push到仓库中,gitea会通知drone进行部署,drone找到.drone.yml配置文件,就会按照配置文件中的步骤进行构建了,部署期间可以在drone中查看到每一步的部署情况
2020-05-10T08:22:16.png

3.2 分布式云部署

云部署主要步骤:

  1. drone拉取最新的代码到当前工作目录
  2. drone创建maven/grade容器,通过指定的构建工具maven/grade等将代码编译/打包为jar包,然后将打包完成后的jar移动到根目录,也就是工作目录
  3. drone创建docker构建容器,将jar通过dockerfile指定的方式构建为镜像,然后将构建完成的镜像上传到腾讯云/阿里云私有仓库中
  4. drone创建ssh容器,通过ssh连接到一个或多个主机,然后批量从私有仓库中拉取最新镜像进行部署
  5. drone创建钉钉通知容器,将构建成功/失败的消息通过钉钉机器人通知到管理员

这种方式配置较为简单,所有操作都在docker中进行,没有和主机通讯,并且构建好的镜像直接推送到第三方私有仓库中的,期间会通过网络传输,多多少少都会耗费点时间;
不过好在同一服务商的私有仓库都是通过内网进行访问的,访问速度影响可忽略不计;
而且有很大的优化空间,毕竟我们写的代码只有几百k的大小,其中的系统层、jdk层、lib依赖库层,基本上代码都是不变的;
所以可以通过对docker镜像进行分层构建,分层推送,通过缓存的加速也是可以做到秒级推送,
这样每次更新的就只有我们写的代码,传输的数据量就很少了,不过这个属于比较高级的应用了,我们这里先不研究了;
云部署主要好处是可以做到分布式部署,多个主机可以从私有仓库中拉取最新镜像,实用性还是很高的。

编写drone部署的文件
同样也是在仓库的根目录下创建.drone.yml配置文件
参考如下配置:

# drone 云部署
kind: pipeline
type: docker
name: MyHelloWorld

# drone构建步骤
steps:
  # 1.maven打包
  - name: maven compile
    pull: if-not-exists
    image: maven:ibmjava-alpine
    volumes:
      # maven构建缓存
      - name: cache
        path: /root/.m2
    commands:
      # 开始打包maven工程
      - cd demo
      - mvn clean package -Dmaven.test.skip=true
      # 将打包后的jar包移动到 Dockerfile 文件同级目录
      - mv target/demo-0.0.1-SNAPSHOT.jar ../demo-0.0.1-SNAPSHOT.jar
  # 2.Docker 制作镜像,推送到私有仓库
  - name: docker build
    image: plugins/docker
    pull: if-not-exists
    volumes:
      - name: docker
        path: /var/run/docker.sock
    settings:
      username: username
      password:
        from_secret: dockerHub_password
      tags:
        - latest
      repo: ccr.ccs.tencentyun.com/fanfan/hellodemo
      registry: ccr.ccs.tencentyun.com
      dockerfile: Dockerfile

  # 3.使用ssh访问主机运行最新版容器
  - name: ssh commands
    pull: if-not-exists
    image: appleboy/drone-ssh:1.5.7
    settings:
      host: 49.234.106.44
      username: root
      password:
        from_secret: ssh_password
      port: 22
      script:
        - echo =======暂停容器=======
        - docker stop `docker ps -a | grep springdemo | awk '{print $1}' `
        - echo =======暂停旧容器=======
        - docker rm -f `docker ps -a | grep springdemo | awk '{print $1}' `
        - echo =======开始部署应用=======
        - docker run -d -p 8188:8180 --name springdemo --restart=always ccr.ccs.tencentyun.com/fanfan/hellodemo:latest
        - echo =======部署成功=======

  # 4.钉钉通知
  - name: dingTalk notification
    pull: if-not-exists
    image: guoxudongdocker/drone-dingtalk:latest
    settings:
      token:
        from_secret: dingtalk_token
      type: markdown
      message_color: true
      message_pic: true
      sha_link: true
    when:
      status: [failure, success]

# 挂载的主机卷,可以映射到docker容器中
volumes:
  # maven构建缓存
  - name: cache
    host:
      # path: /tmp/cache/.m2
      path: /var/lib/cache
  - name: docker
    host:
      path: /var/run/docker.sock

# drone执行触发器
trigger:
  branch:
    - master

编写好部署的配置文件,推送到仓库中,drone就行按照配置的步骤进行部署了
首次部署的速度可能略慢,我这里用了50s,不过等仓库缓存变热之后速度就上来了,这里我用了33s
2020-05-10T08:40:33.png
总体很是可以接受的,如果进行分层构建优化的话,应该还会再快,不过还是和本地部署的速度没得比,这里根据自己的需要选择不同的部署方式即可,并且不同语言的应用部署的方式是不一样的,可以定制属于自己的部署方式

部署成功后的钉钉通知页面
2020-05-10T11:28:36.png

4 资源占用

Gitea和Drone都是基于go语言编写的,其优点是轻量级,占用资源较少,下面为部署了gitea和drone后的资源占用
2020-05-10T08:55:25.png
可以看到总占用也不到200M的内存,相比传统的gitlab+Jenkins非常轻量化了,并且该有的功能都有,如果用户数不是很大的话,还是很推荐大家使用的 笑眼.png

自动部署一旦配置好了,所有的编译/测试/打包/部署的工作就交给drone去完成了,而我们只需要专注于我们的代码编写就行了,是不是特别爽呢?还不快去搭建一个属于自己的自动构建平台吧~ 太开心.png