1 - Service

Pod pada Kubernetes bersifat mortal. Artinya apabila pod-pod tersebut dibuat dan kemudian mati, pod-pod tersebut tidak akan dihidupkan kembali. ReplicaSets secara khusus bertugas membuat dan menghapus Pod secara dinamis (misalnya, pada proses scaling out atau scaling in). Meskipun setiap Pod memiliki alamat IP-nya masing-masing, kamu tidak dapat mengandalkan alamat IP yang diberikan pada pod-pod tersebut, karena alamat IP yang diberikan tidak stabil. Hal ini kemudian menimbulkan pertanyaan baru: apabila sebuah sekumpulan Pod (yang selanjutnya kita sebut backend) menyediakan service bagi sebuah sekumpulan Pod lain (yang selanjutnya kita sebut frontend) di dalam klaster Kubernetes, bagaimana cara frontend menemukan backend mana yang digunakan?

Inilah alasan kenapa Service ada.

Sebuah Service pada Kubernetes adalah sebuah abstraksi yang memberikan definisi set logis yang terdiri beberapa Pod serta policy bagaimana cara kamu mengakses sekumpulan Pod tadi - seringkali disebut sebagai microservices. Set Pod yang dirujuk oleh suatu Service (biasanya) ditentukan oleh sebuah Label Selector (lihat penjelasan di bawah untuk mengetahui alasan kenapa kamu mungkin saja membutuhkan Service tanpa sebuah selector).

Sebagai contoh, misalnya terdapat sebuah backend yang menyediakan fungsionalitas image-processing yang memiliki 3 buah replica. Replica-replica tadi sifatnya sepadan - dengan kata lain frontend tidak peduli backend manakah yang digunakan. Meskipun Pod penyusun sekumpulan backend bisa berubah, frontend tidak perlu peduli bagaimana proses ini dijalankan atau menyimpan list dari backend-backend yang ada saat itu. Service memiliki tujuan untuk decouple mekanisme ini.

Untuk aplikasi yang dijalankan di atas Kubernetes, Kubernetes menyediakan API endpoint sederhana yang terus diubah apabila state sebuah sekumpulan Pod di dalam suatu Service berubah. Untuk aplikasi non-native, Kubernetes menyediakan bridge yang berbasis virtual-IP bagi Service yang diarahkan pada Pod backend.

Mendefinisikan sebuah Service

Sebuah Service di Kubernetes adalah sebuah objek REST, layaknya sebuah Pod. Seperti semua objek REST, definisi Service dapat dikirim dengan method POST pada apiserver untuk membuat sebuah instans baru. Sebagai contoh, misalnya saja kamu memiliki satu sekumpulan Pod yang mengekspos port 9376 dan memiliki label "app=MyApp".

kind: Service
apiVersion: v1
metadata:
  name: my-service
spec:
  selector:
    app: MyApp
  ports:
  - protocol: TCP
    port: 80
    targetPort: 9376

Spesifikasi ini akan ditranslasikan sebagai sebuah objek Service baru dengan nama "my-service" dengan target port 9376 pada setiap Pod yang memiliki label "app=MyApp". Service ini juga akan memiliki alamat IP tersendiri (yang terkadang disebut sebagai "cluster IP"), yang nantinya akan digunakan oleh service proxy (lihat di bagian bawah). Selector pada Service akan selalu dievaluasi dan hasilnya akan kembali dikirim dengan menggunakan method POST ke objek Endpoints yang juga disebut "my-service".

Perhatikan bahwa sebuah Service dapat melakukan pemetaan setiap incoming port pada targetPort mana pun. Secara default, field targetPort akan memiliki value yang sama dengan value dari field port. Hal menarik lainnya adalah value dari targetPort bisa saja berupa string yang merujuk pada nama dari port yang didefinisikan pada Pod backend. Nomor port yang diberikan pada port dengan nama tadi bisa saja memiliki nilai yang berbeda di setiap Pod backend. Hal ini memberikan fleksibilitas pada saat kamu melakukan deploy atau melakukan perubahan terhadap Service. Misalnya saja suatu saat kamu ingin mengubah nomor port yang ada pada Pod backend pada rilis selanjutnya tanpa menyebabkan permasalahan pada sisi klien.

Secara default, protokol yang digunakan pada service adalah TCP, tapi kamu bisa saja menggunakan protokol yang tersedia. Karena banyak Service memiliki kebutuhan untuk mengekspos lebih dari sebuah port, Kubernetes menawarkan definisi multiple port pada sebuah objek Service. Setiap definisi port dapat memiliki protokol yang berbeda.

Service tanpa selector

Secara umum, Service memberikan abstraksi mekanisme yang dilakukan untuk mengakses Pod, tapi mereka juga melakukan abstraksi bagi backend lainnya. Misalnya saja:

  • Kamu ingin memiliki sebuah basis data eksternal di environment production tapi pada tahap test, kamu ingin menggunakan basis datamu sendiri.
  • Kamu ingin merujuk service kamu pada service lainnya yang berada pada Namespace yang berbeda atau bahkan klaster yang berbeda.
  • Kamu melakukan migrasi workloads ke Kubernetes dan beberapa backend yang kamu miliki masih berada di luar klaster Kubernetes.

Berdasarkan skenario-skenario di atas, kamu dapat membuat sebuah Service tanpa selector:

kind: Service
apiVersion: v1
metadata:
  name: my-service
spec:
  ports:
  - protocol: TCP
    port: 80
    targetPort: 9376

Karena Service ini tidak memiliki selector, objek Endpoints bagi Service ini tidak akan dibuat. Dengan demikian, kamu bisa membuat Endpoints yang kamu inginkan:

kind: Endpoints
apiVersion: v1
metadata:
  name: my-service
subsets:
  - addresses:
      - ip: 1.2.3.4
    ports:
      - port: 9376

Cara mengakses suatu Service tanpa selector sama saja dengan mengakses suatu Service dengan selector. Trafik yang ada akan di-route ke Endpoints yang dispesifikasikan oleh pengguna (dalam contoh kali ini adalah 1.2.3.4:9376).

Sebuah ExternalName Service merupakan kasus spesial dari Service dimana Service tidak memiliki selector dan menggunakan penamaan DNS. Untuk informasi lebih lanjut silahkan baca bagian ExternalName.

IP Virtual dan proxy Service

Setiap node di klaster Kubernetes menjalankan kube-proxy. kube-proxy bertanggung jawab terhadap implementasi IP virtual bagi Services dengan tipe selain ExternalName.

Pada Kubernetes versi v1.0, Services adalah "layer 4" (TCP/UDP pada IP), proxy yang digunakan murni berada pada userspace. Pada Kubernetes v1.1, API Ingress ditambahkan untuk merepresentasikan "layer 7"(HTTP), proxy iptables juga ditambahkan dan menjadi mode operasi default sejak Kubernetes v1.2. Pada Kubernetes v1.8.0-beta.0, proxy ipvs juga ditambahkan.

Mode Proxy: userspace

Pada mode ini, kube-proxy mengamati master Kubernetes apabila terjadi penambahan atau penghapusan objek Service dan Endpoints. Untuk setiap Service, kube-proxy akan membuka sebuah port (yang dipilih secara acak) pada node lokal. Koneksi pada "proxy port" ini akan dihubungkan pada salah satu Pod backend dari Service (yang tercatat pada Endpoints). Pod backend yang akan digunakan akan diputuskan berdasarkan SessionAffinity pada Service. Langkah terakhir yang dilakukan oleh kube-proxy adalah melakukan instalasi rules iptables yang akan mengarahkan trafik yang ada pada clusterIP (IP virtual) dan port dari Service serta melakukan redirect trafik ke proxy yang memproksikan Pod backend. Secara default, mekanisme routing yang dipakai adalah round robin.

Ikhtisar diagram <em>Services</em> pada <em>proxy</em> <em>userspace</em>

Mode Proxy: iptables

Pada mode ini, kube-proxy mengamati master Kubernetes apabila terjadi penambahan atau penghapusan objek Service dan Endpoints. Untuk setiap Service, kube-proxy akan melakukan instalasi rules iptables yang akan mengarahkan trafik ke clusterIP (IP virtual) dan port dari Service. Untuk setiap objek Endpoints, kube-proxy akan melakukan instalasi rules iptables yang akan memilih satu buah Pod backend. Secara default, pemilihan backend ini dilakukan secara acak.

Tentu saja, iptables yang digunakan tidak boleh melakukan switching antara userspace dan kernelspace, mekanisme ini harus lebih kokoh dan lebih cepat dibandingkan dengan userspace proxy. Meskipun begitu, berbeda dengan mekanisme proxy userspace, proxy iptables tidak bisa secara langsung menjalankan mekanisme retry ke Pod lain apabila Pod yang sudah dipilih sebelumnya tidak memberikan respons, dengan kata lain hal ini akan sangat bergantung pada readiness probes.

Ikhtisar diagram <em>Services</em> pada <em>proxy</em> <code>iptables</code>

Mode Proxy: ipvs

FEATURE STATE: Kubernetes v1.9 [beta]

Pada mode ini, kube-proxy mengamati Services dan Endpoints, kemudian memanggil interface netlink untuk membuat rules ipvs yang sesuai serta melakukan sinkronisasi rules ipvs dengan Services dan Endpoints Kubernetes secara periodik, untuk memastikan status ipvs konsisten dengan apa yang diharapkan. Ketika sebuah Services diakses, trafik yang ada akan diarahkan ke salah satu Pod backend.

Sama halnya dengan iptables, ipvs juga berdasarkan pada fungsi hook netfilter, bedanya adalah ipvs menggunakan struktur data hash table dan bekerja di kernelspace. Dengan kata lain ipvs melakukan redirect trafik dengan lebih cepat dan dengan performa yang lebih baik ketika melakukan sinkronisasi rules proxy. Selain itu, ipvs juga menyediakan lebih banyak opsi algoritma load balancing:

  • rr: round-robin
  • lc: least connection
  • dh: destination hashing
  • sh: source hashing
  • sed: shortest expected delay
  • nq: never queue

Ikhtisar diagram <em>Services</em> pada <em>proxy</em> <em>ipvs</em>

Dari sekian model proxy yang ada, trafik inbound apa pun yang ada diterima oleh IP:Port pada Service akan dilanjutkan melalui proxy pada backend yang sesuai, dan klien tidak perlu mengetahui apa informasi mendetail soal Kubernetes, Service, atau Pod. afinitas session (session affinity) berbasis Client-IP dapat dipilih dengan cara menerapkan nilai "ClientIP" pada service.spec.sessionAffinity (nilai default untuk hal ini adalah "None"), kamu juga dapat mengatur nilai maximum session timeout yang ada dengan mengatur opsi service.spec.sessionAffinityConfig.clientIP.timeoutSeconds jika sebelumnya kamu sudah menerapkan nilai "ClusterIP" pada service.spec.sessionAffinity (nilai default untuk opsi ini adalah "10800").

Multi-Port Services

Banyak Services dengan kebutuhan untuk mengekspos lebih dari satu port. Untuk kebutuhan inilah, Kubernetes mendukung multiple port definitions pada objek Service. Ketika menggunakan multiple port, kamu harus memberikan nama pada setiap port yang didefinisikan, sehingga Endpoint yang dibentuk tidak ambigu. Contoh:

kind: Service
apiVersion: v1
metadata:
  name: my-service
spec:
  selector:
    app: MyApp
  ports:
  - name: http
    protocol: TCP
    port: 80
    targetPort: 9376
  - name: https
    protocol: TCP
    port: 443
    targetPort: 9377

Perhatikan bahwa penamaan port hanya boleh terdiri dari karakter alphanumeric lowercase dan -, serta harus dimulai dan diakhiri dengan karakter alphanumeric, misalnya saja 123-abc dan web merupakan penamaan yang valid, tapi 123_abc dan -web bukan merupakan penamaan yang valid.

Memilih sendiri alamat IP yang kamu inginkan

Kamu dapat memberikan spesifikasi alamat cluster IP yang kamu inginkan sebagai bagian dari request pembuatan objek Service. Untuk melakukan hal ini, kamu harus mengisi fields .spec.clusterIP field. Contoh penggunaannya adalah sebagai berikut, misalnya saja kamu sudah memiliki entry DNS yang ingin kamu gunakan kembali, atau sebuah sistem legacy yang sudah diatur pada alamat IP spesifik dan sulit untuk diubah. Alamat IP yang ingin digunakan pengguna haruslah merupakan alamat IP yang valid dan berada di dalam range CIDR service-cluster-ip-range yang dispesifikasikan di dalam penanda yang diberikan apiserver. Jika value yang diberikan tidak valid, apiserver akan mengembalikan response code HTTP 422 yang mengindikasikan value yang diberikan tidak valid.

Mengapa tidak menggunakan DNS round-robin?

Pertanyaan yang selalu muncul adalah kenapa kita menggunakan IP virtual dan bukan DNS round-robin standar? Terdapat beberapa alasan dibalik semua itu:

  • Terdapat sejarah panjang dimana library DNS tidak mengikuti TTL DNS dan melakukan caching hasil dari lookup yang dilakukan.
  • Banyak aplikasi yang melakukan lookup DNS hanya sekali dan kemudian melakukan cache hasil yang diperoleh.
  • Bahkan apabila aplikasi dan library melakukan resolusi ulang yang proper, load dari setiap klien yang melakukan resolusi ulang DNS akan sulit untuk di manage.

Kami berusaha untuk mengurangi ketertarikan pengguna untuk melakukan yang mungkin akan menyusahkan pengguna. Dengan demikian, apabila terdapat justifikasi yang cukup kuat, kami mungkin saja memberikan implementasi alternatif yang ada.

Discovering services

Kubernetes mendukung 2 buah mode primer untuk melakukan Service - variabel environment dan DNS.

Variabel Environment

Ketika sebuah Pod dijalankan pada node, kubelet menambahkan seperangkat variabel environment untuk setiap Service yang aktif. Environment yang didukung adalah Docker links compatible variabel (perhatikan makeLinkVariables) dan variabel {SVCNAME}_SERVICE_HOST dan {SVCNAME}_SERVICE_PORT, dinama nama Service akan diubah menjadi huruf kapital dan tanda minus akan diubah menjadi underscore.

Sebagai contoh, Service "redis-master" yang mengekspos port TCP 6379 serta alamat cluster IP 10.0.0.11 akan memiliki environment sebagai berikut:

REDIS_MASTER_SERVICE_HOST=10.0.0.11
REDIS_MASTER_SERVICE_PORT=6379
REDIS_MASTER_PORT=tcp://10.0.0.11:6379
REDIS_MASTER_PORT_6379_TCP=tcp://10.0.0.11:6379
REDIS_MASTER_PORT_6379_TCP_PROTO=tcp
REDIS_MASTER_PORT_6379_TCP_PORT=6379
REDIS_MASTER_PORT_6379_TCP_ADDR=10.0.0.11

Hal ini merupakan kebutuhan yang urutannya harus diperhatikan - Service apa pun yang akan diakses oleh sebuah Pod harus dibuat sebelum Pod tersebut dibuat, jika tidak variabel environment tidak akan diinisiasi. Meskipun begitu, DNS tidak memiliki keterbatasan ini.

DNS

Salah satu add-on opsional (meskipun sangat dianjurkan) adalah server DNS. Server DNS bertugas untuk mengamati apakah terdapat objek Service baru yang dibuat dan kemudian bertugas menyediakan DNS baru untuk Service tersebut. Jika DNS ini diaktifkan untuk seluruh klaster, maka semua Pod akan secara otomatis dapat melakukan resolusi DNS.

Sebagai contoh, apabila kamu memiliki sebuah Service dengan nama "my-service" pada Namespace "my-ns", maka record DNS "my-service.my-ns" akan dibuat. Pod yang berada di dalam Namespace "my-ns" dapat langsung melakukan lookup dengan hanya menggunakan "my-service". Sedangkan Pod yang berada di luar Namespace my-ns" harus menggunakan "my-service.my-ns". Hasil dari resolusi ini menrupakan cluster IP.

Kubernetes juga menyediakan record DNS SRV (service) untuk named ports. Jika Service "my-service.my-ns" memiliki port dengan nama "http" dengan protokol TCP, kamu dapat melakukan query DNS SRV untuk "_http._tcp.my-service.my-ns" untuk mengetahui nomor port yang digunakan oleh http.

Server DNS Kubernetes adalah satu-satunya cara untuk mengakses Service dengan tipe ExternalName. Informasi lebih lanjut tersedia di DNS Pods dan Services.

Service headless

Terkadang kamu tidak membutuhkan mekanisme load-balancing dan sebuah single IP Sevice. Dalam kasus ini, kamu dapat membuat "headless" Service dengan cara memberikan spesifikasi None pada cluster IP (.spec.clusterIP).

Opsi ini memungkinkan pengguna mengurangi ketergantungan terhadap sistem Kubernetes dengan cara memberikan kebebasan untuk mekanisme service discovery. Aplikasi akan tetap membutuhkan mekanisme self-registration dan adapter service discovery lain yang dapat digunakan berdasarkan API ini.

Untuk Service "headless" alokasi cluster IP tidak dilakukan dan kube-proxy tidak me-manage Service-Service, serta tidak terdapat mekanisme load balancing yang dilakukan. Bagaimana konfigurasi otomatis bagi DNS dilakukan bergantung pada apakah Service tersebut memiliki selector yang dispesifikasikan.

Dengan selector

Untuk Service "headless" dengan selector, kontroler Endpoints akan membuat suatu record Endpoints di API, serta melakukan modifikasi konfigurasi DNS untuk mengembalikan A records (alamat) yang merujuk secara langsung pada Pod backend.

Tanpa selector

Untuk Service "headless" tanpa selector, kontroler Endpoints tidak akan membuat record Enpoints. Meskipun demikian, sistem DNS tetap melakukan konfigurasi salah satu dari:

  • record CNAME untuk ExternalName-tipe services.
  • record untuk semua Endpoints yang memiliki nama Service yang sama, untuk tipe lainnya.

Mekanisme publish Service - jenis-jenis Service

Untuk beberapa bagian dari aplikasi yang kamu miliki (misalnya saja, frontend), bisa saja kamu memiliki kebutuhan untuk mengekspos Service yang kamu miliki ke alamat IP eksternal (di luar klaster Kubernetes).

ServiceTypes yang ada pada Kubernetes memungkinkan kamu untuk menentukan jenis Service apakah yang kamu butuhkan. Secara default, jenis Service yang diberikan adalah ClusterIP.

Value dan perilaku dari tipe Service dijelaskan sebagai berikut:

  • ClusterIP: Mengekspos Service ke range alamat IP di dalam klaster. Apabila kamu memilih value ini Service yang kamu miliki hanya dapat diakses secara internal. tipe ini adalah default value dari ServiceType.
  • NodePort: Mengekspos Service pada setiap IP node pada port statis atau port yang sama. Sebuah Service ClusterIP, yang mana Service NodePort akan di-route , dibuat secara otomatis. Kamu dapat mengakses Service dengan tipe ini, dari luar klaster melalui <NodeIP>:<NodePort>.
  • LoadBalancer: Mengekspos Service secara eksternal dengan menggunakan LoadBalancer yang disediakan oleh penyedia layanan cloud. Service dengan tipe NodePort dan ClusterIP, dimana trafik akan di-route, akan dibuat secara otomatis.
  • ExternalName: Melakukan pemetaan Service ke konten dari field externalName (misalnya: foo.bar.example.com), dengan cara mengembalikan catatan CNAME beserta value-nya. Tidak ada metode proxy apa pun yang diaktifkan. Mekanisme ini setidaknya membutuhkan kube-dns versi 1.7.

Type NodePort

Jika kamu menerapkan value NodePort pada field type, master Kubernetes akan mengalokasikan port dari range yang dispesifikasikan oleh penanda --service-node-port-range (secara default, 30000-32767) dan setiap Node akan memproksikan port tersebut (setiap Node akan memiliki nomor port yang sama) ke Service yang kamu miliki. Port tersebut akan dilaporkan pada field .spec.ports[*].nodePort di Service kamu.

Jika kamu ingin memberikan spesifikasi IP tertentu untuk melakukan poxy pada port. kamu dapat mengatur penanda --nodeport-addresses pada kube-proxy untuk range alamat IP tertentu (mekanisme ini didukung sejak v1.10). Sebuah daftar yang dipisahkan koma (misalnya, 10.0.0.0/8, 1.2.3.4/32) digunakan untuk mem-filter alamat IP lokal ke node ini. Misalnya saja kamu memulai kube-proxy dengan penanda --nodeport-addresses=127.0.0.0/8, maka kube-proxy hanya akan memilih interface loopback untuk Service dengan tipe NodePort. Penanda --nodeport-addresses memiliki nilai default kosong ([]), yang artinya akan memilih semua interface yang ada dan sesuai dengan perilaku NodePort default.

Jika kamu menginginkan nomor port yang berbeda, kamu dapat memberikan spesifikasi value dari field nodePort, dan sistem yang ada akan mengalokasikan port tersebut untuk kamu, jika port tersebut belum digunakan (perhatikan bahwa jika kamu menggunakan teknik ini, kamu perlu mempertimbangkan collision yang mungkin terjadi dan bagaimana cara mengatasi hal tersebut) atau transaksi API yang dilakukan akan gagal.

Hal ini memberikan kebebasan bagi pengembang untuk memilih load balancer yang akan digunakan, terutama apabila load balancer yang ingin digunakan belum didukung sepenuhnya oleh Kubernetes.

Perhatikan bahwa Service dapat diakses baik dengan menggunakan <NodeIP>:spec.ports[*].nodePort atau .spec.clusterIP:spec.ports[*].port. (Jika penanda --nodeport-addresses diterapkan, dapat di-filter dengan salah satu atau lebih NodeIP.)

Type LoadBalancer

Pada penyedia layanan cloud yang menyediakan pilihan load balancer eksternal, pengaturan field type ke LoadBalancer akan secara otomatis melakukan proses provision load balancer untuk Service yang kamu buat. Informasi mengenai load balancer yang dibuat akan ditampilkan pada field .status.loadBalancer pada Service kamu. Contohnya:

kind: Service
apiVersion: v1
metadata:
  name: my-service
spec:
  selector:
    app: MyApp
  ports:
  - protocol: TCP
    port: 80
    targetPort: 9376
  clusterIP: 10.0.171.239
  loadBalancerIP: 78.11.24.19
  type: LoadBalancer
status:
  loadBalancer:
    ingress:
    - ip: 146.148.47.155

Trafik dari load balancer eksternal akan diarahkan pada Pod backend, meskipun mekanisme bagaimana hal ini dilakukan bergantung pada penyedia layanan cloud. Beberapa penyedia layanan cloud mengizinkan konfigurasi untuk value loadBalancerIP. Dalam kasus tersebut, load balancer akan dibuat dengan loadbalancerIP yang dispesifikasikan. Jika value dari loadBalancerIP tidak dispesifikasikan. sebuah IP sementara akan diberikan pada loadBalancer. Jika loadBalancerIP dispesifikasikan, tetapi penyedia layanan cloud tidak mendukung hal ini, maka field yang ada akan diabaikan.

Catatan Khusus untuk Azure: Untuk spesifikasi loadBalancerIP publik yang didefinisikan oleh pengguna, sebuah alamat IP statis publik akan disediakan terlebih dahulu, dan alamat IP tersebut harus berada di resource group dari resource yang secara otomatis dibuat oleh klaster. Misalnya saja, MC_myResourceGroup_myAKSCluster_eastus. Berikan spesifikasi alamat IP sebagai loadBalancerIP. Pastikan kamu sudah melakukan update pada securityGroupName pada file konfigurasi penyedia layanan cloud. Untuk informasi lebih lanjut mengenai permission untuk CreatingLoadBalancerFailed kamu dapat membaca troubleshooting untuk Penggunaan alamat IP statis pada load balancer Azure Kubernetes Service (AKS) atau CreatingLoadBalancerFailed pada klaster AKS dengan advanced networking.

Load balancer internal

Di dalam environment, terkadang terdapat kebutuhan untuk melakukan route trafik antar Service yang berada di dalam satu VPC.

Di dalam environment split-horizon DNS kamu akan membutuhkan dua service yang mampu melakukan mekanisme route trafik eskternal maupun internal ke endpoints yang kamu miliki.

Hal ini dapat diraih dengan cara menambahkan anotasi berikut untuk service yang disediakan oleh penyedia layanan cloud.

Pilih salah satu tab.

[...]
metadata:
    name: my-service
    annotations:
        cloud.google.com/load-balancer-type: "Internal"
[...]

Gunakan cloud.google.com/load-balancer-type: "internal" untuk master dengan versi 1.7.0 to 1.7.3. Untuk informasi lebih lanjut, dilahkan baca dokumentasi.

[...]
metadata:
    name: my-service
    annotations:
        service.beta.kubernetes.io/aws-load-balancer-internal: 0.0.0.0/0
[...]

[...]
metadata:
    name: my-service
    annotations:
        service.beta.kubernetes.io/azure-load-balancer-internal: "true"
[...]

[...]
metadata:
    name: my-service
    annotations:
        service.beta.kubernetes.io/openstack-internal-load-balancer: "true"
[...]

[...]
metadata:
    name: my-service
    annotations:
        service.beta.kubernetes.io/cce-load-balancer-internal-vpc: "true"
[...]

Dukungan untuk SSL di AWS

Dukungan parsial untuk SSL bagi klaster yang dijalankan di AWS mulai diterapkan, mulai versi 1.3 terdapat 3 anotasi yang dapat ditambahkan pada Service dengan tipe LoadBalancer:

metadata:
  name: my-service
  annotations:
    service.beta.kubernetes.io/aws-load-balancer-ssl-cert: arn:aws:acm:us-east-1:123456789012:certificate/12345678-1234-1234-1234-123456789012

Anotasi pertama memberikan spesifikasi ARN dari sertifikat yang akan digunakan. Sertifikat yang digunakan bisa saja berasal dari third party yang diunggah ke IAM atau sertifikat yang dibuat secara langsung dengan menggunakan sertifikat manajer AWS.

metadata:
  name: my-service
  annotations:
    service.beta.kubernetes.io/aws-load-balancer-backend-protocol: (https|http|ssl|tcp)

Anotasi kedua memberikan spesifikasi bagi protokol yang digunakan oleh Pod untuk saling berkomunikasi. Untuk HTTPS dan SSL, ELB membutuhkan Pod untuk melakukan autentikasi terhadap dirinya sendiri melalui koneksi yang dienkripsi.

Protokol HTTP dan HTTPS akan memilih mekanisme proxy di tingkatan ke-7: ELB akan melakukan terminasi koneksi dengan pengguna, melakukan proses parsing headers, serta memasukkan value bagi header X-Forwarded-For dengan alamat IP pengguna (Pod hanya dapat melihat alamat IP dari ELB pada akhir koneksi yang diberikan) ketika melakukan forwarding suatu request.

Protokol TCP dan SSL akan memilih mekanisme proxy pada tingkatan 4: ELB akan melakukan forwarding trafik tanpa melakukan modifikasi headers.

Pada environment campuran dimana beberapa port diamankan sementara port lainnya dalam kondisi tidak dienkripsi, anotasi-anotasi berikut dapat digunakan:

    metadata:
      name: my-service
      annotations:
        service.beta.kubernetes.io/aws-load-balancer-backend-protocol: http
        service.beta.kubernetes.io/aws-load-balancer-ssl-ports: "443,8443"

Pada contoh di atas, jika Service memiliki 3 buah port, yaitu: 80, 443, dan 8443, maka 443 adan 8443 akan menggunakan sertifikat SSL, tetapi 80 hanya akan di-proxy menggunakan protokol HTTP.

Mulai versi 1.9, Service juga dapat menggunakan predefined policy untuk HTTPS atau listener SSL. Untuk melihat policy apa saja yang dapat digunakan, kamu dapat menjalankan perintah awscli:

aws elb describe-load-balancer-policies --query 'PolicyDescriptions[].PolicyName'

Policy ini kemudian dapat dispesifikasikan menggunakan anotasi "service.beta.kubernetes.io/aws-load-balancer-ssl-negotiation-policy", contohnya:

    metadata:
      name: my-service
      annotations:
        service.beta.kubernetes.io/aws-load-balancer-ssl-negotiation-policy: "ELBSecurityPolicy-TLS-1-2-2017-01"

Protokol PROXY pada AWS

Untuk mengaktifkan dukungan protokol PROXY untuk klaster yang dijalankan di AWS, kamu dapat menggunakan anotasi di bawah ini:

    metadata:
      name: my-service
      annotations:
        service.beta.kubernetes.io/aws-load-balancer-proxy-protocol: "*"

Sejak versi 1.3.0, penggunaan anotasi berlaku untuk semua port yang diproksi oleh ELB dan tidak dapat diatur sebaliknya.

Akses Log ELB pada AWS

Terdapat beberapa anotasi yang digunakan untuk melakukan manajemen akses log untuk ELB pada AWS.

Anotasi service.beta.kubernetes.io/aws-load-balancer-access-log-enabled mengatur akses log mana sajakah yang diaktifkan.

Anotasi service.beta.kubernetes.io/aws-load-balancer-access-log-emit-interval mengatur interval (dalam menit) publikasi akses log. Kamu dapat memberikan spesifikasi interval diantara range 5-60 menit.

Anotasi service.beta.kubernetes.io/aws-load-balancer-access-log-s3-bucket-name mengatur nama bucket Amazon S3 dimana akses log load balancer disimpan.

Anotasi service.beta.kubernetes.io/aws-load-balancer-access-log-s3-bucket-prefix memberikan spesifikasi hierarki logis yang kamu buat untuk bucket Amazon S3 yang kamu buat.

    metadata:
      name: my-service
      annotations:
        service.beta.kubernetes.io/aws-load-balancer-access-log-enabled: "true"
        # Specifies whether access logs are enabled for the load balancer
        service.beta.kubernetes.io/aws-load-balancer-access-log-emit-interval: "60"
        # The interval for publishing the access logs. You can specify an interval of either 5 or 60 (minutes).
        service.beta.kubernetes.io/aws-load-balancer-access-log-s3-bucket-name: "my-bucket"
        # The name of the Amazon S3 bucket where the access logs are stored
        service.beta.kubernetes.io/aws-load-balancer-access-log-s3-bucket-prefix: "my-bucket-prefix/prod"
        # The logical hierarchy you created for your Amazon S3 bucket, for example _my-bucket-prefix/prod_

Mekanisme Draining Koneksi pada AWS

Mekanisme draining untuk ELB klasik dapat dilakukan dengan menggunakan anotasi service.beta.kubernetes.io/aws-load-balancer-connection-draining-enabled serta mengatur value-nya menjadi "true". Anotasi service.beta.kubernetes.io/aws-load-balancer-connection-draining-timeout juga dapat digunakan untuk mengatur maximum time (dalam detik), untuk menjaga koneksi yang ada agar selalu terbuka sebelum melakukan deregistering instance.

    metadata:
      name: my-service
      annotations:
        service.beta.kubernetes.io/aws-load-balancer-connection-draining-enabled: "true"
        service.beta.kubernetes.io/aws-load-balancer-connection-draining-timeout: "60"

Anotasi ELB lainnya

Terdapat beberapa anotasi lain yang dapat digunakan untuk mengatur ELB klasik sebagaimana dijelaskan seperti di bawah ini:

    metadata:
      name: my-service
      annotations:
        service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout: "60"
        # The time, in seconds, that the connection is allowed to be idle (no data has been sent over the connection) before it is closed by the load balancer

        service.beta.kubernetes.io/aws-load-balancer-cross-zone-load-balancing-enabled: "true"
        # Specifies whether cross-zone load balancing is enabled for the load balancer

        service.beta.kubernetes.io/aws-load-balancer-additional-resource-tags: "environment=prod,owner=devops"
        # A comma-separated list of key-value pairs which will be recorded as
        # additional tags in the ELB.

        service.beta.kubernetes.io/aws-load-balancer-healthcheck-healthy-threshold: ""
        # The number of successive successful health checks required for a backend to
        # be considered healthy for traffic. Defaults to 2, must be between 2 and 10

        service.beta.kubernetes.io/aws-load-balancer-healthcheck-unhealthy-threshold: "3"
        # The number of unsuccessful health checks required for a backend to be
        # considered unhealthy for traffic. Defaults to 6, must be between 2 and 10

        service.beta.kubernetes.io/aws-load-balancer-healthcheck-interval: "20"
        # The approximate interval, in seconds, between health checks of an
        # individual instance. Defaults to 10, must be between 5 and 300
        service.beta.kubernetes.io/aws-load-balancer-healthcheck-timeout: "5"
        # The amount of time, in seconds, during which no response means a failed
        # health check. This value must be less than the service.beta.kubernetes.io/aws-load-balancer-healthcheck-interval
        # value. Defaults to 5, must be between 2 and 60

        service.beta.kubernetes.io/aws-load-balancer-extra-security-groups: "sg-53fae93f,sg-42efd82e"
        # A list of additional security groups to be added to ELB

Dukungan Network Load Balancer (NLB) pada AWS [alpha]

Sejak versi 1.9.0, Kubernetes mendukung Network Load Balancer (NLB). Untuk menggunakan NLB pada AWS, gunakan anotasi service.beta.kubernetes.io/aws-load-balancer-type dan atur value-nya dengan nlb.

    metadata:
      name: my-service
      annotations:
        service.beta.kubernetes.io/aws-load-balancer-type: "nlb"

Tidak seperti ELB klasik, NLB, melakukan forwarding IP klien melalui node. Jika field .spec.externalTrafficPolicy diatur value-nya menjadi Cluster, maka alamat IP klien tidak akan diteruskan pada Pod.

Dengan mengatur value dari field .spec.externalTrafficPolicy ke Local, alamat IP klien akan diteruskan ke Pod, tapi hal ini bisa menyebabkan distribusi trafik yang tidak merata. Node yang tidak memiliki Pod untuk Service dengan tipe LoadBalancer akan menyebabkan kegagalan health check NLB Target pada tahapan auto-assigned .spec.healthCheckNodePort dan tidak akan menerima trafik apa pun.

Untuk menghasilkan distribusi trafik yang merata, kamu dapat menggunakan DaemonSet atau melakukan spesifikasi pod anti-affinity agar Pod tidak di-assign ke node yang sama.

NLB juga dapat digunakan dengan anotasi internal load balancer.

Agar trafik klien berhasil mencapai instances dibelakang ELB, security group dari node akan diberikan rules IP sebagai berikut:

RuleProtokolPortIpRange(s)Deskripsi IpRange
Health CheckTCPNodePort(s) (.spec.healthCheckNodePort for .spec.externalTrafficPolicy = Local)VPC CIDRkubernetes.io/rule/nlb/health=<loadBalancerName>
Client TrafficTCPNodePort(s).spec.loadBalancerSourceRanges (defaults to 0.0.0.0/0)kubernetes.io/rule/nlb/client=<loadBalancerName>
MTU DiscoveryICMP3,4.spec.loadBalancerSourceRanges (defaults to 0.0.0.0/0)kubernetes.io/rule/nlb/mtu=<loadBalancerName>

Perhatikan bahwa jika .spec.loadBalancerSourceRanges tidak dispesifikasikan, Kubernetes akan mengizinkan trafik dari 0.0.0.0/0 ke Node Security Group. Jika node memiliki akses publik, maka kamu harus memperhatikan tersebut karena trafik yang tidak berasal dari NLB juga dapat mengakses semua instance di security group tersebut.

Untuk membatasi klien IP mana yang dapat mengakses NLB, kamu harus memberikan spesifikasi loadBalancerSourceRanges.

spec:
  loadBalancerSourceRanges:
  - "143.231.0.0/16"

Tipe ExternalName

Service dengan tipe ExternalName melakukan pemetaan antara Service dan DNS, dan bukan ke selector seperti my-service atau cassandra. Kamu memberikan spesifikasi spec.externalName pada Service tersebut.

Definisi Service ini, sebagai contoh, melaukan pemetaan Service my-service pada namespace prod ke DNS my.database.example.com:

kind: Service
apiVersion: v1
metadata:
  name: my-service
  namespace: prod
spec:
  type: ExternalName
  externalName: my.database.example.com

Ketika melakukan pencarian host my-service.prod.svc.cluster.local, servis DNS klaster akan mengembalikan record CNAME dengan value my.database.example.com. Mekanisme akses pada my-service bekerja dengan cara yang sama dengan Service pada umumnya, perbedaan yang krusial untuk hal ini adalah mekanisme redirection terjadi pada tingkatan DNS dan bukan melalui proxy forward. Apabila kamu berniat memindahkan basis data yang kamu pakai ke dalam klaster, kamu hanya perlu mengganti instans basis data kamu dan menjalankannya di dalam Pod, menambahkan selector atau endpoint yang sesuai, serta mengupah type dari Service yang kamu gunakan.

IP Eksternal

Jika terdapat sebuah alamat IP eksternal yang melakukan mekanisme route ke satu atau lebih node yang ada di klaster, Service Kubernetes dapat diekspos dengan menggunakan externalIP. Trafik yang diarahkan ke klaster dengan IP eksternal (sebagai destinasi IP), pada port Service akan di-route ke salah satu endpoint Service. Value dari externalIP tidak diatur oleh Kubernetes dan merupakan tanggung jawab dari administrator klaster.

Pada ServiceSpec, kamu dapat memberikan spesifikasi externalIP dan ServiceTypes. Pada contoh di bawah ini. "my-service" dapat diakses oleh klien pada "198.51.100.32:80" (externalIP:port).

kind: Service
apiVersion: v1
metadata:
  name: my-service
spec:
  selector:
    app: MyApp
  ports:
  - name: http
    protocol: TCP
    port: 80
    targetPort: 9376
  externalIPs:
  - 80.11.12.10

Kekurangan

Penggunaan proxy userspace untuk VIP dapat digunakan untuk skala kecil hingga menengah, meski begitu hal ini tidak scalable untuk klaster yang sangat besar dan memiliki ribuan Service. Perhatikan Desain proposal orisinil untuk portal untuk informasi lebih lanjut.

Penggunaan proxy userspace menghilangkan source-IP dari packet yang mengakses sebuah Service. Hal ini membuat mekanisme firewall menjadi sulit untuk diterapkan. Proxy iptables tidak menghilangkan source IP yang berasal dari dalam klaster, meski begitu, hal ini masih berimbas pada klien yang berasal dari Service dengan tipe load-balancer atau node-port.

Field tipe didesain sebagai fungsionalitas yang berantai - setiap tingkatan menambahkan tambahan pada tingkatansebelumnya. Hal ini tidak selalu berlaku bagi semua penyedia layanan cloud (misalnya saja Google Compute Engine tidak perlu melakukan alokasi NodePort untuk membuat LoadBalancer bekerja sebagaimana mestinya, hal ini berbeda dengan AWS yang memerlukan hal ini, setidaknya untuk API yang mereka miliki saat ini).

Pengerjaan lebih lanjut

Di masa mendatang, kami berencana untuk membuat policy proxy menjadi lebih bervariasi dan bukan hanya round robin, misalnya saja master-elected atau sharded. Kami juga berharap bahwa beberapa Service bisa saja memiliki load balancer yang sebenarnya, suatu kasus dimana VIP akan secara langsung mengantarkan paket.

Kami ingin meningkatkan dukungan lebih lanjut untuk Service dengan tingkatan Service L7(HTTP).

Kami ingin memiliki mode ingress yang lebih fleksibel untuk Service yang mencakup mode ClusterIP, NodePort, dan LoadBalancer dan banyak lagi.

Detail mendalam mengenai IP virtual

Informasi sebelumnya sudah cukup bagi sebagian orang yang hanya ingin menggunakan Service. Meskipun begitu, terdapat banyak hal yang sebenarnya terjadi dan akan sangat bermanfaat untuk dipelajari lebih lanjut.

Menghindari collison

Salah satu filosofi Kubernetes adalah pengguna tidak mungkin menghadapi situasi dimana apa yang mereka mengalami kegagalan tanpa adanya alasan yang jelas. Dalam kasus ini, kita akan coba memahami lebih lanjut mengenai network port - pengguna tidak seharusnya memilih nomor port jika hal itu memungkinkan terjadinya collision dengan pengguna lainnya. Hal ini merupakan mekanisme isolasi kegagalan.

Agar pengguna dapat menentukan nomor port bagi Service mereka, kita harus memastikan bahwa tidak ada dua Service yang mengalami collision. Kita melakukan hal tersebut dengan cara melakukan alokasi alamat IP pada setiap Service.

Untuk memastikan setiap Service memiliki alamat IP yang unik, sebuah allocator internal akan secara atomik melakukan pemetaan alokasi global di dalam etcd ketika membuat sebuah Service baru. Pemetaan objek harus tersedia pada registry Service dimana Service akan diberikan sebuah IP, jika tidak, proses pembuatan Service akan gagal dan sebuah pesan akan memberikan informasi bahwa alamat IP tidak dapat dialokasikan. Sebuah backgroud controller bertanggung jawab terhadap mekanisme pemetaan tersebut (migrasi dari versi Kubernetes yang digunakan dalam memory locking) sekaligus melakukan pengecekan terhadap assignment yang tidak valid yang terjadi akibat intervensi administrator dan melakukan penghapusan daftar IP yang dialokasikan tapi tidak digunakan oleh Service mana pun.

IP dan VIP

Tidak seperti alamat IP Pod, yang akan di route ke destinasi yang "pasti", IP Service tidak mengarahkan request hanya pada satu host. Sebagai gantinya, kita mneggunakan iptables (logika pemrosesan paket pada Linux) untuk melakukan definisi alamat IP virtual yang secara transparan akan diarahkan sesuai kebutuhan. Ketika klien dihubungkan pada VIP, trafik yang ada akan secara otomatis dialihkan pada endpoint yang sesuai. Variabel environment dan DNS untuk Service terdiri dalam bentuk VIP dan port.

Kami mendukung tiga jenis mode proxy - userspace, iptables, dan ipvs yang memiliki perbedaan cara kerja satu sama lainnya.

Userspace

Sebagai contoh, anggaplah kita memiliki aplikasi image processing seperti yang sudah disebutkan di atas. Ketika Service backend dibuat, master Kubernetes akan mengalokasikan sebuah alamat IP virtual, misalnya 10.0.0.1. Dengan asumsi port dari Service tersebut adalah 1234, maka Service tersebut akan diamati oleh semua instance kube-proxy yang ada di klaster. Ketika sebuah proxy mendapati sebuah Service baru, proxy tersebut akan membuka sebuah port acak, menyediakan iptables yang mengarahkan VIP pada port yang baru saja dibuat, dan mulai koneksi pada port tersebut.

Ketika sebuah klien terhubung ke VIP dan terdapat rules iptables yang diterapkan, paket akan diarahkan ke port dari proxy Service itu sendiri. Proxy Service akan memilih sebuah backend, dan mulai melakukan mekanisme proxy trafik dari klien ke backend.

Dengan demikian, pemilik Service dapat memilih port mana pun yang dia inginkan tanpa adanya kemungkinan terjadinya collision. Klien dapat dengan mudah mengakses IP dan port, tanpa harus mengetahui Pod mana yang sebenarnya diakses.

Iptables

Kembali, bayangkan apabila kita memiliki aplikasi image processing seperti yang sudah disebutkan di atas. Ketika Service backend dibuat, master Kubernetes akan mengalokasikan sebuah alamat IP virtual, misalnya 10.0.0.1. Dengan asumsi port dari Service tersebut adalah 1234, maka Service tersebut akan diamati oleh semua instance kube-proxy yang ada di klaster. Ketika sebuah proxy mendapati sebuah Service baru, proxy tersebut akan melakukan instalasi serangkaian rules iptables yang akan melakukan redirect VIP ke rules tiap Service. Rules untuk tiap Service ini terkait dengan rules tiap Endpoints yang mengarahkan (destinasi NAT) ke backend.

Ketika sebuah klien terhubung ke VIP dan terdapat _rules _iptables yang diterapkan. Sebuah backend akan dipilih (hal ini dapat dilakukan berdasarkan session affinity maupun secara acak) dan paket-paket yang ada akan diarahkan ke backend. Tidak seperti mekanisme yang terjadi di userspace, paket-paket yang ada tidak pernah disalin ke userspace, kube-proxy tidak harus aktif untuk menjamin kerja VIP, serta IP klien juga tidak perlu diubah.

Tahapan yang dijalankan sama dengan tahapan yang dijalankan ketika trafik masuk melalui sebuah node-port atau load-balancer, meskipun pada dua kasus di atas klien IP tidak akan mengalami perubahan.

Ipvs

Operasi iptables berlangsung secara lambat pada klaster dengan skala besar (lebih dari 10.000 Service). IPVS didesain untuk mekanisme load balance dan berbasis pada hash tables yang berada di dalam kernel. Dengan demikian kita dapat mendapatkan performa yang konsisten pada jumlah Service yang cukup besar dengan menggunakan kube-proxy berbasis ipvs. Sementara itu, kube-proxy berbasis ipvs memiliki algoritma load balance yang lebih bervariasi (misalnya saja least conns, locality, weighted, persistence).

Objek API

Service merupakan resource top-level pada API Kubernetes. Penjelasan lebih lanjut mengenai objek API dapat ditemukan pada: objek API Service.

Protokol yang didukung

TCP

FEATURE STATE: Kubernetes v1.0 [stable]

Kamu dapat menggunakan TCP untuk Service dengan type apa pun, dan protokol ini merupakan protokol default yang digunakan.

UDP

FEATURE STATE: Kubernetes v1.0 [stable]

Kamu dapat menggunakan UDP untuk sebagian besar Service. Untuk Service dengan type=LoadBalancer, dukungan terhadap UDP bergantung pada penyedia layanan cloud yang kamu gunakan.

HTTP

FEATURE STATE: Kubernetes v1.1 [stable]

Apabila penyedia layanan cloud yang kamu gunakan mendukung, kamu dapat menggunakan Service dengan type LoadBalancer untuk melakukan mekanisme reverse proxy bagi HTTP/HTTPS, dan melakukan forwarding ke Endpoints dari _Service.

Protokol PROXY

FEATURE STATE: Kubernetes v1.1 [stable]

Apabila penyedia layanan cloud yang kamu gunakan mendukung, (misalnya saja, AWS), Service dengan type LoadBalancer untuk melakukan konfigurasi load balancer di luar Kubernetes sendiri, serta akan melakukan forwarding koneksi yang memiliki prefiks protokol PROXY.

Load balancer akan melakukan serangkaian inisiasi octet yang memberikan deskripsi koneksi yang datang, dengan bentuk yang menyerupai:

PROXY TCP4 192.0.2.202 10.0.42.7 12345 7\r\n

yang kemudian diikuti data dari klien.

SCTP

FEATURE STATE: Kubernetes v1.12 [alpha]

Kubernetes memberikan dukungan bagi SCTP sebagai value dari definition yang ada pada Service, Endpoints, NetworkPolicy dan Pod sebagai fitur alpha. Untuk mengaktifkan fitur ini, administrator klaster harus mengaktifkan feature gate SCTPSupport pada apiserver, contohnya “--feature-gates=SCTPSupport=true,...”. Ketika fature gate ini diaktifkan, pengguna dapat memberikan value SCTP pada field protocol Service, Endpoints, NetworkPolicy dan Pod. Kubernetes kemudian akan melakukan pengaturan agar jaringan yang digunakan agar jaringan tersebut menggunakan SCTP, seperti halnya Kubernetes mengatur jaringan agar menggunakan TCP.

Perhatian

Dukungan untuk asoasiasi multihomed SCTP

Dukungan untuk asosiasi multihomed SCTP membutuhkan plugin CNI yang dapat memberikan pengalokasian multiple interface serta alamat IP pada sebuah Pod.

NAT untuk asosiasi multihomed SCTP membutuhkan logika khusus pada modul kernel terkait.

Service dengan type=LoadBalancer

Sebuah Service dengan type LoadBalancer dan protokol SCTP dapat dibuat hanya jika implementasi load balancer penyedia layanan cloud menyediakan dukungan bagi protokol SCTP. Apabila hal ini tidak terpenuhi, maka request pembuatan Servixe ini akan ditolak. Load balancer yang disediakan oleh penyedia layanan cloud yang ada saat ini (Azure, AWS, CloudStack, GCE, OpenStack) tidak mendukung SCTP.

Windows

SCTP tidak didukung pada node berbasis Windows.

Kube-proxy userspace

Kube-proxy tidak mendukung manajemen asosiasi SCTP ketika hal ini dilakukan pada mode userspace

Selanjutnya

Baca Bagaimana cara menghubungkan Front End ke Back End menggunakan sebuah Service.

2 - Topologi Service (Service Topology)

FEATURE STATE: Kubernetes v1.17 [alpha]

Topologi Service memungkinkan Service untuk merutekan lalu lintas jaringan berdasarkan topologi Node dalam klaster. Misalnya, suatu layanan dapat menentukan lalu lintas jaringan yang lebih diutamakan untuk dirutekan ke beberapa endpoint yang berada pada Node yang sama dengan klien, atau pada availability zone yang sama.

Pengantar

Secara bawaan lalu lintas jaringan yang dikirim ke ClusterIP atau NodePort dari Service dapat dialihkan ke alamat backend untuk Service tersebut. Sejak Kubernetes 1.7 dimungkinkan untuk merutekan lalu lintas jaringan "eksternal" ke Pod yang berjalan di Node yang menerima lalu lintas jaringan, tetapi fitur ini tidak didukung untuk ClusterIP dari Service, dan topologi yang lebih kompleks — seperti rute zonasi — belum memungkinkan. Fitur topologi Service mengatasi kekurangan ini dengan mengizinkan pembuat layanan untuk mendefinisikan kebijakan dalam merutekan lalu lintas jaringan berdasarkan label Node untuk Node-Node asal dan tujuan.

Dengan menggunakan label Node yang sesuai antara asal dan tujuan, operator dapat menunjuk kelompok Node mana yang "lebih dekat" dan mana yang "lebih jauh" antara satu sama lain, dengan menggunakan metrik apa pun yang masuk akal untuk memenuhi persyaratan dari operator itu. Untuk sebagian besar operator di publik cloud, misalnya, ada preferensi untuk menjaga layanan lalu lintas jaringan dalam zona yang sama, karena lalu lintas jaringan antar zona memiliki biaya yang dibebankan, sementara lalu lintas jaringan dalam zona yang sama tidak ada biaya. Kebutuhan umum lainnya termasuk kemampuan untuk merutekan lalu lintas jaringan ke Pod lokal yang dikelola oleh sebuah DaemonSet, atau menjaga lalu lintas jaringan ke Node yang terhubung ke top-of-rack switch yang sama untuk mendapatkan latensi yang terendah.

Menggunakan Topologi Service

Jika klaster kamu mengaktifkan topologi Service kamu dapat mengontrol rute lalu lintas jaringan Service dengan mengatur bagian topologyKeys pada spesifikasi Service. Bagian ini adalah daftar urutan label-label Node yang akan digunakan untuk mengurutkan endpoint saat mengakses Service ini. Lalu lintas jaringan akan diarahkan ke Node yang nilai label pertamanya cocok dengan nilai dari Node asal untuk label yang sama. Jika tidak ada backend untuk Service pada Node yang sesuai, maka label kedua akan dipertimbangkan, dan seterusnya, sampai tidak ada label yang tersisa.

Jika tidak ditemukan kecocokan, lalu lintas jaringan akan ditolak, sama seperti jika tidak ada sama sekali backend untuk Service tersebut. Artinya, endpoint dipilih berdasarkan kunci topologi yang pertama yang tersedia pada backend. Jika dalam bagian ini ditentukan dan semua entri tidak memiliki backend yang sesuai dengan topologi klien, maka Service tidak memiliki backend untuk klien dan koneksi harus digagalkan. Nilai khusus "*" dapat digunakan untuk mengartikan "topologi apa saja". Nilai catch-all ini, jika digunakan, maka hanya sebagai nilai terakhir dalam daftar.

Jika topologyKeys tidak ditentukan atau kosong, tidak ada batasan topologi yang akan diterapkan.

Seandainya sebuah klaster dengan Node yang dilabeli dengan nama host , nama zona, dan nama wilayah mereka, maka kamu dapat mengatur nilai topologyKeys dari sebuah Service untuk mengarahkan lalu lintas jaringan seperti berikut ini.

  • Hanya ke endpoint dalam Node yang sama, gagal jika tidak ada endpoint pada Node: ["kubernetes.io/hostname"].
  • Lebih memilih ke endpoint dalam Node yang sama, jika tidak ditemukan maka ke endpoint pada zona yang sama, diikuti oleh wilayah yang sama, dan selain itu akan gagal: ["kubernetes.io/hostname ", "topology.kubernetes.io/zone", "topology.kubernetes.io/region"]. Ini mungkin berguna, misalnya, dalam kasus di mana lokalitas data sangatlah penting.
  • Lebih memilih ke endpoint dalam zona yang sama, tetapi memilih endpoint mana saja yang tersedia apabila tidak ada yang tersedia dalam zona ini: ["topology.kubernetes.io/zone ","*"].

Batasan

  • Topologi Service tidak kompatibel dengan externalTrafficPolicy=Local, dan karena itu Service tidak dapat menggunakan kedua fitur ini sekaligus. Dimungkinkan untuk menggunakan kedua fitur pada klaster yang sama untuk Service yang berbeda, bukan untuk Service yang sama.
  • Untuk saat ini kunci topologi yang valid hanya terbatas pada kubernetes.io/hostname, topology.kubernetes.io/zone, dan topology.kubernetes.io/region, tetapi akan digeneralisasikan ke label Node yang lain di masa depan.
  • Kunci topologi harus merupakan kunci label yang valid dan paling banyak hanya 16 kunci yang dapat ditentukan.
  • Nilai catch-all, "*", harus menjadi nilai terakhir pada kunci topologi, jika nilai itu digunakan.

Contoh

Berikut ini adalah contoh umum penggunaan fitur topologi Service.

Hanya pada endpoint pada Node lokal

Service yang hanya merutekan ke endpoint pada Node lokal. Jika tidak ada endpoint pada Node, lalu lintas jaringan akan dihentikan:

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  selector:
    app: my-app
  ports:
    - protocol: TCP
      port: 80
      targetPort: 9376
  topologyKeys:
    - "kubernetes.io/hostname"

Lebih memilih endpoint pada Node lokal

Service yang lebih memilih endpoint pada Node lokal, namun akan memilih ke endpoint dalam klaster jika endpoint pada Node lokal tidak ada:

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  selector:
    app: my-app
  ports:
    - protocol: TCP
      port: 80
      targetPort: 9376
  topologyKeys:
    - "kubernetes.io/hostname"
    - "*"

Hanya untuk endpoint pada zona atau wilayah yang sama

Service yang lebih memilih endpoint dalam zona yang sama daripada wilayah yang sama. Jika tidak ada endpoint pada
keduanya, maka lalu lintas jaringan akan dihentikan.

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  selector:
    app: my-app
  ports:
    - protocol: TCP
      port: 80
      targetPort: 9376
  topologyKeys:
    - "topology.kubernetes.io/zone"
    - "topology.kubernetes.io/region"

Lebih memilih endpoint pada Node lokal, zona yang sama, dan kemudian wilayah yang sama

Service yang lebih memilih endpoint pada Node lokal, zona yang sama, dan kemudian baru wilayah yang sama, namun jika tetap tidak ditemukan maka akan memilih endpoint diseluruh klaster.

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  selector:
    app: my-app
  ports:
    - protocol: TCP
      port: 80
      targetPort: 9376
  topologyKeys:
    - "kubernetes.io/hostname"
    - "topology.kubernetes.io/zone"
    - "topology.kubernetes.io/region"
    - "*"

Selanjutnya

3 - EndpointSlice

FEATURE STATE: Kubernetes v1.17 [beta]

EndpointSlice menyediakan sebuah cara yang mudah untuk melacak endpoint jaringan dalam sebuah klaster Kubernetes. EndpointSlice memberikan alternatif yang lebih scalable dan lebih dapat diperluas dibandingkan dengan Endpoints.

Motivasi

Endpoints API telah menyediakan sebuah cara yang mudah dan sederhana untuk melacak endpoint jaringan pada Kubernetes. Sayangnya, seiring dengan besarnya klaster Kubernetes dan Service, batasan-batasan yang dimiliki API tersebut semakin terlihat. Terutama, hal tersebut termasuk kendala-kendala mengenai proses scaling endpoint jaringan dalam jumlah yang besar.

Karena semua endpoint jaringan untuk sebuah Service disimpan dalam satu sumber daya Endpoints, sumber daya tersebut dapat menjadi cukup besar. Hal itu dapat mempengaruhi kinerja dari komponen-komponen Kubernetes (terutama master control plane) dan menyebabkan lalu lintas jaringan dan pemrosesan yang cukup besar ketika Endpoints berubah. EndpointSlice membantu kamu menghindari masalah-masalah tersebut dan juga menyediakan platform yang dapat diperluas untuk fitur-fitur tambahan seperti topological routing.

Sumber daya EndpointSlice

Pada Kubernetes, sebuah EndpointSlice memiliki referensi-referensi terhadap sekumpulan endpoint jaringan. Controller EndpointSlice secara otomatis membuat EndpointSlice untuk sebuah Service Kubernetes ketika sebuah selektor dituliskan. EndpointSlice tersebut akan memiliki referensi-referensi menuju Pod manapun yang cocok dengan selektor pada Service tersebut. EndpointSlice mengelompokkan endpoint jaringan berdasarkan kombinasi Service dan Port yang unik. Nama dari sebuah objek EndpointSlice haruslah berupa nama subdomain DNS yang sah.

Sebagai contoh, berikut merupakan sampel sumber daya EndpointSlice untuk sebuah Service Kubernetes yang bernama example.

apiVersion: discovery.k8s.io/v1beta1
kind: EndpointSlice
metadata:
  name: example-abc
  labels:
    kubernetes.io/service-name: example
addressType: IPv4
ports:
  - name: http
    protocol: TCP
    port: 80
endpoints:
  - addresses:
      - "10.1.2.3"
    conditions:
      ready: true
    hostname: pod-1
    topology:
      kubernetes.io/hostname: node-1
      topology.kubernetes.io/zone: us-west2-a

Secara bawaan, setiap EndpointSlice yang dikelola oleh controller EndpointSlice tidak akan memiliki lebih dari 100 endpoint. Di bawah skala tersebut, EndpointSlice akan memetakan 1:1 dengan Endpoints dan Service dan akan memiliki kinerja yang sama.

EndpointSlice dapat bertindak sebagai sumber kebenaran untuk kube-proxy sebagai acuan mengenai bagaimana cara untuk merutekan lalu lintas jaringan internal. Ketika diaktifkan, EndpointSlice semestinya memberikan peningkatan kinerja untuk Service yang memiliki Endpoints dalam jumlah besar.

Tipe-tipe Alamat

EndpointSlice mendukung tiga tipe alamat:

  • IPv4
  • IPv6
  • FQDN (Fully Qualified Domain Name)

Topologi

Setiap endpoint pada EndpointSlice dapat memiliki informasi topologi yang relevan. Hal ini digunakan untuk mengindikasikan di mana endpoint berada, berisi informasi mengenai Node yang bersangkutan, zona, dan wilayah. Ketika nilai-nilai tersebut tersedia, label-label Topology berikut akan ditambahkan oleh controller EndpointSlice:

  • kubernetes.io/hostname - Nama dari Node tempat endpoint berada.
  • topology.kubernetes.io/zone - Zona tempat endpoint berada.
  • topology.kubernetes.io/region - Region tempat endpoint berada.

Nilai-nilai dari label-label berikut berasal dari sumber daya yang diasosiasikan dengan tiap endpoint pada sebuah slice. Label hostname merepresentasikan nilai dari kolom NodeName pada Pod yang bersangkutan. Label zona dan wilayah merepresentasikan nilai dari label-label dengan nama yang sama pada Node yang bersangkutan.

Pengelolaan

Secara bawaan, EndpointSlice dibuat dan dikelola oleh controller EndpointSlice. Ada berbagai macam kasus lain untuk EndpointSlice, seperti implementasi service mesh, yang memungkinkan adanya entitas atau controller lain yang dapat mengelola beberapa EndpointSlice sekaligus. Untuk memastikan beberapa entitas dapat mengelola EndpointSlice tanpa mengganggu satu sama lain, sebuah label endpointslice.kubernetes.io/managed-by digunakan untuk mengindikasikan entitas yang mengelola sebuah EndpointSlice. Controller EndpointSlice akan menambahkan endpointslice-controller.k8s.io sebagai nilai dari label tersebut pada seluruh EndpointSlice yang dikelolanya. Entitas lain yang mengelola EndpointSlice juga diharuskan untuk menambahkan nilai yang unik untuk label tersebut.

Kepemilikan

Pada kebanyakan kasus, EndpointSlice akan dimiliki oleh Service yang diikutinya. Hal ini diindikasikan dengan referensi pemilik pada tiap EndpointSlice dan juga label kubernetes.io/service-name yang memudahkan pencarian seluruh EndpointSlice yang dimiliki oleh sebuah Service.

Controller EndpointSlice

Controller EndpointSlice mengamati Service dan Pod untuk memastikan EndpointSlice yang bersangkutan berada dalam kondisi terkini. Controller EndpointSlice akan mengelola EndpointSlice untuk setiap Service yang memiliki selektor. Ini akan merepresentasikan IP dari Pod yang cocok dengan selektor dari Service tersebut.

Ukuran EndpointSlice

Secara bawaan, jumlah endpoint yang dapat dimiliki tiap EndpointSlice dibatasi sebanyak 100 endpoint. Kamu dapat mengaturnya melalui opsi --max-endpoints-per-slice kube-controller-manager sampai dengan jumlah maksimum sebanyak 1000 endpoint.

Distribusi EndpointSlice

Tiap EndpointSlice memiliki sekumpulan port yang berlaku untuk seluruh endpoint dalam sebuah sumber daya. Ketika nama port digunakan untuk sebuah Service, Pod mungkin mendapatkan nomor target port yang berbeda-beda untuk nama port yang sama, sehingga membutuhkan EndpointSlice yang berbeda. Hal ini mirip dengan logika mengenai bagaimana subset dikelompokkan dengan Endpoints.

Controller EndpointSlice akan mencoba untuk mengisi EndpointSlice sebanyak mungkin, tetapi tidak secara aktif melakukan rebalance terhadap EndpointSlice tersebut. Logika dari controller cukup sederhana:

  1. Melakukan iterasi terhadap EndpointSlice yang sudah ada, menghapus endpoint yang sudah tidak lagi dibutuhkan dan memperbarui endpoint yang sesuai yang mungkin telah berubah.
  2. Melakukan iterasi terhadap EndpointSlice yang sudah dimodifikasi pada langkah pertama dan mengisinya dengan endpoint baru yang dibutuhkan.
  3. Jika masih tersisa endpoint baru untuk ditambahkan, mencoba untuk menambahkannya pada slice yang tidak berubah sebelumnya dan/atau membuat slice yang baru.

Terlebih penting, langkah ketiga memprioritaskan untuk membatasi pembaruan EndpointSlice terhadap distribusi dari EndpointSlice yang benar-benar penuh. Sebagai contoh, jika ada 10 endpoint baru untuk ditambahkan dan ada 2 EndpointSlice yang masing-masing memiliki ruang untuk 5 endpoint baru, pendekatan ini akan membuat sebuah EndpointSlice baru daripada mengisi 2 EndpointSlice yang sudah ada. Dengan kata lain, pembuatan sebuah EndpointSlice lebih diutamakan daripada pembaruan beberapa EndpointSlice.

Dengan kube-proxy yang berjalan pada tiap Node dan mengamati EndpointSlice, setiap perubahan pada sebuah EndpointSlice menjadi sangat mahal karena hal tersebut akan dikirimkan ke setiap Node dalam klaster. Pendekatan ini ditujukan untuk membatasi jumlah perubahan yang perlu dikirimkan ke setiap Node, meskipun hal tersebut berdampak pada banyaknya EndpointSlice yang tidak penuh.

Pada praktiknya, distribusi yang kurang ideal seperti ini akan jarang ditemukan. Kebanyakan perubahan yang diproses oleh controller EndpointSlice akan cukup kecil untuk dapat masuk pada EndpointSlice yang sudah ada, dan jika tidak, cepat atau lambat sebuah EndpointSlice baru akan segera dibutuhkan. Pembaruan bertahap (rolling update) dari Deployment juga menyediakan sebuah proses pengemasan ulang EndpointSlice yang natural seiring dengan digantikannya seluruh Pod dan endpoint yang bersangkutan.

Selanjutnya

4 - DNS untuk Service dan Pod

Laman ini menyediakan ikhtisar dari dukungan DNS oleh Kubernetes.

Pendahuluan

Kubernetes DNS melakukan scheduling DNS Pod dan Service yang ada pada klaster, serta melakukan konfigurasi kubelet untuk memberikan informasi bagi setiap Container untuk menggunakan DNS Service IP untuk melakukan resolusi DNS.

Apa Sajakah yang Mendapatkan Nama DNS?

Setiap Service yang didefinisikan di dalam klaster (termasuk server DNS itu sendiri) memiliki nama DNS. Secara default, sebuah list pencarian DNS pada Pod klien akan mencantumkan namespace Pod itu sendiri serta domain default klaster. Hal ini dapat diilustrasikan dengan contoh berikut:

Asumsikan sebuah Service dengan nama foo pada Kubernetes dengan namespace bar. Sebuah Pod yang dijalankan di namespace bar dapat melakukan resolusi terhadap Service ini dengan melakukan query DNS untuk foo. Sebuah Pod yang dijalankan pada namespace quux dapat melakukan resolusi Service ini dengan melakukan query DNS untuk foo.bar.

Bagian di bawah ini akan menampilkan detail tipe rekaman serta layout yang didukung. Layout atau nama query lain yang dapat digunakan dianggap sebagai detail implementasi yang bisa saja berubah tanpa adanya pemberitahuan sebelumnya. Untuk informasi spesifikasi terbaru kamu dapat membaca Service Discovery pada Kubernetes berbasis DNS.

Service

A record

Service "Normal" (bukan headless) akan diberikan sebuah A record untuk sebuah nama dalam bentuk my-svc.my-namespace.svc.cluster-domain.example. Inilah yang kemudian digunakan untuk melakukan resolusi IP klaster dari Service tersebut.

Service "Headless" (tanpa IP klaster) juga memiliki sebuah A record DNS dengan format my-svc.my-namespace.svc.cluster-domain.example. Tidak seperti halnya Service normal, DNS ini akan melakukan resolusi pada serangkauan IP dari Pod yang dipilih oleh Service tadi. Klien diharapkan untuk mengkonsumsi serangkaian IP ini atau cara lain yang digunakan adalah pemilihan menggunakan penjadwalan Round-Robin dari set yang ada.

SRV record

SRV record dibuat untuk port bernama yang merupakan bagian dari Service normal maupun Headless Services. Untuk setiap port bernama, SRV record akan memiliki format _my-port-name._my-port-protocol.my-svc.my-namespace.svc.cluster-domain.example. Untuk sebuah Service normal, ini akan melakukan resolusi pada nomor port dan nama domain: my-svc.my-namespace.svc.cluster-domain.example. Untuk Service headless, ini akan melakukan resolusi pada serangkaian Pod yang merupakan backend dari Service tersebut yang memiliki format: auto-generated-name.my-svc.my-namespace.svc.cluster-domain.example.

Pod

Hostname Pod dan Field Subdomain

Saat ini ketika sebuah Pod dibuat, hostname-nya adalah nilai dari metadata.name.

Spek Pod memiliki field opsional hostname, yang dapat digunakan untuk menspesifikasikan hostname Pod. Ketika dispesifikasikan, maka nama ini akan didahulukan di atas nama Pod . Misalnya, sebuah Pod dengan hostname yang diberikan nilai "my-host", maka hostname Pod tersebut akan menjadi "my-host".

Spek Pod juga memiliki field opsional subdomain yang dapat digunakan untuk menspesifikasikan subdomain Pod tersebut. Misalnya saja sebuah Pod dengan hostname yang diberi nilai "foo", dan subdomain yang diberi nilai "bar", pada namespace "my-namespace", akan memiliki fully qualified domain name (FQDN) "foo.bar.my-namespace.svc.cluster-domain.example".

Contoh:

apiVersion: v1
kind: Service
metadata:
  name: default-subdomain
spec:
  selector:
    name: busybox
  clusterIP: None
  ports:
  - name: foo # Actually, no port is needed.
    port: 1234
    targetPort: 1234
---
apiVersion: v1
kind: Pod
metadata:
  name: busybox1
  labels:
    name: busybox
spec:
  hostname: busybox-1
  subdomain: default-subdomain
  containers:
  - image: busybox:1.28
    command:
      - sleep
      - "3600"
    name: busybox
---
apiVersion: v1
kind: Pod
metadata:
  name: busybox2
  labels:
    name: busybox
spec:
  hostname: busybox-2
  subdomain: default-subdomain
  containers:
  - image: busybox:1.28
    command:
      - sleep
      - "3600"
    name: busybox

Jika terdapat sebuah Service headless memiliki nama yang sama dengan subdomain dari suatu Pod pada namespace yang sama, server KubeDNS klaster akan mengembalikan A record untuk FQDN Pod. Sebagai contoh, misalnya terdapat sebuah Pod dengan hostname "busybox-1" dan subdomain "default-subdomain", serta sebuah Service headless dengan nama "default-subdomain"
berada pada suatu namespace yang sama, maka Pod tersebut akan menerima FQDN dirinya sendiri sebagai "busybox-1.default-subdomain.my-namespace.svc.cluster-domain.example". DNS mengembalikan A record pada nama tersebut dan mengarahkannya pada IP Pod. Baik Pod "busybox1" dan "busybox2" bisa saja memiliki A record yang berbeda.

Objek Endpoint dapat menspesifikasikan hostname untuk alamat endpoint manapun beserta dengan alamat IP-nya.

Kebijakan DNS Pod

Kebijakan DNS dapat diaktifkan untuk setiap Pod. Kubernetes saat ini mendukung kebijakan DNS spesifik Pod (pod-specific DNS policies). Kebijakan ini dispesifikasikan pada field dnsPolicy yang ada pada spek Pod.

  • "Default": Pod akan mewarisi konfigurasi resolusi yang berasal dari Node dimana Pod tersebut dijalankan. Silakan baca diskusi terkait untuk detailnya.
  • "ClusterFirst": Query DNS apa pun yang tidak sesuai dengan sufiks domain klaster yang sudah dikonfigurasi misalnya "www.kubernetes.io", akan di-forward ke nameserver upstream yang diwarisi dari Node. Administrator klaster bisa saja memiliki stub-domain atau DNS usptream lain yang sudah dikonfigurasi. Silakan lihat diskusi terkait untuk detail lebih lanjut mengenai bagaimana query DNS melakukan hal tersebut.
  • "ClusterFirstWithHostNet": Untuk Pod yang dijalankan dengan menggunakan hostNetwork, kamu harus secara eksplisit mengaktifkan kebijakan DNS-nya menjadi "ClusterFirstWithHostNet".
  • "None": Hal ini mengisikan sebuah Pod untuk mengabaikan konfigurasi DNS dari environment Kubernetes Semua pengaturan DNS disediakan menngunakan field dnsConfig yang ada pada spek Pod. Silakan lihat konfigurasi DNS Pod di bawah.

Contoh di bawah ini menunjukkan sebuah Pod dengan kebijakan DNS yang diubah menjadi "ClusterFirstWithHostNet" karena field hostNetwork diubah menjadi true.

apiVersion: v1
kind: Pod
metadata:
  name: busybox
  namespace: default
spec:
  containers:
  - image: busybox:1.28
    command:
      - sleep
      - "3600"
    imagePullPolicy: IfNotPresent
    name: busybox
  restartPolicy: Always
  hostNetwork: true
  dnsPolicy: ClusterFirstWithHostNet

Konfigurasi DNS Pod

Konfigurasi DNS Pod mengizinkan pengguna untuk memiliki lebih banyak kontrol terhadap pengaturan DNS pada Pod.

Field dnsConfig bersifat opsional dan dapat digunakan dengan pengaturan dnsPolicy apa pun. Meskipun begitu, ketika field dnsPolicy pada sebuah Pod diubah menjadi "None", maka field dnsConfig harus dispesifikasikan.

Berikut merupakan properti yang dapat dispesifikasikan oleh pengguna pada field dnsConfig:

  • nameservers: serangkaian alamat IP yang akan digunakan sebagai server DNS bagi Pod. Jumlah maksimum dari IP yang dapat didaftarkan pada field ini adalah tiga buah IP. Ketika sebuah dnsPolicy pada Pod diubah menjadi "None", maka list ini setidaknya harus mengandung sebuah alamat IP, selain kasus tersebut properti ini bersifat opsional. Server yang didaftarkan akan digabungkan di dalam nameserver dasar yang dihasilkan dari kebijakan DNS yang dispesifikasikan, apabila terdapat duplikat terhadap alamat yang didaftarkan maka alamat tersebut akan dihapus.
  • searches: merupakan serangkaian domain pencarian DNS yang digunakan untuk proses lookup pada Pod. Properti ini bersifat opsional. Ketika dispesifikasikan, list yang disediakan akan digabungkan dengan nama domain pencarian dasar yang dihasilkan dari kebijakan DNS yang dipilih. Alamat yang duplikat akan dihapus. Nilai maksimum domain pencarian yang dapat didaftarkan adalah 6 domain.
  • options: merupakan sebuah list opsional yang berisikan objek dimana setiap objek bisa saja memiliki properti name (yang bersifat wajib). Isi dari properti ini akan digabungkan dengan opsi yang dihasilkan kebijakan DNS yang digunakan. Alamat yang duplikat akan dihapus.

Di bawah ini merupakan contoh sebuah Pod dengan pengaturan DNS kustom:

apiVersion: v1
kind: Pod
metadata:
  namespace: default
  name: dns-example
spec:
  containers:
  - name: test
    image: nginx
  dnsPolicy: "None"
  dnsConfig:
    nameservers:
    - 1.2.3.4
    searches:
    - ns1.svc.cluster-domain.example
    - my.dns.search.suffix
    options:
    - name: ndots
      value: "2"
    - name: edns0

Ketika Pod diatas dibuat, maka Container test memiliki isi berkas /etc/resolv.conf sebagai berikut:

nameserver 1.2.3.4
search ns1.svc.cluster-domain.example my.dns.search.suffix
options ndots:2 edns0

Untuk pengaturan IPv6, path pencarian dan name server harus dispesifikasikan sebagai berikut:

kubectl exec -it dns-example -- cat /etc/resolv.conf

Keluaran yang dihasilkan akan menyerupai:

nameserver fd00:79:30::a
search default.svc.cluster-domain.example svc.cluster-domain.example cluster-domain.example
options ndots:5

Keberadaan Fitur (Feature Availability)

Keberadaan Pod DNS Config dan DNS Policy "None"" diilustrasikan pada tabel di bawah ini.

versi k8sDukungan Fitur
1.14Stable
1.10Beta (aktif secara default)
1.9Alpha

Selanjutnya

Untuk petunjuk lebih lanjut mengenai administrasi konfigurasi DNS, kamu dapat membaca Cara Melakukan Konfigurasi Service DNS

5 - Menghubungkan aplikasi dengan Service

Model Kubernetes untuk menghubungkan kontainer

Sekarang kamu memiliki aplikasi yang telah direplikasi, kamu dapat mengeksposnya di jaringan. Sebelum membahas pendekatan jaringan di Kubernetes, akan lebih baik jika kamu paham bagaimana jaringan bekerja di dalam Docker.

Secara default, Docker menggunakan jaringan host, jadi kontainer dapat berkomunikasi dengan kontainer lainnya jika mereka berada di dalam node yang sama. Agar kontainer Docker dapat berkomunikasi antar node, masing-masing kontainer tersebut harus diberikan port yang berbeda di alamat IP node tersebut, yang akan diteruskan (proxied) ke dalam kontainer. Artinya adalah para kontainer di dalam sebuah node harus berkoordinasi port mana yang akan digunakan atau dialokasikan secara otomatis.

Akan sulit untuk mengkoordinasikan port yang digunakan oleh banyak pengembang. Kubernetes mengasumsikan bahwa Pod dapat berkomunikasi dengan Pod lain, terlepas di Node mana Pod tersebut di deploy. Kubernetes memberikan setiap Pod alamat ClusterIP sehingga kamu tidak perlu secara explisit membuat jalur antara Pod ataupun memetakan port kontainer ke dalam port di dalam Node tersebut. Ini berarti kontainer di dalam sebuah Pod dapat berkomunikasi dengan localhost via port, dan setiap Pod di dalam klaster dapat berkomunikasi tanpa NAT. Panduan ini akan membahas bagaimana kamu dapat menjalankan sebuah layanan atau aplikasi di dalam model jaringan di atas.

Panduan ini menggunakan server nginx sederhana untuk mendemonstrasikan konsepnya. Konsep yang sama juga ditulis lebih lengkap di Aplikasi Jenkins CI.

Mengekspos Pod ke dalam klaster

Kita melakukan ini di beberapa contoh sebelumnya, tetapi mari kita lakukan sekali lagi dan berfokus pada prespektif jaringannya. Buat sebuah nginx Pod, dan perhatikan bahwa templat tersebut mempunyai spesifikasi port kontainer:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-nginx
spec:
  selector:
    matchLabels:
      run: my-nginx
  replicas: 2
  template:
    metadata:
      labels:
        run: my-nginx
    spec:
      containers:
      - name: my-nginx
        image: nginx
        ports:
        - containerPort: 80

Ini membuat aplikasi tersebut dapat diakses dari node manapun di dalam klaster kamu. Cek lokasi node dimana Pod tersebut berjalan:

kubectl apply -f ./run-my-nginx.yaml
kubectl get pods -l run=my-nginx -o wide
NAME                        READY     STATUS    RESTARTS   AGE       IP            NODE
my-nginx-3800858182-jr4a2   1/1       Running   0          13s       10.244.3.4    kubernetes-minion-905m
my-nginx-3800858182-kna2y   1/1       Running   0          13s       10.244.2.5    kubernetes-minion-ljyd

Cek IP dari Pod kamu:

kubectl get pods -l run=my-nginx -o yaml | grep podIP
    podIP: 10.244.3.4
    podIP: 10.244.2.5

Kamu dapat melakukan akses dengan ssh ke dalam node di dalam klaster dan mengakses IP Pod tersebut menggunakan curl. Perlu dicatat bahwa kontainer tersebut tidak menggunakan port 80 di dalam node, atau aturan NAT khusus untuk merutekan trafik ke dalam Pod. Ini berarti kamu dapat menjalankan banyak nginx Pod di node yang sama dimana setiap Pod dapat menggunakan containerPort yang sama, kamu dapat mengakses semua itu dari Pod lain ataupun dari node di dalam klaster menggunakan IP. Seperti Docker, port masih dapat di publikasi ke dalam * interface node*, tetapi kebutuhan seperti ini sudah berkurang karena model jaringannya.

Kamu dapat membaca lebih detail bagaimana kita melakukan ini jika kamu penasaran.

Membuat Service

Kita mempunyai Pod yang menjalankan nginx di dalam klaster. Teorinya, kamu dapat berkomunikasi ke Pod tersebut secara langsung, tapi apa yang terjadi jika sebuah node mati? Pod di dalam node tersebut ikut mati, dan Deployment akan membuat Pod baru, dengan IP yang berbeda. Ini adalah masalah yang Service selesaikan.

Service Kubernetes adalah sebuah abstraksi yang mendefinisikan sekumpulan Pod yang menyediakan fungsi yang sama dan berjalan di dalam klaster. Saat dibuat, setiap Service diberikan sebuah alamat IP (disebut juga ClusterIP). Alamat ini akan terus ada, dan tidak akan pernah berubah selama Service hidup. Pod dapat berkomunikasi dengan Service dan trafik yang menuju Service tersebut akan otomatis dilakukan mekanisme load balancing ke Pod yang merupakan anggota dari Service tersebut.

Kamu dapat membuat Service untuk replika 2 nginx dengan kubectl explose:

kubectl expose deployment/my-nginx
service/my-nginx exposed

Perintah di atas sama dengan kubectl apply -f dengan yaml sebagai berikut:

apiVersion: v1
kind: Service
metadata:
  name: my-nginx
  labels:
    run: my-nginx
spec:
  ports:
  - port: 80
    protocol: TCP
  selector:
    run: my-nginx

Spesifikasi ini akan membuat Service yang membuka TCP port 80 di setiap Pod dengan label run: my-nginx dan mengeksposnya ke dalam port Service (targetPort: adalah port kontainer yang menerima trafik, port adalah service port yang dapat berupa port apapun yang digunakan Pod lain untuk mengakses Service).

Lihat Service objek API untuk melihat daftar field apa saja yang didukung di definisi Service. Cek Service kamu:

kubectl get svc my-nginx
NAME       TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)   AGE
my-nginx   ClusterIP   10.0.162.149   <none>        80/TCP    21s

Seperti yang disebutkan sebelumnya, sebuah Service berisi sekumpulan Pod. Pod diekspos melalui endpoints. Service selector akan mengecek Pod secara terus-menerus dan hasilnya akan dikirim (POSTed) ke objek endpoint yang bernama my-nginx. Saat sebuah Pod mati, IP Pod di dalam endpoint tersebut akan otomatis dihapus, dan Pod baru yang sesuai dengan Service selector akan otomatis ditambahkan ke dalam endpoint. Cek endpoint dan perhatikan bahwa IP sama dengan Pod yang dibuat di langkah pertama:

kubectl describe svc my-nginx
Name:                my-nginx
Namespace:           default
Labels:              run=my-nginx
Annotations:         <none>
Selector:            run=my-nginx
Type:                ClusterIP
IP Family Policy:    SingleStack
IP Families:         IPv4
IP:                  10.0.162.149
IPs:                 10.0.162.149
Port:                <unset> 80/TCP
TargetPort:          80/TCP
Endpoints:           10.244.2.5:80,10.244.3.4:80
Session Affinity:    None
Events:              <none>
kubectl get ep my-nginx
NAME       ENDPOINTS                     AGE
my-nginx   10.244.2.5:80,10.244.3.4:80   1m

Kamu sekarang dapat melakukan curl ke dalam nginx Service di <CLUSTER-IP>:<PORT> dari node manapun di klaster. Perlu dicatat bahwa Service IP adalah IP virtual, IP tersebut tidak pernah ada di interface node manapun. Jika kamu penasaran bagaimana konsep ini bekerja, kamu dapat membaca lebih lanjut tentang service proxy.

Mengakses Service

Kubernetes mendukung 2 mode utama untuk menemukan sebuah Service - variabel environment dan DNS. DNS membutuhkan tambahan CoreDNS di dalam klaster.

Variabel Environment

Saat sebuah Pod berjalan di Node, kubelet akan menambahkan variabel environment untuk setiap Service yang aktif ke dalam Pod. Ini menimbulkan beberapa masalah. Untuk melihatnya, periksa environment dari Pod nginx yang telah kamu buat (nama Pod-mu akan berbeda-beda):

kubectl exec my-nginx-3800858182-jr4a2 -- printenv | grep SERVICE
KUBERNETES_SERVICE_HOST=10.0.0.1
KUBERNETES_SERVICE_PORT=443
KUBERNETES_SERVICE_PORT_HTTPS=443

Perlu dicatat tidak ada variabel environment yang menunjukan Service yang kamu buat. Ini terjadi karena kamu membuat replika terlebih dahulu sebelum membuat Service. Kerugian lain ditimbulkan adalah bahwa komponen scheduler mungkin saja bisa menempatkan semua Pod di dalam satu Node, yang akan membuat keseluruhan Service mati jika Node tersebut mati. Kita dapat menyelesaikan masalah ini dengan menghapus 2 Pod tersebut dan menunggu Deployment untuk membuat Pod kembali. Kali ini Service ada sebelum replika Pod tersebut ada. Ini akan memberikan kamu scheduler-level Service (jika semua Node kamu mempunyai kapasitas yang sama), serta variabel environment yang benar:

kubectl scale deployment my-nginx --replicas=0; kubectl scale deployment my-nginx --replicas=2;

kubectl get pods -l run=my-nginx -o wide
NAME                        READY     STATUS    RESTARTS   AGE     IP            NODE
my-nginx-3800858182-e9ihh   1/1       Running   0          5s      10.244.2.7    kubernetes-minion-ljyd
my-nginx-3800858182-j4rm4   1/1       Running   0          5s      10.244.3.8    kubernetes-minion-905m

Kamu mungkin saja melihat Pod dengan nama yang berbeda, hal ini terjadi karena Pod-Pod itu dihapus dan dibuat ulang.

kubectl exec my-nginx-3800858182-e9ihh -- printenv | grep SERVICE
KUBERNETES_SERVICE_PORT=443
MY_NGINX_SERVICE_HOST=10.0.162.149
KUBERNETES_SERVICE_HOST=10.0.0.1
MY_NGINX_SERVICE_PORT=80
KUBERNETES_SERVICE_PORT_HTTPS=443

DNS

Kubernetes menawarkan sebuah layanan DNS klaster tambahan yang secara otomatis memberikan sebuah nama dns pada Service. Kamu dapat mengecek jika DNS berjalan di dalam klaster Kubernetes:

kubectl get services kube-dns --namespace=kube-system
NAME       TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)         AGE
kube-dns   ClusterIP   10.0.0.10    <none>        53/UDP,53/TCP   8m

Jika DNS belum berjalan, kamu dapat mengaktifkannya.

Sisa panduan ini mengasumsikan kamu mempunyai Service dengan IP (my-nginx), dan sebuah server DNS yang memberikan nama ke dalam IP tersebut (CoreDNS klaster), jadi kamu dapat berkomunikasi dengan Service dari Pod lain di dalam klaster menggunakan metode standar (contohnya gethostbyname). Jalankan aplikasi curl lain untuk melakukan pengujian ini:

kubectl run curl --image=radial/busyboxplus:curl -i --tty --rm
Waiting for pod default/curl-131556218-9fnch to be running, status is Pending, pod ready: false
Hit enter for command prompt

Lalu tekan enter dan jalankan nslookup my-nginx:

[ root@curl-131556218-9fnch:/ ]$ nslookup my-nginx
Server:    10.0.0.10
Address 1: 10.0.0.10

Name:      my-nginx
Address 1: 10.0.162.149

Mengamankan Service

Hingga sekarang kita hanya mengakses nginx server dari dalam klaster. Sebelum mengekspos Service ke internet, kamu harus memastikan bahwa kanal komunikasi aman. Untuk melakukan hal tersebut, kamu membutuhkan:

  • Self signed certificates untuk https (kecuali jika kamu sudah mempunyai identity certificate)
  • Sebuah server nginx yang terkonfigurasi untuk menggunakan certificate tersebut
  • Sebuah secret yang membuat setifikat tersebut dapat diakses oleh pod

Kamu dapat melihat semua itu di contoh nginx https. Contoh ini mengaharuskan kamu melakukan instalasi go dan make. Jika kamu tidak ingin melakukan instalasi tersebut, ikuti langkah-langkah manualnya nanti, singkatnya:

make keys KEY=/tmp/nginx.key CERT=/tmp/nginx.crt
kubectl create secret tls nginxsecret --key /tmp/nginx.key --cert /tmp/nginx.crt
secret/nginxsecret created
kubectl get secrets
NAME                  TYPE                                  DATA      AGE
default-token-il9rc   kubernetes.io/service-account-token   1         1d
nginxsecret           Opaque                                2         1m

Berikut ini adalah langkah-langkah manual yang harus diikuti jika kamu mengalami masalah menjalankan make (pada windows contohnya):

#membuat sebuah key-pair public private
openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout /d/tmp/nginx.key -out /d/tmp/nginx.crt -subj "/CN=my-nginx/O=my-nginx"
#rubah key tersebut ke dalam pengkodean base64
cat /d/tmp/nginx.crt | base64
cat /d/tmp/nginx.key | base64

Gunakan hasil keluaran dari perintah sebelumnya untuk membuat sebuah file yaml seperti berikut. Nilai yang dikodekan base64 harus berada di dalam satu baris.

apiVersion: "v1"
kind: "Secret"
metadata:
  name: "nginxsecret"
  namespace: "default"
data:
  nginx.crt: "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURIekNDQWdlZ0F3SUJBZ0lKQUp5M3lQK0pzMlpJTUEwR0NTcUdTSWIzRFFFQkJRVUFNQ1l4RVRBUEJnTlYKQkFNVENHNW5hVzU0YzNaak1SRXdEd1lEVlFRS0V3aHVaMmx1ZUhOMll6QWVGdzB4TnpFd01qWXdOekEzTVRKYQpGdzB4T0RFd01qWXdOekEzTVRKYU1DWXhFVEFQQmdOVkJBTVRDRzVuYVc1NGMzWmpNUkV3RHdZRFZRUUtFd2h1CloybHVlSE4yWXpDQ0FTSXdEUVlKS29aSWh2Y05BUUVCQlFBRGdnRVBBRENDQVFvQ2dnRUJBSjFxSU1SOVdWM0IKMlZIQlRMRmtobDRONXljMEJxYUhIQktMSnJMcy8vdzZhU3hRS29GbHlJSU94NGUrMlN5ajBFcndCLzlYTnBwbQppeW1CL3JkRldkOXg5UWhBQUxCZkVaTmNiV3NsTVFVcnhBZW50VWt1dk1vLzgvMHRpbGhjc3paenJEYVJ4NEo5Ci82UVRtVVI3a0ZTWUpOWTVQZkR3cGc3dlVvaDZmZ1Voam92VG42eHNVR0M2QURVODBpNXFlZWhNeVI1N2lmU2YKNHZpaXdIY3hnL3lZR1JBRS9mRTRqakxCdmdONjc2SU90S01rZXV3R0ljNDFhd05tNnNTSzRqYUNGeGpYSnZaZQp2by9kTlEybHhHWCtKT2l3SEhXbXNhdGp4WTRaNVk3R1ZoK0QrWnYvcW1mMFgvbVY0Rmo1NzV3ajFMWVBocWtsCmdhSXZYRyt4U1FVQ0F3RUFBYU5RTUU0d0hRWURWUjBPQkJZRUZPNG9OWkI3YXc1OUlsYkROMzhIYkduYnhFVjcKTUI4R0ExVWRJd1FZTUJhQUZPNG9OWkI3YXc1OUlsYkROMzhIYkduYnhFVjdNQXdHQTFVZEV3UUZNQU1CQWY4dwpEUVlKS29aSWh2Y05BUUVGQlFBRGdnRUJBRVhTMW9FU0lFaXdyMDhWcVA0K2NwTHI3TW5FMTducDBvMm14alFvCjRGb0RvRjdRZnZqeE04Tzd2TjB0clcxb2pGSW0vWDE4ZnZaL3k4ZzVaWG40Vm8zc3hKVmRBcStNZC9jTStzUGEKNmJjTkNUekZqeFpUV0UrKzE5NS9zb2dmOUZ3VDVDK3U2Q3B5N0M3MTZvUXRUakViV05VdEt4cXI0Nk1OZWNCMApwRFhWZmdWQTRadkR4NFo3S2RiZDY5eXM3OVFHYmg5ZW1PZ05NZFlsSUswSGt0ejF5WU4vbVpmK3FqTkJqbWZjCkNnMnlwbGQ0Wi8rUUNQZjl3SkoybFIrY2FnT0R4elBWcGxNSEcybzgvTHFDdnh6elZPUDUxeXdLZEtxaUMwSVEKQ0I5T2wwWW5scE9UNEh1b2hSUzBPOStlMm9KdFZsNUIyczRpbDlhZ3RTVXFxUlU9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K"
  nginx.key: "LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV2UUlCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQktjd2dnU2pBZ0VBQW9JQkFRQ2RhaURFZlZsZHdkbFIKd1V5eFpJWmVEZWNuTkFhbWh4d1NpeWF5N1AvOE9ta3NVQ3FCWmNpQ0RzZUh2dGtzbzlCSzhBZi9WemFhWm9zcApnZjYzUlZuZmNmVUlRQUN3WHhHVFhHMXJKVEVGSzhRSHA3VkpMcnpLUC9QOUxZcFlYTE0yYzZ3MmtjZUNmZitrCkU1bEVlNUJVbUNUV09UM3c4S1lPNzFLSWVuNEZJWTZMMDUrc2JGQmd1Z0ExUE5JdWFubm9UTWtlZTRuMG4rTDQKb3NCM01ZUDhtQmtRQlAzeE9JNHl3YjREZXUraURyU2pKSHJzQmlIT05Xc0RadXJFaXVJMmdoY1kxeWIyWHI2UAozVFVOcGNSbC9pVG9zQngxcHJHclk4V09HZVdPeGxZZmcvbWIvNnBuOUYvNWxlQlkrZStjSTlTMkQ0YXBKWUdpCkwxeHZzVWtGQWdNQkFBRUNnZ0VBZFhCK0xkbk8ySElOTGo5bWRsb25IUGlHWWVzZ294RGQwci9hQ1Zkank4dlEKTjIwL3FQWkUxek1yall6Ry9kVGhTMmMwc0QxaTBXSjdwR1lGb0xtdXlWTjltY0FXUTM5SjM0VHZaU2FFSWZWNgo5TE1jUHhNTmFsNjRLMFRVbUFQZytGam9QSFlhUUxLOERLOUtnNXNrSE5pOWNzMlY5ckd6VWlVZWtBL0RBUlBTClI3L2ZjUFBacDRuRWVBZmI3WTk1R1llb1p5V21SU3VKdlNyblBESGtUdW1vVlVWdkxMRHRzaG9reUxiTWVtN3oKMmJzVmpwSW1GTHJqbGtmQXlpNHg0WjJrV3YyMFRrdWtsZU1jaVlMbjk4QWxiRi9DSmRLM3QraTRoMTVlR2ZQegpoTnh3bk9QdlVTaDR2Q0o3c2Q5TmtEUGJvS2JneVVHOXBYamZhRGR2UVFLQmdRRFFLM01nUkhkQ1pKNVFqZWFKClFGdXF4cHdnNzhZTjQyL1NwenlUYmtGcVFoQWtyczJxWGx1MDZBRzhrZzIzQkswaHkzaE9zSGgxcXRVK3NHZVAKOWRERHBsUWV0ODZsY2FlR3hoc0V0L1R6cEdtNGFKSm5oNzVVaTVGZk9QTDhPTm1FZ3MxMVRhUldhNzZxelRyMgphRlpjQ2pWV1g0YnRSTHVwSkgrMjZnY0FhUUtCZ1FEQmxVSUUzTnNVOFBBZEYvL25sQVB5VWs1T3lDdWc3dmVyClUycXlrdXFzYnBkSi9hODViT1JhM05IVmpVM25uRGpHVHBWaE9JeXg5TEFrc2RwZEFjVmxvcG9HODhXYk9lMTAKMUdqbnkySmdDK3JVWUZiRGtpUGx1K09IYnRnOXFYcGJMSHBzUVpsMGhucDBYSFNYVm9CMUliQndnMGEyOFVadApCbFBtWmc2d1BRS0JnRHVIUVV2SDZHYTNDVUsxNFdmOFhIcFFnMU16M2VvWTBPQm5iSDRvZUZKZmcraEppSXlnCm9RN3hqWldVR3BIc3AyblRtcHErQWlSNzdyRVhsdlhtOElVU2FsbkNiRGlKY01Pc29RdFBZNS9NczJMRm5LQTQKaENmL0pWb2FtZm1nZEN0ZGtFMXNINE9MR2lJVHdEbTRpb0dWZGIwMllnbzFyb2htNUpLMUI3MkpBb0dBUW01UQpHNDhXOTVhL0w1eSt5dCsyZ3YvUHM2VnBvMjZlTzRNQ3lJazJVem9ZWE9IYnNkODJkaC8xT2sybGdHZlI2K3VuCnc1YytZUXRSTHlhQmd3MUtpbGhFZDBKTWU3cGpUSVpnQWJ0LzVPbnlDak9OVXN2aDJjS2lrQ1Z2dTZsZlBjNkQKckliT2ZIaHhxV0RZK2Q1TGN1YSt2NzJ0RkxhenJsSlBsRzlOZHhrQ2dZRUF5elIzT3UyMDNRVVV6bUlCRkwzZAp4Wm5XZ0JLSEo3TnNxcGFWb2RjL0d5aGVycjFDZzE2MmJaSjJDV2RsZkI0VEdtUjZZdmxTZEFOOFRwUWhFbUtKCnFBLzVzdHdxNWd0WGVLOVJmMWxXK29xNThRNTBxMmk1NVdUTThoSDZhTjlaMTltZ0FGdE5VdGNqQUx2dFYxdEYKWSs4WFJkSHJaRnBIWll2NWkwVW1VbGc9Ci0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0K"

Sekarang buat secrets menggunakan file tersebut:

kubectl apply -f nginxsecrets.yaml
kubectl get secrets
NAME                  TYPE                                  DATA      AGE
default-token-il9rc   kubernetes.io/service-account-token   1         1d
nginxsecret           Opaque                                2         1m

Sekarang modifikasi replika nginx untuk menjalankan server https menggunakan certificate di dalam secret dan Service untuk mengekspos semua port (80 dan 443):

apiVersion: v1
kind: Service
metadata:
  name: my-nginx
  labels:
    run: my-nginx
spec:
  type: NodePort
  ports:
  - port: 8080
    targetPort: 80
    protocol: TCP
    name: http
  - port: 443
    protocol: TCP
    name: https
  selector:
    run: my-nginx
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-nginx
spec:
  selector:
    matchLabels:
      run: my-nginx
  replicas: 1
  template:
    metadata:
      labels:
        run: my-nginx
    spec:
      volumes:
      - name: secret-volume
        secret:
          secretName: nginxsecret
      containers:
      - name: nginxhttps
        image: bprashanth/nginxhttps:1.0
        ports:
        - containerPort: 443
        - containerPort: 80
        volumeMounts:
        - mountPath: /etc/nginx/ssl
          name: secret-volume

Berikut catatan penting tentang manifes nginx-secure-app:

  • di dalam file tersebut, ada spesifikasi Deployment dan Service
  • ada server nginx yang melayani trafik HTTP di port 80 dan trafik HTTPS di port 443, dan Service nginx yang mengekspos kedua port tersebut.
  • Setiap kontainer mempunyai akses ke key melalui volume yang di mount pada /etc/nginx/ssl. Ini adalah konfigurasi sebelum server nginx dijalankan.
kubectl delete deployments,svc my-nginx; kubectl create -f ./nginx-secure-app.yaml

Pada tahapan ini, kamu dapat berkomunikasi dengan server nginx dari node manapun.

kubectl get pods -l run=my-nginx -o custom-columns=POD_IP:.status.podIPs
    POD_IP
    [map[ip:10.244.3.5]]
node $ curl -k https://10.244.3.5
...
<h1>Welcome to nginx!</h1>

Perlu dicatat bahwa kita menggunakan parameter -k saat menggunakan curl, ini karena kita tidak tau apapun tentang Pod yang menjalankan nginx saat pembuatan seritifikat, jadi kita harus memberitahu curl untuk mengabaikan ketidakcocokan CName. Dengan membuat Service, kita menghubungkan CName yang digunakan pada certificate dengan nama pada DNS yang digunakan Pod. Lakukan pengujian dari sebuah Pod (secret yang sama digunakan untuk agar mudah, Pod tersebut hanya membutuhkan nginx.crt untuk mengakses Service)

apiVersion: apps/v1
kind: Deployment
metadata:
  name: curl-deployment
spec:
  selector:
    matchLabels:
      app: curlpod
  replicas: 1
  template:
    metadata:
      labels:
        app: curlpod
    spec:
      volumes:
      - name: secret-volume
        secret:
          secretName: nginxsecret
      containers:
      - name: curlpod
        command:
        - sh
        - -c
        - while true; do sleep 1; done
        image: radial/busyboxplus:curl
        volumeMounts:
        - mountPath: /etc/nginx/ssl
          name: secret-volume
kubectl apply -f ./curlpod.yaml
kubectl get pods -l app=curlpod
NAME                               READY     STATUS    RESTARTS   AGE
curl-deployment-1515033274-1410r   1/1       Running   0          1m
kubectl exec curl-deployment-1515033274-1410r -- curl https://my-nginx --cacert /etc/nginx/ssl/nginx.crt
...
<title>Welcome to nginx!</title>
...

Mengekspos Service

Kamu mungkin ingin mengekspos Service ke alamat IP eksternal. Kubernetes mendukung dua cara untuk melakukan ini: NodePort dan LoadBalancer. Service yang dibuat tadi sudah menggunakan NodePort, jadi replika nginx sudah siap untuk menerima trafik dari internet jika Node kamu mempunyai IP publik.

kubectl get svc my-nginx -o yaml | grep nodePort -C 5
  uid: 07191fb3-f61a-11e5-8ae5-42010af00002
spec:
  clusterIP: 10.0.162.149
  ports:
  - name: http
    nodePort: 31704
    port: 8080
    protocol: TCP
    targetPort: 80
  - name: https
    nodePort: 32453
    port: 443
    protocol: TCP
    targetPort: 443
  selector:
    run: my-nginx
kubectl get nodes -o yaml | grep ExternalIP -C 1
    - address: 104.197.41.11
      type: ExternalIP
    allocatable:
--
    - address: 23.251.152.56
      type: ExternalIP
    allocatable:
...

$ curl https://<EXTERNAL-IP>:<NODE-PORT> -k
...
<h1>Welcome to nginx!</h1>

Mari coba membuat ulang Service menggunakan cloud load balancer, ubah saja type Service my-nginx dari NodePort ke LoadBalancer:

kubectl edit svc my-nginx
kubectl get svc my-nginx
NAME       TYPE        CLUSTER-IP     EXTERNAL-IP        PORT(S)               AGE
my-nginx   ClusterIP   10.0.162.149   162.222.184.144    80/TCP,81/TCP,82/TCP  21s
curl https://<EXTERNAL-IP> -k
...
<title>Welcome to nginx!</title>

IP address pada kolom EXTERNAL-IP menunjukan IP yang tersedia di internet. Sedangkan kolom CLUSTER-IP merupakan IP yang hanya tersedia di dalam klaster kamu (IP private).

Perhatikan pada AWS, tipe LoadBalancer membuat sebuah ELB, yang menggunakan hostname yang panjang, bukan IP. Karena tidak semua keluar pada standar keluaran kubectl get svc. Jadi kamu harus menggunakan kubectl describe service my-nginx untuk melihatnya. Kamu akan melihat seperti ini:

kubectl describe service my-nginx
...
LoadBalancer Ingress:   a320587ffd19711e5a37606cf4a74574-1142138393.us-east-1.elb.amazonaws.com
...

Selanjutnya

Kubernetes juga mendukung Federated Service, yang bisa mempengaruhi banyak klaster dan penyedia layanan cloud, untuk meningkatkan ketersediaan, peningkatan toleransi kesalahan, dan pengembangan dari Service kamu. Lihat Panduan Federated Service untuk informasi lebih lanjut.

6 - Ingress

Sebuah obyek API yang mengatur akses eksternal terhadap Service yang ada di dalam klaster, biasanya dalam bentuk request HTTP.

Ingress juga menyediakan load balancing, terminasi SSL, serta name-based virtual hosting.

Terminologi

Untuk memudahkan, di awal akan dijelaskan beberapa terminologi yang sering dipakai:

  • Node: Sebuah mesin fisik atau virtual yang berada di dalam klaster Kubernetes.
  • Klaster: Sekelompok node yang merupakan resource komputasi primer yang diatur oleh Kubernetes, biasanya diproteksi dari internet dengan menggunakan firewall.
  • Edge router: Sebuah router mengatur policy firewall pada klaster kamu. Router ini bisa saja berupa gateway yang diatur oleh penyedia layanan cloud maupun perangkat keras.
  • Jaringan klaster: Seperangkat links baik logis maupus fisik, yang memfasilitasi komunikasi di dalam klaster berdasarkan model jaringan Kubernetes.
  • Service: Sebuah Service yang mengidentifikasi beberapa Pod dengan menggunakan selector label. Secara umum, semua Service diasumsikan hanya memiliki IP virtual yang hanya dapat diakses dari dalam jaringan klaster.

Apakah Ingress itu?

Ingress ditambahkan sejak Kubernetes v1.1, mengekspos rute HTTP dan HTTPS ke berbagai services di dalam klaster. Mekanisme routing trafik dikendalikan oleh aturan-aturan yang didefinisikan pada Ingress.

    internet
        |
   [ Ingress ]
   --|-----|--
   [ Services ]

Sebuah Ingress dapat dikonfigurasi agar berbagai Service memiliki URL yang dapat diakses dari eksternal (luar klaster), melakukan load balance pada trafik, terminasi SSL, serta Virtual Host berbasis Nama. Sebuah kontroler Ingress bertanggung jawab untuk menjalankan fungsi Ingress yaitu sebagai loadbalancer, meskipun dapat juga digunakan untuk mengatur edge router atau frontend tambahan untuk menerima trafik.

Sebuah Ingress tidak mengekspos sembarang port atau protokol. Mengekspos Service untuk protokol selain HTTP ke HTTPS internet biasanya dilakukan dengan menggunakan service dengan tipe Service.Type=NodePort atau Service.Type=LoadBalancer.

Prasyarat

FEATURE STATE: Kubernetes v1.1 [beta]

Sebelum kamu mulai menggunakan Ingress, ada beberapa hal yang perlu kamu ketahui sebelumnya. Ingress merupakan resource dengan tipe beta.

GCE/Google Kubernetes Engine melakukan deploy kontroler Ingress pada master. Perhatikan laman berikut keterbatasan versi beta kontroler ini jika kamu menggunakan GCE/GKE.

Jika kamu menggunakan environment selain GCE/Google Kubernetes Engine, kemungkinan besar kamu harus melakukan proses deploy kontroler ingress kamu sendiri. Terdapat beberapa jenis kontroler Ingress yang bisa kamu pilih.

Sebelum kamu memulai

Secara ideal, semua kontroler Ingress harus memenuhi spesifikasi ini, tetapi beberapa kontroler beroperasi sedikit berbeda satu sama lain.

Resource Ingress

Berikut ini merupakan salah satu contoh konfigurasi Ingress yang minimum:

apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: test-ingress
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  rules:
  - http:
      paths:
      - path: /testpath
        backend:
          serviceName: test
          servicePort: 80

Seperti layaknya resource Kubernetes yang lain, sebuah Ingress membutuhkan field apiVersion, kind, dan metadata. Untuk informasi umum soal bagaimana cara bekerja dengan menggunakan berkas konfigurasi, silahkan merujuk pada melakukan deploy aplikasi, konfigurasi kontainer, mengatur resource. Ingress seringkali menggunakan anotasi untuk melakukan konfigurasi beberapa opsi yang ada bergantung pada kontroler Ingress yang digunakan, sebagai contohnya adalah anotasi rewrite-target. Kontroler Ingress yang berbeda memiliki jenis anotasi yang berbeda. Pastikan kamu sudah terlebih dahulu memahami dokumentasi kontroler Ingress yang akan kamu pakai untuk mengetahui jenis anotasi apa sajakah yang disediakan.

Spesifikasi Ingress memiliki segala informasi yang dibutuhkan untuk melakukan proses konfigurasi loadbalancer atau server proxy. Hal yang terpenting adalah bagian inilah yang mengandung semua rules yang nantinya akan digunakan untuk menyesuaikan trafik yang masuk. Resource Ingress hanya menyediakan fitur rules untuk mengarahkan trafik dengan protokol HTTP.

Rule Ingress

Setiap rule HTTP mengandung informasi berikut:

  • Host opsional. Di dalam contoh ini, tidak ada host yang diberikan, dengan kata lain, semua rules berlaku untuk inbound trafik HTTP bagi alamat IP yang dispesifikasikan. JIka sebuah host dispesifikasikan (misalnya saja, foo.bar.com), maka rules yang ada akan berlaku bagi host tersebut.
  • Sederetan path (misalnya, /testpath), setiap path ini akan memiliki pasangan berupa sebuah backend yang didefinisikan dengan serviceName dan servicePort. Baik host dan path harus sesuai dengan konten dari request yang masuk sebelum loadbalancer akan mengarahkan trafik pada service yang sesuai.
  • Suatu backend adalah kombinasi service dan port seperti yang dideskripsikan di dokumentasi Service. Request HTTP (dan HTTPS) yang sesuai dengan host dan path yang ada pada rule akan diteruskan pada backend terkait.

Backend default seringkali dikonfigurasi pada kontroler kontroler Ingress, tugas backend default ini adalah mengarahkan request yang tidak sesuai dengan path yang tersedia pada spesifikasi.

Backend Default

Sebuah Ingress yang tidak memiliki rules akan mengarahkan semua trafik pada sebuah backend default. Backend default inilah yang biasanya bisa dimasukkan sebagai salah satu opsi konfigurasi dari kontroler Ingress dan tidak dimasukkan dalam spesifikasi resource Ingress.

Jika tidak ada host atau path yang sesuai dengan request HTTP pada objek Ingress, maka trafik tersebut akan diarahkan pada backend default.

Jenis Ingress

Ingress dengan satu Service

Terdapat konsep Kubernetes yang memungkinkan kamu untuk mengekspos sebuah Service, lihat alternatif lain. Kamu juga bisa membuat spesifikasi Ingress dengan backend default yang tidak memiliki rules.

apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: test-ingress
spec:
  backend:
    serviceName: testsvc
    servicePort: 80

Jika kamu menggunakan kubectl apply -f kamu dapat melihat:

kubectl get ingress test-ingress
NAME           HOSTS     ADDRESS           PORTS     AGE
test-ingress   *         107.178.254.228   80        59s

Dimana 107.178.254.228 merupakan alamat IP yang dialokasikan oleh kontroler Ingress untuk memenuhi Ingress ini.

Fanout sederhana

Sebuah konfigurasi fanout akan melakukan route trafik dari sebuah alamat IP ke banyak Service, berdasarkan URI HTTP yang diberikan. Sebuah Ingress memungkinkan kamu untuk memiliki jumlah loadbalancer minimum. Contohnya, konfigurasi seperti di bawah ini:

foo.bar.com -> 178.91.123.132 -> / foo    service1:4200
                                 / bar    service2:8080

akan memerlukan konfigurasi Ingress seperti:

apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: simple-fanout-example
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  rules:
  - host: foo.bar.com
    http:
      paths:
      - path: /foo
        backend:
          serviceName: service1
          servicePort: 4200
      - path: /bar
        backend:
          serviceName: service2
          servicePort: 8080

Ketika kamu membuat Ingress dengan perintah kubectl apply -f:

kubectl describe ingress simple-fanout-example
Name:             simple-fanout-example
Namespace:        default
Address:          178.91.123.132
Default backend:  default-http-backend:80 (10.8.2.3:8080)
Rules:
  Host         Path  Backends
  ----         ----  --------
  foo.bar.com
               /foo   service1:4200 (10.8.0.90:4200)
               /bar   service2:8080 (10.8.0.91:8080)
Annotations:
  nginx.ingress.kubernetes.io/rewrite-target:  /
Events:
  Type     Reason  Age                From                     Message
  ----     ------  ----               ----                     -------
  Normal   ADD     22s                loadbalancer-controller  default/test

Kontroler Ingress akan menyediakan loadbalancer (implementasinya tergantung dari jenis Ingress yang digunakan), selama service-service yang didefinisikan (s1, s2) ada. Apabila Ingress selesai dibuat, maka kamu dapat melihat alamat IP dari berbagai loadbalancer pada kolom address.

Virtual Host berbasis Nama

Virtual Host berbasis Nama memungkinkan mekanisme routing berdasarkan trafik HTTP ke beberapa host name dengan alamat IP yang sama.

foo.bar.com --|                 |-> foo.bar.com s1:80
              | 178.91.123.132  |
bar.foo.com --|                 |-> bar.foo.com s2:80

Ingress di bawah ini memberikan perintah pada loadbalancer untuk melakukan mekanisme routing berdasarkan header host.

apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: name-virtual-host-ingress
spec:
  rules:
  - host: foo.bar.com
    http:
      paths:
      - backend:
          serviceName: service1
          servicePort: 80
  - host: bar.foo.com
    http:
      paths:
      - backend:
          serviceName: service2
          servicePort: 80

Jika kamu membuat sebuah Ingress tanpa mendefinisikan host apa pun, maka trafik web ke alamat IP dari kontroler Ingress tetap dapat dilakukan tanpa harus menyesuaikan aturan name based virtual host. Sebagai contoh, resource Ingress di bawah ini akan melakukan pemetaan trafik dari first.bar.com ke service1, second.foo.com ke service2, dan trafik lain ke alamat IP tanpa host name yang didefinisikan di dalam request (yang tidak memiliki request header) ke service3.

apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: name-virtual-host-ingress
spec:
  rules:
  - host: first.bar.com
    http:
      paths:
      - backend:
          serviceName: service1
          servicePort: 80
  - host: second.foo.com
    http:
      paths:
      - backend:
          serviceName: service2
          servicePort: 80
  - http:
      paths:
      - backend:
          serviceName: service3
          servicePort: 80

TLS

Kamu dapat mengamankan Ingress yang kamu miliki dengan memberikan spesifikasi secret yang mengandung private key dan sertifikat TLS. Saat ini, Ingress hanya memiliki fitur untuk melakukan konfigurasi single TLS port, yaitu 443, serta melakukan terminasi TLS. Jika section TLS pada Ingress memiliki spesifikasi host yang berbeda, rules yang ada akan dimultiplekskan pada port yang sama berdasarkan hostname yang dispesifikasikan melalui ekstensi TLS SNI. Secret TLS harus memiliki key bernama tls.crt dan tls.key yang mengandung private key dan sertifikat TLS, contohnya:

apiVersion: v1
data:
  tls.crt: base64 encoded cert
  tls.key: base64 encoded key
kind: Secret
metadata:
  name: testsecret-tls
  namespace: default
type: kubernetes.io/tls

Ketika kamu menambahkan secret pada Ingress maka kontroler Ingress akan memberikan perintah untuk memproteksi channel dari klien ke loadbalancer menggunakan TLS. Kamu harus memastikan secret TLS yang digunakan memiliki sertifikat yang mengandung CN untuk sslexample.foo.com.

apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: tls-example-ingress
spec:
  tls:
  - hosts:
    - sslexample.foo.com
    secretName: testsecret-tls
  rules:
    - host: sslexample.foo.com
      http:
        paths:
        - path: /
          backend:
            serviceName: service1
            servicePort: 80

Loadbalancing

Sebuah kontroler Ingress sudah dibekali dengan beberapa policy terkait mekanisme load balance yang nantinya akan diterapkan pada semua Ingress, misalnya saja algoritma load balancing, backend weight scheme, dan lain sebagainya. Beberapa konsep load balance yang lebih advance (misalnya saja persistent sessions, dynamic weights) belum diekspos melalui Ingress. Meskipun begitu, kamu masih bisa menggunakan fitur ini melalui loadbalancer service.

Perlu diketahui bahwa meskipun health check tidak diekspos secara langsung melalui Ingress, terdapat beberapa konsep di Kubernetes yang sejalan dengan hal ini, misalnya readiness probes yang memungkinkan kamu untuk memperoleh hasil yang sama. Silahkan pelajari lebih lanjut dokumentasi kontroler yang kamu pakai untuk mengetahui bagaimana implementasi health checks pada kontroler yang kamu pilih (nginx, GCE).

Mengubah Ingress

Untuk mengubah Ingress yang sudah ada dan menambahkan host baru, kamu dapat mengubahnya dengan mode edit:

kubectl describe ingress test
Name:             test
Namespace:        default
Address:          178.91.123.132
Default backend:  default-http-backend:80 (10.8.2.3:8080)
Rules:
  Host         Path  Backends
  ----         ----  --------
  foo.bar.com
               /foo   s1:80 (10.8.0.90:80)
Annotations:
  nginx.ingress.kubernetes.io/rewrite-target:  /
Events:
  Type     Reason  Age                From                     Message
  ----     ------  ----               ----                     -------
  Normal   ADD     35s                loadbalancer-controller  default/test
kubectl edit ingress test

Sebuah editor akan muncul dan menampilkan konfigurasi Ingress kamu dalam format YAML apabila kamu telah menjalankan perintah di atas. Ubah untuk menambahkan host:

spec:
  rules:
  - host: foo.bar.com
    http:
      paths:
      - backend:
          serviceName: s1
          servicePort: 80
        path: /foo
  - host: bar.baz.com
    http:
      paths:
      - backend:
          serviceName: s2
          servicePort: 80
        path: /foo
..

Menyimpan konfigurasi dalam bentuk YAML ini akan mengubah resource pada API server, yang kemudian akan memberi tahu kontroler Ingress untuk mengubah konfigurasi loadbalancer.

kubectl describe ingress test
Name:             test
Namespace:        default
Address:          178.91.123.132
Default backend:  default-http-backend:80 (10.8.2.3:8080)
Rules:
  Host         Path  Backends
  ----         ----  --------
  foo.bar.com
               /foo   s1:80 (10.8.0.90:80)
  bar.baz.com
               /foo   s2:80 (10.8.0.91:80)
Annotations:
  nginx.ingress.kubernetes.io/rewrite-target:  /
Events:
  Type     Reason  Age                From                     Message
  ----     ------  ----               ----                     -------
  Normal   ADD     45s                loadbalancer-controller  default/test

Kamu juga dapat mengubah Ingress dengan menggunakan perintah kubectl replace -f pada berkas konfigurasi Ingress yang ingin diubah.

Mekanisme failing pada beberapa zona availability

Teknik untuk menyeimbangkan persebaran trafik pada failure domain berbeda antar penyedia layanan cloud. Kamu dapat mempelajari dokumentasi yang relevan bagi kontoler Ingress untuk informasi yang lebih detail. Kamu juga dapat mempelajari dokumentasi federasi untuk informasi lebih detail soal bagaimana melakukan deploy untuk federasi klaster.

Pengembangan selanjutnya

Silahkan amati SIG Network untuk detail lebih lanjut mengenai perubahan Ingress dan resource terkait lainnya. Kamu juga bisa melihat repositori Ingress untuk informasi yang lebih detail soal perubahan berbagai kontroler.

Alternatif lain

Kamu dapat mengekspos sebuah Service dalam berbagai cara, tanpa harus menggunakan resource Ingress, dengan menggunakan:

Selanjutnya

7 - Kontroler Ingress

Agar Ingress dapat bekerja sebagaimana mestinya, sebuah klaster harus memiliki paling tidak sebuah kontroler Ingress.

Berbeda dengan kontroler-kontroler lainnya yang dijalankan sebagai bagian dari binary kube-controller-manager, kontroler Ingress tidak secara otomatis dijalankan di dalam klaster. Kamu bisa menggunakan laman ini untuk memilih implementasi kontroler Ingress yang kamu pikir paling sesuai dengan kebutuhan kamu.

Kubernetes sebagai sebuah proyek, saat ini, mendukung dan memaintain kontroler-kontroler GCE dan nginx.

Kontroler-kontroler lainnya

Menggunakan beberapa jenis kontroler Ingress sekaligus

Kamu dapat melakukan deploy berapa pun banyaknya kontroler Ingress dalam sebuah klaster. Jika kamu ingin membuat Ingress, kamu tinggal memberikan anotasi setiap Ingress sesuai dengan ingress.class yang sesuai untuk menandai kontroler Ingress mana yang digunakan jika terdapat lebih dari satu kontroler Ingress yang ada di klaster kamu.

Apabila kamu tidak mendefinisikan class yang dipakai, penyedia layanan cloud kamu akan menggunakan kontroler Ingress default yang mereka miliki.

Idealnya, semua ingress harus memenuhi spesifikasi ini, tetapi berbagai jenis kontroler Ingress bisa saja memiliki sedikit perbedaan cara kerja.

Selanjutnya

8 - NetworkPolicy

Sebuah NetworkPolicy adalah spesifikasi dari sekelompok Pod atau endpoint yang diizinkan untuk saling berkomunikasi.

NetworkPolicy menggunakan label untuk memilih Pod serta mendefinisikan serangkaian rule yang digunakan untuk mendefinisikan trafik yang diizinkan untuk suatu Pod tertentu.

Prasyarat

NetworkPolicy diimplementasikan dengan menggunakan plugin jaringan, dengan demikian kamu harus memiliki penyedia jaringan yang mendukung NetworkPolicy - membuat resource tanpa adanya controller tidak akan berdampak apa pun.

Pod yang terisolasi dan tidak terisolasi

Secara default, Pod bersifat tidak terisolasi; Pod-Pod tersebut menerima trafik dari resource apa pun.

Pod menjadi terisolasi apabila terdapat NetworkPolicy yang dikenakan pada Pod-Pod tersebut. Apabila terdapat NetworkPolicy di dalam namespace yang dikenakan pada suatu Pod, Pod tersebut akan menolak koneksi yang tidak diizinkan NetworkPolicy. (Pod lain dalam namespace yang tidak dikenakan NetworkPolicy akan tetap menerima trafik dari semua resource.)

Resource NetworkPolicy

Lihat NetworkPolicy untuk definisi lengkap resource.

Sebuah contoh NetworkPolicy akan terlihat seperti berikut:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: test-network-policy
  namespace: default
spec:
  podSelector:
    matchLabels:
      role: db
  policyTypes:
  - Ingress
  - Egress
  ingress:
  - from:
    - ipBlock:
        cidr: 172.17.0.0/16
        except:
        - 172.17.1.0/24
    - namespaceSelector:
        matchLabels:
          project: myproject
    - podSelector:
        matchLabels:
          role: frontend
    ports:
    - protocol: TCP
      port: 6379
  egress:
  - to:
    - ipBlock:
        cidr: 10.0.0.0/24
    ports:
    - protocol: TCP
      port: 5978

Mengirimkan ini ke API server dengan metode POST tidak akan berdampak apa pun kecuali penyedia jaringan mendukung network policy.

Field-field yang bersifat wajib: Sama dengan seluruh config Kubernetes lainnya, sebuah NetworkPolicy membutuhkan field-field apiVersion, kind, dan metadata. Informasi generik mengenai bagaimana bekerja dengan file config, dapat dilihat di Konfigurasi Kontainer menggunakan ConfigMap, serta Manajemen Objek.

spec: NetworkPolicy spec memiliki semua informasi yang harus diberikan untuk memberikan definisi network policy yang ada pada namespace tertentu.

podSelector: Setiap NetworkPolicy memiliki sebuah podSelector yang bertugas memfilter Pod-Pod yang dikenai policy tersebut. Contoh yang ada memfilter Pod dengan label "role=db". Sebuah podSelector yang empty akan memilih semua Pod yang ada di dalam namespace.

policyTypes: Setiap NetworkPolicy memiliki sebuah daftar policyTypes yang dapat berupa Ingress, Egress, atau keduanya. Field policyTypes mengindikasikan apakah suatu policy diberikan pada trafik ingress, egress, atau camputan ingress dan egress pada Pod tertentu. Jika tidak ada policyTypes tyang diberikan pada NetworkPolicy maka Ingress default akan diterapkan dan Egress akan diterapkan apabila policy tersebut memberikan spesifikasi egress.

ingress: Setiap NetworkPolicy bisa saja memberikan serangkaian whitelist rule-rule ingress. Setiap rule mengizinkan trafik yang sesuai dengan section from dan ports. Contoh policy yang diberikan memiliki sebuah rule, yang sesuai dengan trafik pada sebuah port single, bagian pertama dispesifikasikan melalui ipBlock, yang kedua melalui namespaceSelector dan yang ketiga melalui podSelector.

egress: Setiap NetworkPolicy bisa saja meliputi serangkaian whitelist rule-rule egress. Setiap rule mengizinkan trafik yang sesuai dengan section to dan ports. Contoh policy yang diberikan memiliki sebuah rule, yang sesuai dengan port single pada destinasi 10.0.0.0/24.

Pada contoh, NetworkPolicy melakukan hal berikut:

  1. Mengisolasi Pod-Pod dengan label "role=db" pada namespace "default" baik untuk ingress atau egress.

  2. (Rule Ingress) mengizinkan koneksi ke semua Pod pada namespace “default” dengan label “role=db” untuk protokol TCP port 6379 dari:

    • semua Pod pada namespace "default" dengan label "role=frontend"
    • semua Pod dalam sebuah namespace dengan label "project=myproject"
    • alamat IP pada range 172.17.0.0–172.17.0.255 dan 172.17.2.0–172.17.255.255 (yaitu, semua 172.17.0.0/16 kecuali 172.17.1.0/24)
  3. (Rule Egress) mengizinkan koneksi dari semua Pod pada namespace "default" dengan label "role=db" ke CIDR 10.0.0.0/24 untuk protokol TCP pada port 5978

Lihat mekanisme Deklarasi Network Policy untuk penjelasan lebih mendalam.

Perilaku selektor to dan from

Terdapat empat jenis selektor yang dapat dispesifikasikan dalam section ingress from atau section egress to:

podSelector: Ini digunakan untuk memfilter Pod tertentu pada namespace dimana NetworkPolicy berada yang akan mengatur destinasi ingress atau egress.

namespaceSelector: Ini digunakan untuk memfilter namespace tertentu dimana semua Pod diperbolehkan sebagai source ingress atau destinasi egress.

namespaceSelector and podSelector: Sebuah entri to/from yang memberikan spesifikasi namespaceSelector dan podSelector serta memilih Pod-Pod tertentu yang ada di dalam namespace. Pastikan kamu menggunakan sintaks YAML yang tepat; policy ini:

  ...
  ingress:
  - from:
    - namespaceSelector:
        matchLabels:
          user: alice
      podSelector:
        matchLabels:
          role: client
  ...

mengandung sebuah elemen from yang mengizinkan koneksi dari Pod-Pod dengan label role=client di namespace dengan label user=alice. Akan tetapi, policy ini:

  ...
  ingress:
  - from:
    - namespaceSelector:
        matchLabels:
          user: alice
    - podSelector:
        matchLabels:
          role: client
  ...

mengandung dua elemen pada array from, dan mengizinkan koneksi dari Pod pada Namespace lokal dengan label role=client, atau dari Pod di namespace apa pun dengan label user=alice.

Ketika kamu merasa ragu, gunakan kubectl describe untuk melihat bagaimana Kubernetes menginterpretasikan policy tersebut.

ipBlock: Ini digunakan untuk memilih range IP CIDR tertentu untuk berperan sebagai source ingress atau destinasi egress. Alamat yang digunakan harus merupakan alamat IP eksternal klaster, karena alamat IP Pod bersifat ephemeral dan tidak dapat ditebak.

Mekanisme ingress dan egress klaster seringkali membutuhkan mekanisme rewrite alamat IP source dan destinasi paket. Pada kasus-kasus dimana hal ini, tidak dapat dipastikan bahwa apakah hal ini terjadi sebelum atau setelah pemrosesan NetworkPolicy, dan perilaku yang ada mungkin saja berbeda untuk kombinasi plugin jaringan, penyedia layanan cloud, serta implementasi Service yang berbeda.

Pada ingress, artinya bisa saja kamu melakukan filter paket yang masuk berdasarkan source IP, sementara di kasus lain "source IP" yang digunakan oleh Network Policy adalah alamat IP LoadBalancer, node dimana Pod berada, dsb.

Pada egress, bisa saja sebuah koneksi dari Pod ke IP Service di-rewrite ke IP eksternal klaster atau bahkan tidak termasuk di dalam ipBlock policy.

Policy Default

Secara default, jika tidak ada policy yang ada dalam suatu namespace, maka semua trafik ingress dan egress yang diizinkan ke atau dari Pod dalam namespace. Contoh di bawah ini akan memberikan gambaran bagaimana kamu dapat mengubah perilaku default pada sebuah namespace.

Default: tolak semua trafik ingress

Kamu dapat membuat policy isolasi "default" untuk sebuah namespace dengan membuat sebuah NetworkPolicy yang memilih semua Pod tapi tidak mengizinkan trafik ingress masuk ke Pod-Pod tersebut.

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny
spec:
  podSelector: {}
  policyTypes:
  - Ingress

Hal ini menjamin bahwa bahkan Pod yang tidak dipilih oleh NetworkPolicy lain masih terisolasi. Policy ini tidak mengubah perilaku default dari egress.

Default: izinkan semua trafik ingress

Jika kamu ingin mengizinkan semua trafik ingress pada semua Pod dalam sebuah namespace (bahkan jika policy ditambahkan dan menyebabkan beberapa Pod menjadi terisolasi), kamu dapat secara eksplisit mengizinkan semua trafik bagi namespace tersebut.

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-all
spec:
  podSelector: {}
  ingress:
  - {}
  policyTypes:
  - Ingress

Default: tolak semua trafik egress

Kamu dapat membuat policy isolasi "default" untuk sebuah namespace dengan membuat sebuah NetworkPolicy yang memilih semua Pod tapi tidak mengizinkan trafik egress keluar dari Pod-Pod tersebut.

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny
spec:
  podSelector: {}
  policyTypes:
  - Egress

Hal ini menjamin bahwa bahkan Pod yang tidak dipilih oleh NetworkPolicy lain masih terisolasi. Policy ini tidak mengubah perilaku default dari ingress.

Default: izinkan semua trafik egress

Jika kamu ingin mengizinkan semua trafik egress pada semua Pod dalam sebuah namespace (bahkan jika policy ditambahkan dan menyebabkan beberapa Pod menjadi terisolasi), kamu dapat secara eksplisit mengizinkan semua trafik bagi namespace tersebut.

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-all
spec:
  podSelector: {}
  egress:
  - {}
  policyTypes:
  - Egress

Default: tolak semua trafik ingress dan egress

Kamu dapat membuat sebuah policy "default" jika kamu ingin menolak semua trafik ingress maupun egress pada semua Pod dalam sebuah namespace.

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny
spec:
  podSelector: {}
  policyTypes:
  - Ingress
  - Egress

Hal ini menjamin bahwa bahkan Pod yang tidak dipilih oleh NetworkPolicy tidak akan mengizinkan trafik ingress atau egress.

Dukungan terhadap SCTP

FEATURE STATE: Kubernetes v1.12 [alpha]

Kubernetes mendukung SCTP sebagai value protocol pada definisi NetworkPolicy sebagai fitur alpha. Untuk mengaktifkan fitur ini, administrator klaster harus mengaktifkan gerbang fitur SCTPSupport pada apiserver, contohnya “--feature-gates=SCTPSupport=true,...”. Ketika gerbang fitur ini diaktifkan, pengguna dapat menerapkan value dari field protocol pada NetworkPolicy menjadi SCTP. Kubernetes akan mengatur jaringan sesuai dengan SCTP, seperti halnya koneksi TCP.

Plugin CNI harus mendukung SCTP sebagai value dari protocol pada NetworkPolicy.

Selanjutnya

9 - Menambahkan Entry pada /etc/hosts Pod dengan HostAliases

Menambahkan entri pada berkas /etc/hosts Pod akan melakukan override resolusi hostname pada level Pod ketika DNS dan opsi lainnya tidak tersedia. Pada versi 1.7, pengguna dapat menambahkan entri yang diinginkan beserta field HostAliases pada PodSpec.

Modifikasi yang dilakukan tanpa menggunakan HostAliases tidaklah disarankan karena berkas ini diatur oleh Kubelet dan dapat di-override ketika Pod dibuat/di-restart.

Isi Default pada Berkas Hosts

Misalnya saja kamu mempunyai sebuah Pod Nginx yang memiliki sebuah IP Pod:

kubectl run nginx --image nginx --generator=run-pod/v1
pod/nginx created

Perhatikan IP Pod tersebut:

kubectl get pods --output=wide
NAME     READY     STATUS    RESTARTS   AGE    IP           NODE
nginx    1/1       Running   0          13s    10.200.0.4   worker0

File hosts yang ada akan tampak sebagai berikut:

kubectl exec nginx -- cat /etc/hosts
# Berkas hosts yang dikelola Kubernetes
127.0.0.1	localhost
::1	localhost ip6-localhost ip6-loopback
fe00::0	ip6-localnet
fe00::0	ip6-mcastprefix
fe00::1	ip6-allnodes
fe00::2	ip6-allrouters
10.200.0.4	nginx

Secara default, berkas hosts hanya berisikan boilerplate alamat IP IPv4 and IPv6 seperti localhost dan hostname dari Pod itu sendiri.

Menambahkan Entri Tambahan dengan HostAliases

Selain boilerplate default, kita dapat menambahkan entri pada berkas hosts untuk melakukan resolusi foo.local, bar.local pada 127.0.0.1 dan foo.remote, bar.remote pada 10.1.2.3, kita dapat melakukannya dengan cara menambahkan HostAliases pada Pod di bawah field .spec.hostAliases:

apiVersion: v1
kind: Pod
metadata:
  name: hostaliases-pod
spec:
  restartPolicy: Never
  hostAliases:
  - ip: "127.0.0.1"
    hostnames:
    - "foo.local"
    - "bar.local"
  - ip: "10.1.2.3"
    hostnames:
    - "foo.remote"
    - "bar.remote"
  containers:
  - name: cat-hosts
    image: busybox
    command:
    - cat
    args:
    - "/etc/hosts"

Pod ini kemudian dapat dihidupkan dengan perintah berikut:

kubectl apply -f hostaliases-pod.yaml
pod/hostaliases-pod created

Perhatikan IP dan status Pod tersebut:

kubectl get pod --output=wide
NAME                           READY     STATUS      RESTARTS   AGE       IP              NODE
hostaliases-pod                0/1       Completed   0          6s        10.200.0.5      worker0

File hosts yang ada akan tampak sebagai berikut:

kubectl logs hostaliases-pod
# Berkas hosts yang dikelola Kubernetes
127.0.0.1	localhost
::1	localhost ip6-localhost ip6-loopback
fe00::0	ip6-localnet
fe00::0	ip6-mcastprefix
fe00::1	ip6-allnodes
fe00::2	ip6-allrouters
10.200.0.5	hostaliases-pod

# Entries added by HostAliases.
127.0.0.1	foo.local	bar.local
10.1.2.3	foo.remote	bar.remote

Dengan tambahan entri yang telah dispesifikasikan sebelumnya.

Kenapa Kubelet Melakukan Mekanisme Manajemen Berkas Hosts?

Kubelet melakukan proses manajemen berkas hosts untuk setiap container yang ada pada Pod untuk mencegah Docker melakukan modifikasi pada berkas tersebut setelah kontainer dihidupkan.

Karena sifat dari berkas tersebut yang secara otomatis di-manage, semua hal yang didefinisikan oleh pengguna akan ditimpa (overwrite) ketika berkas hosts di-mount kembali oleh Kubelet ketika ada kontainer yang di-restart atau Pod di-schedule ulang. Dengan demikian tidak dianjurkan untuk memodifikasi berkas tersebut secara langsung.

10 - Dual-stack IPv4/IPv6

FEATURE STATE: Kubernetes v1.16 [alpha]

Dual-stack IPv4/IPv6 memungkinkan pengalokasian alamat IPv4 dan IPv6 untuk Pod dan Service.

Jika kamu mengaktifkan jaringan dual-stack IPv4/IPv6 untuk klaster Kubernetes kamu, klaster akan mendukung pengalokasian kedua alamat IPv4 dan IPv6 secara bersamaan.

Fitur-fitur yang didukung

Mengaktifkan dual-stack IPv4 / IPv6 pada klaster Kubernetes kamu untuk menyediakan fitur-fitur berikut ini:

  • Jaringan Pod dual-stack (pengalokasian sebuah alamat IPv4 dan IPv6 untuk setiap Pod)
  • Service yang mendukung IPv4 dan IPv6 (setiap Service hanya untuk satu keluarga alamat)
  • Perutean Pod ke luar klaster (misalnya Internet) melalui antarmuka IPv4 dan IPv6

Prasyarat

Prasyarat berikut diperlukan untuk menggunakan dual-stack IPv4/IPv6 pada klaster Kubernetes :

  • Kubernetes versi 1.16 atau yang lebih baru
  • Dukungan dari penyedia layanan untuk jaringan dual-stack (Penyedia layanan cloud atau yang lainnya harus dapat menyediakan antarmuka jaringan IPv4/IPv6 yang dapat dirutekan) untuk Node Kubernetes
  • Sebuah plugin jaringan yang mendukung dual-stack (seperti Kubenet atau Calico)
  • Kube-proxy yang berjalan dalam mode IPVS

Mengaktifkan dual-stack IPv4/IPv6

Untuk mengaktifkan dual-stack IPv4/IPv6, aktifkan gerbang fitur (feature gate) IPv6DualStack untuk komponen-komponen yang relevan dari klaster kamu, dan tetapkan jaringan dual-stack pada klaster:

  • kube-controller-manager:
    • --feature-gates="IPv6DualStack=true"
    • --cluster-cidr=<IPv4 CIDR>,<IPv6 CIDR> misalnya --cluster-cidr=10.244.0.0/16,fc00::/24
    • --service-cluster-ip-range=<IPv4 CIDR>,<IPv6 CIDR>
    • --node-cidr-mask-size-ipv4|--node-cidr-mask-size-ipv6 nilai bawaannya adalah /24 untuk IPv4 dan /64 untuk IPv6
  • kubelet:
    • --feature-gates="IPv6DualStack=true"
  • kube-proxy:
    • --proxy-mode=ipvs
    • --cluster-cidr=<IPv4 CIDR>,<IPv6 CIDR>
    • --feature-gates="IPv6DualStack=true"

Service

Jika klaster kamu mengaktifkan jaringan dual-stack IPv4/IPv6, maka kamu dapat membuat Service dengan alamat IPv4 atau IPv6. Kamu dapat memilih keluarga alamat untuk clusterIP
Service kamu dengan mengatur bagian, .spec.ipFamily, pada Service tersebut. Kamu hanya dapat mengatur bagian ini saat membuat Service baru. Mengatur bagian .spec.ipFamily bersifat opsional dan hanya boleh digunakan jika kamu berencana untuk mengaktifkan Service dan Ingress IPv4 dan IPv6 pada klaster kamu. Konfigurasi bagian ini bukanlah syarat untuk lalu lintas [egress] (#lalu-lintas-egress).

Kamu dapat mengatur .spec.ipFamily menjadi salah satu dari:

  • IPv4: Dimana server API akan mengalokasikan IP dari service-cluster-ip-range yaitu ipv4
  • IPv6: Dimana server API akan mengalokasikan IP dari service-cluster-ip-range yaitu ipv6

Spesifikasi Service berikut ini tidak memasukkan bagian ipFamily. Kubernetes akan mengalokasikan alamat IP (atau yang dikenal juga sebagai "cluster IP") dari service-cluster-ip-range yang dikonfigurasi pertama kali untuk Service ini.

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  selector:
    app: MyApp
  ports:
    - protocol: TCP
      port: 80
      targetPort: 9376

Spesifikasi Service berikut memasukkan bagian ipFamily. Sehingga Kubernetes akan mengalokasikan alamat IPv6 (atau yang dikenal juga sebagai "cluster IP") dari service-cluster-ip-range yang dikonfigurasi untuk Service ini.

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  ipFamily: IPv6
  selector:
    app: MyApp
  ports:
    - protocol: TCP
      port: 80
      targetPort: 9376

Sebagai perbandingan, spesifikasi Service berikut ini akan dialokasikan sebuah alamat IPv4 (atau yang dikenal juga sebagai "cluster IP") dari service-cluster-ip-range yang dikonfigurasi untuk Service ini.

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  ipFamily: IPv4
  selector:
    app: MyApp
  ports:
    - protocol: TCP
      port: 80
      targetPort: 9376

Tipe LoadBalancer

Penyedia layanan cloud yang mendukung IPv6 untuk pengaturan beban eksternal, Mengatur bagian type menjadi LoadBalancer sebagai tambahan terhadap mengatur bagian ipFamily menjadi IPv6 menyediakan sebuah cloud load balancer untuk Service kamu.

Lalu lintas egress

Penggunaan blok alamat IPv6 yang dapat dirutekan dan yang tidak dapat dirutekan secara publik diperbolehkan selama CNI dari penyedia layanan dapat mengimplementasikan transportasinya. Jika kamu memiliki Pod yang menggunakan IPv6 yang dapat dirutekan secara publik dan ingin agar Pod mencapai tujuan di luar klaster (misalnya Internet publik), kamu harus mengatur IP samaran untuk lalu lintas keluar dan balasannya. ip-masq-agent bersifat dual-stack aware, jadi kamu bisa menggunakan ip-masq-agent untuk masquerading IP dari klaster dual-stack.

Masalah-masalah yang diketahui

  • Kubenet memaksa pelaporan posisi IP untuk IPv4,IPv6 IP (--cluster-cidr)

Selanjutnya