创建一个互联网公司需要几个人?一位来自旧金山的软件工程师告诉我们:一个就够了。这位工程师创建了一个名为「Listen Notes」的播客搜索引擎,像谷歌一样可以方便地搜索海量播客资源。在这篇文章中,这位工程师向我们介绍了他创建「Listen Notes」用到的各种「无聊」技术。
Listen Notes 地址:https://www.listennotes.com/
Listen Notes 是一个播客搜索引擎和数据库。而 Listen Notes 背后的技术实际上毫无新意。没有 AI,没有深度学习,没有区块链。「任何说我必须使用 AI 的人都没有使用真正的 AI」,文章作者这样表示。
作者在前言中说:「阅读完本文后,读者应该能够复制我为 Listen Notes 构建的内容,或者轻松地实现类似的操作。你无需聘请很多工程师。请记住,当 Instagram 融了 5750 万美元并被 Facebook 以 10 亿美元收购时,他们只有 13 名员工——而且并非所有人都是工程师。Instagram 的故事发生在 2012 年初。现在是 2019 年了,一个小型工程团队甚至一个人创造一些有意义的东西比以往任何时候都有可能。」
这篇介绍了用很多「无聊」技术创建互联网产品的文章引起了很多网友的讨论,有人表示,技术本身就是一种手段,如果能够很好地实现产品的功能,即使是「无聊」的技术也会很棒。
也有网友表示,能够一个人同时掌握前端、数据库、搜索引擎、存储、集群、开发、源控制和权限管理等很多技术是非常难得的。
在下文中,作者从前端、后端、API 等几个角度介绍了自己用到的技术细节。
概述
让我们从 Listen Notes 项目的需求和功能说起。
Listen Notes 为终端用户做了两件事:
- 为播客听众提供了网站 ListenNotes.com。该网站可以提供搜索引擎、播客数据库、「稍后听」播放列表、音频剪辑以及「收听提醒」(当新播客中提及指定的关键字时,它会通知你)。
- 开发人员的播客搜索和目录 API。我们需要跟踪 API 使用情况,从付费用户处获取资金,进行客户支持等等。
一切都在 AWS 上运行。截至 2019 年 5 月 5 日有 20 台服务器在工作:
运行 Listen Notes 的服务器。
读者可以通过主机名轻松猜出每个服务器的任务。
- production-web 为 ListenNotes.com 提供网络流量。
- production-api 提供 api 流量。我们运行两个版本的 API(截至 2019 年 5 月 4 日),即 v1api(旧版本)和 v2api(新版本)。
- production-db 运行 PostgreSQL(主服务器和副服务器)
- production-es 运行 Elasticsearch 集群。
- production-worker 运行离线处理任务,以使播客数据库始终保持最新状态,并提供一些神奇功能(例如搜索结果排名、剧集/播客推荐......)。
- production-lb 是负载均衡器。为方便起见,我还在这台服务器上运行 Redis 和 RabbitMQ。我知道这不是最理想的方案。反正我不是完美的人:)
- production-pangu 是类似生产的服务器,我有时会运行一次性脚本和测试更改。那「pangu」是什么意思?请自行搜索。
这些服务器大多数都可以水平扩展。这就是为什么我将它们命名为 production-something1,production-something2 ...... 将 production-something3 和 production-something4 再加入其中就非常容易了。
后端
整个后端是用 Django/Python3 编写的。操作系统是 Ubuntu。
使用 uWSGI 来提供网络流量。NGINX 放在 uWSGI 进程前面充当负载均衡器。
主要数据存储是 PostgreSQL,我有多年开发和运营经验——经过测试的技术很完善,所以我晚上能睡得着觉。Redis 用于缓存、统计数据等多个地方。Elasticsearch 用来索引播客和剧集并提供搜索查询,就像大多数无聊的企业一样。
Celery 用于离线处理。而 Celery Beat 用于调度任务,类似于 Cron 作业,但性能更佳。如果将来 Listen Notes 越来越受欢迎并且 Celery&Beat 导致一些扩展问题,我可能会转向我为前雇主做的两个项目:ndkale 和 ndscheduler。
Supervisord 用于每台服务器上的进程管理。
那 Docker / Kubernetes / serverless 怎么样?不。随着经验积累,你就会知道什么时候不要过度设计。实际上,我在 2014 年为我的前雇主做过一些早期的 Docker 工作,这对于一家市值十亿美元的中等规模创业公司来说是件好事,但对于一个小型创业公司来说可能有点「大炮打蚊子」了。
前端
网页前端主要使用 React+Redux+Webpack+ES。这是非常标准的。在部署到生产时,JS 包将上传到 Amazon S3 并通过 CloudFront 提供服务。
在 ListenNotes.com 上,大多数网页都是半服务器端呈现(Django 模板)和半客户端呈现(React)。服务器端呈现部分提供网页的样板,客户端呈现部分基本上是交互式网页应用程序。但是一些网页完全通过服务器端呈现,因为我的懒惰使事情变得完美以及出现一些潜在的 SEO 优势。
音频播放器
我使用了一个经过大量修改的 react-media-player 在 ListenNotes.com 上构建音频播放器,该播放器应用在 Listen Notes 网站、Twitter 嵌入式播放器和第三方网站上的嵌入式播放器中:
第三方网站上的嵌入式播放器。
播客 API
我们为开发人员提供简单可靠的播客 API。构建 API 类似于构建网站。在后端使用相同的 Django/Python 堆栈,前端使用相同的 ReactJs(例如 API Dashboard、文档......)。
ListenNotes 的 API dashboard。
ListenNotes 的 API 文档。
对于 API,我们需要追踪用户在当前计费周期中使用的请求数量,并在周期结束时收取费用。不难想象,Redis 在这里被大量使用:)
DevOps
机器配置和代码部署
我使用 Ansible 进行机器配置。基本上,我写了一堆 yaml 文件来指定需要什么类型的服务器、什么配置文件和什么软件。我可以用所有正确的配置文件和软件启动一个服务器,并通过「一键安装」完成安装。这是 Ansible yaml 文件的目录结构:
我可以在命名方面做得更好。但现在已经足够好了。
我还使用 Ansible 将代码部署到生产环境中。基本上,我有一个在 macOS 上运行的打包脚本 deploy.sh:
- ./deploy.sh production HEAD web
deploy.sh 脚本有三个参数:
- 环境:生产或分段;
- listennotes repo 版本:HEAD 表示「仅部署最新版本」。如果指定了 git commit 的 SHA,那么它将部署特定版本的代码——当我需要从错误的部署撤回时,这特别有用;
- 服务器类型:web、worker、api 或所有服务器。我不必一次部署到所有服务器。有时我会对 Javascript 代码进行修改,然后我只需要部署到 Web,而无需涉及 api 或 worker。
部署过程主要由 Ansible yaml 文件制定,当然,它很简单:
- 在 Macbook Pro 上,如果要部署到 Web 服务器,则构建 Javascript 包并上传到 S3;
- 在目标服务器上,git 将 listennotes repo 克隆到一个以时间戳命名的文件夹,检查特定版本,然后安装新的 Python 依赖项(如果有的话);
- 在目标服务器上,将符号链接切换到上述时间戳命名文件夹,并通过 supervisorctl 重新启动服务器。
我不使用那些花哨的 CI 工具。实际的操作非常简易。
监控和警报
我使用 Datadog 进行监控和警报。我在一个简单的监控界面中有一些高级指标。这里所做的一切都是为了在弄乱生产服务器时增强信心。
适用于 Listen Notes 的 Datadog 监控界面(截至 2017 年 12 月)。
我将 Datadog 连接到 PagerDuty。如果出现问题,PagerDuty 将通过电话和短信向我发送提醒。
我还使用 Rollbar 来监控 Django 代码的运行状况,它会捕捉异常,并通过电子邮件和 Slack 通知我。
我经常使用 Slack。这是个一个人的公司,所以我不使用 Slack 与人交流。我使用 Slack 来监视有趣的应用级事件。除了将 Datadog 和 Rollbar 与 Slack 集成之外,我还在 Listen Notes 后端代码中使用 Slack 传入 webhooks,以便在用户注册或执行一些操作(例如添加或删除内容)时通知我。这是科技公司常用的做法。当你阅读亚马逊或 PayPal 早期发展的一些书籍时,你会发现两家公司都有类似的通知机制:每当用户注册时,都会有「叮」的声音通知办公室中的每个人。
自 2017 年初推出以来,Listen Notes 只出现过一次长时崩溃(大于 5 分钟)。在这些操作方面,我总是非常谨慎和务实。网络服务器显著过剩,是为了防止某个新闻事件或其他因素导致流量飙升。
开发
我在旧金山的 WeWork 共享空间工作。有些人可能想知道为什么不在家里或咖啡店工作。那是因为我非常重视效率,我愿意在效率上投资。我不相信堆砌时间就能开发出好软件。我很少一天工作超过 8 小时(对不起了,996 的朋友)。我想把每一分钟都花得有意义。因此,我需要一个精致且相对昂贵的私人办公室:) 与其为了省钱花更多时间,我选择花费更少的时间去赚钱:)
我在 WeWork 的办公室。
我用的电脑是 MacBook Pro。我在 Vagrant+ VirtualBox 中运行(几乎)相同的基础架构。我使用与上述相同的 Ansible yaml 文件集在 Vagrant 中配置开发环境。
我赞同 monorepo 理念。因此,我只有一个 listennotes repo,包含 DevOps 脚本、前端和后端代码。这个 listennotes repo 是作为 GitHub 私人 repo 托管。我在主分支上做所有的开发工作。我很少使用功能分支。
我使用 PyCharm 编写代码并运行 dev 服务器(Django runserver 和 webpack dev server)。我知道这很无聊。毕竟它不是 Visual Studio Code、Atom 或 IDE。但 PyCharm 对我来说效果很好。毕竟我很老派。
我的 PyCharm。
其他
我使用许多有用的工具和服务将 Listen Notes 打造为一款产品和一个公司:
- iTerm2 和 tmux 用于获取终端内容。
- Notion 用于 TODO 列表、维基、记笔记和设计文档......
- G Suite 用于 @listennotes.com 电子邮件帐户、日历和其他 Google 服务。
- MailChimp 发送每月电子邮件简报。
- Amazon SES 用于发送交易邮件和营销邮件。
- Gusto 用于支付自己和非 Upwork 承包人的工资。
- Upwork 用于寻找承包人。
- Google Ads Manager 管理直销广告并跟踪绩效。
- Carbon Ads 和 BuySellAds 用于备用广告。
- Cloudflare 用于 DNS 管理、CDN 和防火墙。
- Zapier 和 Trello 简化了播客工作流程。
- Medium 是公司博客的媒介。
- Godaddy 和 Namecheap 用于域名。
- Stripe 用于从用户处获取资金(主要用于 API)。
- Google speech-to-text API 用于语音转换。
- Kaiser Permanente 用于健康保险。
- Stripe Atlas 用于合并 Listen Notes Inc。
- Clerky 为融资(SAFE)和雇佣非 Upwork 承包人生成法律文件。
- Quickbooks 用于记账。
- 1password 用于管理大量服务的登录凭证。
- Brex 用于信用卡,还可以获得额外$ 5,000 AWS 积分,可以在 WeWork 或 Stripe Atlas 的 AWS 积分上应用。
- Bonvoy Business Amex 卡——可以获得豪华酒店和航班的 Marriott Bonvoy 积分。这是旅行的最佳信用卡积分:)
- Capital One Spark 用于支票账户。
胆大心细,马上行动
如你所见,我们生活在一个创业的黄金时代。有许多现成的工具和服务可以节省我们的时间和金钱,提高我们的生产力。使用简单而枯燥的技术,以一己之力(或一个团队)创造对世界有用的东西比以往任何时候都更有可能。
随着时间的推移,公司会变得越来越小。你无需雇佣大量的全职员工。你可以通过购买服务或外包来完成工作。
大多数时候,创造和传播的最大障碍是想太多。这怎么办?那怎么办?孩子,你把自己看太重了。每个人都忙于自己的生活。没有人关心你和你创建的东西,直到你证明你值得别人关注。即使你搞砸了最初的产品,也很少有人会注意到。「宏观思维,细节到位,迅速反应!」只要你真正解决了问题,就算使用无聊的技术完成简单(甚至简陋)的任务也完全没问题。