PUID และ PGID ใน Docker Compose คืออะไรและตั้งค่าอย่างไร
PUID และ PGID ไม่ใช่การตั้งค่าของ Docker แต่เป็นข้อตกลงของ linuxserver.io เพื่อจัดการสิทธิ์ไฟล์บน Host เรียนรู้วิธีแก้ไขปัญหาไฟล์ติดสิทธิ์ 911:911 อย่างถูกต้อง
PUID และ PGID คืออะไร
PUID และ PGID คือตัวแปรสภาพแวดล้อม (environment variables) สองตัวที่ container image บางตัวจะอ่านค่าเมื่อเริ่มต้นทำงาน ตัว Docker เองไม่ได้ตรวจสอบค่าเหล่านี้แต่อย่างใด ค่าเหล่านี้เป็นข้อตกลงร่วมกันที่ใช้ใน image ของ linuxserver.io และ image อื่นๆ อีกจำนวนหนึ่ง ดังนั้นหาก image ไม่ได้ถูกเขียนมาให้อ่านค่าเหล่านี้ มันก็จะเพิกเฉยต่อตัวแปรดังกล่าวโดยไม่มีการแจ้งเตือน
ภายใน image ของ linuxserver.io จะมีผู้ใช้ชื่อ abc ซึ่งถูกสร้างขึ้นในขั้นตอน build โดยมี UID (user ID) เป็น 911 และ GID (group ID) เป็น 911 ตัว container จะเริ่มต้นด้วยสิทธิ์ root จากนั้นจะรันสคริปต์เริ่มต้น (init scripts) และหนึ่งในสคริปต์เหล่านั้นจะทำการเปลี่ยนหมายเลข UID/GID ของผู้ใช้ดังกล่าวก่อนที่จะดำเนินการอื่นใด:
groupmod -o -g "${PGID}" abc
usermod -o -u "${PUID}" abcแฟล็ก -o อนุญาตให้ใช้ ID ที่มีการใช้งานอยู่แล้วในที่อื่น หลังจากนั้นสคริปต์เริ่มต้นจะลดระดับสิทธิ์ลงและรันแอปพลิเคชันในฐานะผู้ใช้ abc ดังนั้น PUID=1000 จึงไม่เคยส่งไปถึง Docker ตัวแปรนี้จะทำการเปลี่ยนหมายเลขผู้ใช้ภายใน container ก่อนที่แอปพลิเคชันจะเริ่มทำงาน ซึ่งหมายความว่าทุกไฟล์ที่แอปพลิเคชันเขียนลงบนดิสก์ของคุณจะมีเจ้าของเป็น 1000 หากคุณไม่กำหนดค่า PUID ตัวแปร abc จะคงค่าเป็น 911 ไว้ ซึ่งเป็นสาเหตุที่ทำให้ bind mount ที่ไม่ได้ตั้งค่าไว้มีไฟล์ที่ถูกสร้างขึ้นโดยมีเจ้าของเป็น 911:911
รับค่าตัวเลขสองค่าของคุณด้วย id
ให้รันคำสั่งนี้บนโฮสต์ โดยใช้ผู้ใช้ที่เป็นเจ้าของไดเรกทอรีข้อมูล:
iduid=1000(deploy) gid=1000(deploy) groups=1000(deploy),27(sudo),988(docker)uid คือ PUID ของคุณ และ gid คือ PGID ของคุณ สำหรับการเขียนสคริปต์ id -u และ id -g จะแสดงเฉพาะตัวเลขออกมา ใน VPS ส่วนใหญ่ที่ติดตั้งใหม่ บัญชีผู้ใช้แรกมักจะเป็น 1000:1000 แต่ห้ามคาดเดาเอาเอง เซิร์ฟเวอร์ที่สร้างใหม่ หรือบัญชีที่สองที่เพิ่มเข้ามาภายหลัง อาจได้ค่าเป็น 1001 หรือสูงกว่า ซึ่งการระบุตัวเลขผิดในส่วนนี้คือสาเหตุของปัญหาทั้งหมด หากบริการของคุณทำงานภายใต้ บัญชีผู้ใช้สำหรับบริการโดยเฉพาะแทนที่จะเป็นบัญชีล็อกอินของคุณเอง ให้รัน id thatuser แล้วนำตัวเลขจากผลลัพธ์นั้นมาใช้
เหตุใดไฟล์ของคุณจึงแสดงผลเป็น 911:911
ls -l จะแสดงผลเป็นตัวเลข ID แทนชื่อผู้ใช้เมื่อไม่มีบัญชีในโฮสต์ที่ตรงกับ ID นั้น เนื่องจากไม่มีสิ่งใดบนเซิร์ฟเวอร์ของคุณที่มี UID เป็น 911 จึงไม่มีชื่อให้แสดงผล ให้ใช้ ls -ln เพื่อดูตัวเลขทุกครั้งและขจัดความคลุมเครือ:
ls -ln /srv/appdata/sonarrdrwxr-xr-x 2 911 911 4096 Aug 7 09:12 Backups
-rw-r--r-- 1 911 911 512 Aug 7 09:12 config.xmlผลลัพธ์ดังกล่าวระบุว่าคอนเทนเนอร์ทำงานด้วยค่าเริ่มต้นที่มากับระบบ ให้ยืนยันจากภายในคอนเทนเนอร์แทนการคาดเดา:
docker exec sonarr id abc
docker compose logs sonarr | head -n 25กระบวนการเริ่มต้นของ linuxserver จะพิมพ์ผลลัพธ์ใน log การเริ่มระบบเป็นสองบรรทัด:
User UID: 911
User GID: 911หากบรรทัดเหล่านั้นแสดงค่า 911 หลังจากที่คุณตั้งค่า PUID=1000 ในไฟล์ Compose ของคุณ แสดงว่าตัวแปรนั้นไม่ได้ถูกส่งไปยังคอนเทนเนอร์ สาเหตุทั่วไปคือคุณแก้ไขไฟล์ docker-compose.yml แล้วรันคำสั่ง docker compose restart ซึ่งเป็นการนำคอนเทนเนอร์เดิมที่มีสภาพแวดล้อมแบบเก่ากลับมาใช้ใหม่ การเปลี่ยนแปลงสภาพแวดล้อมจำเป็นต้องใช้คำสั่ง docker compose up -d ซึ่งจะทำการสร้างคอนเทนเนอร์ขึ้นมาใหม่
เหตุผลที่คุณไม่สามารถลบไฟล์ที่คอนเทนเนอร์สร้างขึ้นได้
เคอร์เนลเปรียบเทียบตัวเลข ไม่ใช่ชื่อ เชลล์ของคุณทำงานในฐานะ UID 1000 แต่ไฟล์นั้นเป็นของ UID 911 ไดเรกทอรีที่เก็บไฟล์ดังกล่าวคือ drwxr-xr-x ซึ่งเป็นของ 911 เช่นกัน ดังนั้นกลุ่มและผู้ใช้อื่นจึงมีสิทธิ์อ่านและเรียกใช้งานได้ แต่ไม่มีสิทธิ์เขียน การลบไฟล์จำเป็นต้องมีสิทธิ์เขียนบน ไดเรกทอรี ที่เก็บไฟล์นั้น ไม่ใช่ที่ตัวไฟล์เอง คุณจึงพบข้อผิดพลาดนี้แม้ว่าตัวไฟล์จะดูไม่มีอันตรายก็ตาม:
rm: cannot remove '/srv/appdata/sonarr/config.xml': Permission deniedคอนเทนเนอร์ที่พยายามเขียนไฟล์จะพบปัญหาเดียวกันในมุมกลับ หากไดเรกทอรีบนโฮสต์เป็นของผู้ใช้ของคุณในโหมด 755 และแอปพลิเคชันทำงานในฐานะ 911 การเขียนครั้งแรกจะล้มเหลวด้วย Permission denied และแอปพลิเคชันจะรายงานข้อผิดพลาดในรูปแบบของตนเอง ในแอปพลิเคชัน .NET เช่น Sonarr หรือ Radarr ข้อผิดพลาดนี้จะปรากฏเป็น UnauthorizedAccessException: Access to the path '/data/downloads' is denied สตริงสิทธิ์ที่อยู่หน้าชื่อไฟล์จะบอกคุณว่าคุณกำลังถูกตรวจสอบด้วยชุดสิทธิ์ใดในสามชุด และการ อ่าน drwxr-xr-x ให้ถูกต้อง คือสิ่งที่เปลี่ยนข้อผิดพลาดที่ดูซับซ้อนให้กลายเป็นเรื่องที่เข้าใจได้ทันที
นี่เป็นปัญหาที่เกิดจาก bind mount โดยเฉพาะ เมื่อ Docker สร้าง named volume ที่ว่างเปล่า และ mount ทับ path ที่มีอยู่แล้วในอิมเมจ Docker จะคัดลอกเนื้อหาของ path นั้นลงใน volume รวมถึงความเป็นเจ้าของและบิตสิทธิ์ต่างๆ ทำให้แอปพลิเคชันพบไดเรกทอรีที่ตนเองเป็นเจ้าของอยู่แล้ว แต่ bind mount จะไม่ได้รับการจัดการในลักษณะนี้ Docker จะ mount ไดเรกทอรีบนโฮสต์ของคุณตามสภาพที่เป็นจริง ความแตกต่างนี้เป็นหนึ่งในเหตุผลเชิงปฏิบัติที่คุณควรทราบว่า เมื่อใดที่ bind mount ดีกว่า named volume และเมื่อใดที่ไม่ควรใช้
การแก้ไขไดเรกทอรีที่ตั้งค่าสิทธิ์ไม่ถูกต้องไปแล้ว
การตั้งค่า PUID และ PGID จะส่งผลต่อการทำงานของแอปพลิเคชันนับจากนี้เป็นต้นไป แต่จะไม่ย้อนกลับไปแก้ไขไฟล์ที่อยู่บนดิสก์อยู่ก่อนแล้ว ให้หยุดการทำงานของ stack จากนั้นแก้ไขความเป็นเจ้าของไฟล์ด้วยตนเอง แล้วจึงเริ่มการทำงานใหม่อีกครั้ง:
docker compose down
sudo chown -R 1000:1000 /srv/appdata/sonarr
docker compose up -dให้ใช้ sudo chown -R "$(id -u):$(id -g)" /srv/appdata/sonarr หากคุณไม่ต้องการพิมพ์ตัวเลขด้วยตนเอง ให้ดำเนินการในขณะที่ container หยุดทำงานอยู่เท่านั้น เนื่องจากหากแอปพลิเคชันกำลังเขียนข้อมูลในระหว่างที่คำสั่ง chown แบบ recursive กำลังทำงาน อาจทำให้โครงสร้างไดเรกทอรีถูกแก้ไขเพียงบางส่วน และนำไปสู่ข้อผิดพลาดรอบสองที่สร้างความสับสนได้
สิ่งที่ PUID และ PGID แก้ไขไม่ได้
นี่คือส่วนที่ทำให้ผู้ใช้ที่ทำตามขั้นตอนอย่างถูกต้องต้องประสบปัญหา สคริปต์เริ่มต้นของ linuxserver จะทำการ chown เส้นทางเพียง 3 แห่งเท่านั้นเมื่อเริ่มทำงาน ได้แก่ /app, /config และ /defaults โดยที่ mount point ของสื่อบันทึกข้อมูลของคุณไม่ได้อยู่ในรายการนี้ เส้นทาง /data, /downloads และ /tv จะถูกส่งต่อไปยังแอปพลิเคชันโดยไม่มีการเปลี่ยนแปลงใดๆ ดังนั้นหากฝั่ง host ของ mount เหล่านี้มีสิทธิ์การเข้าถึงที่ผู้ใช้ภายใน container ไม่สามารถเขียนข้อมูลลงไปได้ container จะเริ่มทำงานได้ตามปกติ แสดง UID ที่ถูกต้องใน banner แต่จะล้มเหลวทันทีเมื่อพยายามนำเข้าข้อมูลครั้งแรก
นี่คือพฤติกรรมที่ถูกต้องแล้ว การสั่ง chown แบบ recursive บนคลังสื่อขนาด 12 เทราไบต์ทุกครั้งที่ container เริ่มทำงานจะเป็นหายนะของระบบ อย่างไรก็ตาม นั่นหมายความว่าไดเรกทอรีสื่อบันทึกข้อมูลเป็นหน้าที่ของคุณในการจัดการ และเป็นจุดที่มักเกิดปัญหาเรื่องสิทธิ์การเข้าถึงจริง ๆ
สามวิธีในการควบคุมผู้ใช้งานและสถานการณ์ที่ควรใช้แต่ละวิธี
ตัวแปรสภาพแวดล้อม PUID และ PGID
วิธีนี้ใช้ได้เฉพาะกับอิมเมจที่ entrypoint รองรับการอ่านค่าตัวแปรเหล่านี้เท่านั้น วิธีนี้เป็นที่นิยมเนื่องจากคอนเทนเนอร์ยังคงเริ่มทำงานด้วยสิทธิ์ root เพื่อตั้งค่าต่างๆ แก้ไข /config และลดสิทธิ์ลงในภายหลัง ทำให้ Docker Mods และสคริปต์ init แบบกำหนดเองยังคงทำงานได้ตามปกติ ข้อเสียคือคุณต้องเชื่อมั่นในธรรมเนียมปฏิบัติมากกว่าฟีเจอร์ของแพลตฟอร์ม และชื่อตัวแปรเหล่านี้ไม่มีมาตรฐานเดียวกันในทุกโปรเจกต์
คีย์ user: ใน Compose
นี่คือฟีเจอร์จริงของ Docker และใช้งานได้กับทุกอิมเมจ เนื่องจาก container runtime จะนำไปใช้ก่อนที่โค้ดของอิมเมจจะเริ่มทำงาน:
services:
sonarr:
image: lscr.io/linuxserver/sonarr:latest
user: "1000:1000"กระบวนการทำงานจะไม่รันด้วยสิทธิ์ root แม้แต่ช่วงเวลาเดียว ซึ่งถือเป็นการเพิ่มความปลอดภัยอย่างแท้จริง แต่จะทำให้สิ่งที่อยู่ใน entrypoint ซึ่งจำเป็นต้องใช้สิทธิ์ root ไม่สามารถทำงานได้ สำหรับอิมเมจจาก linuxserver ทางโปรเจกต์รองรับวิธีนี้ตามความเหมาะสมและเฉพาะอิมเมจที่ผ่านการทดสอบแล้วเท่านั้น โดยมีข้อควรระวังคือ PUID และ PGID จะไม่มีผล Docker Mods จะไม่ทำงาน บริการที่กำหนดเองจะไม่ทำงาน และคุณต้องรับผิดชอบสิทธิ์การเข้าถึงในทุก volume ที่ mount ไว้ รูปแบบที่แนะนำคือการใช้แฟล็กนี้คู่กับ /run ที่เขียนข้อมูลได้:
user: 1000:1000
tmpfs:
- /run:uid=1000,gid=1000,exec
security_opt:
- no-new-privileges=trueผลข้างเคียงด้านการแสดงผลที่ทำให้ผู้ใช้ประหลาดใจคือ user: ที่เป็นตัวเลขจะไม่มีรายการที่ตรงกันใน /etc/passwd ของคอนเทนเนอร์ ทำให้เครื่องมือภายในรายงานว่าเป็น whoami: cannot find name for user ID 1000 ทั้งที่ ID นั้นถูกต้องและสามารถเข้าถึงไฟล์ได้ตามปกติ เพียงแต่ไม่สามารถระบุชื่อผู้ใช้ได้เท่านั้น
Rootless Docker
Rootless Docker จะรัน daemon ด้วยสิทธิ์ของผู้ใช้ทั่วไปของคุณ ดังนั้นจึงไม่มีกระบวนการใดบนเครื่องที่รันด้วยสิทธิ์ root จริง วิธีนี้เปลี่ยนหลักการคำนวณความเป็นเจ้าของไฟล์โดยสิ้นเชิง โดย UID 0 ในคอนเทนเนอร์จะถูก map ไปยัง UID ของผู้ใช้ที่รัน Rootless Docker บนโฮสต์ ส่วน UID n ในคอนเทนเนอร์สำหรับ n ที่มีค่าตั้งแต่ 1 ขึ้นไป จะถูก map ไปยัง subuid + (n - 1) โดยที่ subuid คือค่าเริ่มต้นของช่วง ID ที่ถูกจัดสรรให้คุณใน /etc/subuid และ /etc/subgid ซึ่ง Docker ต้องการ ID ย่อยอย่างน้อย 65,536 รายการในส่วนนี้
โปรดทำความเข้าใจการ map นี้อีกครั้ง เพราะมันกลับกันกับคำแนะนำทั่วไป ใน Rootless Docker คอนเทนเนอร์ที่เขียนไฟล์ด้วยสิทธิ์ root จะสร้างไฟล์ที่เป็นของคุณ แต่ถ้าคอนเทนเนอร์เขียนด้วย UID 1000 ไฟล์นั้นจะเป็นของ ID ย่อยที่อยู่ประมาณ 100999 ซึ่ง shell ของคุณจะไม่สามารถจัดการได้ ดังนั้นค่า PUID ที่ถูกต้องบน rootful daemon จึงเป็นค่าที่ผิดในกรณีนี้ กลไกทั้งสองแก้ปัญหาเดียวกันในระดับที่ต่างกัน การนำมาใช้ร่วมกันโดยไม่ตรวจสอบจะทำให้เกิดไดเรกทอรีที่คุณต้องใช้ sudo เพื่อลบออก หากคุณเลือกใช้ Rootless ให้ทดสอบความเป็นเจ้าของไฟล์ที่เขียนขึ้นบนเซิร์ฟเวอร์ของคุณก่อนที่จะย้ายข้อมูลทั้งหมดเข้าไป
สำหรับ stack ส่วนใหญ่ที่ self-host บน VPS เดียว การใช้ PUID และ PGID บน rootful daemon เป็นทางเลือกที่ใช้งานได้จริง เพราะเป็นสิ่งที่อิมเมจส่วนใหญ่ถูกสร้างและจัดทำเอกสารมาให้รองรับ ให้เลือกใช้ user: เมื่อ README ของอิมเมจระบุว่าผ่านการทดสอบแล้ว หรือเมื่อคุณรันอิมเมจต้นฉบับที่เป็นทางการซึ่งไม่มีการรองรับ PUID เลย ตัวอย่างเช่น การติดตั้ง AFFiNE แบบ self-hosted บน VPS เดียว จัดอยู่ในกรณีหลัง เนื่องจากไม่มีคอนเทนเนอร์ใดอ่านค่า PUID และความเป็นเจ้าของไดเรกทอรีฐานข้อมูลรวมถึงไฟล์ที่อัปโหลดจะถูกกำหนดโดย runtime แทนที่จะเป็นค่าใน environment block
กรณีศึกษา media stack: การใช้กลุ่มผู้ใช้ร่วมกันระหว่างคอนเทนเนอร์
arr media stack ที่ประกอบด้วย Sonarr, Radarr และโปรแกรมดาวน์โหลด เป็นตัวอย่างที่ทำให้เรื่องนี้ไม่ใช่แค่ทฤษฎี โปรแกรมดาวน์โหลดจะเขียนไฟล์ที่เสร็จสมบูรณ์ลงใน /data/downloads จากนั้น Sonarr จะทำการ hardlink หรือย้ายไฟล์นั้นไปยัง /data/media เพื่อให้การทำ hardlink สำเร็จ คอนเทนเนอร์ทั้งสองต้องมีสิทธิ์เขียนในโครงสร้างไฟล์เดียวกัน หากโปรแกรมดาวน์โหลดรันด้วย UID 1000 ในขณะที่ Sonarr รันด้วย 1001 ฝ่ายหนึ่งจะเป็นเจ้าของไฟล์ที่อีกฝ่ายอ่านได้อย่างเดียว
วิธีแก้ไขคือการใช้กลุ่มผู้ใช้ร่วมกัน (shared group) ซึ่งคอนเทนเนอร์ทุกตัวใน stack จะใช้เป็น PGID ของตน:
sudo groupadd -g 13000 media
sudo usermod -aG media deploy
sudo chown -R deploy:media /srv/media
sudo find /srv/media -type d -exec chmod 2775 {} +
sudo find /srv/media -type f -exec chmod 0664 {} +2 ที่อยู่หน้า 2775 คือ setgid bit สำหรับไดเรกทอรี หมายความว่าไฟล์และไดเรกทอรีย่อยใหม่ทุกรายการที่ถูกสร้างขึ้นภายในจะสืบทอดกลุ่ม media แทนที่จะเป็นกลุ่มหลักของผู้สร้าง ดังนั้นการตั้งค่านี้จึงคงอยู่กับการดาวน์โหลดใหม่โดยที่คุณไม่ต้องรัน chown ซ้ำ ให้คุณออกจากระบบแล้วล็อกอินใหม่ หรือรัน newgrp media ก่อนตรวจสอบสิทธิ์การเข้าถึงของคุณเอง เนื่องจากกลุ่มที่เพิ่มด้วย usermod -aG จะไม่ปรากฏในเซสชัน shell ที่เปิดค้างไว้
ภายในคอนเทนเนอร์ groupmod -o -g 13000 abc จะเปลี่ยนหมายเลขกลุ่ม abc เป็น 13000 เพื่อให้ abc เขียนไฟล์ด้วย GID เดียวกับกลุ่ม media บนโฮสต์ของคุณ คอนเทนเนอร์ทุกตัวใน stack จะคง PUID ของตนเองไว้และใช้ PGID เดียวกันนี้ร่วมกัน
จากนั้นตั้งค่า UMASK=002 บนคอนเทนเนอร์ linuxserver ทุกตัวใน stack นี่คือขั้นตอนที่คนมักมองข้าม ค่าเริ่มต้นในอิมเมจเหล่านี้คือ UMASK=022 ซึ่งจะตัดสิทธิ์การเขียนของกลุ่มออกจากไฟล์ใหม่ทุกไฟล์ ทำให้ไฟล์ที่ได้มีสิทธิ์เป็น 0644 และการแชร์ที่คุณตั้งค่าไว้จะไม่มีผล 002 จะทำให้ได้ไฟล์ที่มีสิทธิ์ 0664 และไดเรกทอรีที่มีสิทธิ์ 0775 ซึ่งกลุ่มสามารถเขียนได้:
services:
sonarr:
image: lscr.io/linuxserver/sonarr:latest
container_name: sonarr
environment:
- PUID=${PUID}
- PGID=${PGID}
- UMASK=002
- TZ=Etc/UTC
volumes:
- /srv/appdata/sonarr:/config
- /srv/media:/data
restart: unless-stoppedค่าทั้งสองนี้ควรอยู่ในไฟล์ .env ที่วางอยู่ข้างไฟล์ Compose เพื่อให้ทั้ง stack อ่านนิยามเดียวกัน:
PUID=1000
PGID=13000Compose จะอ่านไฟล์นั้นโดยอัตโนมัติสำหรับการแทนที่ค่าแบบ ${PUID} ซึ่งเป็นกลไกเดียวกับที่คุณใช้สำหรับข้อมูลรับรอง (credentials) นิสัยในการ แยกค่าต่างๆ ออกจาก docker-compose.yml ไปไว้ในไฟล์ .env สามารถนำมาใช้ในกรณีนี้ได้เช่นกัน โดยมีความแตกต่างตรงที่ตัวเลขสองค่านี้ไม่ใช่ความลับ
ตรวจสอบผลลัพธ์ตั้งแต่ต้นจนจบแทนที่จะเชื่อการตั้งค่าเพียงอย่างเดียว ให้ลองเขียนไฟล์จากภายในคอนเทนเนอร์หนึ่งตัวแล้วอ่านจากโฮสต์:
docker exec sonarr touch /data/downloads/permtest
ls -ln /srv/media/downloads/permtestผลลัพธ์ที่ถูกต้องจะแสดง PUID ของคุณเป็นเจ้าของ, 13000 เป็นกลุ่ม และ -rw-rw-r-- เป็นโหมด หากกลุ่มแสดงผลเป็น 1000 แสดงว่า setgid bit หายไปจากไดเรกทอรีนั้น หากโหมดแสดงผลเป็น -rw-r--r-- แสดงว่าตัวแปร UMASK ไม่มีผล ให้ตรวจสอบว่าคุณได้สร้างคอนเทนเนอร์ใหม่ (recreate) แทนการรีสตาร์ทเพียงอย่างเดียว ลบไฟล์ทดสอบด้วย rm /srv/media/downloads/permtest เมื่อเสร็จสิ้น
อิมเมจใดใช้ตัวแปรใดบ้าง
อิมเมจจาก linuxserver.io ใช้ PUID, PGID และ UMASK ส่วน Paperless-ngx ใช้ชื่อเรียกอื่นสำหรับแนวคิดเดียวกันคือ USERMAP_UID และ USERMAP_GID ซึ่งทั้งคู่มีค่าเริ่มต้นเป็น 1000 และเอกสารประกอบระบุให้คุณอ่านค่าจาก id -u และ id -g สำหรับเซิร์ฟเวอร์รูปภาพก็มีความหลากหลายเช่นกัน โดย PhotoPrism มีคู่ PHOTOPRISM_UID และ PHOTOPRISM_GID เป็นของตนเอง ในขณะที่ Immich ไม่ได้จัดเตรียมตัวแปรที่เทียบเท่ากันไว้ และปล่อยให้ผู้ใช้คอนเทนเนอร์จัดการผ่านคีย์ user: ของ Docker ดังนั้นการ เลือกระหว่าง PhotoPrism กับ Immich จึงเป็นการตัดสินใจด้วยว่าคุณจะต้องดูแลกลไกใดในบรรดากลไกเหล่านี้สำหรับคลังข้อมูลขนาดใหญ่ที่สุดบนเซิร์ฟเวอร์ของคุณ อิมเมจต้นทางที่เป็นทางการหลายรายการ รวมถึงอิมเมจฐานข้อมูลและเว็บเซิร์ฟเวอร์ทั่วไป จะกำหนดผู้ใช้แบบตายตัวมาให้ภายในและคาดหวังให้คุณใช้ user: หรือปล่อยไว้ตามค่าเริ่มต้น กรณีเดียวกันนี้ยังใช้กับโครงสร้างพื้นฐานที่คุณติดตั้งเพิ่มในภายหลัง ดังนั้นการ ใช้ Authentik หน้าแอปพลิเคชันของคุณเพื่อทำ Single Sign-On หมายถึงการรันอิมเมจที่เป็นทางการของเซิร์ฟเวอร์, Postgres และ Redis ซึ่งไม่ได้อ่านค่า PUID เลย และความเป็นเจ้าของไฟล์ใน volume จะมาจาก runtime แทนที่จะมาจาก entrypoint ที่คุณกำหนดค่าได้
ดังนั้น โปรดตรวจสอบไฟล์ README ของแต่ละอิมเมจก่อนที่คุณจะคัดลอกบล็อก environment ไปใช้ระหว่างโปรเจกต์ Docker จะส่งผ่านตัวแปร environment ใดก็ตามที่คุณตั้งค่าเข้าไปในคอนเทนเนอร์ ไม่ว่าจะมีสิ่งใดภายในนั้นอ่านค่าหรือไม่ก็ตาม และการใส่ PUID ที่ไม่มีกระบวนการใดเรียกใช้จะไม่ทำให้เกิดข้อผิดพลาด คำเตือน หรือผลลัพธ์ใดๆ คอนเทนเนอร์จะรันด้วยผู้ใช้ที่ Dockerfile ของมันกำหนดไว้เป็นลำดับสุดท้าย และคุณจะทราบได้จากความเป็นเจ้าของไฟล์ที่คอนเทนเนอร์นั้นเขียนขึ้นมา
FAQ
ทำไมไฟล์ Docker ของฉันถึงมีเจ้าของเป็น 911:911?
911 คือ UID และ GID ของผู้ใช้ abc ที่ถูกสร้างไว้ในอิมเมจของ linuxserver.io การที่เห็นเลขนี้หมายความว่าคอนเทนเนอร์เริ่มทำงานโดยไม่ได้ตั้งค่า PUID และ PGID ระบบจึงใช้ค่าเริ่มต้นที่ฝังมากับอิมเมจ ls -l แสดงเป็นตัวเลขดิบเพราะไม่มีบัญชีผู้ใช้บนโฮสต์ของคุณที่มี ID เป็น 911 จึงไม่มีชื่อมาแสดงผล ให้ตั้งค่า PUID และ PGID เป็นผลลัพธ์จากคำสั่ง id จากนั้นสร้างคอนเทนเนอร์ใหม่ด้วย docker compose up -d แล้วแก้ไขสิทธิ์ของไฟล์ที่มีอยู่ด้วย sudo chown -R 1000:1000 ในไดเรกทอรีที่ได้รับผลกระทบ
PUID และ PGID ใช้ได้กับทุก Docker image หรือไม่?
ไม่ได้ เพราะสิ่งเหล่านี้ไม่ใช่ฟีเจอร์ของ Docker และ Docker ไม่เคยอ่านค่าดังกล่าว มันจะทำงานได้เฉพาะกับอิมเมจที่มี entrypoint อ่านค่าเหล่านี้แล้วเรียกใช้ usermod และ groupmod ก่อนเริ่มแอปพลิเคชันเท่านั้น ซึ่งได้แก่กลุ่มอิมเมจของ linuxserver.io และบางโปรเจกต์ที่คัดลอกรูปแบบนี้ไปใช้ โปรเจกต์อื่นอาจใช้ชื่อเรียกต่างออกไป เช่น USERMAP_UID และ USERMAP_GID ใน paperless-ngx สำหรับอิมเมจที่ไม่รองรับทั้งสองแบบ ตัวแปรเหล่านี้จะถูกรับค่าไว้แต่จะถูกละเลยโดยไม่มีการแจ้งเตือน
ฉันควรใช้ PUID และ PGID หรือคีย์ user: ใน Docker Compose?
ให้ใช้ PUID และ PGID เมื่ออิมเมจรองรับ เพราะ entrypoint จะยังคงรันในฐานะ root นานพอที่จะแก้ไข /config และเริ่มบริการของตัวเองได้อย่างถูกต้อง ให้ใช้ user: เมื่ออิมเมจไม่รองรับ PUID หรือเมื่อ README ของอิมเมจระบุว่าผ่านการทดสอบสำหรับการรันแบบ non-root แล้ว ในอิมเมจของ linuxserver การตั้งค่า user: จะทำให้ PUID และ PGID ไม่มีผล, หยุดการทำงานของ Docker Mods และบริการเสริมต่างๆ รวมถึงทำให้คุณต้องรับผิดชอบสิทธิ์ของทุก volume ที่ mount เข้ามาด้วยตนเอง
Sonarr มี PUID ที่ถูกต้องแล้วแต่ยังย้ายไฟล์ไม่ได้ เกิดจากอะไร?
ให้ตรวจสอบสามสิ่งตามลำดับ ดังนี้: ประการแรก คือตัว media mount เอง: สคริปต์เริ่มต้นจะทำ chown เฉพาะ /app, /config และ /defaults เท่านั้น ดังนั้น /data หรือ /downloads จะยังคงสิทธิ์เดิมที่มีอยู่บนโฮสต์ ประการที่สอง คือกลุ่มผู้ใช้ร่วม: หากโปรแกรมดาวน์โหลดและ Sonarr รันภายใต้ GID ที่ต่างกัน ทั้งสองจะไม่สามารถแก้ไขไฟล์ของกันและกันได้ ดังนั้นให้กำหนด PGID เดียวกันให้กับทุกคอนเทนเนอร์ใน stack ประการที่สาม คือ umask: ค่าเริ่มต้นของอิมเมจ UMASK=022 จะเขียนไฟล์เป็น 0644 โดยไม่มีสิทธิ์การเขียนของกลุ่ม ซึ่งทำให้การใช้กลุ่มร่วมกันไม่ได้ผล ให้ตั้งค่า UMASK=002 และตั้งค่า setgid bit บนไดเรกทอรีด้วย chmod 2775 เพื่อให้ไฟล์ใหม่สืบทอดกลุ่มของผู้ปกครองมาด้วย