17 Temmuz 2020 Cuma

DOCKER SWARM ÜZERİNDE ELASTIC STACK (ELS) DEPLOY EDİLMESİ (1)

Merhaba, 

Bu yazıda docker ekosistemi ve sunucu mimarilerinde olay bilgisi kayıtlarını (log bilgisi) belirli bir mantık içerisinde toplayıp daha sonra toplanan bu kayıtlar üzerinde bir takım görseller , raporlar oluşturarak bilgi sistem altyapısının izlenmesi ve bilginin analiz edilmesi konularına değineceğiz. Bu kapsamda kuracağımız sistem Elastic Stack (ELK) olarak bilinen bir takım yazılımlar kümesi olacaktır. Elastic Stack ve barındırdığı yazılımlara kısaca değinelim.

Elastic Stack içerisinde Elasticsearch, Kibana, Logstash, Beats (Filebeat, Metricbeat, Heartbeat, Packetbeat, Auditbeat, Winlogbeat, Functionbeat ) yazılımlarını içeren bir bütünleşik uygulama mimarisidir. Her bileşenin farklı işlemleri gerçekleştirdiği bu yapıda bileşenler şu görevleri yapmaktadırlar:

- Elasticsearch: Dağıtık RESTful tabanlı arama ve analiz motorudur. Verileri JSON formatında tutulduğu  merkezi veri depolama sistemidir. Arama ve analiz konusunda çok başarılı ve hızlıdır.
- Kibana: Elasticsearch verilerini görüntülemenizi sağlayan web tabanlı bir arayüz sağlayan  yazılımdır. İsteklerinizin uygulamanızdaki akışını anlamaya kadar birçok bilgiyi sizin için görserlleştirir.
-  Logstash: Çok sayıda kaynaktan veri alan, dönüştüren ve saklamak istediğiniz alanı elde etmenizi sağlayan bir veri işleme hattıdır.
-  Beats: Çeşitli sistemlerden farklı farklı verileri toplayıp elasticsearch veya logstash'e gönderen veri göndericileridir. İçerisinde topladığı veriye göre farklı farklı türleri bulunmaktadır. (Filebeat, Metricbeat, Heartbeat, Packetbeat, Auditbeat, Winlogbeat, Functionbeat)

Bu kısa açıklamadan sonra kuracağımız sistem mimarisinden bahsedelim. Kuracağımız yapıda docker-swarm üzerinden "Filebeat" bileşenini kullanarak log kayıtlarını toplayacağız. Ayrıca Ekosistemimizi oluşturan host ve sanal makinlerin metrik değerlerini (CPU, RAM,  I/O, Bandwith vb.) "Metricbeat" ile toplayacağız. Topladığımız bu verileri doğrudan elasticsearch'e gönderip daha sonra kibana üzerinden elasticsearch ile bağlantı kurarak topladığımız verileri anlamlandıracağız.  Bu senaryoda "logstash" kullanmayacağız. Kuracağımız tüm ELS bileşenleri (metricbeat hariç) docker üzerinde servis olarak çalıştırılacaktır. Uygulama mimarisi mantıksal olarak aşağıdaki gibi olacaktır. 



Not: Bu yapı önceki yazılarda anlatılan  docker swarm mimarisi üzerinde deplop edilecektir. 
Uygulamayı docker-compose dosyası olarak yml formatında hazırlayıp docker-swarm cluster üzerinde deploy edeceğiz. Yapacağımız konfigürasyonların ve toplayacağımız verilerin kalıcı olması için öncelikle uygulamalar için konfigürasyon dosyalarını oluşturuyoruz. 

Elasticsearch için yalnızca tek nodelu bir cluster oluşturuyoruz. Burada amacımız yüksek erişilebilirliği göstermekten ziyade uygulamaların fonksiyonlarını gözlemlemek olacaktır. Bu kapsamda elasticsearch config dosyası aşağıdaki gibi olacaktır. 

Not: elasticsearh için role ve user tanımı yapmayacağız. Ayrıca security özelliklerini de aktif etmeyeceğiz. Bu kurulum yalnızca ELS çalışma mantığına odaklanmaktadır. 

elasticsearch.yml
--------------------------------------------------------------------------------------------------------------------------
cluster.name: "docker-cluster"
network.host: 0.0.0.0
--------------------------------------------------------------------------------------------------------------------------
kibana konfigürasyon dosyası aşağıdaki gibidir.
kibana.yml
--------------------------------------------------------------------------------------------------------------------------
#
# ** THIS IS AN AUTO-GENERATED FILE **
#

# Default Kibana configuration for docker target
server.name: kibana.omer.com
server.host: "0"
elasticsearch.hosts: [ "http://elasticsearch:9200" ]              
xpack.monitoring.ui.container.elasticsearch.enabled: true
--------------------------------------------------------------------------------------------------------------------------
Docker ekosisteminden logları toplayabilmek için filebeat konfigürasyon dosyası aşağıdaki gibidir.
filebeat-config.yml
--------------------------------------------------------------------------------------------------------------------------
filebeat.config:
  modules:
    path: ${path.config}/modules.d/*.yml
    reload.enabled: false

filebeat.autodiscover:
  providers:
    - type: docker
      hints.enabled: true
      hints.default_config.enabled: false

processors:
- add_cloud_metadata: ~

output.elasticsearch:
  hosts: 'http://elasticsearch:9200'

setup.kibana:
  host: 'http://kibana:5601'

setup.dashboards.enabled: true

logging.level: warning
logging.to_stderr: true
--------------------------------------------------------------------------------------------------------------------------
Konfigürasyon dosyalarındaki bazı kritik satırlara değinelim. Burada kibana.yml dosyasında "http://elasticsearch:9200" ifadesinde yer alan "elasticsearch" host adının oluşacak kibana konteynerinde nasıl çözümleneceği sorusu akıllara gelecektir. Aynı soru filebeat-config-yml dosyasında yer alan  'http://kibana:5601' ifadesi için de sorulabilir. Bu sorunu docker-compose dosyasında her servis için "alias" kullanarak çözeceğiz. Oluşturduğumuz her servise bir alias atayıp bu isimleri kullanarak servislerin kontrol ettiği konteynerlerin birbirlerini bulmalarını ve isim çözümlemesi yapmalarını sağlıyoruz. Örneğin kibana servisi için "kibana" alias'ı, elasticsearch servisi için "elasticsearh" alias'ı kullanılmaktadır. Alias kullanımı ile alakalı ayrıntılı bilgi için linke tıklayabilirsiniz. Devam edelim. filebeat-config.yml dosyasında provider'ın docker olduğunu belirttikten sonra docker sisteminde "hints.default_config.enabled: false" satırını yazarak yalnızca loglarını toplamak istediğimiz docker servislerin loglarını toplayacağız. Peki hangi docker servisinden log çekeceğimizi nasıl belirliyoruz? Bu noktada docker-swarm ekosisteminde çalışmakta olan konteynerlere label ekleyerek, filebeat docker servisinin bu konteynerleri bulmasını sağlayacağız. Fakat bu aşamada kritik olan şey loglarının toplanmasını istediğiniz docker servisler için labelları  "docker service" labelı olarak değil, docker konteyner labelı kullanmalısınız!!! Nasıl yapılacağını aşağıda göstereceğiz. Konfigürasyon dosyalarını da hazırladıktan sonra son olarak ELK yı deploy etmek için docker-compose dosyamızı oluşturuyoruz.

elk.yml
--------------------------------------------------------------------------------------------------------------------------
#elasticstack application
version: "3.8"
services:
  elasticsearch:
    image: docker.elastic.co/elasticsearch/elasticsearch:7.6.2
    volumes:
      - /mnt/docker-swarm/es/data/:/usr/share/elasticsearch/data
      - /mnt/docker-swarm/es/config/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml
    ports:
      - 9200:9200
    environment:
      - discovery.type=single-node
    deploy:
      replicas: 1
      placement:
        constraints:
          - "node.hostname==centos03"
    networks:
      base-network:
        aliases:
          - elasticsearch


  kibana:
    image: docker.elastic.co/kibana/kibana:7.6.2
    deploy:
      placement:
        constraints:
          - "node.hostname==centos02"
    volumes:
      - /mnt/docker-swarm/kibana/config/kibana.yml:/usr/share/kibana/config/kibana.yml
    ports:
      - 5601:5601
    networks:
      base-network:
        aliases:
          - kibana
  filebeat:
    image: docker.elastic.co/beats/filebeat:7.6.2
    user: root
    deploy:
      mode: global
    volumes:
      - /mnt/docker-swarm/beats/filebeat/filebeat.docker.yml:/usr/share/filebeat/filebeat.yml:ro
      - /var/lib/docker/containers:/var/lib/docker/containers:ro
      - /var/run/docker.sock:/var/run/docker.sock:ro
    networks:
      base-network:
        aliases:
          - filebeat
networks:
  base-network:
    external: true
--------------------------------------------------------------------------------------------------------------------------
elk.yml dosyasına değinecek olur isek; daha önceden de belirttiğimiz gibi  "uygulama stack"ini linkte  bahsedilen altyapı üzerinde çalıştırıyoruz. Kaynakların verimli kullanılması açısından kibana ve elasticstack konteynerlerini farklı hostlarda ayağı kaldırıyoruz. kibana servisi 5601, elasticsearch servisi 9200 portlarını dış dünyaya duyurmaktadır. Verilerin ve konfigürasyonların kalıcı olabilmesi adına her servis için ayrı ayrı "bind" konfigürasyonu yapılmıştır. Ayrıca filebeat yazılımının; docker-swarm sistemini keşfedebilmesi için docker soketi filebeat servisine "read-only" olarak bind edilmiş, log dosyalarını okuyabilmesi için  host üzerinde  docker loglarının yer aldığı "/var/lib/docker/containers" dizini filebeat servisine bind edilmiş ve filebeat servisi deploy edilirken "mode: global" seçilerek tüm hostlarda konteyner oluşturması böylece tüm docker-swarm ekosisteminden logların çekebilmesi sağlanmıştır.  Oluşturulan yml dosyası aşağıdaki komut ile docker-swarm üzerinde deploy edilir.

~# docker stack deploy -c elk.yml ELK

Docker üzerinde ELK birkaç dakika içerisinde ayağı kalkacaktır. Servislerin deploy edilme sürecini 
"docker stack ps ELK" komutu ile görüntüleyebilirisiniz. ELK uygulamaları sorunsuz bir şekilde başlatıldığında "http://192.168.1.101:9200" adresinden elasticsearch api arayüzüne ve "http://192.168.1.102:5601" adresinden kibana web uygulaması arayüzüne erişebilir durumda olursunuz. Aşağıdaki şekilde kibana arayüzü gösterilmiştir.



Sıra mevcut durumda çalışan docker servislerinin loglarını toplamaya geldi. Bu noktada önceki yazılarda oluşturulan ve deploy edilen wordpress servis loglarının toplanmasını sağlayacağız. Bunun için konteyner labellarını kullanacağımızı daha önce belirtmiştik. Aşağıda wordpress uygulamasının deploy edildiği wordpress.yml dosyasında docker-swarm servisler için labelların nasıl oluşturulduğu gösterilmiştir. Eklenen alanlar kırmızı ile gösterilmiştir.

wordpress.yml
--------------------------------------------------------------------------------------------------------------------------

#wordpress application
version: "3.8"
services:
  wordpress:
    image: wordpress:latest
    labels:
      - "co.elastic.logs/enabled=true"  #ELK'nın logları toplaması sağlar.
      - "co.elastic.logs/module=apache" #Toplanan loglar için apache modulü aktif edilmiştir.
      - "co.elastic.logs/fileset.stdout=access" 
      - "co.elastic.logs/fileset.stderr=error"
    depends_on:
      - db
    volumes:
      - /mnt/docker-swarm/wordpress/:/var/www/html/wp-content
    deploy:
      replicas: 3
      labels:
        - "traefik.enable=true"
        - "traefik.docker.network=base-network"
        - "traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https"
        - "traefik.http.routers.router-websecure.tls=true"
        - "traefik.http.routers.router-websecure.entrypoints=websecure"
        - "traefik.http.routers.router-websecure.rule=Host(`omer.com`)"
        - "traefik.http.routers.router-web.entrypoints=web"
        - "traefik.http.routers.router-web.rule=Host(`omer.com`)"
        - "traefik.http.routers.router-web.middlewares=redirect-to-https"
        - "traefik.http.services.wordpress.loadbalancer.server.port=80"
        - "traefik.http.services.wordpress.loadbalancer.sticky=true"
        - "traefik.http.services.wordpress.loadbalancer.sticky.cookie.name=wp-elif"
        - "traefik.http.services.wordpress.loadbalancer.healthcheck.port=80"

    environment:
      - WORDPRESS_DB_NAME=wordpress
      - WORDPRESS_DB_USER=elif
      - WORDPRESS_DB_PASSWORD=elif
      - WORDPRESS_DB_HOST=db:3306
      - WORDPRESS_TABLE_PREFIX=elif
    networks:
      base-network:

  db:
    image: mysql:5
    labels:
      - "co.elastic.logs/enabled=true"
      - "co.elastic.logs/module=mysql"
      - "co.elastic.logs/fileset.stdout=access"
      - "co.elastic.logs/fileset.stderr=error"
    deploy:
      replicas: 1
      placement:
        constraints:
          - "node.role==manager"
    environment:
      - MYSQL_ROOT_PASSWORD=elif
      - MYSQL_ALLOW_EMPTY_PASSWORD=no
      - MYSQL_USER=elif
      - MYSQL_PASSWORD=elif
      - MYSQL_DATABASE=wordpress
    volumes:
      - /mnt/docker-swarm/mysql:/var/lib/mysql
    networks:
      base-network:

  loadbalancer:
    image: traefik
    labels:
      - "co.elastic.logs/enabled=true"
      - "co.elastic.logs/module=traefik"
      - "co.elastic.logs/fileset.stdout=access"
      - "co.elastic.logs/fileset.stderr=error"
    ports:
      - 80:80
      - 8080:8080
      - 3000:3000
      - 443:443
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - /home/centos/swarm-file/config.yml:/etc/traefik/traefik.yml
      - /home/centos/swarm-file/config-dyn.yml:/etc/traefik/config-dyn.yml
        #- /mnt/docker-swarm/logs:/var/log
      - /mnt/docker-swarm/cert:/etc/ca-certificates
    deploy:
      resources:
      restart_policy:
        condition: any
      mode: replicated
      replicas: 1
      update_config:
        delay: 2s
      placement:
        constraints:
          - "node.role == manager"
    networks:
      base-network:

networks:
  base-network:
    external: True
--------------------------------------------------------------------------------------------------------------------------
Yukarıda eklenen labellar yml dosya hiyerarşisinde doğrudan servislerin altına eklenmiştir. (Docker servise labelları deploy kısmının altında tanımlanmaktadır.) Böylece servisin üreteceği her konteynerde bu labellar yer alacaktır ve bu sayede ELK bu konyetnerlerden log toplayabilecektir. Labellar ile elastic logları aktif edilmiş, log toplanan servisin türüne göre özel log formatlarının oluşması için ilgili modül seçilmiş, (örnekte apache, traefik, mysql modlülleri aktif edilmiştir.) seçilen modüllerde hangi tür logların işlem göreceği belirtilmiştir. Şimdi değişikliklerden sonra wordpress uygulamasını güncelliyoruz.

~#docker stack deploy -c wordpress.yml WORPRESS_APP

Yukarıdaki komut çalıştırıldıktan sonra docker-swarm daha önceden deploy edilmiş olan tüm wordpress uygulaması konteynerlerini belirtilen labelları ekleyerek yeniden oluşturacaktır. Labelların oluşup oluşmadığını önce konteylerleri listeleyip daha sonra konteynerleri "inspect" komutuyla inceleyerek görüntülenebilir.

~# docker ps


~# docker inspect --format '{{ range $k, $v := .Config.Labels -}}             
{{ $k }}={{ $v }}
{{ end -}}' ba39dc156b64


Yukarıdaki komutlar ile wordpress konteynerinde (ba39dc156b64 idsine sahip) labelların oluştuğu gösterilmiştir. Şimdi wordpress uygulaması için logların toplanmaya başlatmış oluyoruz. Toplanan bu logları görebilmek için kibana arayüzünü açıyoruz ve management sekmesini seçiyoruz.



Açılan sayfada elasticsearch bölümünde index management bölümüne tıklnarak indislerin oluşup oluşmadığı kontrol edilir.




Yukarıdaki şekilde görüldüğü gibi "filebeat*" olarak indisleri görüyoruz. Şimdi bu indisleri kibanada "index pattern" oluşturmak için kullanacağız. İndex pattern oluşturmak için yapılması gerekenler aşağıdaki şekillerde gösterilmiştir.




İndex pattern oluşturulmadan önce son adımda bazı filtreleri seçmenizi isteyecektir. Burada @timestamp seçip "create index pattern" diyerek işlemi tamamlıyoruz.


Bu son aşamadan sonra artık kibana üzerinde log bölümüne gelerek docker loglarını görüntüleyebilirsiniz.



Log kayıtlarını toplamaya başladıktan sonra kibana üzerinden logları istediğiniz gibi filtreleyip, isteğinizi log kaydını aratabilirsiniz. Kibana üzerinde "visualise" yani  görseller oluşturup daha sonra bu görselleri bir araya getirerek "dashboarad" oluşturabilirsiniz. Böylece loglarınız grafiklere dönüştürerek anlaşılabilirliğini artırabilirsiniz. Bu konuya başka bir yazıda değinilecektir. Bir sonraki yazıda ise oluşturacağımız mimarinin diğer kısmını yani metricbeat ile metrik değerlerin toplanıp kibana üzerinden görselleştirilmesini anlatacağız. İyi çalışmalar.

Yararlanılan Kaynaklar:
[1] https://www.elastic.co/guide/index.html
[2] https://gist.github.com/steve-jansen/a90f942e05e326e817aaeb04dff3f4e6
[3] https://docs.docker.com/compose/compose-file/













8 Temmuz 2020 Çarşamba

DOCKER SWARM ÜZERİNDE TRAEFIK CLOUD LOAD-BALANCER İLE WORDPRESS UYGULAMASI DEPLOY ETME (2)

Bir önceki yazıda wordpress uygulamasının docker-swarm cluster üzerinde nasıl deploy edileceğine değinilmişti. Yapılan örnek ile wordpress uygulaması 3 düğümlü clusterda çalışır hale getirilmiş ve tarayıcı üzerinden herhangi bir swarm nodunun ip adresi ve tcp 80 portuyla erişim sağlanmıştır. Fakat kurulan mimarinin birçok eksiği bulunmaktadır. Bunları sıralayacak olur isek;

- Uygulamaya overlay üzerinden erişim sağlanmaktadır fakat herhangi bir "persistence session" olmadığından, her seferinde uygulama gönderilen http istekleri farklı bir konteynere düşecektir. 
- Uygulama 80 portundan yani http olarak hizmet vermekte olup, tüm trafik şifresiz gerçekleşmektedir. 
-  Docker swarm cluster gelen istekleri konteynere dağıtmaktadır fakat herhangi bir sağlık kontrolü yapmadığından, "down" duruma düşen konteynere http istekleri gitmeye devam edecektir. 

Yukarıda bahsedilen sebeplerden ötürü uygulamanın önüne "reverse-proxy" veya "load balancer" koyma ihtiyacı ortaya çıkmaktadır. Bu açıdan bakıldığında en bilinir load-balancerlar olan nginx veya haproxy çözümü akıllara gelecektir. Fakat bu yazılımlar konteyner ekosistemi için tasarlanmamıştır. Service-discovery, dinamik konfigürasyon gibi özellikleri barındırmadıklarından bunları kullanmak efektif olmayacaktır. Bu sebeple cloud-native load-balancera ihtiyaç duyulmaktadır. Yapılan araştırma ve inceleme neticesinde "traefik" adlı yazılımın konteyner dünyası için tasarlandığı ve bu iş için kullanılabileceği değerlendirilmiştir. Traefik load-balancerını kullanmadan önce traefik load-balancerdan ve temel konseptlerin bahsedelim.

Traefik "open-source" bir edge router  olup kubernetes, docker, AWS, Mesos vb. cluster yapılarıyla doğrudan uyumlu olarak çalışabilmektedir. En faydalı özelliklerinden biri,  servisleri otomatik algılayıp konfigürasyonu ona göre uyarlayabilmesidir. Traefik edge-routerı anlamak için temel kavramlarından bahsedelim:

1- Entrypoint kavramı:  Dış dünyadan gelen istekleri karşılama biriminin adıdır. Örneğin http hizmeti veriyor iseniz, tcp 80'e bind ettiğiniz bir entrypoint gelen istekleri bu porttan  karşılar. Aşağıdaki şekil bunu simule etmektedir.


2- Routers kavramı: Router entrypointten gelen isteği dinler (isteğe bağlı olarak bir veya birden çok entryointten gelen isteği dinleyebilir.) ve gelen isteğe kendi üzerinde daha önce oluşturulan kuralı uygulayarak arka tarafta yer alan servise yönlendirir. Şayet gelen isteğin üzerinde bir oynama yapılacak ise bu isteği middleware birimine yönlendirir. Aşağıdaki şekil bunu simule etmektedir.

3- Middleware kavramı: Middlewareler routerlara bağlı olarak çalışırlar. Routerlardan gelen istekler üzerinde değişiklik yapabilmektedirler. Örneğin gelen isteği (request) ve başlığı (header) değiştirebilir, yeniden yönlendirebilir (redirect), authentication ekleyebilir vb. Kullanımı isteğe bağlıdır.  Aşağıdaki şekil bunu simule etmektedir.
4- Service kavramı: Servisler gelen isteği routerlardan alırlar ve isteklerin hedeflerine nasıl ulaştırılacağını belirlerler. Örneğin isteğin ulaşacağı hizmet sağlayıcılar (provider veya backend), load balance algoritması, sticky session, healt check, cookie ekleme vb. burada belirlenir. Aşağıdaki şekil bunu simule etmektedir.
5- Provider kavramı: Hizmet isteğinin ulaşacağı ve işleneceği son adres olarak tanımlanabilir. Traefik çeşitli providerları desteklemektedir. Örneğin docker, kubernetes, kv, rancher vb. Klasik load-balance yöntemlerinde backend olarak ifade edilen birime de karşılık gelebilir.

Şimdi traefik edge-routerı docker-swarm üzerinde oluşturduğumuz sisteme nasıl adapte edeceğimizi anlatalım:
- Traefik yazılımını da konteyner üzerinde ayağa kaldıracağız ve service discovery özelliğini çalıştırabilmek için docker soketini dinlettireceğiz. Bu sebeple swarm clusterda manager node'da konumlandıracağız. 
- Wordpress uygulama konteynerlerının kontrolünü traefik'e verebilmek için wordpress servisinde labelları kullanacağız. 
- Traefik için iki ayrı konfigürasyon dosyası oluşturulacaktır. Traefik dinamik ve statik olarak iki farklı konfigürasyon seçeneği ile yapılandırılmaktadır . Bazı özellikleri her iki dosyada da yapılandırabiliyor iken bazı özellikleri yalnızca tek bir dosyada tanımlayabilmektesiniz. Bu sebeple iki adet konfigürsayon dosyası kullanılacaktır. Ayrıca dinamik ve statik konfigürasyonu aynı dosya içerisinde tanımlamak mümkün değildir.
    a- Static dosyada provider, entrypoint, log ayarları ve traefik'i gözlemleyeceğiniz web arayüzü yapılandırılacak; dinamik konfigürsayon dosyasının yolu belirtilecek,
    b- Dinamik dosyada ise web sayfası için kullanılacak olan tls sertifika ayarları yapılandırılacaktır. 

Statik yapılandırma dosyası aşağıdaki gibidir:

config-static.yml
--------------------------------------------------------------------------------------------------------------------------
providers:
  docker:
    swarmMode: true
    watch: true
    exposedByDefault: false
    swarmModeRefreshSeconds: 5
    network: "base-network"
  file:
    filename: "/etc/traefik/config-dynamic.yml"
api:
  insecure: true
log:
  level: FATAL
  #  filePath: "/var/log/traefik.log" #traefik loglarını dosyaya yönlendirmek isterseniz bu satırı açabilrisiniz
#
#ENTRYPOINTS#
entryPoints:     # wordpress uygulaması için iki adet entrypoint tanımlanmıştır.
  web:
    address: ':80'
  websecure:
    address: ':443'
--------------------------------------------------------------------------------------------------------------------------
config-dynamic.yml
--------------------------------------------------------------------------------------------------------------------------
#TLS CERTIFICATES#
tls:
  certificates:
    - certFile: /etc/ca-certificates/omer.cer
      keyFile: /etc/ca-certificates/omer.key

Hazırladığımız config dosyalarını traefik konteynerine bind ederek traefiği ayağı kaldırıyoruz.

load-balancer.yml
--------------------------------------------------------------------------------------------------------------------------
#wordpress application
version: "3.8"
services:
  traefik
    image: traefik
    ports:
      - 80:80
      - 443:443
      - 8080:8080 #traefik yönetim paneli erişimi için kullanılacaktır.
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - /home/centos/swarm-file/config-static.yml:/etc/traefik/traefik.yml
      - /home/centos/swarm-file/config-dynamic.yml:/etc/traefik/config-dynamic.yml
     - /mnt/docker-swarm/cert:/etc/ca-certificates  #websitesi için kullanılacak olan serfitikaları bind ediyoruz.
    deploy:
      restart_policy:
        condition: any
      mode: replicated
      replicas: 1
      update_config:
        delay: 2s               # 2sn aralıklarla konfigürasyon yenilenecektir.
      placement:
        constraints:
          - "node.role == manager"    #traefik konteynerini manager node'da çalıştırıyoruz. "Best practice" olarak HA (High Availability) traefik için birden çok swarm manager node'unuz olmalı ve traefik konteynerlerini manager nodelarda çalıştırmalısınız.
    networks:
      base-network:

networks:
  base-network:
    external: True

Uygulamanın ssl üzerinden hizmet vermesi için wordpress konteynırları üzerinde ayar yapmak iş yükü ve karmaşık konfigürsayon gerektirir iken, traefik üzerinde kolayca bu işi halledebilirsiniz. Ayrıca konteyner tarafını düşünmeksizin yalnızca traefik üzerinden tüm sertifikalarınızı yönetebilirsiniz. Burada örnek olarak "omer.com" sitesi için openssl ile self-signed sertifika üretip, bu sertifikayı "/mnt/docker-swarm/cert" dizini altına taşıyor ve daha sonra bu alanı traefik konteynerine bind ederek dinamik konfigürsayon dosyasında sertifikaları gösterip trafiğin https olarak akmasını sağlıyoruz. Tüm bunlar için hazırladığımız load-balancer.yml'ı deploy ediyoruz.

~# docker stack deploy -c load-balancer.yml WORDPRESS_LOADBALANCER

Traefik konteynerini  çalıştırdık fakat henüz wordpress konteynerlerini nasıl kontrol altına alacağını kararlaştırmadık. Burada docker swarm providerı kullandığımız için servis "label"larını kullanarak daha önce deploy edilmiş olan wordpress.yml'ı labellarla güncelleyerek traefik2in kontrolü altına alıyoruz. wodpress.yml dosyasına eklenen alanlar kırmızı ile gösterilmiştir.

wordpress.yml
--------------------------------------------------------------------------------------------------------------------------
#wordpress application
version: "3.8"
services:
  wordpress:
    image: wordpress:latest
    depends_on:
      - db
    volumes:
      - /mnt/docker-swarm/wordpress/:/var/www/html/wp-content
    deploy:
      mode: global
     ports: 80:80         #traefik tarafından dış dünyaya portlar expose edileceğinden wordpress expose satırı silinir.
      labels:
        - "traefik.enable=true"   #traefik bu servis için aktif edilir.
        - "traefik.docker.network=base-network" #traefik'in base-network' için çalışması sağlanır.
        - "traefik.http.routers.router-web.entrypoints=web" #router-web adında router' tanımlar ve web entrypointe bağlandığı kararlaştırılır.
        - "traefik.http.routers.router-web.middlewares=redirect-to-https"  #router-web için  redirect-to-https adında middleware tanımlanır.
        - "traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https" #redirect-to-https middleware'ını kullarak http isteklerini https'e yönlendiriyoruz.
        - "traefik.http.routers.router-websecure.tls=true"  #router-websecure adında  router'ı tanımlanır ve tls enable edilir.
        - "traefik.http.routers.router-websecure.entrypoints=websecure" #https için websecure entrypoint seçilir
        - "traefik.http.routers.router-websecure.rule=Host(`omer.com`)" #router-websecure için kural belirliyoruz.
        - "traefik.http.routers.router-web.rule=Host(`omer.com`)" #router-web için kural belirliyoruz.
        - "traefik.http.services.wordpress.loadbalancer.server.port=80" #wordpress adında servis tanımlıyor ve gelen isteklerin bu servisin arkasında çalışan backend serverların  80.portuna yani wordpress konteynerlerine gönderilmesini sağlıyoruz..
        - "traefik.http.services.wordpress.loadbalancer.sticky=true" 
        - "traefik.http.services.wordpress.loadbalancer.sticky.cookie.name=wp-elif" #sticky sessin için cookie eklenir.
        - "traefik.http.services.wordpress.loadbalancer.healthcheck.port=80" #load-balance yapılan konteynerler için healt-check tanımlanır.
    environment:
      - WORDPRESS_DB_NAME=wordpress
      - WORDPRESS_DB_USER=elif
      - WORDPRESS_DB_PASSWORD=elif
      - WORDPRESS_DB_HOST=db:3306
      - WORDPRESS_TABLE_PREFIX=elif
    networks:
      base-network:

  db:
    .
    .
    .
--------------------------------------------------------------------------------------------------------------------------
wodpress.yml güncellendikten sonra wordpress servisinin bu tanımlar ile çalıması için wordpress servisi güncellenir.

~# docker stack deploy -c wordpress.yml WORDPRESS

Tüm bu işlem  adımları uygulandıktan sonra mimari aşağıdaki gibi düzenlenmiş olacaktır.

Sonuç olarak uygulama artık https üzerinden çalışmaya başlamış, cookie kullanımı ile persistence session özellğine kazanmış, healtcheck ile load-balancing sağlıklı olarak yapılımış ve servis üzerinden yapılacak güncelleme ile konteyner sayısı artırıldığında otomatik olarak traefik yeni oluşan konteynerlere trafiği yönlendirmeye başlayacaktır. Bu yazıda yalnızca traefik edge-routerın docker-swarm üzerinde bir takım özellekleri kullanılmıl olup daha ayrıntılı bilgiye yararlanılan kaynaklar bölümünden ulaşabilirsiniz. İyi çalışmalar.

Yararlanılan Kaynaklar,
[1] https://docs.docker.com/compose/compose-file/
[2] https://docs.traefik.io/







23 Haziran 2020 Salı

DOCKER SWARM ÜZERİNDE TRAEFIK CLOUD LOAD-BALANCER İLE WORDPRESS UYGULAMASI DEPLOY ETME (1)

Merhaba. Bu yazı serisinde docker swarm cluster üzerinde (CentOS işletim sistemi kullanılmıştır.)traefik load-balancer kullanarak wordpress uygulaması deploy etmeyi anlatacağız. Bu uygulama için ilk yazıda wordpress uygulamasını docker-swarm üzerinde deploy edilmesinden bahsedeceğiz. Daha sonra traefik load-balancer ile uygulamaya kazandırılan yetenekler anlatılacaktır. Öncelikle kuracağımız mimariyi 3 node üzerinde kurulu olan docker-swarm altyapısını kullanarak oluşturacağız. Aşağıda ilk etap için uygulama bileşenlerinin topolojisi gösterilmiştir. 




Docker swarm clusterı üzerinde wordpress sitesi ayaklandırabilmemiz için temelde iki adet konteynere ihtiyaç duyulmaktadır. Birincisi wordpress uygulamasının çalışacağı wordpress, ikincisi ise veritabanı olarak kullanacağımız mysql konteyneri. Kurulacak olan sistemin kalıcı veri depolama ve nodelardan bir veya birden çoğu kapandığı zaman hizmet vermesini sağlama amacı ile ortak disk ihtiyacı oluşmuş, bu ihtiyaç NFS (Network File System) server aracılığı ile sağlanacaktır. Konteynerlerin ayrıntısına girmeden önce NFS server ve NFS client yapılandırması yapmak gerekecektir. Bunun için aşağıdaki adımlar izlenir.

NFS server hizmeti verecek olan host üzerinde (192.168.1.45, bu mimaride gösterilmeyen bir host üzerinde NFS server çalıştırılmıştır. NFS Server test amaçlı yapılandırılacağından herhangi bir docker swarm nodeunda başlatılabilir.) Öncelike nfs server bileşenleri yüklenir ve nfs server servisleri başlatılır.

~# dnf install nfs-utils
~# systemctl start nfs-server.service
~# systemctl enable nfs-server.service

NFS ile paylaştırılacak alan için bir mount point oluşturulur.

~# mkdir -p /mnt/nfs-share/docker-swarm

NFS server konfigürasyonu için "/etc/export" dosyası açılarak aşağıdaki satır eklenir.

/mnt/nfs-share/docker-swarm 192.168.1.0/24(rw,sync,no_subtree_check,all_squash,anonuid=0,anongid=0)

Yukarıda gösterilen satırda ilk alan hangi dizinin nfs server ile paylaşılacağı, ikinci alan hangi ip bloğundan ve hangi kullanıcı idlerinin hangi izinlerle bu alana erişeceğini belirtmektedir. Örnekte belirtilen ip bloğundaki hostlar için okuma/yazma yetkisi ile kullanıcıdan bağımsız olarak erişim yetkisi verilmiştir. Yapılan değişiklikten sonra exportfs komutu ile belirtilen alan nfs protokolü ile sunulmaya  başlanır. 

~# exportfs

CentOS firewalldan nfs servisi için yetkilendirme yapılır.

~# firewall-cmd --permanent --add-service=nfs

Son olarak mimaride belirtilen hostlarda (centos01, centos02, centos03) aşağıdaki komutlar çalıştırılarak paylaştırılan alan mount edilerek kullanıma başlanır.

~# mount -t nfs 192.168.1.45:/mnt/nfs-share/docker-swarm /mnt/docker-swarm/

mount edilen alanın restart sonrası tekrardan kullanılabilmesi için /etc/fstab dosyasına yazılması gerekmektedir. Aşağıdaki komut ile bu işlem sağlanır.

~# echo "192.168.1.45:/mnt/nfs-share/docker-swarm      /mnt/docker-swarm  nfs     defaults 0 0" >> /etc/fstab

NFS paylaşım alanı oluşturulduktan sonra konteynerlerin üzerinde koşacağı overlay network oluşturulur. Bunun için aşağıdaki komut çalıştırılır. 

~# docker network create -d overlay --subnet 10.0.6.0/24 base-network

Altyapı oluşturulduktan sonra docker-swarm üzerinde konteyner deployment için yaml dosyası oluşturacağız. Bu dosyada konteynerlerimizi servislere bağlı olarak deploy edeceğiz. Deploy planımızda:

- iki adet servis yer alacaktır. Bunlar wordpress ve db servisleri.
- wordpress servisi 3 adet wordpress konteyner oluşturacak ve bunları hostlara dağıtacaktır. 
- wordress servisi wordpress içeriğinin kalıcı olması için wp-content klasörünü nfs share alanına bind edecektir. 
- wordpress servisi TCP 80 portunu dış dünyaya expose edecektir. 
- wordpress servisi base-network üzerinde ayağı kalkacaktır. 
- wordpress servisi veritabanına environment variableları kullanarak bağlanacaktır. 
- wordpress servisi "depends_on" özelliğini kullanarak veritabanı konteynerının keşfedecektir.
- db servisi manager node üzerinde konteyner ayağı kaldıracaktır.
- db servisi kalıcı olabilmesi için "/var/lib/mysql" dizinini nfs share alanına bind edecektir. 
- db servisi konteyerı başlatırken wordpressin bağlanacağı veritabanını oluşturmak için environment variable ları kullanacaktır. 
- wordpress servisi base-network üzerinde ayağı kalkacaktır. 

Yukarıda belirtilen işlemleri aşağıda gösterilen yaml dosyası ile tanımlıyoruz.

wordpress.yml:
--------------------------------------------------------------------------------------------------------------------------

#wordpress application
version: "3.8"
services:
  wordpress:
    image: wordpress:latest
    depends_on:
      - db
    volumes:
      - /mnt/docker-swarm/wordpress/:/var/www/html/wp-content
    deploy:
      mode: global   #konteynerlerin tüm hostlarda oluşmasını sağlar
    ports: 80:80
    environment:
      - WORDPRESS_DB_NAME=wordpress
      - WORDPRESS_DB_USER=test-user
      - WORDPRESS_DB_PASSWORD=test-passwd
      - WORDPRESS_DB_HOST=db:3306
      - WORDPRESS_TABLE_PREFIX=first-site
    networks:
      base-network:

  db:
    image: mysql:5
    deploy:
      placement:
        constraints:
          - "node.role==manager"    # mysql konteynerini swarm manager node üzerinde çalıştırır.
    environment:
      - MYSQL_ROOT_PASSWORD=test-passwd-root
      - MYSQL_ALLOW_EMPTY_PASSWORD=no
      - MYSQL_USER=test-user
      - MYSQL_PASSWORD=test-passwd
      - MYSQL_DATABASE=wordpress
    volumes:
      - /mnt/docker-swarm/mysql:/var/lib/mysql
    networks:
      base-network:
--------------------------------------------------------------------------------------------------------------------------
Yukarıda gösterilen dosya hazırlandıktan sonra aşağıdaki komut ile wordpress uygulaması deploy edilir.

~# docker stack deploy -c wordpress.yml WORDPRESS_APP

Komut çalıştırıldan bir süre sonra wordpress uygulaması ayağı kalkmış olur  ve tarayıcıdan docker hostlarının herhangi birisinin ip adresi ve 80. portu girilerek wordpress kurulum safyasına ulaşılabilir. Siteye tüm hostlardan erişilebilmesi overlay networkü sayesinde gerçekleşmektedir. Örneğin wordpress konteynıru sadece tek bir hostta (192.168.1.101) tanımlanmış olsaydı dahi yine diğer sunucu ip adresleri yazılarak wordpress uygulamasına erişilebilir olurdu. 

Bu makalede docker-swarm üzerinde wordpress uygulamasının nasıl deploy edileceği anlatılmıştır. Bir sonraki  yazıda mimarinin eksiklikleri değerlendirilecek ve sonrasında traefik cloud-native load balancer ile bu eksiklikler tamamlanmaya çalışılacaktır. 


Yararlanılan Kaynaklar:










17 Mayıs 2020 Pazar

Bulut İçin Linux İmajları Oluşturma ve Deploy Etme (2)

Merhaba. Bir önceki makalede "cloud-init" yöntemi ile KVM üzerinde CentOS deployment örneği yapılmıştı. Bu makalede ise Ubuntu 18.04 sürümünün KVM üzerinde aynı metot ile nasıl deploy edildiği anlatılacaktır.

Öncelikle Ubuntu 18.04 cluod imajı https://cloud-images.ubuntu.com/ adresinden download edilir. Ubuntu 18.04 Bionic kurulumu yapacağımızdan indirme linkimiz aşağıdaki gibi olacaktır.

~# wget https://cloud-images.ubuntu.com/bionic/20200507/bionic-server-cloudimg-amd64.img

Diskin boyutunu yeniden belirliyoruz.

~#qemu-img resize bionic-server-cloudimg-amd64.img 30G

Daha sonra bir önceki makalede de bahsettiğimiz user-data, meta-data, network-config dosyalarını konfigüre ediyoruz.

meta-data:
--------------------------------------------------------------------------------------------------------------------------
instance-id: ubuntu01
local-hostname: ubuntu01
--------------------------------------------------------------------------------------------------------------------------
network-config
--------------------------------------------------------------------------------------------------------------------------
version: 2
ethernets:
  eth0:
    dhcp4: false
    addresses:
      - 192.168.1.101/24
    gateway4: 192.168.1.1
    nameservers:
      search: [omer.com, bar.local]
      addresses: [8.8.8.8, 4.4.4.4]
--------------------------------------------------------------------------------------------------------------------------
user-data:
--------------------------------------------------------------------------------------------------------------------------
#cloud-config
password: Parola01
chpasswd: {expire: False}
ssh_pwauth: True
sudo: ["ALL=(ALL) NOPASSWD:ALL"]
timezone: Europe/Istanbul
ssh_authorized_keys:
  - ssh-rsa [ssh public key]
#apt paket yöneticisinin birincil olarak tr.archive.ubuntu adresini kullnamasını istiyoruz.
apt:
  primary:
    - arches: [default]
      uri: http://tr.archive.ubuntu.com/ubuntu/
runcmd:
  - systemctl disable cloud-config.service
  - systemctl disable cloud-final.service
  - systemctl disable cloud-init-local.service
  - systemctl disable cloud-init.service
  - localectl set-keymap trq
--------------------------------------------------------------------------------------------------------------------------
Konfigüre ettiğimiz dosyaları kullanarak işletim sistemini boot edeceğimiz "ubuntu.iso" dosyasını oluşturuyoruz.

~#genisoimage -output ubuntu.iso -volid cidata -joliet -rock user-data meta-data network-config

Son olarak virt-install komutu ile indirmiş olduğumuz qcow2 formatındaki ubuntu OS diskini işletim sistemi diski, oluşturulan "ubuntu.iso" kalıp dosyasını ise bootable media olarak  cdroma bağlayıp sanal makine özellikleri verilerek başlatılır.

~#virt-install --import --name ubuntu01 --memory 2048 --vcpus 2 --disk bionic-server-cloudimg-amd64.img,format=qcow2,bus=virtio --cdrom=ubuntu.iso --network bridge=br0 --graphics vnc  &


İşletim sistemi ayağı kalkarken cloud-init servisler ile istediğimiz şekilde konfigüre edilmiş olacaktır. Kurulum aşamaları log kayıtlarının incelenmesi için https://omerurhan.blogspot.com/2020/05/bulut-icin-linux-imajlar-olusturma-ve.html makalesinde anlatılan servislerin loglarına aşağıdaki komutlar ile ulaşılabilmektedir.

~# journalctl -u cloud-init-local.service
~# journalctl -u cloud-init.service
~# journalctl -u cloud-config.service
~# journalctl -u cloud-final.service


Ayrıca sorun çözme aşamalarında yardımcı olacak olan cloud-init servislerinin ne tür ayar dosyalarını meyadana getirdiğini "/run/cloud-init" dizini altında gözlemleyebilirsiniz.

Son tahlilde cloud-init servislerini kullanarak hemen hemen tüm cloud platformlarında çok kısa sürede istediğiniz konfigürasyonda işletim sistemi ayaklandırabilirisiz. İyi çalışmalar.

Yararlanılan kaynaklar:
[1] https://cloudinit.readthedocs.io/en/latest/



7 Mayıs 2020 Perşembe

Bulut İçin Linux İmajları Oluşturma ve Deploy Etme (1)

Merhaba. Bu yazıda günümüz "cloud-computing" teknolojilerinin de kullandığı yöntem olan cloud-init yöntemi ile linux işletim sistemi deploy etmeyi açıklayacağız. Cloud-init yazılımı ile hemen hemen tüm public cloud platformlarında ve bazı private cloud platformlarında (KVM, OpenStack, LXD, Bare metal installs) çok kısa sürede tüm büyük linux dağıtımları ayağı kaldırılabilmektedir. Peki nasıl?

Öncelikle cloud-init servislerinin çalışma mantığından bahsedeceğiz. Cloud-init servisleri işletim sisteminin boot edilmesi esnasında çalıştırılmaktadır. Böylece kurulumunu yapacağınız işletim sistemini ilk boot esnasında konfigüre etmenizi sağlar. Bu servislerin çalıştırılabilmesi için öncelikle cloud tabanlı işletim sistemi imajlarını kullanıyor olmanız gerekmektedir. Örneğin Ubuntu veya CentOS işletim sistemi kuracaksanız, işletim sistemi imajlarınızı https://cloud-images.ubuntu.com/ - https://cloud.centos.org/centos/ adreslerinden indirmelisiniz. İndirmiş olduğunuz imaj dosyaları içerisinde cloud-init servisleri bulunmaktadır. Cloud-init servisleri çalıştığında çeşitli kaynaklardan verileri okuyarak konfigürsayonları gerçekleştiriler. Örneğin veriler dosyadan okunacak ise yaml formatında üç farklı dosya oluşturulabilir:

    - meta-data        : instance-id,  hostname gibi makineye ait bilgiler
    - user-data         : Kullanıcı adı, parola, ssh public anahtarları, işletim sisteminde kurulması istenen paketler vb. ayarların yapıldığı dosyadır.
    - network-config : Ağ ayarlarının yapıldığı dosyadır. 

Bu aşamada işletim sistemi boot edilirken sırası ile aşağıdaki cloud-init servisleri çalışmaktadır.

1- Generator: Bu hizmet ile boot başlatıldığında systemd boot hedeflerinin içinde cloud-init hedefi yer alıyorsa -varsayılanda yer almaktadır- cloud-init servisi başlatılır. Yalnızca aşağıdaki durumlarda cloud-init servisleri çalıştırılmaz.
    - /etc/cloud/cloud-init.disabled dosyası mevcut ise,
    - kernel komut satırında cloud-init=disabled ifadesi yer alıyor ise.
2- Local: cloud-init-local.service servisi tarafından kontrol edilir. "/" kök dosya sistemi mount edilir edilmez çalışmaya başlamaktadır. Lokal "data-source" kaynaklarını bulur ve data-source kaynaklarını okuyarak ağ konfigürasyonlarını gerçekleştirir. (Data-source  bulunan deploy ortamına bağlı olarak değişmekle birlikte bizim yapacağımız örnekte meta-data ve network-config dosyaları olacaktır.)
3- Network: cloud-init.service servisi tarafından kontrol edilir. Local adımından sonra çalıştırılır. Bu adım çalıştırılmadan önce makine ağ ayarlarını almış ve "interface"ler "up" duruma gelmiştir. Bu aşamada disk_setup ve mounts modulleri çalışarak mount pointler, partitionlar yönetilir veya diskler formatlanır. Ayrıca bootcmd gibi ön yükleme adımları nu aşamada çalıştırılır. 
4- Config: cloud-config.service servisi tarafından kontrol edilir. Network adımından sonra çalıştırılır ve config modullerini çalıştırır. Örneğin password, timezone, runcmd  modüller. Bu aşamada kullanıcı adı, parola, ssh public anahtarları, işletim sisteminde kurulması istenen paketler vb. ayarlar yapılmaktadır.
5- Final: cloud-final.service servisi tarafından kontrol edilir. Son olarak bu adım çalıştırılır. Klasik olarak rc.local altındaki uygulamalar ile aynı zamanda çalışmaktadır. Bu aşama paket yükleme, configration managemet pluginlerinin yüklenmesi, shell scriptlerinin (user-data ile gelen) çalıştırılması gibi işlemlerin yapılma aşamasıdır.


Cloud-init servislerini ve çalışma süreçlerini anlattıktan sonra örnek üzerinden deployment sürecini izleyellim. Örnekte platform olarak KVM private cloud ortamını kullanılacaktır. Kısa sürede linux imajı oluşturup, oluşturulan bu imajı KVM hipervizörü üzerinde deploy edeceğiz. Bu çalışmada linux dağıtımlarından CentOS ve Ubuntu için yapılacak olan işlem adımları ayrı ayrı anlatılacaktır. 

"cloud-init" yöntemi olarak da bilinen bu yöntemde öncelikle temel işletim sistemi imajı resmi sayfasından indirilir. Bu imaj işletim sisteminin yalın ve sade hali ile gelmektedir. CentOS işletim sistemi için https://cloud.centos.org/centos/ adresine girilerek kurulacak işletim sistemi sürümü seçilir (bu çalışmada CentOS 8 sürümünü deploy edeceğiz.) ve listelenen imajlardan "qcow2" uzantılı "GenericCloud" disk seçilir ve indirilir. 

~]# wget https://cloud.centos.org/centos/8/x86_64/images/CentOS-8-GenericCloud-8.1.1911-20200113.3.x86_64.qcow2 

meta-data, user-data, network-script dosyaları yml dosyası formatında oluşturulur. Bu örnekte deploy edeceğimiz işletim sisteminin docker uygulaması ile birlikte statik ip adresi alarak ayağa kalmasını istiyoruz. Bu sebeple aşağıda gösterilen dosyalar hazırlanır.

meta-data:
--------------------------------------------------------------------------------------------------------------------------
# cloud-metadata dosyası

instance-id: centos01

local-hostname: centos01


--------------------------------------------------------------------------------------------------------------------------
network-config:
--------------------------------------------------------------------------------------------------------------------------
version: 2
ethernets:
  eth0:
    dhcp4: false
    addresses:
      - 192.168.1.104/24
    gateway4: 192.168.1.1
    nameservers:
      search: [omer.com, bar.local]
      addresses: [8.8.8.8, 4.4.4.4]
--------------------------------------------------------------------------------------------------------------------------

user-data:
--------------------------------------------------------------------------------------------------------------------------
#cloud-config
password: "sizin parolanız"
chpasswd: {expire: False}
ssh_pwauth: True
sudo: ["ALL=(ALL) NOPASSWD:ALL"]
timezone: Europe/Istanbul
ssh_authorized_keys:
  - ssh-rsa  "public-key"
# runcmd bölümüne  deploy etttiğiniz işletim sistemi ayağa kalktıktan sonra çalıştıracağınız komutları yazabilirsiniz. Zira sunucu boot olduktan ve systemd ile servisler başlatıldıktan sonra bu #bölüm çalıştırılır.
runcmd:
# Kurulum sonrası tüm cloud servisler disable edilerek sunucunun sonraki açılışlarında bu servislerin çalışmasının önüne geçilir.
  - systemctl disable cloud-config.service
  - systemctl disable cloud-final.service
  - systemctl disable cloud-init-local.service
  - systemctl disable cloud-init.service
# keymap seçilir.
  - localectl set-keymap trq
# docker uygulaması için gerekli olan paket kurulumları 
  - yum install -y yum-utils
  - yum install vim-enhanced -y
  - dnf config-manager --add-repo=https://download.docker.com/linux/centos/docker-ce.repo
  - dnf install docker-ce --nobest -y
  - dnf install curl -y
# docker-compose kurulumu
  - curl -L "https://github.com/docker/compose/releases/download/1.25.0/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
  - systemctl start docker
  - systemctl enable docker
  - usermod -a -G docker centos
  - chmod +x /usr/local/bin/docker-compose
--------------------------------------------------------------------------------------------------------------------------
user-data, meta-data dosyaları oluşturulduktan sonra işletim sisteminin açılışta boot edileceği iso dosyası bu üç dosya kullanılarak oluşturulur. Böylece işletim sistemi ayağı kalkarken "data-source" olarak iso dosyası okuyup konfigürsayonları buradan çekecektir. Bu aşamada "genisoimage" yazılımı kullanılarak bu üç ayar dosyası iso kalıp dosyasının içine aktarılır.

~]# genisoimage -output centos.iso -volid cidata -joliet -rock user-data meta-data network-config 

Yukarıda çalıştırılan komut ile "centos.iso" adında bir imaj kalıp dosyası oluşturulmuş olacaktır. İşletim sistemini KVM üzerinde deploy etmeden önce indirmiş olduğumuz centos GenericCloud diskinin kapsitesini değiştirebilirsiniz. Varsayalında 10G olarak gelen bu diski "qemu-img" yazılımı ile düzenleyebilirsiniz. Bu örnekte disk kapasitesini 30G olarak güncelleyeceğiz. Ayrıca indirdiğimiz disk üzerinde düzenleme yapmadan önce bu diski kopyalayıp değişiklikleri kopyaladığımız disk üzerinde yapacağız.

~]# sudo cp CentOS-8-GenericCloud-8.1.1911-20200113.3.x86_64.qcow2 centos.img
~]# qemu-img resize centos.img  30g

Son olarak KVM üzerinde komut satırı ile sanal makine kurulumu yapmamıızı sağlayan "virt-install" yazılımı ile hazırlamış olduğumuz işletim sistemini aşağıdaki komut ile deploy edeceğiz. 

~]# virt-install --import --name centos --memory 2048 --vcpus 4 --disk centos.img,format=qcow2,bus=virtio --cdrom=centos.iso --network bridge=br0 --graphics vnc  &

Komutun çalışmasını müteakip işletim sistemi KVM üzerinde ayağı kalkacak ve cloud-init servisleri sırası ile çalışarak işletim sisteminin yapılandıracaktır. Bu yöntem ile ön hazırlığını yaparak çok kısa bir süre içerisinde işletim sistemlerini deploy edebilme imkanı kazanmış olacaksınız. Bir sonraki yazıda Ubuntu işletim sisteminin KVM üzerinde "cloud-init" metodu kullanılarak deploy edilmesini inceleyeceğiz.

Yararlanılan kaynaklar:
[1] Linux Bible Ninth Edition
[2] https://cloudinit.readthedocs.io/en/latest/