Skip to main content

· 25 分钟阅读

Choerodon猪齿鱼平台使用 React 作为前端应用框架,对前端的展示做了一定的封装和处理,并配套提供了前端组件库Choerodon UI。结合实际业务情况,不断对组件优化设计,提高代码质量。

本文将结合Choerodon猪齿鱼平台使用案例,简单说明组件的分类、设计原则和设计模式,帮助开发者在不同场景下选择正确的设计和方案编写组件(示例代码基于ES6/ES7的语法,适于有一定前端基础的读者)。

文章的主要内容包括:

  • React 组件简介
  • 组件分类
  • 组件设计原则、最佳实践
  • 组件设计模式简介

React 组件简介

React是指用于构建用户界面的 JavaScript 库。换言之,React是一个构建视图层的类库(或框架)。不管 React 本身如何复杂,不管其生态如何庞大,构建视图始终是它的核心。

可以用个公式说明:

UI = f(data)

React的基础原则有三条,分别是:

  1. React 界面完全由数据驱动;
  2. React 中一切都是组件;
  3. props 是 React 组件之间通讯的基本方式。

那么组件又是什么?

组件是一个函数或者一个 Class(当然 Class 也是 function),它根据输入参数,最终返回一个 React Element。简单地说,React Element 描述了“你想”在屏幕上看到的事物。抽象地说,React Element 元素是一个描述了 Dom Node 的对象。

所以实际上使用 React Component 来生成 React Element,对于开发体验有巨大的提升,比如不需要手写React.createElement等。

那么所有 React Component 都需要返回 React Element 吗?显然是不需要的。 return null; 或者返回其他的 React 组件都有存在的意义,它能完成并实现很多巧妙的设计、思想和副作用,在下文会有所扩展。

可以说,在 React 中一切皆为组件:

  • 用户界面就是组件;
  • 组件可以嵌套包装组成复杂功能;
  • 组件可以用来实现副作用。

React 也提供了多种编写组件的方法适用于各种场景实例。

组件分类

如何在场景下快速正确地选择组件设计模式和方案,首先得有一个自己接受和常用的组件分类,以便从分类中快速确定编写方法,再考虑设计模式等后续问题。

Vue的作者尤雨溪在一场Live中也表达过自己对前端组件的看法,“组件可以是函数,是有分类的。”从功能维度对组件进行了分类,这四种分类方式也适用于Choerodon猪齿鱼前端开发中的业务场景:

  • 纯展示型组件:数据进,DOM出,直观明了
  • 接入型组件:在React场景下的container
  • component,这种组件会跟数据层的service打交道,会包含一些跟服务器或者说数据源打交道的逻辑,container会把数据向下传递给展示型组件、
  • 交互型组件:典型的例子是对于表单组件的封装和加强,大部分的组件库都是以交互型组件为主,比如说Element UI,特点是有比较复杂的交互逻辑,但是是比较通用的逻辑,强调组件的复用
  • 功能型组件:以Vue的应用场景举例,路由的router-view组件、transition组件,本身并不渲染任何内容,是一个逻辑型的东西,作为一种扩展或者是抽象机制存在

在此以Choerodon猪齿鱼平台的一个创建界面来分析。

  • 红色布局:功能型组件
  • 蓝色菜单:交互型组件,菜单项:遍历菜单数据输出DOM的纯展示型组件
  • 右块内容:接入型组件(容器组件)
  • Table、btn等:交互型组件

可以看到,一个复杂界面可以分割成很多简单或复杂的组件,复杂组件还包括子组件等。此外,除了从功能维度对组件进行划分,也可以从开发者对组件的使用习惯进行分类(以下分类非对立关系):

  • 无状态组件
  • 有状态组件
  • 容器组件
  • 高阶组件
  • Render Callback组件

简单说明一下几种组件:

  • 无状态组件:无状态组件(Stateless Component)是最基础的组件形式,由于没有状态的影响所以就是纯静态展示的作用。基本组成结构就是属性(props)加上一个渲染函数(render)。由于不涉及到状态的更新,所以这种组件的复用性也最强。例如在各UI库中开发的按钮、输入框、图标等等。
  • 有状态组件:组件内部包含状态(state)且状态随着事件或者外部的消息而发生改变的时候,这就构成了有状态组件(Stateful Component)。有状态组件通常会带有生命周期(lifecycle),用以在不同的时刻触发状态的更新。在写业务逻辑时常用到,不同场景所用的状态和生命周期也会不同。
  • 容器组件:为使组件的职责更加单一,耦合性进一步地降低,引入了容器组件(Container Component)的概念。重要负责对数据获取以及处理的逻辑。下文的设计模式也会提到。
  • 高阶组件:“高阶组件(HoC)”也算是种组件设计模式。做为一个高阶组件,可以在原有组件的基础上,对其增加新的功能和行为。如打印日志,获取数据和校验数据等和展示无关的逻辑的时候,抽象出一个高阶组件,用以给基础的组件增加这些功能,减少公共的代码。
  • Render Callback组件:组件模式是在组件中使用渲染回调的方式,将组件中的渲染逻辑委托给其子组件。也是种重用组件逻辑的方式,也叫render props 模式。

以上这些组件编写模式基本上可以覆盖目前工作中所需要的模式。在写一些复杂的框架组件的时候,仔细设计和研究组件间的解耦和组合方式,能够使后续的项目可维护性大大增强。

对立的两大分类:

  • 基于类的组件:基于类的组件(Class based components)是包含状态和方法的。
  • 基于函数的组件:基于函数的组件(Functional Components)是没有状态和方法的。它们是纯粹的、易读的。尽可能的使用它们。

当然,React v16.7.0-alpha 中第一次引入了 Hooks 的概念,Hooks 的目的是让开发者不需要再用 class 来实现组件。这是React的未来,基于函数的组件也可处理状态。

了解了这些以后就需要有一个自己开发新组件前的思考,遵循组件设计原则,快速确定分类开始编写Code。

设计原则/最佳实践

React 的组件其实是软件设计中的模块,其设计原则也需遵从通用的组件设计原则,简单说来,就是要减少组件之间的耦合性(Coupling),让组件简单,这样才能让整体系统易于理解、易于维护。

即,设计原则:

  1. 接口小,props 数量少;
  2. 划分组件,充分利用组合(composition);
  3. 把 state 往上层组件提取,让下层组件只需要实现为纯函数。

就像搭积木,复杂的应用和组件都是由简单的界面和组件组成的。划分组件也没有绝对的方法,选择在当下场景合适的方式划分,充分利用组合即可。实际编写代码也是逐步精进的过程,努力做到:

  1. 功能正常;
  2. 代码整洁;
  3. 高性能。

取Choerodon猪齿鱼平台Devops项目的应用管理模块实例,导入应用:

这个界面看起来很简单,功能简介 + 导入步骤条,实际因为存在步骤条,内容很丰富。

首先组件叫做AppImport,组件内包含简介和步骤条,需要记录当前步骤条第几步状态’current‘,所以需要维持状态(state),可以肯定,AppImport 是一个有状态的组件,不能只是一个纯函数,而是一个继承自 Component 的类。

class AppImport extends React.Component {
constructor() {
super(...arguments);
this.state = {
current: 0,
};
}
render() {
//TODO: 返回所有JSX
}
}

接下来划分组件,按照数据边界来分割组件:

  • 使用了choerodon-front-boot 中定义好的容器组件,Page、Header、Content;
  • 渲染 Header,返回上级菜单,渲染当前界面title。
  • 渲染 Content,封装好的组件处理了导入应用和其详情简介;
  • 渲染 Steps 卡片,步骤条卡片渲染,state 为当前步以及后续需要导入提交的数据 data;
  • 最后,Steps 每一步数据需求都不同,均拆成单独子组件。

在 React 中,有一个误区,就是把 render 中的代码分拆到多个 renderXXXX 函数中去,比如下面这样:

class AppImport extends React.Component {
render() {
const Header = this.renderHeader();
const Content = this.renderContent();
const Steps = this.renderSteps();

return (
<Page>
{Header}
{Content}
{Steps}
</Page>
);
}

renderHeader() {
//TODO: 返回上级菜单,渲染当前界面title
}

renderContent() {
//TODO: 导入应用和其详情简介
}

renderSteps() {
//TODO: 返回步骤条卡片
}
}

用上面的方法组织代码,当然比写一个巨大的 render 函数要强,但是,实现这么多 renderXXXX 函数并不是一个明智之举,因为这些 renderXXXX 函数访问的是同样的 props 和 state,这样代码依然耦合在了一起。更好的方法是把这些 renderXXXX 重构成各自独立的 React 组件,像下面这样

class AppImport extends React.Component {
constructor() {
super(...arguments);
this.state = {
data: {},
current: 0,
};
}

next = () => {}

cancel = () => {}

render() {
return (
<Page>
<Header title:'xxx' backPath='xxxxxx' />
<Content code="app.import" values={{ appName }}>
<div className="c7n-app-import-wrap">
<Steps current={current} className="steps-line">
<Step key={item.key} title:{item.title} />
</Steps>
<div className="steps-content">
<Step0 onNext={this.next} onCancel={this.cancel} values={data} />
</div>
</div>
</Content>
</Page>
);
}
}

const Step = (props) => {
//TODO: 返回步骤条Content
};

const Steps = (props) => {
//TODO: Steps
};

const Page = (props) => {
//TODO: Page
}

// Header / Content

// 根据代码量,尽量每个组件都有自己专属的源代码文件 导出,再导入
// 示例代码中 Page、Header、Content 使用了choerodon-front-boot 中定义好的容器组件,
// Steps 使用了choerodon-ui 库
// 所以在头部导入即可
// import { Steps } from 'choerodon-ui';
// import { Content, Header, Page } from 'choerodon-front-boot';

实际情况下,步骤条不止一步,处理函数也不止那么简单,但是经过划分和抽取,作为展示组件的 AppImport 结构清晰,代码整洁,接口少(props只涉及公共的 store、history 等 )。再处理下StepN(子组件根据实际内容处理,这里略过),整个 AppImport 代码不超过150行,相比不划分组件,代码随便超过1000+行,划分优化后思路清晰,可维护性高。

最终代码:

import React, { Component, Fragment } from 'react';
import { observer } from 'mobx-react';
import { withRouter } from 'react-router-dom';
import { injectIntl, FormattedMessage } from 'react-intl';
import { Steps } from 'choerodon-ui';
import { Content, Header, Page, stores } from 'choerodon-front-boot';
import '../../../main.scss';
import './AppImport.scss';
import { Step0, Step1, Step2, Step3 } from './steps/index';

const { AppState } = stores;
const Step = Steps.Step;

@observer
class AppImport extends Component {
constructor() {
super(...arguments);
this.state = {
data: {},
current: 0,
};
}

next = (values) => {
// 点击下一步处理函数,略
};

prev = () => {
// 点击上一步处理函数,略
};

cancel = () => {
// 点击取消处理函数,略
};

importApp = () => {
// 点击导入,数据处理,略
};

render() {
const { current, data } = this.state;
// const ...

const steps = [{
key: 'step0',
title: <FormattedMessage id="app.import.step1" />,
content: <Step0 onNext={this.next} onCancel={this.cancel} store={AppStore} values={data} />,
}, {
key: 'step1',
title: <FormattedMessage id="app.import.step2" />,
content: <Step1 onNext={this.next} onPrevious={this.prev} onCancel={this.cancel} store={AppStore} values={data} />,
}, {
key: 'step2',
title: <FormattedMessage id="app.import.step3" />,
content: <Step2 onNext={this.next} onPrevious={this.prev} onCancel={this.cancel} store={AppStore} values={data} />,
}, {
key: 'step3',
title: <FormattedMessage id="app.import.step4" />,
content: <Step3 onImport={this.importApp} onPrevious={this.prev} onCancel={this.cancel} store={AppStore} values={data} />,
}];

return (
<Page>
<Header title:'xxx' backPath='xxxxxx' />
<Content code="app.import" values={{ name }}>
<div className="c7n-app-import-wrap">
<Steps current={current} className="steps-line">
{steps.map(item => <Step key={item.key} title:{item.title} />)}
</Steps>
<div className="steps-content">{steps[current].content}</div>
</div>
</Content>
</Page>
);
}
}

export default withRouter(injectIntl(AppImport));

过程中会接触到一些最佳实践和技巧:

  1. 避免 renderXXXX 函数
  2. 给回调函数类型的 props 加统一前缀(onNext、onXXX 或 handleXXX 规范,可读性好)
  3. 使用 propTypes 来定义组件的 props
  4. 尽量每个组件都有自己专属的源代码文件(StepN)
  5. 用解构赋值(destructuring assignment)的方法获取参数 props 的每个属性值
  6. 利用属性初始化(property initializer)来定义 state 和成员函数

组件设计模式

不同的业务情境下使用合适的设计模式能大大提高开发效率和可维护性。了解以上内容后能更好的理解和选择设计模式。

常用的设计模式有:

  1. 容器组件和展示组件(Container and Presentational Components);
  2. 高阶组件;
  3. render props 模式;
  4. 提供者模式(Provider Pattern);
  5. 组合组件。

网上介绍这些模式的文章有很多,每个模式都可以长篇详解。但是,模式就是特定于一种问题场景的解决办法。

模式(Pattern) = 问题场景(Context) + 解决办法(Solution)

明确使用场景才能正确发挥模式的功能。所以,简单介绍一下各模式实际应用于什么场景较好。

容器组件和展示组件

React最简单也是最常用的一种组件模式就是“容器组件和展示组件”。其本质就是把一个功能分配到两个组件中,形成父子关系,外层的父组件负责管理数据状态,内层的子组件只负责展示,让一个模块都专注于一个功能,这样更利于代码的维护。

上文步骤条的实例就是把获取和管理数据这件事和界面渲染这件事分开。做法就是,把获取和管理数据的逻辑放在父组件,也就是容器组件;把渲染界面的逻辑放在子组件,也就是展示组件。有关数据处理的变动就只需要对容器组件进行修改,例如修改数据状态管理方式,完全不影响展示组件。

高阶组件

高阶组件适用场景于“不要重复自己”(DRY,Don't Repeat Yourself)编码原则,某些功能是多个组件通用的,在每个组件都重复实现逻辑,浪费、可维护行低。第一想法是共用逻辑提取为一个 React 组件,但是共用逻辑单独无法使用,不足以抽象成组件,仅仅是对其他组件的功能加强。当然,高阶组件并不是 React 中唯一的重用组件逻辑的方式,下文的 render props 模式也可处理。

例如,很多网站应用,有些模块都需要在用户已经登录的情况下才显示。比如,对于一个电商类网站,“退出登录”按钮、“购物车”这些模块,就只有用户登录之后才显示,对应这些模块的 React 组件如果连“只有在登录时才显示”的功能都重复实现,那就浪费了。

render props 模式

所谓 render props,指的是让 React 组件的 props 支持函数这种模式。因为作为 props 传入的函数往往被用来渲染一部分界面,所以这种模式被称为 render props。适用场景和高阶组件差不多,但是与其还是有一些差别:

  1. render props 模式的应用,是一个 React 组件,而高阶组件,虽然名为“组件”,其实只是一个产生 React 组件的函数
  2. 高阶组件可链式调用,因为实质是函数
  3. render props 相对于高阶组件还有一个显著优势,就是对于新增的 props 更加灵活

所以以上对比,当需要重用 React 组件的逻辑时,建议首先看这个功能是否可以抽象为一个简单的组件;如果行不通的话,考虑是否可以应用 render props 模式;再不行的话,才考虑应用高阶组件模式。当然,没有绝对的使用顺序,实际场景为准。

提供者模式

在 React 中,props 是组件之间通讯的主要手段,但是,有一种场景单纯靠 props 来通讯是不恰当的,那就是两个组件之间间隔着多层其他组件。避免 props 逐级传递,即是提供者模式的适用场景。实现方式也分老Context API和新Context API。新版本的 Context API 才是未来,在 React v17 中,可能就会删除对老版 Context API 的支持,所以,现在大家都应该使用第二种实现方式。新版API详解

典型用例就是实现“样式主题”(Theme),多语言支持等。

组合组件

组合组件模式要解决的是这样一类问题:父组件想要传递一些信息给子组件,但是,如果用 props 传递又显得十分麻烦。利用 Context?当然还有其他解决方案,就是组合组件模式。

应用组合组件场景的往往是共享组件库,把一些常用的功能封装在组件里,让应用层直接用就行。在 antd 和 bootstrap 这样的共享库中,都使用了组合组件这种模式。将复杂度都封装起来了,从使用者角度,连 props 都看不见。实例扩展

总 结

对前端来说,前端不是不用设计模式,而是已经把设计模式融入到了开发的基础当中。Choerodon猪齿鱼平台前端真实的业务场景往往需要应用多个设计模式,界面也会包含多个大小不一的组件。开发设计时,符合程序设计的原则:「高内聚,低耦合」即可。本文只是简单总结,提供一些思路和简单的应用场景给开发者,真正的熟练把握和应用还得多实践开发使用,多对自己欠缺的知识点去深挖学习和思考,不断进步。

参考/引用资料:

关于猪齿鱼

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

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

· 16 分钟阅读

知识管理是Choerodon猪齿鱼的一个重要功能,支持在线自定义文档编辑、成员协助共享等功能,帮助团队集中管理创意、需求和设计。本文将会和大家聊一聊知识管理对敏捷团队的重要性,介绍Choerodon选用XWiki作为开发基础的原因,以及开发团队如何设计Choerodon知识管理,最后会简单介绍一下Choerodon知识管理的一些主要功能和特色。

敏捷团队的知识管理

对于软件开发团队来说,每个成员都是知识工作者,知识工作者需要具备一个很重要的能力——知识管理能力。知识是软件行业的灵魂和生存之本,然而很多时候大家都忽略或轻视了团队知识管理的重要性,所以Choerodon希望能够为团队提供一个便捷高效的知识管理方式。

Choerodon猪齿鱼知识管理是为了解决敏捷团队在快速迭代的开发过程中可能忽视的知识沉淀、文档管理和团队协作等问题而诞生的。

印第安人在赶了3天路之后,会停下来小憩一天,因为他们要等待自己的灵魂跟上来。敏捷开发过程也是如此,在经历一个迭代或者冲刺之后,也需要做一些休整,那就是敏捷回顾。每次回顾时,团队成员需要归纳总结开发过程中积累下来的各种问题或经验,并将这些知识记录在可共享的wiki上,这样既便于团队成员互相学习,也利于以后需要时快速查阅。

为什么基于XWiki开发Choerodon知识管理

现在常见到的wiki有很多,开源免费的如 MediaWiki、DokuWiki,企业收费的如 Confluence等。在开发Choerodon猪齿鱼知识管理初期,团队希望能够选择一款既开源,又能针对Choerodon猪齿鱼平台的应用场景,做定制化的wiki系统。比如适应Choerodon的组织项目层级结构,以及与Choerodon猪齿鱼中的敏捷管理、测试管理等做更多的互动,让用户能够在使用平台时,快速准确地记录下自己的知识,让知识管理在应用交付和自动化运营过程中发挥更大的价值。在经过许久对比和甄选之后,团队选择了一个优秀、强大的开源wiki系统——XWiki。

XWiki是由一家法国的公司XWiki SAS于2004年开发出的一款wiki系统,并于2007年开源。至今已经10多年,这个社区仍然有众多活跃的贡献者和使用者,并且拥有大量的客户群体和产品案例。XWiki有健壮的编辑器、强大的编辑语法,能提供多种拓展特性来定制化wiki。除了XWiki官方提供的功能以外,还有超过100个社区参与者贡献的拓展项目,这些拓展都可以直接在XWiki系统的拓展市场上安装和管理。

XWiki、Confluence以及MediaWiki对比

接下来通过XWiki与Confluence以及MediaWiki的对比,来谈谈Choerodon为什么选择XWik作为其知识管理的基础。

XWiki是一个开源的项目,使用了LGPL开源协议,使用者可自己搭建和拓展XWiki而不需要购买任何授权。它使用java语言开发,提供了众多面向企业的特性,并且拥有丰富的拓展库和宏,灵活性与拓展性很强。有许多如Amazon、AFP、EDF等大型的公司使用XWiki创建知识库或者协作工具,也有一些企业和组织使用XWiki搭建门户网站。

Confluence是一个团队协作软件,由澳大利亚的公司Atlassian开发和销售,它是一个面向业务的专业wiki,使用java开发,主要应用于企业环境。目前Confluence有许多大型客户案例,如Facebook、eBay、Adobe等,不过使用Confluence,需要向Atlassian公司购买授权。

MediaWiki是一款使用PHP开发的开源wiki软件,它最著名的案例就是维基百科,所以也是受众最大的wiki。MediaWiki支持多语言管理、各种扩展和媒体格式,还能够配置wiki外观。但是MediaWiki在权限管理,组织架构管理上并不适合Choerodon的解决方案,也不太适用企业的业务需求。

除了上面谈到的三款wiki软件之外,我们还对比了其他比较流行的wiki软件,综合考虑之后,最终选择了开源的、有强大的拓展特性以及适应企业多种业务场景的XWiki。

Choerodon猪齿鱼知识管理的设计

Choerodon猪齿鱼平台是基于spring cloud的微服务架构开发,而Choerodon知识管理选择以XWiki作为开发的基础,那么就需要做到统一的用户、权限以及组织架构。因此开发团队开发了微服务wiki-service,这个微服务主要用于监听Choerodon平台中其他服务的操作,比如IAM的创建组织、创建项目等,然后使用http请求的形式,让XWiki做对应的业务处理,比如创建对应的空间和页面。另外XWiki需要使用Choerodon猪齿鱼平台统一的登录和权限校验,所以Choerodon在XWiki中添加了OAuth登录认证。下图是Choerodon猪齿鱼知识管理设计示意图:

Choerodon猪齿鱼平台中有三层组织层次,即全局层、组织层和项目层。Choerodon 使用三层组织结构来管理用户、权限、项目、环境资源、菜单,以及其他系统资源和功能。其中全局层包含了系统的一些基本设置,例如组织管理、菜单管理、全局角色管理等;组织层管理用户、权限、项目、环境资源,以及其它系统资源和功能;项目层用来管理软件的开发,项目属于组织。Choerodon知识管理也是按照组织层和项目层,以及组织和项目下的空间来管理wiki的。为了保证XWiki与Choerodon平台有统一的组织和项目以及组织、项目对应的人员、权限等,需要将每个组织项目的创建、角色分配等都在XWiki中做对应的处理。下图是当IAM创建一个组织时,wiki-service为保证数据一致性做的处理:

Choerodon猪齿鱼知识管理功能介绍

Choerodon知识管理实现了一个强大的Wiki平台,允许用户根据自己的特定需求自定义Wiki,为企业、IT团队提供方便的项目协作平台和强大的项目内容管理平台,集中式管理产品相关内容,例如需求收集、架构设计、功能设计、开发规范、命名规范、会议记录、计划安排等。目前Choerodon知识管理除基础的空间页面创建、文档编辑、文档共享等,还开发了一些特色功能,如个人空间、组织项目文档权限控制、匿名分享、文档模板等。

基本概念简介

  • 空间

空间相当于一个分组,它是一类文档的集合,组织和项目下都可以创建空间。在空间下可以创建对应类型的文档树,实现各类文档的分别维护和管理。

  • 页面

页面是Wiki中的基本内容单元,也就是使用者在空间下创建的文档。页面可以创建在空间下或者在其他页面之下,这样用户就可以随意地组织团队的文档结构。页面可以进行编辑、分享、评论、收藏等操作,还支持使用多语法编辑和多人协作编辑,另外每个页面都可以有版本控制。

个人空间

Choerodon知识管理中除了组织项目下的共享空间之外,Choerodon还设计了一种特殊的空间——个人空间。这个空间为每个wiki用户提供了一个私人的编辑区域,用户可以在个人空间中随意编辑和记录文档,并且可以选择将一些文档发布到需要的公开区域,比如某个组织或者项目下。

空间、页面权限

在Choerodon知识管理中,空间和页面采用相同的权限模型,以用户组的形式配置权限。对于组织和项目下的空间、以及空间下的页面,在不单独配置权限的情况下,默认是继承组织和项目的权限的,也就是只有组织或者项目成员才可以查看。

同时,用户可以针对某个页面或者空间,个性化配置权限,可以是对某个用户组开放,也可以对所有人或者匿名用户开放,权限包括视图查看、评论、编辑、修改脚本、删除和设置管理员。如下图的权限配置页面所示:

匿名访问

Choerodon知识管理的内容使用空间和页面的形式组织,并且受到组织项目权限的管控,当用户想将文档分享给未注册用户时,可以将一些需要公开的文档设置成可匿名访问,既不影响其他页面的权限,也保证了文档的安全。在权限设置中选择未注册用户勾选相应权限即可。

文档模板

Choerodon知识管理预置了产品需求和敏捷回顾会议记录等文档的模板,可以直接进行编辑,简化了用户编辑排版操作。平台管理员也可以自定义创建团队常用文档模板、修改页面布局界面。

其他功能概述

除了上面提到的几个特色以外,Choerodon知识管理还提供了比如:

  • 所有更新、热门:用户可以查看最近wiki中发生的文档创建、编辑、评论等操作,以及一些浏览量较高的文档,能够了解到一些新的文档动态,及时的学习和共享。

  • 最近工作、最近访问:用户可以在wiki中查看到自己最近编辑以及查看的文章,方便用户查看自己的历史操作记录。

  • 通知:页面的操作可以通过站内信或者邮件的形式通知到关注者,帮助用户及时跟进文档的状态。

  • 搜索:wiki系统提供了强大的文章搜索引擎,可以搜索到文章中的关键字,提高搜索精度。

  • 导入、导出:页面还可以进行批量的导入导出,可以导入office文档,wiki会自动解析文档内容,并添加到页面中。

了解更多功能,请查看Choerodon知识管理用户手册

总结

Choerodon Agent 自发布以来,经历了一系列优化与改进,不管是易用性还是稳定性都在不断提升,例如应用Chart模板中的资源对象将不用再预先插入平台所需要的标签,每个环境一个Agent客户端改成了一个集群一个客户端。增加了定期同步各资源状态的逻辑,有效地消除了平台中与集群的K8S资源状态的不一致,GitOps流程也越来越稳定。同时也感谢社区的朋友们反馈的一些Bug和建议,一起为产品完善而努力。

关于猪齿鱼

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

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

· 12 分钟阅读

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

2019年01月18日,Choerodon猪齿鱼发布0.13版本,本次更新新增了文档个人空间、文档分类收藏、平台菜单点击统计和应用第三方导入等诸多新功能,欢迎各位更新体验。

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

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

新增功能

知识管理

  • 新增wiki用户个人空间,在此可创建属于自己的页面。
  • 新增wiki界面部分英文汉化。
  • 新增wiki分类收藏,页面可以选择收藏在不同的文件夹。
  • 新增修改猪齿鱼组织、项目名称并同步到wiki的功能。
  • 新增删除wiki组中有重复用户的功能。
  • 新增wiki编辑器的格式刷和对齐方式。
  • 新增wiki文章选中部分能加入注释的功能。

敏捷管理

  • 统计图增加标签维度,同时增加冲刺、版本、时间过滤条件。
  • 面板设置中新增修改面板名称功能,同时增加重名校验。
  • 问题详情窄样式增加日志信息。
  • 问题在创建的时候支持填入工时、故事点。
  • 增加史诗、模块、版本重名校验。

持续交付

  • 开发控制台界面新增流水线模块,其中包括:分支管理、持续集成与应用版本;支持以分支为中心在流水线中进行开发操作。
  • 网络模块中目标对象部分新增支持Endpoints类型的网络设置。
  • 网络模块中网络配置部分新增支持LoadBalancer的选项。
  • 实例详情模块新增StatefulSet、DaemonSet、PVC、Service以及Ingress的详情展示。
  • 实例详情内新增Pod的增减功能,支持在当前界面直接增减Pod的数量。
  • 应用管理模块新增导入应用的功能,支持从Github和GitLab库中导入已有应用,并按照所选应用模板为导入的应用添加相应的文件。
  • 预定义模板新增SpringBoot、Go应用模板。
  • 容器界面新增显示每个Pod中各个Container的名称与状态。

微服务开发框架

  • 仪表盘支持改变大小。
  • 添加平台统计功能,方便平台管理员了解平台的使用情况。
  • 主页添加在线人数、新增人数、组织统计、事务失败情况卡片,使平台管理员更便捷的管理平台。
  • 项目信息和组织信息合并为权限信息。
  • 组织和项目支持上传头像,更好的标识您的组织或项目。
  • 消息通知添加系统公告详情页。
  • 事务实例添加强制将任务置为失败。
  • 任务调度添加执行策略。
  • LDAP添加自定义筛选用户条件,可根据筛选条件在同步用户时过滤 特定用户。
  • 系统公告添加公告标题以及配置是否发送站内信项。

功能优化

知识管理

  • 修改wiki左侧边栏菜单显示。
  • 修改wiki右上角通知样式。
  • 修改wiki右上角用户信息下拉框样式。
  • 修改wiki文章样式,显示标准A4纸大小。
  • 修改wiki页面中文翻译错误。
  • 修改wiki菜单数据过多,增加按钮显示全部。
  • 修改wiki更多下拉框样式。

敏捷管理

  • 待办事项中选中问题用户动作监听优化。
  • 部分页面样式优化。
  • 优化燃尽图计算逻辑。

持续交付

  • 创建网络时,网络配置部分选择NodePort时,节点端口改为了非必填。
  • 容器界面详细区分了Pod与容器(container)的概念,使界面更加直观明了。
  • 优化了实例详情界面,将实例事件与运行详情模块的顺序进行了调换。
  • 优化统一了实例事件界面Job与Pod状态的展现方式。
  • 优化了上传证书的过程,调换了Cert文件与Key文件的填写顺序。

测试管理服务

  • 优化测试执行中缺陷关联操作。
  • 自动化测试导入测试结果时测试循环时间范围自动调整。
  • 测试计划中修改测试阶段时间时自动适配父级测试循环时间。
  • 优化测试计划日历组件。

微服务开发框架

  • 优化API测试为当在左侧树形结构中,鼠标悬停在路径上时,显示路径描述。
  • 优化API测试为鼠标悬停在路径框上时,显示完整路径。
  • 优化菜单配置切换层级时保存的问题。

缺陷修复

知识管理

  • 修复safari浏览器编辑页面文章无法滚动。
  • 修复wiki编辑页面图片样式变形。
  • 修复修改组织、项目名称,wiki空间名与标题不统一。
  • 修复旧数据wiki组中用户有"."符号的用户权限不生效的问题

敏捷管理

  • 待办事项中计划中冲刺人员信息统计重复。
  • 累积流图脏数据修复(需要手动调用进行修复)。
  • 创建状态、删除状态导致状态机草稿配置表产生脏数据后发布不可用。
  • 日期选择器的节假日显示错误。
  • 状态机方案搜索异常。
  • 状态机方案发布后,被移除的状态机仍然为活跃状态。
  • 敏捷服务增加状态,从状态机节点的发布配置和草稿配置中同时添加,并修复脏数据。
  • 敏捷服务删除状态,从状态机节点的发布配置和草稿配置中同时移除,并修复脏数据。

持续交付

  • 修复了实例界面部署实例时,选择应用模块后,无法看到正在部署的实例的问题。
  • 修复了创建环境时名称校验的问题。
  • 修复了编辑网络时,目标对象中实例的显示问题。
  • 修复了创建网络时,由于存在名称相同的文件而引起的报错问题。
  • 修复了创建域名时,重新选择网络时,对应端口不变的问题。
  • 修复了通过实例界面点击查看容器详情进行跳转后某些对应应用未显示的问题。
  • 修复了自动化测试模块里,部署失败后日志丢失的问题。
  • 修复了创建实例时,实例名为空时未提示的问题。
  • 修复了实例事件由于操作人员为空导致npe异常的问题。
  • 修复了环境流水线修改环境分组报错的问题。
  • 修复处理了webhook时区的问题。
  • 修复了导出应用自定义名称中带点时引起的格式问题。
  • 修复了在创建或修改操作提交后,表单内容仍可编辑的问题。

测试管理服务

  • 修复自动化测试运行报错时不会将状态更新成失败的问题。
  • 修复测试用例导出时,如果版本中包含空格会导出失败的问题。
  • 修复自动化测试生成的测试循环无法删除的问题。
  • 修复测试用例、文件夹删除权限错误匹配。

微服务开发框架

  • 修复消息通知日期显示可能覆盖图标的问题。
  • 修复富文本编辑器处理后的内容,可能显示边距有误的问题。
  • 修复系统公告图片显示可能超出边界的问题。

删除

知识管理

  • 删除wiki界面打印预览、查看源码、重命名等不常用功能。

持续交付

  • 移除了环境总览界面实例详情里的Networking的显示。

社区参与

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

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

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

· 18 分钟阅读

Kubernetes作为一个容器编排调度引擎,资源调度是它的最基本也是最重要的功能。当开发者部署一个应用时它运行在哪个节点?这个节点满不满足开发的运行要求?Kubernetes又是如何进行资源调度的呢?

▌通过本文可了解到以下信息:

  • 资源请求及限制对pod调度的影响
  • 查看调度事件events
  • 了解label选择器对pod调度的影响
  • 了解节点亲和性和Pod亲和性对调度的影响
  • 不使用调度器,手动调度一个pod
  • 了解Daemonset的角色
  • 了解如何配置Kubernetes scheduler

在Kubernetes中有一个kube-scheduler组件,该组件运行在master节点上,它主要负责pod的调度。Kube-scheduler监听kube-apiserver中是否有还未调度到node上的pod(即Spec.NodeName为空的Pod),再通过特定的算法为pod指定分派node运行。如果分配失败,则将该pod放置调度队列尾部以重新调度。调度主要分为几个部分:首先是预选过程,过滤不满足Pod要求的节点。然后是优选过程,对通过要求的节点进行优先级排序,最后选择优先级最高的节点分配,其中涉及到的两个关键点是过滤和优先级评定的算法。调度器使用一组规则过滤不符合要求的节点,其中包括设置了资源的request和指定了Nodename或者其他亲和性设置等等。优先级评定将过滤得到的节点列表进行打分,调度器考虑一些整体的优化策略,比如将Deployment控制的多个副本集分配到不同节点上等。

资源请求及限制对pod调度的影响

在部署应用时,开发者会考虑到使这个应用运行起来需要多少的内存和CPU资源的使用量,这样才能判断应将他运行在哪个节点上。在部署文件resource属性中添加requests字段用于说明运行该容器所需的最少资源,当调度器开始调度该Pod时,调度程序确保对于每种资源类型,计划容器的资源请求总和必须小于节点的容量才能分配该节点运行Pod,resource属性中添加limits字段用于限制容器运行时所获得的最大资源。如果该容器超出其内存限制,则可能被终止。 如果该容器可以重新启动,kubelet会将它重新启动。如果调度器找不到合适的节点运行Pod时,就会产生调度失败事件,调度器会将Pod放置调度队列以循环调度,直到调度完成。

在下面例子中,运行一个nginx Pod,资源请求了256Mi的内存和100m的CPU,调度器将判断哪个节点还剩余这么多的资源,寻找到了之后就会将这个Pod调度上去。同时也设置了512Mi的内存和300m的CPU的使用限制,如果该Pod运行之后超出了这一限制就将被重启甚至被驱逐。

apiVersion: v1
kind: Pod
metadata:
name: nginx
spec:
containers:
- name: nginx
image: nginx
resources:
requests:
memory: "256Mi"
cpu: "100m"
limits:
memory: "512Mi"
cpu: "300m"

参考文档:

  • Assign CPU Resources to Containers and Pods

  • Assign Memory Resources to Containers and Pods

查看调度事件events

在部署应用后,可以使用 kubectl describe 命令进行查看Pod的调度事件,下面是一个coredns被成功调度到node3运行的事件记录。

$ kubectl describe po coredns-5679d9cd77-d6jp6 -n kube-system
...
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 29s default-scheduler Successfully assigned kube-system/coredns-5679d9cd77-d6jp6 to node3
Normal Pulled 28s kubelet, node3 Container image "grc.io/kubernetes/coredns:1.2.2" already present on machine
Normal Created 28s kubelet, node3 Created container
Normal Started 28s kubelet, node3 Started container

下面是一个coredns被调度失败的事件记录,根据记录显示不可调度的原因是没有节点满足该Pod的内存请求。

$ kubectl describe po coredns-8447874846-5hpmz -n kube-system
...
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Warning FailedScheduling 22s (x3 over 24s) default-scheduler 0/3 nodes are available: 3 Insufficient memory.

label选择器对pod调度的影响

例如开发者需要部署一个ES集群,由于ES对磁盘有较高的要求,而集群中只有一部分节点有SSD磁盘,那么就需要将标记一下带有SSD磁盘的节点即给这些节点打上Lable,让ES的pod只能运行在带这些标记的节点上。

Lable是附着在K8S对象(如Pod、Service等)上的键值对。它可以在创建对象的时候指定,也可以在对象创建后随时指定。Kubernetes最终将对labels最终索引和反向索引用来优化查询和watch,在UI和命令行中会对它们排序。通俗的说,就是为K8S对象打上各种标签,方便选择和调度。

  1. 查看节点信息。

    $ kubectl get nodes
    NAME STATUS ROLES AGE VERSION
    node1 Ready etcd,master 128m v1.12.4
    node2 Ready etcd,lb,master 126m v1.12.4
    node3 Ready etcd,lb,worker 126m v1.12.4
  2. 选择出有SSD磁盘的节点,并给这个节点打上标记(label)。

    $ kubectl label nodes <your-node-name> disktype=ssd
    node/<your-node-name> labeled
  3. 验证节点上是否有成功打上对应label。

    $ kubectl get nodes --show-labels
    NAME STATUS ROLES AGE VERSION LABELS
    node1 Ready etcd,master 139m v1.12.4 ...disktype=ssd,kubernetes.io/hostname=node1...
    node2 Ready etcd,lb,master 137m v1.12.4 ...kubernetes.io/hostname=node2...
    node3 Ready etcd,lb,worker 137m v1.12.4 ...kubernetes.io/hostname=node3...
  4. 创建一个ES的pod, 调度到有SSD磁盘标记的节点上。在pod的配置里, 要指定nodeSelector属性值为disktype:ssd。这意味着pod启动后会调度到打上了disktype=ssd标签的node上。

        apiVersion: v1
    kind: Pod
    metadata:
    name: es
    spec:
    containers:
    - name: es
    image: es
    nodeSelector:
    disktype: ssd
  5. 验证pod启动后是否调度到指定节点上。

    $ kubectl get pods -o wide
    NAMESPACE NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE
    default es-5679d9cd77-sbmcx 1/1 Running 0 134m 10.244.2.3 node1 <none>

参考文档:

  • Assign Pods to Nodes

节点亲和性和Pod亲和性对调度的影响

上小节讲述的nodeSelector提供了一种非常简单的方法,可以将pod限制为具有特定标签的节点。而更为强大的表达约束类型则可以由Affinity和Anti-affinity来配置。即亲和性与反亲和性的设置。亲和性和反亲和性包括两种类型:节点(反)亲和性与Pod(反)亲和性。

Node affinity与NodeSelector很相似,它允许你根据节点上的标签限制你的pod可以在哪些节点上进行调度。目前有两种类型的节点关联,称为required During Scheduling Ignored During Execution和 preferred During Scheduling Ignored During Execution。可以将它们分别视为“硬规则”和“软规则”,前者指定了要将 pod调度到节点上必须满足的规则,而后者指定调度程序将尝试强制但不保证的首选项。名称中的“Ignored During Execution”部分意味着,类似于nodeSelector工作方式,如果节点上的标签在运行时更改,不再满足pod上的关联性规则,pod仍将继续在该节点上运行。Pod affinity强调的是同一个节点中Pod之间的亲和力。可以根据已在节点上运行的pod上的标签来约束pod可以调度哪些节点上。比如希望运行该Pod到某个已经运行了Pod标签为app=webserver的节点上,就可以使用Pod affinity来表达这一需求。

目前有两种类型Pod亲和力和反亲和力,称为required During Scheduling Ignored During Execution以及 preferred During Scheduling Ignored During Execution,其中表示“硬规则”与“软规则”的要求。类似于Node affinity,IgnoredDuringExecution部分表示如果在Pod运行期间改变了Pod标签导致亲和性不满足以上规则,则pod仍将继续在该节点上运行。无论是Selector还是Affinity,都是基于Pod或者Node的标签来表达约束类型。从而让调度器按照约束规则来调度Pod运行在合理的节点上。

节点亲和性如下所示,其中亲和性定义为:该pod只能放置在一个含有键为kubernetes.io/hostname并且值为node1或者node2标签的节点上。此外,在满足该标准的节点中,具有其键为app且值为webserver的标签的节点应该是优选的。

apiVersion: v1
kind: Pod
metadata:
name: with-node-affinity
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/hostname
operator: In
values:
- node1
- node2
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 1
preference:
matchExpressions:
- key: app
operator: In
values:
- webserver
containers:
- name: with-node-affinity
image: k8s.gcr.io/pause:2.0

Pod反亲和性如下所示,其中反亲和性定义为:在此拓扑域(相当于以topologyKey的值进行的节点分组)中,命名空间为default下有标签键为app,标签值为redis的Pod时不在此Node上运行。

apiVersion: v1
kind: Pod
metadata:
name: with-node-affinity
spec:
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- redis
namespaces:
- default
topologyKey: kubernetes.io/hostname
containers:
- name: with-node-affinity
image: k8s.gcr.io/pause:2.0

不使用调度器, 手动调度一个pod

Scheduling过程的本质其实就是给Pod赋予nodeName属性合适的值。那么在开发者进行Pod部署时就直接指定这个值是否可行呢?答案是肯定的。如下配置,将nginx直接分配到node1上运行。

apiVersion: v1
kind: Pod
metadata:
name: nginx
spec:
containers:
- image: nginx
name: nginx
nodeName: node1

还有一种指定节点的部署方式——static pod,就像它名称一样,他是一个“静态”的Pod,它不通过apiserver,直接由kubelet进行托管。在kubelet的启动参数中--pod-manifest-path=DIR,这里的DIR就是放置static pod的编排文件的目录。把static pod的编排文件放到此目录下,kubelet就可以监听到变化,并根据编排文件创建pod。还有一个启动参数--manifest-url=URL,kubelet会从这个URL下载编排文件,并创建pod。static pod有一个特性是我们使用docker或kubectl删除static pod后, static pod还能被kubelet进程拉起。通过这种方式保证了应用的可用性。有点相当于systemd的功能, 但比systemd好的一点是, static pod的镜像信息会在apiserver中注册。 这样的话, 我们就可以统一对部署信息进行可视化管理。 此外static pod是容器, 无需拷贝二进制文件到主机上, 应用封装在镜像里也保证了环境的一致性, 无论是应用的编排文件还是应用的镜像都方便进行版本管理和分发。

在使用kubeadm部署kubernetes集群时,static pod得到了大量的应用,比如 etcd、kube-scheduler、kube-controller-manager、kube-apiserver 等都是使用 static pod的方式运行的。

使用static pod部署出来的pod名称与其他pod有很大的不同点,名称中没有“乱码”,只是简单的将pod的name属性值与它运行在的node的name属性值相连接而成。如下所示,coredns是通过Deployment部署出来的名称中就有部分“乱码”,而etcd,kube-apiserver这种Pod就是static pod。

$ kubectl get po --all-namespaces
NAMESPACE NAME READY STATUS RESTARTS AGE
kube-system coredns-5679d9cd77-d6jp6 1/1 Running 0 6m59s
kube-system etcd-node1 1/1 Running 0 6m58s
kube-system etcd-node2 1/1 Running 0 6m58s
kube-system etcd-node3 1/1 Running 0 6m54s
kube-system kube-proxy-nxj5d 1/1 Running 0 6m52s
kube-system kube-proxy-tz264 1/1 Running 0 6m56s
kube-system kube-proxy-zxgxc 1/1 Running 0 6m57s

了解Daemonset角色

DaemonSet是一种控制器,它确保在一些或全部Node上都运行一个指定的Pod。这些Pod就相当于守护进程一样不期望被终止。当有Node加入集群时,也会为他们新增一个Pod。当有Node从集群移除时,对应的Pod也会被回收。当删除DaemonSet时将会删除它创建的所有Pod。一般情况下,Pod运行在哪个节点上是由Kubernates调度器选择的。但是在Kubernates 1.11版本之前由DaemonSet Controller创建的Pod在创建时已经确定了在哪个节点上运行(pod在创建的时候.spec.nodeName字段就指定了, 因此会被scheduler忽略),所以即使调度器没有启动DaemonSet Controller创建的Pod仍然也可以被分配node。直到Kubernates 1.11版本,DaemonSet的pod由scheduler调度才作为alpha特性引入。在上小节中kube-proxy就是以DaemonSet的方式进行运行的。

配置Kubernetes scheduler

如果需要配置一些高级的调度策略以满足我们的需要,可以修改默认调度程序的配置文件。kube-scheduler在启动的时候可以通过--policy-config-file参数来指定调度策略文件,开发者可以根据自己的需要来组装Predicates和Priority函数。选择不同的过滤函数和优先级函数。调整控制优先级函数的权重和过滤函数的顺序都会影响调度结果。

官方的Policy文件如下:

kind: Policy
apiVersion: v1
predicates:
- {name: PodFitsHostPorts}
- {name: PodFitsResources}
- {name: NoDiskConflict}
- {name: NoVolumeZoneConflict}
- {name: MatchNodeSelector}
- {name: HostName}
priorities:
- {name: LeastRequestedPriority, weight: 1}
- {name: BalancedResourceAllocation, weight: 1}
- {name: ServiceSpreadingPriority, weight: 1}
- {name: EqualPriority, weight: 1}

其中predicates区域是调度的预选阶段所需要的过滤算法。priorities区域是优选阶段的评分算法。

总结

再来回顾一下调度的主要构成部分:首先是预选过程,过滤掉不满足Pod要求的节点,然后是优选过程,对通过要求的节点进行优先级排序,最后选择优先级最高的节点进行分配。当调度器不工作时或有临时需求可以手动指定nodeName属性的值,让其不通过调度器进行调度直接运行在指定的节点上。

关于猪齿鱼

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

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

· 15 分钟阅读

Choerodon Agent是支撑Choerodon平台持续交付部署流水线的一个核心组件,负责将平台生成的部署文件应用到应用部署环境对应的Kubernetes集群之中。并实施返回各个应用实例以及应用实例下所有资源的最新状态信息,同时通过监听各个环境对应的部署文件Git库,执行CD操作。而且支持混合云以及多云作为平台的部署环境,通过返回回来的容器信息和反馈回来各个实例下的容器,还可以实时获取容器日志,以及容器exec执行远程命令。

Choerodon持续交付可以支持任意数量的集群加入平台,作为应用的部署环境,只要将平台中生成的Agent安装脚本在任意Kubernetes集群中执行,就可以将该集群加入平台,然后在平台上创建环境时可以选择该集群,可以一键创建环境。在同一个集群中可以创建多个环境,各个环境之间通过Kubernetes的命令空间隔离。

平台初始化部署集群只需要将平台生成的Agent安装脚本在Kubernetes环境中执行。Agent在集群中安装之后便可以在该集群中创建环境。作为项目应用的部署环境。

创建集群之后,平台会提供一份激活指令,将指令粘贴至Kubernetes集群中执行,成功后集群就连接成功了。在平台的界面上集群显示的状态也为运行中。下图所示脚本就是Agent的激活指令。

helm install --repo=http://chart.choerodon.com.cn/choerodon/c7ncd/ \
--namespace=choerodon \
--name=choerodon-cluster-agent-test \
--version=2018.12.10-112732-master \
--set config.connect=ws://devops.com.cn/agent/ \
--set config.token=dccf4539-43e7-4970-a2d4-271267850d67 \
--set config.clusterId=21 \
--set config.choerodonId=434ha8v7sz90 \
--set rbac.create=true \
choerodon-cluster-agent

集群连接成功之后,可以在环境流水线管理界面,选择相应的集群一键创建环境。创建环境时,平台会给Agent发送一条指令,让Agent创建相应命令空间,并拉去初始化环境对应的Git库,开始准备同步。

环境创建成功之后相关持续交付的部署操作即可选择该环境作为目标环境,进行应用部署,网络、域名、证书的创建。所有操作都将发送至Agent由Agent执行,所对应资源对象状态的变更也有Agent传输回来,进行实时展示。

实现分析

基于以上的这些功能目标,而且考虑到Choerodon Agent作为一个连通持续交付平台和Kubernetes集群的代理客户端,需要具备实时性、稳定性和高性能,所以决定用go语言实现,充分利用go轻量高效的特性。使用go作为开发语言,可以便捷的使用client-go,helm client等现成工具库开发CD相关操作。由于需要实时监听,实时反馈,等特性,Agent与DevOps之间的交互采用WebSocket长连接。并且使用客户端的方式,不提供对外暴露访问接口,充分保证安全性而且不需要集群提供对外暴露访问接口。

用户在平台中创建环境时同时会创建一个与环境对应的Git仓库用来存放部署配置文件,之后所有在环境中部署相关操作,都会转化为Git库中部署配置文件的操作,用户在平台界面操作部署或者直接推送部署文件至Git库都会触发Git库配置的Webhook,之后继续交付微服务拉取最新提交,解析并生成tag,然后通知环境客户端去执行环境对应Git库中最新的变更。环境客户端收到持续交付服务通知后,执行最新一次的tag,执行完毕后生成执行tag,当环境的最新提交Commit sha,与持续交付服务解释tag的sha,与环境客户端执行的tag sha一致时,表示最新的操作或者提交都已经应用到环境。

  1. 用户可以通过直接向Git库提交和在界面上进行相关部署操作,在GitOps中,界面上进行的部署操作都会先直接修改环境对应的部署文件Git库。例如一个应用实例版本更新部署操作对应在部署文件中的提交如上图。

  2. 部署文件Git库产生提交后,git库中的webhook随机触发,将变更发送至DevOps服务

  3. DevOps服务拉取库中最新提交,与上一个tag版本进行比较,根据比较结果分别生成创建、更新、删除记录,重新生成tag。

  4. 通知对应环境Agent拉取DevOps服务最新解析tag。

  5. Agent从部署库中拉取最新DevOps解析tag。

  6. 并根据部署库文件,与环境中真实部署的对象列表进行比较,然后在环境执行创建、更新、删除操作。执行完成后将执行结果发回至DevOps服务。

具体设计

Choerodon Agent通过WebSocket Client与外部的猪齿鱼部署服务进行连接、执行命令等交互。内部通过Helm客户端与Kubernetes集群。

内部的tiller server执行Chart安装删除等操作,并且通过Kube Client直接对Kubernetes各种资源对象进行操作,监听各资源对象的状态变更。

通过长连接及时通知部署服务。Choerodon Agent和部署服务之间的交互采用Command/Response模式,启动时立即向部署服务建立连接,接收Command执行并返回结果Repsonse。作为WebSocket Client将Command通过Channel不断的发送至执行器,执行器Worker是一个可伸缩配置的工作线程/协程池,执行后将结果通过Channel给Websocket Client写回。具体实现可主要分为如下几个主要功能块。

建立连接初始化信息

Agent启动时立即与DevOps服务建立WebSocket长连接,通过Websocket Client立即与DevOps服务建立WebSocket长连接,连接成功之后,DevOps服务集群的初始化信息从WebSocket发送至Agent、Agent根据初始化信息,启动Controller监听对应的命名空间,对集群下的每个GitOps环境库启动Git库同步程序。初始配置信息包含对各个环境Git库的SSH配置,如下所示。

 Host c7n-agile-prod
HostName code.choerodon.com.cn
StrictHostKeyChecking no
UserKnownHostsFile /dev/null
IdentityFile /rsa-c7n-agile-prod
LogLevel error
Host c7n-tm-prod
HostName code.choerodon.com.cn
StrictHostKeyChecking no
UserKnownHostsFile /dev/null
IdentityFile /rsa-c7n-tm-prod
LogLevel error

Command/Response模式

Agent不断从长连接中读取命令,也不停的从Channel中读取返回结果写至长连接中,命令解析出来之后通过Channel发送至Worker,然后在Worker中通过K8S Client或者Helm Client执行相应命令。执行成功之后将结果再通过Channel发回至 Agent Websocket Client,Client将结果通过长连接发送回DevOps。

实时状态反馈

通过Controller机制监听实例下的各个Kubernetes资源对象、只要有对象创建、更新或者删除、Controller中就会监听到,在Controller监听到对应的资源对象之后,判断,并实时反馈传输回DevOps服务。

func NewpodController(podInformer v1_informer.PodInformer, responseChan chan<- *model.Packet, namespaces *manager.Namespaces) *controller {
c := &controller{
queue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "pod"),
workerLoopPeriod: time.Second,
lister: podInformer.Lister(),
responseChan: responseChan,
namespaces: namespaces,
}

podInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
AddFunc: c.enqueuepod,
UpdateFunc: func(old, new interface{}) {
newpod := new.(*v1.Pod)
oldpod := old.(*v1.Pod)
if newpod.ResourceVersion == oldpod.ResourceVersion { return
}
c.enqueuepod(new)
},

DeleteFunc: c.enqueuepod,
})
c.podsSynced = podInformer.Informer().HasSynced
return c

}

Helm Operator

将Chart应用实例通过K8S自定义对象描述出来,执行创建或者修改实例时候,先创建相应实例对应的文件或者修改应用实例在Git库中对应的文件,Controller中监听到这些文件的变化之后在通过文件执行相应的Install或者Upgrade操作。保证环境中实例的状态与描述文件的状态一致。

---
apiVersion: choerodon.io/v1alpha1
kind: C7NHelmRelease
metadata:
name: choerodon-front-devops-5c483
spec:
chartName: choerodon-front-devops
chartVersion: 0.11.0
repoUrl: http://chart.choerodon.com.cn/choerodon/c7ncd/
values: |-
env:
open:
PRO_HEADER_TITLE_NAME: Choerodon1

GitOps

在GitOps中针对每个环境,在Agent初始化之后,将各个环境Git库SSH配置创建出来、并将Git库通过SSH拉至本机,检测是否有权限创建和删除tag,定时拉取最新的提交、同时在收到DevOps服务执行指令后,将最新的提交版本中的所有K8S资源文件执行至环境对应的命令空间之中。

状态同步与修复

由于Agent和DevOps服务之间连接交互采用长连接,可能出现由于网络或者其他原因导致消息丢失,从而产生预期与实际的状态不一致。所以增加了状态同步和修复的机制。保证一致性。当网络连接断开重新连接之后,各个Controller重新同步各类Kubernetes资源,使DevOps服务中各类资源对象的状态与实际情况保持一致,避免环境中的各实例资源状态与平台中展示的不一致.同时Devops服务会定时将一些超过一段时间还处于中间状态的对象发送至DevOps服务查询状态。如果中间Agent发给DevOps的部分消息丢失或者处理失败,造成一些不一致的状态,会通过这个状态修复功能将该对象的状态修复正常,以保证两边的资源状态一致性。

Log和Exec长连接

Agent从长连接中收到Log或者Exec请求指令后,建立一个Pipe,通过K8S Client Log或者Exec相关Api建立与Api Server的长连接,同时通过WebSocket Client 向DevOps请求建立一个长连接,通过这个Pipe中转打通两个长连接。从而实现Log和Exec长连接的中转代理。

总结

Choerodon Agent 自发布以来,经历了一系列优化与改进,不管是易用性还是稳定性都在不断提升,例如应用Chart模板中的资源对象将不用再预先插入平台所需要的标签,每个环境一个Agent客户端改成了一个集群一个客户端。增加了定期同步各资源状态的逻辑,有效地消除了平台中与集群的K8S资源状态的不一致,GitOps流程也越来越稳定。同时也感谢社区的朋友们反馈的一些Bug和建议,一起为产品完善而努力。

关于猪齿鱼

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

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

· 14 分钟阅读

▌文章的主要内容包括:

  • 配置是什么
  • 为什么需要微服务配置中心
  • Choerodon的配置中心

在早期单体应用的时代,监控等系统配置管理可能并不是什么困难的问题。但是在微服务架构中,和安全、日志、非功能需求一样,配置管理也是一种非功能需求。配置中心也是整个微服务架构体系中的一个重要组件,即使它的功能看上去并不起眼,无非就是简单配置管理和存取,但它是整个微服务架构中不可或缺的一环。

在Choerodon的微服务体系中,如何通过更加高效的配置管理方式,帮助微服务系统进行配置规划,推动项目的持续交付,动态调整和控制系统的运行状态,这些问题都值得深入思考和研究。

配置是什么

在讲配置中心之前,首先提到一个核心的概念,就是服务的“配置”。配置可能各位都不陌生,一个配置项大多是key-value的形式,value可能是一个有限值的集合。Choerodon通过这些配置项,来控制系统的运行状态,可以说,配置其实是独立于程序的可配变量,同一份程序在不同配置下会有不同的行为。

常见的配置有如下几种:

  • 程序内置的硬编码:在开发过程中将配置写死,这种形式几乎不具备任何的扩展性可动态修改的能力。通常是不建议的!
  • 程序配置文件:Springboot提供了一种方式将配置放在application.properties或者application.yaml文件中,系统运行时的大部分配置,都可以通过这种格式进行配置。不同环境会有各自的配置文件。
  • 环境变量:将配置预制在操作系统的环境变量中,在程序运行时读取。尤其在docker容器中,不同容器间相互隔离,环境变量不会受到影响。
  • 启动参数:可以在程序启动时制定参数,修改时需要重新启动。
  • 数据库存储:易变化的配置存储在数据库中,这样在运行期可以灵活的调整。

而微服务的配置中心,将散落在各处的应用系统配置信息收集起来,进行集中式的统一管理,并且提供额外功能,如动态刷新,版本控制,功能开关等。

举一个简单的例子,有这样的一个配置:logging.level。在生产环境上的日志级别通常为error级别,但是在一些测试环境,或者当系统出问题时,开发人员会期望日志的级别为debug,此时则需要一种机制来帮助实现。

为什么需要微服务配置中心

既然已经有了这么多种形式来管理配置,又为什么要引入配置中心呢?

在传统单体应用中,开发者将系统打成一个包,并在包里提供一些配置,当需要修改配置时,只需要登录到服务器上将配置修改,然后reload一下就可以了。

在微服务体系中,服务的数量以及配置信息日益增多,比如各种服务器参数配置、各种数据库访问参数配置、各种环境下配置信息的不同、配置信息修改之后实时生效等等,显然开发者已经不可能登录到一台台虚拟机中,对配置进行修改重启,并且,传统的配置管理可能会带来如下的一些问题:

  • 配置的格式不一致:有的使用xml,有的使用properties,还有一些存在数据库中,不同项目对于配置的管理方式五花八门。
  • 失效性低:配置大多使用静态文件的格式,再通过打包放入容器中运行。当实例过多时,需要修改完配置重新打包,周期较长。
  • 不安全:配置跟随源代码保存在代码库中,容易造成配置泄漏。同时不同环境的配置不同,稍不注意,可能会将测试环境的配置带入生产环境,引发事故。
  • 功能局限:不支持动态调整。

同时,随着“微服务+容器化+DevOps”落地,对于服务的配置管理也提出了更高的要求。

  • 代码与配置分离:遵循“一次打包,多处部署”的原则。将代码和配置分离。配置单独管理。
  • 配置抽象:屏蔽后台实现,提供页面管理功能。
  • 跨环境跨集群:支持对多环境和多集群应用配置的集中式管理。
  • 实时性:配置更新需要尽快通知到客户端。
  • 可治理:配置审计、版本管理、权限控制。

所以,一个能够方便用户进行自助式的配置管理,标准化的配置中心服务,是Choerodon社区里的开发者急切所需的。

Choerodon的配置中心

配置分类

在Choerodon猪齿鱼中,将服务的配置按照不同的功能,大致分为3类。

  • 静态配置:程序打包前一次配置好,一般不会修改。这些配置通常在服务启动之前生效。如:服务的端口,名称,配置中心的地址等。
  • 部署时配置:程序在部署时添加的配置,一般和环境相关,每个环境不同。如:数据库配置,其他中间件配置,或者一些服务相关的配置。
  • 运行时配置:程序运行时可能会修改的配置,一般用来调整应用的行为和功能。如:日志级别,功能开关,线程池大小等等。

服务配置设计原则

对配置进行分类之后,还需要一种设计原则,来指导开发者对服务的配置进行划分。在Choerodon猪齿鱼中,开发者遵循下面的一些原则。

  1. 所有可能要修改的配置,都不应该采取硬编码的方式。
  2. 程序中的配置文件,均使用yaml文件进行管理。
  3. 引入spring-cloud-config-client,将服务名,端口号,管理端口,等不会修改的配置,添加在src/main/resources/bootstrap.yml,如下所示:
# bootstrap.yml
server:
port: 8080
spring:
application:
name: iam-service
cloud:
config:
failFast: true
retry:
maxAttempts: 6
multiplier: 1.5
maxInterval: 2000
uri: localhost:8010
enabled: false
management:
port: 80381
security:
enabled: false
security:
basic:
enabled: false
  1. 将服务运行中需要的配置添加在src/main/resources/application.yml。包括注册中心的地址,数据库连接,并为这些配置添加默认值,如下所示:
# application.yml
spring:
datasource:
url: jdbc:mysql://localhost/iam_service?useUnicode=true&characterEncoding=utf-8&useSSL=false
username: choerodon
password: 123456
eureka:
instance:
preferIpAddress: true
leaseRenewalIntervalInSeconds: 10
leaseExpirationDurationInSeconds: 30
metadata-map:
VERSION: v1
client:
serviceUrl:
defaultZone: http://localhost:8000/eureka/
registryFetchIntervalSeconds: 10
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds: 15000
ribbon:
ReadTimeout: 5000
ConnectTimeout: 5000
mybatis:
mapperLocations: classpath*:/mapper/*.xml
configuration: # 数据库下划线转驼峰配置
mapUnderscoreToCamelCase: true
  1. 开发阶段,每个开发人员自己的本地配置,添加在src/main/resources/application-default.yml文件中,并且该文件不应该上传到代码库中,例如本地的数据库连接配置。
# application-default.yml
spring:
datasource:
url: jdbc:mysql://127.0.0.1:3307/iam_service?useUnicode=true&characterEncoding=utf-8&useSSL=false
username: username
password: pwd
  1. 配置的优先级遵循:环境变量>配置文件。通过Chart在部署时根据环境的不同,修改具体的环境变量值,来实现不同环境配置的不同,如下图所示:

  1. 对于紧要的配置修改通过配置中心修改,动态生效。对于实时性要求不高的配置,通过修改部署时的values重新部署生效。

配置中心实现

首先来看Spring Cloud自带的配置中心是怎么样的,如下图所示:

Spring Cloud Config将不同环境的所有配置存放在git 仓库中,服务启动时通过接口拉取配置。遵循{ServiceID}-{profile}.properties的结构,按照profile拉取自己所需的配置。

当开发者修改了配置项之后,需要结合spring config bus将配置通知到对应的服务,实现配置的动态更新。

可以看到,Spring Cloud Config已经具备了一个配置中心的雏形,可以满足小型项目对配置的管理,但仍然有着很多局限性。配置使用git库进行管理,那么git库的权限如何来判断?不同环境的安全性也得不到保障。配置的添加和删除,配置项的汇总,也只能通过git命令来实现,对运维人员也并不友好。

Choerodon猪齿鱼在Spring Cloud Config的基础上,将配置由git库存储改为由db存储。并且添加单独的manager-service来对配置进行管理。config-server则专注于将配置传递给服务。

同时,服务在部署的时候,通过猪齿鱼提供的工具包choerodon-tool-config将服务中的application.yml文件初始化到数据库中。在服务运行时通过前端来对配置进行操作,及时更新配置到各服务上,流程如下图所示:

同时可以在页面上依据已有的配置创建新的配置,并将配置应用给正在运行的服务实例。对于失效的配置文件,也可以在页面中删除。如下图所示:

Choerodon猪齿鱼通过配置中心管理服务配置的创建,生效,乃至销毁,并结合Chart来管理服务部署时的配置,通过这样的一种机制,满足了对服务配置整个生命周期管理的基本要求和配置版本、动态生效等进一步的需求。

总结

回顾一下这篇文章,整体介绍了配置中心在微服务架构中的作用,并提出了Choerodon对于配置管理的一些心得和规范。服务配置是服务运行起来的基础,而配置中心是整个微服务技术体系中的关键基础保障,如何做好服务配置规划,并推动项目的持续交付,则是Choerodon仍需持续考虑的问题。

关于猪齿鱼

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

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

· 16 分钟阅读

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

2018年12月21日,Choerodon猪齿鱼发布0.12版本,本次更新新增了文档访客访问、状态机设置、应用权限控制、配置映射功能、自动化测试等功能,并对一些功能细节做了进一步优化,欢迎各位更新体验。

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

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

新增功能

知识管理

  • 新增Wiki中的文档或者空间可以设置未登录用户访问的权限。
  • 新增Wiki编辑器在编辑时可以设置字体颜色和背景色的功能。
  • 新增Wiki编辑器快捷键Ctrl + s快速保存的功能。
  • 新增issue宏选择得到的issue的完成状态是实时获取的功能。
  • 新增issue宏在项目选择时可以搜索的功能。
  • 新增Wiki左侧边栏的树状浏览器可以自动定位到用户当前页面位置的功能。
  • 新增项目或组织首页的Wiki空间小组件在没有空间的情况下引导用户创建空间的功能。

敏捷管理

  • 新增组织层问题设置模块:包括问题类型设置、状态机设置、问题属性三个部分。
  1. 问题类型设置:包括问题类型和问题类型方案。将敏捷服务和测试服务的问题类型进行统一的管理,将一些问题类型组合在为问题类型方案,并关联项目,决定项目中敏捷和测试服务的问题类型。

  1. 状态机设置:包括状态机和状态机方案。可以设置状态机,也就是设置不同问题类型状态的流转流程。并将问题类型和状态机的组合应用于项目,决定项目中所有问题类型流转的方案。(一个状态机方案可以包含多个状态机及关联的问题,只有草稿状态机发布才会应用在关联的项目中。)

不同的问题类型及不同的状态机组合在一起形成状态机方案。本版本,项目初始化时,给项目一套默认的状态机方案,若不想使用默认方案,组织管理员可在此处编辑状态机方案使项目中问题的流转方案发生改变。

若对状态机方案进行了编辑,编辑的是草稿状态机方案,想要此状态机方案在项目中生效,需要对草稿状态机方案进行发布,否则项目中使用的还是原来的状态机方案。

  1. 新增问题属性:包括状态和优先级。可以设置项目中问题在某时段所处的状态以及问题在项目中紧急程度的表示。

  • 问题详情新增可添加Wiki文档的功能:将问题和Wiki相关联,方便用户在查看问题时,可以直接跳转相关文档中查看更多的问题细节。
  • 新增版本详情筛选功能:版本详情支持高级过滤筛选。
  • 新增活跃冲刺问题拖动排序功能:活跃冲刺面板上的问题支持拖动排序(故事泳道故事无法拖动)。
  • 新增活跃冲刺成员筛选过滤功能:活跃冲刺支持项目下所有成员筛选过滤功能。
  • 工作日历新增2019年法定节假日数据。
  • 版本详情新增问题链接。
  • 发布版本新增预计发布日期。

持续交付

  • 新增应用的开发权限分配功能,支持为各个应用配置特定的开发操作人员。

  • 新增配置映射功能,支持在每个环境下添加配置映射。
  • 新增密文功能,支持在每个环境下添加密文,可用于存储小片敏感数据的k8s资源,例如密码,token,或者密钥。
  • 新增实例事件,支持查看各个实例job与pod的事件记录。

  • 新增组织层证书的管理功能,在组织层创建的证书,可在组织下各个项目创建证书时使用。
  • 新增实例下Deployment查看更多的详情,包括:端口、数据卷、健康检查、主机设置、环境变量、标签。
  • 新增测试应用的创建,此类型的应用仅可用于自动化测试。
  • 应用模板、集群模块与环境模块的创建时,新增处理中的状态,用于解决异步处理带来的滞后问题。
  • 新增页面自动刷新的功能,可点击所有刷新按钮旁的自动刷新按钮开关进行设置。
  • 集群中自动新增CrtManager插件,用于使该集群下的环境能正常使用申请证书的功能。
  • 新增Dashboard与报表中的查看权限,此权限与应用权限和环境权限相关。
  • 新建实例时新增实例名称自定义的功能。
  • 环境卡片上新增显示该环境所连接的集群名称。

测试管理

  • 增加自动化测试模块。

在持续交付服务中基于测试框架模板(目前支持 mocha + chai 的 api 测试框架 )创建测试应用,将测试脚本存储到应用中。在测试管理模块中通过使用Choerodon agent进行多环境调度,运行测试应用。并将测试报告返回到测试管理服务进行解析然后将结果导入测试管理模块数据中。生成可视化报告、测试循环、测试执行等数据。

微服务开发框架

  • 新增API概览,以便于平台管理员和平台开发者了解API的调用情况。

  • 新增授权管理,用户可以查看并管理自己的授权信息。
  • 任务调度创建任务添加通知对象,当任务状态改变时,会向通知对象发送消息。
  • 添加系统配置平台密码策略。
  • 系统配置平台徽标和导航栏图形标支持剪裁。
  • 添加系统公告,平台管理员可以向整个平台发送公告。

  • 用户管理列表页添加重置密码功能。
  • 发送设置可配置站内信类型和是否允许接收配置。
  • 组织层和项目层添加事务实例。
  • 事务实例添加按任务查看。
  • 事务实例执行失败默认向触发者发送站内信。

功能优化

知识管理

  • 修改Wiki的编辑器样式,编辑区域为标准A4纸大小。
  • 修改上传附件过程的样式。
  • 修改创建页面的标题等显示内容。
  • 增加通知信息的查看更新按钮。
  • 修改了403、404页面显示

敏捷管理

  • 待办事项中冲刺的经办人工作量修改为问题总数、问题剩余数、总任务工时、剩余任务工时。

  • 问题创建人可以删除自己创建的问题。
  • 任务转化为子任务后状态自动修改为默认状态。
  • 完成冲刺时,未完成的子任务随父任务一并移动到下个冲刺。
  • 创建快速搜索的关系字段显示名称改为中文。
  • 创建版本时结束日期修改为预计发布日期。
  • 发布版本的时候需要输入实际发布时间。
  • 面板设置列约束只允许项目所有者修改。
  • 合并版本只能选择规划中的版本。
  • 版本详情创建日期修改为开始日期。
  • 列约束权限变更,只有项目管理员可以编辑。
  • 优化了史诗、版本侧栏、故事地图图标等。
  • 优化了待办事项编辑版本名称的重复校验。

持续交付

  • 优化了标记名称的命名规则。
  • 优化了实例模块的显示方式,在实例栏中显示该实例下pod的状态与总数量。
  • 优化了YMAL编辑器的样式。
  • 优化了环境总览页面内应用部署成功后的页面逻辑。
  • 优化了开发流水线与报表部分空界面的显示。
  • 优化了上传证书时的粘贴框。

测试管理

  • 测试计划中增加以测试阶段批量指派给对应测试人员的功能。
  • 测试计划和测试执行测试执行列表增加优先级字段及其筛选功能。

微服务开发框架

  • 优化个人信息界面。
  • 优化创建任务为分步进行。
  • 优化API测试界面为树形结构。
  • 优化事务实例界面。
  • 优化角色分配新增角色页面,用户下拉框显示用户头像。
  • 404页面美化。
  • 优化新手指引提示。
  • 优化消息已读的逻辑。
  • 修改右上角头像菜单栏。

缺陷修复

知识管理

  • 修复Wiki创建页面之后保存报错,但实际已经保存了的问题。
  • 修复用户使用长方形的头像时显示错误的问题。
  • 修复空间首页用户点击编辑按钮页面错乱的问题。
  • 修复在页面复制时,标题显示错误的bug。
  • 修复通知信息的标题太长导致遮挡的问题。
  • 修复所有更新页数据获取较慢的问题。

敏捷管理

  • 修复了版本统计未完成问题计数错误。
  • 修复了问题详情优先级下拉列表显示不全错误。
  • 修复了版本名称为中文时创建失败的错误。
  • 修复了创建史诗增加空操作约束的错误。

持续交付

  • 修复环境流水线内,切换环境按钮消失的问题。
  • 修复环境流水线内修改环境名称后页面逻辑的问题。
  • 修复Dashboard页面拖动卡片出现空白页的问题。
  • 修复实例升级失败,相关网络状态显示不正常的问题。
  • 修复持续集成列表,sonarqube阶段不可链接跳转的问题。
  • 修复代码提交报表中英文缺失导致的空白问题。
  • 修复应用部署时环境选择引起的问题。
  • 修复创建应用失败后,未隐藏编辑按钮与停用按钮的问题。

测试管理

  • 修复导出excel中issue描述错误以及富文本图片错误的问题。
  • 修复测试计划和 测试执行界面中切换项目数据错误的问题。
  • 修复执行详情中切换上一个\下一个时测试步骤数据错误的问题。
  • 修复仪表盘数据显示的问题。

微服务开发框架

  • 修复邮件通知
  • 修复平台配置重置后无法保存修改的问题。
  • 修复菜单配置删除菜单不发请求的问题。

删除

持续交付

  • 移除了实例详情界面的部署详情模块。

测试管理

  • 移除0.10.0版本中使用的修复数据接口。

社区参与

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

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

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

· 11 分钟阅读

Choerodon的需求和冲刺管理回顾

Choerodon敏捷管理中,我们使用用户故事地图和待办事项进行需求和冲刺管理。在敏捷开发实践中,整理需求和规划冲刺是开发中的重要阶段,通过规划管理可以使开发达到以下目标:

  1. 可视化管理团队
  2. 明确开发需求优先级
  3. 明确各个任务项
  4. 可视化任务进展情况

明确需求和冲刺管理的目标后,我们会有四个敏捷会议贯穿整个开发过程,需要开发团队的每个成员参与其中,此时也可以使用Choerodon敏捷管理进行会议的管理支持来达到团队的开发目标。

Choerodon对敏捷各类会议的管理支持

标准的敏捷流程包含了四个会议,即计划会、每日站会、评审会和回顾会。我们在实际开发中会结合Choerodon猪齿鱼平台来管理支持团队中的敏捷会议。

计划会

在每个Sprint进入开发之前,团队将会召开Sprint计划会议。

在会议之前,产品负责人会在Choerodon敏捷管理中使进行需求和冲刺的规划,并且排列好Backlog的优先级,为计划会议做准备。

在Sprint计划会议的前半段,根据Choerodon敏捷管理计划冲刺中的Backlog,产品负责人会从优先级最高的功能依次为开发团队进行讲解。然后,团队成员针对Backlog中的待开发功能提出问题,团队就该问题进行展开讨论,直到这个功能相关的问题全部解决,再进入下一个功能的讨论。

如果Backlog中的待开发功能在此迭代中已经饱和,或者有阻碍需要在进行重新计划的,产品负责人会把它从迭代计划拖动至待办事项列表进行重新规划。

在Sprint计划会议的下半段,开发团队会讨论这些确定开发的功能问题,这时可以使用Choerodon敏捷管理的报表:迭代速度图。

迭代速度图可以看出每个开发团队在每个迭代中完成任务的情况,大致可以得到一个团队迭代中任务的饱和点,然后开发团队根据这个数据决定下一轮迭代能够完成的工作量。

之后,团队成员对计划中的Sprint列表中的故事进行拆分,将每个故事拆分成一个一个的任务,并估算每个故事的故事点。一旦迭代开始,这些迭代任务将不会发生大的变化。

通过敏捷的计划会议,开发团队和产品负责人可以确认共同的迭代目标和价值。

每日站会

Choerodon敏捷管理中的活跃冲刺看板可以用来进行开发迭代任务可视化管理,每个任务下的子任务、经办人、任务状态、任务类型都在看板中显示,每一个开发人员都可以看到团队开发的流程进度。

每日站会是敏捷开发中用于开发团队沟通了解进度的会议,可以把看板泳道切换为经办人泳道,这样可以清楚每个人身上的任务进度。

每日站会中,开发团队成员根据看板中自己的任务进度和大家进行交流:我昨天做了什么,今天要做什么,我有遇到什么困难。可以一边交流一边拖动看板上的卡片(或者在站会开始前就已经根据自己情况进行卡片拖动完成),团队成员可以对大家的各种任务状态和受阻问题进行了解。

在每日站会拖动完团队成员的卡片后,会切换到Choerodon猪齿鱼敏捷管理看板中的工作台页面,团队会共同维护一张“燃尽图”(Burn Down Chart),即所有任务的累积剩余时间随开发进程与日递减的图形,用以观察和预测所有任务是否会按期完成。

每日站会中展示燃尽图,也是向团队成员展示一个迭代开发的任务或者时间的总体完成情况,可以指导团队随时调整迭代计划与速度。

评审会

每个Sprint结束时,都会有一个Sprint评审会议。评审会议最重要的工作是演示功能和成果,验证用户故事的实现场景,并接受评价。

所以在评审会开始之前,开发团队会检查Choerodon敏捷管理活跃冲刺看板中的故事完成情况,根据完成任务后的测试情况拖动卡片到看板已完成的列,把未完成的任务进行整理。

团队成员整理好看板中的已完成和未完成的任务时,我们就可以完成Sprint。这个迭代中未完成的遗留问题我们可以移动到待办事项中进行重新计划,对于已完成的任务,则会在评审会中进行演示验收。

评审会中,团队成员会对本次迭代中已完成的功能进行演示,产品负责人进行验收。团队成员需要接受产品负责人的评价,再针对已有的功能进行优化或者直接验收成功。

评审会后产品负责人会根据验收成果在Choerodon敏捷管理的待办事项中进行下个Spring的优先级调整,重新规划将要进行的新的Sprint。

回顾会

在评审会之后,开发团队会进行回顾会。回顾会的重点是团队检视与调整,进行工作问题和改进点的反馈。回顾会可以提供一个很好的机会给开发团队,来讨论什么方法能起作用而什么不起作用,并一致通过改进的方法。

在进行敏捷回顾会议时,开发团队会使用Choerodon猪齿鱼Wiki管理中进行会议记录,创建时使用“敏捷回顾会议记录”的文档模板,可进入已经设置好的回顾会议文档编辑页面。

回顾会议中,开发团队会提前根据本迭代的达成目标、产品功能、敏捷流程、需求管理等方面进行准备,针对开发团队在实施敏捷开发中的各种进步和问题进行讨论。可以指派一位团队成员进行会议记录,在已有的Wiki文档模板中记录大家提出的各种问题。

然后,团队成员会共同讨论找寻这些问题出现的根本原因,提出大家认同的解决方案,统一在下一个Sprint中改进,并在下一个回顾会议上评审改进问题的结果。

在Choerodon猪齿鱼知识管理中的回顾会文档,可以记录开发团队关于每个迭代的各种问题的思考,帮助团队不断调整敏捷实施方案,突出敏捷开发的效果。

总 结

回顾整篇文章,我们继上篇进行了简单的敏捷主要规则介绍,同时详细描述了Choerodon猪齿鱼是怎样管理支持敏捷的各个会议,通过使用Choerodon猪齿鱼不断规范、记录开发过程,实现敏捷的开发,希望可以对大家有所帮助。

参考资料:

关于猪齿鱼

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

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

· 11 分钟阅读

创建Namespace

在一个Kubernetes集群中可以创建多个Namespace进行“环境隔离”,当项目和人员众多的时候,可以考虑根据项目的实际情况(例如生产、测试、开发)划分不同的Namespace。

创建一个名称为“nginx”的Namespace:

[root@localhost~]# kubectl create ns nginx

namespace "nginx" created

查看集群中已创建出来的Namespace:

[root@localhost~]# kubectl get ns

NAME STATUS AGE
default Active 35d
kube-public Active 35d
kube-system Active 35d
nginx Active 19s

创建Deployment

Deployment为Pod 和Replica Set(下一代Replication Controller)提供声明式更新。只需要在 Deployment 中描述想要的目标状态是什么,Deployment Controller 就会帮开发者将 Pod 和 ReplicaSet 的实际状态改变成目标状态。开发者可以定义一个全新的 Deployment 来创建 ReplicaSet 或者删除已有的 Deployment 并创建一个新的来替换。使用Deployment能够更加方便地管理Pod,包括扩容、缩容、暂停、滚动更新、回滚等。在Choerodon中用实例的方式来展现Deployment,同时支持在线升级,停止,删除等多元化功能。

典型的应用场景包括:

  • 定义Deployment来创建Pod和ReplicaSet
  • 滚动升级和回滚应用
  • 扩容和缩容
  • 暂停和继续Deployment

编写名为dp.yaml文件,内容如下:

apiVersion: apps/v1beta1
kind: Deployment
metadata:
name: nginx-deployment
namespace: nginx
labels:
app: nginx
spec:
replicas: 1
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.13.5-alpine
ports:
- containerPort: 80
readinessProbe:
httpGet:
path: /
port: 80

保存后使用kubectl命令部署:

[root@localhost~]# kubectl apply -f dp.yaml

deployment.apps"nginx-deployment"created

可执行下面命令查看部署出来的Deployment:

[root@localhost~]# kubectl get deployment -n nginx

NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
inx-deployment 1 1 1 1 51s

可执行下面命令查看Deployment创建出来的Pod:

[root@localhost~]# kubectl get pod -n nginx -o wide

NAME READY STATUS RESTARTS AGE IP NODE
nginx-deployment-866d7c64c7-8rnd5 1/1 Running 0 3m 10.233.68.248 clusternode11

Pod状态为Running,说明已经正常工作了就可以在集群中通过Pod IP进行访问了:

[root@localhost~]# curl 10.233.68.248

<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
body{
width:35em;
margin:0 auto;
font-family:Tahoma,Verdana,Arial,sans-serif;
}
</style>
</head>
<body>
<h1>Welcome to nginx!<h1>
<p>If you see this page,the nginx web server is successfully installed and working. Further configuration is required.</P>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for uesing nginx.</em></p>
</body>
</html>

更多关于Deployment的介绍请参考这里: https://kubernetes.io/docs/concepts/workloads/controllers/deployment/

创建Service

Kubernetes Pod 是有生命周期的,它们可以被创建,也可以被销毁,然而一旦被销毁生命就永远结束。 通过 Deployment 能够动态地创建和销毁 Pod。 每个 Pod 都会获取它自己的 IP 地址,然而这些IP地址并不是稳定固定的,在销毁Pod时这些IP也会进行回收。 这会导致一个问题:在 Kubernetes 集群中,如果一组 Pod(称为 Backend)为其它Pod (称为 Frontend)提供服务,那么那些 Frontend 该如何发现,并连接到这组 Pod 中的哪些 Backend 呢?

这里要隆重的请出Service来解决这个问题。

编写名为svc.yaml文件,内容如下:

apiVersion: v1
kind: Service
metadata:
namespace: nginx
name: nginx-service
spec:
selector:
app: nginx
ports:
- name: http
protocol: TCP
port: 80
targetPort: 80

使用kubectl命令部署:

[root@localhost~]# kubectl apply -f svc.yaml

service "nginx-service"created

可执行下面命令查看部署出来的Service:

[root@localhost~]# kubectl get svc -n nginx

NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
nginx-service ClusterIP 10.233.47.128 <none> 80/TCP 56s

可以看到Pod对应的Service已经建好了,而这个“对应”就是依靠Service里面的Selector与Pod的Labels建立映射的(即Selector内容与Pod的Labels内容需一致)。现在集群内部可以通过该Service访问到Nginx,Choerodon中提供了Service的可视化创建操作,可以更加方便便捷的创建网络:

[root@localhost~]# curl 10.233.47.128

<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
body{
width:35em;
margin:0 auto;
font-family:Tahoma,Verdana,Arial,sans-serif;
}
</style>
</head>
<body>
<h1>Welcome to nginx!<h1>
<p>If you see this page,the nginx web server is successfully installed and working. Further configuration is required.</P>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for uesing nginx.</em></p>
</body>
</html>

更多关于Service的介绍请参考这里: https://kubernetes.io/docs/concepts/services-networking/service/

创建Ingress

此时,只有集群内部和所在主机能访问Nginx,要让节点外的其他主机能够访问,还得需要创建Ingress。Ingress 可以给 Service 提供集群外部访问的 URL、负载均衡、SSL 终止、HTTP 路由等功能。Ingress对应了Choerodon中的域名,Choerodon中除了对域名的管理外还添加了域名证书的管理,支持在线申请和导入。

编写名为ing.yaml文件,内容如下:

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: nginx-ingress
namespace: nginx
spec:
rules:
- host: nginx.example.local #此域名需解析到k8s集群负载主机IP
http:
paths:
- backend:
serviceName: nginx-service
servicePort: 80
path: /

使用kubectl命令部署:

[root@localhost~]# kubectl apply -f ing.yaml

ingress.extensions "nginx-ingress" created

可执行下面命令查看部署出来的Ingress:

[root@localhost~]# kubectl get ingress -n nginx

NAME HOSTS ADDRESS PORTS AGE
nginx-ingress nginx.example.local 80 1m

此时,就可以在浏览器中使用定义的URL访问Nginx了:

更多关于Ingress的介绍请参考这里: https://kubernetes.io/docs/concepts/services-networking/ingress/

看完传统Kubernetes的应用搭建后,来看看Choerodon中如何进行应用搭建吧。

基于Choerodon的应用搭建

一键部署

Choerodon中的应用部署简单明了,其构建了一套环境,实例,服务,域名的对象来进行Kubernetes的基础对象映射,为Kubernetes基础对象的创建和修改提供了可视化的操作界面。

在Choerodon猪齿鱼平台部署一个应用只需要在“应用管理”页面点击“创建应用”,并在“开发流水线”创建分支,提交代码后发布应用,在“部署流水线”页面选择要部署的应用,版本,目标环境,部署模式,设置好网络、域名即完成应用的部署。

那Choerodon在应用的搭建的背后又做了哪些事情呢?这就不得不提到GitOps了。

GitOps

Choerodon采用Kubernetes作为基础平台,通过Helm Chart打包应用,将Helm Release抽象成Kubernets自定义对象,这样就能通过Kubernetes资源对象文件描述整个环境部署应用系统的状态。在Choerodon中将应用搭建最终生成的yaml文件以配置库的方式存储在GitLab中,通过对比配置yaml文件的改动来判断应用的搭建状态,实现业务代码与配置代码的分离。

同时用户在平台中创建环境时会同步创建一个与环境对应的Git仓库用来存放部署配置文件,之后所有在环境中部署的相关操作,都会转化为Git库中部署配置文件的操作,同时触发Choerodon部署服务进行状态记录,应用状态记录后触发Choerodon在Kubernetes中的Agent进行应用部署。最终可以根据配置库,Choerodon部署服务状态记录,Choerodon Agent来诠释整个应用部署所经历的流程。

总结

以上步骤可以看出,从构建程序到外部访问,K8S提供了Namespace, Deployment, Service, Ingress等基础对象。以此为基础,Choerodon猪齿鱼平台构建了一套环境,实例,服务,域名的对象来进行映射,包括滚动升级、容错提高、服务测试等,并以友好的UI界面管理进行管理,使用户可以简单的通过页面进行Kubernetes的对象操作,从而创建自己的应用。

Choerodon猪齿鱼平台不仅实现一键部署,平台还提供了完备的测试管理,用于对新发布应用的测试。知识管理模块提供了企业内部的信息分享平台,报表模块则能够提供更为详细的开发、迭代信息。Choerodon猪齿鱼平台提升了K8S持续集成、持续部署的能力,使各个基础平台的耦合性更高,更加适用于企业实践DevOps。

关于猪齿鱼

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

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

· 4 分钟阅读

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

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

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

分享者介绍

张礼军

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

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

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

时间:12月8日 (周六)08:00-17:00

地址:上海市静安区铜仁路258号英孚教育B1

关于信息

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