Doc updates
This commit is contained in:
@@ -1,107 +1,113 @@
|
|||||||
|
|
||||||
A high level view of the internals of Fission.
|
A high level view of the internals of Fission.
|
||||||
|
|
||||||
|
How it works
|
||||||
|
============
|
||||||
|
|
||||||
|
Fission is a FaaS -- users create functions (source level), register
|
||||||
|
them with fission using a CLI, and associate functions with triggers.
|
||||||
|
|
||||||
|
Fission wraps those functions into a service, and runs them on
|
||||||
|
Kubernetes on demand.
|
||||||
|
|
||||||
|
Here's an overview of the services that make up fission.
|
||||||
|
|
||||||
Components
|
Components
|
||||||
==========
|
==========
|
||||||
|
|
||||||
Language-neutral components:
|
Language-neutral components:
|
||||||
|
|
||||||
* Controller
|
* controller
|
||||||
* Container Pool Manager
|
* poolmgr
|
||||||
* Container Specializer
|
* router
|
||||||
* Router
|
* kubewatcher
|
||||||
|
|
||||||
Language-specific components:
|
Language-specific components:
|
||||||
|
|
||||||
* Language Build Container
|
* Environment container
|
||||||
* Language Run Container
|
|
||||||
|
|
||||||
|
|
||||||
Controller
|
Controller
|
||||||
----------
|
----------
|
||||||
|
|
||||||
Function and Trigger CRUD APIs. APIs to watch for changes are also
|
The controller contains CRUD APIs for functions, http triggers,
|
||||||
included (useful for other components that cache state).
|
environments, Kubernetes event watches. This is the component that
|
||||||
|
the client talks to.
|
||||||
See api/swagger.json for API details.
|
|
||||||
|
|
||||||
This is the only stateful component. It needs to be configured with a
|
This is the only stateful component. It needs to be configured with a
|
||||||
URL to an etcd cluster and a path to a persistent volume. The volume
|
URL to an etcd cluster and a path to a persistent volume. The volume
|
||||||
will be used to store the functions' source code.
|
is used to store the functions' source code. Etcd is used as the DB.
|
||||||
|
|
||||||
Etcd is used as the DB.
|
[Work to extend to other storage backends is planned, see issue #83.]
|
||||||
|
|
||||||
|
Pool Manager
|
||||||
|
------------
|
||||||
|
|
||||||
Container Pool Manager
|
Poolmgr manages pools of generic containers and function containers.
|
||||||
----------------------
|
|
||||||
|
|
||||||
Manage pool of generic containers.
|
It has a simple API; both these endpoints are called by the router.
|
||||||
|
|
||||||
Probably use K8s RCs. Can we Use labels to move pods from one rc to
|
* GetFunctionService takes function metadata and returns the address
|
||||||
another? What about jobs?
|
of a service.
|
||||||
|
|
||||||
|
* TapService lets poolmgr know a service is being used; if it's not
|
||||||
|
called for a few minutes the pod(s) backing the service are killed.
|
||||||
|
|
||||||
Container Specializer
|
Poolmgr watches the controller API and eagerly creates generic pools
|
||||||
---------------------
|
for environments. It uses Kubernetes deployments to do that. The
|
||||||
|
environment container runs in a pod with the 'fetcher' container.
|
||||||
|
Fetcher is a very simple utility that downloads a URL sent to it and
|
||||||
|
saves it at a configured location.
|
||||||
|
|
||||||
Inputs: a running generic language run container, a user function,
|
GetFunctionService "specializes" a pod. The implementation chooses a
|
||||||
optionally an http trigger URL.
|
pod from the pool, relabels it to "orphan" the pod from the
|
||||||
|
deployment, invokes fetcher to copy the function into the pod, and
|
||||||
Calls Language Run Container and sets up Router to point to it.
|
hits the the specialize endpoint on the environment container. This
|
||||||
|
causes the function to be loaded. The pod is now specific to that
|
||||||
|
function.
|
||||||
|
|
||||||
|
This function pod is cached; it's cleaned up if it's unused for a few
|
||||||
|
minutes.
|
||||||
|
|
||||||
Router
|
Router
|
||||||
------
|
------
|
||||||
|
|
||||||
- Cache trigger -> container instance mapping; implement cache miss and expiration.
|
The router forwards HTTP requests to function pods. If there's no
|
||||||
|
running service for a function, it requests one from poolmgr, while
|
||||||
|
holding on to the request; when the function's service is ready it
|
||||||
|
forwards the request.
|
||||||
|
|
||||||
- Invoke Specializer, setup up k8s API
|
The router is stateless and can be scaled up if needed, according to
|
||||||
|
load.
|
||||||
|
|
||||||
- Forward requests
|
Kubewatcher
|
||||||
|
-----------
|
||||||
|
|
||||||
The Router is stateless -- it can be scaled or killed at any time.
|
Kubewatcher watches the Kubernetes API and invokes functions
|
||||||
|
associated with watches, sending the watch event to the function.
|
||||||
|
|
||||||
There's a lot of functionality overlap with K8S Ingress Controllers.
|
The controller keeps track of user's requested watches and associated
|
||||||
We should clearly use Ingress and Ingress Controllers in some way.
|
functions. Kubewatcher watches the API based on these requests; when
|
||||||
It's not exactly clear at the moment how -- should make a whole new
|
a watch event occurs, it serializes the object and calls the function
|
||||||
Ingress Controller perhaps based on the contrib/nginx
|
via the router.
|
||||||
|
|
||||||
|
While a few simple retries are done, there isn't yet a reliable
|
||||||
|
message bus between Kubewatcher and the function. Work for this is
|
||||||
|
tracked in issue #64.
|
||||||
|
|
||||||
Autoscaler
|
Environment Container
|
||||||
----------
|
---------------------
|
||||||
|
|
||||||
This autoscales the language run containers that are backing a
|
|
||||||
trigger.
|
|
||||||
|
|
||||||
What metrics this is based on is TBD.
|
|
||||||
|
|
||||||
- Number of requests/sec
|
|
||||||
- "Backlog" -- number of outstanding requests not yet started -- how to measure this?
|
|
||||||
- Change in turn-around time?
|
|
||||||
|
|
||||||
|
|
||||||
Language Build Container
|
|
||||||
------------------------
|
|
||||||
|
|
||||||
* The Language Build Container is a container that is invoked for a
|
|
||||||
build. It takes one user-created function and outputs something
|
|
||||||
that can be run by the corresponding Language Run Container.
|
|
||||||
|
|
||||||
* The Build Container must implement the Language Build Container
|
|
||||||
interface.
|
|
||||||
|
|
||||||
|
|
||||||
Language Run Container
|
|
||||||
----------------------
|
|
||||||
|
|
||||||
* The Language Run Container is the container in which user functions
|
|
||||||
run.
|
|
||||||
|
|
||||||
* The Run Container is started without the user function. It must
|
|
||||||
start as a "Generic Container". It must implement the
|
|
||||||
"specialization interface". In short, it must implement an HTTP
|
|
||||||
server that can receive a piece of code, verify its signature, and
|
|
||||||
map it to an HTTP endpoint. See
|
|
||||||
Documentation/specs/LanguageRunContainerSpec.md for details.
|
|
||||||
|
|
||||||
|
Environment containers run user-defined functions. Environment
|
||||||
|
containers are language specific. They must contain an HTTP server
|
||||||
|
and a loader for functions.
|
||||||
|
|
||||||
|
Poolmgr deploys the environment container into a pod with fetcher
|
||||||
|
(fetcher is a simple utility that can fetch an HTTP url to a file at a
|
||||||
|
configured location). This pod forms a "generic pod", because it can
|
||||||
|
be loaded with any function.
|
||||||
|
|
||||||
|
When poolmgr needs to create a service for a function, it calls
|
||||||
|
fetcher to fetch the function. Fetcher downloads the function into a
|
||||||
|
volume shared between fetcher and this environment container. Poolmgr
|
||||||
|
then requests the container to load the function.
|
||||||
+11
-18
@@ -1,22 +1,15 @@
|
|||||||
|
_Function_: A fission function is something that's mapped to a
|
||||||
_Function_: A _function_ is the smallest unit of a program in Fission.
|
_trigger_ and run on demand. Though we call it a "function", this is
|
||||||
It's a program with an function as an entry point -- it doesn't have
|
a bit imprecise, since it's actually a module with an function as an
|
||||||
to be just one function.
|
entry point -- it doesn't have to be just one function.
|
||||||
|
|
||||||
_Trigger_: Triggers are what cause functions to be called. For
|
_Trigger_: Triggers are what cause functions to be called. For
|
||||||
example the HTTP trigger causes functions to be called on HTTP
|
example, an HTTP trigger causes functions to be called on HTTP
|
||||||
requests.
|
requests. Kubernetes Watch triggers cause functions to be called when
|
||||||
|
a Kubernetes watch changes. Future triggers will include message
|
||||||
|
queues, timers, storage systems, etc.
|
||||||
|
|
||||||
_Instance_: Fission creates and runs servers containing these
|
_Environments_: Environments are the language-specific parts of
|
||||||
functions. The running server is called an _instance_. You normally
|
Fission. Environment containers wrap the user's function and present
|
||||||
don't need to worry about instances: their management is completely
|
a common interface to the rest of the fission framework.
|
||||||
automatic and transparent to you. However, certain configuration
|
|
||||||
settings allow you to tweak the behaviour of instances -- for example,
|
|
||||||
you can configure the strategy for destroying unused instances.
|
|
||||||
|
|
||||||
_Generic Instance_: An instance with no user code in it. The process
|
|
||||||
of including the user code to a Generic Instance is _Specialization_,
|
|
||||||
and the resulting instance is a _Specialized Instance_.
|
|
||||||
|
|
||||||
_Specialized Instance_: See _General Instance_.
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,15 +0,0 @@
|
|||||||
|
|
||||||
Fission is functions-as-a-service for Kubernetes.
|
|
||||||
|
|
||||||
You write some functions, and setup some way for them to be invoked
|
|
||||||
("triggers"). Triggers can be HTTP requests (HTTP Triggers) or timers
|
|
||||||
(Time Triggers).
|
|
||||||
|
|
||||||
Topics
|
|
||||||
======
|
|
||||||
|
|
||||||
Testing
|
|
||||||
-------
|
|
||||||
|
|
||||||
Function Updates
|
|
||||||
----------------
|
|
||||||
@@ -0,0 +1,167 @@
|
|||||||
|
# Fission Environments Redesign
|
||||||
|
|
||||||
|
As Fission supports more languages and reaches a wider set of use
|
||||||
|
cases, it's time to ask how well the current Environments design is
|
||||||
|
holding up.
|
||||||
|
|
||||||
|
|
||||||
|
## Environments V1: What we learned
|
||||||
|
|
||||||
|
Environments V1 is very simple idea: an environments is one Docker
|
||||||
|
image with an HTTP server + dynamic loader for that language; it's run
|
||||||
|
in a pod with a language-agnostic sidecar (fetcher) that downloads and
|
||||||
|
saves the function into a volume shared with the language-specific
|
||||||
|
container.
|
||||||
|
|
||||||
|
### Pros:
|
||||||
|
|
||||||
|
* Abstracted away images.
|
||||||
|
|
||||||
|
* Very fast cold start
|
||||||
|
|
||||||
|
* No image registry to manage (neither for the user nor for fission
|
||||||
|
implementation)
|
||||||
|
|
||||||
|
* Relatively small amount of language specific code. (python env is <
|
||||||
|
100 lines)
|
||||||
|
|
||||||
|
### Cons:
|
||||||
|
|
||||||
|
* Doesn't work well for compiled languages
|
||||||
|
|
||||||
|
* Users have to rebuild the image to add dependencies
|
||||||
|
|
||||||
|
* Only one file supported
|
||||||
|
|
||||||
|
* Errors in loading are not surfaced properly. It is especially
|
||||||
|
annoying to wait until runtime to see a syntax error that could have
|
||||||
|
been caught on function upload.
|
||||||
|
|
||||||
|
* Not great for a large code base
|
||||||
|
|
||||||
|
* Some people want to operate at the image level but still get the
|
||||||
|
on-demand execution semantics of FaaS. This is a cost-optimization
|
||||||
|
use case.
|
||||||
|
|
||||||
|
|
||||||
|
### Discussion
|
||||||
|
|
||||||
|
Early feedback shows that almost evey user ends up rebuilding images
|
||||||
|
to add some dependecies. Some sort of automated dependecy resolution
|
||||||
|
would be very nice to have and improve the development workflow. In
|
||||||
|
other words, just attach a package.json(nodejs) or
|
||||||
|
requirements.txt(python) with a function, and fission will do the
|
||||||
|
rest. There's also the possiblity of supporting buildpacks (simple
|
||||||
|
zipfiles), a la AWS Lambda.
|
||||||
|
|
||||||
|
Though we can support compiled languages by doing the compilation
|
||||||
|
inside the cold-start, that's not a great solution because: (a)
|
||||||
|
compile errors would be reported at runtime, and (b) because the
|
||||||
|
overhead of compilation doesn't really need to be inside the
|
||||||
|
cold-start latency.
|
||||||
|
|
||||||
|
Non-trivial functions will need multiple files. That also helps for
|
||||||
|
common code across functions. So we need a way for the user to define
|
||||||
|
a function as a collection of code with an entry point.
|
||||||
|
|
||||||
|
Finally, Docker images remain the most flexible way to package an app.
|
||||||
|
Today, users can always rebuild an environment image to include
|
||||||
|
anything they want. But those images must still run a server that
|
||||||
|
implements fission-environment interface (i.e. the specialize
|
||||||
|
endpoint). So perhaps there could be a way for users to say "don't
|
||||||
|
use environments, I've already packaged up my function, here it is".
|
||||||
|
|
||||||
|
|
||||||
|
## Environment V2 Requirements
|
||||||
|
|
||||||
|
Roughly in order of priority:
|
||||||
|
|
||||||
|
0. Retain the simplicity of the simple use cases. First user
|
||||||
|
experience shoud remain trivial -- write a function, map a URL,
|
||||||
|
done.
|
||||||
|
|
||||||
|
1. Support compiled languages. Support error reporting on function
|
||||||
|
upload rather than cold start.
|
||||||
|
|
||||||
|
2. Support functions as a collection of files rather than just one
|
||||||
|
file.
|
||||||
|
|
||||||
|
3. Support automated environment-specific dependecy resolution.
|
||||||
|
|
||||||
|
(#3 may end up having the same solution as #1. You could think of
|
||||||
|
gathering deps as a "compilation" of package.json,
|
||||||
|
requirements.txt, etc.)
|
||||||
|
|
||||||
|
4. Support functions as images.
|
||||||
|
|
||||||
|
|
||||||
|
### User stories
|
||||||
|
|
||||||
|
#### Compiled language
|
||||||
|
|
||||||
|
User writes a function in Go.
|
||||||
|
|
||||||
|
```
|
||||||
|
$ fission function create --code blah.go
|
||||||
|
|
||||||
|
<compilation errors>
|
||||||
|
|
||||||
|
<user edits file>
|
||||||
|
$ $EDITOR blah.go
|
||||||
|
<fixes errors>
|
||||||
|
|
||||||
|
$ fission function update --code blah.go
|
||||||
|
<success>
|
||||||
|
```
|
||||||
|
|
||||||
|
(Or perhaps we could have a `fission function check --env x --code y`
|
||||||
|
which just does compilation, without creating a function object?
|
||||||
|
Useful for integration into IDEs. Basically, just an on-demand
|
||||||
|
builder. Useful when you don't wanna setup anything on your laptop.)
|
||||||
|
|
||||||
|
This same user story applies to interpreted languages too, where the
|
||||||
|
"compilation" step can be used to check for syntax errors.
|
||||||
|
|
||||||
|
|
||||||
|
#### Collections of files
|
||||||
|
|
||||||
|
We should probably have a manifest in YAML/JSON/etc syntax for
|
||||||
|
specifying a function. We could also use that YAML to let users
|
||||||
|
specify the function's environment, resource requirements, etc.
|
||||||
|
|
||||||
|
```
|
||||||
|
$ fission funcion create -f blah.yaml
|
||||||
|
|
||||||
|
$ cat blah.yaml
|
||||||
|
type: Function
|
||||||
|
metadata:
|
||||||
|
name: ...
|
||||||
|
environment: ...
|
||||||
|
files:
|
||||||
|
- foo.py
|
||||||
|
- bar.py
|
||||||
|
- baz/*.py
|
||||||
|
```
|
||||||
|
|
||||||
|
The yaml file could specify a list of files. The fission client would
|
||||||
|
deal with packaging up this set of files and uploading the package.
|
||||||
|
|
||||||
|
|
||||||
|
#### Handling Dependencies
|
||||||
|
|
||||||
|
A manifest could point at a function's dependency spec. The
|
||||||
|
Environment's "builder" container could then fetch these deps.
|
||||||
|
|
||||||
|
So a NodeJS function manifest could contain a reference to
|
||||||
|
package.json. A "builder" container in the NodeJS environment would
|
||||||
|
then run npm on that function.
|
||||||
|
|
||||||
|
``` $ cat func.yaml
|
||||||
|
type: Function
|
||||||
|
metadata: ...
|
||||||
|
environment: ...
|
||||||
|
dependencies: package.json
|
||||||
|
|
||||||
|
$ fission function create -f func.yaml
|
||||||
|
```
|
||||||
|
|
||||||
Reference in New Issue
Block a user