Docker bind mount, host makinedeki bir dosyayı ya da dizini olduğu gibi container içine bağlar. Volume'dan farkı, verinin Docker'ın kendi depolama dizininde değil, sizin seçtiğiniz bir host yolunda durması ve hem host üzerindeki süreçlerin hem de container'ın aynı dosyaları aynı anda değiştirebilmesidir. Bir yapılandırma dosyasını sunucuda düzenleyip container'ın görmesini istiyorsanız, ya da container'ın ürettiği dosyaların doğrudan host dosya sisteminde durmasını istiyorsanız bind mount doğru araçtır.

Bu yazıda bind mount'u bir Linux sunucu açısından ayrıntılı olarak ele alıyoruz: -v ile --mount arasındaki davranış farkı (özellikle kaynak dizin yokken), salt okunur bağlama, dolu bir dizinin üstüne bağlama, özyinelemeli bağlamalar, bind propagation, SELinux etiketleri ve Compose içindeki kısa ve uzun sözdizimi. Volume ile bind mount arasındaki temel seçim Docker Compose başlangıç rehberi yazısında özetleniyor; volume'un kendisi ise Docker volume kullanımı yazısında.

Bind mount ne zaman kullanılır?

Belgeler bind mount için üç tipik kullanım sayıyor. Birincisi, geliştirme ortamındaki kaynak kodu ya da derleme çıktılarını container ile paylaşmak. İkincisi, container içinde dosya oluşturup bu dosyaların host dosya sisteminde kalıcı olmasını istemek. Üçüncüsü, host üzerindeki yapılandırma dosyalarını container'lara vermek. Docker'ın kendisi de bu son yöntemi kullanır: container'lara varsayılan DNS çözümlemesini, host'taki /etc/resolv.conf dosyasını her container'a bağlayarak sağlar.

Bind mount yalnızca çalışan container'lar için değildir; build sırasında da kaynak kodu build container'ına bağlayıp test, lint ya da derleme yapabilirsiniz. Build tarafındaki bu kullanımı ve COPY ile farkını Docker COPY ve ADD farkı yazısı ele alıyor. Bu yazıdaki her şey çalışan container'lardaki bind mount ile ilgilidir.

Bir sunucuda bind mount'un en sık görüldüğü yerler web sunucusu yapılandırmaları, statik site dosyaları, sertifika dizinleri ve uygulamanın yüklenen dosyaları yazdığı dizinlerdir. Veritabanı dizinleri gibi yoğun yazılan ve host tarafında elle dokunulmaması gereken veriler için ise belgeler volume'u öneriyor; volume'lar Docker CLI ile yönetilebilir ve yedeklemesi daha kolaydır.

Kısıtlar ve güvenlik etkileri

Kısıtlar ve güvenlik etkileri: Varsayılan olarak yazma erişimi vardır., Bağlama daemon'ın çalıştığı host'ta yapılır, istemcide değil., Container host'a sıkı bağlıdır.

Bind mount'un esnekliği bazı sorumlulukları da beraberinde getirir. Belgeler üç konuyu özellikle vurguluyor:

  • Varsayılan olarak yazma erişimi vardır. Container içindeki süreçler host dosya sistemini değiştirebilir; önemli sistem dosyalarını oluşturabilir, değiştirebilir ya da silebilir. Bu, host üzerindeki Docker dışı süreçleri de etkileyebilecek bir güvenlik konusudur. Container'ın yazmasına gerek yoksa readonly ya da ro seçeneğini kullanın.
  • Bağlama daemon'ın çalıştığı host'ta yapılır, istemcide değil. Uzak bir Docker daemon'ına bağlıysanız istemci makinenizdeki dosyaları container'a bind mount ile veremezsiniz. Docker Desktop'ta daemon bir Linux sanal makinesinde çalışır ve Desktop bunu kendi mekanizmalarıyla şeffaf biçimde çözer.
  • Container host'a sıkı bağlıdır. Bind mount, host dosya sisteminde belirli bir dizin yapısının bulunmasına dayanır. Aynı dizin yapısı olmayan başka bir host'ta container başarısız olabilir.

İkinci madde, sunucuyu dizüstü bilgisayarınızdan yönetiyorsanız önemlidir. docker context ile uzak bir sunucuya bağlanıp -v ./site:/usr/share/nginx/html yazdığınızda kaynak yol sizin bilgisayarınızda değil sunucuda aranır. Uzak engine yönetimi için Docker context ile uzak sunucu yönetimi yazısına bakabilirsiniz.

-v ve --mount sözdizimi, kaynak yokken davranış

Bind mount oluşturmak için de --mount ya da --volume (-v) kullanılır. Belgeler genel olarak --mount öneriyor, çünkü daha açık ve tüm seçenekleri destekliyor.

bash
docker run --mount type=bind,src=<host-path>,dst=<container-path>
docker run --volume <host-path>:<container-path>

İki bayrak arasındaki en önemli pratik fark, host'taki kaynak yol yokken ortaya çıkar. -v ile henüz var olmayan bir yolu bağlarsanız Docker bu yolu host üzerinde sizin için oluşturur ve her zaman dizin olarak oluşturur. Bu, bir dosyayı bağlamak isterken yazım hatası yaptığınızda host'ta o adda boş bir dizin oluşması ve container'ın yapılandırma dosyası yerine bir dizin görmesi demektir. Daemon'ın kaynak dizini oluşturma izni yoksa dizini container'ı başlatmadan önce kendiniz oluşturmalısınız.

--mount ise varsayılan olarak kaynak yolu oluşturmaz ve hata verir. Belgelerdeki örnek çıktı şöyledir:

bash
docker run --mount type=bind,src=/dev/noexist,dst=/mnt/foo alpine
docker: Error response from daemon: invalid mount config for type "bind": bind source path does not exist: /dev/noexist.

Kaynak dizinin yoksa oluşturulmasını istiyorsanız bind-create-src seçeneğini ekleyebilirsiniz. Engine 29 sürüm notlarına göre bu seçenek 29.3.0 sürümünde --mount bayrağına eklendi; bu yüzden daha eski bir CLI ile çalışmayabilir.

bash
docker run --mount type=bind,src=/home/user/mydir,dst=/mnt/foo,bind-create-src alpine

Sunucuda hata vermesini tercih etmeniz genellikle daha güvenlidir: yanlış yazılmış bir yol sessizce boş bir dizin üretmek yerine container'ın başlamamasına yol açar ve hatayı hemen görürsünüz. --mount için geçerli seçenekler şunlardır:

--mount seçeneğiAnlamı
source, srcHost'taki dosya veya dizin; mutlak ya da göreli yol olabilir
destination, dst, targetContainer içindeki yol; mutlak olmalı
readonly, roSalt okunur bağlar
bind-propagationBind propagation ayarını değiştirir
bind-create-srcKaynak dizin yoksa host üzerinde oluşturur
bind-recursiveAlt bağlamaların nasıl ele alınacağını belirler (yalnızca --mount)

-v tarafında üçüncü alan virgülle ayrılmış seçenek listesidir: ro (readonly), SELinux için z ve Z, propagation için de rprivate (varsayılan), private, rshared, shared, rslave ve slave. Belgelerdeki iki örnek aynı bağlamayı iki sözdizimiyle yazar:

bash
docker run --mount type=bind,src=.,dst=/project,ro,bind-propagation=rshared
docker run -v .:/project:ro,rshared

Bind mount ile container başlatmak

Belgelerdeki senaryoda source adlı bir proje dizini var ve derleme çıktıları source/target/ altına yazılıyor. Amaç, her derlemede container'ın yeni çıktıyı /app altında görmesi. Komut source dizininin içinden çalıştırılır; $(pwd) Linux ve macOS'ta o anki çalışma dizinine genişler:

bash
docker run -d \
  -it \
  --name devtest \
  --mount type=bind,source="$(pwd)"/target,target=/app \
  nginx:latest

docker inspect devtest çıktısının Mounts bölümünde türün bind olduğunu, kaynak ve hedef yolları, bağlamanın okuma yazma olduğunu ve propagation değerinin rprivate olduğunu görürsünüz. Belgelerdeki çıktı:

json
"Mounts": [
    {
        "Type": "bind",
        "Source": "/tmp/source/target",
        "Destination": "/app",
        "Mode": "",
        "RW": true,
        "Propagation": "rprivate"
    }
],

Volume çıktısından farklı olarak burada Name ve Driver alanları yoktur, çünkü bind mount Docker'ın yönettiği bir nesne değildir. docker volume ls bind mount'ları listelemez ve docker volume prune onlara dokunmaz; host'taki dizinin yaşam döngüsü tamamen sizin sorumluluğunuzdadır.

Dolu bir dizinin üstüne bağlamak

Container içinde dosya bulunan bir dizine bind mount yaparsanız dizinin mevcut içeriği bağlama tarafından örtülür. Bu, imajı yeniden derlemeden uygulamanın yeni bir sürümünü denemek için faydalı olabilir, ama aynı zamanda şaşırtıcı da olabilir. Belgeler özellikle bu davranışın volume'lardan farklı olduğunu belirtiyor: boş bir volume bağlandığında container içindeki dosyalar volume'a kopyalanır, bind mount'ta ise böyle bir kopyalama yoktur; host dizini boşsa container o dizini boş görür.

Belgelerdeki uç örnek, container'ın /usr dizinini host'taki /tmp ile değiştirir. Nginx'in çalıştırılabilir dosyası artık görünmediği için container oluşturulur ama başlamaz:

bash
docker run -d \
  -it \
  --name broken-container \
  --mount type=bind,source=/tmp,target=/usr \
  nginx:latest

docker: Error response from daemon: oci runtime error: container_linux.go:262:
starting container process caused "exec: \"nginx\": executable file not found in $PATH".

Gerçek hayatta bu genellikle daha masum bir biçimde olur: bir web sunucusu imajının içerik dizinine boş bir host dizini bağlarsınız ve varsayılan sayfa yerine boş bir dizin listesi ya da hata görürsünüz. Bağlamayı kaldırıp örtülen dosyaları yeniden göstermenin kolay bir yolu yoktur; en iyi seçenek container'ı bağlama olmadan yeniden oluşturmaktır.

Salt okunur bind mount

Bazı geliştirme senaryolarında container'ın bind mount'a yazması ve değişikliklerin host'a geri yansıması istenir. Sunucuda ise çoğu zaman yalnızca okuma erişimi yeterlidir. Salt okunur bağlama için --mount tarafında readonly, -v tarafında :ro eklersiniz:

bash
docker run -d \
  -it \
  --name devtest \
  -v "$(pwd)"/target:/app:ro \
  nginx:latest

Bu durumda docker inspect çıktısında "Mode": "ro" ve "RW": false görünür. Container'ı kaldırmak için belgeler docker container rm -fv devtest komutunu kullanıyor.

Özyinelemeli bağlamalar: bind-recursive

Özyinelemeli bağlamalar: bind-recursive: enabled (varsayılan), disabled, writable, readonly

İçinde başka bağlamalar bulunan bir yolu bind mount ile bağlarsanız bu alt bağlamalar da varsayılan olarak bağlamaya dahil edilir. Bağlama salt okunursa Docker Engine alt bağlamaları da salt okunur yapmak için elinden geleni yapar; buna özyinelemeli salt okunur bağlama denir. Bu özellik Linux çekirdeği 5.12 veya üstünü gerektirir. Daha eski çekirdeklerde alt bağlamalar varsayılan olarak okuma yazma bağlanır ve bind-recursive=readonly ile salt okunur yapmaya çalışmak hata verir. Bu davranış yalnızca --mount ile ayarlanabilir:

bind-recursive değeriDavranış
enabled (varsayılan)Çekirdek 5.12 veya üstündeyse salt okunur bağlama özyinelemeli salt okunur olur, değilse alt bağlamalar okuma yazmadır
disabledAlt bağlamalar yok sayılır, bağlamaya dahil edilmez
writableAlt bağlamalar okuma yazmadır
readonlyAlt bağlamalar salt okunurdur; çekirdek 5.12 veya üstü gerekir

Bu konu özellikle bir dizini salt okunur bağladığınız halde altındaki bir bağlama noktasına yazılabildiğini fark ettiğinizde önem kazanır. Eski bir çekirdekte çalışan sunucularda salt okunur bağlamanın alt bağlamaları kapsamayabileceğini aklınızda tutun. Ayrıca Engine 29.0.0 sürüm notları, artık kullanımdan kaldırılmış olan bind-nonrecursive seçeneğinin --mount bayrağından çıkarıldığını kaydediyor; eski betiklerinizde bu seçenek varsa bind-recursive=disabled ile değiştirmeniz gerekir.

Bind propagation

Bind propagation: shared, slave, private, rshared

Bind propagation, bir bind mount içinde oluşturulan bağlamaların o bağlamanın kopyalarına yansıyıp yansımayacağını belirler. Varsayılan hem bind mount hem volume için rprivate'tir; yalnızca bind mount'larda ve yalnızca Linux host'larda değiştirilebilir. Belgeler bunun ileri düzey bir konu olduğunu ve çoğu kullanıcının hiç ayarlamaya ihtiyaç duymadığını belirtiyor. Docker Desktop'ta mount propagation çalışmaz.

AyarAnlamı
sharedOrijinal bağlamanın alt bağlamaları kopyalara, kopyalarınkiler de orijinale yansır
slaveTek yönlü: orijinaldeki alt bağlama kopyada görünür, kopyadaki orijinalde görünmez
privateHiçbir yönde yansıma olmaz
rsharedshared gibi, iç içe bağlama noktalarını da kapsar
rslaveslave gibi, iç içe bağlama noktalarını da kapsar
rprivateVarsayılan; iç içe noktalar dahil hiçbir yönde yansıma olmaz

Propagation ayarlamadan önce host dosya sisteminin bunu desteklemesi gerekir. Belgelerdeki örnek aynı target/ dizinini iki kez bağlar; ikinci bağlama hem salt okunur hem rslave'dir. Bu durumda /app/foo/ oluşturduğunuzda /app2/foo/ da görünür:

bash
docker run -d \
  -it \
  --name devtest \
  --mount type=bind,source="$(pwd)"/target,target=/app \
  --mount type=bind,source="$(pwd)"/target,target=/app2,readonly,bind-propagation=rslave \
  nginx:latest

SELinux etiketi: z ve Z

SELinux kullanan dağıtımlarda bind mount'a z ya da Z seçeneği ekleyerek bağlanan host dosyasının veya dizininin SELinux etiketini değiştirebilirsiniz. z, içeriğin birden fazla container arasında paylaşıldığını; Z ise içeriğin özel ve paylaşılmayan olduğunu belirtir. Bu seçenekler host'taki dosyanın kendisini etkiler ve Docker'ın kapsamı dışında sonuçlar doğurabilir.

bash
docker run -d \
  -it \
  --name devtest \
  -v "$(pwd)"/target:/app:z \
  nginx:latest

Compose içinde bind mount

Compose'da servis volumes alanındaki kısa sözdiziminde ilk alan bir host yolu ise bağlama bind mount olur. Adlandırılmış volume ile karışmaması için göreli yollar her zaman . ya da .. ile başlamalıdır; göreli yol Compose dosyasının bulunduğu dizine göre çözülür. Erişim modu alanında rw (varsayılan), ro, z ve Z kullanılabilir; SELinux seçenekleri SELinux olmayan platformlarda yok sayılır.

yaml
services:
  web:
    image: nginx:latest
    volumes:
      - ./site:/usr/share/nginx/html:ro
      - ./nginx.conf:/etc/nginx/nginx.conf:ro

Bu dosyayı docker compose config ile doğruladığımızda, Compose kısa sözdizimini uzun biçime çevirdi: her satır type: bind, göreli kaynak yerine Compose dosyasının klasörüne göre çözülmüş mutlak bir yol ve read_only: true olarak yazıldı. Göreli yolların bu şekilde çözülmesi, belgelerin belirttiği gibi yalnızca yerel bir container çalışma ortamına dağıtım yapan Compose için desteklenir.

Kısa sözdizimiyle ilgili kritik bir ayrıntı daha var: belgelere göre kısa sözdizimi, host'taki kaynak yol yoksa orada bir dizin oluşturur. Bu, eski docker-compose ile geriye dönük uyumluluk için korunan bir davranıştır ve -v bayrağındaki sorunun aynısını doğurur. Örneğin ./nginx.conf dosyası henüz yokken servisi başlatırsanız host'ta nginx.conf adlı boş bir dizin oluşabilir. Bunu önlemek için uzun sözdiziminde create_host_path: false kullanın:

yaml
services:
  web:
    image: nginx:latest
    volumes:
      - type: bind
        source: ./site
        target: /usr/share/nginx/html
        read_only: true
        bind:
          create_host_path: false
      - type: bind
        source: ./uploads
        target: /data/uploads
        bind:
          selinux: z

Uzun sözdizimindeki bind alt alanı üç ayar kabul eder: propagation, varsayılanı true olan create_host_path ve z ya da Z değeri alan selinux. read_only ise bağlamayı salt okunur yapar. Bu dosya da doğrulamadan geçti ve config çıktısında create_host_path: false ve selinux: z ayarları korunmuş olarak göründü.

Compose'da bir host yolunu volume gibi adlandırmak isterseniz local sürücünün driver_opts alanıyla kurulan bir desen de vardır. Bu "adlandırılmış bind mount" deseni Docker volume yedekleme ve taşıma yazısında örnekleniyor.

Sık yapılan hatalar

  • Dosya yerine dizin oluşması: -v ya da Compose kısa sözdizimiyle var olmayan bir dosya yolunu bağlarsanız host'ta dizin oluşur. Dosyayı önceden oluşturun ya da --mount veya create_host_path: false kullanın.
  • Boş host dizininin imaj içeriğini örtmesi: Bind mount, volume gibi kopyalama yapmaz. Boş bir dizini içerik dizinine bağlarsanız container boş görür.
  • Uzak daemon'da yerel yol beklemek: Kaynak yol daemon'ın çalıştığı host'ta aranır.
  • Yazılabilir sistem dizini bağlamak: Container host dosyalarını değiştirebilir; gerekmeyen her yerde ro kullanın.
  • Z seçeneğini geniş dizinlerde kullanmak: Sistem dizinlerinde Z host'u kullanılamaz hale getirebilir.
  • Göreli yolu nokta olmadan yazmak: Compose'da site:/usr/share/nginx/html bir named volume olarak yorumlanır; bind mount için ./site yazın.

Sık Sorulan Sorular

Bind mount mı volume mü kullanmalıyım?

Dosyalara host tarafından da erişmeniz ya da düzenlemeniz gerekiyorsa bind mount, verinin Docker tarafından yönetilmesi, kolay yedeklenmesi ve host dizin yapısından bağımsız olması gerekiyorsa volume kullanın. Belgeler kalıcı uygulama verisi için volume'u tercih edilen mekanizma olarak tanımlıyor.

Bind mount'ta dosya izinleri neden sorun çıkarıyor?

Bind mount host dosyasını olduğu gibi bağladığı için dosyanın host'taki sahibi ve izinleri container içinde de geçerlidir. Container'daki süreç farklı bir kullanıcıyla çalışıyorsa yazma izni olmayabilir. Kullanıcı ve grup yönetimi için Linux kullanıcı ve grup yönetimi yazısına bakabilirsiniz.

Tek bir dosyayı bind mount ile bağlayabilir miyim?

Evet, kaynak ve hedef bir dosya olabilir; belgelerdeki Compose örneği de bir soket dosyasını bağlıyor. Dosyanın host'ta önceden var olduğundan emin olun, aksi halde -v veya kısa sözdizimi o yolda bir dizin oluşturur.

Swarm servislerinde bind mount kullanabilir miyim?

Kullanılabilir, ancak belgelere göre servislerde SELinux etiketleri ve :ro yok sayılır. Ayrıca bind mount host dizin yapısına bağlı olduğundan, görevin çalıştığı her düğümde aynı yolun bulunması gerekir. Swarm için Docker stack deploy yazısına bakın.

Marka adları sahiplerine aittir. Bu yazı bağımsız bir teknik rehberdir; adı geçen kuruluşlarla bayilik veya ortaklık ilişkisi bulunduğu anlamına gelmez.

İlgili içerikler: Docker öğrenme rehberi · Docker volume kullanımı · tmpfs ve image mount · Volume yedekleme ve taşıma · Docker context · Docker Compose başlangıç rehberi