Obi Madu 的博客
返回所有文章
InfrastructureDevOpsProjects

Self Hosting 项目 (从零开始的 DevOps 基础设施)

使用 Terraform、Ansible 和 Docker 从零开始构建自托管 DevOps 基础设施的全面指南。

Self Hosting 项目 (从零开始的 DevOps 基础设施)

关于本项目

背景故事

所以在最终将我的灵魂出卖给 Kubernetes 之前,我决定是时候开展一个相对全面的 IaaS 项目了。我想好好利用扎实的、基础的 网络 (Networking)、安全 (Security)、脚本 (Scripting),然后是 Terraform、Ansible、Docker、Traefik 和 Wireguard(以及它们代表的所有概念),来完成一个完整的项目。由于 Kubernetes 在我的“待学习”列表中占据首要位置已经有一段时间了,我想,好吧,为什么不在一个前置项目中部署所有我转向使用它所必需的工具呢?这感觉是个好主意!

小插曲是,我产生这个想法有一段时间了,直到我有理由迅速建立起能让我托管自己的 Vaultwarden 密码管理器的基础设施,因为当时我的 Bitwarden 订阅即将到期。线上的 Bitwarden 并不贵,但我当时没有 10 美元来续费。我拥有的是 DigitalOcean 上的一些云积分、一个域名、免费的 Cloudflare、这个项目的想法,以及还算过得去的 IT 基础设施技能。

你可能会感兴趣,在这个项目的实施过程中,我使用 Neovim (LazyVIM) 创建了每一个文件,这是我在 8 月底开始学习的。总体而言,我相信这让整个过程变得更加有趣和有价值。

所以对于这个项目,有 5 个至关重要的目标(和第 6 个):-

  1. 完全自动化 (COMPLETE AUTOMATION)(端到端设置好就可以去睡觉的那种)
  2. 完全可视化 (COMPLETE VISIBILITY)(环境里任何风吹草动都会被发现并记录)
  3. 完全可重现 (COMPLETE REPRODUCIBILITY)(所有重要数据全面备份。附带一套设置好就不用管的基于日期的恢复策略)
  4. 良好的安全性 (GOOD SECURITY)(仅可通过私有 VPN 和 SSH 密钥访问。防火墙完全锁定,安全的 Docker 到主机通信)
  5. 以最便宜 (CHEAPEST)自托管 (SELF-HOSTED)、**开源 (OPEN-SOURCE)**的方式完成(在符合我预算的单台 VPS 上)
  6. 从零开始 (SCRATCH)

在您继续阅读之前,我必须指出,该项目不适用于高可用性环境。尽管其环境具有高度可重复的性质,但在单台服务器上意味着在恢复操作期间会有一些操作上的(尽管不是工作负载上的)停机时间。也就是说,小型、新兴、预算有限、需要以闪电般速度启动基础设施的组织完全可以使用这种方案。

在这个项目中,我部署了人们通常需要用来驱动 k8s 环境的 5 个最常见的服务(以及一些额外的个人项目,包括一个我称之为 Jarvis 的 LLM 前端 😉)。

  1. 私有 Git 托管服务 (Gitea)
  2. CI/CD 工具 (Jenkins)
  3. 凭据管理工具 (Harshicorp Vault)
  4. 私有制品库 (JFrog Container Registry)
  5. 漏洞扫描工具 (SonarQube)

有了这些,你可以将私有代码托管在 Gitea 上,用 Jenkins 构建,用 SonarQube 和 Trivy 扫描(代码和镜像扫描),发布到自托管的 Artifactory,然后部署到 Kubernetes 或任何其他环境。在执行所有这些操作的同时,通过中央 Vault 实例确保你的凭据在 Pipeline 和 Kubernetes 环境中的安全。

为了实现第二个目标,我部署了一个 监控和可观测性栈 (Monitoring and Observability stack),提供对宿主服务器和所有容器环境的完整洞察。

  1. PrometheuscAdvisor 用于指标 (Metrics) 收集
  2. LokiPromtail 用于日志 (Log) 聚合
  3. Grafana 用于指标和日志的可视化

关于安全,由于此基础设施旨在成为组织 DevOps 运营的主干,因此是内部的,其环境完全与公共互联网隔离,并且只能通过 VPN 访问。该项目还配置了自定义的 SSH 端口,有效地保护环境免受大多数自动化的、随机的互联网攻击。此外,通过适当的 IPTables 规则实现了安全的 Docker 到主机通信,确保了容器和主机环境之间保持适当的隔离。

源代码在哪里?

我非常有空(非常渴望)帮助你的组织将此基础设施或类似的系统投入使用。我将其移植到任何环境(云或本地),并保证上面列出的核心功能都不会缺失或不完整。把活儿交给我吧 :cool:

我的下一步是什么?

现在,凭借赋予我的力量(由某人赋予),我正式宣布自己为 Kubestronaut。从现在开始,我的生活和呼吸都离不开 Kubernetes,哈哈。所以接下来,我将继续开展一些我和几个朋友正在酝酿的有趣的 k8s 项目。我们将从零开始(通过 DevSecOps)将一个相当复杂的微服务应用程序移植到 k8s 上。创建 k8s 集群,为它们配备 ArgoCD,并在 11 月疯狂地玩转 GitOps。

如果你喜欢这个项目,不要忘记点赞和关注,以获取更多类似的“内容”。也请在评论中告诉我你对这个项目的看法,以及你认为我怎样可以做得更好。

如果你想重复这个项目

  1. 警告:我没有编写关于如何做任何事情的说明。
  2. 你能找到的只有关于做什么的说明。这些步骤是我在和朋友一起开发这个项目时记录下来的。理想情况下,这些说明应该能帮助你以理智的方式完成这件事。
  3. 所有的乐趣都在于弄清楚你不熟悉的项目部分的“如何”。

项目前提条件

  1. 一个域名
  2. 访问云托管服务
  3. 一个云密钥存储
  4. 一个(或多个)云存储桶 (bucket)
  5. 一些 IT 知识

关于项目结构和组织的重要说明

控制和目标云账户

本项目假设存在两个不同的云平台账户。一个用于目标资源,另一个用于对部署在目标账户上的资源进行最终的管理员访问。

这个项目最重要的部分包括:

  1. Terraform 状态 (State)(原因很明显)
  2. 服务器 SSH 密钥,Ansible 或其他人需要它来连接到服务器。
  3. 备份存档 (Backup Archives)
  4. 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) 基础设施

  1. 通过本地 state 在 DO/AWS/等上的单台服务器
  2. 防火墙/安全组
  3. 在 Cloudflare/Route53 等为未来的服务 (jenkins 等) 配置 DNS 记录

任务 2

目标:为基础设施设置远程状态 (remote state)。确保 Ansible 可以访问,并部署到服务器资源。

  1. 为 Terraform 配置远程状态
  2. 配置 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 基础知识。

  1. 创建一个 docker-compose 文件,该文件使用 Traefik 反向代理容器将临时的 Jenkins 实例部署到 url jenkins.your-domain.com。仅部署到 http。暂时不需要通过 Docker 卷进行任何持久化。
  2. 通过 Ansible 角色 (role) 部署 docker-compose 文件。必须通过角色部署。记得现在去掉之前虚拟的部署,它不再需要了。

任务 4

目标:实施基本的系统安全。

  1. 配置我们的服务器使用不同的 SSH 端口。
  2. 禁用所有用户的 SSH 密码登录
  3. 禁用 SSH Root 登录
  4. 修改我们的防火墙规则以关闭端口 22 并开放新的 SSH 端口。
  5. 配置 Ansible 以通过新的 ssh 端口连接。
  6. 为我们的 Docker compose 部署添加 https。

任务 5

目标:配置宿主服务器监控。升级我们的容器工作负载环境。

  1. 通过 Ansible 在宿主服务器上安装 Prometheus node exporter。
  2. 在我们的容器工作负载中安装 Prometheus 和 Grafana。
  3. 创建一个带有 bridge 驱动的新 Docker 网络,分配任何私有 /24 地址作为其子网。将所有容器工作负载分配到此网络。
    (如果你愿意,可以为监控栈和服务栈创建不同的 compose 文件。实际上建议这样做,因为所有这些服务都在一个 compose 文件中到最后看起来可能会有点乱)。
  4. 为每个 compose 服务创建适当的 Docker 卷,并将这些卷分配给服务。
  5. 通过 Ansible 将我们的宿主机作为指标目标 (target) 添加到 Prometheus 服务中。
  6. 配置 Grafana 以获取我们的 Prometheus 指标。
  7. 添加一个 Grafana 仪表板来监控我们的宿主服务器。目前,这只是为了确保设置工作正常。这将是临时的。

任务 6

目标:实施备份。

  1. 编写一个脚本来备份所有 docker 卷并将它们上传到远程存储桶。
    1.1 脚本应将每个卷挂载到一个临时容器中
    1.2 将其内容进行 Tar 和 Gzip 压缩。保持所有文件和目录的所有权信息不变。
    1.3 将存档保存在 'backups' 目录中。使用 'volume-name-timestamp.tar.gz' 文件命名约定。
    1.4 当所有卷都归档后。将 'backups' 文件夹中的所有内容上传到您指定用于备份的云存储桶。
  2. 使用 cron-job 使此备份脚本每天运行两次,通过 Ansible 在你选择的任何两个时间运行。

任务 7

目标:创建备份保留策略。

  1. 编写一个脚本来删除所有超过指定天数的备份。天数应该是调用时传递给脚本的参数。
    1.1 例如,如果使用参数 '5' 调用脚本,它应该删除所有不在过去 5 天时间范围内的备份。它应该只保留 5 天内的最近备份。
    可选地,你可以选择始终保留每个卷的最新备份。
  2. 通过 Ansible,创建一个 cron-job 每天运行一次此 'cleanup'(清理)脚本。保留 7 天的近期备份。

任务 8

目标:设置日志记录基础设施。

  1. 修改你的备份和清理脚本以将相关操作记录到 stdout。
  2. 修改你的备份和清理 cron-jobs,将所有输出和错误以及相关标签通过管道 (pipe) 传递给 syslog。
  3. 通过 Docker compose 部署 Promtail 和 Grafana Loki。将 '/var/log' 目录以只读方式挂载到 Promtail 容器中。
  4. 使用 Ansible,编写一个 Promtail 配置文件以从 /var/log 目录抓取 (scrape) 日志,并将它们发送到 Loki 服务。
  5. 使用 Ansible 将 Loki docker 插件安装到宿主服务器中。通过适当编辑 /etc/docker/daemon.json 文件,配置 Docker 守护进程以使用 Loki 作为默认的日志驱动。记得在此步骤后重启 Docker 服务并重新创建所有容器。
  6. 将 Loki 作为数据源 (data-source) 添加到 Grafana。确认你可以通过 Grafana 'Explore' 选项卡可视化来自 /var/log 目录和在主机上运行的所有 docker 容器的日志。

任务 9

目标:完成服务部署。

  1. 通过 docker-compose 部署 Gitea
  2. 通过 docker-compose 部署 Hashicorp Vault
  3. 部署 JFrog Container Registry OSS docker-compose
  4. 通过 docker-compose 部署 SonarQube
  5. 部署 cAdvisor 以监控每个容器服务的宿主机资源使用情况
  6. 从 Traefik 服务暴露 Prometheus 指标
  7. 为 Traefik 和 cAdvisor 创建 Grafana 仪表板
  8. 部署你喜欢的任何其他服务!

任务 10

目标:实施备份恢复策略。

  1. 创建一个备份恢复脚本,该脚本仅在 'restore' 变量的值等于 'true' 时运行。该脚本应接受存储桶名称、要恢复的特定备份的日期,以及指示应恢复当天的第一个还是第二个备份的 '1' 或 '2' 值。

任务 11

目标:通过 WireGuard VPN 私有化对基础设施的访问。

  1. 通过 Ansible 将 Wireguard 安装到宿主服务器。
  2. 通过结合 Ansible 和 Terraform 步骤,在服务器上设置和配置 Wireguard 接口,为该接口生成适当的配置文件,使用配置文件配置该接口,然后生成适当的客户端配置文件供连接到该接口时使用。
  3. 通过 IP 白名单配置 Traefik,仅允许通过 Wireguard VPN 子网发起的连接的流量访问所有服务。

任务 12

目标:通过 Terraform 和 Ansible 部署和配置 Jenkins Agent VPS。

  1. 使用 Terraform,部署一个你选择的任何大小的额外 VPS 服务器,以充当你 Jenkins 实例的构建代理 (build agent)。
  2. 使用 Ansible 配置你的代理,安装 Jenkins 需要的任何工具,例如 JDK 包,以及用于容器镜像扫描的 Trivy。
  3. 使用 Ansible,在你的代理上安装 Wireguard 并将其设置为你私有 VPN 中的对等节点 (Peer)。
  4. (可选) 如果你希望能够从 Agent 使用 Traefik 中服务的 FQDN 访问它们,请使用 Ansible 在 Agent 上设置适当的 DNS 记录。

结论

瞧!如果你的技能带你走到了这一步 😉 那么你现在已经拥有了这个基础设施的有效副本。如果你喜欢这个项目,不要忘记点赞。也请在评论中告诉我你对这个项目的看法,以及你认为我怎样可以做得更好。