如何在 Ubuntu 20.04 上使用 Docker Compose 设置 Laravel、Nginx 和 MySQL

作者选择了 The FreeBSD Foundation作为 Write for Donations计划的一部分接受捐款。

介绍

在过去的几年中,Docker(https://docs.docker.com/)已成为应用程序部署的常用解决方案,因为它简化了运行和部署以期限的应用程序(https://www.docker.com/resources/what-container)。当您使用LEMP应用程序堆栈时,例如,使用PHP(https://php.net/docs.php),Nginx(https://nginx.org/en/),MySQL(https://dev.mysql.com/doc/)和Laravel(https://laravel.com/docs/5.6)框架时,Docker可以显著简化设置过程。

Docker Compose进一步简化了开发过程,允许开发人员在一个文件中定义他们的基础设施,包括应用服务,网络和卷,Docker Compose提供了运行多个docker container createdocker container run命令的有效替代方案。

在本教程中,您将使用 Laravel 框架构建一个 Web 应用程序,使用 Nginx 作为 Web 服务器和 MySQL 作为数据库,所有这些都在 Docker 容器中。

前提条件

在你开始之前,你需要:

步骤 1 – 下载 Laravel 和安装依赖

作为第一步,您将获得最新的Laravel版本,并安装该项目的依赖,包括 Composer,PHP的应用级包管理器. 我们将与Docker一起安装这些依赖,以免需要在全球范围内安装Composer。

首先,检查您是否在您的主目录中,并将最新Laravel版本克隆到名为laravel-app的目录中:

1cd ~
2git clone https://github.com/laravel/laravel.git laravel-app

移动到laravel-app目录:

1cd ~/laravel-app

接下来,使用Docker的composer图像(LINK0)来配置您需要的Laravel项目的目录,并避免全球安装Composer:

1docker run --rm -v $(pwd):/app composer install

使用-v--rm的旗帜与docker run创建一个短期容器,将被绑定到您的当前目录之前被删除。

作为最后一步,在项目目录中设置权限,以使其属于您的非root用户:

1sudo chown -R sammy:sammy ~/laravel-app

在步骤 4 中为您的应用程序图像编写 Dockerfile 时,这将是很重要的,因为它将允许您使用应用程序代码工作,并在容器中运行过程作为非root用户。

有了应用程序代码,您可以继续使用 Docker Compose 来定义您的服务。

步骤 2 – 创建 Docker 组件文件

使用 Docker Compose 构建应用程序简化了基础设施的设置和版本化过程. 为了设置您的 Laravel 应用程序,您将编写一个定义您的 Web 服务器、数据库和应用程序服务的docker-compose文件。

打开文件:

1nano ~/laravel-app/docker-compose.yml

docker-compose文件中,您将定义三个服务:app,webserverdb。 将以下代码添加到文件中,确保在db服务下将MYSQL_ROOT_PASSWORD代替为环境变量(https://docs.docker.com/compose/compose-file/#environment),并使用您所选择的强有力的密码:

 1[label ~/laravel-app/docker-compose.yml]
 2version: '3'
 3services:
 4
 5  #PHP Service
 6  app:
 7    build:
 8      context: .
 9      dockerfile: Dockerfile
10    image: digitalocean.com/php
11    container_name: app
12    restart: unless-stopped
13    tty: true
14    environment:
15      SERVICE_NAME: app
16      SERVICE_TAGS: dev
17    working_dir: /var/www
18    networks:
19      - app-network
20
21  #Nginx Service
22  webserver:
23    image: nginx:alpine
24    container_name: webserver
25    restart: unless-stopped
26    tty: true
27    ports:
28      - "80:80"
29      - "443:443"
30    networks:
31      - app-network
32
33  #MySQL Service
34  db:
35    image: mysql:5.7.22
36    container_name: db
37    restart: unless-stopped
38    tty: true
39    ports:
40      - "3306:3306"
41    environment:
42      MYSQL_DATABASE: laravel
43      MYSQL_ROOT_PASSWORD: MYSQL_ROOT_PASSWORD
44      SERVICE_TAGS: dev
45      SERVICE_NAME: mysql
46    networks:
47      - app-network
48
49#Docker Networks
50networks:
51  app-network:
52    driver: bridge

这里定义的服务包括:

  • app:本服务定义包含 Laravel 应用程序,并运行自定义 Docker 图像, digitalocean.com/php,您将在步骤 4 中定义。它还将容器中的 working_dir 设置为 /var/www
  • webserver:本服务定义从 Docker 提取了 nginx:alpine 图像并暴露了端口 80443
  • db:本服务定义从 Docker 提取了 mysql:5.7.22 图像并定义了一些环境变量,包括一个名为 laravel 的数据库,为您的应用程序

每个container_name属性都定义了容器的名称,该名称与服务的名称相匹配. 如果您不定义此属性,Docker 会将一个名称分配给每个容器,通过将历史上著名的名人名和一个分开的随机单词相结合。

为了方便集装箱之间的通信,服务连接到称为应用网络的桥梁网络。桥梁网络使用软件桥梁,允许连接到同一个桥梁网络的集装箱相互通信。桥梁驱动程序在主机中自动安装规则,以便不同桥梁网络上的集装箱无法直接相互通信。这为应用创造了更高的安全级别,确保只有相关服务才能相互通信。这也意味着您可以定义多个网络和服务连接到相关功能:前端应用服务可以使用前端网络,后端服务可以使用后端网络

接下来,您将看看如何将卷添加到您的服务定义中,并将插件绑定到服务定义中,以保持应用数据。

步骤3 - 持久数据

Docker 具有强大而方便的数据持久功能. 在您的应用程序中,您将使用 volumesbind mounts 来持久数据库和应用程序和配置文件. Volumes 为备份和持久性提供了灵活性,超出容器的生命周期,而 bind mounts 可在开发过程中促进代码的更改,使您的主机文件或目录的更改立即在您的容器中可用。

<$>[警告] 警告: 使用 bind mount,您可以通过在容器中运行的流程来更改主机文件系统,包括创建、修改或删除重要的系统文件或目录。

docker-compose文件中,在db服务定义下定义一个名为dbdata的卷,以保持MySQL数据库:

 1[label ~/laravel-app/docker-compose.yml]
 2...
 3#MySQL Service
 4db:
 5  ...
 6    volumes:
 7      - dbdata: /var/lib/mysql
 8    networks:
 9      - app-network
10  ...

被命名为dbdata的卷仍然包含容器内存在的/var/lib/mysql文件夹的内容,这允许您停止并重新启动db服务而不会丢失数据。

在文件的底部,添加dbdata卷的定义:

1[label ~/laravel-app/docker-compose.yml]
2...
3#Volumes
4volumes:
5  dbdata:
6    driver: local

有了这个定义,您将能够在服务中使用此卷。

接下来,为您在步骤 7 中创建的 MySQL 配置文件添加一个 bind mount 到 db 服务:

1[label ~/laravel-app/docker-compose.yml]
2...
3#MySQL Service
4db:
5  ...
6    volumes:
7      - dbdata:/var/lib/mysql
8      - ./mysql/my.cnf:/etc/mysql/my.cnf
9  ...

这种结合装置将 ~/laravel-app/mysql/my.cnf 绑定到 /etc/mysql/my.cnf 在容器中。

接下来,向webserver服务添加绑定组件,将有两种:一个用于您的应用程序代码,另一个用于您在步骤 6 中创建的 Nginx 配置定义。

1[label ~/laravel-app/docker-compose.yml]
2#Nginx Service
3webserver:
4  ...
5  volumes:
6     - ./:/var/www
7     - ./nginx/conf.d/:/etc/nginx/conf.d/
8  networks:
9      - app-network

第一个 bind mount 将 ~/laravel-app 目录中的应用程序代码绑定到容器内部的 /var/www 目录。 您将添加到 ~/laravel-app/nginx/conf.d/ 的配置文件也将安装在容器中的 /etc/nginx/conf.d/ 目录中,允许您根据需要添加或修改配置目录的内容。

最后,为应用程序代码和配置文件添加到应用程序服务的下列绑定插件:

1[label ~/laravel-app/docker-compose.yml]
2#PHP Service
3app:
4  ...
5  volumes:
6      - ./:/var/www
7      - ./php/local.ini:/usr/local/etc/php/conf.d/local.ini
8  networks:
9      - app-network

应用程序服务将包含应用程序代码的~/laravel-app文件夹绑定到容器中的/var/www文件夹中,这将加速开发过程,因为对本地应用程序目录的任何更改将立即反映在容器内部。您还将您的PHP配置文件~/laravel-app/php/local.ini绑定到容器内部的/usr/local/etc/php/conf.d/local.ini

您的docker-compose文件现在将看起来像这样:

 1[label ~/laravel-app/docker-compose.yml]
 2version: '3'
 3services:
 4
 5  #PHP Service
 6  app:
 7    build:
 8      context: .
 9      dockerfile: Dockerfile
10    image: digitalocean.com/php
11    container_name: app
12    restart: unless-stopped
13    tty: true
14    environment:
15      SERVICE_NAME: app
16      SERVICE_TAGS: dev
17    working_dir: /var/www
18    volumes:
19      - ./:/var/www
20      - ./php/local.ini:/usr/local/etc/php/conf.d/local.ini
21    networks:
22      - app-network
23
24  #Nginx Service
25  webserver:
26    image: nginx:alpine
27    container_name: webserver
28    restart: unless-stopped
29    tty: true
30    ports:
31      - "80:80"
32      - "443:443"
33    volumes:
34      - ./:/var/www
35      - ./nginx/conf.d/:/etc/nginx/conf.d/
36    networks:
37      - app-network
38
39  #MySQL Service
40  db:
41    image: mysql:5.7.22
42    container_name: db
43    restart: unless-stopped
44    tty: true
45    ports:
46      - "3306:3306"
47    environment:
48      MYSQL_DATABASE: laravel
49      MYSQL_ROOT_PASSWORD: your_mysql_root_password
50      SERVICE_TAGS: dev
51      SERVICE_NAME: mysql
52    volumes:
53      - dbdata:/var/lib/mysql/
54      - ./mysql/my.cnf:/etc/mysql/my.cnf
55    networks:
56      - app-network
57
58#Docker Networks
59networks:
60  app-network:
61    driver: bridge
62#Volumes
63volumes:
64  dbdata:
65    driver: local

保存文件,并在完成更改后离开编辑器。

有了您的docker-compose文件,您现在可以为您的应用程序构建自定义图像。

步骤 4 – 创建 Dockerfile

Docker 允许您使用 Dockerfile 指定单个容器内部的环境. Dockerfile 允许您创建自定义图像,您可以使用它们来安装应用程序所需的软件,并根据您的要求配置设置。

您的Dockerfile将位于您的~/laravel-app目录中。

1nano ~/laravel-app/Dockerfile

Dockerfile将设置基础图像并指定构建 Laravel 应用程序图像所需的命令和指示。

 1[label ~/laravel-app/php/Dockerfile]
 2FROM php:7.4-fpm
 3
 4# Copy composer.lock and composer.json
 5COPY composer.lock composer.json /var/www/
 6
 7# Set working directory
 8WORKDIR /var/www
 9
10# Install dependencies
11RUN apt-get update && apt-get install -y \
12    build-essential \
13    libpng-dev \
14    libjpeg62-turbo-dev \
15    libfreetype6-dev \
16    locales \
17    zip \
18    jpegoptim optipng pngquant gifsicle \
19    vim \
20    unzip \
21    git \
22    curl \
23    libzip-dev
24
25# Clear cache
26RUN apt-get clean && rm -rf /var/lib/apt/lists/*
27
28# Install extensions
29RUN docker-php-ext-install pdo_mysql mbstring zip exif pcntl
30RUN docker-php-ext-configure gd --with-gd --with-freetype-dir=/usr/include/ --with-jpeg-dir=/usr/include/ --with-png-dir=/usr/include/
31RUN docker-php-ext-install gd
32
33# Install composer
34RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
35
36# Add user for laravel application
37RUN groupadd -g 1000 www
38RUN useradd -u 1000 -ms /bin/bash -g www www
39
40# Copy existing application directory contents
41COPY . /var/www
42
43# Copy existing application directory permissions
44COPY --chown=www:www . /var/www
45
46# Change current user to www
47USER www
48
49# Expose port 9000 and start php-fpm server
50EXPOSE 9000
51CMD ["php-fpm"]

首先,Dockerfile 会在 php:7.4-fpm Docker 图像上方创建一个图像,这是一个基于 Debian 的图像,安装了 PHP FastCGI 实现程序 PHP-FPM

RUN指令规定了更新、安装和配置容器内部设置的命令,包括创建一个名为 www的专用用户和组。

创建具有限制权限的专用用户和组缓解了运行 Docker 容器时固有的漏洞,这些容器默认运行为 root. 而不是运行这个容器作为 root,您创建了 www 用户,该用户通过使用COPY命令访问了/var/www文件夹,并使用了--chown旗帜来复制应用程序文件夹的权限。

最后,EXPOSE命令暴露了容器中的一个端口,即9000,用于php-fpm服务器。

保存文件,并在完成更改后离开编辑器。

您现在可以继续定义您的PHP配置。

步骤 5 - 配置 PHP

现在你已经在docker-compose文件中定义了基础设施,你可以配置PHP服务作为PHP处理器来处理来自Nginx的请求。

要配置PHP,您将创建在php文件夹内的local.ini文件,这是您在步骤 2中在容器内绑定到/usr/local/etc/php/conf.d/local.ini的文件,创建此文件将允许您在启动时读取PHP的默认php.ini文件。

创建php目录:

1mkdir ~/laravel-app/php

然后打开local.ini文件:

1nano ~/laravel-app/php/local.ini

要展示如何配置 PHP,您将添加以下代码来设置上传文件的大小限制:

1[label ~/laravel-app/php/local.ini]
2upload_max_filesize=40M
3post_max_size=40M

upload_max_filesizepost_max_size指令设置了上传文件的最大允许大小,并展示了您如何从您的local.ini文件中设置php.ini配置。

保存文件并离开编辑器。

有了你的PHP「local.ini」文件,你可以继续配置 Nginx。

步骤 6 – 配置 Nginx

随着 PHP 服务的配置,您可以修改 Nginx 服务以使用 PHP-FPM 作为 FastCGI 服务器来服务动态内容. FastCGI 服务器基于交互式程序与 Web 服务器的二进制协议。

要配置 Nginx,您将创建一个 app.conf 文件,在 ~/laravel-app/nginx/conf.d/ 文件夹中使用服务配置。

首先,创建nginx/conf.d/目录:

1mkdir -p ~/laravel-app/nginx/conf.d

接下来,创建app.conf配置文件:

1nano ~/laravel-app/nginx/conf.d/app.conf

将以下代码添加到文件中,以指定您的 Nginx 配置:

 1[label ~/laravel-app/nginx/conf.d/app.conf]
 2server {
 3    listen 80;
 4    index index.php index.html;
 5    error_log  /var/log/nginx/error.log;
 6    access_log /var/log/nginx/access.log;
 7    root /var/www/public;
 8    location ~ \.php$ {
 9        try_files $uri =404;
10        fastcgi_split_path_info ^(.+\.php)(/.+)$;
11        fastcgi_pass app:9000;
12        fastcgi_index index.php;
13        include fastcgi_params;
14        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
15        fastcgi_param PATH_INFO $fastcgi_path_info;
16    }
17    location / {
18        try_files $uri $uri/ /index.php?$query_string;
19        gzip_static on;
20    }
21}

服务器块用以下指令定义了 Nginx Web 服务器的配置:

  • Listen:本指令定义了服务器会听到接收请求的端口。
  • error_logaccess_log:这些指令定义了写日志的文件。

php位置块中,fastcgi_pass指令规定app服务是在端口9000上听 TCP 接口。这使得 PHP-FPM 服务器在网络上听取,而不是在 Unix 接口上听取。尽管 Unix 接口在速度上比 TCP 接口略有优势,但它没有网络协议,因此跳过了网络堆栈。对于在一个机器上设有主机的情况下,Unix 接口可能有意义,但在你在不同的主机上运行服务的情况下,TCP 接口提供了允许你连接到分布式服务的优势。因为你的app容器在与你的Web 服务器容器不同的主机上运行,TCP 接口对你的配置最有意义。

保存文件,并在完成更改后离开编辑器。

由于您在步骤 2 中创建的 bind mount,您在nginx/conf.d/文件夹中所做的任何更改都会直接反映在webserver容器中。

接下来,您将查看并配置您的MySQL设置。

第7步:配置MySQL

有了 PHP 和 Nginx 配置,您可以允许 MySQL 作为您的应用程序的数据库。

要配置MySQL,您将在mysql文件夹中创建my.cnf文件,这是您在步骤 2中将集装箱中的/etc/mysql/my.cnf连接到的文件。

要展示此功能是如何工作的,您将添加设置到「my.cnf」文件中,允许通用查询日志并指定日志文件。

首先,创建mysql目录:

1mkdir ~/laravel-app/mysql

接下来,创建my.cnf文件:

1nano ~/laravel-app/mysql/my.cnf

在文件中,添加以下代码以启用查询日志并设置日志文件位置:

1[label ~/laravel-app/mysql/my.cnf]
2[mysqld]
3general_log = 1
4general_log_file = /var/lib/mysql/general.log

此「my.cnf」文件允许日志,将「general_log」设置定义为「1」以允许通用日志。

保存文件并离开编辑器。

你的下一步将是开始容器。

步骤 8 – 更改环境设置并运行容器

现在,您已经在您的docker-compose文件中定义了所有服务,并为这些服务创建了配置文件,您可以启动容器,但作为最后一步,您将创建Laravel默认包含的.env.example文件的副本,并命名副本.env,这是Laravel希望定义其环境的文件:

1cp .env.example .env

您现在可以修改app容器上的.env文件,以包含有关您的设置的具体细节。

使用nano或您所选择的文本编辑器打开文件:

1nano .env

尋找指定「DB_CONNECTION」的區塊,並更新它以反映您的安裝的特點。

  • DB_HOST 将是您的 db 数据库容器.
  • DB_DATABASE 将是 laravel 数据库.
  • DB_USERNAME 将是您为您的数据库使用的用户名。 在这种情况下,您将使用 laraveluser.
  • DB_PASSWORD 将是您希望为此用户帐户使用的安全密码。
1[label /var/www/.env]
2DB_CONNECTION=mysql
3DB_HOST=db
4DB_PORT=3306
5DB_DATABASE=laravel
6DB_USERNAME=laraveluser
7DB_PASSWORD=your_laravel_db_password

保存您的更改并退出您的编辑器。

对于在docker-compose文件中定义的所有服务,请使用以下单一命令启动所有容器,创建卷,并设置和连接网络:

1docker-compose up -d

当你第一次运行docker-compose up,它将下载所有必要的Docker图像,这可能需要一段时间。一旦这些图像下载并存储在你的本地机器上,Compose将创建你的容器。

完成此过程后,使用以下命令列出所有正在运行的容器:

1docker ps

您将看到以下输出,其中包含有关您的应用程序,Web服务器db容器的详细信息:

1[secondary_label Output]
2CONTAINER ID NAMES IMAGE STATUS PORTS
3c31b7b3251e0 db mysql:5.7.22 Up 2 seconds 0.0.0.0:3306->3306/tcp
4ed5a69704580 app digitalocean.com/php Up 2 seconds 9000/tcp
55ce4ee31d7c0 webserver nginx:alpine Up 2 seconds 0.0.0.0:80->80/tcp, 0.0.0.0:443->443/tcp

此输出中的CONTAINER ID是每个容器的唯一标识符,而NAMES列出了与每个容器相关的服务名称. 您可以使用这两个标识符访问容器。

现在,您将使用docker-compose exec为 Laravel 应用程序设置应用程序密钥. docker-compose exec命令允许您在容器中运行特定命令。

以下命令将生成一个密钥,并将其复制到您的.env 文件中,以确保您的用户会话和加密数据保持安全:

1docker-compose exec app php artisan key:generate

现在你有运行应用程序所需的环境设置. 要将这些设置缓存成一个文件,这将提高应用程序的加载速度,运行:

1docker-compose exec app php artisan config:cache

您的配置设置将被加载到容器上的 /var/www/bootstrap/cache/config.php

作为最后一步,请访问浏览器中的http://your_server_ip。您将看到您的Laravel应用程序的以下首页:

Laravel Home Page

随着您的容器运行和配置信息的位置,您可以继续对db容器上的laravel数据库配置您的用户信息。

第9步:为MySQL创建用户

默认 MySQL 安装只会创建 root 管理帐户,该帐户在数据库服务器上具有无限的权限。一般来说,在与数据库交互时最好避免使用 root 管理帐户。

要创建一个新用户,请在db容器上运行一个交互式 bash shell,使用docker-compose exec:

1docker-compose exec db bash

在容器内部,登录MySQL root管理帐户:

1[environment second]
2mysql -u root -p

您将被要求在安装过程中在您的docker-compose文件中为MySQL root帐户设置的密码。

首先,請檢查您在「docker-compose」檔案中定義的名為「laravel」的資料庫,執行「顯示資料庫」命令以檢查現有資料庫:

1[environment second]
2show databases;

您将看到输出中列出的laravel数据库:

 1[secondary_label Output]
 2[environment second]
 3+--------------------+
 4| Database           |
 5+--------------------+
 6| information_schema |
 7| laravel      |
 8| mysql              |
 9| performance_schema |
10| sys                |
11+--------------------+
125 rows in set (0.00 sec)

接下来,创建将被允许访问该数据库的用户帐户。您的用户名将是Laraveluser,但如果您喜欢的话,您可以将其替换为另一个名称。

1[environment second]
2GRANT ALL ON laravel.* TO 'laraveluser'@'%' IDENTIFIED BY 'your_laravel_db_password';

扫描权限以通知MySQL服务器的更改:

1[environment second]
2FLUSH PRIVILEGES;

离开思维:

1[environment second]
2EXIT;

最后,离开容器:

1[environment second]
2exit

您已配置了 Laravel 应用程序数据库的用户帐户,并已准备好迁移数据并使用 Tinker 控制台工作。

步骤 10 – 迁移数据并使用 Tinker 控制台工作

当您的应用程序运行时,您可以迁移数据并使用tinker命令进行实验,该命令将启动一个 PsySH主机,Laravel已预加载。PsySH是PHP的运行时间开发主机和交互式调试器,而Tinker是Laravel专用的REPL(https://andsky.com/tech/tutorials/what-is-repl)。

首先,通过运行Laravel手工迁移命令来测试与MySQL的连接,该命令将从容器内部创建数据库中的迁移表:

1docker-compose exec app php artisan migrate

此命令将迁移默认的 Laravel 表格. 确认迁移的输出将如下:

1[secondary_label Output]
2
3Migration table created successfully.
4Migrating: 2014_10_12_000000_create_users_table
5Migrated:  2014_10_12_000000_create_users_table
6Migrating: 2014_10_12_100000_create_password_resets_table
7Migrated:  2014_10_12_100000_create_password_resets_table

一旦迁移完成,您可以运行查询以检查使用tinker命令是否正确连接到数据库:

1docker-compose exec app php artisan tinker

通过获取您刚刚迁移的数据来测试MySQL连接:

1\DB::table('migrations')->get();

你会看到这样的输出:

 1[secondary_label Output]
 2=> Illuminate\Support\Collection {#2856
 3     all: [
 4       {#2862
 5         +"id": 1,
 6         +"migration": "2014_10_12_000000_create_users_table",
 7         +"batch": 1,
 8       },
 9       {#2865
10         +"id": 2,
11         +"migration": "2014_10_12_100000_create_password_resets_table",
12         +"batch": 1,
13       },
14     ],
15   }

您可以使用tinker与数据库互动,并使用服务和模型进行实验。

有了 Laravel 应用程序,您已经做好了进一步的开发和实验的准备。

结论

你现在有一个LEMP堆栈应用程序在你的服务器上运行,你已经通过访问Laravel欢迎页面和创建MySQL数据库迁移来测试了它。

Published At
Categories with 技术
comments powered by Disqus