Docker Compose เลือกใช้ Bind Mount หรือ Named Volume ดี
เปรียบเทียบความแตกต่างระหว่าง Bind mount และ Named volume ใน Docker Compose พร้อมวิธีเลือกใช้งานให้เหมาะสมกับข้อมูลแต่ละประเภท ปัญหาเรื่องสิทธิ์ไฟล์ และแนวทางการสำรองข้อมูล
Bind mount หรือ named volume: คำตอบโดยย่อ
Docker Compose volumes มีสองประเภท และการเลือกใช้ขึ้นอยู่กับว่าใครเป็นเจ้าของไฟล์ ให้ใช้ bind mount สำหรับไฟล์ที่คุณเขียนและอ่านเอง เช่น ไฟล์ตั้งค่า, เทมเพลต, เว็บไซต์แบบ static ให้ใช้ named volume สำหรับข้อมูลที่แอปพลิเคชันเป็นเจ้าของ เช่น ไฟล์ฐานข้อมูล, ดัชนีการค้นหา, สื่อที่อัปโหลด Bind mount จะชี้ไปยัง path บน host ที่คุณสามารถเปิดแก้ไขได้ด้วยโปรแกรมแก้ไขข้อความ ส่วน named volume คือพื้นที่จัดเก็บที่ Docker สร้างและติดตามให้คุณ ซึ่งคุณจะเข้าถึงได้ผ่านทาง Docker
ทั้งสองประเภทปรากฏอยู่ภายใต้ key volumes: เดียวกันภายใน service ซึ่งเป็นเหตุผลว่าทำไมจึงมักสับสนกัน ความแตกต่างอยู่ที่ด้านซ้ายของเครื่องหมายทวิภาค (colon) หากด้านซ้ายขึ้นต้นด้วย . หรือ / จะถือเป็น host path ซึ่งก็คือ bind mount ส่วนค่าอื่นใดที่เหลือจะเป็นชื่อ ซึ่งถือเป็น named volume และชื่อนั้นจะต้องถูกประกาศไว้ในบล็อก volumes: ระดับบนสุดด้วย
ไวยากรณ์สองรูปแบบในไฟล์ compose
services:
db:
image: postgres:17
environment:
POSTGRES_PASSWORD: changeme
volumes:
- pgdata:/var/lib/postgresql/data
web:
image: nginx:1.27
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
- ./site:/usr/share/nginx/html:ro
volumes:
pgdata:pgdata:/var/lib/postgresql/data คือ named volume ส่วน ./nginx.conf:/etc/nginx/nginx.conf คือ bind mount และ :ro จะทำการ mount แบบอ่านได้อย่างเดียว ซึ่งเป็นค่าเริ่มต้นที่เหมาะสมสำหรับไฟล์คอนฟิกที่คอนเทนเนอร์ไม่ควรแก้ไข หากลืมระบุรายการ volumes: ไว้ที่ระดับบนสุด Compose จะหยุดทำงานพร้อมกับข้อผิดพลาด service "db" refers to undefined volume pgdata
สั่งเริ่มการทำงานและแสดงรายการสิ่งที่ Docker สร้างขึ้น:
docker compose up -d
docker volume lsvolume ดังกล่าวไม่ได้ชื่อ pgdata แต่มีชื่อว่า <project>_pgdata โดยชื่อโปรเจกต์จะใช้ชื่อของไดเรกทอรีที่เก็บไฟล์ compose เป็นค่าเริ่มต้น หากไดเรกทอรีชื่อ myapp จะได้ชื่อเป็น myapp_pgdata เรื่องนี้มีความสำคัญเพราะหากเปลี่ยนชื่อไดเรกทอรี คุณจะได้ volume ใหม่ที่ว่างเปล่า และแอปพลิเคชันจะดูเหมือนข้อมูลสูญหาย ทั้งที่ความจริงแล้วข้อมูลไม่ได้หายไปไหน แต่ volume เก่าจะยังคงแสดงอยู่ในรายการของ docker volume ls คุณสามารถกำหนดชื่อให้คงที่ด้วย name: ในไฟล์ compose หรือตั้งค่า COMPOSE_PROJECT_NAME หากมีความเป็นไปได้ที่ไดเรกทอรีจะถูกย้าย การตั้งค่าลักษณะนี้ควรจัดเก็บไว้พร้อมกับ ไฟล์สภาพแวดล้อมและข้อมูลลับของ Compose อื่นๆ ของคุณ
ทำไมปัญหาเรื่องสิทธิ์การเข้าถึงถึงมักเกิดกับ bind mounts
นี่คือความแตกต่างในทางปฏิบัติที่สำคัญที่สุด และส่วนใหญ่มาจากกฎข้อเดียวคือ: named volume ที่ว่างเปล่าในการใช้งานครั้งแรกจะถูกเติมข้อมูลจาก image แต่ bind mount จะไม่มีการทำเช่นนั้น
เมื่อ Docker ทำการ mount named volume ที่ว่างเปล่าทับไดเรกทอรีที่มีข้อมูลอยู่แล้วใน image ระบบจะคัดลอกข้อมูลเหล่านั้นลงใน volume โดยใช้ความเป็นเจ้าของและโหมดที่กำหนดไว้ใน image ตัวอย่างเช่น Postgres image อย่างเป็นทางการจะจัดส่ง /var/lib/postgresql/data ซึ่งเป็นเจ้าของโดย postgres ของมันเอง ดังนั้น volume ที่ได้จึงมีเจ้าของเป็น numeric id เดียวกันและฐานข้อมูลจึงเริ่มทำงานได้
การเติมข้อมูลดังกล่าวเป็นข้อได้เปรียบเพียงอย่างเดียวที่ named volume ได้รับ Docker เป็นผู้จัดการตำแหน่งที่ตั้งของ volume แต่ container จะอ่านและเขียนข้อมูลด้วยการดำเนินการไฟล์ระบบทั่วไป ดังนั้นความเป็นเจ้าของและโหมดต่างๆ จึงถูกบังคับใช้เช่นเดียวกับ bind mount หาก named volume ถูก mount ใน path ที่ image ไม่มี หรือถูกเติมข้อมูลครั้งแรกโดย image หรือผู้ใช้อื่น มันจะไม่ได้รับความเป็นเจ้าของจาก image และกระบวนการที่ไม่ใช่ root อาจถูกปฏิเสธการเข้าถึงที่นั่นได้เช่นกัน การตรวจสอบ id ด้านล่างใช้ได้กับทั้งสองกรณี
bind mount ทำงานในทางตรงกันข้าม สิ่งที่อยู่บน host คือสิ่งที่ container มองเห็น รวมถึงความเป็นเจ้าของด้วย และเนื้อหาของ image ที่ path นั้นจะถูกซ่อนไว้ หากไดเรกทอรีบน host ไม่มีอยู่จริง Docker daemon จะสร้างมันขึ้นมา และเนื่องจาก daemon ทำงานในฐานะ root คุณจึงได้ไดเรกทอรีที่มี root:root เป็นเจ้าของ กระบวนการใน container ที่ทำงานด้วยผู้ใช้ที่ไม่ใช่ root จึงไม่สามารถเขียนข้อมูลลงไปได้:
PermissionError: [Errno 13] Permission denied: '/data/app.db'วิธีแก้ไขคือทำให้ตัวเลขตรงกัน ความเป็นเจ้าของผ่าน bind mount จะถูกเปรียบเทียบด้วย numeric user id ไม่ใช่ด้วยชื่อ เพราะ container มี /etc/passwd ของตัวเอง ผู้ใช้ที่ชื่อ app ภายใน container ไม่มีความหมายใดๆ บน host Uid 1000 หมายถึง uid 1000 ทั้งสองฝั่ง เว้นแต่ daemon จะทำงานด้วย user-namespace remapping (userns-remap ซึ่งตั้งค่าใน /etc/docker/daemon.json หรือบนบรรทัดคำสั่ง dockerd) การทำ remapping จะเลื่อน id ของทุก container ไปยังช่วงย่อยบน host ในช่วงตัวอย่างของ Docker ที่เริ่มที่ 231072, container uid 0 คือ host uid 231072 ดังนั้น container uid 1000 จึงเป็น host uid 232072
id -u
mkdir -p ./data
docker compose exec web id
sudo chown -R 1000:1000 ./datadocker compose exec web id จะแสดง uid ที่กระบวนการใน container ใช้งานจริง หากไม่มีการทำ remapping ให้จับคู่ไดเรกทอรีบน host ให้ตรงกับตัวเลขนั้น หรือกำหนด container ให้ใช้ตัวเลขของคุณด้วย user: "1000:1000" ใน service หากเปิดใช้งาน remapping แล้ว user: จะไม่สามารถกำหนด container ให้ตรงกับตัวเลขบน host ของคุณได้ เพราะ id ที่คุณตั้งค่าไว้จะถูกเลื่อนไปด้วย: user: "1000:1000" จะยังคงไปตกที่ host uid 232072 ในช่วงตัวอย่างนั้น ให้ใช้ chown ไดเรกทอรีบน host ไปยัง mapped host id แทน (ช่วงของคุณอยู่ใน /etc/subuid และ /etc/subgid) เอกสารของ Docker ระบุเพียง --userns=host บน docker container create, run หรือ exec ว่าเป็นวิธีปิดการทำ remapping สำหรับ container เดียว การกำหนด user: นั้นสะอาดกว่าสำหรับแอปพลิเคชันที่คุณเขียนเอง การใช้ chown กับไดเรกทอรีบน host ปลอดภัยกว่าสำหรับ image ที่คุณไม่ได้เขียนเอง เพราะบาง image เริ่มต้น entrypoint ในฐานะ root แล้วจึงลดสิทธิ์ลง และคาดหวังความเป็นเจ้าของที่เฉพาะเจาะจงภายใต้สิทธิ์นั้น
มีอีกสองประเด็นที่ควรทราบ บน Fedora, RHEL และระบบอื่นๆ ที่บังคับใช้ SELinux (security-enhanced Linux) bind mount จะถูกปฏิเสธจนกว่าจะมีการเปลี่ยน label ดังนั้นให้เพิ่ม :z สำหรับ path ที่แชร์ระหว่าง container หรือ :Z สำหรับ path ที่ควรใช้เพียง container เดียว โดยเขียนเป็น - ./data:/data:Z และการทำ bind mount ของไฟล์เดียวแทนที่จะเป็นไดเรกทอรีจะเกิดปัญหาเมื่อโปรแกรมแก้ไขไฟล์แทนที่ไฟล์นั้นแทนที่จะเขียนทับในที่เดิม เพราะ mount จะติดตาม inode เดิม container จะยังคงเห็นเนื้อหาเก่าจนกว่าคุณจะรีสตาร์ท ให้ mount ไดเรกทอรีแม่แทนหากมีการแก้ไขไฟล์บ่อยครั้ง
ประสิทธิภาพ: ความแตกต่างที่แท้จริง
บนเซิร์ฟเวอร์ Linux ทั้งสองรูปแบบจะผ่าน kernel path เดียวกัน ดังนั้นความแตกต่างของ throughput จึงน้อยมากจนไม่ควรนำมาเป็นเกณฑ์ในการตัดสินใจ Named volumes ที่ใช้ driver เริ่มต้น local จะอยู่ในระบบไฟล์เดียวกับส่วนที่เหลือของ Docker ภายใต้ /var/lib/docker/volumes/ ในขณะที่ bind mount จะอยู่ในตำแหน่งที่คุณระบุไว้
ความแตกต่างจะปรากฏชัดเจนบน Docker Desktop สำหรับ macOS และ Windows ซึ่งคอนเทนเนอร์จะทำงานอยู่ภายใน virtual machine ในกรณีนั้น bind mount จะต้องข้ามจากระบบไฟล์ของ host เข้าไปยัง virtual machine ผ่านชั้นการแชร์ไฟล์ ทำให้ภาระงานที่มีการดำเนินการกับไฟล์ขนาดเล็กจำนวนมาก เช่น dependency tree ของ Node.js หรือ cache ของ PHP framework ทำงานช้าลงอย่างเห็นได้ชัด ส่วน named volumes จะคงอยู่ภายใน virtual machine จึงไม่มีค่าใช้จ่ายในส่วนนี้ นี่คือเหตุผลที่ไฟล์ compose สำหรับการพัฒนาจำนวนมากใช้ bind-mount กับไดเรกทอรีซอร์สโค้ด แต่ประกาศ named volume ทับบน node_modules
ความแตกต่างที่แท้จริงอีกประการหนึ่งคือตำแหน่งที่จัดเก็บข้อมูล Bind mount ไปยัง /mnt/backup จะวางข้อมูลไว้บนดิสก์ลูกนั้น ส่วน named volume จะลงไปอยู่ในระบบไฟล์ใดก็ตามที่ถือครอง /var/lib/docker ซึ่งบน VPS มักจะเป็น root disk ฐานข้อมูลที่ขยายขนาดขึ้นภายใน named volume จะทำให้ดิสก์ลูกเดียวกับที่เก็บ system log เต็มได้ ควรตรวจสอบก่อนที่จะกลายเป็นเหตุการณ์ไม่คาดฝัน:
docker system df -v
df -h /var/lib/dockerdocker system df -v จะแสดงรายการ volume ทั้งหมดพร้อมขนาด และระบุ volume ที่ไม่มีคอนเทนเนอร์ใดอ้างอิงถึงแล้ว
การตรวจสอบ named volume
Named volume ไม่ใช่กล่องดำ คุณสามารถสอบถาม Docker ได้ว่าข้อมูลอยู่ที่ใด:
docker volume inspect myapp_pgdataฟิลด์ Mountpoint จะแสดง path จริงบนโฮสต์ ซึ่งโดยปกติคือ /var/lib/docker/volumes/myapp_pgdata/_data คุณสามารถอ่านไฟล์ได้ด้วย sudo ls ซึ่งมีประโยชน์สำหรับการตรวจสอบอย่างรวดเร็ว แต่อย่าแก้ไขไฟล์ในตำแหน่งนี้โดยตรง การเขียนไฟล์ด้วยสิทธิ์ root จะทำให้เกิดปัญหาเรื่องความเป็นเจ้าของไฟล์ตามที่กล่าวไว้ข้างต้น และ path ดังกล่าวเป็นรายละเอียดเฉพาะของไดรเวอร์ local ซึ่งไดรเวอร์ volume อื่นอาจไม่มีเหมือนกัน
วิธีที่ปลอดภัยในการตรวจสอบภายในคือการใช้ container แบบชั่วคราวเพื่อ mount volume นั้น:
docker run --rm -v myapp_pgdata:/vol alpine ls -la /volวิธีนี้ใช้ได้กับทุกไดรเวอร์ โดยจะเห็นสิทธิ์การเข้าถึงไฟล์เหมือนกับที่ container จริงเห็น และไม่ทิ้งสิ่งใดไว้เบื้องหลังเนื่องจากมีการใช้ --rm
การสำรองข้อมูลแต่ละประเภท
Bind mount เป็นไดเรกทอรีทั่วไป ดังนั้นเครื่องมือสำรองข้อมูลระดับไฟล์จึงรองรับอยู่แล้ว เพียงแค่ระบุ path บนโฮสต์ให้เครื่องมือสำรองข้อมูลก็เสร็จสิ้น ส่วน Named volume จำเป็นต้องมีขั้นตอนเพิ่มอีกหนึ่งขั้นเนื่องจากเครื่องมือต้องเข้าถึงข้อมูลภายใน volume นั้น ให้ mount volume และไดเรกทอรีบนโฮสต์เข้าไปใน container ชั่วคราวตัวเดียวกัน จากนั้นจึงเขียนไฟล์ archive:
docker run --rm \
-v myapp_pgdata:/data:ro \
-v "$PWD":/backup \
alpine tar czf /backup/pgdata.tar.gz -C /data .การกู้คืนข้อมูลทำได้โดยการย้อนกระบวนการกลับไปยัง volume ใหม่:
docker volume create myapp_pgdata_restored
docker run --rm \
-v myapp_pgdata_restored:/data \
-v "$PWD":/backup \
alpine tar xzf /backup/pgdata.tar.gz -C /datatar จะรักษาความเป็นเจ้าของแบบตัวเลข (numeric ownership) ไว้เมื่อรันในฐานะ root ภายใน container ซึ่งเป็นสิ่งที่ทำให้แอปพลิเคชันสามารถใช้งาน volume ที่กู้คืนมาได้
มีข้อควรระวังหนึ่งประการที่ใช้กับทั้งสองประเภท การคัดลอกไฟล์ของฐานข้อมูลในขณะที่ฐานข้อมูลกำลังทำงานอยู่จะทำให้ได้ archive ของข้อมูลที่เปลี่ยนแปลงตลอดเวลา ซึ่งอาจกู้คืนกลับมาในสถานะที่เสียหายได้ ให้หยุดบริการก่อน หรือใช้เครื่องมือของฐานข้อมูลเองในการ dump ข้อมูล ดังที่แสดงใน docker compose exec -T db pg_dump -U postgres appdb > appdb.sql วิธีนี้จะได้ไฟล์ธรรมดาที่คุณสามารถนำไปรวมไว้ใน กระบวนการสำรองข้อมูลด้วย restic ที่เข้ารหัส ควบคู่ไปกับไฟล์ compose ของคุณได้
การย้าย bind mount ไปยัง named volume
การย้ายข้อมูลนี้เป็นการคัดลอก ไม่ใช่การเปลี่ยนชื่อ และใช้เวลาดำเนินการประมาณ 1 นาที
docker compose down
docker volume create myapp_pgdata
docker run --rm \
-v "$PWD/data":/from \
-v myapp_pgdata:/to \
alpine sh -c 'cp -a /from/. /to/'cp -a จะคงความเป็นเจ้าของ โหมด และ timestamp ไว้ ดังนั้นผู้ใช้ใน container ที่เคยอ่านไดเรกทอรีเดิมได้จะยังคงอ่าน volume ใหม่ได้เช่นเดิม จากนั้นให้แก้ไข service เพื่อใช้ pgdata:/var/lib/postgresql/data เพิ่ม pgdata ลงในบล็อก volumes: ระดับบนสุด รันคำสั่ง docker compose up -d และตรวจสอบ log ของแอปพลิเคชันก่อนที่คุณจะลบไดเรกทอรีเดิมทิ้ง การย้ายในทิศทางตรงกันข้ามจะใช้คำสั่งเดียวกันโดยสลับ /from และ /to
โปรดจำไว้หนึ่งเรื่องในระหว่างการทดสอบ docker compose down จะไม่ยุ่งกับ named volume แต่ docker compose down -v จะลบทุก named volume ที่โปรเจกต์ประกาศไว้ทิ้งทั้งหมดและไม่สามารถย้อนกลับได้ ในขณะที่ bind mount จะยังคงอยู่ทั้งสองกรณีเนื่องจาก Docker ไม่เคยเป็นเจ้าของไดเรกทอรีนั้น หากคุณยังไม่คุ้นเคยกับคำสั่งจัดการวงจรชีวิตของคอนเทนเนอร์ คู่มือพื้นฐาน Docker Compose สำหรับ VPS จะช่วยอธิบายขั้นตอนเหล่านี้ให้คุณเข้าใจ
การเลือกใช้สำหรับแต่ละบริการ
ให้พิจารณาว่าใครเป็นผู้เขียนไฟล์นั้น หากเป็นค่าคอนฟิกที่คุณแก้ไขด้วยโปรแกรมแก้ไขข้อความและบันทึกไว้ใน git ให้ใช้ bind mount โดยเมาท์ไว้ที่ :ro เนื่องจากคุณต้องการให้ไฟล์เหล่านั้นมองเห็นได้และมีการทำเวอร์ชันควบคุม ส่วนสถานะของแอปพลิเคชันที่คุณไม่เคยเปิดแก้ไขด้วยตนเองนั้น ให้ใช้ named volume เนื่องจาก Docker จะกำหนดสิทธิ์การเข้าถึงจากอิมเมจโดยอัตโนมัติ และข้อมูลดังกล่าวไม่ได้ขึ้นอยู่กับ path บนโฮสต์
กรณีผสมคือไฟล์สื่อ (media) เช่น คลังรูปภาพ ซึ่งแอปพลิเคชันเป็นผู้เขียนแต่คุณเป็นผู้จัดการ และมักมีขนาดใหญ่จนต้องใช้ดิสก์เฉพาะ ให้ใช้ bind mount ไปยัง path บนดิสก์นั้น และกำหนดความเป็นเจ้าของ (ownership) ให้ถูกต้องเพียงครั้งเดียว นี่คือรูปแบบที่ stack สำหรับ self-host ส่วนใหญ่เลือกใช้ ได้แก่ ใช้ named volume สำหรับฐานข้อมูลและแคช และใช้ bind mounts สำหรับไฟล์คอนฟิกและไดเรกทอรีขนาดใหญ่ที่คุณต้องการดูแล ตัวอย่างเช่นบริการสนับสนุนลูกค้าอย่าง Chatwoot ที่รันบน VPS ก็ใช้รูปแบบนี้ โดยเก็บ Postgres ไว้ใน named volume และเก็บไฟล์แนบที่อัปโหลดไว้ใน path ที่คุณสามารถกำหนดจุดสำรองข้อมูลได้
FAQ
bind mount และ named volume แตกต่างกันอย่างไร?
bind mount จะแมป path บน host เข้าไปใน container ทำให้ทั้งสองฝั่งเห็นไดเรกทอรีเดียวกันและคุณสามารถแก้ไขด้วยเครื่องมือปกติได้ named volume คือพื้นที่จัดเก็บที่ Docker สร้างและจัดการ โดยอ้างอิงด้วยชื่อและประกาศไว้ในบล็อก volumes: ระดับบนสุด ความแตกต่างในทางปฏิบัติคือความเป็นเจ้าของ: bind mount สำหรับ config ที่คุณดูแล และ named volume สำหรับข้อมูลที่แอปพลิเคชันดูแล
ทำไมฉันถึงได้รับข้อผิดพลาด "permission denied" กับ bind mount แต่ไม่เกิดกับ named volume?
named volume ที่ว่างเปล่าจะถูกเติมข้อมูลจาก image ดังนั้นมันจึงมักสืบทอดความเป็นเจ้าของที่ image กำหนดไว้และผู้ใช้ใน container สามารถเขียนข้อมูลได้ แต่มันยังคงบังคับใช้สิทธิ์การเข้าถึง ดังนั้นมันอาจล้มเหลวในลักษณะเดียวกันหาก image ไม่มีข้อมูลที่ path นั้น bind mount จะแสดงไดเรกทอรีบน host ตามที่เป็นจริง และหาก Docker ต้องสร้างไดเรกทอรีนั้น มันจะกำหนดให้ root เป็นเจ้าของ ให้รัน docker compose exec <service> id เพื่อดู numeric id ที่ container ใช้ จากนั้นใช้ sudo chown -R <uid>:<gid> กับไดเรกทอรีบน host หรือเมื่อ daemon ไม่ได้ทำ user-namespace remapping ให้ตั้งค่า user: "1000:1000" บน service หาก daemon ใช้ user-namespace remapping ให้ใช้ chown ไปยัง mapped host id แทน เพราะ id ของ container จะถูกเลื่อนบน host และ user: ไม่ได้เปลี่ยนสิ่งนั้น
Docker จัดเก็บ named volume ไว้ที่ไหนบนดิสก์?
ด้วยไดรเวอร์ local ค่าเริ่มต้น พวกมันจะอยู่ที่ /var/lib/docker/volumes/<volume>/_data และ docker volume inspect <volume> จะแสดง Mountpoint ที่แน่นอน อ่านข้อมูลนั้นหากคุณต้องการตรวจสอบบางอย่าง แต่อย่าเขียนข้อมูลลงไปโดยตรงนอกจากผ่าน container เพราะการแก้ไขในฐานะ root บน host จะเปลี่ยนความเป็นเจ้าของในรูปแบบที่ container ไม่คาดคิด
ฉันจะสำรองข้อมูล named volume ได้อย่างไร?
ให้รัน container ชั่วคราวโดย mount ทั้ง volume และไดเรกทอรีบน host จากนั้นทำ archive จากที่หนึ่งไปยังอีกที่หนึ่งด้วย docker run --rm -v myvol:/data:ro -v "$PWD":/backup alpine tar czf /backup/myvol.tar.gz -C /data . สำหรับฐานข้อมูล ให้ dump ด้วยเครื่องมือของฐานข้อมูลเองแทนการคัดลอกไฟล์ขณะทำงาน เพราะการคัดลอกไฟล์ในขณะที่มีการเขียนข้อมูลอาจทำให้การกู้คืนข้อมูลอยู่ในสถานะเสียหายได้
คำสั่ง docker compose down จะลบ volume ของฉันหรือไม่?
docker compose down จะลบ container และ network แต่จะคง named volume ไว้ที่เดิม docker compose down -v จะลบทุก named volume ที่โปรเจกต์ประกาศไว้อย่างถาวร bind mount จะไม่ถูกลบโดยคำสั่งใดๆ เพราะไดเรกทอรีนั้นเป็นของ host ไม่ใช่ของ Docker