Ansible 开发环境的搭建

通常我不喜欢写开发环境搭建类文章的,但是见到不少同学在 Ansible 的开发环境花了很多时间。所以,就想写这么一篇文章。希望能帮助到有需要的同学。 在介绍开发环境搭建之前,需要介绍 Ansible 脚本的开发流程。 不像普通的业务系统的开发,只需要打开 IDE 就可以写代码,然后调试了。当然 Ansible 脚本也可以进行单元测试,但是 Ansible 脚本还是需要真实运行并部署,才能验证脚本的正确性。 所以,Ansible 脚本的开发过程通常是这样的: 启动一台虚拟机。 在开发机上编辑 Ansible 脚本。 在开发机上执行 ansible-playbook -i hosts playbook.yml 命令。 通过 Ansible 脚本的开发过程了解到,开发环境的搭建可以分成3部分: 测试机器的准备。 文本编辑器的准备。 Ansible 的安装。 1. 测试机器的准备 见到不少同学使用 Vmware 或 VirtualBox 手工创建虚拟机。这种方式是可以达到搭建测试机器的目的。但是笔者认为这样不够好。因为验证 Ansible 脚本,我们需要频繁创建新的虚拟机。手工创建虚拟机的效率太低。而且不利于版本控制。 所以,测试机器的准备,笔者使用的是 Vagrant。通过它,可以自动化创建和配置虚拟机。当然,整个过程还是版本控制的。 同时需要注意,Vagrant 本身并不是一个虚拟机的实现,它是基于 VirtualBox 和 Vmware 的。换句说就是我们可以通过 Vagrant 去控制 Vmware 和 VirtualBox。所以,在安装 Vagrant 的同时,也需要安装 VirtualBox 或 Vmware。本文使用 VirtualBox。 Vagrant 和 VirtualBox 的具体安装在本文末有官方教程。 2. Vagrant 介绍 Vagrant 本身只是一个软件,提供了 vagrant 命令。我们通过一个名为 Vagrantfile 的文件声明启动什么配置的虚拟机。 ...

2019-11-07 · 3 min · 452 words · 翟志军 Jack Zhai

谈 DevOps 平台设计:版本号相关功能的设计

在设计 DevOps 平台时,笔者认为版本号的管理是一个绕不开的课题。可是,行业里似乎很少人提这个事,笔者觉得要谈一谈,所以就有了这篇文章。 一万个人的眼里有一万个“版本号” 笔者这三年在同一家公司里,换岗换了四个团队。团队的成员组成各异,有的团队都是在大型跨国企业跳槽过来的,有的团队大部人都是刚毕业的。 每到一个团队,团队运行一段时间,都会做一件事情:讨论该怎么定义这个版本号。版本号的制定,有些只有开发人员参与,有时会有产品经理参与,有时还有 PMO 参与。 经过这些讨论,我发现:一万人的眼里有一万个“版本号”。讨论的最后,基本上就是谁的嗓子大,听谁的。 所以,在讨论“版本号”之前,一定要搞清楚讨论各方对于“版本号”的理解,再深入讨论,否则,大家谈的都会是牛头不对马嘴的东西。浪费时间。 为什么对于“版本号”,各方的理解,差异会如此大。笔者认为,主要是因为他们关心的面不同。 APP产品经理关心的是该APP在用户界面上显示的版本号,比如当前爱彼迎的APP的版本号是:1.9.44.china。 对于后端开发工程师,关心的是网关服务的版本是1.2.1、客服服务的版本是4.11.1。 对于前端开发工程师,关心的是通用组件的版本是2.1.1、首页组件的版本是3.1.1。 而对于 PMO,他们可能只关心在 Staging 环境的最后一个版本是否为一个稳定的版本(这写在他们的管理规范里),保证不影响测试人员的工作,根本不关心具体的“版本号”是多少。 重新认识版本号 各方的关注点不同,不是问题,但是我们作为一个平台的设计必须对“版本号”有更深入的理解。 笔者分析各方的关注点,他们所说的“版本号”分布在以下两个层面: 技术层面:程序员关心线上跑的是哪份代码(对应的是Git\SVN中的Commit ID)、运维关心线上跑是哪个版本(对应的就是具体哪个包)。 业务层面:方便终端用户识别的版本号,产品经理也属于这一层面。 认识到这点,我们设计DevOps平台,就会对两种版本号进行区别对待,进而设计出对团队非常有用的功能,最终帮助团队更好的实现交付。 为方便沟通,技术层面的版本号,如 Commit ID 我们称为技术版本号,业务层面的版本号,称为业务版本号。 版本号相关功能设计 但是版本号有什么用?仔细想想,除了产品经理发布时要定个版,后端服务的版本用于保证服务之间的相互引用或调用不出问题,就没有什么别的用处了。 也许是因为大家都不了解版本号的用处,也或者是认为它根本就不值得讨论,所以,笔者在国内的几个大的平台都没有看到版本号的相关功能的设计。唯一使用到版本号的地方就是在制品库,部署时需要指定制品的版本号。而业务版本号与技术号之间的关系被隐藏得很深,用户很难查到。 笔者不想一开始就谈它的好处。我直接上功能,下图是笔者臆想出来的。 笔者认为,DevOps 平台应该有的功能之一:能输出这么一幅图,暂定名为版本关系图。图中的方块下,同时标有业务版本号和技术版本号。而图中的系统之间的连接线是应用系统的调用链,读者可忽略。 版本关系图应该能提供以下信息: 系统应用之间的版本依赖。 系统内部所依赖的组件的版本。 能根据某系统的版本查到目前直接依赖于或间接依赖于它的其他系统。 各系统的版本变迁信息。 这些信息能给用户带来的价值如下: 团队内信息更透明,沟通效率更高,可以有效避免某个员工成为单点。你不必等其他成员,自己也可以得到整个系统的版本信息。 可以提高团队成员的排错能力,因为当A发布新版本后,APP 首页打开变慢,有了版本关系,我们可以首根据整个平台的“版本事件”来排查问题。同时,团队也很快可以找到相应的代码变更,然后进行 review 及修复。 上图中,当 A 服务是一个集群时,我们还可以将部署的目标机器与版本号关联起来了。这样,团队就可以轻松的知道,哪台机器部署了哪个版本。 上图只是整个业务系统的某个时间点的“快照”。事实上,我们还可以在版本号上做更大的文章。比如让技术版本号与代码质量、构建速度等过程指标关联起来,这样我们可以在不同的版本之间进行对比。再比如计算两个业务版本号之间,代码质量的差异,长期积累下来这些数据后,我们就有能力计算出代码质量与业务指标之间的关系。 总的来说,版本号就是整个研发流程中的各项指标数据的枢纽。 后记 版本号和其它数据的关系的价值,笔者认为被大大低估了。希望本文能给 DevOps 平台设计者带来不一样的想法。

2019-11-06 · 1 min · 55 words · 翟志军 Jack Zhai

谈DevOps平台实施:实现从内网拉取外网依赖的一种方案

背景 在大型企业内部,网络通常会被划分成多个不能直接访问的区域。比如本例中,网络被分成了内网和DMZ两个区域。出于安全的考虑,内网的机器不能直接访问外网。内网访问 DMZ 的机器、DMZ的机器要访问外网都需要单独提流程。 但是,我们的应用能部署到 DMZ 区域中吗?答案是技术上不是问题,但是管理上不允许这样做。 所以,在这样的大型企业内部,应用都会部署到内网中(本例中的A、B、C、D)。 可是,总会有一些应用需要发 HTTP 请求到外网。比如实施DevOps平时,我们的应用需要从外网拉取依赖。 这时,怎么办呢?本文就是为解决此问题而写。 解决方案 最后的解决方案如下: Privoxy 是一个HTTP 协议过滤代理。 Squid 是HTTP代理服务器软件。Squid用途广泛,可以作为缓存服务器,可以过滤流量帮助网络安全,也可以作为代理服务器链中的一环,向上级代理转发数据或直接连接互联网。 说实话,光看介绍,笔者一开始也一头雾水。不过,看完本文就应该知道它们的作用了。 以下是方案具体实施步骤: 在应用机器上设置全局环境变量: export http_proxy=http://192.168.1.100:3126 export https_proxy=http://192.168.1.100:3126 这一步的作用是将本机的 http 流量都代理到 192.168.1.100 的 3126 端口 在 192.168.1.100 上安装 Privoxy。它的作用是根据配置,决定流量走哪个网络。本例中,它的作用是我们指定的http请求,走到 dmz。而其它的则和原来一样。它的配置如下: cat /etc/privoxy/config listen-address 192.168.1.100:3126 forward .abc.com/ 192.168.42.12:3127 listen-address 指 Privoxy 监听的IP和端口 forward 指接收到符合域名规则(.abc.com)的请求,将转发给 192.168.42.12 的 3127 端口。 到此,得到的效果就是当在应用机器访问 abc.com,admin.abc.com 等时,这些流量都会被 Privoxy 转发到 192.168.42.12 的 3127 端口。其它 HTTP 请求则不会。 而 192.168.42.12 则是安装了 Squid 实现 HTTP 代理的机器的 IP。 ...

2019-11-04 · 1 min · 107 words · 翟志军 Jack Zhai

这样理解Ansible更容易

滚滚长江东逝水,浪花淘尽英雄。是非成败转头空。青山依旧在,几度夕阳红。—— 《临江仙》 电脑店 从前,有一家电脑店。原来你即是老板,又是店员时,拿到清单,你就必须亲自动手采购,然后一个个零件组装。每天都做着即重复又辛苦的活。 虽说你的组装技术已经很娴熟了,但是偶尔还发生装错的情况(大概是那天和老板娘吵架了),把一个客人要求的 CPU i5 装成了 CPU i7。结果是你亏本或者赚得少了。 后来,你采购了一个自动组装电脑的机器人。你只要告诉它电脑的配置,并把零件放到指定的箱子中。接着启动这台机器,它就自动帮你组装好电脑了。它每天都干重复的活也不会叫辛苦。 最重要的是准确,它不会因为心情不好,而装错。因为它根本不会闹情绪。 这样,老板就可以从重复的工作解放出来。然后将多出来的时间花在与人的沟通上,为有不同需求的人设计更合适的电脑配置清单。毕竟游戏发烧友和办公小白领的需求是不一样的。 在运维领域,不少运维人都干着即是老板又是店员的工作。如果在运维领域也能有这样的“机器人”该多好。事实上,Ansible、Puppet、Check 就是这样的机器人。 为什么要从零设计一个运维机器人 本文并不想生硬地罗列 Ansible 的各个知识点。因为那样,大家不如直接看 Ansible 官方文档就好。 笔者采用从零设计一个运维机器人的方式来告诉你,为什么 Ansible 会是现在这个样子。当然,现实中的 Ansible 不会像本文所写的那样一步步设计。 为什么要这样呢?因为笔者觉得只有知道一个工具背后的设计原理,真正用这个工具才会得心应手。 运维机器人的最终模样 首先,需要确定一下实现这个运维机器人的目的是什么。我们并不是希望所有的运维工作都交给运维机器人,而是希望运维工作中重复的那部分尽可能的交给机器人,把创造性的工作全部交给人。如下图所示。 以终为始是一种非常有效的实现目标的思考模型。根据此思考模型,我们首先必须探讨运维机器人的最终模样。然后,再讨论可能的解决方案。 那么,什么样的运维机器人能帮助我们实现上述的自动化运维目标呢?想像一下。是不是只要我们对着运维机器人说一句:“我要部署一个 Nginx 到 192.168.12.11”。它就可以帮我们完成了? 但是它怎么知道如何连接到 192.168.12.11 呢?是使用用户名密码的方式,还是使用私钥?它又怎么知道 Nginx 需要什么样的配置呢?一问下来,其实,语音运维只适用于启动一些预定义的动作。就像汽车的一键启动。你不可能使用语音来对 Nginx 进行大量的配置。 而纯文本才是进行大量配置的最好媒介。 所以,运维机器人的最终模样是:我们将部署的主机 IP、登录方式、Nginx 的配置放在一个文本文件中,然后运维机器人读取这个文本文件,然后根据配置进行部署。如果部署的是业务系统,我们还需要准备该业务系统的二进制包。如下图所示。 那么,我们在文本文件中使用何种语言描述我们的配置需求呢?可以分成两种。一种是利于人类学习的自然语言(如英语)。另一种是利于机器读取的结构化数据(如YAML、JSON)。 按当前的技术实现的可能性,不论是运维机器人,还是交给其它程序,都需要将自然语言转到结构化的数据。就像程序员,需要将业务知识翻译成编程语言;像编译器将编程语言翻译成机器真正能识别的二进制代码。 运维机器人的真正核心不是将自然语言转成结构化的数据。所以,文本文件中,我们直接写结构化数据。同时,我们决定使用 YAML 格式作为结构化数据的载体。因为它是非常流行的配置文件格式。降低人们写结构化数据的难度。 当然,好的设计不应该与具体配置文件格式耦合。 实现运维机器人要解决哪些问题 以上只是确定了运维机器人的最终模样及使用方式,解决的是用户的问题。但是因为我们是运维机器人的设计者,我们必须考虑如何实现它。 回想一下,平时我们的运维人员是如何实现自动化的?是不是写好了 bash 脚本后,然后将脚本上传到目标机器,最后在目标机器上执行该脚本。 这个 bash 脚本其实也可以算是一种结构化的数据格式,而且是一种不需要再做编译,目标机器能直接运行的格式。 在平时的自动化方式基础上进行抽象。我们觉得要实现运维机器人要解决的关键问题有: 需要将 YAML 转成目标机器可执行的程序(或脚本)。 需要将可执行的程序上传到目标机器,并执行。 为什么一定要将 YAML 转成目标机器可执行的程序呢?直接写 bash 不就可以了? ...

2019-09-19 · 7 min · 1362 words · 翟志军 Jack Zhai

你们的 save 方法是写在实体上,还是写 Dao 上?

注:Dao 在不同语言中的叫法可能不一样。Dao 可以理解为对数据进行持久化的具体实现。 关于实体的保存,笔者知道行业内有两种方式: dogDao.save(dog); dog.save(); 相信不少同学,现实中,通常使用第一种,很少见到第二种写法。 为了让大家站在同一个讨论上下文,笔者决定贴出更详细的代码。 注:以下代码会省略很多本文不相关的代码,比如数据校验。读者朋友不必太纠结。 第一种写法:save 方法写在 Dao 上 Dog dog = new Dog() dog.setName('didi'); dog.setColor('white'); dogDao.save(dog); 第二种写法:save 方法写在实体上 // DogService.java Dog dog = new Dog() dog.setName('didi'); dog.setColor('white'); dog.save(); 这两种写法,有什么区别吗?事实上,从字面上看,没有什么区别。因为 Dog 类的 save() 方法是这样实现: // Dog.java public void save(){ dogDao.save(this); } 但是从抽象的角度来看,就不一样了。 假如你的项目中存在这样的抽象: 如果采用第一种写法,意味着,每多出多一种 Animal,我们就必须多写一套 Service。Service 中会很多这样的方法: // DogService.java void save(Dog dog){ dogDao.save(dog); } // FishService.java void save(Fish fish){ fishDao.save(fish); } // BirdService.java void save(Bird bird){ birdDao.save(bird); } 相信大家对于以上代码并不陌生。 ...

2019-07-31 · 2 min · 228 words · 翟志军 Jack Zhai

那只住在我们楼上的大黄狗

当电梯到的时候,一只大土狗也跟着我进了电梯。我按了7楼。大狗依然张开嘴吐着舌头,站在原地。电梯直达7楼,我不知道大土狗要去几楼,所以没帮它按。走出电梯后,电梯理所当然的关门,然后向下了。它还在电梯里。 一天,我们一家人挤进电梯下楼。电梯有一股恶臭。儿子叫了一声:大黄。这时,我才注意到拥挤的电梯里,大土狗在角落里。看样子,它也是要下楼。 后来,从我老婆那里了解到。她经常看到这只大土狗,所以和儿子给它起了一个名字:大黄。因为它的毛是黄色的。 而狗的主人就住在11楼。只不过,现在主人不要它了。它现在每天就睡在前主人的11楼的门外。饭点的时候,就会看到它端端正正的坐在餐桌不远处,目不转睛地看着吃饭的人。偶尔会有好心丢给吃的给它。 当听到这些时,就酸了鼻子,两眼湿润。 再后来,当我和大黄坐电梯时,都会帮它按下11楼。 某一天,在楼下的操场边上,看到大黄兴奋地围在一年轻人的身边疯跑。看得出来,年轻人是它的前主人。只是当年的主人已经不是它的主人。年轻人直视前方冷漠地自走自的,仿佛大黄不存在一样。 而我身为旁观者,本来想说点什么,最后,也只能路过。

2019-07-26 · 1 min · 8 words · 翟志军 Jack Zhai

我们不是研发,不会天天去关注代码

前段时间,项目实施人员告诉我,我写的 Ansible 脚本中有一处写死了版本号。并把代码截图给我看。我一看,这代码是老版本了。他代码应该没有更新。我这么跟他说。 然后他说出了一句出乎我意料的话:我们不是研发,不会天天去关注代码。 最后,我回了一句:要想自动化,就必须关注代码。屏幕背后的我,露出无奈与惋惜的表情。 惋惜的不是他有没有关注代码这件事情。而是他使用职位来限制住自己的能力。 不要笑话上面的同事,工作中不少这样的事情: 我是测试,那是研发的事情。 我是研发,那是运维人员的事情。 我是设计人员,不是研发。 前段时间,听另一个项目组的同事说:两周一迭代,前一周测试闲死,后一周开发闲死。 当时,我问了两个问题: 在后一周的时候,开发在干嘛? 在后一周的时候,产品经理在干嘛? 开发与产品经理都不是很忙的情况,为什么他们不可以参与测试呢?如果问他们,得到的回答可能是:因为那是测试的事情。 后来,我仔细想,那是XXX的事情,其实也不能完全怪他们。因为现实中,如果线上出现测试不到位的Bug,测试人员很可能会被 KPI。 最后,我才恍然大悟:那是XXX的事情的思维方式并不是员工原本的思维方式,而是这个管理制度下的结果。

2019-07-22 · 1 min · 18 words · 翟志军 Jack Zhai

如何设计 Ansible 的入门工作坊

本月在公司内部做了一次 Ansible 的入门工作坊。本文即对这次工作坊的设计过程进行一次总结。其他技术类的工作坊也可以参考。 设计过程大概过程如下文所述。 首先,我们需要确定参加本次工作坊的受众。他们是否具有最基本的前提。本次工作坊的受众有开发、测试、运维,还有毕业生。但是他们都会使用 shell。这已经满足最基本的前提。同时,了解受众后了,也就可以因材施教。 第二,分析工作坊的内容。Ansible 是一款上手非常容易的自动化运维工具。它的特点就是实操性非常强,不需要理解 Ansible 背后的概念就可以使用的工具。 笔者根据受众和教学内容的特点,得出本次工作坊的目标(教学目标): 知道 Ansible 是什么,并知道它的作用。 了解如何查文档。 能部署一个 Spring Boot 应用。 是不是很简单?其实不然。整个工作坊没有一个人能完成所有的任务。同时发现有运维和开发基础的同学会做得更快。 那接下来怎么实现这个目标呢?笔者使用的是任务驱动的方法。也就是受众通过做一个个任务,在任务中完成学习。同时,教师可以任务过程穿插讲相关的知识点。 以下为任务列表: 执行 ansible-playbook -i hosts playbook.yml 成功 创建用户 apps 及用户组 apps: user 模块: https://docs.ansible.com/ansible/latest/modules/user_module.html group 模块: https://docs.ansible.com/ansible/latest/modules/group_module.html 创建以下文件夹,并设置文件夹的用户和组为 apps: /apps,/apps/hello,/apps/hello/bin,/apps/hello/logs file 模块: https://docs.ansible.com/ansible/latest/modules/file_module.html 将 helloworld-0.0.2.jar copy 到 /apps/hello/bin 目录下,设置该 jar 文件的用户和用户组为 apps copy 模块: https://docs.ansible.com/ansible/latest/modules/copy_module.html 使用 template 模块将 app.service copy 到目标服务器的 /etc/systemd/system 中,并重命名 hello.service : template 模块: https://docs.ansible.com/ansible/latest/modules/template_module.html 启动 hello 服务 service 模块: https://docs.ansible.com/ansible/latest/modules/service_module.html 监听 hello 服务是否启动成功 wait_for 模块: https://docs.ansible.com/ansible/latest/modules/wait_for_module.html 为目标机器安装 JDK 1.8: 在本地仓库中创建 roles 目录 clone 代码:https://github.com/geerlingguy/ansible-role-java 到 roles 目录中 在 playbook.yml 文件中加入 ansible-role-java 的role 创建自定义 role: hello role 进入 roles 目录:cd roles 使用命令生成 role 模板:ansible-galaxy init hello 将 hello 的部署逻辑(在 playbook.yml 中)写入到 hello role 中 将 hello 部署到多台机器 需要修改 hosts 文件 多环境部署 任务的设计并不是随意的,而是有意的。比如: ...

2019-07-19 · 1 min · 146 words · 翟志军 Jack Zhai

小明说:代码是测试通过了,但是修改了一行代码,等和其他人的功能一起上吧

小明修复了 Web 后端的一个不大不小的 Bug。只修改了一行代码。并在 UAT 环境测试通过。 可是,当我问他为什么不发版时,他说: 代码是测试通过了,但是修改了一行代码,等和其他人的功能一起上吧 在我不长不短的职业生涯中,经常遇到这样的小明。我已经见怪不怪了。 笔者的观念是:如果变更的代码上线不会死人,能上就上。如果每次发版都很痛苦,那么先把发版难这个问题解决。至少也要朝着这个方向前进。 为什么我会这样觉得呢?因为: 程序员写出来的代码,只有真正运行在生产环境上了,才算完成工作。测试通过的代码不上线,就是库存。这在丰田称为“库存的浪费”。也就是不发布到生产环境,你为什么要写出来?还测试通过了。 如果一个分支停留太长时间,分支之间发生冲突的可能性就越大,而解决冲突这类操作对于产品的最终用户来说,是毫无价值的。用丰田生产方式的话说来,就是“动作上的无效劳动”。再者合并冲突过程容易再次引入缺陷。所以,应该避免分支停留过长时间。这个“过长”怎么定义,需要具体问题具体分析。 后记 其实,我很理解小明说出这样的话。大多是因为对部署没有足够的信心。对于没有自动化部署的团队来说,属于正常现象,你不能将责任推到一个人身上。而解决部署难的问题,不仅在管理上下功夫,还要在技术上下功能。

2019-07-07 · 1 min · 15 words · 翟志军 Jack Zhai

从文具的购买看一家企业的效率

Photo by **Leon Macapagal **from Pexels 团队每天都会面对着看板开早会。过完卡后,会有一些技术问题需要进行深入讨论。大家你一句我一句,实在说不清了,我就想从桌子上拿一支笔将想法画出来。 但是在会议室找不到可用的笔,然后跑到文印室也找不到。这样,几分钟过去了,5个人团队就干等着我找笔。实在找不到,我们只得打开投影仪,打开 Windows 系统自带的画图软件画起架构图。 在经历过几次这样的痛苦后,我决定找办公室负责采购的小姐姐说这事。小姐姐心好,从其它地方拿笔到我们经常早会的会议室。 最后小姐姐补充:本月的文具已购买,下月我再向经理审批再买哦。 我当时没有反应过来,没有理解她这句话的意思。 过了两个星期,我发现又没有笔可用了(后来发现是我在文印室没有找仔细)。我再次找到小姐姐。并跟她说:能不能多放一些笔? 小姐姐说:笔是统一放在文印室的,因为不知道哪个地方需要用。所以,你们有需要去文印室拿哦。 我说:文印室也没有了,没找到。 然后我实在没有忍住,一连串说了几句文具的成本是多低,节约人的时间,提高工作效率,可以节约更多的成本。 她似乎没有听进去:之前放了一支,你们又丢了。 我说:就一支,是不是别人拿到别的会议室了? 她说:不知道。我买文具是要经理审批的。 我这才想意识到,她没有决定权。问题出在经理那里。为什么经理不允许买呢? 从小姐姐那里了解到原来有一次小姐姐按往常一次提流程买笔和本子。谁知经理说:纸巾每月买可以理解,但是为什么笔和本子每个月都要买。 这就是现在为什么小姐姐很少买笔和本子的原因了。 最后,我也不提笔的事情了。因为我单独找经理谈这个事情,在当前的环境下,会被人说“跨级”。再者对小姐姐可能也不好。 谁知,过了两个星期,事业部的副经理从会议室跑出来,问:有没有见到大头笔? 我苦笑地说:没有。 苦笑是因为我猜到发生在我身上的事,也会发生在别人身上。只不过没想到,发生在了经理级别上而已。 后来,也不知道他有没有找到笔。但是那个会议室还是偶尔找不到笔。 不知道读者朋友看出来问题没有?笔者认为关键问题出在: 提效不一定非要花巨资买个 DevOps 平台,效率发生在每一个企业运营的细节。 软件工程是知识密集型工作,人与人之间需要高效沟通。那么笔、白板(或纸)就是即高效,成本又低的工具。说实在点,工作少出现一个沟通失误,节约下来的钱就可能买下整个公司几年的笔了。小姐姐没有认识到,经理也没有认识到。 企业文化让看到问题的人不敢提问题。 你没有想到吧,一个负责采购小姐姐的决定,也可能影响一家公司的效率?这里没有贬低小姐姐的意思。每个岗位都有它的意义。 这些问题怎么从根本上解决? 笔者认为,关键点是没有人从效率的角度考虑公司内部的行为。小姐姐考虑的是下次提流程时,经理不会问那样的问题。经理疑问了文具为什么用得这么快,但是并没有深究(几支笔的事情,当然没有必要深究)。其他人(可能)不想惹事,也不会找相关的人提“笔”的事情。在很多企业奉行谁提问题谁解决。有时我怀疑这句话对企业是有害的。 怎么解决呢?我只说一句:解铃还须系铃人。 一定能解决吗?我不知道。因为我也只是觉得可以解决。所以希望和更多人交流解决之道。 我把这些写出来,很有可能犯“政治错误”,得罪某些人。我还是要写出来。因为我相信还有不少企业发生类似的事情。不敢面对,怎么进步。

2019-05-27 · 1 min · 40 words · 翟志军 Jack Zhai