This commit is contained in:
Oli Passey
2025-06-27 11:12:24 +01:00
parent 191184ba5e
commit 448af85a38
4 changed files with 544 additions and 0 deletions

214
AZURE-DEVOPS-SETUP.md Normal file
View File

@@ -0,0 +1,214 @@
# Azure DevOps Pipeline Setup Guide
This guide will help you set up the Azure DevOps pipeline to automatically build and push your Price Tracker Docker images to your registry at `dock.ptslondon.co.uk`.
## Prerequisites
1. Azure DevOps project with your Price Tracker repository
2. Access to your Docker registry at `dock.ptslondon.co.uk`
3. Admin permissions in Azure DevOps to create service connections
## Step 1: Create Docker Registry Service Connection
1. In your Azure DevOps project, go to **Project Settings** (bottom left)
2. Under **Pipelines**, click **Service connections**
3. Click **New service connection**
4. Select **Docker Registry** and click **Next**
5. Choose **Others** for registry type
6. Fill in the details:
- **Docker Registry**: `https://dock.ptslondon.co.uk`
- **Docker ID**: Your registry username
- **Docker Password**: Your registry password
- **Service connection name**: `dock-ptslondon-connection`
7. Check **Grant access permission to all pipelines**
8. Click **Save**
## Step 2: Choose Your Pipeline
You have two pipeline options:
### Basic Pipeline (`azure-pipelines.yml`)
- Simple build and push
- Basic testing
- Good for getting started
### Advanced Pipeline (`azure-pipelines-advanced.yml`)
- Multi-stage deployment
- Security scanning with Trivy
- Separate dev/prod environments
- More comprehensive testing
## Step 3: Create the Pipeline
1. In Azure DevOps, go to **Pipelines****Pipelines**
2. Click **New pipeline**
3. Select **Azure Repos Git** (or your source)
4. Select your repository
5. Choose **Existing Azure Pipelines YAML file**
6. Select the pipeline file:
- `/azure-pipelines.yml` (basic)
- `/azure-pipelines-advanced.yml` (advanced)
7. Click **Continue**
8. Review the pipeline and click **Run**
## Step 4: Configure Environments (Advanced Pipeline Only)
If using the advanced pipeline, create environments:
1. Go to **Pipelines****Environments**
2. Click **New environment**
3. Create two environments:
- **Name**: `price-tracker-dev`
- **Name**: `price-tracker-prod`
4. For each environment, you can add approval gates:
- Go to the environment
- Click the three dots → **Approvals and checks**
- Add **Approvals** for production
## Step 5: Pipeline Configuration
The pipeline is configured to:
### Triggers
- **Push to main**: Builds and deploys to production
- **Push to develop**: Builds and deploys to development
- **Pull requests**: Builds and tests only
### Build Process
1. Checkout source code
2. Build Docker image
3. Run container tests
4. Push to registry (if not PR)
5. Security scan (optional)
6. Deploy to appropriate environment
### Variables Used
- `dockerRegistryServiceConnection`: Service connection name
- `imageRepository`: `price-tracker`
- `containerRegistry`: `dock.ptslondon.co.uk`
- `tag`: Uses build ID for versioning
## Step 6: Customize for Your Needs
### Registry Settings
If your registry requires different settings, update these variables in the pipeline:
```yaml
variables:
dockerRegistryServiceConnection: 'your-connection-name'
imageRepository: 'your-repo-name'
containerRegistry: 'dock.ptslondon.co.uk'
```
### Branch Strategy
Current setup:
- `main` branch → Production deployment
- `develop` branch → Development deployment
To change this, modify the trigger and condition sections.
### Security Scanning
The advanced pipeline includes Trivy security scanning. To disable:
- Remove the `SecurityScan` stage
- Remove it from the `dependsOn` lists
## Step 7: Verify the Setup
1. Make a small change to your code
2. Push to the `develop` branch (for testing)
3. Watch the pipeline run in Azure DevOps
4. Verify the image appears in your registry
5. Check that the application deploys correctly
## Troubleshooting
### Common Issues
1. **Service Connection Failed**
- Verify registry credentials
- Check registry URL format
- Ensure registry is accessible from Azure
2. **Docker Build Failed**
- Check Dockerfile syntax
- Verify all required files are in repository
- Check build logs for specific errors
3. **Push to Registry Failed**
- Verify service connection permissions
- Check registry quota/space
- Ensure repository name is correct
4. **Tests Failed**
- Check application startup logs
- Verify port mappings
- Ensure dependencies are available
### Debug Tips
1. **Enable verbose logging**:
```yaml
- script: |
set -x # Enable debug mode
# your commands here
```
2. **Add diagnostic steps**:
```yaml
- script: |
docker images
docker ps -a
curl -v http://localhost:5000/
```
3. **Check container logs**:
```yaml
- script: |
docker logs price-tracker-test
```
## Pipeline Outputs
### Successful Run Produces:
- Docker image tagged with build ID
- Docker image tagged as `latest`
- Images pushed to `dock.ptslondon.co.uk/price-tracker:BUILD_ID`
- Deployment artifacts
- Test results
### Image Tags
- `dock.ptslondon.co.uk/price-tracker:latest` - Latest build
- `dock.ptslondon.co.uk/price-tracker:12345` - Specific build ID
## Production Deployment
For production deployment, the pipeline:
1. Only runs on `main` branch
2. Requires environment approval (if configured)
3. Runs security scans
4. Performs health checks
5. Can include notifications
## Monitoring
Monitor your pipeline:
1. **Pipeline runs**: Azure DevOps → Pipelines → Runs
2. **Environment status**: Azure DevOps → Pipelines → Environments
3. **Registry images**: Check your registry dashboard
4. **Application logs**: From deployed containers
## Next Steps
1. Set up monitoring and alerting for your deployed application
2. Configure backup strategies for your registry
3. Set up staging environments for testing
4. Add integration tests to the pipeline
5. Configure notification webhooks for deployment status
## Security Best Practices
1. **Never commit secrets** - Use Azure DevOps secure variables
2. **Use service connections** - Don't embed credentials
3. **Scan images** - Enable vulnerability scanning
4. **Limit permissions** - Use least privilege access
5. **Monitor access** - Regular audit of service connections

View File

@@ -0,0 +1,251 @@
trigger:
branches:
include:
- main
- develop
paths:
include:
- src/*
- templates/*
- requirements.txt
- Dockerfile
- config.json
- main.py
pr:
branches:
include:
- main
paths:
include:
- src/*
- templates/*
- requirements.txt
- Dockerfile
- config.json
- main.py
variables:
# Container registry service connection
dockerRegistryServiceConnection: 'dock-ptslondon-connection'
imageRepository: 'price-tracker'
containerRegistry: 'dock.ptslondon.co.uk'
dockerfilePath: '$(Build.SourcesDirectory)/Dockerfile'
# Agent VM image name
vmImageName: 'ubuntu-latest'
stages:
- stage: Build
displayName: 'Build and Test'
jobs:
- job: Build
displayName: 'Build Docker Image'
pool:
vmImage: $(vmImageName)
variables:
tag: '$(Build.BuildId)'
steps:
- checkout: self
displayName: 'Checkout source code'
- task: Docker@2
displayName: 'Build Docker image'
inputs:
command: 'build'
repository: $(imageRepository)
dockerfile: $(dockerfilePath)
tags: |
$(tag)
latest
- script: |
echo "Running container tests..."
# Start container for testing
docker run -d --name price-tracker-test -p 5000:5000 $(imageRepository):$(tag)
# Wait for container to be ready
echo "Waiting for container to start..."
for i in {1..30}; do
if curl -f http://localhost:5000/ > /dev/null 2>&1; then
echo "Container is ready!"
break
fi
echo "Waiting... ($i/30)"
sleep 2
done
# Run basic health checks
echo "Running health checks..."
curl -f http://localhost:5000/ || (echo "Health check failed" && exit 1)
# Cleanup
docker stop price-tracker-test
docker rm price-tracker-test
echo "All tests passed!"
displayName: 'Test Docker container'
- task: Docker@2
displayName: 'Push to registry'
inputs:
command: 'push'
repository: $(imageRepository)
containerRegistry: $(dockerRegistryServiceConnection)
tags: |
$(tag)
latest
condition: and(succeeded(), ne(variables['Build.Reason'], 'PullRequest'))
- stage: SecurityScan
displayName: 'Security Scanning'
dependsOn: Build
condition: and(succeeded(), ne(variables['Build.Reason'], 'PullRequest'))
jobs:
- job: VulnerabilityScan
displayName: 'Vulnerability Scan'
pool:
vmImage: $(vmImageName)
variables:
tag: '$(Build.BuildId)'
steps:
- task: Docker@2
displayName: 'Pull image for scanning'
inputs:
command: 'pull'
arguments: '$(containerRegistry)/$(imageRepository):$(tag)'
containerRegistry: $(dockerRegistryServiceConnection)
- script: |
# Install Trivy
sudo apt-get update
sudo apt-get install wget apt-transport-https gnupg lsb-release -y
wget -qO - https://aquasecurity.github.io/trivy-repo/deb/public.key | sudo apt-key add -
echo "deb https://aquasecurity.github.io/trivy-repo/deb $(lsb_release -sc) main" | sudo tee -a /etc/apt/sources.list.d/trivy.list
sudo apt-get update
sudo apt-get install trivy -y
# Run vulnerability scan
trivy image --exit-code 0 --severity LOW,MEDIUM --format table $(containerRegistry)/$(imageRepository):$(tag)
trivy image --exit-code 1 --severity HIGH,CRITICAL --format table $(containerRegistry)/$(imageRepository):$(tag)
displayName: 'Run Trivy vulnerability scan'
continueOnError: true
- stage: DeployDev
displayName: 'Deploy to Development'
dependsOn:
- Build
- SecurityScan
condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/develop'))
jobs:
- deployment: DeployToDev
displayName: 'Deploy to Development Environment'
environment: 'price-tracker-dev'
pool:
vmImage: $(vmImageName)
variables:
tag: '$(Build.BuildId)'
strategy:
runOnce:
deploy:
steps:
- checkout: self
- task: Docker@2
displayName: 'Pull latest image'
inputs:
command: 'pull'
arguments: '$(containerRegistry)/$(imageRepository):$(tag)'
containerRegistry: $(dockerRegistryServiceConnection)
- script: |
# Create deployment directory
mkdir -p ~/price-tracker-deployment
cd ~/price-tracker-deployment
# Copy deployment files
cp $(Pipeline.Workspace)/s/docker-compose.yml .
cp $(Pipeline.Workspace)/s/config.json .
# Update image tag in docker-compose
sed -i "s/price-tracker:latest/$(containerRegistry)\/$(imageRepository):$(tag)/g" docker-compose.yml
# Deploy using docker-compose
docker-compose down || true
docker-compose up -d
# Wait for deployment
sleep 10
# Verify deployment
curl -f http://localhost:5000/ || (echo "Deployment verification failed" && exit 1)
echo "Deployment to development completed successfully!"
displayName: 'Deploy to development'
- stage: DeployProd
displayName: 'Deploy to Production'
dependsOn:
- Build
- SecurityScan
condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/main'))
jobs:
- deployment: DeployToProd
displayName: 'Deploy to Production Environment'
environment: 'price-tracker-prod'
pool:
vmImage: $(vmImageName)
variables:
tag: '$(Build.BuildId)'
strategy:
runOnce:
deploy:
steps:
- checkout: self
- task: Docker@2
displayName: 'Pull latest image'
inputs:
command: 'pull'
arguments: '$(containerRegistry)/$(imageRepository):$(tag)'
containerRegistry: $(dockerRegistryServiceConnection)
- script: |
# Production deployment script
echo "Deploying to production..."
# Create deployment directory
mkdir -p ~/price-tracker-production
cd ~/price-tracker-production
# Copy deployment files
cp $(Pipeline.Workspace)/s/docker-compose.yml .
cp $(Pipeline.Workspace)/s/config.json .
# Update image tag in docker-compose for production
sed -i "s/price-tracker:latest/$(containerRegistry)\/$(imageRepository):$(tag)/g" docker-compose.yml
# Production-specific environment settings
export FLASK_ENV=production
# Deploy with zero-downtime strategy
docker-compose pull
docker-compose up -d
# Health check
sleep 15
for i in {1..10}; do
if curl -f http://localhost:5000/; then
echo "Production deployment successful!"
break
fi
echo "Waiting for production deployment... ($i/10)"
sleep 5
done
displayName: 'Deploy to production'
- script: |
# Post-deployment notifications (optional)
echo "Production deployment completed at $(date)"
# You can add notification webhooks here
displayName: 'Post-deployment tasks'

79
azure-pipelines.yml Normal file
View File

@@ -0,0 +1,79 @@
trigger:
branches:
include:
- main
- develop
paths:
include:
- src/*
- templates/*
- requirements.txt
- Dockerfile
- config.json
- main.py
variables:
# Container registry service connection established during pipeline creation
dockerRegistryServiceConnection: 'dock-ptslondon-connection'
imageRepository: 'price-tracker'
containerRegistry: 'dock.ptslondon.co.uk'
dockerfilePath: '$(Build.SourcesDirectory)/Dockerfile'
tag: '$(Build.BuildId)'
# Agent VM image name
vmImageName: 'ubuntu-latest'
stages:
- stage: Build
displayName: Build and push stage
jobs:
- job: Build
displayName: Build
pool:
vmImage: $(vmImageName)
steps:
- task: Docker@2
displayName: Build and push Docker image
inputs:
command: buildAndPush
repository: $(imageRepository)
dockerfile: $(dockerfilePath)
containerRegistry: $(dockerRegistryServiceConnection)
tags: |
$(tag)
latest
# Optional: Run security scan on the image
- task: Docker@2
displayName: Run Trivy vulnerability scanner
inputs:
command: 'run'
arguments: '--rm -v /var/run/docker.sock:/var/run/docker.sock -v $(System.DefaultWorkingDirectory):/tmp/trivy aquasec/trivy image --exit-code 0 --severity HIGH,CRITICAL $(containerRegistry)/$(imageRepository):$(tag)'
continueOnError: true
# Optional: Test the built image
- task: Docker@2
displayName: Test Docker image
inputs:
command: 'run'
arguments: '--rm -d --name price-tracker-test -p 5001:5000 $(containerRegistry)/$(imageRepository):$(tag)'
continueOnError: true
- script: |
# Wait for container to start
sleep 10
# Test health endpoint
curl -f http://localhost:5001/ || echo "Health check failed"
# Cleanup test container
docker stop price-tracker-test || true
displayName: 'Health check test'
continueOnError: true
# Publish build artifacts
- task: PublishBuildArtifacts@1
displayName: 'Publish docker-compose and deployment files'
inputs:
PathtoPublish: '$(Build.SourcesDirectory)'
ArtifactName: 'deployment-files'
publishLocation: 'Container'
condition: succeeded()

0
src/uk_scraper_new.py Normal file
View File