Docker

Hocanın dökümantasyonu https://github.com/gkandemi/docker

Docker Nedir? Nasıl Kullanılır?

Proses izolasyonu sağlar. Çalıştırılan programın ortamı, ortamın da uygulamayı etkilemesini engeller.

Container kendine ait prosesleri, servisleri ve ağı olan. Aymı işletim sistemi veya virtual machine (VM) üzerinde çalışan, diğer ortamlardan izole edilmiş ortamlardır.

Docker, container ile işletim sistemi kerneli arasında bağlantıyı sağlar.

Docker container işini kolaylaştırmak için bize birçok high level araç sunar.

Docker LXC container türü kullanır.

Windows üzerine docker yüklendiğinde Docker araya otomatik olarak linux VM yükler ve containerları kendisinin yüklediği linux VM üzerinden yönetir.

Container çalışan yapılardır. Kaynak kod image içinde.

image içinde birçok yapıyı barındırabilir. Containerin nasıl çalışacağını anlatır. image çalıştırıldığında container oluşturur. Bu image dosyaları genelde bulutta tutulur. Docker için genelde Docker Hub kullanılır. İmageı çalıştırdığımızda elde ettiğimiz proses containerdır. İmage çalışmayı durdurduğunda container ortadan kalkar.

DockerHub üzerinden ubuntu indirmek için docker pull ubuntu. docker imajından ubuntu çalıştırmak için docker run ubuntu bilgisayarda yoksa image dosyasını kendisi çeker ve çalıştırır.

Docker Kurulumu

docker.com adresinden indir ve kur.

DockerHub

https://hub.docker.com/

dockerhub>"Explore" filtrelemede

  • "verified publisher": kendi yayıncısı tarafından dockerize edildiği ve yüklendiği image dosyaları.
  • "offical images": büyük şirketlerin ürünlerinin docker tarafından dockerize edildiği ve yüklendiği image dosyaları.

İmage Çekme ve Açma

Terminale

docker pull ubuntu
yazdık. Dockerhub üzerinden ubuntuyu çekti

Terminale

docker pull mongo
yazdık. Dockerhub üzerinden mongoyu çekti

docker images
yazdığımızda indirmiş olduğumuz docker images listelenir

docker run redis
yazdığımızda redis lokalde varsa çalışır. Yoksa image indirilir ve çalıştırılır. Çalıştığını terminalde görürüz.

docker run ubuntu
ile ubuntu çalışır ama hemen geri kapanır.

docker run ubuntu sleep 5
ile ubuntu açılır 5 sn bekler ve kapanır.

docker run -it ubuntu
ile ubuntu açılır ve içinde işlem yapılabilir. -it interaktif terminal anlamında.

docker ps
veya
docker conlainer ls
ayakta olan containerları gösterir.

docker ps -a
veya
docker ps --all
veya
docker conlainer ls -a
ayakta olan olmayan tüm containerları gösterir

Container İsimlendirme (--name)

docker run -it --name bash_ubuntu ubuntu
olarak yazdığızda container bash_ubuntu adını alır ve isim altında ayarları da kaydedilir.
docker start bash_ubuntu
yazarak ismini ve özelliklerini kaydettiğimiz container ayağa kaldırılır. Terminalde kaybolur. Arka planda çalışır.
docker stop bash_ubuntu
ile kapatılır.

docker stop <container-name> kısmında <container-name> yerine <container-id> nin baştan ilk 2 ve daha fazla karakteri girilerek de durdurma işlemi yapılabilir. container id lerin ilk kısmında çakışma olursa daha çok karakter girmek gerekir.

docker ps -a
ile listelediğimiz herhangi bir container bu yöntemle açılabilir ve kapatılabilir.

Container Silme (rm) ve Image Silme (rmi)

Container Silme (rm)

docker ps -a
ile listelenenleri silmek için
docker rm <container-name>
yazarak tek tek silebiliriz. docker start ve docker stop kodlarındaki gibi name yerine idnin başı yazılabilir. Id veya name boşluk bırakılarak birden fazla yazılabilir.

docker container rm $(docker container ls -aq)
tüm idleri silme fonksiyonuna taşıyarak tüm containerları siler.

Image Silme (rmi)

docker images
ile listelenenleri silmek için
docker rmi <image-name>
yazarak tek tek silebiliriz.

Tag Nedir?

sürüm?

docker run redis:5
redisin 5. sürümünü indirir ve çalıştırır.

docker image tag <image name veya id> <new tag name>
ile images name değiştirilir.

docker image tag ubuntu my_ubuntu
ile my_ubuntu adında bir ubuntu kopyası elde ederiz.

Detach mode (-d), Attach mode (attach) ve Log

Detach mode: Çalıştırılan imageın oluşturduğu container arka planda çalışsın.

docker run -d redis

Attach mode: detach modda çalışan uygulamayı öne getirir

docker attach <container id veya name>

docker attach bold_meitner
ile bold_meitner isimli container öne getirilir.

Log: detach modda çalışan uygulamanın verdiği logları görmek için kullanılır.

docker container logs <container id veya name>

İnteraktif Terminal (-it)

kullanıcı girişli bir uygulamanız varsa kullanıcı girişini (interaktif terminal) açabilmek için -it flagi kulanılır.

docker run -it gkandemir/interactive-terminal-app

Port Mapping (-p)

docker run -p Dış_port:iç_port image_name

docker run -d 27018:27017 mongo
dışarıdan biz bağlandığımızda 27018 portunu kullanır. docker içinde ise default portu olan 27017 kullanılır. mongoDBCompass mongodb://localhost:27018 ile bu mongoya bağlanabilir.

docker run gkandemir/node-app
yazdığımızda terminalde
Example app listening at http://localhost:3000 
yazar ancak biz buna web tarayıcımızdan erişemeyiz. Çünkü 3000 portu docker içi için geçerlidir.

docker run -p 3001:3000 gkandemir/node-app
yazdığımızda dış portu olan 3001 portuna web tarayıcıya "http://localhost:3001/" yazarak ulaşabiliriz.

Birden fazla container birden fazla portta eş zamanlı çalıştırabiliriz.

Volume Mapping (-v)

Dockerhost üzerinde containerlar stateless olarak çalışır. Yani içindeki bilgileri kayıt etmez. Container durduğunda içindeki bilgiler silinir.

Kaydedilecek bilgiler için volume kullanılır. Bir containerı ayağa kaldırdığımızda kayıt edilecek klasörün adresini docker host üzerinden bildiririz.

docker run -v /opt/data:/data/db mongo
data/db içinde değişiklik olduğunda host içinde opt/data içine kaydet. Çalışırken de buradaki datayı al.

hangi dosyanın veri tutuğunu ve kaydedilmesi gerektiğini dökümantasyondan bulabiliriz.

docker içinde datanın tutulduğu klasörün docker desktop tarafından bilinen ve izin verilmiş bir klasör olması gerekiyor. (windowsta buna gerek yok. Herhangi bir dizini kabul ediyor.)

docker run -v /myData/data:/data/db mongo

Bizim kullandığımız docker versiyonunda volume almaya gerek var mı emin değilim. Her container açtığımda zaten Docker Desktop üzerinde bir volume açılıyor gibi. Belki image her container haline getirilirken bu veriyi de alması için kullanılyor olabilir.

docker inspect <...>

container hakkında bilgi almak için

 docker inspect <container name veya id>
yazıyoruz.

Gelen ekranda "HostConfig" > "Binds" kısmında array olarak volume alınacak dosyalar mevcut. "HostConfig" > "PortBindings" port yönlendirmesini verir.

"Config" > "ExposedPosts" docker içindeki portu verir. "NetworkSettings" > "Ports" iç ve dış portunu verir.

image hakkında bilgi almak için

 docker inspect <image name veya id>
yazıyoruz.

Environment Variable (-e) ve Linkleme (--link)

docker run -e MYSQL_ROOT_PASSWORD=test123 -d mysql
-e den sonraki kısım environment variable

MySQL - phpmyadminproject

docker pull phpmyadmin/phpmyadmin
ile phpmyadmin image indirildi. mysql ile bunu bağlayacağız.

Önce bağlanılacak olan çalıştırılır.

docker run --name mysql-server -p 3306:3306 -e MYSQL_ROOT_PASSWORD=test123 -d mysql

Sonra bağlanacak olan çalıştırılır.

docker run --name pmyadmin -p 8000:80 --link mysql-server:db -d phpmyadmin/phpmyadmin
--link ten sonra bağlanacağı container name ve : sonrası bağlanacağı alias(takma ad) yazılır. Bu kısım dökümantasyonda verilir.

Tarayıcıda http://localhost:8000/ yazıp phpmyadmin açılır. kullanıcı adı: root, pass: test123 yazılarak mysql databasee bağlanılır.

Volume eklenmiş örnek

Önce bağlanılacak olan çalıştırılır.

docker run --name mysql-server -p 3306:3306 -v /opt/data:/etc/mysql/conf.d -e MYSQL_ROOT_PASSWORD=test123 -d mysql

Sonra bağlanacak olan çalıştırılır.

docker run --name pmyadmin -p 8000:80 --link mysql-server:db -d phpmyadmin/phpmyadmin
--link ten sonra bağlanacağı container name ve : sonrası bağlanacağı alias(takma ad) yazılır. Bu kısım dökümantasyonda verilir.

Tarayıcıda http://localhost:8000/ yazıp phpmyadmin açılır. kullanıcı adı: root, pass: test123 yazılarak mysql databasee bağlanılır.

Docker Network Türleri

Dockerda 3 tür default network var: bridge, none, host

bridge network: default olarak kaldırılan image ile aldığımız neteork. Containerlar birbirine bir gateaway üzerinden bağlanıyor.

none network: dışarıdan erişilememesi için tanımlanmış network. Örn: docker run mongo --network=none

host network: docker host üzerinden sadece port numarası eklenerek ulaşılan network Örn: docker run mongo --network=host

Kullanıcı Tanımlı Network

Kullanıcının kendisinin tanımladığı network örn: docker network create --driver bridge --subnet 182.18.0.0/16 --gateway 182.18.0.1 todo-app-network

docker network ls
veya
docker network list
ile tüm network görüntülenebilir.

docker network rm <network-name veya id>
ile network silinebilir.

Normalde mongoDB gibi bir databasei uygulamamıza bağlarken

...
await Mongoose.connect("mongodb://localhost:27017/todos")
...
                  
daki gibi localhost veya sabit bir ip kullanılır. Docker içinde her çalışmada ip sabit kalmadığından biz bunun yerine container name kullanırız.
...
await Mongoose.connect("mongodb://mongo-server:27017/todos")
...
                  

Uygulama Örneği: Aynı Network Üzerinden Containerların Konuşması (NodeJs - MongoDB)

docker pull gkandemir/todo-app
ile hocanın bu iş için oluşturduğu uygulamayı çektik.

Önce networkü oluşturuyoruz:

docker network create --driver bridge --subnet 182.18.0.1/24 --gateway 182.18.0.1 custom-network
--driver ile türünü belirttik. --subnet ile çalışma aralığını --gateway ile gatewayini belirttik. En son custom-network ise bizim ona verdiğimiz adı belirtti.

docker inspect <id>
ile oluşan networkü inceleyebiliriz.

docker run --name mongo-server --net custom-network -d mongo
--name mongo-server kısmı uygulamamızda kullanacağımız container name ile aynı olmalı. --net custom-network bağlanacağı networkü seçtik.

docker run --net custom-network -p 3000:3000 gkandemir/todo-app
--net custom-network ile ağa bağlandık. Dışarıdan da ulaşabilmek için -p 3000:3000 ile port mapping yaptık.

postman ile http://localhost:3000 üzerinden bağlanıp data gönderebiliyoruz.

Bu bağlantıyı link yöntemi ile yapsaydık kod içinde alias tanımlamak gerekecekti.

Kendimize Ait Image Oluşturmak

docker pull ubuntu:18.04
ile ubuntu v18.04 çekildi.
docker run -it ubuntu:18.04
ile ubuntu container içine girildi.

ubuntu içinde terminale

apt-get update
yazarak güncellemeler yapıldı.

apt-get install curl -y
ile curl kuruldu.
curl -sL https://deb.nodesource.com/setup_10.x | bash
ile node.js indirildi.
apt-get install nodejs -y
ile indirilen nodejs kuruldu.

cd opt
mkdir node-app
cd node-app
                  
ile bir dosya oluşturuldu ve içine girildi.

echo 'console.log("nodejsapp from ubuntu ...");' > index.js
ile index.js dosyası oluşturuldu ve içine console.log("nodejsapp from ubuntu ..."); yazıldı.

node index.js
yazdığımızda terminalde
nodejsapp from ubuntu ...
yazısı belirdi. Bu da bize node.jsin ubuntuda düzgün çalıştığını gösterdi.

ubuntuda

history
yazınca o zamana kadar ubuntu terminalinde yaptıklarımızı listeler.
1  apt-get update
2  apt-get install curl -y
3  curl -sL https://deb.nodesource.com/setup_10.x | bash
4  apt-get install nodejs -y
5  ls
6  cd opt
7  ls
8  mkdir node-app
9  ls
10  cn note-app
11  cd note-app
12  ls
13  cd node-app
14  echo 'console.log("nodejsapp from ubuntu ...");' > index.js
15  ls
16  node index.js
17  history
                  

Image Dosyasını Hazırlamak

Bir proje klasörü oluşturduk ve içinde index.js

console.log("Hello from ubuntu")
                  
ve Dockerfile dosyası daha önce aldığımız historiye göre oluşturuldu.
FROM ubuntu:18.04
RUN apt-get update
RUN apt-get install curl -y
RUN curl -sL https://deb.nodesource.com/setup_10.x | bash
RUN apt-get install nodejs -y
COPY . /opt/node-app/
# Yukarıdaki kısım imageden container oluşturmak için
# aşağıdaki kısım oluşan containerın çalışma anında çalışacak kısım.
# CMD [ "node", "/opt/node-app/index.js" ]
WORKDIR /opt/node-app/
CMD [ "node", "index.js" ]
                  
oluşturuldu.

Dockerfile dosyasında önce containerın açılacağı image FROM ile belirlendi. Sonra bunun içindeki işlemler RUN ile oluşturuldu. COPY . /opt/node-app/ komutu ile bu dizindeki tüm dosyalar container içinde belirtilen yere eklendi. Bu kısım image -> container işlemi için gerekli.

WORKDIR ile bundan sonra çalışılacak dizin belirlendi. CMD [ "node", "index.js" ] ile container çalışmaya başladığında yapacağı iş tanımlandı.

Image almak

Proje dosyasında terminal açıldı.

docker build .
işlemi bende hata verdi.
docker system prune
ile dockerı tamamen temizleyip tetkrar deneyince oldu :D

docker images
yazınca bizim oluşturduğumuz image de listeleniyor.

isim vermek için

docker build . -t simple-node-app

Dockerfile içindeki her işlem adımı docker içerisinde layer olarak tutulur. Başka bir image için aynı şeye gerek olduğunda işlem oradan kopyalanır. Bu nedenle zaten dockerımızda olan bir işlem yapıldığında çok hızlı yapar.

ENV ile Çalışmak

index.js aşağıdaki gibi güncellendi.

console.log("Hello from ubuntu")
console.log("env =>", process.env.channel)
                  

Dockerfile içine ENV olarak eklendi.

FROM ubuntu:18.04
RUN apt-get update
RUN apt-get install curl -y
RUN curl -sL https://deb.nodesource.com/setup_10.x | bash
RUN apt-get install nodejs -y
COPY . /opt/node-app/
# 
ENV channel=kablosuzkedi
# Yukarıdaki kısım imageden container oluşturmak için
# aşağıdaki kısım oluşan containerın çalışma anında çalışacak kısım.
# CMD [ "node", "/opt/node-app/index.js" ]
WORKDIR /opt/node-app/
CMD [ "node", "index.js" ]
                  

docker build . -t simple-node-app
ile tekrar build alınır.

docker run simple-node-app
ile çalıştırıldı ve treminalde aşağıdaki çıktı alındı.
Hello from ubuntu
env => kablosuzkedi
                  

Dockerfile doldururken en üstte muhakkak baseimage olmalı.

Uygulama 2 - Aynı İşi Hazır Node BaseImage İle Yapmak

Yeni bir proje klasörü oluşturuldu ve index.js önceki projeden kopyalandı

Dockerfile

FROM node
WORKDIR /opt/node-app
COPY . .
ENV channel=kablosuzkedi
CMD [ "node", "index.js" ]
                  

docker build . -t simple-node-app-2
ile image oluşturulur.

image dosyası 1.1 GB. Bıyutu daha önce kendimizin ubuntu ile yaptığından daha büyük. Bu nedenle node versiyonunu 16-slim ile değiştirdik.

FROM node:16-slim
WORKDIR /opt/node-app
COPY . .
ENV channel=kablosuzkedi
CMD [ "node", "index.js" ]
                  

Önceki image silindi

docker rmi simple-node-app-2
ve yeniden image alındı
docker build . -t simple-node-app-2

İmage dosyamız şimdi 179MB :D

docker run simple-node-app-2
yazdığımızda terminalde
Hello from ubuntu
env => kablosuzkedi
                  
cevabı alındı.

Uygulama 3 - Node Server

Yeni bir proje klasörü oluşturuldu ve içine App.js eklendi.

const express = require("express");
const app = express();
const PORT = process.env.PORT || 3000;

app.get("/", (req, res) => {
  res.send("hello world");
});

app.listen(PORT, () => {
  console.log(`Example app listening at http://localhost:${PORT}`);
});
                  

npm paketlerini kullanabilmek için terminale

npm init -y
yazdık. Sonra express modülünü indirmek için
npm install --save express
yazıldı.

Bu hali ile çalıştırdığımızda server lokalde çalışıyor.

image alırken "node_modules" klasörünü da kopyalamamak için ".dockerignore" klasörü oluşturuldu ve içine

node_modules/
yazıldı.

Dockerfile dosyası oluşturuldu.

FROM node:16-slim
WORKDIR /opt/node-server
COPY . .
RUN npm install
CMD ["node", "app.js"
                  

docker run simple-node-server
ile image container haline gelir ve çalıştırılır. Terminalde
Example app listening at http://localhost:3000
çıktısı alınır.

docker run -p 3001:3000 simple-node-server
ile dışarıya port açılır ve http://localhost:3001/ üzerinden görülebilir.

Uygulama 4 - Simple PHP App

Yeni bir uygulama klasörü oluşturduk. İçinde index.php dosyası oluşturduk.

<?php
    echo "PHP Uygulamasını Dockerize Etmek...";
?>
                  
Dockerfile dosyası oluşturup içine:
FROM php:7-apache
COPY index.php /var/www/html/index.php
EXPOSE 80
CMD ["/usr/sbin/apache2ctl", "-D", "FOREGROUND"]
                  
yazıldı.

Proje klasöründe terminale

docker build . -t simple-php-app
yazılarak image oluşturuldu.
docker run -p 8080:80 simple-php-app
diyerek uygulama up edilir. http://localhost:8080/ üzerinden de görüntülenebilir.

Uygulama 5 - Mondo Todo App

Yeni bir proje klasörü açtık ve hocanın hazır kodlarını kullandık.

App.js

const express = require("express");
const bodyParser = require("body-parser");
const cors = require("cors");
const app = express();
const { TodoModel } = require("./TodoModel");
const Mongoose = require("mongoose");
const PORT = process.env.PORT || 3000;

app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.use(cors());

app.get("/", (req, res) => {
  TodoModel.find({})
    .then((todoList) => res.status(200).json(todoList))
    .catch((e) => res.status(500).json(e));
});

app.post("/todo", (req, res) => {
  const todo = new TodoModel({
    ...req.body,
    created_at: new Date(),
  });

  todo
    .save()
    .then((savedTodo) => res.status(200).json(savedTodo))
    .catch((e) => res.status(400).json(e));
});

app.listen(PORT, async () => {
  console.log(`Sunucu çalışıyor... ${PORT} | MongoDB'ye bağlanılacak..`);
  await Mongoose.connect("mongodb://mongo-alias:27017/todos", {
    useNewUrlParser: true,
    useUnifiedTopology: true,
  });
  console.log("MongoDB'ye bağlantı başarılı!");
});
                  
TodoModel.js
const Mongoose = require("mongoose");

const todoSchema = new Mongoose.Schema({
  title: String,
  description: String,
  completed: Boolean,
  created_at: Date,
});

module.exports = {
  TodoModel: Mongoose.model("todo", todoSchema),
};
                  
package.json
{
  "name": "node-mongo",
  "version": "1.0.0",
  "description": "",
  "main": "app.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "body-parser": "^1.19.0",
    "cors": "^2.8.5",
    "express": "^4.17.1",
    "mongoose": "^5.11.13"
  },
  "devDependencies": {
    "nodemon": "^2.0.7"
  }
}
                  

Terminale

npm i
yazıp bağımlılıkları indirdik.

.dockerignore dosyasına

node_modules/
yazdık.

Dockerfile dosyasına

FROM node:16-slim
WORKDIR /opt/node-todo-app
COPY . .
ENV PORT=4000
EXPOSE 4000
RUN npm install
CMD ["node", "app.js"]
                  
yazdık.

proje klasörünü terminalde açıp

docker build . -t todo-app
yazarak image aldık.

docker run --name mongo-server -p 27017:27017 -d mongo
ile mongo server ayağa kaldırıldı.

docker run --link mongo-server:mongo-alias -p 4000:4000 todo-app
--link mongo-server:mongo-alias kısmında mongo-server adındaki containera bağlan, bu bağlantı senin kodunda mongo-alias olarak geçiyor dedik. app.js kodunu incelersek orada await Mongoose.connect("mongodb://mongo-alias:27017/todos"...) kullanımını görürüz.

CMD ve ENTERYPOINT

CMD Container ayağa kalktığında çalışacak fonksiyon. CMD tam bir fonksiyondur. Container çalıştığında çalışır. CMD override edilebilir.

ENTERYPOINT sadece executable komut içerir. Komutun parametresini çalışma anında bizden bekler. Yoksa çalışmaz. Varsayılan değer atamak için Dockerfile içinde hemen altında CMD ile varsayılan değer belirtilebilir.

Container yapılacak bir işlem varsa açık kalır yoksa kapanır.

Uygulama 6 - Ubuntu Sleeper

v.1

Yeni proje klasörü aç. Dockerfile dosyası

FROM ubuntu:18.04
CMD ["sleep", "4"] 
#  Bu yazım şeklinde 1. komut executable (çalıştırılabilir) olmalı. Devamında array olarak parametreler girilir.

# CMD sleep 4
# Bu yazımda direk çalıştırılacak kod yazılır.
                  

Proje klasöründe

docker build -t ubuntu-sleeper .
yazılarak image oluşturulur ve
docker run ubuntu-sleeper
yazarsak cotainer 4 sn açık kalır. sonra kendini kapatır.

docker run ubuntu-sleeper sleep 10
yazarsak 10 sn sonra container kapanır. run komutunda container adından sonra girilen komut cmd ile girdiğimiz yere override olur (üzerine yazılır).

v.2

Dockerfile dosyası

FROM ubuntu:18.04

ENTRYPOINT [ "sleep" ]
                  
terminalde
docker build -t ubuntu-sleeper .
yazılarak image oluşturulur ve
docker run ubuntu-sleeper 5
yazarsak cotainer 5 sn açık kalır. sonra kendini kapatır.
docker run ubuntu-sleeper
yazarsak terminal bize
sleep: missing operand
Try 'sleep --help' for more information.
                  
döner.

ENTERYPOINT terminalden gelen veriyle çalışır. Buna defaut değer atamak için hemen altında CMD kullanılır.

FROM ubuntu:18.04

ENTRYPOINT [ "sleep" ]
CMD ["4"]
                  
Terminale değer girilmezse default olarak 4 alır.

Docker Compose ile Servis Yönetimi

Birden fazla container ayağa kaldırıp birlikte kullanma işlemlerinde docker compose kullanılır.

Hoca docker compose a girmeden 2 Dockerfile örneği daha yapmak istiyor. Biz de öyle yaparız :D

Uygulama 7 - Python App

Yeni bir proje klasörü içine hocanın hazırlamış olduğu src/server.py dosyasını

from flask import Flask
server = Flask(__name__)

@server.route("/")
def hello():
    return "Hello World!"

if __name__ == "__main__":
    server.run(host='0.0.0.0')
                  
ve dependencies.txt dosyasını
Flask==2.2.3
                  
ekledik.

Dockerfile dosyasını

FROM python:3.8-slim
WORKDIR /server
COPY dependencies.txt .
RUN pip install -r dependencies.txt
COPY src/ .
CMD ["python", "./server.py"]
                  
olarak yazdık.

Proje dosyasında terminale

docker build . -t python-app
yazarak image oluşturduk.
docker run -p 3000:5000 python-app
ile uygulama ayağa kaldırıldı.

Uygulama 8 - Vue App

Hocanın hazırladığı örneği proje klasörümüze kopyaladık.

Dockerfile

FROM node:14-slim
WORKDIR /vue-app
COPY app/ .
RUN npm install
RUN npm install -g live-server
RUN npm run build
EXPOSE 8080
CMD [ "live-server", "dist" ]
                  

Proje dosyasında terminale

docker build . -t vue-app
yazarak image alındı.
docker run -p 9000:8080 vue-app
ile app ayağa kaldırıldı. http://localhost:9000/ ile uygulamaya ulaşılabiliyor.

Her Şeyi Silmek (docker container prune)

docker contaşner prune ile tüm containerlar silinir.

docker images prune ile tüm images silinir. docker volume prune ile de tüm volume silinir. Bu kalıp diğer bileşenlerde de uygulanabilir.

Docker Compose

Birden fazla container ayağa kaldırıp birlikte kullanma işlemlerinde docker compose kullanılır.

Docker compose için proje klasörümüzde docker-compose.yml adında bir dosya oluşturuyoruz. .yml YMAL file dosya uzantısıdır. Bilgi aktarımı için kullanılır. JSON ve xml e göre daha kolay ve basit bir kullanımı var. Key: value düzeni ile yazılır. value array ise - işareti ile alt alta yazılır. Subkey bir iç çıkıntıda yazılır. Bu nedenle intent (çıkıntı) kavramı çok önemli.

İlk önce version keyi yazılır. Dockerın hangi sürümünün kullanılacağını belirler. Bu yapılacak işlemleri etkiler.

sonra services altına çalıştırılacak containerlar yazılır.

Örnek - 1: Tek Service

önceki projelerde yaptığımız node server içindeki app.js, package.json ve package-lock.json proje klasörümüzde app klasörüne kopyalandı. Dockerfile ise direk proje klasörümüze kopyalandı.

docker-compose.yml dosyası:

version: '3.4'
services:
  node-server: 
    container_name: my-server
    build: .
    ports: 
      - 3001:3000
                  
Dockerfile dosyası bazı işleri docker-compose.yml üzerine taşıdığımız için ve bazı klasörlerin yeri değiştiği için tekrar düzenlendi.
FROM node:16-slim
WORKDIR /opt/node-server
COPY app/ .
RUN npm install
CMD ["node", "app.js"]
                  

Proje dizininde terminale

docker-compose build
diyerek build aldırılır.

Terminale

docker images
yazdığımızda bizi 1-node-server-node-server adında bir image karşılar. Burada ilk kısım klasörün adı (1-node-server) ikinci kısım da servisin adı (docker-compose.yml -> services: node-server: ...)

Proje dizininde terminale

docker-compose up
diyerek çalıştırılır. Daha önce build alınmadıysa build alır ve sonra çalıştırır

Örnek - 2: Multiple Service

5-node-mongo-todo projesindeki Dockerfile yeni projemizin kök dizinine, app.js, package.json package-lock.js ve TodoModel.js yeni ptojenin app klasörüne eklendi.

Dockerfile dosyası aşağıdaki gibi düzenlendi.

FROM node:16-slim
WORKDIR /opt/node-todo-app
COPY app/ .
RUN npm install
CMD ["node", "app.js"]
                  

docker-compose.yml dosyası:

version: '3.4'
services: 
  todo-app:
    container_name: dc-todo-app
    build: .
    ports:
      - 3000:3000
  mongodb: 
    image: mongo
    ports: 
      - 27017:27017
    volumes:
      - todo-app-data:/data/db
volumes: 
  todo-app-data:
                  

docker-compose ile volume eklerken container seviyesindeki volume name ana seviyede de belirtilmeli.

app/app.js içinde mondoDB bağlantısı için docker-compose.yml içindeki servis adı (mongodb) kullanılır.

app.listen(PORT, async () => {
  ...
  await Mongoose.connect("mongodb://mongodb:27017/todos", {
    ...
  });
  ...
});
                  

Değişiklik yaptığımızda docker-compose up yapmadan önce docker-compose build ile build almak gerekir.

docker-compose down
projeyi durdurmanın en doğru yoludur. containerları tek tek kapatmak hataya neden olabilir.

Örnek - 3: Upload Edilen Dosyanın Saklanması

Hocanın uygulamasını proje klasörümüze app klaaörü içine ekledik.

Proje klasöründe Dockerfile dosyası eklendi.

FROM node:16-slim
WORKDIR /uploader-app
COPY app/. .
RUN npm i
EXPOSE 3000
CMD [ "node", "server.js" ]
                  
docker-compose.yml dosyası
version: "3.4"
services:
  uploader-app:
    build: .
    ports:
      - 3000:3000
    volumes:
      - uploader-app-uploads:/uploads
volumes:
  uploader-app-uploads:
                  
volume olarak dosyaların uygulamada yüklendiği "uploads" klasörünü gösterdik.

Örnek - 4: Variable Environment kullanmak

Bu seferki ürünü sadece docker-compose.yml ile oluşturduk. hub.docker.com -> wordpress dökümantasyonundan faydalandık.

version: '3.4'
services:
  wordpress:
    image: wordpress
    ports:
      - 8080:80
    environment:
      WORDPRESS_DB_HOST: db
      WORDPRESS_DB_USER: bloguser
      WORDPRESS_DB_PASSWORD: test123
      WORDPRESS_DB_NAME: blogdb
    volumes:
      - wordpress:/var/www/html
  db:
    image: mysql:5.7
    restart: always
    environment:
      MYSQL_DATABASE: blogdb
      MYSQL_USER: bloguser
      MYSQL_PASSWORD: test123
      MYSQL_RANDOM_ROOT_PASSWORD: "1"
    volumes:
      - db:/var/lib/mysql
volumes:
  wordpress:
  db:
                  

services: db: ile database oluşturuldu. db ismine dikkat. bunu diğer serviste WORDPRESS_DB_HOST: db olarak kullandık.

Örnek - 5: Çalışmak İçin Diğer Containerların Çalışmasına İhtiyaç Duymak

Bu projenin dosyalarını hoca paylaşmamış sadece docker-compose.yml üzerinde konuştuk.

version: '3,4'
services:
  twitter-app:
    build: .
    ports:
      - 3000:3000
    depends_on:
      - mongodb
      - redis
  mongodb:
    image: mongo:latest
    ports:
      - 27017:27017
    volumes:
      - data:/data/db
  redis:
    image: redis
    ports:
      - 6379:6379
volumes:
  data:
                  

depends_on: altına verilen servisler o servisin çalışması için gerekenleri tanımlar. Önce onlar çalışır. Sonra içine yazıldığı container çalışır.

linuxDockerSetupProductionLginx

Hoca işlemleri digitalocean.com üzerinden host alıp yapıyor. Biz de onu takip edeceğiz.

Create>Droplets ile formu doldurup makineyi ayağa kaldırıyoruz.

Oluşturduğumuz makineye docker kurmak için docker.com/engine/install/ubuntu/ adresindeki yönergeleri izleyeceğiz.

terminale

ssh root@<host-id>
sintaxı ile bağlanıyoruz. Bizimki
ssh root@165.232.122.105
Gelen ekrana parolamızı yazıp giriş yapıyoruz.

Ubuntuya girdiğimizde terminale

sudo apt-get update
yazarak mevcut paketleri güncelliyoruz.

Sonra terminale

sudo apt-get install ca-certificates curl gnupg
yazıyoruz.

apt install docker.io
ile docker kuruldu.

apt install docker-compose
ile docker-compose kuruldu.

systemctl status docker
ile docker'ın çalışma durumu kontrol edilir.

Örnek 1: Docker Compose ile Wordpress Site Ayağa Kaldırma.

Uzak bilgisayara bağlandıktan sonra kök dizinde terminale

cd /tmp
mkdir
mkdir web-apps
cd web-apps
                  
yazılarak tmp içine web-apps dosyası oluşturuldu ve içine girildi

vi docker-compose.yml
ile bu klasörde docker-compose.yml klasörü oluşturuldu ve içine girildi. Bu aşamada klavyede i ye basıp düzenleme (insert) modu açıldı. Daha önce wordpress için kullandığımız docker-compose içeriği buraya aktarıldı (hoca direk yapıştırdı. Bende düzgün yapışmadığından elle tek tek yazdım.)
version: '3.4'
services:
  wordpress:
    image: wordpress
    ports:
      - 8080:80
    environment:
      WORDPRESS_DB_HOST: db
      WORDPRESS_DB_USER: bloguser
      WORDPRESS_DB_PASSWORD: test123
      WORDPRESS_DB_NAME: blogdb
    volumes:
      - wordpress:/var/www/html
  db:
    image: mysql:5.7
    restart: always
    environment:
      MYSQL_DATABASE: blogdb
      MYSQL_USER: bloguser
      MYSQL_PASSWORD: test123
      MYSQL_RANDOM_ROOT_PASSWORD: "1"
    volumes:
      - db:/var/lib/mysql
volumes:
  wordpress:
  db:
                  

esc ile insert modundan çıkıldı. :wq ile dosya kaydedilip kapatıldı.

terminale

mkdir videomeet
mv docker-compose.yml videomeet/
                  
yazarak önce videomeet adında bir klasör oluşturduk ve ardından docker-compose.yml dosyasını buraya taşıdık.

                    cd videomeet
                    cat docker-compose
                  
yazarak önce bu klasöre girdik. Sonra da docker-compose.yml içeriğini terminalde görüntüledik.

docker-compose up
ile projemizi ayağa kaldırdık.

Projeye 165.232.122.105:8080 ile ulaşabiliriz.

projeyi kapatırken ctrl + C yeterli olur ancak tüm bileşenlerin kapanması için

docker-compose down
komutu kullanılır.

Nginx kurulumu ve Reverse Proxy tanımı

Reverse proxy (ters yönlü proxy), bir ağdaki istemcilerin (genellikle internet kullanıcıları) bir hizmet sunucusuna erişirken, istemcilerin bu hizmet sunucusunu doğrudan değil de arada bir sunucu üzerinden erişmelerini sağlayan bir teknolojidir. Bu arada sunucuya "reverse proxy" denir çünkü geleneksel olarak çalışan "forward proxy"nin tersine, istemciden gelen istekleri bir sunucuya ileterek geriye doğru işlem yapar.

Biz reverse proxy ile aynı IP ile de bağlı olan iki domaini isteğin geldiği server_name e göre farklı container içine göndereceğiz.

Nginx kurulumu için hocanın kullandığı kaynak

terminale

apt install nginx
yazılarak ngnix yüklenir.

Terminale

unlink /etc/nginx/sites-enabled/default
yazılarak Nginxin, Ubuntu'nun paket yöneticisi apt aracılığıyla yüklendiğinde önceden yapılandırılmış olan varsayılan sanal ana bilgisayarı devre dışı bırakılır.

cd /etc/nginx/sites-available
vi reverse-proxy.conf
                  
ile ilgili klasöre gidip revers proxy için configuration dosyası oluşturduk. içine yazmak için klavyede i ye bastık ve aşağıdakileri yazdık.
server {
        listen 80;
        listen [::]:80;
        server_name videomeet.app;
        server_name_in_redirect off;

        access_log /var/log/nginx/reverse-access.log;
        error_log /var/log/nginx/reverse-error.log;

        location / {
            proxy_set_header Client-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header Host $host;
            proxy_pass http://127.0.0.1:8080;
  }
}
                  
esc ile insert mode dan çıktık :wq ile kaydedip çıktık.

terminale

ln -s /etc/nginx/sites-available/reverse-proxy.conf /etc/nginx/sites-enabled/reverse-proxy.conf
ile yazdığımız dosya gerekli olan başka bir klasör ile bağlandı.

Terminale

nginx -t
yazarak nginx test edilir.
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
                  
çıktısı alıyorsa her şey yolunda demektir.

systemctl restart nginx
ile nginx restart edilir. ngnixin sistemde çalışmasını sorgulamak için terminale
systemctl status nginx
yazılır.

Terminale

cd /tmp/web-apps/videomeet
yazılarak projemizin olduğu dosyaya ulaştık.
docker-compose up
ile projeyi ayağa kaldırdık.

Burada bazı işlemler domain gerektiriyor. Bunu daha sonra tekrar deneriz. Bu kısımda domaine ve wordpress e host id girildi.

Örnek 2: Docker Compose ile Aynı Hostta Asana Clone Ayağa Kaldırma.

Proje dosyasını ssh üzerinden scp ile göndereceğiz.

Bunun için daha önce hocanın dosyalarından aldığımız ve göndereceğimiz 8-vue-app-docker-file klasörünü .tar formatında sıkıştırıyoruz. Sıkıştıracağımız klasörde terminale

tar -cvf asana-clone.tar *
yazdık.

Dosyayı göndermek için dosyanın olduğu klasörde terminale

scp asana-clone.tar ssh root@165.232.122.105:/tmp/web-apps/
yazdık

server tarafında

cd /tmp/web-apps
ile dosyayı gönderdiğimiz dizine girdik.
chmod 755 asana-clone.tar
ile dosya erişim iznini değiştirdik.
tar xf asana-clone.tar
ile dizinden çıkarttık.
mkdir asana-clone
yazarak yeni bir klasör oluşturduk.
mv app asana-clone/app
mv Dockerfile asana-clone/Dockerfile
                  
ile .tar dan çıkartılanlar bu dosyaya taşındı.
rm asana-clone.tar
ile sıkıştırılmış dosya silindi

cd asana-clone
docker build . -t asana-clone-app
                    
ile build aldık ve
docker run -p 8090:8080 -d asana-clone-app
ile uygulama up edildi.

http://165.232.122.105:8090/ ile uygulamaya ulaşılabilir.

cd /etc/nginx/sites-available/
ile nginx ayarlarının olduğu klasörü açtık.
vi reverse-proxy.conf
ile içine girdik. Klavyede i ye basarak insert modunu açtık ve dosyaya gerekli eklemeleri yaptık. Son hali:
server {
        listen 80;
        listen [::]:80;
        server_name videomeet.app;
        server_name_in_redirect off;

        access_log /var/log/nginx/reverse-access.log;
        error_log /var/log/nginx/reverse-error.log;

        location / {
            proxy_set_header Client-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header Host $host;
            proxy_pass http://127.0.0.1:8080;
  }
}

server {
        listen 80;
        listen [::]:80;
        server_name talkinghead.app;
        server_name_in_redirect off;

        access_log /var/log/nginx/reverse-access.log;
        error_log /var/log/nginx/reverse-error.log;

        location / {
            proxy_set_header Client-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header Host $host;
            proxy_pass http://127.0.0.1:8090;
  }
}
                  
esc sonra da ":wq" ile kaydedilip kapatılır.

Her iki domain de aynı IP ile de bağlı olsa isteğin geldiği server_name e göre yönlendirme değişmektedir.

systemctl restart nginx
ile nginx restart edilir. Böylece yeni ayarlar devreye alınmış olur.

Domain aldığımızda biz de bu durumu deneriz.

Kubernetes (K8S)

Tamamen teorik bir eğitim. Uygulama adına hiçbir şey yapmadık.

Kubernetes'e Giriş

Containerlar üzerinde çalışan microservice tabanlı uygulamaların orkestrasyonu.

  1. Uygulamayı geliştirip container haline getiririz
  2. YAML veya JSON dosyası olarak "Deployment" objesini yaratırız
  3. Yarattığımız dosyayı kubernetes api-server'a göndeririz
    Kompanenetler
  1. Master (Control Plane)
    1. kube-api-server: K8S in beyni
    2. cluster store(etcd): K8S in hafızası
    3. kube-controller-manager: kontrollör
    4. kube-scheduler: organizatör
  2. Node (Worker)
    1. kubelet: Node Agent
    2. container engine: Container yöneticisi
    3. kube-proxy: Load Balancer

Pod nedir Docker için container ne ise K8S için de Pod odur. Podların içinde containerlar çalışır. K8S ölöekleme birimidir. 1 Pod birden fazla K8S nodu üzerine deploy edilemez.

Deployment nedir? REST objeleridir. YAML yada JSON dosyasında tanımlanabilirler. Bir kere hazırlanıp birden fazla deploy edilebilirler. Versiyonlanabilirler. Yaratımı api-server üzerinden olur.

Service nedir? K8S üzerinde bir network var. Podların trafiğini service yönetir. Sadece sağlıklı podlara yönlendirir. Load balancing görevini de yerine getirir.

Master'a Genel Bakış

Cloudda veya lokalde herhangi bir host üzerinden çalışabilir. Cluster'a gözkulak olur. Cluster üzerinde nelerin çalışacağına master karar verir. Workload ayarlamaları, yaşam döngüsü, ölçekleme ve upgrade işlemlerinden sorumludur.

Komponentleri

kube-api-server kuberneteste birşey yaratmak ve kullanmak için faydalanacağımız, K8S in beyni. API(REST) üzerinden fonksiyonları expose eder. JSON yada YAML dosyaları vasıtasıyla fonksiyonları kullanabilir. 443 portu üzerinden çalışır (HTTPS). $kubectl komut satırı ile kullanılır.

cluster store Yönetimdeki tek stateful kısımdır. Kalıcı bir saklama alanıdır. Key/value store teknolojisini kullanır.

kube-controller-manager controllerları koordine eder. Her işlem için controller bulunabilir. Desired state (arzulanan hal) pozisyonunu kontrol eder. Arzulanan halden çıkılırsa geri dönmek için gerekeni yapan kısımdır.

kube-scheduler api-serverdan gelen yeni pod yaratma görevleri için bekler. Node'lara workload atamasından sorumludur.

Node'a Genel Bakış

Cluster'ın çalışan gücü. Fiziksel yada sanal makinede olabilirler. Node'u node yapan en önemli şey container runtime'dır.

Komponentleri

kubelet node'u cluster'a register etmeye yarar. api-server dan gelen direktifleri bekler. Pod oluşturmada görev alır. Master ile sürekli iletişim halinde.

container engine container'ları yönetme işini halleder. Pluggable (kaldırılabilir, değiştirilebilir). Genelde Docker Container Engine kullanılır. Container'ları start-stop etmede görevlidir.

kube-proxy kubernetes network fonksiyonlarını sağlar. Node içinde her pod tek bir IP adresine sahiptir. Service içinde tanımlanan Pod'ların yük dengelemesini sağlıyor. Frontend ve backend podlar kube-proxy üzerinden haberleşir.

Master-Node Arası İletişim

api-server node'lardaki loglara erişmek, port-front etmek ve portlara erişmek için kubelet'i kullanır. api-server'ın node'lar, pod'lar ve service'ler ile iletişimi şu an için secure (https) değil.

Kubernetes'in Çalışma Prensipleri

Kubernetes karmaşık mikroservice yapılarını organize eder. Herhangi bir microservice down olursa yeni bir cluster kubernetes tarafından ayağa kaldırılır.

Biz kubectl kullanarak K8S in beyni olan api-server ile konuşur. Manifest dosyamiz ve kubectl komutu ile api-server üzerinden işlem başlar.

Kubernetes Kuruyoruz-1

Hocanın notları Node ve Master kurulumunda notlardan faydalandık ancak bu kısımda bu dökümantasyondan faydalandık. Hocanınki bizde çalışmadı. Gerçi bizim yaptığımız hali de eninde sonunda çöktü :D

Hoca kurulumu direk linux ortamında yapmış. Ben de digitalocean üzerinden 2 makine açıp orada deneyeceğim

Hem master hem de node makinede swap disable edilmeli.

swapoff -a
Bu olmazsa kubernetes ayağa kalkmıyor.

swap nedir? Swap (Takas) Alanı, işletim sistemi tarafından sabit diskinizde ayrılmış bir bölümdür. İşlenecek veriler ön belleğe (RAM) sığmadığı zaman bu bölüm “RAM” gibi kullanılır ve böylelikle veri akışının ve proseslerinin devam etmesi sağlanır.

Her iki makineyi de güncelle

sudo apt-get update

Her iki makineye google apt reposty ekle

curl -fsSL https://dl.k8s.io/apt/doc/apt-key.gpg | sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-archive-keyring.gpg

Her iki makineye kubernetes apt repository ekle.

echo "deb [signed-by=/etc/apt/keyrings/kubernetes-archive-keyring.gpg] https://apt.kubernetes.io/ kubernetes-xenial main" | sudo tee /etc/apt/sources.list.d/kubernetes.list
                  

Her iki makineyi tekrar güncelle

sudo apt-get update

Repository deki versiyonları daha sonra incelemek için apt-cache ile cacheliyelim.

apt-cache policy kubelet|head -n 20
apt-cache policy docker.io|head -n 20
                  

Gerekli paketleri repositoryden makinemize çekelim.

sudo apt-get install -y docker.io kubelet kubeadm kubectl
                  

kubelet, container runtime'mımız olan docker statusleri kontrol edelim.

sudo systemctl status kubelet.service
sudo systemctl status docker.service
                  

sistem ayağa kalktığında çalışacak şekilde ayarlayalım.

sudo systemctl enable kubelet.service
sudo systemctl enable docker.service
                  

Kubernetes Kuruyoruz-Master

Pod network yaratmak icin calico yaml lari indirelim

wget https://docs.projectcalico.org/v3.3/getting-started/kubernetes/installation/hosted/rbac-kdd.yaml
wget https://docs.projectcalico.org/v3.3/getting-started/kubernetes/installation/hosted/kubernetes-datastore/calico-networking/1.7/calico.yaml
                  

Calico yaml file daki ip bilgisine uygun olarak, bir pod network range i belirleyerek Kubernetes cluster imizi olusturalim

sudo kubeadm init --pod-network-cidr=192.168.0.0/16

API server a admin erisim yetkisine sahip bir hesap yaratalim

mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
                  

Pod network olusturmak icin gerekli yaml dosyalarini calistiralim

kubectl apply -f rbac-kdd.yaml
kubectl apply -f calico.yaml
                  

Sistem specific podlari ve calico podlarini inceleyelim
Not : DNS pod umuz network deploy olup calisir halene kadar aktif olmayacak

kubectl get pods --all-namespaces
                  

Cluster da mevcut podlarin listesine bakalim - sadece master gorunecek

kubectl get nodes

kubelet static pod manifestleri calistiriyor yani bir anlamda core cluster podlari calisir hale geliyor

sudo systemctl status kubelet.service

kubeconfig dosyasinin bulundugu directory i inceleyelim

ls /etc/kubernetes

master daki manifestleri inceleyelim

ls /etc/kubernetes/manifests

api-server ve etcd nin manifestlerini incelemek istersek...

sudo more /etc/kubernetes/manifests/etcd.yaml
sudo more /etc/kubernetes/manifests/kube-apiserver.yaml
                  

Kubernetes Kuruyoruz-Node

kubelet ve container runtime service lerini kontrol ederim

sudo systemctl status kubelet.service
sudo systemctl status docker.service  
                  

Sistem basladigi zaman baslayacak sekilde set edelim

sudo systemctl enable kubelet.service  
sudo systemctl enable docker.service
                  

Master'da token bilgisi verilmisti eger not almadiysak su sekilde listeleyebiliriz

kubeadm token list

Yeniden bir token generate etmek istiyorsak

kubeadm token create

Master uzerinde ca cert hash bulunmakta. Onu elde etmek için:

openssl x509 -pubkey -in /etc/kubernetes/pki/ca.crt | openssl rsa -pubin -outform der 2>/dev/null | openssl dgst -sha256 -hex | sed 's/^.* //'
                  

Yukarıdaki kodlarla elde ettiğimiz token imiz ve cert ile Api Server in IP adresi ya da adını kullanarak Node'umuzu cluster a dahil edelim.

sudo kubeadm join <ip>:6443 --token<token> --discovery-token-ca-cert-hash <ca_cert_hash>
                  

bizim durumumuzda aşağıdaki kod node'a yazılır.

kubeadm join 174.138.60.6:6443 --token nyndhw.xynrxsohej6fw400 \
        --discovery-token-ca-cert-hash sha256:ccb7f01df76693e522c9ba719adb9817d8c28f0ab1cf18148fd21022ee04a74d
                  

NodePort - ClusterIP - LoadBalancer - Ingress

Kuberneteste Ptrafik yönetimi: Podlar tüm cruster içinde erişebilir olur. Service üzerinden Podları erişebilir hale getirir. kube-dns servislere otomatik DNS name atar.

NodePort nedir? Dış dünyadan gelen trafiği Cluster'imizin içine kabul etmenin temel yolu. Tüm nodelar üzerinde bir portu erişime açar (default range of 30000-32767). Cluster dışından <NodeIp>:<NodePort> şeklinde erişilebilir. Gelen istekler ilişkilendirilen Service'e yönlendirilir.

ClusterIp nedir? Service'e cluster genelinde geçerli bir IP verme yöntemi. Default olarak dış dünyaya erişime açmaz ama proxy vasıtasıyla dışarıdan erişim sağlanır.

LoadBalancer nedir? Kuberneteste service'i dış dünyaya expose etmek için standart yöntem. Service'lerin yük dağılımı NodePort üzerinden Service'e yönlendirme ile sağlanır. Yaratılan LoadBalancer tipindeki Service'lerin de bir IP si olur. İstek NodeBalancer üzerinden NodePort'a oradan podlara geçer.

Ingress aslında bir service değil bir object. Reverse proxy işi yapar. Service'ler arası yük dağıtan bir router gibi davranır. Çalışması için cluster'da bir Ingress Controllerîn bulunması gerekir.

Daemonset ve Kullanımı

Cluster üzerinde her Node üzerinde belirlediğimiz bir Pod'u çalıştırmanın yolu. İstenilen şartta bir Node oluştuğu anda set edilen işlemi gerçekleştirir (İçinde default olarak gelmesi istenen bir pod vs gibi). Spesifik işler yapmak için spesifik Node'lara deploy olabilirler.

DeamonSetlere ulaşmak için: DeamonSet'i yaratırken port bilgisi verilip, NodeIp-Port bilgisiyle veya DeamonSet podları bir Service objesine bind edilip service üzerinden ulaşılabilirler.

DeamonSetler genelde dışarı data push etmek için kullanılır (Loglama vs).

Imperative ve Declarative Yöntemler

Imperative yöntem özel komutlar vasıtasıyla süreci yönetmek.

Declarative yöntem yapılacak işi bir veya birden fazla script içerisinde tanımlayıp çalıştırmak

Imperative bizim tek tek elle yaptığımız işlemlerken, declarative ise manifest dosyaları gibi önceden hazırlanmış dosyalarla yaptığımız kalıptan yapılan işlemlerdir.

Pod Yaşam Döngüsü ve Probe'lar

    Pod'un statüleri
  1. Pending: kubernetes tarafından kabul edildi. Container image'ların inmesi bekleniyor
  2. Running: Pod bir node ile ilişkilendirildi. Tün container'lar yaratıldı
  3. Succeded: Pod'daki tüm containerlar görevini tamamlayıp sonlandırıldı. Yeniden başlamayacaklar.
  4. Failed: Tüm containerlar sonlandırıldı. Sonlandırılırken en az biri hata verdi.
  5. Unknown: Podun stat'i alınamıyor. Podun üzerinde çalıştığı node'dan bilgisi okunamıyor.

Probe kubernetesin Pod'un durumunu öğrenmek için yaptığı işlem. Periyodik olarak cluster'da yapılan diagnostic operasyondur.

    Üç çeşit prop vardır
  1. Liveness Probe: Sağlıklı çalışıyor mu? Beklendiği gibi çalışıyor mu?
  2. Readiness Probe: Pod request kabul edebilir mi?
  3. Startup Probe: Containerdaki uygulama başarılı çalıştı mı?
    Üç Probe yöntemi vardır
  1. Execation prob sırasında bir aksiyon alınıyor.
  2. TCPSocketraction prob sırasında soket açılıp denetleniyor
  3. HTTPGetAction http request gönderip sonucu veriyor (success, failure, unknown)

Cluster'da Loglama

Clusterda olan bitenden haberdar olmak (monitmr etme, debug etme) için kullanılır.

    Bunlar log üretir
  • Podlar
  • Contanerlar
  • Kubernetes Componentleri
  • DeamonSetler
  • Birçok Kubernetes Service'i

Kubernetes api üzerinden loglara erişilir.

Node level ve cluster level olarak iki ayrı log seviyesi vardır. Node level loglarını default olarak tutar. Cluster level için K8S te bir log kayıt mekanizması yok. Biz ekliyoruz.

Replication Yöntemlerine Genel Bakış

Kubernetesin var olma sebebi. Sistemde çalışan uygulamaların, mikroservislerin ve containerların çoklanması işlemidir. Kubernetese verdiğimiz desired state'e göre kill olanların yerini yeniler alarak bizim istediğimiz yapının kalıcılığı sağlanır.

    Üç ihtiyacı karşılar
  • Scaling (ölçeklendirme)
  • Reliability (Sistemin kusursuz çalışması)
  • Load Balancing
    Replication Yöntemleri
  • Replication Controller: Kubernetes'te replication’in en temel halini uygulayan yöntem. Pod’ların replication yönetimi bir controller üzerinden gercekleşir
  • ReplicaSet: Contoller yapısı kullanmadan daha hedefe yönelik (new-gen RC). Replication Controller’a gére daha fazla esnek selector yapı
  • Deployment: (Resmi olarak önerilen yöntem) Replicas tanımlaması ile deployment'in içi replication’ı yedirme. Rollout ve gerekirse rollback avantajı

Kubectl ve İlk Deployment

Kubectl kubernetes command-line aracı. Cruster'ı yönetmeye (deployment yapmak, uygulamaları incelemek ve loglara erişmek) yarar. ~/.kube/config dosyasının içindeki bilgilere göre çalışır.

kubectl komutu syntax:

kubectl [command] [type] [name] [flags]
Örnek:
kubectl get pods
kubectl get service my-service
kubectl create deployment nginx --image=nginx
                  

Heroku yerine Deta.Space

Hoca heroku anlatacak ancak biz heroku artık ücretli olduğundan deta.space üzerinden takip etmeye çalışacağız.

diğer alternatifler: Render, Netlify, Cyclic

Node JS ile Basit Rest Api Yapımı

Proje ekranında terminale

npm init y
ile package.json oluşturuldu ve npm başlatıldı.

npm install --save express body-parser
ile gereken bağımlılıklar yüklendi

npm install --save-dev nodemon
ile nodemon kuruldu.

db.json dosyası oluşturuldu ve içine hocanın verdiği data eklendi.

app.js oluşturuldu son hali aşağıda.

const app = require("express")();
const db = require("./db.json");
const bodyParser = require("body-parser");

app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));

app.get("/users", (req, res) => {
  res.status(200).send(db);
});
app.get("/users/:id", (req, res) => {
  if (isNaN(req.params.id)) {
    res.status(400).send({
      message: "İşlenemeyen veri",
    });
  } else {
    const user = db.find((user) => user.id == req.params.id);
    if (user) {
      res.status(200).send(user);
    } else {
      res.status(400).send({
        message: "Kullanıcı bulunamadı",
      });
    }
  }
});
app.post("/users", (req, res) => {
  const willSaveData = {
    id: new Date().getTime(),
    full_name: req.body.full_name,
    country: req.body.country,
    email: req.body.email,
    created_at: new Date(),
  };
  db.push(willSaveData);
  res.send(willSaveData);
});
app.patch("/users/:id", (req, res) => {
  if (isNaN(req.params.id)) {
    res.status(400).send({
      message: "İşlenemeyen veri",
    });
  } else {
    const user = db.find((user) => user.id == req.params.id);
    if (user) {
      // Kayıt değişikliği
      // pass by referance ve pass by value araştırılacak.
      Object.keys(req.body).forEach((key) => {
        user[key] = req.body[key];
      });
      res.status(200).send(user);
    } else {
      res.status(400).send({
        message: "Kullanıcı bulunamadı",
      });
    }
  }
});
app.delete("/users/:id", (req, res) => {
  if (isNaN(req.params.id)) {
    res.send(400, {
      message: "İşlenemeyen veri",
    });
  } else {
    const userIndex = db.findIndex((user) => user.id == req.params.id);
    if (userIndex > -1) {
      //silme işlemi
      db.splice(userIndex, 1);
      res.status(201).send({
        message: "Kullanıcı Silindi",
      });
    } else {
      res.status(400).send({
        message: "Kullanıcı bulunamadı",
      });
    }
  }
});

app.listen(process.env.PORT || 3000, () => {
  console.log("Sunucu ayaktadır... Çalışıyor...");
});

                

nodemonun bizim her değişikliğimizde kodumuzu tekrar çalıştırması için package.json içine

"scripts": {
    "start": "nodemon app.js"
  },
                
eklendi ve terminale
npm start
yazılarak proje ayağa kaldırıldı.

Heroku yerine Deta.Space

Heroku ücretli olduğundan alternatif arayışındayız. deta.space terminal kullanımı ve tamamen ücretsiz olması nedeni ile ilgimizi çekti. Burayı deneyeceğiz.

diğer alternatifler: Render, Netlify, Cyclic

Terminale

iwr https://deta.space/assets/space-cli.ps1 -useb | iex
yazarak bilgisayarımıza kurduk.

space login
ile uygulamayı açtık. Bizden istediği access token için deta.space sayfasında en alttaki form yardımıyla setting>access token ürettik ve terminale girdik. Ve login olduk.

Proje dizininde terminale

space new
yazılarak proje deta.space üzerinde pluşturulur.

Dockerdakine benzer bir şekilde projemiz için Spacefile adında bir manifest oluşturduk.

v: 0
micros:
  - name: node-rest-api
    src: ./
    engine: nodejs16
    primary: true
    run: node app.js
    dev: npm start
    public: true
                

Terminale

space push
yazarak projeyi deploy ettik.

Diğer Eğitimlerdeki Deployment Örnekleri