正文
在我职业生涯的早期,我曾在一家工作内容为构建Web内容管理系统的公司工作。他们的产品帮助营销部门可以自己管理网站的内容,而不是依靠开发人员来管理网站。该产品帮助他们的客户降低了运营成本,并帮助我学习如何构建Web应用程序。
虽然产品本身有一个非常普通的用途,但其客户缺倾向于使用它来解决非常特殊的问题。这些问题以各种眼花缭乱的方式将对产品的要求推到了极限,并且要求该产品必须提供解决方案。在这种环境中工作了十多年,让我对生产环境下的Web应用程序有了全面的了解,其中一些我们将在本文中讨论。
这些年我学到的经验之一是,对于开发Web应用程序,个别工程师倾向于非常深入地了解他们感兴趣的东西,对于不感兴趣但必要的组件缺只学习皮毛,这其实是非常“危险”。这对于具有良好沟通的工程师团队来说非常有效,因为这些组合知识将重叠以填补任何个人的局限。然而,这对于独立工程师或者在这方面经验不足的团队,就显得“危险”。
如果你是在这样的环境中开始,然后开始从头开始构建和部署整个Web应用程序,你可能很快就会理解我说的“危险”的意思。
业界已经提供了许多旨在解决这个问题的解决方案:托管Web应用程序(Beanstalk,AppEngine等),托管容器管理(Kubernetes,ECS等等)以及许多其他解决方案。一旦你启动并运行它们,它们就可以正常工作,我认为它们在解决问题方面做得很好。它们隐藏了启动和运行Web应用程序所需的大量复杂性,并且它们倾向于“刚好能工作”。
不幸的是,当它不是“刚好能工作”,或者当你需要完成一些特殊的业务时,你可能会发现自己会希望更多地了解那个不祥的黑盒子。
在这篇文章中,我将采用一个不可靠的系统,并将其演变为具有合理可靠性的系统。沿途的每一步都将使用现实中会遇到的问题作为进入下一步的目的。我没有讨论一个最终设计的每一部分,而是使用这种增量方法有助于读者了解自己的需求,以及自己目前处于哪一步。我们将从头开始构建托管Web应用程序托管服务所提供的基本结构,并希望能够详细介绍每个部分存在的意义。
让我们开始
让我们假设你有一年500美元的托管预算,因此你决定从Amazon AWS租用一台t2.medium服务器。 在撰写本文时,每年仅花费约400美元。
你事先知道你需要设计登录系统,并且你需要存储用户信息,因此你需要一个数据库。 由于预算有限,让我们在我们唯一的服务器上托管它。 最终得到的结构如下:
看起来足够了,哈哈。 事实上,它可能会稳定工作很长一段时间。因为你网站的体量还很小。 此时,你可能每天最多只能处理10次访问。 一个小实例可能已经足够了,但由于你对公司的发展持乐观态度,因此你使用t2.medium实例做出了不错的选择。
你的业务价值存储在该数据库中,因此非常重要。你应该确保就算该服务器发生故障,不会导致你的数据丢失。所以最好去确保下你没有将数据库内容存储在临时磁盘上,不然的话,如果实例被删除,你将丢失所有数据。这会非常可怕。
此外,你还应确保将备份转到外部存储。AWS S3似乎是一个放置这些的好地方,它相对便宜,所以让我们设置它。而且你肯定应该通过每隔一段时间做一次数据备份来测试它是否正常工作。
你的结构现在应该如下所示:
在这时,你已经提高了数据库的可靠性,接下来,你想通过对服务器运行负载测试来为可能到来的大规模的黑客新闻流量(译者注:原文为Hacker News,一个信息源资讯网站)峰值做好准备。一切似乎进展顺利,直到500错误开始出现,然后是404流,所以你要调查弄清楚发生了什么。
事实证明,你没有任何线索来得知网站崩溃到底是因为什么原因,因为你把日志写到控制台,而没有将控制台输出传递到日志文件中。你还看到该进程未运行,因此你默认了这就是你获得404的原因。你脸上的紧张情绪稍稍缓解了点,并且庆幸还好自己没有直接将你的网站登记到Hacker News上。
你创建了一个运行Web服务器的systemd服务来保证你的服务会在崩溃后自动重启。此外,你最终解决了日志记录问题。然后你运行另一个负载测试,以确保你已经解决了所有问题。
你又看见了500错误(幸好没有404),你检查日志以查看出错的地方。你发现数据库连接池已经饱和,该连接池设置为10。你更新了参数,重新启动数据库,然后再次运行负载测试。一切顺利,所以你决定在Hacker News上推广你的网站。
发布日
我得到了404,所以我必须检查页面的存档版本。如果有人需要,这是链接:...
妈的空白页啊!我禁用了Javascript,为什么网站作者会觉得我会取读取你的2 MB Javascript文件 ...
你的主页需要4秒钟才能加载。我居住在澳大利亚,Traceroute显示服务器托管在德克萨斯州的某个地方。另外,为什么你的网页需要2 MB的Javascript?
在混乱中,你被迫在服务器上设置了Nginx作为应用程序的反向代理,并将其配置为服务器静态404页面。 还将静态文件推送到AWS S3,这样做是为了让CloudFront CDN能够起作用,来减少澳大利亚用户的访问时间。
这时候你已经解决了当前的问题,这之后,你可以随时访问服务器并查看日志。 但你慢慢发现,你的SSH连接非常迟钝。经过检查,你发现你的日志文件已经完全耗尽了你的磁盘空间,这会使你的进程崩溃并阻止它再次启动。你创建一个更大的磁盘并在其上挂载日志。 你还设置了滚动日志来防止日志文件再次变得非常巨大。
性能问题
几个月过去了。你的用户群在慢慢增长。你的网站开始变慢。你在CloudWatch监控中注意到,这似乎只发生在中午和晚上。由于变慢的开始和结束时间每天都相同,你猜测这是由于服务器上的计划任务造成的。你检查了你的crontab,看见了你自己在午夜安排了一份工作:备份数据库。果然,你的备份需要12个小时,并且备份程序使数据库过载了,导致了网站访问极慢。
发现了这个问题后,你决定新建一个从数据库,并在从数据库上运行备份。 所以你创建了一个从数据库。在同一台服务器上运行从数据库没有多大意义,你决定,是时候扩展了!你创建两个新服务器:一个用于master数据库,另一个用于slave数据库。 你将备份更改为在从属数据库中定时运行。
组建团队
一切都运行平稳了一段时间,几个月过去了,你聘请了一个更大的开发团队,其中一位新开发人员发现了一个bug,这个bug会导致生产服务器的崩溃。此位程序员觉得是由于开发环境与生产不同导致的。他说的话有些道理,你听起来觉得很对,所以你决定把这个问题解决。
你构建了更多不同的环境:Staging,QA和生产环境。幸运的是,你从写这个项目第一天开始就搭建了自动基础架构,因此环境的增加很容易。并且从第一天起,你就使用了良好的持续交付机制,因此你可以轻松地从管道构建新分支。
在这之后,营销部门希望推出v2.0版本。你不确定v2.0版本是什么,但无论如何你还是决定做了。是时候准备另一次流量的飙升了。在Web服务器上运行的服务已经接近服务器的峰值利用率,因此你决定开始对流量进行负载平衡。亚马逊ELB能够让你轻松上手。在这个时候,你还发现博客文章中的分层图表应该从上到下而不是从左到右显示图层。:)
你再次将你的网站发布到Hacker News。 它撑住了巨大的流量,取得了极大的成功!
这一切似乎都很棒,直到有一天,你去检查了你的日志。 这时你才发现,检查一次日志,需要一个小时,因为要检查12台服务器(每个环境中有4台服务器),这显得很麻烦。幸运的是,你的公司现在已经赚了足够的钱来实现ELK堆栈(ElasticSearch,LogStash,Kibana),你构建了一个ELK环境并将它用在了所有环境中。
这样一来,你可以轻松地查看日志了,你发现,有很多奇怪的东西混在了日志里。
GET /wp-login.php HTTP/1.1" 404 169 "-" "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:40.0) Gecko/20100101 Firefox/40.1 GET /wp-login.php HTTP/1.1" 404 169 "-" "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:40.0) Gecko/20100101 Firefox/40.1 GET /wp-login.php HTTP/1.1" 404 169 "-" "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:40.0) Gecko/20100101 Firefox/40.1 GET /wp-login.php HTTP/1.1" 404 169 "-" "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:40.0) Gecko/20100101 Firefox/40.1 GET /wp-login.php HTTP/1.1" 404 169 "-" "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:40.0) Gecko/20100101 Firefox/40.1 GET /wp-login.php HTTP/1.1" 404 169 "-" "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:40.0) Gecko/20100101 Firefox/40.1 GET /wp-login.php HTTP/1.1" 404 169 "-" "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:40.0) Gecko/20100101 Firefox/40.1 GET /wp-login.php HTTP/1.1" 404 169 "-" "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:40.0) Gecko/20100101 Firefox/40.1 GET /wp-login.php HTTP/1.1" 404 169 "-" "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:40.0) Gecko/20100101 Firefox/40.1 GET /wp-login.php HTTP/1.1" 404 169 "-" "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:40.0) Gecko/20100101 Firefox/40.1 复制代码
你根本没运行过任何PHP有关的服务,这非常令人担忧。 你注意到数据库服务器上也有类似的可疑日志,现在你很想问自己为什么将它们的端口暴露在了外网上,是时候区分公共和私人子网了。
OK,你又开始检查你的日志。黑客还是可以攻击你,但现在它们仅限于负载均衡器上的端口80,因为你的应用程序服务器,数据库服务器和ELK堆栈不再暴露在互联网上,这下你舒坦了。
尽管进行了集中式日志记录,但你还是不得不通过手动检查日志来发现中断。 你可以使用Amazon CloudWatch设置磁盘,CPU和网络警报,以便在达到80%容量时向你发送电子邮件。还能说什么,简直完美。
一帆风顺
开玩笑!没有一帆风顺的事情,总有事情会出错。幸运的是,你有很多工具可以更轻松地处理这些问题。
我们构建了一个可扩展的Web应用程序,包括备份,回滚,集中式日志记录,监控和警报。这是一个很好的总结时刻,因为这里的增长往往取决于特定应用的需求。
业界提供了许多托管选项,可以为你处理大部分内容。你可以依靠Beanstalk,AppEngine,GKE,ECS等,而不是自己构建所有这些服务。大多数这些服务都会自动设置合理的权限,负载均衡器,子网等。他们需要花费很多麻烦才能使应用程序快速启动并运行,这样可以确保你的站点长时间运行所需的可靠性。
无论如何,我认为了解这些平台提供的功能以及提供它们的原因是有用的。它可以根据你自己的需求更轻松地选择平台。一旦你在平台上运行了所有东西,你就已经弄清楚了这个工具的这些重要方面是如何工作的。当出现问题时,有助于了解你拥有解决问题的必要工具。
总结
这篇文章跳过了很多细节问题。它不包括如何自动创建基础结构,如何配置服务器或如何配置服务器。它不包括如何创建开发环境,如何设置连续交付管道,或如何执行部署或回滚。它不包括网络安全,秘密共享或最小特权原则。它不包括不可变基础架构或无状态服务器或迁移的重要性。这些主题中的每一个都需要自己的帖子。
本文的目的主要是提供一个合理的生产Web应用程序应该是什么样子的高级概述。 未来的帖子可以参考这个并扩展它。