diff --git a/.github/workflows/container-image.yml b/.github/workflows/container-image.yml new file mode 100644 index 000000000..1b85b2028 --- /dev/null +++ b/.github/workflows/container-image.yml @@ -0,0 +1,105 @@ +name: Build container image + +on: + pull_request: + paths: + - ".github/workflows/container-image.yml" + - "Containerfile" + - "pyproject.toml" + - "setup.py" + - "python/**" + push: + branches: [main] + tags: ["v*"] + paths: + - ".github/workflows/container-image.yml" + - "Containerfile" + - "pyproject.toml" + - "setup.py" + - "python/**" + workflow_dispatch: + +concurrency: + group: container-image-${{ github.ref }} + cancel-in-progress: true + +jobs: + build: + runs-on: ubuntu-latest + timeout-minutes: 45 + permissions: + contents: read + steps: + - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 + + - uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3 + + - name: Generate image metadata + id: meta + uses: docker/metadata-action@c299e40c65443455700f0fdfc63efafe5b349051 # v5 + with: + images: ghcr.io/${{ github.repository }} + tags: | + type=ref,event=branch + type=ref,event=pr + type=semver,pattern={{version}} + type=semver,pattern={{major}}.{{minor}} + type=semver,pattern={{major}} + type=sha,format=long + + - name: Build container image + uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6 + with: + context: . + file: ./Containerfile + platforms: linux/amd64 + push: false + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max + + publish: + needs: build + if: github.event_name == 'push' && github.repository == 'FlashML-org/FreeToken' + runs-on: ubuntu-latest + timeout-minutes: 45 + permissions: + contents: read + packages: write + steps: + - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 + + - uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3 + + - name: Generate image metadata + id: meta + uses: docker/metadata-action@c299e40c65443455700f0fdfc63efafe5b349051 # v5 + with: + images: ghcr.io/${{ github.repository }} + tags: | + type=ref,event=branch + type=semver,pattern={{version}} + type=semver,pattern={{major}}.{{minor}} + type=semver,pattern={{major}} + type=raw,value=latest,enable={{is_default_branch}} + type=sha,format=long + + - name: Log in to GitHub Container Registry + uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Publish container image + uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6 + with: + context: . + file: ./Containerfile + platforms: linux/amd64 + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max diff --git a/Containerfile b/Containerfile new file mode 100644 index 000000000..0d7157774 --- /dev/null +++ b/Containerfile @@ -0,0 +1,27 @@ +ARG CUDA_IMAGE=nvidia/cuda:13.0.2-cudnn-devel-ubuntu24.04 +FROM ${CUDA_IMAGE} + +ENV DEBIAN_FRONTEND=noninteractive \ + PIP_BREAK_SYSTEM_PACKAGES=1 \ + PYTHONDONTWRITEBYTECODE=1 \ + PYTHONUNBUFFERED=1 + +RUN apt-get update \ + && apt-get install --no-install-recommends -y \ + build-essential \ + python3 \ + python3-pip \ + && rm -rf /var/lib/apt/lists/* + +WORKDIR /opt/freetoken + +COPY pyproject.toml README.md LICENSE setup.py ./ +COPY python ./python + +# The development image keeps nvcc available when FreeToken compiles JIT kernels. +RUN python3 -m pip install --no-cache-dir ".[accel]" + +EXPOSE 1919 + +ENTRYPOINT ["ft"] +CMD ["serve"] diff --git a/docs/install.md b/docs/install.md index f5205ab3b..6b9c46b3b 100644 --- a/docs/install.md +++ b/docs/install.md @@ -23,6 +23,18 @@ uv venv && source .venv/bin/activate uv pip install -e ".[accel]" ``` +## Method 3: Run the container image + +GitHub publishes images for `main` and version tags to GitHub Container Registry. +Mount the directory containing a model and pass it to `ft serve`: + +```bash +docker run --gpus all --rm -p 1919:1919 \ + -v /path/to/models:/models:ro \ + ghcr.io/flashml-org/freetoken:latest \ + serve --model /models/Qwen3.6-35B-A3B +``` + ## Verify ```bash