作者选择FreeBSD基金会作为Write for DOnations计划的一部分接受捐赠。
介绍
在过去几年中, Docker已成为部署应用程序的常用解决方案,这要归功于它如何简化在临时容器中运行和部署应用程序的过程。 当使用LEMP应用程序时,例如,使用PHP , Nginx , MySQL和Laravel框架,Docker可以显着简化设置过程。
Docker Compose允许开发人员在单个文件中定义其基础架构(包括应用程序服务,网络和卷),从而进一步简化了开发过程。 Docker Compose提供了运行多个docker container create
和docker container run
命令的有效替代方法。
在本教程中,您将使用Laravel框架构建Web应用程序,其中Nginx作为Web服务器,MySQL作为数据库,所有这些都在Docker容器中。 您将在docker-compose
文件中定义整个配置,以及PHP,MySQL和Nginx的配置文件。
先决条件
在开始之前,您将需要:
- 一个Ubuntu 18.04服务器和一个具有
sudo
权限的非root用户。 按照初始服务器设置与Ubuntu 18.04教程进行设置。 - 按照如何在Ubuntu 18.04上安装和使用Docker的第1步和2 安装Docker 。
- 按照如何在Ubuntu 18.04上安装Docker Compose的第1步安装Docker Compose 。
第1步 - 下载Laravel并安装依赖项
作为第一步,我们将获得最新版本的Laravel并安装项目的依赖项,包括Composer ,PHP的应用程序级包管理器。 我们将使用Docker安装这些依赖项,以避免必须全局安装Composer。
首先,检查您是否在主目录中并将最新的Laravel版本克隆到名为laravel-app
的目录:
cd ~
git clone https://github.com/laravel/laravel.git laravel-app
进入laravel-app
目录:
cd ~/laravel-app
接下来,使用Docker的composer
镜像来安装Laravel项目所需的目录,并避免全局安装Composer的开销:
docker run --rm -v $(pwd):/app composer install
将-v
和--rm
标志与--rm
docker run
会创建一个临时容器,该容器将在被删除之前绑定到当前目录。 这会将~/ laravel-app
目录的内容复制到容器中,并确保将Composer创建的vendor
文件夹复制到当前目录中。
最后一步,在项目目录上设置权限,使其由非root用户拥有:
sudo chown -R $USER:$USER ~/laravel-app
在第4步中为应用程序映像编写Dockerfile时,这将非常重要,因为它将允许您使用应用程序代码并以非root用户身份在容器中运行进程。
有了应用程序代码,您可以继续使用Docker Compose定义服务。
第2步 - 创建Docker Compose文件
使用Docker Compose构建应用程序简化了基础架构的设置和版本控制过程。 要设置我们的Laravel应用程序,我们将编写一个docker docker-compose
文件,用于定义我们的Web服务器,数据库和应用程序服务。
打开文件:
nano ~/laravel-app/docker-compose.yml
在docker-compose
文件中,您将定义三个服务: app
, webserver
和db
。 将以下代码添加到文件中,确保使用您选择的强密码替换MYSQL_ROOT_PASSWORD
的root密码,该密码在db
服务下定义为环境变量 :
version: '3'
services:
#PHP Service
app:
build:
context: .
dockerfile: Dockerfile
image: digitalocean.com/php
container_name: app
restart: unless-stopped
tty: true
environment:
SERVICE_NAME: app
SERVICE_TAGS: dev
working_dir: /var/www
networks:
- app-network
#Nginx Service
webserver:
image: nginx:alpine
container_name: webserver
restart: unless-stopped
tty: true
ports:
- "80:80"
- "443:443"
networks:
- app-network
#MySQL Service
db:
image: mysql:5.7.22
container_name: db
restart: unless-stopped
tty: true
ports:
- "3306:3306"
environment:
MYSQL_DATABASE: laravel
MYSQL_ROOT_PASSWORD: your_mysql_root_password
SERVICE_TAGS: dev
SERVICE_NAME: mysql
networks:
- app-network
#Docker Networks
networks:
app-network:
driver: bridge
这里定义的服务包括:
-
app
:此服务定义包含Laravel应用程序并运行您将在第4步中定义的自定义Docker映像digitalocean.com/php
。它还将容器中的working_dir
设置为/var/www
。 -
webserver
:此服务定义从Docker中提取nginx:alpine
图像并公开端口80
和443
。 -
db
:此服务定义从Docker中提取mysql:5.7.22
映像并定义一些环境变量,包括应用程序的laravel
数据库和数据库的root密码。 您可以根据需要随意命名数据库,并且应该使用自己的强密码替换your_mysql_root_password
。 此服务定义还将主机上的端口3306
映射到容器上的端口3306
。
每个container_name
属性都定义容器的名称,该名称对应于服务的名称。 如果您没有定义此属性,Docker将通过组合历史名人的姓名和由下划线分隔的随机单词为每个容器指定一个名称。
为了促进容器之间的通信,服务连接到称为app-network
的桥接app-network
。 桥接网络使用软件桥,允许连接到同一桥接网络的容器相互通信。 网桥驱动程序会自动在主机中安装规则,以便不同网桥上的容器无法直接相互通信。 这为应用程序创建了更高级别的安全性,确保只有相关服务才能相互通信。 它还意味着您可以定义连接到相关功能的多个网络和服务:例如, frontend
应用程序服务可以使用frontend
网络,后端服务可以使用backend
网络。
让我们看看如何添加卷并将挂载绑定到服务定义以保留应用程序数据。
第3步 - 保留数据
Docker具有强大而方便的功能来保存数据。 在我们的应用程序中,我们将使用卷和绑定挂载来持久化数据库,应用程序和配置文件。 卷为容器的生命周期之外的备份和持久性提供了灵活性,而绑定挂载有助于在开发期间更改代码,从而可以在容器中立即更改主机文件或目录。 我们的设置将使用两者。
警告:通过使用绑定装入,您可以通过容器中运行的进程更改主机文件系统,包括创建,修改或删除重要的系统文件或目录。 这是一种具有安全隐患的强大功能,可能会影响主机系统上的非Docker进程。 小心使用绑定挂载。
在dbdata
docker-compose
文件中,在db
服务定义下定义一个名为dbdata
的卷以保留MySQL数据库:
...
#MySQL Service
db:
...
volumes:
- dbdata:/var/lib/mysql
networks:
- app-network
...
命名卷dbdata
持久保存容器内存在的/var/lib/mysql
文件夹的内容。 这允许您在不丢失数据的情况下停止并重新启动db
服务。
在文件的底部,添加dbdata
卷的定义:
...
#Volumes
volumes:
dbdata:
driver: local
有了这个定义,您就可以跨服务使用此卷。
接下来,为您将在第7步中创建的MySQL配置文件的db
服务添加绑定装载:
...
#MySQL Service
db:
...
volumes:
- dbdata:/var/lib/mysql
- ./mysql/my.cnf:/etc/mysql/my.cnf
...
这个绑定挂载将~/laravel-app/mysql/my.cnf
绑定到容器中的~/laravel-app/mysql/my.cnf
。
接下来,将绑定挂载添加到webserver
服务。 将有两个:一个用于您的应用程序代码,另一个用于您将在第6步中创建的Nginx配置定义:
#Nginx Service
webserver:
...
volumes:
- ./:/var/www
- ./nginx/conf.d/:/etc/nginx/conf.d/
networks:
- app-network
第一个绑定装置将~/laravel-app
目录中的应用程序代码绑定到容器内的/var/www
目录。 您将添加到~/laravel-app/nginx/conf.d/
的配置文件也将挂载到/etc/nginx/conf.d/
中的/etc/nginx/conf.d/
,允许您根据需要添加或修改配置目录的内容。
最后,将以下绑定装入添加到app
服务中以获取应用程序代码和配置文件:
#PHP Service
app:
...
volumes:
- ./:/var/www
- ./php/local.ini:/usr/local/etc/php/conf.d/local.ini
networks:
- app-network
app
服务将~/laravel-app
文件夹(包含应用程序代码)绑定到容器中的/var/www
文件夹。 这将加快开发过程,因为对本地应用程序目录所做的任何更改都将立即反映在容器中。 您还将PHP配置文件~/laravel-app/php/local.ini
绑定到容器内的/usr/local/etc/php/conf.d/local.ini
。 您将在第5步中创建本地PHP配置文件。
你的docker-compose
文件现在看起来像这样:
version: '3'
services:
#PHP Service
app:
build:
context: .
dockerfile: Dockerfile
image: digitalocean.com/php
container_name: app
restart: unless-stopped
tty: true
environment:
SERVICE_NAME: app
SERVICE_TAGS: dev
working_dir: /var/www
volumes:
- ./:/var/www
- ./php/local.ini:/usr/local/etc/php/conf.d/local.ini
networks:
- app-network
#Nginx Service
webserver:
image: nginx:alpine
container_name: webserver
restart: unless-stopped
tty: true
ports:
- "80:80"
- "443:443"
volumes:
- ./:/var/www
- ./nginx/conf.d/:/etc/nginx/conf.d/
networks:
- app-network
#MySQL Service
db:
image: mysql:5.7.22
container_name: db
restart: unless-stopped
tty: true
ports:
- "3306:3306"
environment:
MYSQL_DATABASE: laravel
MYSQL_ROOT_PASSWORD: your_mysql_root_password
SERVICE_TAGS: dev
SERVICE_NAME: mysql
volumes:
- dbdata:/var/lib/mysql/
- ./mysql/my.cnf:/etc/mysql/my.cnf
networks:
- app-network
#Docker Networks
networks:
app-network:
driver: bridge
#Volumes
volumes:
dbdata:
driver: local
完成更改后,保存文件并退出编辑器。
编写docker-compose
文件后,您现在可以为应用程序构建自定义映像。
第4步 - 创建Dockerfile
Docker允许您使用Dockerfile指定单个容器内的环境。 Dockerfile使您可以创建自定义映像,可用于安装应用程序所需的软件并根据您的要求配置设置。 您可以将创建的自定义映像推送到Docker Hub或任何私有注册表。
我们的Dockerfile
将位于~/laravel-app
目录中。 创建文件:
nano ~/laravel-app/Dockerfile
此Dockerfile
将设置基本映像并指定构建Laravel应用程序映像所需的命令和说明。 将以下代码添加到文件中:
FROM php:7.2-fpm
# Copy composer.lock and composer.json
COPY composer.lock composer.json /var/www/
# Set working directory
WORKDIR /var/www
# Install dependencies
RUN apt-get update && apt-get install -y \
build-essential \
mysql-client \
libpng-dev \
libjpeg62-turbo-dev \
libfreetype6-dev \
locales \
zip \
jpegoptim optipng pngquant gifsicle \
vim \
unzip \
git \
curl
# Clear cache
RUN apt-get clean && rm -rf /var/lib/apt/lists/*
# Install extensions
RUN docker-php-ext-install pdo_mysql mbstring zip exif pcntl
RUN docker-php-ext-configure gd --with-gd --with-freetype-dir=/usr/include/ --with-jpeg-dir=/usr/include/ --with-png-dir=/usr/include/
RUN docker-php-ext-install gd
# Install composer
RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
# Add user for laravel application
RUN groupadd -g 1000 www
RUN useradd -u 1000 -ms /bin/bash -g www www
# Copy existing application directory contents
COPY . /var/www
# Copy existing application directory permissions
COPY --chown=www:www . /var/www
# Change current user to www
USER www
# Expose port 9000 and start php-fpm server
EXPOSE 9000
CMD ["php-fpm"]
首先,Dockerfile在php:7.2-fpm
Docker镜像之上创建一个图像 。 这是一个基于Debian的映像,它安装了PHP FastCGI实现PHP-FPM 。 该文件还为Laravel安装了必备软件包: mcrypt
, pdo_mysql
, mbstring
和imagick
with composer
。
RUN
指令指定更新,安装和配置容器内设置的命令,包括创建名为www的专用用户和组。 WORKDIR
指令将/var/www
目录指定为应用程序的工作目录。
创建具有受限权限的专用用户和组可以减少运行Docker容器时的固有漏洞,该容器默认以root身份运行。 我们创建了www用户, --chown
是以root用户身份运行这个容器,因为我们正在使用带有--chown
标志的COPY
指令来复制应用程序文件夹的权限,他对/var/www
文件夹具有读/写权限。 。
最后, EXPOSE
命令为php-fpm
服务器公开容器中的端口9000
。 CMD
指定创建容器后应运行的命令。 在这里, CMD
指定"php-fpm"
,它将启动服务器。
完成更改后,保存文件并退出编辑器。
您现在可以继续定义PHP配置。
第5步 - 配置PHP
现在您已经在docker-compose
文件中定义了基础结构,您可以将PHP服务配置为充当来自Nginx的传入请求的PHP处理器。
要配置PHP,您将在php
文件夹中创建local.ini
文件。 这是在第2步中绑定到容器内的/usr/local/etc/php/conf.d/local.ini
的文件。创建此文件将允许您覆盖PHP读取的默认php.ini
文件什么时候开始
创建php
目录:
mkdir ~/laravel-app/php
接下来,打开local.ini
文件:
nano ~/laravel-app/php/local.ini
为了演示如何配置PHP,我们将添加以下代码来设置上传文件的大小限制:
upload_max_filesize=40M
post_max_size=40M
upload_max_filesize
和post_max_size
指令设置上传文件的最大允许大小,并演示如何从local.ini
文件设置php.ini
配置。 您可以在local.ini
文件中放置要覆盖的任何特定于PHP的配置。
保存文件并退出编辑器。
使用PHP local.ini
文件,您可以继续配置Nginx。
第6步 - 配置Nginx
配置PHP服务后,您可以修改Nginx服务以使用PHP-FPM作为FastCGI服务器来提供动态内容。 FastCGI服务器基于二进制协议,用于将交互式程序与Web服务器连接。 有关更多信息,请参阅有关在Nginx中理解和实现FastCGI代理的文章。
要配置Nginx,您将在~/laravel-app/nginx/conf.d/
文件夹中创建一个带有服务配置的app.conf
文件。
首先,创建nginx/conf.d/
目录:
mkdir -p ~/laravel-app/nginx/conf.d
接下来,创建app.conf
配置文件:
nano ~/laravel-app/nginx/conf.d/app.conf
将以下代码添加到文件中以指定Nginx配置:
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;
}
}
服务器块使用以下指令定义Nginx Web服务器的配置:
-
listen
:该指令定义服务器将监听传入请求的端口。 -
error_log
和access_log
:这些指令定义用于写入日志的文件。 -
root
:该指令设置根文件夹路径,形成本地文件系统上任何请求文件的完整路径。
在php
位置块中, fastcgi_pass
指令指定app
服务正在监听端口9000
上的TCP套接字。 这使得PHP-FPM服务器通过网络而不是Unix套接字进行监听。 尽管Unix套接字在速度上比TCP套接字略有优势,但它没有网络协议,因此会跳过网络。 对于主机位于一台计算机上的情况,Unix套接字可能有意义,但是如果您在不同主机上运行服务,则TCP套接字提供了允许您连接到分布式服务的优势。 因为我们的app
容器在与我们的webserver
容器不同的主机上运行,所以TCP套接字对我们的配置最有意义。
完成更改后,保存文件并退出编辑器。
由于您在第2步中创建的绑定装载,您在nginx/conf.d/
文件夹中所做的任何更改都将直接反映在webserver
容器中。
接下来,让我们看看我们的MySQL设置。
第7步 - 配置MySQL
配置PHP和Nginx后,您可以启用MySQL作为应用程序的数据库。
要配置MySQL,您将在mysql
文件夹中创建my.cnf
文件。 这是在第2步中绑定到容器内的my.cnf
的文件。此绑定装置允许您在需要时覆盖my.cnf
设置。
为了演示其工作原理,我们将向my.cnf
文件添加设置,以启用常规查询日志并指定日志文件。
首先,创建mysql
目录:
mkdir ~/laravel-app/mysql
接下来,制作my.cnf
文件:
nano ~/laravel-app/mysql/my.cnf
在该文件中,添加以下代码以启用查询日志并设置日志文件位置:
[mysqld]
general_log = 1
general_log_file = /var/lib/mysql/general.log
此my.cnf
文件启用日志,将general_log
设置定义为1
以允许常规日志。 general_log_file
设置指定日志的存储位置。
保存文件并退出编辑器。
我们的下一步将是启动容器。
第8步 - 运行容器和修改环境设置
现在您已在docker-compose
文件中定义了所有服务并为这些服务创建了配置文件,您可以启动容器。 但是,作为最后一步,我们将复制.env.example
默认包含的.env.example
文件,并将副本命名为.env
,这是Laravel期望定义其环境的文件:
cp .env.example .env
一旦启动容器,我们将在此文件中配置我们的设置的具体细节。
在docker-compose
文件中定义了所有服务后,您只需发出一个命令来启动所有容器,创建卷,以及设置和连接网络:
docker-compose up -d
当你第一次运行docker-compose up
时,它会下载所有必要的Docker镜像,这可能需要一段时间。 下载图像并将其存储在本地计算机后,Compose将创建您的容器。 -d
标志守护进程,在后台运行容器。
该过程完成后,使用以下命令列出所有正在运行的容器:
docker ps
您将看到以下输出,其中包含有关您的app
, webserver
和db
容器的详细信息:
OutputCONTAINER ID NAMES IMAGE STATUS PORTS
c31b7b3251e0 db mysql:5.7.22 Up 2 seconds 0.0.0.0:3306->3306/tcp
ed5a69704580 app digitalocean.com/php Up 2 seconds 9000/tcp
5ce4ee31d7c0 webserver nginx:alpine Up 2 seconds 0.0.0.0:80->80/tcp, 0.0.0.0:443->443/tcp
此输出中的CONTAINER ID
是每个容器的唯一标识符,而NAMES
列出与每个容器关联的服务名称。 您可以使用这两个标识符来访问容器。 IMAGE
定义每个容器的图像名称,而STATUS
提供有关容器状态的信息:它是否正在运行,重新启动或停止。
您现在可以修改app
容器上的.env
文件,以包含有关您的设置的特定详细信息。
使用docker-compose exec
打开文件,它允许您在容器中运行特定命令。 在这种情况下,您将打开文件进行编辑:
docker-compose exec app nano .env
找到指定DB_CONNECTION
的块并更新它以反映您的设置的细节。 您将修改以下字段:
-
DB_HOST
将是您的db
数据库容器。 -
DB_DATABASE
将是laravel
数据库。 -
DB_USERNAME
将是您将用于数据库的用户名。 在这种情况下,我们将使用laraveluser
。 -
DB_PASSWORD
将是您要用于此用户帐户的安全密码。
DB_CONNECTION=mysql
DB_HOST=db
DB_PORT=3306
DB_DATABASE=laravel
DB_USERNAME=laraveluser
DB_PASSWORD=your_laravel_db_password
保存更改并退出编辑器。
接下来,使用php artisan key:generate
命令为Laravel应用程序设置应用程序密钥。 此命令将生成密钥并将其复制到.env
文件,以确保您的用户会话和加密数据保持安全:
docker-compose exec app php artisan key:generate
您现在拥有运行应用程序所需的环境设置。 要将这些设置缓存到文件中,这将提高应用程序的加载速度,请运行:
docker-compose exec app php artisan config:cache
您的配置设置将加载到容器上的/var/www/bootstrap/cache/config.php
中。
最后一步,在浏览器中访问http:// your_server_ip
。 您将看到Laravel应用程序的以下主页:
随着容器的运行和配置信息的到位,您可以继续在db
容器上配置laravel
数据库的用户信息。
第9步 - 为MySQL创建用户
默认的MySQL安装仅创建根管理帐户,该帐户在数据库服务器上具有无限权限。 通常,最好避免在与数据库交互时使用根管理帐户。 相反,让我们为应用程序的Laravel数据库创建一个专用的数据库用户。
要创建新用户,请使用docker-compose exec
在db
容器上执行交互式bash shell:
docker-compose exec db bash
在容器内,登录MySQL 根管理帐户:
mysql -u root -p
在docker-compose
文件中安装期间,系统将提示您输入为MySQL root帐户设置的密码。
首先检查您在laravel
docker-compose
文件中定义的名为laravel
的数据库。 运行show databases
命令以检查现有数据库:
show databases;
您将看到输出中列出的laravel
数据库:
Output+--------------------+
| Database |
+--------------------+
| information_schema |
| laravel |
| mysql |
| performance_schema |
| sys |
+--------------------+
5 rows in set (0.00 sec)
接下来,创建将允许访问此数据库的用户帐户。 我们的用户名将是laraveluser
,但如果您愿意,可以使用其他名称替换它。 请确保您的用户名和密码与您在上一步中的.env
文件中设置的详细信息相符:
GRANT ALL ON laravel.* TO 'laraveluser'@'%' IDENTIFIED BY 'your_laravel_db_password';
刷新权限以通知MySQL服务器更改:
FLUSH PRIVILEGES;
退出MySQL:
EXIT;
最后,退出容器:
exit
您已为Laravel应用程序数据库配置了用户帐户,并准备好迁移数据并使用Tinker控制台。
第10步 - 迁移数据和使用Tinker控制台
在您的应用程序运行时,您可以迁移数据并使用tinker
命令进行实验,该命令将启动预装了Laravel的PsySH控制台。 PsySH是用于PHP的运行时开发人员控制台和交互式调试器,而Tinker是专门用于Laravel的REPL。 使用tinker
命令将允许您从交互式shell中的命令行与Laravel应用程序进行交互。
首先,通过运行Laravel artisan migrate
命令测试与MySQL的连接,该命令在容器内部创建数据库中的migrations
表:
docker-compose exec app php artisan migrate
此命令将迁移默认的Laravel表。 确认迁移的输出将如下所示:
Output
Migration table created successfully.
Migrating: 2014_10_12_000000_create_users_table
Migrated: 2014_10_12_000000_create_users_table
Migrating: 2014_10_12_100000_create_password_resets_table
Migrated: 2014_10_12_100000_create_password_resets_table
迁移完成后,您可以使用tinker
命令运行查询以检查是否已正确连接到数据库:
docker-compose exec app php artisan tinker
通过获取刚刚迁移的数据来测试MySQL连接:
\DB::table('migrations')->get();
您将看到如下所示的输出:
Output=> Illuminate\Support\Collection {#2856
all: [
{#2862
+"id": 1,
+"migration": "2014_10_12_000000_create_users_table",
+"batch": 1,
},
{#2865
+"id": 2,
+"migration": "2014_10_12_100000_create_password_resets_table",
+"batch": 1,
},
],
}
您可以使用tinker
程序与数据库进行交互并尝试使用服务和模型。
有了Laravel应用程序,您就可以进行进一步的开发和实验。
结论
您现在已经在服务器上运行了LEMP应用程序,您已通过访问Laravel欢迎页面并创建MySQL数据库迁移来测试该应用程序。
这种安装简单的关键是Docker Compose,它允许您使用单个命令创建一组Docker容器,这些容器在单个文件中定义。 如果您想了解有关如何使用Docker Compose进行CI的更多信息,请参阅如何在Ubuntu 16.04上使用Docker和Docker Compose配置持续集成测试环境 。 如果您想简化Laravel应用程序部署过程,那么如何在Ubuntu 16.04上使用Deployer自动部署Laravel应用程序将是一个相关资源。