Buat API TODO untuk Golang dengan Kubernetes

Halo semuanya! Menjelang peluncuran kursus Platform Infrastruktur berbasis Kubernetes, kami menyiapkan terjemahan materi menarik lainnya.



Artikel ini ditujukan bagi para pendatang baru di Kubernetes, yang akan tertarik untuk memahami contoh praktis tentang cara menulis Golang API untuk mengelola daftar TODO, dan kemudian bagaimana cara menyebarkannya ke Kubernetes.

Setiap pengembang menyukai daftar TODO yang bagus, bukan? Bagaimana lagi kita bisa mengatur diri kita sendiri sebaliknya?


Setiap pengembang menyukai aplikasi TODO yang bagus, bukan?

Kami akan mulai dengan meninjau daftar komponen yang diperlukan, kemudian pergi untuk mengkonfigurasi Kubernetes, menyediakan database Postgresql, dan kemudian menginstal kerangka kerja aplikasi yang akan membantu kami dengan mudah menggunakan Go API di Kubernetes, tanpa berfokus pada detail.

Kami akan membuat dua titik akhir di API - satu untuk membuat catatan TODO baru, yang lain untuk memilih semua catatan TODO.

Semua kode dari tutorial ini tersedia di GitHub.

Daftar komponen yang diperlukan:


  • Docker yang Diinstal Secara Lokal

Kubernetes menjalankan kode dalam gambar kontainer, jadi Anda harus menginstal Docker di komputer Anda.

Instal Docker: https://www.docker.com
Daftarkan akun Docker Hub untuk menyimpan gambar Docker Anda: https://hub.docker.com/

  • Cluster Kubernetes


Anda dapat memilih salah satu kluster lokal atau jauh, tetapi yang mana yang lebih baik? Opsi yang disederhanakan, seperti k3d, berfungsi di komputer mana pun yang dapat dijalankan Docker, jadi menjalankan cluster lokal tidak lagi membutuhkan banyak RAM. Cluster jarak jauh juga bisa menjadi solusi yang sangat efisien, tetapi perlu diingat bahwa semua gambar Docker Anda perlu diunggah dan diunggah untuk setiap perubahan.

Instal k3d: https://github.com/rancher/k3d

k3d tidak akan menginstal kubectl (ini adalah CLI untuk Kubernetes), jadi instal secara terpisah dari sini: https://kubernetes.io/docs/tasks/tools / install-kubectl

  • Golang


Anda juga perlu menginstal Golang dengan IDE di komputer Anda. Go gratis dan Anda dapat mengunduhnya untuk MacOS, Windows atau Linux dari tautan berikut: https://golang.org/dl

  • IDE


Saya akan merekomendasikan menggunakan Visual Studio Code, ini gratis dan memiliki satu set plugin untuk Go yang dapat Anda tambahkan. Beberapa rekan lebih suka Goland dari Jetbrains. Jika Anda seorang programmer Java, Anda mungkin lebih suka membayar untuk Goland, karena akan mengingatkan Anda pada produk mereka yang lain.

Instal VSCode atau Golang .

Buat sebuah cluster


Anda perlu menginstal semua perangkat lunak yang ditentukan di bagian sebelumnya.

Buat cluster baru menggunakan k3d:


k3d create

Konfigurasikan kubectl sehingga menunjuk ke sebuah cluster baru. Ingatlah bahwa banyak kluster dapat digunakan dari satu komputer, jadi sangat penting untuk menunjuk ke yang benar:

export KUBECONFIG="$(k3d get-kubeconfig --name='k3s-default')"


Pastikan bahwa cluster memiliki setidaknya satu simpul. Di sini Anda dapat melihat bahwa saya telah menginstal Kubernetes 1.17 - versi yang relatif baru:

kubectl get node
NAME                     STATUS   ROLES    AGE   VERSION
k3d-k3s-default-server   Ready    master   48s   v1.17.0+k3s.1


Kami akan menyimpan catatan TODO kami di tabel database, jadi sekarang kita perlu menginstalnya. Postgresql adalah database relasional populer yang dapat kita instal dalam sebuah cluster menggunakan grafik Helm.

Instal bahtera


arkade adalah CLI Go, mirip dengan "buatan" atau "apt-get", tetapi untuk aplikasi Kubernetes. Ini menggunakan proyek Helm, kubectl, atau CLI untuk menginstal proyek atau produk ke cluster Anda.

curl -sLS https://dl.get-arkade.dev | sudo sh


Sekarang instal Postgresql

arkade install postgresql
===================================================================== = PostgreSQL has been installed.                                    =
=====================================================================


Anda juga akan melihat informasi tentang string koneksi dan cara memulai CLI Postgresql melalui gambar Docker di dalam cluster.

Anda dapat memperoleh informasi ini kapan saja menggunakan arkade info postgresql.

Kami akan merancang tata letak tabel

CREATE TABLE todo (
 id              INT GENERATED ALWAYS AS IDENTITY,
 description     text NOT NULL,
 created_date    timestamp NOT NULL,
 completed_date  timestamp NOT NULL
);


Jalankan arkade info postgresqluntuk mendapatkan informasi koneksi lagi. Seharusnya terlihat seperti ini:

export POSTGRES_PASSWORD=$(kubectl get secret --namespace default postgresql -o jsonpath="{.data.postgresql-password}" | base64 --decode)
kubectl run postgresql-client --rm --tty -i --restart='Never' --namespace default --image docker.io/bitnami/postgresql:11.6.0-debian-9-r0 --env="PGPASSWORD=$POSTGRES_PASSWORD" --command -- psql --host postgresql -U postgres -d postgres -p 5432


Sekarang Anda memiliki command prompt :, postgres = #Anda dapat membuat tabel dengan menyalinnya di dalam, lalu jalankan \dtuntuk memperlihatkan tabel:

postgres=# \dt
List of relations
Schema | Name | Type  |  Owner
--------+------+-------+----------
public | todo | table | postgres
(1 row)


Instal kerangka kerja aplikasi


Sama seperti pengembang PHP mempercepat alur kerja mereka menggunakan LAMP (Linux Apache + Mysql + PHP) dan pengembang Rails menggunakan tumpukan yang sudah disiapkan, pengembang Kubernetes dapat menggunakan kerangka kerja aplikasi.
Tumpukan PLONK adalah singkatan dari Prometheus, Linux, OpenFaaS, NATS, dan Kubernetes.
  • Prometheus menyediakan metrik, penskalaan otomatis, dan kemampuan pengamatan untuk menguji kesehatan sistem Anda, memungkinkannya merespons lonjakan permintaan dan memangkas biaya dengan menyediakan metrik untuk membuat keputusan tentang penskalaan ke nol.
  • Linux, meskipun bukan satu-satunya pilihan untuk menjalankan beban kerja di Kubernetes, adalah standar dan termudah untuk digunakan.
  • Awalnya, OpenFaaS menyediakan fungsi portabel untuk pengembang, tetapi berfungsi dengan baik ketika menggunakan API dan layanan mikro. Fleksibilitasnya berarti bahwa setiap wadah Docker dengan server HTTP dapat digunakan dan dikelola.
  • NATS adalah proyek CNCF populer yang digunakan untuk pengiriman pesan dan pub / sub. Dalam tumpukan PLONK, ia menyediakan kemampuan untuk mengeksekusi permintaan secara tidak sinkron dan untuk antri.
  • Kubernetes adalah alasan kita ada di sini. Ini menyediakan infrastruktur terukur, penyembuhan diri, dan deklaratif. Dan jika Anda tidak lagi membutuhkan bagian dari semua keagungan ini, API-nya disederhanakan dengan OpenFaaS.


Instal tumpukan PLONK melalui bahtera:

arkade install openfaas


Baca pesan informasi dan jalankan setiap perintah:

  • Instal faas-cli.
  • Dapatkan kata sandi Anda.
  • Redirect gateway OpenFaaS UI (melalui port 8080)
  • Dan masuk ke sistem melalui CLI.


Seperti sebelumnya, Anda dapat menerima pesan informasi menggunakan info openfaas.

Sebarkan API Go Pertama Anda


Ada beberapa cara untuk membuat API Go menggunakan PLONK. Opsi pertama adalah menggunakan Dockerfile dan secara manual menentukan port TCP, pemeriksaan kesehatan, server HTTP, dan sebagainya. Ini dapat dilakukan dengan menggunakan faas-cli new --lang dockerfile API_NAME, tetapi ada cara yang lebih sederhana dan lebih otomatis.

Cara kedua adalah dengan menggunakan templat bawaan yang ditawarkan oleh Function Store:

faas-cli template store list | grep go
go                       openfaas           Classic Golang template
golang-http              openfaas-incubator Golang HTTP template
golang-middleware        openfaas-incubator Golang Middleware template


Karena kami ingin membuat API gaya HTTP tradisional, templat golang-middleware akan paling tepat.

Di awal tutorial, Anda mendaftar dengan akun Docker Hub Anda untuk menyimpan gambar Docker Anda. Setiap beban kerja Kubernet harus tertanam dalam gambar Docker sebelum dapat digunakan.

Tarik pola khusus:

faas-cli template store pull golang-middleware


Gunakan Scaffold untuk API Anda dengan golang-middleware dan nama pengguna Anda di Docker Hub:

export PREFIX=alexellis2
export LANG=golang-middleware
export API_NAME=todo
faas-cli new --lang $LANG --prefix $PREFIX $API_NAME


Anda akan melihat dua file yang dihasilkan:

  • ./todo.yml - menyediakan cara untuk mengkonfigurasi, menyebarkan, dan menginstal templat dan nama
  • ./todo/handler.go - di sini Anda menulis kode dan menambahkan file atau paket lain yang Anda butuhkan


Mari kita lakukan beberapa pengeditan dan kemudian kembangkan kode.

package function
import (
   "net/http"
   "encoding/json"
)
type Todo struct {
   Description string `json:"description"`
}
func Handle(w http.ResponseWriter, r *http.Request) {
   todos := []Todo{}
   todos = append(todos, Todo{Description: "Run faas-cli up"})
   res, _ := json.Marshal(todos)
   w.WriteHeader(http.StatusOK)
   w.Header().Set("Content-Type", "application/json")
   w.Write([]byte(res))
}


Jika Anda tidak menggunakan VScode dan pluginnya untuk mengedit dan memformat kode, kemudian jalankan setelah setiap perubahan untuk memastikan bahwa file diformat dengan benar.

gofmt -w -s ./todo/handler.go


Sekarang sebarkan kode - pertama buat gambar baru, luncurkan di Hub Docker dan sebarkan ke cluster menggunakan OpenFaaS API:

Invoke your endpoint when ready:
curl http://127.0.0.1:8080/function/todo
{
"description": "Run faas-cli up"
}


Izinkan pengguna membuat catatan TODO baru


Sekarang mari kita izinkan pengguna untuk membuat entri baru dalam daftar TODO mereka. Pertama, Anda perlu menambahkan tautan atau "ketergantungan" untuk Pergi ke perpustakaan Postgresql.

Kita bisa mendapatkannya menggunakan modul vending atau Go yang diperkenalkan di Go 1.11 dan diinstal secara default di Go 1.13.

Edit handler.godan tambahkan modul yang kita butuhkan untuk mengakses Postgresql:

import (
   "database/sql"
   _ "github.com/lib/pq"
...


Agar modul Go untuk mendeteksi dependensi, kita perlu mendeklarasikan sesuatu di dalam file, yang akan kita gunakan nanti. Jika tidak, maka VSCode akan menghapus baris-baris ini ketika disimpan.

Tambahkan ini di bawah impor dalam file

var db *sql.DB


Selalu jalankan perintah ini di todofolder di mana ia berada handler.go, dan bukan di level root c todo.yml.

Inisialisasi modul Go yang baru:

cd todo/
ls
handler.go
export GO111MODULE=on
go mod init


Sekarang perbarui file menggunakan pq library:

go get
go mod tidy
cat go.mod
module github.com/alexellis/todo1/todo
go 1.13
require github.com/lib/pq v1.3.0


Apa pun yang ada di dalam go.mod, salin isinya keGO_REPLACE.txt

cat go.mod > GO_REPLACE.txt


Sekarang mari kita pastikan bahwa perakitan masih berfungsi sebelum menambahkan kode yang dimasukkan.

faas-cli build -f todo.yml --build-arg GO111MODULE=on


Anda mungkin memperhatikan bahwa sekarang kami masuk --build-arguntuk melaporkan pola penggunaan modul Go.

Selama perakitan, Anda akan melihat bahwa modul diunduh sesuai kebutuhan dari Internet.

Step 16/29 : RUN go test ./... -cover
---> Running in 9a4017438500
go: downloading github.com/lib/pq v1.3.0
go: extracting github.com/lib/pq v1.3.0
go: finding github.com/lib/pq v1.3.0
?       github.com/alexellis/todo1/todo [no test files]
Removing intermediate container 9a4017438500


Mengkonfigurasi Rahasia untuk Akses Postgresql


Kita dapat membuat kumpulan koneksi init (), metode yang hanya akan berjalan sekali ketika program dimulai.

// init       .   ,     ,   /   /

func init() {
       if _, err := os.Stat("/var/openfaas/secrets/password"); err == nil {
               password, _ := sdk.ReadSecret("password")
               user, _ := sdk.ReadSecret("username")
               host, _ := sdk.ReadSecret("host")
               dbName := os.Getenv("postgres_db")
               port := os.Getenv("postgres_port")
               sslmode := os.Getenv("postgres_sslmode")
               connStr := "postgres://" + user + ":" + password + "@" + host + ":" + port + "/" + dbName + "?sslmode=" + sslmode
var err error
               db, err = sql.Open("postgres", connStr)
               if err != nil {
                       panic(err.Error())
               }
               err = db.Ping()
               if err != nil {
                       panic(err.Error())
               }
       }
}


Seperti yang Anda perhatikan, beberapa informasi diambil dari apa yang os.Getenvdibaca dari lingkungan. Saya akan menganggap nilai-nilai ini non-rahasia, mereka ditetapkan dalam file todo.y.

Selebihnya, seperti kata sandi dan host, yang bersifat rahasia, disimpan dalam rahasia Kubernetes .

Anda dapat membuatnya melalui faas-cli secret createatau melalui kubectl create secret generic -n openfaas-fn.

String sdk.ReadSecretberasal dari OpenFaaS Cloud SDK, dengan impor berikut: github.com/openfaas/openfaas-cloud/sdk. Itu membaca file rahasia dari disk dan mengembalikan nilai atau kesalahan.

Dapatkan nilai rahasia dari arkade info postgresql.

Sekarang untuk setiap kata sandi, lakukan hal berikut:

export POSTGRES_PASSWORD=$(kubectl get secret --namespace default postgresql -o jsonpath="{.data.postgresql-password}" | base64 --decode)
export USERNAME="postgres"
export PASSWORD=$POSTGRES_PASSWORD
export HOST="postgresql.default"
faas-cli secret create username --from-literal $USERNAME
faas-cli secret create password --from-literal $PASSWORD
faas-cli secret create host --from-literal $HOST


Periksa rahasia untuk ketersediaan dan kepatuhan:

faas-cli secret ls
NAME
username
password
host
# And via kubectl:
kubectl get secret -n openfaas-fn
NAME                  TYPE                                  DATA   AGE
username              Opaque                                1      13s
password              Opaque                                1      13s
host                  Opaque                                1      12s


Edit file YAML kami dan tambahkan yang berikut:

secrets:
   - host
   - password
   - username
   environment:
     postgres_db: postgres
     postgres_sslmode: "disable"
     postgres_port: 5432


Selanjutnya, perbarui modul Go dan jalankan build lagi:

cd todo
go get
go mod tidy
cd ..
faas-cli build -f todo.yml --build-arg GO111MODULE=on
Successfully built d2c609f8f559
Successfully tagged alexellis2/todo:latest
Image: alexellis2/todo:latest built.
[0] < Building todo done in 22.50s.
[0] Worker done.
Total build time: 22.50s


Majelis berfungsi seperti yang diharapkan, jadi mari kita jalankan faas-clidengan argumen yang sama untuk memulai dan menggunakan gambar. Jika kredensial dan konfigurasi SQL benar, kami tidak akan melihat kesalahan dalam log, namun, jika mereka salah, kami akan mendapatkan kode panik di init ().

Periksa log:

faas-cli logs todo
2020-03-26T14:10:03Z Forking - ./handler []
2020-03-26T14:10:03Z 2020/03/26 14:10:03 Started logging stderr from function.
2020-03-26T14:10:03Z 2020/03/26 14:10:03 Started logging stdout from function.
2020-03-26T14:10:03Z 2020/03/26 14:10:03 OperationalMode: http
2020-03-26T14:10:03Z 2020/03/26 14:10:03 Timeouts: read: 10s, write: 10s hard: 10s.
2020-03-26T14:10:03Z 2020/03/26 14:10:03 Listening on port: 8080
2020-03-26T14:10:03Z 2020/03/26 14:10:03 Metrics listening on port: 8081
2020-03-26T14:10:03Z 2020/03/26 14:10:03 Writing lock-file to: /tmp/.lock


Sementara semuanya terlihat bagus, sekarang mari kita coba memanggil titik akhir:

echo | faas-cli invoke todo -f todo.yml
2020-03-26T14:11:02Z 2020/03/26 14:11:02 POST / - 200 OK - ContentLength: 35


Jadi, sekarang kami telah berhasil membuat koneksi ke database, dan kami dapat melakukan insert. Bagaimana kita tahu itu? Karena itu db.Ping ()mengembalikan kesalahan, jika tidak maka akan membuat panik:

err = db.Ping()
if err != nil {
   panic(err.Error())
}


Ikuti tautan untuk detail lebih lanjut tentang paket database / sql .

Menulis kode penyisipan


Kode ini menyisipkan baris baru ke dalam tabel tododan menggunakan sintaksis khusus di mana nilainya tidak dilampirkan dalam tanda kutip, tetapi diganti dengan kode db.Query. Dalam "masa lalu" pemrograman LAMP, kesalahan umum yang menyebabkan banyak sistem menjadi tidak aman adalah kurangnya sanitasi data input dan penyatuan input pengguna langsung ke pernyataan SQL.

Bayangkan seseorang memasukkan deskripsi; drop table todoItu tidak akan terlalu menyenangkan.

Oleh karena itu, kita menjalankan db.Query, lalu meneruskan pernyataan SQL menggunakan $1, $2dll untuk setiap nilai, dan kemudian kita bisa mendapatkan hasil dan / atau kesalahan. Kita juga harus menutup hasil ini, jadi gunakan penundaan untuk ini.

func insert(description string) error {
       res, err := db.Query(`insert into todo (id, description, created_date) values (DEFAULT, $1, now());`,
               description)
       if err != nil {
               return err
       }
       defer res.Close()
       return nil
}


Sekarang mari kita hubungkan ini ke kode.

func Handle(w http.ResponseWriter, r *http.Request) {
       if r.Method == http.MethodPost && r.URL.Path == "/create" {
               defer r.Body.Close()
               body, _ := ioutil.ReadAll(r.Body)
               if err := insert(string(body)); err != nil {
                       http.Error(w, fmt.Sprintf("unable to insert todo: %s", err.Error()), http.StatusInternalServerError)
               }
       }
}


Mari kita gunakan dan jalankan.

echo | faas-cli invoke todo -f todo.yml
curl http://127.0.0.1:8080/function/todo/create --data "faas-cli build"
curl http://127.0.0.1:8080/function/todo/create --data "faas-cli push"
curl http://127.0.0.1:8080/function/todo/create --data "faas-cli deploy"


Periksa log API:

faas-cli logs todo
2020-03-26T14:35:29Z 2020/03/26 14:35:29 POST /create - 200 OK - ContentLength: 0


Periksa isi tabel menggunakan pgsql:

export POSTGRES_PASSWORD=$(kubectl get secret --namespace default postgresql -o jsonpath="{.data.postgresql-password}" | base64 --decode)
kubectl run postgresql-client --rm --tty -i --restart='Never' --namespace default --image docker.io/bitnami/postgresql:11.6.0-debian-9-r0 --env="PGPASSWORD=$POSTGRES_PASSWORD" --command -- psql --host postgresql -U postgres -d postgres -p 5432
postgres=# select * from todo;
id |   description   |        created_date        | completed_date
----+-----------------+----------------------------+----------------
1 | faas-cli build  | 2020-03-26 14:36:03.367789 |
2 | faas-cli push   | 2020-03-26 14:36:03.389656 |
3 | faas-cli deploy | 2020-03-26 14:36:03.797881 |


Selamat, Anda sekarang memiliki API TODO yang dapat menerima permintaan masuk melalui curlatau klien HTTP lainnya dan menuliskannya ke tabel database.

Catat Permintaan


Mari kita membuat fungsi baru untuk meminta catatan TODO dari tabel:

func selectTodos() ([]Todo, error) {
   var error err
   var todos []Todo
   return todos, err
}


Kami tidak dapat memberi nama metode pemilihan ini karena ini adalah kata kunci yang disediakan untuk bekerja dengan goroutine.

Sekarang hubungkan metode ke pengendali utama:

} else if r.Method == http.MethodGet && r.URL.Path == "/list" {
   todos, err := selectTodos()
   if err != nil {
       http.Error(w, fmt.Sprintf("unable to get todos: %s", err.Error()), http.StatusInternalServerError)
   }
   out, _ := json.Marshal(todos)
   w.Header().Set("Content-Type", "application/json")
   w.Write(out)
}


Sekarang ada bidang tambahan untuk tanggal dalam skema data kami, perbarui struktur Todo:

type Todo struct {
   ID int `json:"id"`
   Description   string `json:"description"`
   CreatedDate   *time.Time `json:"created_date"`
   CompletedDate *time.Time `json:"completed_date"`
}


Sekarang mari kita tambahkan selectTodos()kode permintaan ke metode kami :

func selectTodos() ([]Todo, error) {
       rows, getErr := db.Query(`select id, description, created_date, completed_date from todo;`)
   if getErr != nil {
       return []Todo{}, errors.Wrap(getErr, "unable to get from todo table")
   }
   todos := []Todo{}
   defer rows.Close()
   for rows.Next() {
       result := Todo{}
       scanErr := rows.Scan(&result.ID, &result.Description, &result.CreatedDate, &result.CompletedDate)
       if scanErr != nil {
           log.Println("scan err:", scanErr)
       }
       todos = append(todos, result)
   }
   return todos, nil
}


Seperti sebelumnya, kita perlu menunda menutup baris untuk permintaan. Setiap nilai dimasukkan ke dalam struktur baru menggunakan metode rows.Scan. Di akhir metode, kami memiliki konten Todo.

Mari mencoba:

faas-cli up -f todo.yml --build-arg GO111MODULE=on
curl http://127.0.0.1:8080/function/todo/list


Inilah hasilnya:

[
 {
   "id": 2,
   "description": "faas-cli build",
   "created_date": "2020-03-26T14:36:03.367789Z",
   "completed_date": null
 },
 {
   "id": 3,
   "description": "faas-cli push",
   "created_date": "2020-03-26T14:36:03.389656Z",
   "completed_date": null
 },
 {
   "id": 4,
   "description": "faas-cli deploy",
   "created_date": "2020-03-26T14:36:03.797881Z",
   "completed_date": null
 }
]


Untuk menghapus nilai, kita dapat memperbarui anotasi struktur dengan menambahkan omitempty:

CompletedDate *time.Time `json:"completed_date,omitempty"`


Untuk meringkas pekerjaan yang dilakukan


Kami belum selesai, tetapi ini adalah saat yang tepat untuk berhenti dan meninjau kembali apa yang telah kami capai sejauh ini. Kita:

  • Instal Go, Docker, kubectl dan VSCode (IDE)
  • Dikerahkan Kubernetes di komputer lokal kami
  • Menginstal Postgresql menggunakan bahtera dan helm3
  • Menginstal OpenFaaS dan tumpukan PLONK untuk pengembang aplikasi Kubernetes
  • Membuat API REST statis awal menggunakan Go dan golang-middlewaretemplat OpenFaaS
  • Menambahkan fungsi "masukkan" ke API TODO kami
  • Menambahkan fungsi pilih ke API TODO kami


Contoh kode lengkap yang telah kami buat sejauh ini tersedia di akun GitHub saya: alexellis / kubernetes-todo-go-app

Kemudian kita dapat melakukan lebih banyak lagi, misalnya:

  • Menambahkan Otentikasi Menggunakan Token Pembawa Statis
  • Buat halaman web menggunakan HTML statis atau React template untuk merender daftar TODO dan membuat elemen baru.
  • Menambahkan dukungan multi-pengguna ke API


Dan banyak lagi. Kami juga dapat mempelajari tumpukan PLONK dan menggunakan dasbor Grafana untuk mulai memantau API kami dan memahami berapa banyak sumber daya yang digunakan dengan dasbor Kubernetes atau server metrik (diinstal menggunakan arkade install).

Pelajari lebih lanjut tentang arkade: https://get-arkade.dev

Hadiri lokakarya OpenFaaS untuk mempelajari lebih lanjut tentang hal di atas: https://github.com/openfaas/workshop/

Anda dapat berlangganan buletin Pembaruan Orang Dalam premium saya di
https: / /www.alexellis.io/
dan di twitter saya Alex Ellis



Pelajari lebih lanjut tentang kursus



All Articles