Customizing ImagesΒΆ
Comprehensive guide to extending DevOps Images with custom tools, packages, and configurations tailored to your team's specific needs.
Why Customize?ΒΆ
Common Customization Reasons
- π§ Proprietary tools: Add internal or licensed software
- π§ Specific versions: Lock tool versions for compliance
- π§ Additional languages: Add Ruby, Go, Rust, etc.
- π§ Custom scripts: Include team-specific automation
- π§ Organization standards: Enforce coding standards, linters
- π§ Size optimization: Remove unused tools
Customization ApproachesΒΆ
Approach 1: Extend Existing Image (Recommended)ΒΆ
Fastest and simplest - build on top of official images:
# Dockerfile.custom
FROM ghcr.io/jinalshah/devops/images/all-devops:latest
# Add your custom tools here
RUN pip3 install custom-package
Pros: - β Minimal build time (~2-5 minutes) - β Inherits all updates from base image - β Easy to maintain
Cons: - β Cannot remove base tools - β Slight size overhead
Approach 2: Fork and Modify DockerfileΒΆ
Clone repository and modify the main Dockerfile:
git clone https://github.com/jinalshah/devops-images
cd devops-images
# Edit Dockerfile
docker build --target all-devops -t my-devops:latest .
Pros: - β Full control over all layers - β Can remove unwanted tools - β Maximum optimization
Cons: - β Longer build time (~20+ minutes) - β Must manually merge upstream updates - β More complex maintenance
Approach 3: Multi-Stage with BaseΒΆ
Use base image as builder, copy what you need:
FROM ghcr.io/jinalshah/devops/images/all-devops:latest AS base
FROM rockylinux:9
# Copy only specific tools from base
COPY --from=base /usr/local/bin/terraform /usr/local/bin/
COPY --from=base /usr/local/bin/kubectl /usr/local/bin/
# Add your custom setup
Pros: - β Minimal final image size - β Pick and choose tools
Cons: - β Complex dependency management - β May miss library dependencies - β Higher maintenance
Quick Customization ExamplesΒΆ
Add Python PackagesΒΆ
FROM ghcr.io/jinalshah/devops/images/all-devops:latest
# Add Python packages
RUN pip3 install --no-cache-dir \
requests==2.31.0 \
pydantic==2.5.0 \
black==23.12.1 \
pytest==7.4.3
Add Node.js PackagesΒΆ
FROM ghcr.io/jinalshah/devops/images/all-devops:latest
# Add Node.js global packages
RUN npm install -g \
prettier@3.1.1 \
eslint@8.56.0 \
typescript@5.3.3
Add System ToolsΒΆ
FROM ghcr.io/jinalshah/devops/images/all-devops:latest
# Add system packages
RUN dnf install -y \
httpd-tools \
nmap \
tcpdump \
&& dnf clean all
Add Binary ToolsΒΆ
FROM ghcr.io/jinalshah/devops/images/all-devops:latest
# Add custom binary
RUN curl -fsSL https://example.com/tool -o /usr/local/bin/tool && \
chmod +x /usr/local/bin/tool
Complete Customization ExamplesΒΆ
Example 1: Development Team ImageΒΆ
Add linters, formatters, and quality tools:
FROM ghcr.io/jinalshah/devops/images/all-devops:latest
# Python development tools
RUN pip3 install --no-cache-dir \
black \
pylint \
mypy \
pytest \
pytest-cov \
pre-commit
# Node.js development tools
RUN npm install -g \
prettier \
eslint \
@typescript-eslint/parser \
@typescript-eslint/eslint-plugin
# Add shellcheck for bash linting
RUN dnf install -y ShellCheck && dnf clean all
# Install hadolint for Dockerfile linting
RUN curl -fsSL https://github.com/hadolint/hadolint/releases/download/v2.12.0/hadolint-Linux-x86_64 \
-o /usr/local/bin/hadolint && \
chmod +x /usr/local/bin/hadolint
LABEL maintainer="devops@company.com"
LABEL version="1.0"
Build:
Example 2: Security-Focused ImageΒΆ
Add security scanning and hardening tools:
FROM ghcr.io/jinalshah/devops/images/all-devops:latest
# Security scanning tools
RUN pip3 install --no-cache-dir \
safety \
bandit \
checkov
# Add Snyk CLI
RUN npm install -g snyk
# Add Syft for SBOM generation
RUN curl -sSfL https://raw.githubusercontent.com/anchore/syft/main/install.sh | sh -s -- -b /usr/local/bin
# Add Grype for vulnerability scanning
RUN curl -sSfL https://raw.githubusercontent.com/anchore/grype/main/install.sh | sh -s -- -b /usr/local/bin
# Configure automated scans
COPY scan-scripts/ /opt/security-scripts/
ENV PATH="/opt/security-scripts:${PATH}"
LABEL purpose="security-scanning"
Example 3: Compliance & Audit ImageΒΆ
Pin all tool versions for regulatory compliance:
# Build args for version pinning
ARG TERRAFORM_VERSION=1.6.6
ARG PACKER_VERSION=1.10.0
ARG PYTHON_VERSION=3.12
FROM ghcr.io/jinalshah/devops/images/all-devops:latest
# Install specific versions
RUN curl -fsSL "https://releases.hashicorp.com/terraform/${TERRAFORM_VERSION}/terraform_${TERRAFORM_VERSION}_linux_amd64.zip" -o terraform.zip && \
unzip terraform.zip && \
mv terraform /usr/local/bin/ && \
rm terraform.zip
RUN curl -fsSL "https://releases.hashicorp.com/packer/${PACKER_VERSION}/packer_${PACKER_VERSION}_linux_amd64.zip" -o packer.zip && \
unzip packer.zip && \
mv packer /usr/local/bin/ && \
rm packer.zip
# Pin Python packages
COPY requirements.txt /tmp/
RUN pip3 install --no-cache-dir -r /tmp/requirements.txt
# Add compliance metadata
LABEL compliance="sox-compliant"
LABEL audit.terraform="${TERRAFORM_VERSION}"
LABEL audit.packer="${PACKER_VERSION}"
requirements.txt:
Example 4: Multi-Language ImageΒΆ
Add support for additional programming languages:
FROM ghcr.io/jinalshah/devops/images/all-devops:latest
# Install Go
ARG GO_VERSION=1.21.5
RUN curl -fsSL "https://go.dev/dl/go${GO_VERSION}.linux-amd64.tar.gz" | tar -C /usr/local -xz
ENV PATH="/usr/local/go/bin:${PATH}"
# Install Rust
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
ENV PATH="/root/.cargo/bin:${PATH}"
# Install Ruby
RUN dnf install -y ruby ruby-devel && dnf clean all
RUN gem install bundler
# Install Java (OpenJDK)
RUN dnf install -y java-17-openjdk-devel && dnf clean all
ENV JAVA_HOME=/usr/lib/jvm/java-17-openjdk
# Verify installations
RUN go version && \
rustc --version && \
ruby --version && \
java -version
Example 5: Minimal Image (Size-Optimized)ΒΆ
Remove unnecessary tools to minimize size:
# Start from scratch with only what you need
FROM rockylinux:9 AS builder
# Copy only specific tools from official image
FROM ghcr.io/jinalshah/devops/images/all-devops:latest AS source
FROM rockylinux:9
# Copy only Terraform and kubectl
COPY --from=source /usr/local/bin/terraform /usr/local/bin/
COPY --from=source /usr/local/bin/kubectl /usr/local/bin/
COPY --from=source /usr/local/bin/helm /usr/local/bin/
# Add minimal Python
RUN dnf install -y python3 && dnf clean all
CMD ["/bin/bash"]
Adding Custom ScriptsΒΆ
Single ScriptΒΆ
FROM ghcr.io/jinalshah/devops/images/all-devops:latest
# Add custom deployment script
COPY deploy.sh /usr/local/bin/deploy
RUN chmod +x /usr/local/bin/deploy
Script DirectoryΒΆ
FROM ghcr.io/jinalshah/devops/images/all-devops:latest
# Add script directory
COPY scripts/ /opt/company-scripts/
RUN chmod +x /opt/company-scripts/*.sh
# Add to PATH
ENV PATH="/opt/company-scripts:${PATH}"
Project structure:
.
βββ Dockerfile
βββ scripts/
βββ deploy-app.sh
βββ backup-db.sh
βββ security-scan.sh
Configuration ManagementΒΆ
Environment VariablesΒΆ
FROM ghcr.io/jinalshah/devops/images/all-devops:latest
# Set company-specific defaults
ENV COMPANY_NAME="Acme Corp"
ENV DEFAULT_AWS_REGION="us-west-2"
ENV DEFAULT_GCP_PROJECT="acme-prod"
ENV TERRAFORM_BACKEND="s3://acme-terraform-state"
Configuration FilesΒΆ
FROM ghcr.io/jinalshah/devops/images/all-devops:latest
# Copy configuration files
COPY configs/.terraformrc /root/.terraformrc
COPY configs/.ansible.cfg /root/.ansible.cfg
COPY configs/.gitconfig /root/.gitconfig
# Copy custom shell config
COPY configs/.zshrc.custom /root/.zshrc.d/company.zsh
Build Args for FlexibilityΒΆ
Define Build ArgsΒΆ
ARG BASE_IMAGE=ghcr.io/jinalshah/devops/images/all-devops:latest
ARG PYTHON_PACKAGES="requests pyyaml"
ARG NODE_PACKAGES="prettier eslint"
FROM ${BASE_IMAGE}
RUN pip3 install --no-cache-dir ${PYTHON_PACKAGES}
RUN npm install -g ${NODE_PACKAGES}
Build with Custom ArgsΒΆ
docker build \
--build-arg BASE_IMAGE=ghcr.io/jinalshah/devops/images/aws-devops:latest \
--build-arg PYTHON_PACKAGES="boto3 requests" \
--build-arg NODE_PACKAGES="serverless webpack" \
-t my-custom-devops:latest \
.
Best PracticesΒΆ
Layer OptimizationΒΆ
Minimize Layers
Combine RUN commands to reduce layers:
Bad (3 layers):
Good (1 layer):
Cache BustingΒΆ
# Add this to force rebuild from this point
ARG CACHE_BUST=1
# Subsequent layers will rebuild
COPY requirements.txt /tmp/
RUN pip3 install -r /tmp/requirements.txt
Version PinningΒΆ
Always Pin Versions
Bad (unpredictable):
Good (reproducible):
Cleanup After InstallΒΆ
Use .dockerignoreΒΆ
.dockerignore:
Testing Custom ImagesΒΆ
Validation ScriptΒΆ
#!/bin/bash
# test-custom-image.sh
IMAGE=$1
echo "Testing $IMAGE..."
# Test base tools
docker run --rm $IMAGE terraform version || exit 1
docker run --rm $IMAGE kubectl version --client || exit 1
# Test custom additions
docker run --rm $IMAGE python3 -c "import requests; print(requests.__version__)" || exit 1
docker run --rm $IMAGE node -e "console.log(require('prettier').version)" || exit 1
# Test custom scripts
docker run --rm $IMAGE which deploy || exit 1
echo "β
All tests passed!"
Run TestsΒΆ
CI/CD IntegrationΒΆ
GitHub ActionsΒΆ
name: Build Custom Image
on:
push:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build custom image
run: |
docker build -t company/devops-custom:${{ github.sha }} .
- name: Test image
run: |
./test-custom-image.sh company/devops-custom:${{ github.sha }}
- name: Push to registry
run: |
echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin
docker push company/devops-custom:${{ github.sha }}
GitLab CIΒΆ
build-custom:
stage: build
image: docker:latest
services:
- docker:dind
script:
- docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA .
- docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
Security ConsiderationsΒΆ
Scan Custom ImagesΒΆ
# Scan with Trivy
docker run --rm \
-v /var/run/docker.sock:/var/run/docker.sock \
aquasec/trivy image my-devops:latest
Sign ImagesΒΆ
Use Secrets ProperlyΒΆ
Never Bake Secrets
Never do this:
Instead, pass at runtime:
Maintenance StrategyΒΆ
Automated RebuildsΒΆ
# .github/workflows/rebuild.yml
name: Rebuild Custom Image
on:
schedule:
- cron: '0 2 * * 1' # Weekly, Monday 2 AM
workflow_dispatch:
jobs:
rebuild:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Pull latest base
run: docker pull ghcr.io/jinalshah/devops/images/all-devops:latest
- name: Build custom image
run: docker build -t my-devops:latest .
- name: Test
run: ./test-custom-image.sh my-devops:latest
- name: Push
run: docker push my-devops:latest
Version TrackingΒΆ
Add labels:
LABEL base.image="ghcr.io/jinalshah/devops/images/all-devops:1.0.abc1234"
LABEL custom.version="1.2.0"
LABEL custom.build-date="2025-01-15"
LABEL custom.maintainer="devops@company.com"
Check labels:
TroubleshootingΒΆ
Custom package installation fails
Problem: Package not found or version incompatible
Solutions:
-
Check package exists:
-
Try different version:
-
Install from source:
Image size too large
Problem: Custom image exceeds expected size
Solutions:
-
Check layer sizes:
-
Use multi-stage builds:
-
Clean up in same layer:
Build fails with permission errors
Problem: Cannot write files or install packages
Solutions:
-
Run as root (default in Dockerfile):
-
Fix ownership after:
Example Repository StructureΒΆ
custom-devops-image/
βββ Dockerfile
βββ Dockerfile.dev
βββ Dockerfile.prod
βββ .dockerignore
βββ .github/
β βββ workflows/
β βββ build.yml
βββ configs/
β βββ .terraformrc
β βββ .ansible.cfg
β βββ .gitconfig
βββ scripts/
β βββ deploy.sh
β βββ backup.sh
β βββ security-scan.sh
βββ tests/
β βββ test-image.sh
βββ requirements.txt
βββ package.json
βββ README.md
Next StepsΒΆ
- Build Images Guide - Building from source
- Optimization Guide - Reduce image size
- Multi-Platform Images - Build for multiple architectures
- Architecture Overview - Understand image layers