大家好! 作为课程作业的一部分,我正在研究这样一个国内云平台的可能性:
你想收到什么?
使用 Web 服务器启动虚拟机后,您可以访问其主机并获得漂亮的 UI、指定数据库作为进一步工作的源、创建仪表板和图表。
基本版本有一个显着的缺点——它根本不具备容错能力。 也就是说,应用程序的整体性能取决于一台虚拟机的生存能力。 如果她拒绝或者10个人同时打开UI,就会出现问题。
这些问题的解决方法很简单:您只需部署许多具有 Web 服务器的相同虚拟机,并将它们放置在 L3 平衡器下。 但这里并不是所有事情都那么明确。 Grafana 将用户设置(数据库、仪表板、图形等的路径)直接存储在其虚拟机的磁盘上。 因此,如果您更改 UI 中的某些设置,那么这些更改将仅显示在平衡器发送给我们的虚拟机上。 这将导致我们的应用程序设置不一致、启动和使用出现问题。
此时另一个数据库将派上用场,例如 MySQL 或其等效数据库。 我们告诉 Grafana 她应该将用户设置存储在这个“备用”数据库中。 之后,在每台计算机上指定一次该数据库的路径就足够了,并在任何虚拟机上编辑所有其他用户设置,它们将在其余虚拟机上增长。
这是最终应用程序基础架构的示意图:
学会用手举起
MySQL 和 ClickHouse
在通过单击按钮部署此类应用程序之前,有必要了解如何使用手柄提起其每个组件并将它们相互集成。
Yandex.Cloud 将在这方面为我们提供帮助,它提供 L3 平衡器、ClickHouse 和 MySQL 作为托管服务。 用户只需要指定参数并等待平台将一切置于工作状态即可。
我为自己注册、创建了一个云和一个计费帐户。 之后,我进入云端,用最少的设置搭建了 MySQL 和 ClickHouse 集群。 等到他们变得活跃。
您还需要记住在每个集群中创建一个数据库并通过登录名和密码配置对其的访问。 我不会在这里详细介绍 - 界面中的一切都非常明显。
不明显的细节是这些数据库有许多提供容错能力的主机。 然而,Grafana 只需要为其使用的每个数据库配备一台主机。 长读 c-<cluster_id>.rw.mdb.yandexcloud.net
映射到具有相应 ID 的当前活动集群主控主机。 这就是我们将给予 Grafana 的。
网络服务器
现在由网络服务器决定。 让我们用Linux搭建一个普通的虚拟机,并在上面亲手设置Grafana。
让我们通过 ssh 连接并安装必要的软件包。
sudo apt-get install -y apt-transport-https software-properties-common wget
wget -q -O - https://packages.grafana.com/gpg.key | sudo apt-key add -
sudo add-apt-repository "deb https://packages.grafana.com/enterprise/deb stable main"
sudo apt-get update
sudo apt-get install -y grafana-enterprise
之后,我们将在 systemctl 下启动 Grafana 并安装与 ClickHouse 配合使用的插件(是的,基本包中没有提供)。
sudo systemctl start grafana-server
sudo systemctl enable grafana-server
sudo grafana-cli plugins install vertamedia-clickhouse-datasource
之后的一切都只需一个简单的命令
sudo service grafana-server start
我们将启动网络服务器。 现在可以在浏览器中驱动虚拟机的外部IP地址,指定端口3000并看到漂亮的UI grafana。
但不要着急,在设置 Grafana 之前,您一定不要忘记指定 MySQL 的路径,以便其存储设置。
Grafana Web 服务器的整个配置都在该文件中 /etc/grafana/grafana.ini
。 所需的行如下所示:
;url =
我们将主机暴露给 MySQL 集群。 同一个文件包含上图中访问Grafana的登录名和密码,默认情况下两者是相等的 admin
.
您可以使用 sed 命令:
sudo sed -i "s#.*;url =.*#url = mysql://${MYSQL_USERNAME}:${MYSQL_PASSWORD}@${MYSQL_CLUSTER_URI}#" /etc/grafana/grafana.ini
sudo sed -i "s#.*;admin_user =.*#admin_user = ${GRAFANA_USERNAME}#" /etc/grafana/grafana.ini
sudo sed -i "s#.*;admin_password =.*#admin_password = ${GRAFANA_PASSWORD}#" /etc/grafana/grafana.ini
是时候重新启动网络服务器了!
sudo service grafana-server restart
现在在 Grafana UI 中,我们将指定 ClickHouse 作为数据源。
我设法通过以下设置实现了工作配置:
我已经给出了 URL https://c-<cluster_id>.rw.mdb.yandexcloud.net:8443
全部! 我们有一台正在运行的虚拟机,其 Web 服务器连接到 CH 和 MySQL。 您已经可以将数据集上传到 ClickHouse 并构建仪表板。 然而,我们还没有达到我们的目标,也没有部署成熟的基础设施。
包装机
Yandex.Cloud 允许您创建现有虚拟机的磁盘映像,并基于它,您可以创建任意数量的相同计算机。 这正是我们将要使用的。 为了方便地组装图像,请使用工具
我们的 json 文件将包含两个块:构建器和配置器。 第一个块描述了作为实体的图像本身的参数,第二个块描述了用必要的内容填充它的指令。
建筑商
{
"builders": [
{
"type": "yandex",
"endpoint": "{{user `endpoint`}}",
"folder_id": "<folder_id>",
"subnet_id": "{{user `subnet_id`}}",
"zone": "{{user `zone`}}",
"labels": {},
"use_ipv4_nat": true,
"use_internal_ip": false,
"service_account_key_file": "<service_account_key_file>",
"image_name": "grafana-{{timestamp}}",
"image_family": "grafana",
"image_labels": {},
"image_description": "GRAFANA",
"source_image_family": "ubuntu-1804-lts",
"disk_size_gb": 3,
"disk_type": "network-hdd",
"ssh_username": "ubuntu"
}
],
...
}
在此模板中,您需要设置云中要在其中创建图像的部分的标识符,以及包含先前在此部分中创建的服务帐户的密钥的文件的路径。 您可以在相应部分中阅读有关以文件形式创建服务帐户和密钥的更多信息。
此配置表示磁盘映像将基于平台构建 ubuntu-1804-lts
,放置在图像系列中适当的用户部分 GRAFANA
以...之名 grafana-{{timestamp}}
.
供给者
现在是配置中更有趣的部分。 它将描述将虚拟机的状态冻结到磁盘映像之前需要在虚拟机上执行的操作序列。
{
...,
"provisioners": [
{
"type": "shell",
"pause_before": "5s",
"scripts": [
"prepare-ctg.sh"
]
},
{
"type": "file",
"source": "setup.sh",
"destination": "/opt/grafana/setup.sh"
},
{
"type": "shell",
"execute_command": "sudo {{ .Vars }} bash '{{ .Path }}'",
"pause_before": "5s",
"scripts": [
"install-packages.sh",
"grafana-setup.sh",
"run-setup-at-reboot.sh"
]
}
]
}
在这里,所有动作分为3个阶段。 在第一阶段,执行一个简单的脚本来创建一个辅助目录。
准备-ctg.sh:
#!/bin/bash
sudo mkdir -p /opt/grafana
sudo chown -R ubuntu:ubuntu /opt/grafana
在下一阶段,我们在此目录中放置一个脚本,该脚本需要在启动虚拟机后立即运行。 该脚本会将要设置的用户变量放入 Grafana 的配置中并重新启动 Web 服务器。
安装程序.sh:
#!/bin/bash
CLUSTER_ID="<cluster_id>"
USERNAME="<username>"
PASSWORD="<password>"
sudo sed -i "s#.*;url =.*#url = mysql://${USERNAME}:${PASSWORD}@c-${CLUSTER_ID}.rw.mdb.yandexcloud.net#" /etc/grafana/grafana.ini
sudo sed -i "s#.*;admin_user =.*#admin_user = ${USERNAME}#" /etc/grafana/grafana.ini
sudo sed -i "s#.*;admin_password =.*#admin_password = ${PASSWORD}#" /etc/grafana/grafana.ini
sudo service grafana-server restart
之后,还有 3 件事要做:
1)安装包
2)systemctl下启动Grafana并安装ClickHouse插件
3)将setup.sh脚本放入队列中,在开启虚拟机后立即运行。
安装包.sh:
#!/bin/bash
sudo systemd-run --property='After=apt-daily.service apt-daily-upgrade.service' --wait /bin/true
sudo apt-get install -y apt-transport-https
sudo apt-get install -y software-properties-common wget
wget -q -O - https://packages.grafana.com/gpg.key | sudo apt-key add -
sudo add-apt-repository "deb https://packages.grafana.com/enterprise/deb stable main"
sudo apt-get update
sudo apt-get install -y grafana-enterprise
grafana-setup.sh:
#!/bin/bash
sudo systemctl start grafana-server
sudo systemctl enable grafana-server
sudo grafana-cli plugins install vertamedia-clickhouse-datasource
运行-setup-at-reboot.sh:
#!/bin/bash
chmod +x /opt/grafana/setup.sh
cat > /etc/cron.d/first-boot <<EOF
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games
@reboot root /bin/bash /opt/grafana/setup.sh > /var/log/yc-setup.log 2>&1
EOF
chmod +x /etc/cron.d/first-boot;
现在剩下的就是运行 Packer 并获取放置在指定分区中的输出映像。 创建虚拟机时,可以选择它作为启动盘,启动后就得到一个现成的Grafana Web服务器。
实例组和平衡器
一旦有了允许您创建许多相同的 Grafana Web 服务器的磁盘映像,我们就可以创建一个实例组。 在 Yandex.Cloud 平台上,该术语指具有相同特征的虚拟机的联合。 创建实例组时,会配置该组中所有机器的原型,然后配置该组本身的特征(例如,活动机器的最小和最大数量)。 如果当前数量不满足此标准,则实例组本身将删除不必要的机器或在镜像和相似中创建新机器。
作为我们任务的一部分,我们将创建一个 Web 服务器实例组,该实例组将从之前创建的磁盘映像中生成。
最后一个实例组设置确实值得注意。 与负载均衡器集成的目标组将帮助您通过按几个按钮在此组的虚拟机之上设置 L3 均衡器。
在设置平衡器时,我实现了两个要点:
- 我这样做是为了让平衡器接受端口 80 上的用户流量,并将其重定向到虚拟机的端口 3000,即 Grafana 所在的位置。
- 通过在端口 3000 上 ping 机器来设置机器运行状况检查。
小总计
最后,我们能够手动部署所需的应用程序基础设施,现在我们拥有了高度弹性的 Grafana 服务。 只需知道平衡器的 IP 地址作为应用程序的入口点和 ClickHouse 集群的主机即可将数据集加载到其中。
看来是胜利了? 是的,胜利。 但有些事情仍然困扰着我。 上面的整个过程需要大量的手动操作,并且根本无法扩展,如果可能的话我想将其自动化。 这将是下一节的重点。
与 Terraform 集成
我们将再次使用 HashiCorp 的一个工具,称为
所有使用 Terraform 的工作都归结为编写配置文件(*.tf
)以及基于它的基础设施的创建。
变量
在文件的开头,我们将取出决定未来基础设施部署地点和方式的变量。
variable "oauth_token" {
type = string
default = "<oauth-token>"
}
variable "cloud_id" {
type = string
default = "<cloud-id>"
}
variable "folder_id" {
type = string
default = "<folder_id>"
}
variable "service_account_id" {
type = string
default = "<service_account_id>"
}
variable "image_id" {
type = string
default = "<image_id>"
}
variable "username" {
type = string
default = "<username>"
}
variable "password" {
type = string
default = "<password>"
}
variable "dbname" {
type = string
default = "<dbname>"
}
variable "public_key_path" {
type = string
default = "<path to ssh public key>"
}
部署应用程序的整个过程将归结为构建磁盘映像并设置这些变量。 让我解释一下他们的职责:
oauth_token — 用于访问云的令牌。 可以通过以下方式获得
云ID - 我们将在其中部署应用程序的云的标识符
文件夹 ID — 我们将部署应用程序的部分的标识符
服务帐户 ID — 云相应部分中服务帐户的标识符。
图片编号 - 使用Packer获得的磁盘映像的标识符
用户名 и 密码 - 用于访问数据库和 Grafana Web 服务器的用户名和密码
DBNAME - CH 和 MySQL 集群内的数据库名称
公钥路径 - 包含您的公共 ssh 密钥的文件的路径,可用于以该名称进行连接 ubuntu
到具有 Web 服务器的虚拟机
提供商设置
现在您需要配置 Terraform 提供程序 - 在我们的示例中为 Yandex:
provider "yandex" {
token = var.oauth_token
cloud_id = var.cloud_id
folder_id = var.folder_id
zone = "ru-central1-a"
}
您可以看到这里我们使用了上面定义的变量。
网络和集群
现在让我们创建一个网络,基础设施的各个元素将在其中进行通信,三个子网(每个区域一个)并建立 CH 和 MySQL 集群。
resource "yandex_vpc_network" "grafana_network" {}
resource "yandex_vpc_subnet" "subnet_a" {
zone = "ru-central1-a"
network_id = yandex_vpc_network.grafana_network.id
v4_cidr_blocks = ["10.1.0.0/24"]
}
resource "yandex_vpc_subnet" "subnet_b" {
zone = "ru-central1-b"
network_id = yandex_vpc_network.grafana_network.id
v4_cidr_blocks = ["10.2.0.0/24"]
}
resource "yandex_vpc_subnet" "subnet_c" {
zone = "ru-central1-c"
network_id = yandex_vpc_network.grafana_network.id
v4_cidr_blocks = ["10.3.0.0/24"]
}
resource "yandex_mdb_clickhouse_cluster" "ch_cluster" {
name = "grafana-clickhouse"
environment = "PRODUCTION"
network_id = yandex_vpc_network.grafana_network.id
clickhouse {
resources {
resource_preset_id = "s2.micro"
disk_type_id = "network-ssd"
disk_size = 16
}
}
zookeeper {
resources {
resource_preset_id = "s2.micro"
disk_type_id = "network-ssd"
disk_size = 10
}
}
database {
name = var.dbname
}
user {
name = var.username
password = var.password
permission {
database_name = var.dbname
}
}
host {
type = "CLICKHOUSE"
zone = "ru-central1-a"
subnet_id = yandex_vpc_subnet.subnet_a.id
}
host {
type = "CLICKHOUSE"
zone = "ru-central1-b"
subnet_id = yandex_vpc_subnet.subnet_b.id
}
host {
type = "CLICKHOUSE"
zone = "ru-central1-c"
subnet_id = yandex_vpc_subnet.subnet_c.id
}
host {
type = "ZOOKEEPER"
zone = "ru-central1-a"
subnet_id = yandex_vpc_subnet.subnet_a.id
}
host {
type = "ZOOKEEPER"
zone = "ru-central1-b"
subnet_id = yandex_vpc_subnet.subnet_b.id
}
host {
type = "ZOOKEEPER"
zone = "ru-central1-c"
subnet_id = yandex_vpc_subnet.subnet_c.id
}
}
resource "yandex_mdb_mysql_cluster" "mysql_cluster" {
name = "grafana_mysql"
environment = "PRODUCTION"
network_id = yandex_vpc_network.grafana_network.id
version = "8.0"
resources {
resource_preset_id = "s2.micro"
disk_type_id = "network-ssd"
disk_size = 16
}
database {
name = var.dbname
}
user {
name = var.username
password = var.password
permission {
database_name = var.dbname
roles = ["ALL"]
}
}
host {
zone = "ru-central1-a"
subnet_id = yandex_vpc_subnet.subnet_a.id
}
host {
zone = "ru-central1-b"
subnet_id = yandex_vpc_subnet.subnet_b.id
}
host {
zone = "ru-central1-c"
subnet_id = yandex_vpc_subnet.subnet_c.id
}
}
正如您所看到的,这两个集群均通过放置在三个可用区中而构建为具有相当的容错能力。
网络服务器
看起来你可以以同样的精神继续下去,但我遇到了困难。 在此之前,我首先启动了 MySQL 集群,只有在知道其 ID 后,我才收集了具有所需配置的磁盘映像,其中我指定了集群的主机。 但现在我们在 Terraform 启动之前(包括在构建映像时)不知道集群 ID。 所以我不得不采取以下措施
使用 Amazon 的元数据服务,我们将一些参数传递给虚拟机,虚拟机将接受并处理这些参数。 我们需要机器在启动后访问集群的 MySQL 主机的元数据以及用户在 Terraform 文件中指定的用户名密码。 稍微改变一下文件的内容 setup.sh
,它在虚拟机打开时运行。
安装程序.sh:
#!/bin/bash
CLUSTER_URI="$(curl -H 'Metadata-Flavor:Google' http://169.254.169.254/computeMetadata/v1/instance/attributes/mysql_cluster_uri)"
USERNAME="$(curl -H 'Metadata-Flavor:Google' http://169.254.169.254/computeMetadata/v1/instance/attributes/username)"
PASSWORD="$(curl -H 'Metadata-Flavor:Google' http://169.254.169.254/computeMetadata/v1/instance/attributes/password)"
sudo sed -i "s#.*;url =.*#url = mysql://${USERNAME}:${PASSWORD}@${CLUSTER_URI}#" /etc/grafana/grafana.ini
sudo sed -i "s#.*;admin_user =.*#admin_user = ${USERNAME}#" /etc/grafana/grafana.ini
sudo sed -i "s#.*;admin_password =.*#admin_password = ${PASSWORD}#" /etc/grafana/grafana.ini
sudo service grafana-server restart
实例组和均衡器
重建新的磁盘映像后,我们终于可以为 Terraform 添加文件了。
我们表明我们要使用现有的磁盘映像:
data "yandex_compute_image" "grafana_image" {
image_id = var.image_id
}
现在让我们创建一个实例组:
resource "yandex_compute_instance_group" "grafana_group" {
name = "grafana-group"
folder_id = var.folder_id
service_account_id = var.service_account_id
instance_template {
platform_id = "standard-v1"
resources {
memory = 1
cores = 1
}
boot_disk {
mode = "READ_WRITE"
initialize_params {
image_id = data.yandex_compute_image.grafana_image.id
size = 4
}
}
network_interface {
network_id = yandex_vpc_network.grafana_network.id
subnet_ids = [yandex_vpc_subnet.subnet_a.id, yandex_vpc_subnet.subnet_b.id, yandex_vpc_subnet.subnet_c.id]
nat = "true"
}
metadata = {
mysql_cluster_uri = "c-${yandex_mdb_mysql_cluster.mysql_cluster.id}.rw.mdb.yandexcloud.net:3306/${var.dbname}"
username = var.username
password = var.password
ssh-keys = "ubuntu:${file("${var.public_key_path}")}"
}
network_settings {
type = "STANDARD"
}
}
scale_policy {
fixed_scale {
size = 6
}
}
allocation_policy {
zones = ["ru-central1-a", "ru-central1-b", "ru-central1-c"]
}
deploy_policy {
max_unavailable = 2
max_creating = 2
max_expansion = 2
max_deleting = 2
}
load_balancer {
target_group_name = "grafana-target-group"
}
}
值得关注的是我们如何传递到元数据 cluster_uri
, username
и password
。 他们的虚拟机将在启动时获取它并将其放入 Grafana 配置中。
是关于平衡器的。
resource "yandex_lb_network_load_balancer" "grafana_balancer" {
name = "grafana-balancer"
listener {
name = "grafana-listener"
port = 80
target_port = 3000
external_address_spec {
ip_version = "ipv4"
}
}
attached_target_group {
target_group_id = yandex_compute_instance_group.grafana_group.load_balancer.0.target_group_id
healthcheck {
name = "healthcheck"
tcp_options {
port = 3000
}
}
}
}
少许糖
所剩无几了。 基础设施部署完成后,您必须转到 Grafana UI 并手动添加 CH 集群(您仍需要获取其 ID)作为数据源。 但 Terraform 知道集群 ID。 让我们让他完成工作吧。
让我们添加一个新的提供商 - Grafana,并将平衡器 IP 作为主机。 Terraform 在平衡器确定的机器上所做的所有更改都将在 MySQL 中增长,因此在所有其他机器上也会增长。
provider "grafana" {
url = "http://${[for s in yandex_lb_network_load_balancer.grafana_balancer.listener: s.external_address_spec.0.address].0}"
auth = "${var.username}:${var.password}"
}
resource "grafana_data_source" "ch_data_source" {
type = "vertamedia-clickhouse-datasource"
name = "grafana"
url = "https://c-${yandex_mdb_clickhouse_cluster.ch_cluster.id}.rw.mdb.yandexcloud.net:8443"
basic_auth_enabled = "true"
basic_auth_username = var.username
basic_auth_password = var.password
is_default = "true"
access_mode = "proxy"
}
我们来梳理一下
显示平衡器 IP 和 ClickHouse 集群主机
output "grafana_balancer_ip_address" {
value = [for s in yandex_lb_network_load_balancer.grafana_balancer.listener: s.external_address_spec.0.address].0
}
output "clickhouse_cluster_host" {
value = "https://c-${yandex_mdb_clickhouse_cluster.ch_cluster.id}.rw.mdb.yandexcloud.net:8443"
}
能跑
全部! 我们的配置文件已准备就绪,我们可以通过设置变量来告诉 Terraform 引发我们上面描述的所有内容。 整个过程花了我大约15分钟。
最后,你可以看到一条漂亮的消息:
Apply complete! Resources: 9 added, 0 changed, 0 destroyed.
Outputs:
clickhouse_cluster_host = https://c-c9q14ipa2ngadqsbp2iq.rw.mdb.yandexcloud.net:8443
grafana_balancer_ip_address = 130.193.50.25
在云中,提升的基础设施的元素将是可见的:
综合
现在,使用 Grafana 的示例,每个人都可以在 Yandex.Cloud 平台上部署具有庞大云架构的应用程序。 HashiCorp 的有用工具(例如 Packer 和 Terraform)可以帮助您完成此任务。 我希望有人觉得这篇文章有用 🙂
PS 下面我将附上存储库的链接,您可以在其中找到 Packer 和 Terraform 的现成配方,我在本文中引用了其中的片段。
来源: habr.com