引言
kubeflow 是 google 開源的一個基於 kubernetes 的 ML workflow 平臺,其集成了大量的機器學習工具,比如用於交互性實驗的 jupyterlab 環境,用於超參數調整的 katib,用於 pipeline 工作流控制的 argo workflow等。作為一個“大型工具箱”集合,kubeflow 為機器學習開發者提供了大量可選的工具,同時也為機器學習的工程落地提供了可行性工具。
當然,由於 kubeflow 主要是 google 在主導,雖然其作為一個開源項目,但在很多選型上都和 google 自家產品深度綁定,比如 google 自己的存儲工具 gstuil 作為一等公民,鏡像倉庫地址也大多是 grc.io
這樣的google自己的鏡像倉庫地址。
安裝部署
關於 kubeflow 的安裝部署如果沒有比較好的外網訪問環境的話,大家可以參考我開源的一個project,專門做國內manifest,鏡像倉庫都是採用阿里雲鏡像,在國內網絡環境下也能快速輕鬆安裝部署:
git clone https://github.com/shikanon/kubeflow-manifests.git
cd kubeflow-manifests
python install.py
安裝完成後查看等待所有 pod running。
$ kubectl get po -A
NAMESPACE NAME READY STATUS RESTARTS AGE
auth dex-6686f66f9b-54s96 1/1 Running 0 2h6m
cattle-system cattle-cluster-agent-5f695c79c-x9ql7 1/1 Running 0 3h
cert-manager cert-manager-9d5774b59-4xjmk 1/1 Running 0 2h23m
cert-manager cert-manager-cainjector-67c8c5c665-nmcp6 1/1 Running 0 2h23m
cert-manager cert-manager-webhook-75dc9757bd-z2k5c 1/1 Running 1 2h23m
fleet-system fleet-agent-7d959597cb-q8ckq 1/1 Running 0 3h
istio-system authservice-0 1/1 Running 0 2h23m
istio-system cluster-local-gateway-66bcf8bc5d-j9kvp 1/1 Running 0 2h23m
istio-system istio-ingressgateway-85b49c758f-l4hgc 1/1 Running 0 2h22m
istio-system istiod-5ff6cdbbcd-2v5kj 1/1 Running 0 2h23m
knative-eventing broker-controller-5c84984b97-86zkx 1/1 Running 0 2h23m
knative-eventing eventing-controller-54bfbd5446-rx9ll 1/1 Running 0 2h23m
knative-eventing eventing-webhook-58f56d9cf4-bnq9q 1/1 Running 0 2h23m
knative-eventing imc-controller-769896c7db-kzjv6 1/1 Running 0 2h23m
knative-eventing imc-dispatcher-86954fb4cd-9b6gz 1/1 Running 0 2h23m
knative-serving activator-75696c8c9-9c5ff 1/1 Running 0 2h23m
knative-serving autoscaler-6764f9b5c5-2gwqj 1/1 Running 0 2h23m
knative-serving controller-598fd8bfd7-bpn5k 1/1 Running 0 2h23m
knative-serving istio-webhook-785bb58cc6-ts9f2 1/1 Running 0 2h23m
knative-serving networking-istio-77fbcfcf9b-pg26h 1/1 Running 0 2h23m
knative-serving webhook-865f54cf5f-rzpjf 1/1 Running 0 2h23m
kube-system coredns-5644d7b6d9-hwwnr 1/1 Running 0 3h1m
kube-system coredns-5644d7b6d9-zds92 1/1 Running 0 3h1m
kube-system etcd-kubeflow-control-plane 1/1 Running 0 3h
kube-system kindnet-8tvm5 1/1 Running 0 3h1m
kube-system kindnet-zkmkq 1/1 Running 0 3h1m
kube-system kube-apiserver-kubeflow-control-plane 1/1 Running 0 3h
kube-system kube-controller-manager-kubeflow-control-plane 1/1 Running 0 3h
kube-system kube-proxy-c8zn7 1/1 Running 0 3h1m
kube-system kube-proxy-k7b8c 1/1 Running 0 3h1m
kube-system kube-scheduler-kubeflow-control-plane 1/1 Running 0 3h
kubeflow admission-webhook-deployment-6fb9d65887-pzvgc 1/1 Running 0 2h22m
kubeflow cache-deployer-deployment-7558d65bf4-jhgwg 2/2 Running 1 2h6m
kubeflow cache-server-c64c68ddf-stz72 2/2 Running 0 22m
kubeflow centraldashboard-7b7676d8bd-g2s8j 1/1 Running 0 2h7m
kubeflow jupyter-web-app-deployment-66f74586d9-scbsm 1/1 Running 0 2h5m
kubeflow katib-controller-77675c88df-mx4rh 1/1 Running 0 2h22m
kubeflow katib-db-manager-646695754f-z797r 1/1 Running 0 2h22m
kubeflow katib-mysql-5bb5bd9957-gbl5t 1/1 Running 0 2h22m
kubeflow katib-ui-55fd4bd6f9-r98r2 1/1 Running 0 2h22m
kubeflow kfserving-controller-manager-0 2/2 Running 0 2h22m
kubeflow kubeflow-pipelines-profile-controller-5698bf57cf-btpn5 1/1 Running 0 22m
kubeflow metacontroller-0 1/1 Running 0 2h7m
kubeflow metadata-envoy-deployment-76d65977f7-rmlzc 1/1 Running 0 2h7m
kubeflow metadata-grpc-deployment-697d9c6c67-j6dl2 2/2 Running 3 2h7m
kubeflow metadata-writer-58cdd57678-8t6gw 2/2 Running 1 2h7m
kubeflow minio-6d6784db95-tqs77 2/2 Running 0 2h7m
kubeflow ml-pipeline-85fc99f899-plsz2 2/2 Running 1 2h7m
kubeflow ml-pipeline-persistenceagent-65cb9594c7-xvn4j 2/2 Running 1 2h7m
kubeflow ml-pipeline-scheduledworkflow-7f8d8dfc69-7wfs4 2/2 Running 0 2h7m
kubeflow ml-pipeline-ui-5c765cc7bd-4r2j7 2/2 Running 0 2h7m
kubeflow ml-pipeline-viewer-crd-5b8df7f458-5b8qg 2/2 Running 1 2h7m
kubeflow ml-pipeline-visualizationserver-56c5ff68d5-92bkf 2/2 Running 0 2h7m
kubeflow mpi-operator-789f88879-n4xms 1/1 Running 0 2h22m
kubeflow mxnet-operator-7fff864957-vq2bg 1/1 Running 0 2h22m
kubeflow mysql-56b554ff66-kd7bd 2/2 Running 0 2h7m
kubeflow notebook-controller-deployment-74d9584477-qhpp8 1/1 Running 0 2h22m
kubeflow profiles-deployment-67b4666796-k7t2h 2/2 Running 0 2h22m
kubeflow pytorch-operator-fd86f7694-dxbgf 2/2 Running 0 2h22m
kubeflow tensorboard-controller-controller-manager-fd6bcffb4-k9qvx 3/3 Running 1 2h22m
kubeflow tensorboards-web-app-deployment-78d7b8b658-dktc6 1/1 Running 0 2h22m
kubeflow tf-job-operator-7bc5cf4cc7-gk8tz 1/1 Running 0 2h22m
kubeflow volumes-web-app-deployment-68fcfc9775-bz9gq 1/1 Running 0 2h22m
kubeflow workflow-controller-5449754fb4-tdg2t 2/2 Running 1 22m
kubeflow xgboost-operator-deployment-5c7bfd57cc-9rtq6 2/2 Running 1 2h22m
local-path-storage local-path-provisioner-58f6947c7-mv4mg 1/1 Running 0 3h1m
訪問控制
kubeflow 通過dex 進行鑑權服務,安裝好kubeflow,打開本地瀏覽器,看到 dex 的登錄驗證框,輸出賬號密碼:
這裡的賬號密碼可以通過 dex 的 configmap 設置:
apiVersion: v1
data:
config.yaml: |
issuer: http://dex.auth.svc.cluster.local:5556/dex
storage:
type: kubernetes
config:
inCluster: true
web:
http: 0.0.0.0:5556
logger:
level: "debug"
format: text
oauth2:
skipApprovalScreen: true
enablePasswordDB: true
staticPasswords:
- email: "[email protected]"
hash: "$2a$10$2b2cU8CPhOTaGrs1HRQuAueS7JTT5ZHsHSzYiFPm1leZck7Mc8T4W"
username: "admin"
userID: "08a8684b-db88-4b73-90a9-3cd1661f5466"
staticClients:
- idEnv: OIDC_CLIENT_ID
redirectURIs: ["/login/oidc"]
name: 'Dex Login Application'
secretEnv: OIDC_CLIENT_SECRET
kind: ConfigMap
metadata:
name: dex
namespace: auth
email 就是我們登錄的用戶名,hash 就是我們的設置的密碼,可以通過以下這段python代碼來生成:
from passlib.hash import bcrypt
import getpass
print(bcrypt.using(rounds=12, ident="2y").hash(getpass.getpass()))
組件功能介紹
可以看到新版的kubeflow多了很多功能。
這裡按模塊介紹下 Kubeflow 的幾個核心組件。
- Notebook Servers,作為一個管理線上交互實驗的記錄工具,可以幫助算法人員快速完成算法實驗,同時notebook server 提供了統一的文檔管理能力。
- AutoML,提供自動化的服務,對特徵處理、特徵選擇、模型選擇、模型參數的配置、模型訓練和評估等方面,實現了全自動建模,降低算法人員手動實驗次數。
- Pipeline,提供一個算法流水線的工程化工具,將算法各流程模塊以拓撲圖的形式組合起來,同時結合 argo 可以實現 MLOps。
- Serverless,將模型直接發佈成一個對外的服務,縮短從實驗到生產的路徑。
Notebook Servers
notebook 可以說是做機器學習最喜歡用到的工具了,完美的將動態語言的交互性發揮出來,kubeflow 提供了 jupyter notebook 來快速構建雲上的實驗環境,這裡以一個我們自定義的鏡像為例:
我們創建了一個test-for-jupyter
名字的鏡像,配置了一個 tensorflow 的鏡像,點擊啟動,我們可以看到在kubeflow-user-example-com
命名空間下已經創建我們的應用了:
kubectl get po -nkubeflow-user-example-com
NAME READY STATUS RESTARTS AGE
ml-pipeline-ui-artifact-6d7ffcc4b6-9kxkk 2/2 Running 0 48m
ml-pipeline-visualizationserver-84d577b989-5hl46 2/2 Running 0 48m
test-for-jupyter-0 0/2 PodInitializing 0 44s
創建完成後點擊 connect 就可以進入我們創建的應用界面中了
在 jupyterlab 環境中開發人員可以很方便的進行算法實驗,同時由於運行在雲上利用 k8s api甚至可以很方便構建k8s資源,比如通過 kfserving 創建一個ML服務。
AutoML
AutoML 是機器學習比較熱的領域,主要用來模型自動優化和超參數調整,這裡其實是用的 Katib來實現的,一個基於k8s的 AutoML 項目,詳細見https://github.com/kubeflow/katib。
Katib 主要提供了 超參數調整(Hyperparameter Tuning),早停法(Early Stopping)和神經網絡架構搜索(Neural Architecture Search)
這裡以一個隨機搜索算法為例:
apiVersion: "kubeflow.org/v1beta1"
kind: Experiment
metadata:
namespace: kubeflow-user-example-com
name: random-example
spec:
objective:
type: maximize
goal: 0.99
objectiveMetricName: Validation-accuracy
additionalMetricNames:
- Train-accuracy
algorithm:
algorithmName: random
parallelTrialCount: 3
maxTrialCount: 12
maxFailedTrialCount: 3
parameters:
- name: lr
parameterType: double
feasibleSpace:
min: "0.01"
max: "0.03"
- name: num-layers
parameterType: int
feasibleSpace:
min: "2"
max: "5"
- name: optimizer
parameterType: categorical
feasibleSpace:
list:
- sgd
- adam
- ftrl
trialTemplate:
primaryContainerName: training-container
trialParameters:
- name: learningRate
description: Learning rate for the training model
reference: lr
- name: numberLayers
description: Number of training model layers
reference: num-layers
- name: optimizer
description: Training model optimizer (sdg, adam or ftrl)
reference: optimizer
trialSpec:
apiVersion: batch/v1
kind: Job
spec:
template:
spec:
containers:
- name: training-container
image: docker.io/kubeflowkatib/mxnet-mnist:v1beta1-45c5727
command:
- "python3"
- "/opt/mxnet-mnist/mnist.py"
- "--batch-size=64"
- "--lr=${trialParameters.learningRate}"
- "--num-layers=${trialParameters.numberLayers}"
- "--optimizer=${trialParameters.optimizer}"
restartPolicy: Never
這裡以一個簡單的神經網絡為例,該程序具有三個參數 lr, num-layers, optimizer,採用的算法是隨機搜索,目標是最大化準確率(accuracy)。
可以直接在界面中填上yaml文件,然後提交。
完成後會生成一張各參數和準確率的關係圖和訓練列表:
Experiments and Pipelines
experiments 為我們提供了一個可以創建實驗空間功能, pipeline
定義了算法組合的模板,通過 pipeline
我們可以將算法中各處理模塊按特定的拓撲圖的方式組合起來。
這裡可以看看官方提供的幾個 pipeline 例子:
kubeflow pipeline
本質是基於 argo workflow
實現,由於我們的kubeflow是基於kind上構建的,容器運行時用的containerd,而workflow默認的pipeline執行器是docker,因此有些特性不兼容,這塊可以見 argo workflow 官方說明:https://argoproj.github.io/argo-workflows/workflow-executors/。
這裡我是把 workflow 的 containerRuntimeExecutor
改成了 k8sapi
。但 k8sapi
由於在 workflow 是二級公民,因此有些功能不能用,比如 kubeflow pipeline 在 input/output 的 artifacts 需要用到 docker cp
命令,可以參考這個issue: https://github.com/argoproj/argo-workflows/issues/2685#issuecomment-613632304
由於以上原因 kubeflow 默認給的幾個案例並沒有用 volumes 是無法在 kind 中運行起來,這裡我們基於 argo workflow 語法自己實現一個 pipeline
基於pipeline構建一個的工作流水
第一步,構建一個 workflow pipeline 文件:
apiVersion: argoproj.io/v1alpha1
kind: Workflow
metadata:
generateName: kubeflow-test-
spec:
entrypoint: kubeflow-test
templates:
- name: kubeflow-test
dag:
tasks:
- name: print-text
template: print-text
dependencies: [repeat-line]
- {name: repeat-line, template: repeat-line}
- name: repeat-line
container:
args: [--line, Hello, --count, '15', --output-text, /gotest/outputs/output_text/data]
command:
- sh
- -ec
- |
program_path=$(mktemp)
printf "%s" "$0" > "$program_path"
python3 -u "$program_path" "$@"
- |
def _make_parent_dirs_and_return_path(file_path: str):
import os
os.makedirs(os.path.dirname(file_path), exist_ok=True)
return file_path
def repeat_line(line, output_text_path, count = 10):
'''Repeat the line specified number of times'''
with open(output_text_path, 'w') as writer:
for i in range(count):
writer.write(line + '\n')
import argparse
_parser = argparse.ArgumentParser(prog='Repeat line', description='Repeat the line specified number of times')
_parser.add_argument("--line", dest="line", type=str, required=True, default=argparse.SUPPRESS)
_parser.add_argument("--count", dest="count", type=int, required=False, default=argparse.SUPPRESS)
_parser.add_argument("--output-text", dest="output_text_path", type=_make_parent_dirs_and_return_path, required=True, default=argparse.SUPPRESS)
_parsed_args = vars(_parser.parse_args())
_outputs = repeat_line(**_parsed_args)
image: python:3.7
volumeMounts:
- name: workdir
mountPath: /gotest/outputs/output_text/
volumes:
- name: workdir
persistentVolumeClaim:
claimName: kubeflow-test-pv
metadata:
annotations:
- name: print-text
container:
args: [--text, /gotest/outputs/output_text/data]
command:
- sh
- -ec
- |
program_path=$(mktemp)
printf "%s" "$0" > "$program_path"
python3 -u "$program_path" "$@"
- |
def print_text(text_path): # The "text" input is untyped so that any data can be printed
'''Print text'''
with open(text_path, 'r') as reader:
for line in reader:
print(line, end = '')
import argparse
_parser = argparse.ArgumentParser(prog='Print text', description='Print text')
_parser.add_argument("--text", dest="text_path", type=str, required=True, default=argparse.SUPPRESS)
_parsed_args = vars(_parser.parse_args())
_outputs = print_text(**_parsed_args)
image: python:3.7
volumeMounts:
- name: workdir
mountPath: /gotest/outputs/output_text/
volumes:
- name: workdir
persistentVolumeClaim:
claimName: kubeflow-test-pv
metadata:
annotations:
argo workflow 的語法可以參考:https://argoproj.github.io/argo-workflows/variables/
這裡我們定義了兩個任務 repeat-line 和 print-text, repeat-line 任務會將生產結果寫入 kubeflow-test-pv
的 PVC 中, print-text 會從 PVC 中讀取數據輸出到 stdout。
這裡由於用到 PVC,我們需要先在集群中創建一個kubeflow-test-pv
的PVC:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: kubeflow-test-pv
namespace: kubeflow-user-example-com
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 128Mi
第二步,定義好 pipeline 文件後可以創建pipeline:
第三步,啟動一個pipeline:
啟動 pipeline 除了單次運行模式 one-off,也支持定時器循環模式 Recurring,這塊可以根據自己的需求確定。
查看運行結果:
運行完後,可以將實驗進行歸檔(Archived)。
關於 MLOps 的一點思考
我們來看一個簡單的 ML 運作流程:
這是一個 google 提供的 level 1 級別的機器學習流水線自動化,整個流水線包括以下幾部分:
- 構建快速算法實驗的環境(experimentation),這裡的步驟已經過編排,各個步驟之間的轉換是自動執行的,這樣可以快速迭代實驗,並更好地準備將整個流水線移至生產環境,在這個環境中算法研究員只進行模塊內部的工作。
- 構建可複用的生產環境流水線,組件的源代碼模塊化,實驗環境模塊化流水線可以直接在 staging 環境和 production 環境中使用。
- 持續交付模型,生產環境中的機器學習流水線會向使用新數據進行訓練的新模型持續交付預測服務。
基於上述功能描述我們其實可以基於 kubeflow 的 pipeline
和 kfserving
功能輕鬆實現一個簡單的 MLOps 流水線發佈流程。不過,值得注意的是,DevOps 本身並不僅僅是一種技術,同時是一種工程文化,所以在實踐落地中需要團隊各方的協同分階段的落地。這塊可以參考《MLOps: Continuous delivery and automation pipelines in machine learning》和《Hidden Technical Debt in Machine Learning Systems》
參考文獻
- https://www.tensorflow.org/tutorials/quickstart/beginner
- https://github.com/dexidp/dex
- https://github.com/kubeflow/kfserving/tree/master/docs
- https://argoproj.github.io/argo-workflows/workflow-executors/
- https://github.com/shikanon/kubeflow-manifests
- https://argoproj.github.io/argo-workflows/variables/
- https://cloud.google.com/architecture/mlops-continuous-delivery-and-automation-pipelines-in-machine-learning