Deploy your own code
You can also deploy your own code on Lunni. In this tutorial, we provide instructions for GitLab CI and GitLab Container Registry, or GitHub Actions and GHCR, but you can adapt it to pretty much any CI and Docker registry.
Clone our example project to get started, or make your own (it should have Dockerfile
in repo root):
git clone https://gitlab.com/lunni/examples/python.git -b tutorial-start lunni-example-python
cd lunni-example-python
git remote set-url origin git@gitlab.com:YOURUSERNAME/lunni-example-python
git clone https://gitlab.com/lunni/examples/python.git -b tutorial-start lunni-example-python
cd lunni-example-python
git remote set-url origin git@github.com:YOURUSERNAME/lunni-example-python
Step 1. Set up CI
Our example project already has a Dockerfile
, which means we can build an image. You could use docker build
locally, but in the long run it's better to have a CI do it for you.
Create a file with the following contents:
# .gitlab-ci.yml
docker-build:
# Use the official docker image.
image: docker:latest
stage: build
services:
- docker:dind
before_script:
- docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" $CI_REGISTRY
# Default branch leaves tag empty (= latest tag)
# All other branches are tagged with the escaped branch name (commit ref slug)
script:
- |
if [[ "$CI_COMMIT_BRANCH" == "$CI_DEFAULT_BRANCH" ]]; then
tag=""
echo "Running on default branch '$CI_DEFAULT_BRANCH': tag = 'latest'"
else
tag=":$CI_COMMIT_REF_SLUG"
echo "Running on branch '$CI_COMMIT_BRANCH': tag = $tag"
fi
- docker build --pull -t "$CI_REGISTRY_IMAGE${tag}" .
- docker push "$CI_REGISTRY_IMAGE${tag}"
# Run this job in a branch where a Dockerfile exists
rules:
- if: $CI_COMMIT_BRANCH
exists:
- Dockerfile
Commit and push, then open your repository. In the left sidebar, hover CI/CD and go to Pipelines. You should see your pipeline running.
Once it finishes, in the left sidebar, hover Packages and registries and select Container Registry. If you see your image there, everything worked correctly.
# .github/workflows/build-docker.yml
name: Create and publish a Docker image
on:
push:
branches: ['release']
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
# GitHub recommends pinning actions to a commit SHA.
# To get a newer version, you will need to update the SHA.
# You can also reference a tag or branch, but the action may change without warning.
jobs:
build-and-push-image:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: Log in to the Container registry
uses: docker/login-action@f054a8b539a109f9f41c372932f1ae047eff08c9
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@98669ae865ea3cffbcbaa878cf57c20bbf1c6c38
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
- name: Build and push Docker image
uses: docker/build-push-action@ad44023a93711e3deb337508980b4b5e9bcdc5dc
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
Commit and push, then open your repository. Under your repository name, click Actions. In the left sidebar, click Create and publish a Docker image. You should see your workflow running.
Once it finishes, navigate to the main page of the repository again, then to the right of the list of files, click Packages. If you see your image there, everything worked correctly.
Step 2. Login to container registry in Lunni
In order for Lunni to see your image, you'll have to authorize it to access the registry. The easiest way is to create a personal access token. Don't worry, this only needs to be done once for a given Lunni instance.
On your GitLab instance:
- In the top-right corner, select your avatar.
- Select Edit profile.
On the left sidebar, select Access Tokens.
Enter a name and optional expiry date for the token.
Make sure name is descriptive enough, so that you can remember what this token is for later. For example,
Lunni on acme-apps.cloud
.Select the
read_registry
scope.- Select Create personal access token.
On GitHub:
- In the upper-right corner of any page, click your profile photo, then click Settings.
- In the left sidebar, click Developer settings.
- In the left sidebar, under Personal access tokens, click Tokens (classic).
Click Generate new token.
Under Token name, enter a name for the token.
Make sure name is descriptive enough, so that you can remember what this token is for later. For example,
Lunni on acme-apps.cloud
.To give your token an expiration, select the Expiration drop-down menu, then click a default or use the calendar picker.
- Select the
read:packages
scope. - Select Generate token.
Copy the resulting token. Then, on Lunni:
- In the upper-right corner of any page, click your profile photo, then click Settings.
- In the left sidebar select Registries, then click Add registry.
- Authenticate using your token as a password.
Treat your access tokens like passwords
Personal access tokens are intended to access resources on behalf of yourself. While the scope limits the access granted by the token to just reading the packages, we recommend you to paste it right into Lunni and not store it anywhere.
Step 3. Deploy your app
You can now deploy your app as usual! Let's write a Compose file for it:
version: "3"
networks:
traefik-public: { external: true }
services:
server:
image: registry.gitlab.com/YOURUSERNAME/lunni-example-python:latest
networks: [traefik-public]
deploy:
labels:
- traefik.enable=true
- traefik.docker.network=traefik-public
- traefik.constraint-label=traefik-public
- traefik.http.routers.${PROJECT_NAME?}-http.rule=Host(`${DOMAIN?}`)
- traefik.http.routers.${PROJECT_NAME?}-http.entrypoints=http
- traefik.http.routers.${PROJECT_NAME?}-http.middlewares=https-redirect
- traefik.http.routers.${PROJECT_NAME?}-https.rule=Host(`${DOMAIN?}`)
- traefik.http.routers.${PROJECT_NAME?}-https.entrypoints=https
- traefik.http.routers.${PROJECT_NAME?}-https.tls=true
- traefik.http.routers.${PROJECT_NAME?}-https.tls.certresolver=le
- traefik.http.services.${PROJECT_NAME?}.loadbalancer.server.port=80
version: "3"
networks:
traefik-public: { external: true }
services:
server:
image: ghcr.io/YOURUSERNAME/lunni-example-python:latest
networks: [traefik-public]
deploy:
labels:
- traefik.enable=true
- traefik.docker.network=traefik-public
- traefik.constraint-label=traefik-public
- traefik.http.routers.${PROJECT_NAME?}-http.rule=Host(`${DOMAIN?}`)
- traefik.http.routers.${PROJECT_NAME?}-http.entrypoints=http
- traefik.http.routers.${PROJECT_NAME?}-http.middlewares=https-redirect
- traefik.http.routers.${PROJECT_NAME?}-https.rule=Host(`${DOMAIN?}`)
- traefik.http.routers.${PROJECT_NAME?}-https.entrypoints=https
- traefik.http.routers.${PROJECT_NAME?}-https.tls=true
- traefik.http.routers.${PROJECT_NAME?}-https.tls.certresolver=le
- traefik.http.services.${PROJECT_NAME?}.loadbalancer.server.port=80
Paste it on the Create project page, specify DOMAIN
and click Deploy. Give it a minute or two to fetch the image and obtain a TLS certificate. After that, let's verify that it works:
$ http get https://lunni-example-python.demo.lunni.cloud/
HTTP/1.1 200 OK
Content-Length: 25
Content-Type: application/json
Date: Thu, 19 Jan 2023 03:05:56 GMT
Server: uvicorn
{
"message": "Hello World"
}