From 000b74d4adfb41124cd0aae59c3338f9ba9e3cc5 Mon Sep 17 00:00:00 2001 From: Soam Vasani Date: Mon, 23 Jan 2017 19:11:37 -0800 Subject: [PATCH] Doc updates --- Documentation/Architecture.md | 140 ++++++++-------- Documentation/TERMINOLOGY | 29 ++-- Documentation/UserManual.md | 15 -- Documentation/{ => wip}/ProgrammingModel.md | 0 Documentation/wip/environments-v2.md | 167 ++++++++++++++++++++ 5 files changed, 251 insertions(+), 100 deletions(-) delete mode 100644 Documentation/UserManual.md rename Documentation/{ => wip}/ProgrammingModel.md (100%) create mode 100644 Documentation/wip/environments-v2.md diff --git a/Documentation/Architecture.md b/Documentation/Architecture.md index bf38a9c8..0be7879e 100644 --- a/Documentation/Architecture.md +++ b/Documentation/Architecture.md @@ -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. \ No newline at end of file diff --git a/Documentation/TERMINOLOGY b/Documentation/TERMINOLOGY index 69b9c59c..02abc99b 100644 --- a/Documentation/TERMINOLOGY +++ b/Documentation/TERMINOLOGY @@ -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. diff --git a/Documentation/UserManual.md b/Documentation/UserManual.md deleted file mode 100644 index 7396e64c..00000000 --- a/Documentation/UserManual.md +++ /dev/null @@ -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 ----------------- diff --git a/Documentation/ProgrammingModel.md b/Documentation/wip/ProgrammingModel.md similarity index 100% rename from Documentation/ProgrammingModel.md rename to Documentation/wip/ProgrammingModel.md diff --git a/Documentation/wip/environments-v2.md b/Documentation/wip/environments-v2.md new file mode 100644 index 00000000..994d94f1 --- /dev/null +++ b/Documentation/wip/environments-v2.md @@ -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 + + + + + $ $EDITOR blah.go + + + $ fission function update --code blah.go + +``` + +(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 +``` +