我是一名Python+Go开发人员,过去几年一直在全球范围内从事应用程序开发工作。我和我的团队每天与大约200万客户打交道,处理这种规模的交易并不容易,所以我想分享我过去几年的一些经验和技巧。安全永远不应该是事后才想到的应用程序安全永远不应该是事后才想到的,或者降级为“事后才考虑的事情”。我们需要从应用程序开发的第一天开始,而不是第300天,将强大的安全考虑纳入每个讨论和开发过程。将安全问题放在最后会增加开发时间,并需要重构以适应安全问题。更糟糕的是,您没有足够的时间来修复任何错误并最终交付易受攻击的代码。看看像雅虎这样的公司,看看他们是怎么做到的。不同的应用有不同的需求。根据需要为应用进行技术选择,而不是被行政压力或市场热情所迫。每个应用都是不同的,这一点不需要重复。没有一套适用于所有应用程序的神圣指南(当然包括这个)。当开始开发一个新的应用程序时,应用程序本身及其架构决定了它将使用哪种技术或将基于哪个平台。在考虑“我的应用程序需要什么?”这个问题之前决定使用gRPC还是Kubernetes只有一个后果:在你开始编写代码之前,你为未来设置了障碍,这就是为什么我们陷入像Canonical这样的情况,这让公司处于为物联网设备提供Kubernetes服务的荒谬境地。引用JeffGoldblum的话,“你的科学家们对他们能做的事情如此着迷,他们不会停下来想知道他们是否应该这样做”你可能不需要微服务我知道,微服务很迷人。能够独立地扩展应用程序中的各种组件是令人兴奋的,并且证明了在特定代码库上正在进行的工作是合理的。但是,如果您诚实地考虑一下,您可能不需要“微服务化”,原因如下:“我想将功能X与功能Y分离”,“以避免糟糕的代码污染应用程序的其余部分”,或“当应用崩溃,最小化影响半径”,这些都不是微服务的借口,而是糟糕的开发实践(保持现在的样子,需要的时候触碰),代码审查标准需要更严格(不满足要求的代码是不暂时合并),缺乏细粒度安全控制的性能。您是否真的需要独立扩展应用程序的不同部分,并且目前有能力解决一个或多个组件(例如登录流程)?您的应用程序是否运行在基于虚拟服务器的架构上并希望降低成本?那么你不应该探索微服务。在最好的情况下,您可以收支平衡。在最坏的情况下,需要启动额外的实例。假设您有一个包含五个服务的单体应用程序,您将其分解为多个微服务。现在,您有5个应用程序,并且要么a)为它们启动专用实例,将初始内存占用量增加5倍,要么b)使用现有内存占用量并增加管理它的运营成本。拥有一个标准的开发环境当您与多个开发人员一起工作时,最有益的事情之一就是在整个团队中标准化开发环境。这并不意味着您必须同时使用基于容器的虚拟化开发环境“voodoo”。当然,如果你愿意,你可以这样做。但是像使用相同版本的开发语言这样简单的事情有时可以在团队中创造奇迹。当你的同事正在Go1.11上开发时,你试图基于Go1.12定位一个错误,你会哭的。协作升级版本可能很困难,但如果操作正确,则可以很顺利。配置比看起来难,按需计划与一些热门网站所说的相反,配置实际上比“把所有东西都扔进环境变量”要复杂得多。在我看来,配置一个应用至少应该有四种方式:代码中的默认配置本地配置文件命令行标志环境变量远程配置源(比如Hashicorp的Consul)我把远程配置当作可选的,但是其他四种是必须的.在开发过程中,至少可以说将您依赖的27个配置值扔到环境变量中只是为了在本地运行您的应用程序是令人沮丧的。或者您可能需要更好的自动化和Makefile?提供一种类似于application.yaml文件的方式来访问本地配置源,允许您设置默认的“dev”配置。还有,代码中的默认配置,就是说你只需要设置需要修改的默认配置即可。当通过systemd等初始化系统运行应用程序时,命令行标志非常有用,因为它可以在跟踪进程时更轻松地查看配置信息。当应用程序运行在容器中时,环境变量非常有用,但是密钥等配置不适合环境变量。切勿将密码、身份验证令牌、证书和其他您不想泄露的密钥放入环境变量中,因为这是非常不安全的,并且可能被主机上的任何进程读取。你应该始终使用密钥管理器来管理你的密钥,我个人的选择是Hashicorp的Vault,你可以选择最适合你的。按需使用包管理,不仅仅是因为它有效。我们都知道“left-pad”事件,为什么一个只有11行代码的NPM包被从存储库中删除会对整个网络产生影响?让我们不要这样做。包应该只在有合理需要导入包的情况下使用,比如特定厂商的SDK(比如AWS的SDK),是一组非常详细的标准库的抽象(这也是为什么大家都喜欢用Python的Requests库,而不是使用urllib),或者导入广泛使用的框架,例如Go语言的EchoHTTP服务器或Python语言的FlaskWSGI服务器。一些方便的库也可用,例如LodashforJavaScript,它提供了一些标准库中没有的常用功能和附加功能。这些外部依赖项应该使开发更容易,并消除对手写样板或集成代码的需要——这些是包管理的好处,但是,就像left-pad一样,很容易陷入这个陷阱——“这里只是一个函数解决问题的图书馆,就用旧的吧。”您导入的每个依赖项都会增加不稳定、不安全或根本无法维护的风险。每次导入包时,新依赖项本身的风险就会增加——这也称为传递依赖项。如果您导入一个包,并且该包又导入了五个包,那么您现在将继承这五个依赖项以及它们带来的所有风险和危险。我和业内很多人都认为包不应该引入传递依赖,但这是不现实的,至少包应该尽可能精简,并为用户提供在需要更强大的功能时显式扩展的方式。我现在遵循的一个简单规则是,如果我导入一个我可以在10-15分钟内自己实现的库,就自己实现它。否则,如果有的话,我会使用外部库。在开发过程中牢记这条规则可以避免导入不必要的包。如果你不想每次都从头编写一个新的HTTP服务器来服务API,那么这个方法就足够了。如果没有必要,就不需要抽象。一个容易落入的大陷阱是“抽象一切”黑洞。“我以后可能会重用它”的想法可能会让你陷入一些黑暗和可怕的面向对象的误入歧途,我想出了原因,DRY原则根深蒂固并且有充分的理由。但是你花了太多时间抽象而没有足够的时间编写逻辑。只是安心地编写代码,如果您发现需要实现与您之前完成的其他工作类似的方法或功能,那么您可以返回并对其进行抽象——但还是要有节制。我个人倾向于遵循这样的原则,如果在抽象之前是一个简单的三行函数,最好保持不变,然后再重复。如果只有3行代码,也许问问自己,是否需要将其转换为函数?你应该时不时地“涅槃”你的项目,但你绝对需要它。偶尔从头开始是一件好事,它允许您从代码中删除垃圾并实现新想法,而无需修改现有代码库并迫使每个人重新评估项目。将项目想象成森林,每一行代码都是绵延数英里的森林中的一棵巨大的松树。随着森林的发展,灌木丛、废弃的松针、松果、枯枝和其他碎片散落在整个森林中。这是你讨厌的东西,你的技术债务。它会不断堆积,直到发生一些翻天覆地的变化。对于森林,这种变化以野火的形式出现。大火席卷了森林,烧毁了成堆的无用垃圾。留下足够厚的树皮的树木,未成熟或发育不全的树木在火灾中被摧毁。虽然这看起来像是森林的尽头,但它隐藏着一个大秘密:森林一直在等待这场大火。耐心地,年复一年,森林一直在等待大火净化自己并带来变化。因为当大火在树冠下肆虐时,下一代参天大树正在松果中发芽。随着大火蔓延到森林地面,易受伤害的幼树出现并加入了变黑的幸存者行列。这就是您的应用程序应有的样子:有弹性的、编写良好的部分在清除后幸存下来,而其他部分则为新想法和新代码让路——就像凤凰从灰烬中重生一样。除非你是谷歌,否则你不是谷歌,但如果你真的是谷歌,你为什么还要阅读这篇文章?关键是你可能不是谷歌,不是微软,不是亚马逊,不是Twitter,不是Facebook。您无需在全球17个不同数据中心的10,000台裸机服务器上编排150,000个容器。您的问题通常不会对世界上的每个人产生太大影响。我们为什么要讨论这个?因为你的规模决定了你的操作平台。如果你正在运行几百个容器,你真的需要Kubernetes吗?你真的需要自己运行Kubernetes,还是只想将它添加到你的简历中?像HashiCorpNomad这样的工具非常适合解决中小型问题:它易于配置,几乎不需要维护,有很好的文档,并且非常容易迁移应用程序,因为它适用于容器、系统进程和JVM应用程序。如果真要用Kubernetes,为什么不基于Rancher来管理,把繁琐的细节抽象出来?运行像Kubernetes(专为谷歌这样的公司设计)这样复杂的系统所带来的麻烦和开销对于一个团队来说是难以承受的。我什至会说初创公司应该不惜一切代价避免它,除非他们的产品是特定于Kubernetes的。需要注意的是在各自的云产品中使用谷歌、亚马逊和微软提供的托管服务。因为他们管理所有丑陋的东西,所以很多开销都不在你的肩上。但是不要让我发现你在IoT设备上使用Kubernetes,不要。不要照搬网上陌生人的开发理念。您应该对要遵循的开发风格和原则形成自己的想法。以上10条建议也仅供讨论,我只是一个过客。
