Tại sao CI/CD quan trọng cho dự án Laravel Livewire

Laravel Livewire là một framework full‑stack cho phép xây dựng giao diện động mà không cần viết JavaScript. Tuy nhiên, khi dự án phát triển, số lượng component, migration, và test case sẽ tăng nhanh, khiến việc quản lý môi trường phát triển, kiểm thử và triển khai trở nên phức tạp. CI/CD (Continuous Integration / Continuous Deployment) giúp tự động hoá các bước này, giảm thiểu lỗi con người, tăng tốc độ đưa tính năng mới vào production và duy trì tính ổn định của hệ thống.

Đối với Livewire, một trong những thách thức lớn là đồng bộ trạng thái component giữa server và client. Khi có thay đổi trong component, chúng ta cần chắc chắn rằng các unit test và integration test vẫn chạy thành công trước khi đẩy lên môi trường thực tế. CI/CD cung cấp một quy trình chuẩn, từ build Docker image, chạy phpunit, tới deploy tự động, giúp đội ngũ phát triển tập trung vào việc viết code thay vì lo lắng về cấu hình môi trường.

Chuẩn bị môi trường Docker cho Laravel Livewire

Docker cho phép chúng ta đóng gói toàn bộ stack (PHP, Nginx, MySQL, Redis, …) vào các container độc lập, tái tạo môi trường một cách nhất quán trên máy local, CI runner và server production. Đối với Livewire, chúng ta cần một image PHP-FPM có các extension cần thiết (pdo_mysql, bcmath, intl, …) và một web server (thường là Nginx) để phục vụ các request.

Tạo Dockerfile cho PHP-FPM

FROM php:8.2-fpm

# Cài đặt các extension cần thiết cho Laravel
RUN apt-get update && apt-get install -y \
    libpng-dev \
    libjpeg62-turbo-dev \
    libfreetype6-dev \
    zip \
    unzip \
    git \
    && docker-php-ext-configure gd --with-freetype --with-jpeg \
    && docker-php-ext-install gd pdo_mysql bcmath intl opcache

# Cài đặt Composer
COPY --from=composer:2.7 /usr/bin/composer /usr/bin/composer

# Thiết lập thư mục làm việc
WORKDIR /var/www/html

# Copy source code (sẽ được mount trong docker‑compose cho môi trường dev)
COPY . /var/www/html

# Đặt quyền cho storage và bootstrap/cache
RUN chown -R www-data:www-data storage bootstrap/cache

EXPOSE 9000
CMD ["php-fpm"]

Dockerfile trên sử dụng php:8.2-fpm làm base image, cài đặt các extension Laravel thường dùng và Composer. Lưu ý việc chown thư mục storagebootstrap/cache để PHP‑FPM có quyền ghi.

Cấu hình Docker Compose cho môi trường phát triển

version: "3.9"

services:
  app:
    build:
      context: .
      dockerfile: Dockerfile
    image: laravel-livewire-app:dev
    container_name: livewire_app
    restart: unless-stopped
    volumes:
      - .:/var/www/html
    environment:
      - APP_ENV=local
      - APP_DEBUG=true
      - DB_HOST=db
      - DB_DATABASE=livewire
      - DB_USERNAME=root
      - DB_PASSWORD=secret
    depends_on:
      - db
      - redis
    networks:
      - laravel

  nginx:
    image: nginx:stable-alpine
    container_name: livewire_nginx
    restart: unless-stopped
    ports:
      - "8080:80"
    volumes:
      - .:/var/www/html
      - ./nginx/conf.d:/etc/nginx/conf.d
    depends_on:
      - app
    networks:
      - laravel

  db:
    image: mysql:8.0
    container_name: livewire_mysql
    restart: unless-stopped
    environment:
      - MYSQL_ROOT_PASSWORD=secret
      - MYSQL_DATABASE=livewire
    volumes:
      - db_data:/var/lib/mysql
    ports:
      - "3306:3306"
    networks:
      - laravel

  redis:
    image: redis:7-alpine
    container_name: livewire_redis
    restart: unless-stopped
    ports:
      - "6379:6379"
    networks:
      - laravel

networks:
  laravel:
    driver: bridge

volumes:
  db_data:

File docker-compose.yml định nghĩa bốn service: app (PHP‑FPM), nginx, db (MySQL) và redis. Thông qua volumes chúng ta mount source code vào container, cho phép hot‑reload khi chỉnh sửa file.

Cấu hình GitHub Actions: workflow tự động

GitHub Actions là nền tảng CI/CD tích hợp sẵn trong GitHub, cho phép chúng ta định nghĩa các job dưới dạng YAML. Dưới đây là một workflow mẫu thực hiện các bước:

  1. Checkout code.
  2. Build Docker image.
  3. Chạy unit test với PHPUnit.
  4. Push image lên Docker Hub (hoặc GitHub Container Registry).
  5. Deploy image tới server production bằng SSH.

File .github/workflows/ci-cd.yml

name: CI/CD Laravel Livewire

on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

jobs:
  build-test-deploy:
    runs-on: ubuntu-latest
    services:
      mysql:
        image: mysql:8.0
        env:
          MYSQL_ROOT_PASSWORD: secret
          MYSQL_DATABASE: livewire_test
        ports: ["3306:3306"]
        options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3
    steps:
      - name: Checkout repository
        uses: actions/checkout@v3

      - name: Set up PHP
        uses: shivammathur/setup-php@v2
        with:
          php-version: '8.2'
          extensions: mbstring, bcmath, intl, pdo_mysql, gd
          tools: composer

      - name: Install dependencies
        run: composer install --prefer-dist --no-progress --no-suggest

      - name: Generate application key
        run: php artisan key:generate

      - name: Run migrations
        env:
          DB_HOST: 127.0.0.1
          DB_DATABASE: livewire_test
          DB_USERNAME: root
          DB_PASSWORD: secret
        run: php artisan migrate --no-interaction

      - name: Run PHPUnit tests
        env:
          DB_HOST: 127.0.0.1
          DB_DATABASE: livewire_test
          DB_USERNAME: root
          DB_PASSWORD: secret
        run: vendor/bin/phpunit --colors=always

      - name: Build Docker image
        run: |
          docker build -t myrepo/livewire-app:${{ github.sha }} .

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

      - name: Push Docker image
        run: |
          docker push myrepo/livewire-app:${{ github.sha }}

      - name: Deploy to production server
        if: github.ref == 'refs/heads/main'
        uses: appleboy/[email protected]
        with:
          host: ${{ secrets.PROD_HOST }}
          username: ${{ secrets.PROD_USER }}
          key: ${{ secrets.PROD_SSH_KEY }}
          script: |
            docker pull myrepo/livewire-app:${{ github.sha }}
            docker stop livewire_app || true
            docker rm livewire_app || true
            docker run -d \
              --name livewire_app \
              -p 9000:9000 \
              -e APP_ENV=production \
              -e APP_DEBUG=false \
              -e DB_HOST=${{ secrets.PROD_DB_HOST }} \
              -e DB_DATABASE=${{ secrets.PROD_DB_NAME }} \
              -e DB_USERNAME=${{ secrets.PROD_DB_USER }} \
              -e DB_PASSWORD=${{ secrets.PROD_DB_PASS }} \
              myrepo/livewire-app:${{ github.sha }}

Workflow trên sử dụng service MySQL để chạy migration và test, sau đó build Docker image, đẩy lên Docker Hub và cuối cùng SSH vào server để khởi chạy container mới. Tất cả các biến nhạy cảm được lưu trong GitHub Secrets để bảo mật.

Kiểm thử tự động với PHPUnit

Laravel đã tích hợp sẵn PHPUnit, cho phép viết unit test cho model, service, và thậm chí cho Livewire component. Đối với Livewire, chúng ta có thể sử dụng Livewire\Testing\Testable để mô phỏng hành vi của component.

Ví dụ test một component Livewire

assertSet('count', 0)
            ->call('increment')
            ->assertSet('count', 1);
    }
}
?>

Test trên khởi tạo một instance của component Counter, kiểm tra giá trị mặc định count và gọi method increment. Khi chạy vendor/bin/phpunit, GitHub Actions sẽ tự động thực thi và báo cáo kết quả.

Triển khai lên môi trường production

Sau khi Docker image đã được đẩy lên registry, chúng ta có thể triển khai bằng nhiều cách: Docker Swarm, Kubernetes, hoặc đơn giản là Docker Compose trên server. Đối với các dự án vừa và nhỏ, Docker Compose vẫn là lựa chọn nhanh chóng.

docker‑compose.production.yml

version: "3.9"

services:
  app:
    image: myrepo/livewire-app:${IMAGE_TAG}
    container_name: livewire_app_prod
    restart: always
    environment:
      - APP_ENV=production
      - APP_DEBUG=false
      - DB_HOST=${DB_HOST}
      - DB_DATABASE=${DB_DATABASE}
      - DB_USERNAME=${DB_USERNAME}
      - DB_PASSWORD=${DB_PASSWORD}
    networks:
      - laravel

  nginx:
    image: nginx:stable-alpine
    container_name: livewire_nginx_prod
    restart: always
    ports:
      - "80:80"
    volumes:
      - ./nginx/conf.d:/etc/nginx/conf.d
    depends_on:
      - app
    networks:
      - laravel

networks:
  laravel:
    driver: bridge

Trên server production, chúng ta chỉ cần thiết lập các biến môi trường (IMAGE_TAG, DB_HOST, …) và chạy docker compose -f docker-compose.production.yml up -d. Docker sẽ tải image mới nhất, khởi động các container và tự động cân bằng tải qua Nginx.

Kết luận và hướng học thêm

Việc thiết lập một pipeline CI/CD hoàn chỉnh cho Laravel Livewire không chỉ giúp giảm thiểu rủi ro khi đưa tính năng mới lên production mà còn tạo ra một môi trường phát triển nhất quán, dễ bảo trì. Các bước chính bao gồm: xây dựng Docker image chuẩn, viết test tự động với PHPUnit, cấu hình workflow GitHub Actions và triển khai bằng Docker Compose hoặc orchestrator khác. Khi quy trình này được tự động hoá, đội ngũ phát triển có thể tập trung vào việc tối ưu UI/UX của Livewire component, cải thiện trải nghiệm người dùng mà không lo lắng về các lỗi môi trường.

Để nâng cao kiến thức và thực hành chi tiết hơn, Tham khảo khóa học "Khóa học Laravel Livewire từ cơ bản đến nâng cao" tại đây.