AI EngineeringZero to ProductionHome·About·What’s new·Contact
AWS AI Automation · Chapter W8

SageMaker deploy

SageMaker is for when you need to own the model. Deploy an open model from JumpStart to an endpoint, invoke it from boto3 — and learn the delete-the-endpoint discipline.

⏱️ ~2 hours🧪 2 labs🎯 Advanced
⚙️ To run this for realThe code here is complete and correct as written. To actually execute it you'll need:
  • AWS credentials with SageMaker access + pip install sagemaker
  • AWS credentials (aws configure) + pip install boto3
Reading and learning works without any of this — run when you're ready. IDs/ARNs in examples are placeholders; swap in your own.

Learning objectives

  • Explain where SageMaker fits vs. Bedrock (own/host models vs. managed FMs).
  • Deploy an open model from JumpStart to a real-time endpoint.
  • Invoke the endpoint from boto3 and read predictions.
  • Avoid the classic cost trap: delete endpoints you are not using.
▶ Runnable companionEvery code block in this lesson is also a standalone file under code/aws8-sagemaker-deploy/ in the course, with a README on how to run it. Read here, run there — no need to copy-paste.

SageMaker vs. Bedrock intermediate

Here's a mistake that shows up in cloud bills every month. A team needs to run a language model. They've used Bedrock before — call an API, pay per token, done. So they reach for SageMaker the same way, spin up an endpoint to host an open model "just to try it," get pulled into a meeting, and forget about it. Bedrock trained them that "not calling the model" means "not paying." SageMaker does not work that way, and the difference is the single most important thing on this whole page. The two services look like they do the same job — "give me a model" — but they bill on completely opposite principles, and confusing them is how you wake up to a four-figure surprise.

The cleanest way to hold the distinction is a rental analogy. Bedrock is a taxi: you get in, you pay for the ride, you get out, the meter stops. AWS owns the car, keeps it running, and you never think about it between trips — that's a managed foundation model, called by API, priced per token, scaling to zero when idle. SageMaker is leasing a car: you sign for the vehicle and you pay for it whether you drive it or leave it in the garage. In exchange you get the keys — you can put any model on it (an open-weight LLM off Hugging Face, a model you fine-tuned yourself, or a plain old XGBoost classifier that isn't an LLM at all), tune it, and control exactly how it runs. That's the trade in one line: Bedrock trades control for zero-maintenance; SageMaker trades maintenance for control.

So the real question isn't "which is better" — it's "do I need to own the model, or just talk to one?" If a hosted Claude or Titan answers your need, calling it through Bedrock is almost always the right default: less code, no servers, no idle cost. You cross over to SageMaker only when ownership is the actual requirement — the model isn't in Bedrock's catalog, you fine-tuned custom weights, compliance demands the model live inside your own account, or it's a classic ML model that was never a foundation model to begin with. The word to listen for is "host." The moment a task says "host the weights yourself," you've left Bedrock's territory and taken on the responsibility (and the hourly bill) that comes with the keys. The table below is that decision boiled down to a lookup.

same job — "give me a model" — opposite billing principles Bedrock — a taxi managed FM, called by API priced PER TOKEN idle → scales to ZERO, $0 no servers · less control SageMaker endpoint — a lease a server you rent; host ANY model billed PER HOUR it exists idle → STILL billing you own & host · full control The trade in one picture. Bedrock bills per token and costs nothing while idle; a SageMaker endpoint bills per hour for as long as it exists — awake or idle — which is exactly why "host the weights yourself" is the phrase that moves you from the left box to the right.
🗺️ How to read this diagram

Compare the two columns side by side — the left blue box is Bedrock, the right amber box is a SageMaker endpoint; each inner strip is how that option bills.

  • The left (blue) column is Bedrock, the "taxi": you call a managed model by API. Its two green strips are the good news — you pay per token, and when idle it scales to zero, so an unused model costs nothing.
  • The right (amber) column is a SageMaker endpoint, the "lease": you rent a server and can host any model. Its two red strips are the catch — it bills per hour it exists, and an idle endpoint is still billing.
  • The payoff is the contrast between the green strips and the red strips: same outcome ("a model answers you"), but one only charges for use while the other charges for existence. That red "idle → still billing" strip is the four-figure-surprise this page keeps warning about.

In short: Bedrock trades control for zero idle cost; SageMaker trades an hourly bill for the keys. Reach right only when you must own the model.

NeedUse
Call Claude / an FM by APIBedrock
Host an open-weight model yourselfSageMaker endpoint
Train / fine-tune a custom modelSageMaker training + pipelines
Serve a classic ML model (xgboost…)SageMaker endpoint

Deploy from JumpStart intermediate

Think about what "host your own model" actually involves if you did it by hand. You'd rent a GPU server, install the right CUDA drivers, download tens of gigabytes of model weights, load them into GPU memory, stand up a web server that accepts requests and streams tokens back, wire up health checks, and configure networking so nothing leaks to the public internet. That's a week of fiddly, error-prone work before you've answered a single prompt — and it's exactly the work that makes people give up on self-hosting and just call an API. JumpStart exists to make that week disappear.

JumpStart is best understood as an app store for models. Instead of assembling the plumbing yourself, you pick a model off a shelf by its catalog name, and SageMaker already knows the recipe: which serving container it needs, how to load its weights, what instance size it wants, how to expose it. You name the model; AWS builds the rest. What you get at the end is an endpoint — a private, always-on web address inside your AWS account that takes a request and returns the model's answer. Mentally, an endpoint is "your model, running on a server, reachable at a URL only your account can hit."

There's one distinction in the code below that is worth slowing down for, because getting it wrong is the whole cost lesson: describing a model and deploying it are two different acts, and only one of them costs money. Constructing the JumpStartModel(...) object is free — it's just you writing down "I want this model." Nothing has been rented; no weights have moved. The .deploy(...) call is the line that reaches into AWS, provisions a physical GPU instance, pulls the weights onto it, and starts the server. That call takes several minutes and, the instant the server reports healthy, the meter starts ticking by the hour — and it will keep ticking until you explicitly tear the endpoint down. So when you read the four lines below, read them as "three lines of describing, one line of spending." The instance type you pass to .deploy matters too: an LLM needs a GPU (that's why the example uses a ml.g5-class machine, not a cheap CPU box), and bigger models need bigger, pricier instances. Pick the smallest instance that fits the model, because you are paying for every hour it exists, not every prompt it answers.

three lines of describing (free) → one line of spending (the meter starts) pick a model_id catalog name — FREE JumpStartModel(…) just describes — FREE .deploy(ml.g5…) rents a GPU — SPENDS cost boundary live endpoint bills PER HOUR until deleted nothing rented yet no weights moved, $0 so far Only .deploy(...) spends money. Naming the model and building the JumpStartModel object are free descriptions; the .deploy call is the cost boundary — it rents a GPU instance and leaves a live endpoint that bills per hour until you delete it.
🗺️ How to read this diagram

Read the top row left to right — it is the order the four code lines run — then follow the arrows down to what each half leaves behind.

  • The two white boxes on the left (pick a model_id, build JumpStartModel(…)) are pure describing steps. The dashed arrow drops to the green box: nothing has been rented, so you are still at $0.
  • The "cost boundary" label over the middle arrow marks the moment everything changes — the next call is the one that spends.
  • The amber .deploy(...) box is that spending line: its solid arrow drops to the red box — a live endpoint that bills per hour until deleted. That red terminus is the payoff to remember: after .deploy succeeds, the meter is running whether or not you ever send a prompt.

In short: three describing lines cost nothing; the single .deploy line starts the hourly clock — which is why deleting the endpoint later is not optional.

Example code for learning — review, test, and adapt it before running against real or production systems. Commands can create, change, or delete resources. See the Terms & Disclaimer.
Lab W8.1
deploy.pyfrom sagemaker.jumpstart.model import JumpStartModel

# Deploy an open text-generation model to a real-time endpoint.
model = JumpStartModel(model_id="huggingface-llm-mistral-7b-instruct")
predictor = model.deploy(
    instance_type="ml.g5.2xlarge",   # GPU instance — billed per hour while it runs
    initial_instance_count=1,
)
print("endpoint:", predictor.endpoint_name)
▶ How this works

SageMaker is the AWS service for hosting your own machine-learning model on servers you rent by the hour. JumpStart is a built-in catalog of ready-made models — you pick one by name and SageMaker downloads it, puts it on a server, and gives you a private web address (an endpoint) to send requests to. This block does the whole deploy in four lines.

  1. from sagemaker.jumpstart.model import JumpStartModel pulls in the helper class that knows how to deploy a catalog model. The sagemaker library talks to your AWS account for you.
  2. JumpStartModel(model_id="huggingface-llm-mistral-7b-instruct") picks which model to deploy — here the open Mistral 7B chat model. The model_id is just its catalog name; nothing runs or costs money yet, you've only described what you want.
  3. model.deploy(...) is the step that actually rents a server and loads the model onto it. instance_type="ml.g5.2xlarge" asks for a GPU machine (needed to run an LLM fast), and initial_instance_count=1 says start with one of them. This call takes several minutes and the meter starts running the moment the server is up.
  4. It hands back a predictor object — your remote control for the live endpoint. predictor.endpoint_name is the unique name AWS assigned it, which you print so you can find it again later.

What the output means: After a few minutes you'll see something like endpoint: hf-llm-mistral-7b-.... That name proves a live GPU server is now running your model and waiting for requests.

Try this: Notice there is no delete step here — that's the trap. A running ml.g5.2xlarge bills every hour whether you use it or not, so the moment you're done you must call predictor.delete_endpoint() to stop the charge.

The endpoint bills per hour, awake or idleUnlike Bedrock (pay-per-token, scales to zero), a real-time endpoint charges for the instance every hour it exists. Delete it when done: predictor.delete_endpoint(). An idle ml.g5.2xlarge left overnight is a real, avoidable bill.

Invoke from boto3 advanced

A live endpoint is useless until something talks to it, and the first time people try, they reach for the same sagemaker library they used to deploy — and hit friction. That library is heavy: it's built for managing models (creating them, deploying them, tearing them down), and it's not what your application code should carry just to send a prompt. The realization that unlocks this section is that deploying and invoking are separate jobs done by separate tools. Deploying is an admin task you do once. Invoking is what your app does a thousand times a second in production — and for that you want the lightweight, general-purpose AWS client, boto3.

Here's a useful mental split: there are two "SageMakers" as far as the API is concerned. There's sagemaker (the control plane) — the office that sets up and manages endpoints — and there's sagemaker-runtime (the data plane) — the counter you actually walk up to and hand your request. Invoking a model is a data-plane operation, so you open a boto3.client("sagemaker-runtime"). This is the same pattern you'll see across AWS: the tooling that builds a resource is deliberately kept separate from the high-traffic path that uses it, so your request path stays fast and dependency-light.

Two small details in the code below trip up almost everyone, so name them now. First, region is not optional. An endpoint lives in exactly one AWS region, and if your client points at a different region, AWS will honestly tell you the endpoint doesn't exist — because from that region's point of view, it doesn't. The region_name on the client must match where you deployed. Second, the response comes back as raw bytes, not a tidy Python object. SageMaker hands you a stream, and you have to .read() those bytes and then json.loads(...) them yourself — forgetting this is why beginners print something that looks like <botocore.response.StreamingBody> instead of an answer. Also note the payload shape: these text-generation containers expect your prompt under an "inputs" key in JSON. That key is a convention of the serving container, not a law of SageMaker — a different model might expect a different field — but for the JumpStart LLMs here, "inputs" is what they read.

two "SageMakers": one sets endpoints up, one you send requests to sagemaker · control plane deploy / manage / delete (once) sagemaker-runtime · data plane invoke — the high-traffic path one invoke_endpoint call, left → right: JSON payload {"inputs": "…"} sagemaker-runtime endpoint · right region byte stream .read() + json.loads → Python object raw bytes Invoke through the data plane, and parse the bytes yourself. Deploying uses the sagemaker control plane; invoking uses the lightweight sagemaker-runtime client — which returns a byte stream you must .read() and json.loads(...) into a Python object, with the region matching where you deployed.
🗺️ How to read this diagram

Read the top pair of boxes first (the two "SageMakers"), then follow the bottom row left to right — that is one invoke_endpoint call from prompt to parsed answer.

  • The top two boxes split the API in half: the blue sagemaker control plane is the admin tool that deploys and deletes endpoints (used once); the amber sagemaker-runtime data plane is the counter you hand requests to a thousand times a second. Invoking lives on the right.
  • On the bottom row, your JSON payload with the prompt under the "inputs" key goes into the green runtime/endpoint box — whose region must match where you deployed, or AWS reports "no such endpoint."
  • The reply arrow is labelled raw bytes: the last box is the step people forget — you must .read() the stream and json.loads(...) it, or you print a StreamingBody instead of the answer.

In short: deploy on the control plane, invoke on the lightweight runtime, match the region, and parse the byte stream into a Python object yourself.

Lab W8.2
invoke_endpoint.pyimport boto3, json
smr = boto3.client("sagemaker-runtime", region_name="us-east-1")

resp = smr.invoke_endpoint(
    EndpointName="my-endpoint",
    ContentType="application/json",
    Body=json.dumps({"inputs": "Explain blue-green deployment in one sentence."}),
)
print(json.loads(resp["Body"].read()))
▶ How this works

Once an endpoint is live, this is how you actually ask it a question from plain Python. You don't need the SageMaker deploy library for this — just boto3, AWS's general-purpose client. You send a request to the endpoint by name and read back whatever the model generated.

  1. boto3.client("sagemaker-runtime", region_name="us-east-1") opens a connection to the part of AWS that runs endpoints (called sagemaker-runtime). region_name must match the region where you deployed. We name it smr.
  2. smr.invoke_endpoint(...) sends one request. EndpointName="my-endpoint" is the name from the deploy step (swap in your real one). ContentType tells the server the request is JSON.
  3. Body=json.dumps({"inputs": "Explain blue-green deployment..."}) is the actual prompt. json.dumps turns the Python dictionary into a JSON text string, because the endpoint expects JSON — the "inputs" key is the standard field these models read the prompt from.
  4. The reply comes back as raw bytes inside resp["Body"]. resp["Body"].read() reads those bytes and json.loads(...) parses them back into a Python object so you can use the answer.

What the output means: You'll see the model's generated text, typically as a list like [{'generated_text': 'Blue-green deployment runs two identical environments...'}]. That is the model's one-sentence answer to your prompt.

Try this: Change the sentence inside "inputs" to any question and re-run — same endpoint, new answer, no re-deploy. Each call is cheap; it's the idle server between calls that costs money.

Batch transform: no endpoint needed expert

Picture the wrong version of this first, because it's a real and expensive mistake. You have a million product reviews sitting in S3 and you need a sentiment score for each one — a one-time job, no user waiting on the other end. The instinct, fresh off the last two sections, is to deploy an endpoint and loop your million rows through invoke_endpoint. It works. But that endpoint is a server that sits awake the entire time, and — critically — it doesn't turn itself off when the last row is scored. It keeps billing by the hour until a human remembers to delete it. You've used a real-time tool for an offline job, and the mismatch is money.

The fix is to stop thinking "server" and start thinking "job." A real-time endpoint is a shop that stays open waiting for walk-in customers; batch transform is a work crew you hire for one project. The crew shows up, does the work, and goes home — you pay for the hours worked, not for a storefront sitting empty overnight. Concretely, batch transform takes a whole dataset from S3, spins up one or more instances, runs every record through the model, writes the results back to S3, and then automatically tears the instances down when the job finishes. There is no endpoint left running afterward, which means there is nothing to forget to delete and no idle-hour bill to accrue. That self-teardown is the entire point.

The way to decide between the two is to ask "is someone waiting for this answer right now?" If yes — a chat UI, an API your app calls live — you need the always-on endpoint and its low per-request latency, and you accept the discipline of deleting it. If no — you're scoring a file, backfilling a table, running a nightly report — batch transform is almost always the cheaper, safer choice precisely because it can't be left running. It also parallelizes naturally: point it at more instances and a huge dataset finishes faster, then everything shuts down. Same model, same weights; the only thing that changed is whether you provisioned a server or a job.

"is someone waiting right now?" — no → use the job, not the server real-time endpoint wrong here — a shop that stays open loop rows → invoke_endpoint last row scored… still awake bills until a humandeletes it batch transform right here — a crew hired for one job read dataset from S3 spin up → score → write S3 tears itself down→ $0 idle A job that ends beats a server that waits. For offline scoring, a real-time endpoint keeps billing after the last row is done until someone deletes it; batch transform reads S3, scores, writes S3, and tears its own instances down — so there is nothing left to forget and no idle bill.
🗺️ How to read this diagram

Compare the two columns top to bottom — both run the same model over your data, but watch what each one does after the work is finished (the bottom strip in each).

  • The left (blue) column is the tempting-but-wrong choice: deploy an endpoint and loop your rows through invoke_endpoint. Its lower strips turn red — once the last row is scored the server is still awake, and it keeps billing until a human deletes it.
  • The right (amber) column is batch transform, the "work crew": read the dataset from S3, spin up instances, score, write results back to S3. Its bottom strip is green — the instances tear themselves down, so idle cost is $0.
  • The payoff is the bottom-row contrast: red "bills until deleted" versus green "tears itself down." Same weights, same scoring — the only difference is whether the compute can be left running by mistake.

In short: if no one is waiting on the answer, pick the job that ends itself over the server you have to remember to switch off.

Exercise W8.1 — Deploy, invoke, delete

Context: The deploy/invoke/delete loop is the whole SageMaker hosting lifecycle in miniature, and the reason it matters is money: the only reliable proof you stopped the charge is seeing it disappear from the bill the next day.

Your task: Deploy a small JumpStart model, invoke it three times from boto3, then delete the endpoint and confirm it is gone — and check Cost Explorer the next day to prove deletion stopped the charge.

Requirements:

  • Deploy one small JumpStart model to a real-time endpoint
  • Invoke it three times against the same live endpoint
  • Delete the endpoint and confirm it no longer appears as in-service
  • Verify in Cost Explorer the following day that charges stopped
  • Needs AWS creds; treat the deletion as the load-bearing step

💡 Hint: Reuse the expert rung's finally-guarded cleanup so the delete happens no matter what, then let the next-day bill be the real receipt.

🪜 Practice ladder beginner → industry

Six graded exercises, easy to real-world. Try each before opening its solution.

Exercise 1 · Choose SageMaker vs BedrockBeginner

Context: The first decision in any hosting task is which service even fits. Bedrock gives you managed foundation models by API that scale to zero; SageMaker is for when you must own the model — an open-weight LLM or a classic ML model — on instances you rent by the hour.

Your task: For each need, say whether you would reach for Bedrock or a SageMaker endpoint: (a) call Claude by API, (b) host an open-weight Mistral yourself, (c) serve a classic xgboost model — justifying each in one line.

Requirements:

  • Map (a) to Bedrock: managed FM by API, pay-per-token, scales to zero
  • Map (b) to a SageMaker endpoint: you host the open-weight model yourself
  • Map (c) to a SageMaker endpoint: classic ML served on rented instances
  • State the trade in one line: more control for more responsibility (and the hourly bill)
  • Runs offline (a reasoning exercise, no API calls)

💡 Hint: Ask one question per case: do I need to own and host the weights, or just call a model? Ownership pushes you to SageMaker.

Show solution

Mapping straight from the lesson's decision table:

(a) Call Claude / an FM by API        -> Bedrock
(b) Host an open-weight model yourself -> SageMaker endpoint
(c) Serve a classic ML model (xgboost) -> SageMaker endpoint

Bedrock gives you managed foundation models by API (pay-per-token, scales to zero), so it fits (a). SageMaker is for when you need to own the model — host an open-weight LLM or serve a classic ML model on instances you rent by the hour — so it fits (b) and (c). The trade is more control for more responsibility, including the instance bill.

Exercise 2 · Deploy a JumpStart model to an endpointIntermediate

Context: JumpStart is the fast path to hosting an open model: choosing the model is free and only describes what you want, while .deploy(...) is the call that actually rents a GPU server and starts the meter. Knowing which line costs money is the core lesson.

Your task: Using the SageMaker Python SDK, deploy huggingface-llm-mistral-7b-instruct from JumpStart to a single-instance real-time endpoint on a GPU instance, and print the endpoint name.

Requirements:

  • Build a JumpStartModel(model_id=...) for the Mistral instruct model
  • Call .deploy(...) with a GPU instance_type (e.g. ml.g5.2xlarge) and initial_instance_count=1
  • Capture the returned predictor
  • Print predictor.endpoint_name so you can find it again
  • Needs AWS creds; the meter starts once the server is up

💡 Hint: Constructing the model object is free — it is .deploy() that spins up billable hardware, so treat that line as the cost boundary.

Show solution
from sagemaker.jumpstart.model import JumpStartModel

# Deploy an open text-generation model to a real-time endpoint.
model = JumpStartModel(model_id="huggingface-llm-mistral-7b-instruct")
predictor = model.deploy(
    instance_type="ml.g5.2xlarge",   # GPU instance - billed per hour while it runs
    initial_instance_count=1,
)
print("endpoint:", predictor.endpoint_name)

Picking the model is free — JumpStartModel(model_id=...) only describes what you want. The .deploy(...) call is what actually rents a GPU server and loads the model, returning a predictor whose endpoint_name you print so you can find it again. The meter starts the moment the server is up. Needs AWS credentials to run (SageMaker access + pip install sagemaker).

Exercise 3 · Invoke the endpoint from boto3Advanced

Context: Calling a live endpoint does not need the heavyweight deploy library — just the general-purpose boto3 runtime client. The gotcha is the payload shape and reading the response back out of a byte stream.

Your task: Write a boto3 script that sends a JSON prompt to an endpoint named my-endpoint in us-east-1 via the sagemaker-runtime client and prints the parsed response.

Requirements:

  • Use the sagemaker-runtime client with a matching region_name
  • Put the prompt under the "inputs" key, JSON-encoded
  • Set ContentType="application/json" on invoke_endpoint
  • Read resp["Body"] and parse it with json.loads
  • Print the parsed object (typically a list with generated_text); needs AWS creds

💡 Hint: The region on the client must match where the endpoint was deployed, and the reply arrives as bytes you must .read() before parsing.

Show solution
import boto3, json
smr = boto3.client("sagemaker-runtime", region_name="us-east-1")

resp = smr.invoke_endpoint(
    EndpointName="my-endpoint",
    ContentType="application/json",
    Body=json.dumps({"inputs": "Explain blue-green deployment in one sentence."}),
)
print(json.loads(resp["Body"].read()))

You don't need the SageMaker deploy library to invoke — just the general-purpose boto3 client for the sagemaker-runtime service, whose region_name must match where you deployed. The prompt goes in the "inputs" field (the standard key these text-generation models read), JSON-encoded with json.dumps. The reply arrives as bytes in resp["Body"]; .read() plus json.loads(...) parses it into a Python object, typically [{'generated_text': '...'}]. Needs AWS credentials to run (aws configure + pip install boto3).

Exercise 4 · Deploy, invoke three times, then deleteExpert

Context: An idle real-time endpoint bills every hour whether or not it is used, so the professional discipline is deploy → invoke → delete — and the delete must happen even if an invocation blows up, or you leave a GPU billing overnight.

Your task: Implement W8.1: deploy a small JumpStart model, invoke it three times from boto3, then delete the endpoint so the per-hour charge stops — cleaning up even if invocation fails.

Requirements:

  • Deploy once and reuse the same live endpoint for all three invocations
  • Loop three prompts through invoke_endpoint, printing each parsed reply
  • Wrap the invocations so cleanup is guaranteed on any error
  • Call predictor.delete_endpoint() in a finally block
  • Needs AWS creds; confirm the endpoint is gone afterward

💡 Hint: Put the delete in finally, not after the loop — that is the one placement that survives an exception mid-invocation.

Show solution
import json, boto3
from sagemaker.jumpstart.model import JumpStartModel

model = JumpStartModel(model_id="huggingface-llm-mistral-7b-instruct")
predictor = model.deploy(instance_type="ml.g5.2xlarge", initial_instance_count=1)
name = predictor.endpoint_name
print("endpoint:", name)

smr = boto3.client("sagemaker-runtime", region_name="us-east-1")
try:
    for q in ["What is CI/CD?", "Define idempotency.", "What is a canary deploy?"]:
        resp = smr.invoke_endpoint(
            EndpointName=name,
            ContentType="application/json",
            Body=json.dumps({"inputs": q}),
        )
        print(q, "->", json.loads(resp["Body"].read()))
finally:
    predictor.delete_endpoint()        # stop the per-hour charge no matter what
    print("deleted:", name)

This is the full deploy -> invoke -> delete discipline. The three invocations reuse the same live endpoint (no re-deploy per call — each call is cheap; it's the idle server between calls that costs money). The finally block guarantees predictor.delete_endpoint() runs even if an invocation raises, which is exactly how you avoid an ml.g5.2xlarge left billing overnight. Needs AWS credentials to run (SageMaker + boto3).

Exercise 5 · Score a dataset with batch transform (no endpoint)Professional

Context: For a one-off offline scoring run, a real-time endpoint is exactly the wrong tool: it bills for every idle hour it exists. Batch transform spins up instances, reads S3, writes S3, and tears itself down — no long-lived endpoint, no idle cost.

Your task: Explain why a real-time endpoint is wrong for a large one-off scoring job, then sketch a batch transform job with the SageMaker SDK that reads from S3, writes to S3, and tears itself down.

Requirements:

  • State the cost argument: endpoints bill per hour awake or idle; batch transform does not linger
  • Build a transformer(...) off the model with an S3 output_path
  • Call transformer.transform(data=...) pointing at an S3 input prefix
  • Call transformer.wait(); instances tear down when the job finishes
  • Needs AWS creds; no endpoint is left running

💡 Hint: The mental model: batch transform is a job that ends, not a server that waits — which is why it dodges the per-hour endpoint trap.

Show solution

A real-time endpoint bills for its instance every hour it exists, awake or idle — wasteful for a one-off offline scoring run. Batch transform spins up instances, processes an S3 input, writes results to S3, and tears down: no long-lived endpoint, no idle cost.

from sagemaker.jumpstart.model import JumpStartModel

model = JumpStartModel(model_id="huggingface-llm-mistral-7b-instruct")

transformer = model.transformer(
    instance_count=1,
    instance_type="ml.g5.2xlarge",
    output_path="s3://my-bucket/scored/",
)
transformer.transform(
    data="s3://my-bucket/inputs/",
    content_type="application/json",
)
transformer.wait()   # instances are torn down when the job finishes

The key operational point from the lesson: for offline scoring of a large dataset, batch transform avoids the classic per-hour endpoint cost trap because nothing stays running after the job completes. Needs AWS credentials to run (SageMaker access + pip install sagemaker).

Exercise 6 · Enforce the no-idle-endpoint policyIndustry scenario

Context: Orgs get surprise bills from endpoints engineers forget to delete. Because SageMaker charges per hour with no scale-to-zero, you cannot trust people to remember delete_endpoint() — you need an automated sweep plus a written policy.

Your task: Design a lightweight guardrail: a boto3 script that lists all InService endpoints and flags ones older than a threshold, plus a one-line policy statement.

Requirements:

  • List endpoints with list_endpoints(StatusEquals="InService")
  • Compute each endpoint's age from its CreationTime vs now (UTC)
  • Flag any endpoint older than a threshold (e.g. 12 hours)
  • Include a commented hard-enforcement path (delete_endpoint) and a one-line policy
  • Note it is meant to run on a schedule (EventBridge → Lambda); needs AWS creds

💡 Hint: Compare timezone-aware datetimes (both UTC) so the age math is correct, and keep auto-delete behind a comment until the policy is agreed.

Show solution
import boto3, datetime as dt

sm = boto3.client("sagemaker", region_name="us-east-1")
MAX_AGE_HOURS = 12
now = dt.datetime.now(dt.timezone.utc)

resp = sm.list_endpoints(StatusEquals="InService")
for ep in resp["Endpoints"]:
    age_h = (now - ep["CreationTime"]).total_seconds() / 3600
    flag = "FLAG" if age_h > MAX_AGE_HOURS else "ok"
    print(f"{ep['EndpointName']:40} {age_h:6.1f}h  {flag}")
    # optional hard enforcement:
    # if age_h > MAX_AGE_HOURS:
    #     sm.delete_endpoint(EndpointName=ep['EndpointName'])

Policy: "Real-time endpoints are ephemeral — every endpoint older than 12 hours is auto-flagged (and, off-hours, auto-deleted); anything long-lived must be an approved, tagged production endpoint or moved to batch transform." This operationalizes the lesson's core warning: unlike Bedrock's pay-per-token scale-to-zero, a SageMaker endpoint charges per hour whether used or not, so the org needs an automated sweep rather than trusting engineers to remember delete_endpoint(). Run it on a schedule (EventBridge -> Lambda). Needs AWS credentials to run (SageMaker access + boto3).

✓ Checkpoint — you can move on when you can…

  • State when to use SageMaker instead of Bedrock.
  • Deploy a JumpStart model to a real-time endpoint.
  • Invoke an endpoint from boto3.
  • Explain the per-hour cost trap and how batch transform avoids it.

Knowledge check check yourself

✓ Knowledge check

When should you choose SageMaker over Bedrock?

Show answer
Use Bedrock to call a managed foundation model by API. Use SageMaker when you need to own the model: host an open-weight LLM yourself, train/fine-tune a custom model, or serve a classic ML model -- more control at the cost of more responsibility (including the instance bill).
✓ Knowledge check

What is the classic cost trap of a SageMaker real-time endpoint, and how does batch transform avoid it?

Show answer
A real-time endpoint bills for its instance every hour it exists, awake or idle, so you must delete_endpoint when done (unlike Bedrock's pay-per-token, scale-to-zero). Batch transform instead spins up instances to score an S3 dataset, writes results to S3, and tears down -- no long-lived endpoint and no idle cost.
© 2026 studybydoing.in · AI Engineering: Zero to Production · All rights reserved. · About · Privacy Policy · Terms · Contact
Educational content, provided as-is and without warranty. Code samples are examples — review, test, and adapt them before using in production. See the Terms of Use & Disclaimer. Use at your own risk.
© studybydoing.in