作者选择了 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 create
和docker container run
命令的有效替代方案。
在本教程中,您将使用 Laravel 框架构建一个 Web 应用程序,使用 Nginx 作为 Web 服务器和 MySQL 作为数据库,所有这些都在 Docker 容器中。
前提条件
在你开始之前,你需要:
- 一个 Ubuntu 20.04 服务器,以及具有
sudo
特权的非根
用户。 遵循 Ubuntu 20.04 初始服务器设置 教程来设置此设置。 - Docker 安装,遵循 Ubuntu 20.04 上的如何安装和使用 Docker 的步骤 1 和 2。
- Docker Compose 安装,遵循 Ubuntu 20.04 上的如何安装 Docker Compose 的步骤 1)。
步骤 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
,webserver
和db
。 将以下代码添加到文件中,确保在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
图像并暴露了端口80
和443
。db
:本服务定义从 Docker 提取了mysql:5.7.22
图像并定义了一些环境变量,包括一个名为laravel
的数据库,为您的应用程序
每个container_name
属性都定义了容器的名称,该名称与服务的名称相匹配. 如果您不定义此属性,Docker 会将一个名称分配给每个容器,通过将历史上著名的名人名和一个分开的随机单词相结合。
为了方便集装箱之间的通信,服务连接到称为应用网络
的桥梁网络。桥梁网络使用软件桥梁,允许连接到同一个桥梁网络的集装箱相互通信。桥梁驱动程序在主机中自动安装规则,以便不同桥梁网络上的集装箱无法直接相互通信。这为应用创造了更高的安全级别,确保只有相关服务才能相互通信。这也意味着您可以定义多个网络和服务连接到相关功能:前端应用服务可以使用前端网络
,后端服务可以使用后端网络
。
接下来,您将看看如何将卷添加到您的服务定义中,并将插件绑定到服务定义中,以保持应用数据。
步骤3 - 持久数据
Docker 具有强大而方便的数据持久功能. 在您的应用程序中,您将使用 volumes 和 bind 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_filesize
和post_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_log
和access_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应用程序的以下首页:
随着您的容器运行和配置信息的位置,您可以继续对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数据库迁移来测试了它。