【Docker】之使用Gitea+Drone实现持续集成(CI/CD)

前言

  在开发或生产环境中,我们经常会搞一套自动化部署方案(俗称一键部署)。比较流行的一种就是Gitlab+Jenkins实现方案,不过这种方案占用内存比较大,没有个8G内存,很难流畅运行,而且部署起来也不快。最近发现一款神器Drone,轻量级CI/DI工具,结合Gogs使用内存占用不到1G,几行脚本就能实现自动化部署,推荐给大家!

安装Gitea

  Gitea 是一个开源社区驱动的 Gogs 克隆, 是一个轻量级的代码托管解决方案,后端采用 Go 编写,采用 MIT 许可证.
  Gitea的是创建一个极易安装,运行非常快速,安装和使用体验良好的自建 Git 服务。采用Go作为后端语言,只要生成一个可执行程序即可。并且它还支持跨平台,支持 Linux, macOS 和 Windows 以及各种架构,除了x86,amd64,还包括 ARM 和 PowerPC。

  安装过程,参考:【Docker】之安装Gitea

安装Drone

  Drone是一种基于容器技术的持续交付系统。Drone使用简单的YAML配置文件(docker-compose的超集)来定义和执行Docker容器中的Pipelines。
  Drone与流行的源代码管理系统无缝集成,包括GitHub,GitHub Enterprise,Bitbucket等。
  Drone附上官网文档地址:https://docs.drone.io/

  1. 首先下载Drone的Server和Runner的镜像;
    # Drone的Server
    docker pull drone/drone
    # Drone的Runner
    docker pull drone/drone-runner-docker
    • 这里有个Server和Runner的概念,我们先来理解下;
      • Server:为Drone的管理提供了Web页面,用于管理从Git上获取的仓库中的流水线任务。
      • Runner:一个单独的守护进程,会轮询Server,获取需要执行的流水线任务,之后执行。
  2. 生成GiteaOauth
      gitea右上角->设置->应用->创建应用
    客户端id : f4d1e22d-b092-4e86-bedd-dffb362b1c70
    客户端秘钥 : EBjiksxi7QBaAtY9cIisGY36Ghr81Y4vtk2uqj8g5qEa
      生成rpc秘钥: openssl rand -hex 16
    [root@loaclhost build]# openssl rand -hex 16 
    1bd0710533d6ade9ab943df6822e7e1d
      这个用于DRONE_RPC_SECRET,当然也可随便设置值(dronerpc666)
  3. 接下来我们来安装drone-server,使用如下命令即可;
    docker run \
    --net docker-mynet --ip 172.172.0.12 \
    --volume=/root/.docker/drone/data:/data/docker/drone \
    --env=DRONE_AGENTS_ENABLED=true \
    --env=DRONE_GIT_ALWAYS_AUTH=true \
    --env=DRONE_GITEA_SERVER=http://10.211.55.81:3000 \
    --env=DRONE_GITEA_CLIENT_ID=f4d1e22d-b092-4e86-bedd-dffb362b1c70 \
    --env=DRONE_GITEA_CLIENT_SECRET=EBjiksxi7QBaAtY9cIisGY36Ghr81Y4vtk2uqj8g5qEa \
    --env=DRONE_RPC_SECRET=1bd0710533d6ade9ab943df6822e7e1d \
    --env=DRONE_SERVER_HOST=10.211.55.81:3080 \
    --env=DRONE_SERVER_PROTO=http \
    --env=DRONE_USER_CREATE=username:leeze,admin:true \
    --publish=3080:80 \
    --publish=10443:443 \
    --restart=always \
    --detach=true \
    --name=drone \
    drone/drone:latest
    # 这里的配置参数比较多,下面统一解释下;
    DRONE_AGENTS_ENABLED : 是否开启身份验证
    DRONE_GITEA_SERVER : Gitea服务器地址((建议填写宿主机地址+端口, 需要`http://`开头))
    DRONE_GITEA_CLIENT_ID : 应用的客户端id(gitea上生成的Client ID)
    DRONE_GITEA_CLIENT_SECRET : 应用的客户端秘钥(gitea上生成的Client Secret)
    DRONE_RPC_SECRET : Drone的共享秘钥,用于验证连接到server的rpc连接,server和runner需要提供同样的秘钥。(后面安装runner使用相同的秘钥)
    DRONE_SERVER_HOST : Drone服务地址,外部可访问的域名或IP地址(建议填写宿主机地址+端口, 不需要`http://`开头)
    DRONE_SERVER_PROTO : Drone提供服务的协议类型(可选为`http`或`https`)
    DRONE_USER_CREATE : 设置Drone管理员账号(`注意设置的是Gitea平台里的账号`), `Drone`是直接使用`Gogs账号`登录的
      调试阶段建议启动设置detach=false, 这样可以查看日志输出
  4. 接下来安装drone-runner-docker,当有需要执行的任务时,会启动临时的容器来执行流水线任务;
    docker run -d \
    -v /var/run/docker.sock:/var/run/docker.sock \
    --net docker-mynet --ip 172.172.0.13 \
    -e DRONE_RPC_PROTO=http \
    -e DRONE_RPC_HOST=10.211.55.81:3080 \
    -e DRONE_RPC_SECRET=1bd0710533d6ade9ab943df6822e7e1d \
    -e DRONE_RUNNER_CAPACITY=2 \
    -e DRONE_RUNNER_NAME=runner-docker \
    -e TZ="Asia/Shanghai" \
    -p 3010:3000 \
    --restart always \
    --name runner-docker \
    drone/drone-runner-docker:latest
    # 这里的配置参数比较多,下面统一解释下。
    DRONE_RPC_PROTO : 用于配置连接到Drone server的协议(必须是`http`或`https`)
    DRONE_RPC_HOST : 用于配置Drone server的访问地址,runner会连接到server获取流水线任务并执行。
    DRONE_RPC_SECRET : 用于配置连接到Drone server的共享秘钥。
    DRONE_RUNNER_CAPACITY : 限制runner并发执行的流水线任务数量(默认2)。
    DRONE_RUNNER_NAME : 自定义runner的名称。
  5. 开放相关端口:
    firewall-cmd  --zone=public --add-port=3080/tcp --permanent
    firewall-cmd --reload

    Drone使用

      让我们来访问下Drone的控制台页面,第一次登录需要输入账号密码(在Gitea中注册的账号),访问地址:http://10.211.55.81:3080/

  此时我们在Gitea中的项目会现在在列表中,如果没有的话可以点下SYNC按钮;

  接下来我们需要对仓库进行设置,将仓库设置为Trusted(否则Drone创建的容器无法挂载目录到宿主机),最后点击SAVE按钮保存;

  保存成功后会在Gitea中自动配置一个Web钩子,当我们推送代码到Gitea中去时,会触发这个钩子,然后执行在Drone中的流水线任务;

  拉到最下面,我们可以发送一个测试推送,推送成功会显示绿色的√;

  此时我们在Drone中发现其实流水线执行失败了,那是因为我们在脚本中引用了Secret中的ssh_password

  在仓库的设置中添加一个Secret即可,Secret是专门用来存储密码的,此密码只能被使用或删除,无法被查看;

  在ACTIVITY FEED中使用RESTART可以重新执行该流水线,发现已经成功执行。

编写脚本

  当我们向Git仓库Push代码时,会自动触发Web钩子,然后Drone就会从Git仓库Clone代码,再通过项目目录下的.drone.yml配置,执行相应的流水线,接下来我们来看看这个脚本是如何写的。

  1. 项目结构
  2. 首先我们来了解下在.drone.yml中配置的工作流都有哪些操作,看下流程图就知道了;
  3. 再来一个完整的.drone.yml,配上详细的注解,看下就基本懂了!
    kind: pipeline # 定义对象类型,还有secret和signature两种类型
    type: docker # 定义流水线类型,还有kubernetes、exec、ssh等类型
    name: mybatisplus-drone # 定义流水线名称
    
    steps: # 定义流水线执行步骤,这些步骤将顺序执行
      - name: package # 流水线名称
        image: maven:3-jdk-8 # 定义创建容器的Docker镜像
        volumes: # 将容器内目录挂载到宿主机,仓库需要开启Trusted设置
          - name: maven-cache
            path: /root/.m2 # 将maven下载依赖的目录挂载出来,防止重复下载
          - name: maven-build
            path: /app/build # 将应用打包好的Jar和执行脚本挂载出来
        commands: # 定义在Docker容器中执行的shell命令
          - mvn clean package # 应用打包命令
          - cp mybatisplus/target/mybatisplus-0.0.1-SNAPSHOT.jar /app/build/mybatisplus.jar
          - cp Dockerfile /app/build/Dockerfile
          - cp run.sh /app/build/run.sh
    
      - name: build-start
        image: appleboy/drone-ssh # SSH工具镜像
        settings:
          host: 10.211.55.81 # 远程连接地址
          username: root # 远程连接账号
          password:
            from_secret: ssh_password # 从Secret中读取SSH密码
          port: 22 # 远程连接端口
          command_timeout: 5m # 远程执行命令超时时间
          script:
            - cd /mydata/maven/build # 进入宿主机构建目录
            - chmod +x run.sh # 更改为可执行脚本
            - ./run.sh # 运行脚本打包应用镜像并运行
    
    volumes: # 定义流水线挂载目录,用于共享数据
      - name: maven-build
        host:
          path: /mydata/maven/build # 从宿主机中挂载的目录
      - name: maven-cache
        host:
          path: /mydata/maven/cache
  4. Dockerfile
    # 该镜像需要依赖的基础镜像
    FROM clockard/alpine-java:8_jdk
    LABEL maintainer="mybatisplus" \
          version="1.0.0" \
          description="This is mybatisplus's test."
    RUN echo "Asia/Shanghai" > /etc/timezone
    # 将当前目录下的jar包复制到docker容器的/目录下
    ADD mybatisplus.jar /app/mybatisplus.jar
    # 指定docker容器启动时运行jar包
    #在容器启动的时候运行命令,来启动我们的项目
    ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app/mybatisplus.jar","-Xms128m","-Xmx128m","-c"]
    # 指定维护者的名字
    MAINTAINER leeze
    ```   
    5. `run.sh`执行脚本可以实现打包应用和运行容器镜像
    ```bash
    #!/usr/bin/env bash
    # 定义应用组名
    group_name='zancunli'
    # 定义应用名称
    app_name='mybatisplus'
    # 定义应用版本
    app_version='1.0.0'
    # 定义应用环境
    #profile_active='prod'
    echo '----copy jar----'
    docker stop ${app_name}
    echo '----stop container----'
    docker rm ${app_name}
    echo '----rm container----'
    docker rmi ${group_name}/${app_name}:${app_version}
    echo '----rm image----'
    # 打包编译docker镜像
    docker build -t ${group_name}/${app_name}:${app_version} .
    echo '----build image----'
    docker run -p 7080:9091 --name ${app_name}  --net docker-mynet --ip 172.172.0.88 \
    -e TZ="Asia/Shanghai" \
    -v /etc/localtime:/etc/localtime \
    -v /mydata/app/${app_name}/logs:/var/logs \
    -d ${group_name}/${app_name}:${app_version} \
    echo '----start container----'
  5. 运行成功
      浏览器:http://10.211.55.81:7080/user/list ,成功取到数据

问题

  1. gitea & drone webhook推送不成功
    修改 Gitea 服务器的 Webhook 白名单
    出于安全考虑,您通过 Gitea Webhook 触发外部服务器的响应前需要设定 webhook.ALLOWED_HOST_LIST 白名单来控制 Webhook 的目的地址。具体信息参考文档 Webhook
    修改配置时,打开 conf/app.ini,添加 ALLOWED_HOST_LIST = * 到 [webhook] 栏目中,并重启 Gitea 服务器。例如:
    [webhook]
    ALLOWED_HOST_LIST = *
    # 添加 gitea/conf/app.ini
    
    [webhook]
    ALLOWED_HOST_LIST = 192.168.0.0/16

参考资料

官方文档:https://docs.drone.io/
结合Maven使用:https://docs.drone.io/pipeline/kubernetes/examples/language/maven/
结合SSH使用:http://plugins.drone.io/appleboy/drone-ssh/
将容器目录挂载到宿主机:https://docs.drone.io/pipeline/docker/syntax/volumes/host/