Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Stack vs sample proposal and outer loop proposal #492

Merged
merged 5 commits into from
Jun 17, 2021
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
310 changes: 310 additions & 0 deletions docs/proposals/devfile/outer-loop-build-and-deploy.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,310 @@
# Project Proposal - Devfile 2.0 outer-loop devfile build and deploy functions

## Background
Existing devfile 2.0 mainly focuses on providing inner-loop support. This proposal adds support on outer-loop scenarios to expand the scope of usage of devfile support.

### Outer-loop scenario
As an application developer, I would like to build a microservice and deploy it to Kubernetes to do a build that is typically done as part of a pipeline

- Do a full build to build the container image
- Input\
The build may use different technologies for building, e.g. dockerfile, buildpacks
- Output:\
A container image that contains the microservice and ready to be deployed to Kubernetes
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is more readable on single lines

    - **Input**: The build may use different technologies for building, e.g. dockerfile, buildpacks
    - **Output**: A container image that contains the microservice and ready to be deployed to Kubernetes

Also, - Input is missing a colon.

Copy link
Contributor Author

@elsony elsony Jun 17, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I find using it more readable using separate lines so I keep the separate lines. I've added the missing colon.

- Deploy the built image to Kubernetes:
- Deploy the built image to a cluster:\
Deployment may use different methods of deployment, e.g. Kubernetes deployment manifest, Operators, Helm, Knative

The focus of this proposal is to provide the ability to build and deploy applications or services in a way similar to what will be done for production. In contrast to the inner-loop scenario, the build will not only focus on the building of the application but will also cover building the runtime container that runs the application.

We will cover two main stages:
1. Build the image
2. Deployment of the image. Deploy the image to a cluster
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
2. Deployment of the image. Deploy the image to a cluster
2. Deploy the image to a cluster

Copy link
Contributor Author

@elsony elsony Jun 17, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed


## Build the image

This includes the creation of the base image of the runtime container, building the application, and packaging the application as part of the container image. Different mechanisms can be used for building the image, for example:
1. Dockerfile
2. Dockerfile stages
3. Buildpacks
4. Source-to-Image (S2I)
5. Tekton

The initial design will focus on the first two items for discussion. Other ways of building the image will be covered later.

The definition of the image build introduces a new component type `image` for the definition of the image build. The `image` component definition can use different build strategies, e.g. `dockerfile`, `s2i`, `buildpacks`, etc.

__Alternative approach:__ we can consider reusing the `exec` type except that these build types are special and we may not want to incorporate the full image build command into an `exec` command. Therefore, I chose to use a different type of command here.

### Example using a file Dockerfile as part of the app:
variables:
myimage: myimagename
components:
- name: mydockerfileimage
image:
imageName: {{myimage}}
dockerfile:
buildContext: ${PROJECTS_ROOT}/build
location: Dockerfile
args: [ "arg1", "arg2", "arg3" ]
rootRequired: false
Comment on lines +41 to +51
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use regular code blocks to get syntax highlighting.

Suggested change
variables:
myimage: myimagename
components:
- name: mydockerfileimage
image:
imageName: {{myimage}}
dockerfile:
buildContext: ${PROJECTS_ROOT}/build
location: Dockerfile
args: [ "arg1", "arg2", "arg3" ]
rootRequired: false
```yaml
variables:
myimage: myimagename
components:
- name: mydockerfileimage
image:
imageName: {{myimage}}
dockerfile:
buildContext: ${PROJECTS_ROOT}/build
location: Dockerfile
args: [ "arg1", "arg2", "arg3" ]
rootRequired: false

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed


`imageName`: name of the generated image (or we may use an id for referencing the image). This imageName can also be fully qualified to allow pushing of the image to a specific image registry.

`buildContext`: Path of source directory to establish build context. Default to ${PROJECT_ROOT} (optional)

`location`: Dockerfile location which can be an URL or a path relative to buildContext

`args`: Argument list for the docker build (optional)

`rootRequired`: Specifies whether a privileged builder pod is required. Default is false. (optional)

__Note:__
1. A common pattern will be using a global variable to define the image name (`myimage` in the example above) so that it can be easily referred to in the deploy step later.

### Example using a Dockerfile with registry and secret for the image push:
variables:
myimage: myimagename
components:
- name: mydockerfileimage
image:
imageName: {{myimage}}
dockerfile:
buildContext: ${PROJECTS_ROOT}/build
location: https://github.com/redhat-developer/devfile-sample/blob/master/src/Dockerfile
args: [ "arg1", "arg2", "arg3" ]
rootRequired: false
envFrom:
- secretRef:
name: my-secret
Comment on lines +69 to +82
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Enclose in ```yaml (....) ```

Applies to other code blocks in this document as well.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed all


`secretRef`: The reference to the secret for pushing the image to the registry

For the secrets, it should support the same mechanisms as specified in https://github.com/devfile/api/issues/299.

### Example using `apply` command on an image component:
commands:
- id: deploybuild
apply:
component: mydockerfileimage
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure I understand the purpose of the apply command here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See the note section. It is used for controlling the image startup.


### Example using a Dockerfile stored within the devfile registry as a resource with `apply` command:
variables:
myimage: myimagename
components:
- name: mydockerfileimage
image:
imageName: {{myimage}}
dockerfile:
id: mycompany/my-node-stack-dockerfile/v2.2.2
args: [ ]
Comment on lines +105 to +107
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand what this refers to. What is this an id into?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's for referring to a resource within the registry.

commands:
- id: deploybuild
apply:
component: mydockerfileimage

__Note:__
1. The `apply` command with the `image` component is optional. If the `apply` command that references an image component does not exist, then the image build will be done at the startup automatically (similar to the behaviour of the existing `kubernetes` components). If there is an `apply` command that references the `image` component, then the user will need to create a composite command to chain the image build apply command with the deploy command (see the deployment command section) to complete the build and deploy.

### Example using a Dockerfile stored in a git repo with push registry without apply command:
variables:
myimage: myimagename
components:
- name: mydockerfileimage
image:
imageName: {{myimage}}
dockerfile:
git:
remotes:
origin: "https://github.com/odo-devfiles/nodejs-ex.git"
location: Dockerfile
args: [ ]
Comment on lines +126 to +130
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We're really overloading .image.dockerfile with multiple disjoint sets of fields. This might be hard to use/understand.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR is supposed to convert the existing design proposal spec so that everyone has access. If there are suggestions on spec changes, please open a separate item to discuss.


The git definition will be the same as the one in `starterProjects` definition that supports `checkoutFrom`

`location`: the location of the dockerfile within the repo. If `checkoutFrom` is being used, the location will be relative to the root of the resources after cloning the resources using the `checkoutFrom` setting.
Comment on lines +133 to +135
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we're using a lot of logic from projects, why don't we just use projects for this? If you have the section above as a project in your devfile, you should just be able to use the first case (dockerfile + buildContext, etc.)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We are only reusing the location definition, e.g. git and zip, of a project. Project has special meaning for defining and it is incorrect to retry to replace the dockerfile component with the project component.


Notes:
1. The build tool/mechanism, e.g. buildah/kaniko, used for building the dockerfile is up to the tools so it is not part of the spec
1. If endpoints definition is needed, it will be defined via the corresponding deployment manifest in the deployment step, e.g. inside the kubernetes deployment manifest.
1. Do we need image push registry info to specify where to push the image to? If needed, then the secret for the push can be specified as `secretKeyRef` in https://github.com/devfile/api/issues/299

### Example using SourceToImage (S2I):
variables:
myimage: myimagename
components:
- name: mys2iimage
image:
imageName: {{myimage}}
s2i:
builderImageNamespace: mynamespace
builderImageStreamTag: mytag
scriptLocation:
remotes:
origin: "https://github.com/odo-devfiles/nodejs-ex.git"
commands:
- id: deploybuild
apply:
component: mys2iimage

`builderImageNamespace`: Namespace where builder image is present

`builderImageStreamTag`: Builder image name with tag

`scriptionLocation`: Script URL to override default scripts provided by builder image

`incrementalBuild`: Flag that indicates whether to perform incremental builds. Default is true (optional)

## Deployment of the image
Deploy the image to a cluster. Different technologies can be used for deploying the image, for example:
1. Kubernetes deployment manifest
1. Operators
1. Helm

The initial design will focus on the first two items for discussion. Other ways of building the image will be covered later.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These items may need to be reordered -- it looks like the proposal below covers deployment manifest and helm.

Copy link
Contributor Author

@elsony elsony Jun 17, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reordered the example to match.


To specify the deploy step, we reuse the existing `apply` command type under the `commands` to apply `kubernetes` components. The deploy step uses a new `group` kind called `deploy`.

### Deploying technologies
The current design is to try to reuse the existing `kubernetes` component as much as possible for all three deployment technologies, namely Kubernetes deployment manifest, Operators, and Helm given that all of them can be represented as `kubernetes` objects. We’ll only introduce `operator` or `helm` component types only if we find that there are extra requirements needed for Operators and Helm.

#### Examples of using `deploy` group command:
##### Kubernetes deployment manifest:
components:
- name: myk8sdeploy
kubernetes:
uri: deploy/deployment-manifest.yaml

commands:
- id: deployk8s
apply:
component: myk8sdeploy
group:
kind: deploy
isDefault: true
attributes:
- name: CONTAINER_IMAGE
value: {{myimage}}

`uri`: Kubernetes manifest location (can use `kubectl` to deploy) which can be an URL or a path relative to the devfile. [Example of Kubernetes deployment](#markdown-header-example-of-kubernetes-deployment-manifest) manifest and [example of Operator deployment manifest](#markdown-header-example-of-operator-deployment-manifest).

Variables that need to be replaced during the deployment can be specified using a new `attributes` definition under the `apply` command. One usage example is to pass the image name along to the deployment manifest. A common practice is to use a global variable, e.g. `myimage` in the example above, to refer to the image built in the image built stage.

##### Kubernetes deployment manifest (inlined):
components:
- name: myk8deploy
kubernetes:
inlined: |
apiVersion: batch/v1
kind: Job
metadata:
name: pi
spec:
template:
spec:
containers:
- name: job
image: {{myimage}}
command: ["some", "command", "with", "parameters"]
restartPolicy: Never
backoffLimit: 4
commands:
- id: deployk8s
apply:
component: myk8sdeploy
group:
kind: deploy
isDefault: true

##### Helm:
components:
- name: myhelmdeploy
helm:
chart: http://helm-chart-url
values:
image: quay.io/sample/hello-world # A chart may expose a well-known values.yaml parameter called "image".
replicas: 3
commands:
- id: deployHelm
apply:
component: myhelmdeploy
group:
kind: deploy
isDefault: true
variables:
- name: CONTAINER_IMAGE
value: {{myimage}}


### Deployment manifest examples
#### Example of Kubernetes deployment manifest:
---
kind: Deployment
apiVersion: apps/v1
metadata:
name: {{.COMPONENT_NAME}}
spec:
replicas: 1
selector:
matchLabels:
app: {{.COMPONENT_NAME}}
template:
metadata:
creationTimestamp: null
labels:
app: {{.COMPONENT_NAME}}
spec:
containers:
- name: {{.COMPONENT_NAME}}
image: {{.CONTAINER_IMAGE}}
ports:
- name: http
containerPort: {{.PORT}}
protocol: TCP
---
kind: Service
apiVersion: v1
metadata:
name: {{.COMPONENT_NAME}}
spec:
ports:
- protocol: TCP
port: {{.PORT}}
targetPort: {{.PORT}}
selector:
app: {{.COMPONENT_NAME}}
type: ClusterIP
sessionAffinity: None
---
kind: Route
apiVersion: route.openshift.io/v1
metadata:
name: {{.COMPONENT_NAME}}
annotations:
openshift.io/host.generated: 'true'
spec:
to:
kind: Service
name: {{.COMPONENT_NAME}}
weight: 100
port:
targetPort: {{.PORT}}
wildcardPolicy: None

This example is using variables for tools to replace some of the info during deployment.

#### Example of Operator deployment manifest:

apiVersion: app.stacks/v1beta1
kind: RuntimeComponent
metadata:
name: {{.COMPONENT_NAME}}
spec:
applicationImage: {{.CONTAINER_IMAGE}}
service:
type: ClusterIP
port: {{.PORT}}
expose: true
storage:
size: 2Gi
mountPath: "/logs"
Loading