Docker : “Senin de lokalinde çalışsın”

Ahmet Burak Küçük
Pia-Team Tech

--

Herkese selam, ilk makaleme hoşgeldiniz 😁 elimden geldiğince Docker’ı anlatmaya çalışacağım. Docker Nedir? Neden kullanılır? Nasıl kullanılır? gibi gibi sorulara cevap vermeye çalışacağım.

Keyifli okumalar. 🥳🥳🥳

Öncelikle Docker’ın kitabi tanımından başlamak gerekiyor. Docker, “konteynerleştirme” olarak da bilinen işletim sistemi seviyesinde sanallaştırma sağlayan bir araçtır. Burada öncelikle dikkatinizi sanallaştırma sağlayan araç cümlesine vermenizi istiyorum. Eğer Docker işletim sistemi seviyesinde bir sanallaştırma yapıyorsa acaba farklı seviyelerde de bir sanallaştırma mümkün mü? sorusu aklınıza gelebilir. Evet mümkün! tabii ki Docker’dan bahsedip de Sanal makinelerden (Virtual Machine) bahsetmemek olmazdı. Büyük resmi görmemizi sağlayabilir.

Sanal makineler gerçek bilgisayarın yazılım uyarlamasıdır aslında. Gerçek bilgisayar kullanırken gördüğünüz her şeyi görürsünüz fakat ortada sanal makineyi kurduğunuz bilgisayar dışında ikinci bir bilgisayar yoktur. Bu aslında tek bir serverde birden fazla uygulamayı aynı anda çalıştırabiliriz demek. AMA! bu sanal makinelerin hepsinin ayrı ayrı ram kapasitesine, disk alanına, işletim sistemi lisansına ihtiyaç var demek oluyor. Bir diğer dezavantajı da yeterince hızlı olmamaları ve taşınabilirlik yeterince kolay değil.

# Peki Container’lar?

Konteynerler ise kabaca sanal makinelere benziyorlar. En temel farkı ise kurulduğu sunucudaki işletim sistemi seviyesinde bir sanallaştırma yaptığı için çok gelişmiş bir işletim sistemine ihtiyaç duymazlar. Hatta yalnızca 5MB’lık bir Alpine Image’ı dahi iş görebiliyor. Bu durum gereksiz RAM, CPU, Lisans gibi masraflardan kaçınmanızı sağlıyor. Ve konteynerler oldukça hızlılar, taşınabilirler.Burada şöyle bir soru akla gelebilir eğer geliştirme ortamımız windows ya da OSX ise? Windows tarafında da aynı linux da olduğu gibi işliyor olay fakat macos tarafında bir linux vm ihtiyacı herhalükarda doğuyor.

# Ya Docker?

Makalemizin konusu docker olduğu için ileride çok daha detaylı değineceğiz fakat şimdilik docker’ın linux container’larının yönetimini oldukça kolaylaştırdığını bilmemiz gerekiyor. Docker’ın Linux Container’larından en temel farkı ise kullanıcıyı tek bir process’e zorluyor olmasıdır. LXC kullanıcıya oldukça geniş, kapsamlı fonksiyonlar sunmakta fakat docker bu geniş kapsamı daraltıp ilgili container’ın tekrar kullanılması yönünde bir vizyon ile geliştirildiği için fonksiyonları kısıtlama yönünde ilerlemiştir. Docker’ın burada Linux container’ı geliştiren ekibe bağımlı olduğunu fark etmişsinizdir. Docker bunun da önüne geçmek için libcontainer adını verdiği kendi library ile linux çekirdeğindeki konteynerleştirme kısmını soyutlayıp (bir git tabiri ile forklayıp da denebilir ) teknolojiyi kendi ekibi tarafından geliştirme kararı almıştır.

# Terminloloji

Docker

Docker linux ve windowsta çalışan (macos’ta dolaylı yoldan çalışan) container oluşturabildiğimiz, yönetebildiğimiz open-source bir yazılım.

indirmek için kullandığınız işletim sisteminin linkine tıklayıp yönergeleri takip etmeniz yeterli.

Docker for Windows

Docker for Mac

Docker for Linux

Eğer lokalinize yükleme yapmadan devam etmek isterseniz Play with Docker sitesinden create new instance diyip yeni bir docker yüklü instance oluşturabilirsiniz.

Gerekli kontrolleri sağlamak için terminale:
$ docker -v
yazmanız yeterli. Şöyle bi ekran ile karşılaşmış olmalısınız:

# Image

Container’ın oluşurken baz alacağı içerisinde bütün bağımlılıkların yazıldığı, ilgili ayarların yazıldığı text dosyası.

lokalinizdeki image’ları görmek isterseniz:

$ docker image ls

lokalinizde herhangi bir image yoksa komutun çıktısı olmayacaktır ama dilerseniz ilk image’ı lokalimize alalım.

$ docker image pull redis

$ docker image pull redis

bir diğer image olarak alpine’ı indirelim

$ docker image pull alpine

$ docker image pull alpine

Ve şimdi tekrar lokalimizdeki image’lara bakmaya çalışalım:

lokalimizdeki docker image’larımız

burada dikkatinizi çekmek istediğim noktalardan birisi pull dedikten sonra redis ve alpine gibi net ifadeler yazdık. Bu image’lar official image yani image’ı yayınlayan kullanıcının resmi ve onaylanmış olduğunu gösterir. Bir de bunların haricinde unofficial image’lar var. Bunlar sizin benim gibi insanlar tarafından yayınlanmışlardır.
Docker cli ile birlikte aslında biz terminal komutları yazıyoruz en temelinde her ne kadar bir kaç tane docker spesifik komut olsa da terminalde kullandığınız komutları kullanabilirsiniz. Örneğin lokalinizdeki image sayısı çok fazla ve siz filtrelemek istiyorsanız
-- filter= gibi bir ifade kullanabilirsiniz.

Dolayısıyla herhangi bir image’ı silmek istersseniz:
$ docker image rm <ID>
şeklinde bir ibare girebilirisiniz
.

# Container

işletim sistemi seviyesinde sanallaştırılmış, diğerlerinden izole edilmiş, processlerin her biri. Aslında burda image’ların çalıştırılmış hali de denebilir.
Bir image ile birden fazla container oluşturulabilir, bu durum VM — VM Template ilişkisine benzer durumda fakat tabii ki en büyük farkı bir image’dan container oluşturmuk VM Template’den VM oluşturmaya göre çok çok daha hızlı ve aynı zamanda çok hafif.

Örneğin elimizde bir image var ve biz bunu run etmek istiyorsak

$ docker run <imageName> şeklinde bir ifade yazmanız yeterli olacaktır. Örneğin;
$ docker run redis
dediğimizde eğer lokalimizde redis image’ı yoksa önce pull edip daha sonra ilgili image’ı çalıştırır.

ilk containerımız

lokalimizde hali hazırda çalışan containerların listesini görmek istiyosak

$ docker container ls
veya
$ docker ps

lokalimizdeki çalışan/çalışmayan bütün containerları görmek istiyorsak

$ docker container ls -a
veya
$ docker ps -a

Çalışan bir containerın içine girip container terminaline ulaşmak istiyorsak:
$ docker container exec -it <container-name-or-id> bash
artık kendi terminalinizde değil container terminalindesiniz. Burada exec komutu ile yeni bir bash prosesi başlattığınız için yeni oluşturduğunuz bash’te exit komutunu verirseniz container’ı sonlandırmış olmazsınız. Eğer ilgili container’ı sonlandırmak istiyorsanız :
$ docker container stop <container-name-or-id>

Eğer container’ı silmek istiyorsanız :
$ docker container rm <container-name-or-id>

Uygulama

Bu bölümde beraber uygulamalar yapıp yukarıda yazdıklarımızı pekiştirmek istiyorum. Ben uygulamayı play with docker üzerinden oluşturduğum bir instance üzerinden yapacağım. Sizler de isterseniz kullanabilirsiniz.

öncelikle bir nginx image’ı run etmek istiyorum. Şu şekilde:
$ docker run nginx
bu komutu girdiğiniz zaman docker lokalinizde eğer nginx image’ı yoksa önce pull eder sonra run eder.

$ docker ps dediğimiz zaman nginx’in çalışır olduğunu görürüz fakat biz kendi lokalimizden bu nginx’ erişemeyiz. Çünkü container’ın herhangi bir portu dışarıya açık değil. Bunun için kullanmamız gereken şey -p tagı. (CTRL + C ile oluşan nginx container’ından çıkıp)
$ docker run -p 80:80 nginx yazdığımız zaman docker nginx’i çalıştırığ container içindeki 80 portunu benim lokalimdeki 80 portuna bağlar. Bu sayede ben gidip browser da localhost:80 yazdığım zaman karşıma nginx’in çalıştığını görmüş olacağız. Fakat burada da şöyle bir sorun ortaya çıkıyor. Aslında tam sorun değil fakat istenilmeyen durum denebilir. Komut çalıştığında docker beni direkt çalıştırdığın nginx container’ının içine, terminaline yönlendiriyor.

Bunun yerine ben terminaldeki işlerime devam edeyim istiyorum. Bu durumu -d tagı ile çözebiliriz.
$ docker run -p 80:80 -d nginx
burada docker yeni bir nginx container’ı oluşturup 80 portlarını eşleştiriyor fakat aynı zamanda bunu -d tagı sayesinde arka planda çalıştırtmaya devam ettiriyor. Size ise kendi terminalinizde sadece oluşan container’ın ID’sini gösteriyor.

Şimdi ise ben oluşan nginx container’ındaki default yüklenen index.html i düzenleyip container’ı durdurmak/silmek istiyorum. Daha sonra ise bu düzenlenen dosyanın hangi şartlarda silinmesi ve silinmemesi neler yapabileceğime bakmak istiyorum. Oluşturduğumuz nginx container’ının içine
$ docker exec -it <nginx-container-id> bash
komutu ile girebiliyorum. buradan
$ cd usr/share/nginx/html
komutunu yazıp ilgili html dosyasının bulunduğu dizine gidiyorum. Bu noktada vim’e ihtiyacımız olacak bunun için önce
$ apt-get update
daha sonra ise
$ apt-get install vim -y
diyerek vim’i containerımızın içine yüklüyoruz. Ardından
$ vim index.html
komutu ile index html’i istediğiniz şekilde düzenleyebilirsiniz. (Vim den değişiklikleri kaydedip çıkmak için ESC ye basıp :wq yazmanız yeterli ) Buradan sonra container’ı durdurup tekrar çalıştırırsanız veriler kaybolmaz fakat eğer container’ı silerseniz index.html de yaptığınız değişiklikler kaybolur. Peki biz bu durumun da önüne geçmek istiyorsak ne yapmalıyız? Karşımıza Volume kavramı çıkıyor. Örneğin ben masaüstümde Proje01 diye bir klasör açıp içerisine index.html adına bir dosya kaydediyorum. İçerisinde bir kaç değişiklik yaptıktan sonra terminalden Proje01 klasöürünün olduğu dizine gidip şu komutu çalıştırıyorum.
$ docker run --name projem -v $(pwd):/usr/share/nginx/html -p 80:80 -d nginx
(Burdaki pwd komutu terminalde bulunduğumuz dizinin yolunu bize verir.) Browserdan localhost:80 yazdığımız zaman bizim değiştirdiğimiz index.html dosyasının çalıştığını görürüz. Bu aşamadan sonra container’ı silseniz dahi lokalinizdeki Proje01 klasörünin içinde ki index.html silinmeyecektir. Ve bundan sonra oluşturacağınız her container’a verileriniz -v tagı sayesinde yüklenecektir.

# Dockerfile

Bir de birlikte Dockerfile örneği yapmak istedim. Dockerfile’ın text temelli olması container oluşturmayı oldukça yönetilebilir ve kolay kılıyor. Örneğin bir Dockerfile oluşturup içerisine şunları ekleyelim.

Örnek bir Dockerfile

ve daha sonra ilgili dizinde terminali açtığımız zaman:
$ docker build . -t <containerName>
komutu ile birlikte oluşturduğumuz image’ı build edip bir container oluşturabiliyoruz. Dockerfile’ın içindeki anahtar kelimeleri kısaca özetleyecek olursak;
FROM : terminalde docker pull <imageName> yapmamız ile aynı görevi görüyor. yanına yazdığımız image’ı pull etmemizi sağlıyor.
RUN : Dockerfile build edildiği sırada çalıştırmasını istediğimiz komutları çalıştırır.
COPY: source ve destination alır. belirttiğiniz source yolundaki klasörleri destination yoluna kopyalar.
WORKDIR: terminal komutlarına aşina olanlar için cd komutu ile aynı işlevi görmektedir. Yanına yazdığımız dosya yolunun içine girmesini sağlar.
CMD: oluşturulan Dockerfile build edildikten sonra oluşan image’ı container haline getirirken execute eden komuttur. Örneğin CMD komutunun yanına yazdığınız komut terminale $ docker build . -t myImageName yazdığımızda çalışmaz. Yalnızca oluşan image’ın $ docker run myImageName dediğimizde CMD komutunun yanına yazdığımız dizi içerisindeki komutlar sırayla çalışır.
ENV: pull ettiğimiz image ile bir container oluşturulma aşamasında bazı zorunlu “parametreler” almak isteyebilir. Örneğin MySql image’ını pull ettikten sonra docker run ile container’ını oluştururken mutlaka MYSQL_ROOT_PASSWORD değerini atamanız gerekir. Eğer sizinde Image’ınız bunun gibi zorunlu değişkenlerin atanması gerekiyorsa hatta bu değişkenleri aynı Dockerfile’ın bir RUN komutunda kullanmanız gerekiyorsa ENV komutunu kullanmanız gerekmektedir.
EXPOSE: container’ımıza hangi porttan ulaşılabileceğini söylediğimiz yer.

Yazının çok uzadığını fark ederek. docker-compose ve network konularını bir sonraki yazıma bırakıyorum.

Bu makaleyi yazmak için beni oldukça motive eden Abdullah Kaya ‘ya Teşekkürlerimle.

Kaynaklar:

https://github.com/gkandemi/docker

https://www.amazon.com/Docker-Deep-Dive-Nigel-Poulton-ebook/dp/B01LXWQUFF

--

--