Catatan : ini bukan artikel panduan lengkap, melainkan pengingat / petunjuk bagi mereka yang sudah menggunakan ConfigMap di Kubernetes atau hanya menyiapkan aplikasi mereka untuk bekerja di dalamnya.
Latar Belakang: Dari rsync ke ... Kubernetes
Apa yang terjadi sebelumnya? Di era "administrasi klasik", dalam versi yang paling sederhana, file konfigurasi ditempatkan tepat di sebelah aplikasi (atau di repositori, jika Anda mau). Sederhana: kami membuat pengiriman dasar (CD) untuk kode kami bersama dengan konfigurasi. Bahkan implementasi rsync bersyarat dapat disebut sebagai dasar CD.Ketika infrastruktur tumbuh, konfigurasi yang berbeda diperlukan untuk lingkungan yang berbeda (dev / stage / produksi). Aplikasi dilatih untuk memahami konfigurasi mana yang akan digunakan, meneruskannya sebagai argumen untuk memulai atau variabel lingkungan yang ditetapkan di lingkungan. Bahkan lebih banyak CD yang rumit dengan munculnya Chef / Wayang / Beraneka yang sangat berguna. Peran muncul di server, dan lingkungan tidak lagi dijelaskan di tempat yang berbeda - kita sampai pada IaC (Infrastruktur sebagai kode).Apa yang terjadi selanjutnya? Jika memungkinkan untuk melihat manfaat penting Kubernet untuk dirinya sendiri dan bahkan dapat menyesuaikan diri dengan kebutuhan untuk memodifikasi aplikasi agar berfungsi di lingkungan ini, migrasi akan terjadi. Dalam perjalanan, saya mengharapkan banyak nuansa dan perbedaan dalam konstruksi arsitektur, tetapi ketika saya berhasil mengatasi bagian utama, saya menjalankan aplikasi yang telah lama ditunggu-tunggu berjalan di K8s.Setelah di sini, kita masih bisa menggunakan konfigurasi yang disiapkan dalam repositori di sebelah aplikasi atau meneruskan ENV ke wadah. Namun, selain metode ini, ConfigMaps juga tersedia . Primitif K8 ini memungkinkan Anda untuk menggunakan templat Go di konfigurasi, mis. membuat mereka seperti halaman HTML dan memuat ulang aplikasi saat mengubah konfigurasi tanpa restart. Dengan ConfigMaps, tidak perlu lagi menyimpan 3+ konfigurasi untuk lingkungan yang berbeda dan melacak relevansi masing-masing.Pengantar umum untuk ConfigMaps dapat ditemukan, misalnya, di sini . Dan dalam artikel ini saya akan fokus pada beberapa fitur bekerja dengan mereka.ConfigMaps Sederhana
Seperti apa konfigurasi pada Kubernetes? Apa yang mereka dapatkan dari templat go? Misalnya, berikut adalah ConfigMap biasa untuk aplikasi yang digunakan dari grafik Helm:apiVersion: v1
kind: ConfigMap
metadata:
name: app
data:
config.json: |
{
"welcome": {{ pluck .Values.global.env .Values.welcome | quote }},
"name": {{ pluck .Values.global.env .Values.name | quote }}
}
Di sini nilai yang diganti .Values.welcome
dan .Values.name
akan diambil dari file values.yaml
. Kenapa tepatnya dari mana values.yaml
? Bagaimana cara kerja mesin templat Go? Kami sudah membicarakan detail ini lebih terinci di sini .Panggilan pluck
membantu untuk memilih jalur yang diperlukan dari peta:$ cat .helm/values.yaml
welcome:
production: "Hello"
test: "Hey"
name:
production: "Bob"
test: "Mike"
Selain itu, Anda dapat mengambil kedua garis khusus dan seluruh fragmen konfigurasi.Misalnya, ConfigMap mungkin seperti ini:data:
config.json: |
{{ pluck .Values.global.env .Values.data | first | toJson | indent 4 }}
... dan di values.yaml
- isi berikut:data:
production:
welcome: "Hello"
name: "Bob"
Yang terlibat di sini global.env
adalah nama lingkungan. Mengganti nilai ini selama penyebaran, Anda dapat merender ConfigMaps dengan konten yang berbeda. first
diperlukan di sini karena pluck
mengembalikan daftar, elemen pertama yang berisi nilai yang diinginkan.Ketika ada banyak konfigurasi
Satu ConfigMap dapat berisi beberapa file konfigurasi:data:
config.json: |
{
"welcome": {{ pluck .Values.global.env .Values.welcome | first | quote }},
"name": {{ pluck .Values.global.env .Values.name | first | quote }}
}
database.yml: |
host: 127.0.0.1
db: app
user: app
password: app
Anda bahkan dapat memasang setiap konfigurasi secara terpisah: volumeMounts:
- name: app-conf
mountPath: /app/configfiles/config.json
subPath: config.json
- name: app-conf
mountPath: /app/configfiles/database.yml
subPath: database.yml
... atau ambil semua konfigurasi sekaligus dengan direktori: volumeMounts:
- name: app-conf
mountPath: /app/configfiles
Jika Anda mengubah deskripsi sumber daya Penempatan selama penyebaran, Kubernetes akan membuat ReplicaSet baru, mengurangi yang lama ke 0 dan meningkatkan yang baru ke jumlah replika yang ditentukan. (Ini berlaku untuk strategi penyebaran RollingUpdate
.)Tindakan semacam itu akan mengarah pada penciptaan kembali pod dengan deskripsi baru. Misalnya: ada gambar image:my-registry.example.com:v1
, tetapi menjadi - image:my-registry.example.com:v2
. Dan sama sekali tidak masalah apa yang sebenarnya kita ubah dalam deskripsi Penempatan kami: yang utama adalah hal ini menyebabkan replikaSet (dan, sebagai hasilnya, pod) diciptakan kembali. Dalam hal ini, versi baru file konfigurasi di versi baru aplikasi secara otomatis dipasang dan tidak akan ada masalah.ConfigMap Change Response
Dalam hal perubahan dalam ConfigMap, empat skenario acara dapat mengikuti. Pertimbangkan mereka:- : ConfigMap, subPath.
: . - : ConfigMap, pod.
: pod . - : ConfigMap Deployment -.
: , ConfigMap’, Deployment, pod , — . - : ConfigMap, .
: pod’ / pod’.
Kami akan menganalisis lebih detail.skenario 1
Kami hanya memperbaiki ConfigMap? Aplikasi tidak akan memulai ulang. Dalam hal pemasangan oleh, subPath
tidak akan ada perubahan sampai pod dimulai ulang secara manual.Semuanya sederhana: Kubernet me-mount ConfigMap kami di pod versi sumber daya yang spesifik. Karena sudah di-mount dengan subPath
, tidak ada "pengaruh" tambahan pada konfigurasi tidak lagi disediakan.Skenario 2
Tidak dapat memperbarui file tanpa membuat ulang pod? Oke, kami memiliki 6 replika di Deployment, sehingga kami dapat bergantian melakukan semuanya secara manual delete pod
. Kemudian ketika membuat pod baru, mereka akan “mengambil” versi baru ConfigMap.Skenario 3
Bosan melakukan operasi seperti itu secara manual? Solusi untuk masalah ini dijelaskan dalam tips dan trik Helm :kind: Deployment
spec:
template:
metadata:
annotations:
checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }}
[...]
Dengan demikian, spec.template
hash dari konfigurasi anotasi yang diberikan hanya ditulis ke template pod ( ).Anotasi adalah bidang nilai kunci yang berubah-ubah tempat Anda dapat menyimpan nilai-nilai Anda. Jika Anda mendaftarkannya di templat spec.template
pod masa depan, bidang ini akan jatuh ke dalam ReplicaSet dan pod itu sendiri. Kubernetes akan melihat bahwa template pod telah berubah (karena konfigurasi sha256 telah berubah) dan akan mulai RollingUpdate
, di mana tidak ada yang berubah kecuali penjelasan ini.Sebagai hasilnya, kami menyimpan versi aplikasi dan uraian Deployment yang sama dan pada dasarnya hanya memicu pembuatan ulang pod oleh mesin - mirip dengan cara kami melakukannya secara manual kubectl delete
, tetapi sudah “benar”: secara otomatis dan dengan RollingUpdate
.Skenario 4
Mungkin aplikasi sudah tahu cara memantau perubahan dalam konfigurasi dan secara otomatis memuat ulang? Di sinilah letak fitur penting dari ConfigMaps ...Di Kubernetes, jika config di-mount dengannya subPath
, itu tidak akan diperbarui sampai pod dimulai ulang (lihat tiga skenario pertama yang dibahas di atas) . Tetapi jika Anda memasang ConfigMap sebagai direktori, tanpa subPath
, maka di dalam wadah akan ada direktori dengan konfigurasi yang diperbarui tanpa memulai ulang pod.Ada fitur lain yang berguna untuk diingat:- File konfigurasi yang diperbarui di dalam wadah diperbarui dengan penundaan. Ini karena fakta bahwa file tersebut tidak di-mount dengan tepat, tetapi objek Kubernetes.
- File di dalamnya adalah symlink. Contoh dengan
subPath
:
$ kubectl -n production exec go-conf-example-6b4cb86569-22vqv -- ls -lha /app/configfiles
total 20K
drwxr-xr-x 1 root root 4.0K Mar 3 19:34 .
drwxr-xr-x 1 app app 4.0K Mar 3 19:34 ..
-rw-r--r-- 1 root root 42 Mar 3 19:34 config.json
-rw-r--r-- 1 root root 47 Mar 3 19:34 database.yml
Dan apa yang akan terjadi tanpa subPath
ketika dipasang oleh direktori?
$ kubectl -n production exec go-conf-example-67c768c6fc-ccpwl -- ls -lha /app/configfiles
total 12K
drwxrwxrwx 3 root root 4.0K Mar 3 19:40 .
drwxr-xr-x 1 app app 4.0K Mar 3 19:34 ..
drwxr-xr-x 2 root root 4.0K Mar 3 19:40 ..2020_03_03_16_40_36.675612011
lrwxrwxrwx 1 root root 31 Mar 3 19:40 ..data -> ..2020_03_03_16_40_36.675612011
lrwxrwxrwx 1 root root 18 Mar 3 19:40 config.json -> ..data/config.json
lrwxrwxrwx 1 root root 19 Mar 3 19:40 database.yml -> ..data/database.yml
Perbarui konfigurasi (via deploy atau kubectl edit
), tunggu 2 menit (waktu caching apiserver) - dan voila:
$ kubectl -n production exec go-conf-example-67c768c6fc-ccpwl -- ls -lha --color /app/configfiles
total 12K
drwxrwxrwx 3 root root 4.0K Mar 3 19:44 .
drwxr-xr-x 1 app app 4.0K Mar 3 19:34 ..
drwxr-xr-x 2 root root 4.0K Mar 3 19:44 ..2020_03_03_16_44_38.763148336
lrwxrwxrwx 1 root root 31 Mar 3 19:44 ..data -> ..2020_03_03_16_44_38.763148336
lrwxrwxrwx 1 root root 18 Mar 3 19:40 config.json -> ..data/config.json
lrwxrwxrwx 1 root root 19 Mar 3 19:40 database.yml -> ..data/database.yml
Perhatikan cap waktu yang diubah di direktori yang dibuat oleh Kubernetes.
Ubah pelacakan
Dan akhirnya - contoh sederhana tentang bagaimana Anda dapat memantau perubahan dalam konfigurasi.Kami akan menggunakan aplikasi-Go semacam itupackage main
import (
"encoding/json"
"fmt"
"log"
"os"
"time"
"github.com/fsnotify/fsnotify"
)
type Config struct {
Welcome string `json:"welcome"`
Name string `json:"name"`
}
var (
globalConfig *Config
)
func LoadConfig(path string) (*Config, error) {
configFile, err := os.Open(path)
if err != nil {
return nil, fmt.Errorf("Unable to read configuration file %s", path)
}
config := new(Config)
decoder := json.NewDecoder(configFile)
err = decoder.Decode(&config)
if err != nil {
return nil, fmt.Errorf("Unable to parse configuration file %s", path)
}
return config, nil
}
func ConfigWatcher() {
watcher, err := fsnotify.NewWatcher()
if err != nil {
log.Fatal(err)
}
defer watcher.Close()
done := make(chan bool)
go func() {
for {
select {
case event, ok := <-watcher.Events:
if !ok {
return
}
log.Println("event:", event)
if event.Op&fsnotify.Write == fsnotify.Write {
log.Println("modified file:", event.Name)
}
globalConfig, _ = LoadConfig("./configfiles/config.json")
log.Println("config:", globalConfig)
case err, ok := <-watcher.Errors:
if !ok {
return
}
log.Println("error:", err)
}
}
}()
err = watcher.Add("./configfiles/config.json")
if err != nil {
log.Fatal(err)
}
<-done
}
func main() {
log.Println("Start")
globalConfig, _ = LoadConfig("./configfiles/config.json")
go ConfigWatcher()
for {
log.Println("config:", globalConfig)
time.Sleep(30 * time.Second)
}
}
... menambahkannya dengan konfigurasi seperti itu:$ cat configfiles/config.json
{
"welcome": "Hello",
"name": "Alice"
}
Jika dijalankan, lognya adalah:2020/03/03 22:18:22 config: &{Hello Alice}
2020/03/03 22:18:52 config: &{Hello Alice}
Dan sekarang kita akan menginstal aplikasi ini di Kubernetes, setelah memasang konfigurasi ConfigMap di pod alih-alih file dari gambar. Contoh grafik Helm telah disiapkan di GitHub :helm install -n habr-configmap --namespace habr-configmap ./habr-configmap --set 'name.production=Alice' --set 'global.env=production'
Dan ubah hanya ConfigMap:- production: "Alice"
+ production: "Bob"
Perbarui grafik Helm di cluster, misalnya, seperti ini:helm upgrade habr-configmap ./habr-configmap --set 'name.production=Bob' --set 'global.env=production'
Apa yang akan terjadi?Anda dapat melihat bagaimana situasi serupa - melacak perubahan ConfigMap - diselesaikan dalam proyek yang lebih dewasa (dan hanya "nyata"), di sini .Penting! Penting juga untuk mengingat bahwa semua hal di atas dalam artikel ini juga berlaku untuk Secret'ov di Kubernetes ( kind: Secret
): bukan tidak ada artinya mereka sangat mirip dengan ConfigMap ...Bonus! Solusi Pihak Ketiga
Jika Anda tertarik dengan topik pelacakan perubahan di konfigurasi, sudah ada utilitas siap pakai untuk ini:- jimmidyson / configmap-reload - Mengirim permintaan HTTP jika file telah berubah. Pengembang juga berencana untuk mengajarkan SIGHUP untuk mengirim, tetapi kurangnya komitmen sejak Oktober 2019 membuat rencana ini dipertanyakan;
- stakater / Reloader - memonitor ConfigMap / Secrets dan melakukan upgrade bergulir (sebagaimana penulisnya menyebutnya) pada sumber daya yang terkait dengannya.
Akan lebih mudah untuk meluncurkan aplikasi seperti itu dengan wadah sespan untuk aplikasi yang sudah ada. Namun, jika Anda mengetahui fitur Kubernetes / ConfigMap dan konfigurasi untuk diedit tidak "langsung" (melalui edit
), tetapi hanya sebagai bagian dari penyebaran ... maka kemampuan utilitas tersebut mungkin tampak berlebihan, mis. menduplikasi fungsi dasar.Kesimpulan
Dengan munculnya ConfigMap di Kubernetes, konfigurasi pindah ke babak pengembangan selanjutnya: penggunaan mesin template memberi mereka fleksibilitas yang sebanding dengan rendering halaman HTML. Untungnya, komplikasi seperti itu tidak menggantikan solusi yang ada, tetapi menjadi pelengkap mereka . Oleh karena itu, untuk administrator (atau lebih tepatnya, bahkan pengembang) yang menganggap fitur baru berlebihan, file lama yang baik masih tersedia.Bagi mereka yang sudah menggunakan ConfigMaps atau hanya melihatnya, artikel ini memberikan gambaran singkat tentang esensi mereka dan nuansa penggunaan. Jika Anda memiliki tips & trik Anda sendiri pada topik - Saya akan senang melihat di komentar.PS
Baca juga di blog kami: