转载

基于 Docker 部署 GitLab + Jenkins + nginx 自动化环境

前段时间入职新公司,整体感觉不错。唯一不太帅的地方是公司配备的2015版15寸 MBP + 双2k屏,出于安全考虑不能使用自己电脑,导致我的2017版只能在家做个备机。

后来在开发过程中发现有一个略费劲的地方,就是部署环境,我们是使用强大的 Jenkins + GitLab ,基于分支开发,因为之前我是使用 GitHub + Travis + Netlify 工作流,那么可以找一些插件优化下目前的环境问题,再加上我们是前后端分离这样的天然优势,其实能做的事情很多。而我又是想到做到的理念,那就动手吧,但面临个问题: Jenkins + GitLab 运行耗费资源且本地安装麻烦 ,正好赶上最近两天在学习 Docker ,那就本地搞起吧。

目标

git push

准备工作

使用功能、服务:

  • Docker Runtime - 本地 Docker 运行环境
  • GitLab Docker - 镜像,由 nginx 代理访问
  • Jenkins Docker - 镜像,由 nginx 代理访问
  • nginx Docker - 镜像,本地访问入口
  • 一个可以配置泛路径的域名

开发流程

  1. 保护 Master 分支,禁止
  2. 开发时迁出别的分支,如: feat/home-v2
  3. 开发完成本地自测后,向 Master 发起 Merge Request(PR)
  4. Jenkins 自动拉取最新代码,运行 npm run build ,成功后把产出目录( dist/ )按约定复制到指定目录
  5. nginx 直接使用泛域名转发到对应路径,也就是预览环境
  6. 在预览环境回归、测试,通过后合入 Master

部署环境

使用 Docker-compose 运行:

version: '3'

services:
  jenkins:
    container_name: 'jenkins.ci' # 镜像名称,为了统一使用 .ci 域名
    image: 'jenkins/jenkins:lts'
    # ports: 不对外暴露端口,使用 nginx 容器里直接转到了 jenkins:8080 即可
      # - '8080:8080'
      # - '50000:50000'
    volumes: # 路径映射,html 主要是编译打包后需要存放的路径
      - './html:/usr/share/jenkins/html'
      - './jenkins/data:/var/jenkins_home'
  gitlab:
    container_name: 'gitlab.ci' # 镜像名称,为了统一使用 .ci 域名
    image: 'gitlab/gitlab-ee:latest'
    restart: always
    hostname: 'gitlab.ci'
    ports: # 只对外开发 22 ssh 端口,web 服务由 nginx 容器里直接转到了 gitlab.ci 即可,因为本身 GitLab 镜像对外就是80
      - '22:22'
      # - '80:80'
      # - '443:443'
    volumes:
      - './gitlab/config:/etc/gitlab'
      - './gitlab/logs:/var/log/gitlab'
      - './gitlab/data:/var/opt/gitlab'
  nginx:
    container_name: 'nginx'
    depends_on: # 依赖其她两个服务运行
      - jenkins
      - gitlab
    image: nginx:alpine
    volumes: # 映射代码路径、配置文件路径
      - './html:/usr/share/nginx/html'
      - './nginx/nginx.conf:/etc/nginx/nginx.conf' # nginx 配置入口
      - './nginx/conf:/etc/nginx/conf' # jenkins.ci gitlab.ci 的配置,和预览环境的配置
    ports:
      - 80:80 # 统一对外映射
    restart: always

权限和配置

本地 Hosts

由于镜像名称是 gitlab.cijenkins.ci ,那么在容器内天然的支持直接使用这两个名称访问,但本地需要配置这两个名称到本地的 127.0.0.1

Jenkins

  • GitLab Plugin - 连接 GitLab 的插件
  • NodeJS plugin - Node 运行环境,并在系统管理-全局工具配置里配置需要的 NodeJS 版本

GitLab 访问 Jenkins

需要用到 GitLab webhook ,在项目变更时自动触发 Jenkins 任务,我找到两种方式:

  1. 创建一个专门触发任务的 Jenkins 用户,并生成 Jenkins Api Token ,在 GitLab webhook 里写: http://jenkinsuser:jenkinsapitoken@jenkins.ci:8080/
  2. 关闭 Jenkins 触发任务的检查权限功能,参考: https://github.com/jenkinsci/gitlab-plugin#disabling-authentication

这里有两个坑头疼了我半天:

  1. GitLab webhook 默认不允许触发本地的服务(会报500),需要开启 Allow requests to the local network from hooks and services ,使用管理员( @root )帐户访问 gitlaburl/admin/application_settings 里开启
  2. GitLab webhook 请求 Jenkins 时报403,使用上面两个方式得到解决

Jenkins 访问 GitLab

Access Tokens

由于 Jenkins GitLab Plugin 插件需要调用 GitLab 接口来设置编译状态、评论等,需要在 GitLab 创建一个用户,比如叫 bot ,该用户需要具备所编译项目的权限,并创建 Personal Access Tokens ,在 Jenkins 系统管理-系统设置里配置 Gitlab 连接,详情参考: https://github.com/jenkinsci/gitlab-plugin#jenkins-to-gitlab-authentication

克隆项目权限

在编译项目前需要克隆代码,这需要项目的访问权限,可以是用户名密码验证、密钥验证,但我这里使用的 Jenkins 主机的公钥验证:登录 Jenkins 机器,并生成 ssh-key ,把生成的 ssh-key 公钥配置到 GitLab/@bot 用户的 SSH 配置里,这样有个好处是说 bot 用户本身就需要对编译项目有权限,使用她的公钥更合理,并且没有越权问题,这样也只维护一个用户,如果用某个人的帐号,又会涉及到离职交接之类的。

一个小提示,这里创建的 GitLab@bot 用户其实作用很大,比如权限合理些,头像自动义一个 builder ,哈哈哈。

web server

由于要做到多项目、多 PR 不冲突,借鉴于 netlify 的思路,提到以下约定:

项目名--分支名.branch.ci.apmjs.org/
项目名--PRID.pr.ci.apmjs.org/

注意:项目名里的 / 需要替换为 - 。上面的域名是我之前注册的域名,因为需要使用泛路径 *.branch.ci.apmjs.org 的 A 记录解析,本地的 hosts 搞不定,再者总不能每个项目解析一个 A 记录吧。

使用 nginx 正则匹配泛路径,并指定目录:

# PR 解析
server {
    listen       80;
    server_name ~^([/w-]+)--(/d+)/.pr/.ci/.apmjs/.org$;
    root /usr/share/nginx/html/$1/pr/$2;

    location / {
        try_files $uri $uri/ /index.html;
    }
}

# 分支解析
server {
    listen       80;
    server_name ~^([/w-]+)--(/w+)/.branch/.ci/.apmjs/.org$;
    root /usr/share/nginx/html/$1/branch/$2;

    location / {
        try_files $uri $uri/ /index.html;
    }
}

这样就做到了只需要把编译产出的 dist/ 目录放到对应的目录中即可,nginx 配置只有一份,目录如:

/usr/share/nginx/html/项目名/branch/分支名
/usr/share/nginx/html/项目名/pr/分支名

创建项目

  1. 首先需要创建 GitLab 组织,并把 bot 用户加入该组织,配置 Developer 权限
  2. 创建 GitLab 组织下的项目
  3. 创建 Jenkins 任务
    git ssh
    origin
    +refs/heads/*:refs/remotes/origin/* +refs/merge-requests/*/head:refs/remotes/origin/merge-requests/*
    On push to source branch
    

Jenkins 编译项目

在任务中配置构建 shell 命令,如:

npm -v
node -v
npm install
npm run build

以上命令会安装项目依赖,并编译项目,一般是产出 dist/ 目录

在任务中再添加一个配置构建 shell 命令,如:

#!/bin/bash

# 当项目是 Merge Request(PR)时路径会不一样
if [ "$gitlabActionType" = "MERGE" ] ; then
  url="/usr/share/jenkins/html/$gitlabSourceRepoName/pr/$gitlabMergeRequestId"
else
  url="/usr/share/jenkins/html/$gitlabSourceRepoName/branch/${gitlabSourceBranch/////-}"
fi

mkdir -p $url
rm -rf $url/*
cp -r dist/* $url/

这里要重点的感谢下 @wei 帮我解决了大问题。。。

以上命令是把产出的 dist/ 目录复制到不同的目录中,而该目录是 Docker 映射的 ./html/ 目录,也会把该目录映射给 nginx ,这样就做到了编译产出后 nginx 那边直接可以使用

配置编译通知

在 Jenkins 任务里添加构建后操作:

编译成功,请访问 <http://${gitlabTargetRepoName}--${gitlabMergeRequestId}.pr.ci.apmjs.org/> 查看最新环境。

效果图

基于 Docker 部署 GitLab + Jenkins + nginx 自动化环境

基于 Docker 部署 GitLab + Jenkins + nginx 自动化环境

基于 Docker 部署 GitLab + Jenkins + nginx 自动化环境

基于 Docker 部署 GitLab + Jenkins + nginx 自动化环境

基于 Docker 部署 GitLab + Jenkins + nginx 自动化环境

Todo

npm run build

结语

其实这里说的比较乱,只是记录我的一些过程,当然也只是一条思路,自身需要根据公司的项目而相应的调整、优化。

原文  https://xuexb.com/post/docker-gitlab-jenkins-nginx.html
正文到此结束
Loading...