Docker has become an essential tool for building and deploying modern applications. In a continuous integration and delivery (CI/CD) pipeline, Docker can help streamline the process of testing and deploying code changes. In this blog post, we’ll provide an overview of how to use Docker in a CI/CD pipeline, including how to automate testing and deployment. We’ll show you how to set up a basic CI/CD pipeline using Github Actions, how to build a Docker image for your application, and how to run automated tests and deploy your application using Docker.
Setting up the CI/CD pipeline with Github Actions
Github Actions is a powerful tool that allows you to automate your software development workflows. With Github Actions, you can create custom workflows that automatically build, test, and deploy your code changes. In this section, we’ll walk through the steps of setting up a basic CI/CD pipeline using Github Actions.
Before you begin, add your Docker Hub username and password as secrets in your GitHub repository by navigating to your repository’s settings page, selecting “Secrets” from the left sidebar, and clicking “New repository secret”. Add DOCKER_USERNAME
and DOCKER_PASSWORD
so your scripts can log in and use your Docker hub images.
We have used placeholders for the following values, make sure you replace these values before trying to run your scripts.
your-dockerhub-username | The username of your docker account |
your-docker-image-name | The name of your image on docker hub |
Running automated tests with Docker
To get started, you’ll need to create a workflow file in your Github repository. A workflow file is a YAML file (e.g., .github/workflows/push.yml
) that contains the instructions for Github Actions to follow. Here’s an example workflow file that sets up a basic CI/CD pipeline:
name: CI
on: [push]
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Login to Docker Hub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Run Codeception tests
uses: docker://your-dockerhub-username/your-docker-image-name:latest
with:
entrypoint: vendor/bin/codecept run
In this example, the workflow defines a single job named test
, which runs on the latest version of Ubuntu. The job has two steps:
- The
Checkout code
step checks out your code from your Github repository. - The
Run Codeception tests
step uses the Docker imageyour-dockerhub-username/your-docker-image-name:latest
to run your Codeception tests. Theentrypoint
option specifies the command to run inside the Docker container.
You’ll need to replace your-dockerhub-username
and your-docker-image-name
with the appropriate values for your Docker image.
If you need to run other Docker images in addition to your own Docker image, you can use the docker-compose
command to define a multi-container environment and specify the environment variables that need to be passed to each container. Here’s an example workflow file that shows how to use docker-compose
to run your own Docker image along with a MySQL container, and pass environment variables to both containers:
name: CI
on: [push]
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Login to Docker Hub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Start containers
run: |
docker-compose up -d
sleep 5 # wait for containers to start up
- name: Run Codeception tests
run: docker exec my-image vendor/bin/codecept run
- name: Stop containers
run: docker-compose down
In this example, the db
service uses the mysql:8.0
Docker image and sets the MYSQL_DATABASE
, MYSQL_USER
, and MYSQL_PASSWORD
environment variables. These variables are set in the docker-compose.yml file.
version: '3.8'
services:
db:
image: mysql:8.0
environment:
MYSQL_DATABASE: my_database
MYSQL_USER: root
MYSQL_PASSWORD: secret
tests:
image: your-dockerhub-username/your-docker-image-name:latest
environment:
DB_HOST: db
DB_USER: root
DB_PASS: secret
entrypoint: vendor/bin/codecept run
Note that in the Run Codeception tests
step, we’re using the --network="host"
option to connect to the MySQL container running on the host network. This allows us to access the MySQL container without exposing ports or setting up additional network configuration. However, you can also use other network modes and configurations to connect to your MySQL container, depending on your requirements
Building a Docker image
Create a new file in your project directory called Dockerfile
with the following contents:
# Use an official PHP runtime as a parent image
FROM php:8.2-apache
# Set the working directory to /var/www/html
WORKDIR /var/www/html
# Copy the current directory contents into the container at /var/www/html
COPY . /var/www/html/
# Install any needed packages specified in requirements.txt
RUN apt-get update && apt-get install -y \
git \
unzip \
libzip-dev \
&& docker-php-ext-install zip
# Expose port 80 for the web server
EXPOSE 80 443
Create a new GitHub Actions workflow file (e.g., .github/workflows/build-and-publish.yml
) with the following contents:
name: Build and Publish Docker Image
on:
push:
branches: [main]
jobs:
build-and-publish:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Get previous Docker image tag
id: get_previous_tag
run: |
echo "::set-output name=previous_tag::$(curl -sS -u ${{ secrets.DOCKER_USERNAME }}:${{ secrets.DOCKER_PASSWORD }} https://registry.hub.docker.com/v2/repositories/your-dockerhub-username/your-docker-image-name/tags/?page_size=10000 | jq -r '.results[].name' | sort -r -V | head -n 1)"
- name: Build Docker image
uses: docker/build-push-action@v2
with:
context: .
push: true
tags: |
your-dockerhub-username/your-docker-image-name:${{ steps.get_previous_tag.outputs.previous_tag }}
your-dockerhub-username/your-docker-image-name:latest
dockerfile: Dockerfile
- name: Login to Docker Hub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Push Docker image to Docker Hub
uses: docker/build-push-action@v2
with:
context: .
push: true
tags: |
your-dockerhub-username/your-docker-image-name:${{ steps.get_previous_tag.outputs.previous_tag }}
your-dockerhub-username/your-docker-image-name:latest
The on push
part of the yml defines when this action is run. In this case, when a new push is made to the main branch, we automatically create a new docker image with a new tag, and overwrites the latest tag.
In this workflow file, the curl
command uses the -u
option to pass the Docker Hub username and password for authentication. This allows the API request to access non-public images. You will need to replace your-dockerhub-username
and your-docker-image-name
with your own values.
Also, make sure that you have set up the DOCKER_USERNAME
and DOCKER_PASSWORD
secrets in your GitHub repository. You can set these secrets by navigating to your repository settings, selecting “Secrets” from the left sidebar, and then clicking “New repository secret”
Note: This workflow assumes that you are using semver-style tags (e.g., v1.2.3
). If you are using a different tag format, you may need to modify the jq
command to extract the previous tag correctly.
Docker tagging
There are two common ways to tag a Docker image: using a version number or using the “latest” tag. A version number tag, such as “v1.0” or “1.0.1”, identifies a specific version of an image that can be referred to later. The “latest” tag, on the other hand, always refers to the most recently built version of an image, regardless of whether it is a new version or an existing version that has been updated.
When deploying a Docker image to a production environment, it is always recommended to use a specific version tag rather than the “latest” tag. This is because the “latest” tag is always changing, and there is no guarantee that the most recent version is stable or compatible with your environment. By using a specific version tag, you can ensure that the same version of the image is deployed every time, which makes it easier to troubleshoot issues and maintain consistency across your infrastructure.
Additionally, if you only use the “latest” tag and there are multiple versions of the image with the same tag, it can become difficult to track which version is currently deployed in production. If you use version number tags, it’s easier to keep track of which versions have been deployed and roll back to previous versions if necessary.
So while the “latest” tag can be convenient for development and testing environments, it is best to always use version number tags when deploying Docker images to production environments. This helps ensure stability, consistency, and easier management of your Docker images.
Key takeaways
- Docker is a powerful tool that can be used to streamline your CI/CD pipeline by providing a consistent environment for testing and deployment.
- You can use GitHub Actions to automate your CI/CD pipeline and easily integrate Docker into your workflow.
- Building Docker images can be automated using GitHub Actions and Docker Hub.
- Automated testing can be run in a Docker container using tools like Codeception.
- Deploying your application using Docker can be done with Docker Compose or Kubernetes.
- It’s important to tag your Docker images with version numbers and deploy using specific tags to ensure consistency and avoid issues with the latest tag.
Fuse Web can help
Docker is a powerful platform with lots of advantages for businesses, but it can also be daunting to use. The process of containerizing apps and maintaining containers can be complicated and time-consuming for many businesses. Furthermore, businesses may be worried about the security and scalability of their containerized apps, and they may even lack the skills or resources to operate their Docker environment successfully.
Fuse Web can assist businesses in overcoming these challenges by offering professional guidance and support for their Docker-based initiatives. Our team has considerable Docker knowledge and can assist businesses with containerizing their apps, managing their containers, and optimizing their Docker environment for speed and scalability. Don’t hesitate, contact us now to see how we can help.
Related content
-
Unleashing the Power of CI/CD: How Our PHP Development Team Streamlines Software Delivery
Hello, fellow tech enthusiasts! Today, we’re excited to share our insights into the world of Continuous Integration (CI), Continuous Deployment…Continue reading »
-
The Triumphs and Challenges of 20 Years in PHP Development: Building Scalable Websites and Lessons Learned
Over the past 20 years, we have been at the forefront of PHP development, creating high-performance and scalable websites for…Continue reading »
-
Best Practices for Using Docker to Deploy and Scale Web Applications
Docker is a popular tool for deploying and scaling web applications, and it offers several benefits for developers and operators…Continue reading »