

기계 번역으로 제공되는 번역입니다. 제공된 번역과 원본 영어의 내용이 상충하는 경우에는 영어 버전이 우선합니다.

# 를 사용한 컨테이너 제품의 계약 요금 AWS License Manager
<a name="container-license-manager-integration"></a>

계약 요금이 적용되는 컨테이너 기반 제품의 경우 AWS License Manager 를 사용하여 라이선스를 제품과 연결할 수 있습니다. AWS License Manager 는 애플리케이션이 고객이 구매한 라이선스(권한이라고도 함)를 추적하고 업데이트할 수 있는 라이선스 관리 도구입니다. 이 섹션에서는 제품을와 통합하는 방법에 대한 정보를 제공합니다 AWS License Manager. 통합이 완료되면 제품 목록을 AWS Marketplace에 게시할 수 있습니다.

License Manager를 Amazon EKS Anywhere, Amazon ECS Anywhere, Amazon Elastic Compute Cloud(Amazon EC2) 또는 온프레미스 인프라용 AWS Marketplace for Containers Anywhere 제품과 통합하는 경우의 지침을 따르세요[AWS Marketplace for Containers Anywhere와 License Manager 통합](container-anywhere-license-manager-integration.md).

에 대한 자세한 내용은 [AWS License Manager 사용 설명서](https://docs.aws.amazon.com/license-manager/latest/userguide/license-manager.html) 및 *AWS CLI 명령* AWS License Manager참조의 [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 for Containers Anywhere와 License Manager 통합](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 USD/GB  |  16.00 USD/GB  |  30.00 USD/GB  |  GB당 60.00 USD  | 
|  암호화된 데이터(GB)  |  1.55 USD/GB  |  16.60 USD/GB  |  31.20 USD/GB  |  GB당 61.20 USD  | 


**예제: 로그 모니터링 제품**  

|   | 1개월 요금 | 12개월 요금  | 24개월 요금 | 36개월 요금 | 
| --- | --- | --- | --- | --- | 
|  기본(10개의 호스트 모니터링, 5개의 컨테이너 모니터링)  |  100 USD  |  1000 USD  | 2000 USD  | 4000 USD | 
|  스탠다드(20개의 호스트 모니터링, 10개의 컨테이너 모니터링)  |  \$1200  |  2000 USD  | 4000 USD  | 8000 USD | 
|  프로(40개의 호스트 모니터링, 20개의 컨테이너 모니터링)  |  400 USD  |  4000 USD  | 8000 USD  | 16,000 USD | 
|  시간당 추가 호스트 모니터링  | 10 USD  | 100 USD  |  \$1200 | 400 USD | 
|  시간당 추가 컨테이너 모니터링  | 10 USD  | 100 USD  |  \$1200 | 400 USD | 

**참고**  
요금은 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 USD/단위)를 설정할 수 있습니다.  
구매자는 5, 10 또는 20단위를 구매하기로 선택할 수 있습니다.  
판매자의 제품은 사용량을 추적하고 측정하여 소비된 리소스 양을 측정합니다.

구성 모델의 경우 다음 두 가지 방법 중 하나로 권한이 계산됩니다.
+ [드로다운 라이선스](#container-floating-lic)
+ [플로팅 라이선스](#container-floating-lic) 

#### 드로다운 라이선스
<a name="container-drawndown-lic"></a>

 라이선스를 사용한 만큼 허용된 라이선스 수량 풀에서 차감됩니다. 해당 권한은 영구적으로 체크아웃되며 라이선스 풀로 반환할 수 없습니다.

**Example 제한된 양의 데이터 처리 예시**  
한 사용자에게 500GB의 데이터를 처리할 권한이 있습니다. 사용자가 데이터를 계속 처리하면 500GB 라이센스가 모두 소진될 때까지 500GB 풀에서 수량이 차감됩니다.

드로다운 라이선스의 경우 `CheckoutLicense` API 작업을 사용하여 소비된 라이선스 단위(권한)를 체크아웃할 수 있습니다.

**Example 연간 여러 단위에 대해 S3로 백업한 예시**  
Amazon Simple Storage Service에 1년 동안 데이터를 최대 1,024단위까지 백업할 수 있는 스토리지 제품을 보유하고 있습니다. 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 1년간 동시 사용자 수 예시**  
제품 요금은 동시 사용자 수를 기준으로 책정됩니다. 고객이 1년간 사용자 10명 라이선스를 구매합니다. 고객이 AWS Identity and Access Management (IAM) 권한을 제공하여 소프트웨어를 시작합니다. 사용자가 로그인하면 애플리케이션이 `CheckoutLicense` API 작업을 호출하여 수량을 1만큼 줄입니다. 사용자가 로그아웃하면 애플리케이션이 `CheckInLicense` API 작업을 호출하여 해당 라이선스를 풀에 반환합니다. `CheckInLicense`를 호출하지 않으면 1시간 후에 라이선스 단위가 자동으로 체크인됩니다.

**참고**  
다음 요청에서 `key-fingerprint`는 자리 표시자 값이 아니라 모든 라이선스를 게시하는 데 사용되는 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 Management Portal생성하고 해당 제품 코드를 기록해 둡니다.

   자세한 내용은 [개요: 컨테이너 제품 만들기](container-product-getting-started.md#create-container-product) 단원을 참조하십시오.

1. `CheckoutLicense`, `ExtendLicenseConsumption` 및 `CheckInLicense` API 작업을 호출하는 데 필요한 IAM 권한이 있는 애플리케이션을 실행하는 작업 또는 포드에 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 SDK를 다운로드합니다.
**참고**  
소프트웨어 내에서 자격 AWS 증명을 구성하지 마세요. 구매자의 AWS 자격 증명은 컨테이너가 Amazon EC2 인스턴스, Amazon 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` API 작업을 사용합니다.
+ `ListReceivedLicenses` API - 구매자가 구매한 라이선스를 나열합니다.

## 라이선스 갱신 및 업그레이드
<a name="container-LM-lic-renew-upgrade"></a>

고객은 AWS Marketplace Management Portal에서 라이선스를 갱신하거나 업그레이드할 수 있습니다. 추가 구매를 한 후는 새 권한을 반영하는 라이선스의 새 버전을 AWS Marketplace 생성합니다. 소프트웨어는 동일한 API 작업을 사용하여 새 권한을 읽습니다. 갱신 및 업그레이드를 처리하기 위해 License Manager 통합과 관련하여 아무 것도 할 필요가 없습니다.

라이선스 갱신, 업그레이드, 취소 등으로 인해 제품이 사용되는 동안 제품에서 정기적으로 `CheckoutLicense` API 작업을 호출하는 것이 좋습니다. `CheckoutLicense` API 작업을 정기적으로 사용하면 제품에서 업그레이드 및 만료와 같은 권한 변경을 감지할 수 있습니다.

15분마다 `CheckoutLicense` API 직접 호출을 수행하는 것이 좋습니다.

# AWS Marketplace for Containers Anywhere와 License Manager 통합
<a name="container-anywhere-license-manager-integration"></a>

 AWS Marketplace 판매자는 Amazon EKS Anywhere, Amazon ECS Anywhere, Amazon EC2 또는 온프레미스 인프라를 위한 AWS License Manager AWS Marketplace for Containers Anywhere 제품과 통합할 수 있습니다. Amazon ECS Anywhere 다음 섹션에서는 이 통합에 대한 지침을 제공합니다.

사용 가능한 라이선스 모델을 AWS Marketplace포함하여 License Manager와의 통합에 대한 일반적인 내용은 섹션을 참조하세요[를 사용한 컨테이너 제품의 계약 요금 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 for Containers Anywhere 제품을 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 for Containers Anywhere 제품을 License Manager와 통합
<a name="containers-anywhere-integrate-with-LM"></a>

다음 지침에 따라 AWS Marketplace for Containers Anywhere 제품을와 통합합니다 AWS License Manager.

**AWS Marketplace for Containers Anywhere 제품을 License Manager와 통합하려면**

1. 웹 브라우저를 열고 [AWS Marketplace Management Portal](https://aws.amazon.com/marketplace/management/)에 로그인합니다.

1. 다음 단계를 수행하여 컨테이너 제품의 제품 ID를 생성합니다. 이후 단계에서 라이선스 확인을 위해 컨테이너 이미지에서 이 ID를 사용하게 됩니다.

   1. 메뉴 모음에서 [**Assets(자산)**]를 확장하고 [**Container(컨테이너)**]를 선택합니다.

   1. 고객에게 표시되는 제품 이름을 입력하고 **생성**을 선택합니다. 이 이름은 나중에 변경할 수 있습니다.

   1. **제품 ID**를 적어 둡니다. 제품 요금 세부 정보를 생성하거나 업데이트할 때 필요합니다.
**작은 정보**  
제품 ID를 분실한 경우 **자산** 메뉴에서 **컨테이너**를 AWS Marketplace Management Portal 선택하여에서 찾을 수 있습니다. **컨테이너** 페이지에는 제품 및 연결된 제품 ID 목록이 표시됩니다.

1. 최신 퍼블릭 AWS SDK를 다운로드한 다음 컨테이너 애플리케이션에 설치합니다. [AWS에서 빌드할 도구](https://aws.amazon.com/tools/)에서 원하는 AWS SDK에 대한 설치 지침을 찾을 수 있습니다.
**참고**  
Amazon EKS Anywhere 또는에서 제공하지 않는 Kubernetes 클러스터에서 License Manager API 작업을 호출하려면 지원되는 AWS SDK를 사용해야 AWS합니다. 지원되는 AWS SDKs. [AWS](https://docs.aws.amazon.com/eks/latest/userguide/iam-roles-for-service-accounts-minimum-sdk.html) 

1. 온프레미스 AWS 뿐만 아니라에 배포된 컨테이너 애플리케이션에 자격 증명을 제공할 수 있도록 사용자 지정 자격 증명 공급자로 AWS License Manager 클라이언트를 생성합니다. 사용자 지정 보안 인증 정보 공급자 `LicenseCredentialProvider`에 대한 전체 소스 코드는 다음 섹션을 참조하세요.
   + [`LicenseManagerCredentialsProvider` - Java 구현](#container-license-manager-cred-provider-java)
   + [`LicenseManagerCredentialsProvider` - `Golang` 구현](#container-license-manager-cred-provider-golang)

    `LicenseCredentialsProvider`는를 추가하여 온프레미스 사용을 위해 AWS SDK의 기본 자격 증명 공급자 체인을 확장합니다`LicenseManagerTokenCredentialsProvider`. 이렇게 하면 온프레미스 환경에서 License Manager OIDC 발급 ID 토큰을 사용하여 보안 인증 정보가 제공됩니다. `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`가 예외를 throw합니다.

   다음은 `CheckoutLicense` API 작업을 호출할 때 필요한 파라미터입니다.
   + `CheckoutType` - 유효한 값은 `PROVISIONAL` 또는 `PERPETUAL`입니다.
     + 체크아웃된 권한 수량이 풀에서 모두 소진되면 `PERPETUAL`을 사용합니다.

       예: 구매자에게 500GB의 데이터를 처리할 권한이 있습니다. 구매자가 데이터를 계속 처리하다 보면 수량이 감소하고 500GB 풀이 모두 소진됩니다.
     + 풀에서 권한을 체크아웃하고 사용 후 반환하는 플로팅 라이선스 권한에 `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 에서 발급한 라이선스의 key fingerprint는 `aws:294406891311:AWS/Marketplace:issuer-fingerprint`입니다. 이 키 지문을 사용하면 신뢰할 수 없는 엔터티가 AWS Marketplace 아닌에서 라이선스를 발급할 수 있습니다.
   + `ProductSKU` - 이전 단계에서 AWS Marketplace Management Portal 에 생성된 제품 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을 업데이트하여 License Manager API를 통해 라이선스에 액세스하기 위한 구성을 포함하는 선택적 입력으로 Kubernetes 보안 암호를 수락합니다. 구성 보안 암호에는 License Manager에서 발급한 자격 증명 토큰과 앞서 설명한 사용자 지정 자격 증명 공급자가 컨테이너 애플리케이션이 온프레미스에 배포될 때 License Manager APIs 호출하기 위한 AWS 자격 증명을 가져오는 데 사용할 AWS Identity and Access Management 역할이 포함됩니다. 또한 AWS 리전 을 기본값인 `us-east-1`로 하여 입력으로 추가합니다.

   컨테이너 애플리케이션을 온프레미스에 배포하는 구매자는 컨테이너 제품에 대한 AWS Marketplace 구매자 경험을 통해 Kubernetes 보안 암호를 생성할 수 있습니다. `helm install` 명령의 입력으로 Kubernetes 보안 암호 이름을 제공합니다. 구성 보안 암호는 다음 형식으로 구성됩니다.

   ```
   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. 다음을 AWS License Manager 포함하도록와 통합된 컨테이너 이미지의 차트 Helm에서 애플리케이션 배포 템플릿을 업데이트합니다.
   + 포드용 서비스 계정 - Amazon EKS에서 Helm을 배포하려면 서비스 계정이 필요합니다. 컨테이너 이미지의 서비스 계정에 대한 IAM 역할을 설정하여 License Manager API 작업을 호출할 권한을 얻는 데 사용됩니다. 서비스 계정의 IAM 역할에 대한 자세한 내용은 [서비스 계정에 대한 IAM 역할](https://docs.aws.amazon.com/eks/latest/userguide/iam-roles-for-service-accounts.html)을 참조하세요.
   + 온프레미스 배포에 사용할 라이선스 액세스 권한 - 온프레미스 환경에서 Helm 배포를 위해 License Manager API 작업을 호출하는 데 필요한 보안 인증 정보와 적절한 권한을 제공하려면 라이선스 구성 보안 암호가 필요합니다. 구매자는 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 for Containers Anywhere 제품을 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` 서비스 계정에 IAM 역할을 연결합니다. 따라서 서비스 계정은 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. 이전 명령에서 IAM 역할이 연결된 서비스 계정의 Helm을 통해 애플리케이션을 배포합니다. 애플리케이션이 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`는를 추가하여 온프레미스 사용을 위해 AWS SDK의 기본 자격 증명 공급자 체인을 확장합니다`LicenseManagerTokenCredentialsProvider`.

**`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 발급 ID 토큰을 사용하여 보안 인증 정보를 제공합니다. `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`는를 추가하여 온프레미스 사용을 위해 AWS SDK의 기본 자격 증명 공급자 체인을 확장합니다`LicenseManagerTokenCredentialsProvider`.

```
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 발급 ID 토큰을 사용하여 보안 인증 정보를 제공합니다. `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
	}
}
```