From 050154f4069a767554a907554c0228bf7c070347 Mon Sep 17 00:00:00 2001 From: Steven Hangger Date: Sun, 18 Feb 2024 19:43:19 +0800 Subject: [PATCH] feat(docker): add Docker support --- .github/workflows/docker.yml | 171 +++++++++++++++++++++++++++++++++++ Dockerfile | 40 ++++++++ docker-compose.yml | 18 ++++ scripts/merge_manifest.py | 14 +++ 4 files changed, 243 insertions(+) create mode 100644 .github/workflows/docker.yml create mode 100644 Dockerfile create mode 100644 docker-compose.yml create mode 100644 scripts/merge_manifest.py diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml new file mode 100644 index 00000000..cea19ab9 --- /dev/null +++ b/.github/workflows/docker.yml @@ -0,0 +1,171 @@ +name: Publish Docker Image +on: [push] + +concurrency: + group: ${{ github.ref }}-${{ github.workflow }} + cancel-in-progress: true + +jobs: + docker_build: + name: Build ${{ matrix.arch }} Image + runs-on: ubuntu-latest + + strategy: + matrix: + include: + - arch: amd64 + name: amd64 + # - arch: arm64 + # name: arm64 + + steps: + - name: Free up disk spaces + run: | + sudo rm -rf /usr/share/dotnet || true + sudo rm -rf /opt/ghc || true + sudo rm -rf "/usr/local/share/boost" || true + sudo rm -rf "$AGENT_TOOLSDIRECTORY" || true + + - name: Get lowercase string for the repository name + id: lowercase-repo-name + uses: ASzc/change-string-case-action@v2 + with: + string: ${{ github.event.repository.name }} + + - name: Checkout base + uses: actions/checkout@v2 + with: + fetch-depth: 0 + + - name: Cache Docker layers + uses: actions/cache@v2 + with: + path: /tmp/.buildx-cache + key: ${{ github.ref }}-${{ matrix.arch }} + restore-keys: | + ${{ github.ref }}-${{ matrix.arch }} + + - name: Set up QEMU + uses: docker/setup-qemu-action@v2 + with: + platforms: linux/${{ matrix.arch }} + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2 + + - name: Docker login + uses: docker/login-action@v2 + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_PASSWORD }} + + - name: Get commit SHA + id: vars + run: echo "::set-output name=sha_short::$(git rev-parse --short HEAD)" + + - name: Build and export + id: build + if: github.ref == 'refs/heads/master' + uses: docker/build-push-action@v3 + with: + push: true + platforms: linux/${{ matrix.arch }} + tags: ${{ secrets.DOCKER_USERNAME }}/${{ steps.lowercase-repo-name.outputs.lowercase }}:${{ matrix.name }}-latest + build-args: | + SHA=${{ steps.vars.outputs.sha_short }} + outputs: type=image,push=true + cache-from: type=local,src=/tmp/.buildx-cache + cache-to: type=local,dest=/tmp/.buildx-cache + + - name: Replace tag without `v` + if: startsWith(github.ref, 'refs/tags/') + uses: actions/github-script@v1 + id: version + with: + script: | + return context.payload.ref.replace(/\/?refs\/tags\/v/, '') + result-encoding: string + + - name: Build release and export + id: build_rel + if: startsWith(github.ref, 'refs/tags/') + uses: docker/build-push-action@v3 + with: + push: true + platforms: linux/${{ matrix.arch }} + tags: ${{ secrets.DOCKER_USERNAME }}/${{ steps.lowercase-repo-name.outputs.lowercase }}:${{ matrix.name }}-${{steps.version.outputs.result}} + build-args: | + SHA=${{ steps.version.outputs.result }} + outputs: type=image,push=true + cache-from: type=local,src=/tmp/.buildx-cache + cache-to: type=local,dest=/tmp/.buildx-cache + + - name: Save digest + if: github.ref == 'refs/heads/master' + run: echo ${{ steps.build.outputs.digest }} > /tmp/digest.txt + + - name: Save release digest + if: startsWith(github.ref, 'refs/tags/') + run: echo ${{ steps.build_rel.outputs.digest }} > /tmp/digest.txt + + - name: Upload artifact + uses: actions/upload-artifact@v3 + with: + name: digest_${{ matrix.name }} + path: /tmp/digest.txt + + manifests: + name: Build manifests + needs: [docker_build] + runs-on: ubuntu-latest + steps: + - name: Get lowercase string for the repository name + id: lowercase-repo-name + uses: ASzc/change-string-case-action@v2 + with: + string: ${{ github.event.repository.name }} + + - name: Checkout base + uses: actions/checkout@v2 + with: + fetch-depth: 0 + + # https://github.com/docker/setup-qemu-action + - name: Set up QEMU + uses: docker/setup-qemu-action@v2 + + # https://github.com/docker/setup-buildx-action + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2 + with: + config-inline: | + [worker.oci] + max-parallelism = 1 + + - name: Download artifact + uses: actions/download-artifact@v3 + with: + path: /tmp/images/ + + - name: Docker login + uses: docker/login-action@v2 + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_PASSWORD }} + + - name: Replace tag without `v` + if: startsWith(github.ref, 'refs/tags/') + uses: actions/github-script@v1 + id: version + with: + script: | + return context.payload.ref.replace(/\/?refs\/tags\/v/, '') + result-encoding: string + + - name: Merge and push manifest on master branch + if: github.ref == 'refs/heads/master' + run: python scripts/merge_manifest.py "${{ secrets.DOCKER_USERNAME }}/${{ steps.lowercase-repo-name.outputs.lowercase }}" + + - name: Merge and push manifest on release + if: startsWith(github.ref, 'refs/tags/') + run: python scripts/merge_manifest.py "${{ secrets.DOCKER_USERNAME }}/${{ steps.lowercase-repo-name.outputs.lowercase }}" ${{steps.version.outputs.result}} diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..993b0a65 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,40 @@ +FROM node:21-slim AS frontend + +RUN echo "registry=https://registry.npmmirror.com/" > ~/.npmrc + +WORKDIR /app + +COPY manifest.json manifest.json +COPY frontend frontend + +WORKDIR /app/frontend + +RUN npm ci +RUN npm run build + +FROM nvidia/cuda:11.6.1-devel-ubuntu20.04 AS runtime + +ENV DEBIAN_FRONTEND=noninteractive + +RUN apt update && \ + apt install -yq git curl wget build-essential ninja-build aria2 jq software-properties-common + +RUN add-apt-repository -y ppa:deadsnakes/ppa && \ + add-apt-repository -y ppa:ubuntu-toolchain-r/test && \ + apt install -y g++-11 python3.10 python3.10-distutils python3.10-dev && \ + curl -sS http://mirrors.aliyun.com/pypi/get-pip.py | python3.10 + +FROM runtime AS final + +WORKDIR /app + +COPY ./backend-python/requirements.txt ./backend-python/requirements.txt + +RUN python3.10 -m pip install --quiet -r ./backend-python/requirements.txt + +COPY . . +COPY --from=frontend /app/frontend/dist /app/frontend/dist + +EXPOSE 27777 + +CMD ["python3.10", "./backend-python/main.py", "--port", "27777", "--host", "0.0.0.0", "--webui"] diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 00000000..dccd76f7 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,18 @@ +services: + rmkv_runner: + image: rwkv-runner:latest + build: . + # Append "--rwkv.cpp" parameter to use rwkv.cpp + # command: python3.10 ./backend-python/main.py --port 27777 --host 0.0.0.0 --webui --rwkv.cpp + volumes: + - /mnt:/mnt + ports: + - "27777:27777" + # Comment the following lines if use rwkv.cpp + deploy: + resources: + reservations: + devices: + - driver: nvidia + count: 1 + capabilities: [gpu] diff --git a/scripts/merge_manifest.py b/scripts/merge_manifest.py new file mode 100644 index 00000000..4f25607b --- /dev/null +++ b/scripts/merge_manifest.py @@ -0,0 +1,14 @@ +import glob +import os, sys + +MAIN_IMAGE_NAME=sys.argv[1] +TARGET_TAG="latest" if len(sys.argv) < 3 else sys.argv[2] + +args=["docker manifest create {}:{}".format(MAIN_IMAGE_NAME, TARGET_TAG)] +for i in glob.glob("/tmp/images/*/*.txt"): + with open(i, "r") as file: + args += " --amend {}@{}".format(MAIN_IMAGE_NAME, file.readline().strip()) +cmd_create="".join(args) +cmd_push="docker manifest push {}:{}".format(MAIN_IMAGE_NAME, TARGET_TAG) +os.system(cmd_create) +os.system(cmd_push) \ No newline at end of file