Skip to main content

· 7 min read
Panda

一、简介

Docusaurus 是一款静态网站生成器,可以帮助我们快速搭建一个优美的文档网站。

Docusaurus 的基本特性主要有:

  • 支持 Markdown
  • 支持侧边栏
  • 支持目录导航
  • 支持提示功能
  • 支持自定义样式
  • ……

此外,Docusaurus 还有一些高级特性:

  • 支持夜间模式
  • 支持站内搜索
  • 支持 SEO 优化
  • 支持插件扩展
  • 支持多语言
  • 支持多版本
  • ……

以下为 Docusaurus 的官网截图,我们可以先一睹为快:

二、核心组件

Docusaurus 的核心组件有:Docs、Blog、Pages。

2.1 Docs

Docs 为用户提供了按层级管理 Markdown 文档的功能,由官方插件 @docusaurus/plugin-content-docs 提供支持。

在 Docs 中,Markdown 文档具有清晰的层级关系,如下图所示:

2.2 Blog

Blog 即为我们熟知的博客。

该功能由官方插件 @docusaurus/plugin-content-blog 提供,可以帮助我们快速搭建博客页面。

下图为 Blog 示例:

2.3 Pages

Pages 指独立的页面,页面之间没有父子关系,由官方插件 @docusaurus/plugin-content-pages 提供支持。

在 Docusaurus 中,用户可以添加一个 React 页面,也可以添加一个 Markdown 页面。

下图为 Pages 示例:

三、使用指南

3.1 环境准备

Docusaurus 要求我们预先安装以下组件:

caution

yarn 指的是依赖管理工具,并不是 Hadoop 中的资源管理器 yarn。若本地安装了 Hadoop,注意在环境变量中去掉 yarn 脚本的地址,避免 yarn 命令冲突。

3.2 创建站点

在任意路径执行以下命令:

npx create-docusaurus@latest my-website classic

若创建成功,在当前路径下会出现 my-website 目录,且控制台输出如下信息:

3.3 配置说明

my-website 目录下包含多个配置文件,这些配置文件的具体作用可参考下表:

配置文件具体说明
docusaurus.config.js核心配置文件,包含基础信息配置、插件配置、主题配置等
src/css/custom.css自定义样式设置
src/pages/index.js主页标题栏配置
src/components/HomepageFeatures.js主页特性栏配置
info

具体配置项说明请参考官方文档:https://docusaurus.io/docs/api/docusaurus-config

3.4 快速启动

进入 my-website 目录,输入以下命令快速启动站点:

npx docusaurus start

若启动正常,则控制台会输出如下信息:

此时,我们便可以通过 http://localhost:3000/ 预览我们的本地站点。

3.5 语法高亮

默认情况下,Docusaurus 未开启 Java、Scala 等语言的语法高亮,需要我们在 docusaurus.config.js 增加如下配置:

module.exports = {
// ...
themeConfig: {
prism: {
additionalLanguages: ['powershell'],
},
// ...
},
};
info

Docusaurus 基于 Prism 实现语法高亮。

四、编辑器

4.1 编辑器选择

对于自建博客来说,找到合适的 MD 编辑器是非常重要的一件事,这将会影响到我们的文档编写体验与发布效率。

若条件允许,我们希望编辑器可以满足以下功能:

  • 功能一:支持与博客站点相同的文档层级结构
  • 功能二:支持自定义 CSS,以实现本地的实时预览
  • 功能三:支持与站点相同的提示功能与语法(Admonition),以实现本地的实时预览
  • 功能四:支持源文件快速同步

基于这些功能需求,笔者类比了几款知名 MD 编辑器,得出如下结论:

编辑器功能一功能二功能三功能四
Mark Text
Joplin
Obsidian
info

Typero 目前已收费,因此不列入比较清单。此外,Obsidian 实际上是支持功能四的,但是需要付费。

综合以上,笔者最终选择 Obsidian 作为本地的编辑器,其编辑界面如下图所示:

4.2 自定义设置

使用 Obsidian,我们有两个自定义项需要设置。

4.2.1 自定义 CSS

自定义 CSS 可以帮助我们复制博客站点的 CSS,使编辑器的预览样式与站点一致。为实现该功能,需要以下步骤:

  1. .obsidian/snippets 目录下创建 custom.css 文件,将博客站点的全部 CSS 复制到该文件中
  2. 通过路径 Settings → Appearance → CSS snippets 进入设置页面,点击按钮启用自定义样式

4.2.2 Admonition

默认情况下,Obsidian 不支持 Admonition。

为了使 Obsidian 支持 Admonition,我们需要下载 Admonition 插件,然后安装到目录 .obsidian/plugins

info

Admonition 的下载地址为:https://github.com/valentine195/obsidian-admonition

安装完成后,进入设置页面,并启用 Enable Non-codeblock Admonitions

插件支持的提示语法为 !!!???,与博客站点的 ::: 不同,因此我们还需要做一些修改。

打开目录 .obsidian/plugins/obsidian-admonition 下的 main.js 文件,找到其中的 enableMarkdownProcessor 方法,将 TYPE_REGEXEND_REGEX 修改为图中所示值:

重启 Obsidian,此时,我们便可以正常使用 Admonition 功能了。

五、部署

我们已经完成了 Docusaurus 的本地启动和编辑,现在,我们还需要将它发布到 GitHub Pages。

发布时,只需在 my-website 目录下输入以下命令:

set "GIT_USER=xxx" && set "GIT_PASS=xxx" && yarn deploy

其中,各参数的说明为:

  • GIT_USER:用户的 GitHub 账号
  • GIT_PASS:用户的 GitHub 私有凭证
caution

GIT_PASS 并非指密码,而是私有凭证,其生成方式请参考:https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token

· 5 min read
Panda

一、前言

与 Java 不同,Scala 比较小众,没有放之四海而皆准的规范。

为了使团队在 Scala 项目上的代码尽可能地保持一致,笔者在借鉴 官方文档开源项目 的基础上,整理了适用于自身团队的 Scala 代码规范。

二、项目创建规约

本规约展示了一个在 IDEA 上创建 Scala 项目的流程,以供参考。

2.1 依赖管理

Scala 支持通过 Maven 或 sbt 来管理项目。基于以下因素,我们选择 Maven:

  • Scala 开源项目大部分都是通过 Maven 进行管理
  • 团队成员对 Maven 比较熟悉,学习成本低
  • 主流持续集成产品对 Maven 支持度比较高

使用 Maven 创建 Scala 项目时,与 Java 无太大区别,可以通过 archetype 快速创建:

此时,项目仅能创建 Java 类,还无法创建 Scala 类。

2.2 Scala SDK

Scala SDK 是 Scala 项目中不可缺少的部分,我们可以在 Project Structure 完成 Scala SDK 的添加。

info

若本地没有 Scala SDK,可以点击 + 号,在弹出的对话框中选择对应的 Scala 版本进行下载。

2.3 框架支持

此时,距离完整创建 Scala 项目还差最后一步,即框架支持。

我们可以右键项目根目录,点击 Add Framework Support,然后会弹出如下对话框:

勾选 Scala,点击 OK。此时,Scala 项目创建完毕,可以在项目中新建 Scala 类并进行编码。

三、代码风格规约

Scala 官方提供了代码风格参考:

https://docs.scala-lang.org/style/index.html

但是,基于文档的规约常常是不安全的,团队中总会有“糊涂蛋”一不小心犯了“错”。为避免这个问题,我们可以引入 Scalafmt 插件,以强制的手段保证项目代码风格的一致性。

3.1 Scalafmt 安装

Scalafmt 可以直接在 IDEA 的插件商场中安装。

安装完成后,需重启 IDEA。

3.2 Scalafmt 配置

Scalafmt 基于 .scalafmt.conf 文件实现配置。

首先,打开 Settings,进入 Editor > Code Style > Scala 界面,将 Formatter 设置为 Scalafmt,如下图所示:

然后,在项目根目录下创建 .scalafmt.conf 文件,输入如下内容:

maxColumn = 150

若配置成功,可在 Editor > Code Style > Scala 界面看到如下结果:

3.3 变量类型补全

在 Scala 中,变量的实际类型可以是不显式指定的。例如,我们可以这样定义字符串类型的变量:

val value = "panda"

实际上,它的完整表达式是:

val value: String = "panda"

为了提高代码阅读性,使其他开发人员能快速知晓变量的实际类型,我们规定 变量类型要完全补全

在 Java 开发中,我们常常使用 .var 实现变量类型自动补全。但是,在 Scala 项目里,.var 补全的内容默认情况下是不显示实际类型的,如下图所示:

若想要显示实际类型,需要在 Type Annotations 选项卡中勾选 Local definition 选项,并取消勾选 Type is stable* 选项:

此时,再次使用 .var 进行补全时,会发现变量的实际类型已经自动补上了。

· 11 min read
Panda

一、为什么要用 Jenkins?

目前,我们在持续集成上使用的是 GitLab 自带的 gitlab-ci。它支持自动化完成代码编译、镜像构建与镜像推送,减少开发人员在项目构建上的时间开销。

但是,gitlab-ci 有一个明显的缺陷:在遇到多模块项目时(如基于 SpringBoot 搭建的微服务项目),它显得有些无力。具体表现为,gitlab-ci 不支持多管道,每当项目中某个模块的代码有新的提交,gitlab-ci 都会触发全项目的自动构建,而无法仅针对产生变动的子模块进行构建。当项目规模较大时,这种构建方式会导致自动构建的代价非常大。

而在这种场景下,Jenkins 可以很好地实现我们的需求。当代码提交触发构建事件时,Jenkins 可以筛选出产生变动的子模块,并对这些子模块进行构建与打包,使我们的自动化构建变得精准而优雅。

下图为 Jenkins 持续集成的工作流示意图:

二、Jenkins 安装与部署

本节将介绍 Jenkins 的安装与部署。若只想了解流水线的构建,可以直接跳过本节内容。

2.1 环境准备

我们将采用 Docker 的方式部署 Jenkins,因此,部署前需在宿主机完成 Docker 环境的搭建。

info

Docker 环境搭建不作为本文分享内容,请自行实现。

2.2 基础镜像

Jenkins 官方已提供现成的 Docker 镜像 jenkinsci/blueocean,该镜像内已包含以下组件:

  • Docker
  • Java
  • Git

但是我们不能直接使用它,因为它并未安装常用的构建工具(如 Maven、Gradle 等)。

为解决这个问题,我们可以参考以下 Dockerfile 文件,构建符合我们自身需求的 Jenkins 镜像:

FROM jenkinsci/blueocean:1.24.7
MAINTAINER pentaxie@qq.com

ARG BASE_DIR=/opt/maven

USER root
ADD apache-maven-3.6.3-bin.tar.gz ${BASE_DIR}
ADD settings.xml ${BASE_DIR}/apache-maven-3.6.3/conf
RUN ["cp","/usr/share/zoneinfo/Asia/Shanghai","/etc/localtime"]

ENV M2_HOME=${BASE_DIR}/apache-maven-3.6.3
ENV PATH=$PATH:$M2_HOME/bin
info

该镜像在官方镜像的基础上,安装了 Maven,并相应地修改了 Maven 配置文件。

2.3 启动 Jenkins

完成 Jenkins 基础镜像的构建后,我们可以直接使用 Docker 命令启动容器:

docker run \
-u root \
-d \
-p 38080:8080 \
-v /data/test/jenkins-data:/var/jenkins_home \
-v /var/run/docker.sock:/var/run/docker.sock \
hub.panda.com/dc-jenkins:2.289

其中,两个 -v 需要了解其作用:

  • 第一个 -v 表示将 Jenkins 的安装及数据存储目录挂载到宿主机,下次我们重启 Jenkins 的 Docker 容器时,Jenkins 中的数据才得以保留
  • 第二个 -v 表示支持容器内的 Docker 客户端直接使用宿主机的 Docker 服务,这样我们在容器内执行构建命令时,实际上是在宿主机构建镜像

2.4 插件安装

Jenkins 启动后,首先需要安装社区推荐的插件。

除此之外,我们还需要在 插件管理 界面自行安装以下插件:

  • GitLab
  • Maven Integration
  • Pathignore

上述插件的主要功能为:

插件名称主要功能
GitLab支持 GitLab Token 的配置
Maven IntegrationMaven 集成插件,支持在流水线中创建 Maven Project
Pathignore路径过滤插件,支持子模块过滤(实现多模块持续集成的关键)

2.5 必要配置

2.5.1 GitLab Token 设置

安装完 GitLab 插件后,需要在 系统管理 - 系统设置 页面添加 GitLab Token。

GitLab Token 是 GitLab 提供的身份验证的令牌,其生成方式可参考下图:

info

该 Token 尽可能赋予足够的权限,避免后续 Jenkins 集成出现权限不足的情况。

info

添加后,需点击图中的 Test Connection 按钮进行校验,确保 Token 可以正常运作。

2.5.2 Maven 设置

基础镜像部分介绍到,我们在官方 Jenkins 镜像的基础上安装了 Maven,并设置了环境变量与配置文件。

但这些还不够,使用 Jenkins 时,我们还需要在 系统管理 - 全局工具配置 页面中配置 Maven 的具体位置,使 Jenkins 能准确找到 Maven 的地址。

2.5.3 时区设置

在基础镜像部分,我们已经将镜像的时区设置为东八区。

但是在 Jenkins 的界面中,每个用户的初始时区都是伦敦时间,需要我们手动设置为东八区:

完成此步骤后,Jenkins 的整个初始化配置过程便完成了,接下来可开始创建流水线进行深入体验。

三、构建流水线

本节将以 多模块集成流水线 为例,展示具体的流水线构建流程。

3.1 创建任务

集成了 Maven Integration 插件后,我们就可以在 新建任务 界面中创建一个 Maven Project:

info

通过 Maven Project,我们可以很便捷地在 Jenkins 上实现代码的编译与打包。

3.2 配置流水线

流水线任务创建后,需要经过一系列配置。

3.2.1 代码源

Jenkins 支持从 GitLab 上拉取代码。

caution

仓库地址记得使用 HTTPS,如果要使用 SSH 方式,需在容器中创建私钥(本文不介绍此种方式)。

如果我们的 GitLab 项目是 Private 级别,那么我们还需要创建一个 GitLab 凭据(Username with password),该凭据需要填写具有项目对应权限的 GitLab 用户名与密码,如下所示:

3.2.2 构建器

构建器用于设置流水线任务触发的条件。

在本例中,我们勾选 Build when a change is pushed to GitLab

该选项实现了当 GitLab 上有代码提交等行为时,触发 Jenkins 作业。

需要注意的是,要实现 GitLab 主动向 Jenkins 推送触发事件,需在 GitLab 上配置 Webhooks:

Webhooks 中,有两个配置项需要格外注意:

  • URL 为 Jenkins 对应构建项目的地址,如本例为:http://192.168.5.98:38080/project/demo
  • Secret token 为 Jenkins 对应构建项目的令牌,通过点击 高级 按钮进行生成,具体示例可见下图

此外,图例中的 Allowed Branched 需要指定触发构建的分支。默认情况下,任意分支的提交都会触发构建任务。

info

在本例中,我们设置为只有 master 分支发生变更才会触发构建任务。

3.2.3 构建环境

构建环境设定了构建过程中的一些条件与行为。

由于本节主要介绍多模块的持续集成,因此关键点在于 Do not build if only specified paths have changed 选项。

info

该选项由 Pathignore 插件提供,可支持子模块的单独构建。

图例中的配置表示,只有子模块 dim-service 产生了变更项,才会触发构建任务。这是实现多模块持续集成的根基,在此基础上,我们可以很轻松地过滤掉其他子模块的构建流程。

3.2.4 Pre Steps

顾名思义,Pre Steps 可以完成构建前的一些准备工作。

例如,我们想要在构建时推送 Docker 镜像,那么就需要提前登录对应的 Docker 仓库:

3.2.5 Build

该步骤指明了我们整个流水线想要完成的构建指令。

在本例中,我们通过 -pl 参数指定构建的模块为 dim-service,配合上述 Pathignore 插件的过滤功能,我们可以实现当 dim-serivce 子模块发生变更项时自动触发该模块的 Docker 镜像创建与推送:

info

docker:builddocker:push 依赖 docker-maven-plugin 插件,可自行研究,或等待后续教程。

完成上述所有步骤后,点击 应用保存,便完成了流水线的配置。如果项目有多个模块,创建多个对应的流水线任务即可。

3.3 构建与验收

现在,我们可以提交一次代码,验证一下流水线任务执行情况。若无异常,在我们提交代码后,流水线会自动开启构建任务,完成我们预设的构建工作。

info

代码提交时,各子模块对应的流水线任务都会被触发,但是由于 Pathignore 插件的作用,流水线任务检测自身对应的子模块无变更项时,当前被触发的构建任务不会执行任何构建操作。

在构建任务明细中,可以通过点击 控制台输出 按钮,查看具体的构建日志:

如果一切顺利,即可在我们的 Docker 私仓里看到本次构建的镜像了!

· 16 min read
Panda

一、背景介绍

目前,成熟的代码检测工具有很多,如 SpotBugs(前身为 FindBugs)、PMD、Checkstyle 等,我们可以在本地开发环境,非常便捷地利用这些工具实现代码的扫描与检测。这看上去好像很不错,但实际上,采用这种代码检测方式,我们面临着两个问题:

  • 代码提交前需要手动运行检测工具
  • 检测结果仅对自己可见

那么,有没有什么方案可以规避这些问题呢?答案当然是有的。

我们可以基于 GitLab 和 SonarQube 搭建自动化代码检测平台,并通过该平台实现以下功能:

  • 每次提交时,自动触发 SonarQube 代码检测,产生检测报告(包含坏味道、BUG 数、覆盖率等)
  • 上述检测报告,若来源于主分支提交或合并,则发送至 SonarQube,若来源于其他分支提交,则以评论形式直接反馈至 GitLab
info

如果不知道 SonarQube 是什么,可以参考 SonarQube 官方网站

二、环境要求

本次搭建使用的操作系统为 CentOS 7,需要安装的组件如下表所示:

组件名版本号
SonarQube6.7
GitLab11.1.4
GitLab-Runner13.0.1
Maven3.6.3

实际版本号需严格遵循表格中的版本号,避免出现兼容性问题。目前,笔者已知的兼容性问题有以下几点:

  • SonarQube 在 7.6 版本后不支持插件 sonar-gitlab-plugin,若使用 7.6 之后的版本,将无法实现 SonarQube 与 GitLab 协作(除非使用付费版)
  • GitLab 与 GitLab-Runner 的版本要考虑兼容性,否则会出现 GitLab-Runner 无法连接至 GitLab 的问题

三、安装与配置

本节将描述上述组件安装与配置的详细步骤。其中,安装方式主要有本地安装和基于 Docker 安装,因此在安装前需确保宿主机已经具备 Docker 和 Docker Compose 的环境。

3.1 SonarQube

3.1.1 SonarQube 安装

使用 Docker Compose 安装 SonarQube 很简单,只需以下几个步骤:

  • 在任意位置创建安装目录 sonar,如 /usr/local/sonar
  • 在安装目录下创建 docker-compose.yml 文件,文件内容如下:
version: '2.1'

services:
# 使用 PostgreSQL 作为 SonarQube 的持久化方案
# 注:由于 SonarQube 7.9 及之后的版本不支持 MySQL,因此 MySQL 不作为考虑对象
postgres:
image: postgres:12
container_name: sonarqube_postgres
privileged: true
ports:
- "5432:5432"
networks:
- sonarnet
restart: always
# 将 PostgreSQL 数据文件存放至宿主机
volumes:
- ./postgres:/var/lib/postgresql/data
environment:
POSTGRES_DB: sonar
POSTGRES_USER: sonar
POSTGRES_PASSWORD: sonar

sonarqube:
image: sonarqube:6.7
container_name: sonarqube
privileged: true
ports:
- "9000:9000"
restart: always
networks:
- sonarnet
depends_on:
- postgres
# 将 SonarQube 日志文件、数据文件、配置文件、扩展插件存放至宿主机
volumes:
- ./data:/opt/sonarqube/data
- ./extensions:/opt/sonarqube/extensions
- ./logs:/opt/sonarqube/logs
- ./conf:/opt/sonarqube/conf
# 配置 SonarQube 的数据源,本例为 PostgreSQL
environment:
SONARQUBE_JDBC_USERNAME: sonar
SONARQUBE_JDBC_PASSWORD: sonar
SONARQUBE_JDBC_URL: jdbc:postgresql://postgres:5432/sonar?useUnicode=true&characterEncoding=utf8
# 由于 SonarQube 内部会启动 ElasticSearch,因此需要此配置
ulimits:
nproc: 65535
nofile:
soft: 65536
hard: 65536

networks:
sonarnet:
driver: bridge
  • 在安装目录下分别创建 dataextensionslogsconf 文件夹,并使用 chmod -R 777 <dir> 赋予所有权限,避免出现权限问题
  • 若宿主机有开启防护墙,需开放上述配置的端口:
firewall-cmd --add-port=5432/tcp --permanent
firewall-cmd --add-port=9000/tcp --permanent
firewall-cmd --reload
  • 在安装目录下,使用 docker-compose up -d 命令启动 SonarQube,若启动成功,控制台输出如下日志:
Creating network "sonar_sonarnet" with driver "bridge"
Creating sonarqube_postgres ... done
Creating sonarqube ... done
  • 打开浏览器,输入地址 http://sonar.example.com:9000 访问 SonarQube,若访问成功,则出现如下页面:

info

安装完成后,SonarQube 会自动生成管理员账号 admin:admin

3.1.2 SonarQube 插件

SonarQube 安装完成后,还需要安装部分插件以支持某些功能,具体插件清单如下:

插件名版本号插件用途
sonar-gitlab-plugin3.0.2为每个 GitLab 提交提供代码注释和评论的功能
sonar-l10n-zh-plugin1.19支持 SonarQube 汉化

插件安装非常简单,只需要将插件 jar 包放至扩展目录 ${sonarqube_home}/extensions/plugins 下,重启 SonarQube 即可。

关于 SonarQube 与 sonar-gitlab-plugin 的版本对照,可以参考:

https://github.com/gabrie-allaigre/sonar-gitlab-plugin

关于 SonarQube 与 sonar-l10n-zh-plugin 的版本对照,可以参考:

https://github.com/SonarQubeCommunity/sonar-l10n-zh%20

info

上述插件不支持在 SonarQube 的应用中心直接下载,因为官方已经不再提供这两款插件的在线安装了,我们可以直接在上述的 GitHub 地址中下载指定版本的 jar 包。

3.1.3 Sonar GitLab Plugin 配置

sonar-gitlab-plugin 成功安装,则在 SonarQube 的配置页面可以见到如下页面:

具体路径为:登录 admin 账号 -> 点击顶部导航栏【配置】按钮 -> 点击【通用设置】中的【GitLab】选项卡

在该页面中,需要修改以下参数的值:

参数名称参数标识目标值
GitLab urlsonar.gitlab.urlhttp://gitlab.example.com
GitLab User Tokensonar.gitlab.user_tokenGitLab 的用户令牌,获取方式参考 3.2.2
GitLab API versionsonar.gitlab.api_versionv4
info

上述参数中,GitLab User Token 是核心,设置错误的 token,将直接影响到 SonarQube 与 GitLab 的协作。特别要注意的是,生成该 token 的用户 一定要具备管理员权限,否则 SonarQube 无法对 GitLab 上的所有项目进行评论。

3.2 GitLab

3.2.1 GitLab 安装

与 SonarQube 相似,我们采用 Docker Compose 的方式安装 GitLab,具体步骤如下:

  • 在任意位置创建安装目录 gitlab,如 /usr/local/gitlab
  • 在安装目录下创建 docker-compose.yml 文件,文件内容如下:
version: '3'
services:
gitlab:
# 使用 gitlab 中文镜像,直接安装 gitlab 中文版
image: 'twang2218/gitlab-ce-zh:11.1.4'
restart: unless-stopped
# 若没有域名,建议直接写ip,否则页面上克隆的复制链接串将显示该值,无法直接使用
hostname: '192.168.117.128'
container_name: gitlab
environment:
# 设置时区
TZ: 'Asia/Shanghai'
GITLAB_OMNIBUS_CONFIG: |
external_url 'http://192.168.117.128'
gitlab_rails['time_zone'] = 'Asia/Shanghai'
ports:
- '80:80'
- '443:443'
- '22:22'
# 将 gitlab 日志文件、数据文件、配置文件存放至宿主机
volumes:
- config:/etc/gitlab
- data:/var/opt/gitlab
- logs:/var/log/gitlab
volumes:
config:
data:
logs:
  • 若宿主机 ssh 端口为默认值 22,则需要将其修改为其他值后,才能继续执行后续步骤,具体修改方式本文略
  • 若宿主机有开启防护墙,需开放上述配置的端口(22 端口默认开启,无需配置):
firewall-cmd --add-port=80/tcp --permanent
firewall-cmd --add-port=443/tcp --permanent
firewall-cmd --reload
  • 在安装目录下,使用 docker-compose up -d 命令启动 GitLab,若启动成功,控制台输出如下日志:
Creating network "gitlab_default" with the default driver
Creating gitlab ... done
  • 打开浏览器,输入地址 http://gitlab.example.com 访问 GitLab,若访问成功,则出现如下页面:

info

安装完成后,GitLab会自动生成管理员账号 root,首次登录前需要修改密码。

3.2.2 GitLab 用户令牌生成

GitLab 安装完成后,我们需要需要根据 3.1.3 中的要求,生成用户令牌。

具体步骤如下:

  • 登录具有管理员权限的账号(一定要是管理员身份)
  • 访问地址 http://gitlab.example.com/profile/personal_access_tokens 进入令牌生成页面
  • 输入令牌名称,勾选 apiread_usersudo 权限,点击【创建】按钮,即可生成用户令牌

生成的令牌需要马上保存,后续不再显示,具体令牌信息在如下位置:

3.3 GitLab Runner

3.3.1 GitLab Runner 安装

GitLab Runner 是一个处理构建的应用程序,我们需要通过它执行我们在 GitLab CI 中定义的 Job。

在本例中,由于后续编写的 Job 依赖于 Maven 环境,因此选择直接在宿主机上安装 GitLab Runner。

(当然,我们也可以自己构建包含 GitLab Runner 和 Maven 的镜像,然后使用 Docker 安装,具体实现方式本例略。)

在宿主机上安装 GitLab Runner 的步骤如下:

  • 下载 GitLab 官方源:
curl -L https://packages.gitlab.com/install/repositories/runner/gitlab-runner/script.rpm.sh | sudo bash
  • 使用 yum 下载 GitLab Runner:
yum install gitlab-runner

3.3.2 注册与启动 Runner

注册 Runner 前,我们需要先获取 GitLab 的注册令牌。

登录管理员账号,访问地址 http://gitlab.example.com/admin/runners,即可看到我们想要的注册令牌:

接下来,即可开始注册流程。

首先,执行注册命令:

gitlab-runner register

然后,根据控制台上的提示,我们需要依次输入以下信息:

  • GitLab 地址:http://gitlab.example.com
  • 注册令牌
  • Runner 描述,如 test
  • Runner 标签(与后续编写的 .gitlab-ci.yml 中的 tags 要一致,因此需要慎重填写,当然,写错了也无妨,后续在 UI 界面上可以修改)
  • 选择执行者(ssh, docker+machine, docker-ssh+machine, kubernetes, docker, parallels, virtualbox, docker-ssh, shell)
  • 若执行者选择 shell,则注册流程结束,若执行者选择 docker,则需要选择默认的镜像,如 docker:stable

完成注册后,即可在刚刚的地址中看见该 Runner 的信息:

现在,我们可以使用命令 gitlab-ci-multi-runner run 启动所有 Runner。

3.4 Maven

Maven 用于执行 SonarQube 的代码检测命令(同样的工具有 Sonar Scanner、Gradle 等)。

由于没有找到已配置好阿里源的 Maven 镜像,所以我们也选择使用本地安装的方式安装 Maven,具体步骤如下:

  • 在任意目录下(如 /usr/local)下载 Maven 二进制包:
wget https://mirror.bit.edu.cn/apache/maven/maven-3/3.6.3/binaries/apache-maven-3.6.3-bin.tar.gz
  • 解压压缩包,并将目录重命名为 maven
tar -xzvf apache-maven-3.6.3-bin.tar.gz
mv apache-maven-3.6.3/ maven
  • /etc/profile 文件追加以下内容:
export M2_HOME=/usr/local//maven
export PATH=$PATH:$M2_HOME/bin
  • 重置环境变量
source /etc/profile

至此,环境要求的所有组件已全部安装完毕。

四、结果验证

现在,我们通过一次完整的代码提交,来验证自动化代码检测平台是否生效,即:

  • 代码提交时,是否触发 SonarQube 检测作业
  • SonarQube 的检测结果,是否可以反馈至 GitLab

4.1 新建 Maven 工程

首先,我们创建满足要求的 Maven 工程。该工程的 pom.xml 文件要求具备以下内容:

  • 加入 sonar-maven-pluginjacoco-maven-plugin 插件:
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
</plugin>
<plugin>
<groupId>org.sonarsource.scanner.maven</groupId>
<artifactId>sonar-maven-plugin</artifactId>
<version>3.6.0.1398</version>
</plugin>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.4</version>
</plugin>
</plugins>
</pluginManagement>
</build>
  • 添加 jacoco-maven-plugin 插件的配置信息:
<profiles>
<profile>
<id>coverage</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<build>
<plugins>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<executions>
<execution>
<id>prepare-agent</id>
<goals>
<goal>prepare-agent</goal>
</goals>
</execution>
<execution>
<id>report</id>
<goals>
<goal>report</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>

可能有人会有疑问,为什么需要加入 jacoco-maven-plugin 插件呢?这是因为,SonarQube 本身并不支持覆盖率计算,我们需要借助 jacoco 来计算出项目单元测试的覆盖率。

4.2 集成 GitLab-CI

3.3.2 中,我们演示了如何创建 GitLab Runner。

在这里,我们首先创建执行者为 docker 的 Runner,并将其命名为 docker_runner

然后,我们编写如下 .gitlab-ci.yml 文件:

image: maven:3.6.3-jdk-8

variables:
SONAR_TOKEN: "${sonar_token}"
SONAR_HOST_URL: "http://sonar.example.com:9000"
GIT_DEPTH: 0

stages:
- build_push
- feedback_to_gitlab

# 执行 SonarQube 分析,并将检测结果推送至 SonarQube
sonarqube_analysis:
stage: build_push
only:
- merge_requests
- master
script:
- mvn --batch-mode verify sonar:sonar -Dsonar.host.url=$SONAR_HOST_URL -Dsonar.login=$SONAR_TOKEN
tags:
- docker_runner

# 执行 SonarQube 分析,并将检测结果反馈至 GitLab
sonarqube_gitlab_comment:
stage: feedback_to_gitlab
except:
- master
script:
- mvn --batch-mode verify sonar:sonar -Dsonar.host.url=$SONAR_HOST_URL -Dsonar.login=$SONAR_TOKEN -Dsonar.analysis.mode=preview -Dsonar.gitlab.commit_sha=$CI_COMMIT_SHA -Dsonar.gitlab.ref_name=$CI_COMMIT_REF_NAME -Dsonar.gitlab.project_id=$CI_PROJECT_ID
tags:
- docker_runner

其中,参数 SONAR_TOKEN 为 SonarQube 的令牌,在 SonarQube 首次创建项目时即可创建。与 GitLab 的令牌一样,此 token 一旦创建,就需要马上保存,否则后续不再显示。

引入该 .gitlab-ci.yml 文件后,当我们提交代码时,上述 Runner 便会根据提交所来源的分支选择相应的执行脚本,具体描述如下:

  • 若提交为主分支提交(或合并至主分支),则执行器 docker_runner 会执行 SonarQube 的分析指令,并将分析结果报送至 SonarQube 平台
  • 若提交为非主分支提交,则执行器 docker_runner 会执行 SonarQube 的分析指令,并将分析结果反馈至 GitLab 中对应的分支下

4.3 提交验证

现在,我们分别在其他分支和主分支提交代码,查看提交结果。

在提交其他分支代码时,由于我们的代码中有严重 BUG,SonarQube 会在此分支下标记出 BUG 的具体信息,并标记该流水线的状态为失败,如下图所示:

在提交主分支代码时,我们发现执行器自动触发 SonarQube 分析作业,并将检测结果推送至 SonarQube 平台,如下图所示:

综上,我们发现结果符合预期,说明自动化代码检测平台搭建成功,enjoy it!

· 19 min read
Panda

My Way

note

写于 2016 年国庆,彼时 23 周岁,大学刚毕业。

为什么选择编程

曾经有人问我,为什么要学编程,去国企或者考公多好啊。我记得当时的自己,本想张口反驳,最后却又闭上嘴巴保持沉默。因为那时候,我不知道怎么回答,如果我像世人那样很装逼地用梦想两个字来解释,那么也许我从未明白自己想要的是什么。因为,嘴巴里说出来的梦想,就像空中楼阁,永远也触摸不到。

直到今天。当我在浏览器的地址栏上输入自己在花生壳申请的域名然后看着页面跳转到我自己的网页作品时,我知道这两个月孤单的学习生活值得,我知道曾经的放弃是正确的答案,我知道心中梦想究竟是什么模样,我也知道未来的路该往哪儿走。

什么都无法舍弃的人,什么也改变不了。我庆幸自己自己做出了选择,我自豪自己做出的改变,哪怕这条不同领域的道路对于新生的自己来说可能是荆棘遍地,但只要脚下的路是自己的,走得再怎么磕脚也是幸福的。

人生第二篇章,钥匙在自己手里

在中国,如果有人问你,如何评价一个大学生是否优秀,你会如何回答?我想,对于很多大学生来说,评判标准很可能就在以下几种。是不是学生干部?是不是学霸学神?演讲出不出色?PPT牛不牛?是不是多才多艺?
早在很小的时候,我们就明白了社会对学生评判的标准,我们知道学习怎样的技能,达到怎样的成绩,可以获取周边同学艳羡的目光,可以收获大人们毫不吝啬的赞赏。也因此,从小我们不会去考虑自己内心想要的是什么,不会去思考自己适合的是什么,因为优秀的标准已经很明确的摆在那里,我们还费那么多功夫想这些做什么呢?我们只要按部就班,考取个年段前列的优异成绩,就足以在过年过节的时候赢得满桌大人的喝彩。

所以,我们也就顺着这个惯性,把小时候的思维,一并带入到大学生活中。我们知道,学生干部可以为综测加分、可以为自己的简历点缀、可以向家里的大人炫耀,所以就在大一一开学时一个班长的位置竞争是那样的激烈;我们知道,就算没有学生工作,只要 GPA 够高,仍然是自己炫耀的资本,所以我们会不分昼夜泡在图书馆,更有甚者会为了绩点去台湾交流,只因那边的老师打分比较高。不知道从何时开始,我们前进的动力,竟然是建立在家长、学校、社会所给予的标尺上。他们说好,我们奋不顾身地去追求,他们说不好,我们连一秒钟的时间也懒得花费。

因此在大学的尾巴,在临近毕业时,我们可以看见一大批人手握着家长的评判标尺,义无反顾地投入到考公的大军中,好像未来的道路已经明朗,好像人生的目标就已经在前方。

当然,我想表达的意思,并不是说这种评判标尺不好。我想说的其实是,从小我们在标尺下成长,我们很轻松地就知道何为优秀。但当我们从学校的象牙塔走出,面对纷杂的社会,如果此时我们继续接受家长的标尺,去考公、去国企、去银行、去一切一切看起来可以稳定终老的单位,那么一切都还是那样美好。但假如,我不呢?假如我拒绝一眼看到头的生活,假如我拒绝不存在挑战的安逸日子呢?那么,失去这个评判标尺的我,在拥有这么多行业的社会中,该往哪走?社会舆论教会了我优秀的定义,可它不曾告诉我兴趣的含义。

所以我说,人生第二篇章的钥匙,在我们自己手里。当我们从学校走出,当我们不再循规蹈矩,那么没有人能告诉你什么是好的,因为也没有人知道真正的答案。在日新月异的今天,只有你自己可以做出选择。

改变,是二次学习

高谈阔论了半天,其实上面所提及的大学生现状就是我自己的真实写照,当然,学霸那一点不算。记得当初高考填报志愿的时候,我遵循父上大人的建议,报了厦大经院的财政系,立志未来进入财政局成为一名有头有脸的大人物。可惜天不遂人愿,那一年厦大经院的录取分普遍比往年高,导致我连吃两次级差,最后录取了第五个志愿,电子商务。然后更好笑的是,当时我不曾想自己会沦落到录取第五个志愿的下场,因此从第五个志愿开始是瞎填的,结果我就在一脸懵逼中,到了厦大管院电子商务专业开始我的大学生活。

现在想来,在电子商务这个专业,有好有坏吧。先说说好的方面吧。厦大电子商务,号称全能专业,文能学宏经、微经、会计学等高大上的经济类课程,理能学高数 A、概统、可以碾压专业理科生的运筹学等烧脑的理科类课程。甚至还开设了一系列软件相关课程,学 Java,学 C,学数构,学数据库。我至今记得,在尚未学会编程规范、尚未把面向对象理解透,就被强行布置了一个题库软件的日子。嗯,说完好的,再来谈谈不好的地方。那就是,多而不精!每个人的精力是有限的,如果学得太多太泛,那么就无法在某个垂直领域学得深。因此电子商务专业的同学,在毕业季找工作的时候,通常都会遇到一个窘况,那就是在写简历的时候,内容可以比其他同学来得丰富,但是在面试深入交谈的的时候,全部露了马脚。当然我并不是想质疑系里的开课水平,毕竟这种教育方式从长远来看说不定也有可取的地方,因为据说现在的市场上对跨界人才的呼声是越来越高。但至少,电子商务专业,给本来就按部就班的自己,下了一剂更强的迷幻药。

有人说过,兴趣是可以培养的。比如一个同学,他对财务从来不了解,但假如有一天他置身于财务的环境中,那么日积月累下,有可能他就会发现财务给自己带来的强烈荣誉感,从而使他能有更强烈的欲望在财务的天地里闯荡。但是,这个前提,是专业性,是心无旁骛,是只有唯一的选择。如果他所接触的课程和领域,浸入的面实在太广,那么此时也许他就无法往某个单一领域深入地发展,自然无法获得深入研究所带来的成就感。这就像在高速路上,如果我们前行的方向只有一条,我们永远不会走错,除非你衰到撞坏护栏掉落悬崖。但假如我们行驶到高速枢纽,当前方的分岔口太多,我们走错的几率也就大大提高。

所以,大学四年下来,我依旧只会做好我的学生工作,保证我的课业不挂科,却从来不曾想我的兴趣是什么,我未来的职业道路要怎么走。直到大四上的时候,当看到同样在做校园自媒体平台的数院宣传巨头因为对设计的强烈热爱而斩获阿里交互设计师 offer 时,当看到同样在做学生工作的某同学因为对数据分析的热爱而斩获阿里的数据运营 offer 时,我隐藏在内心的焦虑和恐慌才越来越明显。

此时,我正在联想做着与本专业相关的运营工作。我开始觉得,我的大学,好像缺失了什么。我好像,从来不知道自己奋斗的动力来自何方。我仿佛机械一般,做着眼前的运营工作,仿佛为了证明自己很努力,拼了命地阅读鸟哥笔记里的干货。但其实,我很明白,我不喜欢运营,我不喜欢拉新不喜欢搞活动不喜欢促留存。不是运营不好,只是不喜欢而已,也许 10W+ 能给爱好运营的人带来王冠般的荣耀,但是对我来说却是没有丝毫吸引力,当然,我自己也写不了 10W+ 的文案。纯粹就是兴趣问题而已,仅此而已。

也就在那一阵,在那个即将面临毕业的关口,我彻底地陷入了迷茫。我终于发现自己所缺失的东西,我终于知道自己很奋斗却从未收获的原因在于自己不曾问问自己想要的是什么。我终于明白,奋斗从来不难,无休止的加班、无休止的工作都能证明你的奋斗,但是冷静下来的思考和专研,才是真正重要真正决定成败的因素。也恰好就在此时,我一直感激着的卢同学,一位在欧莱雅闯荡的女强人,对我说了一句话,让我整个人恍若醍醐灌顶。她说,在她的眼中,虽然我是学院 5 大学生干部之一,但她从来不觉得我是 leader,她觉得我更像是技术帝,更像那种默默在背后付出的人。那一瞬间,我仿佛记起了自己做的运动会视频在运动会庆功宴上,引起全场运动员尖叫而自己默默在台下吃饭的场景;我仿佛记起了院里的同学在微信公众平台上为了毕马威大赛而疯狂抢票时,作为幕后始作俑者的自己当时的满足感;我仿佛记起了在 Java 大作业提交前夕,一群人打开自己写的应用时那种无与伦比的成就感。原来世人所言不假,人可以从他人的眼睛中看到最真实的自己。原来我一直是这样的人,喜欢在幕后默默学习默默做事而从不想要舞台从不渴望聚光灯的人。原来其实我一直被外人打上了技术型男生的标签。难怪我不喜欢需要哗众取宠、需要吸引广大用户关注的运营。兴趣不同,何必强融呢?

但是,让人深感绝望的是,当我终于认清自己,认清自己与世界的关系时,我已经站在了毕业的尾巴。在这个关键的阶段,我怯弱了,害怕了,因为改变,意味着二次学习,意味着一切从头开始,意味着我以前的实习经历全部只能成为简历上的寥寥几笔。

什么都无法舍弃的人,什么也改变不了

心中埋下了梦想的种子,却从来不敢触碰它。我想,有很多人和我一样吧。当发现了自己想要的东西,却错过了合适的年纪。维稳的我们从来不敢去冒险,因为代价实在太大。改变不难,难的是需要舍弃从前的改变。

所以在浑浑噩噩中,我靠着自己勉强能入眼的文案水平,靠着自己拙劣的活动策划能力,勉强地在运营岗位上坚持着。但我一直明白,这并不是自己所追求的。

如果不曾遇见他,也许我这辈子真的会在运营岗位上慢慢熬,然后凭借多年的经验慢慢的混个小主管。是他告诉我改变未晚,是他告诉我成功需要勇气。

16 年的 6 月,已经临近毕业典礼,走出校门的日子已近在眼前。这时,阴差阳错的,我从朋友圈点入了一篇文章,文章的具体标题我已经忘记了,讲的是作者自己如何在 28 岁的高龄从体制内走出,去香港读自己向往已久的金融硕士,然后彻底颠覆了自己的生活。说实话,文章有点鸡汤的嫌疑,但对当时的自己,真的需要鸡汤来为自己加持。恰好此时,我发现了作者著有一本书籍《优秀的人都敢对自己下狠手》,本着我不喝鸡汤谁喝鸡汤的悲壮信念,我毅然地买下了这本书。在之前,我从来不曾想过天涯彼岸的一个人能对另一个人产生这么大的影响,但是他的书确确实实唤醒了我内心最原始的激情。他的书与其说是鸡汤,倒不如说是跌宕起伏的人生中的鸡血。整本书,满满是他突破体制与桎梏,实现第二人生的干货。

就在那时,我终于坚定了信念,我终于决定重整行装,向着梦里的技术帝出发。我知道自己不是科班出身,我知道自己已经错过了 4 年,但这些都无妨。只要我自己信念坚定,我可以在别人休息的时候付出更多的精力和心血来弥补。也许我不一定会有天资,也许我不一定会学得快,但只要能看到每天有一点改变的自己,我相信那会是最好的报答。

什么都无法舍弃的人,什么也改变不了。我庆幸我舍弃了从前,选择了自己所热爱的技术道路。我知道未来还很远,但自从感受到按下运行按钮弹出自己的作品时内心溢出的那种骑士般的荣耀感时,我体会到了多年不曾体会到的满足感。我相信,在未来的日子,编程于我而言,是信仰,是执着,是荣耀,是生命不可割舍的部分。