关于本项目
背景故事
所以在最终将我的灵魂出卖给 Kubernetes 之前,我决定是时候开展一个相对全面的 IaaS 项目了。我想好好利用扎实的、基础的 网络 (Networking)、安全 (Security)、脚本 (Scripting),然后是 Terraform、Ansible、Docker、Traefik 和 Wireguard(以及它们代表的所有概念),来完成一个完整的项目。由于 Kubernetes 在我的“待学习”列表中占据首要位置已经有一段时间了,我想,好吧,为什么不在一个前置项目中部署所有我转向使用它所必需的工具呢?这感觉是个好主意!
小插曲是,我产生这个想法有一段时间了,直到我有理由迅速建立起能让我托管自己的 Vaultwarden 密码管理器的基础设施,因为当时我的 Bitwarden 订阅即将到期。线上的 Bitwarden 并不贵,但我当时没有 10 美元来续费。我拥有的是 DigitalOcean 上的一些云积分、一个域名、免费的 Cloudflare、这个项目的想法,以及还算过得去的 IT 基础设施技能。
你可能会感兴趣,在这个项目的实施过程中,我使用 Neovim (LazyVIM) 创建了每一个文件,这是我在 8 月底开始学习的。总体而言,我相信这让整个过程变得更加有趣和有价值。
所以对于这个项目,有 5 个至关重要的目标(和第 6 个):-
- 完全自动化 (COMPLETE AUTOMATION)(端到端设置好就可以去睡觉的那种)
- 完全可视化 (COMPLETE VISIBILITY)(环境里任何风吹草动都会被发现并记录)
- 完全可重现 (COMPLETE REPRODUCIBILITY)(所有重要数据全面备份。附带一套设置好就不用管的基于日期的恢复策略)
- 良好的安全性 (GOOD SECURITY)(仅可通过私有 VPN 和 SSH 密钥访问。防火墙完全锁定,安全的 Docker 到主机通信)
- 以最便宜 (CHEAPEST)、自托管 (SELF-HOSTED)、**开源 (OPEN-SOURCE)**的方式完成(在符合我预算的单台 VPS 上)
- 从零开始 (SCRATCH)
在您继续阅读之前,我必须指出,该项目不适用于高可用性环境。尽管其环境具有高度可重复的性质,但在单台服务器上意味着在恢复操作期间会有一些操作上的(尽管不是工作负载上的)停机时间。也就是说,小型、新兴、预算有限、需要以闪电般速度启动基础设施的组织完全可以使用这种方案。
在这个项目中,我部署了人们通常需要用来驱动 k8s 环境的 5 个最常见的服务(以及一些额外的个人项目,包括一个我称之为 Jarvis 的 LLM 前端 😉)。
- 私有 Git 托管服务 (Gitea)
- CI/CD 工具 (Jenkins)
- 凭据管理工具 (Harshicorp Vault)
- 私有制品库 (JFrog Container Registry)
- 漏洞扫描工具 (SonarQube)
有了这些,你可以将私有代码托管在 Gitea 上,用 Jenkins 构建,用 SonarQube 和 Trivy 扫描(代码和镜像扫描),发布到自托管的 Artifactory,然后部署到 Kubernetes 或任何其他环境。在执行所有这些操作的同时,通过中央 Vault 实例确保你的凭据在 Pipeline 和 Kubernetes 环境中的安全。
为了实现第二个目标,我部署了一个 监控和可观测性栈 (Monitoring and Observability stack),提供对宿主服务器和所有容器环境的完整洞察。
- Prometheus 和 cAdvisor 用于指标 (Metrics) 收集
- Loki 和 Promtail 用于日志 (Log) 聚合
- Grafana 用于指标和日志的可视化
关于安全,由于此基础设施旨在成为组织 DevOps 运营的主干,因此是内部的,其环境完全与公共互联网隔离,并且只能通过 VPN 访问。该项目还配置了自定义的 SSH 端口,有效地保护环境免受大多数自动化的、随机的互联网攻击。此外,通过适当的 IPTables 规则实现了安全的 Docker 到主机通信,确保了容器和主机环境之间保持适当的隔离。
源代码在哪里?
我非常有空(非常渴望)帮助你的组织将此基础设施或类似的系统投入使用。我将其移植到任何环境(云或本地),并保证上面列出的核心功能都不会缺失或不完整。把活儿交给我吧 :cool:
我的下一步是什么?
现在,凭借赋予我的力量(由某人赋予),我正式宣布自己为 Kubestronaut。从现在开始,我的生活和呼吸都离不开 Kubernetes,哈哈。所以接下来,我将继续开展一些我和几个朋友正在酝酿的有趣的 k8s 项目。我们将从零开始(通过 DevSecOps)将一个相当复杂的微服务应用程序移植到 k8s 上。创建 k8s 集群,为它们配备 ArgoCD,并在 11 月疯狂地玩转 GitOps。
如果你喜欢这个项目,不要忘记点赞和关注,以获取更多类似的“内容”。也请在评论中告诉我你对这个项目的看法,以及你认为我怎样可以做得更好。
如果你想重复这个项目
- 警告:我没有编写关于如何做任何事情的说明。
- 你能找到的只有关于做什么的说明。这些步骤是我在和朋友一起开发这个项目时记录下来的。理想情况下,这些说明应该能帮助你以理智的方式完成这件事。
- 所有的乐趣都在于弄清楚你不熟悉的项目部分的“如何”。
项目前提条件
- 一个域名
- 访问云托管服务
- 一个云密钥存储
- 一个(或多个)云存储桶 (bucket)
- 一些 IT 知识
关于项目结构和组织的重要说明
控制和目标云账户
本项目假设存在两个不同的云平台账户。一个用于目标资源,另一个用于对部署在目标账户上的资源进行最终的管理员访问。
这个项目最重要的部分包括:
- Terraform 状态 (State)(原因很明显)
- 服务器 SSH 密钥,Ansible 或其他人需要它来连接到服务器。
- 备份存档 (Backup Archives)
- Hashicorp Vault 自动解封 (Auto-Unseal) 密钥
有了这四样东西,再加上代码实现,整个系统就可以从头开始完全重现。
因此,在这个项目期间,我假设会有一个个人(或一组个人)拥有对整个公司基础(运营)基础设施的超级管理员权限。这些人(理想情况下)是关键利益相关者,例如 CEO 或工程主管。他们将是掌握王国最终(隐喻的)钥匙的人。
在实践中,“控制”账户和“资源”账户可以采取各种形式和配置,这取决于你的具体设置和要求。这些账户可以代表跨云基础设施多个方面的不同实体。这里有几个可能的场景:
1. 多云 (Multi-cloud): 这些账户可能完全属于不同的云平台。例如,你的控制账户可能托管在 Azure 上,而你的资源部署在单独的云提供商(如 Digital Ocean)上。这允许你利用每个平台提供的独特功能和服务。
2. 相同云平台,不同账户: 这些账户可能是同一个云提供商中的独立账户。当你想要维护不同环境(如生产和开发)之间的隔离,或者组织内不同部门或项目之间的隔离时,这种设置很常见。
3. 相同云平台,不同组织单位: 在一些云平台中,如拥有资源组的 Azure 或拥有项目的 Google Cloud,这些账户可以代表同一个云账户中不同的逻辑分组。这允许你根据特定标准(如项目、环境或应用程序)组织和管理资源。
我采用了多云方法,我的资源部署在 Digital Ocean,而我的控制账户在 Azure 上。
关于部署其他(额外)服务
正如你可能已经注意到的,除了核心工具之外,我还部署了 VaultWarden 密码管理器和 AnythingLLM 应用程序。这些工具是我为自己部署的。你可以随意去掉它们并部署你选择的任何其他工具。
总体概览
本着总体概览的精神,下面是本项目三个核心组件中每一个的功能;
Terraform 用于部署;
- 服务器资源 (VPC, 防火墙/安全组)
- 位于 DO/AWS/等的服务器,带有 Userdata 脚本
- Cloudflare/等的 DNS 记录
- Ansible playbook
Ansible playbook 用于;
- 配置服务器 (主机名, 网络/安全规则)
- 安装实用工具 (s3cmd, prometheus-node-exporter)
- 安装 & 配置 Docker
- 安装 & 配置 WireGuard VPN
- 部署 Docker compose (服务和监控栈)
- Docker 卷备份的脚本和任务
- Docker 卷恢复的脚本
Docker Compose 用于部署;
- Traefik 网关 (HTTP, SSL, HTTP-HTTPS 重定向, IP 白名单)
- Gitea
- Jenkins
- Hashicorp Vault
- JFrog Container Registry
- SonarQube OSS
- Prometheus
- Loki & Promtail
- Grafana
- Vaultwarden
- AnythingLLM
任务分组
这些任务分组对于我完成这个项目起到了非常关键的作用。我在和朋友一起做项目的每一天都会画出它们。它们是我没有放弃的一个重要原因。在大多数(如果不是全部)情况下,它们代表了每日目标。按顺序完成它们,你将拥有一个功能齐全的基础设施。
任务 1
目标:通过 Terraform 引导 (Bootstrap) 基础设施
- 通过本地 state 在 DO/AWS/等上的单台服务器
- 防火墙/安全组
- 在 Cloudflare/Route53 等为未来的服务 (jenkins 等) 配置 DNS 记录
任务 2
目标:为基础设施设置远程状态 (remote state)。确保 Ansible 可以访问,并部署到服务器资源。
- 为 Terraform 配置远程状态
- 配置 Terraform 以部署虚拟的 (dummy) Ansible playbook
2.1 创建虚拟的 Ansible playbook
2.2 创建用于服务器访问的 ssh 密钥对
2.2 通过 cloud-init userdata 脚本在服务器上配置 Ansible 用户账户。Userdata 脚本创建一个 'ansible' 用户账户,并配置 known-host 密钥(使用在步骤 2.2 生成的公钥)。
2.3 使用我们的私钥,通过 Terraform local-provisioner 在服务器上运行这个虚拟的 playbook。
任务 3
目标:学习通过 Ansible 部署 Docker Compose。学习 Traefik 基础知识。
- 创建一个 docker-compose 文件,该文件使用 Traefik 反向代理容器将临时的 Jenkins 实例部署到 url
jenkins.your-domain.com。仅部署到 http。暂时不需要通过 Docker 卷进行任何持久化。 - 通过 Ansible 角色 (role) 部署 docker-compose 文件。必须通过角色部署。记得现在去掉之前虚拟的部署,它不再需要了。
任务 4
目标:实施基本的系统安全。
- 配置我们的服务器使用不同的 SSH 端口。
- 禁用所有用户的 SSH 密码登录
- 禁用 SSH Root 登录
- 修改我们的防火墙规则以关闭端口 22 并开放新的 SSH 端口。
- 配置 Ansible 以通过新的 ssh 端口连接。
- 为我们的 Docker compose 部署添加 https。
任务 5
目标:配置宿主服务器监控。升级我们的容器工作负载环境。
- 通过 Ansible 在宿主服务器上安装 Prometheus node exporter。
- 在我们的容器工作负载中安装 Prometheus 和 Grafana。
- 创建一个带有 bridge 驱动的新 Docker 网络,分配任何私有 /24 地址作为其子网。将所有容器工作负载分配到此网络。
(如果你愿意,可以为监控栈和服务栈创建不同的 compose 文件。实际上建议这样做,因为所有这些服务都在一个 compose 文件中到最后看起来可能会有点乱)。 - 为每个 compose 服务创建适当的 Docker 卷,并将这些卷分配给服务。
- 通过 Ansible 将我们的宿主机作为指标目标 (target) 添加到 Prometheus 服务中。
- 配置 Grafana 以获取我们的 Prometheus 指标。
- 添加一个 Grafana 仪表板来监控我们的宿主服务器。目前,这只是为了确保设置工作正常。这将是临时的。
任务 6
目标:实施备份。
- 编写一个脚本来备份所有 docker 卷并将它们上传到远程存储桶。
1.1 脚本应将每个卷挂载到一个临时容器中
1.2 将其内容进行 Tar 和 Gzip 压缩。保持所有文件和目录的所有权信息不变。
1.3 将存档保存在 'backups' 目录中。使用 'volume-name-timestamp.tar.gz' 文件命名约定。
1.4 当所有卷都归档后。将 'backups' 文件夹中的所有内容上传到您指定用于备份的云存储桶。 - 使用 cron-job 使此备份脚本每天运行两次,通过 Ansible 在你选择的任何两个时间运行。
任务 7
目标:创建备份保留策略。
- 编写一个脚本来删除所有超过指定天数的备份。天数应该是调用时传递给脚本的参数。
1.1 例如,如果使用参数 '5' 调用脚本,它应该删除所有不在过去 5 天时间范围内的备份。它应该只保留 5 天内的最近备份。
可选地,你可以选择始终保留每个卷的最新备份。 - 通过 Ansible,创建一个 cron-job 每天运行一次此 'cleanup'(清理)脚本。保留 7 天的近期备份。
任务 8
目标:设置日志记录基础设施。
- 修改你的备份和清理脚本以将相关操作记录到 stdout。
- 修改你的备份和清理 cron-jobs,将所有输出和错误以及相关标签通过管道 (pipe) 传递给 syslog。
- 通过 Docker compose 部署 Promtail 和 Grafana Loki。将 '/var/log' 目录以只读方式挂载到 Promtail 容器中。
- 使用 Ansible,编写一个 Promtail 配置文件以从 /var/log 目录抓取 (scrape) 日志,并将它们发送到 Loki 服务。
- 使用 Ansible 将 Loki docker 插件安装到宿主服务器中。通过适当编辑 /etc/docker/daemon.json 文件,配置 Docker 守护进程以使用 Loki 作为默认的日志驱动。记得在此步骤后重启 Docker 服务并重新创建所有容器。
- 将 Loki 作为数据源 (data-source) 添加到 Grafana。确认你可以通过 Grafana 'Explore' 选项卡可视化来自 /var/log 目录和在主机上运行的所有 docker 容器的日志。
任务 9
目标:完成服务部署。
- 通过 docker-compose 部署 Gitea
- 通过 docker-compose 部署 Hashicorp Vault
- 部署 JFrog Container Registry OSS docker-compose
- 通过 docker-compose 部署 SonarQube
- 部署 cAdvisor 以监控每个容器服务的宿主机资源使用情况
- 从 Traefik 服务暴露 Prometheus 指标
- 为 Traefik 和 cAdvisor 创建 Grafana 仪表板
- 部署你喜欢的任何其他服务!
任务 10
目标:实施备份恢复策略。
- 创建一个备份恢复脚本,该脚本仅在 'restore' 变量的值等于 'true' 时运行。该脚本应接受存储桶名称、要恢复的特定备份的日期,以及指示应恢复当天的第一个还是第二个备份的 '1' 或 '2' 值。
任务 11
目标:通过 WireGuard VPN 私有化对基础设施的访问。
- 通过 Ansible 将 Wireguard 安装到宿主服务器。
- 通过结合 Ansible 和 Terraform 步骤,在服务器上设置和配置 Wireguard 接口,为该接口生成适当的配置文件,使用配置文件配置该接口,然后生成适当的客户端配置文件供连接到该接口时使用。
- 通过 IP 白名单配置 Traefik,仅允许通过 Wireguard VPN 子网发起的连接的流量访问所有服务。
任务 12
目标:通过 Terraform 和 Ansible 部署和配置 Jenkins Agent VPS。
- 使用 Terraform,部署一个你选择的任何大小的额外 VPS 服务器,以充当你 Jenkins 实例的构建代理 (build agent)。
- 使用 Ansible 配置你的代理,安装 Jenkins 需要的任何工具,例如 JDK 包,以及用于容器镜像扫描的 Trivy。
- 使用 Ansible,在你的代理上安装 Wireguard 并将其设置为你私有 VPN 中的对等节点 (Peer)。
- (可选) 如果你希望能够从 Agent 使用 Traefik 中服务的 FQDN 访问它们,请使用 Ansible 在 Agent 上设置适当的 DNS 记录。
结论
瞧!如果你的技能带你走到了这一步 😉 那么你现在已经拥有了这个基础设施的有效副本。如果你喜欢这个项目,不要忘记点赞。也请在评论中告诉我你对这个项目的看法,以及你认为我怎样可以做得更好。
继续阅读
通过 Github Pull Requests、Actions、Bot、Environments 和远程后端(Remote Backend)协作实现 Terraform 核心工作流。
使用 GitHub Actions、pull requests 和远程后端为生产团队实现协作式 Terraform 工作流。
通过 Terraform 在 Digital Ocean 上部署服务器 (指南)
使用 Terraform 和基本的“基础设施即代码”(IaC) 实践来配置 DigitalOcean droplet 的分步指南。
通过 Ansible 将 Jenkins 自动部署到远程服务器(带有免费 SSL)(操作指南)
了解如何使用 Ansible 自动部署自托管的 Jenkins 服务器,并配有免费的自动续订 SSL 证书。
