Preparazione delle immagini contenitore

Prepara, crea ed esegui il push di un'immagine contenitore agente per le distribuzioni in hosting.

Utilizzare le linee guida riportate in questo argomento per preparare le immagini Docker.

Architettura dell'immagine contenitore supportata

Per le immagini, il servizio di intelligenza artificiale generativa supporta l'architettura x86 a 64 bit, nome codice amd64. Quando si creano immagini contenitore, utilizzare linux/amd64 come tipo di piattaforma.

Ad esempio:

docker buildx build --platform linux/amd64 -t myimage:latest

Preparazione codice

Il contenitore Docker deve soddisfare i requisiti riportati di seguito per essere eseguito nell'ambiente di distribuzione hosted.

Host e porto
  • Il contenitore deve ascoltare sull'host 0.0.0.0.
  • Il contenitore deve ascoltare sulla porta 8080.
Tipo contenuto della risposta HTTP

Il contenitore deve esporre un servizio basato su HTTP che implementa gli endpoint di richiesta e risposta in stile REST utilizzando metodi quali GET, PUT, POST, DELETE e PATCH con percorsi definiti dall'utente.

La piattaforma determina se una richiesta prevede una risposta di streaming ispezionando l'intestazione Accept.

  • Se l'intestazione Accept include testo/flusso di eventi, l'endpoint deve restituire una risposta SSE (Server-Sent Events) con testo del tipo di contenuto/flusso di eventi.
  • In caso contrario, l'endpoint deve restituire una risposta JSON standard con applicazione/json del tipo di contenuto.

Questa impostazione supporta un endpoint per le interazioni in streaming e non in streaming in modo coerente e compatibile con le versioni precedenti.

Endpoint disponibilità

Il contenitore Docker deve esporre un endpoint di idoneità per verificare che l'applicazione sia completamente inizializzata e pronta per gestire le richieste.

  • Percorso: /ready
  • Scopo: indica se il container è pronto a ricevere il traffico.
  • Formato risposta: solo codice di stato HTTP
  • Tipo di contenuto: application/json
  • Codice stato operazione riuscita: 200 OK (l'applicazione è pronta)

Se il contenitore non è pronto, l'endpoint deve restituire un codice di stato non 200 per impedire l'instradamento del traffico.

Endpoint durata

Il contenitore Docker deve esporre un endpoint di durata per verificare che l'applicazione sia in esecuzione correttamente e non richieda un riavvio.

  • Percorso: /health
  • Scopo: rileva se il contenitore è attivo e funzionante
  • Formato risposta: solo codice di stato HTTP
  • Tipo di contenuto: application/json
  • Codice stato operazione riuscita: 200 OK (l'applicazione è in buono stato)

Se l'applicazione entra in un deadlock o in uno stato irreversibile, l'endpoint deve restituire un codice di stato non-200 in modo che la piattaforma possa riavviare automaticamente il contenitore.

Architettura immagine

Il servizio supporta amd64.

Nota

Si consiglia di utilizzare le immagini di base fornite da Oracle Container Registry. Le immagini su quel sito superano la scansione delle vulnerabilità.
Variabili di ambiente riservate

Le seguenti variabili d'ambiente sono riservate per l'uso da parte del sistema. Non definirli nel codice contenitore:

PORT
K_SERVICE
K_CONFIGURATION
K_REVISION
OCI_RESOURCE_PRINCIPAL_VERSION
OCI_RESOURCE_PRINCIPAL_PRIVATE_PEM
OCI_RESOURCE_PRINCIPAL_RPST
KUBERNETES_*

Architettura immagine

L'immagine deve essere creata per la piattaforma linux/amd64, come mostrato nell'esempio seguente.

Accesso file

Il file system del contenitore è di sola lettura, ad eccezione della directory /tmp, che è scrivibile. Se l'applicazione deve scrivere i file localmente, scriverli a /tmp.

Analisi delle vulnerabilità

Prima della distribuzione, l'immagine viene analizzata utilizzando OCI Vulnerability Scanning Service. La distribuzione non riesce se vengono rilevate vulnerabilità critiche.

Oracle consiglia di utilizzare le immagini di base di OCI Container Registry:

https://container-registry.oracle.com/, dove le immagini hanno già superato la scansione delle vulnerabilità.

Altre restrizioni

  • I comandi del punto di ingresso personalizzato non sono supportati. Definire il comando di immissione nel Dockerfile utilizzando CMD o ENTRYPOINT.
  • Mapping del volume non supportato. I container devono essere senza conservazione dello stato perché i dati del file locale non vengono conservati durante la ridistribuzione o la sostituzione del nodo.

Codice di esempio

L'esempio seguente mostra un agente semplice sviluppato in LangGraph e sottoposto a wrapping con FastAPI.

from contextlib import asynccontextmanager
import os
import sys
from typing import Any, Dict

from dotenv import load_dotenv
from fastapi import FastAPI
from langchain_core.messages import HumanMessage
from langchain_openai import ChatOpenAI
from langgraph.checkpoint.memory import MemorySaver
from langgraph.prebuilt import create_react_agent

load_dotenv()

OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
OPENAI_MODEL = os.getenv("OPENAI_MODEL", "gpt-4o-mini")
TEMPERATURE = float(os.getenv("OPENAI_TEMPERATURE", "0.7"))
OPENAI_MOCK_MODE_ENV = os.getenv("OPENAI_MOCK_MODE")
if OPENAI_MOCK_MODE_ENV is None:
    OPENAI_MOCK_MODE = not bool(OPENAI_API_KEY)
else:
    OPENAI_MOCK_MODE = OPENAI_MOCK_MODE_ENV.strip().lower() in (
        "1",
        "true",
        "yes",
        "on",
    )

app_graph = None


@asynccontextmanager
async def lifespan(app: FastAPI):
    global app_graph

    if OPENAI_MOCK_MODE and not OPENAI_API_KEY:
        print(
            "OPENAI_API_KEY is not set; running in OPENAI_MOCK_MODE.",
            file=sys.stderr,
        )
        yield
        return

    if not OPENAI_API_KEY:
        raise RuntimeError("OPENAI_API_KEY is not set in environment.")

    model = ChatOpenAI(
        model=OPENAI_MODEL,
        temperature=TEMPERATURE,
        api_key=OPENAI_API_KEY,
        streaming=True,
    )
    app_graph = create_react_agent(
        model=model,
        tools=[],
        checkpointer=MemorySaver(),
    )
    yield


app = FastAPI(lifespan=lifespan)


@app.post("/chat")
async def chat(body: Dict[str, Any]):
    thread_id = body["thread_id"]
    msg = body["message"]

    if OPENAI_MOCK_MODE and not OPENAI_API_KEY:
        return {
            "reply": f"[MOCK] OPENAI_API_KEY missing. Echo: {msg}",
            "thread_id": thread_id,
        }

    response = await app_graph.ainvoke(
        {"messages": [HumanMessage(content=msg)]},
        config={"configurable": {"thread_id": thread_id}},
    )

    if "messages" in response and response["messages"]:
        last_message = response["messages"][-1]
        ai_content = getattr(last_message, "content", str(last_message))
    else:
        ai_content = "I'm not sure how to respond to that."

    return {"reply": ai_content}


@app.get("/health")
async def health():
    return {
        "status": "Healthy",
        "mode": "mock" if (OPENAI_MOCK_MODE and not OPENAI_API_KEY) else "normal",
    }


@app.get("/ready")
async def ready():
    if OPENAI_MOCK_MODE and not OPENAI_API_KEY:
        return {"status": "Ready", "mode": "mock"}
    return {"status": "Ready"}


Struttura progetto

project_directory/
├── agent_example.py # Your main agent code
├── pyproject.toml # Dependencies for your agent
├── Dockerfile # docker file for building image
├── uv.lock # auto generated by uv
└── __init__.py # Makes the directory a Python package

Crea immagine contenitore

Di seguito è riportato un esempio di file Docker in una directory di progetto.

Dockerfile
FROM python:3.11-slim

# Set working directory
WORKDIR /app

# Install uv
COPY --from=ghcr.io/astral-sh/uv:latest /uv /bin/uv

# Copy project files
COPY pyproject.toml uv.lock ./
COPY *.py ./


# Install dependencies using uv
RUN uv sync --frozen

# Expose port
EXPOSE 8080

# Run the application using uv
CMD ["uv", "run", "python", "agent_example.py"]

Creare l'immagine del docker nell'architettura amd64

docker buildx build --platform linux/amd64 -t my_agent:v1 .

Esegui PUSH dell'immagine sul registro

Creare un registro contenitore. Vedere Panoramica del Registro contenitore.

Utilizzare l'interfaccia CLI del docker per eseguire il push delle immagini del docker nel registro contenitore.

Passo 1: Accesso al registro contenitore Codice di esempio:

docker login kix.ocir.io

Passo 2: contrassegnare l'immagine utilizzando l'URL del registro contenitore e lo spazio di nomi. Codice esempio:

docker tag my_agent:v1 ap-osaka-1.ocir.io/{your_tenancy_namespace}/my_agent:v1

Passo 3: immagine push Codice di esempio:

docker push ap-osaka-1.ocir.io/<your_tenancy_namespace>/my_agent:v1

Scansione immagini per vulnerabilità (consigliata e facoltativa)

Non è raro che i pacchetti del sistema operativo inclusi nelle immagini abbiano vulnerabilità. La gestione di queste vulnerabilità consente di rafforzare il livello di sicurezza del sistema e di rispondere rapidamente quando vengono rilevate nuove vulnerabilità.

Per creare una recipe di scansione e una destinazione di scansione, vedere Scansione di immagini per le vulnerabilità.