

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

# 具託管服務的自訂推論程式碼
<a name="your-algorithms-inference-code"></a>

本節說明 Amazon SageMaker AI 與執行您自有的推論程式碼以取得託管服務之 Docker 容器的互動方式。請運用本文資訊來撰寫推論程式碼和建立 Docker 映像。

**Topics**
+ [SageMaker AI 執行推論映像的方式](#your-algorithms-inference-code-run-image)
+ [SageMaker AI 載入模型成品的方式](#your-algorithms-inference-code-load-artifacts)
+ [容器對推論請求應有的回應方式](#your-algorithms-inference-code-container-response)
+ [容器對運作狀態檢查 (Ping) 請求應有的回應方式](#your-algorithms-inference-algo-ping-requests)
+ [支援雙向串流功能的容器合約](#your-algorithms-inference-algo-bidi)
+ [讓即時推論容器使用私有 Docker 登錄檔](your-algorithms-containers-inference-private.md)

## SageMaker AI 執行推論映像的方式
<a name="your-algorithms-inference-code-run-image"></a>

欲設定容器做為可執行檔來執行，請使用 Dockerfile 的 `ENTRYPOINT` 指示。注意下列事項：
+ 對於模型推論，SageMaker AI 執行容器的方法如下所示：

  ```
  docker run image serve
  ```

  SageMaker AI 在映像名稱後指定 `serve` 引數，藉此覆寫容器中預設的 `CMD` 陳述式。您在 Dockerfile 中搭配 `CMD` 指令提供的引數，會被 `serve` 引數覆寫。

   
+ SageMaker AI 預期所有容器都使用根使用者執行。建立您的容器，使其僅使用根使用者。SageMaker AI 執行您的容器時，沒有根層級存取權的使用者可能會造成許可問題。

   
+ 建議使用 `ENTRYPOINT` 指示的 `exec` 格式：

  ```
  ENTRYPOINT ["executable", "param1", "param2"]
  ```

  例如：

  ```
  ENTRYPOINT ["python", "k_means_inference.py"]
  ```

  `ENTRYPOINT` 指示的 `exec` 格式會直接做為可執行檔啟動，而非 `/bin/sh` 的子項。如此其便能自 SageMaker API 作業接收 `SIGTERM` 和 `SIGKILL` 等訊號，而這是一項要求。

   

  例如，使用 [https://docs.aws.amazon.com/sagemaker/latest/APIReference/API_CreateEndpoint.html](https://docs.aws.amazon.com/sagemaker/latest/APIReference/API_CreateEndpoint.html) API 建立端點時，SageMaker AI 所佈建的機器學習 (ML) 運算執行個體數量，正是您在請求中所指定的端點組態所需的數量。SageMaker AI 會在這些執行個體上執行 Docker 容器。

   

  若減少支援端點的執行個體數目 (藉由呼叫 [https://docs.aws.amazon.com/sagemaker/latest/APIReference/API_UpdateEndpointWeightsAndCapacities.html](https://docs.aws.amazon.com/sagemaker/latest/APIReference/API_UpdateEndpointWeightsAndCapacities.html) API)，SageMaker AI 會執行命令，讓被終止的執行個體上的 Docker 容器停止。該命令會傳送出 `SIGTERM` 訊號，三十秒後再傳送 `SIGKILL` 訊號。

   

  若更新端點 (藉由呼叫 [https://docs.aws.amazon.com/sagemaker/latest/APIReference/API_UpdateEndpoint.html](https://docs.aws.amazon.com/sagemaker/latest/APIReference/API_UpdateEndpoint.html) API)，SageMaker AI 會啟動另一組機器學習 (ML) 運算執行個體，並在上面執行包含您自有的推論程式碼的 Docker 容器。然後會執行命令，將前一個 Docker 容器停止。若要停止 Docker 容器，命令會傳送 `SIGTERM` 訊號，30 秒後再傳送 `SIGKILL` 訊號。

   
+ SageMaker AI 會使用您在 [https://docs.aws.amazon.com/sagemaker/latest/APIReference/API_CreateModel.html](https://docs.aws.amazon.com/sagemaker/latest/APIReference/API_CreateModel.html) 請求中提供的容器定義，設定環境變數和容器的 DNS 主機名稱，如下所示：

   
  + 使用 `ContainerDefinition.Environment` 字串對字串的映射來設定環境變數。
  + 它使用 `ContainerDefinition.ContainerHostname` 設定 DNS 主機名稱。

     
+ 如果您計劃使用 GPU 裝置進行模型推論 (在您的 `CreateEndpointConfig` 請求中指定以 GPU 為基礎的機器學習 (ML) 運算執行個體)，請確保您的容器與 `nvidia-docker` 相容。請勿將 NVIDIA 驅動程式與映像整合成套件。如需 `nvidia-docker` 的詳細資訊，請參閱 [NVIDIA/nvidia-docker](https://github.com/NVIDIA/nvidia-docker)。

   
+ 您無法將 `tini` 初始設定式作為 SageMaker AI 容器的進入點使用，因為 `train` 和 `serve` 引數會混淆該設定式。

  

## SageMaker AI 載入模型成品的方式
<a name="your-algorithms-inference-code-load-artifacts"></a>

在 [https://docs.aws.amazon.com/sagemaker/latest/APIReference/API_CreateModel.html](https://docs.aws.amazon.com/sagemaker/latest/APIReference/API_CreateModel.html) API 請求中，您可以使用 `ModelDataUrl` 或 `S3DataSource` 參數來識別存放模型成品的 S3 位置。SageMaker AI 會從 S3 位置將模型成品複製到 `/opt/ml/model` 目錄內，以供您的推論程式碼使用。您的容器只有 `/opt/ml/model` 的唯讀存取權限。請勿寫入此目錄。

`ModelDataUrl` 必須指向 tar.gz 檔案。否則，SageMaker AI 不會下載檔案。

如果在 SageMaker AI 訓練您的模型，則模型成品會儲存為 Amazon S3 的單一壓縮 tar 檔案。如果您在 SageMaker AI 外部訓練模型，則需要建立此單一壓縮 tar 檔案，並將其儲存在 S3 位置。容器開始之前，SageMaker AI 會將此 tar 檔案解壓縮至 /opt/ml/model 目錄。

對於部署大型模型，我們建議您依照 [部署未壓縮的模型](large-model-inference-uncompressed.md)。

## 容器對推論請求應有的回應方式
<a name="your-algorithms-inference-code-container-response"></a>

為了取得推論，用戶端應用程式會傳送 POST 請求至 SageMaker AI 端點。SageMaker AI 將請求傳遞至容器，再將容器所得的推論結果傳回用戶端。

如需容器將收到的推論請求的詳細資訊，請參閱 *Amazon SageMaker AI API 參考*中的下列動作：
+ [ InvokeEndpoint](https://docs.aws.amazon.com/sagemaker/latest/APIReference/API_runtime_InvokeEndpoint.html)
+ [ InvokeEndpointAsync](https://docs.aws.amazon.com/sagemaker/latest/APIReference/API_runtime_InvokeEndpointAsync.html)
+ [ InvokeEndpointWithResponseStream](https://docs.aws.amazon.com/sagemaker/latest/APIReference/API_runtime_InvokeEndpointWithResponseStream.html)
+ [ InvokeEndpointWithResponseStream](https://docs.aws.amazon.com/sagemaker/latest/APIReference/API_runtime_InvokeEndpointWithBidirectionalStream.html)

**推論容器的需求**

若要回應推論請求，您的容器須符合下列要求：
+ SageMaker AI 會移除所有 `POST` 標題，僅會留下由 `InvokeEndpoint` 支援的部分。SageMaker AI 可能會新增其他標題。推論容器必須能安全地忽略這類額外的標題。
+ 為了接收推論請求，容器須擁有可以監聽 8080 連接埠的 Web 伺服器，且需接受傳至 `/invocations` 和 `/ping` 端點的 `POST` 請求。
+ 客戶的模型容器必須在 250 毫秒內接受插槽連線請求。
+ 客戶的模型容器必須在 60 秒內回應請求。模型本身在回應 `/invocations` 之前的處理時間上限為 60 秒。如果您的模型處理時間需要 50-60 秒，則 SDK 的插槽逾時應設為 70 秒。
+ 支援雙向串流的客戶模型容器必須：
  + 根據預設， 支援連接埠 8080 到 /invocations-bidirectional-stream 的 WebSockets 連線。
  + 讓 Web 伺服器接聽連接埠 8080，且必須接受對 /ping 端點的 POST 請求。
  + 除了透過 HTTP 進行容器運作狀態檢查之外，容器還必須針對傳送的 WebSocket Ping Frame 回應 Pong Frame per ([RFC6455](https://datatracker.ietf.org/doc/html/rfc6455#section-5.5.3))。

**Example 調用函式**  
下列範例示範容器中的程式碼如何處理推論請求。這些範例處理用戶端應用程式使用 InvokeEndpoint 動作傳送的請求。  
FastAPI 是使用 Python 建置 API 的 Web 架構。  

```
from fastapi import FastAPI, status, Request, Response
. . .
app = FastAPI()
. . .
@app.post('/invocations')
async def invocations(request: Request):
    # model() is a hypothetical function that gets the inference output:
    model_resp = await model(Request)

    response = Response(
        content=model_resp,
        status_code=status.HTTP_200_OK,
        media_type="text/plain",
    )
    return response
. . .
```
在此範例中，`invocations` 函式處理 SageMaker AI 傳送至 `/invocations` 端點的推論請求。
Flask 是使用 Python 開發 Web 應用程式的架構。  

```
import flask
. . .
app = flask.Flask(__name__)
. . .
@app.route('/invocations', methods=["POST"])
def invoke(request):
    # model() is a hypothetical function that gets the inference output:
    resp_body = model(request)
    return flask.Response(resp_body, mimetype='text/plain')
```
在此範例中，`invoke` 函式處理 SageMaker AI 傳送至 `/invocations` 端點的推論請求。

**Example 串流請求的調用函式**  
下列範例示範推論容器中的程式碼如何處理串流推論請求。這些範例處理用戶端應用程式使用 InvokeEndpointWithResponseStream 動作傳送的請求。  
容器處理串流推論請求時，在模型產生推論時會以遞增方式連續傳回一部分的模型推論。用戶端應用程式在可用時立即開始接收回應。該應用程式不需要等待模型產生整個回應。您可以實作串流以支援快速互動體驗，例如聊天機器人、虛擬助理和音樂產生器。  
FastAPI 是使用 Python 建置 API 的 Web 架構。  

```
from starlette.responses import StreamingResponse
from fastapi import FastAPI, status, Request
. . .
app = FastAPI()
. . .
@app.post('/invocations')
async def invocations(request: Request):
    # Streams inference response using HTTP chunked encoding
    async def generate():
        # model() is a hypothetical function that gets the inference output:
        yield await model(Request)
        yield "\n"

    response = StreamingResponse(
        content=generate(),
        status_code=status.HTTP_200_OK,
        media_type="text/plain",
    )
    return response
. . .
```
在此範例中，`invocations` 函式處理 SageMaker AI 傳送至 `/invocations` 端點的推論請求。為了串流回應，該範例使用 Starlette 架構中的`StreamingResponse` 類別。
Flask 是使用 Python 開發 Web 應用程式的架構。  

```
import flask
. . .
app = flask.Flask(__name__)
. . .
@app.route('/invocations', methods=["POST"])
def invocations(request):
    # Streams inference response using HTTP chunked encoding

    def generate():
        # model() is a hypothetical function that gets the inference output:
        yield model(request)
        yield "\n"
    return flask.Response(
        flask.stream_with_context(generate()), mimetype='text/plain')
. . .
```
在此範例中，`invocations` 函式處理 SageMaker AI 傳送至 `/invocations` 端點的推論請求。為了串流回應，該範例使用 Flask 架構中的 `flask.stream_with_context` 函式。

**Example 雙向串流的範例調用函數**  
下列範例示範容器中的程式碼如何處理串流推論請求和回應。這些範例使用 InvokeEndpointWithBidirectionalStream 動作來處理用戶端應用程式傳送的串流請求。  
具有雙向串流功能的容器會處理串流推論請求，其中部分會在用戶端遞增產生並串流至容器。它會在模型產生模型時，以一系列部分的形式將模型的推論傳回給用戶端。用戶端應用程式在可用時立即開始接收回應。他們不需要等待在用戶端完全產生的 請求，也不需要等待模型產生整個回應。您可以實作雙向串流以支援快速互動式體驗，例如聊天機器人、互動式語音 AI 助理和即時翻譯，以獲得更即時的體驗。  
FastAPI 是使用 Python 建置 API 的 Web 架構。  

```
import sys
import asyncio
import json
from fastapi import FastAPI, WebSocket, WebSocketDisconnect
from fastapi.responses import JSONResponse
import uvicorn

app = FastAPI()
...
@app.websocket("/invocations-bidirectional-stream")
async def websocket_invoke(websocket: WebSocket):
    """
    WebSocket endpoint with RFC 6455 ping/pong and fragmentation support
    
    Handles:
    - Text messages (JSON) - including fragmented frames
    - Binary messages - including fragmented frames
    - Ping frames (automatically responds with pong)
    - Pong frames (logs receipt)
    - Fragmented frames per RFC 6455 Section 5.4
    """
    await manager.connect(websocket)
    
    # Fragment reassembly buffers per RFC 6455 Section 5.4
    text_fragments = []
    binary_fragments = []
    
    while True:
        # Use receive() to handle all WebSocket frame types
        message = await websocket.receive()
        print(f"Received message: {message}")
        if message["type"] == "websocket.receive":
            if "text" in message:
                # Handle text frames (including fragments)
                text_data = message["text"]
                more_body = message.get("more_body", False)
                
                if more_body:
                    # This is a fragment, accumulate it
                    text_fragments.append(text_data)
                    print(f"Received text fragment: {len(text_data)} chars (more coming)")
                else:
                    # This is the final frame or a complete message
                    if text_fragments:
                        # Reassemble fragmented message
                        text_fragments.append(text_data)
                        complete_text = "".join(text_fragments)
                        text_fragments.clear()
                        print(f"Reassembled fragmented text message: {len(complete_text)} chars total")
                        await handle_text_message(websocket, complete_text)
                    else:
                        # Complete message in single frame
                        await handle_text_message(websocket, text_data)
                
            elif "bytes" in message:
                # Handle binary frames (including fragments)
                binary_data = message["bytes"]
                more_body = message.get("more_body", False)
                
                if more_body:
                    # This is a fragment, accumulate it
                    binary_fragments.append(binary_data)
                    print(f"Received binary fragment: {len(binary_data)} bytes (more coming)")
                else:
                    # This is the final frame or a complete message
                    if binary_fragments:
                        # Reassemble fragmented message
                        binary_fragments.append(binary_data)
                        complete_binary = b"".join(binary_fragments)
                        binary_fragments.clear()
                        print(f"Reassembled fragmented binary message: {len(complete_binary)} bytes total")
                        await handle_binary_message(websocket, complete_binary)
                    else:
                        # Complete message in single frame
                        await handle_binary_message(websocket, binary_data)
                
        elif message["type"] == "websocket.ping":
            # Handle ping frames - RFC 6455 Section 5.5.2
            ping_data = message.get("bytes", b"")
            print(f"Received PING frame with payload: {ping_data}")
            # FastAPI automatically sends pong response
            
        elif message["type"] == "websocket.pong":
            # Handle pong frames
            pong_data = message.get("bytes", b"")
            print(f"Received PONG frame with payload: {pong_data}")
            
        elif message["type"] == "websocket.close":
            # Handle close frames - RFC 6455 Section 5.5.1
            close_code = message.get("code", 1000)
            close_reason = message.get("reason", "")
            print(f"Received CLOSE frame - Code: {close_code}, Reason: '{close_reason}'")
            
            # Send close frame response if not already closing
            try:
                await websocket.close(code=close_code, reason=close_reason)
                print(f"Sent CLOSE frame response - Code: {close_code}")
            except Exception as e:
                print(f"Error sending close frame: {e}")
            break
            
        elif message["type"] == "websocket.disconnect":
            print("Client initiated disconnect")
            break

        else:
            print(f"Received unknown message type: {message['type']}")
            break

                        
async def handle_binary_message(websocket: WebSocket, binary_data: bytes):
    """Handle incoming binary messages (complete or reassembled from fragments)"""
    print(f"Processing complete binary message: {len(binary_data)} bytes")
    
    try:
        # Echo back the binary data
        await websocket.send_bytes(binary_data)
    except Exception as e:
        print(f"Error handling binary message: {e}")

async def handle_text_message(websocket: WebSocket, data: str):
    """Handle incoming text messages"""
    try:
        # Send response back to the same client
        await manager.send_personal_message(data, websocket)
    except Exception as e:
        print(f"Error handling text message: {e}")

def main():
    if len(sys.argv) > 1 and sys.argv[1] == "serve":
        print("Starting server on port 8080...")
        uvicorn.run(app, host="0.0.0.0", port=8080)
    else:
        print("Usage: python app.py serve")
        sys.exit(1)

if __name__ == "__main__":
    main()
```
在此範例中，`websocket_invoke` 函式處理 SageMaker AI 傳送至 `/invocations-bidirectional-stream` 端點的推論請求。它會顯示處理串流請求並將回應串流回用戶端。

## 容器對運作狀態檢查 (Ping) 請求應有的回應方式
<a name="your-algorithms-inference-algo-ping-requests"></a>

SageMaker AI 會在以下情況啟動新的推論容器：
+ 回應 `CreateEndpoint`、`UpdateEndpoint` 和 `UpdateEndpointWeightsAndCapacities` API 呼叫
+ 安全性修補程式
+ 取代狀態不佳的執行個體

容器啟動後不久，SageMaker AI 會開始定期傳送 GET 請求至 `/ping` 端點。

容器上最簡單的請求是以 HTTP 200 狀態碼和空內文做為回應。這是通知 SageMaker AI，容器已準備好可在 `/invocations` 端點接受推論請求。

如果容器並未開始通過運作狀態檢查，即在啟動後的 8 分鐘內持續回應 200 狀態碼，則新執行個體啟動會失敗。這會導致 `CreateEndpoint` 失敗，使端點處於失敗狀態。`UpdateEndpoint` 請求的更新不會完成，不會套用安全修補程式，且不會取代狀況不良的執行個體。

雖然容器的最低標準是傳回靜態的 200，容器開發人員也能運用此功能來進行更加深入的檢查。`/ping` 嘗試的請求逾時為 2 秒。

此外，能夠處理雙向串流請求的容器必須使用 Pong Frame （每個 WebSocket 通訊協定 [RFC6455](https://datatracker.ietf.org/doc/html/rfc6455#section-5.5.3)) 回應 Ping Frame。如果連續 5 個 Ping 未收到 Pong Frame，SageMaker AI 平台會關閉與容器的連線。SageMaker AI 平台也會使用 Pong Frames 從模型容器回應 Ping Frames。

## 支援雙向串流功能的容器合約
<a name="your-algorithms-inference-algo-bidi"></a>

如果您想要將模型容器託管為支援雙向串流功能的 SageMaker AI 端點，則模型容器必須支援以下合約：

**1。雙向 Docker 標籤 **

模型容器應該有一個 Docker 標籤，向 SageMaker AI 平台指出此容器支援雙向串流功能。

```
com.amazonaws.sagemaker.capabilities.bidirectional-streaming=true
```

**2. 支援叫用 WebSocket 連線**

根據`/invocations-bidirectional-stream`預設，支援雙向串流的客戶模型容器必須支援連接埠 8080 到 的 WebSockets 連線。

叫用 InvokeEndpointWithBidirectionalStream API 時，傳遞 X-Amzn-SageMaker-Model-Invocation-Path 標頭可以覆寫此路徑。此外，使用者可以指定要附加至此路徑的查詢字串，方法是在叫用 InvokeEndpointWithBidirectionalStream API 時傳遞 X-Amzn-SageMaker-Model-Query-String 標頭。

**3. 請求串流處理**

InvokeEndpointWithBidirectionalStream API 輸入承載會以一系列的 PayloadParts 串流，這只是二進位區塊的包裝函式 (「位元組」：***<Blob>***)：

```
{
   "PayloadPart": { 
      "Bytes": <Blob>,
      "DataType": <String: UTF8 | BINARY>,
      "CompletionState": <String: PARTIAL | COMPLETE>
      "P": <String>
   }
}
```

**3.1. 資料框架**

SageMaker AI 會將輸入 PayloadParts 傳遞至模型容器做為 WebSocket Data Frames ([RFC6455-Section-5.6](https://datatracker.ietf.org/doc/html/rfc6455#section-5.6))

1. SageMaker AI 不會檢查為二進位區塊。

1. 接收輸入 PayloadPart 時
   + SageMaker AI 從 建立恰好一個 WebSocket 資料框架`PayloadPart.Bytes`，然後將其傳遞至模型容器。
   + 如果為 `PayloadPart.DataType = UTF8`，SageMaker AI 會建立文字資料框架
   + 如果 `PayloadPart.DataType` 不存在 或 `PayloadPart.DataType = BINARY`，SageMaker AI 會建立二進位資料框架

1. 對於一系列具有 的 PayloadParts`PayloadPart.CompletionState = PARTIAL`，以及由具有 的 PayloadPart 終止`PayloadPart.CompletionState = COMPLETE`，SageMaker AI 會將它們轉譯為 WebSocket 分段訊息 [RFC6455-Section-5.4： Fragmentation](https://datatracker.ietf.org/doc/html/rfc6455#section-5.4)：
   + 具有 的初始 PayloadPart `PayloadPart.CompletionState = PARTIAL`將翻譯為 WebSocket 資料框架，其中 FIN 位元清除。
   + 搭配 的後續 PayloadParts `PayloadPart.CompletionState = PARTIAL`會轉譯為具有 FIN 位元清除的 WebSocket 持續影格。
   + 使用 的最終 PayloadPart `PayloadPart.CompletionState = COMPLETE`將翻譯為具有 FIN 位元集的 WebSocket 持續架構。

1. SageMaker AI 不會從輸入 PayloadPart 編碼或解碼二進位區塊，位元組會依原狀傳遞至模型容器。

1. SageMaker AI 不會將多個輸入 PayloadParts 合併為一個 BinaryDataFrame。

1. SageMaker AI 不會將一個輸入 PayloadPart 區塊化為多個 BinaryDataFrames

**範例：分段訊息流程**

```
Client sends:
PayloadPart 1: {Bytes: "Hello ", DataType: "UTF8", CompletionState: "PARTIAL"}
PayloadPart 2: {Bytes: "World", DataType: "UTF8", CompletionState: "COMPLETE"}

Container receives:
Frame 1: Text Data Frame with "Hello " (FIN=0)
Frame 2: Continuation Frame with "World" (FIN=1)
```

**3.2. 控制影格**

除了資料影格之外，SageMaker AI 還會將控制影格傳送至模型容器 ([RFC6455-Section-5.5](https://datatracker.ietf.org/doc/html/rfc6455#section-5.5))：

1. 關閉影格：如果連線因任何原因關閉，SageMaker AI 可能會傳送關閉影格 ([RFC6455-Section-5.5.1](https://datatracker.ietf.org/doc/html/rfc6455#section-5.5.1)) 給模型容器。

1. Ping Frame：SageMaker AI 每 60 秒傳送一次 Ping Frame ([RFC6455-Section-5.5.2](https://datatracker.ietf.org/doc/html/rfc6455#section-5.5.2))，模型容器必須使用 Pong Frame 回應。如果連續 5 個 Ping 未收到 Pong Frame ([RFC6455-Section-5.5.3](https://datatracker.ietf.org/doc/html/rfc6455#section-5.5.3))，SageMaker AI 會關閉連線。

1. Pong Frame：SageMaker AI 將使用 Pong Frames 從模型容器回應 Ping Frames。

**4. 回應串流處理**

輸出會串流為一系列的 PayloadParts、ModelStreamErrors 或 InternalStreamFailures。

```
{   
   "PayloadPart": { 
      "Bytes": <Blob>,
      "DataType": <String: UTF8 | BINARY>,
      "CompletionState": <String: PARTIAL | COMPLETE>,
   },
   "ModelStreamError": {
      "ErrorCode": <String>,
      "Message": <String>
   },
   "InternalStreamFailure": {
      "Message": <String>
   }
}
```

**4.1. 資料框架**

SageMaker AI 會將從模型容器接收的資料框架轉換為輸出 PayloadParts：

1. 從模型容器接收 WebSocket Text Data Frame 時，SageMaker AI 會從文字資料框架取得原始位元組，並將其包裝為回應 PayloadPart，同時設定 `PayloadPart.DataType = UTF8`。

1. 從模型容器接收 WebSocket 二進位資料框架時，SageMaker AI 會將資料框架中的位元組直接包裝為回應 PayloadPart，同時設定 `PayloadPart.DataType = BINARY`。

1. 對於 [RFC6455-Section-5.4： Fragmentation](https://datatracker.ietf.org/doc/html/rfc6455#section-5.4) 中定義的分段訊息：
   + 具有 FIN 位元清除的初始資料框架將轉換為具有 的 PayloadPart`PayloadPart.CompletionState = PARTIAL`。
   + 具有 FIN 位元清除的後續連續影格將使用 轉換為 PayloadParts`PayloadPart.CompletionState = PARTIAL`。
   + 具有 FIN 位元集的最終 Continuation Frame 將使用 轉換為 PayloadPart`PayloadPart.CompletionState = COMPLETE`。

1. SageMaker AI 不會對從模型容器收到的位元組進行編碼或解碼，位元組會依原狀傳遞到模型容器。

1. SageMaker AI 不會將從模型容器收到的多個資料框架合併為一個回應 PayloadPart。

1. SageMaker AI 不會將從模型容器接收的資料框架區塊化為多個回應 PayloadParts。

**範例：串流回應流程**

```
Container sends:
Frame 1: Text Data Frame with "Generating" (FIN=0)
Frame 2: Continuation Frame with " response..." (FIN=1)

Client receives:
PayloadPart 1: {Bytes: "Generating", DataType: "UTF8", CompletionState: "PARTIAL"}
PayloadPart 2: {Bytes: " response...", DataType: "UTF8", CompletionState: "COMPLETE"}
```

**4.2. 控制影格**

SageMaker AI 會從模型容器回應下列控制框架：

1. 從模型容器接收關閉影格 ([RFC6455-Section-5.5.1](https://datatracker.ietf.org/doc/html/rfc6455#section-5.5.1)) 時，SageMaker AI 會將狀態碼 ([RFC6455-Section-7.4](https://datatracker.ietf.org/doc/html/rfc6455#section-7.4)) 和失敗訊息包裝到 ModelStreamError，並將其串流回最終使用者。

1. 從模型容器接收 Ping Frame ([RFC6455-Section-5.5.2](https://datatracker.ietf.org/doc/html/rfc6455#section-5.5.2)) 時，SageMaker AI 將回應 Pong Frame。

1. Pong Frame([RFC6455-Section-5.5.3](https://datatracker.ietf.org/doc/html/rfc6455#section-5.5.3))：如果連續 5 個 Ping 未收到 Pong Frame，SageMaker AI 會關閉連線。

# 讓即時推論容器使用私有 Docker 登錄檔
<a name="your-algorithms-containers-inference-private"></a>

Amazon SageMaker AI 託管可讓您使用儲存在 Amazon ECR 中的映像，依預設值建置即時推論容器。或者，您可以從私有 Docker 登錄檔中的映像建立即時推論容器。私有登錄檔必須可以從您帳戶中的 Amazon VPC 存取。您基於私有 Docker 登錄檔中儲存的映像建立的模型須設定為連線至可存取私有 Docker 登錄檔的相同 VPC。如需將您的模型連線至 VPC 的資訊，請參閱[讓 SageMaker AI 託管的端點可以存取 Amazon VPC 中的資源](host-vpc.md)。

Docker 登錄檔須使用來自已知公有憑證授權單位 (CA) 的 TLS 憑證來保護。

**注意**  
您的私有 Docker 登錄檔須允許來自您在 VPC 組態中為模型指定的安全群組的傳入流量，以便 SageMaker AI 託管能夠從登錄檔中提取模型映像。  
如果您的 VPC 內有通往開放網際網路的路徑，SageMaker AI 可以從 DockerHub 提取模型映像。

**Topics**
+ [將映像儲存在 Amazon Elastic Container Registry 以外的私有 Docker 登錄檔中](#your-algorithms-containers-inference-private-registry)
+ [使用來自私有 Docker 登錄檔的映像進行即時推論](#your-algorithms-containers-inference-private-use)
+ [允許 SageMaker AI 向私有 Docker 登錄檔進行驗證](#inference-private-docker-authenticate)
+ [建立 Lambda 函式](#inference-private-docker-lambda)
+ [將您的執行角色許可給予 Lambda](#inference-private-docker-perms)
+ [為 Lambda 建立介面 VPC 端點](#inference-private-docker-vpc-interface)

## 將映像儲存在 Amazon Elastic Container Registry 以外的私有 Docker 登錄檔中
<a name="your-algorithms-containers-inference-private-registry"></a>

若要使用私有 Docker 登錄檔儲存您的映像以供 SageMaker AI 即時推論使用，請建立可從 Amazon VPC 存取的私有登錄檔。如需建立 Docker 登錄檔的相關資訊，請參閱 Docker 文件中的[部署登錄伺服器](https://docs.docker.com/registry/deploying/)。Docker 登錄檔須符合下列規定：
+ 登錄檔須為 [Docker 登錄檔 HTTP API V2](https://docs.docker.com/registry/spec/api/) 登錄檔。
+ Docker 登錄檔須可從您在建立模型時指定的 `VpcConfig` 參數中指定的相同 VPC 存取。

## 使用來自私有 Docker 登錄檔的映像進行即時推論
<a name="your-algorithms-containers-inference-private-use"></a>

建立模型並將其部署到 SageMaker AI 託管時，您可以指定該模型使用私有 Docker 登錄檔中的映像來建置推論容器。在傳遞給 [create\$1model](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/sagemaker.html#SageMaker.Client.create_model) 函式呼叫的 `PrimaryContainer` 參數中的 `ImageConfig` 物件中指定此值。

**若要將儲存在私有 Docker 登錄檔中的映像用於推論容器**

1. 建立映像組態物件，並為 `RepositoryAccessMode` 欄位指定 `Vpc` 的值。

   ```
   image_config = {
                       'RepositoryAccessMode': 'Vpc'
                  }
   ```

1. 如果您的私有 Docker 登錄檔需要驗證，請將 `RepositoryAuthConfig` 物件新增至映像組態物件。針對 `RepositoryAuthConfig` 物件的 `RepositoryCredentialsProviderArn` 欄位，指定 函數的 Amazon Resource Name (ARN)，該 AWS Lambda 函數提供登入資料，允許 SageMaker AI 驗證您的私有 Docker 登錄檔。如需如何建立 Lambda 函式以提供驗證的相關資訊，請參閱[允許 SageMaker AI 向私有 Docker 登錄檔進行驗證](#inference-private-docker-authenticate)。

   ```
   image_config = {
                       'RepositoryAccessMode': 'Vpc',
                       'RepositoryAuthConfig': {
                          'RepositoryCredentialsProviderArn': 'arn:aws:lambda:Region:Acct:function:FunctionName'
                        }
                  }
   ```

1. 使用您在上一步建立的映像組態物件，建立要傳遞至 `create_model` 的主要容器物件。

   以[摘要](https://docs.docker.com/engine/reference/commandline/pull/#pull-an-image-by-digest-immutable-identifier)形式提供您的映像。如果您使用 `:latest` 標籤提供映像，則 SageMaker AI 可能會提取比預期更新的映像版本。使用摘要表單，確保 SageMaker AI 提取預期的映像版本。

   ```
   primary_container = {
       'ContainerHostname': 'ModelContainer',
       'Image': 'myteam.myorg.com/docker-local/my-inference-image:<IMAGE-TAG>',
       'ImageConfig': image_config
   }
   ```

1. 指定模型名稱與您要傳遞至 `create_model` 的執行角色。

   ```
   model_name = 'vpc-model'
   execution_role_arn = 'arn:aws:iam::123456789012:role/SageMakerExecutionRole'
   ```

1. 為您的模型的 VPC 組態指定一個或多個安全群組和子網路。私有 Docker 登錄檔須允許來自您指定的安全性群組的傳入流量。您指定的子網路須與私有 Docker 登錄檔位於同一 VPC 中。

   ```
   vpc_config = {
       'SecurityGroupIds': ['sg-0123456789abcdef0'],
       'Subnets': ['subnet-0123456789abcdef0','subnet-0123456789abcdef1']
   }
   ```

1. 取得 Boto3 SageMaker AI 用戶端。

   ```
   import boto3
   sm = boto3.client('sagemaker')
   ```

1. 呼叫 `create_model` 來建立模型，使用您在上一步為 `PrimaryContainer` 和 `VpcConfig` 參數指定的值。

   ```
   try:
       resp = sm.create_model(
           ModelName=model_name,
           PrimaryContainer=primary_container,
           ExecutionRoleArn=execution_role_arn,
           VpcConfig=vpc_config,
       )
   except Exception as e:
       print(f'error calling CreateModel operation: {e}')
   else:
       print(resp)
   ```

1. 最後，使用您在上一步建立的模型，呼叫 [create\$1endpoint\$1config](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/sagemaker.html#SageMaker.Client.create_endpoint_config) 和 [create\$1endpoint](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/sagemaker.html#SageMaker.Client.create_endpoint) 來建立託管端點。

   ```
   endpoint_config_name = 'my-endpoint-config'
   sm.create_endpoint_config(
       EndpointConfigName=endpoint_config_name,
       ProductionVariants=[
           {
               'VariantName': 'MyVariant',
               'ModelName': model_name,
               'InitialInstanceCount': 1,
               'InstanceType': 'ml.t2.medium'
           },
       ],
   )
   
   endpoint_name = 'my-endpoint'
   sm.create_endpoint(
       EndpointName=endpoint_name,
       EndpointConfigName=endpoint_config_name,
   )
   
   sm.describe_endpoint(EndpointName=endpoint_name)
   ```

## 允許 SageMaker AI 向私有 Docker 登錄檔進行驗證
<a name="inference-private-docker-authenticate"></a>

若要從需要身分驗證的私有 Docker 登錄檔提取推論映像，請建立提供登入資料的 AWS Lambda 函數，並在您呼叫 [create\$1model](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/sagemaker.html#SageMaker.Client.create_model) 時提供 Lambda 函數的 Amazon Resource Name (ARN)。SageMaker AI 執行 `create_model` 時，會呼叫您指定的 Lambda 函式，以取得憑證向您的 Docker 登錄檔驗證。

## 建立 Lambda 函式
<a name="inference-private-docker-lambda"></a>

建立 AWS Lambda 函數，以傳回具有下列格式的回應：

```
def handler(event, context):
   response = {
      "Credentials": {"Username": "username", "Password": "password"}
   }
   return response
```

視您為私有 Docker 登錄檔設定驗證的方式而定，Lambda 函式傳回的憑證可能表示下列其中一項：
+ 如果您將私有 Docker 登錄檔設置為使用基本驗證，請提供登入的憑據以向登錄檔進行驗證。
+ 如果您將私有 Docker 登錄檔設置為使用承載權杖驗證，則登入憑證將傳送至您的授權伺服器，該伺服器傳回可用於驗證私有 Docker 登錄檔的承載權杖。

## 將您的執行角色許可給予 Lambda
<a name="inference-private-docker-perms"></a>

您用來呼叫 的執行角色`create_model`必須具有呼叫 AWS Lambda 函數的許可。將以下內容新增到執行角色的許可政策中。

```
{
    "Effect": "Allow",
    "Action": [
        "lambda:InvokeFunction"
    ],
    "Resource": [
        "arn:aws:lambda:*:*:function:*myLambdaFunction*"
    ]
}
```

當 *myLambdaFunction *是您的 Lambda 函式的名稱。如需編輯角色許可政策的資訊，請參閱 *AWS Identity and Access Management 使用者指南*中的[修改角色許可政策 (主控台)](https://docs.aws.amazon.com/IAM/latest/UserGuide/roles-managingrole-editing-console.html#roles-modify_permissions-policy)。

**注意**  
連接 `AmazonSageMakerFullAccess` 受管政策的執行角色有許可能夠呼叫任何在名稱中含有 **SageMaker** 的 Lambda 函式。

## 為 Lambda 建立介面 VPC 端點
<a name="inference-private-docker-vpc-interface"></a>

建立介面端點，以便 Amazon VPC 可以與您的 AWS Lambda 函式通訊，而不必透過網際網路傳送流量。如需此使用方法的資訊，請參閱 *AWS Lambda 開發人員指南*中的[設定 Lambda 的介面 VPC 端點](https://docs.aws.amazon.com/lambda/latest/dg/configuration-vpc-endpoints.html)。

SageMaker AI 託管會透過您的 VPC 傳送請求至 `lambda.region.amazonaws.com`，以呼叫您的 Lambda 函式。如果您在建立介面端點時選擇私有 DNS 名稱，Amazon Route 53 會將呼叫路由至 Lambda 介面端點。如果您使用不同的 DNS 提供者，請務必將 `lambda.region.amazonaws.com` 對應到您的 Lambda 介面端點。