TL;DR
请直接跳到最后看总结。
Dev 与 Ops 的技术栈对比
DevOps 是最近几年非常流行的词汇,有越来越多的公司开始实践 DevOps,但如何可以真正的把 DevOps 做好呢?
我曾经和几个业内朋友聊过,为什么现在的 DevOps 培训关注在协作或者工具上?这些就是把 DevOps 做好落地所需要的吗?那几个朋友也同意,只是关注在协作和工具上是不够的。
首先,运维(Ops)是一个全面并复杂的工作,并不是把开发团队开发完成的应用部署到生产环境,如果出了问题就找开发团队来解决。运维包括了系统、网络、数据库、安全、开发等不同领域的工作。要把运维这个工作做好也不止是会一些工具这么简单的事情。其次,也不只有 DevOps 才关注团队之间的协作。如果协作和工具对于做好 DevOps 是足够的,就好比协作和开发语言或者框架对于做好开发是足够的。但我们都知道,对于开发来说,除了协作、编程语言、开发框架外还有不少很重要的技能,而这些技能也是区分资深开发工程师和初级工程师的标准之一。作为一名资深的开发工程师,除了基本的编程语言和框架外,还需要了解和掌握一些高级知识。比如资深的 Java 开发工程师需要了解并掌握反射、类加载、垃圾回收、多线程开发、调试工具等等高级知识。这些高级知识可以帮助他们更好地处理一些疑难复杂问题,并构建更加健壮灵活的应用程序。而且也有越来越多的开发工程师开始意识到 TDD、重构这类工程实践可以帮助他们提升开发技能,以开发出代码质量更好,也易维护的应用。
对于 DevOps 来说,技术栈不仅仅包括开发和运维,还包括测试、安全、网络、业务等等领域,但开发和运维是相对重要的两个。对于提升开发质量的高级知识和技能,很多人都已经很熟悉了,但有哪些是做好运维的高级知识和技能呢?如果运维做不好,那也很难做好 DevOps 的落地实践。下面我们以 Java Web 开发工程师和 Unix/Linux 运维工程师为例,来做一个技术栈的对比。通过这个技术栈的对比,可以让我们更加清楚的看到做好运维的基础在哪里。
Java Web 开发技术栈
主要开发语言
对于 Java 开发工程师来说,哪个编程语言是主要开发语言呢?很显然是 Java。如果一个工程师的主要开发语言是 PHP,那他就是 PHP 开发工程师了。作为一名合格的 Java 开发工程师,显然要对 Java 语言非常熟悉。不需要对 Java 语言的每一个细节都了如指掌,但最起码对绝大多数的语法非常了解。很难想象一名对于 if
语句都不甚明了的 Java 开发工程师可以开发出高质量的 Java 应用。
还需要掌握其他编程语言或标记语言
Java 工程师在开发应用时只会用 Java 语言是不够的,还需要了解并掌握一些其他编程语言或标记语言。比如 SQL、JavaScript、CSS、HTML、XML 等等。如果使用了一些CSS框架,可能还要掌握 Less (层叠样式表) 或者 Sass。
核心开发库
在学习 Java 编程的时候,主要是在学习什么?事实上Java 语法并不多、也不复杂,在几天内就可以把所有的语法学习完毕。那在学习 Java 编程的时候是在学习什么呢?其实是在学习 Java Development Kit(JDK),这是 Java 开发的核心开发库。在 JDK 中有非常多的包和类,几乎没人可以记得有哪些包,每个包里面有哪些类。但作为一名合格的 Java 开发者,一定会熟悉一些常用包,比如: java.lang、 java.util、 java.io 等等。同样的,也很少有人可以记得这些常用的包里面有哪些类,但一定非常熟悉一些常用的类及其方法,比如:java.lang.String、java.io.File、java.util.HashMap 等等。
如果要开发嵌入式或者企业级 Java 应用,可能还要熟练掌握 Java ME 或者 Jakarta EE(以前的 Java EE、J2EE)。
除了 JDK 的核心类库以外,资深的 Java 工程师还要了解很多 Java 虚拟机的相关知识。比如Java 内存模型、垃圾回收、Java 的类加载、高效并发的 Java 多线程开发技术等等。
第三方类库
理论上,当一名 Java 开发工程师掌握了 Java 语法以及 JDK,并且也熟悉一些第三方的语言,他已经可以开发出几乎任何的 Java 应用程序了,虽然可能会花费比较多的精力和代价。我们可能会使用一些第三方的类库来避免『重新发明轮子』,比如 Apache Commons、Google Guava、SLF4J 等等。借助很多的第三方类库,Java 开发工程师可以非常方便的处理 Excel 文档、解码音频文件、校验 OpenPGP 签名、处理日志等等。这些第三方类库可能也依赖其他的类库,所以可能就需要 Apache Maven 或者 Gradle 来帮助进行依赖管理和构建。
框架
虽然有很多的第三方类库可以帮助 Java 开发工程师减少一些重复的代码开发工作,但是对于不同的项目可能会有一些类似的行为逻辑。比如对于绝大多数的 Web 项目,总是需要处理模板、数据与表单的绑定、异常的拦截和处理、权限和安全认证等等重复的工作,于是需要开发框架来简化这些重复的工作。开发框架与具体的应用和业务无关,但是实现了最为基本的软件架构和体系,并提供了通用的功能,以便让开发者关注在业务逻辑的实现上。所以,开发框架对于业务逻辑来说什么也没有做,开发工程师还是需要自己去实现所有的业务逻辑。常见的 Java 开发框架有 Spring Framework、Apache Struts 2、Play Framework 等。
框架固然重要,但编程语言的基础和核心开发库的掌握更重要。尽管有的公司内部有自己开发框架,并且内部的开发框架也实现了一些通用的业务逻辑。但对于每一个特定的内部项目来说,还是需要开发工程师去实现这些特定的业务逻辑代码。
以上已经列出了最为基本的 Java Web 开发技术栈,绝大部分的 Java Web 应用都是基于以上的技术栈来开发的。
基本的运行环境
对于 Java Web 应用,最基本的运行环境有 Apache Tomcat、Jetty 等。通常 Java 开发工程师会在开发环境、测试环境使用这些基本的运行环境来调试、测试应用程序。如果没有太多的用户,甚至也可以用到生产环境中。
一旦需要为更多的用户提供服务,单个的最基本的运行环境可能就不足以支撑了。
集群和高可用的运行环境
虽然也可以用 Tomcat 或者 Jetty 来搭建一个规模不大的集群环境,但是使用一些 Java 企业级应用服务器会让事情变的更简单,比如 IBM WebSphere、Oracle WebLogic Server、JBoss Enterprise Application Platform 等等。不仅有了更高的性能、更高可用性、增强的安全控制,也更方便的去管理和部署 Java 应用集群。
Java 的开发工作环境
作为一名 Java 开发工程师,平时的工作环境通常是在一个 Java 集成开发环境 中,可以是 IntelliJ IDEA、Eclipse 或者 NetBeans,甚至是使用 Vim 或者 Emacs 搭配一些 Java 开发相关的插件和命令行。不管是哪一个,至少一定会非常熟悉其中的一个。
资深的 Java 开发工程师可以在自己熟悉的开发环境中非常高效的工作,熟悉常用的配置、快捷键。通常通过观察一名 Java 开发工程师对于日常工作环境的熟练程度,基本上可以判断是否是一名经验丰富的开发工程师。
Unix/Linux 运维技术栈
主要的开发语言
Java 开发工程师的主要工作语言是 Java。那什么是 Unix/Linux 运维工程师的主要语言呢?有一些人会说是 Python,但其实应该是 Bash 或者其它某一个 Unix shell。对于这个结论,有些人可能会对此有些争议。现在我先简单的说明一下,首先 Bash 是一个 Shell,而 Python 不是。我们必须通过操作系统来使用计算机,但我们无法直接使用操作系统内核。Shell 是一个用户界面,用于访问操作系统内核所提供的服务,比如文件管理、进程管理等。其次 Bash 作为一个编程语言,绝大部分的人对 Bash 的语法了解比较有限。常见的一个误区是,认为 Bash 的语法比较奇怪。比如绝大部分编程语言的 if
、while
等关键字后面是圆括号,而 Bash 用的是方括号 [ ]
,并且这个方括号和表达式之间必须有个空格,而且如果没有空格就会出现语法错误。如果认为这是 Bash 奇怪语法的一部分,那就完全错误了。
请看下面的 5 个 Bash if
语句的例子,这些语句之间的区别就是 if
后面的『括号』,哪些语法是正确的?
if [ ... ]; then ...; fi
if [[ ... ]]; then ...; fi
if ( ... ); then ...; fi
if (( ... )); then ...; fi
if { ... }; then ...; fi
大部分人会认为第1、2个是正确的,毕竟经常在各种 Shell Script 里面见到,虽然不一定明白一个方括号和两个方括号的区别。少部分人猜测第3、4个或许也是正确的,多数也仅仅只是猜测而已。但几乎所有的人认为第5个是错误的,因为从来没有见过 if
关键字后面可以用『花括号』作为语法的编程语言。
其实,所有的5个语法都是正确的。如果答案不是这个,说明对 Bash 的语法并不熟悉,甚至不了解。很遗憾的是,甚至市面上的一些有关 Unix/Linux 运维的技术书籍里面也有这样的错误,有从国外翻译的也有国人编写的。
如果一名开发者在对于一个编程语言了解极为有限,甚至连语法都不熟悉的情况下,是如何得出这个语言『很简单』、『仅适合简单的任务,不适合做复杂的工作』、『语法怪异』、『没有某某编程语言强大』等结论的?
我猜这来源于一个根深蒂固的认知,认为脚本和代码不一样,脚本更简单,无需编译就可以执行。比如 Java 代码需要用 javac
命令编译后,才能用 java
命令来执行,不可以像 Bash 这样直接运行。
下面这个截图没有做过任何的修改,从截图上可以看到 Java 代码可以像 Bash 脚本一样直接执行!这怎么可能?但是在我的电脑上可以随时演示这个奇怪的『真正的 Java Script』例子,因为我用了 Zsh 的一些特性来实现了直接执行 Java 代码的功能。我只是通过这个例子来展示代码和脚本其实是同样的东西! 脚本只是隐含了编译的过程,没有显式的编译并不意味着不需要编译,难道 CPU 可以直接执行脚本吗?别忘记在十多年前,我们不也把 Python,JavaScript 称作脚本语言吗?但现在都叫它们代码!
下面是我拍摄的几本书里面关于 Bash if
语法错误的例子:
这里就不继续讨论了,我会在另一篇文章里来单独聊一下 Bash。
还需要掌握其他编程语言或标记语言
仅仅会用 Shell 是不够的,合格的运维工程师也需要了解并掌握其他语言,比如:Python、Perl、SQL、XML 等等。也包括一些 sed
、awk
这些命令也有自己的一套开发语言。
核心开发库
Java Development Kit(JDK)是 Java 开发的核心库,每一位合格的 Java 开发工程师都应该了解并掌握。那对于 Unix/Linux 运维工程师来说,运维工作的核心开发库是什么?
在学习 Java 的时候,我们花了绝大部分的时间在学习 JDK 上。那在学习 Shell 的时候,我们也不是一直在学习 Bash 的语法。虽然 Bash 的语法与其他编程语言有些不同,但也不多不复杂,只要花上几天也可以把所有语法了解完毕。那花了绝大部分的时间是在学习什么呢?是 Unix/Linux 的核心命令。
任何一个 Java 源码里面都会用到很多 JDK 里面的类库,同样,任何一个 Bash 脚本里面也会使用很多的 Unix/Linux 核心命令,比如 ls、cd、ps、grep、kill、cut、sort、uniq、wc、mkdir、rm、……
。这些命令会随着 Unix/Linux 一起发布,随着系统一起更新。通常位于 /bin
、/usr/bin
、/sbin
、/usr/sbin
等路径中。
与 Java 开发类似,几乎没有谁可以记得 JDK 的所有包和类。同样的,对于运维工程师来说也很少有谁可以记得所有的 Unix/Linux 的核心命令以及每个命令的所有选项。但是作为合格的运维工程师,需要非常熟悉并掌握一些常用的核心命令,以及这些命令常用的选项参数。
资深的 Java 工程师需要了解 JDK 和 Java 虚拟机的一些高级知识。对于资深的运维工程师来说,也一样需要了解 Unix/Linux 的一些高级知识,比如信号、进程、内存管理、磁盘管理、RAID、防火墙、路由表等等。所以如果一名运维工程师不明白 HUP 信号,而到处使用 nohup 命令启动程序,或者不分青红皂白的总是使用 kill -9
来杀掉进程,很难相信他会是一名资深或者合格的运维工程师。
第三方类库
理论上,当一名运维工程师掌握了 Shell、其他编程语言以及Unix/Linux 的核心命令,他已经可以做几乎所有的运维工作了,但是将会花费他很多的精力和代价。比如要在不同的服务器之间同步文件,是可以通过 Shell 以及系统的核心命令来完成这个工作,但使用类似 rsync
这样的第三方工具会节省很多的时间和精力。
Unix/Linux 运维的第三方库就是不属于操作系统核心命令的那些命令,比如 rsync、 curl、zip、unzip、unrar、tmux、Xvnc、……
等等,这些命令通常位于 /usr/bin
、/usr/local/bin
等路径中。这些第三方的工具可就非常的多了。
框架
Java 开发者有不少的 Java 开发框架可以使用,比如 Spring、Struts 什么的。那运维的框架有哪些呢?或者运维工作中有『框架』这样的工具吗?好像从来没有听说过。
让我们先看一下 Java 开发工程师使用的开发框架,这些框架提供了一些通用的功能,可以帮助我们去做一些重复的工作,但是与具体的业务无关。在运维工程师在工作中有哪些通用重复的工作呢?比如:在安装一个软件包之前,可能需要检查这个软件包是否已经安装;在启动一个服务之前,要检查这个服务是否已经启动;在修改用户密码之前,先检查用户的密码是不是就是当前就要修改的这个密码;在完成一系列的运维工作后,可能需要生成一份报表,在哪些服务器上做了哪些变更,哪些服务器出现了错误等等;……。
所以配置管理工具就好比运维工程师的开发框架,比如 Puppet、Chef、Ansible 等等。框架与具体的业务无关,运维工程师需要在服务器上安装什么软件包、需要做什么配置变更、该向哪个进程发送什么信号,不会因为使用了配置管理工具就可以不用去做。也或者,配置管理工具虽然可以帮助运维工程师重启某个特定的服务,但对于自定义的应用来说,重启时该做什么操作、需要监控哪个进程、应该切换到哪个用户的权限下,这些工作还是需要运维工程师自己去实现。
以上列出了最基本的 Unix/Linux 运维工程师的技术栈,绝大部分的 Unix/Linux 运行环境都是基于这个技术栈来维护管理的。
基本的运行环境
对于运维工程师来说,最基本的管理环境就是一台独立的服务器、虚拟机、VPS、或者一个 Docker 容器等等。通常开发环境或者测试环境都运行在一个比较基本的运行环境中,如果没有太多的用户,也可以用于生产环境。
一旦要为更多的用户提供服务,一台或者几台服务器可能就不够了,需要更多的服务器、VPS、Docker 容器。
集群和高可用的运行环境
如果运行节点很多,环境也比较复杂,可能就需要比如 Amazon AWS、OpenStack、Google云端平台 等平台帮我们管理大规模的 VPS 环境。对于 Docker 容器,可能就需要 Kubernetes、Docker Swarm 等平台。
借助这些 IaaS 平台,不仅有了更灵活的管理、更高的可用性和安全性。运维工程师可以非常方便的把应用部署到一个集群中,或者一个简单的命令就可以增加或减少服务节点数量,监控和维护整个应用集群。
运维的工作环境
Java IDE 是 Java 开发工程师的工作环境,运维工程师的工作环境呢?有人会说是 Vim,因为很多的运维工程师在服务器上都是用 Vim 来修改配置文件的。Java 工程师可以在一个 Java IDE 中去完成几乎所有的开发工作,运维工程师可以不用退出 Vim 而完成几乎所有的工作吗?(如果是使用 Emacs 还是有可能的,但 Vim 真的有点儿够呛。开个玩笑,不是要在这里引发编辑器之战,但的确也是事实!)
事实上,运维工程师的工作环境就是操作系统的 Shell,对于 Unix/Linux 来说就是命令行 或者 GNOME 这类的图形用户界面。
一名合格的运维工程师一定会非常熟悉他的工作环境的,如果一位 Windows 运维工程师不知道 Ctrl-x
、Ctrl-v
、Ctrl-z
等常用快捷键,很难相信他会是一位合格或者资深的 Windows 系统的运维工程师。有多少人知道可以在命令行进行剪切、粘贴、撤销等操作?
总结
通过这个对比,可以帮助我们看清楚一些问题。比如有人会问,已经在项目中使用了 Kubernates,还有必要使用 Ansible 吗?这就好比已经在项目中使用了 JBoss,还有必要使用 Spring Framework 吗?还有人会问,Unix/Linux 的各种命令实在太多了,学习使用 Chef 是不是可以容易点儿?这就好比学习 JDK 的各种类库太多了,学习 Play Framework 是不是可以让开发更容易些?
基础技能
从这个对比可以看出,表格的上半部分是基础技能,不管是对于 Java 开发工程师还是 Unix/Linux 运维工程师。
如果一个团队的开发质量比较差,系统 bug 多,对这个团队进行三天的 WebSphere 或者 Tomcat 培训可以起多大的作用?运维工作的质量不高,系统不稳定,三天的 Docker 或者 Kubernates 培训能改善多少?想想看,在三天的 Docker、Kubernates 培训中,花在 Dockerfile
的 RUN
指令后面的脚本上的时间有多少?花在与命令行与各种奇怪问题斗争的时间有多少?
有很多资深的开发工程师在写代码的时候总是会记得重构代码、要解耦、注意好的命名,但一写 Shell 脚本就全都忘记了。别忘记,脚本就是代码,代码有各种臭味,脚本也有。
一名优秀的运维工程师一定是优秀的开发工程师,但优秀的开发工程师不一定是优秀的运维工程师。
[…] 要想了解运维工作的知识体系以及技术脉络请看DevOps 技术栈。再看看你是否也有关于 Bash 的 10 个常见误解?要想写出可靠稳定的 Bash 脚本,那一定要写Bash 脚本的单元测试。 […]