Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 12 additions & 12 deletions .github/workflows/build-docker.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,46 +16,46 @@ concurrency:

env:
BUILDKIT_PROGRESS: "plain" # Full logs for CI build.
REGISTRY_SRC: ${{ vars.REGISTRY_SRC || 'docker.io' }} # For BASE_NAMESPACE of images: where to pull base images from, docker.io or other source registry URL.
REGISTRY_DST: ${{ vars.REGISTRY_DST || 'docker.io' }} # For tags of built images: where to push images to, docker.io or other destination registry URL.
REGISTRY_SRC: ${{ vars.REGISTRY_SRC || 'quay.io' }} # For BASE_NAMESPACE of images: where to pull base images from, docker.io or other source registry URL.
REGISTRY_DST: ${{ vars.REGISTRY_DST || 'quay.io' }} # For tags of built images: where to push images to, docker.io or other destination registry URL.
# DOCKER_REGISTRY_USERNAME and DOCKER_REGISTRY_PASSWORD is required for docker image push, they should be set in CI secrets.
DOCKER_REGISTRY_USERNAME: ${{ vars.DOCKER_REGISTRY_USERNAME }}
DOCKER_REGISTRY_PASSWORD: ${{ secrets.DOCKER_REGISTRY_PASSWORD }}
# used to sync image to mirror registry
DOCKER_MIRROR_REGISTRY_USERNAME: ${{ vars.DOCKER_MIRROR_REGISTRY_USERNAME }}
DOCKER_MIRROR_REGISTRY_PASSWORD: ${{ secrets.DOCKER_MIRROR_REGISTRY_PASSWORD }}
CI_PROJECT_NAME: ${{ vars.CI_PROJECT_NAME || 'LabNow/lab-container' }}

jobs:
job-k3s-ctk:
name: 'k3s-ctk'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
- run: |
source ./tool.sh
build_image k3s-ctk latest docker_k8s/k3s-ctk.Dockerfile && push_image
source ./tool.sh && build_image k3s-ctk latest docker_kube/k3s-ctk.Dockerfile && push_image

job-k0s-ctk:
name: 'k0s-ctk'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
- run: |
source ./tool.sh
build_image k0s-ctk latest docker_k8s/k0s-ctk.Dockerfile && push_image
source ./tool.sh && build_image k0s-ctk latest docker_kube/k0s-ctk.Dockerfile && push_image


## Sync all images in this build (listed by "names") to mirror registry.
sync_images:
needs: ["job-k3s-ctk", "job-k0s-ctk"]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
- env:
AUTH_FILE_CONTENT: ${{ secrets.AUTH_FILE_CONTENT }}
DOCKER_MIRROR_REGISTRY: ${{ vars.DOCKER_MIRROR_REGISTRY }}
run: |
source ./tool.sh && ls -alh ./
source ./tool.sh
printf '%s' "$AUTH_FILE_CONTENT" > .github/workflows/auth.json && ls -alh ./.github/workflows
printenv | grep -v 'PATH' > /tmp/docker.env && echo "REGISTRY_URL=${REGISTRY_DST}" >> /tmp/docker.env
docker run --rm --env-file /tmp/docker.env -v $(pwd):/tmp -w /tmp qpod/docker-kit \
image-syncer --proc=8 --retries=2 --images /tmp/task_sync_imgs/images.yaml --auth /tmp/.github/workflows/auth.json
docker run --rm --env-file /tmp/docker.env -v $(pwd):/tmp -w /tmp ${IMG_PREFIX_DST:-labnow}/docker-kit \
python /opt/utils/image-syncer/run_jobs.py --auth-file=/tmp/.github/workflows/auth.json
17 changes: 0 additions & 17 deletions docker_k8s/README.md

This file was deleted.

108 changes: 108 additions & 0 deletions docker_kube/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
# LabNow Container Kit (Kubernetes Air-gap Installation)

This project provides one-click installation assets for deploying Kubernetes (`k3s` / `k0s`) in air-gapped (offline) environments.

All binaries, offline image bundles, and setup scripts are packaged into lightweight Docker images. Users can pull these images on a connected machine, transfer them to the offline host, and extract the assets using a volume mount to complete the installation without internet access.

## Workflow

1. **Build (Online)**: The Dockerfile downloads Kubernetes binaries, `kubectl`, `helm`, and the air-gap image bundle, packaging them into a runner image.
2. **Deploy (Offline)**:
- Export/pull the built image on the target host.
- Run the container with a volume mount to copy installation assets to the host filesystem.
- Run the provided scripts to install the cluster.

---

## Image Components

The tools and files included in each container image:

| Component Role | `k3s-ctk` Image | `k0s-ctk` Image | Description |
| :--- | :--- | :--- | :--- |
| **Kubernetes Engine** | `k3s` | `k0s` | The core Kubernetes distribution binary. |
| **Air-gap Image Bundle** | `k3s-airgap-images-*.tar.zst` | `k0s-airgap-bundle-*` | System container images bundle for offline deployment. |
| **Kubernetes CLI** | `kubectl` | `kubectl` | Command-line tool for control plane operations. |
| **Package Manager** | `helm` | `helm` | Tool for managing Kubernetes pre-packaged charts. |
| **CRI Adapter** | `cri-dockerd` | *N/A* | Adapter to use Docker as the container runtime (embedded in K0s). |
| **Official Installer** | `script-get-k3s-io.sh` | *N/A* | Official installation script (embedded in K0s). |
| **Setup Helper Script** | `script-setup-k3s.sh` | `script-setup-k0s.sh` | Scripts containing functions for configuration and setup. |

---

## Air-Gap Installation

### Option A: Install K3s (with Docker Runtime)

#### 1. Extract installation assets
Run the image to copy all assets to `/opt/k3s` on the host:
```bash
docker run --rm -it -v /opt/k3s:/tmp quay.io/labnow/k3s-ctk:latest
```

#### 2. Load K3s air-gap images
Import the K3s system images into your Docker daemon:
```bash
cd /opt/k3s
zstd -cd ./k3s-airgap-images-${ARCH:-amd64}.tar.zst | docker load
```

#### 3. Configure and start cri-dockerd
```bash
# Generate systemd socket and service unit files
source ./script-setup-k3s.sh && create_cri_dockerd_unit_files

# Enable and start the service
sudo systemctl daemon-reload
sudo systemctl enable --now cri-docker.socket cri-docker.service
```

#### 4. Run the offline installer
```bash
INSTALL_K3S_SKIP_DOWNLOAD=true \
INSTALL_K3S_BIN_DIR=/opt/k3s \
./script-get-k3s-io.sh --docker --data-dir /data/storage/data-k3s
```

#### 5. Link binaries to PATH (Optional)
```bash
sudo ln -sf /opt/k3s/k3s* /opt/k3s/kubectl /opt/k3s/cri-dockerd /usr/local/bin/
```

---

### Option B: Install K0s

#### 1. Extract installation assets
Run the image to copy all assets to `/opt/k0s` on the host:
```bash
docker run --rm -it -v /opt/k0s:/tmp quay.io/labnow/k0s-ctk:latest
```

#### 2. Copy binaries and setup the air-gap bundle
K0s loads offline images from `/var/lib/k0s/images/` by default.
```bash
cd /opt/k0s

# Copy binaries to PATH
sudo cp ./k0s ./kubectl ./helm /usr/local/bin/

# Copy the air-gap bundle to the designated directory
sudo mkdir -p /var/lib/k0s/images/
sudo cp ./k0s-airgap-bundle-* /var/lib/k0s/images/
```

#### 3. Install and start K0s service
```bash
# Install as a single-node controller + worker
sudo k0s install controller --single --enable-worker

# Start the service
sudo k0s start
```

#### 4. Verify installation
```bash
sudo k0s status
sudo k0s kubectl get nodes
```
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,12 @@ LABEL maintainer="haobibo@gmail.com"
COPY work /opt/utils/

RUN set -eux && chmod +x /opt/utils/*.sh \
&& source /opt/utils/script-setup-k8s-common.sh && setup_verify_arch && setup_kubectl && setup_helm \
&& source /opt/utils/script-setup-k0s.sh && setup_k0s && setup_k0s_pack \
&& source /opt/utils/script-setup-k8s-common.sh && setup_verify_arch && setup_kubectl && setup_helm && setup_k9s \
&& source /opt/utils/script-setup-k0s.sh && setup_k0s && setup_k0s_pack \
&& mv /opt/k8s/* /opt/k0s/ \
&& mv /opt/utils/script-setup-k0s.sh /opt/k0s/ \
&& mv /opt/k8s/* /opt/k0s/ && rm -rf /opt/k8s \
&& ls -alh /opt/*

&& rm -rf /opt/k8s && ls -alh /opt/*


FROM docker.io/busybox
Expand Down
11 changes: 5 additions & 6 deletions docker_k8s/k3s-ctk.Dockerfile → docker_kube/k3s-ctk.Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,11 @@ LABEL maintainer="haobibo@gmail.com"
COPY work /opt/utils/

RUN set -eux && chmod +x /opt/utils/*.sh \
&& source /opt/utils/script-setup-k8s-common.sh && setup_verify_arch && setup_kubectl && setup_helm \
&& source /opt/utils/script-setup-k3s.sh && setup_k3s && setup_cri_dockerd && setup_k3s_pack \
&& mv /opt/utils/script-setup-k3s.sh /opt/k3s/ \
&& mv /opt/utils/script-get-k3s-io.sh /opt/k3s/ \
&& mv /opt/k8s/* /opt/k3s/ && rm -rf /opt/k8s \
&& ls -alh /opt/*
&& source /opt/utils/script-setup-k8s-common.sh && setup_verify_arch && setup_kubectl && setup_helm && setup_k9s \
&& source /opt/utils/script-setup-k3s.sh && setup_k3s && setup_cri_dockerd && setup_k3s_pack \
&& mv /opt/k8s/* /opt/k3s/ \
&& mv /opt/utils/script-setup-k3s.sh /opt/utils/script-get-k3s-io.sh /opt/k3s/ \
&& rm -rf /opt/k8s && ls -alh /opt/*


FROM docker.io/busybox
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,18 @@ setup_helm() {
&& chmod +x /opt/k8s/helm
/opt/k8s/helm version
}

setup_k9s() {
# ref: https://github.com/derailed/k9s , the binary is roughly 120MB
local K9S_ARCH=$ARCH
[ "$K9S_ARCH" = "arm" ] && K9S_ARCH="armv7"

VER_K9S=$(curl -sL https://github.com/derailed/k9s/releases.atom | grep 'releases/tag/v' | grep -v 'rc' | head -1 | sed -E 's/.*\/tag\/([^"]+).*/\1/') \
&& URL_K9S="https://github.com/derailed/k9s/releases/download/${VER_K9S}/k9s_Linux_${K9S_ARCH}.tar.gz" \
&& echo "Downloading K9s version ${VER_K9S} from: ${URL_K9S}" \
&& curl -L -s -o /tmp/k9s.tar.gz "${URL_K9S}" \
&& mkdir -pv /opt/k8s \
&& tar -zxvf /tmp/k9s.tar.gz -C /opt/k8s/ k9s \
&& chmod +x /opt/k8s/k9s
/opt/k8s/k9s version
}