GitHub has evolved into a comprehensive DevOps platform that provides powerful tools for containerized application development, deployment, and management. From GitHub Actions for CI/CD automation to GitHub Container Registry for artifact storage and GitHub Codespaces for cloud development environments, GitHub offers a complete ecosystem for modern containerized workflows.
Table of Contents
- Overview
- GitHub Actions
- GitHub Container Registry
- GitHub Codespaces
- GitHub Packages
- Workflow Templates
- Container Security
- Environment Management
- Multi-Container Applications
- Kubernetes Integration
- Docker Integration
- Security Best Practices
- Monitoring and Observability
- Cost Optimization
- Best Practices
- Troubleshooting
- Resources
Overview
GitHub's container ecosystem provides integrated tools for the entire container lifecycle, from development to deployment and monitoring. The platform seamlessly integrates with popular container technologies and cloud providers, making it an ideal choice for teams building containerized applications.
Core Container Features
- GitHub Actions: CI/CD automation with container support
- GitHub Container Registry: Secure container image storage and distribution
- GitHub Codespaces: Cloud-based development environments
- GitHub Packages: Package and artifact management
- Security Scanning: Vulnerability detection for container images
- Environment Protection: Deployment controls and approvals
Integration Ecosystem
GitHub integrates seamlessly with:
- Container Platforms: Docker, Kubernetes, OpenShift
- Cloud Providers: AWS, Azure, Google Cloud, DigitalOcean
- Container Registries: Docker Hub, Amazon ECR, Azure ACR
- Monitoring Tools: Prometheus, Grafana, Datadog, New Relic
- Security Platforms: Snyk, Aqua Security, Twistlock
When to Use GitHub for Containers
GitHub's container features are ideal for:
- CI/CD Automation: Automated building, testing, and deployment
- Multi-Environment Workflows: Development, staging, and production pipelines
- Security-First Development: Integrated security scanning and compliance
- Team Collaboration: Code review and deployment approval workflows
- Microservices Architecture: Managing multiple containerized services
- Cloud-Native Applications: Kubernetes and serverless deployments
GitHub Actions
GitHub Actions provides powerful CI/CD automation capabilities specifically designed for containerized applications. It offers native Docker support, extensive marketplace of actions, and flexible workflow configurations.
Container Workflow Basics
Simple Docker Build and Push
name: Docker Build and Push
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main ]
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
build-and-push:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to Container Registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=ref,event=branch
type=ref,event=pr
type=sha,prefix={{branch}}-
type=raw,value=latest,enable={{is_default_branch}}
- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
context: .
platforms: linux/amd64,linux/arm64
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
Multi-Stage Build with Testing
name: Advanced Docker Workflow
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main ]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run tests in container
run: |
docker build --target test -t app:test .
docker run --rm app:test
security-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build image for scanning
run: docker build -t app:scan .
- name: Run Trivy vulnerability scanner
uses: aquasecurity/trivy-action@master
with:
image-ref: 'app:scan'
format: 'sarif'
output: 'trivy-results.sarif'
- name: Upload Trivy scan results
uses: github/codeql-action/upload-sarif@v2
with:
sarif_file: 'trivy-results.sarif'
build-and-deploy:
needs: [test, security-scan]
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: |
ghcr.io/${{ github.repository }}:latest
ghcr.io/${{ github.repository }}:${{ github.sha }}
platforms: linux/amd64,linux/arm64
cache-from: type=gha
cache-to: type=gha,mode=max
Advanced Workflow Patterns
Matrix Builds for Multiple Environments
name: Matrix Container Builds
on:
push:
branches: [ main ]
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
include:
- environment: development
dockerfile: Dockerfile.dev
tag-suffix: dev
- environment: staging
dockerfile: Dockerfile.staging
tag-suffix: staging
- environment: production
dockerfile: Dockerfile
tag-suffix: prod
steps:
- uses: actions/checkout@v4
- name: Build ${{ matrix.environment }} image
run: |
docker build -f ${{ matrix.dockerfile }} \
-t ghcr.io/${{ github.repository }}:${{ matrix.tag-suffix }} .
- name: Push to registry
run: |
echo ${{ secrets.GITHUB_TOKEN }} | docker login ghcr.io -u ${{ github.actor }} --password-stdin
docker push ghcr.io/${{ github.repository }}:${{ matrix.tag-suffix }}
Conditional Workflows with Environments
name: Environment-Based Deployment
on:
push:
branches: [ main, staging, develop ]
jobs:
determine-environment:
runs-on: ubuntu-latest
outputs:
environment: ${{ steps.set-env.outputs.environment }}
steps:
- name: Set environment
id: set-env
run: |
if [[ "${{ github.ref }}" == "refs/heads/main" ]]; then
echo "environment=production" >> $GITHUB_OUTPUT
elif [[ "${{ github.ref }}" == "refs/heads/staging" ]]; then
echo "environment=staging" >> $GITHUB_OUTPUT
else
echo "environment=development" >> $GITHUB_OUTPUT
fi
deploy:
needs: determine-environment
runs-on: ubuntu-latest
environment: ${{ needs.determine-environment.outputs.environment }}
steps:
- uses: actions/checkout@v4
- name: Deploy to ${{ needs.determine-environment.outputs.environment }}
run: |
echo "Deploying to ${{ needs.determine-environment.outputs.environment }}"
# Add your deployment logic here
Container Service Integration
Docker Compose Services
name: Integration Tests with Services
on: [push, pull_request]
jobs:
integration-tests:
runs-on: ubuntu-latest
services:
postgres:
image: postgres:15
env:
POSTGRES_DB: testdb
POSTGRES_USER: testuser
POSTGRES_PASSWORD: testpass
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
- 5432:5432
redis:
image: redis:7-alpine
options: >-
--health-cmd "redis-cli ping"
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
- 6379:6379
steps:
- uses: actions/checkout@v4
- name: Build application image
run: docker build -t app:test .
- name: Run integration tests
run: |
docker run --rm --network host \
-e DATABASE_URL=postgresql://testuser:testpass@localhost:5432/testdb \
-e REDIS_URL=redis://localhost:6379 \
app:test npm run test:integration
GitHub Container Registry
GitHub Container Registry (GHCR) provides secure, scalable container image storage with fine-grained access controls and seamless integration with GitHub workflows.
Registry Configuration
Authentication Setup
# Login with Personal Access Token
echo $PAT | docker login ghcr.io -u USERNAME --password-stdin
# Login with GitHub Actions token (in workflow)
echo ${{ secrets.GITHUB_TOKEN }} | docker login ghcr.io -u ${{ github.actor }} --password-stdin
# Using GitHub CLI
gh auth token | docker login ghcr.io -u USERNAME --password-stdin
Image Naming Conventions
# Repository images
ghcr.io/OWNER/REPOSITORY/IMAGE_NAME:TAG
# Examples
ghcr.io/mycompany/web-app:latest
ghcr.io/mycompany/web-app:v1.2.3
ghcr.io/mycompany/web-app:pr-123
ghcr.io/mycompany/api-service:main-abc1234
Advanced Registry Features
Multi-Platform Images
- name: Build multi-platform images
uses: docker/build-push-action@v5
with:
context: .
platforms: linux/amd64,linux/arm64,linux/arm/v7
push: true
tags: ghcr.io/${{ github.repository }}:latest
Image Signing and Verification
- name: Install cosign
uses: sigstore/cosign-installer@v3
- name: Sign container image
run: |
cosign sign --yes ghcr.io/${{ github.repository }}@${{ steps.build.outputs.digest }}
env:
COSIGN_EXPERIMENTAL: 1
Registry Management
Lifecycle Policies
# .github/workflows/cleanup.yml
name: Registry Cleanup
on:
schedule:
- cron: '0 2 * * 0' # Weekly cleanup
workflow_dispatch:
jobs:
cleanup:
runs-on: ubuntu-latest
steps:
- name: Delete old images
uses: actions/delete-package-versions@v4
with:
package-name: 'my-app'
package-type: 'container'
min-versions-to-keep: 10
delete-only-untagged-versions: 'true'
Image Vulnerability Scanning
- name: Scan image with Trivy
uses: aquasecurity/trivy-action@master
with:
image-ref: 'ghcr.io/${{ github.repository }}:${{ github.sha }}'
format: 'json'
output: 'trivy-results.json'
- name: Upload scan results
uses: github/codeql-action/upload-sarif@v2
if: always()
with:
sarif_file: 'trivy-results.sarif'
GitHub Codespaces
GitHub Codespaces provides cloud-based development environments with full container support, enabling consistent development experiences across teams.
Codespace Configuration
Basic devcontainer.json
{
"name": "Node.js & TypeScript",
"image": "mcr.microsoft.com/devcontainers/typescript-node:18-bullseye",
"features": {
"ghcr.io/devcontainers/features/docker-in-docker:2": {},
"ghcr.io/devcontainers/features/kubectl-helm-minikube:1": {},
"ghcr.io/devcontainers/features/github-cli:1": {}
},
"customizations": {
"vscode": {
"extensions": [
"ms-vscode.vscode-typescript-next",
"ms-azuretools.vscode-docker",
"ms-kubernetes-tools.vscode-kubernetes-tools",
"github.vscode-pull-request-github"
]
}
},
"postCreateCommand": "npm install",
"postStartCommand": "docker version",
"mounts": [
"source=/var/run/docker.sock,target=/var/run/docker.sock,type=bind"
],
"forwardPorts": [3000, 8080],
"portsAttributes": {
"3000": {
"label": "Application",
"protocol": "http"
}
}
}
Custom Development Container
# .devcontainer/Dockerfile
FROM mcr.microsoft.com/devcontainers/base:bullseye
# Install additional tools
RUN apt-get update && apt-get install -y \
postgresql-client \
redis-tools \
kubectl \
helm \
&& rm -rf /var/lib/apt/lists/*
# Install Node.js
RUN curl -fsSL https://deb.nodesource.com/setup_18.x | bash - \
&& apt-get install -y nodejs
# Install Docker
RUN curl -fsSL https://get.docker.com -o get-docker.sh \
&& sh get-docker.sh
# Install development tools
RUN npm install -g typescript ts-node nodemon
# Setup user
USER vscode
Docker Compose Development Environment
# .devcontainer/docker-compose.yml
version: '3.8'
services:
app:
build:
context: ..
dockerfile: .devcontainer/Dockerfile
volumes:
- ..:/workspace:cached
- /var/run/docker.sock:/var/run/docker-host.sock
command: sleep infinity
network_mode: service:db
db:
image: postgres:15
restart: unless-stopped
environment:
POSTGRES_DB: devdb
POSTGRES_USER: devuser
POSTGRES_PASSWORD: devpass
volumes:
- postgres-data:/var/lib/postgresql/data
volumes:
postgres-data:
Advanced Codespace Features
Prebuilds Configuration
{
"$schema": "https://raw.githubusercontent.com/devcontainers/spec/main/schemas/devContainer.schema.json",
"name": "Full Stack Development",
"dockerComposeFile": "docker-compose.yml",
"service": "app",
"workspaceFolder": "/workspace",
"features": {
"ghcr.io/devcontainers/features/docker-outside-of-docker:1": {},
"ghcr.io/devcontainers/features/kubernetes-helm:1": {}
},
"customizations": {
"vscode": {
"settings": {
"terminal.integrated.defaultProfile.linux": "bash"
}
}
},
"onCreateCommand": {
"install-deps": "npm install",
"setup-db": "docker-compose up -d db && sleep 10 && npm run db:migrate"
},
"postStartCommand": "npm run dev",
"secrets": {
"DATABASE_URL": {
"description": "Database connection string"
}
}
}
GitHub Packages
GitHub Packages provides comprehensive package management for containers and other artifacts, with integrated security scanning and access controls.
Package Configuration
Docker Package Publishing
name: Publish Package
on:
release:
types: [published]
jobs:
publish:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract version
id: version
run: echo "VERSION=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT
- name: Build and push
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: |
ghcr.io/${{ github.repository }}:latest
ghcr.io/${{ github.repository }}:${{ steps.version.outputs.VERSION }}
platforms: linux/amd64,linux/arm64
Package Metadata
- name: Add package metadata
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: ghcr.io/${{ github.repository }}:latest
labels: |
org.opencontainers.image.title=${{ github.event.repository.name }}
org.opencontainers.image.description=${{ github.event.repository.description }}
org.opencontainers.image.url=${{ github.event.repository.html_url }}
org.opencontainers.image.source=${{ github.event.repository.clone_url }}
org.opencontainers.image.version=${{ steps.version.outputs.VERSION }}
org.opencontainers.image.created=${{ steps.date.outputs.date }}
org.opencontainers.image.revision=${{ github.sha }}
org.opencontainers.image.licenses=${{ github.event.repository.license.spdx_id }}
Workflow Templates
Comprehensive Application Workflow
name: Full Application Workflow
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main ]
release:
types: [ published ]
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
changes:
runs-on: ubuntu-latest
outputs:
app: ${{ steps.filter.outputs.app }}
docker: ${{ steps.filter.outputs.docker }}
steps:
- uses: actions/checkout@v4
- uses: dorny/paths-filter@v2
id: filter
with:
filters: |
app:
- 'src/**'
- 'package*.json'
docker:
- 'Dockerfile*'
- '.dockerignore'
test:
needs: changes
if: needs.changes.outputs.app == 'true'
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [16, 18, 20]
services:
postgres:
image: postgres:15
env:
POSTGRES_PASSWORD: postgres
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: npm
- run: npm ci
- run: npm run test:unit
- run: npm run test:integration
security:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run security audit
run: npm audit --audit-level moderate
- name: Run CodeQL analysis
uses: github/codeql-action/analyze@v2
with:
languages: javascript
build:
needs: [changes, test, security]
if: needs.changes.outputs.app == 'true' || needs.changes.outputs.docker == 'true'
runs-on: ubuntu-latest
outputs:
image-digest: ${{ steps.build.outputs.digest }}
steps:
- uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to 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
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=ref,event=branch
type=ref,event=pr
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=raw,value=latest,enable={{is_default_branch}}
- name: Build and push
id: build
uses: docker/build-push-action@v5
with:
context: .
platforms: linux/amd64,linux/arm64
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
scan:
needs: build
if: needs.build.outputs.image-digest
runs-on: ubuntu-latest
steps:
- name: Run Trivy vulnerability scanner
uses: aquasecurity/trivy-action@master
with:
image-ref: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}@${{ needs.build.outputs.image-digest }}
format: sarif
output: trivy-results.sarif
- name: Upload scan results
uses: github/codeql-action/upload-sarif@v2
with:
sarif_file: trivy-results.sarif
deploy-staging:
needs: [build, scan]
if: github.ref == 'refs/heads/develop'
runs-on: ubuntu-latest
environment: staging
steps:
- name: Deploy to staging
run: |
echo "Deploying ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}@${{ needs.build.outputs.image-digest }}"
# Add your deployment logic
deploy-production:
needs: [build, scan]
if: github.event_name == 'release'
runs-on: ubuntu-latest
environment: production
steps:
- name: Deploy to production
run: |
echo "Deploying ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}@${{ needs.build.outputs.image-digest }}"
# Add your deployment logic
Microservices Workflow
name: Microservices Build and Deploy
on:
push:
branches: [ main ]
jobs:
changes:
runs-on: ubuntu-latest
outputs:
services: ${{ steps.filter.outputs.changes }}
steps:
- uses: actions/checkout@v4
- uses: dorny/paths-filter@v2
id: filter
with:
filters: |
api: 'services/api/**'
web: 'services/web/**'
worker: 'services/worker/**'
build:
needs: changes
if: needs.changes.outputs.services != '[]'
runs-on: ubuntu-latest
strategy:
matrix:
service: ${{ fromJson(needs.changes.outputs.services) }}
steps:
- uses: actions/checkout@v4
- name: Build ${{ matrix.service }}
uses: docker/build-push-action@v5
with:
context: services/${{ matrix.service }}
push: true
tags: ghcr.io/${{ github.repository }}/${{ matrix.service }}:${{ github.sha }}
Container Security
Security Scanning Integration
Comprehensive Security Pipeline
name: Security Scanning
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
schedule:
- cron: '0 6 * * *' # Daily scans
jobs:
vulnerability-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build image
run: docker build -t scan-target .
- name: Trivy vulnerability scan
uses: aquasecurity/trivy-action@master
with:
image-ref: 'scan-target'
format: 'json'
output: 'trivy-results.json'
severity: 'CRITICAL,HIGH'
- name: Snyk container scan
uses: snyk/actions/docker@master
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
with:
image: scan-target
args: --severity-threshold=medium
secrets-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: TruffleHog OSS
uses: trufflesecurity/trufflehog@main
with:
path: ./
base: main
head: HEAD
extra_args: --debug --only-verified
license-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: License compatibility check
uses: fossa-contrib/fossa-action@v2
with:
api-key: ${{ secrets.FOSSA_API_KEY }}
Runtime Security
- name: Generate SBOM
uses: anchore/sbom-action@v0
with:
image: ghcr.io/${{ github.repository }}:latest
format: spdx-json
- name: Sign image with cosign
uses: sigstore/cosign-installer@v3
- run: |
cosign sign --yes ghcr.io/${{ github.repository }}@${{ steps.build.outputs.digest }}
env:
COSIGN_EXPERIMENTAL: 1
- name: Verify signature
run: |
cosign verify ghcr.io/${{ github.repository }}@${{ steps.build.outputs.digest }}
env:
COSIGN_EXPERIMENTAL: 1
Environment Management
Environment Protection Rules
Production Environment Configuration
# .github/environments/production.yml
name: production
protection_rules:
- type: required_reviewers
required_reviewers:
- team: platform-team
- user: lead-developer
- type: wait_timer
wait_timer: 300 # 5 minutes
- type: branch_policy
branch_policy:
protected_branches: true
custom_branch_policies: false
deployment_branch_policy:
protected_branches: true
custom_branch_policies: false
environment_variables:
- name: ENVIRONMENT
value: production
- name: LOG_LEVEL
value: info
secrets:
- DATABASE_URL
- API_SECRET_KEY
- REDIS_URL
Environment-Specific Deployments
name: Environment Deployment
on:
push:
branches: [ main, staging, develop ]
jobs:
deploy:
runs-on: ubuntu-latest
environment: ${{ github.ref_name == 'main' && 'production' || (github.ref_name == 'staging' && 'staging' || 'development') }}
steps:
- name: Deploy to ${{ vars.ENVIRONMENT }}
run: |
echo "Deploying to ${{ vars.ENVIRONMENT }}"
kubectl set image deployment/app app=ghcr.io/${{ github.repository }}:${{ github.sha }}
kubectl rollout status deployment/app
Multi-Container Applications
Docker Compose in CI/CD
name: Multi-Container Application
on: [push, pull_request]
jobs:
integration-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Create environment file
run: |
cat << EOF > .env
POSTGRES_DB=testdb
POSTGRES_USER=testuser
POSTGRES_PASSWORD=testpass
REDIS_URL=redis://redis:6379
API_URL=http://api:3000
EOF
- name: Start services
run: docker-compose -f docker-compose.test.yml up -d
- name: Wait for services
run: |
timeout 60 bash -c 'until docker-compose -f docker-compose.test.yml exec -T api curl -f http://localhost:3000/health; do sleep 2; done'
- name: Run integration tests
run: docker-compose -f docker-compose.test.yml exec -T api npm run test:integration
- name: Collect logs
if: failure()
run: docker-compose -f docker-compose.test.yml logs
- name: Cleanup
if: always()
run: docker-compose -f docker-compose.test.yml down -v
Helm Chart Testing
- name: Install Helm
uses: azure/setup-helm@v3
- name: Add chart dependencies
run: helm dependency update ./chart
- name: Lint Helm chart
run: helm lint ./chart
- name: Test Helm templates
run: |
helm template test ./chart \
--set image.tag=${{ github.sha }} \
--set environment=test > rendered.yaml
- name: Validate Kubernetes manifests
uses: instrumenta/kubeval-action@master
with:
files: rendered.yaml
Kubernetes Integration
GitOps Workflow
name: GitOps Deployment
on:
push:
branches: [ main ]
jobs:
update-manifests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
token: ${{ secrets.GITOPS_TOKEN }}
repository: myorg/gitops-repo
- name: Update image tags
run: |
yq eval '.spec.template.spec.containers[0].image = "ghcr.io/${{ github.repository }}:${{ github.sha }}"' \
-i k8s/production/deployment.yaml
- name: Commit changes
run: |
git config --local user.email "action@github.com"
git config --local user.name "GitHub Action"
git add k8s/production/deployment.yaml
git commit -m "Update image to ${{ github.sha }}" || exit 0
git push
Kubernetes Deployment
- name: Deploy to Kubernetes
uses: azure/k8s-deploy@v1
with:
manifests: |
k8s/deployment.yaml
k8s/service.yaml
k8s/ingress.yaml
images: |
ghcr.io/${{ github.repository }}:${{ github.sha }}
kubectl-version: 'latest'
Docker Integration
Advanced Docker Features
Multi-Stage Optimization
# syntax=docker/dockerfile:1
FROM node:18-alpine AS base
WORKDIR /app
COPY package*.json ./
FROM base AS deps
RUN npm ci --only=production && npm cache clean --force
FROM base AS build
RUN npm ci
COPY . .
RUN npm run build
FROM base AS test
RUN npm ci
COPY . .
RUN npm test
FROM node:18-alpine AS final
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY --from=build /app/dist ./dist
COPY --from=build /app/package*.json ./
EXPOSE 3000
USER node
CMD ["npm", "start"]
Docker Build Arguments
- name: Build with build args
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: ghcr.io/${{ github.repository }}:latest
build-args: |
NODE_ENV=production
BUILD_VERSION=${{ github.sha }}
BUILD_DATE=${{ steps.date.outputs.date }}
Security Best Practices
Secrets Management
Secure Secret Usage
name: Secure Deployment
on:
push:
branches: [ main ]
jobs:
deploy:
runs-on: ubuntu-latest
environment: production
steps:
- uses: actions/checkout@v4
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
role-session-name: GitHubActions
aws-region: us-west-2
- name: Login to Amazon ECR
uses: aws-actions/amazon-ecr-login@v2
- name: Deploy with secrets
run: |
kubectl create secret generic app-secrets \
--from-literal=database-url="${{ secrets.DATABASE_URL }}" \
--from-literal=api-key="${{ secrets.API_KEY }}" \
--dry-run=client -o yaml | kubectl apply -f -
Least Privilege Access
permissions:
contents: read
packages: write
security-events: write
id-token: write # for OIDC
Container Hardening
# Use non-root user
FROM node:18-alpine
RUN addgroup -g 1001 -S nodejs
RUN adduser -S nextjs -u 1001
# Set security options
LABEL security.non-root=true
LABEL security.readonly-rootfs=true
# Copy files with proper ownership
COPY --chown=nextjs:nodejs . .
USER nextjs
Monitoring and Observability
Application Performance Monitoring
- name: Deploy with monitoring
run: |
kubectl apply -f - <<EOF
apiVersion: apps/v1
kind: Deployment
metadata:
name: app
labels:
app: myapp
spec:
template:
spec:
containers:
- name: app
image: ghcr.io/${{ github.repository }}:${{ github.sha }}
env:
- name: OTEL_EXPORTER_OTLP_ENDPOINT
value: "http://otel-collector:4317"
- name: OTEL_SERVICE_NAME
value: "myapp"
- name: OTEL_SERVICE_VERSION
value: "${{ github.sha }}"
EOF
Health Check Integration
- name: Wait for deployment
run: |
kubectl rollout status deployment/app --timeout=300s
- name: Health check
run: |
ENDPOINT=$(kubectl get service app -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
curl -f http://$ENDPOINT/health || exit 1
- name: Smoke tests
run: |
npm run test:smoke -- --endpoint=http://$ENDPOINT
Cost Optimization
Efficient Resource Usage
Optimized Builds
- name: Build with cache optimization
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: ghcr.io/${{ github.repository }}:latest
cache-from: |
type=gha
type=registry,ref=ghcr.io/${{ github.repository }}:buildcache
cache-to: |
type=gha,mode=max
type=registry,ref=ghcr.io/${{ github.repository }}:buildcache,mode=max
Conditional Workflows
- name: Check for changes
uses: dorny/paths-filter@v2
id: changes
with:
filters: |
src:
- 'src/**'
- 'Dockerfile'
- 'package*.json'
- name: Skip build if no changes
if: steps.changes.outputs.src != 'true'
run: echo "No changes detected, skipping build"
Resource Management
# Cleanup old images
- name: Delete old container images
uses: actions/delete-package-versions@v4
with:
package-name: ${{ env.IMAGE_NAME }}
package-type: container
min-versions-to-keep: 5
delete-only-untagged-versions: true
Best Practices
Workflow Organization
- Modular Workflows: Break complex workflows into reusable components
- Matrix Strategies: Use matrix builds for multiple environments/versions
- Conditional Execution: Use path filters and conditions to optimize builds
- Caching Strategies: Implement effective caching for dependencies and builds
- Parallel Execution: Run independent jobs in parallel for faster execution
Security Guidelines
- Least Privilege: Use minimal required permissions
- Secret Management: Store sensitive data in GitHub Secrets
- Image Scanning: Always scan container images for vulnerabilities
- Dependency Updates: Keep dependencies and base images updated
- Access Controls: Use branch protection and environment protection rules
Performance Optimization
- Build Optimization: Use multi-stage builds and build caches
- Resource Efficiency: Right-size runners and containers
- Network Optimization: Use registry caching and proximity
- Monitoring: Track workflow performance and resource usage
- Cleanup: Regularly clean up old artifacts and images
Code Quality
- name: Code quality checks
run: |
npm run lint
npm run type-check
npm run format-check
- name: Test coverage
run: |
npm run test:coverage
npx codecov
Troubleshooting
Common Issues and Solutions
Build Failures
- name: Debug build failure
if: failure()
run: |
docker system df
docker buildx ls
docker buildx inspect
- name: Build with verbose output
uses: docker/build-push-action@v5
with:
context: .
push: false
tags: debug:latest
build-args: |
BUILDKIT_PROGRESS=plain
Registry Authentication
# Debug registry login
echo ${{ secrets.GITHUB_TOKEN }} | docker login ghcr.io -u ${{ github.actor }} --password-stdin
# Check token permissions
curl -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
https://api.github.com/user/packages?package_type=container
Performance Issues
- name: Monitor resource usage
run: |
free -h
df -h
docker system df
- name: Optimize build context
run: |
echo "Build context size:"
tar -czf - . | wc -c
Debugging Workflows
- name: Debug information
run: |
echo "Runner OS: ${{ runner.os }}"
echo "GitHub Context: ${{ toJson(github) }}"
echo "Environment: ${{ env.ENVIRONMENT }}"
docker version
kubectl version --client
Log Analysis
# View workflow logs
gh run view $RUN_ID --log
# Download logs
gh run download $RUN_ID
# Monitor live logs
gh run watch $RUN_ID
Resources
Official Documentation
Container Integration Guides
Security Resources
Community Resources
Learning Resources
- GitHub Learning Lab
- GitHub Actions Workflow Examples
- Container CI/CD Patterns
- Kubernetes CI/CD Examples