作为 Write for DOnations 计划的一部分,作者选择了 FreeBSD 基金会 来接受捐赠。
介绍
在过去的几年里,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 用户。 按照 Initial Server Setup with 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 image 挂载 Laravel 项目所需的目录,避免全局安装 Composer 的开销:
docker run --rm -v $(pwd):/app composer install
将 -v
和 --rm
标志与 docker run
一起使用会创建一个临时容器,该容器将在被删除之前绑定安装到您的当前目录。 这会将 ~/laravel-app
目录的内容复制到容器中,并确保 Composer 在容器内创建的 vendor
文件夹复制到当前目录。
最后一步,设置项目目录的权限,使其归您的非 root 用户所有:
sudo chown -R $USER:$USER ~/laravel-app
当您在第 4 步中为应用程序映像编写 Dockerfile 时,这一点很重要,因为它将允许您以非 root 用户身份使用应用程序代码并在容器中运行进程。
准备好应用程序代码后,您可以继续使用 Docker Compose 定义服务。
第二步——创建 Docker Compose 文件
使用 Docker Compose 构建应用程序可以简化基础设施的设置和版本控制过程。 为了设置我们的 Laravel 应用程序,我们将编写一个 docker-compose
文件来定义我们的 Web 服务器、数据库和应用程序服务。
打开文件:
nano ~/laravel-app/docker-compose.yml
在 docker-compose
文件中,您将定义三个服务:app
、webserver
和 db
。 将以下代码添加到文件中,确保替换MYSQL_ROOT_PASSWORD
的root密码,定义为db
服务下的环境变量,使用您选择的强密码:
~/laravel-app/docker-compose.yml
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 应用程序并运行自定义 Docker 映像digitalocean.com/php
,您将在第 4 步中定义该映像。 它还将容器中的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
的桥接网络。 桥接网络使用软件桥接器,允许连接到同一桥接网络的容器相互通信。 桥接驱动程序会自动在主机中安装规则,使不同桥接网络上的容器无法直接相互通信。 这为应用程序创建了更高级别的安全性,确保只有相关服务才能相互通信。 这也意味着您可以定义多个网络和服务连接到相关功能:例如前端应用服务可以使用frontend
网络,后端服务可以使用backend
网络.
让我们看看如何添加卷并将挂载绑定到您的服务定义以持久保存您的应用程序数据。
第 3 步 — 持久化数据
Docker 具有强大且方便的数据持久化功能。 在我们的应用程序中,我们将使用 volumes 和 bind mounts 来持久化数据库、应用程序和配置文件。 卷为容器生命周期之外的备份和持久性提供了灵活性,而绑定挂载促进了开发过程中的代码更改,使对主机文件或目录的更改立即在容器中可用。 我们的设置将利用这两者。
警告: 通过使用绑定挂载,您可以通过在容器中运行的进程来更改主机文件系统,包括创建、修改或删除重要的系统文件或目录。 这是一项具有安全隐患的强大功能,可能会影响主机系统上的非 Docker 进程。 小心使用绑定坐骑。
在 docker-compose
文件中,在 db
服务定义下定义一个名为 dbdata
的卷来持久化 MySQL 数据库:
~/laravel-app/docker-compose.yml
... #MySQL Service db: ... volumes: - dbdata:/var/lib/mysql networks: - app-network ...
命名卷 dbdata
保留了容器内存在的 /var/lib/mysql
文件夹的内容。 这允许您停止和重新启动 db
服务而不会丢失数据。
在文件底部,添加 dbdata
卷的定义:
~/laravel-app/docker-compose.yml
... #Volumes volumes: dbdata: driver: local
有了这个定义,您将能够跨服务使用这个卷。
接下来,为您将在第 7 步中创建的 MySQL 配置文件添加绑定挂载到 db
服务:
~/laravel-app/docker-compose.yml
... #MySQL Service db: ... volumes: - dbdata:/var/lib/mysql - ./mysql/my.cnf:/etc/mysql/my.cnf ...
此绑定挂载将容器中的 ~/laravel-app/mysql/my.cnf
绑定到 /etc/mysql/my.cnf
。
接下来,将绑定挂载添加到 webserver
服务。 将有两个:一个用于您的应用程序代码,另一个用于您将在第 6 步中创建的 Nginx 配置定义:
~/laravel-app/docker-compose.yml
#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/
中,允许您根据需要添加或修改配置目录的内容。
最后,将以下绑定挂载添加到应用程序代码和配置文件的 app
服务:
~/laravel-app/docker-compose.yml
#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
文件现在将如下所示:
~/laravel-app/docker-compose.yml
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 应用程序镜像。 将以下代码添加到文件中:
~/laravel-app/php/Dockerfile
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 \ 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
和 composer
。
RUN
指令指定用于更新、安装和配置容器内设置的命令,包括创建名为 www 的专用用户和组。 WORKDIR
指令将 /var/www
目录指定为应用程序的工作目录。
创建具有受限权限的专用用户和组可缓解运行 Docker 容器时的固有漏洞,默认情况下以 root 运行。 我们没有将这个容器作为 root 运行,而是创建了 www 用户,该用户对 /var/www
文件夹具有读/写权限,这要归功于 [ X157X] 指令与 --chown
标志一起用于复制应用程序文件夹的权限。
最后,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,我们将添加以下代码来设置上传文件的大小限制:
~/laravel-app/php/local.ini
upload_max_filesize=40M post_max_size=40M
upload_max_filesize
和 post_max_size
指令设置上传文件的最大允许大小,并演示如何从 local.ini
文件设置 php.ini
配置。 您可以将要覆盖的任何 PHP 特定配置放在 local.ini
文件中。
保存文件并退出编辑器。
准备好 PHP local.ini
文件后,您可以继续配置 Nginx。
第 6 步 — 配置 Nginx
配置好 PHP 服务后,您可以修改 Nginx 服务以使用 PHP-FPM 作为 FastCGI 服务器来提供动态内容。 FastCGI 服务器基于二进制协议,用于将交互式程序与 Web 服务器连接。 更多信息请参考这篇文章Understanding and Implementing FastCGI Proxying in Nginx。
要配置 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 配置:
~/laravel-app/nginx/conf.d/app.conf
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; } }
server 块 使用以下指令定义 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 中绑定到容器内的 /etc/mysql/my.cnf
的文件。 此绑定安装允许您在需要时覆盖 my.cnf
设置。
为了演示这是如何工作的,我们将在 my.cnf
文件中添加启用通用查询日志并指定日志文件的设置。
首先,创建mysql
目录:
mkdir ~/laravel-app/mysql
接下来,制作 my.cnf
文件:
nano ~/laravel-app/mysql/my.cnf
在该文件中,添加以下代码以启用查询日志并设置日志文件位置:
~/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
文件中定义了所有服务并为这些服务创建了配置文件,您可以启动容器。 不过,作为最后一步,我们将复制 Laravel 默认包含的 .env.example
文件,并将该副本命名为 .env
,这是 Laravel 期望定义其环境的文件:
cp .env.example .env
您现在可以修改 app
容器上的 .env
文件,以包含有关您的设置的特定详细信息。
使用 nano
或您选择的文本编辑器打开文件:
nano .env
找到指定 DB_CONNECTION
的块并更新它以反映您的设置的细节。 您将修改以下字段:
DB_HOST
将是您的db
数据库容器。DB_DATABASE
将是laravel
数据库。DB_USERNAME
将是您将用于数据库的用户名。 在这种情况下,我们将使用laraveluser
。DB_PASSWORD
将是您要用于此用户帐户的安全密码。
/var/www/.env
DB_CONNECTION=mysql DB_HOST=db DB_PORT=3306 DB_DATABASE=laravel DB_USERNAME=laraveluser DB_PASSWORD=your_laravel_db_password
保存更改并退出编辑器。
在 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
提供有关容器状态的信息:它是正在运行、正在重新启动还是已停止。
我们现在将使用 docker-compose exec
来设置 Laravel 应用程序的应用程序密钥。 docker-compose exec
命令允许您在容器中运行特定命令。
以下命令将生成一个密钥并将其复制到您的 .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 安装仅创建 root 管理帐户,该帐户在数据库服务器上具有无限权限。 通常,在与数据库交互时最好避免使用 root 管理帐户。 相反,让我们为应用程序的 Laravel 数据库创建一个专用的数据库用户。
要创建新用户,请使用 docker-compose exec
在 db
容器上执行交互式 bash shell:
docker-compose exec db bash
在容器内,登录 MySQL root 管理帐户:
mysql -u root -p
在安装过程中,将提示您在 docker-compose
文件中为 MySQL root 帐户设置的密码。
首先检查您在 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 应用程序将是一个相关资源。