mirror of
https://github.com/psviderski/uncloud.git
synced 2026-08-26 11:03:34 +00:00
docs: combine landing page and docusaurus together and deploy as one image
This commit is contained in:
+151
@@ -0,0 +1,151 @@
|
||||
Output demo.mp4
|
||||
|
||||
Set Shell "bash"
|
||||
Set FontSize 15
|
||||
Set FontFamily "SauceCodePro Nerd Font"
|
||||
Set Width 1200
|
||||
Set Height 700
|
||||
Set WindowBar Colorful
|
||||
Set Theme "Solarized Dark Higher Contrast"
|
||||
Set Padding 20
|
||||
|
||||
Type "# 🚀 Uncloud demo: Docker simplicity with multi-machine power"
|
||||
Enter
|
||||
Sleep 1s
|
||||
Type "# Let's deploy a highly available web app with automatic HTTPS in just a couple minutes"
|
||||
Enter
|
||||
Enter
|
||||
Sleep 1s
|
||||
Type "# First, let's initialise our first machine using a free Oracle Cloud VM"
|
||||
Enter
|
||||
Sleep 1s
|
||||
Type "uc machine init --name oracle-vm ubuntu@152.67.101.197"
|
||||
Sleep 1s
|
||||
Enter
|
||||
# This takes time, so we wait for it to complete.
|
||||
Wait@1m
|
||||
Sleep 3s
|
||||
|
||||
Enter
|
||||
Type "# Let's see our machine in the cluster"
|
||||
Enter
|
||||
Type "uc machine ls"
|
||||
Enter
|
||||
Wait
|
||||
Sleep 3s
|
||||
|
||||
Ctrl+L
|
||||
|
||||
Type "# Now let's deploy a simple web app with automatic HTTPS called myapp"
|
||||
Enter
|
||||
Sleep 1s
|
||||
Type "uc run -n myapp -p myapp.psviderski.name:80/https traefik/whoami"
|
||||
Sleep 1s
|
||||
Enter
|
||||
Wait
|
||||
Sleep 2s
|
||||
|
||||
Enter
|
||||
Type "# The -p flag publishes the container port 80 as HTTPS via reverse proxy Caddy using the provided domain"
|
||||
Enter
|
||||
Type "# Let's check it out! First using the auto-generated uncloud domain"
|
||||
Enter
|
||||
Sleep 1s
|
||||
|
||||
Hide
|
||||
Sleep 5s
|
||||
Show
|
||||
|
||||
Type "curl https://myapp.xuw3xd.cluster.uncloud.run"
|
||||
Sleep 1s
|
||||
Enter
|
||||
Wait
|
||||
Sleep 2s
|
||||
|
||||
Enter
|
||||
Type "# For the provided domain to work, we need to manually add a DNS record pointing to the machine IP"
|
||||
Enter
|
||||
Type "# To simplify IP management, I added a CNAME record: myapp.psviderski.name -> myapp.xuw3xd.cluster.uncloud.run"
|
||||
Enter
|
||||
Sleep 2s
|
||||
Type "curl https://myapp.psviderski.name"
|
||||
Sleep 1s
|
||||
Enter
|
||||
Wait
|
||||
Sleep 1s
|
||||
|
||||
Enter
|
||||
Type "# Sweet!"
|
||||
Enter
|
||||
Sleep 2s
|
||||
|
||||
Ctrl+L
|
||||
|
||||
Type "# Let's add redundancy by adding another machine, a Hetzner server in a different region"
|
||||
Enter
|
||||
Sleep 1s
|
||||
Type "uc machine add --name hetzner-server root@5.223.45.199"
|
||||
Sleep 1s
|
||||
Enter
|
||||
Wait@1m
|
||||
Sleep 3s
|
||||
|
||||
Enter
|
||||
Type "# Now the cool part - let's add a powerful server from my home network to the same cluster"
|
||||
Enter
|
||||
Type "# Uncloud lets you mix cloud VMs with your own hardware to save costs on resource-intensive workloads"
|
||||
Enter
|
||||
Sleep 2s
|
||||
Type "uc machine add --name home-server --public-ip none spy@192.168.40.243"
|
||||
Sleep 2s
|
||||
Enter
|
||||
Wait@1m
|
||||
Sleep 3s
|
||||
|
||||
Enter
|
||||
Type "# Let's see our hybrid cluster spanning cloud providers and home network"
|
||||
Enter
|
||||
Sleep 1s
|
||||
Type "uc machine ls"
|
||||
Sleep 1s
|
||||
Enter
|
||||
Wait
|
||||
Sleep 3s
|
||||
|
||||
Ctrl+L
|
||||
|
||||
Type "# Time to scale our service across all machines"
|
||||
Enter
|
||||
Sleep 1s
|
||||
Type "uc scale myapp 3"
|
||||
Sleep 1s
|
||||
Enter
|
||||
Wait
|
||||
Sleep 1s
|
||||
|
||||
Enter
|
||||
Type "uc inspect myapp"
|
||||
Sleep 1s
|
||||
Enter
|
||||
Wait
|
||||
Sleep 2s
|
||||
|
||||
Enter
|
||||
Type "# Let's see load balancing in action"
|
||||
Enter
|
||||
Sleep 1s
|
||||
Type "for i in {1..5}; do curl -s https://myapp.psviderski.name | grep Hostname; done"
|
||||
Sleep 1s
|
||||
Enter
|
||||
Wait
|
||||
Sleep 3s
|
||||
|
||||
Ctrl+L
|
||||
|
||||
Type "# ✨ And that's it! We've deployed a highly available web app across providers in just a couple minutes"
|
||||
Enter
|
||||
Type "# Your service stays up even if the home server and any cloud machine go down"
|
||||
Enter
|
||||
Type "# No master nodes to maintain, no complex cluster setup. Try Uncloud today! 🚀"
|
||||
Enter
|
||||
Sleep 1s
|
||||
+157
@@ -0,0 +1,157 @@
|
||||
# Uncloud design
|
||||
|
||||
Uncloud aims to provide a solution for deploying and running containerized applications and services across various
|
||||
platforms — from cloud VPSs to Raspberry Pis and powerful servers — or any combination of them.
|
||||
|
||||
While existing prominent PaaS solutions like https://coolify.io/ and https://dokploy.com/ use a single machine or Docker
|
||||
Swarm as their underlying infrastructure, Uncloud takes a different approach. It aims to offer a multi-host experience
|
||||
similar to the serverless solutions provided by cloud providers but on users' own machines. Users should be able to add
|
||||
machines as compute resources without worrying about control plane high availability and cluster management. For
|
||||
example, a user could combine a cloud VPS with a spare Raspberry Pi to create a unified computing environment.
|
||||
|
||||
Uncloud aims to replace the concept of a "cluster" with a "network" of machines. It's similar to Tailscale's design
|
||||
where users can add machines to a network to enable secure communication between them. The primary challenges in this
|
||||
design are:
|
||||
|
||||
1. Building such a network
|
||||
2. Orchestrating containers across it
|
||||
3. Exposing services to the internet
|
||||
|
||||
## Network of machines
|
||||
|
||||
While Tailscale is an obvious solution for establishing an overlay network, it has some drawbacks:
|
||||
|
||||
1. Users must register an account and generate a key for each machine.
|
||||
2. Every container requires a Tailscale proxy to communicate with other machines and containers.
|
||||
|
||||
The Tailscale subnet router feature could potentially be used to route traffic from the machine to the internal Docker
|
||||
network.
|
||||
|
||||
As an alternative, we can configure a simple flat WireGuard mesh network during the machine setup process. For example:
|
||||
|
||||
| CIDR | Description |
|
||||
|-----------------|----------------------------------------------------------|
|
||||
| `10.210.0.0/16` | The entire WireGuard mesh network |
|
||||
| `10.210.X.0/24` | `/24` subnet assigned to machine `X` |
|
||||
| `10.210.X.1/32` | Machine `X` address is the first address from the subnet |
|
||||
| `10.210.X.Y/32` | Container `Y` address running on machine `X` |
|
||||
|
||||
Testing has shown that inter-container and machine routing works well when `/24` is assigned to the Docker network
|
||||
bridge, while the WireGuard interface is configured with just the `/32` machine address.
|
||||
|
||||
This setup allows containers to communicate with each other on the same machine or across machines without address
|
||||
translation. This design also enables the future implementation of [ACLs](https://tailscale.com/kb/1018/acls) and
|
||||
security groups to restrict traffic between machines and containers if needed.
|
||||
|
||||
The peer discovery and NAT traversal techniques for the WireGuard mesh are heavily inspired by the
|
||||
Talos [KubeSpan](https://www.talos.dev/v1.7/talos-guides/network/kubespan/) design.
|
||||
|
||||
## Orchestration
|
||||
|
||||
The main question that drives the design of the orchestration system is can we build a system that doesn't require a
|
||||
centralised control plane? The short answer is yes, we can. However, it's not easy as decentralised systems are
|
||||
inherently more complex than centralised ones.
|
||||
|
||||
The existing solutions such as Kubernetes, Docker Swarm, and Nomad use a centralised control plane to provide a single
|
||||
point of entry for the user to interact with the cluster. I'm envisioning a system where all machines are equal, yet
|
||||
each one can represent the entire cluster and receive commands from the user. Where a user can be a CLI tool or a web
|
||||
interface that can run on any machine in the network. In case of network partitioning, the user should continue to be
|
||||
able to interact with each partition separately to manage services running on them. The system should be able to
|
||||
reconcile the state of the network when the partition is healed. Such an architecture favors Availability and Partition
|
||||
tolerance (AP) over Consistency and Availability (CA) in the [CAP theorem](https://en.wikipedia.org/wiki/CAP_theorem).
|
||||
|
||||
### Shared state
|
||||
|
||||
One of the ways to achieve this is to store the entire shared state on every machine and keep them all in sync. A couple
|
||||
of techniques can be used to assist with this:
|
||||
a [Conflict-Free Replicated Data Type](https://en.wikipedia.org/wiki/Conflict-free_replicated_data_type) (CRDT) and a
|
||||
peer-to-peer [gossip protocol](https://en.wikipedia.org/wiki/Gossip_protocol).
|
||||
|
||||
By composing the state using CRDT data structures allows any machine to modify its own copy of the state independently,
|
||||
concurrently, and without coordination with other machines. The CRDT automatically resolves conflicts that might arise
|
||||
from concurrent updates. Although the machines may have different states at any given time, they're guaranteed to
|
||||
eventually converge to the *same* state. The tradeoff is that the *same* state doesn't always mean the intended one from
|
||||
each user's point of view.
|
||||
|
||||
There is a distributed key-value store implementation in Go ([ipfs/go-ds-crdt](https://github.com/ipfs/go-ds-crdt)) from
|
||||
the IPFS ecosystem that uses
|
||||
[Merkle CRDTs](https://research.protocol.ai/publications/merkle-crdts-merkle-dags-meet-crdts/psaras2020.pdf). It's
|
||||
independent of the permanent storage implementation and the transport layer for broadcasting and receiving updates from
|
||||
peers.
|
||||
|
||||
The gossip protocol can be used to propagate state updates across the network. There is a great project from HashiCorp
|
||||
called [Serf](https://www.serf.io/) that provides a gossip protocol implementation in addition to cluster membership,
|
||||
failure detection, and basic orchestration that is decentralized, fault-tolerant and highly available. It's used in
|
||||
Nomad and Consul internally.
|
||||
|
||||
[`experiment/serf_crdt.go`](../experiment/serf_crdt.go) is an experimental distributed key-value store
|
||||
using [BadgerDB](https://github.com/dgraph-io/badger) as the persistent storage for
|
||||
[ipfs/go-ds-crdt](https://github.com/ipfs/go-ds-crdt) and Serf as the gossip protocol that uses its custom user events
|
||||
and queries features. The implementation is very inefficient but the results are promising. A couple of machines are
|
||||
able to replicate changes to the store across the internet in a fraction of a second and converge their copies if they
|
||||
get out of sync. Although, the convergence is quite slow at the moment due to the inefficient broadcast and query
|
||||
implementation.
|
||||
|
||||
There is a hypothesis that in practice there shouldn't be much churn and conflicts in the state of relatively small
|
||||
deployments. The updates should be propagated quickly enough thus the eventually consistent behaviour should be
|
||||
acceptable.
|
||||
|
||||
### Container scheduling
|
||||
|
||||
There are two fundamental approaches to design an orchestration system: declarative and imperative. I believe that
|
||||
Uncloud should use a hybrid approach that favours the imperative over the declarative one where possible.
|
||||
|
||||
The imperative approach allows the errors to be handled in a more predictable way. While the declarative approach allows
|
||||
to decouple components of the system by using a shared state and events for communication.
|
||||
|
||||
For example, the user can run a command to start a container on a specific machine. The command can call the target
|
||||
machine directly to start the container, handle the errors accordingly, and return the result to the user.
|
||||
Alternatively, the command can update the local state declaring that a container should be started on a specific machine
|
||||
and wait until it's propagated to the target machine and reconciled. Arguably, the latter approach is more complex, less
|
||||
predictable, and has more edge cases to handle.
|
||||
|
||||
However, asynchronously updating the configuration of DNS servers or reverse proxies in accordance to the state changes
|
||||
caused by started or stopped containers is likely a more reliable approach than doing it imperatively.
|
||||
|
||||
Not sure if we really need this yet, but more complex scenarios like running and watching a replica set of containers
|
||||
might require a sort of coordination between machines. I believe this can be achieved by using consensus and leader
|
||||
election algorithms on demand.
|
||||
|
||||
Maybe for now we should only aim for a simpler and more static container orchestrator where container scheduling can
|
||||
only be initiated by a user. Docker on each machine will ensure that the containers are running and restarted on
|
||||
failures, of course. But they won't be moved to other machines automatically.
|
||||
|
||||
## Service discovery
|
||||
|
||||
Service discovery using DNS has its own drawbacks but it's likely the simplest way to implement it. Like in Docker and
|
||||
Docker Swarm, the Uncloud agent on every machine will expose an internal DNS server to the machine itself and the
|
||||
containers running on it. The DNS server will resolve machine, container, and service names to their respective IP
|
||||
addresses within the mesh network.
|
||||
|
||||
DNS servers will watch running containers through the shared state and update their DNS records accordingly. The
|
||||
following table is an example list of available DNS names:
|
||||
|
||||
| DNS name | Resolves to |
|
||||
|----------------------------------------------------|----------------------------------------------------------------------------|
|
||||
| `<machine-name>.machine.internal` | Mesh IP of the machine. Machine names must be unique within the org |
|
||||
| `<container-name>.<machine-name>.machine.internal` | Mesh IP of the container on the machine |
|
||||
| `<service-name>.internal` | Mesh IPs of all containers for the service |
|
||||
| `lb.<service-name>.internal` | Virtual IP / LB IP that balances traffic to all containers for the service |
|
||||
|
||||
A proper thinking about the name design is needed. Most likely we need to namespace them by project or environment name
|
||||
to allow for multiple instances of the same service and avoid conflicts.
|
||||
|
||||
Virtual IPs for services using IPVS, ip/nftables, or eBPF can be implemented later if we see fit.
|
||||
|
||||
## Ingress
|
||||
|
||||
Services can be exposed to the internet by running a reverse proxy on a machine(s) that listens on the public IP
|
||||
address. A user should be able to control which machine(s) should run a reverse proxy by assigning an appropriate role
|
||||
to them.
|
||||
|
||||
Traefik or Caddy can be used as a reverse proxy that can automatically discover services running on the network using
|
||||
the internal DNS server. They both support automatic TLS certificate generation and renewal using Let's Encrypt.
|
||||
|
||||
In case of network partitioning, DNS servers should adjust their records in accordance with what containers are
|
||||
available in their partition. Consequently, the reverse proxy should adjust its configuration to route traffic to only
|
||||
the available containers by resolving the updated DNS records.
|
||||
@@ -0,0 +1,138 @@
|
||||
<mxfile host="app.diagrams.net" agent="Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:133.0) Gecko/20100101 Firefox/133.0" version="26.0.6">
|
||||
<diagram id="l8hXMBHkgcEJcSW0mbfh" name="Page-1">
|
||||
<mxGraphModel dx="2522" dy="1435" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1169" pageHeight="827" math="0" shadow="0">
|
||||
<root>
|
||||
<mxCell id="0" />
|
||||
<mxCell id="1" parent="0" />
|
||||
<mxCell id="anjOE1Lszn3xuFwhIM3--51" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0;entryY=0.5;entryDx=0;entryDy=0;exitX=0.5;exitY=1;exitDx=0;exitDy=0;curved=1;startArrow=classic;startFill=1;strokeColor=#871619;strokeWidth=2;" edge="1" parent="1" source="anjOE1Lszn3xuFwhIM3--15" target="anjOE1Lszn3xuFwhIM3--47">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="anjOE1Lszn3xuFwhIM3--15" value="" style="rounded=1;whiteSpace=wrap;html=1;strokeWidth=1;glass=0;shadow=0;sketch=1;curveFitting=1;jiggle=2;strokeColor=#71717A;" vertex="1" parent="1">
|
||||
<mxGeometry x="40" y="110" width="200" height="230" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="anjOE1Lszn3xuFwhIM3--16" value="<font face="Indie Flower">Virtual machine</font>" style="text;html=1;align=left;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=18;fontColor=#18181B;" vertex="1" parent="1">
|
||||
<mxGeometry x="40" y="73" width="120" height="30" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="anjOE1Lszn3xuFwhIM3--30" value="" style="rounded=1;whiteSpace=wrap;html=1;sketch=1;curveFitting=1;jiggle=2;strokeWidth=1;strokeColor=#71717A;" vertex="1" parent="1">
|
||||
<mxGeometry x="540" y="110" width="200" height="270" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="anjOE1Lszn3xuFwhIM3--31" value="" style="shape=image;verticalLabelPosition=bottom;labelBackgroundColor=default;verticalAlign=top;aspect=fixed;imageAspect=0;image=https://cdn.worldvectorlogo.com/logos/digitalocean-2.svg;" vertex="1" parent="1">
|
||||
<mxGeometry x="40" y="40" width="177.34" height="30" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="anjOE1Lszn3xuFwhIM3--32" value="<font face="Indie Flower">Dedicated server<br></font>" style="text;html=1;align=left;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=18;fontColor=#18181B;" vertex="1" parent="1">
|
||||
<mxGeometry x="540" y="73" width="170" height="30" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="anjOE1Lszn3xuFwhIM3--35" value="" style="shape=image;verticalLabelPosition=bottom;labelBackgroundColor=default;verticalAlign=top;aspect=fixed;imageAspect=0;image=https://upload.wikimedia.org/wikipedia/commons/thumb/0/0c/Logo_Hetzner.svg/512px-Logo_Hetzner.svg.png;" vertex="1" parent="1">
|
||||
<mxGeometry x="540" y="43.5" width="186.92" height="23" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="anjOE1Lszn3xuFwhIM3--36" value="<div><font face="Indie Flower" style="font-size: 22px;"><b>Office / Homelab</b></font></div><div><font face="Indie Flower">Mac mini / PC<br></font></div>" style="text;html=1;align=left;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=18;fontColor=#18181B;" vertex="1" parent="1">
|
||||
<mxGeometry x="500" y="555" width="170" height="30" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="anjOE1Lszn3xuFwhIM3--37" value="$ <font face="JetBrains Mono" data-font-src="https://fonts.googleapis.com/css?family=JetBrains+Mono">uc CLI<br></font>" style="rounded=1;whiteSpace=wrap;html=1;sketch=1;curveFitting=1;jiggle=2;strokeWidth=1;fontColor=#FFFFFF;fillColor=#27272A;fillStyle=solid;fontSize=16;" vertex="1" parent="1">
|
||||
<mxGeometry x="42" y="470" width="120" height="70" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="anjOE1Lszn3xuFwhIM3--50" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;curved=1;startArrow=classic;startFill=1;strokeColor=#871619;strokeWidth=2;entryX=0.5;entryY=1;entryDx=0;entryDy=0;" edge="1" parent="1" source="anjOE1Lszn3xuFwhIM3--47" target="anjOE1Lszn3xuFwhIM3--30">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="anjOE1Lszn3xuFwhIM3--47" value="" style="rounded=1;whiteSpace=wrap;html=1;sketch=1;curveFitting=1;jiggle=2;strokeWidth=1;strokeColor=#71717A;" vertex="1" parent="1">
|
||||
<mxGeometry x="290" y="300" width="200" height="290" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="anjOE1Lszn3xuFwhIM3--52" value="" style="shape=image;verticalLabelPosition=bottom;labelBackgroundColor=default;verticalAlign=top;aspect=fixed;imageAspect=0;image=https://cdn.icon-icons.com/icons2/2699/PNG/512/wireguard_logo_icon_167956.png;" vertex="1" parent="1">
|
||||
<mxGeometry x="324" y="163" width="148" height="74" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="anjOE1Lszn3xuFwhIM3--53" value="<font face="Indie Flower">Private network<br></font>" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=18;fontColor=#18181B;" vertex="1" parent="1">
|
||||
<mxGeometry x="338" y="230" width="120" height="30" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="anjOE1Lszn3xuFwhIM3--74" value="https://app.example.com" style="text;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=18;fontStyle=1;html=1;fontFamily=Indie Flower;fontSource=https%3A%2F%2Ffonts.googleapis.com%2Fcss%3Ffamily%3DIndie%2BFlower;spacing=2;spacingTop=0;fontColor=#18181B;" vertex="1" parent="1">
|
||||
<mxGeometry x="295.89" y="80" width="194.23" height="30" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="anjOE1Lszn3xuFwhIM3--76" value="" style="rounded=1;whiteSpace=wrap;html=1;strokeWidth=1;dashed=1;shadow=0;glass=0;strokeColor=#71717A;" vertex="1" parent="1">
|
||||
<mxGeometry x="52.11" y="122" width="175.77" height="138" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="anjOE1Lszn3xuFwhIM3--78" value="containers" style="shape=image;verticalLabelPosition=middle;labelBackgroundColor=default;verticalAlign=middle;aspect=fixed;imageAspect=0;image=https://static-00.iconduck.com/assets.00/docker-icon-1024x739-rivf80b4.png;labelPosition=right;align=left;fontFamily=Indie Flower;fontSource=https%3A%2F%2Ffonts.googleapis.com%2Fcss%3Ffamily%3DIndie%2BFlower;fontSize=16;fontColor=#18181B;" vertex="1" parent="1">
|
||||
<mxGeometry x="84.99999999999994" y="127.55000000000001" width="34" height="24.55" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="anjOE1Lszn3xuFwhIM3--79" value="<font face="Indie Flower">app</font>" style="rounded=0;whiteSpace=wrap;html=1;strokeWidth=1;fillStyle=hachure;sketch=1;curveFitting=1;jiggle=2;fillColor=#e1d5e7;labelBackgroundColor=none;labelBorderColor=none;textShadow=0;fontSize=14;align=center;verticalAlign=middle;strokeColor=#9673a6;fontColor=#18181B;" vertex="1" parent="1">
|
||||
<mxGeometry x="74.99999999999994" y="210" width="130" height="30" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="anjOE1Lszn3xuFwhIM3--80" value="<font face="Indie Flower">caddy</font>" style="rounded=0;whiteSpace=wrap;html=1;strokeWidth=1;fillStyle=hachure;fillColor=#d5e8d4;labelBackgroundColor=none;labelBorderColor=none;textShadow=0;fontSize=14;align=center;verticalAlign=middle;sketch=1;curveFitting=1;jiggle=2;strokeColor=#82b366;fontColor=#18181B;" vertex="1" parent="1">
|
||||
<mxGeometry x="74.99999999999994" y="170" width="130" height="30" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="anjOE1Lszn3xuFwhIM3--83" value="<font face="Indie Flower">uncloud daemon<br></font>" style="rounded=0;whiteSpace=wrap;html=1;strokeWidth=1;fillStyle=cross-hatch;sketch=1;curveFitting=1;jiggle=2;fillColor=#D6D6D6;labelBackgroundColor=none;labelBorderColor=none;textShadow=0;fontSize=14;strokeColor=#27272A;fontColor=#18181B;" vertex="1" parent="1">
|
||||
<mxGeometry x="328" y="545" width="130" height="30" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="anjOE1Lszn3xuFwhIM3--92" style="rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=1;entryY=0.5;entryDx=0;entryDy=0;sketch=1;curveFitting=1;jiggle=2;exitX=0.407;exitY=1;exitDx=0;exitDy=0;exitPerimeter=0;strokeColor=#18181B;" edge="1" parent="1" source="anjOE1Lszn3xuFwhIM3--74" target="anjOE1Lszn3xuFwhIM3--80">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="anjOE1Lszn3xuFwhIM3--69" style="rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0;entryY=0.5;entryDx=0;entryDy=0;edgeStyle=orthogonalEdgeStyle;curved=1;sketch=1;curveFitting=1;jiggle=2;exitX=1;exitY=0.5;exitDx=0;exitDy=0;strokeColor=#18181B;" edge="1" parent="1" source="anjOE1Lszn3xuFwhIM3--37" target="anjOE1Lszn3xuFwhIM3--83">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="150.0000000000001" y="560.03" as="sourcePoint" />
|
||||
<mxPoint x="316.12" y="606" as="targetPoint" />
|
||||
<Array as="points" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="anjOE1Lszn3xuFwhIM3--94" value="<div>Control the cluster and services</div><div>through the closest machine</div>" style="edgeLabel;html=1;align=left;verticalAlign=middle;resizable=0;points=[];fontFamily=Indie Flower;fontSource=https%3A%2F%2Ffonts.googleapis.com%2Fcss%3Ffamily%3DIndie%2BFlower;fontSize=16;fontColor=#18181B;" vertex="1" connectable="0" parent="anjOE1Lszn3xuFwhIM3--69">
|
||||
<mxGeometry x="0.312" y="-2" relative="1" as="geometry">
|
||||
<mxPoint x="-212" y="8" as="offset" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="anjOE1Lszn3xuFwhIM3--114" value="<div><font face="Indie Flower" data-font-src="https://fonts.googleapis.com/css?family=Indie+Flower">RPC over SSH</font></div>" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];labelBackgroundColor=none;rotation=13;fontSize=14;fontColor=#18181B;" vertex="1" connectable="0" parent="anjOE1Lszn3xuFwhIM3--69">
|
||||
<mxGeometry x="-0.0843" y="-4" relative="1" as="geometry">
|
||||
<mxPoint x="-11" y="-20" as="offset" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="anjOE1Lszn3xuFwhIM3--97" value="" style="rounded=1;whiteSpace=wrap;html=1;strokeWidth=1;dashed=1;shadow=0;glass=0;strokeColor=#71717A;" vertex="1" parent="1">
|
||||
<mxGeometry x="552.69" y="122" width="175.77" height="178" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="anjOE1Lszn3xuFwhIM3--98" value="containers" style="shape=image;verticalLabelPosition=middle;labelBackgroundColor=default;verticalAlign=middle;aspect=fixed;imageAspect=0;image=https://static-00.iconduck.com/assets.00/docker-icon-1024x739-rivf80b4.png;labelPosition=right;align=left;fontFamily=Indie Flower;fontSource=https%3A%2F%2Ffonts.googleapis.com%2Fcss%3Ffamily%3DIndie%2BFlower;fontSize=16;fontColor=#18181B;" vertex="1" parent="1">
|
||||
<mxGeometry x="585.5799999999999" y="129.55" width="34" height="24.55" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="anjOE1Lszn3xuFwhIM3--99" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.01;entryY=0.422;entryDx=0;entryDy=0;curved=1;startArrow=classic;startFill=1;strokeColor=#871619;strokeWidth=2;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryPerimeter=0;" edge="1" parent="1" source="anjOE1Lszn3xuFwhIM3--15" target="anjOE1Lszn3xuFwhIM3--30">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="323.75" y="183" as="sourcePoint" />
|
||||
<mxPoint x="473.75" y="307" as="targetPoint" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="anjOE1Lszn3xuFwhIM3--102" value="<font face="Indie Flower">uncloud daemon<br></font>" style="rounded=0;whiteSpace=wrap;html=1;strokeWidth=1;fillStyle=cross-hatch;sketch=1;curveFitting=1;jiggle=2;fillColor=#D6D6D6;labelBackgroundColor=none;labelBorderColor=none;textShadow=0;fontSize=14;strokeColor=#27272A;fontColor=#18181B;" vertex="1" parent="1">
|
||||
<mxGeometry x="575" y="330" width="130" height="30" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="anjOE1Lszn3xuFwhIM3--103" value="<font face="Indie Flower">uncloud daemon<br></font>" style="rounded=0;whiteSpace=wrap;html=1;strokeWidth=1;fillStyle=cross-hatch;sketch=1;curveFitting=1;jiggle=2;fillColor=#D6D6D6;labelBackgroundColor=none;labelBorderColor=none;textShadow=0;fontSize=14;strokeColor=#27272A;fontColor=#18181B;" vertex="1" parent="1">
|
||||
<mxGeometry x="75" y="290" width="130" height="30" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="anjOE1Lszn3xuFwhIM3--104" value="<font face="Indie Flower">caddy</font>" style="rounded=0;whiteSpace=wrap;html=1;strokeWidth=1;fillStyle=hachure;fillColor=#d5e8d4;labelBackgroundColor=none;labelBorderColor=none;textShadow=0;fontSize=14;align=center;verticalAlign=middle;sketch=1;curveFitting=1;jiggle=2;strokeColor=#82b366;fontColor=#18181B;" vertex="1" parent="1">
|
||||
<mxGeometry x="574.9999999999999" y="170" width="130" height="30" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="anjOE1Lszn3xuFwhIM3--105" value="<font face="Indie Flower">app</font>" style="rounded=0;whiteSpace=wrap;html=1;strokeWidth=1;fillStyle=hachure;sketch=1;curveFitting=1;jiggle=2;fillColor=#e1d5e7;labelBackgroundColor=none;labelBorderColor=none;textShadow=0;fontSize=14;align=center;verticalAlign=middle;strokeColor=#9673a6;fontColor=#18181B;" vertex="1" parent="1">
|
||||
<mxGeometry x="575" y="210" width="130" height="30" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="anjOE1Lszn3xuFwhIM3--106" value="<font face="Indie Flower">app-db</font>" style="rounded=0;whiteSpace=wrap;html=1;strokeWidth=1;fillStyle=hachure;sketch=1;curveFitting=1;jiggle=2;fillColor=#e1d5e7;labelBackgroundColor=none;labelBorderColor=none;textShadow=0;fontSize=14;align=center;verticalAlign=middle;strokeColor=#9673a6;fontColor=#18181B;" vertex="1" parent="1">
|
||||
<mxGeometry x="575" y="250" width="130" height="30" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="anjOE1Lszn3xuFwhIM3--107" value="" style="rounded=1;whiteSpace=wrap;html=1;strokeWidth=1;dashed=1;shadow=0;glass=0;strokeColor=#71717A;" vertex="1" parent="1">
|
||||
<mxGeometry x="302.12" y="312" width="175.77" height="218" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="anjOE1Lszn3xuFwhIM3--108" value="containers" style="shape=image;verticalLabelPosition=middle;labelBackgroundColor=default;verticalAlign=middle;aspect=fixed;imageAspect=0;image=https://static-00.iconduck.com/assets.00/docker-icon-1024x739-rivf80b4.png;labelPosition=right;align=left;fontFamily=Indie Flower;fontSource=https%3A%2F%2Ffonts.googleapis.com%2Fcss%3Ffamily%3DIndie%2BFlower;fontSize=16;fontColor=#18181B;" vertex="1" parent="1">
|
||||
<mxGeometry x="335.00999999999993" y="319.55" width="34" height="24.55" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="anjOE1Lszn3xuFwhIM3--109" value="<font face="Indie Flower">caddy</font>" style="rounded=0;whiteSpace=wrap;html=1;strokeWidth=1;fillStyle=hachure;fillColor=#d5e8d4;labelBackgroundColor=none;labelBorderColor=none;textShadow=0;fontSize=14;align=center;verticalAlign=middle;sketch=1;curveFitting=1;jiggle=2;strokeColor=#82b366;fontColor=#18181B;" vertex="1" parent="1">
|
||||
<mxGeometry x="324.4299999999999" y="360" width="130" height="30" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="anjOE1Lszn3xuFwhIM3--110" value="<font face="Indie Flower">app-staging</font>" style="rounded=0;whiteSpace=wrap;html=1;strokeWidth=1;fillStyle=hachure;sketch=1;curveFitting=1;jiggle=2;fillColor=#ffe6cc;labelBackgroundColor=none;labelBorderColor=none;textShadow=0;fontSize=14;align=center;verticalAlign=middle;strokeColor=#d79b00;fontColor=#18181B;" vertex="1" parent="1">
|
||||
<mxGeometry x="324.43" y="400" width="130" height="30" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="anjOE1Lszn3xuFwhIM3--111" value="<font face="Indie Flower">app-db-staging</font>" style="rounded=0;whiteSpace=wrap;html=1;strokeWidth=1;fillStyle=hachure;sketch=1;curveFitting=1;jiggle=2;fillColor=#ffe6cc;labelBackgroundColor=none;labelBorderColor=none;textShadow=0;fontSize=14;align=center;verticalAlign=middle;strokeColor=#d79b00;fontColor=#18181B;" vertex="1" parent="1">
|
||||
<mxGeometry x="324.43" y="440" width="130" height="30" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="anjOE1Lszn3xuFwhIM3--112" value="<font face="Indie Flower">monitoring stack<br></font>" style="rounded=0;whiteSpace=wrap;html=1;strokeWidth=1;fillStyle=hachure;sketch=1;curveFitting=1;jiggle=2;fillColor=#dae8fc;labelBackgroundColor=none;labelBorderColor=none;textShadow=0;fontSize=14;align=center;verticalAlign=middle;strokeColor=#6c8ebf;fontColor=#18181B;" vertex="1" parent="1">
|
||||
<mxGeometry x="325" y="480" width="130" height="30" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="anjOE1Lszn3xuFwhIM3--93" style="rounded=0;orthogonalLoop=1;jettySize=auto;html=1;sketch=1;curveFitting=1;jiggle=2;exitX=0.608;exitY=1;exitDx=0;exitDy=0;exitPerimeter=0;strokeColor=#18181B;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" edge="1" parent="1" source="anjOE1Lszn3xuFwhIM3--74" target="anjOE1Lszn3xuFwhIM3--104">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="481" y="200" as="sourcePoint" />
|
||||
<mxPoint x="470" y="170" as="targetPoint" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
</root>
|
||||
</mxGraphModel>
|
||||
</diagram>
|
||||
</mxfile>
|
||||
@@ -0,0 +1,60 @@
|
||||
# User Guide
|
||||
|
||||
## Initialising a cluster
|
||||
|
||||
To begin setting up a new Uncloud cluster, create the desired nodes. Ensure that their firewall allows the required ports.
|
||||
|
||||
### Ports
|
||||
|
||||
Nodes running Uncloud with a standard configuration must have the following inbound ports allowed in the firewall:
|
||||
|
||||
* 51820/udp (WireGuard meshnet)
|
||||
* 22/tcp (default SSH management port)
|
||||
* 80/tcp (required for challenge if uncloud.run DNS is enabled, pass --no-dns during init to disable)
|
||||
|
||||
In addition, any ports for any workloads you want to expose will need to be open.
|
||||
|
||||
### Configuration
|
||||
|
||||
Uncloud stores its configuration in `~/.config/uncloud/config.yaml`. If you wish to reinitialise a cluster, simply remove it from this config.
|
||||
|
||||
### Initialisation
|
||||
|
||||
Begin by initialising the first node in your cluster with `uc machine init [USER@HOST:PORT]`. If you do not have a need for Caddy reverse proxy, you may disable this feature with `--no-caddy`. If you want to avoid using uncloud's managed DNS service, add the `--no-dns` flag.
|
||||
|
||||
This command will idempotently install Docker, uncloudd, uncloud-corrosion. If Caddy is enabled, it will set up a reverse proxy. If Uncloud DNS is enabled, it will create a DNS A record for the machine's public IP address under `*.[CLUSTER ID].cluster.uncloud.run`.
|
||||
|
||||
If you wish to uninstall Uncloud and its components, run `uncloud-uninstall`.
|
||||
|
||||
### Adding a node
|
||||
|
||||
Just like the initialisation of the first node, a node can be added to the cluster with `uc machine add`.
|
||||
|
||||
### DNS
|
||||
|
||||
Uncloud (uncloud.run) DNS can be managed with the `uc dns` subcommand.
|
||||
|
||||
* To reserve a domain name, run `uc dns reserve`
|
||||
* To release a domain name, run `uc dns release`.
|
||||
* To see the domain name, run `uc dns show`
|
||||
* To avoid using the Uncloud managed DNS service, use the `--no-dns` flag on your `uc machine init` command.
|
||||
|
||||
### Running a service
|
||||
|
||||
Services on an Uncloud cluster can be managed with `uc service`.
|
||||
|
||||
You can run a service with two replicas that expose port 80 like the following:
|
||||
|
||||
```
|
||||
uc service run -p 80/http --replicas 2 nginxdemos/hello
|
||||
```
|
||||
|
||||
This requires the Caddy reverse proxy to be deployed. If it wasn't previously, it can be deployed with `uc caddy deploy`.
|
||||
|
||||
Since this doesn't specify a service name, a random one will be generated.
|
||||
|
||||
The service can be deleted by its service name:
|
||||
|
||||
```
|
||||
uc service rm hello-gsdo
|
||||
```
|
||||
Reference in New Issue
Block a user