In this blog post, we will walk through the process of setting up an Azure DevOps pipeline to build, test, scan, and deploy a React application to Azure Kubernetes Service (AKS). We will also integrate Trivy, an open-source vulnerability scanner, to scan the Docker image for security vulnerabilities.
Prerequisites:
- An Azure DevOps account
- An Azure Kubernetes Service (AKS) cluster
- An Azure Container Registry
- Trivy installed on your Azure DevOps
Project Architecture
1. Set up the Azure DevOps Pipeline
First, create a new pipeline in your Azure DevOps project. Then, add the following YAML code to your azure-pipelines.yml file. This pipeline has four stages: Test, Build, Scan and Push Image, and Deploy.
trigger:
branches:
include:
- master
variables:
acrName: 'acr234346'
imageName: 'crypt-react-app'
k8sNamespace: 'default'
k8sDeploymentName: 'deployment'
stages:
- stage: Test
displayName: 'Test stage'
jobs:
- job: Test
displayName: 'Test Job'
pool:
vmImage: 'ubuntu-latest'
steps:
- checkout: self
- task: NodeTool@0
inputs:
versionSpec: '16.x'
displayName: 'Install Node.js'
- script: |
npm ci
npm run test
displayName: 'Run tests'
- stage: Build
displayName: 'Build stage'
dependsOn: Test
condition: succeeded()
jobs:
- job: Build
displayName: 'Build Job'
pool:
vmImage: 'ubuntu-latest'
steps:
- checkout: self
- task: Docker@2
displayName: 'Build and push Docker image'
inputs:
containerRegistry: 'docker-reg'
repository: '$(imageName)'
command: 'buildAndPush'
Dockerfile: '**/Dockerfile'
- stage: Scan
displayName: 'Scan Image'
dependsOn: Build
condition: succeeded()
jobs:
- job: ScanImage
displayName: 'Scan Image Job'
pool:
vmImage: 'ubuntu-latest'
steps:
- task: Docker@2
displayName: 'Login to ACR'
inputs:
containerRegistry: 'docker-reg'
repository: '$(imageName)'
command: 'login'
- task: trivy@1
inputs:
version: 'latest'
docker: false
loginDockerConfig: true
image: $(acrName).azurecr.io/$(imageName):$(Build.BuildId)
ignoreUnfixed: true
displayName: Scan Docker image with Trivy
- stage: Deploy
displayName: 'Deploy stage'
dependsOn: Scan
condition: succeeded()
jobs:
- deployment: Deploy
displayName: 'Deploy Job'
environment: 'production'
pool:
vmImage: 'ubuntu-latest'
strategy:
runOnce:
deploy:
steps:
- checkout: self
- task: Kubernetes@1
displayName: 'Deploy Deployment to AKS'
inputs:
connectionType: 'Azure Resource Manager'
azureSubscriptionEndpoint: 'my-connection'
azureResourceGroup: 'dev-rg'
kubernetesCluster: 'demo-cluster'
useClusterAdmin: true
command: 'apply'
useConfigurationFile: true
configurationType: 'inline'
inline: |
apiVersion: apps/v1
kind: Deployment
metadata:
name: deployment
namespace: default
spec:
replicas: 1
selector:
matchLabels:
app: crypto-app
template:
metadata:
labels:
app: crypto-app
spec:
containers:
- name: $(imageName)
image: $(acrName).azurecr.io/$(imageName):$(Build.BuildId)
ports:
- containerPort: 5000
secretType: 'dockerRegistry'
containerRegistryType: 'Azure Container Registry'
- task: Kubernetes@1
displayName: 'Deploy Service to AKS'
inputs:
connectionType: 'Azure Resource Manager'
azureSubscriptionEndpoint: 'my-connection'
azureResourceGroup: 'dev-rg'
kubernetesCluster: 'demo-cluster'
useClusterAdmin: true
command: 'apply'
useConfigurationFile: true
configurationType: 'inline'
inline: |
apiVersion: v1
kind: Service
metadata:
name: crypto-app-service
namespace: default
spec:
selector:
app: crypto-app
ports:
- protocol: TCP
port: 80
targetPort: 5000
type: LoadBalancer
secretType: 'dockerRegistry'
containerRegistryType: 'Azure Container Registry'
2. Configure the Kubernetes Deployment and Service
In the Deploy stage of the pipeline, we will deploy our application to AKS using Kubernetes Deployment and Service resources. The pipeline is configured to use inline YAML for these resources, making it easy to modify and maintain the configuration.
In the Deployment resource, we specify the Docker image to be used, pulled from the Azure Container Registry (ACR) created in the previous stage. The Service resource is set up as a LoadBalancer, allowing external access to the application.
Below is the code snippet for the Deploy stage, which includes the Deployment and Service resources:
- stage: Deploy
displayName: 'Deploy stage'
dependsOn: Scan
condition: succeeded()
jobs:
- deployment: Deploy
displayName: 'Deploy Job'
environment: 'production'
pool:
vmImage: 'ubuntu-latest'
strategy:
runOnce:
deploy:
steps:
- checkout: self
- task: Kubernetes@1
displayName: 'Deploy Deployment to AKS'
inputs:
connectionType: 'Azure Resource Manager'
azureSubscriptionEndpoint: 'my-connection'
azureResourceGroup: 'dev-rg'
kubernetesCluster: 'demo-cluster'
useClusterAdmin: true
command: 'apply'
useConfigurationFile: true
configurationType: 'inline'
inline: |
apiVersion: apps/v1
kind: Deployment
metadata:
name: deployment
namespace: default
spec:
replicas: 1
selector:
matchLabels:
app: crypto-app
template:
metadata:
labels:
app: crypto-app
spec:
containers:
- name: $(imageName)
image: $(acrName).azurecr.io/$(imageName):$(Build.BuildId)
ports:
- containerPort: 5000
secretType: 'dockerRegistry'
containerRegistryType: 'Azure Container Registry'
- task: Kubernetes@1
displayName: 'Deploy Service to AKS'
inputs:
connectionType: 'Azure Resource Manager'
azureSubscriptionEndpoint: 'my-connection'
azureResourceGroup: 'dev-rg'
kubernetesCluster: 'demo-cluster'
useClusterAdmin: true
command: 'apply'
useConfigurationFile: true
configurationType: 'inline'
inline: |
apiVersion: v1
kind: Service
metadata:
name: crypto-app-service
namespace: default
spec:
selector:
app: crypto-app
ports:
- protocol: TCP
port: 80
targetPort: 5000
type: LoadBalancer
secretType: 'dockerRegistry'
containerRegistryType: 'Azure Container Registry'
3. Scan Docker Image with Trivy
Trivy is an open-source scanner for vulnerabilities in container images, it detects vulnerabilities of OS packages (Alpine, Red Hat Universal Base Image, etc.) and application dependencies (Bundler, Composer, npm, yarn, etc.). It is highly configurable and easy to use. In this step, we will be integrating Trivy with our pipeline to scan our Docker image before pushing it to the ACR.
To use Trivy, we need to add a new job in our pipeline under the ScanAndPushImage stage. This job will run Trivy to scan the Docker image and report any vulnerabilities found. We will use the Trivy task provided by the Azure DevOps Marketplace.
The Trivy task needs the image name and tag to scan, and the scan results are saved in a JSON file. We can configure the task to fail if any critical vulnerabilities are found.
Here's the updated pipeline code with the Trivy job:
- stage: Scan
displayName: 'Scan Image'
dependsOn: Build
condition: succeeded()
jobs:
- job: ScanImage
displayName: 'Scan Image Job'
pool:
vmImage: 'ubuntu-latest'
steps:
- task: Docker@2
displayName: 'Login to ACR'
inputs:
containerRegistry: 'docker-reg'
repository: '$(imageName)'
command: 'login'
- task: trivy@1
inputs:
version: 'latest'
docker: false
loginDockerConfig: true
image: $(acrName).azurecr.io/$(imageName):$(Build.BuildId)
ignoreUnfixed: true
displayName: Scan Docker image with Trivy
Conclusion
In conclusion, we have successfully set up a CI/CD pipeline for a React app that includes testing, building, scanning, and deploying. We started by creating a basic React app and setting up a GitHub repository to store the code. Then, we configured an Azure Pipeline that automatically builds, tests, and deploys the app to a Kubernetes cluster running on Azure Kubernetes Service (AKS). We used Docker to containerize the app and stored the container image in an Azure Container Registry (ACR). We also integrated the Trivy vulnerability scanner to scan the Docker image for security issues before deploying it to the cluster.
The pipeline consists of several stages, each with its own set of jobs and tasks. In the first stage, the app is tested using Jest and Enzyme, ensuring that it functions correctly before moving on to the next stages. In the second stage, the app is built and a Docker image is created. In the third stage, the Docker image is scanned for vulnerabilities using Trivy, and then pushed to the ACR. In the final stage, the app is deployed to the AKS cluster using Kubernetes.
Throughout this project, we have demonstrated how a CI/CD pipeline can help automate the software development process, reducing the chance of errors and increasing the speed of delivery. With the tools and techniques used here, we have also shown how security can be integrated into the pipeline, ensuring that the app is safe to use and deploy in a production environment.
In summary, this project provides a solid foundation for building and deploying modern web applications using continuous integration and deployment techniques.
Source code: [(https://github.com/rifkhan107/crypto-currency-sample-website.git)]
Top comments (0)