Skip to content

Commit 6c14e86

Browse files
authored
Merge pull request #27 from Dstack-TEE/launcher
Add example launcher pattern
2 parents d876c1a + fc792b6 commit 6c14e86

6 files changed

Lines changed: 223 additions & 0 deletions

File tree

launcher/Dockerfile

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
FROM debian:bookworm-slim@sha256:4b44499bc2a6c78d726f3b281e6798009c0ae1f034b0bfaf6a227147dcff928b
2+
3+
# Use a specific Debian snapshot for reproducible builds
4+
RUN set -e; \
5+
# Create a sources.list file pointing to a specific snapshot
6+
echo 'deb [check-valid-until=no] https://snapshot.debian.org/archive/debian/20250411T024939Z bookworm main' > /etc/apt/sources.list && \
7+
echo 'deb [check-valid-until=no] https://snapshot.debian.org/archive/debian-security/20250411T024939Z bookworm-security main' >> /etc/apt/sources.list && \
8+
echo 'Acquire::Check-Valid-Until "false";' > /etc/apt/apt.conf.d/10no-check-valid-until && \
9+
# Install packages with exact versions for reproducibility
10+
apt-get -o Acquire::Check-Valid-Until=false update && \
11+
apt-get install -y --no-install-recommends docker-compose=1.29.2-3 && \
12+
rm -rf /var/lib/apt/lists/* && \
13+
rm -rf /var/log/* /var/cache/ldconfig/aux-cache
14+
15+
COPY entrypoint.sh get-latest.sh /scripts/
16+
RUN chmod +x /scripts/*.sh
17+
ENV PATH="/scripts:${PATH}"
18+
RUN mkdir -p /app-data
19+
CMD ["/scripts/entrypoint.sh"]

launcher/README.md

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
# Dstack Launcher Pattern Example
2+
3+
This repository demonstrates the dstack launcher pattern - a template for implementing automated container updates in your applications.
4+
5+
## What is the Launcher Pattern?
6+
7+
The launcher pattern is a containerized approach to managing application updates. It consists of:
8+
9+
1. A **launcher container** that runs continuously and checks for updates
10+
2. A **workload container** that is the actual application being managed
11+
12+
The launcher container periodically checks for updates to the workload container and automatically deploys new versions when they become available.
13+
14+
## How This Example Works
15+
16+
This example project demonstrates the basic structure of the launcher pattern:
17+
18+
- `Dockerfile`: Builds the launcher container with necessary dependencies
19+
- `entrypoint.sh`: The main script that runs inside the launcher container, checking for updates and deploying new versions
20+
- `get-latest.sh`: A script that determines the latest version of the workload container (in a real implementation, this would typically check a registry or other source)
21+
- `docker-compose.yml`: Example configuration for running the launcher container
22+
23+
## Using This Template
24+
25+
This project is intended as a starting point. To adapt it for your own use:
26+
27+
1. Modify `get-latest.sh` to implement your own version checking logic (e.g., checking a container registry)
28+
2. Adjust the configuration variables in `entrypoint.sh` to match your application needs
29+
3. Update the `docker-compose.yml` file with any additional configuration your launcher needs
30+
31+
## Implementation Details
32+
33+
### Update Process
34+
35+
The update process follows these steps:
36+
37+
1. The launcher container runs `get-latest.sh` to determine the latest available version
38+
2. If a new version is detected, it generates a new `docker-compose.yml` file for the workload
39+
3. It applies the new configuration using Docker Compose, which pulls and starts the new container
40+
4. The process repeats on a regular interval
41+
42+
### Customization Points
43+
44+
Key areas to customize for your own implementation:
45+
46+
- **Version Detection**: Replace the logic in `get-latest.sh` with your own mechanism for determining the latest version
47+
- **Deployment Configuration**: Modify how the `docker-compose.yml` is generated in `entrypoint.sh`
48+
- **Update Frequency**: Adjust the sleep interval in the main loop of `entrypoint.sh`
49+
- **Additional Logic**: Add pre/post update hooks, validation, or other custom logic
50+
51+
## Getting Started
52+
53+
1. Build the launcher container:
54+
55+
```bash
56+
./build-image.sh yourusername/launcher
57+
```
58+
59+
2. Push the image to Docker Hub (recommended for production use):
60+
61+
```bash
62+
docker push yourusername/launcher
63+
```
64+
65+
3. Deploy
66+
67+
You can now deploy the following compose to dstack or Phala Cloud.
68+
69+
```yaml
70+
services:
71+
launcher:
72+
image: yourusername/launcher
73+
volumes:
74+
- /var/run/docker.sock:/var/run/docker.sock
75+
restart: always
76+
```
77+
78+
**Note:** The example configuration above uses a placeholder `yourusername/launcher` as the image name. Make sure to update it with your actual published image name.
79+
80+
## License
81+
82+
MIT License
83+
84+
Copyright (c) 2025
85+
86+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
87+
88+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
89+
90+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

launcher/build-image.sh

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#!/bin/bash
2+
NAME=$1
3+
if [ -z "$NAME" ]; then
4+
echo "Usage: $0 <name>[:<tag>]"
5+
exit 1
6+
fi
7+
# Check if buildkit_20 already exists before creating it
8+
if ! docker buildx inspect buildkit_20 &>/dev/null; then
9+
docker buildx create --use --driver-opt image=moby/buildkit:v0.20.2 --name buildkit_20
10+
fi
11+
docker buildx build --builder buildkit_20 --no-cache --build-arg SOURCE_DATE_EPOCH="0" --output type=docker,name=$NAME,rewrite-timestamp=true .

launcher/docker-compose.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
services:
2+
launcher:
3+
image: kvin/launcher@sha256:873dd2117b0159575d62b79863dafd984cd02be25b46ff164e3b83ed5c9642f7
4+
volumes:
5+
- /var/run/docker.sock:/var/run/docker.sock
6+
restart: always

launcher/entrypoint.sh

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
#!/bin/bash
2+
3+
PROJECT_NAME=launched
4+
WORKDIR=/app-data
5+
EXTERNAL_PORT=10080
6+
SERVICE_NAME=server
7+
8+
9+
cd $WORKDIR
10+
11+
check-update() {
12+
echo "Checking for updates..."
13+
get-latest.sh latest.tmp
14+
if [ -f latest.tmp ]; then
15+
if [ -f latest ] && diff -q latest.tmp latest > /dev/null; then
16+
echo "No changes detected in latest version"
17+
rm -f latest.tmp
18+
return 1
19+
fi
20+
mv latest.tmp latest
21+
return 0
22+
fi
23+
echo "No update found"
24+
return 1
25+
}
26+
27+
mk-compose() {
28+
if [ ! -f latest ] || [ ! -s latest ]; then
29+
echo "Error: latest file not found or empty"
30+
return 1
31+
fi
32+
cat <<EOF > docker-compose.yml
33+
services:
34+
$SERVICE_NAME:
35+
image: $(cat latest)
36+
ports:
37+
- "$EXTERNAL_PORT:80"
38+
restart: always
39+
EOF
40+
echo "docker-compose.yml created"
41+
return 0
42+
}
43+
44+
apply-update() {
45+
echo "Making docker-compose.yml..."
46+
if ! mk-compose; then
47+
echo "Error: Failed to make docker-compose.yml"
48+
return 1
49+
fi
50+
echo "Applying update..."
51+
docker-compose -p $PROJECT_NAME up -d --remove-orphans
52+
echo "Update applied"
53+
return 0
54+
}
55+
56+
rm -f latest
57+
while true; do
58+
check-update
59+
apply-update
60+
sleep 5
61+
done

launcher/get-latest.sh

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
#!/bin/bash
2+
# Script to get the latest image for demonstration of upgrade process
3+
#
4+
# CUSTOMIZATION GUIDE:
5+
# This is where you implement your own version detection logic. Some options include:
6+
#
7+
# 1. Read from an onchain contract:
8+
# - Install web3 tools: apk add --no-cache nodejs npm && npm install -g web3
9+
# - Query contract: CONTRACT_ADDRESS="0x123..." && IMAGE=$(web3 contract call --abi=/path/to/abi.json $CONTRACT_ADDRESS getLatestImage)
10+
#
11+
# 2. Use another container to check for updates and output the latest image:
12+
# - Create a separate container that runs periodically (via cron or continuous loop)
13+
# - This container can perform complex checks (e.g., registry scanning, security validation)
14+
# - Mount a shared volume between containers: docker-compose.yml:
15+
# volumes:
16+
# - shared-data:/shared
17+
# - The update checker writes to the shared file: echo "new-image:latest" > /shared/latest-image.txt
18+
# - In this script: IMAGE=$(cat /shared/latest-image.txt)
19+
#
20+
# The script should output the full image reference (including tag or digest) to the file specified by $OUTPUT
21+
22+
OUTPUT=$1
23+
24+
# Add a small delay to simulate network/processing time
25+
sleep 2
26+
27+
MINUTE=$(date +%-M)
28+
29+
# Use time-based selection instead of random to create more predictable upgrade patterns
30+
# This will switch images roughly every minute
31+
if [ $((MINUTE % 2)) -eq 0 ]; then
32+
echo "nginx@sha256:d67fed8b03f1ed3d2a5e3cbc5ca268ad7a7528adfdd1220c420c8cf4e3802d9c" > $OUTPUT
33+
else
34+
echo "nginx@sha256:81aa342ba08035632898b78d46d0e11d79abeee63b3a6994a44ac34e102ef888" > $OUTPUT
35+
fi
36+

0 commit comments

Comments
 (0)