From 092a960dd6ee3b06a5017b0871353f493bd66e98 Mon Sep 17 00:00:00 2001 From: whyour Date: Sat, 30 May 2026 18:00:31 +0800 Subject: [PATCH] =?UTF-8?q?chore:=20=E5=90=88=E5=B9=B6=20debian=20?= =?UTF-8?q?=E5=92=8C=20alpine=20=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/build-docker-image.yml | 197 ++++++++++++++++++----- back/config/util.ts | 55 ++++++- back/services/cron.ts | 56 +++++-- docker/310.Dockerfile | 103 +++++------- docker/310.Dockerfile.debian | 112 +++++++++++++ docker/Dockerfile | 103 +++++------- docker/Dockerfile.debian | 112 +++++++++++++ docker/docker-entrypoint.sh | 15 +- shell/bot.sh | 23 ++- shell/start.sh | 53 +++--- 10 files changed, 614 insertions(+), 215 deletions(-) create mode 100644 docker/310.Dockerfile.debian create mode 100644 docker/Dockerfile.debian diff --git a/.github/workflows/build-docker-image.yml b/.github/workflows/build-docker-image.yml index 2d7f38a6..420dc9f4 100644 --- a/.github/workflows/build-docker-image.yml +++ b/.github/workflows/build-docker-image.yml @@ -7,8 +7,6 @@ on: branches: - "master" - "develop" - - "debian" - - "debian-dev" tags: - "v*" workflow_dispatch: @@ -91,9 +89,6 @@ jobs: env: GITHUB_REPO: github.com/${{ github.repository_owner }}/qinglong-static GITHUB_BRANCH: ${{ github.ref_name }} - REPO_GITEE: git@gitee.com:whyour/qinglong-static.git - REPO_GITLAB: git@gitlab.com:whyour/qinglong-static.git - PRIVATE_KEY: ${{ secrets.GITLAB_SSH_PK }} run: | mkdir -p tmp cd ./tmp @@ -137,16 +132,13 @@ jobs: git remote set-url origin git@gitee.com:whyour/qinglong-static.git git push --force --mirror - build: + build-alpine: if: ${{ !startsWith(github.ref, 'refs/tags/') }} needs: build-static - runs-on: ubuntu-22.04 - permissions: packages: write contents: read - steps: - uses: actions/checkout@v6 - uses: pnpm/action-setup@v6 @@ -162,11 +154,9 @@ jobs: run: | VERSION=$(grep '^version:' version.yaml | awk '{print $2}') echo "version=$VERSION" >> $GITHUB_OUTPUT - echo "Version: $VERSION" - name: Setup timezone - run: | - sudo timedatectl set-timezone Asia/Shanghai + run: sudo timedatectl set-timezone Asia/Shanghai - name: Login to DockerHub uses: docker/login-action@v4 @@ -191,9 +181,9 @@ jobs: flavor: | latest=false tags: | - type=ref,event=branch,enable=${{ github.ref == format('refs/heads/{0}', 'debian-dev') }} - type=ref,event=branch,enable=${{ github.ref == format('refs/heads/{0}', 'debian') }} - type=raw,value=${{ steps.version.outputs.version }}-debian,enable=${{ github.ref == format('refs/heads/{0}', 'debian') }} + type=ref,event=branch,enable=${{ github.ref == format('refs/heads/{0}', 'develop') }} + type=ref,event=branch,enable=${{ github.ref == format('refs/heads/{0}', 'master') }} + type=raw,value=${{ steps.version.outputs.version }},enable=${{ github.ref == format('refs/heads/{0}', 'master') }} type=semver,pattern={{version}} - name: Set up QEMU @@ -202,8 +192,7 @@ jobs: - name: Set up Docker Buildx uses: docker/setup-buildx-action@v4 - - name: Build and push - id: docker_build + - name: Build and push (Alpine) uses: docker/build-push-action@v7 with: build-args: | @@ -211,29 +200,22 @@ jobs: QL_BRANCH=${{ github.ref_name }} SOURCE_COMMIT=${{ github.sha }} network: host - platforms: linux/amd64,linux/arm/v7,linux/arm64,linux/ppc64le,linux/s390x + platforms: linux/amd64,linux/arm/v6,linux/arm/v7,linux/arm64,linux/ppc64le,linux/386 context: . file: ./docker/Dockerfile push: true tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} - cache-from: type=registry,ref=whyour/qinglong:cache-debian - cache-to: type=registry,ref=whyour/qinglong:cache-debian,mode=max + cache-from: type=registry,ref=whyour/qinglong:cache-alpine + cache-to: type=registry,ref=whyour/qinglong:cache-alpine,mode=max - - name: Image digest - run: | - echo ${{ steps.docker_build.outputs.digest }} - - build310: - if: ${{ github.ref_name == 'debian' }} + build-debian: + if: ${{ !startsWith(github.ref, 'refs/tags/') }} needs: build-static - runs-on: ubuntu-22.04 - permissions: packages: write contents: read - steps: - uses: actions/checkout@v6 - uses: pnpm/action-setup@v6 @@ -249,11 +231,85 @@ jobs: run: | VERSION=$(grep '^version:' version.yaml | awk '{print $2}') echo "version=$VERSION" >> $GITHUB_OUTPUT - echo "Version: $VERSION" - name: Setup timezone + run: sudo timedatectl set-timezone Asia/Shanghai + + - name: Login to DockerHub + uses: docker/login-action@v4 + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_PASSWORD }} + + - name: Login to GHCR + uses: docker/login-action@v4 + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract metadata (tags, labels) for Docker + id: meta + uses: docker/metadata-action@v6 + with: + images: | + ${{ github.repository }} + ghcr.io/${{ github.repository }} + flavor: | + latest=false + tags: | + type=raw,value=debian-dev,enable=${{ github.ref == format('refs/heads/{0}', 'develop') }} + type=raw,value=debian,enable=${{ github.ref == format('refs/heads/{0}', 'master') }} + type=raw,value=${{ steps.version.outputs.version }}-debian,enable=${{ github.ref == format('refs/heads/{0}', 'master') }} + + - name: Set up QEMU + uses: docker/setup-qemu-action@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v4 + + - name: Build and push (Debian) + uses: docker/build-push-action@v7 + with: + build-args: | + MAINTAINER=${{ github.repository_owner }} + QL_BRANCH=${{ github.ref_name }} + SOURCE_COMMIT=${{ github.sha }} + network: host + platforms: linux/amd64,linux/arm/v7,linux/arm64,linux/ppc64le,linux/s390x + context: . + file: ./docker/Dockerfile.debian + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=registry,ref=whyour/qinglong:cache-debian + cache-to: type=registry,ref=whyour/qinglong:cache-debian,mode=max + + build-alpine310: + if: ${{ github.ref_name == 'master' }} + needs: build-static + runs-on: ubuntu-22.04 + permissions: + packages: write + contents: read + steps: + - uses: actions/checkout@v6 + - uses: pnpm/action-setup@v6 + with: + version: "8.3.1" + - uses: actions/setup-node@v6 + with: + cache: "pnpm" + cache-dependency-path: pnpm-lock.yaml + + - name: Read version from version.yaml + id: version run: | - sudo timedatectl set-timezone Asia/Shanghai + VERSION=$(grep '^version:' version.yaml | awk '{print $2}') + echo "version=$VERSION" >> $GITHUB_OUTPUT + + - name: Setup timezone + run: sudo timedatectl set-timezone Asia/Shanghai - name: Login to DockerHub uses: docker/login-action@v4 @@ -274,8 +330,70 @@ jobs: - name: Set up Docker Buildx uses: docker/setup-buildx-action@v4 - - name: Build and push python3.10 - id: docker_build_310 + - name: Build and push (Alpine Python 3.10) + uses: docker/build-push-action@v7 + with: + build-args: | + MAINTAINER=${{ github.repository_owner }} + QL_BRANCH=${{ github.ref_name }} + SOURCE_COMMIT=${{ github.sha }} + network: host + platforms: linux/amd64,linux/arm/v6,linux/arm/v7,linux/arm64,linux/ppc64le,linux/386 + context: . + file: ./docker/310.Dockerfile + push: true + tags: | + whyour/qinglong:python3.10 + whyour/qinglong:${{ steps.version.outputs.version }}-python3.10 + cache-from: type=registry,ref=whyour/qinglong:cache-alpine-python3.10 + cache-to: type=registry,ref=whyour/qinglong:cache-alpine-python3.10,mode=max + + build-debian310: + if: ${{ github.ref_name == 'master' }} + needs: build-static + runs-on: ubuntu-22.04 + permissions: + packages: write + contents: read + steps: + - uses: actions/checkout@v6 + - uses: pnpm/action-setup@v6 + with: + version: "8.3.1" + - uses: actions/setup-node@v6 + with: + cache: "pnpm" + cache-dependency-path: pnpm-lock.yaml + + - name: Read version from version.yaml + id: version + run: | + VERSION=$(grep '^version:' version.yaml | awk '{print $2}') + echo "version=$VERSION" >> $GITHUB_OUTPUT + + - name: Setup timezone + run: sudo timedatectl set-timezone Asia/Shanghai + + - name: Login to DockerHub + uses: docker/login-action@v4 + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_PASSWORD }} + + - name: Login to GHCR + uses: docker/login-action@v4 + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Set up QEMU + uses: docker/setup-qemu-action@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v4 + + - name: Build and push (Debian Python 3.10) uses: docker/build-push-action@v7 with: build-args: | @@ -285,7 +403,7 @@ jobs: network: host platforms: linux/amd64,linux/arm/v7,linux/arm64,linux/ppc64le,linux/s390x context: . - file: ./docker/310.Dockerfile + file: ./docker/310.Dockerfile.debian push: true tags: | whyour/qinglong:debian-python3.10 @@ -293,14 +411,9 @@ jobs: cache-from: type=registry,ref=whyour/qinglong:cache-debian-python3.10 cache-to: type=registry,ref=whyour/qinglong:cache-debian-python3.10,mode=max - - name: Image digest - run: | - echo ${{ steps.docker_build_310.outputs.digest }} - publish: - if: ${{ github.ref_name == 'debian' }} - needs: build - + if: ${{ github.ref_name == 'master' }} + needs: [build-alpine, build-debian] runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 @@ -318,7 +431,7 @@ jobs: pnpm build:front pnpm build:back - - name: publich npm package + - name: publish npm package run: | echo "//registry.npmjs.org/:_authToken=${{ secrets.NPM_TOKEN }}" >> ~/.npmrc npm publish diff --git a/back/config/util.ts b/back/config/util.ts index 622d4d91..0f050193 100644 --- a/back/config/util.ts +++ b/back/config/util.ts @@ -1,6 +1,6 @@ import * as fs from 'fs/promises'; import * as path from 'path'; -import { exec } from 'child_process'; +import { exec, execSync } from 'child_process'; import psTreeFun from 'ps-tree'; import { promisify } from 'util'; import { load } from 'js-yaml'; @@ -16,6 +16,32 @@ export * from './share'; let osType: 'Debian' | 'Ubuntu' | 'Alpine' | undefined; +function getOsTypeSync(): 'Debian' | 'Ubuntu' | 'Alpine' | undefined { + // 1. 环境变量覆盖 + const envOs = process.env.QL_OS_TYPE?.toLowerCase(); + if (envOs === 'alpine') return 'Alpine'; + if (envOs === 'debian') return 'Debian'; + if (envOs === 'ubuntu') return 'Ubuntu'; + + // 2. 模块缓存(由 detectOS 设置) + if (osType) return osType; + + // 3. 能力检测:检查包管理器二进制 + try { + execSync('which apt-get', { stdio: 'ignore' }); + return 'Debian'; + } catch { + try { + execSync('which apk', { stdio: 'ignore' }); + return 'Alpine'; + } catch { + // macOS / 未知系统 + } + } + + return undefined; +} + export async function getFileContentByName(fileName: string) { const _exsit = await fileExist(fileName); if (_exsit) { @@ -553,7 +579,9 @@ except: spec=u.find_spec(name) print(name if spec else '') ''')"`, - [DependenceTypes.linux]: `apt-get info ${name}`, + [DependenceTypes.linux]: getOsTypeSync() === 'Alpine' + ? `apk info -es ${name}` + : `dpkg-query -s ${name}`, }; return baseCommands[type]; @@ -564,7 +592,9 @@ export function getInstallCommand(type: DependenceTypes, name: string): string { [DependenceTypes.nodejs]: 'pnpm add -g', [DependenceTypes.python3]: 'pip3 install --disable-pip-version-check --root-user-action=ignore', - [DependenceTypes.linux]: 'apt install -y', + [DependenceTypes.linux]: getOsTypeSync() === 'Alpine' + ? 'apk add --no-check-certificate' + : 'apt-get install -y', }; let command = baseCommands[type]; @@ -584,7 +614,9 @@ export function getUninstallCommand( [DependenceTypes.nodejs]: 'pnpm remove -g', [DependenceTypes.python3]: 'pip3 uninstall --disable-pip-version-check --root-user-action=ignore -y', - [DependenceTypes.linux]: 'apt remove -y', + [DependenceTypes.linux]: getOsTypeSync() === 'Alpine' + ? 'apk del' + : 'apt-get remove -y', }; return `${baseCommands[type]} ${name.trim()}`; @@ -619,6 +651,21 @@ export async function detectOS(): Promise< 'Debian' | 'Ubuntu' | 'Alpine' | undefined > { if (osType) return osType; + + const envOs = process.env.QL_OS_TYPE?.toLowerCase(); + if (envOs === 'alpine') { + osType = 'Alpine'; + return osType; + } + if (envOs === 'debian') { + osType = 'Debian'; + return osType; + } + if (envOs === 'ubuntu') { + osType = 'Ubuntu'; + return osType; + } + const platform = os.platform(); if (platform === 'linux') { diff --git a/back/services/cron.ts b/back/services/cron.ts index 3ff5242f..def418a4 100644 --- a/back/services/cron.ts +++ b/back/services/cron.ts @@ -39,6 +39,25 @@ export default class CronService { return false; } + private get schedulerMode(): 'system' | 'node' { + const env = process.env.QL_SCHEDULER; + if (env === 'system') return 'system'; + if (env === 'node') return 'node'; + try { + execSync('which crond', { stdio: 'ignore' }); + return 'system'; + } catch { + return 'node'; + } + } + + private shouldUseCronClient(cron: Crontab): boolean { + if (this.schedulerMode === 'node') { + return !this.isSpecialSchedule(cron.schedule); + } + return this.isNodeCron(cron) && !this.isSpecialSchedule(cron.schedule); + } + private isOnceSchedule(schedule?: string) { return schedule?.startsWith(ScheduleType.ONCE); } @@ -80,7 +99,7 @@ export default class CronService { return doc; } - if (!this.isSpecialSchedule(doc.schedule)) { + if (this.shouldUseCronClient(doc)) { await cronClient.addCron([ { name: doc.name || '', @@ -113,7 +132,7 @@ export default class CronService { await cronClient.delCron([String(newDoc.id)]); - if (!this.isSpecialSchedule(newDoc.schedule)) { + if (this.shouldUseCronClient(newDoc)) { await cronClient.addCron([ { name: doc.name || '', @@ -575,18 +594,20 @@ export default class CronService { public async enabled(ids: number[]) { await CrontabModel.update({ isDisabled: 0 }, { where: { id: ids } }); const docs = await CrontabModel.findAll({ where: { id: ids } }); - const crons = docs.map((doc) => ({ - name: doc.name || '', - id: String(doc.id), - schedule: doc.schedule!, - command: this.makeCommand(doc), - extra_schedules: doc.extra_schedules || [], - })); + const crons = docs + .filter((x) => this.shouldUseCronClient(x)) + .map((doc) => ({ + name: doc.name || '', + id: String(doc.id), + schedule: doc.schedule!, + command: this.makeCommand(doc), + extra_schedules: doc.extra_schedules || [], + })); if (isDemoEnv()) { return; } - + await cronClient.addCron(crons); await this.setCrontab(); } @@ -687,6 +708,15 @@ export default class CronService { await writeFileWithLock(config.crontabFile, crontab_string); + if (this.schedulerMode === 'system') { + try { + execSync(`crontab ${config.crontabFile}`); + } catch (error: any) { + const errorMsg = error.message || String(error); + this.logger.error('[crontab] Failed to update system crontab:', errorMsg); + } + } + await CrontabModel.update({ saved: true }, { where: {} }); } @@ -732,7 +762,11 @@ export default class CronService { public async autosave_crontab() { const tabs = await this.crontabs(); const regularCrons = tabs.data - .filter((x) => x.isDisabled !== 1 && !this.isSpecialSchedule(x.schedule)) + .filter( + (x) => + x.isDisabled !== 1 && + this.shouldUseCronClient(x), + ) .map((doc) => ({ name: doc.name || '', id: String(doc.id), diff --git a/docker/310.Dockerfile b/docker/310.Dockerfile index 194a6faa..6bc02e53 100644 --- a/docker/310.Dockerfile +++ b/docker/310.Dockerfile @@ -1,18 +1,13 @@ -FROM node:22-slim AS nodebuilder - -FROM python:3.10-slim-bookworm AS builder +FROM python:3.10-alpine3.18 AS builder COPY package.json .npmrc pnpm-lock.yaml /tmp/build/ -COPY --from=nodebuilder /usr/local/bin/node /usr/local/bin/ -COPY --from=nodebuilder /usr/local/lib/node_modules/. /usr/local/lib/node_modules/ -RUN set -x && \ - ln -s /usr/local/lib/node_modules/npm/bin/npm-cli.js /usr/local/bin/npm && \ - apt-get update && \ - apt-get install --no-install-recommends -y libatomic1 && \ - npm i -g pnpm@8.3.1 && \ - cd /tmp/build && \ - pnpm install --prod +RUN set -x \ + && apk update \ + && apk add nodejs npm git \ + && npm i -g pnpm@8.3.1 pm2 ts-node \ + && cd /tmp/build \ + && pnpm install --prod -FROM python:3.10-slim-bookworm +FROM python:3.10-alpine ARG QL_MAINTAINER="whyour" LABEL maintainer="${QL_MAINTAINER}" @@ -26,63 +21,51 @@ ENV QL_DIR=/ql \ SHELL=/bin/bash \ PS1="\u@\h:\w \$ " -ARG QL_UID=5432 -ARG QL_GID=5432 -RUN groupadd -g ${QL_GID} qinglong && \ - useradd -m -u ${QL_UID} -g ${QL_GID} -s /bin/bash qinglong && \ - mkdir -p /home/qinglong/bin /home/qinglong/.ssh && \ - chmod 700 /home/qinglong/.ssh && \ - chown -R ${QL_UID}:${QL_GID} /home/qinglong +VOLUME /ql/data -ENV QL_USER=qinglong -ENV QL_HOME=/home/$QL_USER +EXPOSE 5700 -COPY --from=nodebuilder /usr/local/bin/node /usr/local/bin/ -COPY --from=nodebuilder /usr/local/lib/node_modules/. /usr/local/lib/node_modules/ +COPY --from=builder /usr/local/lib/node_modules/. /usr/local/lib/node_modules/ +COPY --from=builder /usr/local/bin/. /usr/local/bin/ -RUN set -x && \ - ln -s /usr/local/lib/node_modules/npm/bin/npm-cli.js /usr/local/bin/npm && \ - apt-get update && \ - apt-get upgrade -y && \ - apt-get install --no-install-recommends -y git \ +RUN set -x \ + && apk update -f \ + && apk upgrade \ + && apk --no-cache add -f bash \ + coreutils \ + git \ curl \ wget \ tzdata \ perl \ openssl \ - openssh-client \ + nodejs \ jq \ + openssh \ procps \ netcat-openbsd \ unzip \ - libatomic1 && \ - apt-get clean && \ - ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && \ - echo "Asia/Shanghai" >/etc/timezone && \ - git config --global user.email "qinglong@users.noreply.github.com" && \ - git config --global user.name "qinglong" && \ - git config --global http.postBuffer 524288000 && \ - npm install -g pnpm@8.3.1 pm2 ts-node && \ - rm -rf /root/.cache && \ - rm -rf /root/.npm && \ - rm -rf /etc/apt/apt.conf.d/docker-clean && \ - ulimit -c 0 - -RUN mkdir -p ${QL_DIR} ${QL_DIR}/data && \ - chown -R ${QL_UID}:${QL_GID} ${QL_DIR} - -USER qinglong + npm \ + && rm -rf /var/cache/apk/* \ + && apk update \ + && ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \ + && echo "Asia/Shanghai" > /etc/timezone \ + && git config --global user.email "qinglong@users.noreply.github.com" \ + && git config --global user.name "qinglong" \ + && git config --global http.postBuffer 524288000 \ + && rm -rf /root/.cache \ + && ulimit -c 0 ARG SOURCE_COMMIT -RUN git clone --depth=1 -b ${QL_BRANCH} ${QL_URL} ${QL_DIR} && \ - cd ${QL_DIR} && \ - cp -f .env.example .env && \ - chmod 777 ${QL_DIR}/shell/*.sh && \ - chmod 777 ${QL_DIR}/docker/*.sh && \ - git clone --depth=1 -b ${QL_BRANCH} https://github.com/${QL_MAINTAINER}/qinglong-static.git /tmp/static && \ - mkdir -p ${QL_DIR}/static && \ - cp -rf /tmp/static/* ${QL_DIR}/static && \ - rm -rf /tmp/static +RUN git clone --depth=1 -b ${QL_BRANCH} ${QL_URL} ${QL_DIR} \ + && cd ${QL_DIR} \ + && cp -f .env.example .env \ + && chmod 777 ${QL_DIR}/shell/*.sh \ + && chmod 777 ${QL_DIR}/docker/*.sh \ + && git clone --depth=1 -b ${QL_BRANCH} https://github.com/${QL_MAINTAINER}/qinglong-static.git /static \ + && mkdir -p ${QL_DIR}/static \ + && cp -rf /static/* ${QL_DIR}/static \ + && rm -rf /static ENV PNPM_HOME=${QL_DIR}/data/dep_cache/node \ PYTHON_HOME=${QL_DIR}/data/dep_cache/python3 \ @@ -96,9 +79,7 @@ ENV PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:${PNPM_HOM RUN pip3 install --prefix ${PYTHON_HOME} requests -COPY --chown=qinglong:qinglong --from=builder /tmp/build/node_modules/. /ql/node_modules/ - -USER root +COPY --from=builder /tmp/build/node_modules/. /ql/node_modules/ WORKDIR ${QL_DIR} @@ -106,7 +87,3 @@ HEALTHCHECK --interval=5s --timeout=2s --retries=20 \ CMD curl -sf --noproxy '*' http://localhost:${QlPort:-5700}/api/health || exit 1 ENTRYPOINT ["./docker/docker-entrypoint.sh"] - -VOLUME /ql/data - -EXPOSE 5700 diff --git a/docker/310.Dockerfile.debian b/docker/310.Dockerfile.debian new file mode 100644 index 00000000..194a6faa --- /dev/null +++ b/docker/310.Dockerfile.debian @@ -0,0 +1,112 @@ +FROM node:22-slim AS nodebuilder + +FROM python:3.10-slim-bookworm AS builder +COPY package.json .npmrc pnpm-lock.yaml /tmp/build/ +COPY --from=nodebuilder /usr/local/bin/node /usr/local/bin/ +COPY --from=nodebuilder /usr/local/lib/node_modules/. /usr/local/lib/node_modules/ +RUN set -x && \ + ln -s /usr/local/lib/node_modules/npm/bin/npm-cli.js /usr/local/bin/npm && \ + apt-get update && \ + apt-get install --no-install-recommends -y libatomic1 && \ + npm i -g pnpm@8.3.1 && \ + cd /tmp/build && \ + pnpm install --prod + +FROM python:3.10-slim-bookworm + +ARG QL_MAINTAINER="whyour" +LABEL maintainer="${QL_MAINTAINER}" +ARG QL_URL=https://github.com/${QL_MAINTAINER}/qinglong.git +ARG QL_BRANCH=develop +ARG PYTHON_SHORT_VERSION=3.10 + +ENV QL_DIR=/ql \ + QL_BRANCH=${QL_BRANCH} \ + LANG=C.UTF-8 \ + SHELL=/bin/bash \ + PS1="\u@\h:\w \$ " + +ARG QL_UID=5432 +ARG QL_GID=5432 +RUN groupadd -g ${QL_GID} qinglong && \ + useradd -m -u ${QL_UID} -g ${QL_GID} -s /bin/bash qinglong && \ + mkdir -p /home/qinglong/bin /home/qinglong/.ssh && \ + chmod 700 /home/qinglong/.ssh && \ + chown -R ${QL_UID}:${QL_GID} /home/qinglong + +ENV QL_USER=qinglong +ENV QL_HOME=/home/$QL_USER + +COPY --from=nodebuilder /usr/local/bin/node /usr/local/bin/ +COPY --from=nodebuilder /usr/local/lib/node_modules/. /usr/local/lib/node_modules/ + +RUN set -x && \ + ln -s /usr/local/lib/node_modules/npm/bin/npm-cli.js /usr/local/bin/npm && \ + apt-get update && \ + apt-get upgrade -y && \ + apt-get install --no-install-recommends -y git \ + curl \ + wget \ + tzdata \ + perl \ + openssl \ + openssh-client \ + jq \ + procps \ + netcat-openbsd \ + unzip \ + libatomic1 && \ + apt-get clean && \ + ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && \ + echo "Asia/Shanghai" >/etc/timezone && \ + git config --global user.email "qinglong@users.noreply.github.com" && \ + git config --global user.name "qinglong" && \ + git config --global http.postBuffer 524288000 && \ + npm install -g pnpm@8.3.1 pm2 ts-node && \ + rm -rf /root/.cache && \ + rm -rf /root/.npm && \ + rm -rf /etc/apt/apt.conf.d/docker-clean && \ + ulimit -c 0 + +RUN mkdir -p ${QL_DIR} ${QL_DIR}/data && \ + chown -R ${QL_UID}:${QL_GID} ${QL_DIR} + +USER qinglong + +ARG SOURCE_COMMIT +RUN git clone --depth=1 -b ${QL_BRANCH} ${QL_URL} ${QL_DIR} && \ + cd ${QL_DIR} && \ + cp -f .env.example .env && \ + chmod 777 ${QL_DIR}/shell/*.sh && \ + chmod 777 ${QL_DIR}/docker/*.sh && \ + git clone --depth=1 -b ${QL_BRANCH} https://github.com/${QL_MAINTAINER}/qinglong-static.git /tmp/static && \ + mkdir -p ${QL_DIR}/static && \ + cp -rf /tmp/static/* ${QL_DIR}/static && \ + rm -rf /tmp/static + +ENV PNPM_HOME=${QL_DIR}/data/dep_cache/node \ + PYTHON_HOME=${QL_DIR}/data/dep_cache/python3 \ + PYTHONUSERBASE=${QL_DIR}/data/dep_cache/python3 \ + HOME=/root + +ENV PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:${PNPM_HOME}:${PYTHON_HOME}/bin:${HOME}/bin \ + NODE_PATH=/usr/local/bin:/usr/local/lib/node_modules \ + PIP_CACHE_DIR=${PYTHON_HOME}/pip \ + PYTHONPATH=${PYTHON_HOME}:${PYTHON_HOME}/lib/python${PYTHON_SHORT_VERSION}:${PYTHON_HOME}/lib/python${PYTHON_SHORT_VERSION}/site-packages + +RUN pip3 install --prefix ${PYTHON_HOME} requests + +COPY --chown=qinglong:qinglong --from=builder /tmp/build/node_modules/. /ql/node_modules/ + +USER root + +WORKDIR ${QL_DIR} + +HEALTHCHECK --interval=5s --timeout=2s --retries=20 \ + CMD curl -sf --noproxy '*' http://localhost:${QlPort:-5700}/api/health || exit 1 + +ENTRYPOINT ["./docker/docker-entrypoint.sh"] + +VOLUME /ql/data + +EXPOSE 5700 diff --git a/docker/Dockerfile b/docker/Dockerfile index 45ba5104..352a6cb6 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,18 +1,13 @@ -FROM node:22-slim AS nodebuilder - -FROM python:3.11-slim-bookworm AS builder +FROM python:3.11-alpine3.18 AS builder COPY package.json .npmrc pnpm-lock.yaml /tmp/build/ -COPY --from=nodebuilder /usr/local/bin/node /usr/local/bin/ -COPY --from=nodebuilder /usr/local/lib/node_modules/. /usr/local/lib/node_modules/ -RUN set -x && \ - ln -s /usr/local/lib/node_modules/npm/bin/npm-cli.js /usr/local/bin/npm && \ - apt-get update && \ - apt-get install --no-install-recommends -y libatomic1 && \ - npm i -g pnpm@8.3.1 && \ - cd /tmp/build && \ - pnpm install --prod +RUN set -x \ + && apk update \ + && apk add nodejs npm git \ + && npm i -g pnpm@8.3.1 pm2 ts-node \ + && cd /tmp/build \ + && pnpm install --prod -FROM python:3.11-slim-bookworm +FROM python:3.11-alpine ARG QL_MAINTAINER="whyour" LABEL maintainer="${QL_MAINTAINER}" @@ -26,63 +21,51 @@ ENV QL_DIR=/ql \ SHELL=/bin/bash \ PS1="\u@\h:\w \$ " -ARG QL_UID=5432 -ARG QL_GID=5432 -RUN groupadd -g ${QL_GID} qinglong && \ - useradd -m -u ${QL_UID} -g ${QL_GID} -s /bin/bash qinglong && \ - mkdir -p /home/qinglong/bin /home/qinglong/.ssh && \ - chmod 700 /home/qinglong/.ssh && \ - chown -R ${QL_UID}:${QL_GID} /home/qinglong +VOLUME /ql/data -ENV QL_USER=qinglong -ENV QL_HOME=/home/$QL_USER +EXPOSE 5700 -COPY --from=nodebuilder /usr/local/bin/node /usr/local/bin/ -COPY --from=nodebuilder /usr/local/lib/node_modules/. /usr/local/lib/node_modules/ +COPY --from=builder /usr/local/lib/node_modules/. /usr/local/lib/node_modules/ +COPY --from=builder /usr/local/bin/. /usr/local/bin/ -RUN set -x && \ - ln -s /usr/local/lib/node_modules/npm/bin/npm-cli.js /usr/local/bin/npm && \ - ln -s /usr/local/lib/node_modules/npm/bin/npx-cli.js /usr/local/bin/npx && \ - apt-get update && \ - apt-get upgrade -y && \ - apt-get install --no-install-recommends -y git \ +RUN set -x \ + && apk update -f \ + && apk upgrade \ + && apk --no-cache add -f bash \ + coreutils \ + git \ curl \ wget \ tzdata \ perl \ openssl \ - openssh-client \ + nodejs \ jq \ + openssh \ procps \ netcat-openbsd \ unzip \ - libatomic1 && \ - apt-get clean && \ - ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && \ - echo "Asia/Shanghai" >/etc/timezone && \ - git config --global user.email "qinglong@users.noreply.github.com" && \ - git config --global user.name "qinglong" && \ - git config --global http.postBuffer 524288000 && \ - npm install -g pnpm@8.3.1 pm2 ts-node && \ - rm -rf /root/.cache && \ - rm -rf /root/.npm && \ - rm -rf /etc/apt/apt.conf.d/docker-clean && \ - ulimit -c 0 + npm \ + && rm -rf /var/cache/apk/* \ + && apk update \ + && ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \ + && echo "Asia/Shanghai" > /etc/timezone \ + && git config --global user.email "qinglong@users.noreply.github.com" \ + && git config --global user.name "qinglong" \ + && git config --global http.postBuffer 524288000 \ + && rm -rf /root/.cache \ + && ulimit -c 0 -RUN mkdir -p ${QL_DIR} ${QL_DIR}/data && \ - chown -R ${QL_UID}:${QL_GID} ${QL_DIR} - -USER qinglong ARG SOURCE_COMMIT -RUN git clone --depth=1 -b ${QL_BRANCH} ${QL_URL} ${QL_DIR} && \ - cd ${QL_DIR} && \ - cp -f .env.example .env && \ - chmod 777 ${QL_DIR}/shell/*.sh && \ - chmod 777 ${QL_DIR}/docker/*.sh && \ - git clone --depth=1 -b ${QL_BRANCH} https://github.com/${QL_MAINTAINER}/qinglong-static.git /tmp/static && \ - mkdir -p ${QL_DIR}/static && \ - cp -rf /tmp/static/* ${QL_DIR}/static && \ - rm -rf /tmp/static +RUN git clone --depth=1 -b ${QL_BRANCH} ${QL_URL} ${QL_DIR} \ + && cd ${QL_DIR} \ + && cp -f .env.example .env \ + && chmod 777 ${QL_DIR}/shell/*.sh \ + && chmod 777 ${QL_DIR}/docker/*.sh \ + && git clone --depth=1 -b ${QL_BRANCH} https://github.com/${QL_MAINTAINER}/qinglong-static.git /static \ + && mkdir -p ${QL_DIR}/static \ + && cp -rf /static/* ${QL_DIR}/static \ + && rm -rf /static ENV PNPM_HOME=${QL_DIR}/data/dep_cache/node \ PYTHON_HOME=${QL_DIR}/data/dep_cache/python3 \ @@ -96,9 +79,7 @@ ENV PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:${PNPM_HOM RUN pip3 install --prefix ${PYTHON_HOME} requests -COPY --chown=qinglong:qinglong --from=builder /tmp/build/node_modules/. /ql/node_modules/ - -USER root +COPY --from=builder /tmp/build/node_modules/. /ql/node_modules/ WORKDIR ${QL_DIR} @@ -106,7 +87,3 @@ HEALTHCHECK --interval=5s --timeout=2s --retries=20 \ CMD curl -sf --noproxy '*' http://localhost:${QlPort:-5700}/api/health || exit 1 ENTRYPOINT ["./docker/docker-entrypoint.sh"] - -VOLUME /ql/data - -EXPOSE 5700 diff --git a/docker/Dockerfile.debian b/docker/Dockerfile.debian new file mode 100644 index 00000000..45ba5104 --- /dev/null +++ b/docker/Dockerfile.debian @@ -0,0 +1,112 @@ +FROM node:22-slim AS nodebuilder + +FROM python:3.11-slim-bookworm AS builder +COPY package.json .npmrc pnpm-lock.yaml /tmp/build/ +COPY --from=nodebuilder /usr/local/bin/node /usr/local/bin/ +COPY --from=nodebuilder /usr/local/lib/node_modules/. /usr/local/lib/node_modules/ +RUN set -x && \ + ln -s /usr/local/lib/node_modules/npm/bin/npm-cli.js /usr/local/bin/npm && \ + apt-get update && \ + apt-get install --no-install-recommends -y libatomic1 && \ + npm i -g pnpm@8.3.1 && \ + cd /tmp/build && \ + pnpm install --prod + +FROM python:3.11-slim-bookworm + +ARG QL_MAINTAINER="whyour" +LABEL maintainer="${QL_MAINTAINER}" +ARG QL_URL=https://github.com/${QL_MAINTAINER}/qinglong.git +ARG QL_BRANCH=develop +ARG PYTHON_SHORT_VERSION=3.11 + +ENV QL_DIR=/ql \ + QL_BRANCH=${QL_BRANCH} \ + LANG=C.UTF-8 \ + SHELL=/bin/bash \ + PS1="\u@\h:\w \$ " + +ARG QL_UID=5432 +ARG QL_GID=5432 +RUN groupadd -g ${QL_GID} qinglong && \ + useradd -m -u ${QL_UID} -g ${QL_GID} -s /bin/bash qinglong && \ + mkdir -p /home/qinglong/bin /home/qinglong/.ssh && \ + chmod 700 /home/qinglong/.ssh && \ + chown -R ${QL_UID}:${QL_GID} /home/qinglong + +ENV QL_USER=qinglong +ENV QL_HOME=/home/$QL_USER + +COPY --from=nodebuilder /usr/local/bin/node /usr/local/bin/ +COPY --from=nodebuilder /usr/local/lib/node_modules/. /usr/local/lib/node_modules/ + +RUN set -x && \ + ln -s /usr/local/lib/node_modules/npm/bin/npm-cli.js /usr/local/bin/npm && \ + ln -s /usr/local/lib/node_modules/npm/bin/npx-cli.js /usr/local/bin/npx && \ + apt-get update && \ + apt-get upgrade -y && \ + apt-get install --no-install-recommends -y git \ + curl \ + wget \ + tzdata \ + perl \ + openssl \ + openssh-client \ + jq \ + procps \ + netcat-openbsd \ + unzip \ + libatomic1 && \ + apt-get clean && \ + ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && \ + echo "Asia/Shanghai" >/etc/timezone && \ + git config --global user.email "qinglong@users.noreply.github.com" && \ + git config --global user.name "qinglong" && \ + git config --global http.postBuffer 524288000 && \ + npm install -g pnpm@8.3.1 pm2 ts-node && \ + rm -rf /root/.cache && \ + rm -rf /root/.npm && \ + rm -rf /etc/apt/apt.conf.d/docker-clean && \ + ulimit -c 0 + +RUN mkdir -p ${QL_DIR} ${QL_DIR}/data && \ + chown -R ${QL_UID}:${QL_GID} ${QL_DIR} + +USER qinglong +ARG SOURCE_COMMIT +RUN git clone --depth=1 -b ${QL_BRANCH} ${QL_URL} ${QL_DIR} && \ + cd ${QL_DIR} && \ + cp -f .env.example .env && \ + chmod 777 ${QL_DIR}/shell/*.sh && \ + chmod 777 ${QL_DIR}/docker/*.sh && \ + git clone --depth=1 -b ${QL_BRANCH} https://github.com/${QL_MAINTAINER}/qinglong-static.git /tmp/static && \ + mkdir -p ${QL_DIR}/static && \ + cp -rf /tmp/static/* ${QL_DIR}/static && \ + rm -rf /tmp/static + +ENV PNPM_HOME=${QL_DIR}/data/dep_cache/node \ + PYTHON_HOME=${QL_DIR}/data/dep_cache/python3 \ + PYTHONUSERBASE=${QL_DIR}/data/dep_cache/python3 \ + HOME=/root + +ENV PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:${PNPM_HOME}:${PYTHON_HOME}/bin:${HOME}/bin \ + NODE_PATH=/usr/local/bin:/usr/local/lib/node_modules \ + PIP_CACHE_DIR=${PYTHON_HOME}/pip \ + PYTHONPATH=${PYTHON_HOME}:${PYTHON_HOME}/lib/python${PYTHON_SHORT_VERSION}:${PYTHON_HOME}/lib/python${PYTHON_SHORT_VERSION}/site-packages + +RUN pip3 install --prefix ${PYTHON_HOME} requests + +COPY --chown=qinglong:qinglong --from=builder /tmp/build/node_modules/. /ql/node_modules/ + +USER root + +WORKDIR ${QL_DIR} + +HEALTHCHECK --interval=5s --timeout=2s --retries=20 \ + CMD curl -sf --noproxy '*' http://localhost:${QlPort:-5700}/api/health || exit 1 + +ENTRYPOINT ["./docker/docker-entrypoint.sh"] + +VOLUME /ql/data + +EXPOSE 5700 diff --git a/docker/docker-entrypoint.sh b/docker/docker-entrypoint.sh index 8eab6ca8..3015073a 100755 --- a/docker/docker-entrypoint.sh +++ b/docker/docker-entrypoint.sh @@ -128,6 +128,19 @@ fi log_with_style "SUCCESS" "🎉 容器启动成功!" -tail -f /dev/null +# 自动检测调度模式:有 crond 二进制 → system 模式,否则 node 模式 +if [ -z "$QL_SCHEDULER" ]; then + if command -v crond &>/dev/null; then + export QL_SCHEDULER="system" + else + export QL_SCHEDULER="node" + fi +fi + +if [ "$QL_SCHEDULER" = "system" ]; then + crond -f > /dev/null +else + tail -f /dev/null +fi exec "$@" diff --git a/shell/bot.sh b/shell/bot.sh index f0d1e4a6..524a44e9 100755 --- a/shell/bot.sh +++ b/shell/bot.sh @@ -9,15 +9,22 @@ else fi echo -e "\n1、安装bot依赖...\n" -os_name=$(source /etc/os-release && echo "$ID") -if [[ $os_name == 'alpine' ]]; then - apk --no-cache add -f zlib-dev gcc jpeg-dev python3-dev musl-dev freetype-dev -elif [[ $os_name == 'debian' ]] || [[ $os_name == 'ubuntu' ]]; then - apt-get install -y gcc python3-dev musl-dev -else - echo -e "暂不支持此系统 $os_name" - exit 1 +os_name="${QL_OS_TYPE:-}" +if [ -z "$os_name" ]; then + os_name=$(source /etc/os-release && echo "$ID") fi +case "$os_name" in + alpine) + apk --no-cache add -f zlib-dev gcc jpeg-dev python3-dev musl-dev freetype-dev + ;; + debian|ubuntu) + apt-get install -y gcc python3-dev musl-dev zlib1g-dev libjpeg-dev libfreetype-dev + ;; + *) + echo -e "暂不支持此系统 $os_name" + exit 1 + ;; +esac echo -e "\nbot依赖安装成功...\n" echo -e "2、下载bot所需文件...\n" diff --git a/shell/start.sh b/shell/start.sh index b01902e0..1f015f2b 100644 --- a/shell/start.sh +++ b/shell/start.sh @@ -36,31 +36,38 @@ command="$1" if [[ $command != "reload" ]]; then # 安装依赖 - os_name=$(source /etc/os-release && echo "$ID") - - if [[ $os_name == 'alpine' ]]; then - apk update - apk add -f bash \ - coreutils \ - git \ - curl \ - wget \ - tzdata \ - perl \ - openssl \ - jq \ - nginx \ - openssh \ - procps \ - netcat-openbsd - elif [[ $os_name == 'debian' ]] || [[ $os_name == 'ubuntu' ]]; then - apt-get update - apt-get install -y git curl wget tzdata perl openssl jq nginx procps netcat-openbsd openssh-client - else - echo -e "暂不支持此系统部署 $os_name" - exit 1 + os_name="${QL_OS_TYPE:-}" + if [ -z "$os_name" ]; then + os_name=$(source /etc/os-release && echo "$ID") fi + case "$os_name" in + alpine) + apk update + apk add -f bash \ + coreutils \ + git \ + curl \ + wget \ + tzdata \ + perl \ + openssl \ + jq \ + nginx \ + openssh \ + procps \ + netcat-openbsd + ;; + debian|ubuntu) + apt-get update + apt-get install -y git curl wget tzdata perl openssl jq nginx procps netcat-openbsd openssh-client + ;; + *) + echo -e "暂不支持此系统部署 $os_name" + exit 1 + ;; + esac + npm install -g pnpm@8.3.1 pm2 ts-node fi