Giới Thiệu

Trong bài viết này, tôi sẽ hướng dẫn bạn cách xây dựng và triển khai hai ứng dụng Go đơn giản lên AWS EC2 sử dụng Docker và Nginx. Chúng ta sẽ tạo một pipeline CI/CD tự động với GitHub Actions để mỗi khi push code lên GitHub, ứng dụng sẽ tự động được build và deploy.

Mục Tiêu

  • Tạo hai ứng dụng Go đơn giản
  • Containerize ứng dụng với Docker
  • Cấu hình Nginx làm reverse proxy
  • Tự động hóa quá trình triển khai với GitHub Actions
  • Triển khai lên AWS EC2

Bước 1: Chuẩn Bị Môi Trường

1.1. Tạo Tài Khoản và Cài Đặt Công Cụ

  1. GitHub Account

    • Đăng ký tài khoản tại GitHub
    • Tạo hai repository mới: app-xapp-y
  2. Docker Hub Account

    • Đăng ký tại Docker Hub
    • Tạo hai repository: app-xapp-y
  3. AWS Account

    • Đăng ký tài khoản AWS nếu chưa có
    • Tạo EC2 instance với Amazon Linux 2023

1.2. Cấu Hình EC2

  1. Tạo Instance

    # Chọn Amazon Linux 2023
    # Instance type: t2.micro
    # Storage: 8GB
    
  2. Cấu Hình Security Group

    • Mở cổng 22 (SSH)
    • Mở cổng 80 (HTTP)
  3. Tạo SSH Key

    ssh-keygen -t rsa -b 4096 -C "your-email@example.com"
    

Bước 2: Tạo Ứng Dụng Go

2.1. App X

  1. Tạo Thư Mục và File

    mkdir app-x
    cd app-x
    
  2. Tạo File main.go

    package main
    
    import (
        "fmt"
        "log"
        "net/http"
    )
    
    func handler(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(w, "Hello from App X!")
    }
    
    func main() {
        http.HandleFunc("/", handler)
        log.Println("App X is running on port 8080...")
        log.Fatal(http.ListenAndServe(":8080", nil))
    }
    
  3. Tạo go.mod

    go mod init app-x
    go mod tidy
    

2.2. App Y

Tương tự như App X, nhưng thay đổi port thành 8081 và message thành “Hello from App Y!”.

Bước 3: Containerize với Docker

3.1. Tạo Dockerfile

  1. Dockerfile cho App X

    FROM golang:1.21-alpine
    
    WORKDIR /app
    
    COPY go.mod ./
    RUN go mod download
    
    COPY . .
    
    RUN go build -o main .
    
    EXPOSE 8080
    
    CMD ["./main"]
    
  2. Dockerfile cho App Y
    Tương tự như App X, nhưng thay đổi port thành 8081.

3.2. Build và Test Docker Image

# App X
docker build -t your-dockerhub-username/app-x:latest .
docker run -p 8080:8080 your-dockerhub-username/app-x:latest

# App Y
docker build -t your-dockerhub-username/app-y:latest .
docker run -p 8081:8081 your-dockerhub-username/app-y:latest

Bước 4: Cấu Hình Nginx

4.1. Cài Đặt Nginx

sudo dnf update -y
sudo dnf install -y nginx
sudo systemctl enable nginx
sudo systemctl start nginx

4.2. Cấu Hình Reverse Proxy

Tạo file cấu hình /etc/nginx/conf.d/multi-app.conf:

server {
    listen 80 default_server;
    server_name localhost;

    # App X
    location /x/ {
        proxy_pass http://localhost:8080/;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        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;
    }

    # App Y
    location /y/ {
        proxy_pass http://localhost:8081/;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        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;
    }

    # Redirect root to App X
    location = / {
        return 301 /x/;
    }
}

Bước 5: Tự Động Hóa với GitHub Actions

5.1. Tạo Workflow File

Tạo file .github/workflows/deploy.yml trong mỗi repository:

name: CI/CD Pipeline

on:
  push:
    branches:
      - main

env:
  DOCKER_IMAGE: your-dockerhub-username/app-x  # hoặc app-y
  CONTAINER_NAME: app-x  # hoặc app-y
  PORT: 8080  # hoặc 8081

jobs:
  build-and-deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout Code
        uses: actions/checkout@v4

      - name: Setup Docker Buildx
        uses: docker/setup-buildx-action@v3

      - name: Login to Docker Hub
        uses: docker/login-action@v3
        with:
          username: ${{ secrets.DOCKER_USERNAME }}
          password: ${{ secrets.DOCKER_PASSWORD }}

      - name: Build and Push Docker Image
        run: |
          docker build -t ${{ env.DOCKER_IMAGE }}:latest .
          docker push ${{ env.DOCKER_IMAGE }}:latest

      - name: Deploy to EC2
        uses: appleboy/ssh-action@v1.0.3
        with:
          host: ${{ secrets.EC2_HOST }}
          username: ec2-user
          key: ${{ secrets.SSH_PRIVATE_KEY }}
          script: |
            # Install Docker if not exists
            if ! command -v docker &> /dev/null; then
              sudo dnf update -y
              sudo dnf install -y docker
              sudo systemctl enable docker
              sudo systemctl start docker
              sudo usermod -aG docker ec2-user
            fi

            # Install Nginx if not exists
            if ! command -v nginx &> /dev/null; then
              sudo dnf install -y nginx
              sudo systemctl enable nginx
              sudo systemctl start nginx
            fi

            # Configure Nginx
            sudo tee /etc/nginx/conf.d/multi-app.conf << 'EOL'
            server {
                listen 80 default_server;
                server_name localhost;

                # App X
                location /x/ {
                    proxy_pass http://localhost:8080/;
                    proxy_http_version 1.1;
                    proxy_set_header Upgrade $http_upgrade;
                    proxy_set_header Connection 'upgrade';
                    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;
                }

                # App Y
                location /y/ {
                    proxy_pass http://localhost:8081/;
                    proxy_http_version 1.1;
                    proxy_set_header Upgrade $http_upgrade;
                    proxy_set_header Connection 'upgrade';
                    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;
                }

                # Redirect root to App X
                location = / {
                    return 301 /x/;
                }
            }
            EOL

            # Test and reload Nginx
            sudo nginx -t && sudo systemctl reload nginx

            # Pull and run new container
            sudo docker pull ${{ env.DOCKER_IMAGE }}:latest
            sudo docker stop ${{ env.CONTAINER_NAME }} || true
            sudo docker rm ${{ env.CONTAINER_NAME }} || true
            sudo docker run --name ${{ env.CONTAINER_NAME }} \
              -p ${{ env.PORT }}:${{ env.PORT }} \
              --restart unless-stopped \
              -d ${{ env.DOCKER_IMAGE }}:latest

5.2. Cấu Hình GitHub Secrets

Thêm các secrets sau vào repository:

  • DOCKER_USERNAME
  • DOCKER_PASSWORD
  • EC2_HOST
  • SSH_PRIVATE_KEY

Bước 6: Kiểm Tra và Xử Lý Sự Cố

6.1. Kiểm Tra Ứng Dụng

# Kiểm tra container
docker ps

# Xem log container
docker logs app-x
docker logs app-y

# Kiểm tra Nginx
sudo nginx -t
sudo systemctl status nginx

6.2. Xử Lý Sự Cố Thường Gặp

  1. Container không chạy

    # Kiểm tra log
    docker logs container_name
    
    # Khởi động lại container
    docker restart container_name
    
  2. Nginx không hoạt động

    # Kiểm tra cấu hình
    sudo nginx -t
    
    # Xem log lỗi
    sudo tail -f /var/log/nginx/error.log
    
  3. Không truy cập được ứng dụng

    # Kiểm tra firewall
    sudo systemctl status firewalld
    
    # Kiểm tra port
    sudo netstat -tulpn | grep LISTEN
    

Kết Luận

Trong bài viết này, chúng ta đã:

  1. Tạo hai ứng dụng Go đơn giản
  2. Containerize chúng với Docker
  3. Cấu hình Nginx làm reverse proxy
  4. Tự động hóa quá trình triển khai với GitHub Actions
  5. Triển khai lên AWS EC2

Bạn có thể mở rộng dự án này bằng cách:

  • Thêm database
  • Cấu hình SSL/TLS
  • Thêm monitoring và logging
  • Tối ưu hóa performance
  • Thêm các tính năng bảo mật

Tài Liệu Tham Khảo