0%

You can’t manage what you don’t measure. - Peter Drucker

你如果无法度量它,就无法管理它。

这是现代管理学之父,彼得·德鲁克的一句名言。项目管理、敏捷开发的前提,还是需要把数据串起来,进行可视化、数据化,这样才能看到它,管理它。

本文将以公司SaaS产品为例,介绍下“小团队”是如何进行敏捷研发的落地的。

为什么要实施


  • 需求的进展不透明,不知道现在到哪里了
  • 需求延期发布成为了家常便饭,不知道什么时候会发布上线
  • 需求发布上线后,心里总是忐忑不安,不知道什么时候会出现问题和故障
  • 团队沟通成本太高,经常性出现RD、FE、QA、PM信息不一致
  • 需求插入随意、频繁,不计成本
  • 不清楚,研发团队的工作量,是正常、超负荷、还是有人不饱和

在互联网初创公司里,需求和有限的资源,永远是矛盾命题,如何在矛盾中寻找平衡,把有限的资源专注于符合公司战略的需求,保持团队的节奏和良好的情绪,就是要实施敏捷管理的痛点,也是我们为什么要实施,敏捷管理也可以很好的回答上面出现的各种问题,给出答案。

使用的工具


下面是我们所使用的工具,Confluence主要是知识库和文档的汇集,JIRA是项目管理工具和BUG管理工具,下面是之前写的如何搭建这些工具的文章,大家可以参考

✔️Atlassian Confluence

创业公司基础设施如何搭建(三) – Confluence(Docker版本)

✔️Atlassian Jira

创业公司基础设施如何搭建(四) – Jira(Docker版本)

如何做好这件事情


需求评审 ➡️ 设计评审 ➡️ 研发实现 ➡️ 测试 ➡️ 验收 ➡️ 发布 ➡️ 后评估

为了让产品和研发过程可视化,更加可控,信息互通,我们采用4个看板模型进行敏捷管理实践,看板名称和功能如下:

公开需求看板(Kanban Board)


通过「看板」建立一个公开需求池,向跨部门成员广泛收集需求,一切市场反馈及时传递到位。看板类型为Kanban Board。

需求看板(Kanban Board)


为需求生命周期搭建流程,按「Backlog - 评审 - 排期 - 设计 - 开发 - 发布」设立多个阶段,需求流转可视化。

任务效能看板(Scrum Board)


为需求预设好发版时间,所有人都可以及时预知逾期风险;产品、开发和需求提出者随时发起沟通,及时同步需求变化或者开发进展。

BUG看板(Kanban Board)


通过看板查询,迭代中的各种类型的BUG数量情况,清楚明了。

公开需求管理


公司属于教育类SaaS,其公开需求主要来源有下面几类:

  • 重要客户(学校)
  • 用户日常使用反馈(教师、学生、家长)
  • 销售渠道

甄别和过滤伪需求和次要或者不符合战略的需求,在这里进行,但是“业务方”提出的众多的需求如何管理,也是一件头疼的事情,这里主要流程发生有下列几种:

  • 用户使用体验 ➡️ 客户成功同学 ➡️ 记录问题 ➡️ 反馈处理结果
  • 大客户需求 ➡️ 客户成功同学 ➡️ 记录问题 ➡️ 反馈处理结果

客户成功同学、销售同学或者其他干系人,都可以在这个看板内,提交原始需求问题,产品同学会过滤、调研,转化为产品需求,到产品需求池内,下面是**公开需求看板**,卡片的内容主要包含了:需求描述、问题类型、解决状态、经办人

  • 判断价值很低或者肯定不会做的需求,直接拖到已完成
  • 判断有一定价值或需要在分析的需求,拖到调研讨论,最终确定后,再拖到已完成

产品研发需求管理


需求分类


产品研发内部,我们把需求分成2类:

  • 产品需求:PM提出的迭代、紧急、日常类需求
  • 技术需求:研发内部为了稳定性、扩展性、维护性而进行的技术重构类需求

需求等级


古语云:师出有名,需求的提出也是如此,为了让研发同学知道需求的重要和紧急程度,需求等级划分是特别需要的一件事情。

  • 产品需求等级划分

    P0:紧急任务,必须穷尽所能,最短时间完成;可以调人支援,可以停止其他项目,需要加班

    P1:非常重要任务,有Deadline,并且不可以Delay;如遇到P0,那么就需要加班保证P1的Deadline

    P2:重要、有影响力的任务,有Deadline,如遇到P0和P1,可以顺延(应该是大部分任务)

    P3:锦上添花的正常任务,优先级最低

  • 技术需求等级划分

    T0:重大性能和漏洞,需要加班加点进行修复

    T1:扩展性和性能风险问题,一般是单独任务进行修复

    T2:设计或者一般性能缺陷,一般是随着迭代进行相关改进

产品需求管理(需求看板)


PM和研发同学,通过PRD的方式进行沟通和交流,研发同学最终也是通过PRD进行开发、测试工作,所以第一步是需要创建PRD,PRD的管理方式采用相对灵活的方式,PM写PRD的工具有的是蓝湖,有的墨刀,我们这里为了统一归档,在Confluence做了归档的统一管理(PRD的详细链接可以是任何工具的链接), 在Confluence创建时选择模板创建,见下图:

主要包含了

  • 背景描述

  • 业务目标的描述

  • 需求链接和功能列表(即Story)

    需求看板的泳道有P0、P1、P1以下、技术需求的4个泳道,为了便于搜索,在快捷搜索列加入了常用的搜索关键词,卡片主要包含:需求等级、到期日、需求负责人

技术需求管理(需求看板)

类似数据结构的变更、技术架构的改进,比如:更换配置中心为Apollo,这类需要简称技术需求,其数据显示和看板功能,和产品需求基本一致,也显示在 需求看板 内,看板如下:

技术任务管理(任务效能看板)

这个阶段,主要是从需求阶段进入到了研发阶段,这个阶段主要包含如下类型的任务:

  • 开发任务:RD、FE
  • 开发自测:RD、FE
  • 测试用例编写:QA
  • 测试用例执行:QA

技术任务类型的问题,主要来源于2个方面

  • 产品需求
  • 技术需求

对于此类任务管理,我们使用的看板是 任务效能看板。在开始之前,我们需要在Backlog内,拖动需要进行迭代的技术需求或产品需求,如下图:

然后,以产品需求和技术需求为父任务,在 需求看板内,创建子任务,界面如下:

创建好后,可以查看父子任务详情,并有工作量体现

点击开始Sprint,并设置好时间,如下图:

RD & QA & FE,在每天下班前,填写其任务的工作量即可达到任务工作量跟踪的效果,如下图:

测试BUG管理(BUG看板)


在Sprint中产生的BUG都会显示在 BUG看板中,工作流主要是打开 ➡️ 处理中 ➡️ 已解决 ➡️ 已关闭,如下图:

我们所采用的BUG类的问题类型有以下几种:

  • 功能错误

  • 界面优化

  • 功能优化

  • 性能问题

  • 安全相关

    在这个迭代结束后,测试人员,会根据BUG的统计报告数据,分析得出本次迭代的测试报告,测试报告,我们在Confluence创建了统一模板,主要内容如下:

小结


需求和效能的生命周期管理,这里仅仅是按照目前产品和团队的需求和阶段,规定了一些适合我们的方法,这个周期管理,还是需要随着人员和阶段的不同而进行不断的改造和演进的,下面是我们在JIRA和Confluence使用的一些核心流程和方法:

  • 4个看板

    公开需求看板:处理市场、销售前端部门提出的“大客户需求”和“用户使用体验问题”

    需求看板:主要是管理技术需求和产品需求

    任务效能看板:主要是管理开发阶段,RD & FE & QA的任务和工作量,跟踪其任务合理性

    BUG看板:主要是管理迭代内产生的5类BUG问题,功能优化 & 功能错误 & 界面优化 & 性能问题 & 安全相关

  • 2个模板

    产品需求模板:产品需求的管理

    测试报告模块:迭代后,针对BUG和其他问题,进行的测试相关的总结

    目前,敏捷相关的管理,这个阶段也仅仅是做了一小部分,还有很多实践方法,在后续的变化中,会加入和实施

    敏捷管理是为了快速、稳定的交付产品而服务的,切忌不要为了追求敏捷工具的使用而耽误了实际要达成的目标。

微信关注我,及时接收最新技术文章

Jenkins Pipeline Maven Project

用代码的方式,管理日常的构建

Maven Project构建的阶段(简单流程)

  1. 清理工作区
  2. 获取code
  3. 加载CI配置文件
  4. 打包
  5. 工件归档
  6. 部署或其他操作
  7. 通知构建结果(这里用的是钉钉)

上面仅仅是针对一般类型项目进行的构建阶段说明,可以针对自己的要求进行增减(还可以包含docker镜像的打包、推送私服、git tag等等)

Maven Demo Project

1
2
3
4
5
6
7
8
9
10
11
12
.
├── README.md
├── app # 主要的code存放module
├── assembly # 打包module
├── commitlint.config.js # git commit message插件配置
├── config # 相关yml properties配置module
├── jenkins.groovy # Jenkins Pipeline调用的配置文件
├── maven # mvnw
├── mvnw
├── mvnw.bat
├── package.json # git commit 依赖包相关
└── pom.xml

上图是标准的Maven多module项目结构,其中有一个jenkins.groovy是和Jenkins Pipeline相关的

1
2
3
4
5
6
7
8
//打包配置文件,配置打包相关信息,CI打包的时候会加载这个文件

// 项目名称
env.PROJECT_CHINESE_NAME = "michael的测试项目"
// 英文名字,用于产物命名,不要随意修改
env.PROJECT_ENGLISH_NAME = "michael-jenkins-pipeline-demo"
// 归档文件正则,如:*.jar
env.ARCH_FILE_PATTERN = "assembly/target/michael-jenkins-pipeline-demo-deploy.zip"

Pipeline Scripts

辅助脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
# 在vars新建fetch_code.groovy
# BUILD_BRANCH为输入参数

def call(GIT_URL) {
git branch: "${BUILD_BRANCH}",
credentialsId: '你的凭证',
url: "${GIT_URL}"
}

# 在vars新建log.groovy

def info(message) {
echo "INFO: ${message}"
}

def warning(message) {
echo "WARNING: ${message}"
}

def error(message) {
echo "ERROR: ${message}"
}

# 在vars新建dingding.groovy
def notice(branch) {
duration = currentBuild.duration / 1000
statusMessage = ''
buildStatusMessage = currentBuild.currentResult
if(buildStatusMessage == 'SUCCESS') {
statusMessage="**<font color=red>成功</font>**"
}else {
statusMessage="**<font color=red>失败</font>**"
}
dingtalk (
robot: '机器人code',
type: 'MARKDOWN',
atAll: false,
title: "${JOB_NAME} BUILD",
text:[
"# 【${JOB_NAME}】构建完成",
"",
"---",
"构建结果: ${statusMessage}",
"> [查看控制台](${BUILD_URL})",
"- 构建分支: ${branch}",
"- 构建用时: ${duration} 秒"
]
)
}

构建脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
/* Config {
deployPath: 部署路径
}
*/
def call(Map config) {
node('java') {
properties([
buildDiscarder(
logRotator(
daysToKeepStr: '30',
numToKeepStr: '50'
)
)
])
stage('清理工作区') {
log.info '清理工作区'
deleteDir()
}
stage('获取代码') {
log.info "获取代码地址:${REPO_URL},获取代码分支: ${BUILD_BRANCH}"
fetch_code "${REPO_URL}"
}
stage('加载CI配置文件') {
log.info "加载CI配置文件"
jenkinsConfigFile="./jenkins.groovy"
if (fileExists("${jenkinsConfigFile}")) {
load "${jenkinsConfigFile}"
log.info "找到打包参数文件${jenkinsConfigFile},加载成功"
} else {
log.error "${jenkinsConfigFile}不存在,请在项目${jenkinsConfigFile}里面配置打包参数"
sh "exit 1"
}
}
stage('构建') {
log.info '构建'
sh "mvn wrapper:wrapper && ./mvnw clean package -Dmaven.test.skip=true"
}
stage('工件归档') {
log.info '工件归档'
archive_file = "${ARCH_FILE_PATTERN}"
echo "Archive ${archive_file}"
archiveArtifacts artifacts: "${archive_file}", fingerprint: true
}
stage('部署 OR 其他操作') {
log.info "部署分支: ${BUILD_BRANCH}"
// 进行对象的部署获取其他操作
currentBuild.description = "代码分支: ${BUILD_BRANCH}<br>"
}
stage('通知') {
dingding.notice("${BUILD_BRANCH}")
}
}
}
微信关注我,及时接收最新技术文章

什么是扩展共享库

在工作中,我们可能会有很多Maven项目的打包部署,还有Android、iOS等等的打包任务,虽然项目不同,但是打包、部署等等流程有很多相似之处,扩展库共享库允许我们抽象出很多公共部分,这样后续在新项目配置Pipeline的时候,会非常简便

准备

  • Jenkins:安装建议参考Jenkins安装
  • Gitlab:安装建议参考Gitlab安装
    为了方便,我这里都选择Docker安装,请大家自行配置Gitlab和Jenkins相关参数,比如Gitlab的SSH Key,Jenkins的凭证,这里不做赘述

扩展共享库配置

创建Gitlab Repo

  • 如图,我创建了名字是jenkins-shared-libraries的Git Repo

目录说明

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
(root)
+- src # Groovy 源码文件
| +- com
| +- michael
| +- Bar.groovy # for org.foo.Bar class,主要是类库相关
+- vars # 变量和公共过程
| +- foo.groovy # 全局'foo'变量
| +- foo.txt # 'foo' 变量的帮助文档
+- resources # 资源文件 (external libraries only)
| +- com
| +- michael
| +- bar.json # static helper data for com.michael.Bar
| +- bar.shell # shell scripts
| +- package.json # 主要是Git commit message 插件
| +- commitlint.config.js # 主要是Git commit message 插件

配置Jenkins扩展共享库

  • 依次点击 系统管理 > 系统配置,找到Global Pipeline Libraries,如下图

编写Pipeline Demo

在git的jenkins-shared-libraries中的vars下面,新建log.groovy文件,并提交

1
2
3
4
5
6
7
8
9
10
11
def info(message) {
echo "INFO: ${message}"
}

def warning(message) {
echo "WARNING: ${message}"
}

def error(message) {
echo "ERROR: ${message}"
}

执行

  • 新建JOB
  • 执行JOB
    在Pipeline中引用共享库,并使用log.info的方法
  • 执行结果

PS:这里仅仅是最简单的Hello world的扩展库介绍,让大家有个感性认识,后面的文章,会针对Maven、Npm这些常见的打包部署方式的项目,进行Pipeline的扩展共享库的实践介绍。

Reference

Jenkins安装
Gitlab安装
Jenkins Shared-Libraries

微信关注我,及时接收最新技术文章

集中式身份管理

Crowd从字面意思看就知道啥意思了,可以管理多种用户目录,如Active Directory、LDAP、OpenLDAP、Microsoft Azure AD等,单点登录 (SSO),无缝集成 Jira、Confluence、Jenkins等等常用DevOps工具系统。

工具包准备

安装启动方式

需要准备

  • Docker

由于官方已经有镜像,我们在官方镜像上面加入自定义的内容即可,官方3.2.1镜像Repo,我们需要的是红框的2个文件,如下(Repo托管是bitbucket):

我在本地Mac上面建立了一个Docker Build目录,包含下面几个文件

  • Dockerfile(上面的官方repo下载)
  • atlassian-crowd-3.2.1.tar.gz(官方二进制包,主要是Dockerfile里面的Curl自动下载的速度实在是慢的可以,我在提前下载好,COPY进去,这块不是非必须用这个包,可以依赖Dockerfile的Curl到官方下载安装包)
  • mysql-connector-java-5.1.45.jar(Mysql官网下载一个)
  • entrypoint.sh(上面的官方repo下载)
    修改Dockerfile
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    FROM openjdk:8-jdk-alpine
    MAINTAINER Dave Chevell

    ENV RUN_USER daemon
    ENV RUN_GROUP daemon

    # https://confluence.atlassian.com/crowd/important-directories-and-files-78676537.html
    ENV CROWD_HOME /var/atlassian/application-data/crowd
    ENV CROWD_INSTALL_DIR /opt/atlassian/crowd

    VOLUME ["${CROWD_HOME}"]

    # Expose HTTP port
    EXPOSE 8095

    WORKDIR $CROWD_HOME

    CMD ["/entrypoint.sh", "-fg"]
    ENTRYPOINT ["/sbin/tini", "--"]

    RUN apk add --no-cache wget curl openssh bash procps openssl perl ttf-dejavu tini

    COPY entrypoint.sh /entrypoint.sh

    ARG CROWD_VERSION=3.2.1

    COPY atlassian-crowd-${CROWD_VERSION}.tar.gz /tmp/atlassian-crowd-${CROWD_VERSION}.tar.gz
    COPY mysql-connector-java-5.1.45.jar /tmp/mysql-connector-java-5.1.45.jar

    RUN mkdir -p ${CROWD_INSTALL_DIR} \
    && tar -xzvf /tmp/atlassian-crowd-${CROWD_VERSION}.tar.gz -C ${CROWD_INSTALL_DIR} --strip-components=1 --no-same-owner \
    && mv /tmp/mysql-connector-java-5.1.45.jar ${CROWD_INSTALL_DIR}/crowd-webapp/WEB-INF/lib \
    && rm -rf /tmp/atlassian-crowd-${CROWD_VERSION}.tar.gz \
    && chown -R ${RUN_USER}:${RUN_GROUP} ${CROWD_INSTALL_DIR}/ \
    && sed -i -e 's/-Xms\([0-9]\+[kmg]\) -Xmx\([0-9]\+[kmg]\)/-Xms\${JVM_MINIMUM_MEMORY:=\1} -Xmx\${JVM_MAXIMUM_MEMORY:=\2} \${JVM_SUPPORT_RECOMMENDED_ARGS} -Dcrowd.home=\${CROWD_HOME}/g' ${CROWD_INSTALL_DIR}/apache-tomcat/bin/setenv.sh \
    && sed -i -e 's/port="8095"/port="8095" secure="${catalinaConnectorSecure}" scheme="${catalinaConnectorScheme}" proxyName="${catalinaConnectorProxyName}" proxyPort="${catalinaConnectorProxyPort}"/' ${CROWD_INSTALL_DIR}/apache-tomcat/conf/server.xml

    执行build
    1
    2
    cd /your-docker-build-path
    docker build -t michael/atlassian-crowd:3.2.1 .
    启动镜像(配套如果使用MYSQL,建议Docker Compose启动Jira + MYSQL)
    1
    2
    3
    4
    5
    6
    docker run -d --rm --name="crowd" \
    -v /your-path/crowd:/var/atlassian/application-data/crowd \
    -p 8095:8095 \
    -e "JVM_MINIMUM_MEMORY=384m"
    -e "JVM_MAXIMUM_MEMORY=768m"
    michael/atlassian-crowd:3.2.1
微信关注我,及时接收最新技术文章

项目与事务跟踪工具

Jira是做需求、Bug、任务等等管理的系统,工作流支持全部自定义,自由度极强,下面介绍下Jira的安装流程

为什么是Jira

  • 缺陷跟踪
  • 需求收集
  • 流程审批
  • 任务跟踪
  • 项目跟踪和敏捷管理
  • 完全自定义的workflow

工具包准备

安装和启动

这里介绍2种安装和启动方式

  • 传统安装启动方式
  • Docker安装启动方式(推荐)

传统安装和启动

需要准备

  • CentOS 7.x
  • JDK 1.8(配置好JAVA_HOME)
  • MYSQL 5.7.X(二进制安装或者Docker镜像)

一顿操作猛如虎。。。

1
2
3
4
5
6
7
8
9
10
11
12
## 解压安装包
cd your-path
tar -xvf atlassian-jira-software-7.10.0.tar.gz
mv /your-path/atlassian-jira-software-7.10.0-standalone/ /your-path/atlassian-jira-7.10.0
## 配置环境变量JIRA_HOME
vim ~/.zshrc #看大家用的什么系统,环境文件有所不同
export JIRA_HOME=/tmp/jira-home
## mysql的驱动Jar去官网下载一个适合5.7的即可
cp mysql-connector-java-5.1.45.jar /your-path/atlassian-jira-7.10.0/atlassian-jira/WEB-INF/lib
## 启动
/your-path/atlassian-jira-7.10.0/bin/start-jira.sh
# 默认是8080端口,在浏览器打开http://localhost:8080

Docker安装启动方式

需要准备

  • Docker

由于官方已经有镜像,我们在官方镜像上面加入自定义的内容即可,官方的7.10.0的Dockerfile的Repo地址,我们需要的是红框的2个文件,如下:

我在本地Mac上面建立了一个Docker Build目录,包含下面几个文件

  • Dockerfile(上面的官方repo下载)
  • atlassian-jira-software-7.10.0.tar.gz(官方二进制包,主要是Dockerfile里面的Curl自动下载的速度实在是慢的可以,我在提前下载好,COPY进去,这块不是非必须用这个包,可以依赖Dockerfile的Curl到官方下载安装包)
  • docker-entrypoint.sh(上面的官方repo下载)
    修改Dockerfile
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    FROM openjdk:8-alpine

    # Configuration variables.
    ENV JIRA_HOME /var/atlassian/jira
    ENV JIRA_INSTALL /opt/atlassian/jira
    ENV JIRA_VERSION 7.10.0

    # [新增]
    COPY atlassian-jira-software-${JIRA_VERSION}.tar.gz /tmp/atlassian-jira-software-${JIRA_VERSION}.tar.gz
    # [新增]
    RUN mkdir -p ${JIRA_INSTALL}
    RUN tar -xzvf /tmp/atlassian-jira-software-${JIRA_VERSION}.tar.gz -C ${JIRA_INSTALL} --strip-components=1 --no-same-owner \
    && rm -rf /tmp/atlassian-jira-software-${JIRA_VERSION}.tar.gz

    # Install Atlassian JIRA and helper tools and setup initial home
    # directory structure.
    RUN set -x \
    && apk add --no-cache curl xmlstarlet bash ttf-dejavu libc6-compat \
    && mkdir -p "${JIRA_HOME}" \
    && mkdir -p "${JIRA_HOME}/caches/indexes" \
    && chmod -R 700 "${JIRA_HOME}" \
    && chown -R daemon:daemon "${JIRA_HOME}" \
    && mkdir -p "${JIRA_INSTALL}/conf/Catalina" \
    && curl -Ls "https://dev.mysql.com/get/Downloads/Connector-J/mysql-connector-java-5.1.38.tar.gz" | tar -xz --directory "${JIRA_INSTALL}/lib" --strip-components=1 --no-same-owner "mysql-connector-java-5.1.38/mysql-connector-java-5.1.38-bin.jar" \
    && rm -f "${JIRA_INSTALL}/lib/postgresql-9.4.1212.jar" \ # [修改] 之前的要删除的jar发现lib没有
    && curl -Ls "https://jdbc.postgresql.org/download/postgresql-42.2.1.jar" -o "${JIRA_INSTALL}/lib/postgresql-42.2.1.jar" \
    && chmod -R 700 "${JIRA_INSTALL}/conf" \
    && chmod -R 700 "${JIRA_INSTALL}/logs" \
    && chmod -R 700 "${JIRA_INSTALL}/temp" \
    && chmod -R 700 "${JIRA_INSTALL}/work" \
    && chown -R daemon:daemon "${JIRA_INSTALL}/conf" \
    && chown -R daemon:daemon "${JIRA_INSTALL}/logs" \
    && chown -R daemon:daemon "${JIRA_INSTALL}/temp" \
    && chown -R daemon:daemon "${JIRA_INSTALL}/work" \
    && sed --in-place "s/java version/openjdk version/g" "${JIRA_INSTALL}/bin/check-java.sh" \
    && echo -e "\njira.home=$JIRA_HOME" >> "${JIRA_INSTALL}/atlassian-jira/WEB-INF/classes/jira-application.properties" \
    && touch -d "@0" "${JIRA_INSTALL}/conf/server.xml"

    # Use the default unprivileged account. This could be considered bad practice
    # on systems where multiple processes end up being executed by 'daemon' but
    # here we only ever run one process anyway.
    USER daemon:daemon

    # Expose default HTTP connector port.
    EXPOSE 8080

    # Set volume mount points for installation and home directory. Changes to the
    # home directory needs to be persisted as well as parts of the installation
    # directory due to eg. logs.
    VOLUME ["/var/atlassian/jira", "/opt/atlassian/jira/logs"]

    # Set the default working directory as the installation directory.
    WORKDIR /var/atlassian/jira

    COPY "docker-entrypoint.sh" "/"
    ENTRYPOINT ["/docker-entrypoint.sh"]

    # Run Atlassian JIRA as a foreground process by default.
    CMD ["/opt/atlassian/jira/bin/start-jira.sh", "-fg"]
    执行build
    1
    2
    cd /your-docker-build-path
    docker build -t michael/atlassian-jira:7.10.0 .
    启动镜像(配套如果使用MYSQL,建议Docker Compose启动Jira + MYSQL)
    1
    2
    3
    4
    docker run -d --rm --name=jira -p 8080:8080 \
    -v /Users/liuyang/CodeMonkey/workspace/docker-workspace/jira/home:/var/atlassian/jira \
    -v /Users/liuyang/CodeMonkey/workspace/docker-workspace/jira/logs:/opt/atlassian/jira/logs \
    michael/atlassian-jira:7.10.0
微信关注我,及时接收最新技术文章

知识库

DevOps、敏捷管理,对于整体流程和团队,2个工具是最不能少的,而且使用频率非常高,知识库 + 问题跟踪,我用的是Confluence + Jira,工欲善其事必先利其器,我们先需要把工具选择和准备好,再开始后面的事情,下面介绍下Confluence的安装流程

为什么是Confluence

  • 丰富的知识库相关的模板
  • 在线协作编辑
  • 每个人的知识可以分享给全公司的人
  • 评论功能和提醒功能完美结合,随时随地得到最新知识库更新
  • 和JIRA结合完美

工具包准备

安装和启动

这里介绍2种安装和启动方式

  • 传统安装启动方式
  • Docker安装启动方式(推荐)

传统安装和启动

需要准备

  • CentOS 7.x
  • JDK 1.8(配置好JAVA_HOME)
  • MYSQL 5.7.X(二进制安装或者Docker镜像)

一顿操作猛如虎。。。

1
2
3
4
5
6
7
8
9
10
11
## 解压安装包
cd your-path
tar -xvf atlassian-confluence-6.9.0.tar.gz
## 指定工作目录
vi /your-path/atlassian-confluence-6.9.0/confluence/WEB-INF/classes/confluence-init.properties
# 在文件中加入 confluence.home=/your-home-path/
## mysql的驱动Jar去官网下载一个适合5.7的即可
cp mysql-connector-java-5.1.45.jar /your-path/atlassian-confluence-6.9.0/confluence/WEB-INF/lib
## 启动
/your-path/atlassian-confluence-6.9.0/bin/start-confluence.sh
# 默认是8090端口,在浏览器打开http://localhost:8090

Docker安装启动方式

需要准备

  • Docker

由于官方已经有镜像,我们在官方镜像上面加入自定义的内容即可,官方的6.9.0的Dockerfile的Repo地址,我们需要的是红框的2个文件,如下:

我在本地Mac上面建立了一个Docker Build目录,包含下面几个文件

  • Dockerfile(上面的官方repo下载)
  • atlassian-confluence-6.9.0.tar.gz(官方二进制包,主要是Dockerfile里面的Curl自动下载的速度实在是慢的可以,我在提前下载好,COPY进去,这块不是非必须用这个包,可以依赖Dockerfile的Curl到官方下载安装包)
  • docker-entrypoint.sh(上面的官方repo下载)
    修改Dockerfile
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    FROM openjdk:8-alpine

    # Setup useful environment variables
    ENV CONF_HOME /var/atlassian/confluence
    ENV CONF_INSTALL /opt/atlassian/confluence
    ENV CONF_VERSION 6.9.0

    ENV JAVA_CACERTS $JAVA_HOME/jre/lib/security/cacerts
    ENV CERTIFICATE $CONF_HOME/certificate

    # [新增] 把build目录的必要文件复制到镜像内
    COPY atlassian-confluence-${CONF_VERSION}.tar.gz /tmp/atlassian-confluence-${CONF_VERSION}.tar.gz

    # [新增] 创建Install目录,解压安装包,替换工具Jar包,删除安装包压缩文件
    RUN mkdir -p ${CONF_INSTALL}
    RUN tar -xzvf /tmp/atlassian-confluence-${CONF_VERSION}.tar.gz -C ${CONF_INSTALL} --strip-components=1 --no-same-owner \
    && rm -rf /tmp/atlassian-confluence-${CONF_VERSION}.tar.gz

    # Install Atlassian Confluence and helper tools and setup initial home
    # directory structure.
    RUN set -x \
    && apk --no-cache add curl xmlstarlet bash ttf-dejavu libc6-compat gcompat \
    && mkdir -p "${CONF_HOME}" \
    && chmod -R 700 "${CONF_HOME}" \
    && chown daemon:daemon "${CONF_HOME}" \
    && mkdir -p "${CONF_INSTALL}/conf" \
    && curl -Ls "https://dev.mysql.com/get/Downloads/Connector-J/mysql-connector-java-5.1.44.tar.gz" | tar -xz --directory "${CONF_INSTALL}/confluence/WEB-INF/lib" --strip-components=1 --no-same-owner "mysql-connector-java-5.1.44/mysql-connector-java-5.1.44-bin.jar" \
    && chmod -R 700 "${CONF_INSTALL}/conf" \
    && chmod -R 700 "${CONF_INSTALL}/temp" \
    && chmod -R 700 "${CONF_INSTALL}/logs" \
    && chmod -R 700 "${CONF_INSTALL}/work" \
    && chown -R daemon:daemon "${CONF_INSTALL}/conf" \
    && chown -R daemon:daemon "${CONF_INSTALL}/temp" \
    && chown -R daemon:daemon "${CONF_INSTALL}/logs" \
    && chown -R daemon:daemon "${CONF_INSTALL}/work" \
    && echo -e "\nconfluence.home=$CONF_HOME" >> "${CONF_INSTALL}/confluence/WEB-INF/classes/confluence-init.properties" \
    && xmlstarlet ed --inplace \
    --delete "Server/@debug" \
    --delete "Server/Service/Connector/@debug" \
    --delete "Server/Service/Connector/@useURIValidationHack" \
    --delete "Server/Service/Connector/@minProcessors" \
    --delete "Server/Service/Connector/@maxProcessors" \
    --delete "Server/Service/Engine/@debug" \
    --delete "Server/Service/Engine/Host/@debug" \
    --delete "Server/Service/Engine/Host/Context/@debug" \
    "${CONF_INSTALL}/conf/server.xml" \
    && touch -d "@0" "${CONF_INSTALL}/conf/server.xml" \
    && chown daemon:daemon "${JAVA_CACERTS}"

    # Use the default unprivileged account. This could be considered bad practice
    # on systems where multiple processes end up being executed by 'daemon' but
    # here we only ever run one process anyway.
    USER daemon:daemon

    # Expose default HTTP connector port.
    EXPOSE 8090 8091

    # Set volume mount points for installation and home directory. Changes to the
    # home directory needs to be persisted as well as parts of the installation
    # directory due to eg. logs.
    VOLUME ["/var/atlassian/confluence", "/opt/atlassian/confluence/logs"]

    # Set the default working directory as the Confluence home directory.
    WORKDIR /var/atlassian/confluence

    COPY docker-entrypoint.sh /
    ENTRYPOINT ["/docker-entrypoint.sh"]

    # Run Atlassian Confluence as a foreground process by default.
    CMD ["/opt/atlassian/confluence/bin/start-confluence.sh", "-fg"]

    执行build
    1
    2
    cd /your-docker-build-path
    docker build -t michael/atlassian-confluence:6.9.0 .
    启动镜像(配套如果使用MYSQL,建议Docker Compose启动Confluence + MYSQL)
    1
    2
    3
    4
    docker run -d --rm --name=wiki -p 8090:8090 -p 8091:8091 \
    -v /your-confluence-path/home:/var/atlassian/confluence \
    -v /your-confluence-path/logs:/opt/atlassian/confluence/logs \
    michael/atlassian-confluence:6.9.0
微信关注我,及时接收最新技术文章

CentOS 7.x / Docker CE

安装docker

1
2
3
4
5
6
7
8
yum install -y yum-utils device-mapper-persistent-data lvm2
# repo建议使用阿里云的,官方的docker,国内连接起来不稳定
yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
yum clean all # 清理yum
yum makecache fast # 生成yum repo缓存
yum install docker-ce # 安装docker社区版本
systemctl enable docker # 设置为开机启动
systemctl start docker # 启动docker服务

设置镜像加速器

1
2
3
4
vim /etc/systemd/system/multi-user.target.wants/docker.service
# registry-mirror用的是阿里云的Hub加速器,可以在https://dev.aliyun.com/search.html申请自己的专属加速器地址
# 找到ExecStart=这行,添加 --registry-mirror=https://xxxx.mirror.aliyuncs.com #使用阿里云加速
ExecStart=/usr/bin/dockerd --registry-mirror=https://xxxx.mirror.aliyuncs.com

配置docker私服地址(可选)

1
2
3
vim /etc/systemd/system/multi-user.target.wants/docker.service
# 找到ExecStart=这行,添加 --insecure-registry=hub.xxx.com
ExecStart=/usr/bin/dockerd --registry-mirror=https://xxxx.mirror.aliyuncs.com --insecure-registry=hub.xxx.com

自定义docker存储目录(可选)

1
2
3
vim /etc/systemd/system/multi-user.target.wants/docker.service
# 找到ExecStart=这行,添加 --graph /opt1/docker
ExecStart=/usr/bin/dockerd --registry-mirror=https://xxxx.mirror.aliyuncs.com --insecure-registry=hub.xxx.com --graph /opt1/docker

重启Docker

1
2
systemctl daemon-reload # 重新加载daemon
systemctl restart docker # 重启docker

参考资料

微信关注我,及时接收最新技术文章

面临的问题

创业公司一般面临下面几个问题

  • 钱不多,省着花
  • 一般研发人员,不具备此类知识体系或者所知不多

需求一直存在

虽然,存在一些问题,但是随着业务发展,人员增多,此类问题会暴露的越发明显,产品技术团队的高效工作,其实是建立在合适、安全、方便的基础设施之上的

如何解决

前期准备

  • Dell服务器1台(约2 - 3w RMB)
  • NAS存储,主要做重要系统数据备份(可选)
  • 交换机(不做过多介绍)
  • 整理办公和研发需求

下面是选择的服务器,这个配置,基本可以满足100人左右企业的日常办公和部分研发测试需求

名称 T640 塔式服务器
CPU Intel(R) Xeon(R) CPU 银牌4110*2
MEM 16G*6
硬盘 4T*3 raid5 + 256固态系统盘

基础设施包含什么

  • 虚拟化的平台工具:我这里选择的是Vmware,好处毋庸置疑,但是收费,我这里之所以选,是因为,有一些历史原因(你懂得,有需求可以单独找我),如果不用,可以考虑OpenStack替代
  • 容器化:Docker是不二之选,这个根据企业自己的需求来看,我个人觉得,初期可以不用容器化,但是,如果有相关维护人员,可以选择Docker Swam或者K8s进行容器化管理
  • 根据需要选择工具:一般企业需要解决的是,知识库、项目和流程管理系统、资产管理系统、统一账户体系、代码库、工件库、如何在外安全上网、内部域名管理、如何安全登录服务器这些需求、数据安全和备份

基础设施搭建示意图

基于VMware ESXI & vSphere

层级示意

架构示意

我用到的工具

后续,会陆续介绍下,我在搭建的时候,相关工具的搭建流程和使用方式

微信关注我,及时接收最新技术文章

写在前面

最近疫情严重,正好利用这个时间,把自己之前的Blog和印象笔记的东西搬搬家,整理整理,希望在疫情过去后,自己也能坚持把自己平时工作的关于技术和管理的实践多写写,一起交流。

产品研发得码多少人

首先打算写一个自己平时在工作中,对于日常的工作流程和工具进行一个整理,也就是DevOps。本人一直在创业公司,有很多人觉得创业公司不需要技术管理和流程,就不到10个人,为什么需要这么多乱七八糟的。在讲这个问题之前,我们想想,现在当下互联网或者物联网相关的产品,需要的角色有多少呢?

角色 职责 备注
RD 研发 各种客户端、云端、大数据等
QA 测试 点点点测试、自动化测试等
PM 产品经理 和程序猿相爱相杀的家伙
OP 运营 数据运营、内容运营、PR推广等
OPS 运维 最苦逼的技术人员
UI 设计 GUI、视觉设计等
BD 商务 负责吹牛逼的
硬件设计 PCB、原理图设计 如果涉及硬件产品
嵌入式 嵌入式驱动开发 如果涉及硬件产品
系统驱动 系统驱动开发 如果涉及硬件产品
结构设计 结构设计开发 如果涉及硬件产品
ID设计 外观设计 如果涉及硬件产品

我整理了下,我们需要的上面这么多角色,当然,在很多创业公司,都是一人兼多职(工资就一份)。但是和我们需要的产品发布和迭代节奏完全相反的是,老板肯定需要快速稳定的发布产品,1周恨不得更新一次feature list。

产品研发流程有多复杂

上面的流程,基本是软件研发的基本流程,为了保证各种质量,涉及到各种角色和阶段,需要整体项目负责人有很强的管理和协调能力,才能保证项目的预期发布和质量管控。纯靠人去跟踪,基本管不好。

迭代速度 快!快 ! 快!

下面的桥段,大家肯定遇到过:

老板说:我们先做一个DEMO,BD需要这周末去个客户演示拿单子。演示完了,然后。。。。

老板再说:客户那边演示的不错,要求1-2周内上线(程序猿懵逼,下面就是产品和程序猿的互撕了)

我们有多少产品和项目是从DEMO改起来的,软件质量、架构体系、产品价值和前景,是否符合公司核心价值,这些都不是考虑范畴了,基本属于,后续全是坑。

任何公司的核心价值都是产品质量的优秀,优秀的产品,我们这里不谈什么是好产品,我们这里聚焦的点是,产品技术怎么保证稳定快速发布新产品。答案就是DevOps。

DevOps的核心价值

DevOps由开发(developments)和运维(operations)组成,字面意义就是鼓励跨部门协作。DevOps的出现其实是敏捷软件开发的产物,下面是敏捷宣言的经典摘录:

1
2
3
4
5
个体和互动 高于 流程和工具,
工作的软件 高于 详尽的文档,
客户合作 高于 合同谈判,
响应变化 高于 遵循计划,
也就是说,尽快右项有其价值,我们更重视左项的价值。

DevOps想要强调的是:个体和互动是非常重要的,用自动化代替重复人工工作,把更多的时间留给大家的沟通和交流,产生真实的价值。

举个简单例子,PM想尽快了解开发的进度(眼见为实,其实UI更需要眼见为实),那就要求RD和QA,需要在测试环境进行快速的增量交付,并且交付的内容可以让PM和UI等利益相关方,尽快看到,并且可以第一个吃狗粮。如果是跨部门的传统合作,试想下,难度是不言而喻的。但是DevOps可以通过一系列自动化和持续交付的工具,以及统一产品目标的理念,让部门壁垒消失,工具提醒人员,去做这个阶段需要做的事情,大家都在管道内,进行有条理的工作,管道的终点就是产品稳定快速发布。

DevOps的流程和工具

  • 流程:DevOps是为了产品交付服务的,交付肯定是有流程的,所以我们先需要明确流程
  • 工具:根据语言、技术人员素质、产品特点,选择合适的工具拓扑,其实最主要的工具包含以下几类:需求管理工具、代码库、构建工具、工件库、测试工具、部署工具、监控工具、问题跟踪

后面几篇文章,会陆续分享下,我在创业公司,是怎么搭建的这套DevOps体系和如何使用的。

谢谢阅读。

微信关注我,及时接收最新技术文章