Capstone Project: Week 4, Prototype
This week I was a lot more productive thanks to my recovery from my illness! I have done a lot, and I am excited to share what I have learned. As always, thank you for reading.
Prototype
This week I wanted to create a very simple prototype that contains the following features:
- Runs automatically on a commit to a GitHub Repo
- Builds a container out of the code
- Deploys it to a container registry
- Extracts container from registry and deploys it to an Azure Resource
Setup
The first thing I did was create and configure an Ubuntu Linux 20.04 LTS Virtual Machine that I had planned to utilize for the project. However, I decided that for a prototype, a simple Azure Web App service would be more efficient and easier. I still plan to use a virtual machine for configuration with Ansible and Monitoring, but this will work for now.
First, I needed an application to test a deployment. I cloned my favorite vulnerable web application, OWASP Juice Shop, and brought it down locally. I removed some unecessary files and then built the application into an image using the command docker build .
which automatically grabs the Dockerfile and runs it. This application comes with a Dockerfile, so it works out well! The Dockerfile includes instructions for how to build the image properly, including the command npm install which is necessary to get all of the requirements needed for a node application.
After verifying the application and Dockerfile works, I knew I was ready to make this into a pipeline. The pipeline is going to build the image just as I had only it will be done automatically.
In Azure, I created a resource group for my CapstonePrototype. I also created a Container Registry in its own resource group as I plan to make it persistent throughout all projects. I created an App Service Plan which is needed for each App Service as it is basically a billing and infrastructure configuration.I used the Cloud CLI to create an App Service instance as the GUI requires a container from the start if you plan to make an App Service based on a container. With the CLI, this is not required and you can do it later. Below displays what happens when you visit the hosted URL when you do this:
Azure App Service is like Amazon’s Elastic Beanstalk and is more of a PaaS than a IaaS. It will automatically scale your application for you, and you can do a lot more things with it. One of the features of it is it allows you to deploy a docker container to it. It also allows for continuous deployment if you want, so any changes to the docker container will update the app service. The app service is automatically hosted as well.
Now I had a configured App Service Instance with an App Service Plan and a link to my Azure Container Registry at thylaw/azurecr.io
. I had my application and my infrastructure, now all that is left is to create the pipeline.
Azure DevOps CI
I uploaded my JuiceShop application to my GitHub and linked the repository to my new Azure DevOps project titled 'Capstone Prototype.' Azure DevOps through the power of an extension can listen on my GitHub repository for changes such as commits and can be triggered to run commands. I also connected my container registry and an Azure Resource Manager which is basically just my Service Plan (Azure for Students). These service connections can be referenced with their name inside of the YAML file I will be creating.
Now with my project setup and my resources connected, I had to create the azure-pipelines.yml
file in my GitHub Repository. This YAML file is the default file that Azure DevOps looks for in your repository to run jobs and tasks. I created the file which is initially empty and began filling it in. I had two main tasks with this YML file: build the docker image and push it to my repository and then deploy the container to my azure app instance.
I will explain my .YAML file as comments within the code. Azure DevOps YAML files have a little bit different of a syntax than normal YAML files. Below is the first half of the YAML file that builds application into a container and sends it to the Azure Container Registry:
1# Trigger is what happens that makes Azure DevOps run the code below. * Is a wildcard, so anything should trigger it, including a commit.
2trigger:
3- '*'
4
5# Variables are simply strings that I don't want to retype and can make the code reusable even if I need to change some things
6variables:
7 buildConfiguration: 'Release'
8 webRepository: 'juiceshop' #name of repo in Azure Container Registry
9 tag: '$(Build.BuildId)' # Default is latest
10
11stages: # breaks jobs into groups that can depend on one another
12- stage: 'Build' #name
13 displayName: 'Build and push' # Display name that groups tasks below
14 jobs:
15 - job: 'Build' # Reference as shown below on dependsOn in 'Deploy'
16 displayName: 'Build job'
17 pool:
18 vmImage: 'ubuntu-20.04' # Type of Runner you want to compute calculations for your pipeline
19 steps: # groups of tasks that make up a job
20 - task: Docker@2 # Azure alias that equates to Docker in a command. Another example would be npm <command>
21 displayName: 'Build and push the image to container registry'
22 inputs: # Attach to 'docker'
23 command: buildAndPush # DevOps specific alias that equates to docker build + docker push
24 buildContext: $(Build.Repository.LocalPath) # workdirectory you want this to be performed in
25 repository: $(webRepository) # name of your repository yo uwant to push to
26 dockerfile: '$(Build.SourcesDirectory)/Dockerfile' # specified docker file you want to build from.
27 containerRegistry: 'Container Registry Connection' # You can use the service connection name to reference the container, as shown above.
28 tags: |
29 $(tag)
Now that it's set, I save the file which triggers a commit. Instantly Azure Pipelines kicks off the pipeline and begins to build the Juice Shop application into a container and deliver it to the registry.
Now the container has been built successfully, and it has automatically been deployed to my container registry:
Azure DevOps CD
Now that I have my continuous integration, I need to have continuous deployment to deliver my application to my App Service. The App Service has a feature that allows it to automatically update when the container updates, but that is boring! I want to create the CD part of my pipeline within the YAML file. I edit my azure-pipelines.yml
file by adding the following:
1- stage: 'Deploy'
2 displayName: 'Deploy the container'
3 dependsOn: Build # will only run if and when the Build stage completes
4 jobs:
5 - job: 'Deploy'
6 displayName: 'Deploy job'
7 pool:
8 vmImage: 'ubuntu-20.04'
9 variables:
10 - group: Release
11 steps:
12 - task: AzureWebAppContainer@1 # Azure alias for azure web app cli
13 inputs:
14 appName: $(WebAppName) # variable for app name
15 azureSubscription: 'Resource Manager CapstonePrototype' # name of resource connection service
16 imageName: $(RegistryName)/$(webRepository):$(build.buildId) # translates to thylaw/juiceshop:latest
The purpose of this stage was to extract the repo from my container registry and deploy it to the Azure App Service. I used the AzureWebAppContainer@1 task for this, which is Azure’s cli tool for Azure App Service. This part was easy as all I needed to do was specify the app name, the azure subscription, and the image name inside my repository.
After committing my changes, the pipeline runs again and the container is built and deployed to my container registry. The pipeline then moves on to the Deploy stage where it extracts the built container from the registry and deploys it to my app service.
My application is now deployed:
Run Through
Now, to test my pipeline from start to finish, I want to make some changes to my application. I edit a file in my local repository to change the title of the application on the navbar and delete some files.
The application automatically runs the pipeline as shown:
Once the pipeline finishes, I see my changes deployed to my app service! My Continuous Integration / Continuous Deployment Pipeline works!
Firewall Configuration
The last step I did was to modify the access control of my application. OWASP JuiceShop is an intentionally vulnerable web application meant to be used for pentesting and vulnerability finding. It should not be able to be accessed by the world and my server's security is at stake with it live. As such, I modifed the firewall to deny all except my own IP address. I learned today that 0.0.0.0/0 is an alias for ALL IP's!
Docker + Azure Container Registry
I have finished my initial learning of Docker by completing the majority of the course linked in my previous post.. However, this course was structured completely for Docker Hub. I learned a lot, including how to use Docker Compose. I skipped the lessons on Kubernetes and Docker Swarm as those will be used for stretch goals.
As this project is going to be almost completely focused on Azure, it only makes sense to use the Azure Container Registry, as spoken about before. While it is not technically easier to implement Azure Container Registry from within Azure, there are some benefits to using it:
Azure Container Registry Benefits
- Much more control over who can see and use your images
- Sign images to increase trust and reduce changes of an image becoming corrupted or infected
- All images stored in container registry are encrypted at rest
- Can be replicated to store images near where they're likely to be stored.
- Highly scalable, providing enhanced throughput for Docker pulls.
You can also push a docker file and container registry will build it for you using acr build. Note: Not all of these benefits and more benefits may be found with plans beyond basic. As I do not need many features, I will be sticking with the basic plan.
Azure Container Registry w/ CLI
I decided that it would be good to be able to deploy containers directly to my Azure Container Registry.
You can build and deploy an image to your registry quickly using the Azure CLI. The Azure CLI can be installed locally or you can use the cloudshell ide found on Azure:
1git clone https://github.com/MicrosoftDocs/mslearn-deploy-run-container-app-service.git
2
3cd mslearn-deploy-run-container-app-service/node
4
5az acr build --registry <container_registry_name> --image webimage .
I also learned how to deploy to your container registry directly through your Docker cli. To do this, you need to use the docker login
command. I had some trouble with this, but I figured out you have to generate a special password using Access Keys to do so.
After doing so, you can now push Docker images to Azure Container Registry using Docker Desktop Powershell (For Windows). You need to attach your registry name + azurecr.io to your image name in order to do so, as shown below.
Now I am mostly comfortable with Docker. I can pull and push containers, run them, tag them, create images out of running containers, build images from Dockerfiles, configure them, and more. I still need to practice building Dockerfiles, but for the purpose of this project, I don't believe I will have to do anything too complex, such as what was done in the OWASP JuiceShop project. Docker Swarm and Kubernetes are Container Orchestration Tools that are apart of my stretch goals that I would love to learn as well.
Conclusion
I now have a simple but working pipeline that automatically builds and deploys a container of my application to Azure App Service. I have learned a lot of Docker, Azure DevOps, Azure App Service, and Azure Container Registry. My next steps are to learn Ansible and expand on my prototype.