部署前端应用程序——有趣的方式
别废话了,直接说 TL;DR
问题
解开魔盒
结论
无耻的自我推销
在这篇文章中,我将向你展示如何使用 GitHub、Jenkins、Docker 和Digital Ocean部署我的前端应用程序。在我的学习过程中,我遇到了一些疑问,所以我决定把我学到的东西写在这篇文章里,作为我的笔记。
注意:我的朋友,作为拥有 15 年以上 DevOps 架构经验的专家和顾问大师,这篇文章并非为您而写;它面向的是那些刚刚开始探索 CI/CD 技术之美的读者。本文可能包含一些不好的做法,请谨慎对待。
别废话了,直接说 TL;DR
你跟我一样,四处看看,捡到子弹,然后就走人了。以下是 TL;DR 的总结:
- 启动 2 台安装了 Docker 的服务器:构建服务器和生产服务器
- 在构建服务器中安装 Jenkins(或任何其他 CI)
- 在你的 CI 中创建一个作业,在 Docker 镜像中克隆、安装和构建你的项目
- 在 GitHub 中设置 webhook,以便在每次推送时触发该作业
- 将 Docker 镜像推送到您的个人 Docker Hub
- 通过 SSH 连接到您的生产服务器
- 从 Docker Hub 拉取镜像并运行容器
- 利润
谢谢阅读。
希望我的文章能引起你的注意,让你现在想了解更多细节。在接下来的段落中,我将概述我的思路,并解释我的部署流程是如何运作的。我不会深入探讨技术细节,而是会提供一些有用的链接,供你进一步研究。
问题
简单:我在 Angular 中开发了一个TypeScript AST 交互式查看器,并且我想部署它。
可是……为什么不使用 Firebase、Google Engine、原生 FTP、GitHub 页面呢?你可能会问,就像“一键部署”一样。所有这些工具都很好用,但大多数要么太贵,要么感觉太神奇(没有什么比“gcloud app deploy”更好了),而且学习起来也不那么有趣!我想要理解、自动化并完全控制整个部署过程。我想要挑战,我想要学习,我想要从中获得乐趣(我的灵感真的来了)。
归根结底
我设立了自己的挑战,如下所示:
-
我想要推送代码并让它自动部署——也就是持续交付。
-
我想支付便宜的托管服务
让我们在黑盒图中绘制这些非功能性需求:
问题说明。从 GitHub 到 Live App。图标:由 Noun Project 的 ✦ Shmidt Sergey ✦ 设计的魔法
解开魔盒
我将本章分为三个部分。每个部分将揭示我的“魔法盒”的一小部分:
-
从 GitHub 到 Magic
-
开发魔法
-
从魔术到 Live App
1. 从 GitHub 到 Magic
推送代码,坐下来放松,刷新上线的应用,然后赚钱。这就是我想要的。
选择我的 VPS(虚拟专用服务器)
第一步是搭建一个服务器,它可以从 GitHub 获取我的代码,并执行一系列命令(npm i && npm run build)。我不太确定下一步该做什么,但我知道我必须从这里开始。
在了解了市面上最好的 VPS 提供商之后,我最终选择了Digital Ocean。很棒的教程,新手上手简单,DNS 管理也不错,价格也很合理——完全不费脑子。
无处不在的 CI 工具
注册后,我知道我需要某种工具来获取代码并自动化构建过程——CI (持续集成)工具。市面上有很多这样的工具,有些是专门为开源项目设计的,有些主要设计为自托管,有些是付费的,有些是免费的。我选择 Jenkins 主要是因为它的流水线概念,而且我对这个工具比较熟悉。
看了一些教程,我觉得启动并运行我的 Jenkins 实例相对容易。现在我需要告诉 Jenkins 每次我 git push 的时候都抓取我的代码。我发现网上有很多帖子教你如何设置一个从你的仓库指向 Jenkins Droplet IP 的 GitHub webhook。设置过程比我预想的要快(不过对于私有 GitHub 仓库来说就没那么简单了)。
让我们回顾一下
目前,我有一个运行 Linux 的 DO droplet,里面有一个 Jenkins 实例,每次 git push 都会从 GitHub 拉取代码。是不是很酷?让我们看看我们的“魔盒”是如何展现它的:
使用 Digital Ocean Droplet 和 Jenkins 的黑盒图
有用的链接:
2. 发挥魔法
在接下来的几段中,我将阐述我的策略核心,该策略利用了 Docker 和 DO Droplet。让我们开始吧。
Docker 化一切
此时,我有一个dist文件夹,里面有一个index.html 文件及其依赖项,一切准备就绪。我知道我需要启动一个 Web 服务器来通过 Web 提供这些文件。接下来,让我们在同一台服务器上安装nginx(或Apache)来提供这些文件。我还需要安装 Node 和一些全局 Node 包。很简单,我们开始吧……
“但是等等,卡洛斯,那个嗯……感觉不太对劲——你最终会在服务器上安装一大堆东西,管理依赖项以后会变得非常麻烦……只是说说而已”——内心的自我
确实,我需要一种方法来封装依赖项、dist组件,甚至 Web 服务器,以免弄乱我的服务器。那就用Docker吧。它可以让我构建一个镜像,将 Web 服务器运行在服务器的一个端口上,而服务器文件系统几乎不会察觉到我刚才的操作。是不是很酷?
我为我的应用程序设置了一个Dockerfile,并在 Jenkins 中编辑了作业,构建了一个 Docker 镜像,并运行了暴露 80 端口的容器。一切运行正常。我可以通过http://my.server.ip实时访问我的应用程序。
关于扩展和其他
我计划在同一台服务器上部署多个采用相同模式的应用程序。这样一来,将 Jenkins 和所有容器都运行在同一台服务器上就显得不合理了。我感觉我可以做得更好——我想把关注点分开。当然,我可以同时使用不同的用户,让 Jenkins 驻留在自己的用户中等等,但我内心深处真的想保留一台专门用于构建应用程序的机器。
基于此前提,我创建了另一个 Droplet(又名服务器),它内存较小(不做繁重工作,仅用于服务 Web 应用),并安装了 Docker,因为它应该能够运行 Docker 容器。然后,我会在这台服务器上运行我的所有应用。这样可以实现良好的扩展性,因为我可以轻松更改构建服务器上的内存分配,同时不影响我的应用。
让我们回顾一下
我们取得了相当不错的进展。我们已经确定使用 Docker 作为核心构建机制,并且为了便于维护,我们还决定在另一台服务器上部署一个独立的生产服务器。
使用 Docker 和 2 个 DO droplet 的部署工作流程
有用的链接
3. 从魔术到直播应用
我不确定如何集成我的服务器(构建 + 生产)。在寻求帮助和研究之后,我得出结论,对于我的特定用例,我可以实现以下工作流程:
-
在Build Server中构建 docker 镜像
-
将 Docker 镜像推送到我的Docker Hub
-
通过 SSH 登录我的生产服务器
-
从Docker Hub拉取镜像并运行容器
这很合理,因为我不需要使用 docker-machine 或 Kubernetes 之类的工具来编排我的服务器。它很好用,简单易用,而且对我来说看起来足够简洁。
安全注意事项:建议在生产服务器上禁用基于密码的身份验证,并在构建服务器上仅启用基于密钥的身份验证。也就是说,如果不从构建服务器登录,几乎不可能登录到生产服务器。
给我看一些代码
一篇技术文章如果不展示一些代码就不算是真正的技术文章。让我们看看在集成整个流程时,Jenkins 流水线在 CI 中是什么样子的:
#!/usr/bin/env groovy
node {
def app
stage("Clone") {
git 'https://github.com/caroso1222/ast-viewer.git'
}
stage("Build") {
app = docker.build("caroso1222/ts-ast-viewer")
}
stage("Push") {
docker.withRegistry("https://registry.hub.docker.com", "docker-hub-credentials") {
app.push("${env.BUILD_ID}")
app.push("latest")
}
}
stage("Deploy") {
sh "ssh root@my.server.ip \"docker stop ast_0 && \
docker rm ast_0 && \
docker pull caroso1222/ts-ast-viewer:latest && \
docker run -d --name=ast_0 -p 8080:80 caroso1222/ts-ast-viewer:latest\""
}
}
我在代码片段中标记了一些行,让我们快速浏览一下:
-
根据Dockerfile构建 docker 镜像。
-
将 Docker 镜像推送到我的Docker Hub帐户。
-
通过 SSH 进入生产服务器
-
从我的 Docker Hub 拉取镜像
-
从我们刚刚拉取的镜像中运行容器。我将容器运行在 8080 端口上,这是因为我设置了 Nginx,通过子域名proxy_pass将流量路由到同一服务器中的多个容器(例如,domainA.com 服务于容器 A,domainB.com 服务于容器 B)。我不会详细阐述这项技术,因为它超出了本文的讨论范围。
正如本文开头所述,此流水线在每个git push
master 分支上运行。下图展示了 Jenkins 每次部署的流程。顺便说一句,我很喜欢这张图片,因为它描绘了我在深入研究之前脑海中对这个挑战的设想。
Jenkins 流水线的阶段视图。每一行代表由 git push 触发的一次部署。
让我们总结一下
我们终于成功了。我们解开了黑匣子。所有秘密都已揭晓,我们也能够覆盖其每一个组件。现在,让我们来看看最终的部署工作流程是什么样的!
本文涵盖的完整部署策略图。Docker 镜像在构建服务器中构建,经过 Docker Hub 传输,最终到达生产服务器。
有用的链接
结论
市面上部署策略种类繁多,各有优缺点,并且每一种都依赖于不同的技术栈,各有各的特色。这正是它的魅力所在。在本文中,我仅介绍了其中一种策略,它让我能够完全控制使用两台服务器、一个持续集成 (CI) 和 Docker 的自定义部署流水线。
如果你对我们讨论的概念不太熟悉,这一切听起来可能有点吓人。别担心,我说。这篇文章很长,主要是因为它总结了我近三个月的学习历程,我每晚都要花一两个小时在这些服务器上磨练,阅读一些我不太熟悉的东西。
即使你是一位专注于浏览器领域的前端开发者,我仍然认为熟悉全栈开发非常有价值。这将使你在公司讨论 DevOps 问题时,能够进行扎实的沟通并形成自己的观点。与其说“嗯,那不是我的事,你们这些运维人员自己解决吧”,不如说“你们是专家,但这是我对此事的看法……”。
我希望你能得到一些启发,继续实施你的个人部署管道,并了解所有这些 DevOps 技术——未来会充满乐趣!
无耻的自我推销
我不是业余的 SoundCloud 说唱歌手,也不是在 Patreon 上寻找资金。我只是帮助开发者在Toptal等顶级远程平台找到工作。我撰写有关职业发展、面试技巧、CSS 的趣味文章,并时不时在 DEV 上发布一些讨论。
现在我正在写一本免费指南,里面有很多技巧和窍门,教你如何在远程技术面试中脱颖而出。如果你感兴趣,可以在这里注册候补名单,成为第一个获得它的人。
在 Twitter 上找到我@caroso1222,别忘了跟我打招呼!
文章来源:https://dev.to/caroso1222/deploying-frontend-applications-the-fun-way-1fpf