在Nginx容器wsl2上运行Laravel时,Docker非常慢

时间:2020-07-22 14:22:23

标签: docker docker-compose windows-subsystem-for-linux docker-for-windows

我已将Windows 10更新到2004最新版本,安装了wsl2并进行了更新,安装了docker和ubuntu。

当我使用“ Hello World”创建一个简单的 index.php 文件时,它运行良好(响应:100-400ms),但是当我添加我的 Laravel 项目时,它将变为即使在 PHPMyAdmin 运行非常平稳的情况下,它在执行请求之前加载 7sec 的时间很惨,并且响应为 4-7秒? :1-2秒)。

我的 docker-compose.yml 文件:

version: '3.8'
networks:
  laravel:

services:
  nginx:
    image: nginx:stable-alpine
    container_name: nginx
    ports:
      - "8080:80"
    volumes:
      - ./src:/var/www/html
      - ./nginx/default.conf:/etc/nginx/conf.d/default.conf
    depends_on:
      - php
      - mysql
      - phpmyadmin
    networks:
      - laravel

  mysql:
    image: mysql:latest
    container_name: mysql
    restart: unless-stopped
    tty: true
    ports:
      - "3306:3306"
    environment:
      MYSQL_ROOT_PASSWORD: secret
      SERVICE_TAGS: dev
      SERVICE_NAME: mysql
    networks:
      - laravel

  phpmyadmin:
    image: phpmyadmin/phpmyadmin
    restart: always
    depends_on:
      - mysql
    ports:
      - 8081:80
    environment:
      PMA_HOST: mysql
      PMA_ARBITRARY: 1

  php:
    build:
      context: .
      dockerfile: Dockerfile
    container_name: php
    volumes:
      - ./src:/var/www/html
    ports:
      - "9000:9000"
    networks:
      - laravel

  composer:
    image: composer:latest
    container_name: composer
    volumes:
      - ./src:/var/www/html
    working_dir: /var/www/html
    depends_on:
      - php
    networks:
      - laravel

  npm:
    image: node:latest
    container_name: npm
    volumes:
      - ./src:/var/www/html
    working_dir: /var/www/html
    entrypoint: ['npm']

  artisan:
    build:
      context: .
      dockerfile: Dockerfile
    container_name: artisan
    volumes:
      - ./src:/var/www/html
    depends_on:
      - mysql
    working_dir: /var/www/html
    entrypoint: ['php', '/var/www/html/artisan']
    networks:
      - laravel

我已经尝试修复此问题2天了,但是找不到答案。

谢谢

6 个答案:

答案 0 :(得分:6)

看起来您正在将Laravel项目安装在容器中。如果将这些文件从Windows环境挂载到WSL 2,则可能导致很差的文件I / O,因为WSL 2当前在访问Windows环境中的文件时遇到很多问题。该I / O问题截至2020年7月存在,您可以在Github here上找到该问题的持续状态。

我可以想到三种可能的解决方案,它们现在可以解决此问题。


为Docker禁用基于WSL 2的引擎,直到问题解决

由于仅当WSL 2尝试访问Windows文件系统时才会出现此问题,因此您可以选择禁用WSL 2泊坞窗集成,而是在Windows环境中运行容器。您可以在Docker桌面的用户界面中找到禁用它的选项: enter image description here


将项目存储在WSL 2的Linux文件系统中

同样,由于当WSL 2尝试访问/mnt下的Windows文件系统的安装点时会发生此问题,因此您可以选择将项目存储到WSL 2的Linux文件系统上。


构建自己的Dockerfile

您可以选择创建自己的Dockerfile,而不是装载项目,而可以COPY将所需目录插入Docker映像。由于WSL 2仍必须访问Windows文件系统以构建这些docker映像,因此这将导致构建性能不佳,但是运行时性能会好得多,因为它不必从Windows环境中检索这些文件。每次。

答案 1 :(得分:2)

好的,所以我得到了一个有趣的事实:))

在没有WSL2的Windows上运行docker。

一个请求具有TTFB 5.41s。这是index.php文件。我使用die()检查时间在哪里更大,我发现如果终止后使用die(),则TTFB变为〜2.5s。

<?php
/**
 * Laravel - A PHP Framework For Web Artisans
 *
 * @package  Laravel
 * @author   Taylor Otwell <taylor@laravel.com>
 */

define('LARAVEL_START', microtime(true));

require __DIR__.'/../../application/vendor/autoload.php';

$app = require_once __DIR__.'/../../application/bootstrap/app.php';

#die(); <-- TTFB 1.72s
$kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);

$response = $kernel->handle(
    $request = Illuminate\Http\Request::capture()
);

$response->send();

#die(); <-- TTFB 2.67s

$kernel->terminate($request, $response);

#die(); <-- TTFB 2.74s

#if there is no die in the file then TTFB is ~6s

答案 2 :(得分:1)

您正在/mnt/xxx文件夹中运行项目,不是吗?

这是因为wsl2 filesystem performance is much slower than wsl1 in /mnt.

如果您想要一个非常短的解决方案,请在这里。可从Windows应用商店在Ubuntu 18.04和Debian上运行:

  1. 转到docker设置并打开Expose daemon on tcp://localhost:2375 without TLS,然后关闭Use the WSL 2 based engine
  2. 运行此命令:
clear && sudo apt-get update && \
sudo curl -fsSL https://get.docker.com -o get-docker.sh && sudo sh get-docker.sh && sudo usermod -aG docker $USER && \
sudo curl -L "https://github.com/docker/compose/releases/download/1.26.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose && sudo chmod +x /usr/local/bin/docker-compose && \
echo "export PATH=\"$PATH:$HOME/.local/bin\"" >> ~/.profile && source ~/.profile && \
echo "export DOCKER_HOST=tcp://localhost:2375" >> ~/.bashrc && source ~/.bashrc && \
printf '[automount]\nroot = /\noptions = metadata' | sudo tee -a /etc/wsl.conf

我编写了有关如何将Docker桌面与WSL集成的说明: https://github.com/CaliforniaMountainSnake/wsl-1-docker-integration

答案 3 :(得分:0)

这确实是提高速度的粗略方法,但实际上是这样:

问题

vendor目录中加载作曲家依赖项的速度目前确实很慢,该目录从Windows上的项目根目录通过WSL 2映射到Docker容器。

解决方案

将供应商目录复制到docker映像,并使用它,而不是在项目根目录中映射的目录。

使用MySQL数据库和Apache PHP 7.4以及composer自动加载功能的项目结构如下:

db
    - init.sql
dev
    - db
        - Dockerfile
        - data.sql
    - www
        - Dockerfile
        - vendor-override.php
    - docker-compose.yaml
src
    - ...
vendor
    - ...
composer.json
index.php
...

这里的想法是使开发人员资料与主根目录分开。

dev / docker-compose.yaml

version: '3.8'
services:
  test-db:
    build:
      context: ../
      dockerfile: dev/db/Dockerfile

  test-www:
    build:
      context: ../
      dockerfile: dev/www/Dockerfile
    ports:
      - {insert_random_port_here}:80
    volumes:
      - ../:/var/www/html

这里,我们有两项服务,一项用于MySQL数据库,另一项用于带有PHP的Apache,它们将Web根/var/www/html映射到我们的项目根。这使Apache可以查看项目源文件(srcindex.php)。

dev / db / Dockerfile

FROM mysql:5.7.24

# Add initialize script (adding 0 in front of it, makes sure it is executed first as the scripts are loaded alphabetically)
ADD db/init.sql /docker-entrypoint-initdb.d/0init.sql

# Add test data (adding 99 infront of it, makes sure it is executed last)
ADD dev/db/data.sql /docker-entrypoint-initdb.d/99data.sql

dev / www / Dockerfile

FROM php:7.4.0-apache-buster

# Install PHP extensions and dependencies required by them
RUN apt-get update -y & \
    apt-get install -y libzip-dev libpng-dev libssl-dev libxml2-dev libcurl4-openssl-dev & \
    docker-php-ext-install gd json pdo pdo_mysql mysqli ftp simplexml curl

# Enable apache mods and .htaccess files
RUN a2enmod rewrite & \
    sed -e '/<Directory \/var\/www\/>/,/<\/Directory>/s/AllowOverride None/AllowOverride All/' -i /etc/apache2/apache2.conf

# Add composer to improve loading speed since its located inside linux
ADD vendor /var/www/vendor
ADD dev/www/vendor-override.php /var/www/
RUN chmod -R 777 /var/www & \
    mkdir /var/www/html/src & \
    ln -s /var/www/html/src /var/www/src

# Expose html dir for easier deployments
VOLUME /var/www/html

我在PHP 7.4上使用了官方的Apache Buster映像。

  1. 在这里,我们将vendor目录和vendor-override.php复制到Webroot(/var/www)上方的目录,以免干扰项目根目录。
ADD vendor /var/www/vendor
ADD dev/www/vendor-override.php /var/www/
  1. 接下来,我们为所有人设置读写执行权限,以便Apache可以读取它。这是必需的,因为它在webroot之外。
chmod -R 777 /var/www
  1. 现在,这里的技巧是确保作曲家可以从src目录中自动加载类。这可以通过在项目根目录中创建从/var/www/src//var/www/html/src的链接来解决。
mkdir /var/www/html/src
ln -s /var/www/html/src /var/www/src

dev / www / vendor-override.php

# Override default composer dependencies to be loaded from inside docker. This is used because
# loading files over mapped volumes is really slow on WSL 2.
require_once "/var/www/vendor/autoload.php";

只需在docker映像中使用vendor目录。

index.php

$fixFile = "../vendor-override.php";

if (file_exists($fixFile))
    require_once $fixFile;
else
    require_once "vendor/autoload.php";
    
...

如果检测到vendor-override.php文件,则使用该文件,而不使用项目根目录下的文件。这样可以确保index.php将dir加载到docker映像内部,速度更快。

composer.json

{
  "autoload": {
    "psr-4": {
      "Namespace\\": ["src"]
    }
  },
  ...
}

简单的自动加载设置将“名称空间”映射到项目根目录中的src目录。

注意事项

  • index.php从proect根加载vendor-override.php而不是供应商<​​/ li>
  • 通过链接解决PSR-4自动加载问题

缺点

缺点是每次更新依赖项时都必须构建docker映像。

答案 4 :(得分:0)

您可以从撰写文件中排除 vendor 文件夹 喜欢

    volumes:
  - ./www:/var/www
  - vendor:/var/www/vendor

答案 5 :(得分:0)

您只需将所有源项目移动到文件夹

\\wsl$\Ubuntu-20.04\home\<User Name>\<Project Name>

在 Linux Native 上运行速度会非常快

之前

Before

之后

After