如何用CockroachDB、Django和K8s建立一个多区域的Python应用程序

95 阅读6分钟

有一天,我接到一个请求,问我是否有Django的经验,能否让它与CockroachDB的多地区功能一起工作。我以前从未听说过Django,但我从不拒绝挑战,所以我接受了。

首先,让我们解释一下什么是Django以及它的用途。

什么是Django,Django的用例有哪些?

Django是一个网络框架,它鼓励Python应用程序的快速开发和简洁、实用的设计。它通过提供一个成熟的模式来设计可扩展的网络应用程序,其中有许多功能是开箱即用的。这有助于防止开发人员花费不必要的时间来 "重新发明轮子",这样他们就可以花更多的时间来编写所需的应用程序。通过启动一个Django项目,你会得到一个网络应用程序的基本布局,你可以开始构建。然而,这并不是一个Django教程,所以我们将跳到眼前的话题:我们如何在Django中使用CockroachDB的多区域功能?

当你扩大使用多区域集群时,你可能需要将某些数据子集保存在特定的地方。将特定的数据保存在特定地理位置的服务器上,也被称为数据归属。CockroachDB使用ALTER DATABASE ... PLACEMENT RESTRICTED 语句对多区域集群中的数据统治有基本的支持。

要跟上博客的步伐,你需要三个Kubernetes集群。理想情况下,这些集群位于不同的区域,但这并不是必须的。你还需要对DockerKubernetes有基本了解。

作为一个Django和Python的新手,我选择使用CockroachDB文档中Django应用实例。这是一个将客户、产品和订单插入CockroachDB数据库的简单应用。

这个博客的最新的多区域代码可以在这里找到。

为了展示CockroachDB的多区域功能,我将更新Django示例程序中的捕获客户的python函数,以记录客户应该居住在哪个云提供商。例如,如果客户 "Mike "是从AWS发布的,那么Mike的客户记录应该保留在该地区的节点上。

需要进行一些更新,以便应用程序接受额外的字段,将云记录到数据库中。需要做一些改变,首先是改变model.py 文件以增加额外的字段。

class Customers(models.Model):
    id = models.UUIDField(
        primary_key=True,
        default=uuid.uuid4,
        editable=False)
    name = models.CharField(max_length=250)
    cloud = models.CharField(max_length=250, null=True)

更新views.py 以接受新的字段。

  def post(self, request, *args, **kwargs):
        form_data = json.loads(request.body.decode())
        name, cloud = form_data['name'], form_data['cloud']
        c = Customers(name=name, cloud=cloud)
        c.save()
        return HttpResponse(status=200)

修改settings.py ,使其具有你的数据库配置。

DATABASES = {
    'default': {
        'ENGINE': 'django_cockroachdb',
        'NAME': 'django',
        'USER': 'user',
        'PASSWORD': 'password',
        'HOST': 'cockroachdb-public',
        'PORT': '26257',
        # If connecting with SSL, include the section below, replacing the
        # file paths as appropriate.
        'OPTIONS': {
            'sslmode': 'verify-full',
            'sslrootcert': '/certs/ca.crt',
            # Either sslcert and sslkey (below) or PASSWORD (above) is
            # required.
            # 'sslcert': '/certs/client.root.crt',
            # 'sslkey': '/certs/client.root.key',
        },
    },
}

最后,在0001_inital.py 文件中添加额外的字段到迁移中。

operations = [
        migrations.CreateModel(
            name='Customers',
            fields=[
                ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
                ('name', models.CharField(max_length=250)),
                ('cloud', models.CharField(max_length=250, null=True)),

现在我们有了一个可以部署的应用程序,我们需要准备我们的CockroachDB集群。我们需要做的第一件事是创建一个数据库供应用程序使用。因为我的CockroachDB集群是部署在Kubernetes上的,我将部署一个带有正确证书的安全豆荚来连接并创建一个名为Django的数据库。

CREATE DATABASE django;

现在我们有了一个数据库,我们可以在每个区域部署我们的应用程序。通过这样做,Django将创建所有需要的数据库表等。同样,因为我使用的是Kubernetes,我将直接部署上面git仓库中的清单。确保你设置了上下文并部署到了正确的命名空间。

kubectl apply -f ./kubernetes/deployment.yaml

一旦应用程序部署完毕,负载平衡器服务创建完毕,我们就可以检索外部IP或主机名(如果是AWS的话)来发布我们的数据。在这里,我已经为我的每个上下文和每个命名空间设置了一个环境变量。

az_app_ip=$(kubectl get svc django-service --context $clus1 --namespace $azregion -o json | jq -r '.status.loadBalancer.ingress[0].ip')
aws_app_ip=$(kubectl get svc django-service --context $clus2 --namespace $aws_region -o json | jq -r '.status.loadBalancer.ingress[0].hostname')
gcp_app_ip=$(kubectl get svc django-service  --context $clus3 --namespace $gcp_region -o json | jq -r '.status.loadBalancer.ingress[0].ip')

使用应用程序的简单API向数据库添加三个条目。你会注意到第二个字段是 "cloud",用不同的值来表示它被部署到哪个云。

curl --header "Content-Type: application/json" \
--request POST \
--data '{"name":"Carl", "cloud":"azure"}' http://$az_app_ip:8000/customer/

curl --header "Content-Type: application/json" \
--request POST \
--data '{"name":"Mike", "cloud":"aws"}' http://$aws_app_ip:8000/customer/

curl --header "Content-Type: application/json" \
--request POST \
--data '{"name":"Dan", "cloud":"gcp"}' http://$gcp_app_ip:8000/customer/

现在我们在CockroachDB内的django数据库中有了一些数据,我们可以把注意力转移到多区域的能力上。

为了启用多区域配置,需要执行几个简单的步骤。首先是为数据库设置主区域,然后再添加其他区域。在我的案例中,这是Azure的uksouth作为主区域,然后是AWS的eu-west-1和GCP的europe-west4。

ALTER DATABASE django PRIMARY REGION "uksouth";
ALTER DATABASE django ADD REGION "eu-west-1";
ALTER DATABASE django ADD REGION "europe-west4";

对于cockroach_example_customers表,我们想根据云列的值来定位数据。这意味着优化访问数据的正确表定位是REGIONAL BY ROW。这些语句使用CASE语句,将给定的云的数据放在正确的区域中。

ALTER TABLE cockroach_example_customers ADD COLUMN region crdb_internal_region AS (
  CASE WHEN cloud = 'aws' THEN 'eu-west-1'
       WHEN cloud = 'azure' THEN 'uksouth'
       WHEN cloud = 'gcp' THEN 'europe-west4'
  END
) STORED;
ALTER TABLE cockroach_example_customers ALTER COLUMN REGION SET NOT NULL;
ALTER TABLE cockroach_example_customers  SET LOCALITY REGIONAL BY ROW AS "region";

接下来,运行一个复制报告,看看哪些范围仍然不符合你所期望的定位。

SELECT * FROM system.replication_constraint_stats WHERE violating_ranges > 0;

接下来,运行复制报告文档中建议的查询,该查询应该显示哪些数据库和表的名称包含违规的_ranges。

WITH
    partition_violations
        AS (
            SELECT
                *
            FROM
                system.replication_constraint_stats
            WHERE
                violating_ranges > 0
        ),
    report
        AS (
            SELECT
                crdb_internal.zones.zone_id,
                crdb_internal.zones.subzone_id,
                target,
                database_name,
                table_name,
                index_name,
                partition_violations.type,
                partition_violations.config,
                partition_violations.violation_start,
                partition_violations.violating_ranges
            FROM
                crdb_internal.zones, partition_violations
            WHERE
                crdb_internal.zones.zone_id
                = partition_violations.zone_id
        )
SELECT * FROM report;

你应该看到cockroach_example_customers表包含违规的范围。现在我们可以启用放置限制,将这些范围重新放置在正确位置的节点上。

ALTER DATABASE django PLACEMENT RESTRICTED;

现在你已经限制了所有区域表的无投票权复制的位置,你可以运行另一个复制报告来查看效果。请耐心等待,因为这可能需要几分钟的时间才能产生效果(需要移动的范围越多,需要的时间越长)。

SELECT * FROM system.replication_constraint_stats WHERE violating_ranges > 0;

作为Python和Django的新手,我发现编辑一个现有的应用程序来展示CockroachDB的多区域功能是很简单的。这向我展示了在Django框架的帮助下开发Python应用程序是多么容易。

使用CockroachDB的数据管理(通俗地说,就是把数据钉在特定的地方)有助于提高读写的性能。将范围钉在特定的位置上,可以减少做出共识决定的往返时间,从而减少写的延迟。这项功能的另一个好处是,通过控制数据的位置,你可以符合数据主权或所有权的立法。所以,如果你想在关系数据库的支持下创建多区域的Python应用程序,Django和CockroachDB是一个很好的组合。

不要忘记我在博客中使用的所有代码都可以在这里找到。