Docker & CI/CD
Container deployment
Build the image:
# Dockerfile
FROM python:3.12-slim
RUN pip install metapod-probes[all]
WORKDIR /app
ENTRYPOINT ["metapod"]
docker build -t metapod .
Run with mounted config and output:
docker run --rm \
-v $(pwd)/config.yaml:/app/config.yaml \
-v $(pwd)/output:/app/output \
metapod run "oracle.metadata.*" --config /app/config.yaml
Docker Compose
# docker-compose.yml
version: "3.8"
services:
metapod:
build: .
volumes:
- ./config.yaml:/app/config.yaml:ro
- ./output:/app/output
environment:
- ORACLE_PASSWORD=${ORACLE_PASSWORD}
command: run "oracle.metadata.*" --config /app/config.yaml --delta
# Optional: daemon mode
metapod-daemon:
build: .
ports:
- "9090:9090"
volumes:
- ./config.yaml:/app/config.yaml:ro
command: serve --config /app/config.yaml --port 9090
GitHub Actions
# .github/workflows/metadata.yml
name: Metadata Extraction
on:
schedule:
- cron: "0 6 * * 1-5" # Weekdays at 6 AM
workflow_dispatch: # Manual trigger
jobs:
extract:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install metapod
run: pip install metapod-probes[oracle,azure]
- name: Extract metadata
env:
ORACLE_PASSWORD: ${{ secrets.ORACLE_PASSWORD }}
run: metapod run "oracle.metadata.*" --config config.yaml --delta
- name: Upload artifacts
uses: actions/upload-artifact@v4
with:
name: metadata-${{ github.run_number }}
path: output/
retention-days: 30
# Optional: push to catalog
- name: Push to Dataflix
if: always()
env:
DATAFLIX_API_KEY: ${{ secrets.DATAFLIX_API_KEY }}
run: |
metapod push \
--target https://dataflix.example.com \
--project production \
--api-key $DATAFLIX_API_KEY
GitLab CI
# .gitlab-ci.yml
metadata-extraction:
image: python:3.12-slim
stage: extract
rules:
- if: $CI_PIPELINE_SOURCE == "schedule"
script:
- pip install metapod-probes[oracle]
- metapod run "oracle.metadata.*" --config config.yaml --delta
artifacts:
paths:
- output/
expire_in: 30 days
variables:
ORACLE_PASSWORD: $ORACLE_PASSWORD
Kubernetes CronJob
# k8s/cronjob.yaml
apiVersion: batch/v1
kind: CronJob
metadata:
name: metapod-extraction
spec:
schedule: "0 6 * * *"
jobTemplate:
spec:
template:
spec:
containers:
- name: metapod
image: metapod:latest
command:
- metapod
- run
- "oracle.metadata.*"
- --config
- /config/config.yaml
- --delta
- --output
- /output
volumeMounts:
- name: config
mountPath: /config
- name: output
mountPath: /output
envFrom:
- secretRef:
name: metapod-secrets
volumes:
- name: config
configMap:
name: metapod-config
- name: output
persistentVolumeClaim:
claimName: metapod-output
restartPolicy: OnFailure
Credential management
Never put credentials in config files. Use:
Environment variables:
ORACLE_PASSWORD,AZURE_CLIENT_SECRETCI/CD secrets: GitHub Secrets, GitLab CI Variables, Azure Key Vault
.env file (local dev only, add to
.gitignore)Kubernetes secrets: mounted as env vars or files
Example .env:
# .env (add to .gitignore!)
ORACLE_PASSWORD=my-secret
AZURE_TENANT_ID=xxxx
AZURE_CLIENT_ID=xxxx
AZURE_CLIENT_SECRET=xxxx
metapod loads .env automatically via python-dotenv.