著者は、Write for DOnationsプログラムの一部として寄付を受け取るためにDiversity in Tech Fundを選択しました。
前書き
Pulumiは、汎用プログラミング言語で記述されたコードを使用してインフラストラクチャを作成、展開、および管理するためのツールです。 アプリケーションの構成に加えて、Droplet、管理対象データベース、DNSレコード、Kubernetesクラスターなど、DigitalOceanのすべての管理サービスの自動化をサポートしています。 展開は使いやすいコマンドラインインターフェイスから実行されます。このインターフェイスは、さまざまな一般的なCI / CDシステムとも統合されています。
Pulumiは複数の言語をサポートしていますが、このチュートリアルでは、Node.jsランタイムを使用する静的に型指定されたJavaScriptのバージョンであるTypeScriptを使用します。 つまり、IDEのサポートとコンパイル時のチェックが行われ、ユーティリティタスクのNPMモジュールにアクセスしながら、適切なリソースを構成したり、正しいスラッグを使用したりしたことを確認できます。
このチュートリアルでは、DigitalOceanKubernetesクラスター、負荷分散されたKubernetesアプリケーション、および選択した安定したドメイン名でアプリケーションを利用できるようにするDigitalOceanDNSドメインをプロビジョニングします。 これはすべて、60行のinfrastructure-as-codeと1つのpulumi up
コマンドラインジェスチャでプロビジョニングできます。 このチュートリアルを完了すると、DigitalOceanとKubernetesの全領域を活用するPulumi as-codeを使用して強力なクラウドアーキテクチャを生産的に構築する準備が整います。
前提条件
このチュートリアルを実行するには、次のものが必要です。
-
リソースをデプロイするDigitalOceanアカウント。 まだ持っていない場合は、register here。
-
自動展開を実行するDigitalOcean APIトークン。 Generate a personal access token hereを作成し、手順2で使用するので手元に置いておきます。
-
Kubernetesクラスタを作成して使用するため、install
kubectl
を実行する必要があります。 それをさらに構成することを心配しないでください-後でそれを行います。 -
TypeScriptでインフラストラクチャとしてコードを記述するため、Node.js 8以降が必要になります。 Download it hereまたはusing your system’s package managerをインストールします。
-
Pulumiを使用してインフラストラクチャをデプロイするため、install the open source Pulumi SDKを実行する必要があります。
-
オプションのステップ5を実行するには、DigitalOceanネームサーバーを使用するように構成されたドメイン名が必要です。 This guideは、選択したレジストラに対してこれを行う方法を説明しています。
[[step-1 -—- scaffolding-a-new-project]] ==ステップ1—新しいプロジェクトの足場
最初のステップは、Pulumiプロジェクトを保存するディレクトリを作成することです。 このディレクトリには、プロジェクトとそのNPM依存関係を説明するメタデータファイルに加えて、インフラストラクチャ定義のソースコードが含まれます。
まず、ディレクトリを作成します。
mkdir do-k8s
次に、新しく作成されたディレクトリに移動します。
cd do-k8s
今後は、新しく作成したdo-k8s
ディレクトリからコマンドを実行します。
次に、新しいPulumiプロジェクトを作成します。 これを実現するにはさまざまな方法がありますが、最も簡単な方法は、typescript
プロジェクトテンプレートでpulumi new
コマンドを使用することです。 このコマンドは、最初にPulumiにログインするように促し、プロジェクトとデプロイメントの状態を保存してから、現在のディレクトリに単純なTypeScriptプロジェクトを作成します。
pulumi new typescript -y
ここでは、-y
オプションをnew
コマンドに渡して、デフォルトのプロジェクトオプションを受け入れるように指示しています。 たとえば、プロジェクト名は現在のディレクトリの名前から取得されるため、do-k8s
になります。 プロジェクト名にさまざまなオプションを使用する場合は、-y
を削除するだけです。
コマンドを実行した後、ディレクトリの内容をls
で一覧表示します。
ls
次のファイルが存在するようになります。
OutputPulumi.yaml index.ts node_modules
package-lock.json package.json tsconfig.json
編集するプライマリファイルはindex.ts
です。 このチュートリアルではこの単一のファイルのみを使用しますが、Node.jsモジュールを使用して、プロジェクトを適切に編成することができます。 このチュートリアルでは、Pulumiが変更内容のみを検出して段階的に展開できるという事実を活用して、一度に1ステップずつ説明します。 必要に応じて、プログラム全体にデータを入力し、pulumi up
を使用してすべてを一度にデプロイできます。
新しいプロジェクトを足場にしたので、チュートリアルを進めるために必要な依存関係を追加する準備ができました。
[[step-2 -—- adding-dependencies]] ==ステップ2—依存関係の追加
次のステップは、DigitalOceanおよびKubernetesパッケージのインストールと依存関係の追加です。 最初に、NPMを使用してインストールします。
npm install @pulumi/digitalocean @pulumi/kubernetes
これにより、NPMパッケージ、Pulumiプラグインがダウンロードされ、依存関係として保存されます。
次に、お気に入りのエディターでindex.ts
ファイルを開きます。 このチュートリアルではnanoを使用します。
nano index.ts
index.ts
の内容を次のように置き換えます。
index.ts
import * as digitalocean from "@pulumi/digitalocean";
import * as kubernetes from "@pulumi/kubernetes";
これにより、これらのパッケージのすべての内容がプログラムで利用可能になります。 TypeScriptとNode.jsを理解するIDEを使用して"digitalocean."
と入力すると、たとえば、このパッケージでサポートされているDigitalOceanリソースのリストが表示されます。
コンテンツを追加した後、ファイルを保存して閉じます。
[.note]#Note:これらのパッケージで利用可能なもののサブセットを使用します。 リソース、プロパティ、および関連するAPIの完全なドキュメントについては、@pulumi/digitalocean
および@pulumi/kubernetes
パッケージの関連するAPIドキュメントを参照してください。
#
次に、Pulumiがアカウントのリソースをプロビジョニングできるように、DigitalOceanトークンを構成します。
pulumi config set digitalocean:token YOUR_TOKEN_HERE --secret
--secret
フラグに注目してください。これは、Pulumiの暗号化サービスを使用してトークンを暗号化し、暗号文に格納されていることを確認します。 必要に応じて、代わりにDIGITALOCEAN_TOKEN
環境変数を使用できますが、プログラムを更新するたびに設定することを忘れないでください。構成を使用すると、プロジェクトに自動的に保存されて使用されます。
このステップでは、Kubernetesクラスターをプロビジョニングできるように、必要な依存関係を追加し、PulumiでAPIトークンを構成しました。
[[step-3 -—- provisioning-a-kubernetes-cluster]] ==ステップ3—Kubernetesクラスターのプロビジョニング
これで、DigitalOcean Kubernetesクラスターを作成する準備が整いました。 index.ts
ファイルを再度開くことから始めます。
nano index.ts
index.ts
ファイルの最後に次の行を追加します。
index.ts
...
const cluster = new digitalocean.KubernetesCluster("do-cluster", {
region: digitalocean.Regions.SFO2,
version: "latest",
nodePool: {
name: "default",
size: digitalocean.DropletSlugs.DropletS2VPCU2GB,
nodeCount: 3,
},
});
export const kubeconfig = cluster.kubeConfigs[0].rawConfig;
この新しいコードは、digitalocean.KubernetesCluster
のインスタンスを割り当て、それにいくつかのプロパティを設定します。 これには、sfo2
region slug、latest
でサポートされているバージョンのKubernetes、s-2vcpu-2gb
Droplet size slugの使用が含まれ、3つのDropletインスタンスの必要な数が示されます。 これらは自由に変更できますが、この記事の執筆時点では、DigitalOcean Kubernetesは特定の地域でのみ利用可能です。 リージョンの可用性に関する最新情報については、product documentationを参照できます。
クラスターで構成できるプロパティの完全なリストについては、KubernetesCluster
API documentationを参照してください。
そのコードスニペットの最後の行は、結果のKubernetesクラスターのkubeconfig
fileをエクスポートして、使いやすくします。 エクスポートされた変数はコンソールに出力され、ツールからもアクセスできます。 これを一時的に使用して、kubectl
などの標準ツールからクラスターにアクセスします。
これで、クラスターをデプロイする準備が整いました。 これを行うには、pulumi up
を実行します。
pulumi up
このコマンドは、プログラムを取得し、説明されているインフラストラクチャを作成するための計画を生成し、それらの変更を展開するための一連の手順を実行します。 これは、後続の更新が行われたときにインフラストラクチャを比較および更新できることに加えて、インフラストラクチャの最初の作成でも機能します。 この場合、出力は次のようになります。
OutputPreviewing update (dev):
Type Name Plan
+ pulumi:pulumi:Stack do-k8s-dev create
+ └─ digitalocean:index:KubernetesCluster do-cluster create
Resources:
+ 2 to create
Do you want to perform this update?
yes
> no
details
これは、更新を続行すると、do-cluster
という名前の単一のKubernetesクラスターが作成されることを示しています。 yes/no/details
プロンプトを使用すると、実際に変更を加える前に、これが望ましい結果であることを確認できます。 details
を選択すると、リソースとそのプロパティの完全なリストが表示されます。 yes
を選択して、展開を開始します。
OutputUpdating (dev):
Type Name Status
+ pulumi:pulumi:Stack do-k8s-dev created
+ └─ digitalocean:index:KubernetesCluster do-cluster created
Outputs:
kubeconfig: "..."
Resources:
+ 2 created
Duration: 6m5s
Permalink: https://app.pulumi.com/.../do-k8s/dev/updates/1
クラスターの作成には数分かかりますが、クラスターが稼働し、完全なkubeconfig
がコンソールに出力されます。 kubeconfig
をファイルに保存します。
pulumi stack output kubeconfig > kubeconfig.yml
次に、それをkubectl
とともに使用して、Kubernetesコマンドを実行します。
KUBECONFIG=./kubeconfig.yml kubectl get nodes
次のような出力が表示されます。
OutputNAME STATUS ROLES AGE VERSION
default-o4sj Ready 4m5s v1.14.2
default-o4so Ready 4m3s v1.14.2
default-o4sx Ready 3m37s v1.14.2
この時点で、Infrastructure-as-Codeをセットアップし、新しいDigitalOcean Kubernetesクラスターを起動して構成するための繰り返し可能な方法を使用できました。 次のステップでは、これに基づいてコードでKubernetesインフラストラクチャを定義し、Kubernetesインフラストラクチャを同様に展開および管理する方法を学習します。
[[step-4 -—- deploying-an-application-to-your-cluster]] ==ステップ4—アプリケーションをクラスターにデプロイする
次に、Infrastructure-as-codeを使用してKubernetesアプリケーションの構成を説明します。 これは3つの部分で構成されます。
-
kubectl
が使用するように構成されているデフォルトではなく、KubernetesリソースをDigitalOceanクラスターにデプロイするようにPulumiに指示するProvider
オブジェクト。 -
Kubernetes Deployment。これは、任意の数のポッドにレプリケートされるDockerコンテナイメージをデプロイする標準のKubernetesの方法です。
-
Kubernetes Service。これは、ポッドのターゲットセット(この場合は上記のデプロイ)全体でアクセスを負荷分散するようにKubernetesに指示する標準的な方法です。
これは、Kubernetesで負荷分散サービスを起動して実行するためのかなり標準的なreference architectureです。
これら3つすべてをデプロイするには、index.ts
ファイルを再度開きます。
nano index.ts
ファイルが開いたら、このコードをファイルの最後に追加します。
index.ts
...
const provider = new kubernetes.Provider("do-k8s", { kubeconfig })
const appLabels = { "app": "app-nginx" };
const app = new kubernetes.apps.v1.Deployment("do-app-dep", {
spec: {
selector: { matchLabels: appLabels },
replicas: 5,
template: {
metadata: { labels: appLabels },
spec: {
containers: [{
name: "nginx",
image: "nginx",
}],
},
},
},
}, { provider });
const appService = new kubernetes.core.v1.Service("do-app-svc", {
spec: {
type: "LoadBalancer",
selector: app.spec.template.metadata.labels,
ports: [{ port: 80 }],
},
}, { provider });
export const ingressIp = appService.status.loadBalancer.ingress[0].ip;
このコードは、標準のKubernetes構成に似ており、オブジェクトとそのプロパティの動作は同等です。ただし、他のインフラストラクチャ宣言とともにTypeScriptで記述されている点が異なります。
変更を行った後、ファイルを保存して閉じます。
前と同じように、pulumi up
を実行して変更をプレビューし、展開します。
pulumi up
続行するためにyes
を選択した後、CLIは、ポッドの可用性、IPアドレスの割り当てなどに関する診断を含む、詳細なステータス更新を出力します。 これは、展開が完了するのに時間がかかったり、停止したりする理由を理解するのに役立ちます。
完全な出力は次のようになります。
OutputUpdating (dev):
Type Name Status
pulumi:pulumi:Stack do-k8s-dev
+ ├─ pulumi:providers:kubernetes do-k8s created
+ ├─ kubernetes:apps:Deployment do-app-dep created
+ └─ kubernetes:core:Service do-app-svc created
Outputs:
+ ingressIp : "157.230.199.202"
Resources:
+ 3 created
2 unchanged
Duration: 2m52s
Permalink: https://app.pulumi.com/.../do-k8s/dev/updates/2
これが完了したら、必要な数のPodが実行されていることに注意してください。
KUBECONFIG=./kubeconfig.yml kubectl get pods
OutputNAME READY STATUS RESTARTS AGE
do-app-dep-vyf8k78z-758486ff68-5z8hk 1/1 Running 0 1m
do-app-dep-vyf8k78z-758486ff68-8982s 1/1 Running 0 1m
do-app-dep-vyf8k78z-758486ff68-94k7b 1/1 Running 0 1m
do-app-dep-vyf8k78z-758486ff68-cqm4c 1/1 Running 0 1m
do-app-dep-vyf8k78z-758486ff68-lx2d7 1/1 Running 0 1m
プログラムがクラスターのkubeconfig
ファイルをエクスポートする方法と同様に、このプログラムはKubernetesサービスの結果のロードバランサーのIPアドレスもエクスポートします。 これを使用してエンドポイントをcurl
し、それが稼働していることを確認します。
curl $(pulumi stack output ingressIp)
ここから、アプリケーションインフラストラクチャを簡単に編集および再デプロイできます。 たとえば、replicas: 5
行をreplicas: 7
に変更してから、pulumi up
を再実行してみてください。
pulumi up
変更内容を表示するだけで、詳細を選択すると正確な差分が表示されることに注意してください。
OutputPreviewing update (dev):
Type Name Plan Info
pulumi:pulumi:Stack do-k8s-dev
~ └─ kubernetes:apps:Deployment do-app-dep update [diff: ~spec]
Resources:
~ 1 to update
4 unchanged
Do you want to perform this update? details
pulumi:pulumi:Stack: (same)
[urn=urn:pulumi:dev::do-k8s::pulumi:pulumi:Stack::do-k8s-dev]
~ kubernetes:apps/v1:Deployment: (update)
[id=default/do-app-dep-vyf8k78z]
[urn=urn:pulumi:dev::do-k8s::kubernetes:apps/v1:Deployment::do-app-dep]
[provider=urn:pulumi:dev::do-k8s::pulumi:providers:kubernetes::do-k8s::80f36105-337f-451f-a191-5835823df9be]
~ spec: {
~ replicas: 5 => 7
}
これで、完全に機能するKubernetesクラスターと動作するアプリケーションの両方ができました。 アプリケーションが稼働している状態で、アプリケーションで使用するカスタムドメインを構成できます。 次のステップでは、Pulumiを使用してDNSを構成します。
[[step-5 -—- creating-a-dns-domain-optional]] ==ステップ5— DNSドメインの作成(オプション)
Kubernetesクラスターとアプリケーションは稼働していますが、アプリケーションのアドレスはクラスターによる自動IPアドレス割り当ての気まぐれに依存しています。 調整して再デプロイすると、このアドレスが変わる可能性があります。 このステップでは、カスタムDNS名をロードバランサーのIPアドレスに割り当てて、インフラストラクチャを後で変更しても安定するようにする方法を確認します。
[.note]#Note:この手順を完了するには、DigitalOceanのDNSネームサーバー、ns1.digitalocean.com
、ns2.digitalocean.com
、およびns3.digitalocean.com
を使用するドメインがあることを確認してください。 これを構成する手順は、「前提条件」セクションにあります。
#
DNSを構成するには、index.ts
ファイルを開き、ファイルの最後に次のコードを追加します。
index.ts
...
const domain = new digitalocean.Domain("do-domain", {
name: "your_domain",
ipAddress: ingressIp,
});
このコードは、KubernetesサービスのIPアドレスを参照するAレコードを持つ新しいDNSエントリを作成します。 このスニペットのyour_domain
を、選択したドメイン名に置き換えます。
www
などの追加のサブドメインがWebアプリケーションを指すようにするのが一般的です。 これは、DigitalOcean DNSレコードを使用して簡単に達成できます。 この例をより面白くするために、www.your_domain.com
がyour_domain.com
を指すCNAME
レコードも追加します。
index.ts
...
const cnameRecord = new digitalocean.DnsRecord("do-domain-cname", {
domain: domain.name,
type: "CNAME",
name: "www",
value: "@",
});
これらの変更を行った後、ファイルを保存して閉じます。
最後に、pulumi up
を実行して、既存のアプリケーションとクラスターを指すようにDNS変更を展開します。
OutputUpdating (dev):
Type Name Status
pulumi:pulumi:Stack do-k8s-dev
+ ├─ digitalocean:index:Domain do-domain created
+ └─ digitalocean:index:DnsRecord do-domain-cname created
Resources:
+ 2 created
5 unchanged
Duration: 6s
Permalink: https://app.pulumi.com/.../do-k8s/dev/updates/3
DNSの変更が反映されると、カスタムドメインでコンテンツにアクセスできるようになります。
curl www.your_domain.com
次のような出力が表示されます。
これで、新しいDigitalOcean Kubernetesクラスターを正常にセットアップし、負荷分散されたKubernetesアプリケーションをデプロイし、そのアプリケーションのロードバランサーにDigitalOcean DNSを使用した安定したドメイン名を、すべて60行のコードとpulumi up
で指定しました。コマンド。
次のステップでは、不要になったリソースを削除します。
[[step-6 -—- removing-the-resources-optional]] ==ステップ6—リソースの削除(オプション)
チュートリアルを終了する前に、上記で作成したすべてのリソースを破棄することができます。 これにより、使用されていないリソースに対して課金されることがなくなります。 アプリケーションを稼働したままにする場合は、この手順をスキップしてください。
次のコマンドを実行して、リソースを破棄します。 これは元に戻せないため、使用には注意してください!
pulumi destroy
up
コマンドの場合と同様に、destroy
は、アクションを実行する前にプレビューとプロンプトを表示します。
OutputPreviewing destroy (dev):
Type Name Plan
- pulumi:pulumi:Stack do-k8s-dev delete
- ├─ digitalocean:index:DnsRecord do-domain-cname delete
- ├─ digitalocean:index:Domain do-domain delete
- ├─ kubernetes:core:Service do-app-svc delete
- ├─ kubernetes:apps:Deployment do-app-dep delete
- ├─ pulumi:providers:kubernetes do-k8s delete
- └─ digitalocean:index:KubernetesCluster do-cluster delete
Resources:
- 7 to delete
Do you want to perform this destroy?
yes
> no
details
これが必要なものであると想定して、yes
を選択し、削除が発生するのを確認します。
OutputDestroying (dev):
Type Name Status
- pulumi:pulumi:Stack do-k8s-dev deleted
- ├─ digitalocean:index:DnsRecord do-domain-cname deleted
- ├─ digitalocean:index:Domain do-domain deleted
- ├─ kubernetes:core:Service do-app-svc deleted
- ├─ kubernetes:apps:Deployment do-app-dep deleted
- ├─ pulumi:providers:kubernetes do-k8s deleted
- └─ digitalocean:index:KubernetesCluster do-cluster deleted
Resources:
- 7 deleted
Duration: 7s
Permalink: https://app.pulumi.com/.../do-k8s/dev/updates/4
この時点では、何も残っていません。DNSエントリはなくなり、Kubernetesクラスターは、その内部で実行されているアプリケーションとともに、消えています。 パーマリンクはまだ利用可能ですので、戻ってこのスタックの更新の完全な履歴を見ることができます。 これは、サービスがすべてのリソースの完全な状態履歴を保持するため、破壊が間違いだった場合の回復に役立ちます。
プロジェクト全体を破壊する場合は、スタックを削除します。
pulumi stack rm
スタックの名前を入力して、削除の確認を求める出力を受け取ります。
OutputThis will permanently remove the 'dev' stack!
Please confirm that this is what you'd like to do by typing ("dev"):
クラウドインフラストラクチャリソースを削除するdestroyコマンドとは異なり、スタックを削除すると、Pulumiの範囲からスタックの完全な履歴が完全に消去されます。
結論
このチュートリアルでは、このクラスターを使用するKubernetesアプリケーション構成に加えて、DigitalOceanインフラストラクチャリソース(KubernetesクラスターとAおよびCNAMEレコードを持つDNSドメイン)をデプロイしました。 既存のエディタ、ツール、およびライブラリで動作し、既存のコミュニティとパッケージを活用する、使い慣れたプログラミング言語TypeScriptで記述されたコードとしてのインフラストラクチャを使用してこれを行いました。 アプリケーションとインフラストラクチャにまたがる展開を行うために、単一のコマンドラインワークフローを使用してすべてを完了しました。
ここから、次のステップを実行できます。
このチュートリアルのサンプル全体はavailable on GitHubです。 今日の独自のプロジェクトでPulumiInfrastructure-as-Codeを使用する方法の詳細については、Pulumi Documentation、Tutorials、またはGetting Startedガイドを確認してください。 Pulumiはオープンソースであり、無料で使用できます。