Arsitektur Logging

Log aplikasi dan sistem dapat membantu kamu untuk memahami apa yang terjadi di dalam klaster kamu. Log berguna untuk mengidentifikasi dan menyelesaikan masalah serta memonitor aktivitas klaster. Hampir semua aplikasi modern mempunyai sejenis mekanisme log sehingga hampir semua mesin kontainer didesain untuk mendukung suatu mekanisme logging. Metode logging yang paling mudah untuk aplikasi dalam bentuk kontainer adalah menggunakan standard output dan standard error.

Namun, fungsionalitas bawaan dari mesin kontainer atau runtime biasanya tidak cukup memadai sebagai solusi log. Contohnya, jika sebuah kontainer gagal, sebuah pod dihapus, atau suatu node mati, kamu biasanya tetap menginginkan untuk mengakses log dari aplikasimu. Oleh sebab itu, log sebaiknya berada pada penyimpanan dan lifecyle yang terpisah dari node, pod, atau kontainer. Konsep ini dinamakan sebagai logging pada level klaster. Logging pada level klaster ini membutuhkan backend yang terpisah untuk menyimpan, menganalisis, dan mengkueri log. Kubernetes tidak menyediakan solusi bawaan untuk penyimpanan data log, namun kamu dapat mengintegrasikan beragam solusi logging yang telah ada ke dalam klaster Kubernetes kamu.

Arsitektur logging pada level klaster yang akan dijelaskan berikut mengasumsikan bahwa sebuah logging backend telah tersedia baik di dalam maupun di luar klastermu. Meskipun kamu tidak tertarik menggunakan logging pada level klaster, penjelasan tentang bagaimana log disimpan dan ditangani pada node di bawah ini mungkin dapat berguna untukmu.

Hal dasar logging pada Kubernetes

Pada bagian ini, kamu dapat melihat contoh tentang dasar logging pada Kubernetes yang mengeluarkan data pada standard output. Demonstrasi berikut ini menggunakan sebuah spesifikasi pod dengan kontainer yang akan menuliskan beberapa teks ke standard output tiap detik.

apiVersion: v1
kind: Pod
metadata:
  name: counter
spec:
  containers:
  - name: count
    image: busybox
    args: [/bin/sh, -c,
            'i=0; while true; do echo "$i: $(date)"; i=$((i+1)); sleep 1; done']

Untuk menjalankan pod ini, gunakan perintah berikut:

kubectl apply -f https://k8s.io/examples/debug/counter-pod.yaml

Keluarannya adalah:

pod/counter created

Untuk mengambil log, gunakan perintah kubectl logs sebagai berikut:

kubectl logs counter

Keluarannya adalah:

0: Mon Jan  1 00:00:00 UTC 2001
1: Mon Jan  1 00:00:01 UTC 2001
2: Mon Jan  1 00:00:02 UTC 2001
...

Kamu dapat menambahkan parameter --previous pada perintah kubectl logs untuk mengambil log dari kontainer sebelumnya yang gagal atau crash. Jika pod kamu memiliki banyak kontainer, kamu harus menspesifikasikan kontainer mana yang kamu ingin akses lognya dengan menambahkan nama kontainer pada perintah tersebut. Lihat dokumentasi kubectl logs untuk informasi lebih lanjut.

Node-level logging

Node-level logging

Semua hal yang ditulis oleh aplikasi dalam kontainer ke stdout dan stderr akan ditangani dan diarahkan ke suatu tempat oleh mesin atau engine kontainer. Contohnya,mesin kontainer Docker akan mengarahkan kedua aliran tersebut ke suatu logging driver, yang akan dikonfigurasi pada Kubernetes untuk menuliskan ke dalam berkas dalam format json.

Secara default, jika suatu kontainer restart, kubelet akan menjaga kontainer yang mati tersebut beserta lognya. Namun jika suatu pod dibuang dari node, maka semua hal dari kontainernya juga akan dibuang, termasuk lognya.

Hal lain yang perlu diperhatikan dalam logging pada level node adalah implementasi rotasi log, sehingga log tidak menghabiskan semua penyimpanan yang tersedia pada node. Kubernetes saat ini tidak bertanggung jawab dalam melakukan rotasi log, namun deployment tool seharusnya memberikan solusi terhadap masalah tersebut. Contohnya, pada klaster Kubernetes, yang di deployed menggunakan kube-up.sh, terdapat alat bernama logrotate yang dikonfigurasi untuk berjalan tiap jamnya. Kamu juga dapat menggunakan runtime kontainer untuk melakukan rotasi log otomatis, misalnya menggunakan log-opt Docker. Pada kube-up.sh, metode terakhir digunakan untuk COS image pada GCP, sedangkan metode pertama digunakan untuk lingkungan lainnya. Pada kedua metode, secara default akan dilakukan rotasi pada saat berkas log melewati 10MB.

Sebagai contoh, kamu dapat melihat informasi lebih rinci tentang bagaimana kube-up.sh mengatur logging untuk COS image pada GCP yang terkait dengan script.

Saat kamu menjalankan perintah kubectl logs seperti pada contoh tadi, kubelet di node tersebut akan menangani permintaan untuk membaca langsung isi berkas log sebagai respon.

Komponen sistem log

Terdapat dua jenis komponen sistem: yaitu yang berjalan di dalam kontainer dan komponen lain yang tidak berjalan di dalam kontainer. Sebagai contoh:

  • Kubernetes scheduler dan kube-proxy berjalan di dalam kontainer.
  • Kubelet dan runtime kontainer, contohnya Docker, tidak berjalan di dalam kontainer.

Pada mesin yang menggunakan systemd, kubelet dan runtime runtime menulis ke journald. Jika systemd tidak tersedia, keduanya akan menulis ke berkas .log pada folder /var/log. Komponen sistem di dalam kontainer akan selalu menuliskan ke folder /var/log, melewati mekanisme default logging. Mereka akan menggunakan logging library klog. Kamu dapat menemukan konvensi tentang tingkat kegawatan logging untuk komponen-komponen tersebut pada dokumentasi development logging.

Seperti halnya pada log kontainer, komponen sistem yang menuliskan log pada folder /var/log juga harus melakukan rotasi log. Pada klaster Kubernetes yang menggunakan kube-up.sh, log tersebut telah dikonfigurasi dan akan dirotasi oleh logrotate secara harian atau saat ukuran log melebihi 100MB.

Arsitektur klaster-level logging

Meskipun Kubernetes tidak menyediakan solusi bawaan untuk logging level klaster, ada beberapa pendekatan yang dapat kamu pertimbangkan. Berikut beberapa diantaranya:

  • Menggunakan agen logging pada level node yang berjalan pada setiap node.
  • Menggunakan kontainer sidecar khusus untuk logging aplikasi di dalam pod.
  • Mengeluarkan log langsung ke backend dari dalam aplikasi

Menggunakan agen node-level logging

Menggunakan agen node-level logging

Kamu dapat mengimplementasikan klaster-level logging dengan menggunakan agen yang berjalan pada setiap node. Agen logging merupakan perangkat khusus yang akan mengekspos log atau mengeluarkan log ke backend. Umumnya agen logging merupakan kontainer yang memiliki akses langsung ke direktori tempat berkas log berada dari semua kontainer aplikasi yang berjalan pada node tersebut.

Karena agen logging harus berjalan pada setiap node, umumnya dilakukan dengan menggunakan replika DaemonSet, manifest pod, atau menjalankan proses khusus pada node. Namun dua cara terakhir sudah dideprekasi dan sangat tidak disarankan.

Menggunakan agen logging pada level node merupakan cara yang paling umum dan disarankan untuk klaster Kubernetes. Hal ini karena hanya dibutuhkan satu agen tiap node dan tidak membutuhkan perubahan apapun dari sisi aplikasi yang berjalan pada node. Namun, node-level logging hanya dapat dilakukan untuk aplikasi yang menggunakan standard output dan standard error.

Kubernetes tidak menspesifikasikan khusus suatu agen logging, namun ada dua agen logging yang dimasukkan dalam rilis Kubernetes: Stackdriver Logging untuk digunakan pada Google Cloud Platform, dan Elasticsearch. Kamu dapat melihat informasi dan instruksi pada masing-masing dokumentasi. Keduanya menggunakan fluentd dengan konfigurasi kustom sebagai agen pada node.

Menggunakan kontainer sidecar dengan agen logging

Kamu dapat menggunakan kontainer sidecar dengan salah satu cara berikut:

  • Kontainer sidecar mengeluarkan log aplikasi ke stdout miliknya sendiri.
  • Kontainer sidecar menjalankan agen logging yang dikonfigurasi untuk mengambil log dari aplikasi kontainer.

Kontainer streaming sidecar

Kontainer sidecar dengan kontainer streaming

Kamu dapat memanfaatkan kubelet dan agen logging yang telah berjalan pada tiap node dengan menggunakan kontainer sidecar. Kontainer sidecar dapat membaca log dari sebuah berkas, socket atau journald. Tiap kontainer sidecar menuliskan log ke stdout atau stderr mereka sendiri.

Dengan menggunakan cara ini kamu dapat memisahkan aliran log dari bagian-bagian yang berbeda dari aplikasimu, yang beberapa mungkin tidak mendukung log ke stdout dan stderr. Perubahan logika aplikasimu dengan menggunakan cara ini cukup kecil, sehingga hampir tidak ada overhead. Selain itu, karena stdout dan stderr ditangani oleh kubelet, kamu juga dapat menggunakan alat bawaan seperti kubectl logs.

Sebagai contoh, sebuah pod berjalan pada satu kontainer tunggal, dan kontainer menuliskan ke dua berkas log yang berbeda, dengan dua format yang berbeda pula. Berikut ini file konfigurasi untuk Pod:

apiVersion: v1
kind: Pod
metadata:
  name: counter
spec:
  containers:
  - name: count
    image: busybox
    args:
    - /bin/sh
    - -c
    - >
      i=0;
      while true;
      do
        echo "$i: $(date)" >> /var/log/1.log;
        echo "$(date) INFO $i" >> /var/log/2.log;
        i=$((i+1));
        sleep 1;
      done      
    volumeMounts:
    - name: varlog
      mountPath: /var/log
  volumes:
  - name: varlog
    emptyDir: {}

Hal ini akan menyulitkan untuk mengeluarkan log dalam format yang berbeda pada aliran log yang sama, meskipun kamu dapat me-redirect keduanya ke stdout dari kontainer. Sebagai gantinya, kamu dapat menggunakan dua buah kontainer sidecar. Tiap kontainer sidecar dapat membaca suatu berkas log tertentu dari shared volume kemudian mengarahkan log ke stdout-nya sendiri.

Berikut file konfigurasi untuk pod yang memiliki dua buah kontainer sidecard:

apiVersion: v1
kind: Pod
metadata:
  name: counter
spec:
  containers:
  - name: count
    image: busybox:1.28
    args:
    - /bin/sh
    - -c
    - >
      i=0;
      while true;
      do
        echo "$i: $(date)" >> /var/log/1.log;
        echo "$(date) INFO $i" >> /var/log/2.log;
        i=$((i+1));
        sleep 1;
      done      
    volumeMounts:
    - name: varlog
      mountPath: /var/log
  - name: count-log-1
    image: busybox:1.28
    args: [/bin/sh, -c, 'tail -n+1 -F /var/log/1.log']
    volumeMounts:
    - name: varlog
      mountPath: /var/log
  - name: count-log-2
    image: busybox:1.28
    args: [/bin/sh, -c, 'tail -n+1 -F /var/log/2.log']
    volumeMounts:
    - name: varlog
      mountPath: /var/log
  volumes:
  - name: varlog
    emptyDir: {}

Saat kamu menjalankan pod ini, kamu dapat mengakses tiap aliran log secara terpisah dengan menjalankan perintah berikut:

kubectl logs counter count-log-1
0: Mon Jan  1 00:00:00 UTC 2001
1: Mon Jan  1 00:00:01 UTC 2001
2: Mon Jan  1 00:00:02 UTC 2001
...
kubectl logs counter count-log-2
Mon Jan  1 00:00:00 UTC 2001 INFO 0
Mon Jan  1 00:00:01 UTC 2001 INFO 1
Mon Jan  1 00:00:02 UTC 2001 INFO 2
...

Agen node-level yang terpasang di klastermu akan mengambil aliran log tersebut secara otomatis tanpa perlu melakukan konfigurasi tambahan. Bahkan jika kamu mau, kamu dapat mengonfigurasi agen untuk melakukan parse baris log tergantung dari kontainer sumber awalnya.

Sedikit catatan, meskipun menggunakan memori dan CPU yang cukup rendah (sekitar beberapa milicore untuk CPU dan beberapa megabytes untuk memori), penulisan log ke file kemudian mengalirkannya ke stdout dapat berakibat penggunaan disk yang lebih besar. Jika kamu memiliki aplikasi yang menuliskan ke file tunggal, umumnya lebih baik menggunakan /dev/stdout sebagai tujuan daripada menggunakan pendekatan dengan kontainer sidecar.

Kontainer sidecar juga dapat digunakan untuk melakukan rotasi berkas log yang tidak dapat dirotasi oleh aplikasi itu sendiri. Contoh dari pendekatan ini adalah sebuah kontainer kecil yang menjalankan rotasi log secara periodik. Namun, direkomendasikan untuk menggunakan stdout dan stderr secara langsung dan menyerahkan kebijakan rotasi dan retensi pada kubelet.

Kontainer sidecar dengan agen logging

Kontainer sidecar dengan agen logging

Jika agen node-level logging tidak cukup fleksible untuk kebutuhanmu, kamu dapat membuat kontainer sidecar dengan agen logging yang terpisah, yang kamu konfigurasi spesifik untuk berjalan dengan aplikasimu.

Sebagai contoh, kamu dapat menggunakan Stackdriver, yang menggunakan fluentd sebagai agen logging. Berikut ini dua file konfigurasi yang dapat kamu pakai untuk mengimplementasikan cara ini. File yang pertama berisi sebuah ConfigMap untuk mengonfigurasi fluentd.

apiVersion: v1
kind: ConfigMap
metadata:
  name: fluentd-config
data:
  fluentd.conf: |
    <source>
      type tail
      format none
      path /var/log/1.log
      pos_file /var/log/1.log.pos
      tag count.format1
    </source>

    <source>
      type tail
      format none
      path /var/log/2.log
      pos_file /var/log/2.log.pos
      tag count.format2
    </source>

    <match **>
      type google_cloud
    </match>    

File yang kedua mendeskripsikan sebuah pod yang memiliki kontainer sidecar yang menjalankan fluentd. Pod ini melakukan mount sebuah volume yang akan digunakan fluentd untuk mengambil data konfigurasinya.

apiVersion: v1
kind: Pod
metadata:
  name: counter
spec:
  containers:
  - name: count
    image: busybox
    args:
    - /bin/sh
    - -c
    - >
      i=0;
      while true;
      do
        echo "$i: $(date)" >> /var/log/1.log;
        echo "$(date) INFO $i" >> /var/log/2.log;
        i=$((i+1));
        sleep 1;
      done      
    volumeMounts:
    - name: varlog
      mountPath: /var/log
  - name: count-agent
    image: registry.k8s.io/fluentd-gcp:1.30
    env:
    - name: FLUENTD_ARGS
      value: -c /etc/fluentd-config/fluentd.conf
    volumeMounts:
    - name: varlog
      mountPath: /var/log
    - name: config-volume
      mountPath: /etc/fluentd-config
  volumes:
  - name: varlog
    emptyDir: {}
  - name: config-volume
    configMap:
      name: fluentd-config

Setelah beberapa saat, kamu akan mendapati pesan log pada interface Stackdriver.

Ingat, ini hanya contoh saja dan kamu dapat mengganti fluentd dengan agen logging lainnya, yang dapat membaca sumber apa saja dari dalam kontainer aplikasi.

Mengekspos log langsung dari aplikasi

Mengekspos log langsung dari aplikasi

Kamu dapat mengimplementasikan klaster-level logging dengan mengekspos atau mengeluarkan log langsung dari tiap aplikasi; namun cara implementasi mekanisme logging tersebut diluar cakupan dari Kubernetes.

Last modified February 22, 2023 at 9:09 AM PST: 更新编辑 (f4a7975)