mirror of
https://github.com/psviderski/uncloud.git
synced 2026-08-26 11:03:34 +00:00
docs: add 'Deploy an app' how-to guide, init 'Image tag format' reference
This commit is contained in:
@@ -0,0 +1,434 @@
|
|||||||
|
# Deploy an app
|
||||||
|
|
||||||
|
Deploy a containerised application to your Uncloud cluster using either source code or pre-built images.
|
||||||
|
|
||||||
|
This guide covers both scenarios:
|
||||||
|
|
||||||
|
- **[Deploy from source code](#deploy-from-source-code)**: Build Docker images from your application code and use them
|
||||||
|
to deploy service containers
|
||||||
|
- **[Deploy pre-built images](#deploy-pre-built-images)**: Deploy service containers using existing images from a
|
||||||
|
registry or your local machine
|
||||||
|
|
||||||
|
Uncloud uses [Compose Specification](https://compose-spec.io/) for defining the deployment configuration of your app's
|
||||||
|
services. It implements the most common Compose features with some Uncloud-specific extensions. See
|
||||||
|
[Compose support matrix](../8-compose-file-reference/1-support-matrix.md) for details.
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
- `uc` CLI [installed](../2-getting-started/1-install-cli.md) on your local machine
|
||||||
|
- An Uncloud cluster with at least one machine (see [Quick start](../2-getting-started/2-deploy-demo-app.md))
|
||||||
|
- Basic knowledge of [Compose Specification](https://compose-spec.io/)
|
||||||
|
|
||||||
|
## Deploy from source code
|
||||||
|
|
||||||
|
Use this scenario when you want to build and deploy your application from
|
||||||
|
a [Dockerfile](https://docs.docker.com/reference/dockerfile/) and source code available on your local machine.
|
||||||
|
|
||||||
|
### 1. Create a Compose file
|
||||||
|
|
||||||
|
Create a `compose.yaml` file in your application directory with a
|
||||||
|
[`build`](https://github.com/compose-spec/compose-spec/blob/main/spec.md#build) section for each service you want to
|
||||||
|
build. Here is a minimal example of a Compose file that builds and deploys a web app that consists of a single service
|
||||||
|
called `web`:
|
||||||
|
|
||||||
|
```yaml title="compose.yaml"
|
||||||
|
services:
|
||||||
|
web:
|
||||||
|
# Build an image from the Dockerfile in the current directory
|
||||||
|
build: .
|
||||||
|
# Publish the container port 8000 as https://app.example.com
|
||||||
|
x-ports:
|
||||||
|
- app.example.com:8000/https
|
||||||
|
```
|
||||||
|
|
||||||
|
If you don't have a Dockerfile for building an image from your source code, create one in the same directory.
|
||||||
|
|
||||||
|
### 2. Build and deploy your app
|
||||||
|
|
||||||
|
To build and deploy services defined in your Compose file, navigate to the directory with `compose.yaml` and run:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
uc deploy
|
||||||
|
```
|
||||||
|
|
||||||
|
This command looks for a Compose file in your current working directory and:
|
||||||
|
|
||||||
|
1. **Builds images** for services with a `build` section using your local Docker and tags them with the current Git
|
||||||
|
version
|
||||||
|
2. **Pushes built images** directly **to cluster machines** using
|
||||||
|
[unregistry](https://github.com/psviderski/unregistry), transferring only the missing layers
|
||||||
|
3. **Plans the deployment** and shows you what will change, asking for confirmation
|
||||||
|
4. **Creates any missing volumes** on target machines
|
||||||
|
5. **Deploys service containers** using the built images and latest configuration changes with zero-downtime rolling
|
||||||
|
updates
|
||||||
|
|
||||||
|
See the [uc deploy](../9-cli-reference/uc_deploy.md) reference for all available options.
|
||||||
|
|
||||||
|
Watch `uc deploy` building and deploying a demo app (18 seconds):
|
||||||
|
|
||||||
|
<video controls width="100%">
|
||||||
|
<source src="https://media.uncloud.run/docs/uc-deploy-demo.mp4"/>
|
||||||
|
</video>
|
||||||
|
|
||||||
|
### Customise the build configuration
|
||||||
|
|
||||||
|
If you need more control over the build configuration, you can specify additional attributes in the `build` section of
|
||||||
|
your Compose file. For example, to specify a custom Dockerfile location, set build-time arguments, or build
|
||||||
|
multi-platform images.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
services:
|
||||||
|
web:
|
||||||
|
build:
|
||||||
|
# Relative path to the directory with your Dockerfile
|
||||||
|
context: ./backend
|
||||||
|
# Set build-time variables defined as ARG in your Dockerfile
|
||||||
|
args:
|
||||||
|
ALPINE_VERSION: 3.22
|
||||||
|
BUILD_ENV: prod
|
||||||
|
# Build a multi-platform image
|
||||||
|
platforms:
|
||||||
|
- linux/amd64
|
||||||
|
- linux/arm64
|
||||||
|
```
|
||||||
|
|
||||||
|
You can pass additional build arguments or override existing ones using the `--build-arg` flag with `uc deploy`:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
uc deploy --build-arg BUILD_ENV=dev
|
||||||
|
```
|
||||||
|
|
||||||
|
You can also use advanced features like build caches, secrets, and SSH access.
|
||||||
|
See [Compose Build Specification](https://github.com/compose-spec/compose-spec/blob/main/build.md) for all supported
|
||||||
|
attributes.
|
||||||
|
|
||||||
|
:::info note
|
||||||
|
|
||||||
|
To build multi-platform images, you need to configure your local Docker to use the
|
||||||
|
[containerd image store](https://docs.docker.com/desktop/features/containerd/).
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
|
### Customise image tags
|
||||||
|
|
||||||
|
If you don't specify the `image` attribute, `uc deploy` tags built images with a Git-based version like
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# <project>/<service>:<git datetime>.<short git sha>
|
||||||
|
myapp/web:2025-10-30-223604.84d33bb
|
||||||
|
```
|
||||||
|
|
||||||
|
It uses your local date/time if the working directory is not a Git repository.
|
||||||
|
|
||||||
|
You can customise the image name and tag format using the `image` attribute. It can be a static name or a dynamic
|
||||||
|
template using [environment variables](https://github.com/compose-spec/compose-spec/blob/main/spec.md#interpolation) and
|
||||||
|
the [Go template](https://pkg.go.dev/text/template) syntax.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
services:
|
||||||
|
web:
|
||||||
|
build: .
|
||||||
|
# Custom image name and tag format
|
||||||
|
image: myapp:{{gitdate "20060102"}}.{{gitsha 7}}.${GITHUB_RUN_ID:-local}{{if .Git.IsDirty}}.dirty{{end}}
|
||||||
|
```
|
||||||
|
|
||||||
|
This generates tags like:
|
||||||
|
|
||||||
|
- `myapp:20251030.84d33bb.local.dirty` when building locally with uncommitted changes
|
||||||
|
- `myapp:20251030.84d33bb.1234` when building in CI with `GITHUB_RUN_ID=1234` and a clean repo
|
||||||
|
|
||||||
|
`uc deploy` renders the image templates when it loads the Compose file and then uses the resulting names for the build
|
||||||
|
and deploy stages.
|
||||||
|
|
||||||
|
See the [Image tag format](../8-compose-file-reference/2-image-tag-format.md) reference for all available template
|
||||||
|
variables and functions.
|
||||||
|
|
||||||
|
### Separate build and deploy steps
|
||||||
|
|
||||||
|
You might want to build and deploy services as separate commands. For example, to run them as separate steps in your
|
||||||
|
CI/CD pipeline or have more control over the build and deploy process. Here are the commands that are equivalent to
|
||||||
|
`uc deploy`:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
# Build images and push to cluster machines
|
||||||
|
uc build --push
|
||||||
|
|
||||||
|
# Deploy services using the built images
|
||||||
|
uc deploy --no-build
|
||||||
|
```
|
||||||
|
|
||||||
|
### Deploy configuration changes only
|
||||||
|
|
||||||
|
You can use the `--no-build` flag with `uc deploy` to deploy only the configuration changes in your Compose file if your
|
||||||
|
source code and images haven't changed. However, the image tag may still change if you're using
|
||||||
|
[dynamic tags](#customise-image-tags) based on the Git state (default). In that case, the deploy will likely fail
|
||||||
|
because the new tag won't be found on cluster machines.
|
||||||
|
|
||||||
|
Use less sensitive dynamic tags or specify the built and pushed images you want to deploy explicitly to avoid this
|
||||||
|
issue.
|
||||||
|
|
||||||
|
The recommended approach though is to commit all your changes, including the configuration ones, to the repo. Then
|
||||||
|
rebuild and deploy your services from a clean repo state. This way, the image tags will always reflect the exact source
|
||||||
|
code and configuration used for the deployment.
|
||||||
|
|
||||||
|
## Deploy pre-built images
|
||||||
|
|
||||||
|
Use this scenario when you want to deploy your application using pre-built images from a container registry (for
|
||||||
|
example, Docker Hub or GitHub Container Registry) or your local Docker.
|
||||||
|
|
||||||
|
### 1. Create a Compose file
|
||||||
|
|
||||||
|
Create a `compose.yaml` file that references an image from a registry using the
|
||||||
|
[`image`](https://github.com/compose-spec/compose-spec/blob/main/spec.md#image) attribute. It's important that you don't
|
||||||
|
include a `build` section for services using pre-built images. Here is a minimal example of a Compose file that deploys
|
||||||
|
an app that consists of a single `nginx` service:
|
||||||
|
|
||||||
|
```yaml title="compose.yaml"
|
||||||
|
services:
|
||||||
|
nginx:
|
||||||
|
# Use the nginx image from Docker Hub
|
||||||
|
image: nginx:latest
|
||||||
|
# Publish the container port 80 as https://nginx.example.com
|
||||||
|
x-ports:
|
||||||
|
- nginx.example.com:80/https
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Deploy your app
|
||||||
|
|
||||||
|
To deploy services defined in your Compose file, navigate to the directory with `compose.yaml` and run:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
uc deploy
|
||||||
|
```
|
||||||
|
|
||||||
|
This command looks for a Compose file in your current working directory and:
|
||||||
|
|
||||||
|
1. **Plans the deployment** and shows you what will change, asking for confirmation
|
||||||
|
2. **Creates any missing volumes** on target machines
|
||||||
|
3. **Pulls images** from a registry on cluster machines where services are deployed according to the
|
||||||
|
[`pull_policy`](https://github.com/compose-spec/compose-spec/blob/main/spec.md#pull_policy)
|
||||||
|
4. **Deploys service containers** using the pulled images and latest configuration changes with zero-downtime rolling
|
||||||
|
updates
|
||||||
|
|
||||||
|
### Control image pulling
|
||||||
|
|
||||||
|
By default, `uc deploy` pulls an image from a registry only if it's missing on a target machine. You can change this
|
||||||
|
behavior using the [`pull_policy`](https://github.com/compose-spec/compose-spec/blob/main/spec.md#pull_policy)
|
||||||
|
attribute. For example, to always pull the latest version of an image before deploying.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
services:
|
||||||
|
nginx:
|
||||||
|
image: nginx:alpine
|
||||||
|
# Always pull the latest :alpine tag before deploying
|
||||||
|
pull_policy: always
|
||||||
|
```
|
||||||
|
|
||||||
|
Available `pull_policy` values:
|
||||||
|
|
||||||
|
- `always`: Always pull the image from the registry before deploying
|
||||||
|
- `missing` (default): Pull only if the image isn't available on the target machine
|
||||||
|
- `never`: Never pull, the image must be present on the target machine or the deploy will fail
|
||||||
|
|
||||||
|
### Pull from a private registry
|
||||||
|
|
||||||
|
If your images are in a private registry, `uc deploy` needs an authentication token to pull them. You can provide it by
|
||||||
|
either:
|
||||||
|
|
||||||
|
- Logging in to the registry using your local Docker (recommended)
|
||||||
|
- Logging in to the registry using Docker on each cluster machine you want to deploy to
|
||||||
|
|
||||||
|
When you're logged in using your local Docker, `uc deploy` automatically passes your local Docker credentials for the
|
||||||
|
private registry to cluster machines when pulling images. This way, you don't need to log in on each machine manually.
|
||||||
|
|
||||||
|
See [`docker login`](https://docs.docker.com/reference/cli/docker/login/) for instructions on how to log in to a private
|
||||||
|
registry.
|
||||||
|
|
||||||
|
### Push local images to cluster machines
|
||||||
|
|
||||||
|
You can also deploy pre-built images that exist only in your local Docker and are not available for pulling on cluster
|
||||||
|
machines. For example, images you built locally outside of the Compose workflow or pulled from a private registry that
|
||||||
|
is unreachable from your cluster machines. This is useful for deploying to air-gapped or restricted environments.
|
||||||
|
|
||||||
|
You can push these local images directly to your cluster machines using the
|
||||||
|
[`uc image push`](../9-cli-reference/uc_image_push.md) command:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
# Push to *all* cluster machines
|
||||||
|
uc image push myapp:latest
|
||||||
|
|
||||||
|
# Push to specific machines only
|
||||||
|
uc image push myapp:latest -m machine1,machine2
|
||||||
|
```
|
||||||
|
|
||||||
|
This command uploads the image from your local Docker to the cluster machines using
|
||||||
|
[unregistry](https://github.com/psviderski/unregistry) running as part of the Uncloud daemon on each machine. It
|
||||||
|
efficiently transfers only the image layers that don't already exist on the target machines.
|
||||||
|
|
||||||
|
After pushing the image, update your Compose file to reference it:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
services:
|
||||||
|
web:
|
||||||
|
# Reference the image you just pushed
|
||||||
|
image: myapp:latest
|
||||||
|
# Never try to pull from a registry since the image is only available locally
|
||||||
|
pull_policy: never
|
||||||
|
```
|
||||||
|
|
||||||
|
Run [`uc images`](../9-cli-reference/uc_images.md) to verify that the image is available on the target machines.
|
||||||
|
|
||||||
|
Then deploy as usual:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
uc deploy
|
||||||
|
```
|
||||||
|
|
||||||
|
:::tip
|
||||||
|
|
||||||
|
Set `pull_policy: never` when using local images to prevent `uc deploy` from trying to pull them from a registry.
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
|
## Mix source builds and pre-built images
|
||||||
|
|
||||||
|
Define multiple services in your Compose file, mixing source builds and pre-built images:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
services:
|
||||||
|
web:
|
||||||
|
# Build web service from source
|
||||||
|
build: .
|
||||||
|
x-ports:
|
||||||
|
- app.example.com:8000/https
|
||||||
|
|
||||||
|
db:
|
||||||
|
# Pull a pre-built PostgreSQL image from Docker Hub
|
||||||
|
image: postgres:18
|
||||||
|
environment:
|
||||||
|
POSTGRES_PASSWORD: ${DB_PASSWORD}
|
||||||
|
```
|
||||||
|
|
||||||
|
`uc deploy` builds and pushes images for services with a `build` section and pulls images from a registry for services
|
||||||
|
with only an `image` attribute.
|
||||||
|
|
||||||
|
:::warning
|
||||||
|
|
||||||
|
**Service names must be globally unique** across all Compose files deployed to the same cluster.
|
||||||
|
|
||||||
|
Unlike Docker Compose or Docker Swarm, Uncloud doesn't automatically prefix service names with project or stack names.
|
||||||
|
Choose unique names to avoid conflicts with services deployed from other Compose files.
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
|
## Deploy to specific machines
|
||||||
|
|
||||||
|
By default, Uncloud randomly chooses available machines to run your services on, spreading multiple replicas of a
|
||||||
|
service across all machines for high availability. You can restrict which machines can run your service using the
|
||||||
|
[`x-machines`](../8-compose-file-reference/1-support-matrix.md#x-machines) extension in your Compose file.
|
||||||
|
|
||||||
|
This is useful when you want to:
|
||||||
|
|
||||||
|
- Deploy services to machines in a specific region or data center
|
||||||
|
- Run services only on machines with specific hardware (for example, GPUs or ARM processors)
|
||||||
|
- Keep certain services isolated to dedicated machines
|
||||||
|
- Deploy to a subset of machines for testing before rolling out cluster-wide
|
||||||
|
|
||||||
|
Here's a basic example:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
services:
|
||||||
|
web:
|
||||||
|
build: .
|
||||||
|
x-ports:
|
||||||
|
- app.example.com:8000/https
|
||||||
|
# Spread 3 replicas across machine-1 and machine-2 only
|
||||||
|
scale: 3
|
||||||
|
x-machines:
|
||||||
|
- machine-1
|
||||||
|
- machine-2
|
||||||
|
|
||||||
|
db:
|
||||||
|
image: postgres:18
|
||||||
|
environment:
|
||||||
|
POSTGRES_PASSWORD: ${DB_PASSWORD}
|
||||||
|
volumes:
|
||||||
|
- db-data:/var/lib/postgresql
|
||||||
|
# Create the db-data volume and run the DB container on machine-db
|
||||||
|
x-machines: machine-db
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
db-data:
|
||||||
|
```
|
||||||
|
|
||||||
|
When you deploy this Compose file with `uc deploy`:
|
||||||
|
|
||||||
|
- The `web` service will create and spread its 3 replicas only across `machine-1` and `machine-2`
|
||||||
|
- The `db` service will create its `db-data` volume and run only on `machine-db`
|
||||||
|
|
||||||
|
:::tip
|
||||||
|
|
||||||
|
Use [`uc machine ls`](../9-cli-reference/uc_machine_ls.md) to see available machines in your cluster.
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
|
### Push images to specific machines only
|
||||||
|
|
||||||
|
When building from source, `uc deploy` and `uc build --push` automatically push images to **all** cluster machines by
|
||||||
|
default. This ensures images are available wherever services might be deployed.
|
||||||
|
|
||||||
|
If you're using `x-machines` to restrict deployments to specific machines, `uc deploy` and `uc build --push` push images
|
||||||
|
only to those machines. This saves time and bandwidth by uploading images only where they're needed.
|
||||||
|
|
||||||
|
## Run one replica on each machine
|
||||||
|
|
||||||
|
To deploy exactly one service replica on each machine in your cluster, set
|
||||||
|
[`mode: global`](https://github.com/compose-spec/compose-spec/blob/main/deploy.md#mode) under the `deploy` section. It's
|
||||||
|
useful for cluster-wide services like monitoring agents, log collectors, or reverse proxies.
|
||||||
|
|
||||||
|
You can also combine `mode: global` with `x-machines` to run one container on each machine in a specific set.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
services:
|
||||||
|
monitoring:
|
||||||
|
image: quay.io/prometheus/node-exporter:latest
|
||||||
|
deploy:
|
||||||
|
# Run one container on each machine in the cluster
|
||||||
|
mode: global
|
||||||
|
# Optionally run one container on each of these specific machines only
|
||||||
|
#x-machines:
|
||||||
|
# - machine-1
|
||||||
|
# - machine-2
|
||||||
|
```
|
||||||
|
|
||||||
|
## Use a different Compose file location
|
||||||
|
|
||||||
|
If your Compose file has a different name or location, use the `-f/--file` flag to specify its path:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
uc deploy -f path/to/your-compose.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
You can also specify multiple Compose files to merge configurations:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
uc deploy -f compose.yaml -f compose.prod.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
See [Use multiple Compose files](https://docs.docker.com/compose/how-tos/multiple-compose-files/) for details on how you
|
||||||
|
can customise your Compose application for different environments or workflows.
|
||||||
|
|
||||||
|
## Verify your deployment
|
||||||
|
|
||||||
|
After deploying your app, you can verify that your services are running as expected by listing all deployed services in
|
||||||
|
the cluster:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
uc ls
|
||||||
|
```
|
||||||
|
|
||||||
|
and inspecting the status of containers for a specific service:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
uc inspect web
|
||||||
|
```
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
# Image tag format
|
||||||
|
|
||||||
|
TBD
|
||||||
Reference in New Issue
Block a user