Pulumi 在 Azure 上的实践
在本文中,我们探索了使用 Pulumi 和 TypeScript 在 Azure 上构建基础设施的简便性。
在上一篇文章中,我们通过 Pulumi 轻松入门了基础设施即代码。我们配置了 CLI,并设置了 Pulumi 服务来存储我们的配置和状态。我们还使用 JavaScript 构建了一个简单的虚拟机,并探讨了 JavaScript、TypeScript 和 C# 之间的代码差异。
在本文中,我们将扩展这些概念,使用 TypeScript 构建一个三层 Web 应用程序的基础设施。我们首先将构建一个数据库服务,该服务将允许我们托管系统的结构化数据。然后,我们将添加一个 WebApp,我们可以通过连接字符串将其连接到数据库。最后,我们将创建一个无服务器前端,使用 Azure 存储来托管网页和其他客户端代码。
创建新应用程序
首先,我们需要创建一个新应用程序,以便开始构建我们的基础设施。创建一个新目录并运行命令 pulumi new azure-typescript
。这将为 Azure 资源构建一个新的 TypeScript 应用程序。
此过程完成后,请删除 index.ts 中的所有现有示例基础设施,以清理它们。
我们将设置特定的配置元素,以便通过引用不同的配置文件来构建不同的环境(例如开发和生产)。使用以下 Pulumi 配置命令来设置位置和资源组名称
pulumi config set resourceGroup gpDevPulumi pulumi config set location EastUS
稍后,我们将使用相同的模式来设置一些额外的变量以配置不同的资源,但这两个变量是我们环境的核心。
接下来,我们将以下代码添加到 index.ts 文件中
import * as pulumi from "@pulumi/pulumi";
import * as resources from "@pulumi/azure-native/resources";
const config = new pulumi.Config();
const configRG = config.require("resourceGroupName");
const configLocation = config.require("location");
// Create an Azure Resource Group
const resourceGroup = new resources.ResourceGroup(configRG, { location: configLocation, resourceGroupName: configRG });
这段代码与构建示例 VM 的代码开头非常相似,除了我们现在可以从 Pulumi 配置文件中检索配置变量。如果我们现在使用 pulumi up
构建此基础设施,将使用我们通过配置提供的名称和位置创建一个标准的资源组。
构建数据库
现在我们的资源组已启动并运行,让我们使用 Azure Cosmos DB 构建我们的数据库。我们先构建数据库,因为它在我们的架构中除了资源组和位置之外还有其他依赖项。其他资源也将依赖于此数据库。
为了确保我们的基础设施由配置驱动,我们还将通过配置文件设置所有必需的变量。要使用 Pulumi 的数据库 API,我们需要添加另一个 import
行
import * as storage from "@pulumi/azure-native/storage";
我们还需要创建两个配置变量来保存 Cosmos DB 账户名称和 Cosmos DB 数据库名称
const configDBAccountName = config.require("cosmosAccountName"); const configDBName = config.require("cosmosDBName");
一旦我们拥有了这些组件,我们就可以通过输入以下代码块来创建我们的 Cosmos DB 账户和数据库
// Cosmos DB Account
var cosmosdbAccount = new documentdb.DatabaseAccount(configDBAccountName, {
resourceGroupName: configRG,
databaseAccountOfferType: documentdb.DatabaseAccountOfferType.Standard,
locations: [{
locationName: configLocation,
failoverPriority: 0,
}],
consistencyPolicy: {
defaultConsistencyLevel: documentdb.DefaultConsistencyLevel.Session,
},
});
// Cosmos DB Database
var cosmosdbDatabase = new documentdb.SqlResourceSqlDatabase(configDBName, {
resourceGroupName: configRG,
accountName: cosmosdbAccount.name,
resource: {
id: configDBName,
},
});
此代码将创建我们的 Cosmos DB 账户和数据库。但在我们测试并运行 pulumi up
之前,我们需要使用以下代码设置我们的配置
pulumi config set cosmosContainerName pulumi-dev-cosmosdbcontainer pulumi config set cosmosDBName pulumi-dev-cosmosdb
设置好这两个变量后,我们现在可以使用 pulumi up
来构建我们的基础设施。一旦成功,让我们通过 pulumi down
来折叠我们已构建的内容,然后继续处理 Web 应用程序组件。
添加 Web 应用程序
现在我们的数据库已就绪,我们可以遵循类似的过程为我们的应用程序层添加 Azure WebApp。对于我们的 WebApp,我们需要一个存储账户、服务计划、应用程序洞察实例以及 WebApp 本身。为了让 Pulumi 构建此基础设施,我们需要另外三个导入
import * as storage from "@pulumi/azure-native/storage";
import * as web from "@pulumi/azure-native/web";
import * as insights from "@pulumi/azure-native/insights";
我们还需要额外的配置选项,因为我们可以为所有三个组件指定几种不同的类型和 SKU
// Storage Configuration const configStorageName = config.require("storageName"); const configStorageKind = config.require("storageKind"); const configStorageSKU = config.require("storageSKU"); // WebApp Service Configuration const configAppServiceName = config.require("appServiceName"); const configAppServiceKind = config.require("appServiceKind"); const configAppServiceSKUName = config.require("appServiceSKUName"); const configAppServiceSKUTier = config.require("appServiceSKUTier"); // WebApp App Insights Configuration const configAppInsightsName = config.require("appInsightsName"); const configAppInsightsKind = config.require("appInsightsKind"); const configAppInsightsType = config.require("appInsightsType"); // WebApp Configuration const configAppName = config.require("webAppName");
通过使用配置驱动的组件,如果我们需要增加环境的范围,我们的部署代码将来就不需要更改太多。
一旦我们拥有了所有这些,我们还需要使用 pulumi config
命令来设置它们。
您可以查阅Azure 文档以获取有关类型和 SKU 的更多信息,但请注意,如果您的配置中设置了不正确的类型,Pulumi 将生成错误。
我们现在可以创建将部署我们基础设施的代码。存储、服务计划和应用洞察都相当直接。
// Storage Account
var storageAccount = new storage.StorageAccount(configStorageName, {
resourceGroupName: resourceGroup.name,
kind: configStorageKind,
sku: {
name: configStorageSKU,
},
});
// WebApp Service Plan
var appServicePlan = new web.AppServicePlan(configAppServiceName, {
resourceGroupName: resourceGroup.name,
kind: configAppServiceKind,
sku: {
name: configAppServiceSKUName,
tier: configAppServiceSKUTier,
},
});
// App Insights for the Web App
var appInsights = new insights.Component(configAppInsightsName, {
resourceGroupName: resourceGroup.name,
kind: configAppInsightsKind,
applicationType: configAppInsightsType,
});
对于我们的 WebApp,我们需要从上一步检索 Cosmos DB 连接字符串。要做到这一点,我们可以使用 Cosmos DB 账户变量并通过添加以下行从中检索连接字符串
var comosdbDonnectionString = cosmosdbAccount.documentEndpoint;
Pulumi 会向我们创建的资源公开许多属性和函数,使我们能够在它们创建后检索它们的信息。Pulumi 还能理清资源之间的任何依赖关系,确保代码的最大程度并行运行,而不会在部署时导致错误。
一旦我们有了这个连接字符串,我们就可以创建我们的 WebApp 并通过添加以下代码块来添加额外的 Cosmos DB 字符串
var webApp = new web.WebApp(configAppName, {
resourceGroupName: resourceGroup.name,
serverFarmId: appServicePlan.id,
siteConfig: {
appSettings: [
{
name: "APPINSIGHTS_INSTRUMENTATIONKEY",
value: appInsights.instrumentationKey,
},
{
name: "APPLICATIONINSIGHTS_CONNECTION_STRING",
value: pulumi.interpolate`InstrumentationKey=${appInsights.instrumentationKey}`,
},
{
name: "ApplicationInsightsAgent_EXTENSION_VERSION",
value: "~2",
}
],
connectionStrings: [{
name: "db",
connectionString: comosdbDonnectionString,
type: web.ConnectionStringType.DocDb
}],
},
});
添加了所有这些组件后,我们现在已创建了 WebApp 并将其连接到数据库。我们可以再次运行 pulumi up
来在 Azure 中构建我们的基础设施。完成后,我们可以检查 WebApp 的配置,以确认我们的 Cosmos DB 连接字符串已正确填充。
完成前端部分
现在我们有了应用程序和数据库,让我们通过修改存储账户来托管静态页面,来为我们的无服务器前端构建一些基础设施。这次我们不需要添加任何额外的导入,但我们需要添加以下配置行
const configFrontEndName = config.require("frontEndName");
我们还需要使用 CLI 设置此变量
pulumi config set frontEndName pulumiFrontEnd
因为我们已经有了存储账户,让我们重复使用它
// Enable static website support
var staticWebsite = new storage.StorageAccountStaticWebsite(configFrontEndName, {
accountName: storageAccount.name,
resourceGroupName: resourceGroup.name,
indexDocument: "index.html",
error404Document: "404.html",
});
这会设置一些基本的文件服务配置。我们还应该有一些要服务的文件,所以让我们在 websrc 目录中创建一个示例 index 和 404 文件。Pulumi 的主要优点之一是源代码可以使用相同的语言,并驻留在同一个代码库中。
让我们通过添加以下代码块将这些文件添加到我们的 blob 存储中
["index.html", "404.html"].map(name =>
new storage.Blob(name, {
resourceGroupName: resourceGroup.name,
accountName: storageAccount.name,
containerName: staticWebsite.containerName,
source: new pulumi.asset.FileAsset(`./websrc/${name}`),
contentType: "text/html",
}),
);
此代码使用 Pulumi 的 asset 函数从我们的源目录检索文件并将其添加到 Blob 存储。让我们添加最后一个函数来涵盖我们所做的所有事情
export const staticEndpoint = storageAccount.primaryEndpoints.web;
每当我们使用 Pulumi 脚本中的 export 关键字时,Pulumi API 都会根据我们发送的对象或属性生成 Output。在这种情况下,我们发送的是我们的存储容器用于提供网页的 URL。
要了解有关使用 Azure Pulumi 进行云工程的更多信息,请查看资源使用 Azure Pulumi 进行云工程。
后续步骤
在本文中,我们通过 Pulumi 使用 TypeScript 构建了一个三层 Web 应用程序的基础设施。我们探讨了 Pulumi 如何存储和检索配置变量以用于配置组件,以及我们如何访问基础设施属性甚至本地文件来进一步构建我们的应用程序。
在本系列的最后一篇文章中,我们将研究构建测试以确保我们构建的基础设施满足我们的要求。
要了解有关使用 Azure Pulumi 进行云工程的更多信息,请查看资源使用 Azure Pulumi 进行云工程。