diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 3fcbf7f..dc4a9fa 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -69,6 +69,90 @@ jobs: cache-from: type=gha cache-to: type=gha,mode=max + # ============================================================ + # Build LinuxKit Images + # ============================================================ + linuxkit: + name: LinuxKit (${{ matrix.image }}-${{ matrix.arch }}) + runs-on: ubuntu-latest + needs: docker # Needs Docker images to be built first + + strategy: + matrix: + image: [developer, server-php] + arch: [amd64, arm64] + format: [qcow2-bios, iso-bios] + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Install Core CLI + run: | + # Download latest core binary + curl -fsSL "https://github.com/host-uk/core/releases/latest/download/core-linux-amd64.tar.gz" -o core.tar.gz + tar -xzf core.tar.gz + sudo mv core /usr/local/bin/core + chmod +x /usr/local/bin/core + core --version + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Login to GHCR + if: github.event_name != 'pull_request' + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build LinuxKit Image + run: | + mkdir -p dist + core build --type linuxkit \ + --config ./${{ matrix.image }}/linuxkit.yml \ + --format ${{ matrix.format }} \ + --arch ${{ matrix.arch }} \ + -o ./dist/${{ matrix.image == 'developer' && 'core-dev' || matrix.image }}-${{ matrix.arch }} + + - name: Upload Artifact + uses: actions/upload-artifact@v4 + with: + name: ${{ matrix.image == 'developer' && 'core-dev' || matrix.image }}-${{ matrix.arch }}-${{ matrix.format }} + path: ./dist/* + + # ============================================================ + # Release LinuxKit Images + # ============================================================ + release-linuxkit: + name: Release LinuxKit Images + runs-on: ubuntu-latest + needs: linuxkit + if: startsWith(github.ref, 'refs/tags/v') + permissions: + contents: write + + steps: + - name: Download all artifacts + uses: actions/download-artifact@v4 + with: + path: ./dist + merge-multiple: true + + - name: Generate checksums + run: | + cd dist + sha256sum * > checksums.txt + + - name: Upload to Release + uses: softprops/action-gh-release@v1 + with: + files: | + dist/* + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + # ============================================================ # Build TIM Bundles (when core build --type tim is ready) # ============================================================ diff --git a/README.md b/README.md index 1040224..7d1cca1 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,16 @@ # core-images -Container images for the host-uk ecosystem. Each image produces dual outputs: +Container images for the host-uk ecosystem. Each image produces multiple outputs: - **Docker image** → `ghcr.io/host-uk/` -- **TIM bundle** → `--.tim` +- **LinuxKit image** → `-.qcow2` / `-.iso` +- **TIM bundle** → `--.tim` (future) ## Images -| Image | Purpose | Docker | TIM | -|-------|---------|--------|-----| -| `developer` | Full-fat dev environment (100+ tools) | `ghcr.io/host-uk/core-dev` | `core-dev.tim` | -| `server-php` | Alpine + Nginx + PHP-FPM | `ghcr.io/host-uk/server-php` | `server-php.tim` | +| Image | Purpose | Docker | LinuxKit | +|-------|---------|--------|----------| +| `developer` | Full-fat dev environment (100+ tools) | `ghcr.io/host-uk/core-dev` | `core-dev-amd64.qcow2` | +| `server-php` | Alpine + Nginx + PHP-FPM | `ghcr.io/host-uk/server-php` | `server-php-amd64.qcow2` | ## Usage @@ -23,20 +24,50 @@ docker run -it ghcr.io/host-uk/core-dev docker run -p 80:80 ghcr.io/host-uk/server-php ``` -### TIM (Docker-free via Core) +### LinuxKit (via Core CLI) + +```bash +# Build LinuxKit image +core build --type linuxkit --config developer/linuxkit.yml + +# Run LinuxKit image +core run core-dev-amd64.qcow2 + +# Run with custom resources +core run core-dev-amd64.qcow2 --memory 4096 --cpus 4 +``` + +### TIM (future, Docker-free) ```bash # Install dev environment core dev install -# Or run directly +# Run directly core run core-dev.tim # Run PHP server core run server-php.tim -p 80:80 ``` -## Building +## Building Locally + +### Using Core CLI (recommended) + +```bash +# Install core CLI +go install github.com/host-uk/core/cmd/core@latest + +# Build LinuxKit images +core build --type linuxkit --config developer/linuxkit.yml --format qcow2-bios +core build --type linuxkit --config server-php/linuxkit.yml --format qcow2-bios + +# Build Docker images +core build --type docker --config developer/Dockerfile +core build --type docker --config server-php/Dockerfile +``` + +### Using Task ```bash # Requires: task (taskfile.dev) @@ -47,30 +78,36 @@ task build # Build specific image task build:developer task build:server-php - -# Build TIM only -task build:developer:tim - -# Build Docker only -task build:developer:docker ``` ## Structure ``` core-images/ -├── developer/ # core-dev.tim - Full dev environment +├── developer/ # core-dev - Full dev environment +│ ├── Dockerfile # Docker build +│ ├── linuxkit.yml # LinuxKit build +│ ├── Borgfile # TIM build (future) +│ └── config/ +├── server-php/ # server-php - Nginx + PHP-FPM │ ├── Dockerfile +│ ├── linuxkit.yml │ ├── Borgfile │ └── config/ -├── server-php/ # server-php.tim - Nginx + PHP-FPM -│ ├── Dockerfile -│ ├── Borgfile -│ └── config/ -└── base/ # Shared base configurations - └── alpine/ +└── linuxkit/ # LinuxKit documentation + └── README.md ``` +## CI/CD + +The GitHub Actions workflow builds: + +1. **Docker images** - Multi-arch (amd64, arm64) pushed to GHCR +2. **LinuxKit images** - qcow2 and ISO formats for both architectures +3. **Release artifacts** - LinuxKit images uploaded to GitHub Releases on tags + +All builds use the `core` CLI for consistency. + ## Sources Consolidated from: diff --git a/developer/linuxkit.yml b/developer/linuxkit.yml new file mode 100644 index 0000000..86737b5 --- /dev/null +++ b/developer/linuxkit.yml @@ -0,0 +1,230 @@ +# ============================================================ +# LinuxKit Configuration - Developer Environment +# +# A complete developer environment mirroring the Docker +# developer image, built as a bootable VM. +# +# Build: linuxkit build -format qcow2-bios developer/linuxkit.yml +# Run: linuxkit run qemu developer +# ============================================================ + +kernel: + image: linuxkit/kernel:6.6.13 + cmdline: "console=ttyS0 console=tty0" + +init: + - linuxkit/init:v1.2.0 + - linuxkit/runc:v1.1.12 + - linuxkit/containerd:v1.7.13 + - linuxkit/ca-certificates:v1.0.0 + +onboot: + # System initialization + - name: sysctl + image: linuxkit/sysctl:v1.0.0 + + - name: sysfs + image: linuxkit/sysfs:v1.0.0 + + - name: modprobe + image: linuxkit/modprobe:v1.0.0 + command: ["modprobe", "-a", "overlay", "nf_conntrack", "br_netfilter"] + + # Format and mount persistent workspace volume + - name: format + image: linuxkit/format:v1.0.0 + + - name: mount + image: linuxkit/mount:v1.0.0 + command: ["/usr/bin/mountie", "/var/lib/docker", "/workspace"] + +onshutdown: + - name: shutdown + image: linuxkit/shutdown:v1.0.0 + +services: + # ============================================================ + # Core Services + # ============================================================ + - name: getty + image: linuxkit/getty:v1.0.0 + env: + - INSECURE=true + binds: + - /etc/profile.d:/etc/profile.d + capabilities: + - CAP_SYS_ADMIN + - CAP_SYS_TTY_CONFIG + + - name: rngd + image: linuxkit/rngd:v1.0.0 + + - name: dhcpcd + image: linuxkit/dhcpcd:v1.0.0 + + - name: ntpd + image: linuxkit/openntpd:v1.0.0 + + # ============================================================ + # SSH Access + # ============================================================ + - name: sshd + image: linuxkit/sshd:v1.0.0 + binds: + - /etc/ssh/authorized_keys:/root/.ssh/authorized_keys + - /workspace:/workspace + capabilities: + - CAP_NET_BIND_SERVICE + - CAP_SYS_CHROOT + - CAP_SETUID + - CAP_SETGID + + # ============================================================ + # Docker-in-LinuxKit (DinL) + # ============================================================ + - name: dockerd + image: docker:26.1-dind + capabilities: + - all + net: host + pid: host + mounts: + - type: cgroup + options: ["rw", "nosuid", "noexec", "nodev", "relatime"] + binds: + - /etc/resolv.conf:/etc/resolv.conf + - /var/lib/docker:/var/lib/docker + - /var/run:/var/run + - /workspace:/workspace + runtime: + mkdir: + - /var/lib/docker + + # ============================================================ + # Developer Environment Container + # ============================================================ + - name: developer + image: ghcr.io/host-uk/core-dev:latest + capabilities: + - CAP_NET_ADMIN + - CAP_SYS_ADMIN + - CAP_SETUID + - CAP_SETGID + net: host + binds: + - /workspace:/workspace + - /var/run/docker.sock:/var/run/docker.sock + - /etc/profile.d:/etc/profile.d:ro + env: + - TERM=xterm-256color + - LANG=C.UTF-8 + - LC_ALL=C.UTF-8 + - SHELL=/bin/zsh + - GOPATH=/root/go + - PATH=/root/go/bin:/root/.local/bin:/root/.composer/vendor/bin:/usr/local/bin:/usr/bin:/bin + runtime: + mkdir: + - /workspace + +# ============================================================ +# Static Files +# ============================================================ +files: + # SSH authorized keys (placeholder - mount your own) + - path: /etc/ssh/authorized_keys + contents: | + # Add your SSH public keys here + # ssh-ed25519 AAAA... user@host + mode: "0600" + + # Profile for shell environment + - path: /etc/profile.d/00-developer.sh + contents: | + #!/bin/sh + export TERM=xterm-256color + export LANG=C.UTF-8 + export LC_ALL=C.UTF-8 + export SHELL=/bin/zsh + export EDITOR=vim + export GOPATH=/root/go + export PATH="/root/go/bin:/root/.local/bin:/root/.composer/vendor/bin:/usr/local/bin:$PATH" + cd /workspace 2>/dev/null || true + mode: "0644" + + # Shell aliases from developer config + - path: /etc/profile.d/aliases.sh + contents: | + # Core-dev shell aliases + + # Navigation + alias ..='cd ..' + alias ...='cd ../..' + alias ll='eza -la --icons --git' + alias la='eza -la --icons' + alias lt='eza --tree --level=2 --icons' + + # Git + alias g='git' + alias gs='git status' + alias gd='git diff' + alias gc='git commit' + alias gp='git push' + alias gl='git log --oneline -20' + alias gco='git checkout' + alias gb='git branch' + alias lg='lazygit' + + # Docker + alias d='docker' + alias dc='docker compose' + alias dps='docker ps' + alias di='docker images' + alias dex='docker exec -it' + + # Kubernetes + alias k='kubectl' + alias kgp='kubectl get pods' + alias kgs='kubectl get svc' + alias kgd='kubectl get deployments' + + # PHP/Laravel + alias art='php artisan' + alias sail='./vendor/bin/sail' + alias pest='./vendor/bin/pest' + alias pint='./vendor/bin/pint' + + # Core + alias c='core' + alias cdev='core dev' + alias cbuild='core build' + alias crun='core run' + + # Misc + alias cat='bat' + alias find='fd' + alias grep='rg' + mode: "0644" + + # Motd + - path: /etc/motd + contents: | + + ╔══════════════════════════════════════════════════════════════╗ + ║ Host UK Core Developer Environment ║ + ║ ║ + ║ Tools: Node, Go, PHP, Python, Docker ║ + ║ Workspace: /workspace (persistent) ║ + ║ ║ + ║ Run 'claude' to start AI-assisted development ║ + ╚══════════════════════════════════════════════════════════════╝ + + mode: "0644" + +# ============================================================ +# Trust Configuration +# ============================================================ +trust: + org: + - linuxkit + - library + - docker diff --git a/linuxkit/README.md b/linuxkit/README.md new file mode 100644 index 0000000..3306e93 --- /dev/null +++ b/linuxkit/README.md @@ -0,0 +1,102 @@ +# LinuxKit Images + +This directory contains documentation for LinuxKit image builds. + +## Building with Core CLI + +The recommended way to build LinuxKit images is using the `core` CLI: + +```bash +# Install core CLI +go install github.com/host-uk/core/cmd/core@latest + +# Build developer image (QCOW2 for QEMU/KVM) +core build --type linuxkit --config developer/linuxkit.yml --format qcow2-bios + +# Build server-php image (QCOW2 for QEMU/KVM) +core build --type linuxkit --config server-php/linuxkit.yml --format qcow2-bios + +# Build ISO for bare metal / other hypervisors +core build --type linuxkit --config developer/linuxkit.yml --format iso-bios +core build --type linuxkit --config server-php/linuxkit.yml --format iso-bios + +# Build for specific architecture +core build --type linuxkit --config developer/linuxkit.yml --arch arm64 +``` + +## Running Images + +```bash +# Run with core CLI +core run core-dev-amd64.qcow2 + +# Run with custom resources +core run core-dev-amd64.qcow2 --memory 4096 --cpus 4 + +# Run in detached mode +core run -d core-dev-amd64.qcow2 +``` + +## Output Formats + +| Format | Use Case | +|--------|----------| +| `qcow2-bios` | QEMU/KVM (default) | +| `iso-bios` | Bare metal, VMware, VirtualBox | +| `raw-bios` | Direct disk write | +| `vhd` | Hyper-V | +| `vmdk` | VMware | +| `aws` | AWS EC2 AMI | +| `gcp` | Google Cloud | +| `azure` | Microsoft Azure | + +## Image Architecture + +### Developer Image + +The developer LinuxKit image provides: + +- Full developer toolchain (Node, Go, PHP, Python) +- Docker-in-LinuxKit for container workflows +- SSH access for remote development +- Persistent `/workspace` volume +- Pre-configured shell with aliases and starship prompt + +### Server PHP Image + +The server PHP LinuxKit image provides: + +- Minimal Alpine base +- Nginx + PHP-FPM stack +- SSH access for management +- Health check endpoint at `/health` +- Persistent `/var/www/html` volume + +## Customization + +### Adding SSH Keys + +Edit the `authorized_keys` file in the linuxkit.yml: + +```yaml +files: + - path: /etc/ssh/authorized_keys + contents: | + ssh-ed25519 AAAA... your-key@host + mode: "0600" +``` + +### Environment Variables + +Add environment variables to the service definition: + +```yaml +services: + - name: developer + env: + - MY_VAR=value +``` + +### Custom Packages + +For additional packages, create a custom container image that inherits from the base and add it to the LinuxKit config. diff --git a/server-php/linuxkit.yml b/server-php/linuxkit.yml new file mode 100644 index 0000000..f567227 --- /dev/null +++ b/server-php/linuxkit.yml @@ -0,0 +1,272 @@ +# ============================================================ +# LinuxKit Configuration - PHP Server +# +# A minimal production PHP server with Nginx + PHP-FPM, +# built as a bootable VM. +# +# Build: linuxkit build -format qcow2-bios server-php/linuxkit.yml +# Run: linuxkit run qemu server-php +# ============================================================ + +kernel: + image: linuxkit/kernel:6.6.13 + cmdline: "console=ttyS0 console=tty0" + +init: + - linuxkit/init:v1.2.0 + - linuxkit/runc:v1.1.12 + - linuxkit/containerd:v1.7.13 + - linuxkit/ca-certificates:v1.0.0 + +onboot: + # System initialization + - name: sysctl + image: linuxkit/sysctl:v1.0.0 + + - name: sysfs + image: linuxkit/sysfs:v1.0.0 + + # Format and mount persistent data volume + - name: format + image: linuxkit/format:v1.0.0 + + - name: mount + image: linuxkit/mount:v1.0.0 + command: ["/usr/bin/mountie", "/var/www/html"] + +onshutdown: + - name: shutdown + image: linuxkit/shutdown:v1.0.0 + +services: + # ============================================================ + # Core Services + # ============================================================ + - name: rngd + image: linuxkit/rngd:v1.0.0 + + - name: dhcpcd + image: linuxkit/dhcpcd:v1.0.0 + + - name: ntpd + image: linuxkit/openntpd:v1.0.0 + + # ============================================================ + # SSH Access (for management) + # ============================================================ + - name: sshd + image: linuxkit/sshd:v1.0.0 + binds: + - /etc/ssh/authorized_keys:/root/.ssh/authorized_keys + capabilities: + - CAP_NET_BIND_SERVICE + - CAP_SYS_CHROOT + - CAP_SETUID + - CAP_SETGID + + # ============================================================ + # PHP Server Container + # ============================================================ + - name: server-php + image: ghcr.io/host-uk/server-php:latest + capabilities: + - CAP_NET_BIND_SERVICE + - CAP_CHOWN + - CAP_SETUID + - CAP_SETGID + net: host + binds: + - /var/www/html:/var/www/html + - /etc/php-server:/etc/php-server:ro + env: + - APP_ENV=production + - PHP_VERSION=84 + runtime: + mkdir: + - /var/www/html + + # ============================================================ + # Health Check Service + # ============================================================ + - name: healthcheck + image: linuxkit/healthcheck:v1.0.0 + binds: + - /run:/run + capabilities: + - CAP_NET_RAW + command: + - /healthcheck + - --endpoint=http://127.0.0.1/health + - --interval=30s + - --timeout=10s + +# ============================================================ +# Static Files +# ============================================================ +files: + # SSH authorized keys (placeholder - mount your own) + - path: /etc/ssh/authorized_keys + contents: | + # Add your SSH public keys here + # ssh-ed25519 AAAA... user@host + mode: "0600" + + # PHP-FPM configuration + - path: /etc/php-server/php-fpm.conf + contents: | + [global] + pid = /run/php-fpm.pid + error_log = /proc/self/fd/2 + daemonize = no + + [www] + user = nobody + group = nobody + listen = /run/php-fpm.sock + listen.owner = nobody + listen.group = nobody + listen.mode = 0660 + + pm = dynamic + pm.max_children = 50 + pm.start_servers = 5 + pm.min_spare_servers = 5 + pm.max_spare_servers = 35 + pm.max_requests = 500 + + clear_env = no + catch_workers_output = yes + decorate_workers_output = no + + php_admin_value[error_log] = /proc/self/fd/2 + php_admin_flag[log_errors] = on + mode: "0644" + + # Nginx configuration + - path: /etc/php-server/nginx.conf + contents: | + worker_processes auto; + error_log /dev/stderr warn; + pid /run/nginx.pid; + + events { + worker_connections 1024; + multi_accept on; + use epoll; + } + + http { + include /etc/nginx/mime.types; + default_type application/octet-stream; + + log_format main '$remote_addr - $remote_user [$time_local] "$request" ' + '$status $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "$http_x_forwarded_for"'; + + access_log /dev/stdout main; + + sendfile on; + tcp_nopush on; + tcp_nodelay on; + keepalive_timeout 65; + types_hash_max_size 2048; + + # Gzip compression + gzip on; + gzip_vary on; + gzip_proxied any; + gzip_comp_level 6; + gzip_types text/plain text/css text/xml application/json application/javascript + application/rss+xml application/atom+xml image/svg+xml; + + server { + listen 80; + listen [::]:80; + server_name _; + + root /var/www/html/public; + index index.php index.html; + + # Health check endpoint + location /health { + access_log off; + return 200 "OK\n"; + add_header Content-Type text/plain; + } + + location / { + try_files $uri $uri/ /index.php?$query_string; + } + + location ~ \.php$ { + fastcgi_pass unix:/run/php-fpm.sock; + fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; + include fastcgi_params; + fastcgi_hide_header X-Powered-By; + } + + # Security headers + add_header X-Frame-Options "SAMEORIGIN" always; + add_header X-Content-Type-Options "nosniff" always; + add_header X-XSS-Protection "1; mode=block" always; + + # Deny hidden files + location ~ /\. { + deny all; + } + } + } + mode: "0644" + + # Supervisor configuration (used inside the container) + - path: /etc/php-server/supervisord.conf + contents: | + [supervisord] + nodaemon=true + user=root + logfile=/dev/null + logfile_maxbytes=0 + pidfile=/run/supervisord.pid + + [program:php-fpm] + command=/usr/sbin/php-fpm84 -F -y /etc/php-server/php-fpm.conf + stdout_logfile=/dev/stdout + stdout_logfile_maxbytes=0 + stderr_logfile=/dev/stderr + stderr_logfile_maxbytes=0 + autorestart=true + startretries=5 + + [program:nginx] + command=/usr/sbin/nginx -g 'daemon off;' -c /etc/php-server/nginx.conf + stdout_logfile=/dev/stdout + stdout_logfile_maxbytes=0 + stderr_logfile=/dev/stderr + stderr_logfile_maxbytes=0 + autorestart=true + startretries=5 + depends_on=php-fpm + mode: "0644" + + # Motd + - path: /etc/motd + contents: | + + ╔══════════════════════════════════════════════════════════════╗ + ║ Host UK Core PHP Server ║ + ║ ║ + ║ Stack: Alpine + Nginx + PHP-FPM ║ + ║ Webroot: /var/www/html ║ + ║ ║ + ║ Health: http://localhost/health ║ + ╚══════════════════════════════════════════════════════════════╝ + + mode: "0644" + +# ============================================================ +# Trust Configuration +# ============================================================ +trust: + org: + - linuxkit + - library