

本文為英文版的機器翻譯版本，如內容有任何歧義或不一致之處，概以英文版為準。

# 了解如何使用 搭配自訂屬性使用 AWS Cloud Map 服務探索 AWS CLI
<a name="tutorial-microservices-cli"></a>

本教學課程示範如何搭配自訂屬性使用 AWS Cloud Map 服務探索。您將建立使用 的微服務應用程式， AWS Cloud Map 以使用自訂屬性動態探索資源。應用程式包含兩個 Lambda 函數，可將資料寫入 DynamoDB 資料表並從中讀取，其中已註冊所有資源 AWS Cloud Map。

如需教學課程的 AWS 管理主控台 版本，請參閱 [了解如何搭配自訂屬性使用 AWS Cloud Map 服務探索](tutorial-microservices.md)。

## 先決條件
<a name="prerequisites"></a>

開始本教學課程之前，請先完成 中的步驟[設定 以使用 AWS Cloud Map](setting-up-cloud-map.md)。

## 建立 AWS Cloud Map 命名空間
<a name="create-an-aws-cloud-map-namespace"></a>

命名空間是用來將應用程式服務分組的建構。在此步驟中，您將建立一個命名空間，允許透過 AWS Cloud Map API 呼叫探索資源。

1. 執行下列命令來建立 HTTP 命名空間：

   ```
   aws servicediscovery create-http-namespace \
     --name cloudmap-tutorial \
     --creator-request-id cloudmap-tutorial-request
   ```

   命令會傳回 操作 ID。您可以使用下列命令檢查操作的狀態：

   ```
   aws servicediscovery get-operation \
     --operation-id operation-id
   ```

1. 建立命名空間後，您可以擷取其 ID 以用於後續命令：

   ```
   aws servicediscovery list-namespaces \
     --query "Namespaces[?Name=='cloudmap-tutorial'].Id" \
     --output text
   ```

1. 將命名空間 ID 存放在變數中以供日後使用：

   ```
   NAMESPACE_ID=$(aws servicediscovery list-namespaces \
     --query "Namespaces[?Name=='cloudmap-tutorial'].Id" \
     --output text)
   ```

## 建立 DynamoDB 資料表
<a name="create-a-dynamodb-table"></a>

接著，建立 DynamoDB 資料表來存放應用程式的資料：

1. 執行下列命令來建立資料表：

   ```
   aws dynamodb create-table \
     --table-name cloudmap \
     --attribute-definitions AttributeName=id,AttributeType=S \
     --key-schema AttributeName=id,KeyType=HASH \
     --billing-mode PAY_PER_REQUEST
   ```

1. 等待資料表變成作用中，再繼續：

   ```
   aws dynamodb wait table-exists --table-name cloudmap
   ```

   此命令會等到資料表完全建立並可供使用。

## 建立 AWS Cloud Map 資料服務並註冊 DynamoDB 資料表
<a name="create-an-aws-cloud-map-data-service-and-register-the-dynamodb-table"></a>

現在，在您的命名空間中建立服務來代表資料儲存資源：

1. 執行下列命令來建立資料儲存資源 AWS Cloud Map 的服務：

   ```
   aws servicediscovery create-service \
     --name data-service \
     --namespace-id $NAMESPACE_ID \
     --creator-request-id data-service-request
   ```

1. 取得資料服務的服務 ID：

   ```
   DATA_SERVICE_ID=$(aws servicediscovery list-services \
     --query "Services[?Name=='data-service'].Id" \
     --output text)
   ```

1. 使用指定資料表名稱的自訂屬性，將 DynamoDB 資料表註冊為服務執行個體：

   ```
   aws servicediscovery register-instance \
     --service-id $DATA_SERVICE_ID \
     --instance-id data-instance \
     --attributes tablename=cloudmap
   ```

   自訂屬性`tablename=cloudmap`可讓其他服務動態探索 DynamoDB 資料表名稱。

## 為 Lambda 函數建立 IAM 角色
<a name="create-an-iam-role-for-lambda-functions"></a>

建立 Lambda 函數用來存取 AWS 資源的 IAM 角色：

1. 使用下列 JSON 建立 IAM 角色的信任政策文件：

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

****  

   ```
   {
     "Version":"2012-10-17",		 	 	 		 	 	 
     "Statement": [
       {
         "Effect": "Allow",
         "Principal": {
           "Service": "lambda.amazonaws.com"
         },
         "Action": "sts:AssumeRole"
       }
     ]
   }
   ```

------

1. 執行下列命令，使用信任政策建立 IAM 角色：

   ```
   aws iam create-role \
     --role-name cloudmap-tutorial-role \
     --assume-role-policy-document file://lambda-trust-policy.json
   ```

1. 使用下列 JSON 為具有最低權限許可的自訂 IAM 政策建立檔案：

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

****  

   ```
   {
     "Version":"2012-10-17",		 	 	 		 	 	 
     "Statement": [
       {
         "Effect": "Allow",
         "Action": [
           "logs:CreateLogGroup",
           "logs:CreateLogStream",
           "logs:PutLogEvents"
         ],
         "Resource": "arn:aws:logs:*:*:*"
       },
       {
         "Effect": "Allow",
         "Action": [
           "servicediscovery:DiscoverInstances"
         ],
         "Resource": "*"
       },
       {
         "Effect": "Allow",
         "Action": [
           "dynamodb:PutItem",
           "dynamodb:Scan"
         ],
         "Resource": "arn:aws:dynamodb:*:*:table/cloudmap"
       }
     ]
   }
   ```

------

1. 建立政策並將其連接至 IAM 角色：

   ```
   aws iam create-policy \
     --policy-name CloudMapTutorialPolicy \
     --policy-document file://cloudmap-policy.json
   
   POLICY_ARN=$(aws iam list-policies \
     --query "Policies[?PolicyName=='CloudMapTutorialPolicy'].Arn" \
     --output text)
   
   aws iam attach-role-policy \
     --role-name cloudmap-tutorial-role \
     --policy-arn $POLICY_ARN
   
   aws iam attach-role-policy \
     --role-name cloudmap-tutorial-role \
     --policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
   ```

## 建立 Lambda 函數以寫入資料
<a name="create-the-lambda-function-to-write-data"></a>

若要建立將資料寫入 DynamoDB 資料表的 Lambda 函數，請遵循下列步驟：

1. 建立寫入函數的 Python 檔案：

   ```
   cat > writefunction.py << EOF
   import json
   import boto3
   import random
   
   def lambda_handler(event, context):
       try:
           serviceclient = boto3.client('servicediscovery')
           
           response = serviceclient.discover_instances(
               NamespaceName='cloudmap-tutorial',
               ServiceName='data-service')
           
           if not response.get("Instances"):
               return {
                   'statusCode': 500,
                   'body': json.dumps({"error": "No instances found"})
               }
               
           tablename = response["Instances"][0]["Attributes"].get("tablename")
           if not tablename:
               return {
                   'statusCode': 500,
                   'body': json.dumps({"error": "Table name attribute not found"})
               }
              
           dynamodbclient = boto3.resource('dynamodb')
              
           table = dynamodbclient.Table(tablename)
           
           # Validate input
           if not isinstance(event, str):
               return {
                   'statusCode': 400,
                   'body': json.dumps({"error": "Input must be a string"})
               }
              
           response = table.put_item(
               Item={ 'id': str(random.randint(1,100)), 'todo': event })
              
           return {
               'statusCode': 200,
               'body': json.dumps(response)
           }
       except Exception as e:
           return {
               'statusCode': 500,
               'body': json.dumps({"error": str(e)})
           }
   EOF
   ```

   此函數使用 從自訂屬性 AWS Cloud Map 探索 DynamoDB 資料表名稱，然後將資料寫入資料表。

1. 封裝和部署 Lambda 函數：

   ```
   zip writefunction.zip writefunction.py
   
   ROLE_ARN=$(aws iam get-role --role-name cloudmap-tutorial-role \
     --query 'Role.Arn' --output text)
   
   aws lambda create-function \
     --function-name writefunction \
     --runtime python3.12 \
     --role $ROLE_ARN \
     --handler writefunction.lambda_handler \
     --zip-file fileb://writefunction.zip \
     --architectures x86_64
   ```

1. 更新函數逾時以避免逾時錯誤：

   ```
   aws lambda update-function-configuration \
     --function-name writefunction \
     --timeout 5
   ```

## 建立 AWS Cloud Map 應用程式服務並註冊 Lambda 寫入函數
<a name="create-an-aws-cloud-map-app-service-and-register-the-lambda-write-function"></a>

若要在您的命名空間中建立其他服務來代表應用程式函數，請依照下列步驟執行：

1. 為應用程式函數建立服務：

   ```
   aws servicediscovery create-service \
     --name app-service \
     --namespace-id $NAMESPACE_ID \
     --creator-request-id app-service-request
   ```

1. 取得應用程式服務的服務 ID：

   ```
   APP_SERVICE_ID=$(aws servicediscovery list-services \
     --query "Services[?Name=='app-service'].Id" \
     --output text)
   ```

1. 使用自訂屬性將 Lambda 寫入函數註冊為服務執行個體：

   ```
   aws servicediscovery register-instance \
     --service-id $APP_SERVICE_ID \
     --instance-id write-instance \
     --attributes action=write,functionname=writefunction
   ```

   自訂屬性`action=write`，並`functionname=writefunction`允許用戶端根據其用途探索此函數。

## 建立 Lambda 函數以讀取資料
<a name="create-the-lambda-function-to-read-data"></a>

若要建立從 DynamoDB 資料表讀取資料的 Lambda 函數，請遵循下列步驟：

1. 為讀取函數建立 Python 檔案：

   ```
   cat > readfunction.py << EOF
   import json
   import boto3
   
   def lambda_handler(event, context):
       try:
           serviceclient = boto3.client('servicediscovery')
   
           response = serviceclient.discover_instances(
               NamespaceName='cloudmap-tutorial', 
               ServiceName='data-service')
           
           if not response.get("Instances"):
               return {
                   'statusCode': 500,
                   'body': json.dumps({"error": "No instances found"})
               }
               
           tablename = response["Instances"][0]["Attributes"].get("tablename")
           if not tablename:
               return {
                   'statusCode': 500,
                   'body': json.dumps({"error": "Table name attribute not found"})
               }
              
           dynamodbclient = boto3.resource('dynamodb')
              
           table = dynamodbclient.Table(tablename)
           
           # Use pagination for larger tables
           response = table.scan(
               Select='ALL_ATTRIBUTES',
               Limit=50  # Limit results for demonstration purposes
           )
           
           # For production, you would implement pagination like this:
           # items = []
           # while 'LastEvaluatedKey' in response:
           #     items.extend(response['Items'])
           #     response = table.scan(
           #         Select='ALL_ATTRIBUTES',
           #         ExclusiveStartKey=response['LastEvaluatedKey']
           #     )
           # items.extend(response['Items'])
   
           return {
               'statusCode': 200,
               'body': json.dumps(response)
           }
       except Exception as e:
           return {
               'statusCode': 500,
               'body': json.dumps({"error": str(e)})
           }
   EOF
   ```

   此函數也會使用 AWS Cloud Map 來探索 DynamoDB 資料表名稱，然後從資料表讀取資料。它包含錯誤處理和分頁註解。

1. 封裝和部署 Lambda 函數：

   ```
   zip readfunction.zip readfunction.py
   
   aws lambda create-function \
     --function-name readfunction \
     --runtime python3.12 \
     --role $ROLE_ARN \
     --handler readfunction.lambda_handler \
     --zip-file fileb://readfunction.zip \
     --architectures x86_64
   ```

1. 更新函數逾時：

   ```
   aws lambda update-function-configuration \
     --function-name readfunction \
     --timeout 5
   ```

## 將 Lambda 讀取函數註冊為服務執行個體
<a name="register-the-lambda-read-function-as-a-service-instance"></a>

若要將 Lambda 讀取函數註冊為應用程式服務中的另一個服務執行個體，請遵循下列步驟：

```
aws servicediscovery register-instance \
  --service-id $APP_SERVICE_ID \
  --instance-id read-instance \
  --attributes action=read,functionname=readfunction
```

自訂屬性`action=read`，並`functionname=readfunction`允許用戶端根據其用途探索此函數。

## 建立和執行用戶端應用程式
<a name="create-and-run-client-applications"></a>

若要建立 Python 用戶端應用程式，以使用 AWS Cloud Map 來探索和叫用寫入函數，請遵循下列步驟：

1. 為寫入用戶端應用程式建立 Python 檔案：

   ```
   cat > writeclient.py << EOF
   import boto3
   import json
   
   try:
       serviceclient = boto3.client('servicediscovery')
   
       print("Discovering write function...")
       response = serviceclient.discover_instances(
           NamespaceName='cloudmap-tutorial', 
           ServiceName='app-service', 
           QueryParameters={ 'action': 'write' }
       )
   
       if not response.get("Instances"):
           print("Error: No instances found")
           exit(1)
           
       functionname = response["Instances"][0]["Attributes"].get("functionname")
       if not functionname:
           print("Error: Function name attribute not found")
           exit(1)
           
       print(f"Found function: {functionname}")
   
       lambdaclient = boto3.client('lambda')
   
       print("Invoking Lambda function...")
       resp = lambdaclient.invoke(
           FunctionName=functionname, 
           Payload='"This is a test data"'
       )
   
       payload = resp["Payload"].read()
       print(f"Response: {payload.decode('utf-8')}")
       
   except Exception as e:
       print(f"Error: {str(e)}")
   EOF
   ```

   此用戶端使用 `QueryParameters`選項來尋找具有 `action=write` 屬性的服務執行個體。

1. 為讀取用戶端應用程式建立 Python 檔案：

   ```
   cat > readclient.py << EOF
   import boto3
   import json
   
   try:
       serviceclient = boto3.client('servicediscovery')
   
       print("Discovering read function...")
       response = serviceclient.discover_instances(
           NamespaceName='cloudmap-tutorial', 
           ServiceName='app-service', 
           QueryParameters={ 'action': 'read' }
       )
   
       if not response.get("Instances"):
           print("Error: No instances found")
           exit(1)
           
       functionname = response["Instances"][0]["Attributes"].get("functionname")
       if not functionname:
           print("Error: Function name attribute not found")
           exit(1)
           
       print(f"Found function: {functionname}")
   
       lambdaclient = boto3.client('lambda')
   
       print("Invoking Lambda function...")
       resp = lambdaclient.invoke(
           FunctionName=functionname, 
           InvocationType='RequestResponse'
       )
   
       payload = resp["Payload"].read()
       print(f"Response: {payload.decode('utf-8')}")
       
   except Exception as e:
       print(f"Error: {str(e)}")
   EOF
   ```

1. 執行寫入用戶端，將資料新增至 DynamoDB 資料表：

   ```
   python3 writeclient.py
   ```

   輸出應會顯示 HTTP 狀態碼為 200 的成功回應。

1. 執行讀取用戶端以從 DynamoDB 資料表擷取資料：

   ```
   python3 readclient.py
   ```

   輸出應會顯示寫入資料表的資料，包括隨機產生的 ID 和「這是測試資料」值。

## 清除資源
<a name="clean-up-resources"></a>

完成教學課程後，請清除資源以避免產生額外費用。

1. 首先，執行下列命令來取消註冊服務執行個體：

   ```
   aws servicediscovery deregister-instance \
     --service-id $APP_SERVICE_ID \
     --instance-id read-instance
   
   aws servicediscovery deregister-instance \
     --service-id $APP_SERVICE_ID \
     --instance-id write-instance
   
   aws servicediscovery deregister-instance \
     --service-id $DATA_SERVICE_ID \
     --instance-id data-instance
   ```

1. 執行下列命令來刪除服務：

   ```
   aws servicediscovery delete-service \
     --id $APP_SERVICE_ID
   
   aws servicediscovery delete-service \
     --id $DATA_SERVICE_ID
   ```

1. 執行下列命令來刪除命名空間：

   ```
   aws servicediscovery delete-namespace \
     --id $NAMESPACE_ID
   ```

1. 執行下列命令來刪除 Lambda 函數：

   ```
   aws lambda delete-function --function-name writefunction
   aws lambda delete-function --function-name readfunction
   ```

1. 執行下列命令來刪除 IAM 角色和政策：

   ```
   aws iam detach-role-policy \
     --role-name cloudmap-tutorial-role \
     --policy-arn $POLICY_ARN
   
   aws iam detach-role-policy \
     --role-name cloudmap-tutorial-role \
     --policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
   
   aws iam delete-policy \
     --policy-arn $POLICY_ARN
   
   aws iam delete-role --role-name cloudmap-tutorial-role
   ```

1. 執行下列命令來刪除 DynamoDB 資料表：

   ```
   aws dynamodb delete-table --table-name cloudmap
   ```

1. 執行下列命令來清除暫存檔案：

   ```
   rm -f lambda-trust-policy.json cloudmap-policy.json writefunction.py readfunction.py writefunction.zip readfunction.zip writeclient.py readclient.py
   ```