Google Cloud Sürekli Dağıtımına Daha İyi Bir Yaklaşım
Yayınlanan: 2022-03-11Sürekli dağıtım (CD), yeni kodu üretime otomatik olarak dağıtma uygulamasıdır. Çoğu sürekli dağıtım sistemi, dağıtılacak kodun uygulanabilir olduğunu birim ve işlevsel testler çalıştırarak doğrular ve her şey yolunda görünüyorsa dağıtım kullanıma sunulur. Kodun beklendiği gibi davranmaması durumunda geri alınabilmesi için kullanıma sunmanın kendisi genellikle aşamalar halinde gerçekleşir.
AWS yığını, Google Cloud yığını, Bitbucket ardışık düzeni vb. gibi çeşitli araçları kullanarak kendi CD ardışık düzeninizi nasıl uygulayacağınızla ilgili hiçbir blog yazısı sıkıntısı yok. Ancak bunların çoğunun ne kadar iyi bir CD ardışık düzeni hakkındaki fikrime uymadığını görüyorum. şöyle görünmelidir: önce derleyen ve yalnızca o tek yerleşik dosyayı test eden ve dağıtan.
Bu makalede, önce oluşturulan ve ardından dağıtımın son yapıtımız üzerinde testler çalıştıran olay güdümlü bir sürekli dağıtım ardışık düzeni oluşturacağım. Bu, yalnızca test sonuçlarımızı daha güvenilir kılmakla kalmaz, aynı zamanda CD ardışık düzenini kolayca genişletilebilir hale getirir. Şuna benzer bir şey olurdu:
- Kaynak depomuza bir taahhütte bulunulur.
- Bu, ilişkili görüntünün bir yapısını tetikler.
- Testler, yerleşik yapı üzerinde çalıştırılır.
- Her şey yolunda görünüyorsa, görüntü üretime dağıtılır.
Bu makale, en azından Kubernetes ve kapsayıcı teknolojisine aşina olduğunuzu varsayar, ancak aşina değilseniz veya bilgi tazeleme kullanabiliyorsanız, bkz. Kubernetes Nedir? Konteynerleştirme ve Dağıtım Kılavuzu.
Çoğu CD Kurulumu ile ilgili sorun
Çoğu CD işlem hattıyla ilgili sorunum şu: Genellikle derleme dosyasındaki her şeyi yaparlar. Bununla ilgili okuduğum çoğu blog gönderisi, sahip oldukları derleme dosyası ne olursa olsun aşağıdaki sıranın bazı varyasyonlarına sahip olacaktır (Google Cloud Build için cloudbuild.yaml, cloudbuild.yaml
için bitbucket bitbucket-pipeline.yaml
).
- Testleri çalıştırın
- Resim oluştur
- Resmi konteyner deposuna itin
- Ortamı yeni görüntüyle güncelleyin
Testlerinizi son eseriniz üzerinde yapmıyorsunuz.
İşleri bu sırayla yaparak testlerinizi gerçekleştirirsiniz. Başarılı olurlarsa, görüntüyü oluşturur ve boru hattının geri kalanıyla devam edersiniz. Oluşturma işlemi, resminizi, testler artık geçmeyecek şekilde değiştirirse ne olur? Benim düşünceme göre, bir yapıt (son kapsayıcı görüntüsü) üreterek başlamalısınız ve bu yapı, yapı ile üretime dağıtıldığı zaman arasında değişmemelidir. Bu, söz konusu yapı hakkında sahip olduğunuz verilerin (test sonuçları, boyut vb.) her zaman geçerli olmasını sağlar.
Yapı ortamınız "krallığın anahtarlarına" sahiptir.
İmajınızı üretim yığınınıza yerleştirmek için inşa ortamınızı kullanarak, onun üretim ortamınızı değiştirmesine etkin bir şekilde izin vermiş olursunuz. Bunu çok kötü bir şey olarak görüyorum çünkü kaynak deponuza yazma erişimi olan herkes artık üretim ortamınıza istedikleri her şeyi yapabilir.
Son adım başarısız olursa, tüm boru hattını yeniden çalıştırmanız gerekir.
Son adım başarısız olursa (örneğin, bir kimlik bilgisi sorunu nedeniyle), başka bir şey yapmak için daha iyi harcanabilecek zaman ve diğer kaynakları alarak tüm işlem hattınızı yeniden çalıştırmanız gerekir.
Bu beni son noktama götürüyor:
Adımlarınız bağımsız değil.
Daha genel anlamda, bağımsız adımlara sahip olmak, boru hattınızda daha fazla esnekliğe sahip olmanızı sağlar. İşlem hattınıza işlevsel testler eklemek istediğinizi varsayalım. Adımlarınızı tek bir derleme dosyasında alarak, derleme ortamınızın işlevsel bir test ortamı başlatmasını ve testleri bu ortam içinde (büyük olasılıkla sırayla) çalıştırmanız gerekir. Adımlarınız bağımsız olsaydı, “image yerleşik” olayıyla hem birim testlerinizi hem de işlevsel testlerinizi başlatabilirdiniz. Daha sonra kendi ortamlarında paralel olarak koşarlardı.
İdeal CD Kurulumum
Bana göre, bu soruna yaklaşmanın daha iyi bir yolu, hepsi bir olay mekanizmasıyla birbirine bağlanan bir dizi bağımsız adıma sahip olmak olacaktır.
Bunun önceki yönteme kıyasla birkaç avantajı vardır:
Farklı olaylarda birkaç bağımsız eylemde bulunabilirsiniz.
Yukarıda belirtildiği gibi, yeni bir görüntünün başarılı bir şekilde oluşturulması, yalnızca “başarılı bir yapı” olayı yayınlayacaktır. Buna karşılık, bu olay tetiklendiğinde birkaç şeyi çalıştırabiliriz. Bizim durumumuzda birim ve fonksiyonel testleri başlatırdık. Bir derleme başarısız olayı tetiklendiğinde veya testler başarısız olduğunda geliştiriciyi uyarmak gibi şeyler de düşünebilirsiniz.
Her ortamın kendi hakları vardır.
Her adımı kendi ortamında gerçekleştirerek, tüm haklara sahip olmak için tek bir ortamın gerekliliğini ortadan kaldırıyoruz. Artık derleme ortamı yalnızca derleme yapabilir, test ortamı yalnızca test edebilir ve devreye alma ortamı yalnızca dağıtabilir. Bu, imajınız oluşturulduktan sonra değişmeyeceğinden emin olmanızı sağlar. Üretilen eser, üretim yığınınızda sona erecek olandır. Ayrıca, bir dizi kimlik bilgisini bir adıma bağlayabildiğiniz için, işlem hattınızın hangi adımının ne yaptığını daha kolay denetlemenize olanak tanır.
Daha fazla esneklik var.
Her başarılı derlemede birine e-posta göndermek ister misiniz? Sadece o olaya tepki veren ve bir e-posta gönderen bir şey ekleyin. Kolaydır—yapı kodunuzu değiştirmeniz gerekmez ve kaynak havuzunuzda birinin e-postasını sabit kodlamanız gerekmez.
Yeniden denemeler daha kolaydır.
Bağımsız adımlara sahip olmak, bir adım başarısız olursa tüm boru hattını yeniden başlatmanız gerekmediği anlamına da gelir. Başarısızlık durumu geçiciyse veya manuel olarak düzeltildiyse, başarısız olan adımı yeniden deneyebilirsiniz. Bu, daha verimli bir boru hattı sağlar. Bir derleme adımı birkaç dakika sürdüğünde, dağıtım ortamınıza kümenize yazma erişimi vermeyi unuttuğunuz için görüntüyü yeniden oluşturmanız gerekmez.
Google Cloud Sürekli Dağıtımı Uygulama
Google Cloud Platform, böyle bir sistemi kısa sürede ve çok az kodla oluşturmak için gerekli tüm araçlara sahiptir.
Test uygulamamız, yalnızca bir parça statik metin sunan basit bir Flask uygulamasıdır. Bu uygulama, daha geniş internete hizmet eden bir Kubernetes kümesine dağıtılır.
Daha önce tanıttığım boru hattının basitleştirilmiş bir versiyonunu uygulayacağım. Temelde test adımlarını kaldırdım, böylece şimdi şöyle görünüyor:
- Kaynak deposuna yeni bir taahhütte bulunuldu
- Bu, bir görüntü oluşturmayı tetikler. Başarılı olursa kapsayıcı deposuna gönderilir ve bir Pub/Sub konusuna bir etkinlik yayınlanır
- Küçük bir komut dosyası bu konuya abone olur ve görüntünün parametrelerini kontrol eder; bizim istediğimizle eşleşirlerse, Kubernetes kümesine dağıtılır.
İşte boru hattımızın grafiksel bir temsili.
Akış aşağıdaki gibidir:
- Birisi depomuza taahhütte bulunuyor.
- Bu, kaynak deposuna dayalı bir Docker görüntüsü oluşturan bir bulut yapısını tetikler.
- Bulut yapısı, görüntüyü kapsayıcı deposuna iletir ve bulut pub/sub'a bir mesaj yayınlar.
- Bu, yayınlanan mesajın parametrelerini (yapılandırma durumu, oluşturulan görüntünün adı vb.) kontrol eden bir bulut işlevini tetikler.
- Parametreler iyiyse bulut işlevi, yeni görüntüyle bir Kubernetes dağıtımını günceller.
- Kubernetes, yeni görüntüyle yeni kapsayıcıları dağıtır.
Kaynak kodu
Kaynak kodumuz, yalnızca bazı statik metinler sunan çok basit bir Flask uygulamasıdır. İşte projemizin yapısı:

├── docker │ ├── Dockerfile │ └── uwsgi.ini ├── k8s │ ├── deployment.yaml │ └── service.yaml ├── LICENSE ├── Pipfile ├── Pipfile.lock └── src └── main.py
Docker dizini, Docker görüntüsünü oluşturmak için gereken her şeyi içerir. Görüntü, uWSGI ve Nginx görüntüsünü temel alır ve yalnızca bağımlılıkları yükler ve uygulamayı doğru yola kopyalar.
K8s dizini, Kubernetes yapılandırmasını içerir. Bir hizmet ve bir dağıtımdan oluşur. Dağıtım, Dockerfile'den oluşturulan görüntüye dayalı olarak bir kapsayıcı başlatır. Ardından hizmet, genel bir IP adresine sahip olan ve uygulama kapsayıcılarına yeniden yönlendiren bir yük dengeleyici başlatır.
Bulut Oluşturma
Bulut oluşturma yapılandırmasının kendisi, bulut konsolu veya Google Cloud komut satırı aracılığıyla yapılabilir. Bulut konsolunu kullanmayı seçtim.
Burada, herhangi bir daldaki herhangi bir taahhüt için bir imaj oluşturuyoruz, ancak örneğin geliştirme ve üretim için farklı imajlarınız olabilir.
Derleme başarılı olursa, bulut derlemesi görüntüyü kendi başına kapsayıcı kayıt defterine yayınlar. Ardından cloud-builds pub/sub konusuna bir mesaj yayınlayacaktır.
Bulut derlemesi ayrıca bir derleme devam ederken ve başarısız olduğunda mesajları yayınlar, böylece bu mesajlara tepki vermesini de sağlayabilirsiniz.
Bulut yapısının yayın/alt bildirimlerine ilişkin belgeler burada ve mesajın biçimi burada bulunabilir
Bulut Pub/Sub
Bulut konsolundaki bulut pub/alt sekmenize bakarsanız, bulut derlemesinin bulut derlemeleri adlı bir konu oluşturduğunu görürsünüz. Bulut yapısının durum güncellemelerini yayınladığı yer burasıdır.
Bulut İşlevi
Şimdi yapacağımız şey, bulut oluşturma konusunda yayınlanan herhangi bir mesajda tetiklenen bir bulut işlevi oluşturmak. Yine, bulut konsolunu veya Google Cloud komut satırı yardımcı programını kullanabilirsiniz. Benim durumumda yaptığım şey, her değişiklik olduğunda bulut işlevini dağıtmak için bulut yapısını kullanmamdır.
Bulut işlevi için kaynak kodu burada.
Önce bu bulut işlevini dağıtan koda bakalım:
steps: - name: 'gcr.io/cloud-builders/gcloud' id: 'test' args: ['functions', 'deploy', 'new-image-trigger', '--runtime=python37', '--trigger-topic=cloud-builds', '--entry-point=onNewImage', '--region=us-east1', '--source=https://source.developers.google.com/projects/$PROJECT_ID/repos/$REPO_NAME']
Burada Google Cloud Docker imajını kullanıyoruz. Bu, GCcloud komutlarının kolayca çalıştırılmasına olanak tanır. Yürüttüğümüz şey, aşağıdaki komutu doğrudan bir terminalden çalıştırmanın eşdeğeridir:
gcloud functions deploy new-image-trigger --runtime=python37 --trigger-topic=cloud-builds --entry-point=onNewImage --region=us-east1 --source=https://source.developers.google.com/projects/$PROJECT_ID/repos/$REPO_NAME
Google Cloud'dan Python 3.7 çalışma zamanını kullanacak ve bulut oluşturma konusundaki yeni mesajlar tarafından tetiklenecek yeni bir bulut işlevi dağıtmasını (veya o bölgede bu ada sahip bir işlev zaten varsa değiştirin) istiyoruz. Google'a bu işlevin kaynak kodunu nerede bulacağını da söyleriz (burada PROJECT_ID ve REPO_NAME, derleme işlemi tarafından belirlenen ortam değişkenleridir). Ayrıca giriş noktası olarak hangi işlevi çağıracağını da söyleriz.
Bir yan not olarak, bunun çalışması için, bulut işlevini dağıtabilmesi için cloudbuild hizmet hesabınıza hem "bulut işlevleri geliştiricisi" hem de "hizmet hesabı kullanıcısı" vermeniz gerekir.
İşte bulut işlev kodunun yorumlanmış bazı parçacıkları
Giriş noktası verileri, pub/sub konusunda alınan mesajı içerecektir.
def onNewImage(data, context):
İlk adım, o belirli dağıtım için değişkenleri ortamdan almaktır (bunları bulut konsolunda bulut işlevini değiştirerek tanımladık.
project = os.environ.get('PROJECT') zone = os.environ.get('ZONE') cluster = os.environ.get('CLUSTER') deployment = os.environ.get('DEPLOYMENT') deploy_image = os.environ.get('IMAGE') target_container = os.environ.get('CONTAINER')
Mesajın yapısının beklediğimiz gibi olduğunu kontrol ettiğimiz ve yapının başarılı olduğunu ve bir görüntü yapaylığı ürettiğini doğruladığımız kısmı atlayacağız.
Sonraki adım, oluşturulan görüntünün dağıtmak istediğimiz görüntü olduğundan emin olmaktır.
image = decoded_data['results']['images'][0]['name'] image_basename = image.split('/')[-1].split(':')[0] if image_basename != deploy_image: logging.error(f'{image_basename} is different from {deploy_image}') return
Şimdi bir Kubernetes istemcisi alıyoruz ve değiştirmek istediğimiz dağıtımı alıyoruz
v1 = get_kube_client(project, zone, cluster) dep = v1.read_namespaced_deployment(deployment, 'default') if dep is None: logging.error(f'There was no deployment named {deployment}') return
Son olarak, dağıtımı yeni görüntüyle yamalıyoruz; Kubernetes bunu kullanıma sunacak.
for i, container in enumerate(dep.spec.template.spec.containers): if container.name == target_container: dep.spec.template.spec.containers[i].image = image logging.info(f'Updating to {image}') v1.patch_namespaced_deployment(deployment, 'default', dep)
Çözüm
Bu, şeylerin bir CD ardışık düzeninde tasarlanmasını nasıl sevdiğimin çok basit bir örneğidir. Hangi pub/sub olayının neyi tetiklediğini değiştirerek daha fazla adıma sahip olabilirsiniz.
Örneğin, görüntünün içindeki testleri çalıştıran ve başarı durumunda bir olay ve başarısızlık durumunda başka bir olay yayınlayan bir kapsayıcı çalıştırabilir ve sonuca bağlı olarak bir dağıtımı güncelleyerek veya uyarı vererek bunlara tepki verebilirsiniz.
Oluşturduğumuz işlem hattı oldukça basittir, ancak diğer bölümler için başka bulut işlevleri yazabilirsiniz (örneğin, birim testlerinizi bozan kodu işleyen geliştiriciye bir e-posta gönderecek bir bulut işlevi).
Gördüğünüz gibi, derleme ortamımız Kubernetes kümemizdeki hiçbir şeyi değiştiremez ve dağıtım kodumuz (bulut işlevi) oluşturulan görüntüyü değiştiremez. Ayrıcalık ayrımımız iyi görünüyor ve haydut bir geliştiricinin üretim kümemizi çökertmeyeceğini bilerek rahat uyuyabiliriz. Ayrıca, daha operasyon odaklı geliştiricilerimize, düzeltebilmeleri veya iyileştirebilmeleri için bulut işlev koduna erişim verebiliriz.
Herhangi bir sorunuz, yorumunuz veya iyileştirmeniz varsa, aşağıdaki yorumlarda bize ulaşmaktan çekinmeyin.