Tìm kiếm trang web

Cách Cài Đặt và Thiết Lập Laravel với Docker Compose trên Ubuntu 22.04


Giới thiệu

Việc đóng gói ứng dụng vào container đề cập đến quá trình điều chỉnh một ứng dụng và các thành phần của nó để có thể chạy trong các môi trường nhẹ được gọi là container. Những môi trường này được tách biệt và có thể loại bỏ, và có thể được sử dụng để phát triển, kiểm thử và triển khai ứng dụng lên môi trường sản xuất.

Trong hướng dẫn này, chúng tôi sẽ sử dụng Docker Compose để đóng gói một ứng dụng Laravel vào container cho mục đích phát triển. Khi hoàn tất, bạn sẽ có một ứng dụng Laravel demo chạy trên ba container dịch vụ riêng biệt:

  • Một dịch vụ app chạy PHP7.4-FPM;
  • Một dịch vụ db đang chạy MySQL 8.0;
  • Một dịch vụ nginx sử dụng dịch vụ app để phân tích mã PHP trước khi phục vụ ứng dụng Laravel đến người dùng cuối.

Để cho phép quá trình phát triển được tinh gọn và tạo điều kiện thuận lợi cho việc gỡ lỗi ứng dụng, chúng ta sẽ giữ các tệp ứng dụng đồng bộ bằng cách sử dụng các volume chia sẻ. Chúng ta cũng sẽ xem cách sử dụng lệnh docker compose exec để chạy Composer và Artisan trên container app.

Triển khai các ứng dụng frontend của bạn từ GitHub bằng Nền tảng Ứng dụng Linux-Console.net. Hãy để Linux-Console.net tập trung vào việc mở rộng ứng dụng của bạn.

Những điểm chính cần lưu ý:

  • Container hóa làm cho việc phát triển trở nên dự đoán được: Chạy Laravel trong Docker đảm bảo môi trường nhất quán trên các máy, loại bỏ vấn đề “trên máy tôi chạy được” và giúp việc cộng tác dễ dàng hơn.
  • Tách biệt dịch vụ giúp cải thiện khả năng bảo trì: Việc tách PHP, MySQL và Nginx vào các container riêng biệt giúp cô lập các vấn đề, đơn giản hóa việc cập nhật và mô phỏng môi trường sản xuất thực tế.
  • Biến môi trường là trung tâm của cấu hình linh hoạt: Sử dụng tệp .env cho phép các nhà phát triển thay đổi thông tin đăng nhập cơ sở dữ liệu và các thiết lập khác mà không cần chạm vào mã ứng dụng.
  • Hình ảnh Docker tùy chỉnh cải thiện kiểm soát và bảo mật: Tạo hình ảnh PHP riêng của bạn đảm bảo bạn chỉ cài đặt những gì dự án của bạn cần, giúp môi trường nhẹ và an toàn.
  • Quyền truy cập tệp phải phù hợp với người dùng trong container: Thiết lập quyền sở hữu và quyền truy cập chính xác cho các thư mục có thể ghi của Laravel sẽ ngăn ngừa lỗi khi chạy và hỗ trợ phát triển cục bộ suôn sẻ.
  • Sử dụng các tệp override hỗ trợ nhiều môi trường: docker-compose.override.yml cho phép điều chỉnh cục bộ—như tải lại trực tiếp—mà không làm thay đổi cấu hình sản xuất. Điều này giúp tách biệt rõ ràng giữa phát triển và triển khai.
  • Khắc phục sự cố bắt đầu bằng việc hiểu cách hoạt động của container: Biết cách kiểm tra nhật ký, kiểm tra các dịch vụ đang chạy và kiểm tra kết nối là điều then chốt để giải quyết hầu hết các vấn đề nhanh chóng trong môi trường container hóa.
  • Một thiết lập dựa trên Docker phát triển cùng với quy trình làm việc của bạn: Khi đã được xây dựng, cùng một cấu hình có thể hỗ trợ các môi trường cục bộ, thử nghiệm hoặc sản xuất, tạo nền tảng ổn định và có thể tái sử dụng cho các dự án Laravel trong tương lai.

Điều kiện tiên quyết

  • Truy cập vào một máy Ubuntu cục bộ hoặc máy chủ phát triển với tư cách người dùng không phải root nhưng có quyền sudo. Nếu bạn đang sử dụng máy chủ từ xa, nên có tường lửa đang hoạt động. Để thiết lập những điều này, vui lòng tham khảo Hướng dẫn Cài đặt Máy chủ Ban đầu của chúng tôi cho Ubuntu.
  • Docker đã được cài đặt trên máy chủ của bạn, theo các Bước 1 và 2 của Hướng dẫn Cài đặt và Sử dụng Docker trên Ubuntu.
  • Docker Compose được cài đặt trên máy chủ của bạn, theo Bước 1 của Hướng dẫn Cách Cài đặt và Sử dụng Docker Compose trên Ubuntu.

Bước 1 — Lấy ứng dụng demo

Để bắt đầu, chúng ta sẽ lấy ứng dụng Laravel demo từ kho Github của nó. Chúng ta quan tâm đến nhánh tutorial-01, nhánh này chứa ứng dụng Laravel cơ bản mà chúng ta đã tạo trong hướng dẫn Cài đặt và Cấu hình Laravel với Nginx trên Ubuntu 22.04 (LEMP).

Để nhận mã ứng dụng tương thích với hướng dẫn này, hãy tải bản phát hành tutorial-1.0.1 về thư mục chính của bạn bằng cách:

cd ~

curl -L https://github.com/do-community/travellist-laravel-demo/archive/tutorial-1.0.1.zip -o travellist.zip

Chúng ta sẽ cần lệnh unzip để giải nén mã ứng dụng. Trong trường hợp bạn chưa cài gói này trước đây, hãy cài đặt ngay bây giờ bằng:

sudo apt update

sudo apt install unzip

Bây giờ, giải nén nội dung của ứng dụng và đổi tên thư mục vừa giải nén để dễ truy cập hơn:

unzip travellist.zip

mv travellist-laravel-demo-tutorial-1.0.1 travellist-demo

Điều hướng đến thư mục travellist-demo:

cd travellist-demo

Trong bước tiếp theo, chúng ta sẽ tạo một tệp cấu hình .env để thiết lập ứng dụng.

Bước 2 — Thiết lập tệp .env của ứng dụng

Các tập tin cấu hình của Laravel được đặt trong một thư mục có tên là config, nằm trong thư mục gốc của ứng dụng. Ngoài ra, một tập tin .env được sử dụng để thiết lập cấu hình phụ thuộc vào môi trường, chẳng hạn như thông tin đăng nhập và bất kỳ thông tin nào có thể khác nhau giữa các lần triển khai. Tập tin này không được bao gồm trong hệ thống quản lý phiên bản.

Cảnh báo: Tệp cấu hình môi trường chứa thông tin nhạy cảm về máy chủ của bạn, bao gồm thông tin đăng nhập cơ sở dữ liệu và các khóa bảo mật. Vì lý do đó, bạn không bao giờ nên chia sẻ tệp này công khai.

Các giá trị chứa trong file .env sẽ được ưu tiên hơn so với các giá trị được thiết lập trong các file cấu hình thông thường nằm ở thư mục config. Mỗi lần cài đặt trên một môi trường mới đều yêu cầu một file môi trường được tùy chỉnh để xác định các thiết lập như kết nối cơ sở dữ liệu, tùy chọn gỡ lỗi, URL ứng dụng, cùng với các mục khác có thể thay đổi tùy thuộc vào môi trường mà ứng dụng đang chạy.

Bây giờ chúng ta sẽ tạo một tệp .env mới để tùy chỉnh các tùy chọn cấu hình cho môi trường phát triển mà chúng ta đang thiết lập. Laravel đi kèm với một tệp .env mẫu mà chúng ta có thể sao chép để tạo tệp riêng của mình:

cp .env.example .env

Mở tệp này bằng nano hoặc trình soạn thảo văn bản mà bạn chọn:

nano .env

Tập tin .env hiện tại từ ứng dụng demo travellist chứa các thiết lập để sử dụng cơ sở dữ liệu MySQL cục bộ, với 127.0.0.1 là máy chủ cơ sở dữ liệu. Chúng ta cần cập nhật biến DB_HOST để nó trỏ đến dịch vụ cơ sở dữ liệu mà chúng ta sẽ tạo trong môi trường Docker. Trong hướng dẫn này, chúng ta sẽ gọi dịch vụ cơ sở dữ liệu của mình là db. Hãy tiếp tục và thay thế giá trị hiện tại của DB_HOST bằng tên dịch vụ cơ sở dữ liệu:

APP_NAME=Travellist
APP_ENV=dev
APP_KEY=
APP_DEBUG=true
APP_URL=http://localhost:8000

LOG_CHANNEL=stack

DB_CONNECTION=mysql
DB_HOST=db
DB_PORT=3306
DB_DATABASE=travellist
DB_USERNAME=travellist_user
DB_PASSWORD=password
...

Bạn cũng có thể thay đổi tên cơ sở dữ liệu, tên người dùng và mật khẩu nếu muốn. Những biến này sẽ được sử dụng trong bước sau, khi chúng ta thiết lập tệp docker-compose.yml để cấu hình các dịch vụ của mình.

Lưu tệp khi bạn đã chỉnh sửa xong. Nếu bạn dùng nano, bạn có thể làm điều đó bằng cách nhấn Ctrl+x, sau đó nhấn Y và Enter để xác nhận.

Bước 3 – Thiết lập Dockerfile của ứng dụng

Mặc dù cả dịch vụ MySQL và Nginx của chúng ta sẽ dựa trên các hình ảnh mặc định lấy từ Docker Hub, chúng ta vẫn cần xây dựng một hình ảnh tùy chỉnh cho container ứng dụng. Chúng ta sẽ tạo một Dockerfile mới cho việc đó.

Hình ảnh travellist của chúng tôi sẽ dựa trên hình ảnh PHP chính thức php:7.4-fpm từ Docker Hub. Trên môi trường PHP-FPM cơ bản đó, chúng tôi sẽ cài đặt thêm một vài module PHP và công cụ quản lý phụ thuộc Composer.

Chúng tôi cũng sẽ tạo một người dùng hệ thống mới; điều này cần thiết để thực thi các lệnh artisan và composer trong khi phát triển ứng dụng. Cài đặt uid đảm bảo rằng người dùng bên trong container có cùng uid với người dùng hệ thống của bạn trên máy chủ, nơi bạn đang chạy Docker. Bằng cách này, bất kỳ tệp nào được tạo ra bởi các lệnh này sẽ được sao chép trên máy chủ với quyền một cách chính xác. Điều này cũng có nghĩa là bạn sẽ có thể sử dụng trình chỉnh sửa mã ưa thích của mình trên máy chủ để phát triển ứng dụng đang chạy bên trong các container.

Tạo một Dockerfile mới với:

nano Dockerfile

Sao chép các nội dung sau vào Dockerfile của bạn:

FROM php:7.4-fpm

# Arguments defined in docker-compose.yml
ARG user
ARG uid

# Install system dependencies
RUN apt-get update && apt-get install -y \
    git \
    curl \
    libpng-dev \
    libonig-dev \
    libxml2-dev \
    zip \
    unzip

# Clear cache
RUN apt-get clean && rm -rf /var/lib/apt/lists/*

# Install PHP extensions
RUN docker-php-ext-install pdo_mysql mbstring exif pcntl bcmath gd

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

# Create system user to run Composer and Artisan Commands
RUN useradd -G www-data,root -u $uid -d /home/$user $user
RUN mkdir -p /home/$user/.composer && \
    chown -R $user:$user /home/$user

# Set working directory
WORKDIR /var/www

USER $user

Đừng quên lưu file khi bạn đã xong.

Dockerfile của chúng tôi bắt đầu bằng việc xác định hình ảnh cơ sở mà chúng tôi đang sử dụng: php:7.4-fpm.

Sau khi cài đặt các gói hệ thống và phần mở rộng PHP, chúng tôi cài đặt Composer bằng cách sao chép tệp thực thi composer từ hình ảnh chính thức mới nhất của nó sang hình ảnh ứng dụng của chúng tôi.

Một người dùng hệ thống mới sau đó được tạo và thiết lập bằng cách sử dụng các đối số user và uid đã được khai báo ở đầu Dockerfile. Các giá trị này sẽ được Docker Compose chèn vào trong thời gian xây dựng.

Cuối cùng, chúng tôi đặt thư mục làm việc mặc định là /var/www và chuyển sang người dùng mới tạo. Điều này sẽ đảm bảo bạn đang kết nối với tư cách là người dùng thông thường và đang ở đúng thư mục khi chạy các lệnh composer và artisan trên container ứng dụng.

Bước 4 — Cấu hình Nginx và Tệp sao lưu cơ sở dữ liệu

Khi tạo môi trường phát triển với Docker Compose, thường cần chia sẻ các tệp cấu hình hoặc khởi tạo với các container dịch vụ, nhằm thiết lập hoặc khởi động các dịch vụ đó. Thực hành này giúp dễ dàng thay đổi các tệp cấu hình để tinh chỉnh môi trường của bạn trong khi đang phát triển ứng dụng.

Bây giờ chúng ta sẽ tạo một thư mục với các tệp sẽ được sử dụng để cấu hình và khởi tạo các container dịch vụ của chúng ta.

Để cài đặt Nginx, chúng tôi sẽ chia sẻ một tệp travellist.conf để cấu hình cách ứng dụng được phục vụ. Tạo thư mục docker-compose/nginx với:

mkdir -p docker-compose/nginx

Mở một tệp mới có tên travellist.conf trong thư mục đó:

nano docker-compose/nginx/travellist.conf

Sao chép cấu hình Nginx sau vào tệp đó:

server {
    listen 80;
    index index.php index.html;
    error_log  /var/log/nginx/error.log;
    access_log /var/log/nginx/access.log;
    root /var/www/public;
    location ~ \.php$ {
        try_files $uri =404;
        fastcgi_split_path_info ^(.+\.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;
    }
    location / {
        try_files $uri $uri/ /index.php?$query_string;
        gzip_static on;
    }
}

Tệp này sẽ cấu hình Nginx để lắng nghe trên cổng 80 và sử dụng index.php làm trang chỉ mục mặc định. Nó sẽ đặt thư mục gốc tài liệu là /var/www/public, sau đó cấu hình Nginx để sử dụng dịch vụ app trên cổng 9000 để xử lý các tệp *.php.

Lưu và đóng tệp khi bạn đã chỉnh sửa xong.

Để thiết lập cơ sở dữ liệu MySQL, chúng tôi sẽ chia sẻ một bản sao cơ sở dữ liệu sẽ được nhập khi container được khởi tạo. Đây là một tính năng được cung cấp bởi hình ảnh MySQL 8.0 mà chúng tôi sẽ sử dụng trên container đó.

Tạo một thư mục mới cho các tập tin khởi tạo MySQL của bạn bên trong thư mục docker-compose:

mkdir docker-compose/mysql

Mở một tệp .sql mới:

nano docker-compose/mysql/init_db.sql

Bản sao lưu MySQL sau dựa trên cơ sở dữ liệu mà chúng tôi đã thiết lập trong hướng dẫn Laravel trên LEMP. Nó sẽ tạo một bảng mới có tên places. Sau đó, nó sẽ điền bảng này bằng một tập hợp các địa điểm mẫu.

Thêm đoạn mã sau vào tệp:

DROP TABLE IF EXISTS `places`;

CREATE TABLE `places` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
  `visited` tinyint(1) NOT NULL DEFAULT '0',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=12 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

INSERT INTO `places` (name, visited) VALUES ('Berlin',0),('Budapest',0),('Cincinnati',1),('Denver',0),('Helsinki',0),('Lisbon',0),('Moscow',1),('Nairobi',0),('Oslo',1),('Rio',0),('Tokyo',0);

Bảng places chứa ba trường: id, name và visited. Trường visited là một cờ dùng để xác định những địa điểm vẫn chưa đi. Bạn có thể thay đổi các địa điểm mẫu hoặc thêm địa điểm mới nếu muốn. Lưu và đóng tệp khi bạn hoàn tất.

Chúng tôi đã hoàn tất việc thiết lập Dockerfile của ứng dụng và các tệp cấu hình dịch vụ. Tiếp theo, chúng tôi sẽ thiết lập Docker Compose để sử dụng các tệp này khi tạo các dịch vụ của mình.

Bước 5 — Tạo môi trường đa container với Docker Compose

Docker Compose cho phép bạn tạo các môi trường đa container cho các ứng dụng chạy trên Docker. Nó sử dụng định nghĩa dịch vụ để xây dựng các môi trường có thể tùy chỉnh hoàn toàn với nhiều container có thể chia sẻ mạng và ổ dữ liệu. Điều này cho phép tích hợp liền mạch giữa các thành phần của ứng dụng.

Để thiết lập các định nghĩa dịch vụ của chúng ta, chúng ta sẽ tạo một file mới có tên docker-compose.yml. Thông thường, file này nằm ở thư mục gốc của ứng dụng, và nó định nghĩa môi trường container của bạn, bao gồm các image cơ bản mà bạn sẽ sử dụng để xây dựng các container, cũng như cách các dịch vụ của bạn sẽ tương tác với nhau.

Chúng ta sẽ định nghĩa ba dịch vụ khác nhau trong file docker-compose.yml của mình: app, db và nginx.

Dịch vụ app sẽ xây dựng một image có tên là travellist, dựa trên Dockerfile mà chúng ta đã tạo trước đó. Container được định nghĩa bởi dịch vụ này sẽ chạy một server php-fpm để phân tích mã PHP và gửi kết quả trở lại dịch vụ nginx, dịch vụ này sẽ chạy trên một container riêng biệt. Dịch vụ mysql định nghĩa một container chạy server MySQL 8.0. Các dịch vụ của chúng ta sẽ chia sẻ một mạng cầu nối có tên là travellist.

Các tệp ứng dụng sẽ được đồng bộ trên cả dịch vụ app và nginx thông qua bind mounts. Bind mounts rất hữu ích trong môi trường phát triển vì chúng cho phép đồng bộ hai chiều hiệu quả giữa máy chủ và các container.

Tạo một tệp docker-compose.yml mới tại thư mục gốc của ứng dụng:

nano docker-compose.yml

Một file docker-compose.yml điển hình bắt đầu với định nghĩa phiên bản, sau đó là một nút services, trong đó tất cả các dịch vụ được định nghĩa. Các mạng chia sẻ thường được định nghĩa ở cuối file đó.

Để bắt đầu, hãy sao chép đoạn mã mẫu này vào tệp docker-compose.yml của bạn:

version: "3.9"
services:


networks:
  travellist:
    driver: bridge

Bây giờ chúng ta sẽ chỉnh sửa nút services để bao gồm các dịch vụ app, db và nginx.

Dịch vụ app

Dịch vụ app sẽ thiết lập một container với tên travellist-app. Nó tạo một image Docker mới dựa trên Dockerfile nằm trong cùng đường dẫn với file docker-compose.yml. Image mới sẽ được lưu trữ cục bộ với tên travellist.

Mặc dù thư mục gốc của tài liệu được phục vụ như ứng dụng nằm trong container nginx, chúng ta vẫn cần các tệp ứng dụng ở đâu đó bên trong container app, để có thể thực thi các tác vụ dòng lệnh với công cụ Laravel Artisan.

Sao chép định nghĩa dịch vụ sau vào dưới node services của bạn, bên trong tệp docker-compose.yml:

  app:
    build:
      args:
        user: sammy
        uid: 1000
      context: ./
      dockerfile: Dockerfile
    image: travellist
    container_name: travellist-app
    restart: unless-stopped
    working_dir: /var/www/
    volumes:
      - ./:/var/www
    networks:
      - travellist

Các cài đặt này thực hiện các chức năng sau:

  • build: Cấu hình này cho Docker Compose biết cách xây dựng một hình ảnh cục bộ cho dịch vụ app, sử dụng đường dẫn (context) và Dockerfile được chỉ định để làm theo các hướng dẫn. Các đối số user và uid được truyền vào Dockerfile để tùy chỉnh các lệnh tạo người dùng khi xây dựng.
  • image: Tên sẽ được sử dụng cho hình ảnh đang được xây dựng.
  • container_name: Thiết lập tên container cho dịch vụ này.
  • restart: Luôn khởi động lại, trừ khi dịch vụ đã dừng.
  • working_dir: Đặt thư mục mặc định cho dịch vụ này là /var/www.
  • volumes: Tạo một volume chia sẻ sẽ đồng bộ nội dung từ thư mục hiện tại đến /var/www bên trong container. Lưu ý rằng đây không phải là thư mục gốc của tài liệu của bạn, bởi vì thư mục đó sẽ nằm trong container nginx.
  • networks: Thiết lập dịch vụ này để sử dụng một mạng có tên travellist.

Dịch vụ db

Dịch vụ db sử dụng một hình ảnh MySQL 8.0 đã được xây dựng sẵn từ Docker Hub. Vì Docker Compose tự động tải các tệp biến .env nằm trong cùng thư mục với tệp docker-compose.yml, chúng ta có thể lấy các thiết lập cơ sở dữ liệu từ tệp .env của Laravel mà chúng ta đã tạo trong bước trước.

Bao gồm định nghĩa dịch vụ sau trong nút services của bạn, ngay sau dịch vụ app:

  db:
    image: mysql:8.0
    container_name: travellist-db
    restart: unless-stopped
    environment:
      MYSQL_DATABASE: ${DB_DATABASE}
      MYSQL_ROOT_PASSWORD: ${DB_PASSWORD}
      MYSQL_PASSWORD: ${DB_PASSWORD}
      MYSQL_USER: ${DB_USERNAME}
      SERVICE_TAGS: dev
      SERVICE_NAME: mysql
    volumes:
      - ./docker-compose/mysql:/docker-entrypoint-initdb.d
    networks:
      - travellist

Các cài đặt này thực hiện các chức năng sau:

  • image: Xác định Docker image sẽ được sử dụng cho container này. Trong trường hợp này, chúng ta đang sử dụng image MySQL 8.0 từ Docker Hub.
  • container_name: Thiết lập tên container cho dịch vụ này: travellist-db.
  • restart: Luôn luôn khởi động lại dịch vụ này, trừ khi nó bị dừng rõ ràng.
  • environment: Định nghĩa các biến môi trường trong container mới. Chúng tôi đang sử dụng các giá trị lấy từ file .env của Laravel để thiết lập dịch vụ MySQL của mình, dịch vụ này sẽ tự động tạo một cơ sở dữ liệu và người dùng mới dựa trên các biến môi trường được cung cấp.
  • volumes: Tạo một volume để chia sẻ một bản dump cơ sở dữ liệu .sql sẽ được sử dụng để khởi tạo cơ sở dữ liệu ứng dụng. Hình ảnh MySQL sẽ tự động nhập các tệp .sql được đặt trong thư mục /docker-entrypoint-initdb.d bên trong container.
  • networks: Thiết lập dịch vụ này để sử dụng một mạng có tên travellist.

Dịch vụ nginx

Dịch vụ nginx sử dụng một hình ảnh Nginx đã được xây dựng sẵn trên nền Alpine, một bản phân phối Linux nhẹ. Nó tạo ra một container tên là travellist-nginx, và sử dụng định nghĩa ports để tạo một chuyển hướng từ cổng 8000 trên hệ thống chủ sang cổng 80 bên trong container.

Bao gồm định nghĩa dịch vụ sau trong nút services của bạn, ngay sau dịch vụ db:

  nginx:
    image: nginx:1.17-alpine
    container_name: travellist-nginx
    restart: unless-stopped
    ports:
      - 8000:80
    volumes:
      - ./:/var/www
      - ./docker-compose/nginx:/etc/nginx/conf.d
    networks:
      - travellist

Các cài đặt này thực hiện các chức năng sau:

  • image: Định nghĩa image Docker sẽ được sử dụng cho container này. Trong trường hợp này, chúng ta đang sử dụng image Alpine Nginx 1.17.
  • container_name: Thiết lập tên container cho dịch vụ này: travellist-nginx.
  • restart: Luôn luôn khởi động lại dịch vụ này, trừ khi nó bị dừng rõ ràng.
  • ports: Thiết lập chuyển tiếp cổng cho phép truy cập từ bên ngoài qua cổng 8000 đến máy chủ web đang chạy trên cổng 80 bên trong container.
  • volumes: Tạo hai volume chia sẻ. Volume đầu tiên sẽ đồng bộ nội dung từ thư mục hiện tại đến /var/www bên trong container. Bằng cách này, khi bạn thực hiện các thay đổi cục bộ trên các tệp ứng dụng, chúng sẽ nhanh chóng được phản ánh trong ứng dụng đang được Nginx phục vụ bên trong container. Volume thứ hai sẽ đảm bảo rằng tệp cấu hình Nginx của chúng ta, nằm tại docker-compose/nginx/travellist.conf, được sao chép vào thư mục cấu hình Nginx của container.
  • networks: Thiết lập dịch vụ này để sử dụng một mạng có tên travellist.

Hoàn thành tệp docker-compose.yml

Đây là cách mà file docker-compose.yml hoàn chỉnh của chúng ta trông như thế nào:

version: "3.9"
services:
  app:
    build:
      args:
        user: sammy
        uid: 1000
      context: ./
      dockerfile: Dockerfile
    image: travellist
    container_name: travellist-app
    restart: unless-stopped
    working_dir: /var/www/
    volumes:
      - ./:/var/www
    networks:
      - travellist

  db:
    image: mysql:8.0
    container_name: travellist-db
    restart: unless-stopped
    environment:
      MYSQL_DATABASE: ${DB_DATABASE}
      MYSQL_ROOT_PASSWORD: ${DB_PASSWORD}
      MYSQL_PASSWORD: ${DB_PASSWORD}
      MYSQL_USER: ${DB_USERNAME}
      SERVICE_TAGS: dev
      SERVICE_NAME: mysql
    volumes:
      - ./docker-compose/mysql:/docker-entrypoint-initdb.d
    networks:
      - travellist

  nginx:
    image: nginx:1.17-alpine
    container_name: travellist-nginx
    restart: unless-stopped
    ports:
      - 8000:80
    volumes:
      - ./:/var/www
      - ./docker-compose/nginx:/etc/nginx/conf.d/
    networks:
      - travellist

networks:
  travellist:
    driver: bridge

Hãy chắc chắn rằng bạn lưu tệp khi hoàn tất.

Bước 6 — Chạy Ứng Dụng với Docker Compose

Bây giờ chúng ta sẽ sử dụng các lệnh docker compose để xây dựng hình ảnh ứng dụng và chạy các dịch vụ mà chúng ta đã chỉ định trong cài đặt của mình.

Xây dựng hình ảnh app bằng lệnh sau:

docker compose build app

Lệnh này có thể mất vài phút để hoàn thành. Bạn sẽ thấy đầu ra tương tự như sau:

Building app
Sending build context to Docker daemon  377.3kB
Step 1/11 : FROM php:7.4-fpm
 ---> 8c08d993542f
Step 2/11 : ARG user
 ---> e3ce3af04d87
Step 3/11 : ARG uid
 ---> 30cb921ef7df
Step 4/11 : RUN apt-get update && apt-get install -y     git     curl     libpng-dev     libonig-dev     libxml2-dev     zip     unzip
. . .
 ---> b6dbc7a02e95
Step 5/11 : RUN apt-get clean && rm -rf /var/lib/apt/lists/*
 ---> 10ef9dde45ad
. . .
Step 6/11 : RUN docker-php-ext-install pdo_mysql mbstring exif pcntl bcmath gd
. . .
 ---> 920e4f09ec75
Step 7/11 : COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
 ---> dbbcd44e44af
Step 8/11 : RUN useradd -G www-data,root -u $uid -d /home/$user $user
 ---> db98e899a69a
Step 9/11 : RUN mkdir -p /home/$user/.composer &&     chown -R $user:$user /home/$user
 ---> 5119e26ebfea
Step 10/11 : WORKDIR /var/www
 ---> 699c491611c0
Step 11/11 : USER $user
 ---> cf250fe8f1af
Successfully built cf250fe8f1af
Successfully tagged travellist:latest

Khi quá trình xây dựng hoàn tất, bạn có thể chạy môi trường ở chế độ nền bằng:

docker compose up -d
Creating travellist-db    ... done
Creating travellist-app   ... done
Creating travellist-nginx ... done

Điều này sẽ chạy các container của bạn ở chế độ nền. Để hiển thị thông tin về trạng thái của các dịch vụ đang hoạt động, hãy chạy:

docker compose ps

Bạn sẽ thấy kết quả như sau:

      Name                    Command              State                  Ports                
-----------------------------------------------------------------------------------------------
travellist-app     docker-php-entrypoint php-fpm   Up      9000/tcp                            
travellist-db      docker-entrypoint.sh mysqld     Up      3306/tcp, 33060/tcp                 
travellist-nginx   nginx -g daemon off;            Up      0.0.0.0:8000->80/tcp,:::8000->80/tcp

Môi trường của bạn hiện đã được thiết lập và hoạt động.

Bây giờ bạn có thể kiểm tra xem mỗi dịch vụ đóng gói trong container có phản hồi đúng không bằng cách sử dụng các lệnh curl đơn giản.

  1. Để kiểm tra dịch vụ web Nginx:

    curl -I http://localhost:8000

    Đầu ra dự kiến:

    HTTP/1.1 200 OK
    Server: nginx/1.17.10
    Content-Type: text/html; charset=UTF-8

    Điều này xác nhận rằng container Nginx đang chạy và phục vụ các phản hồi từ ứng dụng Laravel của bạn.

  2. Để kiểm tra PHP-FPM qua Nginx:

    curl -s http://localhost:8000 | grep "Berlin"

    Nếu ứng dụng đang chạy đúng cách, lệnh này sẽ trả về HTML của trang chào mừng có chứa từ Berlin.

Chúng ta vẫn cần thực hiện vài lệnh để hoàn tất việc thiết lập ứng dụng. Bạn có thể sử dụng lệnh docker compose exec để chạy các lệnh trong các container dịch vụ, chẳng hạn như ls -l để hiển thị thông tin chi tiết về các tập tin trong thư mục ứng dụng:

docker compose exec app ls -l
total 256
-rw-r--r-- 1 sammy sammy    737 Apr 18 14:21 Dockerfile
-rw-r--r-- 1 sammy sammy    101 Jan  7  2020 README.md
drwxr-xr-x 6 sammy sammy   4096 Jan  7  2020 app
-rwxr-xr-x 1 sammy sammy   1686 Jan  7  2020 artisan
drwxr-xr-x 3 sammy sammy   4096 Jan  7  2020 bootstrap
-rw-r--r-- 1 sammy sammy   1501 Jan  7  2020 composer.json
-rw-r--r-- 1 sammy sammy 179071 Jan  7  2020 composer.lock
drwxr-xr-x 2 sammy sammy   4096 Jan  7  2020 config
drwxr-xr-x 5 sammy sammy   4096 Jan  7  2020 database
drwxr-xr-x 4 sammy sammy   4096 Apr 18 14:22 docker-compose
-rw-r--r-- 1 sammy sammy   1017 Apr 18 14:29 docker-compose.yml
-rw-r--r-- 1 sammy sammy   1013 Jan  7  2020 package.json
-rw-r--r-- 1 sammy sammy   1405 Jan  7  2020 phpunit.xml
drwxr-xr-x 2 sammy sammy   4096 Jan  7  2020 public
-rw-r--r-- 1 sammy sammy    273 Jan  7  2020 readme.md
drwxr-xr-x 6 sammy sammy   4096 Jan  7  2020 resources
drwxr-xr-x 2 sammy sammy   4096 Jan  7  2020 routes
-rw-r--r-- 1 sammy sammy    563 Jan  7  2020 server.php
drwxr-xr-x 5 sammy sammy   4096 Jan  7  2020 storage
drwxr-xr-x 4 sammy sammy   4096 Jan  7  2020 tests
-rw-r--r-- 1 sammy sammy    538 Jan  7  2020 webpack.mix.js

Bây giờ chúng ta sẽ chạy composer install để cài đặt các phụ thuộc của ứng dụng:

docker compose exec app rm -rf vendor composer.lock

docker compose exec app composer install

Bạn sẽ thấy kết quả như sau:

No composer.lock file present. Updating dependencies to latest instead of installing from lock file. See https://getcomposer.org/install for more information.
. . .
Lock file operations: 89 installs, 0 updates, 0 removals
  - Locking doctrine/inflector (2.0.4)
  - Locking doctrine/instantiator (1.4.1)
  - Locking doctrine/lexer (1.2.3)
  - Locking dragonmantank/cron-expression (v2.3.1)
  - Locking egulias/email-validator (2.1.25)
  - Locking facade/flare-client-php (1.9.1)
  - Locking facade/ignition (1.18.1)
  - Locking facade/ignition-contracts (1.0.2)
  - Locking fideloper/proxy (4.4.1)
  - Locking filp/whoops (2.14.5)
. . .
Writing lock file
Installing dependencies from lock file (including require-dev)
Package operations: 89 installs, 0 updates, 0 removals
  - Downloading doctrine/inflector (2.0.4)
  - Downloading doctrine/lexer (1.2.3)
  - Downloading dragonmantank/cron-expression (v2.3.1)
  - Downloading symfony/polyfill-php80 (v1.25.0)
  - Downloading symfony/polyfill-php72 (v1.25.0)
  - Downloading symfony/polyfill-mbstring (v1.25.0)
  - Downloading symfony/var-dumper (v4.4.39)
  - Downloading symfony/deprecation-contracts (v2.5.1)
. . .
Generating optimized autoload files
> Illuminate\Foundation\ComposerScripts::postAutoloadDump
> @php artisan package:discover --ansi
Discovered Package: facade/ignition
Discovered Package: fideloper/proxy
Discovered Package: laravel/tinker
Discovered Package: nesbot/carbon
Discovered Package: nunomaduro/collision
Package manifest generated successfully.

Điều cuối cùng chúng ta cần làm trước khi kiểm tra ứng dụng là tạo một khóa ứng dụng duy nhất bằng công cụ dòng lệnh artisan của Laravel. Khóa này được sử dụng để mã hóa các phiên người dùng và các dữ liệu nhạy cảm khác:

docker compose exec app php artisan key:generate
Application key set successfully.

Bây giờ hãy mở trình duyệt và truy cập tên miền hoặc địa chỉ IP của máy chủ của bạn trên cổng 8000:

http://server_domain_or_IP:8000

Lưu ý: Trong trường hợp bạn đang chạy bản demo này trên máy cục bộ của mình, hãy sử dụng http://localhost:8000 để truy cập ứng dụng từ trình duyệt của bạn.

Bạn sẽ thấy một trang như thế này:

Bạn cũng có thể kiểm thử ứng dụng Laravel từ bên trong container ứng dụng mà không cần sử dụng trình duyệt:

docker compose exec app curl http://nginx

Nếu mọi thứ được cấu hình đúng, bạn sẽ thấy đầu ra HTML của trang chào mừng mặc định của Laravel.

<html>
<head>
    <title>Travel List</title>
</head>

<body>
    <h1>My Travel Bucket List</h1>
    <h2>Places I'd Like to Visit</h2>
    <ul>
        <li>Berlin</li>
        <li>Budapest</li>
        <li>Denver</li>
        <li>Helsinki</li>
        <li>Lisbon</li>
        <li>Nairobi</li>
        <li>Rio</li>
        <li>Tokyo</li>
    </ul>

    <h2>Places I've Already Been To</h2>
    <ul>
        <li>Cincinnati</li>
        <li>Moscow</li>
        <li>Oslo</li>
    </ul>
</body>
</html>

Bạn có thể sử dụng lệnh logs để kiểm tra các bản ghi được tạo bởi dịch vụ của bạn:

docker compose logs nginx
Attaching to travellist-nginx
. . .
travellist-nginx | 172.24.9.1 - - [18/Apr/2022:14:49:16 +0000] "GET / HTTP/1.1" 200 627 "-" "curl/7.82.0"
travellist-nginx | 172.24.9.1 - - [18/Apr/2022:14:51:27 +0000] "GET / HTTP/1.1" 200 627 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:89.0) Gecko/20100101 Firefox/89.0"
travellist-nginx | 172.24.9.1 - - [18/Apr/2022:14:51:27 +0000] "GET /favicon.ico HTTP/1.1" 200 0 "http://localhost:8000/" "Mozilla/5.0 (X11; Linux x86_64; rv:89.0) Gecko/20100101 Firefox/89.0"

Bạn cũng có thể sử dụng lệnh curl sau để kích hoạt các mục nhật ký:

curl http://localhost:8000

Sau đó chạy lại:

docker compose logs nginx

Bây giờ bạn nên thấy một mục nhật ký truy cập tương ứng cho yêu cầu của mình.

Nếu bạn muốn tạm dừng môi trường Docker Compose của mình trong khi vẫn giữ trạng thái của tất cả các dịch vụ, hãy chạy:

docker compose pause
Pausing travellist-db    ... done
Pausing travellist-nginx ... done
Pausing travellist-app   ... done

Bạn có thể tiếp tục sử dụng dịch vụ của mình với:

docker compose unpause
Unpausing travellist-app   ... done
Unpausing travellist-nginx ... done
Unpausing travellist-db    ... done

Để tắt môi trường Docker Compose của bạn và xóa tất cả các container, mạng và volume của nó, hãy chạy:

docker compose down
Stopping travellist-nginx ... done
Stopping travellist-db    ... done
Stopping travellist-app   ... done
Removing travellist-nginx ... done
Removing travellist-db    ... done
Removing travellist-app   ... done
Removing network travellist-laravel-demo_travellist

Để có cái nhìn tổng quan về tất cả các lệnh Docker Compose, vui lòng xem tài liệu tham khảo lệnh của Docker Compose.

Sửa quyền tệp và thư mục trong Laravel

Khi chạy Laravel trong các container Docker, một trong những vấn đề phổ biến mà các lập trình viên gặp phải liên quan đến quyền truy cập tệp và thư mục. Laravel yêu cầu một số thư mục nhất định phải có quyền ghi bởi tiến trình PHP-FPM bên trong container app. Nếu các thư mục này không có quyền ghi, ứng dụng có thể không ghi nhật ký lỗi, lưu tệp cấu hình cache hoặc lưu dữ liệu phiên làm việc đúng cách.

Bạn có thể gặp các thông báo như:

The stream or file "/var/www/storage/logs/laravel.log" could not be opened in append mode: failed to open stream: Permission denied

hoặc

file_put_contents(/var/www/bootstrap/cache/config.php): Failed to open stream: Permission denied

Các lỗi này cho thấy người dùng đang chạy PHP trong container không có quyền ghi vào các thư mục cần thiết.

Các bước sau đây mô tả cách chẩn đoán và khắc phục các vấn đề này cả trong môi trường cục bộ và môi trường sản xuất.

Điều chỉnh quyền thư mục có thể ghi

Laravel yêu cầu hai thư mục có thể ghi được bởi người dùng máy chủ web:

  • /var/www/storage
  • /var/www/bootstrap/cache

Nếu bạn gặp lỗi quyền truy cập, bạn có thể khắc phục chúng bằng cách điều chỉnh quyền sở hữu và quyền truy cập từ bên trong container:

docker compose exec app chown -R sammy:sammy /var/www/storage /var/www/bootstrap/cache

docker compose exec app chmod -R 775 /var/www/storage /var/www/bootstrap/cache

Nếu Dockerfile của bạn sử dụng tên người dùng khác (được định nghĩa thông qua đối số build user), hãy thay sammy bằng tên người dùng đó.

Bạn có thể xác minh quyền sở hữu bằng lệnh sau:

docker compose exec app ls -ld /var/www/storage /var/www/bootstrap/cache

Đầu ra nên hiển thị rằng người dùng và nhóm khớp với tiến trình PHP-FPM bên trong container.

Nếu bạn muốn điều chỉnh quyền từ máy chủ, hãy chạy:

sudo chown -R $(id -u):$(id -g) storage bootstrap/cache

sudo chmod -R 775 storage bootstrap/cache

Điều này đảm bảo rằng cả hai thư mục Laravel đều có thể ghi được bởi container và người dùng trên máy của bạn.

Đặt quyền sở hữu thư mục trong quá trình xây dựng hình ảnh

Để ngăn việc gặp lại các vấn đề về quyền sau mỗi lần xây dựng lại, bạn có thể thiết lập quyền sở hữu thư mục trong quá trình xây dựng hình ảnh Docker. Mở Dockerfile của bạn và thêm dòng sau gần cuối, trước khi chuyển sang người dùng không phải root:

RUN chown -R $user:www-data /var/www

Lệnh này đảm bảo rằng tất cả các tệp và thư mục dưới /var/www đều thuộc quyền sở hữu của người dùng mà bạn đã định nghĩa trong Dockerfile của mình.

Để có cách tiếp cận cụ thể và an toàn hơn, bạn có thể chỉ điều chỉnh các thư mục mà Laravel cần ghi vào:

RUN chown -R $user:www-data /var/www/storage /var/www/bootstrap/cache

Sau khi thực hiện thay đổi này, hãy xây dựng lại hình ảnh và khởi động lại các container:

docker compose build app

docker compose up -d

Lần tiếp theo khi container khởi động, nó sẽ đã có các quyền đúng.

Xác minh tính nhất quán của ID người dùng giữa máy chủ và container

Khi sử dụng bind mounts, Docker chia sẻ tệp giữa máy chủ và container của bạn. Nếu UID số của người dùng trên máy chủ khác với UID được định nghĩa trong container, có thể xảy ra xung đột quyền sở hữu tệp. Điều này thường xảy ra trên các hệ thống mà ID người dùng khác nhau giữa các máy.

Để xác minh các giá trị UID, hãy chạy:

id -u $(whoami)

docker compose exec app id -u

Nếu hai số này không khớp, hãy cập nhật tệp docker-compose.yml của bạn để truyền UID thực tế của bạn vào quy trình build:

build:
  args:
    user: sammy
    uid: 1000

Sau đó xây dựng lại hình ảnh:

docker compose build app

docker compose up -d

Điều này đảm bảo rằng người dùng trong container trùng với người dùng trên máy chủ của bạn, ngăn chặn sự không khớp quyền trên các tập tin được gắn kết.

Sửa quyền thư mục bộ nhớ đệm của Composer

Composer lưu các phụ thuộc đã tải xuống và các tệp bộ nhớ đệm trong thư mục home của người dùng bên trong container (/home/sammy/.composer). Nếu thư mục này không thể ghi được, bạn có thể thấy các cảnh báo như:

Cannot create cache directory /home/sammy/.composer/cache

Bạn có thể khắc phục điều này bằng cách tạo thư mục cache và gán quyền sở hữu đúng:

docker compose exec app mkdir -p /home/sammy/.composer/cache

docker compose exec app chown -R sammy:sammy /home/sammy/.composer

Điều này đảm bảo rằng Composer có thể lưu trữ các phụ thuộc và tệp cache mà không gặp lỗi quyền, điều này cũng giúp tăng tốc các lần chạy composer install trong tương lai.

Các Thực Tiễn Khuyến Nghị cho Môi Trường Sản Xuất

Trong môi trường sản xuất, quản lý quyền nên được xử lý trong quá trình xây dựng hình ảnh thay vì lúc chạy. Điều này giúp cải thiện tính nhất quán và bảo mật.

Thực hiện các bước sau để có cấu hình sẵn sàng cho sản xuất:

  • Tránh sử dụng bind mounts cho mã ứng dụng: Thay vào đó, sao chép các tệp ứng dụng vào trong image khi xây dựng. Bind mounts nên chỉ dành cho phát triển.

  • Đặt quyền trong quá trình xây dựng: Thêm các lệnh RUN chown và chmod vào Dockerfile của bạn để đảm bảo quyền sở hữu và quyền truy cập chính xác trước khi triển khai.

  • Sử dụng người dùng có quyền hạn thấp nhất: Đảm bảo tiến trình PHP chạy dưới quyền người dùng không phải root (cài đặt hiện tại với $user đã thực hiện điều này).

  • Hạn chế quyền trên các thư mục không thể ghi: Hầu hết các tập tin ứng dụng nên chỉ được quyền đọc. Bạn có thể thực hiện điều này bằng:

    chmod -R 755 /var/www
    
    chmod -R 775 /var/www/storage /var/www/bootstrap/cache

    Cài đặt này cho phép Laravel chỉ ghi ở những nơi cần thiết trong khi bảo vệ các tệp ứng dụng khác khỏi việc chỉnh sửa.

Bằng cách làm theo các bước này, các container Laravel của bạn sẽ có quyền truy cập tệp ổn định và có thể dự đoán được cả trong môi trường cục bộ và sản xuất. Điều này giúp giảm các lỗi lặp lại và đảm bảo Laravel có thể ghi dữ liệu, lưu trữ tệp cache và ghi phiên một cách đáng tin cậy.

Hiểu về docker-compose.override.yml cho Môi Trường Cục Bộ và Sản Xuất

Docker Compose cho phép bạn định nghĩa các ứng dụng nhiều container bằng một tệp cấu hình duy nhất. Trong hầu hết các trường hợp, cấu hình này được lưu trong một tệp có tên docker-compose.yml. Tuy nhiên, việc quản lý nhiều môi trường như phát triển cục bộ và sản xuất thường yêu cầu các điều chỉnh cụ thể theo môi trường. Đây là lúc tệp docker-compose.override.yml trở nên hữu ích.

Theo mặc định, Docker Compose tự động tìm kiếm một tệp có tên docker-compose.override.yml trong cùng thư mục với tệp Compose cơ sở. Khi tìm thấy, nó sẽ hợp nhất các cấu hình từ cả hai tệp khi bạn chạy bất kỳ lệnh docker compose nào. Cách tiếp cận này cho phép các nhà phát triển duy trì một cấu hình cơ sở sạch và có thể tái sử dụng, đồng thời tùy chỉnh các khía cạnh cụ thể của thiết lập cho việc sử dụng cục bộ hoặc sản xuất.

Vai trò của docker-compose.override.yml

Tệp docker-compose.override.yml là một phần mở rộng của cấu hình Compose cơ bản. Nó sửa đổi hoặc thêm các thuộc tính chỉ áp dụng cho một môi trường cụ thể, mà không cần thay đổi tệp docker-compose.yml chính. Tệp override đặc biệt hữu ích trong quy trình phát triển cục bộ, nơi các nhà phát triển thường cần gắn mã nguồn, bật các công cụ gỡ lỗi hoặc sửa đổi các biến môi trường để thử nghiệm.

Khi bạn chạy lệnh docker compose up, Docker tự động gộp nội dung của docker-compose.yml và docker-compose.override.yml. Nếu một dịch vụ, volume hoặc cấu hình được định nghĩa trong cả hai tệp, các giá trị trong tệp override sẽ được ưu tiên.

Để sử dụng một tệp ghi đè khác, ví dụ như tệp dành cho môi trường sản xuất, bạn có thể chỉ định tệp đó thủ công bằng cách sử dụng cờ -f:

docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d

Cách tiếp cận này cho phép bạn duy trì các cấu hình riêng biệt cho các môi trường khác nhau trong khi vẫn sử dụng một cấu trúc lệnh nhất quán duy nhất.

Cấu trúc hóa cho phát triển địa phương

Các môi trường phát triển cục bộ thường yêu cầu thay đổi thường xuyên, tải lại mã nguồn trực tiếp và ghi nhật ký chi tiết. Tệp docker-compose.override.yml cho phép đưa những tính năng này vào mà không ảnh hưởng đến các môi trường khác.

Một tệp ghi đè địa phương điển hình có thể bao gồm:

  • Gắn kết thư mục để cập nhật mã trực tiếp
  • Chỉ dẫn xây dựng tại chỗ thay vì sử dụng các hình ảnh dựng sẵn
  • Biến môi trường đặc thù cho phát triển
  • Dịch vụ gỡ lỗi và kiểm thử

Ví dụ:

version: "3.9"

services:
  app:
    build:
      context: .
      dockerfile: Dockerfile.dev
    volumes:
      - ./src:/app
      - ./logs:/app/logs
    environment:
      - DATABASE_URL=postgres://postgres:postgres@db:5432/app_db
      - LOG_LEVEL=debug
    ports:
      - "8000:8000"
    depends_on:
      - db

  db:
    environment:
      - POSTGRES_PASSWORD=postgres

Trong ví dụ này:

  • Dịch vụ app được xây dựng trực tiếp từ Dockerfile cục bộ (Dockerfile.dev), giúp dễ dàng thử nghiệm các thay đổi mà không cần phải xây lại hình ảnh qua một registry.
  • Mã nguồn được gắn bằng cách sử dụng bind mounts, cho phép container phản ánh các thay đổi tệp cục bộ trong thời gian thực.
  • Các biến môi trường như LOG_LEVEL=debug được đặt để cung cấp thông tin chi tiết hơn trong quá trình phát triển.
  • Cơ sở dữ liệu sử dụng thông tin đăng nhập đơn giản phù hợp cho việc thử nghiệm cục bộ nhưng không nên sử dụng trong môi trường sản xuất.

Cấu hình này cung cấp một môi trường phát triển nhanh hơn và linh hoạt hơn trong khi vẫn giữ nguyên cấu hình cơ bản.

Chuẩn bị cho Sản xuất

Môi trường sản xuất yêu cầu sự ổn định, khả năng tái tạo và bảo mật. Các container nên chạy từ các hình ảnh đã được xây dựng sẵn thay vì xây dựng tại chỗ, và thông tin nhạy cảm cần được quản lý một cách an toàn. Khác với file ghi đè được áp dụng tự động cho phát triển cục bộ, các ghi đè trong môi trường sản xuất phải được chỉ định rõ ràng.

Một tệp ghi đè cho môi trường sản xuất (docker-compose.prod.yml) thường được thiết kế để thay thế các bước xây dựng cục bộ bằng các tham chiếu hình ảnh, sử dụng các volume đặt tên thay vì mount liên kết, và định nghĩa các thuộc tính liên quan đến triển khai như giới hạn tài nguyên và khả năng mở rộng.

Ví dụ:

version: "3.9"

services:
  app:
    image: registry.example.com/myapp:1.2.3
    environment:
      - DATABASE_URL=${DATABASE_URL}
      - LOG_LEVEL=info
    volumes:
      - app-data:/var/lib/app
    ports:
      - "8000:8000"
    restart: unless-stopped
    deploy:
      replicas: 3
      resources:
        limits:
          cpus: "0.5"
          memory: 512M

  db:
    image: postgres:15
    volumes:
      - db-data:/var/lib/postgresql/data
    environment:
      - POSTGRES_PASSWORD_FILE=/run/secrets/db_password

secrets:
  db_password:
    external: true

volumes:
  app-data:
  db-data:

Trong cấu hình này:

  • Dịch vụ app tham chiếu đến một hình ảnh đã được xây dựng sẵn (myapp:1.2.3) được lưu trữ trong registry thay vì xây dựng cục bộ.

  • Các bản gắn kết được thay thế bằng các volume có tên để đảm bảo lưu trữ dữ liệu nhất quán và tách biệt.

  • Thông tin đăng nhập nhạy cảm được lưu trữ an toàn bằng cách sử dụng Docker secrets.

  • Phần deploy xác định hành vi mở rộng và giới hạn tài nguyên để tăng độ ổn định khi chịu tải cao.

  • Cấu hình phải được áp dụng rõ ràng trong quá trình triển khai:

    docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d

Cấu trúc này đảm bảo rằng việc triển khai sản xuất được thực hiện một cách nhất quán, có kiểm soát và không chứa các yếu tố từ phát triển cục bộ.

So sánh các ghi đè cục bộ và sản xuất

Khía cạnh cấu hìnhCục bộ (docker-compose.override.yml)Sản xuất (docker-compose.prod.yml)
Áp dụng tự độngVângKhông (phải sử dụng -f)
Xử lý mã nguồnGắn mount để cập nhật trực tiếpHình ảnh có sẵn từ kho lưu trữ
Biến môi trườngCác giá trị hướng đến nhà phát triểnGiá trị an toàn cho sản xuất
Khai thác gỗChi tiết (debug)Ngắn gọn (info hoặc error)
Quản lý bí mậtGiá trị thuần hoặc .envBí mật Docker hoặc bí mật được quản lý
Cân chỉnhMột containerNhiều bản sao
Tối ưu hiệu suấtKhông bắt buộcGiới hạn tài nguyên đã được xác định
Công cụ gỡ lỗiĐã bậtVô hiệu hóa
Chính sách khởi động lạiThủ công hoặc không có
restart: unless-stopped

Sử dụng nhiều tệp Docker Compose cung cấp một cách rõ ràng để tách các cấu hình theo môi trường ra khỏi thiết lập cơ bản. Tệp cơ bản docker-compose.yml định nghĩa cấu trúc ứng dụng chung, trong khi docker-compose.override.yml điều chỉnh nó cho phát triển cục bộ. Khi triển khai lên môi trường sản xuất, một tệp override rõ ràng đảm bảo cấu hình có thể dự đoán và an toàn.

Mô hình này cho phép các nhóm duy trì một quy trình làm việc nhất quán. Các nhà phát triển có thể khởi chạy ứng dụng cục bộ bằng cách sử dụng:

docker compose up

và triển khai lên môi trường sản xuất bằng:

docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d

Cả hai môi trường đều chia sẻ cùng cấu hình cốt lõi trong khi vẫn duy trì các thiết lập riêng phù hợp với các yêu cầu cụ thể của chúng. Sự tách biệt này giúp cải thiện khả năng bảo trì, giảm lỗi cấu hình và giữ cho môi trường địa phương và sản xuất luôn đồng bộ.

Khắc phục sự cố môi trường của bạn

Phần này cung cấp các bước xử lý sự cố cho những vấn đề thường gặp mà bạn có thể gặp phải khi chạy ứng dụng của mình với Docker Compose.

502 Lỗi Gateway (Nginx hiển thị phản hồi 502)

Triệu chứng:

  • Trình duyệt hiển thị lỗi 502 Bad Gateway khi truy cập http://localhost:8000.
  • Các bản ghi Nginx chứa các dòng như recv() failed (111: Connection refused) hoặc connect() failed (111: Connection refused) while connecting to upstream.

Nguyên nhân có thể:

  • PHP-FPM (dịch vụ app) không chạy hoặc không lắng nghe trên socket/cổng như mong đợi.
  • Nginx fastcgi_pass trỏ đến host/port sai.
  • Tên container không khớp hoặc có sự cố mạng giữa nginx và app.
  • app bị sự cố khi khởi động (ví dụ: do thiếu phần mở rộng hoặc lỗi cấu hình).

Cách xác nhận:

  1. Kiểm tra nhật ký Nginx:

    docker compose logs nginx | tail -n 100

    Tìm các thông báo 502 hoặc lỗi kết nối đầu nguồn.

  2. Kiểm tra trạng thái và nhật ký của container app:

    docker compose ps app
    
    docker compose logs app --tail=200
  3. Xác nhận PHP-FPM đang lắng nghe bên trong container app:

    docker compose exec app ss -ltnp | grep :9000 || docker compose exec app ss -lxp | grep php-fpm

    Dự kiến: hoặc là một trình lắng nghe TCP trên cổng 9000 hoặc một socket Unix được sử dụng bởi nginx (ít phổ biến hơn với compose này).

  4. Xác nhận nginx có thể giải quyết và kết nối tới app trên mạng compose:

    docker compose exec nginx ping -c 2 app
    
    docker compose exec nginx wget -qO- --timeout=2 http://app:9000/ || true

    Nếu ping thất bại hoặc wget hết thời gian chờ, có thể có sự không khớp giữa mạng hoặc tên dịch vụ.

Sửa chữa phổ biến:

  • Nếu PHP-FPM không chạy, hãy kiểm tra nhật ký app để tìm các lỗi PHP nghiêm trọng hoặc thất bại khi khởi động. Khắc phục lỗi cơ bản (thường là thiếu tiện ích mở rộng hoặc vấn đề quyền) và khởi động lại:

    docker compose restart app
    
    docker compose logs app --tail=100
  • Nếu cấu hình Nginx sử dụng một upstream khác (ví dụ: fastcgi_pass 127.0.0.1:9000;), hãy cập nhật docker-compose/nginx/travellist.conf thành fastcgi_pass app:9000; (phù hợp với tên dịch vụ trong compose của bạn) và tải lại Nginx:

    # After editing config in host
    
    docker compose exec nginx nginx -s reload
    
    docker compose logs nginx --tail=50
  • Nếu PHP-FPM lắng nghe trên một socket Unix (ví dụ: /run/php/php7.4-fpm.sock) nhưng Nginx lại mong đợi TCP hoặc ngược lại, hãy chuẩn hóa về TCP app:9000 cho các thiết lập Compose hoặc gắn kết socket đúng cách giữa các container (khuyến nghị: sử dụng TCP trong Compose nhiều container).

Thiếu các phần mở rộng PHP (lỗi khi chạy Composer hoặc Artisan, hoặc lỗi khi thực thi)

Triệu chứng:

  • Các lệnh Composer hoặc Laravel thất bại với các thông báo như Class "PDO" not found, ext-pdo is missing, hoặc Call to undefined function json_encode.
  • Lỗi PHP trong các bản ghi app liên quan đến các lớp hoặc hàm không xác định.

Cách xác nhận:

  1. Kiểm tra danh sách mô-đun PHP của app container:

    docker compose exec app php -m | sort

    Tìm kiếm các tên phần mở rộng bạn cần (đối với các phần mở rộng thông thường của Laravel: pdo_mysql, mbstring, bcmath, openssl, tokenizer, xml, ctype, json, gd).

  2. Kiểm tra văn bản lỗi của composer, thường nêu tên các phần mở rộng cần thiết:

    docker compose exec app composer install

Sửa lỗi:

  • Thêm các tiện ích mở rộng còn thiếu vào Dockerfile của bạn và xây dựng lại hình ảnh. Ví dụ (cho pdo_mysql và gd):

    RUN apt-get update && apt-get install -y libpng-dev libxml2-dev \
        && docker-php-ext-install pdo_mysql mbstring xml gd

    Sau đó xây dựng lại và khởi động lại:

    docker compose build app
    
    docker compose up -d app
    
    docker compose logs app --tail=100
  • Nếu một tiện ích mở rộng yêu cầu các thư viện hệ thống (ví dụ gd cần libpng-dev), hãy thêm các dòng apt-get install tương ứng trước docker-php-ext-install.

  • Nếu bạn cần một tiện ích mở rộng PHP không có sẵn mặc định, hãy cân nhắc cài đặt qua PECL và bật nó trong php.ini.

Lỗi kết nối cơ sở dữ liệu (Laravel không thể kết nối với MySQL)

Triệu chứng:

  • Laravel hiển thị lỗi kết nối cơ sở dữ liệu (SQLSTATE[HY000] [2002] Kết nối bị từ chối hoặc Truy cập bị từ chối cho người dùng).
  • Việc di chuyển thất bại, hoặc ứng dụng hiển thị một trang trống đối với các trang dựa trên dữ liệu.

Cách xác nhận:

  1. Kiểm tra nhật ký container DB để tìm lỗi khởi tạo:

    docker compose logs db --tail=200
  2. Xác minh container DB đang chạy:

    docker compose ps db
  3. Xác nhận rằng thông tin đăng nhập và máy chủ nhất quán giữa .env và docker-compose.yml. Trong .env của Laravel, DB_HOST nên là tên dịch vụ trong compose (db), không phải 127.0.0.1 hoặc localhost:

    DB_HOST=db
    DB_PORT=3306
    DB_DATABASE=travellist
    DB_USERNAME=travellist_user
    DB_PASSWORD=password
  4. Kiểm tra từ bên trong container db:

    docker compose exec db mysql -u"${DB_USERNAME}" -p"${DB_PASSWORD}" -e "SHOW DATABASES;"

    Hoặc, kiểm tra từ container app để đảm bảo kết nối mạng:

    docker compose exec app sh -c 'which mysql >/dev/null 2>&1 || echo "no mysql client"; mysql -h db -u"${DB_USERNAME}" -p"${DB_PASSWORD}" -e "SELECT VERSION();"'

Nguyên nhân thường gặp và cách khắc phục:

  • Máy chủ sai trong .env: cập nhật DB_HOST=db, tạo lại các container hoặc tải lại các biến môi trường:

    docker compose down
    
    docker compose up -d
    
    docker compose exec app php artisan migrate
  • Cơ sở dữ liệu chưa sẵn sàng khi ứng dụng khởi động: sử dụng cơ chế thử lại trong ứng dụng của bạn hoặc thêm kiểm tra tình trạng và bắt đầu đặt hàng. Bạn cũng có thể chờ thủ công:

    docker compose exec db mysqladmin -u"${DB_USERNAME}" -p"${DB_PASSWORD}" ping
    
    # expected: mysqld is alive
  • Khởi tạo SQL thất bại: kiểm tra các tập tin trong ./docker-compose/mysql, lỗi cú pháp hoặc quyền có thể làm quá trình khởi tạo cơ sở dữ liệu bị gián đoạn. Kiểm tra nhật ký cơ sở dữ liệu để tìm lỗi nhập liệu.

  • Quyền hạn/thông tin đăng nhập sai: xác nhận cơ sở dữ liệu và người dùng tồn tại. Tạo lại cơ sở dữ liệu hoặc chạy SQL để cấp quyền.

  • Sử dụng cổng ở mức host: nếu bạn thử curl telnet://localhost:3306 và dịch vụ db không mở cổng host, kết nối đó sẽ bị từ chối theo thiết kế. Hãy kiểm tra từ bên trong các container, hoặc mở cổng trong docker-compose.yml (ports: - "3306:3306") để debug chỉ trên máy cục bộ.

Vấn đề về quyền truy cập và hệ thống tệp (lưu trữ, bộ nhớ đệm, tệp nhật ký)

Triệu chứng:

  • Laravel đưa ra các ngoại lệ liên quan đến việc ghi bộ nhớ đệm hoặc nhật ký: Không thể mở luồng hoặc tệp "/var/www/storage/logs/laravel.log".
  • Các tệp do tiến trình container tạo ra thuộc sở hữu của root và không thể ghi bởi người dùng trên máy chủ của bạn.

Cách xác nhận:

  1. Kiểm tra quyền truy cập tệp từ máy chủ:

    ls -l
    
    ls -l storage bootstrap/cache
    
    docker compose exec app id -u   # user id inside container
  2. Kiểm tra nhật ký của container để tìm lỗi quyền truy cập.

Sửa lỗi:

  • Đảm bảo Dockerfile thiết lập người dùng và UID phù hợp với người dùng trên máy chủ của bạn (Dockerfile của bạn đã tạo một người dùng với ARG uid). Xác nhận uid trong các đối số build của docker-compose.yml khớp với UID trên máy chủ của bạn (thường là 1000).

  • Sửa quyền sở hữu từ máy chủ hoặc bên trong container:

    docker compose exec app chown -R sammy:sammy /var/www/storage /var/www/bootstrap/cache

    hoặc từ máy chủ:

    sudo chown -R $(id -u):$(id -g) storage bootstrap/cache
  • Trong phát triển, việc gắn mount có thể gây ra sự không khớp về quyền; ưu tiên chạy các lệnh trong container để điều chỉnh quyền sở hữu sau khi khởi động container.

Lỗi liên quan đến trình biên soạn hoặc nhà cung cấp

Triệu chứng:

  • Lệnh composer install thất bại bên trong app hoặc thiếu thư mục vendor gây ra lỗi không tìm thấy lớp.
  • composer báo cáo hết bộ nhớ trong container.

Cách xác nhận:

  • Chạy composer bên trong container app và ghi lại kết quả:

    docker compose exec app composer install --no-interaction --prefer-dist

Sửa lỗi:

  • Nếu giới hạn bộ nhớ của composer bị vượt quá, hãy cấp thêm bộ nhớ hoặc thiết lập biến môi trường cho composer:

    docker compose exec app php -d memory_limit=-1 /usr/bin/composer install
  • Đảm bảo tệp thực thi Composer có sẵn (Dockerfile của bạn đã sao chép composer). Nếu không, hãy xây dựng lại image với lớp composer.

  • Nếu vendor bị thiếu do bind mount overlay, hãy đảm bảo bind mount của bạn không che khuất thư mục vendor được tạo trong image. Thực hành tốt nhất là chạy composer install sau khi các container đã khởi động để vendor được tạo trong thư mục đã mount, hoặc thêm vendor vào container thông qua việc build image cho môi trường production.

Ứng dụng hiển thị trang trắng hoặc lỗi 500

Triệu chứng:

  • Trình duyệt hiển thị phản hồi trống hoặc lỗi HTTP 500.
  • Nhật ký Nginx hiển thị 200 nhưng trang trống; nhật ký app hiển thị các ngoại lệ nghiêm trọng.

Cách xác nhận:

  • Kiểm tra nhật ký ứng dụng:

    docker compose exec app tail -n 200 storage/logs/laravel.log
    
    docker compose logs app --tail=200
  • Kiểm tra môi trường: APP_DEBUG=true trong .env sẽ khiến Laravel hiển thị lỗi chi tiết trong quá trình phát triển (không bật trong môi trường sản xuất).

Sửa lỗi:

  • Sửa lỗi cơ bản được hiển thị trong laravel.log.

  • Đảm bảo .env tồn tại và APP_KEY đã được thiết lập:

    docker compose exec app php artisan key:generate
  • Chạy migrations, xóa cache:

    docker compose exec app php artisan migrate --force
    
    docker compose exec app php artisan config:cache
    
    docker compose exec app php artisan route:cache
    
    docker compose exec app php artisan view:cache

Lỗi xây dựng container (lỗi trong quá trình docker compose build)

Triệu chứng:

  • docker compose build báo lỗi do sự cố apt hoặc lỗi trong bước build.

Cách xác nhận:

  • Chạy lại bản dựng với nhật ký:

    docker compose build app

Sửa lỗi:

  • Đọc kết quả của bước thất bại. Các vấn đề thường gặp:

    • Mạng/DNS trong môi trường build: thử lại hoặc đảm bảo Docker có thể truy cập internet.
    • Thiếu gói hoặc nguồn apt sai: thêm apt-get update trước khi cài đặt và dọn dẹp danh sách apt sau đó.
    • Quyền trên các tập tin được sao chép vào hình ảnh: đảm bảo các đường dẫn COPY chính xác.

Ví dụ đoạn Dockerfile cho cài đặt mạnh mẽ:

RUN apt-get update && apt-get install -y --no-install-recommends \
    libpng-dev libxml2-dev zip unzip \
  && docker-php-ext-install pdo_mysql mbstring xml gd \
  && apt-get clean && rm -rf /var/lib/apt/lists/*

Sự cố mạng hoặc DNS giữa các container

Triệu chứng:

  • Một container không thể kết nối với container khác bằng tên dịch vụ (ví dụ, app không thể kết nối với db qua db:3306).
  • ping theo tên dịch vụ không thành công.

Cách xác nhận:

docker compose exec app ping -c 3 db

docker compose exec app getent hosts db

docker network inspect travellist

Sửa lỗi:

  • Xác nhận rằng các dịch vụ chia sẻ cùng một mạng trong docker-compose.yml (cả hai nên tham chiếu đến travellist).

  • Nếu bạn đã thay đổi tên dịch vụ hoặc tên mạng, hãy cập nhật DB_HOST và các upstream của Nginx cho phù hợp.

  • Tái tạo mạng (việc này sẽ xóa các container tạm thời, vì vậy hãy cẩn thận):

    docker compose down
    
    docker network prune   # optional and destructive
    
    docker compose up -d

Các lệnh chẩn đoán hữu ích

Chạy những cái này để kiểm tra theo mục tiêu:

  • Hiển thị các container đang chạy và cổng:

    docker compose ps
  • Hiển thị nhật ký theo thời gian thực cho một dịch vụ:

    docker compose logs -f --tail=200 nginx
  • Thực thi một shell bên trong container:

    docker compose exec app sh
    
    docker compose exec db bash
  • Kiểm tra biến môi trường của container:

    docker compose exec app env | sort
    
    docker compose exec db env | grep MYSQL
  • Kiểm tra danh sách tiến trình của container:

    docker compose exec app ps aux
    
    docker compose exec app ss -ltnp
  • Kiểm tra tình trạng sức khỏe của MySQL:

    docker compose exec db mysqladmin -u"${DB_USERNAME}" -p"${DB_PASSWORD}" ping

Khi nào nên mở các cổng máy chủ để gỡ lỗi

Theo mặc định, dịch vụ db trong cấu hình này không được liên kết với các cổng trên máy chủ. Chỉ mở cổng để gỡ lỗi cục bộ. Để tạm thời mở MySQL trên máy chủ, thêm vào docker-compose.yml dưới db:

ports:
  - "3306:3306"

Sau đó khởi động lại:

docker compose up -d db

Sau khi gỡ lỗi, hãy loại bỏ ánh xạ ports để tránh việc mở cơ sở dữ liệu ra mạng.

Câu hỏi thường gặp

1. Tôi có cần cài đặt Composer trên máy chủ không?

Không. Bạn có thể chạy Composer hoàn toàn bên trong container ứng dụng, đây là cách được khuyến nghị cho quy trình làm việc với Compose này. Dockerfile trong hướng dẫn bao gồm Composer, vì vậy hãy chạy các lệnh như docker compose exec app composer install hoặc docker compose run --rm app composer require vendor/package. Chạy Composer trong container đảm bảo sử dụng đúng phiên bản PHP và các tiện ích mở rộng, đồng thời tránh tạo các tệp vendor với quyền sở hữu không phù hợp trên máy chủ.

2. Tôi có thể chạy các migration của Laravel từ bên trong container không?

Đúng. Chạy các migrations từ container ứng dụng để chúng sử dụng cùng runtime và mạng với ứng dụng của bạn: docker compose exec app php artisan migrate. Đối với các tác vụ một lần hoặc CI, bạn có thể sử dụng docker compose run --rm app php artisan migrate --force. Trước khi chạy migrations, hãy chắc chắn rằng container cơ sở dữ liệu đang hoạt động bình thường và chấp nhận kết nối, ví dụ với docker compose exec db mysqladmin -u$ {DB_USERNAME} -p$ {DB_PASSWORD} ping hoặc thử lại cho đến khi nó phản hồi.

3. Làm thế nào để tôi cài đặt thêm các tiện ích mở rộng PHP?

Thêm các gói hệ thống cần thiết và lệnh mở rộng vào Dockerfile, sau đó xây dựng lại hình ảnh. Ví dụ, cài đặt các phụ thuộc và xây dựng các tiện ích mở rộng phổ biến với một cái gì đó như:

RUN apt-get update && apt-get install -y libpng-dev libjpeg-dev \
  && docker-php-ext-configure gd --with-jpeg \
  && docker-php-ext-install pdo_mysql mbstring gd

Nếu bạn cần một tiện ích mở rộng PECL, hãy sử dụng pecl install và docker-php-ext-enable. Sau khi chỉnh sửa Dockerfile, chạy docker compose build app và sau đó docker compose up -d app để áp dụng các thay đổi.

4. Làm thế nào để tôi mở container Nginx cho HTTPS?

Thêm một khối máy chủ HTTPS vào cấu hình Nginx của bạn, gắn chứng chỉ và khóa của bạn vào trong container, và mở cổng 443 trong docker-compose. Đây là một ví dụ với các thay đổi tối thiểu trong docker-compose.yml:

services:
  nginx:
    ports:
      - "8000:80"
      - "443:443"
    volumes:
      - ./docker-compose/nginx:/etc/nginx/conf.d
      - ./certs:/etc/nginx/certs:ro

Sau đó thêm một khối máy chủ SSL tham chiếu đến /etc/nginx/certs/fullchain.pem và /etc/nginx/certs/privkey.pem. Trong môi trường sản xuất, hãy sử dụng một reverse proxy hoặc trình quản lý chứng chỉ như Traefik hoặc một bộ cân bằng tải ngoài để lấy và gia hạn chứng chỉ một cách an toàn thay vì lưu trữ khóa riêng trong stack ứng dụng.

5. Tôi làm thế nào để xây dựng lại Laravel sau khi thay đổi mã?

Điều đó phụ thuộc vào những gì đã thay đổi. Nếu bạn đang sử dụng bind mounts cho thư mục dự án, các thay đổi đối với PHP, template, route và controller sẽ được phản ánh ngay lập tức; chạy lệnh xóa cache của Laravel nếu cần, ví dụ:

docker compose exec app php artisan config:clear

docker compose exec app php artisan route:clear

docker compose exec app php artisan view:clear

Nếu bạn thay đổi phụ thuộc của composer hoặc Dockerfile (thêm extension mới hoặc gói hệ thống), hãy cài đặt lại hoặc xây dựng lại: docker compose exec app composer install cho các thay đổi liên quan đến phụ thuộc, và docker compose build app && docker compose up -d cho các thay đổi Dockerfile. Khởi động lại dịch vụ bị ảnh hưởng khi bạn thay đổi cấu hình PHP-FPM hoặc Nginx: docker compose restart app hoặc docker compose restart nginx.

Kết luận

Trong hướng dẫn này, chúng tôi đã thiết lập một môi trường Docker với ba container sử dụng Docker Compose để định nghĩa cơ sở hạ tầng của chúng tôi trong một tệp YAML.

Từ thời điểm này trở đi, bạn có thể làm việc trên ứng dụng Laravel của mình mà không cần phải cài đặt và thiết lập máy chủ web cục bộ để phát triển và thử nghiệm. Hơn nữa, bạn sẽ làm việc trong một môi trường dùng một lần có thể dễ dàng sao chép và phân phối, điều này sẽ hữu ích khi phát triển ứng dụng của bạn và cũng khi chuyển sang môi trường sản xuất.

Để có thêm các hướng dẫn về Laravel, hãy xem các bài viết sau:

  • Lưu trữ ứng dụng Laravel của bạn trên Linux-Console.net
  • Cách Cài Đặt và Cấu Hình Laravel với Nginx trên Ubuntu (LEMP Stack)
  • Cách Tạo Tài Nguyên API Eloquent Laravel để Chuyển Đổi Mô Hình thành JSON
  • Cách Khởi Tạo Ứng Dụng Laravel Mới với Docker Compose

Cảm ơn bạn đã học cùng cộng đồng Linux-Console.net.