Streamlining Angular Deployment with GitHub Actions, GitHub Container registry , Docker, and Nginx
Tags: devops, docker, webdev, cicd
Published: 2024-06-06 19:50:35

In modern web development, setting up an efficient CI/CD pipeline for deploying your Angular applications can significantly enhance your workflow. This blog post guides you through setting up a GitHub Actions pipeline to build and deploy an Angular application using Docker, Nginx, and ensuring basic security group configurations in EC2.

## Prerequisites

- A GitHub repository containing your Angular application.
- Docker installed on your local machine.
- GitHub Container Registry (GHCR) for storing Docker images.
- An EC2 instance or any server with Docker and Nginx installed.
- Proper security group settings on your EC2 instance.

## Step 1: Modify Dockerfile to Accept Build Arguments

First, we need to update our Dockerfile to accept build arguments that specify the Angular environment. This ensures that our build process can dynamically adjust based on the environment.

```dockerfile
# Use the official Node.js image as a base
FROM node:14

# Create and set the working directory
WORKDIR /app

# Copy package.json and package-lock.json
COPY package*.json ./

# Install dependencies
RUN npm install

# Copy the rest of the application files
COPY . .

# Set the environment variable to increase memory limit
ENV NODE_OPTIONS=--max_old_space_size=4096

# Build the Angular application based on the provided environment
ARG ANGULAR_ENV=production
RUN npm run build -- --configuration=$ANGULAR_ENV

# Expose the port the app runs on
EXPOSE 80

# Command to run the application
CMD ["npm", "start"]
```

## Step 2: Set Up GitHub Actions Workflow

Next, we'll configure a GitHub Actions workflow to handle the build and deployment process.

```yaml
name: Build and Deploy Angular

on:
  push:
    branches:
      - main # Change this to your main branch name

jobs:
  build-and-deploy:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout Code
        uses: actions/checkout@v2

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v2

      - name: Log in to GitHub Container Registry
        uses: docker/login-action@v2
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }} # Use GitHub token secret

      - name: Extract Git Commit Hash
        id: vars
        run: echo "COMMIT_HASH=$(git rev-parse --short HEAD)" >> $GITHUB_ENV

      - name: Build Docker Image
        run: |
          docker build --build-arg ANGULAR_ENV=production -t ghcr.io/yourusername/angular-docker:${{ env.COMMIT_HASH }} .

      - name: Push Docker Image to GitHub Container Registry
        run: |
          docker push ghcr.io/yourusername/angular-docker:${{ env.COMMIT_HASH }}

      - name: SSH and Deploy to Server
        env:
          PRIVATE_KEY: ${{ secrets.PRIVATE_KEY }} # This is the private key secret for SSH
          COMMIT_HASH: ${{ env.COMMIT_HASH }}
        run: |
          echo "$PRIVATE_KEY" > private_key.pem
          chmod 600 private_key.pem
          ssh -o StrictHostKeyChecking=no -i private_key.pem ubuntu@your-ec2-instance <<EOF
            echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin
            docker pull ghcr.io/yourusername/angular-docker:${COMMIT_HASH}
            docker stop angular-app || true
            docker rm angular-app || true
            docker run -d --name angular-app -p 8080:80 ghcr.io/yourusername/angular-docker:${COMMIT_HASH}
            sudo systemctl restart nginx
            docker system prune -a -f --volumes
          EOF
          rm -f private_key.pem

    permissions:
      contents: read
      packages: write
      id-token: write
```
## Step 3: Nginx Configuration

Update your Nginx configuration to correctly proxy requests to the Docker container.

```nginx
server {
    if ($host = yourdomain.com) {
        return 301 https://$host$request_uri;
    } # managed by Certbot

    listen 80;
    server_name yourdomain.com;
    return 404;
}

server {
    listen 443 ssl http2;
    server_name yourdomain.com;

    location / {
        proxy_pass http://localhost:8080/;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }

    ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem; # managed by Certbot
}
```
## Step 4: EC2 Security Group Configuration

Ensure your EC2 instance's security group allows inbound traffic on ports 80 and 443.

1. **Log in to AWS Management Console**.
2. **Navigate to EC2 Dashboard**.
3. **Select Security Groups** under "Network & Security".
4. **Find and Select Your Security Group** associated with your EC2 instance.
5. **Edit Inbound Rules** to allow traffic on ports 80 (HTTP) and 443 (HTTPS):
   - **HTTP** (port 80):
     - Type: HTTP
     - Protocol: TCP
     - Port Range: 80
     - Source: 0.0.0.0/0
   - **HTTPS** (port 443):
     - Type: HTTPS
     - Protocol: TCP
     - Port Range: 443
     - Source: 0.0.0.0/0
 6. **Login ghcr.io** to pull the  iamges from your github 
```
export CR_PAT=YOUR_TOKEN
echo $CR_PAT | docker login ghcr.io -u USERNAME --password-stdin
```

## Summary

This setup ensures that:
- Each deployment uses a Docker image tagged with the specific Git commit hash.
- Unused Docker images are removed from your server to free up space.
- Your server runs the latest Docker image without accumulating old images.