Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

docs: update readme #3

Merged
merged 1 commit into from
Apr 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
196 changes: 99 additions & 97 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,21 @@
> :warning: Pico is currently only a proof of concept so is not yet suitable
for production.

Pico is a reverse proxy that allows you to expose a service that isn't publicly
routable (known as tunnelling). Unlike other open-source tunelling solutions,
Pico is designed to serve production traffic.
Pico is a reverse proxy that allows you to expose an endpoint that isn’t
publicly routable (known as tunnelling).

Upstream services register a listener with Pico via an outbound-only
connection. Downstream clients may then send HTTP(S) requests to Pico which
will be routed to an upstream listener.
Unlike many open-source tunnelling solutions, Pico is designed to serve
production traffic. Such as you may use Pico to expose services in a customer
network, a bring your own cloud (BYOC) service or to connect to IoT devices.

Listeners are identified by an endpoint ID. Incoming HTTP requests include the
endpoint ID to route to in either the `Host` header or an `x-pico-endpoint`
header, then Pico load balances requests among registered listeners with that
endpoint ID.
Upstream endpoints register with Pico via an outbound-only connection. Clients
then send HTTP(S) requests to Pico which will proxy the requests to a
registered endpoint.

Requests identify the target endpoint ID using either the `Host` header or an
`x-pico-endpoint` header. When multiple endpoints have the same ID, Pico will
load balance requests for that ID among the registered endpoints. Therefore an
endpoint ID is the equivalent of a domain name in Pico.

![overview](assets/images/overview.png)

Expand All @@ -29,124 +32,123 @@ endpoint ID.
## Design Goals

### Production Traffic
Unlike most open-source tunnelling solutions that are built for testing and
Unlike many open-source tunnelling solutions that are built for testing and
development (such as sharing a demo running on your local machine), Pico is
built to serve production traffic. Such as it could be used to access a
customer network in a bring your own cloud (BYOC) service.
built to serve production traffic. Such as you could use Pico to:
* Access customer networks
* Build a bring your own cloud (BYOC) solution
* Access IoT devices

Pico supports running the server as a cluster of Pico nodes, meaning Pico is
fault tolerant and scales horizontally.
Therefore Pico supports running as a cluster of server nodes in order to be
fault tolerant, scale horizontally and support zero downtime deployments. It
also includes observability tools for monitoring, alerting and debugging.

### Hosting
Pico is designed to be simple to host, particularly in Kubernetes. Therefore
Pico may be hosted behind a HTTP load balancer or
[Kubernetes Gateway](https://kubernetes.io/docs/concepts/services-networking/gateway/).
Pico is designed to be simple to host, particularly on Kubernetes. A cluster of
server nodes can be hosted behind a HTTP load balancer or
[Kubernetes Gateway](https://kubernetes.io/docs/concepts/services-networking/gateway/)
as a Kubernetes deployment or stateful set.

The downside of this approach is it means Pico only supports HTTP. Pico
also uses WebSockets internally to communicate with upstream listeners, which
are typically supported by HTTP load balancers.
Upstream endpoints and proxied requests may be load balanced to any node in the
cluster, then Pico will manage routing the request to the correct endpoint.

### Dynamic Endpoints
Upstream listeners may register any endpoint ID dynamically at runtime, without
any static configuration. When multiple listeners register with the same
endpoint ID, Pico will load balance requests among those listeners.
Upstreams may register any endpoint ID dynamically at runtime without any
static configuration. If there are multiple registered endpoints for a given
ID, Pico load balances requests among those endpoints.

## Components

### Server
The Pico server is responsible for proxying requests from downstream clients to
registered upstream listeners.

Upstreams register one or more listeners with the server via an outbound-only
connection. Each listener is identified by an endpoint ID.

Pico may be hosted as a cluster of servers for fault tolerance and scalability.

The server has four ports:
* Proxy port (`8000`): Listens for requests from downstream clients which
are forwarded to upstream listeners
* Upstream port (`8001`): Listens for connections from upstream listeners
* Admin port (`8002`): Listeners for admin requests to inspect the server
status
* Gossip port (`7000`): Used for inter-node gossip traffic to discover the
status of each node in the cluster

The proxy and upstream ports are separate since they downstream clients and
upstream listeners will typically be on different networks. Such as you may
expose the upstream port to the Internet but only allow requests to the proxy
port from nodes in the same network.

#### Routing
Incoming HTTP requests include the endpoint ID to route to in either the `Host`
header or an `x-pico-endpoint` header, then Pico load balances requests among
registered listeners with that endpoint ID.

When the `Host` header is used, the server must be configured with a wildcard
DNS entry, where the bottom-level domain contains the endpoint ID. Such as if
you host Pico at `pico.example.com`, you can then send requests to
`<endpoint ID>.pico.example.com`.

Alternatively if an `x-pico-endpoint` header is included, it takes precedence
over the `Host` header, such as you could send a request to `pico.example.com`
with header `x-pico-endpoint: <endpoint ID>`. This means you don't have to
setup a wildcard DNS entry, though it does mean Pico isn't transparent to the
client.
registered upstream endpoints.

Upstream endpoints register with Pico via an outbound-only connection. Clients
then send HTTP(S) requests to the Pico server, which will proxy the requests to
a registered endpoint.

Incoming requests identify the target endpoint ID using either the `Host`
header or `x-pico-endpoint` header. When the `Host` header is used, the lowest
level domain is used as the endpoint ID. Such as if you send a request to
`my-endpoint.pico.example.com`, `my-endpoint` will be used. `x-pico-endpoint`
takes precedence over the `Host`.

Pico supports running as a cluster of server nodes. Upstream listeners and
downstream clients may connect to any node in the cluster and Pico manages
routing requests to the correct listener.

The server exposes 4 ports:
* Proxy port: Listens for HTTP(S) requests from downstream clients and forwards
the requests to upstream listeners (defaults to `8000`)
* Upstream port: Listens for connections from upstream listeners (defaults to
`8001`)
* Admin port: Listens for admin requests to inspect the status of the server
(defaults to `8002`)
* Gossip port: Listens for gossip traffic between nodes in the same cluster
(defaults to `8003`)

The server has separate proxy and upstream ports as upstream listeners and
downstream clients will be in separate networks (otherwise there isn’t any need
for Pico). Such as you may expose the upstream port to the Internet for
external networks to register endpoints, though only allow requests to the
proxy port from nodes in the same network.

Run a server node with `pico server`.

### Agent
The Pico agent is a CLI that runs alongside your upstream service that
registers one or more listeners.
The Pico agent is a lightweight proxy that runs alongside your upstream
services that registers endpoints with the Pico server and forwards incoming
requests.

The agent will connect to a Pico server, register the configured listeners,
then forwards incoming requests to your upstream service.
Such as you run an Pico agent and register endpoint `my-endpoint` that forwards
requests to `localhost:3000`.

Such as if you have a service running at `localhost:3000`, you can register
endpoint `my-endpoint` that forwards requests to that local service.
Run the Pico agent with `pico agent`.

## Getting Started
This section describes how to run both the Pico server and agent locally. In
production you'd host the server remotely as a cluster, though this is still
useful to demo Pico.

Start by building Pico with `make pico`, which builds Pico at `build/pico`
(which requires Go 1.21 or later).
This section describes how to run both the Pico server and agent locally to
register and endpoint. In production you'd host the server remotely as a
cluster, though this is still useful to demo Pico.

### Server
Start the server with `pico server`, which will listen for proxy requests at
`localhost:8000` and upstream connections at `localhost:8001` by default.
This example registers an endpoint `my-endpoint` and forwards requests to
`localhost:3000`. To see the requests being forwarded, start a simple file
server at `localhost:3000` with `python3 -m http.server 3000`.

See `pico server -h` for the available configuration options.
Pico has a single binary that can be built with `make pico`, which is output to
`build/pico` (requires Go 1.21 or later).

### Agent
Next start a service you would like to route requests to, such as
`python3 -m http.server 3000` to start a simple HTTP file server listening on
port `3000`.
### Setup

Start the Pico server with `pico server`. This listens for proxy requests on
port `8000` and upstream connections on `8001`.

Next you can start Pico agent with
`pico agent --endpoints my-endpoint-123/localhost:3000` which registers a
listener with endpoint ID `my-endpoint-123` and forwards requests to
`localhost:3000`.
Next start the Pico agent and register the above endpoint with
`pico agent endpoints my-endpoint/localhost:3000`. This creates an outbound
connection to the Pico server that registers to receive requests for the given
endpoints.

See `pico agent -h` for the available configuration options.
You can inspect the status of the server using the `pico status` CLI. Such as
use `pico status proxy endpoints` to view the list of endpoints registered on
the server.

### Send a Request
As described above, Pico routes requests using the endpoint ID in either the
`Host` header or `x-pico-endpoint` (where `x-pico-endpoint` takes precedence).
### Request

Since using a `Host` header requires setting up a wildcard DNS entry, the
simplest option when running locally is to set the `x-pico-endpoint` header.
As described above, when sending a request to Pico you can identify the
endpoint ID using either the `Host` header or the `x-pico-agent`.

Such as to send a HTTP request to your service at `localhost:3000` via endpoint
`my-endpoint-123`, use
`curl -H "x-pico-endpoint: my-endpoint-123" http://localhost:8000`.
Therefore to send a request to the upstream endpoint use:
```
# x-pico-endpoint
curl http://localhost:8000 -H "x-pico-endpoint: my-endpoint"

You can also inspect the server status using `pico status`. Such as to view the
endpoints registered to this server use `pico status proxy endpoints`.
# Host
curl --connect-to my-endpoint.example.com:8000:localhost:8000 http://my-endpoint.example.com:8000
```

## Docs

See [docs](./docs) for details on deploying and managing Pico, plus details on
the Pico architecture:
- Deploy
See [docs](./docs) for details on deploying and managing Pico:
- [Kubernetes](./docs/deploy/kubernetes.md)
- [Observability](./docs/deploy/observability.md)

Expand Down
Binary file modified assets/images/overview.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading