วิธีติดตั้ง 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 ก็ใช้วิธีเดียวกันนี้
การติดตั้งนั้นสั้นมาก ดังนั้นเนื้อหาส่วนใหญ่ในคู่มือนี้จึงครอบคลุมถึงสิ่งที่ 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 ได้เหมือนกับแพ็กเกจอื่นๆ ในระบบของคุณ
Podman กำลังตอบสนองต่อคำสั่ง docker อยู่หรือไม่?
Rocky Linux และ AlmaLinux มี Podman มาให้ใน repository เริ่มต้น และ VPS image จำนวนมากก็ติดตั้งมาให้คุณเรียบร้อยแล้ว บาง 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.อาจมีบางคนสร้างไฟล์นั้นขึ้นมาเพื่อปิดการแสดงผล banner ดังนั้นอย่าเชื่อถือเพียงแค่วิธีนี้วิธีเดียว ให้สอบถามฐานข้อมูลแพ็กเกจว่าแพ็กเกจใดเป็นเจ้าของ binary ดังกล่าว:
command -v docker
rpm -qf "$(command -v docker)"หากคำตอบขึ้นต้นด้วย podman-docker แสดงว่า Podman เป็นผู้ตอบสนองคำสั่ง หากขึ้นต้นด้วย docker-ce-cli แสดงว่าเป็น Docker ของจริง แต่ถ้า rpm -qf รายงานว่าไม่มีแพ็กเกจใดเป็นเจ้าของไฟล์ แสดงว่ามีคนติดตั้งด้วยตนเอง คุณควรตรวจสอบ script นั้นก่อนที่จะใช้งาน
Podman สามารถรัน OCI image เดียวกันได้และเป็นตัวเลือกที่เหมาะสม หากคุณต้องการใช้งาน Podman อยู่แล้ว ให้หยุดที่ขั้นตอนนี้ แต่หากคุณต้องการ Docker Engine ให้ลบแพ็กเกจที่ขัดแย้งกันออกก่อน นี่คือรายการที่ 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 ซึ่งแพ็กเกจ containerd.io จะเข้ามาแทนที่ อย่างไรก็ตาม เอกสารของ Docker ถือว่า Podman เป็นแพ็กเกจที่ขัดแย้งกัน ดังนั้นโครงสร้างนี้จึงไม่ได้รับการสนับสนุนจาก Docker หากการติดตั้งยังคงแจ้งว่ามีความขัดแย้ง ให้ใช้รายการลบแบบเต็มตามที่ระบุไว้ข้างต้น
เพิ่ม repository ของ Docker ด้วย dnf config-manager
Docker เผยแพร่ RPM สำหรับ Enterprise Linux ที่ download.docker.com ไฟล์ repository จะชี้ไปยัง tree ของ CentOS ซึ่งเป็นจุดที่ Rocky Linux และ AlmaLinux ใช้ตรวจสอบ ตรวจสอบเมื่อเดือนสิงหาคม 2026 พบว่า Docker จัดทำเอกสาร repository นี้สำหรับ CentOS Stream 9 และ CentOS Stream 10
sudo dnf -y install dnf-plugins-core
sudo dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repodnf เวอร์ชัน 5 ได้ตัดอาร์กิวเมนต์ --add-repo ออกไป ดังนั้นคำสั่งที่สองจึงล้มเหลวในรุ่นที่ใหม่กว่า ให้ตรวจสอบว่าคุณใช้เวอร์ชันใด แล้วเลือกรูปแบบที่เหมาะสม:
dnf --versionหากแสดงผลเป็นเวอร์ชัน 5.x ให้ใช้รูปแบบคำสั่งย่อยแทน:
sudo dnf config-manager addrepo --from-repofile=https://download.docker.com/linux/centos/docker-ce.repoทั้งสองคำสั่งจะเขียนไฟล์เดียวกันลงใน /etc/yum.repos.d/docker-ce.repo รูปแบบที่ผิดจะแจ้งข้อผิดพลาดว่าไม่รู้จักอาร์กิวเมนต์แทนที่จะทำงานผิดพลาดโดยไม่แจ้งเตือน ดังนั้นคุณจะไม่พลาดจุดนี้
ไฟล์ repo นั้นตั้งค่า baseurl ให้เป็น path ที่มี $releasever อยู่ และ dnf จะขยายตัวแปรนั้นจากแพ็กเกจ release ของคุณ Rocky Linux และ AlmaLinux ตั้งค่าตัวแปรนี้เป็นเลขเวอร์ชันหลัก เช่น 9 บน EL 9 และ 10 บน EL 10 ซึ่งเป็นเหตุผลว่าทำไม repository ของ CentOS จึงทำงานได้อย่างถูกต้องบนเครื่อง Rocky ให้ยืนยันการขยายค่าก่อนที่คุณจะติดตั้ง:
sudo dnf repoinfo docker-ce-stableอ่านบรรทัด Repo-baseurl โดยควรลงท้ายด้วย /9/x86_64/stable หรือ /10/x86_64/stable หาก release ของคุณตั้งค่า $releasever เป็นเวอร์ชันย่อย เช่น 9.6 ตัว dnf จะรายงาน Status code: 404 สำหรับ URL นั้นเมื่อดึงข้อมูล metadata ให้แก้ไขโดยเปิดไฟล์ /etc/yum.repos.d/docker-ce.repo แล้วแทนที่ $releasever ด้วยเลขเวอร์ชันหลักเพียงอย่างเดียว
ติดตั้ง 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 ไว้ในรูปแบบหยุดทำงานและปิดการใช้งานหลังการติดตั้ง นี่คือเหตุผลที่ขั้นตอนนี้ปรากฏในหน้าเอกสารสำหรับ CentOS ของ Docker แต่ไม่ปรากฏในหน้าของ Ubuntu ซึ่งตัว deb จะเริ่ม service ให้คุณโดยอัตโนมัติ หากข้าม enable ไป Docker จะทำงานจนกว่าจะมีการ reboot ครั้งถัดไป จากนั้นมันจะหยุดทำงานและทำให้ container ทั้งหมดหยุดตามไปด้วย
systemctl status ควรแสดงผลเป็น Active: active (running) ส่วน container hello-world ควรพิมพ์ข้อความ This message shows that your installation appears to be working correctly. ออกมาแล้วจบการทำงาน หากระบบแสดงข้อผิดพลาดเรื่องสิทธิ์การเข้าถึงที่ /var/run/docker.sock แสดงว่าคุณไม่ได้ใส่ sudo ซึ่งส่วนการตั้งค่ากลุ่ม docker ด้านล่างจะช่วยแก้ไขปัญหานี้
ให้ตรวจสอบปลั๊กอิน compose แยกต่างหาก เนื่องจากเป็นแพ็กเกจที่แตกต่างกันและอาจขาดหายไปได้ในขณะที่ตัว engine ยังทำงานปกติ:
docker compose versionผลลัพธ์ที่แสดงว่าระบบทำงานปกติจะมีลักษณะเหมือน Docker Compose version v2.x.x การทำให้บริการของคุณกลับมาทำงานหลังการ reboot เป็นคนละประเด็นกับการเปิดใช้งาน daemon โดย restart policies จะเป็นตัวกำหนดว่าบริการ 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 เท่านั้น ไดเรกทอรีที่คุณสร้างบน host จะได้รับ label ตาม path แม่ของมัน ซึ่งไม่ใช่ container_file_t ดังนั้น container จึงถูกปฏิเสธสิทธิ์แม้ว่าเจ้าของ กลุ่ม และโหมดไฟล์จะดูถูกต้องเมื่อตรวจสอบจากฝั่ง host ก็ตาม คุณสามารถจำลองเหตุการณ์นี้ได้ด้วย 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 ซึ่งสำหรับ path ภายใต้ /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 ทำการ relabel path ให้คุณ:
sudo docker run --rm -v /srv/site:/usr/share/nginx/html:ro,z nginx:alpine cat /usr/share/nginx/html/index.html:z (ตัวพิมพ์เล็ก) จะทำการ relabel เนื้อหาให้เป็นแบบ shared เพื่อให้หลาย container สามารถใช้ไดเรกทอรีเดียวกันได้ ส่วน :Z (ตัวพิมพ์ใหญ่) จะทำการ relabel ให้เป็นแบบ private และไม่แชร์ ซึ่งจะผูกติดกับ container เดียวเท่านั้น และหาก container ที่สองพยายามอ่าน path เดียวกันก็จะถูกปฏิเสธสิทธิ์ ให้ใช้ :z สำหรับสิ่งที่ sidecar หรือ backup container ต้องเข้าถึง และใช้ :Z สำหรับไดเรกทอรีฐานข้อมูลที่ container เดียวเป็นเจ้าของ
เอกสารของ Docker มีคำเตือนที่ควรย้ำเตือนไว้ เพราะการ relabel จะทำงานแบบ recursive การทำ bind-mount ไดเรกทอรีระบบ เช่น /home หรือ /usr โดยใช้ :Z จะ "ทำให้เครื่อง host ของคุณใช้งานไม่ได้ และคุณอาจต้อง relabel ไฟล์บนเครื่อง host ด้วยตนเอง" ให้ใช้ suffix เหล่านี้กับไดเรกทอรีที่คุณสร้างขึ้นสำหรับ container เท่านั้น ห้ามใช้กับ path ของระบบโดยเด็ดขาด
ใน 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 อีกประการคือความเป็นเจ้าของแบบ numeric user และ group ปกติ ซึ่งเป็น สิ่งที่ตัวแปร PUID และ PGID มีไว้เพื่อแก้ไข ls -lnZ จะแสดงโหมด, เจ้าของที่เป็นตัวเลข และ label ในบรรทัดเดียว เพื่อให้คุณทราบว่ากำลังเผชิญกับปัญหาใดอยู่
เหตุใดพอร์ตที่เผยแพร่ไว้จึงเข้าถึงได้ทั้งที่ firewalld แสดงว่าปิดอยู่
Firewalld เป็นไฟร์วอลล์เริ่มต้นบน Rocky Linux และ AlmaLinux ให้ตรวจสอบว่าไฟร์วอลล์ทำงานอยู่หรือไม่ด้วย sudo systemctl is-active 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 ไม่ได้มุ่งหน้ามาที่โฮสต์โดยตรง: Docker จะติดตั้งกฎ destination NAT (network address translation) เพื่อเขียนที่อยู่ปลายทางใหม่ให้เป็นที่อยู่ของคอนเทนเนอร์ก่อนที่แพ็กเก็ตจะมาถึง input path ของโฮสต์ ทำให้เคอร์เนลส่งต่อ (forward) แพ็กเก็ตแทนที่จะส่งให้โฮสต์โดยตรง จากนั้น Docker จะนำอินเทอร์เฟซแบบ bridge ไปไว้ในโซนของ firewalld ที่ชื่อ docker ซึ่งมี target เป็น 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 ได้ปิดช่องโหว่ที่คล้ายกัน: การเข้าถึงพอร์ตคอนเทนเนอร์โดยตรงผ่านการ route ซึ่งไม่เคยถูกเผยแพร่มาก่อน จะถูกบล็อกใน 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 แก่บัญชีนั้นด้วย หากคุณกำลังตั้งค่าบัญชีบนเซิร์ฟเวอร์ใหม่ ให้ตัดสินใจเรื่องนี้ควบคู่ไปกับการตั้งค่า การกำหนดสิทธิ์ผู้ใช้ขั้นต่ำบน 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 จะขยายค่านี้เป็นเลขเวอร์ชันหลักโดยอัตโนมัติ ดังนั้นเครื่องที่ใช้ EL 9 จะเชื่อมต่อไปยัง tree ของ CentOS 9 และเครื่อง EL 10 จะเชื่อมต่อไปยัง tree ของ CentOS 10 ให้ตรวจสอบการขยายค่าด้วยคำสั่ง sudo dnf repoinfo docker-ce-stable และอ่านบรรทัด Repo-baseurl หากเกิด Status code: 404 ในขณะที่ dnf กำลังดึงข้อมูล metadata แสดงว่าตัวแปรขยายค่าไปเป็นเวอร์ชันย่อย ให้แก้ไขไฟล์ /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 และสามารถเข้าถึงได้เฉพาะไฟล์ที่ถูกติดป้ายกำกับ (label) ว่า container_file_t เท่านั้น ดังนั้นไดเรกทอรีที่คุณสร้างขึ้นจึงมี label ที่ไม่ถูกต้อง ทำให้ถูกปฏิเสธการเข้าถึงโดยไม่คำนึงถึงเจ้าของหรือโหมดของไฟล์ ให้ตรวจสอบด้วยคำสั่ง ls -ldZ บน path ของ host และ sudo ausearch -m avc -ts recent ซึ่งจะแสดง avc: denied พร้อมกับบริบท (context) สองรายการที่ไม่ตรงกัน ให้เพิ่ม :z เข้าไปในอาร์กิวเมนต์ของ volume สำหรับเนื้อหาที่แชร์ระหว่าง container หรือ :Z สำหรับเนื้อหาที่เป็นส่วนตัวของ container เดียว ห้ามชี้ :Z ไปที่ /home หรือ /usr โดยเด็ดขาด เพราะการเปลี่ยน label จะทำงานแบบ recursive และจะทำให้ระบบ host เสียหาย
จำเป็นต้องเปิดพอร์ตใน firewalld เพื่อ publish พอร์ตของ container หรือไม่
ไม่จำเป็น และนั่นคือปัญหา กฎ NAT ของ Docker จะเขียนทับที่อยู่ปลายทางก่อนที่แพ็กเก็ตจะไปถึงเส้นทาง input ของ host ดังนั้นกฎของ zone ใน firewalld จึงไม่ตรวจสอบแพ็กเก็ตดังกล่าว นอกจากนี้ Docker ยังนำ bridge ของตนไปไว้ใน zone ของ firewalld ที่ชื่อ docker ซึ่งมี target เป็น ACCEPT ทำให้ container ที่เริ่มด้วย -p 8080:80 สามารถเข้าถึงได้จากอินเทอร์เน็ตในขณะที่ sudo firewall-cmd --list-ports ไม่แสดงผลลัพธ์ใดๆ ให้ publish ไปยังที่อยู่ที่เฉพาะเจาะจงด้วย -p 127.0.0.1:8080:80 หากต้องการให้เข้าถึงบริการได้เฉพาะจาก host เท่านั้น หรือแทรกกฎการกรองเข้าไปใน 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 ภายใต้ผู้ใช้ที่ไม่มีสิทธิ์พิเศษ และเป็นขั้นตอนการติดตั้งที่แยกต่างหาก ไม่ใช่เพียงแค่การตั้งค่า