Task 03 - Build, Push, and Deploy Changes (40 minutes)
Introduction
With our Azure resources created, we have laid out the foundation for our application. Now, we must connect our source code with its destination. The first step in this journey is called Continuous Integration (CI).
Continuous Integration is the process of merging local code changes into source control and may include steps to automatically build and test the code. Effective Continuous Integration allows developers rapidly to iterate and collaborate. It also helps ensure that newly added code does not break the current application.
Review the following articles:
The next practical step is to extend our workflow with steps to build a Docker image and push it to Azure Container Registry (ACR). After that, we will configure the Web App to pull this image from ACR.
Containers are a great way to package and deploy applications consistently across environments. If you are new to containers, there are 3 key steps to creating and publishing an image, which we will cover below. Because this training focuses on DevOps and not containers, we’ve strived to make this task as straightforward as possible.
-
docker login
- You need to log into the container registry into which you will push your image. These generally require authentication from an authorized user, as that will prevent unauthorized individuals from publishing images in your registry. -
docker build
- The second step is to request that Docker (running locally on your machine or on your build server) create the container image. A critical component of this is theDockerfile
, which gives instructions to Docker on how to build the image, files to copy, ports to expose, and startup commands. -
docker push
- Once you have created your Docker image, you need to store it in a container registry. Container registries are secure, centralized locations to store Docker images. Docker supports apush
command that copies the Docker image to the registry in the repository you specify. A repository is a logical way of grouping and versioning Docker images, and Docker repositories do not necessarily need to match the structure of your GitHub repositories.
After we automate the build process, we also want to automate the release process. This is a technique called Continuous Delivery (CD). Please take a moment to review this brief article talking about why Continuous Delivery is important.
Description
The team at Munson’s Pickles and Preserves would like to see how to implement Continuous Integration and Continuous Delivery (CI/CD) in the context of using Docker containers to build code and GitHub Actions workflows to deploy that code.
- Create a new GitHub Actions
.NET
workflow. You should be able to find this in the “Continuous integration workflows” section. - Review the workflow contents. Then, under the “Setup .NET Core” step, ensure that the .NET version is
6.0.x
in order to match the version defined in the Team Messaging System. - Ensure that the workflow is configured to trigger on both pushes and pull requests, and then configure both of these triggers with path triggers which only act on changes in the
/Application
folder. Use GitHub Copilot to assist you in this step. - Update each of the three pre-defined GitHub Actions workflow steps used to build the .NET application. For each of the three steps, you will need to update each command to pass the relative path to the appropriate
.csproj
file as an argument. The three steps arerestore
,build
, andtest
. Forrestore
andbuild
, the argument you will pass in is the path to the application csproj file. Fortest
, the argument you will pass in is the path to the unit test csproj file. Use GitHub Copilot to assist you in this step. - Build a Docker file called
Dockerfile
in the folderApplication/src/RazorPagesTestSample
, using GitHub Copilot to assist you in developing the file. This Dockerfile should target a .NET 6 project with an entry point ofRazorPagesTestSample.dll
. - Test the workflow by making a small change to application code, such as adding a comment to a file. Commit and push the change, and then ensure that the workflow completes successfully. At this point, any changes pushed to the
/Application
folder will automatically trigger the workflow–this is Continuous Integration in action. - At the top of your workflow file, create 4 environment variables:
registryName
- the full server address of your Azure Container Registry instance. Set this to “registryName
.azurecr.io”, replacingregistryName
with the name of your registry.repositoryName
- The repository to target in the registry. Set this to “techboost/dotnetcoreapp
”.dockerFolderPath
- The path to the folder that contains the Dockerfile. You will need to point to the folderApplication/src/RazorPagesTestSample
.tag
- This needs to be a unique value each time, as this is used to version the images in the repository. GitHub makes environment variables available to help with this. Settag
to the github.run_number environment variable.
-
Go to the Azure Portal and get the username, password, and login server for your ACR instance and save these three as GitHub Actions secrets. The names for these secrets should be
ACR_USERNAME
,ACR_PASSWORD
, andACR_LOGIN_SERVER
, respectively. GitHub Copilot can assist you in finding the right place to create these GitHub Actions secrets.GitHub has multiple types of secrets you can create: you are able to create secrets for Actions, Codespaces, and Dependabot. Be sure to create your secrets for GitHub Actions, not for Codespaces or Dependabot.
- Add a second job to your existing .NET Core workflow. Make sure the first step in your second job includes
- uses: actions/checkout@v2
. - To authenticate to the registry, add a step named
Docker login
with the following as therun
command:docker login $registryName -u ACR_USERNAME -p ACR_PASSWORD
. Be sure to use the GitHub secrets for ACR username and ACR password instead of the string literals “ACR_USERNAME” and “ACR_PASSWORD”. Use GitHub Copilot for this and subsequent steps as needed. - To build your image, add a step named
Docker build
with the following as therun
command:docker build -t $registryName/$repositoryName:$tag --build-arg build_version=$tag $dockerFolderPath
- To push your image to ACR, add a step named
Docker push
with the following as therun
command:docker push $registryName/$repositoryName:$tag
- Test the workflow by adding a comment to one of the application files. Commit, push, monitor the workflow. Verify that the GitHub Actions workflow builds a new container image, tags that image, and pushes the image to ACR.
- Configure your
dev
environment to pull the latest container image from ACR.- Log into Azure using your service principal, if needed (hint).
- Use the
Azure/webapps-deploy@v2
action to update the Web App to pull the latest image from ACR. Key parameters to configure include:app-name
- the name of the wep app instance to targetimages
- the path to the image you pushed to ACR
You will need to hard-code the registry name and repository name in the
images
section of theazure/webapps-deploy
GitHub Action. This action does not support environment variables, repository variables, or secrets. If you do not hard-code the registry name and repository name, you will get a LinuxFxVersion error indicating that this version is invalid.
- Make a small change to your application, such as adding a string of display text to
Pages/Index.cshtml
in the app. From there, commit and push the change. Then, monitor the workflow and see if the change shows up on the dev instance of the website. - Configure your workflow to deploy to your
test
andprod
environments. Ensure that each environment includes a manual approval step before deployment.
Success Criteria
- Any changes pushed to the
/Application
folder automatically trigger the workflow. - .NET Core restore, build, and test steps complete successfully.
- Each successful workflow run builds a new container image with a unique tag. Each container image should be available in the Azure Container Registry after each successful workflow run.
- A small change to a file such as
Pages/Index.cshtml
automatically shows up on the website running in thedev
environment,<prefix>devops-dev
.azurewebsites.net. - Manual approval is required to deploy to the
test
andprod
environments.
Learning Resources
- Introduction to GitHub Actions
- .NET Core Action to build and test
- Understanding workflow path filters
- dotnet commands
- GitHub Actions for Azure
- Environment variables
- Introduction to GitHub Actions
- Understanding workflow path filters
- Authenticate with an Azure container registry
- GitHub Actions for Azure
- Deploy a custom container to App Service using GitHub Actions
- Using environments for deployment
Solution
Expand this section to view the solution
- Create a new secret in GitHub named AZURE_CREDENTIALS
- In the Azure CLI run the following command:
az ad sp create-for-rbac --name "TechExcelDotNetDeploy" --json-auth --role contributor --scopes /subscriptions/{your_subscription_id}
- Copy and Paste the json output into the AZURE_CREDENTIALS secret in GitHub
- The solution for GitHub Actions in this task is a YAML file in the solutions folder. To make the solution easier to understand, there are several versions of the solution file. Each version builds upon the prior, so the final version covers all parts of the solution.
- The first version of the solution file covers steps 1-5.
- The second version of the solution file adds in steps 6-12.
- The third version of the solution file adds in steps 13-15.
The deployment YAML files will not run on their own because they include sections which require developer input, such as
{your_prefix}
and{your_registry_name}
. Filling these in with appropriate values is necessary to get the .yml files in the appropriate condition to run.
The
images
command in eachwebapps-deploy
call has a hard-coded value for the image location oftechboost/dotnetcoreapp
and expect you to hard-code the ACR location. This is because theimages
command will not accept repository secrets or variables defined in the workflow like the steps in thedockerBuildPush
job do.
- The solution for a Dockerfile in this task is in the solutions folder.
Advanced Challenges (optional)
If the GitHub Actions workflow fails, GitHub sends an e-mail to the repository owner. Sometimes, however, you may wish to log the failure or even create a GitHub issue upon failure. Add a step to your workflow which creates a GitHub issue if there is a failure at any point in the workflow.
Advanced Challenge Solution
Expand this section to view the advanced challenge's solution
- The fourth version of the solution file adds in the advanced challenge.