Semaphore継続的インテグレーションとデリバリーを使用してNode.jsアプリケーションを構築してDigitalOceanKubernetesにデプロイする方法
著者は、 Open Internet / Free Speech Fund を選択して、 Write forDOnationsプログラムの一環として寄付を受け取りました。
序章
Kubernetes を使用すると、ユーザーは1つのコマンドで復元力のあるスケーラブルなサービスを作成できます。 良すぎると思われるものと同様に、問題があります。最初に適切な Docker イメージを準備し、徹底的にテストする必要があります。
継続的インテグレーション(CI)は、更新のたびにアプリケーションをテストする方法です。 これを手動で行うのは面倒でエラーが発生しやすいですが、CIプラットフォームはテストを実行し、エラーを早期にキャッチして、エラーが発生したポイントを特定します。 リリースと展開の手順は、多くの場合、複雑で時間がかかり、信頼性の高いビルド環境が必要です。 継続的デリバリー(CD)を使用すると、人間の介入なしに、更新ごとにアプリケーションを構築およびデプロイできます。
プロセス全体を自動化するには、継続的インテグレーションおよびデリバリー(CI / CD)プラットフォームであるセマフォを使用します。
このチュートリアルでは、Node.jsを使用して名簿APIサービスを構築します。 APIは、データベース内の人を作成、削除、および検索するための単純な RESTfulAPIインターフェースを公開します。 Git を使用して、コードをGitHubにプッシュします。 次に、セマフォを使用してアプリケーションをテストし、Dockerイメージを構築して、 DigitalOceanKubernetesクラスターにデプロイします。 データベースの場合、 DigitalOcean ManagedDatabasesを使用してPostgreSQLクラスターを作成します。
前提条件
読み進める前に、次のものがあることを確認してください。
- DigitalOceanアカウントとパーソナルアクセストークン。 パーソナルアクセストークンの作成に従って、アカウントにトークンを設定します。
- DockerHubアカウント。
- GitHubアカウント。
- セマフォアカウント。 GitHubアカウントでサインアップできます。
- プロジェクト用の
addressbook
という新しいGitHubリポジトリ。 リポジトリを作成するときは、このリポジトリをREADME チェックボックスで初期化し、 Add.gitignoreメニューでNodeを選択します。 詳細については、GitHubのリポジトリの作成ヘルプページをフォローしてください。 - Git がローカルマシンにインストールされ、がGitHubアカウントで動作するようにを設定します。 慣れていない場合や復習が必要な場合は、Gitの使用方法リファレンスガイドを読むことを検討してください。
- curlがローカルマシンにインストールされています。
- Node.jsがローカルマシンにインストールされています。 このチュートリアルでは、Node.jsバージョン
10.16.0
を使用します。
ステップ1—データベースとKubernetesクラスターを作成する
アプリケーションを強化するサービス(DigitalOceanデータベースクラスターとDigitalOcean Kubernetesクラスター)をプロビジョニングすることから始めます。
DigitalOceanアカウントにログインし、プロジェクトを作成します。 プロジェクトを使用すると、アプリケーションを構成するすべてのリソースを整理できます。 プロジェクトをaddressbook
と呼びます。
次に、PostgreSQLクラスターを作成します。 PostgreSQLデータベースサービスはアプリケーションのデータを保持します。 利用可能な最新バージョンを選択できます。 サービスの準備が整うまでに数分かかるはずです。
PostgreSQLサービスの準備ができたら、データベースとユーザーを作成します。 データベース名をaddessbook_db
に設定し、ユーザー名をaddressbook_user
に設定します。 新しいユーザー用に生成されたパスワードをメモします。 データベースは、PostgreSQLのデータを整理する方法です。 通常、各アプリケーションには独自のデータベースがありますが、これに関する厳格なルールはありません。 アプリケーションは、ユーザー名とパスワードを使用してデータベースにアクセスし、データを保存および取得できるようにします。
最後に、Kubernetesクラスターを作成します。 データベースが実行されているのと同じリージョンを選択します。 cluserにaddressbook-server
という名前を付け、ノード数を3
に設定します。
ノードのプロビジョニング中に、アプリケーションの構築を開始できます。
ステップ2—アプリケーションの作成
展開するアドレス帳アプリケーションを作成しましょう。 まず、前提条件で作成したGitHubリポジトリのクローンを作成して、GitHubが作成した.gitignore
ファイルのローカルコピーを作成します。手動で作成しなくても、アプリケーションコードをすばやくコミットできます。リポジトリ。 ブラウザを開き、新しいGitHubリポジトリに移動します。 クローンまたはダウンロードボタンをクリックして、提供されたURLをコピーします。 Gitを使用して、空のリポジトリをマシンに複製します。
git clone https://github.com/your_github_username/addressbook
プロジェクトディレクトリを入力します。
cd addressbook
リポジトリのクローンを作成すると、アプリの作成を開始できます。 2つのコンポーネントを構築します。データベースと対話するモジュールと、HTTPサービスを提供するモジュールです。 データベースモジュールは、名簿データベースから人を保存および取得する方法を認識し、HTTPモジュールは要求を受信し、それに応じて応答します。
厳密には必須ではありませんが、コードの記述中にコードをテストすることをお勧めします。そのため、テストモジュールも作成します。 これは、アプリケーションの計画されたレイアウトです。
database.js
:データベースモジュール。 データベース操作を処理します。app.js
:エンドユーザーモジュールとメインアプリケーション。 ユーザーが接続するためのHTTPサービスを提供します。database.test.js
:データベースモジュールをテストします。
さらに、プロジェクトの package.json ファイルが必要になります。このファイルには、プロジェクトとその必要な依存関係が記述されています。 エディターを使用して手動で作成することも、npmを使用してインタラクティブに作成することもできます。 npm init
コマンドを実行して、ファイルをインタラクティブに作成します。
npm init
コマンドは、開始するためにいくつかの情報を要求します。 例に示すように値を入力します。 リストに回答が表示されない場合は、回答を空白のままにします。これは、括弧内のデフォルト値を使用します。
npm outputpackage name: (addressbook) addressbook version: (1.0.0) 1.0.0 description: Addressbook API and database entry point: (index.js) app.js test command: git repository: URL for your GitHub repository keywords: author: Sammy the Shark <[email protected]>" license: (ISC) About to write to package.json: { "name": "addressbook", "version": "1.0.0", "description": "Addressbook API and database", "main": "app.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "author": "", "license": "ISC" } Is this OK? (yes) yes
これで、コードの記述を開始できます。 データベースは、開発しているサービスの中核です。 他のコンポーネントを作成する前に、適切に設計されたデータベースモデルを用意することが不可欠です。 したがって、データベースコードから始めるのは理にかなっています。
アプリケーションのすべてのビットをコーディングする必要はありません。 Node.jsには、再利用可能なモジュールの大規模なライブラリがあります。 たとえば、プロジェクトに Sequelize ORM モジュールがある場合は、SQLクエリを作成する必要はありません。 このモジュールは、データベースをJavaScriptオブジェクトおよびメソッドとして処理するインターフェースを提供します。 また、データベースにテーブルを作成することもできます。 SequelizeがPostgreSQLで動作するには、pgモジュールが必要です。
npm install
コマンドと--save
オプションを使用してモジュールをインストールします。これにより、npm
にモジュールをpackage.json
に保存するように指示されます。 次のコマンドを実行して、sequelize
とpg
の両方をインストールします。
npm install --save sequelize pg
データベースコードを保持する新しいJavaScriptファイルを作成します。
nano database.js
次の行をファイルに追加して、sequelize
モジュールをインポートします。
database.js
const Sequelize = require('sequelize'); . . .
次に、その行の下で、システム環境から取得するデータベース接続パラメーターを使用してsequelize
オブジェクトを初期化します。 これにより、コードにクレデンシャルが含まれないため、コードをGitHubにプッシュするときに誤ってクレデンシャルを共有することがなくなります。 process.env
を使用して環境変数にアクセスし、JavaScriptの||
演算子を使用して未定義の変数のデフォルトを設定できます。
database.js
. . . const sequelize = new Sequelize(process.env.DB_SCHEMA || 'postgres', process.env.DB_USER || 'postgres', process.env.DB_PASSWORD || '', { host: process.env.DB_HOST || 'localhost', port: process.env.DB_PORT || 5432, dialect: 'postgres', dialectOptions: { ssl: process.env.DB_SSL == "true" } }); . . .
次に、Person
モデルを定義します。 例が複雑になりすぎないように、作成するフィールドはfirstName
とlastName
の2つだけで、どちらも文字列値を格納します。 次のコードを追加して、モデルを定義します。
database.js
. . . const Person = sequelize.define('Person', { firstName: { type: Sequelize.STRING, allowNull: false }, lastName: { type: Sequelize.STRING, allowNull: true }, }); . . .
これにより2つのフィールドが定義され、firstName
がallowNull: false
で必須になります。 Sequelizeのモデル定義ドキュメントには、使用可能なデータ型とオプションが示されています。
最後に、sequelize
オブジェクトとPerson
モデルをエクスポートして、他のモジュールがそれらを使用できるようにします。
database.js
. . . module.exports = { sequelize: sequelize, Person: Person };
開発中いつでも呼び出すことができる別のファイルにテーブル作成スクリプトがあると便利です。 これらのタイプのファイルは移行と呼ばれます。 このコードを保持する新しいファイルを作成します。
nano migrate.js
これらの行をファイルに追加して、定義したデータベースモデルをインポートし、sync()
関数を呼び出してデータベースを初期化します。これにより、モデルのテーブルが作成されます。
merge.js
var db = require('./database.js'); db.sequelize.sync();
アプリケーションは、システム環境変数でデータベース接続情報を探しています。 .env
というファイルを作成して、これらの値を保持します。これらの値は、開発中に環境にロードされます。
nano .env
次の変数宣言をファイルに追加します。 DB_HOST
、DB_PORT
、およびDB_PASSWORD
をDigitalOceanPostgreSQLクラスターに関連付けられているものに設定していることを確認してください。
.env
export DB_SCHEMA=addressbook_db export DB_USER=addressbook_user export DB_PASSWORD=your_db_user_password export DB_HOST=your_db_cluster_host export DB_PORT=your_db_cluster_port export DB_SSL=true export PORT=3000
ファイルを保存します。
警告:環境ファイルをソース管理にチェックインしないでください。 彼らは通常機密情報を持っています。
リポジトリの作成時にデフォルトの.gitignore
ファイルを定義したため、このファイルはすでに無視されています。
これで、データベースを初期化する準備が整いました。 環境ファイルをインポートして、migrate.js
を実行します。
source ./.env node migrate.js
これにより、データベーステーブルが作成されます。
Output Executing (default): CREATE TABLE IF NOT EXISTS "People" ("id" SERIAL , "firstName" VARCHAR(255) NOT NULL, "lastName" VARCHAR(255), "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL, PRIMARY KEY ("id")); Executing (default): SELECT i.relname AS name, ix.indisprimary AS primary, ix.indisunique AS unique, ix.indkey AS indkey, array_agg(a.attnum) as column_indexes, array_agg(a.attname) AS column_names, pg_get_indexdef(ix.indexrelid) AS definition FROM pg_class t, pg_class i, pg_index ix, pg_attribute a WHERE t.oid = ix.indrelid AND i.oid = ix.indexrelid AND a.attrelid = t.oid AND t.relkind = 'r' and t.relname = 'People' GROUP BY i.relname, ix.indexrelid, ix.indisprimary, ix.indisunique, ix.indkey ORDER BY i.relname;
出力には2つのコマンドが表示されます。 最初のものは、定義に従ってPeople
テーブルを作成します。 2番目のコマンドは、PostgreSQLカタログでテーブルを検索して、テーブルが実際に作成されたことを確認します。
コードのテストを作成することをお勧めします。 テストを使用すると、コードの動作を検証できます。 システムの各関数、メソッド、またはその他の部分のチェックを記述し、手動でテストしなくても、期待どおりに機能することを確認できます。
jest テストフレームワークは、Node.jsアプリケーションに対するテストを作成するのに最適です。 Jestは、プロジェクト内のファイルをスキャンしてテストファイルを探し、それらを1つずつ実行します。 --save-dev
オプションを指定してJestをインストールします。これにより、npm
に、モジュールがプログラムを実行する必要がないことを通知しますが、これはアプリケーション開発の依存関係です。
npm install --save-dev jest
データベースからレコードを挿入、読み取り、および削除できることを確認するためのテストを作成します。 これらのテストは、データベース接続とアクセス許可が適切に構成されていることを確認し、後でCI/CDパイプラインで使用できるいくつかのテストも提供します。
database.test.js
ファイルを作成します。
nano database.test.js
次のコンテンツを追加します。 データベースコードをインポートすることから始めます。
database.test.js
const db = require('./database'); . . .
データベースを使用できるようにするには、beforeAll
関数内でsync()
を呼び出します。
database.test.js
. . . beforeAll(async () => { await db.sequelize.sync(); }); . . .
最初のテストでは、データベースに個人レコードを作成します。 sequelize
ライブラリは、すべてのクエリを非同期で実行します。つまり、クエリの結果を待機しません。 テストで結果を待機させて検証できるようにするには、async
およびawait
キーワードを使用する必要があります。 このテストでは、create()
メソッドを呼び出して、データベースに新しい行を挿入します。 expect
を使用して、person.id
列を1
と比較します。 別の値を取得すると、テストは失敗します。
database.test.js
. . . test('create person', async () => { expect.assertions(1); const person = await db.Person.create({ id: 1, firstName: 'Sammy', lastName: 'Davis Jr.', email: '[email protected]' }); expect(person.id).toEqual(1); }); . . .
次のテストでは、findByPk()
メソッドを使用して、id=1
で行を取得します。 次に、firstName
とlastName
の値を検証します。 もう一度、async
とawait
を使用します。
database.test.js
. . . test('get person', async () => { expect.assertions(2); const person = await db.Person.findByPk(1); expect(person.firstName).toEqual('Sammy'); expect(person.lastName).toEqual('Davis Jr.'); }); . . .
最後に、データベースから人を削除することをテストします。 destroy()
メソッドは、id=1
を持つ人を削除します。 それが機能したことを確認するために、もう一度その人を取得して、戻り値がnull
であることを確認してみてください。
database.test.js
. . . test('delete person', async () => { expect.assertions(1); await db.Person.destroy({ where: { id: 1 } }); const person = await db.Person.findByPk(1); expect(person).toBeNull(); }); . . .
最後に、このコードを追加して、すべてのテストが終了したら、close()
を使用してデータベースへの接続を閉じます。
app.js
. . . afterAll(async () => { await db.sequelize.close(); });
ファイルを保存します。
jest
コマンドは、プログラムのテストスイートを実行しますが、コマンドをpackage.json
に保存することもできます。 このファイルをエディターで開きます。
nano package.json
scripts
キーワードを見つけて、既存のtest
行(単なるプレースホルダー)を置き換えます。 テストコマンドはjest
です。
. . . "scripts": { "test": "jest" }, . . .
これで、npm run test
を呼び出してテストスイートを呼び出すことができます。 これは長いコマンドかもしれませんが、後でjest
コマンドを変更する必要がある場合は、外部サービスを変更する必要はありません。 npm run test
を引き続き呼び出すことができます。
テストを実行します。
npm run test
次に、結果を確認します。
Output console.log node_modules/sequelize/lib/sequelize.js:1176 Executing (default): CREATE TABLE IF NOT EXISTS "People" ("id" SERIAL , "firstName" VARCHAR(255) NOT NULL, "lastName" VARCHAR(255), "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL, PRIMARY KEY ("id")); console.log node_modules/sequelize/lib/sequelize.js:1176 Executing (default): SELECT i.relname AS name, ix.indisprimary AS primary, ix.indisunique AS unique, ix.indkey AS indkey, array_agg(a.attnum) as column_indexes, array_agg(a.attname) AS column_names, pg_get_indexdef(ix.indexrelid) AS definition FROM pg_class t, pg_class i, pg_index ix, pg_attribute a WHERE t.oid = ix.indrelid AND i.oid = ix.indexrelid AND a.attrelid = t.oid AND t.relkind = 'r' and t.relname = 'People' GROUP BY i.relname, ix.indexrelid, ix.indisprimary, ix.indisunique, ix.indkey ORDER BY i.relname; console.log node_modules/sequelize/lib/sequelize.js:1176 Executing (default): INSERT INTO "People" ("id","firstName","lastName","createdAt","updatedAt") VALUES ($1,$2,$3,$4,$5) RETURNING *; console.log node_modules/sequelize/lib/sequelize.js:1176 Executing (default): SELECT "id", "firstName", "lastName", "createdAt", "updatedAt" FROM "People" AS "Person" WHERE "Person"."id" = 1; console.log node_modules/sequelize/lib/sequelize.js:1176 Executing (default): DELETE FROM "People" WHERE "id" = 1 console.log node_modules/sequelize/lib/sequelize.js:1176 Executing (default): SELECT "id", "firstName", "lastName", "createdAt", "updatedAt" FROM "People" AS "Person" WHERE "Person"."id" = 1; PASS ./database.test.js ✓ create person (344ms) ✓ get person (173ms) ✓ delete person (323ms) Test Suites: 1 passed, 1 total Tests: 3 passed, 3 total Snapshots: 0 total Time: 5.315s Ran all test suites.
テストされたデータベースコードを使用して、アドレス帳のユーザーを管理するAPIサービスを構築できます。
HTTPリクエストを処理するには、 ExpressWebフレームワークを使用します。 Expressをインストールし、npm install
を使用して依存関係として保存します。
npm install --save express
body-parser モジュールも必要です。これは、HTTPリクエストの本文にアクセスするために使用します。 これも依存関係としてインストールします。
npm install --save body-parser
メインアプリケーションファイルapp.js
を作成します。
nano app.js
express
、body-parser
、およびdatabase
モジュールをインポートします。 次に、app
というexpress
モジュールのインスタンスを作成して、サービスを制御および構成します。 app.use()
を使用して、ミドルウェアなどの機能を追加します。 これを使用してbody-parser
モジュールを追加し、アプリケーションがurlエンコード文字列を読み取れるようにします。
app.js
var express = require('express'); var bodyParser = require('body-parser'); var db = require('./database'); var app = express(); app.use(bodyParser.urlencoded({ extended: true })); . . .
次に、アプリケーションにルートを追加します。 ルートは、アプリやWebサイトのボタンに似ています。 これらは、アプリケーションで何らかのアクションをトリガーします。 ルートは、一意のURLをアプリケーションのアクションにリンクします。 各ルートは特定のパスを提供し、異なる操作をサポートします。
定義する最初のルートは、/person/$ID
パスに対するGET
要求を処理します。これにより、指定されたIDを持つユーザーのデータベースレコードが表示されます。 Expressは、要求された$ID
の値をreq.params.id
変数に自動的に設定します。
アプリケーションは、JSON文字列としてエンコードされた個人データで応答する必要があります。 データベーステストで行ったように、findByPk()
メソッドを使用してIDで個人を取得し、HTTPステータス200
(OK)でリクエストに応答し、個人レコードをJSONとして送信します。 次のコードを追加します。
app.js
. . . app.get("/person/:id", function(req, res) { db.Person.findByPk(req.params.id) .then( person => { res.status(200).send(JSON.stringify(person)); }) .catch( err => { res.status(500).send(JSON.stringify(err)); }); }); . . .
エラーが発生すると、catch()
のコードが実行されます。 たとえば、データベースがダウンしている場合、接続は失敗し、代わりにこれが実行されます。 問題が発生した場合は、HTTPステータスを500
(内部サーバーエラー)に設定し、エラーメッセージをユーザーに返送してください。
別のルートを追加して、データベースに人を作成します。 このルートはPUT
リクエストを処理し、req.body
から個人のデータにアクセスします。 create()
メソッドを使用して、データベースに行を挿入します。
app.js
. . . app.put("/person", function(req, res) { db.Person.create({ firstName: req.body.firstName, lastName: req.body.lastName, id: req.body.id }) .then( person => { res.status(200).send(JSON.stringify(person)); }) .catch( err => { res.status(500).send(JSON.stringify(err)); }); }); . . .
DELETE
要求を処理する別のルートを追加します。これにより、アドレス帳からレコードが削除されます。 まず、IDを使用してレコードを見つけ、次にdestroy
メソッドを使用してレコードを削除します。
app.js
. . . app.delete("/person/:id", function(req, res) { db.Person.destroy({ where: { id: req.params.id } }) .then( () => { res.status(200).send(); }) .catch( err => { res.status(500).send(JSON.stringify(err)); }); }); . . .
また、便宜上、/all
パスを使用してデータベース内のすべてのユーザーを取得するルートを追加します。
app.js
. . . app.get("/all", function(req, res) { db.Person.findAll() .then( persons => { res.status(200).send(JSON.stringify(persons)); }) .catch( err => { res.status(500).send(JSON.stringify(err)); }); }); . . .
最後のルートが1つ残っています。 リクエストが以前のルートのいずれにも一致しなかった場合は、ステータスコード404
(見つかりません)を送信します。
app.js
. . . app.use(function(req, res) { res.status(404).send("404 - Not Found"); }); . . .
最後に、サービスを起動するlisten()
メソッドを追加します。 環境変数PORT
が定義されている場合、サービスはそのポートでリッスンします。 それ以外の場合は、デフォルトでポート3000
になります。
app.js
. . . var server = app.listen(process.env.PORT || 3000, function() { console.log("app is running on port", server.address().port); });
学習したように、package.json
ファイルを使用すると、テストの実行、アプリの起動、その他のタスクを実行するためのさまざまなコマンドを定義できます。これにより、多くの場合、はるかに少ない入力で一般的なコマンドを実行できます。 package.json
に新しいコマンドを追加して、アプリケーションを起動します。 ファイルを編集します。
nano package.json
start
コマンドを追加すると、次のようになります。
package.json
. . . "scripts": { "test": "jest", "start": "node app.js" }, . . .
scripts
セクションではエントリをコンマで区切る必要があるため、前の行にコンマを追加することを忘れないでください。
ファイルを保存して、初めてアプリケーションを起動します。 まず、source
を使用して環境ファイルをロードします。 これにより、変数がセッションにインポートされ、アプリケーションで使用できるようになります。 次に、npm run start
でアプリケーションを起動します。
source ./.env npm run start
アプリはポート3000
で起動します。
Outputapp is running on port 3000
ブラウザを開き、http://localhost:3000/all
に移動します。 []
を示すページが表示されます。
端末に戻り、CTRL-C
を押してアプリケーションを停止します。
今こそ、コード品質テストを追加する絶好の機会です。 リンターとも呼ばれるコード品質ツールは、プロジェクトをスキャンしてコードの問題を探します。 未使用の変数を残す、セミコロンでステートメントを終了しない、中括弧がないなどの不適切なコーディング方法は、見つけるのが難しいバグを引き起こす可能性があります。
開発依存関係として、JavaScriptリンターであるjshintツールをインストールします。
npm install --save-dev jshint
何年にもわたって、JavaScriptは更新、機能、および構文の変更を受け取りました。 この言語は、 ECMAInternationalによって「ECMAScript」という名前で標準化されています。 約年に1回、ECMAは新しい機能を備えた新しいバージョンのECMAScriptをリリースします。
デフォルトでは、jshint
は、コードがES6(ECMAScriptバージョン6)と互換性があると想定し、そのバージョンでサポートされていないキーワードが見つかるとエラーをスローします。 コードと互換性のあるバージョンを見つける必要があります。 最近のすべてのバージョンの機能テーブルを見ると、async/await
キーワードはES8まで導入されていなかったことがわかります。 データベーステストコードで両方のキーワードを使用したため、互換性のある最小バージョンがES8に設定されます。
使用しているバージョンをjshint
に伝えるには、.jshintrc
というファイルを作成します。
nano .jshintrc
ファイルにesversion
を指定します。 jshintrc
ファイルはJSONを使用するため、ファイルに新しいJSONオブジェクトを作成します。
.jshintrc
{ "esversion": 8 }
ファイルを保存して、エディターを終了します。
jshint
を実行するコマンドを追加します。 package.json
を編集します:
nano package.json
package.json
のscripts
セクションで、プロジェクトにlint
コマンドを追加します。 このコマンドは、これまでに作成したすべてのJavaScriptファイルに対してlintツールを呼び出します。
package.json
. . . "scripts": { "test": "jest", "start": "node app.js", "lint": "jshint app.js database*.js migrate.js" }, . . .
これで、リンターを実行して問題を見つけることができます。
npm run lint
エラーメッセージは表示されないはずです。
Output> jshint app.js database*.js migrate.js
エラーがある場合は、jshint
に問題のある行が表示されます。
プロジェクトを完了し、プロジェクトが機能することを確認しました。 ファイルをリポジトリに追加し、コミットして、変更をプッシュします。
git add *.js git add package*.json git add .jshintrc git commit -m 'initial commit' git push origin master
これで、DigitalOceanパーソナルアクセストークンとデータベースクレデンシャルを使用してSemaphoreを構成することから始めて、アプリケーションをテスト、ビルド、およびデプロイするようにSemaphoreを構成できます。
ステップ3—セマフォでシークレットを作成する
GitHubリポジトリに属していない情報がいくつかあります。 パスワードとAPIトークンはこの良い例です。 この機密データを別のファイルに保存して環境にロードしました。セマフォを使用する場合は、シークレットを使用して機密データを保存できます。
プロジェクトには3種類の秘密があります。
- Docker Hub:DockerHubアカウントのユーザー名とパスワード。
- DigitalOceanパーソナルアクセストークン:アプリケーションをKubernetesクラスターにデプロイします。
- 環境変数:データベースのユーザー名とパスワードの接続パラメーター用。
最初のシークレットを作成するには、ブラウザを開いてセマフォのWebサイトにログインします。 左側のナビゲーションメニューで、CONFIGURATION見出しの下にあるSecretsをクリックします。 新しいシークレットの作成ボタンをクリックします。
シークレットの名前にdockerhub
と入力します。 次に、環境変数の下に、2つの環境変数を作成します。
DOCKER_USERNAME
:DockerHubのユーザー名。DOCKER_PASSWORD
:DockerHubのパスワード。
[変更を保存]をクリックします。
DigitalOceanパーソナルアクセストークンの2番目のシークレットを作成します。 もう一度、左側のナビゲーションメニューのシークレットをクリックしてから、新しいシークレットの作成をクリックします。 このシークレットをdo-access-token
と呼び、パーソナルアクセストークンに設定された値でDO_ACCESS_TOKEN
という環境値を作成します。
秘密を保存します。
次の秘密として、環境変数を直接設定する代わりに、プロジェクトのルートから.env
ファイルをアップロードします。
env-production
という新しいシークレットを作成します。 ファイルセクションで、ファイルのアップロードリンクを押して.env
ファイルを見つけてアップロードし、Semaphoreに/home/semaphore/env-production
に配置するように指示します。
注:ファイルが非表示になっているため、コンピューターでファイルを見つけるのに問題が発生する可能性があります。 通常、CTRL+H
などの非表示ファイルを表示するためのメニュー項目またはキーの組み合わせがあります。 他のすべてが失敗した場合は、非表示になっていない名前でファイルをコピーしてみてください。
cp .env env
次に、ファイルをアップロードして、名前を元に戻します。
cp env .env
環境変数はすべて構成されています。 これで、継続的インテグレーションのセットアップを開始できます。
ステップ4—プロジェクトをSemaphoreに追加する
このステップでは、プロジェクトをSemaphoreに追加し、継続的インテグレーション(CI)パイプラインを開始します。
まず、GitHubリポジトリをSemaphoreにリンクします。
- セマフォアカウントにログインします。
- プロジェクトの横にある+アイコンをクリックします。
- リポジトリの横にあるリポジトリの追加ボタンをクリックします。
Semaphoreが接続されたので、リポジトリ内の変更を自動的に取得します。
これで、アプリケーションの継続的インテグレーションパイプラインを作成する準備が整いました。 パイプラインは、コードがビルド、テスト、およびデプロイされるために移動する必要のあるパスを定義します。 パイプラインは、GitHubリポジトリに変更があるたびに自動的に実行されます。
まず、Semaphoreが開発中に使用していたのと同じバージョンのNodeを使用していることを確認する必要があります。 マシンで実行されているバージョンを確認できます。
node -v
Outputv10.16.0
リポジトリに.nvmrc
というファイルを作成することで、使用するNode.jsのバージョンをSemaphoreに伝えることができます。 内部的には、Semaphoreはノードバージョンマネージャーを使用してNode.jsバージョンを切り替えます。 .nvmrc
ファイルを作成し、バージョンを10.16.0
に設定します。
echo '10.16.0' > .nvmrc
セマフォパイプラインは.semaphore
ディレクトリに配置されます。 ディレクトリを作成します。
mkdir .semaphore
新しいパイプラインファイルを作成します。 最初のパイプラインは常にsemaphore.yml
と呼ばれます。 このファイルでは、アプリケーションのビルドとテストに必要なすべての手順を定義します。
nano .semaphore/semaphore.yml
注:YAML形式でファイルを作成しています。 チュートリアルに示されているように、先頭のスペースを保持する必要があります。
最初の行は、セマフォファイルのバージョンを設定する必要があります。 現在の安定版はv1.0
です。 また、パイプラインには名前が必要です。 次の行をファイルに追加します。
.semaphore / semaphore.yml
version: v1.0 name: Addressbook . . .
セマフォは、タスクを実行するために仮想マシンを自動的にプロビジョニングします。 から選択できるさまざまなマシンがあります。 統合ジョブには、e1-standard-2
(2 CPU 4 GB RAM)とUbuntu18.04OSを使用します。 次の行をファイルに追加します。
.semaphore / semaphore.yml
. . . agent: machine: type: e1-standard-2 os_image: ubuntu1804 . . .
セマフォはブロックを使用してタスクを整理します。 各ブロックには、1つ以上のジョブを含めることができます。 ブロック内のすべてのジョブは並行して実行され、それぞれが分離されたマシンで実行されます。 セマフォは、ブロック内のすべてのジョブが通過するのを待ってから、次のジョブを開始します。
最初のブロックを定義することから始めます。これにより、アプリケーションをテストして実行するためのすべてのJavaScript依存関係がインストールされます。
.semaphore / semaphore.yml
. . . blocks: - name: Install dependencies task: . . .
NODE_ENV
をtest
に設定するなど、すべてのジョブに共通の環境変数を定義できるため、Node.jsはこれがテスト環境であることを認識します。 task
の後に次のコードを追加します。
.semaphore / semaphore.yml
. . . task: env_vars: - name: NODE_ENV value: test . . .
prologue セクションのコマンドは、ブロック内の各ジョブの前に実行されます。 セットアップタスクを定義するのに便利な場所です。 checkout を使用して、GitHubリポジトリのクローンを作成できます。 次に、nvm use
は、.nvmrc
で指定した適切なNode.jsバージョンをアクティブにします。 prologue
セクションを追加します。
.semaphore / semaphore.yml
task: . . . prologue: commands: - checkout - nvm use . . .
次に、このコードを追加して、プロジェクトの依存関係をインストールします。 ジョブを高速化するために、Semaphoreはcacheツールを提供しています。 cache store
を実行して、node_modules
ディレクトリをセマフォのキャッシュに保存できます。 cache
は、どのファイルとディレクトリを保存するかを自動的に判断します。 2回目にジョブが実行されると、cache restore
はディレクトリを復元します。
.semaphore / semaphore.yml
. . . jobs: - name: npm install and cache commands: - cache restore - npm install - cache store . . .
2つのジョブを実行する別のブロックを追加します。 1つはlintテストを実行し、もう1つはアプリケーションのテストスイートを実行します。
.semaphore / semaphore.yml
. . . - name: Tests task: env_vars: - name: NODE_ENV value: test prologue: commands: - checkout - nvm use - cache restore . . .
prologue
は前のブロックと同じコマンドを繰り返し、キャッシュからnode_module
を復元します。 このブロックはテストを実行するため、NODE_ENV
環境変数をtest
に設定します。
次に、ジョブを追加します。 最初のジョブは、jshintを使用してコード品質チェックを実行します。
.semaphore / semaphore.yml
. . . jobs: - name: Static test commands: - npm run lint . . .
次のジョブは単体テストを実行します。 本番データベースを使用したくないので、それらを実行するにはデータベースが必要です。 セマフォのsem-serviceは、完全に分離されたテスト環境でローカルPostgreSQLデータベースを起動できます。 ジョブが終了すると、データベースは破棄されます。 このサービスを開始し、テストを実行します。
.semaphore / semaphore.yml
. . . - name: Unit test commands: - sem-service start postgres - npm run test
.semaphore/semaphore.yml
ファイルを保存します。
次に、変更をGitHubリポジトリに追加してコミットします。
git add .nvmrc git add .semaphore/semaphore.yml git commit -m "continuous integration pipeline" git push origin master
コードがGitHubにプッシュされるとすぐに、SemaphoreはCIパイプラインを開始します。
パイプラインをクリックして、ブロックとジョブ、およびそれらの出力を表示できます。
次に、アプリケーションのDockerイメージを構築する新しいパイプラインを作成します。
ステップ5—アプリケーション用のDockerイメージを構築する
Dockerイメージは、Kubernetesデプロイメントの基本ユニットです。 イメージには、アプリケーションの実行に必要なすべてのバイナリ、ライブラリ、およびコードが含まれている必要があります。 Dockerコンテナーは軽量の仮想マシンではありませんが、そのように動作します。 Docker Hubレジストリには、すぐに使用できる数百のイメージが含まれていますが、独自のイメージを作成します。
このステップでは、新しいパイプラインを追加して、アプリのカスタムDockerイメージを構築し、DockerHubにプッシュします。
カスタムイメージを作成するには、Dockerfile
を作成します。
nano Dockerfile
Dockerfile
は画像を作成するためのレシピです。 official Node.jsディストリビューションを最初から始める代わりに、開始点として使用できます。 これをDockerfile
に追加します。
Dockerfile
FROM node:10.16.0-alpine . . .
次に、package.json
とpackage-lock.json
をコピーするコマンドを追加し、イメージ内にノードモジュールをインストールします。
Dockerfile
. . . COPY package*.json ./ RUN npm install . . .
Dockerがこのステップをキャッシュするため、最初に依存関係をインストールすると、後続のビルドが高速化されます。
次に、プロジェクトルート内のすべてのアプリケーションファイルをイメージにコピーする次のコマンドを追加します。
Dockerfile
. . . COPY *.js ./ . . .
最後に、EXPOSE
は、コンテナがアプリケーションがリッスンしているポート3000
で接続をリッスンすることを指定し、CMD
は、コンテナの起動時に実行するコマンドを設定します。 次の行をファイルに追加します。
Dockerfile
. . . EXPOSE 3000 CMD [ "npm", "run", "start" ]
ファイルを保存します。
Dockerfileが完成したら、新しいパイプラインを作成して、コードをGitHubにプッシュするときにSemaphoreがイメージを構築できるようにします。 docker-build.yml
という名前の新しいファイルを作成します。
nano .semaphore/docker-build.yml
CIパイプラインと同じボイラープレートでパイプラインを開始しますが、名前はDocker build
です。
.semaphore / docker-build.yml
version: v1.0 name: Docker build agent: machine: type: e1-standard-2 os_image: ubuntu1804 . . .
このパイプラインには、1つのブロックと1つのジョブしかありません。 手順3では、DockerHubのユーザー名とパスワードを使用してdockerhub
という名前のシークレットを作成しました。 ここでは、secrets
キーワードを使用してこれらの値をインポートします。 このコードを追加します:
.semaphore / docker-build.yml
. . . blocks: - name: Build task: secrets: - name: dockerhub . . .
Dockerイメージはリポジトリに保存されます。 公開画像の数に制限がない公式のDockerHubを使用します。 これらの行を追加してGitHubからコードをチェックアウトし、docker login
コマンドを使用してDockerHubで認証します。
.semaphore / docker-build.yml
task: . . . prologue: commands: - checkout - echo "${DOCKER_PASSWORD}" | docker login -u "${DOCKER_USERNAME}" --password-stdin . . .
各Dockerイメージは、名前とタグの組み合わせによって完全に識別されます。 名前は通常、製品またはソフトウェアに対応し、タグはソフトウェアの特定のバージョンに対応します。 たとえば、node.10.16.0
です。 タグが指定されていない場合、Dockerはデフォルトで特別なlatest
タグを使用します。 したがって、latest
タグを使用して最新の画像を参照することをお勧めします。
次のコードを追加してイメージをビルドし、DockerHubにプッシュします。
.semaphore / docker-build.yml
. . . jobs: - name: Docker build commands: - docker pull "${DOCKER_USERNAME}/addressbook:latest" || true - docker build --cache-from "${DOCKER_USERNAME}/addressbook:latest" -t "${DOCKER_USERNAME}/addressbook:$SEMAPHORE_WORKFLOW_ID" . - docker push "${DOCKER_USERNAME}/addressbook:$SEMAPHORE_WORKFLOW_ID"
Dockerがイメージをビルドするとき、既存のイメージの一部を再利用してプロセスを高速化します。 最初のコマンドは、DockerHubからlatest
イメージをプルして、再利用できるようにします。 コマンドのいずれかがゼロ以外のステータスコードを返す場合、セマフォはパイプラインを停止します。 たとえば、リポジトリにlatest
イメージがない場合、最初の試行ではないため、パイプラインは停止します。 コマンドに|| true
を追加することにより、失敗したコマンドをセマフォに無視させることができます。
2番目のコマンドは、イメージをビルドします。 この特定の画像を後で参照するために、一意の文字列でタグ付けできます。 セマフォは、ジョブにいくつかの環境変数を提供します。 そのうちの1つ、$SEMAPHORE_WORKFLOW_ID
は一意であり、ワークフロー内のすべてのパイプライン間で共有されます。 展開の後半でこのイメージを参照するのに便利です。
3番目のコマンドは、イメージをDockerHubにプッシュします。
ビルドパイプラインの準備はできていますが、メインCIパイプラインに接続しない限り、Semaphoreはビルドパイプラインを開始しません。 Promotions を使用して、複数のパイプラインをチェーンし、複雑なマルチブランチワークフローを作成できます。
メインパイプラインファイル.semaphore/semaphore.yml
を編集します。
nano .semaphore/semaphore.yml
ファイルの最後に次の行を追加します。
.semaphore / semaphore.yml
. . . promotions: - name: Dockerize pipeline_file: docker-build.yml auto_promote_on: - result: passed
auto_promote_on
は、docker build
パイプラインを開始するための条件を定義します。 この場合、semaphore.yml
ファイルで定義されているすべてのジョブが通過したときに実行されます。
新しいパイプラインをテストするには、変更したすべてのファイルをGitHubに追加、コミット、プッシュする必要があります。
git add Dockerfile git add .semaphore/docker-build.yml git add .semaphore/semaphore.yml git commit -m "docker build pipeline" git push origin master
CIパイプラインが完了すると、Dockerビルドパイプラインが開始されます。
完了すると、DockerHubリポジトリに新しいイメージが表示されます。
ビルドプロセスのテストとイメージの作成が完了しました。 次に、アプリケーションをKubernetesクラスターにデプロイするための最終的なパイプラインを作成します。
ステップ6—Kubernetesへの継続的デプロイの設定
Kubernetesデプロイメントの構成要素は、ポッドです。 ポッドは、単一のユニットとして管理されるコンテナのグループです。 ポッド内のコンテナは同時に開始および停止し、常に同じマシン上で実行され、そのリソースを共有します。 各ポッドにはIPアドレスがあります。 この場合、ポッドにはコンテナが1つだけあります。
ポッドは一時的なものです。 それらは頻繁に作成および破棄されます。 開始するまで、各ポッドにどのIPアドレスが割り当てられるかはわかりません。 これを解決するには、 services を使用します。これは、パブリックIPアドレスが固定されているため、着信接続の負荷を分散してポッドに転送できます。
ポッドを直接管理することもできますが、デプロイメントを使用してKubernetesに処理させることをお勧めします。 このセクションでは、クラスターの最終的な望ましい状態を説明する宣言型マニフェストを作成します。 マニフェストには2つのリソースがあります。
- 展開:必要に応じてクラスターノードでポッドを起動し、そのステータスを追跡します。 このチュートリアルでは3ノードのクラスターを使用しているため、3つのポッドをデプロイします。
- サービス:ユーザーのエントリポイントとして機能します。 ポート
80
(HTTP)でトラフィックをリッスンし、接続をポッドに転送します。
deployment.yml
というマニフェストファイルを作成します。
nano deployment.yml
Deployment
リソースでマニフェストを開始します。 次の内容を新しいファイルに追加して、デプロイメントを定義します。
deploy.yml
apiVersion: apps/v1 kind: Deployment metadata: name: addressbook spec: replicas: 3 selector: matchLabels: app: addressbook template: metadata: labels: app: addressbook spec: containers: - name: addressbook image: ${DOCKER_USERNAME}/addressbook:${SEMAPHORE_WORKFLOW_ID} env: - name: NODE_ENV value: "production" - name: PORT value: "$PORT" - name: DB_SCHEMA value: "$DB_SCHEMA" - name: DB_USER value: "$DB_USER" - name: DB_PASSWORD value: "$DB_PASSWORD" - name: DB_HOST value: "$DB_HOST" - name: DB_PORT value: "$DB_PORT" - name: DB_SSL value: "$DB_SSL" . . .
マニフェストのリソースごとに、apiVersion
を設定する必要があります。 デプロイメントには、安定バージョンであるapiVersion: apps/v1
を使用してください。 次に、このリソースがkind: Deployment
を使用したデプロイであることをKubernetesに伝えます。 各定義には、metadata.name
で定義された名前が必要です。
spec
セクションで、Kubernetesに目的の最終状態を通知します。 この定義では、Kubernetesがreplicas: 3
を使用して3つのポッドを作成する必要があります。
Labels は、Kubernetesリソースを整理および相互参照するために使用されるキーと値のペアです。 metadata.labels
でラベルを定義し、selector.matchLabels
で一致するラベルを探すことができます。 これは、要素を相互に接続する方法です。
キーspec.template
は、Kubernetesが各ポッドを作成するために使用するモデルを定義します。 spec.template.metadata.labels
内で、ポッドに1つのラベルapp: addressbook
を設定します。
spec.selector.matchLabels
を使用すると、展開でapp: addressbook
というラベルの付いたポッドを管理できます。 この場合、このデプロイメントにすべてのポッドの責任を負わせます。
最後に、ポッドで実行されるイメージを定義します。 spec.template.spec.containers
で画像名を設定します。 Kubernetesは、必要に応じてレジストリからイメージをプルします。 この場合、Docker Hubからプルします)。 コンテナの環境変数を設定することもできます。これは、データベース接続にいくつかの値を指定する必要があるため、幸いです。
デプロイメントマニフェストを柔軟に保つために、変数に依存することになります。 ただし、YAML形式では変数が許可されていないため、ファイルはまだ有効ではありません。 Semaphoreのデプロイメントパイプラインを定義するときに、この問題を解決します。
展開は以上です。 しかし、これはポッドを定義するだけです。 トラフィックがポッドに流れるようにするサービスがまだ必要です。 区切り文字として3つのハイフン(---
)を使用している限り、同じファイルに別のKubernetesリソースを追加できます。
次のコードを追加して、addressbook
ラベルの付いたポッドに接続するロードバランサーサービスを定義します。
deploy.yml
. . . --- apiVersion: v1 kind: Service metadata: name: addressbook-lb spec: selector: app: addressbook type: LoadBalancer ports: - port: 80 targetPort: 3000
ロードバランサーはポート80
で接続を受信し、アプリケーションがリッスンしているポッドのポート3000
に転送します。
ファイルを保存します。
次に、マニフェストを使用してアプリをデプロイするSemaphoreのデプロイメントパイプラインを作成します。 .semaphore
ディレクトリに新しいファイルを作成します。
nano .semaphore/deploy-k8s.yml
バージョン、名前、イメージを指定して、通常どおりパイプラインを開始します。
.semaphore / deploy-k8s.yml
version: v1.0 name: Deploy to Kubernetes agent: machine: type: e1-standard-2 os_image: ubuntu1804 . . .
このパイプラインには2つのブロックがあります。 最初のブロックは、アプリケーションをKubernetesクラスターにデプロイします。
ブロックを定義し、すべてのシークレットをインポートします。
.semaphore / deploy-k8s.yml
. . . blocks: - name: Deploy to Kubernetes task: secrets: - name: dockerhub - name: do-access-token - name: env-production . . .
DigitalOcean Kubernetesクラスター名を環境変数に保存して、後で参照できるようにします。
.semaphore / deploy-k8s.yml
. . . env_vars: - name: CLUSTER_NAME value: addressbook-server . . .
DigitalOcean Kubernetesクラスターは、kubectl
とdoctl
の2つのプログラムの組み合わせで管理されます。 前者はすでにセマフォのイメージに含まれていますが、後者は含まれていないため、インストールする必要があります。 prologue
セクションを使用してそれを行うことができます。
このプロローグセクションを追加します。
.semaphore / deploy-k8s.yml
. . . prologue: commands: - wget https://github.com/digitalocean/doctl/releases/download/v1.20.0/doctl-1.20.0-linux-amd64.tar.gz - tar xf doctl-1.20.0-linux-amd64.tar.gz - sudo cp doctl /usr/local/bin - doctl auth init --access-token $DO_ACCESS_TOKEN - doctl kubernetes cluster kubeconfig save "${CLUSTER_NAME}" - checkout . . .
最初のコマンドは、doctl
公式リリースとwget
をダウンロードします。 2番目のコマンドは、tar
を使用して解凍し、ローカルパスにコピーします。 doctl
をインストールすると、DigitalOcean APIで認証し、クラスターのKubernetes構成ファイルをリクエストするために使用できます。 コードをチェックアウトすると、prologue
が完了します。
次は、パイプラインの最後の部分であるクラスターへのデプロイです。
deployment.yml
にはいくつかの環境変数があり、YAMLではそれが許可されていないことに注意してください。 その結果、現在の状態のdeployment.yml
は機能しません。 これを回避するには、source
環境ファイルで変数をロードしてから、envsubst
コマンドを使用して、変数を実際の値で所定の位置に展開します。 結果として、deploy.yml
というファイルは、値が挿入された完全に有効なYAMLです。 ファイルを配置したら、kubectl apply
で展開を開始できます。
.semaphore / deploy-k8s.yml
. . . jobs: - name: Deploy commands: - source $HOME/env-production - envsubst < deployment.yml | tee deploy.yml - kubectl apply -f deploy.yml . . .
2番目のブロックは、DockerHubのイメージにlatest
タグを追加して、これがデプロイされている最新バージョンであることを示します。 Dockerログイン手順を繰り返してから、Docker Hubをプル、再タグ付け、プッシュします。
.semaphore / deploy-k8s.yml
. . . - name: Tag latest release task: secrets: - name: dockerhub prologue: commands: - checkout - echo "${DOCKER_PASSWORD}" | docker login -u "${DOCKER_USERNAME}" --password-stdin - checkout jobs: - name: docker tag latest commands: - docker pull "${DOCKER_USERNAME}/addressbook:$SEMAPHORE_WORKFLOW_ID" - docker tag "${DOCKER_USERNAME}/addressbook:$SEMAPHORE_WORKFLOW_ID" "${DOCKER_USERNAME}/addressbook:latest" - docker push "${DOCKER_USERNAME}/addressbook:latest"
ファイルを保存します。
このパイプラインはデプロイを実行しますが、Dockerイメージが正常に生成されてDockerHubにプッシュされた場合にのみ開始できます。 そのため、ビルドパイプラインとデプロイメントパイプラインをプロモーションに接続する必要があります。 Dockerビルドパイプラインを編集して追加します。
nano .semaphore/docker-build.yml
ファイルの最後にプロモーションを追加します。
.semaphore / docker-build.yml
. . . promotions: - name: Deploy to Kubernetes pipeline_file: deploy-k8s.yml auto_promote_on: - result: passed
CI/CDワークフローの設定が完了しました。
残っているのは、変更されたファイルをプッシュし、Semaphoreに作業を任せることだけです。 リポジトリの変更を追加、コミット、およびプッシュします。
git add .semaphore/deploy-k8s.yml git add .semaphore/docker-build.yml git add deployment.yml git commit -m "kubernetes deploy pipeline" git push origin master
展開が完了するまでに数分かかります。
次に、アプリケーションをテストしてみましょう。
ステップ7—アプリケーションのテスト
この時点で、アプリケーションは稼働しています。 このステップでは、curl
を使用してAPIエンドポイントをテストします。
DigitalOceanがクラスターに与えたパブリックIPを知る必要があります。 次の手順に従って見つけてください。
- DigitalOceanアカウントにログインします。
- アドレスブックプロジェクトを選択します
- ネットワーキングに移動します。
- ロードバランサーをクリックします。
- IPアドレスが表示されます。 IPアドレスをコピーします。
curl
を使用して/all
ルートを確認しましょう。
curl -w "\n" YOUR_CLUSTER_IP/all
-w "\n"
オプションを使用して、curl
がすべての行を印刷するようにすることができます。
データベースにはまだレコードがないため、結果として空のJSON配列が取得されます。
Output[]
/person
エンドポイントにPUT
リクエストを送信して、新しい個人レコードを作成します。
curl -w "\n" -X PUT \ -d "firstName=Sammy&lastName=the Shark" YOUR_CLUSTER_IP/person
APIは、個人のJSONオブジェクトを返します。
Output{ "id": 1, "firstName": "Sammy", "lastName": "the Shark", "updatedAt": "2019-07-04T23:51:00.548Z", "createdAt": "2019-07-04T23:51:00.548Z" }
2人目の人を作成します。
curl -w "\n" -X PUT \ -d "firstName=Tommy&lastName=the Octopus" YOUR_CLUSTER_IP/person
出力は、2人目の人物が作成されたことを示しています。
Output{ "id": 2, "firstName": "Tommy", "lastName": "the Octopus", "updatedAt": "2019-07-04T23:52:08.724Z", "createdAt": "2019-07-04T23:52:08.724Z" }
次に、GET
リクエストを作成して、2
のid
を持つ人を取得します。
curl -w "\n" YOUR_CLUSTER_IP/person/2
サーバーは、要求されたデータで応答します。
Output{ "id": 2, "firstName": "Tommy", "lastName": "the Octopus", "createdAt": "2019-07-04T23:52:08.724Z", "updatedAt": "2019-07-04T23:52:08.724Z" }
人物を削除するには、DELETE
リクエストを送信します。
curl -w "\n" -X DELETE YOUR_CLUSTER_IP/person/2
このコマンドでは出力は返されません。
データベースには、1
のid
を持つ人を1人だけ含める必要があります。 /all
をもう一度取得してみてください。
curl -w "\n" YOUR_CLUSTER_IP/all
サーバーは、1つのレコードのみを含む一連の人物で応答します。
Output[ { "id": 1, "firstName": "Sammy", "lastName": "the Shark", "createdAt": "2019-07-04T23:51:00.548Z", "updatedAt": "2019-07-04T23:51:00.548Z" } ]
この時点で、データベースに残っているのは1人だけです。
これで、アプリケーション内のすべてのエンドポイントのテストが完了し、チュートリアルの終わりを示します。
結論
このチュートリアルでは、DigitalOceanのマネージドPostgreSQLデータベースサービスを使用して、完全なNode.jsアプリケーションを最初から作成しました。 次に、SemaphoreのCI / CDパイプラインを使用して、コンテナーイメージをテストおよび構築し、Docker Hubにアップロードして、DigitalOceanKubernetesにデプロイするワークフローを完全に自動化しました。
Kubernetesの詳細については、Kubernetesの概要およびその他のDigitalOceanのKubernetesチュートリアルをご覧ください。
アプリケーションがデプロイされたので、ドメイン名の追加、データベースクラスターの保護、またはデータベースのアラートの設定を検討できます。