# 透過WSL安裝網頁開發環境LNMP
WSL的教學與指令可以參考[保哥的這篇教學](https://blog.miniasp.com/post/2020/07/26/Multiple-Linux-Dev-Environment-build-on-WSL-2)
HOST環境:win10 pro最新版
WSL Distro
使用Linux: ubuntu
環境: nginx、PHP、MySQL
版本都使用當前最新版
---
## 參考資料
- [1]使用 WSL 2 打造優質的多重 Linux 開發環境
https://blog.miniasp.com/post/2020/07/26/Multiple-Linux-Dev-Environment-build-on-WSL-2
- [2]ubuntu-18.04-分別安裝-nginx、mysql、php7.4
https://pardocch.pixnet.net/blog/post/308060942-ubuntu-18.04-%E5%88%86%E5%88%A5%E5%AE%89%E8%A3%9D-nginx%E3%80%81mysql%E3%80%81php7.4
- [3]How To Install Linux, Nginx, MySQL, PHP (LEMP stack) on Ubuntu 18.04
https://www.digitalocean.com/community/tutorials/how-to-install-linux-nginx-mysql-php-lemp-stack-ubuntu-18-04
- [5]ubuntu21.04(linux):用apt安装nginx/php/mysql/phpmyadmin(开发环境)
https://www.cnblogs.com/architectforest/p/14905031.html
- [6]How to find WSL2 machine's IP address from windows
https://superuser.com/questions/1586386/how-to-find-wsl2-machines-ip-address-from-windows
---
### 列出幾個常用WSL指令
從來源[1][6]中擷取幾個常用指令
指令|功能
---|---
wsl -l -v | 列出目前已經安裝的 WSL Distro 版本<br>(包含顯示執行狀態與 WSL 版本)
wsl -l -q | 列出所有的 WSL Distro 版本<br>(僅顯示 WSL Distro 名稱)(方便做 CLI 整合)
wsl --export WLinux D:\WSL\WLinux.tar | 將WLinux匯出為WLinux.tar
wsl --import WLinux C:\WSL\WLinux WLinux.tar | 匯入特定 WSL Distro 版本
wsl -d WLinuxDev | 啟動特定 WSL Distro並進入shell
wsl -d Ubuntu-20.04 -u root | 啟動特定 WSL Distro並使用特定使用者進入shell
wsl -t WLinux | 關閉特定 WSL Distro
wsl --shutdown | 關閉所有 WSL Distro <br>(等同於關閉 VM 執行)
wsl --unregister WLinux2 | 移除 WSL Distro 版本<br>(注意:所有該 WSL Distro 下所有的檔案,都會被全數刪除,刪除後無法復原喔!)
wsl hostname -I | (來源[6]) 取得預設WSL Distro的內部IP
wsl -d "distroname" hostname -I | (來源[6]) 取得指定WSL Distro的內部IP
wsl --set-default "distroname" | (來源[6]) 設定預設WSL Distro
從內部執行
指令|功能
---|---
explorer.exe . |從 WSL Distro 內執行以下命令,就可以快速開啟「檔案總管」並且直接進入你在 WSL Distro 內的資料夾,超級方便!<br>(請注意:你一定要輸入 explorer.exe . 才可以,若是輸入 explorer . 是沒有效果的。)
## 安裝流程
網頁伺服器 > 資料庫 > PHP > 設定 > 測試
## 環境確認
HOST端(win10)
```
C:\Users\ma2g0>ipconfig
(略)
乙太網路卡 vEthernet (WSL):
連線特定 DNS 尾碼 . . . . . . . . :
連結-本機 IPv6 位址 . . . . . . . : fe80::680a:8bc6:4c90:71aa%44
IPv4 位址 . . . . . . . . . . . . : 172.21.144.1
子網路遮罩 . . . . . . . . . . . .: 255.255.240.0
預設閘道 . . . . . . . . . . . . .:
(略)
```
VM內(WSL Distro)
```
# cat /proc/version
Linux version 5.4.72-microsoft-standard-WSL2 (oe-user@oe-host) (gcc version 8.2.0 (GCC)) #1 SMP Wed Oct 28 23:40:43 UTC 2020
# lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description: Ubuntu 20.04.2 LTS
Release: 20.04
Codename: focal
# hostname -I
172.30.69.189
# hostname
DESKTOP-7OQ9ECL
```
為了方便起見,
可考慮把wsl內的IP加進win10的 hosts 中
但要注意每次重新啟動IP都會變動,
因為需要管理者權限無法自動化,
變成每次都要手動調整hosts的IP
## 加入各種來源
項目|指令
---|---
Nginx|sudo add-apt-repository ppa:ondrej/nginx<br>或 sudo add-apt-repository ppa:ondrej/nginx-mainline
PHP|sudo add-apt-repository ppa:ondrej/php
## 開始安裝
### 安裝Nginx
```
sudo apt install nginx
sudo service nginx restart
```
配置Nginx的位置為 /etc/nginx/sites-available
確認是否可以連線
```
# curl 127.0.0.1
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
...(略)...
```
連線ok
### 安裝MySQL
安裝:
```
sudo apt install mysql-server
```
為了確保安裝安全,MySQL 附帶了一個腳本,該腳本會詢問我們是否要修改一些不安全的默認值。通過鍵入以下內容啟動腳本:
設置:
設定root密碼與相關安全性
```
sudo mysql_secure_installation
```
結果竟然出錯@@
`Error: Can't connect to local MySQL server through socket '/var/run/mysqld/mysqld.sock' (2)`
為了解決這問題參考了一些資料
- [Mysql啟動失敗Can’t connect to local MySQL server through socket ‘/var/lib/mysql/mysq](https://www.itread01.com/p/1151446.html)
發現缺少/var/lib/mysql/mysql.sock檔案,懷疑路徑不對
- [‘mysql unrecognized service’ error – Why you can’t connect to MySQL?](https://bobcares.com/blog/mysql-unrecognized-service/)
看起來好像因為缺少mysql-devel,但我裝不起來,可能是因為文章的OS不同(他是用yum的,我ubuntu是用apt)
- [Missing mysqld.sock file?](https://mariadb.com/kb/en/missing-mysqldsock-file/)
或許是因為多個my.cnf導致,不過我只有一個,在/etc/mysql/my.cnf
- [Missing /var/lib/mysql/mysql.sock file](https://stackoverflow.com/a/46071961/7894412)
簡單暴力,缺啥就建立啥,直接建一個空白檔案
```
# touch /var/lib/mysql/mysql.sock
# touch /var/lib/mysql/mysql.pid
# chown -R mysql:mysql /var/lib/mysql
# sudo service mysql restart
* Restarting nginx nginx [ OK ]
root@DESKTOP-7OQ9ECL:/var/lib/mysql# sudo service mysql restart
* Stopping MySQL database server mysqld [ OK ]
* Starting MySQL database server mysqld
su: warning: cannot change directory to /nonexistent: No such file or directory
[ OK ]
```
哦哦哦!!!
看來復活了不過發現一個錯誤訊息
*su: warning: cannot change directory to /nonexistent: No such file or directory*
照篇文章解決他
[MySQL won't start - error: su: warning: cannot change directory to /nonexistent: No such file or directory](https://stackoverflow.com/questions/62987154/mysql-wont-start-error-su-warning-cannot-change-directory-to-nonexistent)
>mysql user is looking for a home directory, which seems to have not been assigned. To do that, you can execute:
```
sudo service mysql stop
sudo usermod -d /var/lib/mysql/ mysql
sudo service mysql start
```
重啟後正常,那接續設定~
>備註:
>重啟服務不要使用sudo systemctl restart mysql
>不然會出現以下出錯:
>System has not been booted with systemd as init system (PID 1). Can't operate.
>Failed to connect to bus: Host is down
再來一次`sudo mysql_secure_installation`
會詢問是否啟用`VALIDATE PASSWORD PLUGIN`來增加安全性
```
使用VALIDATE PASSWORD PLUGIN : y
安全等級 (0 = LOW, 1 = MEDIUM and 2 = STRONG) : 2
root New password: 新密碼
移除匿名帳戶(Remove anonymous users) : y
禁止root遠端登入(Disallow root login remotely) : n (這樣我開發比較方便)
移除測試資料庫(Remove test database and access to it) : y
重新載入設定(Reload privilege tables now) : y
```
依照來源[3]提到
> Note that in Ubuntu systems running MySQL 5.7 (and later versions), the root MySQL user is set to authenticate using the auth_socket plugin by default rather than with a password. This allows for some greater security and usability in many cases, but it can also complicate things when you need to allow an external program (e.g., phpMyAdmin) to access the user.
因為我的專案是直接用root+密碼登入的,所以會需要把auth_socket換成password,一起調整。
查看當前狀況
可以看到root那行是auth_socket,其他是caching_sha2_password
```
#sudo mysql
mysql> SELECT user,authentication_string,plugin,host FROM mysql.user;
+------------------+------------------------------------------------------------------------+-----------------------+-----------+
| user | authentication_string | plugin | host |
+------------------+------------------------------------------------------------------------+-----------------------+-----------+
| debian-sys-maint | $A$005$ZY\t!Ep(Pw]wHx/yuoDe8iVy84BN9UHbsHuTh56zpCkjLEmauCeNJKu84V0 | caching_sha2_password | localhost |
| mysql.infoschema | $A$005$THISISACOMBINATIONOFINVALIDSALTANDPASSWORDTHATMUSTNEVERBRBEUSED | caching_sha2_password | localhost |
| mysql.session | $A$005$THISISACOMBINATIONOFINVALIDSALTANDPASSWORDTHATMUSTNEVERBRBEUSED | caching_sha2_password | localhost |
| mysql.sys | $A$005$THISISACOMBINATIONOFINVALIDSALTANDPASSWORDTHATMUSTNEVERBRBEUSED | caching_sha2_password | localhost |
| root | | auth_socket | localhost |
+------------------+------------------------------------------------------------------------+-----------------------+-----------+
```
輸入指令來調整
`ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'password';`
調整後再看看
```
mysql> SELECT user,authentication_string,plugin,host FROM mysql.user;
+------------------+------------------------------------------------------------------------+-----------------------+-----------+
| user | authentication_string | plugin | host |
+------------------+------------------------------------------------------------------------+-----------------------+-----------+
| debian-sys-maint | $A$005$ZY\t!Ep(Pw]wHx/yuoDe8iVy84BN9UHbsHuTh56zpCkjLEmauCeNJKu84V0 | caching_sha2_password | localhost |
| mysql.infoschema | $A$005$THISISACOMBINATIONOFINVALIDSALTANDPASSWORDTHATMUSTNEVERBRBEUSED | caching_sha2_password | localhost |
| mysql.session | $A$005$THISISACOMBINATIONOFINVALIDSALTANDPASSWORDTHATMUSTNEVERBRBEUSED | caching_sha2_password | localhost |
| mysql.sys | $A$005$THISISACOMBINATIONOFINVALIDSALTANDPASSWORDTHATMUSTNEVERBRBEUSED | caching_sha2_password | localhost |
| root | *9E79180715D8D7FF07F9A4FB7895AC4DECC44A34 | mysql_native_password | localhost |
+------------------+------------------------------------------------------------------------+-----------------------+-----------+
5 rows in set (0.00 sec)
```
看起來Ok,重新載入後離開
`mysql > FLUSH PRIVILEGES;`
### 安裝PHP
安裝:
```
# sudo apt install php php-fpm php-mysql
```
確認當前版本php-fpm套件名稱與相關路徑
```
# whereis php-fpm
php-fpm: /usr/sbin/php-fpm8.0
```
得知當前版本名稱為 php-fpm8.0 之後會用到
知道名稱後就啟用他
```
# service php8.0-fpm start
# service php8.0-fpm status
* php-fpm8.0 is running
```
### 調整Nginx設定
Nginx
設定檔:/etc/nginx/sites-enabled/default
log:/var/log/nginx/error.log
服務重啟:sudo service nginx restart
PHP
查看php-fpm使用的端口状态(需要套件):netstat -nlt | grep 9000
log:/var/log/php8.0-fpm.log (每個人的預設的位置與名稱可能不一樣,可用上個小節的方式得知最有可能的名稱)
服務重啟:sudo service php8.0-fpm restart
調整後
拿掉一些註解
```
server {
listen 80 default_server;
listen [::]:80 default_server;
index index.php index.html index.htm index.nginx-debian.html;
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php8.0-fpm.sock;
}
}
```
服務重啟ok
```
# sudo service php8.0-fpm restart
* Restarting PHP 8.0 FastCGI Process Manager php-fpm8.0 [ OK ]
```
>備註:
假設若遇到找不到/run/php/php8.0-fpm.sock引起的錯誤,
先找找看是否是在不同路徑,
若還是找不到就直接建一個空白的給他
## 測試
接著建立一個測試頁
`vi /var/www/html/info.php`
內容
```
<?php
phpinfo();
```
儲存離開
接著連線測試
```
# curl -i localhost/info.php
HTTP/1.1 200 OK
Server: nginx/1.21.0
Date: Sat, 17 Jul 2021 00:55:23 GMT
Content-Type: text/html; charset=UTF-8
Transfer-Encoding: chunked
Connection: keep-alive
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"><head>
<style type="text/css">
(略)
```
測試ok
要不要刪掉測試頁看個人
`rm /var/www/html/info.php`
## 擴增與調整
安裝PHP其他功能
這裡只教學怎麼透過pecl安裝xdebug套件,
其他的功能在各自專案中安裝
### pecl
透過pecl來管理php擴充功能應該是最簡單的方法
他會用到php-dev套件,所以一並安裝
```
# sudo apt -y install php-pear php-dev
```
### xdebug
參考了這一篇論壇[How to install Xdebug on Ubuntu?](https://stackoverflow.com/questions/53133005/how-to-install-xdebug-on-ubuntu)
安裝xdebug有三種方法
1. 直接透過apt安裝php-xdebug,然後編輯php.ini / xdebug.ini
1. 透過`php -i`匯出資訊傳到 [xdebug官網](https://xdebug.org/wizard)分析後,接著照著官網步驟下載與安裝
1. 透過pecl來管理php擴充功能
第三點看起來最簡單方便,就他了,pecl安裝方式請看pecl小節
`sudo pecl install xdebug`
然後發現沒效果....@@
看一下phpinfo發現沒有xdebug... 阿哩 怎麼回事????
好吧用第一種方法繼續
`sudo apt install php-xdebug`
服務重啟後phpinfo就有xdebug了
但是還是不能單步執行...
會不會是第一種方法跟第三種方法衝突?
但拿掉其中一個就phpinfo就找不到xdebug了...
看起來還是只能編輯php.ini了
```
[XDebug]
debug.mode=debug,develop
xdebug.discover_client_host=false
xdebug.start_with_request=yes
xdebug.log=/var/www/xdebug.log
xdebug.output_dir = '/var/www/xdebug/'
;xdebug.default_enable = 1
;xdebug.remote_enable=1
xdebug.remote_handler=dbgp
;xdebug.client_host=host.docker.internal
;xdebug.remote_port=9003
```
設定完畢服務重啟後
phpinfo上面的設定
xdebug.output_dir是有變了,
但debug.mode還是在develop :question:

算了,以後有時間再回頭處理
### php.ini相關調整
參考:
[php.ini 設定詳解](https://www.puritys.me/docs-blog/article-50-php.ini-%E8%A8%AD%E5%AE%9A%E8%A9%B3%E8%A7%A3.html)
[記錄PHP錯誤日誌 display_errors與log_errors的區別](https://codertw.com/%E7%A8%8B%E5%BC%8F%E8%AA%9E%E8%A8%80/243547/)
#### 圖片上傳
項目 | 建議值 | 描述
--|--|--
file_uploads | on(預設) |
upload_max_filesize | 10M | 代表允許上傳的檔案最大 size<br>別忘了post_max_size也要調整
upload_tmp_dir | (留空) | 指定上傳檔案時的暫存資料夾,如果不指定的說,預設會存在 /tmp 裡
max_file_uploads | 20(預設) | 一次可以上傳的最大檔案數
post_max_size | 50M | 使用POST上傳的最大資料量
#### 錯誤相關
因為是開發用,不是正式場合,幾個選項可以調整一下方便開發
項目 | 建議值 | 描述
--|--|--
display_errors | ON | 開啟狀態下,若出現錯誤,則報錯,出現錯誤提示
log_errors | ON |
error_log | /var/log/php_errors.log |
error_reporting | E_ALL & ~E_NOTICE<br>(預設:E_ALL & ~E_DEPRECATED & ~E_STRICT) | 全部記錄但排除NOTICE
PS
- log_errors= On 且 error_log 未設定,則忽略display_errors設定,強制為ON
#### 其他
項目 | 建議值 | 描述
--|--|--
date.timezone| "Asia/Taipei"|
## 其他
### vi關閉貼上換行時自動被加上註解符號
來源 : https://disp.cc/b/11-3Trd
在vi中輸入`:set formatoptions-=r` 或 `:set fo-=r`
就可以關掉這個雞婆的功能了,不過效果只限於目前這個檔
### cli、fpm的php.ini不同
在處理xdebug得時候意外發現兩者使用不同的php.ini
在網頁上查看<?php phpinfo(); ?>
php.ini路徑為 /etc/php/8.0/fpm/php.ini
在shell中輸入 `php --ini | grep php.ini`
php.ini路徑為 /etc/php/8.0/cli/php.ini
查了一下
[[PHP] 使用 Nginx 與 PHP-FPM 搭配下分開 PHP-CLI 的 php.ini 設定](https://www.mxp.tw/6007/)
>像是 exec、eval、proc_open 等風險函式如果本機需要就打開,那線上環境也等於暴露在高風險環境下,此時勢必要區隔 PHP-FPM 與 PHP-CLI 這兩邊所載入的 php.ini 組態檔案。
>
>方法如這篇說明: [Setting php values in php-fpm confs instead of php.ini](**https://serverfault.com/questions/405684/setting-php-values-in-php-fpm-confs-instead-of-php-ini)
>
>等於是 PHP-CLI 使用預設 php.ini ,而 PHP-FPM 使用 php-fpm.conf 去覆寫預設 php.ini。
簡單說因為安全性所以cli、fpm的php.ini要分開,
然後文章說cli使用預設 php.ini,fpm用php-fpm.conf覆蓋???
文章最後更新時間為2019年,可能已經失效了?
還有一個奇怪的地方,
兩者的php.ini的擴充套件(extension)全部都是註解掉的,
那麼實際使用這些套件的設定在什麼地方?
查了一下有部分放在 `/usr/lib/php/20200930` 中
{"metaMigratedAt":"2023-06-16T04:31:07.532Z","metaMigratedFrom":"YAML","title":"透過WSL安裝網頁開發環境LNMP","breaks":true,"contributors":"[{\"id\":\"790c2f97-411b-4bae-8abe-c4aa8dddd981\",\"add\":26966,\"del\":13408}]"}