Zero Downtime Jenkins Continuous Deployment dengan Terraform di AWS

Diterbitkan: 2022-03-11

Di dunia internet saat ini di mana semuanya harus selalu aktif 24/7, keandalan adalah kuncinya. Ini berarti hampir nol waktu henti untuk situs web Anda, menghindari halaman kesalahan "Tidak ditemukan: 404" yang ditakuti, atau gangguan layanan lainnya saat Anda meluncurkan rilis terbaru Anda.

Misalkan Anda telah membangun aplikasi baru untuk klien Anda, atau mungkin diri Anda sendiri, dan telah berhasil mendapatkan basis pengguna yang baik yang menyukai aplikasi Anda. Anda telah mengumpulkan umpan balik dari pengguna Anda, dan Anda pergi ke pengembang Anda dan meminta mereka untuk membangun fitur baru dan membuat aplikasi siap untuk diterapkan. Dengan kesiapan tersebut, Anda dapat menghentikan seluruh aplikasi dan menerapkan versi baru atau membangun pipa penyebaran CI/CD tanpa henti yang akan melakukan semua pekerjaan membosankan untuk mendorong rilis baru kepada pengguna tanpa intervensi manual.

Dalam artikel ini, kita akan berbicara persis tentang yang terakhir, bagaimana kita dapat memiliki pipeline penerapan berkelanjutan dari aplikasi web tiga tingkat yang dibangun di Node.js di AWS Cloud menggunakan Terraform sebagai orkestra infrastruktur. Kami akan menggunakan Jenkins untuk bagian penerapan berkelanjutan dan Bitbucket untuk meng-host basis kode kami.

Repositori Kode

Kami akan menggunakan aplikasi web demo tiga tingkat yang kodenya dapat Anda temukan di sini.

Repo berisi kode untuk web dan lapisan API. Ini adalah aplikasi sederhana di mana modul web memanggil salah satu titik akhir di lapisan API yang secara internal mengambil informasi tentang waktu saat ini dari database dan kembali ke lapisan web.

Struktur repo adalah sebagai berikut:

  • API: Kode untuk lapisan API
  • Web: Kode untuk lapisan web
  • Terraform: Kode untuk orkestrasi infrastruktur menggunakan Terraform
  • Jenkins: Kode untuk orkestra infrastruktur untuk server Jenkins yang digunakan untuk pipa CI/CD.

Sekarang setelah kita memahami apa yang perlu kita terapkan, mari kita diskusikan hal-hal yang harus kita lakukan untuk menerapkan aplikasi ini di AWS dan kemudian kita akan berbicara tentang cara membuat bagian itu dari saluran CI/CD.

Memanggang Gambar

Karena kami menggunakan Terraform untuk orkestra infrastruktur, masuk akal untuk memiliki gambar yang telah dibuat sebelumnya untuk setiap tingkat atau aplikasi yang ingin Anda terapkan. Dan untuk itu, kami akan menggunakan produk lain dari Hashicorp—yaitu, Packer.

Packer adalah alat sumber terbuka yang membantu membangun Amazon Machine Image atau AMI, yang akan digunakan untuk penerapan di AWS. Ini dapat digunakan untuk membangun gambar untuk berbagai platform seperti EC2, VirtualBox, VMware, dan lainnya.

Berikut ini cuplikan cara file konfigurasi Packer ( terraform/packer-ami-api.json ) digunakan untuk membuat AMI untuk lapisan API.

 { "builders": [{ "type": "amazon-ebs", "region": "eu-west-1", "source_ami": "ami-844e0bf7", "instance_type": "t2.micro", "ssh_username": "ubuntu", "ami_name": "api-instance {{timestamp}}" }], "provisioners": [ { "type": "shell", "inline": ["mkdir api", "sudo apt-get update", "sudo apt-get -y install npm nodejs-legacy"], "pause_before": "10s" }, { "type": "file", "source" : "../api/", "destination" : "api" }, { "type": "shell", "inline": ["cd api", "npm install"], "pause_before": "10s" } ] }

Dan Anda perlu menjalankan perintah berikut untuk membuat AMI:

 packer build -machine-readable packer-ami-api.json

Kami akan menjalankan perintah ini dari build Jenkins nanti di artikel ini. Dengan cara yang sama, kita akan menggunakan file konfigurasi Packer ( terraform/packer-ami-web.json ) untuk lapisan web juga.

Mari kita lihat file konfigurasi Packer di atas dan pahami apa yang coba dilakukan.

  1. Seperti disebutkan sebelumnya, Packer dapat digunakan untuk membangun gambar untuk banyak platform, dan karena kami menerapkan aplikasi kami ke AWS, kami akan menggunakan pembuat "amazon-ebs", karena itu adalah pembuat termudah untuk memulai.
  2. Bagian kedua dari konfigurasi mengambil daftar penyedia yang lebih seperti skrip atau blok kode yang dapat Anda gunakan untuk mengonfigurasi gambar Anda.
    • Langkah 1 menjalankan penyedia shell untuk membuat folder API dan menginstal Node.js pada gambar menggunakan properti inline , yang merupakan kumpulan perintah yang ingin Anda jalankan.
    • Langkah 2 menjalankan penyedia file untuk menyalin kode sumber kami dari folder API ke instance.
    • Langkah 3 lagi menjalankan penyedia shell tetapi kali ini menggunakan properti skrip untuk menentukan file (terraform/scripts/install_api_software.sh) dengan perintah yang perlu dijalankan.
    • Langkah 4 menyalin file konfigurasi ke instance yang diperlukan untuk Cloudwatch, yang diinstal pada langkah berikutnya.
    • Langkah 5 menjalankan penyedia shell untuk menginstal agen AWS Cloudwatch. Input ke perintah ini adalah file konfigurasi yang disalin pada langkah sebelumnya. Kami akan berbicara tentang Cloudwatch secara rinci nanti di artikel.

Jadi, pada dasarnya, konfigurasi Packer berisi info tentang pembuat yang Anda inginkan dan kemudian satu set penyedia yang dapat Anda tentukan dalam urutan apa pun tergantung pada bagaimana Anda ingin mengonfigurasi gambar Anda.

Menyiapkan Deployment Berkelanjutan Jenkins

Selanjutnya, kita akan melihat pengaturan server Jenkins yang akan digunakan untuk pipeline CI/CD kita. Kami akan menggunakan Terraform dan AWS untuk menyiapkan ini juga.

Kode Terraform untuk setting Jenkins ada di dalam folder jenkins/setup . Mari kita lihat beberapa hal menarik tentang pengaturan ini.

  1. Kredensial AWS: Anda dapat memberikan ID kunci akses AWS dan kunci akses rahasia ke penyedia Terraform AWS ( instance.tf ) atau Anda dapat memberikan lokasi file kredensial ke properti shared_credentials_file di penyedia AWS.
  2. Peran IAM: Karena kami akan menjalankan Packer dan Terraform dari server Jenkins, mereka akan mengakses layanan S3, EC2, RDS, IAM, load balancing, dan penskalaan otomatis di AWS. Jadi kami memberikan kredensial kami di Jenkins untuk Packer & Terraform untuk mengakses layanan ini atau kami dapat membuat Profil IAM ( iam.tf ), yang dengannya kami akan membuat instance Jenkins.
  3. Status Terraform: Terraform harus mempertahankan status infrastruktur di suatu tempat dalam file dan, dengan S3 ( backend.tf ), Anda dapat mempertahankannya di sana, sehingga Anda dapat berkolaborasi dengan rekan kerja lain, dan siapa pun dapat mengubah dan menerapkannya sejak status tersebut dipelihara di lokasi yang jauh.
  4. Pasangan kunci publik/pribadi: Anda perlu mengunggah kunci publik dari pasangan kunci Anda bersama dengan instance sehingga Anda dapat ssh ke dalam instance Jenkins setelah selesai. Kami telah mendefinisikan sumber daya aws_key_pair ( key.tf ) di mana Anda menentukan lokasi kunci publik Anda menggunakan variabel Terraform.

Langkah-langkah untuk menyiapkan Jenkins:

Langkah 1: Untuk menjaga status Terraform jarak jauh, Anda perlu membuat bucket secara manual di S3 yang dapat digunakan oleh Terraform. Ini akan menjadi satu-satunya langkah yang dilakukan di luar Terraform. Pastikan Anda menjalankan AWS configure sebelum menjalankan perintah di bawah ini untuk menentukan kredensial AWS Anda.

 aws s3api create-bucket --bucket node-aws-jenkins-terraform --region eu-west-1 --create-bucket-configuration LocationConstraint=eu-west-1

Langkah 2: Jalankan terraform init . Ini akan menginisialisasi status dan mengonfigurasinya untuk disimpan di S3 dan mengunduh plugin penyedia AWS.

Langkah 3: Jalankan terraform apply . Ini akan memeriksa semua kode Terraform dan membuat rencana dan menunjukkan berapa banyak sumber daya yang akan dibuat setelah langkah ini selesai.

Langkah 4: Ketik yes , lalu langkah sebelumnya akan mulai membuat semua sumber daya. Setelah perintah selesai, Anda akan mendapatkan alamat IP publik dari server Jenkins.

Langkah 5: Ssh ke server Jenkins, menggunakan kunci pribadi Anda. ubuntu adalah nama pengguna default untuk instans yang didukung AWS EBS. Gunakan alamat IP yang dikembalikan oleh perintah terraform apply .

 ssh -i mykey [email protected]

Langkah 6: Mulai UI web Jenkins dengan membuka http://34.245.4.73:8080 . Kata sandi dapat ditemukan di /var/lib/jenkins/secrets/initialAdminPassword .

Langkah 7: Pilih "Instal Plugin yang Disarankan" dan Buat pengguna Admin untuk Jenkins.

Mengatur Pipa CI Antara Jenkins dan Bitbucket

  1. Untuk ini, kita perlu menginstal plugin Bitbucket di Jenkins. Buka Kelola Jenkins → Kelola Plugin dan dari Plugin yang tersedia instal plugin Bitbucket.
  2. Di sisi repo Bitbucket, buka Pengaturan → Webhooks , tambahkan webhook baru. Hook ini akan mengirimkan semua perubahan dalam repositori ke Jenkins dan itu akan memicu pipeline.
    Menambahkan webhook ke penerapan berkelanjutan Jenkins melalui Bitbucker

Jenkins Pipeline untuk Memanggang/Membuat Gambar

  1. Langkah selanjutnya adalah membuat pipeline di Jenkins.
  2. Pipeline pertama adalah proyek Freestyle yang akan digunakan untuk membangun AMI aplikasi menggunakan Packer.
  3. Anda perlu menentukan kredensial dan URL untuk repositori Bitbucket Anda.
    Tambahkan kredensial ke bitbucket
  4. Tentukan pemicu Build.
    Mengonfigurasi pemicu build
  5. Tambahkan dua langkah build, satu untuk membangun AMI untuk modul aplikasi dan lainnya untuk membangun AMI untuk modul web.
    Menambahkan langkah-langkah pembuatan AMI
  6. Setelah ini selesai, Anda dapat menyimpan proyek Jenkins dan sekarang, ketika Anda mendorong apa pun ke repositori Bitbucket Anda, itu akan memicu build baru di Jenkins yang akan membuat AMI dan mendorong file Terraform yang berisi nomor AMI dari Gambar itu ke Bucket S3 yang dapat Anda lihat dari dua baris terakhir pada langkah pembuatan.
 echo 'variable "WEB_INSTANCE_AMI" { default = "'${AMI_ID_WEB}'" }' > amivar_web.tf aws s3 cp amivar_web.tf s3://node-aws-jenkins-terraform/amivar_web.tf

Jenkins Pipeline untuk Memicu Skrip Terraform

Sekarang setelah kami memiliki AMI untuk API dan modul web, kami akan memicu build untuk menjalankan kode Terraform untuk menyiapkan seluruh aplikasi dan kemudian melalui komponen dalam kode Terraform yang membuat pipeline ini menerapkan perubahan tanpa downtime layanan.

  1. Kami membuat proyek Jenkins gaya bebas lainnya, nodejs-terraform , yang akan menjalankan kode Terraform untuk menerapkan aplikasi.
  2. Pertama-tama kita akan membuat kredensial jenis "teks rahasia" di domain kredensial global, yang akan digunakan sebagai input ke skrip Terraform. Karena kami tidak ingin membuat hard-code kata sandi untuk layanan RDS di dalam Terraform dan Git, kami meneruskan properti itu menggunakan kredensial Jenkins.
    membuat rahasia untuk digunakan dengan Terraform ci cd
  3. Anda perlu menentukan kredensial dan URL yang mirip dengan proyek lain.
  4. Di bagian pemicu build, kami akan menautkan proyek ini dengan yang lain sedemikian rupa sehingga proyek ini dimulai ketika yang sebelumnya selesai.
    Tautkan proyek bersama
  5. Kemudian kita bisa mengonfigurasi kredensial yang kita tambahkan sebelumnya ke proyek menggunakan binding, sehingga tersedia di langkah build.
    Mengonfigurasi binding
  6. Sekarang kita siap untuk menambahkan langkah build, yang akan mengunduh file skrip Terraform ( amivar_api.tf dan amivar_web.tf ) yang diunggah ke S3 oleh proyek sebelumnya dan kemudian menjalankan kode Terraform untuk membangun seluruh aplikasi di AWS.
    Menambahkan skrip pembuatan

Jika semuanya dikonfigurasi dengan benar, sekarang jika Anda memasukkan kode apa pun ke repositori Bitbucket Anda, itu akan memicu proyek Jenkins pertama diikuti oleh yang kedua dan Anda harus menerapkan aplikasi Anda ke AWS.

Konfigurasi Terraform Zero Downtime untuk AWS

Sekarang mari kita bahas apa yang ada dalam kode Terraform yang membuat pipeline ini menerapkan kode tanpa downtime.

Hal pertama adalah Terraform menyediakan blok konfigurasi siklus hidup ini untuk sumber daya di mana Anda memiliki opsi create_before_destroy sebagai flag yang secara harfiah berarti bahwa Terraform harus membuat sumber daya baru dari jenis yang sama sebelum menghancurkan sumber daya saat ini.

Sekarang kami mengeksploitasi fitur ini di sumber daya aws_autoscaling_group dan aws_launch_configuration . Jadi aws_launch_configuration mengonfigurasi jenis instans EC2 mana yang harus disediakan dan cara kami menginstal perangkat lunak pada instans tersebut, dan sumber daya aws_autoscaling_group menyediakan grup penskalaan otomatis AWS.

Tangkapan yang menarik di sini adalah bahwa semua sumber daya di Terraform harus memiliki kombinasi nama dan jenis yang unik. Jadi, kecuali Anda memiliki nama yang berbeda untuk aws_autoscaling_group dan aws_launch_configuration baru, Anda tidak dapat menghancurkan yang sekarang.

Terraform menangani kendala ini dengan menyediakan properti name_prefix ke sumber daya aws_launch_configuration . Setelah properti ini ditentukan, Terraform akan menambahkan sufiks unik ke semua sumber daya aws_launch_configuration dan kemudian Anda dapat menggunakan nama unik itu untuk membuat sumber daya aws_autoscaling_group .

Anda dapat memeriksa kode untuk semua hal di atas di terraform/autoscaling-api.tf

 resource "aws_launch_configuration" "api-launchconfig" { name_prefix = "api-launchconfig-" image_ instance_type = "t2.micro" security_groups = ["${aws_security_group.api-instance.id}"] user_data = "${data.template_file.api-shell-script.rendered}" iam_instance_profile = "${aws_iam_instance_profile.CloudWatchAgentServerRole-instanceprofile.name}" connection { user = "${var.INSTANCE_USERNAME}" private_key = "${file("${var.PATH_TO_PRIVATE_KEY}")}" } lifecycle { create_before_destroy = true } } resource "aws_autoscaling_group" "api-autoscaling" { name = "${aws_launch_configuration.api-launchconfig.name}-asg" vpc_zone_identifier = ["${aws_subnet.main-public-1.id}"] launch_configuration = "${aws_launch_configuration.api-launchconfig.name}" min_size = 2 max_size = 2 health_check_grace_period = 300 health_check_type = "ELB" load_balancers = ["${aws_elb.api-elb.name}"] force_delete = true lifecycle { create_before_destroy = true } tag { key = "Name" value = "api ec2 instance" propagate_at_launch = true } }

Dan tantangan kedua dengan penerapan tanpa waktu henti adalah memastikan penerapan baru Anda siap untuk mulai menerima permintaan. Hanya menerapkan dan memulai instans EC2 baru tidak cukup dalam beberapa situasi.

Untuk mengatasi masalah itu, aws_launch_configuration memiliki properti user_data yang mendukung properti penskalaan otomatis AWS user_data asli yang dapat digunakan untuk meneruskan skrip apa pun yang ingin Anda jalankan saat memulai instans baru sebagai bagian dari grup penskalaan otomatis. Dalam contoh kami, kami mengekor log server aplikasi dan menunggu pesan startup berada di sana. Anda juga dapat memeriksa server HTTP dan melihat kapan mereka aktif.

 until tail /var/log/syslog | grep 'node ./bin/www' > /dev/null; do sleep 5; done

Bersamaan dengan itu, Anda juga dapat mengaktifkan pemeriksaan ELB pada tingkat sumber daya aws_autoscaling_group , yang akan memastikan instance baru telah ditambahkan untuk lulus pemeriksaan ELB sebelum Terraform menghancurkan instance lama. Beginilah tampilan pemeriksaan ELB untuk lapisan API; itu memeriksa titik akhir /api/status untuk mengembalikan kesuksesan.

 resource "aws_elb" "api-elb" { name = "api-elb" subnets = ["${aws_subnet.main-public-1.id}"] security_groups = ["${aws_security_group.elb-securitygroup.id}"] listener { instance_port = "${var.API_PORT}" instance_protocol = "http" lb_port = 80 lb_protocol = "http" } health_check { healthy_threshold = 2 unhealthy_threshold = 2 timeout = 3 target = "HTTP:${var.API_PORT}/api/status" interval = 30 } cross_zone_load_balancing = true connection_draining = true connection_draining_timeout = 400 tags { Name = "my-elb" } }

Ringkasan dan Langkah Selanjutnya

Jadi ini membawa kita ke akhir artikel ini; mudah-mudahan, sekarang, aplikasi Anda sudah di-deploy dan dijalankan dengan pipeline CI/CD zero-downtime menggunakan penerapan Jenkins dan praktik terbaik Terraform atau Anda sedikit lebih nyaman menjelajahi wilayah ini dan membuat penerapan Anda hanya memerlukan sedikit intervensi manual. mungkin.

Dalam artikel ini, strategi penyebaran yang digunakan disebut penyebaran Biru-Hijau di mana kami memiliki instalasi saat ini (Biru) yang menerima lalu lintas langsung saat kami menerapkan dan menguji versi baru (Hijau) dan kemudian kami menggantinya setelah versi baru siap. Selain strategi ini, ada cara lain untuk menerapkan aplikasi Anda, yang dijelaskan dengan baik dalam artikel ini, Pengantar Strategi Penerapan. Mengadaptasi strategi lain sekarang semudah mengonfigurasi pipeline Jenkins Anda.

Juga, dalam artikel ini, saya berasumsi bahwa semua perubahan baru di API, web, dan lapisan data kompatibel sehingga Anda tidak perlu khawatir tentang versi baru yang berbicara dengan versi yang lebih lama. Tapi, pada kenyataannya, mungkin tidak selalu demikian. Untuk mengatasi masalah itu, saat merancang rilis/fitur baru Anda, selalu pikirkan tentang lapisan kompatibilitas mundur atau Anda perlu mengubah penerapan Anda untuk menangani situasi itu juga.

Pengujian integrasi adalah sesuatu yang hilang dari pipa penyebaran ini juga. Karena Anda tidak ingin apa pun dirilis ke pengguna akhir tanpa diuji, itu pasti sesuatu yang perlu diingat ketika saatnya tiba untuk menerapkan strategi ini ke proyek Anda sendiri.

Jika Anda tertarik untuk mempelajari lebih lanjut tentang cara kerja Terraform dan bagaimana Anda dapat menerapkan ke AWS menggunakan teknologi tersebut, saya merekomendasikan Terraform AWS Cloud: Manajemen Infrastruktur Sane di mana sesama Toptaler Radoslaw Szalski menjelaskan Terraform dan kemudian menunjukkan kepada Anda langkah-langkah yang diperlukan untuk mengonfigurasi multi -Pengaturan Terraform lingkungan dan siap produksi untuk tim

Terkait: Terraform vs. CloudFormation: Panduan Definitif