作者选择了 The FreeBSD Foundation作为 Write for Donations计划的一部分接受捐款。
介绍
在过去的几年中,Docker(https://docs.docker.com/)已成为应用程序部署的常用解决方案,因为它简化了运行和部署以期限的应用程序(https://www.docker.com/resources/what-container)。
Docker Compose进一步简化了开发过程,允许开发人员在一个文件中定义他们的基础设施,包括应用服务,网络和卷,Docker Compose提供了运行多个docker container create
和docker container run
命令的有效替代方案。
在本教程中,您将使用 Laravel 框架构建一个 Web 应用程序,使用 Nginx 作为 Web 服务器和 MySQL 作为数据库,所有这些都在 Docker 容器中。
前提条件
在你开始之前,你需要:
- 一个 Ubuntu 18.04 服务器,以及具有
sudo
特权的非根用户。 遵循 初始服务器设置与 Ubuntu 18.04 教程来设置此设置。 - Docker 安装,遵循 如何在 Ubuntu 18.04 上安装和使用 Docker 的步骤 1 和 2。
- Docker Compose 安装,遵循 如何在 Ubuntu 18.04 上安装 Docker Compose 的步骤 1)。
步骤 1 – 下载 Laravel 和安装依赖
作为第一步,我们将获得最新的Laravel版本,并安装该项目的依赖,包括PHP的应用级包管理器Composer(LINK0)。
首先,检查您是否在您的主目录中,并将最新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 $USER:$USER ~/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
的 root密码,定义为 环境变量:
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: your_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 - 持久数据
在我们的应用中,我们将使用 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.2-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
24# Clear cache
25RUN apt-get clean && rm -rf /var/lib/apt/lists/*
26
27# Install extensions
28RUN docker-php-ext-install pdo_mysql mbstring zip exif pcntl
29RUN docker-php-ext-configure gd --with-gd --with-freetype-dir=/usr/include/ --with-jpeg-dir=/usr/include/ --with-png-dir=/usr/include/
30RUN docker-php-ext-install gd
31
32# Install composer
33RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
34
35# Add user for laravel application
36RUN groupadd -g 1000 www
37RUN useradd -u 1000 -ms /bin/bash -g www www
38
39# Copy existing application directory contents
40COPY . /var/www
41
42# Copy existing application directory permissions
43COPY --chown=www:www . /var/www
44
45# Change current user to www
46USER www
47
48# Expose port 9000 and start php-fpm server
49EXPOSE 9000
50CMD ["php-fpm"]
首先,Dockerfile 会在 php:7.2-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
,虽然你可以用另一个名字更换,如果你喜欢。 只要确保你的用户名和密码在这里匹配你在上一步中设置的.env
文件的细节:
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。使用tinker
命令将允许您从命令行与Laravel应用程序进行交互。
首先,通过运行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 数据库迁移来测试该应用程序。
如果您想了解更多关于如何使用 Docker Compose 进行 CI 的信息,请参阅 如何在 Ubuntu 16.04 上配置使用 Docker 和 Docker Compose 进行连续集成测试环境. 如果您想要简化 Laravel 应用程序部署过程,那么 如何在 Ubuntu 16.04 上自动使用 Deployer 部署 Laravel 应用程序将是一个相关资源。