雖然標題上是寫 Laravel 用的環境,不過這套環境也適用於 WordPress。
LNMP 是一般對 Linux + Nginx + MySQL(MariaDB)+ PHP 的簡稱,不過用 Docker 來做的話應該要稱做 NMP才對。本次的範例在任何的 Linux Distro 底下應該都會有一樣的效果,理論上在 MacOS 也差不多,不過 Mac 也許還是會有此許的不同,這點我就沒有再多做測試。
會寫這篇的原因除了想開始學習 PHP 與 Laravel 之外,也是想多熟練 Docker,畢竟 config 或 yaml 寫好之後,帶到哪都可輕鬆重現環境,這種 IaC(Infrastructure-as-Code)加上版控的作法最近非常吸引我(就是喜歡不刺眼的黑黑畫面!)
扣除建立 Laravel 練習環境這件事,這篇文章其實也等同於用 Docker 建立一個普通的 LNMP Web Server,不過因為是練習用,所以 nginx 或 php 的一些細項設定就不太會提到了。
前置作業
首先在系統裡建立一個新資料夾 lnmp,裡面再分別建立四個資料夾,這邊將資料夾命名為:php、ngnix、mariadb 與 projects。
1 | mkdir lnmp && cd lnmp |
4 個資料夾的用途如下:
- mariaDB:
給 MariaDB 的 container 掛載用,存放 data base 的資料,也可以選擇不掛載 - nginx:
給 nginx container 掛載用,存放 nginx conf - php:
放自行 build 的 php Dockerfile - projects:
統一將 Laravel 或 WordPress 的資料夾放在這邊
使用 tree 指令是為了方便給大家看資料與資料夾間的關聯,這個指令通常不會內建在系統中,也不是很必要,可依個人喜好自行安裝
編寫 PHP 的 Dockerfile
PHP 官方的 Docker Image 基底為 Debian,另外也有出基於 Alpine 的版本,Alpine 做出來的 image 會比較輕量,另外我們的 web servce 是使用 nginx,所以就要選 php-fpm 的 image,而不是 php image。
本次的 Dockerfile 使用官方的 php:7.4-fpm-alpine 為底,如果只是想基本的練練 php,那直接使用官方的 php-fpm image 就可以了,但在 Laravel 的官網中有提到,7.x 版的 Laravel 需要許多額外的套件:
1 | - BCMath |
這些套件在公版 php image 裡是沒有的,所以我們必須自己寫 Dockerfile,將這些套件包進 image。
另外 WordPress 的官網也有提到所需的套件如下:
1 | - curl |
上面大多 module 是預設已經有的,或是與 Laravel 有套件有重覆到,所以這次包的 image 就是以上面的 module 為目標,加上 Laravel 會用到的 composer 把它們全部包起來,Dockerfile 如下:
1 | vim ./php/Dockerfile |
1 | # 注:此处只能使用PHP的fpm版本,如果不带fmp的话,nginx无法执行php脚本。至于能不能执行php-cli的定时任务,待验证。 |
這份 Dockerfile 是以 GitHub 上,jpswade 的範例為參考再加上缺少的套件。下面用了很多獨立的行來安裝單一的套件,是我在範例外新增的,其實也可以合併成一個,或合併到最上面的 RUN,看個人的偏好(分開寫的好處是比較容易 debug),可以等內容都很確定之後再重寫的乾淨點。
另外最底下把 image 裡 www-data 這個 user 的 uid 改成了 1000,這是為了之後 container 可以正常的存取檔案,而不受到檔案權限的阻擋。
新增 Nginx 設定檔
在 nginx 裡建立一個 conf.d 的資料夾,再新增設定檔如下:
1 | mkdir ./nginx/conf.d |
1 | server { |
基本上是直接拿 Laravel 官網的 conf 檔來改,要注意的地方如下:
- server_name:
自訂網站的域名,即使沒有申請,也建議設一下,之後再修改 hosts 檔就可以模擬真實的情況。 - root:
網頁檔存放的路徑,路徑可自訂,之後啟動 container 時要把本機的 projects 資料夾掛載到這邊。 - fastcgi_pass:
負責解析 php 的 service,一般的 LNMP Server 在這邊可能會是本機 127.0.0.1:9000,不過我們的 php 會放在別的 container 裡,並且會將該 php container 命名為 php,之後 docker-compose 啟動的每個 container 都能互相解析彼此的 host name,所以這邊設 php:9000 即可。
編寫 docker-compose
接著用 docker-compose 將所有的 service 都建構出來執行,內容如下:
1 | vim docker-compose.yml |
1 | services: |
- php:
- build 的 dockerfile 裡,指定要用哪個 Dockerfile 來 build php 的 image
- 在 volumes 這邊,必須要跟 nginx 一起掛載同個目錄,本例為本機上的 projects 資料夾 - nginx:
- ports,基本就是 80:80 的 mapping 或是加個 443:443
- volumes 部分,將本機上的 conf 與 projects 掛載到 container 中 - mariadb:
- ports 用預設的 3306:3306
- volumes 可將 container 裡的 DB 資料放到本機上,不設定的話,container 刪除後 DB 的資料也會不見,如果每次都想啟個乾淨環境的話就不用設這個
- environments 這邊設定 DB 的 root 密碼
執行 docker-compose
docker-compose.yml 寫好後,直接執行即可
1 | docker-compose up -d |
第一次執行時,php 的 image 需要 build ,所以要花比較多的時間,完成後可用指令查看 container 是否有成功的執行
1 | docker ps |
启动出现问题:docker-compose生成的容器立刻退出,exited with code 0
原因:docker容器执行任务完成后就会处于exited状态
解决:
Docker镜像的缺省命令是bash,如果不加-it
,bash命令执行了自动会退出,加-it
后docker命令会为容器分配一个伪终端,并接管其stdin/stdout支持交互操作,这时候bash命令不会自动退出
像不使用docker-compose,我们会执行类似如下的命令docker run -it --name node node
但docker-compose需要额外配置下,需要在docker-compose.yml中包含以下行:
1 | nginx: |
第一个对应于docker run中的
-i
,第二个对应于-t
。
建立資料庫與資料庫使用者
docker-compose 成功啟動後,表示 php、nginx、MariaDB 應該有順利運行了,再我們就建個資料庫給 Laravel 使用
首先用 root 登入 container 裡的 DB
1 | docker exec -it mariadb mysql -u root -p |
執行後會提示要輸入密碼,打上之前在 docker-compose.yml 裡定義好的 root 密碼
建立名為 laravel 的資料庫
1 | CREATE DATABASE laravel; |
新增使用者 laravel_user,密碼為 abcd1234
1 | CREATE USER 'laravel_user' IDENTIFIED BY 'abcd1234'; |
賦予使用者 laravel_user 存取 laravel 資料庫的權限,最後離開 DB
1 | GRANT ALL PRIVILEGES ON laravel.* TO 'laravel_user'; |
用 container 裡的 composer 建立 Laravel 專案
Laravel 可以從官網直接下載,也可以使用 composer 來安裝,既然我們有把 composer 這個套件包進 php 的 image 裡,就試試看用這種方式安裝吧!
首先進到 projects 目錄中
1 | cd projects |
查看 php image 的名稱
1 | docker images |
接著用自製的 php image 來執行 composer 指令
1 | docker run --rm -v $(pwd):/app lnmp_php composer create-project laravel/laravel /app/take1 |
-
--rm
:該 container 執行後就會刪除,因為建立專案是一次性的行為,因此加上自行刪除指令就不會留下多餘的 container -
-v $(pwd):/app
:將目前的資料夾掛載到 container 裡的 /app -
lnmp_php
:php 的 image 檔 composer create-project laravel/laravel /app/take1
:用 composer 在 container 裡的 /app/take1 中建立 Laravel 專案,如果 composer 是裝在本機而非 docker image 的話,只需要這一行就夠了。
另外因為本機的 projects 資料夾已和 container 中的 /app 做掛載綁定,因此指令完成後,就會在本機的 projects 裡建立一個名為 take1 的資料夾,裡面的內容即為 Laravel 的檔案
開啟 Laravel 的測試頁
還記得之前在 nginx conf 中設的 server_name「my-lab」 嗎?我們先給網站指定了域名,但這只是個假域名,所以記得先去 /etc/hosts 把 my-lab 這個域名與這台 Docker 主機的 ip 做 mapping。
接著就來試著用瀏覽器打開 Laravel 的首頁 http://my-lab/take1/public/ 看是否运行成功。
測試 Laravel 與 MariaDB 的連線
最後我們來測試 Laravel 是否可正常的與 DB 連線,如果只是想單純測試 php 與 DB 的連線,只要新增一段程式碼到 ./projects/take1/public 裡就可以了
1 | vim ./projects/take1/public/db_test.php |
1 |
|
再來用 curl 試一下這隻程式
1 | curl -L 'http://my-lab/take1/public/db_test.php' |
不過除了 php 之外,我也想知道 Laravel 能否正常連到 DB,這時我們就先編輯一下 Laravel 的 .env 這個檔案
1 | vim ./projects/take1/.env |
找到下面這段,並把它改成我們的連線資訊
1 | DB_CONNECTION=mysql |
最後再以 Laravel artisan 指令的 migrate 當作測試
1 | docker exec -it php php /my_projects/take1/artisan migrate:status |
有出現 table not found 就表示 Laravel 有連到 DB 了(不然只會出現錯誤的訊息)
參考資料:
PHP 7.4 PHP-FPM Alpine with core extensions gd
USE USERMOD AND GROUPMOD IN ALPINE LINUX DOCKER IMAGES
composer create-project, the permissions of all directories is 777?
Using a custom user for PHP-FPM and Nginx configurations in docker containers
Check for database connection, otherwise display message