前言
在公有云技术与产品飞速发展的时代,业务对于其自身的可用性提出了越来越高的要求,当跨区域容灾已经无法满足业务需求的情况下,我们通常会考虑多云部署我们的业务平台,以规避更大规模的风险。但在多云平台部署的架构下,多云资源的管理成为了一个耗时耗力,管理与运维成本颇高的难点。这导致了第一,云管人员需要精通掌握两家甚至多家云厂商的技术与服务产品,或者第二,多支独立团队分别运维与管理导致的高团队成本等诸多痛点。所以公有云资源纳管平台(以下简称云管平台)应运而生。
云管平台简化了运管人员的操作流程,降低了公有云产品的技术门槛,但是在多云产品的集中式纳管上,却难度重重。其一,不同公有云厂商,使用独立的infrastructure-as-code(以下简称IaC)体系或者服务,譬如亚马逊云科技的Cloudformation服务;其二,不论云厂商还是开源的IaC产品,均使用标记语言或者类标记语言编写代码,鲜有使用Java,Python或者Golang等开发语言的运行时,这对于精通于开发语言的,云管平台的开发人员导致了额外学习成本,需要从0开始学习标记语言,往往会导致开发团队的抵触甚至拒绝;其三,标记语言运行过程中,更倾向于将整体堆栈运行完毕之后,输出对应的outputs,但在运行过程中,存在难以直接循环调用outputs,无法写入数据库等诸多难点,这增大了编写难度以及代码编写工作量。有没有一款产品,能够适配主流公有云IaC,又可以使用高级编程语言编写代码呢?
什么是Pulumi?
Pulumi是非常流行的,现代化IaC平台。Pulumi引入了主流编程语言,譬如JavaScript,Python,Java,Golang,.NET,以及标记语言YAML,可以使用上述语言,通过Pulumi SDK管理不同云厂商的资源。Pulumi目前已经支持了主流公有云厂商譬如亚马逊云科技,微软Azure,谷歌云与阿里云,及其生态譬如Fastly,Akamai,Cloudflare,Kubernetes,Kong,Apache Kafka等。
Pulumi的组成如下图所示:
每一个Pulumi项目包含至少一个程序组,程序组由编程语言书写,描述不同资源的运作
以及彼此之间的关系。
以下以Python 代码为例,描述我们在Pulumi中创建了web-sg名称安全组,并附加该安全组,以’ami-6869aa05’为镜像,创建了实例规格为t2.micro的EC2虚拟机。
import pulumi
import pulumi_aws as aws
group = aws.ec2.SecurityGroup('web-sg',
description='Enable HTTP access',
ingress=[
{ 'protocol': 'tcp', 'from_port': 80, 'to_port': 80, 'cidr_blocks': ['0.0.0.0/0'] }
])
server = aws.ec2.Instance('web-server',
ami='ami-6869aa05',
instance_type='t2.micro',
vpc_security_group_ids=[group.name]
)
pulumi.export('public_ip', server.public_ip)
pulumi.export('public_dns', server.public_dns)
Pulumi 如何工作
Pulumi的运作由几个部分组成:
第一,语言处理中枢。Language Host负责运行Pulumi的程序,并为您的开发语言准备好与之对应的环境,譬如Python3.7。语言中枢由两部分组成:
1.执行器。它会协助Pulumi准备并设置好相应的Runtime(运行时);
2.运行时。它会负责为您编写的程序做好运行准备,并在过程中监控程序的运行。
第二,资源提供方Provider。
资源提供方通过资源插件(Resource Plugin,用来管理资源)与原生SDK协作,来管理云端资源。
有了上述两部分组成,Engine引擎就可以实现云端资源的管理。引擎已经被封装进pulumi cli,无需额外安装与部署。
如何创建一个Pulumi项目
1.安装Pulumi(以Linux为例)
curl -fsSL https://get.pulumi.com | sh
2.安装运行时(以Python为例)
请阅读 如何在Linux上安装Python3 本文不再复述
3.配置权限
请阅读 安装或更新最新版本的 AWS CLI ,完成AWS CLI的安装,本文不再复述
请阅读 配置 AWS CLI 完成完成权限配置,本文不再复述
4.创建新项目
$mkdir newproject && cd newproject
$pulumi new aws-python
Pulumi new的命令行会通过交互式的方式,为您创建新的项目与新的堆栈(stack),并安装好所有需要的组件(Module)。
5.部署这个项目
执行pulumi up进行项目部署
执行成功后,我们可以在S3中看到有pulumi创建出来的S3桶
6.调整项目部署
我们要在已经部署的项目中,做出一些调整。之前的部署结束后,我们创建了一个S3桶,这次我们需要为其添加一个index.html 并将其托管为静态站点
6.1创建站点首页
在其中添加文本:
<html>
<body>
<h1>Hello, Amazon!</h1>
</body>
</html>
保存后,编辑__main__.py
在结尾处添加:
bucketObject = s3.BucketObject( ‘index.html’, bucket=bucket.id, source=pulumi.FileAsset(‘index.html’))
再次执行pulumi up变更部署
6.2 调整部署
这次我们需要编辑S3 bucket的属性,使其托管静态站点,并调整Bucket ACL,使其可以被匿名访问。
编辑 __main__.py
替换bucket segment,使其成为:
bucket = s3.Bucket('my-bucket',
website=s3.BucketWebsiteArgs(
index_document="index.html",
))
替换bucketObject segment,使其成为:
bucketObject = s3.BucketObject(
'index.html',
acl='public-read',
content_type='text/html',
bucket=bucket,
source=pulumi.FileAsset('index.html'),
)
在结尾处,增加输出:
pulumi.export('bucket_endpoint', pulumi.Output.concat('http://', bucket.website_endpoint))
执行Pulumi up,发布项目变更后,得到输出结果:
Outputs:
+ bucketEndpoint: "http://my-bucket-e7bfd5a.s3-website-us-west-2.amazonaws.com"
bucketName : "my-bucket-e7bfd5a"
访问bucketEndpoint,我们可以看到
7.销毁项目
我们可以执行pulumi destroy 销毁项目。
结论
通过这个简单的案例,我们展示了通过pulumi可以轻松的创建,管理与删除一个项目,在项目中,我们可以创建,调整,管理,删除与项目相关的Resource,将pulumi与编程语言相结合,可以实现云管平台的个性化需求,并通过云管平台的几次点击,实现复杂逻辑下,不同云服务的组合,满足业务场景的需求。
额外案例
我们会额外提供几个pulumi program,方便大家直观的了解到pulumi的编码方式。
- 创建一个名称为“DocumentDBCluster”Amazon DocumentDB的Cluster,自动备份保存15天,必须开启删除保护。然后将DocumentDB Cluster Endpoint输出。
import pulumi
import pulumi_aws as aws
docdb = aws.docdb.Cluster("docdb",
backup_retention_period=5,
cluster_identifier="DocumntDBCluster",
engine="docdb",
master_password="mustbeeightchars",
master_username="foo",
deletion_protection=True,
skip_final_snapshot=True)
pulumi.export('DocumentCluster_endpoint', docdb.endpoint)
下面是一个稍微复杂一些的场景:
- 场景共由五个部分组成,第一部分,环境定义;第二部分,创建SSH&HTTP的安全组,第三部分,创建EC2并附加EIP,第四部分,创建Aurora ServerlessV2,第五部分,输出。大家能够通过下列python代码与场景逐一对应。
import pulumi
import pulumi_aws as aws
config = pulumi.Config()
availability_zone = config.require("availabilityZone")
environment_type = config.get("environmentType")
if environment_type is None:
environment_type = "dev"
ami_id = config.get("amiID")
if ami_id is None:
ami_id = ""
ami_id_value = "/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2" if ami_id == "" else aws.get_ssm_parameter_string().value
key_pair_name = config.require("keyPairName")
db_instance_identifier = config.get("dbInstanceIdentifier")
if db_instance_identifier is None:
db_instance_identifier = "mydatabase"
db_username = config.get("dbUsername")
if db_username is None:
db_username = "postgres"
db_password = config.require("dbPassword")
web_app_security_group = aws.ec2.SecurityGroup("webAppSecurityGroup",
name="webAppSG",
description="Allow HTTP/HTTPS and SSH inbound and outbound traffic",
ingress=[
aws.ec2.SecurityGroupIngressArgs(
ip_protocol="tcp",
from_port=80,
to_port=80,
cidr_ip="0.0.0.0/0",
),
aws.ec2.SecurityGroupIngressArgs(
ip_protocol="tcp",
from_port=443,
to_port=443,
cidr_ip="0.0.0.0/0",
),
aws.ec2.SecurityGroupIngressArgs(
ip_protocol="tcp",
from_port=22,
to_port=22,
cidr_ip="0.0.0.0/0",
),
])
web_app_instance = aws.ec2.Instance("webAppInstance",
availability_zone=availability_zone,
image_id=ami_id_value,
instance_type="t2.small",
key_name=key_pair_name,
security_groups=[web_app_security_group.id,default,])
web_app_eip = aws.ec2.EIP("webAppEIP",
vpc=True,
instance=web_app_instance.id)
web_app_database = aws.rds.Cluster("webAppDatabase",
cluster_identifier=db_instance_identifier,
engine="aurora-postgresql",
engine_mode="provisioned",
engine_version="13.6",
database_name=db_username,
master_username=db_username,
master_password=db_password,
serverlessv2_scaling_configuration=aws.rds.ClusterServerlessv2ScalingConfigurationArgs(
max_capacity=1,
min_capacity=0.5,
))
web_app_database_instance = aws.rds.ClusterInstance("webAppDatabaseInstance",
cluster_identifier=web_app_database.id,
instance_class="db.serverless",
engine=web_app_database.engine,
engine_version=web_app_database.engine_version)
pulumi.export("websiteURL", web_app_eip.id.apply(lambda id: f"http://{id}"))
pulumi.export("webServerPublicDNS", web_app_instance.public_dns_name)
pulumi.export("webAppDatabaseEndpoint", web_app_database.endpoint)
参考资源:
了解Pulumi的Registry
https://www.pulumi.com/registry/
安装Python3
https://docs.aws.amazon.com/zh_cn/parallelcluster/latest/ug/install-linux-python.html
安装或更新最新版本的 AWS CLI
https://docs.aws.amazon.com/zh_cn/cli/latest/userguide/getting-started-install.html
配置 AWS CLI
https://docs.aws.amazon.com/zh_cn/cli/latest/userguide/cli-chap-configure.html
本篇作者