From 9bf81dcefa2e79ff62cfcb256ab53d3c44cb2db8 Mon Sep 17 00:00:00 2001 From: Ce Gao Date: Wed, 12 Sep 2018 18:55:49 +0800 Subject: [PATCH 1/2] *: Support img Signed-off-by: Ce Gao --- cmd/kubeflow-kernel/command/run.go | 27 ++++++++++- pkg/config/config.go | 9 ++++ pkg/s2i/img/img.go | 74 ++++++++++++++++++++++++++++++ pkg/s2i/img/template.go | 13 ++++++ pkg/s2i/simple/simple.go | 5 +- s2i/pytorch-s2i/Dockerfile | 2 +- 6 files changed, 124 insertions(+), 6 deletions(-) create mode 100644 pkg/config/config.go create mode 100644 pkg/s2i/img/img.go create mode 100644 pkg/s2i/img/template.go diff --git a/cmd/kubeflow-kernel/command/run.go b/cmd/kubeflow-kernel/command/run.go index 9f31fe4..98f8f28 100644 --- a/cmd/kubeflow-kernel/command/run.go +++ b/cmd/kubeflow-kernel/command/run.go @@ -1,12 +1,16 @@ package command import ( + "fmt" "log" kubeflowbackend "github.com/caicloud/ciao/pkg/backend/kubeflow" + "github.com/caicloud/ciao/pkg/config" simpleinterpreter "github.com/caicloud/ciao/pkg/interpreter/simple" "github.com/caicloud/ciao/pkg/kernel" "github.com/caicloud/ciao/pkg/manager" + "github.com/caicloud/ciao/pkg/s2i" + imgs2i "github.com/caicloud/ciao/pkg/s2i/img" simples2i "github.com/caicloud/ciao/pkg/s2i/simple" "github.com/caicloud/ciao/version" "github.com/spf13/cobra" @@ -29,7 +33,7 @@ func init() { } func run(cmd *cobra.Command, args []string) { - kubeConfig := viper.GetString("kubeconfig") + kubeConfig := viper.GetString(config.KubeConfig) if kubeConfig == "" { log.Fatalln("Failed to start the kernel: Kubeconfig missed") } @@ -45,7 +49,15 @@ func run(cmd *cobra.Command, args []string) { log.Fatalf("Error building kubeflow backend: %s\n", err.Error()) } - s2iClient := simples2i.New() + s2iConfig := viper.GetStringMapString(config.S2I) + if s2iConfig == nil { + log.Fatalf("Error creating s2i client: Failed to find the config\n") + } + + s2iClient, err := createS2IClient(s2iConfig) + if err != nil { + log.Fatalf("Error creating s2i client: %s\n", err.Error()) + } interpreter := simpleinterpreter.New() @@ -56,3 +68,14 @@ func run(cmd *cobra.Command, args []string) { log.Println("Running Kubeflow kernel for Jupyter...") ciao.RunKernel() } + +func createS2IClient(s2iConfig map[string]string) (s2i.Interface, error) { + switch s2iConfig[config.S2IProvider] { + case config.S2IProviderS2I: + return simples2i.New(), nil + case config.S2IProviderImg: + return imgs2i.New(), nil + default: + return nil, fmt.Errorf("Failed to find the provider %s", s2iConfig[config.S2IProvider]) + } +} diff --git a/pkg/config/config.go b/pkg/config/config.go new file mode 100644 index 0000000..ea98d26 --- /dev/null +++ b/pkg/config/config.go @@ -0,0 +1,9 @@ +package config + +const ( + KubeConfig = "kubeconfig" + S2I = "s2i" + S2IProvider = "provider" + S2IProviderS2I = "s2i" + S2IProviderImg = "img" +) diff --git a/pkg/s2i/img/img.go b/pkg/s2i/img/img.go new file mode 100644 index 0000000..7614f2e --- /dev/null +++ b/pkg/s2i/img/img.go @@ -0,0 +1,74 @@ +package img + +import ( + "fmt" + "io/ioutil" + "os" + "os/exec" + "path/filepath" + + "github.com/caicloud/ciao/pkg/types" +) + +const ( + prefix = "kubeflow-kernel-code." + codeFile = "code.py" + dockerFile = "Dockerfile" + imageOwner = "gaocegege" +) + +// Client is the type for using img. +type Client struct { +} + +// New creates a new Client. +func New() *Client { + return &Client{} +} + +// SourceToImage converts the code to the image. +func (c Client) SourceToImage(code string, parameter *types.Parameter) (string, error) { + // This is a hack to let kubernetes do not pull from docker registry. + imageName := fmt.Sprintf("%s:v1", filepath.Join(imageOwner, parameter.GenerateName)) + + dir, err := ioutil.TempDir(os.TempDir(), prefix) + if err != nil { + return "", err + } + + err = ioutil.WriteFile(filepath.Join(dir, codeFile), []byte(code), 0666) + if err != nil { + return "", err + } + + if err = c.writeDockerfile(dir, parameter); err != nil { + return "", err + } + + cmd := exec.Command("img", "build", "-t", imageName, dir) + output, err := cmd.Output() + if err != nil { + fmt.Printf("[kubeflow] Failed to build the image: %s", string(output)) + return "", err + } + + fmt.Printf("[kubeflow] Pushing the image...\n") + cmd = exec.Command("img", "push", imageName) + output, err = cmd.Output() + if err != nil { + fmt.Printf("[kubeflow] Failed to push the image: %s", string(output)) + return "", err + } + return imageName, nil +} + +func (c Client) writeDockerfile(dir string, parameter *types.Parameter) error { + var template string + switch parameter.Framework { + case types.FrameworkTypeTensorFlow: + template = tensorflowTemplate + case types.FrameworkTypePyTorch: + template = pytorchTemplate + } + return ioutil.WriteFile(filepath.Join(dir, dockerFile), []byte(template), 0666) +} diff --git a/pkg/s2i/img/template.go b/pkg/s2i/img/template.go new file mode 100644 index 0000000..183a0c7 --- /dev/null +++ b/pkg/s2i/img/template.go @@ -0,0 +1,13 @@ +package img + +const ( + tensorflowTemplate = `FROM tensorflow/tensorflow:1.10.1-py3 +LABEL maintainer="Kubeflow" +COPY ./code.py / +ENTRYPOINT ["python", "/app/code.py"]` + + pytorchTemplate = `FROM pytorch/pytorch:v0.2 +LABEL maintainer="Kubeflow" +COPY ./code.py / +ENTRYPOINT ["python", "/code.py"]` +) diff --git a/pkg/s2i/simple/simple.go b/pkg/s2i/simple/simple.go index 6a7015b..57b904c 100644 --- a/pkg/s2i/simple/simple.go +++ b/pkg/s2i/simple/simple.go @@ -15,8 +15,7 @@ const ( codeFile = "code.py" builderImageTF = "gaocegege/tensorflow-s2i:1.10.1-py3" builderImagePyTorch = "gaocegege/pytorch-s2i:v0.2" - // builderImagePyTorch = "gaocegege/pytorch-s2i:0.4_cuda9_cudnn7" - imageOwner = "caicloud" + imageOwner = "caicloud" ) // S2IClient is the type for using s2i. @@ -47,7 +46,7 @@ func (s S2IClient) SourceToImage(code string, parameter *types.Parameter) (strin cmd.Dir = dir output, err := cmd.Output() if err != nil { - fmt.Printf("[kubeflow] Failed build the image: %s", string(output)) + fmt.Printf("[kubeflow] Failed to build the image: %s", string(output)) return "", err } diff --git a/s2i/pytorch-s2i/Dockerfile b/s2i/pytorch-s2i/Dockerfile index ad8c934..ae429fe 100644 --- a/s2i/pytorch-s2i/Dockerfile +++ b/s2i/pytorch-s2i/Dockerfile @@ -1,4 +1,4 @@ -FROM pytorch/pytorch:0.4_cuda9_cudnn7 +FROM pytorch/pytorch:v0.2 LABEL maintainer="Kubeflow" From 204e011b891a6993dc3a2bf6b4cb09860becc484 Mon Sep 17 00:00:00 2001 From: Ce Gao Date: Wed, 12 Sep 2018 18:55:58 +0800 Subject: [PATCH 2/2] docs: Add Signed-off-by: Ce Gao --- README.md | 1 - docs/installation.md | 13 ++++++++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index d5ad56a..c1a9200 100644 --- a/README.md +++ b/README.md @@ -52,4 +52,3 @@ Please see the [Design Document](./docs/design.md) to know the architecture of C - Thank [kubeflow/kubeflow](https://github.com/kubeflow/kubeflow) for the awesome operators which supports TensorFlow/PyTorch and many other ML frameworks on Kubernetes. - Thank [gopherdata/gophernotes](https://github.com/gopherdata/gophernotes) for the reference implementation of Jupyter Kernel in Golang. -- Thank [openshift/source-to-image](https://github.com/openshift/source-to-image) for the tool to convert source code to Docker image directly. diff --git a/docs/installation.md b/docs/installation.md index 3d36e7d..1eb1c71 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -24,14 +24,25 @@ Then we need to create a configuration file `$HOME/.ciao/config.yaml`: ```yaml kubeconfig: {path to your kubeconfig} +s2i: + provider: {img or s2i} ``` -## Install S2I Builder Image +There are two options about tools to convert the source code in Jupyter Notebook to Docker image: + +- [img](https://github.com/genuinetools/img) (Recommended), which is a daemon-free tool to build and push Docker images. +- [s2i](https://github.com/openshift/source-to-image), which is a source to image tool. + +## Install Image For better performance, we recommend pulling the builder images from Docker Registry ahead of time. There are two builder images for different ML frameworks: - `gaocegege/tensorflow-s2i:1.10.1-py3` - `gaocegege/pytorch-s2i:v0.2` +- `tensorflow/tensorflow:1.10.1-py3` +- `pytorch/pytorch:v0.2` + +Or the time of the first run will be extremely long (which depends on your network). ## Run the Kernel