Skip to content

Commit aff804a

Browse files
committed
Add colocated python Dockerfile and build upload script
1 parent c3d6fdc commit aff804a

2 files changed

Lines changed: 166 additions & 0 deletions

File tree

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Template for building the MaxText Colocated Python image.
2+
ARG BASE_IMAGE
3+
FROM ${BASE_IMAGE}
4+
5+
RUN apt-get update && apt-get install -y git
6+
7+
WORKDIR /app
8+
9+
# Copy the current directory (MaxText repo) into the image
10+
COPY . /app/maxtext/
11+
12+
# Install MaxText dependencies
13+
# We assume requirements.txt is in maxtext/src/dependencies/requirements/generated_requirements/tpu-requirements.txt based on repo structure
14+
RUN uv pip install --upgrade pip setuptools wheel
15+
RUN uv pip install -r maxtext/src/dependencies/requirements/generated_requirements/tpu-requirements.txt -c /opt/venv/server_constraints.txt
16+
17+
# Ensure MaxText src is in PYTHONPATH
18+
ENV PYTHONPATH=/app/maxtext/src:$PYTHONPATH
19+
20+
WORKDIR /app/maxtext
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
#!/bin/bash
2+
3+
# Copyright 2023–2025 Google LLC
4+
#
5+
# Licensed under the Apache License, Version 2.0 (the "License");
6+
# you may not use this file except in compliance with the License.
7+
# You may obtain a copy of the License at
8+
#
9+
# https://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing, software
12+
# distributed under the License is distributed on an "AS IS" BASIS,
13+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
# See the License for the specific language governing permissions and
15+
# limitations under the License.
16+
17+
# Script to build and upload the MaxText colocated Python Docker image.
18+
# This script should be run from the root directory of the MaxText repository.
19+
#
20+
# Arguments can be provided as KEY=VALUE pairs, e.g.:
21+
# bash build_upload_colocated_python_image.sh PROJECT=my-gcp LOCAL_IMAGE_NAME=my-colocated
22+
#
23+
# Supported KEYs:
24+
# PROJECT: GCP project for gcr.io.
25+
# LOCAL_IMAGE_NAME: Overrides the local tag used during docker build. Defaults to 'maxtext-colocated-python'.
26+
# IMAGE_LOCATION: The full URL for the final image in the registry.
27+
# Defaults to gcr.io/${PROJECT}/${USER}_${LOCAL_IMAGE_NAME}:latest.
28+
# Setting this variable fully overrides PROJECT and LOCAL_IMAGE_NAME.
29+
30+
# Example 1: Provide PROJECT
31+
# bash src/dependencies/scripts/build_upload_colocated_python_image.sh PROJECT=my-tpu-dev
32+
33+
# Example 2: Specify a custom local image name
34+
# bash src/dependencies/scripts/build_upload_colocated_python_image.sh PROJECT=my-tpu-dev LOCAL_IMAGE_NAME=maxtext-cp
35+
36+
# Example 3: Provide a complete IMAGE_LOCATION
37+
# bash src/dependencies/scripts/build_upload_colocated_python_image.sh IMAGE_LOCATION=us-docker.pkg.dev/my-artifact-repo/images/colocated-python:stable
38+
39+
# Example 4: Specify a custom JAX version
40+
# bash src/dependencies/scripts/build_upload_colocated_python_image.sh PROJECT=my-tpu-dev JAX_VERSION=0.10.0
41+
42+
for ARGUMENT in "$@"; do
43+
if [[ "$ARGUMENT" == *"="* ]]; then
44+
IFS='=' read -r RAW_KEY VALUE <<< "$ARGUMENT"
45+
KEY=$(echo "$RAW_KEY" | tr '[:lower:]' '[:upper:]')
46+
export "$KEY"="$VALUE"
47+
echo " Parsed: $KEY=$VALUE"
48+
else
49+
echo "Warning: Ignoring argument '$ARGUMENT'. Arguments should be in KEY=VALUE format (e.g., PROJECT=my-proj)."
50+
fi
51+
done
52+
53+
# Set default values using parameter expansion.
54+
: "${LOCAL_IMAGE_NAME:=maxtext-colocated-python}"
55+
if [[ -z "${IMAGE_LOCATION}" ]]; then
56+
# IMAGE_LOCATION was not provided, so PROJECT is required for the default.
57+
if [[ -z "${PROJECT}" ]]; then
58+
echo "Error: Either PROJECT or IMAGE_LOCATION must be provided." >&2
59+
echo " - To use a default location in GCR, set PROJECT=your-gcp-project-id." >&2
60+
echo " - To specify a full custom location, set IMAGE_LOCATION=your/registry/path/image:tag." >&2
61+
exit 1
62+
fi
63+
IMAGE_LOCATION="gcr.io/${PROJECT}/${USER}_${LOCAL_IMAGE_NAME}:latest"
64+
fi
65+
66+
DOCKERFILE_TEMPLATE="src/dependencies/dockerfiles/colocated_python.Dockerfile.template"
67+
CONTEXT="maxtext/"
68+
69+
echo "$(date): Building and pushing MaxText Colocated Python image..."
70+
echo " PROJECT: ${PROJECT}"
71+
echo " LOCAL_IMAGE_NAME: ${LOCAL_IMAGE_NAME}"
72+
echo " IMAGE_LOCATION: ${IMAGE_LOCATION}"
73+
echo " Dockerfile Template: ${DOCKERFILE_TEMPLATE}"
74+
echo " Build Context: ${CONTEXT}"
75+
76+
# --- Step 1: Handle JAX Version ---
77+
REQ_FILE="src/dependencies/requirements/generated_requirements/tpu-requirements.txt"
78+
if [[ -z "${JAX_VERSION}" ]]; then
79+
echo "$(date): Extracting JAX version from requirements..."
80+
if [[ ! -f "${REQ_FILE}" ]]; then
81+
echo "Error: Requirements file not found: ${REQ_FILE}" >&2
82+
exit 1
83+
fi
84+
# Extracts version like "0.10.0" from lines like "jax>=0.10.0" or "jax==0.10.0"
85+
JAX_VERSION=$(grep -E "^jax(>=|==)" "${REQ_FILE}" | head -1 | sed -E 's/^jax(>=|==)([0-9.]+).*/\2/')
86+
if [[ -z "${JAX_VERSION}" ]]; then
87+
echo "Error: Could not extract jax version from ${REQ_FILE}. Ensure it's in the format 'jax>=X.Y.Z' or 'jax==X.Y.Z'." >&2
88+
exit 1
89+
fi
90+
echo " Detected required JAX version: ${JAX_VERSION}"
91+
else
92+
TEMP_REQ_FILE=$(mktemp)
93+
echo "$(date): Generating temporary requirements file: ${TEMP_REQ_FILE} for custom JAX version ${JAX_VERSION}"
94+
trap 'rm -f "${TEMP_REQ_FILE}"' EXIT
95+
sed "s/^jax\(>=\|==\).*$/jax==${JAX_VERSION}/" "${REQ_FILE}" > "${TEMP_REQ_FILE}"
96+
REQ_FILE="${TEMP_REQ_FILE}"
97+
fi
98+
99+
# --- Step 2: Find the Latest Compatible Base Image Tag ---
100+
BASE_IMAGE_REPO="us-docker.pkg.dev/cloud-tpu-v2-images/pathways-colocated-python/sidecar"
101+
TARGET_JAX_TAG_PART="jax_${JAX_VERSION}"
102+
echo "$(date): Searching for base image tag in '${BASE_IMAGE_REPO}' containing '${TARGET_JAX_TAG_PART}'..."
103+
104+
# Authenticate Docker for the base image registry (us-docker.pkg.dev)
105+
BASE_REGISTRY=$(echo "${BASE_IMAGE_REPO}" | cut -d/ -f1)
106+
echo "$(date): Configuring Docker for base image registry: ${BASE_REGISTRY}"
107+
gcloud auth configure-docker --quiet "${BASE_REGISTRY}"
108+
109+
# List tags, filter by JAX version, sort by date (desc), and take the latest.
110+
BASE_IMAGE_TAG=$(gcloud artifacts docker images list "${BASE_IMAGE_REPO}" --include-tags --format=json | \
111+
jq -r '.[] | .tags[]?' | \
112+
grep "${TARGET_JAX_TAG_PART}" | \
113+
sort -r | \
114+
head -1)
115+
116+
if [[ -z "${BASE_IMAGE_TAG}" ]]; then
117+
echo "Error: Could not find a suitable base image tag in ${BASE_IMAGE_REPO} for JAX version '${JAX_VERSION}'." >&2
118+
echo " Searched for tags containing '${TARGET_JAX_TAG_PART}'." >&2
119+
echo " Available matching tags found:" >&2
120+
gcloud artifacts docker images list "${BASE_IMAGE_REPO}" --include-tags --format=json | \
121+
jq -r '.[] | .tags[]?' | sort -r >&2
122+
exit 1
123+
fi
124+
FULL_BASE_IMAGE="${BASE_IMAGE_REPO}:${BASE_IMAGE_TAG}"
125+
echo " Found latest compatible base image: ${FULL_BASE_IMAGE}"
126+
127+
# --- Step 3: Build the Docker image using --build-arg ---
128+
echo "$(date): Running docker build with local tag '${LOCAL_IMAGE_NAME}' using ${TMP_DOCKERFILE}..."
129+
docker build --no-cache \
130+
-f ${DOCKERFILE_TEMPLATE} \
131+
--build-arg BASE_IMAGE="${FULL_BASE_IMAGE}" \
132+
-t "${LOCAL_IMAGE_NAME}" \
133+
.
134+
135+
# --- Step 4: Tag and Push the image ---
136+
echo "$(date): Tagging '${LOCAL_IMAGE_NAME}' as '${IMAGE_LOCATION}'..."
137+
docker tag "${LOCAL_IMAGE_NAME}" "${IMAGE_LOCATION}"
138+
139+
echo "$(date): Pushing '${IMAGE_LOCATION}'..."
140+
docker push "${IMAGE_LOCATION}"
141+
142+
# --- Step 5: Cleanup ---
143+
echo "$(date): Cleaning up local tag '${LOCAL_IMAGE_NAME}'..."
144+
docker image rm "${LOCAL_IMAGE_NAME}"
145+
146+
echo "$(date): Build and push complete for ${IMAGE_LOCATION}"

0 commit comments

Comments
 (0)