Pulumiを使用してDigitalOceanおよびKubernetesインフラストラクチャを管理する方法
著者は、 Diversity in Tech Fund を選択して、 Write forDOnationsプログラムの一環として寄付を受け取りました。
序章
Pulumi は、汎用プログラミング言語で記述されたコードを使用してインフラストラクチャを作成、展開、および管理するためのツールです。 アプリケーション構成に加えて、Droplets、マネージドデータベース、DNSレコード、KubernetesクラスターなどのDigitalOceanのすべてのマネージドサービスの自動化をサポートします。 展開は、さまざまな一般的なCI/CDシステムとも統合された使いやすいコマンドラインインターフェイスから実行されます。
Pulumiは複数の言語をサポートしていますが、このチュートリアルでは、Node.jsランタイムを使用する静的に型付けされたバージョンのJavaScriptであるTypeScriptを使用します。 これは、ユーティリティタスク用の NPM モジュールにアクセスしながら、適切なリソースを構成したり、正しいスラッグを使用したりするのに役立つIDEサポートとコンパイル時チェックを取得することを意味します。 。
このチュートリアルでは、DigitalOcean Kubernetes クラスター、負荷分散されたKubernetesアプリケーション、および選択した安定したドメイン名でアプリケーションを利用できるようにするDigitalOceanDNSドメインをプロビジョニングします。 これはすべて、60行のInfrastructure-as-Codeと1つのpulumi up
コマンドラインジェスチャでプロビジョニングできます。 このチュートリアルを終えると、DigitalOceanとKubernetesの全表面積を活用するPulumi Infrastructure-as-Codeを使用して、強力なクラウドアーキテクチャを生産的に構築する準備が整います。
前提条件
このチュートリアルに従うには、次のものが必要です。
- リソースをデプロイするDigitalOceanアカウント。 まだお持ちでない場合は、こちらから登録してください。
- 自動デプロイメントを実行するためのDigitalOceanAPIトークン。 ここでパーソナルアクセストークンを生成し、ステップ2で使用するので手元に置いておきます。
- Kubernetesクラスタを作成して使用するため、kubectlをインストールする必要があります。 さらに構成する必要はありません。後で構成します。
- TypeScriptでinfrastructure-as-codeを記述するため、Node.js8以降が必要になります。 ここからダウンロードするか、システムのパッケージマネージャーを使用してインストールします。
- Pulumiを使用してインフラストラクチャをデプロイするため、オープンソースのPulumiSDKをインストールする必要があります。
- オプションのステップ5を実行するには、DigitalOceanネームサーバーを使用するように構成されたドメイン名が必要です。 このガイドでは、選択したレジストラに対してこれを行う方法について説明しています。
ステップ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
を使用してすべてを一度にデプロイできます。
新しいプロジェクトの足場を固めたので、チュートリアルに従うために必要な依存関係を追加する準備が整いました。
ステップ2—依存関係を追加する
次のステップは、DigitalOceanパッケージとKubernetesパッケージに依存関係をインストールして追加することです。 まず、NPMを使用してそれらをインストールします。
- npm install @pulumi/digitalocean @pulumi/kubernetes
これにより、NPMパッケージ、Pulumiプラグインがダウンロードされ、依存関係として保存されます。
次に、お気に入りのエディタでindex.ts
ファイルを開きます。 このチュートリアルではnanoを使用します。
- nano index.ts
index.ts
の内容を次のように置き換えます。
import * as digitalocean from "@pulumi/digitalocean";
import * as kubernetes from "@pulumi/kubernetes";
これにより、これらのパッケージの全内容がプログラムで利用できるようになります。 TypeScriptとNode.jsを理解するIDEを使用して"digitalocean."
と入力すると、たとえば、このパッケージでサポートされているDigitalOceanリソースのリストが表示されます。
コンテンツを追加したら、ファイルを保存して閉じます。
注:これらのパッケージで利用可能なもののサブセットを使用します。 リソース、プロパティ、および関連するAPIの完全なドキュメントについては、 @ pulumi /digitaloceanおよび@pulumi /kubernetesパッケージに関連するAPIドキュメントを参照してください。
次に、Pulumiがアカウントのリソースをプロビジョニングできるように、DigitalOceanトークンを構成します。
- pulumi config set digitalocean:token YOUR_TOKEN_HERE --secret
--secret
フラグに注目してください。このフラグは、Pulumiの暗号化サービスを使用してトークンを暗号化し、暗号文に格納されていることを確認します。 必要に応じて、代わりにDIGITALOCEAN_TOKEN
環境変数を使用できますが、プログラムを更新するたびに設定することを忘れないでください。構成を使用すると、プロジェクトに自動的に保存されて使用されます。
このステップでは、必要な依存関係を追加し、Pulumiを使用してAPIトークンを構成して、Kubernetesクラスターをプロビジョニングできるようにしました。
ステップ3—Kubernetesクラスターのプロビジョニング
これで、DigitalOceanKubernetesクラスターを作成する準備が整いました。 index.ts
ファイルを再度開いて開始します。
- nano 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インスタンスの希望する数。 これらのいずれかを自由に変更できますが、この記事の執筆時点では、DigitalOceanKubernetesは特定の地域でのみ利用可能であることに注意してください。 リージョンの可用性に関する最新情報については、製品ドキュメントを参照してください。
クラスタで設定できるプロパティの完全なリストについては、KubernetesClusterAPIドキュメントを参照してください。
そのコードスニペットの最後の行は、結果のKubernetesクラスターのkubeconfigファイルをエクスポートして使いやすくします。 エクスポートされた変数はコンソールに出力され、ツールからもアクセスできます。 これを一時的に使用して、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 <none> 4m5s v1.14.2
default-o4so Ready <none> 4m3s v1.14.2
default-o4sx Ready <none> 3m37s v1.14.2
この時点で、Infrastructure-as-Codeをセットアップし、新しいDigitalOceanKubernetesクラスターを起動して構成するための繰り返し可能な方法があります。 次のステップでは、この上に構築して、コードでKubernetesインフラストラクチャを定義し、それらを同様にデプロイおよび管理する方法を学習します。
ステップ4—アプリケーションをクラスターにデプロイする
次に、Infrastructure-as-Codeを使用してKubernetesアプリケーションの設定について説明します。 これは3つの部分で構成されます。
Provider
オブジェクト。これは、kubectl
が使用するように構成されているデフォルトではなく、KubernetesリソースをDigitalOceanクラスターにデプロイするようにPulumiに指示します。- Kubernetes Deployment 。これは、任意の数のポッドにレプリケートされるDockerコンテナイメージをデプロイする標準のKubernetesの方法です。
- Kubernetes Service 。これは、ポッドのターゲットセット(この場合は上記のデプロイ)全体でアクセスを負荷分散するようにKubernetesに指示する標準的な方法です。
これは、Kubernetesで負荷分散サービスを起動して実行するためのかなり標準的なリファレンスアーキテクチャです。
これら3つすべてをデプロイするには、index.ts
ファイルをもう一度開きます。
- nano 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
これが完了したら、必要な数のポッドが実行されていることに注意してください。
- 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)
Output<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
body {
width: 35em;
margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif;
}
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
ここから、アプリケーションインフラストラクチャを簡単に編集および再デプロイできます。 たとえば、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を構成する方法について説明します。
ステップ5— DNSドメインの作成(オプション)
Kubernetesクラスタとアプリケーションは稼働していますが、アプリケーションのアドレスは、クラスタによる自動IPアドレス割り当ての気まぐれに依存しています。 調整して再デプロイすると、このアドレスが変わる可能性があります。 このステップでは、カスタムDNS名をロードバランサーのIPアドレスに割り当てて、後でインフラストラクチャを変更しても安定するようにする方法を説明します。
注:この手順を完了するには、DigitalOceanのDNSネームサーバーns1.digitalocean.com
、ns2.digitalocean.com
、およびns3.digitalocean.com
を使用するドメインがあることを確認してください。 これを構成する手順は、「前提条件」セクションにあります。
DNSを構成するには、index.ts
ファイルを開き、ファイルの最後に次のコードを追加します。
...
const domain = new digitalocean.Domain("do-domain", {
name: "your_domain",
ipAddress: ingressIp,
});
このコードは、KubernetesサービスのIPアドレスを参照するAレコードを使用して新しいDNSエントリを作成します。 このスニペットのyour_domain
を選択したドメイン名に置き換えます。
www
などの追加のサブドメインで、Webアプリケーションを指すようにするのが一般的です。 これは、DigitalOceanDNSレコードを使用して簡単に実行できます。 この例をさらに面白くするために、www.your_domain.com
をyour_domain.com
にポイントするCNAME
レコードも追加します。
...
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
次のような出力が表示されます。
Output<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
body {
width: 35em;
margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif;
}
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
これで、新しいDigitalOcean Kubernetesクラスターを正常にセットアップし、負荷分散されたKubernetesアプリケーションをデプロイし、そのアプリケーションのロードバランサーに、DigitalOceanDNSを使用した安定したドメイン名をすべて60行のコードと
次のステップでは、リソースが不要になった場合にリソースを削除する方法について説明します。
手順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で記述されたInfrastructure-as-Codeを使用して行いました。 アプリケーションとインフラストラクチャにまたがる展開を行うための単一のコマンドラインワークフローを使用して、すべてを実行しました。
ここから、次のステップがいくつかあります。
- PulumiがサポートするフルセットのDigitalOceanリソースをご覧ください。
- 一般的なアーキテクチャを含む、Kubernetesアプリケーションに対するPulumiのサポートをご覧ください。
- CI/CDとGitワークフローの統合によりデプロイメントを自動的にトリガーします。
- プログラムの特定の要素を例のように構成可能にし、より大きなプロジェクトとマルチスタックアプローチ(開発、テスト、ステージング、本番など)を促進します。
このチュートリアルのサンプル全体は、GitHubで入手できます。 今日の独自のプロジェクトでPulumiInfrastructure-as-Codeを使用する方法の詳細については、 Pulumi Documentation 、 Tutorials 、または GettingStartedガイドをご覧ください。 Pulumiはオープンソースであり、無料で使用できます。