Şimdiye kadar yaptığımız birçok örnekte Docker Container’ları hep servis tipi (Web sunucu, yük dağıtıcı, Cache, Database) işler için kullandık. Docker Container’lar, servis tipi işlerde kullanılmanın yanında Batch olarak adlandırabileceğimiz, zamanlanarak veya isteğe bağlı çalışacak bir şekilde de kullanılabilirler. Soru / Cevap serimizin bu bölümünde Docker’ı Batch tipi işlerde kullanmamıza yardımcı olarak konseptleri ve teknikleri tanıyacağız.

Öncelikle Container başlatıldıktan sonra (Container ayakta olsa da olmasa da) Host’tan Container’a ve Container’dan Host’a nasıl dosya kopyalayabileceğimizi göreceğiz.

Bu özelliklerin hangi amaçlarla kullanılabileceğine bu blog boyunca yer yer değineceğiz fakat asıl olarak ilerleyen blog’larda bunu referans vererek işlemler yapacağız ve daha fazla kullanım alanı tanımlamış olacağız.

Önemli Uyarı

Bu blog’da yapılan işlemlerde Docker CLI Native olarak Linux üzerinde koşturulmuştur. Docker for Windows ve Docker for Mac kurulumlarında fonksiyonlar, bu blog’da belirtildiği gibi çalışmayabilir.

Soru 3

Belirli bir klasöre konan dosyaları işleyen ve bu işlemlerden elde edilen bilgileri toplayarak öğrenen bir uygulamam var (örneğin bir Yapay Zeka uygulaması). Bu uygulama güncel dosyayı işlerken, önceden işlediği dosyalardaki bilgilere de ihtiyaç duyuyor. Dolayısıyla Container’ı Host’tan her yeni dosya geldiğinde yeniden başlatmak önceki dosyaların tekrar tekrar işlenmesini gerektireceği için performans problemi yaratmakta. Container çalışırken Host üzerinden Container içine nasıl dosya kopyalayabilirim?

Demo Ortamının Yaratılması

Gösterimi yapabilmek için Container içindeki /opt/my-app/data/input klasörüne eklenen dosyaları alıp işleyerek /opt/my-app/data/processed klasörüne taşıyan bir Docker Container yaratalım. Gösterimi kolaylaştırmak için yukarıda Yapay Zeka olarak tanımlanan uygulamamız basit bir Bash Script’i olsun ve dosyanın işlenmesi işlemini sadece ekrana bir mesaj bastırarak simüle edelim.

Belirtilen adımları siz de takip etmek istiyorsanız bu blog için hazırladığım Github Repository‘sini klonlayıp komut satırında repo içindeki Question-3/Demo klasörüne geçin. Yolumuz düşerse ve beğendiysek Github’da Star vermeyi ihmal etmeyelim :-)

Bu klasörde içeriği aşağıda verilen, program adlı tek servis içeren Docker Compose dosyası ile birlikte Container’a kopyalayacağımız test dosyaları ve Container’ın çalıştırdığı simülasyon programıdır.

Demo $ tree
.
├── data
│   ├── file-0.txt
│   ├── file-1.txt
│   ├── file-2.txt
│   ├── file-3.txt
│   └── file-4.txt
├── docker-compose.yml
└── program
    └── artificial-intelligence.sh

Demo klasöründe iken docker-compose up program komutunu vererek Container’ı çalıştırın. Ekranda aşağıdakine benzer bir çıktı görmelisiniz. Aşağıdaki çıktıdan Creating demo_program_1 görebileceğiniz üzere yaratılan Container’ın ismi demo_program_1 oldu. Önceki Docker Compose blog’undan hatırladığınız üzere Container isminin demo ile başlaması Demo klasöründe olduğumuzdan, program ile devam etmesi servis isminden, sondaki 1 ise ilgili servis için yaratılan ilk Container olmasından dolayıdır. Birazdan Container ismini cevap bölümünde kullanacağız.

Demo $ docker-compose up program
Creating network "demo_default" with the default driver
Creating demo_program_1
Attaching to demo_program_1
program_1  | Started listening on the changes for directory /opt/my-app/data/input
program_1  | Could not found /opt/my-app/data/input, creating the directory
program_1  | Could not found /opt/my-app/data/output, creating the directory
program_1  | Sleeping for 10 seconds for the next run
program_1  | No new files found for this interval
program_1  | Sleeping for 10 seconds for the next run

Cevap 3

Soruyu ve demo ortamını hazırladıktan sonra şimdi cevaba geçebiliriz.

Öncelikle şunu belirtmek gerekir ki, Container çalışırken, Host’tan Container’a dosya kopyalanması aslında çoğunluk durumda kaçınılması gereken ve “Best Practice” olmayan bir durumdur. Tavsiye edilen yöntem, çalışan Container’daki dosyayı değiştirmekten ziyade, ilgili dosyayı veya klasörü Docker Container’a Mount etme veya duruma göre ilgili dosya ile yeni bir Image oluşturma ve bu Image’dan yeni bir Container yaratmaktır. Bu yöntemin neden “Best Practice” olduğunu ilerleyen zamanlarda başka bir blog yazısında tartışabiliriz.

Çok nadir olmakla birlikte, yukarıda belirtilen gibi, bazı özel durumlarda performans ve yönetilebilirliği artırmak için Container çalışırken, Host’tan Container’a dosya kopyalamak gerekebilir.

Host’tan Container’a dosya kopyalama sentaksı aşağıdaki gibidir. Yani ilk parametre Host sistemdeki PATH, sonraki parametre ise Container ID’den sonra konulan iki nokta ve Container içerisindeki PATH’dir.

docker cp [OPTIONS] SRC_PATH|- CONTAINER:DEST_PATH

Örneğin Host içindeki /a/b/c.txt dosyasını 12345678 ID’li Container içindeki /x/y/z.txt dosyası olarak kopyalamak istersek aşağıdaki komutu kullanabiliriz.

docker cp /a/b/c.txt 12345678:/x/y/z.txt

Demo Ortamının Yaratılması adımında zaten programı çalıştırmıştık. Şimdi başka bir terminal açarak yine Question-3/Demo klasörüne geçin. Yapmak istediğimiz şey Demo/data/file-1.txt dosyasını demo_program_1 adlı Container’ın içindeki /opt/my-app/data/input klasörüne kopyalamak olduğu için aşağıdaki iki komuttan birini Container ismi veya ID’si ile kullanabiliriz.

# Relative Path kullanılarak
docker cp ./data/file-1.txt demo_program_1:/opt/my-app/data/input/

# Absolute Path kullanılarak
docker cp `pwd`/data/file-1.txt demo_program_1:/opt/my-app/data/input/

Bu komutlardan birini verdikten sonra Docker Compose ile başlattığınız Program ekranında aşağıdaki gibi Processing file file-1.txt bir çıktı görmelisiniz.

program_1  | Sleeping for 10 seconds for the next run
program_1  | Processing file file-1.txt
program_1  | Sleeping for 10 seconds for the next run
program_1  | No new files found for this interval

Pratik olması açısından dosyanın da gerçekten işlenip işlenmediğini anlamak için /opt/my-app/data/input ve /opt/my-app/data/output klasörlerinin içeriğini görüntüleyelim. Container’ın içine girmeden bunu exec komutu ile yapabiliriz. Gördüğünüz gibi /opt/my-app/data/input klasörüne kopyaladığımız file-1.txt adlı dosya /opt/my-app/data/output klasörüne taşındı.

Demo $ docker exec demo_program_1 ls /opt/my-app/data/input
Demo $ docker exec demo_program_1 ls /opt/my-app/data/output
file-1.txt

Soru 4

Zaman zaman Container’a yaptırdığınız bir işlemin sonucunda oluşan dosyayı Host’a kopyalamak isteyebilirsiniz. Host’tan Container’a dosya kopyalamanın aksine bu durum “Best Practice” ile çok fazla ters düşen bir durum değildir. Continuous Integration Pipeline’ınınızda Container içinde koşturduğunuz Unit, Integration ve Acceptance test sonuçlarınızı Jenkins veya başka bir araçta ilgili Plugin’in raporlaması için Export etmek gibi bir ihtiyacınız olabilir.

Çalıştırıldığında 4K boyutunda Random sayılardan oluşan bir dosya oluşturan bir Container’ım var. Bu Container sonlandıktan sonra oluşan dosyayı Host üzerindeki bir klasörün içine nasıl kopyalayabilirim?

Demo Ortamının Yaratılması

Gösterimi yapabilmek için Container içinde öncelikle /opt/my-app/data/random-4K.bin isimli bir dosya oluşturan uygulamayı Bash Script’i ile yaratalım. Dosyanın başarılı oluşturulması üzerine Container çıkış yapsın. Container içindeki dosyanın boyutunu Host üzerinden görerek yaptığımız Setup’ı doğrulayabiliriz.

Belirtilen adımları siz de takip etmek istiyorsanız bu blog için hazırladığım Github Repository‘sini klonlayıp komut satırında repo içindeki Question-4/Demo klasörüne geçin. Yolumuz düşerse ve beğendiysek Github’da Star vermeyi ihmal etmeyelim :-)

Bu klasörde içeriği aşağıda verilen, program adlı tek servis içeren Docker Compose dosyası ile birlikte Container’da çalıştırılacak ve data üretecek simülasyon programıdır.

Demo $ tree
.
├── data
├── docker-compose.yml
└── program
    └── random-file-generator.sh

Demo klasöründe iken docker-compose up program komutunu vererek Container’ı çalıştırın. Ekranda aşağıdakine benzer bir çıktı görmelisiniz. Aşağıdaki çıktıdan Creating demo_program_1 görebileceğiniz üzere yaratılan Container’ın ismi yine demo_program_1 oldu ve dosya oluşturma işlemi bittikten sonra Container çıkış yaptı.

Demo $ docker-compose up program
Creating network "demo_default" with the default driver
Creating demo_program_1
Attaching to demo_program_1
program_1  | Up and running to generate a 4K-size random file
program_1  | Could not find /opt/my-app/data, creating the directory
program_1  | Outputing the file to /opt/my-app/data/random-4K.bin
program_1  | 8+0 records in
program_1  | 8+0 records out
program_1  | 4096 bytes (4.1 kB, 4.0 KiB) copied, 8.3719e-05 s, 48.9 MB/s
demo_program_1 exited with code 0

Cevap 4

Soruyu ve demo ortamını hazırladıktan sonra şimdi cevaba geçebiliriz.

Container’dan Host’a dosya kopyalama sentaksı aşağıdaki gibidir. Yani ilk parametre Container ID’den sonra konulan iki nokta ve Container içerisindeki PATH, ikinci parametre ise Host sistemdeki PATH’dir.

Demo $ docker cp [OPTIONS] CONTAINER:SRC_PATH DEST_PATH|-

Örneğin 12345678 ID’li Container içindeki /x/y/z.txt dosyasını Host’a /a/b/c.txt dosyası olarak kopyalamak istersek aşağıdaki komutu kullanabiliriz.

Demo $ docker cp 12345678:/x/y/z.txt /a/b/c.txt 

Demo Ortamının Yaratılması adımında zaten programı çalıştırmıştık. Şimdi başka bir terminal açarak yine Question-4/Demo klasörüne geçin. Yapmak istediğimiz şey demo_program_1 adlı Container içinde bulunan /opt/my-app/data/random-4K.bin dosyasını Host üzerindeki /tmp/random-4K.bin dosyası olarak kopyalamak olsun. Bu durumda aşağıdaki iki yöntemden birini kullanabiliriz.

1.

Demo $ docker cp demo_program_1:/opt/my-app/data/random-4K.bin /tmp/random-4K.bin

2.

Demo $ docker ps -a
CONTAINER ID        IMAGE               COMMAND                CREATED             STATUS                      PORTS               NAMES
3659d8a028b1        ubuntu:16.04        "/usr/app/runner.sh"   1 minutes ago      Exited (0) 1 minutes ago                       demo_program_1
Demo $ docker cp 3659d8a028b1:/opt/my-app/data/random-4K.bin /tmp/random-4K.bin
Demo $ ls -lrt /tmp/random-4K.bin
-rw-r--r-- 1 vagrant vagrant 4096 Feb 24 21:31 /tmp/random-4K.bin

Kapanış

Bu blog’daki yer verilen bilgiler devam eden blog’larda yapılacak çalışmalara yardımcı olacaktır. Bir sonraki blog’da buluşmak üzere.