Skip to main content

· 15 分钟阅读

Choerodon猪齿鱼全场景效能平台,是基于Kubernetes,Istio,knative,Gitlab,Spring Cloud来实现本地和云端环境的集成,实现企业多云/混合云应用环境的一致性。平台通过提供精益敏捷、持续交付、容器环境、微服务、DevOps等能力来帮助组织团队来完成软件的生命周期管理,从而更快、更频繁地交付更稳定的软件。

2018年11月23日,Choerodon猪齿鱼发布0.11版本,本次更新内容包含新增了访问快捷方式、敏捷工作日历、站内通知、开发控制台、集群管理等诸多功能,以及对Agent、环境权限等进行了优化,欢迎各位更新体验。

  • 发布版本:0.11
  • 发布时间:2018年11月23日
  • 功能范围:知识管理、敏捷管理、持续交付、测试管理以及微服务开发框架

下面就为大家带来详细的版本更新介绍。

新增功能

知识管理

  • 新增在编辑器中能够添加敏捷issue的宏的功能:可以针对文档中的内容将其链接到敏捷管理中的问题,方便用户直接关联问题进行内容调整。
  • 新增在Wiki中创建空间的功能。
  • 新增文档收藏功能。
  • 新增分享按钮,可以直接复制页面短链接。
  • 新增空间弹出框的空间搜索功能。
  • Wiki新增配置邮件服务器,能够使用邮件通知和邮件分享功能。
  • 新增Wiki的logo和favicon可以根据Choerodon平台的设置同步设置的功能。
  • 新增Choerodon平台分配平台管理员时,Wiki同步分配系统管理员的功能。
  • 新增了Choerodon平台的组织和项目首页的Wiki空间组件。

敏捷管理

  • 新增时区日历功能:用户可以在组织层设置时区、节假日、工作日并应用在敏捷管理中。在开启冲刺选择日期的时候,可以勾选当前冲刺期间的工作日与非工作日。

  • 新增站内信通知功能:用户可以在组织层对问题创建、问题分配、问题解决3个事件分配对应的通知对象。

  • 新增版本管理和模块管理搜索功能:版本管理列表和模块管理列表增加字段搜索功能。

持续交付

  • 开发流水线部分新增开发控制台,集成了开发流水线内主要的功能,能够更便捷地进行开发操作。

  • 新增集群管理模块,支持对Kubernetes集群的创建、编辑以及权限分配。
  • 新增环境的权限分配功能,支持为各个环境配置特定的操作人员。
  • 新增删除环境的功能,支持在环境停用区对环境进行删除操作。
  • 新增开发流水线代码仓库中查看代码质量的入口。
  • Dashboard页面新增快速查看分支情况、代码提交情况、应用构建情况与部署情况的模块,并提供了快速跳转至相应模块的入口。
  • 实例部分新增deployments层,且支持一个chart文件中存在多个deployments。
  • 新增实例重新部署的功能。
  • 新增报表中部署失败的错误信息。

测试管理

  • 测试用例新增使用模板Excel导入功能。
  • 测试用例新增导出功能。
  • 测试执行详情新增翻页功能。

微服务开发框架

  • 平台界面增加新用户使用指导,可按照教程快速了解Choerodon平台基本设置。

  • 平台界面新增快捷方式入口,可添加常用功能界面至快捷方式,方便用户快速跳转页面。

  • 新增系统自定义配置,平台管理员可以对平台自定义设置。

  • 新增客户端角色分配,平台管理员、组织管理员和项目管理员可以为客户端分配操作权限。
  • 新增消息接收设置,用户可以设置接收消息的类别。
  • 新增仪表盘启停用功能,平台管理员可以启停用自己的仪表盘。
  • 新增组织层、项目层任务调度。
  • LDAP同步添加超时强制停止。

功能优化

平台功能

  • 本次更新将敏捷报表、DevOps报表、测试报表集中于报表管理功能中,方便用户查看。

知识管理

  • 修改Wiki页面复制或移动成功之后直接跳转到目标页。
  • 修改Wiki页面删除成功之后跳转到其父页面。
  • Wiki管理菜单现在提到了组织和项目的顶层,并增加了Wiki空间菜单。
  • 修改了搜索的弹出框和搜索页面的样式。
  • 修改了404、403的页面样式。
  • 修改了所有更新、热门、最近工作、最近访问、所有空间、最近空间为异步加载,提高页面加载性能。
  • 修改了用户信息页样式。
  • 页面的编辑等功能按钮移动到了靠左的位置。

敏捷管理

  • 问题管理新增字段展示、字段搜索、字段排序,支持自定义筛选。
  • 活跃冲刺中的问题拖到其他位置,问题及其子任务全部还原到状态机初始状态。
  • 活跃冲刺界面展示和问题详情表单页面优化。
  • 产品全局图标优化。
  • 待办事项史诗计数详情优化。
  • 问题链接列表显示经办人信息。
  • 迭代速度图未开启的冲刺不统计。
  • 故事地图中移除问题添加验证。
  • 优化待办事项创建问题请求。

持续交付

  • 从之前“利用单个环境客户端管理单个环境”的模式变为使用“单个集群客户端可以统一管理多个环境”的模式。 0.10版本中,当在同一个集群上初始化多个环境时,需要向集群安装多个环境Agent应用,升级时需要针对每一个Agent在同一个集群执行升级脚本,维护成本较高;升级成集群客户端之后,在同一个集群创建环境时将不再需要执行环境客户端安装脚本,一键即可以创建环境、停用、删除环境。针对环境维度的操作不需要再去集群中执行相应脚本。
  • 重新整理优化开发流水线结构,统一以应用为中心进行操作。
  • 重新整理优化部署流水线结构,统一以环境为中心进行操作。
  • 优化了应用市场导出文件的命名,支持自定义命名。
  • 优化统一了平台各个空界面。
  • 优化了删除操作提示框,明确指出了删除对象名称。
  • 完善了平台指导文案,加强初级用户的理解。
  • 优化了删除实例后,关联网络列表中的目标对象内容。
  • 优化了实例升级失败或新建失败后,列表中版本的显示问题。
  • 优化了环境总览界面顶部创建操作按钮的显示。
  • 优化了部署总览界面快速部署的图标显示。

测试管理

  • 测试执行导出改为异步修改,增加进度条。
  • 配合敏捷服务修改部分接口。
  • 测试用例文件夹复制和移动现在可进行批量操作。
  • 测试阶段文件夹查看增加版本显示。
  • 创建阶段有默认时间。
  • 测试步骤可拖动滚动。
  • 测试执行页面隐藏空循环。
  • 测试摘要按版本显示从新到旧排序。
  • 测试用例倒序排列。
  • 将测试执行和测试计划侧边展开状态保存。
  • 创建测试步骤不弹出新建页,在表格中插入新行进行编辑。
  • 测试步骤复制图标改为按钮。
  • 测试计划、测试执行表格的样式调整。
  • 测试计划中克隆测试阶段可以跨循环、版本。

微服务开发框架

  • 仪表盘配置优化为可在界面上控制哪些角色可见。
  • 邮件模板创建时优化为可添加网络图片,并且支持HTML编码。
  • API 测试修改为内部接口不能在页面进行测试。

缺陷修复

知识管理

  • 修复Wiki文章的内容块区域互相遮挡的问题。
  • 修复Wiki创建页面在没有填写标题的情况下也能创建成功的问题。
  • 修复Wiki编辑器添加的issue宏,url中没有项目名的问题。
  • 修复Wiki的通知信息,用户没办法删除的问题。
  • 修复创建页面的模板描述太长的问题。
  • 修复创建页面树状浏览器选择出现不应该出现的页面的问题。
  • 修复创建页面树状浏览器选择没办法选择到组织的问题。

敏捷管理

  • 修复问题管理中工作日志时间登记后页面数据没有更新的问题。
  • 修复待办事项版本、史诗排序错误。
  • 修复活跃冲刺及迭代工作台剩余时间计算错误。
  • 修复活跃冲刺中同列多个状态拖动白屏的问题。
  • 修复问题转换为子任务状态颜色不正确的问题。
  • 修复发布版本跳转未解决问题列表筛选错误的问题。
  • 修复链接地址中未做转码处理导致请求重复的问题。
  • 修复燃尽图报告点击子任务进入的是父任务详情的问题。
  • 修复史诗和版本燃耗图中链接到问题管理,返回页面404的问题
  • 修复发布版本时统计未完成数量不正确的问题。

持续交付

  • 修复编辑应用名称时,未分辨输入字母的大小写的问题。
  • 修复创建网络时,选择实例与选择应用的逻辑问题。
  • 修复了偶现替换实例失败的问题。
  • 修复了部署超时后无法操作的问题。
  • 修复了创建域名时未校验环境的问题。
  • 修复了创建应用失败后不能处理的问题。

测试管理

  • 修复测试用例文件夹复制拖动的不滚动的问题。
  • 修复测试计划页面滚动底部的问题。
  • 修复表格编辑保存时闪现旧值的问题。
  • 修复了修改测试用例后不跳到第一页的问题。

微服务开发框架

  • 修复API测试加载缓慢的问题。
  • 修复新导入的LADP用户报错的问题。
  • 修复IE兼容性问题。

删除

知识管理

  • 删除了Wiki中无用的宏。

持续交付

  • 移除了项目中部署管理员角色,并将其所有权限分配给项目所有者。
  • 移除了部署流水线实例管理中的部署实例与单应用视图。
  • 移除了停止实例后的升级实例与重新部署的选项。

社区参与

感谢以下这些朋友在社区论坛中提出反馈和意见,在此次版本更新中作出突出贡献。

更加详细的内容,请参阅Release Notes官网

欢迎通过我们的GitHub猪齿鱼社区进行反馈与贡献,帮助Choerodon猪齿鱼不断成长,我们将持续迭代优化,敬请期待。

· 5 分钟阅读

敏捷之旅是一个国际非盈利性组织,于2008年成立,总部位于法国。其目的是提供一个高效有趣的敏捷开发学习途径,在全球范围内推广敏捷的思想和实践,帮助企业更好的实施敏捷。

敏捷之旅北京站始于2010年。活动致力于在北京地区推广敏捷的实践经验,传播敏捷实践方法,建立敏捷的学习圈子,让敏捷爱好者彼此建立联接。

大会议程安排

技术研发团队敏捷转型案例分享

汉得信息作为国内领先的企业级数字化服务提供商,本课题将分享其技术研发团队为了支持公司帮助客户完成数字化变革的业务,根据技术研发团队自身的需求,逐步引入和学习敏捷工程实践及敏捷管理方法,并根据使用的反馈持续的进行优化调整,最后将敏捷及相关的最佳实践落地到Choerodon猪齿鱼平台中,帮助客户开启数字化转型之旅。

分享者介绍

张礼军

上海汉得信息技术有限公司研发总经理&Choerodon猪齿鱼平台总设计师

16年的IT从业经验,拥有多个产品的成功研发经验,包括传统的Web应用和SaaS应用系统,并广泛被各种类型的客户所使用;同时拥有多年的大型系统架构设计、实施和推广经验。目前正带领团队经营Choerodon猪齿鱼社区、敏捷组织转型、DevOps实践,推动数字化服务转型与变革。获得DevOps Master、Lean IT Professional、Agile Scrum Master、ITIL V3 Expert等证书。

内容提要
  • 敏捷转型的背景
    • 业务转型驱动的技术转型
    • 数字技术转型的需求
    • 数字技术转型的目标
    • 转型面临的双峰挑战(Bimodal Challenge)
  • 敏捷实践及工具的应用
    • 敏捷团队管理
    • 敏捷流程建立
    • 技术堆栈选择
    • 团队学习和探索    
  • 敏捷转型成果:Choerodon猪齿鱼平台
    • 平台定位与用途
    • 服务模块
  • 敏捷转型的经验总结
    • 敏捷的理解
    • 变革的挑战
    • 变革的应对
    • 变革的步骤
    • 研发体系的目标
时间和地址

时间:2018.11.25(周日) 9:30-17:30

地址:北京市海淀区中关村软件园13号二期广联达大厦

关于信息

欢迎通过我们的GitHub猪齿鱼社区进行反馈与贡献,帮助Choerodon猪齿鱼不断成长,我们将持续迭代优化,敬请期待。

· 15 分钟阅读

▌文章的主要内容包括:

  • 服务注册/发现
  • 服务注册表
  • 健康检查

在上一篇文章的开始,我们提到解决微服务架构中的通信问题,基本只要解决下面三个问题:

  • 服务网络通信能力
  • 服务间的数据交互格式
  • 服务间如何相互发现与调用

网络的互通保证了服务之间是可以通信的,通过对JSON 的序列化和反序列化来实现网络请求中的数据交互。Choerodon 的 API 网关则统一了所有来自客户端的请求,并将请求路由到具体的后端服务上。然而这里就会有一个疑问,API 网关是如何与后端服务保持通信的,后端服务之间又是如何来进行通信的?当然我们能想到最简单的方式就是通过 URL + 端口的形式直接访问(例如:http://127.0.0.1:8080/v1/hello)。

在实际的生产中,我们认为这种方式应该是被避免的。因为 Choerodon 的每个服务实例都部署在K8S的不同 pod中,每一个服务实例的 IP 地址和端口都可以改变。同时服务间相互调用的接口地址如何管理,服务本身集群化后又是如何进行负载均衡。这些都是我们需要考虑的。

为了解决这个问题,自然就想到了微服务架构中的注册中心。一个注册中心应该包含下面几个部分:

  • 服务注册/发现:服务注册是微服务启动时,将自己的信息注册到注册中心的过程。服务发现是注册中心监听所有可用微服务,查询列表及其网络地址。
  • 服务注册表:用来纪录各个微服务的信息。
  • 服务检查:注册中心使用一定的机制定时检测已注册的服务,如果发现某实例长时间无法访问,就会从服务注册表中移除该实例。

Choerodon 中服务注册的过程如下图所示:

服务注册/发现

当我们通过接口去调用其他服务时,调用方则需要知道对应服务实例的 IP 地址和端口。对于传统的应用而言,服务实例的网络地址是相对不变的,这样可以通过固定的配置文件来读取网络地址,很容易地使用 HTTP/REST 调用另一个服务的接口。

但是在微服务架构中,服务实例的网络地址是动态分配的。而且当服务进行自动扩展,更新等操作时,服务实例的网络地址则会经常变化。这样我们的客户端则需要一套精确地服务发现机制。

Eureka 是 Netflix 开源的服务发现组件,本身是一个基于REST的服务。它包含 Server 和 Client 两部分。

Eureka Server 用作服务注册服务器,提供服务发现的能力,当一个服务实例被启动时,会向 Eureka Server 注册自己的信息(例如IP、端口、微服务名称等)。这些信息会被写到注册表上;当服务实例终止时,再从注册表中删除。这个服务实例的注册表通过心跳机制动态刷新。这个过程就是服务注册,当服务实例注册到注册中心以后,也就相当于注册中心发现了服务实例,完成了服务注册/发现的过程。

阅读 Spring Cloud Eureka 的源码可以看到,在eureka-client-1.6.2.jar 的包中,com.netflix.discovery. DiscoveryClient 启动的时候,会初始化一个定时任务,定时的把本地的服务配置信息,即需要注册到远端的服务信息自动刷新到注册服务器上。该类包含了Eureka Client 向 Eureka Server 注册的相关方法。

在 DiscoveryClient 类有一个服务注册的方法 register(),该方法是通过 HTTP 请求向Eureka Server 注册。其代码如下:

boolean register() throws Throwable {
logger.info(PREFIX + appPathIdentifier + ": registering service...");
EurekaHttpResponse<Void> httpResponse;
try {
httpResponse = eurekaTransport.registrationClient.register(instanceInfo);
} catch (Exception e) {
logger.warn("{} - registration failed {}", PREFIX + appPathIdentifier, e.getMessage(), e);
throw e;
}
if (logger.isInfoEnabled()) {
logger.info("{} - registration status: {}", PREFIX + appPathIdentifier, httpResponse.getStatusCode());
}
return httpResponse.getStatusCode() == 204;
}

对于 Choerodon 而言,客户端依旧采用 Eureka Client,而服务端采用 GoLang 编写,结合 K8S,通过主动监听 K8Spod 的启停,发现服务实例上线,Eureka Client 则通过 HTTP 请求获取注册表,来实现服务注册/发现过程。

注册中心启动时,会构造一个podController,用来监听pod 的生命周期。代码如下:

func Run(s *options.ServerRunOptions, stopCh <-chan struct{}) error {
... ...
podController := controller.NewController(kubeClient, kubeInformerFactory, appRepo)


go kubeInformerFactory.Start(stopCh)

go podController.Run(instance, stopCh, lockSingle)

return registerServer.PrepareRun().Run(appRepo, stopCh)
}

github.com/choerodon/go-register-server/controller/controller.go 中定义了 Controller,提供了 Run() 方法,该方法会启动两个进程,用来监听环境变量 REGISTER_SERVICE_NAMESPACE 中配置的对应 namespace 中的pod,然后在pod 启动时,将 pod 信息转化为自定义的服务注册信息,存储起来。在 pod 下线时,从存储中删除服务信息。其代码如下:

func (c *Controller) syncHandler(key string, instance chan apps.Instance, lockSingle apps.RefArray) (bool, error) {
namespace, name, err := cache.SplitMetaNamespaceKey(key)
if err != nil {
runtime.HandleError(fmt.Errorf("invalid resource key: %s", key))
return true, nil
}
pod, err := c.podsLister.Pods(namespace).Get(name)
if err != nil {
if errors.IsNotFound(err) {
if ins := c.appRepo.DeleteInstance(key); ins != nil {
ins.Status = apps.DOWN
if lockSingle[0] > 0 {
glog.Info("create down event for ", key)
instance <- *ins
}
}
runtime.HandleError(fmt.Errorf("pod '%s' in work queue no longer exists", key))
return true, nil
}
return false, err
}
_, isContainServiceLabel := pod.Labels[ChoerodonServiceLabel]
_, isContainVersionLabel := pod.Labels[ChoerodonVersionLabel]
_, isContainPortLabel := pod.Labels[ChoerodonPortLabel]
if !isContainServiceLabel || !isContainVersionLabel || !isContainPortLabel {
return true, nil
}
if pod.Status.ContainerStatuses == nil {
return true, nil
}
if container := pod.Status.ContainerStatuses[0]; container.Ready && container.State.Running != nil && len(pod.Spec.Containers) > 0 {
if in := convertor.ConvertPod2Instance(pod); c.appRepo.Register(in, key) {
ins := *in
ins.Status = apps.UP
if lockSingle[0] > 0 {
glog.Info("create up event for ", key)
instance <- ins
}
}
} else {
if ins := c.appRepo.DeleteInstance(key); ins != nil {
ins.Status = apps.DOWN
if lockSingle[0] > 0 {
glog.Info("create down event for ", key)
instance <- *ins
}
}
}
return true, nil
}

github.com/choerodon/go-register-server/eureka/repository/repository 中的 ApplicationRepository 提供了 Register() 方法,该方法手动将服务的信息作为注册表存储在注册中心中。

func (appRepo *ApplicationRepository) Register(instance *apps.Instance, key string) bool {

if _, ok := appRepo.namespaceStore.Load(key); ok {
return false
} else {
appRepo.namespaceStore.Store(key, instance.InstanceId)
}
appRepo.instanceStore.Store(instance.InstanceId, instance)
return true
}

通过上面的代码我们可以了解到Choerodon 注册中心是如何实现服务注册的。有了注册中心后,下面我们来介绍下服务发现中的服务注册表。

服务注册表

在微服务架构中,服务注册表是一个很关键的系统组件。当服务向注册中心的其他服务发出请求时,请求调用方需要获取注册中心的服务实例,知道所有服务实例的请求地址。

Choerodon 沿用 Spring Cloud Eureka 的模式,由注册中心保存服务注册表,同时客户端缓存一份服务注册表,每经过一段时间去注册中心拉取最新的注册表。

github.com/choerodon/go-register-server/eureka/apps/types 中定义了Instance 对象,声明了一个微服务实例包含的字段。代码如下:

type Instance struct {
InstanceId string `xml:"instanceId" json:"instanceId"`
HostName string `xml:"hostName" json:"hostName"`
App string `xml:"app" json:"app"`
IPAddr string `xml:"ipAddr" json:"ipAddr"`
Status StatusType `xml:"status" json:"status"`
OverriddenStatus StatusType `xml:"overriddenstatus" json:"overriddenstatus"`
Port Port `xml:"port" json:"port"`
SecurePort Port `xml:"securePort" json:"securePort"`
CountryId uint64 `xml:"countryId" json:"countryId"`
DataCenterInfo DataCenterInfo `xml:"dataCenterInfo" json:"dataCenterInfo"`
LeaseInfo LeaseInfo `xml:"leaseInfo" json:"leaseInfo"`
Metadata map[string]string `xml:"metadata" json:"metadata"`
HomePageUrl string `xml:"homePageUrl" json:"homePageUrl"`
StatusPageUrl string `xml:"statusPageUrl" json:"statusPageUrl"`
HealthCheckUrl string `xml:"healthCheckUrl" json:"healthCheckUrl"`
VipAddress string `xml:"vipAddress" json:"vipAddress"`
SecureVipAddress string `xml:"secureVipAddress" json:"secureVipAddress"`

IsCoordinatingDiscoveryServer bool `xml:"isCoordinatingDiscoveryServer" json:"isCoordinatingDiscoveryServer"`
LastUpdatedTimestamp uint64 `xml:"lastUpdatedTimestamp" json:"lastUpdatedTimestamp"`
LastDirtyTimestamp uint64 `xml:"lastDirtyTimestamp" json:"lastDirtyTimestamp"`
ActionType string `xml:"actionType" json:"actionType"`
}

客户端可以通过访问注册中心的/eureka/apps 接口获取对应的注册表信息。如下所示:

{
"name": "iam-service",
"instance": [
{
"instanceId": "10.233.73.39:iam-service:8030",
"hostName": "10.233.73.39",
"app": "iam-service",
"ipAddr": "10.233.73.39",
"status": "UP",
"overriddenstatus": "UNKNOWN",
"port": {
"@enabled": true,
"$": 8030
},
"securePort": {
"@enabled": false,
"$": 443
},
"countryId": 8,
"dataCenterInfo": {
"name": "MyOwn",
"@class": "com.netflix.appinfo.InstanceInfo$DefaultDataCenterInfo"
},
"leaseInfo": {
"renewalIntervalInSecs": 10,
"durationInSecs": 90,
"registrationTimestamp": 1542002980,
"lastRenewalTimestamp": 1542002980,
"evictionTimestamp": 0,
"serviceUpTimestamp": 1542002980
},
"metadata": {
"VERSION": "2018.11.12-113155-master"
},
"homePageUrl": "http://10.233.73.39:8030/",
"statusPageUrl": "http://10.233.73.39:8031/info",
"healthCheckUrl": "http://10.233.73.39:8031/health",
"vipAddress": "iam-service",
"secureVipAddress": "iam-service",
"isCoordinatingDiscoveryServer": true,
"lastUpdatedTimestamp": 1542002980,
"lastDirtyTimestamp": 1542002980,
"actionType": "ADDED"
}
]
}

我们可以在服务注册表中获取到所有服务的 IP 地址、端口以及服务的其他信息,通过这些信息,服务直接就可以通过 HTTP 来进行访问。有了注册中心和注册表之后,我们的注册中心又是如何来确保服务是健康可用的,则需要通过健康检查机制来实现。

健康检查

在我们提供了注册中心以及服务注册表之后,我们还需要确保我们的服务注册表中的信息,与服务实际的运行状态保持一致,需要提供一种机制来保证服务自身是可被访问的。在Choerodon微服务架构中处理此问题的方法是提供一个健康检查的端点。当我们通过HTTP 进行访问时,如果能够正常访问,则应该回复HTTP 状态码200,表示健康。

Spring Boot 提供了默认的健康检查端口。需要添加spring-boot-starter-actuator 依赖。

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

访问/health 端点后,则会返回如下类似的信息表示服务的状态。可以看到 HealthEndPoint 给我们提供默认的监控结果,包含磁盘检测和数据库检测等其他信息。

{
"status": "UP",
"diskSpace": {
"status": "UP",
"total": 398458875904,
"free": 315106918400,
"threshold": 10485760
},
"db": {
"status": "UP",
"database": "MySQL",
"hello": 1
}
}
  • K8S 通过,/health 通过
  • K8S 通过,/health 未通过
  • K8S 未通过,/health 通过

第一种情况,当两种都通过的话,服务是可以被访问的。

第二种情况,K8S 认为服务是正常运行的,但注册中心认为服务是不健康的,注册表中不会记录该服务,这样其他服务则不能获取该服务的注册信息,也就不会通过接口进行服务调用。则服务间不能正常访问。如下图所示。

第三种情况,服务通过心跳告知注册中心自己是可用的,但是可能因为网络的原因,K8Spod 标识为不可访问,这样当其他服务来请求该服务时,则不可以访问。这种情况下服务间也是不能正常访问的。如下图所示。

同时,当我们配置了管理端口之后,该端点则需要通过管理端口进行访问。可以在配置文件中添加如下配置来修改管理端口。

management.port: 8081

但是在这种情况下,会使我们的健康检查变得更加复杂,健康检查并不能获取服务真正的健康状态。

在这种情况下,Choerodon 使用 K8S 来监听服务的健康端口,同时需要保证服务的端口与管理端口都能被正常访问,才算通过健康检查。可以在部署的 deploy 文件中添加readinessProbe 参数。

apiVersion: v1
kind: Pod
spec:
containers:
readinessProbe:
exec:
command:
- /bin/sh
- -c
- curl -s localhost:8081/health --fail && nc -z localhost 8080
failureThreshold: 3
initialDelaySeconds: 60
periodSeconds: 10
successThreshold: 1
timeoutSeconds: 10

这样,当我们的服务启动之后,才会被注册中心正常的识别。当服务状态异常时,也可以尽快的从注册表中移除。

关于猪齿鱼

Choerodon 猪齿鱼是一个全场景效能平台,基于 Kubernetes 的容器编排和管理能力,整合 DevOps 工具链、微服务和移动应用框架,来帮助企业实现敏捷化的应用交付和自动化的运营管理的平台,同时提供 IoT、支付、数据、智能洞察、企业应用市场等业务组件,致力帮助企业聚焦于业务,加速数字化转型。

大家也可以通过以下社区途径了解猪齿鱼的最新动态、产品特性,以及参与社区贡献:

· 22 分钟阅读

在敏捷开发的实践中,通过规划冲刺中不同的阶段,开发可以达到如下几个目的:

  1. 可视化管理团队的目标;
  2. 明确目标的优先级;
  3. 明确目标分解后的任务项;
  4. 可视化管理任务的进展状况。

规划冲刺

利用发布计划,可顺利地将粗颗粒度的故事分配到发布中的多轮迭代中。不过,在开始一轮迭代时,有必要去针对该迭代再去做进一步的计划。

计划会议

整个团队通过举行计划会议来为下一个迭代做计划。客户以及团队中的所有开发人员,包括程序员,测试人员和其他人都要参与这个会议,计划会议的内容一般如下:

  1. 讨论分析用户故事;
  2. 从故事中拆分出任务;
  3. 分配每个开发人员需要承担的任务;
  4. 开发人员单独估计自身所承担的任务,以确保他们不会做出过于乐观的承诺。

在实际的操作中,团队计划会议后会开启一个冲刺,将backlog中确定在该冲刺进行的故事拖到冲刺列表中,随后为每个故事进行分解并关联对应的任务。

讨论故事

迭代计划会后,团队会获得一个已经排好优先级的故事集合。正如程序员可能改变他们对实现一个故事难度的看法,客户或产品负责人一样可以改变他们对故事优先级的想法。计划会议是客户或产品负责人为团队调整故事优先级的最佳阶段。

会议中,PO(产品负责人)会从最高优先级的故事开始给团队讲解每个故事的内容。然后由开发人员提问,直到他们充分理解故事,能从故事中分解出任务。这个过程没有必要理解故事的所有细节,过分地深入每个故事细节会使会议变得低效、冗长,因为会议中不是每个人都需要聆听所有故事的细节。在计划会议后,开发人员可以和PO一起理清故事的关键细节。

拆分任务

理解用户故事的大致内容后,需再将故事分解为任务。在Choerodon敏捷管理中,可以通过创建子任务的方式来分解故事,并将故事与这些拆分后的任务进行关联。只有当所有的子任务状态变成已完成时,这个故事才能拖到已完成的列中。

为何要拆分故事而不直接把故事作为独立的工作单位?

尽管故事的确可以小到作为工作单位,但将故事拆分出更小的任务,一般更符合项目开发的需要。

首先,对于很多团队来说,实现故事的开发人员不止一个,需要由多个开发人员共同完成一个故事。这要么是因为开发人员在某些特定技术上的专业性,比如前后端分离开;要么是因为工作划分是完成故事的最快途径。

其次,故事是对用户或客户有价值的功能的描述,不是开发人员的代办事项,把故事拆解成任务有助于发现那些可能会被遗忘的任务。一整个小组一起来拆分任务,依靠的是所有人的集体智慧,避免某个人忘记了故事中的某个部分。

相比瀑布过程,敏捷没有前期设计阶段,其过程特点是做频繁的短期设计,当脑海里至少有一个最小的设计方案时,才可能从故事中拆分出任务。所以,一个故事的任务拆分其实是即时设计中的一次短脉冲,以这些短脉冲的集合取代瀑布过程的前期设计阶段。

在团队成员说明任务时,需要实时将围绕某个故事的任务都记录下来,然后将任务以卡片的形式在物理或电子看板中进行展示。

Choerodon猪齿鱼敏捷管理中的活跃冲刺模块,团队可以针对每一个迭代开启一个新的冲刺,规划好时间,通过看板工具实时查看工作进度或开发瓶颈。

承担责任

通过看板,我们可以清晰地看到每个任务的责任人(采用结对编程时,每个任务也只关联一个人的名字,此人将承担完成任务和与PO、客户沟通信息的责任,确保在迭代期间完成任务。)

虽然任务事先已经由每个人认领,但在冲刺期间并不是一成不变的。在冲刺中,随着开发取得进展,成员会比之前预想的更了解任务,因此任务的认领及承诺也需要相应做出调整。

在敏捷中,团队协作非常重要,只有所有人达到预期,这个迭代才算完成,如果有开发人员不能完成他承担的所有任务,团队中的其他成员应该尽量勇于承担。

估算和确认

通常当一个团队经历了3个以上的迭代之后,基本可以确定团队的速率(即一个迭代可以完成的故事点总数),当然前提是每个迭代的时间周期是一样的。

假如你的团队的速率是平均每个迭代40个故事点,每个开发人员首先以理想时间估算自己承担的工作量,然后整个团队相加进而做出实际的评估,判断在该迭代中能否完成所有任务。

例如,为期2周的冲刺开始了,一个开发人员估算完成任务实际需要55个小时,算上必须要做的其他事情,没有把握在这些任务上投入更多时间,这种情况有以下选择:

  • 继续往前走,一个一个的去完成,寄希望于一切顺利
  • 请求团队中其他成员接收一部分任务
  • 与PO讨论,放弃一个故事,或者拆分故事,放弃其中一部分
  • 最好的方法是后两种,整个团队必须同舟共济。

开启冲刺

迭代会议结束后,团队正式进入到这个迭代的开发中。Choerodon猪齿鱼敏捷管理中,把一个正式开启的迭代称作为一个活跃冲刺,一个看板针对一个活跃冲刺。团队根据计划制定这个迭代的目标和时间范围。

在迭代过程中,Choerodon敏捷管理引用了看板方法来管理我们的开发。 看板的结构通常包括如下几个列:

  • Backlog :是这个冲刺需要完成的所有用户故事,这些故事加在一起就是这个迭代的目标,这些故事通常按照优先级从上到下排列。
  • Todo :这一列代表的是待办任务项,用户故事会被拆分为对应的开发任务,这些待办的开发任务将展示在Todo这一列中。
  • Doing : 进行中的任务。
  • Done :已经完成的任务和用户故事。

任务看板上除了有默认的4列之外,还可以自定义新建泳道,通过泳道来管理故事和任务的对应关系。

在冲刺的交付阶段我们应该将以下几点作为看板方法的步骤去贯彻执行:

  • 专注于质量;
  • 减少进行中的工作(work-in-process);
  • 频繁交付;
  • 根据交付速率来平衡任务的请求量;
  • 按照任务的优先级排序进行开发;
  • 消除变异性的根源,提高可预测性。

专注于质量

很多开发团队将可用资源花在与缺陷修复相关的工作上,这会对高缺陷率团队的生产力和交付速率产生巨大影响。

鼓励提高初始质量,很可能提升2-4倍的交付速率,如果团队真的很糟糕,那么通过“专注于质量”的做法,甚至都可能获得10倍的交付速率的提升。

专业的测试人员应该做好测试,让测试人员来发现缺陷,防止缺陷遗漏在代码中。要求开发人员编写单元测试代码,使单元测试代码自动化,也就是我们经常提及的自动化测试,以提供自动化的回归测试,这样也可以产生巨大的效果。测试驱动开发似乎确实能带来使测试覆盖更为完整的好处。对于普通团队,在功能编码前编写测试代码,能够提高代码质量。

代码检查能够帮助改善外部和内部的代码质量,最好经常做,并且以小批量进行为好。

协作式的分析和设计,也能够提高代码质量。团队一起分析问题和设计解决方案,产出的质量会更高。

还有使用现代开发工具提高质量,许多现代开发工具都包括静态代码分析和动态代码分析的功能,需要在开发中不断地进行代码优化,这些工具可以防止程序员犯低级错误。

减少在制品

在制品和平均前置时间之间存在相关性。前置时间越长,质量就会显著下降,而在制品数量越多,平均前置时间则越长。因此,提高质量的关键是减少在制品数量。

减少在制品的数量或缩短迭代的长度,将对初始质量产生重大影响。随着在制品数量的增加,缺陷数量会不成比例地增加。为期2周的迭代比4周的迭代好是很有道理的。较短的的迭代会产出更高的质量。

仅需使用看板来限制在制品数量便可提升质量,那为什么不引入明确的规则来限制在制品数量,解放管理人员使其可关注于其他的管理工作。

敏捷管理中强调的对于在制品的限制,Choerodon敏捷管理也进行了应用。团队可以根据开发能力和进度设置列约束,通过限制处理中的卡片数量来控制开发人员的在制品数量,只有当在制品卡片数量小于限制数,才可以到backlog中拉取新的任务。如下图:

频繁交付

减少在制品数量能够缩短前置时间,缩短前置时间意味着可以更为频繁地提交可用的代码。频繁的提交代码,能够与外部团队或业务建立信任。

Choerodon敏捷开发中,开发人员会在每天定点提交合并代码(即使只完成部分功能)。如下图的代码提交日志会记录每个人提交合并的时间,会对提交代码部分进行备注,这样可以提高多个开发人员的合作效率:

在软件开发中,规模虽小但是频繁、高质量地发布交付,较之规模大但频率低的发布,更能够在团队合作上建立起信任。小规模的发布说明,团队具有交付能力,并能够一直致力于产出价值。便于软件开发团队与市场、与用户之间建立起信任。

为什么以小批量的方式进行编码能够提高产品质量?这点其实也很好理解。随着进行中工作项数量的增加,问题的复杂性也会呈指数级增加。在软件开发中,有很多知识迁移和信息发现活动,他们是隐性的,而且都是面对面的协作过程中发生的。虽然这些信息具备口头表述性和可视性的特征,但是他们大多以画像在草图之类的非正式形态存在。我们的大脑无法存储这些隐性知识,即时记住,也会遗忘。更无法记得确切的细节,并会因此犯错。

如果减少进行中的工作,能减少对隐性知识的遗忘,从而获得高质量的产品,并促进更为频繁地发布。

根据交付速率来平衡任务的请求量

意味着根据交付可工作代码的速率,来设置新的任务进入开发流中,这样便可有效地将进行中的任务数量固定在某个值。在有交付后,便会从SpringBacklog里拉入新的任务。因此,任何对新工作的优先级排序,只可能在现有任务被交付的情况下才发生。

这一变化具有深远的影响,流程的交付速率会受限于某个瓶颈,可往往很难准确的找到这个瓶颈在何处。事实上,价值流中的每个人都会说自己已经超载。但通过交付速率,处于价值流其他环节的人员就会发现他们有了富余能力,而那些处于瓶颈处的人员的工作会很忙,但不会过载。这时整个团队成员会有一种手头终于有时间的感觉了。

在活跃冲刺中,团队可以实时查看每个成员的任务量,从而掌握团队进度以及瓶颈。

人们只有释放了大部分压力,才能集中精力准确高质量地完成工作。那些手上有富余时间的工作者,会开始将精力投向于环境改造。他们可能会开始不断提升自身技能,改进使用的工作,改善沟通协作方式等。随着时间的推移,团队在持续进行改善,进而整个团队都会得到改变。通过限制在制品以及当只有可用产能时才拉入新工作的做法,产生的富余时间能带来无法想象的改善行动。

想要进行持续改善,就要具备富余时间,为了产生富余时间,要做到根据交付速率来平衡需求请求量,限制在制品的数量。

优先级排序

当团队做到前几点要求后,心理重心应该转向优先级排序,而不仅仅只是交付的代码数量。

当交付方面缺乏可预测性时,很少有人会去关注优先级排序的问题。在解决这个问题之前,需要保证次序的可靠性,将管理精力重点用于改善交付能力和交付的可预测性上,一旦能够真正做到按需求请求的大致次序交付需求,就应该把思考转向如何对输入的需求进行优先级排序。

Choerodon猪齿鱼敏捷管理中,根据计划会议讨论后的结果,PO可以重新对故事的优先级进行排序,团队也将根据这个优先级安排开发任务的顺序。

如果不具备定期交付高质量代码的能力,就不可能建立起信任关系,因此,在优先级排序上具备影响力的可能性也会很小,也就无法进一步优化团队交付的价值。

总结

在敏捷管理方法中,前期的需求计划和讨论必不可少。而在正式的开发过程中,首先要学习构建高质量的代码,其次,减少进行中的工作项数量,缩短前置时间,并频繁发布。第三,根据交付速率来平衡需求请求量,对在制品设置限制,进而产生富余时间并释放个体的创造力,促进改善行为的发生。第四,随着软件开发的顺畅运转和能力优化,通过改善优先级排序来优化交付的价值。

期望一步实现优化业务价值,只是一种不切实际的美好愿望。遵循以上几点并采取行动,会让你的敏捷团队逐步达到期望的成熟度水平。

关于猪齿鱼

Choerodon 猪齿鱼是一个全场景效能平台,基于 Kubernetes 的容器编排和管理能力,整合 DevOps 工具链、微服务和移动应用框架,来帮助企业实现敏捷化的应用交付和自动化的运营管理的平台,同时提供 IoT、支付、数据、智能洞察、企业应用市场等业务组件,致力帮助企业聚焦于业务,加速数字化转型。

大家也可以通过以下社区途径了解猪齿鱼的最新动态、产品特性,以及参与社区贡献:

· 3 分钟阅读

Choerodon猪齿鱼社区的架构师分别介绍了Choerodon猪齿鱼最新版本的各项核心功能,以及后续功能开发计划。比如在DevOps方面,会在已有部署、开发、构建相关的可视化图表基础上,增加新的报表,帮助用户直观了解开发情况。同时会增强分支管理与敏捷管理的任务可追溯性,更好地将开发过程融入项目管理。

双方在交流中,主要探讨了Choerodon使用过程中遇到的一些问题以及开发技巧。票易通团队从不同的角度提出了一些改进意见和需求,Choerodon猪齿鱼将从中汲取这些反馈,归纳出一些可以增加的功能和优化方向。

比如在部署实施方面,将权限进一步细化到单个应用、单个环境级别,使项目下的每个环境和应用可以单独授权给不同的成员;微服务方面,增加工具来进行K8S容器的监控和管理;基础组件应用方面,优化基础组件调度问题等。

本次两个团队的技术交流是Choerodon猪齿鱼社区首个团队面对面交流活动,Choerodon猪齿鱼社区致力于促进Choerodon猪齿鱼平台的创新与应用,提供分享与交流的平台,来帮助开发人员更好地了解和应用Choerodon猪齿鱼,使企业能够更快地构建出色的产品,最终实现敏捷化的应用交付和自动化的运营管理。

关于信息

欢迎通过我们的GitHub猪齿鱼社区进行反馈与贡献,帮助Choerodon猪齿鱼不断成长,我们将持续迭代优化,敬请期待。

· 7 分钟阅读

首先,通过下面命令克隆并进入项目:

git clone https://github.com/choerodon/kubeadm-ansible.git && cd kubeadm-ansible

通过 Virtualbox + Vagrant启动三台CentOS系统虚拟机,在项目根目录中有编写好的Vagrantfile文件,直接使用就可以了。

Vagrantfile文件如下:

Vagrant.configure(2) do |config|

(1..3).each do |i|
config.vm.define "node#{i}" do |s|
s.vm.box = "bento/centos-7.3"
s.vm.box_url = "http://file.choerodon.com.cn/vagrant/box/bento_centos-7.3.box"
s.vm.hostname = "node#{i}"
n = 10 + i
s.vm.network "private_network", ip: "192.168.56.#{n}"
s.vm.provider "virtualbox" do |v|
v.cpus = 2
v.memory = 4096
end
end
end
end

其中box_url指定box镜像下载地址,hostname指虚拟机主机名,private_network指内网ip地址,cpus和memory指虚拟机的硬件资源要求。

vagrant-cachier插件用于不同虚拟机中共享公共包缓存,减少虚拟机的包下载时间。 根据上述Vagrantfile文件启动的虚拟机相关信息如下:

HostnameCPUMemoryIPSystem
node124G192.168.56.11CentOS 7.3
node224G192.168.56.12CentOS 7.3
node324G192.168.56.13CentOS 7.3

在项目根目录中执行下面命令启动虚拟机:

启动前请确认主机已开启CPU虚拟化支持。

vagrant up

登录虚拟机node1

vagrant ssh node1

部署Kubernetes

在node1中部署Ansible所需的环境

sudo yum install -y epel-release && \
sudo yum install -y \
ansible \
git \
httpd-tools \
pyOpenSSL \
python-cryptography \
python-lxml \
python-netaddr \
python-passlib \
python-pip

在node1中再次克隆项目代码(防止换行符改变导致后期部署出错)

git clone https://github.com/choerodon/kubeadm-ansible.git && cd kubeadm-ansible

在node1中编辑项目下的kubeadm-ansible/inventory/hosts文件,修改各机器的访问地址、用户名、密码,并维护好各节点与角色的关系,前面的名称为机器的hostname。该用户必须是具有root权限的用户,但并非要求一定是root用户,其他具有root权限的用户也可以。比如,想要部署单master节点集群,只需要这样配置(参考):

*在all分区中每一行为一个节点的信息,node1为该节点的hostname,ansible_host指节点内网IP,ip指Kubernetes目标绑定网卡IP,ansible_user为该节点具有管理员权限的一个用户,ansible_ssh_pass为该用户的密码,ansible_become代表执行命令时使用管理员权限。

其中Kube-Master节点为Kubernetes主节点、Kube-Node节点为Kubernetes普通节点、Etcd节点为将部署Etcd的节点,按本教程安装Kube-Master节点与Etcd节点必须一致,Etcd官方建议Etcd集群节点个数为奇数个(比如1、3、5)以防止脑裂。

[all]
node1 ansible_host=192.168.56.11 ip=192.168.56.11 ansible_user=root ansible_ssh_pass=vagrant ansible_become=true
node2 ansible_host=192.168.56.12 ip=192.168.56.12 ansible_user=root ansible_ssh_pass=vagrant ansible_become=true
node3 ansible_host=192.168.56.13 ip=192.168.56.13 ansible_user=root ansible_ssh_pass=vagrant ansible_become=true
[kube-master]
node1
[etcd]
node1
[kube-node]
node1
node2
node3

*在项目下的kubeadm-ansible/inventory/hosts文件默认配置的是3个master节点集群。

在node1中执行下面命令部署集群:

ansible-playbook -i inventory/hosts -e @inventory/vars cluster.yml

在cluster.yml中我们将集群的安装划分为6个阶段,分别为:

  • 安装预备
    • 安装前检查:检查系统,确认Yum库,下载cfssl。
    • Docker相关检查:检查Docker Engine,Configuration,Proxy。
  • Etcd安装
    • 生成Etcd证书
    • 安装Docker
    • 配置系统环境
  • kube-master:kube-node必须组件安装
    • kubelet
  • kube-master安装
    • 检查kubeadm
    • 生成证书
    • 修改配置
  • kube-node安装
    • 生成配置文件
    • kubeadm join
  • 其他组件安装
    • 配置flannel网络
    • 安装ingress-nginx
    • 安装dashboard
    • 安装heapster
    • 安装kube-lego

至此,集群部署到此结束,可以执行下面命令查看pod状态,都为Running状态则部署成功:

kubectl get po -n kube-system

如果部署失败,想要重置集群(所有数据),执行:

ansible-playbook -i inventory/hosts reset.yml

添加节点

如果想再添加一个节点,进入已有集群当中可以按下面步骤进行: 编辑kubeadm-ansible/inventory/hosts,将新节点信息添加进去。比如新节点hostname为node4,ip为192.168.56.14,其余信息与其他节点相同,那么进行如下添加信息:

[all]
node1 ansible_host=192.168.56.11 ip=192.168.56.11 ansible_user=root ansible_ssh_pass=vagrant ansible_become=true
node2 ansible_host=192.168.56.12 ip=192.168.56.12 ansible_user=root ansible_ssh_pass=vagrant ansible_become=true
node3 ansible_host=192.168.56.13 ip=192.168.56.13 ansible_user=root ansible_ssh_pass=vagrant ansible_become=true
node4 ansible_host=192.168.56.14 ip=192.168.56.14 ansible_user=root ansible_ssh_pass=vagrant ansible_become=true
[kube-master]
node1
[etcd]
node1
[kube-node]
node1
node2
node3
node4

节点信息添加完成后,就可以进行节点添加操作了:

ansible-playbook -i inventory/hosts -e @inventory/vars scale.yml

添加完成后查看节点信息:

kubectl get node

集群部署的介绍就到此结束了,下一篇我们将为大家介绍如何搭建第一个应用程序。

关于猪齿鱼

Choerodon 猪齿鱼是一个全场景效能平台,基于 Kubernetes 的容器编排和管理能力,整合 DevOps 工具链、微服务和移动应用框架,来帮助企业实现敏捷化的应用交付和自动化的运营管理的平台,同时提供 IoT、支付、数据、智能洞察、企业应用市场等业务组件,致力帮助企业聚焦于业务,加速数字化转型。

大家也可以通过以下社区途径了解猪齿鱼的最新动态、产品特性,以及参与社区贡献:

· 18 分钟阅读

我们了解到在微服务架构中,一个完整的单体应用被拆分成多个有着独立部署能力的业务服务,每个服务可以使用不同的编程语言,不同的存储介质,来保持最低限度的集中式管理。本篇将介绍Choerodon在搭建微服务网关时考虑的一些问题以及两种常见的微服务网关。

▌文章的主要内容包括:

  • 为什么要使用API Gateway
  • 两种Gateway 模式
  • Choerodon 的网关

对于Choerodon 而言,前端通过ReactJs实现,后端服务则通过Java,GoLang等多种语言实现。我们通过将后端拆分成许多个单独的业务服务,选择不同的语言切实地帮助我们来实现系统功能,这种面向服务的模式给我们带来了开发的便捷性,但是也带来了新的问题。服务之间如何做到相互通信,前端与后端又是如何进行通信的,是我们需要去解决的问题。

回到微服务架构的领域,如果要解决基本的通信问题,基本上只要解决下面三个问题就可以了。

  • 服务网络通信能力
  • 服务间的数据交互格式
  • 服务间如何相互发现与调用

除了这些基本的问题以外,因为整个Choerodon是一个分布式的系统,开始时看似清晰的服务拆分,实则杂乱无章,有时候完成一个业务逻辑需要到不同的服务区调取接口,这是一件很痛苦的事,同时我们又不得不面对分布式的一些问题。包括负载均衡,链路追踪,限流,熔断,链路加密,服务鉴权等等一大堆的问题。于是一个面向服务治理、服务编排的组件——微服务网关,是我们首要考虑的解决方案。

为什么要使用API Gateway

我们回到文章开始时的三个问题,我们来考虑如何解决服务间的通信。

为什么使用HTTP?

HTTP是互联网上应用最为广泛的一种网络协议,是一个客户端和服务器端请求和应答的标准(TCP),无论在哪种语言中几乎都有原生的支持,即使没有,也有第三方库来支持。通过HTTP 减少网络传输,来解决服务间网络的通信问题。

为什么选择JSON 作为数据交互格式?

因为 JSON 本身轻量、简洁,不管是编写,传输,还是解析都更加高效,而且相对来说,每个语言支持地都比较好。通过对JSON 的序列化和反序列化来实现网络请求中的数据交互。

为什么使用K8S 来进行服务发现?

对于负载均衡而言,业内已经有多种成熟的解决方案了,也大多是通过DNS 的方式去发现服务。不论是使用硬件F5 来解决,或者软件nginx,甚至Spring Cloud 也提供了对Eureka、Consul 等多种服务发现的支持。不过由于Choerodon 使用K8s 来作为服务编排引擎,基于K8s Client 来实现服务发现则更符合我们的切实需求。

当然对于Choerodon 而言,我们需要的不仅仅是一个简单的通信方式,而是一个完整的微服务解决方案。

API Gateway(API 网关)作为微服务体系里面的一部分,其需要解决的问题和 Choerodon 需要解决的问题非常类似。顾名思义,是企业 IT 在系统边界上提供给外部访问内部接口服务的统一入口。在微服务概念的流行之前,API网关的实体就已经诞生了,例如银行、证券等领域常见的前置机系统,它也是解决访问认证、报文转换、访问统计等问题的。

API 网关是一个服务器,是系统的唯一入口。从面向对象设计的角度看,它与 Facade 模式类似。API 网关封装了系统内部架构,为每个客户端提供一个定制的API。它可能还具有其它职责,如身份验证、监控、负载均衡、缓存、请求分片与管理、静态响应处理。

如果没有 API 网关,大家可能想到的一贯做法是通过前端客户端与后端服务直接通信。这样会存在以下一些问题:

  • 客户端直连各个服务,耦合度太高
  • 所有的服务接口都需要暴露给客户端,会存在安全隐患
  • 当服务增多时,后端服务对于客户端而言则是一个噩梦
  • 多次访问不同的服务,请求数量过多,影响效率
  • 权限、身份校验等需要每个服务都实现,重复造轮子

Choerodon 通过使用 Spring Cloud Zuul,将所有的后端都通过统一的网关接入微服务体系中,并在网关层处理所有的非业务功能,同时提供统一的REST/HTTP 方式对外提供API。

单节点Gateway 模式

所谓的单节点Gateway 模式,也就是提供一个单一的Gateway 来支持不同的客户端访问。

这种模式下,大家会使用一个自定义 API 网关服务面对多个不同客户端应用程序。其中最大的好处就是所有的请求都受统一网关的控制,实现简单。对于请求的身份认证、负载均衡、监控等都可以在统一的网关中实现。

伴随这一好处的同时,也会带来一定的风险。因为随着后端服务的增多,网关的API 将针对不同客户端发展,越来越多。同时,由于接口权限、身份验证等都在网关中实现,统一网关也会变得越来越庞大,类似于一个单独的应用程序或者单体应用。

除此之外,也会引入一个新的问题,即资源隔离的问题。假设后端的一个服务突然变慢,由于所有的请求都使用同一个网关入口,可能会将网关拖垮,进而影响到其他服务接口的访问。

要解决这个问题,有两种方式可以去解决,一种是做线程池的隔离,可以给一些重要的业务一些单独的线程池,不重要的业务再放到一个大的单独的线程池里面。另一种就是给不同的业务设置不同的网关。

Spring Cloud 可以通过修改 ZUUL 和 hystrix 的配置,将信号量隔离修改为线程池隔离,提高性能。

zuul:
ribbonIsolationStrategy: THREAD

hystrix:
command:
default:
execution:
isolation:
strategy: THREAD #hystrix隔离策略,默认为THREAD
thread:
timeoutInMilliseconds: 20000 #hystrix超时时间
threadpool:
default:
coreSize: 100 #并发执行的最大线程数
maximumSize: 5000 #最大线程池大小
allowMaximumSizeToDivergeFromCoreSize: true #允许maximumSize 配置生效
maxQueueSize: -1 #设置最大队列大小,为-1时,使用SynchronousQueue

线程池隔离仅仅做到了线程池的隔离,但是 CPU 和 Memory 之类资源的隔离其实并没有做。如果想要更加彻底的隔离方式,可以采用和线程池隔离类似的方式,给重要的服务用独立的网关来为其服务,不重要的服务,再给一个独立的网关来服务。这也就是多节点的Gateway 模式。

多节点Gateway 模式

多节点的Gateway 模式本身是一种BFF架构,即为不同的设备提供不同的API接口,引申而来,也可以按照不同的业务类型划分为多种业务场景下的网关。

上面这张图显示了一个简化版本的多API 网关。在这种情况下,每个API 的边界是基于BFF 模式,因此只提供每个客户端应用所有要的API。

这种模式带来的好处是根据不同颗粒度的API网关,在性能上能够做到更精确的控制。但是在服务网关中可以完成一系列的横切功能,例如权限校验、限流以及监控等,则需要在每个网关中重复实现。代码开发比较冗余。

可以说,这两种模式各有利弊,并不能单纯的比较其好坏,而应该根据实际的业务场景来选择适合自己的解决方案。

Choerodon 的网关

结合Choerodon 自身的核心业务,我们在不考虑多终端的情况下,最终选择了单一网关,并在此基础上,做了插件化的开发。

Choerodon 认为,一个网关应该包含两部分。

服务网关 = 路由转发 + 过滤器

路由转发:将外部的请求,转发到对应的微服务上 过滤器:包含一系列非功能的横切需求。例如权限校验、限流、监控等

我们在API 网关中保留了Spring Cloud Zuul 的路由转发,然后将权限校验等抽离到一个叫做gateway-helper 的服务中。如下图所示。

请求到达api-gateway 后,根据当前的HttpContext 上下文封装一个RibbonCommandContext 对象,该对象将包含了请求转发的gateway-helper 对应的信息。再由RibbonCommandFactory 根据RibbonCommandContext 对象生成一个RibbonCommand,由RibbonCommand 完成HTTP 请求的发送并的得到响应结果ClientHttpResponse。核心代码如下:

// GateWayHelperFilter.java
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws ServletException, IOException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse res = (HttpServletResponse) response;
RibbonCommandContext commandContext = buildCommandContext(req);
try (ClientHttpResponse clientHttpResponse = forward(commandContext)) {
if (clientHttpResponse.getStatusCode().is2xxSuccessful()) {
request.setAttribute(HEADER_JWT, clientHttpResponse.getHeaders().getFirst(HEADER_JWT));
chain.doFilter(request, res);
} else {
setGatewayHelperFailureResponse(clientHttpResponse, res);
}
} catch (ZuulException e) {
res.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
res.setCharacterEncoding("utf-8");
try (PrintWriter out = res.getWriter()) {
out.println(e.getMessage());
out.flush();
}
}
}

private RibbonCommandContext buildCommandContext(HttpServletRequest req) {
Boolean retryable = gatewayHelperProperties.isRetryable();
String verb = getVerb(req);
MultiValueMap<String, String> headers = buildZuulRequestHeaders(req);
MultiValueMap<String, String> params = buildZuulRequestQueryParams(req);
InputStream requestEntity;
long contentLength;
String requestService = gatewayHelperProperties.getServiceId();
requestEntity = new ByteArrayInputStream("".getBytes());
contentLength = 0L;
return new RibbonCommandContext(requestService, verb, req.getRequestURI(), retryable,
headers, params, requestEntity, this.requestCustomizers, contentLength);
}

private ClientHttpResponse forward(RibbonCommandContext context) throws ZuulException {
RibbonCommand command = this.ribbonCommandFactory.create(context);
try {
return command.execute();
} catch (HystrixRuntimeException ex) {
throw new ZuulException(ex, "Forwarding gateway helper error", 500, ex.getMessage());
}
}

可以看到,我们在api-gateway 服务中完成了对请求的首次转发。请求到达gateway-helper。在gateway-helper 中,针对配置进行判断,如果有自定义的helper,则会重定向到自定义的helper 上进行后续的处理。否则的话按照默认的逻辑进行权限校验。核心代码如下:

// RequestRootFilter.java
public boolean filter(final HttpServletRequest request) {
String uri = RequestRibbonForwardUtils.buildZuulRequestUri(request);
String service = RequestRibbonForwardUtils.getHelperServiceByUri(helperZuulRoutesProperties, uri);
if (StringUtils.isEmpty(service)) {
return requestPermissionFilter.permission(request) && requestRatelimitFilter.through(request);
}
return customGatewayHelperFilter(request, service, uri);
}

private boolean customGatewayHelperFilter(final HttpServletRequest request, final String service, final String uri) {
ClientHttpResponse clientHttpResponse = null;
try {
RibbonCommandContext commandContext = RequestRibbonForwardUtils.buildCommandContext(request, requestCustomizers, service, uri);
clientHttpResponse = RequestRibbonForwardUtils.forward(commandContext, ribbonCommandFactory);
return clientHttpResponse.getStatusCode().is2xxSuccessful();
} catch (Exception e) {
LOGGER.warn("error.customGatewayHelperFilter");
return false;
} finally {
if (clientHttpResponse != null) {
clientHttpResponse.close();
}
}
}

RequestPermissionFilter 中,我们对请求进行权限校验,来判断该用户是否有对应资源的操作权限。核心代码如下:

//RequestPermissionFilterImpl.java
public boolean permission(final HttpServletRequest request) {
if (!permissionProperties.isEnabled()) {
return true;
}
//如果是文件上传的url,以/zuul/开否,则去除了/zuul再进行校验权限
String requestURI = request.getRequestURI();
if (requestURI.startsWith(ZUUL_SERVLET_PATH)) {
requestURI = requestURI.substring(5, requestURI.length());
}
//skipPath直接返回true
for (String skipPath : permissionProperties.getSkipPaths()) {
if (matcher.match(skipPath, requestURI)) {
return true;
}
}
//如果获取不到该服务的路由信息,则不允许通过
ZuulRoute route = ZuulPathUtils.getRoute(requestURI, helperZuulRoutesProperties.getRoutes());
if (route == null) {
LOGGER.info("error.permissionVerifier.permission, can't find request service route, "
+ "request uri {}, zuulRoutes {}", request.getRequestURI(), helperZuulRoutesProperties.getRoutes());
return false;
}
String requestTruePath = ZuulPathUtils.getRequestTruePath(requestURI, route.getPath());
final RequestInfo requestInfo = new RequestInfo(requestURI, requestTruePath,
route.getServiceId(), request.getMethod());
final CustomUserDetails details = DetailsHelper.getUserDetails();
//如果是超级管理员用户,且接口非内部接口,则跳过权限校验
if (details != null && details.getAdmin() != null && details.getAdmin()) {
return passWithinPermissionBySql(requestInfo);
}
//判断是不是public接口获取loginAccess接口
if (passPublicOrLoginAccessPermissionByMap(requestInfo, details)
|| passPublicOrLoginAccessPermissionBySql(requestInfo, details)) {
return true;
}
if (details == null || details.getUserId() == null) {
LOGGER.info("error.permissionVerifier.permission, can't find userDetail {}", requestInfo);
return false;
}
//其他接口权限权限审查
if (passSourcePermission(requestInfo, details.getUserId())) {
return true;
}
LOGGER.info("error.permissionVerifier.permission when passSourcePermission {}", requestInfo);
return false;
}

通过上述的代码片段可以看到。在Choerodon 中,可以自主实现自己的geteway-helper,来对请求进行更复杂的控制。

Choerodon 支持在页面上对路由信息进行配置和修改,控制路由的动态调整。如下图所示。

以看到,通过页面对路由进行修改后,路由动态更新到 api-gateway及gateway-heler。通过配置中心实时生效,避免了修改代码重新部署带来的麻烦。

总结

回顾一下这篇文章,我们介绍了Choerodon 在搭建微服务网关时考虑的一些问题以及两种常见的微服务网关,并且通过代码介绍了Choerodon 的网关时如何实现的。这些都是我们实践过程中的一些做法和体会,希望大家可以结合自己的业务来参考。

关于猪齿鱼

Choerodon 猪齿鱼是一个全场景效能平台,基于 Kubernetes 的容器编排和管理能力,整合 DevOps 工具链、微服务和移动应用框架,来帮助企业实现敏捷化的应用交付和自动化的运营管理的平台,同时提供 IoT、支付、数据、智能洞察、企业应用市场等业务组件,致力帮助企业聚焦于业务,加速数字化转型。

大家也可以通过以下社区途径了解猪齿鱼的最新动态、产品特性,以及参与社区贡献:

· 25 分钟阅读

▌主要内容:

  • 瀑布流开发模式弊端
  • 敏捷需求管理
    • 如何获取需求
    • 如何管理需求
      • 史诗
      • 用户故事
    • 如何编写用户故事
    • 如何规划需求——故事地图
  • 总结

瀑布流开发模式弊端

在介绍敏捷之前先介绍一下瀑布流模式,这是产品开发中非常常见的一种管理模式,它以文档为驱动,在整个开发过程中,开发人员根据需求文档进行开发。所以在项目初期,确定需求和文档至关重要,通常在项目初期整个项目和产品便已经有了较为明确的雏形,项目成员需要严格按照需求文档和项目计划完成各自需要完成的工作。

由于在开发的中后期才会看到软件原型,早期只能通过文档来了解系统的模样,因此在一定的阶段,需求文档看起来会比产品实际的代码要重要。

这样的需求管理方式在传统软件系统管理中的优势在于可以在项目初期确定开发内容,更便于确定产品范围、人天预估、难度确认等,可以大大地减少项目失败的风险,同时项目成员也能通过详尽的文档来确认自己的职责和范围。但是这种管理方式在日新月异的产品开发中显得越来越笨重,在实践的过程中产生了以下难以解决的冲突和问题,比如:

  • 需求变更:需求变更是软件研发中经常遇到的一种情况,而在瀑布流的方式中,软件开发的后一阶段都是在前一阶段交付后才开始实施,流程多、周期长,这样的特点导致瀑布流在应对需求变更时需要付出很大的代价。

  • 质量管理:瀑布模式的测试阶段往往在大块功能开发完成后才会开始,在产品开发的时间线上都会比较晚,若测出来比较大的缺陷,可能导致产品无法准时上线。

  • 成员积极性:由于需求驱动性开发,开发人员容易形成“事不关己”的态度,机械性的等待设计人员或者前阶段的产出与设计,不会站在客户或者实际使用者的角度去进行功能开发,导致开发出的功能常常“很难用”,一旦形成返工修改的恶性循环,甚至导致项目延期,会进一步打击项目成员的积极性。

  • 过度开发:由于需求划定早,但是实际产出较晚,大概率出现实际上线的功能不符合预期或者花了大量时间在使用度并不高的功能上。导致过度开发的时间成本浪费。

  • 适应性:市场需求、客户需求等对于产品的实际预期大概率会产生变化,现在产品需求的灵活度、创新性和变化度变得越来越频繁,瀑布式的开发模式由于开发流程时间长的特点很难适应这种场景下的需求管理。

针对以上传统瀑布流开发模式的弊端,敏捷管理的思维应运而生。在下面的文章中,将会以需求的产生、管理和规划三个部分去详细描述敏捷团队如何来处理软件开发中的需求。

敏捷需求管理

如何获取需求

需求实际上分为业务需求和软件需求,业务需求由不一定熟悉IT的业务人员完成,来源很广,会脱离实际的实现方式,着重于功能的描述。而软件需求由开发人员根据业务需求编写,通过基于技术实现的角度对业务需求进行筛选、梳理、归纳,通过算法、架构、数据库等设计过程完成向软件需求的转化。

在此主要讨论业务需求如何获取,如果一个敏捷团队能够有现场客户,这当然是最理想的情况。但在大多数情况下,我们很难在实际操作中将敏捷团队与客户进行无缝结合,这时候,在敏捷团队中充当客户这个角色的人往往是敏捷团队的PO,PO最重要的职责就是与客户、实际使用者等“终端实际用户”进行交流,能够找到并制定软件系统的功能范围,充分了解和分析需求,找到并确定软件系统真正有用的需求,并将其转化为系统的业务需求,即后续的用户故事。

一个优秀的PO可以做到如下工作内容:

  • 在产品积压列表(Backlog)中创建容易理解的、可行的故事。
  • 根据优先级调整 Backlog 中故事的完成顺序,以最优的计划实现设定的目标。
  • 优化并最大化scrum团队的输出。
  • 通过尽可能梳理清楚团队内外所有相关的事项,以避免混乱。
  • 帮助团队对后续的迭代进行规划。
  • 为产品验收定义标准和关键指标

PO在规划产品需求的过程中,要对产品的商业价值有足够的理解,只有这样,才能把产品分解成小的独立交付价值体,同时也能够对产品待办列表进行优化和价值排序,以便团队能够始终将工作投入在价值最高的产品功能上。

如何管理需求

在敏捷管理中,产品需求同样也是非常重要的环节和组成部分,团队的所有活动都会基于需求展开,由产品负责人维护和排列各自的优先级。在敏捷管理体系中,我们将需求映射为用户故事,同时使用史诗对需求进行更粗粒度的划分和规划。

史诗

在项目开始,我们在定义一个产品的粗略范围的时候如果直接以用户故事作为唯一的细粒度标准进行划分的话,会非常混乱,因为一个系统往往非常复杂,直接对一整个系统进行分析拆分其实是很困难的。所以针对这种情况,猪齿鱼敏捷管理引入了史诗的概念。

史诗是有一个共同目标的大量工作,类似但是区别于系统的“模块”概念,通过定义史诗来划分产品的功能线,再基于史诗来详细讨论相关的用户故事,史诗通常需要多个冲刺才能完成。同时,史诗会随着时间的推移变化,并不是在产品规划的初期就能确定史诗的规模大小,他会通过一系列冲刺对范围进行修改以适应需求的变化,随着团队通过开发和客户反馈,团队成员会不断添加和删除史诗相关的用户故事,以优化团队的发布时间。

用户故事

用户故事(User Story)是指用户通过系统完成的一个有价值的目标,用户故事只是描述系统的外在行为,完全忽略系统的内部动作。好的用户故事应该包括角色、功能和商业价值三个要素。一个完整的用户故事可以涵盖以下问题:

  • 实际使用的角色是谁?——期望用户的类型
  • 具体要使用的功能是什么?——我希望功能
  • 完成这个功能的原因是什么?——产生的价值 和好处

因此,一个用户故事的经典格式为:作为一个角色 , 我想要功能 , 以便于功能价值。

如何编写用户故事

用户故事的定义必须是从真正业务用户的角度,不可以是“技术用户(开发者本身)故事”,在编写用户故事之前,应该清楚地了解创建用户故事的用户是谁,所以,做一个适当的用户调查,区分所有的用户角色并根据用户角色对故事进行区分是非常必要的。通常,客户代表(如产品所有者)负责用户故事。尽管如此,用户故事并不是高层给团队的规范,而是产品所有者和团队之间的协作技术,这就是为什么如果用户故事是合作编写的更好。一个好的方法是成立一个故事编写研讨会,由团队合作编写。

为了构造好的用户故事,我们关注以下两个原则:NVEST 原则和 3C原则

INVEST 原则

  • Independent 相互独立的:用户故事之间应该是相互独立的,相互关联依赖的故事会导致故事的优先级和计划编排变得很困难,复杂的依赖更会导致故事的难以预估和控制。通常我们会通过组合用户故事、分隔用户故事的手段去减少用户故事之间的依赖性。用户故事的独立性越高,越能根据实际情况去确立故事的优先级以及预估故事完成的可能性。

  • Negotiable 便于协商的:用户故事包含了一个需求的简短描述,而详情是通过相关人员(客户、需求人员、开发人员等)之间通过讨论阶段来完成,如果用户故事非常详细会导致故事的可讨论、可协商性大大下降,不便于更好地沟通及协商,也就不可能划分出既让客户满意,也能让开发认同的好的用户故事。

  • Valuable 有价值的:用户故事一定是站在客户和使用者的角度去思考和编写的,描述的是产品的一个一个功能,而不是实际实现的任务。

  • Estimable 能估算的:功能的实现者需要去评估一个用户故事,来确定他的规划。估算一个用户故事需要一定的领域知识,同时太大的故事也会导致估算的不准确。

  • Small 适量小的:故事在评估时在工作量上要小一些,以不超过一个迭代周期为宜(我们实践中1-3天是一个比较合适的量),短小的用户故事可以减少划分过程中估算的误差,否则很容易在划分范围和估计时出现很多错误。

  • Testable 可验证的: 这点用于确定用户故事是否完成,如果一个故事无法进行测试,那我们就无法确定这个用户故事是否完成了,在划分故事时,最好确定一个故事的验收标准,这一点很重要。

3C原则

  • Card 卡片:作为用户故事的书面描述,不会包含详细故事细节,更多的是一个提醒,一个必须跟进后续沟通的承诺。

  • Conversation 交谈:与产品负责人和客户的交谈中获取故事背后的细节,可以通过一些文档进行补充。

  • Confirmation 确认:使用验收测试来确认故事是否完成,是否符合工作要求。

在这里需要注意把握需求文档与敏捷故事的平衡点。需求文档属于产品级的文档,不论一个软件系统经过多少次的重构开发,它的核心功能是不会有太大的变化的,这类描述产品的需求文档是项目中不可缺少的一部分。而用户故事则是项目级的功能描述,类似于做完就会扔掉的“抛弃型”需求。很多企业和团队在实践敏捷流程时,对于是否需要编写需求文档经常产生疑问,在实际的操作中,可能部分强调快速交付,或者生命周期很短、业务模式高度可变的互联网、网游等项目,是可以减少甚至放弃需求文档而全部使用故事来管理需求,但是现阶段要求企业的IT项目全面接纳需求完全无文档化其实并不是很现实。

在这里我们认为更合适的方式是找到需求文档与敏捷故事之间的一个平衡点,理性的辨别两者的区别,一份需求用例加上用户故事,然后驱动开发这种方式可能更适合大型企业的敏捷需求实践模式。

如何规划需求——故事地图

在上文,我们已经确定了软件系统的开发范围,找出了需要实现的业务需求,同时根据用户故事的编写原则生成大量对应的用户故事,并且在迭代规划中按照优先级挑选出迭代需要完成的用户故事,将这些故事放入了下图的待办事项(Backlog)中。

但是我们如何对这些需求进行规划管理?如何将待办事项(Backlog)的故事列表在时间轴上进行编排呢?

为了更好地对用户故事进行规划,敏捷中使用 故事地图(User map)作为故事管理方式。故事地图会将产品的backlog从简单的列表模式变为一张二维地图,从而能容易地看到整个产品规划的全貌,不仅能帮助业务人员整理产品需求,协助开发人员更快速便捷地了解客户的需求,同时还能够确定产品模块的实现优先级,实现最大用户价值。

在猪齿鱼敏捷管理系统中,我们将史诗、用户故事、发布版本、冲刺迭代紧密结合,以史诗为主轴,用户故事为核心元素,实现了发布版本和迭代冲刺两个维度的产品需求规划,更加直观、快捷地将用户故事也其他的相关任务进行编排规划。

通过选择故事地图的泳道类型,我们将故事地图区分为冲刺维度和版本维度两种编排方式。

冲刺维度

  • 以史诗作为地图的横轴坐标,将地图中的用户故事根据史诗进行分类。
  • 冲刺迭代作为地图的纵轴子tab页,可以将地图中的用户故事放入对应的迭代。
  • 未规划区的故事是指已经分配了史诗,但是没有编入任意一个冲刺迭代的用户故事。
  • 需求池是指未完成的,并且未分配史诗、也未编入任意一个冲刺迭代的用户故事。

版本维度

  • 同样也是以史诗作为地图的横轴坐标,将地图中的用户故事根据史诗进行分类。
  • 软件的发布版本作为地图的纵轴子tab页,可以将地图中的用户故事放入对应的版本。
  • 未规划区的故事是指已经分配了史诗,但是没有编入任意一个发布版本的用户故事。
  • 需求池是指未完成的,并且未分配史诗、也未编入任意一个发布版本的用户故事。

通过在故事地图中对用户故事进行操作,可以对用户故事的史诗、迭代、版本进行直观的编排,完成用户故事的规划过程。

总结

在软件系统的开发中,不论是瀑布流模式还是敏捷模式,需求管理都是开发过程中最重要的环节之一,细致深入的项目需求和有效的需求工程管理可以让项目成员所做的工作尽可能地明晰,每一分资源的投入都尽量保证有效地产出。开发者们花费了大量的精力开发的功能由于需求调研的失误发现最后并没有用户去用,或者在大量的开发之后业务人员一句没用便推倒了所有的设计,这些情况是项目管理中是十分常见和极为头疼的情况,不仅打击团队成员的积极性,激化产品和开发的矛盾,还会浪费大量的资源,导致返工频繁、质量低下等结果。

需求管理在敏捷中并不是在项目开始就全部盖棺定论的,在项目期初进行的是大致需求和核心功能的讨论确定,但是后续实现的细节会在每次迭代完成后尽早的反馈给客户,由实际的使用用户来讨论鉴定他们真正的需求,然后根据反馈的情况和效果进行针对性地修改,尽可能做到不合理的需求尽早发现修改,减少返工的成本,同时保证最终项目产出的结果是最为贴合用户需求的产品。

当然这种管理模式也会有一些弊端,比如在实际的项目管控中需要在项目开启时便完成人天的预估,大量的需求变更会导致项目时间的不可控。其实这个问题反过来看,如果真的有如此之多的需求变更,那按照原先确定的需求执着地进行开发可能导致的后果会更加严重。所以在实际项目的中,如何让需求管理也变得敏捷不是单纯地用敏捷理论来生搬硬套,我们要切实的明白敏捷的核心理念,然后贴合实际项目的情况来做出对应的变化。让敏捷管理真的能让我们的项目敏捷起来,让客户、产品、开发者在敏捷中产生享受的快感。

关于猪齿鱼

Choerodon 猪齿鱼是一个全场景效能平台,基于 Kubernetes 的容器编排和管理能力,整合 DevOps 工具链、微服务和移动应用框架,来帮助企业实现敏捷化的应用交付和自动化的运营管理的平台,同时提供 IoT、支付、数据、智能洞察、企业应用市场等业务组件,致力帮助企业聚焦于业务,加速数字化转型。

大家也可以通过以下社区途径了解猪齿鱼的最新动态、产品特性,以及参与社区贡献:

· 14 分钟阅读

Choerodon猪齿鱼全场景效能平台,是基于Kubernetes,Istio,knative,Gitlab,Spring Cloud来实现本地和云端环境的集成,实现企业多云/混合云应用环境的一致性。平台通过提供精益敏捷、持续交付、容器环境、微服务、DevOps等能力来帮助组织团队来完成软件的生命周期管理,从而更快、更频繁地交付更稳定的软件。

2018年10月15日,Choerodon猪齿鱼发布0.10版本,本次更新对知识管理、敏捷管理、持续交付等各项服务增加了新功能,并对一些功能细节做了进一步优化,欢迎各位更新体验,同时特别感谢社区中的朋友给Choerodon猪齿鱼提出的诸多中肯意见。

  • 发布版本:0.10
  • 发布时间:2018年10月15日
  • 功能范围:知识管理、敏捷管理、持续交付、测试管理以及微服务开发框架

下面就为大家带来详细的版本更新介绍。

新增功能

1.知识管理

  • 新增在编辑器中添加敏捷issue的宏
  • 新增了wiki系统中产品需求的模板
  • 新增了wiki系统的最近空间功能

同时,新增了手动重试功能,当同步组织、项目失败或者创建空间失败时可手动重试。

2.敏捷管理

敏捷管理服务新增了史诗燃耗图、版本燃耗图两个报告,并且新增了迭代工作台和报告工作台,详情如下:

  • 史诗燃耗图:展示了团队在不同史诗中取得的工作进展,并对未来冲刺完成趋势进行预估。

  • 版本燃耗图:展示了团队在不同版本中取得的工作进展,并对未来冲刺完成趋势进行预估。

  • 冲刺工作台:用户可以在冲刺工作台中查看问题的状态、优先级、经办人、类型分布,冲刺的简要信息、燃尽图、问题列表详情。

  • 报告工作台:用户可以在报告工作台中查看累积流量图、经办人分布图等图表的实时数据。

  • 新增故事地图导出图片功能

  • 新增故事地图全屏操作功能

3.持续交付

持续交付本次主要新增了DevOps报表功能,支持查看代码提交、应用构建以及应用部署的情况:

  • 代码提交图:跟踪项目团队与个人的代码提交情况,可以了解团队的整体效率与个人效率。

  • 构建次数图:从应用的维度展示某个应用的构建次数,构建成功次数以及构建成功率,可帮助团队快速了解到该应用的构建情况。

  • 构建时长图:从应用的维度展示所选时间段某个应用每一次构建的时长,更直观地了解应用构建的效率。

  • 部署次数图:展示了项目各个环境中各个应用的部署频率,了解团队的部署频次与整体的效率。

  • 部署时长图:展示该项目下某一环境中各个应用部署时长等部署相关的信息,了解各个应用的部署情况。

  • 新增部署总览功能,可以查看所有应用在各个环境下的部署情况,并能在此页面完成应用最新版本的快速部署。

  • 支持使用shell命令操作pod以便于调试

  • 新增环境分组,支持按照环境分组查看流水线

  • 新增域名证书管理功能,支持域名证书的申请与上传

  • 支持创建标记时填写release notes,并支持查看编辑与修改

  • 新增查看容器日志时的Stop Following、Go Top功能,并支持全屏查看容器日志

  • 新增容器界面,选择环境与应用的下拉框和应用版本界面选择应用的下拉框便于搜索与过滤

4.测试管理

测试管理此次主要增加以下几个功能:

  • 增加了Oracle数据库支持

  • 增加了测试计划功能

  • 测试用例管理添加文件夹层级

  • 用例管理侧边栏具有宽窄两种展示

  • 新增测试计划页面

  • 测试执行增加用户筛选

5.微服务开发框架

微服务开发框架增加了如下的功能:

  • 新增发送设置,平台管理员和组织管理员可以设置发送的模板样式及规则

  • 新增站内信模板,平台管理员可定义发送给用户的站内信内容

  • 新增消息记录,平台管理员和组织管理员可以查看邮件发送的记录

  • 新增任务明细,开发者可以创建和管理定时任务,包括简单任务和cron任务

  • 新增执行记录,开发者可以查看定时任务的执行记录

  • 新增可执行程序,开发者可以查看定时任务可调用的程序(类)

  • 新增消息通知,用户可以接收到站内信的消息通知

  • 新增角色分配批量导入,平台管理员、组织管理员和项目管理员可以下载导入模板,填写后上传文件批量导入用户角色信息

  • 新增找回密码,登录界面支持忘记密码时进行邮箱验证重置密码

功能优化

1.知识管理

  • 修改wiki系统树状浏览器样式
  • 修改了wiki系统编辑器的布局
  • 修改了wiki系统部署中初始化配置的模式
  • 修改了wiki系统页面导出的布局和子预览页布局
  • 修改了查看源码页面布局
  • 修改了复制页面、重命名页、删除页面、邮件分享页的布局
  • 修改了知识管理服务支持oracle数据库

2.敏捷管理

  • 优化了故事地图的滑动功能,使其更加流畅
  • 故事地图在移动问题时可以记录其位置
  • 故事地图在需求池拖动问题的时候可以记录其位置
  • 部分页面内存优化
  • 报告中的燃尽图、冲刺报告可以建立缓存保留上一次选择的冲刺及其单位
  • 修改了看板配置中添加状态的样式
  • 优化累积流量图获取时间函数
  • 仪表盘中的版本进度过滤掉归档版本
  • 设置中创建问题链接增加了重名校验
  • 故事地图各种泳道支持上下拖动排序

3.持续交付

  • 优化了容器日志长链接的状态
  • 统一了系统内状态显示的图标
  • 优化了网络、域名、实例、应用部署等相关状态
  • 优化了总览界面的加载速度
  • 优化了表格的分页、筛选、排序和刷新

4.测试管理

  • 测试循环和测试阶段的开始和结束时间成为必选项
  • 测试循环下将不再直接含有测试执行
  • 测试计划页面执行的跳转只能查看执行信息,不能进行编辑
  • 用例创建时,版本成为必选项,文件夹成为可选项
  • 原测试循环界面更改为测试执行界面
  • 报表去除选取用例的功能
  • 修改报表数据源选择操作,当前只显示拥有测试关联的数据
  • 报表的执行跳转,无法对执行进行编辑

5.微服务开发框架

  • 仪表盘配置优化为可在界面上控制哪些角色可见
  • 邮件模板创建时优化为可添加网络图片,并且支持HTML编码
  • API 测试修改为内部接口不能在页面进行测试
  • 修改密码优化为LADP用户不能修改密码

缺陷修复

1.知识管理

  • 修复了Wiki系统的登录授权时长与Choerodon不一致的问题
  • 修复了wiki系统中部分页面中英文混杂的问题
  • 修复wiki管理界面删除空间之后,没办法创建同名空间的问题

2.敏捷管理

  • 修复了活跃冲刺中拖动到有多种状态的一列处出现的页面堆叠现象
  • 修复了活跃冲刺界面拖动问题排序,页面数据延迟问题
  • 修复了待办事项创建问题执行时间过长的问题
  • 修复了问题管理导出Excel中问题的描述带有格式的问题
  • 修复了冲刺燃尽图根据问题数量统计在一部分条件下加载失败

3.持续交付

  • 修复部署超时失败后无法操作的问题
  • 修复部署部分替换实例未做修改判断的问题
  • 修复0.9.0版本网络列表提示错误、时间组件显示错误的问题
  • 修复网络编辑中实例状态报错的问题
  • 修复创建应用时,删掉应用模板里面的commit的问题
  • 修复创建同名项目时,gitlab组创建失败的问题
  • 修复页面连接文档的icon不统一的问题
  • 修复gitops解释逻辑,list存放重复旧文件对象关系导致删除重复数据报错的问题
  • 修复网络创建中添加端口按钮消失的问题

4.测试管理

  • 修复table内编辑的保存操作
  • 修复一些样式问题
  • 修复拖动table没有搜索到数据的样式问题

5.微服务开发框架

  • 修复API测试加载缓慢的问题
  • 修复新导入的ldap用户报错的问题
  • 修复密码策略校验的问题
  • 修复平台角色分配过滤的问题
  • 修复IE兼容性问题

删除

1.敏捷管理

  • 分页功能中若展示条目低于10条,则不显示分页工具栏

2.持续交付

  • 移除部署流水线实例管理中的多应用视图

更加详细的内容,请参阅Release Notes官网

欢迎通过我们的GitHub猪齿鱼社区进行反馈与贡献,帮助Choerodon猪齿鱼不断成长,我们将持续迭代优化,敬请期待。

· 28 分钟阅读

Choerodon猪齿鱼平台是一个PaaS平台,其本身不提供应用系统的运行环境,用户需要自主安装Kubernetes集群,一般来说一个应用系统需要有开发环境、测试环境和正式环境(如下图所示),每一个环境都是一个独立的Kubernetes集群。当然用户也可以根据具体的需求来调整,例如开发环境和测试环境共用一套Kubernetes集群。

所以,利用Choerodon猪齿鱼PaaS能力的第一项任务就是 搭建应用系统的运行环境。

准备服务器

Choerodon猪齿鱼支持本地化部署,也支持公用云部署。Kubernetes集群的硬件要求与应用系统的要求一致,当然考虑到应用系统已经完全容器化,所以用户可以根据自身需求动态定制Kubernetes集群的规模(使用公有云可以非常方便的做到这一点)。

以下表格是一个最低配置要求。

系统配置数量用途说明
CentOS7.2+2Core 4G内存 512G硬盘1NFS文件服务器
CentOS7.2+4Core 16G内存 64G硬盘3k8s集群根据自身应用规模增加或减少节点个数
另外,服务器网络需能连接外网,能够与Choerodon猪齿鱼系统连接。还有,操作系统要求是Centos 7.2及以上版本。

安装Kubernetes集群

Kubernetes集群的安装采用标准安装方式即可,Choerodon猪齿鱼整理了详细方便的Kubernetes安装文档,可供参考。用户可以根据自身需求选择。

1.Choerodon猪齿鱼整理了详细方便的Kubernetes安装文档。

2.在安装时,请详细阅读安装文档前部分,其中涉及到前置要求与约定、防火墙及端口检测、同步服务器时区和同步服务器时间等。

数据库迁移

如果原有环境没有数据,则可以忽略本节。

可根据情况选择以下两种方式:

  • 如果新的集群能够连通原有数据库,则可以选择使用原有的数据库连接信息而无需对数据库作任何处理。

  • 如果不能连通原有数据库,考虑备份数据库配置、数据等关键数据。

具体操作如下:

  1. 确定需要备份的数据,如配置,数据等,具体内存请根据实际数据库进行调整。
  2. 部署新数据库到新集群中。建议使用helm部署,具体操作可以参照mysql部署。
  3. 使用数据库自带工具恢复数据到新数据库。
  4. 检查数据库是否正常运行。

应用迁移

应用迁移主要是将应用系统的代码迁移到Choerodon猪齿鱼中,并通过Choerodon猪齿鱼的开发流水线、部署流水线等进行应用系统的开发和部署等工作。应用迁移主要包括Choerodon猪齿鱼系统的创建项目、创建应用、改造原代码库、将原代码库迁移到Choerodon、生成新的应用版本、创建应用系统环境、部署版本、创建网络、创建域名和测试访问等步骤。请用户按照此步骤顺序进行。

如果是SaaS版本的用户需要先申请开通组织。

创建项目

项目是最小粒度的管理层次。在组织下创建项目,则创建的项目属于这个组织。

关于Choerodon猪齿鱼中项目的详细信息,以及相关操作等可以参考项目管理

1.使用“组织管理员”角色登录系统

2.点击组织,例如“汉得研发”,进入到组织层的管理菜单。

3.在组织层的管理菜单中,点击:“汉得研发” -> 左上角的菜单 -> “组织设置” -> “项目管理”。进入到项目管理与创建功能界面

4.在“项目管理”界面,单击“创建项目”,在弹出的窗口中填写项目编码、项目名称。

例如,

  • 项目编码:hand-rdc-halm
  • 项目名称:汉得资产云平台

5.点击“创建”,即可创建完成。项目创建完成之后,用户就可以使用Choerodon猪齿鱼的系统功能,例如知识管理、敏捷管理、开发流水线、测试管理、部署流水线等。

创建应用

应用是满足用户某些需求的程序代码的集合,一个应用可以是一个单体应用,也可以是微服务系统的一个服务。服务端相关应用,例如Java、Python、C/C++/C#、Go、.NET等应用,以及前端相关应用,例如ReactJs、VueJs、AngularJs等等,理论上讲没有什么限制。

关于如何创建应用,以及相关操作和信息等,可以参考Choerodon官网的应用管理

1.切换到项目层,例如“汉得资产云平台”。

2.在组织层的管理菜单中,点击:“汉得资产云平台” -> 左上角的菜单 -> “应用管理” -> “应用”,进入到应用创建功能界面。

3.在创建应用的弹框中填写应用编码、应用名称和选择“应用模板”。

例如,

  • 编码:halm-dev
  • 名称:资产云应用
  • 选择应用模板:MicroService

关于Choerodon的应用模板,可以参考应用模板。如果是迁移原库的代码,则随便选择一个即可。

4.创建完成应用之后,Choerodon会在Gitlab中创建相关的代码库。 例如:https://code.choerodon.com.cn/hand-rdc-halm/halm-dev

强烈建议不要直接在Gitlab中操作代码库,Choerodon已经封装了对Gitlab库的增删改查等操作,例如创建库、创建分支、删除分支、合并代码等,所以这些操作尽量在Choerodon上进行操作。

应用容器化配置

Choerodon猪齿鱼秉承云原生的理念,基于平台的应用需要进行容器化改造才能够使用Choerodon进行开发和部署。在本节中将给大家介绍Choerodon容器化的一些概念、如何构建应用的基础镜像,以及为原代码库增加相关的配置使其满足Choerodon容器化要求。

应用的容器化配置是整个迁移过程中最难的部分,在此需要熟悉、掌握Kubernetes、Helm等。

Choerodon猪齿鱼应用容器化概念

在Choerodon猪齿鱼中,使用Helm管理Kubernetes包等,Helm之于Kubernetes好比yum之于RHEL,或者apt-get之于Ubuntu。Helm使用Charts管理应用,Charts就好像RPM一样,里面描述了应用及其依赖关系。

所以, 在Choerodon的标准应用代码结构中一定要包含charts文件夹,如下截图,这是一个后端项目的标准结构。

  • templates为模板文件,将模板文件渲染成实际文件,然后发送给Kubernetes。
  • values.yaml为模板的预定义变量。
  • Chart.yaml包含 chart 的版本信息说明,您可以从模板中访问它。
  • deployment.yaml:创建 Kubernetes 部署的基本清单。
  • service.yaml:为您的部署创建服务端点的基本清单。
  • _helpers.tpl:放置模板助手的地方,您可以在整个 chart 中重复使用

构建应用基础镜像

什么是应用基础镜像?先来看一张图,一般在应用基础镜像中预先安装了工具类、依赖包、系统基础一致性设置等应用程序构建、测试、运行等相关的基础依赖工具和系统配置。

资产云应用为PHP项目,那么应用基础镜像中就应该为PHP运行环境,首先去DockerHub上搜索是否有官方提供的公共镜像,可以对官方提供的公共镜像做进一步定制,生成需要的镜像,也可以从一个基础的只有系统的镜像进行定制。

具体的步骤如下:

1.编写Dockerfile定制基础镜像时,尽量将镜像大小往小的做,镜像层数往少的写,仅添加应用运行时必须的相关组件,不要添加不必要的东西进入。在项目根目录下新建名为Dockerfile.base文件,在文件中写入Dockerfile定义的信息,例如:

# 以ubuntu作为系统
FROM ubuntu:16.04
# 设置环境变量
ENV NODE_HOME=/usr/local/node8
ENV PATH=$NODE_HOME/bin:$PATH
ENV COMPOSER_ALLOW_SUPERUSER=1
ENV COMPOSER_HOME=/composer
ENV USER=root
ENV SASS_BINARY_PATH=/opt/linux-x64-57_binding.node

# 添加源并安装所需要的软件
RUN echo "deb-src http://archive.ubuntu.com/ubuntu xenial main restricted #Added by software-properties" > /etc/apt/sources.list \
&& echo "deb http://mirrors.aliyun.com/ubuntu/ xenial main restricted" >> /etc/apt/sources.list \
&& echo "deb-src http://mirrors.aliyun.com/ubuntu/ xenial main restricted multiverse universe #Added by software-properties" >> /etc/apt/sources.list \
&& echo "deb http://mirrors.aliyun.com/ubuntu/ xenial-updates main restricted" >> /etc/apt/sources.list \
&& echo "deb-src http://mirrors.aliyun.com/ubuntu/ xenial-updates main restricted multiverse universe #Added by software-properties" >> /etc/apt/sources.list \
&& echo "deb http://mirrors.aliyun.com/ubuntu/ xenial universe" >> /etc/apt/sources.list \
&& echo "deb http://mirrors.aliyun.com/ubuntu/ xenial-updates universe" >> /etc/apt/sources.list \
&& echo "deb http://mirrors.aliyun.com/ubuntu/ xenial multiverse" >> /etc/apt/sources.list \
&& echo "deb http://mirrors.aliyun.com/ubuntu/ xenial-updates multiverse" >> /etc/apt/sources.list \
&& echo "deb http://mirrors.aliyun.com/ubuntu/ xenial-backports main restricted universe multiverse" >> /etc/apt/sources.list \
&& echo "deb-src http://mirrors.aliyun.com/ubuntu/ xenial-backports main restricted universe multiverse #Added by software-properties" >> /etc/apt/sources.list \
&& echo "deb http://archive.canonical.com/ubuntu xenial partner" >> /etc/apt/sources.list \
&& echo "deb-src http://archive.canonical.com/ubuntu xenial partner" >> /etc/apt/sources.list \
&& echo "deb http://mirrors.aliyun.com/ubuntu/ xenial-security main restricted" >> /etc/apt/sources.list \
&& echo "deb-src http://mirrors.aliyun.com/ubuntu/ xenial-security main restricted multiverse universe #Added by software-properties" >> /etc/apt/sources.list \
&& echo "deb http://mirrors.aliyun.com/ubuntu/ xenial-security universe" >> /etc/apt/sources.list \
&& echo "deb http://mirrors.aliyun.com/ubuntu/ xenial-security multiverse" >> /etc/apt/sources.list \

&& apt-get update \
&& apt-get install -y gcc g++ make xz-utils axel libsass-dev libmcrypt-dev curl supervisor rpm libaio1 libaio-dev \
&& apt-get install -y nginx \
&& apt-get install -y php-fpm php-pdo-mysql php-pdo-sqlite php-curl php-redis php-mongodb \
php-gd php-mcrypt php-mbstring php-xml php-ldap php-imap php-zip php-dom php-soap php-dev phpunit \

&& apt-get autoclean \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/* \
&& echo "daemon off;" >> /etc/nginx/nginx.conf \
&& sed -i "s/^user\swww-data;/user root;/g" /etc/nginx/nginx.conf \
&& sed -i "s/^user\s=\swww-data/user = root/g" /etc/php/7.0/fpm/pool.d/www.conf \
&& sed -i "s/^group\s=\swww-data/group = root/g" /etc/php/7.0/fpm/pool.d/www.conf \
&& sed -i "s/^;daemonize\s*=\s*yes/daemonize = no/g" /etc/php/7.0/fpm/php-fpm.conf \
&& sed -i "s/^;mbstring\.internal_encoding\s*=.*$/mbstring\.internal_encoding = UTF-8/g" /etc/php/7.0/fpm/php.ini \
&& sed -i "s/^;mbstring\.internal_encoding\s*=.*$/mbstring\.internal_encoding = UTF-8/g" /etc/php/7.0/cli/php.ini \
&& echo "[program:nginx]\ncommand=/usr/sbin/nginx" >> /etc/supervisor/conf.d/nginx.conf \
&& echo "[program:php-fpm7.0]\ncommand=/usr/sbin/php-fpm7.0 --nodaemonize --fpm-config /etc/php/7.0/fpm/php-fpm.conf -R" >> /etc/supervisor/conf.d/php.conf \
&& mkdir /run/php \

&& sed -i "s/\[supervisord\]/[supervisord]\nnodaemon=true\nuser=root\n/g" /etc/supervisor/supervisord.conf \

&& axel -n 20 https://getcomposer.org/download/1.4.2/composer.phar \
&& mv composer.phar /usr/local/bin/composer \
&& chmod +x /usr/local/bin/composer \
&& composer config -g repo.packagist composer https://packagist.phpcomposer.com \

&& cd /usr/local \
&& axel -n 10 https://nodejs.org/dist/v8.2.1/node-v8.2.1-linux-x64.tar.xz \
&& xz -d node-v8.2.1-linux-x64.tar.xz \
&& tar xvf node-v8.2.1-linux-x64.tar \
&& unlink /usr/local/node-v8.2.1-linux-x64.tar \
&& mv node* node8 \
&& chown -R root:root node8 \
&& npm install cnpm -g --registry=https://registry.npm.taobao.org \
&& npm install nrm -g -registry=https://registry.npm.taobao.org \
&& nrm use taobao
# 暴露端口
EXPOSE 80 443
# 设置默认启动命令
CMD ["/usr/bin/supervisord", "-c", "/etc/supervisor/supervisord.conf"]

2.在项目根目录下执行命令进行应用基础镜像构建,构建好之后推送镜像到镜像仓库,例如registry.choerodon.com.cn/hand-rdc-halm仓库下(注意:用户也可以根据自身具体的需求,选择镜像库地址,例如DockerHub等),这个仓库在Choerodon创建项目时会自动创建:

docker build -t registry.choerodon.com.cn/hand-rdc-halm/php:ubuntu-16.04 -f Dockerfile.base .

3.将构建好的镜像推送到镜像仓库中:

docker login registry.choerodon.com.cn
docker push -t registry.choerodon.com.cn/hand-rdc-halm/php:ubuntu-16.04

在原来代码库中增加Dockerfile、和Helm Chart

1.Dockerfile

在有应用基础镜像的基础上编写应用的Dockerfile那就很容易了,只需要将程序放入基础镜像特定的目录,设置好镜像运行前置处理和启动命令即可。

本事例中,应用运行时需链接数据库,但数据库相关信息是运行时才会知道的,那么解决方式是将这些信息配置为环境变量,镜像运行时从环境变量中获取这些信息并替换到对应的配置文件中去。

第一步:修改配置文件并编写启动脚本

在项目根目录下新建auto_devops文件夹,将配置文件拷贝至该文件夹下,下面为配置文件中数据库配置片段,将会改变的值全部使用大写的字母进行替换,替换时需保证在此文件中唯一。

'dbconfig' =>
array(
'db_host_name' => 'DB_HOST_NAME',
'db_host_instance' => 'SQLEXPRESS',
'db_user_name' => 'DB_USER_NAME',
'db_password' => 'DB_PASSWORD',
'db_name' => 'DB_NAME',
'db_type' => 'mysql',
'db_port' => 'DB_PORT',
'db_manager' => 'MysqliManager'
),

然后在auto_devops文件夹新建docker-entrypoint.sh文件编写启动脚本,这个脚本将替换配置文件中大写的那些变量名。

#!/bin/bash

sed -i "s DB_HOST_NAME $DB_HOST_NAME g" /var/www/config.php
sed -i "s DB_USER_NAME $DB_USER_NAME g" /var/www/config.php
sed -i "s DB_PASSWORD $DB_PASSWORD g" /var/www/config.php
sed -i "s DB_NAME $DB_NAME g" /var/www/config.php
sed -i "s DB_PORT $DB_PORT g" /var/www/config.php

exec "$@"

第二步:编写应用Dockerfile 在项目根目录下新建名为Dockerfile的文件

# 应用基础镜像
FROM registry.choerodon.com.cn/hand-rdc-halm/php:ubuntu-16.04
# 将程序复制到/var/www/目录中
COPY . /var/www/
# 将配置文件复制到对应目录中
COPY ./auto_devops/config.php /var/www/config.php
# 将替换环境变量的脚本复制到镜像中
COPY ./auto_devops/docker-entrypoint.sh /docker-entrypoint.sh
# 默认运行镜像时执行的命令
ENTRYPOINT ["/bin/sh", "/docker-entrypoint.sh"]
# 启动服务
CMD ["/usr/bin/supervisord", "-c", "/etc/supervisor/supervisord.conf"]

2.Helm Chart

在编写Helm Chart之前你需要了解Kubernetes中的对象及其概念,本事例中运行应用只需定义一个deployment对象即可。

第一步:创建目录

在项目根目录下创建如下目录结构,首先创建一个名为chart的文件夹,再创建一个与应用名相同的文件夹,本事例为Helm Chart,在halm-dev文件夹中再创建一个templates目录。

chart
└── halm-dev
├── Chart.yaml
├── templates
│ ├── _helpers.tpl
│ └── deployment.yaml
└── values.yaml

第二步:编写_helpers.tpl文件 在templates文件夹下将一些公共的lable或值定义到 _helpers.tpl文件中:

{{/* vim: set filetype=mustache: */}}
{{- /*
service.labels.standard prints the standard service Helm labels.
The standard labels are frequently used in metadata.
*/ -}}
{{- define "service.labels.standard" -}}
choerodon.io/release: {{ .Release.Name | quote }}
{{- end -}}

第三步:编写deployment.yml文件 在templates文件夹下创建一个名为deployment.yml的文件,内容如下:

apiVersion: apps/v1beta2
kind: Deployment
metadata:
name: {{ .Release.Name }}
labels:
{{ include "service.labels.standard" . | indent 4 }}
spec:
replicas: {{ .Values.replicaCount }}
selector:
matchLabels:
{{ include "service.labels.standard" . | indent 6 }}
template:
metadata:
labels:
{{ include "service.labels.standard" . | indent 8 }}
spec:
containers:
- name: {{ .Release.Name }}
image: "{{ .Values.image.repository }}:{{ .Chart.Version }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
env:
{{- range $name, $value := .Values.env }}
{{- if not (empty $value) }}
- name: {{ $name | quote }}
value: {{ $value | quote }}
{{- end }}
{{- end }}
ports:
- name: http
containerPort: {{ .Values.service.port }}
protocol: TCP
resources:
{{ toYaml .Values.resources | indent 12 }}

第四步:编写Chart.yaml文件 在halm-dev文件夹中编写Chart.yaml文件,这个文件中写明应用的的相关信息。

# api版本
apiVersion: v1
# 应用版本
appVersion: "1.0"
# 应用描述
description: A Helm chart for Kubernetes
# 应用名称
name: halm-dev
# 应用chart版本
version: 0.1.0

第五步:编写文件 在halm-dev文件夹中编写 values.yaml文件,这个文件中编写 templates文件夹中 deployment.yml文件会用到的变量及默认值。

# Declare variables to be passed into your templates.

replicaCount: 1

image:
repository: registry.choerodon.com.cn/hand-rdc-halm/halm-dev
pullPolicy: Always

env:
SITE_URL: http://localhost:8081/
DB_HOST_NAME:
DB_USER_NAME:
DB_PASSWORD:
DB_NAME:
DB_PORT: "3306"

logs:
parser: nginx

resources:
# We usually recommend not to specify default resources and to leave this as a conscious
# choice for the user. This also increases chances charts run on environments with little
# resources,such as Minikube. If you do want to specify resources,uncomment the following
# lines,adjust them as necessary,and remove the curly braces after 'resources:'.
limits:
# cpu: 100m
# memory: 2Gi
requests:
# cpu: 100m
# memory: 1Gi

CI持续集成配置

在上节“应用容器化配置”中,有提到Choerodon标准的应用源代码结构中必须包含charts文件件。同样,Choerodon使用Gitlab-CI作为CI工具,所以需要在应用源代码中加上.gitlab-ci.yml文件。

在CI中主要的工作就是进行镜像构建并且生成Chart包,最后将Chart包上传至Choerodon,与Choerodon进行集成。

在项目根目录下新建.gitlab-ci.yml文件,粘贴以下内容:

# 设置CI运行时的环境镜像
image: registry.cn-hangzhou.aliyuncs.com/choerodon-tools/cibase:0.6.0

# 设置阶段,这里只进行镜像构建和生成Chart包,所以定义为一个阶段即可
stages:
- docker-build

docker-build:
stage: docker-build
# 阶段中需要执行的命令
script:
- docker_build
- chart_build

# 这里是最为关键的,定义了一个全局脚本,在每一个阶段运行前都将执行下面代码从Choerodon平台中获取相应变量及封装的shell函数。
.auto_devops: &auto_devops |
http_status_code=`curl -o .auto_devops.sh -s -m 10 --connect-timeout 10 -w %{http_code} "${CHOERODON_URL}/devops/ci?token=${Token}"`
if [ "$http_status_code" != "200" ]; then
cat .auto_devops.sh
exit 1
fi
source .auto_devops.sh
# 重写docker_build函数
function docker_build(){
docker build --pull -t ${DOCKER_REGISTRY}/${GROUP_NAME}/${PROJECT_NAME}:${CI_COMMIT_TAG} .
docker push ${DOCKER_REGISTRY}/${GROUP_NAME}/${PROJECT_NAME}:${CI_COMMIT_TAG}
}

before_script:
- *auto_devops

更多如何配置符合Choerodon标准和要求的.gitlab-ci.yml文件,请参考Choerodon官网持续集成

将原来代码库替换到Choerodon代码库

经过了“应用容器化配置”和“CI持续集成配置”两步之后,将得到一个包含了charts和.gitlab-ci.yml文件的新的代码库(charts文件夹和.gitlab-ci.yml文件一定是放在代码库的根目录),现在就将代码库同步到Choerodon对应的代码库,替换生成的标准代码库。

Git相关的命令如下:

git commit -m "Change repo." # 先把所有为保存的修改打包为一个commit
git remote remove origin # 删掉原来git源
git remote add origin [YOUR NEW .GIT URL] # 将新源地址写入本地版本库配置文件
git push -u origin master # 提交所有代码

生成新的版本

当在上一步“将原来代码库替换到Choerodon代码库”中提交代码到Choerodon的远程新库的时候,Choerodon会自动生成一个master分支上的开发版本,即“2018.8.27-234043-master”,这个版本是可以部署运行的,当然,往往生成的第一个版本会由于各种BUG等,需要经过反复地调试才可以。

可以进入到Choerodon系统中查看生成的版本,系统路径:“汉得研发”(组织)->“<你的项目>”->应用管理->应用版本。如下图所示。

创建一个环境

有了可部署的版本之后,就可以把此版本部署到环境中去了。在步骤“应用系统环境搭建”中已经安装好了应用系统运行的Kubernetes集群环境,并且在“数据库迁移”步骤中已经安装部署好数据库。

1.进入到的Choerodon猪齿鱼创建环境页面,系统路径:“汉得研发”(组织)->“<你的项目>”->部署流水线->环境流水线。

2.单击“创建环境”按钮,在弹出框中输入环境编码、环境名称和环境描述。

例如,

  • 环境编码:halm-dev
  • 环境名称:开发环境
  • 环境描述:开发环境

3.保存时,系统会跳出来另一个对话框,如下图,需要将这段命令在步骤“应用系统环境搭建”中创建的Kubernetes环境中运行,以安装Choerodon Agent。这一步是必须要执行的,关于Choerodon Agent可以参考官网Choerodon Agent。

4.命令是具体应用、具体环境而不同的,所以,以下是笔者的环境生产的命令,请不要复制执行。

helm install --repo=http://chart.choerodon.com.cn/choerodon/c7ncd/ \
--namespace=halm-dev \
--name=halm-dev \
--version=0.9.7 \
--set config.connect=ws://devops.service.choerodon.com.cn/agent/ \
--set config.token=a932598f-8945-449a-9dc7-7a2db489eff6 \
--set config.envId=162 \
--set rbac.create=true \
choerodon-agent

5.如果在Kubernetes中执行成功,则可以看到“开发环境”显示“运行中”,否则就是不成功。

部署新生成的版本

可部署版本就绪,环境就绪,现在就还要把可部署的版本部署到环境中。

1.进入到的Choerodon猪齿鱼应用部署页面,系统路径:“汉得研发”(组织)->“<你的项目>”->部署流水线->应用部署。

2.选择应用及版本。

例如,

  • 选择应用:资产云应用(halm-dev)
  • 选择版本:2018.8.27-234043-master

3.选择环境及修改配置信息。

例如,

  • 选择环境:开发环境
  • 还有,下面的配置信息可以根据自身需求修改。

4.选择部署模式。

例如,

  • 选择部署模式:新建实例
  • 对于第一次部署,需要选择“新建实例”。

5.确认信息及部署。

6.最后,确认检查好信息之后,部署即可。可以在“实例”界面查看部署的情况。最终部署的实例名称为:“halm-dev-9fc8”。

创建网络

部署完成应用之后,还不能够被外部访问,需要创建网络和域名。现在先创建网络。

1.进入到Choerodon猪齿鱼网络页面,系统路径:“汉得研发”(组织)->“<你的项目>”->部署流水线->网络

2.单击“创建网络”,弹出创建网络界面。填写相关的字段信息。

例如,

  • 环境:选择“开发环境”。
  • 目标对象:选择“选择实例”。
  • 应用名称:选择“资产云应用”
  • 选择实例:选择“halm-dev-9fc8”,就是上一步部署生成的实例。
  • 网络配置:选择“ClusterIP”
  • 外部IP:NULL
  • 端口:80 ,镜像内部应用的端口
  • 目标端口:80 ,K8s中已经部署的应用对外提供服务的端口
  • 网络名称:halm-dev-3491

创建域名

有了网络还要有域名才可以。

1.进入到Choerodon猪齿鱼域名页面,系统路径:“汉得研发”(组织)->“<你的项目>”->部署流水线->域名

2.单击“创建域名”,在弹出页面中填写相关信息。

测试访问

创建好域名之后,使用URL:handalm.hand-china.com 访问。

关于猪齿鱼

Choerodon 猪齿鱼是一个全场景效能平台,基于 Kubernetes 的容器编排和管理能力,整合 DevOps 工具链、微服务和移动应用框架,来帮助企业实现敏捷化的应用交付和自动化的运营管理的平台,同时提供 IoT、支付、数据、智能洞察、企业应用市场等业务组件,致力帮助企业聚焦于业务,加速数字化转型。

大家也可以通过以下社区途径了解猪齿鱼的最新动态、产品特性,以及参与社区贡献: