How you can master continuous delivery with Vue, Docker, and Azure

As a full-stack developer, I know how critical it is to get new features and fixes out to users quickly and reliably. In today‘s fast-paced software world, long release cycles and manual deployments just don‘t cut it anymore. That‘s why practicing continuous delivery is so important for keeping a competitive edge.

In this in-depth guide, we‘ll walk through implementing a robust continuous delivery workflow for a Vue.js application using Docker and Azure. Whether you‘re new to these technologies or have some experience, I‘ll share my personal tips and best practices to help you ship better code faster. Let‘s jump in!

What is continuous delivery?

Continuous delivery is the practice of automating the entire software release process so that you can confidently deploy new versions to production at any time. The goal is to make releases boring and routine rather than stressful and risky.

With continuous delivery, every code change that passes the automated tests is a candidate for release. Releases happen frequently (often multiple times a day) and are fully automated – from building to testing to deploying.

Studies show that organizations that implement continuous delivery can deploy code 46x more frequently, have a 7x lower change failure rate, and recover from failures 2604x faster than their peers [1]. Developers also report higher levels of job satisfaction and less burnout.

While implementing continuous delivery requires an initial investment, it quickly pays off by enabling faster iteration and learning. Rather than spending days or weeks preparing for a release, developers can focus on writing code and teams can get rapid feedback from real users.

Why use Vue, Docker and Azure?

To setup a continuous delivery workflow, you need several key components:

  1. A version control system to track code changes
  2. An automated pipeline to build, test, and package your app
  3. An image registry to store versioned packages
  4. A way to test and deploy your app to production

There are many great tools available in each of these areas, but I‘m a big fan of the Vue + Docker + Azure stack for it‘s simplicity and power:

  • Vue is a progressive JavaScript framework for building web UIs. It‘s lightweight, performant, and easy to learn. Vue‘s component-based architecture makes it a great fit for continuous delivery.

  • Docker is the industry-standard platform for packaging and running apps in containers. Containerizing your app ensures consistency across environments and simplifies deployments. According to surveys, 79% of companies are running containerized applications in production [2].

  • Azure is Microsoft‘s comprehensive set of cloud services for building, deploying, and managing applications. Azure provides all the pieces you need for an end-to-end continuous delivery pipeline. 64% of Fortune 500 companies use Azure today [3].

Specifically, we‘ll use:

  • Azure Repos for version control
  • Azure Pipelines for CI/CD
  • Azure Container Registry for our Docker images
  • Azure Web Apps for hosting our containerized app

You can substitute equivalents like GitHub, Jenkins, Docker Hub, AWS, etc. based on your needs and preferences. But I find Azure‘s integrated offering makes it quick and easy to get all the pieces working together smoothly.

Running Vue in Docker

First, let‘s dockerize our Vue app for local development. Assuming you already have a Vue project, create a new Dockerfile in the root:

# base stage
FROM node:lts-alpine as base
WORKDIR /app
COPY package*.json ./
RUN npm ci

# build stage
FROM base as build
COPY . .
RUN npm run build

# production stage 
FROM nginx:stable-alpine as production 
COPY --from=build /app/dist /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

This multi-stage Dockerfile:

  1. Starts from a Node.js base image to install dependencies
  2. Builds the Vue app in a separate stage
  3. Copies the built files to an Nginx image for serving
  4. Exposes port 80 and starts Nginx

Now build and run the Docker image:

docker build -t my-vue-app .
docker run -d -p 8080:80 --name my-vue-app my-vue-app

Browse to http://localhost:8080 and you should see your Vue app running in a Docker container!

Packaging your app in a Docker container has many benefits:

  • All dependencies are included so the build is reproducible
  • The app runs the same in every environment
  • You can easily scale the app by spinning up multiple containers
  • Deployment is simplified to pushing and pulling Docker images

83% of developers say containerization is important to their work and company [4]. So this is an essential skill to learn for continuous delivery and modern software development in general.

Pushing to Azure Container Registry

To deploy our Vue app, we first need to push the Docker image to a container registry. A registry allows you to store, manage, and secure your Docker images as you would source code.

We‘ll create a private registry with Azure Container Registry (ACR) to hold our images. From the Azure portal, search for "Container registries" and click "Create":

Create Azure Container Registry

Choose a unique registry name, select your subscription and resource group, and click "Create". Your new registry should be provisioned within a few minutes.

Back in your terminal, log in to the ACR:

az acr login --name <registry-name>

Tag your local Docker image with the ACR server URL and push it:

docker tag my-vue-app <registry-name>.azurecr.io/my-vue-app:v1
docker push <registry-name>.azurecr.io/my-vue-app:v1

You‘ve now stored your first Docker image in Azure Container Registry! Navigate to your ACR in the Azure portal to see the image listed under "Repositories".

Deploying to Azure Web Apps

With our Vue app packaged in a Docker image and pushed to ACR, we‘re ready to deploy it to Azure. We‘ll run the app in an Azure Web App, which is a fully-managed platform for hosting web applications.

In the Azure portal, click "Create a resource", search for "Web App", and click "Create". Fill out the basics like subscription, resource group, name and region. Under "Publish", select "Docker Container". Then configure the container details:

  • Image Source: "Azure Container Registry"
  • Registry: Select your ACR from the dropdown
  • Image: Select the "my-vue-app" repository
  • Tag: Select "v1"

Configure Web App Container

Leave the other settings at their defaults and click "Review + create", then "Create". Azure will now provision your web app and automatically pull the specified Docker image from your ACR.

Once the deployment completes, find your web app‘s URL in the "Overview" tab. Browsing there, you should see your Vue application!

Not only is your Vue app now running in the cloud, but the entire deployment process is easily repeatable. Whenever you push a new version of the Docker image to ACR, you can simply update the image tag on the web app to deploy it.

However, we don‘t want to have to manually update the image each time – that‘s where continuous delivery comes in! In the next section, we‘ll fully automate our build and deployment process.

Automating releases with Azure Pipelines

To complete our continuous delivery workflow, we‘ll setup an Azure Pipelines to automatically build, test, and deploy our Vue app whenever we push new code.

Azure Pipelines is a cloud-based CI/CD service that works with any language, platform, and cloud. It offers a wide range of pre-built tasks and integrations to automate your pipeline.

Creating the pipeline

First, push your Vue app code to an Azure Repos repository (or GitHub, or elsewhere). Then, in your Azure DevOps project, go to "Pipelines" and click "New pipeline".

Select your code repository and choose the "Starter pipeline". This will generate a sample azure-pipelines.yml file that defines your pipeline steps. Replace the contents with:

trigger:
- main

pool:
  vmImage: ubuntu-latest

variables:
  dockerRegistryServiceConnection: ‘<registry-connection>‘
  imageRepository: ‘my-vue-app‘
  containerRegistry: ‘<registry-name>.azurecr.io‘
  dockerfilePath: ‘**/Dockerfile‘
  tag: ‘$(Build.BuildId)‘

stages:
- stage: Build
  displayName: Build and push stage
  jobs:  
  - job: Build
    displayName: Build
    pool:
      vmImage: ubuntu-latest
    steps:
    - task: Npm@1
      displayName: Run npm install
      inputs:
        command: ‘install‘
    - task: Npm@1
      displayName: Run npm build
      inputs:
        command: ‘custom‘
        customCommand: ‘run build‘
    - task: Docker@2
      displayName: Build and push Docker image
      inputs:
        containerRegistry: ‘$(dockerRegistryServiceConnection)‘
        repository: ‘$(imageRepository)‘
        command: ‘buildAndPush‘
        Dockerfile: ‘**/Dockerfile‘
        tags: |
          $(tag)
          latest

- stage: Deploy
  displayName: Deploy to web app
  jobs:
  - job: Deploy
    displayName: Deploy
    pool: 
      vmImage: ubuntu-latest
    steps:
    - task: AzureWebApp@1
      displayName: ‘Deploy Azure Web App‘
      inputs:
        azureSubscription: ‘<azure-subscription>‘
        appName: ‘<app-name>‘
        containers: ‘$(containerRegistry)/$(imageRepository):$(tag)‘

This pipeline has two stages:

  1. Build stage – Installs dependencies, builds the Vue app, builds the Docker image, and pushes it to ACR. The resulting image is tagged with the build ID and latest.

  2. Deploy stage – Deploys the new Docker image to our Azure web app using the Azure Web App task.

You‘ll need to replace the <placeholders> with your own values:

  • <registry-connection> – The name of an Azure service connection for your ACR. Create this under "Project settings" > "Service connections".

  • <azure-subscription> – The name of your Azure service connection. Create this under "Project settings" > "Service connections" and choose the Azure Resource Manager type.

  • <registry-name> – The name of your Azure Container Registry, e.g. "myacr"

  • <app-name> – The name of your Azure Web App

Commit this file to your repo. This will trigger your first pipeline run! You can view the status and logs under the "Pipelines" section of Azure DevOps:

Running pipeline

The pipeline will checkout your code, run the build, push the Docker image to ACR, and deploy that image to your web app. If all goes well, you should see your changes live after a few minutes!

Optimizing the pipeline

This basic pipeline automates our entire release process end-to-end which is a huge step forward! But there are a few best practices and optimizations we can add:

  • Caching – Cache the node_modules directory between builds using the Cache task to speed up the npm install step.

  • Testing – Run your unit and integration tests in the pipeline and block the release if any fail. You can use the Npm task with a custom command like npm run test.

  • Release gates – Add approval gates to control when releases happen. For example, require manual approval for deployments to production.

  • Rolling updates – Implement rolling updates to gradually shift traffic to the new version of your app rather than all at once. The Azure Web App task supports this with its deploymentMethod option.

Here‘s an updated azure-pipelines.yml with some of these enhancements:

variables:
  nodeVersion: ‘14.x‘

stages:
- stage: Build
  jobs:  
  - job: Build
    steps:
    - task: NodeTool@0
      inputs:
        versionSpec: ‘$(nodeVersion)‘ 
      displayName: ‘Install Node.js‘

    - task: Cache@2
      inputs:
        key: ‘npm | "$(Agent.OS)" | package-lock.json‘
        path: ‘node_modules‘
        cacheHitVar: ‘CACHE_RESTORED‘
      displayName: Cache node_modules

    - task: Npm@1
      condition: ne(variables.CACHE_RESTORED, ‘true‘) 
      inputs:
        command: ‘ci‘
      displayName: ‘Run npm ci if cache not restored‘

    - task: Npm@1
      inputs:
        command: ‘custom‘
        customCommand: ‘run build‘
      displayName: ‘Run npm run build‘

    - task: Npm@1
      inputs:
        command: ‘custom‘
        customCommand: ‘run test‘
      displayName: ‘Run npm test‘

    - task: Docker@2
      inputs:
        containerRegistry: ‘$(dockerRegistryServiceConnection)‘
        repository: ‘$(imageRepository)‘
        command: ‘buildAndPush‘
        Dockerfile: ‘**/Dockerfile‘
        tags: |
          $(tag)
          latest

- stage: Deploy
  jobs:
  - deployment: Production
    pool:
      vmImage: ‘ubuntu-latest‘
    environment: production
    strategy:
      runOnce:
        deploy:
          steps:
          - task: AzureWebApp@1
            inputs:
              azureSubscription: ‘<azure-subscription>‘
              appName: ‘<app-name>‘
              containers: ‘$(containerRegistry)/$(imageRepository):$(tag)‘
              deploymentMethod: ‘Gradual swap‘

The key changes:

  • Caching node_modules to speed up npm installs
  • Adding a npm test step to run automated tests
  • Requiring an approval for the "Production" environment
  • Using the "Gradual swap" deployment method for zero-downtime rolling updates

Feel free to further customize your pipeline to fit your team‘s needs. The great thing about defining your pipeline as code is that you can continuously improve and iterate on your release process!

Conclusion

Whew, that was a lot! Let‘s recap what we accomplished:

  • Packaged a Vue app in a Docker container
  • Pushed the container image to Azure Container Registry
  • Deployed the containerized app to Azure Web Apps
  • Created a CI/CD pipeline in Azure Pipelines to automate the entire workflow

By leveraging Vue, Docker, and Azure, you can implement a reliable and efficient continuous delivery process for your web applications. Automating your releases enables you to ship quality code faster and more frequently with confidence.

Of course, this is just the tip of the iceberg. True continuous delivery requires a shift in mindset and culture. The goal is to make each step of the software development lifecycle – from planning to coding to testing to deployment – as streamlined and automated as possible.

Some additional continuous delivery best practices include:

  • Implementing trunk-based development and feature flags
  • Automating your infrastructure provisioning
  • Practicing "shift left" testing to catch bugs earlier
  • Monitoring your applications proactively with good observability
  • Establishing a blameless culture of continuous improvement

Ultimately, continuous delivery is a journey – not a destination. Start small, iterate often, and strive to make your release process a little bit better each day.

I hope this guide gave you a solid foundation to kickstart your continuous delivery transformation! Feel free to reach out if you have any questions. Happy shipping!

References

[1] 2021 State of DevOps Report, Google Cloud
[2] Containers in the Enterprise, 2020, Red Hat
[3] 10 Key Benefits of Microsoft Azure, Hanu
[4] Stack Overflow Developer Survey 2021

Similar Posts