

本文属于机器翻译版本。若本译文内容与英语原文存在差异，则一律以英文原文为准。

# AWS Marketplace 上基于容器的产品
<a name="container-based-products"></a>

**重要**  
2026 年 3 月 1 日，AWS Marketplace 将停用关于在 Amazon EKS 上部署 Helm 图表的快速启动功能。现有部署将继续正常运行。不过，您仍然可以使用标准 Helm 命令或容器映像在 Amazon ECS 上进行部署。

AWS Marketplace 支持使用 Docker 容器的软件产品。容器产品由交付选项组成，这些交付选项是一起使用的一组容器映像和部署模板。您可以为产品提交至少一个，最多为四个交付选项。对于每个交付选项，您都需要提供一组容器映像、使用说明和部署模板链接，以便客户启动该交付选项。本主题提供有关 AWS Marketplace 上基于容器的产品的信息。

AWS Marketplace 买家可以在为其提供的已发布产品详细信息页面上看到可用的交付选项。订阅产品并选择首选交付选项后，买家将看到有关启动和使用该产品的信息和说明。对于容器映像交付选项，买家可以看到指向可用部署模板和容器映像 URL 的链接。他们还会收到有关如何拉取单个容器映像的说明。有关 Helm 图表交付选项，买家将看到使用 Helm 启动的分步说明。

有关购买体验的演练，您可以参考此视频：在 [Amazon ECS 集群上部署 AWS Marketplace 容器](https://www.youtube.com/watch?v=XaiUAiQQJtk) (3:34)。

你可以在任何环境中的任何 Kubernetes 集群上通过 AWS Marketplace 查找、订阅和部署第三方 Kubernetes 应用程序。您可以在 Amazon Elastic Container Service (Amazon ECS)、Amazon Elastic Kubernetes Service (Amazon EKS)、AWS Fargate 和 使用 Amazon EKS Anywhere (EKS Anywhere) 的本地部署第三方 Kubernetes 应用程序。您也可以将它们部署在本地自行管理 Kubernetes 集群上或 Amazon Elastic Compute Cloud (Amazon EC2) 中。

你可以在任何兼容 Docker 的运行时上运行免费和自带许可 (BYOL) 模式容器产品。

**Topics**
+ [获取帮助](#container-help)
+ [容器产品入门](container-product-getting-started.md)
+ [基于容器的产品要求 AWS Marketplace](container-product-policies.md)
+ [的容器产品定价 AWS Marketplace](pricing-container-products.md)
+ [容器产品计费、计量和许可集成](container-products-billing-integration.md)
+ [容器 产品的 Amazon SNS 通知](container-notification.md)

## 获取帮助
<a name="container-help"></a>

如需容器产品的帮助，请联系您的 AWS Marketplace 业务开发合作伙伴或 [AWS Marketplace 卖家运营](https://aws.amazon.com/marketplace/management/contact-us/)团队。

# 容器产品入门
<a name="container-product-getting-started"></a>

作为 AWS Marketplace 卖家，您可以创建基于容器的软件产品。容器产品由交付选项组成，这些交付选项是一起使用的一组容器映像和部署模板。以下主题将介绍如何开始使用容器产品。
+ [产品生命周期](#container-product-lifecycle)
+ [先决条件](#container-prereq)
+  [第 1 步：为容器产品创建产品 ID 和产品代码](#create-initial-container-product) 
+ [第 2 步：创建初始列表](#container-initial-listing)
+ [第 3 步：添加产品的初始版本](#container-add-version-gs)
+ [第 4 步：（仅适用于付费产品）集成计量或合同定价](#getting-started-integrate-metering)
+ [后续步骤](#getting-started-integrate-metering)
+ [容器产品扫描是否存在安全问题](#container-security)

## 产品生命周期
<a name="container-product-lifecycle"></a>

当您在中创建产品时 AWS Marketplace，该产品最初发布时可见性有限，因此允许列表中的账户（包括创建该产品的账户）可以看到该产品。准备就绪后，您可以将其发布到 AWS Marketplace 目录中，以允许买家订阅和购买您的产品。

在[服务器产品](https://aws.amazon.com/marketplace/management/products/server)页面上，您可以查看您的产品列表。根据所处的阶段，产品将具有以下任一状态。
+ **暂存** – 未完成的产品，您仍在为其添加信息。在第一次**保存并退出**自助服务体验时，成功的变更请求会使用您提交的完成步骤中的信息创建未发布的产品。在此状态下，您可以继续向产品添加信息或通过变更请求更改已提交的详细信息。
+ **受限** – 产品在提交给系统并通过系统中的所有验证后即已完成。然后，产品将发布到**受限**状态。此时，该产品有一个详细信息页面，只有您的账户和您加入允许列表的人才能访问该页面。您可以通过详细信息页面测试您的产品。如需更多信息或帮助，请联系 [AWS Marketplace 卖家运营](https://aws.amazon.com/marketplace/management/contact-us/)团队。
+ **公开** – 当您准备发布产品以便买家可以查看和订阅产品时，您可以使用**更新可见性**更改请求。此请求启动了 AWS Marketplace 卖家运营团队根据 AWS 政策审查和审核您的商品的工作流程。产品获得批准并处理变更请求后，产品将从**受限**状态更改为**公开**。有关 AWS 指导方针的信息，请参阅[基于容器的产品要求 AWS Marketplace](container-product-policies.md)。
+ **受限** – 果您想阻止新用户订阅您的产品，则可以使用**更新可见性**更改请求来限制该产品。**受限**状态表示现有允许列表中的用户可以继续使用该产品。但是，该产品将不再对公众可见，也不会再向新用户提供。

## 先决条件
<a name="container-prereq"></a>

在开始之前，您必须满足以下先决条件：

1. 访问和使用 [AWS Marketplace 管理门户](https://aws.amazon.com/marketplace/management/)。这是您用来注册为卖家并管理您销售的商品的工具 AWS Marketplace。有关更多信息，请参阅 [AWS Marketplace 管理门户](https://docs.aws.amazon.com/marketplace/latest/userguide/user-guide-for-sellers.html#management-portal)。

1. 注册为卖家并提交您的税务和银行信息。有关更多信息，请参阅 [注册过程](registration-process.md)。

1. 在 Amazon Elastic Container Service (Amazon ECS)、Amazon Elastic Kubernetes Service (Amazon EKS) 或 AWS Fargate中创建至少一个容器。请确保您有相关映像的链接。

1. 规划如何在 AWS Marketplace中创建和集成容器产品。

   我们建议您在公开发布产品之前尽早规划您的定价、授权和计量策略。
   + 有关基于容器的产品要求的信息，请参阅[基于容器的产品要求 AWS Marketplace](container-product-policies.md)。
   + 有关为产品设置定价的信息，请参阅[的容器产品定价 AWS Marketplace](pricing-container-products.md)。
   + 有关付费版基于容器的产品的自定义计量的信息，请参阅[按小时计量和自定义计量 AWS Marketplace Metering Service](container-products-billing-integration.md#entitlement-and-metering-for-paid-products)。

## 概述：创建容器产品
<a name="create-container-product"></a>

创建容器产品涉及以下步骤：

1. [第 1 步：为容器产品创建产品 ID 和产品代码](#create-initial-container-product)

1. [第 2 步：创建初始列表](#container-initial-listing)

1. [第 3 步：添加产品的初始版本](#container-add-version-gs)

1. [第 4 步：（仅适用于付费产品）集成计量或合同定价](#getting-started-integrate-metering)

1. [更新产品知名度](#container-product-visibility)

有关产品生命周期的信息，请参阅[产品生命周期](#container-product-lifecycle)。

## 第 1 步：为容器产品创建产品 ID 和产品代码
<a name="create-initial-container-product"></a>

要开始使用容器产品，您必须在中创建产品编码和产品代码记录 AWS Marketplace。产品 ID 用于在产品的整个生命周期中对其进行跟踪。

使用以下步骤在中创建新的容器产品并生成产品 ID。 AWS Marketplace 管理门户

**注意**  
此过程还会为您的容器创建与您的产品配对的公钥。

**要创建容器产品 ID，请执行以下操作：**

1. 打开 Web 浏览器并登录 [AWS Marketplace 管理门户](https://aws.amazon.com/marketplace/management/)。

1. 从菜单栏中选择**产品**，然后选择**服务器**。

1. 选择**创建服务器产品**，然后选择**容器**。

1. 生成容器产品 ID 和代码。
**注意**  
（可选）您可以为产品添加标签以获得基于标签的授权。有关更多信息，请参阅[标记您的 AWS 资源](https://docs.aws.amazon.com/tag-editor/latest/userguide/tagging.html)。

1. 选择**继续**以继续创建产品。

## 第 2 步：创建初始列表
<a name="container-initial-listing"></a>

生成产品 ID、产品代码和公钥后，您将使用向导创建初始列表。

1. 为您的产品列表提供产品信息。

1. 确定产品的定价模式。
**注意**  
有关更多信息，请参阅[容器产品定价](https://docs.aws.amazon.com/marketplace/latest/userguide/pricing-container-products.html)。
**注意**  
对于付费产品，起价为 0.01 美元，这样您和 AWS Marketplace 卖家运营团队测试产品时就不会产生高昂的成本。产品公开上架时请提供实际价格。

1. 提供其他优惠信息，包括退款政策、EULA 和优惠可用性。

1. 为您的容器产品添加初始存储库。

1. 在最后一步选择**提交**，将产品移至“受限可见性”。
**注意**  
您的容器产品最初是使用占位符版本创建的。当产品的可见性受限时，您将添加最终版本。

## 第 3 步：添加产品的初始版本
<a name="container-add-version-gs"></a>

您的产品在其生命周期内可能有多个版本。每个版本都有一组特定于该版本的容器映像。要添加产品的初始版本，请参阅 [在上添加您的容器产品的新版本 AWS Marketplace](container-add-version.md)。

## 第 4 步：（仅适用于付费产品）集成计量或合同定价
<a name="getting-started-integrate-metering"></a>

对于按用量定价的基于容器的产品，您可以使用 [AWS Marketplace Metering Service](https://docs.aws.amazon.com/marketplacemetering/latest/APIReference/Welcome.html) 来检查使用产品的权利以及对用量进行计量以进行计费。您必须根据在设置定价信息时创建的定价模式进行计量。有关更多信息，请参阅 [按小时计量和自定义计量 AWS Marketplace Metering Service](container-products-billing-integration.md#entitlement-and-metering-for-paid-products)。

### 合同定价
<a name="container-integrate-LM"></a>

对于采用合同定价的基于容器的产品，您可以使用 AWS License Manager 将许可证与您的产品相关联。

有关与集成的更多信息 AWS License Manager，请参阅[集装箱产品的合同定价 AWS License Manager](container-license-manager-integration.md)。

## 第 5 步：更新产品可见性
<a name="container-product-visibility-allowlist"></a>

当您在中创建产品时 AWS Marketplace，该产品最初发布时可见性有限，因此允许列表中的账户（包括创建该产品的账户）可以看到该产品。您可以更新产品可见性，以使买家订阅和购买您的产品。或者，您可以更新要添加 AWS 账户的产品允许列表。本主题将介绍如何管理哪些买家可以在 AWS Marketplace中查看您的产品。

有关产品可见性和生命周期的更多信息，请参阅 [产品生命周期](#container-product-lifecycle)。

**Topics**
+ [更新产品知名度](#container-product-visibility)
+ [更新允许名单 AWS 账户 IDs](#container-update-allowlist)

### 更新产品知名度
<a name="container-product-visibility"></a>

**要更新可见性，请执行以下操作：**

1. 打开 AWS Marketplace 管理门户 at[https://aws.amazon.com/marketplace/management/tour/](https://aws.amazon.com/marketplace/management/tour/)，然后登录您的卖家账户。

1. 转到[https://aws.amazon.com/marketplace/management/products/server](https://aws.amazon.com/marketplace/management/products/server)页面，在**当前服务器产品**选项卡上，选择要修改的基于容器的产品。

1. 从**请求更改**下拉列表中，选择**更新可见性**。
**注意**  
您可以使用此更改请求请求将产品从**受限**态更改为**公开**状态。但是，变更请求必须经过 AWS Marketplace 卖家运营团队的批准才能移至**公开**。

1. 选择**提交**，提交您的请求以供审核。

1. 确认**请求**选项卡将**请求状态**显示为**正在审核**。请求完成后，状态变为**成功**。

### 更新允许名单 AWS 账户 IDs
<a name="container-update-allowlist"></a>

您可以更改可以在 AWS 账户 IDs 受限状态下查看您的产品的列表。允许列表中的账户在产品详细信息页面上的产品版本旁边会显示一个受限徽章。

1. 打开 AWS Marketplace 管理门户 at [https://aws.amazon.com/marketplace/管理/tour/](https://aws.amazon.com/marketplace/management/tour/) 并登录您的卖家账户。

1. 在[https://aws.amazon.com/marketplace/management/products/server](https://aws.amazon.com/marketplace/management/products/server)页面上，选择要修改的容器产品。

1. 从**请求更改**下拉列表中，选择**更新允许列表**。将显示当前被列入允许列表的账户列表。

1. 在 “**允许名单 AWS 账户**” 字段中，输入 AWS 账户 IDs 并使用逗号分隔它们。

1. 选择**提交**，提交您的请求以供审核。

1. 确认**请求**选项卡将**请求状态**显示为**正在审核**。请求完成后，状态将更新为**成功**或**失败**。

## 后续步骤
<a name="container-next-steps"></a>

创建容器产品后，您可以使用以下主题中的信息对其进行配置和管理：
+ [在上更新您的集装箱产品的产品信息 AWS Marketplace](update-container-product-info.md)
+ [在上添加您的容器产品的新版本 AWS Marketplace](container-add-version.md)
+ [管理集装箱产品定价 AWS Marketplace](container-pricing.md)
+ [按国家/地区更新集装箱产品的供应情况 AWS Marketplace](container-update-offer-avail-country.md)
+ [在 AWS Marketplace 上更新容器产品的最终用户许可协议（EULA）](container-update-eula.md)
+ [在上测试和发布您的容器产品 AWS Marketplace](test-release-product.md)

## 容器产品扫描是否存在安全问题
<a name="container-security"></a>

当您创建更改请求以向容器产品添加新版本时，我们会扫描该新版本中包含的容器映像并检查是否存在安全漏洞。为此，我们对图像执行 layer-by-layer静态扫描。如果我们发现具有可远程利用风险的关键漏洞，我们将提供已发现的问题列表。我们强烈建议您使用容器映像扫描程序（如 Clair、Twistlock、Aqua Security 或 Trend Micro）执行自己的安全分析，以避免延迟提取和发布过程。

您为构建容器映像所选择的基本映像可对最终映像的安全配置文件产生重大影响。如果您所选取的基本映像已具有已知关键漏洞，则会因基本层而标记它们，即使应用程序软件层是干净的也是如此。我们建议您在构建镜像并将其提交给之前，先确认自己从一个没有漏洞的基础容器开始 AWS Marketplace。

# 在上更新您的集装箱产品的产品信息 AWS Marketplace
<a name="update-container-product-info"></a>

创建产品 ID 并设置定价后，您可以编辑您的产品信息，包括客户将在 AWS Marketplace中看到的有关您的容器产品的内容。例如，如果新版本修改了产品的描述或要点，则您可以使用新数据编辑产品信息。您还可以更新其他产品信息，包括产品名称、SKU 描述、类别、关键词等。以下步骤概述了如何为您的产品创建产品详细信息。

**要为您的容器产品创建或更新产品详细信息，请执行以下操作：**

1. 登录到 [AWS Marketplace 管理门户](https://aws.amazon.com/marketplace/management/)。

1. 从**产品**菜单中选择**服务器**。

1. 在**服务器产品**选项卡上，选择要修改的产品。

1. 从**请求更改**下拉列表中，选择**更新产品信息**。

1. 更新以下任何您想要更改的字段：
   + **产品标题**
   + **SKU**
   + **简短描述**
   + **详细描述**
   + **产品徽标图片 URL**
   + **要点**
   + **产品类别**
   + **关键词**
   + **产品视频 URL**
   + **资源**
   + **支持信息**
**注意**  
图片 URLs 必须位于可公开访问的 Amazon S3 存储桶中。有关徽标格式的更多详细信息，请参阅[公司和产品徽标要求](product-submission.md#seller-and-product-logos)。

1. 选择**提交**。

1. 验证请求是否显示在**请求**选项卡上，状态为**正在审核**。您可能需要刷新页面才能在列表中看到请求。

您可以随时从[服务器产品](https://aws.amazon.com/marketplace/management/products/server)页面的**请求**选项卡中查看请求的状态。

# 在上添加您的容器产品的新版本 AWS Marketplace
<a name="container-add-version"></a>

作为 AWS Marketplace 卖家，您可以添加集装箱商品的新版本、管理版本和更新版本信息。您的产品在其生命周期内可能有多个版本。每个版本都有一组特定于该版本的容器映像。以下主题将说明如何管理容器产品的产品版本。

**注意**  
只有为产品创建了产品 ID 和定价，才能向产品添加版本。有关这些步骤的更多信息，请参阅[第 1 步：为容器产品创建产品 ID 和产品代码](container-product-getting-started.md#create-initial-container-product)。

**Topics**
+ [第 1 步：添加存储库](#add-repositories)
+ [第 2 步：将容器映像和构件上传到存储库](#upload-resources)
+ [第 3 步：向容器产品添加新版本](#add-new-version)
+ [第 4 步：更新版本信息](#container-product-updating-version)
+ [限制 Amazon EKS 附加组件的版本](#restriciting-version-eks-addon)

## 第 1 步：添加存储库
<a name="add-repositories"></a>

您的产品容器映像和其他构件存储在 AWS Marketplace中的存储库中。通常，您可以为所需的每个构件创建一个存储库，但该存储库可以存储该构件的多个版本（使用不同的标签）。

**注意**  
产品部署中的所有映像都必须使用 AWS Marketplace 存储库中的映像。

以下过程介绍如何在中添加任何所需的存储库 AWS Marketplace。

**要添加存储库，请执行以下操作：**

1. 登录到 [AWS Marketplace 管理门户](https://aws.amazon.com/marketplace/management/)。

1. 从**产品**菜单中选择**服务器**。

1. 在**服务器产品**选项卡上，选择要修改的产品，然后从**请求更改**下拉列表中选择**添加存储库**。

1. 输入您要创建的存储库的名称。如果要创建多个新的存储库：
   + 针对每个新增存储库，选择**添加新存储库**。
   + 为其指定唯一名称。您所选择的唯一名称必须在您卖家账户的所有产品中保持唯一性。
**注意**  
存储库将具有以下结构：`<repositoryID>.dkr.ecr.us-east-1.amazonaws.com/<sellerName>/<repositoryName>`. 当您将项目添加到存储库时（在以下过程中），它们将获得一个标签并具有以下结构：`<repositoryID>.dkr.ecr.us-east-1.amazonaws.com/<sellerName>/<repositoryName>:<tag>`。  
`repositoryID`是的内部 ID AWS Marketplace。
`sellerName` 是基于您为卖家账户创建的名称。当您的卖家显示名称生成无效`sellerName`的存储库前缀时，AWS Marketplace 会自动用 UUID（通用唯一标识符）代替卖家名称。要更改存储库名称中的 UUID 前缀，请联系 AWS Marketplace 运营团队。
`respositoryName`是在此步骤中定义的。
`tag` 是在将构件上传到存储库时设置的。

1. 选择**提交**。

**注意**  
每个产品最多可以有 70 个存储库。

新请求已创建并显示在**请求**选项卡上。完成后，您可以在几分钟之内开始向已创建的存储库中添加容器映像和其他构件。

## 第 2 步：将容器映像和构件上传到存储库
<a name="upload-resources"></a>

**要将容器映像和构件上传到存储库，请执行以下操作：**

1. 登录到 [AWS Marketplace 管理门户](https://aws.amazon.com/marketplace/management/)。

1. 从**产品**菜单中选择**服务器**。

1. 在**服务器产品**选项卡上，选择要修改的产品。

1. 从**请求更改**下拉列表中，选择**添加存储库**。

1. 选择**查看现有存储库**。

1. 选择要上传到的存储库。

1. 选择**查看推送命令**以打开指令列表，其中包括可用于将 Docker 容器映像和 Helm 图表推送到该存储库的命令。

   有关如何将容器映像和其他构件推送到存储库的一般信息，请参阅《Amazon Elastic Container Registry 用户指南》**中的[推送映像](https://docs.aws.amazon.com/AmazonECR/latest/userguide/image-push.html)。
**注意**  
调用 `docker pull` 或 `docker push` 时，您可以使用以下Amazon Elastic Container Registry (Amazon ECR) API 操作：  
`DescribeImages` – 用于查看存储库中有关映像的元数据。
`GetAuthorizationToken` – 用于在将构件上传到存储库之前进行身份验证，然后使用 `docker pull` 或 `docker push` 命令。
`ListImages` – 用于查看您推送的映像列表。

1. 使用列出的命令将任何需要的工件从本地存储库推送到产品的 AWS Marketplace 存储库。
**注意**  
您在 `push` 命令中提供的 **tag** 用于区分要上传到存储库的构件的版本。使用对构件所属版本有意义的标签。

1. 对版本中需要的每个容器映像或构件重复此操作。
**注意**  
您的版本在每个交付选项中最多可以包含 50 个容器映像或构件。有关交付选项的更多信息，请参阅以下过程。

上传构件后，您就可以创建产品版本了。

**注意**  
系统会自动扫描您的容器映像，以查看它们是否符合[基于容器的产品要求 AWS Marketplace](container-product-policies.md)。有关更多信息，请参阅 [容器产品扫描是否存在安全问题](container-product-getting-started.md#container-security)。

### 添加新的交付选项
<a name="add-delivery-option"></a>

容器产品的每个版本都需要一个交付选项。交付选项指定买家可使用的部署选项。根据以下交付选项之一，您需要将相应的构件上传到存储库。
+ 对于**容器镜像**传送选项，请将安装产品所需的所有容器镜像上传到控制台中创建的亚马逊弹性容器注册表 (Amazon ECR) Container Registry 存储库。 AWS Marketplace 
+ 对于**Helm图表**交付选项，请将Helm图表和容器图像上传到 AWS Marketplace 控制台中创建的 Amazon ECR 存储库中。
+ 对于 A **mazon EKS 控制台附加组件**交付选项，请将Helm图表和容器图像上传到在 AWS Marketplace 控制台中创建的 Amazon ECR 存储库中。

## 第 3 步：向容器产品添加新版本
<a name="add-new-version"></a>

**注意**  
如果您在向容器添加新版本时收到任何错误，请参阅《AWS Marketplace Catalog API 参考》**中的[添加新版本异步错误表](https://docs.aws.amazon.com/marketplace-catalog/latest/api-reference/container-products.html#container-add-version)。

**要向容器产品添加新版本，请执行以下操作：**

1. 登录到 [AWS Marketplace 管理门户](https://aws.amazon.com/marketplace/management/)。

1. 从**产品**菜单中选择**服务器**。

1. 在**服务器产品**选项卡上，选择要向其添加版本的产品。然后，从**请求更改**下拉列表中选择**添加新版本**。

1. 在**添加新版本**页面上，输入**版本标题**和**发布说明**。

1. 输入版本详细信息后，下一步是添加交付选项。交付选项是一组说明和信息，买家可以使用这些说明和信息从您的产品版本启动软件。交付选项被称为买家的*交付选项*。
**注意**  
您的产品可以支持具有不同容器映像的多个平台（例如 Kubernetes 和 Ubuntu 部署）。您可以为客户设置产品的每种方式创建一个交付选项，每个版本的产品最多可创建四个交付选项。

   1. 如果产品在其他版本中已有交付选项，则可以使用现有选项作为模板，向新版本添加交付选项。在**交付选项**中，从列表中选择要添加的交付选项。您可以按照以下步骤中的说明编辑该选项。

   1. 要添加新的交付选项，请选择**新交付选项**。添加选项后，按照以下步骤中的说明，对其进行配置。

1. 为交付选项选择一种交付方式。交付方式决定了买家将如何启动您的软件。
   + 对于**容器映像**交付选项，请提供在 AWS Marketplace 控制台中创建的 Amazon Elastic Container Registry (Amazon ECR) 存储库中的容器映像的路径。买家使用容器映像路径通过将映像直接拉入其环境来启动软件。
   + 对于 **Helm 图表**交付选项，请提供在 AWS Marketplace 控制台中创建的 Amazon ECR 存储库中 Helm 图表的路径。买家在其部署环境中安装 Helm 图表以启动软件。
   + 对于 **Amazon EKS 控制台附加组件**交付选项，请提供在 AWS Marketplace 控制台中创建的 Amazon ECR 存储库中 Helm 图表的路径。买家使用亚马逊 EKS 控制台或原生 Amazon EKS 附加组件安装容器 APIs 来启动软件。有关更多信息，请参阅[来自 Amazon EKS 的可用 Amazon EKS 附加组件](https://docs.aws.amazon.com/eks/latest/userguide/eks-add-ons.html#workloads-add-ons-available-eks)。

   1. 要添加**容器映像**交付选项，请执行以下步骤：

      1. 在**容器映像**中，将 Amazon ECR URL 添加到包含产品版本软件的容器映像中。

      1. 在**交付选项标题**和**部署选项描述**中，输入此交付选项的标题和描述。

      1. 在**使用说明**中，输入详细信息，以帮助买家在启动软件后使用该软件。

      1. 在**支持的服务**中，选择买家可以在其中启动软件的环境。

      1. 在**部署模板**中，添加买家可以用来启动软件的资源。输入每个模板的标题和资源 URL。

   1. 要添加 **Helm 图表**交付选项，请执行以下步骤：

      1. 在 **Helm 图表**中，将 Amazon ECR URL 添加到 Helm 图表中，买家将在部署环境中安装该网址以启动您的软件。

      1. 在**容器映像**中，将 Amazon ECR URL 添加到包含产品版本软件的容器映像中。

      1. 在**交付选项标题**和**部署选项描述**中，输入此交付选项的标题和描述。

      1. 在**使用说明**中，输入详细信息，以帮助买家在启动软件后使用该软件。

      1. 在**支持的服务**中，选择买家可以在其中启动软件的环境。

      1. *可选 - * 在 **Helm 版本名称**中，输入将在其中安装 Helm 图表的 Kubernetes 命名空间的名称。

      1. *可选 - * 在 **Helm 安装命名空间**中，输入 `helm install` 命令将使用的 Helm 版本的名称。

      1. *可选 - * 在 **Kubernetes 服务账户名称**中，输入将用于连接 AWS Identity and Access Management （IAM）的 Kubernetes 服务账户的名称。Kubernetes 服务账户调用许可或计量等 AWS 服务。

      1. 在**覆盖参数**中，输入将在启动软件的 Helm CLI 命令中使用的参数。买家可使用这些参数覆盖提供的默认值。使用 AWS Marketplace 管理控制台时，参数限制为 15 个，但使用时没有限制 AWS Marketplace Catalog API。有关更多信息，请参阅[向基于容器的产品添加新版本](https://docs.aws.amazon.com/marketplace-catalog/latest/api-reference/container-products.html#container-add-version)。
**注意**  
一些**覆盖参数**是必需的。Amazon EKS Anywhere 产品要求为 `"${AWSMP_LICENSE_SECRET}"` 设置为 `DefaultValue` 的许可证密钥**覆盖参数**。对于付费产品，您必须为 `"${AWSMP_SERVICE_ACCOUNT}"` 设置为 `DefaultValue` 的服务账号配置**覆盖参数**。

      1. 选择 “**隐藏密码和机密**” 以屏蔽控制台、命令行工具和中的敏感信息 APIs。有关更多信息，请参阅《AWS CloudFormation 用户指南》**中[参数](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/parameters-section-structure.html)中的 `NoEcho` 参数文档。

   1. 要添加 **Amazon EKS 控制台附加组件**交付选项，请确保该构件符合 [Amazon EKS 附加组件产品的要求](container-product-policies.md#publishing-eks-add-on)，然后执行以下步骤：
**注意**  
每个版本仅支持一个 Amazon EKS 附加组件交付选项。在您正在处理的当前版本发布到 Amazon EKS 控制台之前，您无法添加新版本。

      1. 在 **Helm 图表**中，将 Amazon ECR URL 添加到 Helm 图表中，买家将在部署环境中安装该网址以启动您的软件。

      1. 在**容器映像**中，将 Amazon ECR URL 添加到包含产品版本软件的容器映像中。确保列出了 Helm 图表内的所有映像。

      1. 在**交付选项标题**和**部署选项描述**中，输入此交付选项的标题和描述。

      1. 在**可见性**中，保持默认值**有限**选项。

      1. 在**附加组件名称**中，输入此附加组件的唯一名称。您输入的附加组件名称在 Amazon EKS 控制台中显示时将附加上卖家的名称。

      1. 在**附加组件版本**中，输入安装或升级此附加组件时将显示的附加组件版本。请遵循格式 `major.minor.patch`。

      1. 在**附加组件类型**中，从下拉列表中为您的附加组件选择一个类别。

      1. 在**Kubernetes 版本**中，选择您的附加组件将支持的所有 Kubernetes 版本。

      1. 在**架构**中，选择您的附加组件支持的平台架构。选项为 **AMD64** 和 **ARM64**。我们建议同时支持这两种架构，以最大限度地提高兼容性。如果您的附加组件不支持 ARM64 设备，则必须指定添加支持的计划日期，然后才能在所有广告中发布您的产品 AWS 区域。

      1. 在**命名空间**中，输入要在其中安装附加组件的唯一 Kubernetes 命名空间。安装第三方附加组件时不支持 `default`、`kube-system` 和 `kube-public` 命名空间。

      1. 在**环境覆盖参数**中，您最多可以从 Amazon EKS 附加组件框架中选择 2 个环境参数。您可以将参数名称从 values.yaml 映射到这些环境变量，即 `${AWS_REGION}` 和 `${AWS_EKS_CLUSTER_NAME}`。

1. 要添加其他交付选项，请选择**新交付选项**，然后重复前面的步骤进行配置。

1. 选择**提交**。

## 第 4 步：更新版本信息
<a name="container-product-updating-version"></a>

创建版本后，可通过修改与版本关联的信息，向买家提供更新的信息。例如，如果您计划在版本 1.1 发布后限制版本 1.0，则可以更新版本 1.0 的描述以引导买家使用版本 1.1。提供版本 1.0 的受限日期。您可以从 AWS Marketplace 管理门户中更新版本信息。

**要更新版本信息，请执行以下操作：**

1. 登录到 [AWS Marketplace 管理门户](https://aws.amazon.com/marketplace/management/)。

1. 从**产品**菜单中选择**服务器**。

1. 在**服务器产品**选项卡上，选择要修改的产品。

1. 从**请求更改**下拉列表中，选择**更新版本信息**。

1. 在**更新版本**页面上，选择要更新的版本。

1. 对所选版本进行更新。可供更新的字段取决于产品版本或交付选项的状态。

   1. 对于所有版本，您可以更新**发布说明**。

   1. 对于尚未公开发布的版本，您可以更新**版本标题**。

   1. 对于未受限制的交付选项，您可以更新以下字段：
      + **描述**
      + **使用说明**
      + **支持的服务**

   1. 对于尚未公开发布的版本中的交付选项，您可以更新以下字段：
      + **交付选项标题**
      + **Helm 图表**（仅适用于 **Helm 图表**交付选项）
      + **容器映像**
      + **部署资源**
      + **AddOn 名称**
      + **AddOn 版本**
      + **AddOn Type**
      + **Helm 图表 URI**
      + **CompatibleKubernetesVersions**
      + **SupportedArchitectures**
      + **命名空间**
      + **EnvironmentOverrideParameters**

   1. 对于公开版本中的配送选项，您可以更新**SupportedArchitectures**。

1. 选择**提交**。

1. 验证请求是否显示在**请求**选项卡上，状态为**正在审核**。

您可以随时从[服务器产品](https://aws.amazon.com/marketplace/management/products/server)页面的**请求**选项卡中查看请求的状态。

## 限制 Amazon EKS 附加组件的版本
<a name="restriciting-version-eks-addon"></a>

要限制作为附加组件发布的容器产品版本，请使用 [AWS Marketplace 管理门户](https://aws.amazon.com/marketplace/management/) 底部的“联系我们”表单联系 AWS Marketplace 运营团队。

# 管理集装箱产品定价 AWS Marketplace
<a name="container-pricing"></a>

作为 AWS Marketplace 卖家，您可以管理和更新集装箱产品定价。对于容器产品，您可以为 Amazon Elastic Container Service（Amazon ECS）、Amazon Elastic Kubernetes Service（Amazon EKS）和 AWS Fargate上架免费产品、自带许可（BYOL）产品和付费产品。您只能为每个产品设置一个价格。您可以添加或更新定价维度。根据定价模式，您可以添加合同和用量维度。您还可以更新定价条款或退款政策。有关更多信息，请参阅以下部分。

 有关容器产品定价模型的更多信息 AWS Marketplace，请参阅[的容器产品定价 AWS Marketplace](pricing-container-products.md)。

**Topics**
+ [添加定价维度](#container-add-pricing-dimensions)
+ [更新维度信息](#container-update-dimensions-information)
+ [更新定价条款](#container-update-pricing-terms)
+ [更新产品的退款政策](#container-update-refund-policy)

## 添加定价维度
<a name="container-add-pricing-dimensions"></a>

您可以向用于产品计费的定价模式中添加定价维度。有关定价模式的更多信息，请参阅[容器定价模式](pricing-container-products.md#pricing-models-for-server-products)。

**注意**  
上不提供为包含消费定价的合同添加定 pay-as-you-go价维度（例如，额外用量的定价） AWS Marketplace 管理门户。  
您不能在合同、用量和带消费定价的合同之间更改定价模式。联系 [AWS Marketplace 卖家运营](https://aws.amazon.com/marketplace/management/contact-us/)团队获取帮助。

1. 打开 AWS Marketplace 管理门户 at [https://aws.amazon.com/marketplace/管理/tour/](https://aws.amazon.com/marketplace/management/tour/) 并登录您的卖家账户。

1. 在[https://aws.amazon.com/marketplace/management/products/server](https://aws.amazon.com/marketplace/management/products/server)页面上，选择要修改的容器产品。

1. 从**请求更改**下拉列表中，选择**更新定价维度**，然后选择**添加定价维度**。

1. 根据定价模式，您可以通过提供 **API 标识符**、**显示名称**和**描述**信息来添加合同维度或使用维度。

1. 选择**下一步**，然后输入您的合同维度定价。

1. 选择**提交**，提交您的请求以供审核。

1. 确认**请求**选项卡将**请求状态**显示为**正在审核**。请求完成后，状态将更新为**成功**或**失败**。

## 更新维度信息
<a name="container-update-dimensions-information"></a>

您可以更改产品的维度信息。有关定价模式的更多信息，请参阅[容器定价模式](pricing-container-products.md#pricing-models-for-server-products)。

1. 打开 AWS Marketplace 管理门户 at [https://aws.amazon.com/marketplace/管理/tour/](https://aws.amazon.com/marketplace/management/tour/) 并登录您的卖家账户。

1. 在[https://aws.amazon.com/marketplace/management/products/server](https://aws.amazon.com/marketplace/management/products/server)页面上，选择要修改的容器产品。

1. 从**请求更改**下拉列表中，选择**更新定价维度**，然后选择**更新维度信息**。

1. 您可以通过为要更新的维度的**显示名称**和**描述**提供信息来添加维度信息。

1. 选择**提交**，提交您的请求以供审核。

1. 确认**请求**选项卡将**请求状态**显示为**正在审核**。请求完成后，状态将更新为**成功**或**失败**。

## 更新定价条款
<a name="container-update-pricing-terms"></a>

您可以更改产品的定价条款。如果您更改销售产品的国家/地区，则可能需要更新定价条款。

1. 打开 AWS Marketplace 管理门户 at [https://aws.amazon.com/marketplace/管理/tour/](https://aws.amazon.com/marketplace/management/tour/) 并登录您的卖家账户。

1. 在[https://aws.amazon.com/marketplace/management/products/server](https://aws.amazon.com/marketplace/management/products/server)页面上，选择要修改的容器产品。

1. 从**请求更改**列表中，选择**更新公开优惠**，然后选择**更新定价条款**。

1. 当前定价已预先填充供您编辑。您可以删除当前定价并添加新价格。我们建议您在提交请求以供审核之前，先检查您请求的价格。

1. 选择**提交**，提交您的请求以供审核。

1. 确认**请求**选项卡将**请求状态**显示为**正在审核**。请求完成后，状态将更新为**成功**或**失败**。

**注意**  
如果您使用**更新定价条款**提高某个维度的价格，则在 90 天内将无法更新定价。从您开始变更之日起，价格提高将锁定 90 天。这种价格锁定仅对价格提高而非价格降低有效。

## 更新产品的退款政策
<a name="container-update-refund-policy"></a>

您可以更新产品的退款政策。退款政策的更新对所有用户生效。有关更多信息，请参阅 [退款和取消订单 AWS Marketplace](refunds.md)。

1. 打开 AWS Marketplace 管理门户 at [https://aws.amazon.com/marketplace/管理/tour/](https://aws.amazon.com/marketplace/management/tour/) 并登录您的卖家账户。

1. 在[https://aws.amazon.com/marketplace/management/products/server](https://aws.amazon.com/marketplace/management/products/server)页面上，选择要修改的容器产品。

1. 从**请求更改**下拉列表中，选择**更新公开优惠**，然后选择**更新退款政策**。

1. 当前的退款政策已预先填充供您编辑。您可以删除当前的退款政策并添加新的退款政策。我们建议您在提交请求以供审核之前，先检查您请求的退款政策。提交请求会改写当前的退款政策。

1. 选择**提交**，提交您的请求以供审核。

1. 确认**请求**选项卡将**请求状态**显示为**正在审核**。请求完成后，状态将更新为**成功**或**失败**。

# 按国家/地区更新集装箱产品的供应情况 AWS Marketplace
<a name="container-update-offer-avail-country"></a>

作为 AWS Marketplace 卖家，您可以更改集装箱产品销售和订阅的国家/地区。有关更多信息，请参阅 [国家/地区](regions-and-countries.md#product-countries)。以下过程介绍如何按国家/地区更新您的容器产品可用性。

1. 打开 AWS Marketplace 管理门户 at [https://aws.amazon.com/marketplace/管理/tour/](https://aws.amazon.com/marketplace/management/tour/) 并登录您的卖家账户。

1. 在[https://aws.amazon.com/marketplace/management/products/server](https://aws.amazon.com/marketplace/management/products/server)页面上，选择要修改的容器产品。

1. 从**请求更改**下拉列表中，选择**更新公开优惠**，然后选择**按国家/地区更新可用性**。

1. 选择下列选项之一：
   + **所有国家/地区** – 适用于所有受支持的国家/地区。
   + **所有国家/地区（部分除外）** – 在所有受支持的国家/地区均可用，部分国家/地区除外。
   + **仅允许列表中的国家/地区** – 买家只能在您指定的国家/地区购买。

1. 选择**提交**，提交您的请求以供审核。

1. 确认**请求**选项卡将**请求状态**显示为**正在审核**。请求完成后，状态将更新为**成功**或**失败**。

# 在 AWS Marketplace 上更新容器产品的最终用户许可协议（EULA）
<a name="container-update-eula"></a>

作为 AWS Marketplace 卖家，您可以更新最终用户许可协议（EULA）以使用 [AWS Marketplace 标准合同](standardized-license-terms.md#standard-contracts)或自定义 EULA。对 EULA 所做的更新将对订阅您的产品和产品续订的新用户生效。要更新 EULA，请使用以下过程。

1. 通过 [https://aws.amazon.com/marketplace/management/tour/](https://aws.amazon.com/marketplace/management/tour/) 打开 AWS Marketplace 管理门户 并登录您的卖家账户。

1. 在[https://aws.amazon.com/marketplace/management/products/server](https://aws.amazon.com/marketplace/management/products/server)页面上，选择要修改的容器产品。

1. 从**请求更改**下拉列表中，选择**更新公开优惠**，然后选择**更新 EULA**。

1. 选择 **AWS Marketplace 的标准合同**或提交您的自定义 EULA。对于自定义 EULA，必须从 Amazon S3 桶提供合同。

1. 选择**提交**，提交您的请求以供审核。

1. 确认**请求**选项卡将**请求状态**显示为**正在审核**。请求完成后，状态将更新为**成功**或**失败**。

# 更新基于容器的产品的退款政策 AWS Marketplace
<a name="update-container-refund-policy"></a>

作为 AWS Marketplace 卖家，您可以为基于容器的商品设置退款政策。如果您想更改产品的退款政策，可以使用**更新退款政策**。以下流程介绍了如何更新退款政策。

**要更新退款政策，请执行以下操作：**

1. 打开 AWS Marketplace 管理门户 at[https://aws.amazon.com/marketplace/management/tour/](https://aws.amazon.com/marketplace/management/tour/)，然后登录您的卖家账户。

1. 前往[服务器产品](https://aws.amazon.com/marketplace/management/products/server)页面，然后选择要修改的产品。

1. 从**请求更改**下拉列表中，选择**更新公开报价**，然后选择**更新退款政策**。

1. 文本框中提供了当前退款政策的详细信息供您编辑。提交请求会改写当前的退款政策。

1. 选择**提交**，提交您的请求以供审核。

1. 确认**请求**选项卡将**请求状态**显示为**正在审核**。请求完成后，状态变为**成功**。

# 在上测试和发布您的容器产品 AWS Marketplace
<a name="test-release-product"></a>

在向中添加容器产品的新版本后 AWS Marketplace，您可以测试您的产品，然后将其发布给公众。本主题概述了测试并向公众发布产品所需执行的具体步骤和流程。

**Topics**
+ [容器映像和 Helm 图表交付选项](#container-helm-delivery)
+ [Amazon EKS 附加组件交付选项](#eks-addon-delivery)

## 容器映像和 Helm 图表交付选项
<a name="container-helm-delivery"></a>

本节就发布容器映像和 Helm 图表提供指导。

您对新版本的请求已创建并应在几分钟内完成。您可以从**服务器产品**页面的**请求**选项卡中跟踪请求。如果您在测试或发布附加组件时遇到任何错误，请参阅《AWS Marketplace Catalog API 参考》**中[添加新版本](https://docs.aws.amazon.com/marketplace-catalog/latest/api-reference/container-products.html#container-add-version)部分的“异步错误”表。

**注意**  
如果您的产品当前设置为限量供应，则只有可购买该产品的买家才能访问该产品版本。如果您的产品目前已设置为公开发售，则所有 AWS Marketplace 买家都可以访问该产品版本。

如果这是您的第一个版本集，那么您的产品现在可以发布了。

## Amazon EKS 附加组件交付选项
<a name="eks-addon-delivery"></a>

本节就测试和发布 Amazon EKS 附加组件提供指导。

**测试您的附加组件**
+ 提交附加组件后， AWS Marketplace 处理您的请求并在有限状态下发布您的附加组件，以便您在 Amazon EKS 附加组件目录中进行验证。您可以从 AWS Marketplace 管理门户中**服务器产品**页面的**请求**选项卡中跟踪请求。提取时间将从 5-10 个工作日不等，具体取决于我们正在处理的请求量。

  当您的请求处于 **“正在审核**” 状态时， AWS 团队仍在将该插件发布 AWS Marketplace 到 Amazon EKS 附加组件目录中。一旦附加组件发布并处于**受限**状态后，请求状态将更改为**成功**。之后您便可以开始测试附加组件。
+ 您的附加组件上市后，您可以在亚太地区（首尔）地区找到它进行测试。 AWS Marketplace 依靠您的专业知识来验证软件的功能。要测试您的附加组件，您必须在卖家账户中创建一个位于亚太地区（首尔）区域的 Amazon EKS 集群，并确保您的附加组件在该区域列入允许列表。要测试您的附加组件，请按照[此链接中的详细说明](https://aws.amazon.com/blogs/awsmarketplace/deploy-third-party-software-add-ons-aws-marketplace-amazon-eks-clusters/)操作。请务必在您的软件支持的每个 Kubernetes 版本上进行测试。
+ 如果您提供付费产品，请为以下内部 AWS 账户创建专属优惠。这些账户有助于将您的软件集成到所有商业 AWS 区域的 Amazon EKS 控制台中。

  ```
  288092140294, 288092140294, 408202761791
  ```
+ 在您的插件版本获得 AWS Marketplace 批准并将您的插件版本移至公开之前，请保持您的测试集群处于活动状态。
**注意**  
AWS Marketplace 将不承担在 Amazon EKS 集群上测试您的容器产品期间产生的 AWS 基础设施成本。在我们验证测试结果的同时，您可以按照合适的规模调整机制，将节点缩减至最低运营成本。

**向公众发布您的附加组件**

在您通过 Amazon EKS 集群将软件作为附加组件进行验证后，您可以使用 [AWS Marketplace 管理门户](https://aws.amazon.com/marketplace/management/) 或 AWS Marketplace Catalog API提交请求，以向公众发布您的 Amazon EKS 附加组件版本。

 有关更多信息，请参阅《AWS Marketplace Catalog API 参考》**中的[更新 Amazon EKS 附加组件的可用性](https://docs.aws.amazon.com/marketplace-catalog/latest/api-reference/container-products.html#update-delivery-option-visibility)。

您可以从 AWS Marketplace 管理门户中**服务器产品**页面的**请求**选项卡中跟踪请求。提取时间会有所不同。

# 基于容器的产品要求 AWS Marketplace
<a name="container-product-policies"></a>

AWS Marketplace 对所有基于容器的产品和产品保持以下要求。 AWS Marketplace这些要求有助于为我们的客户推广安全、可靠和值得信赖的目录。我们还鼓励卖家审查其他控制措施和协议的实施情况，以满足其特定产品的需求。

所有产品及其相关元数据在提交时都要经过审核，以确保它们符合或超过现行 AWS Marketplace 政策。这些政策会定期更新，以适应不断变化的安全准则。 AWS Marketplace 持续扫描商品，以验证现有商品是否继续满足这些要求的任何更改。如果商品不合规， AWS Marketplace 将联系卖家更新其产品以符合新标准。在某些情况下，产品可能暂时无法向新订阅用户提供，直到问题得到解决。此过程有助于为所有用户维护 AWS Marketplace 平台的安全性和可信度。

**Topics**
+ [安全策略](#container-security-requirements)
+ [客户信息要求](#container-customer-info-requirements)
+ [产品使用要求](#container-usage-requirements)
+ [架构要求](#container-architecture-requirements)
+ [Helm 图表结构要求](#helm-chart-structure-requirements)
+ [容器产品使用说明](#container-product-usage-instructions)
+ [Amazon EKS 附加组件产品的要求](#publishing-eks-add-on)

## 安全策略
<a name="container-security-requirements"></a>

 所有基于容器的产品必须满足以下安全要求：
+ 容器镜像不得包含任何已知漏洞、恶意软件或 End-of-Life (EoL) 软件包和操作系统。
+ 容器不得请求 AWS 凭证来访问 AWS 服务。当您的产品需要访问 AWS 服务时，您必须使用以下服务之一：
  + 对于 Amazon Elastic Kubernetes Service（Amazon EKS）工作负载，使用适用于服务账户的 IAM 角色。
  + 对于 Amazon Elastic Container Service（Amazon ECS）工作负载，使用适用于任务的 IAM 角色。
+ 基于容器的产品只需要最低权限即可运行。有关更多信息，请参阅 [Amazon Elastic Container Service 中的安全](https://docs.aws.amazon.com//AmazonECS/latest/developerguide/security.html)和 [Amazon EKS 中的安全性](https://docs.aws.amazon.com//eks/latest/userguide/security.html)。
+ 默认情况下，应将容器映像配置为以非根权限运行。
+ 容器中不得包含任何硬编码的机密，例如系统用户及服务的密码（即使经过哈希处理）、私钥、凭证等。
+ 在容器内运行的任何服务中，身份验证不得使用基于密码的方式，即使该密码由用户在启动时生成、重置或定义。同时，也不允许使用空密码和空白密码。
+ 容器映像不得包含具有不受支持架构的层（例如，in-toto Attestation Framework 元数据）。

## 客户信息要求
<a name="container-customer-info-requirements"></a>

 所有基于容器的产品必须满足以下客户信息要求：
+ 除非 BYOL（自带许可）另行要求，否则软件不得在客户不知晓且未明确同意的情况下收集或导出客户数据。收集或导出客户数据的应用程序必须遵循以下准则：
  + 客户数据收集必须是自助服务、自动化和安全的。买家无需等待卖家批准即可部署软件。
  + 客户数据的收集必须符合您与之达成的协议 AWS，包括但不限于 [AWS Marketplace 条款和条件](https://aws.amazon.com/legal/seller-terms/)、[AWS 服务条款](https://aws.amazon.com/service-terms/)、[AWS 隐私声明](https://aws.amazon.com/privacy/)和[AWS 客户协议](https://aws.amazon.com/agreement/)。
  + 不得收集付款信息。

## 产品使用要求
<a name="container-usage-requirements"></a>

 所有基于容器的产品必须满足以下产品使用要求：
+ 卖家只能发布功能齐全的产品。不允许使用用于试用或评估目的的测试版或预发行产品。如果卖家在提供免费版本后的 90 天 AWS Marketplace 内提供同等的付费版本，则支持商业软件的开发者、社区和 BYOL 版本。
+ 基于容器的产品的所有使用说明都必须包括部署基于容器的产品的所有步骤。使用说明必须提供指向 AWS Marketplace上相应容器映像的命令和部署资源。
+ 基于容器的产品必须包含订阅用户使用该软件所需的所有容器映像。此外，基于容器的产品不得要求用户使用外部的任何映像 AWS Marketplace （例如，来自第三方存储库的容器映像）启动产品。
+ 容器及其软件必须可以自助方式部署，并且不得要求额外的付款方式或费用。部署时需要外部依赖的应用程序必须遵循以下准则：
  + 必须在列表的描述或使用说明中披露该要求。例如，*此产品需要互联网连接才能正确部署。部署时会下载以下软件包：<list of package>。*
  + 卖家需对所有外部依赖的使用负责，并确保其可用性和安全性。
  + 如果外部依赖关系不再可用，则还必须从中 AWS Marketplace 删除该产品。
  + 外部依赖项不得要求额外的付款方式或费用。
+ 需要持续连接不受买方直接控制的外部资源（例如，外部资源或由卖方 APIs 或第三方 AWS 服务 管理的容器）必须遵循以下指南：
  + 必须在列表的描述或使用说明中披露该要求。例如，*此产品需要持续的互联网连接。需要以下持续的外部服务才能正常运行：<list of resources>。*
  + 卖家需对所有外部资源的使用负责，并确保其可用性和安全性。
  + 如果外部资源不再可用，则还必须从中 AWS Marketplace 移除该产品。
  + 外部资源不得要求额外的付款方式或费用，并且必须自动设置连接。
+ 产品软件和元数据不得包含将用户重定向到 AWS Marketplace中未提供的其他云平台、其他产品或追加销售服务的语言。
+ 如果您的产品是其他产品或其他 ISV 产品的附加组件，则您的产品描述必须表明它扩展了其他产品的功能，如果没有它，产品的应用将受到限制。例如，*本产品扩展了 <product name> 的功能，如果没有它，则该产品的应用将受到限制。请注意，<product name> 可能需要自己的许可才能使用此列表的全部功能。*

## 架构要求
<a name="container-architecture-requirements"></a>

 所有基于容器的产品必须满足以下架构要求：
+ 的源容器镜像 AWS Marketplace 必须推送到所拥有的亚马逊弹性容器注册表 (Amazon ECR) Container Registry 存储库。 AWS Marketplace您可以在 AWS Marketplace 管理门户 中的服务器产品下为每个容器产品列表创建这些存储库。
+ 容器映像必须基于 Linux。
+ 付费版基于容器的产品必须能够部署在 [Amazon ECS](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/Welcome.html)、[Amazon EKS](https://docs.aws.amazon.com/eks/latest/userguide/what-is-eks.html) 或 [AWS Fargate](https://docs.aws.amazon.com/AmazonECS/latest/userguide/what-is-fargate.html) 中。
+ 具有合同定价和集成的基于容器的付费产品 AWS License Manager 应部署在亚马逊 EKS、Amazon ECS、Amazon EKS Anywhere AWS Fargate、Amazon ECS Anywhere、Amazon ECS Anywhere、红帽 OpenShift 服务 AWS (ROSA)、本地自行管理的 Kubernetes 集群或亚马逊弹性计算云上。
+ 对于 Helm 图表类产品，容器映像引用必须按照 [Helm 图表结构要求](#helm-chart-structure-requirements)进行构建，以支持跨区域部署。
+ 如果您的基于容器的产品要求买家部署亚马逊系统映像 (AMI)，则该映像必须是 AWS托管的 AMI 或中发布的单独的 AMI。 AWS Marketplace如果您在中发布自己的 AMI AWS Marketplace，则它必须符合，[基于 AMI 的产品要求 AWS Marketplace](product-and-ami-policies.md)并且必须按照中的要求表明它是附加产品[产品使用策略](product-and-ami-policies.md#product-usage)。您可以将基于 AMI 的产品定价为 BYOL，因为它是基于容器的产品的扩展。 AWS Marketplace 扫描基于 AMI 的产品中是否存在未修补的常见漏洞和漏洞 (CVEs) 以及安全要求。您的买家还必须先订阅您的基于 AMI 的产品，然后再进行部署。

## Helm 图表结构要求
<a name="helm-chart-structure-requirements"></a>

提交给的所有Helm chart产品都 AWS Marketplace 必须遵守以下结构要求，以确保适当的区域化和跨 AWS 区域部署：
+ 容器映像引用必须仅在 `values.yaml` 文件中定义，不得在 Helm 图表内的其他任何文件中硬编码。这样可以在将产品复制 AWS Marketplace 到不同地区时自动替换这些参考文献。
+ 该`values.yaml`文件必须为所有容器镜像引用使用变量。
+ 或者，您可以拆分`tag`为`registry`与存储库相同级别的单独字段，以建立您的图像引用。
+ Helm 模板必须使用标准 Helm 模板语法（例如 `{{ .Values.image.repository }}:{{ .Values.image.tag }}`）来引用这些变量。
+ 避免在模板中使用会绕过 `values.yaml` 中定义的映像引用的条件逻辑。
+ 在使用不同 AWS 区域测试 Helm 图表时，请确保`values.yaml`正确更改区域会更新已部署资源中的所有图像引用。

AWS Marketplace 验证在产品提交过程中，`values.yaml`文件中是否正确定义了所有容器图片引用。不符合这些要求的产品将被拒绝。

### Helm 图表中对容器镜像参考的要求
<a name="helm-chart-best-practices"></a>

以下内容演示了在 Helm 图表中构造容器镜像引用的方法：

**`values.yaml`（建议的格式）：**

```
image:
  registry: "709825985650.dkr.ecr.us-east-1.amazonaws.com"
  repository: "accuknox/kubearmor"
  tag: "v1.1.1"
```

**注意**  
我们建议您在 `values.yaml` 文件结构中采用上述方法，但以下替代方案同样有效。

**`values.yaml`（替代格式）：**

```
image:
  repository: "709825985650.dkr.ecr.us-east-1.amazonaws.com/guance/datakit"
  tag: "1.0"
```

**`values.yaml`（替代格式）：**

```
image:
  repository: "709825985650.dkr.ecr.us-east-1.amazonaws.com/guance/datakit:1.0"
```

**注意**  
对于部署模板，以下格式是唯一可用的有效格式。

**部署模板：**

```
containers:
- name: kubearmor
  image: "{{ .Values.image.registry }}/{{ .Values.image.repository }}:{{ .Values.image.tag }}"
```

**错误方式（请勿使用）：**

```
containers:
- name: kubearmor
  image: "709825985650.dkr.ecr.us-east-1.amazonaws.com/accuknox/kubearmor:v1.1.1"
```

### 可能的 Helm 图表验证错误
<a name="helm-chart-validation-errors"></a>

在产品提交过程中， AWS Marketplace 对 Helm chart 产品进行验证检查，以确保符合容器图像参考要求。如果您的 Helm 图表不符合这些要求，则可能会遇到以下验证错误：


| 错误 | 说明 | 
| --- | --- | 
| INCOMPATIBLE\$1HELM\$1OBJECTS | EKS 插件不支持指定的 Helm 对象。请参阅[Amazon EKS 附加组件产品的要求](#publishing-eks-add-on)。 | 
| INVALID\$1DEPENDENT\$1HELM\$1CHARTS | 依赖的 Helm 图表必须包含在父图表目录中，而不是外部来源。 | 
| INVALID\$1HELM\$1SENSITIVE\$1CONFIG | 配置架构不能包含收集敏感信息的字段。配置架构不得接受密码、API 密钥、证书或机密。相反，请为客户单独创建的 Kubernetes 密钥提供字段。 | 
| INVALID\$1HELM\$1CHART\$1IMAGES | 所有映像（包括开源依赖项）都必须推送到通过 “[添加存储库” 请求创建的 AWS Marketplace Amazon ECR 存储库](container-add-version.md#add-repositories)。 | 
| INVALID\$1HELM\$1UNDECLARED\$1IMAGES | 必须在 “[添加版本](container-add-version.md#add-new-version)” 请求中明确列出所有容器镜像引用。 | 
| INVALID\$1HELM\$1LINT | Helm 图表helm lint验证失败。helm lint在本地运行以识别和修复结构或语法问题。使用 Helm 版本3.19.0或更高版本。 | 
| INVALID\$1HELM\$1TEMPLATE | Helm 图表helm template验证失败。该图表无法转换为有效的 Kubernetes 清单。使用本地测试helm template以识别模板语法或逻辑错误。使用 Helm 版本3.19.0或更高版本。 | 
| MISSING\$1HELM\$1DEPLOYMENT\$1CONFIG | Amazon EKS 附加组件的 Helm 图表必须包含部署或 DaemonSet 资源。Amazon EKS 至少需要其中一种工作负载类型才能进行附加组件生命周期管理。请参阅[Amazon EKS 附加组件产品的要求](#publishing-eks-add-on)。 | 
| INCOMPATIBLE\$1CONFIGURATION\$1SCHEMA\$1VERSION | 不支持中的 JSON 架构版本。aws\$1mp\$1configuration\$1schema.json[架构要求](#schema-requirements)有关支持的架构版本，请参阅。 | 
| INVALID\$1IMAGE\$1REFERENCE | 所有图像都必须定义为变量，values.yaml并使用 Helm 模板语法进行引用，如中所述[Helm 图表结构要求](#helm-chart-structure-requirements)。 | 
| MISSING\$1VALUES\$1IMAGE\$1REFERENCE | 每个容器镜像引用中都必须有相应的条目values.yaml。 | 
| MISSING\$1IMAGE\$1TAG | 中的容器图像引用values.yaml必须包含明确的标签值或默认为图表版本Chart.yaml。 | 

## 容器产品使用说明
<a name="container-product-usage-instructions"></a>

在为容器产品创建使用说明时，请按照[为创建 AMI 和容器产品使用说明 AWS Marketplace](ami-container-product-usage-instructions.md)中的步骤和指导进行操作。

### Helm 图表使用说明
<a name="helm-chart-usage-instructions"></a>

为 Helm 图表产品创建使用说明时：
+ 清楚记录 `values.yaml` 文件中的所有可配置参数，包括映像仓库、标签及注册表参数。
+ 提供有关如何在安装 Helm 图表时覆盖这些参数的示例。
+ 不得指导用户修改 `values.yaml` 以外的任何文件，也不得指导用户在安装图表时使用 `--set` 参数。
+ 包含关于产品如何处理容器映像区域化策略的信息。

## Amazon EKS 附加组件产品的要求
<a name="publishing-eks-add-on"></a>

Amazon EKS 附加组件是为 Kubernetes 应用程序提供操作功能的软件，但并不特定于应用程序。例如，Amazon EKS 附加组件包括可观察性代理或Kubernetes驱动程序，允许集群与用于联网、计算和存储的底层 AWS 资源进行交互。

作为容器产品的卖家，您可以从多个部署选项（包括 Amazon EKS）中进行选择。您可以将产品版本作为 AWS Marketplace 附加组件发布到 Amazon EKS 附加组件目录中。您的附加组件显示在 Amazon EKS 控制台中，旁边是其他供应商维护 AWS 的插件。您的买家可以将您的软件作为附加组件部署，就像部署其他附加组件一样简单。

有关更多信息，请参阅《Amazon EKS 用户指南》**中的 [Amazon EKS 附加组件](https://docs.aws.amazon.com/eks/latest/userguide/eks-add-ons.html)。

### 将您的容器产品作为 AWS Marketplace 附加组件进行准备
<a name="preparing-eks-addon"></a>

要将您的容器产品作为 AWS Marketplace 插件发布，它必须满足以下要求：
+ 您的集装箱商品必须发布在 AWS Marketplace。
+ 您的容器产品必须与 ARM64架构 AMD64 兼容。
+ 您的容器产品不得使用自带许可 (BYOL) [定价模式](https://docs.aws.amazon.com/marketplace/latest/userguide/pricing-container-products.html)。
**注意**  
Amazon EKS 附加组件交付不支持 BYOL。
+ 您必须遵守所有[基于容器的产品要求](https://docs.aws.amazon.com/marketplace/latest/userguide/container-product-policies.html)，包括将所有容器图像和Helm图表推送到托管 AWS Marketplace Amazon ECR 存储库。这项要求包括开源映像，例如，`nginx`。图像和图表不能托管在其他外部存储库中，包括但不限于 [Amazon ECR 公开映像浏览馆](https://docs.aws.amazon.com/AmazonECR/latest/public/public-repositories.html)、Docker Hub 和 Quay。
+ **Helm 图表** - 准备软件并将其打包为 Helm 图表。Amazon EKS 附加组件框架可将 Helm 图表转换为 Kubernetes 清单。Amazon EKS 系统不支持某些 Helm 功能。以下列表描述了将您的软件作为 Amazon EKS 附加组件接入之前必须满足的各项要求。在此列表中，所有Helm命令都使用Helm版本 3.19.0：
  + 支持所有`Capabilities`对象，但有例外`.APIVersions`。 `.APIVersions`不支持 non-built-in自定义Kubernetes APIs。
  + 仅支持 ```Release.Name` 和 `Release.Namespace` 对象。
  + 不支持 Helm 钩子和 `lookup` 函数。
  + 所有相关图表都必须位于主 Helm 图表中（使用存储库路径 file://... 指定）。
  + Helm 图表必须成功通过 Helm Lint 和 Helm 模板且无错误。命令如下：
    + Helm Lint – `helm lint helm-chart`

      常见问题包括父图表元数据中未声明的图表。例如，`chart metadata is missing these dependencies: chart-base Error: 1 chart(s) linted, 1 chart(s) failed`
    + Helm 模板 – `helm template chart-name chart-location --set k8version=Kubernetes-version --kube-version Kubernetes-version --namespace addon-namespace --include-crds --no-hooks -f any-overriden-values`

      使用 `-f` 标志传递任何被覆盖的配置。
  + 将所有容器二进制文件存储在 AWS Marketplace Amazon ECR 存储库中。要创建清单，请使用前面显示的 Helm 模板命令。在清单中搜索任何外部映像引用，例如 `busybox` 或 `gcr` 映像。使用请求下拉列表中的 “**添加存储库” 选项创建的 AWS Marketplace Amazon ECR 存储库将所有容器映像和依赖项上传到创建的 Amazon ECR 存储库**中。
+ **自定义配置** - 您可以在部署期间添加自定义变量。有关如何识别最终用户体验、将软件命名为 `aws_mp_configuration_schema.json` 以及打包为带有 Helm 图表的包装器的信息，请参阅 [Amazon EKS add-ons: Advanced configuration](https://aws.amazon.com/blogs/containers/amazon-eks-add-ons-advanced-configuration/)。

  根据[“\$1schema”关键字](https://json-schema.org/draft/2020-12/json-schema-core#name-the-schema-keyword)，`$schema` 必须是指向有效 `application/schema+json` 资源的 URI。

  此文件不得接受任何敏感信息，例如密码、许可证密钥和证书。

  要处理密钥和证书安装，您可以向最终用户提供 pre-Add-on安装后或安装步骤。产品不应依赖任何外部许可证。产品应基于 AWS Marketplace 权利运行。

  有关 `aws_mp_configuration_schema.json` 限制的更多信息，请参阅 [附加组件配置要求和附加组件提供商的最佳做法](#eks-addon-configuration)。
+ **确定并创建要在其中部署软件的命名空间** - 在产品的第一个版本中，您必须通过添加模板化命名空间来确定要在其中部署软件的命名空间。
+ **自定义资源定义 (CRDs)** — Amazon EKS 插件框架不支持安装 CRDs和基于 CRDs 应用于同一插件的自定义资源声明。如果您的插件有自定义资源并且依赖于此 CRDs，则您可以：
  + **发布两个附加组件：**将 CRD 定义拆分为一个单独的附加组件（单独的 Helm 图表），并将实际的[自定义资源](https://kubernetes.io/docs/concepts/extend-kubernetes/api-extension/custom-resources/)安装拆分为一个单独的附加组件。
  + **发布带有其他手动说明的单个插件：**发布一个 CRDs 在集群上安装的插件。提供使用说明以及 kubernetes 清单文件，供最终用户设置依赖这些文件的自定义资源。 CRDs
+ **`serviceAccount`如果适用，请创建** — 如果该软件是付费软件 AWS Marketplace 或者必须与其他软件连接 AWS 服务，请确保Helm图表是`serviceAccount`默认创建的。如果 `serviceAccount` 创建由 `values.yaml` 文件中的参数处理，请将参数值设置为 `true`。例如 `serviceAccount.create = true`。这是必需操作，因为客户可能会选择通过沿用已具有所需权限的底层节点实例中的权限来安装附加组件。如果 Helm 图表未创建 `serviceAccount`，则无法将权限绑定到 `serviceAccount`。
+ **可追踪的部署或 Daemonset** - 确保您的 Helm 图表有 Daemonset 或部署。Amazon EKS 附加组件框架使用它们跟踪您的 Amazon EKS 资源的部署。如果没有可追踪的部署或 Daemonset，您的附加组件将面临部署错误。如果您的附加组件没有部署或 Daemonset，例如，如果您的附加组件部署了一堆无法追踪的自定义资源或 Kubernetes 作业，请添加虚拟部署或 Daemonset 对象。
+ **支持 AMD 和 ARM 架构** — ARM64 如今，许多 Amazon EKS 客户都在使用 AWS Graviton 实例。第三方软件必须同时支持这两种架构。
+ **与许可或计量集 APIs 成 AWS Marketplace** — AWS Marketplace 支持多种计费模式。有关更多信息，请参阅 [容器产品计费、计量和许可集成](container-products-billing-integration.md)。如果您想通过 PAYG 机制销售产品，请参阅[使用 AWS Marketplace Metering Service 对容器产品配置自定义计量](container-metering-meterusage.md)。如果您想通过预付模式或合同模式销售产品，请参阅 [集装箱产品的合同定价 AWS License Manager](container-license-manager-integration.md)。
+ **上传软件以及所有构件和依赖项** - Helm 图表必须是独立的，并且不需要从外部来源（如 GitHub）获取依赖项。如果软件需要外部依赖项，则必须将依赖项推送到同一 AWS Marketplace 列表下的 AWS Marketplace 私有 Amazon ECR 存储库。
+ **在您的网站上提供部署说明** - 我们要求您为客户提供一份部署指南，说明如何通过 [create-addon](https://docs.aws.amazon.com/cli/latest/reference/eks/create-addon.html) 命令部署软件。
+ **附加 permissions/IAM 角色** — 如果从发布的插件 AWS Marketplace 需要访问 AWS 服务，则您的软件应有一个带有 IAM 策略注释的 Kubernetes 服务账户才能访问服务。 AWS 您可以从两个选项中进行选择，让您的服务帐号向 AWS 服务发出 API 请求：
  + 通过 IRSA 获取凭证：此选项允许软件从身份和访问管理（IAM）角色服务（IRSA）获取代入凭证。有关更多信息，请参阅[服务账户的 IAM 角色](https://docs.aws.amazon.com/eks/latest/userguide/iam-roles-for-service-accounts.html)。
  + Amazon EKS 容器身份：此选项允许您的软件使用亚马逊 EKS 容器的 Pod 身份向 AWS 服务发出 API 请求。有关更多信息，请参阅[了解 EKS Pod Identity 如何授予 pod 访问 AWS 服务的](https://docs.aws.amazon.com/eks/latest/userguide/pod-identities.html)权限

  您的附加组件必须在 Helm 图表的顶层目录（即与当前自定义配置架构文件 `aws_mp_configuration_schema.json` 所在的同一目录）中，额外添加一个名为 `aws_mp_addon_parameters.json` 的配置文件。目前，该文件仅用于处理与容器组（pod）身份兼容的权限配置。文件格式如下：

  ```
  {
    "permissions": {
        "isPodIdentityCompatible" : true,
        "permissionsList": [
         {
          "serviceAccount" : "String",
          "managedPolicies" : ["Policy Arn"],
         }
       ]
      }
    }
  ```

  **文件名：`aws_mp_addon_parameters.json`**
**注意**  
`aws_mp_addon_parameters.json` 文件可在 Amazon EKS 控制台的**附加组件配置设置**页面中启用**附加组件访问权限**部分    
[\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_cn/marketplace/latest/userguide/container-product-policies.html)
**注意**  
Pay-as-you-go 来自的 (PAYG) 附加产品 AWS Marketplace 无法使用 Amazon EKS Pod 身份，必须使用服务账户的 IAM 角色 (IRSA) 进行访问控制。
+ **版本更新** - Amazon EKS 会在上游版本发布几周后发布新的 Kubernetes 版本。随着新的 Amazon EKS 集群版本正式发布，供应商有 45 天的时间来认证或更新其软件，使其与新的 Amazon EKS 集群版本兼容。如果您当前版本的附加组件支持新的 Kubernetes 版本，请对其进行验证和认证，以便我们更新版本兼容性矩阵。如果需要新的附加组件版本来支持新的 Kubernetes 版本，则请提交新版本以便载入。
+ 合作伙伴的软件必须属于以下类型之一，或者是能够增强 Kubernetes 或 Amazon EKS 的操作软件：Gitops \$1 监控 \$1 日志 \$1 证书管理 \$1 策略管理 \$1 成本管理 \$1 自动扩展 \$1 存储 \$1 kubernetes 管理 \$1 服务网格 \$1 etcd-backup \$1 \$1 负载均衡器 \$1 本地注册表\$1 网络 \$1 安全 \$1 备份 \$1 入口控制器 \$1 可观察性 ingress-service-type
+ 软件不能是[容器网络接口（CNI）](https://github.com/containernetworking/cni)。
+ 软件必须通过付费产品销售 AWS Marketplace 并与许可和计 APIs 量功能集成。不接受 BYOL 产品。

### 附加组件配置要求和附加组件提供商的最佳做法
<a name="eks-addon-configuration"></a>

Amazon EKS 要求附加组件提供商以 [Helm JSON 架构](https://helm.sh/docs/topics/charts/#schema-files)字符串的形式进行配置。需要必需配置或允许可选配置的插件必须包含已提交 Helm Chart 的`aws_mp_configuration_schema.json`文件。 AWS Marketplace Amazon EKS 将使用此架构来验证客户的配置输入，并拒绝输入值不符合该架构的 API 调用。附加组件配置通常分为两类：
+ 一般 Kubernetes 属性的配置，例如标签、容忍度、nodeSelector 等
+ 特定于附加组件的配置，例如许可证密钥 URLs、功能启用等。

本部分重点介绍与一般 Kubernetes 属性相关的第一类。

Amazon EKS 建议遵循有关配置 Amazon EKS 附加组件的最佳实践。
+ [架构要求](#schema-requirements)
+ [允许配置的常用参数](#parameters-allowed)
+ [不允许配置的常用参数](#parameters-not-available)

#### 架构要求
<a name="schema-requirements"></a>

定义 JSON 架构时，请确保您使用的是 Amazon EKS 附加组件支持的 jsonschema 版本。

支持的架构列表：
+ https://json-schema。 org/draft-04/schema
+ https://json-schema。 org/draft-06/schema
+ https://json-schema。 org/draft-07/schema
+ https://json-schema。 org/draft/2019-09/schema

使用任何其他 JSON 架构版本都与 Amazon EKS 附加组件不兼容，并且会导致该附加组件在问题得到解决之前无法发布。

**Helm 架构文件示例**

```
{
"$schema": "http://json-schema.org/schema#",
  "type": "object",
  "properties": {
"podAnnotations": {
"description": "Pod Annotations"
"type": "object"
    },
    "podLabels": {
"description": "Pod Labels"
"type": "string"
    },
    "resources": {
"type": "object"
"description": "Resources"
    },
    "logLevel": {
"description": "Logging Level"
"type": "string",
      "enum": [
        "info",
        "debug"
      ]
    },
    "config": {
"description": "Custom Configuration"
"type": "object"
    }
  }
}
```

**camelCase**  
配置参数必须采用 camelCase 格式，如果不符合此格式，则会被拒绝。

**描述为必填项**  
务必包含对架构属性的有意义的描述。此描述将用于在 Amazon EKS 控制台中为每个配置参数呈现标签名称。

**RBAC 定义**  
附加组件提供商需要根据最低权限原则，定义和提供成功安装附加组件所需的 RBAC 权限。如果需要针对新版本的附加组件或任何解决 CVE 问题的修复而更改 RBAC 权限，则附加组件提供商需要将此更改告知 Amazon EKS 团队。每个 Kubernetes 资源所需的权限应仅限于对象的资源名称。  

```
apiGroups: ["apps"]
resources: ["daemonsets"]
resourceNames: ["ebs-csi-node"]
verbs: ["create", "delete", "get", "list", "patch", "update", "watch"]
```

**密钥管理**  
本部分仅适用于需要客户配置机密信息（如应用程序密钥、API 密钥、密码等）的附加组件。由于存在安全隐患，Amazon EKS 目前 APIs 不支持以纯文本形式传递机密信息。但是，客户可以使用配置来传入包含附加组件所需密钥的 Kubernetes Secret 的名称。客户需要创建包含密钥的 Kubernetes Secret 对象，其命名空间与先决条件步骤中的相同，然后在创建附加组件时使用配置 blob 传入机密的名称。我们建议附加组件提供商为架构属性命名，这样客户就不会意外将其误认为是实际密钥。例如： appSecretName， connectionSecretName 等等。  
总之，附加组件提供商可以利用该架构，让客户传入机密的名称，但不允许传入实际持有机密的密钥。

**示例配置值**  
您可以在架构中包含配置示例，以帮助客户配置附加组件。以下示例来自 AWS Distro 的 OpenTelemetry 插件架构。  

```
"examples": [
      {
        "admissionWebhooks": {
          "namespaceSelector": {},
          "objectSelector": {}
        },
        "affinity": {},
        "collector": {
          "amp": {
            "enabled": true,
            "remoteWriteEndpoint": "https://aps-workspaces.us-west-2.amazonaws.com/workspaces/ws-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/api/v1/remote_write"
          },
          "cloudwatch": {
            "enabled": true
          },
          "mode": "deployment",
          "replicas": 1,
          "resources": {
            "limits": {
              "cpu": "256m",
              "memory": "512Mi"
            },
            "requests": {
              "cpu": "64m",
              "memory": "128Mi"
            }
          },
          "serviceAccount": {
            "annotations": {},
            "create": true,
            "name": "adot-collector"
          },
          "xray": {
            "enabled": true
          }
        },
        "kubeRBACProxy": {
          "enabled": true,
          "resources": {
            "limits": {
              "cpu": "500m",
              "memory": "128Mi"
            },
            "requests": {
              "cpu": "5m",
              "memory": "64Mi"
            }
          }
        },
        "manager": {
          "env": {},
          "resources": {
            "limits": {
              "cpu": "100m",
              "memory": "128Mi"
            },
            "requests": {
              "cpu": "100m",
              "memory": "64Mi"
            }
          }
        },
        "nodeSelector": {},
        "replicaCount": 1,
        "tolerations": []
      }
    ]
```

#### 允许配置的常用参数
<a name="parameters-allowed"></a>

以下是面向客户的 Helm 架构文件中的建议参数。


| 参数 | 说明 | 是否应该有默认值？ | 
| --- | --- | --- | 
| additionalLabels | 向附加组件管理的所有 Kubernetes 对象添加 Kubernetes 标签。 | 否 | 
| additionalAnnotations | 向附加组件管理的所有 Kubernetes 对象添加 Kubernetes 注释。 | 否 | 
| podLabels | 向附加组件管理的容器组（pod）添加 Kubernetes 标签。 | 否 | 
| podAnnotations | 向附加组件管理的容器组（pod）添加 Kubernetes 注释。 | 否 | 
| logLevel | 附加组件管理的组件的日志级别。 | 是 | 
| nodeSelector | 建议的最简单的节点选择约束形式。您可以在容器组（pod）规范中添加 nodeSelector 字段，并指定希望目标节点具有的节点标签。 | 例如，可能仅限 Linux 节点 | 
| tolerations | 容忍度适用于容器组（pod）。容忍度允许调度器调度带有匹配污点的容器组（pod）。容忍度允许调度，但不能保证调度。 | 也许，在 DaemonSet 中更常见 | 
| affinity | 亲和性功能由两种类型的亲和性组成：节点亲和性，功能类似于 nodeSelector 字段，但更具表现力，可使您指定软规则；容器组（pod）间亲和性/反亲和性，允许您针对其他容器组（pod）上的标签对容器组（pod）进行约束。 | 也许 | 
| topologySpreadConstraints | 您可以使用拓扑分布约束来控制容器组（pod）在集群内故障域（如区域、可用区、节点和其他用户定义的拓扑域）间的分布方式。这有助于实现高可用性以及高效的资源利用率。 | 也许 | 
| resource request/limits | 指定 cpu/memory 每个容器需要多少。强烈建议设置请求。可选择设置限制。 | 是 | 
| replicas | 附加组件管理的容器组（pod）的副本数量。不适用于 Daemonset。 | 是 | 

**注意**  
对于工作负载调度配置参数，您可能需要在必要时将架构中的顶级组件分开。例如，Amazon EBS CSI 驱动程序包含两个主要组件，即控制器和节点代理，客户需要 selectors/tolerations 为每个组件使用不同的节点。

**注意**  
JSON 架构中定义的默认值仅用于用户文档目的，并不能取代在 `values.yaml` 文件中使用正确默认值的必要性。如果使用该默认属性，请确保 `values.yaml` 中的默认值与架构中的默认值相匹配，并且每当对 Helm 图表进行更改时，两个构件（`values.schema.json` 和 `values.yaml`）都保持同步。

```
"affinity": {
            "default": {
              "affinity": {
                "nodeAffinity": {
                  "preferredDuringSchedulingIgnoredDuringExecution": [
                    {
                      "preference": {
                        "matchExpressions": [
                          {
                            "key": "eks.amazonaws.com/compute-type",
                            "operator": "NotIn",
                            "values": [
                              "fargate"
                            ]
                          }
                        ]
                      },
                      "weight": 1
                    }
                  ]
                },
                "podAntiAffinity": {
                  "preferredDuringSchedulingIgnoredDuringExecution": [
                    {
                      "podAffinityTerm": {
                        "labelSelector": {
                          "matchExpressions": [
                            {
                              "key": "app",
                              "operator": "In",
                              "values": [
                                "ebs-csi-controller"
                              ]
                            }
                          ]
                        },
                        "topologyKey": "kubernetes.io/hostname"
                      },
                      "weight": 100
                    }
                  ]
                }
              }
            },
            "description": "Affinity of the controller pod",
            "type": [
              "object",
              "null"
            ]
          }
```

### 不允许配置的常用参数
<a name="parameters-not-available"></a>

各种附加组件（例如 Elastic Load Balancing Controller）可能需要集群元数据参数，例如 `clusterName`、`region`、`vpcId`、`accountId` 和其他参数。Amazon EKS 服务已知的任何与这些参数相似的参数都将由 Amazon EKS 附加组件自动注入，用户无需负责指定为配置选项。这些参数包括：
+ AWS 区域
+ Amazon EKS 集群名称
+ 集群的 VPC ID
+ 容器注册表，专门用于 build-prod 账户，供网络附加组件使用
+ DNS 集群 IP，专门用于 coredns 附加组件
+ Amazon EKS 集群 API 端点
+ IPv4 已在集群上启用
+ IPv6 已在集群上启用
+ 在集群上 IPv6 启用的前缀委派

附加组件提供商需要确保您已为此类适用参数定义了模板。上述每个参数都将有一个由 Amazon EKS 定义的预定义 `parameterType` 属性。版本元数据将指定模板中参数 name/path 的`parameterType`和之间的映射。这样，Amazon EKS 就可以动态传入这些值，而无需客户通过配置进行指定，还可以让附加组件提供商灵活地定义自己的模板名称/路径。应从架构文件中排除 Amazon EKS 需要动态注入的上述参数。

**来自版本元数据的映射示例**

```
"defaultConfiguration": [
       {
            "key": "image.containerRegistry",
            "parameterType": "CONTAINER_REGISTRY"
       }
]
```

不建议在面向客户的 Helm 架构文件中配置以下参数。这些参数要么应具有不可修改的默认值，要么根本不包含在附加组件模板中。


| 参数 | 说明 | 是否应该有默认值？ | 
| --- | --- | --- | 
| image | 将部署在 Kubernetes 集群上的容器映像。 | 否，通过附加组件定义进行管理 | 
| imagePullSecrets | 将容器组（pod）配置为使用机密从私有注册表中提取。 | 不适用 | 
| livenessProbe | Kubelet 进程使用存活性探测器来知道何时重启容器。例如，存活性探测器可捕获死锁，即应用程序正在运行，但无法取得进展。尽管存在错误，但在这种状态下重启容器有助于提高应用程序的可用性。 | 是 | 
| readinessProbe | 为您的容器准备一个就绪性探测器至关重要。这样，在数据面板上运行的 Kubelet 进程就会知道容器何时已准备好提供流量。当容器组（pod）的所有容器都准备就绪时，容器组（pod）即被视为准备就绪。此信号的一个用途是控制哪些容器组（pod）用作服务的后端。当容器组（pod）未准备就绪时，它会从服务负载均衡器中移除。 | Yes | 
| startupProbe | kubelet 使用启动探测器来了解容器应用程序何时启动。如果配置了这种探测器，它会禁用存活性和就绪性检查，直到成功为止，以确保这些探测器不会干扰应用程序的启动。这可用于对启动缓慢的容器进行活性检查，避免它们在启动并运行之前就被 kubelet 终止。 | 可选 | 
| podDisruptionBudget | 定义 Pod Discruption Budget（PDB），确保在自愿中断期间，保持最低数量的容器组（pod）继续运行。PDB 限制了因自愿中断而同时关闭的复制应用程序的容器组（pod）数量。例如，基于法定人数的应用程序希望确保运行的副本数量永远不会低于法定人数所需的数量。Web 前端可能希望确保提供负载的副本数量永远不会低于总数的特定百分比。 | 是，如果默认为两个以上的副本 | 
| serviceAccount (name) | 容器组（pod）将在其下运行的服务账户的名称。 | 是 | 
| serviceAccount (annotations) | 应用于服务账户的注释。通常用于服务账户的 IAM 角色功能 | 否，IAM 服务账户角色 ARN 是在顶级 Amazon EKS 附加组件 API 中设置的。此规则的一个例外情况是，如果您的插件有多个插件 deployments/controllers （例如 Flux），并且需要单独的 IRSA 角色 ARNs。 | 
| priorityClassName | 优先级表示一个容器组（pod）相对于其他容器组（pod）的重要性。如果无法调度容器组（pod），调度器会尝试抢占（驱逐）优先级较低的容器组（pod），以便可以调度待处理的容器组（pod）。 | 可以。大多数附加组件对集群功能至关重要，默认情况下应设置优先级类别。 | 
| podSecurityContext | 安全上下文定义容器组（pod）或容器的权限和访问控制设置。通常用于设置 fsGroup，这是版本 1.19 及更低版本集群中的 IRSA 所必需的。 | 不太可能，因为 Amazon EKS 不再支持 Kubernetes v1.19 | 
| securityContext | 安全上下文定义容器组（pod）或容器的权限和访问控制设置。 | 是 | 
| updateStrategy | 指定用于用新容器组（pod）替换旧容器组（pod）的策略。 | 是 | 
| nameOverride | 覆盖容器组（pod）的名称。 | 否 | 
| podSecurityPolicy |  对参数实施限制。  | 不 PSPs 是-已弃用 | 
| extraVolumeMounts/额外卷 |  用于非 Amazon EKS 集群中的 IRSA。  | 否 | 

# 的容器产品定价 AWS Marketplace
<a name="pricing-container-products"></a>

在上 AWS Marketplace，您可以发布亚马逊弹性容器服务 (Amazon ECS)、亚马逊 Elastic Kubernetes Service（亚马逊 EKS）的免费商品、自带许可模式 (BYOL) 商品和付费商品。 AWS Fargate您只能为每个产品设置一个价格。本主题概述了容器产品的可用定价模式。

**注意**  
您可以使用 [AWS Marketplace Metering Service](https://docs.aws.amazon.com/marketplacemetering/latest/APIReference/Welcome.html) 为您的付费产品强制执行授权和计量用量。对于每个任务或每个容器组 (pod) 定价， AWS会自动计量用量。

您为集装箱产品设定的价格适用于所有商品 AWS 区域。每当您降低容器产品的价格时，将立即为您的买家实施新的价格。对于价格上涨，现有买家会在变更影响其计费前的 90 天收到有关变更的通知。新买家将按新金额计费。

**注意**  
对于新订阅用户，价格变动立即生效。对于现有订阅用户，价格变动在自发送价格变动通知之日起的 90 天期限之后的下一个月的第一天生效。例如，假设您在 3 月 16 日发送了价格变动通知。3 月 16 日之后的 90 天大约是 6 月 16 日。由于价格变动发生在 90 天期限之后的下一个月的第一天，因此变更的生效日期为 7 月 1 日。

**Topics**
+ [容器定价模式](#pricing-models-for-server-products)

## 容器定价模式
<a name="pricing-models-for-server-products"></a>

AWS Marketplace 集装箱产品有多种定价模式。

下表提供了有关基于容器的产品的定价模式的一般信息。


**容器产品的定价模式**  

| 定价模式 | 说明 | 
| --- | --- | 
| 自带许可（BYOL） | BYOL 是在外部 AWS Marketplace 通过您与买家保持的外部账单关系进行管理的。容器中的软件未与集成 AWS Marketplace 以进行计费。 | 
| 每月 | **固定每月价格**固定每月价格，可为用户提供在下个月无限制使用产品的权限。示例：您将产品价格设置为每月 99 USD。您的产品包括使用 Amazon ECS 任务定义部署的三个不同的容器映像。在客户订阅您的产品后，他们立即支付 99 美元，每月重复一次，直到他们取消订阅。买家还可以无限制地使用该产品。买家还单独为任何运行任务的基础设施付费。订阅后，他们可以访问您的容器映像。在任何配置下，他们都能在 Amazon ECS 或 Amazon EKS 上从这些映像启动并运行任何数量的容器。如果买家在月中取消订阅，他们将无法访问存储容器映像的 Amazon ECR AWS Marketplace 存储库。买家可能已经提取并存储了原始映像。但是，他们无法再提取您提供的新容器镜像版本 AWS Marketplace。买家将获得最后一个月未使用部分的退款。您的付款基于买家的使用量减去商定的费用。 AWS Marketplace  | 
| 自定义计量定价维度 |  根据您定义的维度（例如用户、节点、存储库或 GB）自定义计量价格，每个产品最多 24 个维度。 示例：您的产品按用户收费。您有管理员用户和普通用户，您可以将定价定义为管理员用户 2 美元，普通用户 1 美元。在上架产品时，您可以将它们设置为单独的维度。您按每天登录的用户收费，然后计量每天的用量。 要了解基于使用量的定价的自定义计量的更多信息，请参阅[使用 AWS Marketplace Metering Service 对容器产品配置自定义计量](container-metering-meterusage.md)。  | 
| 每个任务或每个容器组 (Pod) 每小时价格 |  **Amazon ECS 任务或 Amazon EKS 容器组 (pod)** 以每小时设置的价格衡量到秒级的每个 Amazon ECS 任务或 Amazon EKS 容器组 (pod) 定价。 示例：您的产品包含三个不同的容器映像：控制器节点、Worker 节点和分析节点。由于没有控制器节点，您的产品就无法正常运行或无法使用，因此您可以决定为该映像收取使用费。您将价格设置为每小时 6 美元。 您修改控制器节点的容器映像中的软件以与 [AWS Marketplace Metering Service](https://docs.aws.amazon.com/marketplacemetering/latest/APIReference/Welcome.html) `RegisterUsage` API 操作集成。这可确保只有具有活动订阅的买家可以启动并运行该容器映像并根据其运行时间长度计量其用量。 对于每个运行的 Amazon EKS 控制器容器组 (pod)，买家收取每小时 6 美元的使用费。如果买家启动了五个包含控制器节点容器的 Amazon EKS 控制器容器组 (pod)，他们将支付每小时 30 美元的费用（每个容器组 (pod) 6 美元）。买家还单独为容器组 (pod) 在其上运行的任何基础设施付费。 对于每小时定价，按秒计费，最低支付 1 分钟的费用。如果客户运行此控制器容器的时间长度为 20 分钟和 30 秒，他们将支付 `20 x ($6/60) + 30 x ($6/60/60) = $2 + $0.05 = $2.05` 的费用。您的付款基于买家的使用量减去商定的费用。 AWS Marketplace  要了解有关每项任务或每个 pod 每小时定价的更多信息，请参阅[使用 AWS Marketplace Metering Service 配置按小时计量](container-metering-registerusage.md)。  | 
| 按小时定价或带有长期合同的自定义计量定价 |  以较低的价格签订的长期合同，可以预先支付或定期分期付款。可以将长期合同添加到具有自定义计费定价或每个任务和每个容器组 (pod) 定价的现有产品。当买家的消费量超过他们在长期合同中购买的数量时，买家将支付计量价格。 示例：对于计量定价模式，您可以为买家添加长期合同价格，以获得承诺预付的折扣。假设通常每消耗一部分单位收取 1 美元。每小时使用 1 个单位的买家每年将支付 8760 美元（`365 days x 24 hours x $1 per hour`）。您可以启用合同，允许买家在这 365 天内以该价格的一半（4380 美元）每小时使用 1 个单位。在这种情况下，买家承诺为一年期的合同预付款，价格从每单位 1 美元降至每单位 0.5 美元。您也可以让买家购买其中的多份合同。如果计量数量显示买家在一小时内消耗了 10 个单位，并且他们有两份合同，那么 2 份合同中将包括 2 个单位。另外 8 个单位将按每小时 1 美元的常规价格计费，该小时的总计为 8 美元。 例如，对于每任务或每容器组 (pod)，您可以为买家添加长期合同价格，以获得承诺预付的折扣。如果您通常为每个容器组 (pod) 收取 6 美元，则可以将长期合同期限设置为 365 天，价格为 13140 美元（`365 days x 24 hours x $3 per pod per hour`）。然后，一份合同将授权客户在这 365 天内每小时获得 1 个容器组 (pod)。客户可以选择购买多份合同。例如，客户可以购买两份合同，授权他们每小时获得 2 个容器组 (pod)。如果客户每小时运行的容器组 (pod) 个数多于授权的合同，则超出的容器组 (pod) 将按您的正常小时价格计费。 在这两种情况下，购买长期合同的买家都会预先被收取费用，无论是作为一次性付款还是定期安排的未来付款。买家也将按计量费率计算超出合同的任何额外使用费用。  | 
| 容器合同定价 |  **采用合同定价的容器** – 一种基于容器的产品，买家需要为此支付预付费用。 要了解有关合同定价的更多信息，请参阅[集装箱产品的合同定价 AWS License Manager](container-license-manager-integration.md)。  | 

# 容器产品计费、计量和许可集成
<a name="container-products-billing-integration"></a>

AWS Marketplace 与其他产品集成 AWS 服务 ，为您的集装箱产品提供计量和基于合同的定价。对于按用量定价的基于容器的产品，您可以使用 [AWS Marketplace Metering Service](https://docs.aws.amazon.com/marketplacemetering/latest/APIReference/Welcome.html) 来检查使用产品的权利以及对用量进行计量以进行计费。对于采用合同定价的基于容器的产品，您可以使用将许可证与您的产品相关联。 AWS License Manager 以下部分提供有关 AWS Marketplace Metering Service 的按小时计量和自定义计量以及 AWS License Manager的合同定价的更多信息。

**Topics**
+ [按小时计量和自定义计量 AWS Marketplace Metering Service](#entitlement-and-metering-for-paid-products)
+ [与的合同定价 AWS License Manager](#container-products-contracts-license-manager)
+ [使用 AWS Marketplace Metering Service 配置按小时计量](container-metering-registerusage.md)
+ [使用 AWS Marketplace Metering Service 对容器产品配置自定义计量](container-metering-meterusage.md)
+ [集装箱产品的合同定价 AWS License Manager](container-license-manager-integration.md)

## 按小时计量和自定义计量 AWS Marketplace Metering Service
<a name="entitlement-and-metering-for-paid-products"></a>

要检查使用产品的权利以及对用量进行计量以进行计费，您可以使用 [AWS Marketplace Metering Service](https://docs.aws.amazon.com/marketplacemetering/latest/APIReference/Welcome.html)。如果您想定义自己的定价单位并将使用量计给我们进行计费，请使用 [MeterUsage](https://docs.aws.amazon.com/marketplacemetering/latest/APIReference/API_MeterUsage.html)API 操作进行集成。如果您想根据使用的任务或容器数量对产品进行定价，并自动 AWS 计量该使用情况，请使用 [RegisterUsage](https://docs.aws.amazon.com/marketplacemetering/latest/APIReference/API_RegisterUsage.html)API 操作进行集成。对于这两种类型的定价，您可以添加长期合同价格，而无需更改与 AWS Marketplace Metering Service集成的方式。

当您在中创建新的容器产品时 AWS Marketplace 管理门户，我们会提供一组产品标识符（产品代码和公钥），用于将您的产品与集成 AWS Marketplace Metering Service。

### 权利
<a name="seller-container-entitlement"></a>

通过与集成， AWS Marketplace Metering Service 您可以验证运行付费软件的客户是否订阅了您的产品 AWS Marketplace，从而保护您在容器启动时免受未经授权的使用。要验证授权，请使用[MeterUsage](https://docs.aws.amazon.com/marketplacemetering/latest/APIReference/API_MeterUsage.html)或 [RegisterUsage](https://docs.aws.amazon.com/marketplacemetering/latest/APIReference/API_RegisterUsage.html)API 操作，具体取决于您的定价模式。对于每小时和固定每月定价模式，请使用 `RegisterUsage` API 操作。对于自定义计量定价模式，请使用 `MeterUsage` API 操作。

如果买家无权使用您的产品，则这些 API 操作会返回 `CustomerNotEntitledException` 异常。

**注意**  
如果买家在运行您的产品时取消订阅了产品，他们有权继续运行产品。但是，他们无法为您的产品启动其他容器。

### 集成指南
<a name="integration-guidelines"></a>

在创建和发布容器产品以及使用 `MeterUsage` 或 `RegisterUsage` API 操作验证权利和计量时，请记住以下准则：
+ 请勿在软件或 Docker 容器镜像中配置 AWS 凭据。 AWS 当您的容器映像在 Amazon ECS 任务或 Amazon EKS 容器中运行时，系统会在运行时自动获取买家证书。
+  要从 Amazon EKS 调用`MeterUsage`或 `RegisterUsage` API 操作，您必须[使用支持的 AWS 软件开发工具包](https://docs.aws.amazon.com/eks/latest/userguide/iam-roles-for-service-accounts-minimum-sdk.html)。要测试 Amazon EKS 的 `MeterUsage` 或 `RegisterUsage` 集成，您运行的 Amazon EKS 集群必须运行 Kubernetes 1.13.x 或更高版本。 AWS Identity and Access Management (IAM) 角色需要 Kubernetes 1.13 才能支持容器。正在运行的 pod 需要 IAM 角色才能获得在 Amazon EKS 上调用这些操作所需的 AWS 证书。
+ 您可以进行本地开发，但您将获得 `PlatformNotSupportedException` 异常。当您在容器服务（亚马逊 ECS、Amazon EKS 和 Fargate）上启动 AWS 容器时，不会发生此异常。

### 支持的 AWS 区域
<a name="supported-regions-metering"></a>

有关所有 AWS Marketplace 支持的列表 AWS 区域，请参阅全球基础设施网站上的[区域表](https://aws.amazon.com/about-aws/global-infrastructure/regional-product-services/)。

#### 获取 AWS 区域 用于计量的
<a name="metering-aws-region-configuration"></a>

将用于计量的容器与`MeterUsage`或 `RegisterUsage` API 操作集成时，请不要将 AWS SDK 配置为使用特定的 AWS 区域。必须在运行时动态获取区域。

**Example**  
例如，客户启动 Amazon ECS 任务或 Amazon EKS 容器组 (pod)。`RegisterUsage` API 操作在与启动 Amazon ECS 任务或 Amazon EKS 容器组 (pod) 的区域不同的区域中调用。因此，`RegisterUsage` API 操作会抛出 `InvalidRegionException` 错误。



AWS SDK 语言无法以一致`AWS_REGION`的方式确定。如果您的 SDK 没有自动获取 `AWS_REGION`，则需要手动编写软件来确定 `AWS_Region`。例如，在环境变量或其他配置不存在时， 适用于 Java 的 AWS SDK 会自动使用 [Amazon EC2 实例元数据](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-instance-metadata.html)（具体为 `ec2InstanceMetadata`）获取区域。在这种情况下，只有在 `AWS_REGION` 环境变量不存在时才调用 `ec2InstanceMetadata`。

有关如何在运行 AWS 区域 时动态获取的信息，请参阅适用于您的编程语言的 [AWS SDK 开发人员指南](https://aws.amazon.com/tools)。

### 防止计量修改
<a name="prevent-metering-modification"></a>

为买家引入修改或覆盖对 `RegisterUsage` 或 `MeterUsage` 的调用的方法可能导致不受欢迎的账单和付款问题。我们强烈建议您集成计量和授权逻辑。

在设计您的产品以防止计量修改时，请注意以下事项：
+ 如果买家可以插入包含 `CMD` 或 `ENTRYPOINT` 指令的新映像层，请直接将 `RegisterUsage` 或 `MeterUsage` 集成到买家正在通过容器映像运行的软件中。否则，从基本映像中通过 `CMD` 或 `ENTRYPOINT` 执行的对 `RegisterUsage` 或 `MeterUsage` 的调用可能会被买家覆盖。
+ 我们建议您管理您的软件用作输入 AWS Marketplace 的产品代码，`RegisterUsage`或者以买家无法修改`MeterUsage`的方式进行管理。但是，如果您的产品以客户可以覆盖的方式管理产品代码 AWS CloudFormation，例如 Helm chart 或 Kubernetes 清单，则必须维护*可信* AWS Marketplace 产品代码列表。这样可确保您的软件作为 `RegisterUsage` 或 `MeterUsage` 的输入传递的产品代码有效。
+  如果任何受信任的产品代码适用于免费产品，请确保它们无法用于替代付费产品代码。

## 与的合同定价 AWS License Manager
<a name="container-products-contracts-license-manager"></a>

对于采用合同定价的基于容器的产品，您可以使用 AWS License Manager 将许可证与您的产品关联起来。

AWS License Manager 是一种许可证管理工具，可让您的应用程序跟踪和更新客户购买的许可证（也称为授权）。本部分提供有关如何将您的产品与 AWS License Manager集成的信息。集成完成后，您可以在 AWS Marketplace上发布您的产品清单。

有关的更多信息 AWS License Manager，请参阅《[AWS License Manager 用户指南》和《AWS CLI](https://docs.aws.amazon.com/license-manager/latest/userguide/license-manager.html)*命令参考*》一[AWS License Manager](https://docs.aws.amazon.com/cli/latest/reference/license-manager/index.html)节。

**注意**  
合同到期后，客户无法启动新的容器实例。但是，在合同有效期内，他们可以启动任意数量的实例。这些许可证不绑定到特定的节点或实例。在任何节点上的任何容器上运行的任何软件都可以签出许可证，只要它具有分配的 AWS 凭证。
**专属优惠创建** – 卖家可以使用 AWS Marketplace 管理门户中的专属优惠创建工具为产品生成专属优惠。
**报告** – 您可以通过在 AWS Marketplace 管理门户中的**报告**部分设置 Amazon S3 存储桶来设置数据源。有关更多信息，请参阅 [中的卖家报告、数据源和控制面板 AWS Marketplace](reports-and-data-feed.md)。

### 集成工作流
<a name="container-LM-LM-workflow"></a>

以下步骤显示了将容器产品与 AWS License Manager集成的工作流：

1. 卖家创建具有 AWS License Manager 集成功能的产品。

1. 卖家在上架商品 AWS Marketplace。

1. 买家在上面找到产品 AWS Marketplace 并购买。

1. 许可证通过买家的 AWS 账户发送给他们。

1. 买家通过启动 Amazon EC2 实例、 Amazon EC2 任务或 Amazon EKS 容器组 (pod) 软件来使用该软件。客户使用 IAM 角色进行部署。

1. 软件读取买方 AWS License Manager 账户中的许可证，发现购买的权利并相应地配置功能。
**注意**  
License Manager 不进行任何跟踪或更新；这些是通过卖家的应用程序完成的。

# 使用 AWS Marketplace Metering Service 配置按小时计量
<a name="container-metering-registerusage"></a>

**注意**  
 对于 Amazon EKS 部署，您的软件必须使用[服务账户的 IAM 角色（IRSA）](https://docs.aws.amazon.com/eks/latest/userguide/iam-roles-for-service-accounts.html)来签署对 [https://docs.aws.amazon.com/marketplace/latest/APIReference/API_marketplace-metering_RegisterUsage.html](https://docs.aws.amazon.com/marketplace/latest/APIReference/API_marketplace-metering_RegisterUsage.html) API 操作的 API 调用。不支持使用 [EKS 容器组身份](https://docs.aws.amazon.com/eks/latest/userguide/pod-identities.html)、节点角色或长期访问密钥。  
对于 Amazon ECS 部署，您的软件必须使用 [Amazon ECS 任务 IAM](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task-iam-roles.html) 角色来签署对 [https://docs.aws.amazon.com/marketplace/latest/APIReference/API_marketplace-metering_RegisterUsage.html](https://docs.aws.amazon.com/marketplace/latest/APIReference/API_marketplace-metering_RegisterUsage.html) API 操作的 API 调用。不支持使用节点角色或长期访问密钥。

如果您的容器产品使用每小时每任务或每容器组 (pod) 定价，而不是自定义计量定价维度，则无需定义自定义计量维度。您可以在 AWS Marketplace中使用 AWS Marketplace Metering Service 对容器产品进行按小时计量。以下部分将介绍如何使用 AWS Marketplace Metering Service 配置按小时计量。

`RegisterUsage`API 操作每 Amazon Elastic Container Service (Amazon ECS) 任务或 Amazon Elastic Kubernetes Service (Amazon EKS) 容器组 (pod) 每小时计量软件使用情况，用量按比例计算到秒。最低 1 分钟用量适用于有效期较短的任务或 pod。软件使用的连续计量由自动处理 AWS Marketplace Metering Control Plane。除了调用 `RegisterUsage` 一次以启动软件使用的计量外，您的软件无需执行任何计量特定操作。

`RegisterUsage` 必须在启动容器时立即调用。如果您在容器启动后的前 6 个小时内没有注册容器，AWS Marketplace Metering Service 将不为前几个月提供任何计量保证。但是，计量将在当月继续进行，直到容器结束。

无论客户的订阅状态如何，都会 AWS Marketplace Metering Control Plane继续向客户收取运行 Amazon ECS 任务和 Amazon EKS Pod 的费用。这样，您的软件就无需在任务或容器组 (pod) 首次成功启动后执行授权检查。

有关将 AWS Marketplace Metering Service API 与按小时定价的容器产品集成的更多信息，请参阅*AWS Marketplace 卖家研讨会*的 “[与小时计量集成](https://catalog.workshops.aws/mpseller/en-US/container/integrate-hourly)” 实验室。

**Topics**
+ [每小时计量先决条件](#hourly-metering-prereqs)
+ [测试 `RegisterUsage` 集成](#testing-integration-for-registerusage)
+ [`RegisterUsage` 中的错误处理](#hourly-metering-entitlement-error-handling)
+ [使用以下方法将您的容器产品与 AWS Marketplace 计量服务集成 适用于 Java 的 AWS SDK](java-integration-example-registerusage.md)

## 每小时计量先决条件
<a name="hourly-metering-prereqs"></a>

发布产品之前，您必须首先完成以下操作：

1. 在中创建新的容器产品 AWS Marketplace 管理门户，并记下其产品代码。

   有关更多信息，请参阅 [概述：创建容器产品](container-product-getting-started.md#create-container-product)。

1. 为运行应用程序的任务或容器使用 AWS Identity and Access Management (IAM) 角色，并获得调用所需的 IAM 权限`RegisterUsage`。IAM 托管策略 `AWSMarketplaceMeteringRegisterUsage` 具有这些权限。有关该策略的更多信息，请参阅[ AWSMarketplaceMeteringFullAccess](https://docs.aws.amazon.com/aws-managed-policy/latest/reference/AWSMarketplaceMeteringFullAccess.html)《*AWS 托管策略参考*》。

1. （可选）如果您想查看日志记录，我们建议您在任务或 pod 定义中启用 AWS CloudTrail 日志记录。

1. 使用您定义的所有定价维度的记录对 `RegisterUsage` API 操作进行测试调用。

## 测试 `RegisterUsage` 集成
<a name="testing-integration-for-registerusage"></a>

在将图片提交给发布之前，请使用 `RegisterUsage` API 操作测试您的集成。 AWS Marketplace 

通过在 Amazon ECS 或 Amazon EKS 上运行您的产品，从容器映像中调用 `RegisterUsage`。使用您用于发布商品的 AWS 账户 AWS Marketplace。您的计量集成必须动态设置 AWS 区域，而不是对其进行硬编码。但是，在测试时，请至少在美国东部（弗吉尼亚州北部）区域启动一个包含您的付费容器的 Amazon ECS 任务或 Amazon EKS 容器组 (pod)。这样， AWS Marketplace 运营团队就可以使用该区域的日志来验证您的工作。

**注意**  
如果您的产品同时支持 Amazon ECS 和 Amazon EKS，则您只需在 Amazon EKS 中启动，我们便能验证您的集成。

在使用所有必需的元数据和定价信息发布您的产品之前，您无法完全测试集成。如果需要， AWS Marketplace 目录运营团队可以验证您的计量记录是否收到。

## `RegisterUsage` 中的错误处理
<a name="hourly-metering-entitlement-error-handling"></a>

如果您的容器镜像与集成， AWS Marketplace Metering Service 并且`ThrottlingException`在容器启动之外收到异常，则应终止容器以防止未经授权的使用。

仅在初始调用 `RegisterUsage` API 操作时引发 `ThrottlingException` 以外的异常。从同一 Amazon ECS 任务或 Amazon EKS 容器组 (pod) 进行的后续调用不会引发 `CustomerNotSubscribedException`，即使客户在任务或 容器组 (pod) 仍在运行时取消订阅也是如此。这些客户在取消订阅并跟踪其使用情况后仍需支付运行容器的费用。

下表描述了 `RegisterUsage` API 操作可能会引发的错误。每种 AWS SDK 编程语言都有一套错误处理指南，您可以参考这些指南以获取更多信息。


|  **错误**  |  **描述**  | 
| --- | --- | 
|  InternalServiceErrorException  |  RegisterUsage 不可用。 | 
|  CustomerNotEntitledException  |  客户没有产品的有效订阅。 | 
|  InvalidProductCodeException  |  作为请求的一部分传入的 ProductCode 值不存在。 | 
|  InvalidPublicKeyException  |  作为请求的一部分传入的 PublicKeyVersion 值不存在。 | 
|  PlatformNotSupportedException  |  AWS Marketplace 不支持从底层平台计量使用情况。仅支持 Amazon ECS、Amazon EKS 和 AWS Fargate 。 | 
|  ThrottlingException  |  对 RegisterUsage 的调用受限。 | 
|  InvalidRegionException  |  RegisterUsage必须使用与启动 Amazon ECS 任务或 Amazon EKS 容器相同的 AWS 区域 方法进行调用。这可防止容器在调用 RegisterUsage 时选择区域（例如，withRegion(“us-east-1”)）。 | 

# 使用以下方法将您的容器产品与 AWS Marketplace 计量服务集成 适用于 Java 的 AWS SDK
<a name="java-integration-example-registerusage"></a>

您可以使用与 AWS Marketplace 计量服务集成。 适用于 Java 的 AWS SDK 软件使用的连续计量由自动处理 AWS Marketplace Metering Control Plane。除了调用 `RegisterUsage` 一次以启动软件使用的计量外，您的软件无需执行任何计量特定操作。本主题提供了一个使用与[AWS Marketplace 计量服务](https://docs.aws.amazon.com/marketplacemetering/latest/APIReference/Welcome.html)`RegisterUsage`操作集成的实现示例。 适用于 Java 的 AWS SDK 

`RegisterUsage` 必须在启动容器时立即调用。如果您在容器启动后的前 6 个小时内没有注册容器，AWS Marketplace Metering Service 将不为前几个月提供任何计量保证。但是，计量将在当月继续进行，直到容器结束。

有关完整源代码，请参阅[RegisterUsage Java 示例](#registerusage-java-example)。无论使用哪种 AWS SDK 语言，其中的许多步骤都适用。



**AWS Marketplace Metering Service 集成的示例步骤**

1. 登录到 [AWS Marketplace 管理门户](https://aws.amazon.com/marketplace/management/tour)。

1. 从**资产**中，选择**容器**以开始创建新容器产品。创建产品会生成产品的产品代码以与您的容器映像集成。有关设置 IAM 权限的信息，请参阅[AWS Marketplace 计量和授权 API 权限](iam-user-policy-for-aws-marketplace-actions.md)。

1.  下载公开的 [AWS Java SDK](https://aws.amazon.com/sdk-for-java/)。
**重要**  
 要 APIs 从 Amazon EKS 调用计量，您必须[使用支持的 AWS 软件开发工具包并在运行 K](https://docs.aws.amazon.com/eks/latest/userguide/iam-roles-for-service-accounts-minimum-sdk.html) ubernetes 1.13 或更高版本的亚马逊 EKS 集群上运行。

1.  （可选）如果您要与`RegisterUsage`操作集成，并且想要执行数字签名验证，则需要在应用程序类路径中配置[BouncyCastle](https://mvnrepository.com/artifact/org.bouncycastle/bcprov-jdk15on)签名验证库。

   如果要使用 JSON Web Token (JWT)，则还必须在应用程序类路径中包括 [JWT Java](https://jwt.io/) 库。使用 JWT 提供了一种更简单的签名验证方法，但这不是必需的，您可以 BouncyCastle 改用独立版。无论您使用 JWT 还是 BouncyCastle，都需要使用诸如 Maven 之类的构建系统在应用程序类路径中包含 BouncyCastle 或 JWT 的传递依赖关系。

   ```
   // Required for signature verification using code sample
   <dependency>
       <groupId>org.bouncycastle</groupId>
       <artifactId>bcpkix-jdk15on</artifactId>
       <version>1.60</version>
   </dependency>
   
   // This one is only required for JWT
   <dependency>
       <groupId>com.nimbusds</groupId>
       <artifactId>nimbus-jose-jwt</artifactId>
       <version>6.0</version>
   </dependency>
   ```

1.  从您的产品中的每个付费容器映像调用 `RegisterUsage`。`ProductCode` 和 `PublicKeyVersion` 是必需参数，所有其他输入都是可选的。以下是 `RegisterUsage` 的示例负载。

   ```
   {
       "ProductCode" : "string", // (required)
       "PublicKeyVersion": 1,    // (required)
       "Nonce": "string",        // (optional) to scope down the registration
                                 //            to a specific running software
                                 //            instance and guard against
                                 //            replay attacks
   }
   ```
**注意**  
可能出现 AWS Marketplace Metering Service 瞬时连接问题。 AWS Marketplace 强烈建议实施最长 30 分钟的重试，并添加指数退避，以避免短期中断或网络问题。

1.  `RegisterUsage` 使用 SHA-256 生成可用于验证请求真实性的 RSA-PSS 数字签名。该签名包括以下字段：`ProductCode`、`PublicKeyVersion` 和 `Nonce`。要验证数字签名，您必须保留请求中的这些字段。以下代码是 `RegisterUsage` 调用的示例响应。

   ```
   {
   "Signature": "<<JWT Token>>"
   }
   
   // Where the JWT Token is composed of 3 dot-separated, 
   // base-64 URL Encoded sections.
   // e.g. eyJhbGcVCJ9.eyJzdWIMzkwMjJ9.rrO9Qw0SXRWTe
   
   // Section 1: Header/Algorithm
   {
   "alg": "PS256",
   "typ": "JWT"
   }
   
   // Section 2: Payload
   {
   "ProductCode" : "string",
   "PublicKeyVersion": 1,
   "Nonce": "string",
   "iat": date // JWT issued at claim 
   }
   
   // Section 3: RSA-PSS SHA256 signature
   "rrO9Q4FEi3gweH3X4lrt2okf5zwIatUUwERlw016wTy_21Nv8S..."
   ```

1. 重建包含 `RegisterUsage` 调用的新版本容器映像，标记容器，然后将其推送至与 Amazon ECS 或 Amazon EKS 兼容的任何容器注册表，如 Amazon ECR 或 Amazon ECR Public。如果您使用的是 Amazon ECR，请确保启动 Amazon ECS 任务或 Amazon EKS 容器组 (pod) 的账户对 Amazon ECR 存储库拥有权限。否则，启动失败。

1.  创建一个 [IAM](https://aws.amazon.com/iam/) 角色来授予容器调用 `RegisterUsage` 的权限，如以下代码所定义。您必须在 Amazon ECS 任务或 Amazon EKS 容器组 (pod) 定义的[任务角色](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task_definition_parameters.html#task_role_arn)参数中提供此 IAM 角色。

------
#### [ JSON ]

****  

   ```
   {
       "Version":"2012-10-17",		 	 	 
       "Statement": [
           {
               "Action": [
                   "aws-marketplace:RegisterUsage"
                   ],
                   "Effect": "Allow",
                   "Resource": "*"
           }
       ]
   }
   ```

------

1. 创建 Amazon ECS 任务或 Amazon EKS 容器定义，引用已与您在步骤 7 中创建的 IAM 角色集成的容器 AWS Marketplace 并引用该角色。如果要查看 AWS CloudTrail 日志记录，则应在任务定义中启用日志记录。

1. 创建 Amazon ECS 或 Amazon EKS 集群来执行您的任务或容器组 (pod)。有关创建 Amazon ECS 集群的更多信息，请参阅《Amazon Elastic Container Service 开发人员指南》**中的[创建集群](https://docs.aws.amazon.com/AmazonECS/latest/userguide/create_cluster.html)。有关（使用 Kubernetes 版本 1.1.3.x 或更高版本）创建 Amazon EKS 集群的更多信息，请参阅[创建 Amazon EKS 集群](https://docs.aws.amazon.com/eks/latest/userguide/create_cluster.html)。

1. 配置 Amazon ECS 或 Amazon EKS 集群，然后在 us-east- AWS 区域 1 中启动你创建的亚马逊 ECS 任务定义或亚马逊 EKS 容器。只有在此测试过程中，在产品上线之前，您才必须使用此区域。

1. 当您从 `RegisterUsage` 获得有效的响应时，您可以开始创建您的容器产品。如有问题，请联系 [AWS Marketplace 卖家运营](https://aws.amazon.com/marketplace/management/contact-us/)团队。

## RegisterUsage Java 示例
<a name="registerusage-java-example"></a>

以下示例使用 适用于 Java 的 AWS SDK 和 AWS Marketplace 计量服务来调用该`RegisterUsage`操作。签名验证是可选的，但如果您要执行签名验证，则必须包含所需的数字签名验证库。此示例仅用于说明。

```
import com.amazonaws.auth.PEM;
import com.amazonaws.services.marketplacemetering.AWSMarketplaceMetering;
import com.amazonaws.services.marketplacemetering.AWSMarketplaceMeteringClientBuilder;
import com.amazonaws.services.marketplacemetering.model.RegisterUsageRequest;
import com.amazonaws.services.marketplacemetering.model.RegisterUsageResult;
import com.amazonaws.util.json.Jackson;
import com.fasterxml.jackson.databind.JsonNode;
import com.nimbusds.jose.JWSObject;
import com.nimbusds.jose.JWSVerifier;
import com.nimbusds.jose.crypto.RSASSAVerifier;
import java.io.ByteArrayInputStream;
import java.nio.charset.StandardCharsets;
import java.security.PublicKey;
import java.security.Security;
import java.security.Signature;
import java.security.interfaces.RSAPublicKey;
import java.util.Base64;
import java.util.Optional;
import java.util.UUID;
import org.bouncycastle.jce.provider.BouncyCastleProvider;

/**
 * Class for making calls out to &MKT; Metering Service.
 */
class RegisterUsage {

    private static final String PRODUCT_CODE = ".......";

    private final AWSMarketplaceMetering registerUsageClient;
    private final SignatureVerifier signatureVerifier;
    private final int publicKeyVersion;

    public RegisterUsage(final SignatureVerifier signatureVerifier) {
        this.signatureVerifier = signatureVerifier;
        this.publicKeyVersion = PublicKeyProvider.PUBLIC_KEY_VERSION;
        this.registerUsageClient = AWSMarketplaceMeteringClientBuilder.standard().build();
    }

    /**
     * Shows how to call RegisterUsage client and verify digital signature.
     */
    public void callRegisterUsage() {
        RegisterUsageRequest request = new RegisterUsageRequest()
                .withProductCode(PRODUCT_CODE)
                .withPublicKeyVersion(publicKeyVersion)
                .withNonce(UUID.randomUUID().toString());

        // Execute call to RegisterUsage (only need to call once at container startup)
        RegisterUsageResult result = this.registerUsageClient.registerUsage(request);

        // Verify Digital Signature w/o JWT
        boolean isSignatureValid = this.signatureVerifier.verify(request, result);
        if (!isSignatureValid) {
            throw new RuntimeException("Revoke entitlement, digital signature invalid.");
        }
    }
}

/**
 * Signature verification class with both a JWT-library based verification
 * and a non-library based implementation.
 */
class SignatureVerifier {
    private static BouncyCastleProvider BC = new BouncyCastleProvider();

    private static final String SIGNATURE_ALGORITHM = "SHA256withRSA/PSS";

    private final PublicKey publicKey;

    public SignatureVerifier(PublicKeyProvider publicKeyProvider) {
        this.publicKey = publicKeyProvider.getPublicKey().orElse(null);
        Security.addProvider(BC);
    }

    /**
     * Example signature verification using the NimbusJOSEJWT library to verify the JWT Token.
     *
     * @param request RegisterUsage Request.
     * @param result  RegisterUsage Result.
     * @return true if the token matches.
     */
    public boolean verifyUsingNimbusJOSEJWT(final RegisterUsageRequest request, final RegisterUsageResult result) {
        if (!getPublicKey().isPresent()) {
            return false;
        }

        try {
            JWSVerifier verifier = new RSASSAVerifier((RSAPublicKey) getPublicKey().get());
            JWSObject jwsObject = JWSObject.parse(result.getSignature());
            return jwsObject.verify(verifier) && validatePayload(jwsObject.getPayload().toString(), request, result);
        } catch (Exception e) {
            // log error
            return false;
        }
    }

    /**
     * Example signature verification without any JWT library support.
     *
     * @param request RegisterUsage Request.
     * @param result  RegisterUsage Result.
     * @return true if the token matches.
     */
    public boolean verify(final RegisterUsageRequest request, final RegisterUsageResult result) {
        if (!getPublicKey().isPresent()) {
            return false;
        }
        try {
            String[] jwtParts = result.getSignature().split("\\.");
            String header = jwtParts[0];
            String payload = jwtParts[1];
            String payloadSignature = jwtParts[2];

            Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM, BC);
            signature.initVerify(getPublicKey().get());
            signature.update(String.format("%s.%s", header, payload).getBytes(StandardCharsets.UTF_8));
            boolean verified = signature.verify(Base64.getUrlDecoder()
                    .decode(payloadSignature.getBytes(StandardCharsets.UTF_8)));

            String decodedPayload = new String(Base64.getUrlDecoder().decode(payload));
            return verified && validatePayload(decodedPayload, request, result);
        } catch (Exception e) {
            // log error
            return false;
        }
    }

    /**
     * Validate each value in the returned payload matches values originally
     * supplied in the request to RegisterUsage. TimeToLiveInMillis and
     * PublicKeyExpirationTimestamp will have the values in the payload compared
     * to values in the signature
     */
    private boolean validatePayload(final String payload, final RegisterUsageRequest request,
                                    final RegisterUsageResult result) {
        try {
            JsonNode payloadJson = Jackson.getObjectMapper().readTree(payload);
            boolean matches = payloadJson.get("productCode")
                    .asText()
                    .equals(request.getProductCode());
            matches = matches && payloadJson.get("nonce")
                    .asText()
                    .equals(request.getNonce());
            return matches = matches && payloadJson.get("publicKeyVersion")
                    .asText()
                    .equals(String.valueOf(request.getPublicKeyVersion()));

        } catch (Exception ex) {
            // log error
            return false;
        }
    }

    private Optional<PublicKey> getPublicKey() {
        return Optional.ofNullable(this.publicKey);
    }
}

/**
 * Public key provider taking advantage of the &AWS; PEM Utility.
 */
class PublicKeyProvider {
    // Replace with your public key. Ensure there are new-lines ("\n") in the
    // string after "-----BEGIN PUBLIC KEY-----\n" and before "\n-----END PUBLIC KEY-----".
    private static final String PUBLIC_KEY =
            "-----BEGIN PUBLIC KEY-----\n"
                    + "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDdlatRjRjogo3WojgGHFHYLugd\n"
                    + "UWAY9iR3fy4arWNA1KoS8kVw33cJibXr8bvwUAUparCwlvdbH6dvEOfou0/gCFQs\n"
                    + "HUfQrSDv+MuSUMAe8jzKE4qW+jK+xQU9a03GUnKHkkle+Q0pX/g6jXZ7r1/xAK5D\n"
                    + "o2kQ+X5xK9cipRgEKwIDAQAB\n"
                    + "-----END PUBLIC KEY-----";

    public static final int PUBLIC_KEY_VERSION = 1;

    public Optional<PublicKey> getPublicKey() {
        try {
            return Optional.of(PEM.readPublicKey(new ByteArrayInputStream(
                    PUBLIC_KEY.getBytes(StandardCharsets.UTF_8))));
        } catch (Exception e) {
            // log error
            return Optional.empty();
        }
    }
}
```

# 使用 AWS Marketplace Metering Service 对容器产品配置自定义计量
<a name="container-metering-meterusage"></a>

**注意**  
 对于 Amazon EKS 部署，您的软件必须使用[服务账户的 IAM 角色（IRSA）](https://docs.aws.amazon.com/eks/latest/userguide/iam-roles-for-service-accounts.html)来签署对 [https://docs.aws.amazon.com/marketplace/latest/APIReference/API_marketplace-metering_MeterUsage.html](https://docs.aws.amazon.com/marketplace/latest/APIReference/API_marketplace-metering_MeterUsage.html) API 操作的 API 调用。不支持使用 [EKS 容器组身份](https://docs.aws.amazon.com/eks/latest/userguide/pod-identities.html)、节点角色或长期访问密钥。  
对于 Amazon ECS 部署，您的软件必须使用 [Amazon ECS 任务 IAM](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task-iam-roles.html) 角色来签署对 [https://docs.aws.amazon.com/marketplace/latest/APIReference/API_marketplace-metering_MeterUsage.html](https://docs.aws.amazon.com/marketplace/latest/APIReference/API_marketplace-metering_MeterUsage.html) API 操作的 API 调用。不支持使用节点角色或长期访问密钥。  
对于 Amazon Bedrock AgentCore 运行时部署，您的软件必须使用[AgentCore 运行时执行角色](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/runtime-permissions.html#runtime-permissions-execution)来签署 API 操作的 [https://docs.aws.amazon.com/marketplace/latest/APIReference/API_marketplace-metering_MeterUsage.html](https://docs.aws.amazon.com/marketplace/latest/APIReference/API_marketplace-metering_MeterUsage.html)API 调用。不支持使用长期访问密钥。

AWS Marketplace 集装箱产品可以对每种产品多达 24 种不同的定价维度进行自定义计量。每个维度都可以具有与之相关的长期合同价格。要启用自定义计量，请将您的容器产品与 AWS Marketplace Metering Service 集成。您可以使用 [https://docs.aws.amazon.com/marketplacemetering/latest/APIReference/API_MeterUsage.html](https://docs.aws.amazon.com/marketplacemetering/latest/APIReference/API_MeterUsage.html)API 操作为该使用量定义自己的定价单位和自定义计量以 AWS 进行计费。以下部分将介绍如何为容器产品配置自定义计量。

价格维度在两个位置定义，一次是在 AWS Marketplace 管理门户 （卖家门户）创建产品时，另一次是在软件中执行 `MeterUsage` 操作时。此双因素方法可确保后续的产品在向公众提供之前按预期工作。

要设置自定义计量，您需要选择使用类别、单位类型和定价维度：
+ **用户类别** - 用户类别可帮助买家了解您的产品是什么以及如何使用它。
+ **单位类型** - 单位类型定义计费的计量单位。例如，以 GBps 或为单位的带宽 MBps、以主机数量为单位的带宽，或者以 MB、GB 或 TB 为单位的数据。
+ **定价维度**-定价维度表示您为其设置了单位价格的功能或服务（例如，用户、扫描CPUs、v 或已部署的代理）。定价维度是公开的。但是，您仍然可以为公开产品定义专属和自带许可 (BYOL) 优惠。请勿在计量记录中发送定价。您可以计量单位数量，我们将其与您在创建产品时定义的价格一起使用，以计算买家的账单。

  如果您的产品定价不符合任何预定义类别或单位类型，您可以选择通用**单位**类别。然后，使用维度描述来描述单位是什么。

或者，您可以按您跟踪的属性将用量分发到分配中。分配以标签形式呈现给买家。这些标签允许买家按标签值查看按用量划分的费用。例如，如果您按用户收费，并且用户具有“部门”属性，则可以使用键为“部门”的标签创建使用分配，每个值一个分配。这不会更改您报告的价格、维度或总用量，但允许您的客户按与您的产品相应类别查看其成本。

我们建议您每小时发送一次计量记录。但是，您也可以汇总每日或每月的用量。如果您遇到中断，您可以聚合买家的软件用量，并在接下来的小时计量中发送。每小时不能发送多条记录。

有关将集装箱产品 AWS Marketplace Metering Service 的 API 与自定义计量定价集成的更多信息，请参阅*AWS Marketplace 卖家研讨会*的 “[与自定义计量集成](https://catalog.workshops.aws/mpseller/en-US/container/integrate-custom)” 实验室。

**重要**  
免费试用和预付费权利按小时进行跟踪。因此，单独发送这些记录可能会导致买家被多收费用。

**Topics**
+ [自定义计量先决条件](#custom-metering-prereqs)
+ [测试 ECS 和 EKS 的 `MeterUsage` 集成](#testing-meterusage-integration)
+ [测试 MeterUsage 集成 AgentCore](#testing-agentcore-metering)
+ [`MeterUsage` 中的错误处理](#custom-metering-entitlement-error-handling)
+ [（可选）供应商计量标记](#container-vendor-metered-tagging)
+ [代码示例](#container-meter-code-example)
+ [使用自定义计量将您的容器产品与 AWS Marketplace Metering Service 和集成 适用于 Java 的 AWS SDK](java-integration-example-meterusage.md)

## 自定义计量先决条件
<a name="custom-metering-prereqs"></a>

发布产品之前，您必须首先完成以下操作：

1. 在中创建新的容器产品 AWS Marketplace 管理门户，并记下其产品代码。

1. 为运行应用程序的任务、容器或 AgentCore 运行时终端节点使用 AWS Identity and Access Management (IAM) 角色，并获得调用所需的 IAM 权限`MeterUsage`。IAM 托管策略 `AWSMarketplaceMeteringRegisterUsage` 具有这些权限。有关该策略的更多信息，请参阅[ AWSMarketplaceMeteringFullAccess](https://docs.aws.amazon.com/aws-managed-policy/latest/reference/AWSMarketplaceMeteringFullAccess.html)《*AWS 托管策略参考*》。

1. （可选）如果您想查看 AWS CloudTrail 日志记录，我们建议您在任务或 pod 定义中启用日志记录。

1. 使用您定义的所有定价维度的记录对 `MeterUsage` API 操作进行测试调用。

## 测试 ECS 和 EKS 的 `MeterUsage` 集成
<a name="testing-meterusage-integration"></a>

在将图片提交给发布之前，请使用该`MeterUsage`操作来 AWS Marketplace 测试您的集成。

通过在亚马逊弹性容器服务 (Amazon ECS) 或亚马逊 Elastic Kubernetes Service (Amazon EKS) 上运行您的产品，使用您用来发布商品的容器镜像调`MeterUsage`用。 AWS 账户 AWS Marketplace您的计量集成必须动态设置 AWS 区域，而不是对其进行硬编码。但是，在测试时，请至少在美国东部（弗吉尼亚北部）地区启动一个包含您的付费容器的 Amazon ECS 任务或 Amazon EKS Pod，以便 AWS Marketplace 运营团队可以使用该地区的日志验证您的工作。

**注意**  
如果您的产品同时支持 Amazon ECS 和 Amazon EKS，则您只需在 Amazon EKS 中启动，我们便能验证您的集成。
在向公众发布产品之前和添加新维度之后，对每个维度进行测试。如果您没有发送与容器产品关联的每个维度的计量记录，则会导致错误并导致请求失败。

在使用所有必需的元数据和定价信息发布您的产品之前，您无法完全测试集成。如果需要， AWS Marketplace 目录运营团队可以验证您的计量记录是否收到。

## 测试 MeterUsage 集成 AgentCore
<a name="testing-agentcore-metering"></a>

在将图片提交给发布之前，请使用该`MeterUsage`操作来 AWS Marketplace 测试您的集成。

使用您上架商品时使用的 AWS 账户在 Amazon Bedrock AgentCore 上 AWS Marketplace运行您的商品，`MeterUsage`从容器图片中调用。您的计量集成必须动态设置 AWS 区域，而不是对其进行硬编码。但是，在测试时，请至少在美国东部（弗吉尼亚北部）地区启动一个包含您的付费容器的 Amazon Bedrock AgentCore 代理，以便 AWS Marketplace 运营团队可以使用该地区的日志验证您的工作。

 您无需汇总每小时使用记录。每次调用代理时，都应针对该次调用的使用量调用 `MeterUsage` 操作。

您必须使用适用于您的语言的最新发布版本的 AWS SDK。这会自动使用自动生成的值填充 `ClientToken` 参数，以帮助确定幂等性。之前发布的未填充此字段的 SDK 将不适用于来自 Amazon Bedro AgentCore ck 内部的`MeterUsage`呼叫。由于网络问题，重试时必须重复使用完全相同的请求。这样做可以确保以幂等方式处理请求。

由于 Amazon Bedrock AgentCore 与其他容器产品之间的预期计量行为存在差异，因此我们不建议在亚马逊 Bedrock AgentCore 和亚马逊 ECS 或 EKS 上共享相同的容器映像。

## `MeterUsage` 中的错误处理
<a name="custom-metering-entitlement-error-handling"></a>

容器启动时调用 `MeterUsage`，并将 `DryRun` 参数设为 true，以此验证计量集成是否正常工作。如果您的容器映像与 `MeterUsage` 操作集成并在容器启动时收到 `ThrottlingException` 之外的异常，您应终止容器以防止未经授权的使用。

仅在初始调用 `MeterUsage` 时引发 `ThrottlingException` 以外的异常。即使客户在任务或容器仍在 AgentCore 运行时取消订阅，来自同一 Amazon ECS 任务或 Amazon EKS 容器或运行时终端节点的后续调用也不会抛出`CustomerNotSubscribedException`。这些客户在取消订阅并跟踪其使用情况后仍需支付运行容器的费用。

有关常见错误[MeterUsage](https://docs.aws.amazon.com/marketplacemetering/latest/APIReference/API_MeterUsage.html)的详细说明，请参阅 *AWS Marketplace Metering Service API 参考*中的`MeterUsage`。每种 AWS SDK 编程语言都有一套错误处理指南，您可以参考这些指南以获取更多信息。

## （可选）供应商计量标记
<a name="container-vendor-metered-tagging"></a>

供应商计量标签可帮助独立软件供应商 (ISVs) 让购买者更精细地了解其软件使用情况，并可以帮助他们进行成本分配。

**注意**  
Amazon Bedrock 产品的计量请求不支持供应商计量标签。 AgentCore 

您可通过多种方式为买家的软件使用情况添加标签。一种方法是先询问买家他们希望在成本分配中看到什么。然后，您可以在为买家帐户跟踪的属性之间分配使用情况。属性的示例包括 `AccountId`、`Business Unit`、`Cost Centers`，以及您的产品的其他相关元数据。这些属性作为标签展示给买家。使用标签，买家可以在 AWS 账单控制台（[https://console.aws.amazon.com/costmanagement/](https://console.aws.amazon.com/costmanagement/)）中查看按标签值划分为使用量的费用。供应商计量标记不会更改您报告的价格、维度或总用量。它允许您的客户按您产品的相应类别查看成本。

在常见情形中，买家会通过一个 AWS 账户订阅您的产品。买家还有许多与同一产品订阅相关的用户。您可以使用具有 `AccountId` 键的标签创建用量分配，然后将用量分配给每个用户。在这种情况下，买家可以在其账单与成本管理控制台中激活 `AccountId` 标签并分析个人用户的使用情况。

### 卖家体验
<a name="container-vendor-metered-tag-seller"></a>

卖家可以汇总具有相同标签集的资源的计量记录，而不是汇总所有资源的使用情况。例如，卖家可以构造包含不同的 `UsageAllocations` 存储桶的计量记录。每个存储桶代表一组标签的 `UsageQuantity`，例如 `AccountId` 和 `BusinessUnit`。

在下图中，**资源 1** 具有一组唯一的 `AccountId` 和 `BusinessUnit` 标签，并作为单个条目出现在**计量记录**中。

**资源 2** 和**资源 3** 都具有相同的 `AccountId` 标签 `2222` 和相同的 `BusinessUnit` 标签 `Operations`。因此，它们在**计量记录**中合并为一个 `UsageAllocations` 条目。

![\[该图显示了供应商计量标签如何合并用量数据。三个具有不同 AccountIds 和的资源（资源 1、2 和 3）在发送到 M AWS arketplace 计量服务 BusinessUnit 之前，将 UsageAllocations 按分组 AccountId 并整合到单个计量记录中。 BusinessUnits\]](http://docs.aws.amazon.com/zh_cn/marketplace/latest/userguide/images/seller-vendor-meter-tag.png)


卖家还可以将没有标签的资源组合成一个包含分配用量的 `UsageAllocation`，然后将其作为 `UsageAllocations` 中的一个条目发送。

限制包括：
+ 标签数 – 5
+ `UsageAllocations`（基数）的大小 – 2500

验证包括：
+ 标签键和值允许使用的字符 — a-zA-Z 0-9\$1-= 。 \$1:\$1 /@
+ `UsageAllocation` 列表中的最大标签数 – 5
+ 两个 `UsageAllocations` 的标签不能相同（也就是说，标签键和值的组合相同）。如果是这样的话，他们必须使用相同的 `UsageAllocation`。
+ `UsageAllocation` 的 `AllocatedUsageQuantity` 之和必须等于 `UsageQuantity`，即聚合用量。

### 买家体验
<a name="container-vendor-metered-tag-buyer"></a>

下表显示了买家激活 `AccountId` 和 `BusinessUnit` 供应商标签后的买家体验示例。

在此示例中，买家可以在其**成本使用报告**中看到分配的用量。供应商计量标签使用前缀 `“aws:marketplace:isv”`。在账单与成本管理中，买家可以再**成本分配标签**下的 **AWS生成的成本分配标签**中激活它们。

**成本使用报告**的第一行和最后一行与卖家向 Metering Service 发送的内容相关（如 [卖家体验](#container-vendor-metered-tag-seller) 示例所示）。


**成本使用报告（简化）**  

| ProductCode  | 买家 | UsageDimension | UsageQuantity | `aws:marketplace:isv:AccountId ` | `aws:marketplace:isv:BusinessUnit` | 
| --- | --- | --- | --- | --- | --- | 
| xyz | 111122223333 | 网络：每 (GB) 检查一次  | 70 | 2222 | 运营 | 
| xyz | 111122223333 | 网络：每 (GB) 检查一次  | 30 | 3333 | 财务 | 
| xyz | 111122223333 | 网络：每 (GB) 检查一次  | 20 | 4444 | IT | 
| xyz | 111122223333 | 网络：每 (GB) 检查一次  | 20 | 5555 | 市场营销 | 
| xyz | 111122223333 | 网络：每 (GB) 检查一次  | 30 | 1111 | 市场营销 | 

有关代码示例，请参阅 [带有用量分配标签的 `MeterUsage` 代码示例（可选）](#container-meterusage-code-example)。

## 代码示例
<a name="container-meter-code-example"></a>

以下代码示例旨在帮助您将容器产品与发布和维护产品 AWS Marketplace APIs 所需的产品集成。

### 带有用量分配标签的 `MeterUsage` 代码示例（可选）
<a name="container-meterusage-code-example"></a>

以下代码示例与具有消费定价模式的容器产品相关。Python 示例将带有相应使用量分配标签的计量记录发送给您的客户 AWS Marketplace ，以向您的客户收取 pay-as-you-go费用。

```
# NOTE: Your application will need to aggregate usage for the 
#       customer for the hour and set the quantity as seen below. 
# AWS Marketplace can only accept records for up to an hour in the past. 
#
# productCode is supplied after the AWS Marketplace Ops team has 
# published the product to limited

# Import AWS Python SDK
import boto3
import time

usageRecord = [
    { 
        "AllocatedUsageQuantity": 2, 
        "Tags": 
            [ 
                { "Key": "BusinessUnit", "Value": "IT" },
                { "Key": "AccountId", "Value": "123456789" },
            ]

    },
    { 
        "AllocatedUsageQuantity": 1, 
        "Tags": 
            [ 
                { "Key": "BusinessUnit", "Value": "Finance" },
                { "Key": "AccountId", "Value": "987654321" },
            ]

    }
]

marketplaceClient = boto3.client("meteringmarketplace")

response = marketplaceClient.meter_usage(
    ProductCode="testProduct",
    Timestamp=int(time.time()),
    UsageDimension="Dimension1",
    UsageQuantity=3,
    DryRun=False,
    UsageAllocations=usageRecord 
)
```

有关的更多信息`MeterUsage`，请参阅 *AWS Marketplace Metering Service API 参考[MeterUsage](https://docs.aws.amazon.com/marketplacemetering/latest/APIReference/API_MeterUsage.html)*中的。

### 响应示例
<a name="container-meterusage-code-response"></a>

```
{ "MeteringRecordId": "string" }
```

# 使用自定义计量将您的容器产品与 AWS Marketplace Metering Service 和集成 适用于 Java 的 AWS SDK
<a name="java-integration-example-meterusage"></a>

AWS Marketplace 集装箱产品可以对每种产品多达 24 种不同的定价维度进行自定义计量。要启用自定义计量，请将您的容器产品与 AWS Marketplace Metering Service 集成。您可以使用 [https://docs.aws.amazon.com/marketplacemetering/latest/APIReference/API_MeterUsage.html](https://docs.aws.amazon.com/marketplacemetering/latest/APIReference/API_MeterUsage.html) API 操作为该用量定义自己的定价单位和自定义计量以便 AWS 进行计费。以下示例概述了使用与[AWS Marketplace 计量服务](https://docs.aws.amazon.com/marketplacemetering/latest/APIReference/Welcome.html)`MeterUsage`操作集成的实现。 适用于 Java 的 AWS SDK 

有关完整的详细信息，请参阅 [`MeterUsage` Java 示例](#meterusage-java-example)。以下步骤中的许多步骤适用于任何语言。

**示例： AWS Marketplace 计量服务集成**

1. 登录到 [AWS Marketplace 管理门户](https://aws.amazon.com/marketplace/management/tour)。

1. 从**资产**中，选择**容器**以开始创建新容器产品。创建产品会生成产品的产品代码以与您的容器映像集成。有关设置 AWS Identity and Access Management (IAM) 权限的信息，请参阅[AWS Marketplace 计量和授权 API 权限](iam-user-policy-for-aws-marketplace-actions.md)。

1.  下载公开的 [AWS Java SDK](https://aws.amazon.com/sdk-for-java/)。
**重要**  
 要从 Amazon Elastic Kubernetes Service (Amazon EKS) 调用计量 API 操作，您必须[使用支持的](https://docs.aws.amazon.com/eks/latest/userguide/iam-roles-for-service-accounts-minimum-sdk.html) AWS SDK 并在运行 Kubernetes 1.13 或更高版本的 Amazon EKS 集群上运行。

1. 针对每个维度使用情况，每小时从任务或容器组 (pod) 调用一次 `MeterUsage` 操作。此 API 操作对于 `Dimension`、`Resource` 和 `Hour` 的唯一组合接受一个计量记录。资源要么是 Amazon Elastic Container Service (Amazon ECS) 任务，要么是 Amazon EKS 容器组 (pod)。

   ```
   {
       "ProductCode" : "string", // (required)
       "UsageDimension" : "string", // (required)
       "UsageQuantity":  int, // (optional) Default is 0. Acceptable value from [0, 2147483647 (INT_MAX)]
       "Timestamp": Date, // (required) Timestamp in UTC. Value can be one hour in the past.
       "UsageAllocations": List<UsageAllocation> // (optional) UsageAllocations across 1 or more tags.
   }
   ```
**注意**  
在连接时可能会出现暂时性问题 AWS Marketplace Metering Service。 AWS Marketplace 强烈建议实施最长 30 分钟的重试，并以指数级退缩，以避免短期中断或网络问题。

1. 重建包含 `MeterUsage` 调用的新版本容器映像，标记容器，然后将其推送至与 Amazon ECS 或 Amazon EKS 兼容的任何 Docker 注册表，如 Amazon Elastic Container Registry (Amazon ECR)。如果您使用的是 Amazon ECR，请确保启动 Amazon ECS 任务或 Amazon EKS 容器组 (pod) 的账户对 Amazon ECR 存储库拥有权限。否则，该操作将失败。

1. 创建一个 [IAM](https://aws.amazon.com/iam/) 角色来授予容器调用 `MeterUsage` 的权限，如以下代码示例所定义。您必须在 Amazon ECS 任务的[任务角色](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task_definition_parameters.html#task_role_arn)参数或 Amazon EKS 容器定义中提供此 AWS Identity and Access Management (IAM) 角色。

------
#### [ JSON ]

****  

   ```
   {
       "Version":"2012-10-17",		 	 	 
       "Statement": [
           {
               "Action": [
                   "aws-marketplace:MeterUsage"
                   ],
                   "Effect": "Allow",
                   "Resource": "*"
           }
       ]
   }
   ```

------

1. 创建 Amazon ECS 任务或 Amazon EKS 容器定义，引用已与您在步骤 6 中创建的 IAM 角色集成的容器 AWS Marketplace 并引用该角色。如果要查看日志记录，请在任务定义中启用 AWS CloudTrail 日志记录。

1. 创建 Amazon ECS 或 Amazon EKS 集群来运行您的任务或容器组 (pod)。有关创建 Amazon ECS 集群的更多信息，请参阅《Amazon Elastic Container Service 开发人员指南》**中的[创建集群](https://docs.aws.amazon.com/AmazonECS/latest/userguide/create_cluster.html)。有关（使用 Kubernetes 版本 1.1.3.x 或更高版本）创建 Amazon EKS 集群的更多信息，请参阅[创建 Amazon EKS 集群](https://docs.aws.amazon.com/eks/latest/userguide/create_cluster.html)。

1. 配置 Amazon ECS 或 Amazon EKS 集群，然后在 us-east-1 AWS 区域启动您在步骤 8 中创建的 Amazon ECS 任务定义或亚马逊 EKS 容器。只有在此测试过程中，在产品上线之前，您才必须使用此区域。

1. 当您从正在为产品发布的每个维度的 `MeterUsage` 获得有效相应时，您可以开始创建容器产品。如有问题，请联系 [AWS Marketplace 卖家运营](https://aws.amazon.com/marketplace/management/contact-us/)团队。

## `MeterUsage` Java 示例
<a name="meterusage-java-example"></a>

以下代码示例使用 适用于 Java 的 AWS SDK 和 AWS Marketplace 计量服务来调用该`MeterUsage`操作。

使用以下示例代码调用 `MeterUsage` 操作，不设置 `UsageAllocations`。

```
import com.amazonaws.services.marketplacemetering.AWSMarketplaceMetering;
import com.amazonaws.services.marketplacemetering.AWSMarketplaceMeteringClientBuilder;
import com.amazonaws.services.marketplacemetering.model.MeterUsageRequest;
import com.amazonaws.services.marketplacemetering.model.MeterUsageResult;

import java.util.Date;

public class MeterUsage {
    private static final String PRODUCT_CODE = ".......";
    private final AWSMarketplaceMetering awsMarketplaceMetering;

    public MeterUsage() {
        awsMarketplaceMetering = AWSMarketplaceMeteringClientBuilder.standard().build();
    }

    /**
     * Submits metering record for a FCP Dimension. The API accepts 1 metering record per dimension
     * for a given buyer's resource for a given timestamp hour. Ex. If a buyer is running 10 tasks,
     * the API will accepts 1 call to MeterUsage in an hour for a given dimension for each running task.
     *
     * @param dimension - FCP dimension name provided during the publishing of the product.
     * @param quantity - FCP dimension consumption value for the hour.
     * @param timestamp - Timestamp, in UTC, for which the usage is being reported.
     *                  Timestamp cant be more than 1 hour in the past.
     *                  Make sure the timestamp value is not before the start of the software usage.
     */
    public void callMeterUsage(String dimension, int quantity, Date timestamp) {
        MeterUsageRequest meterUsageRequest = new MeterUsageRequest()
                .withProductCode(PRODUCT_CODE)
                .withUsageDimension(dimension)
                .withUsageQuantity(quantity)
                .withTimestamp(timestamp);
        MeterUsageResult meterUsageResult = awsMarketplaceMetering.meterUsage(meterUsageRequest);
    }
}
```

使用以下示例代码调用 `MeterUsage` 操作，设置 `UsageAllocations`。

```
private static String callMeterUsageWithAllocationsByTag(AWSMarketplaceMetering marketplaceMetering) {
        // Tag Keys for the product
        String tagKey1 = "Key1";
        String tagKey2 = "Key2";
        String tagKey3 = "Key3";

        // 1st Usage Allocation bucket which has two Tags [{Key1, Key1Value1},{Key2, Key2Value1}]
        List<Tag> tagsForUsageAllocation1 = Arrays.asList(new Tag().withKey(tagKey1).withValue("Key1Value1"),
                new Tag().withKey(tagKey2).withValue("Key2Value1"));
        UsageAllocation usageAllocation1 = new UsageAllocation()
                .withTags(tagsForUsageAllocation1)
                .withAllocatedUsageQuantity(20);

        // 2nd Usage Allocation bucket which has two Tags [{Key1, Key1Value2},{Key2, Key2Value1}]
        List<Tag> tagsForUsageAllocation2 = Arrays.asList(new Tag().withKey(tagKey1).withValue("Key1Value2"),
                new Tag().withKey(tagKey2).withValue("Key2Value1"));
        UsageAllocation usageAllocation2 = new UsageAllocation()
                .withTags(tagsForUsageAllocation2)
                .withAllocatedUsageQuantity(20);

        // 3rd Usage Allocation bucket which has two Tags [{Key1, Key1Value2},{Key2, Key2Value2},{Key3, Key3Value1}]
        List<Tag> tagsForUsageAllocation3 = Arrays.asList(new Tag().withKey(tagKey1).withValue("Key1Value2"),
                new Tag().withKey(tagKey2).withValue("Key2Value2"),
                new Tag().withKey(tagKey3).withValue("Key3Value1"));
        UsageAllocation usageAllocation3 = new UsageAllocation()
                .withTags(tagsForUsageAllocation3)
                .withAllocatedUsageQuantity(15);

        // 4th Usage Allocation bucket with no tags
        UsageAllocation usageAllocation4 = new UsageAllocation()
                .withAllocatedUsageQuantity(15);

        List<UsageAllocation> usageAllocationList = Arrays.asList(usageAllocation1,
                usageAllocation2,
                usageAllocation3,
                usageAllocation4);

        MeterUsageRequest meterUsageRequest = new MeterUsageRequest()
                .withProductCode("TestProductCode")
                .withUsageDimension("Dimension1")
                .withTimestamp(new Date())
                //UsageQuantity value must match with sum of all AllocatedUsageQuantity
                .withUsageQuantity(70)
                .withUsageAllocations(usageAllocationList);

        MeterUsageResult meterUsageResult;
        try {
            meterUsageResult = marketplaceMetering.meterUsage(meterUsageRequest);
        } catch (Exception e) {
            // Log Error
            throw e;
        }

        return meterUsageResult.getMeteringRecordId();
    }
```

# 集装箱产品的合同定价 AWS License Manager
<a name="container-license-manager-integration"></a>

对于采用合同定价的基于容器的产品，您可以使用 AWS License Manager 将许可证与您的产品相关联。 AWS License Manager 是一种许可证管理工具，可让您的应用程序跟踪和更新客户购买的许可证（也称为授权）。本节提供有关如何将您的产品与集成的信息 AWS License Manager。集成完成后，您可以在 AWS Marketplace上发布您的产品清单。

如果你要将 License Manager 与 AWS Marketplace 适用于亚马逊 EKS Anywhere、Amazon ECS Anywhere、Amazon Elastic Compute Cloud (Amazon EC2) 或本地基础设施的 For Containers Anywhere 产品集成，请按照中的说明进行操作。[AWS Marketplace 使用 License Manager 集成 for Container](container-anywhere-license-manager-integration.md)

有关的更多信息 AWS License Manager，请参阅《[AWS License Manager 用户指南》和《AWS CLI](https://docs.aws.amazon.com/license-manager/latest/userguide/license-manager.html)*命令参考*》一[AWS License Manager](https://docs.aws.amazon.com/cli/latest/reference/license-manager/index.html)节。

有关通过合同定价 AWS License Manager 与集装箱产品集成的更多信息，请参阅*AWS Marketplace 卖方研讨*会的 “[与前期付款集成](https://catalog.workshops.aws/mpseller/en-US/container/integrate-contract)” 实验室。

**Topics**
+ [容器产品的合同定价](#container-contracts)
+ [许可模式](#container-LM-license-models)
+ [AWS License Manager 集成先决条件](#container-LM-prereqs)
+ [将容器产品与 License Manager 集成](#container-integrate-with-LM)
+ [License Manager API 操作](#container-LM-API-calls)
+ [许可证续订和升级](#container-LM-lic-renew-upgrade)
+ [AWS Marketplace 使用 License Manager 集成 for Container](container-anywhere-license-manager-integration.md)

## 容器产品的合同定价
<a name="container-contracts"></a>

对于采用合同定价的基于集装箱的产品，根据您与客户之间的合同，提前或按照您定义的付款时间表向客户开具 AWS Marketplace 账单。之后，客户将有权使用那些资源。

要设置您的定价，请选择您向客户提供的一个或多个合同期限。您可以为每个合同期限输入不同的价格。您可以选择 1 个月、12 个月、24 个月和 36 个月的合同期限。对于专属优惠，您可以指定以月为单位的自定义期限（最多 60 个月）。

选择最能描述您的产品定价的类别。定价类别会显示在 AWS Marketplace 网站上。您可以选择**带宽** (GB/s, MB/s)、**数据**（GB、MB、TB）、**主机**、**请求**、**层**或**用户**。如果所有预定义的类别均无法满足您的需求，您可以选择更通用的**单位**类别。

该优惠允许向其中添加多达 24 个维度。


**示例：数据存储应用程序**  

|   | 1 个月价格 | 12 个月价格  | 24 个月价格  | 36 个月价格  | 
| --- | --- | --- | --- | --- | 
|  未加密的数据 (GB)  |  1.50 美元/GB  |  16.00 美元/GB  |  30.00 美元/GB  |  60.00 美元/GB  | 
|  加密的数据 (GB)  |  1.55 美元/GB  |  16.60 美元/GB  |  31.20 美元/GB  |  61.20 美元/GB  | 


**示例：日志监控产品**  

|   | 1 个月价格 | 12 个月价格  | 24 个月价格 | 36 个月价格 | 
| --- | --- | --- | --- | --- | 
|  基本（监控 10 台主机，监控 5 个容器）  |  100 USD  |  1000 美元  | 2000 美元  | 4000 美元 | 
|  标准（监控 20 台主机，监控 10 个容器）  |  \$1200  |  2000 美元  | 4000 美元  | 8000 美元 | 
|  专业（监控 40 台主机，监控 20 个容器）  |  400 美元  |  4000 美元  | 8000 美元  | 16,000 美元 | 
|  额外主机的每小时监控成本  | 10 美元  | 100 USD  |  \$1200 | 400 美元 | 
|  额外容器的每小时监控成本  | 10 美元  | 100 USD  |  \$1200 | 400 美元 | 

**注意**  
价格可以采用以下期限：1 个月、12 个月、24 个月或 36 个月。可以选择为产品提供这些选项中的一个或多个选项。期限对于每个维度都必须相同。  

**Example**  
例如，在您有`ReadOnlyUsers`和`AdminUsers`维度的情况下，如果您为其提供年度价格 ReadOnlyUsers，则还必须为`AdminUsers`其提供年度价格。


### 自动续订
<a name="ami-contracts-automatic-renewals"></a>

 当客户 AWS Marketplace 使用集装箱合同购买您的产品时，他们可以同意自动续订合同条款。客户继续按每月或按 1 年、2 年或 3 年期限支付权利费用。

客户可以随时修改续订设置。有关更多信息，请参阅《AWS Marketplace 买家指南》**中的[修改现有合同](https://docs.aws.amazon.com/marketplace/latest/buyerguide/buyer-container-contracts.html#modify-existing-contract)。

## 许可模式
<a name="container-LM-license-models"></a>

AWS Marketplace 与集成 AWS License Manager 支持两种许可模式：
+ [可配置许可模式](#container-LM-config-lic-model)
+ [分层许可模式](#container-LM-tiered-lic-model)

### 可配置许可模式
<a name="container-LM-config-lic-model"></a>

可配置许可证模型（也称为可量化许可证模型）在买家获得许可证后授权买家获得特定数量的资源。

您可以设置定价维度和每单位价格。然后，买家可以选择他们想要购买的资源数量。

**Example 定价维度和每单位价格**  
您可以设置定价维度（例如数据备份）和每单位价格（例如每单位 30 美元）  
买家可以选择购买 5、10 或 20 个单位。  
您的产品会跟踪和计量使用情况，以衡量消耗的资源数量。

在配置模式下，权利将通过以下两种方式之一进行计数：
+ [消耗许可证](#container-floating-lic)
+ [浮动许可证](#container-floating-lic) 

#### 消耗许可证
<a name="container-drawndown-lic"></a>

 在使用时，许可证是从允许数量的许可证池中消耗。该权利已永久签出，无法返回到许可证池。

**Example 处理有限数量的数据示例**  
用户有权处理 500 GB 的数据。当他们继续处理数据时，系统会从 500 GB 的池中消耗数量，直到所有 500 GB 的许可证都用完为止。

对于扣除许可证，您可以使用 `CheckoutLicense` API 操作来查看已消耗的许可证单位（权利）。

**Example 每年备份到 Amazon S3 的多个单位示例**  
您拥有的存储产品允许将多达 1,024 个单位的数据备份到 Amazon Simple Storage Service 中以存储一年的数据。您的应用程序可以通过使用多个 Amazon EC2 实例启动。您的应用程序具有跟踪和聚合数据的机制。您的软件在每次备份时或以固定的时间间隔使用产品 ID 调用 `CheckoutLicense` API 操作来更新消耗的数量。  
在此示例中，您的软件调用 `CheckoutLicense` API 操作以签出 10 个单位的数据。当总容量达到客户购买的备份限制时，API 调用失败。

**请求**

```
linux-machine ~]$ aws license-manager checkout-license\
--product-sku "2205b290-19e6-4c76-9eea-377d6bf7la47" \
--checkout-type "PERPETUAL" \
--key-fingerprint "aws:294406891311:AWS/Marketplace:issuer-fingerprint" \
--entitlements "Name=DataConsumption, Value=l0, Unit=Count" \
--client-token "AKIAIOSFODNN7EXAMPLE"
```

**响应**

```
{"CheckoutType": "PERPETUAL",
"EntitlementsAllowed": [{
"Name": "IntermediateTier",
"Units": "None"
}],
"Expiration": "2021-04-22Tl9:02:36",
"IssuedAt": "2021-04-22Tl8:02:36",
"LicenseArn": "arn:aws:license-manager::294406891311:license:l-16bf01b...",
"LicenseConsumptionToken": "AKIAIOSFODNN7EXAMPLE"
}
```

#### 浮动许可证
<a name="container-floating-lic"></a>

 使用后，许可证将返回到允许数量的许可证池中。

对于浮动许可证，在使用资源时，应用程序使用 `CheckoutLicense` API 操作从权利池中签出权利。`CheckoutLicense` API 操作的响应包括许可证消耗令牌，该令牌是签出的唯一标识符。许可证消耗令牌可用于对已签出的权利执行其他操作，例如将其签回许可证池或延长签出时间。

在不再使用资源时，要将权利签回池中，请使用 `CheckInLicense` API 操作。

```
aws license-manager check-in-license --license-consumption-token "f1603b3c1f574b7284db84..."
```

如果未能签入权利（比如应用程序崩溃），则权利将在 60 分钟后自动签回池中。如果资源的使用时间超过 60 分钟，则只要资源在使用中，最佳实操是使用 `ExtendLicenseConsumption` API 操作保持权利从池中签出。

```
aws license-manager extend-license-consumption --license-consumption-token "f1603b3c1f574b7284..."
```

**Example 固定上限的用户数示例**  
某用户被授予应用程序 500 个并发用户的权利。当用户登录和注销时，用户会被消耗并返回到 500 个用户的池中。但是，应用程序无法从池中消耗超过 500 个用户，因为 500 个并发用户是固定上限。

对于浮动权利，您可以使用 `CheckInLicense` API 操作将许可证单位返回到权利池。

**Example 一年的并发用户数示例**  
您的产品根据并发用户数量定价。客户为 10 个用户购买了一份为期一年的许可证。客户通过提供 AWS Identity and Access Management (IAM) 权限来启动软件。当用户登录时，您的应用程序会调用 `CheckoutLicense` API 操作将数量减少 1。当用户注销时，应用程序会通过调用 `CheckInLicense` API 操作将该许可证返回到池中。如果您不调用 `CheckInLicense`，则许可证单位将在 1 小时后自动签入。

**注意**  
在以下请求中，`key-fingerprint` 不是占位符值，而是发布所有许可证时使用的指纹的实际值。

**请求**

```
aws license-manager checkout-license\
--product-sku "2205b290-19e6-4c76-9eea-377d6bf7la47" \
--checkout-type "PROVISIONAL" \
--key-fingerprint "aws:294406891311:AWS/Marketplace:issuer-fingerprint" \
--entitlements "Name=ReadOnlyUSers, Value=l0, Unit=Count" \
--client-token "AKIAIOSFODNN7EXAMPLE"
```

**响应**

```
{
  "CheckoutType": "PROVISIONAL",
  "EntitlementsAllowed": [
    {
      "Name": "ReadOnlyUsers", 
      "Count": 10,
      "Units": "Count",
      "Value": "Enabled"
    }
},
  "Expiration": "2021-04-22Tl9:02: 36",
  "IssuedAt": "2021-04-22Tl8:02:36",
  "LicenseArn": "arn:aws:license-manager::294406891311:license:l-16bf01b...",
  "LicenseConsumptionToken": "AKIAIOSFODNN7EXAMPLE"
}
```

### 分层许可模式
<a name="container-LM-tiered-lic-model"></a>

分层许可模式在买家获得许可后，使买家有权使用特定级别或层的应用程序特征。

您可以为产品创建层，例如基本、中级和高级。然后，买家选择一个预定义的层。

应用程序无需跟踪或计量应用程序的使用情况。

在分层许可模式下，权利不计算在内，而是表示客户购买的服务层。

如果您想提供捆绑特征，则最好使用层。

**Example 基本、中级和高级层**  
客户可以签署软件三个可能层之一的合同：基础、中级或高级。每个层都有自己的定价。您的软件可以通过调用 `CheckoutLicense` API 操作并在请求中指定所有可能的层来识别客户已注册的层。  
请求响应包含与客户购买的层相对应的权利。基于这些信息，该软件可以预置适当的客户体验。

#### 请求
<a name="container-LM-tiered-request"></a>

```
linux-machine  ~]$ aws  license-manager   checkout-license\
--product-sku  "2205b290-19e6-4c76-9eea-377d6bf7la47"  \
--checkout-type  "PROVISIONAL"  \
--key-fingerprint  "aws:294406891311:AWS/Marketplace:issuer-fingerprint" \
--entitlements  "Name=BasicTier,  Unit=None"   "Name=IntermediateTier,  Unit=None"	\ "Name=PremiumTier, Unit=None"
```

#### 响应
<a name="container-LM-tiered-response"></a>

```
{
  "CheckoutType": "PROVISIONAL",
  "EntitlementsAllowed": [
    {
      "Name": "IntermediateTier", 
      "Units": "None"
    }
},
  "Expiration": "2021-04-22Tl9:02:36",
  "IssuedAt": "2021-04-22Tl8:02:36",
  "LicenseArn": "arn:aws:license-manager::294406891311:license:l-16bf01b...",
  "LicenseConsumptionToken": "AKIAIOSFODNN7EXAMPLE"
}
```

## AWS License Manager 集成先决条件
<a name="container-LM-prereqs"></a>

发布产品之前，您必须首先完成以下操作：

1. 在中创建新的容器产品 AWS Marketplace 管理门户，并记下其产品代码。

   有关更多信息，请参阅 [概述：创建容器产品](container-product-getting-started.md#create-container-product)。

1. 将用于运行应用程序的任务或容器组 (pod) 的IAM 角色与调用 `CheckoutLicense`、`ExtendLicenseConsumption` 和 `CheckInLicense` API 操作所需的 IAM 权限搭配使用。

   以下 IAM 策略中详细介绍了所需的 IAM 权限。

------
#### [ JSON ]

****  

   ```
   {
      "Version":"2012-10-17",		 	 	 
      "Statement":[
         {
            "Sid":"VisualEditorO",
            "Effect":"Allow",
            "Action":[
               "license-manager:CheckoutLicense",
               "license-manager:GetLicense",
               "license-manager:CheckInLicense",
               "license-manager:ExtendLicenseConsumption",
               "license-manager:ListReceivedLicenses"
            ],
            "Resource":"*"
         }
      ]
   }
   ```

------

1. 使用您定义的所有定价维度的记录对 `RegisterUsage` API 操作进行测试调用。

## 将容器产品与 License Manager 集成
<a name="container-integrate-with-LM"></a>

**要将基于容器的产品与 License Manager 集成，请执行以下操作：**

1. 设置 IAM 权限以调用 License Manager。有关更多信息，请参阅 [AWS License Manager 集成先决条件](#container-LM-prereqs)。

1. 下载 AWS 软件开发工具包。
**注意**  
不要在软件中配置 AWS 凭据。 AWS 当您的容器在 Amazon EC2 实例、亚马逊 ECS 任务或 Amazon EKS 容器中运行时，系统会在运行时自动获取买家证书。

1. 向您的产品添加许可证检查。

   无论在何处执行许可证检查，您的产品都可以调用 `CheckoutLicense` API 操作。要查看许可证，您的产品必须了解：

   1. 许可证的可信颁发者 (AWS Marketplace)

   1. 应用程序的产品 SKU（产品 ID）

   1. 查看此应用程序的权利

   根据您设置的定价许可证类型，API 调用会有所不同。

1. 在上发布您的产品清单 AWS Marketplace。

## License Manager API 操作
<a name="container-LM-API-calls"></a>

要管理存储在客户 License Manager 帐户中的许可证，您的软件可以使用以下 API 操作：
+ `GetLicense` – 软件可以查询的 API。它会检索已购买许可证的状态（即已过期或即将到期），并向客户发送状态通知。
+ `CheckoutLicense` – 发现用户已购买的许可证。当用户使用了一定数量的许可证时，您还可以使用 `CheckoutLicense` API 操作来更新许可证数量。使用 `CheckoutLicense`，您可以继续签出客户使用的许可证数量。当客户耗尽所有许可证时，此调用将返回错误。有关建议运行 `CheckoutLicense` 的节奏的信息，请参阅[许可证续订和升级](#container-LM-lic-renew-upgrade)。
+ `ExtendLicenseConsumption` – 如果是浮动维度，当软件签出许可证时，许可证会在 60 分钟后自动返回到池中。如果您想延长许可证的签出时间，可以使用 `ExtendLicenseConsumption` API 操作将许可证再延长 60 分钟。
+ `CheckInLicense` – 如果是浮动维度，则要将许可证返回到权利池时，请使用 `CheckInLicense` 操作。
+ `ListReceivedLicenses` API – 列出买家购买的许可证。

## 许可证续订和升级
<a name="container-LM-lic-renew-upgrade"></a>

客户可以在 AWS Marketplace 管理门户上续订或升级许可证。在他们进行额外购买后， AWS Marketplace 会生成反映新权利的新版本的许可证。您的软件使用相同的 API 操作读取新的权利。您不必在 License Manager 集成方面执行任何不同的操作即可处理续订和升级。

由于许可证续订、升级、取消等，我们建议在使用产品时该产品按固定节奏调用 `CheckoutLicense` API 操作。通过定期使用 `CheckoutLicense` API 操作，产品可以检测权利的变化，例如升级和到期。

我们建议您每 15 分钟执行一次 `CheckoutLicense` API 调用。

# AWS Marketplace 使用 License Manager 集成 for Container
<a name="container-anywhere-license-manager-integration"></a>

作为 AWS Marketplace 卖家，您可以 AWS License Manager 与 AWS Marketplace 适用于亚马逊 EKS Anywhere、Amazon ECS Anywhere、Amazon EC2 或本地基础设施的 for Containers Anywhere 产品集成。以下部分提供了此集成的说明。

有关 License Manager 与集成的一般信息 AWS Marketplace，包括可用的许可模式，请参阅[集装箱产品的合同定价 AWS License Manager](container-license-manager-integration.md)。有关 AWS License Manager的更多信息，请参阅《[AWS License Manager 用户指南](https://docs.aws.amazon.com/license-manager/latest/userguide/license-manager.html)》和《AWS CLI 命令参考》**中的 [AWS License Manager](https://docs.aws.amazon.com/cli/latest/reference/license-manager/index.html) 部分。

**Topics**
+ [将 “容器随处 AWS Marketplace 可用” 产品与 License Manager 集成](#containers-anywhere-integrate-with-LM)
+ [在本地测试 License Manager 集成](#container-testing-LM-integration-locally)
+ [在 Amazon EKS 上测试 License Manager 集成](#container-testing-LM-integration-EKS)
+ [使用 License Manager 管理浮动许可证特权](#container-LM-floating-license)
+ [针对本地部署集成 License Manager 的最佳实操](#container-LM-best-practices-on-prem)
+ [`LicenseManagerCredentialsProvider` - Java 实现。](#container-license-manager-cred-provider-java)
+ [`LicenseManagerCredentialsProvider` - `Golang` 实现。](#container-license-manager-cred-provider-golang)

## 将 “容器随处 AWS Marketplace 可用” 产品与 License Manager 集成
<a name="containers-anywhere-integrate-with-LM"></a>

按照以下说明将你的 for Container AWS Marketplace s Anywhere 产品与集成 AWS License Manager。

**将 for Container AWS Marketplace s Anywhere 产品与 License M**

1. 打开 Web 浏览器并登录 [AWS Marketplace 管理门户](https://aws.amazon.com/marketplace/management/)。

1. 执行以下步骤，为您的容器产品创建产品 ID。您将在容器映像中使用此 ID，以便在后续步骤中进行许可证检查。

   1. 从菜单栏中，展开**资产**，然后选择**容器**。

   1. 为您的产品输入面向客户的名称，然后选择**创建**。您以后可以更改此名称。

   1. 记下**产品 ID**。之后在创建或更新产品定价详情时会使用它。
**提示**  
如果您丢失了产品 ID，则可以从 “**资产**” 菜单中选择 “容器”，在 “**容器**” 中找到它。 AWS Marketplace 管理门户 **容**器页面显示您的产品及其关联产品的列表 IDs。

1. 下载最新的公共 AWS SDK，然后将其安装到您的容器应用程序中。您可以在 [AWS 上构建的 AWS 工具中找到首选软件开发工具包的](https://aws.amazon.com/tools/)安装说明。
**注意**  
要从 Amazon EKS Anywhere 或未提供的 Kubernetes 集群调用 License Manager API 操作 AWS，您必须使用支持的软件开发工具包。 AWS 要查看支持的列表 AWS SDKs，请参阅[使用支持的 AWS SDK](https://docs.aws.amazon.com/eks/latest/userguide/iam-roles-for-service-accounts-minimum-sdk.html)。

1. 使用自定义凭证提供程序创建 AWS License Manager 客户端，以便它可以为部署在 AWS 本地和本地的容器应用程序提供凭证。有关自定义凭证提供程序的完整源代码 `LicenseCredentialProvider`，请参阅以下各部分：
   + [`LicenseManagerCredentialsProvider` - Java 实现。](#container-license-manager-cred-provider-java)
   + [`LicenseManagerCredentialsProvider` - `Golang` 实现。](#container-license-manager-cred-provider-golang)

    `LicenseCredentialsProvider`通过添加`LicenseManagerTokenCredentialsProvider`扩展 AWS SDK 的默认凭证提供程序链，以供本地使用。这通过在本地环境中使用 License Manager OIDC 颁发的身份令牌来提供凭证。必须在应用程序类路径中包含 `LicenseCredentialsProvider` 的源代码。
**注意**  
扩展`DefaultCredentialsProvider`允许同一个容器应用程序在本地环境中运行 AWS 和在本地环境中运行时获得证书。如果容器应用程序已经使用自定义凭证提供程序链而不是默认凭证提供程序链，则也可以通过将 `LicenseManagerTokenCredentialsProvider` 添加到自定义链中来进行扩展。

   以下代码片段是使用 Java 创建 AWS License Manager 客户端的示例。

   ```
   LicenseManagerClientBuilder clientBuilder = LicenseManagerClient.builder().credentialsProvider(LicenseCredentialsProvider.create());
   ```

1. 使用产品中每个付费容器映像中的 `aws license-manager checkout-license` 命令调用 `CheckoutLicense` API 操作。这将检查买家是否有权使用您的应用程序许可证。如果买家有权使用该应用程序，则 `CheckoutLicense` 成功并返回所请求的权利及其值。如果买家无权使用该应用程序，则 `CheckoutLicense` 会抛出异常。

   调用 `CheckoutLicense` API 操作时需要以下参数：
   + `CheckoutType` – 有效值为 `PROVISIONAL` 或 `PERPETUAL`。
     + `PERPETUAL` – 当已签出的权利池中的权利数量将用尽时使用。

       示例：买家有权处理 500 GB 的数据。当他们继续处理数据时，会消耗 500 GB 池的数量并将其耗尽。
     + `PROVISIONAL` 用于浮动许可证权利，其中权利从池中签出，并在使用后返回。

       示例：某用户被授予应用程序 500 个并发用户的权利。当用户登录或注销时，用户会被消耗或返回到 500 个用户的池中。要了解有关浮动许可证权利的更多信息，请参阅[使用 License Manager 管理浮动许可证特权](#container-LM-floating-license)。
   + `ClientToken` – 区分大小写的唯一标识符。我们建议为每个唯一请求使用随机的 UUID。
   + `Entitlements` – 待签出的权利列表。
     + 对于特征权利，请按以下方式提供 `Name` 和 `Unit` 属性。

       ```
       {
         "Name": "<Entitlement_Name>",
         "Unit": "None"
       }
       ```
     + 对于技术特权，请按以下方式提供 `Name`、`Unit` 和 `Count` 属性。

       ```
       {
         "Name": "<Entitlement_Name>",
         "Unit": "<Entitlement_Unit>",
         "Value": <Desired_Count>
       }
       ```
   + `KeyFingerprint` – 由 AWS Marketplace 颁发的许可证的密钥指纹是 `aws:294406891311:AWS/Marketplace:issuer-fingerprint`。使用此密钥指纹可确保许可证由不可靠的实体颁发 AWS Marketplace ，而不是由不可靠的实体颁发。
   + `ProductSKU`— 在之前的步骤 AWS Marketplace 管理门户 中生成的产品 ID。

   以下代码段是通过 AWS CLI使用 `CheckoutLicense` API 操作进行调用的示例。

   ```
   aws license-manager checkout-license \
   --product-sku "2205b290-19e6-4c76-9eea-377d6bf71a47" \
   --checkout-type "PROVISIONAL" \
   --client-token "79464194dca9429698cc774587a603a1" \
   --entitlements "Name=AWS::Marketplace::Usage/Drawdown/DataConsumption, Value=10, Unit=Gigabytes" \
   --key-fingerprint "aws:294406891311:AWS/Marketplace:issuer-fingerprint"
   ```
**注意**  
要查看许可证，容器应用程序需要出站网络访问权限才能使用 License Manager。部署在本地的应用程序可能会遇到出站网络访问不可靠或缓慢的情况。这些应用程序在调用 License Manager 时应包括够的重试次数。有关更多信息，请参阅 [针对本地部署集成 License Manager 的最佳实操](#container-LM-best-practices-on-prem)。

1. 定期调用 `CheckoutLicense` API 操作，以确定由于在 AWS Marketplace上进行续订、升级或取消而对客户的许可证进行的任何更改。调用频率取决于应用程序。我们建议每天检查一次许可证，以便在没有任何买家干预的情况下自动获取更改。

   部署在本地的应用程序可能具有不可靠的出站网络访问权限，无法定期检查许可证。在这种情况下，应用程序应使用缓存许可证以获得足够的弹性。有关更多信息，请参阅 [针对本地部署集成 License Manager 的最佳实操](#container-LM-best-practices-on-prem)。

1. 将 `CheckoutLicense` 调用与容器应用程序集成后，基于更改生成 Docker 容器映像的新版本。

1. 更新应用程序的 Helm 图表以接受 Kubernetes 密钥作为可选输入，其中包含使用许可证管理器访问许可证的配置。 APIs配置密钥将包含由 License Manager 颁发的身份令牌和一个 AWS Identity and Access Management 角色，前面描述的自定义凭据提供者将使用该角色来获取在本地部署容器应用程序 APIs 时调用 License Manager 的 AWS 凭证。另外，将 AWS 区域 作为输入添加，默认值为 `us-east-1`。

   在本地部署容器应用程序的买家可以通过容器产品的购买者体验创建 Kubernetes 秘密。 AWS Marketplace 提供 Kubernetes 密钥名称作为 `helm install` 命令的输入。配置密钥采用以下格式配置。

   ```
   apiVersion: v1
   kind: Secret
   metadata:
     name: aws-marketplace-license-config
   type: Opaque
   stringData:
     license_token: <token_value> // License Manager issued JWT token
     iam_role: <role_arn> // AWS Identity and Access Management role to assume with license token
   ```

1. 更新集成的容器镜像的 Helm 图表中的应用程序部署模板， AWS License Manager 使其包含以下内容：
   + 容器组 (pod) 的服务账户 – 在 Amazon EKS 上部署 Helm 需要服务账户。它用于通过在容器映像上为服务账号设置 IAM 角色来获得调用 License Manager API 操作的权限。有关服务账户的 IAM 角色的更多信息，请参阅[服务账户的 IAM 角色](https://docs.aws.amazon.com/eks/latest/userguide/iam-roles-for-service-accounts.html)。
   + 本地部署的许可证访问权限 – 需要许可证配置密钥才能提供凭证和相应权限，以便调用 License Manager API 操作，在本地环境中进行 Helm 部署。买家将根据 AWS Marketplace 买家体验生成许可证秘密并将其提供给 Helm。

   以下代码片段是示例部署规范，其中包含服务帐号、许可证配置和映像拉取密钥。

   ```
   apiVersion: apps/v1
   kind: Deployment
   metadata:
     name: example-app
   spec:
     replicas: 1
     selector:
       matchLabels:
         app: example-app
     template:
       metadata:
         labels:
           app: example-app
   spec:
         // Service account for pod
         serviceAccountName: {{ .Values.serviceAccountName }}
         containers:
           - name: example-app
             image: example-app
             ports:
               - containerPort: 8001
   // Add the following conditional attributes
   {{ - if .Values.awsmp.licenseConfigSecretName }}
             //Mount the license volume to the container image
             volumeMounts:
               - name: awsmp-product-license
                 mountPath: "/var/run/secrets/product-license"
             //Add following environment variable to container for credential
   provider
             env:
               - name: AWS_WEB_IDENTITY_REFRESH_TOKEN_FILE
                 value: "/var/run/secrets/product-license/license_token"
               - name: AWS_ROLE_ARN
                   valueFrom:
                       secretKeyRef:
                       name: {{ .Values.aws.licenseConfigSecretName }}
                       key: iam_role
         //Mount the license secret as a volume to the pod
         volumes:
           - name: awsmp-product-license
             secret:
               secretName: {{ .Values.aws.licenseConfigSecretName }}
               optional: true
   {{ - end }}
   ```
**注意**  
许可证配置密钥是可选的。买家仅在本地部署时使用该值。对于 AWS 部署，部署规范必须包含 License Manager 集成映像的服务帐户。

1. 通过执行以下各部分中的步骤，在本地和 Amazon EKS 上测试 License Manager 集成：

   1. [在本地测试 License Manager 集成](#container-testing-LM-integration-locally)

   1. [在 Amazon EKS 上测试 License Manager 集成](#container-testing-LM-integration-EKS)

1. 成功验证本地 AWS 和本地的 License Manager 集成后，您可以按照中的步骤创建容器产品清单[概述：创建容器产品](container-product-getting-started.md#create-container-product)。

## 在本地测试 License Manager 集成
<a name="container-testing-LM-integration-locally"></a>

你可以使用 minikube 或任何其他设置来在本地测试任何 Kubernetes 集群上的 License Manager 集成。确保 Kubernetes 集群具有出站互联网访问权限以调用 License Manager API 操作。

**要在本地测试 License Manager 集成，请执行以下操作：**

1. 使用所需权利在测试卖家账户中创建测试许可证。要设置测试许可证，请参阅 *AWS License Manager API 参考[CreateLicense](https://docs.aws.amazon.com/license-manager/latest/APIReference/API_CreateLicense.html)*中的。或者，使用以下脚本创建测试许可证，然后为测试买家账户创建许可授权，以使用该许可证。以下脚本使用测试卖家账户凭证。

   ```
   read -p 'AWS Account for test buyer: ' TEST_BUYER_ACCOUNT_ID
   read -p 'License entitlements: ' ENTITLEMENTS
   
   # TEST_SELLER_ACCOUNT_ID="109876543210"
   # ENTITLEMENTS="{\"Name\": \"ByData\",\"MaxCount\": 1000,\"Overage\":true,\"Unit\": \"Gigabits\",\"AllowCheckIn\": true}"
   
   # Create License
   
   NOW=$(date +"%Y-%m-%dT00:00:00+00:00")
   
   PRODUCT_NAME="My awesome product"
   PRODUCT_SKU="c97b7825-44c4-4f42-b025-12baa4c171e0"
   
   LICENSE_BENEFICIARY=" arn:aws:iam::$TEST_BUYER_ACCOUNT_ID:root "
   LICENSE_ISSUER_NAME="test-seller"
   LICENSE_NAME="test-seller-license"
   
   CLIENT_TOKEN="b3920968-a94f-4547-af07-3dd232319367"
   CONSUMPTION_TTL=180
   CONSUMPTION_RENEW_TYPE="None"
   
   HOME_REGION="us-east-1"
   
   LICENSE_ARN=$(aws license-manager create-license --license-name "$LICENSE_NAME" --product-name "$PRODUCT_NAME" --product-sku "$PRODUCT_SKU" --issuer Name="$LICENSE_ISSUER_NAME" --home-region "$HOME_REGION" --validity Begin="$NOW" --entitlements "$ENTITLEMENTS" --beneficiary "$LICENSE_BENEFICIARY" --consumption-configuration RenewType="$CONSUMPTION_RENEW_TYPE",ProvisionalConfiguration={MaxTimeToLiveInMinutes=$CONSUMPTION_TTL} --client-token "$CLIENT_TOKEN" | jq -r ".LicenseArn" )
   
   echo "License arn: $LICENSE_ARN"
   
   # Create Grant
   
   GRANT_TOKEN="e9a14140-4fca-4219-8230-57511a6ea6"
   GRANT_NAME="test-grant"
   
   GRANT_ARN=$(aws license-manager create-grant --grant-name "$GRANT_NAME" --license-arn "$LICENSE_ARN" --principals "$LICENSE_BENEFICIARY" --home-region "$HOME_REGION" --client-token "$GRANT_TOKEN" --allowed-operations "CheckoutLicense" "CheckInLicense" "ExtendConsumptionLicense" "CreateToken" | jq -r ".GrantArn")
   
   echo "Grant arn: $GRANT_ARN"
   ```

1. 采用之前定义的密钥格式，使用许可证令牌和 IAM 角色创建 Kubernetes 密钥。使用 License Manager `CreateToken` API 操作生成许可证令牌。然后，使用 IAM `CreateRole` API 操作创建具有权限和信任策略的 IAM 角色。请参阅以下示例脚本中的示例。以下脚本使用测试买家账户凭证。

   ```
   read -p 'AWS Account for test license: ' TEST_ACCOUNT_ID
   read -p 'License Arn' LICENSE_ARN
   # Create IAM Role
   ROLE_NAME="AWSLicenseManagerConsumptionTestRole"
   ROLE_DESCRIPTION="Role to test AWS License Manager integration on-prem"
   ROLE_POLICY_ARN="arn:aws:iam::aws:policy/service-role/AWSLicenseManagerConsumptionPolicy"
   ROLE_TRUST_POLICY="{\"Version\": \"2012-10-17\",\"Statement\": [{ \"Effect\":\"Allow\", \"Principal\": { \"Federated\": \"openid-license-manager.amazonaws.com\" }, \"Action\": \"sts:AssumeRoleWithWebIdentity\",\"Condition\": { \"ForAnyValue:StringLike\": { \"openid-license-manager.amazonaws.com:amr\": \"aws:license-manager:token-issuer-account-id:${TEST_ACCOUNT_ID}\" }}}]}"
   ROLE_SESSION_DURATION=3600
   
   ROLE_ARN=$(aws iam create-role --role-name "$ROLE_NAME" --description "$ROLE_DESCRIPTION" --assume-role-policy-document "$ROLE_TRUST_POLICY" --max-session-duration $ROLE_SESSION_DURATION | jq ".Role" | jq -r ".Arn")
   
   aws iam attach-role-policy --role-name "$ROLE_NAME" --policy-arn "$ROLE_POLICY_ARN"
   
   echo "Role arn: $ROLE_ARN"
   
   # Create Token
   CLIENT_TOKEN="b3920968-a94f-4547-af07-3dd232319367"
   
   TOKEN=$(aws license-manager create-token --license-arn $LICENSE_ARN --role-arns $ROLE_ARN --client-token $CLIENT_TOKEN | jq '.Token')
   
   echo "License access token: $TOKEN"c
   ```

1. 设置托管在外部的任何 Kubernetes 集群。 AWS使用它来测试容器应用程序是否可以从其他环境连接到 AWS License Manager API， AWS 以及自定义凭证提供程序是否已很好地集成到应用程序中。

1. 将之前生成的许可证令牌和 IAM 角色部署到本地 Kubernetes 集群中。

   ```
   kubectl create secret generic "awsmp-license-access-config" \
   --from-literal=license_token=${TOKEN} \
   --from-literal=iam_role=${ROLE_ARN}
   ```

1. 使用密钥名称作为输入通过 Helm 部署应用程序，并验证应用程序是否可以调用 License Manager API 操作来执行权利检查。有关 Helm 和部署规范的更改，请参阅[将 “容器随处 AWS Marketplace 可用” 产品与 License Manager 集成](#containers-anywhere-integrate-with-LM)中的步骤 9。

## 在 Amazon EKS 上测试 License Manager 集成
<a name="container-testing-LM-integration-EKS"></a>

您还可以在 Amazon EKS 上测试 License Manager 集成。测试以确保应用程序可以在没有许可证配置密钥的情况下调用 License Manager API 操作。此外，请确保服务账户可用于设置服务账户的 IAM 角色 (IRSA)，并为应用程序提供相关凭证。

**要在 Amazon EKS 上测试 License Manager 集成，请执行以下操作：**

1. 使用所需权利在测试卖家账户中创建测试许可证。请参阅 [CreateLicense API 参考](https://docs.aws.amazon.com/license-manager/latest/APIReference/API_CreateLicense.html)来设置您的测试许可证，或者使用以下脚本创建测试许可证，然后向测试买家账户创建使用许可证的许可授权。以下脚本使用测试卖家账户凭证。

   ```
   read -p 'AWS Account for test buyer: ' TEST_BUYER_ACCOUNT_ID
   read -p 'License entitlements: ' ENTITLEMENTS
   
   # TEST_SELLER_ACCOUNT_ID="109876543210"
   # ENTITLEMENTS="{\"Name\": \"ByData\",\"MaxCount\": 1000,\"Overage\": true,\"Unit\": \"Gigabits\",\"AllowCheckIn\": true}"
   
   # Create License
   
   NOW=$(date +"%Y-%m-%dT00:00:00+00:00")
   
   PRODUCT_NAME="My awesome product"
   PRODUCT_SKU="c97b7825-44c4-4f42-b025-12baa4c171e0"
   
   LICENSE_BENEFICIARY=" arn:aws:iam::$TEST_BUYER_ACCOUNT_ID:root "
   LICENSE_ISSUER_NAME="test-seller"
   LICENSE_NAME="test-seller-license"
   
   CLIENT_TOKEN="b3920968-a94f-4547-af07-3dd232319367"
   CONSUMPTION_TTL=180
   CONSUMPTION_RENEW_TYPE="None"
   
   HOME_REGION="us-east-1"
   
   LICENSE_ARN=$(aws license-manager create-license --license-name "$LICENSE_NAME" --product-name "$PRODUCT_NAME" --product-sku "$PRODUCT_SKU" --issuer Name="$LICENSE_ISSUER_NAME" --home-region "$HOME_REGION" --validity Begin="$NOW" --entitlements "$ENTITLEMENTS" --beneficiary "$LICENSE_BENEFICIARY" --consumption-configuration RenewType="$CONSUMPTION_RENEW_TYPE",ProvisionalConfiguration={MaxTimeToLiveInMinutes=$CONSUMPTION_TTL} --client-token "$CLIENT_TOKEN" | jq -r ".LicenseArn" )
   
   echo "License arn: $LICENSE_ARN"
   
   # Create Grant
   
   GRANT_TOKEN="e9a14140-4fca-4219-8230-57511a6ea6"
   GRANT_NAME="test-grant"
   
   GRANT_ARN=$(aws license-manager create-grant --grant-name "$GRANT_NAME" --license-arn "$LICENSE_ARN" --principals "$LICENSE_BENEFICIARY" --home-region "$HOME_REGION" --client-token "$GRANT_TOKEN" --allowed-operations "CheckoutLicense" "CheckInLicense" "ExtendConsumptionLicense" "CreateToken" | jq -r ".GrantArn")
   
   echo "Grant arn: $GRANT_ARN"
   ```

1. 创建包含所需配置的测试 Amazon EKS 集群，或运行以下命令以使用默认配置。

   ```
   aws ec2 create-key-pair --region us-west-2 --key-name eks-key-pair
   ```

   ```
   eksctl create cluster \
   --name awsmp-eks-test-example \
   --region us-west-2 \
   --with-oidc \
   --ssh-access \
   --ssh-public-key eks-key-pair
   ```

1. 为现有集群创建服务账户并将其与 IAM 角色关联。以下命令将使用 `AWSLicenseManagerConsumptionPolicy` 创建 IAM 角色。然后，该命令将其附加到应在其中部署 License Manager 集成映像的 Amazon EKS 集群的 `test_sa` 服务账户。这样一来，服务帐号可以获得相应的凭证来调用 License Manager API 操作。

   ```
   eksctl create iamserviceaccount \
   --name test_sa \
   --namespace test_namespace \
   --cluster awsmp-eks-test-example \
   --attach-policy-arn "arn:aws:iam::aws:policy/service-role/AWSLicenseManagerConsumptionPolicy" \
   --approve \
   --override-existing-serviceaccounts
   ```

1. 在服务账户中通过 Helm 部署应用程序，该账户通过前面的命令与 IAM 角色关联。验证应用程序是否可以调用 License Manager API 操作来执行权利检查。

## 使用 License Manager 管理浮动许可证特权
<a name="container-LM-floating-license"></a>

使用浮动许可证，当用户登录应用程序时，将从可用许可证池中消耗许可证。用户注销后，许可证将重新添加到可用许可证池中。

对于浮动许可证，应用程序使用 `CheckoutLicense` API 操作在使用资源时从权利池中签出权利。`CheckoutLicense` API 操作的响应包括许可证消耗令牌，该令牌是签出的唯一标识符。许可证消耗令牌可以对已签出的权利执行其他操作，例如将其签回许可证池或延长签出时间。

当资源不再使用时，应用程序会使用 `CheckInLicense` API 操作将权利签回池中。

```
aws license-manager check-in-license \
--license-consumption-token "f1603b3c1f574b7284db84a9e771ee12"
```

如果许可证未能签回池中，例如，如果应用程序在操作过程中崩溃，则权利将在 60 分钟后自动签回池中。因此，如果资源的使用时间超过 60 分钟，则最佳实操是将权利保留在资源池之外。为此，只要资源正在使用，就可以使用 `ExtendLicenseConsumption` API 操作。

```
aws license-manager extend-license-consumption \
--license-consumption-token "f1603b3c1f574b7284db84a9e771ee12"
```

## 针对本地部署集成 License Manager 的最佳实操
<a name="container-LM-best-practices-on-prem"></a>

本地环境中的容器应用程序部署可能会遇到不可靠的出站网络访问。使用以下最佳实操来增加弹性，以避免因互联网连接不畅导致的潜在问题而导致买家的服务中断：
+ **充分重试** — 临时网络问题可能会使您的应用程序无法连接到。 AWS License Manager实现最长 30 分钟的重试，并关闭指数级回退。这可以帮助避免短期中断或网络问题。
+ **避免硬限制** – 部署在联网集群中的应用程序可以定期检查许可证，以确定由于升级或续订而导致的任何更改。由于出站访问不可靠，应用程序可能无法识别这些更改。应用程序应尽可能避免因无法通过 License Manager 检查许可证而中断向买家提供的服务。当许可证到期时，应用程序可能会依赖免费试用或开源体验，并且无法检查许可证是否有效。
+ **通知客户** - 使用缓存许可证时，许可证的任何更改（包括续订或升级）都不会自动反映在正在运行的工作负载上。通知您的客户（他们必须暂时允许对应用程序进行出站访问），以便应用程序可以更新其缓存的许可证。例如，通过应用程序本身或通过其文档通知客户。同样，当回退到较低的功能集时，请通知客户其权利已用尽或许可证已过期。然后，他们可以选择升级或续订。

## `LicenseManagerCredentialsProvider` - Java 实现。
<a name="container-license-manager-cred-provider-java"></a>

`LicenseCredentialsProvider`通过添加`LicenseManagerTokenCredentialsProvider`扩展 AWS SDK 的默认凭证提供程序链，以供本地使用。

**`LicenseCredentialsProvider`**

```
package com.amazon.awsmp.license;

import software.amazon.awssdk.auth.credentials.AwsCredentials;
import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
import software.amazon.awssdk.auth.credentials.AwsCredentialsProviderChain;
import software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider;
import software.amazon.awssdk.auth.credentials.internal.LazyAwsCredentialsProvider;
import software.amazon.awssdk.utils.SdkAutoCloseable;

public class LicenseCredentialsProvider implements AwsCredentialsProvider, SdkAutoCloseable {
    private static final LicenseCredentialsProvider CREDENTIALS_PROVIDER = new LicenseCredentialsProvider();
    private final LazyAwsCredentialsProvider providerChain;

    private LicenseCredentialsProvider() {
        this.providerChain = createChain();
    }

    public static LicenseCredentialsProvider create() {
        return CREDENTIALS_PROVIDER;
    }

    @Override
    public AwsCredentials resolveCredentials() {
        return this.providerChain.resolveCredentials();
    }

    @Override
    public void close() {
        this.providerChain.close();
    }

    private LazyAwsCredentialsProvider createChain() {
        return LazyAwsCredentialsProvider.create(() -> {
            AwsCredentialsProvider[] credentialsProviders = new AwsCredentialsProvider[]{
                    DefaultCredentialsProvider.create(),
                    LicenseManagerTokenCredentialsProvider.create()};

            return AwsCredentialsProviderChain.builder().reuseLastProviderEnabled(true)
                    .credentialsProviders(credentialsProviders).build();
        });
    }
}
```

**`LicenseManagerTokenCredentialsProvider`**

`LicenseManagerTokenCredentialsProvider` 通过在本地环境中使用 License Manager OIDC 颁发的身份令牌来提供凭证。必须在应用程序类路径中包含 `LicenseCredentialsProvider` 的源代码。

```
package com.amazon.awsmp.license;

import software.amazon.awssdk.auth.credentials.AnonymousCredentialsProvider;
import software.amazon.awssdk.auth.credentials.AwsCredentials;
import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
import software.amazon.awssdk.core.SdkSystemSetting;
import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration;
import software.amazon.awssdk.core.retry.RetryPolicyContext;
import software.amazon.awssdk.core.retry.conditions.OrRetryCondition;
import software.amazon.awssdk.core.retry.conditions.RetryCondition;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.regions.providers.DefaultAwsRegionProviderChain;
import software.amazon.awssdk.services.licensemanager.LicenseManagerClient;
import software.amazon.awssdk.services.licensemanager.model.GetAccessTokenRequest;
import software.amazon.awssdk.services.licensemanager.model.GetAccessTokenResponse;
import software.amazon.awssdk.services.sts.StsClient;
import software.amazon.awssdk.services.sts.auth.StsAssumeRoleWithWebIdentityCredentialsProvider;
import software.amazon.awssdk.services.sts.model.AssumeRoleWithWebIdentityRequest;
import software.amazon.awssdk.services.sts.model.IdpCommunicationErrorException;
import software.amazon.awssdk.utils.IoUtils;
import software.amazon.awssdk.utils.SdkAutoCloseable;
import software.amazon.awssdk.utils.StringUtils;
import software.amazon.awssdk.utils.SystemSetting;

import java.io.IOException;
import java.io.InputStream;
import java.io.UncheckedIOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.Duration;
import java.util.function.Supplier;

public class LicenseManagerTokenCredentialsProvider implements AwsCredentialsProvider, SdkAutoCloseable {

    private final StsAssumeRoleWithWebIdentityCredentialsProvider credentialsProvider;
    private final RuntimeException loadException;

    private Path licenseAccessTokenFile;
    private String roleArn;
    private String roleSessionName;
    private StsClient stsClient;
    private LicenseManagerClient lmClient;

    public static LicenseManagerTokenCredentialsProvider create() {
        return new Builder().build();
    }

    @Override
    public AwsCredentials resolveCredentials() {
        if (this.loadException != null) {
            throw this.loadException;
        }
        return this.credentialsProvider.resolveCredentials();
    }

    @Override
    public void close() {
        IoUtils.closeQuietly(this.credentialsProvider, null);
        IoUtils.closeQuietly(this.stsClient, null);
        IoUtils.closeIfCloseable(this.lmClient, null);
    }

    private LicenseManagerTokenCredentialsProvider(Builder builder) {
        StsAssumeRoleWithWebIdentityCredentialsProvider credentialsProvider = null;
        RuntimeException loadException = null;

        try {
            this.licenseAccessTokenFile = Paths.get(StringUtils.trim(LicenseSystemSetting.AWS_WEB_IDENTITY_REFRESH_TOKEN_FILE.getStringValueOrThrow()));
            this.roleArn = SdkSystemSetting.AWS_ROLE_ARN.getStringValueOrThrow();
            this.roleSessionName = SdkSystemSetting.AWS_ROLE_SESSION_NAME.getStringValue().orElse("aws-sdk-java-" + System.currentTimeMillis());
            this.stsClient = builder.stsClient != null ? builder.stsClient : StsClientFactory.create();
            this.lmClient = builder.lmClient != null ? builder.lmClient : LicenseManagerClientFactory.create();

            AssumeRoleWithWebIdentityRequest request = AssumeRoleWithWebIdentityRequest.builder()
                    .roleArn(this.roleArn).roleSessionName(this.roleSessionName).build();

            Supplier<AssumeRoleWithWebIdentityRequest> supplier = new AssumeRoleRequestSupplier(request,
                    this.licenseAccessTokenFile, this.lmClient);

            credentialsProvider = StsAssumeRoleWithWebIdentityCredentialsProvider.builder()
                    .stsClient(this.stsClient).refreshRequest(supplier).build();
        } catch (RuntimeException ex) {
            loadException = ex;
        }

        this.credentialsProvider = credentialsProvider;
        this.loadException = loadException;
    }

    public static final class Builder {
        private Path licenseAccessTokenFile;
        private String roleArn;
        private String roleSessionName;
        private StsClient stsClient;
        private LicenseManagerClient lmClient;

        public LicenseManagerTokenCredentialsProvider build() {
            return new LicenseManagerTokenCredentialsProvider(this);
        }

        public LicenseManagerTokenCredentialsProvider.Builder licenseAccessTokenFile(Path licenseAccessTokenFile) {
            this.licenseAccessTokenFile = licenseAccessTokenFile;
            return this;
        }

        public LicenseManagerTokenCredentialsProvider.Builder roleArn(String roleArn) {
            this.roleArn = roleArn;
            return this;
        }

        public LicenseManagerTokenCredentialsProvider.Builder roleSessionName(String roleSessionName) {
            this.roleSessionName = roleSessionName;
            return this;
        }

        public LicenseManagerTokenCredentialsProvider.Builder stsClient(StsClient stsClient) {
            this.stsClient = stsClient;
            return this;
        }

        public LicenseManagerTokenCredentialsProvider.Builder lmClient(LicenseManagerClient lmClient) {
            this.lmClient = lmClient;
            return this;
        }
    }

    private static final class AssumeRoleRequestSupplier implements Supplier {
        private final LicenseManagerClient lmClient;
        private final AssumeRoleWithWebIdentityRequest request;
        private final Path webIdentityRefreshTokenFile;

        AssumeRoleRequestSupplier(final AssumeRoleWithWebIdentityRequest request,
                                                 final Path webIdentityRefreshTokenFile,
                                                 final LicenseManagerClient lmClient) {
            this.lmClient = lmClient;
            this.request = request;
            this.webIdentityRefreshTokenFile = webIdentityRefreshTokenFile;
        }

        public AssumeRoleWithWebIdentityRequest get() {
            return this.request.toBuilder()
                    .webIdentityToken(getIdentityToken())
                    .build();
        }

        private String getIdentityToken() {
            return refreshIdToken(readRefreshToken(this.webIdentityRefreshTokenFile));
        }

        private String readRefreshToken(Path file) {
            try (InputStream webIdentityRefreshTokenStream = Files.newInputStream(file)) {
                return IoUtils.toUtf8String(webIdentityRefreshTokenStream);
            } catch (IOException e) {
                throw new UncheckedIOException(e);
            }
        }

        private String refreshIdToken(String licenseRefreshToken) {
            final GetAccessTokenRequest request = GetAccessTokenRequest.builder()
                    .token(licenseRefreshToken)
                    .build();

            GetAccessTokenResponse response = this.lmClient.getAccessToken(request);
            return response.accessToken();
        }
    }

    private static final class LicenseManagerClientFactory {
        private static final Duration DEFAULT_API_TIMEOUT = Duration.ofSeconds(30);
        private static final Duration DEFAULT_API_ATTEMPT_TIMEOUT = Duration.ofSeconds(10);

        public static LicenseManagerClient create() {
            return getLicenseManagerClient();
        }

        private static LicenseManagerClient getLicenseManagerClient() {
            ClientOverrideConfiguration configuration = ClientOverrideConfiguration.builder()
                    .apiCallTimeout(DEFAULT_API_TIMEOUT)
                    .apiCallAttemptTimeout(DEFAULT_API_ATTEMPT_TIMEOUT)
                    .build();

            LicenseManagerClient client = LicenseManagerClient.builder()
                    .region(configureLicenseManagerRegion())
                    .credentialsProvider(AnonymousCredentialsProvider.create())
                    .overrideConfiguration(configuration).build();
            return client;
        }

        private static Region configureLicenseManagerRegion() {
            Region defaultRegion = Region.US_EAST_1;

            Region region;
            try {
                region = (new DefaultAwsRegionProviderChain()).getRegion();
            } catch (RuntimeException ex) {
                region = defaultRegion;
            }
            return region;
        }
    }

    private static final class StsClientFactory {
        private static final Duration DEFAULT_API_TIMEOUT = Duration.ofSeconds(30);
        private static final Duration DEFAULT_API_ATTEMPT_TIMEOUT = Duration.ofSeconds(10);

        public static StsClient create() {
            return getStsClient();
        }

        private static StsClient getStsClient() {
            OrRetryCondition retryCondition = OrRetryCondition.create(new StsRetryCondition(),
                    RetryCondition.defaultRetryCondition());

            ClientOverrideConfiguration configuration = ClientOverrideConfiguration.builder()
                    .apiCallTimeout(DEFAULT_API_TIMEOUT)
                    .apiCallAttemptTimeout(DEFAULT_API_ATTEMPT_TIMEOUT)
                    .retryPolicy(r -> r.retryCondition(retryCondition))
                    .build();

            return StsClient.builder()
                    .region(configureStsRegion())
                    .credentialsProvider(AnonymousCredentialsProvider.create())
                    .overrideConfiguration(configuration).build();
        }

        private static Region configureStsRegion() {
            Region defaultRegion = Region.US_EAST_1;
            Region stsRegion;
            try {
                stsRegion = (new DefaultAwsRegionProviderChain()).getRegion();
            } catch (RuntimeException ex) {
                stsRegion = defaultRegion;
            }
            return stsRegion;
        }

        private static final class StsRetryCondition implements RetryCondition {
            public boolean shouldRetry(RetryPolicyContext context) {
                return context.exception() instanceof IdpCommunicationErrorException;
            }
        }
    }

    private enum LicenseSystemSetting implements SystemSetting {
        AWS_WEB_IDENTITY_REFRESH_TOKEN_FILE("aws.webIdentityRefreshTokenFile");

        private String systemProperty;
        private String defaultValue = null;

        LicenseSystemSetting(String systemProperty) {
            this.systemProperty = systemProperty;
        }

        @Override
        public String property() {
            return this.systemProperty;
        }

        @Override
        public String environmentVariable() {
            return this.name();
        }

        @Override
        public String defaultValue() {
            return this.defaultValue;
        }
    }
}
```

## `LicenseManagerCredentialsProvider` - `Golang` 实现。
<a name="container-license-manager-cred-provider-golang"></a>

**`LicenseCredentialsProvider`**

`LicenseCredentialsProvider`通过添加`LicenseManagerTokenCredentialsProvider`扩展 AWS SDK 的默认凭证提供程序链，以供本地使用。

```
package lib

import (
	"context"
	"fmt"
	"sync"

	"github.com/aws/aws-sdk-go-v2/aws"
	"github.com/aws/aws-sdk-go-v2/config"
)

// LicenseCredentialsProvider is the custom credential provider that can retrieve valid temporary aws credentials
type LicenseCredentialsProvider struct {
	fallBackProvider   aws.CredentialsProvider
	mux                sync.RWMutex
	licenseCredentials aws.Credentials
	err                error
}

// NewLicenseCredentialsProvider method will create a LicenseCredentialProvider Object which contains valid temporary aws credentials
func NewLicenseCredentialsProvider() (*LicenseCredentialsProvider, error) {
	licenseCredentialProvider := &LicenseCredentialsProvider{}
	fallBackProvider, err := createCredentialProvider()
	if err != nil {
		return licenseCredentialProvider, fmt.Errorf("failed to create LicenseCredentialsProvider, %w", err)
	}
	licenseCredentialProvider.fallBackProvider = fallBackProvider
	return licenseCredentialProvider, nil
}

// Retrieve method will retrieve temporary aws credentials from the credential provider
func (l *LicenseCredentialsProvider) Retrieve(ctx context.Context) (aws.Credentials, error) {
	l.mux.RLock()
	defer l.mux.RUnlock()
	l.licenseCredentials, l.err = l.fallBackProvider.Retrieve(ctx)
	return l.licenseCredentials, l.err
}

func createCredentialProvider() (aws.CredentialsProvider, error) {
	// LoadDefaultConfig will examine all "default" credential providers
	ctx := context.TODO()
	cfg, err := config.LoadDefaultConfig(ctx)
	if err != nil {
		return nil, fmt.Errorf("failed to create FallBackProvider, %w", err)
	}

	var useFallbackProvider bool
	if cfg.Credentials != nil {
		if _, err := cfg.Credentials.Retrieve(ctx); err != nil {
			// If the "default" credentials provider cannot retrieve credentials, enable fallback to customCredentialsProvider.
			useFallbackProvider = true
		}
	} else {
		useFallbackProvider = true
	}

	if useFallbackProvider {
		customProvider, err := newLicenseManagerTokenCredentialsProvider()
		if err != nil {
			return cfg.Credentials, fmt.Errorf("failed to create fallBackProvider, %w", err)
		}
		// wrap up customProvider with CredentialsCache to enable caching
		cfg.Credentials = aws.NewCredentialsCache(customProvider)
	}
	return cfg.Credentials, nil
}
```

**`LicenseManagerTokenCredentialsProvider`**

`LicenseManagerTokenCredentialsProvider` 通过在本地环境中使用 License Manager OIDC 颁发的身份令牌来提供凭证。必须在应用程序类路径中包含 `LicenseCredentialsProvider` 的源代码。

```
package lib

import (
	"context"
	"fmt"
	"io/ioutil"
	"os"
	"sync"
	"time"

	"github.com/aws/aws-sdk-go-v2/aws"
	"github.com/aws/aws-sdk-go-v2/config"
	"github.com/aws/aws-sdk-go-v2/service/sts"
)

const awsRefreshTokenFilePathEnvVar = "AWS_LICENSE_ACCESS_FILE"

// licenseManagerTokenCredentialsProvider defines and contains StsAssumeRoleWithWebIdentityProvider
type licenseManagerTokenCredentialsProvider struct {
	stsCredentialProvider *stsAssumeRoleWithWebIdentityProvider
	mux                   sync.RWMutex
	licenseCredentials    aws.Credentials
	err                   error
}

// Retrieve method will retrieve credentials from credential provider.
// Make this method public to make this provider satisfies CredentialProvider interface
func (a *licenseManagerTokenCredentialsProvider) Retrieve(ctx context.Context) (aws.Credentials, error) {
	a.mux.RLock()
	defer a.mux.RUnlock()
	a.licenseCredentials, a.err = a.stsCredentialProvider.Retrieve(ctx)
	return a.licenseCredentials, a.err
}

// newLicenseManagerTokenCredentialsProvider will create and return a LicenseManagerTokenCredentialsProvider Object which wraps up stsAssumeRoleWithWebIdentityProvider
func newLicenseManagerTokenCredentialsProvider() (*licenseManagerTokenCredentialsProvider, error) {
	// 1. Retrieve variables From yaml environment
	envConfig, err := config.NewEnvConfig()
	if err != nil {
		return &licenseManagerTokenCredentialsProvider{}, fmt.Errorf("failed to create LicenseManagerTokenCredentialsProvider, %w", err)
	}
	roleArn := envConfig.RoleARN
	var roleSessionName string
	if envConfig.RoleSessionName == "" {
		roleSessionName = fmt.Sprintf("aws-sdk-go-v2-%v", time.Now().UnixNano())
	} else {
		roleSessionName = envConfig.RoleSessionName
	}
	tokenFilePath := os.Getenv(awsRefreshTokenFilePathEnvVar)
	b, err := ioutil.ReadFile(tokenFilePath)
	if err != nil {
		return &licenseManagerTokenCredentialsProvider{}, fmt.Errorf("failed to create LicenseManagerTokenCredentialsProvider, %w", err)
	}
	refreshToken := aws.String(string(b))

	// 2. Create stsClient
	cfg, err := config.LoadDefaultConfig(context.TODO())
	if err != nil {
		return &licenseManagerTokenCredentialsProvider{}, fmt.Errorf("failed to create LicenseManagerTokenCredentialsProvider, %w", err)
	}
	stsClient := sts.NewFromConfig(cfg, func(o *sts.Options) {
		o.Region = configureStsClientRegion(cfg.Region)
		o.Credentials = aws.AnonymousCredentials{}
	})

	// 3. Configure StsAssumeRoleWithWebIdentityProvider
	stsCredentialProvider := newStsAssumeRoleWithWebIdentityProvider(stsClient, roleArn, roleSessionName, refreshToken)

	// 4. Build and return
	return &licenseManagerTokenCredentialsProvider{
		stsCredentialProvider: stsCredentialProvider,
	}, nil
}

func configureStsClientRegion(configRegion string) string {
	defaultRegion := "us-east-1"
	if configRegion == "" {
		return defaultRegion
	} else {
		return configRegion
	}
}
```

# 容器 产品的 Amazon SNS 通知
<a name="container-notification"></a>

要接收通知，您可以订阅商品创建期间向您提供的亚马逊简单通知服务 (Amazon SNS) Simple Notification Service 主题 AWS Marketplace 。这些主题提供有关客户产品订阅发生更改的通知。例如，您可以通过这些通知了解客户何时接受专属报价。

**注意**  
在产品创建过程中，会为您的产品创建一个 Amazon SNS 主题。要订阅通知，您需要 Amazon SNS 主题的 Amazon 资源名称（ARN）（例如 `arn:aws:sns:us-east-1:123456789012:aws-mp-subscription-notification-PRODUCTCODE`）。服务器产品的卖家门户中没有该 ARN。请联系 [AWS Marketplace 运营团队](https://aws.amazon.com/marketplace/management/contact-us)请求该 ARN。

以下 Amazon SNS 主题适用于容器产品：
+ [Amazon SNS 主题：`aws-mp-subscription-notification`](#container-sns-subscription-message-body) – 当买家订阅或取消订阅产品时，此主题会向您发送通知。此通知适用于按小时定价模式，包括长期按小时定价模式和按年计费模式。

## Amazon SNS 主题：`aws-mp-subscription-notification`
<a name="container-sns-subscription-message-body"></a>

`aws-mp-subscription-notification` 主题中的每条消息都采用以下格式。

```
{
    "action": "<action-name>",
    "customer-identifier": " X01EXAMPLEX",
    "product-code": "n0123EXAMPLEXXXXXXXXXXXX",
    "offer-identifier": "offer-abcexample123"
}
```

*<action-name>*将根据通知而有所不同。可能的操作包括：
+ `subscribe-success`
+ `subscribe-fail`
+ `unsubscribe-pending`
+ `unsubscribe-success`

仅当操作为 `subscribe-success` 或 `subscribe-fail` 时，通知中才会包含 `offer-identifier`。当操作为 `unsubscribe-pending` 或 `unsubscribe-success` 时，则通知中不包含此标识符。对于 2024 年 1 月之前创建的优惠，只有关于专属优惠的通知中才会包含此标识符。对于 2024 年 1 月及之后创建的优惠，此标识符将包含在所有优惠的通知中，包括专属优惠和公开优惠。

有关报价类型的信息，请参阅 [DescribeEntity API](https://docs.aws.amazon.com//marketplace/latest/APIReference/work-with-private-offers.html#describe-entity) 的回复或协议[续订控制面板中协议的报价可见性。](https://docs.aws.amazon.com//marketplace/latest/userguide/agreements-renewals-dashboard.html)

**注意**  
 对于 [DescribeEntity API](https://docs.aws.amazon.com//marketplace/latest/APIReference/work-with-private-offers.html#describe-entity)，如果您 AWS 账户 在针对该优惠的定位规则中发现了账户定位方面，则该优惠就是私有优惠。如果账户定位规则 AWS 账户 中没有针对该优惠的定向规则，则为公开报价。

## 为 Amazon SQS 队列订阅 Amazon SNS 主题
<a name="subscribing-sqs-queue-to-sns-topic"></a>

我们建议为 Amazon SQS 队列订阅提供的 SNS 主题。有关创建SQS队列并将队列订阅主题的详细说明，请参阅《Amazon Simple Notification Service 开发人员指南》**中的[为 Amazon SQS 队列订阅 Amazon SNS 主题](https://docs.aws.amazon.com/sns/latest/dg/subscribe-sqs-queue-to-sns-topic.html)。

**注意**  
您只能订阅 AWS 账户 用于销售产品的用户的 AWS Marketplace SNS 主题。但是，您可以将消息转发到其他帐户。有关更多信息，请参阅《Amazon Simple Notification Service 开发人员指南》**中的[将 Amazon SNS 消息发送到不同账户的 Amazon SQS 队列](https://docs.aws.amazon.com/sns/latest/dg/sns-send-message-to-sqs-cross-account.html)。

### 轮询 SQS 队列以获取通知
<a name="polling-the-sqs-for-notifications"></a>

在您的 SQS 队列订阅 SNS 主题后，消息将存储在 SQS 中。您需要定义一项服务，以不断轮询该队列，查找消息并相应地处理它们。