Add UBI 9 container with vLLM and GuideLLM (OCTOET-1123) #10
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Build and Push vLLM CPU Perf Eval Container | |
| "on": | |
| push: | |
| branches: | |
| - "main" | |
| paths: | |
| - "Containerfile" | |
| - ".github/workflows/build-and-push.yml" | |
| pull_request: | |
| branches: | |
| - "main" | |
| paths: | |
| - "Containerfile" | |
| - ".github/workflows/build-and-push.yml" | |
| workflow_dispatch: | |
| inputs: | |
| tag: | |
| description: "Image tag (default: latest)" | |
| required: false | |
| default: "latest" | |
| env: | |
| IMAGE_NAME: vllm-cpu-perf-eval | |
| REGISTRY: quay.io | |
| QUAY_ORG: octo-et | |
| jobs: | |
| build-and-test: | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| packages: write | |
| id-token: write | |
| strategy: | |
| matrix: | |
| arch: [amd64, arm64] | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| - name: Validate manual tag policy | |
| if: github.event_name == 'workflow_dispatch' | |
| run: | | |
| if [[ "${{ github.event.inputs.tag }}" == "latest" && "${{ github.ref_name }}" != "main" ]]; then | |
| echo "::error::Cannot use 'latest' tag from non-main branch '${{ github.ref_name }}'" | |
| echo "::error::The 'latest' tag is reserved for the main branch only" | |
| echo "::error::Please specify a different tag (e.g., 'dev', 'test-feature', 'pr-123') or merge to main first" | |
| exit 1 | |
| fi | |
| - name: Set up QEMU | |
| uses: docker/setup-qemu-action@v3 | |
| with: | |
| platforms: linux/amd64,linux/arm64 | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@v3 | |
| # Login to Red Hat registry to pull UBI base images | |
| - name: Log in to Red Hat Registry | |
| uses: docker/login-action@v3 | |
| with: | |
| registry: registry.redhat.io | |
| username: ${{ secrets.REDHAT_REGISTRY_USERNAME }} | |
| password: ${{ secrets.REDHAT_REGISTRY_PASSWORD }} | |
| # Only login to Quay if we're going to push (not on PR) | |
| - name: Log in to Quay.io | |
| if: github.event_name != 'pull_request' | |
| uses: docker/login-action@v3 | |
| with: | |
| registry: ${{ env.REGISTRY }} | |
| username: ${{ secrets.QUAY_USERNAME }} | |
| password: ${{ secrets.QUAY_PASSWORD }} | |
| - name: Extract metadata | |
| id: meta | |
| uses: docker/metadata-action@v5 | |
| with: | |
| images: ${{ env.REGISTRY }}/${{ env.QUAY_ORG }}/${{ env.IMAGE_NAME }} | |
| tags: | | |
| type=raw,value=${{ github.event.inputs.tag }},enable=${{ github.event.inputs.tag != '' }} | |
| type=ref,event=branch | |
| type=ref,event=pr | |
| type=semver,pattern={{version}} | |
| type=semver,pattern={{major}}.{{minor}} | |
| type=raw,value=latest,enable={{is_default_branch}} | |
| type=sha | |
| flavor: | | |
| suffix=-${{ matrix.arch }} | |
| - name: Build container image | |
| id: build | |
| uses: docker/build-push-action@v5 | |
| with: | |
| context: . | |
| file: ./Containerfile | |
| platforms: linux/${{ matrix.arch }} | |
| # Only push to Quay if not a PR | |
| push: ${{ github.event_name != 'pull_request' }} | |
| tags: ${{ steps.meta.outputs.tags }} | |
| labels: ${{ steps.meta.outputs.labels }} | |
| cache-from: type=gha,scope=build-${{ matrix.arch }} | |
| cache-to: type=gha,mode=max,scope=build-${{ matrix.arch }} | |
| build-args: | | |
| TARGETARCH=${{ matrix.arch }} | |
| # Save to local tar for testing in PRs | |
| outputs: ${{ github.event_name == 'pull_request' && 'type=docker,dest=/tmp/image.tar' || '' }} | |
| # Test the image in PRs | |
| - name: Load and test image (PR only) | |
| if: github.event_name == 'pull_request' | |
| run: | | |
| docker load --input /tmp/image.tar | |
| # Capture the first tag to a variable | |
| image_tag=$(echo "${{ steps.meta.outputs.tags }}" | head -1) | |
| echo "Testing image: $image_tag" | |
| echo "Testing vLLM installation..." | |
| docker run --rm "$image_tag" python -c "import vllm; print(f'vLLM version: {vllm.__version__}')" | |
| echo "Testing GuideLLM installation..." | |
| docker run --rm "$image_tag" python -c "import guidellm; print('GuideLLM installed successfully')" | |
| echo "✅ Container tests passed!" | |
| # Only sign and push digest if not a PR | |
| - name: Install Cosign | |
| if: github.event_name != 'pull_request' | |
| uses: sigstore/cosign-installer@v3 | |
| - name: Sign the container image (using immutable digest) | |
| if: github.event_name != 'pull_request' | |
| env: | |
| COSIGN_EXPERIMENTAL: "true" | |
| run: | | |
| # Sign using immutable digest reference only (not mutable tags) | |
| image_ref="${{ env.REGISTRY }}/${{ env.QUAY_ORG }}/${{ env.IMAGE_NAME }}@${{ steps.build.outputs.digest }}" | |
| echo "Signing image at digest: $image_ref" | |
| cosign sign --yes "$image_ref" | |
| echo "✅ Signed ${{ matrix.arch }} image" | |
| - name: Export digest | |
| if: github.event_name != 'pull_request' | |
| run: | | |
| mkdir -p /tmp/digests | |
| digest="${{ steps.build.outputs.digest }}" | |
| touch "/tmp/digests/${digest#sha256:}" | |
| - name: Upload digest | |
| if: github.event_name != 'pull_request' | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: digests-${{ matrix.arch }} | |
| path: /tmp/digests/* | |
| if-no-files-found: error | |
| retention-days: 1 | |
| # Only create manifest if not a PR | |
| create-manifest: | |
| runs-on: ubuntu-latest | |
| if: github.event_name != 'pull_request' | |
| needs: build-and-test | |
| permissions: | |
| contents: read | |
| packages: write | |
| id-token: write | |
| steps: | |
| - name: Download digests | |
| uses: actions/download-artifact@v4 | |
| with: | |
| path: /tmp/digests | |
| pattern: digests-* | |
| merge-multiple: true | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@v3 | |
| - name: Log in to Quay.io | |
| uses: docker/login-action@v3 | |
| with: | |
| registry: ${{ env.REGISTRY }} | |
| username: ${{ secrets.QUAY_USERNAME }} | |
| password: ${{ secrets.QUAY_PASSWORD }} | |
| - name: Extract metadata | |
| id: meta | |
| uses: docker/metadata-action@v5 | |
| with: | |
| images: ${{ env.REGISTRY }}/${{ env.QUAY_ORG }}/${{ env.IMAGE_NAME }} | |
| tags: | | |
| type=raw,value=${{ github.event.inputs.tag }},enable=${{ github.event.inputs.tag != '' }} | |
| type=ref,event=branch | |
| type=semver,pattern={{version}} | |
| type=raw,value=latest,enable={{is_default_branch}} | |
| - name: Create manifest list and push | |
| working-directory: /tmp/digests | |
| run: | | |
| docker buildx imagetools create \ | |
| $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \ | |
| $(printf '${{ env.REGISTRY }}/${{ env.QUAY_ORG }}/${{ env.IMAGE_NAME }}@sha256:%s ' *) | |
| - name: Install Cosign | |
| uses: sigstore/cosign-installer@v3 | |
| - name: Sign the manifest (using immutable digest) | |
| env: | |
| COSIGN_EXPERIMENTAL: "true" | |
| run: | | |
| # Get the first tag to resolve the manifest digest | |
| first_tag=$(echo "${{ steps.meta.outputs.tags }}" | head -1) | |
| echo "Resolving digest for tag: $first_tag" | |
| # Get the multi-arch manifest digest (OCI index) | |
| manifest_digest=$(docker buildx imagetools inspect "$first_tag" --format '{{json .Manifest}}' | jq -r '.digest') | |
| echo "Signing manifest digest: $manifest_digest" | |
| # Sign the immutable digest (not mutable tags) | |
| cosign sign --yes "${{ env.REGISTRY }}/${{ env.QUAY_ORG }}/${{ env.IMAGE_NAME }}@${manifest_digest}" | |
| echo "✅ Signed image at digest: $manifest_digest" | |
| - name: Inspect image | |
| run: | | |
| docker buildx imagetools inspect \ | |
| ${{ env.REGISTRY }}/${{ env.QUAY_ORG }}/${{ env.IMAGE_NAME }}:${{ steps.meta.outputs.version }} | |
| - name: Comment on PR with image info | |
| if: github.event_name == 'push' | |
| run: | | |
| echo "✅ Multi-arch container image published!" | |
| echo "📦 Image: ${{ env.REGISTRY }}/${{ env.QUAY_ORG }}/${{ env.IMAGE_NAME }}:latest" | |
| echo "🔐 Image signed with Cosign" |