วิธีติดตั้ง Docker บน Rocky Linux และ AlmaLinux
ติดตั้ง Docker Engine บน Rocky Linux และ AlmaLinux ด้วย dnf พร้อมวิธีแก้ปัญหา Podman ทับคำสั่ง docker และการตั้งค่า SELinux สำหรับ bind mounts ที่มักถูกมองข้ามในคู่มือทั่วไป
การติดตั้ง Docker บน Rocky Linux และ AlmaLinux
ในการติดตั้ง Docker บน Rocky Linux หรือ AlmaLinux คุณต้องเพิ่ม dnf repository ของ Docker เอง ติดตั้ง engine พร้อมกับ compose plugin จากนั้นจึงเปิดใช้งาน service ขั้นตอนนี้ประกอบด้วย 4 คำสั่ง และเหมือนกันทั้งสอง distribution เนื่องจากทั้งคู่เป็น rebuild ของ Red Hat Enterprise Linux (RHEL) และใช้โครงสร้างแพ็กเกจเดียวกัน CentOS Stream ก็ใช้วิธีเดียวกันนี้ ทุกอย่างที่ระบุด้านล่างนี้ใช้ได้กับทั้งสองระบบ ดังนั้นหากคุณยังตัดสินใจเลือกไม่ได้ ปัจจัยในการตัดสินใจคือคำมั่นสัญญาเรื่องความเข้ากันได้ของแต่ละโปรเจกต์ และการรองรับ CPU รุ่นเก่าของคุณ
การติดตั้งนั้นสั้นมาก เนื้อหาส่วนใหญ่ของคู่มือนี้จึงครอบคลุมสิ่งที่ Enterprise Linux (EL) ทำต่างจาก Ubuntu Podman อาจยึดครองคำสั่ง docker บน image ของคุณไปแล้ว SELinux จะบล็อกไฟล์ที่ทำ bind-mount จนกว่าไฟล์เหล่านั้นจะมี label ที่ถูกต้อง Firewalld ไม่กรองพอร์ตที่ Docker เปิดใช้งาน ดังนั้นพอร์ตของ container อาจเปิดสู่สาธารณะในขณะที่ firewall-cmd รายงานว่าไม่มีพอร์ตใดเปิดอยู่
ห้ามใช้สคริปต์อำนวยความสะดวกจาก get.docker.com เอกสารของ Docker เองระบุว่าไม่แนะนำให้ใช้ในสภาพแวดล้อม production สคริปต์ดังกล่าวจะเขียนทับการตั้งค่า repository ของคุณโดยไม่แจ้งเตือน และไม่สามารถรันซ้ำเพื่ออัปเกรดได้อย่างปลอดภัย การเพิ่ม repository ด้วยตนเองจะทำให้ dnf upgrade จัดการ Docker เหมือนกับแพ็กเกจอื่น ๆ ในเครื่อง ซึ่งจะทำให้ engine อยู่ในขอบเขตของ dnf-automatic หากคุณตั้งค่าให้ใช้การอัปเดตความปลอดภัยตามเวลา ดังนั้นให้ตัดสินใจตั้งแต่เนิ่นๆ ว่าคุณต้องการให้ Docker ได้รับการแพตช์โดยอัตโนมัติหรือต้องการเก็บไว้ในช่วงเวลาบำรุงรักษา ไม่ว่าจะกรณีใด การอัปเกรดจะแทนที่ binary ที่ติดตั้งไว้ในขณะที่ dockerd ตัวเดิมยังคงทำงานอยู่ และ needs-restarting คือคำสั่งที่จะบอกคุณว่า service ใดบ้างที่ยังคงรันโค้ดที่คุณเพิ่งแทนที่ไป
Podman กำลังตอบสนองคำสั่ง docker อยู่หรือไม่?
Rocky Linux และ AlmaLinux มี podman มาให้ใน repository เริ่มต้น และ VPS หลายแห่งก็ติดตั้งมาให้ล่วงหน้า บาง image อาจติดตั้ง podman-docker เพิ่มเติม ซึ่งจะวาง shell script ไว้ที่ /usr/bin/docker เพื่อเรียกใช้งาน podman ส่งผลให้ทุกคำสั่ง docker ที่คุณพิมพ์จะกลายเป็นการรัน podman แทน ทำให้ผลลัพธ์ที่ได้จากคู่มือที่เขียนสำหรับ Docker ไม่เป็นไปตามที่คาดหวัง
สัญญาณแรกที่สังเกตได้คือข้อความแจ้งเตือน (banner) โดย script /usr/bin/docker จะตรวจสอบไฟล์ /etc/containers/nodocker และหากไฟล์ดังกล่าวไม่มีอยู่ มันจะแสดงข้อความหนึ่งบรรทัดก่อนเริ่มทำงาน:
Emulate Docker CLI using podman. Create /etc/containers/nodocker to quiet msg.อาจมีบางคนสร้างไฟล์นั้นขึ้นมาเพื่อปิดการแสดงข้อความแจ้งเตือน ดังนั้นอย่าพึ่งพาเพียงวิธีนี้วิธีเดียว ให้สอบถามฐานข้อมูล package ว่า package ใดเป็นเจ้าของ binary ดังกล่าว:
command -v docker
rpm -qf "$(command -v docker)"หากคำตอบขึ้นต้นด้วย podman-docker หมายความว่า podman กำลังตอบสนองคำสั่งอยู่ หากขึ้นต้นด้วย docker-ce-cli หมายความว่าเป็น Docker ของจริง แต่ถ้า rpm -qf รายงานว่าไม่มี package ใดเป็นเจ้าของไฟล์ แสดงว่ามีคนติดตั้งด้วยตนเอง คุณควรตรวจสอบ script นั้นก่อนที่จะเชื่อถือ
Podman สามารถรัน OCI image เดียวกันได้และเป็นตัวเลือกที่สมเหตุสมผล หากคุณต้องการใช้งาน podman ก็สามารถหยุดที่ขั้นตอนนี้ได้ ทั้งสองเป็น container engine บน Linux เหมือนกัน ดังนั้นหากคุณยังตัดสินใจเลือก platform ไม่ได้ ควรทราบว่า FreeBSD jails จะแยกส่วน userland ทั้งหมดแทนการรัน layered image ที่ดึงมาจาก registry หากคุณต้องการ Docker Engine ให้ลบ package ที่ขัดแย้งกันออกก่อน นี่คือรายการที่ Docker ระบุไว้สำหรับ RHEL:
sudo dnf remove docker docker-client docker-client-latest docker-common \
docker-latest docker-latest-logrotate docker-logrotate docker-engine podman runcโปรดอ่านสิ่งที่ dnf วางแผนจะลบออกก่อนที่คุณจะยืนยัน ใน VPS image ที่เพิ่งติดตั้งใหม่ รายการนี้จะสั้น แต่ในเครื่องที่มีการใช้งานมาแล้ว การลบ podman อาจดึงเอา cockpit-podman หรือเครื่องมืออื่นที่ขึ้นต่อกันออกไปด้วย
ในทางทฤษฎี คุณสามารถเก็บ podman ไว้คู่กับ Docker ได้ โดยลบเพียง podman-docker เพื่อให้ชื่อ docker ว่างลง และลบ runc ซึ่ง package containerd.io จะเข้ามาแทนที่ อย่างไรก็ตาม เอกสารของ Docker ระบุว่า podman เป็น package ที่ขัดแย้งกัน ดังนั้น Docker จึงไม่รองรับการติดตั้งรูปแบบนี้ หากการติดตั้งยังคงรายงานว่ามีความขัดแย้ง ให้ใช้รายการลบแบบเต็มตามที่ระบุไว้ข้างต้น
Add Docker's repository with dnf config-manager
Docker publishes RPMs for Enterprise Linux at download.docker.com. The repository file points at the CentOS tree, which is the one Rocky Linux and AlmaLinux resolve against. Pointing a Rocky box at a CentOS repository looks like a mistake until you know how both distributions grew out of the CentOS lineage after Red Hat turned CentOS into Stream in 2020. Checked in August 2026, Docker documents this repository for CentOS Stream 9 and CentOS Stream 10.
sudo dnf -y install dnf-plugins-core
sudo dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repoVersion 5 of dnf dropped the --add-repo argument, so that second command fails on newer releases. Check which one you have, then pick the matching form:
dnf --versionIf it prints a 5.x version, use the subcommand form instead:
sudo dnf config-manager addrepo --from-repofile=https://download.docker.com/linux/centos/docker-ce.repoBoth write the same file to /etc/yum.repos.d/docker-ce.repo. The wrong form fails with an unknown-argument error rather than doing something silently wrong, so you will not miss it.
That repo file sets baseurl to a path containing $releasever, and dnf expands that variable from your release package. Rocky Linux and AlmaLinux set it to the major version number, so 9 on EL 9 and 10 on EL 10, which is why a CentOS repository resolves correctly on a Rocky box. Confirm the expansion before you install:
sudo dnf repoinfo docker-ce-stableRead the Repo-baseurl line. It should end in /9/x86_64/stable or /10/x86_64/stable. If your release sets $releasever to a point version such as 9.6, dnf reports Status code: 404 for that URL when it fetches metadata. Fix it by editing /etc/yum.repos.d/docker-ce.repo and replacing $releasever with the bare major number.
ติดตั้ง engine และ compose plugin
sudo dnf install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-pluginมีแพ็กเกจทั้งหมด 5 รายการ โดยแต่ละรายการมีหน้าที่เฉพาะตัว docker-ce คือ daemon หลักของระบบ dockerd ส่วน docker-ce-cli คือคำสั่ง docker ที่คุณใช้พิมพ์เรียกใช้งาน containerd.io คือ container runtime ที่ daemon ใช้ขับเคลื่อนการทำงาน docker-buildx-plugin ทำหน้าที่สร้าง image และ docker-compose-plugin จัดเตรียมคำสั่งย่อย docker compose ไว้ให้ใช้งาน
แพ็กเกจเหล่านี้ไม่ได้ติดตั้งไบนารี docker-compose ที่มีเครื่องหมายขีดคั่น ซึ่งนั่นคือ Compose v1 ที่สิ้นสุดระยะเวลาสนับสนุนไปแล้วเมื่อเดือนกรกฎาคม 2023 ดังนั้น หากมีการเรียกใช้งาน docker-compose โดยใช้เครื่องหมายขีดคั่น จำเป็นต้องปรับปรุงให้เป็น docker compose โดยใช้การเว้นวรรคแทน
การติดตั้งครั้งแรกจะหยุดเพื่อนำเข้า signing key ของ Docker และแสดง fingerprint ให้คุณเห็น เนื่องจาก key ดังกล่าวมาจาก gpgkey=https://download.docker.com/linux/centos/gpg ในไฟล์ repo ที่คุณเพิ่งเพิ่มเข้าไป คุณควรตรวจสอบ fingerprint ที่ dnf แสดงผลเทียบกับ URL นั้นก่อนที่จะกดยอมรับ
มีปัญหาหนึ่งที่พบได้บ่อยจนควรกล่าวถึง หาก dnf แจ้งว่า containerd.io ต้องการ container-selinux แต่ไม่มีแพ็กเกจใดจัดเตรียมให้ แสดงว่า repository ของ AppStream ของคุณถูกปิดใช้งานอยู่ ให้รันคำสั่ง dnf repolist และตรวจสอบว่ามี appstream อยู่ในรายการหรือไม่ เพราะนั่นคือแหล่งที่มาของ container-selinux บน EL 9 และ EL 10
เริ่มการทำงานของ Docker และยืนยันสถานะ
sudo systemctl enable --now docker
sudo systemctl status docker --no-pager
sudo docker run --rm hello-worldแพ็กเกจ RPM ของ Docker จะคงสถานะ daemon ไว้ในรูปแบบหยุดทำงานและปิดการใช้งานหลังการติดตั้ง นี่คือเหตุผลที่ขั้นตอนนี้ปรากฏในหน้าเอกสารของ Docker สำหรับ CentOS แต่ไม่ปรากฏในหน้าของ Ubuntu ซึ่งตัว deb จะเริ่ม service ให้โดยอัตโนมัติ หากข้ามขั้นตอน enable ไป Docker จะทำงานได้จนกว่าจะมีการรีบูตครั้งถัดไป จากนั้นมันจะหยุดทำงานและทำให้ container ทั้งหมดหยุดทำงานตามไปด้วย
systemctl status ควรแสดงผลเป็น Active: active (running) ส่วน container hello-world ควรแสดงข้อความ This message shows that your installation appears to be working correctly. แล้วจบการทำงาน หากระบบแสดงข้อความแจ้งเตือนสิทธิ์การเข้าถึง (permission error) ที่ /var/run/docker.sock แสดงว่าคุณไม่ได้ดำเนินการ sudo ซึ่งหัวข้อกลุ่ม docker ด้านล่างจะช่วยแก้ไขปัญหานี้
ให้ตรวจสอบ docker compose plugin แยกต่างหาก เนื่องจากเป็นแพ็กเกจที่แตกต่างกันและอาจขาดหายไปในขณะที่ตัว engine ทำงานได้ตามปกติ:
docker compose versionผลลัพธ์ที่แสดงว่าระบบทำงานปกติจะมีลักษณะเหมือน Docker Compose version v2.x.x การทำให้บริการของคุณกลับมาทำงานหลังการรีบูตเป็นคนละประเด็นกับการเปิดใช้งาน daemon โดย นโยบายการรีสตาร์ทจะเป็นตัวกำหนดว่าบริการ Compose จะกลับมาทำงานเมื่อบูตเครื่องหรือไม่
เหตุใด bind mount จึงแจ้งว่า permission denied?
Rocky Linux และ AlmaLinux เปิดใช้งาน SELinux (Security-Enhanced Linux) ในโหมด enforcing เป็นค่าเริ่มต้น ให้ตรวจสอบด้วยคำสั่ง getenforce ซึ่งจะแสดงผลเป็น Enforcing
Docker container ทำงานภายใต้ SELinux type ที่ชื่อ container_t และ type ดังกล่าวอาจมีสิทธิ์อ่านและเขียนไฟล์ที่ถูกระบุ label เป็น container_file_t เท่านั้น ไดเรกทอรีที่คุณสร้างบนโฮสต์จะมี label ตามพาธแม่ ซึ่งไม่ใช่ container_file_t ส่งผลให้ container ถูกปฏิเสธการเข้าถึงแม้ว่าเจ้าของ กลุ่ม และโหมดไฟล์จะดูถูกต้องจากฝั่งโฮสต์ก็ตาม คุณสามารถจำลองเหตุการณ์นี้ได้ด้วย 3 คำสั่ง:
sudo mkdir -p /srv/site
echo hello | sudo tee /srv/site/index.html
sudo docker run --rm -v /srv/site:/usr/share/nginx/html:ro nginx:alpine cat /usr/share/nginx/html/index.htmlContainer จะแสดงข้อความ:
cat: can't open '/usr/share/nginx/html/index.html': Permission deniedคำสั่งสองคำสั่งจะแสดงสาเหตุให้คุณเห็น ls -ldZ /srv/site จะแสดง label ซึ่งสำหรับพาธภายใต้ /srv จะเป็น system_u:object_r:var_t:s0 ไม่ใช่ container_file_t จากนั้น sudo ausearch -m avc -ts recent จะแสดงบันทึก audit ของ kernel ซึ่งประกอบด้วย avc: denied { read }, ฟิลด์ scontext= ที่ระบุ container_t และฟิลด์ tcontext= ที่ระบุ label ที่คุณเพิ่งเห็นบนไดเรกทอรี ความไม่สอดคล้องกันระหว่างสองฟิลด์นี้คือสาเหตุทั้งหมดของปัญหา
วิธีแก้ไขคือการเพิ่ม suffix ต่อท้ายอาร์กิวเมนต์ volume เพื่อให้ Docker ทำการเปลี่ยน label ของพาธนั้นให้คุณ:
sudo docker run --rm -v /srv/site:/usr/share/nginx/html:ro,z nginx:alpine cat /usr/share/nginx/html/index.html:z แบบตัวพิมพ์เล็กจะเปลี่ยน label ของเนื้อหาให้เป็นแบบ shared เพื่อให้หลาย container สามารถใช้ไดเรกทอรีเดียวกันได้ ส่วน :Z แบบตัวพิมพ์ใหญ่จะเปลี่ยน label ให้เป็นแบบ private และไม่แชร์ ซึ่งผูกติดกับ container เดียว หาก container ที่สองพยายามอ่านพาธเดียวกันจะถูกปฏิเสธ ให้ใช้ :z สำหรับสิ่งที่ sidecar หรือ container สำรองข้อมูลต้องเข้าถึง และใช้ :Z สำหรับไดเรกทอรีฐานข้อมูลที่ container เดียวเป็นเจ้าของ
เอกสารของ Docker มีคำเตือนที่ควรย้ำเตือนไว้ เนื่องจากกระบวนการเปลี่ยน label จะทำงานแบบ recursive การทำ bind-mount ไดเรกทอรีระบบ เช่น /home หรือ /usr โดยใช้ :Z จะ "ทำให้เครื่องโฮสต์ของคุณใช้งานไม่ได้ และคุณอาจต้องแก้ไข label ของไฟล์บนเครื่องโฮสต์ด้วยตนเอง" ให้ใช้ suffix เหล่านี้กับไดเรกทอรีที่คุณสร้างขึ้นสำหรับ container เท่านั้น ห้ามใช้กับพาธของระบบโดยเด็ดขาด
ใน Compose ให้ใส่ suffix ต่อท้ายในสตริงเดียวกัน:
services:
web:
image: nginx:alpine
volumes:
- /srv/site:/usr/share/nginx/html:ro,zมีข้อจำกัดสองประการที่มักพบเจอได้ง่าย คือ flag --mount ไม่สามารถตั้งค่า SELinux label ได้เลย ดังนั้นให้ใช้ -v เมื่อคุณต้องการ label ส่วน named volume ไม่จำเป็นต้องใช้ suffix เพราะ Docker จะจัดการ label ให้ไดเรกทอรีที่สร้างขึ้นภายใต้ /var/lib/docker/volumes เองโดยอัตโนมัติ
ห้ามปิด SELinux ให้ใช้ sudo setenforce 0 เพื่อการทดสอบเพียงหนึ่งนาทีเท่านั้น หาก container ทำงานได้หลังจากนั้น แสดงว่าปัญหาเกิดจาก label และ :z คือคำตอบ ให้เปิดใช้งานกลับคืนด้วย sudo setenforce 1 ทันที บน Enterprise Linux ปัญหา permission denied บน bind mount มีสาเหตุแยกกันสองประการที่ดูเหมือนกันจากภายใน container ประการแรกคือ SELinux label ประการที่สองคือความเป็นเจ้าของไฟล์แบบตัวเลข user และ group ปกติ ซึ่งเป็น สิ่งที่ตัวแปร PUID และ PGID มีไว้เพื่อแก้ไข คำสั่ง ls -lnZ จะแสดงโหมด, เจ้าของที่เป็นตัวเลข และ label ในบรรทัดเดียว เพื่อให้คุณทราบว่ากำลังเผชิญกับปัญหาใดอยู่
เหตุใดพอร์ตที่เผยแพร่ไว้จึงเข้าถึงได้ทั้งที่ firewalld ดูเหมือนปิดอยู่
Firewalld เป็นไฟร์วอลล์เริ่มต้นบน Rocky Linux และ AlmaLinux ให้ตรวจสอบว่าไฟร์วอลล์ทำงานอยู่หรือไม่ด้วย sudo systemctl is-active firewalld หากคุณยังไม่ได้กำหนดค่าบนเครื่องนี้ การเปิด SSH และพอร์ตเว็บด้วย firewalld คือขั้นตอนแรกที่ต้องทำ เพราะสิ่งที่น่าประหลาดใจด้านล่างนี้จะเข้าใจได้ก็ต่อเมื่อคุณมีชุดกฎของโซนที่ใช้งานได้จริงเพื่อนำมาเปรียบเทียบเท่านั้น ตอนนี้ให้ลองเผยแพร่พอร์ตและดูว่า firewalld รายงานว่าพอร์ตใดเปิดอยู่บ้าง:
sudo docker run -d --name web -p 8080:80 nginx:alpine
sudo firewall-cmd --list-portsfirewall-cmd จะแสดงผลเป็นบรรทัดว่าง จากเครื่องอื่น curl -I http://YOUR_SERVER_IP:8080/ จะส่งค่ากลับมาเป็น HTTP/1.1 200 OK พอร์ตดังกล่าวเปิดสู่สาธารณะแล้ว แต่ไฟร์วอลล์ของคุณกลับไม่รายงานสิ่งใดเลย
สาเหตุเกิดจากเส้นทางที่แพ็กเก็ตเดินทาง กฎของโซนใน firewalld จะกรองเฉพาะทราฟฟิกที่มุ่งหน้ามายังตัวโฮสต์เอง แต่พอร์ตที่เผยแพร่ไม่ได้มุ่งหน้ามาที่โฮสต์โดยตรง Docker จะติดตั้งกฎ destination NAT (network address translation) ซึ่งจะเขียนปลายทางใหม่ไปยังที่อยู่ของคอนเทนเนอร์ก่อนที่แพ็กเก็ตจะมาถึงเส้นทาง input ของโฮสต์ ดังนั้นเคอร์เนลจึงส่งต่อ (forward) แพ็กเก็ตแทนที่จะส่งไปยังเครื่องโดยตรง จากนั้น Docker จะวางอินเทอร์เฟซ bridge ไว้ในโซนของ firewalld ที่ชื่อ docker ซึ่งมีเป้าหมายเป็น ACCEPT และเพิ่มนโยบายการส่งต่อที่เรียกว่า docker-forwarding ซึ่งอนุญาตให้ส่งต่อจากโซนใดก็ได้ไปยังโซน docker กฎในโซนของคุณจึงไม่เคยตรวจพบแพ็กเก็ตเหล่านี้เลย
วิธีแก้ไขที่สะอาดที่สุดคือไม่ต้องใช้กฎไฟร์วอลล์ ให้ผูก (bind) ฝั่งโฮสต์ของการเผยแพร่ไว้กับ loopback แล้ววาง reverse proxy ไว้ด้านหน้าแทน:
sudo docker rm -f web
sudo docker run -d --name web -p 127.0.0.1:8080:80 nginx:alpine
curl -I http://127.0.0.1:8080/curl ในเครื่องจะส่งค่ากลับมาเป็น HTTP/1.1 200 OK และคำขอเดียวกันจากเครื่องอื่นจะไม่สามารถเชื่อมต่อได้อีกต่อไป สิ่งใดก็ตามที่ไม่มีที่อยู่โฮสต์ในอาร์กิวเมนต์ -p จะถูกเผยแพร่บนทุกอินเทอร์เฟซ ดังนั้นให้ถือว่าการใช้ -p 8080:80 เปล่าๆ เป็นการตัดสินใจเปิดเผยบริการนั้นสู่สาธารณะ
เมื่อคุณต้องการให้บริการเข้าถึงได้จากบางที่อยู่เท่านั้น Docker ได้เตรียม chain ไว้ให้คุณใช้งาน DOCKER-USER จะถูกประมวลผลก่อนกฎ accept ของ Docker เอง ดังนั้นกฎที่คุณใส่ไว้ในนั้นจะยังคงอยู่แม้ Docker จะรีสตาร์ทและเขียน chain ใหม่:
sudo iptables -I DOCKER-USER -i enp1s0 ! -s 203.0.113.10 -j DROP
sudo iptables -S DOCKER-USERให้ใช้ชื่ออินเทอร์เฟซจาก ip route show default แทนการคาดเดาว่าเป็น eth0 เนื่องจากอิมเมจ EL ปัจจุบันใช้ชื่ออย่าง enp1s0 หรือ ens3 บน Rocky และ AlmaLinux คำสั่ง iptables เป็นเลเยอร์ความเข้ากันได้ที่ครอบอยู่บน nftables และ chain ของ Docker ก็สามารถมองเห็นได้ผ่านคำสั่งนี้ กฎที่เพิ่มด้วยวิธีนี้จะหายไปหลังการรีบูตเว้นแต่คุณจะบันทึกไว้ ดังนั้นให้เขียนกฎเหล่านั้นลงใน systemd unit เมื่อคุณพอใจกับการตั้งค่าแล้ว
Docker Engine 28.0 ซึ่งปล่อยออกมาในปี 2025 ได้ปิดช่องโหว่ที่ใกล้เคียงกัน คือการเข้าถึงพอร์ตคอนเทนเนอร์โดยตรงผ่านการ routing ที่ไม่ได้เผยแพร่ไว้ ซึ่งปัจจุบันถูกบล็อกใน chain DOCKER แล้ว การเปลี่ยนแปลงนั้นไม่ส่งผลต่อพอร์ตที่เผยแพร่ไว้ ดังนั้นทุกอย่างข้างต้นยังคงใช้ได้กับเวอร์ชันปัจจุบัน นิสัยการทำงานอย่างหนึ่งที่ควรสร้างคือ หลังจากทำ sudo firewall-cmd --reload ทุกครั้ง ให้ทดสอบพอร์ตที่เผยแพร่อีกครั้ง หากพอร์ตหยุดตอบสนอง sudo systemctl restart docker จะช่วยติดตั้งกฎของ Docker ใหม่อีกครั้ง
ผู้ดูแลระบบ Ubuntu จะพบกำแพงเดียวกันนี้ผ่านเครื่องมือที่ต่างออกไป ซึ่งเป็นเหตุผลว่า ทำไมพอร์ต Docker ที่เผยแพร่จึงเพิกเฉยต่อกฎ ufw เส้นทาง NAT คือสาเหตุในทั้งสองกรณี ต่างกันเพียงแค่ไฟร์วอลล์ที่อยู่ด้านหน้าเท่านั้น
การเพิ่มผู้ใช้ที่ไม่ใช่ root เข้าสู่กลุ่ม docker
การพิมพ์ sudo นำหน้าทุกคำสั่ง docker เป็นเรื่องที่น่าเบื่อหน่าย และการเข้ากลุ่ม docker จะช่วยลดความจำเป็นนี้ลง:
sudo usermod -aG docker $USER
newgrp docker
docker run --rm hello-worldusermod -aG จะแก้ไขไฟล์ /etc/group แต่ shell ปัจจุบันของคุณมีรายการกลุ่มที่โหลดไว้แล้ว การเปลี่ยนแปลงนี้จึงยังไม่มีผลจนกว่าคุณจะเริ่ม shell ใหม่ newgrp docker จะเริ่ม shell ที่มีสิทธิ์ของกลุ่มนี้แนบมาด้วยเพื่อให้คุณทดสอบได้ทันที ส่วนการเชื่อมต่อ SSH ครั้งใหม่จะได้รับสิทธิ์นี้โดยอัตโนมัติ
โปรดทำความเข้าใจให้ชัดเจนว่ากลุ่มนี้ให้สิทธิ์อะไรบ้าง การเป็นสมาชิกกลุ่มจะให้สิทธิ์เขียนใน /var/run/docker.sock และสิ่งใดก็ตามที่สามารถสื่อสารกับ socket นั้นได้ สามารถสั่งให้ daemon เริ่ม container ที่ mount ระบบไฟล์ของโฮสต์ได้ คำสั่งเดียวต่อไปนี้แสดงให้เห็นว่านั่นหมายถึงอะไร:
docker run --rm -v /:/host alpine wc -l /host/etc/shadowคำสั่งนั้นจะอ่านไฟล์ที่อ่านได้เฉพาะ root เท่านั้น จากบัญชีที่ไม่มีสิทธิ์ sudo เอกสารหลังการติดตั้งของ Docker เองก็ได้ระบุไว้เช่นกันว่า: กลุ่ม docker ให้สิทธิ์เทียบเท่ากับ root ให้เพิ่มบัญชีเข้ากลุ่มนี้ก็ต่อเมื่อคุณยินดีที่จะให้สิทธิ์ sudo แก่บัญชีนั้นด้วย หากคุณกำลังตั้งค่าบัญชีบนเซิร์ฟเวอร์ใหม่ ให้ตัดสินใจเรื่องนี้ควบคู่ไปกับส่วนที่เหลือของ การตั้งค่าผู้ใช้ด้วยหลัก least-privilege บน VPS แทนที่จะมาทำหลังจากนั้น
Docker ยังมีโหมด rootless ที่รัน daemon ในฐานะผู้ใช้ที่ไม่มีสิทธิ์พิเศษ ซึ่งเป็นเส้นทางการติดตั้งแยกต่างหากและส่งผลต่อการทำงานของ storage driver รวมถึงพอร์ตที่ต่ำกว่า 1024 ดังนั้นควรวางแผนให้เป็นโปรเจกต์เฉพาะ แทนที่จะมองว่าเป็นเพียง flag ที่จะเพิ่มในภายหลัง
ขั้นตอนถัดไป
ขณะนี้คุณมี engine, compose plugin, บริการที่ทำงานต่อเนื่องหลังรีบูต และพฤติกรรมเฉพาะของ EL ทั้ง 3 ประการตามที่ระบุไว้ข้างต้นแล้ว ขั้นตอนถัดไปคือการสร้าง compose.yaml สำหรับแต่ละบริการ โดยเนื้อหาใน โครงสร้างของไฟล์ Compose จะครอบคลุมรูปแบบไฟล์และคำสั่งที่ใช้ควบคุม หากนี่เป็น container host เครื่องแรกของคุณ เนื้อหาใน การรัน Docker บน VPS จะครอบคลุมประเด็นเรื่องการจัดสรรขนาดทรัพยากร, พื้นที่จัดเก็บข้อมูล และการดูแลรักษา image ซึ่งเป็นส่วนที่คู่มือนี้ไม่ได้กล่าวถึง
FAQ
Repository ของ Docker สำหรับ CentOS สามารถใช้งานบน Rocky Linux และ AlmaLinux ได้หรือไม่
ได้ โดยให้เพิ่ม https://download.docker.com/linux/centos/docker-ce.repo ด้วย dnf config-manager ในไฟล์ดังกล่าวจะมี baseurl ซึ่งประกอบด้วย $releasever โดย Rocky Linux และ AlmaLinux จะขยายค่านี้เป็นเลขเวอร์ชันหลัก (major version) ดังนั้นเครื่องที่ใช้ EL 9 จะเชื่อมต่อไปยัง tree ของ CentOS 9 และเครื่อง EL 10 จะเชื่อมต่อไปยัง tree ของ CentOS 10 ให้ตรวจสอบการขยายค่าด้วย sudo dnf repoinfo docker-ce-stable และอ่านบรรทัด Repo-baseurl หากเกิด Status code: 404 ในขณะที่ dnf ดึงข้อมูล metadata แสดงว่าตัวแปรขยายไปเป็นเวอร์ชันย่อย (point release) ซึ่งสามารถแก้ไขได้โดยการแก้ไขไฟล์ /etc/yum.repos.d/docker-ce.repo ให้ใช้เพียงเลขเวอร์ชันหลักเท่านั้น
สามารถติดตั้ง Docker และ podman บนเซิร์ฟเวอร์เดียวกันได้หรือไม่
เอกสารของ Docker ระบุว่า podman และ runc เป็นแพ็กเกจที่ขัดแย้งกัน และแนะนำให้ลบทั้งสองออกก่อนติดตั้ง Docker Engine ความขัดแย้งที่เกิดขึ้นจริงคือแพ็กเกจ podman-docker ซึ่งเป็นเจ้าของ /usr/bin/docker และเปลี่ยนคำสั่ง docker ทุกคำสั่งให้กลายเป็นคำสั่งของ podman ให้รัน rpm -qf "$(command -v docker)" เพื่อดูว่าแพ็กเกจใดเป็นเจ้าของ path ดังกล่าว หากผลลัพธ์ขึ้นต้นด้วย podman-docker แสดงว่า podman เป็นผู้ตอบสนอง การเก็บ engine ทั้งสองไว้ด้วยกันไม่ใช่รูปแบบที่ Docker รองรับ ดังนั้นบนเซิร์ฟเวอร์ที่สำคัญควรเลือกใช้เพียงอย่างเดียว
เหตุใด container ของฉันจึงได้รับข้อผิดพลาด permission denied บน bind mount
SELinux จะทำงานในโหมด enforcing เป็นค่าเริ่มต้นบน Rocky Linux และ AlmaLinux โดย container จะรันด้วย type container_t และสามารถเข้าถึงได้เฉพาะไฟล์ที่ติดป้ายกำกับเป็น container_file_t เท่านั้น ดังนั้นไดเรกทอรีที่คุณสร้างขึ้นจึงมีป้ายกำกับที่ไม่ถูกต้อง ทำให้ถูกปฏิเสธการเข้าถึงโดยไม่คำนึงถึงเจ้าของหรือโหมดของไฟล์ ให้ตรวจสอบด้วย ls -ldZ บน path ของโฮสต์ และ sudo ausearch -m avc -ts recent ซึ่งจะแสดง avc: denied พร้อมบริบทที่ขัดแย้งกันสองรายการ ให้เพิ่ม :z ลงในอาร์กิวเมนต์ volume สำหรับเนื้อหาที่แชร์ระหว่าง container หรือ :Z สำหรับเนื้อหาที่เป็นส่วนตัวของ container เดียว ห้ามชี้ :Z ไปที่ /home หรือ /usr เนื่องจากกระบวนการเปลี่ยนป้ายกำกับ (relabel) จะทำงานแบบ recursive และจะทำให้ระบบโฮสต์เสียหาย
ฉันจำเป็นต้องเปิดพอร์ตใน firewalld เพื่อเผยแพร่พอร์ตของ container หรือไม่
ไม่จำเป็น และนั่นคือปัญหา กฎ NAT ของ Docker จะเขียนที่อยู่ปลายทางใหม่ก่อนที่แพ็กเกจจะไปถึงเส้นทาง input ของโฮสต์ ดังนั้นกฎ zone ของ firewalld จึงไม่ตรวจสอบแพ็กเกจเหล่านั้น นอกจากนี้ Docker ยังนำ bridge ของตนไปไว้ใน zone ของ firewalld ที่ชื่อ docker ซึ่งมีเป้าหมายเป็น ACCEPT ทำให้ container ที่เริ่มด้วย -p 8080:80 สามารถเข้าถึงได้จากอินเทอร์เน็ตในขณะที่ sudo firewall-cmd --list-ports ไม่แสดงผลลัพธ์ใดๆ ให้เผยแพร่ไปยังที่อยู่ที่เฉพาะเจาะจงด้วย -p 127.0.0.1:8080:80 หากต้องการให้เข้าถึงบริการได้เฉพาะจากโฮสต์เท่านั้น หรือแทรกกฎการกรองลงใน chain DOCKER-USER ซึ่ง Docker จะประมวลผลก่อนกฎ accept ของตนเอง
การเพิ่มผู้ใช้ของฉันเข้าในกลุ่ม docker ปลอดภัยหรือไม่
การทำเช่นนั้นเท่ากับการให้สิทธิ์ root สมาชิกของกลุ่ม docker สามารถเขียนไฟล์ลงใน /var/run/docker.sock ได้ และ docker run --rm -v /:/host alpine wc -l /host/etc/shadow จะอ่านไฟล์ที่จำกัดสิทธิ์เฉพาะ root จากบัญชีที่ไม่มีสิทธิ์ sudo เอกสารหลังการติดตั้งของ Docker ระบุถึงความเท่าเทียมกันนี้เช่นกัน ให้เพิ่มเฉพาะบัญชีที่คุณไว้วางใจให้ใช้ sudo เท่านั้น และให้ใช้ sudo docker ต่อไปสำหรับบัญชีที่ใช้ร่วมกันหรือบัญชีบริการ โหมด Rootless เป็นทางเลือกเมื่อคุณต้องการรัน container ภายใต้ผู้ใช้ที่ไม่มีสิทธิ์พิเศษ ซึ่งเป็นเส้นทางการติดตั้งแยกต่างหากไม่ใช่เพียงการตั้งค่าทั่วไป