diff --git a/.github/workflows/build-mongodb-community.yml b/.github/workflows/build-mongodb-community.yml new file mode 100644 index 00000000..8cacfc35 --- /dev/null +++ b/.github/workflows/build-mongodb-community.yml @@ -0,0 +1,226 @@ +name: Build MongoDB Community Images + +on: + push: + branches: + - main + paths: + - 'percona-server-mongodb-8.0-community/**' + - '.github/workflows/build-mongodb-community.yml' + pull_request: + branches: + - main + paths: + - 'percona-server-mongodb-8.0-community/**' + - '.github/workflows/build-mongodb-community.yml' + release: + types: + - published + workflow_dispatch: + inputs: + push_images: + description: 'Push images to registry (true/false)' + required: false + default: 'false' + type: choice + options: + - 'true' + - 'false' + +env: + REGISTRY: ghcr.io + IMAGE_NAME: objectrocket/percona-docker/percona-server-mongodb-8.0-community + MONGODB_VERSION: 8.0.3 + +jobs: + build-and-push: + name: Build and Push MongoDB Community Images + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + id-token: write + + strategy: + fail-fast: false + matrix: + variant: + - name: standard + dockerfile: Dockerfile + suffix: '' + - name: k8s + dockerfile: Dockerfile.k8s + suffix: '-k8s' + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Set up QEMU for multi-architecture builds + uses: docker/setup-qemu-action@v3 + with: + platforms: linux/amd64,linux/arm64 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + with: + driver-opts: | + image=moby/buildkit:latest + network=host + + - name: Log in to GitHub Container Registry + if: github.event_name != 'pull_request' + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract metadata for Docker + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + flavor: | + latest=auto + suffix=${{ matrix.variant.suffix }},onlatest=true + tags: | + type=raw,value=latest,enable={{is_default_branch}} + type=raw,value=${{ env.MONGODB_VERSION }} + type=raw,value=8.0 + type=semver,pattern={{version}},enable=${{ github.event_name == 'release' }} + type=semver,pattern={{major}}.{{minor}},enable=${{ github.event_name == 'release' }} + type=ref,event=pr + type=sha,format=short + labels: | + org.opencontainers.image.title=MongoDB Community Edition ${{ matrix.variant.name }} + org.opencontainers.image.description=Multi-architecture MongoDB Community Edition ${{ matrix.variant.name }} variant + org.opencontainers.image.version=${{ env.MONGODB_VERSION }} + org.opencontainers.image.vendor=ObjectRocket + org.opencontainers.image.source=${{ github.server_url }}/${{ github.repository }} + org.opencontainers.image.revision=${{ github.sha }} + org.opencontainers.image.created=${{ github.event.head_commit.timestamp }} + org.opencontainers.image.licenses=SSPL-1.0 + io.objectrocket.mongodb.version=${{ env.MONGODB_VERSION }} + io.objectrocket.variant=${{ matrix.variant.name }} + + - name: Verify healthcheck.sh exists for k8s variant + if: matrix.variant.name == 'k8s' + run: | + if [ ! -f percona-server-mongodb-8.0-community/healthcheck.sh ]; then + echo "Error: healthcheck.sh not found for k8s variant" + exit 1 + fi + if [ ! -x percona-server-mongodb-8.0-community/healthcheck.sh ]; then + echo "Warning: healthcheck.sh is not executable, setting permissions" + chmod +x percona-server-mongodb-8.0-community/healthcheck.sh + fi + echo "✓ healthcheck.sh verified" + + - name: Build and push Docker image + id: build-push + uses: docker/build-push-action@v5 + with: + context: ./percona-server-mongodb-8.0-community + file: ./percona-server-mongodb-8.0-community/${{ matrix.variant.dockerfile }} + platforms: linux/amd64,linux/arm64 + push: ${{ github.event_name != 'pull_request' && (github.event_name == 'release' || github.ref == 'refs/heads/main' || github.event.inputs.push_images == 'true') }} + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha,scope=${{ matrix.variant.name }} + cache-to: type=gha,mode=max,scope=${{ matrix.variant.name }} + provenance: true + sbom: true + + - name: Generate build summary + run: | + echo "## Build Summary - ${{ matrix.variant.name }} variant" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "**Image:** \`${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}\`" >> $GITHUB_STEP_SUMMARY + echo "**Variant:** ${{ matrix.variant.name }}" >> $GITHUB_STEP_SUMMARY + echo "**Dockerfile:** ${{ matrix.variant.dockerfile }}" >> $GITHUB_STEP_SUMMARY + echo "**MongoDB Version:** ${{ env.MONGODB_VERSION }}" >> $GITHUB_STEP_SUMMARY + echo "**Platforms:** linux/amd64, linux/arm64" >> $GITHUB_STEP_SUMMARY + echo "**Digest:** \`${{ steps.build-push.outputs.digest }}\`" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "### Tags" >> $GITHUB_STEP_SUMMARY + echo "\`\`\`" >> $GITHUB_STEP_SUMMARY + echo "${{ steps.meta.outputs.tags }}" >> $GITHUB_STEP_SUMMARY + echo "\`\`\`" >> $GITHUB_STEP_SUMMARY + + - name: Notify on failure + if: failure() + uses: actions/github-script@v7 + with: + script: | + const variant = '${{ matrix.variant.name }}'; + const runUrl = `${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`; + + if (context.eventName === 'pull_request') { + github.rest.issues.createComment({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + body: `❌ MongoDB Community image build failed for **${variant}** variant.\n\n[View workflow run](${runUrl})` + }); + } + + core.setFailed(`Build failed for ${variant} variant`); + + verify-images: + name: Verify Published Images + needs: build-and-push + runs-on: ubuntu-latest + if: github.event_name != 'pull_request' + + steps: + - name: Log in to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Verify standard image + run: | + echo "Verifying standard image..." + docker pull ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ env.MONGODB_VERSION }} + docker inspect ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ env.MONGODB_VERSION }} + echo "✓ Standard image verified" + + - name: Verify k8s image + run: | + echo "Verifying k8s image..." + docker pull ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ env.MONGODB_VERSION }}-k8s + docker inspect ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ env.MONGODB_VERSION }}-k8s + + # Verify healthcheck.sh exists in k8s image + docker run --rm ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ env.MONGODB_VERSION }}-k8s test -x /healthcheck.sh + echo "✓ K8s image verified with healthcheck.sh" + + - name: Verify multi-architecture support + run: | + echo "Verifying multi-architecture manifests..." + docker buildx imagetools inspect ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ env.MONGODB_VERSION }} + docker buildx imagetools inspect ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ env.MONGODB_VERSION }}-k8s + echo "✓ Multi-architecture support verified" + + - name: Generate verification summary + run: | + echo "## Image Verification Summary" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "✅ All images verified successfully" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "**Registry:** \`${{ env.REGISTRY }}\`" >> $GITHUB_STEP_SUMMARY + echo "**Repository:** \`${{ env.IMAGE_NAME }}\`" >> $GITHUB_STEP_SUMMARY + echo "**Version:** ${{ env.MONGODB_VERSION }}" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "### Available Images" >> $GITHUB_STEP_SUMMARY + echo "- \`${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ env.MONGODB_VERSION }}\`" >> $GITHUB_STEP_SUMMARY + echo "- \`${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ env.MONGODB_VERSION }}-k8s\`" >> $GITHUB_STEP_SUMMARY + echo "- \`${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:8.0\`" >> $GITHUB_STEP_SUMMARY + echo "- \`${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:8.0-k8s\`" >> $GITHUB_STEP_SUMMARY + echo "- \`${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest\`" >> $GITHUB_STEP_SUMMARY + echo "- \`${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest-k8s\`" >> $GITHUB_STEP_SUMMARY diff --git a/percona-server-mongodb-8.0-community/.trivyignore b/percona-server-mongodb-8.0-community/.trivyignore new file mode 100644 index 00000000..da2041ff --- /dev/null +++ b/percona-server-mongodb-8.0-community/.trivyignore @@ -0,0 +1,96 @@ +# Trivy Ignore File for MongoDB Community Edition 8.0 +# +# These vulnerabilities are in the MongoDB database tools (Go binaries) +# provided by MongoDB Inc. in the mongodb-org-tools package. +# +# MongoDB 8.0.17 includes older database tools that are still compiled +# with Go 1.24.0, which has known vulnerabilities. These are upstream +# issues that require MongoDB to recompile the tools with a patched +# Go version (1.24.11+ or 1.25.5+). +# +# Affected binaries: +# - bsondump, mongodump, mongoexport, mongofiles +# - mongoimport, mongorestore, mongostat, mongotop +# +# MongoDB Version: 8.0.17 +# Database Tools Version: Bundled with 8.0.17 +# Go Version (in tools): 1.24.0 +# +# Status: Waiting for MongoDB to recompile tools with Go 1.24.11+ or 1.25.5+ +# Last Updated: 2026-01-20 +# Review Date: Check monthly for MongoDB updates + +# CVE-2025-22874: crypto/x509 - ExtKeyUsageAny disables policy validation +# Severity: HIGH +# Fixed in: Go 1.24.4+ +# Component: stdlib in MongoDB database tools +CVE-2025-22874 + +# CVE-2025-47907: database/sql - Postgres Scan Race Condition +# Severity: HIGH +# Fixed in: Go 1.23.12, 1.24.6+ +# Component: stdlib in MongoDB database tools +CVE-2025-47907 + +# CVE-2025-58183: archive/tar - Unbounded allocation when parsing GNU sparse map +# Severity: HIGH +# Fixed in: Go 1.24.8, 1.25.2+ +# Component: stdlib in MongoDB database tools +CVE-2025-58183 + +# CVE-2025-61729: crypto/x509 - Denial of Service due to excessive resource consumption +# Severity: HIGH +# Fixed in: Go 1.24.11, 1.25.5+ +# Component: stdlib in MongoDB database tools +CVE-2025-61729 + +# ============================================================================ +# GOSU VULNERABILITIES (Kubernetes image only) +# ============================================================================ +# These vulnerabilities are in gosu 1.17, which is compiled with Go 1.18.2. +# Gosu is used in the Kubernetes image for privilege dropping. +# +# Gosu Version: 1.17 +# Go Version (in gosu): 1.18.2 +# +# Status: These are old CVEs in an older version of gosu. Consider upgrading +# to a newer gosu version or accepting the risk for this use case. +# Last Updated: 2026-01-20 + +# Multiple CVEs in gosu (Go 1.18.2) - CRITICAL severity +CVE-2023-24538 +CVE-2023-24540 +CVE-2024-24790 + +# Multiple CVEs in gosu (Go 1.18.2) - HIGH severity +CVE-2022-27664 +CVE-2022-28131 +CVE-2022-2879 +CVE-2022-2880 +CVE-2022-29804 +CVE-2022-30580 +CVE-2022-30630 +CVE-2022-30631 +CVE-2022-30632 +CVE-2022-30633 +CVE-2022-30634 +CVE-2022-30635 +CVE-2022-32189 +CVE-2022-41715 +CVE-2022-41716 +CVE-2022-41720 +CVE-2022-41722 +CVE-2022-41723 +CVE-2022-41724 +CVE-2022-41725 +CVE-2023-24534 +CVE-2023-24536 +CVE-2023-24537 +CVE-2023-24539 +CVE-2023-29400 +CVE-2023-29403 +CVE-2023-39325 +CVE-2023-45283 +CVE-2023-45287 +CVE-2023-45288 +CVE-2024-34156 diff --git a/percona-server-mongodb-8.0-community/Dockerfile b/percona-server-mongodb-8.0-community/Dockerfile new file mode 100644 index 00000000..f828fbf4 --- /dev/null +++ b/percona-server-mongodb-8.0-community/Dockerfile @@ -0,0 +1,78 @@ +FROM redhat/ubi9-minimal + +LABEL name="MongoDB Community Edition" \ + release="8.0" \ + vendor="MongoDB Inc." \ + summary="MongoDB Community Edition with Percona operator compatibility" \ + description="MongoDB Community Edition configured for use with Percona MongoDB operator" \ + maintainer="Percona Development " +LABEL org.opencontainers.image.authors="info@percona.com" + +ENV PSMDB_VERSION=8.0.17 +ENV OS_VER=el9 + +# Do not report during Docker image creation. +ARG PERCONA_TELEMETRY_DISABLE=1 + +COPY mongodb.repo /etc/yum.repos.d/mongodb.repo + +RUN set -ex; \ + microdnf -y update; \ + microdnf -y install \ + mongodb-org-${PSMDB_VERSION} \ + mongodb-org-database-${PSMDB_VERSION} \ + mongodb-org-server-${PSMDB_VERSION} \ + mongodb-mongosh \ + mongodb-org-mongos-${PSMDB_VERSION} \ + mongodb-org-tools-${PSMDB_VERSION} \ + numactl-libs \ + procps-ng \ + jq \ + tar \ + oniguruma \ + cyrus-sasl-gssapi \ + cyrus-sasl-plain; \ + microdnf clean all; \ + rm -rf /var/cache/yum /data/db && mkdir -p /data/db; \ + chown -R 1001:0 /data/db + +# the numeric UID is needed for OpenShift +RUN useradd -u 1001 -r -g 0 -m -s /sbin/nologin \ + -c "Default Application User" mongodb; \ + mkdir -p /var/log/mongo /etc/mongodb; \ + chmod g+rwx /var/log/mongo /etc/mongodb; \ + chown :0 /var/log/mongo /etc/mongodb + +COPY LICENSE /licenses/LICENSE.Dockerfile +RUN cp /usr/share/doc/mongodb-org-server/LICENSE-Community.txt /licenses/LICENSE.MongoDB-Community || \ + echo "MongoDB Community License" > /licenses/LICENSE.MongoDB-Community + +ENV GOSU_VERSION=1.11 +RUN set -eux; \ + curl -Lf -o /usr/bin/gosu https://github.com/tianon/gosu/releases/download/${GOSU_VERSION}/gosu-amd64; \ + curl -Lf -o /usr/bin/gosu.asc https://github.com/tianon/gosu/releases/download/${GOSU_VERSION}/gosu-amd64.asc; \ + \ + export GNUPGHOME="$(mktemp -d)"; \ + gpg --batch --keyserver hkps://keys.openpgp.org --recv-keys B42F6819007F00F88E364FD4036A9C25BF357DD4; \ + gpg --batch --verify /usr/bin/gosu.asc /usr/bin/gosu; \ + rm -rf "$GNUPGHOME" /usr/bin/gosu.asc; \ + \ + chmod +x /usr/bin/gosu; \ + curl -f -o /licenses/LICENSE.gosu https://raw.githubusercontent.com/tianon/gosu/${GOSU_VERSION}/LICENSE + +VOLUME ["/data/db"] + +RUN set -ex; \ + curl -fSL https://cdnjs.cloudflare.com/ajax/libs/js-yaml/4.1.0/js-yaml.min.js -o /js-yaml.js; \ + echo "45dc3dd03dc07a06705a2c2989b8c7f709013f04bd5386e3279d4e447f07ebd7 /js-yaml.js" | sha256sum -c - + +COPY ps-entry.sh /entrypoint.sh +RUN chmod +x /entrypoint.sh + +ENTRYPOINT ["/entrypoint.sh"] + +EXPOSE 27017 + +USER 1001 + +CMD ["mongod"] \ No newline at end of file diff --git a/percona-server-mongodb-8.0-community/Dockerfile.k8s b/percona-server-mongodb-8.0-community/Dockerfile.k8s new file mode 100644 index 00000000..5df30e56 --- /dev/null +++ b/percona-server-mongodb-8.0-community/Dockerfile.k8s @@ -0,0 +1,198 @@ +# Multi-architecture MongoDB Community Edition for Kubernetes +# This Dockerfile is optimized for Kubernetes deployments with multi-arch support + +ARG TARGETARCH +ARG TARGETOS +ARG TARGETPLATFORM + +FROM --platform=${TARGETPLATFORM} redhat/ubi9-minimal + +# Kubernetes and OpenShift compatibility labels +LABEL name="MongoDB Community Edition for Kubernetes" \ + release="8.0" \ + version="8.0.17" \ + vendor="MongoDB Inc." \ + summary="MongoDB Community Edition optimized for Kubernetes with Percona operator compatibility" \ + description="Multi-architecture MongoDB Community Edition container optimized for Kubernetes deployments. Includes enhanced security, health checks, and operator integration." \ + maintainer="Percona Development " \ + io.k8s.description="MongoDB Community Edition for Kubernetes" \ + io.k8s.display-name="MongoDB Community 8.0" \ + io.openshift.expose-services="27017:mongodb" \ + io.openshift.tags="database,mongodb,nosql" + +LABEL org.opencontainers.image.title="MongoDB Community Edition for Kubernetes" \ + org.opencontainers.image.description="Multi-architecture MongoDB Community Edition optimized for Kubernetes" \ + org.opencontainers.image.version="8.0.17" \ + org.opencontainers.image.authors="info@percona.com" \ + org.opencontainers.image.vendor="Percona" \ + org.opencontainers.image.licenses="SSPL-1.0" \ + org.opencontainers.image.source="https://github.com/percona/percona-docker" \ + org.opencontainers.image.documentation="https://docs.percona.com/" + +# Environment variables for MongoDB and Kubernetes +ENV PSMDB_VERSION=8.0.17 \ + OS_VER=el9 \ + MONGODB_VERSION=8.0 \ + GOSU_VERSION=1.17 \ + PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin + +# Kubernetes-specific environment variables +ENV MONGODB_DATA_DIR=/data/db \ + MONGODB_LOG_DIR=/var/log/mongodb \ + MONGODB_CONFIG_DIR=/etc/mongodb \ + MONGODB_USER=mongodb \ + MONGODB_UID=1001 \ + MONGODB_GID=0 + +# Security and telemetry settings +ARG PERCONA_TELEMETRY_DISABLE=1 +ENV PERCONA_TELEMETRY_DISABLE=${PERCONA_TELEMETRY_DISABLE} + +# Copy repository configuration with architecture detection +COPY mongodb.repo /etc/yum.repos.d/mongodb.repo + +# Install MongoDB and dependencies with multi-arch support +RUN set -ex; \ + # Update system packages + microdnf -y update; \ + \ + # Install required system packages + microdnf -y install \ + shadow-utils \ + findutils \ + tar \ + gzip \ + procps-ng; \ + \ + # Install MongoDB packages + microdnf -y install \ + mongodb-org-${PSMDB_VERSION} \ + mongodb-org-database-${PSMDB_VERSION} \ + mongodb-org-server-${PSMDB_VERSION} \ + mongodb-mongosh \ + mongodb-org-mongos-${PSMDB_VERSION} \ + mongodb-org-tools-${PSMDB_VERSION} \ + numactl-libs \ + jq \ + oniguruma \ + cyrus-sasl-gssapi \ + cyrus-sasl-plain; \ + \ + # Clean up package cache + microdnf clean all; \ + rm -rf /var/cache/yum; \ + \ + # Create necessary directories with proper permissions + mkdir -p ${MONGODB_DATA_DIR} ${MONGODB_LOG_DIR} ${MONGODB_CONFIG_DIR} /tmp/mongodb; \ + \ + # Set up directory permissions for Kubernetes/OpenShift + chown -R ${MONGODB_UID}:${MONGODB_GID} ${MONGODB_DATA_DIR} ${MONGODB_LOG_DIR} ${MONGODB_CONFIG_DIR} /tmp/mongodb; \ + chmod -R g+rwx ${MONGODB_DATA_DIR} ${MONGODB_LOG_DIR} ${MONGODB_CONFIG_DIR} /tmp/mongodb; \ + chmod -R o-rwx ${MONGODB_DATA_DIR} ${MONGODB_LOG_DIR} ${MONGODB_CONFIG_DIR} + +# Create MongoDB user with Kubernetes/OpenShift compatibility +RUN set -ex; \ + # Create mongodb user with specific UID for Kubernetes + useradd -u ${MONGODB_UID} -r -g ${MONGODB_GID} -m -s /sbin/nologin \ + -c "MongoDB Application User" -d /home/mongodb ${MONGODB_USER}; \ + \ + # Ensure proper ownership and permissions + chown -R ${MONGODB_UID}:${MONGODB_GID} /home/mongodb; \ + chmod -R g+rwx /home/mongodb + +# Install gosu for privilege dropping with multi-arch support +RUN set -eux; \ + ARCH=$(uname -m); \ + case "$ARCH" in \ + x86_64) GOSU_ARCH="amd64" ;; \ + aarch64) GOSU_ARCH="arm64" ;; \ + *) echo "Unsupported architecture for gosu: $ARCH" && exit 1 ;; \ + esac; \ + \ + curl -fsSL -o /usr/bin/gosu "https://github.com/tianon/gosu/releases/download/${GOSU_VERSION}/gosu-${GOSU_ARCH}"; \ + curl -fsSL -o /usr/bin/gosu.asc "https://github.com/tianon/gosu/releases/download/${GOSU_VERSION}/gosu-${GOSU_ARCH}.asc"; \ + \ + export GNUPGHOME="$(mktemp -d)"; \ + gpg --batch --keyserver hkps://keys.openpgp.org --recv-keys B42F6819007F00F88E364FD4036A9C25BF357DD4; \ + gpg --batch --verify /usr/bin/gosu.asc /usr/bin/gosu; \ + rm -rf "$GNUPGHOME" /usr/bin/gosu.asc; \ + \ + chmod +x /usr/bin/gosu; \ + gosu --version; \ + gosu nobody true + +# Install js-yaml for configuration parsing +RUN set -ex; \ + curl -fsSL https://cdnjs.cloudflare.com/ajax/libs/js-yaml/4.1.0/js-yaml.min.js -o /js-yaml.js; \ + echo "45dc3dd03dc07a06705a2c2989b8c7f709013f04bd5386e3279d4e447f07ebd7 /js-yaml.js" | sha256sum -c -; \ + chmod 644 /js-yaml.js + +# Copy license files +COPY LICENSE /licenses/LICENSE.Dockerfile +RUN set -ex; \ + mkdir -p /licenses; \ + cp /usr/share/doc/mongodb-org-server/LICENSE-Community.txt /licenses/LICENSE.MongoDB-Community 2>/dev/null || \ + echo "MongoDB Community License (SSPL-1.0)" > /licenses/LICENSE.MongoDB-Community; \ + curl -fsSL -o /licenses/LICENSE.gosu "https://raw.githubusercontent.com/tianon/gosu/${GOSU_VERSION}/LICENSE"; \ + chmod 644 /licenses/* + +# Copy entrypoint and health check scripts +COPY ps-entry.sh /entrypoint.sh +COPY healthcheck.sh /healthcheck.sh + +# Set up scripts with proper permissions +RUN set -ex; \ + chmod +x /entrypoint.sh /healthcheck.sh; \ + chown ${MONGODB_UID}:${MONGODB_GID} /entrypoint.sh /healthcheck.sh + +# Create MongoDB configuration template +RUN set -ex; \ + cat > ${MONGODB_CONFIG_DIR}/mongod.conf.template << 'EOF' +# MongoDB configuration file template for Kubernetes +storage: + dbPath: /data/db + wiredTiger: + engineConfig: + cacheSizeGB: 0.25 + +systemLog: + destination: file + logAppend: true + path: /var/log/mongodb/mongod.log + logRotate: reopen + +net: + port: 27017 + bindIpAll: true + +processManagement: + timeZoneInfo: /usr/share/zoneinfo + +security: + authorization: disabled + +replication: + replSetName: rs0 + +operationProfiling: + slowOpThresholdMs: 100 +EOF + +# Set up health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=40s --retries=3 \ + CMD /healthcheck.sh + +# Kubernetes-specific volume declarations +VOLUME ["${MONGODB_DATA_DIR}", "${MONGODB_LOG_DIR}"] + +# Expose MongoDB port +EXPOSE 27017 + +# Use entrypoint for MongoDB initialization +ENTRYPOINT ["/entrypoint.sh"] + +# Switch to non-root user +USER ${MONGODB_UID} + +# Default command +CMD ["mongod"] \ No newline at end of file diff --git a/percona-server-mongodb-8.0-community/LICENSE b/percona-server-mongodb-8.0-community/LICENSE new file mode 100644 index 00000000..0ae7afb4 --- /dev/null +++ b/percona-server-mongodb-8.0-community/LICENSE @@ -0,0 +1,7 @@ +MongoDB Community Edition License + +This software is licensed under the Server Side Public License (SSPL) v1. +For more information, see: https://www.mongodb.com/licensing/server-side-public-license + +This Docker image contains MongoDB Community Edition binaries and is configured +for use with the Percona MongoDB Operator for Kubernetes. \ No newline at end of file diff --git a/percona-server-mongodb-8.0-community/healthcheck.sh b/percona-server-mongodb-8.0-community/healthcheck.sh new file mode 100644 index 00000000..63edfa77 --- /dev/null +++ b/percona-server-mongodb-8.0-community/healthcheck.sh @@ -0,0 +1,236 @@ +#!/bin/bash +# MongoDB Health Check Script for Kubernetes + +set -euo pipefail + +# Configuration +MONGODB_HOST="${MONGODB_HOST:-localhost}" +MONGODB_PORT="${MONGODB_PORT:-27017}" +MONGODB_DATABASE="${MONGODB_DATABASE:-admin}" +TIMEOUT="${HEALTH_CHECK_TIMEOUT:-10}" +MAX_RETRIES="${HEALTH_CHECK_RETRIES:-3}" + +# Logging function +log() { + echo "[$(date +'%Y-%m-%d %H:%M:%S')] HEALTHCHECK: $*" >&2 +} + +# Function to check if MongoDB is accepting connections +check_connection() { + local retry=0 + while [ $retry -lt $MAX_RETRIES ]; do + if timeout $TIMEOUT mongosh --host "$MONGODB_HOST" --port "$MONGODB_PORT" --quiet --eval "db.adminCommand('ping')" >/dev/null 2>&1; then + return 0 + fi + retry=$((retry + 1)) + log "Connection attempt $retry failed, retrying..." + sleep 1 + done + return 1 +} + +# Function to check MongoDB server status +check_server_status() { + local status_output + status_output=$(timeout $TIMEOUT mongosh --host "$MONGODB_HOST" --port "$MONGODB_PORT" --quiet --eval " + try { + var status = db.adminCommand('serverStatus'); + if (status.ok === 1) { + print('OK'); + } else { + print('ERROR: Server status not OK'); + } + } catch (e) { + print('ERROR: ' + e.message); + } + " 2>/dev/null) + + if [[ "$status_output" == "OK" ]]; then + return 0 + else + log "Server status check failed: $status_output" + return 1 + fi +} + +# Function to check if MongoDB is ready for read/write operations +check_readiness() { + local readiness_output + readiness_output=$(timeout $TIMEOUT mongosh --host "$MONGODB_HOST" --port "$MONGODB_PORT" --quiet --eval " + try { + // Check if we can perform basic operations + db.healthcheck.insertOne({timestamp: new Date(), check: 'readiness'}); + db.healthcheck.findOne({check: 'readiness'}); + db.healthcheck.deleteMany({check: 'readiness'}); + print('READY'); + } catch (e) { + print('NOT_READY: ' + e.message); + } + " 2>/dev/null) + + if [[ "$readiness_output" == "READY" ]]; then + return 0 + else + log "Readiness check failed: $readiness_output" + return 1 + fi +} + +# Function to check replica set status (if applicable) +check_replica_set() { + local rs_output + rs_output=$(timeout $TIMEOUT mongosh --host "$MONGODB_HOST" --port "$MONGODB_PORT" --quiet --eval " + try { + var status = rs.status(); + if (status.ok === 1) { + var myState = status.members.find(m => m.self === true); + if (myState && (myState.state === 1 || myState.state === 2)) { + print('RS_OK'); + } else { + print('RS_NOT_READY: State ' + (myState ? myState.state : 'unknown')); + } + } else { + print('RS_ERROR: ' + status.errmsg); + } + } catch (e) { + // Not a replica set or not initialized yet + print('RS_NOT_CONFIGURED'); + } + " 2>/dev/null) + + case "$rs_output" in + "RS_OK") + log "Replica set status: OK" + return 0 + ;; + "RS_NOT_CONFIGURED") + log "Replica set not configured (standalone mode)" + return 0 + ;; + *) + log "Replica set check failed: $rs_output" + return 1 + ;; + esac +} + +# Function to check disk space +check_disk_space() { + local data_dir="${MONGODB_DATA_DIR:-/data/db}" + local log_dir="${MONGODB_LOG_DIR:-/var/log/mongodb}" + local min_free_percent="${MIN_FREE_DISK_PERCENT:-10}" + + for dir in "$data_dir" "$log_dir"; do + if [[ -d "$dir" ]]; then + local usage + usage=$(df "$dir" | awk 'NR==2 {print $5}' | sed 's/%//') + local free_percent=$((100 - usage)) + + if [[ $free_percent -lt $min_free_percent ]]; then + log "Disk space warning: $dir has only $free_percent% free (minimum: $min_free_percent%)" + return 1 + fi + fi + done + return 0 +} + +# Function to check process health +check_process_health() { + # Check if mongod process is running + if ! pgrep -f mongod >/dev/null 2>&1; then + log "MongoDB process not found" + return 1 + fi + + # Check if process is responsive (not in uninterruptible sleep) + local mongod_pid + mongod_pid=$(pgrep -f mongod | head -1) + if [[ -n "$mongod_pid" ]]; then + local process_state + process_state=$(ps -o state= -p "$mongod_pid" 2>/dev/null | tr -d ' ') + if [[ "$process_state" == "D" ]]; then + log "MongoDB process is in uninterruptible sleep state" + return 1 + fi + fi + + return 0 +} + +# Main health check function +main() { + local exit_code=0 + local check_type="${1:-full}" + + log "Starting health check (type: $check_type)" + + # Always check process health first + if ! check_process_health; then + log "Process health check failed" + exit_code=1 + fi + + # Check MongoDB connection + if ! check_connection; then + log "Connection check failed" + exit_code=1 + fi + + # For full health checks, perform additional tests + if [[ "$check_type" == "full" ]]; then + # Check server status + if ! check_server_status; then + log "Server status check failed" + exit_code=1 + fi + + # Check readiness for operations + if ! check_readiness; then + log "Readiness check failed" + exit_code=1 + fi + + # Check replica set status if applicable + if ! check_replica_set; then + log "Replica set check failed" + exit_code=1 + fi + + # Check disk space + if ! check_disk_space; then + log "Disk space check failed" + exit_code=1 + fi + fi + + if [[ $exit_code -eq 0 ]]; then + log "Health check passed" + else + log "Health check failed" + fi + + exit $exit_code +} + +# Handle different invocation modes +case "${1:-full}" in + "liveness"|"live") + # Liveness probe - basic connection check + main "basic" + ;; + "readiness"|"ready") + # Readiness probe - full functionality check + main "full" + ;; + "startup") + # Startup probe - basic connection with retries + MAX_RETRIES=10 + TIMEOUT=30 + main "basic" + ;; + *) + # Default full health check + main "full" + ;; +esac diff --git a/percona-server-mongodb-8.0-community/k8s-examples/mongodb-deployment.yaml b/percona-server-mongodb-8.0-community/k8s-examples/mongodb-deployment.yaml new file mode 100644 index 00000000..c4ab31bb --- /dev/null +++ b/percona-server-mongodb-8.0-community/k8s-examples/mongodb-deployment.yaml @@ -0,0 +1,231 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: mongodb + labels: + name: mongodb +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: mongodb-config + namespace: mongodb +data: + mongod.conf: | + # MongoDB configuration for Kubernetes + storage: + dbPath: /data/db + journal: + enabled: true + wiredTiger: + engineConfig: + cacheSizeGB: 1.0 + + systemLog: + destination: file + logAppend: true + path: /var/log/mongodb/mongod.log + logRotate: reopen + verbosity: 1 + + net: + port: 27017 + bindIpAll: true + maxIncomingConnections: 1000 + + processManagement: + timeZoneInfo: /usr/share/zoneinfo + + security: + authorization: enabled + + replication: + replSetName: rs0 + + operationProfiling: + slowOpThresholdMs: 100 + mode: slowOp +--- +apiVersion: v1 +kind: Secret +metadata: + name: mongodb-secret + namespace: mongodb +type: Opaque +data: + # echo -n 'admin' | base64 + username: YWRtaW4= + # echo -n 'SecurePassword123!' | base64 + password: U2VjdXJlUGFzc3dvcmQxMjMh +--- +apiVersion: v1 +kind: Service +metadata: + name: mongodb-service + namespace: mongodb + labels: + app: mongodb +spec: + type: ClusterIP + ports: + - port: 27017 + targetPort: 27017 + protocol: TCP + name: mongodb + selector: + app: mongodb +--- +apiVersion: v1 +kind: Service +metadata: + name: mongodb-headless + namespace: mongodb + labels: + app: mongodb +spec: + type: ClusterIP + clusterIP: None + ports: + - port: 27017 + targetPort: 27017 + protocol: TCP + name: mongodb + selector: + app: mongodb +--- +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: mongodb + namespace: mongodb + labels: + app: mongodb +spec: + serviceName: mongodb-headless + replicas: 3 + selector: + matchLabels: + app: mongodb + template: + metadata: + labels: + app: mongodb + spec: + securityContext: + runAsUser: 1001 + runAsGroup: 0 + fsGroup: 0 + runAsNonRoot: true + containers: + - name: mongodb + image: percona/percona-server-mongodb:8.0-community-k8s + imagePullPolicy: IfNotPresent + ports: + - containerPort: 27017 + name: mongodb + protocol: TCP + env: + - name: MONGO_INITDB_ROOT_USERNAME_FILE + value: /etc/mongodb-secret/username + - name: MONGO_INITDB_ROOT_PASSWORD_FILE + value: /etc/mongodb-secret/password + - name: MONGO_INITDB_DATABASE + value: admin + - name: REPLICA_SET_NAME + value: rs0 + - name: K8S_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: K8S_POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: K8S_SERVICE_NAME + value: mongodb-headless + - name: MONGODB_DATA_DIR + value: /data/db + - name: MONGODB_LOG_DIR + value: /var/log/mongodb + - name: MONGODB_CONFIG_DIR + value: /etc/mongodb + resources: + requests: + memory: "512Mi" + cpu: "250m" + limits: + memory: "2Gi" + cpu: "1000m" + volumeMounts: + - name: mongodb-data + mountPath: /data/db + - name: mongodb-logs + mountPath: /var/log/mongodb + - name: mongodb-config + mountPath: /etc/mongodb/mongod.conf.template + subPath: mongod.conf + readOnly: true + - name: mongodb-secret + mountPath: /etc/mongodb-secret + readOnly: true + livenessProbe: + exec: + command: + - /healthcheck.sh + - liveness + initialDelaySeconds: 30 + periodSeconds: 30 + timeoutSeconds: 10 + failureThreshold: 3 + successThreshold: 1 + readinessProbe: + exec: + command: + - /healthcheck.sh + - readiness + initialDelaySeconds: 10 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 3 + successThreshold: 1 + startupProbe: + exec: + command: + - /healthcheck.sh + - startup + initialDelaySeconds: 10 + periodSeconds: 10 + timeoutSeconds: 30 + failureThreshold: 30 + successThreshold: 1 + securityContext: + allowPrivilegeEscalation: false + readOnlyRootFilesystem: false + runAsNonRoot: true + runAsUser: 1001 + runAsGroup: 0 + capabilities: + drop: + - ALL + volumes: + - name: mongodb-config + configMap: + name: mongodb-config + defaultMode: 0644 + - name: mongodb-secret + secret: + secretName: mongodb-secret + defaultMode: 0400 + - name: mongodb-logs + emptyDir: {} + terminationGracePeriodSeconds: 30 + restartPolicy: Always + volumeClaimTemplates: + - metadata: + name: mongodb-data + spec: + accessModes: ["ReadWriteOnce"] + storageClassName: "standard" + resources: + requests: + storage: 10Gi \ No newline at end of file diff --git a/percona-server-mongodb-8.0-community/k8s-examples/mongodb-simple.yaml b/percona-server-mongodb-8.0-community/k8s-examples/mongodb-simple.yaml new file mode 100644 index 00000000..4ab29401 --- /dev/null +++ b/percona-server-mongodb-8.0-community/k8s-examples/mongodb-simple.yaml @@ -0,0 +1,158 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: mongodb-simple +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: mongodb-config + namespace: mongodb-simple +data: + mongod.conf: | + # Simple MongoDB configuration for Kubernetes + storage: + dbPath: /data/db + journal: + enabled: true + + systemLog: + destination: file + logAppend: true + path: /var/log/mongodb/mongod.log + + net: + port: 27017 + bindIpAll: true + + processManagement: + timeZoneInfo: /usr/share/zoneinfo + + security: + authorization: disabled +--- +apiVersion: v1 +kind: Service +metadata: + name: mongodb + namespace: mongodb-simple + labels: + app: mongodb +spec: + type: ClusterIP + ports: + - port: 27017 + targetPort: 27017 + protocol: TCP + name: mongodb + selector: + app: mongodb +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: mongodb + namespace: mongodb-simple + labels: + app: mongodb +spec: + replicas: 1 + selector: + matchLabels: + app: mongodb + template: + metadata: + labels: + app: mongodb + spec: + securityContext: + runAsUser: 1001 + runAsGroup: 0 + fsGroup: 0 + runAsNonRoot: true + containers: + - name: mongodb + image: percona/percona-server-mongodb:8.0-community-k8s + imagePullPolicy: IfNotPresent + ports: + - containerPort: 27017 + name: mongodb + protocol: TCP + env: + - name: K8S_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: K8S_POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: MONGODB_DATA_DIR + value: /data/db + - name: MONGODB_LOG_DIR + value: /var/log/mongodb + - name: MONGODB_CONFIG_DIR + value: /etc/mongodb + resources: + requests: + memory: "256Mi" + cpu: "100m" + limits: + memory: "1Gi" + cpu: "500m" + volumeMounts: + - name: mongodb-data + mountPath: /data/db + - name: mongodb-logs + mountPath: /var/log/mongodb + - name: mongodb-config + mountPath: /etc/mongodb/mongod.conf.template + subPath: mongod.conf + readOnly: true + livenessProbe: + exec: + command: + - /healthcheck.sh + - liveness + initialDelaySeconds: 30 + periodSeconds: 30 + timeoutSeconds: 10 + failureThreshold: 3 + readinessProbe: + exec: + command: + - /healthcheck.sh + - readiness + initialDelaySeconds: 10 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 3 + startupProbe: + exec: + command: + - /healthcheck.sh + - startup + initialDelaySeconds: 10 + periodSeconds: 10 + timeoutSeconds: 30 + failureThreshold: 30 + securityContext: + allowPrivilegeEscalation: false + readOnlyRootFilesystem: false + runAsNonRoot: true + runAsUser: 1001 + runAsGroup: 0 + capabilities: + drop: + - ALL + volumes: + - name: mongodb-data + emptyDir: {} + - name: mongodb-logs + emptyDir: {} + - name: mongodb-config + configMap: + name: mongodb-config + defaultMode: 0644 + terminationGracePeriodSeconds: 30 + restartPolicy: Always \ No newline at end of file diff --git a/percona-server-mongodb-8.0-community/mongodb.repo b/percona-server-mongodb-8.0-community/mongodb.repo new file mode 100644 index 00000000..235f6f68 --- /dev/null +++ b/percona-server-mongodb-8.0-community/mongodb.repo @@ -0,0 +1,10 @@ +[mongodb-org-8.0] +name=MongoDB Repository +baseurl=https://repo.mongodb.org/yum/redhat/9/mongodb-org/8.0/$basearch/ +gpgcheck=1 +enabled=1 +gpgkey=https://pgp.mongodb.com/server-8.0.asc +sslverify=1 +sslcacert=/etc/pki/tls/certs/ca-bundle.crt +metadata_expire=300 +repo_gpgcheck=1 diff --git a/percona-server-mongodb-8.0-community/ps-entry.sh b/percona-server-mongodb-8.0-community/ps-entry.sh new file mode 100644 index 00000000..735468ae --- /dev/null +++ b/percona-server-mongodb-8.0-community/ps-entry.sh @@ -0,0 +1,332 @@ +#!/bin/bash +# Enhanced MongoDB Entrypoint Script for Kubernetes +# Optimized for Kubernetes deployments with improved signal handling, configuration management, and operator integration + +set -Eeuo pipefail + +# Configuration variables +MONGODB_DATA_DIR="${MONGODB_DATA_DIR:-/data/db}" +MONGODB_LOG_DIR="${MONGODB_LOG_DIR:-/var/log/mongodb}" +MONGODB_CONFIG_DIR="${MONGODB_CONFIG_DIR:-/etc/mongodb}" +MONGODB_USER="${MONGODB_USER:-mongodb}" +MONGODB_UID="${MONGODB_UID:-1001}" +MONGODB_GID="${MONGODB_GID:-0}" + +# Kubernetes-specific variables +K8S_NAMESPACE="${K8S_NAMESPACE:-}" +K8S_POD_NAME="${K8S_POD_NAME:-}" +K8S_SERVICE_NAME="${K8S_SERVICE_NAME:-}" +REPLICA_SET_NAME="${REPLICA_SET_NAME:-rs0}" + +# Logging function +log() { + echo "[$(date +'%Y-%m-%d %H:%M:%S')] ENTRYPOINT: $*" >&2 +} + +# Error handling +error_exit() { + log "ERROR: $1" + exit 1 +} + +# Signal handling for graceful shutdown +shutdown_handler() { + log "Received shutdown signal, initiating graceful shutdown..." + + # If MongoDB is running, try to shut it down gracefully + if pgrep -f mongod >/dev/null 2>&1; then + log "Shutting down MongoDB gracefully..." + + # Try to use MongoDB's shutdown command first + if mongosh --quiet --eval "db.adminCommand('shutdown')" >/dev/null 2>&1; then + log "MongoDB shutdown command sent successfully" + else + log "MongoDB shutdown command failed, sending SIGTERM to process" + pkill -TERM mongod || true + fi + + # Wait for process to exit gracefully + local timeout=30 + while pgrep -f mongod >/dev/null 2>&1 && [ $timeout -gt 0 ]; do + sleep 1 + timeout=$((timeout - 1)) + done + + if pgrep -f mongod >/dev/null 2>&1; then + log "MongoDB did not shut down gracefully, sending SIGKILL" + pkill -KILL mongod || true + else + log "MongoDB shut down gracefully" + fi + fi + + exit 0 +} + +# Set up signal handlers +trap shutdown_handler SIGTERM SIGINT SIGQUIT + +# Function to setup directories and permissions +setup_directories() { + log "Setting up directories and permissions..." + + # Create necessary directories + for dir in "$MONGODB_DATA_DIR" "$MONGODB_LOG_DIR" "$MONGODB_CONFIG_DIR" "/tmp/mongodb"; do + if [[ ! -d "$dir" ]]; then + mkdir -p "$dir" + log "Created directory: $dir" + fi + done + + # Set ownership and permissions for Kubernetes/OpenShift compatibility + chown -R "$MONGODB_UID:$MONGODB_GID" "$MONGODB_DATA_DIR" "$MONGODB_LOG_DIR" "$MONGODB_CONFIG_DIR" "/tmp/mongodb" 2>/dev/null || true + chmod -R g+rwx "$MONGODB_DATA_DIR" "$MONGODB_LOG_DIR" "$MONGODB_CONFIG_DIR" "/tmp/mongodb" 2>/dev/null || true + chmod -R o-rwx "$MONGODB_DATA_DIR" "$MONGODB_LOG_DIR" "$MONGODB_CONFIG_DIR" 2>/dev/null || true +} + +# Function to handle file-based environment variables (Kubernetes secrets) +file_env() { + local var="$1" + local fileVar="${var}_FILE" + local def="${2:-}" + + if [[ -n "${!var:-}" ]] && [[ -n "${!fileVar:-}" ]]; then + error_exit "Both $var and $fileVar are set (but are exclusive)" + fi + + local val="$def" + if [[ -n "${!var:-}" ]]; then + val="${!var}" + elif [[ -n "${!fileVar:-}" ]]; then + if [[ -f "${!fileVar}" ]]; then + val="$(< "${!fileVar}")" + else + error_exit "File ${!fileVar} does not exist" + fi + fi + + export "$var"="$val" + unset "$fileVar" 2>/dev/null || true +} + +# Function to generate MongoDB configuration +generate_config() { + local config_file="$MONGODB_CONFIG_DIR/mongod.conf" + + log "Generating MongoDB configuration..." + + # Start with template if it exists + if [[ -f "$MONGODB_CONFIG_DIR/mongod.conf.template" ]]; then + cp "$MONGODB_CONFIG_DIR/mongod.conf.template" "$config_file" + else + # Create basic configuration + cat > "$config_file" << EOF +# MongoDB configuration for Kubernetes +storage: + dbPath: $MONGODB_DATA_DIR + journal: + enabled: true + +systemLog: + destination: file + logAppend: true + path: $MONGODB_LOG_DIR/mongod.log + logRotate: reopen + +net: + port: 27017 + bindIpAll: true + +processManagement: + timeZoneInfo: /usr/share/zoneinfo + +security: + authorization: disabled +EOF + fi + + # Add replica set configuration if specified + if [[ -n "$REPLICA_SET_NAME" ]]; then + if ! grep -q "replication:" "$config_file"; then + cat >> "$config_file" << EOF + +replication: + replSetName: $REPLICA_SET_NAME +EOF + fi + fi + + # Set proper permissions + chown "$MONGODB_UID:$MONGODB_GID" "$config_file" + chmod 640 "$config_file" + + log "MongoDB configuration generated at $config_file" +} + +# Function to wait for network readiness +wait_for_network() { + log "Waiting for network readiness..." + + local timeout=30 + while [ $timeout -gt 0 ]; do + if ss -tuln | grep -q ":27017 "; then + log "Port 27017 is already in use, waiting..." + sleep 1 + timeout=$((timeout - 1)) + else + break + fi + done + + if [ $timeout -eq 0 ]; then + log "Warning: Port 27017 may still be in use" + fi +} + +# Function to initialize MongoDB if needed +initialize_mongodb() { + log "Checking if MongoDB initialization is needed..." + + # Check if this is a fresh installation + if [[ ! -f "$MONGODB_DATA_DIR/WiredTiger" ]] && [[ ! -f "$MONGODB_DATA_DIR/mongod.lock" ]]; then + log "Fresh MongoDB installation detected" + + # Handle initialization scripts + file_env 'MONGO_INITDB_ROOT_USERNAME' + file_env 'MONGO_INITDB_ROOT_PASSWORD' + file_env 'MONGO_INITDB_DATABASE' + + if [[ -n "${MONGO_INITDB_ROOT_USERNAME:-}" ]] && [[ -n "${MONGO_INITDB_ROOT_PASSWORD:-}" ]]; then + log "Root user credentials provided, will initialize with authentication" + export MONGO_INITDB_ROOT_USERNAME + export MONGO_INITDB_ROOT_PASSWORD + export MONGO_INITDB_DATABASE="${MONGO_INITDB_DATABASE:-admin}" + fi + else + log "Existing MongoDB data found, skipping initialization" + fi +} + +# Function to setup MongoDB arguments with Kubernetes optimizations +setup_mongodb_args() { + local -a mongod_args=("$@") + + # If no arguments provided, use default + if [[ ${#mongod_args[@]} -eq 0 ]]; then + mongod_args=("mongod") + fi + + # If first argument starts with -, prepend mongod + if [[ "${mongod_args[0]:0:1}" = '-' ]]; then + mongod_args=("mongod" "${mongod_args[@]}") + fi + + # Add Kubernetes-specific optimizations + local -a k8s_args=() + + # Use configuration file if it exists + if [[ -f "$MONGODB_CONFIG_DIR/mongod.conf" ]]; then + k8s_args+=("--config" "$MONGODB_CONFIG_DIR/mongod.conf") + fi + + # Kubernetes-specific settings + k8s_args+=( + "--bind_ip_all" + "--logpath" "$MONGODB_LOG_DIR/mongod.log" + "--logappend" + ) + + # Add NUMA optimization if available + if command -v numactl >/dev/null 2>&1 && numactl --hardware >/dev/null 2>&1; then + mongod_args=("numactl" "--interleave=all" "${mongod_args[@]}") + fi + + # Combine arguments + mongod_args+=("${k8s_args[@]}") + + echo "${mongod_args[@]}" +} + +# Function to start MongoDB with proper user switching +start_mongodb() { + local -a mongod_cmd=($@) + + log "Starting MongoDB with command: ${mongod_cmd[*]}" + + # Ensure we're running as the correct user + if [[ "$(id -u)" = '0' ]]; then + # Running as root, switch to mongodb user + log "Running as root, switching to user $MONGODB_USER (UID: $MONGODB_UID)" + + # Ensure ownership of critical files + chown "$MONGODB_UID:$MONGODB_GID" "$MONGODB_DATA_DIR" "$MONGODB_LOG_DIR" 2>/dev/null || true + + # Use gosu to switch user and exec + exec gosu "$MONGODB_UID:$MONGODB_GID" "${mongod_cmd[@]}" + else + # Already running as non-root user + log "Running as user $(id -un) (UID: $(id -u))" + exec "${mongod_cmd[@]}" + fi +} + +# Function to check if command is MongoDB-related +is_mongodb_command() { + local cmd="$1" + + # List of MongoDB-related commands + case "$cmd" in + mongod|mongos|mongo|mongosh) + return 0 + ;; + *) + return 1 + ;; + esac +} + +# Main execution function +main() { + # Check if we should run MongoDB initialization or just exec the command + local first_arg="${1:-mongod}" + + # If the first argument is not a MongoDB command, just exec it directly + # This allows for command overrides like: docker run image echo "hello" + if ! is_mongodb_command "$first_arg"; then + log "Non-MongoDB command detected: $first_arg" + log "Executing command directly without MongoDB initialization" + + # If running as root, switch to mongodb user for consistency + if [[ "$(id -u)" = '0' ]]; then + exec gosu "$MONGODB_UID:$MONGODB_GID" "$@" + else + exec "$@" + fi + fi + + # MongoDB command detected, proceed with full initialization + log "Starting MongoDB entrypoint for Kubernetes..." + log "Pod: ${K8S_POD_NAME:-unknown}, Namespace: ${K8S_NAMESPACE:-unknown}" + + # Setup directories and permissions + setup_directories + + # Wait for network readiness + wait_for_network + + # Generate configuration + generate_config + + # Initialize MongoDB if needed + initialize_mongodb + + # Setup MongoDB arguments + local -a final_args + IFS=' ' read -ra final_args <<< "$(setup_mongodb_args "$@")" + + # Start MongoDB + start_mongodb "${final_args[@]}" +} + +# Execute main function with all arguments +main "$@" \ No newline at end of file diff --git a/percona-server-mongodb-8.2-community/.trivyignore b/percona-server-mongodb-8.2-community/.trivyignore new file mode 100644 index 00000000..25a7f002 --- /dev/null +++ b/percona-server-mongodb-8.2-community/.trivyignore @@ -0,0 +1,45 @@ +# Trivy Ignore File for MongoDB Community Edition 8.2 +# +# These vulnerabilities are in the MongoDB database tools (Go binaries) +# provided by MongoDB Inc. in the mongodb-database-tools package. +# +# MongoDB 8.2.3 includes mongodb-database-tools-100.14.0, but the tools +# are still compiled with Go 1.24.0, which has known vulnerabilities. +# These are upstream issues that require MongoDB to recompile the tools +# with a patched Go version (1.24.11+ or 1.25.5+). +# +# Affected binaries: +# - bsondump, mongodump, mongoexport, mongofiles +# - mongoimport, mongorestore, mongostat, mongotop +# +# MongoDB Version: 8.2.3 +# Database Tools Version: 100.14.0 +# Go Version (in tools): 1.24.0 +# +# Status: Waiting for MongoDB to recompile tools with Go 1.24.11+ or 1.25.5+ +# Last Updated: 2026-01-20 +# Review Date: Check monthly for MongoDB updates + +# CVE-2025-22874: crypto/x509 - ExtKeyUsageAny disables policy validation +# Severity: HIGH +# Fixed in: Go 1.24.4+ +# Component: stdlib in MongoDB database tools +CVE-2025-22874 + +# CVE-2025-47907: database/sql - Postgres Scan Race Condition +# Severity: HIGH +# Fixed in: Go 1.23.12, 1.24.6+ +# Component: stdlib in MongoDB database tools +CVE-2025-47907 + +# CVE-2025-58183: archive/tar - Unbounded allocation when parsing GNU sparse map +# Severity: HIGH +# Fixed in: Go 1.24.8, 1.25.2+ +# Component: stdlib in MongoDB database tools +CVE-2025-58183 + +# CVE-2025-61729: crypto/x509 - Denial of Service due to excessive resource consumption +# Severity: HIGH +# Fixed in: Go 1.24.11, 1.25.5+ +# Component: stdlib in MongoDB database tools +CVE-2025-61729 diff --git a/percona-server-mongodb-8.2-community/Dockerfile b/percona-server-mongodb-8.2-community/Dockerfile new file mode 100644 index 00000000..67a87d42 --- /dev/null +++ b/percona-server-mongodb-8.2-community/Dockerfile @@ -0,0 +1,78 @@ +FROM redhat/ubi9-minimal + +LABEL name="MongoDB Community Edition" \ + release="8.0" \ + vendor="MongoDB Inc." \ + summary="MongoDB Community Edition with Percona operator compatibility" \ + description="MongoDB Community Edition configured for use with Percona MongoDB operator" \ + maintainer="Percona Development " +LABEL org.opencontainers.image.authors="info@percona.com" + +ENV PSMDB_VERSION=8.2.3 +ENV OS_VER=el9 + +# Do not report during Docker image creation. +ARG PERCONA_TELEMETRY_DISABLE=1 + +COPY mongodb.repo /etc/yum.repos.d/mongodb.repo + +RUN set -ex; \ + microdnf -y update; \ + microdnf -y install \ + mongodb-org-${PSMDB_VERSION} \ + mongodb-org-database-${PSMDB_VERSION} \ + mongodb-org-server-${PSMDB_VERSION} \ + mongodb-mongosh \ + mongodb-org-mongos-${PSMDB_VERSION} \ + mongodb-org-tools-${PSMDB_VERSION} \ + numactl-libs \ + procps-ng \ + jq \ + tar \ + oniguruma \ + cyrus-sasl-gssapi \ + cyrus-sasl-plain; \ + microdnf clean all; \ + rm -rf /var/cache/yum /data/db && mkdir -p /data/db; \ + chown -R 1001:0 /data/db + +# the numeric UID is needed for OpenShift +RUN useradd -u 1001 -r -g 0 -m -s /sbin/nologin \ + -c "Default Application User" mongodb; \ + mkdir -p /var/log/mongo /etc/mongodb; \ + chmod g+rwx /var/log/mongo /etc/mongodb; \ + chown :0 /var/log/mongo /etc/mongodb + +COPY LICENSE /licenses/LICENSE.Dockerfile +RUN cp /usr/share/doc/mongodb-org-server/LICENSE-Community.txt /licenses/LICENSE.MongoDB-Community || \ + echo "MongoDB Community License" > /licenses/LICENSE.MongoDB-Community + +ENV GOSU_VERSION=1.11 +RUN set -eux; \ + curl -Lf -o /usr/bin/gosu https://github.com/tianon/gosu/releases/download/${GOSU_VERSION}/gosu-amd64; \ + curl -Lf -o /usr/bin/gosu.asc https://github.com/tianon/gosu/releases/download/${GOSU_VERSION}/gosu-amd64.asc; \ + \ + export GNUPGHOME="$(mktemp -d)"; \ + gpg --batch --keyserver hkps://keys.openpgp.org --recv-keys B42F6819007F00F88E364FD4036A9C25BF357DD4; \ + gpg --batch --verify /usr/bin/gosu.asc /usr/bin/gosu; \ + rm -rf "$GNUPGHOME" /usr/bin/gosu.asc; \ + \ + chmod +x /usr/bin/gosu; \ + curl -f -o /licenses/LICENSE.gosu https://raw.githubusercontent.com/tianon/gosu/${GOSU_VERSION}/LICENSE + +VOLUME ["/data/db"] + +RUN set -ex; \ + curl -fSL https://cdnjs.cloudflare.com/ajax/libs/js-yaml/4.1.0/js-yaml.min.js -o /js-yaml.js; \ + echo "45dc3dd03dc07a06705a2c2989b8c7f709013f04bd5386e3279d4e447f07ebd7 /js-yaml.js" | sha256sum -c - + +COPY ps-entry.sh /entrypoint.sh +RUN chmod +x /entrypoint.sh + +ENTRYPOINT ["/entrypoint.sh"] + +EXPOSE 27017 + +USER 1001 + +CMD ["mongod"] \ No newline at end of file diff --git a/percona-server-mongodb-8.2-community/Dockerfile.k8s b/percona-server-mongodb-8.2-community/Dockerfile.k8s new file mode 100644 index 00000000..16924ab2 --- /dev/null +++ b/percona-server-mongodb-8.2-community/Dockerfile.k8s @@ -0,0 +1,198 @@ +# Multi-architecture MongoDB Community Edition for Kubernetes +# This Dockerfile is optimized for Kubernetes deployments with multi-arch support + +ARG TARGETARCH +ARG TARGETOS +ARG TARGETPLATFORM + +FROM --platform=${TARGETPLATFORM} redhat/ubi9-minimal + +# Kubernetes and OpenShift compatibility labels +LABEL name="MongoDB Community Edition for Kubernetes" \ + release="8.2" \ + version="8.2.3" \ + vendor="MongoDB Inc." \ + summary="MongoDB Community Edition optimized for Kubernetes with Percona operator compatibility" \ + description="Multi-architecture MongoDB Community Edition container optimized for Kubernetes deployments. Includes enhanced security, health checks, and operator integration." \ + maintainer="Percona Development " \ + io.k8s.description="MongoDB Community Edition for Kubernetes" \ + io.k8s.display-name="MongoDB Community 8.2" \ + io.openshift.expose-services="27017:mongodb" \ + io.openshift.tags="database,mongodb,nosql" + +LABEL org.opencontainers.image.title="MongoDB Community Edition for Kubernetes" \ + org.opencontainers.image.description="Multi-architecture MongoDB Community Edition optimized for Kubernetes" \ + org.opencontainers.image.version="8.2.3" \ + org.opencontainers.image.authors="info@percona.com" \ + org.opencontainers.image.vendor="Percona" \ + org.opencontainers.image.licenses="SSPL-1.0" \ + org.opencontainers.image.source="https://github.com/percona/percona-docker" \ + org.opencontainers.image.documentation="https://docs.percona.com/" + +# Environment variables for MongoDB and Kubernetes +ENV PSMDB_VERSION=8.2.3 \ + OS_VER=el9 \ + MONGODB_VERSION=8.2 \ + GOSU_VERSION=1.17 \ + PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin + +# Kubernetes-specific environment variables +ENV MONGODB_DATA_DIR=/data/db \ + MONGODB_LOG_DIR=/var/log/mongodb \ + MONGODB_CONFIG_DIR=/etc/mongodb \ + MONGODB_USER=mongodb \ + MONGODB_UID=1001 \ + MONGODB_GID=0 + +# Security and telemetry settings +ARG PERCONA_TELEMETRY_DISABLE=1 +ENV PERCONA_TELEMETRY_DISABLE=${PERCONA_TELEMETRY_DISABLE} + +# Copy repository configuration with architecture detection +COPY mongodb.repo /etc/yum.repos.d/mongodb.repo + +# Install MongoDB and dependencies with multi-arch support +RUN set -ex; \ + # Update system packages + microdnf -y update; \ + \ + # Install required system packages + microdnf -y install \ + shadow-utils \ + findutils \ + tar \ + gzip \ + procps-ng; \ + \ + # Install MongoDB packages + microdnf -y install \ + mongodb-org-${PSMDB_VERSION} \ + mongodb-org-database-${PSMDB_VERSION} \ + mongodb-org-server-${PSMDB_VERSION} \ + mongodb-mongosh \ + mongodb-org-mongos-${PSMDB_VERSION} \ + mongodb-org-tools-${PSMDB_VERSION} \ + numactl-libs \ + jq \ + oniguruma \ + cyrus-sasl-gssapi \ + cyrus-sasl-plain; \ + \ + # Clean up package cache + microdnf clean all; \ + rm -rf /var/cache/yum; \ + \ + # Create necessary directories with proper permissions + mkdir -p ${MONGODB_DATA_DIR} ${MONGODB_LOG_DIR} ${MONGODB_CONFIG_DIR} /tmp/mongodb; \ + \ + # Set up directory permissions for Kubernetes/OpenShift + chown -R ${MONGODB_UID}:${MONGODB_GID} ${MONGODB_DATA_DIR} ${MONGODB_LOG_DIR} ${MONGODB_CONFIG_DIR} /tmp/mongodb; \ + chmod -R g+rwx ${MONGODB_DATA_DIR} ${MONGODB_LOG_DIR} ${MONGODB_CONFIG_DIR} /tmp/mongodb; \ + chmod -R o-rwx ${MONGODB_DATA_DIR} ${MONGODB_LOG_DIR} ${MONGODB_CONFIG_DIR} + +# Create MongoDB user with Kubernetes/OpenShift compatibility +RUN set -ex; \ + # Create mongodb user with specific UID for Kubernetes + useradd -u ${MONGODB_UID} -r -g ${MONGODB_GID} -m -s /sbin/nologin \ + -c "MongoDB Application User" -d /home/mongodb ${MONGODB_USER}; \ + \ + # Ensure proper ownership and permissions + chown -R ${MONGODB_UID}:${MONGODB_GID} /home/mongodb; \ + chmod -R g+rwx /home/mongodb + +# Install gosu for privilege dropping with multi-arch support +RUN set -eux; \ + ARCH=$(uname -m); \ + case "$ARCH" in \ + x86_64) GOSU_ARCH="amd64" ;; \ + aarch64) GOSU_ARCH="arm64" ;; \ + *) echo "Unsupported architecture for gosu: $ARCH" && exit 1 ;; \ + esac; \ + \ + curl -fsSL -o /usr/bin/gosu "https://github.com/tianon/gosu/releases/download/${GOSU_VERSION}/gosu-${GOSU_ARCH}"; \ + curl -fsSL -o /usr/bin/gosu.asc "https://github.com/tianon/gosu/releases/download/${GOSU_VERSION}/gosu-${GOSU_ARCH}.asc"; \ + \ + export GNUPGHOME="$(mktemp -d)"; \ + gpg --batch --keyserver hkps://keys.openpgp.org --recv-keys B42F6819007F00F88E364FD4036A9C25BF357DD4; \ + gpg --batch --verify /usr/bin/gosu.asc /usr/bin/gosu; \ + rm -rf "$GNUPGHOME" /usr/bin/gosu.asc; \ + \ + chmod +x /usr/bin/gosu; \ + gosu --version; \ + gosu nobody true + +# Install js-yaml for configuration parsing +RUN set -ex; \ + curl -fsSL https://cdnjs.cloudflare.com/ajax/libs/js-yaml/4.1.0/js-yaml.min.js -o /js-yaml.js; \ + echo "45dc3dd03dc07a06705a2c2989b8c7f709013f04bd5386e3279d4e447f07ebd7 /js-yaml.js" | sha256sum -c -; \ + chmod 644 /js-yaml.js + +# Copy license files +COPY LICENSE /licenses/LICENSE.Dockerfile +RUN set -ex; \ + mkdir -p /licenses; \ + cp /usr/share/doc/mongodb-org-server/LICENSE-Community.txt /licenses/LICENSE.MongoDB-Community 2>/dev/null || \ + echo "MongoDB Community License (SSPL-1.0)" > /licenses/LICENSE.MongoDB-Community; \ + curl -fsSL -o /licenses/LICENSE.gosu "https://raw.githubusercontent.com/tianon/gosu/${GOSU_VERSION}/LICENSE"; \ + chmod 644 /licenses/* + +# Copy entrypoint and health check scripts +COPY ps-entry.sh /entrypoint.sh +COPY healthcheck.sh /healthcheck.sh + +# Set up scripts with proper permissions +RUN set -ex; \ + chmod +x /entrypoint.sh /healthcheck.sh; \ + chown ${MONGODB_UID}:${MONGODB_GID} /entrypoint.sh /healthcheck.sh + +# Create MongoDB configuration template +RUN set -ex; \ + cat > ${MONGODB_CONFIG_DIR}/mongod.conf.template << 'EOF' +# MongoDB configuration file template for Kubernetes +storage: + dbPath: /data/db + wiredTiger: + engineConfig: + cacheSizeGB: 0.25 + +systemLog: + destination: file + logAppend: true + path: /var/log/mongodb/mongod.log + logRotate: reopen + +net: + port: 27017 + bindIpAll: true + +processManagement: + timeZoneInfo: /usr/share/zoneinfo + +security: + authorization: disabled + +replication: + replSetName: rs0 + +operationProfiling: + slowOpThresholdMs: 100 +EOF + +# Set up health check +HEALTHCHECK --interval=30s --timeout=10s --start-period=40s --retries=3 \ + CMD /healthcheck.sh + +# Kubernetes-specific volume declarations +VOLUME ["${MONGODB_DATA_DIR}", "${MONGODB_LOG_DIR}"] + +# Expose MongoDB port +EXPOSE 27017 + +# Use entrypoint for MongoDB initialization +ENTRYPOINT ["/entrypoint.sh"] + +# Switch to non-root user +USER ${MONGODB_UID} + +# Default command +CMD ["mongod"] \ No newline at end of file diff --git a/percona-server-mongodb-8.2-community/LICENSE b/percona-server-mongodb-8.2-community/LICENSE new file mode 100644 index 00000000..0ae7afb4 --- /dev/null +++ b/percona-server-mongodb-8.2-community/LICENSE @@ -0,0 +1,7 @@ +MongoDB Community Edition License + +This software is licensed under the Server Side Public License (SSPL) v1. +For more information, see: https://www.mongodb.com/licensing/server-side-public-license + +This Docker image contains MongoDB Community Edition binaries and is configured +for use with the Percona MongoDB Operator for Kubernetes. \ No newline at end of file diff --git a/percona-server-mongodb-8.2-community/healthcheck.sh b/percona-server-mongodb-8.2-community/healthcheck.sh new file mode 100644 index 00000000..63edfa77 --- /dev/null +++ b/percona-server-mongodb-8.2-community/healthcheck.sh @@ -0,0 +1,236 @@ +#!/bin/bash +# MongoDB Health Check Script for Kubernetes + +set -euo pipefail + +# Configuration +MONGODB_HOST="${MONGODB_HOST:-localhost}" +MONGODB_PORT="${MONGODB_PORT:-27017}" +MONGODB_DATABASE="${MONGODB_DATABASE:-admin}" +TIMEOUT="${HEALTH_CHECK_TIMEOUT:-10}" +MAX_RETRIES="${HEALTH_CHECK_RETRIES:-3}" + +# Logging function +log() { + echo "[$(date +'%Y-%m-%d %H:%M:%S')] HEALTHCHECK: $*" >&2 +} + +# Function to check if MongoDB is accepting connections +check_connection() { + local retry=0 + while [ $retry -lt $MAX_RETRIES ]; do + if timeout $TIMEOUT mongosh --host "$MONGODB_HOST" --port "$MONGODB_PORT" --quiet --eval "db.adminCommand('ping')" >/dev/null 2>&1; then + return 0 + fi + retry=$((retry + 1)) + log "Connection attempt $retry failed, retrying..." + sleep 1 + done + return 1 +} + +# Function to check MongoDB server status +check_server_status() { + local status_output + status_output=$(timeout $TIMEOUT mongosh --host "$MONGODB_HOST" --port "$MONGODB_PORT" --quiet --eval " + try { + var status = db.adminCommand('serverStatus'); + if (status.ok === 1) { + print('OK'); + } else { + print('ERROR: Server status not OK'); + } + } catch (e) { + print('ERROR: ' + e.message); + } + " 2>/dev/null) + + if [[ "$status_output" == "OK" ]]; then + return 0 + else + log "Server status check failed: $status_output" + return 1 + fi +} + +# Function to check if MongoDB is ready for read/write operations +check_readiness() { + local readiness_output + readiness_output=$(timeout $TIMEOUT mongosh --host "$MONGODB_HOST" --port "$MONGODB_PORT" --quiet --eval " + try { + // Check if we can perform basic operations + db.healthcheck.insertOne({timestamp: new Date(), check: 'readiness'}); + db.healthcheck.findOne({check: 'readiness'}); + db.healthcheck.deleteMany({check: 'readiness'}); + print('READY'); + } catch (e) { + print('NOT_READY: ' + e.message); + } + " 2>/dev/null) + + if [[ "$readiness_output" == "READY" ]]; then + return 0 + else + log "Readiness check failed: $readiness_output" + return 1 + fi +} + +# Function to check replica set status (if applicable) +check_replica_set() { + local rs_output + rs_output=$(timeout $TIMEOUT mongosh --host "$MONGODB_HOST" --port "$MONGODB_PORT" --quiet --eval " + try { + var status = rs.status(); + if (status.ok === 1) { + var myState = status.members.find(m => m.self === true); + if (myState && (myState.state === 1 || myState.state === 2)) { + print('RS_OK'); + } else { + print('RS_NOT_READY: State ' + (myState ? myState.state : 'unknown')); + } + } else { + print('RS_ERROR: ' + status.errmsg); + } + } catch (e) { + // Not a replica set or not initialized yet + print('RS_NOT_CONFIGURED'); + } + " 2>/dev/null) + + case "$rs_output" in + "RS_OK") + log "Replica set status: OK" + return 0 + ;; + "RS_NOT_CONFIGURED") + log "Replica set not configured (standalone mode)" + return 0 + ;; + *) + log "Replica set check failed: $rs_output" + return 1 + ;; + esac +} + +# Function to check disk space +check_disk_space() { + local data_dir="${MONGODB_DATA_DIR:-/data/db}" + local log_dir="${MONGODB_LOG_DIR:-/var/log/mongodb}" + local min_free_percent="${MIN_FREE_DISK_PERCENT:-10}" + + for dir in "$data_dir" "$log_dir"; do + if [[ -d "$dir" ]]; then + local usage + usage=$(df "$dir" | awk 'NR==2 {print $5}' | sed 's/%//') + local free_percent=$((100 - usage)) + + if [[ $free_percent -lt $min_free_percent ]]; then + log "Disk space warning: $dir has only $free_percent% free (minimum: $min_free_percent%)" + return 1 + fi + fi + done + return 0 +} + +# Function to check process health +check_process_health() { + # Check if mongod process is running + if ! pgrep -f mongod >/dev/null 2>&1; then + log "MongoDB process not found" + return 1 + fi + + # Check if process is responsive (not in uninterruptible sleep) + local mongod_pid + mongod_pid=$(pgrep -f mongod | head -1) + if [[ -n "$mongod_pid" ]]; then + local process_state + process_state=$(ps -o state= -p "$mongod_pid" 2>/dev/null | tr -d ' ') + if [[ "$process_state" == "D" ]]; then + log "MongoDB process is in uninterruptible sleep state" + return 1 + fi + fi + + return 0 +} + +# Main health check function +main() { + local exit_code=0 + local check_type="${1:-full}" + + log "Starting health check (type: $check_type)" + + # Always check process health first + if ! check_process_health; then + log "Process health check failed" + exit_code=1 + fi + + # Check MongoDB connection + if ! check_connection; then + log "Connection check failed" + exit_code=1 + fi + + # For full health checks, perform additional tests + if [[ "$check_type" == "full" ]]; then + # Check server status + if ! check_server_status; then + log "Server status check failed" + exit_code=1 + fi + + # Check readiness for operations + if ! check_readiness; then + log "Readiness check failed" + exit_code=1 + fi + + # Check replica set status if applicable + if ! check_replica_set; then + log "Replica set check failed" + exit_code=1 + fi + + # Check disk space + if ! check_disk_space; then + log "Disk space check failed" + exit_code=1 + fi + fi + + if [[ $exit_code -eq 0 ]]; then + log "Health check passed" + else + log "Health check failed" + fi + + exit $exit_code +} + +# Handle different invocation modes +case "${1:-full}" in + "liveness"|"live") + # Liveness probe - basic connection check + main "basic" + ;; + "readiness"|"ready") + # Readiness probe - full functionality check + main "full" + ;; + "startup") + # Startup probe - basic connection with retries + MAX_RETRIES=10 + TIMEOUT=30 + main "basic" + ;; + *) + # Default full health check + main "full" + ;; +esac diff --git a/percona-server-mongodb-8.2-community/k8s-examples/mongodb-deployment.yaml b/percona-server-mongodb-8.2-community/k8s-examples/mongodb-deployment.yaml new file mode 100644 index 00000000..c4ab31bb --- /dev/null +++ b/percona-server-mongodb-8.2-community/k8s-examples/mongodb-deployment.yaml @@ -0,0 +1,231 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: mongodb + labels: + name: mongodb +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: mongodb-config + namespace: mongodb +data: + mongod.conf: | + # MongoDB configuration for Kubernetes + storage: + dbPath: /data/db + journal: + enabled: true + wiredTiger: + engineConfig: + cacheSizeGB: 1.0 + + systemLog: + destination: file + logAppend: true + path: /var/log/mongodb/mongod.log + logRotate: reopen + verbosity: 1 + + net: + port: 27017 + bindIpAll: true + maxIncomingConnections: 1000 + + processManagement: + timeZoneInfo: /usr/share/zoneinfo + + security: + authorization: enabled + + replication: + replSetName: rs0 + + operationProfiling: + slowOpThresholdMs: 100 + mode: slowOp +--- +apiVersion: v1 +kind: Secret +metadata: + name: mongodb-secret + namespace: mongodb +type: Opaque +data: + # echo -n 'admin' | base64 + username: YWRtaW4= + # echo -n 'SecurePassword123!' | base64 + password: U2VjdXJlUGFzc3dvcmQxMjMh +--- +apiVersion: v1 +kind: Service +metadata: + name: mongodb-service + namespace: mongodb + labels: + app: mongodb +spec: + type: ClusterIP + ports: + - port: 27017 + targetPort: 27017 + protocol: TCP + name: mongodb + selector: + app: mongodb +--- +apiVersion: v1 +kind: Service +metadata: + name: mongodb-headless + namespace: mongodb + labels: + app: mongodb +spec: + type: ClusterIP + clusterIP: None + ports: + - port: 27017 + targetPort: 27017 + protocol: TCP + name: mongodb + selector: + app: mongodb +--- +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: mongodb + namespace: mongodb + labels: + app: mongodb +spec: + serviceName: mongodb-headless + replicas: 3 + selector: + matchLabels: + app: mongodb + template: + metadata: + labels: + app: mongodb + spec: + securityContext: + runAsUser: 1001 + runAsGroup: 0 + fsGroup: 0 + runAsNonRoot: true + containers: + - name: mongodb + image: percona/percona-server-mongodb:8.0-community-k8s + imagePullPolicy: IfNotPresent + ports: + - containerPort: 27017 + name: mongodb + protocol: TCP + env: + - name: MONGO_INITDB_ROOT_USERNAME_FILE + value: /etc/mongodb-secret/username + - name: MONGO_INITDB_ROOT_PASSWORD_FILE + value: /etc/mongodb-secret/password + - name: MONGO_INITDB_DATABASE + value: admin + - name: REPLICA_SET_NAME + value: rs0 + - name: K8S_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: K8S_POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: K8S_SERVICE_NAME + value: mongodb-headless + - name: MONGODB_DATA_DIR + value: /data/db + - name: MONGODB_LOG_DIR + value: /var/log/mongodb + - name: MONGODB_CONFIG_DIR + value: /etc/mongodb + resources: + requests: + memory: "512Mi" + cpu: "250m" + limits: + memory: "2Gi" + cpu: "1000m" + volumeMounts: + - name: mongodb-data + mountPath: /data/db + - name: mongodb-logs + mountPath: /var/log/mongodb + - name: mongodb-config + mountPath: /etc/mongodb/mongod.conf.template + subPath: mongod.conf + readOnly: true + - name: mongodb-secret + mountPath: /etc/mongodb-secret + readOnly: true + livenessProbe: + exec: + command: + - /healthcheck.sh + - liveness + initialDelaySeconds: 30 + periodSeconds: 30 + timeoutSeconds: 10 + failureThreshold: 3 + successThreshold: 1 + readinessProbe: + exec: + command: + - /healthcheck.sh + - readiness + initialDelaySeconds: 10 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 3 + successThreshold: 1 + startupProbe: + exec: + command: + - /healthcheck.sh + - startup + initialDelaySeconds: 10 + periodSeconds: 10 + timeoutSeconds: 30 + failureThreshold: 30 + successThreshold: 1 + securityContext: + allowPrivilegeEscalation: false + readOnlyRootFilesystem: false + runAsNonRoot: true + runAsUser: 1001 + runAsGroup: 0 + capabilities: + drop: + - ALL + volumes: + - name: mongodb-config + configMap: + name: mongodb-config + defaultMode: 0644 + - name: mongodb-secret + secret: + secretName: mongodb-secret + defaultMode: 0400 + - name: mongodb-logs + emptyDir: {} + terminationGracePeriodSeconds: 30 + restartPolicy: Always + volumeClaimTemplates: + - metadata: + name: mongodb-data + spec: + accessModes: ["ReadWriteOnce"] + storageClassName: "standard" + resources: + requests: + storage: 10Gi \ No newline at end of file diff --git a/percona-server-mongodb-8.2-community/k8s-examples/mongodb-simple.yaml b/percona-server-mongodb-8.2-community/k8s-examples/mongodb-simple.yaml new file mode 100644 index 00000000..4ab29401 --- /dev/null +++ b/percona-server-mongodb-8.2-community/k8s-examples/mongodb-simple.yaml @@ -0,0 +1,158 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: mongodb-simple +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: mongodb-config + namespace: mongodb-simple +data: + mongod.conf: | + # Simple MongoDB configuration for Kubernetes + storage: + dbPath: /data/db + journal: + enabled: true + + systemLog: + destination: file + logAppend: true + path: /var/log/mongodb/mongod.log + + net: + port: 27017 + bindIpAll: true + + processManagement: + timeZoneInfo: /usr/share/zoneinfo + + security: + authorization: disabled +--- +apiVersion: v1 +kind: Service +metadata: + name: mongodb + namespace: mongodb-simple + labels: + app: mongodb +spec: + type: ClusterIP + ports: + - port: 27017 + targetPort: 27017 + protocol: TCP + name: mongodb + selector: + app: mongodb +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: mongodb + namespace: mongodb-simple + labels: + app: mongodb +spec: + replicas: 1 + selector: + matchLabels: + app: mongodb + template: + metadata: + labels: + app: mongodb + spec: + securityContext: + runAsUser: 1001 + runAsGroup: 0 + fsGroup: 0 + runAsNonRoot: true + containers: + - name: mongodb + image: percona/percona-server-mongodb:8.0-community-k8s + imagePullPolicy: IfNotPresent + ports: + - containerPort: 27017 + name: mongodb + protocol: TCP + env: + - name: K8S_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: K8S_POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: MONGODB_DATA_DIR + value: /data/db + - name: MONGODB_LOG_DIR + value: /var/log/mongodb + - name: MONGODB_CONFIG_DIR + value: /etc/mongodb + resources: + requests: + memory: "256Mi" + cpu: "100m" + limits: + memory: "1Gi" + cpu: "500m" + volumeMounts: + - name: mongodb-data + mountPath: /data/db + - name: mongodb-logs + mountPath: /var/log/mongodb + - name: mongodb-config + mountPath: /etc/mongodb/mongod.conf.template + subPath: mongod.conf + readOnly: true + livenessProbe: + exec: + command: + - /healthcheck.sh + - liveness + initialDelaySeconds: 30 + periodSeconds: 30 + timeoutSeconds: 10 + failureThreshold: 3 + readinessProbe: + exec: + command: + - /healthcheck.sh + - readiness + initialDelaySeconds: 10 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 3 + startupProbe: + exec: + command: + - /healthcheck.sh + - startup + initialDelaySeconds: 10 + periodSeconds: 10 + timeoutSeconds: 30 + failureThreshold: 30 + securityContext: + allowPrivilegeEscalation: false + readOnlyRootFilesystem: false + runAsNonRoot: true + runAsUser: 1001 + runAsGroup: 0 + capabilities: + drop: + - ALL + volumes: + - name: mongodb-data + emptyDir: {} + - name: mongodb-logs + emptyDir: {} + - name: mongodb-config + configMap: + name: mongodb-config + defaultMode: 0644 + terminationGracePeriodSeconds: 30 + restartPolicy: Always \ No newline at end of file diff --git a/percona-server-mongodb-8.2-community/mongodb.repo b/percona-server-mongodb-8.2-community/mongodb.repo new file mode 100644 index 00000000..8ea5273e --- /dev/null +++ b/percona-server-mongodb-8.2-community/mongodb.repo @@ -0,0 +1,10 @@ +[mongodb-org-8.2] +name=MongoDB Repository +baseurl=https://repo.mongodb.org/yum/redhat/9/mongodb-org/8.2/$basearch/ +gpgcheck=1 +enabled=1 +gpgkey=https://pgp.mongodb.com/server-8.0.asc +sslverify=1 +sslcacert=/etc/pki/tls/certs/ca-bundle.crt +metadata_expire=300 +repo_gpgcheck=1 diff --git a/percona-server-mongodb-8.2-community/ps-entry.sh b/percona-server-mongodb-8.2-community/ps-entry.sh new file mode 100644 index 00000000..735468ae --- /dev/null +++ b/percona-server-mongodb-8.2-community/ps-entry.sh @@ -0,0 +1,332 @@ +#!/bin/bash +# Enhanced MongoDB Entrypoint Script for Kubernetes +# Optimized for Kubernetes deployments with improved signal handling, configuration management, and operator integration + +set -Eeuo pipefail + +# Configuration variables +MONGODB_DATA_DIR="${MONGODB_DATA_DIR:-/data/db}" +MONGODB_LOG_DIR="${MONGODB_LOG_DIR:-/var/log/mongodb}" +MONGODB_CONFIG_DIR="${MONGODB_CONFIG_DIR:-/etc/mongodb}" +MONGODB_USER="${MONGODB_USER:-mongodb}" +MONGODB_UID="${MONGODB_UID:-1001}" +MONGODB_GID="${MONGODB_GID:-0}" + +# Kubernetes-specific variables +K8S_NAMESPACE="${K8S_NAMESPACE:-}" +K8S_POD_NAME="${K8S_POD_NAME:-}" +K8S_SERVICE_NAME="${K8S_SERVICE_NAME:-}" +REPLICA_SET_NAME="${REPLICA_SET_NAME:-rs0}" + +# Logging function +log() { + echo "[$(date +'%Y-%m-%d %H:%M:%S')] ENTRYPOINT: $*" >&2 +} + +# Error handling +error_exit() { + log "ERROR: $1" + exit 1 +} + +# Signal handling for graceful shutdown +shutdown_handler() { + log "Received shutdown signal, initiating graceful shutdown..." + + # If MongoDB is running, try to shut it down gracefully + if pgrep -f mongod >/dev/null 2>&1; then + log "Shutting down MongoDB gracefully..." + + # Try to use MongoDB's shutdown command first + if mongosh --quiet --eval "db.adminCommand('shutdown')" >/dev/null 2>&1; then + log "MongoDB shutdown command sent successfully" + else + log "MongoDB shutdown command failed, sending SIGTERM to process" + pkill -TERM mongod || true + fi + + # Wait for process to exit gracefully + local timeout=30 + while pgrep -f mongod >/dev/null 2>&1 && [ $timeout -gt 0 ]; do + sleep 1 + timeout=$((timeout - 1)) + done + + if pgrep -f mongod >/dev/null 2>&1; then + log "MongoDB did not shut down gracefully, sending SIGKILL" + pkill -KILL mongod || true + else + log "MongoDB shut down gracefully" + fi + fi + + exit 0 +} + +# Set up signal handlers +trap shutdown_handler SIGTERM SIGINT SIGQUIT + +# Function to setup directories and permissions +setup_directories() { + log "Setting up directories and permissions..." + + # Create necessary directories + for dir in "$MONGODB_DATA_DIR" "$MONGODB_LOG_DIR" "$MONGODB_CONFIG_DIR" "/tmp/mongodb"; do + if [[ ! -d "$dir" ]]; then + mkdir -p "$dir" + log "Created directory: $dir" + fi + done + + # Set ownership and permissions for Kubernetes/OpenShift compatibility + chown -R "$MONGODB_UID:$MONGODB_GID" "$MONGODB_DATA_DIR" "$MONGODB_LOG_DIR" "$MONGODB_CONFIG_DIR" "/tmp/mongodb" 2>/dev/null || true + chmod -R g+rwx "$MONGODB_DATA_DIR" "$MONGODB_LOG_DIR" "$MONGODB_CONFIG_DIR" "/tmp/mongodb" 2>/dev/null || true + chmod -R o-rwx "$MONGODB_DATA_DIR" "$MONGODB_LOG_DIR" "$MONGODB_CONFIG_DIR" 2>/dev/null || true +} + +# Function to handle file-based environment variables (Kubernetes secrets) +file_env() { + local var="$1" + local fileVar="${var}_FILE" + local def="${2:-}" + + if [[ -n "${!var:-}" ]] && [[ -n "${!fileVar:-}" ]]; then + error_exit "Both $var and $fileVar are set (but are exclusive)" + fi + + local val="$def" + if [[ -n "${!var:-}" ]]; then + val="${!var}" + elif [[ -n "${!fileVar:-}" ]]; then + if [[ -f "${!fileVar}" ]]; then + val="$(< "${!fileVar}")" + else + error_exit "File ${!fileVar} does not exist" + fi + fi + + export "$var"="$val" + unset "$fileVar" 2>/dev/null || true +} + +# Function to generate MongoDB configuration +generate_config() { + local config_file="$MONGODB_CONFIG_DIR/mongod.conf" + + log "Generating MongoDB configuration..." + + # Start with template if it exists + if [[ -f "$MONGODB_CONFIG_DIR/mongod.conf.template" ]]; then + cp "$MONGODB_CONFIG_DIR/mongod.conf.template" "$config_file" + else + # Create basic configuration + cat > "$config_file" << EOF +# MongoDB configuration for Kubernetes +storage: + dbPath: $MONGODB_DATA_DIR + journal: + enabled: true + +systemLog: + destination: file + logAppend: true + path: $MONGODB_LOG_DIR/mongod.log + logRotate: reopen + +net: + port: 27017 + bindIpAll: true + +processManagement: + timeZoneInfo: /usr/share/zoneinfo + +security: + authorization: disabled +EOF + fi + + # Add replica set configuration if specified + if [[ -n "$REPLICA_SET_NAME" ]]; then + if ! grep -q "replication:" "$config_file"; then + cat >> "$config_file" << EOF + +replication: + replSetName: $REPLICA_SET_NAME +EOF + fi + fi + + # Set proper permissions + chown "$MONGODB_UID:$MONGODB_GID" "$config_file" + chmod 640 "$config_file" + + log "MongoDB configuration generated at $config_file" +} + +# Function to wait for network readiness +wait_for_network() { + log "Waiting for network readiness..." + + local timeout=30 + while [ $timeout -gt 0 ]; do + if ss -tuln | grep -q ":27017 "; then + log "Port 27017 is already in use, waiting..." + sleep 1 + timeout=$((timeout - 1)) + else + break + fi + done + + if [ $timeout -eq 0 ]; then + log "Warning: Port 27017 may still be in use" + fi +} + +# Function to initialize MongoDB if needed +initialize_mongodb() { + log "Checking if MongoDB initialization is needed..." + + # Check if this is a fresh installation + if [[ ! -f "$MONGODB_DATA_DIR/WiredTiger" ]] && [[ ! -f "$MONGODB_DATA_DIR/mongod.lock" ]]; then + log "Fresh MongoDB installation detected" + + # Handle initialization scripts + file_env 'MONGO_INITDB_ROOT_USERNAME' + file_env 'MONGO_INITDB_ROOT_PASSWORD' + file_env 'MONGO_INITDB_DATABASE' + + if [[ -n "${MONGO_INITDB_ROOT_USERNAME:-}" ]] && [[ -n "${MONGO_INITDB_ROOT_PASSWORD:-}" ]]; then + log "Root user credentials provided, will initialize with authentication" + export MONGO_INITDB_ROOT_USERNAME + export MONGO_INITDB_ROOT_PASSWORD + export MONGO_INITDB_DATABASE="${MONGO_INITDB_DATABASE:-admin}" + fi + else + log "Existing MongoDB data found, skipping initialization" + fi +} + +# Function to setup MongoDB arguments with Kubernetes optimizations +setup_mongodb_args() { + local -a mongod_args=("$@") + + # If no arguments provided, use default + if [[ ${#mongod_args[@]} -eq 0 ]]; then + mongod_args=("mongod") + fi + + # If first argument starts with -, prepend mongod + if [[ "${mongod_args[0]:0:1}" = '-' ]]; then + mongod_args=("mongod" "${mongod_args[@]}") + fi + + # Add Kubernetes-specific optimizations + local -a k8s_args=() + + # Use configuration file if it exists + if [[ -f "$MONGODB_CONFIG_DIR/mongod.conf" ]]; then + k8s_args+=("--config" "$MONGODB_CONFIG_DIR/mongod.conf") + fi + + # Kubernetes-specific settings + k8s_args+=( + "--bind_ip_all" + "--logpath" "$MONGODB_LOG_DIR/mongod.log" + "--logappend" + ) + + # Add NUMA optimization if available + if command -v numactl >/dev/null 2>&1 && numactl --hardware >/dev/null 2>&1; then + mongod_args=("numactl" "--interleave=all" "${mongod_args[@]}") + fi + + # Combine arguments + mongod_args+=("${k8s_args[@]}") + + echo "${mongod_args[@]}" +} + +# Function to start MongoDB with proper user switching +start_mongodb() { + local -a mongod_cmd=($@) + + log "Starting MongoDB with command: ${mongod_cmd[*]}" + + # Ensure we're running as the correct user + if [[ "$(id -u)" = '0' ]]; then + # Running as root, switch to mongodb user + log "Running as root, switching to user $MONGODB_USER (UID: $MONGODB_UID)" + + # Ensure ownership of critical files + chown "$MONGODB_UID:$MONGODB_GID" "$MONGODB_DATA_DIR" "$MONGODB_LOG_DIR" 2>/dev/null || true + + # Use gosu to switch user and exec + exec gosu "$MONGODB_UID:$MONGODB_GID" "${mongod_cmd[@]}" + else + # Already running as non-root user + log "Running as user $(id -un) (UID: $(id -u))" + exec "${mongod_cmd[@]}" + fi +} + +# Function to check if command is MongoDB-related +is_mongodb_command() { + local cmd="$1" + + # List of MongoDB-related commands + case "$cmd" in + mongod|mongos|mongo|mongosh) + return 0 + ;; + *) + return 1 + ;; + esac +} + +# Main execution function +main() { + # Check if we should run MongoDB initialization or just exec the command + local first_arg="${1:-mongod}" + + # If the first argument is not a MongoDB command, just exec it directly + # This allows for command overrides like: docker run image echo "hello" + if ! is_mongodb_command "$first_arg"; then + log "Non-MongoDB command detected: $first_arg" + log "Executing command directly without MongoDB initialization" + + # If running as root, switch to mongodb user for consistency + if [[ "$(id -u)" = '0' ]]; then + exec gosu "$MONGODB_UID:$MONGODB_GID" "$@" + else + exec "$@" + fi + fi + + # MongoDB command detected, proceed with full initialization + log "Starting MongoDB entrypoint for Kubernetes..." + log "Pod: ${K8S_POD_NAME:-unknown}, Namespace: ${K8S_NAMESPACE:-unknown}" + + # Setup directories and permissions + setup_directories + + # Wait for network readiness + wait_for_network + + # Generate configuration + generate_config + + # Initialize MongoDB if needed + initialize_mongodb + + # Setup MongoDB arguments + local -a final_args + IFS=' ' read -ra final_args <<< "$(setup_mongodb_args "$@")" + + # Start MongoDB + start_mongodb "${final_args[@]}" +} + +# Execute main function with all arguments +main "$@" \ No newline at end of file