Harness Academy
Tài liệu gốc 09 23 phútTiếng Việt + English
running_the_code/07_infra.mdXem bản gốc
Ngôn ngữ bài đọc
Bản dịch tiếng Việt

Bản dịch đầy đủ theo đúng cấu trúc tài liệu; code, liên kết và định danh kỹ thuật được giữ nguyên.

Infra - ngăn xếp runtime từ xa (decode run trên Modal, do ZenML/Kitaru phối hợp)

Trạng thái: đã triển khai và xác minh. make run-remote TASK="…" đệ trình một agent headless cho một máy chủ Kitaru tự lưu trữ, nó thực thi trong vùng chứa Modal, tạo ra hộp cát bash Modal của riêng nó và mọi điểm kiểm tra đều có trên máy chủ — bền bỉ, có thể chơi lại, có thể tiếp tục từ bất kỳ máy nào.

💰 Đây là phần duy nhất của khóa học tính phí bằng tiền thật — và hoàn toàn không bắt buộc. Mọi đường đua khác (các bài học, REPL, chạy headless, hộp cát, vòng đua) đều chạy miễn phí và cục bộ. Việc triển khai ngăn xếp này sẽ tiêu tốn một dự án GCP ~$16/tháng khi nó hoạt động (VM + IP tĩnh; chính xác là chi tiết về §5 Chi phí) cộng với tính toán Modal dựa trên mức sử dụng. Bỏ qua nó trừ khi bạn đặc biệt muốn các hoạt động không cần đầu được thực thi hoàn toàn trên đám mây — và khi bạn thực hiện xong, scripts/deploy.sh down xóa mọi thứ nó tạo ra.

decode đã chạy sandboxes của nó trên Modal (SANDBOX_MODE=modal). Ngăn xếp này di chuyển ** headless chính agent** ở đó. scripts/deploy.sh cung cấp tất cả những thứ đó; §1 là duy nhất phần bạn gõ bằng tay.

Bắt đầu nhanh

Thực hiện §1 đầu tiên — chuỗi công cụ, gcloud auth login, modal token set, chuyển đổi vùng chứa Docker và .env. deploy.sh từ chối bắt đầu mà không có họ. Sau đó:

1. Triển khai (~10-15 phút; dừng một lần để yêu cầu bạn phê duyệt quy tắc tường lửa 80/443):

scripts/deploy.sh up

2. Đọc URL - nó được lấy từ IP của máy chủ, do đó nó thay đổi mỗi lần xây dựng lại:

scripts/deploy.sh status         # the `server` row, e.g. https://34.153.164.72.nip.io

URL đó vừa là API mà CLI giao tiếp vừa là trang tổng quan bạn mở trong trình duyệt.

3. Lấy mật khẩu bảng điều khiển — người dùng là admin, mật khẩu đã được tạo trong quá trình up:

pbcopy < ~/.config/decode/kitaru-admin-password    # copies it; do NOT echo or `cat` it

4. Chạy cái gì đó:

make run-remote TASK="run bash 'uname -a && pwd' and tell me what you see"

Muốn có hạt nhân Linux x86-64 và /workspace - điều đó chứng tỏ bash chạy trong hộp cát Modal và không có trên máy tính xách tay của bạn. Lần gửi đầu tiên xây dựng và đẩy hình ảnh luồng (3-5 phút); những cái sau tái sử dụng nó (~ thập niên 90). Xem nó hạ cánh với uv run kitaru executions list.

Xong trong ngày? Xé nó xuống (nó lập hóa đơn khi còn hoạt động):

scripts/deploy.sh down <your-project>

Mọi thứ bên dưới là chi tiết đằng sau bốn bước này.

Hình dạng (và tại sao mỗi phần tồn tại)

MảnhCái gìTại sao điều này và không nhiều hơn
Kitaru/Máy chủ ZenMLmột zenmldocker/kitaru container trên một máy ảo GCE, SQLite trên đĩa khởi động, Caddy ở phía trước cho TLSCốt lõi về độ bền: thực thi, siêu dữ liệu điểm kiểm tra, phát lại, chờ HITL và Nhóm môi trường. Nó phải có thể truy cập được từ Modal, vì vậy nó không thể ở trên máy tính xách tay. Một VM + SQLite đánh bại Cloud Run/GKE/MySQL cho khóa học một người dùng.
Ngăn xếp dàn nhạc phương thứcZenML của modal người điều khiển + modal hương vị hộp cátBộ chứa luồng chạy dưới dạng Hộp cát phương thức; các hộp cát bash riêng của bộ decode được sinh ra từ nó (lồng nhau).
Nhóm GCScửa hàng hiện vật (gs://…)Tải trọng điểm kiểm tra, tạo phẩm, mã được tải lên. Modal không thể đọc repository tạo tác cục bộ - điều khiển từ xa là bắt buộc.
repository đăng ký hiện vậtđăng ký containerHình ảnh luồng được xây dựng cục bộ tại thời điểm gửi và được đẩy vào đây; Modal kéo nó. Cũng bắt buộc-từ xa.
Tài khoản dịch vụ runtimedecode-kitaru@…Đặc quyền tối thiểu: Đối tượng GCS + đẩy/kéo hình ảnh, cộng với một ràng buộc quản trị viên trong phạm vi nhóm (§4). Tài khoản con người của bạn chỉ dành cho bootstrap.

Cố tình không ở đây: ZenML Pro (~$999/tháng so với ~$16/tháng), phía máy chủ kitaru deploy (khách hàng-gửi cần ít khái niệm hơn - decode run là trình kích hoạt), MySQL/Cloud SQL (chỉ dành cho nhiều bản sao).


1. Những việc bạn PHẢI làm bằng tay

deploy.sh không thể thực hiện những điều này — họ cần có trình duyệt, chuyển đổi GUI, quản trị viên tổ chức hoặc khả năng phán đoán của bạn. Làm chúng một lần, theo thứ tự.

1.1 Cài đặt chuỗi công cụ

make install                    # uv sync + git hooks
brew install --cask docker      # or Docker Desktop from docker.com
brew install google-cloud-sdk   # gcloud

uv, dockervà gcloud tất cả đều phải nằm trên ĐƯỜNG của bạn. deploy.sh từ chối bắt đầu khác.

1.2 Xác thực - tương tác, dựa trên trình duyệt, không thể viết kịch bản

gcloud auth login               # a human account with roles/owner on the project
gcloud config set project <your-project>
modal token set                 # writes ~/.modal.toml — the tokens the orchestrator submits with

Cả hai đều mở trình duyệt. gcloud thông tin đăng nhập cũng hết hạn và deploy.sh chạy rồi sẽ chết cùng Reauthentication failed. cannot prompt during non-interactive execution. - chỉ gcloud auth login một lần nữa.

1.3 TẮT kho image container của Docker Desktop

Không tùy chọn. Đây là mặc định kể từ Docker Desktop 4.34 và nó đẩy một bảng kê khai OCI có các lớp giữ các loại phương tiện Docker. Modal giải nén hình ảnh với umoci, xác nhận nghiêm ngặt chống lại thông số OCI và chết trên hybrid:

Terminating task due to error: command umoci raw unpack … had exit status: 1

Cài đặt → Chung → bỏ chọn "Sử dụng vùng chứa để kéo và lưu trữ hình ảnh" → Áp dụng & Khởi động lại.

deploy.sh preflight từ chối chạy khi nó đang bật, vì vậy bạn không thể quên. (Thay thế bằng kịch bản: đặt UseContainerdSnapshotter=false trong ~/Library/Group Containers/group.com.docker/settings-store.json, sau đó docker desktop restart.)

1.4 Điền vào .env

cp .env.example .env            # then fill in the keys

Tối thiểu GEMINI_API_KEY, cộng MODAL_TOKEN_ID / MODAL_TOKEN_SECRET (sao chép chúng ra khỏi ~/.modal.toml) và SANDBOX_GIT_TOKEN nếu bạn muốn agent đẩy công việc của mình trở lại như một chi nhánh. deploy.sh phản chiếu tập tin này vào máy chủ decode-prod Xô môi trường.

1.5 Phê duyệt quy tắc tường lửa khi tập lệnh yêu cầu

deploy.sh up dừng lại và hỏi trước khi mở cổng, vì câu trả lời là quyết định bảo mật chứ không phải một mặc định:

The next rule opens tcp:80 and tcp:443 to 0.0.0.0/0 …
Create the firewall rule? [y/N]

Cần có cổng 80 (Let's Encrypt xác thực thử thách ACME đối với cổng này) và 443 là máy chủ. Nó không thể được thu hẹp vào máy tính xách tay của bạn: bộ chứa luồng Modal cũng phải đến được máy chủ và Modal IP đầu ra không thể ghim được trên các gói tiêu chuẩn. Hậu quả: trang đăng nhập có thể truy cập được từ Internet và máy quét sẽ tìm thấy nó trong vòng vài giờ. TLS dừng việc nghe lén, không thể truy cập được. Hãy đối xử với mọi khóa trong repository bí mật của máy chủ đó dưới dạng có thể xoay được.

1.6 Chỉ khi việc tạo khóa SA không thành công: ngoại lệ chính sách tổ chức

Nếu deploy.sh up chết trên constraints/iam.disableServiceAccountKeyCreation, tổ chức của bạn có Google chính sách bảo mật theo mặc định (bất kỳ tổ chức nào được tạo từ giữa năm 2024 đều có). Điều quan trọng vẫn là quyết định đúng đắn — Modal là điện toán nước ngoài không có danh tính GCP, vì vậy thứ gì đó di động phải tiếp cận nó và SA có phạm vi nhịp chính lưu trữ phía máy chủ token người dùng do chủ sở hữu cung cấp. Miễn trừ chỉ dự án này; nhu cầu roles/orgpolicy.policyAdmin trên tổ chức:

gcloud organizations add-iam-policy-binding <ORG_ID> \
  --member=user:<you> --role=roles/orgpolicy.policyAdmin
gcloud resource-manager org-policies disable-enforce \
  iam.disableServiceAccountKeyCreation --project=<your-project>

Quá trình lan truyền mất một hoặc hai phút — việc thử lại ngay lập tức sẽ không thành công với cùng một lỗi. Đảo ngược bất cứ lúc nào với enable-enforce.


2. Triển khai

scripts/deploy.sh up        # provision everything
scripts/deploy.sh status    # what exists right now
scripts/deploy.sh update    # re-apply the mutable parts
scripts/deploy.sh down      # delete everything it created

Ghi đè bất kỳ PROJECT, REGION, ZONE, DECODE_ENV như env vars; mặc định là của khóa học này (coding-agent-course, europe-west2, europe-west2-a, prod).

up — cung cấp, có thể chạy lại một cách an toàn

Mỗi bước đều kiểm tra trước khi tạo, do đó, việc chạy lại sau khi xảy ra lỗi sẽ tiếp tục thay vì gặp lỗi với "đã tồn tại". Theo thứ tự:

  1. Preflight — gcloud/uv/docker hiện tại, một tài khoản gcloud đang hoạt động, ~/.modal.toml tồn tại, cửa hàng container đang TẮT và kéo python:3.12-slim dành cho linux/amd64 (Modal chỉ là x86-64; không có điều này, bản dựng Apple Silicon sẽ tạo ra hình ảnh arm64 Modal không thể chạy).
  2. API — điện toán, đăng ký tạo tác, lưu trữ.
  3. Tài khoản dịch vụ decode-kitaru — roles/storage.objectAdmin + roles/artifactregistry.writer, và một khóa JSON tại ~/.config/decode/decode-kitaru-key.json.
  4. Nhóm + sổ đăng ký — cộng với trong phạm vi nhóm roles/storage.admin ràng buộc (§4 giải thích tại sao vai trò cấp dự án là không đủ).
  5. Máy chủ — IP tĩnh, mật khẩu quản trị viên được tạo, máy ảo GCE chạy bộ chứa Kitaru, /var/kitaru sửa chữa quyền sở hữu (mọi up, không chỉ lần đầu tiên - đó là cách vòng lặp sự cố máy chủ sẽ hoạt động trở lại), quy tắc tường lửa 80/443 (yêu cầu bạn trước), quy tắc TỪ CHỐI đối với :8080và xóa của bất kỳ tiền TLS nào allow-kitaru quy tắc.
  6. TLS — cài đặt Caddy trên máy ảo, nhận được chứng chỉ Let's Encrypt thực sự cho <ip>.nip.io và proxy ngược tới máy chủ. Chờ đợi https://…/health.
  7. Đăng nhập — đúc tiền decode-runner tài khoản dịch vụ + khóa API trên API REST, sau đó kitaru login.
  8. Ngăn xếp — đăng ký trình kết nối GCP, repository tạo phẩm GCS, sổ đăng ký AR, bộ điều phối phương thức và hộp cát, lắp ráp prod-modal xếp chồng, chạy kitaru init, sau đó kích hoạt nó (thứ tự đó vấn đề - §4).
  9. Bí mật — viết decode-modal bí mật (token phương thức) và gương .env vào decode-prod Xô môi trường.

Các tệp mà nó ghi và không có gì khác ngoài GCP/Modal/Kitaru:

Đường dẫnCái gì
~/.config/decode/decode-kitaru-key.jsonkhóa SA runtime mà ngăn xếp xác thực GCS + AR bằng
~/.config/decode/kitaru-admin-passwordmật khẩu quản trị viên của máy chủ (được tạo một lần)
~/.config/decode/kitaru-api-keycái decode-runner khóa CLI đăng nhập bằng

update — sau khi bạn thay đổi mã, deps, hoặc .env

Áp dụng lại TLS (Caddy đã chết tự phục hồi), thông tin đăng nhập, ngăn xếp và các bí mật. không chạm vào VM hoặc thùng. Tiếp theo run-remote xây dựng lại hình ảnh luồng nếu mã hoặc dep thay đổi.

status - sự thật trên một màn hình

project    coding-agent-course
server     https://34.13.23.119.nip.io  (healthy)
bucket     gs://coding-agent-course-kitaru
registry   europe-west2-docker.pkg.dev/coding-agent-course/kitaru-images
vm         RUNNING
firewall   allow-kitaru-tls (tcp:80,443 → 0.0.0.0/0)
legacy     none (:8080 not exposed)
plaintext  denied (tcp:8080 DENY @ priority 100)
tls        Let's Encrypt, valid
stack      prod-modal

URL di chuyển cùng với IP: nip.io mã hóa địa chỉ trong tên máy chủ, vì vậy mọi máy chủ được xây dựng lại đều có một tên mới (và chứng chỉ mới). status là nơi bạn đọc cái hiện tại.

Xác minh việc triển khai

Bốn tờ séc, rẻ nhất trước. Mỗi lớp chứng minh một lớp khác nhau, vì vậy lớp đầu tiên thất bại sẽ cho bạn biết nơi để tìm.

1. Ngăn xếp tồn tại (~10 giây, miễn phí):

scripts/deploy.sh status

muốn: server … (healthy), tls Let's Encrypt, valid, plaintext denied, stack prod-modalvà không có gì <none>. A (unreachable) máy chủ với một RUNNING vm hầu như luôn luôn là /var/kitaru bẫy quyền sở hữu (§4) - chạy lại up, hiện đang sửa chữa nó.

2. agent chạy từ xa — vùng chứa luồng, hydrat hóa Nhóm môi trường, lệnh gọi mô hình và hộp cát Modal lồng nhau. Không --repo, vì vậy không có gì được vận chuyển:

make run-remote TASK="run bash 'uname -a && pwd' and tell me what you see"

Muốn: một hạt nhân Linux x86-64 và /workspace. Đó chính là bằng chứng bash đã hạ cánh ở Modal hộp cát chứ không phải trên máy tính xách tay của bạn - hạt nhân macOS ở đây có nghĩa là SANDBOX_MODE không bao giờ đến được container.

3. Công việc quay trở lại — Bàn giao lại (ADR-0012 §8). Nhiệm vụ phải cấm đẩy, để khai thác là những gì vận chuyển chi nhánh, không phải mô hình:

make run-remote REPO=https://github.com/you/your-repo.git \
  TASK="add a line to README.md saying hello. commit it. do NOT push and do NOT open a PR."
git ls-remote https://github.com/you/your-repo.git 'refs/heads/decode/*'

Muốn: một decode/<first-8-of-the-exec_id> nhánh mang cam kết của mô hình. Thiếu có nghĩa là dòng chảy container không thể đẩy: xác nhận SANDBOX_GIT_TOKEN đạt đến xô với

make sync-secrets ENV=prod      # re-mirrors .env; prints key NAMES only, never values

và token có thể ghi vào repository đó. Hand-back không thành công nên lệnh chạy vẫn trả về câu trả lời của nó dù thế nào đi nữa - hãy đọc [handback] dòng trong đầu ra của lần chạy.

4. Bản ghi rất bền — máy chủ đã nhìn thấy nó:

uv run kitaru executions list
uv run kitaru executions logs <exec_id>

Hoặc mở bảng điều khiển (scripts/deploy.sh status in URL; người dùng admin, mật khẩu trong ~/.config/decode/kitaru-admin-password) — hoạt động này nằm trên trang Flows.

Một điều đáng xem nữa, vì đó là sự khác biệt giữa một tài khoản gọn gàng và 16 ứng dụng mồ côi:

uv run modal app list      # exactly decode-prod + decode-sandbox-prod, one per environment

down - sự sụp đổ

Xóa VM — kèm theo nó mọi bản ghi thực thi và cả bí mật Kitaru (decode-<env>, decode-modal), nằm trong SQLite trên đĩa khởi động của nó — cộng với IP, quy tắc tường lửa, nhóm và mọi tạo phẩm trong đó, sổ đăng ký và tài khoản dịch vụ có khóa của nó; loại bỏ ba các tập tin cục bộ ở trên. Một máy chủ được xây dựng lại sẽ lấy lại các bí mật từ bạn .env (make sync-secrets), nhưng lịch sử chạy đã biến mất.

Nó yêu cầu bạn nhập tên dự án để xác nhận. Không có terminal (a ! lệnh trong phiên agent, CI) không có gì để nhập vào, vì vậy hãy chuyển nó làm đối số thay thế - cùng một xác nhận, do bạn nhập:

scripts/deploy.sh down coding-agent-course

Nó cũng dừng hai ứng dụng Modal của môi trường này — decode-<env> (ứng dụng của bộ chứa luồng) và decode-sandbox-<env> (hộp cát bash lồng nhau của nó). Việc dừng lại là vĩnh viễn; tiếp theo up tái tạo chúng trong lần gửi đầu tiên. Các ứng dụng từ môi trường khác sẽ được giữ nguyên. Nếu bạn sử dụng chính sách tổ chức §1.6 ngoại lệ, hoàn tác nó với enable-enforce.

Xác minh việc phá bỏ

status cũng hoạt động trên một ngăn xếp trống - đó là mục đích của việc chạy nó sau khi phân tích:

scripts/deploy.sh status

Muốn: mỗi hàng <none>, cộng plaintext n/a (no server) và stack <none>. Sau đó xác nhận không có gì sống sót mà vẫn có thể gửi hóa đơn cho bạn:

gcloud compute instances list --project=coding-agent-course           # no kitaru-server
gcloud compute addresses list --project=coding-agent-course           # no kitaru-server-ip (a reserved
                                                                      #   IP with nothing attached BILLS)
gcloud compute firewall-rules list --project=coding-agent-course      # no allow-kitaru-tls / deny-…
gcloud storage ls --project=coding-agent-course                       # no gs://…-kitaru
gcloud artifacts repositories list --project=coding-agent-course      # no kitaru-images
gcloud iam service-accounts list --project=coding-agent-course        # no decode-kitaru
uv run modal app list                                                 # decode-prod + decode-sandbox-prod
                                                                      #   both `stopped`
ls ~/.config/decode                                                   # empty

URL cũ bây giờ hoàn toàn không kết nối được (không chỉ 502 - điều đó có nghĩa là Caddy vẫn hoạt động). Địa phương của bạn kitaru client vẫn trỏ tới server chết; tiếp theo up đăng nhập lại.


3. Điều hành một agent headless

Cách CLI đến máy chủ

Không có URL nào được truyền qua dòng lệnh - decode run đọc nó từ cấu hình máy khách kitaru login đã viết lại up bước 7. Hai tệp, không nằm dưới ~/.config/kitaru (con đường đó không tồn tại - nhìn có vẻ là ngõ cụt):

Đường dẫnCái gì
~/Library/Application Support/kitaru/config.yamlstore.type: rest + store.url: https://<ip>.nip.io — chuyển từ cửa hàng SQLite cục bộ sang máy chủ của bạn, cộng thêm active_stack_id / active_project_id
~/Library/Application Support/kitaru/credentials.yamltoken API, được gửi dưới dạng vật mang trong mỗi lệnh gọi REST. Bí mật - không bao giờ cat nó vào một terminal mà một nhân viên có thể đọc
.kitaru/config.yaml (repo-local)active_stack_id / active_project_id cho lần thanh toán này; cũng là điểm đánh dấu gốc nguồn (§4)

Chuỗi đặt chúng ở đó: kitaru_bootstrap_api_key.py cấp mật khẩu JWT với quản trị viên mật khẩu → tạo decode-runner tài khoản dịch vụ → ghi khóa của nó vào ~/.config/decode/kitaru-api-key → kitaru login <url> --api-key … trao đổi nó lấy token được lưu trữ.

Vì vậy, việc gửi là: client REST-gọi máy chủ cho prod-modal định nghĩa ngăn xếp → xây dựng luồng hình ảnh cục bộ và đẩy nó vào Artifact Register → tải repository mã lên GCS → Kéo phương thức và chạy nó → các điểm kiểm tra quay trở lại cùng kết nối REST đó. ** Thùng chứa Modal đạt tới máy chủ ở cùng một nơi công cộng nip.io URL mà máy tính xách tay của bạn sử dụng** — đó là lý do tại sao quy tắc tường lửa của §1.5 không thể áp dụng được thu hẹp IP của bạn.

Kết nối bị hỏng? uv run kitaru stack list là công cụ thăm dò rẻ nhất - nó sẽ trả lời từ máy chủ hoặc cho bạn biết auth đã chết. Chạy lại scripts/deploy.sh update, nó sẽ ghi lại và đăng nhập lại.

Gửi

make run-remote TASK="explain what this repo does"
make run-remote TASK="fix the failing test" REPO=https://github.com/you/your-repo.git
make run-remote TASK="…" SANDBOX=none          # bash runs in the flow container itself

đó chỉ là:

DOCKER_BUILDKIT=1 KITARU_STACK=prod-modal DECODE_ENV=prod SANDBOX_MODE=modal \
  uv run --group remote decode run "$TASK" --repo "$REPO"

KITARU_STACK là đường nối mã 0: lệnh gọi luồng của decode .run() không có đối số ngăn xếp, vì vậy giá trị xung quanh quyết định nơi họ thực hiện. Thứ tự giải quyết:

explicit  .run(stack=…)   >   KITARU_STACK env   >   the configured active stack

KITARU_STACK và "ngăn xếp hoạt động" là các nút bấm khác nhau, không phải hai tên cho một. Kitaru là rõ ràng về nó (kitaru/_config/_active_context.py): "KITARU_STACK là một mặc định thực thi và không thiết lập ngăn xếp hoạt động của ZenML." Vì vậy make run-remote đáp xuống prod-modal bởi vì Makefile xuất nó - bất kể điều gì kitaru stack current báo cáo. Ngăn xếp đang hoạt động là những gì trần trụi uv run kitaru … các lệnh và stack hàng của deploy.sh status đọc. Mong đợi thấy họ không đồng ý; chỉ có status hàng đáng để sửa (uv run kitaru stack use prod-modal).

Việc gửi chỉ xây dựng và đẩy hình ảnh luồng (3-5 phút) khi mã, phần phụ hoặc cài đặt Docker đã thay đổi; nếu không nó sẽ sử dụng lại bản dựng đã ghi (Reusing existing build …, ~90s). SANDBOX_MODE lựa chọn agent ở đâu bash vùng đất:

SANDBOX_MODEỞ đâu bash chạy--repo Không gian làm việcTrở lại
modal (mặc định)một hộp cát phương thức lồng nhau, /workspacevângvâng - đẩy decode/<exec-id> với SANDBOX_GIT_TOKEN
nonebên trong thùng chứa dòng chảy, /app/codekhông (ADR-0012 §3)không

Handback chạy bên trong ngăn chứa lưu lượng, không phải trên máy tính xách tay của bạn (ADR-0012 §8, đã sửa đổi). Đó là nơi Không gian làm việc được sao chép, làm việc và quét ngược lại từ hộp cát — máy gửi .decode/sandbox là một người xa lạ với cuộc chạy trốn. Đó cũng là lý do duy nhất mà lực đẩy cần SANDBOX_GIT_TOKEN: một thùng chứa luồng không có thông tin xác thực git môi trường nào cả. Không có token thì chạy vẫn trả lời và nói rõ ràng rằng nó không thể đẩy được. Xác minh nó bằng kiểm tra 3 ở trên.

Ứng dụng phương thức: một ứng dụng cho mỗi môi trường, không bao giờ một ứng dụng cho mỗi lần chạy. Vùng chứa luồng sẽ tiếp cận decode-<env> và hộp cát bash của nó trong decode-sandbox-<env> (runtime/modal_app.py, sandbox/modal_backend.py). ZenML sẽ đặt tên ứng dụng theo cách khác zenml-<run_id> và để lại một ứng dụng mới sau mỗi lần chạy.

Vận hành một cuộc chạy trực tiếp

uv run kitaru executions list
uv run kitaru executions logs <exec_id> --follow
uv run kitaru executions input <exec_id> --wait <name> --value "…"   # resolve a HITL wait
uv run kitaru executions replay <exec_id> --from <checkpoint>

Trang tổng quan: cùng một URL deploy.sh status bản in. người dùng admin, mật khẩu từ ~/.config/decode/kitaru-admin-password (pbcopy < … - đừng lặp lại nó). Trang Flows của nó hiển thị một hàng trên mỗi luồng chỉ với lần chạy mới nhất của nó; Thực thi là danh sách mỗi lần chạy.


4. Tham khảo — các bẫy và lý do tại sao tập lệnh thực hiện chức năng đó

Đọc cái này khi có thứ gì đó bị hỏng. Mỗi mục đều tốn một chu kỳ gỡ lỗi.

roles/storage.objectAdmin vẫn chưa đủ. Lệnh gọi trình kết nối GCS của ZenML get_bucket() ** mọi lúc nó tạo ra thông tin xác thực** — không chỉ trên verify. objectAdmin cấp cho đối tượng quyền đọc/ghi/liệt kê/xóa nhưng không storage.buckets.get, vì vậy việc gửi sẽ chết với 403 … does not have storage.buckets.get access. Do đó, phạm vi xô storage.admin ràng buộc (legacyBucketReader là giải pháp thay thế chặt chẽ hơn).

/var/kitaru phải được chọn là UID 1000. Vùng chứa chạy dưới dạng UID 1000; thư mục máy chủ được gắn kết là được tạo ra thuộc sở hữu gốc. Do đó, lần khởi động đầu tiên bị chết với sqlite3.OperationalError: không thể mở tập tin cơ sở dữ liệu and crash-loops forever, while the health check just hangs. up thăm dò ý kiến cho thư mục — nó không tồn tại cho đến khi konlet khởi động vùng chứa, vì vậy mọi chế độ ngủ cố định sẽ chạy đua với nó — và nó kiểm tra lại mọi up, không chỉ khi nó tạo VM: máy chủ gặp sự cố chính xác là trạng thái bạn chạy lại up để sửa chữa. Một thư mục đã đúng sẽ được giữ nguyên (không cần khởi động lại máy chủ hoạt động tốt).

Gửi đồng thời chủng tộc khi tải lên mã. ZenML lưu trữ gốc nguồn, đặt tên đối tượng theo hàm băm nội dung của nó và tải nó lên với kiểm tra rồi sao chép — TOCTOU. Gửi N chạy cùng một lúc khi đó repository chưa có trong nhóm và tất cả họ đều quyết định tải nó lên; kẻ thua cuộc chết cùng với FileExistsError: Destination file 'gs://…/code_uploads/<hash>.tar.gz' already exists. Một lần chạy khởi động trước tiên hãy tải lên repository và mỗi lần gửi sau đó sẽ bỏ qua quá trình tải lên - thật ấm áp, SAU ĐÓ hãy tản ra và làm không chỉnh sửa các tệp được theo dõi ở giữa (bất kỳ chỉnh sửa nào sẽ thay đổi hàm băm và tái khởi động cuộc đua). scripts/demo-multiple-attempts.sh thực hiện cả hai, cộng với sự dao động 8 giây như một biện pháp bảo hiểm.

Không bao giờ vượt qua platform= để ImageSettings — ghim nó vào Dockerfile FROM. Ngược lại mọi gửi bản dựng lại và đẩy lại hình ảnh mãi mãi. ZenML quyết định liệu nó có thể sử dụng lại bản dựng bằng cách băm hay không cài đặt Docker, nhưng trình tạo của nó thay đổi các cài đặt đó trong quá trình xây dựng (build_config.build_options: pull/rm lật None → False). find_existing_build băm chúng trước đột biến và tổng kiểm tra được lưu trữ trên bản dựng được tính sau nó, vì vậy việc tra cứu không bao giờ có thể khớp với nội dung của bản dựng được ghi lại - một lỗi vĩnh viễn, không phải là một lỗi nhớ đệm. Người dùng mặc định không bao giờ nhìn thấy nó vì build_config là None và không có gì để thay đổi; đi qua platform là thứ tạo ra đối tượng. Modal là x86-64 còn máy Mac thì không, vì vậy nền tảng phải được ghim ở đâu đó — FROM --platform=linux/amd64 làm điều đó không có build_configvà các lớp ZenML hình ảnh ở trên cùng kế thừa kiến trúc. Xác nhận đã sửa: lần gửi thứ hai liên tiếp hiện đã ghi nhật ký Reusing existing build … for stack prod-modal.

TLS là bắt buộc, không đánh bóng. Máy chủ Kitaru gửi strict-transport-security: max-age=63072000 trong khi phục vụ HTTP đơn giản. Một trình duyệt tuân theo nó, nâng cấp yêu cầu tiếp theo lên HTTPS, truy cập vào một cổng không nói gì và bảng điều khiển không phân tích được văn bản gốc của uvicorn Invalid HTTP request received. như JSON: Unexpected token 'I', "Invalid HT"... is not valid JSON. Let's Encrypt sẽ không phát hành trong một thời gian IP trần và không có vùng DNS ở đây nên máy chủ được đặt tên thông qua nip.io (<ip>.nip.io → <ip>).

Từ chối :8080; "quy tắc không cho phép" không giống như đã đóng. Vùng chứa vẫn tiếp tục lắng nghe 0.0.0.0:8080 (konlet sử dụng mạng máy chủ; không có núm liên kết vòng lặp) và mạng mặc định của GCP được cung cấp default-allow-internal — tcp:0-65535 từ 10.128.0.0/9. Bất kỳ VM nào trong VPC đều có thể đọc được quản trị viên mật khẩu và mọi bí mật đều rõ ràng. Quy tắc DENY ở mức ưu tiên 100 đánh bại nó.

kitaru stack create --type modal không tồn tại. Các loại là local/kubernetes/vertex/sagemaker/ azureml. Modal được vận chuyển dưới dạng tích hợp ZenML, do đó ngăn xếp được tập hợp với zenml CLI — Kitaru đọc các ngăn xếp giống nhau trên cùng một máy chủ.

kitaru init là bắt buộc - và phải chạy TRƯỚC kitaru stack use. Không có .kitaru đánh dấu, ZenML suy ra nguồn gốc từ tập lệnh điểm vào - dành cho uv run decode đó là .venv/bin - và repository mã được tải lên trống: RuntimeError: repository mã được tải lên không chứa bất kỳ tập tin. But khởi tạo also writes .kitaru/config.yaml with its own active_stack_id: <default>, vậy chạy nó sau stack use âm thầm hoàn nguyên lựa chọn. Lỗi đó chỉ xảy ra khi triển khai mới — sau này upbỏ qua init bởi vì .kitaru đã tồn tại nên nó sẽ ẩn mỗi lần chạy lại — và đó là mỹ phẩm dành cho chạy bộ (make run-remote xuất khẩu KITARU_STACK, §3), nhưng nó bỏ đi deploy.sh status báo cáo stack default và việc kiểm tra xác minh bên dưới không thành công mà không có lý do thực sự. ensure_stack bây giờ ra lệnh cho họ init → use.

** Cái remote nhóm phụ thuộc là tất cả hoặc không có gì.** Việc gửi cần có toàn bộ của ZenML gcp hội nhập cục bộ (gcsfs, kfp, google-cloud-aiplatform, kubernetes,…). Bỏ lỡ một gói và check_installation() không thành công, quá trình tích hợp không bao giờ kích hoạt và bạn nhận được Loại trình kết nối dịch vụ gcp không có sẵn tại địa phương even though the connector exists on the server. Hence chạy uv --group remote trên mọi lệnh từ xa; cài đặt cơ bản vẫn nhẹ nhàng cho những người khác.

ENV UV_SYSTEM_PYTHON=1 trong hình ảnh luồng. ZenML xếp lớp uv pip install -r của riêng nó .zenml_stack_integration_requirements on top without --system, và uv từ chối cài đặt khi nó không tìm thấy virtualenv (thoát 2).

Bộ nhớ đệm ZenML xây dựng phía máy chủ. Thay đổi thiết lập Docker, gửi lại và ZenML có thể sử dụng lại cài đặt cũ hình ảnh theo thông báo — bản sửa lỗi của bạn có vẻ như không hoạt động. Buộc xây dựng lại:

uv run --group remote zenml pipeline builds list
uv run --group remote zenml pipeline builds delete <id>

Đăng nhập không cần đầu cần khởi động REST. kitaru login mất --api-key hoặc một trình duyệt tương tác flow và việc tạo khóa API cần phải đăng nhập trước. scripts/kitaru_bootstrap_api_key.py phá vỡ chu kỳ với cấp mật khẩu OAuth2.

Gỡ lỗi một lần chạy từ xa không thành công. Nhật ký máy chủ chỉ chứa những gì luồng đã ghi. của thùng chứa traceback tồn tại trong hộp cát Modal, tên lỗi (Modal orchestration sandbox sb-… failed):

import modal
print(modal.Sandbox.from_id("sb-…").stderr.read())

Không phải lỗi nối dây: Gemini thỉnh thoảng trả lời những câu trả lời trống rỗng/chỉ suy nghĩ và pydantic-ai thử lại cho đến khi UnexpectedModelBehavior: Exceeded maximum output retries (3). Không có công cụ nào chạy, không điểm kiểm tra xuất hiện Chạy lại nó.

Bí mật. make sync-secrets ENV=prod gương .env vào decode-prod xô. cái một ngoại lệ (ADR-0015): MODAL_TOKEN_ID / MODAL_TOKEN_SECRET không phải Settings cánh đồng, vì vậy cái xô không bao giờ mang chúng - nhưng bộ chứa luồng cần chúng trong quá trình env để tạo ra các hộp cát bash. Họ đi xe riêng decode-modal bí mật thông qua ImageSettings.secret_environment_from.

Ghim hình ảnh máy chủ vào máy khách. kitaru --version → 0.18.0 phải khớp với thẻ vùng chứa. Nâng cấp kết hợp cả hai với nhau. SQLite trên đĩa khởi động là toàn bộ cơ sở dữ liệu; đĩa điện toán gcloud snapshot là câu chuyện dự phòng của bạn.

5. Chi phí

Mục~/tháng
e2-small VM + đĩa 10GB + IP tĩnh~$15
GCS + Cơ quan đăng ký hiện vật (quy mô khóa học)~$1
Luồng phương thức + hộp đựng cátdựa trên cách sử dụng; thời gian chờ của dàn nhạc (2 giờ) và hộp cát (30 phút) sẽ giới hạn mức ghi
Mục lục nguồn