• [技术干货] HTML 拖拉功能的实现代码(上)
    基于 vue此功能核心思想就是通过 JavaScript 代码控制 node 在页面上的左边距与顶边距,不同的的样式定位方式有不同的解决方案本方案采用position: absolute定位方式的解决方案css 样式的核心代码1234// 父容器核心样式   width: 100%;  height: 100%;12345// 子容器核心样式  position: absolute;  top: 50%;  left: 50%;  transform: translate(-50%,-50%);父容器通过width && height字段占满整个浏览器的可视范围,子容器通过position: absolute属性开启在父容器内的绝对定位,在通过top && left && transform: translate(-50%, -50%)属性控制子容器在父容器内的绝对位置JavaScript 逻辑控制的核心代码首先分解下,要实现 node 的移动需要哪些步骤和对应的 event 事件子容器创建时,在父容器内的绝对位置鼠标按键按下时,onmousedown 事件鼠标移动时,onmousemove 事件鼠标按键弹起时,onmouseup 事件只要使用 onMousedown、onMousemove和onMouseup 这三个事件,就可以实现最简单的移动12345678/** 在子容器创建的时候获取子容器相对于父容器的 top 和 left 位置*/ mounted () {  this.left = this.$refs.fatherBox.offsetLeft  this.top = this.$refs.fatherBox.offsetTop}12345678910111213/** 鼠标按下时* 1. 开启允许子容器移动的 flag* 2. 记录鼠标点击时的位置信息*/ mouseDown (e) {  // 设置允许弹窗移动的 flag  this.moveFlag = true  // 保存鼠标开始位置  this.startLeft = e.clientX - this.left  this.startTop = e.clientY - this.top}1234567891011121314151617/** 鼠标移动时* 1. 判断 flag 是否允许子容器移动* 2. 设置弹框左边位置* 3. 设置弹框右边位置*/ move (e) {  // 判断 flag 是否允许移动  if (!this.moveFlag) return   // 设置弹框左边位置  this.left = e.clientX - this.startLeft  // 设置弹框右边位置  this.top = e.clientY - this.startTop }12345678/** 鼠标按键弹起时* 1. 关闭允许子容器移动的 flag*/ mouseUp (e) {  this.flag = false}通过这几个方法就可以获取鼠标按下移动时,鼠标的top 和 left 的偏移量,通过把这偏移量暴露出去给父组件,父组件实时设置子组件的 top 和 left 值,来使得子容器跟随鼠标的移动父组件部分代码父组件通过设置子组件的 ref、zIndex 值,而且父组件的 backValue 方法会从子组件接收 zIndex 值,通过 zIndex 来识别具体的子组件实例12345678910111213141516171819202122232425262728293031323334353637383940/** 父组件代码片段 jsx 版*/ export default {  props: {    playList: {      type: Array,      required: true    }  },  render () {    return (      <div style={{width: '100%', height: '100%'}} ref={'father'}>        {          this.playList && this.playList.map((item, index) => {            return (              <ChildComponents                key={index}                ref={index}                zIndex={index}                visible={true}                backValue={this.backValue}                info={item}                width={600}                height={400}              />            )          })        }      </div>    )  },  methods: {    backValue (left, top, zIndex) {      this.$refs[zIndex].$el.style.top = `${top}px`      this.$refs[zIndex].$el.style.left = `${left}px`    }  }}
  • STL deque容器
    STL中的容器:deque容器的常用接口及用法:deque:它被称作双端数组,可以在头部和尾部插入或删除数据;deque 容器和 vector 容器的区别:1.vector 容器对头部插入、删除数据的效率较低,因为 vector 容器是单端数组,若要从头部插入或删除,得把后面的数据都往后挪或往前挪,因此数据量越大,则其时间效率越低;2.deque 容器相对 vector 容器而言,它对于头部插入数据或头部删除数据的效率就高多了,这与它的内部实现相关;3.vector 容器访问单个数据的效率要高于 deque 容器,原因也是与 deque 容器的内部实现有关;图片转自于黑马程序员,是在学习的过程中截图下来的deque 容器内部工作原理:1.deque 内部有个中控器,它维护着每段缓冲区中的内容,而缓冲区里面放着真实的数据;2.中控器维护的其实是缓冲区的地址,使得使用 deque 容器时像一段连续的内存空间;3.缓冲区的头和尾是没有插满数据的,因此可以继续添加。添加满后,则会开辟一块新的缓冲区,中控器则记录下新的缓冲区的地址;4.由于中控器维护的是地址,因此当我们访问单个元素时,内部的实现是从地址再转到缓冲区,这里的时间效率就低于 vector 容器了;图片转自于黑马程序员,是在学习的过程中截图下来的各种函数接口具体如何使用,下面的代码块中会有详细的使用方法deque 容器构造函数:1.deque<T> d; 默认(无参)构造;2.deque(const deque & d); 拷贝构造函数3.deque(begin,end); 把区间 [begin,end) 之间的数据拷贝到新创建的 deque 容器;4.deque(n,elem); 把n个 elem 拷贝给新创建的 deque 容器;#include <iostream>#include <deque>  //使用STL中的容器,得包含它的头文件 #include <algorithm>  //使用STL提供的算法,得包含它的头文件using namespace std;void printDeque(const deque<int> & d)  //限制传进来的deque容器只读的状态{for(deque<int>::const_iterator it=d.begin();it!=d.end();it++){cout << *it << " ";}cout << endl;}void test_1()  //deque容器构造函数 {int i = 0;deque<int> d1;  //默认(无参)构造函数 for(i=0;i<10;i++){d1.push_back(i+1);}printDeque(d1);deque<int> d2(d1);  //拷贝构造函数printDeque(d2);deque<int> d3(d1.begin(),d1.end());  //把区间[d1.begin(),d1.end())之间的数据拷贝到新创建的deque容器printDeque(d3);deque<int> d4(10,1);  //把10个1拷贝到新创建的deque容器 printDeque(d4);}int main(){test_1();system("pause");return 0;} 123456789101112131415161718192021222324252627282930313233343536373839404142434445deque 容器赋值:1.deque& operator=(const deque & d); 通过重载赋值运算符的方式给新创建的 deque 容器赋值;2.assign(begin,end); 通过成员函数 assign() 把 [begin,end) 之间的数据给新创建的 deque 容器赋值;3.assign(n,elem); 通过成员函数 assign() 把n个 elem 给新创建的 deque 容器赋值;#include <iostream>#include <deque>  //使用STL中的容器,得包含它的头文件 #include <algorithm>  //使用STL提供的算法,得包含它的头文件using namespace std;void printDeque(const deque<int> & d)  限制传进来的deque容器只读的状态{for(deque<int>::const_iterator it=d.begin();it!=d.end();it++){cout << *it << " ";}cout << endl;}void test_1()  //deque容器赋值{int i = 0;deque<int> d1;  //默认(无参)构造函数 for(i=0;i<10;i++){d1.push_back(i+1);}printDeque(d1);deque<int> d2;d2 = d1;  //通过重载赋值运算符的方式给新创建的deque容器赋值printDeque(d2);deque<int> d3;d3.assign(d1.begin(),d1.end());  //通过成员函数assign()把[begin,end)之间的数据给新创建的deque容器赋值printDeque(d3);deque<int> d4;d4.assign(10,1);  //通过成员函数assign()把10个1给新创建的deque容器赋值printDeque(d4);}int main(){test_1();system("pause");return 0;} 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647deque 容器的大小:1.empty(); 判断 deque 容器是否为空,如果 deque 容器为空,则返回 true,否则返回 false;2.size() 用于查看容器的大小(元素个数);3.resize(int num); 重新指定容器的大小为 num,若容器扩大了,则以默认值(0)来填充,若容器变小了,则删除多出来的部分;4.resize(int num,int elem);重新指定容器的大小为 num,若容器扩大了,则以 elem 来填充多出来的部分,若容器变小了,则删除多出来的部分;注意:由于 deque 内部工作原理的特殊性,deque 容器没有容量的概念;#include <iostream>#include <deque>  //使用STL中的容器,得包含它的头文件 #include <algorithm>  //使用STL提供的算法,得包含它的头文件using namespace std;void printDeque(const deque<int> & d){for(deque<int>::const_iterator it=d.begin();it!=d.end();it++){cout << *it << " ";}cout << endl;}void test_1()  //deque容器的大小 {int i = 0;deque<int> d1;for(i=0;i<10;i++){d1.push_back(i+1);}printDeque(d1);if(d1.empty())  //判断deque容器是否为空,如果deque容器为空,则返回true,否则返回false{cout << "当前容器为空" << endl;}else{cout << "当前容器不为空" << endl;cout << "d1的大小为:" << d1.size() << endl;  //用于查看容器的大小(元素个数)}d1.resize(20);  //重新指定容器的大小为20,容器扩大,以默认值(0)来填充printDeque(d1);d1.resize(25,10);  //重新指定容器的大小为25,容器扩大,以10来填充printDeque(d1);d1.resize(5);  //重新指定容器的大小为5,容器缩小,删除多出来的部分printDeque(d1);}int main(){test_1();system("pause");return 0;} 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354deque 容器插入和删除:两端插入和删除:1.push_back(elem); 在 deque 容器的尾部插入数据;2.push_front(elem); 在 deque 容器的头部插入数据;3.pop_back(); 在 deque 容器的尾部删除数据;4.pop_front(); 在 deque 容器的头部删除数据;指定位置的插入和删除:1.insert(pos,elem); 通过迭代器在 deque 容器的指定位置插入一个 elem 元素;2.insert(pos,n,elem); 通过迭代器在 deque 容器的指定位置插入n个 elem 元素;3.insert(pos,begin,end); 通过迭代器在 deque 容器的指定位置插入区间 [begin,end) 的数据;4.erase(pos); 通过迭代器删除掉 deque 容器指定位置的数据;5.erase(begin,end); 通过迭代器删除掉 deque 容器区间 [begin,end) 之间的数据;6.clear(); 清空当前的 deque 容器;#include <iostream>#include <deque>  //使用STL中的容器,得包含它的头文件 #include <algorithm>  //使用STL提供的算法,得包含它的头文件using namespace std;void printDeque(const deque<int> & d){for(deque<int>::const_iterator it=d.begin();it!=d.end();it++){cout << *it << " ";}cout << endl;}void test_1()  //deque容器两端插入和删除 {deque<int> d1;//尾插法 d1.push_back(10);d1.push_back(20);//头插法d1.push_front(100);d1.push_front(200);printDeque(d1);//尾删法d1.pop_back();//头删法d1.pop_front();printDeque(d1); }void test_2()  //deque容器指定位置的插入和删除 {deque<int> d1;d1.push_back(10);d1.push_back(20);d1.push_front(100);d1.push_front(200);d1.insert(d1.begin(),5000);  //通过迭代器在deque容器的头部插入一个5000printDeque(d1);d1.insert(d1.end(),2,5000);  //通过迭代器在deque容器的尾部部插入两个5000printDeque(d1);deque<int> d2;d2.push_back(1);d2.push_back(2);d2.push_back(3);d1.insert(d1.begin(),d2.begin(),d2.end());  //通过迭代器在d1的头部插入d2区间[d2.begin(),d2.end())的数据printDeque(d1);d1.erase(d1.begin());  //通过迭代器删除掉deque容器头部的数据printDeque(d1);deque<int>::iterator it = d2.begin(); it++;d2.erase(it,d2.end());  //通过迭代器删除掉deque容器区间[it,end)之间的数据printDeque(d2);d1.clear();  //清空当前的deque容器printDeque(d1);} int main(){test_1();test_2();system("pause");return 0;}1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980deque 容器数据存取:1.operator[]; 通过重载 [] 的方式,获取 deque 容器的每一个元素;2.at(int idx); 通过成员函数 at() 获取 deque 容器的每一个元素;3.front(); 返回容器中的第一个元素;4.back(); 返回容器中的最后一个元素;#include <iostream>#include <deque>  //使用STL中的容器,得包含它的头文件 #include <algorithm>  //使用STL提供的算法,得包含它的头文件using namespace std;void test_1()  //deque容器数据存取 {int i = 0;deque<int> d1;//300 200 100 10 20 30 d1.push_back(10);d1.push_back(20);d1.push_back(30);d1.push_front(100);d1.push_front(200);d1.push_front(300);for(i=0;i<d1.size();i++){cout << d1[i] << " ";  //通过重载[]的方式,获取deque容器的每一个元素}cout << endl;for(i=0;i<d1.size();i++){cout << d1.at(i) << " ";  // 通过成员函数at()获取deque容器的每一个元素}cout << endl;cout << "第一个元素为:" << d1.front() << endl;  //返回容器中的第一个元素cout << "最后一个元素为:" << d1.back() << endl;  //返回容器中的最后一个元素}int main(){test_1();system("pause");return 0;}1234567891011121314151617181920212223242526272829303132333435363738394041deque 容器排序:1.sort(iterator begin,iterator end); 对区间 [begin,end) 之间的元素进行排序;(默认为从小到大,即升序)注意:对于支持随机访问的迭代器,都可以直接利用 sort() 排序进行排序(sort()算法其实是 STL 中很常用的排序算法,STL 中的常用算法后续也会更新出来(●’◡’●));这里提一嘴,STL中的常用算法只允许拥有 支持随机访问的迭代器 的容器使用,迭代器不支持随机访问 的容器是不可以使用这些常用算法的,但为了解决这样的问题,这些容器内置了一些成员函数,这些成员函数的功能与常用算法一致,这些成员函数就只能由对应的容器来使用了;#include <iostream>#include <deque>  //使用STL中的容器,得包含它的头文件 #include <algorithm>  //使用STL提供的算法,得包含它的头文件using namespace std;void printDeque(const deque<int> & d){for(deque<int>::const_iterator it=d.begin();it!=d.end();it++){cout << *it << " ";}cout << endl;}void test_1()  //deque容器排序 {int i = 0;deque<int> d1;//300 200 100 10 20 30 d1.push_back(10);d1.push_back(20);d1.push_back(30);d1.push_front(100);d1.push_front(200);d1.push_front(300);cout << "排序前:" << endl;printDeque(d1);sort(d1.begin(),d1.end());  //对于支持随机访问的迭代器,都可以直接利用sort排序进行排序 cout << "排序后:" << endl;printDeque(d1);}int main(){test_1();system("pause");return 0;}以上就是STL中deque容器的一些常用接口和用法啦O(∩_∩)O。笔记中有错误的地方,欢迎指出,欢迎大家讨论!
  • [技术干货] 一个杯子你怎么测,能写出20条测试用例吗
      1.功能测试:主要关注水杯基本功能:  1.1 水杯是否可以正常装水  1.2 水杯是否可以正常喝水  1.3 水杯是否有盖子,盖子是否可以正常盖住  1.4 水杯是否有保温功能,保温功能是否正常保温  1.5 水杯是否会漏水,盖住盖子拧紧后是否会漏水  2.界面测试:主要关注水杯外观、颜色、设计等方面:  2.1 外观是否完整  2.2 外观是否舒适  2.3 颜色搭配及使用是否让人感到舒适  2.4 杯子外观大小是否适中  2.5 杯子是否有图案,图案是否易磨损  3.易用性测试:主要关注水杯使用是否方便:  3.1 水杯喝水时否方便  3.2 水杯拿起放下是否方便,这里会衍生到水杯形状的测试  3.3 水杯装水是否方便  3.4 水杯携带是否方方便  3.5 水杯是否有防滑功能  3.6 水杯装有低温或者高温水时,是否会让手感到不适  4.性能测试:  4.1 水杯装满水时,是否会漏出来  4.2 水杯最大使用次数  4.3 水杯的保温性是否达到要求  4.4 水杯的耐寒性是否达到要求  4.5 水杯的耐热性是否达到要求  4.6 水杯掉落时,是否可以正常使用  4.7 水杯长时间放置时,是否会发生泄露  5.兼容性测试:主要关注水杯是否可以装其他液体,如果汁、汽油、酒精等  6.可移植性测试:主要关注水杯放置环境等  6.1 将水杯放在常温环境中,使用是否正常  6.2 将水杯放在零下的环境中,使用是否正常  6.3 将水杯放在高于正常温度的环境中,使用是否正常  7.安全性测试:主要关注水杯外观和各种异常条件下是否释放有毒物质等  7.1 当水杯装满热水时,水杯是否会烫手  7.2 当水杯装上水后,是否会产生有毒物质  7.3 把水杯放在零下环境时,是否会产生有毒物质  7.4 把水杯放在高温环境时,是否会产生有毒物质
  • [热门活动] 【预告】华为云携手超图软件,构筑更弹性、更稳定的云原生GIS
    云原生是云计算技术不断进化的产物,它与传统GIS(地理信息系统)行业结合,解决了海量数据的分析与计算瓶颈,提供了更稳定、高效、高可用的系统架构。云原生GIS适用于与位置、数据、可视化有关的数据共享、处理、分析、管理类项目。其弹性伸缩、故障自动恢复、跨平台部署等特性,为众多平台搭建、项目改造升级提供了解决方案。如何基于华为云云容器引擎部署云原生GIS?它又有哪些典型应用场景呢?2021年4月1日 19:00,华为云云市场·新生态直播间将邀请北京超图软件股份有限公司云产品研发中心产品经理周世杰,为大家带来《场景+应用,带你“硬核”解析云原生GIS》主题分享。周老师从事GIS软件开发与云计算技术研发工作十数年,具有丰富的云原生理论知识与实战经验,此次直播他将从云原生GIS的概念与背景入手,通过部署实践、应用场景分析、真实案例分享等方面对云原生GIS技术进行全面讲解,剖析基于华为云部署云原生GIS的优势所在。为了推广落地云原生GIS,超图软件(SuperMap)将繁琐的部署流程封装,通过SuperMap iManager实现了一键部署,大大降低人工与时间成本。华为云云容器引擎(Cloud Container Engine)是提供高可靠高性能的企业级容器应用管理服务,支持 Kubernetes 社区原生应用和工具,简化云上自动化容器运行环境搭建。用户可基于华为云 CCE 平台,快速部署云原生GIS,从而享受高弹性、高可用的GIS服务。云原生GIS具有二三维一体化的服务发布、管理与聚合功能,提供多层次的扩展开发能力,内置丰富的 Web 端应用,具备零代码可视化界面定制,并可实现智能化监控、一体化运维。自推出以来,已助力多个项目成功上线,并长期保持稳定、高效运行,其中包括省市级“一张图”平台、大数据服务平台、时空信息平台、CIM平台建设等项目。想了解更多云原生 GIS技术干货,请点击云市场直播间选择新生态在线直播第30期——《场景+应用,带你“硬核”解析云原生GIS》,直播期间(2021年4月1日19:00~20:00),超图软件嘉宾周老师将结合应用场景与实际案例,带你深度解析云原生GIS技术和应用方案,参会还可以赢取超图917大学GIS学院开发者训练营入场券、蓝牙音箱、双肩包等超值大礼!点击查看本期直播商品:超图云GIS管理服务器平台超图云GIS应用服务器平台超图云GIS门户服务器平台超图边缘GIS服务器平台【华为云云市场,助您上云无忧】
  • [热门活动] 【预告】华为云携手超图软件,构筑更弹性、更稳定的云原生GIS
    云原生是云计算技术不断进化的产物,它与传统GIS(地理信息系统)行业结合,解决了海量数据的分析与计算瓶颈,提供了更稳定、高效、高可用的系统架构。云原生GIS适用于与位置、数据、可视化有关的数据共享、处理、分析、管理类项目。其弹性伸缩、故障自动恢复、跨平台部署等特性,为众多平台搭建、项目改造升级提供了解决方案。如何基于华为云云容器引擎部署云原生GIS?它又有哪些典型应用场景呢?2021年4月1日 19:00,华为云云市场·新生态直播间将邀请北京超图软件股份有限公司云产品研发中心产品经理周世杰,为大家带来《场景+应用,带你“硬核”解析云原生GIS》主题分享。周老师从事GIS软件开发与云计算技术研发工作十数年,具有丰富的云原生理论知识与实战经验,此次直播他将从云原生GIS的概念与背景入手,通过部署实践、应用场景分析、真实案例分享等方面对云原生GIS技术进行全面讲解,剖析基于华为云部署云原生GIS的优势所在。为了推广落地云原生GIS,超图软件(SuperMap)将繁琐的部署流程封装,通过SuperMap iManager实现了一键部署,大大降低人工与时间成本。华为云云容器引擎(Cloud Container Engine)是提供高可靠高性能的企业级容器应用管理服务,支持 Kubernetes 社区原生应用和工具,简化云上自动化容器运行环境搭建。用户可基于华为云 CCE 平台,快速部署云原生GIS,从而享受高弹性、高可用的GIS服务。 云原生GIS具有二三维一体化的服务发布、管理与聚合功能,提供多层次的扩展开发能力,内置丰富的 Web 端应用,具备零代码可视化界面定制,并可实现智能化监控、一体化运维。自推出以来,已助力多个项目成功上线,并长期保持稳定、高效运行,其中包括省市级“一张图”平台、大数据服务平台、时空信息平台、CIM平台建设等项目。想了解更多云原生 GIS技术干货,请点击云市场直播间选择新生态在线直播第30期——《场景+应用,带你“硬核”解析云原生GIS》,直播期间(2021年4月1日19:00~20:00),超图软件嘉宾周老师将结合应用场景与实际案例,带你深度解析云原生GIS技术和应用方案,参会还可以赢取超图917大学GIS学院开发者训练营入场券、蓝牙音箱、双肩包等超值大礼!点击查看本期直播商品:超图云GIS管理服务器平台超图云GIS应用服务器平台超图云GIS门户服务器平台超图边缘GIS服务器平台【华为云云市场,助您上云无忧】
  • [技术干货] 混合云、边缘计算走向主流
    2021年3月22日,业界应用最为广泛的企业级Kubernetes管理平台Rancher发布了2020年Kubernetes行业调研报告,研究结果表明,2020年,更多的受访者在混合云环境中使用Kubernetes 运行容器,与此同时,更多的企业将功能和服务部署至边缘,最终进一步推动企业IT现代化的进程。2019年和2020年,Rancher分别对近1,000名专业人员展开了调查。调查结果表明,Kubernetes在不同行业连续两年保持了90%以上的采用率,而生产环境中的容器采用率从2019年的85%增长至2020年的87%。“从调研结果可以清晰地看到,用户持续推动容器在混合云和多云环境落地,92%的受访者将容器作为DevOps、IT运维、IT架构、应用程序开发和基础架构转型的关键部分。”Kubernetes和云:真实环境下的真实工具近1,000名在技术、工程、电信、银行、网络安全和咨询等行业工作的专业人员的反馈,他们主要专注于实现DevOps、IT运维、IT架构、应用程序开发和基础架构转型,92%的受访者选择使用容器来实现这些目标。另一方面,这些受访者均更倾向于采用Rancher所推出的Kubernetes发行版。36%的受访者将RKE作为其Kubernetes发行版,而32%的受访者使用K3s。受访者指出,在容器快速发展的大前提下,多层级控制和功能是RKE被广泛采用的关键原因。推动传统IT架构现代化受访者表示,容器化是推动传统IT现代化的关键路径。49%的受访者通过容器实现传统IT应用程序的现代化,而67%的受访者利用容器设计基于微服务的应用程序。通过使用Kubernetes,企业可以将使用了数十年的传统IT系统转化为基于微服务的容器应用程序,并通过Kubernetes进行编排。迁移到Kubernetes还可以使开发团队并行工作,从而减少了重复的可能性,简化开发并加速部署。在混合云环境中,开发团队还可以通过云托管集群完全替代某些本地工作负载,这些集群可以在像Rancher一样的Kubernetes管理平台上运行和管理,进一步实现IT架构现代化的战略。推动边缘环境生产落地容器在生产环境中的采用率逐步增长,也覆盖到了应用端。基于此,许多受访者使用容器来为客户提供各种服务,包括应用程序、边缘计算、混合/多云应用程序、内部应用程序、传统应用程序现代化和将传统应用程序迁移上云等。K3s等体积较小的Kubernetes已经使企业可以更容易地将功能和服务部署至边缘,它赋予了组织从试点项目转向生产环境的能力,并使其能够根据需要进行扩展。2020年,62%的受访者在边缘使用K3s,去年这一比例仅为50%。随着混合云网络的成熟,客户可以部署面向客户的云原生微服务应用程序,所需的维护量更少,迭代次数更多,并且可以随时间轻松添加新功能。我们完全有理由相信,在面向客户的环境中几乎完全采用由Kubernetes管理的容器的这一趋势将持续发展。结论随着公司将他们的网络、应用程序和流程发展成为一个更现代的框架,他们发现无论环境如何,Kubernetes 都可以随之改变。世界各地的组织正在利用 Kubernetes 解决方案来创建一个云原生微服务集群。他们还对单体系统进行现代化,建立强大的云架构,同时无缝管理现有集群。这是向加速和保护应用程序的时代迈进的一步,同时赋予了DevOps和工程团队利用未来“更聪明地工作”而不是“更努力地工作”的能力。“容器及Kubernetes是混合云时代软件定义基础架构的最佳选择,它们不仅能帮助企业实现传统IT基础架构的容器化改造,还能帮助企业释放混合云的全部价值。”通过结合容器及Kubernetes的可靠性和灵活性推动企业在任意场景进行无限创新,从而推动创新无处不在。”文章转载自:https://tech.china.com/article/20210325/032021_738288.html
  • [体验官] 部署k8s kubeadm join 无反应
    书读百遍,软件部署也一样,想要出坑就跟游戏打怪一样一样的,多试几遍!一开始搞不明白master和node是怎么关联到一起的: kubeadm init运行的时候会自动生成token,然后按照提示,在node上输入token运行 kubeadm join。再有就是不明白哪些在master上安装,哪些在node上安装,这种问题真的是小白变小黑的门槛![root@ecs-385f ~]# kubeadm join 192.168.x.x:6443 --token ************     --discovery-token-ca-cert-hash sha256:********************************b6f34c  W0322 22:27:13.158886    9533 join.go:346] [preflight] WARNING: JoinControlPane.controlPlane settings will be ignored when control-plane flag is not set. [preflight] Running pre-flight checks         [WARNING SystemVerification]: this Docker version is not on the list of validated versions: 20.10.5. Latest validated version: 19.03 ^C//卡在这里不动了。搜资料,通常join卡住原因有几个:kubelet起不来 (系统兼容性导致)kubelet能起来但是docker无法正常创建容器 (系统兼容性导致)不通apiserver (服务器网络导致)sealos的ipvs代理异常,规则没创建或创建不生效 (系统网络不支持ipvs nat)[root@ecs-385f ~]# ping 192.168.x.x 64 bytes from 192.168.x.x: icmp_seq=1 ttl=64 time=0.529 ms ^C --- 192.168.x.x ping statistics --- 4 packets transmitted, 4 received, 0% packet loss, time 3000ms rtt min/avg/max/mdev = 0.274/0.344/0.529/0.108 ms [root@ecs-385f ~]# systemctl status kubelet ● kubelet.service - kubelet: The Kubernetes Node Agent    Loaded: loaded (/usr/lib/systemd/system/kubelet.service; enabled; vendor preset: disabled)   Drop-In: /usr/lib/systemd/system/kubelet.service.d            â””─10-kubeadm.conf    Active: activating (auto-restart) (Result: exit-code) since Mon 2021-03-22 22:30:20 CST; 5s ago      Docs: https://kubernetes.io/docs/   Process: 9785 ExecStart=/usr/bin/kubelet $KUBELET_KUBECONFIG_ARGS $KUBELET_CONFIG_ARGS $KUBELET_KUBEADM_ARGS $KUBELET_EXTRA_ARGS (code=exited, status=255)  Main PID: 9785 (code=exited, status=255) Mar 22 22:30:20 ecs-385f systemd[1]: kubelet.service: main process exited, code=exited, status=255/n/a Mar 22 22:30:20 ecs-385f systemd[1]: Unit kubelet.service entered failed state. Mar 22 22:30:20 ecs-385f systemd[1]: kubelet.service failed. [root@ecs-385f ~]# curl -k https://192.168.x.x:6443 curl: (7) Failed connect to 192.168.x.x:6443; Connection timed out这里报错了,但是由于不知道这个测试是用来测什么的,去一个网络没问题的地方试试,对,就是master上:这里是一个网页文本显示,虽然是403,这个不用管它。推测是网络问题。返回控制台查询master的安全组,购买时以为默认会选全放通的,结果是web-server的.放通权限修改后,重新在node上运行测试,这次就成功了。[root@ecs-385f ~]# curl -k https://192.168.x.x:6443 {   "kind": "Status",   "apiVersion": "v1",   "metadata": {        },   "status": "Failure",   "message": "forbidden: User \"system:anonymous\" cannot get path \"/\"",   "reason": "Forbidden",   "details": {        },   "code": 403 } [root@ecs-385f ~]# kubeadm join 192.168.x.x:6443 --token *********     --discovery-token-ca-cert-hash sha256:*****************89ceb58d22b6f34c  W0322 22:37:32.704624   21395 join.go:346] [preflight] WARNING: JoinControlPane.controlPlane settings will be ignored when control-plane flag is not set. [preflight] Running pre-flight checks         [WARNING SystemVerification]: this Docker version is not on the list of validated versions: 20.10.5. Latest validated version: 19.03 [preflight] Reading configuration from the cluster... [preflight] FYI: You can look at this config file with 'kubectl -n kube-system get cm kubeadm-config -oyaml' [kubelet-start] Downloading configuration for the kubelet from the "kubelet-config-1.17" ConfigMap in the kube-system namespace [kubelet-start] Writing kubelet configuration to file "/var/lib/kubelet/config.yaml" [kubelet-start] Writing kubelet environment file with flags to file "/var/lib/kubelet/kubeadm-flags.env" [kubelet-start] Starting the kubelet [kubelet-start] Waiting for the kubelet to perform the TLS Bootstrap... This node has joined the cluster: * Certificate signing request was sent to apiserver and a response was received. * The Kubelet was informed of the new secure connection details. Run 'kubectl get nodes' on the control-plane to see this node join the cluster.在master上查看:
  • [虚拟化] Docker个人学习总结
    1 Docker入门基础1.1 容器级虚拟机化技术主机级虚拟化,          Type1:在硬件上安装虚拟机,hyper-v,没有宿主机         Type2:有宿主机及宿主机OS,虚拟机OS与宿主机OS不一样,如Vmware,workstations。容器级虚拟化:实现用户空间和宿主环境空间的隔离,保障了系统安全。在UTS内可以以名称空间互相隔离的,在同一个空间上创建多个名称空间,每个名称空间互相隔离,拥有各自的根文件系统,root用户,PID 和端口范围。       Namespace原生支持UTS、mount、IPC、PID、User、Net。Linux容器化需要内核支持namespace技术。所以容器是Linux内核封装的技术。1.2 Docker和容器关系1.2.1 Linux容器1、隔离与共享一台服务器运行着多个逻辑隔离的服务器进程,谁的运行环境都不希望影响到谁,也就是一个物理机需要虚拟出多个环境或容器,Linux提供一种创建和进入容器的方式,操作系统让应用程序就像在独立的机器上运行一样,但又能共享很多底层的资源。2、实现基础Linux容器功能是基于cgroups和Namespace实现的。(1)cgroups(control groups 控制组)cgroups是将进程分组管理的内核功能,通过cgroups可以隔离进程,同时还可以隔离进程的资源占用(cpu,内存等)情况,在操作系统底层限制物理资源,起到container的作用,进程可用的cpu资源由cpuset指定。(2)NamespaceNamespace让每个进程拥有独立的PID、IPC和网络空间。Namespace是通过clone系统调用来实现的。clone系统调用的第三个参数flags就是通过设置Namespace来划分资源的。Linux一共构建了6种不同的Namespace,用于不同场景下的隔离1.Mount - 隔离文件系统挂载点;2.UTS - 隔离主机名和域名3.IPC - 隔离进程间通信资源4.PID - 隔离PID空间5.Network - 隔离网络接口6.User - 隔离用户/用户组空间1.2.2 LXC基本概念根据Docker布道师Jerome Petazzoni的说法,Docker约等于LXC+AUFS(之前只支持ubuntu时)(作者2015-10-22更新:Docker0.9.0版本开始引入libcontainer,可以视作LXC的替代品)。其中LXC负责资源管理,AUFS负责镜像管理;而LXC包括cgroup、namespace、chroot等组件,并通过cgroup进行资源管理。所以只从资源管理这条线来看的话,Docker、LXC、Cgroup三者的关系是:Cgroup在最底层落实资源管理,LXC在cgroup上封装了一层,Docker又在LXC封装了一层,关系图如图1(b)所示。因此,要想玩转Docker,有必要了解负责资源管理的CGroup和LXC。Jail->vserver(chroot)->LXC, 大规模创建容器很难,管理不方便。1)LXC是什么LinuxContainer容器可以提供轻量级的内核级虚拟化,以便隔离进程和资源,而且不需要提供指令解释机制以及全虚拟化的其他复杂性。容器有效地将由单个操作系统管理的资源划分到孤立的组中,以更好地在孤立的组之间平衡有冲突的资源使用需求。LXC建立在CGroup基础上,我们可以粗略的认为LXC = Cgroup+ namespace + Chroot + veth +用户态控制脚本。LXC利用内核的新特性(CGroup)来提供用户空间的对象,用来保证资源的隔离和对于应用或者系统的资源控制。根据LXC官网(http://linuxcontainers.org/)的描述,LXC具有以下特性:与虚拟化相比,它的优势在于:a)不需要指令级模拟;b)不需要即时(Just-in-time)编译;c)容器可以在CPU核心的本地运行指令,而不需要任何专门的解释机制;d)避免了准虚拟化和系统调用替换中的复杂性。总结来说,就是LXC更加轻量级,具有更小的性能开销、更快的相应时间。linux contains 的技术是linux 内核的代码,并非Docker 开发出来的,Docker或者其他的虚拟化容器都是基于LXC 的技术,在基础的lxc 上包了一层代码,让LXC 更简单、更友好,更加好推广;下面就看下LXC 的三个技术 •chroot: 创建一个虚拟的根目录文件系统 【实质还是调用底层的文件系统】,不过是建立一个虚拟的,可以跟其他容器的虚拟文件系统相互隔离;但共享底层的文件系统•namespace : 命名空间可以提供一个进程相互隔离的独立网络空间,不同的容器间进程pid可以相同,进程并不冲突影响;但可以共享底层的计算和存储(cpu + mem)•cgroups: 实现了对容器的资源分配和限制,比如给容器1分配10core 30G 内存;那这个容器最多用这么大的资源;如果内存超过30G ,会启动swap,效率降低,也可能会被调度系统给kill掉LXC通过namespace进行资源的隔离,Gust1下的进程与Guset2下的进程是独立的,可以看作运行在两台物理机上一样。Contaniner管理工具就是对Guest进行管理的(创建、销毁)。下图是LXC与KVM技术的比较,KVM的优点是一个物理机上可以跑多个操作系统(Guest-OS),然后在每个操作系统运行应用,通过这种方式实现应用的隔离。而使用LXC技术直接可以在Host-OS的基础上实现隔离的。这就是LXC的优势--运行快。但是,如果有两个应用一个是在windows运行的,一个是在linux上运行的,这时只能使用KVM技术来实现了。1.2.2.1 Cgroups基本概念1)Cgroup是什么Cgroups是control groups的缩写,是Linux内核提供的一种可以限制、记录、隔离进程组(process groups)所使用的物理资源(如:CPU, Memory, IO等)的机制。最初由Google的工程师提出,后来被整合进Linux内核。Cgroups也是LXC为实现虚拟化所使用的资源管理手段,可以说没有Cgroups就没有LXC,也就没有Docker。Cgroups最初的目标是为资源管理提供的一个统一的框架,既整合现有的Cpuset等子系统,也为未来开发新的子系统提供接口。现在的Cgroups适用于多种应用场景,从单个进程的资源控制,到实现操作系统层次的虚拟化(OS Level Virtualization)。简单说是把系统级资源分成多个组,每组资源分配到特定用户空间来使用。Cgroups提供以下功能:a)限制进程组可以使用的资源数量(Resource limiting )。比如:Memory子系统可以为进程组设定一个Memory使用上限,一旦进程组使用的内存达到限额再申请内存,就会出发OOM(out of  memory)。b)进程组的优先级控制(Prioritization)。比如:可以使用CPU子系统为某个进程组分配特定CPUshare。c)进程组隔离(Isolation)。比如:使用ns子系统可以使不同的进程组使用不同的namespace,以达到隔离的目的,不同的进程组有各自的进程、网络、文件系统挂载空间。d)记录进程组使用的资源数量(Accounting)。比如:可以使用Cpuacct子系统记录某个进程组使用的CPU时间e)进程组控制(Control)。比如:使用freezer子系统可以将进程组挂起和恢复。2)Cgroup基本概念与术语任务(task):在Cgroups中,任务就是系统的一个进程。控制族群(control group):控制族群就是一组按照某种标准划分的进程,控制族群通常按照应用划分,即与某应用相关的一组进程,被划分为一个进程组,即控制族群(control group)。Cgroups中的资源控制都是以控制族群为单位实现。一个进程可以加入到某个控制族群,也可以从一个进程组迁移到另一个控制族群。一个进程组的进程可以使用Cgroups以控制族群为单位分配的资源,同时受到Cgroups以控制族群为单位设定的限制。层级(hierarchy):控制族群可以组织成hierarchical的形式,既一颗控制族群树。控制族群树上的子节点控制族群是父节点控制族群的孩子,继承父控制族群的特定的属性。控制族群树的示意图如图2所示。子系统(subsystem):一个子系统就是一个资源控制器,比如CPU子系统就是控制CPU时间分配的一个控制器。子系统必须附加(attach)到一个层级上才能起作用,一个子系统附加到某个层级以后,这个层级上的所有控制族群都受到这个子系统的控制。3)Cgroup子系统介绍a)blkio -- 这个子系统为块设备设定输入/输出限制,比如物理设备(磁盘,固态硬盘,USB等等)。b)cpu -- 这个子系统使用调度程序提供对CPU 的 Cgroup 任务访问。c)cpuacct -- 这个子系统自动生成Cgroup中任务所使用的 CPU 报告。d)cpuset-- 这个子系统为 Cgroup中的任务分配独立CPU(在多核系统)和内存节点。e)devices -- 这个子系统可允许或者拒绝Cgroup中的任务访问设备。f)freezer -- 这个子系统挂起或者恢复Cgroup中的任务。g)memory -- 这个子系统设定Cgroup中任务使用的内存限制,并自动生成由那些任务使用的内存资源报告。h)net_cls -- 这个子系统使用等级识别符(classid)标记网络数据包,可允许Linux 流量控制程序(tc)识别从具体cgroup 中生成的数据包。i)ns -- 名称空间子系统。Cgroup具有不同的挂载方法——“多挂载点”和“单挂载点”。子系统“多挂载点”挂载就是指不同子系统的文件挂载在不同的目录下,每个子系统各有一个挂载点,目录结构如图3(a)所示。cgroup对应服务cgconfig默认使用的就是“多挂载点”的方法。“单挂载点”则是指所有子系统的文件都挂载在同一个目录下,所有子系统都统一挂载在一个挂载点,目录结构如图3(b)所示。  1.2.2.2 NamespaceLinux Namespaces机制提供一种资源隔离方案。PID,IPC,Network等系统资源不再是全局性的(在Linux2.6内核以前是全局的),而是属于特定的Namespace。每个Namespace里面的资源对其他Namespace都是透明的。namespace是container中使用到的重要技术之一,是对系统资源的操作上的隔离。使Guest-OS1的操作对Guest-OS2无法产生影响。当然namespace的实现还在完善中,下面是3.8以上的内核实现的namespace。1. Mount namespace是对挂载的文件系统布局进行隔离。图中显示在Namespace1中的进程看到的文件系统的挂载方式是一致的,但是在Mount Namespace2中看到的是一另一种情况。2.IPC:处于同一namespace下的进程才可以进行进程间通信。3. NET NAMESPACE实现网络协议栈上的隔离,在自己的namespace中对网络的设置只能在本namespace中生效4.PID:我们通过fork来创建进程时可以为每个进程指定命名空间。linux下的进程关系是一棵树,所以有了父命名空间和子名字空间之分。在namespace2创建的P2进程有两个pid。第一个是在父命名空间的下的它的PID号,一个是在自己空间下的PID号。之所以有父pid号是因为P2最终还是在父命名空间下运行的,而为进程指定命名空间是为了让P2和P3实现隔离。5. User namespace中使用到了map转换,由于container并不是真正的虚拟化,所以在Guest-OS中创建的root用户会被映射到Host-OS中的普通用户中去。下图中的例子中,root用户在自己的namespace下创建了一个文件,那这个文件的所有者ID应该是0,当时在磁盘上存的时候文件UID会被转换为kuid,并且所有者ID为1000。想说名一点是在Guest-OS下你是个root用户,但是在Host-OS你只不过被转为一个普通用户而已。因为我们知道在Host-OS下已经有一个root用户了。6.PID namespace:linux下的proc目录是对整个系统状态的描述,用户可以通过查看proc目录来了解当前的系统状态。在proc目录下有很多数字,这些数字对应的是系统创建的进程ID,以前我们说进程是看不见摸不着的,但是通过proc目录我们的确可以看到一些关于进程的信息。每个进程下有个ns目录,在目下记录了该进程使用的到namespace。1.2.2.3 Chroot1.  linux chroot 机制的由来•root 用户启动一个daemon在linux 系统上启动一个daemon 必须用root 用户来启动,比如一个web 服务器(nginx/apapce 80端口)是在操作系统的接口(1-1024),只有root 有这个权限来启动这类接口;用root 户启动daemon 的程序也是一个自然的事情。  •安全问题日益变大随着安全的攻击越来越严重,如果任何一个提供TCP 服务的程序出现漏洞,那攻击者就获取到了root 权限,无疑是灾难性的。为了降低这个问题带来的风险,需要主动放弃root 权限,该用一个普通的用户(比如 admin/nobody) 进行运行。这样一旦攻击者获取到了这个程序的权限,也是此时运行用户的权限,对系统造成的危害相对要小。•chroot 机制的引入为了进一步提高系统的安全性,linux 系统引入了chroot 机制;chroot 是一个系统调用,程序可以通过调用chroot的函数库来 更改一个进程所能看到的跟目录  。比如httpd 软件安装在/usr/local/httpd 这个目录下,那这个进程(httpd) 只可以读、写到这个指定的目录: [ usr/local/httpd] ;这样即使攻击者获取进程的权限,也只能读写这个目录下的文件,这样就变的安全了许多,起码不会影响这个台机器其他的进程,和其他的机器的安全问题 。2. chroot 简介chroot 全程是change to root : 其中root 是根目录的意思,也就是改变(linux 根目录是/,也可以理解为设置)一个程序运行时参考的根目录的位置 # 根目录的参考linux 系统(原始的方案)  | 引入chroot 机制/                                 /lxc/usr                            /lxc/usr/bin                            /lxc/bin/sys                            /lxc/sys如上图我们看的一旦使用了chroot ,用户的春心就不是linux 系统的根目录,而是我们指定的/lxc (这个目录可以任意指定),所以chroot 确实可以修改根目录. 3. chroot 机制的意义•增强系统的安全行•指定程序访问的根目录,防止用攻击者可以通过程序的漏洞获取其他目录的读写权限; 4. chroot 机制在虚拟化中的作用chroot 机制因为安全问题才被引入的,但是在LXC 中却启动了举足轻重的作用,因为chroot 机制可以指定虚拟根目录,让不同的容器在不同的根目录下工作; •不同的容器进程参考的根目录不同,相互直接不影响•不同的容器因为共享linux 底层的文件系统,所以容器集成os的功能,实现轻量级!1.2.2.4 LXC总结通过对ns和Cgroups的介绍,一方面可以实现对系统资源的隔离,但是使用就非常不方便涉及代码实践、底层内核调用以及进程克隆等。通过LXC可以实现对这些工具的封装调用,简化了用户使用。但是LXC使用也不是很方便,比如需要创建出一个虚拟环境,通常会执行一个模板脚本,从指定官网下载对应的软件工具,在本地完成安装后,生产名字空间的运行环境。所以LXC的使用比虚拟机也不是很大方便,隔离性也不比虚拟机好。大规模使用和分发也不友好,后面出现docker。Docker是LXC的加强版,相当于是LXC的前端应用工具。容器是内核技术,docker通过简化容器的使用,得以普及。解决方案为镜像。利用容器管理引擎,通过镜像,来创建出名字空间所需的环境。 1.2.3 LXC与docker的比较:容器技术不是模仿硬件,而是在Linux内核里使用cgroup和namespaces来打造轻便的、将近裸机速度的虚拟技术操作系统环境。因为不是虚拟化存储,所以容器技术不会管底层存储或者文件系统,而是你放哪里,它操作哪里。 这从根本上改变了我们如何虚拟化工作负载和应用程序,因为容器速度比硬件虚拟化技术更快,更加便捷,弹性扩容的更加高效,只是它的工作负载要求操作系统,而不是Linux或特定的Linux内核版本。Docker并不是LXC的替代品,Docker的底层就是使用了LXC来实现的。LXC将Linux进程沙盒化,使得进程之间相互隔离,并且能够控制各进程的资源分配。 在LXC的基础之上,Docker提供了一系列更强的功能。不过现在Docker底层已不使用LXC,先后推出libcontainer和runC. 利用LXC做容器管理引擎,将用户空间需要用到的组件打包封装好,成为镜像文件。极大简化了容器的使用难度,使得容器使用得以推广。容器技术一方面给技术带来极大便利,同时也给维护带来很大挑战。 1.3 Docker基础概念1.3.1 docker是什么Docker是一个开源的应用容器引擎,可以轻松的为任何应用创建一个轻量级的、可移植的、自给自足的容器。开发者在本地编译通过的容器可以批量的在生产环境上部署。Docker类似于集装箱,各式各样的货物,经过集装箱的标准化进行托管,而集装箱与集装箱之前没有影响。Docker是一个开放平台,使开发人员和管理员可以在称为容器的松散隔离的环境中构建镜像、交互和运行分布式应用程序,以便在开发、QA和生产环境之间进行高效的应用程序生命周期管理。分层构架和联合挂载虚拟机原理需要在Host OS(内核)之上虚拟出一套虚拟硬件,在虚拟硬件之上重新安装操作系统,即为虚拟机操作系统(内核),在该系统之上安装APP,对外提供服务。可见虚拟化对硬件资源的消耗还是比较大,通常在20~30%。而容器概念在于在Host OS之上,提供一套虚拟隔离环境(内核态),在用户空间隔离出进程环境。1.3.2 Docker三个组件 1.3.2.5 镜像一个特殊的文件系统。操作系统分为内核和用户空间,对于Linux来说,内核启动后会挂载root文件系统为其提供用户控件的支持。而Docker镜像,就相当于是一个root文件系统。除了提供容器运行时所需的程序、库、资源、配置等文件外,还包含一些为运行时准备的配置参数。镜像不包含任何动态数据,其内容在构建之后也不会被改变。镜像实际是由多层文件系统联合组成。镜像构建时,会一层一层构建,前一层是后一层的基础。每一层构建完就不会再改变,后一层上的任何改变只发生在当前层。比如:删除前一层文件的操作,实际不是真的删除前一层的文件,而是仅把当前层标记为该文件已删除。分层存储的特征还使得镜像的复用、定制变的更为容易。甚至可以用之前构建好的镜像作为基础层,然后进一步添加新的层,以定制自己所需的内容,构建新的镜像。1.3.2.6 容器镜像(Image)和容器(Container)的关系,就像是面向对象程序设计中的类和实例,镜像是静态的定义,容器是镜像运行时的实体。容器可以被创建、启动、暂停、停止、删除等。容器的实质是进程,但与直接在宿主执行的进程不同,容器进程运行与属于自己独立的命名空间,容器也是分层存储。容器存储层的生命周期跟容器一样,容器消亡时,容器存储层也会消亡,任何保存于容器存储层的信息都会丢失。容器不应该向其存储层内写入任何数据,容器存储层也要保持无状态化。所有的文件写入操作,都应该使用数据卷、或者绑定宿主目录,在这些位置的读写会跳过存储层,直接对宿主发生读写,其性能和稳定性更高。容器消亡后数据卷的数据不会丢失。容器在整个应用程序生命周期工作流中提供以下优点:隔离性、可移植性、灵活性、可伸缩性和可控性。 最重要的优点是可在开发和运营之间提供隔离。1.3.2.7 仓库Docker Registry是一个集中存储、分发镜像的服务。一个Registry可以包含多个仓库(Repository),每个仓库只包含一种软件,但可以包含多个标签(tag,也就是版本),每个标签对应一个镜像。这三个组件的关系如下图,比如有两个仓库,分别是Redis和MySQL。 1.3.3 Docker组成客户端:docker pull build run服务器仓库          镜像image:是个模板,通过模板创建容器服务。通过镜像创建多个容器         容器container:docker通过容器独立运行一个或一组应用,通过镜像来创建的。启动 停止 删除等基本命令                   容器是一个简易的Linux系统         仓库repository:存放镜像的地方1.3.4 Docker架构Client-Server架构:•docker守护进程运行在宿主机上systemctl start docker•daemon进程通过socket从客户端(docker命令)接受命令来运行管理各个容器•容器是一个运行时环境,可以看做是运行中的精简版Linux系统         1.4 docker安装:docker文档:https://docs.docker.com/安装指导:https://docs.docker.com/engine/install/centos/推荐使用阿里云的docker镜像安装:https://blog.csdn.net/lvdingding/article/details/112862396dockerHUB地址: ce社区版,官方推荐ee企业版 hello-world流程底层原理docker是server-client结构的系统,docker的守护进程运行在主机上,通过socketo从客户端访问。dockerserver接受客户端的指令,就会执行docker命令。 1.5 Docker和虚拟机的不同:1.传统虚拟机,虚拟出一套硬件,运行一个完整的操作系统,在这个系统之上安装和运行软件。占用资源大,启动慢。2.容器内的应用直接运行在宿主机的内核上,没有自己的内核,也不需要虚拟硬件,因此容器轻便。3.每个容器互相隔离,每个容器内部都有自己的文件系统,互不影响。DevOps开发运维应用更快的交付和部署传统的交付部署:一堆帮助文档,安装程序docker:打包镜像发布测试一键运行         更便捷的升级和扩缩容         更简单的系统运维,测试环境基本一致         更高效的计算资源利用,docker是内核级的虚拟化,一台服务器上可以运行多个docker实例。         内核的作用在于资源的管理和分配,如内存虚拟化,CPU调用,IO的调度,生产服务,如Nginx等都运行在用户应用空间。1.5.1 Virtual MachinesVM(VMware)在宿主机器、宿主机器操作系统的基础上创建虚拟层、虚拟化的操作系统、虚拟化的仓库,然后再安装应用;1.5.2 Containers容器完全在容器主机的应用程序层中运行。在UserLAnd[1]中,OSs有内核空间,操作系统的核心在其中运行。然后是用户空间,所有与用户活动相关的东西都在这里运行。没有安装操作系统。容器共享主机的内核空间,类似于主机中的一个用户,LXC所实现的隔离性主要是来自kernel的namespace, 其中pid, net, ipc, mnt, uts 等namespace将container的进程, 网络, 消息, 文件系统和hostname 隔离开。但是,cgroup的隔离性要比kvm粒度大,并且很难metric指标。比如,磁盘的IO就很难隔离。导致一个容器的io增大,这个主机都有可能hang。容器实际上就是一个进程。当它在容器内时,感觉就像一台完全独立的机器。我们可以启动新的进程,当监控这些进程时,您将只看到容器内的进程。但是,如果您监控主机进程,仍然能够看到容器中运行的所有内容。 容器共享主机。那么他是如何在同一台机器上运行Ubuntu和CentOS呢? 因为容器实际上共享主机的内核。特定的Linux发行版都构建在同一个内核之上(尽管版本不同)。所有的包管理器、UI之类的东西,以及其他各种软件,都可以在用户空间中运行,这些软件使发行版独一无二,并创造出不同的Linux风格。具有不同发行版的不同容器可以在同一台机器上运行而不会产生冲突。 当涉及到平台设计时,这个内核共享事实还有其他重要的含义。例如,Windows容器将不能在Linux主机上运行。 不可修改 容器本质上是不可变的。 容器操作系统、库、实用程序和应用程序在构建时都是冻结的,在此之后它就不能更改了。所以,不需要以传统的方式更新容器。重新构建和重新部署。虽然有一些缺点,但是在可重复性、简化部署和可靠性方面有很大的提高。 镜像(image) 镜像其实是一个标准的文件包,它表示容器运行时文件系统的状态。这可以发布到注册表,也可以用作父镜像。 大多数镜像将构建在父镜像之上(父镜像通常也构建在另一个镜像像之上)。基本映像没有父镜像的。1.5.3 小结 简而言之,容器化为单个OS上的工作负载隔离提供了标准化的方法,而虚拟化为在一台服务器上安装多个OSs提供了标准化的方法。它们在业界都很突出,在云计算中经常一起使用。 因为容器没有安装完整的操作系统,所以它的重量更轻,因为下载和运行更快,存储更小。下图以尽可能简单的方式说明了上述差异。请注意使用VMs和容器运行相同的两个工作负载的堆栈组合。 如果大家理解了上面描述的容器和vm之间的关键区别,那么第二个图将提供对正在发生的事情的更深入的信息。大家可以清楚地看到这两种技术如何提供工作负载隔离。 记住,堆栈表示逻辑层次结构。我们知道这些容器都在主机操作系统上作为唯一的进程运行,而VM客户操作系统是成熟的操作系统,可以管理它们自己的进程。 现在,让我们看看vm和容器通常是如何一起使用的。假设我们想在云中运行Python Flask应用程序和Java Spring应用程序。下图描述了AWS上可行的状态。  Amazon EC2是Amazon的托管计算服务。这意味着用户不需要担心服务器或管理程序。我们只需选择实例类型(针对不同工作负载的不同特性)并部署VM。(国内的同学们还是使用阿里云吧) 有许多不同的Amazon机器镜像可供选择[3],或者可以创建自己的镜像。因为vm比容器更重。 我们可以在许多不同的服务中共同使用VM镜像。可以将基本操作系统、包更新、一些脚本、监视代理的安装和配置,以及其他操作和安全工具打包到vm中。 通常使用配置管理工具(如Ansible或Chef)来管理工作负载或服务。Chef已经是devops的标准管理工具。 1.6 docker为什么比VM快1.docker的抽象层比虚拟机更少2.docker利用的是宿主机的内核,VM需要guest OSdocker申请容器的时候不需要像虚拟机那样加载一个操作系统内核,避免引导耗时。虚拟机是加载Guest OS,分钟级的docker是利用宿主机的操作系统,省略了这个复杂的过程,秒级。 可见虚拟机对硬件资源的消耗很大,通常在20~30%。 1.7 Docker常用命令docker version版本信息/info 显示docker的系统信息,包括镜像和容器的数量/help 帮助命令官方命令地址:https://docs.docker.com/reference/镜像命令docker imagesdocker searchdocker pulldocker rmidocker rm 容器命令docker inspect 容器IDdocker top 容器ID进入当前运行的容器docker exec -it 容器ID /bin/bash  进入容器后开启一个新终端docker  attach 容器ID             进入容器正在执行的终端,不会执行新进程docker stop/start 容器ID从容器内拷贝内容到宿主机上docker  cp 容器ID:容器内部地址  宿主机地址  思考:在宿主机中提供容器文件的映射地址,实现在宿主机上修改达到容器内部内容的自动修改。---数据卷 docker  run  -it --rm tomcat ##之前都启动是后台启动,容器还可以查到,这个方法为用完就删除容器和镜像1.8 ElasticSearch实验https://www.elastic.co/guide/en/elasticsearch/reference/current/docker.html#docker-cli-run Docker stats:  1.9 PortainerPortainer是docker图形管理工具,提供一个后台面板供开发者使用。访问测试:登录创建用户后选择local本地仓库点击connect之后进入portainer主页  1.10 Docker持续开发工作流  2 Docker镜像详解2.1 镜像详解2.1.1 镜像是什么将应用和环境打包成一个镜像。镜像是轻量级、可执行的独立软件包,打包了运行某个软件(比如tomcat镜像)所需的所有内容,包括:•代码(tomcat代码)•运行时环境(OS、JDK)•依赖库•环境变量•配置文件等镜像底层基础是Union File System(联合文件系统):UnionFS:一种分层、轻量级且高性能的文件系统,支持对文件系统的修改作为一次提交来一层层的叠加,也支持将不同目录挂载到同一虚拟文件系统下。镜像由一层层的文件系统组成,通过分层进行继承。基于基础镜像,可以制作出各种具体的应用镜像。镜像运行时,一次联合加载多个文件系统,根据继承关系进行叠加,最终外部只看到一个文件系统,但拥有了完整的文件和目录结构。 2.1.2 Docker镜像加载原理分层构架,联合挂载。 下载镜像时一层一层下载就是这个概念。所以这也是为什么虚拟机是分钟级,容器是秒级的。 镜像实际有一层层的文件系统组成,即UnionFS。文件系统层级中主要关注bootfs和rootfs“”•bootfs包括BootLoader和kernel(操作系统内核),BootLoader主要是引导加载kernel。同Linux,docker镜像最底层是bootfs。Linux系统启动时,会加载bootfs,然后BootLoader加载kernel(Linux内核)至内存,完成之后内存的使用权由bootfs转移给内核,接着卸载掉bootfs。•rootfs包含了我们熟悉的Linux文件目录结构:/dev/ /proc/ /bin/ /etc/ 等。对于不同的Linux发行版(Ubuntu、centos等),bootfs基本一致(内核相同,都是Linux-kernel),而rootfs会有差别。why一个centos的docker镜像只有200M,而VMware的centos系统镜像几个G?对于一个精简的Linux系统,rootfs可以很小,只需要包括最基本的命令、工具和程序库就OK了。docker容器共用了宿主机的系统内核,只需要提供精简的rootfs就OK,所以docker的os镜像体积可以这么小,因此可以把docker容器看做一个精简的Linux系统。why一个tomcat的docker镜像反而比一个centos的docker镜像大得多每个应用级别的docker镜像,都是源于基础镜像(联合文件系统),类比Java中的Object类,一层层继承而来。 2.2 提交镜像Docker commit 提交容器成为一个新的副本实践如下:    3 Docker数据卷3.1 什么是容器数据卷Docker的核心是把应用和环境打包成镜像,那么数据呢?如果数据保存在容器中,那就无法保证数据的持久化。所以希望容器之间有数据共享功能。通过容器数据卷技术可以将容器中产生的数据,同步到本地。简单说,就是目录挂载,将容器内的目录挂载到宿主机。实现容器数据的持久化和同步操作,也实现了容器间的数据共享。Docker  -v 宿主机目录:容器目录 查看容器卷是否挂载成功,执行命令docker inspect 容器ID3.2 具名和匿名挂载如何确认是具名还是匿名,以及指定路径挂载?3.3 Dockerfile创建 这个卷一定是与外部目录有一个同步目录。该挂载方式为匿名挂载。对应容器外的路径3.4 容器数据卷创建容器数据卷创建目标容器 实现了两个容器数据的同步。多个也是可以实现数据的同步。实现数据在两个mysql中的数据共享  4 Docker dockerfile4.1 基础知识Dockerfile就是构建docker镜像的构建文件,命令参数脚本。 4.2 Dockerfile构建过程Dockerfile是面向开发的,以后发布项目,做镜像,就要编写dockerfile文件,这个文件很简单。Docker镜像逐步成为企业交付的标准,必须掌握。Dockerfile是构建文件,定义了一切步骤,源代码;dockerimages通过dockerfile构建生成的镜像,最终发布和运行产品。Docker容器是镜像运行起来提供服务的。4.3 Dockerfile指令 
  • [容器专区] 【开放性】当主站ip地址与容器网桥地址同网段时处理配置方法
    问题背景:当前使用AR-CORE融合终端(版本:R20C00SPC100)在陕西送检时反馈,业务主站ip地址为192.168.100.11/24,设备ip地址配置为192.168.100.222/16,容器IP地址192.168.100.2,因同网段导致pc无法登陆设备及容器;解决方案:1、将GE0帮到br0网桥下:命令行如下ip link set dev GE0 master br0,缺点:不能将LTE0绑到br0网桥下,导致现网不能使用;2、配置对应接口的明细路由,命令行:ip route add 192.168.100.11/32 dev GE0,将主站ip地址作为明细地址,此时在pc端可以ssh登录设备,但还无法登录容器,此时需要再添加一条指令:ip link set dev br0 proxy,此时可以通过nft配置的端口号登录容器,LTE同理(ip route add 192.168.100.11/32 dev LTE0;ip link set dev br0 proxy)。以上方案二选一即可,不可同时使用。ps:存在以太口和LTE同时配置后设备需要切换的场景,具体逻辑如下:以太网、4G同时在线,是可以配两条指向同样主站ip的路由的,配路由的时候在命令后面加metric参数可以指定优先级,metric越低优先级越高,配FE口的路由metric比LTE口低就行;配置如下:ip route add 192.168.100.11/32 dev GE0 metric 100ip route add 192.168.100.11/32 dev LTE0 metric 120
  • [技术干货] 一个合格的CloudNative应用:程序当开源软件编写,应用配置外置(1)
    作者名:关耳山石作者博客链接:https://bbs.huaweicloud.com/community/usersnew/id_1593343710234702 摘要:对于一个合格的CloudNative应用,应该把自己的程序当做开源软件来编写的,不该将数据库连接信息和密码放在代码里,一定要将配置外置。对于一个合格的CloudNative应用,应该把自己的程序当做开源软件来编写的,不该将数据库连接信息和密码放在代码里,一定要将配置外置。因此我试着在华为云上落地这套标准,期间尝试了从ServiceStage、CCE、CSE这三个入口进行配置注入,最终实现能够在应用启动时,主动拉取配置,覆盖本地配置文件里的调试配置,并能够在线配置并生效。打法是:CCE配置启动参数,制定SpringProfiles;配合CSE做应用配置,将资源外置后的配置记录于此,并可以动态更新,最终实现了配置外置的诉求。另外,通过这次增加的多版本管理,尝试梳理了一下ServiceStage的组件、CCE的workload、CSE的应用和微服务之间,错综复杂的概念之间的关系,个人浅见,欢迎指正:1. 初始化配置在系统初始化的时候,不可能一点配置都没有,所以保留一些基础配置在配置文件里是有必要的。配置外置并不意味着100%的配置都要外置,而是把外部依赖资源的配置,特别是容易变化配置外置。1.1 启用Bootstrap和Spring profile在代码层面,首先要进行基础配置。先看代码结构,这里除了最基本的application.yaml,增加了bootstrap.yaml和*-dev.yml等带后缀的文件。介绍一下原理:首先介绍Bootstrap Application Context:它是Spring Cloud Context的父Context,所以从外部配置源里加载配置一般从这里来,且优先级高于其他一切配置文件。于是,我们也利用这个能力,借助CSE的微服务配置中心能力,进行Spring的外部参数写入。然后介绍Spring profiles:为了把通用配置和环境相关的配置区分开,比如DEV环境没有AK/SK认证,而线上环境都有认证,这种配置项上的区别,引入了Spring profiles的概念,当Springboot启动的时候,会根据profiles.active参数判断应该启用那个环境配置PS:参考SpringCloud官方解释另外,就是哪些配置应该放在配置文件里,理论上除了最基础的配置,在启动时使用的,其余的配置都可以放在CSE的微服务配置中心里,而不必在本地配置文件里存在,另外就是本地配置里存在的,也可以通过二次设置在微服务的配置中心里,进行覆盖,比如数据库连接池的大小,而不需要修改代码,至于改完以后,要不要重启,那就得看生效的逻辑了。1.2 引入CSE配置中心CSE配置中心引入,需要先引入依赖包,然后在bootstrap.yaml中配置CSE配置中心的地址、认证信息等。这里可以参考官方文档,主要关注bootstrap.yaml的配置参数:使用分布式配置中心补充:获取spring-cloud-starter-huawei-config的版本参考地址:huaweicloud/spring-cloud-huawei可以参考现有的SpringCloud基线版本,选择SpringCloud-Huawei的版本补充:关于配置中心的优先级微服务引擎提供了分层次的配置机制。按照优先级从高到低,分为:配置中心(动态配置)Java System Property(-D参数)环境变量配置文件参考官方文档:配置微服务2. 本地CSE配置中心能力验证前提条件,本地得安装一个Local-CSE并启动,这部分参考云上DevOps:2.1-本地环境准备2.1 配置application.yaml和bootstrap.yml原始文件可以参考Github的Demo,配置文件入口,我这里进行了修改,因为bootstrap.yaml优先级高于application.yaml,参数只要在bootstrap.yaml中出现过,就不会使用application.yaml中的配置了。上代码,application.yaml,可以看到配置很少,因为大部分bootstrap.yml中已经包含,就都去掉了这是bootstrap.yml,注意这里的spring.application.name、spring.cloud.servicecomb.discovery.appName和spring.cloud.servicecomb.discovery.version会组成一个微服务私有的参数作用域,这里的优先级高于全局作用域application。特别注意,云上的CSE环境,除了上面提到的,还需要增加server.env参数。另外,server.env有四个参数可以选development、testing、acceptance、production关于如上参数的定义,参考官方文档:使用分布式注册中心和官方文档:使用分布式配置中心。2.2 准备验证代码直接上代码2.3 静态配置能力为了验证CSE配置中心的参数,是否能覆盖application.yaml中的参数配置,我选了一个很特别的参数:spring.datasource.password,如果能够覆盖成功,那么启动后,创建数据库连接池一定会报错。首先,打开本地CSE配置中心,地址为:http://localhost:30106/#/cse/services/config,创建一个配置项,作用域选VodMgrService@CabgOne#1.0.0,关于application的作用域后面再解释。重启本地微服务,启动后创建数据库连接池就报错了,符合预期。结论:CSE配置中心的参数,能够覆盖application.yaml中的参数配置2.4 动态配置能力为了验证CSE配置中心的参数动态生效,需要使用注解@RefreshScope,同时也引入ConfigRefreshEvent来监听事件变化,这样就会得到一个效果,对于动态生效的参数,可能需要一些重建或刷新,比如连接池、缓存、Client等。首先,增加一个配置项config.value然后很快可以看到后台日志打印出来,包括自己的监听器,也响应了日志查看一下数据,已经响应为TryMe具体的配置,在后台是轮询的,响应周期配置参数为cloud.servicecomb.config.watch.delay,目前是10秒一次修改一下配置,为TryMeAgain
  • [维护宝典] Kafka生产发送数据失败
                    我们使用kafka时,有时候会遇到发送数据失败的情况,其原因及解决方案如下:1.     Kafka topic leader为-1Kafka客户端执行如下命令查看topic的leader信息:kafka-topics.sh --describe --zookeeper zk业务IP:24002/kafka如果leader为-1,需查看Replicas中的副本节点是否正常,查看命令如下:kafka-broker-info.sh --zookeeper zk业务IP:24002/kafka命令中可以查询到brokerid,说明节点kafka服务正常如果leader为-1的分区对应的Replicas中节点都不正常,需要先恢复异常节点kafka服务。如果都正常但ISR列表中无节点信息,或者ISR列表中的节点不正常,需要查看Kafka服务端的unclean.leader.election.enable参数是否为true或者topic端是否配置此参数为true。如果都不为true,需要对此topic修改配置为true。Kafka服务端的unclean.leader.election.enable参数配置查看方式如下:                                             Kafka topic端unclean.leader.election.enable参数配置查看方式如下:kafka-topics.sh --describe --zookeeper zk业务IP:24002/kafka --topic topicName如果Config中无unclean.leader.election.enable信息,则与服务端配置一致。如果有,则此配置优先级高于服务端配置。修改topic端此配置的方式如下:kafka-topics.sh --alter --topic topicName --zookeeper zk业务IP:24002/kafka --config unclean.leader.election.enable=true2.     DNS配置导致执行 vi /etc/resolv.conf,如果有“nameserver X.X.X.X”,把此内容注释掉3.     网络异常生产端节点ping服务端IP,ping -s 15000 IP,如果延迟高于5ms,说明网络延迟过高,也可通过长ping来判断是否有网络丢包。4.     CPU或者IO过高也可能导致连接失败iostat -d -x 1查看CPU参数idle和IO参数util,idle值越高,CPU越空闲,util值越高,IO使用率越高。如果idle值小于20%,top -Hp kafkaPid查看CPU使用率高的线程,打jstack日志分析具体的原因;如果util值大于80%,查看磁盘对应的读写速率、await和svctm的大小判断对IO影响大的原因,如果读写速率比较大,排查kafka读写请求和延时,如果awati远大于svctm,IO队列太长,应用响应时间很慢。5.     磁盘坏道或者其他原因也可能导致连接失败如果server.log日志中有大量“java.io.IOException: Connection to IP:21007 (id: 2 rack: null) failed”日志,查看IP节点操作系统日志中是否有“Sense Key : Medium Error”信息,如果有,说明出现磁盘坏道,需修复或更换磁盘。另外,还需要打此节点的jstack信息,排查是否有阻塞或者死锁。如果是C80版本,且jstack信息中有如下信息,需打死锁补丁:6.     如果报“TimeoutException”或偶尔发送失败,可先调大request.timeout.ms,并查看服务端num.io.threads和num.network.threads是否可以优化(这两个参数一般调整为节点磁盘个数的倍数)。根据发送的数据量的大小也可适当调整batch.size、buffer.memory、linger.ms的大小。如果发送的数据量很大且可容忍一定时延,也可以考虑开启压缩,compression.type指定压缩方式,可配置为“gzip”、“snappy”或“lz4”。7.     ssh卡住ssh -v -p 端口号 异常节点ip如果如上图所示卡住,解决方式是将GSSAPIAuthentication修改为no8.     如果是集群外客户端生产发送失败,还可以通过集群内客户端测试下生产是否成功,进一步减小排查方向。
  • [热门活动] 如何选到一款标准、安全、高可用并具丰富功能的企业级应用服务器?
    TongWeb应用服务器是一款标准、安全、高可用并具丰富功能的企业级应用服务器,为企业级应用提供了便捷的开发、随需应变的灵活部署、丰富的运行时监视、高效的易管理等关键支撑。产品两大主要功能:1,提供应用中文容错性、多框架兼容性、动态加载、多样性配置管理、系统运行状态多方面分析、集群管控、应用性能监控等能力,操作简约容易。2,提供应用运行的基础框架,包括但不限于容器、线程、日志、接口、安全、监控、界面、配置参数、优化等功能实现。TongWeb应用服务器提供了各种容器和功能组件,包括Web容器、EJB容器、RMI服务容器、Web服务平台、JCA服务、数据库连接池、事务控制组件等,并支持各种成熟开发框架,以帮助企业快速构建各种业务应用处理系统,为企业级信息化建设构建基础应用平台。 TongWeb可以通过使用集群功能实现负载均衡和备份,以增强应用的健壮性和稳定性。同时通过动态扩展的功能实现集群部署的动态管理。TongWeb应用服务器的集群功能提供跨多种平台服务器的集群部署配置以及故障切换,从而快速适应企业现有软硬件环境并可确保关键应用和服务高效可用。 产品还提供多种方式以提高企业级应用的安全性,从而限制对应用的访问,保障企业数据的安全,防止恶意攻击。通过TongWeb应用服务器提供的监控管理工具对服务的运行情况进行实时跟踪监控,并提供大量方便的日志管理功能以便用户进行审计。文中提到的的商品链接:东方通应用服务器软件(TongWeb)【华为云云市场,助您无忧上云】
  • [技术干货] 【DevOps职业认证训练营FAQ】DevOps&amp;敏捷8大领域60个精彩问答
    “DevOps的价值是又快又好地交付软件”——《凤凰项目》的作者Gene Kim和《持续交付》的作者JezHumble当前数字化转型的形势下,软件行业面临着巨大的市场机遇,而软件系统复杂度不断增加,跨地域高效协作、多环境部署等问题也逐渐突出,DevOps能帮助企业提升软件研发效率,通过自动化“软件交付”和“架构变更”的流程,来使得构建、测试、发布软件能够更加快捷、频繁和可靠。基于此,我们策划组织了2期【DevOps职业认证训练营】,并邀请到姚冬、卜汉东两位专家老师全程陪伴学习与答疑。在整理问答的过程中我们发现,学员提出的问题覆盖了规划设计、开发集成、测试、部署发布、运维监控等DevOps落地实践中的关键疑点与难点。本文从中挑选整理了60个精华问答,希望通过这些问题与解析,帮助更多DevOps实践者解决DevOps落地过程中的疑惑与痛点。(文末可下载大纲模式的pdf文档方便浏览)【一、华为端到端DevOps概览】Q1:华为端到端的DevOps工具链是如何承载敏捷和DevOps相关理念和方法的?A:敏捷和DevOps的理念其实是相通的,DevOps可以视作敏捷的延伸,敏捷思想打破了需求与开发之间的壁垒,DevOps则通过将开发与运维间的壁垒打破,打通软件交付全流程。华为云DevOps工具链DevCloud包含了从需求管理到代码托管、构建部署、测试等一系列步骤,覆盖软件开发全生命周期。理念往往需要结合实践,我们可以通过DevCloud进行需求管理、每日站会等等许多敏捷实践,通过提交代码可以触发执行流水线,让开发人员专注开发。Q2:华为云DevCloud与传统基于开源组件拼接的工具链,有什么差异优势?A:传统的由开源组件拼接而成的工具链,大部分都是使用Jira来进行需求管理、用Git来做代码托管、用Jenkins做DevOps开发,因为其组件大部分都是开源的,所以一般费用较低或者免费,其缺点是使用者需要掌握很多工具,而且这些工具并不是在同一个平台上。华为云DevCloud是一站式的软件开发平台,可以做到所有工具都在一个平台上,端到端打通覆盖整个软件开发全生命周期。用Jenkins的人都知道,在使用之前首先需要搭建一套Jenkins的环境,还需要定制化地做一些脚本、配置等,华为云DevCloud相当于是一个已经封装好了的DevOps开发工具,可以极大减少这些操作。在华为云DevCloud里,将编译构建、部署任务等做成了原子化的操作,如果我们想要做Tomcat部署,可以直接使用这些模板,只需要对里面的步骤进行细微的调整即可。而且它还使用了可视化视图,操作起来一目了然,学习成本也比较低。华为云DevCloud还支持代码检查、自定义shell、Python、脚本、自定义report展示。Q3:DevOps /敏捷和SDLC 有何不同?A:DevOps/敏捷和SDLC的角度不一样。SDLC是指系统生命周期,它提出的几种典型生命周期模型包括瀑布模型、快速原型模型、迭代模型。敏捷打破了需求和开发之间的沟通壁垒,DevOps则打通了整个软件交付的全流程。Q4:DevOps人员在与项目的结合中是否会承担更多开发、测试、运维的工作?A:DevOps不会让人去承担更多开发、测试、运维的工作。DevOps里有一个理念:让开发的人专注于开发、测试的人专注于测试、运维的人专注于运维,所有的工具层面的东西全部交给工具,只要把一切可自动化的东西自动化,所有的人忙自己手头的工作就好了。Q5:DevOps的反模式有哪些?A:参考《9种DevOps团队结构适用类型与7种反型》 Q6:DevOps适合哪些行业的业务模式?对于非软件行业是否需要调整模式?A:DevOps也好,敏捷也好,其初衷和理念适用于所有行业,但是每个行业在执行和实际落地效果上会有一些折扣,比如持续交付的生产环境、自动化部署、质量管控、自动化流转等过程的实现等。简单而言,互联网的一些应用,或者说SaaS应用,相对来说更适合DevOps的研发模式。原因是:其业务对软件更新、发布的要求较高;没有太大的历史包袱;相对更容易对标目标受众群体,包括生产环境等。传统类的业务比较重,比如银行的核心系统,实践起来相对较难,也不是说不能用敏捷或DevOps。比如持续集成、每天多次构建、多次提交代码、自动化测试、可视化等,都可以实行。对于非软件行业,如硬件、嵌入式、机械类,实践起来也比较难,比如测试自动化等,需要做一些工具或平台的适配,引进插件或工具后,流程也能够跑起来,只是会慢一些。综上,我认为敏捷和DevOps本身是一条没有终点的路,所有行业都可以到这条路上来,只是走得难易与远近的问题。Q7:在企业落地DevOps有没有什么套路?A:企业实际情况各不相同,落地DevOps没有统一的套路,但会有一些建议的方式。DevOps偏工程侧,通常建议先把版本管理建立起来,比如Git代码仓、代码分支管理等;接下来需要把流水线构建起来,在上面逐渐进行自动化测试、分层测试等。Q8:最能有效促进Scrum团队本身的持续改进的是什么实践?A:每个团队遇到的问题都是不一样的,如果一定要找一个通用的答案,首先要保证团队每日站会、评审会议等如质如期进行,以此来保持持续改进。 【二、持续规划与设计】Q9:基于DevOps实现持续有效规划应该先从哪个层面去入手呢?A:首先需要理解DevOps和敏捷的含义,我们一般说的规划与设计更偏向于敏捷项目管理中涵盖的需求和计划。狭义的DevOps主要是CI/CD,即持续集成和持续部署,是偏工程侧的。广义的DevOps,即本训练营中讲的DevOps是“端到端的DevOps”,从持续集成/持续部署,向前延伸到业务侧,向后延伸到运维/运营侧,因此也涵盖了前段的需求和设计层面。回到问题,基于DevOps实现持续有效规划,应该从需求和计划切入,包括整个的市场分析、目标客户群体的用户画像,用户的痛点是什么,针对这些痛点提供什么样的功能,然后到产品应该怎么设计,接下来才真正落到研发这个主体上。从方法论角度来看,需求和设计层面的方法论包括设计思维、精益创业等。做好需求分析后,就要进行需求拆分,排列优先级,这样就进到敏捷项目计划里,方法论包括看板、Scrum等,大规模团队敏捷框架有SAFe等。Q10:Scrum,看板和 XP 是敏捷开发的具体方式,老师能否具体讲解一下区别?A:参考文章《DevOps VS 敏捷:傻傻分不清楚》。Scrum和看板更侧重在团队级敏捷项目管理层面,XP更偏向于工程实践层面。Scrum和看板两者比较:“标准的”Scrum包括3355的框架;看板源自丰田的精益生产,其背后是精益的思想,通过可视化、限制在制品的数量,快速暴露问题和瓶颈点,集中对最严重的瓶颈点进行修复,然后去寻找下一个瓶颈点。DevOps的很多理念同样借鉴了精益的思想,个人认为,看板可以应用到很多领域。另外,Scrum和看板在实施或应用时并没有冲突,可以结合起来使用。 Q11:企业组织架构中什么角色或者部分适合推行DevOps落地?A:企业组织架构中一般都没有专门的组织来推行和落地DevOps。DevOps包括两个部分“Dev”和“Ops”,就是指开发部门和运维部门。几种常见的情况:如果是由开发部门来发起DevOps落地,就是由开发往运维去推进。我们平时看到比较多的是测试团队或传统的质量管理部门来发起,从开发到测试再往前一步到运维生产环境上去,因为这些部门本身就承担着代码托管、编译构建、自动化测试等职能。而有的公司会把内部的基础设施、IT支撑、测试等放在数据中心,往前去推把自己变成类似我们讲的DevOps工程师,然后通过自动化工具帮助开发团队进行自动化部署等,这就是从运维侧往前推进DevOps落地。还有一种情况,就是近年来比较火的云原生,架构师更多考虑采用微服务架构,通过基础设施即代码等方式自动化部署到Docker环境中去,因此引入自动化流水线、Infrastructure as Code(基础设施即代码)、接口测试等实践,这些都属于DevOps的范畴。还有一些其他的角色,比如敏捷教练、内部的技术教练等,他们本身就是在做研发管理的落地实践,很自然地转化去做DevOps推进。综上,DevOps的推进和落地不一定非要有一个DevOps工程师或独立的DevOps团队,初期引入DevOps的时候需要有一个团队或角色去承担起这个职责,进行概念和实践的导入和探索,这时更容易把DevOps工程师、DevOps团队建立起来。而后期应该把这些工程师或能力分散到各个团队中去,让DevOps在企业内有更广泛的传播和实践。Q12:请问在Scrum中,如果没有项目经理,是由TeamLeader还是ScrumMaster协调资源?A:应该由TeamLeader来协调资源,ScrumMaster不是管理角色,而更多的是一个辅助的牧羊犬的角色,在Scrum实施过程中守护团队Scrum流程不受干扰。Q13:对于非产品形态的项目,Product Owner来自哪个部门更合适?(业务部门/研发部门)A:Product Owner代表客户,一般是哪个部门更接近业务,更了解业务和系统,就由这个部门的人来担任。非产品形态项目的Product Owner,要求既了解业务又懂技术,一般可以由业务分析师、PMO等角色担任。Q14:实际开发中,客户往往无人承担PO的角色,而是领导来承担,如何破解这个问题?A:这种情况可称为“BDD”,Boss-Driven Development,老板驱动开发。好处是至少有一个人能拍板;坏处是拍板的人,你可能很难去辩驳或谈判,所以最好还是能够把客户侧的人拉进来。当然,如果老板确实对业务非常了解,也非常专业,并且是一个可沟通的人,也是可以的。PO的核心要求是需要有一个人代表客户或业务侧,针对需求或范围做决定,且当团队有问题的时候,可以随时找到这个人。Q15:影响地图主要应用于哪个环节?A:从HE2E DevOps实施框架图可以看到,在端到端的DevOps实践中,影响地图通常用于需求规划或业务规划阶段,与传统的Scrum流程相比,更偏业务侧。影响地图通过四层结构:why、who、how、what来拆解业务和需求,也可以用于运营或项目冷启动环节。Q16:请问如果一个大的Story拆分成多个小的Story,甚至再次拆分成孙子辈的Story,如何更好地表示这些关联关系?A:Story拆分有两种方式:一种是从epic(史诗故事)到feature到story的拆分,epic以月为单位,feature以周为单位,story以天为单位;另一种是平级拆分,所有拆分的故事全部叫story,只不过它们之间存在父子关系。不管是三层还是四层,我们只关注父子关系,从一个父story拆分出子story,如果粒度不够小,则以子story为父story继续拆分出它的子story。如果系统需要有层级追溯,可以用树状或脑图等结构来展现。Q17:学完课程感觉用户故事和项目管理里的工作包很像,二者有个共同的问题,拆解到什么粒度是好的用户故事?A:故事也好,需求也好,只是一个名字,用户故事之所以叫用户故事,有两点表征:1)它是站在用户的角度去看;2)它讲了一个故事、一个场景。好的用户故事遵循INVEST原则,即一个合适的用户故事应该是独立的(Independent)、有价值的(Valuable)、可讨论的(Negotiable)、小的(Small)、可估算的(Estimable)和可测试验证的(Testable)。Q18:如果采用敏捷开发,最终的用户需求如何呈现给用户?如果是需要存档的用户需求说明书、设计说明书或操作手册之类的文档,适合从DevCloud导出后再修改么?另外如果出现变更,如何确保文档与代码一致?A:如果是需求文档,可以以用户故事的形式存放,华为云DevCloud或者其他工具都提供多元的存储格式,如文本、图片、附件等,华为云DevCloud有一个帮助网站,每一个新上线的功能都会在这里进行同步和更新。也可以把词条或需求存放到wiki里,并跟前端的需求条目之间建立链接。wiki本身是可以有层级关系的,可以把需求从wiki里导出来形成文档形式,如果做得好,还会有版本计划,比如版本里包括10条需求,可以统一导出一篇需求规格说明文档。需求和代码之间的同步,可以通过流程等方式去控制,比如发版的检查点,这可能需要以人工方式去做,但也可以通过一些工具来辅助。比如提交代码的时候需要提交注释,可以把这个注释关联到一个工作项上,一个需求可能会修改多个文件里的多段代码,这其实就是一个完整的变更集的概念,这个变更是为了同一个目的,是有相关性的,如果要从代码里去剥离的话,应该会把这一次变更集统一进行剥离。在未来查看代码时,可以进行代码版本比较,看两个版本之间进行了哪些增加/修改、这些变更是为什么目的、其意图是什么。Q19:对于变化的需求或者新增的需求,是应该放到当前迭代里,还是规划到后面的迭代里,持续规划是指规划过程贯穿整个生命周期么?A:变化或新增的需求都会统一放到一个大的池子里,我们称之为product backlog(产品待办事项列表),这是一个一维的表格,所有需求按照优先级排列。我们要通过判断新进需求的优先级,看它应该放在什么位置。敏捷强调需求是动态变化的,我们会定期对需求列表进行梳理,看是否需要进行优先级排序的调整,因此变化或新增的需求不会放到当前的迭代里,因为当前迭代是一个固定的时间窗口,且范围相对固定,团队对此进行了承诺。我们会将其放入大的需求池,是在下个迭代还是之后的迭代实现,取决于该需求的优先级。Q20:对于初学者刚刚接触一个项目,但是项目的需求不明确、结构不成熟,怎么从敏捷入手?A:这里包括两种情况:初学者、项目在初级阶段。如果是初学者,应该通过获取现有资产快速熟悉和上手;如果项目处于初级阶段,需求也不太明确,可以通过敏捷的快速交付、精益的MVP等实践,快速获取反馈,对后续工作进行指导和建议。Q21:作为整个项目的入口,需求的质量如何把控和评测?A:明确定义需求可以转开发的标准,即DoR。那什么是DoR呢?敏捷开发发展了几个年头之后,人们发现进入迭代开发应当满足一定条件,否则过于模糊的需求会导致迭代的失败,在迭代内花费过多的时间去做需求澄清,因此给进入迭代设立门槛,就是Definition of Ready,简略称之为“DoR”, 最初的Ready是指准备好可以进入迭代开发。Q22:持续规划与设计有什么度量数据或指标用于衡量团队绩效或用于持续改进?如何衡量持续规划与设计的成熟度?A:度量工具推荐Scum的燃尽图、看板的累积流图。研发效能的核心度量数据指标包括团队速率、Lead time,即需求的平均交付时长。Q23:敏捷下的组织过程资产(配置、文档等)这些有好的存储方案么?A:理论上文档、资产等都存储在资产库里,常用的知识库或资产平台有Conflunce、IBM的Rational Asset Manager等。资产和知识是不同的概念,现在做资产管理的相对少一些,知识库可以用wiki等平台,便于统一维护更新和协同。Q24:DevOps 持续规划与设计在DevOps生命周期中是处于开始的时刻,为什么还说代码集成是整个DevOps生命周期的核心呢?A:“代码集成”包括两部分:代码和集成。整个软件生命周期包括三个版本:需求版本,即发版计划;代码版本;上线发布的二进制包的版本。其中代码版本处于承前启后的中间位置,且是唯一真正有价值的。需求和文档是没有价值的,只有由代码编译成二进制包并部署上线才是有价值的。在代码层面多花一些精力是非常有必要的,所有的研发其实都是在一个代码仓库里进行协同开发,包括代码版本管理、分支管理等模式,因此将代码视为DevOps生命周期的核心也是必然的。软件研发最痛苦的地方往往是在集成层面,一开始大家各写各的代码,一旦要将这些不同的代码进行集成的时候,问题就出现了。持续集成的概念来源于XP,“如果代码集成是一件非常痛苦的事情,那我们就每天多次地进行。”一切杀不死你的都会让你更强大,持续地进行集成,你会想办法去减少集成的痛苦。就像跑步一样,假如以前的集成是一块大石头,每天多次集成就相当于将这块石头变成一颗颗的小石子,大石头打在身上会非常疼,小石子就好多了。这也是我们为什么要把集成往前提,并且持续去进行的原因,所以在DevOps生命周期中持续集成也非常重要。【三、持续开发与集成】Q25:如何加强开发人员对于版本质量的信心?A:加强对版本质量的信心,不只是针对开发人员,对所有人都应该如此。整个DevOps的过程其实就是在保障整体的版本质量,包括静态代码检测、接口API测试等。另一方面,版本对需求的映射关系或完成程度,应该从业务场景往下去切,看整个需求的匹配程度。第三点应该是我们通常说的非功能性需求(Non-functional requirements),比如负载、性能、安全、并发支持等,这些要根据我们服务承诺的质量来做相关措施。 Q26:敏捷开发相比传统开发有什么优点?A:我认为最大的优点或特点是敏捷开发更真实,或者说它更愿意承认研发的本质或现状。传统的研发认为质量受三个因素制约:范围、资源、时间,且默认范围和资源投入是相对确定的,时间是变化的。然而,在真实场景或变化的市场下,时间和资源是固定的,没办法讨价还价,因为市场、业务、客户都不会等你,在这样的前提下,软件的需求或范围实际上是可以商量或讨论的,我们要以可变的范围去赢得市场、时间窗口。敏捷开发要求我们不断交付高优先级的需求,并获取反馈,不断调整。这是敏捷开发的最大的核心,承认市场是变化莫测的,需求范围是可变的。Q27:一个产品,既有主线版本,又有很多的行业定制分支(50+),适合什么样的分支策略?A:这种场景在传统的产品里比较常见,个人认为应该考虑的是产品策略而不是分支策略。如果分支非常多,会导致产品碎片化严重。我们在持续集成、持续交付的时候,推崇主干开发或短的分支,不希望这些分支长期存在,否则在产品进行合并时会非常痛苦,工作量也会随着分支的多少和分支存在的时间呈几何倍数增长,所以不建议用长期存在的分支。那可以用什么样的方式来解决呢?首先要看整个版本上是否一定要出现这么多定制化的分支,这些分支有没有可能通过配置文件、功能开放等方式处理或实现。举个例子,我们做项目管理的软件,每个客户要求的字段、功能流转的流程都不太一样,如果都通过代码实现,有多少客户就会出现多少个分支,可能都不止50个。我们是怎么做的呢?针对字段,我可以配置一个界面,里面包括常见属性的字段,这个字段可以是文本类型或下拉框等形式;功能流转的话,新进来一个需求,它的下一个状态是什么、应该触发什么动作、应该是什么样的角色来触发这个动作等这些都是可以进行配置的,这些配置信息存在数据库里,变成用户的配置数据,这样我的主代码主程序是保持不变的,只需要提供一套模型根据数据去驱动适配或实现。这是我们更推崇的方式,可以用来消灭那些分支。Q28:日常项目开发,在代码分支管理上经常疑惑用什么分支管理策略,比如是选择基于生产分支工作流,还是基于环境等等,在实际实践中,我们应该重点考虑哪些因素?既可以兼顾管理效率,又可以确保代码质量。A:个人建议采用分支开发主干发布或分支开发分支发布的分支管理策略。基于环境进行分支构建的话,以前我们会有开发库测试库等仓库管理的概念,但现在全部是持续集成、自动化部署,就没必要再基于环境去拉取分支了。如何保证代码质量,我们在CI/CD流水线、自动化部署和构建的同时需要考虑每一个环境上跑哪些测试,这些测试大部分通过自动化的方式实现,也有少量的是手工进行。Q29:像华为云这样团队成员能力超强、应用场景以线上服务为主,一般会采用什么样的分支管理模式?A:华为云团队也是采用特性分支的管理模式,同时会做多级流水线触发不同环境的流水线来做相关构建,除了开发环境的流水线以外,还有测试、类生产环境等流水线。Q30: 要做到主干上的提交始终处于可发布状态,不受隐含的代码冲突、提交的feature只部分完成等因素影响,对开发团队和基础设施有哪些要求?A:首先主干上提交的流程或质量要严格控制,真正达到DoD(Definition of Done)的标准,这里可能需要一些机制人为地进行管控,比如Committer机制等。提交的时候,除了非功能性的要求,比如跑相关的回归测试、代码检视以外,还有很重要的功能性要求,比如对需求的实现程度的检查。另外“基础设施即代码”,还要看持续集成、持续部署、自动化测试能不能快速有效地跑起来,并保持高度一致。Q31:持续集成的成功因素是什么? A:持续集成主要包括代码仓库、自动构建、自动部署、自动测试四个方面。要求每人每天都要向主干提交代码,触发自动构建和自动部署,在类生产环境进行自动化测试,同时需要团队每个成员确保清楚正在发生的状况,以此来保证持续集成的成功。Q32:华为云上的CI/CD与K8s上搭的CI/CD有什么区别?A:华为云DevCloud打通了端到端的软件交付全流程,集成了常用的DevOps开发工具,不仅可以完成CI/CD,还可以直接在上面进行项目管理和开发;而K8s只是软件开发中一个单独的工具,没有项目需求管理等功能,需要配合其他工具一起使用才能实现完整的软件开发与交付。Q33:开发和修复bug的工时如何进行安排呢?之前迭代出来的bug是按照单独工时安排,还是统一安排在开发中?A:主要看发版的标准和要求是什么,通常来说可以带病发版,但如果是非常严重的缺陷,就不能上线,必须先修复这些bug。一般bug会跟需求放在同一个池子里,根据它的优先级和影响程度来进行排序,决定是先修复bug还是先做需求。如果修改bug是为了扫清技术债务,建议在一个迭代里固定一定比例的时间来进行。Q34:感觉SaltStack和Ansible中哪个是最好的配置管理(CM)工具?为什么?A:两者定位不一样。个人认为Ansible并不是一个标准的配置管理工具,它更多是通过自动化部署的手段去touch环境这一侧,SaltStack相对来说功能性更强一些。Q35:在代码互评审和评审流程中如何高效的提升代码质量?A:人机结合,将重复性的,比如检查代码风格、命名规则等工作交给工具;人工集中看代码实践的逻辑、对需求的匹配等。将人从重复性的工作中解放出来,节约时间和人力。华为实行代码审查Committer机制,开发人员提交代码后,会自动拉起自动化代码检查。提交一个Pull Request,工具匹配相关的review进行评审和打分,如果是重要实现还可能会有一个评审会议,然后进入最终Committer决定是否将提交的代码合并到主干上去。【四、持续测试与反馈】Q36:“通过持续测试实现快速与高质量“是敏捷测试原则之一,而测试金字塔顶端的一些测试往往依赖许多外部因素,较为脆弱,容易因被测软件之外的因素而失败;且由于这类测试同时测试了软件中的多个模块,定位问题就会更难一些。对于 Flaky tests 怎样处理比较好?删除还是进行标记使其不中断后续的测试且不影响质量门禁?A:Flaky Tests,就是指在被测对象和测试条件都不变的情况下,有时候失败、有时候成功的测试。因此,Flaky Tests实际上就是不稳定的测试,或者随机失败(随机成功)的测试。测试金字塔之所以是正的三角形,核心理念是越往上,即金字塔顶端的测试,其跨度越大,影响面越大,一旦出现问题,爆炸的半径也会更大,在这个层面做测试投入产出较小,工作量大且很难执行,比如测试故障定位等,而且自动化用例的复用程度或稳定性也较差,维护成本也比较高。当然该做的工作一定要做,但相对而言,建议这个层面的测试数量要适当减少。相反,越往底层,比如单元测试,爆炸半径相对就小一些,复用度和投入产出比也更高,而且在这个层面发现的bug应该是最多的。建议金字塔底层的测试措施应该相对多做。中间比如接口测试或跨组件的集成等,如果微服务拆分相对颗粒度小一些,各方面相对就比较好,且接口测试相应的工具也比较多,投入产出比也会越来越大。接口测试也可以多做一些,这样中间层变大,金字塔也会变成橄榄球形。Q37:构建本地持续测试和云上持续测试的对比难易程度和成本,如何选取?A:本地持续测试和云上持续测试的差异在于:本地需要自行对工具和版本进行维护,云上的环境相对快捷。从成本方面考虑,云上是按需的,性能测试、压力测试等适合在云上进行,因为自己去搭建一套10万/100万并发的环境成本非常高;越往前端的测试频度非常高,适合在本地进行构建。另外还需要综合考虑开发人员的使用习惯、公司对于数据的安全要求等进行选取。Q38:从传统的瀑布型测试到敏捷测试再到DevOps,三者之间具体有什么区别?A:瀑布型测试是在开发完成交付以后才进行完整的测试,测试主体是测试人员;敏捷测试往前走一步,做大量的持续集成等实践(如果敏捷实践不只是在管理层面的话);DevOps是全流程测试,除了测试左移外,还有测试右移,频繁地持续部署到准生产或生产环境上去跑相关测试,甚至还有现网测试,包括混沌工程、Chaos monkey等,其概念更广。DevOps信奉Resilience(韧性),测试这件事很痛苦,我们要频繁地去做。和反脆弱的概念比较一致,“一切杀不死你的让你更坚强”。Q39 在测试自动化环节中应该如何简化测试流程又能快速发现业务风险?A:测试流程未必会简化,所谓的简化应该是指人员参与的流程减少,把大量能够让机器完成的工作交给机器、回归测试等实现自动化,将人从枯燥的重复性的测试活动中解放出来,去做一些新型测试的探索。Q40:SRE和DevOps有什么区别和联系?A:DevOps通常由两种角色去发起,Dev和Ops,即开发和运维。SRE是Google首先提出的一个概念,Site Reliability Engineer(网站可靠性工程师),从Google运维体系出来的一个角色。SRE工程师会通过自动化工具帮助开发人员,以运维的角度去参与研发并提供一些支持,包括开发一些自动化部署及运维相关的工具,通过这些工具和流程使能开发人员。两者比较而言,DevOps概念和范围相对更大一些,SRE则聚焦在开发与运维层面。Q41:在Scrum中只有 Dev team,没有专门的测试团队。“做测试者胜于做检查者”也要求测试人员不仅能发现问题更要准确定位问题。持续测试向价值流持续交付的两端延伸,要求测试人员不仅要懂业务、懂开发还要懂运维,对测试人员的要求很高。在这种背景下,测试人员该如何进行职业发展规划?A:确实测试人员的焦虑相对更多,因为不管流程也好、角色分工也好,他都处于开发和运维之间的位置,像三明治一样,比较难受。换个角度来看,测试是承上启下的活动,DevOps或敏捷在开始的时候都会相对顺利一些,短期成效很快,但等真正进入到测试层面,就像进入深水区,推进变得困难,原因可能就是自动化测试没做好。这样看来测试人员或测试活动其实大有可为,我们强调测试应该是一类活动,分配在整个研发生命周期过程中,而不是中间的某个阶段,因此对测试人员的要求当然也会更高。以往测试人员给人的印象是在研发提交后才参与进来,或者大量通过手工界面的点击去做回归等工作。现在和未来,这类测试人员存在的价值会很低,未来可能会要求测试人员懂业务,从业务的角度设计测试用例;还要懂开发,需要写测试脚本;还要懂运维。其实这些要求对所有的工程师都同样存在,包括开发工程师,要会做架构、做设计、做开发、还可能要自己做测试、部署运维等;运维工程师也是如此,如果转型SRE工程师的话,也要往前段去走。从这点来看,大家都在同一水平线上,所有人都要求往T型人才发展。综上,测试工程师应该是一个全程的质量保障人员,要从专业测试的视角对研发流程、需求、交付等进行质量控制,还需要引入相关实践、开发工具或做工具集成,去赋能开发和运维。真正好的测试对整个团队的帮助和提升应该是最大的。Q42:与传统项目比较,在敏捷项目中,测试工作在整个流程中所占的比重是否更少了,频次更高了,这是否意味着人效更高了?在DevOps流程下,产品人员、开发人员、主导测试人员的比例是否有一个新的经验参考?A:与传统项目相比,敏捷项目中,测试工作比重更大、频次更高、人效也会更高,但这个更高不是通过人去堆,而是通过自动化工具或时间来完成。在DevOps流程下,专职的测试人员数量会下降,现在大量的开发测试是由开发人员来做,在华为内部称为开发者测试,强调开发人员自己去做测试,以前开发测试比例差不多是3:1, 甚至1:1,现在可能是5:1或10:1的比例。产品人员跟以往应该没有太大差异,现在强调产品思维、运营思维,业务运营人员的人数会增加。Q43:小团队(5人,分工:2前端,3后端,没有专业测试人员)需要单独配备测试人员吗,一边开发一边测试,还是每个人对自己代码负责最后一块集中测试。这两种哪种好一些?A:个人认为5人团队没有必要配置专职测试,可以先由开发承担测试,当团队认为需要有一个专业的测试去知道或支持时,再去引入专业测试人员。那端到端的测试谁来做?建议是采用轮岗机制,类似on call,让团队成员轮流去做,这样可以让所有人对完整的测试都有了解和重视。Q44:未来是否会研测一体化?A:我认为研侧一体化会是一个趋势,开发者测试或开发测试的比例会越来越大,且不断往前端延伸,社会分工本就是合久必分分久必合,大分工衍生了一些新的概念,专业的人做专业的事,驱使我们更聚焦于自己的业务本质,比如IaaS(基础设施即服务),运维/环境管理、系统管理等会有专业的人去做,可以看看你是否就是这样一个专职的人才;测试也是如此,比如TaaS(Test as a service),也是一个非常专业的领域,要求懂开发、懂业务、懂运维。再有就是看公司的核心业务是什么,很多公司都不是专门做测试、运维或工具的,我们应该专注聚焦于公司主营业务。【五、持续安全与审计】Q45:如果组织中缺少专业的安全与审计人员,应该如何去补足这方面的能力?A:有些团队会把这个能力转移给相关的SaaS服务平台或第三方厂商。但平台只能提供问题的展现,实际的安全审计处理还需要专人进行。团队规模小时,可以通过业务上的分割和一些工具手段,尽量减轻相应人员的压力。Q46:小团队安全管控得太严格了,对开发测试都会造成很多不便,也会影响问题排除追踪,如何合理度量安全管控?A:项目进入正规化流程后会有很多环境:开发环境、测试环境、类生产环境、生产环境等,可以采用多环境不同程度安全管控的策略,比如在进行开发环境测试时安全管控力度可以松一些,类生产环境测试时安全管控严格一些。【六、持续部署与发布】Q47:持续部署是不是可以做到热部署,不暂停业务直接通过流水线进行部署、提供用户体验?A:持续部署,每一次变化都是直接部署到生产环境里,但持续交付是有一定选择性的,我们可以选择性地把一些需要的东西部署到生产环境中。如果希望可以做到热更新、热部署,不暂停业务,可以通过持续部署的方式,直接使用流水线来实现。Q48:K8s和Docker在应用上有什么区别?A:Docker是一种容器技术,在实践中可以直接使用Docker进行镜像构建等操作;K8s是进行集群管理的技术手段,华为云DevCloud的帮助中心有一个凤凰商城的实践案例,和HCIP考试中的实验一样,只是多了CI/CD的环节,在这个环节中就使用了K8s。Q49:K8S 和 云原生是什么关系?A:云原生是包括微服务、DevOps、容器化、持续交付等理念和方法,K8s只是一个集群管理的工具。 Q50:如果生产环境有等保要求,还有什么办法实现持续部署吗?A:如果生产环境有等保要求的话,不太适合直接做持续部署,这时使用持续交付的方式更好一些,我们可以先决定应该把哪些特性搬到生产环境上去。Q51:新版程序修改了数据结构,如何进行应用设计或部署方案,以应对可能出现重大问题所需要的版本回退?A:当我们做一些比较大的修改时,一般会先部署到类生产环境上,检视没问题后才会通过灰度的方式同步到生产环境中。Q52:我们已经在做持续集成了,但持续交付和持续部署应该怎么落地?A:如果已经在做持续集成,且做得比较成熟了的话,再往前落地持续交付和持续部署会相对容易。我们经常说:持续交付只是持续集成往前的一小步,最后一公里或最后一米会比较痛苦。其实更多的痛点不在于技术层面,而是在于流程、制度层面,可能很难打穿部门墙、穿透企业管控类的要求,这些都未必是技术能解决的问题。【七、持续运维与监控】Q53:通过自动化的方式实现持续集成和持续交付,中间会不会出现干扰而发生错误?A:一般来说,通过自动化的方式实现持续集成和持续交付后,不是很容易发生错误。错误的出现可能是由于配置问题导致的,在配置相应流水线时没有配置好,比如参数出现问题,版本变得不一致等。除此之外还可能会有一个意外导致的问题,比如网络故障等。Q54:如果生产环境要求网络隔离,还有什么办法实现持续部署吗?A:如果生产环境要求网络隔离的话,我们的流水线一般会搭建在公司内部,也就是从提交代码到构建部署都会在公司内部实现。这个过程中使用云上自动化产品会少一些,因为目前大部分云上构建的工具都必须访问公网才可以做到流水线的效果。因此这种环境下,建议在本地搭建自动化构建流水线,或者购买可供私有化部署的工具。也可以在公网进行代码托管和构建,只在部署的时候通过手工部署的方式将软件包放到网络隔离的机器上去。Q55:Docker与虚拟机有何不同?A:从上图可以用比较清楚的看到Docker和虚拟机的异同。左边的VM是虚拟机使用,container是容器使用,也就是我们说的Docker。两边都有server端和Host OS(虚拟机上的系统)。我们知道每个APP上都有Bin/libs,在Docker容器技术环境下,相同的APP可以共用同一个Bin/libs。大大节省了所占的资源空间。【八、DevOps实践与转型路径】Q56:到目前为止,已经学习了很多DevOps的功能,但是有一个困惑,对于使用DevOps是零代码,那么对于专业的开发人员来说,会不会慢慢降低他们的代码开发积极性?A:DevOps提供的零代码是指在整个DevOps工具链中希望是零代码的,通过将一切可自动化的工具自动化,将开发人员从各种维护工作中解放出来,使他们专注于开发。Q57:在DevOps实践中,环境差异的问题需要在哪个环节就开始着手来注意减少或者避免?A:配置即代码,在开发环节配置差异化的时候把环境差异等都配置进去。Q58:在DevOps转型过程中,对组织和团队最有挑战的有哪些?A:我认为DevOps转型最难的有两方面:一是如何争取公司高层同意推动DevOps转型;二是如果请了教练/顾问协助DevOps转型,顾问/教练走后,如何继续保持和落地DevOps实践。Q59:小团队如果想要使用DevOps需要全员学习吗,感觉每个人都学习时间成本挺高的,是否可以专人负责特定阶段?A:团队如果想要进行DevOps转型,需要专门有一个人把这些流程和工具研究明白,或者聘请一位外部DevOps顾问,再由整个专职人员或DevOps顾问在整个团队进行培训和推动。Q60:四个闭环过程中遇到困难或者难点是否可以列举?有什么避免的方案?A:先回忆一下四个闭环过程:l   第一阶段闭环:需求开发测试融合,将产品、研发、测试等角色融合,组建跨职能团队,提升产品交付价值与质量; l   第二阶段闭环:开发测试融合,组建研发部门内部的跨职能团队,提升自动化水平,降低修复成本; l   第三阶段闭环:研发运营一体化,实施产品自运营、自运维,打破了市场、研发、运维部门之间的壁垒,更多角色融入交付链路,提升业务响应力,建立价值反馈流; l   第四阶段闭环:目标是逐步实现所有业务线都以跨职能团队为最小组织单元,实现业务敏捷性,持续提升企业的市场竞争力。难点包括:l   打通需求难。产品侧和研发侧沟通难。在传统瀑布模式下产品和研发的沟通存在很多问题,比如需求沟通不明确等,引入敏捷的计划会议,在计划会议上做需求澄清,可以解决这一难题。l   开发测试融合难。在很多公司这是两个团队,还有些公司没有测试的角色,要进行人员和过程的融合比较困难。l   研发运营结合难。研发运营一体化,运营部分的内容怎么跟开发结合也是一个难点。l   组织结构管理难。整个流程打通了,人员的管理和组织结构的变动方面也可能会存在问题。以上难点需要根据公司具体情况来实践和探索最佳解决方案。+小姐姐微信加入训练营交流群
  • [云计算周刊] 中国云计算市场规模将达1000亿美元!
    云计算作为当前企业IT基础架构技术的不二之选,已走过探索实践阶段,迎来了多样化全面化的发展时期。  根据调研机构IDC发布的数据统计,2019年,包括云服务(Cloud as a service:公有云服务和专属云服务)、云相关服务(Cloud-related services:云专业服务和云管理服务)、云基础设施建设(Cloud infrastructure build:企业用户和服务商云基础设施建设)在内的中国整体云计算市场规模达到了329亿美元,预计2024年该市场规模将达到1,000亿美元以上。产品服务方面  云产品从虚拟机、存储、数据库、中间件等主要类型向大数据、人工智能、IoT等产品门类发展,再到当前以DevOps、容器和Serverless等为代表的云原生产品不断推广落地;云服务模式上,公有云、企业自建私有云阵营不断融合渗透,混合云、行业云的热潮尚未过去,专属云(包括本地云、托管私有云)、边缘云等热潮已经到来。  生态形成方面  围绕着主要的公有云服务商、私有云产品和技术提供商,一大批帮助企业客户上云、用云、管云的咨询服务商、系统集成商、软件开发商、销售代理商生态已经形成。云计算的发展为初创企业进入和老牌IT服务商业转型带来了发展机遇,也正在逐渐打破原来IT市场上由国际化硬件、软件和服务厂商占主导的生态格局。  市场竞争方面  公有云服务市场的聚集效应仍然明显,《中国公有云服务市场(2020第三季度)跟踪》报告显示,阿里云、腾讯云、华为云、天翼云和AWS 在IaaS+PaaS市场总体占据76.3%的市场份额,此外,金山云、浪潮云、UCloud、移动云等在同期表现出了强劲的增长。公有云服务商加速向本地云、边缘云、企业自建私有云等市场渗透,紫光云、中国电科云、中国电子云等生力军纷纷加入政企云计算市场,再加上原有私有云建设市场上的新华三、浪潮、曙光、VMware以及大批开源软件服务商等,云计算市场竞争日益白热化。  行业应用方面  在疫情刺激下,视频、游戏、电商等带动的互联网行业推动了公有云服务市场的快速增长。政府、医疗、教育、制造、服务等非互联网行业也纷纷看到了云计算对于业务数字化转型的重要作用,将加速推动云计算的全面应用。在云服务商和企业需求的双重推动下,云的部署将无处不在,为行业用户带来更多样化的产品选择和模式选择。  未来,云计算仍将迎来下一个黄金十年,进入普惠发展期。一是随着新基建的推进,云计算将加快应用落地进程,在互联网、政务、金融、交通、物流、教育等不同领域实现快速发展。二是全球数字经济背景下,云计算成为企业数字化转型的必然选择,企业上云进程将进一步加速。三是新冠肺炎疫情的出现,加速了远程办公、在线教育等SaaS服务落地,推动云计算产业快速发展。中国信通院发布的云计算发展白皮书(2020年),对未来云计算发展作出展望,归纳出以下六个关键词。  关键词1:云原生  随着市场持续增长,云技术也不断推陈出新,其中一个值得高度关注的趋势是——云原生采纳率持续攀升。目前超四成的企业已经在使用容器技术,超过七成的私有云企业已经使用或计划使用微服务架构。  关键词2:SaaS  疫情下,越来越多的企业接受SaaS的模式,从业务上看,我国IaaS发展成熟,PaaS增长高速,SaaS潜力巨大。2019年,我国SaaS市场规模达到194亿元,与全球整体市场(1095亿美元)的成熟度差距明显,但是发展空间却十分巨大。尤其是受疫情的推动,预计未来市场将加速发展。  关键词3:分布式云  随着云边协同的发展,在工业等多个领域,分布式云将成为主要模式。中国信通院的调研显示,超过50%的用户已经计划或者已经使用边缘云的模式,“中心云+边缘云”的分布式云的架构已经崭露头角。  关键词4:原生云安全  近年来,一个新的理念诞生,即原生云安全。中国信通院发布的《中国公有云发展调查报告(2020年)》显示,42.4%的企业在选择公有云服务商时会考虑服务安全性,安全是影响企业选择的重要因素。而随着云原生快速兴起,原生云安全也成为关注焦点。  关键词5:数字化转型  数字化转型已经成为经济社会发展的重要趋势。随着云计算技术、架构、安全等方面的推陈出新,云计算在数字化转型中扮演重要角色。调查显示,超过五成的企业使用云计算是为了降本增效,超四成的企业表示使用云计算提升了IT运行效率,IT运维工作量减少和安全性提升的占比分别为25.8%和24.2%。  关键词6:新基建  随着利好政策不断加码,云计算已经成为新基建的重要组成部分。无论是工业和信息化部发布的《中小企业数字化赋能专项行动方案》,国家发展改革委、中央网信办发布的《关于推进“上云用数赋智”行动培育新经济发展实施方案》,还是国家发展改革委对新基建概念的解读,都表明——云计算已经成为新基建的重要组成部分。  无论是如火如荼的“新基建”、稳步推进的企业数字化转型,还是突如其来的疫情,都将云计算推向了一个新高度。未来十年,云计算将进入全新发展阶段,具体表现为以下六大趋势:  趋势1:云技术从粗放向精细转型  趋势2:云需求从IaaS向SaaS上移  趋势3:云架构从中心向边缘延伸  趋势4:云安全从外部向原生转变  趋势5:云应用从互联网向行业生产渗透  趋势6:云定位既是基础资源也是基建操作系统来源: 小巫女聊星座
  • [技术干货] Multi-Architecture镜像制作指南已到,请查收!
    摘要:使用Multi-Architecture镜像,可以让docker根据系统架构去拉取对应的镜像,服务的部署脚本等可以在不同架构的系统间使用相同的配置,减化服务配置,提高了服务在不同系统架构间的一致性。背景由于Kubernetes集群支持amd64和arm64架构的系统,容器部署时两种类型的节点都可能被集群调度到;所以容器在打包推送到镜像仓库时需要考虑支持多架构,防止调度到不支持的架构节点导致运行失败。简介Docker register: v2.3.0开始支持Multi-Architecture镜像Docker CLIv1.11开始支持Multi-Architecture镜像拉取v18.03开始支持Multi-Architecture镜像制作参考资料:Manifest标准:https://docs.docker.com/registry/spec/manifest-v2-1/Manifest命令:https://docs.docker.com/engine/reference/commandline/manifest/Registry:https://github.com/docker/distribution/releases/tag/v2.3.0制作说明虽然Multi-Architecture标准很早就有,但是官方的Docker CLI直到v18.03版本才开始支持Multi-Architecture镜像的制作;或者可以考虑使用第三方工具,参考:构建多CPU架构支持的Docker镜像后续Multi-Architecture镜像的制作使用Docker v18.09版本进行说明。设置DockerDocker使用manifest命令来设置MANIFEST_LIST从而支持Multi-Architecture。到Docker v18.09版本为止,manifest命令还是实验性的,所以要配置Docker使能实验性功能。Docker daemon修改配置文件/etc/docker/daemon.json,添加配置"experimental": true:$ sudo cat /etc/docker/daemon.json[sudo] password for zhangsan:{    "data-root": "/home/common/docker",    "experimental": true,               <- 使能docker daemon实验性功能    "storage-driver": "overlay2",    "log-driver": "json-file",    "log-opts": {        "max-file": "10",        "max-size": "100m"    },    "insecure-registries": [        "docker-hub.***.com"    ]}Docker Cli修改当前用户home目录的配置文件~/.docker/config.json,添加配置"experimental": "enabled":$ cat ~/.docker/config.json{    "experimental": "enabled",          <- 使能docker cli实验性功能        "proxies":        {            "default":            {                "httpProxy": "http://127.0.0.1:3128",                "httpsProxy": "http://127.0.0.1:3128",                "ftpProxy": "http://127.0.0.1:3128"            }        }}验证配置重启docker服务,手工运行docker manifest,出现如下信息说明配置成功:$ docker manifest Usage:  docker manifest COMMAND  Manage Docker image manifests and manifest lists  Commands:  annotate    Add additional information to a local image manifest  create      Create a local manifest list for annotating and pushing to a registry  inspect     Display an image manifest, or manifest list  push        Push a manifest list to a repository Run 'docker manifest COMMAND --help' for more information on a command.镜像打包示例程序stop是一个go程序,启动后暂停不做任何操作,编译到amd64和arm64平台,在amd64平台分别运行如下:$ ll -hR.:total 8.0Kdrwxrwxr-x 2 zhangsan zhangsan 4.0K Jun  3 17:21 amd64drwxrwxr-x 2 zhangsan zhangsan 4.0K Jun  3 17:21 arm64 ./amd64:total 240K-rw-rw-r-- 1 zhangsan zhangsan   51 Jun  3 17:21 Dockerfile-rwxrwxr-x 1 zhangsan zhangsan 235K Jul 19  2014 stop ./arm64:total 656K-rw-rw-r-- 1 zhangsan zhangsan   51 Jun  3 17:21 Dockerfile-rwxr-xr-x 1 zhangsan zhangsan 651K May 22 14:38 stop $ amd64/stop^C% $ arm64/stopzsh: exec format error: arm64/stop使用相同的Dockerfile:$ cat DockerfileFROM scratch COPY stop /stopENTRYPOINT ["/stop"]amd64版本$ cd amd64 $ lsDockerfile  stop # 使用tag标注平台信息。$ docker build -t docker-hub.***.com/zhangsan/stop:1.0-amd64 .Sending build context to Docker daemon  242.7kBStep 1/3 : FROM scratch --->Step 2/3 : COPY stop /stop ---> ffb0d366bb8cStep 3/3 : ENTRYPOINT ["/stop"] ---> Running in 715d1fbcf450Removing intermediate container 715d1fbcf450 ---> 0584fe60ca3dSuccessfully built 0584fe60ca3dSuccessfully tagged docker-hub.***.com/zhangsan/stop:1.0-amd64 $ docker images | grep stopREPOSITORY                                        TAG                 IMAGE ID            CREATED             SIZEdocker-hub.***.com/zhangsan/stop        1.0-amd64           0584fe60ca3d        3 seconds ago       240kB $ docker push docker-hub.***.com/zhangsan/stop:1.0-amd64The push refers to repository [docker-hub.***.com/zhangsan/stop]9e352dcc98ab: Pushed1.0-amd64: digest: sha256:e5b6263b7e45840f139f1bf40b774294712c95df1b43d41598b736f07110341f size: 526 # 可以用manifest的子命令inspect查看镜像的manifest信息,由于是私有仓库,需要使用--insecure参数。$ docker manifest inspect -v --insecure docker-hub.***.com/zhangsan/stop:1.0-amd64{        "Ref": "docker-hub.***.com/zhangsan/stop:1.0-amd64",        "Descriptor": {                "mediaType": "application/vnd.docker.distribution.manifest.v2+json",                "digest": "sha256:e5b6263b7e45840f139f1bf40b774294712c95df1b43d41598b736f07110341f",                "size": 526,                "platform": {                        "architecture": "amd64",                        "os": "linux"                }        },        "SchemaV2Manifest": {                "schemaVersion": 2,                "mediaType": "application/vnd.docker.distribution.manifest.v2+json",                "config": {                        "mediaType": "application/vnd.docker.container.image.v1+json",                        "size": 1489,                        "digest": "sha256:0584fe60ca3dbff4c746d376855e89b72b022a4198373b3c8d4c41b97b8b4faf"                },                "layers": [                        {                                "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",                                "size": 71322,                                "digest": "sha256:7797d3b3ba41f7abc6a914250f793cf2d69a3e7c0bcc787a596eab4836554552"                        }                ]        }}arm64版本$ cd arm64 $ lsDockerfile  stop $ docker build -t docker-hub.***.com/zhangsan/stop:1.0-arm64 .Sending build context to Docker daemon  669.2kBStep 1/3 : FROM scratch --->Step 2/3 : COPY stop /stop ---> e02dd9cc9ffaStep 3/3 : ENTRYPOINT ["/stop"] ---> Running in 9b574680691aRemoving intermediate container 9b574680691a ---> 1fc97e49b088Successfully built 1fc97e49b088Successfully tagged docker-hub.***.com/zhangsan/stop:1.0-arm64 $ docker images | grep stopdocker-hub.***.com/zhangsan/stop        1.0-arm64           1fc97e49b088        8 seconds ago       666kBdocker-hub.***.com/zhangsan/stop        1.0-amd64           0584fe60ca3d        3 minutes ago       240kB $ docker push docker-hub.***.com/zhangsan/stop:1.0-arm64The push refers to repository [docker-hub.***.com/zhangsan/stop]a0c07ccfc4ae: Pushed1.0-arm64: digest: sha256:089798dc96fcc3d2bf4da3ec4243adcc2507d2ae0c5ba77c5582af626702b3d6 size: 527 # 由于打包镜像的机器是amd64架构,所以arm64的应用镜像中architecture为amd64,后面可以修改,或者直接在arm64机器上打包。$ docker manifest inspect -v --insecure docker-hub.***.com/zhangsan/stop:1.0-arm64{        "Ref": "docker-hub.***.com/zhangsan/stop:1.0-arm64",        "Descriptor": {                "mediaType": "application/vnd.docker.distribution.manifest.v2+json",                "digest": "sha256:089798dc96fcc3d2bf4da3ec4243adcc2507d2ae0c5ba77c5582af626702b3d6",                "size": 527,                "platform": {                        "architecture": "amd64",                        "os": "linux"                }        },        "SchemaV2Manifest": {                "schemaVersion": 2,                "mediaType": "application/vnd.docker.distribution.manifest.v2+json",                "config": {                        "mediaType": "application/vnd.docker.container.image.v1+json",                        "size": 1488,                        "digest": "sha256:1fc97e49b0883f59e1e894404785ea1867b9e557d55bdac92f2da09c92b659e7"                },                "layers": [                        {                                "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",                                "size": 318698,                                "digest": "sha256:4397808817371da0348eb097ca181996165a9836d629aa1fb97b0d3824ffe2e2"                        }                ]        }}验证镜像$ docker images | grep stopdocker-hub.***.com/zhangsan/stop        1.0-arm64           1fc97e49b088        21 hours ago        666kBdocker-hub.***.com/zhangsan/stop        1.0-amd64           0584fe60ca3d        21 hours ago        240kB $ docker run docker-hub.***.com/zhangsan/stop:1.0-amd64^C% $ docker run docker-hub.***.com/zhangsan/stop:1.0-arm64standard_init_linux.go:207: exec user process caused "exec format error"创建MANIFEST_LIST创建一个MANIFEST_LIST引用之前两个不同平台的镜像。$ docker manifest inspect -v --insecure docker-hub.***.com/zhangsan/stop:1.0no such manifest: docker-hub.***.com/zhangsan/stop:1.0 $ docker manifest create --insecure docker-hub.***.com/zhangsan/stop:1.0 docker-hub.***.com/zhangsan/stop:1.0-amd64 docker-hub.***.com/zhangsan/stop:1.0-arm64Created manifest list docker-hub.***.com/zhangsan/stop:1.0 $ docker manifest inspect -v --insecure docker-hub.***.com/zhangsan/stop:1.0[        {                "Ref": "docker-hub.***.com/zhangsan/stop:1.0-amd64",                "Descriptor": {                        "mediaType": "application/vnd.docker.distribution.manifest.v2+json",                        "digest": "sha256:e5b6263b7e45840f139f1bf40b774294712c95df1b43d41598b736f07110341f",                        "size": 526,                        "platform": {                                "architecture": "amd64",                                "os": "linux"                        }                },                "SchemaV2Manifest": {                        "schemaVersion": 2,                        "mediaType": "application/vnd.docker.distribution.manifest.v2+json",                        "config": {                                "mediaType": "application/vnd.docker.container.image.v1+json",                                "size": 1489,                                "digest": "sha256:0584fe60ca3dbff4c746d376855e89b72b022a4198373b3c8d4c41b97b8b4faf"                        },                        "layers": [                                {                                        "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",                                        "size": 71322,                                        "digest": "sha256:7797d3b3ba41f7abc6a914250f793cf2d69a3e7c0bcc787a596eab4836554552"                                }                        ]                }        },        {                "Ref": "docker-hub.***.com/zhangsan/stop:1.0-arm64",                "Descriptor": {                        "mediaType": "application/vnd.docker.distribution.manifest.v2+json",                        "digest": "sha256:089798dc96fcc3d2bf4da3ec4243adcc2507d2ae0c5ba77c5582af626702b3d6",                        "size": 527,                        "platform": {                                "architecture": "amd64",                                "os": "linux"                        }                },                "SchemaV2Manifest": {                        "schemaVersion": 2,                        "mediaType": "application/vnd.docker.distribution.manifest.v2+json",                        "config": {                                "mediaType": "application/vnd.docker.container.image.v1+json",                                "size": 1488,                                "digest": "sha256:1fc97e49b0883f59e1e894404785ea1867b9e557d55bdac92f2da09c92b659e7"                        },                        "layers": [                                {                                        "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",                                        "size": 318698,                                        "digest": "sha256:4397808817371da0348eb097ca181996165a9836d629aa1fb97b0d3824ffe2e2"                                }                        ]                }        }]修改MANIFEST_LIST修改刚创建的MANIFEST_LIST,使镜像和架构对应。$ docker manifest annotate docker-hub.***.com/zhangsan/stop:1.0 docker-hub.***.com/zhangsan/stop:1.0-arm64 --arch arm64[        {                "Ref": "docker-hub.***.com/zhangsan/stop:1.0-amd64",                "Descriptor": {                        "mediaType": "application/vnd.docker.distribution.manifest.v2+json",                        "digest": "sha256:e5b6263b7e45840f139f1bf40b774294712c95df1b43d41598b736f07110341f",                        "size": 526,                        "platform": {                                "architecture": "amd64",                                "os": "linux"                        }                },                "SchemaV2Manifest": {                        "schemaVersion": 2,                        "mediaType": "application/vnd.docker.distribution.manifest.v2+json",                        "config": {                                "mediaType": "application/vnd.docker.container.image.v1+json",                                "size": 1489,                                "digest": "sha256:0584fe60ca3dbff4c746d376855e89b72b022a4198373b3c8d4c41b97b8b4faf"                        },                        "layers": [                                {                                        "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",                                        "size": 71322,                                        "digest": "sha256:7797d3b3ba41f7abc6a914250f793cf2d69a3e7c0bcc787a596eab4836554552"                                }                        ]                }        },        {                "Ref": "docker-hub.***.com/zhangsan/stop:1.0-arm64",                "Descriptor": {                        "mediaType": "application/vnd.docker.distribution.manifest.v2+json",                        "digest": "sha256:089798dc96fcc3d2bf4da3ec4243adcc2507d2ae0c5ba77c5582af626702b3d6",                        "size": 527,                        "platform": {                                "architecture": "arm64",                                "os": "linux"                        }                },                "SchemaV2Manifest": {                        "schemaVersion": 2,                        "mediaType": "application/vnd.docker.distribution.manifest.v2+json",                        "config": {                                "mediaType": "application/vnd.docker.container.image.v1+json",                                "size": 1488,                                "digest": "sha256:1fc97e49b0883f59e1e894404785ea1867b9e557d55bdac92f2da09c92b659e7"                        },                        "layers": [                                {                                        "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",                                        "size": 318698,                                        "digest": "sha256:4397808817371da0348eb097ca181996165a9836d629aa1fb97b0d3824ffe2e2"                                }                        ]                }        }]推送MANIFEST_LIST到镜像仓库创建的MANIFEST_LIST会保存在本地目录~/.docker/manifests,在push时可以使用-p(--purge)参数删除本地数据:$ ll -R ~/.docker/manifests/home/zhangsan/.docker/manifests:total 4drwxr-xr-x 2 zhangsan zhangsan 4096 Jun  3 17:57 docker-hub.***.com_zhangsan_stop-1.0 /home/zhangsan/.docker/manifests/docker-hub.***.com_zhangsan_stop-1.0:total 8-rw-r--r-- 1 zhangsan zhangsan 733 Jun  3 17:57 docker-hub.***.com_zhangsan_stop-1.0-amd64-rw-r--r-- 1 zhangsan zhangsan 734 Jun  3 18:01 docker-hub.***.com_zhangsan_stop-1.0-arm64 $ sudo docker manifest push -p --insecure docker-hub.***.com/zhangsan/stop:1.0[sudo] password for zhangsan:sha256:401767ef0864a65578bef86e3baed2e1e0be905d08d88b57cdeb299850400ece验证$ docker images | grep stopdocker-hub.***.com/zhangsan/stop        1.0-arm64           1fc97e49b088        16 hours ago        666kBdocker-hub.***.com/zhangsan/stop        1.0-amd64           0584fe60ca3d        16 hours ago        240kB # 在amd64架构的系统下拉取不带架构信息的镜像$ uname -aLinux SZX1000514415 4.4.0-87-generic #110-Ubuntu SMP Tue Jul 18 12:55:35 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux $ docker pull docker-hub.***.com/zhangsan/stop:1.01.0: Pulling from zhangsan/stopDigest: sha256:401767ef0864a65578bef86e3baed2e1e0be905d08d88b57cdeb299850400eceStatus: Downloaded newer image for docker-hub.***.com/zhangsan/stop:1.0 $ docker images | grep stopdocker-hub.***.com/zhangsan/stop        1.0-arm64           1fc97e49b088        16 hours ago        666kBdocker-hub.***.com/zhangsan/stop        1.0                 0584fe60ca3d        16 hours ago        240kBdocker-hub.***.com/zhangsan/stop        1.0-amd64           0584fe60ca3d        16 hours ago        240kB # 可正常运行$ docker run docker-hub.***.com/zhangsan/stop:1.0^C% # 在arm64架构的系统下拉取不带架构信息的镜像[root@kwephicprc09532 ~]# uname -aLinux kwephicprc09532 4.1.44-06.160.vhulk1711.1.1.aarch64 #1 SMP Tue Oct 16 18:45:06 UTC 2018 aarch64 aarch64 aarch64 GNU/Linux [root@kwephicprc09532 ~]# docker images | grep stop [root@kwephicprc09532 ~]# docker pull docker-hub.***.com/zhangsan/stop:1.01.0: Pulling from zhangsan/stop439780881737: Pull completeDigest: sha256:401767ef0864a65578bef86e3baed2e1e0be905d08d88b57cdeb299850400eceStatus: Downloaded newer image for docker-hub.***.com/zhangsan/stop:1.0 [root@kwephicprc09532 ~]# docker images | grep stopdocker-hub.***.com/zhangsan/stop                          1.0                 1fc97e49b088        16 hours ago        666.5 kB # 可正常运行[root@kwephicprc09532 ~]# docker run docker-hub.***.com/zhangsan/stop:1.0^CShutting down, got signal: Interrupt升级MANIFEST_LIST后期升级MANIFEST_LIST方法与创建类似,如:docker-hub.***.com/zhangsan/stop:1.0-amd64镜像升级或docker-hub.***.com/zhangsan/stop:1.0这个MANIFEST_LIST要关联其它镜像时,都需要升级MANIFEST_LIST。需要注意几点:manifest create时使用参数-a(--amend)升级后没有关联到期望的镜像可以先手工删除本地保存的manifest再尝试使用Multi-Architecture镜像的好处使用Multi-Architecture镜像,可以让docker根据系统架构去拉取对应的镜像,服务的部署脚本等可以在不同架构的系统间使用相同的配置,减化服务配置,提高了服务在不同系统架构间的一致性。 本文分享自《叮!快收好这份Multi-Architecture镜像制作指南》,原文作者:Thir**mans 。
总条数:888 到第
上滑加载中