@@ -29,6 +29,10 @@ KFP_NAMESPACE="${1:?KFP namespace required}"
2929MLFLOW_NAMESPACE=" ${2:? MLflow namespace required} "
3030CONFIG_JSON_PATH=" ${3:? Path to source config.json required} "
3131
32+ MLFLOW_CA_CONFIGMAP=" mlflow-ca-cert"
33+ KFP_CA_BUNDLE_DIR=" /kfp/certs"
34+ KFP_CA_BUNDLE_PATH=" ${KFP_CA_BUNDLE_DIR} /ca.crt"
35+
3236echo " Services in ${MLFLOW_NAMESPACE} namespace:"
3337kubectl get svc -n " $MLFLOW_NAMESPACE " --no-headers
3438MLFLOW_SVC=$( kubectl get svc -n " $MLFLOW_NAMESPACE " --no-headers -o custom-columns=" :metadata.name" | grep -i mlflow | head -1)
@@ -43,7 +47,6 @@ MLFLOW_ENDPOINT="https://${MLFLOW_HOST}:${MLFLOW_PORT}${MLFLOW_STATIC_PREFIX}"
4347echo " MLflow service: $MLFLOW_SVC port=$MLFLOW_PORT endpoint=$MLFLOW_ENDPOINT "
4448
4549# --- Extract CA certificate from the MLflow TLS secret ---
46- CA_MOUNT_PATH=" /etc/mlflow/tls/ca.crt"
4750CA_CERT_FILE=" /tmp/mlflow-ca.crt"
4851
4952echo " Extracting MLflow CA certificate..."
7073echo " $CA_DATA " | base64 -d > " $CA_CERT_FILE "
7174echo " CA certificate extracted to $CA_CERT_FILE ($( wc -l < " $CA_CERT_FILE " ) lines)"
7275
73- # Create a ConfigMap with the CA cert in the KFP namespace for workflow pods
74- kubectl create configmap mlflow-ca-cert -n " $KFP_NAMESPACE " \
76+ # ConfigMap consumed by the API server (plugin config) and compiler (driver/launcher pods).
77+ kubectl create configmap " $MLFLOW_CA_CONFIGMAP " -n " $KFP_NAMESPACE " \
7578 --from-file=ca.crt=" $CA_CERT_FILE " --dry-run=client -o yaml | kubectl apply -f -
7679
77- # --- Patch Argo workflow-controller-configmap to mount the CA cert into all workflow pods ---
78- echo " Patching workflow-controller-configmap to inject MLflow CA into workflow pods..."
79-
80- EXISTING_MAIN_CONTAINER=$( kubectl get configmap workflow-controller-configmap -n " $KFP_NAMESPACE " \
81- -o jsonpath=' {.data.mainContainer}' 2> /dev/null || echo " " )
82- if [ -n " $EXISTING_MAIN_CONTAINER " ]; then
83- MAIN_CONTAINER_PATCH=$( echo " $EXISTING_MAIN_CONTAINER " | \
84- python3 -c "
85- import sys, json
86- raw = sys.stdin.read().strip()
87- try:
88- cfg = json.loads(raw)
89- except json.JSONDecodeError:
90- import yaml
91- cfg = yaml.safe_load(raw) or {}
92- vms = cfg.get('volumeMounts', [])
93- vms = [vm for vm in vms if vm.get('name') != 'mlflow-ca']
94- vms.append({'name': 'mlflow-ca', 'mountPath': '/etc/mlflow/tls', 'readOnly': True})
95- cfg['volumeMounts'] = vms
96- print(json.dumps(cfg))
97- " )
98- else
99- MAIN_CONTAINER_PATCH=' {"volumeMounts":[{"name":"mlflow-ca","mountPath":"/etc/mlflow/tls","readOnly":true}]}'
100- fi
101-
102- EXISTING_WF_DEFAULTS=$( kubectl get configmap workflow-controller-configmap -n " $KFP_NAMESPACE " \
103- -o jsonpath=' {.data.workflowDefaults}' 2> /dev/null || echo " " )
104- if [ -n " $EXISTING_WF_DEFAULTS " ]; then
105- WF_DEFAULTS_PATCH=$( echo " $EXISTING_WF_DEFAULTS " | \
106- python3 -c "
107- import sys, yaml
108- cfg = yaml.safe_load(sys.stdin) or {}
109- spec = cfg.setdefault('spec', {})
110- volumes = spec.setdefault('volumes', [])
111- volumes = [v for v in volumes if v.get('name') != 'mlflow-ca']
112- volumes.append({'name': 'mlflow-ca', 'configMap': {'name': 'mlflow-ca-cert'}})
113- spec['volumes'] = volumes
114- cfg['spec'] = spec
115- print(yaml.dump(cfg, default_flow_style=False))
116- " )
117- else
118- WF_DEFAULTS_PATCH=$( cat << 'YAML '
119- spec:
120- volumes:
121- - name: mlflow-ca
122- configMap:
123- name: mlflow-ca-cert
124- YAML
125- )
126- fi
127-
128- kubectl patch configmap workflow-controller-configmap -n " $KFP_NAMESPACE " --type=merge \
129- -p " $( jq -n --arg mc " $MAIN_CONTAINER_PATCH " --arg wd " $WF_DEFAULTS_PATCH " \
130- ' {"data":{"mainContainer":$mc,"workflowDefaults":$wd}}' ) "
131-
132- # Restart workflow controller to pick up configmap changes
133- kubectl rollout restart deployment/workflow-controller -n " $KFP_NAMESPACE "
134- kubectl rollout status deployment/workflow-controller -n " $KFP_NAMESPACE " --timeout=120s
135-
13680# --- Build the MLflow plugin config with caBundlePath ---
137- MLFLOW_PATCH=$( jq -n --arg endpoint " $MLFLOW_ENDPOINT " --arg caBundlePath " $CA_MOUNT_PATH " ' {
81+ MLFLOW_PATCH=$( jq -n --arg endpoint " $MLFLOW_ENDPOINT " --arg caBundlePath " $KFP_CA_BUNDLE_PATH " ' {
13882 endpoint: $endpoint,
13983 tls: { caBundlePath: $caBundlePath },
14084 settings: { workspacesEnabled: true }
@@ -146,11 +90,11 @@ jq --argjson mlflow "$MLFLOW_PATCH" '. + { plugins: { mlflow: $mlflow } }' \
14690echo " Patched config.json plugins.mlflow:"
14791jq ' .plugins.mlflow' /tmp/kfp-config.json
14892
149- # --- Deploy the config and mount the CA cert into the API server ---
93+ # --- Deploy plugin config and wire CA trust on the API server + compiler ---
15094kubectl create configmap kfp-mlflow-config -n " $KFP_NAMESPACE " \
15195 --from-file=config.json=/tmp/kfp-config.json --dry-run=client -o yaml | kubectl apply -f -
15296kubectl patch deployment ml-pipeline -n " $KFP_NAMESPACE " --type=strategic -p \
153- ' {"spec":{"template":{"spec":{"volumes":[{"name":"mlflow-cfg","configMap":{"name":"kfp-mlflow-config"}},{"name":"mlflow-ca","configMap":{"name":"mlflow-ca-cert" }}],"containers":[{"name":"ml-pipeline-api-server","volumeMounts":[{"name":"mlflow-cfg","mountPath":"/config/config.json","subPath":"config.json"},{"name":"mlflow-ca","mountPath":"/etc/mlflow/tls ","readOnly":true}]}]}}}}'
97+ ' {"spec":{"template":{"spec":{"volumes":[{"name":"mlflow-cfg","configMap":{"name":"kfp-mlflow-config"}},{"name":"mlflow-ca","configMap":{"name":"' " $MLFLOW_CA_CONFIGMAP " ' " }}],"containers":[{"name":"ml-pipeline-api-server","env":[{"name":"CABUNDLE_CONFIGMAP_NAME","value":" ' " $MLFLOW_CA_CONFIGMAP " ' "}]," volumeMounts":[{"name":"mlflow-cfg","mountPath":"/config/config.json","subPath":"config.json"},{"name":"mlflow-ca","mountPath":"' " $KFP_CA_BUNDLE_DIR " ' ","readOnly":true}]}]}}}}'
15498kubectl rollout status deployment/ml-pipeline -n " $KFP_NAMESPACE " --timeout=180s
15599
156100pkill -f " kubectl port-forward.*ml-pipeline.*8888" || true
0 commit comments