GitHub Web Kancalarını Kullanarak Web Uygulamalarını Otomatik Olarak Dağıtın
Yayınlanan: 2022-03-11Web uygulamaları geliştiren ve bunları kendi yönetilmeyen sunucularında çalıştırmaya çalışan herkes, uygulamalarını dağıtmanın ve gelecekteki güncellemeleri zorlamanın sıkıcı sürecinin farkındadır. Hizmet olarak platform (PaaS) sağlayıcıları, maliyetlerde hafif bir artış ve esneklikte azalma karşılığında, bireysel sunucuları sağlama ve yapılandırma sürecinden geçmek zorunda kalmadan web uygulamalarının dağıtımını kolaylaştırdı. PaaS işleri kolaylaştırmış olabilir, ancak bazen uygulamaları kendi yönetilmeyen sunucularımızda dağıtmaya ihtiyacımız var veya istiyoruz. Bu web uygulamalarını sunucunuza dağıtma sürecini otomatikleştirmek ilk başta bunaltıcı gelebilir, ancak gerçekte bunu otomatikleştirmek için basit bir araç bulmak düşündüğünüzden daha kolay olabilir. Bu aracı uygulamanın ne kadar kolay olacağı, büyük ölçüde ihtiyaçlarınızın ne kadar basit olduğuna bağlıdır, ancak bunu başarmak kesinlikle zor değildir ve muhtemelen web uygulamasının sıkıcı tekrarlayan bitlerini yaparak çok fazla zaman ve emekten tasarruf etmenize yardımcı olabilir. dağıtımlar.
Birçok geliştirici, web uygulamalarının dağıtım süreçlerini otomatikleştirmenin kendi yollarını buldu. Web uygulamalarınızı nasıl dağıttığınız büyük ölçüde kullanılan teknoloji yığınına bağlı olduğundan, bu otomasyon çözümleri birbirinden farklılık gösterir. Örneğin, bir PHP web sitesini otomatik olarak dağıtmakla ilgili adımlar, bir Node.js web uygulamasını dağıtmaktan farklıdır. Dokku gibi oldukça genel olan başka çözümler de var ve bu şeyler (yapı paketleri olarak adlandırılır) daha geniş bir teknoloji yığınıyla iyi çalışır.
Bu öğreticide, GitHub web kancaları, derleme paketleri ve Procfiles kullanarak web uygulaması dağıtımlarınızı otomatikleştirmek için oluşturabileceğiniz basit bir aracın arkasındaki temel fikirlere göz atacağız. Bu yazımızda inceleyeceğimiz prototip programın kaynak kodu GitHub'da mevcut.
Web Uygulamalarına Başlarken
Web uygulamamızın dağıtımını otomatikleştirmek için basit bir Go programı yazacağız. Go'ya aşina değilseniz, bu makale boyunca kullanılan kod yapıları oldukça basit olduğundan ve anlaşılması kolay olması gerektiğinden, takip etmekten çekinmeyin. Eğer öyle hissediyorsanız, muhtemelen tüm programı istediğiniz bir dile kolayca aktarabilirsiniz.
Başlamadan önce, sisteminizde Go dağıtımının kurulu olduğundan emin olun. Go'yu yüklemek için resmi belgelerde belirtilen adımları takip edebilirsiniz.
Ardından GitHub deposunu klonlayarak bu aracın kaynak kodunu indirebilirsiniz. Bu, bu makaledeki kod parçacıkları karşılık gelen dosya adlarıyla etiketlendiğinden, takip etmenizi kolaylaştırmalıdır. Dilerseniz hemen deneyebilirsiniz.
Go'yu bu program için kullanmanın en büyük avantajlarından biri, onu minimum dış bağımlılıklara sahip olacak şekilde oluşturabilmemizdir. Bizim durumumuzda, bu programı bir sunucuda çalıştırmak için Git ve Bash'in kurulu olduğundan emin olmamız yeterli. Go programları statik olarak bağlantılı ikili dosyalar halinde derlendiğinden, programı bilgisayarınızda derleyebilir, sunucuya yükleyebilir ve neredeyse sıfır çabayla çalıştırabilirsiniz. Günümüzün diğer popüler dillerinin çoğu için bu, yalnızca dağıtım otomatikleştiricinizi çalıştırmak için sunucuda yüklü bazı devasa çalışma zamanı ortamı veya yorumlayıcı gerektirir. Go programları, doğru yapıldığında, CPU ve RAM üzerinde de çok kolay olabilir - bu, bunun gibi programlardan istediğiniz bir şeydir.
GitHub Web kancaları
GitHub Webhooks ile GitHub deponuzu, depoda bir şey her değiştiğinde veya bazı kullanıcılar barındırılan depoda belirli eylemler gerçekleştirdiğinde olayları yaymak üzere yapılandırmak mümkündür. Bu, kullanıcıların bu olaylara abone olmalarına ve havuzunuzun çevresinde gerçekleşen çeşitli olayların URL çağrıları aracılığıyla bilgilendirilmelerine olanak tanır.
Bir web kancası oluşturmak çok basittir:
- Deponuzun ayarlar sayfasına gidin
- Sol gezinme menüsünde "Web kancaları ve Hizmetler"e tıklayın
- “Web kancası ekle” düğmesine tıklayın
- Bir URL ve isteğe bağlı olarak bir sır (alıcının yükü doğrulamasını sağlayacak) ayarlayın
- Gerektiğinde formda başka seçimler yapın
- Yeşil “Web kancası ekle” düğmesine tıklayarak formu gönderin
GitHub, Web kancaları ve tam olarak nasıl çalıştıkları, çeşitli olaylara yanıt olarak yükte hangi bilgilerin teslim edildiği vb. hakkında kapsamlı belgeler sağlar. Bu makalenin amacı doğrultusunda, özellikle her seferinde biri tarafından yayılan "push" olayıyla ilgileniyoruz. herhangi bir depo şubesine iter.
Yapı paketleri
Yapı paketleri bugünlerde oldukça standart. Birçok PaaS sağlayıcısı tarafından kullanılan derleme paketleri, bir uygulama dağıtılmadan önce yığının nasıl yapılandırılacağını belirlemenize olanak tanır. Web uygulamanız için derleme paketleri yazmak gerçekten kolaydır, ancak çoğu zaman web'de hızlı bir arama yapmak, web uygulamanız için herhangi bir değişiklik yapmadan kullanabileceğiniz bir derleme paketi bulabilir.
Heroku gibi PaaS'a uygulama dağıttıysanız, yapı paketlerinin ne olduğunu ve bunları nerede bulacağınızı zaten biliyor olabilirsiniz. Heroku, derleme paketlerinin yapısı hakkında bazı kapsamlı belgelere ve iyi oluşturulmuş bazı popüler derleme paketlerinin bir listesine sahiptir.
Otomasyon programımız, uygulamayı başlatmadan önce hazırlamak için derleme komut dosyasını kullanacaktır. Örneğin, Heroku tarafından oluşturulan bir Node.js, package.json dosyasını ayrıştırır, uygun bir Node.js sürümünü indirir ve uygulama için NPM bağımlılıklarını indirir. Her şeyi basit tutmak için prototip programımızda derleme paketleri için kapsamlı bir desteğimiz olmayacağını belirtmekte fayda var. Şimdilik, buildpack betiklerinin Bash ile çalıştırılmak üzere yazıldığını ve olduğu gibi yeni bir Ubuntu kurulumunda çalışacaklarını varsayacağız. Gerekirse, daha ezoterik ihtiyaçları karşılamak için bunu gelecekte kolayca genişletebilirsiniz.
profiller
Procfile'lar, uygulamanızda sahip olduğunuz çeşitli işlem türlerini tanımlamanıza izin veren basit metin dosyalarıdır. Çoğu basit uygulama için, ideal olarak, HTTP isteklerini işleyen süreç olan tek bir "web" süreciniz olur.
Profiller yazmak kolaydır. Adını, ardından iki nokta üst üste koyarak ve ardından işlemi oluşturacak komutu yazarak satır başına bir işlem türü tanımlayın:
<type>: <command>
Örneğin, Node.js tabanlı bir web uygulaması ile çalışıyorsanız, web sunucusunu başlatmak için “node index.js” komutunu çalıştırırsınız. Kodun temel dizininde basitçe bir Procfile oluşturabilir ve onu aşağıdakilerle “Procfile” olarak adlandırabilirsiniz:
web: node index.js
Kodu aldıktan sonra bunları otomatik olarak başlatabilmemiz için uygulamaların Procfiles'de işlem türlerini tanımlamasını isteyeceğiz.
Olayları İşleme
Programımıza GitHub'dan gelen POST isteklerini almamızı sağlayacak bir HTTP sunucusu eklemeliyiz. GitHub'dan gelen bu istekleri işlemek için bazı URL yollarını ayırmamız gerekecek. Bu gelen yükleri işleyecek olan fonksiyon şuna benzer:
// hook.go type HookOptions struct { App *App Secret string } func NewHookHandler(o *HookOptions) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { evName := r.Header.Get("X-Github-Event") if evName != "push" { log.Printf("Ignoring '%s' event", evName) return } body, err := ioutil.ReadAll(r.Body) if err != nil { http.Error(w, "Internal Server Error", http.StatusInternalServerError) return } if o.Secret != "" { ok := false for _, sig := range strings.Fields(r.Header.Get("X-Hub-Signature")) { if !strings.HasPrefix(sig, "sha1=") { continue } sig = strings.TrimPrefix(sig, "sha1=") mac := hmac.New(sha1.New, []byte(o.Secret)) mac.Write(body) if sig == hex.EncodeToString(mac.Sum(nil)) { ok = true break } } if !ok { log.Printf("Ignoring '%s' event with incorrect signature", evName) return } } ev := github.PushEvent{} err = json.Unmarshal(body, &ev) if err != nil { log.Printf("Ignoring '%s' event with invalid payload", evName) http.Error(w, "Bad Request", http.StatusBadRequest) return } if ev.Repo.FullName == nil || *ev.Repo.FullName != o.App.Repo { log.Printf("Ignoring '%s' event with incorrect repository name", evName) http.Error(w, "Bad Request", http.StatusBadRequest) return } log.Printf("Handling '%s' event for %s", evName, o.App.Repo) err = o.App.Update() if err != nil { return } }) }
Bu yükü oluşturan olay türünü doğrulayarak başlıyoruz. Sadece “push” olayı ile ilgilendiğimiz için diğer tüm olayları görmezden gelebiliriz. Web kancasını yalnızca "push" olayları yayınlayacak şekilde yapılandırsanız bile, kanca uç noktanızda almayı bekleyebileceğiniz en az bir tür olay daha olacaktır: "ping". Bu olayın amacı, webhook'un GitHub'da başarıyla yapılandırılıp yapılandırılmadığını belirlemektir.
Daha sonra, gelen isteğin tüm gövdesini okuruz, web kancamızı yapılandırmak için kullanacağımız aynı sırrı kullanarak HMAC-SHA1'ini hesaplarız ve gelen yükün geçerliliğini, başlığın içerdiği imza ile karşılaştırarak belirleriz. istek. Programımızda, secret yapılandırılmamışsa bu doğrulama adımını yok sayarız. Bir yan not olarak, en azından burada ne kadar veriyle uğraşmak isteyeceğimiz konusunda bir tür üst sınır olmadan tüm metni okumak akıllıca bir fikir olmayabilir, ancak kritik yönlere odaklanmak için işleri basit tutalım. bu aracın.
Ardından, gelen yükün sırasını kaldırmak için Git için GitHub istemci kitaplığından bir yapı kullanırız. Bunun bir “push” olayı olduğunu bildiğimiz için PushEvent yapısını kullanabiliriz. Ardından, yükü yapının bir örneğine ayırmak için standart json kodlama kitaplığını kullanırız. Birkaç akıl sağlığı kontrolü yapıyoruz ve her şey yolundaysa, uygulamamızı güncellemeye başlayan işlevi çağırıyoruz.
Uygulama Güncelleme
Web kancası uç noktamızda bir olay bildirimi aldığımızda, uygulamamızı güncellemeye başlayabiliriz. Bu yazıda, bu mekanizmanın oldukça basit bir uygulamasına bir göz atacağız ve kesinlikle iyileştirmeler için yer olacak. Ancak, bazı temel otomatik dağıtım işlemlerine başlamamızı sağlayacak bir şey olmalı.
Yerel Depoyu Başlatma
Bu işlem, uygulamayı ilk kez dağıtmaya çalışıp çalışmadığımızı belirlemek için basit bir kontrolle başlayacaktır. Bunu, yerel depo dizininin olup olmadığını kontrol ederek yapacağız. Eğer mevcut değilse, önce yerel depomuzu başlatacağız:
// app.go func (a *App) initRepo() error { log.Print("Initializing repository") err := os.MkdirAll(a.repoDir, 0755) // Check err cmd := exec.Command("git", "--git-dir="+a.repoDir, "init") cmd.Stderr = os.Stderr err = cmd.Run() // Check err cmd = exec.Command("git", "--git-dir="+a.repoDir, "remote", "add", "origin", fmt.Sprintf("[email protected]:%s.git", a.Repo)) cmd.Stderr = os.Stderr err = cmd.Run() // Check err return nil }
App yapısındaki bu yöntem, yerel depoyu başlatmak için kullanılabilir ve mekanizmaları son derece basittir:
- Mevcut değilse, yerel depo için bir dizin oluşturun.
- Çıplak bir depo oluşturmak için “git init” komutunu kullanın.
- Yerel depomuza uzak depo için bir URL ekleyin ve "origin" olarak adlandırın.
Başlatılmış bir depomuz olduğunda, değişiklikleri almak basit olmalıdır.
Değişiklikler Alınıyor
Uzak depodan değişiklikleri almak için sadece bir komut çağırmamız gerekiyor:
// app.go func (a *App) fetchChanges() error { log.Print("Fetching changes") cmd := exec.Command("git", "--git-dir="+a.repoDir, "fetch", "-f", "origin", "master:master") cmd.Stderr = os.Stderr return cmd.Run() }
Bu şekilde yerel depomuz için bir "git getirme" yaparak, belirli senaryolarda Git'in hızlı ileri saramamasıyla ilgili sorunları önleyebiliriz. Zorla getirmeler güvenmeniz gereken bir şey değildir, ancak uzak deponuza bir zorlama zorlamanız gerekiyorsa, bu onu zarafetle halledecektir.

Uygulama Derleme
Dağıtılan uygulamalarımızı derlemek için derleme paketlerinden komut dosyaları kullandığımızdan, buradaki görevimiz nispeten kolay:
// app.go func (a *App) compileApp() error { log.Print("Compiling application") _, err := os.Stat(a.appDir) if !os.IsNotExist(err) { err = os.RemoveAll(a.appDir) // Check err } err = os.MkdirAll(a.appDir, 0755) // Check err cmd := exec.Command("git", "--git-dir="+a.repoDir, "--work-tree="+a.appDir, "checkout", "-f", "master") cmd.Dir = a.appDir cmd.Stderr = os.Stderr err = cmd.Run() // Check err buildpackDir, err := filepath.Abs("buildpack") // Check err cmd = exec.Command("bash", filepath.Join(buildpackDir, "bin", "detect"), a.appDir) cmd.Dir = buildpackDir cmd.Stderr = os.Stderr err = cmd.Run() // Check err cacheDir, err := filepath.Abs("cache") // Check err err = os.MkdirAll(cacheDir, 0755) // Check err cmd = exec.Command("bash", filepath.Join(buildpackDir, "bin", "compile"), a.appDir, cacheDir) cmd.Dir = a.appDir cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr return cmd.Run() }
Önceki uygulama dizinimizi (varsa) kaldırarak başlıyoruz. Ardından, yeni bir tane oluşturuyoruz ve ana dalın içeriğini ona göre kontrol ediyoruz. Ardından, uygulamanın işleyebileceğimiz bir şey olup olmadığını belirlemek için yapılandırılmış yapı paketindeki "algılama" komut dosyasını kullanırız. Ardından gerekirse buildpack derleme işlemi için bir “cache” dizini oluşturuyoruz. Bu dizin yapılar arasında devam ettiğinden, yeni bir dizin oluşturmamız gerekmeyebilir, çünkü bir önceki derleme sürecinden bir dizin zaten mevcut olacaktır. Bu noktada, derleme paketinden "derleme" komut dosyasını çağırabilir ve başlatmadan önce uygulama için gerekli her şeyi hazırlamasını sağlayabiliriz. Derleme paketleri düzgün bir şekilde çalıştırıldığında, önceden önbelleğe alınmış kaynakların önbelleğe alınmasını ve yeniden kullanılmasını kendi başlarına halledebilirler.
Uygulamayı Yeniden Başlatma
Bu otomatik dağıtım sürecini uygulamamızda, derleme sürecine başlamadan önce eski süreçleri durduracağız ve ardından derleme aşaması tamamlandıktan sonra yeni süreçlere başlayacağız. Bu, aracın uygulanmasını kolaylaştırsa da, otomatik dağıtım sürecini iyileştirmenin potansiyel olarak şaşırtıcı bazı yollarını bırakır. Bu prototipi geliştirmek için, muhtemelen güncellemeler sırasında sıfır kesinti süresi sağlayarak başlayabilirsiniz. Şimdilik daha basit yaklaşımla devam edeceğiz:
// app.go func (a *App) stopProcs() error { log.Print(".. stopping processes") for _, n := range a.nodes { err := n.Stop() if err != nil { return err } } return nil } func (a *App) startProcs() error { log.Print("Starting processes") err := a.readProcfile() if err != nil { return err } for _, n := range a.nodes { err = n.Start() if err != nil { return err } } return nil }
Prototipimizde, her bir düğümün uygulamanın örneklerinden birine karşılık gelen bir süreç olduğu (bu aracı sunucuda başlatmadan önce yapılandırıldığı gibi) bir dizi düğüm üzerinde yineleme yaparak çeşitli işlemleri durdurur ve başlatırız. Aracımızda, her bir düğüm için sürecin mevcut durumunu takip ediyoruz. Ayrıca onlar için bireysel günlük dosyaları da tutuyoruz. Tüm düğümler başlatılmadan önce, her birine belirli bir bağlantı noktası numarasından başlayarak benzersiz bir bağlantı noktası atanır:
// node.go func NewNode(app *App, name string, no int, port int) (*Node, error) { logFile, err := os.OpenFile(filepath.Join(app.logsDir, fmt.Sprintf("%s.%d.txt", name, no)), os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666) if err != nil { return nil, err } n := &Node{ App: app, Name: name, No: no, Port: port, stateCh: make(chan NextState), logFile: logFile, } go func() { for { next := <-n.stateCh if n.State == next.State { if next.doneCh != nil { close(next.doneCh) } continue } switch next.State { case StateUp: log.Printf("Starting process %s.%d", n.Name, n.No) cmd := exec.Command("bash", "-c", "for f in .profile.d/*; do source $f; done; "+n.Cmd) cmd.Env = append(cmd.Env, fmt.Sprintf("HOME=%s", n.App.appDir)) cmd.Env = append(cmd.Env, fmt.Sprintf("PORT=%d", n.Port)) cmd.Env = append(cmd.Env, n.App.Env...) cmd.Dir = n.App.appDir cmd.Stdout = n.logFile cmd.Stderr = n.logFile err := cmd.Start() if err != nil { log.Printf("Process %s.%d exited", n.Name, n.No) n.State = StateUp } else { n.Process = cmd.Process n.State = StateUp } if next.doneCh != nil { close(next.doneCh) } go func() { err := cmd.Wait() if err != nil { log.Printf("Process %s.%d exited", n.Name, n.No) n.stateCh <- NextState{ State: StateDown, } } }() case StateDown: log.Printf("Stopping process %s.%d", n.Name, n.No) if n.Process != nil { n.Process.Kill() n.Process = nil } n.State = StateDown if next.doneCh != nil { close(next.doneCh) } } } }() return n, nil } func (n *Node) Start() error { n.stateCh <- NextState{ State: StateUp, } return nil } func (n *Node) Stop() error { doneCh := make(chan int) n.stateCh <- NextState{ State: StateDown, doneCh: doneCh, } <-doneCh return nil }
Bir bakışta, bu şimdiye kadar yaptıklarımızdan biraz daha karmaşık görünebilir. Her şeyi kavramayı kolaylaştırmak için yukarıdaki kodu dört parçaya ayıralım. İlk ikisi “NewNode” işlevi içindedir. Çağrıldığında, "Düğüm" yapısının bir örneğini doldurur ve bu Düğüme karşılık gelen işlemi başlatmaya ve durdurmaya yardımcı olan bir Go rutini oluşturur. Diğer ikisi “Düğüm” yapısındaki iki yöntemdir: “Başlat” ve “Durdur”. Bu düğüm başına Go rutininin izlemekte olduğu belirli bir kanaldan bir "mesaj" geçirilerek bir süreç başlatılır veya durdurulur. İşlemi başlatmak için bir mesaj veya durdurmak için farklı bir mesaj iletebilirsiniz. Bir işlemi başlatmak veya durdurmakla ilgili gerçek adımlar tek bir Go rutininde gerçekleştiğinden, yarış koşullarını alma şansı yoktur.
Go rutini, "stateCh" kanalı aracılığıyla bir "mesaj" beklediği sonsuz bir döngü başlatır. Bu kanala iletilen mesaj, düğümden işlemi başlatmasını isterse ("case StateUp" içinde), komutu yürütmek için Bash'i kullanır. Bunu yaparken, komutu kullanıcı tanımlı ortam değişkenlerini kullanacak şekilde yapılandırır. Ayrıca standart çıktı ve hata akışlarını önceden tanımlanmış bir günlük dosyasına yönlendirir.
Öte yandan, bir süreci durdurmak (“case StateDown” içinde), basitçe onu öldürür. Muhtemelen yaratıcı olabileceğiniz yer burasıdır ve süreci öldürmek yerine hemen bir SIGTERM gönderin ve gerçekten öldürmeden önce birkaç saniye bekleyin, böylece sürece zarif bir şekilde durma şansı verin.
“Başlat” ve “Durdur” yöntemleri, uygun mesajın kanala iletilmesini kolaylaştırır. “Başlat” yönteminden farklı olarak, “Durdur” yöntemi, geri dönmeden önce süreçlerin öldürülmesini bekler. "Başlat", işlemi başlatmak için kanala bir mesaj iletir ve geri döner.
Hepsini Birleştirmek
Son olarak, tek yapmamız gereken, programın ana işlevi içindeki her şeyi kablolamak. Burası, yapılandırma dosyasını yükleyip ayrıştıracağımız, yapı paketini güncelleyeceğimiz, uygulamamızı bir kez güncellemeye çalışacağımız ve GitHub'dan gelen "push" olay yüklerini dinlemek için web sunucusunu başlatacağımız yerdir:
// main.go func main() { cfg, err := toml.LoadFile("config.tml") catch(err) url, ok := cfg.Get("buildpack.url").(string) if !ok { log.Fatal("buildpack.url not defined") } err = UpdateBuildpack(url) catch(err) // Read configuration options into variables repo (string), env ([]string) and procs (map[string]int) // ... app, err := NewApp(repo, env, procs) catch(err) err = app.Update() catch(err) secret, _ := cfg.Get("hook.secret").(string) http.Handle("/hook", NewHookHandler(&HookOptions{ App: app, Secret: secret, })) addr, ok := cfg.Get("core.addr").(string) if !ok { log.Fatal("core.addr not defined") } err = http.ListenAndServe(addr, nil) catch(err) }
Derleme paketlerinin basit Git depoları olmasını istediğimizden, "UpdateBuildpack" (buildpack.go'da uygulanır), derleme paketinin yerel kopyasını güncellemek için depo URL'siyle yalnızca bir "git klonu" ve bir "git çekme" gerçekleştirir.
Denemek
Depoyu henüz klonlamadıysanız, şimdi yapabilirsiniz. Go dağıtımı kuruluysa, programı hemen derlemek mümkün olmalıdır.
mkdir hopper cd hopper export GOPATH=`pwd` go get github.com/hjr265/toptal-hopper go install github.com/hjr265/toptal-hopper
Bu komut dizisi, hopper adında bir dizin oluşturacak, onu GOPATH olarak ayarlayacak, kodu gerekli Go kitaplıkları ile birlikte GitHub'dan alacak ve programı “$GOPATH/bin” dizininde bulabileceğiniz bir ikili dosyada derleyecektir. Bunu bir sunucuda kullanmadan önce, bunu test etmek için basit bir web uygulaması oluşturmamız gerekiyor. Kolaylık sağlamak için basit bir "Merhaba, dünya" benzeri Node.js web uygulaması oluşturdum ve bu test için çatallayıp yeniden kullanabileceğiniz başka bir GitHub deposuna yükledim. Ardından, derlenmiş ikili dosyayı bir sunucuya yüklememiz ve aynı dizinde bir yapılandırma dosyası oluşturmamız gerekiyor:
# config.tml [core] addr = ":26590" [buildpack] url = "https://github.com/heroku/heroku-buildpack-nodejs.git" [app] repo = "hjr265/hopper-hello.js" [app.env] GREETING = "Hello" [app.procs] web = 1 [hook] secret = ""
Yapılandırma dosyamızdaki ilk seçenek olan “core.addr”, programımızın dahili web sunucusunun HTTP portunu yapılandırmamızı sağlayan şeydir. Yukarıdaki örnekte, programın “http://{host}:26590/hook” konumunda “push” olay yüklerini dinlemesini sağlayacak “:26590” olarak ayarladık. GitHub web kancasını kurarken, "{host}" yerine sunucunuzu gösteren alan adı veya IP adresi koymanız yeterlidir. Bir tür güvenlik duvarı kullanıyorsanız, bağlantı noktasının açık olduğundan emin olun.
Ardından, Git URL'sini ayarlayarak bir yapı paketi seçiyoruz. Burada Heroku'nun Node.js derleme paketini kullanıyoruz.
“Uygulama” altında, uygulama kodunu barındıran GitHub deponuzun tam adını “repo” olarak ayarladık. Örnek uygulamayı “https://github.com/hjr265/hopper-hello.js” adresinde barındırdığım için, havuzun tam adı “hjr265/hopper-hello.js”.
Ardından, uygulama için bazı ortam değişkenleri ve ihtiyaç duyduğumuz her tür işlemin sayısını belirledik. Ve son olarak, gelen "push" olay yüklerini doğrulayabilmemiz için bir sır seçiyoruz.
Artık sunucu üzerinde otomasyon programımızı başlatabiliriz. Her şey doğru yapılandırılmışsa (depoya sunucudan erişilebilmesi için SSH anahtarlarını dağıtmak dahil), program kodu getirmeli, ortamı derleme paketini kullanarak hazırlamalı ve uygulamayı başlatmalıdır. Şimdi tek yapmamız gereken, Push olaylarını yaymak ve onu “http://{host}:26590/hook”a yönlendirmek için GitHub deposunda bir web kancası kurmak. “{host}” yerine sunucunuzu gösteren alan adı veya IP adresi eklediğinizden emin olun.
Son olarak test etmek için örnek uygulamada bazı değişiklikler yapın ve bunları GitHub'a gönderin. Otomasyon aracının hemen harekete geçtiğini ve sunucudaki depoyu güncellediğini, uygulamayı derlediğini ve yeniden başlattığını fark edeceksiniz.
Çözüm
Deneyimlerimizin çoğundan bunun oldukça faydalı bir şey olduğunu söyleyebiliriz. Bu yazımızda hazırladığımız prototip uygulaması, olduğu gibi bir üretim sisteminde kullanmak isteyeceğiniz bir uygulama olmayabilir. İyileştirme için bir ton yer var. Bunun gibi bir araç daha iyi hata işleme özelliğine sahip olmalı, zarif kapatmaları/yeniden başlatmaları desteklemelidir ve işlemleri doğrudan çalıştırmak yerine içermek için Docker gibi bir şey kullanmak isteyebilirsiniz. Özel durumunuz için tam olarak neye ihtiyacınız olduğunu bulmak ve bunun için bir otomasyon programı bulmak daha akıllıca olabilir. Ya da belki de tüm İnternet'te bulunan çok daha kararlı, zamana göre test edilmiş başka bir çözüm kullanın. Ancak, çok özelleştirilmiş bir şey sunmak istiyorsanız, umarım bu makale bunu yapmanıza yardımcı olur ve web uygulaması dağıtım sürecinizi otomatikleştirerek uzun vadede ne kadar zaman ve emekten tasarruf edebileceğinizi gösterir.