AWS Marketplace for Containers Anywhere와 License Manager 통합 - AWS Marketplace

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

AWS Marketplace for Containers Anywhere와 License Manager 통합

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. AWS License Manager에 대한 자세한 내용은 AWS License Manager 사용 설명서AWS CLI 명령 참조의 AWS License Manager 섹션을 참조하세요.

AWS Marketplace for Containers Anywhere 제품을 License Manager와 통합

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

AWS Marketplace for Containers Anywhere 제품을 License Manager와 통합하려면
  1. 웹 브라우저를 열고 AWS Marketplace Management Portal에 로그인합니다.

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

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

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

    3. 제품 ID를 적어 둡니다. 제품 요금 세부 정보를 생성하거나 업데이트할 때 필요합니다.

      작은 정보

      제품 ID를 분실한 경우 자산 메뉴에서 컨테이너를 AWS Marketplace Management Portal 선택하여에서 찾을 수 있습니다. 컨테이너 페이지에는 제품 및 연결된 제품 ID 목록이 표시됩니다.

  3. 최신 퍼블릭 AWS SDK를 다운로드한 다음 컨테이너 애플리케이션에 설치합니다. AWS에서 빌드할 도구에서 원하는 AWS SDK의 설치 지침을 찾을 수 있습니다.

    참고

    Amazon EKS Anywhere 또는에서 제공하지 않는 Kubernetes 클러스터에서 License Manager API 작업을 호출하려면 지원되는 AWS SDK를 사용해야 AWS합니다. 지원되는 AWS SDKs 목록을 보려면 지원되는 AWS SDK 사용을 참조하세요.

  4. 사용자 지정 자격 증명 공급자를 사용하여 AWS License Manager 클라이언트를 생성하여 온프레미스뿐만 AWS 아니라에 배포된 컨테이너 애플리케이션에 자격 증명을 제공할 수 있습니다. 사용자 지정 보안 인증 정보 공급자 LicenseCredentialProvider에 대한 전체 소스 코드는 다음 섹션을 참조하세요.

    LicenseCredentialsProvider는를 추가하여 온프레미스 사용을 위한 AWS SDK의 기본 자격 증명 공급자 체인을 확장합니다LicenseManagerTokenCredentialsProvider. 이렇게 하면 온프레미스 환경에서 License Manager OIDC 발급 ID 토큰을 사용하여 보안 인증 정보가 제공됩니다. LicenseCredentialsProvider의 소스 코드를 애플리케이션 클래스 경로에 포함해야 합니다.

    참고

    를 확장DefaultCredentialsProvider하면 온프레미스 환경에서를 실행할 때 AWS 와를 실행할 때 동일한 컨테이너 애플리케이션이 자격 증명을 얻을 수 있습니다. 컨테이너 애플리케이션이 이미 기본값 대신 사용자 지정 보안 인증 정보 공급자 체인을 사용하는 경우 사용자 지정 체인에 LicenseManagerTokenCredentialsProvider를 추가하여 확장할 수도 있습니다.

    다음 코드 조각은 Java를 사용하여 AWS License Manager 클라이언트를 생성하는 예제입니다.

    LicenseManagerClientBuilder clientBuilder = LicenseManagerClient.builder().credentialsProvider(LicenseCredentialsProvider.create());
  5. 제품 제안의 각 유료 컨테이너 이미지에서 aws license-manager checkout-license 명령을 사용하여 CheckoutLicense API 작업을 호출합니다. 그러면 구매자가 애플리케이션의 라이선스를 사용할 권한이 있는지 확인됩니다. 구매자에게 애플리케이션에 대한 권한이 있으면 CheckoutLicense가 성공하고 요청된 권한과 해당 값을 반환합니다. 구매자에게 애플리케이션에 대한 권한이 없으면 CheckoutLicense가 예외를 throw합니다.

    다음은 CheckoutLicense API 작업을 호출할 때 필요한 파라미터입니다.

    • CheckoutType - 유효한 값은 PROVISIONAL 또는 PERPETUAL입니다.

      • 체크아웃된 권한 수량이 풀에서 모두 소진되면 PERPETUAL을 사용합니다.

        예: 구매자에게 500GB의 데이터를 처리할 권한이 있습니다. 구매자가 데이터를 계속 처리하다 보면 수량이 감소하고 500GB 풀이 모두 소진됩니다.

      • 풀에서 권한을 체크아웃하고 사용 후 반환하는 플로팅 라이선스 권한에 PROVISIONAL을 사용합니다.

        예: 사용자는 애플리케이션에서 동시 사용자 500명을 이용할 수 있습니다. 사용자가 로그인 또는 로그아웃하면 사용자가 차감되거나 사용자 500명 풀로 반환됩니다. 플로팅 라이선스 권한에 대한 자세한 내용은 플로팅 라이선스 권한과 License Manager 섹션을 참조하세요.

    • ClientToken - 대/소문자를 구분하는 고유의 식별자입니다. 각각의 고유한 요청에는 무작위 UUID를 사용하는 것이 좋습니다.

    • Entitlements - 체크아웃할 권한 목록입니다.

      • 기능 권한의 경우 다음과 같이 NameUnit 속성을 제공합니다.

        { "Name": "<Entitlement_Name>", "Unit": "None" }
      • 집계된 권한의 경우 다음과 같이 Name, UnitCount 속성을 제공합니다.

        { "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와 통합하는 모범 사례 단원을 참조하세요.

  6. 정기적으로 CheckoutLicense API 작업을 호출하여 AWS Marketplace에서 갱신, 업그레이드 또는 취소로 인해 변경된 고객 라이선스 변경 사항을 확인합니다. 주기는 애플리케이션에 따라 다릅니다. 하루에 한 번 라이선스를 확인하여 구매자의 개입 없이 변경 내용을 자동으로 적용하는 것이 좋습니다.

    온프레미스에 배포된 애플리케이션은 정기적으로 라이선스를 확인하기에는 아웃바운드 네트워크 액세스가 불안정할 수 있습니다. 이 경우 애플리케이션에서 충분한 복원력을 확보할 수 있도록 캐시된 라이선스를 사용해야 합니다. 자세한 내용은 온프레미스 배포를 위해 License Manager와 통합하는 모범 사례 단원을 참조하세요.

  7. CheckoutLicense 호출을 컨테이너 애플리케이션과 통합한 후에는 변경 사항이 적용된 새 버전의 Docker 컨테이너 이미지를 빌드합니다.

  8. 애플리케이션의 차트 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
  9. 와 통합된 컨테이너 이미지의 Helm 차트에서 애플리케이션 배포 템플릿을 업데이트 AWS License Manager 하여 다음을 포함합니다.

    • 포드용 서비스 계정 - Amazon EKS에서 Helm을 배포하려면 서비스 계정이 필요합니다. 컨테이너 이미지의 서비스 계정에 대한 IAM 역할을 설정하여 License Manager API 작업을 호출할 권한을 얻는 데 사용됩니다. 서비스 계정의 IAM 역할에 대한 자세한 내용은 서비스 계정에 대한 IAM 역할을 참조하세요.

    • 온프레미스 배포에 사용할 라이선스 액세스 권한 - 온프레미스 환경에서 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 통합 이미지의 서비스 계정이 포함되어야 합니다.

  10. 다음 섹션의 단계를 수행하여 로컬 및 Amazon EKS에서 License Manager 통합을 테스트합니다.

    1. 로컬로 License Manager 통합 테스트

    2. Amazon EKS에서 License Manager 통합 테스트

  11. 온프레미스 AWS 와 온프레미스 모두에서 License Manager 통합을 성공적으로 확인한 후의 단계에 따라 컨테이너 제품 목록을 생성할 수 있습니다개요: 컨테이너 제품 만들기.

로컬로 License Manager 통합 테스트

minikube 또는 기타 설정을 사용하여 아무 Kubernetes 클러스터에서 로컬로 License Manager 통합을 테스트할 수 있습니다. Kubernetes 클러스터에 License Manager API 작업을 호출할 수 있는 아웃바운드 인터넷 액세스 권한이 있어야 합니다.

로컬로 License Manager 통합을 테스트하는 방법
  1. 원하는 권한이 있는 테스트 판매자 계정에서 테스트 라이선스를 생성합니다. 테스트 라이선스를 설정하려면 AWS License Manager API 참조의 CreateLicense를 참조하세요. 또는 다음 스크립트를 사용하여 테스트 라이선스를 생성한 다음, 테스트 구매자 계정에 라이선스를 부여하여 라이선스를 사용할 수 있습니다. 다음 스크립트는 테스트 판매자 계정 보안 인증 정보를 사용합니다.

    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"
  2. 이전에 정의한 보안 암호 형식을 사용하는 라이선스 토큰과 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
  3. 외부에서 호스팅되는 Kubernetes 클러스터를 설정합니다 AWS. 이를 사용하여 컨테이너 애플리케이션이 이외의 환경에서 AWS License Manager API에 연결할 수 AWS 있고 사용자 지정 자격 증명 공급자가 애플리케이션에 잘 통합되었는지 테스트합니다.

  4. 앞에서 생성한 라이선스 토큰과 IAM 역할을 로컬 Kubernetes 클러스터에 배포합니다.

    kubectl create secret generic "awsmp-license-access-config" \ --from-literal=license_token=${TOKEN} \ --from-literal=iam_role=${ROLE_ARN}
  5. 보안 암호 이름을 입력으로 사용하여 Helm을 통해 애플리케이션을 배포하고 애플리케이션이 License Manager API 작업을 호출하여 권한 검사를 수행할 수 있는지 확인합니다. Helm 및 배포 사양 변경에 대한 내용은 AWS Marketplace for Containers Anywhere 제품을 License Manager와 통합의 9단계를 참조하세요.

Amazon EKS에서 License Manager 통합 테스트

Amazon EKS에서 License Manager 통합을 테스트할 수도 있습니다. 애플리케이션이 라이선스 구성 보안 암호 없이 License Manager API 작업을 호출할 수 있는지 테스트합니다. 또한 서비스 계정을 사용하여 서비스 계정에 대한 IAM 역할(IRSA)을 설정하고 애플리케이션에 관련 보안 인증 정보를 제공할 수 있는지 확인합니다.

Amazon EKS에서 License Manager 통합을 테스트하는 방법
  1. 원하는 권한이 있는 테스트 판매자 계정에서 테스트 라이선스를 생성합니다. 테스트 라이선스를 설정하거나, 다음 스크립트를 사용하여 테스트 라이선스를 생성하고 테스트 구매자 계정에 라이선스를 부여하여 라이선스를 사용하려면 CreateLicense API 참조를 확인하세요. 다음 스크립트는 테스트 판매자 계정 보안 인증 정보를 사용합니다.

    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"
  2. 원하는 구성의 테스트 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
  3. 기존 클러스터의 서비스 계정을 생성하고 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
  4. 이전 명령에서 IAM 역할이 연결된 서비스 계정의 Helm을 통해 애플리케이션을 배포합니다. 애플리케이션이 License Manager API 작업을 호출하여 권한 검사를 수행할 수 있는지 확인합니다.

플로팅 라이선스 권한과 License Manager

플로팅 라이선스를 사용하면 사용자가 애플리케이션에 로그인할 때 사용 가능한 라이선스 풀에서 라이선스를 가져옵니다. 사용자가 로그아웃하면 라이선스가 사용 가능한 라이선스 풀에 다시 추가됩니다.

플로팅 라이선스의 경우 애플리케이션은 리소스가 사용될 때 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와 통합하는 모범 사례

온프레미스 환경에서 컨테이너 애플리케이션을 배포할 때 아웃바운드 네트워크 액세스가 불안정할 수 있습니다. 인터넷 연결 불량으로 인한 잠재적 문제 때문에 구매자에게 제공되는 서비스가 중단되지 않도록 다음 모범 사례에 따라 복원력을 강화합니다.

  • 적절한 재시도 - 일시적인 네트워크 문제로 인해 애플리케이션이에 연결되지 않을 수 있습니다 AWS License Manager. 지수 백오프를 사용하여 최대 30분 동안 재시도하도록 구현합니다. 이렇게 하면 단기적인 중단이나 네트워크 문제를 방지하는 데 도움이 될 수 있습니다.

  • 하드 제한 금지 - 연결된 클러스터에 배포된 애플리케이션은 정기적으로 라이선스를 확인하여 업그레이드 또는 갱신으로 인한 변경 사항을 식별할 수 있습니다. 아웃바운드 액세스가 불안정하면 애플리케이션이 이러한 변경 사항을 식별하지 못할 수 있습니다. 가급적이면 애플리케이션에서 License Manager를 통해 라이선스를 확인할 수 없어 구매자에게 제공되는 서비스가 중단되는 일이 없도록 해야 합니다. 라이선스가 만료되고 라이선스가 유효한지 확인할 수 없는 경우 애플리케이션이 무료 평가판 또는 오픈 소스 환경을 사용할 수 있습니다.

  • 고객 알림 - 캐시된 라이선스를 사용하는 경우 라이선스 변경 사항(갱신 또는 업그레이드 포함)이 실행 중인 워크로드에 자동으로 반영되지 않습니다. 애플리케이션이 캐시된 라이선스를 업데이트할 수 있도록 애플리케이션에 대한 아웃바운드 액세스를 일시적으로 다시 허용해야 한다고 고객에게 알려야 합니다. 예를 들어 애플리케이션 자체를 통해 또는 애플리케이션의 설명서를 통해 고객에게 알립니다. 마찬가지로, 기능 하위 세트로 대체하는 경우에도 권한이 모두 소진되었거나 라이선스가 만료되었다고 고객에게 알립니다. 그러면 고객은 업그레이드 또는 갱신을 선택할 수 있습니다.

LicenseManagerCredentialsProvider - Java 구현

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 구현

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 } }