Dari kehidupan dengan Kubernetes: Bagaimana kami menghapus DBMS (dan tidak hanya) dari lingkungan ulasan menjadi statis



Catatan : artikel ini tidak mengklaim sebagai praktik terbaik. Ini menggambarkan pengalaman implementasi spesifik tugas infrastruktur dalam hal menggunakan Kubernetes dan Helm, yang dapat berguna dalam memecahkan masalah terkait.

Menggunakan lingkungan ulasan di CI / CD bisa sangat berguna, baik untuk pengembang dan insinyur sistem. Pertama-tama mari kita menyinkronkan ide-ide umum tentang mereka:

  1. Lingkungan tinjauan dapat dibuat dari cabang terpisah di repositori Git yang didefinisikan oleh pengembang (yang disebut cabang fitur).
  2. Mereka dapat memiliki instance DBMS yang terpisah, prosesor antrian, layanan caching, dll. - secara umum, semuanya untuk reproduksi penuh dari lingkungan produksi.
  3. Mereka memungkinkan pengembangan paralel, secara signifikan mempercepat pelepasan fitur baru dalam aplikasi. Pada saat yang sama, lusinan lingkungan seperti itu mungkin diperlukan setiap hari, itulah sebabnya kecepatan penciptaannya sangat penting.

Di persimpangan titik kedua dan ketiga, kesulitan sering muncul: karena infrastruktur sangat berbeda, komponennya dapat digunakan untuk waktu yang lama. Waktu yang dihabiskan ini, misalnya, termasuk mengembalikan database dari cadangan yang sudah disiapkan *. Artikel ini adalah tentang cara menarik yang pernah kami lakukan untuk menyelesaikan masalah seperti itu.

* Ngomong-ngomong, secara khusus tentang dump database besar dalam konteks ini, kami sudah menulis di materi tentang mempercepat database bootstrap .)

Masalah dan cara mengatasinya


Dalam salah satu proyek, kami diberi tugas "menciptakan titik masuk tunggal untuk pengembang dan insinyur QA." Formulasi ini menyembunyikan secara teknis hal berikut:

  1. Untuk menyederhanakan pekerjaan QA-engineer dan beberapa karyawan lainnya, keluarkan semua basis data (dan vhosts terkait) yang digunakan dalam tinjauan, dalam lingkungan statis - terpisah. Untuk alasan yang berlaku dalam proyek ini, cara berinteraksi dengan mereka adalah optimal.
  2. Kurangi waktu yang diperlukan untuk membuat lingkungan ulasan. Seluruh proses penciptaan mereka dari awal tersirat, yaitu. termasuk kloning basis data, migrasi, dll.

Dari sudut pandang implementasi, masalah utama bermuara pada memastikan idempotensi saat membuat dan menghapus lingkungan ulasan. Untuk mencapai ini, kami mengubah mekanisme untuk menciptakan lingkungan ulasan dengan memigrasi layanan PostgreSQL, MongoDB, dan RabbitMQ terlebih dahulu ke lingkungan statis. Statis mengacu pada lingkungan "permanen" yang tidak akan dibuat atas permintaan pengguna (seperti halnya dengan lingkungan ulasan).

Penting! Pendekatan dengan lingkungan statis jauh dari ideal - untuk kekurangan spesifiknya, lihat bagian akhir artikel. Namun, kami membagikan pengalaman ini secara terperinci, karena dapat lebih atau kurang berlaku dalam tugas-tugas lain, dan pada saat yang sama berfungsi sebagai argumen saat membahas masalah desain infrastruktur.

Jadi, urutan tindakan dalam implementasi:

  • Saat membuat lingkungan ulasan, berikut ini harus terjadi sekali: pembuatan database di dua DBMS (MongoDB dan PostgreSQL), pemulihan database dari cadangan / templat, dan pembuatan vhost di RabbitMQ. Ini akan membutuhkan cara yang nyaman untuk memuat dump saat ini. (Jika Anda pernah memiliki lingkungan ulasan sebelumnya, kemungkinan besar Anda sudah memiliki solusi yang siap pakai untuk ini.)
  • Setelah menyelesaikan lingkungan ulasan, Anda harus menghapus database dan host virtual di RabbitMQ.

Dalam kasus kami, infrastruktur beroperasi dalam kerangka Kubernetes (menggunakan Helm). Oleh karena itu, untuk pelaksanaan tugas-tugas di atas, kait Helm sangat baik . Mereka dapat dilakukan baik sebelum penciptaan semua komponen lain dalam rilis Helm, dan / atau setelah mereka dihapus. Karena itu:

  • untuk tugas inisialisasi, kami akan menggunakan pengait pre-installuntuk meluncurkannya sebelum membuat semua sumber daya dalam rilis;
  • untuk tugas hapus, sebuah kait post-delete.

Mari beralih ke detail implementasi.

Implementasi praktis


Dalam versi aslinya, proyek ini hanya menggunakan satu Pekerjaan, yang terdiri dari tiga kontainer. Tentu saja, ini tidak sepenuhnya nyaman, karena hasilnya adalah manifes besar yang sulit dibaca. Karena itu, kami membaginya menjadi tiga pekerjaan kecil.

Berikut ini adalah daftar untuk PostgreSQL, dan dua lainnya (MongoDB dan RabbitMQ) identik dalam struktur manifes:

{{- if .Values.global.review }}
---
apiVersion: batch/v1
kind: Job
metadata:
  name: db-create-postgres-database
  annotations:
    "helm.sh/hook": "pre-install"
    "helm.sh/hook-weight": "5"
spec:
  template:
    metadata:
      name: init-db-postgres
    spec:
      volumes:
      - name: postgres-scripts
        configMap:
          defaultMode: 0755
          name: postgresql-configmap
      containers:
      - name: init-postgres-database
        image: private-registry/postgres 
        command: ["/docker-entrypoint-initdb.d/01-review-load-dump.sh"]
        volumeMounts:
        - name: postgres-scripts
          mountPath: /docker-entrypoint-initdb.d/01-review-load-dump.sh
          subPath: review-load-dump.sh
        env:
{{- include "postgres_env" . | indent 8 }}
      restartPolicy: Never
{{- end }}

Komentar pada isi manifes:

  1. Job review-. review CI/CD Helm- (. if .Values.global.review ).
  2. Job β€” , ConfigMap. , , . , hook-weight.
  3. cURL , PostgreSQL, .
  4. PostgreSQL : , shell- .

PostgreSQL


Yang paling menarik adalah di skrip shell ( review-load-dump.sh) yang telah disebutkan dalam daftar . Apa opsi umum untuk memulihkan database di PostgreSQL?

  1. Pemulihan "Standar" dari cadangan;
  2. Pemulihan menggunakan templat .

Dalam kasus kami, perbedaan antara kedua pendekatan terutama dalam kecepatan membuat database untuk lingkungan baru. Yang pertama - kita memuat dump database dan mengembalikannya dengan pg_restore. Dan bersama kami ini terjadi lebih lambat dari metode kedua, sehingga pilihan yang sesuai dibuat.

Menggunakan opsi kedua ( pemulihan dengan templat) Anda dapat mengkloning basis data pada tingkat fisik tanpa mengirim data ke dalamnya dari wadah di lingkungan lain - ini mengurangi waktu pemulihan. Namun, ada batasan: Anda tidak dapat mengkloning database yang koneksi aktifnya tetap. Karena kita menggunakan stage sebagai lingkungan statis (dan bukan lingkungan ulasan terpisah), kita perlu membuat database kedua dan mengubahnya menjadi templat, memperbaruinya setiap hari (misalnya, di pagi hari). CronJob kecil disiapkan untuk ini:

---
apiVersion: batch/v1beta1
kind: CronJob
metadata:
  name: update-postgres-template
spec:
  schedule: "50 4 * * *"
  concurrencyPolicy: Forbid
  successfulJobsHistoryLimit: 3
  failedJobsHistoryLimit: 3
  startingDeadlineSeconds: 600
  jobTemplate:
    spec:
      template:
        spec:
          restartPolicy: Never
          imagePullSecrets:
          - name: registrysecret
          volumes:
          - name: postgres-scripts
            configMap:
              defaultMode: 0755
              name: postgresql-configmap-update-cron
          containers:
          - name: cron
            command: ["/docker-entrypoint-initdb.d/update-postgres-template.sh"]
          image: private-registry/postgres 
            volumeMounts:
            - name: postgres-scripts
              mountPath: /docker-entrypoint-initdb.d/update-postgres-template.sh
              subPath: update-postgres-template.sh
            env:
{{- include "postgres_env" . | indent 8 }}

Manifes ConfigMap lengkap yang berisi skrip kemungkinan besar tidak masuk akal (laporkan dalam komentar jika ini bukan masalahnya). Sebagai gantinya, saya akan memberikan hal yang paling penting - skrip bash:

#!/bin/bash -x

CREDENTIALS="postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@${POSTGRES_HOST}/postgres"

psql -d "${CREDENTIALS}" -w -c "REVOKE CONNECT ON DATABASE ${POSTGRES_DB_TEMPLATE} FROM public"
psql -d "${CREDENTIALS}" -w -c "SELECT pg_terminate_backend(pg_stat_activity.pid) FROM pg_stat_activity WHERE pg_stat_activity.datname = '${POSTGRES_DB_TEMPLATE}'"

curl --fail -vsL ${HOST_FORDEV}/latest_${POSTGRES_DB_STAGE}.psql -o /tmp/${POSTGRES_DB}.psql

psql -d "${CREDENTIALS}" -w -c "ALTER DATABASE ${POSTGRES_DB_TEMPLATE} WITH is_template false allow_connections true;"
psql -d "${CREDENTIALS}" -w -c "DROP DATABASE ${POSTGRES_DB_TEMPLATE};" || true
psql -d "${CREDENTIALS}" -w -c "CREATE DATABASE ${POSTGRES_DB_TEMPLATE};" || true
pg_restore -U ${POSTGRES_USER} -h ${POSTGRES_HOST} -w -j 4 -d ${POSTGRES_DB_TEMPLATE} /tmp/${POSTGRES_DB}.psql

psql -d "${CREDENTIALS}" -w -c "ALTER DATABASE ${POSTGRES_DB_TEMPLATE} WITH is_template true allow_connections false;"

rm -v /tmp/${POSTGRES_DB}.psql

Anda dapat memulihkan beberapa database sekaligus dari satu templat tanpa konflik. Yang utama adalah bahwa koneksi database harus dilarang, dan database itu sendiri harus berupa template. Ini dilakukan pada langkah kedua dari belakang.

Manifes yang berisi skrip shell untuk memulihkan database ternyata seperti ini:

---
apiVersion: v1
kind: ConfigMap
metadata:
  name: postgresql-configmap
  annotations:
    "helm.sh/hook": "pre-install"
    "helm.sh/hook-weight": "1"
    "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded
data:
  review-load-dump.sh: |
    #!/bin/bash -x
    
 
 
    CREDENTIALS="postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@${POSTGRES_HOST}/postgres"

    if [ "$( psql -d "${CREDENTIALS}" -tAc "SELECT CASE WHEN EXISTS (SELECT * FROM pg_stat_activity WHERE datname = '${POSTGRES_DB}' LIMIT 1) THEN 1 ELSE 0 END;" )" = '1' ]
      then
          echo "Open connections has been found in ${POSTGRES_DB} database, will drop them"
          psql -d "${CREDENTIALS}" -c "SELECT pg_terminate_backend(pg_stat_activity.pid) FROM pg_stat_activity WHERE pg_stat_activity.datname = '${POSTGRES_DB}' -- AND pid <> pg_backend_pid();"
      else
          echo "No open connections has been found ${POSTGRES_DB} database, skipping this stage"
    fi

    psql -d "${CREDENTIALS}" -c "DROP DATABASE ${POSTGRES_DB}"

    if [ "$( psql -d "${CREDENTIALS}" -tAc "SELECT 1 FROM pg_database WHERE datname='${POSTGRES_DB}'" )" = '1' ]
      then
          echo "Database ${POSTGRES_DB} still exists, delete review job failed"
          exit 1
      else
          echo "Database ${POSTGRES_DB} does not exist, skipping"
    fi


    psql ${CREDENTIALS} -d postgres -c 'CREATE DATABASE ${POSTGRES_DB} TEMPLATE "loot-stage-copy"'

Rupanya, mereka terlibat di sini hook-delete-policy. Rincian tentang penerapan kebijakan ini ditulis di sini . Dalam manifes yang diberikan kami menggunakan before-hook-creation,hook-succeededyang memungkinkan untuk memenuhi persyaratan berikut: hapus objek sebelumnya sebelum membuat hook baru dan hapus hanya ketika hook berhasil.

Kami akan menghapus basis data dalam ConfigMap ini:

---
apiVersion: v1
kind: ConfigMap
metadata:
  name: postgresql-configmap-on-delete
  annotations:
    "helm.sh/hook": "post-delete, pre-delete"
    "helm.sh/hook-weight": "1"
    "helm.sh/hook-delete-policy": before-hook-creation
data:
  review-delete-db.sh: |
    #!/bin/bash -e

    CREDENTIALS="postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@${POSTGRES_HOST}/postgres"

    psql -d "${CREDENTIALS}" -w postgres -c "DROP DATABASE ${POSTGRES_DB}"

Meskipun kami memindahkannya ke ConfigMap yang terpisah, ia dapat ditempatkan di ConfigMap biasa command. Bagaimanapun, itu dapat dibuat satu garis tanpa menyulitkan penampilan manifes itu sendiri.

Jika opsi dengan templat PostgreSQL karena alasan tertentu tidak sesuai atau tidak cocok, Anda dapat kembali ke jalur pemulihan "standar" yang disebutkan di atas menggunakan cadangan . Algoritma akan sepele:

  1. Setiap malam, cadangan database dibuat sehingga dapat diunduh dari jaringan lokal cluster.
  2. Pada saat pembuatan lingkungan ulasan, database dimuat dan dipulihkan dari dump.
  3. Ketika dump dikerahkan, semua tindakan lain dilakukan.

Dalam hal ini, skrip pemulihan akan menjadi kira-kira sebagai berikut:

---
apiVersion: v1
kind: ConfigMap
metadata:
  name: postgresql-configmap
  annotations:
    "helm.sh/hook": "pre-install"
    "helm.sh/hook-weight": "1"
    "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded
data:
  review-load-dump.sh: |
    #!/bin/bash -x

    CREDENTIALS="postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@${POSTGRES_HOST}/postgres"
    psql -d "${CREDENTIALS}" -w -c "DROP DATABASE ${POSTGRES_DB}" || true
    psql -d "${CREDENTIALS}" -w -c "CREATE DATABASE ${POSTGRES_DB}"

    curl --fail -vsL ${HOST_FORDEV}/latest_${POSTGRES_DB_STAGE}.psql -o /tmp/${POSTGRES_DB}.psql

    psql psql -d "${CREDENTIALS}" -w -c "CREATE EXTENSION ip4r;"
    pg_restore -U ${POSTGRES_USER} -h ${POSTGRES_HOST} -w -j 4 -d ${POSTGRES_DB} /tmp/${POSTGRES_DB}.psql
    rm -v /tmp/${POSTGRES_DB}.psql

Prosedur sesuai dengan apa yang telah dijelaskan di atas. Satu-satunya perubahan adalah penghapusan file psql setelah semua pekerjaan ditambahkan.

Catatan : baik dalam skrip pemulihan dan skrip uninstall, database dihapus setiap waktu. Hal ini dilakukan untuk menghindari kemungkinan konflik selama pembuatan ulang ulasan: Anda perlu memastikan bahwa database benar-benar dihapus. Juga, masalah ini berpotensi dapat diselesaikan dengan menambahkan flag --cleandi utilitas pg_restore, tetapi hati-hati: flag ini membersihkan data hanya dari elemen-elemen yang ada di dump itu sendiri, jadi dalam kasus kami opsi ini tidak berfungsi.

Akibatnya, kami mendapat mekanisme kerja yang membutuhkan perbaikan lebih lanjut (hingga mengganti skrip Bash dengan kode yang lebih elegan). Kami akan meninggalkan mereka di luar ruang lingkup artikel (walaupun komentar pada topik, tentu saja, selamat datang).

Mongodb


Komponen selanjutnya adalah MongoDB. Kesulitan utama dengan itu adalah bahwa untuk DBMS ini, opsi untuk menyalin database (seperti dalam PostgreSQL) ada agak nominal, karena:

  1. Dia dalam kondisi usang .
  2. Menurut hasil pengujian kami, kami tidak menemukan perbedaan besar dalam waktu pemulihan basis data dibandingkan dengan yang biasa mongo_restore. Namun, saya perhatikan bahwa pengujian dilakukan sebagai bagian dari satu proyek - dalam kasus Anda, hasilnya bisa sangat berbeda.

Ternyata dalam kasus volume basis data yang besar, masalah serius dapat muncul: kami menghemat waktu untuk memulihkan basis data di PgSQL, tetapi pada saat yang sama mengembalikan dump di Mongo untuk waktu yang sangat lama. Pada saat penulisan, dan dalam kerangka infrastruktur yang ada, kami melihat tiga cara (omong-omong, mereka dapat digabungkan):

  1. Pemulihan bisa memakan waktu lama, misalnya, jika DBMS Anda berada di sistem file jaringan (untuk kasus-kasus yang tidak dengan lingkungan produksi). Kemudian Anda cukup mentransfer DBMS dari stage ke node yang terpisah dan menggunakan penyimpanan lokal. Karena ini bukan produksi, kecepatan membuat ulasan lebih penting bagi kami.
  2. Anda dapat memindahkan setiap pemulihan Pekerjaan ke pod yang terpisah, memungkinkan Anda untuk melakukan pra-eksekusi migrasi dan proses lain yang bergantung pada DBMS. Jadi kami menghemat waktu dengan menyelesaikannya terlebih dahulu.
  3. Kadang-kadang Anda dapat mengurangi ukuran dump dengan menghapus data lama / tidak relevan - sampai pada titik itu cukup untuk hanya meninggalkan struktur database. Tentu saja, ini bukan untuk kasus-kasus ketika dump penuh diperlukan (katakanlah, untuk tugas pengujian QA).

Jika Anda tidak perlu membuat lingkungan ulasan dengan cepat, maka semua kesulitan yang dijelaskan dapat diabaikan.

Kami, karena tidak dapat menyalin basis data yang mirip dengan PgSQL, akan pergi dengan cara pertama, yaitu pemulihan standar dari cadangan. Algoritmanya sama dengan PgSQL. Ini mudah dilihat jika Anda melihat manifestonya:

---
apiVersion: v1
kind: ConfigMap
metadata:
  name: mongodb-scripts-on-delete
  annotations:
    "helm.sh/hook": "post-delete, pre-delete"
    "helm.sh/hook-weight": "1"
    "helm.sh/hook-delete-policy": before-hook-creation
data:
  review-delete-db.sh: |
    #!/bin/bash -x

    mongo ${MONGODB_NAME} --eval "db.dropDatabase()" --host ${MONGODB_REPLICASET}/${MONGODB_HOST}
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: mongodb-scripts
  annotations:
    "helm.sh/hook": "pre-install"
    "helm.sh/hook-weight": "1"
    "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded
data:
  review-load-dump.sh: |
    #!/bin/bash -x

    curl --fail -vsL ${HOST_FORDEV}/latest_${MONGODB_NAME_STAGE}.gz -o /tmp/${MONGODB_NAME}.gz

    mongo ${MONGODB_NAME} --eval "db.dropDatabase()" --host ${MONGODB_REPLICASET}/${MONGODB_HOST}
    mongorestore --gzip --nsFrom "${MONGODB_NAME_STAGE}.*" --nsTo "${MONGODB_NAME}.*" --archive=/tmp/${MONGODB_NAME}.gz --host ${MONGODB_REPLICASET}/${MONGODB_HOST}

Ada detail penting di sini. Dalam kasus kami, MongoDB ada di gugus dan Anda perlu memastikan bahwa koneksi selalu terjadi pada simpul Primer . Jika Anda menentukan, misalnya, host pertama dalam kuorum, maka setelah beberapa waktu host tersebut dapat beralih dari Utama ke Sekunder, yang akan mencegah pembuatan database. Oleh karena itu, Anda harus terhubung bukan ke satu host, tetapi segera ke ReplicaSet , mendaftar semua host di dalamnya. Untuk alasan ini saja, Anda perlu menjadikan MongoDB sebagai StatefulSet sehingga nama host selalu sama (belum lagi bahwa MongoDB adalah aplikasi stateful secara alami). Dalam opsi ini, Anda dijamin untuk terhubung ke simpul Utama.

Untuk MongoDB, kami juga menghapus database sebelum membuat ulasan - ini dilakukan karena alasan yang sama seperti di PostgreSQL.

Nuansa terakhir: karena basis data untuk peninjauan berada di lingkungan yang sama dengan tahap, nama yang terpisah diperlukan untuk basis data yang dikloning. Jika dump bukan file BSON, kesalahan berikut akan terjadi:

the --db and --collection args should only be used when restoring from a BSON file. Other uses are deprecated and will not exist in the future; use --nsInclude instead

Karena itu, dalam contoh di atas, --nsFromdan digunakan --nsTo.

Kami tidak menemukan masalah lain dengan pemulihan. Pada akhirnya, saya hanya akan menambahkan bahwa dokumentasi untuk copyDatabaseMongoDB tersedia di sini - jika Anda ingin mencoba opsi ini.

Rabbitmq


Aplikasi terakhir pada daftar persyaratan kami adalah RabbitMQ. Sederhana dengan itu: Anda perlu membuat vhost baru atas nama pengguna yang dengannya aplikasi akan terhubung. Dan kemudian hapus.

Manifesto untuk membuat dan menghapus vhosts:

---
apiVersion: v1
kind: ConfigMap
metadata:
  name: rabbitmq-configmap
  annotations:
    "helm.sh/hook": "pre-install"
    "helm.sh/hook-weight": "1"
    "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded
data:
  rabbitmq-setup-vhost.sh: |
    #!/bin/bash -x

    /usr/local/bin/rabbitmqadmin -H ${RABBITMQ_HOST} -u ${RABBITMQ_USER} -p ${RABBITMQ_PASSWORD} declare vhost name=${RABBITMQ_VHOST}
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: rabbitmq-configmap-on-delete
  annotations:
    "helm.sh/hook": "post-delete, pre-delete"
    "helm.sh/hook-weight": "1"
    "helm.sh/hook-delete-policy": before-hook-creation
data:
  rabbitmq-delete-vhost.sh: |
    #!/bin/bash -x

    /usr/local/bin/rabbitmqadmin -H ${RABBITMQ_HOST} -u ${RABBITMQ_USER} -p ${RABBITMQ_PASSWORD} delete vhost name=${RABBITMQ_VHOST}

Dengan kesulitan besar di RabbitMQ kita (sejauh ini?) Belum pernah bertemu. Secara umum, pendekatan yang sama dapat berlaku untuk layanan lain yang tidak memiliki ikatan kritis untuk data.

kerugian


Mengapa keputusan ini tidak mengklaim sebagai "praktik terbaik"?

  1. Ternyata satu titik kegagalan dalam bentuk lingkungan panggung.
  2. Jika aplikasi dalam lingkungan panggung hanya berjalan dalam satu replika, kita menjadi semakin bergantung pada host tempat aplikasi ini berjalan. Dengan demikian, dengan peningkatan jumlah tinjauan lingkungan, beban pada simpul meningkat secara proporsional tanpa kemampuan untuk menyeimbangkan beban ini.

Tidaklah mungkin untuk sepenuhnya menyelesaikan dua masalah ini, dengan mempertimbangkan kemampuan infrastruktur proyek tertentu, namun, pengelompokan (penambahan node baru) dan penskalaan vertikal dapat meminimalkan potensi kerusakan.

Kesimpulan


Saat aplikasi berkembang dan dengan peningkatan jumlah pengembang, cepat atau lambat, beban pada lingkungan peninjauan meningkat dan persyaratan baru ditambahkan pada mereka. Penting bagi pengembang untuk memberikan perubahan produksi berikutnya secepat mungkin, tetapi untuk mewujudkannya, kita perlu lingkungan tinjauan dinamis yang membuat pengembangan β€œparalel”. Akibatnya, beban pada infrastruktur semakin meningkat, dan waktu untuk menciptakan lingkungan seperti itu semakin meningkat.

Artikel ini ditulis berdasarkan pengalaman nyata dan agak spesifik. Hanya dalam kasus luar biasa kita mengisolasi layanan apa pun di lingkungan statis, dan di sini khusus tentangnya. Ukuran yang diperlukan seperti itu memungkinkan kami untuk mempercepat pengembangan dan debugging aplikasi - berkat kemampuan untuk dengan cepat membuat lingkungan ulasan dari awal.

Ketika kami mulai melakukan tugas ini, rasanya sangat sederhana, tetapi ketika kami mengerjakannya, kami menemukan banyak nuansa. Merekalah yang dirangkum dalam artikel terakhir: meskipun tidak universal, mereka dapat menjadi contoh untuk dasar / inspirasi keputusan mereka sendiri dalam mempercepat lingkungan ulasan.

PS


Baca juga di blog kami:


All Articles