GitHub Actions密钥无法在Docker容器中生效问题求助
环境变量无法注入Docker容器的排查与解决
问题场景
通过GitHub Actions构建Docker镜像并推送到GitHub Container Registry,随后部署到VPS服务器,但容器内无法获取到环境变量,要么为空值,要么直接报错找不到变量。
相关配置文件
GitHub Actions工作流文件
# GitHub packages build and push v2 name: Build and Push Docker Image on: push: branches: [ master ] pull_request: branches: [ master ] jobs: build: runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v2 - name: Set up QEMU uses: docker/setup-qemu-action@v1 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v1 - name: Login to GitHub Container Registry uses: docker/login-action@v1 with: registry: ghcr.io username: ${{ github.repository_owner }} password: ${{ secrets.TOKEN }} - name: Build and push id: docker_build uses: docker/build-push-action@v2 with: context: . file: ./Dockerfile push: true tags: ghcr.io/${{ github.repository_owner }}/piggybot:latest deploy: needs: build runs-on: ubuntu-latest env: BOT_TOKEN: ${{ secrets.BOT_TOKEN }} POSTGRES_DSN: ${{ secrets.POSTGRES_DSN }} REDIS_DSN: ${{ secrets.REDIS_DSN }} APP_HOST: ${{ secrets.APP_HOST }} APP_PORT: ${{ secrets.APP_PORT }} WEBHOOK_DOMAIN: ${{ secrets.WEBHOOK_DOMAIN }} WEBHOOK_PATH: ${{ secrets.WEBHOOK_PATH }} ENVIRONMENT: ${{ secrets.ENVIRONMENT }} SERVICE_NAME: ${{ secrets.SERVICE_NAME }} steps: - name: Deploy to VDS uses: appleboy/ssh-action@master with: host: ${{ secrets.SSH_HOST }} username: ${{ secrets.SSH_USER }} password: ${{ secrets.SSH_PASS }} port: ${{ secrets.SSH_PORT }} script: | docker login -u ${{ github.repository_owner }} -p ${{ secrets.TOKEN }} ghcr.io docker pull ghcr.io/${{ github.repository_owner }}/piggybot:latest docker stop piggybot docker rm piggybot docker run -e BOT_TOKEN=$BOT_TOKEN -e POSTGRES_DSN=$POSTGRES_DSN -e REDIS_DSN=$REDIS_DSN -e APP_HOST=$APP_HOST -e APP_PORT=$APP_PORT -e WEBHOOK_DOMAIN=$WEBHOOK_DOMAIN -e WEBHOOK_PATH=$WEBHOOK_PATH -e ENVIRONMENT=$ENVIRONMENT -e SERVICE_NAME=$SERVICE_NAME -d --name piggybot ghcr.io/${{ github.repository_owner }}/piggybot:latest
Python环境变量读取代码
class Config(BaseSettings): bot_token: str = os.environ["BOT_TOKEN"] postgres_dsn: PostgresDsn = os.environ["POSTGRES_DSN"] redis_dsn: Optional[RedisDsn] = os.environ["REDIS_DSN"] app_host: Optional[str] = os.environ["APP_HOST"] app_port: Optional[int] = os.environ["APP_PORT"] webhook_domain: Optional[str] = os.environ["WEBHOOK_DOMAIN"] webhook_path: Optional[str] = os.environ["WEBHOOK_PATH"] environment: Optional[str] = os.environ["ENVIRONMENT"] service_name: Optional[str] = os.environ["SERVICE_NAME"]
Dockerfile
# Separate build image FROM python:3.10-slim-bullseye as compile-image RUN python -m venv /opt/venv ENV PATH="/opt/venv/bin:$PATH" COPY requirements.txt . RUN apt-get update \ && apt-get install -y gcc \ && pip install --no-cache-dir --upgrade pip \ && pip install --no-cache-dir setuptools wheel \ && pip install --no-cache-dir -r requirements.txt \ && rm -rf /var/lib/apt/lists/* # Final image FROM python:3.10-slim-bullseye COPY --from=compile-image /opt/venv /opt/venv ENV PATH="/opt/venv/bin:$PATH" WORKDIR /app COPY app /app CMD ["python", "-m", "tgbot"]
问题排查与修复
1. SSH脚本中环境变量传递错误
核心原因:GitHub Actions runner中定义的env变量无法直接传递到SSH连接的服务器shell中,导致docker run命令里的$BOT_TOKEN等变量为空。
修复方案(二选一):
方案一:通过ssh-action的env传递变量
修改deploy步骤的ssh-action配置,将需要的Secrets直接注入到SSH会话的环境中:
- name: Deploy to VDS uses: appleboy/ssh-action@master with: host: ${{ secrets.SSH_HOST }} username: ${{ secrets.SSH_USER }} password: ${{ secrets.SSH_PASS }} port: ${{ secrets.SSH_PORT }} # 定义要传递到服务器的环境变量 env: | BOT_TOKEN=${{ secrets.BOT_TOKEN }} POSTGRES_DSN=${{ secrets.POSTGRES_DSN }} REDIS_DSN=${{ secrets.REDIS_DSN }} APP_HOST=${{ secrets.APP_HOST }} APP_PORT=${{ secrets.APP_PORT }} WEBHOOK_DOMAIN=${{ secrets.WEBHOOK_DOMAIN }} WEBHOOK_PATH=${{ secrets.WEBHOOK_PATH }} ENVIRONMENT=${{ secrets.ENVIRONMENT }} SERVICE_NAME=${{ secrets.SERVICE_NAME }} script: | docker login -u ${{ github.repository_owner }} -p ${{ secrets.TOKEN }} ghcr.io docker pull ghcr.io/${{ github.repository_owner }}/piggybot:latest # 容器不存在时避免命令报错 docker stop piggybot || true docker rm piggybot || true # 包含特殊字符的变量用双引号包裹,避免shell解析错误 docker run -e BOT_TOKEN=$BOT_TOKEN -e POSTGRES_DSN="$POSTGRES_DSN" -e REDIS_DSN="$REDIS_DSN" -e APP_HOST=$APP_HOST -e APP_PORT=$APP_PORT -e WEBHOOK_DOMAIN=$WEBHOOK_DOMAIN -e WEBHOOK_PATH=$WEBHOOK_PATH -e ENVIRONMENT=$ENVIRONMENT -e SERVICE_NAME=$SERVICE_NAME -d --name piggybot ghcr.io/${{ github.repository_owner }}/piggybot:latest
方案二:直接在script中注入Secrets
跳过runner的env变量,直接将${{ secrets.XXX }}写入docker run命令:
script: | docker login -u ${{ github.repository_owner }} -p ${{ secrets.TOKEN }} ghcr.io docker pull ghcr.io/${{ github.repository_owner }}/piggybot:latest docker stop piggybot || true docker rm piggybot || true docker run -e BOT_TOKEN=${{ secrets.BOT_TOKEN }} -e POSTGRES_DSN="${{ secrets.POSTGRES_DSN }}" -e REDIS_DSN="${{ secrets.REDIS_DSN }}" -e APP_HOST=${{ secrets.APP_HOST }} -e APP_PORT=${{ secrets.APP_PORT }} -e WEBHOOK_DOMAIN=${{ secrets.WEBHOOK_DOMAIN }} -e WEBHOOK_PATH=${{ secrets.WEBHOOK_PATH }} -e ENVIRONMENT=${{ secrets.ENVIRONMENT }} -e SERVICE_NAME=${{ secrets.SERVICE_NAME }} -d --name piggybot ghcr.io/${{ github.repository_owner }}/piggybot:latest
2. Python代码中环境变量读取方式错误
核心原因:手动调用os.environ会绕过Pydantic BaseSettings的自动处理逻辑,且变量不存在时会抛出未捕获的KeyError。
修复方案:利用BaseSettings的自动读取特性,去掉手动的os.environ调用:
from pydantic import BaseSettings, PostgresDsn, RedisDsn from typing import Optional class Config(BaseSettings): # 必填字段,未设置会抛出明确错误 bot_token: str postgres_dsn: PostgresDsn # 可选字段,未设置则为None redis_dsn: Optional[RedisDsn] = None app_host: Optional[str] = None app_port: Optional[int] = None webhook_domain: Optional[str] = None webhook_path: Optional[str] = None environment: Optional[str] = None service_name: Optional[str] = None class Config: # 本地开发时可加载.env文件,部署时无需此配置 env_file = ".env" # 允许环境变量大写(如BOT_TOKEN)匹配小写字段(bot_token) case_sensitive = False
3. 验证环境变量是否注入成功
在部署脚本末尾添加验证命令,可在GitHub Actions日志中查看容器内的环境变量:
docker exec piggybot printenv | grep -E "(BOT_TOKEN|POSTGRES_DSN|REDIS_DSN)"
内容的提问来源于stack exchange,提问作者Humanoid
相关产品推荐
相关产品推荐

