Kubernetes'te Kafka kümesi için uygun boyutu belirleme

Not. tercüme: Bu makalede Banzai Cloud, Kafka'nın Kubernetes'te kullanımını kolaylaştırmak için özel araçlarının nasıl kullanılabileceğine dair bir örnek paylaşıyor. Aşağıdaki talimatlar, altyapınızın en uygun boyutunu nasıl belirleyeceğinizi ve gerekli verimi elde etmek için Kafka'nın kendisini nasıl yapılandırabileceğinizi göstermektedir.

Kubernetes'te Kafka kümesi için uygun boyutu belirleme

Apache Kafka, güvenilir, ölçeklenebilir ve yüksek performanslı gerçek zamanlı akış sistemleri oluşturmaya yönelik dağıtılmış bir akış platformudur. Etkileyici yetenekleri Kubernetes kullanılarak genişletilebilir. Bunun için geliştirdik Açık Kaynak Kafka operatörü ve adı verilen bir araç süper tüpler. Kafka'yı Kubernetes'te çalıştırmanıza ve aracı yapılandırmasında ince ayar yapma, yeniden dengeleme ile metrik tabanlı ölçeklendirme, raf farkındalığı, "yumuşak" gibi çeşitli özelliklerini kullanmanıza olanak tanır. (zarif) güncellemelerin yayınlanması vb.

Kümenizde Supertubes'u deneyin:

curl https://getsupertubes.sh | sh и supertubes install -a --no-democluster --kubeconfig <path-to-eks-cluster-kubeconfig-file>

Veya iletişime geçin belgeleme. Ayrıca, işi Supertubes ve Kafka operatörü kullanılarak otomatikleştirilen Kafka'nın bazı yetenekleri hakkında da bilgi edinebilirsiniz. Blogda onlar hakkında zaten yazdık:

Kubernetes'te bir Kafka kümesi dağıtmaya karar verdiğinizde, muhtemelen temel altyapının en uygun boyutunu belirleme zorluğuyla ve aktarım hızı gereksinimlerini karşılamak için Kafka yapılandırmanızda ince ayar yapma ihtiyacıyla karşı karşıya kalacaksınız. Her aracının maksimum performansı, bellek, işlemci, disk hızı, ağ bant genişliği vb. gibi temel altyapı bileşenlerinin performansına göre belirlenir.

İdeal olarak aracı yapılandırması, tüm altyapı öğelerinin maksimum yetenekleriyle kullanılacak şekilde olmalıdır. Ancak gerçek hayatta bu kurulum oldukça karmaşıktır. Kullanıcıların aracıları bir veya iki bileşenin (disk, bellek veya işlemci) kullanımını en üst düzeye çıkaracak şekilde yapılandırması daha olasıdır. Genel olarak konuşursak, bir aracı, yapılandırması en yavaş bileşenin tam anlamıyla kullanılmasına izin verdiğinde maksimum performans gösterir. Bu şekilde bir komisyoncunun kaldırabileceği yük hakkında kabaca bir fikir edinebiliriz.

Teorik olarak, belirli bir yükü işlemek için gereken komisyoncu sayısını da tahmin edebiliriz. Ancak pratikte farklı seviyelerde o kadar çok konfigürasyon seçeneği vardır ki, belirli bir konfigürasyonun potansiyel performansını değerlendirmek (imkansız olmasa da) çok zordur. Başka bir deyişle, belirli bir performansa dayalı bir konfigürasyon planlamak çok zordur.

Supertubes kullanıcıları için genellikle şu yaklaşımı izliyoruz: Bazı konfigürasyonlarla (altyapı + ayarlar) başlıyoruz, ardından performansını ölçüyoruz, komisyoncu ayarlarını ayarlıyoruz ve süreci tekrarlıyoruz. Bu, altyapının en yavaş bileşeni tamamen kullanılıncaya kadar gerçekleşir.

Bu şekilde, bir kümenin belirli bir yükü işlemek için kaç aracıya ihtiyacı olduğu konusunda daha net bir fikir ediniriz (aracı sayısı aynı zamanda dayanıklılığı sağlamak için minimum mesaj kopyası sayısı, bölüm sayısı gibi diğer faktörlere de bağlıdır). liderler vb.). Ayrıca hangi altyapı bileşenlerinin dikey ölçeklendirme gerektirdiğine dair fikir ediniyoruz.

Bu makalede, başlangıç ​​konfigürasyonlarındaki en yavaş bileşenlerden en iyi şekilde yararlanmak ve bir Kafka kümesinin verimini ölçmek için attığımız adımlardan bahsedeceğiz. Oldukça dayanıklı bir yapılandırma, en az üç çalışan aracı gerektirir (min.insync.replicas=3), üç farklı erişilebilirlik bölgesine dağıtılmıştır. Kubernetes altyapısını yapılandırmak, ölçeklendirmek ve izlemek için hibrit bulutlara yönelik kendi konteyner yönetimi platformumuzu kullanıyoruz. Boru Hattı. Şirket içi (çıplak donanım, VMware) ve beş tür bulutu (Alibaba, AWS, Azure, Google, Oracle) ve bunların herhangi bir kombinasyonunu destekler.

Kafka kümesi altyapısı ve yapılandırması üzerine düşünceler

Aşağıdaki örneklerde bulut sağlayıcı olarak AWS'yi, Kubernetes dağıtımı olarak ise EKS'yi seçtik. Benzer bir konfigürasyon kullanılarak uygulanabilir. P.K.E. - Banzai Cloud'dan CNCF onaylı Kubernetes dağıtımı.

disk

Amazon çeşitli teklifler sunuyor EBS birim türleri. Merkezde gp2 и io1 ancak yüksek verim sağlamak için SSD sürücüleri var gp2 birikmiş kredileri tüketir (G/Ç kredileri), bu yüzden türü tercih ettik io1tutarlı yüksek verim sunar.

Örnek türleri

Kafka'nın performansı büyük ölçüde işletim sisteminin sayfa önbelleğine bağlıdır, bu nedenle aracılar (JVM) ve sayfa önbelleği için yeterli belleğe sahip örneklere ihtiyacımız var. Misal c5.2xbüyük - 16 GB belleğe sahip olduğundan iyi bir başlangıç ​​ve EBS ile çalışacak şekilde optimize edildi. Dezavantajı ise 30 saatte yalnızca 24 dakikayı geçmeyecek şekilde maksimum performans sağlayabilmesidir. İş yükünüz daha uzun bir süre boyunca en yüksek performansı gerektiriyorsa diğer bulut sunucusu türlerini dikkate almak isteyebilirsiniz. Biz de tam olarak bunu yaptık, durduk c5.4xbüyük. Maksimum verim sağlar 593,75 Mb/sn. Bir EBS biriminin maksimum verimi io1 örneğinden daha yüksek c5.4xbüyükdolayısıyla altyapının en yavaş unsuru muhtemelen bu bulut sunucusu tipinin G/Ç aktarım hızı olacaktır (yük testlerimizin de bunu onaylaması gerekir).

Ağ verimi, VM örneğinin ve diskin performansıyla karşılaştırıldığında yeterince büyük olmalıdır, aksi takdirde ağ bir darboğaz haline gelir. Bizim durumumuzda ağ arayüzü c5.4xbüyük 10 Gb/s'ye varan hızları destekler; bu, bir VM örneğinin G/Ç aktarım hızından önemli ölçüde yüksektir.

Broker Dağıtımı

CPU, bellek, ağ ve disk kaynakları için diğer işlemlerle rekabeti önlemek amacıyla aracıların özel düğümlere dağıtılması (Kubernetes'te planlanması) gerekir.

Java sürümü

Mantıksal seçim Java 11'dir çünkü JVM'nin aracının çalıştığı kapsayıcıda bulunan işlemcileri ve belleği doğru şekilde belirlemesi anlamında Docker ile uyumludur. CPU sınırlarının önemli olduğunu bilen JVM, GC iş parçacıklarının ve JIT iş parçacıklarının sayısını dahili olarak ve şeffaf bir şekilde ayarlar. Kafka imajını kullandık banzaicloud/kafka:2.13-2.4.0Java 2.4.0'de Kafka sürüm 2.13'ı (Scala 11) içerir.

Kubernetes'te Java/JVM hakkında daha fazla bilgi edinmek istiyorsanız aşağıdaki yayınlarımıza göz atın:

Aracı bellek ayarları

Aracı belleğini yapılandırmanın iki önemli yönü vardır: JVM ve Kubernetes bölmesi ayarları. JVM'nin kendi belleğinde bulunan Java metauzayına ve Kafka'nın aktif olarak kullandığı işletim sistemi sayfa önbelleğine yer açması için, bir bölme için ayarlanan bellek sınırının maksimum yığın boyutundan büyük olması gerekir. Testlerimizde Kafka brokerlarını parametrelerle başlattık -Xmx4G -Xms2Gve bölmenin bellek sınırı şuydu: 10 Gi. Lütfen JVM'nin bellek ayarlarının aşağıdakiler kullanılarak otomatik olarak alınabileceğini unutmayın: -XX:MaxRAMPercentage и -X:MinRAMPercentage, bölmenin bellek sınırına göre.

Aracı işlemci ayarları

Genel olarak konuşursak, Kafka'nın kullandığı iş parçacıklarının sayısını artırarak paralelliği artırarak performansı artırabilirsiniz. Kafka için ne kadar çok işlemci mevcutsa o kadar iyidir. Testimizde 6 işlemci sınırıyla başladık ve kademeli olarak (yinelemeler yaparak) bu sayıyı 15'e çıkardık. num.network.threads=12 Ağdan veri alan ve gönderen iş parçacıklarının sayısını artırmak için aracı ayarlarında. Takipçi komisyoncularının replikaları yeterince hızlı alamadıklarını hemen keşfettiler ve yükselttiler. num.replica.fetchers Takipçi komisyoncularının liderlerden gelen mesajları kopyalama hızını artırmak için 4'e çıktı.

Yük Oluşturma Aracı

Seçilen yük oluşturucunun (kıyaslama yapılan) Kafka kümesi maksimum yüküne ulaşmadan önce kapasitesinin tükenmediğinden emin olmalısınız. Başka bir deyişle, yük oluşturma aracının yeteneklerinin bir ön değerlendirmesini yapmak ve bunun için yeterli sayıda işlemci ve belleğe sahip örnek türlerini seçmek gerekir. Bu durumda aracımız Kafka kümesinin kaldırabileceğinden daha fazla yük üretecektir. Birçok denemeden sonra üç kopya üzerinde karar kıldık c5.4xbüyükher birinde çalışan bir jeneratör vardı.

kıyaslama

Performans ölçümü, aşağıdaki aşamaları içeren yinelemeli bir süreçtir:

  • altyapının kurulması (EKS kümesi, Kafka kümesi, yük oluşturma aracının yanı sıra Prometheus ve Grafana);
  • toplanan performans göstergelerindeki rastgele sapmaları filtrelemek için belirli bir süre boyunca bir yük oluşturulması;
  • aracının altyapısını ve yapılandırmasını gözlemlenen performans göstergelerine göre ayarlamak;
  • gerekli Kafka kümesi verimi düzeyine ulaşılıncaya kadar işlemin tekrarlanması. Aynı zamanda tutarlı bir şekilde tekrarlanabilir olmalı ve üretimde minimum değişiklik göstermelidir.

Sonraki bölümde test kümesi kıyaslama işlemi sırasında gerçekleştirilen adımlar açıklanmaktadır.

Araçlar

Temel yapılandırmayı hızla dağıtmak, yük oluşturmak ve performansı ölçmek için aşağıdaki araçlar kullanıldı:

  • Banzai Bulut Boru Hattı Amazon c'den bir EKS kümesi düzenlemek için Prometheus (Kafka ve altyapı ölçümlerini toplamak için) ve grafana (bu ölçümleri görselleştirmek için). Faydalandık Birleşik в Boru Hattı birleşik izleme, merkezi günlük toplama, güvenlik açığı taraması, olağanüstü durum kurtarma, kurumsal düzeyde güvenlik ve çok daha fazlasını sağlayan hizmetler.
  • Sangrenel — Kafka kümesinin yük testi için bir araç.
  • Kafka ölçümlerini ve altyapısını görselleştirmek için Grafana kontrol panelleri: Kubernetes Kafka, Düğüm Aktarıcı.
  • Kubernetes'te Kafka kümesi kurmanın en kolay yolu için Supertubes CLI. Zookeeper, Kafka operatörü, Envoy ve diğer birçok bileşen Kubernetes'te üretime hazır bir Kafka kümesini çalıştıracak şekilde kurulur ve uygun şekilde yapılandırılır.
    • Kurulum için süper tüpler CLI verilen talimatları kullanın burada.

Kubernetes'te Kafka kümesi için uygun boyutu belirleme

EKS kümesi

Özel çalışan düğümleriyle bir EKS kümesi hazırlayın c5.4xbüyük Kafka aracılarına sahip bölmeler için farklı kullanılabilirlik alanlarının yanı sıra yük oluşturucu ve izleme altyapısı için özel düğümlerde.

banzai cluster create -f https://raw.githubusercontent.com/banzaicloud/kafka-operator/master/docs/benchmarks/infrastructure/cluster_eks_202001.json

EKS kümesi çalışır hale geldikten sonra entegresini etkinleştirin izleme hizmeti — Prometheus ve Grafana'yı bir kümeye yerleştirecek.

Kafka sistem bileşenleri

Süper tüpler CLI'yi kullanarak Kafka sistem bileşenlerini (Zookeeper, kafka-operator) EKS'ye yükleyin:

supertubes install -a --no-democluster --kubeconfig <path-to-eks-cluster-kubeconfig-file>

Kafka kümesi

EKS varsayılan olarak şu türdeki EBS birimlerini kullanır: gp2, bu nedenle birimlere dayalı ayrı bir depolama sınıfı oluşturmanız gerekir io1 Kafka kümesi için:

kubectl create -f - <<EOF
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: fast-ssd
provisioner: kubernetes.io/aws-ebs
parameters:
  type: io1
  iopsPerGB: "50"
  fsType: ext4
volumeBindingMode: WaitForFirstConsumer
EOF

Aracılar için parametreyi ayarlayın min.insync.replicas=3 ve aracı bölmelerini üç farklı kullanılabilirlik bölgesindeki düğümlere dağıtın:

supertubes cluster create -n kafka --kubeconfig <path-to-eks-cluster-kubeconfig-file> -f https://raw.githubusercontent.com/banzaicloud/kafka-operator/master/docs/benchmarks/infrastructure/kafka_202001_3brokers.yaml --wait --timeout 600

Konular

Üç yük oluşturucu örneğini paralel olarak çalıştırdık. Her biri kendi konusuna yazıyor yani toplamda üç konuya ihtiyacımız var:

supertubes cluster topic create -n kafka --kubeconfig <path-to-eks-cluster-kubeconfig-file> -f -<<EOF
apiVersion: kafka.banzaicloud.io/v1alpha1
kind: KafkaTopic
metadata:
  name: perftest1
spec:
  name: perftest1
  partitions: 12
  replicationFactor: 3
  retention.ms: '28800000'
  cleanup.policy: delete
EOF

supertubes cluster topic create -n kafka --kubeconfig <path-to-eks-cluster-kubeconfig-file> -f -<<EOF
apiVersion: kafka.banzaicloud.io/v1alpha1
kind: KafkaTopic
metadata:
    name: perftest2
spec:
  name: perftest2
  partitions: 12
  replicationFactor: 3
  retention.ms: '28800000'
  cleanup.policy: delete
EOF

supertubes cluster topic create -n kafka --kubeconfig <path-to-eks-cluster-kubeconfig-file> -f -<<EOF
apiVersion: kafka.banzaicloud.io/v1alpha1
kind: KafkaTopic
metadata:
  name: perftest3
spec:
  name: perftest3
  partitions: 12
  replicationFactor: 3
  retention.ms: '28800000'
  cleanup.policy: delete
EOF

Her konu için çoğaltma faktörü 3'tür; bu, yüksek kullanılabilirliğe sahip üretim sistemleri için önerilen minimum değerdir.

Yük Oluşturma Aracı

Yük oluşturucunun üç kopyasını yayınladık (her biri ayrı bir konu altında yazılmıştır). Yük oluşturma bölmeleri için, düğüm benzeşimini yalnızca kendilerine ayrılan düğümlerde planlanacak şekilde ayarlamanız gerekir:

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  labels:
    app: loadtest
  name: perf-load1
  namespace: kafka
spec:
  progressDeadlineSeconds: 600
  replicas: 1
  revisionHistoryLimit: 10
  selector:
    matchLabels:
      app: loadtest
  strategy:
    rollingUpdate:
      maxSurge: 25%
      maxUnavailable: 25%
    type: RollingUpdate
  template:
    metadata:
      creationTimestamp: null
      labels:
        app: loadtest
    spec:
      affinity:
        nodeAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            nodeSelectorTerms:
            - matchExpressions:
              - key: nodepool.banzaicloud.io/name
                operator: In
                values:
                - loadgen
      containers:
      - args:
        - -brokers=kafka-0:29092,kafka-1:29092,kafka-2:29092,kafka-3:29092
        - -topic=perftest1
        - -required-acks=all
        - -message-size=512
        - -workers=20
        image: banzaicloud/perfload:0.1.0-blog
        imagePullPolicy: Always
        name: sangrenel
        resources:
          limits:
            cpu: 2
            memory: 1Gi
          requests:
            cpu: 2
            memory: 1Gi
        terminationMessagePath: /dev/termination-log
        terminationMessagePolicy: File
      dnsPolicy: ClusterFirst
      restartPolicy: Always
      schedulerName: default-scheduler
      securityContext: {}
      terminationGracePeriodSeconds: 30

Dikkat edilmesi gereken birkaç nokta:

  • Yük oluşturucu 512 bayt uzunluğunda mesajlar üretir ve bunları 500 mesajlık gruplar halinde Kafka'ya yayınlar.
  • Bir argüman kullanma -required-acks=all İletinin tüm senkronize kopyaları Kafka aracıları tarafından alınıp onaylandığında yayın başarılı sayılır. Bu, kıyaslamada yalnızca liderlerin mesaj alma hızını değil, aynı zamanda takipçilerinin de mesajları kopyalama hızını da ölçtüğümüz anlamına geliyor. Bu testin amacı tüketicinin okuma hızını değerlendirmek değildir. (tüketiciler) hala işletim sistemi sayfa önbelleğinde kalan yakın zamanda alınan mesajlar ve bunun diskte depolanan mesajların okuma hızıyla karşılaştırılması.
  • Yük oluşturucu 20 işçiyi paralel olarak çalıştırır (-workers=20). Her işçi, işçinin Kafka kümesiyle bağlantısını paylaşan 5 üretici içerir. Sonuç olarak her jeneratörün 100 üreticisi vardır ve bunların hepsi Kafka kümesine mesaj gönderir.

Kümenin durumunu izleme

Kafka kümesinin yük testi sırasında, bölmenin yeniden başlatılmadığından, senkronizasyon dışı kopya olmadığından ve minimum dalgalanmalarla maksimum aktarım hızından emin olmak için durumunu da izledik:

  • Yük oluşturucu, yayınlanan mesajların sayısı ve hata oranı hakkında standart istatistikler yazar. Hata oranı aynı kalmalı 0,00%.
  • Seyir kontrolükafka-operatörü tarafından konuşlandırılan , kümenin durumunu da izleyebileceğimiz bir kontrol paneli sağlar. Bu paneli görüntülemek için şunları yapın:
    supertubes cluster cruisecontrol show -n kafka --kubeconfig <path-to-eks-cluster-kubeconfig-file>
  • ISR düzeyi (“senkronize” kopyaların sayısı) Büzülme ve genişleme 0'a eşittir.

Ölçüm sonuçları

3 aracı, mesaj boyutu - 512 bayt

Bölümlerin üç aracıya eşit olarak dağıtılması sayesinde performansa ulaşmayı başardık ~500 Mb/s (saniyede yaklaşık 990 bin mesaj):

Kubernetes'te Kafka kümesi için uygun boyutu belirleme

Kubernetes'te Kafka kümesi için uygun boyutu belirleme

Kubernetes'te Kafka kümesi için uygun boyutu belirleme

JVM sanal makinesinin bellek tüketimi 2 GB'ı aşmadı:

Kubernetes'te Kafka kümesi için uygun boyutu belirleme

Kubernetes'te Kafka kümesi için uygun boyutu belirleme

Kubernetes'te Kafka kümesi için uygun boyutu belirleme

Disk aktarım hızı, aracıların çalıştığı üç örneğin tamamında maksimum G/Ç düğümü aktarım hızına ulaştı:

Kubernetes'te Kafka kümesi için uygun boyutu belirleme

Kubernetes'te Kafka kümesi için uygun boyutu belirleme

Kubernetes'te Kafka kümesi için uygun boyutu belirleme

Düğümlerin bellek kullanımına ilişkin verilerden, sistem ara belleğe alma ve önbelleğe alma işleminin ~10-15 GB aldığı sonucu çıkıyor:

Kubernetes'te Kafka kümesi için uygun boyutu belirleme

Kubernetes'te Kafka kümesi için uygun boyutu belirleme

Kubernetes'te Kafka kümesi için uygun boyutu belirleme

3 aracı, mesaj boyutu - 100 bayt

Mesaj boyutu küçüldükçe verim yaklaşık %15-20 oranında düşer: her mesajın işlenmesi için harcanan süre onu etkiler. Ayrıca işlemci yükü neredeyse iki katına çıktı.

Kubernetes'te Kafka kümesi için uygun boyutu belirleme

Kubernetes'te Kafka kümesi için uygun boyutu belirleme

Kubernetes'te Kafka kümesi için uygun boyutu belirleme

Aracı düğümlerin hala kullanılmamış çekirdekleri olduğundan, Kafka yapılandırması değiştirilerek performans artırılabilir. Bu kolay bir iş değildir, dolayısıyla verimi artırmak için daha büyük mesajlarla çalışmak daha iyidir.

4 aracı, mesaj boyutu - 512 bayt

Sadece yeni aracılar ekleyerek ve bölüm dengesini koruyarak Kafka kümesinin performansını kolayca artırabilirsiniz (bu, yükün aracılar arasında eşit şekilde dağıtılmasını sağlar). Bizim durumumuzda, bir aracı eklendikten sonra küme verimi şuna yükseldi: ~580 Mb/s (saniyede ~1,1 milyon mesaj). Büyümenin beklenenden daha az olduğu ortaya çıktı: Bu esas olarak bölümler arasındaki dengesizlikle açıklanıyor (tüm brokerlar yeteneklerinin zirvesinde çalışmıyor).

Kubernetes'te Kafka kümesi için uygun boyutu belirleme

Kubernetes'te Kafka kümesi için uygun boyutu belirleme

Kubernetes'te Kafka kümesi için uygun boyutu belirleme

Kubernetes'te Kafka kümesi için uygun boyutu belirleme

JVM makinesinin bellek tüketimi 2 GB'nin altında kaldı:

Kubernetes'te Kafka kümesi için uygun boyutu belirleme

Kubernetes'te Kafka kümesi için uygun boyutu belirleme

Kubernetes'te Kafka kümesi için uygun boyutu belirleme

Kubernetes'te Kafka kümesi için uygun boyutu belirleme

Sürücülü komisyoncuların çalışmaları, bölümlerin dengesizliğinden etkilendi:

Kubernetes'te Kafka kümesi için uygun boyutu belirleme

Kubernetes'te Kafka kümesi için uygun boyutu belirleme

Kubernetes'te Kafka kümesi için uygun boyutu belirleme

Kubernetes'te Kafka kümesi için uygun boyutu belirleme

Bulgular

Yukarıda sunulan yinelemeli yaklaşım, yüzlerce tüketiciyi, yeniden bölümlendirmeyi, güncellemelerin yuvarlanmasını, kapsülün yeniden başlatılmasını vb. içeren daha karmaşık senaryoları kapsayacak şekilde genişletilebilir. Bütün bunlar, Kafka kümesinin çeşitli koşullardaki yeteneklerinin sınırlarını değerlendirmemize, işleyişindeki darboğazları belirlememize ve bunlarla mücadele etmenin yollarını bulmamıza olanak tanıyor.

Supertube'ları hızlı ve kolay bir şekilde bir kümeyi dağıtmak, yapılandırmak, aracıları ve konuları eklemek/kaldırmak, uyarılara yanıt vermek ve Kafka'nın genel olarak Kubernetes'te düzgün çalışmasını sağlamak için tasarladık. Amacımız ana göreve odaklanmanıza (“Kafka mesajları oluşturmak” ve “tüketmek”) yardımcı olmak ve tüm zor işi Supertubes ve Kafka operatörüne bırakmaktır.

Banzai Bulut teknolojileri ve Açık Kaynak projeleriyle ilgileniyorsanız, şu adresten şirkete abone olun: GitHub, LinkedIn veya Twitter.

çevirmenden PS

Blogumuzda da okuyun:

Kaynak: habr.com

Yorum ekle