[CI/CD] Using GitHub Actions + Docker for CI/CD

ยท

7 min read

[CI/CD] Using GitHub Actions + Docker for CI/CD

I apologize for the inconvenience. I will continue in English.

๐Ÿ‘พ What is CI/CD?

CI/CD stands for Continuous Integration/Continuous Deployment. It is the practice of automating the entire process from application development to deployment to create an efficient service environment.

  • CI (Continuous Integration): It involves regularly merging, building, and testing newly created and modified code.

  • CD (Continuous Deployment): It includes automating the process of setting up a deployable environment, including CI, and deploying the application.

The flow of CI/CD

  1. Source code

  2. Merge

  3. Test

  4. Build

  5. Ready to Deploy

  6. Automatic Deployment

๐Ÿ‘พ Requirements for Setting up CI/CD

To set up CI/CD, you need the following:

  1. Project creation and writing test code and logic.

  2. Dockerfile configuration.

  3. Issuing a token for using GitHub Container Registry.

  4. Writing GitHub Actions Workflow.

  5. Creating an AWS EC2 instance.

  6. Installing GitHub Actions Runner & Docker in the EC2 SSH environment.

You can follow these steps to implement CI/CD in your project.


๐Ÿ‘พ 1. Project Creation and Writing Test Code and Logic

Create a Nest.js project and write the API logic and test code. Ensure that all test cases pass successfully for the GitHub Actions to pass the tests.

๐Ÿ‘พ 2. Dockerfile Configuration

  1. Install and run Docker Desktop.

  2. Create a Dockerfile with the following contents (ensure the filename is correct):

# Match the local node version
FROM node:16.16.0

# Create a working directory for executing commands
RUN mkdir /app
WORKDIR /app

# Add the entire project to the working directory
ADD . /app

# Install project dependencies
RUN npm install

# Build Nest.js
RUN npm run build

# Expose port 3000
EXPOSE 3000

# Start the server
ENTRYPOINT npm run start:prod
  1. Create a .dockerignore file to specify files or folders that are not required for the build process.
node_modules/
dist/
  1. Build the Docker image with the command docker build -t [image name] .. Replace [image name] with the desired name for your image.

  2. Run the container in the background with the command docker run --name [container name] -d -p 3002:3000 [image name]. Replace [container name] with the desired name for your container. This command maps port 3002 on the host to port 3000 in the container.

  3. Access the API at http://localhost:3002 to verify that it is running successfully.

By completing these steps, you have successfully created and run a container.

๐Ÿ‘พ 3. Issuing a Token for GitHub Container Registry

What is GitHub Container Registry?

GitHub provides a service called GitHub "Package" Registry, which is a package storage service for GitHub. It allows GitHub users to store, share, and distribute packages among themselves.

GitHub "Container" Registry is an enhanced version of the Package Registry that focuses specifically on managing Docker images.

  1. To use GitHub Container Registry, you need to generate a GitHub Access Token.

  2. Click on the "Generate new token" button and enter the necessary details in the classic version.

  3. Configure the token with the required permissions. You can refer to the image in the original post for an example.

  4. Copy the generated token and keep it securely.

  5. Go to your repository's Settings > Secrets and variables > Actions, and click on "New repository secret".

  6. Enter CODEBLUE_TOKEN as the name and paste the copied token as the value.

  7. Save the secret.

You have successfully issued a token for GitHub Container Registry.

๐Ÿ‘พ 4. Writing GitHub Actions Workflow

  1. In your repository, create a directory named .github/workflows.

  2. Inside the .github/workflows directory, create a file named ci.yml.

  3. Write the following code in the ci.yml file:

# Workflow title
name: CodeBLUE-CI-CD

# Define environmental variables used in main.yml
env:
  DOCKER_IMAGE: ghcr.io/siwon-kim/vog-nest
  DOCKER_CONTAINER: vog-nest-container

# Event
# When push or pull request driven on the main branch
on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

# Define tasks when event driven
# Order: test -> build -> deploy 
# Each job will be executed under the runner container
jobs:
  # test code with Jest
  test:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout Source Code
        uses: actions/checkout@v3
      - name: Setup node.js 16.x
        uses: actions/setup-node@v3
        with:
          node-version: 16.x
          cache: 'npm'
      - name: Load env file
        run: |
          touch .env
          echo "MODE=${{ secrets.MODE }}" >> .env
          echo "PORT=${{ secrets.PORT }}" >> .env
          echo "RDS_DB_NAME=${{ secrets.RDS_DB_NAME }}" >> .env
          echo "RDS_HOSTNAME=${{ secrets.RDS_HOSTNAME }}" >> .env
          echo "RDS_PASSWORD=${{ secrets.RDS_PASSWORD }}" >> .env
          echo "RDS_PORT=${{ secrets.RDS_PORT }}" >> .env
          echo "RDS_USERNAME=${{ secrets.RDS_USERNAME }}" >> .env
      - name: Install dependencies
        run: npm install
      - run: npm run test
  # Docker image build
  build:
    # Test must be completed for Build to get started 
    needs: test
    runs-on: ubuntu-latest
    steps:
      - name: Checkout Source Code
        uses: actions/checkout@v3
      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v2
        with:
          driver-opts: |
            image=moby/buildkit:v0.10.6
      - name: login to ghcr
        uses: docker/login-action@v2
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.CODEBLUE_TOKEN }}
      - name: Build and push
        id: docker_build
        uses: docker/build-push-action@v3
        with:
          push: true
          tags: ${{ env.DOCKER_IMAGE }}:latest

  # Deploy to AWS ec2
  deploy:
    # Build must be completed for Deploy to get started
    needs: build
    # execute job using runner where ec2 is installed
    runs-on: [self-hosted, label-vog]
    # login into Github container registry 
    steps:
      - name: Login to ghcr
        uses: actions/checkout@v3
      - name: Setup docker build
        id: buildx
        uses: docker/setup-buildx-action@v2
      - name: login to ghcr
        uses: docker/login-action@v2
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.CODEBLUE_TOKEN }}
      - name: Run docker
        run: |
          touch .env
          echo "MODE=${{ secrets.MODE }}" >> .env
          echo "PORT=${{ secrets.PORT }}" >> .env
          echo "RDS_DB_NAME=${{ secrets.RDS_DB_NAME }}" >> .env
          echo "RDS_HOSTNAME=${{ secrets.RDS_HOSTNAME }}" >> .env
          echo "RDS_PASSWORD=${{ secrets.RDS_PASSWORD }}" >> .env
          echo "RDS_PORT=${{ secrets.RDS_PORT }}" >> .env
          echo "RDS_USERNAME=${{ secrets.RDS_USERNAME }}" >> .env
          docker stop ${{ env.DOCKER_CONTAINER }} && docker rm ${{ env.DOCKER_CONTAINER }} && docker rmi ${{ env.DOCKER_IMAGE }}:latest
          docker run --env-file ./.env -d -p 80:3000 --name ${{ env.DOCKER_CONTAINER }} --restart always ${{ env.DOCKER_IMAGE }}:latest
  1. Replace your-container-image-name in env: IMAGE_NAME: your-container-image-name with the desired name for your container image.

  2. Commit and push the changes to your repository.

By completing these steps, you have successfully written a GitHub Actions Workflow.

๐Ÿ‘พ 5. Creating an AWS EC2 Instance

  1. Go to the AWS Management Console and sign in to your AWS account.

  2. Open the EC2 Dashboard.

  3. Click on "Launch Instances" to create a new EC2 instance.

  4. Choose an Amazon Machine Image (AMI) based on your requirements. For example, you can select an Amazon Linux 2 AMI.

  5. Select an instance type that suits your needs.

  6. Configure the instance details, such as the number of instances, network settings, and storage.

  7. Add tags if required.

  8. Configure security groups to allow inbound and outbound traffic. For this setup, you'll need to allow inbound traffic on port 22 (SSH) and port 3000 (for the Nest.js application).

  9. Review the instance details and click on "Launch" to start the EC2 instance.

  10. Select an existing key pair or create a new one to connect to the instance.

  11. Launch the instance.

You have now created an AWS EC2 instance.

๐Ÿ‘พ 6. Installing GitHub Actions Runner & Docker in the EC2 SSH Environment

  1. Connect to your EC2 instance using SSH. You can use a terminal or SSH client for this step.

  2. Install the GitHub Actions Runner by following the official documentation: https://docs.github.com/en/actions/hosting-your-own-runners.

  3. Install Docker on the EC2 instance by following the official Docker documentation: https://docs.docker.com/engine/install/.

Once you have completed these steps, you will have installed GitHub Actions Runner and Docker in the EC2 SSH environment.

๐Ÿ‘พ Conclusion

By following the steps outlined above, you can set up CI/CD using GitHub Actions, Docker, and an AWS EC2 instance. This setup allows for continuous integration, automated testing, and deployment of your Nest.js application.

Please note that these instructions provide a general overview, and you may need to adapt them based on your specific project requirements and infrastructure setup.

ย