Quay lại
Build Laravel App Với Docker (LEMP)

Xin chào các bạn hôm nay mình sẽ hướng dẫn các bạn build một dự án Laravel bằng cách sử dụng docker, với cấu trúc LEMP stack một cách dễ hiểu và chi tiết nhất, các bạn có thể sử dụng nó trong chính dự án của các bạn hoặc có thể tham khảo, bạn nào chưa hiểu về docker cũng có thể làm theo và tự dựng riêng cho mình một môi trường!

Nếu bạn nào chưa biết về docker căn bản thì hãy xem qua series về docker mà mình đã viết trước đó để hiểu hơn nhé!

Một số yêu cầu trước khi chúng ta bắt đầu:

  • Đã từng làm với cấu trúc LEMP (Linux + Nginx+ MySQL + PHP)
  • Đã cài đặt Docker và hiểu biết cơ bản về docker.

Tạo Laravel App

Việc tạo ra một Laravel app bằng composer đã trở nên rất quen thuộc với chúng ta rồi đúng không?, mình sẽ không quá đi sâu chi tiết về nó nữa các bạn có thể lên Laravel doc để  install nhé! Hoặc có thể làm theo bước dưới đây. Nếu bạn nào sử dụng cho dự án của mình thì bỏ qua các bước dưới đây nhé!

  • Composer: Cài đặt Composer, trình quản lý gói PHP, từ trang web chính thức.

  • Mở terminal và di chuyển đến thư mục mà bạn muốn tạo dự án Laravel mới.

  • Chạy lệnh sau để tạo dự án mới:

    composer create-project laravel/laravel example-app​

    Thay example-app​ bằng tên mà bạn muốn đặt cho dự án của mình.

  • Di chuyển vào thư mục dự án mới:

    cd example-app​

Chuẩn Bị

Trước khi dựng docker cho dự án thì các bạn nên biết trong hệ thống của mình có những services gì ví dụ trong demo này sẽ bao gồm :

  • Nginx
  • MariaDB
  • PHP và dependencies
  • Phpmyadmin management

Trong trường hợp các bạn sử dụng Redis và Elastic search ... thì các bạn cũng nên liệt kê ra trước để biết hệ thống của mình cần pull những image nào, cần phải custom những image nào.

Mình sẽ liệt kê các bước mà chúng ta cần phải làm để buid được system của chúng ta:

  1. Tạo Dockerfile: Tạo một Dockerfile trong thư mục gốc của dự án Laravel để xây dựng image của ứng dụng. Dockerfile sẽ chứa các bước cần thiết để cài đặt và cấu hình môi trường chạy của ứng dụng Laravel.

    1. Cài Đặt PHP và Composer: Trong Dockerfile, cài đặt PHP và Composer để cài đặt các dependency và chạy ứng dụng Laravel. Sử dụng các image Docker chứa PHP và Composer.

    2. Quản Lý Dependency: Đảm bảo rằng tất cả các dependency của dự án Laravel đã được định nghĩa rõ ràng trong tệp composer.json. Chạy composer install để cài đặt các dependency trong Dockerfile.

    3. Cấu Hình Môi Trường: Sử dụng các biến môi trường để cấu hình ứng dụng Laravel, bao gồm cả biến môi trường cơ sở dữ liệu, cài đặt ứng dụng, và các cài đặt khác.

  2. Viết docker-compose để quản lý các services:
    1. Laravel app
    2. Nginx
    3. MariaDB
    4. Phpmyadmin
  3. Kiểm Tra và Debug: Kiểm tra và debug quá trình xây dựng Dockerfile và Docker Compose file để đảm bảo rằng mọi thứ hoạt động như mong đợi trước khi triển khai.

Cấu trúc thư mục:

Trước khi sang bước build các bạn hãy tạo ra một cấu trúc thư mục để chúng ta có thể quản lý một cách dễ dàng nhé !

laravel-project/
├── app/                     # Chứa code của ứng dụng
│   ├── Console/             # Chứa các commands
│   ├── Exceptions/          # Chứa các exception handlers
│   ├── Http/                # Chứa các controllers, middleware, và requests
│   ├── Models/              # Chứa các models
│   └── ...
├── docker/                  # Chứa code setup của docker
│   ├── mysql/               # Cấu hình db cho container db
│   ├── ├── conf.d/          # Chứa các file config để mount vào container
│   ├── ├──├── my.cnf        # file cấu hình mysql
│   ├── nginx/               # Chứa các file cấu hình nginx
│   ├── ├── conf.d/          # Thư mục chứa cấu hình nginx
│   ├── ├──├── default.conf  # Cấu hình nginx
│   ├── php/                 # Chứa file cấu hình php được mount vào trong container 
│   ├── ├──php.init            
├── ...
├── .env                     # File cấu hình môi trường
├── .env.example             # File mẫu cấu hình môi trường
├── Dockerfile               # File script custom docker image
├── dockder-compose.yml      # File định nghĩa và cấu hình các dịch vụ
├── docker-start.sh          # File run cmd để build môi trường cho laravel
└── ...

Build

Hãy vào github của mình để xem toàn bộ: Link

Tạo Dockerfile

Trước tiên các bạn vào thự mục của dự án

cd example-app​

Sau đó tạo cho mình một file ở thư mục gốc là: Dockerfile, Dockerfile này sẽ chứa các bước cần thiết để cài đặt và cấu hình môi trường chạy của ứng dụng Laravel, trong file này chúng ta sẽ đi cài đặt PHP và các dependencies của nó, thiết lập thư mục cho dự án của chúng ta trong container, cũng như một vài command để hỗ trợ build được dự án laravel, mình sẽ để đoạn code dưới đây và giải thích cho các bạn hiểu từng đoạn code.

dockerfile:

FROM php:8.1-fpm-alpine

# Set working directory
ARG workdir=/var/www

WORKDIR $workdir

# Install system dependencies
RUN apk update
RUN apk add --no-cache \
    libjpeg-turbo-dev \
    libpng-dev \
    libwebp-dev \
    freetype-dev \
    libzip-dev \
    zip \
    bash \
    dos2unix

# Install PHP extensions
RUN docker-php-ext-install pdo pdo_mysql
RUN docker-php-ext-install mysqli && docker-php-ext-enable mysqli
RUN docker-php-ext-install exif
RUN docker-php-ext-install zip
RUN docker-php-ext-configure gd --with-freetype --with-jpeg
RUN docker-php-ext-install -j$(nproc) gd

#syns php.init
COPY ./docker/php/php.ini /usr/local/etc/php/

COPY . .

# Get latest Composer
COPY --from=composer /usr/bin/composer /usr/bin/composer

# Copy the docker-app-start.sh script from the local directory into the container
COPY docker-start.sh /var/www/

# Set executable permission for docker-app-start.sh
RUN chmod +x /var/www/docker-start.sh

CMD ["/var/www/docker-start.sh"]

docker-start.sh

#!/bin/bash
composer install
php /var/www/artisan key:generate
php /var/www/artisan migrate
#php /var/www/artisan queue:listen --timeout=0 &

php-fpm
  1. FROM php:8.1-fpm-alpine: Đây là lệnh cơ bản trong Dockerfile để xác định image base cho container của bạn. Trong trường hợp này, chúng ta đang sử dụng image php:8.1-fpm-alpine, là một image chứa PHP 8.1 và FPM trên Alpine Linux.

  2. ARG workdir=/var/www: Định nghĩa một biến workdir để lưu đường dẫn đến thư mục làm việc trong container. Giá trị mặc định là /var/www.

  3. WORKDIR $workdir: Đặt thư mục làm việc của container thành giá trị của biến workdir.

  4. RUN apk update: Cập nhật các gói trong Alpine Linux.

  5. RUN apk add --no-cache ...: Cài đặt các dependency cần thiết cho PHP và ứng dụng Laravel, bao gồm các thư viện liên quan đến hình ảnh (libjpeg-turbo-dev, libpng-dev, libwebp-dev, freetype-dev), thư viện zip (libzip-dev, zip), bash (để chạy các lệnh Bash), dos2unix (để chuyển đổi định dạng tệp tin giữa Windows và Unix).

  6. RUN docker-php-ext-install ...: Cài đặt các extension PHP cần thiết, bao gồm pdo, pdo_mysql, mysqli, exif, zip và gd (cho xử lý hình ảnh).

  7. COPY . .: Sao chép toàn bộ nội dung của thư mục hiện tại (thư mục chứa Dockerfile) vào thư mục làm việc của container.

  8. COPY --from=composer ...: Sao chép Composer từ một stage trước đó trong quá trình xây dựng (được định nghĩa trong Dockerfile nhưng không được hiển thị trong phần bạn đã cung cấp) vào đường dẫn /usr/bin/composer trong container.

  9. RUN chmod +x /var/www/docker-start.sh: Cấp quyền thực thi cho tệp docker-start.sh, một tập lệnh được sử dụng để khởi động ứng dụng Laravel.

  10. CMD ["/var/www/docker-start.sh"]: Đặt lệnh mặc định khi container khởi động là thực thi tệp docker-start.sh. Điều này sẽ khởi động ứng dụng Laravel khi container được triển khai.

Viết docker-compose

Trong thư mục gốc của dự án các bạn tạo thêm cho mình một file name docker-compose.yml sau đó thì chúng ta sẽ cùng nhau viết script cho từng service.

Để các bạn dễ hiểu hơn mình sẽ chia nhỏ ra từng service và trong mỗi service mình sẽ mô tả chi tiết.

1.Buid service db:

Trước khi viết docker compose cho service này các bạn cần phải chắc chắn là đã tạo ra thư mục "./docker/mysql" và chứa file my.cnf với config như sau, cái này do từ mình định nghĩa trong trường hợp bạn chạy dự án cần bổ sung cấu hình thì thiết lập thêm nhé!

my.cnf
######################################
### Nhóm mysqld: Cấu hình cho máy chủ MySQL
######################################

[mysqld]
# Cấu hình bộ ký tự/ thứ tự so sánh
character-set-server = utf8mb4
collation-server = utf8mb4_bin

######################################
### Nhóm mysql: Cấu hình cho các tùy chọn của MySQL
######################################

[mysql]
# Cấu hình bộ ký tự mặc định
default-character-set = utf8mb4

######################################
### Nhóm client: Cấu hình cho các công cụ client của MySQL
######################################

[client]
# Cấu hình bộ ký tự mặc định
default-character-set = utf8mb4

Cấu hình trong docker-compose.yml

  db:
    container_name: db-app
    image: mariadb:latest
    restart: unless-stopped
    environment:
      MARIADB_ROOT_PASSWORD: app
      MARIADB_DATABASE: db_app
      MARIADB_USER: root
      MARIADB_PASSWORD: app
    volumes:
      - ./docker/mysql/conf.d:/etc/mysql/conf.d
      - db-store:/var/lib/mysql
    networks:
      - network-app
  1. db: Đây là tên của dịch vụ, định danh mà bạn sẽ sử dụng để gọi dịch vụ này trong các phần khác của tệp Docker Compose hoặc trong các lệnh Docker Compose.

  2. container_name: db-app: Đây là tên của container được tạo ra từ image mariadb:latest. Khi bạn chạy docker ps, bạn sẽ thấy container này được đặt tên là db-app.

  3. image: mariadb:latest: Đây là image được sử dụng để tạo container cho dịch vụ. Trong trường này, image là mariadb:latest, nghĩa là Docker sẽ sử dụng image mới nhất của MariaDB.

  4. restart: unless-stopped: Điều này chỉ định rằng container sẽ tự động khởi động lại nếu nó bị dừng, trừ khi bạn tường minh dừng nó.

  5. environment: Đây là các biến môi trường được sử dụng bên trong container. Trong trường này, chúng đặt các biến môi trường cho MariaDB, bao gồm mật khẩu cho người dùng root, tên cơ sở dữ liệu và mật khẩu cho người dùng cơ sở dữ liệu.

  6. volumes: Đây là khai báo cho các khối dữ liệu được chia sẻ giữa container và máy host. Trong trường này, nó chia sẻ một thư mục local (./docker/mysql/conf.d) với thư mục /etc/mysql/conf.d trong container và một volume có tên db-store với thư mục /var/lib/mysql trong container.

  7. networks: Đây là các mạng mà container sẽ được tham gia. Trong trường này, container sẽ tham gia vào mạng có tên network-app.

2. Buid service app:

Để có thể build được service app các bạn chuẩn bị cho mình một số bước sau:

Tạo thêm một thư mục "docker/php" và tạo thêm file php.init , nó có nhiệm vụ mount vào trong container để chúng ta có thể kiểm soát được cấu hình của PHP. Dứoi đây mình chỉ lấy ra một vài config chủ yếu, còn trong trường hợp bạn cần bổ sung thì cứ thêm vào đây nhé!

php.init

date.timezone = Asia/Ho_Chi_Minh

log_errors = On
error_log = /dev/stderr
display_errors = Off

output_buffering = On

mbstring.language = ""

Bước tiếp theo viết docker-compose.yml cho service app

version: '3.8'

services: 
 app:
    container_name: app-app
    restart: unless-stopped
    build: 
      context: .
      dockerfile: Dockerfile
    volumes:
      - ./:/var/www
      - ./docker/php/php.ini:/usr/local/etc/php/php.ini
    networks:
      - network-app
    environment:
      MARIADB_HOST: db
      MARIADB_PORT: 3306
      MARIADB_DATABASE: db_app
      MARIADB_USER: root
      MARIADB_PASSWORD: app
  1. container_name: app-app: Đặt tên cho container của service là app-app.

  2. restart: unless-stopped: Xác định cách Docker sẽ tự động khởi động lại container trong trường hợp container bị dừng. Trong trường hợp này, container sẽ tự động khởi động lại trừ khi bạn tắt nó bằng cách sử dụng lệnh docker-compose down.

  3. build: Xác định cách Docker sẽ xây dựng image cho container của service. Trong trường hợp này, Docker sẽ xây dựng image từ Dockerfile được chỉ định.

    • context: .: Chỉ định thư mục gốc chứa Dockerfile. Trong trường hợp này, Dockerfile được tìm thấy trong thư mục hiện tại (thư mục chứa file docker-compose.yml).

    • dockerfile: Dockerfile: Chỉ định tên của Dockerfile sẽ được sử dụng để xây dựng image.

  4. volumes: Xác định các volumes sẽ được mount vào container. Trong trường hợp này:

    • ./:/var/www: Mount thư mục hiện tại vào thư mục làm việc của container. Điều này cho phép bạn chỉnh sửa code trong máy host và thấy thay đổi ngay lập tức trong container.

    • ./docker/php/php.ini:/usr/local/etc/php/php.ini: Mount tệp php.ini-development từ thư mục docker/php vào container. Điều này cho phép bạn cấu hình cài đặt PHP cho ứng dụng Laravel của bạn.

  5. networks: Xác định mạng mà container sẽ tham gia. Trong trường hợp này, container sẽ tham gia vào mạng có tên là network-app. Điều này cho phép các container khác trong cùng một mạng tương tác với nhau.

  6. environment: Xác định các biến môi trường sẽ được set trong container. Trong trường hợp này, các biến môi trường liên quan đến cơ sở dữ liệu MariaDB được cấu hình, bao gồm MARIADB_HOST, MARIADB_PORT, MARIADB_DATABASE, MARIADB_USER, và MARIADB_PASSWORD. Điều này cho phép ứng dụng Laravel của bạn kết nối đến cơ sở dữ liệu MariaDB được chạy trong container khác.

3. Buid service nginx:

Trước khi build service nginx, trong folder docker các bạn hãy tạo cho mình một thư mục "nginx " sau đó thì tiếp tục tạo thêm folder "conf.d", sau đó thi các bạn thêm file default.conf và copy đoạn cấu hình nginx bên dưới đây nhé !

server {
    listen 80 default_server;
    server_name localhost;

    root /var/www/public;
    index index.php index.html index.htm;

    location / {
        try_files $uri $uri/ /index.php?$args;
    }

    location ~ \.php(/|$) {
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        # app là tên của container mà PHP-FPM đang được sử dụng.
        # 9000 là cổng mà PHP-FPM đang lắng nghe.
        # Mục đính chỉ định Nginx sử dụng FastCGI để chuyển các yêu cầu PHP đến một trình quản lý quá trình PHP
        fastcgi_pass   app:9000;
        fastcgi_index  index.php;
        include        fastcgi_params;
        fastcgi_param  SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param  PATH_INFO $fastcgi_path_info;
    }
}

Bước tiếp theo viết docker-compose.yml cho service nginx 

  nginx:
    container_name: nginx-service
    restart: unless-stopped
    image: nginx:latest
    volumes:
      - ./docker/nginx/conf.d/dev.conf:/etc/nginx/conf.d/dev.conf
      - /dev/null:/etc/nginx/conf.d/default.conf
      - ./docker/nginx/generalconfig:/etc/nginx/generalconfig
    ports:
      - 9001:80
    networks:
      - network-app
    depends_on:
      - app
  1. nginx: Đây là tên của dịch vụ, định danh mà bạn sẽ sử dụng để gọi dịch vụ này trong các phần khác của tệp Docker Compose hoặc trong các lệnh Docker Compose.

  2. container_name: nginx-service: Đây là tên của container được tạo ra từ image nginx:latest. Khi bạn chạy docker ps, bạn sẽ thấy container này được đặt tên là nginx-service.

  3. restart: unless-stopped: Điều này chỉ định rằng container sẽ tự động khởi động lại nếu nó bị dừng, trừ khi bạn tường minh dừng nó.

  4. image: nginx:latest: Đây là image được sử dụng để tạo container cho dịch vụ. Trong trường này, image là nginx:latest, nghĩa là Docker sẽ sử dụng image mới nhất của Nginx.

  5. volumes: Đây là khai báo cho các khối dữ liệu được chia sẻ giữa container và máy chủ. Trong trường này, nó chia sẻ một tệp cấu hình Nginx local (./docker/nginx/conf.d/default.conf) với tệp cấu hình mặc định (/etc/nginx/conf.d/default.conf) trong container.

  6. ports: Đây là khai báo cho các cổng mạng được mở ra từ container và kết nối với cổng mạng trên máy chủ. Trong trường này, nó liên kết cổng 9001 trên máy chủ với cổng 80 trong container.

  7. networks: Đây là các mạng mà container sẽ được tham gia. Trong trường này, container sẽ tham gia vào mạng có tên network-app.

  8. depends_on: Đây là danh sách các dịch vụ mà dịch vụ hiện tại phụ thuộc vào. Trong trường này, dịch vụ Nginx phụ thuộc vào dịch vụ có tên là app. Điều này có nghĩa là khi bạn chạy dịch vụ Nginx, Docker Compose sẽ tự động khởi chạy dịch vụ app trước đó nếu nó chưa được khởi chạy.

4. Buid service phpmyadmin:

  phpmyadmin:
    platform: linux/x86_64
    image: phpmyadmin/phpmyadmin
    container_name: phpmyadmin
    depends_on:
      - db
    ports:
      - 91:80
    environment:
      PMA_PORT: 3306
      PMA_HOST: db
    networks:
      - network-app
  1. phpmyadmin: Đây là tên của service hoặc container PHPMyAdmin.
  2. platform: linux/x86_64: Điều này chỉ định nền tảng của hình ảnh Docker mà container sẽ chạy trên. Trong trường hợp này, nó là Linux trên kiến trúc x86_64.
  3. image: phpmyadmin/phpmyadmin: Đây là tên và phiên bản của hình ảnh Docker mà container sẽ được dựng dựa trên. Trong trường hợp này, nó sử dụng hình ảnh phpmyadmin/phpmyadmin.
  4. container_name: phpmyadmin: Đây là tên mà Docker sẽ sử dụng để định danh container khi nó được tạo.
  5. depends_on: Đây là danh sách các services mà service hiện tại phụ thuộc vào. Trong trường hợp này, service PHPMyAdmin phụ thuộc vào service có tên "db". Điều này có nghĩa là service PHPMyAdmin sẽ không được khởi động cho đến khi service "db" đã được khởi động thành công.
  6. ports: Đây là cách các cổng trên máy chủ được ánh xạ với cổng trong container. Trong trường hợp này, cổng 91 của máy chủ được ánh xạ với cổng 80 của container, cho phép truy cập vào giao diện web của PHPMyAdmin qua cổng 91.
  7. environment: Đây là các biến môi trường được sử dụng bởi PHPMyAdmin container để cấu hình kết nối với cơ sở dữ liệu. Trong trường hợp này:
    1. PMA_PORT: 3306: Xác định cổng của cơ sở dữ liệu MySQL/MariaDB, mặc định là 3306.
    2. PMA_HOST: db: Xác định hostname của cơ sở dữ liệu MySQL/MariaDB container, trong trường hợp này là "db".
  8. networks: Đây là danh sách các mạng mà container sẽ kết nối đến. Trong trường hợp này, nó được kết nối đến mạng có tên là "network-app".

Full code:

version: '3.8'

volumes:
  db-store:
networks:
  network-app:
    driver: bridge

services:
  db:
    container_name: db-app
    image: mariadb:latest
    restart: unless-stopped
    environment:
      MARIADB_ROOT_PASSWORD: app
      MARIADB_DATABASE: db_app
      MARIADB_USER: root
      MARIADB_PASSWORD: app
    volumes:
      - ./docker/mysql/conf.d:/etc/mysql/conf.d
      - db-store:/var/lib/mysql
    networks:
      - network-app
  app:
    container_name: app-app
    restart: unless-stopped
    build:
      context: .
      dockerfile: Dockerfile
    volumes:
      - ./:/var/www
      - ./docker/php/php.ini:/usr/local/etc/php/php.ini
    networks:
      - network-app
    environment:
      MARIADB_HOST: db
      MARIADB_PORT: 3306
      MARIADB_DATABASE: db_app
      MARIADB_USER: root
      MARIADB_PASSWORD: app
  nginx:
    container_name: nginx-service
    restart: unless-stopped
    image: nginx:latest
    volumes:
      - ./docker/nginx/conf.d/default.conf:/etc/nginx/conf.d/default.conf
    ports:
      - 9001:80
    networks:
      - network-app
    depends_on:
      - app
  phpmyadmin:
    platform: linux/x86_64
    image: phpmyadmin/phpmyadmin
    container_name: phpmyadmin
    depends_on:
      - db
    ports:
      - 91:80
    environment:
      PMA_PORT: 3306
      PMA_HOST: db
    networks:
      - network-app

      

Bình luận (0)

Michael Gough
Michael Gough
Michael Gough
Michael Gough
Michael Gough
Michael Gough
Michael Gough
Michael Gough
Michael Gough
Michael Gough
Michael Gough
Michael Gough
Michael Gough
Michael Gough
Michael Gough
Michael Gough

Bài viết liên quan

Learning English Everyday