ML pipelines, retraining and CI/CD/CT
Section 5: Agent Platform Pipelines (Kubeflow Pipelines and TFX), Managed Service for Apache Airflow, Ray, data and model validation gates, keeping preprocessing consistent between training and serving, retraining policies, MLOps maturity levels and CI/CD/CT with Cloud Build. Includes a pipeline lab.
1. End-to-end ML pipelines
A pipeline turns notebook steps into a repeatable, versioned, automated workflow: ingest → validate data → transform → train → evaluate → validate the model → register → deploy → monitor. Each run records lineage, so you can answer "which data and code produced the model in production?"
Orchestrators
| Orchestrator | Choose when |
|---|---|
| Agent Platform Pipelines | Default for ML: serverless, runs Kubeflow Pipelines (KFP) v2 or TFX pipelines, automatic lineage in ML Metadata, caching, Google Cloud Pipeline Components, scheduling |
| Managed Service for Apache Airflow (formerly Cloud Composer) | Orchestrating whole data platforms: many systems, ETL schedules, cross-team DAGs. It can trigger ML pipelines, but isn't ML-aware (no artifact lineage) |
| Ray on Agent Platform | Python-native distributed data processing, training and tuning (Ray Data, Ray Train, Ray Tune) on a managed Ray cluster |
| Kubeflow Pipelines on GKE | Self-managed open source on your own cluster |
| Templates | Prebuilt pipelines (Tabular Workflows, model evaluation, tuning templates) to start from instead of writing your own |
from kfp import dsl, compiler
from google_cloud_pipeline_components.v1.custom_job import CustomTrainingJobOp
@dsl.component(base_image="python:3.11", packages_to_install=["pandas", "google-cloud-bigquery"])
def validate_data(table: str) -> bool:
... # row counts, schema, nulls, ranges; return False to stop the run
@dsl.component(base_image="python:3.11")
def evaluate(model: dsl.Input[dsl.Model], metrics: dsl.Output[dsl.Metrics]) -> float:
...
@dsl.pipeline(name="churn-training")
def pipeline(table: str = "bq://PROJECT_ID.shop.train", min_auc_pr: float = 0.65):
ok = validate_data(table=table)
with dsl.If(ok.output == True):
train = CustomTrainingJobOp(display_name="train", worker_pool_specs=[...])
score = evaluate(model=...)
with dsl.If(score.output >= min_auc_pr): # model validation gate
... # ModelUploadOp → ModelDeployOp (or register as "challenger")
compiler.Compiler().compile(pipeline, "pipeline.yaml")
# aiplatform.PipelineJob(display_name="churn", template_path="pipeline.yaml",
# enable_caching=True, parameter_values={...}).submit()
dsl.If versus the older dsl.Condition) varies by version; questions focus on concepts: components, artifacts, parameters, conditions, caching and lineage.TFX components (TensorFlow Extended)
| Component | Job |
|---|---|
| ExampleGen | Ingest and split data |
| StatisticsGen, SchemaGen, ExampleValidator | Data statistics, an inferred schema, and anomaly detection (TensorFlow Data Validation, TFDV) |
| Transform | TensorFlow Transform: full-pass preprocessing (vocabularies, normalization) exported into the serving graph, so serving applies identical transforms |
| Trainer, Tuner | Train and tune |
| Evaluator | TensorFlow Model Analysis: metrics overall and by slice; "blesses" the model only if it beats thresholds or the current model |
| InfraValidator, Pusher | Check the model loads and serves; push blessed models to serving |
Validating data and models
- Data validation: schema (types, required columns), ranges, null rates, category values, distribution shifts against training statistics. Stop the pipeline on failure instead of training on bad data.
- Model validation: compare the candidate with thresholds and with the current production model, overall and per slice (region, device, demographic group) to catch hidden regressions and fairness issues. Only then register or deploy.
- Infrastructure validation: the model loads in the serving container and answers within latency limits.
Consistent preprocessing between training and serving
| Approach | How it guarantees consistency |
|---|---|
BigQuery ML TRANSFORM | Preprocessing stored with the model, applied by ML.PREDICT |
| TensorFlow Transform | Transform graph attached to the serving model |
Preprocessing layers inside the model (Keras preprocessing, scikit-learn Pipeline) | The saved artifact contains the logic |
| Feature Store | The same feature definitions feed training (offline) and serving (online) |
| Custom prediction routine | One Python preprocessing function used in both |
2. Retraining and CI/CD/CT
Retraining policy
| Trigger | Good for | Risk |
|---|---|---|
| Schedule (daily, weekly) | Steady data change; simple to run | Retrains when nothing changed (cost) or too late when things change fast |
| New data volume (for example every 100k labelled rows) | Label arrival drives value | Needs data counting |
| Monitoring alert (drift or skew above threshold) | Retrain when inputs change | Drift doesn't always hurt accuracy |
| Performance drop on ground truth | Retrain when it matters | Labels may arrive weeks late |
| On demand (code change) | New features or architecture | Through CI/CD, not by hand |
CI/CD/CT
| Practice | Meaning in ML | Google Cloud pieces |
|---|---|---|
| Continuous integration (CI) | On every commit: unit-test components and data checks, build containers, compile the pipeline | Cloud Build triggers on the repository, Artifact Registry for images and pipeline templates |
| Continuous delivery (CD) | Deploy the new pipeline (and, after validation, models) to staging then production | Cloud Build, Cloud Deploy, approvals |
| Continuous training (CT) | The pipeline itself retrains on schedule or trigger | Pipeline schedules, Cloud Scheduler, Pub/Sub or Eventarc triggers, Cloud Run functions |
Google's MLOps maturity levels: level 0 is manual notebooks and hand deployment; level 1 automates training with a pipeline (CT); level 2 automates the pipeline's own build, test and deployment (CI/CD of the pipeline).
# cloudbuild.yaml (sketch): test, build the trainer image, compile and upload the pipeline
steps:
- name: python:3.11
entrypoint: bash
args: ["-c", "pip install -r requirements.txt && pytest tests/"]
- name: gcr.io/cloud-builders/docker
args: ["build", "-t", "us-docker.pkg.dev/PROJECT_ID/ml/trainer:$SHORT_SHA", "trainer/"]
- name: python:3.11
entrypoint: bash
args: ["-c", "pip install kfp && python compile_pipeline.py && python upload_template.py"]
images: ["us-docker.pkg.dev/PROJECT_ID/ml/trainer:$SHORT_SHA"]
3. MLOps maturity at a glance
| Level | What it looks like | Typical exam fix |
|---|---|---|
| 0: manual | Notebooks, CSV exports, hand-uploaded models, no lineage | Move training into a pipeline reading BigQuery directly; register models |
| 1: pipeline automation (CT) | Pipeline retrains on schedule or trigger with validation gates | Add data and model validation, evaluation against production, scheduled or triggered runs |
| 2: CI/CD of the pipeline | Code changes are tested, built and deployed automatically | Cloud Build triggers, Artifact Registry, staged rollout with approvals |
4. Lab: a Kubeflow pipeline with a validation gate
- Create three lightweight components:
validate_data(checks row count and nulls),train, andevaluate(returns a metric). - Wire them with conditions so training only runs when validation passes, and registration happens only when the metric beats the current production model.
- Compile to YAML, submit with
PipelineJob(enable_caching=True), then re-run unchanged and watch cached steps skip. - Open the lineage view for the resulting model and trace it back to the dataset and run parameters.
- Add a Cloud Build trigger that runs component unit tests and recompiles the pipeline on each commit.
- Create a weekly schedule for the pipeline; then delete the schedule.
5. "Which orchestrator?" chooser
Pick an item to see what it means, why, and the tempting wrong choice.
6. Sample outputs
What the tools return. Expand an item to see a typical result and what to read in it.
Pipeline run (console summary)A run where the model validation gate stopped deployment
Run: churn-training-20261114-0200 State: SUCCEEDED
validate-data SUCCEEDED (cached)
train SUCCEEDED 41m
evaluate SUCCEEDED 3m output: 0.612
condition-1 (score >= 0.65) SKIPPED
model-upload NOT_TRIGGERED
model-deploy NOT_TRIGGEREDWhat to look at: The run succeeded, but the new model scored 0.612, below the 0.65 gate, so nothing was registered or deployed: the pipeline did its job. The data validation step reused a cached result because its inputs didn't change.
7. Scenarios
Exam-style situations: how to diagnose each one, the likely cause and the fix.
8. Searchable reference
| Item | Meaning |
|---|
9. Flashcards (click to flip)
10. Common gotchas in one list
- Agent Platform Pipelines for ML lineage; Airflow for data-platform orchestration.
- Never auto-deploy without an evaluation gate against the current model.
- Retraining policy follows the data: schedules, data volume, drift or performance triggers.
- Late labels mean you can't wait for accuracy drops; use drift and schedules.
- CI tests code and builds images; CT retrains; CD deploys.
- Caching needs deterministic inputs.
- Validate data before training; schema changes otherwise train garbage.
- Build containers in CI and tag by commit for reproducibility.
- Composer is now Managed Service for Apache Airflow; the gcloud composer commands remain.