自由學習的風

幽夢影 張潮 少年讀書,如隙中窺月;中年讀書,如庭中望月;老年讀書,如臺上玩月。皆以閱歷之淺深,為所得之淺深耳。

Docker container 的 UID/GID 權限整理

2026年7月28日 星期二

示意圖:Docker UID/GID 權限關係


這次整理 Docker 多網站環境時,遇到一個很常見的問題:不同 container 裡的使用者 UID/GID 不一樣,結果 bind mount 到 host 的檔案權限就容易亂掉。

問題:

目前有這些服務:

  1. nginx
  2. php-fpm
  3. php82-fpm
  4. php83-fpm
  5. mysql

PHP-FPM 大多已經是:

uid=1000(www) gid=1000(www)

但是 Nginx worker 可能是:

uid=101(nginx) gid=101(nginx)

MySQL 則是自己的:

uid=27(mysql) gid=27(mysql)

這時候如果 PHP 建立檔案、Nginx 要讀檔,或者 host 要直接修改檔案,就可能遇到權限問題。

原則:

不要把所有 container 都硬改成同一個 UID。

應該分成兩類:

網站檔案:
nginx、php-fpm、php82-fpm、php83-fpm

資料庫資料:
mysql

也就是網站相關服務統一,MySQL 保持自己的使用者。

建議目標:

nginx worker -> 1000:1000
php-fpm      -> 1000:1000
php82-fpm    -> 1000:1000
php83-fpm    -> 1000:1000
mysql        -> 保持 mysql 預設 UID/GID

網站目錄可以整理成:

sudo chown -R 1000:1000 ./wwwroot
sudo chown -R 1000:1000 ./session
sudo chown -R 1000:1000 ./log/nginx

MySQL 資料目錄不要一起改。

如果 MySQL container 裡查到是 27:27:

sudo chown -R 27:27 ./data/mysql/data

Nginx 注意事項:

原本以為可以直接在 nginx.conf 寫:

user 1000 1000;

結果啟動時出現:

getpwnam("1000") failed

原因是 Nginx 的 user 指令會去找使用者名稱,不是單純用數字 UID。

比較好的方式是自建 Nginx image,在 image 裡建立一個 www 使用者,UID/GID 是 1000:1000,然後 nginx.conf 寫:

user www www;

驗證:

先讓 PHP 建立測試檔:

docker compose exec php-fpm sh -c 'echo ok > /var/www/permission-test.txt'

再讓 Nginx 讀取:

docker compose exec nginx sh -c 'cat /var/www/permission-test.txt'

回 host 看 owner:

ls -lan ./wwwroot/permission-test.txt

如果看到:

1000 1000

而且 Nginx 也能讀到 ok,表示權限大致整理好了。

結論:

遇到 Docker 權限問題,不要先用:

chmod -R 777

這只是把問題蓋掉。

比較好的做法是先弄清楚誰要讀、誰要寫,再把共同使用同一批檔案的 container 對齊 UID/GID。

Docker 多網站部署架構整理

示意圖:Docker 多網站部署架構








圖片檔 blog-drafts/images

目前在 Linux 主機上利用 Docker 跑多個網站,這次順手把目前的建置方式整理一下,避免之後要調整時又要重新看一次。

環境:

  1. Linux 主機
  2. Docker / Docker Compose
  3. Nginx 或 Caddy 做 Web 入口
  4. 多個 PHP-FPM container
  5. MySQL container

目前架構:

大致上是這樣:

Browser
  ↓
Nginx / Caddy
  ↓
依不同網站轉給不同 PHP-FPM
  ↓
MySQL

這樣的做法有點像把傳統的 LEMP 主機,改成用 Docker 來管理。

目的:

  1. 一台主機可以放多個 PHP 網站
  2. 不同網站可以使用不同 PHP 版本
  3. 網站目錄、log、資料庫資料都放在 host 上,方便備份
  4. Web server 統一處理 80、443、SSL、virtual host
  5. 需要瀏覽器自動化時,可以用 geckodriver container

目前比較像「共享主機 Docker 化」,不是每個網站都獨立一整組服務。

建議調整:

1. 不要使用 latest

例如:

NGINX_VERSION=nginx:latest

建議改成固定版本:

NGINX_VERSION=nginx:1.29.3-alpine

不然之後重新 build 或 pull 時,可能會拉到不同版本,問題會比較不好追。

2. PHP 7.4 要列為舊系統

如果還有網站需要 PHP 7.4,可以先保留,但要知道這已經是 legacy 環境。

建議做法:

  1. 先列出哪些網站還需要 PHP 7.4
  2. 確認不能升級的原因
  3. 有計畫地改到 PHP 8.2 / 8.3 / 8.4

3. MySQL 不一定要開給外部

如果只有 container 裡的網站會連 MySQL,通常不需要:

ports:
  - 3306:3306

比較保守可以改成:

ports:
  - 127.0.0.1:3306:3306

或者乾脆不要 publish port,只讓 Docker network 內部連線。

4. volumes 和 bind mount 要整理

目前像這些目錄都會影響備份:

wwwroot/
data/mysql/data/
etc/nginx/
etc/caddy/
log/
session/

建議之後整理一份備份清單,才不會只備份 image,卻漏掉真正重要的網站資料。

結論:

這個架構是可行的,而且蠻適合維護多個 PHP 網站。

接下來優先處理的順序,我覺得可以是:

  1. 固定 image 版本
  2. 整理 Nginx / PHP-FPM 權限
  3. 收斂 MySQL 對外連線
  4. 補上 healthcheck
  5. 將 PHP 7.4 網站逐步升級
  6. 評估改用 Caddy 簡化 HTTPS

先把基礎整理好,之後要搬主機、重建服務或排除錯誤都會輕鬆很多。

VS Code 直接透過 sftp 同步到遠端伺服器

2024年9月29日 星期日

環境介紹:

  • 正式機:10.x.y.aaa,  Linux Ubuntu + Docker(Nginx + PHP + MySQL)
  • 開發機:10.x.y.bbb, Windows 11 + Laragon + VS Code

之前利用 VS Code 開發之前,總是利用 SSHFS-Win 掛載至本機後,再用比對軟體檢查哪些檔案有修改,是否需要上傳(註:freecommander 很好用)。

不過,有時只是一、二支程式需要上傳,總覺得這樣做的話有點繁瑣,最近發現有支 vscode 的 extension(擴充套件):sftp。

可以 ssh 連到遠端伺服器,直接上傳,也支援多個網站,所以可以先上傳到測試伺服器,沒問題後再上傳到正式機。

安裝後要先設定遠端伺器資訊,按【F1】,選擇「SFTP: Config」,會在 .vscode 目錄中產生 sftp.json 設定範本檔,直接依個人需求填入設定值即可。


預設是只會有一組伺服器設定,若有多組伺服器的話,例如有測試機、正式機…等,那就要設定 profiles,把設定檔改成下列即可,連線時,按按【F1】,選擇「SFTP: Set Profile」,選擇要連線的伺服器即可。


這支擴充套件還提供其它蠻方便的功能,也可以去發掘喔!

利用 Docker 快速建立 Sql Server 環境

2023年2月16日 星期四

環境:

  1. Ubuntu Linux 2022
  2. Docker CE 23.0.1

Docker 映像檔:

  • mcr.microsoft.com/mssql/server:2022-latest

操作:

  1. 下載 SQL Server 2022 Image
    $ docker pull mcr.microsoft.com/mssql/server:2022-latest
  2. 執行容器(指定參數)
    $ docker run \
    -e "ACCEPT_EULA=Y" \
    -e "MSSQL_SA_PASSWORD=<YourStrong@Passw0rd>" \
    -p 1433:1433 --name sql1 --hostname sql1 \
    -d \
    mcr.microsoft.com/mssql/server:2022-latest

  3. 檢查容器執行狀態
    $ docker ps  
  4. 安裝成功後,可下載或利用 chocolatey 來安裝 Sql Studio Management Studio
    c:\> choco install sql-server-management-studio
  5. 連線至 SQL Server 容器

容器執行參數說明:

  1. -e ACCEPT_EULA=Y  同意使用者授權合約
  2. -e MSSQL_SA_PASSWORD=<YourStrong@Passw0rd> 設定 SQL Server 最高管理員 sa 的密碼
  3. --name sql1 設定容器名稱
  4. --hostname sql1 設定容器主機名稱
  5. -p 1433: 1433 設定 port 的對應
  6. -d 設定容器以背景模式執行
  7. mcr.microsoft.com/mssql/server:2022-latest  SQL Server 映像檔來源與名稱

進階:

1. 掛載 volume 保存資料庫內容

  1. host 建立掛載的目錄
    $ mkdir data_mssql
  2. 【必須】設定該目錄擁有者, 預設owner id 為 10001
    $ sudo chown -R 10001 data_mssql
  3. 停止、刪除容器後,加上掛載 volume 參數後重新啟動容器
    $ docker run \
    -e "ACCEPT_EULA=Y" \
    -e "MSSQL_SA_PASSWORD=<YourStrong@Passw0rd>" \
    -p 1433:1433 --name sql1 --hostname sql1 \
    -v ./data_mssql:/var/opt/mssql
    -d \
    mcr.microsoft.com/mssql/server:2022-latest

2. 利用 docker-compose 執行容器

1. 建立 docker-compose.yml 檔案,內容如下:
version: '3.5'

services:
  mssql:
    image: mcr.microsoft.com/mssql/server:2022-latest
    restart: always
    ports:
      - 1433:1433
    volumes:
      - ./data_mssql:/var/opt/mssql
    environment:
      - ACCEPT_EULA=Y
      - SA_PASSWORD=PutYourPassword
      - MSSQL_PID=Express
2. 執行

$ docker-compose up -d 


VS Code integreted terminial 的環境變數沒有更新

2023年1月15日 星期日

執行環境:

  • Windows 11 21H2(22000.1042)
  • Laragon 5.0(6.0)
  • VSCode
  • CodeIgniter 4.1.9

在Windows環境上利用 Laragon 來建立 PHP 開發環境十分方便,可以快整切換 PHP 版本,程式撰寫則用  VSCode,本來都沒啥問題,不過,這兩天測試之前建立的 CodeIgniter 4,它的版本是  4.1.9,看官網已經出到  4.3.0,不過需要 PHP 7.4 以上,但是之前都在 PHP 7.3 開發,結果在 Laragon 切換成 PHP 7.4 後,VSCode integreted terminial 顯示的還是  PHP 7.3。

試了下列動作:

  1. 把 VSCode 全部關掉,重新再執行 => 無效
  2. 檢查電腦環境變數,PATH 沒有設定 PHP 路徑
  3. 將 PHP 7.4 路徑加入 PATH 環境變數 => 無效

本來以為是電腦出問題了,後來看到一份資料,需要從終端機直接執行 VSCode,讚!有效,解決了,平時我都是按 Win Key,再直接執行,沒想到執行方式不一樣會造成這種影響。


讀取 空氣品質指標(AQI) 的OpenData 資料

2022年10月15日 星期六

一、前由

前2天有位夥伴反應一個問題,之前讀取線上的 空氣品質指標(AQI) 資料時可以秒開,可是現在已經換成新的網址,結果讀取時間變得得慢,想請教大家有沒有解法。

 1. 空氣品質指標(AQI) 舊網址:http://opendata2.epa.gov.tw/AQI.json

 2. 空氣品質指標(AQI) 新網址:ttps://data.epa.gov.tw/api/v2/aqx_p_432?api_key=e8dd42e6-9b8b-43f8-991e-b3dee723a52d&limit=1000&sort=ImportDate desc&format=JSON

二、測試、找原因

直接用瀏覽器開AQI 舊網址,果然秒開;接著瀏覽器再開新 AQI 網址,心裡默數,回應時間不一定,從6秒~11秒都有,換成 Windows PowerShell終端機,利用 curl 指令來抓取新網址,也是一樣的回應時間。

查了資料,發現新網址是環保署的服務,也有提供 API 的操作手冊,  裡面有提供 filter 來過濾回傳的內容資料,參考操作手冊,直接在網址上加上 filter 的條件,情況還是一樣,而且 filter 條件並沒有作用。

不死心,又查了一下資料,有篇文章「MicroPython 空氣品質 AQI 獲取之方式」有提到抓取資料改成要申請 API_KEY、換成 HTTPS 協定,以及利用 filters 的 API 語法來指定查詢的項目。所以我就去申請了一組 API_KEY 準備看看是否正常,唉!無效,還是一樣慢,API 語法也沒有作用……

總覺得不太可能提供給全國查詢的服務會這麼慢,所以直接利用 php 去抓網頁資料,Guess What ?

秒回啊!

三、實作

利用簡單的語法寫了簡單抓不同城市的 AQI 資料,回應速度快,滿意,結案!

<h2>空氣品質指標(AQI)查詢</h2>
<form action="" method="POST">

  <select name="county" id="">
    <option value="新北市">新北市</option>
    <option value="高雄市">高雄市</option>
    <option value="臺北市">臺北市</option>
    <option value="臺中市">臺中市</option>
    <option value="臺南市">臺南市</option>
  </select>

  <input type="submit" value="查詢">

</form>

<?php
if (isset($_POST["county"]) &&  $_POST["county"] != "") {
  $county = $_POST["county"] ?? '新北市';
  $key = 'e8dd42e6-9b8b-43f8-991e-b3dee723a52d';
  $filter = 'county,EQ,' . $county;
  $url = sprintf('https://data.epa.gov.tw/api/v2/aqx_p_432?filters=%s&api_key=%s&format=json', $filter, $key);
  $data =  file_get_contents($url);

  $jsondata = json_decode($data);

  echo ('<h2>' . $county . '</h2>');
  var_dump($jsondata->records);
}


四、後記

後來發現 Windows 底下的 curl  指令有點"不好操控",利用 Linux 環境直接下 curl 指令,回應速度也是秒開。



sr-only 是做什麼用的?

常常在網頁中看到這個屬性,檢視網頁發現不會顯示,好奇查了一下資料,原因它是指「screen reader only」,也就是設計給螢幕閱讀器讀取的屬性,一般的瀏覽器不會顯示,長知識了!