update the gitea_modal_build.py
This commit is contained in:
@@ -22,40 +22,36 @@ import json
|
|||||||
import os
|
import os
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
import subprocess
|
import subprocess
|
||||||
|
|
||||||
import modal
|
import modal
|
||||||
|
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
# Configuration
|
# Configuration
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
|
|
||||||
|
APP_NAME = "msk-lumina-backend-builder"
|
||||||
|
|
||||||
|
# Default registry/image settings
|
||||||
DEFAULT_REGISTRY = "public.ecr.aws/i9a4e3f6/msk-cv-inference-server"
|
DEFAULT_REGISTRY = "public.ecr.aws/i9a4e3f6/msk-cv-inference-server"
|
||||||
DEFAULT_IMAGE_NAME = "msk-lumina-backend"
|
DEFAULT_IMAGE_NAME = "msk-lumina-backend"
|
||||||
DEFAULT_TAG = "latest"
|
DEFAULT_TAG = "latest"
|
||||||
APP_NAME = "msk-lumina-backend-builder"
|
|
||||||
|
# Modal secret for ECR credentials
|
||||||
|
ECR_SECRET_NAME = "ecr-credentials"
|
||||||
|
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
# Modal Image Definition
|
# Modal Image Definition - Kaniko-based builder (no Docker daemon required)
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
# 1. Start from Kaniko to extract the binary, and map it to a local folder via an App build
|
|
||||||
kaniko_image = (
|
|
||||||
modal.Image.from_registry("gcr.io/kaniko-project/executor:debug")
|
|
||||||
.add_local_dir(os.getcwd(), remote_path="/workspace")
|
|
||||||
)
|
|
||||||
|
|
||||||
# 2. Build your final builder image on a clean Debian system that HAS apt-get
|
# We bake the project source into the image at /workspace so Kaniko can access
|
||||||
|
# the build context and Dockerfile at runtime.
|
||||||
|
# The Dockerfile path is resolved at runtime, not at image build time.
|
||||||
builder_image = (
|
builder_image = (
|
||||||
modal.Image.debian_slim()
|
modal.Image.debian_slim()
|
||||||
.apt_install("curl", "ca-certificates")
|
.apt_install("curl", "ca-certificates")
|
||||||
.pip_install("boto3")
|
.run_commands(
|
||||||
|
"curl -sSL https://github.com/chainguard-forks/kaniko/releases/latest/download/kaniko-linux-amd64 -o /usr/local/bin/kaniko",
|
||||||
# Use standard dockerfile_commands to copy the binary directly from the Kaniko base image layer
|
"chmod +x /usr/local/bin/kaniko",
|
||||||
.dockerfile_commands(
|
|
||||||
[
|
|
||||||
"COPY --from=gcr.io/kaniko-project/executor:debug /kaniko/executor /usr/local/bin/kaniko"
|
|
||||||
]
|
|
||||||
)
|
|
||||||
.add_local_dir(
|
|
||||||
os.getcwd(),
|
|
||||||
remote_path="/workspace",
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -64,11 +60,15 @@ app = modal.App(APP_NAME, image=builder_image)
|
|||||||
# =============================================================================
|
# =============================================================================
|
||||||
# Build and Push Function (Kaniko)
|
# Build and Push Function (Kaniko)
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
|
|
||||||
@app.function(
|
@app.function(
|
||||||
timeout=900,
|
timeout=900, # 15 min for heavy ML deps (torch, transformers, etc.)
|
||||||
cpu=4,
|
cpu=4,
|
||||||
memory=8192,
|
memory=8192, # 8GB RAM - plenty for wheel building
|
||||||
secrets=[modal.Secret.from_name("aws-secrets")],
|
secrets=[
|
||||||
|
modal.Secret.from_name("aws-secrets"), # AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_REGION
|
||||||
|
modal.Secret.from_name(ECR_SECRET_NAME), # ECR_USERNAME, ECR_PASSWORD
|
||||||
|
],
|
||||||
)
|
)
|
||||||
def build_and_push(
|
def build_and_push(
|
||||||
registry: str = DEFAULT_REGISTRY,
|
registry: str = DEFAULT_REGISTRY,
|
||||||
@@ -77,71 +77,73 @@ def build_and_push(
|
|||||||
push: bool = True,
|
push: bool = True,
|
||||||
platform: str = "linux/amd64",
|
platform: str = "linux/amd64",
|
||||||
) -> dict:
|
) -> dict:
|
||||||
import boto3
|
"""
|
||||||
|
Build the backend Docker image with Kaniko and push to registry.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
registry: Container registry hostname (e.g., public.ecr.aws/vkist-project)
|
||||||
|
image_name: Image name (e.g., vkist-backend)
|
||||||
|
tag: Image tag (e.g., v1.2.3, latest, git-sha)
|
||||||
|
push: Whether to push to registry
|
||||||
|
platform: Target platform (linux/amd64, linux/arm64)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Dict with image reference and build status
|
||||||
|
"""
|
||||||
|
# Resolve paths at runtime inside Modal.
|
||||||
|
# The project files are baked into /workspace by the Modal image definition.
|
||||||
|
workspace = Path("/workspace")
|
||||||
|
project_root = workspace if (workspace / "backend").exists() else workspace
|
||||||
|
dockerfile_path = workspace / "deps" / "implementation" / "backend_deploy" / "Dockerfile"
|
||||||
|
|
||||||
|
print(f"--- Runtime Path Verification ---")
|
||||||
|
print(f"Workspace: {workspace} (Exists: {workspace.exists()})")
|
||||||
|
print(f"PROJECT_ROOT: {project_root} (Exists: {project_root.exists()})")
|
||||||
|
print(f"DOCKERFILE_PATH: {dockerfile_path} (Exists: {dockerfile_path.exists()})")
|
||||||
|
print(f"----------------------------------------")
|
||||||
|
|
||||||
full_image = f"{registry}/{image_name}:{tag}"
|
full_image = f"{registry}/{image_name}:{tag}"
|
||||||
print(f"Building {full_image} for {platform}")
|
print(f"Building {full_image} for {platform}")
|
||||||
|
|
||||||
# Explicitly use the inside-container paths
|
|
||||||
workspace_root = Path("/workspace")
|
|
||||||
# Adjust this path if your Dockerfile lives in a specific subdirectory inside your workspace
|
|
||||||
dockerfile_path = workspace_root / "Dockerfile"
|
|
||||||
|
|
||||||
# Prepare docker config for Kaniko if pushing to ECR
|
# Prepare docker config for Kaniko if pushing to ECR
|
||||||
docker_config_dir = None
|
docker_config_dir = None
|
||||||
if push and ("ecr.aws" in registry or "ecr." in registry):
|
if push and ("ecr.aws" in registry or "ecr." in registry):
|
||||||
print(f"Configuring ECR authentication...")
|
print(f"Configuring ECR authentication from Modal secret '{ECR_SECRET_NAME}'...")
|
||||||
region = os.getenv("AWS_REGION", "us-east-1")
|
|
||||||
|
|
||||||
ecr = boto3.client("ecr", region_name=region)
|
ecr_username = os.getenv("ECR_USERNAME")
|
||||||
auth = ecr.get_authorization_token()
|
ecr_password = os.getenv("ECR_PASSWORD")
|
||||||
token = auth["authorizationData"][0]["authorizationToken"]
|
|
||||||
username, password = base64.b64decode(token).decode().split(":")
|
if not ecr_username or not ecr_password:
|
||||||
|
raise RuntimeError(
|
||||||
|
f"ECR_USERNAME and ECR_PASSWORD must be set in Modal secret '{ECR_SECRET_NAME}'"
|
||||||
|
)
|
||||||
|
|
||||||
config = {
|
config = {
|
||||||
"auths": {
|
"auths": {
|
||||||
registry: {
|
registry: {
|
||||||
"username": username,
|
"username": ecr_username,
|
||||||
"password": password,
|
"password": ecr_password,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
docker_config_dir = workspace_root / ".kaniko"
|
docker_config_dir = project_root / ".kaniko"
|
||||||
docker_config_dir.mkdir(exist_ok=True)
|
docker_config_dir.mkdir(parents=True, exist_ok=True)
|
||||||
(docker_config_dir / "config.json").write_text(json.dumps(config))
|
(docker_config_dir / "config.json").write_text(json.dumps(config))
|
||||||
print(f"ECR auth configured for {registry}")
|
print(f"ECR auth configured for {registry}")
|
||||||
|
|
||||||
# # --- INSPECT MODAL WORKSPACE DIRECTORIES HERE --- Success
|
|
||||||
# print("\n=== DEBUG: Inspecting Modal Container Filesystem ===")
|
|
||||||
# try:
|
|
||||||
# # 1. Print the contents of the /workspace root directory
|
|
||||||
# print(f"Contents of {workspace_root}:")
|
|
||||||
# workspace_files = subprocess.run(["ls", "-la", str(workspace_root)], capture_output=True, text=True)
|
|
||||||
# print(workspace_files.stdout)
|
|
||||||
|
|
||||||
# # 2. Verify exactly where the copied Kaniko binary lives
|
|
||||||
# print("Checking for Kaniko binary location:")
|
|
||||||
# # kaniko_check = subprocess.run(["ls", "-la", "/usr/local/bin/kaniko"], capture_output=True, text=True)
|
|
||||||
# # print(kaniko_check.stdout if kaniko_check.returncode == 0 else "❌ Kaniko not found in /usr/local/bin/kaniko\n")
|
|
||||||
|
|
||||||
# # except Exception as e:
|
|
||||||
# # print(f"Failed to run inspection: {e}")
|
|
||||||
# # print("==================================================\n")
|
|
||||||
|
|
||||||
# Build with Kaniko
|
# Build with Kaniko
|
||||||
kaniko_cmd = [
|
kaniko_cmd = [
|
||||||
"/usr/local/bin/kaniko", # Update this line to match where it was copied
|
"/usr/local/bin/kaniko",
|
||||||
"--context", str(workspace_root),
|
"--context", str(project_root),
|
||||||
"--dockerfile", str(dockerfile_path),
|
"--dockerfile", str(dockerfile_path),
|
||||||
"--destination", full_image,
|
"--destination", full_image,
|
||||||
"--custom-platform", platform,
|
"--platform", platform,
|
||||||
"--verbosity", "info",
|
"--verbosity", "info",
|
||||||
]
|
]
|
||||||
|
|
||||||
# Configure the environment variables to include DOCKER_CONFIG if auth exists
|
|
||||||
current_env = os.environ.copy()
|
|
||||||
if docker_config_dir:
|
if docker_config_dir:
|
||||||
current_env["DOCKER_CONFIG"] = str(docker_config_dir)
|
kaniko_cmd.extend(["--docker-config", str(docker_config_dir)])
|
||||||
|
|
||||||
print(f"Running Kaniko: {' '.join(kaniko_cmd)}")
|
print(f"Running Kaniko: {' '.join(kaniko_cmd)}")
|
||||||
result = subprocess.run(kaniko_cmd, capture_output=True, text=True)
|
result = subprocess.run(kaniko_cmd, capture_output=True, text=True)
|
||||||
|
|||||||
Reference in New Issue
Block a user