mirror of https://github.com/knative/docs.git
Commit API spec and design doc (#444)
Initial copy/commit of Elafros API design doc, chunked into sections. Also includes initial definitions of Elafros personas. Still TODO: - Appendices A-E, which cover extended use cases.
This commit is contained in:
parent
31c72e9a24
commit
4ee69f36ed
|
@ -0,0 +1,96 @@
|
|||
# Elafros Personas
|
||||
|
||||
When discussing user actions, it is often helpful to [define specific
|
||||
user roles](https://en.wikipedia.org/wiki/Persona_(user_experience)) who
|
||||
might want to do the action.
|
||||
|
||||
|
||||
## Elafros Compute
|
||||
|
||||
### Developer Personas
|
||||
|
||||
The developer personas are software engineers looking to build and run
|
||||
a stateless application without concern about the underlying
|
||||
infrastructure.
|
||||
|
||||
* Hobbyist
|
||||
* Backend SWE
|
||||
* Full stack SWE
|
||||
* SRE
|
||||
|
||||
User stories:
|
||||
* Deploy some code
|
||||
* Update environment
|
||||
* Roll back the last change
|
||||
* Debug an error in code
|
||||
* Monitor my application
|
||||
|
||||
### Operator Personas
|
||||
|
||||
* Hobbyist / Contributor
|
||||
* Cluster administrator
|
||||
* Security Engineer / Auditor
|
||||
* Capacity Planner
|
||||
|
||||
User stories:
|
||||
* Create an Elafros cluster
|
||||
* Apply policy / RBAC
|
||||
* Control or charge back for resource usage
|
||||
* Choose logging or monitoring plugins
|
||||
|
||||
|
||||
## Elafros Build
|
||||
|
||||
We expect the build components of Elafros to be useful on their own,
|
||||
as well as in conjunction with the compute components.
|
||||
|
||||
### Developer
|
||||
|
||||
User stories:
|
||||
* Start a build
|
||||
* Read build logs
|
||||
|
||||
### Language operator / contributor
|
||||
|
||||
User stories:
|
||||
* Create a build image / build pack
|
||||
|
||||
|
||||
## Elafros Events
|
||||
|
||||
Event generation and consumption is a core part of the serverless
|
||||
(particularly function as a service) computing model. Event generation
|
||||
and dispatch enables decoupling of event producers from consumers.
|
||||
|
||||
## Event consumer (developer)
|
||||
|
||||
User stories:
|
||||
* Determine what event sources are available
|
||||
* Trigger my service when certain events happen (event binding)
|
||||
* Filter events from a provider
|
||||
|
||||
## Event producer
|
||||
|
||||
User stories:
|
||||
* Publish events
|
||||
* Control who can bind events
|
||||
|
||||
|
||||
## Contributors
|
||||
|
||||
Contributors are an important part of the Elafros project. As such, we
|
||||
will also consider how various infrastructure encourages and enables
|
||||
contributors to the project, as well as the impact on end-users.
|
||||
|
||||
* Hobbyist or newcomer
|
||||
* Motivated user
|
||||
* Corporate (employed) maintainer
|
||||
* Consultant
|
||||
|
||||
User stories:
|
||||
* Check out the code
|
||||
* Build and run the code
|
||||
* Run tests
|
||||
* View test status
|
||||
* Run performance tests
|
||||
|
|
@ -0,0 +1,322 @@
|
|||
# Error Conditions and Reporting
|
||||
|
||||
Elafros uses the standard Kubernetes API pattern for reporting
|
||||
configuration errors and current state of the system by writing the
|
||||
report in the `status` section. There are two mechanisms commonly used
|
||||
in status:
|
||||
|
||||
* conditions represent true/false statements about the current state
|
||||
of the resource.
|
||||
|
||||
* other fields may provide status on the most recently retrieved state
|
||||
of the system as it relates to the resource (example: number of
|
||||
replicas or traffic assignments).
|
||||
|
||||
Both of these mechanisms often include additional data from the
|
||||
controller such as `observedGeneration` (to determine whether the
|
||||
controller has seen the latest updates to the spec). Example user and
|
||||
system error scenarios are included below along with how the status is
|
||||
presented to CLI and UI tools via the API.
|
||||
|
||||
* [Revision failed to become Ready](#revision-failed-to-become-ready)
|
||||
* [Build failed](#build-failed)
|
||||
* [Revision not found by Route](#revision-not-found-by-route)
|
||||
* [Configuration not found by Route](#configuration-not-found-by-route)
|
||||
* [Latest Revision of a Configuration deleted](#latest-revision-of-a-configuration-deleted)
|
||||
* [Resource exhausted while creating a revision](#resource-exhausted-while-creating-a-revision)
|
||||
* [Deployment progressing slowly/stuck](#deployment-progressing-slowly-stuck)
|
||||
* [Traffic shift progressing slowly/stuck](#traffic-shift-progressing-slowly-stuck)
|
||||
* [Container image not present in repository](#container-image-not-present-in-repository)
|
||||
* [Container image fails at startup on Revision](#container-image-fails-at-startup-on-revision)
|
||||
|
||||
|
||||
## Revision failed to become Ready
|
||||
|
||||
If the latest Revision fails to become `Ready` for any reason within some reasonable
|
||||
timeframe, the Configuration should signal this
|
||||
with the `LatestRevisionReady` status, copying the reason and the message
|
||||
from the `Ready` condition on the Revision.
|
||||
|
||||
```yaml
|
||||
...
|
||||
status:
|
||||
latestReadyRevisionName: abc
|
||||
latestCreatedRevisionName: bcd # Hasn't become "Ready"
|
||||
conditions:
|
||||
- type: LatestRevisionReady
|
||||
status: False
|
||||
reason: ContainerMissing
|
||||
message: "Unable to start because container is missing and build failed."
|
||||
```
|
||||
|
||||
|
||||
## Build failed
|
||||
|
||||
If the Build steps failed while creating a Revision, you can examine
|
||||
the `Failed` condition on the Build or the `BuildFailed` condition on
|
||||
the Revision (which copies the value from the build referenced by
|
||||
`spec.buildName`). In addition, the Build resource (but not the
|
||||
Revision) should have a status field to link to the log output of the
|
||||
build.
|
||||
|
||||
```http
|
||||
GET /apis/build.dev/v1alpha1/namespaces/default/builds/build-1acub3
|
||||
```
|
||||
```yaml
|
||||
...
|
||||
status:
|
||||
# Link to log stream; could be ELK or Stackdriver, for example
|
||||
buildLogsLink: "http://logging.infra.mycompany.com/...?filter=..."
|
||||
conditions:
|
||||
- type: Failed
|
||||
status: True
|
||||
reason: BuildStepFailed # could also be SourceMissing, etc
|
||||
# reason is a short status, message provides error details
|
||||
message: "Step XYZ failed with error message: $LASTLOGLINE"
|
||||
```
|
||||
|
||||
|
||||
```http
|
||||
GET /apis/elafros.dev/v1alpha1/namespaces/default/revisions/abc
|
||||
```
|
||||
```yaml
|
||||
...
|
||||
status:
|
||||
conditions:
|
||||
- type: Ready
|
||||
status: False
|
||||
reason: ContainerMissing
|
||||
message: "Unable to start because container is missing and build failed."
|
||||
- type: BuildFailed
|
||||
status: True
|
||||
reason: BuildStepFailed
|
||||
# reason is a short status, message provides error details
|
||||
message: "Step XYZ failed with error message: $LASTLOGLINE"
|
||||
```
|
||||
|
||||
|
||||
## Revision not found by Route
|
||||
|
||||
If a Revision is referenced in the Route's `spec.rollout.traffic`, the
|
||||
corresponding entry in the `status.traffic` list will be set to "Not
|
||||
found", and the `TrafficDropped` condition will be marked as True,
|
||||
with a reason of `RevisionMissing`.
|
||||
|
||||
```http
|
||||
GET /apis/elafros.dev/v1alpha1/namespaces/default/routes/abc
|
||||
```
|
||||
```yaml
|
||||
...
|
||||
status:
|
||||
traffic:
|
||||
- revisionName: abc
|
||||
name: current
|
||||
percent: 100
|
||||
- revisionName: "Not found"
|
||||
name: next
|
||||
percent: 0
|
||||
conditions:
|
||||
- type: RolloutInProgress
|
||||
status: False
|
||||
- type: TrafficDropped
|
||||
status: True
|
||||
reason: RevisionMissing
|
||||
# reason is a short status, message provides error details
|
||||
message: "Revision 'qyzz' referenced in rollout.traffic not found"
|
||||
```
|
||||
|
||||
|
||||
## Configuration not found by Route
|
||||
|
||||
If a Route references the `latestReadyRevisionName` of a Configuration
|
||||
and the Configuration cannot be found, the corresponding entry in
|
||||
`status.traffic` list will be set to "Not found", and the
|
||||
`TrafficDropped` condition will be marked as True with a reason of
|
||||
`ConfigurationMissing`.
|
||||
|
||||
```http
|
||||
GET /apis/elafros.dev/v1alpha1/namespaces/default/routes/abc
|
||||
```
|
||||
```yaml
|
||||
...
|
||||
status:
|
||||
traffic:
|
||||
- revisionName: "Not found"
|
||||
percent: 100
|
||||
conditions:
|
||||
- type: RolloutInProgress
|
||||
status: False
|
||||
- type: TrafficDropped
|
||||
status: True
|
||||
reason: ConfigurationMissing
|
||||
# reason is a short status, message provides error details
|
||||
message: "Revision 'my-service' referenced in rollout.traffic not found"
|
||||
```
|
||||
|
||||
|
||||
## Latest Revision of a Configuration deleted
|
||||
|
||||
If the most recent (or most recently ready) Revision is deleted, the
|
||||
Configuration will clear the `latestReadyRevisionName`. If the
|
||||
Configuration is referenced by a Route, the Route will set the
|
||||
`TrafficDropped` condition with reason `RevisionMissing`, as above.
|
||||
|
||||
```http
|
||||
GET /apis/elafros.dev/v1alpha1/namespaces/default/configurations/my-service
|
||||
```
|
||||
```yaml
|
||||
...
|
||||
metadata:
|
||||
generation: 1234 # only updated when spec changes
|
||||
...
|
||||
spec:
|
||||
...
|
||||
status:
|
||||
latestCreatedRevision: abc
|
||||
conditions:
|
||||
- type: LatestRevisionReady
|
||||
status: False
|
||||
reason: RevisionMissing
|
||||
message: "The latest Revision appears to have been deleted."
|
||||
observedGeneration: 1234
|
||||
```
|
||||
|
||||
|
||||
## Resource exhausted while creating a revision
|
||||
|
||||
Since a Revision is only metadata, the Revision will be created, but
|
||||
will have a condition indicating the underlying failure, possibly
|
||||
indicating the failed underlying resource. In a multitenant
|
||||
environment, the customer might not have have access or visibility
|
||||
into the underlying resources in the hosting environment.
|
||||
|
||||
```http
|
||||
GET /apis/elafros.dev/v1alpha1/namespaces/default/revisions/abc
|
||||
```
|
||||
```yaml
|
||||
...
|
||||
status:
|
||||
conditions:
|
||||
- type: Ready
|
||||
status: False
|
||||
reason: NoDeployment
|
||||
message: "The controller could not create a deployment named ela-abc-e13ac."
|
||||
```
|
||||
|
||||
|
||||
## Deployment progressing slowly/stuck
|
||||
|
||||
See
|
||||
[the kubernetes documentation for how this is handled for Deployments](https://kubernetes.io/docs/concepts/workloads/controllers/deployment/#failed-deployment). For
|
||||
Revisions, we will start by assuming a single timeout for deployment
|
||||
(rather than configurable), and report that the Revision was not
|
||||
Ready, with a reason `ProgressDeadlineExceeded`. Note that we will
|
||||
only report `ProgressDeadlineExceeded` if we could not determine
|
||||
another reason (such as quota failures, missing build, or container
|
||||
execution failures).
|
||||
|
||||
Kubernetes controllers will continue attempting to make progress
|
||||
(possibly at a less-aggressive rate) when they encounter a case where
|
||||
the desired status cannot match the actual status, so if the
|
||||
underlying deployment is slow, it might eventually finish after
|
||||
reporting `ProgressDeadlineExceeded`.
|
||||
|
||||
```http
|
||||
GET /apis/elafros.dev/v1alpha1/namespaces/default/revisions/abc
|
||||
```
|
||||
```yaml
|
||||
...
|
||||
status:
|
||||
conditions:
|
||||
- type: Ready
|
||||
status: False
|
||||
reason: ProgressDeadlineExceeded
|
||||
message: "Unable to create pods for more than 120 seconds."
|
||||
```
|
||||
|
||||
|
||||
## Traffic shift progressing slowly/stuck
|
||||
|
||||
Similar to deployment slowness, if the transfer of traffic (either via
|
||||
gradual or abrupt rollout) takes longer than a certain timeout to
|
||||
complete/update, the `RolloutInProgress` condition will remain at
|
||||
True, but the reason will be set to `ProgressDeadlineExceeded`.
|
||||
|
||||
```http
|
||||
GET /apis/elafros.dev/v1alpha1/namespaces/default/routes/abc
|
||||
```
|
||||
```yaml
|
||||
...
|
||||
status:
|
||||
traffic:
|
||||
- revisionName: abc
|
||||
percent: 75
|
||||
- revisionName: def
|
||||
percent: 25
|
||||
conditions:
|
||||
- type: RolloutInProgress
|
||||
status: True
|
||||
reason: ProgressDeadlineExceeded
|
||||
# reason is a short status, message provides error details
|
||||
message: "Unable to update traffic split for more than 120 seconds."
|
||||
```
|
||||
|
||||
|
||||
## Container image not present in repository
|
||||
|
||||
Revisions might be created while a Build is still creating the
|
||||
container image or uploading it to the repository. If the build is
|
||||
being performed by a CRD in the cluster, the spec.buildName attribute
|
||||
will be set (and see the [Build failed](#build-failed) example). In
|
||||
other cases when the build is not supplied, the container image
|
||||
referenced might not be present in the registry (either because of a
|
||||
typo or because it was deleted). In this case, the Ready condition
|
||||
will be set to False with a reason of ContainerMissing. This condition
|
||||
could be corrected if the image becomes available at a later time. We
|
||||
can also make a defensive copy of the container image to avoid this
|
||||
error due to deleted source container.
|
||||
|
||||
```http
|
||||
GET /apis/elafros.dev/v1alpha1/namespaces/default/revisions/abc
|
||||
```
|
||||
```yaml
|
||||
...
|
||||
status:
|
||||
conditions:
|
||||
- type: Ready
|
||||
status: False
|
||||
reason: ContainerMissing
|
||||
message: "Unable to fetch image 'gcr.io/...': <literal error>"
|
||||
- type: Failed
|
||||
status: True
|
||||
reason: ContainerMissing
|
||||
message: "Unable to fetch image 'gcr.io/...': <literal error>"
|
||||
```
|
||||
|
||||
|
||||
## Container image fails at startup on Revision
|
||||
|
||||
Particularly for development cases with interpreted languages like
|
||||
Node or Python, syntax errors or the like might only be caught at
|
||||
container startup time. For this reason, implementations may choose to
|
||||
start a single copy of the container on deployment, before making the
|
||||
container Ready. If the initial container fails to start, the `Ready`
|
||||
condition will be set to False and the reason will be set to
|
||||
`ExitCode:%d` with the exit code of the application, and the last line
|
||||
of output in the message. Additionally, the Revision will include a
|
||||
`logsUrl` which provides the address of an endpoint which can be used to
|
||||
fetch the logs for the failed process.
|
||||
|
||||
```http
|
||||
GET /apis/elafros.dev/v1alpha1/namespaces/default/revisions/abc
|
||||
```
|
||||
```yaml
|
||||
...
|
||||
status:
|
||||
logUrl: "http://logging.infra.mycompany.com/...?filter=revision=abc&..."
|
||||
conditions:
|
||||
- type: Ready
|
||||
status: False
|
||||
reason: ExitCode:127
|
||||
message: "Container failed with: SyntaxError: Unexpected identifier"
|
||||
```
|
Binary file not shown.
After Width: | Height: | Size: 13 KiB |
Binary file not shown.
After Width: | Height: | Size: 14 KiB |
Binary file not shown.
After Width: | Height: | Size: 14 KiB |
Binary file not shown.
After Width: | Height: | Size: 11 KiB |
Binary file not shown.
After Width: | Height: | Size: 17 KiB |
Binary file not shown.
After Width: | Height: | Size: 11 KiB |
|
@ -0,0 +1,22 @@
|
|||
The goal of the Elafros project is to provide a common toolkit and API
|
||||
framework for serverless workloads.
|
||||
|
||||
We define serverless workloads as computing workloads that are:
|
||||
|
||||
* Stateless
|
||||
* Amenable to the process scale-out model
|
||||
* Primarily driven by application level (L7 -- HTTP, for example)
|
||||
request traffic
|
||||
|
||||
While Kubernetes provides basic primitives like Deployment, Service,
|
||||
and Ingress in support of this model, our experience suggests that a
|
||||
more compact and richer opinionated model has substantial benefit for
|
||||
developers. In particular, by standardizing on higher-level primitives
|
||||
which perform substantial amounts of automation of common
|
||||
infrastructure, it should be possible to build consistent toolkits
|
||||
that provide a richer experience than updating yaml files with
|
||||
`kubectl`.
|
||||
|
||||
The Elafros APIs consist of Compute API (these documents),
|
||||
[Build API](https://github.com/elafros/build) and
|
||||
[Eventing API](https://github.com/elafros/eventing).
|
|
@ -0,0 +1,929 @@
|
|||
# Sample API Usage
|
||||
|
||||
Following are several normative sample scenarios utilizing the Elafros
|
||||
API. These scenarios are arranged to provide a flavor of the API and
|
||||
building from the smallest, most frequent operations.
|
||||
|
||||
Examples in this section illustrate:
|
||||
|
||||
* [Automatic rollout of a new Revision to an existing Service with a
|
||||
pre-built container](#1--automatic-rollout-of-a-new-revision-to-existing-service---pre-built-container)
|
||||
* [Creating a first route to deploy a first revision from a pre-built
|
||||
container](#2--creating-route-and-deploying-first-revision---pre-built-container)
|
||||
* [Configuration changes and manual rollout
|
||||
options](#3--manual-rollout-of-a-new-revision---config-change-only)
|
||||
* [Creating a revision from source](#4--deploy-a-revision-from-source)
|
||||
* [Creating a function from source](#5--deploy-a-function)
|
||||
|
||||
Note that these API operations are identical for both app and function
|
||||
based services. (to see the full resource definitions, see the
|
||||
[Resource YAML Definitions](spec.md)).
|
||||
|
||||
CLI samples are for illustrative purposes, and not intended to
|
||||
represent final CLI design.
|
||||
|
||||
## 1) Automatic rollout of a new Revision to existing Service - pre-built container
|
||||
|
||||
**_Scenario_**: User deploys a new revision to an existing service
|
||||
with a new container image, rolling out automatically to 100%
|
||||
|
||||
```
|
||||
$ elafros deploy --service my-service
|
||||
Deploying app to service [my-service]:
|
||||
✓ Starting
|
||||
✓ Promoting
|
||||
Done.
|
||||
Deployed to https://my-service.default.mydomain.com
|
||||
```
|
||||
|
||||
**Steps**:
|
||||
|
||||
* Update the Configuration with the config change
|
||||
|
||||
**Results:**
|
||||
|
||||
* A new Revision is created, and automatically rolled out to 100% once
|
||||
ready
|
||||
|
||||

|
||||
|
||||
|
||||
After the initial Route and Configuration have been created (which is
|
||||
shown in the [second example](TODO)), the typical
|
||||
interaction is to update the revision configuration, resulting in the
|
||||
creation of a new revision, which will be automatically rolled out by
|
||||
the route. Revision configuration updates can be handled as either a
|
||||
PUT or PATCH operation:
|
||||
|
||||
* Optimistic concurrency controls for PUT operations in a
|
||||
read/modify/write routine work as expected in kubernetes.
|
||||
|
||||
* PATCH semantics should work as expected in kubernetes, but may have
|
||||
some limitations imposed by CRDs at the moment.
|
||||
|
||||
In this and following examples PATCH is used. Revisions can be built
|
||||
from source, which results in a container image, or by directly
|
||||
supplying a pre-built container, which this first scenario
|
||||
illustrates. The example demonstrates the PATCH issued by the client,
|
||||
followed by several GET calls to illustrate each step in the
|
||||
reconciliation process as the system materializes the new revision,
|
||||
and begins shifting traffic from the old revision to the new revision.
|
||||
|
||||
The client PATCHes the configuration's template revision with just the
|
||||
new container image, inheriting previous configuration from the
|
||||
configuration:
|
||||
|
||||
```http
|
||||
PATCH /apis/elafros.dev/v1alpha1/namespaces/default/configurations/my-service
|
||||
```
|
||||
```yaml
|
||||
apiVersion: elafros.dev/v1alpha1
|
||||
kind: Configuration
|
||||
metadata:
|
||||
name: my-service # by convention, same name as the service
|
||||
spec:
|
||||
revisionTemplate: # template for building Revision
|
||||
spec:
|
||||
container:
|
||||
image: gcr.io/... # new image
|
||||
```
|
||||
|
||||
The update to the Configuration triggers a new revision being created,
|
||||
and the Configuration is updated to reflect the new Revision:
|
||||
|
||||
```http
|
||||
GET /apis/elafros.dev/v1alpha1/namespaces/default/configurations/my-service
|
||||
```
|
||||
```yaml
|
||||
apiVersion: elafros.dev/v1alpha1
|
||||
kind: Configuration
|
||||
metadata:
|
||||
name: my-service
|
||||
generation: 1235
|
||||
...
|
||||
|
||||
spec:
|
||||
... # same as before, except new container.image
|
||||
status:
|
||||
latestReadyRevisionName: abc
|
||||
latestCreatedRevisionName: def # new revision created, but not ready yet
|
||||
observedGeneration: 1235
|
||||
```
|
||||
|
||||
The newly created revision has the same config as the previous
|
||||
revision, but different code. Note the generation label reflects the
|
||||
new generation of the configuration (1235), indicating the provenance
|
||||
of the revision:
|
||||
|
||||
```http
|
||||
GET /apis/elafros.dev/v1alpha1/namespaces/default/revisions/def
|
||||
```
|
||||
```yaml
|
||||
apiVersion: elafros.dev/v1alpha1
|
||||
kind: Revision
|
||||
metadata:
|
||||
name: def
|
||||
labels:
|
||||
elafros.dev/configuration: my-service
|
||||
elafros.dev/configurationGeneration: 1235
|
||||
...
|
||||
spec:
|
||||
container: # k8s core.v1.Container
|
||||
image: gcr.io/... # new container
|
||||
# same config as previous revision
|
||||
env:
|
||||
- name: FOO
|
||||
value: bar
|
||||
- name: HELLO
|
||||
value: blurg
|
||||
...
|
||||
status:
|
||||
conditions:
|
||||
- type: Ready
|
||||
status: True
|
||||
```
|
||||
|
||||
When the new revision is Ready, i.e. underlying resources are
|
||||
materialized and ready to serve, the configuration updates its
|
||||
`status.latestReadyRevisionName` status to reflect the new
|
||||
revision. The route, which is configured to automatically rollout new
|
||||
revisions from the configuration, watches the configuration and is
|
||||
notified of the `latestReadyRevisionName`, and begins migrating traffic
|
||||
to it. During reconciliation, traffic may be routed to both existing
|
||||
revision `abc` and new revision `def`:
|
||||
|
||||
```http
|
||||
GET /apis/elafros.dev/v1alpha1/namespaces/default/routes/my-service
|
||||
```
|
||||
```yaml
|
||||
apiVersion: elafros.dev/v1alpha1
|
||||
kind: Route
|
||||
metadata:
|
||||
name: my-service
|
||||
...
|
||||
spec:
|
||||
rollout:
|
||||
traffic:
|
||||
- configurationName: my-service
|
||||
percent: 100
|
||||
|
||||
status:
|
||||
# domain:
|
||||
# oss: my-service.namespace.mydomain.com
|
||||
domain: my-service.namespace.mydomain.com
|
||||
# percentages add to 100
|
||||
traffic: # in status, all configurationName refs are dereferenced
|
||||
- revisionName: abc
|
||||
percent: 75
|
||||
- revisionName: def
|
||||
percent: 25
|
||||
conditions:
|
||||
- type: RolloutComplete
|
||||
status: False
|
||||
```
|
||||
|
||||
And once reconciled, revision def serves 100% of the traffic :
|
||||
|
||||
```http
|
||||
GET /apis/elafros.dev/v1alpha1/namespaces/default/routes/my-service
|
||||
```
|
||||
```yaml
|
||||
apiVersion: elafros.dev/v1alpha1
|
||||
kind: Route
|
||||
metadata:
|
||||
name: my-service
|
||||
...
|
||||
spec:
|
||||
rollout:
|
||||
traffic:
|
||||
- configurationName: my-service
|
||||
percent: 100
|
||||
status:
|
||||
domain: my-service.default.mydomain.com
|
||||
traffic:
|
||||
- revisionName: def
|
||||
percent: 100
|
||||
conditions:
|
||||
- type: RolloutComplete
|
||||
status: True
|
||||
...
|
||||
```
|
||||
|
||||
|
||||
## 2) Creating Route and deploying first Revision - pre-built container
|
||||
|
||||
**Scenario**: User creates a new Route and deploys their first
|
||||
Revision based on a pre-built container
|
||||
|
||||
```
|
||||
$ elafros deploy --service my-service --region us-central1
|
||||
✓ Creating service [my-service] in region [us-central1]
|
||||
Deploying app to service [my-service]:
|
||||
✓ Uploading [=================]
|
||||
✓ Starting
|
||||
✓ Promoting
|
||||
Done.
|
||||
Deployed to https://my-service.default.mydomain.com
|
||||
```
|
||||
|
||||
**Steps**:
|
||||
|
||||
* Create a new Configuration and a Route that references a that
|
||||
configuration.
|
||||
|
||||
**Results**:
|
||||
|
||||
* A new Configuration is created, and generates a new Revision based
|
||||
on the configuration
|
||||
|
||||
* A new Route is created, referencing the configuration
|
||||
|
||||
* The route begins serving traffic to the revision that was created by
|
||||
the configuration
|
||||
|
||||

|
||||
|
||||
|
||||
The previous example assumed an existing Route and Configuration to
|
||||
illustrate the common scenario of updating the configuration to deploy
|
||||
a new revision to the service.
|
||||
|
||||
In this getting started example, deploying a first Revision is
|
||||
accomplished by creating a new Configuration (which will generate a
|
||||
new Revision) and creating a new Route referring to that
|
||||
configuration. Note that these two steps can occur in either order, or
|
||||
in parallel.
|
||||
|
||||
A Route can either refer directly to a Revision, or to the latest
|
||||
ready revision of a Configuration, as this example illustrates. This
|
||||
is the most straightforward scenario that many Elafros customers are
|
||||
expected to use, and is consistent with the experience of deploying
|
||||
code that is rolled out immediately.
|
||||
|
||||
The example shows the POST calls issued by the client, followed by
|
||||
several GET calls to illustrate each step in the reconciliation
|
||||
process as the system materializes and begins routing traffic to the
|
||||
revision.
|
||||
|
||||
The client creates the route and configuration, which by convention
|
||||
share the same name:
|
||||
|
||||
```http
|
||||
POST /apis/elafros.dev/v1alpha1/namespaces/default/routes
|
||||
```
|
||||
```yaml
|
||||
apiVersion: elafros.dev/v1alpha1
|
||||
kind: Route
|
||||
metadata:
|
||||
name: my-service
|
||||
spec:
|
||||
rollout:
|
||||
traffic:
|
||||
- configurationName: my-service # named reference to Configuration
|
||||
percent: 100 # automatically activate new Revisions from the configuration
|
||||
```
|
||||
|
||||
```http
|
||||
POST /apis/elafros.dev/v1alpha1/namespaces/default/configurations
|
||||
```
|
||||
```yaml
|
||||
apiVersion: elafros.dev/v1alpha1
|
||||
kind: Configuration
|
||||
metadata:
|
||||
name: my-service # By convention (not req'd), same name as the service.
|
||||
# This will also be set as the "elafros.dev/configuration"
|
||||
# label on the created Revision.
|
||||
spec:
|
||||
revisionTemplate: # template for building Revision
|
||||
metadata: ...
|
||||
spec:
|
||||
container: # k8s core.v1.Container
|
||||
image: gcr.io/...
|
||||
env:
|
||||
- name: FOO
|
||||
value: bar
|
||||
- name: HELLO
|
||||
value: world
|
||||
...
|
||||
```
|
||||
|
||||
Upon the creation of the configuration, the system will create a new
|
||||
Revision, generating its name, and applying the spec and metadata from
|
||||
the configuration, as well as new metadata labels:
|
||||
|
||||
```http
|
||||
GET /apis/elafros.dev/v1alpha1/namespaces/default/revisions/abc
|
||||
```
|
||||
```yaml
|
||||
apiVersion: elafros.dev/v1alpha1
|
||||
kind: Revision
|
||||
metadata:
|
||||
name: abc # generated name
|
||||
labels:
|
||||
# name and generation of the configuration that created the revision
|
||||
elafros.dev/configuration: my-service
|
||||
elafros.dev/configurationGeneration: 1234
|
||||
... # uid, resourceVersion, creationTimestamp, generation, selfLink, etc
|
||||
spec:
|
||||
... # spec from the configuration
|
||||
status:
|
||||
conditions:
|
||||
- type: Ready
|
||||
status: False
|
||||
message: "Starting Instances"
|
||||
```
|
||||
|
||||
Immediately after the revision is created, i.e. before underlying
|
||||
resources have been fully materialized, the configuration is updated
|
||||
with latestCreatedRevisionName:
|
||||
|
||||
```http
|
||||
GET /apis/elafros.dev/v1alpha1/namespaces/default/configurations/my-service
|
||||
```
|
||||
```yaml
|
||||
apiVersion: elafros.dev/v1alpha1
|
||||
kind: Configuration
|
||||
metadata:
|
||||
name: my-service
|
||||
generation: 1234
|
||||
... # uid, resourceVersion, creationTimestamp, selfLink, etc
|
||||
spec:
|
||||
... # same as before
|
||||
status:
|
||||
# latest created revision, may not have materialized yet
|
||||
latestCreatedRevisionName: abc
|
||||
observedGeneration: 1234
|
||||
```
|
||||
|
||||
The configuration watches the revision, and when the revision is
|
||||
updated as Ready (to serve), the latestReadyRevisionName is updated:
|
||||
|
||||
```http
|
||||
GET /apis/elafros.dev/v1alpha1/namespaces/default/configurations/my-service
|
||||
```
|
||||
```yaml
|
||||
apiVersion: elafros.dev/v1alpha1
|
||||
kind: Configuration
|
||||
metadata:
|
||||
name: my-service
|
||||
generation: 1234
|
||||
...
|
||||
spec:
|
||||
... # same as before
|
||||
status:
|
||||
# the latest created and ready to serve. Watched by service
|
||||
latestReadyRevisionName: abc
|
||||
# latest created revision
|
||||
latestCreatedRevisionName: abc
|
||||
observedGeneration: 1234
|
||||
```
|
||||
|
||||
The route, which watches the configuration `my-service`, observes the
|
||||
change to `latestReadyRevisionName` and begins routing traffic to the
|
||||
new revision `abc`, addressable as
|
||||
`my-service.default.mydomain.com`. Once reconciled:
|
||||
|
||||
```http
|
||||
GET /apis/elafros.dev/v1alpha1/namespaces/default/routes/my-service
|
||||
```
|
||||
```yaml
|
||||
apiVersion: elafros.dev/v1alpha1
|
||||
kind: Route
|
||||
metadata:
|
||||
name: my-service
|
||||
generation: 2145
|
||||
...
|
||||
spec:
|
||||
rollout:
|
||||
traffic:
|
||||
- configurationName: my-service
|
||||
percent: 100
|
||||
|
||||
status:
|
||||
domain: my-service.default.mydomain.com
|
||||
|
||||
traffic: # in status, all configurationName refs are dereferenced to latest revision
|
||||
- revisionName: abc # latestReadyRevisionName from configurationName in spec
|
||||
percent: 100
|
||||
|
||||
conditions:
|
||||
- type: RolloutComplete
|
||||
status: True
|
||||
|
||||
observedGeneration: 2145
|
||||
```
|
||||
|
||||
|
||||
## 3) Manual rollout of a new Revision - config change only
|
||||
|
||||
**_Scenario_**: User updates configuration with new configuration (env
|
||||
var change) to an existing service, tests the revision, then
|
||||
proceeds with a manually controlled rollout to 100%
|
||||
|
||||
```
|
||||
$ elafros rollout strategy manual
|
||||
|
||||
$ elafros deploy --service my-service --env HELLO="blurg"
|
||||
[...]
|
||||
|
||||
$ elafros revisions list --service my-service
|
||||
Name Traffic Id Date Deployer Git SHA
|
||||
next 0% v3 2018-01-19 12:16 user1 a6f92d1
|
||||
current 100% v2 2018-01-18 20:34 user1 a6f92d1
|
||||
v1 2018-01-17 10:32 user1 33643fc
|
||||
|
||||
$ elafros rollout next percent 5
|
||||
[...]
|
||||
$ elafros rollout next percent 50
|
||||
[...]
|
||||
$ elafros rollout finish
|
||||
[...]
|
||||
|
||||
$ elafros revisions list --service my-service
|
||||
Name Traffic Id Date Deployer Git SHA
|
||||
current,next 100% v3 2018-01-19 12:16 user1 a6f92d1
|
||||
v2 2018-01-18 20:34 user1 a6f92d1
|
||||
v1 2018-01-17 10:32 user1 33643fc
|
||||
```
|
||||
|
||||
**Steps**:
|
||||
|
||||
* Update the Route to pin the current revision
|
||||
|
||||
* Update the Configuration with the new configuration (env var)
|
||||
|
||||
* Update the Route to address the new Revision
|
||||
|
||||
* After testing the new revision through the named subdomain, proceed
|
||||
with the rollout, incrementally increasing traffic to 100%
|
||||
|
||||
**Results:**
|
||||
|
||||
* The system creates the new revision from the configuration,
|
||||
addressable at next.my-service... (by convention), but traffic is
|
||||
not routed to it until the percentage is manually ramped up. Upon
|
||||
completing the rollout, the next revision is now the current
|
||||
revision
|
||||
|
||||

|
||||
|
||||
|
||||
In the previous examples, the route referenced a Configuration for
|
||||
automatic rollouts of new Revisions. While this pattern is useful for
|
||||
many scenarios such as functions-as-a-service and simple development
|
||||
flows, the Route can also reference Revisions directly to "pin"
|
||||
traffic to specific revisions, which is suitable for manually
|
||||
controlling rollouts, i.e. testing a new revision prior to serving
|
||||
traffic. (Note: see [Appendix B](complex_examples.md) for a
|
||||
semi-automatic variation of manual rollouts).
|
||||
|
||||
The client updates the route to pin the current revision:
|
||||
|
||||
```http
|
||||
PATCH /apis/elafros.dev/v1alpha1/namespaces/default/routes/my-service
|
||||
```
|
||||
```yaml
|
||||
apiVersion: elafros.dev/v1alpha1
|
||||
kind: Route
|
||||
metadata:
|
||||
name: my-service
|
||||
spec:
|
||||
rollout:
|
||||
traffic:
|
||||
- revisionName: def # pin a specific revision, i.e. the current one
|
||||
percent: 100
|
||||
```
|
||||
|
||||
As in the previous example, the configuration is updated to trigger
|
||||
the creation of a new revision, in this case updating the container
|
||||
image but keeping the same config:
|
||||
|
||||
```http
|
||||
PATCH /apis/elafros.dev/v1alpha1/namespaces/default/configurations/my-service
|
||||
```
|
||||
```yaml
|
||||
apiVersion: elafros.dev/v1alpha1
|
||||
kind: Configuration
|
||||
metadata:
|
||||
name: my-service
|
||||
spec:
|
||||
revisionTemplate:
|
||||
spec:
|
||||
container:
|
||||
env: # k8s-style strategic merge patch, updating a single list value
|
||||
- name: HELLO
|
||||
value: blurg # changed value
|
||||
```
|
||||
|
||||
A new revision `ghi` is created that has the same code as the previous
|
||||
revision `def`, but different config:
|
||||
|
||||
```http
|
||||
GET /apis/elafros.dev/v1alpha1/namespaces/default/revisions/ghi
|
||||
```
|
||||
```yaml
|
||||
apiVersion: elafros.dev/v1alpha1
|
||||
kind: Revision
|
||||
metadata:
|
||||
name: ghi
|
||||
...
|
||||
spec:
|
||||
container:
|
||||
image: gcr.io/... # same container as previous revision abc
|
||||
env:
|
||||
- name: FOO
|
||||
value: bar
|
||||
- name: HELLO
|
||||
value: blurg # changed value
|
||||
...
|
||||
status:
|
||||
conditions:
|
||||
- type: Ready
|
||||
status: True
|
||||
```
|
||||
|
||||
Even when ready, the new revision does not automatically start serving
|
||||
traffic, as the route was pinned to revision `def`.
|
||||
|
||||
Update the route to make the existing revision serving traffic
|
||||
addressable through subdomain `current`, and referencing the new
|
||||
revision at 0% traffic but making it addressable through subdomain
|
||||
`next`:
|
||||
|
||||
```http
|
||||
PATCH /apis/elafros.dev/v1alpha1/namespaces/default/routes/my-service
|
||||
```
|
||||
```yaml
|
||||
apiVersion: elafros.dev/v1alpha1
|
||||
kind: Route
|
||||
metadata:
|
||||
name: my-service
|
||||
spec:
|
||||
rollout:
|
||||
traffic:
|
||||
- revisionName: def
|
||||
name: current # addressable as current.my-service.default.mydomain.com
|
||||
percent: 100
|
||||
- revisionName: ghi
|
||||
name: next # addressable as next.my-service.default.mydomain.com
|
||||
percent: 0 # no traffic yet
|
||||
```
|
||||
|
||||
In this state, the route makes both revisions addressable with
|
||||
subdomains `current` and `next` (once the revision `ghi` has a status of
|
||||
Ready), but traffic has not shifted to next yet. Also note that while
|
||||
the names current/next have semantic meaning, they are convention
|
||||
only; blue/green, or any other subdomain names could be configured.
|
||||
|
||||
```http
|
||||
GET /apis/elafros.dev/v1alpha1/namespaces/default/routes/my-service
|
||||
```
|
||||
```yaml
|
||||
apiVersion: elafros.dev/v1alpha1
|
||||
kind: Route
|
||||
metadata:
|
||||
name: my-service
|
||||
...
|
||||
spec:
|
||||
... # unchanged
|
||||
status:
|
||||
domain: my-service.default.mydomain.com
|
||||
traffic:
|
||||
- revisionName: def
|
||||
name: current # addressable as current.my-service.default.mydomain.com
|
||||
percent: 100
|
||||
- revisionName: ghi
|
||||
name: next # addressable as next.my-service.default.mydomain.com
|
||||
percent: 0
|
||||
conditions:
|
||||
- type: RolloutComplete
|
||||
status: True
|
||||
...
|
||||
```
|
||||
|
||||
After testing the new revision at
|
||||
`next.my-service.default.mydomain.com`, it can be rolled out to 100%
|
||||
(either directly, or through several increments, with the split
|
||||
totaling 100%):
|
||||
|
||||
```http
|
||||
PATCH /apis/elafros.dev/v1alpha1/namespaces/default/routes/my-service
|
||||
```
|
||||
```yaml
|
||||
apiVersion: elafros.dev/v1alpha1
|
||||
kind: Route
|
||||
metadata:
|
||||
name: my-service
|
||||
spec:
|
||||
rollout:
|
||||
Traffic: # percentages must total 100%
|
||||
- revisionName: def
|
||||
name: current
|
||||
percent: 0
|
||||
- revisionName: ghi
|
||||
name: next
|
||||
percent: 100 # migrate traffic fully to the next revision
|
||||
```
|
||||
|
||||
After reconciliation, all traffic has been shifted to the new version:
|
||||
|
||||
```http
|
||||
GET /apis/elafros.dev/v1alpha1/namespaces/default/routes/my-service
|
||||
```
|
||||
```yaml
|
||||
apiVersion: elafros.dev/v1alpha1
|
||||
kind: Route
|
||||
metadata:
|
||||
name: my-service
|
||||
...
|
||||
spec:
|
||||
... # unchanged
|
||||
status:
|
||||
domain: my-service.default.mydomain.com
|
||||
traffic:
|
||||
- revisionName: def
|
||||
name: current
|
||||
percent: 0
|
||||
- revisionName: ghi
|
||||
name: next
|
||||
percent: 100
|
||||
conditions:
|
||||
- type: RolloutComplete
|
||||
status: True
|
||||
...
|
||||
```
|
||||
|
||||
By convention, the final step when completing the rollout is to update
|
||||
`current` to reflect the new revision. `next` can either be removed, or
|
||||
left addressing the same revision as current so that
|
||||
`next.my-service.default.mydomain.com` is always addressable.
|
||||
|
||||
```http
|
||||
PATCH /apis/elafros.dev/v1alpha1/namespaces/default/routes/my-service
|
||||
```
|
||||
```yaml
|
||||
apiVersion: elafros.dev/v1alpha1
|
||||
kind: Route
|
||||
metadata:
|
||||
name: my-service
|
||||
spec:
|
||||
rollout:
|
||||
traffic:
|
||||
- revisionName: ghi # update for the next rollout, current = next
|
||||
name: current
|
||||
percent: 100
|
||||
- revisionName: ghi # optional: leave next as also referring to ghi
|
||||
name: next
|
||||
percent: 0
|
||||
```
|
||||
|
||||
|
||||
## 4) Deploy a Revision from source
|
||||
|
||||
**Scenario**: User deploys a revision to an existing service from
|
||||
source rather than a pre-built container
|
||||
|
||||
```
|
||||
$ elafros deploy --service my-service
|
||||
Deploying app to service [my-service]:
|
||||
✓ Uploading [=================]
|
||||
✓ Detected [node-8-9-4] runtime
|
||||
✓ Building
|
||||
✓ Starting
|
||||
✓ Promoting
|
||||
Done.
|
||||
Deployed to https://my-service.default.mydomain.com
|
||||
```
|
||||
|
||||
**Steps**:
|
||||
|
||||
* Create/Update a Configuration, inlining build details.
|
||||
|
||||
**Results**:
|
||||
|
||||
* The Configuration is created/updated, which generates a container
|
||||
build and a new revision based on the template, and can be rolled
|
||||
out per earlier examples
|
||||
|
||||

|
||||
|
||||
|
||||
Previous examples demonstrated configurations created with pre-built
|
||||
containers. Revisions can also be created by providing build
|
||||
information to the configuration, which results in a container image
|
||||
built by the system. The build information is supplied by inlining the
|
||||
BuildSpec of a Build resource in the Configuration. This describes:
|
||||
|
||||
* **What** to build (`build.source`): Source can be provided as an
|
||||
archive, manifest file, or repository.
|
||||
|
||||
* **How** to build (`build.template`): a
|
||||
[BuildTemplate](https://github.com/elafros/build) is referenced,
|
||||
which describes how to build the container via a builder with
|
||||
arguments to the build process.
|
||||
|
||||
* **Where** to publish (`build.template.arguments`): Image registry
|
||||
url and other information specific to this build invocation.
|
||||
|
||||
The client creates the configuration inlining a build spec for an
|
||||
archive based source build, and referencing a nodejs build template:
|
||||
|
||||
```http
|
||||
POST /apis/elafros.dev/v1alpha1/namespaces/default/configurations
|
||||
```
|
||||
```yaml
|
||||
apiVersion: elafros.dev/v1alpha1
|
||||
kind: Configuration
|
||||
metadata:
|
||||
name: my-service
|
||||
spec:
|
||||
build: # build.dev/v1alpha1.BuildTemplateSpec
|
||||
source:
|
||||
# oneof git|gcs|custom:
|
||||
git:
|
||||
url: https://...
|
||||
commit: ...
|
||||
template: # defines build template
|
||||
name: nodejs_8_9_4 # builder name
|
||||
namespace: build-templates
|
||||
arguments:
|
||||
- name: _IMAGE
|
||||
value: gcr.io/... # destination for image
|
||||
|
||||
revisionTemplate: # template for building Revision
|
||||
metadata: ...
|
||||
spec:
|
||||
container: # k8s core.v1.Container
|
||||
image: gcr.io/... # Promise of a future build. Same as supplied in
|
||||
# build.template.arguments[_IMAGE]
|
||||
env: # Updated environment variables to go live with new source.
|
||||
- name: FOO
|
||||
value: bar
|
||||
- name: HELLO
|
||||
value: world
|
||||
```
|
||||
|
||||
Note the `revisionTemplate.spec.container.image` above is supplied
|
||||
with the destination of the build. This enables one-step changes to
|
||||
both config and source code. If the build step were responsible for
|
||||
updating the `revisionTemplate.spec.container.image` at the completion
|
||||
of the build, an update to both source and config could result in the
|
||||
creation of two Revisions, one with the config change, and the other
|
||||
with the new code deployment. It is expected that Revision will wait
|
||||
for the `buildName` to be complete and the
|
||||
`revisionTemplate.spec.container.image` to be live before marking the
|
||||
Revision as "ready".
|
||||
|
||||
Upon creating/updating the configuration's build field, the system
|
||||
creates a new revision. The configuration controller will initiate a
|
||||
build, populating the revision’s buildName with a reference to the
|
||||
underlying Build resource. Via status updates which the revision
|
||||
controller observes through the build reference, the high-level state
|
||||
of the build is mirrored into conditions in the Revision’s status:
|
||||
|
||||
```http
|
||||
GET /apis/elafros.dev/v1alpha1/namespaces/default/revisions/abc
|
||||
```
|
||||
```yaml
|
||||
apiVersion: elafros.dev/v1alpha1
|
||||
kind: Revision
|
||||
metadata:
|
||||
name: abc
|
||||
labels:
|
||||
elafros.dev/configuration: my-service
|
||||
elafros.dev/configurationGeneration: 1234
|
||||
...
|
||||
spec:
|
||||
# name of the build.dev/v1alpha1.Build, if built from source.
|
||||
# Set by Configuration.
|
||||
buildName: ...
|
||||
|
||||
# spec from the configuration, with container.image containing the
|
||||
# newly built container
|
||||
container: # k8s core.v1.Container
|
||||
image: gcr.io/...
|
||||
env:
|
||||
- name: FOO
|
||||
value: bar
|
||||
- name: HELLO
|
||||
value: world
|
||||
status:
|
||||
# This is a copy of metadata from the container image or grafeas, indicating
|
||||
# the provenance of the revision, annotated on the container
|
||||
imageSource:
|
||||
archive|manifest|repository: ...
|
||||
context: ...
|
||||
conditions:
|
||||
- type: Ready
|
||||
status: True
|
||||
- type: BuildComplete
|
||||
status: True
|
||||
# other conditions indicating build failure details, if applicable
|
||||
```
|
||||
|
||||
Rollout operations in the route are identical to the pre-built
|
||||
container examples.
|
||||
|
||||
Also analogous is updating the configuration to create a new
|
||||
revision - in this case, updated source would be provided to the
|
||||
configuration's inlined build spec, which would initiate a new
|
||||
container build, and the creation of a new revision.
|
||||
|
||||
|
||||
## 5) Deploy a Function
|
||||
|
||||
**Scenario**: User deploys a new function revision to an existing service
|
||||
|
||||
```
|
||||
$ elafros deploy --function index --service my-function
|
||||
Deploying function to service [my-function]:
|
||||
✓ Uploading [=================]
|
||||
✓ Detected [node-8-9-4] runtime
|
||||
✓ Building
|
||||
✓ Starting
|
||||
✓ Promoting
|
||||
Done.
|
||||
Deployed to https://my-function.default.mydomain.com
|
||||
```
|
||||
|
||||
**Steps**:
|
||||
|
||||
* Create/Update a Configuration, additionally specifying function details.
|
||||
|
||||
**Results**:
|
||||
|
||||
* The Configuration is created/updated, which generates a new revision
|
||||
based on the template build and spec which can be rolled out per
|
||||
previous examples
|
||||
|
||||

|
||||
|
||||
|
||||
Previous examples illustrated creating and deploying revisions in the
|
||||
context of apps. Functions are created and deployed in the same
|
||||
manner (in particular, as containers which respond to HTTP). In the
|
||||
build phase of the deployment, additional function metadata may be
|
||||
taken into account in order to wrap the supplied code in a functions
|
||||
framework.
|
||||
|
||||
Functions are configured with a language-specific entryPoint. The
|
||||
entryPoint may be provided as an argument to the build template, if
|
||||
language-native autodetection is insufficient. By convention, a type
|
||||
metadata label may also be added that designates revisions as a
|
||||
function, supporting listing revisions by type; there is no change to
|
||||
the system behavior based on type.
|
||||
|
||||
Note that a function may be connected to one or more event sources via
|
||||
Bindings in the Eventing API; the binding of events to functions is
|
||||
not a core function of the compute API.
|
||||
|
||||
Creating the configuration with build and function metadata:
|
||||
|
||||
```http
|
||||
POST /apis/elafros.dev/v1alpha1/namespaces/default/configurations
|
||||
```
|
||||
```yaml
|
||||
apiVersion: elafros.dev/v1alpha1
|
||||
kind: Configuration
|
||||
metadata:
|
||||
name: my-function
|
||||
spec:
|
||||
build: # build.dev/v1alpha1.BuildTemplateSpec
|
||||
source:
|
||||
# oneof git|gcs|custom
|
||||
git:
|
||||
url: https://...
|
||||
commit: ...
|
||||
template: # defines build template
|
||||
name: go_1_9_fn # function builder
|
||||
namespace: build-templates
|
||||
arguments:
|
||||
- name: _IMAGE
|
||||
value: gcr.io/... # destination for image
|
||||
- name: _ENTRY_POINT
|
||||
value: index # language dependent, function-only entrypoint
|
||||
|
||||
revisionTemplate: # template for building Revision
|
||||
metadata:
|
||||
labels:
|
||||
# One-of "function" or "app", convention for CLI/UI clients to list/select
|
||||
elafros.dev/type: "function"
|
||||
spec:
|
||||
container: # k8s core.v1.Container
|
||||
image: gcr.io/... # Promise of a future build. Same as supplied in
|
||||
# build.template.arguments[_IMAGE]
|
||||
env:
|
||||
- name: FOO
|
||||
value: bar
|
||||
- name: HELLO
|
||||
value: world
|
||||
|
||||
# serializes requests for function. Default value for functions
|
||||
concurrencyModel: SingleThreaded
|
||||
# max time allowed to respond to request
|
||||
timeoutSeconds: 20
|
||||
```
|
||||
|
||||
Upon creating or updating the configuration, a new Revision is created
|
||||
per the previous examples. Rollout operations are also identical to
|
||||
the previous examples.
|
|
@ -0,0 +1,86 @@
|
|||
# Resource Types
|
||||
|
||||
The primary resources in the Elafros API are Routes, Revisions, and Configurations:
|
||||
|
||||
* A **Route** provides a named endpoint and a mechanism for routing traffic to
|
||||
|
||||
* **Revisions**, which are immutable snapshots of code + config, created by a
|
||||
|
||||
* **Configuration**, which acts as a stream of environments for Revisions.
|
||||
|
||||

|
||||
|
||||
## Route
|
||||
|
||||
**Route** provides a network endpoint for a user's service (which
|
||||
consists of a series of software and configuration Revisions over
|
||||
time). A kubernetes namespace can have multiple routes. The route
|
||||
provides a long-lived, stable, named, HTTP-addressable endpoint that
|
||||
is backed by one or more **Revisions**. The default configuration is
|
||||
for the route to automatically route traffic to the latest revision
|
||||
created by a **Configuration**. For more complex scenarios, the API
|
||||
supports splitting traffic on a percentage basis, and CI tools could
|
||||
maintain multiple configurations for a single route (e.g. "golden
|
||||
path" and “experiments”) or reference multiple revisions directly to
|
||||
pin revisions during an incremental rollout and n-way traffic
|
||||
split. The route can optionally assign addressable subdomains to any
|
||||
or all backing revisions.
|
||||
|
||||
## Revision
|
||||
|
||||
**Revision** is an immutable snapshot of code and configuration. A
|
||||
revision can be created from a pre-built container image or built from
|
||||
source. While there is a history of previous revisions, only those
|
||||
currently referenced by a Route are addressable or routable. Older
|
||||
inactive revisions need not be backed by underlying resources, they
|
||||
exist only as the revision metadata in storage. Revisions are created
|
||||
by updates to a **Configuration**.
|
||||
|
||||
## Configuration
|
||||
|
||||
A **Configuration** describes the desired latest Revision state, and
|
||||
creates and tracks the status of Revisions as the desired state is
|
||||
updated. A configuration might include instructions on how to transform
|
||||
a source package (either git repo or archive) into a container by
|
||||
referencing a [Build](https://github.com/elafros/build), or might
|
||||
simply reference a container image and associated execution metadata
|
||||
needed by the Revision. On updates to a Configuration, a new build
|
||||
and/or deployment (creating a Revision) may be performed; the
|
||||
Configuration's controller will track the status of created Revisions
|
||||
and makes both the most recently created and most recently *ready*
|
||||
(i.e. healthy) Revision available in the status section.
|
||||
|
||||
|
||||
# Orchestration
|
||||
|
||||
The system will be configured to not allow customer mutations to
|
||||
Revisions. Instead, the creation of immutable Revisions through a
|
||||
Configuration provides:
|
||||
|
||||
* a single referenceable resource for the route to perform automated
|
||||
rollouts
|
||||
* a single resource that can be watched to see a history of all the
|
||||
revisions created
|
||||
* (but doesn’t mandate) PATCH semantics for new revisions to be done
|
||||
on the server, minimizing read-modify-write implemented across
|
||||
multiple clients, which could result in optimistic concurrency
|
||||
errors
|
||||
* the ability to rollback to a known good configuration
|
||||
|
||||
In the conventional single live revision scenario, a route has a
|
||||
single configuration with the same name as the route. Update
|
||||
operations on the configuration enable scenarios such as:
|
||||
|
||||
* *"Push code, keep config":* Specifying a new revision with updated
|
||||
source, inheriting configuration such as env vars from the
|
||||
configuration.
|
||||
* *"Update config, keep code"*: Specifying a new revision as just a
|
||||
change to configuration, such as updating an env variable,
|
||||
inheriting all other configuration and source/image.
|
||||
|
||||
When creating an initial route and performing the first deployment,
|
||||
the two operations of creating a Route and an associated Configuration
|
||||
can be done in parallel, which streamlines the use case of deploying
|
||||
code initially from a button. The
|
||||
[sample API usage](normative_examples.md) section illustrates
|
||||
conventional usage of the API.
|
|
@ -0,0 +1,263 @@
|
|||
## Resource Paths
|
||||
|
||||
Resource paths in the Elafros API have the following standard k8s form:
|
||||
|
||||
```
|
||||
/apis/{apiGroup}/{apiVersion}/namespaces/{metadata.namespace}/{kind}/{metadata.name}
|
||||
```
|
||||
|
||||
For example:
|
||||
|
||||
```
|
||||
/apis/elafros.dev/v1alpha1/namespaces/default/routes/my-service
|
||||
```
|
||||
|
||||
It is expected that each Route will provide a name within a
|
||||
cluster-wide DNS name. While no particular URL scheme is mandated
|
||||
(consult the `domain` property of the Route for the authoritative
|
||||
mapping), a common implementation would be to use the kubernetes
|
||||
namespace mechanism to produce a URL like the following:
|
||||
|
||||
```
|
||||
[$revisionname].$route.$namespace.<common elafros cluster suffix>
|
||||
```
|
||||
|
||||
For example:
|
||||
|
||||
```
|
||||
prod.my-service.default.mydomain.com
|
||||
```
|
||||
|
||||
|
||||
# Resource YAML Definitions
|
||||
|
||||
YAMLs for the Elafros API resources are described below, describing the
|
||||
basic k8s structure: metadata, spec and status, along with comments on
|
||||
specific fields.
|
||||
|
||||
## Route
|
||||
|
||||
For a high-level description of Routes,
|
||||
[see the overview](overview.md#route).
|
||||
|
||||
```yaml
|
||||
apiVersion: elafros.dev/v1alpha1
|
||||
kind: Route
|
||||
metadata:
|
||||
name: my-service
|
||||
namespace: default
|
||||
labels:
|
||||
elafros.dev/type: ... # +optional convention: function|app
|
||||
|
||||
# system generated meta
|
||||
uid: ...
|
||||
resourceVersion: ... # used for optimistic concurrency control
|
||||
creationTimestamp: ...
|
||||
generation: ... # updated only when spec changes; used by observedGeneration
|
||||
selfLink: ...
|
||||
...
|
||||
spec:
|
||||
traffic:
|
||||
# list of oneof configurationName | revisionName.
|
||||
# configurationName watches configurations to address latest latestReadyRevisionName
|
||||
# revisionName pins a specific revision
|
||||
- configurationName: ...
|
||||
name: ... # +optional. Access as {name}.${status.domain},
|
||||
# e.g. oss: current.my-service.default.mydomain.com
|
||||
percent: 100 # list percentages must add to 100. 0 is a valid list value
|
||||
- ...
|
||||
|
||||
status:
|
||||
# domain: The hostname used to access the default (traffic-split)
|
||||
# route. Typically, this will be composed of the name and namespace
|
||||
# along with a cluster-specific prefix (here, mydomain.com).
|
||||
domain: my-service.default.mydomain.com
|
||||
|
||||
traffic:
|
||||
# current rollout status list. configurationName references
|
||||
# are dereferenced to latest revision
|
||||
- revisionName: ... # latestReadyRevisionName from a configurationName in spec
|
||||
name: ...
|
||||
percent: ... # percentages add to 100. 0 is a valid list value
|
||||
- ...
|
||||
|
||||
conditions: # See also the [error conditions documentation](errors.md)
|
||||
- type: RolloutComplete
|
||||
status: True
|
||||
- type: TrafficDropped
|
||||
status: False
|
||||
- ...
|
||||
|
||||
observedGeneration: ... # last generation being reconciled
|
||||
```
|
||||
|
||||
|
||||
## Configuration
|
||||
|
||||
For a high-level description of Configurations,
|
||||
[see the overview](overview.md#configuration).
|
||||
|
||||
|
||||
```yaml
|
||||
apiVersion: elafros.dev/v1alpha1
|
||||
kind: Configuration
|
||||
metadata:
|
||||
name: my-service
|
||||
namespace: default
|
||||
|
||||
# system generated meta
|
||||
uid: ...
|
||||
resourceVersion: ... # used for optimistic concurrency control
|
||||
creationTimestamp: ...
|
||||
generation: ... # updated only when spec changes; used by observedGeneration
|
||||
selfLink: ...
|
||||
...
|
||||
spec:
|
||||
# +optional. composable Build spec, if omitted provide image directly
|
||||
build: # This is a build.dev/v1alpha1.BuildTemplateSpec
|
||||
source:
|
||||
# oneof git|gcs|custom:
|
||||
|
||||
# +optional.
|
||||
git:
|
||||
url: https://github.com/jrandom/myrepo
|
||||
commit: deadbeef # Or branch, tag, ref
|
||||
|
||||
# +optional. A zip archive or a manifest file in Google Cloud
|
||||
# Storage. A manifest file is a file containing a list of file
|
||||
# paths, backing URLs, and sha checksums. Manifest may be a more
|
||||
# efficient mechanism for a client to perform partial upload.
|
||||
gcs:
|
||||
location: https://...
|
||||
type: 'archive' # Or 'manifest'
|
||||
|
||||
# +optional. Custom specifies a container which will be run as
|
||||
# the first build step to fetch the source.
|
||||
custom: # is a core.v1.Container
|
||||
image: gcr.io/cloud-builders/git:latest
|
||||
args: [ "clone", "https://...", "other-place" ]
|
||||
|
||||
template: # build template reference and arguments.
|
||||
name: go_1_9_fn # builder name. Functions may have custom builders
|
||||
namespace: build-templates
|
||||
arguments:
|
||||
- name: _IMAGE
|
||||
value: gcr.io/... # destination for image
|
||||
- name: _ENTRY_POINT
|
||||
value: index # if function, language dependent entrypoint
|
||||
|
||||
revisionTemplate: # template for building Revision
|
||||
metadata: ...
|
||||
labels:
|
||||
elafros.dev/type: "function" # One of "function" or "app"
|
||||
spec: # elafros.RevisionTemplateSpec. Copied to a new revision
|
||||
|
||||
# +optional. if rolling back, the client may set this to the
|
||||
# previous revision's build to avoid triggering a rebuild
|
||||
buildName: ...
|
||||
|
||||
# is a core.v1.Container; some fields not allowed, such as resources, ports
|
||||
container:
|
||||
# image either provided as pre-built container, or built by Elafros from
|
||||
# source. When built by elafros, set to the same as build template, e.g.
|
||||
# build.template.arguments[_IMAGE], as the "promise" of a future build.
|
||||
# If buildName is provided, it is expected that this image will be
|
||||
# present when the referenced build is complete.
|
||||
image: gcr.io/...
|
||||
command: ['run']
|
||||
args: []
|
||||
env:
|
||||
# list of environment vars
|
||||
- name: FOO
|
||||
value: bar
|
||||
- name: HELLO
|
||||
value: world
|
||||
- ...
|
||||
livenessProbe: ... # Optional
|
||||
readinessProbe: ... # Optional
|
||||
|
||||
# +optional concurrency strategy. SingleThreaded default value for functions
|
||||
concurrencyModel: SingleThreaded
|
||||
# +optional. max time the instance is allowed for responding to a request
|
||||
timeoutSeconds: ...
|
||||
serviceAccountName: ... # Name of the service account the code should run as.
|
||||
|
||||
status:
|
||||
# the latest created and ready to serve. Watched by route
|
||||
latestReadyRevisionName: abc
|
||||
# latest created revision, may still be in the process of being materialized
|
||||
latestCreatedRevisionName: def
|
||||
conditions: # See also the [error conditions documentation](errors.md)
|
||||
- type: LatestRevisionReady
|
||||
status: False
|
||||
reason: ContainerMissing
|
||||
message: "Unable to start because container is missing and build failed."
|
||||
observedGeneration: ... # last generation being reconciled
|
||||
```
|
||||
|
||||
|
||||
## Revision
|
||||
|
||||
For a high-level description of Revisions,
|
||||
[see the overview](overview.md#revision).
|
||||
|
||||
```yaml
|
||||
apiVersion: elafros.dev/v1alpha1
|
||||
kind: Revision
|
||||
metadata:
|
||||
name: myservice-a1e34 # system generated
|
||||
namespace: default
|
||||
labels:
|
||||
elafros.dev/configuration: ... # to list configurations/revisions by service
|
||||
elafros.dev/configurationGeneration: ... # generation of configuration that created this Revision
|
||||
elafros.dev/type: "function" # convention, one of "function" or "app"
|
||||
# system generated meta
|
||||
uid: ...
|
||||
resourceVersion: ... # used for optimistic concurrency control
|
||||
creationTimestamp: ...
|
||||
generation: ...
|
||||
selfLink: ...
|
||||
...
|
||||
|
||||
# spec populated by Configuration
|
||||
spec:
|
||||
# +optional. name of the build.dev/v1alpha1.Build if built from source
|
||||
buildName: ...
|
||||
|
||||
container: # core.v1.Container
|
||||
image: gcr.io/...
|
||||
command: ['run']
|
||||
args: []
|
||||
env: # list of environment vars
|
||||
- name: FOO
|
||||
value: bar
|
||||
- name: HELLO
|
||||
value: world
|
||||
- ...
|
||||
livenessProbe: ... # Optional
|
||||
readinessProbe: ... # Optional
|
||||
concurrencyModel: ...
|
||||
timeoutSeconds: ...
|
||||
serviceAccountName: ... # Name of the service account the code should run as.
|
||||
...
|
||||
status:
|
||||
# This is a copy of metadata from the container image or grafeas,
|
||||
# indicating the provenance of the revision. This is based on the
|
||||
# container image, but may need further clarification.
|
||||
imageSource:
|
||||
git|gcs: ...
|
||||
conditions: # See also the documentation in errors.md
|
||||
- type: Ready
|
||||
status: False
|
||||
message: "Starting Instances"
|
||||
# if built from source:
|
||||
- type: BuildComplete
|
||||
status: True
|
||||
# other conditions indicating build failure, if applicable
|
||||
- ...
|
||||
# URL for accessing the logs generated by this revision. Note that logs
|
||||
# may still be access controlled separately from access to the API object.
|
||||
logUrl: "logging.infra.mycompany.com/...?filter=revision=myservice-a1e34&..."
|
||||
```
|
||||
|
||||
|
Loading…
Reference in New Issue