Cách thiết lập một project symfony hỗ trợ làm việc với các subdomain

Tác giả:  Artem Henvald

Có những tình huống khi Symfony cần định tuyến dựa trên subdomain. Ví dụ, khi chúng ta phân phối các cụm chức năng khác nhau trong một dự án hoặc một kho lưu trữ. Giả sử, ngoài API, có một phần quản trị được viết trong các gói Symfony hoặc một số chức năng cho các url truy cập công khai từ phía máy khách.

Dưới đây là một số tùy chọn:

  • api.project-name.work: một máy chủ API cho các ứng dụng khách (web và các máy khách di động).
  • hub.project-name.work: một máy chủ được sử dụng để kết nối ngược với một máy khách di động khi một số tính năng cần có sẵn URL từ trình duyệt. Ví dụ: trong trường hợp tính năng đặt lại mật khẩu, tính năng xác nhận email, tính năng hủy đăng ký email, xử lý móc nối web từ hệ thống thanh toán, xử lý chuyển hướng hệ thống thanh toán và các trang như điều khoản thỏa thuận người dùng sẽ mở từ trình duyệt hoặc từ chế độ xem web trong ứng dụng dành cho thiết bị di động. Bạn có thể chia tất cả chức năng này thành các subdomain nếu bạn cần. Nhưng nó là không hợp lý để lưu trữ tất cả các công cụ này trên subdomain API vì nó không liên quan đến các truy vấn của khách hàng và trong hầu hết các trường hợp có một phương thức xác thực khác với API. Các tên miền con với tên ‘hub’ rất phổ biến trên các dịch vụ khác nhau cho các tính năng cùng loại.
  • admin.project-name.work: một máy chủ nơi phần quản trị được lưu trữ. Thật thuận tiện khi bảng quản trị viên được thực hiện bằng văn bản trong Symfony trong cùng một kho lưu trữ với mã API. Các thực thể phổ biến được sử dụng, và cả Symfony thuần hoặc một số gói quản trị có sẵn (như SonataAdminBundle, EasyAdminBundle, vv) đều có thể thực hiện được. Nó rất tiện lợi khi bạn phân phối với các công ty khởi nghiệp nhỏ hoặc trung bình với ngân sách hạn chế.
  • Tùy thuộc vào lĩnh vực của dự án, có thể sử dụng các subdomain khác, như ‘my’, ‘stats’, ‘legacy’,…

Khi bạn cần các subdomain riêng biệt:

Có subdomain riêng biệt cho các chức năng khác nhau, việc định tuyến và cân bằng lưu lượng truy cập dễ dàng hơn khi bạn cần tăng băng thông của dịch vụ. Nó cũng cho phép bạn triển khai thực hiện front-end với sự trợ giúp của bất kỳ công nghệ hoặc framework nào khác và đặt nó trên miền gốc project-name.work. Ví dụ: khi khách hàng muốn đặt đích cho ứng dụng dành cho thiết bị di động trên miền gốc. Việc kết thúc có thể được phát triển bởi một nhóm khác mà không có sự tham gia của bạn. Và sau đó dự án Symfony của bạn chỉ sẽ được tách ra từ việc định tuyến tên miền gốc và sẽ chỉ chia sẻ với các subdomain có liên quan.

Cách tùy chỉnh Project bằng các Subdomain cho Docker:

Để tùy chỉnh dự án trong Symfony cho Docker, bạn cần một số cấu hình bổ sung. Trong Docker, bạn có thể tạo định tuyến dựa trên các cổng khác nhau, nhưng bạn không thể áp dụng định tuyến từ gói cho các subdomain vì theo mặc định, Docker hoạt động trong phạm vi của một máy chủ.

Với Symfony, có một vấn đề khác: nó không hỗ trợ định tuyến bằng port, nhưng nó hỗ trợ định tuyến phụ. Đó là, cho đến nay, bạn không thể thiết lập Symfony để nó gọi điều này hoặc bộ điều khiển phụ thuộc vào cổng. Và triển khai tùy chọn hỗ trợ này không được mong đợi trong tương lai gần (bạn có thể đăng ký về vấn đề này để theo dõi tất cả các sửa đổi: có thể, họ sẽ quay lại một số điểm của tính năng này).

Từ hai tùy chọn: tùy chỉnh Symfony Router component hoặc thêm hỗ trợ cho các subdomain trong Docker, sau này dễ dàng hơn và nó đã có sẵn một cài đặt sẵn sàng.

Lưu ý: Nó có thể được áp dụng chỉ trong môi trường phát triển cục bộ cho Docker và nó không phải là một giải pháp phổ quát có thể được sử dụng trong thực tế.

Để thêm sự hỗ trợ subdomain trong Docker, bạn cần phải thêm một container dựa trên image jwilder/nginx-proxy và cấu hình cho nó trong file docker-compose.yml. Hãy xem ví dụ về một phần của cấu hình docker-compose.yml cho hai container: nginxnginx-proxy. Rõ ràng, sẽ có các container khác ngoài những thứ này trong dự án của bạn. Trong ví dụ này, jekakm/nginx-core:201802261 là image của chúng tôi mà chúng tôi sử dụng để phát triển.

docker-compose.yml

version: '2'
services:
    nginx_proxy:
        image: jwilder/nginx-proxy
        ports:
            - "80:80"
        volumes:
            - /var/run/docker.sock:/tmp/docker.sock:ro
 
    nginx:
        image: jekakm/nginx-core:201802261
         environment:
            - "VIRTUAL_HOST=hub.project-name.work,admin.project-name.work,api.project-name.work"
        volumes:
            - "./docker-configs/nginx.conf:/etc/nginx/sites-enabled/default"
            - ".:/app:cached"
        expose:
            - 80
        depends_on:
            - "php"

nginx_proxy sẽ nghe port 80 và đi qua nó vào Docker. Ngoài ra, biến môi trường VIRTUAL_HOST được thêm vào nginx container. Trong đó, phân cách bằng dấu phẩy, bạn cần phải đề cập đến tất cả các máy chủ (có tên miền phụ và không có) nên được proxy vào nginx container. Thông qua proxy này, Symfony sẽ nhận được các yêu cầu từ các host đã được đề cập mà không sửa đổi, và nó sẽ có thể, theo các quy tắc định tuyến, để xác định bộ điều khiển nào sẽ xử lý yêu cầu dựa trên các tên miền phụ.

Ví dụ nhanh về định tuyến Symfony dựa trên miền phụ:

easy_admin_bundle:
    resource: "@EasyAdminBundle/Controller/AdminController.php"
    type: annotation
    host: "%admin_host%" # <-- admin panel host
 
api_fos_oauth_server_token:
    resource: "@FOSOAuthServerBundle/Resources/config/routing/token.xml"
    host: "%api_host%" <--  API host
    methods: POST #
 
api:
    resource: ../src/Controller/API
    type: annotation
    host: "%api_host%" <-- API host
    prefix: /v1.0
 
hub:
    resource: ../src/Controller/Hub
    type: annotation
    host: "%hub_host%" <-- host for all "frontend features"
 
admin:
    resource: ../src/Controller/Admin
    type: annotation
    host: "%admin_host%" <-- host for admin panel, the processing of all custom actions which were implemented from the admin panel  
admin_logout:
    path: /logout
    host: "%admin_host%"

Trong cấu hình cho nginx container, có cấu hình máy chủ ./docker-configs/nginx.conf được thay thế vào container. Trong cấu hình này, tham số SERVER_NAME có giá trị project-name-docker. Nó quan trọng là vì giá trị này sau này sẽ được sử dụng để thiết lập XDebug trong PhpStorm (chúng tôi sẽ trình bày vấn đề này sau). Tôi cũng đưa ra ví dụ về một cấu hình đầy đủ cho máy chủ nginx để có thể kiểm tra sự khác biệt nếu cần thiết.

Chú ý! Cấu hình được trình bày dành cho Symfony 4 và các phiên bản mới hơn vì nó sử dụng đường dẫn public/index.php đến frontend controller:

server {
        gzip            	on;
        gzip_types      	text/plain text/css application/x-javascript text/xml application/xml application/rss+xml text/javascript image/x-icon application/json;
        gzip_min_length     1000;
        gzip_comp_level     6;
        gzip_http_version   1.0;
        gzip_vary       	on;
        gzip_proxied    	expired no-cache no-store private auth;
        gzip_disable    	msie6;
 
        listen 80;
 
        client_max_body_size 50M;
 
        root /app/public;
 
        rewrite ^/index\.php/?(.*)$ /$1 permanent;
 
        location / {
                index index.php;
                try_files $uri @rewriteapp;
        }
 
        location @rewriteapp {
                rewrite ^(.*)$ /index.php/$1 last;
        }
 
        location ~ ^/(index|config)\.php(/|$) {
                fastcgi_pass   php:9001;
                fastcgi_split_path_info ^(.+\.php)(/.*)$;
                include fastcgi_params;
                fastcgi_param  SERVER_NAME    	project-name-docker;
                fastcgi_param  SCRIPT_FILENAME	$document_root$fastcgi_script_name;
                fastcgi_param  HTTPS          	off;
        }
 
        location ~* ^.+\.(jpg|jpeg|gif|png|ico|zip|tgz|gz|rar|bz2|doc|xls|exe|pdf|ppt|txt|tar|wav|bmp|rtf|htc)$ {
                expires 	31d;
                add_header  Cache-Control private;
 
                error_page 404 = @rewriteapp;
        }
 
        location ~* \.(css|js)$ {
                expires 	7d;
                add_header  Cache-Control private;
        }
}

Sau khi bạn đã thêm một container nginx-proxy vào tệp docker-compose.yml, đừng quên build các container mới và khởi động lại các container đang chạy.

$ docker-compose build
$ docker-compose down && docker-compose up -d

Thêm điều hướng:

Điều cuối cùng bạn cần làm là thêm điều hướng cho máy chủ cục bộ của mình để các máy chủ bạn sử dụng (hoặc máy chủ có tên miền phụ) chuyển hướng đến máy chủ cục bộ. Bạn có thể làm điều đó bằng cách chỉnh sửa tệp /etc/hosts và thêm các dòng sau vào nó:

127.0.0.1   api.project-name.work
127.0.0.1   hub.project-name.work
127.0.0.1   admin.project-name.work

Hoặc bạn có thể sử dụng tiện ích dnsmasq và cấu hình một quy tắc chung trong nó. Ví dụ: nếu bạn sử dụng tên miền .work cho máy chủ cục bộ, nó được định cấu hình theo quy tắc sau:

address=/.work/127.0.0.1

Việc thêm các điều hướng cho các máy chủ ảo cục bộ là xa từ những gì được coi là thực hành tốt nhất Docker (cụ thể là khi bạn triển khai môi trường làm việc mà không có bất kỳ lệnh bổ sung nào cho máy cục bộ). Nhưng thật không may, không có giải pháp nào khác cho vấn đề này đối với các miền phụ Symfony trong Docker. Và đây là một giải pháp thỏa hiệp. Nó vẫn còn tốt hơn để sử dụng tiện ích dnsmasq để thiết lập một quy tắc chung cho máy cục bộ.

Để PhpStorm thiết lập kết nối XDebug, bạn cần thêm máy chủ cục bộ vào PHP -> Servers configuration:

Tên của máy chủ phải giống với giá trị của tùy chọn SERVER_NAME từ nginx. Trong trường hợp của chúng ta, nó là project-name-docker.

Vậy thì,

Chúng tôi đã thực hành cách thiết lập dự án trong Symfony để làm việc với các tên miền phụ Docker. Hy vọng bài viết sẽ hữu ích cho bạn.

Bài viết gốc được đăng tải tại codeburst.io