Doc updates
This commit is contained in:
@@ -1,107 +1,113 @@
|
||||
|
||||
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
|
||||
==========
|
||||
|
||||
Language-neutral components:
|
||||
|
||||
* Controller
|
||||
* Container Pool Manager
|
||||
* Container Specializer
|
||||
* Router
|
||||
* controller
|
||||
* poolmgr
|
||||
* router
|
||||
* kubewatcher
|
||||
|
||||
Language-specific components:
|
||||
|
||||
* Language Build Container
|
||||
* Language Run Container
|
||||
* Environment container
|
||||
|
||||
|
||||
Controller
|
||||
----------
|
||||
|
||||
Function and Trigger CRUD APIs. APIs to watch for changes are also
|
||||
included (useful for other components that cache state).
|
||||
|
||||
See api/swagger.json for API details.
|
||||
The controller contains CRUD APIs for functions, http triggers,
|
||||
environments, Kubernetes event watches. This is the component that
|
||||
the client talks to.
|
||||
|
||||
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
|
||||
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
|
||||
another? What about jobs?
|
||||
* GetFunctionService takes function metadata and returns the address
|
||||
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,
|
||||
optionally an http trigger URL.
|
||||
|
||||
Calls Language Run Container and sets up Router to point to it.
|
||||
GetFunctionService "specializes" a pod. The implementation chooses a
|
||||
pod from the pool, relabels it to "orphan" the pod from the
|
||||
deployment, invokes fetcher to copy the function into the pod, and
|
||||
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
|
||||
------
|
||||
|
||||
- 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.
|
||||
We should clearly use Ingress and Ingress Controllers in some way.
|
||||
It's not exactly clear at the moment how -- should make a whole new
|
||||
Ingress Controller perhaps based on the contrib/nginx
|
||||
The controller keeps track of user's requested watches and associated
|
||||
functions. Kubewatcher watches the API based on these requests; when
|
||||
a watch event occurs, it serializes the object and calls the function
|
||||
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
|
||||
----------
|
||||
|
||||
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 Container
|
||||
---------------------
|
||||
|
||||
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 _function_ is the smallest unit of a program in Fission.
|
||||
It's a program with an function as an entry point -- it doesn't have
|
||||
to be just one function.
|
||||
_Function_: A fission function is something that's mapped to a
|
||||
_trigger_ and run on demand. Though we call it a "function", this is
|
||||
a bit imprecise, since it's actually a module with an function as an
|
||||
entry point -- it doesn't have to be just one function.
|
||||
|
||||
_Trigger_: Triggers are what cause functions to be called. For
|
||||
example the HTTP trigger causes functions to be called on HTTP
|
||||
requests.
|
||||
example, an HTTP trigger causes functions to be called on HTTP
|
||||
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
|
||||
functions. The running server is called an _instance_. You normally
|
||||
don't need to worry about instances: their management is completely
|
||||
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_.
|
||||
_Environments_: Environments are the language-specific parts of
|
||||
Fission. Environment containers wrap the user's function and present
|
||||
a common interface to the rest of the fission framework.
|
||||
|
||||
|
||||
@@ -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