From a6f207ccc67671c868a086250e8f561e3553aaf1 Mon Sep 17 00:00:00 2001 From: Alex-Andrei Cioc Date: Tue, 2 Jun 2026 11:20:48 +0300 Subject: [PATCH 1/2] chore: QA pass Signed-off-by: Alex-Andrei Cioc --- .gitignore | 5 + .../config/vocabularies/Unikraft/accept.txt | 3 + README.md | 77 ++- pages/cli/legacy-overview.mdx | 8 +- pages/cli/overview.mdx | 2 +- pages/cli/registries.mdx | 222 +++---- pages/faq.mdx | 62 +- pages/features/autokill.mdx | 168 +++-- pages/features/autoscale.mdx | 500 ++++++++++----- pages/features/cron-jobs.mdx | 258 +++++--- pages/features/forking.mdx | 29 +- pages/features/load-balancing.mdx | 200 ++++-- pages/features/roms.mdx | 536 ++++++++-------- pages/features/scale-to-zero.mdx | 293 +++++---- pages/features/snapshots.mdx | 43 +- pages/guides/overview.mdx | 2 +- pages/integrations/kubernetes.mdx | 171 ++++-- pages/integrations/sdks/go.mdx | 64 +- pages/introduction.mdx | 2 +- pages/platform/certificates.mdx | 183 ++++-- pages/platform/delete-locks.mdx | 283 ++++++--- pages/platform/domains.mdx | 158 +++-- pages/platform/images.mdx | 172 +++--- pages/platform/instances.mdx | 455 +++++++++++--- pages/platform/metrics.mdx | 285 +++++++-- pages/platform/metros.mdx | 56 +- .../networking.mdx} | 72 ++- pages/platform/quotas.mdx | 194 ++++-- pages/platform/services.mdx | 309 ++++++---- pages/platform/tagging.mdx | 541 ++++++++++++++-- pages/platform/troubleshooting.mdx | 520 ++++++++-------- pages/platform/volumes.mdx | 578 ++++++++++++++---- pages/tutorials/docker-to-ukc.mdx | 116 ++-- pages/tutorials/environment-variables.mdx | 50 +- pages/tutorials/instance-metrics.mdx | 412 ------------- pages/tutorials/kraftkit-to-unikraft.mdx | 63 +- pages/tutorials/rootfs-compression.mdx | 245 ++++---- pages/tutorials/rootfs-formats.mdx | 236 +++++-- pages/tutorials/rootfs-volumes-roms.mdx | 244 +++++--- pages/tutorials/scale-to-zero-triggers.mdx | 96 ++- pages/use-cases/api-gateways.mdx | 247 +++++++- pages/use-cases/build-test-environments.mdx | 158 ++--- pages/use-cases/game-servers.mdx | 188 +++++- pages/use-cases/headless-browsers.mdx | 122 ++-- pages/use-cases/mcp-servers.mdx | 67 +- pages/use-cases/remote-desktops.mdx | 42 +- pages/use-cases/remote-ides.mdx | 179 +++++- pages/use-cases/sandboxes.mdx | 57 +- pages/use-cases/serverless-databases.mdx | 209 +++++-- pages/use-cases/serverless-functions.mdx | 158 ++--- pages/use-cases/webhooks.mdx | 38 +- scripts/capture_ansi.sh | 149 +++++ zudoku.config.tsx | 41 +- 53 files changed, 6289 insertions(+), 3279 deletions(-) rename pages/{tutorials/network-communication.mdx => platform/networking.mdx} (58%) delete mode 100644 pages/tutorials/instance-metrics.mdx create mode 100755 scripts/capture_ansi.sh diff --git a/.gitignore b/.gitignore index df398fbd..6ad6b839 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,7 @@ /.pnp .pnp.* .yarn/* +.pnpm-store/ !.yarn/patches !.yarn/plugins !.yarn/releases @@ -43,3 +44,7 @@ dist/ apis/platform.yaml build.log .examples/ +pages/cli/kraft/ +pages/cli/unikraft/ +pages/cli/unikraft.mdx +pages/kraftfile/ diff --git a/.vale/styles/config/vocabularies/Unikraft/accept.txt b/.vale/styles/config/vocabularies/Unikraft/accept.txt index 2a4ed8df..894bab4e 100644 --- a/.vale/styles/config/vocabularies/Unikraft/accept.txt +++ b/.vale/styles/config/vocabularies/Unikraft/accept.txt @@ -25,6 +25,7 @@ FUSE ALIAS ANAME ASCII +BYOC CNAME COPY DCO @@ -49,6 +50,7 @@ TOML USA UTC WASI +WSL YAML Unikraft Cloud POSIX @@ -267,6 +269,7 @@ FTP (?i)TypeDoc (?i)typeof (?i)TypeScript +(?i)UDP (?i)ui (?i)uncached (?i)undefined diff --git a/README.md b/README.md index a787c0f0..f8f43929 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,7 @@ Contributions to the documentation are welcome! - [Contributing to the docs](#contributing-to-the-docs) - [Table of contents](#table-of-contents) - [Introduction](#introduction) + - [Local development with Docker](#local-development-with-docker) - [Code contribution guidelines](#code-contribution-guidelines) - [Git commit message guidelines](#git-commit-message-guidelines) - [Copyright and license](#copyright-and-license) @@ -22,17 +23,81 @@ The [`pages/`](./pages) directory contains all the [markdown](https://www.markdo To maintain consistency and accuracy across all documentation pages, [vale](https://vale.sh/) lints all written prose. +### Local development with Docker + +Run the commands in this section from the root of this `docs/` repository. + +The checked-in [docker-compose.yaml](./docker-compose.yaml) builds the production site and does not provide hot reload. +For live editing, first bootstrap the generated CLI, API, and Kraftfile reference content from the existing Docker build: + +```bash +docker build --target build -t ukc-docs-bootstrap . + +cid=$(docker create ukc-docs-bootstrap) +mkdir -p apis pages/cli pages/kraftfile +docker cp "$cid":/docs/apis/platform.yaml ./apis/platform.yaml +docker cp "$cid":/docs/pages/cli/unikraft ./pages/cli/unikraft +docker cp "$cid":/docs/pages/cli/kraft ./pages/cli/kraft +docker cp "$cid":/docs/pages/cli/unikraft.mdx ./pages/cli/unikraft.mdx +docker cp "$cid":/docs/pages/kraftfile/v0.7.md ./pages/kraftfile/v0.7.md +docker rm "$cid" +``` + +That bootstrap image pulls the OpenAPI specification from the [unikraft-cloud/openapi](https://github.com/unikraft-cloud/openapi) repository and materializes the API reference consumed by Zudoku at startup. +If you only need to refresh the API reference, you can fetch that file directly without rebuilding the whole bootstrap image: + +```bash +mkdir -p apis +curl -fsSL \ + https://raw.githubusercontent.com/unikraft-cloud/openapi/refs/heads/prod-staging/platform.yaml \ + -o apis/platform.yaml +``` + +Then run the docs site in a bind-mounted Node container: + +```bash +docker run --rm -it \ + -p 3131:3131 \ + -e CHOKIDAR_USEPOLLING=true \ + -v "$PWD":/docs \ + -v ukc-docs-node-modules:/docs/node_modules \ + -w /docs \ + node:24-bookworm \ + sh -lc 'apt-get update >/dev/null \ + && apt-get install -y ca-certificates >/dev/null \ + && corepack enable >/dev/null \ + && corepack prepare pnpm@10.33.0 --activate >/dev/null \ + && pnpm install >/dev/null \ + && pnpm dev -- --host 0.0.0.0' +``` + +Open `http://localhost:3131/docs` in your browser. +Changes under this directory are bind-mounted into the container, and `CHOKIDAR_USEPOLLING=true` enables hot reload reliably from Docker. + +The bootstrap step is needed because this repository does not check in the generated `/apis/platform.yaml`, `/cli/unikraft`, `/cli/kraft`, and `/kraftfile/v0.7` source files that Zudoku expects at startup. +The first run takes longer because Docker builds the bootstrap image, downloads the base Node image, and `pnpm` installs dependencies into the `ukc-docs-node-modules` volume. +To reset that container-only dependency cache, remove the volume: + +```bash +docker volume rm ukc-docs-node-modules +``` + ### Code contribution guidelines To make the contribution process as seamless as possible, please follow these requirements: -* Fork the project and make your changes. -* When you’re ready to create a pull request, be sure to: - * Run `make lint` to check all documentation. - * Squash your commits into to logical, [atomic commits](https://en.wikipedia.org/wiki/Atomic_commit) (`git rebase -i`). +- Fork the project and make your changes. +- When you’re ready to create a pull request, be sure to: + - Run `make lint` to check all documentation. + - Squash your commits into to logical, [atomic commits](https://en.wikipedia.org/wiki/Atomic_commit) (`git rebase -i`). It's okay to force update your pull request with `git push -f`. - * Follow the **Git Commit Message Guidelines** below. -* All commits must be signed off (`git commit -s`) by all authors in order to certify that the contributions are published under the [Developer Certificate of Origin (DCO)](https://wiki.linuxfoundation.org/dco). + - Follow the **Git Commit Message Guidelines** below. +- All commits must be signed off (`git commit -s`) by all authors in order to certify that the contributions are published under the [Developer Certificate of Origin (DCO)](https://wiki.linuxfoundation.org/dco). +- For consistency, you should follow these soft guidelines for writing docs (besides what `make lint` enforces): + - Where `unikraft` commands are used, provide `kraft` commands as well, if applicable. + - All code blocks should have 2 spaces indentation for multi-line code, and the language should be specified for syntax highlighting (for example, `bash` for shell commands). + - Code blocks used for listing output should have `title=""`, unless context clarifications are needed (e.g., it is tied to a specific CLI in a `CodeTabs`). + - Header titles should be concise and descriptive, starting with a capital letter on the first word only. ### Git commit message guidelines diff --git a/pages/cli/legacy-overview.mdx b/pages/cli/legacy-overview.mdx index dd6f1be5..b7f411b2 100644 --- a/pages/cli/legacy-overview.mdx +++ b/pages/cli/legacy-overview.mdx @@ -26,10 +26,10 @@ brew install unikraft/tap/kraftkit # Update and install dependencies sudo apt-get update sudo apt-get install \ - ca-certificates \ - curl \ - gnupg \ - lsb-release + ca-certificates \ + curl \ + gnupg \ + lsb-release # Add Unikraft's official GPG key: sudo mkdir -p /etc/apt/keyrings diff --git a/pages/cli/overview.mdx b/pages/cli/overview.mdx index a84cfd4a..3f98af51 100644 --- a/pages/cli/overview.mdx +++ b/pages/cli/overview.mdx @@ -79,7 +79,7 @@ See [alternative installation instructions](https://github.com/unikraft/cli/?tab unikraft login # Deploy an instance -unikraft run --metro=fra -p 443:8080/http+tls --image=nginx:latest +unikraft run --metro fra -p 443:8080/http+tls --image nginx:latest # List instances unikraft instances list diff --git a/pages/cli/registries.mdx b/pages/cli/registries.mdx index ea492f2b..8b97e113 100644 --- a/pages/cli/registries.mdx +++ b/pages/cli/registries.mdx @@ -17,25 +17,35 @@ This speeds up deployments and reduces bandwidth usage. Directly pushing to a node's registry or using the central registry is interchangeable. The platform handles synchronization between them. +:::caution[**Limited Access**] +The local registry is only available in BYOC or on-prem Unikraft Cloud installations. +::: + ## Image naming The platform addresses images using the standard OCI reference format: -``` +```text title="" index.unikraft.io//: index..unikraft.cloud//: ``` For example: -``` + + +```text title="central registry" index.unikraft.io/alice/http-python312:latest index.unikraft.io/alice/http-python312@sha256:278cb8b1... +``` +```text title="local registry" index.node1.unikraft.cloud/alice/http-python312:latest index.node1.unikraft.cloud/alice/http-python312@sha256:278cb8b1... ``` + + The `` segment is your Unikraft Cloud organization, available as the `UKC_USER` environment variable in most guides. Tags are arbitrary strings. The platform defaults to `latest` when you don't specify a tag. @@ -44,91 +54,90 @@ You can also refer to images by digest to pin to an exact version. ## Authentication Registry authentication uses the same API token as the rest of the platform. -The new CLI uses profiles, while the legacy CLI reads `UKC_TOKEN` directly. +The new CLI uses [profiles](/tutorials/kraftkit-to-unikraft#profile-management), while the legacy CLI reads `UKC_TOKEN` directly. - ```bash title="unikraft" - unikraft login - ``` +```bash title="unikraft" +unikraft login +``` - ```bash title="kraft" - export UKC_TOKEN= - ``` +```bash title="kraft" +export UKC_TOKEN= +``` The `UKC_TOKEN` and `UKC_METRO` environment variables are only used by the legacy CLI. -You will also need to use the legacy CLI login command for `kraft pkg` operations: +When using external registries (e.g. `ghcr.io`), authenticate explicitly: - + - ```bash title="central registry" - kraft login -u "$UKC_USER" -t "$UKC_TOKEN" index.unikraft.io - ``` +```bash title="unikraft" +docker login ghcr.io -u -p +``` - ```bash title="local registry" - kraft login -u "$UKC_USER" -t "$UKC_TOKEN" index..unikraft.cloud - ``` +```bash title="kraft" +kraft login -u -t ghcr.io +``` +You don't need to login to the central or node registries. + ## Pushing images -### Via the CLI (recommended) +### Via `unikraft build` (recommended) The simplest way to push an image is to use the CLI to build, package, push, and start an instance in one flow: - ```bash title="central registry" - unikraft build . --output index.unikraft.io//:latest - unikraft run --metro=fra -p 443:8080/http+tls -m 256MiB --name --image=index.unikraft.io//:latest - ``` - ```bash title="local registry" - unikraft build . --output index..unikraft.cloud//:latest - unikraft run --metro=fra -p 443:8080/http+tls -m 256MiB --name --image=index..unikraft.cloud//:latest - ``` - -During deployment you will see output confirming the push: - - +```bash title="central registry" +# the index.unikraft.io domain can be omitted as it's the default +unikraft build . --output index.unikraft.io//:latest +unikraft run --metro fra \ + --name \ + -m 256M \ + -p 443:8080/http+tls \ + --image index.unikraft.io//:latest +``` - ```ansi title="central registry" - [+] packaging index.unikraft.io/alice/my-app:latest... done! - [+] pushing index.unikraft.io/alice/my-app:latest (kraftcloud/x86_64)... done! - ``` +```bash title="local registry" +unikraft build . --output index..unikraft.cloud//:latest +unikraft run --metro fra \ + --name \ + -m 256M \ + -p 443:8080/http+tls \ + --image index..unikraft.cloud//:latest +``` - ```ansi title="local registry" - [+] packaging index..unikraft.cloud/alice/my-app:latest... done! - [+] pushing index..unikraft.cloud/alice/my-app:latest (kraftcloud/x86_64)... done! - ``` ### Via `kraft pkg --push` -For more control, for example when packaging [ROMs](/features/roms) or base images, use the legacy CLI with the `--push` flag: +For the legacy CLI, package using the `--push` flag: - ```bash title="central registry" - kraft pkg \ - --plat kraftcloud \ - --arch x86_64 \ - --name index.unikraft.io/"$UKC_USER"/my-app:latest \ - --push \ - . - ``` - - ```bash title="local registry" - kraft pkg \ - --plat kraftcloud \ - --arch x86_64 \ - --name index..unikraft.cloud/"$UKC_USER"/my-app:latest \ - --push \ - . - ``` +```bash title="central registry" +kraft pkg \ + --plat kraftcloud \ + --arch x86_64 \ + --name index.unikraft.io/"$UKC_USER"/my-app:latest \ + --push \ + . +``` + +```bash title="local registry" +kraft pkg \ + --plat kraftcloud \ + --arch x86_64 \ + --name index..unikraft.cloud/"$UKC_USER"/my-app:latest \ + --push \ + . +``` @@ -136,29 +145,29 @@ For more control, for example when packaging [ROMs](/features/roms) or base imag - ```bash title="unikraft" - unikraft images list - ``` +```bash title="unikraft" +unikraft images list +``` - ```bash title="kraft" - kraft cloud image ls - ``` +```bash title="kraft" +kraft cloud image ls +``` - ```text title="central registry" - NAME VERSION SIZE - alice/http-python312 latest 77 MB - alice/my-app latest 42 MB - ``` - - ```text title="local registry" - NAME VERSION SIZE - alice/http-python312 latest 77 MB - index..unikraft.cloud/alice/my-app latest 42 MB - ``` +```ansi title="central registry" +NAME VERSION SIZE +alice/http-python312 latest 77 MB +alice/my-app latest 42 MB +``` + +```ansi title="local registry" +NAME VERSION SIZE +alice/http-python312 latest 77 MB +index..unikraft.cloud/alice/my-app latest 42 MB +``` @@ -168,23 +177,24 @@ There may be a delay of a few minutes between pushing or removing an image and t ## Removing images +You can remove images with specific tags, or all tags at once: + - ```bash title="unikraft" - # Not supported yet for direct pushing to node registries. - # Remove images from the node itself periodically. - ``` +```bash title="unikraft" +unikraft image delete /[:] +``` - ```bash title="kraft" - kraft cloud img rm my-app - ``` +```bash title="kraft" +kraft cloud image rm /[:] +``` ## Kernel layer deduplication An image has two OCI layers: a kernel layer and a rootfs layer. -When you push an update to an existing image, `kraft` checks whether the kernel layer is already present in the registry and skips re-uploading it if so. +When you push an update to an existing image, the CLI checks whether the kernel layer is already present in the registry and skips re-uploading it if so. Only the rootfs layer travels over the wire. This makes iterative pushes faster and cheaper on bandwidth. @@ -192,31 +202,39 @@ This makes iterative pushes faster and cheaper on bandwidth. They only have a rootfs. They appear in the image list alongside regular images. -## Image addresses in instance creation +## Instance creation When creating an instance directly from an existing image, reference it by tag or digest: - + + +```bash title="unikraft" +# By tag +unikraft run --metro fra \ + -m 256M \ + -p 443:8080/http+tls \ + --image index.unikraft.io/alice/my-app:latest + +# By digest (exact version pinning) +unikraft run --metro fra \ + -m 256M \ + -p 443:8080/http+tls \ + --image index.unikraft.io/alice/my-app@sha256:278cb8b1... +``` - ```bash title="central registry" - # By tag - unikraft run --metro=fra -p 443:8080/http+tls -m 256MiB \ - --image=index.unikraft.io/alice/my-app:latest - - # By digest (exact version pinning) - unikraft run --metro=fra -p 443:8080/http+tls -m 256MiB \ - --image=index.unikraft.io/alice/my-app@sha256:278cb8b1... - ``` - - ```bash title="local registry" - # By tag - unikraft run --metro=fra -p 443:8080/http+tls -m 256MiB \ - --image=index..unikraft.cloud/alice/my-app:latest - - # By digest (exact version pinning) - unikraft run --metro=fra -p 443:8080/http+tls -m 256MiB \ - --image=index..unikraft.cloud/alice/my-app@sha256:278cb8b1... - ``` +```bash title="kraft" +# By tag +kraft cloud instance create \ + -m 256MiB \ + -p 443:8080/http+tls \ + index.unikraft.io/alice/my-app:latest + +# By digest (exact version pinning) +kraft cloud instance create \ + -m 256MiB \ + -p 443:8080/http+tls \ + index.unikraft.io/alice/my-app@sha256:278cb8b1... +``` diff --git a/pages/faq.mdx b/pages/faq.mdx index cb18b734..8203eb3e 100644 --- a/pages/faq.mdx +++ b/pages/faq.mdx @@ -63,7 +63,7 @@ Follow this [guide](/platform/services) for instructions. #### With an access token and an app, what should you do? -You'll need a Dockerfile and Kraftfile. +You'll need a `Dockerfile` and `Kraftfile`. See any of the apps/langs guides [here](/guides/bun) to see examples. @@ -75,6 +75,33 @@ See any of the apps/langs guides [here](/guides/bun) to see examples. You can control Unikraft Cloud with it as well as build unikernels and try things out locally. If you want to interact with the latest features of Unikraft Cloud, you should use the new [open source `unikraft` CLI tool](https://github.com/unikraft/cli), written in Go as well. +{/* vale off */} +#### What is `Kraftfile`? +{/* vale on */} + +A `Kraftfile` is used by the CLI toolchain to understand how to build and deploy your instance. +Typically you can use the default `Kraftfile` found in each [Unikraft Cloud example](https://github.com/unikraft-cloud/examples). +Below is a sample `Kraftfile` with a brief explanation: + +```yaml title="Kraftfile" +spec: v0.7 + +runtime: python:3.12 + +rootfs: + source: ./Dockerfile + format: erofs + +cmd: ["/usr/bin/python3", "/src/server.py"] +``` + +The `runtime` specifies one of the Unikraft Cloud runtimes (microVMs) built to run different languages and apps. +Here it specifies a Python runtime. + +The `rootfs` parameter tells the CLI to use a `Dockerfile` in the same directory to build the root filesystem as an [EROFS image](/tutorials/rootfs-formats). + +The `cmd` parameter tells the platform which command to run when deployed. + #### How's millisecond scale-to-zero achieved? @@ -181,13 +208,14 @@ When you're ready to deploy to cloud, use the [`unikraft`](https://github.com/un #### Does Unikraft Cloud work on Mac/Windows/Linux? -Yes, the CLI can run on Mac, Windows or Linux. +The CLI can run on Mac and Linux out of the box. +Windows isn't officially supported, but you can use [WSL](https://learn.microsoft.com/en-us/windows/wsl/install) to run the CLI on Windows. #### How can you track instances on Unikraft Cloud? -Unikraft provides full log support for all users. -Paying customers get access to Prometheus metrics. +Unikraft provides full `stdout` and `stderr` log support for all users. +Paying customers also get access to Prometheus metrics. {/* vale off */} @@ -214,7 +242,7 @@ Other unikernel projects exist, but most are research efforts, unmaintained, or #### Do you have a Kubernetes integration? -[Yes!](/integrations/kubernetes/) +[Yes!](/integrations/kubernetes) #### Do you have a Terraform integration? @@ -229,7 +257,7 @@ Any time you encounter a problem, it's good to first try out the latest staging There's also a chance that staging contains changes available only on non-stable nodes. -#### What keywords does the CLI ignore from a Dockerfile? +#### What keywords does the CLI ignore from a `Dockerfile`? This is the current list of ignored keywords: `EXPOSE`, `HEALTHCHECK`, `ONBUILD`, `SHELL`, `STOPSIGNAL`, `VOLUME`. The `USER` keyword is relevant for running subsequent `RUN` instructions in the Dockerfile, but not for the `ENTRYPOINT` and `CMD` commands. @@ -238,10 +266,10 @@ At runtime, Unikraft Cloud instances run as `root` user, unless explicitly switc #### How does the image size and memory relate to boot times? -When using `CPIO`, there is a [strong correlation](/tutorials/rootfs-formats) between the image size and the boot time. +When using CPIO, there is a [strong correlation](/tutorials/rootfs-formats) between the image size and the boot time. This is because it needs to load the whole image into memory first. -When using `EROFS`, there is no such correlation, as the operating system loads pages directly from disk, on demand. +When using EROFS, there is no such correlation, as the operating system loads pages directly from disk, on demand. At the same time, there is a slight correlation between big memory allocations and higher boot times, though not significant. This is irrespective of the file system used. @@ -251,19 +279,19 @@ This is irrespective of the file system used. End-result wise, cold and warm boots are no different, the instance still works. The server handles the request in both cases if the app is stateless. -The main difference appears in [stateful](/features/snapshots) apps and when considering latency, hence the `--scale-to-zero-stateful` flag. +The main difference appears in [stateful](/features/snapshots) apps and when considering latency. A cold boot equates to starting the instance every time and then stopping it if no traffic hit it for a while. A warm boot resembles the suspend and resume operations in virtual machines. The platform saves the system memory to disk and loads it when a request comes. -Warm boots offer clear advantages over cold boots, having lower boot times, no setup time, and are better in general, with the trade off of using space on the disk. +Warm boots offer clear advantages over cold boots, having lower boot times, no setup time, and are better in general, with the trade-off of using space on the disk. Thus, for most use cases, using [stateful scale-to-zero](/features/scale-to-zero#stateful-scale-to-zero) is the better alternative. Cases exist where short lived VMs are small enough to have a small boot time, but they use a lot of memory. -In this case it might be better to use cold boots, to use disk efficiently. -This should be first experimented or discussed with a Unikraft Engineer to verify it. +In this case, it might be better to use cold boots to use disk efficiently. +You should first experiment with this or discuss it with a Unikraft Engineer to verify it. #### What can you push as an image? @@ -272,7 +300,7 @@ An image is specifically a pair of a kernel and a rootfs bundled together as dif To keep things light, certain optimizations are in place to not repush existing kernels and to reuse them from your account. If these aren't automatically detected, the CLI pushes the kernel again. -At the same time, things like ROMs use the same packaging systems, but push images with a missing kernel and only a rootfs. +At the same time, things like [ROMs](/features/roms) use the same packaging systems, but push images with a missing kernel and only a rootfs. These have specific metadata that identifies them as ROMs. Finally, kernels can also run without a rootfs. @@ -296,13 +324,13 @@ Other small differences exist, which you should report to the Unikraft Team for #### How does the [rootfs format](/tutorials/rootfs-formats) and size equate to the memory used? -When using the `CPIO` file system, the memory allocated to the instance needs to be at least twice as big as the image size. -This is because the system must load and unpack the `CPIO` rootfs format in memory before using it. +When using the CPIO file system, the memory allocated to the instance needs to be at least twice as big as the image size. +This is because the system must load and unpack the CPIO rootfs format in memory before using it. This is a bare minimum and to this you need to add also the memory your app needs to run. -When using `EROFS`, there is no direct correlation between the image size and the memory needed. +When using EROFS, there is no direct correlation between the image size and the memory needed. Ideally, the memory should be at least the image size to make sure the system can load all files in memory before using. The system loads files on demand and maps them directly from disk, ensuring lower memory usage and overhead. If you have files that you know are never used, it's better to remove them from the image to slim it down. -Thus, in theory, `EROFS` uses only the allocated pages, but it's also highly dependent on the workload. +Thus, in theory, EROFS uses only the allocated pages, but it's also highly dependent on the workload. An instance with a 3GB image might use 128MB of RAM at one point, whilst an instance with a 300MB image might use 1GB of RAM at some point. diff --git a/pages/features/autokill.mdx b/pages/features/autokill.mdx index e4fe5a9a..6c36742d 100644 --- a/pages/features/autokill.mdx +++ b/pages/features/autokill.mdx @@ -7,87 +7,165 @@ Autokill automatically terminates instances or service groups when a configured It helps avoid unnecessary resource usage when instances or groups are no longer needed. You can configure autokill on: -- **Instances**: killed after a time, or after reaching a request limit -- **Instance templates**: killed if nobody clones them within a specified time -- **Service groups**: killed after the group has been empty for a specified time +- [**Instances**](/platform/instances): killed after a time, or after reaching a request limit. +- [**Instance templates**](/platform/instances#instance-templates): killed if nobody clones them within a specified time. +- [**Service groups**](/platform/services): killed after the group has been empty for a specified time. ## Instance autokill +:::caution +Autokill skips any instance that has a [delete lock](/platform/delete-locks) set. +The autokill timer doesn't fire while a delete lock is active. +::: + ### Kill after stopped duration -Automatically kill an instance after it stops and remains stopped for a given number of milliseconds: +Automatically kill an instance after it stops and remains stopped for a given time: + + -```json title="POST /instances" -{ - "autokill": { - "time_ms": 3600000 - } -} +```bash title="unikraft" +unikraft instance create --metro fra \ + --autokill time=1h ... ``` +```bash title="API" +curl -X POST \ + -H "Authorization: Bearer $UKC_TOKEN" \ + -H "Content-Type: application/json" \ + "https://api.fra.unikraft.cloud/v1/instances" \ + -d '{ + "autokill": { + "time_ms": 3600000 + }, + ... + }' +``` + + + This example kills the instance 1 hour after it stops. ### Kill after a request count Kill an instance after it serves a specified request count: -```json title="POST /instances" -{ - "autokill": { - "num_requests": 1000 - } -} + + +```bash title="unikraft" +unikraft instance create --metro fra \ + --autokill num-requests=1000 ... ``` +```bash title="API" +curl -X POST \ + -H "Authorization: Bearer $UKC_TOKEN" \ + -H "Content-Type: application/json" \ + "https://api.fra.unikraft.cloud/v1/instances" \ + -d '{ + "autokill": { + "num_requests": 1000 + }, + ... + }' +``` + + + ### Updating autokill on a stopped instance -```json title="PATCH /instances/{uuid}" -{ - "prop": "autokill", - "op": "set", - "value": { - "time_ms": 10000 - } -} + + +```bash title="unikraft" +# not yet supported, use `unikraft api` instead +unikraft api -X PATCH --metro fra \ + -d '{ + "name": "", + "prop": "autokill", + "op": "set", + "value": { + "time_ms": 10000 + } + }' ``` +```bash title="API" +curl -X PATCH \ + -H "Authorization: Bearer $UKC_TOKEN" \ + -H "Content-Type: application/json" \ + "https://api.fra.unikraft.cloud/v1/instances" \ + -d '[{ + "name": "", + "prop": "autokill", + "op": "set", + "value": { + "time_ms": 10000 + } + }]' +``` + + + ## Service group autokill Automatically kill a service group after it stays empty (no instances) for a given number of milliseconds: -```json title="POST /services" -{ - "autokill": { - "time_ms": 300000 - } -} + + +```bash title="unikraft" +unikraft service create --metro fra \ + --autokill time=5m ... ``` +```bash title="API" +curl -X POST \ + -H "Authorization: Bearer $UKC_TOKEN" \ + -H "Content-Type: application/json" \ + "https://api.fra.unikraft.cloud/v1/services" \ + -d '{ + "autokill": { + "time_ms": 300000 + }, + ... + }' +``` + + + This example kills the service group 5 minutes after its last instance leaves. ## Instance template autokill -Automatically kill an instance template if nobody clones it within a given number of milliseconds: +:::info +When you clone an instance from a template, the new instance inherits the autokill configuration. +::: -```json title="POST /instances/templates" -{ - "autokill": { - "time_ms": 86400000 - } -} -``` +Automatically kill an instance template if nobody clones it within a given number of milliseconds: -This example removes the template after 24 hours without a clone. + -## Interaction with delete locks +```bash title="unikraft" +unikraft instance template create --metro fra \ + --autokill time=24h ... +``` -Autokill skips any instance that has a [delete lock](/platform/delete-locks) set. -The autokill timer doesn't fire while a delete lock is active. +```bash title="API" +curl -X POST \ + -H "Authorization: Bearer $UKC_TOKEN" \ + -H "Content-Type: application/json" \ + "https://api.fra.unikraft.cloud/v1/instances/templates" \ + -d '{ + "autokill": { + "time_ms": 86400000 + }, + ... + }' +``` -## Autokill on instance clone + -When you clone an instance from a template, the new instance inherits the autokill configuration. +This example removes the template after 24 hours without a clone. ## Learn more -* Unikraft Cloud's [REST API reference](/api/platform/v1), in particular the sections on [instances](/api/platform/v1/instances) and [services](/api/platform/v1/service-groups) +* Unikraft Cloud's [REST API reference](/api/platform/v1), in particular the sections on [instances](/api/platform/v1/instances) and [services](/api/platform/v1/servicegroups). diff --git a/pages/features/autoscale.mdx b/pages/features/autoscale.mdx index 2b8d2f50..1daf433a 100644 --- a/pages/features/autoscale.mdx +++ b/pages/features/autoscale.mdx @@ -3,6 +3,13 @@ title: Autoscale navigation_icon: square-stack --- +{/* vale off */} +:::caution[**Limited Access**] +At the moment, autoscale **is not** enabled by default (you might get an *"Autoscale not enabled for your account"* error). +If you would like to enable it, please reach out to the [Unikraft Cloud Discord](https://kraft.cloud/discord) or send an email to [support@unikraft.com](mailto:support@unikraft.com). +::: +{/* vale on */} + Autoscaling is [load balancing](/features/load-balancing) where the *number of instances* used to handle your traffic automatically adapts to match the current traffic load. On Unikraft Cloud, scale-out (the process of adding instances to cope with increased load) happens in milliseconds. You can transparently and effortlessly handle load increase including traffic peaks. @@ -12,7 +19,7 @@ You can set autoscale on and let Unikraft Cloud handle your traffic increases an ## The basics -As with [load balancing](/features/load-balancing), autoscaling in Unikraft Cloud takes care of a *service*. +As with [load balancing](/features/load-balancing), autoscaling in Unikraft Cloud takes care of a [service](/platform/services). Services allow you to load balance traffic for an Internet-facing service like a web server by creating many instances within the same service. While you can add or remove instances to a service to scale your service, doing this manually makes it hard to react to changes in traffic load. @@ -21,19 +28,19 @@ This is where autoscale comes into play. With autoscale enabled, Unikraft Cloud takes care of the heavy lifting for you by continuously monitoring the load of your service and automatically creating or deleting instances as needed. -{/* vale off */} -:::caution[Limited Access] -At the moment, autoscale **is not** enabled by default (you might get an "Autoscale not enabled for your account" error). -If you would like to enable it, please reach out to the [Unikraft Cloud Discord](https://kraft.cloud/discord) or send an email to `support@unikraft.com`. -::: -{/* vale on */} - :::note Autoscale, as well as load balancing in general, currently supports only Internet-facing services. ::: ## Setting up autoscale +{/* vale off */} +:::caution +Autoscale is currently only partially available via the legacy CLI. +`unikraft` CLI support is coming soon. +::: +{/* vale on */} + First, create an instance, in this example using NGINX: @@ -41,61 +48,142 @@ First, create an instance, in this example using NGINX: ```bash title="unikraft" git clone https://github.com/unikraft-cloud/examples cd examples/nginx/ -unikraft run --metro=fra -p 443:8080/http+tls -m 256MiB --image=nginx:latest +unikraft run --metro fra \ + -m 256M \ + -p 443:8080/http+tls \ + --image nginx:latest ``` ```bash title="kraft" git clone https://github.com/unikraft-cloud/examples cd examples/nginx/ -kraft cloud deploy -p 443:8080 -M 256 . +kraft cloud deploy \ + -M 256Mi \ + -p 443:8080 \ + . ``` -```ansi title="" +The output shows the instance address and other details: + + + +```ansi title="unikraft" +metro: fra +name: nginx-67zbu +uuid: 8a8bc1b9-0af6-420e-a426-190dc2da9eaa +state: starting +image: /nginx +resources: + memory: 256MiB + vcpus: 1 +service: + uuid: a942b9b5-ad17-3ffe-dcd2-ef4331f9087a + name: nameless-fog-0tvh1uov + domains: + - fqdn: nameless-fog-0tvh1uov.fra.unikraft.app +networks: +- uuid: 62d9bbf0-aec8-61f6-7bdb-86edf63dd068 + private-ip: 10.0.3.3 + mac: 12:b0:c6:23:ed:15 +timestamps: + created: just now +``` + +```ansi title="kraft" [●] Deployed successfully! β”‚ - β”œ────────── name: nginx-4d7u3 - β”œ────────── uuid: 8fda2a70-6a32-4b5e-8900-4395b33d02d7 - β”œ───────── state: running - β”œ─────────── url: https://small-leaf-rafirkw7.fra.unikraft.app - β”œ───────── image: nginx@sha256:389bfa6be6455c92b61cfe429b50491373731dbdd8bd8dc79c08f985d6114758 - β”œ───── boot time: 20.36 ms - β”œ──────── memory: 256 MiB - β”œ─────── service: small-leaf-rafirkw7 - β”œ── private fqdn: nginx-4d7u3.internal - β”œ──── private ip: 172.16.6.5 - β””────────── args: /usr/bin/nginx -c /etc/nginx/nginx.conf + β”œ───────── name: nginx-67zbu + β”œ───────── uuid: 8a8bc1b9-0af6-420e-a426-190dc2da9eaa + β”œ──────── metro: https://api.fra.unikraft.cloud/v1 + β”œ──────── state: starting + β”œ─────── domain: https://nameless-fog-0tvh1uov.fra.unikraft.app + β”œ──────── image: oci://unikraft.io//nginx@sha256:f51ecc121c9ca34abb88a2bc6a69765501304f7893f7e85af15fbec3dc86e2bd + β”œ─────── memory: 256 MiB + β”œ────── service: nameless-fog-0tvh1uov + β”œ─ private fqdn: nginx-67zbu.internal + β””─── private ip: 10.0.3.3 ``` + + This single deploy or run flow does 3 things: +1. Builds the NGINX image and pushes it to the registry. 1. Creates an instance of NGINX which will serve as the **autoscale master** instance. -1. Creates a service via the `-p` flag (named `small-leaf-rafirkw7`). +1. Creates a service via the `-p` flag (named `nameless-fog-0tvh1uov`). 1. Attaches the instance to the service (the `-p` flag also does this automatically). +:::note +Autoscale requires an [instance template](/platform/instances#instance-templates) to create new instances during scale-out. +If you enable autoscale through the API, provide the template under `create_args.template`. +::: + All that's left to do now to set up autoscale is to set an autoscale configuration *policy* and to set the instance as master. Unikraft Cloud then takes care of cloning this master instance whenever load increases. -To achieve this, use the legacy CLI scale command: - + + +```bash title="unikraft" +# not yet supported, use `unikraft api` instead +unikraft api /v1/services/a942b9b5-ad17-3ffe-dcd2-ef4331f9087a/autoscale -X POST --metro fra \ + -d '{ + "min_size": 1, + "max_size": 8, + "warmup_time_ms": 1000, + "cooldown_time_ms": 1000, + "create_args": { + "template": { + "name": "" + } + } + }' + +unikraft api /v1/services/a942b9b5-ad17-3ffe-dcd2-ef4331f9087a/autoscale/policies -X POST --metro fra \ + -d '{ + "name": "scale-out-policy", + "type": { + "name": "scale-out-policy", + "metric": "cpu", + "adjustment_type": "percent", + "steps": [ + { "lower_bound": 600, "upper_bound": 800, "adjustment": 50 }, + { "lower_bound": 800, "adjustment": 100 } + ] + } + }' + +unikraft api /v1/services/a942b9b5-ad17-3ffe-dcd2-ef4331f9087a/autoscale/policies -X POST --metro fra \ + -d '{ + "name": "scale-in-policy", + "type": { + "name": "scale-in-policy", + "metric": "cpu", + "adjustment_type": "percent", + "steps": [ + { "upper_bound": 500, "adjustment": -50 } + ] + } + }' +``` ```bash title="kraft" -kraft cloud scale init small-leaf-rafirkw7 \ - --master nginx-4d7u3 \ +kraft cloud scale init nameless-fog-0tvh1uov \ + --master nginx-67zbu \ --min-size 1 \ --max-size 8 \ --warmup-time 1s \ --cooldown-time 1s -kraft cloud scale add small-leaf-rafirkw7 \ +kraft cloud scale add nameless-fog-0tvh1uov \ --name scale-out-policy \ --metric cpu \ --adjustment percent \ --step 600:800/50 \ --step 800:/100 -kraft cloud scale add small-leaf-rafirkw7 \ +kraft cloud scale add nameless-fog-0tvh1uov \ --name scale-in-policy \ --metric cpu \ --adjustment percent \ @@ -112,59 +200,149 @@ Note the following: * The third command sets the *scale-in* policy: below 50% utilization, the system reduces the number of instances by half (note the `-` sign for scale-in). :::note - The intervals that autoscale uses for making scale-out and scale-in decisions use the `--warmup-time` and `--cooldown-time` parameters of the scale init command, in units of milliseconds. Refer to the API autoscale [reference](/api/platform/v1/autoscale) for more details. - -::: - -:::note - Keep in mind that a few restrictions apply to how you define scale-in/scale-out steps. -You can find the documentation [here](/api/platform/v1/autoscale) at the bottom of the section. - ::: +To check it's working, you can use the legacy CLI `scale get` command to list the autoscale properties of the service: -## Testing it - -To check it's working, you can use the legacy CLI scale get command to list the autoscale properties of the service: + - +```bash title="unikraft" +# not yet supported, use `unikraft api` instead +unikraft api /v1/services/a942b9b5-ad17-3ffe-dcd2-ef4331f9087a/autoscale -X GET --metro fra +``` ```bash title="kraft" -kraft cloud scale get small-leaf-rafirkw7 +kraft cloud scale get nameless-fog-0tvh1uov ``` You should see output like: -```ansi title="" + + +```json title="unikraft" +{ + "status": "success", + "data": { + "service_groups": [ + { + "status": "success", + "uuid": "5ca059ec-a24a-41f2-8413-f09bc58730ca", + "name": "nameless-fog-0tvh1uov", + "enabled": true, + "min_size": 1, + "max_size": 8, + "warmup_time_ms": 1000, + "cooldown_time_ms": 1000, + "template": { + "name": "nginx-template", + "uuid": "8a8bc1b9-0af6-420e-a426-190dc2da9eaa" + }, + "policies": [ + { + "name": "scale-out-policy", + "enabled": true, + "metric": "cpu", + "adjustment_type": "percent", + "steps": [ + { + "adjustment": 50, + "lower_bound": 600, + "upper_bound": 800 + }, + { + "adjustment": 100, + "lower_bound": 800 + } + ] + }, + { + "name": "scale-in-policy", + "enabled": true, + "metric": "cpu", + "adjustment_type": "percent", + "steps": [ + { + "adjustment": -50, + "upper_bound": 500 + } + ] + } + ] + } + ] + } +} +``` + +```ansi title="kraft" uuid: 5ca059ec-a24a-41f2-8413-f09bc58730ca - name: small-leaf-rafirkw7 + name: nameless-fog-0tvh1uov enabled: true min size: 0 max size: 8 warmup (ms): 1000 cooldown (ms): 1000 - master: f840ac12-f485-4f02-9f33-6a0a7de46f1f + master: 8a8bc1b9-0af6-420e-a426-190dc2da9eaa policies: scale-out-policy;scale-in-policy ``` -To list an individual policy, use the legacy CLI scale get command as follows: + - +To list an individual policy, use the legacy CLI `scale get` command as follows: + + + +```bash title="unikraft" +# not yet supported, use `unikraft api` instead +unikraft api /v1/services/a942b9b5-ad17-3ffe-dcd2-ef4331f9087a/autoscale/policies/scale-out-policy -X GET --metro fra +``` ```bash title="kraft" -kraft cloud scale get --policy scale-out-policy small-leaf-rafirkw7 +kraft cloud scale get --policy scale-out-policy nameless-fog-0tvh1uov ``` You should see output like: -```ansi title="" + + +```json title="unikraft" +{ + "status": "success", + "data": { + "policies": [ + { + "status": "success", + "policy": { + "name": "scale-out-policy", + "enabled": true, + "metric": "cpu", + "adjustment_type": "percent", + "steps": [ + { + "adjustment": 50, + "lower_bound": 600, + "upper_bound": 800 + }, + { + "adjustment": 100, + "lower_bound": 800 + } + ] + } + } + ] + } +} +``` + +```ansi title="kraft" adjustment_type: percent enabled: true metric: cpu @@ -179,75 +357,47 @@ steps: type: step ``` -You can further check that the master instance is on `standby` (scaled to zero), assuming your service hasn't received any traffic yet. -You can get the UUID of your master instance from the legacy CLI scale get command above. - - - -```bash title="unikraft" -unikraft instances get f840ac12-f485-4f02-9f33-6a0a7de46f1f -o list -``` - -```bash title="kraft" -kraft cloud instance get f840ac12-f485-4f02-9f33-6a0a7de46f1f -o list -``` - -You should see output like: - -```ansi title="" - uuid: f840ac12-f485-4f02-9f33-6a0a7de46f1f - name: nginx-9mbf2 - fqdn: restless-resonance-0oo7m7s8.fra.unikraft.app - private ip: 172.16.6.4 - state: standby - created at: 30 minutes ago - image: nginx@sha256:d4325c1f1a472c511723148adc380d491029f4c98a2367fbeff628c6456d4180 - memory: 256 MiB - args: /usr/bin/nginx -c /etc/nginx/nginx.conf - env: - volumes: - service : 5ca059ec-a24a-41f2-8413-f09bc58730ca - boot time: 19465us -``` - -Note the value of the `state` field. -Now to make sure the service is up, `curl` the service address: - -```bash title="" -curl https://small-leaf-rafirkw7.fra.unikraft.app -``` - -You should get an immediate response, even though the instance was on `standby`. -You can use a watch command to see if you catch the instance changing state from `standby` to `running`: - - - -```bash title="unikraft" -unikraft instances list --watch -``` - -```bash title="kraft" -watch --color -n 0.5 kraft cloud instance list -``` - - +{/* TODO: add example */} ## Policy types Four autoscale policy types are available. A service can have more than one policy active at the same time. +:::caution +Only the `step` policy is currently available via the legacy CLI, but all policies are available via the API. +::: + ### Step policy The `step` policy scales instances based on metric thresholds. You define up to **4 steps**, each specifying a lower bound, upper bound, and the scaling change to apply when the metric falls in that range. -You should order steps by lower bound with no gaps between them and no overlaps. +You should order steps by lower bound with no gaps between them and no overlaps: -```bash title="" -kraft cloud scale add \ + + +```bash title="unikraft" +# not yet supported, use `unikraft api` instead +unikraft api /v1/services/a942b9b5-ad17-3ffe-dcd2-ef4331f9087a/autoscale/policies -X POST --metro fra \ + -d '{ + "name": "my-step-policy", + "type": { + "name": "my-step-policy", + "metric": "cpu", + "adjustment_type": "change", + "steps": [ + { "lower_bound": 600, "upper_bound": 800, "adjustment": 2 }, + { "lower_bound": 800, "adjustment": 4 } + ] + } + }' +``` + +```bash title="kraft" +kraft cloud scale add nameless-fog-0tvh1uov \ --name my-step-policy \ --type step \ --metric cpu \ @@ -256,7 +406,7 @@ kraft cloud scale add \ --step 800:/4 ``` -#### Metrics + The following metrics can drive a step policy: @@ -266,8 +416,6 @@ The following metrics can drive a step policy: | `inflight_reqs` | Number of requests the platform is processing across all instances | | `reqs_per_sec` | Request throughput in requests per second | -#### Scale change types - Step policies support three scaling change types: | Type | Description | @@ -282,58 +430,124 @@ Step policies support three scaling change types: The `on-demand` policy creates a new instance immediately when an incoming request finds no available instances. This prevents request queuing but introduces cold start delays. -```json title="POST /services" -{ - ... - "policies": [ - { - "name": "scale-out", - "type": "on_demand" - } - ] - ... -} + + +```bash title="unikraft" +# not yet supported, use `unikraft api` instead +unikraft api /v1/services -X POST --metro fra \ + -d '{ + "policies": [ + { + "name": "scale-out", + "type": "on_demand" + } + ], + ... + }' +``` + +```bash title="API" +curl -X POST \ + -H "Authorization: Bearer $UKC_TOKEN" \ + -H "Content-Type: application/json" \ + "https://api.fra.unikraft.cloud/v1/services" \ + -d '{ + "policies": [ + { + "name": "scale-out", + "type": "on_demand" + } + ], + ... + }' ``` + + ### Create policy The `create` policy provisions a new VM when an instance exceeds the `num_requests` threshold. Setting `replace` to true deletes the original VM after the new one starts. -```json title="POST /services" -{ - ... - "policies": [ - { - "name": "create", - "type": "create", - "replace": true, - "num_requests": 100 - } - ] - ... -} + + +```bash title="unikraft" +# not yet supported, use `unikraft api` instead +unikraft api /v1/services -X POST --metro fra \ + -d '{ + "policies": [ + { + "name": "create", + "type": "create", + "replace": true, + "num_requests": 100 + } + ], + ... + }' +``` + +```bash title="API" +curl -X POST \ + -H "Authorization: Bearer $UKC_TOKEN" \ + -H "Content-Type: application/json" \ + "https://api.fra.unikraft.cloud/v1/services" \ + -d '{ + "policies": [ + { + "name": "create", + "type": "create", + "replace": true, + "num_requests": 100 + } + ], + ... + }' ``` + + ### Idle policy The `idle` policy scales in (removes instances) when the service has been idleβ€”receiving no requestsβ€”for a configurable period. -```json title="POST /services" -{ - ... - "policies": [ - { - "name": "scale-in", - "type": "idle", - "idle_time_ms": 1000 - } - ] - ... -} + + +```bash title="unikraft" +# not yet supported, use `unikraft api` instead +unikraft api /v1/services -X POST --metro fra \ + -d '{ + "policies": [ + { + "name": "scale-in", + "type": "idle", + "idle_time_ms": 1000 + } + ], + ... + }' ``` +```bash title="API" +curl -X POST \ + -H "Authorization: Bearer $UKC_TOKEN" \ + -H "Content-Type: application/json" \ + "https://api.fra.unikraft.cloud/v1/services" \ + -d '{ + "policies": [ + { + "name": "scale-in", + "type": "idle", + "idle_time_ms": 1000 + } + ], + ... + }' +``` + + + ## Learn more * The [CLI reference](/docs/cli/unikraft) and the [legacy CLI reference](/docs/cli/kraft/overview). diff --git a/pages/features/cron-jobs.mdx b/pages/features/cron-jobs.mdx index 0bf2f428..ac95cd23 100644 --- a/pages/features/cron-jobs.mdx +++ b/pages/features/cron-jobs.mdx @@ -6,14 +6,20 @@ navigation_icon: clock Scheduled operations let you automatically start, stop, delete, or exec a command in instances on a calendar-based schedule. Each scheduled operation specifies a name, a calendar expression, and an action (`start`, `stop`, `delete`, or `exec`). -Each instance stores its own schedules, and cloning preserves them. +:::info +When you clone an instance from a [template](/platform/instances#instance-templates), the clone inherits its scheduled operations. +::: + +:::caution +You can currently only manage schedules via the [API](/api/platform/v1/instances#create-instance). +::: ## Calendar expression format -Unikraft Cloud uses systemd calendar events (see [systemd.time(7)](https://www.man7.org/linux/man-pages/man7/systemd.time.7.html)). +Unikraft Cloud uses systemd calendar events (see [`systemd.time(7)`](https://www.man7.org/linux/man-pages/man7/systemd.time.7.html)). Calendar expressions have the following fields: -``` +```text title="" [weekday] [[year-]month-day] [hour:minute[:second]] ``` @@ -29,87 +35,193 @@ The expression syntax supports ranges, steps, and comma-separated lists: ## Setting a schedule at instance creation -Pass `schedules` in the create request. +Pass `schedules` in the [create request](/api/platform/v1/instances#create-instance). For example, to start an instance every day at 09:00 UTC and stop it at 18:00 UTC: -```json title="POST /instances" -{ - "image": "...", - "schedules": [ - { - "name": "morning-start", - "when": "*-*-* 09:00:00", - "action": "start" - }, - { - "name": "evening-stop", - "when": "*-*-* 18:00:00", - "action": "stop" - } - ] -} + +```bash title="unikraft" +# not yet supported, use `unikraft api` instead +unikraft api /v1/instances -X POST --metro fra \ + -d '{ + "image": "...", + "schedules": [ + { + "name": "morning-start", + "when": "*-*-* 09:00:00", + "action": "start" + }, + { + "name": "evening-stop", + "when": "*-*-* 18:00:00", + "action": "stop" + } + ] + }' +``` +```bash title="API" +curl -X POST \ + -H "Authorization: Bearer $UKC_TOKEN" \ + -H "Content-Type: application/json" \ + "https://api.fra.unikraft.cloud/v1/instances" \ + -d '{ + "image": "...", + "schedules": [ + { + "name": "morning-start", + "when": "*-*-* 09:00:00", + "action": "start" + }, + { + "name": "evening-stop", + "when": "*-*-* 18:00:00", + "action": "stop" + } + ] + }' ``` + To run a command inside the instance every night at midnight, use the `exec` action with an `args` array: -```json title="POST /instances" -{ - "image": "...", - "schedules": [ - { - "name": "nightly-cleanup", - "when": "*-*-* 00:00:00", - "action": "exec", - "args": ["/bin/sh", "-c", "rm -rf /tmp/*"] - } - ] -} + +```bash title="unikraft" +# not yet supported, use `unikraft api` instead +unikraft api /v1/instances -X POST --metro fra \ + -d '{ + "image": "...", + "schedules": [ + { + "name": "nightly-cleanup", + "when": "*-*-* 00:00:00", + "action": "exec", + "args": ["/bin/sh", "-c", "rm -rf /tmp/*"] + } + ] + }' +``` +```bash title="API" +curl -X POST \ + -H "Authorization: Bearer $UKC_TOKEN" \ + -H "Content-Type: application/json" \ + "https://api.fra.unikraft.cloud/v1/instances" \ + -d '{ + "image": "...", + "schedules": [ + { + "name": "nightly-cleanup", + "when": "*-*-* 00:00:00", + "action": "exec", + "args": ["/bin/sh", "-c", "rm -rf /tmp/*"] + } + ] + }' ``` + ## Updating schedules of an existing instance -Add, set, or remove scheduled operations via `PATCH`: - -```json title="PATCH /instances/{uuid}" -{ - "prop": "schedules", - "op": "add", - "value": [ - { - "name": "weekend-stop", - "when": "Sat,Sun *-*-* 20:00:00", - "action": "stop" - } - ] -} +Add, set, or remove scheduled operations via [PATCH](/api/platform/v1/instances#update-instance-by-uuid): + + +```bash title="unikraft" +# not yet supported, use `unikraft api` instead +unikraft api /v1/instances/ -X PATCH --metro fra \ + -d '{ + "prop": "schedules", + "op": "add", + "value": [ + { + "name": "weekend-stop", + "when": "Sat,Sun *-*-* 20:00:00", + "action": "stop" + } + ] + }' ``` +```bash title="API" +curl -X PATCH \ + -H "Authorization: Bearer $UKC_TOKEN" \ + -H "Content-Type: application/json" \ + "https://api.fra.unikraft.cloud/v1/instances/" \ + -d '{ + "prop": "schedules", + "op": "add", + "value": [ + { + "name": "weekend-stop", + "when": "Sat,Sun *-*-* 20:00:00", + "action": "stop" + } + ] + }' +``` + Add an `exec` schedule the same way, but include `args` with the command to run: -```json title="PATCH /instances/{uuid}" -{ - "prop": "schedules", - "op": "add", - "value": [ - { - "name": "hourly-healthcheck", - "when": "*-*-* *:00:00", - "action": "exec", - "args": ["/usr/bin/healthcheck"] - } - ] -} + +```bash title="unikraft" +# not yet supported, use `unikraft api` instead +unikraft api /v1/instances/ -X PATCH --metro fra \ + -d '{ + "prop": "schedules", + "op": "add", + "value": [ + { + "name": "hourly-healthcheck", + "when": "*-*-* *:00:00", + "action": "exec", + "args": ["/usr/bin/healthcheck"] + } + ] + }' ``` - -```json title="PATCH /instances/{uuid}" -{ - "prop": "schedules", - "op": "del", - "value": [ - "morning-start" - ] -} +```bash title="API" +curl -X PATCH \ + -H "Authorization: Bearer $UKC_TOKEN" \ + -H "Content-Type: application/json" \ + "https://api.fra.unikraft.cloud/v1/instances/" \ + -d '{ + "prop": "schedules", + "op": "add", + "value": [ + { + "name": "hourly-healthcheck", + "when": "*-*-* *:00:00", + "action": "exec", + "args": ["/usr/bin/healthcheck"] + } + ] + }' ``` + + + +```bash title="unikraft" +# not yet supported, use `unikraft api` instead +unikraft api /v1/instances/ -X PATCH --metro fra \ + -d '{ + "prop": "schedules", + "op": "del", + "value": [ + "morning-start" + ] + }' +``` +```bash title="API" +curl -X PATCH \ + -H "Authorization: Bearer $UKC_TOKEN" \ + -H "Content-Type: application/json" \ + "https://api.fra.unikraft.cloud/v1/instances/" \ + -d '{ + "prop": "schedules", + "op": "del", + "value": [ + "morning-start" + ] + }' +``` + ## Actions @@ -120,12 +232,10 @@ Add an `exec` schedule the same way, but include `args` with the command to run: | `delete` | Delete the instance at the scheduled time | | `exec` | Execute a command inside the instance at the scheduled time. Requires the `args` field. | -### The `args` field - The `args` field is an array of strings specifying the command to run when using the `exec` action. The first element is the executable and the remaining elements are its arguments. -```json +```json title="" { "name": "daily-report", "when": "*-*-* 06:00:00", @@ -136,12 +246,6 @@ The first element is the executable and the remaining elements are its arguments No need to specify it for all other actions. -## Notes - -- Each schedule has a name that must be unique within an instance. -- An instance can have more than one scheduled operation. -- When you clone an instance from a template, the clone inherits its scheduled operations. - ## Learn more * Unikraft Cloud's [REST API reference](/api/platform/v1), in particular the section on [instances](/api/platform/v1/instances). diff --git a/pages/features/forking.mdx b/pages/features/forking.mdx index 5fa51227..1e3ce0ea 100644 --- a/pages/features/forking.mdx +++ b/pages/features/forking.mdx @@ -3,16 +3,14 @@ title: Instance Forking navigation_icon: git-fork --- -:::caution +:::caution[**Limited Access**] Instance forking is a preview feature and isn't yet available on stable. The interface described here reflects the current implementation and may change before general availability. ::: Instance forking lets a running instance create an independent copy of itself at a snapshot boundary. Both the original instance (the **parent**) and the copy (the **child**) resume execution from the same memory state. -This works like `fork(2)` in a POSIX process. - -The app inside an instance triggers the fork through a filesystem interface. +This works like [`fork(2)`](https://man7.org/linux/man-pages/man2/fork.2.html) in a POSIX process. ## Filesystem interface @@ -48,12 +46,12 @@ This is how an app determines whether it's the parent or the child after a fork. ### Concurrent fork requests -The `ukp-fuse` daemon serializes fork operations. +The platform serializes fork operations. Only one fork can run at a time. -If a second fork request arrives while one runs, the daemon queues it. +If a second fork request arrives while one runs, the platform queues it. If the queue fills up, the write returns `EBUSY`. -The daemon processes queued requests in order once the current fork completes. +The platform processes queued requests in order once the current fork completes. ## Tracking child instances @@ -82,7 +80,7 @@ After the write completes, reading `wait` returns the numeric exit code. If the child has already stopped by the time you write to `wait`, the write completes immediately. Many processes can wait on the same child concurrently. -The daemon notifies each one when the child exits. +The platform notifies each one when the child exits. For non-blocking usage, `poll()` on the `wait` file descriptor returns `POLLIN` when the exit status becomes available. @@ -110,10 +108,10 @@ Possible states and their numeric values: ## Child lifecycle -- Children have `delete-on-stop` enabled automatically. -The platform removes them when they stop. +- Children have [`delete-on-stop`](/platform/instances#delete-on-stop) enabled automatically. + The platform removes them when they stop. - The child's `/uk/libukp/vm/` directory starts empty. -The child doesn't inherit any children from the parentβ€”it only sees children from forks it starts itself. + The child doesn't inherit any children from the parentβ€”it only sees children from forks it starts itself. - A parent can have many children alive at the same time. - A child can itself fork, becoming a parent to its own children. @@ -125,12 +123,7 @@ The child doesn't inherit any children from the parentβ€”it only sees children f | `EIO` | Hardware signal to the platform failed, or the request belonged to a parent before a fork occurred. | | `ESTALE` | A child VM directory access occurred after the child's cleanup (for example, after the current instance became a child via fork). | -## Constraints - -- The REST API and `kraft` can't trigger a fork. -You must start it from within the instance via the `/uk/libukp/fork` file. - ## Learn more -- [Instance templates](/platform/instances#instance-templates): a related snapshot-based feature for creating instances from a stopped template -- [Instances](/platform/instances) +* [Instance templates](/platform/instances#instance-templates): a related snapshot-based feature for creating instances from a stopped template. +* Unikraft Cloud's [REST API reference](/api/platform/v1), in particular the section on [instances](/api/platform/v1/instances). diff --git a/pages/features/load-balancing.mdx b/pages/features/load-balancing.mdx index f81c5f62..8033837d 100644 --- a/pages/features/load-balancing.mdx +++ b/pages/features/load-balancing.mdx @@ -17,9 +17,9 @@ You can remove instances from a service at any time, and, when you do, Unikraft The limits apply **per instance**. For HTTP services (that is, using the `http` [handler](/platform/services#handlers)), the system checks each individual in-flight request against the limit, but not the underlying TCP connection. For TCP services, the system counts the individual open connections. -In the following, the term request refers to both requests and connections. +In the following, the term request *refers* to both requests and connections. -The load balancer uses the soft limit to decide when to wake up another [standby](/platform/instances#instance-states) instance. +The load balancer uses the soft limit to decide when to wake up another [`standby`](/platform/instances#instance-states) instance. For example, if you set the soft limit to 5 and the service consists of 2 standby instances, one of the instances can receive up to 5 concurrent requests. The 6th parallel request wakes up the second instance. If there are no more standby instances to wake up, the number of requests assigned to each instance will exceed the soft limit. @@ -41,116 +41,204 @@ For example, use NGINX as the app: git clone https://github.com/unikraft-cloud/examples cd examples/nginx/ unikraft build . --output /nginx:latest -unikraft run --metro=fra -p 443:8080/http+tls -m 256MiB --image=/nginx:latest +unikraft run --metro fra \ + -m 256M \ + -p 443:8080/http+tls \ + --image /nginx:latest ``` ```bash title="kraft" git clone https://github.com/unikraft-cloud/examples cd examples/nginx/ -kraft cloud deploy -p 443:8080 -M 256 . +kraft cloud deploy \ + -M 256Mi \ + -p 443:8080 \ + . ``` This single command will (a) create a service via the `-p` flag and (b) start an NGINX instance: -```ansi title="" + + +```ansi title="unikraft" +metro: fra +name: nginx-67zbu +uuid: 8a8bc1b9-0af6-420e-a426-190dc2da9eaa +state: starting +image: /nginx +resources: + memory: 256MiB + vcpus: 1 +service: + uuid: a942b9b5-ad17-3ffe-dcd2-ef4331f9087a + name: nameless-fog-0tvh1uov + domains: + - fqdn: nameless-fog-0tvh1uov.fra.unikraft.app +networks: +- uuid: 62d9bbf0-aec8-61f6-7bdb-86edf63dd068 + private-ip: 10.0.3.3 + mac: 12:b0:c6:23:ed:15 +timestamps: + created: just now +``` + +```ansi title="kraft" [●] Deployed successfully! β”‚ - β”œ────────── name: nginx-8ujeu - β”œ────────── uuid: d6238ac6-27d2-47b3-8a45-c6cac99fb4ef - β”œ───────── state: running - β”œ─────────── url: https://wandering-shape-n6mhimgn.fra.unikraft.app - β”œ───────── image: nginx@sha256:269192f523dca7498423bc54676ab08e415e9c7442d1bd3d65f07ab5e50a43d - β”œ───── boot time: 20.18 ms - β”œ──────── memory: 256 MiB - β”œ─────── service: wandering-shape-n6mhimgn - β”œ── private fqdn: nginx-8ujeu.internal - β”œ──── private ip: 172.16.6.7 - β””────────── args: /usr/bin/nginx -c /etc/nginx/nginx.conf + β”œ───────── name: nginx-67zbu + β”œ───────── uuid: 8a8bc1b9-0af6-420e-a426-190dc2da9eaa + β”œ──────── metro: https://api.fra.unikraft.cloud/v1 + β”œ──────── state: starting + β”œ─────── domain: https://nameless-fog-0tvh1uov.fra.unikraft.app + β”œ──────── image: oci://unikraft.io//nginx@sha256:f51ecc121c9ca34abb88a2bc6a69765501304f7893f7e85af15fbec3dc86e2bd + β”œ─────── memory: 256 MiB + β”œ────── service: nameless-fog-0tvh1uov + β”œ─ private fqdn: nginx-67zbu.internal + β””─── private ip: 10.0.3.3 ``` -With this in place, it's now time to start a second instance and attach it to the created service (in this case, named `wandering-shape-n6mhimgn`): + + + +With this in place, it's now time to start a second instance and attach it to the created service (in this case, named `nameless-fog-0tvh1uov`): ```bash title="unikraft" -cd examples/nginx/ -unikraft run --metro=fra --service wandering-shape-n6mhimgn -m 256MiB --image=/nginx:latest +unikraft run --metro fra \ + -m 256M \ + --service nameless-fog-0tvh1uov \ + --image /nginx:latest ``` ```bash title="kraft" -cd examples/nginx/ -kraft cloud deploy --service wandering-shape-n6mhimgn -M 256 . +kraft cloud instance create \ + --start \ + -M 256Mi \ + --service nameless-fog-0tvh1uov \ + /nginx:latest ``` :::tip -If a prompt appears saying "deployment already exists: what would you like to do with the 1 existing instances," choose `keep`. +If a prompt appears saying *"deployment already exists: what would you like to do with the 1 existing instances"*, choose `keep`. ::: -The command's output should look like this: +You should see another instance starting up, with the same service and domain as the first instance: + + + +```ansi title="unikraft" +metro: fra +name: nginx-0s295 +uuid: 9b1c8e5c-7a0d-4f1e-9c3a-2b8d9f0e5a1f +state: starting +image: /nginx +resources: + memory: 256MiB + vcpus: 1 +service: + uuid: a942b9b5-ad17-3ffe-dcd2-ef4331f9087a + name: nameless-fog-0tvh1uov + domains: + - fqdn: nameless-fog-0tvh1uov.fra.unikraft.app +networks: +- uuid: adf1f17d-bad1-49b2-a126-ed730e06379a + private-ip: 10.0.1.4 + mac: 12:b0:0a:00:01:05 +timestamps: + created: just now +``` -```ansi title="" +```ansi title="kraft" [●] Deployed successfully! β”‚ - β”œ────────── name: nginx-djta3 - β”œ────────── uuid: 06c972a6-a117-4b07-8eba-9389b4cccb42 - β”œ───────── state: running - β”œ─────────── url: https://wandering-shape-n6mhimgn.fra.unikraft.app - β”œ───────── image: nginx@sha256:c00c11a5cbd6a3020dd4d9703fbeb2a2f2aab37f18f7a0ba9c66db5a71897c3a - β”œ───── boot time: 20.46 ms - β”œ──────── memory: 256 MiB - β”œ______─ service: wandering-shape-n6mhimgn - β”œ── private fqdn: nginx-djta3.internal - β”œ──── private ip: 172.16.6.3 - β””────────── args: /usr/bin/nginx -c /etc/nginx/nginx.conf + β”œ───────── name: nginx-0s295 + β”œ───────── uuid: 9b1c8e5c-7a0d-4f1e-9c3a-2b8d9f0e5a1f + β”œ──────── metro: https://api.fra.unikraft.cloud/v1 + β”œ──────── state: starting + β”œ─────── domain: https://nameless-fog-0tvh1uov.fra.unikraft.app + β”œ──────── image: oci://unikraft.io//nginx@sha256:f51ecc121c9ca34abb88a2bc6a69765501304f7893f7e85af15fbec3dc86e2bd + β”œ─────── memory: 256 MiB + β”œ────── service: nameless-fog-0tvh1uov + β”œ─ private fqdn: nginx-0s295.internal + β””─── private ip: 10.0.1.4 ``` -Both the `url` and `service` fields in the 2 instances are the same. + + To check that it worked, run the following command: ```bash title="unikraft" -unikraft services list +unikraft services get nameless-fog-0tvh1uov ``` ```bash title="kraft" -kraft cloud service list -o list +kraft cloud service get nameless-fog-0tvh1uov ``` You should see output such as: -```ansi title="" - uuid: 954b4a52-fc51-4eac-b5c9-1fc2a368a237 - name: wandering-shape-n6mhimgn - fqdn: wandering-shape-n6mhimgn.fra.unikraft.app - services: 443:8080/tls+http 80:443/http+redirect - instances: 06c972a6-a117-4b07-8eba-9389b4cccb42 d6238ac6-27d2-47b3-8a45-c6cac99fb4ef - created at: 14 minutes ago + + +```ansi title="unikraft" +metro: fra +name: nameless-fog-0tvh1uov +uuid: a942b9b5-ad17-3ffe-dcd2-ef4331f9087a +persistent: true +limits: + soft: 1 + hard: 65535 +timestamps: + created: just now +domains: +- fqdn: nameless-fog-0tvh1uov.fra.unikraft.app +instances: +- name: nginx-0s295 + uuid: 9b1c8e5c-7a0d-4f1e-9c3a-2b8d9f0e5a1f +- name: nginx-67zbu + uuid: 8a8bc1b9-0af6-420e-a426-190dc2da9eaa +services: +- source: 443 + destination: 8080 + handlers: ["tls", "http"] +``` + +```ansi title="kraft" + uuid: a942b9b5-ad17-3ffe-dcd2-ef4331f9087a + name: nameless-fog-0tvh1uov + fqdn: nameless-fog-0tvh1uov.fra.unikraft.app + services: 443:8080/tls+http + instances: nginx-0s295 nginx-67zbu + created at: 2026-06-05T12:41:16Z persistent: true + soft limit: 1 + hard limit: 65535 ``` -Note the two instances (their UUIDs) under the `instances` field. + + +Note the two instances under the `instances` field. You're now load balancing across 2 NGINX instances! :::tip -Currently it's impossible to set the service's name via the deploy or run flow. -If you'd like to set the service's name, first use the CLI to create the service. -You can use the `--service` flag to attach the instance to the service, as outlined in this [guide](/platform/services). -::: +To ensure that stopping an instance doesn't drop existing connections, use Unikraft Cloud's draining feature by stopping an instance with a drain timeout: -:::tip -To ensure that stopping an instance doesn't drop existing connections, use Unikraft Cloud's draining feature by stopping an instance with a drain timeout. + - +```bash title="unikraft" +unikraft instance stop --drain-timeout 5s my-instance +``` ```bash title="kraft" -kraft cloud instance stop -w +kraft cloud instance stop --wait 5s my-instance ``` @@ -181,4 +269,6 @@ This is because `i-3` now becomes the only instance with only 1 connection (assu ## Learn more * The [CLI reference](/docs/cli/unikraft) and the [legacy CLI reference](/docs/cli/kraft/overview). -* Unikraft Cloud's [REST API reference](/api/platform/v1), and in particular the section on services. +* Unikraft Cloud's [REST API reference](/api/platform/v1), and in particular the section on [services](/api/platform/v1/servicegroups). +* [Instances](/platform/instances) documentation. +* [Service groups](/platform/services) documentation. diff --git a/pages/features/roms.mdx b/pages/features/roms.mdx index 0dbd50aa..bcf37e6b 100644 --- a/pages/features/roms.mdx +++ b/pages/features/roms.mdx @@ -3,10 +3,11 @@ title: ROMs navigation_icon: hard-drive --- -:::caution[**DISCLAIMER**] -The ROM feature isn't currently enabled for the public Unikraft Cloud offering. -As such, the CLI can't entirely leverage this feature. -For boxes where it's enabled, use it via the [Unikraft Cloud API](/api/platform/v1). +import { Tabs, TabsContent, TabsList, TabsTrigger } from "zudoku/ui/Tabs" + +:::caution[**Limited Access**] +ROMs are only available in BYOC or on-prem Unikraft Cloud installations. +General availability on the public Unikraft Cloud offering is coming soon. ::: Unikraft Cloud supports the ability to attach **Read-Only Memory (ROM)** blobs to instances. @@ -28,32 +29,52 @@ The ROM workflow consists of two main components: This separation allows you to maintain one base image while deploying many different instances with different data. -:::note +:::tip By default, the app running inside the base image needs to mount the attached ROM device. ROM blobs appear as block devices at paths like `/dev/ukp_rom_`, where `` is the ROM name you specify when creating the instance. -For convenience, you can use **automounting** by setting the `at` field on a ROM entry. -When `at` is present, the platform automatically mounts the ROM at the given path before the instance starts, so your app can read files from it directly without any manual mount step. +For convenience, this guide uses **automounting** by setting the `at` field on a ROM entry. +When `at` is present, the platform automatically mounts the ROM at the given path on boot, so your app can read files from it directly without any manual mount step. ::: ## Setup This example shows how to deploy Python functions using ROMs on Unikraft Cloud. -Ensure you have the `kraft` CLI installed and configured with your Unikraft Cloud account. -Set the following environment variables: +Ensure you have the CLI installed and configured with your Unikraft Cloud account. +Make sure to log into Unikraft Cloud and pick a [metro](/platform/metros) close to you. +This guide uses `fra` (Frankfurt, πŸ‡©πŸ‡ͺ): -```bash title="" -export UKC_USER="" -export UKC_TOKEN="" -export UKC_METRO="fra" # or your preferred metro + + +```bash title="unikraft" +unikraft login ``` -### Base Image +```bash title="kraft" +# Set Unikraft Cloud access token +export UKC_TOKEN=token +export UKC_METRO=fra +``` -First, create a base image with a Python HTTP server that loads and executes custom Python programs from a ROM. + - +:::caution +The legacy `kraft` CLI only supports packaging ROMs. +Use the `unikraft` CLI to create instances with ROMs attached. +::: + +### Base image + +First, create a base image with a Python HTTP server that loads and executes custom Python programs from a ROM. + + + Dockerfile + Kraftfile + server.py + wrapper.sh + + ```dockerfile title="Dockerfile" FROM python:3.12 AS build @@ -91,16 +112,21 @@ COPY --from=build /usr/local/lib /usr/local/lib COPY ./wrapper.sh /wrapper.sh COPY ./server.py /src/server.py ``` + + ```yaml title="Kraftfile" -spec: v0.6 +spec: v0.7 runtime: base-compat:latest -rootfs: ./Dockerfile +rootfs: + source: ./Dockerfile + format: erofs -cmd: [ "/wrapper.sh", "/usr/bin/python3", "/src/server.py" ] +cmd: [ "/usr/bin/python3", "/src/server.py" ] ``` - + + ```python title="server.py" import argparse import importlib.util @@ -112,7 +138,7 @@ rom_module = None def load_rom_module(): global rom_module module_name = 'rom' - module_path = '/tmp/rom.py' + module_path = '/rom/rom.py' spec = importlib.util.spec_from_file_location(module_name, module_path) rom_module = importlib.util.module_from_spec(spec) @@ -151,37 +177,44 @@ def parse_args(): if __name__ == "__main__": main(parse_args()) ``` - + + ```bash title="wrapper.sh" #!/usr/bin/sh -/usr/bin/mkdir -p /tmp/ -/usr/bin/mount -t erofs /dev/ukp_rom_python_function.py /tmp/ +# Unused in this example, but left here for reference on how to mount ROMs manually if not using automounting. +/usr/bin/mkdir -p /rom/ +/usr/bin/mount -t erofs /dev/ukp_rom_python_function.py /rom/ exec "$@" ``` - + + :::tip -If you use the `at` field when attaching the ROM (see [Automounting](#automounting)), the platform handles the mount for you and you can remove `wrapper.sh` and the `mount`/`mkdir` calls entirely. +The commands below will use the `at` field to automount the ROM. +That's why the `Kraftfile` omits the `wrapper.sh` script and the app can read the ROM file directly from `/rom/rom.py`. ::: -Ensure that the `wrapper.sh` script is executable: +Package and push the base image: -```bash title="" -chmod a+x wrapper.sh -``` + -Package and push the base image: +```bash title="unikraft" +unikraft build . --output /http-python:latest +``` -```bash title="" +```bash title="kraft" kraft pkg \ --plat kraftcloud \ --arch x86_64 \ - --name "$UKC_USER"/http-python:latest \ + --name index.unikraft.io//http-python:latest \ --rootfs-type erofs \ - --push . + --push \ + . ``` + + Wait a few seconds for propagation and check that the image is present: @@ -196,9 +229,7 @@ kraft cloud image ls -{/* vale off */} ### ROM files -{/* vale on */} To showcase the benefits of using ROMs, create two files, each containing a Python function that the base image will load and execute. Create a separate directory for each of the ROMs: @@ -215,10 +246,10 @@ def function(): ``` ```yaml title="rom1/Kraftfile" -spec: v0.6 +spec: v0.7 roms: - - ./fs +- ./fs ``` ```python title="rom2/fs/rom.py" @@ -227,36 +258,42 @@ def function(): ``` ```yaml title="rom2/Kraftfile" -spec: v0.6 +spec: v0.7 roms: - - ./fs +- ./fs ``` Package and push the ROMs: -```bash title="" -cd rom1/ + + +```bash title="unikraft" +unikraft build rom1/ --output /my-rom1:latest +unikraft build rom2/ --output /my-rom2:latest +``` + +```bash title="kraft" kraft pkg \ - --no-kernel \ --plat kraftcloud \ --arch x86_64 \ - --name "$UKC_USER"/my-rom1:latest \ - --rootfs-type erofs \ - --push . - -cd ../rom2/ + --rom-type erofs \ + --name index.unikraft.io//my-rom1:latest \ + --push \ + rom1/ kraft pkg \ - --no-kernel \ --plat kraftcloud \ --arch x86_64 \ - --name "$UKC_USER"/my-rom2:latest \ - --rootfs-type erofs \ - --push . + --rom-type erofs \ + --name index.unikraft.io//my-rom2:latest \ + --push \ + rom2/ ``` -Wait a few seconds for propagation and check that the ROMs are present: + + +Wait a few seconds for upload and check that the ROMs are present: @@ -270,140 +307,209 @@ kraft cloud image ls -:::tip -The `--no-kernel` flag tells `kraft` to package only the ROM files, without the kernel, since this is data that will get attached to another image. -::: - -:::caution -This example packages the ROMs as EROFS filesystems. -If packaging the ROMs as a cpio archives (that is, `--rootfs-type cpio`), or as standalone files, you must align their size to 4096 bytes. -::: - ### Instances Create two instances with the same base image and attach different ROMs: -```bash title="" + + +```bash title="unikraft" +unikraft run --metro fra \ + --name test-http-python-rom1 \ + -m 512M \ + -p 443:8080/tls+http \ + --domain test-http-python-rom1 \ + --scale-to-zero policy=on,cooldown-time=1000,stateful=false \ + --rom name=python_function.py,image=/my-rom1:latest,at=/rom \ + --image /http-python:latest +unikraft run --metro fra \ + --name test-http-python-rom2 \ + -m 512M \ + -p 443:8080/tls+http \ + --domain test-http-python-rom2 \ + --scale-to-zero policy=on,cooldown-time=1000,stateful=false \ + --rom name=python_function.py,image=/my-rom2:latest,at=/rom \ + --image /http-python:latest +``` + +```bash title="API" curl -X POST \ - -H "Authorization: Bearer ${UKC_TOKEN}" \ + -H "Authorization: Bearer $UKC_TOKEN" \ -H "Content-Type: application/json" \ - "${UKC_METRO}/instances" \ - -d "[ + "https://api.fra.unikraft.cloud/v1/instances" \ + -d '[ + { + "name": "test-http-python-rom1", + "image": "/http-python:latest", + "service_group": { + "services": [ { - 'name': 'test-http-python-rom1', - 'image': '${UKC_USER}/http-python:latest', - 'service_group': { - 'services': [ - { - 'port': 443, - 'destination_port': 8080, - 'handlers': ['tls', 'http'] - } - ], - 'domains': [ - { - 'name': 'test-http-python-rom1' - } - ] - }, - 'memory_mb': 512, - 'scale_to_zero': { - 'policy': 'on', - 'stateful': false, - 'cooldown_time_ms': 1000 - }, - 'autostart': true, - 'roms': [ - { - 'name': 'python_function.py', - 'image': '$UKC_USER/my-rom1:latest' - } - ] - }, + "port": 443, + "destination_port": 8080, + "handlers": ["tls", "http"] + } + ], + "domains": [ { - 'name': 'test-http-python-rom2', - 'image': '${UKC_USER}/http-python:latest', - 'service_group': { - 'services': [ - { - 'port': 443, - 'destination_port': 8080, - 'handlers': ['tls', 'http'] - } - ], - 'domains': [ - { - 'name': 'test-http-python-rom2' - } - ] - }, - 'memory_mb': 512, - 'scale_to_zero': { - 'policy': 'on', - 'stateful': false, - 'cooldown_time_ms': 1000 - }, - 'autostart': true, - 'roms': [ - { - 'name': 'python_function.py', - 'image': '$UKC_USER/my-rom2:latest' - } - ] + "name": "test-http-python-rom1" + } + ] + }, + "memory_mb": 512, + "scale_to_zero": { + "policy": "on", + "stateful": false, + "cooldown_time_ms": 1000 + }, + "autostart": true, + "roms": [ + { + "name": "python_function.py", + "image": "/my-rom1:latest", + "at": "/rom" + } + ] + }, + { + "name": "test-http-python-rom2", + "image": "/http-python:latest", + "service_group": { + "services": [ + { + "port": 443, + "destination_port": 8080, + "handlers": ["tls", "http"] } - ]" + ], + "domains": [ + { + "name": "test-http-python-rom2" + } + ] + }, + "memory_mb": 512, + "scale_to_zero": { + "policy": "on", + "stateful": false, + "cooldown_time_ms": 1000 + }, + "autostart": true, + "roms": [ + { + "name": "python_function.py", + "image": "/my-rom2:latest", + "at": "/rom" + } + ] + } +]' ``` + + +:::note +The `name` field in the ROM entry is optional, and is heplful for identification purposes in the instance configuration. +::: + Check that the instances are up: - ```bash title="unikraft" - unikraft instances get test-http-python-rom1 - unikraft instances get test-http-python-rom2 - ``` +```bash title="unikraft" +unikraft instances get test-http-python-rom1 +unikraft instances get test-http-python-rom2 +``` - ```bash title="kraft" - kraft cloud instance get test-http-python-rom1 - kraft cloud instance get test-http-python-rom2 - ``` +```bash title="kraft" +kraft cloud instance get test-http-python-rom1 +kraft cloud instance get test-http-python-rom2 +``` Note the `roms` array in the instances configurations. -Each ROM is available as a readable device at `/dev/ukp_rom_python_function.py` (in the form of an EROFS filesystem in this case), which the server program mounts at `/tmp/rom.py` and executes. +Each ROM is available as a readable device at `/dev/ukp_rom_python_function.py`, in the form of an EROFS filesystem. +In this example, the platform mounts it automatically at `/rom`, and the app reads `/rom/rom.py` from that mount point. + +### Testing + +Query the instances to call the Python function from the ROM. +You should see different responses: + +```bash +curl https://test-http-python-rom1.fra.kraft.cloud +curl https://test-http-python-rom2.fra.kraft.cloud +``` +```text +Hi from 1st ROM! +Hi from 2nd ROM! +``` + +### Cleanup + +When done, remove the instances: + + + +```bash title="unikraft" +unikraft instances delete test-http-python-rom1 test-http-python-rom2 +``` + +```bash title="kraft" +kraft cloud instance rm test-http-python-rom1 test-http-python-rom2 +``` + + ## Inline ROMs Instead of pre-packaging and pushing a ROM image to a registry, you can provide file contents directly in the instance creation request. -This is useful when you create instances from a template where the snapshot locks ENV and ARGS, but you still need to inject instance-specific data. +This is useful when you create instances from a [template](/platform/instances#instance-templates) where the snapshot locks `ENV` and `ARGS`, but you still need to inject instance-specific data. + +To use inline ROMs with `unikraft`, specify a local directory as source: + +```bash title="unikraft" +unikraft run --metro fra \ + --name test-http-python-rom-inline \ + -m 512M \ + -p 443:8080/tls+http \ + --domain test-http-python-rom-inline \ + --scale-to-zero policy=on,cooldown-time=1000,stateful=false \ + --rom dir=./rom1/fs,at=/rom \ + --image /http-python:latest +``` ### API format +The API allows you to specify the contents of an inline ROM directly in the JSON request body when creating an instance. Add a `files` array to a ROM entry instead of an `image` field: -```json title="POST /instances" -{ - "image": "myuser/my-base:latest", - "roms": [ - { - "name": "my-rom", - "files": [ - { - "path": "config/settings.json", - "encoding": "text", - "data": "{\"debug\": false, \"port\": 8080}" - }, - { - "path": "certs/client.pem", - "encoding": "base64", - "data": "LS0tLS1CRUdJTi..." - } - ], - "at": "/roms/my-rom" - } - ] -} +```bash title="POST /instances" +curl -X POST \ + -H "Authorization: Bearer $UKC_TOKEN" \ + -H "Content-Type: application/json" \ + "https://api.fra.unikraft.cloud/v1/instances" \ + -d '{ + "image": "/my-base:latest", + "roms": [ + { + "name": "my-rom", + "files": [ + { + "path": "config/settings.json", + "encoding": "text", + "data": "{\"debug\": false, \"port\": 8080}" + }, + { + "path": "certs/client.pem", + "encoding": "base64", + "data": "LS0tLS1CRUdJTi..." + } + ], + "at": "/rom/my-rom" + } + ] + } ``` Each file in the `files` array has three fields: @@ -421,13 +527,17 @@ The `at` field is optional and works the same as for image-based ROMsβ€”when pre You can mix inline ROMs and image-based ROMs in the same request: -```json title="POST /instances" -{ - "image": "myuser/my-base:latest", - "roms": [ - { - "name": "app-code", - "image": "myuser/my-rom:latest", +```bash title="POST /instances" +curl -X POST \ + -H "Authorization: Bearer $UKC_TOKEN" \ + -H "Content-Type: application/json" \ + "https://api.fra.unikraft.cloud/v1/instances" \ + -d '{ + "image": "/my-base:latest", + "roms": [ + { + "name": "app-code", + "image": "/my-rom:latest", "at": "/app" }, { @@ -460,104 +570,26 @@ No per-file size limit exists. The total size limit applies to the combined size of all files in a single inline ROM. Since inline ROM data is base64-encoded in the JSON request body, the 8 MB request body limit is the practical upper bound on total data per API call. -## Automounting - -Instead of mounting the ROM device manually inside the guest, you can set the optional `at` field on any ROM entry. -The platform will then mount the ROM at the specified path before the instance starts. - -```json title="POST /instances" -{ - "image": "...", - "roms": [ - { - "name": "python_function.py", - "image": "myuser/my-rom1:latest", - "at": "/tmp" - } - ] -} -``` - -{/* vale off */} -With this configuration the EROFS filesystem is mounted at `/tmp` automatically, so `/tmp/rom.py` is available to the app without any `mount` call in a wrapper script. -In this case you can simplify or remove the `wrapper.sh` from the base image, since the platform handles the mount for you. -{/* vale on */} - -The `at` field is optional--omitting it leaves the ROM as a raw block device at `/dev/ukp_rom_` and the guest remains responsible for mounting it. - -## Inline ROMs - -Instead of referencing a pre-built image, you can supply file contents directly in the instance creation request using **inline ROMs**. -You specify an inline ROM by a `files` array instead of an `image` field. -Each entry in the array specifies the file `path` inside the ROM filesystem, the `encoding` of the provided data (`"base64"` or `"text"`), and the raw `data` string. - -```json title="POST /instances" -{ - "image": "...", - "roms": [ - { - "name": "my-rom", - "files": [ - { - "path": "test/file.txt", - "encoding": "base64", - "data": "VGhpcyBpcyBhIHRlc3QgZmlsZS4=" - }, - { - "path": "another-file.txt", - "encoding": "text", - "data": "This is another test file." - } - ], - "at": "/roms/my-rom" - } - ] -} -``` - -Inline ROMs are particularly useful when working with **instances created from templates**, where you can't change the environment variables and startup arguments of the original template. -By attaching an inline ROM you can inject configuration files, scripts, or other data into the instance at creation time without modifying the base image or the template. - -{/* vale off */} -:::note -An inline ROM is ephemeral: it's automatically deleted once the last reference to it is closed. -::: -{/* vale on */} - -### Testing - -Query the instances to call the Python function from the ROM. -You should see different responses: - -```console title="" -$ curl https://test-http-python-rom1.fra.kraft.cloud -Hi from 1st ROM! -``` - -```console title="" -$ curl https://test-http-python-rom2.fra.kraft.cloud -Hi from 2nd ROM! -``` - -### Cleanup +## Learn more -When done, remove the instances: +Use the `--help` option for detailed information on using Unikraft Cloud: ```bash title="unikraft" -unikraft instances delete test-http-python-rom1 test-http-python-rom2 +unikraft --help ``` ```bash title="kraft" -kraft cloud instance rm test-http-python-rom1 test-http-python-rom2 +kraft cloud --help ``` -## Learn more +Or visit the [CLI Reference](/docs/cli/unikraft) or the [legacy CLI reference](/docs/cli/kraft/overview). +For more information on the features used in this use case and how to use them, check out the following documentation: -* The [CLI reference](/docs/cli/unikraft) and the [legacy CLI reference](/docs/cli/kraft/overview). * Unikraft Cloud's [REST API reference](/api/platform/v1), and in particular the [instances API](/api/platform/v1/instances). -* The `kraft pkg` [command reference](https://unikraft.org/docs/cli/reference/kraft/pkg) for packaging images and ROMs. -* The [systemd `mount` man page](https://www.man7.org/linux/man-pages/man8/mount.8.html) for filesystem mount options relevant to manual mounting scenarios. +* [Tutorial](/tutorials/rootfs-volumes-roms) explaining the difference of storage media on Unikraft Cloud. +* Running [serverless functions](/use-cases/serverless-functions) packaged as ROMs on Unikraft Cloud. +* Running and customizing [game servers](/use-cases/game-servers) using ROMs on Unikraft Cloud. diff --git a/pages/features/scale-to-zero.mdx b/pages/features/scale-to-zero.mdx index 3fb831f5..2135906e 100644 --- a/pages/features/scale-to-zero.mdx +++ b/pages/features/scale-to-zero.mdx @@ -16,8 +16,7 @@ All within a negligible amount of time compared to Internet round-trip times, an By default, Unikraft Cloud reduces network and cloud stack cold start time to a minimum. Some apps take a while to initialize (for example, Spring Boot, Puppeteer, etc). -If you need to deploy such an app and still want millisecond cold starts, Unikraft Cloud offers a *stateful* feature. -Please check out [this guide](/features/scale-to-zero#stateful-scale-to-zero) for more information on how to set this up. +If you need to deploy such an app and still want millisecond cold starts, Unikraft Cloud offers a [*stateful*](/features/scale-to-zero#stateful-scale-to-zero) feature. :::tip If you add instances to a [service](/platform/services), the service will load balance traffic across them and Unikraft Cloud will wake up each instance as needed. @@ -38,16 +37,14 @@ Unikraft Cloud currently supports the following scale-to-zero policies: | `on` | Enables scale-to-zero. When there are no TCP connections or HTTP requests during the cooldown time, the instance goes into standby. | | `idle` | Same as `on`, but also puts the instance into standby when there are TCP connections established that have been inactive during the cooldown time. The connections remain established and incoming packets wake up the instance. Scale-to-zero doesn't happen while there are active HTTP requests (that is, traffic on ports which use the `http` [handler](/platform/services#handlers)). | -:::note -Unikraft Cloud only considers network traffic that's going through its proxy to control scale-to-zero. -Connections that your app actively establishes to servers on the Internet **and** within Unikraft Cloud bypass the proxy for maximum performance. -The instance will thus go into standby irrespective of the presence of and data sent over such connections. +:::info +Unikraft Cloud supports scale-to-zero for traffic going through the proxy, as well as [internal traffic](/platform/networking#internal-communication) via Private FQDNs. ::: ## Application support for scale-to-zero -Your app can use scale-to-zero without any support from your app. +Most apps can use scale-to-zero without any special support. Cases exist where making your app aware of scale-to-zero makes sense. - **Background Jobs**: For example, you want to run background jobs after your app has responded to a request (for example, send trace information to a logging server). @@ -102,7 +99,7 @@ Ensure you configure a cooldown time large enough to accommodate for potential s Before the platform suspends an instance, it can send a notification signal to the guest, giving it time to finish in-flight work before suspension begins. The platform delivers the notification over [vsock](https://man7.org/linux/man-pages/man7/vsock.7.html) on port `138`. -The platform sends the literal string `scaletozero` to the guest at the configured lead time before suspension. +It sends the literal string `scaletozero` to the guest at the configured lead time before suspension. Your app can listen on vsock port `138` and use the signal to: - Drain in-flight requests @@ -111,17 +108,45 @@ Your app can listen on vsock port `138` and use the signal to: ### Configuring the notification lead time -Use the `notify_time_ms` field in the `scale_to_zero` configuration when creating or updating an instance to specify the number of milliseconds before the scaletozero event, when the platform should send the notification signal: +When creating or updating an instance, you can specify the number of milliseconds before the scaletozero event when the platform should send the notification signal: + + +```bash title="unikraft" +unikraft run --metro fra \ + -p 443:8080/http+tls \ + --scale-to-zero policy=on,cooldown-time=1000,notify-time=2000 \ + ... +``` -```json title="POST /instances" -{ - "scale_to_zero": { - "cooldown_time_ms": 5000, - "notify_time_ms": 2000 - } -} +```bash title="API" +curl -X POST \ + -H "Authorization: Bearer $UKC_TOKEN" \ + -H "Content-Type: application/json" \ + "https://api.fra.unikraft.cloud/v1/instances" \ + -d '{ + "service_group": { + "services": [ + { + "port": 443, + "destination_port": 8080, + "handlers": [ + "tls", + "http" + ] + } + ] + }, + "scale_to_zero": { + "policy": "on", + "cooldown_time_ms": 1000, + "notify_time_ms": 2000 + }, + ... + }' ``` + + This sends the notification 2 seconds before the cooldown expires and suspension begins. :::note @@ -134,15 +159,15 @@ If `notify_time_ms` is `0` (the default), the platform skips the notification an Some apps have a long cold boot phase or experience large first response latency. For example, to run just-in-time compilation and fill caches. Using stateful scale-to-zero can dramatically reduce the response time of your service. -With stateful scale-to-zero Unikraft Cloud takes a snapshot of the instance state before putting it into standby. -When incoming network traffic triggers a wake-up, Unikraft Cloud loads the snapshot. +With stateful scale-to-zero Unikraft Cloud takes a [snapshot](/features/snapshots) of the instance state before putting it into standby. +When incoming network traffic triggers a wake-up, Unikraft Cloud loads the snapshot in milliseconds. The instance resumes execution where it left offβ€”with caches already warm. Stateful scale-to-zero can also enable scale-to-zero for apps that need to keep state for functional correctness. This works even if cold boot times are no concern. If your app doesn't require maintaining state and has a satisfactorily short boot time, you might prefer to disable stateful scale-to-zero to save disk space. -:::note +:::info The time to load an instance snapshot is constant and in the order of a few milliseconds. This is what the boot time metrics report. The system loads the actual memory contents from the snapshot only at first access. @@ -166,9 +191,7 @@ Strategies to mitigate this include: 1. **Stateful Scale-to-Zero**: [Enabling stateful mode](#stateful-scale-to-zero) saves memory to disk, reducing wake-up time for complex apps. 2. **Startup Protection**: Use the [manual disable mechanism](#application-support-for-scale-to-zero) to temporarily turn off scale-to-zero during your app's boot sequence, then re-enable it once ready to serve. -### Managing connectivity and state - -#### Continuous connections +### Continuous connections Apps using long-lived connections with frequent keep-alive messages (for example, WebSockets, HTTP/2, HMR) may prevent the instance from scaling to zero. To allow the instance to sleep: @@ -176,40 +199,9 @@ To allow the instance to sleep: * Use a more aggressive cooldown period if appropriate. * If requiring continuous connectivity, consider disabling scale-to-zero for these workloads. -#### Internal communication - -:::info -This feature is currently under active development and may change in future releases. -::: - -Currently, internal traffic via Private FQDNs doesn't trigger the scale-to-zero wake-up mechanism. -Services attempting to reach a standby instance via its Private FQDN will fail to wake it. -To ensure wake-on-request behavior for creating service-to-service communication, use the instance's **Public FQDN**. -Note that this also applies to the legacy [CLI tunnel](/docs/cli/kraft/tunnel) command, which relies on internal connectivity. - - -## Getting started - -You can enable millisecond scale-to-zero via a **label** in each of -the subdirectories' `Kraftfile` or with the `--scale-to-zero` flag in relevant -command-line tool subcommands. - -```yaml title="" -labels: - cloud.unikraft.v1.instances/scale_to_zero.policy: "on" - cloud.unikraft.v1.instances/scale_to_zero.cooldown_time_ms: 1000 -``` - -:::tip -The `--scale-to-zero-cooldown` flag tells Unikraft Cloud how long the instance must be idle before scaling to zero. -The examples include appropriate values so you don't have to worry about this label if you don't want to. -::: - -:::tip -You can disable scale-to-zero either by setting the label to `false`, or with the `--scale-to-zero=off` flag. -::: +## Example -Since Unikraft Cloud has scale-to-zero on by default, all you need to do is to start an instance normally: +You can enable millisecond scale-to-zero with the `--scale-to-zero` flag in relevant CLI subcommands. @@ -217,36 +209,74 @@ Since Unikraft Cloud has scale-to-zero on by default, all you need to do is to s git clone https://github.com/unikraft-cloud/examples cd examples/nginx/ unikraft build . --output /nginx:latest -unikraft run --metro=fra -p 443:8080/http+tls -m 128MiB --image=/nginx:latest +unikraft run --metro fra \ + -m 256M \ + -p 443:8080/http+tls \ + --scale-to-zero policy=on,cooldown-time=1000 + --image /nginx:latest ``` ```bash title="kraft" git clone https://github.com/unikraft-cloud/examples cd examples/nginx/ -kraft cloud deploy -p 443:8080 -M 256 . +kraft cloud deploy \ + -M 256Mi \ + -p 443:8080 \ + --scale-to-zero on \ + --scale-to-zero-cooldown 1s \ + . ``` -This command will create the NGINX instance with scale-to-zero enabled: +This command will create the NGINX instance with scale-to-zero enabled, with a cooldown time of 1 second: + + + +```ansi title="unikraft" +metro: fra +name: nginx-67zbu +uuid: 8a8bc1b9-0af6-420e-a426-190dc2da9eaa +state: starting +image: /nginx +resources: + memory: 256MiB + vcpus: 1 +service: + uuid: a942b9b5-ad17-3ffe-dcd2-ef4331f9087a + name: nameless-fog-0tvh1uov + domains: + - fqdn: nameless-fog-0tvh1uov.fra.unikraft.app +networks: +- uuid: 62d9bbf0-aec8-61f6-7bdb-86edf63dd068 + private-ip: 10.0.3.3 + mac: 12:b0:c6:23:ed:15 +timestamps: + created: just now +scale-to-zero: + enabled: true + policy: on + cooldown-time: 1s +``` -```ansi title="" +```ansi title="kraft" [●] Deployed successfully! β”‚ - β”œ────────── name: nginx-1a747 - β”œ────────── uuid: 66d05e09-1436-4d1f-bbe6-6dc03ae48d7a - β”œ───────── state: running - β”œ─────────── url: https://twilight-gorilla-ui5b6kwt.fra.unikraft.app - β”œ───────── image: nginx@sha256:19854a12fe97f138313cb9b4806828cae9cecf2d050077a0268d98129863f954 - β”œ───── boot time: 19.81 ms - β”œ──────── memory: 256 MiB - β”œ─────── service: twilight-gorilla-ui5b6kwt - β”œ── private fqdn: nginx-1a747.internal - β”œ──── private ip: 172.16.6.1 - β””────────── args: /usr/bin/nginx -c /etc/nginx/nginx.conf + β”œ───────── name: nginx-67zbu + β”œ───────── uuid: 8a8bc1b9-0af6-420e-a426-190dc2da9eaa + β”œ──────── metro: https://api.fra.unikraft.cloud/v1 + β”œ──────── state: starting + β”œ─────── domain: https://nameless-fog-0tvh1uov.fra.unikraft.app + β”œ──────── image: oci://unikraft.io//nginx@sha256:f51ecc121c9ca34abb88a2bc6a69765501304f7893f7e85af15fbec3dc86e2bd + β”œ─────── memory: 256 MiB + β”œ────── service: nameless-fog-0tvh1uov + β”œ─ private fqdn: nginx-67zbu.internal + β””─── private ip: 10.0.3.3 ``` -Note that at first the system lists the status as `running` in the output of the deploy or run flow. + + +Note that at first the system starts the instance as usual. Check the instance's status: @@ -263,48 +293,110 @@ kraft cloud instances list You should see output like: -```ansi title="" -NAME FQDN STATE -nginx-1a747 twilight-gorilla-ui5b6kwt.fra.unikraft.app standby + + +```ansi title="unikraft" +METRO NAME STATE IMAGE ARGS MEMORY VCPUS FQDN CREATED +fra nginx-67zbu standby /nginx 256MiB 1 nameless-fog-0tvh1uov.fra.unikraft.app 2 minutes ago +``` + +```ansi title="kraft" +NAME FQDN STATE STATUS IMAGE MEMORY VCPUS ARGS BOOT TIME +nginx-67zbu nameless-fog-0tvh1uov.fra.unikraft.app standby 5 minutes ago oci://unikraft.io//nginx@sha256:... 256 MiB 1 211.13 ms ``` + + Notice the state is now set to `standby`? At first the deploy or run flow sets the state to `running`. Unikraft Cloud then puts the instance to sleep (it stops it, but keeps state to start it again when needed). -You can also check that scale-to-zero gets enabled through the legacy CLI scale command: +You can also check that scale-to-zero gets enabled by grabbing the instance details + + - +```bash title="unikraft" +unikraft instance get nginx-67zbu +``` ```bash title="kraft" -kraft cloud scale get twilight-gorilla-ui5b6kw +kraft cloud instance get nginx-67zbu ``` which outputs: -```ansi title="" - uuid: 126c4ecb-4718-4a25-9f75-ac9149da9e19 - name: twilight-gorilla-ui5b6kwt - enabled: true - min size: 0 - max size: 1 - warmup (ms): 1000 - cooldown (ms): 1000 - master: 66d05e09-1436-4d1f-bbe6-6dc03ae48d7a - policies: + + +```ansi title="unikraft" +metro: fra +name: nginx-67zbu +uuid: 8a8bc1b9-0af6-420e-a426-190dc2da9eaa +state: standby +image: /nginx +resources: + memory: 256MiB + vcpus: 1 +service: + uuid: a942b9b5-ad17-3ffe-dcd2-ef4331f9087a + name: nameless-fog-0tvh1uov + domains: + - fqdn: nameless-fog-0tvh1uov.fra.unikraft.app +networks: +- uuid: 62d9bbf0-aec8-61f6-7bdb-86edf63dd068 + private-ip: 10.0.3.3 + mac: 12:b0:c6:23:ed:15 +timestamps: + created: just now +scale-to-zero: + enabled: true + policy: on + cooldown-time: 1s +stop: + reason: platform stop + exit-code: 0 ``` -Note the `min size` (0) and `max size` (1) fields -- these mean that the service can scale between 0 and 1 instances, which enables scale-to-zero. +```ansi title="kraft" + uuid: 8a8bc1b9-0af6-420e-a426-190dc2da9eaa + name: nginx-67zbu + fqdn: nameless-fog-0tvh1uov.fra.unikraft.app + private fqdn: nginx-67zbu.internal + private ip: 10.0.3.3 + state: standby + created: 2026-06-05T12:16:10Z + started: 2026-06-05T12:16:10Z + stopped: 2026-06-05T12:16:13Z + start count: 1 + restart count: 0 + restart attempts: + next restart: + restart policy: never + stop origin: + stop reason: + app exit code: 0 + image: oci://unikraft.io//nginx@sha256:f51ecc121c9ca34abb88a2bc6a69765501304f7893f7e85af15fbec3dc86e2bd + memory: 256 MiB + vcpus: 1 + args: + env: + volumes: + service: nameless-fog-0tvh1uov + scale to zero: on + scale to zero cooldown: 1000 + scale to zero stateful: false + boot time: 211.13 ms + up time: 3.317s +``` + -### Testing scale-to-zero -Try using `curl` or your browser to see scale-to-zero (well, scale-to-1 in this case!) in action: +Try using `curl` or your browser to see scale-to-zero (well, *scale-to-one* in this case!) in action: -```bash title="" -curl https://twilight-gorilla-ui5b6kwt.fra.unikraft.app +```bash +curl https://nameless-fog-0tvh1uov.fra.unikraft.app ``` You should get an NGINX response with no noticeable delay. @@ -313,7 +405,7 @@ For fun, try to use the following command to see if you can catch the instance's ```bash title="unikraft" -unikraft instances list --watch +unikraft instances list --watch=0.5s ``` ```bash title="kraft" @@ -322,26 +414,21 @@ watch --color -n 0.5 kraft cloud instance list -If you `curl` enough, you should see the `STATE` turn to a green `running` from -time to time: - -```ansi title="" -NAME FQDN STATE -nginx-1a747 twilight-gorilla-ui5b6kwt.fra.unikraft.app running -``` +If you `curl` enough, you should see the `STATE` turn to a green `running` from time to time. :::note -You can use scale-to-zero *in conjunction with* autoscale. +You can use scale-to-zero *in conjunction with* [autoscale](/features/autoscale). This ensures that as you scale back down, if traffic dies, your last instance gets removed. You're not charged for the service in this state. -For more on autoscale please see the autoscale [guide](/features/autoscale). ::: ## Learn more * The [CLI reference](/docs/cli/unikraft) and the [legacy CLI reference](/docs/cli/kraft/overview). -* Unikraft Cloud's [REST API reference](/api/platform/v1), and in particular the [scale-to-zero schema](/api/platform/v1/~schemas#instance-scale-to-zero). +* Unikraft Cloud's [REST API reference](/api/platform/v1). +* Instance [snapshots](/features/snapshots). +* Service [autoscaling](/features/autoscale). diff --git a/pages/features/snapshots.mdx b/pages/features/snapshots.mdx index b4ffdfb6..f66e833c 100644 --- a/pages/features/snapshots.mdx +++ b/pages/features/snapshots.mdx @@ -11,51 +11,34 @@ You can reduce this initialization time by taking a snapshot of the app memory a ## Setting it up -The quickest way to set this up is via the legacy CLI `--scale-to-zero-stateful` flag: - - - -```bash title="kraft" -kraft cloud deploy --metro fra --scale-to-zero-stateful . -``` - - - -As an alternative, you can add a `label` to your example's `Kraftfile` as follows: - -```yaml title="" -labels: - cloud.unikraft.v1.instances/scale_to_zero.stateful: "true" -``` - -You can see an example of such a `Kraftfile` and label in the [Spring Boot example](/guides/springboot). - -Once deployed, you can check whether this mechanism works for your app via the CLI: +The quickest way to set this up is when creating an instance, via the CLI: ```bash title="unikraft" -unikraft instances get +unikraft run --metro fra \ + -p 443:8080/http+tls \ + --scale-to-zero policy=on,stateful=true \ + ... ``` ```bash title="kraft" -kraft cloud instance get +kraft cloud deploy \ + -p 443:8080/http+tls \ + --scale-to-zero on \ + --scale-to-zero-stateful \ + ... ``` -```ansi -stateful: enabled -``` - - ## How it works When you enable stateful mode and first deploy your app, Unikraft Cloud brings it up normally and lets it fully initialize. This may be fast or take seconds or minutes, depending on your app's init time. Unikraft Cloud waits until the app's exposed port becomes available, and then starts the scale-to-zero process. This assumes no traffic is coming to that port. -At this point, Unikraft Cloud saves the state of your app and sets the app to standby (consuming no resources). +At this point, Unikraft Cloud saves the state of your app and sets the app to `standby` (consuming no resources). Next, when traffic arrives, Unikraft Cloud brings up the app including its saved state. This ensures statefulness across scale-to-zero and scale-to-one cycles. @@ -65,4 +48,6 @@ It also eliminates long initialization times from heavyweight apps. ## Learn more * The [CLI reference](/docs/cli/unikraft) and the [legacy CLI reference](/docs/cli/kraft/overview). -* Unikraft Cloud's [REST API reference](/api/platform/v1), and in particular the [scale-to-zero schema](/api/platform/v1/~schemas#instance-scale-to-zero). +* Unikraft Cloud's [REST API reference](/api/platform/v1). +* [Instances](/platform/instances) documentation. +* [Service groups](/platform/services) documentation. diff --git a/pages/guides/overview.mdx b/pages/guides/overview.mdx index 178ac377..1de48c24 100644 --- a/pages/guides/overview.mdx +++ b/pages/guides/overview.mdx @@ -6,7 +6,7 @@ This page lists all available guides for deploying applications and services on These guides mimic the [examples](https://github.com/unikraft-cloud/examples) repository, and are constantly updated with new content. :::note -Unikraft Cloud can run any workloadβ€”define it in a Dockerfile and it will run it. +Unikraft Cloud can run any workloadβ€”define it in a `Dockerfile` and it will run it. These guides are here to make that journey as fast as possible for you. ::: diff --git a/pages/integrations/kubernetes.mdx b/pages/integrations/kubernetes.mdx index 6214cfd8..23486bc9 100644 --- a/pages/integrations/kubernetes.mdx +++ b/pages/integrations/kubernetes.mdx @@ -20,35 +20,49 @@ Kraftlet will manage the Pod lifecycle to make sure the apps are up and running. ## Getting started -You can install Kraftlet into a Kubernetes cluster using its Helm chart: +Make sure to log into Unikraft Cloud and pick a [metro](/platform/metros) close to you. +Grab the metro name and your API token from the dashboard and set them as environment variables: + ```bash title="" -$ helm install kraftlet \ +# Set Unikraft Cloud access token +export UKC_TOKEN=token +# Set metro to Frankfurt, DE +export UKC_METRO=fra +``` + +You can install Kraftlet into a Kubernetes cluster using its Helm chart: + +```bash +helm install kraftlet \ --namespace kraftlet \ --create-namespace \ --set ukc.metro=$UKC_METRO \ --set ukc.token=$UKC_TOKEN \ oci://ghcr.io/unikraft-cloud/helm-charts/kraftlet ``` -The `UKC_METRO` and `UKC_TOKEN` variables are only for these values and the legacy CLI. -The `unikraft` CLI uses profiles instead. You can check if Kraftlet is running by checking its pods: -```bash title="" -$ kubectl get pods -n kraftlet + +```bash +kubectl get pods -n kraftlet ``` + Which should return a single pod running: -``` + +```ansi title="" NAME READY STATUS RESTARTS AGE kraftlet-74666cf7f5-nbkw7 1/1 Running 0 38s ``` You can also check if the kraftlet successfully registered as a node: -```bash title="" -$ kubectl get nodes + +```bash +kubectl get nodes ``` Which should, among other nodes, return Kraftlet. -``` + +```ansi title="" NAME STATUS ROLES AGE VERSION Kraftlet Ready agent 58s No version provided ``` @@ -64,11 +78,11 @@ Make sure Kraftlet is up and running before trying out examples below. ### Simple app -The configuration below defines an app with three replicas running the nginx image and a single Kubernetes service that exposes port `443`. +The configuration below defines an app with three replicas running the NGINX image and a single Kubernetes service that exposes port `443`. For each service backed by a Pod scheduled to the Kraftlet node, Kraftlet will create a corresponding service. -In this case, Kraftlet will create three nginx instances and a single service called after the Kubernetes service that exposes port 443. +In this case, Kraftlet will create three NGINX instances and a single service called after the Kubernetes service that exposes port 443. -```yaml +```yaml title="simple.yaml" # simple.yaml apiVersion: apps/v1 kind: Deployment @@ -112,57 +126,82 @@ spec: You can apply the configuration with: -```shell -$ kubectl apply -f simple.yaml +```bash +kubectl apply -f simple.yaml ``` Once applied, you can check the status of your newly created pods in the Kubernetes cluster: +```bash +kubectl get pods ``` -$ kubectl get pods + +```ansi title="" NAME READY STATUS RESTARTS AGE my-app-78c766fb67-k2dgk 1/1 Running 0 104s my-app-78c766fb67-mfp77 1/1 Running 0 104s my-app-78c766fb67-tkkxq 1/1 Running 0 104s ``` -Your app is now managed from the Kubernetes cluster, but is actually running on the Unikraft Cloud. +Your app is now managed from the Kubernetes cluster, but is actually running on Unikraft Cloud. To check the instances, run: -```shell title="unikraft" -$ unikraft instances list +```bash title="unikraft" +unikraft instances list ``` -```shell title="kraft" -$ kraft cloud instance list +```bash title="kraft" +kraft cloud instance list ``` Which will return a list of instances created from pods above: + + +```ansi title="unikraft" +METRO NAME STATE IMAGE ARGS MEMORY VCPUS FQDN CREATED +fra my-app-78c766fb67-k2dgk-nginx running /nginx 128MiB 1 my-service-orjlyrac.fra.unikraft.app 2 minutes ago +fra my-app-78c766fb67-tkkxq-nginx running /nginx 128MiB 1 my-service-orjlyrac.fra.unikraft.app 2 minutes ago +fra my-app-78c766fb67-mfp77-nginx running /nginx 128MiB 1 my-service-orjlyrac.fra.unikraft.app 2 minutes ago ``` -NAME FQDN STATE STATUS IMAGE MEMORY VCPUS ARGS BOOT TIME -my-app-78c766fb67-k2dgk-nginx my-service-orjlyrac.fra.unikraft.app running since 24secs nginx@sha256:49d8fb7a9934a87e93f9... 128 MiB 1 /usr/bin/nginx -c /etc/nginx/ngin... 14.06 ms -my-app-78c766fb67-tkkxq-nginx my-service-orjlyrac.fra.unikraft.app running since 24secs nginx@sha256:49d8fb7a9934a87e93f9... 128 MiB 1 /usr/bin/nginx -c /etc/nginx/ngin... 14.43 ms -my-app-78c766fb67-mfp77-nginx my-service-orjlyrac.fra.unikraft.app running since 25secs nginx@sha256:49d8fb7a9934a87e93f9... 128 MiB 1 /usr/bin/nginx -c /etc/nginx/ngin... 15.12 ms +```ansi title="kraft" +NAME FQDN STATE STATUS IMAGE MEMORY VCPUS ARGS BOOT TIME +my-app-78c766fb67-k2dgk-nginx my-service-orjlyrac.fra.unikraft.app running since 24secs oci://unikraft.io//nginx@sha256:49d8fb7a9934a87e93f9... 128 MiB 1 14.06 ms +my-app-78c766fb67-tkkxq-nginx my-service-orjlyrac.fra.unikraft.app running since 24secs oci://unikraft.io//nginx@sha256:49d8fb7a9934a87e93f9... 128 MiB 1 14.43 ms +my-app-78c766fb67-mfp77-nginx my-service-orjlyrac.fra.unikraft.app running since 25secs oci://unikraft.io//nginx@sha256:49d8fb7a9934a87e93f9... 128 MiB 1 15.12 ms ``` + + As you can see, all instances have the same FQDN. This is because Kraftlet created a corresponding Unikraft Cloud service for the Kubernetes service defined in YAML above. You can check the created service with the following command: -```shell title="unikraft" -$ unikraft services list +```bash title="unikraft" +unikraft services list +``` + +```bash title="kraft" +kraft cloud service list +``` + + + + + +```ansi title="unikraft" +NAME NAME AUTOSCALE CREATED FQDN SOURCE DESTINATION HANDLERS +my-service my-service-orjlyrac false 6 minutes ago my-service-orjlyrac.fra.unikraft.app 443 8080 ["tls", "http"] ``` -```shell title="kraft" -$ kraft cloud service list +```ansi title="kraft" NAME FQDN SERVICES INSTANCES CREATED AT PERSISTENT my-service my-service-orjlyrac.fra.unikraft.app 443:8080/tls+http my-app-78c766fb67-k2dgk-nginx my-app-78c766fb67-tkkxq-nginx my-a... 6 minutes ago true ``` @@ -179,7 +218,7 @@ To support provisioning Unikraft Cloud volumes through Kubernetes, Kraftlet list Creating a new PVC object with the specified storage class triggers Kraftlet to create a Unikraft Cloud volume and create a PV object to mark the PVC as `Bound`. Below is an example PVC with the Unikraft Cloud storage class you can apply to your cluster. -```yaml +```yaml title="pvc.yaml" apiVersion: v1 kind: PersistentVolumeClaim metadata: @@ -195,8 +234,11 @@ spec: Once applied, you can check the created PVC status: +```bash +kubectl get pvc ``` -$ kubectl get pvc + +```ansi title="" NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE my-claim Bound pv-7ab06383-ac03-4a81-968a-1b0cff03c23a 10Mi RWO ukc-volume 4s ``` @@ -205,22 +247,35 @@ Also, you can check the volumes on the Unikraft Cloud: -```shell title="unikraft" -$ unikraft volumes list +```bash title="unikraft" +unikraft volumes list +``` + +```bash title="kraft" +kraft cloud volume list +``` + + + + + +```ansi title="unikraft" +METRO NAME STATE SIZE CREATED +fra my-claim available 10MiB 5 minutes ago ``` -```shell title="kraft" -$ kraft cloud volume list +```ansi title="kraft" NAME CREATED AT SIZE ATTACHED TO MOUNTED BY STATE PERSISTENT my-claim 5 minutes ago 10 MiB available true ``` + At the moment, the volume isn't attached or mounted by an instance. To create an instance that would use the volume, you can create a Kubernetes Pod that would reference the PVC: -```yaml +```yaml title="pod.yaml" apiVersion: v1 kind: Pod metadata: @@ -251,14 +306,26 @@ If you check the instances again, you will see a new instance created from the P -```shell title="unikraft" -$ unikraft instances list +```bash title="unikraft" +unikraft instances list ``` -```shell title="kraft" -$ kraft cloud instance list -NAME FQDN STATE STATUS IMAGE MEMORY VCPUS ARGS BOOT TIME -nginx-pod-nginx fragrant-breeze-llesxdta.fra.unikraft.app running since 1min nginx@sha256:49d8fb7a9934a87e93f9eb326... 128 MiB 1 /usr/bin/nginx -c /etc/nginx/nginx.conf 15.14 ms +```bash title="kraft" +kraft cloud instance list +``` + + + + + +```ansi title="unikraft" +METRO NAME STATE IMAGE ARGS MEMORY VCPUS FQDN CREATED +fra nginx-pod-nginx running /nginx 128 MiB 1 fragrant-breeze-llesxdta.fra.unikraft.app 1 minute ago +``` + +```ansi title="kraft" +NAME FQDN STATE STATUS IMAGE MEMORY VCPUS ARGS BOOT TIME +nginx-pod-nginx fragrant-breeze-llesxdta.fra.unikraft.app running since 1min oci://unikraft.io//nginx@sha256:49d8fb7a9934a87e93f9eb326... 128 MiB 1 15.14 ms ``` @@ -267,12 +334,24 @@ And if you check the volume now, you will see it's attached and mounted by the c -```shell title="unikraft" -$ unikraft volumes list +```bash title="unikraft" +unikraft volumes list +``` + +```bash title="kraft" +kraft cloud volume list +``` + + + + + +```ansi title="unikraft" +METRO NAME STATE SIZE CREATED +fra my-claim mounted 10MiB 8 minutes ago ``` -```shell title="kraft" -$ kraft cloud volume list +```ansi title="kraft" NAME CREATED AT SIZE ATTACHED TO MOUNTED BY STATE PERSISTENT my-claim 8 minutes ago 10 MiB nginx-pod-nginx nginx-pod-nginx mounted true ``` diff --git a/pages/integrations/sdks/go.mdx b/pages/integrations/sdks/go.mdx index 837fd5ac..93e0eab4 100644 --- a/pages/integrations/sdks/go.mdx +++ b/pages/integrations/sdks/go.mdx @@ -9,7 +9,7 @@ specification at https://github.com/unikraft-cloud/openapi. ## Installation -```bash title="" +```bash go get unikraft.com/cloud/sdk ``` @@ -21,37 +21,37 @@ Requires Go 1.25 or later. package main import ( - "context" - "fmt" + "context" + "fmt" - "unikraft.com/cloud/sdk/platform" + "unikraft.com/cloud/sdk/platform" ) func main() { - ctx := context.Background() - - // Create a new client. The token and default metro are automatically - // read from environment variables (UKC_TOKEN and UKC_METRO). - client := platform.NewClient() - - // List all instances - resp, err := client.GetInstances(ctx, nil, nil) - if err != nil { - panic(err) - } - - // Check for API-level errors - if resp.Status != "success" { - panic(resp.Message) - } - - for _, inst := range resp.Data.Instances { - fmt.Printf("Name: %s\n", *inst.Name) - fmt.Printf("UUID: %s\n", *inst.Uuid) - fmt.Printf("State: %s\n", *inst.State) - fmt.Printf("Image: %s\n", *inst.Image) - fmt.Println("---") - } + ctx := context.Background() + + // Create a new client. The token and default metro are automatically + // read from environment variables (UKC_TOKEN and UKC_METRO). + client := platform.NewClient() + + // List all instances + resp, err := client.GetInstances(ctx, nil, nil) + if err != nil { + panic(err) + } + + // Check for API-level errors + if resp.Status != "success" { + panic(resp.Message) + } + + for _, inst := range resp.Data.Instances { + fmt.Printf("Name: %s\n", *inst.Name) + fmt.Printf("UUID: %s\n", *inst.Uuid) + fmt.Printf("State: %s\n", *inst.State) + fmt.Printf("Image: %s\n", *inst.Image) + fmt.Println("---") + } } ``` @@ -59,7 +59,7 @@ func main() { Pass your token via `platform.WithToken()`: -```go title="" +```go client := platform.NewClient( platform.WithToken("your-api-token"), ) @@ -75,7 +75,7 @@ Or set one of the following environment variables. You can also set the default metro via the `UKC_METRO` environment variable. If not specified, the SDK defaults to `fra`. -```go title="" +```go // No WithToken/WithDefaultMetro needed, NewClient reads the environment automatically client := platform.NewClient() ``` @@ -309,6 +309,4 @@ client.Healthz(ctx) ## Source -The SDK source and full API reference are available at -[github.com/unikraft-cloud/go-sdk](https://github.com/unikraft-cloud/go-sdk) -and [pkg.go.dev/unikraft.com/cloud/sdk](https://pkg.go.dev/unikraft.com/cloud/sdk). +The SDK source and full API reference are available at [github.com/unikraft-cloud/go-sdk](https://github.com/unikraft-cloud/go-sdk) and [pkg.go.dev/unikraft.com/cloud/sdk](https://pkg.go.dev/unikraft.com/cloud/sdk). diff --git a/pages/introduction.mdx b/pages/introduction.mdx index 52b1ffc5..77abb916 100644 --- a/pages/introduction.mdx +++ b/pages/introduction.mdx @@ -80,7 +80,7 @@ Expect higher server density, cost-savings, I/O performance boosts, active vCPU 1. Deploy your first scale-to-zero, serverless app with Unikraft Cloud. ```bash - unikraft run --metro=fra -p 443:8080/http+tls --image=nginx:latest + unikraft run --metro fra -p 443:8080/http+tls --image nginx:latest ``` diff --git a/pages/platform/certificates.mdx b/pages/platform/certificates.mdx index 870a143c..90766201 100644 --- a/pages/platform/certificates.mdx +++ b/pages/platform/certificates.mdx @@ -3,30 +3,42 @@ title: Certificates navigation_icon: shield-check --- -To use a custom certificate instead of the automatically generated one from the default public certificate authority, create a certificate with the CLI: +When attaching a [service](/platform/services) to an instance, Unikraft Cloud exposes the instance on a subdomain of `unikraft.app` by default. +It automatically issues a certificate for the domain from a public certificate authority. + +## Custom certificates + +You can upload your own certificate to use with your services: -```ansi title="unikraft" -unikraft certificates create \ - --cn *.mydomain.com \ +```bash title="unikraft" +unikraft certificates create --metro fra \ + --cn demo.unikraft.dev. \ --name mydomain-cert \ - --pkey /path/to/private.key \ - --chain /path/to/chain.pem + --pkey \ + --chain ``` -```ansi title="kraft" -kraft cloud cert create \ - --cn *.mydomain.com \ +```bash title="kraft" +kraft cloud certificates create \ + --cn demo.unikraft.dev. \ --name mydomain-cert \ - --pkey /path/to/private.key \ - --chain /path/to/chain.pem + --pkey \ + --chain ``` -The provided common name (CN) must match the one for which Unikraft Cloud issued the certificate. +:::caution +Make sure that the common name given ends with a dot (`.`). +::: + +:::info +Unikraft Cloud checks both the `CN` and `SAN` fields of the certificate. +The provided common name (CN) must match the one on the certificate file, and the certificate must be valid for the domain you want to use. It can also be a wildcard domain. +::: Use the CLI to view and manage certificates. For example, to list certificates: @@ -38,60 +50,161 @@ unikraft certificates list ``` ```bash title="kraft" -kraft cloud cert ls +kraft cloud certificates list ``` You should see output like: -```text title="" -NAME STATE COMMON NAME CREATED AT -mydomain.com-sa4x9 valid mydomain.com 5 days ago -mydomain-cert valid *.mydomain.com 2 days ago + + +```ansi title="unikraft" +METRO NAME COMMON-NAME STATE CREATED NOT-AFTER +fra mydomain-cert demo.unikraft.dev valid 1 minute ago in 12 months +fra mydomain.com-sa4x9 *.mydomain.com valid 5 days ago in 12 months ``` +```ansi title="kraft" +NAME STATE COMMON NAME CREATED AT +mydomain-cert valid demo.unikraft.dev 1 minute ago +mydomain.com-sa4x9 valid *.mydomain.com 5 days ago +``` + + + Retrieve full information about a certificate with: ```bash title="unikraft" -unikraft certificates get mydomain.com-sa4x9 +unikraft certificates get mydomain-cert ``` ```bash title="kraft" -kraft cloud cert get mydomain.com-sa4x9 +kraft cloud certificates get mydomain-cert ``` You should see output like: -```ansi title="" - uuid: b8160db9-7cba-4b80-9107-c4fe27529bf5 - name: mydomain.com-sa4x9 - state: valid - common name: mydomain.com - subject: CN=mydomain.com - issuer: CN=R3,O=Let's Encrypt,C=US -serial number: 0455BBAEC140EACBA5FEEAE6D817E73EF266 - not before: 2024-03-07T18:06:11Z - not after: 2024-06-05T18:06:10Z - created at: 2024-03-07T19:06:04Z - services: wispy-moon-dpg6d54i + + +```ansi title="unikraft" +metro: fra +name: mydomain-cert +uuid: 9fabfc61-a168-439e-89e2-0d5308ee9f13 +common-name: demo.unikraft.dev +subject: CN=demo.unikraft.dev +issuer: CN=demo.unikraft.dev +serial-number: 43C149067F939CD41100A74905B80560C315E92F +state: valid +timestamps: + created: just now + not-before: 1 minute ago + not-after: in 12 months ``` +```ansi title="kraft" + uuid: 9fabfc61-a168-439e-89e2-0d5308ee9f13 + name: mydomain-cert + state: valid + validation attempts: + next attempt: + common name: demo.unikraft.dev + subject: CN=demo.unikraft.dev + issuer: CN=demo.unikraft.dev + serial number: 43C149067F939CD41100A74905B80560C315E92F + not before: 2026-06-04T14:51:03Z + not after: 2027-06-04T14:51:03Z + created at: 2026-06-04T14:52:01Z + services: +``` + + + +## Attaching certificates to services + +You attach the certificate to a service group implicitly. +You do this by creating an instance with a [domain](/platform/domains) that matches the common name of the certificate: + + + +```bash title="unikraft" +unikraft run --metro fra \ + -m 256M \ + -p 443:8080/http+tls \ + --domain demo.unikraft.dev \ + --image /nginx:latest +``` + +```bash title="kraft" +kraft cloud instance create \ + --start \ + -M 256Mi \ + -p 443:8080/tls+http \ + -d demo.unikraft.dev \ + /nginx:latest +``` + + + + + +```ansi title="unikraft" +metro: fra +name: nginx-67zbu +uuid: 8a8bc1b9-0af6-420e-a426-190dc2da9eaa +state: starting +image: /nginx +resources: + memory: 256MiB + vcpus: 1 +service: + uuid: a942b9b5-ad17-3ffe-dcd2-ef4331f9087a + name: nameless-fog-0tvh1uov + domains: + - fqdn: demo.unikraft.dev + certificate: + name: mydomain-cert + uuid: 9fabfc61-a168-439e-89e2-0d5308ee9f13 +networks: +- uuid: 62d9bbf0-aec8-61f6-7bdb-86edf63dd068 + private-ip: 10.0.3.3 + mac: 12:b0:c6:23:ed:15 +timestamps: + created: just now +``` + +```ansi title="kraft" +[●] Deployed successfully! + β”‚ + β”œ───────── name: nginx-67zbu + β”œ───────── uuid: 8a8bc1b9-0af6-420e-a426-190dc2da9eaa + β”œ──────── metro: https://api.fra.unikraft.cloud/v1 + β”œ──────── state: starting + β”œ─────── domain: https://demo.unikraft.dev + β”œ──────── image: oci://unikraft.io//nginx@sha256:f51ecc121c9ca34abb88a2bc6a69765501304f7893f7e85af15fbec3dc86e2bd + β”œ─────── memory: 256 MiB + β”œ────── service: nameless-fog-0tvh1uov + β”œ─ private fqdn: nginx-67zbu.internal + β””─── private ip: 10.0.3.3 +``` + + + To remove a certificate, first remove any instances from the relevant service and then remove the service. Remove the certificate with: ```bash title="unikraft" -unikraft certificates delete mydomain.com-sa4x9 +unikraft certificates delete mydomain-cert ``` ```bash title="kraft" -kraft cloud cert rm mydomain.com-sa4x9 +kraft cloud certificates delete mydomain-cert ``` @@ -99,5 +212,5 @@ kraft cloud cert rm mydomain.com-sa4x9 ## Learn more * The [CLI reference](/docs/cli/unikraft) and the [legacy CLI reference](/docs/cli/kraft/overview). -* Unikraft Cloud's [REST API reference](/api/platform/v1). -* Many more guides [here](/guides/bun). +* Unikraft Cloud's [REST API reference](/api/platform/v1) and, in particular, the section on [certificates](/api/platform/v1/certificates). +* The [domains documentation](/platform/domains) for more information on how to expose your services on custom domains. diff --git a/pages/platform/delete-locks.mdx b/pages/platform/delete-locks.mdx index da8964ad..f67a705c 100644 --- a/pages/platform/delete-locks.mdx +++ b/pages/platform/delete-locks.mdx @@ -6,23 +6,21 @@ navigation_icon: lock import { Tabs, TabsContent, TabsList, TabsTrigger } from "zudoku/ui/Tabs" :::caution -The [KraftKit CLI tool](/cli/) doesn't expose this feature to users yet. -Use it via the [Unikraft Cloud API](/api/platform/v1). +You can currently only manage delete locks via the [API](/api/platform/v1). ::: -Delete locks prevent accidental deletion of instances and volumes. +Delete locks prevent accidental deletion of [instances](/platform/instances) and [volumes](/platform/volumes). When you set a delete lock on a resource, any attempt to delete it returns an error until you remove it. Delete locks apply to: -- Instances -- Instance templates -- Volumes -- Volume templates +- [Instances](/platform/instances) +- [Instance templates](/platform/instances#instance-templates) +- [Volumes](/platform/volumes) +- [Volume templates](/platform/volumes#volume-templates) ## Enabling a delete lock -You can set a delete lock on a resource via a `PATCH` operation. -Set `UKC_TOKEN` and `UKC_METRO` for the API calls: +You can set a delete lock on a resource via a `PATCH` operation: @@ -32,60 +30,110 @@ Set `UKC_TOKEN` and `UKC_METRO` for the API calls: Volume Template -```bash title="Locking an instance" + + +```bash title="unikraft" +# not yet supported, use `unikraft api` instead +unikraft api /v1/instances -X PATCH --metro fra \ + -d '[{ + "name": "", + "prop": "delete_lock", + "op": "set", + "value": true + }]' +``` +```bash title="API" curl -X PATCH \ - -H "Authorization: Bearer $UKC_TOKEN" \ - -H "Content-Type: application/json" \ - "https://api.$UKC_METRO.unikraft.cloud/v1/instances" \ - -d "[{ - 'name': '', - 'prop': 'delete_lock', - 'op': 'set', - 'value': true - }]" + -H "Authorization: Bearer $UKC_TOKEN" \ + -H "Content-Type: application/json" \ + "https://api.fra.unikraft.cloud/v1/instances" \ + -d '[{ + "name": "", + "prop": "delete_lock", + "op": "set", + "value": true + }]' ``` + + -```bash title="Locking an instance template" + +```bash title="unikraft" +# not yet supported, use `unikraft api` instead +unikraft api /v1/instances/templates -X PATCH --metro fra \ + -d '[{ + "name": "", + "prop": "delete_lock", + "op": "set", + "value": true + }]' +``` +```bash title="API" curl -X PATCH \ - -H "Authorization: Bearer $UKC_TOKEN" \ - -H "Content-Type: application/json" \ - "https://api.$UKC_METRO.unikraft.cloud/v1/instance/templates" \ - -d "[{ - 'name': '', - 'prop': 'delete_lock', - 'op': 'set', - 'value': true - }]" + -H "Authorization: Bearer $UKC_TOKEN" \ + -H "Content-Type: application/json" \ + "https://api.fra.unikraft.cloud/v1/instances/templates" \ + -d '[{ + "name": "", + "prop": "delete_lock", + "op": "set", + "value": true + }]' ``` + -```bash title="Locking a volume" + +```bash title="unikraft" +# not yet supported, use `unikraft api` instead +unikraft api /v1/volumes -X PATCH --metro fra \ + -d '[{ + "name": "", + "prop": "delete_lock", + "op": "set", + "value": true + }]' +``` +```bash title="API" curl -X PATCH \ - -H "Authorization: Bearer $UKC_TOKEN" \ - -H "Content-Type: application/json" \ - "https://api.$UKC_METRO.unikraft.cloud/v1/volumes" \ - -d "[{ - 'name': '', - 'prop': 'delete_lock', - 'op': 'set', - 'value': true - }]" + -H "Authorization: Bearer $UKC_TOKEN" \ + -H "Content-Type: application/json" \ + "https://api.fra.unikraft.cloud/v1/volumes" \ + -d '[{ + "name": "", + "prop": "delete_lock", + "op": "set", + "value": true + }]' ``` + -```bash title="Locking a volume template" + +```bash title="unikraft" +# not yet supported, use `unikraft api` instead +unikraft api /v1/volumes/templates -X PATCH --metro fra \ + -d '[{ + "name": "", + "prop": "delete_lock", + "op": "set", + "value": true + }]' +``` +```bash title="API" curl -X PATCH \ - -H "Authorization: Bearer $UKC_TOKEN" \ - -H "Content-Type: application/json" \ - "https://api.$UKC_METRO.unikraft.cloud/v1/volume/templates" \ - -d "[{ - 'name': '', - 'prop': 'delete_lock', - 'op': 'set', - 'value': true - }]" + -H "Authorization: Bearer $UKC_TOKEN" \ + -H "Content-Type: application/json" \ + "https://api.fra.unikraft.cloud/v1/volumes/templates" \ + -d '[{ + "name": "", + "prop": "delete_lock", + "op": "set", + "value": true + }]' ``` + @@ -101,70 +149,115 @@ Before you can delete a resource, you need to remove the delete lock by setting Volume Template -```bash title="Unlocking an instance" + +```bash title="unikraft" +# not yet supported, use `unikraft api` instead +unikraft api /v1/instances -X PATCH --metro fra \ + -d '[{ + "name": "", + "prop": "delete_lock", + "op": "set", + "value": false + }]' +``` +```bash title="API" curl -X PATCH \ - -H "Authorization: Bearer $UKC_TOKEN" \ - -H "Content-Type: application/json" \ - "https://api.$UKC_METRO.unikraft.cloud/v1/instances" \ - -d "[{ - 'name': '', - 'prop': 'delete_lock', - 'op': 'set', - 'value': false - }]" + -H "Authorization: Bearer $UKC_TOKEN" \ + -H "Content-Type: application/json" \ + "https://api.fra.unikraft.cloud/v1/instances" \ + -d '[{ + "name": "", + "prop": "delete_lock", + "op": "set", + "value": false + }]' ``` + -```bash title="Unlocking an instance template" + +```bash title="unikraft" +# not yet supported, use `unikraft api` instead +unikraft api /v1/instances/templates -X PATCH --metro fra \ + -d '[{ + "name": "", + "prop": "delete_lock", + "op": "set", + "value": false + }]' +``` +```bash title="API" curl -X PATCH \ - -H "Authorization: Bearer $UKC_TOKEN" \ - -H "Content-Type: application/json" \ - "https://api.$UKC_METRO.unikraft.cloud/v1/instance/templates" \ - -d "[{ - 'name': '', - 'prop': 'delete_lock', - 'op': 'set', - 'value': false - }]" + -H "Authorization: Bearer $UKC_TOKEN" \ + -H "Content-Type: application/json" \ + "https://api.fra.unikraft.cloud/v1/instances/templates" \ + -d '[{ + "name": "", + "prop": "delete_lock", + "op": "set", + "value": false + }]' ``` + -```bash title="Unlocking a volume" + +```bash title="unikraft" +# not yet supported, use `unikraft api` instead +unikraft api /v1/volumes -X PATCH --metro fra \ + -d '[{ + "name": "", + "prop": "delete_lock", + "op": "set", + "value": false + }]' +``` +```bash title="API" curl -X PATCH \ - -H "Authorization: Bearer $UKC_TOKEN" \ - -H "Content-Type: application/json" \ - "https://api.$UKC_METRO.unikraft.cloud/v1/volumes" \ - -d "[{ - 'name': '', - 'prop': 'delete_lock', - 'op': 'set', - 'value': false - }]" + -H "Authorization: Bearer $UKC_TOKEN" \ + -H "Content-Type: application/json" \ + "https://api.fra.unikraft.cloud/v1/volumes" \ + -d '[{ + "name": "", + "prop": "delete_lock", + "op": "set", + "value": false + }]" ``` + -```bash title="Unlocking a volume template" + +```bash title="unikraft" +# not yet supported, use `unikraft api` instead +unikraft api /v1/volumes/templates -X PATCH --metro fra \ + -d '[{ + "name": "", + "prop": "delete_lock", + "op": "set", + "value": false + }]' +``` +```bash title="API" curl -X PATCH \ - -H "Authorization: Bearer $UKC_TOKEN" \ - -H "Content-Type: application/json" \ - "https://api.$UKC_METRO.unikraft.cloud/v1/volume/templates" \ - -d "[{ - 'name': '', - 'prop': 'delete_lock', - 'op': 'set', - 'value': false - }]" + -H "Authorization: Bearer $UKC_TOKEN" \ + -H "Content-Type: application/json" \ + "https://api.fra.unikraft.cloud/v1/volumes/templates" \ + -d '[{ + "name": "", + "prop": "delete_lock", + "op": "set", + "value": false + }]' ``` + -## Behaviour - -- **Deletion blocked**: Any `DELETE` request on a locked resource returns an error. -- **Autokill skipped**: The [autokill](/features/autokill) subsystem skips instances and service groups that have a delete lock set. -- **Templates**: Delete locks are independently configurable on instance templates and volume templates via their respective `PATCH` endpoints. +:::caution +The [autokill](/features/autokill) subsystem skips instances and service groups that have a delete lock set. +::: ## Learn more -* The [CLI reference](/docs/cli/unikraft) and the [legacy CLI reference](/docs/cli/kraft/overview). * Unikraft Cloud's [REST API reference](/api/platform/v1), in particular the sections on [instances](/api/platform/v1/instances) and [volumes](/api/platform/v1/volumes). diff --git a/pages/platform/domains.mdx b/pages/platform/domains.mdx index 6d82b6b7..77cb2376 100644 --- a/pages/platform/domains.mdx +++ b/pages/platform/domains.mdx @@ -3,30 +3,18 @@ title: Domains navigation_icon: globe --- -This guide shows how to deploy an app and link it to a domain you own (for example, `mydomain.com`). - -:::note -Unikraft Cloud can take a few seconds before issuing the certificate. -If there are misconfigurations or DNS changes that your external DNS provider hasn't propagated yet, this can take much longer. -You can check the validation status of the certificate with the CLI (more on this below). -The controller retries at these intervals: [1,5,10,30] minutes, then [1,6,12,24] hours, then it fails. -::: - -The setup is complete. -Test the deployment with `curl`. - +When attaching a [service](/platform/services) to an instance, Unikraft Cloud exposes the instance on a subdomain of `unikraft.app` by default. +You can also use your own custom domain instead. ## Configuring your external DNS provider Before Unikraft Cloud can launch an app, you need to ensure that you have configured things correctly with your DNS provider: -ALIAS (apex alias) and ANAME (authoritative alias) are DNS record types some providers support. - -* For a subdomain you own such as docs.mydomain.com, a CNAME is enough. - In this case, you can just add a `CNAME` record with your sub-domain (`docs`) as the host and point the record to Unikraft Cloud's metro (for example, `fra.unikraft.app`). +* For a subdomain you own such as `docs.mydomain.com`, a CNAME is enough. + In this case, you can just add a CNAME record with your sub-domain (`docs`) as the host and point the record to Unikraft Cloud's metro (for example, `fra.unikraft.app`). -* For an apex domain (for example, mydomain.com), add one of: ALIAS, ANAME, or a flattened CNAME record. - Depending on your provider, leave the host field empty or enter @. +* For an apex domain (for example, `mydomain.com`), add one of: ALIAS[^1], ANAME[^1], or a flattened CNAME record. + Depending on your provider, leave the host field empty or enter `@`. Point the record to the metro (for example, `fra.unikraft.app`). If the provider doesn't support these record types, add an A record pointing to the metro IP address. @@ -48,10 +36,13 @@ kraft cloud metro ls :::note You can have many domains for the same service. -Unikraft Cloud also supports wildcard domains. -See the [certificates API](/api/platform/v1/certificates) for details. +Unikraft Cloud also supports [wildcard domains](/platform/certificates). ::: +{/* vale off */} +[^1]: ALIAS (apex alias) and ANAME (authoritative alias) are DNS record types some providers support. +{/* vale on */} + ## Launching your app Assume the goal is to use NGINX as an app: @@ -62,8 +53,7 @@ cd examples/nginx ``` Log into Unikraft Cloud and select a [metro](/platform/metros) close to you. -This guide uses `fra` (Frankfurt, πŸ‡©πŸ‡ͺ). -Set the following: +This guide uses `fra` (Frankfurt, DE): @@ -89,51 +79,125 @@ Unikraft Cloud does this through the domain flag: ```bash title="unikraft" unikraft build . --output /nginx:latest -unikraft run --metro=fra -p 443:8080/http+tls --domain mydomain.com --image=/nginx:latest +unikraft run --metro fra \ + -m 256M \ + -p 443:8080/http+tls \ + --domain mydomain.com \ + --image /nginx:latest ``` ```bash title="kraft" -kraft cloud deploy -p 443:8080 -d mydomain.com . +kraft cloud deploy \ + -M 256Mi \ + -p 443:8080 \ + -d mydomain.com \ + . ``` -The resulting output of the `deploy` command should be like: +The resulting output of the deploy command should be like: -```ansi title="" + + + +```ansi title="unikraft" +metro: fra +name: nginx-67zbu +uuid: 8a8bc1b9-0af6-420e-a426-190dc2da9eaa +state: starting +image: /nginx +resources: + memory: 256MiB + vcpus: 1 +service: + uuid: a942b9b5-ad17-3ffe-dcd2-ef4331f9087a + name: nameless-fog-0tvh1uov + domains: + - fqdn: mydomain.com + certificate: + name: mydomain.com-savtl + uuid: 51efebaf-4858-450c-8ba5-8bd1df96c20b +networks: +- uuid: 62d9bbf0-aec8-61f6-7bdb-86edf63dd068 + private-ip: 10.0.3.3 + mac: 12:b0:c6:23:ed:15 +timestamps: + created: just now +``` + +```ansi title="kraft" [●] Deployed successfully! β”‚ - β”œ────────── name: nginx-67zbu - β”œ────────── uuid: 269019de-f7dc-4077-9568-012ad594ca87 - β”œ───────── state: running - β”œ─────────── url: https://mydomain.com - β”œ───────── image: nginx@sha256:6abb4f2ba4501068a84885d7b8b127adaf3d83c25fd43e79d5a142f6d8703c93 - β”œ───── boot time: 11.13ms - β”œ──────── memory: 1024 MiB - β”œ─────── service: wispy-moon-dpg6d54i - β”œ── private fqdn: nginx-67zbu.internal - β”œ──── private ip: 172.16.6.5 - β””────────── args: /usr/bin/nginx -c /etc/nginx/nginx.conf + β”œ───────── name: nginx-67zbu + β”œ───────── uuid: 8a8bc1b9-0af6-420e-a426-190dc2da9eaa + β”œ──────── metro: https://api.fra.unikraft.cloud/v1 + β”œ──────── state: starting + β”œ─────── domain: https://mydomain.com + β”œ──────── image: oci://unikraft.io//nginx@sha256:f51ecc121c9ca34abb88a2bc6a69765501304f7893f7e85af15fbec3dc86e2bd + β”œ─────── memory: 256 MiB + β”œ────── service: nameless-fog-0tvh1uov + β”œ─ private fqdn: nginx-67zbu.internal + β””─── private ip: 10.0.3.3 ``` -When you issue the domain flag, Unikraft Cloud requests a -new certificate from the public certificate authority. + -:::note -Issuing the certificate can take a few seconds. -If there are misconfigurations or pending DNS changes, it can take longer. -Check the validation status with the CLI (more on this command below). -The Unikraft Cloud controller retries at these intervals: [1,5,10,30] minutes, then [1,6,12,24] hours, then it fails. +When you issue the domain flag, Unikraft Cloud requests a new [certificate](/platform/certificates) from the public certificate authority. +To check the validation status of the certificate, use the CLI: + + + +```bash title="unikraft" +unikraft certificates list +``` + +```bash title="kraft" +kraft cloud certificates list +``` + + + +You should see it first in `pending`: + + + +```ansi title="unikraft" +METRO NAME COMMON-NAME STATE CREATED NOT-AFTER +fra mydomain.com-savtl mydomain.com pending just now never +``` + +```ansi title="kraft" +NAME STATE COMMON NAME CREATED AT +mydomain.com-savtl pending mydomain.com just now +``` + + + +{/* vale off */} +:::caution +Unikraft Cloud requests a certificate from Let's Encrypt using the [ACME protocol](https://letsencrypt.org/how-it-works/). +It can take a few seconds before issuing the certificate. +If there are misconfigurations or DNS changes that your external DNS provider hasn't propagated yet, this can take much longer. ::: +{/* vale on */} -The setup is complete. -Test the deployment with `curl`: +After their state changes to `valid`, check the deployment with `curl`: ```bash curl https://mydomain.com ``` +```text title="" + + + +Welcome to nginx! +[...] +``` + ## Learn more * The [CLI reference](/docs/cli/unikraft) and the [legacy CLI reference](/docs/cli/kraft/overview). -* Unikraft Cloud's [REST API reference](/api/platform/v1), in particular the sections on [instances](/api/platform/v1/instances) and [service groups](/api/platform/v1/service-groups). +* Unikraft Cloud's [REST API reference](/api/platform/v1), in particular the sections on [instances](/api/platform/v1/instances), [service groups](/api/platform/v1/servicegroups) and [certificates](/api/platform/v1/certificates). +* The [certificates documentation](/platform/certificates) for more information on how to use custom certificates with your services. diff --git a/pages/platform/images.mdx b/pages/platform/images.mdx index 126fdf0c..8befcdd2 100644 --- a/pages/platform/images.mdx +++ b/pages/platform/images.mdx @@ -14,29 +14,21 @@ You can choose to push an image directly to a node's local registry instead. This bypasses the central registry entirely. Skipping this extra network round trip speeds up deployments. It also reduces bandwidth consumption. -Read more about this in the [Registries documentation](/cli/registries). +Read more about this in the [registries documentation](/cli/registries). -:::caution[DISCLAIMER] -The local registry isn't currently available for the public Unikraft Cloud offering. +:::caution[**Limited Access**] +The local registry is only available in BYOC or on-prem Unikraft Cloud installations. ::: -1. **build**: You execute `unikraft build` with the -`--output` flag. This single command reads your Dockerfile or Kraftfile, compiles -the app locally, packages the resulting files into an OCI image, and -uploads that image directly to the Unikraft Cloud registry. - -1. **run**: You execute unikraft run and provide the uploaded image -name. The Unikraft Cloud controller pulls the image from the registry, -allocates the necessary system resources, and starts your app as a -running instance. - -## `Dockerfiles`, `Kraftfiles` and runtimes +{/* vale off */} +## Dockerfile, Kraftfile and runtime +{/* vale on */} On Unikraft Cloud, a `Dockerfile` guides the process of building images, and a `Kraftfile` guides the process of deploying the resulting image. Use the Python running example: -```bash title="" +```bash git clone https://github.com/unikraft-cloud/examples cd examples/httpserver-python3.12 ``` @@ -56,17 +48,14 @@ cmd: ["/usr/bin/python3", "/src/server.py"] ``` The file is simple. -It defines the start command, instructs the unikraft CLI to build the root filesystem, and specifies the base-compat:latest runtime. +It defines the start command, instructs the CLI to build the root filesystem, and specifies the `base-compat:latest` runtime. + On Unikraft Cloud, a runtime provides a base image containing the minimal code your app needs to execute. -The unikraft CLI combines your app files with this base image during the build step. -It uses the Dockerfile as the source to generate the directory structure. -When packaging these files into a root filesystem, Unikraft offers two main options: cpio and erofs. -The file specifies the erofs format to create a read-only disk image. -The erofs format decompresses data block-by-block only when the system accesses it. -This selective approach saves RAM and improves boot times. -In contrast, the cpio format must remove its entire contents into memory on startup. -Check out this [tutorial](/tutorials/rootfs-formats) comparing rootfs types to understand why Unikraft favors erofs. -For a more in depth explanation of the Kraftfile you can check the [reference](/kraftfile/v0.7) +The CLI combines your app files with this base image during the build step. +It uses the `Dockerfile` as the source to generate the directory structure. +When packaging these files into a root filesystem, Unikraft offers two main options: [CPIO and EROFS](/tutorials/rootfs-formats). +The file specifies the EROFS format to create a read-only disk image, which is the [preferred format for Unikraft Cloud](/tutorials/rootfs-formats#conclusion). +For a more in depth explanation of the `Kraftfile` you can check the [reference](/kraftfile/v0.7). The `Dockerfile` itself for the app looks as follows: @@ -92,10 +81,11 @@ COPY --from=build /etc/ld.so.cache /etc/ld.so.cache COPY ./server.py /src/server.py ``` -If you're familiar with `Dockerfiles` there is nothing unusual here, other than that Unikraft Cloud prefers usage of `FROM scratch` to keep images lean. -This multi-stage build creates a minimal container. +If you're familiar with `Dockerfile`s there is nothing unusual here, other than that Unikraft Cloud prefers usage of `FROM scratch` to keep images lean. + +This multi-stage build creates a minimal image. First, the process loads a standard Python 3.12 image to configure system dependencies. -Next, it starts an empty filesystem called scratch. +Next, it starts an empty filesystem called `scratch`. The builder extracts the compiled Python binary and essential shared system libraries from the first stage. It moves these exact files into the empty environment. Finally, the builder copies your app script into the container. @@ -105,7 +95,8 @@ All [guides](/guides/overview) on Unikraft Cloud, and the [examples](https://git :::tip You can also try a standard base image (for example, `FROM python:alpine`). -This choice may increase image size, memory use, and boot time. +This is a good start for [porting existing Dockerfiles to Unikraft Cloud](/tutorials/docker-to-ukc). +Be aware that this choice may increase image size, memory use, and boot time. ::: @@ -126,11 +117,17 @@ Start with the simplest workflow: create an image from a Python app and start an ```bash title="unikraft" unikraft build . --output /httpserver-python312:latest -unikraft run --metro=fra -p 443:8080/http+tls -m 512M --image /httpserver-python312:latest +unikraft run --metro fra \ + -p 443:8080/http+tls \ + -m 512M \ + --image /httpserver-python312:latest ``` ```bash title="kraft" -kraft cloud deploy -p 443:8080 -M 512Mi . +kraft cloud deploy \ + -p 443:8080 \ + -M 512Mi \ + . ``` @@ -181,7 +178,7 @@ timestamps: This command builds an image named `httpserver-python312@sha256:278cb8b1...` using the `Kraftfile` and `Dockerfile`. It then packages it, pushes it to the registry, and starts an instance named `httpserver-python312-ma2i9` from it. -The controller fetches the image from the registry to start the instance. +The platform fetches the image from the registry to start the instance. You can see your images by running the following command: @@ -200,14 +197,14 @@ kraft cloud image ls You should see output like: -```text title="unikraft" -REF DIGEST -/httpserver-python312 sha256:efcf60c1ab4677464f50f5fc7c0011efef7ea66d371586a22d1960bbeb53e2ee +```ansi title="unikraft" +REF DIGEST +/httpserver-python312 sha256:278cb8b14f9faf9c2702dddd8bfb6124912d82c11b4a2c6590b6e32fc4049472 ``` -```bash title="kraft" -NAME VERSION SIZE -/http-python latest 120 MB +```ansi title="kraft" +NAME VERSION SIZE +/httpserver-python312 latest 114 MB ``` @@ -236,11 +233,19 @@ For the next workflow, start many instances: ```bash title="unikraft" unikraft build . --output /httpserver-python312:latest -unikraft run --metro=fra -p 443:8080/http+tls -m 512M --replicas 2 --image /httpserver-python312:latest +unikraft run --metro fra \ + -p 443:8080/http+tls \ + -m 512M \ + --replicas 2 \ + --image /httpserver-python312:latest ``` ```bash title="kraft" -kraft cloud deploy -p 443:8080 -M 512Mi --replicas 2 . +kraft cloud deploy \ + -p 443:8080 \ + -M 512Mi \ + --replicas 2 \ + . ``` @@ -251,7 +256,7 @@ metro: fra name: httpserver-python312-lscmn uuid: 1121225d-a678-45a2-a8ce-335a9a6a2683 state: running -image: dragosgheorghioiu/httpserver-python312 +image: /httpserver-python312 resources: memory: 512MiB vcpus: 1 @@ -271,7 +276,7 @@ metro: fra name: httpserver-python312-xfgvc uuid: d021379d-9e03-4d0b-92a6-6fdf038e076a state: running -image: dragosgheorghioiu/httpserver-python312 +image: /httpserver-python312 resources: memory: 512MiB vcpus: 1 @@ -291,7 +296,7 @@ metro: fra name: httpserver-python312-7lha2 uuid: 0706c648-0c56-4c74-8dda-5a1aeff7d8b8 state: running -image: dragosgheorghioiu/httpserver-python312 +image: /httpserver-python312 resources: memory: 512MiB vcpus: 1 @@ -307,6 +312,7 @@ networks: timestamps: created: just now ``` + ```ansi title="kraft" [●] Deployed successfully! β”‚ @@ -318,9 +324,10 @@ timestamps: β”œ──────── image: oci://unikraft.io//httpserver-python312@sha256:5b922dfa1632af38c476b98fdd9f4314fb9c5e587d3d31255e6479108c057e88 β”œ─────── memory: 512 MiB β”œ────── service: restless-orangutan-1lklj6z5 - β”œ─ private fqdn: httpserver-python312-8mxq5.internal + β”œ─ private fqdn: httpserver-python312-lscmn.internal β””─── private ip: 10.0.6.37 ``` + Check that it worked by listing all instances with: @@ -338,23 +345,24 @@ kraft cloud instance list + ```ansi title="unikraft" -METRO NAME STATE IMAGE ARGS MEMORY VCPUS FQDN CREATED -fra httpserver-python312-7lha2 running /httpserver-python312 512MiB 1 hidden-silence-l3g520dw.fra.unikraft.app just now -fra httpserver-python312-xfgvc running /httpserver-python312 512MiB 1 icy-violet-hce1qwm5.fra.unikraft.app just now -fra httpserver-python312-lscmn running /httpserver-python312 512MiB 1 restless-orangutan-1lklj6z5.fra.unikraft.app just now +METRO NAME STATE IMAGE ARGS MEMORY VCPUS FQDN CREATED +fra httpserver-python312-7lha2 running /httpserver-python312 512MiB 1 hidden-silence-l3g520dw.fra.unikraft.app just now +fra httpserver-python312-xfgvc running /httpserver-python312 512MiB 1 icy-violet-hce1qwm5.fra.unikraft.app just now +fra httpserver-python312-lscmn running /httpserver-python312 512MiB 1 restless-orangutan-1lklj6z5.fra.unikraft.app just now ``` ```ansi title="kraft" -NAME FQDN STATE STATUS IMAGE MEMORY VCPUS ARGS BOOT TIME -httpserver-python312-7lha2 hidden-silence-l3g520dw.fra.unikraft.app running since 7mins dragosgheorghioiu/httpserver-python312@sh... 512 MiB 1 82.28 ms -httpserver-python312-xfgvc icy-violet-hce1qwm5.fra.unikraft.app running since 7mins dragosgheorghioiu/httpserver-python312@sh... 512 MiB 1 81.27 ms -httpserver-python312-lscmn restless-orangutan-1lklj6z5.fra.unikraft.app running since 7mins dragosgheorghioiu/httpserver-python312@sh... 512 MiB 1 84.28 ms +NAME FQDN STATE STATUS IMAGE MEMORY VCPUS ARGS BOOT TIME +httpserver-python312-7lha2 hidden-silence-l3g520dw.fra.unikraft.app running since 7mins /httpserver-python312@sh... 512 MiB 1 82.28 ms +httpserver-python312-xfgvc icy-violet-hce1qwm5.fra.unikraft.app running since 7mins /httpserver-python312@sh... 512 MiB 1 81.27 ms +httpserver-python312-lscmn restless-orangutan-1lklj6z5.fra.unikraft.app running since 7mins /httpserver-python312@sh... 512 MiB 1 84.28 ms ``` -Three instances run: the original plus two replicas. +Three instances runβ€”the original plus two replicas. ### Create instances from an existing image @@ -363,15 +371,18 @@ In this final workflow, take the existing image and start new instances from it: ```bash title="unikraft" -unikraft run --metro=fra -p 443:8080/http+tls -m 512M --image /httpserver-python312@sha256:1b815914eb568a06ca4bbfdfb7d6cf484a9e9a0947ba8e0e0f1664d972a25bca +unikraft run --metro fra \ + -p 443:8080/http+tls \ + -m 512M \ + --image /httpserver-python312@sha256:1b815914eb568a06ca4bbfdfb7d6cf484a9e9a0947ba8e0e0f1664d972a25bca ``` ```bash title="kraft" kraft cloud instance create \ - --start \ - --port 443:8080 \ - -M 512Mi \ - /httpserver-python312@sha256:1b815914eb568a06ca4bbfdfb7d6cf484a9e9a0947ba8e0e0f1664d972a25bca + --start \ + -p 443:8080 \ + -M 512Mi \ + /httpserver-python312@sha256:1b815914eb568a06ca4bbfdfb7d6cf484a9e9a0947ba8e0e0f1664d972a25bca ``` :::note @@ -385,48 +396,11 @@ If you omit the digest, the CLI automatically pulls the newest version of your i You now have a new instance created from the existing image. - -## REST API reference - -### List images from local registry - -Base address: `https://api..unikraft.cloud/v1` - -``` -GET /image-store -``` - -Returns all images accessible for your account and known by the platform from the local registry. -Each image object includes: - -| Field | Type | Description | -|-------|------|-------------| -| `created_at` | timestamp | Creation time. | -| `owner` | string | Owner of the image (omitted if not root account). | -| `url` | string | Full image address (includes digest). | -| `tags` | array of strings | List of shortened tags. | -| `initrd_or_rom` | bool | Whether the image includes an initrd or ROM component. | -| `size_in_bytes` | int64 | Total image size in bytes. | -| `args` | array of strings | Default arguments (omitted if none). | -| `env` | map of strings | Default environmental variables (omitted if none). | -| `users` | array of strings | Users with access to the image (omitted if not root account). | - - -### List images from central registry - -Base address `https://controlplane.unikraft.cloud/v1` - -``` -GET /images -``` - -Returns all images accessible for your account and known by the platform from the central registry. - -| Field | Type | Description | -|-------|------|-------------| -| `name` | string | Image reference (`{owner}/{registry}/{image}`). Excludes digest and tag. | - ## Learn more * The [CLI reference](/docs/cli/unikraft) and the [legacy CLI reference](/docs/cli/kraft/overview). * Unikraft Cloud's [REST API reference](/api/platform/v1). +* [Porting an existing container app to Unikraft Cloud](/tutorials/docker-to-ukc). +* [Tutorial](/tutorials/rootfs-volumes-roms) explaining the difference of storage media on Unikraft Cloud. +* [Rootfs image formats](/tutorials/rootfs-formats). +* [Rootfs image compression](/tutorials/rootfs-compression). diff --git a/pages/platform/instances.mdx b/pages/platform/instances.mdx index c4fd097e..bf0b2773 100644 --- a/pages/platform/instances.mdx +++ b/pages/platform/instances.mdx @@ -22,47 +22,211 @@ An instance can be in one of the following states: | `standby` | The instance has scaled-to-zero. The instance isn't running, but will be automatically started when there are incoming requests. | Unikraft Cloud reports these as instance `state` values via the endpoints. +You can visualize these states with the CLI. +Here is an example of an instance that's in `stopped` state: -## Stop reason + + +```bash title="unikraft" +unikraft instance get httpserver-go121-taud8 +``` + +```bash title="kraft" +kraft instance list httpserver-go121-taud8 +``` + + + + + +```ansi title="unikraft" +metro: fra +name: httpserver-go121-taud8 +uuid: bd281cc4-7640-4cbc-af48-5a44c2bd7b74 +state: standby +image: /httpserver-go121 +resources: + memory: 256MiB + vcpus: 1 +service: + name: floral-sun-54ixkmi6 + uuid: d20c8a02-53a8-4917-8c25-94b99ab59a88 + domains: + - fqdn: floral-sun-54ixkmi6.fra.unikraft.app +networks: +- uuid: 16ca6566-a368-45d3-b759-3abf0cc37d03 + private-ip: 10.0.0.37 + mac: 12:b0:0a:00:00:25 +timestamps: + created: 6 days ago +scale-to-zero: + enabled: true + policy: on + cooldown-time: 1s +stop: + reason: platform stop + exit-code: 1 +``` + +```ansi title="kraft" + uuid: bd281cc4-7640-4cbc-af48-5a44c2bd7b74 + name: httpserver-go121-taud8 + fqdn: floral-sun-54ixkmi6.fra.unikraft.app + private fqdn: httpserver-go121-taud8.internal + private ip: 10.0.0.37 + state: standby + created: 2026-05-28T13:53:40Z + started: 2026-05-28T13:54:43Z + stopped: 2026-05-28T13:54:45Z + start count: 3 + restart count: 0 + restart attempts: + next restart: + restart policy: never + stop origin: + stop reason: + app exit code: 1 + image: oci://unikraft.io//httpserver-go121:latest@sha256:7785ebdf6c1ffaf05c3beab4cffc2548790c088233a0ee51ac5d047e21e43d77 + memory: 256 MiB + vcpus: 1 + args: + env: + volumes: + service: floral-sun-54ixkmi6 + scale to zero: on + scale to zero cooldown: 1000 + scale to zero stateful: false + boot time: 75.70 ms + up time: 47.521s +``` + + + +You can retrieve even more instance details via the API: + +```bash title="GET /instances/bd281cc4-7640-4cbc-af48-5a44c2bd7b74" +curl -X GET \ + -H "Authorization: Bearer $UKC_TOKEN" \ + -H "Accept: application/json" \ + https://api.fra.unikraft.cloud/v1/instances/bd281cc4-7640-4cbc-af48-5a44c2bd7b74 +``` + +```json title="" +{ + "status": "success", + "data": { + "instances": [ + { + "status": "success", + "uuid": "bd281cc4-7640-4cbc-af48-5a44c2bd7b74", + "name": "httpserver-go121-taud8", + "created_at": "2026-05-28T13:53:40Z", + "state": "standby", + "private_fqdn": "httpserver-go121-taud8.internal", + "image": "oci://unikraft.io//httpserver-go121:latest@sha256:7785ebdf6c1ffaf05c3beab4cffc2548790c088233a0ee51ac5d047e21e43d77", + "memory_mb": 256, + "vcpus": 1, + "stop_reason": 39, + "exit_code": 1, + "stop_code": 33024, + "restart_policy": "never", + "scale_to_zero": { + "enabled": true, + "policy": "on", + "cooldown_time_ms": 1000, + "notify_time_ms": 0, + "stateful": false + }, + "boot_time_us": 75698, + "net_time_us": 79067, + "start_count": 3, + "started_at": "2026-05-28T13:54:43Z", + "stopped_at": "2026-05-28T13:54:45Z", + "uptime_ms": 47521, + "service_group": { + "uuid": "d20c8a02-53a8-4917-8c25-94b99ab59a88", + "name": "floral-sun-54ixkmi6", + "domains": [ + { + "fqdn": "floral-sun-54ixkmi6.fra.unikraft.app" + } + ] + }, + "network_interfaces": [ + { + "uuid": "16ca6566-a368-45d3-b759-3abf0cc37d03", + "private_ip": "10.0.0.37", + "mac": "12:b0:0a:00:00:25" + } + ], + "private_ip": "10.0.0.37", + "volumes": [] + } + ] + } +} +``` + +### Stop reason To understand why Unikraft Cloud stopped an instance or is shutting it down, it provides information about the stop reason. You can retrieve this information via the [`GET /instances`](/api/platform/v1/instances#list-instances) endpoint when an instance is in the `draining`, `stopping`, `stopped` or `standby` state. The `stop_reason` contains a bitmask that tells you the origin of the shutdown: -| Bit | 4 [F] | 3 [U] | 2 [P] | 1 [A] | 0 [K] | -|------|---------------------------|----------------------------|----------------------------|------------------------------------|---------------------------------------| -|Desc. | This was a force stop[^1] | Stop initiated by user[^2] | Stop initiated by platform | App exited - `exit_code` available | Kernel exited - `stop_code` available | + +| Bit | 5 [Z] | 4 [F] | 3 [U] | 2 [P] | 1 [A] | 0 [K] | +|------|---------------------------|---------------------------|----------------------------|--------------------------------|------------------------------------|---------------------------------------| +| Desc.| Stop due to scale-to-zero | This was a force stop[^1] | Stop initiated by user[^2] | Stop initiated by platform[^3] | App exited - `exit_code` available | Kernel exited - `stop_code` available | For example, the `stop_reason` has the following values in these scenarios: -| Value | Bitmask | Scenario | -|-------|-----------------|----------| -| `28` | `11100`/`FUP--` | Forced user-initiated shutdown. | -| `15` | `01111`/`-UPAK` | Regular user-initiated shutdown. The app and kernel have exited. The `exit_code` and `stop_code` show if the app and kernel shut down cleanly. | -| `13` | `01101`/`-UP-K` | The user initiated a shutdown but the app was forcefully killed by the kernel during shutdown. This can be the case if the image doesn't support a clean app exit or the app crashed after receiving a termination signal. Unikraft Cloud ignores the `exit_code` in this scenario. | -| `7` | `00111`/`--PAK` | Unikraft Cloud initiated the shutdown (for example, due to [scale-to-zero](/features/scale-to-zero)). The app and kernel have exited. The `exit_code` and `stop_code` show if the app and kernel shut down cleanly. | -| `3` | `00011`/`---AK` | The app exited. The `exit_code` and `stop_code` show if the app and kernel shut down cleanly. | -| `1` | `00001`/`----K` | The instance likely experienced a fatal crash and the `stop_code` contains more information about the cause of the crash. | -| `0` | `00000`/`-----` | The stop reason is unknown. | +| Value | Bitmask | Scenario | +|-------|-------------------|----------| +| `39` | `100111`/`Z--PAK` | Scale-to-zero shutdown. The platform initiated the stop because the instance scaled to zero, and both the app and kernel exited. | +| `31` | `011111`/`-FUPAK` | Forced user-initiated shutdown where the app and kernel still reported clean exits before the stop completed. | +| `28` | `011100`/`-FUP--` | Forced user-initiated shutdown without app or kernel exit information. | +| `15` | `001111`/`--UPAK` | Regular user-initiated shutdown. The app and kernel have exited. The `exit_code` and `stop_code` show if the app and kernel shut down cleanly. | +| `13` | `001101`/`--UP-K` | The user initiated a shutdown but the app was forcefully killed by the kernel during shutdown. This can happen if the image doesn't support a clean app exit or the app crashed after receiving a termination signal. In this case, ignore `exit_code`. | +| `7` | `000111`/`---PAK` | Platform-initiated shutdown. The app and kernel have exited. The `exit_code` and `stop_code` show if they shut down cleanly. | +| `3` | `000011`/`----AK` | The app exited. The `exit_code` and `stop_code` show if the app and kernel shut down cleanly. | +| `1` | `000001`/`-----K` | The instance likely experienced a fatal crash and the `stop_code` contains more information about the cause of the crash. | +| `0` | `000000`/`------` | The stop reason is unknown. | :::note There can be a short delay of a few milliseconds between the instance reaching the `stopped` state and Unikraft Cloud updating the `stop_reason` (or vice versa). ::: -### Exitcode +:::note +The scale-to-zero shutdown is a special case where the platform initiates the stop because the instance scaled to zero, and both the app and kernel exited. +In the API, the instance appears in the `standby` state, instead of `stopped`. +::: + +#### Exitcode The app exit code is what the app returns upon leaving its main entry point. The encoding of the `exit_code` is app specific. See the documentation of the app for more details. An `exit_code` of `0` indicates success or no failure. -### Stopcode +#### Stopcode + +{/* vale off */} +If bit 0 [K] is set in `stop_reason`, `stop_code` contains the 32-bit code reported by the guest kernel. +Unikraft Cloud also uses platform-defined values when the stop originates in the platform before the guest kernel can report its own code. +{/* vale on */} + +Currently the platform-defined values are: -Unikraft Cloud defines the `stop_code` by the kernel and has the following encoding irrespective of the app. +| Value | Symbol | Scenario | +|-------|--------------|----------| +| `0` | `UNKNOWN` | No platform-specific stop code is available. | +| `1` | `NOMEDIUM` | The instance couldn't start because the image or another required medium wasn't available, for example because an image pull failed. | + +When the guest kernel reports a stop code, it uses the following encoding: | Bits | 31 - 24 (8 bits) | 23 - 16 (8 bits) | 15 [T] | 14 - 8 (7 bits) | 7 - 0 (8 bits) | |------|------------------|------------------|----------------|-----------------|----------------| -| Desc.| Reserved[^3] | `errno` | `shutdown_bit` | `initlvl` | `reason` | +| Desc.| Reserved[^4] | `errno` | `shutdown_bit` | `initlvl` | `reason` | #### Reason @@ -97,7 +261,7 @@ A level of `127` means the instance was executing the app. :::note For example, an out-of-memory (OOM) situation triggers a page fault `PGFAULT(4)` with `errno` set to `ENOMEM(12)`. -In that case the `stop_code` is `0x000C7F04=818948` and the `stop_reason` is `----K (1)` if the stop occurred during app execution. +In that case the `stop_code` is `0x000C7F04=818948` and the `stop_reason` is `-----K (1)` if the stop occurred during app execution. ::: @@ -122,41 +286,15 @@ The `restart.next_at` field indicates when the next restart occurs if a back-off A manual start or stop of the instance aborts the restart sequence and resets the back-off delay. - +{/* vale off */} [^1]: A forced stop doesn't give the instance a chance to perform a clean shutdown. - Bits 0 [K] and 1 [A] can thus never occur for forced shutdowns. - As a result, there won't be an `exit_code` or `stop_code`. + Often, `exit_code` and `stop_code` are therefore not available. + If the guest reports an exit while the forced shutdown is in progress, bits 0 [K] and 1 [A] can still be set. [^2]: A stop command originating from the user travels through the platform controller. This is why bit 2 [P] will also always occur for user-initiated stops. -[^3]: The system sets reserved bits to 0. Ignore them. - -## Instance templates - -An instance template is a snapshotted instance that acts as a source for cloning new instances. -Cloning a template creates a new instance that resumes from the exact original system state. -It preserves memory contents, open files, and populated caches to bypass the standard boot sequence. -Once you convert an instance into a template, you can't reverse the process. - -To transition an actively running instance into a template, from the guest write the value 1 to `/uk/libukp/template_instance`: - -```bash title="Convert instance to template" -echo 1 > /uk/libukp/template_instance -``` - -This action instructs the controller to freeze running processes and save the instance as a reusable template. - -Templates support [delete locks](/platform/delete-locks), [tags](/platform/tagging), and [autokill](/features/autokill). -The platform measures autokill on a template from the time of last clone, not from instance stop time. - -### Nested templates - -You can clone a template from another template, creating a hierarchy. -Each clone inherits the parent's full state at clone time and can independently accumulate further state before you convert it into its own template. -This enables layered configurations, for example a base template with a warm database connection pool, and child templates specialized for different query workloads. - -No depth limit applies to nesting, and circular references aren't possible because the template state is immutable once set. - -For more information, check out the API reference for instance templates [here](/api/platform/v1/instances#create-template-instances-from-instances). +[^3]: Internally this bit is set when ukpd, the platform controller, initiates the stop. +[^4]: The system sets reserved bits to 0. Ignore them. +{/* vale on */} ## Creating instances @@ -173,6 +311,20 @@ The [`POST /instances`](/api/platform/v1/instances#create-instance) request acce All instances share the same image, memory, arguments, and service group configuration. Replicas receive independent names and UUIDs. +You can do this with the CLI as well: + + + +```bash title="unikraft" +unikraft instance create --replicas 2 ... +``` + +```bash title="kraft" +kraft cloud deploy --replicas 2 ... +``` + + + ### Wait for running By default [`POST /instances`](/api/platform/v1/instances#create-instance) returns as soon as the platform queues the instance. @@ -184,6 +336,7 @@ For example: ... "image": "nginx:latest", "memory_mb": 256, + "autostart": true, "timeout_s": 10, ... } @@ -195,6 +348,12 @@ When set, the platform rounds the value up to the next full second. Use `timeout_s` instead. ::: +:::note +The `unikraft` CLI automatically waits for the instance to be running after creation, up until a preconfigured timeout of 10 seconds. +The `kraft` CLI doesn't support this feature yet. +Additionally, if you create an instance using `kraft cloud instance create`, you need to specify the `--start` flag to autostart the instance. +::: + ### Delete on stop Pass `delete-on-stop` in the features array to automatically delete the instance when it stops: @@ -212,6 +371,20 @@ Pass `delete-on-stop` in the features array to automatically delete the instance This is useful for ephemeral workloadsβ€”batch jobs, one-shot tasksβ€”where you don't need to keep the stopped instance. +You can also use the CLI to set this up: + + + +```bash title="unikraft" +unikraft instance create --rm ... +``` + +```bash title="kraft" +# not supported, use unikraft CLI instead +``` + + + :::note To use the "delete-on-stop" feature, set the `restart_policy` to `never`. ::: @@ -238,6 +411,20 @@ Set `drain_timeout_ms` to allow the instance to finish serving in-flight connect } ``` +You can also use the CLI to set this up: + + + +```bash title="unikraft" +unikraft instance stop --drain-timeout 5s my-instance +``` + +```bash title="kraft" +kraft cloud instance stop --wait 5s my-instance +``` + + + While draining, the instance enters the `draining` state. The platform accepts no new connections. The instance stops once all connections close or the timeout elapses. @@ -247,46 +434,154 @@ You can't combine `drain_timeout_ms` with `force: true`. Setting both returns a `400` error. ::: +## Instance templates + +An instance template is a snapshotted instance that acts as a source for cloning new instances. +Cloning a template creates a new instance that resumes from the exact original system state. +It preserves memory contents, open files, and populated caches to bypass the standard boot sequence. +Once you convert an instance into a template, you can't reverse the process. + +To transition an actively running instance into a template, from the guest write the value 1 to `/uk/libukp/template_instance`: + +```bash title="Convert instance to template" +echo 1 > /uk/libukp/template_instance +``` + +This action instructs the controller to freeze running processes and save the instance as a reusable template. +You can also create a template from an instance (even when it's running) by calling [`POST /instances/templates`](/api/platform/v1/instances#create-template-instances-from-instances), or using the CLI: + + + +```bash title="unikraft" +unikraft instance template create httpserver-go121-taud8 +``` + +```bash title="kraft" +kraft cloud instance template create +``` + + + + + +```ansi title="unikraft" +metro: fra +name: httpserver-go121-taud8 +uuid: bd281cc4-7640-4cbc-af48-5a44c2bd7b74 +state: template +image: /httpserver-go121 +resources: + memory: 256MiB + vcpus: 1 +timestamps: + created: 6 days ago +scale-to-zero: + enabled: true + policy: on + cooldown-time: 1s +``` + +```ansi title="kraft" +bd281cc4-7640-4cbc-af48-5a44c2bd7b74 +``` + + + +You can list templates with [`GET /instances/templates`](/api/platform/v1/instances#list-template-instances) or the CLI: + + + +```bash title="unikraft" +unikraft instance template list +``` + +```bash title="kraft" +kraft instance template list +``` + + + + + +```ansi title="unikraft" +METRO NAME STATE IMAGE ARGS MEMORY VCPUS CREATED +fra httpserver-go121-taud8 template /httpserver-go121 256MiB 1 6 days ago +``` + +```ansi title="kraft" +NAME IMAGE ARGS CREATED AT +httpserver-go121-taud8 oci://unikraft.io//httpserver-go121:latest@sha256:7785ebdf6c1ffaf05c3beab4cffc2548790c088233a0ee51ac5d047e21e43d77 6 days ago +``` + + + +Now, you can create an instance from the template using the `unikraft` CLI: + +```bash title="unikraft" +unikraft instance create --metro fra \ + --template httpserver-go121-taud8 \ + --name httpserver-go121-clone +``` + +```ansi title="unikraft" +metro: fra +name: httpserver-go121-clone +uuid: 91796fdb-bd41-4c7b-856f-9e60356c96bd +state: standby +image: acioc/httpserver-go121 +resources: + memory: 256MiB + vcpus: 1 +networks: +- uuid: 8c778b8b-6ede-4e87-a825-159a2269fd19 + private-ip: 10.0.0.85 + mac: 12:b0:0a:00:00:55 +timestamps: + created: just now +scale-to-zero: + enabled: true + policy: on + cooldown-time: 1s +``` + +Templates support [delete locks](/platform/delete-locks), [tags](/platform/tagging), and [autokill](/features/autokill). +The platform measures autokill on a template from the time of last clone, not from instance stop time. + +### Nested templates + +You can clone a template from another template, creating a hierarchy. +Each clone inherits the parent's full state at clone time and can independently accumulate further state before you convert it into its own template. +This enables layered configurations, for example a base template with a warm database connection pool, and child templates specialized for different query workloads. + +No depth limit applies to nesting, and circular references aren't possible because the template state is immutable once set. + +For more information, check out the API reference for instance templates [here](/api/platform/v1/instances#create-template-instances-from-instances). + ## Instance metrics Use the [`GET /instances/metrics`](/api/platform/v1/instances#get-instances-metrics) endpoint to retrieve runtime statistics for one or more instances. With no identifiers, the platform returns metrics for all instances in your account. - -### Response format - -Include the header `Accept: application/json` to receive a JSON response. -Otherwise, you will receive metrics in Prometheus text format, that you can parse with Prometheus client libraries. - -**JSON response**: each instance object contains: - -| Field | Type | Description | -|-------|------|-------------| -| `state` | string | Current instance state. | -| `start_count` | int | Number of times the instance has started. | -| `restart_count` | int | Number of restarts (omitted if 0). | -| `started_at` | timestamp | Time of last start (omitted if unset). | -| `stopped_at` | timestamp | Time of last stop (omitted if unset). | -| `uptime_ms` | int64 | Current uptime in milliseconds. | -| `boot_time_us` | int | Boot time in microseconds (omitted if 0). | -| `net_time_us` | int | Network setup time in microseconds (omitted if 0). | -| `rss_bytes` | int64 | Resident set size in bytes. | -| `cpu_time_ms` | int64 | Consumed CPU time in milliseconds. | -| `rx_bytes` | int64 | Bytes received over the network. | -| `rx_packets` | int64 | Packets received from the network. | -| `tx_bytes` | int64 | Bytes transmitted over the network. | -| `tx_packets` | int64 | Packets transmitted over the network. | -| `nconns` | int | Number of active connections. | -| `nreqs` | int | Number of active HTTP requests. | -| `nqueued` | int | Number of queued connections or requests. | -| `ntotal` | int64 | Total connections or requests processed since start. | -| `wakeup_latency` | array | Histogram buckets. Each bucket has `bucket_ms` (upper bound, or `null` for overflow) and `count`. | -| `wakeup_latency_sum` | uint64 | Sum of all recorded wakeup latencies. | +For more information, check out this dedicated section on [instance metrics](/platform/metrics). ## Instance logs Use the [`GET /instances/logs`](/api/platform/v1/instances#get-instances-logs) endpoint to retrieve logs for one or more instances. The logs capture the instance's `stdout` and `stderr` output, and they're preserved across restarts and stops. +With the CLI, you can also follow the logs in real time: + + + +```bash title="unikraft" +unikraft instance logs --follow my-instance +``` + +```bash title="kraft" +kraft cloud instance logs --follow my-instance +``` + + + ## Learn more * The [CLI reference](/docs/cli/unikraft) and the [legacy CLI reference](/docs/cli/kraft/overview). diff --git a/pages/platform/metrics.mdx b/pages/platform/metrics.mdx index 3094acac..87828d8d 100644 --- a/pages/platform/metrics.mdx +++ b/pages/platform/metrics.mdx @@ -5,14 +5,20 @@ description: | Retrieve runtime statistics for your Unikraft Cloud instances. --- +import { Tabs, TabsContent, TabsList, TabsTrigger } from "zudoku/ui/Tabs" + Unikraft Cloud exposes runtime metrics for each instance: CPU time, memory usage, network I/O, connection counts, and scale-to-zero wakeup latencies. +:::caution +You can currently only view metrics via the [API](/api/platform/v1/instances#get-instances-metrics). +::: + ## Endpoints -``` +```text GET /instances/{uuid}/metrics GET /instances/metrics?uuid=[,…] -POST /instances/metrics (UUIDs or names in request body) +POST /instances/metrics # (UUIDs or names in request body) ``` With no identifiers, the platform returns metrics for all instances in your account. @@ -92,73 +98,238 @@ For instances with [scale-to-zero](/features/scale-to-zero) enabled, the `wakeup | `wakeup_latency` | array | Histogram buckets. Each bucket has `bucket_ms` (upper bound in ms, or `null` for the overflow bucket) and `count` (number of wakeups in that bucket). | | `wakeup_latency_sum` | uint64 | Sum of all recorded wakeup latencies in milliseconds. | -## Prometheus metrics +## Example + + + + JSON output + Prometheus output + + -When not requesting JSON, the endpoint returns standard Prometheus text format. -The available metric names are: + +```bash title="unikraft" +# not yet supported, use `unikraft api` instead +unikraft api /v1/instances/metrics?uuid=f31c1fa8-6188-4ebe-b6e0-b2de063ad7ea -X GET --metro fra ``` -instance_state -instance_start_count -instance_restart_count -instance_started_at -instance_stopped_at -instance_uptime_s -instance_boot_time_s -instance_net_time_s -instance_rss_bytes -instance_cpu_time_s -instance_rx_bytes -instance_tx_bytes -instance_rx_packets -instance_tx_packets -instance_nconns -instance_nreqs -instance_nqueued -instance_ntotal -instance_wakeup_latency_seconds (histogram) + +```bash title="API" +curl -X GET \ + -H "Authorization: Bearer $UKC_TOKEN" \ + "https://api.fra.unikraft.cloud/v1/instances/metrics?uuid=f31c1fa8-6188-4ebe-b6e0-b2de063ad7ea" ``` -The platform labels all metrics with `uuid` and `name`. + -## Example +```json title="JSON" +{ + "status": "success", + "data": { + "instances": [ + { + "status": "success", + "uuid": "f31c1fa8-6188-4ebe-b6e0-b2de063ad7ea", + "name": "httpserver-go1.21-wf9iz", + "state": "standby", + "start_count": 2, + "started_at": "2026-06-05T11:44:32Z", + "stopped_at": "2026-06-05T11:44:36Z", + "uptime_ms": 8161, + "boot_time_us": 97308, + "net_time_us": 102963, + "rss_bytes": 0, + "cpu_time_ms": 150, + "rx_bytes": 2012, + "rx_packets": 23, + "tx_bytes": 1196, + "tx_packets": 11, + "nconns": 0, + "nreqs": 0, + "nqueued": 0, + "ntotal": 2, + "wakeup_latency": [ + { + "bucket_ms": 1, + "count": 0 + }, + { + "bucket_ms": 2, + "count": 0 + }, + { + "bucket_ms": 4, + "count": 0 + }, + { + "bucket_ms": 8, + "count": 0 + }, + { + "bucket_ms": 16, + "count": 0 + }, + { + "bucket_ms": 32, + "count": 0 + }, + { + "bucket_ms": 64, + "count": 0 + }, + { + "bucket_ms": 128, + "count": 1 + }, + { + "bucket_ms": 256, + "count": 0 + }, + { + "bucket_ms": 512, + "count": 0 + }, + { + "bucket_ms": 1024, + "count": 0 + }, + { + "bucket_ms": 2048, + "count": 0 + }, + { + "bucket_ms": 4096, + "count": 0 + }, + { + "bucket_ms": null, + "count": 0 + } + ], + "wakeup_latency_sum": 104 + } + ] + } +} +``` + + + + + -Set `UKC_TOKEN` and `UKC_METRO` for the API call: +```bash title="unikraft" +# not yet supported, use `unikraft api` instead +unikraft api /v1/instances/metrics?uuid=f31c1fa8-6188-4ebe-b6e0-b2de063ad7ea -X GET --metro fra -H "Accept: text/plain" +``` -```bash title="" +```bash title="API" curl -X GET \ - -H "Authorization: Bearer $UKC_TOKEN" \ - -H "Content-Type: application/json" \ - "https://api.$UKC_METRO.unikraft.cloud/v1/instances/metrics?uuid=66d05e09-1436-4d1f-bbe6-6dc03ae48d7a" + -H "Authorization: Bearer $UKC_TOKEN" \ + -H "Accept: text/plain" \ + "https://api.fra.unikraft.cloud/v1/instances/metrics?uuid=f31c1fa8-6188-4ebe-b6e0-b2de063ad7ea" ``` -```json title="" -{ - "instances": [ - { - "uuid": "66d05e09-1436-4d1f-bbe6-6dc03ae48d7a", - "name": "nginx-1a747", - "state": "running", - "start_count": 3, - "uptime_ms": 42310, - "boot_time_us": 19810, - "rss_bytes": 12582912, - "cpu_time_ms": 14, - "rx_bytes": 4096, - "rx_packets": 8, - "tx_bytes": 8192, - "tx_packets": 12, - "nconns": 1, - "nreqs": 0, - "nqueued": 0, - "ntotal": 7 - } - ] -} + + +```text title="Prometheus" +# HELP instance_state 0=stopped,1=starting,2=running,3=draining,4=stopping,5=standby,6=template,7=deleted +# TYPE instance_state gauge +instance_state{instance_uuid="f31c1fa8-6188-4ebe-b6e0-b2de063ad7ea"} 5 + +# HELP instance_start_count Number of times the instance has been started +# TYPE instance_start_count counter +instance_start_count{instance_uuid="f31c1fa8-6188-4ebe-b6e0-b2de063ad7ea"} 2 + +# HELP instance_restart_count Number of times the instance has been restarted +# TYPE instance_restart_count counter +instance_restart_count{instance_uuid="f31c1fa8-6188-4ebe-b6e0-b2de063ad7ea"} 0 + +# HELP instance_started_at Time when the instance started +# TYPE instance_started_at gauge +instance_started_at{instance_uuid="f31c1fa8-6188-4ebe-b6e0-b2de063ad7ea"} 1780659872 + +# HELP instance_stopped_at Time when the instance stopped +# TYPE instance_stopped_at gauge +instance_stopped_at{instance_uuid="f31c1fa8-6188-4ebe-b6e0-b2de063ad7ea"} 1780659876 + +# HELP instance_uptime_s Uptime in seconds +# TYPE instance_uptime_s gauge +instance_uptime_s{instance_uuid="f31c1fa8-6188-4ebe-b6e0-b2de063ad7ea"} 8.161000 + +# HELP instance_boot_time_s Boot time in seconds +# TYPE instance_boot_time_s gauge +instance_boot_time_s{instance_uuid="f31c1fa8-6188-4ebe-b6e0-b2de063ad7ea"} 0.097308 + +# HELP instance_net_time_s Net time in seconds +# TYPE instance_net_time_s gauge +instance_net_time_s{instance_uuid="f31c1fa8-6188-4ebe-b6e0-b2de063ad7ea"} 0.102963 + +# HELP instance_rss_bytes Resident set size in bytes +# TYPE instance_rss_bytes gauge +instance_rss_bytes{instance_uuid="f31c1fa8-6188-4ebe-b6e0-b2de063ad7ea"} 0 + +# HELP instance_cpu_time_s Consumed CPU time in seconds +# TYPE instance_cpu_time_s counter +instance_cpu_time_s{instance_uuid="f31c1fa8-6188-4ebe-b6e0-b2de063ad7ea"} 0.150000 + +# HELP instance_rx_bytes Amount of bytes received over network +# TYPE instance_rx_bytes counter +instance_rx_bytes{instance_uuid="f31c1fa8-6188-4ebe-b6e0-b2de063ad7ea"} 2012 + +# HELP instance_tx_bytes Amount of bytes transmitted over network +# TYPE instance_tx_bytes counter +instance_tx_bytes{instance_uuid="f31c1fa8-6188-4ebe-b6e0-b2de063ad7ea"} 1196 + +# HELP instance_rx_packets Count of packets received from network +# TYPE instance_rx_packets counter +instance_rx_packets{instance_uuid="f31c1fa8-6188-4ebe-b6e0-b2de063ad7ea"} 23 + +# HELP instance_tx_packets Count of packets transmitted over network +# TYPE instance_tx_packets counter +instance_tx_packets{instance_uuid="f31c1fa8-6188-4ebe-b6e0-b2de063ad7ea"} 11 + +# HELP instance_nconns Number of active connections +# TYPE instance_nconns gauge +instance_nconns{instance_uuid="f31c1fa8-6188-4ebe-b6e0-b2de063ad7ea"} 0 + +# HELP instance_nreqs Number of active requests +# TYPE instance_nreqs gauge +instance_nreqs{instance_uuid="f31c1fa8-6188-4ebe-b6e0-b2de063ad7ea"} 0 + +# HELP instance_nqueued Number of queued connections/requests +# TYPE instance_nqueued gauge +instance_nqueued{instance_uuid="f31c1fa8-6188-4ebe-b6e0-b2de063ad7ea"} 0 + +# HELP instance_ntotal Number of processed connections/requests +# TYPE instance_ntotal counter +instance_ntotal{instance_uuid="f31c1fa8-6188-4ebe-b6e0-b2de063ad7ea"} 2 + +# HELP instance_wakeup_latency_seconds Wakeup latencies in seconds +# TYPE instance_wakeup_latency_seconds histogram +instance_wakeup_latency_seconds{le="0.001000"} 0 +instance_wakeup_latency_seconds{le="0.002000"} 0 +instance_wakeup_latency_seconds{le="0.004000"} 0 +instance_wakeup_latency_seconds{le="0.008000"} 0 +instance_wakeup_latency_seconds{le="0.016000"} 0 +instance_wakeup_latency_seconds{le="0.032000"} 0 +instance_wakeup_latency_seconds{le="0.064000"} 0 +instance_wakeup_latency_seconds{le="0.128000"} 1 +instance_wakeup_latency_seconds{le="0.256000"} 1 +instance_wakeup_latency_seconds{le="0.512000"} 1 +instance_wakeup_latency_seconds{le="1.024000"} 1 +instance_wakeup_latency_seconds{le="2.048000"} 1 +instance_wakeup_latency_seconds{le="4.096000"} 1 +instance_wakeup_latency_seconds{le="+Inf"} 1 +instance_wakeup_latency_seconds_sum 104 +instance_wakeup_latency_seconds_count 1 ``` + + + ## Learn more -* Unikraft Cloud's [REST API reference](/api/platform/v1) -* [Instance states](/platform/instances#instance-states) -* [Scale-to-zero](/features/scale-to-zero) +* Unikraft Cloud's [REST API reference](/api/platform/v1). +* [Instance states](/platform/instances#instance-states). +* [Scale-to-zero](/features/scale-to-zero) feature. diff --git a/pages/platform/metros.mdx b/pages/platform/metros.mdx index da5e68a5..42a08d38 100644 --- a/pages/platform/metros.mdx +++ b/pages/platform/metros.mdx @@ -13,9 +13,7 @@ By default, at present, Unikraft Cloud supports the following metros: | `sin` | πŸ‡ΈπŸ‡¬ | Singapore | | `was` | πŸ‡ΊπŸ‡Έ | Washington DC, USA | -:::tip - -You can list the metros available to your account via the CLI. +You can list the metros available to your account via the CLI: @@ -29,10 +27,10 @@ kraft cloud metro ls -::: +Old metros such as `fra0` are nearing end of life, and aren't available for new deployments. Unikraft Cloud continues to add new locations worldwide so you can provision services close to your users. -If there's a metro location you'd need, please contact Unikraft Cloud on Discord or write to support@unikraft.cloud. +If there's a metro location you'd need, please contact Unikraft Cloud on [Discord](https://kraft.cloud/discord) or write to [support@unikraft.cloud](mailto:support@unikraft.cloud). The list of potential metros is: | IATA code | | Region | @@ -71,12 +69,10 @@ The list of potential metros is: For more information on how Unikraft Cloud connects metros together, and for more information about how to reduce in/egress costs with existing deployments at other IaaS providers, please [get in touch](mailto:support@unikraft.cloud). - ## Self-hosted/on-premise metros If you require running solutions in-house or on-premise, either for SOC/ISO/HIPPA compliance or for other security, performance or cost-saving reasons, please [get in contact](mailto:support@kunikraft.cloud) for more details. - ## Usage You can set the metro you wish to use by setting the `--metro` flag. @@ -85,24 +81,38 @@ Use the IATA code descriptor to target the specific metro. For example with `fra`: - ```bash title="unikraft" - unikraft run --metro=fra -p 443:8080/http+tls --image=nginx:latest - ``` - - ```bash title="kraft" - # Via CLI flag - kraft cloud --metro fra run -p 443:8080 nginx:latest - - # or via environmental variable - export UKC_METRO=fra - kraft cloud run -p 443:8080 nginx:latest - ``` - The `UKC_METRO` environment variable is only supported by the legacy CLI. + +```bash title="unikraft" +unikraft run --metro fra \ + -p 443:8080/http+tls \ + --image /nginx:latest +``` + +```bash title="kraft" +# via CLI flag +kraft cloud vm create --metro fra \ + -p 443:8080 \ + /nginx:latest + +# or via environmental variable +export UKC_METRO=fra +kraft cloud vm create \ + -p 443:8080 \ + /nginx:latest +``` + -For on-premise or enterprise users, set the FQDN of the API endpoint as the -metro argument, e.g.: +For on-premise or enterprise users, you need to set the API endpoint of the metro you wish to use. +With `kraft`, this means setting the `UKC_METRO` environment variable: -```bash title="" +```bash title="kraft" export UKC_METRO=https://api.example.cloud/v1 ``` + +With `unikraft`, you need to define a custom [profile](/tutorials/kraftkit-to-unikraft#profile-management) and give an identifier to the metro you wish to use: + +```bash title="unikraft" +unikraft profile use +unikraft run --metro ... +``` diff --git a/pages/tutorials/network-communication.mdx b/pages/platform/networking.mdx similarity index 58% rename from pages/tutorials/network-communication.mdx rename to pages/platform/networking.mdx index a4c28f67..f8e78537 100644 --- a/pages/tutorials/network-communication.mdx +++ b/pages/platform/networking.mdx @@ -1,55 +1,83 @@ --- -title: Network Communication +title: Networking navigation_icon: network --- -:::caution -As of writing this, [scale-to-zero triggers](/tutorials/scale-to-zero-triggers) are unavailable on internal networking and, as such you must use external networking. -Moreover, there is no load balancing on internal networks, this being up to the user. -::: - -## Communication types - The platform separates Unikraft Cloud instances on a per-user level. -This means that all instances part of the scope of a user can "see" each other in the private internal network and they can communicate with each other. +This means that all instances within a user's scope can "see" each other on the private internal network and communicate with each other. This communication is private and unencrypted, and doesn't reach the outside world. You can communicate internally by using the Private IP or Private FQDN. At the same time, when using the Public FQDN the connection goes through the external load balancer applied to every public instance. This means that an entirely different code path resolves the connection, and also the connection is always TLS encrypted. -### External communication +## External communication External communication refers to every request going over the public FQDN of an instance. -This means that the request goes through the external load balancer, and as such TLS encryption is mandatory. +This means that the request goes through the external load balancer, and as such, TLS encryption is mandatory. This also means that the instance will be load balanced with all other public instances behind the same [service](/features/load-balancing). The request will do a round trip from the client through the load balancer and to the instance. This communication is best suited for frontend-to-service communication, public APIs, web services, and more. -### Internal communication +## Internal communication -Internal communication refers to every request going over the private network of a user. -Every instance, by default, receives a private IP and a private FQDN (of the format `.internal`). +Internal communication refers to every request going over a user's private network. This means that the request stays within the user internal network, and doesn't go through the external load balancer. This also means that the system won't encrypt the connection by default. This communication is best suited for service-to-service communication, microservices, databases, caches, and more. For example, you wouldn't want to expose your database publicly, but you would want your app to be able to connect to it internally. -:::note -When using the legacy CLI tunnel command to connect to your instances you are using the internal network to reach your instance. -The command creates a public-facing instance that tunnels your requests to the private instance over the internal network. -This is a good way to keep your instances private while still being able to access them from your local machine. +:::tip +Even when using internal communication, scale-to-zero triggers will still work as expected, and your instances will scale down to zero when not in use. ::: -## Conclusion +Every instance, by default, receives a private IP and a private FQDN (of the format `.internal`). +You can see the private IP of your instance by grabbing its details: + + + +```bash title="unikraft" +unikraft instance get +``` + +```bash title="kraft" +kraft cloud instance get +``` -When building your app architecture on Unikraft Cloud, it's important to understand the differences between internal and external communication. -External communication is best suited for public-facing services, while internal communication is ideal for private interactions between services within your app. -By leveraging both types of communication, you can deploy a robust and efficient app architecture on Unikraft Cloud. + + + + +```ansi title="unikraft" +... +networks: +- uuid: afbd8ce2-daf5-4c47-888a-96bbf1c4a347 + private-ip: 10.0.0.49 + mac: 12:b0:0a:00:00:31 +... +``` + +```ansi title="kraft" +... + private fqdn: httpserver-go121-rs85y.internal + private ip: 10.0.0.49 +... +``` + + + +Check out this [guide](/guides/httpserver-flask-redis) for an example of how to use internal communication to connect a Flask app to a Redis instance, without exposing the latter to the internet. + +:::tip +When using the [legacy CLI tunnel](/cli/kraft/tunnel) command to connect to your instances you are using the internal network to reach your instance. +The command creates a public-facing instance that tunnels your requests to the private instance over the internal network. +This is a good way to keep your instances private while still being able to access them from your local machine. +::: ## Learn more * [Load balancing](/features/load-balancing) of services. * [Platform services](/platform/services) and how they work. * [Scale to zero triggers](/tutorials/scale-to-zero-triggers) for automatic instance scaling. +* Deploy a [Go HTTP server with Redis](/guides/httpserver-go1.22-redis) using internal communication. * The [CLI reference](/docs/cli/unikraft) and the [legacy CLI reference](/docs/cli/kraft/overview). diff --git a/pages/platform/quotas.mdx b/pages/platform/quotas.mdx index 68e27fc7..7b8bdc14 100644 --- a/pages/platform/quotas.mdx +++ b/pages/platform/quotas.mdx @@ -6,16 +6,138 @@ description: | --- Unikraft Cloud enforces per-account resource quotas to govern how many instances, service groups, volumes, and compute resources you can provision. -Use the quotas endpoint to inspect your current usage and limits. -## Endpoints + +```bash title="unikraft" +unikraft metro quotas ``` -GET /users/quotas -GET /users/{uuid}/quotas + +```bash title="kraft" +kraft cloud quotas +``` + +```bash title="API" +curl -X GET \ + -H "Authorization: Bearer $UKC_TOKEN" \ + -H "Content-Type: application/json" \ + "https://api.fra.unikraft.cloud/v1/users/quotas" +``` + + + + + +```ansi title="unikraft" +  fra  + +user uuid: +user name: + +image storage: β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ 702 MiB/1.0 GiB + +active instances: β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ 5/64 +total instances: β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ 5/64 +active vcpus: β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ 5/64 +vcpu limit: 1-1 + +active used memory: β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ 2.5GiB/32GiB +memory size limits: 16MiB-8GiB + +exposed services: β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ 10/253 +services: β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ 5/253 + +volumes: enabled +active volumes: β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ 0/253 +used volume space: β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ 0B/20GiB +volume size limits: 8MiB-20GiB + +autoscale: enabled +autoscale limit: 0-16 +scale-to-zero: enabled + + Tab/←→: switch metro q: quit +``` + +```ansi title="kraft" + user uuid: + user name: + + image storage: β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ 702 MiB/1.0 GiB + + active instances: β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ 5/64 + total instances: β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ 5/64 + active vcpus: β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ 5/64 + vcpu limit: 1-1 + + active used memory: β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ 2.5 GiB/32 GiB + memory size limits: 16 MiB-8.0 GiB + + exposed services: β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ 10/253 + services: β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ 5/253 + + volumes: enabled + active volumes: β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ 0/253 + used volume space: β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ 0 B/20 GiB + volume size limits: 8.0 MiB-20 GiB + + autoscale: enabled + autoscale limit: 0-16 + scale-to-zero: enabled +``` + +```json title="API" +{ + "status": "success", + "data": { + "quotas": [ + { + "status": "success", + "uuid": "", + "used": { + "instances": 5, + "live_instances": 5, + "live_vcpus": 5, + "live_memory_mb": 2560, + "service_groups": 5, + "services": 10, + "volumes": 0, + "total_volume_mb": 0 + }, + "hard": { + "instances": 64, + "live_instances": 64, + "live_vcpus": 64, + "live_memory_mb": 32768, + "service_groups": 253, + "services": 253, + "volumes": 253, + "total_volume_mb": 20480 + }, + "limits": { + "min_memory_mb": 16, + "max_memory_mb": 8192, + "min_vcpus": 1, + "max_vcpus": 1, + "min_volume_mb": 8, + "max_volume_mb": 20480, + "min_autoscale_size": 0, + "max_autoscale_size": 16 + } + } + ] + }, + "op_time_us": 46 +} ``` -`GET /users/quotas` (no UUID) returns the quota for the authenticated user. + + +:::note +The `hard.live_instances` field is a compatibility alias for `hard.live_vcpus`. +It's kept for backward compatibility and the platform may drop it in a future API version. +Prefer `hard.live_vcpus`. +::: ## Response structure @@ -62,66 +184,6 @@ These define the minimum and maximum values you can specify when creating indivi | `min_autoscale_size` | int | Minimum autoscale group size. | | `max_autoscale_size` | int | Maximum autoscale group size. | -## Example - -Set `UKC_TOKEN` and `UKC_METRO` for the API call: - -```bash title="" -curl -X GET \ - -H "Authorization: Bearer $UKC_TOKEN" \ - -H "Content-Type: application/json" \ - "https://api.$UKC_METRO.unikraft.cloud/v1/users/quotas" -``` - -```json title="" -{ - "quotas": [ - { - "status": "ok", - "uuid": "a1b2c3d4-...", - "used": { - "instances": 3, - "live_instances": 2, - "live_vcpus": 2, - "live_memory_mb": 512, - "service_groups": 2, - "services": 4, - "volumes": 1, - "total_volume_mb": 512 - }, - "hard": { - "instances": 50, - "live_vcpus": 16, - "live_memory_mb": 4096, - "service_groups": 20, - "services": 40, - "volumes": 20, - "total_volume_mb": 20480 - }, - "limits": { - "min_memory_mb": 16, - "max_memory_mb": 4096, - "min_vcpus": 1, - "max_vcpus": 8, - "min_volume_mb": 128, - "max_volume_mb": 10240, - "min_autoscale_size": 0, - "max_autoscale_size": 10 - } - } - ] -} -``` - -:::note -The `hard.live_instances` field is a compatibility alias for `hard.live_vcpus`. -It's kept for backward compatibility and the platform may drop it in a future API version. -Prefer `hard.live_vcpus`. -::: - ## Learn more -* Unikraft Cloud's [REST API reference](/api/platform/v1) -* [Instances](/platform/instances) -* [Volumes](/platform/volumes) -* [Services](/platform/services) +* Unikraft Cloud's [REST API reference](/api/platform/v1), in particular the [quotas endpoint](/api/platform/v1/users#get-current-user-quotas). diff --git a/pages/platform/services.mdx b/pages/platform/services.mdx index 0373fd3f..5794934b 100644 --- a/pages/platform/services.mdx +++ b/pages/platform/services.mdx @@ -5,25 +5,85 @@ navigation_icon: group import { Tabs, TabsContent, TabsList, TabsTrigger } from "zudoku/ui/Tabs" -On creation, Unikraft Cloud assigns each instance a private IP address and private FQDN for internal connectivity. -It connects them to the Internet through a *service*: create a service and attach instances to it. - -:::note +On creation, Unikraft Cloud assigns each instance a private IP address and private FQDN for internal connectivity, of the form `.internal`. +To connect to an instance from the Internet, you need to create a service and attach the instance to it. A service defines how to reach a group of instances from the outside world. -It maps an external FQDN port to an internal port common to all instances. +It maps an external FQDN port to an internal port common to all instances within the service. The platform [load balances](/features/load-balancing) incoming connections across instances. + +:::caution Avoid placing apps that expose different ports in the same service. ::: +## Instance service groups + +If you specify a port mapping in the CLI deployment commands, the platform creates a service for you and attaches the instance to it: + + + +```bash title="unikraft" +unikraft run --metro fra \ + -m 256M \ + -p 443:8080/http+tls \ + --image /nginx:latest +``` + +```bash title="kraft" +kraft cloud instance create \ + --start \ + -M 256Mi \ + -p 443:8080/http+tls \ + /nginx:latest +``` -If you use the deploy or run flow, part of the output lists a service, for example: + -```ansi title="" - β”œ─ service: frosty-sky-vz8kwsm - ``` + + +```ansi title="unikraft" +metro: fra +name: nginx-67zbu +uuid: 8a8bc1b9-0af6-420e-a426-190dc2da9eaa +state: starting +image: /nginx +resources: + memory: 256MiB + vcpus: 1 +service: + uuid: a942b9b5-ad17-3ffe-dcd2-ef4331f9087a + name: nameless-fog-0tvh1uov # <---- service name + domains: + - fqdn: nameless-fog-0tvh1uov.fra.unikraft.app # <---- service domain +networks: +- uuid: 62d9bbf0-aec8-61f6-7bdb-86edf63dd068 + private-ip: 10.0.3.3 + mac: 12:b0:c6:23:ed:15 +timestamps: + created: just now +``` + +```ansi title="kraft" +[●] Deployed successfully! + β”‚ + β”œ───────── name: nginx-67zbu + β”œ───────── uuid: 8a8bc1b9-0af6-420e-a426-190dc2da9eaa + β”œ──────── metro: https://api.fra.unikraft.cloud/v1 + β”œ──────── state: starting + β”œ─────── domain: https://nameless-fog-0tvh1uov.fra.unikraft.app # <---- service domain + β”œ──────── image: oci://unikraft.io//nginx@sha256:f51ecc121c9ca34abb88a2bc6a69765501304f7893f7e85af15fbec3dc86e2bd + β”œ─────── memory: 256 MiB + β”œ────── service: nameless-fog-0tvh1uov # <---- service name + β”œ─ private fqdn: nginx-67zbu.internal + β””─── private ip: 10.0.3.3 +``` + + + +This ties the service to the lifetime of the instance, so the platform deletes it when you delete the instance. + +## Persistent service groups -Because the deploy or run flow is a single command to deploy a service, it automatically creates a service and attaches the new instance to it. The rest of this guide shows how to create a service first, then use the CLI to create and attach instances to it. First, create a new service with the CLI: @@ -31,38 +91,72 @@ First, create a new service with the CLI: ```bash title="unikraft" -unikraft services create \ - --set name=my-service \ - --set metro=fra \ - --set services=443:8080/tls+http +unikraft services create --metro fra \ + --name my-service \ + --services 443:8080/http+tls ``` ```bash title="kraft" -kraft cloud service create --name my-service 443:8080/http+tls +kraft cloud service create \ + --name my-service \ + 443:8080/http+tls +``` + + + + + +```ansi title="unikraft" +metro: fra +name: my-service +uuid: 9bfe76ea-2679-482a-954e-577de7d122b3 +persistent: true +limits: + soft: 1 + hard: 65535 +timestamps: + created: just now +domains: +- fqdn: my-service-pc4xjplc.fra.unikraft.app +services: +- source: 443 + destination: 8080 + handlers: ["tls", "http"] +``` + +```ansi title="kraft" +NAME FQDN SERVICES INSTANCES CREATED AT PERSISTENT +my-service my-service-f4744h0c.fra.unikraft.app 443:8080/tls+http 1 second ago true ``` This creates a new service named `my-service` listening on port 443. -Unikraft Cloud terminates TLS and sends HTTP to port 8080. +Unikraft Cloud terminates TLS and sends HTTP traffic to the app's port 8080. This example assumes that the app opens port 8080. Now use the CLI with the service flag to attach the instance to the `my-service` service. -For example, from the [Go web server guide](/guides/go): +For example, from the [Go HTTP server guide](/guides/httpserver-go1.21): ```bash title="unikraft" git clone https://github.com/unikraft-cloud/examples -cd examples/http-go1.21/ -unikraft build . --output /http-go121:latest -unikraft run --metro=fra --service my-service -m 256MiB --image=/http-go121:latest +cd examples/httpserver-go1.21/ +unikraft build . --output /httpserver-go121:latest +unikraft run --metro fra \ + -m 256M \ + --service my-service \ + --image /httpserver-go121:latest ``` ```bash title="kraft" git clone https://github.com/unikraft-cloud/examples -cd examples/http-go1.21/ -kraft cloud deploy --service my-service -M 256 . +cd examples/httpserver-go1.21/ +kraft cloud deploy \ + -M 256Mi \ + --service my-service \ + . ``` @@ -70,43 +164,61 @@ kraft cloud deploy --service my-service -M 256 . This creates a new Go web server instance and immediately attaches it to the `my-service` service. The output shows the instance address and other details: -```ansi title="" + + +```ansi title="unikraft" +metro: fra +name: httpserver-go121-9a2wv +uuid: 8bb34040-9434-4a28-bd1e-c24ee532e2da +state: starting +image: /httpserver-go121 +resources: + memory: 256MiB + vcpus: 1 +service: + uuid: 9bfe76ea-2679-482a-954e-577de7d122b3 + name: my-service + domains: + - fqdn: my-service-f4744h0c.fra.unikraft.app +networks: +- uuid: 51f79dc1-e989-908d-894b-bdf0a87e7901 + private-ip: 10.0.3.3 + mac: 12:b0:57:91:bb:a5 +timestamps: + created: just now +``` + +```ansi title="kraft" [●] Deployed successfully! β”‚ - β”œ────────── name: http-go121-fkt1x - β”œ────────── uuid: 858da707-1851-46cb-9667-05b951471222 - β”œ───────── state: running - β”œ─────────── url: https://my-service-rrtckyyi.fra.unikraft.app - β”œ───────── image: http-go121@sha256:4d536236d226781874c3ad930dbc936c4f407aa3483a1f8e961ba63a7a72c78d - β”œ───── boot time: 17.14 ms - β”œ──────── memory: 256 MiB - β”œ─────── service: my-service - β”œ── private fqdn: http-go121-fkt1x.internal - β”œ──── private ip: 172.16.6.6 - β””────────── args: /server + β”œ───────── name: httpserver-go121-9a2wv + β”œ───────── uuid: 8bb34040-9434-4a28-bd1e-c24ee532e2da + β”œ──────── metro: https://api.fra.unikraft.cloud/v1 + β”œ──────── state: starting + β”œ─────── domain: https://my-service-f4744h0c.fra.unikraft.app + β”œ──────── image: oci://unikraft.io//httpserver-go121@sha256:b16d61bb7898e764d8c11ab5a0b995e8c25a25b5ff89e161fc994ebf25a75680 + β”œ─────── memory: 256 MiB + β”œ────── service: my-service + β”œ─ private fqdn: httpserver-go121-9a2wv.internal + β””─── private ip: 10.0.3.3 ``` -In this case, the instance name is `http-go121-fkt1x`. -The address is `https://my-service-rrtckyyi.fra.unikraft.app`. -These values differ for each run. + + + +In this case, the instance name is `httpserver-go121-9a2wv`. +The address is `https://my-service-f4744h0c.fra.unikraft.app`. -Use `curl` to query the Go web server: +Use `curl` to query the Go HTTP server: -```bash title="" -curl https://my-service-rrtckyyi.fra.unikraft.app +```bash +curl https://my-service-f4744h0c.fra.unikraft.app ``` ```text title="" -hello, world! +Hello, world! ``` -:::tip - -If you specify a port with the publish flag when using the deploy or run flow, the command creates a service automatically. -In that case the platform deletes the service when the instance ends, and you can't define the service name. - -::: - That's it. In the end, if you want to remove a service, use: @@ -168,54 +280,34 @@ For example, the following creates the service `my-service` with three published ```bash title="unikraft" -unikraft services create \ - --set name=my-service \ - --set metro=fra \ - --set services=443:8080/tls+http \ - --set services=80:8080/http+redirect \ - --set services=10000:10000/tls +unikraft services create --metro fra \ + --name my-service \ + --services 443:8080/tls+http \ + --services 80:8080/http+redirect \ + --services 10000:10000/tls ``` ```bash title="kraft" -kraft cloud service create --name my-service 443:8080/http+tls 80:8080/http+redirect 10000:10000/tls +kraft cloud service create \ + --name my-service \ + 443:8080/http+tls \ + 80:8080/http+redirect \ + 10000:10000/tls ``` -## `UDP` Support - -`UDP` is a defined service protocol alongside `TCP`. -Unikraft Cloud stores and reports it in the `protocol` field of each published port. - -You can use `UDP` for both internal VM-to-VM traffic and external traffic. - -### External `UDP` requirements - -To expose `UDP` externally for an user, the box must have an assigned IP address (primary or another assigned IP). -That IP must appear in the `addresses` array in `users.json` for that user: - -```json title="users.json" -{ - [ - "name": "user", - "addresses": [ - { - "ip": "a.b.c.d", - "internal_ip": "x.y.z.w", - "host": "host_name" - } - ] - ] -} -``` - -The `internal_ip` field is optional and used when the box routes traffic through an internal IP. - -You must also list the `UDP` IPs in the `additional_ip_addresses` directive in `nginx.conf.template` (space-separated). +{/* vale off */} +## UDP services +{/* vale on */} -### Creating a `UDP` service +:::caution[**Limited Access**] +UDP services are only available in BYOC or on-prem Unikraft Cloud installations. +CLI support is coming soon. +::: -To create a `UDP` service, set both `protocol` and `ip`: +You can use UDP for both internal VM-to-VM traffic and external traffic. +To create a UDP service, use `POST /services` and set both `protocol` and `ip`: ```json title="POST /services" { @@ -230,7 +322,7 @@ To create a `UDP` service, set both `protocol` and `ip`: } ``` -The service `ip` must match an `addresses[].ip` value in `users.json` for the user. +The IP address must be one of the IP addresses allocated to your user. ## Connection limits @@ -238,34 +330,33 @@ Every service group has a soft and a hard connection limit that the load balance | Field | Default | Range | Description | |-------|---------|-------|-------------| -| `soft_limit` | `1` | 1–65535 | The load balancer starts queuing new connections once the number of active connections reaches this value. | -| `hard_limit` | `65535` | 1–65535 | The load balancer rejects new connections once the number of active connections reaches this value. | +| `soft_limit` | `1` | `1-65535` | The load balancer starts queuing new connections once the number of active connections reaches this value. | +| `hard_limit` | `65535` | `1-65535` | The load balancer rejects new connections once the number of active connections reaches this value. | `soft_limit` must be less than or equal to `hard_limit`. Setting `soft_limit > hard_limit` returns a `400` error. -You can set both fields at service group creation time and update them later with `PATCH /services/{name}`. - -```json title="POST /services" -{ - "name": "my-service", - "soft_limit": 100, - "hard_limit": 500 -} -``` +You can use the CLI to set the connection limits for a service: -## Persistent service groups - -A service group is **persistent** when your user account owns it rather than a specific instance. + -- A service group created with the CLI (or `POST /services`) is persistent. - It survives independently of any instances attached to it. -- A service group created implicitly via the publish flag on instance creation belongs to the instance. - The platform deletes it when you delete that instance. +```bash title="unikraft" +unikraft services create --metro fra \ + --name my-service \ + --services 443:8080/http+tls \ + --soft-limit 5 \ + --hard-limit 10 +``` -The `GET /services/{name}` response includes a `persistent` field (`bool`) that indicates which case applies. +```bash title="kraft" +kraft cloud service create \ + --name my-service \ + 443:8080/http+tls \ + --soft-limit 5 \ + --hard-limit 10 +``` -## Learn more + * The [CLI reference](/docs/cli/unikraft) and the [legacy CLI reference](/docs/cli/kraft/overview). -* Unikraft Cloud's [REST API reference](/api/platform/v1), in particular the section on [service groups](/api/platform/v1/service-groups). +* Unikraft Cloud's [REST API reference](/api/platform/v1), in particular the section on [service groups](/api/platform/v1/servicegroups). diff --git a/pages/platform/tagging.mdx b/pages/platform/tagging.mdx index 1c6fb9f8..215617d8 100644 --- a/pages/platform/tagging.mdx +++ b/pages/platform/tagging.mdx @@ -3,58 +3,358 @@ title: Tagging navigation_icon: tag --- -Tags are labels that you can attach to instances, volumes, instance templates, and volume templates to organize and filter your resources. +import { Tabs, TabsContent, TabsList, TabsTrigger } from "zudoku/ui/Tabs" -Don't confuse them with image tags which serve a different purpose (see [images](/api/platform/v1/images)). +Tags are labels that you can attach to instances, volumes, instance templates, and volume templates to organize and filter your resources. +Don't confuse them with [image](/platform/images) tags which serve a different purpose. ## Tag format Tags can be up to 256 characters long and may contain alphanumeric characters plus `-`, `+`, `_`, `.`, `:`, and `=`. -Each resource (instance, volume, instance template, volume template) can have up to 16 tags. -You can currently only manage tags via the API. -Kraftkit neither displays them nor allows you to edit them. +Each resource ([instance](/platform/instances), [volume](/platform/volumes), [instance template](/platform/instances#instance-templates), [volume template](/platform/volumes#volume-templates)) can have up to 16 tags. + +:::caution +You can currently only manage tags via the [API](/api/platform/v1). +::: ## Adding tags Add tags to a resource at creation time: -```json title="POST /instances" -{ - "image": "...", - "memory_mb": ..., - "tags": [ "production", "customer_A" ] -} + + + Instance + Instance Template + Volume + Volume Template + + + +```bash title="unikraft" +# not yet supported, use `unikraft api` instead +unikraft api /v1/instances -X POST --metro fra \ + -d '{ + "image": "...", + "memory_mb": ..., + "tags": ["production", "customer_A"] + }' +``` +```bash title="API" +curl -X POST \ + -H "Authorization: Bearer $UKC_TOKEN" \ + -H "Content-Type: application/json" \ + "https://api.fra.unikraft.cloud/v1/instances" \ + -d '{ + "image": "...", + "memory_mb": ..., + "tags": ["production", "customer_A"] + }' +``` + + + + +```bash title="unikraft" +# not yet supported, use `unikraft api` instead +unikraft api /v1/instances/templates -X POST --metro fra \ + -d '{ + "image": "...", + "memory_mb": ..., + "tags": ["production", "customer_A"] + }' +``` +```bash title="API" +curl -X POST \ + -H "Authorization: Bearer $UKC_TOKEN" \ + -H "Content-Type: application/json" \ + "https://api.fra.unikraft.cloud/v1/instances/templates" \ + -d '{ + "image": "...", + "memory_mb": ..., + "tags": ["production", "customer_A"] + }' +``` + + + + +```bash title="unikraft" +# not yet supported, use `unikraft api` instead +unikraft api /v1/volumes -X POST --metro fra \ + -d '{ + "size_mb": ..., + "tags": ["production", "customer_A"] + }' +``` +```bash title="API" +curl -X POST \ + -H "Authorization: Bearer $UKC_TOKEN" \ + -H "Content-Type: application/json" \ + "https://api.fra.unikraft.cloud/v1/volumes" \ + -d '{ + "size_mb": ..., + "tags": ["production", "customer_A"] + }' +``` + + + + +```bash title="unikraft" +# not yet supported, use `unikraft api` instead +unikraft api /v1/volumes/templates -X POST --metro fra \ + -d '{ + "size_mb": ..., + "tags": ["production", "customer_A"] + }' +``` +```bash title="API" +curl -X POST \ + -H "Authorization: Bearer $UKC_TOKEN" \ + -H "Content-Type: application/json" \ + "https://api.fra.unikraft.cloud/v1/volumes/templates" \ + -d '{ + "size_mb": ..., + "tags": ["production", "customer_A"] + }' ``` + + + Add tags to an existing resource (`"op": "set"` will replace all existing tags): -```json title="PATCH /instances" -{ - "name": "example-instance", - "prop": "tags", - "op": "set", - "value": ["testsystem", "customer_B"] -} + + + Instance + Instance Template + Volume + Volume Template + + + +```bash title="unikraft" +# not yet supported, use `unikraft api` instead +unikraft api /v1/instances -X PATCH --metro fra \ + -d '[{ + "name": "", + "prop": "tags", + "op": "set", + "value": ["testsystem", "customer_B"] + }]' ``` - -:::note -Use `/volumes` for volumes, `/instances/templates` for instance templates, and `/volumes/templates` for volume templates. -::: +```bash title="API" +curl -X PATCH \ + -H "Authorization: Bearer $UKC_TOKEN" \ + -H "Content-Type: application/json" \ + "https://api.fra.unikraft.cloud/v1/instances" \ + -d '[{ + "name": "", + "prop": "tags", + "op": "set", + "value": ["testsystem", "customer_B"] + }]' +``` + + + + +```bash title="unikraft" +# not yet supported, use `unikraft api` instead +unikraft api /v1/instances/templates -X PATCH --metro fra \ + -d '[{ + "name": "", + "prop": "tags", + "op": "set", + "value": ["testsystem", "customer_B"] + }]' +``` +```bash title="API" +curl -X PATCH \ + -H "Authorization: Bearer $UKC_TOKEN" \ + -H "Content-Type: application/json" \ + "https://api.fra.unikraft.cloud/v1/instances/templates" \ + -d '[{ + "name": "", + "prop": "tags", + "op": "set", + "value": ["testsystem", "customer_B"] + }]' +``` + + + + +```bash title="unikraft" +# not yet supported, use `unikraft api` instead +unikraft api /v1/volumes -X PATCH --metro fra \ + -d '[{ + "name": "", + "prop": "tags", + "op": "set", + "value": ["testsystem", "customer_B"] + }]' +``` +```bash title="API" +curl -X PATCH \ + -H "Authorization: Bearer $UKC_TOKEN" \ + -H "Content-Type: application/json" \ + "https://api.fra.unikraft.cloud/v1/volumes" \ + -d '[{ + "name": "", + "prop": "tags", + "op": "set", + "value": ["testsystem", "customer_B"] + }]' +``` + + + + +```bash title="unikraft" +# not yet supported, use `unikraft api` instead +unikraft api /v1/volumes/templates -X PATCH --metro fra \ + -d '[{ + "name": "", + "prop": "tags", + "op": "set", + "value": ["testsystem", "customer_B"] + }]' +``` +```bash title="API" +curl -X PATCH \ + -H "Authorization: Bearer $UKC_TOKEN" \ + -H "Content-Type: application/json" \ + "https://api.fra.unikraft.cloud/v1/volumes/templates" \ + -d '[{ + "name": "", + "prop": "tags", + "op": "set", + "value": ["testsystem", "customer_B"] + }]' +``` + + + Add tags to a resource without replacing existing ones (`"op": "add"`): -```json title="PATCH /instances" -{ - "name": "example-instance", - "prop": "tags", - "op": "add", - "value": ["legacy"] -} + + + Instance + Instance Template + Volume + Volume Template + + + +```bash title="unikraft" +# not yet supported, use `unikraft api` instead +unikraft api /v1/instances -X PATCH --metro fra \ + -d '[{ + "name": "", + "prop": "tags", + "op": "add", + "value": ["legacy"] + }]' +``` +```bash title="API" +curl -X PATCH \ + -H "Authorization: Bearer $UKC_TOKEN" \ + -H "Content-Type: application/json" \ + "https://api.fra.unikraft.cloud/v1/instances" \ + -d '[{ + "name": "", + "prop": "tags", + "op": "add", + "value": ["legacy"] + }]' + +``` + + + + +```bash title="unikraft" +# not yet supported, use `unikraft api` instead +unikraft api /v1/instances/templates -X PATCH --metro fra \ + -d '[{ + "name": "", + "prop": "tags", + "op": "add", + "value": ["legacy"] + }]' +``` +```bash title="API" +curl -X PATCH \ + -H "Authorization: Bearer $UKC_TOKEN" \ + -H "Content-Type: application/json" \ + "https://api.fra.unikraft.cloud/v1/instances/templates" \ + -d '[{ + "name": "", + "prop": "tags", + "op": "add", + "value": ["legacy"] + }]' +``` + + + + +```bash title="unikraft" +# not yet supported, use `unikraft api` instead +unikraft api /v1/volumes -X PATCH --metro fra \ + -d '[{ + "name": "", + "prop": "tags", + "op": "add", + "value": ["legacy"] + }]' +``` +```bash title="API" +curl -X PATCH \ + -H "Authorization: Bearer $UKC_TOKEN" \ + -H "Content-Type: application/json" \ + "https://api.fra.unikraft.cloud/v1/volumes" \ + -d '[{ + "name": "", + "prop": "tags", + "op": "add", + "value": ["legacy"] + }]' +``` + + + + +```bash title="unikraft" +# not yet supported, use `unikraft api` instead +unikraft api /v1/volumes/templates -X PATCH --metro fra \ + -d '[{ + "name": "", + "prop": "tags", + "op": "add", + "value": ["legacy"] + }]' ``` +```bash title="API" +curl -X PATCH \ + -H "Authorization: Bearer $UKC_TOKEN" \ + -H "Content-Type: application/json" \ + "https://api.fra.unikraft.cloud/v1/volumes/templates" \ + -d '[{ + "name": "", + "prop": "tags", + "op": "add", + "value": ["legacy"] + }]' +``` + + + :::note Tags are automatically deduplicated. -Repeating the same tag adds it only once. Adding a tag already attached to a resource has no effect. ::: @@ -62,30 +362,183 @@ Adding a tag already attached to a resource has no effect. Remove specific tags from a resource (`"op": "del"`): -```json title="PATCH /instances" -{ - "name": "example-instance", - "prop": "tags", - "op": "del", - "value": ["legacy"] -} + + + Instance + Instance Template + Volume + Volume Template + + + +```bash title="unikraft" +# not yet supported, use `unikraft api` instead +unikraft api /v1/instances -X PATCH --metro fra \ + -d '[{ + "name": "", + "prop": "tags", + "op": "del", + "value": ["legacy"] + }]' +``` +```bash title="API" +curl -X PATCH \ + -H "Authorization: Bearer $UKC_TOKEN" \ + -H "Content-Type: application/json" \ + "https://api.fra.unikraft.cloud/v1/instances" \ + -d '[{ + "name": "", + "prop": "tags", + "op": "del", + "value": ["legacy"] + }]' +``` + + + + +```bash title="unikraft" +# not yet supported, use `unikraft api` instead +unikraft api /v1/instances/templates -X PATCH --metro fra \ + -d '[{ + "name": "", + "prop": "tags", + "op": "del", + "value": ["legacy"] + }]' +``` +```bash title="API" +curl -X PATCH \ + -H "Authorization: Bearer $UKC_TOKEN" \ + -H "Content-Type: application/json" \ + "https://api.fra.unikraft.cloud/v1/instances/templates" \ + -d '[{ + "name": "", + "prop": "tags", + "op": "del", + "value": ["legacy"] + }]' +``` + + + + +```bash title="unikraft" +# not yet supported, use `unikraft api` instead +unikraft api /v1/volumes -X PATCH --metro fra \ + -d '[{ + "name": "", + "prop": "tags", + "op": "del", + "value": ["legacy"] + }]' ``` +```bash title="API" +curl -X PATCH \ + -H "Authorization: Bearer $UKC_TOKEN" \ + -H "Content-Type: application/json" \ + "https://api.fra.unikraft.cloud/v1/volumes" \ + -d '[{ + "name": "", + "prop": "tags", + "op": "del", + "value": ["legacy"] + }]' +``` + + + + +```bash title="unikraft" +# not yet supported, use `unikraft api` instead +unikraft api /v1/volumes/templates -X PATCH --metro fra \ + -d '[{ + "name": "", + "prop": "tags", + "op": "del", + "value": ["legacy"] + }]' +``` +```bash title="API" +curl -X PATCH \ + -H "Authorization: Bearer $UKC_TOKEN" \ + -H "Content-Type: application/json" \ + "https://api.fra.unikraft.cloud/v1/volumes/templates" \ + -d '[{ + "name": "", + "prop": "tags", + "op": "del", + "value": ["legacy"] + }]' +``` + + + ## Filtering by tag -Filter the instance list, volume list, instance template list, or volume template list by one or more tags. +Filter the [instance list](/api/platform/v1/instances#list-instances), [volume list](/api/platform/v1/volumes#list-volumes), [instance template list](/api/platform/v1/instances#list-template-instances), or [volume template list](/api/platform/v1/volumes#list-template-volumes) by one or more tags. Only resources that have _all_ the specified tags appear in the results. + + + Instance + Instance Template + Volume + Volume Template + + + +```bash title="unikraft" +# not yet supported, use `unikraft api` instead +unikraft api "/v1/instances?tags=production,customer_A" -X GET --metro fra +``` +```bash title="API" +curl -H "Authorization: Bearer $UKC_TOKEN" \ + "https://api.fra.unikraft.cloud/v1/instances?tags=production,customer_A" +``` + + + + +```bash title="unikraft" +# not yet supported, use `unikraft api` instead +unikraft api "/v1/instances/templates?tags=production,customer_A" -X GET --metro fra +``` +```bash title="API" +curl -H "Authorization: Bearer $UKC_TOKEN" \ + "https://api.fra.unikraft.cloud/v1/instances/templates?tags=production,customer_A" ``` -GET /instances?tags=production,customer_A + + + + +```bash title="unikraft" +# not yet supported, use `unikraft api` instead +unikraft api "/v1/volumes?tags=production,customer_A" -X GET --metro fra ``` +```bash title="API" +curl -H "Authorization: Bearer $UKC_TOKEN" \ + "https://api.fra.unikraft.cloud/v1/volumes?tags=production,customer_A" +``` + + + + +```bash title="unikraft" +# not yet supported, use `unikraft api` instead +unikraft api "/v1/volumes/templates?tags=production,customer_A" -X GET --metro fra +``` +```bash title="API" +curl -H "Authorization: Bearer $UKC_TOKEN" \ + "https://api.fra.unikraft.cloud/v1/volumes/templates?tags=production,customer_A" +``` + + + Separate tags by commas (`,`). -## Tags in API responses - -Each instance, volume, instance template, or volume template status response includes a `tags` array if at least one tag is present. - ## Learn more -* Unikraft Cloud's [REST API reference](/api/platform/v1), in particular the sections on [instances](/api/platform/v1/instances) and [volumes](/api/platform/v1/volumes) +* Unikraft Cloud's [REST API reference](/api/platform/v1). diff --git a/pages/platform/troubleshooting.mdx b/pages/platform/troubleshooting.mdx index efe8c173..97b51f75 100644 --- a/pages/platform/troubleshooting.mdx +++ b/pages/platform/troubleshooting.mdx @@ -5,194 +5,222 @@ navigation_icon: circle-check This guide explains how to debug apps on Unikraft Cloud so you can fix issues or collect enough information for the support team to help you. It follows platform best practices and supports efficient troubleshooting. -If you need help, reach out to Unikraft Cloud Support. +If you need help, reach out to Unikraft Cloud Support on [Discord](https://kraft.cloud/discord) or write to [support@unikraft.cloud](mailto:support@unikraft.cloud). +## Debugging running apps -## Debugging a problematic app - -An app may crash, freeze, or misbehave. -To inspect it, try either: +The most direct way to debug an app is to use the app console output, which may include kernel output. +To see it, after starting the Unikraft Cloud instance, use: ```bash title="unikraft" unikraft instances logs -# or -unikraft instances get ``` ```bash title="kraft" kraft cloud inst logs -# or -kraft cloud inst get ``` -This may, in certain times, provide insufficient information. - +In case of a crash, you'll see a full crash output: -## Large filesystem build process gets stuck +```ansi title="" +Powered by Unikraft Telesto (0.16.2~9c264902) +[ 0.066949] CRIT: [libukvmem] Cannot handle write page fault at 0x1000bb8024 (ec: 0x2): -12 +[ 0.067680] CRIT: [libkvmplat] page fault handler returned error: -12 +[ 0.068227] CRIT: [libkvmplat] Unhandled page fault vaddr=0x1000bb8024, error code=0x2 +[ 0.068910] CRIT: [appelfloader] Unikraft crash - Telesto (0.16.2~9c264902) +[ 0.069503] CRIT: [appelfloader] Thread "python3"@0x4017e4020 +[ 0.069993] CRIT: [appelfloader] RIP: 0008:000000100041fdcb +[ 0.070468] CRIT: [appelfloader] RSP: 0010:000000100017f760 EFLAGS: 00010206 ORIG_RAX: 0000000000000002 +[ 0.071269] CRIT: [appelfloader] RAX: 0000001000bb8000 RBX: 00000004018012f0 RCX:0000000000000007 +[ 0.072040] CRIT: [appelfloader] RDX: 0000000000000000 RSI: aaaaaaaaaaaaaaab RDI:00000010007bccc0 +[ 0.072793] CRIT: [appelfloader] RBP: 000000000000001a R08: 0000000000000001 R09:00000010004e2b99 +[ 0.073558] CRIT: [appelfloader] R10: bb9e744ce6503a27 R11: 0000000401861290 R12:00000010007bccc0 +[ 0.074312] CRIT: [appelfloader] R13: 00000010007bbd48 R14: 000000100017f890 R15:0000000000000038 +[ 0.075076] CRIT: [appelfloader] Stack: +[ 0.075406] CRIT: [appelfloader] 100017f760 00 00 00 00 00 00 00 00 |........| +[ 0.076043] CRIT: [appelfloader] 100017f768 a1 01 00 00 00 00 00 00 |........| +[ 0.076680] CRIT: [appelfloader] 100017f770 48 bd 7b 00 10 00 00 00 |H.{.....| +[ 0.077324] CRIT: [appelfloader] 100017f778 e0 92 7a 00 10 00 00 00 |..z.....| +[ 0.077959] CRIT: [appelfloader] 100017f780 48 bd 7b 00 10 00 00 00 |H.{.....| +[ 0.078596] CRIT: [appelfloader] 100017f788 90 f8 17 00 10 00 00 00 |........| +[ 0.079233] CRIT: [appelfloader] 100017f790 38 00 00 00 00 00 00 00 |8.......| +[ 0.079870] CRIT: [appelfloader] 100017f798 72 fc 41 00 10 00 00 00 |r.A.....| +[ 0.080504] CRIT: [appelfloader] Call Trace: +[ 0.080871] CRIT: [appelfloader] [0x000000100041fdcb] +[ 0.081311] CRIT: [appelfloader] Bad frame pointer +``` -When the filesystem is larger than about `800 MB`, the build may get stuck. -A limitation in BuildKit, the current filesystem build component, causes the issue. +or even -Work around this by reducing the filesystem image below `800 MB`. -Work is in progress to integrate a component without this limitation. +```ansi title="" +[ 0.850992] Initramfs unpacking failed: write error +``` +Inspect the instance details to see the stop reason: -## Gateway error when accessing a service - -When you query a Unikraft Cloud service via its public address, such as: + -```bash title="" -curl https://frosty-bobo-zeev783o.fra.unikraft.app +```bash title="unikraft" +unikraft instances get ``` -you may get a `Bad Gateway` response: - -```html title="" - -502 Bad Gateway - -

502 Bad Gateway

-
openresty
- - +```bash title="kraft" +kraft cloud instance get ``` -This happens when you specify the wrong internal app port (for example, the app exposes port 8080 but you use `443:80` instead of `443:8080`). +
-Another error response you may get is: +This yields output like: -```html title="" - - - - - - - Service not found - - -There is no service on this URL. - - + + +```ansi title="unikraft" +metro: fra +name: node-playwright-chromium-nwa95 +uuid: 59cd6c94-611c-4ca8-911f-78c7db95ad12 +state: stopped +image: /node-playwright-chromium +resources: + memory: 2.441GiB + vcpus: 1 +service: + name: divine-dust-73sxgnb6 + uuid: 3c7ce2b4-ae86-4a21-8562-55033d5409d5 + domains: + - fqdn: divine-dust-73sxgnb6.fra0-fe-test.unikraft.app +networks: +- uuid: 62982265-6521-401c-878a-d7e31804c63c + private-ip: 10.0.0.109 + mac: 12:b0:0a:00:00:6d +timestamps: + created: 2 days ago +scale-to-zero: + enabled: true + policy: on + cooldown-time: 1s +stop: + reason: kernel crash: out of memory (ENOMEM) ``` -This happens when you connect with an HTTPS client (port `443`) and the app doesn't expose that port. -Database services such as [MongoDB](/guides/mongodb) or [MariaDB](/guides/mariadb) use different ports (for example, `27017`, `3306`). +```ansi title="kraft" + uuid: 59cd6c94-611c-4ca8-911f-78c7db95ad12 + name: node-playwright-chromium-nwa95 + fqdn: divine-dust-73sxgnb6.fra0-fe-test.unikraft.app + private fqdn: node-playwright-chromium-nwa95.internal + private ip: 10.0.0.109 + state: stopped + created: 2026-06-02T17:24:31Z + started: 2026-06-02T17:24:31Z + stopped: 2026-06-02T17:24:37Z + start count: 1 + restart count: 0 + restart attempts: + next restart: + restart policy: never + stop origin: initiated by kernel (----k) + stop reason: Out of memory. Try increasing instance's memory (see -M flag). (i0 PGFAULT ENOMEM) + app exit code: + image: oci://unikraft.io//node-playwright-chromium@sha256:e596b51af391792eacb772e52c701996a5c4dbfc3826ea9fd6ecf4b8a29441fb + memory: 2.4 GiB + vcpus: 1 + args: + env: + volumes: + service: divine-dust-73sxgnb6 + scale to zero: on + scale to zero cooldown: 1000 + scale to zero stateful: false + boot time: 0.00 ms + up time: 6.127s +``` -Use the correct exposed port. -You may need a TLS tunnel (see below). + +This output shows the [stop reason](/platform/instances#stop-reason). +Here the cause is insufficient memory. -## Connect to a non-TLS app +If the stop reason lacks detail, enable debug tracing for the instance. -Unikraft Cloud uses TLS to expose services to the outside world. -Some apps (such as [MongoDB](/guides/mongodb) or [MariaDB](/guides/mariadb)) don't use TLS. -Create a TLS tunnel via the legacy CLI, which opens a local endpoint (`localhost` / `127.0.0.1`) and forwards traffic over TLS. -See the [MariaDB guide](/guides/mariadb) for an example. +To do that, pick an app from the [examples repository](https://github.com/unikraft-cloud/examples/) or an app directory you created. +Update the `runtime` entry in the `Kraftfile` to reference the debug build by adding `-dbg` to the runtime name. +For example, if you want to run the [httpserver-go1.21 example](https://github.com/unikraft-cloud/examples/tree/main/httpserver-go1.21) with debug output, update its `Kraftfile` as follows: +```yaml title="Kraftfile" +spec: v0.7 -## "No such file or directory" when building or deploying an image +runtime: base-compat:latest-dbg -When building / deploying an image, you may get the error below: +rootfs: + source: ./Dockerfile + format: erofs -```ansi title="" - E unpacking the image: opening layer: open /.local/share/kraftkit/runtime/oci/digests/sha256/3d30a7ba2a819aec2e73c5df07c24264d66891e926b395b9ef0f66f151db4b49: no such file or directory +cmd: ["/server"] ``` -This often means the local `kraft` cache is in an inconsistent state. +That is, change `base-compat:latest` to `base-compat:latest-dbg`. -To solve this, remove the local legacy CLI cache and local packages: +Now you're ready to re-deploy: - + + +```bash title="unikraft" +unikraft build . --output /http-go-strace:latest +unikraft run --metro fra \ + --name http-go-strace \ + -p 443:8080/http+tls \ + --image /http-go-strace:latest +``` ```bash title="kraft" -kraft pkg rm --all -rm -fr ~/.local/share/kraftkit +kraft cloud deploy \ + --name http-go-strace \ + -p 443:8080 . ``` -## Launched app not visible in list - -The most common reason is that you deployed an app to one metro but listed a different one. -Use the `--metro` flag per command, or use the legacy CLI environment variable for a session: +You can now inspect the logs as before and view system call tracing: ```bash title="unikraft" -unikraft instances list --metro +unikraft instances logs http-go-strace ``` ```bash title="kraft" -export UKC_METRO= -kraft cloud instance list --metro +kraft cloud inst logs http-go-strace ``` -> **Note:** The `UKC_METRO` environment variable is only supported by the legacy CLI. - -## How can you cache the app's filesystem for faster builds - -When using a `Dockerfile` for the app filesystem, Unikraft Cloud passes the commands to [BuildKit](https://docs.docker.com/build/buildkit/). - -By default, each legacy deploy command starts an ephemeral BuildKit container. -The platform then removes the filesystem app data, so each deploy starts from zero. -To prevent this, follow the instructions [here](https://unikraft.org/guides/building-dockerfile-images-with-buildkit): +This mechanism works for all apps and runtimes. +Contact Unikraft Cloud on the [Discord server](/discord) and include this output if you need more detail. -```bash title="" -docker run -d --name buildkitd --privileged moby/buildkit:latest -export KRAFTKIT_BUILDKIT_HOST=docker-container://buildkitd -``` +:::tip +While you debug an issue you can mitigate crashes by setting a restart policy. +For example, use the CLI to set `--restart on-failure` so the platform restarts the app if it crashes. -The approach above caches builds in the BuildKit container filesystem. -Another approach saves the cache in a local host directory: + -```bash title="" -docker run -d --name buildkitd --privileged -v $HOME/.buildkit-cache:/var/lib/buildkit moby/buildkit:latest -export KRAFTKIT_BUILDKIT_HOST=docker-container://buildkitd +```bash title="unikraft" +unikraft run --metro fra --restart on-failure --image my-app:latest ``` -Here `$HOME/.buildkit-cache` is a local path on your machine where BuildKit stores cache data. - -## What's a `Kraftfile` - -A `Kraftfile` is used by the CLI toolchain to understand how to build and deploy your instance. -Typically you can use the default `Kraftfile` found in each Unikraft Cloud example. -Below is a sample `Kraftfile` with a brief explanation: - -```yaml title="Kraftfile" -spec: v0.6 - -runtime: python:3.12 - -rootfs: ./Dockerfile - -cmd: ["/usr/bin/python3", "/src/server.py"] - -labels: - cloud.unikraft.v1.instances/scale_to_zero.policy: "on" - cloud.unikraft.v1.instances/scale_to_zero.stateful: "true" - cloud.unikraft.v1.instances/scale_to_zero.cooldown_time_ms: 1000 +```bash title="kraft" +kraft cloud deploy --restart on-failure . ``` -The `runtime` specifies one of the Unikraft Cloud runtimes (microVMs) built to run different languages and apps. -Here it specifies a Python runtime. - -The `rootfs` parameter tells the CLI to use a `Dockerfile` in the same directory to build the root filesystem, and the `cmd` parameter which command to run when deployed (although you can also specify this in the `Dockerfile`). - -Finally the `labels` specify runtime options. -In the example above, the configuration enables stateful scale-to-zero and the platform waits 1 second with no requests before putting the instance to sleep. + +::: -## Debugging the build, packaging and pushing steps +## Debugging the build and push steps -The legacy deploy command performs steps on your local device before actually deploying your app to Unikraft Cloud: +The CLI performs steps on your local device before actually deploying your app to Unikraft Cloud: 1. Downloads the runtime image (defined in the `Kraftfile`) from the Unikraft Cloud registry. 1. Builds the app filesystem using the `Dockerfile` via [BuildKit](https://unikraft.org/guides/building-dockerfile-images-with-buildkit). @@ -202,224 +230,156 @@ The legacy deploy command performs steps on your local device before actually de If any of these steps fail, enable legacy CLI debugging with the `--log-level` and `--log-type` flags: - ```bash title="kraft" - kraft cloud deploy --log-level debug --log-type basic [...] - ``` - -You should then see debug output for the 4 steps above. +```bash title="unikraft" +unikraft build --log-level debug --log-type text ... +``` -```ansi title="" - D using metro=fra - D using token=*** - D cannot run because: no image specified deployer=image-name - D using deployer=kraftfile-runtime - D querying catalog local=true name=index.unikraft.io/razvan.unikraft.io/http-go-strace2:latest remote=false - D found 0/66 matching packages in oci catalog - D using packager=kraftfile-runtime - D querying catalog arch=x86_64 local=true name=index.unikraft.io/official/base plat=kraftcloud remote=false version=latest - D found 0/66 matching packages in oci catalog - D querying catalog arch=x86_64 local=true name=index.unikraft.io/official/base plat=kraftcloud remote=true version=latest - i found index.unikraft.io/official/base:latest-dbg (kraftcloud/x86_64) (6bd4542, 11c196e, 2.0 MB) - D found 1/67 matching packages in oci catalog - D saving manifest digest=sha256:6bd45428b8ecf667954cb8f829ee3f8f6f6f23fa0a0e7dc1aa27fcae39826f89 ref=index.unikraft.io/official/base:latest - i building rootfs - D using buildkit addr=docker-container://buildkitd version=v0.12.3 +```bash title="kraft" +kraft cloud deploy --log-level debug --log-type basic ... ``` + + +You should then see debug output for the 4 steps above. This output should give enough context to diagnose the issue. -If not, report it with the output on the [Discord server](/discord). +## Caching the app's filesystem for faster builds -## Debugging running apps +When using a `Dockerfile` for the app filesystem, Unikraft Cloud passes the commands to [BuildKit](https://docs.docker.com/build/buildkit/). -The most direct way to debug an app is to use the app console output, which may include kernel output. -To see it, after starting the Unikraft Cloud instance, use: +By default, each legacy `kraft cloud deploy` command starts an ephemeral BuildKit container. +The platform then removes the filesystem app data, so each deploy starts from zero. - +To prevent this, follow the instructions [here](https://unikraft.org/guides/building-dockerfile-images-with-buildkit): -```bash title="unikraft" -unikraft instances logs +```bash +docker run -d --name buildkitd --privileged moby/buildkit:latest +export KRAFTKIT_BUILDKIT_HOST=docker-container://buildkitd ``` -```bash title="kraft" -kraft cloud inst logs +The approach above caches builds in the BuildKit container filesystem. +Another approach saves the cache in a local host directory: + +```bash title="" +docker run -d --name buildkitd --privileged -v $HOME/.buildkit-cache:/var/lib/buildkit moby/buildkit:latest +export KRAFTKIT_BUILDKIT_HOST=docker-container://buildkitd ``` - +Here `$HOME/.buildkit-cache` is a local path on your machine where BuildKit stores cache data. -In case of a crash, you'll see a full crash output: +## Large filesystem build process gets stuck -{/* vale off */} -```text title="" -Powered by Unikraft Telesto (0.16.2~9c264902) -[ 0.066949] CRIT: [libukvmem] Cannot handle write page fault at 0x1000bb8024 (ec: 0x2): -12 -[ 0.067680] CRIT: [libkvmplat] page fault handler returned error: -12 -[ 0.068227] CRIT: [libkvmplat] Unhandled page fault vaddr=0x1000bb8024, error code=0x2 -[ 0.068910] CRIT: [appelfloader] Unikraft crash - Telesto (0.16.2~9c264902) -[ 0.069503] CRIT: [appelfloader] Thread "python3"@0x4017e4020 -[ 0.069993] CRIT: [appelfloader] RIP: 0008:000000100041fdcb -[ 0.070468] CRIT: [appelfloader] RSP: 0010:000000100017f760 EFLAGS: 00010206 ORIG_RAX: 0000000000000002 -[ 0.071269] CRIT: [appelfloader] RAX: 0000001000bb8000 RBX: 00000004018012f0 RCX:0000000000000007 -[ 0.072040] CRIT: [appelfloader] RDX: 0000000000000000 RSI: aaaaaaaaaaaaaaab RDI:00000010007bccc0 -[ 0.072793] CRIT: [appelfloader] RBP: 000000000000001a R08: 0000000000000001 R09:00000010004e2b99 -[ 0.073558] CRIT: [appelfloader] R10: bb9e744ce6503a27 R11: 0000000401861290 R12:00000010007bccc0 -[ 0.074312] CRIT: [appelfloader] R13: 00000010007bbd48 R14: 000000100017f890 R15:0000000000000038 -[ 0.075076] CRIT: [appelfloader] Stack: -[ 0.075406] CRIT: [appelfloader] 100017f760 00 00 00 00 00 00 00 00 |........| -[ 0.076043] CRIT: [appelfloader] 100017f768 a1 01 00 00 00 00 00 00 |........| -[ 0.076680] CRIT: [appelfloader] 100017f770 48 bd 7b 00 10 00 00 00 |H.{.....| -[ 0.077324] CRIT: [appelfloader] 100017f778 e0 92 7a 00 10 00 00 00 |..z.....| -[ 0.077959] CRIT: [appelfloader] 100017f780 48 bd 7b 00 10 00 00 00 |H.{.....| -[ 0.078596] CRIT: [appelfloader] 100017f788 90 f8 17 00 10 00 00 00 |........| -[ 0.079233] CRIT: [appelfloader] 100017f790 38 00 00 00 00 00 00 00 |8.......| -[ 0.079870] CRIT: [appelfloader] 100017f798 72 fc 41 00 10 00 00 00 |r.A.....| -[ 0.080504] CRIT: [appelfloader] Call Trace: -[ 0.080871] CRIT: [appelfloader] [0x000000100041fdcb] -[ 0.081311] CRIT: [appelfloader] Bad frame pointer +When the filesystem is larger than about `800 MB`, the build may get stuck. +A limitation in [BuildKit](https://docs.docker.com/build/buildkit/), the current filesystem build component, causes the issue. -It looks like the instance exited fatally. -Run this for more details: +Work around this by reducing the filesystem image below `800 MB`. +Work is in progress to integrate a component without this limitation. - unikraft instances get http-python312-hb7ij -``` -{/* vale on */} -Use the recommended command for detailed output. -This yields output like: +## Gateway error when accessing a service -```ansi title="" - uuid: 988c3054-09c0-48ef-ac02-877f8352d93f - name: http-python312-hb7ij - fqdn: - private fqdn: http-python312-hb7ij.internal - private ip: 172.16.3.1 - state: stopped - created: 2024-04-18T18:24:12Z - started: 2024-04-18T18:24:12Z - stopped: 2024-04-18T18:24:13Z - start count: 1 - restart count: 0 - restart attempts: - next restart: - restart policy: never - stop origin: initiated by kernel (----k) - stop reason: Out of memory. Try increasing instance's memory (see -M flag). (i127 PGFAULT ENOMEM) - app exit code: - image: http-python312@sha256:8b92ed612f8450de94355a0a5b8917a710fd6c902c4a7be80ffb3989b5a99023 - memory: 128 MiB - args: /usr/bin/python3 /src/server.py - env: - volumes: - service: - boot time: 96.03 ms - up time: 166ms +When you query a Unikraft Cloud service via its public address, such as: + +```bash +curl https://frosty-bobo-zeev783o.fra.unikraft.app ``` -This output shows the stop reason. -Here the cause is insufficient memory. +you may get a `Bad Gateway` response: -If the stop reason lacks detail, enable debug tracing for the instance. +```html title="" + +502 Bad Gateway + +

502 Bad Gateway

+
openresty
+ + +``` -To do that, pick an app from the [examples repository](https://github.com/unikraft-cloud/examples/) or an app directory you created. -Update the `runtime` entry in the `Kraftfile` to reference the debug build by adding `-dbg` to the runtime name. -For example, if you want to run the [http-go1.21 example](https://github.com/unikraft-cloud/examples/tree/main/http-go1.21) with debug output, update its `Kraftfile` as follows: +This happens when you specify the wrong internal app port (for example, the app exposes port 8080 but you use `443:80` instead of `443:8080`). -```yaml title="Kraftfile" -spec: v0.6 +Another error response you may get is: -runtime: base:latest-dbg +```html title="" + + + + + + + Service not found + + +There is no service on this URL. + + +``` -rootfs: ./Dockerfile +This happens when you connect with an HTTPS client (port `443`) and the app doesn't expose that port. +Database services such as [MongoDB](/guides/mongodb) or [MariaDB](/guides/mariadb) use different ports (for example, `27017`, `3306`). -cmd: ["/server"] -``` +Use the correct exposed port. +You may need a TLS tunnel (see below). -That is, change `base:latest` to `base:latest-dbg`. -Now Unikraft Cloud is ready to re-deploy: +If you still have issues, it might mean that the app fails to start or crashes immediately. +In that case, inspect the [app logs](/platform/troubleshooting#debugging-running-apps) first. - -```bash title="unikraft" -unikraft build . --output /http-go-strace:latest -unikraft run --metro=fra --name http-go-strace -p 443:8080/http+tls --image=/http-go-strace:latest -``` +## Connecting to a non-TLS app -```bash title="kraft" -kraft cloud deploy --name http-go-strace -p 443:8080 . -``` +Unikraft Cloud uses TLS to expose services to the outside world. +Some apps (such as [MongoDB](/guides/mongodb) or [MariaDB](/guides/mariadb)) don't use TLS. - +Create a TLS tunnel via the [legacy CLI](/cli/kraft/tunnel) or `socat`, which opens a local endpoint (`localhost` / `127.0.0.1`) and forwards traffic over TLS. +See the [MariaDB guide](/guides/mariadb) for an example. -You can now inspect the logs as before and view system call tracing: - +## "No such file or directory" when building or deploying an image -```bash title="unikraft" -unikraft instances logs http-go-strace -``` +When building / deploying an image using `kraft`, you may get the error below: -```bash title="kraft" -kraft cloud inst logs http-go-strace +```ansi title="" + E unpacking the image: opening layer: open /.local/share/kraftkit/runtime/oci/digests/sha256/3d30a7ba2a819aec2e73c5df07c24264d66891e926b395b9ef0f66f151db4b49: no such file or directory ``` - +This often means the local `kraft` cache is in an inconsistent state. -```ansi title="" -[ 0.000000] Info: [libkvmplat] Unikraft Telesto (0.16.2~5b96d531) -[ 0.000000] Info: [libkvmplat] Architecture: x86_64 -[ 0.000000] Info: [libkvmplat] Boot loader : unknown-lxboot -[ 0.000000] Info: [libkvmplat] Command line: unikraft netdev.ip="172.16.3.4/24:172.16.3.254:172.16.3.254::http-go-strace:internal" vfs.fstab="initrd0:/:extract::ramfs=2:" virtio_mmio.device=4K@0xd0001000:5 -- /server -[...] -epoll_ctl(0x4, 0x1, ...) = 0x0 -epoll_ctl(0x4, 0x1, ...) = 0x0 -getsockname(fd:3, sockaddr:{sa_family=AF_0xffffffff901f0002, ...}, 10385019274329587824) = OK -accept4(0x3, 0xc000045ad0, ...) = Resource temporarily unavailable (-11) -nanosleep(0x100052f120, 0x0, ...) = 0x0 -clock_gettime(CLOCK_MONOTONIC, timespec:{tv_sec=0, tv_nsec=187025020}) = OK -clock_gettime(CLOCK_MONOTONIC, timespec:{tv_sec=0, tv_nsec=187640540}) = OK -set_robust_list(0x10005839a0, 0x18, ...) = Function not implemented (-38) -rt_sigprocmask(0x2, 0x1000583fb0, ...) = 0x0 -mmap(va:0x1050000000, 67108864, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS|0x4000, fd:-1, 0) = va:0x1050000000 -mprotect(va:0x1050000000, 135168, PROT_READ|PROT_WRITE) = OK -sigaltstack(0x0, 0x1000583158, ...) = 0x0 -rt_sigprocmask(0x2, 0x1000583168, ...) = 0x0 -gettid() = pid:6 -epoll_pwait(0x4, 0x10001803c8, ...) = 0x0 -clock_gettime(CLOCK_MONOTONIC, timespec:{tv_sec=0, tv_nsec=191570733}) = OK -epoll_pwait(0x4, 0x100052eb28, ...) = 0x0 -nanosleep(0x100052f120, 0x0, ...) = 0x0 -clock_gettime(CLOCK_MONOTONIC, timespec:{tv_sec=0, tv_nsec=192861008}) = OK -clock_gettime(CLOCK_MONOTONIC, timespec:{tv_sec=0, tv_nsec=193478343}) = OK +To solve this, remove the local cache and local packages: + + + +```bash title="kraft" +kraft pkg rm --all +rm -fr ~/.local/share/kraftkit ``` -This mechanism works for all apps and runtimes. -Contact Unikraft Cloud on the [Discord server](/discord) and include this output if you need more detail. + -:::tip +## Launched app not visible in list -While you debug an issue you can mitigate crashes by setting a restart policy. -For example, use the CLI to set `--restart on-failure` so the platform restarts the app if it crashes. +The most common reason is that you deployed an app to one metro but listed a different one. +Use the `--metro` flag per command, or use the legacy CLI environment variable for a session: ```bash title="unikraft" -unikraft run --metro=fra --restart=on-failure --image=my-app:latest +unikraft instances list --metro ``` ```bash title="kraft" -kraft cloud deploy --restart on-failure . +export UKC_METRO= +kraft cloud instance list ``` -Find more info in the [CLI reference](/docs/cli/unikraft) and the [legacy CLI reference](/docs/cli/kraft/overview). - -::: +The `UKC_METRO` environment variable is only supported by the legacy CLI. ## Learn more * The [CLI reference](/docs/cli/unikraft) and the [legacy CLI reference](/docs/cli/kraft/overview). * Unikraft Cloud's [REST API reference](/api/platform/v1). -* Many more guides [here](/guides/bun). +* Many more guides [here](/guides/overview). diff --git a/pages/platform/volumes.mdx b/pages/platform/volumes.mdx index 80bfe624..aed2191e 100644 --- a/pages/platform/volumes.mdx +++ b/pages/platform/volumes.mdx @@ -5,41 +5,58 @@ navigation_icon: cylinder A volume is a persistent storage device that keeps data across restarts and even redeployments. -This documents presents a guide where you will create a volume, attach it to a web server instance, and write to it. -Then you will detach it, remove that instance, attach it to a new instance, and confirm the data persisted. +## Typical workflow -Then, it presents some features of volumes. +### Creating a persistent volume -## Volume workflow - -:::note -You can create an instance with attached volumes in the same call - see the `volumes` field for the [`POST /instances`](/api/platform/v1/instances#create-instance) endpoint. -In this case, the volume's lifetime is tied to the instance - when you delete the instance, the volume disappears. +:::tip +You can create an instance with attached volumes in a single API operation. +This ties the volumes' lifetimes to the instance - when you delete the instance, the volumes disappear. +This use case is still valid, because an instance with no volumes attached will write all its files into RAM and eventually exhaust its memory. ::: -### Setting up the volume - -To start, create the volume with the CLI, with size in MBs and name `my-volume`: +To start, create the volume with the CLI, with size 100 MiB and name `my-volume`: ```bash title="unikraft" -unikraft volumes create \ - --set name=my-volume \ - --set size=100 \ - --set metro=fra +unikraft volumes create --metro fra \ + --name my-volume \ + --size 100M ``` ```bash title="kraft" kraft cloud volume create \ - --size 100 \ + --size 100Mi \ --name my-volume ``` -The command should return the volume's UUID, and you can check the operation -worked via: + + +```ansi title="unikraft" +metro: fra +name: my-volume +uuid: 4d5576f7-315a-4eab-98b8-6c18fec076e0 +state: available +size: 100MiB +filesystem: ext4 +quota-policy: static +persistent: true +access-mode: rwo +timestamps: + created: just now +``` + +```ansi title="kraft" +NAME CREATED AT SIZE ATTACHED TO MOUNTED BY STATE PERSISTENT +my-volume 1 second ago 100 MiB available true +``` + + + +You can list the volume with: @@ -53,52 +70,77 @@ kraft cloud volume list -which should output something like: + -```ansi title="" -NAME CREATED AT SIZE ATTACHED TO STATE PERSISTENT -my-volume 15 seconds ago 100 MiB available true +```bash title="unikraft" +METRO NAME STATE SIZE CREATED +fra my-volume available 100MiB just now +``` + +```bash title="kraft" +NAME CREATED AT SIZE ATTACHED TO MOUNTED BY STATE PERSISTENT +my-volume 1 minute ago 100 MiB available true ``` -The `ATTACHED TO` field is empty because the platform hasn't attached it to any instance yet. +The volume is in `available` state because it isn't attached to any instance yet. +It's also marked as `persistent` because it will persist even after instance deletion. -### Populating the volume with local data (optional) + -To populate an empty volume with local data, use the legacy CLI volume import command. -For example, assuming the volume's name is `my-volume` and that the data you want to import are in your `my-data` directory, you would run: +### Importing local data - +To populate an empty volume with local data, use the CLI `import` subcommand. +For example, assuming the data you want to import is in your `my-data` directory, you would run: + + + +```bash title="unikraft" +unikraft volume import \ + my-volume \ + --source ./data +``` ```bash title="kraft" -kraft cloud volume import --volume my-volume --source my-data +kraft cloud volume import \ + --volume my-volume \ + --source my-data ``` You should see output like: -```ansi title="" -[+] Packaging source as a CPIO archive... done! [0.0s] -[+] Spawning temporary volume data import instance... done! [0.1s] -[+] Importing data (256 B) β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’ 100% [0.1s] + + +```ansi title="unikraft" +β”‚ importing data into volume size=16.47KiB volume=my-volume +β”‚ import complete free=89.52MiB total=89.69MiB volume=my-volume +``` -[●] Import complete - β”‚ - β”œ─── volume: my-volume - β”œ─ imported: 256 B - β””─ capacity: 100 MiB +```ansi title="kraft" + i Packaging source as a CPIO archive + i Spawning temporary volume data import instance +[+] Importing data (16 KiB) β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’ 100% [0.4s] + +[●] Import complete + β”‚ + β”œβ”€ volume: my-volume + β”œβ”€β”€β”€ free: 90 MiB + └── total: 90 MiB ``` -## Setting up the web server + + +### Creating an instance -Use a Flask web server to write to the volume: +Start from [this guide](/guides/httpserver-python3.12-flask3.0) to create a simple Flask web server: -```bash title="" +```bash git clone https://github.com/unikraft-cloud/examples -cd examples/http-python3.12-flask3.0 +cd examples/httpserver-python3.12-flask3.0 ``` -Replace the contents of `server.py`with: +Replace the contents of `server.py` with: ```py title="server.py" from flask import Flask, send_from_directory @@ -111,8 +153,8 @@ file_path = "/mnt/log.txt" def initialize_file(): if not os.path.exists(file_path): - with open(file_path, 'w') as log_file: - log_file.write("Log file created.\n") + with open(file_path, 'w') as log_file: + log_file.write("Log file created.\n") @app.route('/') def write_to_file(): @@ -122,11 +164,11 @@ def write_to_file(): # Write to file with open(file_path, 'a') as log_file: - log_file.write(f"{current_datetime}\n") + log_file.write(f"{current_datetime}\n") # Read contents of the file with open(file_path, 'r') as log_file: - file_contents = log_file.read() + file_contents = log_file.read() return file_contents @@ -134,43 +176,78 @@ if __name__ == '__main__': app.run(host='0.0.0.0', port=8080) ``` -On every request, this simple server will write a timestamp to a file on the -mounted persistent volume and print out the current contents of the file. +On every request, this simple server will write a timestamp to a file on the mounted persistent volume and print out the current contents of the file. -Start the Flask web server, create a -[service](/platform/services) for it via the publish flag, and mount the `my-volume` volume at `/mnt`: +Build and deploy the Flask web server and mount the `my-volume` volume at `/mnt`: ```bash title="unikraft" -unikraft build . --output /http-python312-flask30:latest -unikraft run --metro=fra -m 512MiB -p 443:8080/http+tls -v my-volume:/mnt --image=/http-python312-flask30:latest +unikraft build . --output /httpserver-python312-flask30:latest +unikraft run --metro fra \ + -m 512M \ + -p 443:8080/http+tls \ + -v my-volume:/mnt \ + --image /httpserver-python312-flask30:latest ``` ```bash title="kraft" -kraft cloud deploy -M 512 -p 443:8080 --volume my-volume:/mnt . +kraft cloud deploy \ + -M 512Mi \ + -p 443:8080 \ + --volume my-volume:/mnt \ + . ``` You should see output like: -```ansi title="" + + +```ansi title="unikraft" +metro: fra +name: httpserver-python312-flask30-bxwxm +uuid: 3ff1ebad-2639-4214-bab4-ed35c4c32fa4 +state: starting +image: /httpserver-python312-flask30 +resources: + memory: 512MiB + vcpus: 1 +service: + uuid: 2021ca1e-5e47-a35c-bde2-7190f3815c07 + name: damp-sunset-azd6dtyt + domains: + - fqdn: damp-sunset-azd6dtyt.fra.unikraft.app +volumes: +- name: my-volume + uuid: 4d5576f7-315a-4eab-98b8-6c18fec076e0 + at: /mnt +networks: +- uuid: 4dc3b272-4fa2-ddb4-7ce0-5e66e8112a61 + private-ip: 10.0.6.5 + mac: 12:b0:66:99:b3:3f +timestamps: + created: just now +``` + +```ansi title="kraft" [●] Deployed successfully! β”‚ - β”œ────────── name: http-python312-flask30-2h608 - β”œ────────── uuid: dcaf899e-6831-4641-86a6-e2b39e9f0d98 - β”œ───────── state: running - β”œ─────────── url: https://holy-bonobo-px345skl.fra.unikraft.app - β”œ───────── image: http-python312-flask30@sha256:3ca3e773d5bc76e1231347e5345e19d0293ba05e502e387956ecba39a4c9372c - β”œ───── boot time: 219.61 ms - β”œ──────── memory: 512 MiB - β”œ─────── service: holy-bonobo-px345skl - β”œ── private fqdn: http-python312-flask30-2h608.internal - β”œ──── private ip: 172.16.6.4 - β””────────── args: /usr/bin/python3 /app/server.py + β”œ───────── name: httpserver-python312-flask30-bxwxm + β”œ───────── uuid: 3ff1ebad-2639-4214-bab4-ed35c4c32fa4 + β”œ──────── metro: https://api.fra.unikraft.cloud/v1 + β”œ──────── state: starting + β”œ─────── domain: https://damp-sunset-azd6dtyt.fra.unikraft.app + β”œ──────── image: oci://unikraft.io//httpserver-python312-flask30@sha256:d6c8e4c5a4f44e1d642d8eaeaa1d820b2841194dd6c5d4a872ae0a895c767da9 + β”œ─────── memory: 512 MiB + β”œ────── service: damp-sunset-azd6dtyt + β”œ─ private fqdn: httpserver-python312-flask30-bxwxm.internal + β””─── private ip: 10.0.6.5 ``` + + To confirm that the platform attached the volume, run: @@ -187,18 +264,27 @@ kraft cloud volume get my-volume You should see output like: -```text title="" -NAME CREATED AT SIZE ATTACHED TO STATE PERSISTENT -my-volume 34 minutes ago 100 MiB http-python312-flask30-2h608 mounted true + + +```ansi title="unikraft" +METRO NAME STATE SIZE CREATED +fra my-volume mounted 100MiB 2 minutes ago +``` + +```ansi title="kraft" +NAME CREATED AT SIZE ATTACHED TO MOUNTED BY STATE PERSISTENT +my-volume 2 minutes ago 100 MiB httpserver-python312-flask30-bxwxm httpserver-python312-flask30-bxwxm mounted true ``` -## Testing the server + + +### Testing The Flask server writes the time and date to `/mnt/log.txt` for each request. Test it by running `curl` several times. Example output: -```bash title="" +```console $ curl https://holy-bonobo-px345skl.fra.unikraft.app Log file created. 2024-02-24 08:15:32 @@ -215,62 +301,109 @@ Log file created. 2024-02-24 08:15:35 ``` -To test data persistence, first stop the instance, detach the volume, and remove the instance: +To test data persistence, you can stop the instance, then detach the volume from the instance: ```bash title="unikraft" -unikraft instances stop http-python312-flask30-2h608 -unikraft instances delete http-python312-flask30-2h608 +unikraft volumes detach my-volume --from httpserver-python312-flask30-bxwxm +unikraft instances delete httpserver-python312-flask30-bxwxm ``` ```bash title="kraft" -kraft cloud instance stop http-python312-flask30-2h608 -kraft cloud volumes detach my-volume -kraft cloud instance rm http-python312-flask30-2h608 +kraft cloud instance stop httpserver-python312-flask30-bxwxm +kraft cloud volume detach my-volume --from httpserver-python312-flask30-bxwxm ``` -The explicit volume detach command is only available in the legacy CLI. +You should see that the volume is now `available` again. + +:::info +While the instance is `stopped`, the volume is still attached to it, but **not** mounted. +Mounting of an attached volume happens automatically when the instance starts. +This is different from a [snapshotted](/features/snapshots/) instance, which is in `standby` state and has its volumes still mounted. +::: + +:::note +If you try to detach a volume while the instance is running, the platform will queue the operation. +The detach operation will trigger when the instance goes into `stopped`. +::: Now start another instance and, like before, mount the same volume: ```bash title="unikraft" -unikraft build . --output /http-python312-flask30:latest -unikraft run --metro=fra -m 512MiB -p 443:8080/http+tls -v my-volume:/mnt --image=/http-python312-flask30:latest +unikraft run --metro fra \ + -m 512M \ + -p 443:8080/http+tls \ + -v my-volume:/mnt \ + --image /httpserver-python312-flask30:latest ``` ```bash title="kraft" -kraft cloud deploy -M 512 -p 443:8080 --volume my-volume:/mnt . +kraft cloud instance create \ + --start \ + -M 512Mi \ + -p 443:8080 \ + --volume my-volume:/mnt \ + /httpserver-python312-flask30:latest ``` This should output something like: -```ansi title="" + + +```ansi title="unikraft" +metro: fra +name: httpserver-python312-flask30-0s9c7 +uuid: 59d06415-84b8-4bf5-85c7-997960382011 +state: starting +image: /httpserver-python312-flask30 +resources: + memory: 512MiB + vcpus: 1 +service: + uuid: ca8ae7e9-3767-85f6-3d70-b77bcf894a7c + name: winter-field-v6m37jgs + domains: + - fqdn: winter-field-v6m37jgs.fra.unikraft.app +volumes: +- name: my-volume + uuid: 4d5576f7-315a-4eab-98b8-6c18fec076e0 + at: /mnt +networks: +- uuid: 935ba4ef-39c2-07e7-d2ae-8cd3c9aae07c + private-ip: 10.0.6.5 + mac: 12:b0:66:99:b3:3f +timestamps: + created: just now +``` + +```ansi title="kraft" [●] Deployed successfully! β”‚ - β”œ────────── name: http-python312-flask30-0s9c7 - β”œ────────── uuid: 59d06415-84b8-4bf5-85c7-997960382011 - β”œ───────── state: running - β”œ─────────── url: https://winter-field-v6m37jgs.fra.unikraft.app - β”œ───────── image: http-python312-flask30@sha256:9f441af88df8968b368e7b658737c804fc8be87c864fc2d695e6adcda9a56acf - β”œ───── boot time: 202.11 ms - β”œ──────── memory: 512 MiB - β”œ─────── service: winter-field-v6m37jgs - β”œ── private fqdn: http-python312-flask30-0s9c7.internal - β”œ──── private ip: 172.16.6.7 - β””────────── args: /usr/bin/python3 /app/server.py + β”œ───────── name: httpserver-python312-flask30-0s9c7 + β”œ───────── uuid: 59d06415-84b8-4bf5-85c7-997960382011 + β”œ──────── metro: https://api.fra.unikraft.cloud/v1 + β”œ──────── state: starting + β”œ─────── domain: https://winter-field-v6m37jgs.fra.unikraft.app + β”œ──────── image: oci://unikraft.io//httpserver-python312-flask30@sha256:d6c8e4c5a4f44e1d642d8eaeaa1d820b2841194dd6c5d4a872ae0a895c767da9 + β”œ─────── memory: 512 MiB + β”œ────── service: winter-field-v6m37jgs + β”œ─ private fqdn: httpserver-python312-flask30-0s9c7.internal + β””─── private ip: 10.0.6.5 ``` + + Run one final `curl` to this new address. You should see the previous contents plus a new entry at the bottom: -```bash title="" +```console $ curl https://winter-field-v6m37jgs.fra.unikraft.app Log file created. 2024-02-24 08:15:32 @@ -279,74 +412,158 @@ Log file created. 2024-02-24 08:20:58 ``` -## Cleaning up +### Cleaning up To clean up, first detach the volume from all instances and then remove it: ```bash title="unikraft" -unikraft volumes delete +unikraft instances stop httpserver-python312-flask30-0s9c7 +unikraft volumes detach my-volume --from httpserver-python312-flask30-0s9c7 +unikraft volumes delete my-volume ``` ```bash title="kraft" -kraft cloud volume detach -kraft cloud volume remove +kraft cloud instance stop httpserver-python312-flask30-0s9c7 +kraft cloud volume detach my-volume --from httpserver-python312-flask30-0s9c7 +kraft cloud volume remove my-volume ``` -The explicit volume detach command is only available in the legacy CLI. +:::info +If the instance is already in `stopped` state, the detach operation will trigger immediately. +Otherwise, the platform will queue it and trigger when the instance fully stops. +Do **not** confuse a `stopped` instance with a [snapshotted](/features/snapshots/) instance, which is in `standby` state and has its volumes still mounted. +::: ## Volume templates -A volume template is a volume in the `TEMPLATE` state. -Once a volume transitions to template state, it's immutable: you can clone it but not write to it or delete it while active clones exist. +A volume template is a volume that you can use as a base to create new volumes with the same data. +When you create a volume template, the platform creates a copy of the source volume and marks it as a template. -The platform creates volume templates automatically when you convert an instance into an [instance template](/platform/instances#instance-templates): the platform converts all volumes attached to that instance into volume templates as an atomic operation. +:::caution +Once a volume transitions to template state, it's immutable: you can clone it but not write to it or delete it while active clones exist. +::: -Volume templates have their own create, read, update, and delete endpoints at [`/volumes/templates`](/api/v1/volumes#create-template-volumes). +Volume templates have their own create, read, update, and delete endpoints at [`/volumes/templates`](/api/platform/v1/volumes#create-template-volume). They support [delete locks](/platform/delete-locks) and [tags](/platform/tagging). -## Volume cloning +:::tip +Volume templates are useful because you can [clone](/platform/volumes#volume-cloning) them. +::: -A volume clone is an independent copy of an existing volume. -The clone operation is **asynchronous**: the platform creates the new volume immediately in a pending state, and the data copy completes in the background. -To clone one or more volumes, you can use the [`POST /volumes/clone`](/api/v1/volumes#clone-volumes) endpoint. +You can create volume templates from existing volumes using the CLI as well: -The platform also clones volumes implicitly when you clone an instance or create one from a template. -It clones each attached volume and attaches the clone to the new instance. + -You can't delete a source volume or template while it has active clones. +```bash title="unikraft" +unikraft volume template create my-volume +``` -## Shared volumes +```bash title="kraft" +kraft cloud volume template create my-volume +``` -More than one instance can mount the same volume simultaneously as long as all mounts are in *read-only* mode. -Read-only sharing is always permitted regardless of how many other instances have the volume mounted. + -A *read-write* mount is **exclusive**. -You can't add one while any other mount exists on that volume, and a volume can't have two *read-write* mounts at once. + -Specify `readonly: true` when attaching a volume at instance creation or via the attach endpoint: +```ansi title="unikraft" +metro: fra +name: my-volume +uuid: 4d5576f7-315a-4eab-98b8-6c18fec076e0 +state: template +size: 100MiB +filesystem: ext4 +persistent: true +timestamps: + created: just now +``` -```json title="POST /instances" -{ - ... - "volumes": [ - { - "name": "my-shared-volume", - "at": "/data", - "readonly": true - } - ], - ... -} +```ansi title="kraft" +4d5576f7-315a-4eab-98b8-6c18fec076e0 ``` -## Custom filesystems + + +You can list all volume templates with: + + -By default, volumes use a platform-configured filesystem (typically `ext4` for block volumes and `virtiofs` for hosted volumes). -If the platform operator has registered more named filesystems, you can select one by passing its name in the `filesystem` field when creating a volume: +```bash title="unikraft" +unikraft volume template list +``` + +```bash title="kraft" +kraft cloud volume template list +``` + + + + + +```ansi title="unikraft" +METRO NAME STATE SIZE CREATED +fra my-volume template 100MiB 1 minute ago +``` + +```ansi title="kraft" +NAME CREATED AT SIZE FREE ATTACHED TO STATE PERSISTENT POLICY +my-volume 1 minute ago 100 MiB 0 B template true static +``` + + + +:::note +The platform converts attached volumes to templates automatically when you convert an instance into an [instance template](/platform/instances#instance-templates). +::: + +## Volume cloning + +A volume clone is an independent copy of an existing volume. +The clone operation is **asynchronous**: the platform creates the new volume immediately in a `pending` state, and the data copy completes in the background. + +To clone one or more volumes, you can use the [`POST /volumes/clone`](/api/platform/v1/volumes#clone-volumes) endpoint. +You can also use the `unikraft` CLI to clone a volume: + +```bash title="unikraft" +unikraft volume clone my-volume --name my-volume-clone +``` + +```ansi title="unikraft" +metro: fra +name: my-volume-clone +uuid: 3f0625b4-cad3-4875-8b61-113aad4b02c7 +state: available +size: 100MiB +filesystem: ext4 +quota-policy: static +persistent: true +access-mode: rwo +timestamps: + created: just now +``` + +:::note +The platform clones volumes implicitly when you clone an instance or create one from a template. +It clones each attached volume and attaches the clone to the new instance. +::: + +:::caution +You can't delete a source volume while it has active clones. +::: + +:::caution +You can't clone a volume template. +::: + +## Filesystems + +By default, volumes are block devices with `ext4` filesystem. +There's also a `virtiofs` filesystem option, which is a shared filesystem that allows more than one instance to read and write the same volume simultaneously. +To specify the filesystem, use the `filesystem` field when creating a volume with [`POST /volumes`](/api/platform/v1/volumes#create-volume): ```json title="POST /volumes" { @@ -356,16 +573,31 @@ If the platform operator has registered more named filesystems, you can select o } ``` +You can also use the `unikraft` CLI: + +```bash title="unikraft" +unikraft volume create --metro fra \ + --name my-volume \ + --size 512M \ + --filesystem virtiofs +``` + +{/* vale off */} +:::tip +For BYOC or on-prem Unikraft Cloud installations, you can configure custom filesystems that can be backed by other media, such as S3 or network storage. +::: +{/* vale on */} + ### Managed volumes -:::caution -This is an advanced feature that's only available on the self-hosted platform. +:::caution[**Limited Access**] +Managed volumes are only available in BYOC or on-prem Unikraft Cloud installations. Quotas aren't yet enforced by the platform for managed volumes, so use them with caution to avoid filling up your host's disk. ::: A managed volume points to an existing directory on the host rather than a platform-allocated storage file. -Create a managed volume with a `host_path` field instead of `size_mb`: +Create a managed volume with a `host_path` field instead of `size_mb` when calling [`POST /volumes`](/api/platform/v1/volumes#create-volume): ```json title="POST /volumes" { @@ -376,12 +608,92 @@ Create a managed volume with a `host_path` field instead of `size_mb`: } ``` +Restrictions apply: + - `host_path` must be an absolute, normalised path to an existing directory on the host (no `.`, `..`, or `:` components). - `uid` and `gid` set the ownership used when accessing the volume from the guest. Both default to `0` if not specified. - `size_mb` and `template` are mutually exclusive with `host_path`. - The platform doesn't create, format, resize, or delete the backing directory for managed volumes. +## Access modes + +On creation, you can specify an `access_mode` that controls how instances can access or share the volume: + +{/* vale off */} +| Access mode | Description | +| ----------- | ----------------------------------------------------------------------------------------------- | +| `rwo` | Read-write once. The volume can only be mounted in read-write mode by a single instance at a time. This is the default access mode. | +| `rox` | Read-only many. The volume can be mounted in read-only mode by multiple instances simultaneously. | +| `rwx` | Read-write many. The volume can be mounted in read-write mode by multiple instances simultaneously. This access mode is only supported for volumes with `virtiofs` filesystem. | +{/* vale on */} + +You can specify the access mode when [creating a volume](/api/platform/v1/volumes#create-volume) with the `access_mode` field: + +```json title="POST /volumes" +{ + "name": "my-volume", + "size_mb": 512, + "access_mode": "rwo" +} +``` + +You can also use the `unikraft` CLI: + +```bash title="unikraft" +unikraft volume create --metro fra \ + --name my-volume \ + --size 512M \ + --access-mode rwo +``` + +### Read-only many + +More than one instance can mount the same `rox` volume simultaneously as long as all mounts are in *read-only* mode. +Read-only sharing is always permitted regardless of how many other instances have the volume mounted. + +Specify `readonly: true` when attaching a volume at [instance creation](/api/platform/v1/instances#create-instance) or via the [attach endpoint](/api/platform/v1/volumes#attach-volumes): + +```json title="POST /instances" +{ + ... + "volumes": [ + { + "name": "my-shared-volume", + "at": "/data", + "readonly": true + } + ], + ... +} +``` + +### Read-write many + +Read-write many (`rwx`) volumes are only supported for volumes with the `virtiofs` filesystem. +The downside is that `virtiofs` volumes are slower than block volumes, so only use them for `rwx` or [managed volumes](/platform/volumes#managed-volumes). +To create a `rwx` volume, you can use the [`POST /volumes`](/api/platform/v1/volumes#create-volume) endpoint: + +```json title="POST /volumes" +{ + "name": "my-volume", + "size_mb": 512, + "access_mode": "rwx", + "filesystem": "virtiofs" +} +``` + +You can also use the `unikraft` CLI: + +```bash title="unikraft" +unikraft volume create --metro fra \ + --name my-volume \ + --size 512M \ + --filesystem virtiofs \ + --access-mode rwx +``` + ## Learn more * The [CLI reference](/docs/cli/unikraft) and the [legacy CLI reference](/docs/cli/kraft/overview). * Unikraft Cloud's [REST API reference](/api/platform/v1), in particular the section on [volumes](/api/platform/v1/volumes). +* [Tutorial](/tutorials/rootfs-volumes-roms) explaining the difference of storage media on Unikraft Cloud. diff --git a/pages/tutorials/docker-to-ukc.mdx b/pages/tutorials/docker-to-ukc.mdx index 3b7ccfa3..4b8cd049 100644 --- a/pages/tutorials/docker-to-ukc.mdx +++ b/pages/tutorials/docker-to-ukc.mdx @@ -71,11 +71,13 @@ FROM image:latest Then create a matching `Kraftfile`: ```yaml title="Kraftfile" -spec: v0.6 +spec: v0.7 runtime: base-compat:latest -rootfs: ./Dockerfile +rootfs: + source: ./Dockerfile + format: erofs cmd: ["/path/from-entrypoint", "arg1", "arg2"] ``` @@ -90,7 +92,7 @@ Replace the placeholder command with the exact values extracted from `docker ima For example, if the image reports `Entrypoint=["/usr/local/bin/docker-entrypoint.sh"]` and `Cmd=["postgres"]`, then use `cmd: ["/usr/local/bin/docker-entrypoint.sh", "postgres"]`. :::caution -These Dockerfile keywords aren't deployment controls on Unikraft Cloud. +These `Dockerfile` keywords aren't deployment controls on Unikraft Cloud. In particular, `EXPOSE`, `HEALTHCHECK`, `ONBUILD`, `SHELL`, `STOPSIGNAL`, and `VOLUME` are ignored as Docker deployment semantics. Also note that instances run as `root` unless you explicitly switch users at runtime. ::: @@ -104,14 +106,18 @@ This confirms that the runtime, filesystem, and command line are valid. ```bash title="unikraft" unikraft build . --output /docker-port:latest -unikraft run --metro=fra -p 443: -m 512MiB --image=/docker-port:latest +unikraft run --metro fra \ + -p 443: \ + -m 512M \ + --image /docker-port:latest ``` ```bash title="kraft" kraft cloud deploy \ - -p 443: \ - -M 512Mi \ - . + -p 443: \ + -M 512Mi \ + --rootfs-type erofs \ + . ``` @@ -122,7 +128,7 @@ Take this value from the image documentation or from the `ExposedPorts` field yo If the instance fails to boot, inspect the logs and fix the command line first. In most broken first attempts, the issue is either a wrong executable path, a missing wrapper script, or insufficient memory. -:::note +:::tip If the upstream image depends on a specific `WORKDIR`, verify whether absolute paths are enough. If not, add a small wrapper script that changes directory and then `exec`s the original command. ::: @@ -158,8 +164,8 @@ If the image doesn't contain a shell, use a temporary debug image or inspect the The goal is the same in both cases: copy only what the final process needs at runtime. :::tip -Verify whether the upstream image is glibc-based or musl-based before copying the loader and shared libraries. -The sample paths above use musl-flavoured locations because that's a common pattern in the examples, but your image may differ. +Verify whether the upstream image is `glibc`-based or `musl`-based before copying the loader and shared libraries. +The sample paths above use `musl`-flavoured locations because that's a common pattern in the examples, but your image may differ. ::: ### Strip binaries and libraries @@ -170,7 +176,7 @@ This helps most with native apps and compiled dependencies. The common tool for this is `strip` from `binutils`. It removes symbol and debug information that the app doesn't need at runtime. -:::note +:::tip Use stripping only after the app already boots correctly. ::: @@ -208,7 +214,7 @@ As a rule of thumb, strip these files only when they're part of your runtime pat Don't strip files if you will need them for debugging later. ::: -### Convert the Dockerfile to a multi-stage build +### Convert the `Dockerfile` to a multi-stage build Start from the upstream image in a build stage. Then copy only the final executable, its direct shared libraries, and the app files into a `scratch` stage. @@ -236,26 +242,35 @@ COPY --from=upstream /etc/os-release /etc/os-release COPY --from=upstream /app /app ``` -This is the same shape used by examples such as [`node21-nextjs`](https://github.com/unikraft-cloud/examples/blob/main/node21-nextjs/Dockerfile), where the final image keeps only the Node binary, its required libraries, `/etc/os-release`, and the built app output. +This is the same shape used by examples such as [`httpserver-node21-nextjs`](https://github.com/unikraft-cloud/examples/tree/main/httpserver-node21-nextjs), where the final image keeps only the Node binary, its required libraries, `/etc/os-release`, and the built app output. ## Recommended deployment settings for the slim image -After reducing the filesystem, deploy it with [`EROFS`](/tutorials/rootfs-formats) when possible. -This often gives better cold-boot behaviour and lower memory pressure than `CPIO` for larger images. +After reducing the filesystem, deploy it with [EROFS](/tutorials/rootfs-formats) when possible. +This often gives better cold-boot behaviour and lower memory pressure than CPIO for larger images. ```bash title="unikraft" +# make sure to specify the erofs format in Kraftfile +# ... +# rootfs: +# source: ./Dockerfile +# format: erofs +# ... unikraft build . --output /docker-port:latest -unikraft run --metro=fra -p 443: -m 256MiB --image=/docker-port:latest +unikraft run --metro fra \ + -p 443: \ + -m 256M \ + --image /docker-port:latest ``` ```bash title="kraft" kraft cloud deploy \ - -p 443: \ - -M 256Mi \ - --rootfs-type erofs \ - . + -p 443: \ + -M 256Mi \ + --rootfs-type erofs \ + . ``` @@ -271,33 +286,35 @@ Before calling the image minimized, check the following: * copy only runtime files into the final stage; * no package manager cache or compiler output remains; * expose the instance port through the CLI `-p` flag; -* use [`EROFS`](/tutorials/rootfs-formats) unless you have a reason to stay on `CPIO`. +* use [EROFS](/tutorials/rootfs-formats) unless you have a reason to stay on CPIO. ## More configurations ### Environment variables If the upstream image depends on environment variables, you can optionally carry them over explicitly. -The most reliable method at deployment time is to pass them through the CLI with `--env` or `-e`. +The most reliable method at deployment time is to pass them through the CLI with `--env` or `-e`: ```bash title="unikraft" unikraft build . --output /docker-port:latest -unikraft run --metro=fra -p 443: -m 256MiB \ +unikraft run --metro fra \ + -p 443: \ + -m 256M \ -e APP_ENV=production \ -e LOG_LEVEL=info \ - --image=/docker-port:latest + --image /docker-port:latest ``` ```bash title="kraft" kraft cloud deploy \ - -p 443: \ - -M 256Mi \ - --rootfs-type erofs \ - -e APP_ENV=production \ - -e LOG_LEVEL=info \ - . + -p 443: \ + -M 256Mi \ + --rootfs-type erofs \ + -e APP_ENV=production \ + -e LOG_LEVEL=info \ + . ``` @@ -305,8 +322,8 @@ kraft cloud deploy \ If the image expects a wrapper entrypoint that prepares environment variables before starting the main process, keep that wrapper in your `cmd` line or replace it with your own script. The [Environment Variables](/tutorials/environment-variables) tutorial describes the wrapper pattern in more detail. -:::note -Dockerfile `ENV` instructions carry over into the image, but you might want to set them explicitly at deployment time for better visibility and control. +:::tip +`Dockerfile` `ENV` instructions carry over into the image, but you might want to override them at deployment time for better visibility and control. ::: ### Users and permissions @@ -321,11 +338,11 @@ When slimming the image, check permission-sensitive paths such as: * runtime directories under `/var` or `/run`; * app state directories under `/app`, `/srv`, or `/data`; -* Unix sockets or process ID files created by the upstream entrypoint. +* UNIX sockets or process ID files created by the upstream entrypoint. If the original image relied on a specific unprivileged user, you may need to preserve passwd and group metadata files as well: -```dockerfile +```dockerfile title="Dockerfile" COPY --from=upstream /etc/passwd /etc/passwd COPY --from=upstream /etc/group /etc/group ``` @@ -343,21 +360,22 @@ The simplest way to test scale-to-zero is to deploy with an explicit scale-to-ze ```bash title="unikraft" unikraft build . --output /docker-port:latest -unikraft run --metro=fra -p 443: -m 256MiB \ - --scale-to-zero \ - --scale-to-zero-cooldown-time-ms=5000 \ - --image=/docker-port:latest +unikraft run --metro fra \ + -p 443: \ + -m 256M \ + --scale-to-zero policy=on,cooldown-time=5000,stateful=true \ + --image /docker-port:latest ``` ```bash title="kraft" kraft cloud deploy \ - -p 443: \ - -M 256Mi \ - --rootfs-type erofs \ - --scale-to-zero on \ - --scale-to-zero-stateful \ - --scale-to-zero-cooldown 5s \ - . + -p 443: \ + -M 256Mi \ + --scale-to-zero on \ + --scale-to-zero-stateful \ + --scale-to-zero-cooldown 5s \ + --rootfs-type erofs \ + . ``` @@ -379,11 +397,11 @@ kraft cloud instance list If everything is working, the instance should transition to `standby` when idle and wake up again on the next request. Apps with long initialization phases, background work, or long-lived connections may need extra adjustments before scale-to-zero behaves well. -In those cases, review the [Scale-to-Zero](/features/scale-to-zero) feature page and consider a longer cooldown, stateful mode, or temporarily disabling scale-to-zero from inside the app during startup. +In those cases, review the [Scale-to-Zero](/features/scale-to-zero) feature page and consider a longer cooldown, stateful mode, or [temporarily disabling scale-to-zero](/tutorials/scale-to-zero-triggers#manual-triggers) from inside the app during startup. ## Learn more * [Images](/platform/images) and how `Dockerfile`s, `Kraftfile`s, and runtimes fit together. -* [Rootfs Formats](/tutorials/rootfs-formats) for understanding the `CPIO` and `EROFS` tradeoffs. -* The [Kraftfile reference](https://unikraft.org/docs/cli/reference/kraftfile) for all supported top-level fields. -* The [Next.js guide](/guides/nextjs) as a concrete example of a multi-stage Docker build trimmed down for deployment. +* [Rootfs Formats](/tutorials/rootfs-formats) for understanding the CPIO and EROFS tradeoffs. +* The [`Kraftfile` reference](/docs/kraftfile/v0.7) for all supported top-level fields. +* The [Next.js guide](/guides/httpserver-node21-nextjs) as a concrete example of a multi-stage Docker build trimmed down for deployment. diff --git a/pages/tutorials/environment-variables.mdx b/pages/tutorials/environment-variables.mdx index 552d1600..f9dae501 100644 --- a/pages/tutorials/environment-variables.mdx +++ b/pages/tutorials/environment-variables.mdx @@ -9,40 +9,27 @@ Environment variables are a common way to configure applications at runtime. Unikraft Cloud supports passing environment variables to instances during deployment through many methods. This tutorial discusses each below and orders them by superseeding precedence (from most powerful to weakest). -## Setting environment variables - Below are all the ways you can set environment variables for your Unikraft Cloud instances. The order they're presented is from the least precedence to the highest precedence. -### Dockerfile ENV - -:::caution -At the moment environment variables from the `Dockerfile` are passed in the image but not read by the Unikraft Cloud platform. -Please use the other methods described below in the meantime. -::: +## Dockerfile ENV You can set environment variables in your `Dockerfile` using the `ENV` instruction in your target `FROM` stage. The CLI automatically reads them when building the image and sets them in the resulting packaged image. -```dockerfile +```dockerfile title="Dockerfile" # ... FROM ubuntu:latest ENV MY_ENV_VAR=my_value # ... ``` -### Kraftfile env - -:::caution -At the moment environment variables from the `Kraftfile` are passed in the image but not read by the Unikraft Cloud platform. -Nonetheless, the legacy deploy command can still read them and set them in the instance. -For other use cases, please use the other methods described below in the meantime. -::: +## Kraftfile env You can set environment variables in your `Kraftfile` using the `env` field. `kraft` automatically reads them when building the image and sets them in the resulting packaged image. -```yaml +```yaml title="Kraftfile" # ... env: MY_ENV_VAR: my_value @@ -53,7 +40,7 @@ This method takes precedence over the `Dockerfile`. It will override environment variables with the same name set in the `Dockerfile`. ::: -### Kraft flags +## CLI flags When deploying an instance, you can pass environment variables using the `--env` (or `-e`) flag. You can specify many environment variables by using the flag many times. @@ -62,11 +49,15 @@ The flag works similarly to the `docker run -e` flag. ```bash title="unikraft" -unikraft run --metro=fra -e MY_ENV_VAR=my_value --image=my-app:latest +unikraft run --metro fra \ + -e MY_ENV_VAR=my_value \ + --image /my-app:latest ``` ```bash title="kraft" -kraft cloud deploy my-app --env MY_ENV_VAR=my_value +kraft cloud deploy my-app \ + -e MY_ENV_VAR=my_value \ + . ``` @@ -76,13 +67,13 @@ This method takes precedence over the `Dockerfile` and `Kraftfile`. It will override environment variables with the same name set in the previous two methods. ::: -### Wrapper script +## Wrapper script Create a wrapper script that sets the environment variables before executing the main app. Check what the original entrypoint of your app is and call it after setting the variables. For example, if the original entrypoint is `/entrypoint.sh`, you can create a new script `wrapper.sh` like this: -```bash +```bash title="wrapper.sh" #!/bin/sh export MY_ENV_VAR="my_value" @@ -98,7 +89,7 @@ Then you change either your `Dockerfile` or `Kraftfile` to use this wrapper scri Kraftfile - ```Dockerfile + ```dockerfile title="Dockerfile" # ... COPY wrapper.sh /wrapper.sh RUN chmod +x /wrapper.sh @@ -106,7 +97,7 @@ Then you change either your `Dockerfile` or `Kraftfile` to use this wrapper scri ``` - ```yaml + ```yaml title="Kraftfile" # ... cmd: "/wrapper.sh" ``` @@ -114,17 +105,12 @@ Then you change either your `Dockerfile` or `Kraftfile` to use this wrapper scri :::tip -This method has the highest precedence and will override environment variables set in the cli, `Dockerfile`, or `Kraftfile` as it hardcodes them in the script. +This method has the highest precedence and will override environment variables set in the CLI, `Dockerfile`, or `Kraftfile` as it hardcodes them in the script. Thus it's recommended to do this only for variables that you set and don't change. ::: -## Conclusion - -Out of the four ways to set environment variables in Unikraft Cloud instances you should pick the ones that work best for you. -Consider that the `Dockerfile` and `Kraftfile` can set variables for the image itself, while the CLI and wrapper script set them at runtime. - ## Learn more * The [CLI reference](/docs/cli/unikraft) and the [legacy CLI reference](/docs/cli/kraft/overview). -* The [Dockerfile reference](https://docs.docker.com/reference/dockerfile/) for understanding how to set environment variables in Dockerfiles. -* The [Kraftfile reference](https://unikraft.org/docs/cli/reference/kraftfile) for understanding how to set environment variables in Kraftfiles. +* The [`Dockerfile` reference](https://docs.docker.com/reference/dockerfile/) for understanding how to set environment variables in `Dockerfile`s. +* The [`Kraftfile` reference](/docs/kraftfile/v0.7) for understanding how to set environment variables in `Kraftfile`s. diff --git a/pages/tutorials/instance-metrics.mdx b/pages/tutorials/instance-metrics.mdx deleted file mode 100644 index eb6b4877..00000000 --- a/pages/tutorials/instance-metrics.mdx +++ /dev/null @@ -1,412 +0,0 @@ ---- -title: Instance Metrics -navigation_icon: bar-chart ---- - -import { Tabs, TabsContent, TabsList, TabsTrigger } from "zudoku/ui/Tabs" - -Unikraft Cloud provides an endpoint to retrieve real-time hardware and network metrics for your running instances. -These metrics are useful for monitoring the performance, memory usage, and network traffic handled by your app. - -## Prerequisites - -To access the instance metrics, you must have the **`developer`** permission role for your user. - -## Retrieving metrics - -You can retrieve the metrics of one or more instances by making a `GET` request to the `/instances/metrics` endpoint. -The request body must contain an array of instance UUIDs or names. -Use a tool like `curl` for ad-hoc queries, or configure your monitoring system to consume the endpoint's Prometheus-formatted output. - -### Example - -The following example creates an instance and retrieves its metrics using `curl`. - -First, provision a new instance: - - - -```bash title="unikraft" -git clone https://github.com/unikraft-cloud/examples -cd examples/nginx/ -unikraft run --metro=fra -p 443:8080/http+tls -m 256MiB --image=nginx:latest -``` - -```bash title="kraft" -git clone https://github.com/unikraft-cloud/examples -cd examples/nginx/ -kraft cloud deploy -p 443:8080 -M 256 . -``` - - - -This command will create the NGINX instance with scale-to-zero enabled: - -```ansi title="" -[●] Deployed successfully! - β”‚ - β”œ────────── name: nginx-26g86 - β”œ────────── uuid: 3605978e-5feb-4209-8f9e-de45f00a7d66 - β”œ───────── state: running - β”œ─────────── url: https://black-snowflake-iy7509ap.fra.unikraft.app - β”œ───────── image: nginx@sha256:19854a12fe97f138313cb9b4806828cae9cecf2d050077a0268d98129863f954 - β”œ───── boot time: 7.77 ms - β”œ──────── memory: 256 MiB - β”œ─────── service: black-snowflake-iy7509ap - β”œ── private fqdn: nginx-26g86.internal - β”œ──── private ip: 172.16.6.1 - β””────────── args: /usr/bin/nginx -c /etc/nginx/nginx.conf -``` - -Now, request the metrics for that instance. -Set `UKC_TOKEN` and `UKC_METRO` for the API calls: - - - - JSON - Prometheus - - -```bash title="" -curl -X GET "https://api.$UKC_METRO.unikraft.cloud/v1/instances/metrics" \ - -H "Authorization: Bearer $UKC_TOKEN" \ - -H "Content-Type: application/json" \ - -H "Accept: application/json" \ - -d '[{"name":"nginx-26g86"}]' -``` - - -```bash title="" -curl -X GET "https://api.$UKC_METRO.unikraft.cloud/v1/instances/metrics" \ - -H "Authorization: Bearer $UKC_TOKEN" \ - -H "Content-Type: application/json" \ - -d '[{"name":"nginx-26g86"}]' -``` - - - - - - JSON - Prometheus - - -```json -{ - "status": "success", - "data": { - "instances": [ - { - "status": "success", - "uuid": "3605978e-5feb-4209-8f9e-de45f00a7d66", - "name": "nginx-26g86", - "state": "standby", - "start_count": 2, - "started_at": "2026-03-18T12:31:49Z", - "stopped_at": "2026-03-18T12:31:50Z", - "uptime_s": 7.817000, - "boot_time_s": 0.005900, - "net_time_s": 0.025256, - "rss_bytes": 0, - "cpu_time_s": 0, - "rx_bytes": 1614, - "rx_packets": 10, - "tx_bytes": 537, - "tx_packets": 5, - "nconns": 0, - "nreqs": 0, - "nqueued": 0, - "ntotal": 1, - "wakeup_latency_seconds": [ - { - "bucket_s": 0.001000, - "count": 0 - }, - { - "bucket_s": 0.002000, - "count": 0 - }, - { - "bucket_s": 0.004000, - "count": 0 - }, - { - "bucket_s": 0.008000, - "count": 0 - }, - { - "bucket_s": 0.016000, - "count": 0 - }, - { - "bucket_s": 0.032000, - "count": 1 - }, - { - "bucket_s": 0.064000, - "count": 0 - }, - { - "bucket_s": 0.128000, - "count": 0 - }, - { - "bucket_s": 0.256000, - "count": 0 - }, - { - "bucket_s": 0.512000, - "count": 0 - }, - { - "bucket_s": 1.024000, - "count": 0 - }, - { - "bucket_s": 2.048000, - "count": 0 - }, - { - "bucket_s": 4.096000, - "count": 0 - }, - { - "bucket_s": null, - "count": 0 - } - ], - "wakeup_latency_seconds_sum": 0.023 - } - ] - }, - "op_time_us": 125 -} -``` - - -```prom -# HELP instance_state 0=stopped,1=starting,2=running,3=draining,4=stopping,5=standby,6=template -# TYPE instance_state gauge -instance_state{instance_uuid="3605978e-5feb-4209-8f9e-de45f00a7d66"} 5 - -# HELP instance_start_count Number of times the instance has been started -# TYPE instance_start_count counter -instance_start_count{instance_uuid="3605978e-5feb-4209-8f9e-de45f00a7d66"} 2 - -# HELP instance_restart_count Number of times the instance has been restarted -# TYPE instance_restart_count counter -instance_restart_count{instance_uuid="3605978e-5feb-4209-8f9e-de45f00a7d66"} 0 - -# HELP instance_started_at Time when the instance started -# TYPE instance_started_at gauge -instance_started_at{instance_uuid="3605978e-5feb-4209-8f9e-de45f00a7d66"} 1773840567 - -# HELP instance_stopped_at Time when the instance stopped -# TYPE instance_stopped_at gauge -instance_stopped_at{instance_uuid="3605978e-5feb-4209-8f9e-de45f00a7d66"} 1773840567 - -# HELP instance_uptime_s Uptime in seconds -# TYPE instance_uptime_s gauge -instance_uptime_s{instance_uuid="3605978e-5feb-4209-8f9e-de45f00a7d66"} 7.817000 - -# HELP instance_boot_time_s Boot time in seconds -# TYPE instance_boot_time_s gauge -instance_boot_time_s{instance_uuid="3605978e-5feb-4209-8f9e-de45f00a7d66"} 0.005900 - -# HELP instance_net_time_s Net time in seconds -# TYPE instance_net_time_s gauge -instance_net_time_s{instance_uuid="3605978e-5feb-4209-8f9e-de45f00a7d66"} 0.025256 - -# HELP instance_rss_bytes Resident set size in bytes -# TYPE instance_rss_bytes gauge -instance_rss_bytes{instance_uuid="3605978e-5feb-4209-8f9e-de45f00a7d66"} 0 - -# HELP instance_cpu_time_s Consumed CPU time in seconds -# TYPE instance_cpu_time_s counter -instance_cpu_time_s{instance_uuid="3605978e-5feb-4209-8f9e-de45f00a7d66"} 0.000000 - -# HELP instance_rx_bytes Amount of bytes received over network -# TYPE instance_rx_bytes counter -instance_rx_bytes{instance_uuid="3605978e-5feb-4209-8f9e-de45f00a7d66"} 1614 - -# HELP instance_tx_bytes Amount of bytes transmitted over network -# TYPE instance_tx_bytes counter -instance_tx_bytes{instance_uuid="3605978e-5feb-4209-8f9e-de45f00a7d66"} 543 - -# HELP instance_rx_packets Count of packets received from network -# TYPE instance_rx_packets counter -instance_rx_packets{instance_uuid="3605978e-5feb-4209-8f9e-de45f00a7d66"} 10 - -# HELP instance_tx_packets Count of packets transmitted over network -# TYPE instance_tx_packets counter -instance_tx_packets{instance_uuid="3605978e-5feb-4209-8f9e-de45f00a7d66"} 5 - -# HELP instance_nconns Number of active connections -# TYPE instance_nconns gauge -instance_nconns{instance_uuid="3605978e-5feb-4209-8f9e-de45f00a7d66"} 0 - -# HELP instance_nreqs Number of active requests -# TYPE instance_nreqs gauge -instance_nreqs{instance_uuid="3605978e-5feb-4209-8f9e-de45f00a7d66"} 0 - -# HELP instance_nqueued Number of queued connections/requests -# TYPE instance_nqueued gauge -instance_nqueued{instance_uuid="3605978e-5feb-4209-8f9e-de45f00a7d66"} 0 - -# HELP instance_ntotal Number of processed connections/requests -# TYPE instance_ntotal counter -instance_ntotal{instance_uuid="3605978e-5feb-4209-8f9e-de45f00a7d66"} 1 - -# HELP instance_wakeup_latency_seconds Wakeup latencies in seconds -# TYPE instance_wakeup_latency_seconds histogram -instance_wakeup_latency_seconds{le="0.001000"} 0 -instance_wakeup_latency_seconds{le="0.002000"} 0 -instance_wakeup_latency_seconds{le="0.004000"} 0 -instance_wakeup_latency_seconds{le="0.008000"} 0 -instance_wakeup_latency_seconds{le="0.016000"} 0 -instance_wakeup_latency_seconds{le="0.032000"} 1 -instance_wakeup_latency_seconds{le="0.064000"} 1 -instance_wakeup_latency_seconds{le="0.128000"} 1 -instance_wakeup_latency_seconds{le="0.256000"} 1 -instance_wakeup_latency_seconds{le="0.512000"} 1 -instance_wakeup_latency_seconds{le="1.024000"} 1 -instance_wakeup_latency_seconds{le="2.048000"} 1 -instance_wakeup_latency_seconds{le="4.096000"} 1 -instance_wakeup_latency_seconds{le="+Inf"} 1 -instance_wakeup_latency_seconds_sum 23 -instance_wakeup_latency_seconds_count 1 -``` - - - -If the request is successful, you will receive a response reporting the instance's performance and network data. - -## Understanding the Response - -The metrics response contains fields for CPU, memory, boot time, and networking. -The Prometheus output comments contain details about each metric. -Below is a detailed breakdown of each field returned in the metrics object. - -### Instance Info - -#### **`uuid`** - -The UUID of the instance. - -#### **`name`** - -The name of the instance. - -#### **`state`** - -The current state of the instance. -Possible values: `stopped`, `starting`, `running`, `draining`, `stopping`, `standby`, `template`. - -### Lifecycle and uptime - -#### **`start_count`** - -Number of times the instance started, including scale-to-zero wakeups. - -#### **`started_at`** - -Timestamp of the most recent instance start, in [RFC 3339](https://www.rfc-editor.org/rfc/rfc3339) format. - -#### **`stopped_at`** - -Timestamp of the most recent instance stop, in [RFC 3339](https://www.rfc-editor.org/rfc/rfc3339) format. - -#### **`uptime_s`** - -Total accumulated uptime of the instance in seconds across all starts. - -### Memory & CPU - -#### **`rss_bytes`** - -Resident set size in bytes. -This is the amount of physical memory that the instance has touched and is currently reserved for it. -It grows as the instance uses more memory up to the configured limit. -This metric drops to `0` when the instance is in standby. - -#### **`cpu_time_s`** - -Consumed CPU time in seconds for the last second. -This metric drops to `0` when the instance is in standby. - -### Boot and network initialization times - -#### **`boot_time_s`** - -Boot time in seconds. -Calculated as the time between the virtualization toolstack responding to a boot request and the moment the guest OS starts executing user code. - -#### **`net_time_s`** - -Network initialization time in seconds. -This is the time from when the instance started until the user-level app starts listening on a non-localhost port. - -### Network traffic - -#### **`rx_bytes`** - -Total amount of network bytes received. - -#### **`rx_packets`** - -Total count of network packets received. - -#### **`tx_bytes`** - -Total amount of network bytes transmitted. - -#### **`tx_packets`** - -Total count of network packets transmitted. - -### Connections & Requests - -#### **`nconns`** - -Number of currently established inbound connections (non-HTTP). -This metric drops to `0` when the instance is in standby. - -#### **`nreqs`** - -Number of in-flight HTTP requests. -This metric drops to `0` when the instance is in standby. - -#### **`nqueued`** - -Number of queued inbound connections and HTTP requests. -This metric drops to `0` when the instance is in standby. - -#### **`ntotal`** - -Total number of inbound connections and HTTP requests handled. - -### Wakeup latency - -#### **`wakeup_latency_seconds`** - -A histogram of scale-to-zero wakeup latencies. -Each entry contains a `bucket_s` threshold (in seconds) and the `count` of wakeups that fell within that bucket. -The final bucket has `bucket_s: null`, representing the `+Inf` overflow bucket for wakeups exceeding all defined thresholds. - -#### **`wakeup_latency_seconds_sum`** - -The sum of all wakeup latencies in seconds. -Together with the histogram buckets this allows computing a mean wakeup latency: `wakeup_latency_seconds_sum / ntotal`. - -## Conclusion - -Instance metrics give you a real-time view into the performance and health of your Unikraft Cloud instances. -By monitoring memory usage, CPU time, boot and network initialization times, and connection statistics, you can understand how your app behaves under load. -The Prometheus format lets you plug these metrics directly into monitoring tools such as Grafana or any other Prometheus-compatible system. - -## Learn more - -* [Scale to zero](/features/scale-to-zero) and how it affects instance lifecycle. -* [Scale to zero triggers](/tutorials/scale-to-zero-triggers) for controlling when instances wake up. -* [Platform instances](/platform/instances) for managing and inspecting your instances. -* The [CLI reference](/docs/cli/unikraft) and the [legacy CLI reference](/docs/cli/kraft/overview). diff --git a/pages/tutorials/kraftkit-to-unikraft.mdx b/pages/tutorials/kraftkit-to-unikraft.mdx index ce2c9116..c0795ef0 100644 --- a/pages/tutorials/kraftkit-to-unikraft.mdx +++ b/pages/tutorials/kraftkit-to-unikraft.mdx @@ -19,7 +19,11 @@ The biggest change is that the legacy CLI combines packaging and deployment into In the legacy flow: ```bash title="kraft" -kraft cloud deploy -p 443:8080/http+tls -M 512Mi --rootfs-type erofs . +kraft cloud deploy \ + -p 443:8080/http+tls \ + -M 512Mi \ + --rootfs-type erofs \ + . ``` the CLI reads the local project, builds the root filesystem and image if needed, pushes it, and starts an instance. @@ -28,7 +32,10 @@ In the new flow, the CLI splits the same work into two clearer stages: ```bash title="unikraft" unikraft build . --output /my-app:latest -unikraft run --metro=fra -p 443:8080/http+tls -m 512M --image=/my-app:latest +unikraft run --metro fra\ + -p 443:8080/http+tls\ + -m 512M \ + --image /my-app:latest ``` This split has two practical consequences: @@ -72,7 +79,8 @@ The `default` profile contains all the publicly available metros: ```bash unikraft profile list ``` -```ansi + +```ansi title="" NAME ACTIVE METROS default true ["fra0", "dal0", "sin0", "was1", "fra", "dal", "sin", "was", "sfo"] ``` @@ -118,7 +126,10 @@ httpserver-go121-xjsli bitter-bonobo-20plsjfu.was.unikraft.app standby[0 Write operations require an explicit metro selection with `--metro`: ```bash title="unikraft" -unikraft run --metro=fra -p 443:8080/http+tls -m 512MiB --image=/my-app:latest +unikraft run --metro fra \ + -p 443:8080/http+tls \ + -m 512Mi \ + --image /my-app:latest ``` For custom or private metros, define a profile in `~/.config/unikraft/config.yaml`: @@ -190,7 +201,13 @@ unikraft build . --output /my-app:latest ``` ```bash title="kraft" -kraft pkg --push -p kraftcloud -m x86_64 --rootfs-type erofs --name /my-app:latest . +kraft pkg \ + --plat kraftcloud \ + --arch x86_64 \ + --name /my-app:latest \ + --rootfs-type erofs \ + --push \ + . ```
@@ -200,11 +217,18 @@ kraft pkg --push -p kraftcloud -m x86_64 --rootfs-type erofs --name /my- ```bash title="unikraft" -unikraft run --metro=fra -p 443:8080/http+tls -m 512M --image=/my-app:latest +unikraft run --metro fra \ + -p 443:8080/http+tls \ + -m 512M \ + --image /my-app:latest ``` ```bash title="kraft" -kraft cloud vm create --start -p 443:8080/http+tls -M 512Mi /my-app:latest +kraft cloud vm create \ + --start \ + -p 443:8080/http+tls \ + -M 512Mi \ + /my-app:latest ``` @@ -242,11 +266,16 @@ kraft cloud instance remove ```bash title="unikraft" -unikraft volume create --metro=fra --name=data --size=200M +unikraft volume create \ + --metro fra \ + --name data \ + --size 200M ``` ```bash title="kraft" -kraft cloud volume create --name data --size 200Mi +kraft cloud volume create \ + --name data \ + --size 200Mi ``` @@ -260,11 +289,15 @@ Memory units behave differently in the new CLI. ```bash title="unikraft" -unikraft run --metro=fra -m 512M --image=/my-app:latest +unikraft run --metro fra \ + -m 512M \ + --image /my-app:latest ``` ```bash title="kraft" -kraft cloud deploy -M 512Mi . +kraft cloud deploy \ + -M 512Mi \ + . ``` @@ -279,9 +312,9 @@ The new CLI folds three legacy switches into one structured flag: ```bash title="unikraft" -unikraft run --metro=fra \ +unikraft run --metro fra \ --scale-to-zero policy=idle,cooldown-time=5000,stateful=true \ - --image=/my-app:latest + --image /my-app:latest ``` ```bash title="kraft" @@ -335,7 +368,7 @@ The migrated workflow is: ```bash title="unikraft" unikraft build . --output /my-app:latest -unikraft run --metro=fra -n my-app -p 443:8080/http+tls -m 512M -e APP_ENV=production --image=/my-app:latest +unikraft run --metro fra -n my-app -p 443:8080/http+tls -m 512M -e APP_ENV=production --image /my-app:latest unikraft instances get my-app unikraft instance logs my-app -f unikraft instances delete my-app @@ -385,4 +418,4 @@ For more details, check the following references: * The [unikraft CLI overview](/docs/cli/overview). * The [legacy CLI overview](/docs/cli/legacy-overview). * The [Filters](/docs/cli/filters) reference for the new filtering language. -* The [Environment Variables](/tutorials/environment-variables) tutorial for runtime configuration patterns. \ No newline at end of file +* The [Environment Variables](/tutorials/environment-variables) tutorial for runtime configuration patterns. diff --git a/pages/tutorials/rootfs-compression.mdx b/pages/tutorials/rootfs-compression.mdx index c008bb25..f879ecec 100644 --- a/pages/tutorials/rootfs-compression.mdx +++ b/pages/tutorials/rootfs-compression.mdx @@ -12,56 +12,79 @@ For example, text files often compress by up to 90% and more, whilst already com ## Getting started The Unikraft Platform supports both compressed and uncompressed rootfses, but it's highly recommended to use uncompressed ones if size allows. -Decompressing is a step done by the kernel at boot time, resulting in slower access times across the board. +Decompressing is a step done by the kernel at boot time (or runtime, with EROFS), resulting in slower access times across the board. That's why the first attempt at using the platform should always be with uncompressed rootfses. -Both formats, [`CPIO` and `EROFS`](/tutorials/rootfs-formats), support compression. -`CPIO` does full file system compression (like running `zip` on the file). -`EROFS` does per-block compression meaning that the system decompresses only the accessed blocks on-the-fly. +Both formats, [CPIO and EROFS](/tutorials/rootfs-formats), support compression. +CPIO does full file system compression (like running `zip` on the file). +EROFS does per-block compression meaning that the system decompresses only the accessed blocks on-the-fly. Both of them support most compression algorithms, but the recommended ones are those that have fast decompression speeds like `lz4hc`. -As presented in the [rootfs formats tutorial](/tutorials/rootfs-formats) and in the [FAQ](/faq), the performance characteristics of the two formats differ. +As presented in the [rootfs formats tutorial](/tutorials/rootfs-formats) and in the [FAQ](/faq#how-does-the-image-size-and-memory-relate-to-boot-times), the performance characteristics of the two formats differ. When using compression, the system amplifies these differences further. -For `CPIO`, the system must decompress the entire archive first before using it. +For CPIO, the system must decompress the entire archive first before using it. This means that boot times increase further, and memory usage during boot is also higher negating the benefit of using compression in the first place. -`EROFS` is better, as the system decompresses only the accessed blocks on-the-fly, meaning that boot times are less affected, and memory usage is also kept lower. +EROFS is better, as the system decompresses only the accessed blocks on-the-fly, meaning that boot times are less affected, and memory usage is also kept lower. This has the added downside that Direct Access (`DAX`) isn't possible when using compression, as the host side can't map a compressed image into guest memory. This caveat results in having to load the entire compressed rootfs into guest memory, increasing memory usage by at least the size of the compressed rootfs. -Still, if many files aren't accessed in boot, the cold boot time might decrease compared to an uncompressed `EROFS` rootfs. +Still, if many files aren't accessed in boot, the cold boot time might decrease compared to an uncompressed EROFS rootfs. -## Packaging a Compressed rootfs +## Packaging a compressed rootfs -Packaging a compressed rootfs is different depending on the format used. -The legacy [`kraft` CLI](https://unikraft.org/docs/cli/install) implements `CPIO` compression directly, while `EROFS` compression must happen externally using the `mkfs.erofs` tool. +The `unikraft` CLI doesn't support compressing rootfses. +This choice is deliberate, as the performance downsides aren't worth it most of the time. +The legacy `kraft` CLI implements CPIO compression directly. -### CPIO Compression +Both CLIs support deploying pre-compressed EROFS rootfses. -To package a compressed `CPIO` rootfs using the legacy CLI, you can use the `--compress` flag when running the `kraft pkg` or `kraft cloud deploy` command. +As a study case, this tutorial uses this [`node` image](https://github.com/unikraft-cloud/examples/tree/main/node-playwright-chromium), which has a big rootfs size. +A full guide on using this image is available [here](/guides/node-playwright-chromium). + +### CPIO compression + +To package a compressed CPIO rootfs using the legacy CLI, you can use the `--compress` flag when running the `kraft pkg` or `kraft cloud deploy` command. This uses `gzip` compression to compress the initrd file before packaging it into a `OCI` image and uploading it to the Unikraft Cloud platform. - + + +```bash title="unikraft" +# not supported, use the legacy CLI +``` ```bash title="kraft" -kraft cloud deploy \ - --rootfs-type cpio \ - --compress \ - ... +kraft pkg \ + --plat kraftcloud \ + --arch x86_64 \ + --name index.unikraft.io//node-playwright-chromium:cpio-compressed \ + --rootfs-type cpio \ + --compress \ + --push \ + . +kraft cloud vm create \ + --start \ + --scale-to-zero on \ + -p 443:8080/tls+http \ + -M 2200Mi \ + index.unikraft.io//node-playwright-chromium:cpio-compressed ``` -### EROFS Compression +:::warning +It's **not** recommended to use CPIO compression due to the performance downsides mentioned above. +Moreover, pre-compressed CPIO rootfses aren't officially supported by the CLI. +::: -Currently, the legacy CLI has no direct support for compressing `EROFS` rootfses. -To package a compressed `EROFS` rootfs, you need to use the `mkfs.erofs` tool before packaging it with `kraft`. +### EROFS compression +To package a compressed EROFS rootfs, you need to use the `mkfs.erofs` tool before packaging it with the CLI. To do this you will also have to first export the image built from the `Dockerfile` into a `tar` archive: ```bash -docker build -o type=tar,dest=rootfs -t my-docker-image -f Dockerfile . +docker build -o type=tar,dest=rootfs.tar -t node-playwright-chromium -f Dockerfile . ``` -You can then use the resulting `tar` archive to create a compressed `EROFS` rootfs using the following command. +You can then use the resulting `tar` archive to create a compressed EROFS rootfs using the following command. Note that you can change the compression algorithm and level by modifying the `-z` option. In this example the level is `9` (maximum) and the algorithm is `lz4hc` (high compression lz4): @@ -69,101 +92,74 @@ In this example the level is `9` (maximum) and the algorithm is `lz4hc` (high co mkfs.erofs --all-root -b 4096 -d2 -E noinline_data -z lz4hc,9 rootfs.erofs --tar=rootfs rootfs.tar ``` -Finally, you can package and deploy the compressed EROFS rootfs using the legacy CLI: +Finally, you can package and deploy the compressed EROFS rootfs using the CLI: + + + +```bash title="unikraft" +# first, modify the Kraftfile to use the compressed EROFS rootfs: +# ... +# rootfs: +# source: ./rootfs.erofs +# ... + +# build and deploy the image +unikraft build . --output /node-playwright-chromium:erofs-compressed +unikraft run --metro fra \ + --scale-to-zero policy=on \ + -p 443:8080/http+tls \ + -m 600M \ + --image /node-playwright-chromium:erofs-compressed +``` + +```bash title="kraft" +kraft pkg \ + --plat kraftcloud \ + --arch x86_64 \ + --name index.unikraft.io//node-playwright-chromium:erofs-compressed \ + --rootfs-type erofs \ + --rootfs ./rootfs.erofs \ + --push +kraft cloud vm create \ + --start \ + --scale-to-zero on \ + -p 443:8080/tls+http \ + -M 600Mi \ + index.unikraft.io//node-playwright-chromium:erofs-compressed +``` + + + +## Performance impact + +You can see below the same [`node` image](https://github.com/unikraft-cloud/examples/tree/main/node-playwright-chromium) packaged and deployed four times, with and without compression, and with both CPIO and EROFS formats. +You can view the results in the table below, which show the image size, boot times, and minimum memory requirements for each case. - +To check the image size, you can list your images: + + + +```bash title="unikraft" +unikraft images list +``` ```bash title="kraft" -kraft cloud deploy \ - --rootfs-type erofs \ - --rootfs ./rootfs.erofs \ - ... +kraft cloud image list ``` -## Performance of Compressed rootfs vs. Uncompressed rootfs Images - -To illustrate the performance differences between compressed and uncompressed rootfses, below you can see the same [`node-22` image](https://github.com/unikraft-cloud/examples/tree/main/node-playwright-chromium) packaged and deployed four times: - -1. Uncompressed `CPIO` - ```ansi title="Uncompressed CPIO" - [●] Deployed successfully! - β”‚ - β”œ─────── name: node-playwright-chromium-gjv31 - β”œ─────── uuid: 7ef181a3-8264-4418-82e9-4690ed75a030 - β”œ────── metro: fra - β”œ────── state: running - β”œ───── domain: https://dark-feather-7drf2jvp.fra.unikraft.app - β”œ────── image: demo/node-playwright-chromium@sha256:eafb34e104efe139234f5aa753f3d380afea2c5b78a3011ccd2017a25a5172c0 - β”œ── boot time: 621.28 ms - β”œ───── memory: 2400 MiB - β”œ──── service: dark-feather-7drf2jvp - β”œ─ private ip: 10.0.1.233 - β””─────── args: /usr/bin/wrapper.sh /usr/bin/node /app/server.js - ``` - -1. Compressed `CPIO` - - ```ansi title="Compressed CPIO" - [●] Deployed successfully! - β”‚ - β”œ─────── name: node-playwright-chromium-bt6r9 - β”œ─────── uuid: 1ebc4ee0-2fb1-4272-81d5-7adc76bfb566 - β”œ────── metro: fra - β”œ────── state: running - β”œ───── domain: https://old-cloud-em09h6ue.fra.unikraft.app - β”œ────── image: demo/node-playwright-chromium@sha256:ac6cde1c766162a3c1bc376985c2442eafacd5b18b26b599a25d3095c0a5f5ed - β”œ── boot time: 2079.74 ms - β”œ───── memory: 2400 MiB - β”œ──── service: old-cloud-em09h6ue - β”œ─ private ip: 10.0.1.233 - β””─────── args: /usr/bin/wrapper.sh /usr/bin/node /app/server.js - ``` - -1. Uncompressed `EROFS` - - ```ansi title="Uncompressed EROFS" - [●] Deployed successfully! - β”‚ - β”œ─────── name: node-playwright-chromium-o4ds4 - β”œ─────── uuid: 967b66fd-f71c-4b2b-8982-88e4c0373d02 - β”œ────── metro: fra - β”œ────── state: running - β”œ───── domain: https://damp-water-8ubacykw.fra.unikraft.app - β”œ────── image: cezar.unikraft.io/node-playwright-chromium@sha256:65bf231a557a0dd938592d0fd67f4d5f5e83c0df1ca27b8f8d8675253138ae4b - β”œ── boot time: 121.75 ms - β”œ───── memory: 900 MiB - β”œ──── service: damp-water-8ubacykw - β”œ─ private ip: 10.0.4.1 - β””─────── args: /usr/bin/wrapper.sh /usr/bin/node /app/server.js - ``` - -1. Compressed `EROFS` - - ```ansi title="Compressed EROFS" - [●] Deployed successfully! - β”‚ - β”œ────── name: node-playwright-chromium-9yb9y - β”œ────── uuid: d7255b2c-394c-4578-a21b-2f879f823cd9 - β”œ───── metro: fra - β”œ───── state: running - β”œ──── domain: https://damp-dream-rpmyh0h0.fra.unikraft.app - β”œ───── image: cezar.unikraft.io/node-playwright-chromium@sha256:7a93fbfa414eb958cd4f61b9cbe4874eb4fe6836e74fb292bca3e2927df71a88 - β”œ─ boot time: 92.36 ms - β”œ──── memory: 900 MiB - β”œ─── service: damp-dream-rpmyh0h0 - β”œ private ip: 10.0.3.253 - β””────── args: /usr/bin/wrapper.sh /usr/bin/node /app/server.js - ``` - -### Results - -| rootfs Type | Compression | Image Size | Boot Time | Instance Memory Usage | -|---------------|-------------|------------|------------|-----------------------| -| CPIO | No | 824 MiB | 621.28 ms | 2400 MiB | -| CPIO | Yes (gzip) | 299 MiB | 2079.74 ms | 2400 MiB | -| EROFS | No | 830 MiB | 121.75 ms | 900 MiB | -| EROFS | Yes (lz4hc) | 410 MiB | 92.36 ms | 900 MiB | + +| rootfs Type | Compression | Image Size | Boot Time | Instance Memory Required | +|---------------|-------------|------------|------------|--------------------------| +| CPIO | No | 916 MiB | 1152.08 ms | 2700 MiB | +| CPIO | Yes (gzip) | 400 MiB | 3479.96 ms | 2200 MiB | +| EROFS | No | 924 MiB | 155.39 ms | 1000 MiB | +| EROFS | Yes (lz4hc) | 542 MiB | 122.10 ms | 600 MiB | + +:::note +The image sizes reported in the table also contain the `base-compat` kernel layer, which is around 36MiB. +The instance memory required is the minimum memory required to boot the instance, and respond to a simple `curl` request. +::: As expected from the previous assumptions, the compressed CPIO rootfs boots slower than the uncompressed one. This happens due to the need to decompress the entire archive before use. @@ -171,16 +167,31 @@ EROFS performs better in both compressed and uncompressed forms, compared to CPI The compressed EROFS rootfs boots even faster than the uncompressed one. This is likely due to the reduced amount of data that the system reads from disk, resulting in faster access times despite the decompression overhead. This only happens in the cold boot scenario. -In [warm boots](/features/snapshots), the uncompressed EROFS rootfs tops the charts as the system needs no decompression at all on demand. +In [warm boots](/features/snapshots), the uncompressed EROFS rootfs tops the charts as the system needs no decompression at all, and wakeups happen faster. + +When it comes to memory usage, you can see that the CPIO images require **a lot** more memory to boot. +This is because the system needs to decompress the entire archive in memory before using it, resulting in higher memory usage during boot. +Then, Unikraft Cloud reclaims that memory after boot, but the initial memory overhead is still higher than the uncompressed case. +With EROFS, you don't see this limitation. + +:::tip +For BYOC or on-prem deployments, Unikraft Cloud offers the option to share the same read-only initrd between instances when using EROFS images. +This has the added benefit that Unikraft Cloud loads the initrd into host memory only once as read-only. +Instances only map the pages they need, reducing memory usage and boot times even further. +Upon writing, the system performs copy-on-write, meaning that it copies only the modified pages into the instance's memory. +In this case, the boot time differences between compressed and uncompressed EROFS rootfses are negligible. +There might still be a difference when handling page faults, when accessing parts of the rootfs for the first time. +::: ## Conclusion -As a rule of thumb, use uncompressed rootfses whenever possible and try to always use `EROFS` over `CPIO` for better performance. -If limited by size constraints, try using compressed `EROFS` rootfses with a fast decompression algorithm like `lz4hc`. +As a rule of thumb, use uncompressed rootfses whenever possible and try to always use EROFS over CPIO for better performance. +If limited by size constraints, try using compressed EROFS rootfses with a fast decompression algorithm like `lz4hc`. ## Learn more -* The [rootfs formats tutorial](/tutorials/rootfs-formats) for understanding the differences between `CPIO` and `EROFS`. -* The [`mkfs.erofs` documentation](https://man.archlinux.org/man/extra/erofs-utils/mkfs.erofs.1.en) for more information on creating compressed `EROFS` file systems. +* The [rootfs formats tutorial](/tutorials/rootfs-formats) for understanding the differences between CPIO and EROFS. +* The [`mkfs.erofs` documentation](https://man.archlinux.org/man/extra/erofs-utils/mkfs.erofs.1.en) for more information on creating compressed EROFS file systems. * The [CLI reference](/docs/cli/unikraft) and the [legacy CLI reference](/docs/cli/kraft/overview). * The `kraft pkg` [command reference](https://unikraft.org/docs/cli/reference/kraft/pkg) for packaging images. +* The `Kraftfile` [reference](/kraftfile/v0.7) for specifying rootfs sources and formats. diff --git a/pages/tutorials/rootfs-formats.mdx b/pages/tutorials/rootfs-formats.mdx index 2b51ac47..02961c67 100644 --- a/pages/tutorials/rootfs-formats.mdx +++ b/pages/tutorials/rootfs-formats.mdx @@ -6,7 +6,7 @@ navigation_icon: files Every virtual machine consists of at least two components: a kernel, and a root file system. This tutorial focuses on the latter, the **rootfs** (also known as initrd). -Currently, on the Unikraft Cloud platform, two types of file system archives are usable: `CPIO` (Copy In, Copy Out) and `EROFS` (Enhanced Read-Only File System). +Currently, on the Unikraft Cloud platform, two types of file system archives are usable: CPIO (Copy In, Copy Out) and EROFS (Enhanced Read-Only File System). They're packaging formats used for loading the root file system in the instance. They consist mainly of metadata and file content without any advanced formatting to keep them as simple as possible. The kernel loads and mounts the data from them at boot time. @@ -17,18 +17,19 @@ The main difference between the rootfs formats is performance-wise. ### CPIO -`CPIO` is a simple format which translates in the need to copy and unpack the entire archive into guest memory when starting the instance. +CPIO is a simple format which translates in the need to copy and unpack the entire archive into guest memory when starting the instance. This incurs a double memory usage during boot time, as the archive is first loaded into memory, then unpacked into a ramfs for the kernel to load. At runtime, the guest can reclaim the initial memory allocated for the archive, and will only use the unpacked ramfs. -The boot times will still proportionally increase with the size of the `CPIO` archive. -Another consequence of using a `CPIO` initrd is that [snapshots](/features/snapshots) must include them, since Unikraft Cloud can't track where they're stored in guest memory. +The boot times will still proportionally increase with the size of the CPIO archive. +Another consequence of using a CPIO initrd is that [snapshots](/features/snapshots) must include them, since Unikraft Cloud can't track where they're stored in guest memory. ### EROFS -`EROFS` wins on startup efficiency by not needing to unpack the archive at all into guest memory. -This means that you don't need to accomodate for double the memory usage during boot time, and boot times are faster as well. +EROFS wins on startup efficiency by not needing to unpack the archive at all into guest memory. +This means that you don't need to accommodate double the memory usage during boot time, and boot times are faster as well. -Another significant benefit of `EROFS` (although not available yet on public metros) is that Unikraft Cloud can enable `DAX` (Direct Access) on instances using `EROFS` rootfs. +:::tip +Another significant benefit of EROFS (although *not available yet* on public metros) is that Unikraft Cloud can enable `DAX` (Direct Access) on instances using EROFS rootfs. This means that the host side loads the initrd, and the guest gets a map (**not a copy**) of it into its memory. Thus, boot times aren't affected by the size of the rootfs at all. Memory usage is also drastically reduced at boot, and only increases as the guest accesses files. @@ -36,99 +37,220 @@ This also means that snapshots can exclude the original initrd. Writing to the rootfs is still possible, because Unikraft Cloud uses **overlayfs** to mount the ramfs. Any file changes are present in the snapshot. Finally, `DAX` enables sharing the same rootfs image between many instances, increasing scalability. +::: -## Packaging the Rootfs +## Packaging the rootfs -The legacy `kraft` CLI supports both formats out of the box. +The CLI supports packaging both rootfs formats out of the box. With it, you can package a rootfs from these sources: - a simple file - a directory -- a Dockerfile +- a `Dockerfile` - a remote OCI image If provided as a file, the CLI tool passes the file as-is, without any extra packaging. -Otherwise, you need to package the rootfs into a supported format (either `CPIO` or `EROFS`). -Already packaged archives with external tools (for example `mkfs.erofs`, `cpio`, `bsdcpio`) are also usable. -Right now the default format for the CLI tool is `CPIO`, with `EROFS` being an alternative, as the former is simpler, widely adoped, and better tested. +Otherwise, you need to package the rootfs into a supported format (either CPIO or EROFS). +Already packaged archives with external tools (for example `mkfs.erofs`, CPIO, `bsdcpio`) are also usable. + It's worth mentioning that after unpacking, there is no difference in the produced file contents between the formats. They may only differ metadata-wise, depending on the implementation. -You may observe that a `CPIO` archive is typically smaller on disk than an `EROFS` one. +You may observe that a CPIO archive is typically smaller on disk than an EROFS one. The only effect this has is on the time it takes to push the image. +:::info +The default format for the CLI tool is EROFS, as it provides better performance and lower memory usage at boot time. +The legacy `kraft` CLI defaults to CPIO, but you can specify the format using the `--rootfs-type` flag. +::: + +You can specify the rootfs format in the `Kraftfile`: + + + +```yaml title="EROFS" +# Kraftfile +rootfs: + source: ./Dockerfile + format: erofs +``` + +```yaml title="CPIO" +# Kraftfile +rootfs: + source: ./Dockerfile + format: cpio +``` + + + +The legacy `kraft` CLI also supports overriding the rootfs format when packaging: + +```bash title="kraft" +kraft pkg --rootfs-type erofs ... +``` + +```bash title="kraft" +kraft cloud deploy --rootfs-type erofs ... +``` + Below are two attempts at creating and starting an instance from the same [image](https://github.com/unikraft-cloud/examples/tree/main/node-playwright-chromium), packaged using both formats. +A full guide on using this image is available [here](/guides/node-playwright-chromium). ### EROFS deployment - + + +```bash title="unikraft" +# make sure to specify the erofs format in Kraftfile +# ... +# rootfs: +# source: ./Dockerfile +# format: erofs +# ... +unikraft build . --output /node-playwright-chromium:latest +unikraft run --metro fra \ + -p 443:8080/http+tls \ + -m 1000M \ + --image /node-playwright-chromium:latest +``` ```bash title="kraft" +# the --keep-file-owners flag preserves the file ownership metadata in the EROFS archive, which is required for some workloads kraft cloud deploy \ - -p 443:8080 \ - -M 900Mi \ - --rootfs-type erofs \ - --keep-file-owners \ - demo/node-playwright-chromium + -p 443:8080 \ + -M 1000Mi \ + --rootfs-type erofs \ + --keep-file-owners \ + /node-playwright-chromium \ + . ``` -```ansi title="Create Instance EROFS" + + +```ansi title="unikraft" +metro: fra +name: node-playwright-chromium-4rrxr +uuid: aeff2d28-d718-4068-9e24-4e2bb203022c +state: starting +image: /node-playwright-chromium +resources: + memory: 1000MiB + vcpus: 1 +service: + name: dry-fire-vhmh9z8u + uuid: 28762473-9e16-4921-bee5-e57c9e5dff34 + domains: + - fqdn: dry-fire-vhmh9z8u.fra.unikraft.app +networks: +- uuid: dd2581ff-f7e8-4aac-b7e6-73b1d64c5cb7 + private-ip: 10.0.3.133 + mac: 12:b0:0a:00:00:19 +timestamps: + created: just now +``` + +```ansi title="kraft" [●] Deployed successfully! β”‚ - β”œ────────── name: node-playwright-chromium-q9ynl - β”œ────────── uuid: cb7a1770-6d6a-4f53-a98a-f021ae796313 - β”œ───────── metro: fra - β”œ───────── state: running - β”œ──────── domain: https://proud-dream-udqhbav3.fra.unikraft.app - β”œ───────── image: demo/node-playwright-chromium@sha256:056d5daa3e0f8a91ac18930a9a7714203515a6fae1244b1ecea831f64f593e79 - β”œ───── boot time: 191.55 ms - β”œ──────── memory: 900 MiB - β”œ─────── service: proud-dream-udqhbav3 - β”œ──── private ip: 10.0.3.133 - β””────────── args: /usr/bin/wrapper.sh /usr/bin/node /app/server.js + β”œ───────── name: node-playwright-chromium-q9ynl + β”œ───────── uuid: cb7a1770-6d6a-4f53-a98a-f021ae796313 + β”œ──────── metro: https://api.fra.unikraft.cloud/v1 + β”œ──────── state: running + β”œ─────── domain: https://proud-dream-udqhbav3.fra.unikraft.app + β”œ──────── image: oci://unikraft.io//node-playwright-chromium@sha256:056d5daa3e0f8a91ac18930a9a7714203515a6fae1244b1ecea831f64f593e79 + β”œ─────── memory: 1000 MiB + β”œ────── service: proud-dream-udqhbav3 + β”œ─ private fqdn: node-playwright-chromium-q9ynl.internal + β””─── private ip: 10.0.3.133 ``` + + ### CPIO deployment - +As stated, CPIO requires more memory to boot, so you need to give more than the 1000MiB used in the EROFS case. + + + +```bash title="unikraft" +# make sure to specify the cpio format in Kraftfile +# ... +# rootfs: +# source: ./Dockerfile +# format: cpio +# ... +unikraft build . --output /node-playwright-chromium:latest +unikraft run --metro fra \ + -p 443:8080/http+tls \ + -m 2700M \ + --image /node-playwright-chromium:latest +``` ```bash title="kraft" kraft cloud deploy \ - -p 443:8080 \ - -M 900Mi \ - --rootfs-type cpio \ - demo/node-playwright-chromium + -p 443:8080 \ + -M 2700Mi \ + --rootfs-type cpio \ + /node-playwright-chromium \ + . ``` -```ansi title="Create Instance CPIO" + + +```ansi title="unikraft" +metro: fra +name: node-playwright-chromium-b0nyn +uuid: e4d00b20-4fc9-4cba-a29d-846fa733c358 +state: starting +image: /node-playwright-chromium +resources: + memory: 2700MiB + vcpus: 1 +service: + name: fragrant-dawn-v42h579x + uuid: e9424066-5c71-4142-94ee-44acc5ed3d09 + domains: + - fqdn: fragrant-dawn-v42h579x.fra.unikraft.app +networks: +- uuid: 762a85e2-e6ad-435a-867e-4139a5fda69f + private-ip: 10.0.3.157 + mac: 12:b0:0a:00:00:9d +timestamps: + created: just now +``` + +```ansi title="kraft" [●] Deployed successfully! β”‚ - β”œ────────── name: node-playwright-chromium-4rrxr - β”œ────────── uuid: aeff2d28-d718-4068-9e24-4e2bb203022c - β”œ───────── metro: fra - β”œ───────── state: running - β”œ──────── domain: https://dry-fire-vhmh9z8u.fra.unikraft.app - β”œ───────── image: demo/node-playwright-chromium@sha256:57d1773398bc6d4ff94f063d6e9b7400dca66171d04ce78cd8a45789ba0a7d93 - β”œ───── boot time: 607.34 ms - β”œ──────── memory: 2400 MiB - β”œ─────── service: dry-fire-vhmh9z8u - β”œ──── private ip: 10.0.3.133 - β””────────── args: /usr/bin/wrapper.sh /usr/bin/node /app/server.js + β”œ───────── name: node-playwright-chromium-b0nyn + β”œ───────── uuid: e4d00b20-4fc9-4cba-a29d-846fa733c358 + β”œ──────── metro: https://api.fra.unikraft.cloud/v1 + β”œ──────── state: running + β”œ─────── domain: https://fragrant-dawn-v42h579x.fra.unikraft.app + β”œ──────── image: oci://unikraft.io//node-playwright-chromium@sha256:57d1773398bc6d4ff94f063d6e9b7400dca66171d04ce78cd8a45789ba0a7d93 + β”œ─────── memory: 2700 MiB + β”œ────── service: fragrant-dawn-v42h579x + β”œ─ private fqdn: node-playwright-chromium-b0nyn.internal + β””─── private ip: 10.0.3.157 ``` -The packaged archives have a size of around 750MiB each. -The `EROFS`-packaged instance requires at least 900MiB to work, and the `CPIO` one requires 2400MiB. -Simple arithmetic backs this. -In the `CPIO` case, the kernel gets a copy of the archive in memory and needs to remove it into ramfs. -This extraction process requires double the memory of the archive size. -This means the instance needs at least an extra 750x2=1500MiB of memory compared to `EROFS` to boot. -Finally, related to boot times, both cases have similar [warm boot](/features/snapshots) times, but the extra copies mentioned increase the cold boot time in the `CPIO` case. + ## Conclusion -Bottom-line is, using `EROFS` is recommended in every case where possible, as the upsides definitely outweigh the small downsides. +The packaged archives have a size of around 900MiB each. +This also includes the kernel, which is around 36MiB. +The EROFS-packaged instance requires at least 1000MiB to work, and the CPIO one requires 2700MiB. +Simple arithmetic backs this. +In the CPIO case, the kernel gets a copy of the archive in memory and needs to move it into ramfs. +This extraction process requires double the memory of the archive size. +This means the instance needs at least an extra (900-36)x2=1728MiB of memory compared to EROFS to boot. +Finally, related to boot times, both cases have similar [warm boot](/features/snapshots) times, but the extra copies mentioned increase the cold boot time in the CPIO case. + +Bottom-line is, using EROFS is the go-to in every case where possible, as the upsides definitely outweigh the small downsides. ## Learn more diff --git a/pages/tutorials/rootfs-volumes-roms.mdx b/pages/tutorials/rootfs-volumes-roms.mdx index 8c56d358..8dffd79d 100644 --- a/pages/tutorials/rootfs-volumes-roms.mdx +++ b/pages/tutorials/rootfs-volumes-roms.mdx @@ -21,40 +21,74 @@ At the same time, because the rootfs resides in volatile memory, changes aren't Because of this it's best to put only essential information in there to keep the size down (1-2GB maximum), to save on boot times and on used RAM. For persistent storage, consider using [volumes](/platform/volumes) instead. -A rootfs can be of two types: `CPIO` or `EROFS`. -Each type has its own advantages and disadvantages, but overall `EROFS` is better suited for Unikraft Cloud. +A rootfs can be of two types: CPIO or EROFS. +Each type has its own advantages and disadvantages, but all-around EROFS is better suited for Unikraft Cloud. The [rootfs formats](/tutorials/rootfs-formats) documentation page details both types. -You can create a rootfs with the legacy [`kraft` CLI](https://unikraft.org/docs/cli/install) for both types, for example (legacy CLI only): - - - - CPIO - EROFS - - - ```bash - kraft cloud deploy \ - -p 443:8080 \ - -M 900Mi \ - --rootfs-type cpio \ - demo/node-playwright-chromium - ``` - - - ```bash - kraft cloud deploy \ - -p 443:8080 \ - -M 900Mi \ - --rootfs-type erofs \ - --keep-file-owners \ - demo/node-playwright-chromium - ``` - - +You can create a rootfs with the CLI for both types, for example: + +**CPIO** + + + +```bash title="unikraft" +# make sure to specify the cpio format in Kraftfile +# ... +# rootfs: +# source: ./Dockerfile +# format: cpio +# ... +unikraft build . --output /node-playwright-chromium:latest +unikraft run --metro fra \ + -p 443:8080/http+tls \ + -m 2700M \ + --image /node-playwright-chromium:latest +``` + +```bash title="kraft" +kraft cloud deploy \ + -p 443:8080 \ + -M 2700Mi \ + --rootfs-type cpio \ + /node-playwright-chromium \ + . +``` + + + +**EROFS** + + + +```bash title="unikraft" +# make sure to specify the erofs format in Kraftfile +# ... +# rootfs: +# source: ./Dockerfile +# format: erofs +# ... +unikraft build . --output /node-playwright-chromium:latest +unikraft run --metro fra \ + -p 443:8080/http+tls \ + -m 900M \ + --image /node-playwright-chromium:latest +``` + +```bash title="kraft" +# the --keep-file-owners flag preserves the file ownership metadata in the EROFS archive, which is required for some workloads +kraft cloud deploy \ + -p 443:8080 \ + -M 900Mi \ + --rootfs-type erofs \ + --keep-file-owners \ + /node-playwright-chromium \ + . +``` + + ### Volumes -[Volumes](/platform/volumes) are a non-essential, persistent, large storage component on Unikraft Cloud. +[Volumes](/platform/volumes) are a persistent, large storage component on Unikraft Cloud. You attach volumes to stopped instances or before you create them through the [API](/api/platform/v1/volumes#create-volume). They're automatically mounted in the instance at the specified path either `RW` (Read-Write) or `RO` (Read-Only). They're backed up on SSD (Solid State Drive) disks and offer redundancy without compromising on access latency. @@ -66,24 +100,33 @@ For example, you need to first create a volume: ```bash title="unikraft" -unikraft volumes create \ - --set name=my-volume \ - --set size=100 \ - --set metro=fra +unikraft volumes create --metro fra \ + --name my-volume \ + --size 100 ``` ```bash title="kraft" -kraft cloud volume create --size 100 --name my-volume +kraft cloud volume create \ + --name my-volume \ + --size 100 ``` Then you can optionally populate it with local data: - + + +```bash title="unikraft" +unikraft volume import \ + my-volume \ + --source my-data/ +``` ```bash title="kraft" -kraft cloud volume import --volume my-volume --source my-data/ +kraft cloud volume import \ + --volume my-volume \ + --source my-data/ ``` @@ -93,15 +136,20 @@ Finally, when you deploy an instance you can attach the volume to it: ```bash title="unikraft" -unikraft run --metro=fra -m 512MiB -p 443:8080/http+tls -v my-volume:/mnt --image=my-app:latest +unikraft run --metro fra \ + -m 512M \ + -p 443:8080/http+tls \ + -v my-volume:/mnt \ + --image /my-app:latest ``` ```bash title="kraft" -kraft cloud deploy \ - -M 512 \ - -p 443:8080 \ - --volume my-volume:/mnt \ - . +kraft cloud vm create \ + --start \ + -M 512Mi \ + -p 443:8080 \ + --volume my-volume:/mnt \ + /my-app:latest ``` @@ -109,72 +157,72 @@ kraft cloud deploy \ ### ROMs [ROMs](/features/roms) are an extension of the rootfs concept and a good way to customize generic rootfses for many instances. -They're attached on stopped instances, but aren't mounted automatically on boot as they can have any format (`ext4`, `cpio`, `erofs`, `xfs`, `ntfs`, etc). -They're not persistent and are `RO` (Read-Only), and as such should contain only custom rootfs code/data. +They're attached on stopped instances and Unikraft Cloud can mount them automatically in the instance. +They're read-only and there is a limit in size, and as such should contain only custom rootfs code/data. They're intended to be small, fast, and flexible as opposed to volumes. For example, you can use ROMs in the context of a web server that offers different index pages based on the ROM attached for localization. By using ROMs, you can have a single slim image to which you can attach different ROMs depending on the language you want to serve. Finally, it's considered good practice to keep ROMs small and focused, and the instance shouldn't depend on them to boot correctly. -You need to first package ROMs as an OCI image and push them to the registry. +You could package ROMs as an OCI image and push them to the registry, or you could create them directly from a local directory. They show up as any other image in the registry, but don't have a kernel. -To package them you can either use `kraft`, or package it yourself and pass it to `kraft` for pushing: +To package them you can use the CLI: -```bash title="Kraft Packaging and Pushing" + + +```bash title="unikraft" +# make sure to specify the rom path in the Kraftfile +# ... +# roms: +# - ./fs +# ... +unikraft build . --output /function-hello:latest +``` + +```bash title="kraft" +# you can override the rom path and type with the kraft pkg command kraft pkg \ - --plat kraftcloud \ - --arch x86_64 \ - --name function-hello:latest \ - --rootfs-type erofs \ - --push \ - . + --plat kraftcloud \ + --arch x86_64 \ + --rom ./fs \ + --rom-type erofs \ + --name index.unikraft.io//function-hello:latest \ + --push \ + . ``` -You can then specify ROMs through the API when creating or updating an instance: - - - - Update Instance - Create Instance - - - ```bash title="Updating Instance with ROMs" - curl -X PATCH "$UKC_METRO/instances/$id" \ - -H "Authorization: Bearer $UKC_TOKEN" \ - -d '{ - "prop": "roms", - "op": "add", - "value": [ - { - "name": "function-hello", - "image": "demo/function-hello:latest" - } - ] - }' - ``` - - - ```json title="Creating Instance with ROMs" - ... - "roms": [ - { - "name": "function-hello", - "image": "demo/function-hello:latest" - } - ], - ... - ``` - - + -Finally, in the instance you need to mount them like any other disk and you can find them under `/dev/` with their specified name: +You can then specify ROMs when creating an instance: -```bash title="Mounting ROMs in Instance" -mkdir /mnt/rom -mount -t erofs /dev/function-hello /mnt/rom + + +```bash title="unikraft" +unikraft run --metro fra \ + --rom name=function-hello,image=/function-hello:latest,at=/rom \ + ... ``` +```bash title="API" +curl -X POST \ + -H "Authorization: Bearer $UKC_TOKEN" \ + -H "Content-Type: application/json" \ + https://api.fra.unikraft.cloud/v1/instances \ + -d '[{ + "roms": [ + { + "name": "function-hello", + "image": "/function-hello:latest", + "at": "/rom" + } + ], + ... + }]' +``` + + + ## Comparison | Type / Metric | Rootfses | Volumes | ROMs | @@ -183,9 +231,9 @@ mount -t erofs /dev/function-hello /mnt/rom | I/O Latency | Small | Noticeable | Small | | Storage Medium | RAM | SSD/HDD | RAM | | Intended Size | Medium | (up to) Large | Small | -| File System | `CPIO`/`EROFS` | `ext4`/`virtio-fs` | Any | -| Mounting | Automatic | Automatic | Manual | -| Persistence | No | Yes | No | +| File System | CPIO/EROFS | `ext4`/`virtio-fs` | Any | +| Mounting | Automatic | Automatic | Automatic/Manual | +| Persistence | No | Yes | Yes (as read-only image) | | Mandatory | Yes | No | No | ## Conclusion @@ -199,5 +247,7 @@ ROMs are a flexible way to customize rootfses, but you should also keep them sma * [Volumes](/platform/volumes) and how to use them. * [Rootfs formats](/tutorials/rootfs-formats) and how to create them. +* [ROMs](/features/roms) and how to use them. * The [CLI reference](/docs/cli/unikraft) and the [legacy CLI reference](/docs/cli/kraft/overview). * The `kraft pkg` [command reference](https://unikraft.org/docs/cli/reference/kraft/pkg) for packaging images. +* The `unikraft build` [command reference](/docs/cli/unikraft/build) for building images. diff --git a/pages/tutorials/scale-to-zero-triggers.mdx b/pages/tutorials/scale-to-zero-triggers.mdx index 54d52632..dbdbc3e1 100644 --- a/pages/tutorials/scale-to-zero-triggers.mdx +++ b/pages/tutorials/scale-to-zero-triggers.mdx @@ -21,68 +21,47 @@ You can also configure scale-to-zero to consider idle active connections when de When you enable this trigger, Unikraft Cloud will track incoming connections to your instance. If there is no traffic for the configured duration, the instance scales down to zero. -You can use the legacy CLI tool or the [API](/api/platform/v1/instances#create-instance) to enable this trigger: - - - - Kraftkit - API - - -```bash +You can use the CLI to enable this trigger: + + + +```bash title="unikraft" +unikraft run \ + --scale-to-zero policy=on,cooldown-time=5000 \ + ... + +```bash title="kraft" kraft cloud instance create \ - --scale-to-zero on \ - --scale-to-zero-cooldown 5s \ - ... + --scale-to-zero on \ + --scale-to-zero-cooldown 5s \ + ... ``` - - -```json -{ - ... - "scale_to_zero": { - "policy": "on", - "cooldown_time_ms": "5000 - } -} -``` - - + + ### Idle connections When enabling this trigger, the platform will consider all active connections to your instance, so all traffic going to them. The instance will scale down if you make no new network connections and all existing connections have been idle for the configured duration. -You can enable this trigger using the legacy CLI tool or the [API](/api/platform/v1/instances#create-instance) as follows: +You can enable this trigger using the CLI as follows: + + + +```bash title="unikraft" +unikraft run \ + --scale-to-zero policy=idle,cooldown-time=5000 \ + ... +``` - - - Kraftkit - API - - ```bash kraft cloud instance create \ - --scale-to-zero idle \ - --scale-to-zero-cooldown 5s \ - --scale-to-zero-stateful \ - ... -``` - - -```json -{ - ... - "scale_to_zero": { - "policy": "idle", - "cooldown_time_ms": "5000", - "stateful": true - } -} + --scale-to-zero idle \ + --scale-to-zero-cooldown 5s \ + ... ``` - - + + :::tip If your app uses [keep-alive](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Keep-Alive) messages over this active connection, your instance won't scale down. @@ -112,15 +91,26 @@ export UKC_METRO=fra Then invoke the following `curl` command to scale down an instance named `my-instance`: -```bash -curl -X POST https://api.$UKC_METRO.unikraft.cloud/v1/instances/suspend \ +```bash title="POST /instances/suspend" +curl -X POST https://api.fra.unikraft.cloud/v1/instances/suspend \ -H "Authorization: Bearer $UKC_TOKEN" \ -H "Content-Type: application/json" \ -d '{ - "name": "my-instance" + "name": "my-instance", + "drain_timeout_ms": 10000 }' ``` +You can do the same using the `unikraft` CLI: + +```bash title="unikraft" +unikraft instance suspend my-instance --drain-timeout 10s +``` + +The drain timeout allows you to specify a grace period for existing connections to finish before the instance goes to standby. +If set to `0`, no grace period exists and the instance goes to standby immediately, dropping all existing connections. +If set to `-1` or unset, the platform will wait indefinitely for existing connections to finish before suspending the instance. + ### Device file You can also manually scale down an instance by writing '+'/'-' to a special device file available inside the instance. diff --git a/pages/use-cases/api-gateways.mdx b/pages/use-cases/api-gateways.mdx index 5bad7636..506d6c0d 100644 --- a/pages/use-cases/api-gateways.mdx +++ b/pages/use-cases/api-gateways.mdx @@ -56,23 +56,36 @@ Ultra-fast, cost-efficient, and scale-to-zeroβ€”so your API edge is always ready This guide shows you how to use [Tyk](https://tyk.io/), a universal API management tool for Representational State Transfer (REST), GraphQL, gRPC and async APIs. It offers open standards, scalability, governance, and AI-ready features for API development, security, and performance. +Often, you deploy Tyk alongside a caching layer like Redis to store API tokens and OAuth clients. To run it, follow these steps: -1. Install the [`kraft` CLI tool](https://unikraft.org/docs/cli/install) and a container runtime engine (for example, [Docker](https://docs.docker.com/engine/install/)). +1. Install the CLI. + Use the [unikraft CLI](/docs/cli/unikraft) or the legacy [kraft CLI](https://unikraft.org/docs/cli/install). + You need a [BuildKit](https://github.com/moby/buildkit) builder. The easiest way to get one is via [Docker](https://docs.docker.com/engine/install/). + You can also set up and use BuildKit directly; see the [quick start](https://github.com/moby/buildkit#quick-start). -1. Clone the [`examples` repository](https://github.com/unikraft-cloud/examples) and `cd` into the `examples/tyk/` directory: + :::note + The unikraft CLI is the current standard, while kraft is the legacy version. + Choose one of the CLIs below and only run the commands associated with it for the rest of this guide. + ::: -```bash title="" -git clone https://github.com/unikraft-cloud/examples -cd examples/tyk/ -``` +2. Clone the [`examples` repository](https://github.com/unikraft-cloud/examples) and `cd` into the `examples/tyk/` directory: + + ```bash title="" + git clone https://github.com/unikraft-cloud/examples + cd examples/tyk/ + ``` Make sure to log into Unikraft Cloud and pick a [metro](/platform/metros) close to you. This guide uses `fra` (Frankfurt, πŸ‡©πŸ‡ͺ): +```bash title="unikraft" +unikraft login +``` + ```bash title="kraft" # Set Unikraft Cloud access token export UKC_TOKEN=token @@ -82,43 +95,221 @@ export UKC_METRO=fra -Then invoke: +## Redis + +The `REDIS_PASSWORD` environment variable sets the Redis `requirepass` directive. +If not provided, it defaults to `unikraft`. +Build and deploy the Redis instance (used internally by Tyk): + + + +```bash title="unikraft" +unikraft build ./redis --output /redis:latest +unikraft run --metro fra \ + -m 256M \ + --scale-to-zero policy=idle,cooldown-time=1000,stateful=true \ + --domain tyk-redis.internal \ + -e REDIS_PASSWORD=unikraft + --image /redis:latest \ +``` + +```bash title="kraft" +kraft cloud deploy \ + -M 256Mi \ + --scale-to-zero idle \ + --scale-to-zero-cooldown 1s \ + --scale-to-zero-stateful \ + --domain tyk-redis.internal \ + -e REDIS_PASSWORD=unikraft \ + ./redis/ +``` + + + +The output shows the Redis instance details: + + + +```ansi title="unikraft" +metro: fra +name: redis-6vgvc +uuid: 63b86d17-06ca-4f95-b921-56e5b3245554 +state: starting +image: /redis +resources: + memory: 256MiB + vcpus: 1 +service: + name: snowy-water-wivk1i4r + uuid: 6612de91-d639-4b5a-95d1-b7ae6e91e3c1 + domains: + - fqdn: tyk-redis.internal +networks: +- uuid: 19e4b80a-9501-448d-99a6-ec3f7b90805e + private-ip: 10.0.0.29 + mac: 12:b0:0a:00:01:49 +timestamps: + created: just now +scale-to-zero: + enabled: true + policy: idle + stateful: true + cooldown-time: 1s +``` + +```ansi title="kraft" +[●] Deployed successfully! + β”‚ + β”œ───────── name: redis-6vgvc + β”œ───────── uuid: 63b86d17-06ca-4f95-b921-56e5b3245554 + β”œ──────── metro: https://api.fra.unikraft.cloud/v1 + β”œ──────── state: starting + β”œ─────── domain: tyk-redis.internal + β”œ──────── image: oci://unikraft.io//redis@sha256:933b8b7714924eb2de880e0f32792698b14a13c83d5aee0f52dddcab5c97099d + β”œ─────── memory: 256 MiB + β”œ────── service: snowy-water-wivk1i4r + β”œ─ private fqdn: redis-6vgvc.internal + β””─── private ip: 10.0.0.29 +``` + + + +## Tyk + +Build and deploy the Tyk instance: + + + +```bash title="unikraft" +unikraft build ./tyk --output /tyk:latest +unikraft run --metro fra \ + -m 256M \ + -p 443:8080/tls+http \ + --scale-to-zero policy=on,cooldown-time=1000 \ + --image /tyk:latest +``` + +```bash title="kraft" +kraft cloud deploy \ + -M 256Mi \ + -p 443:8080/tls+http \ + --scale-to-zero on \ + --scale-to-zero-cooldown 1s \ + ./tyk/ +``` + + + +The output shows the Tyk instance details: - + + +```ansi title="unikraft" +metro: fra +name: tyk-s9ixd +uuid: 4e8a5e56-2d0b-4ca4-88b4-aa816129a66d +state: starting +image: /tyk +resources: + memory: 256MiB + vcpus: 1 +service: + name: icy-haze-8ph4u8cz + uuid: 89079646-353a-4a19-99ac-5498c7d626ad + domains: + - fqdn: icy-haze-8ph4u8cz.fra.unikraft.app +networks: +- uuid: 8085804f-0fe0-4847-ad6e-8518edba126e + private-ip: 10.0.0.1 + mac: 12:b0:0a:00:0e:b1 +timestamps: + created: just now +``` + +```ansi title="kraft" +[●] Deployed successfully! + β”‚ + β”œ───────── name: tyk-s9ixd + β”œ───────── uuid: 4e8a5e56-2d0b-4ca4-88b4-aa816129a66d + β”œ──────── metro: https://api.fra.unikraft.cloud/v1 + β”œ──────── state: starting + β”œ─────── domain: https://icy-haze-8ph4u8cz.fra.unikraft.app + β”œ──────── image: oci://unikraft.io//tyk@sha256:4954033ada90f980f279e5d825dd7971111a429578ce38be764893ba0d1f358d + β”œ─────── memory: 256 MiB + β”œ────── service: icy-haze-8ph4u8cz + β”œ─ private fqdn: tyk-s9ixd.internal + β””─── private ip: 10.0.0.1 +``` + + + +In this case, the instance names are `redis-6vgvc` and `tyk-s9ixd`, and the Tyk address is `https://icy-haze-8ph4u8cz.fra.unikraft.app`. +They're different for each run. + +Use `curl` to query the Tyk instance on Unikraft Cloud: + +```bash +curl https://icy-haze-8ph4u8cz.fra.unikraft.app/hello +``` + +```text title="" +{"status":"pass","version":"v5.3.0-dev","description":"Tyk GW","details":{"redis":{"status":"pass","componentType":"datastore","time":"2026-05-25T12:26:07Z"}}} +``` + +You can list information about the instances by running: + + + +```bash title="unikraft" +unikraft instances list +``` ```bash title="kraft" -kraft cloud compose up +kraft cloud instance list ``` -After deploying, you can query the service using the provided address. -Use the `/hello` path after the address, such as below: + + +```ansi title="unikraft" +METRO NAME STATE IMAGE MEMORY VCPUS FQDN CREATED +fra tyk-s9ixd standby /tyk 256MiB 1 icy-haze-8ph4u8cz.fra.unikraft.app just now +fra redis-6vgvc running /redis 256MiB 1 tyk-redis.internal 1 minute ago +``` -```bash title="" -curl https:///hello | jq +```ansi title="kraft" +NAME FQDN STATE STATUS IMAGE MEMORY VCPUS ARGS BOOT TIME +tyk-s9ixd icy-haze-8ph4u8cz.fra.unikraft.app standby standby oci://unikraft.io//tyk@sha256:4954033... 256 MiB 1 158.32 ms +redis-6vgvc tyk-redis.internal running since 1min oci://unikraft.io//redis@sha256:933b8... 256 MiB 1 1811.99 ms ``` -```json title="" -{ - "status": "pass", - "version": "v5.3.0-dev", - "description": "Tyk GW", - "details": { - "redis": { - "status": "pass", - "componentType": "datastore", - "time": "2024-04-30T10:37:29Z" - } - } -} + + + +When done, you can remove the instances: + + + +```bash title="unikraft" +unikraft instances delete redis-6vgvc tyk-s9ixd ``` +```bash title="kraft" +kraft cloud instance remove redis-6vgvc tyk-s9ixd +``` + + + ## Learn more Use the `--help` option for detailed information on using Unikraft Cloud: +```bash title="unikraft" +unikraft --help +``` + ```bash title="kraft" kraft cloud --help ``` @@ -126,3 +317,7 @@ kraft cloud --help Or visit the [CLI Reference](/docs/cli/unikraft) or the [legacy CLI reference](/docs/cli/kraft/overview). +For more references, check out also: + +* [Tyk](/guides/tyk) on Unikraft Cloud interactive guide. +* [Redis](/guides/redis7.2) on Unikraft Cloud interactive guide. diff --git a/pages/use-cases/build-test-environments.mdx b/pages/use-cases/build-test-environments.mdx index 0c349a88..22077b9d 100644 --- a/pages/use-cases/build-test-environments.mdx +++ b/pages/use-cases/build-test-environments.mdx @@ -48,16 +48,22 @@ The base image contains a Go server (`server.go`) that: ### Prerequisites -1. Install the CLI: +1. Install the CLI. Use the [unikraft CLI](/docs/cli/unikraft) or the legacy [kraft CLI](https://unikraft.org/docs/cli/install). - You need a [BuildKit](https://github.com/moby/buildkit) builder. The easiest way is via [Docker](https://docs.docker.com/engine/install/). + You need a [BuildKit](https://github.com/moby/buildkit) builder. The easiest way to get one is via [Docker](https://docs.docker.com/engine/install/). + You can also set up and use BuildKit directly; see the [quick start](https://github.com/moby/buildkit#quick-start). + + :::note + The unikraft CLI is the current standard, while kraft is the legacy version. + Choose one of the CLIs below and only run the commands associated with it for the rest of this guide. + ::: 1. Clone the examples repository and enter the example directory: -```bash -git clone https://github.com/unikraft-cloud/examples -cd examples/build-environments/ -``` + ```bash + git clone https://github.com/unikraft-cloud/examples + cd examples/build-environments/ + ``` Make sure to log into Unikraft Cloud and pick a [metro](/platform/metros) close to you. This guide uses `fra` (Frankfurt, πŸ‡©πŸ‡ͺ): @@ -88,12 +94,12 @@ unikraft build . --output /go-build-env:latest ```bash title="kraft" kraft pkg \ - --name index.unikraft.io//go-build-env:latest \ - --plat kraftcloud \ - --arch x86_64 \ - --rootfs-type erofs \ - --push \ - . + --name index.unikraft.io//go-build-env:latest \ + --plat kraftcloud \ + --arch x86_64 \ + --rootfs-type erofs \ + --push \ + . ``` @@ -204,21 +210,19 @@ unikraft build rom2/ --output /go-rom2:latest ```bash title="kraft" kraft pkg \ - --rom ./fs \ - --rom-type erofs \ - --plat kraftcloud \ - --arch x86_64 \ - --name index.unikraft.io//go-rom1:latest \ - --push \ - rom1/ + --plat kraftcloud \ + --arch x86_64 \ + --name index.unikraft.io//go-rom1:latest \ + --rom-type erofs \ + --push \ + rom1/ kraft pkg \ - --rom ./fs \ - --rom-type erofs \ - --plat kraftcloud \ - --arch x86_64 \ - --name index.unikraft.io//go-rom2:latest \ - --push \ - rom2/ + --plat kraftcloud \ + --arch x86_64 \ + --name index.unikraft.io//go-rom2:latest \ + --rom-type erofs \ + --push \ + rom2/ ``` @@ -238,40 +242,39 @@ unikraft run --metro fra \ --template go-build-env ``` -```bash title="kraft" -# kraft does not support creating instances with attached ROMs, but you can use the API directly -curl -X POST "$UKC_METRO/instances" \ - -H "Accept: application/json" \ - -H "Authorization: Bearer $UKC_TOKEN" \ - -H "Content-Type: application/json" \ - -d '{ - "name": "go-build-env-rom1", - "template": { +```bash title="API" +curl -X POST "https://api.fra.unikraft.cloud/v1/instances" \ + -H "Accept: application/json" \ + -H "Authorization: Bearer $UKC_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "go-build-env-rom1", + "template": { "name": "go-build-env" - }, - "autostart": true, - "service_group": { + }, + "autostart": true, + "service_group": { "services": [ - { - "port": 443, - "destination_port": 8080, - "handlers": ["tls", "http"] - } + { + "port": 443, + "destination_port": 8080, + "handlers": ["tls", "http"] + } ] - }, - "scale_to_zero": { + }, + "scale_to_zero": { "policy": "on", "stateful": true, "cooldown_time_ms": 1000 - }, - "roms": [ + }, + "roms": [ { - "name": "go_function", - "image": "index.unikraft.io//go-rom1:latest", - "at": "/rom" + "name": "go_function", + "image": "index.unikraft.io//go-rom1:latest", + "at": "/rom" } - ] -}' + ] + }' ``` @@ -304,40 +307,39 @@ unikraft run --metro fra \ --template go-build-env ``` -```bash title="kraft" -# kraft does not support creating instances with attached ROMs, but you can use the API directly -curl -X POST "$UKC_METRO/instances" \ - -H "Accept: application/json" \ - -H "Authorization: Bearer $UKC_TOKEN" \ - -H "Content-Type: application/json" \ - -d '{ - "name": "go-build-env-rom2", - "template": { +```bash title="API" +curl -X POST "https://api.fra.unikraft.cloud/v1/instances" \ + -H "Accept: application/json" \ + -H "Authorization: Bearer $UKC_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "go-build-env-rom2", + "template": { "name": "go-build-env" - }, - "autostart": true, - "service_group": { + }, + "autostart": true, + "service_group": { "services": [ - { - "port": 443, - "destination_port": 8080, - "handlers": ["tls", "http"] - } + { + "port": 443, + "destination_port": 8080, + "handlers": ["tls", "http"] + } ] - }, - "scale_to_zero": { + }, + "scale_to_zero": { "policy": "on", "stateful": true, "cooldown_time_ms": 1000 - }, - "roms": [ + }, + "roms": [ { - "name": "go_function", - "image": "index.unikraft.io//go-rom2:latest", - "at": "/rom" + "name": "go_function", + "image": "index.unikraft.io//go-rom2:latest", + "at": "/rom" } - ] -}' + ] + }' ``` diff --git a/pages/use-cases/game-servers.mdx b/pages/use-cases/game-servers.mdx index 733d6884..a39bef43 100644 --- a/pages/use-cases/game-servers.mdx +++ b/pages/use-cases/game-servers.mdx @@ -42,37 +42,68 @@ The base image is built from [itzg/minecraft-server](https://hub.docker.com/r/it ### Prerequisites -1. Install the [unikraft CLI](/docs/cli/unikraft). +1. Install the CLI. + Use the [unikraft CLI](/docs/cli/unikraft) or the legacy [kraft CLI](https://unikraft.org/docs/cli/install). You need a [BuildKit](https://github.com/moby/buildkit) builder. The easiest way to get one is via [Docker](https://docs.docker.com/engine/install/). + You can also set up and use BuildKit directly; see the [quick start](https://github.com/moby/buildkit#quick-start). + + :::note + The unikraft CLI is the current standard, while kraft is the legacy version. + Choose one of the CLIs below and only run the commands associated with it for the rest of this guide. + ::: 2. Clone the [`examples` repository](https://github.com/unikraft-cloud/examples) and `cd` into the `examples/minecraft/` directory: -```bash -git clone https://github.com/unikraft-cloud/examples -cd examples/minecraft/ -``` + ```bash + git clone https://github.com/unikraft-cloud/examples + cd examples/minecraft/ + ``` + +3. Review and adjust the base server settings in [`base/.env`](https://github.com/unikraft-cloud/examples/tree/main/minecraft/base/.env). + You can check out [this documentation](https://docker-minecraft-server.readthedocs.io/en/latest/) for available configuration options. + Make sure to also set your `PUBKEY` for SSH access, and optionally set `TEMPLATE_WITH_WORLD` if you want the template to include the world (see below). + Optionally, create per-config overrides in `/.env`. + All `.env` files are packaged as [auxiliary ROMs](/features/roms) and mounted at `/rom/`. Make sure to log into Unikraft Cloud and pick a [metro](/platform/metros) close to you. This guide uses `fra` (Frankfurt, πŸ‡©πŸ‡ͺ): + + ```bash title="unikraft" unikraft login ``` -3. Review and adjust the base server settings in [`base/.env`](https://github.com/unikraft-cloud/examples/tree/main/minecraft/base/.env). - You can check out [this documentation](https://docker-minecraft-server.readthedocs.io/en/latest/) for available configuration options. - Make sure to also set your `PUBKEY` for SSH access, and optionally set `TEMPLATE_WITH_WORLD` if you want the template to include the world (see below). - Optionally, create per-config overrides in `/.env`. - All `.env` files are packaged as [auxiliary ROMs](/features/roms) and mounted at `/rom/`. +```bash title="kraft" +# Set Unikraft Cloud access token +export UKC_TOKEN=token +export UKC_METRO=fra +``` + + ### Step 1: Package and push the base image First, package and push the base Minecraft server image: + + ```bash title="unikraft" unikraft build . --output /minecraft:latest ``` +```bash title="kraft" +kraft pkg \ + --name index.unikraft.io//minecraft:latest \ + --plat kraftcloud \ + --arch x86_64 \ + --rootfs-type erofs \ + --push \ + . +``` + + + The image contains the files from the Docker image [itzg/minecraft-server](https://hub.docker.com/r/itzg/minecraft-server) and includes a few tweaks: - The entrypoint uses a custom `wrapper.sh` script that: @@ -89,17 +120,28 @@ The startup script snapshots it into a template before world generation, then th Pass your server settings as a ROM: + + ```bash title="unikraft" unikraft run --metro fra \ - --name minecraft-tpl \ - -m 4096M \ - --vcpus 4 \ - --image /minecraft:latest \ - --rom dir=base,at=/rom/base + --name minecraft-tpl \ + -m 4096M \ + --vcpus 4 \ + --image /minecraft:latest \ + --rom dir=base,at=/rom/base +``` + +```bash title="kraft" +# kraft does not support creating instances with attached ROMs +# use unikraft CLI ``` + + The output shows the instance details: + + ```ansi title="unikraft" metro: fra name: minecraft-tpl @@ -121,18 +163,34 @@ timestamps: created: just now ``` +```bash title="kraft" +# kraft does not support creating instances with attached ROMs +# use unikraft CLI +``` + + + If you also have per-config overrides (for example in `bingo/.env`), create the instance with more ROMs: + + ```bash title="unikraft" unikraft run --metro fra \ - --name minecraft-tpl \ - -m 4096M \ - --vcpus 4 \ - --image /minecraft:latest \ - --rom dir=base,at=/rom/base \ - --rom dir=bingo,at=/rom/bingo + --name minecraft-tpl \ + -m 4096M \ + --vcpus 4 \ + --image /minecraft:latest \ + --rom dir=base,at=/rom/base \ + --rom dir=bingo,at=/rom/bingo +``` + +```bash title="kraft" +# kraft does not support creating instances with attached ROMs +# use unikraft CLI ``` + + The instance runs until initialization completes, then the platform snapshots it as a template and immediately deletes it. You can configure the exact moment of snapshotting in [`patches/start-finalExec`](https://github.com/unikraft-cloud/examples/tree/main/minecraft/patches/start-finalExec) or with the following environment variable: @@ -141,34 +199,70 @@ You can configure the exact moment of snapshotting in [`patches/start-finalExec` Follow the template instance logs until they stop: + + ```bash title="unikraft" unikraft instances logs minecraft-tpl -f ``` +```bash title="kraft" +kraft cloud instance logs minecraft-tpl -f +``` + + + Confirm the template is ready: + + ```bash title="unikraft" unikraft instances templates list ``` +```bash title="kraft" +kraft cloud instance template list +``` + + + + + ```ansi title="unikraft" METRO NAME STATE IMAGE ARGS MEMORY VCPUS CREATED fra minecraft-tpl template /minecraft 4GiB 4 5 seconds ago ``` +```ansi title="kraft" +NAME IMAGE ARGS CREATED AT +minecraft-tpl oci://unikraft.io//minecraft@sha256:fdb4887e84362ebbaf54c713e0d85f547e8ee173fe63a6ab39e94b7e612a9892 5 seconds ago +``` + + + ### Step 3: Launch a server from the template + + ```bash title="unikraft" unikraft run --metro fra \ - --name minecraft \ - -p 2222:2222/tls \ - -p 25565:25565/tls \ - --scale-to-zero policy=on,cooldown-time=5000,stateful=true \ - --template minecraft-tpl + --name minecraft \ + -p 2222:2222/tls \ + -p 25565:25565/tls \ + --scale-to-zero policy=on,cooldown-time=5000,stateful=true \ + --template minecraft-tpl ``` +```bash title="kraft" +# kraft does not support creating instances from templates +# use unikraft CLI +``` + + + The output includes the service address: + + ```ansi title="unikraft" metro: fra name: minecraft @@ -192,14 +286,29 @@ timestamps: scale-to-zero: policy=on,stateful=true,cooldown-time=5s ``` +```bash title="kraft" +# kraft does not support creating instances from templates +# use unikraft CLI +``` + + + The instance address is `https://hidden-water-ewr8l9sp.fra.unikraft.app`. Follow the logs until the server is ready: + + ```bash title="unikraft" unikraft instances logs minecraft -f ``` -```text +```bash title="kraft" +kraft cloud instance logs minecraft -f +``` + + + +```ansi [12:59:45] [Server thread/INFO]: Preparing spawn area: 100% [12:59:45] [Server thread/INFO]: Time elapsed: 3641 ms [12:59:45] [Server thread/INFO]: Done (3.850s)! For help, type "help" @@ -217,7 +326,7 @@ unikraft instances logs minecraft -f ## Connect to the server -:::caution[**DISCLAIMER**] +:::caution[**Limited Access**] At the moment, Unikraft Cloud exposes services over TLS. Minecraft and SSH clients speak plain TCP, so use `socat` to end TLS locally. ::: @@ -250,7 +359,7 @@ rcon-cli --host 127.0.0.1 Example commands: -```text +```text title="" say Hello from Unikraft Cloud! /op ``` @@ -266,18 +375,37 @@ import hashlib, uuid; h = hashlib.md5(b'OfflinePlayer:').digest(); u = ## Cleanup + + ```bash title="unikraft" unikraft instances delete minecraft unikraft instances template delete minecraft-tpl ``` +```bash title="kraft" +kraft cloud instance remove minecraft +kraft cloud instance template remove minecraft-tpl +``` + + + ## Learn more +Use the `--help` option for detailed information on using Unikraft Cloud: + + + ```bash title="unikraft" unikraft --help ``` -Or visit the [CLI Reference](/docs/cli/unikraft). +```bash title="kraft" +kraft cloud --help +``` + + + +Or visit the [CLI Reference](/docs/cli/unikraft) or the [legacy CLI reference](/docs/cli/kraft/overview). For more information on the features used in this use case and how to use them, check out the following documentation: diff --git a/pages/use-cases/headless-browsers.mdx b/pages/use-cases/headless-browsers.mdx index 98a28562..289f8560 100644 --- a/pages/use-cases/headless-browsers.mdx +++ b/pages/use-cases/headless-browsers.mdx @@ -50,15 +50,22 @@ option to run them headless (no UI). To run it, follow these steps: -1. Install the CLI and a container runtime engine (for example, [Docker](https://docs.docker.com/engine/install/)). +1. Install the CLI. Use the [unikraft CLI](/docs/cli/unikraft) or the legacy [kraft CLI](https://unikraft.org/docs/cli/install). + You need a [BuildKit](https://github.com/moby/buildkit) builder. The easiest way to get one is via [Docker](https://docs.docker.com/engine/install/). + You can also set up and use BuildKit directly; see the [quick start](https://github.com/moby/buildkit#quick-start). + + :::note + The unikraft CLI is the current standard, while kraft is the legacy version. + Choose one of the CLIs below and only run the commands associated with it for the rest of this guide. + ::: 1. Clone the [`examples` repository](https://github.com/unikraft-cloud/examples) and `cd` into the `examples/node-express-puppeteer/` directory: -```bash -git clone https://github.com/unikraft-cloud/examples -cd examples/node-express-puppeteer/ -``` + ```bash + git clone https://github.com/unikraft-cloud/examples + cd examples/node-express-puppeteer/ + ``` Make sure to log into Unikraft Cloud and pick a [metro](/platform/metros) close to you. This guide uses `fra` (Frankfurt, πŸ‡©πŸ‡ͺ): @@ -78,13 +85,9 @@ export UKC_METRO=fra -The `UKC_TOKEN` and `UKC_METRO` environment variables are only supported by the legacy CLI. - -:::note - +:::caution A Puppeteer instance on Unikraft Cloud requires 4GB to run. -Request an increase in the instance memory quota when you need more memory. - +Request an increase in the instance memory quota if you need more memory. ::: When done, invoke the following command to deploy this app on Unikraft Cloud: @@ -92,80 +95,125 @@ When done, invoke the following command to deploy this app on Unikraft Cloud: ```bash title="unikraft" -unikraft build . --output /node-express-puppeteer:latest -unikraft run --metro=fra -p 443:3000/http+tls -m 4Gi --image=/node-express-puppeteer:latest +unikraft build . --output /httpserver-node-express-puppeteer:latest +unikraft run --metro fra \ + -p 443:3000/tls+http \ + -m 4G \ + --scale-to-zero policy=idle,cooldown-time=1000,stateful=true \ + --image /httpserver-node-express-puppeteer:latest ``` ```bash title="kraft" -kraft cloud deploy -p 443:3000 -M 4Gi . +kraft cloud deploy \ + -M 4Gi \ + -p 443:3000/tls+http \ + --scale-to-zero idle \ + --scale-to-zero-cooldown 1s \ + --scale-to-zero-stateful \ + . ``` The output shows the instance address and other details: -```ansi + + +```ansi title="unikraft" +metro: fra +name: httpserver-node-express-puppeteer-7afg3 +uuid: 7bb479d7-5b3e-444f-b07c-eae4da6f57cc +state: starting +image: /httpserver-node-express-puppeteer +resources: + memory: 4096MiB + vcpus: 1 +service: + uuid: 996b9cc1-5a51-e707-d443-5c98ea86ded8 + name: little-snow-7qwu6vv5 + domains: + - fqdn: little-snow-7qwu6vv5.fra.unikraft.app +networks: +- uuid: 034fa25e-9154-7842-ccdd-289256cc7a17 + private-ip: 10.0.3.1 + mac: 12:b0:8f:3c:f5:16 +timestamps: + created: just now +``` + +```ansi title="kraft" [●] Deployed successfully! β”‚ - β”œ────────── name: node-express-puppeteer-7afg3 - β”œ────────── uuid: 7bb479d7-5b3e-444f-b07c-eae4da6f57cc - β”œ───────── state: starting - β”œ──────── domain: https://nameless-fog-0tvh1uov.fra.unikraft.app - β”œ───────── image: node-express-puppeteer@sha256:78d0b180161c876f17d05116b93011ddcd44c76758d6fa0359f05938e67cea65 - β”œ──────── memory: 4096 MiB - β”œ─────── service: little-snow-7qwu6vv5 - β”œ── private fqdn: node-express-puppeteer-7afg3.internal - β”œ──── private ip: 172.16.3.1 - β””────────── args: /usr/bin/wrapper.sh /usr/bin/node /app/bin/www + β”œ───────── name: httpserver-node-express-puppeteer-7afg3 + β”œ───────── uuid: 7bb479d7-5b3e-444f-b07c-eae4da6f57cc + β”œ──────── metro: https://api.fra.unikraft.cloud/v1 + β”œ──────── state: starting + β”œ─────── domain: https://nameless-fog-0tvh1uov.fra.unikraft.app + β”œ──────── image: oci://unikraft.io//httpserver-node-express-puppeteer@sha256:78d0b180161c876f17d05116b93011ddcd44c76758d6fa0359f05938e67cea65 + β”œ─────── memory: 4096 MiB + β”œ────── service: little-snow-7qwu6vv5 + β”œ─ private fqdn: httpserver-node-express-puppeteer-7afg3.internal + β””─── private ip: 10.0.3.1 ``` -In this case, the instance name is `node-express-puppeteer-7afg3`. + + +In this case, the instance name is `httpserver-node-express-puppeteer-7afg3`. They're different for each run. Use a browser to access the landing page of the Puppeteer (that uses [ExpressJS](https://expressjs.com/)). The app and the landing page are part of [this repository](https://github.com/christopher-talke/node-express-puppeteer-pdf-example). -In the example run above, the landing page is at the address `https://nameless-fog-0tvh1uov.fra.unikraft.app`. +In the example run above the landing page is at `https://nameless-fog-0tvh1uov.fra.unikraft.app`. You can use the landing page to generate the PDF version of a remote page. -At any time, you can list information about the instance: +You can list information about the instance by running: ```bash title="unikraft" -unikraft instances list node-express-puppeteer-7afg3 +unikraft instances list ``` ```bash title="kraft" -kraft cloud instance list node-express-puppeteer-7afg3 +kraft cloud instance list ``` -```text -NAME FQDN STATE STATUS IMAGE MEMORY ARGS BOOT TIME -node-express-puppeteer-7afg3 node-express-puppeteer-7afg3.fra.unikraft.app running since 6mins node-express-puppeteer-7afg3@s... 4.0 GiB /usr/bin/wrapper.sh /usr/bin/n... 15.27 ms + + +```ansi title="unikraft" +METRO NAME STATE IMAGE ARGS MEMORY VCPUS FQDN CREATED +fra httpserver-node-express-puppeteer-7afg3 running /httpserver-node-express-puppeteer 4096MiB 1 nameless-fog-0tvh1uov.fra.unikraft.app 2 minutes ago ``` +```ansi title="kraft" +NAME FQDN STATE STATUS IMAGE MEMORY VCPUS ARGS BOOT TIME +httpserver-node-express-puppeteer-7afg3 nameless-fog-0tvh1uov.fra.unikraft.app running since 6mins oci://unikraft.io//httpserver-node-express-puppeteer@s... 4.0 GiB 1 15.27 ms +``` + + + When done, you can remove the instance: ```bash title="unikraft" -unikraft instances delete node-express-puppeteer-7afg3 +unikraft instances delete httpserver-node-express-puppeteer-7afg3 ``` ```bash title="kraft" -kraft cloud instance remove node-express-puppeteer-7afg3 +kraft cloud instance remove httpserver-node-express-puppeteer-7afg3 ``` -### Customize your deployment +## Customize your deployment The current deployment uses an ExpressJS service that uses the [PDF generating functionality of Puppeteer](https://devdocs.io/puppeteer/). Customizing the deployment means updating the service, such as adding new functionalities provided by Puppeteer. -You can update the service itself to provide a Representational State Transfer (REST)-like interface. +You can update the service to provide a REST-like interface. ## Learn more diff --git a/pages/use-cases/mcp-servers.mdx b/pages/use-cases/mcp-servers.mdx index 4d5903d5..8216b524 100644 --- a/pages/use-cases/mcp-servers.mdx +++ b/pages/use-cases/mcp-servers.mdx @@ -61,25 +61,41 @@ It allows MCP clients to browse and analyze papers. To run an example based on it, follow these steps: -1. Install the [`kraft` CLI tool](https://unikraft.org/docs/cli/install) and a container runtime engine (for example, [Docker](https://docs.docker.com/engine/install/)). +1. Install the CLI. + Use the [unikraft CLI](/docs/cli/unikraft) or the legacy [kraft CLI](https://unikraft.org/docs/cli/install). + You need a [BuildKit](https://github.com/moby/buildkit) builder. The easiest way to get one is via [Docker](https://docs.docker.com/engine/install/). + You can also set up and use BuildKit directly; see the [quick start](https://github.com/moby/buildkit#quick-start). + + :::note + The unikraft CLI is the current standard, while kraft is the legacy version. + Choose one of the CLIs below and only run the commands associated with it for the rest of this guide. + ::: 1. Clone the [`examples` repository](https://github.com/unikraft-cloud/examples) and `cd` into the `examples/mcp-server-arxiv/` directory: -```bash -git clone https://github.com/unikraft-cloud/examples -cd examples/mcp-server-arxiv/ -``` + ```bash + git clone https://github.com/unikraft-cloud/examples + cd examples/mcp-server-arxiv/ + ``` -Make sure to log into Unikraft Cloud by setting your token and a [metro](/platform/metros) close to you. +Make sure to log into Unikraft Cloud and set your token and a [metro](/platform/metros) close to you. This guide uses `fra` (Frankfurt, πŸ‡©πŸ‡ͺ): -```bash + + +```bash title="unikraft" +unikraft login +``` + +```bash title="kraft" # Set Unikraft Cloud access token export UKC_TOKEN=token # Set metro to Frankfurt, DE export UKC_METRO=fra ``` + + Follow the instructions in the example's README to set it up and grab the server's FQDN. You can use the included example client to test the server, or connect to it using your favourite MCP client. @@ -127,22 +143,41 @@ This shows the core patterns for building your own MCP tools. To run it, follow these steps: -1. Install the [`kraft` CLI tool](https://unikraft.org/docs/cli/install) and a container runtime engine (for example, [Docker](https://docs.docker.com/engine/install/)). +1. Install the CLI. + Use the [unikraft CLI](/docs/cli/unikraft) or the legacy [kraft CLI](https://unikraft.org/docs/cli/install). + You need a [BuildKit](https://github.com/moby/buildkit) builder. The easiest way to get one is via [Docker](https://docs.docker.com/engine/install/). + You can also set up and use BuildKit directly; see the [quick start](https://github.com/moby/buildkit#quick-start). + + :::note + The unikraft CLI is the current standard, while kraft is the legacy version. + Choose one of the CLIs below and only run the commands associated with it for the rest of this guide. + ::: 1. Clone the [`examples` repository](https://github.com/unikraft-cloud/examples) and `cd` into the `examples/mcp-server-simple/` directory: -```bash -git clone https://github.com/unikraft-cloud/examples -cd examples/mcp-server-simple/ -``` + ```bash + git clone https://github.com/unikraft-cloud/examples + cd examples/mcp-server-simple/ + ``` -Make sure to set your Unikraft Cloud token and metro: +Make sure to log into Unikraft Cloud and pick a [metro](/platform/metros) close to you. +This guide uses `fra` (Frankfurt, πŸ‡©πŸ‡ͺ). -```bash + + +```bash title="unikraft" +unikraft login +``` + +```bash title="kraft" +# Set Unikraft Cloud access token export UKC_TOKEN=token +# Set metro to Frankfurt, DE export UKC_METRO=fra ``` + + Follow the instructions in the example's README to set it up and grab the server's FQDN. You can use the included example client to test the server, or connect to it using your favourite MCP client. @@ -196,6 +231,10 @@ Use the `--help` option for detailed information on using Unikraft Cloud: +```bash title="unikraft" +unikraft --help +``` + ```bash title="kraft" kraft cloud --help ``` diff --git a/pages/use-cases/remote-desktops.mdx b/pages/use-cases/remote-desktops.mdx index 418bf6d4..4bffdf1d 100644 --- a/pages/use-cases/remote-desktops.mdx +++ b/pages/use-cases/remote-desktops.mdx @@ -48,15 +48,22 @@ This guide shows you how to use [noVNC](https://novnc.com/info.html), an open so To run it, follow these steps: -1. Install the CLI and a container runtime engine (for example, [Docker](https://docs.docker.com/engine/install/)). +1. Install the CLI. Use the [unikraft CLI](/docs/cli/unikraft) or the legacy [kraft CLI](https://unikraft.org/docs/cli/install). + You need a [BuildKit](https://github.com/moby/buildkit) builder. The easiest way to get one is via [Docker](https://docs.docker.com/engine/install/). + You can also set up and use BuildKit directly; see the [quick start](https://github.com/moby/buildkit#quick-start). + + :::note + The unikraft CLI is the current standard, while kraft is the legacy version. + Choose one of the CLIs below and only run the commands associated with it for the rest of this guide. + ::: 1. Clone the [`examples` repository](https://github.com/unikraft-cloud/examples) and `cd` into the `examples/novnc-browser/` directory: -```bash -git clone https://github.com/unikraft-cloud/examples -cd examples/novnc-browser/ -``` + ```bash + git clone https://github.com/unikraft-cloud/examples + cd examples/novnc-browser/ + ``` Make sure to log into Unikraft Cloud and pick a [metro](/platform/metros) close to you. This guide uses `fra` (Frankfurt, πŸ‡©πŸ‡ͺ): @@ -78,11 +85,9 @@ export UKC_METRO=fra The `UKC_TOKEN` and `UKC_METRO` environment variables are only supported by the legacy CLI. -:::note - +:::caution A noVNC desktop instance on Unikraft Cloud requires 4GB to run. Request an increase in the instance memory quota when you need more memory. - ::: When done, invoke the following command to deploy this app on Unikraft Cloud: @@ -91,18 +96,23 @@ When done, invoke the following command to deploy this app on Unikraft Cloud: ```bash title="unikraft" unikraft build . --output /novnc-browser:latest -unikraft run --scale-to-zero policy=on,cooldown-time=4000,stateful=true --metro fra -p 443:6080/tls+http -m 4G --name vnc-browser --image /novnc-browser:latest +unikraft run --metro fra \ + --name vnc-browser \ + -m 4G \ + -p 443:6080/tls+http \ + --scale-to-zero policy=on,cooldown-time=4000,stateful=true \ + --image /novnc-browser:latest ``` ```bash title="kraft" kraft cloud deploy \ - --scale-to-zero on \ - --scale-to-zero-stateful \ - --scale-to-zero-cooldown 4s \ - -p 443:6080/tls+http \ - -M 4Gi \ - -n vnc-browser \ - . + --name vnc-browser \ + -M 4Gi \ + -p 443:6080/tls+http \ + --scale-to-zero on \ + --scale-to-zero-stateful \ + --scale-to-zero-cooldown 4s \ + . ``` diff --git a/pages/use-cases/remote-ides.mdx b/pages/use-cases/remote-ides.mdx index 5d31ee1e..98b3ef69 100644 --- a/pages/use-cases/remote-ides.mdx +++ b/pages/use-cases/remote-ides.mdx @@ -24,12 +24,12 @@ Sensitive code stays protected in a **minimal, hardened environment**. ### πŸ’° Cost-effective scale-to-zero Idle IDE sessions automatically scale down to zero, eliminating wasteful spending on unused developer environments. -Pay only for active usage, whether it’s a classroom full of students or a globally distributed engineering team. +Pay only for active usage, whether it's a classroom full of students or a globally distributed engineering team. ### 🌍 Seamless collaboration -Unikraft Cloud integrates with serverless backends, databases, and API gatewaysβ€”so IDEs can connect effortlessly to the developer’s toolchain. +Unikraft Cloud integrates with serverless backends, databases, and API gatewaysβ€”so IDEs can connect effortlessly to the developer's toolchain. Perfect for **pair programming, CI/CD pipelines, or education platforms**. ### πŸš€ High performance with low overhead @@ -48,14 +48,22 @@ When not in use, the instance will [scale-to-zero](/docs/features/scale-to-zero) To run it, follow these steps: -1. Install the [`kraft` CLI tool](https://unikraft.org/docs/cli/install) and a container runtime engine (for example, [Docker](https://docs.docker.com/engine/install/)). +1. Install the CLI. + Use the [unikraft CLI](/docs/cli/unikraft) or the legacy [kraft CLI](https://unikraft.org/docs/cli/install). + You need a [BuildKit](https://github.com/moby/buildkit) builder. The easiest way to get one is via [Docker](https://docs.docker.com/engine/install/). + You can also set up and use BuildKit directly; see the [quick start](https://github.com/moby/buildkit#quick-start). -1. Clone the [`examples` repository](https://github.com/unikraft-cloud/examples) and `cd` into the `examples/code-server/` directory: + :::note + The unikraft CLI is the current standard, while kraft is the legacy version. + Choose one of the CLIs below and only run the commands associated with it for the rest of this guide. + ::: -```bash -git clone https://github.com/unikraft-cloud/examples -cd examples/code-server/ -``` +1. Clone the [`examples` repository](https://github.com/unikraft-cloud/examples) and `cd` into the `examples/visual-studio-code-server/` directory: + + ```bash + git clone https://github.com/unikraft-cloud/examples + cd examples/visual-studio-code-server/ + ``` Make sure to log into Unikraft Cloud and pick a [metro](/platform/metros) close to you. This guide uses `fra` (Frankfurt, πŸ‡©πŸ‡ͺ). @@ -82,40 +90,151 @@ When done, invoke the following command to deploy this app on Unikraft Cloud: ```bash title="unikraft" -unikraft volumes create \ - --set name=code-workspace \ - --set size=1024 \ - --set metro=fra +unikraft volumes create --metro fra \ + --name code-workspace \ + --size 1G +unikraft build . --output /visual-studio-code-server:latest +unikraft run --metro fra \ + -m 2G \ + -p 443:8443/tls+http \ + --volume code-workspace:/workspace \ + --scale-to-zero policy=on,cooldown-time=4000,stateful=true \ + -e PGUID=0 \ + -e PGID=0 \ + -e PASSWORD=unikraft \ + -e SUDO_PASSWORD=unikraft \ + -e DEFAULT_WORKSPACE="/workspace" \ + --image /visual-studio-code-server:latest ``` ```bash title="kraft" kraft cloud volume create \ - --name code-workspace \ - --size 1Gi - + --name code-workspace \ + --size 1Gi kraft cloud deploy \ - --scale-to-zero on \ - --scale-to-zero-stateful \ - --scale-to-zero-cooldown 4s \ - --name code-server \ - -p 443:8443 \ - -M 2Gi \ - -v code-workspace:/workspace \ - -e PGUID=0 \ - -e PGID=0 \ - -e PASSWORD=unikraft \ - -e SUDO_PASSWORD=unikraft \ - -e DEFAULT_WORKSPACE="/workspace" \ - . + -M 2Gi \ + -p 443:8443 \ + --scale-to-zero on \ + --scale-to-zero-stateful \ + --scale-to-zero-cooldown 4s \ + -v code-workspace:/workspace \ + -e PGUID=0 \ + -e PGID=0 \ + -e PASSWORD=unikraft \ + -e SUDO_PASSWORD=unikraft \ + -e DEFAULT_WORKSPACE="/workspace" + . ``` -Now, you can access the Code Server in the browser, at the provided address. +The output shows the instance address and other details: + + + +```ansi title="unikraft" +metro: fra +name: code-server-6gxsp +uuid: c1a619a0-e222-4042-94b8-ba4b39353417 +state: starting +image: /visual-studio-code-server +resources: + memory: 2048MiB + vcpus: 1 +service: + uuid: 4a0866e4-3bec-3666-c4aa-61672de542e2 + name: blue-shape-chmxf1g4 + domains: + - fqdn: blue-shape-chmxf1g4.fra.unikraft.app +networks: +- uuid: dcec209f-31a6-b355-a88e-f5ac3edfa20e + private-ip: 10.0.0.49 + mac: 12:b0:ec:d2:df:1c +timestamps: + created: just now +``` + +```ansi title="kraft" +[●] Deployed successfully! + β”‚ + β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€ name: code-server-6gxsp + β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€ uuid: c1a619a0-e222-4042-94b8-ba4b39353417 + β”œβ”€β”€β”€β”€β”€β”€β”€β”€ metro: https://api.fra.unikraft.cloud/v1 + β”œβ”€β”€β”€β”€β”€β”€β”€β”€ state: starting + β”œβ”€β”€β”€β”€β”€β”€β”€ domain: https://blue-shape-chmxf1g4.fra.unikraft.app + β”œβ”€β”€β”€β”€β”€β”€β”€β”€ image: oci://unikraft.io//visual-studio-code-server@sha256:633ec8a8dcb342b093c6f055f84fc056ee1abe40ff56e98bd612c4b9d4ddffcb + β”œβ”€β”€β”€β”€β”€β”€β”€ memory: 2048 MiB + β”œβ”€β”€β”€β”€β”€β”€ service: blue-shape-chmxf1g4 + β”œβ”€ private fqdn: code-server-6gxsp.internal + └─── private ip: 10.0.0.49 +``` + + + +This will create a volume for data persistence, and mount it at `/workspace` inside the VM. +In this case, the instance name is `code-server-6gxsp` and the address is `https://blue-shape-chmxf1g4.fra.unikraft.app`. +They're different for each run. +Enter the provided address into your browser of choice to access the Code server instance. + +You can list information about your instance with: + + + +```bash title="unikraft" +unikraft instances list +``` + +```bash title="kraft" +kraft cloud instances list +``` + + + + + +```ansi title="unikraft" +METRO NAME STATE IMAGE ARGS MEMORY VCPUS FQDN CREATED +fra code-server-6gxsp standby /visual-studio-code-server 2.0GiB 1 blue-shape-chmxf1g4.fra.unikraft.app 2 minutes ago +``` -### Volume +```ansi title="kraft" +NAME FQDN STATE STATUS IMAGE MEMORY VCPUS ARGS BOOT TIME +code-server-6gxsp blue-shape-chmxf1g4.fra.unikraft.app standby standby oci://unikraft.io//visual-studio-code-server@sha256:... 2.0 GiB 1 8.45 ms +``` + + + +## Volume This deployment creates a volume for data persistence: `code-workspace`. +You can list information about the volume by running: + + + +```bash title="unikraft" +unikraft volumes list +``` + +```bash title="kraft" +kraft cloud volume list +``` + + + + + +```ansi title="unikraft" +METRO NAME STATE SIZE CREATED +fra code-workspace mounted 1.0GiB 13 minutes ago +``` + +```ansi title="kraft" +NAME CREATED AT SIZE ATTACHED TO MOUNTED BY STATE PERSISTENT +code-workspace 13 minutes ago 1.0 GiB code-server-6gxsp code-server-6gxsp mounted true +``` + + + Upon deleting the instance, this volume will persist, allowing you to create another instance without losing data. To remove the volume, you can use: diff --git a/pages/use-cases/sandboxes.mdx b/pages/use-cases/sandboxes.mdx index 005f2cae..d20768e0 100644 --- a/pages/use-cases/sandboxes.mdx +++ b/pages/use-cases/sandboxes.mdx @@ -58,17 +58,22 @@ A great example of sandboxes in action is [OpenClaw](https://openclaw.ai/), an a This guide explains how to create and deploy your own OpenClaw gateway on Unikraft Cloud. To run this example, follow these steps: -1. Install the CLI and a container runtime engine (for example, [Docker](https://docs.docker.com/engine/install/)). +1. Install the CLI. Use the [unikraft CLI](/docs/cli/unikraft) or the legacy [kraft CLI](https://unikraft.org/docs/cli/install). You need a [BuildKit](https://github.com/moby/buildkit) builder. The easiest way to get one is via [Docker](https://docs.docker.com/engine/install/). - You could also directly set up and use BuildKit, see the [quick start](https://github.com/moby/buildkit#quick-start). + You can also set up and use BuildKit directly; see the [quick start](https://github.com/moby/buildkit#quick-start). + + :::note + The unikraft CLI is the current standard, while kraft is the legacy version. + Choose one of the CLIs below and only run the commands associated with it for the rest of this guide. + ::: 1. Clone the [`examples` repository](https://github.com/unikraft-cloud/examples) and `cd` into the `examples/openclaw` directory: -```bash -git clone https://github.com/unikraft-cloud/examples -cd examples/openclaw/ -``` + ```bash + git clone https://github.com/unikraft-cloud/examples + cd examples/openclaw/ + ``` Make sure to log into Unikraft Cloud and pick a [metro](/platform/metros) close to you. This guide uses `fra` (Frankfurt, πŸ‡©πŸ‡ͺ): @@ -96,16 +101,23 @@ When done, you may create the OpenClaw Unikraft Cloud image and deploy an instan ```bash title="unikraft" unikraft build . --output /openclaw:latest -unikraft run --scale-to-zero policy=on,cooldown-time=10000 --metro fra -p 18789:18789/tls -p 2222:2222/tls -m 4G -e PUBKEY="...." --image /openclaw:latest +unikraft run --metro fra \ + -m 4G \ + -p 18789:18789/tls \ + -p 2222:2222/tls \ + --scale-to-zero policy=on,cooldown-time=10000,stateful=true \ + -e PUBKEY="...." \ + --image /openclaw:latest ``` ```bash title="kraft" kraft cloud deploy \ - --scale-to-zero on \ - --scale-to-zero-cooldown 10s \ + -M 4Gi \ -p 18789:18789/tls \ -p 2222:2222/tls \ - -M 4Gi \ + --scale-to-zero on \ + --scale-to-zero-cooldown 10s \ + --scale-to-zero-stateful \ -e PUBKEY="..." \ . ``` @@ -123,7 +135,7 @@ metro: fra name: openclaw-8tosm uuid: e2a6183a-721b-4145-bfaf-37a5f859bbc1 state: starting -image: demo/openclaw +image: /openclaw runtime: env: PUBKEY: * @@ -134,7 +146,7 @@ service: uuid: 7ab20338-b04d-4869-947b-9433e21677b1 name: divine-flower-bxsaapup domains: - - fqdn: divine-flower-bxsaapup.fra0-demo.unikraft.app + - fqdn: divine-flower-bxsaapup.fra.unikraft.app networks: - uuid: 2b0b120b-6ce5-4b19-ac4c-04ee8f11526e private-ip: 10.0.12.97 @@ -150,7 +162,8 @@ timestamps: β”œ───────── uuid: e2a6183a-721b-4145-bfaf-37a5f859bbc1 β”œ──────── metro: https://api.fra.unikraft.cloud/v1 β”œ──────── state: starting - β”œ──────── image: demo/openclaw@sha256:7a3d9f2b5e8c1a4d7f0e3b6c9a2d5f8b1e4a7d0c3f6b9e2a5d8c1f4b7e0a3d6c9 + β”œ─────── domain: https://divine-flower-bxsaapup.fra.unikraft.app + β”œ──────── image: oci://unikraft.io//openclaw@sha256:7a3d9f2b5e8c1a4d7f0e3b6c9a2d5f8b1e4a7d0c3f6b9e2a5d8c1f4b7e0a3d6c9 β”œ─────── memory: 4096 MiB β”œ─ private fqdn: openclaw-8tosm.internal β””─── private ip: 10.0.12.97 @@ -158,7 +171,7 @@ timestamps: -In this case, the instance name is `openclaw-8tosm` and the address is `divine-flower-bxsaapup.fra0-demo.unikraft.app`. +In this case, the instance name is `openclaw-8tosm` and the address is `divine-flower-bxsaapup.fra.unikraft.app`. These will be different for each run. You can now SSH into this instance and run the OpenClaw onboarding process. @@ -167,7 +180,7 @@ To SSH, you need to set up a tunnel that handles the TLS connection to the Unikr This way, you have a non-TLS port that your SSH client can connect to: ```bash -socat TCP-LISTEN:2222,reuseaddr,fork OPENSSL:divine-flower-bxsaapup.fra0-demo.unikraft.app:2222,verify=0 +socat TCP-LISTEN:2222,reuseaddr,fork OPENSSL:divine-flower-bxsaapup.fra.unikraft.app:2222,verify=0 ``` Then connect to the instance via SSH using: @@ -193,13 +206,13 @@ kraft cloud instance list ```ansi title="unikraft" -METRO NAME STATE IMAGE ARGS MEMORY VCPUS CREATED -fra openclaw-8tosm starting demo/openclaw 4096MiB 1 just now +METRO NAME STATE IMAGE ARGS MEMORY VCPUS FQDN CREATED +fra openclaw-8tosm standby /openclaw 4.0GiB 1 divine-flower-bxsaapup.fra.unikraft.app 2 minutes ago ``` ```ansi title="kraft" -NAME IMAGE ARGS CREATED AT -openclaw-8tosm demo/openclaw 20 seconds ago +NAME FQDN STATE STATUS IMAGE MEMORY VCPUS ARGS BOOT TIME +openclaw-8tosm divine-flower-bxsaapup.fra.unikraft.app standby standby oci://unikraft.io//openclaw@sha256:7a3d9f... 4.0 GiB 1 8.45 ms ``` @@ -241,13 +254,13 @@ cat ~/.openclaw/openclaw.json Set `gateway.controlUi.allowedOrigins` in `~/.openclaw/openclaw.json`: -```json +```json title="openclaw.json" ... "gateway": { ... "controlUi": { "allowedOrigins": [ - "https://proud-smoke-cjf0wro8.fra0-demo.unikraft.app:18789" + "https://proud-smoke-cjf0wro8.fra.unikraft.app:18789" ] }, ... @@ -265,7 +278,7 @@ openclaw gateway run --bind lan You may now access the web dashboard at the following address: -```ansi +```text title="" https://
:18789?token= ``` diff --git a/pages/use-cases/serverless-databases.mdx b/pages/use-cases/serverless-databases.mdx index 12e34837..d408f918 100644 --- a/pages/use-cases/serverless-databases.mdx +++ b/pages/use-cases/serverless-databases.mdx @@ -52,15 +52,22 @@ The instance will [scale-to-zero](/features/scale-to-zero) when not in use. To run it, follow these steps: -1. Install the [`kraft` CLI tool](https://unikraft.org/docs/cli/install) and a container runtime engine (for example, [Docker](https://docs.docker.com/engine/install/)). +1. Install the CLI. + Use the [unikraft CLI](/docs/cli/unikraft) or the legacy [kraft CLI](https://unikraft.org/docs/cli/install). + You need a [BuildKit](https://github.com/moby/buildkit) builder. The easiest way to get one is via [Docker](https://docs.docker.com/engine/install/). + You can also set up and use BuildKit directly; see the [quick start](https://github.com/moby/buildkit#quick-start). -1. Clone the [`examples` repository](https://github.com/unikraft-cloud/examples) and - `cd` into the `examples/postgres/` directory: + :::note + The unikraft CLI is the current standard, while kraft is the legacy version. + Choose one of the CLIs below and only run the commands associated with it for the rest of this guide. + ::: -```bash -git clone https://github.com/unikraft-cloud/examples -cd examples/postgres/ -``` +1. Clone the [`examples` repository](https://github.com/unikraft-cloud/examples) and `cd` into the `examples/postgres/` directory: + + ```bash + git clone https://github.com/unikraft-cloud/examples + cd examples/postgres/ + ``` Make sure to log into Unikraft Cloud by setting your token and a [metro](/platform/metros) close to you. This guide uses `fra` (Frankfurt, πŸ‡©πŸ‡ͺ): @@ -69,8 +76,6 @@ This guide uses `fra` (Frankfurt, πŸ‡©πŸ‡ͺ): ```bash title="unikraft" unikraft login -unikraft build . --output /postgres:latest -unikraft run --metro=fra -e POSTGRES_PASSWORD=unikraft -p 5432:5432/tls -m 1Gi --image=/postgres:latest ``` ```bash title="kraft" @@ -78,36 +83,88 @@ unikraft run --metro=fra -e POSTGRES_PASSWORD=unikraft -p 5432:5432/tls -m 1Gi - export UKC_TOKEN=token # Set metro to Frankfurt, DE export UKC_METRO=fra -kraft cloud deploy -e POSTGRES_PASSWORD=unikraft -p 5432:5432/tls -M 1Gi . ``` The `UKC_TOKEN` and `UKC_METRO` environment variables are only supported by the legacy CLI. +When done, invoke the following command to deploy this app on Unikraft Cloud: + + + +```bash title="unikraft" +unikraft build . --output /postgres:latest +unikraft run --metro fra \ + -m 1G \ + -p 5432:5432/tls \ + --scale-to-zero policy=idle,cooldown-time=1000,stateful=true \ + -e POSTGRES_PASSWORD=unikraft \ + --image /postgres:latest +``` + +```bash title="kraft" +kraft cloud deploy \ + -M 1Gi \ + -p 5432:5432/tls \ + --scale-to-zero idle \ + --scale-to-zero-cooldown 1s \ + --scale-to-zero-stateful \ + -e POSTGRES_PASSWORD=unikraft \ + . +``` + + + The output shows the instance address and other details: -```ansi + + +```ansi title="unikraft" +metro: fra +name: postgres-saan9 +uuid: 3a1371f2-68c6-4187-84f8-c080f2b028ca +state: starting +image: /postgres +resources: + memory: 1024MiB + vcpus: 1 +service: + uuid: 8e9d810b-b1da-a30b-fd42-5c30c1900cb5 + name: young-thunder-fbafrsxj + domains: + - fqdn: young-thunder-fbafrsxj.fra.unikraft.app +networks: +- uuid: f1fab4c9-7951-75e3-ea1c-d87e47b4c9e2 + private-ip: 10.0.3.1 + mac: 12:b0:31:34:b1:96 +timestamps: + created: just now +``` + +```ansi title="kraft" [●] Deployed successfully! β”‚ - β”œ────────── name: postgres-saan9 - β”œ────────── uuid: 3a1371f2-68c6-4187-84f8-c080f2b028ca - β”œ───────── state: starting - β”œ────────── fqdn: young-thunder-fbafrsxj.fra.unikraft.app - β”œ───────── image: postgres@sha256:2476c0373d663d7604def7c35ffcb4ed4de8ab231309b4f20104b84f31570766 - β”œ──────── memory: 1024 MiB - β”œ─────── service: young-thunder-fbafrsxj - β”œ── private fqdn: postgres-saan9.internal - β”œ──── private ip: 172.16.3.1 - β””────────── args: wrapper.sh docker-entrypoint.sh postgres -c shared_preload_libraries='pg_ukc_scaletozero' + β”œ───────── name: postgres-saan9 + β”œ───────── uuid: 3a1371f2-68c6-4187-84f8-c080f2b028ca + β”œ──────── metro: https://api.fra.unikraft.cloud/v1 + β”œ──────── state: starting + β”œ─────── domain: https://young-thunder-fbafrsxj.fra.unikraft.app + β”œ──────── image: oci://unikraft.io//postgres@sha256:2476c0373d663d7604def7c35ffcb4ed4de8ab231309b4f20104b84f31570766 + β”œ─────── memory: 1024 MiB + β”œ────── service: young-thunder-fbafrsxj + β”œ─ private fqdn: postgres-saan9.internal + β””─── private ip: 10.0.3.1 ``` + + In this case, the instance name is `postgres-saan9` and the service `young-thunder-fbafrsxj`. They're different for each run. If you use port `5432/tls`, according to the example above, you can now directly connect to postgres: -```console +```bash psql -U postgres -h young-thunder-fbafrsxj.fra.unikraft.app ``` @@ -126,24 +183,17 @@ postgres=# Use SQL and `psql` commands for your work. -:::tip[Idle scale-to-zero] -This example uses the [`idle` scale-to-zero policy](/features/scale-to-zero) by default (see the `labels` section in the `Kraftfile`). +:::tip +This example uses the [`idle` scale-to-zero policy](/features/scale-to-zero) by default. This means that the instance will use scale-to-zero even in the presence of `psql` connections. The PostgreSQL example makes use of scale-to-zero app support. This ensures that the instance isn't put into standby even for long running queries (during which the connections are also idle). -To this end, the [`pg_ukc_scaletozero`](https://github.com/unikraft-cloud/pg_ukc_scaletozero) module is loaded into Postgres. +To this end, the example loads the [`pg_ukc_scaletozero`](https://github.com/unikraft-cloud/pg_ukc_scaletozero) module into PostgreSQL. This module suspends scale-to-zero during query processing. You can see this in action by running `SELECT pg_sleep(10);` and verifying that the instance keeps on running. ::: -:::note -If you'd like to use a port other than `5432/tls`, you'll need to use the legacy [CLI tunnel](/docs/cli/kraft/tunnel) command to connect to Postgres. - -You need to explicitly disable scale-to-zero. -You can do this by either changing the label in the `Kraftfile` or by using `--scale-to-zero off` in the deploy command. -::: - At any time, you can list information about the instance: @@ -158,11 +208,20 @@ kraft cloud instance list -```text -NAME FQDN STATE CREATED AT IMAGE MEMORY ARGS BOOT TIME -postgres-saan9 young-thunder-fbafrsxj.fra.unikraft.app running 6 minutes ago postgres@sha256:2476c0373d663d7604d... 1.0 GiB wrapper.sh docker-entrypoint.sh postgres 603.42 ms + + +```ansi title="unikraft" +METRO NAME STATE IMAGE ARGS MEMORY VCPUS FQDN CREATED +fra postgres-saan9 running /postgres 1.0GiB 1 young-thunder-fbafrsxj.fra.unikraft.app 2 minutes ago +``` + +```ansi title="kraft" +NAME FQDN STATE STATUS IMAGE MEMORY VCPUS ARGS BOOT TIME +postgres-saan9 young-thunder-fbafrsxj.fra.unikraft.app running 6 minutes ago oci://unikraft.io//postgres@sha256:... 1.0 GiB 1 603.42 ms ``` + + When done, you can remove the instance: @@ -177,7 +236,7 @@ kraft cloud instance remove postgres-saan9 -### Using volumes +## Using volumes You can use [volumes](/platform/volumes) for data persistence for your PostgreSQL instance. @@ -185,15 +244,16 @@ For that you would first create a volume: -```console title="unikraft" -unikraft volumes create \ - --set name=postgres \ - --set size=200 \ - --set metro=fra +```bash title="unikraft" +unikraft volumes create --metro fra \ + --name postgres \ + --size 200M ``` -```console title="kraft" -kraft cloud volume create --name postgres --size 200 +```bash title="kraft" +kraft cloud volume create \ + --name postgres \ + --size 200Mi ``` @@ -202,28 +262,44 @@ Then start the PostgreSQL instance and mount that volume: -```console title="unikraft" +```bash title="unikraft" unikraft build . --output /postgres:latest -unikraft run --metro=fra -p 5432:5432/tls -m 1Gi -e POSTGRES_PASSWORD=unikraft -e PGDATA=/volume/postgres -v postgres:/volume --image=/postgres:latest +unikraft run --metro fra \ + -m 1G \ + -p 5432:5432/tls \ + --scale-to-zero policy=idle,cooldown-time=1000,stateful=true \ + -e POSTGRES_PASSWORD=unikraft \ + -e PGDATA=/volume/postgres \ + --volume postgres:/volume \ + --image /postgres:latest ``` -```console title="kraft" -kraft cloud deploy -p 5432:5432/tls -M 1Gi -e POSTGRES_PASSWORD=unikraft -e PGDATA=/volume/postgres -v postgres:/volume . +```bash title="kraft" +kraft cloud deploy \ + -M 1Gi \ + -p 5432:5432/tls \ + --scale-to-zero idle \ + --scale-to-zero-cooldown 1s \ + --scale-to-zero-stateful \ + -e POSTGRES_PASSWORD=unikraft \ + -e PGDATA=/volume/postgres \ + -v postgres:/volume \ + . ``` -### Using checkpoints +## Using checkpoints :::note Checkpointing is coming soon. Full documentation will follow once the interface is available. ::: -### Using instance branching +## Using instance branching -:::note -Instance branching is a preview feature and isn't yet available on stable. +:::caution[**Limited Access**] +Instance branching is a preview feature and isn't yet available on the public Unikraft Cloud. The interface described here reflects the current implementation and may change before general availability. ::: @@ -233,9 +309,18 @@ The source instance keeps running unaffected. To create a branch, pass `--branch` with the source instance name to `unikraft run`: -```console title="unikraft" -unikraft run --metro=fra -p 5432:5432/tls --branch + +```bash title="unikraft" +unikraft run --metro fra \ + -p 5432:5432/tls \ + --branch +``` + +```bash title="kraft" +# branching will not be supported in the legacy CLI +# use the unikraft CLI ``` + The branch boots immediately from the same state as the source. @@ -245,12 +330,18 @@ Whatever you do, the source instance remains unaffected. When you're done, remove the branch: -```console title="unikraft" + +```bash title="unikraft" unikraft instances delete ``` + +```bash title="kraft" +kraft cloud instance remove +``` + -### Customize your deployment +## Customize your deployment Your deployment is a standard PostgreSQL installation. Customizing the deployment means providing a different environment. @@ -261,14 +352,6 @@ For that you use a different `POSTGRES_PASSWORD` environment variable when start You could also use a different location to mount your volume or set more configuration options. And, you can use the PostgreSQL instance in conjunction with a frontend service, [see the guide here](/platform/services). -But, in that case make sure to disable scale-to-zero if you plan to use the DB internally. - -:::note - -Support for scale-to-zero for internal instances is coming soon. - -::: - ## Learn more diff --git a/pages/use-cases/serverless-functions.mdx b/pages/use-cases/serverless-functions.mdx index 76c07d6e..13c2c639 100644 --- a/pages/use-cases/serverless-functions.mdx +++ b/pages/use-cases/serverless-functions.mdx @@ -46,16 +46,22 @@ It deploys a Node.js runtime as the base image, then attaches JavaScript or Type ### Prerequisites -1. Install the CLI: +1. Install the CLI. Use the [unikraft CLI](/docs/cli/unikraft) or the legacy [kraft CLI](https://unikraft.org/docs/cli/install). - You need a [BuildKit](https://github.com/moby/buildkit) builderβ€”the easiest way is via [Docker](https://docs.docker.com/engine/install/). + You need a [BuildKit](https://github.com/moby/buildkit) builder. The easiest way to get one is via [Docker](https://docs.docker.com/engine/install/). + You can also set up and use BuildKit directly; see the [quick start](https://github.com/moby/buildkit#quick-start). + + :::note + The unikraft CLI is the current standard, while kraft is the legacy version. + Choose one of the CLIs below and only run the commands associated with it for the rest of this guide. + ::: 1. Clone the [`examples` repository](https://github.com/unikraft-cloud/examples) and `cd` into the `examples/node-code-execution` directory: -```bash -git clone https://github.com/unikraft-cloud/examples -cd examples/node-code-execution/ -``` + ```bash + git clone https://github.com/unikraft-cloud/examples + cd examples/node-code-execution/ + ``` Make sure to log into Unikraft Cloud and pick a [metro](/platform/metros) close to you. This guide uses `fra` (Frankfurt, πŸ‡©πŸ‡ͺ): @@ -88,12 +94,12 @@ unikraft build . --output /node-code-exec:latest ```bash title="kraft" kraft pkg \ - --name index.unikraft.io//node-code-exec:latest \ - --plat kraftcloud \ - --arch x86_64 \ - --rootfs-type erofs \ - --push \ - . + --name index.unikraft.io//node-code-exec:latest \ + --plat kraftcloud \ + --arch x86_64 \ + --rootfs-type erofs \ + --push \ + . ``` @@ -204,21 +210,19 @@ unikraft build rom2/ --output /node-rom2:latest ```bash title="kraft" kraft pkg \ - --rom ./fs \ - --rom-type erofs \ - --plat kraftcloud \ - --arch x86_64 \ - --name index.unikraft.io//node-rom1:latest \ - --push \ - rom1/ + --plat kraftcloud \ + --arch x86_64 \ + --name index.unikraft.io//node-rom1:latest \ + --rom-type erofs \ + --push \ + rom1/ kraft pkg \ - --rom ./fs \ - --rom-type erofs \ - --plat kraftcloud \ - --arch x86_64 \ - --name index.unikraft.io//node-rom2:latest \ - --push \ - rom2/ + --plat kraftcloud \ + --arch x86_64 \ + --name index.unikraft.io//node-rom2:latest \ + --rom-type erofs \ + --push \ + rom2/ ``` @@ -241,40 +245,39 @@ unikraft run --metro fra \ --template node-exec ``` -```bash title="kraft" -# kraft does not support creating instances with attached ROMs, but you can use the API directly -curl -X POST "$UKC_METRO/instances" \ - -H "Accept: application/json" \ - -H "Authorization: Bearer $UKC_TOKEN" \ - -H "Content-Type: application/json" \ - -d '{ - "name": "node-exec-rom1", - "template": { +```bash title="API" +curl -X POST "https://api.fra.unikraft.cloud/v1/instances" \ + -H "Accept: application/json" \ + -H "Authorization: Bearer $UKC_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "node-exec-rom1", + "template": { "name": "node-exec" - }, - "autostart": true, - "service_group": { + }, + "autostart": true, + "service_group": { "services": [ - { - "port": 443, - "destination_port": 8080, - "handlers": ["tls", "http"] - } + { + "port": 443, + "destination_port": 8080, + "handlers": ["tls", "http"] + } ] - }, - "scale_to_zero": { + }, + "scale_to_zero": { "policy": "on", "stateful": true, "cooldown_time_ms": 1000 - }, - "roms": [ + }, + "roms": [ { - "name": "js_function", - "image": "index.unikraft.io//node-rom1:latest", - "at": "/rom" + "name": "js_function", + "image": "index.unikraft.io//node-rom1:latest", + "at": "/rom" } - ] -}' + ] + }' ``` @@ -292,40 +295,39 @@ unikraft run --metro fra \ --template node-exec ``` -```bash title="kraft" -# kraft does not support creating instances with attached ROMs, but you can use the API directly -curl -X POST "$UKC_METRO/instances" \ - -H "Accept: application/json" \ - -H "Authorization: Bearer $UKC_TOKEN" \ - -H "Content-Type: application/json" \ - -d '{ - "name": "node-exec-rom2", - "template": { +```bash title="API" +curl -X POST "https://api.fra.unikraft.cloud/v1/instances" \ + -H "Accept: application/json" \ + -H "Authorization: Bearer $UKC_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "node-exec-rom2", + "template": { "name": "node-exec" - }, - "autostart": true, - "service_group": { + }, + "autostart": true, + "service_group": { "services": [ - { - "port": 443, - "destination_port": 8080, - "handlers": ["tls", "http"] - } + { + "port": 443, + "destination_port": 8080, + "handlers": ["tls", "http"] + } ] - }, - "scale_to_zero": { + }, + "scale_to_zero": { "policy": "on", "stateful": true, "cooldown_time_ms": 1000 - }, - "roms": [ + }, + "roms": [ { - "name": "ts_function", - "image": "index.unikraft.io//node-rom2:latest", - "at": "/rom" + "name": "ts_function", + "image": "index.unikraft.io//node-rom2:latest", + "at": "/rom" } - ] -}' + ] + }' ``` diff --git a/pages/use-cases/webhooks.mdx b/pages/use-cases/webhooks.mdx index 833c9b88..08147b48 100644 --- a/pages/use-cases/webhooks.mdx +++ b/pages/use-cases/webhooks.mdx @@ -46,25 +46,41 @@ This guide shows you how to deploy a GitHub webhook receiver that listens for re The example uses Node.js with Express and the official [Octokit webhooks library](https://www.npmjs.com/package/@octokit/webhooks). To run it, follow these steps: -1. Install the [`kraft` CLI tool](https://unikraft.org/docs/cli/install) and a container runtime engine (for example, [Docker](https://docs.docker.com/engine/install/)). +1. Install the CLI. + Use the [unikraft CLI](/docs/cli/unikraft) or the legacy [kraft CLI](https://unikraft.org/docs/cli/install). + You need a [BuildKit](https://github.com/moby/buildkit) builder. The easiest way to get one is via [Docker](https://docs.docker.com/engine/install/). + You can also set up and use BuildKit directly; see the [quick start](https://github.com/moby/buildkit#quick-start). -1. Clone the [`examples` repository](https://github.com/unikraft-cloud/examples) and `cd` into the `examples/webhook-github-node/` directory: + :::note + The unikraft CLI is the current standard, while kraft is the legacy version. + Choose one of the CLIs below and only run the commands associated with it for the rest of this guide. + ::: -```bash -git clone https://github.com/unikraft-cloud/examples -cd examples/webhook-github-node/ -``` +1. Clone the [`examples` repository](https://github.com/unikraft-cloud/examples) and `cd` into the `examples/github-webhook-node/` directory: -Make sure to log into Unikraft Cloud by setting your token and a [metro](/platform/metros) close to you. + ```bash + git clone https://github.com/unikraft-cloud/examples + cd examples/github-webhook-node/ + ``` + +Make sure to log into Unikraft Cloud and set your token and a [metro](/platform/metros) close to you. This guide uses `fra` (Frankfurt, πŸ‡©πŸ‡ͺ): -```bash + + +```bash title="unikraft" +unikraft login +``` + +```bash title="kraft" # Set Unikraft Cloud access token export UKC_TOKEN=token # Set metro to Frankfurt, DE export UKC_METRO=fra ``` + + Follow the instructions in the example's README to set it up and grab the server's FQDN. After that, check that the service is up: @@ -72,7 +88,7 @@ After that, check that the service is up: curl https:///health ``` -```text +```text title="" {"status":"healthy","timestamp":"2025-12-17T14:55:20.953Z","uptime":0.063799807} ``` @@ -95,6 +111,10 @@ Use the `--help` option for detailed information on using Unikraft Cloud: +```bash title="unikraft" +unikraft --help +``` + ```bash title="kraft" kraft cloud --help ``` diff --git a/scripts/capture_ansi.sh b/scripts/capture_ansi.sh new file mode 100755 index 00000000..14b56aff --- /dev/null +++ b/scripts/capture_ansi.sh @@ -0,0 +1,149 @@ +#!/usr/bin/env bash +set -euo pipefail + +usage() { + cat <<'EOF' +Capture command output while preserving ANSI color codes. + +Usage: + capture_ansi.sh [--docs-bars] [--tui] [--tui-wait ] -- [args...] + +Examples: + capture_ansi.sh build.ansi.log -- ls --color=always + capture_ansi.sh test.ansi.log -- bash -lc 'kraft cloud quotas' + capture_ansi.sh quotas.ansi.log -- kraft cloud quotas + capture_ansi.sh --docs-bars quotas.ansi.log -- kraft cloud quotas + capture_ansi.sh --tui quotas.ansi.log -- unikraft metro quotas + +Notes: + - This script prefers the "script" utility so commands run in a pseudo-TTY. + That keeps tools that only colorize on TTY from dropping colors. + - If "script" is unavailable, it falls back to plain execution; in that + case you may need command flags like --color=always. + - --docs-bars converts background-only bar tails into visible block glyphs, + so empty bar segments remain visible in MDX ANSI renderers. + - --tui captures the rendered screen for full-screen TUIs (requires tmux). + This avoids raw cursor-control escape sequences in the output. + - --tui-wait controls how long to wait before taking a screen snapshot. +EOF +} + +docs_bars=0 +tui_mode=0 +tui_wait="1.2" +tui_quit_key="q" + +while [[ $# -gt 0 ]]; do + case "$1" in + --docs-bars) + docs_bars=1 + shift + ;; + --tui) + tui_mode=1 + shift + ;; + --tui-wait) + if [[ $# -lt 2 ]]; then + echo "error: --tui-wait requires a value in seconds" >&2 + exit 1 + fi + tui_wait=$2 + shift 2 + ;; + -h|--help) + usage + exit 0 + ;; + *) + break + ;; + esac +done + +if [[ $# -lt 3 ]]; then + usage + exit 0 +fi + +out_file=$1 +shift + +if [[ ${1:-} != "--" ]]; then + echo "error: missing '--' before command" >&2 + usage + exit 1 +fi +shift + +mkdir -p "$(dirname "$out_file")" + +sanitize_stream() { + # Keep color/style SGR codes (CSI ... m), but drop terminal query/control noise + # that leaks into captured logs (OSC, DSR/CPR) and normalize line endings. + perl -CSDA -pe ' + s/\r$//; + s/\x1b\][^\x07\x1b]*(?:\x07|\x1b\\)//g; + s/\x1b\[[0-9;?]*n//g; + s/\x1b\[[0-9;]*R//g; + # Remove redundant foreground reset after full reset + s/(\x1b\[0m)\x1b\[39m/$1/g; + ' +} + +render_docs_bars() { + # Render background-only bar tails as visible glyphs while preserving width. + # This intentionally targets only multi-space runs to avoid tab/header padding. + perl -CSDA -pe ' + s/\x1b\[[0-9;]*48;5;([0-9]+)[0-9;]*m( {2,})\x1b\[(?:0|49)?m/ + "\x1b[38;5;".$1."m" . ("\x{2588}" x length($2)) . "\x1b[0m" + /ge; + ' +} + +apply_filters() { + if [[ $docs_bars -eq 1 ]]; then + sanitize_stream | render_docs_bars + else + sanitize_stream + fi +} + +capture_tui_screen() { + if ! command -v tmux >/dev/null 2>&1; then + echo "error: --tui requires tmux" >&2 + exit 1 + fi + + local session="capture_ansi_${RANDOM}_$$" + local cols + local lines + cols=$(tput cols 2>/dev/null || echo 120) + lines=$(tput lines 2>/dev/null || echo 40) + + local cmd + cmd=$(printf ' %q' "$@") + local quoted_cmd + quoted_cmd=$(printf '%q' "${cmd:1}") + + tmux new-session -d -x "$cols" -y "$lines" -s "$session" "bash -lc ${quoted_cmd}" + sleep "$tui_wait" + tmux capture-pane -p -e -t "${session}:0.0" | apply_filters >"$out_file" + + # Try to leave the app cleanly if it is still running. + tmux send-keys -t "${session}:0.0" "$tui_quit_key" || true + sleep 0.15 + tmux kill-session -t "$session" >/dev/null 2>&1 || true +} + +if [[ $tui_mode -eq 1 ]]; then + capture_tui_screen "$@" +elif command -v script >/dev/null 2>&1; then + # Run in a PTY so tools keep colors, then persist output. + cmd=$(printf ' %q' "$@") + script -qec "${cmd:1}" /dev/null | apply_filters >"$out_file" +else + "$@" 2>&1 | apply_filters >"$out_file" +fi + +echo "wrote: $out_file" diff --git a/zudoku.config.tsx b/zudoku.config.tsx index 4b73aa91..054af17c 100644 --- a/zudoku.config.tsx +++ b/zudoku.config.tsx @@ -80,25 +80,6 @@ const config: ZudokuConfig = { "/features/forking", ], }, - { - type: "category", - label: "Use Cases", - icon: "lightbulb", - collapsed: false, - items: [ - "/use-cases/sandboxes", - "/use-cases/headless-browsers", - "/use-cases/mcp-servers", - "/use-cases/api-gateways", - "/use-cases/serverless-functions", - "/use-cases/serverless-databases", - "/use-cases/build-test-environments", - "/use-cases/webhooks", - "/use-cases/remote-ides", - "/use-cases/game-servers", - "/use-cases/remote-desktops", - ], - }, { type: "category", label: "Cloud Platform", @@ -108,6 +89,7 @@ const config: ZudokuConfig = { "/platform/metros", "/platform/instances", "/platform/services", + "/platform/networking", "/platform/domains", "/platform/certificates", "/platform/volumes", @@ -119,6 +101,25 @@ const config: ZudokuConfig = { "/platform/troubleshooting", ], }, + { + type: "category", + label: "Use Cases", + icon: "lightbulb", + collapsed: false, + items: [ + "/use-cases/sandboxes", + "/use-cases/headless-browsers", + "/use-cases/mcp-servers", + "/use-cases/api-gateways", + "/use-cases/serverless-functions", + "/use-cases/serverless-databases", + "/use-cases/build-test-environments", + "/use-cases/webhooks", + "/use-cases/remote-ides", + "/use-cases/game-servers", + "/use-cases/remote-desktops", + ], + }, { type: "category", label: "Integrations", @@ -231,8 +232,6 @@ const config: ZudokuConfig = { "/tutorials/rootfs-compression", "/tutorials/rootfs-volumes-roms", "/tutorials/scale-to-zero-triggers", - "/tutorials/instance-metrics", - "/tutorials/network-communication" ] }, { From 1f70ca45115404307e38c10045baccf685fde431 Mon Sep 17 00:00:00 2001 From: Alex-Andrei Cioc Date: Tue, 9 Jun 2026 16:37:58 +0300 Subject: [PATCH 2/2] chore: Update ANSI coloring Signed-off-by: Alex-Andrei Cioc --- .gitignore | 1 + pages/cli/registries.mdx | 10 +- pages/features/autokill.mdx | 10 +- pages/features/autoscale.mdx | 68 +-- pages/features/cron-jobs.mdx | 10 +- pages/features/load-balancing.mdx | 164 +++---- pages/features/roms.mdx | 6 +- pages/features/scale-to-zero.mdx | 130 +++--- pages/integrations/kubernetes.mdx | 52 +-- pages/platform/certificates.mdx | 96 ++-- pages/platform/delete-locks.mdx | 16 +- pages/platform/domains.mdx | 72 +-- pages/platform/images.mdx | 224 +++++----- pages/platform/instances.mdx | 126 +++--- pages/platform/metrics.mdx | 4 +- pages/platform/networking.mdx | 8 +- pages/platform/quotas.mdx | 2 +- pages/platform/services.mdx | 156 +++---- pages/platform/tagging.mdx | 40 +- pages/platform/troubleshooting.mdx | 52 +-- pages/platform/volumes.mdx | 230 +++++----- pages/tutorials/kraftkit-to-unikraft.mdx | 15 +- pages/tutorials/rootfs-formats.mdx | 124 +++--- pages/tutorials/rootfs-volumes-roms.mdx | 2 +- pages/tutorials/scale-to-zero-triggers.mdx | 2 +- pages/use-cases/api-gateways.mdx | 144 +++--- pages/use-cases/build-test-environments.mdx | 66 +-- pages/use-cases/game-servers.mdx | 78 ++-- pages/use-cases/headless-browsers.mdx | 66 +-- pages/use-cases/remote-desktops.mdx | 64 +-- pages/use-cases/remote-ides.mdx | 76 ++-- pages/use-cases/sandboxes.mdx | 72 +-- pages/use-cases/serverless-databases.mdx | 66 +-- pages/use-cases/serverless-functions.mdx | 62 +-- scripts/transform_readme.py | 318 +++++++++++-- scripts/update_ansi_colors.py | 467 ++++++++++++++++++++ 36 files changed, 1908 insertions(+), 1191 deletions(-) create mode 100755 scripts/update_ansi_colors.py diff --git a/.gitignore b/.gitignore index 6ad6b839..11f865a5 100644 --- a/.gitignore +++ b/.gitignore @@ -39,6 +39,7 @@ pnpm-debug.log* # build outputs dist/ +*.pyc # generated files apis/platform.yaml diff --git a/pages/cli/registries.mdx b/pages/cli/registries.mdx index 8b97e113..5a237a06 100644 --- a/pages/cli/registries.mdx +++ b/pages/cli/registries.mdx @@ -125,7 +125,7 @@ For the legacy CLI, package using the `--push` flag: kraft pkg \ --plat kraftcloud \ --arch x86_64 \ - --name index.unikraft.io/"$UKC_USER"/my-app:latest \ + --name "index.unikraft.io/${UKC_USER}/my-app:latest" \ --push \ . ``` @@ -134,7 +134,7 @@ kraft pkg \ kraft pkg \ --plat kraftcloud \ --arch x86_64 \ - --name index..unikraft.cloud/"$UKC_USER"/my-app:latest \ + --name "index..unikraft.cloud/${UKC_USER}/my-app:latest" \ --push \ . ``` @@ -158,13 +158,13 @@ kraft cloud image ls ```ansi title="central registry" -NAME VERSION SIZE +NAME VERSION SIZE alice/http-python312 latest 77 MB alice/my-app latest 42 MB ``` ```ansi title="local registry" -NAME VERSION SIZE +NAME VERSION SIZE alice/http-python312 latest 77 MB index..unikraft.cloud/alice/my-app latest 42 MB ``` @@ -194,7 +194,7 @@ kraft cloud image rm /[:] ## Kernel layer deduplication An image has two OCI layers: a kernel layer and a rootfs layer. -When you push an update to an existing image, the CLI checks whether the kernel layer is already present in the registry and skips re-uploading it if so. +When you push an update to an existing image, the CLI checks whether the kernel layer is already present in the registry and skips re-uploading it. Only the rootfs layer travels over the wire. This makes iterative pushes faster and cheaper on bandwidth. diff --git a/pages/features/autokill.mdx b/pages/features/autokill.mdx index 6c36742d..1967c5d8 100644 --- a/pages/features/autokill.mdx +++ b/pages/features/autokill.mdx @@ -31,7 +31,7 @@ unikraft instance create --metro fra \ ```bash title="API" curl -X POST \ - -H "Authorization: Bearer $UKC_TOKEN" \ + -H "Authorization: Bearer ${UKC_TOKEN}" \ -H "Content-Type: application/json" \ "https://api.fra.unikraft.cloud/v1/instances" \ -d '{ @@ -59,7 +59,7 @@ unikraft instance create --metro fra \ ```bash title="API" curl -X POST \ - -H "Authorization: Bearer $UKC_TOKEN" \ + -H "Authorization: Bearer ${UKC_TOKEN}" \ -H "Content-Type: application/json" \ "https://api.fra.unikraft.cloud/v1/instances" \ -d '{ @@ -91,7 +91,7 @@ unikraft api -X PATCH --metro fra \ ```bash title="API" curl -X PATCH \ - -H "Authorization: Bearer $UKC_TOKEN" \ + -H "Authorization: Bearer ${UKC_TOKEN}" \ -H "Content-Type: application/json" \ "https://api.fra.unikraft.cloud/v1/instances" \ -d '[{ @@ -119,7 +119,7 @@ unikraft service create --metro fra \ ```bash title="API" curl -X POST \ - -H "Authorization: Bearer $UKC_TOKEN" \ + -H "Authorization: Bearer ${UKC_TOKEN}" \ -H "Content-Type: application/json" \ "https://api.fra.unikraft.cloud/v1/services" \ -d '{ @@ -151,7 +151,7 @@ unikraft instance template create --metro fra \ ```bash title="API" curl -X POST \ - -H "Authorization: Bearer $UKC_TOKEN" \ + -H "Authorization: Bearer ${UKC_TOKEN}" \ -H "Content-Type: application/json" \ "https://api.fra.unikraft.cloud/v1/instances/templates" \ -d '{ diff --git a/pages/features/autoscale.mdx b/pages/features/autoscale.mdx index 1daf433a..700c9d87 100644 --- a/pages/features/autoscale.mdx +++ b/pages/features/autoscale.mdx @@ -70,40 +70,40 @@ The output shows the instance address and other details: ```ansi title="unikraft" -metro: fra -name: nginx-67zbu -uuid: 8a8bc1b9-0af6-420e-a426-190dc2da9eaa -state: starting -image: /nginx -resources: - memory: 256MiB - vcpus: 1 -service: - uuid: a942b9b5-ad17-3ffe-dcd2-ef4331f9087a - name: nameless-fog-0tvh1uov - domains: - - fqdn: nameless-fog-0tvh1uov.fra.unikraft.app -networks: -- uuid: 62d9bbf0-aec8-61f6-7bdb-86edf63dd068 - private-ip: 10.0.3.3 - mac: 12:b0:c6:23:ed:15 -timestamps: - created: just now +metro: fra +name: nginx-67zbu +uuid: 8a8bc1b9-0af6-420e-a426-190dc2da9eaa +state: starting +image: /nginx +resources: + memory: 256MiB + vcpus: 1 +service: + uuid: a942b9b5-ad17-3ffe-dcd2-ef4331f9087a + name: nameless-fog-0tvh1uov + domains: + - fqdn: nameless-fog-0tvh1uov.fra.unikraft.app +networks: +- uuid: 62d9bbf0-aec8-61f6-7bdb-86edf63dd068 + private-ip: 10.0.3.3 + mac: 12:b0:c6:23:ed:15 +timestamps: + created: just now ``` ```ansi title="kraft" -[●] Deployed successfully! - β”‚ - β”œ───────── name: nginx-67zbu - β”œ───────── uuid: 8a8bc1b9-0af6-420e-a426-190dc2da9eaa - β”œ──────── metro: https://api.fra.unikraft.cloud/v1 - β”œ──────── state: starting - β”œ─────── domain: https://nameless-fog-0tvh1uov.fra.unikraft.app - β”œ──────── image: oci://unikraft.io//nginx@sha256:f51ecc121c9ca34abb88a2bc6a69765501304f7893f7e85af15fbec3dc86e2bd - β”œ─────── memory: 256 MiB - β”œ────── service: nameless-fog-0tvh1uov - β”œ─ private fqdn: nginx-67zbu.internal - β””─── private ip: 10.0.3.3 +[●] Deployed successfully! + β”‚ + β”œ───────── name: nginx-67zbu + β”œ───────── uuid: 8a8bc1b9-0af6-420e-a426-190dc2da9eaa + β”œ──────── metro: https://api.fra.unikraft.cloud/v1 + β”œ──────── state: starting + β”œ─────── domain: https://nameless-fog-0tvh1uov.fra.unikraft.app + β”œ──────── image: oci://unikraft.io//nginx@sha256:f51ecc121c9ca34abb88a2bc6a69765501304f7893f7e85af15fbec3dc86e2bd + β”œ─────── memory: 256 MiB + β”œ────── service: nameless-fog-0tvh1uov + β”œ─ private fqdn: nginx-67zbu.internal + β””─── private ip: 10.0.3.3 ``` @@ -448,7 +448,7 @@ unikraft api /v1/services -X POST --metro fra \ ```bash title="API" curl -X POST \ - -H "Authorization: Bearer $UKC_TOKEN" \ + -H "Authorization: Bearer ${UKC_TOKEN}" \ -H "Content-Type: application/json" \ "https://api.fra.unikraft.cloud/v1/services" \ -d '{ @@ -489,7 +489,7 @@ unikraft api /v1/services -X POST --metro fra \ ```bash title="API" curl -X POST \ - -H "Authorization: Bearer $UKC_TOKEN" \ + -H "Authorization: Bearer ${UKC_TOKEN}" \ -H "Content-Type: application/json" \ "https://api.fra.unikraft.cloud/v1/services" \ -d '{ @@ -531,7 +531,7 @@ unikraft api /v1/services -X POST --metro fra \ ```bash title="API" curl -X POST \ - -H "Authorization: Bearer $UKC_TOKEN" \ + -H "Authorization: Bearer ${UKC_TOKEN}" \ -H "Content-Type: application/json" \ "https://api.fra.unikraft.cloud/v1/services" \ -d '{ diff --git a/pages/features/cron-jobs.mdx b/pages/features/cron-jobs.mdx index ac95cd23..f9c7fcaa 100644 --- a/pages/features/cron-jobs.mdx +++ b/pages/features/cron-jobs.mdx @@ -60,7 +60,7 @@ unikraft api /v1/instances -X POST --metro fra \ ``` ```bash title="API" curl -X POST \ - -H "Authorization: Bearer $UKC_TOKEN" \ + -H "Authorization: Bearer ${UKC_TOKEN}" \ -H "Content-Type: application/json" \ "https://api.fra.unikraft.cloud/v1/instances" \ -d '{ @@ -101,7 +101,7 @@ unikraft api /v1/instances -X POST --metro fra \ ``` ```bash title="API" curl -X POST \ - -H "Authorization: Bearer $UKC_TOKEN" \ + -H "Authorization: Bearer ${UKC_TOKEN}" \ -H "Content-Type: application/json" \ "https://api.fra.unikraft.cloud/v1/instances" \ -d '{ @@ -140,7 +140,7 @@ unikraft api /v1/instances/ -X PATCH --metro fra \ ``` ```bash title="API" curl -X PATCH \ - -H "Authorization: Bearer $UKC_TOKEN" \ + -H "Authorization: Bearer ${UKC_TOKEN}" \ -H "Content-Type: application/json" \ "https://api.fra.unikraft.cloud/v1/instances/" \ -d '{ @@ -178,7 +178,7 @@ unikraft api /v1/instances/ -X PATCH --metro fra \ ``` ```bash title="API" curl -X PATCH \ - -H "Authorization: Bearer $UKC_TOKEN" \ + -H "Authorization: Bearer ${UKC_TOKEN}" \ -H "Content-Type: application/json" \ "https://api.fra.unikraft.cloud/v1/instances/" \ -d '{ @@ -210,7 +210,7 @@ unikraft api /v1/instances/ -X PATCH --metro fra \ ``` ```bash title="API" curl -X PATCH \ - -H "Authorization: Bearer $UKC_TOKEN" \ + -H "Authorization: Bearer ${UKC_TOKEN}" \ -H "Content-Type: application/json" \ "https://api.fra.unikraft.cloud/v1/instances/" \ -d '{ diff --git a/pages/features/load-balancing.mdx b/pages/features/load-balancing.mdx index 8033837d..1cff479c 100644 --- a/pages/features/load-balancing.mdx +++ b/pages/features/load-balancing.mdx @@ -63,40 +63,40 @@ This single command will (a) create a service via the `-p` flag and (b) start an ```ansi title="unikraft" -metro: fra -name: nginx-67zbu -uuid: 8a8bc1b9-0af6-420e-a426-190dc2da9eaa -state: starting -image: /nginx -resources: - memory: 256MiB - vcpus: 1 -service: - uuid: a942b9b5-ad17-3ffe-dcd2-ef4331f9087a - name: nameless-fog-0tvh1uov - domains: - - fqdn: nameless-fog-0tvh1uov.fra.unikraft.app -networks: -- uuid: 62d9bbf0-aec8-61f6-7bdb-86edf63dd068 - private-ip: 10.0.3.3 - mac: 12:b0:c6:23:ed:15 -timestamps: - created: just now +metro: fra +name: nginx-67zbu +uuid: 8a8bc1b9-0af6-420e-a426-190dc2da9eaa +state: starting +image: /nginx +resources: + memory: 256MiB + vcpus: 1 +service: + uuid: a942b9b5-ad17-3ffe-dcd2-ef4331f9087a + name: nameless-fog-0tvh1uov + domains: + - fqdn: nameless-fog-0tvh1uov.fra.unikraft.app +networks: +- uuid: 62d9bbf0-aec8-61f6-7bdb-86edf63dd068 + private-ip: 10.0.3.3 + mac: 12:b0:c6:23:ed:15 +timestamps: + created: just now ``` ```ansi title="kraft" -[●] Deployed successfully! - β”‚ - β”œ───────── name: nginx-67zbu - β”œ───────── uuid: 8a8bc1b9-0af6-420e-a426-190dc2da9eaa - β”œ──────── metro: https://api.fra.unikraft.cloud/v1 - β”œ──────── state: starting - β”œ─────── domain: https://nameless-fog-0tvh1uov.fra.unikraft.app - β”œ──────── image: oci://unikraft.io//nginx@sha256:f51ecc121c9ca34abb88a2bc6a69765501304f7893f7e85af15fbec3dc86e2bd - β”œ─────── memory: 256 MiB - β”œ────── service: nameless-fog-0tvh1uov - β”œ─ private fqdn: nginx-67zbu.internal - β””─── private ip: 10.0.3.3 +[●] Deployed successfully! + β”‚ + β”œ───────── name: nginx-67zbu + β”œ───────── uuid: 8a8bc1b9-0af6-420e-a426-190dc2da9eaa + β”œ──────── metro: https://api.fra.unikraft.cloud/v1 + β”œ──────── state: starting + β”œ─────── domain: https://nameless-fog-0tvh1uov.fra.unikraft.app + β”œ──────── image: oci://unikraft.io//nginx@sha256:f51ecc121c9ca34abb88a2bc6a69765501304f7893f7e85af15fbec3dc86e2bd + β”œ─────── memory: 256 MiB + β”œ────── service: nameless-fog-0tvh1uov + β”œ─ private fqdn: nginx-67zbu.internal + β””─── private ip: 10.0.3.3 ``` @@ -132,40 +132,40 @@ You should see another instance starting up, with the same service and domain as ```ansi title="unikraft" -metro: fra -name: nginx-0s295 -uuid: 9b1c8e5c-7a0d-4f1e-9c3a-2b8d9f0e5a1f -state: starting -image: /nginx -resources: - memory: 256MiB - vcpus: 1 -service: - uuid: a942b9b5-ad17-3ffe-dcd2-ef4331f9087a - name: nameless-fog-0tvh1uov - domains: - - fqdn: nameless-fog-0tvh1uov.fra.unikraft.app -networks: -- uuid: adf1f17d-bad1-49b2-a126-ed730e06379a - private-ip: 10.0.1.4 - mac: 12:b0:0a:00:01:05 -timestamps: - created: just now +metro: fra +name: nginx-0s295 +uuid: 9b1c8e5c-7a0d-4f1e-9c3a-2b8d9f0e5a1f +state: starting +image: /nginx +resources: + memory: 256MiB + vcpus: 1 +service: + uuid: a942b9b5-ad17-3ffe-dcd2-ef4331f9087a + name: nameless-fog-0tvh1uov + domains: + - fqdn: nameless-fog-0tvh1uov.fra.unikraft.app +networks: +- uuid: adf1f17d-bad1-49b2-a126-ed730e06379a + private-ip: 10.0.1.4 + mac: 12:b0:0a:00:01:05 +timestamps: + created: just now ``` ```ansi title="kraft" -[●] Deployed successfully! - β”‚ - β”œ───────── name: nginx-0s295 - β”œ───────── uuid: 9b1c8e5c-7a0d-4f1e-9c3a-2b8d9f0e5a1f - β”œ──────── metro: https://api.fra.unikraft.cloud/v1 - β”œ──────── state: starting - β”œ─────── domain: https://nameless-fog-0tvh1uov.fra.unikraft.app - β”œ──────── image: oci://unikraft.io//nginx@sha256:f51ecc121c9ca34abb88a2bc6a69765501304f7893f7e85af15fbec3dc86e2bd - β”œ─────── memory: 256 MiB - β”œ────── service: nameless-fog-0tvh1uov - β”œ─ private fqdn: nginx-0s295.internal - β””─── private ip: 10.0.1.4 +[●] Deployed successfully! + β”‚ + β”œ───────── name: nginx-0s295 + β”œ───────── uuid: 9b1c8e5c-7a0d-4f1e-9c3a-2b8d9f0e5a1f + β”œ──────── metro: https://api.fra.unikraft.cloud/v1 + β”œ──────── state: starting + β”œ─────── domain: https://nameless-fog-0tvh1uov.fra.unikraft.app + β”œ──────── image: oci://unikraft.io//nginx@sha256:f51ecc121c9ca34abb88a2bc6a69765501304f7893f7e85af15fbec3dc86e2bd + β”œ─────── memory: 256 MiB + β”œ────── service: nameless-fog-0tvh1uov + β”œ─ private fqdn: nginx-0s295.internal + β””─── private ip: 10.0.1.4 ``` @@ -189,26 +189,26 @@ You should see output such as: ```ansi title="unikraft" -metro: fra -name: nameless-fog-0tvh1uov -uuid: a942b9b5-ad17-3ffe-dcd2-ef4331f9087a -persistent: true -limits: - soft: 1 - hard: 65535 -timestamps: - created: just now -domains: -- fqdn: nameless-fog-0tvh1uov.fra.unikraft.app -instances: -- name: nginx-0s295 - uuid: 9b1c8e5c-7a0d-4f1e-9c3a-2b8d9f0e5a1f -- name: nginx-67zbu - uuid: 8a8bc1b9-0af6-420e-a426-190dc2da9eaa -services: -- source: 443 - destination: 8080 - handlers: ["tls", "http"] +metro: fra +name: nameless-fog-0tvh1uov +uuid: a942b9b5-ad17-3ffe-dcd2-ef4331f9087a +persistent: true +limits: + soft: 1 + hard: 65535 +timestamps: + created: just now +domains: +- fqdn: nameless-fog-0tvh1uov.fra.unikraft.app +instances: +- name: nginx-0s295 + uuid: 9b1c8e5c-7a0d-4f1e-9c3a-2b8d9f0e5a1f +- name: nginx-67zbu + uuid: 8a8bc1b9-0af6-420e-a426-190dc2da9eaa +services: +- source: 443 + destination: 8080 + handlers: ["tls", "http"] ``` ```ansi title="kraft" diff --git a/pages/features/roms.mdx b/pages/features/roms.mdx index bcf37e6b..4b33c61c 100644 --- a/pages/features/roms.mdx +++ b/pages/features/roms.mdx @@ -334,7 +334,7 @@ unikraft run --metro fra \ ```bash title="API" curl -X POST \ - -H "Authorization: Bearer $UKC_TOKEN" \ + -H "Authorization: Bearer ${UKC_TOKEN}" \ -H "Content-Type: application/json" \ "https://api.fra.unikraft.cloud/v1/instances" \ -d '[ @@ -486,7 +486,7 @@ Add a `files` array to a ROM entry instead of an `image` field: ```bash title="POST /instances" curl -X POST \ - -H "Authorization: Bearer $UKC_TOKEN" \ + -H "Authorization: Bearer ${UKC_TOKEN}" \ -H "Content-Type: application/json" \ "https://api.fra.unikraft.cloud/v1/instances" \ -d '{ @@ -529,7 +529,7 @@ You can mix inline ROMs and image-based ROMs in the same request: ```bash title="POST /instances" curl -X POST \ - -H "Authorization: Bearer $UKC_TOKEN" \ + -H "Authorization: Bearer ${UKC_TOKEN}" \ -H "Content-Type: application/json" \ "https://api.fra.unikraft.cloud/v1/instances" \ -d '{ diff --git a/pages/features/scale-to-zero.mdx b/pages/features/scale-to-zero.mdx index 2135906e..ac445e4f 100644 --- a/pages/features/scale-to-zero.mdx +++ b/pages/features/scale-to-zero.mdx @@ -120,7 +120,7 @@ unikraft run --metro fra \ ```bash title="API" curl -X POST \ - -H "Authorization: Bearer $UKC_TOKEN" \ + -H "Authorization: Bearer ${UKC_TOKEN}" \ -H "Content-Type: application/json" \ "https://api.fra.unikraft.cloud/v1/instances" \ -d '{ @@ -234,44 +234,44 @@ This command will create the NGINX instance with scale-to-zero enabled, with a c ```ansi title="unikraft" -metro: fra -name: nginx-67zbu -uuid: 8a8bc1b9-0af6-420e-a426-190dc2da9eaa -state: starting -image: /nginx -resources: - memory: 256MiB - vcpus: 1 -service: - uuid: a942b9b5-ad17-3ffe-dcd2-ef4331f9087a - name: nameless-fog-0tvh1uov - domains: - - fqdn: nameless-fog-0tvh1uov.fra.unikraft.app -networks: -- uuid: 62d9bbf0-aec8-61f6-7bdb-86edf63dd068 - private-ip: 10.0.3.3 - mac: 12:b0:c6:23:ed:15 -timestamps: - created: just now -scale-to-zero: - enabled: true - policy: on - cooldown-time: 1s +metro: fra +name: nginx-67zbu +uuid: 8a8bc1b9-0af6-420e-a426-190dc2da9eaa +state: starting +image: /nginx +resources: + memory: 256MiB + vcpus: 1 +service: + uuid: a942b9b5-ad17-3ffe-dcd2-ef4331f9087a + name: nameless-fog-0tvh1uov + domains: + - fqdn: nameless-fog-0tvh1uov.fra.unikraft.app +networks: +- uuid: 62d9bbf0-aec8-61f6-7bdb-86edf63dd068 + private-ip: 10.0.3.3 + mac: 12:b0:c6:23:ed:15 +timestamps: + created: just now +scale-to-zero: + enabled: true + policy: on + cooldown-time: 1s ``` ```ansi title="kraft" -[●] Deployed successfully! - β”‚ - β”œ───────── name: nginx-67zbu - β”œ───────── uuid: 8a8bc1b9-0af6-420e-a426-190dc2da9eaa - β”œ──────── metro: https://api.fra.unikraft.cloud/v1 - β”œ──────── state: starting - β”œ─────── domain: https://nameless-fog-0tvh1uov.fra.unikraft.app - β”œ──────── image: oci://unikraft.io//nginx@sha256:f51ecc121c9ca34abb88a2bc6a69765501304f7893f7e85af15fbec3dc86e2bd - β”œ─────── memory: 256 MiB - β”œ────── service: nameless-fog-0tvh1uov - β”œ─ private fqdn: nginx-67zbu.internal - β””─── private ip: 10.0.3.3 +[●] Deployed successfully! + β”‚ + β”œ───────── name: nginx-67zbu + β”œ───────── uuid: 8a8bc1b9-0af6-420e-a426-190dc2da9eaa + β”œ──────── metro: https://api.fra.unikraft.cloud/v1 + β”œ──────── state: starting + β”œ─────── domain: https://nameless-fog-0tvh1uov.fra.unikraft.app + β”œ──────── image: oci://unikraft.io//nginx@sha256:f51ecc121c9ca34abb88a2bc6a69765501304f7893f7e85af15fbec3dc86e2bd + β”œ─────── memory: 256 MiB + β”œ────── service: nameless-fog-0tvh1uov + β”œ─ private fqdn: nginx-67zbu.internal + β””─── private ip: 10.0.3.3 ``` @@ -297,12 +297,12 @@ You should see output like: ```ansi title="unikraft" METRO NAME STATE IMAGE ARGS MEMORY VCPUS FQDN CREATED -fra nginx-67zbu standby /nginx 256MiB 1 nameless-fog-0tvh1uov.fra.unikraft.app 2 minutes ago +fra nginx-67zbu standby /nginx 256MiB 1 nameless-fog-0tvh1uov.fra.unikraft.app 2 minutes ago ``` ```ansi title="kraft" NAME FQDN STATE STATUS IMAGE MEMORY VCPUS ARGS BOOT TIME -nginx-67zbu nameless-fog-0tvh1uov.fra.unikraft.app standby 5 minutes ago oci://unikraft.io//nginx@sha256:... 256 MiB 1 211.13 ms +nginx-67zbu nameless-fog-0tvh1uov.fra.unikraft.app standby 5 minutes ago oci://unikraft.io//nginx@sha256:... 256 MiB 1 211.13 ms ``` @@ -330,32 +330,32 @@ which outputs: ```ansi title="unikraft" -metro: fra -name: nginx-67zbu -uuid: 8a8bc1b9-0af6-420e-a426-190dc2da9eaa -state: standby -image: /nginx -resources: - memory: 256MiB - vcpus: 1 -service: - uuid: a942b9b5-ad17-3ffe-dcd2-ef4331f9087a - name: nameless-fog-0tvh1uov - domains: - - fqdn: nameless-fog-0tvh1uov.fra.unikraft.app -networks: -- uuid: 62d9bbf0-aec8-61f6-7bdb-86edf63dd068 - private-ip: 10.0.3.3 - mac: 12:b0:c6:23:ed:15 -timestamps: - created: just now -scale-to-zero: - enabled: true - policy: on - cooldown-time: 1s -stop: - reason: platform stop - exit-code: 0 +metro: fra +name: nginx-67zbu +uuid: 8a8bc1b9-0af6-420e-a426-190dc2da9eaa +state: standby +image: /nginx +resources: + memory: 256MiB + vcpus: 1 +service: + uuid: a942b9b5-ad17-3ffe-dcd2-ef4331f9087a + name: nameless-fog-0tvh1uov + domains: + - fqdn: nameless-fog-0tvh1uov.fra.unikraft.app +networks: +- uuid: 62d9bbf0-aec8-61f6-7bdb-86edf63dd068 + private-ip: 10.0.3.3 + mac: 12:b0:c6:23:ed:15 +timestamps: + created: just now +scale-to-zero: + enabled: true + policy: on + cooldown-time: 1s +stop: + reason: platform stop + exit-code: 0 ``` ```ansi title="kraft" @@ -364,7 +364,7 @@ stop: fqdn: nameless-fog-0tvh1uov.fra.unikraft.app private fqdn: nginx-67zbu.internal private ip: 10.0.3.3 - state: standby + state: standby created: 2026-06-05T12:16:10Z started: 2026-06-05T12:16:10Z stopped: 2026-06-05T12:16:13Z diff --git a/pages/integrations/kubernetes.mdx b/pages/integrations/kubernetes.mdx index 23486bc9..8b83b390 100644 --- a/pages/integrations/kubernetes.mdx +++ b/pages/integrations/kubernetes.mdx @@ -36,8 +36,8 @@ You can install Kraftlet into a Kubernetes cluster using its Helm chart: helm install kraftlet \ --namespace kraftlet \ --create-namespace \ - --set ukc.metro=$UKC_METRO \ - --set ukc.token=$UKC_TOKEN \ + --set ukc.metro=${UKC_METRO} \ + --set ukc.token=${UKC_TOKEN} \ oci://ghcr.io/unikraft-cloud/helm-charts/kraftlet ``` @@ -50,7 +50,7 @@ kubectl get pods -n kraftlet Which should return a single pod running: ```ansi title="" -NAME READY STATUS RESTARTS AGE +NAME READY STATUS RESTARTS AGE kraftlet-74666cf7f5-nbkw7 1/1 Running 0 38s ``` @@ -63,7 +63,7 @@ kubectl get nodes Which should, among other nodes, return Kraftlet. ```ansi title="" -NAME STATUS ROLES AGE VERSION +NAME STATUS ROLES AGE VERSION Kraftlet Ready agent 58s No version provided ``` @@ -137,7 +137,7 @@ kubectl get pods ``` ```ansi title="" -NAME READY STATUS RESTARTS AGE +NAME READY STATUS RESTARTS AGE my-app-78c766fb67-k2dgk 1/1 Running 0 104s my-app-78c766fb67-mfp77 1/1 Running 0 104s my-app-78c766fb67-tkkxq 1/1 Running 0 104s @@ -163,17 +163,17 @@ Which will return a list of instances created from pods above: ```ansi title="unikraft" -METRO NAME STATE IMAGE ARGS MEMORY VCPUS FQDN CREATED -fra my-app-78c766fb67-k2dgk-nginx running /nginx 128MiB 1 my-service-orjlyrac.fra.unikraft.app 2 minutes ago -fra my-app-78c766fb67-tkkxq-nginx running /nginx 128MiB 1 my-service-orjlyrac.fra.unikraft.app 2 minutes ago -fra my-app-78c766fb67-mfp77-nginx running /nginx 128MiB 1 my-service-orjlyrac.fra.unikraft.app 2 minutes ago +METRO NAME STATE IMAGE ARGS MEMORY VCPUS FQDN CREATED +fra my-app-78c766fb67-k2dgk-nginx running /nginx 128MiB 1 my-service-orjlyrac.fra.unikraft.app 2 minutes ago +fra my-app-78c766fb67-tkkxq-nginx running /nginx 128MiB 1 my-service-orjlyrac.fra.unikraft.app 2 minutes ago +fra my-app-78c766fb67-mfp77-nginx running /nginx 128MiB 1 my-service-orjlyrac.fra.unikraft.app 2 minutes ago ``` ```ansi title="kraft" -NAME FQDN STATE STATUS IMAGE MEMORY VCPUS ARGS BOOT TIME -my-app-78c766fb67-k2dgk-nginx my-service-orjlyrac.fra.unikraft.app running since 24secs oci://unikraft.io//nginx@sha256:49d8fb7a9934a87e93f9... 128 MiB 1 14.06 ms -my-app-78c766fb67-tkkxq-nginx my-service-orjlyrac.fra.unikraft.app running since 24secs oci://unikraft.io//nginx@sha256:49d8fb7a9934a87e93f9... 128 MiB 1 14.43 ms -my-app-78c766fb67-mfp77-nginx my-service-orjlyrac.fra.unikraft.app running since 25secs oci://unikraft.io//nginx@sha256:49d8fb7a9934a87e93f9... 128 MiB 1 15.12 ms +NAME FQDN STATE STATUS IMAGE MEMORY VCPUS ARGS BOOT TIME +my-app-78c766fb67-k2dgk-nginx my-service-orjlyrac.fra.unikraft.app running since 24secs oci://unikraft.io//nginx@sha256:49d8fb7a9934a87e93f9... 128 MiB 1 14.06 ms +my-app-78c766fb67-tkkxq-nginx my-service-orjlyrac.fra.unikraft.app running since 24secs oci://unikraft.io//nginx@sha256:49d8fb7a9934a87e93f9... 128 MiB 1 14.43 ms +my-app-78c766fb67-mfp77-nginx my-service-orjlyrac.fra.unikraft.app running since 25secs oci://unikraft.io//nginx@sha256:49d8fb7a9934a87e93f9... 128 MiB 1 15.12 ms ``` @@ -197,12 +197,12 @@ kraft cloud service list ```ansi title="unikraft" -NAME NAME AUTOSCALE CREATED FQDN SOURCE DESTINATION HANDLERS +NAME NAME AUTOSCALE CREATED FQDN SOURCE DESTINATION HANDLERS my-service my-service-orjlyrac false 6 minutes ago my-service-orjlyrac.fra.unikraft.app 443 8080 ["tls", "http"] ``` ```ansi title="kraft" -NAME FQDN SERVICES INSTANCES CREATED AT PERSISTENT +NAME FQDN SERVICES INSTANCES CREATED AT PERSISTENT my-service my-service-orjlyrac.fra.unikraft.app 443:8080/tls+http my-app-78c766fb67-k2dgk-nginx my-app-78c766fb67-tkkxq-nginx my-a... 6 minutes ago true ``` @@ -239,7 +239,7 @@ kubectl get pvc ``` ```ansi title="" -NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE +NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE my-claim Bound pv-7ab06383-ac03-4a81-968a-1b0cff03c23a 10Mi RWO ukc-volume 4s ``` @@ -260,12 +260,12 @@ kraft cloud volume list ```ansi title="unikraft" -METRO NAME STATE SIZE CREATED -fra my-claim available 10MiB 5 minutes ago +METRO NAME STATE SIZE CREATED +fra my-claim available 10MiB 5 minutes ago ``` ```ansi title="kraft" -NAME CREATED AT SIZE ATTACHED TO MOUNTED BY STATE PERSISTENT +NAME CREATED AT SIZE ATTACHED TO MOUNTED BY STATE PERSISTENT my-claim 5 minutes ago 10 MiB available true ``` @@ -319,13 +319,13 @@ kraft cloud instance list ```ansi title="unikraft" -METRO NAME STATE IMAGE ARGS MEMORY VCPUS FQDN CREATED -fra nginx-pod-nginx running /nginx 128 MiB 1 fragrant-breeze-llesxdta.fra.unikraft.app 1 minute ago +METRO NAME STATE IMAGE ARGS MEMORY VCPUS FQDN CREATED +fra nginx-pod-nginx running /nginx 128 MiB 1 fragrant-breeze-llesxdta.fra.unikraft.app 1 minute ago ``` ```ansi title="kraft" -NAME FQDN STATE STATUS IMAGE MEMORY VCPUS ARGS BOOT TIME -nginx-pod-nginx fragrant-breeze-llesxdta.fra.unikraft.app running since 1min oci://unikraft.io//nginx@sha256:49d8fb7a9934a87e93f9eb326... 128 MiB 1 15.14 ms +NAME FQDN STATE STATUS IMAGE MEMORY VCPUS ARGS BOOT TIME +nginx-pod-nginx fragrant-breeze-llesxdta.fra.unikraft.app running since 1min oci://unikraft.io//nginx@sha256:49d8fb7a9934a87e93f9eb326... 128 MiB 1 15.14 ms ``` @@ -347,12 +347,12 @@ kraft cloud volume list ```ansi title="unikraft" -METRO NAME STATE SIZE CREATED -fra my-claim mounted 10MiB 8 minutes ago +METRO NAME STATE SIZE CREATED +fra my-claim mounted 10MiB 8 minutes ago ``` ```ansi title="kraft" -NAME CREATED AT SIZE ATTACHED TO MOUNTED BY STATE PERSISTENT +NAME CREATED AT SIZE ATTACHED TO MOUNTED BY STATE PERSISTENT my-claim 8 minutes ago 10 MiB nginx-pod-nginx nginx-pod-nginx mounted true ``` diff --git a/pages/platform/certificates.mdx b/pages/platform/certificates.mdx index 90766201..a55f6b4c 100644 --- a/pages/platform/certificates.mdx +++ b/pages/platform/certificates.mdx @@ -60,13 +60,13 @@ You should see output like: ```ansi title="unikraft" -METRO NAME COMMON-NAME STATE CREATED NOT-AFTER +METRO NAME COMMON-NAME STATE CREATED NOT-AFTER fra mydomain-cert demo.unikraft.dev valid 1 minute ago in 12 months fra mydomain.com-sa4x9 *.mydomain.com valid 5 days ago in 12 months ``` ```ansi title="kraft" -NAME STATE COMMON NAME CREATED AT +NAME STATE COMMON NAME CREATED AT mydomain-cert valid demo.unikraft.dev 1 minute ago mydomain.com-sa4x9 valid *.mydomain.com 5 days ago ``` @@ -92,18 +92,18 @@ You should see output like: ```ansi title="unikraft" -metro: fra -name: mydomain-cert -uuid: 9fabfc61-a168-439e-89e2-0d5308ee9f13 -common-name: demo.unikraft.dev -subject: CN=demo.unikraft.dev -issuer: CN=demo.unikraft.dev -serial-number: 43C149067F939CD41100A74905B80560C315E92F -state: valid -timestamps: - created: just now - not-before: 1 minute ago - not-after: in 12 months +metro: fra +name: mydomain-cert +uuid: 9fabfc61-a168-439e-89e2-0d5308ee9f13 +common-name: demo.unikraft.dev +subject: CN=demo.unikraft.dev +issuer: CN=demo.unikraft.dev +serial-number: 43C149067F939CD41100A74905B80560C315E92F +state: valid +timestamps: + created: just now + not-before: 1 minute ago + not-after: in 12 months ``` ```ansi title="kraft" @@ -153,43 +153,43 @@ kraft cloud instance create \ ```ansi title="unikraft" -metro: fra -name: nginx-67zbu -uuid: 8a8bc1b9-0af6-420e-a426-190dc2da9eaa -state: starting -image: /nginx -resources: - memory: 256MiB - vcpus: 1 -service: - uuid: a942b9b5-ad17-3ffe-dcd2-ef4331f9087a - name: nameless-fog-0tvh1uov - domains: - - fqdn: demo.unikraft.dev - certificate: - name: mydomain-cert - uuid: 9fabfc61-a168-439e-89e2-0d5308ee9f13 -networks: -- uuid: 62d9bbf0-aec8-61f6-7bdb-86edf63dd068 - private-ip: 10.0.3.3 - mac: 12:b0:c6:23:ed:15 -timestamps: - created: just now +metro: fra +name: nginx-67zbu +uuid: 8a8bc1b9-0af6-420e-a426-190dc2da9eaa +state: starting +image: /nginx +resources: + memory: 256MiB + vcpus: 1 +service: + uuid: a942b9b5-ad17-3ffe-dcd2-ef4331f9087a + name: nameless-fog-0tvh1uov + domains: + - fqdn: demo.unikraft.dev + certificate: + name: mydomain-cert + uuid: 9fabfc61-a168-439e-89e2-0d5308ee9f13 +networks: +- uuid: 62d9bbf0-aec8-61f6-7bdb-86edf63dd068 + private-ip: 10.0.3.3 + mac: 12:b0:c6:23:ed:15 +timestamps: + created: just now ``` ```ansi title="kraft" -[●] Deployed successfully! - β”‚ - β”œ───────── name: nginx-67zbu - β”œ───────── uuid: 8a8bc1b9-0af6-420e-a426-190dc2da9eaa - β”œ──────── metro: https://api.fra.unikraft.cloud/v1 - β”œ──────── state: starting - β”œ─────── domain: https://demo.unikraft.dev - β”œ──────── image: oci://unikraft.io//nginx@sha256:f51ecc121c9ca34abb88a2bc6a69765501304f7893f7e85af15fbec3dc86e2bd - β”œ─────── memory: 256 MiB - β”œ────── service: nameless-fog-0tvh1uov - β”œ─ private fqdn: nginx-67zbu.internal - β””─── private ip: 10.0.3.3 +[●] Deployed successfully! + β”‚ + β”œ───────── name: nginx-67zbu + β”œ───────── uuid: 8a8bc1b9-0af6-420e-a426-190dc2da9eaa + β”œ──────── metro: https://api.fra.unikraft.cloud/v1 + β”œ──────── state: starting + β”œ─────── domain: https://demo.unikraft.dev + β”œ──────── image: oci://unikraft.io//nginx@sha256:f51ecc121c9ca34abb88a2bc6a69765501304f7893f7e85af15fbec3dc86e2bd + β”œ─────── memory: 256 MiB + β”œ────── service: nameless-fog-0tvh1uov + β”œ─ private fqdn: nginx-67zbu.internal + β””─── private ip: 10.0.3.3 ``` diff --git a/pages/platform/delete-locks.mdx b/pages/platform/delete-locks.mdx index f67a705c..4e0d3d61 100644 --- a/pages/platform/delete-locks.mdx +++ b/pages/platform/delete-locks.mdx @@ -44,7 +44,7 @@ unikraft api /v1/instances -X PATCH --metro fra \ ``` ```bash title="API" curl -X PATCH \ - -H "Authorization: Bearer $UKC_TOKEN" \ + -H "Authorization: Bearer ${UKC_TOKEN}" \ -H "Content-Type: application/json" \ "https://api.fra.unikraft.cloud/v1/instances" \ -d '[{ @@ -71,7 +71,7 @@ unikraft api /v1/instances/templates -X PATCH --metro fra \ ``` ```bash title="API" curl -X PATCH \ - -H "Authorization: Bearer $UKC_TOKEN" \ + -H "Authorization: Bearer ${UKC_TOKEN}" \ -H "Content-Type: application/json" \ "https://api.fra.unikraft.cloud/v1/instances/templates" \ -d '[{ @@ -97,7 +97,7 @@ unikraft api /v1/volumes -X PATCH --metro fra \ ``` ```bash title="API" curl -X PATCH \ - -H "Authorization: Bearer $UKC_TOKEN" \ + -H "Authorization: Bearer ${UKC_TOKEN}" \ -H "Content-Type: application/json" \ "https://api.fra.unikraft.cloud/v1/volumes" \ -d '[{ @@ -123,7 +123,7 @@ unikraft api /v1/volumes/templates -X PATCH --metro fra \ ``` ```bash title="API" curl -X PATCH \ - -H "Authorization: Bearer $UKC_TOKEN" \ + -H "Authorization: Bearer ${UKC_TOKEN}" \ -H "Content-Type: application/json" \ "https://api.fra.unikraft.cloud/v1/volumes/templates" \ -d '[{ @@ -162,7 +162,7 @@ unikraft api /v1/instances -X PATCH --metro fra \ ``` ```bash title="API" curl -X PATCH \ - -H "Authorization: Bearer $UKC_TOKEN" \ + -H "Authorization: Bearer ${UKC_TOKEN}" \ -H "Content-Type: application/json" \ "https://api.fra.unikraft.cloud/v1/instances" \ -d '[{ @@ -188,7 +188,7 @@ unikraft api /v1/instances/templates -X PATCH --metro fra \ ``` ```bash title="API" curl -X PATCH \ - -H "Authorization: Bearer $UKC_TOKEN" \ + -H "Authorization: Bearer ${UKC_TOKEN}" \ -H "Content-Type: application/json" \ "https://api.fra.unikraft.cloud/v1/instances/templates" \ -d '[{ @@ -214,7 +214,7 @@ unikraft api /v1/volumes -X PATCH --metro fra \ ``` ```bash title="API" curl -X PATCH \ - -H "Authorization: Bearer $UKC_TOKEN" \ + -H "Authorization: Bearer ${UKC_TOKEN}" \ -H "Content-Type: application/json" \ "https://api.fra.unikraft.cloud/v1/volumes" \ -d '[{ @@ -240,7 +240,7 @@ unikraft api /v1/volumes/templates -X PATCH --metro fra \ ``` ```bash title="API" curl -X PATCH \ - -H "Authorization: Bearer $UKC_TOKEN" \ + -H "Authorization: Bearer ${UKC_TOKEN}" \ -H "Content-Type: application/json" \ "https://api.fra.unikraft.cloud/v1/volumes/templates" \ -d '[{ diff --git a/pages/platform/domains.mdx b/pages/platform/domains.mdx index 77cb2376..9c298666 100644 --- a/pages/platform/domains.mdx +++ b/pages/platform/domains.mdx @@ -102,43 +102,43 @@ The resulting output of the deploy command should be like: ```ansi title="unikraft" -metro: fra -name: nginx-67zbu -uuid: 8a8bc1b9-0af6-420e-a426-190dc2da9eaa -state: starting -image: /nginx -resources: - memory: 256MiB - vcpus: 1 -service: - uuid: a942b9b5-ad17-3ffe-dcd2-ef4331f9087a - name: nameless-fog-0tvh1uov - domains: - - fqdn: mydomain.com - certificate: - name: mydomain.com-savtl - uuid: 51efebaf-4858-450c-8ba5-8bd1df96c20b -networks: -- uuid: 62d9bbf0-aec8-61f6-7bdb-86edf63dd068 - private-ip: 10.0.3.3 - mac: 12:b0:c6:23:ed:15 -timestamps: - created: just now +metro: fra +name: nginx-67zbu +uuid: 8a8bc1b9-0af6-420e-a426-190dc2da9eaa +state: starting +image: /nginx +resources: + memory: 256MiB + vcpus: 1 +service: + uuid: a942b9b5-ad17-3ffe-dcd2-ef4331f9087a + name: nameless-fog-0tvh1uov + domains: + - fqdn: mydomain.com + certificate: + name: mydomain.com-savtl + uuid: 51efebaf-4858-450c-8ba5-8bd1df96c20b +networks: +- uuid: 62d9bbf0-aec8-61f6-7bdb-86edf63dd068 + private-ip: 10.0.3.3 + mac: 12:b0:c6:23:ed:15 +timestamps: + created: just now ``` ```ansi title="kraft" -[●] Deployed successfully! - β”‚ - β”œ───────── name: nginx-67zbu - β”œ───────── uuid: 8a8bc1b9-0af6-420e-a426-190dc2da9eaa - β”œ──────── metro: https://api.fra.unikraft.cloud/v1 - β”œ──────── state: starting - β”œ─────── domain: https://mydomain.com - β”œ──────── image: oci://unikraft.io//nginx@sha256:f51ecc121c9ca34abb88a2bc6a69765501304f7893f7e85af15fbec3dc86e2bd - β”œ─────── memory: 256 MiB - β”œ────── service: nameless-fog-0tvh1uov - β”œ─ private fqdn: nginx-67zbu.internal - β””─── private ip: 10.0.3.3 +[●] Deployed successfully! + β”‚ + β”œ───────── name: nginx-67zbu + β”œ───────── uuid: 8a8bc1b9-0af6-420e-a426-190dc2da9eaa + β”œ──────── metro: https://api.fra.unikraft.cloud/v1 + β”œ──────── state: starting + β”œ─────── domain: https://mydomain.com + β”œ──────── image: oci://unikraft.io//nginx@sha256:f51ecc121c9ca34abb88a2bc6a69765501304f7893f7e85af15fbec3dc86e2bd + β”œ─────── memory: 256 MiB + β”œ────── service: nameless-fog-0tvh1uov + β”œ─ private fqdn: nginx-67zbu.internal + β””─── private ip: 10.0.3.3 ``` @@ -163,12 +163,12 @@ You should see it first in `pending`: ```ansi title="unikraft" -METRO NAME COMMON-NAME STATE CREATED NOT-AFTER +METRO NAME COMMON-NAME STATE CREATED NOT-AFTER fra mydomain.com-savtl mydomain.com pending just now never ``` ```ansi title="kraft" -NAME STATE COMMON NAME CREATED AT +NAME STATE COMMON NAME CREATED AT mydomain.com-savtl pending mydomain.com just now ``` diff --git a/pages/platform/images.mdx b/pages/platform/images.mdx index 8befcdd2..b5966a49 100644 --- a/pages/platform/images.mdx +++ b/pages/platform/images.mdx @@ -137,40 +137,40 @@ The output should look like: ```ansi title="unikraft" -metro: fra -name: httpserver-python312-ma2i9 -uuid: e7389eee-9808-4152-b2ec-1f3c0541fd05 -state: running -image: /httpserver-python312 -resources: - memory: 512MiB - vcpus: 1 -service: - uuid: 51a41f63-7e88-c443-b9bf-83cd7c04d975 - name: young-night-5fpf0jj8 - domains: - - fqdn: young-night-5fpf0jj8.fra.unikraft.app -networks: -- uuid: 53da3490-c6f5-3718-25f1-219a65163c73 - private-ip: 10.0.3.3 - mac: 12:b0:18:0c:cb:aa -timestamps: - created: just now +metro: fra +name: httpserver-python312-ma2i9 +uuid: e7389eee-9808-4152-b2ec-1f3c0541fd05 +state: running +image: /httpserver-python312 +resources: + memory: 512MiB + vcpus: 1 +service: + uuid: 51a41f63-7e88-c443-b9bf-83cd7c04d975 + name: young-night-5fpf0jj8 + domains: + - fqdn: young-night-5fpf0jj8.fra.unikraft.app +networks: +- uuid: 53da3490-c6f5-3718-25f1-219a65163c73 + private-ip: 10.0.3.3 + mac: 12:b0:18:0c:cb:aa +timestamps: + created: just now ``` ```ansi title="kraft" -[●] Deployed successfully! - β”‚ - β”œ───────── name: httpserver-python312-ma2i9 - β”œ───────── uuid: e7389eee-9808-4152-b2ec-1f3c0541fd05 - β”œ──────── metro: https://api.fra.unikraft.cloud/v1 - β”œ──────── state: starting - β”œ─────── domain: https://young-night-5fpf0jj8.fra.unikraft.app - β”œ──────── image: oci://unikraft.io//httpserver-python312@sha256:278cb8b14f9faf9c2702dddd8bfb6124912d82c11b4a2c6590b6e32fc4049472 - β”œ─────── memory: 512 MiB - β”œ────── service: young-night-5fpf0jj8 - β”œ─ private fqdn: httpserver-python312-ma2i9.internal - β””─── private ip: 10.0.3.3 +[●] Deployed successfully! + β”‚ + β”œ───────── name: httpserver-python312-ma2i9 + β”œ───────── uuid: e7389eee-9808-4152-b2ec-1f3c0541fd05 + β”œ──────── metro: https://api.fra.unikraft.cloud/v1 + β”œ──────── state: starting + β”œ─────── domain: https://young-night-5fpf0jj8.fra.unikraft.app + β”œ──────── image: oci://unikraft.io//httpserver-python312@sha256:278cb8b14f9faf9c2702dddd8bfb6124912d82c11b4a2c6590b6e32fc4049472 + β”œ─────── memory: 512 MiB + β”œ────── service: young-night-5fpf0jj8 + β”œ─ private fqdn: httpserver-python312-ma2i9.internal + β””─── private ip: 10.0.3.3 ``` @@ -198,12 +198,12 @@ You should see output like: ```ansi title="unikraft" -REF DIGEST +REF DIGEST /httpserver-python312 sha256:278cb8b14f9faf9c2702dddd8bfb6124912d82c11b4a2c6590b6e32fc4049472 ``` ```ansi title="kraft" -NAME VERSION SIZE +NAME VERSION SIZE /httpserver-python312 latest 114 MB ``` @@ -252,80 +252,80 @@ kraft cloud deploy \ ```ansi title="unikraft" -metro: fra -name: httpserver-python312-lscmn -uuid: 1121225d-a678-45a2-a8ce-335a9a6a2683 -state: running -image: /httpserver-python312 -resources: - memory: 512MiB - vcpus: 1 -service: - name: restless-orangutan-1lklj6z5 - uuid: 16f4b86d-df8f-452c-b189-41ec34250e58 - domains: - - fqdn: restless-orangutan-1lklj6z5.fra.unikraft.app -networks: -- uuid: da9445a3-c7b7-4327-8499-3bd7001cd829 - private-ip: 10.0.6.37 - mac: 12:b0:0a:00:06:25 -timestamps: - created: just now - -metro: fra -name: httpserver-python312-xfgvc -uuid: d021379d-9e03-4d0b-92a6-6fdf038e076a -state: running -image: /httpserver-python312 -resources: - memory: 512MiB - vcpus: 1 -service: - name: icy-violet-hce1qwm5 - uuid: 4cd63083-d538-44df-92b9-74bd6eb54ec0 - domains: - - fqdn: icy-violet-hce1qwm5.fra.unikraft.app -networks: -- uuid: 51c147cc-8f8d-4347-b628-d2e0f4f9b3be - private-ip: 10.0.0.121 - mac: 12:b0:0a:00:00:79 -timestamps: - created: just now - -metro: fra -name: httpserver-python312-7lha2 -uuid: 0706c648-0c56-4c74-8dda-5a1aeff7d8b8 -state: running -image: /httpserver-python312 -resources: - memory: 512MiB - vcpus: 1 -service: - name: hidden-silence-l3g520dw - uuid: 38703973-ccbd-47ae-b4c0-f9f59c1751e7 - domains: - - fqdn: hidden-silence-l3g520dw.fra.unikraft.app -networks: -- uuid: 6c20f9b2-dc39-41d1-a4bd-250a52a75b19 - private-ip: 10.0.9.185 - mac: 12:b0:0a:00:09:b9 -timestamps: - created: just now +metro: fra +name: httpserver-python312-lscmn +uuid: 1121225d-a678-45a2-a8ce-335a9a6a2683 +state: running +image: /httpserver-python312 +resources: + memory: 512MiB + vcpus: 1 +service: + name: restless-orangutan-1lklj6z5 + uuid: 16f4b86d-df8f-452c-b189-41ec34250e58 + domains: + - fqdn: restless-orangutan-1lklj6z5.fra.unikraft.app +networks: +- uuid: da9445a3-c7b7-4327-8499-3bd7001cd829 + private-ip: 10.0.6.37 + mac: 12:b0:0a:00:06:25 +timestamps: + created: just now + +metro: fra +name: httpserver-python312-xfgvc +uuid: d021379d-9e03-4d0b-92a6-6fdf038e076a +state: running +image: /httpserver-python312 +resources: + memory: 512MiB + vcpus: 1 +service: + name: icy-violet-hce1qwm5 + uuid: 4cd63083-d538-44df-92b9-74bd6eb54ec0 + domains: + - fqdn: icy-violet-hce1qwm5.fra.unikraft.app +networks: +- uuid: 51c147cc-8f8d-4347-b628-d2e0f4f9b3be + private-ip: 10.0.0.121 + mac: 12:b0:0a:00:00:79 +timestamps: + created: just now + +metro: fra +name: httpserver-python312-7lha2 +uuid: 0706c648-0c56-4c74-8dda-5a1aeff7d8b8 +state: running +image: /httpserver-python312 +resources: + memory: 512MiB + vcpus: 1 +service: + name: hidden-silence-l3g520dw + uuid: 38703973-ccbd-47ae-b4c0-f9f59c1751e7 + domains: + - fqdn: hidden-silence-l3g520dw.fra.unikraft.app +networks: +- uuid: 6c20f9b2-dc39-41d1-a4bd-250a52a75b19 + private-ip: 10.0.9.185 + mac: 12:b0:0a:00:09:b9 +timestamps: + created: just now ``` ```ansi title="kraft" -[●] Deployed successfully! - β”‚ - β”œ───────── name: httpserver-python312-lscmn - β”œ───────── uuid: 1121225d-a678-45a2-a8ce-335a9a6a2683 - β”œ──────── metro: https://api.fra.unikraft.cloud/v1 - β”œ──────── state: running - β”œ─────── domain: https://restless-orangutan-1lklj6z5.fra.unikraft.app - β”œ──────── image: oci://unikraft.io//httpserver-python312@sha256:5b922dfa1632af38c476b98fdd9f4314fb9c5e587d3d31255e6479108c057e88 - β”œ─────── memory: 512 MiB - β”œ────── service: restless-orangutan-1lklj6z5 - β”œ─ private fqdn: httpserver-python312-lscmn.internal - β””─── private ip: 10.0.6.37 +[●] Deployed successfully! + β”‚ + β”œ───────── name: httpserver-python312-lscmn + β”œ───────── uuid: 1121225d-a678-45a2-a8ce-335a9a6a2683 + β”œ──────── metro: https://api.fra.unikraft.cloud/v1 + β”œ──────── state: running + β”œ─────── domain: https://restless-orangutan-1lklj6z5.fra.unikraft.app + β”œ──────── image: oci://unikraft.io//httpserver-python312@sha256:5b922dfa1632af38c476b98fdd9f4314fb9c5e587d3d31255e6479108c057e88 + β”œ─────── memory: 512 MiB + β”œ────── service: restless-orangutan-1lklj6z5 + β”œ─ private fqdn: httpserver-python312-lscmn.internal + β””─── private ip: 10.0.6.37 ``` @@ -347,17 +347,17 @@ kraft cloud instance list ```ansi title="unikraft" -METRO NAME STATE IMAGE ARGS MEMORY VCPUS FQDN CREATED -fra httpserver-python312-7lha2 running /httpserver-python312 512MiB 1 hidden-silence-l3g520dw.fra.unikraft.app just now -fra httpserver-python312-xfgvc running /httpserver-python312 512MiB 1 icy-violet-hce1qwm5.fra.unikraft.app just now -fra httpserver-python312-lscmn running /httpserver-python312 512MiB 1 restless-orangutan-1lklj6z5.fra.unikraft.app just now +METRO NAME STATE IMAGE ARGS MEMORY VCPUS FQDN CREATED +fra httpserver-python312-7lha2 running /httpserver-python312 512MiB 1 hidden-silence-l3g520dw.fra.unikraft.app just now +fra httpserver-python312-xfgvc running /httpserver-python312 512MiB 1 icy-violet-hce1qwm5.fra.unikraft.app just now +fra httpserver-python312-lscmn running /httpserver-python312 512MiB 1 restless-orangutan-1lklj6z5.fra.unikraft.app just now ``` ```ansi title="kraft" -NAME FQDN STATE STATUS IMAGE MEMORY VCPUS ARGS BOOT TIME -httpserver-python312-7lha2 hidden-silence-l3g520dw.fra.unikraft.app running since 7mins /httpserver-python312@sh... 512 MiB 1 82.28 ms -httpserver-python312-xfgvc icy-violet-hce1qwm5.fra.unikraft.app running since 7mins /httpserver-python312@sh... 512 MiB 1 81.27 ms -httpserver-python312-lscmn restless-orangutan-1lklj6z5.fra.unikraft.app running since 7mins /httpserver-python312@sh... 512 MiB 1 84.28 ms +NAME FQDN STATE STATUS IMAGE MEMORY VCPUS ARGS BOOT TIME +httpserver-python312-7lha2 hidden-silence-l3g520dw.fra.unikraft.app running since 7mins /httpserver-python312@sh... 512 MiB 1 82.28 ms +httpserver-python312-xfgvc icy-violet-hce1qwm5.fra.unikraft.app running since 7mins /httpserver-python312@sh... 512 MiB 1 81.27 ms +httpserver-python312-lscmn restless-orangutan-1lklj6z5.fra.unikraft.app running since 7mins /httpserver-python312@sh... 512 MiB 1 84.28 ms ``` diff --git a/pages/platform/instances.mdx b/pages/platform/instances.mdx index bf0b2773..aa4a0293 100644 --- a/pages/platform/instances.mdx +++ b/pages/platform/instances.mdx @@ -40,32 +40,32 @@ kraft instance list httpserver-go121-taud8 ```ansi title="unikraft" -metro: fra -name: httpserver-go121-taud8 -uuid: bd281cc4-7640-4cbc-af48-5a44c2bd7b74 -state: standby -image: /httpserver-go121 -resources: - memory: 256MiB - vcpus: 1 -service: - name: floral-sun-54ixkmi6 - uuid: d20c8a02-53a8-4917-8c25-94b99ab59a88 - domains: - - fqdn: floral-sun-54ixkmi6.fra.unikraft.app -networks: -- uuid: 16ca6566-a368-45d3-b759-3abf0cc37d03 - private-ip: 10.0.0.37 - mac: 12:b0:0a:00:00:25 -timestamps: - created: 6 days ago -scale-to-zero: - enabled: true - policy: on - cooldown-time: 1s -stop: - reason: platform stop - exit-code: 1 +metro: fra +name: httpserver-go121-taud8 +uuid: bd281cc4-7640-4cbc-af48-5a44c2bd7b74 +state: standby +image: /httpserver-go121 +resources: + memory: 256MiB + vcpus: 1 +service: + name: floral-sun-54ixkmi6 + uuid: d20c8a02-53a8-4917-8c25-94b99ab59a88 + domains: + - fqdn: floral-sun-54ixkmi6.fra.unikraft.app +networks: +- uuid: 16ca6566-a368-45d3-b759-3abf0cc37d03 + private-ip: 10.0.0.37 + mac: 12:b0:0a:00:00:25 +timestamps: + created: 6 days ago +scale-to-zero: + enabled: true + policy: on + cooldown-time: 1s +stop: + reason: platform stop + exit-code: 1 ``` ```ansi title="kraft" @@ -74,7 +74,7 @@ stop: fqdn: floral-sun-54ixkmi6.fra.unikraft.app private fqdn: httpserver-go121-taud8.internal private ip: 10.0.0.37 - state: standby + state: standby created: 2026-05-28T13:53:40Z started: 2026-05-28T13:54:43Z stopped: 2026-05-28T13:54:45Z @@ -106,7 +106,7 @@ You can retrieve even more instance details via the API: ```bash title="GET /instances/bd281cc4-7640-4cbc-af48-5a44c2bd7b74" curl -X GET \ - -H "Authorization: Bearer $UKC_TOKEN" \ + -H "Authorization: Bearer ${UKC_TOKEN}" \ -H "Accept: application/json" \ https://api.fra.unikraft.cloud/v1/instances/bd281cc4-7640-4cbc-af48-5a44c2bd7b74 ``` @@ -465,20 +465,20 @@ kraft cloud instance template create ```ansi title="unikraft" -metro: fra -name: httpserver-go121-taud8 -uuid: bd281cc4-7640-4cbc-af48-5a44c2bd7b74 -state: template -image: /httpserver-go121 -resources: - memory: 256MiB - vcpus: 1 -timestamps: - created: 6 days ago -scale-to-zero: - enabled: true - policy: on - cooldown-time: 1s +metro: fra +name: httpserver-go121-taud8 +uuid: bd281cc4-7640-4cbc-af48-5a44c2bd7b74 +state: template +image: /httpserver-go121 +resources: + memory: 256MiB + vcpus: 1 +timestamps: + created: 6 days ago +scale-to-zero: + enabled: true + policy: on + cooldown-time: 1s ``` ```ansi title="kraft" @@ -504,12 +504,12 @@ kraft instance template list ```ansi title="unikraft" -METRO NAME STATE IMAGE ARGS MEMORY VCPUS CREATED -fra httpserver-go121-taud8 template /httpserver-go121 256MiB 1 6 days ago +METRO NAME STATE IMAGE ARGS MEMORY VCPUS CREATED +fra httpserver-go121-taud8 template /httpserver-go121 256MiB 1 6 days ago ``` ```ansi title="kraft" -NAME IMAGE ARGS CREATED AT +NAME IMAGE ARGS CREATED AT httpserver-go121-taud8 oci://unikraft.io//httpserver-go121:latest@sha256:7785ebdf6c1ffaf05c3beab4cffc2548790c088233a0ee51ac5d047e21e43d77 6 days ago ``` @@ -524,24 +524,24 @@ unikraft instance create --metro fra \ ``` ```ansi title="unikraft" -metro: fra -name: httpserver-go121-clone -uuid: 91796fdb-bd41-4c7b-856f-9e60356c96bd -state: standby -image: acioc/httpserver-go121 -resources: - memory: 256MiB - vcpus: 1 -networks: -- uuid: 8c778b8b-6ede-4e87-a825-159a2269fd19 - private-ip: 10.0.0.85 - mac: 12:b0:0a:00:00:55 -timestamps: - created: just now -scale-to-zero: - enabled: true - policy: on - cooldown-time: 1s +metro: fra +name: httpserver-go121-clone +uuid: 91796fdb-bd41-4c7b-856f-9e60356c96bd +state: standby +image: acioc/httpserver-go121 +resources: + memory: 256MiB + vcpus: 1 +networks: +- uuid: 8c778b8b-6ede-4e87-a825-159a2269fd19 + private-ip: 10.0.0.85 + mac: 12:b0:0a:00:00:55 +timestamps: + created: just now +scale-to-zero: + enabled: true + policy: on + cooldown-time: 1s ``` Templates support [delete locks](/platform/delete-locks), [tags](/platform/tagging), and [autokill](/features/autokill). diff --git a/pages/platform/metrics.mdx b/pages/platform/metrics.mdx index 87828d8d..e9102eeb 100644 --- a/pages/platform/metrics.mdx +++ b/pages/platform/metrics.mdx @@ -116,7 +116,7 @@ unikraft api /v1/instances/metrics?uuid=f31c1fa8-6188-4ebe-b6e0-b2de063ad7ea -X ```bash title="API" curl -X GET \ - -H "Authorization: Bearer $UKC_TOKEN" \ + -H "Authorization: Bearer ${UKC_TOKEN}" \ "https://api.fra.unikraft.cloud/v1/instances/metrics?uuid=f31c1fa8-6188-4ebe-b6e0-b2de063ad7ea" ``` @@ -225,7 +225,7 @@ unikraft api /v1/instances/metrics?uuid=f31c1fa8-6188-4ebe-b6e0-b2de063ad7ea -X ```bash title="API" curl -X GET \ - -H "Authorization: Bearer $UKC_TOKEN" \ + -H "Authorization: Bearer ${UKC_TOKEN}" \ -H "Accept: text/plain" \ "https://api.fra.unikraft.cloud/v1/instances/metrics?uuid=f31c1fa8-6188-4ebe-b6e0-b2de063ad7ea" ``` diff --git a/pages/platform/networking.mdx b/pages/platform/networking.mdx index f8e78537..cd80b952 100644 --- a/pages/platform/networking.mdx +++ b/pages/platform/networking.mdx @@ -50,10 +50,10 @@ kraft cloud instance get ```ansi title="unikraft" ... -networks: -- uuid: afbd8ce2-daf5-4c47-888a-96bbf1c4a347 - private-ip: 10.0.0.49 - mac: 12:b0:0a:00:00:31 +networks: +- uuid: afbd8ce2-daf5-4c47-888a-96bbf1c4a347 + private-ip: 10.0.0.49 + mac: 12:b0:0a:00:00:31 ... ``` diff --git a/pages/platform/quotas.mdx b/pages/platform/quotas.mdx index 7b8bdc14..986dc3de 100644 --- a/pages/platform/quotas.mdx +++ b/pages/platform/quotas.mdx @@ -19,7 +19,7 @@ kraft cloud quotas ```bash title="API" curl -X GET \ - -H "Authorization: Bearer $UKC_TOKEN" \ + -H "Authorization: Bearer ${UKC_TOKEN}" \ -H "Content-Type: application/json" \ "https://api.fra.unikraft.cloud/v1/users/quotas" ``` diff --git a/pages/platform/services.mdx b/pages/platform/services.mdx index 5794934b..a0d6f337 100644 --- a/pages/platform/services.mdx +++ b/pages/platform/services.mdx @@ -42,40 +42,40 @@ kraft cloud instance create \ ```ansi title="unikraft" -metro: fra -name: nginx-67zbu -uuid: 8a8bc1b9-0af6-420e-a426-190dc2da9eaa -state: starting -image: /nginx -resources: - memory: 256MiB - vcpus: 1 -service: - uuid: a942b9b5-ad17-3ffe-dcd2-ef4331f9087a - name: nameless-fog-0tvh1uov # <---- service name - domains: - - fqdn: nameless-fog-0tvh1uov.fra.unikraft.app # <---- service domain -networks: -- uuid: 62d9bbf0-aec8-61f6-7bdb-86edf63dd068 - private-ip: 10.0.3.3 - mac: 12:b0:c6:23:ed:15 -timestamps: - created: just now +metro: fra +name: nginx-67zbu +uuid: 8a8bc1b9-0af6-420e-a426-190dc2da9eaa +state: starting +image: /nginx +resources: + memory: 256MiB + vcpus: 1 +service: + uuid: a942b9b5-ad17-3ffe-dcd2-ef4331f9087a + name: nameless-fog-0tvh1uov # <---- service name + domains: + - fqdn: nameless-fog-0tvh1uov.fra.unikraft.app # <---- service domain +networks: +- uuid: 62d9bbf0-aec8-61f6-7bdb-86edf63dd068 + private-ip: 10.0.3.3 + mac: 12:b0:c6:23:ed:15 +timestamps: + created: just now ``` ```ansi title="kraft" -[●] Deployed successfully! - β”‚ - β”œ───────── name: nginx-67zbu - β”œ───────── uuid: 8a8bc1b9-0af6-420e-a426-190dc2da9eaa - β”œ──────── metro: https://api.fra.unikraft.cloud/v1 - β”œ──────── state: starting - β”œ─────── domain: https://nameless-fog-0tvh1uov.fra.unikraft.app # <---- service domain - β”œ──────── image: oci://unikraft.io//nginx@sha256:f51ecc121c9ca34abb88a2bc6a69765501304f7893f7e85af15fbec3dc86e2bd - β”œ─────── memory: 256 MiB - β”œ────── service: nameless-fog-0tvh1uov # <---- service name - β”œ─ private fqdn: nginx-67zbu.internal - β””─── private ip: 10.0.3.3 +[●] Deployed successfully! + β”‚ + β”œ───────── name: nginx-67zbu + β”œ───────── uuid: 8a8bc1b9-0af6-420e-a426-190dc2da9eaa + β”œ──────── metro: https://api.fra.unikraft.cloud/v1 + β”œ──────── state: starting + β”œ─────── domain: https://nameless-fog-0tvh1uov.fra.unikraft.app # <---- service domain + β”œ──────── image: oci://unikraft.io//nginx@sha256:f51ecc121c9ca34abb88a2bc6a69765501304f7893f7e85af15fbec3dc86e2bd + β”œ─────── memory: 256 MiB + β”œ────── service: nameless-fog-0tvh1uov # <---- service name + β”œ─ private fqdn: nginx-67zbu.internal + β””─── private ip: 10.0.3.3 ``` @@ -107,25 +107,25 @@ kraft cloud service create \ ```ansi title="unikraft" -metro: fra -name: my-service -uuid: 9bfe76ea-2679-482a-954e-577de7d122b3 -persistent: true -limits: - soft: 1 - hard: 65535 -timestamps: - created: just now -domains: -- fqdn: my-service-pc4xjplc.fra.unikraft.app -services: -- source: 443 - destination: 8080 - handlers: ["tls", "http"] +metro: fra +name: my-service +uuid: 9bfe76ea-2679-482a-954e-577de7d122b3 +persistent: true +limits: + soft: 1 + hard: 65535 +timestamps: + created: just now +domains: +- fqdn: my-service-pc4xjplc.fra.unikraft.app +services: +- source: 443 + destination: 8080 + handlers: ["tls", "http"] ``` ```ansi title="kraft" -NAME FQDN SERVICES INSTANCES CREATED AT PERSISTENT +NAME FQDN SERVICES INSTANCES CREATED AT PERSISTENT my-service my-service-f4744h0c.fra.unikraft.app 443:8080/tls+http 1 second ago true ``` @@ -167,40 +167,40 @@ The output shows the instance address and other details: ```ansi title="unikraft" -metro: fra -name: httpserver-go121-9a2wv -uuid: 8bb34040-9434-4a28-bd1e-c24ee532e2da -state: starting -image: /httpserver-go121 -resources: - memory: 256MiB - vcpus: 1 -service: - uuid: 9bfe76ea-2679-482a-954e-577de7d122b3 - name: my-service - domains: - - fqdn: my-service-f4744h0c.fra.unikraft.app -networks: -- uuid: 51f79dc1-e989-908d-894b-bdf0a87e7901 - private-ip: 10.0.3.3 - mac: 12:b0:57:91:bb:a5 -timestamps: - created: just now +metro: fra +name: httpserver-go121-9a2wv +uuid: 8bb34040-9434-4a28-bd1e-c24ee532e2da +state: starting +image: /httpserver-go121 +resources: + memory: 256MiB + vcpus: 1 +service: + uuid: 9bfe76ea-2679-482a-954e-577de7d122b3 + name: my-service + domains: + - fqdn: my-service-f4744h0c.fra.unikraft.app +networks: +- uuid: 51f79dc1-e989-908d-894b-bdf0a87e7901 + private-ip: 10.0.3.3 + mac: 12:b0:57:91:bb:a5 +timestamps: + created: just now ``` ```ansi title="kraft" -[●] Deployed successfully! - β”‚ - β”œ───────── name: httpserver-go121-9a2wv - β”œ───────── uuid: 8bb34040-9434-4a28-bd1e-c24ee532e2da - β”œ──────── metro: https://api.fra.unikraft.cloud/v1 - β”œ──────── state: starting - β”œ─────── domain: https://my-service-f4744h0c.fra.unikraft.app - β”œ──────── image: oci://unikraft.io//httpserver-go121@sha256:b16d61bb7898e764d8c11ab5a0b995e8c25a25b5ff89e161fc994ebf25a75680 - β”œ─────── memory: 256 MiB - β”œ────── service: my-service - β”œ─ private fqdn: httpserver-go121-9a2wv.internal - β””─── private ip: 10.0.3.3 +[●] Deployed successfully! + β”‚ + β”œ───────── name: httpserver-go121-9a2wv + β”œ───────── uuid: 8bb34040-9434-4a28-bd1e-c24ee532e2da + β”œ──────── metro: https://api.fra.unikraft.cloud/v1 + β”œ──────── state: starting + β”œ─────── domain: https://my-service-f4744h0c.fra.unikraft.app + β”œ──────── image: oci://unikraft.io//httpserver-go121@sha256:b16d61bb7898e764d8c11ab5a0b995e8c25a25b5ff89e161fc994ebf25a75680 + β”œ─────── memory: 256 MiB + β”œ────── service: my-service + β”œ─ private fqdn: httpserver-go121-9a2wv.internal + β””─── private ip: 10.0.3.3 ``` diff --git a/pages/platform/tagging.mdx b/pages/platform/tagging.mdx index 215617d8..77673b86 100644 --- a/pages/platform/tagging.mdx +++ b/pages/platform/tagging.mdx @@ -41,7 +41,7 @@ unikraft api /v1/instances -X POST --metro fra \ ``` ```bash title="API" curl -X POST \ - -H "Authorization: Bearer $UKC_TOKEN" \ + -H "Authorization: Bearer ${UKC_TOKEN}" \ -H "Content-Type: application/json" \ "https://api.fra.unikraft.cloud/v1/instances" \ -d '{ @@ -65,7 +65,7 @@ unikraft api /v1/instances/templates -X POST --metro fra \ ``` ```bash title="API" curl -X POST \ - -H "Authorization: Bearer $UKC_TOKEN" \ + -H "Authorization: Bearer ${UKC_TOKEN}" \ -H "Content-Type: application/json" \ "https://api.fra.unikraft.cloud/v1/instances/templates" \ -d '{ @@ -88,7 +88,7 @@ unikraft api /v1/volumes -X POST --metro fra \ ``` ```bash title="API" curl -X POST \ - -H "Authorization: Bearer $UKC_TOKEN" \ + -H "Authorization: Bearer ${UKC_TOKEN}" \ -H "Content-Type: application/json" \ "https://api.fra.unikraft.cloud/v1/volumes" \ -d '{ @@ -110,7 +110,7 @@ unikraft api /v1/volumes/templates -X POST --metro fra \ ``` ```bash title="API" curl -X POST \ - -H "Authorization: Bearer $UKC_TOKEN" \ + -H "Authorization: Bearer ${UKC_TOKEN}" \ -H "Content-Type: application/json" \ "https://api.fra.unikraft.cloud/v1/volumes/templates" \ -d '{ @@ -145,7 +145,7 @@ unikraft api /v1/instances -X PATCH --metro fra \ ``` ```bash title="API" curl -X PATCH \ - -H "Authorization: Bearer $UKC_TOKEN" \ + -H "Authorization: Bearer ${UKC_TOKEN}" \ -H "Content-Type: application/json" \ "https://api.fra.unikraft.cloud/v1/instances" \ -d '[{ @@ -171,7 +171,7 @@ unikraft api /v1/instances/templates -X PATCH --metro fra \ ``` ```bash title="API" curl -X PATCH \ - -H "Authorization: Bearer $UKC_TOKEN" \ + -H "Authorization: Bearer ${UKC_TOKEN}" \ -H "Content-Type: application/json" \ "https://api.fra.unikraft.cloud/v1/instances/templates" \ -d '[{ @@ -197,7 +197,7 @@ unikraft api /v1/volumes -X PATCH --metro fra \ ``` ```bash title="API" curl -X PATCH \ - -H "Authorization: Bearer $UKC_TOKEN" \ + -H "Authorization: Bearer ${UKC_TOKEN}" \ -H "Content-Type: application/json" \ "https://api.fra.unikraft.cloud/v1/volumes" \ -d '[{ @@ -223,7 +223,7 @@ unikraft api /v1/volumes/templates -X PATCH --metro fra \ ``` ```bash title="API" curl -X PATCH \ - -H "Authorization: Bearer $UKC_TOKEN" \ + -H "Authorization: Bearer ${UKC_TOKEN}" \ -H "Content-Type: application/json" \ "https://api.fra.unikraft.cloud/v1/volumes/templates" \ -d '[{ @@ -260,7 +260,7 @@ unikraft api /v1/instances -X PATCH --metro fra \ ``` ```bash title="API" curl -X PATCH \ - -H "Authorization: Bearer $UKC_TOKEN" \ + -H "Authorization: Bearer ${UKC_TOKEN}" \ -H "Content-Type: application/json" \ "https://api.fra.unikraft.cloud/v1/instances" \ -d '[{ @@ -287,7 +287,7 @@ unikraft api /v1/instances/templates -X PATCH --metro fra \ ``` ```bash title="API" curl -X PATCH \ - -H "Authorization: Bearer $UKC_TOKEN" \ + -H "Authorization: Bearer ${UKC_TOKEN}" \ -H "Content-Type: application/json" \ "https://api.fra.unikraft.cloud/v1/instances/templates" \ -d '[{ @@ -313,7 +313,7 @@ unikraft api /v1/volumes -X PATCH --metro fra \ ``` ```bash title="API" curl -X PATCH \ - -H "Authorization: Bearer $UKC_TOKEN" \ + -H "Authorization: Bearer ${UKC_TOKEN}" \ -H "Content-Type: application/json" \ "https://api.fra.unikraft.cloud/v1/volumes" \ -d '[{ @@ -339,7 +339,7 @@ unikraft api /v1/volumes/templates -X PATCH --metro fra \ ``` ```bash title="API" curl -X PATCH \ - -H "Authorization: Bearer $UKC_TOKEN" \ + -H "Authorization: Bearer ${UKC_TOKEN}" \ -H "Content-Type: application/json" \ "https://api.fra.unikraft.cloud/v1/volumes/templates" \ -d '[{ @@ -383,7 +383,7 @@ unikraft api /v1/instances -X PATCH --metro fra \ ``` ```bash title="API" curl -X PATCH \ - -H "Authorization: Bearer $UKC_TOKEN" \ + -H "Authorization: Bearer ${UKC_TOKEN}" \ -H "Content-Type: application/json" \ "https://api.fra.unikraft.cloud/v1/instances" \ -d '[{ @@ -409,7 +409,7 @@ unikraft api /v1/instances/templates -X PATCH --metro fra \ ``` ```bash title="API" curl -X PATCH \ - -H "Authorization: Bearer $UKC_TOKEN" \ + -H "Authorization: Bearer ${UKC_TOKEN}" \ -H "Content-Type: application/json" \ "https://api.fra.unikraft.cloud/v1/instances/templates" \ -d '[{ @@ -435,7 +435,7 @@ unikraft api /v1/volumes -X PATCH --metro fra \ ``` ```bash title="API" curl -X PATCH \ - -H "Authorization: Bearer $UKC_TOKEN" \ + -H "Authorization: Bearer ${UKC_TOKEN}" \ -H "Content-Type: application/json" \ "https://api.fra.unikraft.cloud/v1/volumes" \ -d '[{ @@ -461,7 +461,7 @@ unikraft api /v1/volumes/templates -X PATCH --metro fra \ ``` ```bash title="API" curl -X PATCH \ - -H "Authorization: Bearer $UKC_TOKEN" \ + -H "Authorization: Bearer ${UKC_TOKEN}" \ -H "Content-Type: application/json" \ "https://api.fra.unikraft.cloud/v1/volumes/templates" \ -d '[{ @@ -494,7 +494,7 @@ Only resources that have _all_ the specified tags appear in the results. unikraft api "/v1/instances?tags=production,customer_A" -X GET --metro fra ``` ```bash title="API" -curl -H "Authorization: Bearer $UKC_TOKEN" \ +curl -H "Authorization: Bearer ${UKC_TOKEN}" \ "https://api.fra.unikraft.cloud/v1/instances?tags=production,customer_A" ``` @@ -506,7 +506,7 @@ curl -H "Authorization: Bearer $UKC_TOKEN" \ unikraft api "/v1/instances/templates?tags=production,customer_A" -X GET --metro fra ``` ```bash title="API" -curl -H "Authorization: Bearer $UKC_TOKEN" \ +curl -H "Authorization: Bearer ${UKC_TOKEN}" \ "https://api.fra.unikraft.cloud/v1/instances/templates?tags=production,customer_A" ``` @@ -518,7 +518,7 @@ curl -H "Authorization: Bearer $UKC_TOKEN" \ unikraft api "/v1/volumes?tags=production,customer_A" -X GET --metro fra ``` ```bash title="API" -curl -H "Authorization: Bearer $UKC_TOKEN" \ +curl -H "Authorization: Bearer ${UKC_TOKEN}" \ "https://api.fra.unikraft.cloud/v1/volumes?tags=production,customer_A" ``` @@ -530,7 +530,7 @@ curl -H "Authorization: Bearer $UKC_TOKEN" \ unikraft api "/v1/volumes/templates?tags=production,customer_A" -X GET --metro fra ``` ```bash title="API" -curl -H "Authorization: Bearer $UKC_TOKEN" \ +curl -H "Authorization: Bearer ${UKC_TOKEN}" \ "https://api.fra.unikraft.cloud/v1/volumes/templates?tags=production,customer_A" ``` diff --git a/pages/platform/troubleshooting.mdx b/pages/platform/troubleshooting.mdx index 97b51f75..4ed8475e 100644 --- a/pages/platform/troubleshooting.mdx +++ b/pages/platform/troubleshooting.mdx @@ -79,31 +79,31 @@ This yields output like: ```ansi title="unikraft" -metro: fra -name: node-playwright-chromium-nwa95 -uuid: 59cd6c94-611c-4ca8-911f-78c7db95ad12 -state: stopped -image: /node-playwright-chromium -resources: - memory: 2.441GiB - vcpus: 1 -service: - name: divine-dust-73sxgnb6 - uuid: 3c7ce2b4-ae86-4a21-8562-55033d5409d5 - domains: - - fqdn: divine-dust-73sxgnb6.fra0-fe-test.unikraft.app -networks: -- uuid: 62982265-6521-401c-878a-d7e31804c63c - private-ip: 10.0.0.109 - mac: 12:b0:0a:00:00:6d -timestamps: - created: 2 days ago -scale-to-zero: - enabled: true - policy: on - cooldown-time: 1s -stop: - reason: kernel crash: out of memory (ENOMEM) +metro: fra +name: node-playwright-chromium-nwa95 +uuid: 59cd6c94-611c-4ca8-911f-78c7db95ad12 +state: stopped +image: /node-playwright-chromium +resources: + memory: 2.441GiB + vcpus: 1 +service: + name: divine-dust-73sxgnb6 + uuid: 3c7ce2b4-ae86-4a21-8562-55033d5409d5 + domains: + - fqdn: divine-dust-73sxgnb6.fra0-fe-test.unikraft.app +networks: +- uuid: 62982265-6521-401c-878a-d7e31804c63c + private-ip: 10.0.0.109 + mac: 12:b0:0a:00:00:6d +timestamps: + created: 2 days ago +scale-to-zero: + enabled: true + policy: on + cooldown-time: 1s +stop: + reason: kernel crash: out of memory (ENOMEM) ``` ```ansi title="kraft" @@ -112,7 +112,7 @@ stop: fqdn: divine-dust-73sxgnb6.fra0-fe-test.unikraft.app private fqdn: node-playwright-chromium-nwa95.internal private ip: 10.0.0.109 - state: stopped + state: stopped created: 2026-06-02T17:24:31Z started: 2026-06-02T17:24:31Z stopped: 2026-06-02T17:24:37Z diff --git a/pages/platform/volumes.mdx b/pages/platform/volumes.mdx index aed2191e..160a9fee 100644 --- a/pages/platform/volumes.mdx +++ b/pages/platform/volumes.mdx @@ -36,21 +36,21 @@ kraft cloud volume create \ ```ansi title="unikraft" -metro: fra -name: my-volume -uuid: 4d5576f7-315a-4eab-98b8-6c18fec076e0 -state: available -size: 100MiB -filesystem: ext4 -quota-policy: static -persistent: true -access-mode: rwo -timestamps: - created: just now +metro: fra +name: my-volume +uuid: 4d5576f7-315a-4eab-98b8-6c18fec076e0 +state: available +size: 100MiB +filesystem: ext4 +quota-policy: static +persistent: true +access-mode: rwo +timestamps: + created: just now ``` ```ansi title="kraft" -NAME CREATED AT SIZE ATTACHED TO MOUNTED BY STATE PERSISTENT +NAME CREATED AT SIZE ATTACHED TO MOUNTED BY STATE PERSISTENT my-volume 1 second ago 100 MiB available true ``` @@ -72,21 +72,21 @@ kraft cloud volume list -```bash title="unikraft" -METRO NAME STATE SIZE CREATED -fra my-volume available 100MiB just now +```ansi title="unikraft" +METRO NAME STATE SIZE CREATED +fra my-volume available 100MiB just now ``` -```bash title="kraft" -NAME CREATED AT SIZE ATTACHED TO MOUNTED BY STATE PERSISTENT +```ansi title="kraft" +NAME CREATED AT SIZE ATTACHED TO MOUNTED BY STATE PERSISTENT my-volume 1 minute ago 100 MiB available true ``` + + The volume is in `available` state because it isn't attached to any instance yet. It's also marked as `persistent` because it will persist even after instance deletion. - - ### Importing local data To populate an empty volume with local data, use the CLI `import` subcommand. @@ -206,44 +206,44 @@ You should see output like: ```ansi title="unikraft" -metro: fra -name: httpserver-python312-flask30-bxwxm -uuid: 3ff1ebad-2639-4214-bab4-ed35c4c32fa4 -state: starting -image: /httpserver-python312-flask30 -resources: - memory: 512MiB - vcpus: 1 -service: - uuid: 2021ca1e-5e47-a35c-bde2-7190f3815c07 - name: damp-sunset-azd6dtyt - domains: - - fqdn: damp-sunset-azd6dtyt.fra.unikraft.app -volumes: -- name: my-volume - uuid: 4d5576f7-315a-4eab-98b8-6c18fec076e0 - at: /mnt -networks: -- uuid: 4dc3b272-4fa2-ddb4-7ce0-5e66e8112a61 - private-ip: 10.0.6.5 - mac: 12:b0:66:99:b3:3f -timestamps: - created: just now +metro: fra +name: httpserver-python312-flask30-bxwxm +uuid: 3ff1ebad-2639-4214-bab4-ed35c4c32fa4 +state: starting +image: /httpserver-python312-flask30 +resources: + memory: 512MiB + vcpus: 1 +service: + uuid: 2021ca1e-5e47-a35c-bde2-7190f3815c07 + name: damp-sunset-azd6dtyt + domains: + - fqdn: damp-sunset-azd6dtyt.fra.unikraft.app +volumes: +- name: my-volume + uuid: 4d5576f7-315a-4eab-98b8-6c18fec076e0 + at: /mnt +networks: +- uuid: 4dc3b272-4fa2-ddb4-7ce0-5e66e8112a61 + private-ip: 10.0.6.5 + mac: 12:b0:66:99:b3:3f +timestamps: + created: just now ``` ```ansi title="kraft" -[●] Deployed successfully! - β”‚ - β”œ───────── name: httpserver-python312-flask30-bxwxm - β”œ───────── uuid: 3ff1ebad-2639-4214-bab4-ed35c4c32fa4 - β”œ──────── metro: https://api.fra.unikraft.cloud/v1 - β”œ──────── state: starting - β”œ─────── domain: https://damp-sunset-azd6dtyt.fra.unikraft.app - β”œ──────── image: oci://unikraft.io//httpserver-python312-flask30@sha256:d6c8e4c5a4f44e1d642d8eaeaa1d820b2841194dd6c5d4a872ae0a895c767da9 - β”œ─────── memory: 512 MiB - β”œ────── service: damp-sunset-azd6dtyt - β”œ─ private fqdn: httpserver-python312-flask30-bxwxm.internal - β””─── private ip: 10.0.6.5 +[●] Deployed successfully! + β”‚ + β”œ───────── name: httpserver-python312-flask30-bxwxm + β”œ───────── uuid: 3ff1ebad-2639-4214-bab4-ed35c4c32fa4 + β”œ──────── metro: https://api.fra.unikraft.cloud/v1 + β”œ──────── state: starting + β”œ─────── domain: https://damp-sunset-azd6dtyt.fra.unikraft.app + β”œ──────── image: oci://unikraft.io//httpserver-python312-flask30@sha256:d6c8e4c5a4f44e1d642d8eaeaa1d820b2841194dd6c5d4a872ae0a895c767da9 + β”œ─────── memory: 512 MiB + β”œ────── service: damp-sunset-azd6dtyt + β”œ─ private fqdn: httpserver-python312-flask30-bxwxm.internal + β””─── private ip: 10.0.6.5 ``` @@ -267,12 +267,12 @@ You should see output like: ```ansi title="unikraft" -METRO NAME STATE SIZE CREATED -fra my-volume mounted 100MiB 2 minutes ago +METRO NAME STATE SIZE CREATED +fra my-volume mounted 100MiB 2 minutes ago ``` ```ansi title="kraft" -NAME CREATED AT SIZE ATTACHED TO MOUNTED BY STATE PERSISTENT +NAME CREATED AT SIZE ATTACHED TO MOUNTED BY STATE PERSISTENT my-volume 2 minutes ago 100 MiB httpserver-python312-flask30-bxwxm httpserver-python312-flask30-bxwxm mounted true ``` @@ -358,44 +358,44 @@ This should output something like: ```ansi title="unikraft" -metro: fra -name: httpserver-python312-flask30-0s9c7 -uuid: 59d06415-84b8-4bf5-85c7-997960382011 -state: starting -image: /httpserver-python312-flask30 -resources: - memory: 512MiB - vcpus: 1 -service: - uuid: ca8ae7e9-3767-85f6-3d70-b77bcf894a7c - name: winter-field-v6m37jgs - domains: - - fqdn: winter-field-v6m37jgs.fra.unikraft.app -volumes: -- name: my-volume - uuid: 4d5576f7-315a-4eab-98b8-6c18fec076e0 - at: /mnt -networks: -- uuid: 935ba4ef-39c2-07e7-d2ae-8cd3c9aae07c - private-ip: 10.0.6.5 - mac: 12:b0:66:99:b3:3f -timestamps: - created: just now +metro: fra +name: httpserver-python312-flask30-0s9c7 +uuid: 59d06415-84b8-4bf5-85c7-997960382011 +state: starting +image: /httpserver-python312-flask30 +resources: + memory: 512MiB + vcpus: 1 +service: + uuid: ca8ae7e9-3767-85f6-3d70-b77bcf894a7c + name: winter-field-v6m37jgs + domains: + - fqdn: winter-field-v6m37jgs.fra.unikraft.app +volumes: +- name: my-volume + uuid: 4d5576f7-315a-4eab-98b8-6c18fec076e0 + at: /mnt +networks: +- uuid: 935ba4ef-39c2-07e7-d2ae-8cd3c9aae07c + private-ip: 10.0.6.5 + mac: 12:b0:66:99:b3:3f +timestamps: + created: just now ``` ```ansi title="kraft" -[●] Deployed successfully! - β”‚ - β”œ───────── name: httpserver-python312-flask30-0s9c7 - β”œ───────── uuid: 59d06415-84b8-4bf5-85c7-997960382011 - β”œ──────── metro: https://api.fra.unikraft.cloud/v1 - β”œ──────── state: starting - β”œ─────── domain: https://winter-field-v6m37jgs.fra.unikraft.app - β”œ──────── image: oci://unikraft.io//httpserver-python312-flask30@sha256:d6c8e4c5a4f44e1d642d8eaeaa1d820b2841194dd6c5d4a872ae0a895c767da9 - β”œ─────── memory: 512 MiB - β”œ────── service: winter-field-v6m37jgs - β”œ─ private fqdn: httpserver-python312-flask30-0s9c7.internal - β””─── private ip: 10.0.6.5 +[●] Deployed successfully! + β”‚ + β”œ───────── name: httpserver-python312-flask30-0s9c7 + β”œ───────── uuid: 59d06415-84b8-4bf5-85c7-997960382011 + β”œ──────── metro: https://api.fra.unikraft.cloud/v1 + β”œ──────── state: starting + β”œ─────── domain: https://winter-field-v6m37jgs.fra.unikraft.app + β”œ──────── image: oci://unikraft.io//httpserver-python312-flask30@sha256:d6c8e4c5a4f44e1d642d8eaeaa1d820b2841194dd6c5d4a872ae0a895c767da9 + β”œ─────── memory: 512 MiB + β”œ────── service: winter-field-v6m37jgs + β”œ─ private fqdn: httpserver-python312-flask30-0s9c7.internal + β””─── private ip: 10.0.6.5 ``` @@ -471,15 +471,15 @@ kraft cloud volume template create my-volume ```ansi title="unikraft" -metro: fra -name: my-volume -uuid: 4d5576f7-315a-4eab-98b8-6c18fec076e0 -state: template -size: 100MiB -filesystem: ext4 -persistent: true -timestamps: - created: just now +metro: fra +name: my-volume +uuid: 4d5576f7-315a-4eab-98b8-6c18fec076e0 +state: template +size: 100MiB +filesystem: ext4 +persistent: true +timestamps: + created: just now ``` ```ansi title="kraft" @@ -505,12 +505,12 @@ kraft cloud volume template list ```ansi title="unikraft" -METRO NAME STATE SIZE CREATED -fra my-volume template 100MiB 1 minute ago +METRO NAME STATE SIZE CREATED +fra my-volume template 100MiB 1 minute ago ``` ```ansi title="kraft" -NAME CREATED AT SIZE FREE ATTACHED TO STATE PERSISTENT POLICY +NAME CREATED AT SIZE FREE ATTACHED TO STATE PERSISTENT POLICY my-volume 1 minute ago 100 MiB 0 B template true static ``` @@ -533,17 +533,17 @@ unikraft volume clone my-volume --name my-volume-clone ``` ```ansi title="unikraft" -metro: fra -name: my-volume-clone -uuid: 3f0625b4-cad3-4875-8b61-113aad4b02c7 -state: available -size: 100MiB -filesystem: ext4 -quota-policy: static -persistent: true -access-mode: rwo -timestamps: - created: just now +metro: fra +name: my-volume-clone +uuid: 3f0625b4-cad3-4875-8b61-113aad4b02c7 +state: available +size: 100MiB +filesystem: ext4 +quota-policy: static +persistent: true +access-mode: rwo +timestamps: + created: just now ``` :::note diff --git a/pages/tutorials/kraftkit-to-unikraft.mdx b/pages/tutorials/kraftkit-to-unikraft.mdx index c0795ef0..98c077e9 100644 --- a/pages/tutorials/kraftkit-to-unikraft.mdx +++ b/pages/tutorials/kraftkit-to-unikraft.mdx @@ -107,18 +107,17 @@ kraft cloud instance list ```ansi title="unikraft" METRO NAME STATE IMAGE ARGS MEMORY VCPUS FQDN CREATED -fra httpserver-go121-qdrlz stopped /httpserver-go121 256MiB 1 summer-star-huyfslqm.fra.unikraft.app 20 hours ago -fra httpserver-go121-bhjvm stopped /httpserver-go121 256MiB 1 restless-gorilla-dalzi9bs.fra.unikraft.app 20 hours ago -was httpserver-go121-xjsli standby /httpserver-go121 256MiB 1 bitter-bonobo-20plsjfu.was.unikraft.app 2 minutes ago +fra httpserver-go121-qdrlz stopped /httpserver-go121 256MiB 1 summer-star-huyfslqm.fra.unikraft.app 20 hours ago +fra httpserver-go121-bhjvm stopped /httpserver-go121 256MiB 1 restless-gorilla-dalzi9bs.fra.unikraft.app 20 hours ago +was httpserver-go121-xjsli standby /httpserver-go121 256MiB 1 bitter-bonobo-20plsjfu.was.unikraft.app 2 minutes ago ``` ```ansi title="kraft" -NAME FQDN STATE STATUS IMAGE MEMORY VCPUS ARGS BOOT TIME -httpserver-go121-qdrlz summer-star-huyfslqm.fra.unikraft.app stopped /httpserver-go121@sha256:7785ebdf6c1ffaf05c3be... 256 MiB 1 90.20 ms -httpserver-go121-bhjvm restless-gorilla-dalzi9bs.fra.unikraft.app stopped /httpserver-go121@sha256:7785ebdf6c1ffaf05c3be... 256 MiB 1 89.33 ms +NAME FQDN STATE STATUS IMAGE MEMORY VCPUS ARGS BOOT TIME +httpserver-go121-qdrlz summer-star-huyfslqm.fra.unikraft.app stopped /httpserver-go121@sha256:7785ebdf6c1ffaf05c3be... 256 MiB 1 90.20 ms +httpserver-go121-bhjvm restless-gorilla-dalzi9bs.fra.unikraft.app stopped /httpserver-go121@sha256:7785ebdf6c1ffaf05c3be... 256 MiB 1 89.33 ms #------------------------- -NAME FQDN STATE STATUS IMAGE MEMORY VCPUS ARGS BOOT TIME -httpserver-go121-xjsli bitter-bonobo-20plsjfu.was.unikraft.app standby standby /httpserver-go121@sha256:7785ebdf6c1ffaf05c3be... 256 MiB 1 165.63 ms +httpserver-go121-xjsli bitter-bonobo-20plsjfu.was.unikraft.app standby standby /httpserver-go121@sha256:7785ebdf6c1ffaf05c3be... 256 MiB 1 165.63 ms ``` diff --git a/pages/tutorials/rootfs-formats.mdx b/pages/tutorials/rootfs-formats.mdx index 02961c67..00a42b39 100644 --- a/pages/tutorials/rootfs-formats.mdx +++ b/pages/tutorials/rootfs-formats.mdx @@ -129,40 +129,40 @@ kraft cloud deploy \ ```ansi title="unikraft" -metro: fra -name: node-playwright-chromium-4rrxr -uuid: aeff2d28-d718-4068-9e24-4e2bb203022c -state: starting -image: /node-playwright-chromium -resources: - memory: 1000MiB - vcpus: 1 -service: - name: dry-fire-vhmh9z8u - uuid: 28762473-9e16-4921-bee5-e57c9e5dff34 - domains: - - fqdn: dry-fire-vhmh9z8u.fra.unikraft.app -networks: -- uuid: dd2581ff-f7e8-4aac-b7e6-73b1d64c5cb7 - private-ip: 10.0.3.133 - mac: 12:b0:0a:00:00:19 -timestamps: - created: just now +metro: fra +name: node-playwright-chromium-4rrxr +uuid: aeff2d28-d718-4068-9e24-4e2bb203022c +state: starting +image: /node-playwright-chromium +resources: + memory: 1000MiB + vcpus: 1 +service: + name: dry-fire-vhmh9z8u + uuid: 28762473-9e16-4921-bee5-e57c9e5dff34 + domains: + - fqdn: dry-fire-vhmh9z8u.fra.unikraft.app +networks: +- uuid: dd2581ff-f7e8-4aac-b7e6-73b1d64c5cb7 + private-ip: 10.0.3.133 + mac: 12:b0:0a:00:00:19 +timestamps: + created: just now ``` ```ansi title="kraft" -[●] Deployed successfully! - β”‚ - β”œ───────── name: node-playwright-chromium-q9ynl - β”œ───────── uuid: cb7a1770-6d6a-4f53-a98a-f021ae796313 - β”œ──────── metro: https://api.fra.unikraft.cloud/v1 - β”œ──────── state: running - β”œ─────── domain: https://proud-dream-udqhbav3.fra.unikraft.app - β”œ──────── image: oci://unikraft.io//node-playwright-chromium@sha256:056d5daa3e0f8a91ac18930a9a7714203515a6fae1244b1ecea831f64f593e79 - β”œ─────── memory: 1000 MiB - β”œ────── service: proud-dream-udqhbav3 - β”œ─ private fqdn: node-playwright-chromium-q9ynl.internal - β””─── private ip: 10.0.3.133 +[●] Deployed successfully! + β”‚ + β”œ───────── name: node-playwright-chromium-q9ynl + β”œ───────── uuid: cb7a1770-6d6a-4f53-a98a-f021ae796313 + β”œ──────── metro: https://api.fra.unikraft.cloud/v1 + β”œ──────── state: running + β”œ─────── domain: https://proud-dream-udqhbav3.fra.unikraft.app + β”œ──────── image: oci://unikraft.io//node-playwright-chromium@sha256:056d5daa3e0f8a91ac18930a9a7714203515a6fae1244b1ecea831f64f593e79 + β”œ─────── memory: 1000 MiB + β”œ────── service: proud-dream-udqhbav3 + β”œ─ private fqdn: node-playwright-chromium-q9ynl.internal + β””─── private ip: 10.0.3.133 ``` @@ -201,40 +201,40 @@ kraft cloud deploy \ ```ansi title="unikraft" -metro: fra -name: node-playwright-chromium-b0nyn -uuid: e4d00b20-4fc9-4cba-a29d-846fa733c358 -state: starting -image: /node-playwright-chromium -resources: - memory: 2700MiB - vcpus: 1 -service: - name: fragrant-dawn-v42h579x - uuid: e9424066-5c71-4142-94ee-44acc5ed3d09 - domains: - - fqdn: fragrant-dawn-v42h579x.fra.unikraft.app -networks: -- uuid: 762a85e2-e6ad-435a-867e-4139a5fda69f - private-ip: 10.0.3.157 - mac: 12:b0:0a:00:00:9d -timestamps: - created: just now +metro: fra +name: node-playwright-chromium-b0nyn +uuid: e4d00b20-4fc9-4cba-a29d-846fa733c358 +state: starting +image: /node-playwright-chromium +resources: + memory: 2700MiB + vcpus: 1 +service: + name: fragrant-dawn-v42h579x + uuid: e9424066-5c71-4142-94ee-44acc5ed3d09 + domains: + - fqdn: fragrant-dawn-v42h579x.fra.unikraft.app +networks: +- uuid: 762a85e2-e6ad-435a-867e-4139a5fda69f + private-ip: 10.0.3.157 + mac: 12:b0:0a:00:00:9d +timestamps: + created: just now ``` ```ansi title="kraft" -[●] Deployed successfully! - β”‚ - β”œ───────── name: node-playwright-chromium-b0nyn - β”œ───────── uuid: e4d00b20-4fc9-4cba-a29d-846fa733c358 - β”œ──────── metro: https://api.fra.unikraft.cloud/v1 - β”œ──────── state: running - β”œ─────── domain: https://fragrant-dawn-v42h579x.fra.unikraft.app - β”œ──────── image: oci://unikraft.io//node-playwright-chromium@sha256:57d1773398bc6d4ff94f063d6e9b7400dca66171d04ce78cd8a45789ba0a7d93 - β”œ─────── memory: 2700 MiB - β”œ────── service: fragrant-dawn-v42h579x - β”œ─ private fqdn: node-playwright-chromium-b0nyn.internal - β””─── private ip: 10.0.3.157 +[●] Deployed successfully! + β”‚ + β”œ───────── name: node-playwright-chromium-b0nyn + β”œ───────── uuid: e4d00b20-4fc9-4cba-a29d-846fa733c358 + β”œ──────── metro: https://api.fra.unikraft.cloud/v1 + β”œ──────── state: running + β”œ─────── domain: https://fragrant-dawn-v42h579x.fra.unikraft.app + β”œ──────── image: oci://unikraft.io//node-playwright-chromium@sha256:57d1773398bc6d4ff94f063d6e9b7400dca66171d04ce78cd8a45789ba0a7d93 + β”œ─────── memory: 2700 MiB + β”œ────── service: fragrant-dawn-v42h579x + β”œ─ private fqdn: node-playwright-chromium-b0nyn.internal + β””─── private ip: 10.0.3.157 ``` diff --git a/pages/tutorials/rootfs-volumes-roms.mdx b/pages/tutorials/rootfs-volumes-roms.mdx index 8dffd79d..6bf3840c 100644 --- a/pages/tutorials/rootfs-volumes-roms.mdx +++ b/pages/tutorials/rootfs-volumes-roms.mdx @@ -206,7 +206,7 @@ unikraft run --metro fra \ ```bash title="API" curl -X POST \ - -H "Authorization: Bearer $UKC_TOKEN" \ + -H "Authorization: Bearer ${UKC_TOKEN}" \ -H "Content-Type: application/json" \ https://api.fra.unikraft.cloud/v1/instances \ -d '[{ diff --git a/pages/tutorials/scale-to-zero-triggers.mdx b/pages/tutorials/scale-to-zero-triggers.mdx index dbdbc3e1..e7b60f15 100644 --- a/pages/tutorials/scale-to-zero-triggers.mdx +++ b/pages/tutorials/scale-to-zero-triggers.mdx @@ -93,7 +93,7 @@ Then invoke the following `curl` command to scale down an instance named `my-ins ```bash title="POST /instances/suspend" curl -X POST https://api.fra.unikraft.cloud/v1/instances/suspend \ - -H "Authorization: Bearer $UKC_TOKEN" \ + -H "Authorization: Bearer ${UKC_TOKEN}" \ -H "Content-Type: application/json" \ -d '{ "name": "my-instance", diff --git a/pages/use-cases/api-gateways.mdx b/pages/use-cases/api-gateways.mdx index 506d6c0d..b0a126f1 100644 --- a/pages/use-cases/api-gateways.mdx +++ b/pages/use-cases/api-gateways.mdx @@ -131,45 +131,45 @@ The output shows the Redis instance details: ```ansi title="unikraft" -metro: fra -name: redis-6vgvc -uuid: 63b86d17-06ca-4f95-b921-56e5b3245554 -state: starting -image: /redis -resources: - memory: 256MiB - vcpus: 1 -service: - name: snowy-water-wivk1i4r - uuid: 6612de91-d639-4b5a-95d1-b7ae6e91e3c1 - domains: - - fqdn: tyk-redis.internal -networks: -- uuid: 19e4b80a-9501-448d-99a6-ec3f7b90805e - private-ip: 10.0.0.29 - mac: 12:b0:0a:00:01:49 -timestamps: - created: just now -scale-to-zero: - enabled: true - policy: idle - stateful: true - cooldown-time: 1s +metro: fra +name: redis-6vgvc +uuid: 63b86d17-06ca-4f95-b921-56e5b3245554 +state: starting +image: /redis +resources: + memory: 256MiB + vcpus: 1 +service: + name: snowy-water-wivk1i4r + uuid: 6612de91-d639-4b5a-95d1-b7ae6e91e3c1 + domains: + - fqdn: tyk-redis.internal +networks: +- uuid: 19e4b80a-9501-448d-99a6-ec3f7b90805e + private-ip: 10.0.0.29 + mac: 12:b0:0a:00:01:49 +timestamps: + created: just now +scale-to-zero: + enabled: true + policy: idle + stateful: true + cooldown-time: 1s ``` ```ansi title="kraft" -[●] Deployed successfully! - β”‚ - β”œ───────── name: redis-6vgvc - β”œ───────── uuid: 63b86d17-06ca-4f95-b921-56e5b3245554 - β”œ──────── metro: https://api.fra.unikraft.cloud/v1 - β”œ──────── state: starting - β”œ─────── domain: tyk-redis.internal - β”œ──────── image: oci://unikraft.io//redis@sha256:933b8b7714924eb2de880e0f32792698b14a13c83d5aee0f52dddcab5c97099d - β”œ─────── memory: 256 MiB - β”œ────── service: snowy-water-wivk1i4r - β”œ─ private fqdn: redis-6vgvc.internal - β””─── private ip: 10.0.0.29 +[●] Deployed successfully! + β”‚ + β”œ───────── name: redis-6vgvc + β”œ───────── uuid: 63b86d17-06ca-4f95-b921-56e5b3245554 + β”œ──────── metro: https://api.fra.unikraft.cloud/v1 + β”œ──────── state: starting + β”œ─────── domain: tyk-redis.internal + β”œ──────── image: oci://unikraft.io//redis@sha256:933b8b7714924eb2de880e0f32792698b14a13c83d5aee0f52dddcab5c97099d + β”œ─────── memory: 256 MiB + β”œ────── service: snowy-water-wivk1i4r + β”œ─ private fqdn: redis-6vgvc.internal + β””─── private ip: 10.0.0.29 ``` @@ -205,40 +205,40 @@ The output shows the Tyk instance details: ```ansi title="unikraft" -metro: fra -name: tyk-s9ixd -uuid: 4e8a5e56-2d0b-4ca4-88b4-aa816129a66d -state: starting -image: /tyk -resources: - memory: 256MiB - vcpus: 1 -service: - name: icy-haze-8ph4u8cz - uuid: 89079646-353a-4a19-99ac-5498c7d626ad - domains: - - fqdn: icy-haze-8ph4u8cz.fra.unikraft.app -networks: -- uuid: 8085804f-0fe0-4847-ad6e-8518edba126e - private-ip: 10.0.0.1 - mac: 12:b0:0a:00:0e:b1 -timestamps: - created: just now +metro: fra +name: tyk-s9ixd +uuid: 4e8a5e56-2d0b-4ca4-88b4-aa816129a66d +state: starting +image: /tyk +resources: + memory: 256MiB + vcpus: 1 +service: + name: icy-haze-8ph4u8cz + uuid: 89079646-353a-4a19-99ac-5498c7d626ad + domains: + - fqdn: icy-haze-8ph4u8cz.fra.unikraft.app +networks: +- uuid: 8085804f-0fe0-4847-ad6e-8518edba126e + private-ip: 10.0.0.1 + mac: 12:b0:0a:00:0e:b1 +timestamps: + created: just now ``` ```ansi title="kraft" -[●] Deployed successfully! - β”‚ - β”œ───────── name: tyk-s9ixd - β”œ───────── uuid: 4e8a5e56-2d0b-4ca4-88b4-aa816129a66d - β”œ──────── metro: https://api.fra.unikraft.cloud/v1 - β”œ──────── state: starting - β”œ─────── domain: https://icy-haze-8ph4u8cz.fra.unikraft.app - β”œ──────── image: oci://unikraft.io//tyk@sha256:4954033ada90f980f279e5d825dd7971111a429578ce38be764893ba0d1f358d - β”œ─────── memory: 256 MiB - β”œ────── service: icy-haze-8ph4u8cz - β”œ─ private fqdn: tyk-s9ixd.internal - β””─── private ip: 10.0.0.1 +[●] Deployed successfully! + β”‚ + β”œ───────── name: tyk-s9ixd + β”œ───────── uuid: 4e8a5e56-2d0b-4ca4-88b4-aa816129a66d + β”œ──────── metro: https://api.fra.unikraft.cloud/v1 + β”œ──────── state: starting + β”œ─────── domain: https://icy-haze-8ph4u8cz.fra.unikraft.app + β”œ──────── image: oci://unikraft.io//tyk@sha256:4954033ada90f980f279e5d825dd7971111a429578ce38be764893ba0d1f358d + β”œ─────── memory: 256 MiB + β”œ────── service: icy-haze-8ph4u8cz + β”œ─ private fqdn: tyk-s9ixd.internal + β””─── private ip: 10.0.0.1 ``` @@ -273,15 +273,15 @@ kraft cloud instance list ```ansi title="unikraft" -METRO NAME STATE IMAGE MEMORY VCPUS FQDN CREATED -fra tyk-s9ixd standby /tyk 256MiB 1 icy-haze-8ph4u8cz.fra.unikraft.app just now -fra redis-6vgvc running /redis 256MiB 1 tyk-redis.internal 1 minute ago +METRO NAME STATE IMAGE MEMORY VCPUS FQDN CREATED +fra tyk-s9ixd standby /tyk 256MiB 1 icy-haze-8ph4u8cz.fra.unikraft.app just now +fra redis-6vgvc running /redis 256MiB 1 tyk-redis.internal 1 minute ago ``` ```ansi title="kraft" NAME FQDN STATE STATUS IMAGE MEMORY VCPUS ARGS BOOT TIME -tyk-s9ixd icy-haze-8ph4u8cz.fra.unikraft.app standby standby oci://unikraft.io//tyk@sha256:4954033... 256 MiB 1 158.32 ms -redis-6vgvc tyk-redis.internal running since 1min oci://unikraft.io//redis@sha256:933b8... 256 MiB 1 1811.99 ms +tyk-s9ixd icy-haze-8ph4u8cz.fra.unikraft.app standby standby oci://unikraft.io//tyk@sha256:4954033... 256 MiB 1 158.32 ms +redis-6vgvc tyk-redis.internal running since 1min oci://unikraft.io//redis@sha256:933b8... 256 MiB 1 1811.99 ms ``` diff --git a/pages/use-cases/build-test-environments.mdx b/pages/use-cases/build-test-environments.mdx index 22077b9d..2274b439 100644 --- a/pages/use-cases/build-test-environments.mdx +++ b/pages/use-cases/build-test-environments.mdx @@ -135,33 +135,33 @@ The output shows the instance address and other details: ```ansi title="unikraft" -metro: fra -name: go-build-env -uuid: 650dbbe7-3949-4c93-88e7-6619a9216e0c -state: starting -image: /go-build-env -resources: - memory: 512MiB - vcpus: 1 -networks: -- uuid: 6f7a8b9c-0d1e-2f3a-4b5c-f6a7b8c9d0e1 - private-ip: 10.0.5.4 - mac: 12:b0:6c:3e:ab:95 -timestamps: - created: just now +metro: fra +name: go-build-env +uuid: 650dbbe7-3949-4c93-88e7-6619a9216e0c +state: starting +image: /go-build-env +resources: + memory: 512MiB + vcpus: 1 +networks: +- uuid: 6f7a8b9c-0d1e-2f3a-4b5c-f6a7b8c9d0e1 + private-ip: 10.0.5.4 + mac: 12:b0:6c:3e:ab:95 +timestamps: + created: just now ``` ```ansi title="kraft" -[●] Deployed successfully! - β”‚ - β”œ───────── name: go-build-env - β”œ───────── uuid: 650dbbe7-3949-4c93-88e7-6619a9216e0c - β”œ──────── metro: https://api.fra.unikraft.cloud/v1 - β”œ──────── state: starting - β”œ──────── image: oci://unikraft.io//go-build-env@sha256:1f57e9bb8702d031743acf43164b24cf182158c398f1eda8c5583208ccc9c300 - β”œ─────── memory: 512 MiB - β”œ─ private fqdn: go-build-env.internal - β””─── private ip: 10.0.5.4 +[●] Deployed successfully! + β”‚ + β”œ───────── name: go-build-env + β”œ───────── uuid: 650dbbe7-3949-4c93-88e7-6619a9216e0c + β”œ──────── metro: https://api.fra.unikraft.cloud/v1 + β”œ──────── state: starting + β”œ──────── image: oci://unikraft.io//go-build-env@sha256:1f57e9bb8702d031743acf43164b24cf182158c398f1eda8c5583208ccc9c300 + β”œ─────── memory: 512 MiB + β”œ─ private fqdn: go-build-env.internal + β””─── private ip: 10.0.5.4 ``` @@ -187,12 +187,12 @@ kraft cloud instance template list ```ansi title="unikraft" METRO NAME STATE IMAGE ARGS MEMORY VCPUS CREATED -fra go-build-env template /go-build-env 512MiB 1 just now +fra go-build-env template /go-build-env 512MiB 1 just now ``` ```ansi title="kraft" -NAME IMAGE ARGS CREATED AT -go-build-env oci://unikraft.io//go-build-env@sha256:1cbd6474386c9df546820cd0522030a1bd8702c59490548f10ae10fb8b0a6e14 20 seconds ago +NAME IMAGE ARGS CREATED AT +go-build-env oci://unikraft.io//go-build-env@sha256:1cbd6474386c9df546820cd0522030a1bd8702c59490548f10ae10fb8b0a6e14 20 seconds ago ``` @@ -245,7 +245,7 @@ unikraft run --metro fra \ ```bash title="API" curl -X POST "https://api.fra.unikraft.cloud/v1/instances" \ -H "Accept: application/json" \ - -H "Authorization: Bearer $UKC_TOKEN" \ + -H "Authorization: Bearer ${UKC_TOKEN}" \ -H "Content-Type: application/json" \ -d '{ "name": "go-build-env-rom1", @@ -310,7 +310,7 @@ unikraft run --metro fra \ ```bash title="API" curl -X POST "https://api.fra.unikraft.cloud/v1/instances" \ -H "Accept: application/json" \ - -H "Authorization: Bearer $UKC_TOKEN" \ + -H "Authorization: Bearer ${UKC_TOKEN}" \ -H "Content-Type: application/json" \ -d '{ "name": "go-build-env-rom2", @@ -381,14 +381,14 @@ kraft cloud instance list ```ansi title="unikraft" METRO NAME STATE IMAGE ARGS MEMORY VCPUS FQDN CREATED -fra go-build-env-rom2 standby /go-build-env 512MiB 1 nameless-wood-gw7pbnls.fra.unikraft.app 2 minutes ago -fra go-build-env-rom1 standby /go-build-env 512MiB 1 sparkling-dawn-syowlbtj.fra.unikraft.app 3 minutes ago +fra go-build-env-rom2 standby /go-build-env 512MiB 1 nameless-wood-gw7pbnls.fra.unikraft.app 2 minutes ago +fra go-build-env-rom1 standby /go-build-env 512MiB 1 sparkling-dawn-syowlbtj.fra.unikraft.app 3 minutes ago ``` ```ansi title="kraft" NAME FQDN STATE STATUS IMAGE MEMORY VCPUS ARGS BOOT TIME -go-build-env-rom2 nameless-wood-gw7pbnls.fra.unikraft.app standby standby oci://unikraft.io//go-build-env@sha256:1cbd64... 512 MiB 1 6.98 ms -go-build-env-rom1 sparkling-dawn-syowlbtj.fra.unikraft.app standby standby oci://unikraft.io//go-build-env@sha256:1cbd64... 512 MiB 1 7.86 ms +go-build-env-rom2 nameless-wood-gw7pbnls.fra.unikraft.app standby standby oci://unikraft.io//go-build-env@sha256:1cbd64... 512 MiB 1 6.98 ms +go-build-env-rom1 sparkling-dawn-syowlbtj.fra.unikraft.app standby standby oci://unikraft.io//go-build-env@sha256:1cbd64... 512 MiB 1 7.86 ms ``` diff --git a/pages/use-cases/game-servers.mdx b/pages/use-cases/game-servers.mdx index a39bef43..c055d281 100644 --- a/pages/use-cases/game-servers.mdx +++ b/pages/use-cases/game-servers.mdx @@ -143,24 +143,24 @@ The output shows the instance details: ```ansi title="unikraft" -metro: fra -name: minecraft-tpl -uuid: d9d7be54-5495-45d0-b5af-48be7f30d1d8 -state: starting -image: /minecraft -resources: - memory: 4GiB - vcpus: 4 -roms: -- name: rom - image: c99125f6-6b8c-4f94-b94c-e2d9551b253b - at: /rom -networks: -- uuid: 123b0ed0-9f2f-417c-9307-34424d80e4cb - private-ip: 10.0.0.29 - mac: 12:b0:0a:00:00:1d -timestamps: - created: just now +metro: fra +name: minecraft-tpl +uuid: d9d7be54-5495-45d0-b5af-48be7f30d1d8 +state: starting +image: /minecraft +resources: + memory: 4GiB + vcpus: 4 +roms: +- name: rom + image: c99125f6-6b8c-4f94-b94c-e2d9551b253b + at: /rom +networks: +- uuid: 123b0ed0-9f2f-417c-9307-34424d80e4cb + private-ip: 10.0.0.29 + mac: 12:b0:0a:00:00:1d +timestamps: + created: just now ``` ```bash title="kraft" @@ -229,7 +229,7 @@ kraft cloud instance template list ```ansi title="unikraft" METRO NAME STATE IMAGE ARGS MEMORY VCPUS CREATED -fra minecraft-tpl template /minecraft 4GiB 4 5 seconds ago +fra minecraft-tpl template /minecraft 4GiB 4 5 seconds ago ``` ```ansi title="kraft" @@ -264,26 +264,26 @@ The output includes the service address: ```ansi title="unikraft" -metro: fra -name: minecraft -uuid: 56787b5d-14fb-4908-88d0-e9609d1ac1e0 -state: running -image: /minecraft -resources: - memory: 4GiB - vcpus: 4 -service: hidden-water-ewr8l9sp -roms: -- name: rom - image: c99125f6-6b8c-4f94-b94c-e2d9551b253b - at: /rom -networks: -- uuid: b6c7d615-7e74-49d6-a8b6-14c39323233b - private-ip: 10.0.0.29 - mac: 12:b0:0a:00:00:1d -timestamps: - created: just now -scale-to-zero: policy=on,stateful=true,cooldown-time=5s +metro: fra +name: minecraft +uuid: 56787b5d-14fb-4908-88d0-e9609d1ac1e0 +state: running +image: /minecraft +resources: + memory: 4GiB + vcpus: 4 +service: hidden-water-ewr8l9sp +roms: +- name: rom + image: c99125f6-6b8c-4f94-b94c-e2d9551b253b + at: /rom +networks: +- uuid: b6c7d615-7e74-49d6-a8b6-14c39323233b + private-ip: 10.0.0.29 + mac: 12:b0:0a:00:00:1d +timestamps: + created: just now +scale-to-zero: policy=on,stateful=true,cooldown-time=5s ``` ```bash title="kraft" diff --git a/pages/use-cases/headless-browsers.mdx b/pages/use-cases/headless-browsers.mdx index 289f8560..3ba08611 100644 --- a/pages/use-cases/headless-browsers.mdx +++ b/pages/use-cases/headless-browsers.mdx @@ -120,40 +120,40 @@ The output shows the instance address and other details: ```ansi title="unikraft" -metro: fra -name: httpserver-node-express-puppeteer-7afg3 -uuid: 7bb479d7-5b3e-444f-b07c-eae4da6f57cc -state: starting -image: /httpserver-node-express-puppeteer -resources: - memory: 4096MiB - vcpus: 1 -service: - uuid: 996b9cc1-5a51-e707-d443-5c98ea86ded8 - name: little-snow-7qwu6vv5 - domains: - - fqdn: little-snow-7qwu6vv5.fra.unikraft.app -networks: -- uuid: 034fa25e-9154-7842-ccdd-289256cc7a17 - private-ip: 10.0.3.1 - mac: 12:b0:8f:3c:f5:16 -timestamps: - created: just now +metro: fra +name: httpserver-node-express-puppeteer-7afg3 +uuid: 7bb479d7-5b3e-444f-b07c-eae4da6f57cc +state: starting +image: /httpserver-node-express-puppeteer +resources: + memory: 4096MiB + vcpus: 1 +service: + uuid: 996b9cc1-5a51-e707-d443-5c98ea86ded8 + name: little-snow-7qwu6vv5 + domains: + - fqdn: little-snow-7qwu6vv5.fra.unikraft.app +networks: +- uuid: 034fa25e-9154-7842-ccdd-289256cc7a17 + private-ip: 10.0.3.1 + mac: 12:b0:8f:3c:f5:16 +timestamps: + created: just now ``` ```ansi title="kraft" -[●] Deployed successfully! - β”‚ - β”œ───────── name: httpserver-node-express-puppeteer-7afg3 - β”œ───────── uuid: 7bb479d7-5b3e-444f-b07c-eae4da6f57cc - β”œ──────── metro: https://api.fra.unikraft.cloud/v1 - β”œ──────── state: starting - β”œ─────── domain: https://nameless-fog-0tvh1uov.fra.unikraft.app - β”œ──────── image: oci://unikraft.io//httpserver-node-express-puppeteer@sha256:78d0b180161c876f17d05116b93011ddcd44c76758d6fa0359f05938e67cea65 - β”œ─────── memory: 4096 MiB - β”œ────── service: little-snow-7qwu6vv5 - β”œ─ private fqdn: httpserver-node-express-puppeteer-7afg3.internal - β””─── private ip: 10.0.3.1 +[●] Deployed successfully! + β”‚ + β”œ───────── name: httpserver-node-express-puppeteer-7afg3 + β”œ───────── uuid: 7bb479d7-5b3e-444f-b07c-eae4da6f57cc + β”œ──────── metro: https://api.fra.unikraft.cloud/v1 + β”œ──────── state: starting + β”œ─────── domain: https://nameless-fog-0tvh1uov.fra.unikraft.app + β”œ──────── image: oci://unikraft.io//httpserver-node-express-puppeteer@sha256:78d0b180161c876f17d05116b93011ddcd44c76758d6fa0359f05938e67cea65 + β”œ─────── memory: 4096 MiB + β”œ────── service: little-snow-7qwu6vv5 + β”œ─ private fqdn: httpserver-node-express-puppeteer-7afg3.internal + β””─── private ip: 10.0.3.1 ``` @@ -185,12 +185,12 @@ kraft cloud instance list ```ansi title="unikraft" METRO NAME STATE IMAGE ARGS MEMORY VCPUS FQDN CREATED -fra httpserver-node-express-puppeteer-7afg3 running /httpserver-node-express-puppeteer 4096MiB 1 nameless-fog-0tvh1uov.fra.unikraft.app 2 minutes ago +fra httpserver-node-express-puppeteer-7afg3 running /httpserver-node-express-puppeteer 4096MiB 1 nameless-fog-0tvh1uov.fra.unikraft.app 2 minutes ago ``` ```ansi title="kraft" NAME FQDN STATE STATUS IMAGE MEMORY VCPUS ARGS BOOT TIME -httpserver-node-express-puppeteer-7afg3 nameless-fog-0tvh1uov.fra.unikraft.app running since 6mins oci://unikraft.io//httpserver-node-express-puppeteer@s... 4.0 GiB 1 15.27 ms +httpserver-node-express-puppeteer-7afg3 nameless-fog-0tvh1uov.fra.unikraft.app running since 6mins oci://unikraft.io//httpserver-node-express-puppeteer@s... 4.0 GiB 1 15.27 ms ``` diff --git a/pages/use-cases/remote-desktops.mdx b/pages/use-cases/remote-desktops.mdx index 4bffdf1d..98b20769 100644 --- a/pages/use-cases/remote-desktops.mdx +++ b/pages/use-cases/remote-desktops.mdx @@ -122,39 +122,39 @@ The output shows the instance address and other details: ```ansi title="unikraft" -metro: fra -name: vnc-browser -uuid: 90a59b05-0ae1-4ca6-8383-79c5115355ee -state: starting -image: /novnc-browser -resources: - memory: 4096MiB - vcpus: 1 -service: - uuid: aaf03f7c-65e6-5624-d5f4-84e87450beee - name: weathered-fog-y5jjmwfd - domains: - - fqdn: weathered-fog-y5jjmwfd.fra.unikraft.app -networks: -- uuid: 61708609-d291-572d-4a4c-399413238199 - private-ip: 10.0.0.49 - mac: 12:b0:1e:47:6c:59 -timestamps: - created: just now +metro: fra +name: vnc-browser +uuid: 90a59b05-0ae1-4ca6-8383-79c5115355ee +state: starting +image: /novnc-browser +resources: + memory: 4096MiB + vcpus: 1 +service: + uuid: aaf03f7c-65e6-5624-d5f4-84e87450beee + name: weathered-fog-y5jjmwfd + domains: + - fqdn: weathered-fog-y5jjmwfd.fra.unikraft.app +networks: +- uuid: 61708609-d291-572d-4a4c-399413238199 + private-ip: 10.0.0.49 + mac: 12:b0:1e:47:6c:59 +timestamps: + created: just now ``` ```ansi title="kraft" -[●] Deployed successfully! - β”‚ - β”œ────────── name: vnc-browser - β”œ────────── uuid: 90a59b05-0ae1-4ca6-8383-79c5115355ee - β”œ───────── state: starting - β”œ──────── domain: https://weathered-fog-y5jjmwfd.fra.unikraft.app - β”œ───────── image: oci://unikraft.io//novnc-browser@sha256:fdb4887e84362ebbaf54c713e0d85f547e8ee173fe63a6ab39e94b7e612a9892 - β”œ──────── memory: 4096 MiB - β”œ─────── service: weathered-fog-y5jjmwfd - β”œ── private fqdn: vnc-browser.internal - β””──── private ip: 10.0.0.49 +[●] Deployed successfully! + β”‚ + β”œ────────── name: vnc-browser + β”œ────────── uuid: 90a59b05-0ae1-4ca6-8383-79c5115355ee + β”œ───────── state: starting + β”œ──────── domain: https://weathered-fog-y5jjmwfd.fra.unikraft.app + β”œ───────── image: oci://unikraft.io//novnc-browser@sha256:fdb4887e84362ebbaf54c713e0d85f547e8ee173fe63a6ab39e94b7e612a9892 + β”œ──────── memory: 4096 MiB + β”œ─────── service: weathered-fog-y5jjmwfd + β”œ── private fqdn: vnc-browser.internal + β””──── private ip: 10.0.0.49 ``` @@ -183,12 +183,12 @@ kraft cloud instance list ```ansi title="unikraft" METRO NAME STATE IMAGE ARGS MEMORY VCPUS FQDN CREATED -fra vnc-browser standby /novnc-browser 4.0GiB 1 weathered-fog-y5jjmwfd.fra.unikraft.app 2 minutes ago +fra vnc-browser standby /novnc-browser 4.0GiB 1 weathered-fog-y5jjmwfd.fra.unikraft.app 2 minutes ago ``` ```ansi title="kraft" NAME FQDN STATE STATUS IMAGE MEMORY VCPUS ARGS BOOT TIME -vnc-browser weathered-fog-y5jjmwfd.fra.unikraft.app standby standby oci://unikraft.io//novnc-browser@sha256:... 4.0 GiB 1 7.17 ms +vnc-browser weathered-fog-y5jjmwfd.fra.unikraft.app standby standby oci://unikraft.io//novnc-browser@sha256:... 4.0 GiB 1 7.17 ms ``` diff --git a/pages/use-cases/remote-ides.mdx b/pages/use-cases/remote-ides.mdx index 98b3ef69..3a808aeb 100644 --- a/pages/use-cases/remote-ides.mdx +++ b/pages/use-cases/remote-ides.mdx @@ -133,40 +133,40 @@ The output shows the instance address and other details: ```ansi title="unikraft" -metro: fra -name: code-server-6gxsp -uuid: c1a619a0-e222-4042-94b8-ba4b39353417 -state: starting -image: /visual-studio-code-server -resources: - memory: 2048MiB - vcpus: 1 -service: - uuid: 4a0866e4-3bec-3666-c4aa-61672de542e2 - name: blue-shape-chmxf1g4 - domains: - - fqdn: blue-shape-chmxf1g4.fra.unikraft.app -networks: -- uuid: dcec209f-31a6-b355-a88e-f5ac3edfa20e - private-ip: 10.0.0.49 - mac: 12:b0:ec:d2:df:1c -timestamps: - created: just now +metro: fra +name: code-server-6gxsp +uuid: c1a619a0-e222-4042-94b8-ba4b39353417 +state: starting +image: /visual-studio-code-server +resources: + memory: 2048MiB + vcpus: 1 +service: + uuid: 4a0866e4-3bec-3666-c4aa-61672de542e2 + name: blue-shape-chmxf1g4 + domains: + - fqdn: blue-shape-chmxf1g4.fra.unikraft.app +networks: +- uuid: dcec209f-31a6-b355-a88e-f5ac3edfa20e + private-ip: 10.0.0.49 + mac: 12:b0:ec:d2:df:1c +timestamps: + created: just now ``` ```ansi title="kraft" -[●] Deployed successfully! - β”‚ - β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€ name: code-server-6gxsp - β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€ uuid: c1a619a0-e222-4042-94b8-ba4b39353417 - β”œβ”€β”€β”€β”€β”€β”€β”€β”€ metro: https://api.fra.unikraft.cloud/v1 - β”œβ”€β”€β”€β”€β”€β”€β”€β”€ state: starting - β”œβ”€β”€β”€β”€β”€β”€β”€ domain: https://blue-shape-chmxf1g4.fra.unikraft.app - β”œβ”€β”€β”€β”€β”€β”€β”€β”€ image: oci://unikraft.io//visual-studio-code-server@sha256:633ec8a8dcb342b093c6f055f84fc056ee1abe40ff56e98bd612c4b9d4ddffcb - β”œβ”€β”€β”€β”€β”€β”€β”€ memory: 2048 MiB - β”œβ”€β”€β”€β”€β”€β”€ service: blue-shape-chmxf1g4 - β”œβ”€ private fqdn: code-server-6gxsp.internal - └─── private ip: 10.0.0.49 +[●] Deployed successfully! + β”‚ + β”œ───────── name: code-server-6gxsp + β”œ───────── uuid: c1a619a0-e222-4042-94b8-ba4b39353417 + β”œ──────── metro: https://api.fra.unikraft.cloud/v1 + β”œ──────── state: starting + β”œ─────── domain: https://blue-shape-chmxf1g4.fra.unikraft.app + β”œ──────── image: oci://unikraft.io//visual-studio-code-server@sha256:633ec8a8dcb342b093c6f055f84fc056ee1abe40ff56e98bd612c4b9d4ddffcb + β”œ─────── memory: 2048 MiB + β”œ────── service: blue-shape-chmxf1g4 + β”œ─ private fqdn: code-server-6gxsp.internal + β””─── private ip: 10.0.0.49 ``` @@ -193,13 +193,13 @@ kraft cloud instances list ```ansi title="unikraft" -METRO NAME STATE IMAGE ARGS MEMORY VCPUS FQDN CREATED -fra code-server-6gxsp standby /visual-studio-code-server 2.0GiB 1 blue-shape-chmxf1g4.fra.unikraft.app 2 minutes ago +METRO NAME STATE IMAGE ARGS MEMORY VCPUS FQDN CREATED +fra code-server-6gxsp standby /visual-studio-code-server 2.0GiB 1 blue-shape-chmxf1g4.fra.unikraft.app 2 minutes ago ``` ```ansi title="kraft" -NAME FQDN STATE STATUS IMAGE MEMORY VCPUS ARGS BOOT TIME -code-server-6gxsp blue-shape-chmxf1g4.fra.unikraft.app standby standby oci://unikraft.io//visual-studio-code-server@sha256:... 2.0 GiB 1 8.45 ms +NAME FQDN STATE STATUS IMAGE MEMORY VCPUS ARGS BOOT TIME +code-server-6gxsp blue-shape-chmxf1g4.fra.unikraft.app standby standby oci://unikraft.io//visual-studio-code-server@sha256:... 2.0 GiB 1 8.45 ms ``` @@ -224,12 +224,12 @@ kraft cloud volume list ```ansi title="unikraft" -METRO NAME STATE SIZE CREATED -fra code-workspace mounted 1.0GiB 13 minutes ago +METRO NAME STATE SIZE CREATED +fra code-workspace mounted 1.0GiB 13 minutes ago ``` ```ansi title="kraft" -NAME CREATED AT SIZE ATTACHED TO MOUNTED BY STATE PERSISTENT +NAME CREATED AT SIZE ATTACHED TO MOUNTED BY STATE PERSISTENT code-workspace 13 minutes ago 1.0 GiB code-server-6gxsp code-server-6gxsp mounted true ``` diff --git a/pages/use-cases/sandboxes.mdx b/pages/use-cases/sandboxes.mdx index d20768e0..9263deec 100644 --- a/pages/use-cases/sandboxes.mdx +++ b/pages/use-cases/sandboxes.mdx @@ -131,42 +131,42 @@ The output shows the instance address and other details: ```ansi title="unikraft" -metro: fra -name: openclaw-8tosm -uuid: e2a6183a-721b-4145-bfaf-37a5f859bbc1 -state: starting -image: /openclaw -runtime: - env: +metro: fra +name: openclaw-8tosm +uuid: e2a6183a-721b-4145-bfaf-37a5f859bbc1 +state: starting +image: /openclaw +runtime: + env: PUBKEY: * -resources: - memory: 4GiB - vcpus: 1 -service: - uuid: 7ab20338-b04d-4869-947b-9433e21677b1 - name: divine-flower-bxsaapup - domains: - - fqdn: divine-flower-bxsaapup.fra.unikraft.app -networks: -- uuid: 2b0b120b-6ce5-4b19-ac4c-04ee8f11526e - private-ip: 10.0.12.97 - mac: 12:b0:0a:00:0c:61 -timestamps: - created: just now +resources: + memory: 4GiB + vcpus: 1 +service: + uuid: 7ab20338-b04d-4869-947b-9433e21677b1 + name: divine-flower-bxsaapup + domains: + - fqdn: divine-flower-bxsaapup.fra.unikraft.app +networks: +- uuid: 2b0b120b-6ce5-4b19-ac4c-04ee8f11526e + private-ip: 10.0.12.97 + mac: 12:b0:0a:00:0c:61 +timestamps: + created: just now ``` ```ansi title="kraft" -[●] Deployed successfully! - β”‚ - β”œ───────── name: openclaw-8tosm - β”œ───────── uuid: e2a6183a-721b-4145-bfaf-37a5f859bbc1 - β”œ──────── metro: https://api.fra.unikraft.cloud/v1 - β”œ──────── state: starting - β”œ─────── domain: https://divine-flower-bxsaapup.fra.unikraft.app - β”œ──────── image: oci://unikraft.io//openclaw@sha256:7a3d9f2b5e8c1a4d7f0e3b6c9a2d5f8b1e4a7d0c3f6b9e2a5d8c1f4b7e0a3d6c9 - β”œ─────── memory: 4096 MiB - β”œ─ private fqdn: openclaw-8tosm.internal - β””─── private ip: 10.0.12.97 +[●] Deployed successfully! + β”‚ + β”œ───────── name: openclaw-8tosm + β”œ───────── uuid: e2a6183a-721b-4145-bfaf-37a5f859bbc1 + β”œ──────── metro: https://api.fra.unikraft.cloud/v1 + β”œ──────── state: starting + β”œ─────── domain: https://divine-flower-bxsaapup.fra.unikraft.app + β”œ──────── image: oci://unikraft.io//openclaw@sha256:7a3d9f2b5e8c1a4d7f0e3b6c9a2d5f8b1e4a7d0c3f6b9e2a5d8c1f4b7e0a3d6c9 + β”œ─────── memory: 4096 MiB + β”œ─ private fqdn: openclaw-8tosm.internal + β””─── private ip: 10.0.12.97 ``` @@ -206,13 +206,13 @@ kraft cloud instance list ```ansi title="unikraft" -METRO NAME STATE IMAGE ARGS MEMORY VCPUS FQDN CREATED -fra openclaw-8tosm standby /openclaw 4.0GiB 1 divine-flower-bxsaapup.fra.unikraft.app 2 minutes ago +METRO NAME STATE IMAGE ARGS MEMORY VCPUS FQDN CREATED +fra openclaw-8tosm standby /openclaw 4.0GiB 1 divine-flower-bxsaapup.fra.unikraft.app 2 minutes ago ``` ```ansi title="kraft" -NAME FQDN STATE STATUS IMAGE MEMORY VCPUS ARGS BOOT TIME -openclaw-8tosm divine-flower-bxsaapup.fra.unikraft.app standby standby oci://unikraft.io//openclaw@sha256:7a3d9f... 4.0 GiB 1 8.45 ms +NAME FQDN STATE STATUS IMAGE MEMORY VCPUS ARGS BOOT TIME +openclaw-8tosm divine-flower-bxsaapup.fra.unikraft.app standby standby oci://unikraft.io//openclaw@sha256:7a3d9f... 4.0 GiB 1 8.45 ms ``` diff --git a/pages/use-cases/serverless-databases.mdx b/pages/use-cases/serverless-databases.mdx index d408f918..501b8de1 100644 --- a/pages/use-cases/serverless-databases.mdx +++ b/pages/use-cases/serverless-databases.mdx @@ -121,40 +121,40 @@ The output shows the instance address and other details: ```ansi title="unikraft" -metro: fra -name: postgres-saan9 -uuid: 3a1371f2-68c6-4187-84f8-c080f2b028ca -state: starting -image: /postgres -resources: - memory: 1024MiB - vcpus: 1 -service: - uuid: 8e9d810b-b1da-a30b-fd42-5c30c1900cb5 - name: young-thunder-fbafrsxj - domains: - - fqdn: young-thunder-fbafrsxj.fra.unikraft.app -networks: -- uuid: f1fab4c9-7951-75e3-ea1c-d87e47b4c9e2 - private-ip: 10.0.3.1 - mac: 12:b0:31:34:b1:96 -timestamps: - created: just now +metro: fra +name: postgres-saan9 +uuid: 3a1371f2-68c6-4187-84f8-c080f2b028ca +state: starting +image: /postgres +resources: + memory: 1024MiB + vcpus: 1 +service: + uuid: 8e9d810b-b1da-a30b-fd42-5c30c1900cb5 + name: young-thunder-fbafrsxj + domains: + - fqdn: young-thunder-fbafrsxj.fra.unikraft.app +networks: +- uuid: f1fab4c9-7951-75e3-ea1c-d87e47b4c9e2 + private-ip: 10.0.3.1 + mac: 12:b0:31:34:b1:96 +timestamps: + created: just now ``` ```ansi title="kraft" -[●] Deployed successfully! - β”‚ - β”œ───────── name: postgres-saan9 - β”œ───────── uuid: 3a1371f2-68c6-4187-84f8-c080f2b028ca - β”œ──────── metro: https://api.fra.unikraft.cloud/v1 - β”œ──────── state: starting - β”œ─────── domain: https://young-thunder-fbafrsxj.fra.unikraft.app - β”œ──────── image: oci://unikraft.io//postgres@sha256:2476c0373d663d7604def7c35ffcb4ed4de8ab231309b4f20104b84f31570766 - β”œ─────── memory: 1024 MiB - β”œ────── service: young-thunder-fbafrsxj - β”œ─ private fqdn: postgres-saan9.internal - β””─── private ip: 10.0.3.1 +[●] Deployed successfully! + β”‚ + β”œ───────── name: postgres-saan9 + β”œ───────── uuid: 3a1371f2-68c6-4187-84f8-c080f2b028ca + β”œ──────── metro: https://api.fra.unikraft.cloud/v1 + β”œ──────── state: starting + β”œ─────── domain: https://young-thunder-fbafrsxj.fra.unikraft.app + β”œ──────── image: oci://unikraft.io//postgres@sha256:2476c0373d663d7604def7c35ffcb4ed4de8ab231309b4f20104b84f31570766 + β”œ─────── memory: 1024 MiB + β”œ────── service: young-thunder-fbafrsxj + β”œ─ private fqdn: postgres-saan9.internal + β””─── private ip: 10.0.3.1 ``` @@ -212,12 +212,12 @@ kraft cloud instance list ```ansi title="unikraft" METRO NAME STATE IMAGE ARGS MEMORY VCPUS FQDN CREATED -fra postgres-saan9 running /postgres 1.0GiB 1 young-thunder-fbafrsxj.fra.unikraft.app 2 minutes ago +fra postgres-saan9 running /postgres 1.0GiB 1 young-thunder-fbafrsxj.fra.unikraft.app 2 minutes ago ``` ```ansi title="kraft" NAME FQDN STATE STATUS IMAGE MEMORY VCPUS ARGS BOOT TIME -postgres-saan9 young-thunder-fbafrsxj.fra.unikraft.app running 6 minutes ago oci://unikraft.io//postgres@sha256:... 1.0 GiB 1 603.42 ms +postgres-saan9 young-thunder-fbafrsxj.fra.unikraft.app running 6 minutes ago oci://unikraft.io//postgres@sha256:... 1.0 GiB 1 603.42 ms ``` diff --git a/pages/use-cases/serverless-functions.mdx b/pages/use-cases/serverless-functions.mdx index 13c2c639..1aae65da 100644 --- a/pages/use-cases/serverless-functions.mdx +++ b/pages/use-cases/serverless-functions.mdx @@ -133,33 +133,33 @@ The output shows the instance details: ```ansi title="unikraft" -metro: fra -name: node-exec -uuid: 96608ed2-45e0-4c8f-8269-5d8cd3e4b41a -state: starting -image: /node-code-exec -resources: - memory: 512MiB - vcpus: 1 -networks: -- uuid: 6f7a8b9c-0d1e-2f3a-4b5c-f6a7b8c9d0e1 - private-ip: 10.0.5.4 - mac: 12:b0:6c:3e:ab:95 -timestamps: - created: just now +metro: fra +name: node-exec +uuid: 96608ed2-45e0-4c8f-8269-5d8cd3e4b41a +state: starting +image: /node-code-exec +resources: + memory: 512MiB + vcpus: 1 +networks: +- uuid: 6f7a8b9c-0d1e-2f3a-4b5c-f6a7b8c9d0e1 + private-ip: 10.0.5.4 + mac: 12:b0:6c:3e:ab:95 +timestamps: + created: just now ``` ```ansi title="kraft" -[●] Deployed successfully! - β”‚ - β”œ───────── name: node-exec - β”œ───────── uuid: 96608ed2-45e0-4c8f-8269-5d8cd3e4b41a - β”œ──────── metro: https://api.fra.unikraft.cloud/v1 - β”œ──────── state: starting - β”œ──────── image: oci://unikraft.io//node-code-exec@sha256:71487fd6196987cf65fb89eb84405cb796677aba177dabacf391f09618313328 - β”œ─────── memory: 512 MiB - β”œ─ private fqdn: node-exec.internal - β””─── private ip: 10.0.5.4 +[●] Deployed successfully! + β”‚ + β”œ───────── name: node-exec + β”œ───────── uuid: 96608ed2-45e0-4c8f-8269-5d8cd3e4b41a + β”œ──────── metro: https://api.fra.unikraft.cloud/v1 + β”œ──────── state: starting + β”œ──────── image: oci://unikraft.io//node-code-exec@sha256:71487fd6196987cf65fb89eb84405cb796677aba177dabacf391f09618313328 + β”œ─────── memory: 512 MiB + β”œ─ private fqdn: node-exec.internal + β””─── private ip: 10.0.5.4 ``` @@ -185,7 +185,7 @@ kraft cloud instance template list ```ansi title="unikraft" METRO NAME STATE IMAGE ARGS MEMORY VCPUS CREATED -fra node-exec template /node-code-exec 512MiB 1 just now +fra node-exec template /node-code-exec 512MiB 1 just now ``` ```ansi title="kraft" @@ -248,7 +248,7 @@ unikraft run --metro fra \ ```bash title="API" curl -X POST "https://api.fra.unikraft.cloud/v1/instances" \ -H "Accept: application/json" \ - -H "Authorization: Bearer $UKC_TOKEN" \ + -H "Authorization: Bearer ${UKC_TOKEN}" \ -H "Content-Type: application/json" \ -d '{ "name": "node-exec-rom1", @@ -298,7 +298,7 @@ unikraft run --metro fra \ ```bash title="API" curl -X POST "https://api.fra.unikraft.cloud/v1/instances" \ -H "Accept: application/json" \ - -H "Authorization: Bearer $UKC_TOKEN" \ + -H "Authorization: Bearer ${UKC_TOKEN}" \ -H "Content-Type: application/json" \ -d '{ "name": "node-exec-rom2", @@ -354,14 +354,14 @@ kraft cloud instance list ```ansi title="unikraft" METRO NAME STATE IMAGE ARGS MEMORY VCPUS FQDN CREATED -fra node-exec-rom2 standby /node-code-exec 512MiB 1 nameless-wood-gw7pbnls.fra.unikraft.app 2 minutes ago -fra node-exec-rom1 standby /node-code-exec 512MiB 1 sparkling-dawn-syowlbtj.fra.unikraft.app 3 minutes ago +fra node-exec-rom2 standby /node-code-exec 512MiB 1 nameless-wood-gw7pbnls.fra.unikraft.app 2 minutes ago +fra node-exec-rom1 standby /node-code-exec 512MiB 1 sparkling-dawn-syowlbtj.fra.unikraft.app 3 minutes ago ``` ```ansi title="kraft" NAME FQDN STATE STATUS IMAGE MEMORY VCPUS ARGS BOOT TIME -node-exec-rom2 nameless-wood-gw7pbnls.fra.unikraft.app standby standby oci://unikraft.io//node-code-exec@sha256:71487f... 512 MiB 1 6.98 ms -node-exec-rom1 sparkling-dawn-syowlbtj.fra.unikraft.app standby standby oci://unikraft.io//node-code-exec@sha256:71487f... 512 MiB 1 7.86 ms +node-exec-rom2 nameless-wood-gw7pbnls.fra.unikraft.app standby standby oci://unikraft.io//node-code-exec@sha256:71487f... 512 MiB 1 6.98 ms +node-exec-rom1 sparkling-dawn-syowlbtj.fra.unikraft.app standby standby oci://unikraft.io//node-code-exec@sha256:71487f... 512 MiB 1 7.86 ms ``` diff --git a/scripts/transform_readme.py b/scripts/transform_readme.py index e0a84e31..ebfc2b7e 100755 --- a/scripts/transform_readme.py +++ b/scripts/transform_readme.py @@ -25,13 +25,46 @@ # ANSI color codes ANSI_RESET = '\x1b[0m' ANSI_BOLD = '\x1b[1m' +ANSI_BOLD_OFF = '\x1b[22m' +ANSI_ITALIC = '\x1b[3m' +ANSI_ITALIC_OFF = '\x1b[23m' ANSI_DARK_GRAY = '\x1b[90m' +ANSI_DARK_GRAY_256 = '\x1b[38;5;245m' ANSI_GREEN = '\x1b[92m' ANSI_LIGHT_BLUE = '\x1b[94m' - -STATE_COLORS: dict[str, str] = { +ANSI_KRAFT_HEADER = '\x1b[0;1;39m' +ANSI_KRAFT_RUNNING = '\x1b[0;32m' +ANSI_KRAFT_STANDBY = '\x1b[0;36m' +ANSI_KRAFT_STOPPED = '\x1b[0;31m' +ANSI_KRAFT_STARTING = '\x1b[92m' +ANSI_UNIKRAFT_RUNNING = '\x1b[38;2;0;188;125m' +ANSI_UNIKRAFT_STANDBY = '\x1b[38;2;43;127;255m' +ANSI_UNIKRAFT_STOPPED = '\x1b[38;2;236;0;63m' +ANSI_UNIKRAFT_STARTING = '\x1b[38;2;144;161;185m' + +STATE_COLORS_DEFAULT: dict[str, str] = { "running": ANSI_GREEN, "standby": ANSI_LIGHT_BLUE, + "stopped": '\x1b[91m', + "starting": ANSI_GREEN, + "template": ANSI_LIGHT_BLUE, +} + +STATE_COLORS_BY_SOURCE: dict[str, dict[str, str]] = { + "unikraft": { + "running": ANSI_UNIKRAFT_RUNNING, + "standby": ANSI_UNIKRAFT_STANDBY, + "stopped": ANSI_UNIKRAFT_STOPPED, + "starting": ANSI_UNIKRAFT_STARTING, + "template": ANSI_UNIKRAFT_STANDBY, + }, + "kraft": { + "running": ANSI_KRAFT_RUNNING, + "standby": ANSI_KRAFT_STANDBY, + "stopped": ANSI_KRAFT_STOPPED, + "starting": ANSI_KRAFT_STARTING, + "template": ANSI_KRAFT_STANDBY, + }, } # Import statement for Tabs component @@ -42,6 +75,28 @@ FENCE_PATTERN = re.compile(r"(^```([^\n]*)\n)(.*?)(\n```)$", flags=re.M | re.S) FRONT_MATTER_PATTERN = re.compile(r"^---\n.*?\n---\n\s*", flags=re.S) ADMONITION_PATTERN = re.compile(r"^(\s*)>\s*\*\*([^*]+?):?\*\*:?\s*$") +TITLE_VALUE_PATTERN = re.compile(r'title\s*=\s*"([^"]*)"', flags=re.I) + + +def has_fence_title(fence_info: str) -> bool: + m = TITLE_VALUE_PATTERN.search(fence_info or "") + return bool(m and m.group(1).strip()) + + +def detect_cli_source(fence_info: str) -> str: + """Infer CLI source from fence metadata, defaulting to generic colors.""" + info = (fence_info or "").lower() + if 'title="unikraft"' in info: + return "unikraft" + if 'title="kraft"' in info: + return "kraft" + return "default" + + +def get_state_color_map(fence_info: str) -> dict[str, str]: + """Get per-source state colors from the block metadata.""" + source = detect_cli_source(fence_info) + return STATE_COLORS_BY_SOURCE.get(source, STATE_COLORS_DEFAULT) def extract_title(text: str, example_name: str | None) -> str: @@ -227,15 +282,19 @@ def convert_code_tabs(text: str) -> str: return out -def color_bracket_and_bullet(line: str) -> str: +def color_bracket_and_bullet(line: str, gray: str = ANSI_DARK_GRAY_256) -> str: """Color the [●] Deployed successfully! line.""" - line = line.replace('[', f'{ANSI_DARK_GRAY}[{ANSI_RESET}') + line = line.replace('[', f'{gray}[{ANSI_RESET}') line = line.replace('●', f'{ANSI_GREEN}●{ANSI_RESET}') - line = line.replace(']', f'{ANSI_DARK_GRAY}]{ANSI_RESET}') + line = line.replace(']', f'{gray}]{ANSI_RESET}') return line -def color_box_drawing_line(line: str) -> str: +def color_box_drawing_line( + line: str, + state_colors: dict[str, str], + gray: str = ANSI_DARK_GRAY_256, +) -> str: """Color lines with box-drawing characters and labels.""" # Match pattern like " β”œβ”€β”€β”€β”€β”€β”€ name: value" or " β”‚" (empty line) colon_match = re.search(r'^(\s*)((?:β”‚|β”œ|β””))((?:─*)\s*)([^:]+)(:)(.*)$', line) @@ -249,13 +308,13 @@ def color_box_drawing_line(line: str) -> str: value = colon_match.group(6) # the value after colon # Color the box character - box_colored = f'{ANSI_DARK_GRAY}{box_char}{ANSI_RESET}' + box_colored = f'{gray}{box_char}{ANSI_RESET}' # Color all dashes - dashes_colored = dashes.replace('─', f'{ANSI_DARK_GRAY}─{ANSI_RESET}') + dashes_colored = dashes.replace('─', f'{gray}─{ANSI_RESET}') # Color the label in dark gray (but not the colon) - label_colored = f'{ANSI_DARK_GRAY}{label}{ANSI_RESET}{colon}' + label_colored = f'{gray}{label}{ANSI_RESET}{colon}' # Check if this is the state line and color the value if 'state' in label.lower(): @@ -265,14 +324,14 @@ def color_box_drawing_line(line: str) -> str: value_spaces = value_match.group(1) state_value = value_match.group(2) rest = value_match.group(3) - color = STATE_COLORS.get(state_value.lower(), ANSI_GREEN) + color = state_colors.get(state_value.lower(), ANSI_GREEN) value = f'{value_spaces}{color}{state_value}{ANSI_RESET}{rest}' return leading_space + box_colored + dashes_colored + label_colored + value else: # No colon, just color the box-drawing characters for char in ['β”‚', 'β”œ', 'β””', '─']: - line = line.replace(char, f'{ANSI_DARK_GRAY}{char}{ANSI_RESET}') + line = line.replace(char, f'{gray}{char}{ANSI_RESET}') return line @@ -283,6 +342,9 @@ def repl(m): lang = (m.group(2) or "").strip() body = m.group(3) fence_end = m.group(4) + if not has_fence_title(lang): + return m.group(0) + state_colors = get_state_color_map(lang) # Only operate on blocks that contain the deployed-success marker text. if 'deployed successfully!' not in body.lower(): @@ -310,12 +372,18 @@ def repl(m): # Color the bracket and bullet line: [●] Deployed successfully! if '●' in line and 'deployed successfully' in line.lower(): - colored_lines.append(color_bracket_and_bullet(line)) + colored_lines.append(color_bracket_and_bullet(line, gray=ANSI_DARK_GRAY_256)) continue # Color lines with box-drawing characters (β”‚, β”œ, β””, ─) if any(char in line for char in ['β”‚', 'β”œ', 'β””', '─']): - colored_lines.append(color_box_drawing_line(line)) + colored_lines.append( + color_box_drawing_line( + line, + state_colors=state_colors, + gray=ANSI_DARK_GRAY_256, + ) + ) else: colored_lines.append(line) @@ -334,8 +402,51 @@ def find_state_column_index(header_line: str) -> int | None: return None -def bold_table_header(header_line: str) -> str: +def is_volume_listing_header(header_line: str) -> bool: + cols = [c.strip().upper() for c in re.split(r'\s{2,}', header_line.strip()) if c.strip()] + if "STATE" not in cols or "SIZE" not in cols or "NAME" not in cols: + return False + return any(c == "CREATED" or c == "CREATED AT" for c in cols) + + +def looks_like_listing_header(line: str) -> bool: + """Detect uppercase table headers like NAME/FQDN/SERVICES/CREATED AT.""" + if '\x1b[' in line: + return False + + cols = [c.strip() for c in re.split(r'\s{2,}', line.strip()) if c.strip()] + if len(cols) < 2: + return False + + upper_like = 0 + for col in cols: + if re.fullmatch(r'[A-Z0-9][A-Z0-9\- ]*', col): + upper_like += 1 + + return upper_like >= 2 + + +def find_listing_header_index(lines: list[str]) -> int | None: + for idx, line in enumerate(lines): + if looks_like_listing_header(line): + return idx + return None + + +def format_header_token(token: str, source: str) -> str: + """Apply source-specific header styling.""" + if source == "kraft": + return f"{ANSI_KRAFT_HEADER}{token}{ANSI_RESET}" + if source == "unikraft": + return f"{ANSI_BOLD}{token}{ANSI_BOLD_OFF}" + return f"{ANSI_BOLD}{token}{ANSI_RESET}" + + +def bold_table_header(header_line: str, source: str) -> str: """Make each token in the table header bold.""" + if '\x1b[' in header_line: + return header_line + pieces = re.split(r'(\s{2,})', header_line) cols = pieces[0::2] seps = pieces[1::2] @@ -343,7 +454,7 @@ def bold_table_header(header_line: str) -> str: for i, col in enumerate(cols): token = col.strip() if token and ANSI_BOLD not in token: - cols[i] = col.replace(token, f"{ANSI_BOLD}{token}{ANSI_RESET}", 1) + cols[i] = col.replace(token, format_header_token(token, source), 1) result = "" for i, col in enumerate(cols): @@ -353,19 +464,109 @@ def bold_table_header(header_line: str) -> str: return result -def color_state_value_in_row(line: str, state_col: int) -> str: +def is_unikraft_run_output(body: str) -> bool: + """Detect `unikraft run`-style YAML-ish output blocks.""" + return bool( + re.search(r'^(?:\x1b\[[0-9;]*m)*metro(?:\x1b\[[0-9;]*m)*:\s+\S', body, flags=re.M) + and re.search(r'^(?:\x1b\[[0-9;]*m)*resources(?:\x1b\[[0-9;]*m)*:\s*$', body, flags=re.M) + ) + + +def is_unikraft_structured_output(body: str) -> bool: + """Detect YAML-like unikraft key/value output blocks beyond `run` output.""" + if any(ch in body for ch in ("β”‚", "β”œ", "β””")): + return False + + lines = [line for line in body.splitlines() if line.strip()] + if not lines: + return False + + kv_count = 0 + for line in lines: + plain = re.sub(r'\x1b\[[0-9;]*m', '', line) + if re.match(r'^\s*-?\s*[a-z][a-z0-9-]*\s*:\s*.*$', plain): + kv_count += 1 + + return kv_count >= 3 + + +def style_unikraft_run_fields(body: str) -> str: + """Style unikraft run keys: top-level bold, nested keys italic.""" + lines = body.splitlines() + out: list[str] = [] + + for line in lines: + top = re.match(r'^([a-z][a-z0-9-]*)(:)(\s*.*)$', line) + if top: + key, colon, rest = top.groups() + if line.startswith(ANSI_BOLD): + out.append(line) + continue + out.append(f"{ANSI_BOLD}{key}{ANSI_BOLD_OFF}{colon}{rest}") + continue + + list_key = re.match(r'^(-\s*[a-z][a-z0-9-]*)(:)(\s*.*)$', line) + if list_key: + key, colon, rest = list_key.groups() + if line.startswith(ANSI_ITALIC): + out.append(line) + continue + out.append(f"{ANSI_ITALIC}{key}{ANSI_ITALIC_OFF}{colon}{rest}") + continue + + nested = re.match(r'^(\s+)(-?\s*[a-z][a-z0-9-]*)(:)(\s*.*)$', line) + if nested: + indent, key, colon, rest = nested.groups() + if line.startswith(ANSI_ITALIC): + out.append(line) + continue + out.append(f"{ANSI_ITALIC}{indent}{key}{ANSI_ITALIC_OFF}{colon}{rest}") + continue + + out.append(line) + + return "\n".join(out) + + +def color_state_value_in_row( + line: str, + state_col: int, + state_colors: dict[str, str], + source: str, + volume_listing: bool, + force_non_error_color: str | None = None, +) -> str: """Color the state value in a table row.""" pieces = re.split(r'(\s{2,})', line) cols = pieces[0::2] seps = pieces[1::2] + for i, col in enumerate(cols): + # Keep listing values plain; only header should remain bold. + col = re.sub(r'\x1b\[1m([^\x1b]+?)\x1b\[22m', r'\1', col) + col = re.sub(r'\x1b\[0;1;39m([^\x1b]+?)\x1b\[0m', r'\1', col) + cols[i] = col + if len(cols) > state_col: raw_val = cols[state_col] val = raw_val.strip() - if val and ANSI_GREEN not in val and ANSI_LIGHT_BLUE not in val: - color = STATE_COLORS.get(val.lower(), ANSI_GREEN) - colored = f"{color}{val}{ANSI_RESET}" - cols[state_col] = raw_val.replace(val, colored, 1) + if val and ANSI_RESET not in val: + clean_val = re.sub(r'\x1b\[[0-9;]*m', '', val).lower() + if volume_listing and source != "unikraft": + color = None + elif force_non_error_color is not None: + error_words = {"error", "failed", "faulted", "corrupted", "unavailable"} + if clean_val == "template": + color = state_colors.get("template", ANSI_LIGHT_BLUE) + elif clean_val in error_words: + color = state_colors.get("stopped", '\x1b[91m') + else: + color = force_non_error_color + else: + color = state_colors.get(clean_val, ANSI_GREEN) + if color is not None: + colored = f"{color}{val}{ANSI_RESET}" + cols[state_col] = raw_val.replace(val, colored, 1) # Reconstruct using original separators to preserve spacing result = "" @@ -376,38 +577,83 @@ def color_state_value_in_row(line: str, state_col: int) -> str: return result +def clean_listing_row_emphasis(line: str) -> str: + pieces = re.split(r'(\s{2,})', line) + cols = pieces[0::2] + seps = pieces[1::2] + + for i, col in enumerate(cols): + col = re.sub(r'\x1b\[1m([^\x1b]+?)\x1b\[22m', r'\1', col) + col = re.sub(r'\x1b\[0;1;39m([^\x1b]+?)\x1b\[0m', r'\1', col) + cols[i] = col + + out = "" + for i, col in enumerate(cols): + out += col + if i < len(seps): + out += seps[i] + return out + + +def color_standalone_template_token(text: str, color: str | None) -> str: + if not color: + return text + return re.sub(r'(? str: """Color state values in table-style fenced blocks (STATE column) based on STATE_COLORS.""" def repl(m): + lang = (m.group(2) or "").strip() + if not has_fence_title(lang): + return m.group(0) + source = detect_cli_source(lang) + state_colors = get_state_color_map(lang) fence_start = m.group(1) body = m.group(3) fence_end = m.group(4) + if source == "unikraft" and (is_unikraft_run_output(body) or is_unikraft_structured_output(body)): + body = style_unikraft_run_fields(body) + lines = body.splitlines() - # Find header line that contains NAME and STATE (case-insensitive) - header_idx = None - for idx, line in enumerate(lines): - if re.search(r"\bNAME\b", line, flags=re.I) and re.search(r"\bSTATE\b", line, flags=re.I): - header_idx = idx - break + # Find listing-like header line. + header_idx = find_listing_header_index(lines) if header_idx is None: + if body != m.group(3): + return fence_start + body + fence_end return m.group(0) + # Always bold listing header, even if there is no STATE column. + new_lines = lines[:header_idx] + new_lines.append(bold_table_header(lines[header_idx], source).rstrip('\n')) + # Find the STATE column index state_col = find_state_column_index(lines[header_idx]) + volume_listing = is_volume_listing_header(lines[header_idx]) if state_col is None: - return m.group(0) - - # Process header: make each header token bold - new_lines = lines[:header_idx] - new_lines.append(bold_table_header(lines[header_idx]).rstrip('\n')) + for row in lines[header_idx + 1:]: + if row.strip() == "": + new_lines.append(row) + else: + new_lines.append(clean_listing_row_emphasis(row)) + return fence_start + "\n".join(new_lines) + fence_end # Process data rows j = header_idx + 1 + force_non_error_color = state_colors.get("running", ANSI_GREEN) if volume_listing else None while j < len(lines) and lines[j].strip() != "": - new_lines.append(color_state_value_in_row(lines[j], state_col).rstrip('\n')) + row = color_state_value_in_row( + lines[j], + state_col, + state_colors, + source, + volume_listing, + force_non_error_color=force_non_error_color, + ).rstrip('\n') + new_lines.append(row) j += 1 # Append any remaining lines @@ -421,6 +667,10 @@ def repl(m): def color_yaml_deploy_block(text: str) -> str: """Color state values in YAML-style unikraft deploy output blocks.""" def repl(m): + lang = (m.group(2) or "").strip() + if not has_fence_title(lang): + return m.group(0) + state_colors = get_state_color_map(lang) body = m.group(3) # Only target blocks that look like unikraft YAML deploy output: @@ -432,7 +682,7 @@ def repl(m): if not re.search(r'^name:\s+\S', body, re.M): return m.group(0) # Skip if already colorized - if ANSI_GREEN in body or ANSI_LIGHT_BLUE in body: + if ANSI_RESET in body and re.search(r'\x1b\[[0-9;]*m(starting|running|standby|stopped|template)', body, re.I): return m.group(0) lines = body.split('\n') @@ -444,7 +694,7 @@ def repl(m): spaces = state_m.group(2) val = state_m.group(3) rest = state_m.group(4) - color = STATE_COLORS.get(val.lower(), ANSI_GREEN) + color = state_colors.get(val.lower(), ANSI_GREEN) colored_lines.append(f'{key}{spaces}{color}{val}{ANSI_RESET}{rest}') else: colored_lines.append(line) diff --git a/scripts/update_ansi_colors.py b/scripts/update_ansi_colors.py new file mode 100755 index 00000000..2469a4f1 --- /dev/null +++ b/scripts/update_ansi_colors.py @@ -0,0 +1,467 @@ +#!/usr/bin/env python3 +"""Bulk-update ANSI colors in non-guide MDX docs pages. + +Updates ANSI fenced blocks under docs/pages, excluding docs/pages/guides: +- Applies CLI-specific state colors for running/standby/stopped/starting. +- Normalizes kraft box-output gray tone from 90 to 38;5;245. + +Usage: + update_ansi_colors.py [--root docs/pages] [--write] + +Without --write, runs in dry-run mode and prints files that would change. +""" + +from __future__ import annotations + +import argparse +import re +from pathlib import Path + +ANSI_RESET = "\x1b[0m" +ANSI_BOLD = "\x1b[1m" +ANSI_BOLD_OFF = "\x1b[22m" +ANSI_ITALIC = "\x1b[3m" +ANSI_ITALIC_OFF = "\x1b[23m" +ANSI_KRAFT_HEADER = "\x1b[0;1;39m" +ANSI_KRAFT_STARTING = "\x1b[92m" +ANSI_GRAY_245 = "\x1b[38;5;245m" + +STATE_COLORS_BY_SOURCE: dict[str, dict[str, str]] = { + "unikraft": { + "running": "\x1b[38;2;0;188;125m", + "standby": "\x1b[38;2;43;127;255m", + "stopped": "\x1b[38;2;236;0;63m", + "starting": "\x1b[38;2;144;161;185m", + "template": "\x1b[38;2;43;127;255m", + }, + "kraft": { + "running": "\x1b[0;32m", + "standby": "\x1b[0;36m", + "stopped": "\x1b[0;31m", + "starting": ANSI_KRAFT_STARTING, + "template": "\x1b[0;36m", + }, + "default": { + "running": "\x1b[92m", + "standby": "\x1b[94m", + "stopped": "\x1b[91m", + "starting": "\x1b[92m", + "template": "\x1b[94m", + }, +} + +FENCE_PATTERN = re.compile(r"(^```ansi([^\n]*)\n)(.*?)(\n```)$", flags=re.M | re.S) +TITLE_VALUE_PATTERN = re.compile(r'title\s*=\s*"([^"]*)"', flags=re.I) +ANSI_PATTERN = re.compile(r"\x1b\[[0-9;]*m") +STATE_TOKEN_PATTERN = re.compile( + r"(? str: + info = (fence_info or "").lower() + if 'title="unikraft"' in info: + return "unikraft" + if 'title="kraft"' in info: + return "kraft" + + # Heuristics for unnamed blocks. + lower_body = body.lower() + if "deployed successfully" in lower_body or any(ch in body for ch in ["β”‚", "β”œ", "β””"]): + return "kraft" + if "metro:" in lower_body and "resources:" in lower_body: + return "unikraft" + return "default" + + +def recolor_state_tokens(body: str, source: str) -> str: + state_colors = STATE_COLORS_BY_SOURCE.get(source, STATE_COLORS_BY_SOURCE["default"]) + + # Keep key labels like "stopped:" uncolored; only color state values. + body = STATE_KEY_LABEL_PATTERN.sub(lambda m: f"{m.group(1)}{m.group(2)}", body) + + def repl(match: re.Match[str]) -> str: + token = match.group(1) + color = state_colors.get(token.lower(), STATE_COLORS_BY_SOURCE["default"]["running"]) + return f"{color}{token}{ANSI_RESET}" + + return STATE_TOKEN_PATTERN.sub(repl, body) + + +def recolor_kraft_gray(body: str, source: str) -> str: + if source != "kraft": + return body + body = body.replace("\x1b[90m", ANSI_GRAY_245) + body = body.replace("\x1b[0;90m", ANSI_GRAY_245) + return body + + +def strip_ansi(text: str) -> str: + return ANSI_PATTERN.sub("", text) + + +def color_box_runs(text: str) -> str: + """Wrap contiguous box-drawing character runs with one ANSI sequence.""" + return re.sub(r"[β”‚β”œβ””β”€]+", lambda m: f"{ANSI_GRAY_245}{m.group(0)}{ANSI_RESET}", text) + + +def style_kraft_deploy_tree(body: str, source: str) -> str: + """Normalize kraft deploy-tree output to the captured ANSI style.""" + if source != "kraft" or "deployed successfully!" not in body.lower(): + return body + + state_colors = STATE_COLORS_BY_SOURCE["kraft"] + out: list[str] = [] + + for line in body.splitlines(): + raw = strip_ansi(line) + + # Canonical deployed marker: gray brackets + green bullet. + marker = re.match(r"^(\s*)\[\s*●\s*\]\s*(.*deployed successfully!.*)$", raw, flags=re.I) + if marker: + indent, msg = marker.groups() + out.append(f"{indent}{ANSI_GRAY_245}[{ANSI_RESET}{ANSI_KRAFT_STARTING}●{ANSI_RESET}{ANSI_GRAY_245}]{ANSI_RESET} {msg}") + continue + + # Box line with label/value like: β”œβ”€β”€β”€β”€β”€β”€β”€β”€ state: starting + colon = re.match(r"^(\s*)((?:β”‚|β”œ|β””))((?:─*)\s*)([^:]+)(:)(.*)$", raw) + if colon: + indent, box, dashes, label, colon_char, value = colon.groups() + box_part = color_box_runs(box) + dash_part = color_box_runs(dashes) + label_part = f"{ANSI_GRAY_245}{label}{ANSI_RESET}{colon_char}" + + if label.strip().lower() == "state": + vm = re.match(r"^(\s*)(\S+)(.*)$", value) + if vm: + spaces, state_word, tail = vm.groups() + color = state_colors.get(state_word.lower(), ANSI_KRAFT_STARTING) + value = f"{spaces}{color}{state_word}{ANSI_RESET}{tail}" + + out.append(indent + box_part + dash_part + label_part + value) + continue + + # Box-only line (e.g., " β”‚"). + if any(ch in raw for ch in ("β”‚", "β”œ", "β””", "─")): + normalized = color_box_runs(raw) + out.append(normalized) + continue + + out.append(line) + + return "\n".join(out) + + +def format_header_token(token: str, source: str) -> str: + if source == "kraft": + return f"{ANSI_KRAFT_HEADER}{token}{ANSI_RESET}" + if source == "unikraft": + return f"{ANSI_BOLD}{token}{ANSI_BOLD_OFF}" + return f"{ANSI_BOLD}{token}{ANSI_RESET}" + + +def looks_like_listing_header(line: str) -> bool: + """Detect uppercase table headers like NAME/FQDN/SERVICES/CREATED AT.""" + if "\x1b[" in line: + return False + + cols = [c.strip() for c in re.split(r"\s{2,}", line.strip()) if c.strip()] + if len(cols) < 2: + return False + + upper_like = 0 + for col in cols: + if re.fullmatch(r"[A-Z0-9][A-Z0-9\- ]*", col): + upper_like += 1 + + return upper_like >= 2 + + +def find_state_column_index(header_line: str) -> int | None: + cols = [c.strip() for c in re.split(r"\s{2,}", header_line.strip()) if c.strip()] + for idx, col in enumerate(cols): + if col.upper() == "STATE": + return idx + return None + + +def is_volume_listing_header(header_line: str) -> bool: + cols = [c.strip().upper() for c in re.split(r"\s{2,}", header_line.strip()) if c.strip()] + if "STATE" not in cols or "SIZE" not in cols or "NAME" not in cols: + return False + return any(c == "CREATED" or c == "CREATED AT" for c in cols) + + +def clean_state_word_ansi(cell: str) -> str: + return STATE_WORD_WITH_ANSI_PATTERN.sub(lambda m: m.group(1), cell) + + +def clean_listing_row_emphasis(cell: str) -> str: + """Remove accidental bold wrappers from listing row values.""" + cell = re.sub(r"\x1b\[1m([^\x1b]+?)\x1b\[22m", r"\1", cell) + cell = re.sub(r"\x1b\[0;1;39m([^\x1b]+?)\x1b\[0m", r"\1", cell) + return cell + + +def color_standalone_template_token(text: str, color: str | None) -> str: + if not color: + return text + return re.sub(r"(? tuple[str, bool]: + """Normalize listing rows: de-bold values; color only STATE column when present.""" + lines = body.splitlines() + header_idx = None + for i, line in enumerate(lines): + plain = strip_ansi(line) + cols = [c.strip() for c in re.split(r"\s{2,}", plain.strip()) if c.strip()] + if len(cols) >= 2: + upper_like = sum(1 for c in cols if re.fullmatch(r"[A-Z0-9][A-Z0-9\- ]*", c)) + if upper_like >= 2: + header_idx = i + break + + if header_idx is None: + return body, False + + state_col = find_state_column_index(strip_ansi(lines[header_idx])) + volume_listing = is_volume_listing_header(strip_ansi(lines[header_idx])) + + state_colors = STATE_COLORS_BY_SOURCE.get(source, STATE_COLORS_BY_SOURCE["default"]) + out = lines[:] + i = header_idx + 1 + + while i < len(out) and out[i].strip() != "": + row = out[i] + parts = re.split(r"(\s{2,})", row) + cols = parts[0::2] + seps = parts[1::2] + + for idx, col in enumerate(cols): + col = clean_listing_row_emphasis(col) + token = col.strip() + if not token: + cols[idx] = col + continue + + if state_col is not None and idx == state_col: + token_clean = strip_ansi(token) + if volume_listing and source != "unikraft": + # Keep kraft/default volume states plain. + color = None + elif volume_listing: + error_words = {"error", "failed", "faulted", "corrupted", "unavailable"} + if token_clean.lower() == "template": + color = state_colors.get("template", "\x1b[94m") + elif token_clean.lower() in error_words: + color = state_colors.get("stopped", "\x1b[91m") + else: + color = state_colors.get("running", "\x1b[92m") + else: + color = state_colors.get(token_clean.lower()) + if color: + cols[idx] = col.replace(token, f"{color}{token_clean}{ANSI_RESET}", 1) + else: + cols[idx] = col.replace(token, token_clean, 1) + else: + cleaned = clean_state_word_ansi(col) + cols[idx] = cleaned + + rebuilt = "" + for idx, col in enumerate(cols): + rebuilt += col + if idx < len(seps): + rebuilt += seps[idx] + + # No fallback coloring for kraft/default volume listing rows. + + out[i] = rebuilt + i += 1 + + return "\n".join(out), True + + +def style_listing_header(body: str, source: str) -> str: + lines = body.splitlines() + header_idx = None + for i, line in enumerate(lines): + if looks_like_listing_header(line): + header_idx = i + break + + if header_idx is None: + return body + + header = lines[header_idx] + if "\x1b[" in header: + return body + + parts = re.split(r"(\s{2,})", header) + cols = parts[0::2] + seps = parts[1::2] + + for idx, col in enumerate(cols): + token = col.strip() + if token: + cols[idx] = col.replace(token, format_header_token(token, source), 1) + + rebuilt = "" + for idx, col in enumerate(cols): + rebuilt += col + if idx < len(seps): + rebuilt += seps[idx] + + lines[header_idx] = rebuilt + return "\n".join(lines) + + +def is_unikraft_run_output(body: str) -> bool: + return bool( + re.search(r"^(?:\x1b\[[0-9;]*m)*metro(?:\x1b\[[0-9;]*m)*:\s+\S", body, flags=re.M) + and re.search(r"^(?:\x1b\[[0-9;]*m)*resources(?:\x1b\[[0-9;]*m)*:\s*$", body, flags=re.M) + ) + + +def is_unikraft_structured_output(body: str) -> bool: + if any(ch in body for ch in ("β”‚", "β”œ", "β””")): + return False + + lines = [line for line in body.splitlines() if line.strip()] + if not lines: + return False + + kv_count = 0 + for line in lines: + plain = strip_ansi(line) + if re.match(r"^\s*-?\s*[a-z][a-z0-9-]*\s*:\s*.*$", plain): + kv_count += 1 + + return kv_count >= 3 + + +def style_unikraft_run_fields(body: str) -> str: + lines = body.splitlines() + out: list[str] = [] + + for line in lines: + top = re.match(r"^([a-z][a-z0-9-]*)(:)(\s*.*)$", line) + if top: + key, colon, rest = top.groups() + if line.startswith(ANSI_BOLD): + out.append(line) + continue + out.append(f"{ANSI_BOLD}{key}{ANSI_BOLD_OFF}{colon}{rest}") + continue + + list_key = re.match(r"^(-\s*[a-z][a-z0-9-]*)(:)(\s*.*)$", line) + if list_key: + key, colon, rest = list_key.groups() + if line.startswith(ANSI_ITALIC): + out.append(line) + continue + out.append(f"{ANSI_ITALIC}{key}{ANSI_ITALIC_OFF}{colon}{rest}") + continue + + nested = re.match(r"^(\s+)(-?\s*[a-z][a-z0-9-]*)(:)(\s*.*)$", line) + if nested: + indent, key, colon, rest = nested.groups() + if line.startswith(ANSI_ITALIC): + out.append(line) + continue + out.append(f"{ANSI_ITALIC}{indent}{key}{ANSI_ITALIC_OFF}{colon}{rest}") + continue + + out.append(line) + + return "\n".join(out) + + +def transform_text(text: str) -> tuple[str, int]: + changes = 0 + + def repl(match: re.Match[str]) -> str: + nonlocal changes + fence_start = match.group(1) + info = (match.group(2) or "").strip() + body = match.group(3) + fence_end = match.group(4) + + # Leave unnamed/empty-title ANSI blocks untouched. + title_match = TITLE_VALUE_PATTERN.search(info) + if not title_match or not title_match.group(1).strip(): + return match.group(0) + + source = detect_cli_source(info, body) + new_body = recolor_kraft_gray(body, source) + new_body = style_kraft_deploy_tree(new_body, source) + new_body = style_listing_header(new_body, source) + new_body, handled_listing_state = color_state_in_listing_rows(new_body, source) + if source == "unikraft" and (is_unikraft_run_output(new_body) or is_unikraft_structured_output(new_body)): + new_body = style_unikraft_run_fields(new_body) + if not handled_listing_state: + new_body = recolor_state_tokens(new_body, source) + + if new_body != body: + changes += 1 + return fence_start + new_body + fence_end + + return FENCE_PATTERN.sub(repl, text), changes + + +def iter_target_files(root: Path) -> list[Path]: + files = [] + for path in root.rglob("*.mdx"): + rel = path.as_posix() + if "/guides/" in rel: + continue + files.append(path) + return sorted(files) + + +def main() -> int: + parser = argparse.ArgumentParser(description="Bulk-update ANSI colors in non-guide MDX files") + parser.add_argument("--root", default="pages", help="Root directory to scan") + parser.add_argument("--write", action="store_true", help="Write changes to disk") + args = parser.parse_args() + + root = Path(args.root) + if not root.exists(): + raise SystemExit(f"error: root does not exist: {root}") + + total_blocks = 0 + changed_files: list[Path] = [] + + for path in iter_target_files(root): + original = path.read_text(encoding="utf-8") + updated, changed_blocks = transform_text(original) + if changed_blocks == 0: + continue + + total_blocks += changed_blocks + changed_files.append(path) + + if args.write: + path.write_text(updated, encoding="utf-8") + + mode = "write" if args.write else "dry-run" + print(f"mode={mode}") + print(f"changed_files={len(changed_files)}") + print(f"changed_ansi_blocks={total_blocks}") + for path in changed_files: + print(path.as_posix()) + + return 0 + + +if __name__ == "__main__": + raise SystemExit(main())