Skip to content
Draft
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 62 additions & 3 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,25 @@ jobs:
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Log in to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Extract metadata for Docker
id: meta
uses: docker/metadata-action@v5
with:
images: ghcr.io/${{ github.repository }}/demo
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 Docker image (test build)
run: |
echo "🐳 Building Docker image for testing..."
Expand All @@ -365,14 +384,29 @@ jobs:
docker run -d --name nlwebnet-test -p 8080:8080 nlwebnet-demo:test

# Wait for container to start
sleep 5
sleep 10

# Check if container is running (basic test)
if docker ps | grep nlwebnet-test; then
echo "✅ Container is running"

# Test health endpoint if available
echo "🔍 Testing health endpoint..."
for i in {1..5}; do
if curl -f -s http://localhost:8080/health >/dev/null 2>&1; then
echo "✅ Health endpoint responds successfully"
break
elif [ $i -eq 5 ]; then
echo "⚠️ Health endpoint not responding (may be expected)"
else
echo "⏳ Waiting for health endpoint... (attempt $i/5)"
sleep 3
fi
done

# Show container logs for debugging
echo "📋 Container logs:"
docker logs nlwebnet-test
echo "📋 Recent container logs:"
docker logs --tail 10 nlwebnet-test
else
echo "❌ Container failed to start"
docker logs nlwebnet-test
Expand All @@ -383,6 +417,31 @@ jobs:
docker stop nlwebnet-test
docker rm nlwebnet-test

- name: Build and push Docker image to GHCR
uses: docker/build-push-action@v5
with:
context: .
file: deployment/docker/Dockerfile
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max

- name: Generate deployment summary
run: |
echo "## 🐳 Docker Build Summary" >> $GITHUB_STEP_SUMMARY
echo "- **Image**: \`ghcr.io/${{ github.repository }}/demo\`" >> $GITHUB_STEP_SUMMARY
echo "- **Tags**: ${{ steps.meta.outputs.tags }}" >> $GITHUB_STEP_SUMMARY
echo "- **Build Status**: ✅ Success" >> $GITHUB_STEP_SUMMARY
echo "- **Smoke Test**: ✅ Passed" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "### Usage" >> $GITHUB_STEP_SUMMARY
echo "\`\`\`bash" >> $GITHUB_STEP_SUMMARY
echo "docker pull ${{ steps.meta.outputs.tags }}" >> $GITHUB_STEP_SUMMARY
echo "docker run -p 8080:8080 ${{ steps.meta.outputs.tags }}" >> $GITHUB_STEP_SUMMARY
Copy link

Copilot AI Aug 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The steps.meta.outputs.tags contains multiple tags separated by newlines, but this will only show the first tag in the docker pull command. This could confuse users about which tag to actually pull. Consider using a specific tag like latest or the first tag from the list.

Suggested change
echo "docker run -p 8080:8080 ${{ steps.meta.outputs.tags }}" >> $GITHUB_STEP_SUMMARY
FIRST_TAG="$(echo "${{ steps.meta.outputs.tags }}" | head -n1)"
echo "\`\`\`bash" >> $GITHUB_STEP_SUMMARY
echo "docker pull $FIRST_TAG" >> $GITHUB_STEP_SUMMARY
echo "docker run -p 8080:8080 $FIRST_TAG" >> $GITHUB_STEP_SUMMARY

Copilot uses AI. Check for mistakes.

Copy link

Copilot AI Aug 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same issue as the previous line - steps.meta.outputs.tags contains multiple tags but only the first will be used in the docker run command, which may not be the intended tag for users to run.

Suggested change
echo "docker run -p 8080:8080 ${{ steps.meta.outputs.tags }}" >> $GITHUB_STEP_SUMMARY
FIRST_TAG=$(echo "${{ steps.meta.outputs.tags }}" | awk -F',' '{print $1}')
echo "docker pull $FIRST_TAG" >> $GITHUB_STEP_SUMMARY
echo "docker run -p 8080:8080 $FIRST_TAG" >> $GITHUB_STEP_SUMMARY

Copilot uses AI. Check for mistakes.

echo "\`\`\`" >> $GITHUB_STEP_SUMMARY

# Alternative: .NET SDK Container Build (modern approach)
dotnet-container-build:
runs-on: ubuntu-latest
Expand Down
16 changes: 14 additions & 2 deletions deployment/docker/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,15 @@ WORKDIR /src
ENV DOTNET_SKIP_FIRST_TIME_EXPERIENCE=true
ENV DOTNET_CLI_TELEMETRY_OPTOUT=true
ENV NUGET_XMLDOC_MODE=skip
# Configure .NET to handle SSL issues in Docker environment
ENV DOTNET_SYSTEM_NET_HTTP_USESOCKETSHTTPHANDLER=0
ENV NUGET_CERT_REVOCATION_MODE=offline

# Update package lists and install certificates
RUN apt-get update && \
apt-get install -y ca-certificates && \
update-ca-certificates && \
rm -rf /var/lib/apt/lists/*

# Copy solution file and project files for dependency resolution
COPY ["NLWebNet.sln", "./"]
Expand All @@ -18,8 +27,11 @@ COPY ["src/NLWebNet/NLWebNet.csproj", "src/NLWebNet/"]
COPY ["samples/Demo/NLWebNet.Demo.csproj", "samples/Demo/"]
COPY ["tests/NLWebNet.Tests/NLWebNet.Tests.csproj", "tests/NLWebNet.Tests/"]

# Restore dependencies
RUN dotnet restore "samples/Demo/NLWebNet.Demo.csproj"
# Restore dependencies with SSL workarounds
RUN dotnet restore "samples/Demo/NLWebNet.Demo.csproj" \
--disable-parallel \
--verbosity minimal \
--force

# Copy source code
COPY . .
Expand Down
141 changes: 141 additions & 0 deletions deployment/docker/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
# Docker Build Guide for NLWebNet

This document provides guidance for building Docker containers for the NLWebNet Demo application, including workarounds for common SSL issues in certain environments.

## Quick Start

### Standard Build (GitHub Actions / CI)

For CI/CD environments with proper SSL configuration:

```bash
docker build -f deployment/docker/Dockerfile -t nlwebnet-demo:latest .
```

### Local Development Build

For local development environments that may have SSL certificate issues:

```bash
# Use the automated build script that handles SSL issues
./deployment/docker/build-docker.sh
```

## Build Approaches

### 1. Standard Dockerfile (Recommended for CI/CD)

**File**: `deployment/docker/Dockerfile`

- Restores NuGet packages during Docker build
- Optimized for CI/CD environments
- Uses multi-stage build for minimal final image
- Includes SSL certificate handling improvements

### 2. Pre-built Approach (Fallback for SSL Issues)

**File**: `deployment/docker/Dockerfile.pre-built`

- Requires packages to be restored on host before build
- Bypasses SSL issues by using `--no-restore`
- Useful for environments with network restrictions

## SSL Certificate Issues

### Problem

In some Docker environments, you may encounter SSL certificate validation errors:

```
error NU1301: Unable to load the service index for source https://api.nuget.org/v3/index.json
The remote certificate is invalid because of errors in the certificate chain: UntrustedRoot
```

### Solutions

1. **Use GitHub Actions** (Recommended)
- CI/CD environments typically have proper SSL configuration
- The workflow automatically builds and publishes to GHCR

2. **Pre-restore packages locally**:
```bash
dotnet restore
docker build -f deployment/docker/Dockerfile.pre-built -t nlwebnet-demo .
```

3. **Use the automated build script**:
```bash
./deployment/docker/build-docker.sh
```

## Testing the Container

### Health Check

The container includes a health check endpoint:

```bash
# Start the container
docker run -p 8080:8080 nlwebnet-demo:latest

# Test health endpoint
curl http://localhost:8080/health
```

### Automated Smoke Test

Use the provided smoke test script:

```bash
# Run smoke test on built image
./deployment/docker/smoke-test.sh nlwebnet-demo:latest
```

## GitHub Container Registry (GHCR)

The GitHub Actions workflow automatically publishes images to GHCR:

```bash
# Pull from GHCR
docker pull ghcr.io/nlweb-ai/nlweb-net/demo:latest

# Run the image
docker run -p 8080:8080 ghcr.io/nlweb-ai/nlweb-net/demo:latest
```

## Environment Variables

Key environment variables for the container:

- `ASPNETCORE_ENVIRONMENT`: Set to `Production` by default
- `ASPNETCORE_URLS`: Set to `http://+:8080`
- `ASPNETCORE_HTTP_PORTS`: Set to `8080`

## Security

- Container runs as non-root user (`nlwebnet`)
- Uses minimal ASP.NET Core runtime image
- Includes security best practices

## Troubleshooting

### Build Issues

1. **SSL Certificate Errors**: Use pre-built approach or build in CI
2. **Package Restore Fails**: Ensure internet connectivity and DNS resolution
3. **Permission Denied**: Check Docker daemon permissions

### Runtime Issues

1. **Container won't start**: Check logs with `docker logs <container-name>`
2. **Health check fails**: Verify port 8080 is accessible
3. **Application errors**: Check environment variables and configuration

## Contributing

When making changes to Docker configuration:

1. Test both Dockerfile approaches
2. Update this documentation
3. Verify the smoke test passes
4. Ensure GitHub Actions workflow succeeds
32 changes: 32 additions & 0 deletions deployment/docker/build-docker.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#!/bin/bash
Copy link

Copilot AI Aug 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider adding set -euo pipefail instead of just set -e to make the script more robust by failing on undefined variables and pipe failures.

Copilot uses AI. Check for mistakes.

# Build script for Docker container in environments with SSL issues
# This script prepares a build-ready environment and builds the Docker image

set -euo pipefail

echo "🔧 Building NLWebNet Docker image with SSL workarounds..."

# Check if we're in the right directory
if [ ! -f "NLWebNet.sln" ]; then
echo "❌ Error: Must be run from repository root directory"
exit 1
fi

# Pre-restore packages locally to avoid SSL issues in Docker
echo "📦 Pre-restoring packages locally..."
dotnet restore

# Build the Docker image with appropriate strategy based on environment
echo "🐳 Building Docker image..."

# Build the Docker image
if docker build -f deployment/docker/Dockerfile . -t nlwebnet-demo:latest; then
echo "✅ Docker build successful"
else
echo "❌ Docker build failed"
exit 1
fi

echo "🎉 Docker image build completed successfully!"
echo "📝 To run the container: docker run -p 8080:8080 nlwebnet-demo:latest"
echo "🔍 To test health endpoint: curl http://localhost:8080/health"
Loading
Loading