CircleCIを使用してDigitalOceanKubernetesへのデプロイを自動化する方法

提供:Dev Guides
移動先:案内検索

著者は、 Write forDOnationsプログラムの一環として寄付を受け取るためにTechEducationFundを選択しました。

序章

自動展開プロセスを持つことは、スケーラブルで復元力のあるアプリケーションの要件であり、 GitOpsまたはGitベースのDevOpsは、Gitを使用してCI/CDを整理する一般的な方法に急速になりました。 「信頼できる唯一の情報源」としてのリポジトリ。 CircleCI などのツールはGitHubリポジトリと統合されているため、リポジトリに変更を加えるたびにコードを自動的にテストしてデプロイできます。 この種のCI/CDをKubernetesインフラストラクチャの柔軟性と組み合わせると、需要の変化に合わせて簡単に拡張できるアプリケーションを構築できます。

この記事では、CircleCIを使用して、サンプルアプリケーションをDigitalOcean Kubernetes(DOKS)クラスターにデプロイします。 このチュートリアルを読むと、これらの同じ手法を適用して、Dockerイメージとしてビルド可能な他のCI/CDツールをデプロイできるようになります。

前提条件

このチュートリアルに従うには、次のものが必要です。

このチュートリアルでは、Kubernetesバージョン1.22.7およびkubectlバージョン1.23.5を使用します。

ステップ1—DigitalOceanKubernetesクラスターを作成する

注:すでにDigitalOcean Kubernetesクラスターを実行している場合は、このセクションをスキップできます。


この最初のステップでは、サンプルアプリケーションをデプロイするDigitalOcean Kubernetes(DOKS)クラスターを作成します。 ローカルマシンから実行されるkubectlコマンドは、Kubernetesクラスターから直接情報を変更または取得します。

DigitalOceanアカウントのKubernetesページに移動します。

Kubernetesクラスターの作成をクリックするか、ページの右上にある緑色の Create ボタンをクリックして、ドロップダウンメニューからKubernetesを選択します。

次のページでは、クラスターの詳細を指定します。 でKubernetesバージョンを選択バージョン1.22.7-do.0を選択します。 これが利用できない場合は、最新の推奨バージョンを選択してください。

データセンターリージョンの選択の場合、最も近いリージョンを選択します。 このチュートリアルでは、 SanFranciscoを使用します。

次に、ノードプールを構築するオプションがあります。 Kubernetesでは、ノードはワーカーマシンであり、ポッドの実行に必要なサービスが含まれています。 DigitalOceanでは、各ノードはドロップレットです。 ノードプールは、単一の基本ノードで構成されます。 1GB / 1vCPU 構成を選択し、ノード数を1ノードに変更します。

必要に応じてタグを追加できます。 これは、 DigitalOcean API を使用する場合、またはノードプールをより適切に整理する場合に役立ちます。

名前を選択します。このチュートリアルでは、kubernetes-deployment-tutorialを使用します。 これにより、次のセクションを読みながら、全体を簡単に理解できるようになります。 最後に、緑色のクラスターの作成ボタンをクリックしてクラスターを作成します。 クラスタの作成が完了するまで待ちます。

クラスタの作成後、クラスタに接続するための手順が表示されます。 自動(推奨)タブの指示に従うか、手動タブでkubeconfigファイルをダウンロードします。 これは、クラスターに対して実行するkubectlコマンドの認証に使用するファイルです。 kubectlマシンにダウンロードします。

そのファイルを使用するデフォルトの方法は、kubectlで実行するすべてのコマンドで常に--kubeconfigフラグとそのファイルへのパスを渡すことです。 たとえば、構成ファイルをDesktopにダウンロードした場合は、次のようにkubectl get podsコマンドを実行します。

kubectl --kubeconfig ~/Desktop/kubernetes-deployment-tutorial-kubeconfig.yaml get pods

これにより、次の出力が生成されます。

OutputNo resources found.

これは、クラスターにアクセスしたことを意味します。 クラスタにポッドがないため、No resources found.メッセージは正しいです。

他のKubernetesクラスタを維持していない場合は、kubeconfigファイルをホームディレクトリの.kubeというフォルダにコピーできます。 存在しない場合に備えて、そのディレクトリを作成します。

mkdir -p ~/.kube

次に、設定ファイルを新しく作成した.kubeディレクトリにコピーし、名前をconfigに変更します。

cp current_kubernetes-deployment-tutorial-kubeconfig.yaml_file_path ~/.kube/config

これで、構成ファイルのパスは~/.kube/configになります。 これは、コマンドの実行時にkubectlがデフォルトで読み取るファイルであるため、--kubeconfigを渡す必要はありません。 次を実行します。

kubectl get pods

次の出力が表示されます。

OutputNo resources found in default namespace.

次に、次のコマンドでクラスターにアクセスします。

kubectl get nodes

クラスタ上のノードのリストを受け取ります。 出力は次のようになります。

OutputNAME                   STATUS   ROLES    AGE   VERSION
pool-upkissrv3-uzm8z   Ready    <none>   12m   v1.22.7

このチュートリアルでは、すべてのkubectlコマンドとマニフェストファイルdefault名前空間を使用します。これらのファイルは、Kubernetesでの作業のワークロードと操作パラメーターを定義するファイルです。 名前空間は、単一の物理クラスター内の仮想クラスターのようなものです。 他の任意の名前空間に変更できます。 必ず--namespaceフラグを使用してkubectlに渡すか、Kubernetesマニフェストメタデータフィールドで指定してください。 これらは、チームとその実行環境の展開を整理するための優れた方法です。 それらの詳細については、Namespacesに関するKubernetesの公式概要をご覧ください。

この手順を完了すると、クラスターに対してkubectlを実行できるようになります。 次のステップでは、サンプルアプリケーションを格納するために使用するローカルGitリポジトリを作成します。

ステップ2—ローカルGitリポジトリを作成する

次に、ローカルGitリポジトリでサンプルデプロイメントを構造化します。 また、クラスターで実行するすべてのデプロイに対してグローバルになるいくつかのKubernetesマニフェストを作成します。

注:このチュートリアルはUbuntu 20.04でテストされており、個々のコマンドはこのOSに一致するようにスタイル設定されています。 ただし、ここでのコマンドのほとんどは、変更をほとんどまたはまったく必要とせずに他のLinuxディストリビューションに適用でき、kubectlなどのコマンドはプラットフォームに依存しません。


まず、後でGitHubにプッシュする新しいGitリポジトリをローカルに作成します。 ホームディレクトリにdo-sample-appという空のフォルダを作成し、その中にcdを作成します。

mkdir ~/do-sample-app
cd ~/do-sample-app

次に、次のコマンドを使用して、このフォルダーに新しいGitリポジトリを作成します。

git init .

このリポジトリ内に、kubeという名前の空のフォルダを作成します。

mkdir ~/do-sample-app/kube/

これは、クラスタにデプロイするサンプルアプリケーションに関連するKubernetesリソースマニフェストを保存する場所になります。

ここで、kube-generalという名前の別のフォルダーを作成しますが、今回は作成したGitリポジトリーの外にあります。 ホームディレクトリ内に作成します。

mkdir ~/kube-general/

このフォルダーは、クラスター上の単一のデプロイメントに固有ではなく、複数のデプロイメントに共通のマニフェストを格納するために使用されるため、Gitリポジトリーの外部にあります。 これにより、これらの一般的なマニフェストをさまざまな展開で再利用できるようになります。

フォルダーを作成し、サンプルアプリケーションのGitリポジトリーを配置したら、DOKSクラスターの認証と承認を調整します。

ステップ3—サービスアカウントを作成する

通常、デフォルトの admin ユーザーを使用して、他のServicesからKubernetesクラスターへの認証を行うことはお勧めしません。 外部プロバイダーのキーが危険にさらされると、クラスター全体が危険にさらされます。

代わりに、RBACKubernetes認証モデルの一部である特定のロールを持つ単一のサービスアカウントを使用します。

この承認モデルは、ロールおよびリソースに基づいています。 基本的にクラスター上のユーザーであるサービスアカウントを作成することから始め、次に、クラスター上でアクセスできるリソースを指定するロールを作成します。 最後に、 Role Binding を作成します。これは、ロールと以前に作成したサービスアカウント間の接続を確立するために使用され、ロールがアクセスできるすべてのリソースへのアクセスをサービスアカウントに付与します。

作成する最初のKubernetesリソースは、CI / CDユーザーのサービスアカウントです。このチュートリアルでは、このアカウントにcicdという名前を付けます。

~/kube-generalフォルダー内にファイルcicd-service-account.ymlを作成し、お気に入りのテキストエディターで開きます。

nano ~/kube-general/cicd-service-account.yml

その上に次の内容を書いてください。

〜/ kube-general / cicd-service-account.yml

apiVersion: v1
kind: ServiceAccount
metadata:
  name: cicd
  namespace: default

これはYAMLファイルです。 すべてのKubernetesリソースは1つを使用して表されます。 この場合、このリソースはKubernetesAPIバージョンv1からのものであり(内部的にkubectlはKubernetesHTTP APIを呼び出してリソースを作成します)、ServiceAccountです。

metadataフィールドは、このリソースに関する詳細情報を追加するために使用されます。 この場合、このServiceAccountcicdという名前を付け、default名前空間に作成します。

これで、次のようにkubectl applyを実行して、クラスター上にこのサービスアカウントを作成できます。

kubectl apply -f ~/kube-general/

次のような出力が表示されます。

Outputserviceaccount/cicd created

サービスアカウントが機能していることを確認するには、それを使用してクラスターにログインしてみてください。 これを行うには、最初にそれぞれのアクセストークンを取得し、それを環境変数に格納する必要があります。 すべてのサービスアカウントには、KubernetesがSecretとして保存するアクセストークンがあります。

次のコマンドを使用して、このシークレットを取得できます。

TOKEN=$(kubectl get secret $(kubectl get secret | grep cicd-token | awk '{print $1}') -o jsonpath='{.data.token}' | base64 --decode)

このコマンドが何をしているのかについての説明:

$(kubectl get secret | grep cicd-token | awk '{print $1}')

これは、cicdサービスアカウントに関連するシークレットの名前を取得するために使用されます。 kubectl get secretは、デフォルトの名前空間のシークレットのリストを返します。次に、grepを使用して、cicdサービスアカウントに関連する行を検索します。 次に、grepから返される単一行の最初のものであるため、名前を返します。

kubectl get secret preceding-command -o jsonpath='{.data.token}' | base64 --decode

これにより、サービスアカウントトークンのシークレットのみが取得されます。 次に、jsonpathを使用してトークンフィールドにアクセスし、結果をbase64 --decodeに渡します。 トークンはBase64文字列として保存されるため、これが必要です。 トークン自体はJSONWebトークンです。

これで、cicdサービスアカウントを使用してポッドの取得を試みることができます。 次のコマンドを実行し、server-from-kubeconfig-fileを、~/.kube/config(クラスター用にダウンロードした構成ファイル)のserver:の後にあるサーバーURLに置き換えます。 このコマンドは、このチュートリアルの後半で学習する特定のエラーを表示します。

kubectl --insecure-skip-tls-verify --kubeconfig="/dev/null" --server=server-from-kubeconfig-file --token=$TOKEN get pods

--insecure-skip-tls-verifyは、サーバーの証明書を検証するステップをスキップします。これは、テストしているだけであり、これを検証する必要がないためです。 --kubeconfig="/dev/null"は、kubectlが構成ファイルと資格情報を読み取らず、代わりに提供されたトークンを使用するようにするためのものです。

出力は次のようになります。

OutputError from server (Forbidden): pods is forbidden: User "system:serviceaccount:default:cicd" cannot list resource "pods" in API group "" in the namespace "default"

これはエラーですが、トークンが機能したことを示しています。 受け取ったエラーは、サービスアカウントに、リソースsecretsを一覧表示するために必要な認証がないことに関するものですが、サーバー自体にはアクセスできました。 トークンが機能しなかった場合、エラーは次のようになります。

Outputerror: You must be logged in to the server (Unauthorized)

認証が成功したので、次のステップはサービスアカウントの認証エラーを修正することです。 これを行うには、必要な権限を持つロールを作成し、それをサービスアカウントにバインドします。

ステップ4—ロールとロールバインディングの作成

Kubernetesには、ロールを定義する2つの方法があります。RoleまたはClusterRoleリソースを使用する方法です。 前者と後者の違いは、最初の名前空間は単一の名前空間に適用され、もう1つはクラスター全体に有効であるということです。

このチュートリアルでは単一の名前空間を使用しているため、Roleを使用します。

ファイル~/kube-general/cicd-role.ymlを作成し、お気に入りのテキストエディタで開きます。

nano ~/kube-general/cicd-role.yml

基本的な考え方は、default名前空間内のほとんどのKubernetesリソースに関連するすべてを実行するためのアクセスを許可することです。 Roleは次のようになります。

〜/ kube-general / cicd-role.yml

kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: cicd
  namespace: default
rules:
  - apiGroups: ["", "apps", "batch", "extensions"]
    resources: ["deployments", "services", "replicasets", "pods", "jobs", "cronjobs"]
    verbs: ["*"]

このYAMLは、以前に作成したものといくつかの類似点がありますが、ここでは、このリソースがRoleであり、Kubernetes APIrbac.authorization.k8s.io/v1からのものであると言っています。 ロールにcicdという名前を付け、ServiceAccountdefaultを作成したのと同じ名前空間にロールを作成します。

次に、rulesフィールドがあります。これは、このロールがアクセスできるリソースのリストです。 Kubernetesでは、リソースは、それらが属するAPIグループ、リソースの種類自体、および動詞で表されるそのときに実行できるアクションに基づいて定義されます。 これらの動詞はHTTPの動詞に似ています。

この場合、Roleは、deploymentsservices、 [ X142X]、podsjobs、およびcronjobs。 これは、次のAPIグループに属するリソースにも適用されます:""(空の文字列)、appsbatch、およびextensions。 空の文字列は、ルートAPIグループを意味します。 リソースの作成時にapiVersion: v1を使用する場合は、このリソースがこのAPIグループの一部であることを意味します。

Role自体は何もしません。 また、 RoleBinding を作成する必要があります。これは、Roleを何か(この場合はServiceAccount)にバインドします。

ファイル~/kube-general/cicd-role-binding.ymlを作成し、それを開きます。

nano ~/kube-general/cicd-role-binding.yml

次の行をファイルに追加します。

〜/ kube-general / cicd-role-binding.yml

kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: cicd
  namespace: default
subjects:
  - kind: ServiceAccount
    name: cicd
    namespace: default
roleRef:
  kind: Role
  name: cicd
  apiGroup: rbac.authorization.k8s.io

RoleBindingには、このチュートリアルではまだカバーされていない特定のフィールドがいくつかあります。 roleRefは、何かにバインドするRoleです。 この場合、それは前に作成したcicdロールです。 subjectsは、役割をバインドしているリソースのリストです。 この場合、それはcicdと呼ばれる単一のServiceAccountです。

注: ClusterRoleを使用した場合は、RoleBindingの代わりにClusterRoleBindingを作成する必要があります。 ファイルはほとんど同じです。 唯一の違いは、metadata内にnamespaceフィールドがないことです。


これらのファイルを作成すると、kubectl applyを再び使用できるようになります。 次のコマンドを実行して、Kubernetesクラスターにこれらの新しいリソースを作成します。

kubectl apply -f ~/kube-general/

次のような出力が表示されます。

Outputrolebinding.rbac.authorization.k8s.io/cicd created
role.rbac.authorization.k8s.io/cicd created
serviceaccount/cicd unchanged

ここで、前に実行したコマンドを試してください。

kubectl --insecure-skip-tls-verify --kubeconfig="/dev/null" --server=server-from-kubeconfig-file --token=$TOKEN get pods

ポッドがないため、次の出力が生成されます。

OutputNo resources found in default namespace.

このステップでは、CircleCIで使用するサービスアカウントに、リソースの一覧表示、作成、更新など、クラスターで意味のあるアクションを実行するために必要な承認を与えました。 次に、サンプルアプリケーションを作成します。

ステップ5—サンプルアプリケーションの作成

注:これ以降に作成されるすべてのコマンドとファイルは、前に作成した~/do-sample-appフォルダーから開始されます。 これは、クラスターにデプロイするサンプルアプリケーションに固有のファイルを作成しているためです。


作成するKubernetesDeploymentは、 Nginx イメージをベースとして使用し、アプリケーションは単純な静的HTMLページになります。 Nginxから直接HTMLを提供することでデプロイメントが機能するかどうかをテストできるため、これは素晴らしいスタートです。 後で説明するように、ローカルaddress:portに着信するすべてのトラフィックをクラスター上のデプロイメントにリダイレクトして、クラスターが機能しているかどうかをテストできます。

以前に設定したリポジトリ内に、新しいDockerfileファイルを作成し、選択したテキストエディタで開きます。

nano ~/do-sample-app/Dockerfile

その上に次のように書いてください。

〜/ do-sample-app / Dockerfile

FROM nginx:1.21

COPY index.html /usr/share/nginx/html/index.html

これにより、Dockerはnginxイメージからアプリケーションコンテナをビルドするようになります。

次に、新しいindex.htmlファイルを作成し、それを開きます。

nano ~/do-sample-app/index.html

次のHTMLコンテンツを記述します。

〜/ do-sample-app / index.html

<!DOCTYPE html>
<title>DigitalOcean</title>
<body>
  Kubernetes Sample Application
</body>

このHTMLは、アプリケーションが機能しているかどうかを知らせる簡単なメッセージを表示します。

イメージをビルドして実行することで、イメージが正しいかどうかをテストできます。

まず、次のコマンドを使用してイメージをビルドし、dockerhub-usernameを独自のDockerHubユーザー名に置き換えます。 ここでユーザー名を指定する必要があります。これにより、後でDockerHubにプッシュしたときに機能します。

docker build ~/do-sample-app/ -t dockerhub-username/do-kubernetes-sample-app

次に、イメージを実行します。 次のコマンドを使用して、イメージを開始し、ポート8080のローカルトラフィックをイメージ内のポート80に転送します。ポートNginxはデフォルトでリッスンします。

docker run --rm -it -p 8080:80 dockerhub-username/do-kubernetes-sample-app

コマンドの実行中は、コマンドプロンプトのインタラクティブ機能が停止します。 代わりに、Nginxアクセスログが表示されます。 いずれかのブラウザでlocalhost:8080を開くと、~/do-sample-app/index.htmlのコンテンツを含むHTMLページが表示されます。 使用可能なブラウザがない場合は、新しいターミナルウィンドウを開き、次のcurlコマンドを使用してWebページからHTMLをフェッチできます。

curl localhost:8080

次の出力が表示されます。

Output<!DOCTYPE html>
<title>DigitalOcean</title>
<body>
  Kubernetes Sample Application
</body>

コンテナー(コンテナーが実行されているターミナルのCTRL + C)を停止し、このイメージをDockerHubアカウントに送信します。 これを行うには、最初にDockerHubにログインします。

docker login

Docker Hubアカウントに関する必要な情報を入力し、次のコマンドでイメージをプッシュします(dockerhub-usernameを独自のものに置き換えることを忘れないでください)。

docker push dockerhub-username/do-kubernetes-sample-app

これで、サンプルアプリケーションイメージがDockerHubアカウントにプッシュされました。 次のステップでは、このイメージからDOKSクラスターにデプロイメントを作成します。

ステップ6—Kubernetesのデプロイとサービスを作成する

Dockerイメージを作成して機能させたら、クラスター上でDockerイメージからDeploymentを作成する方法をKubernetesに指示するマニフェストを作成します。

YAMLデプロイメントファイル~/do-sample-app/kube/do-sample-deployment.ymlを作成し、テキストエディターで開きます。

nano ~/do-sample-app/kube/do-sample-deployment.yml

ファイルに次のコンテンツを書き込み、dockerhub-usernameをDockerHubのユーザー名に置き換えてください。

〜/ do-sample-app / kube / do-sample-deployment.yml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: do-kubernetes-sample-app
  namespace: default
  labels:
    app: do-kubernetes-sample-app
spec:
  replicas: 1
  selector:
    matchLabels:
      app: do-kubernetes-sample-app
  template:
    metadata:
      labels:
        app: do-kubernetes-sample-app
    spec:
      containers:
        - name: do-kubernetes-sample-app
          image: dockerhub-username/do-kubernetes-sample-app:latest
          ports:
            - containerPort: 80
              name: http

KubernetesのデプロイはAPIグループappsからのものであるため、マニフェストのapiVersionapps/v1に設定されます。 metadataで、metadata.labelsと呼ばれる以前に使用したことのない新しいフィールドを追加しました。 これは、展開を整理するのに役立ちます。 フィールドspecは、デプロイメントの動作仕様を表します。 デプロイメントは、1つ以上のポッドを管理する責任があります。 この場合、spec.replicasフィールドによって単一のレプリカが作成されます。 つまり、単一のポッドを作成して管理します。

ポッドを管理するには、デプロイメントが担当するポッドを認識している必要があります。 spec.selectorフィールドは、その情報を提供するフィールドです。 この場合、展開はタグapp=do-kubernetes-sample-appを持つすべてのポッドを担当します。 spec.templateフィールドには、この展開で作成されるPodの詳細が含まれます。 テンプレート内には、spec.template.metadataフィールドもあります。 このフィールド内のlabelsは、spec.selectorで使用されているものと一致する必要があります。 spec.template.specはポッド自体の仕様です。 この場合、do-kubernetes-sample-appと呼ばれる単一のコンテナーが含まれています。 そのコンテナのイメージは、以前にビルドしてDockerHubにプッシュしたイメージです。

このYAMLファイルは、このコンテナがポート80を公開していることをKubernetesに通知し、このポートにhttpという名前を付けます。

Deploymentによって公開されているポートにアクセスするには、サービスを作成します。 ~/do-sample-app/kube/do-sample-service.ymlという名前のファイルを作成し、お気に入りのエディターで開きます。

nano ~/do-sample-app/kube/do-sample-service.yml

次に、ファイルに次の行を追加します。

〜/ do-sample-app / kube / do-sample-service.yml

apiVersion: v1
kind: Service
metadata:
  name: do-kubernetes-sample-app
  namespace: default
  labels:
    app: do-kubernetes-sample-app
spec:
  type: ClusterIP
  ports:
    - port: 80
      targetPort: http
      name: http
  selector:
    app: do-kubernetes-sample-app

このファイルは、Serviceに展開で使用されるのと同じラベルを付けます。 これは必須ではありませんが、Kubernetesでアプリケーションを整理するのに役立ちます。

サービスリソースには、specフィールドもあります。 spec.typeフィールドは、サービスの動作を担当します。 この場合、これはClusterIPです。これは、サービスがクラスター内部IPで公開され、クラスター内からのみ到達可能であることを意味します。 これは、サービスのデフォルトのspec.typeです。 spec.selectorは、このサービスで公開するポッドを選択するときに使用するラベルセレクターの基準です。 ポッドにはapp: do-kubernetes-sample-appというタグが付いているので、ここで使用しました。 spec.portsは、このサービスから公開するポッドのコンテナによって公開されるポートです。 ポッドには、ポート80を公開するhttpという名前の単一のコンテナーがあるため、ここではtargetPortとして使用しています。 このサービスは、同じ名前のポート80でもそのポートを公開しますが、コンテナーのものとは異なるポート/名前の組み合わせを使用することもできます。

ServiceおよびDeploymentマニフェストファイルを作成したら、kubectlを使用してKubernetesクラスターにこれらのリソースを作成できるようになります。

kubectl apply -f ~/do-sample-app/kube/

次の出力が表示されます。

Outputdeployment.apps/do-kubernetes-sample-app created
service/do-kubernetes-sample-app created

マシンの1つのポートを、サービスがKubernetesクラスター内で公開しているポートに転送して、これが機能しているかどうかをテストします。 kubectl port-forwardを使用してこれを行うことができます。

kubectl port-forward $(kubectl get pod --selector="app=do-kubernetes-sample-app" --output jsonpath='{.items[0].metadata.name}') 8080:80

サブシェルコマンド$(kubectl get pod --selector="app=do-kubernetes-sample-app" --output jsonpath='{.items[0].metadata.name}')は、使用したappタグに一致するポッドの名前を取得します。 それ以外の場合は、kubectl get podsを使用してポッドのリストから取得できます。

port-forwardを実行すると、シェルは対話型でなくなり、代わりにクラスターにリダイレクトされた要求を出力します。

OutputForwarding from 127.0.0.1:8080 -> 80
Forwarding from [::1]:8080 -> 80

任意のブラウザでlocalhost:8080を開くと、コンテナをローカルで実行したときに表示されたのと同じページがレンダリングされますが、現在はKubernetesクラスタから取得されています。 以前と同様に、新しいターミナルウィンドウでcurlを使用して、機能しているかどうかを確認することもできます。

curl localhost:8080

次の出力が表示されます。

Output<!DOCTYPE html>
<title>DigitalOcean</title>
<body>
  Kubernetes Sample Application
</body>

次に、作成したすべてのファイルをGitHubリポジトリにプッシュします。 これを行うには、最初にdigital-ocean-kubernetes-deployという名前のリポジトリをGitHubに作成する必要があります。

デモンストレーションの目的でこのリポジトリをシンプルに保つために、GitHub UIで要求されたときに、READMElicense、または.gitignoreファイルで新しいリポジトリを初期化しないでください。 これらのファイルは後で追加できます。

リポジトリを作成したら、ローカルリポジトリをGitHub上のリポジトリにポイントします。 これを行うには、CTRL + Cを押してkubectl port-forwardを停止し、コマンドラインを元に戻します。次に、次のコマンドを実行して、originという新しいリモコンを追加します。 :

cd ~/do-sample-app/
git remote add origin https://github.com/your-github-account-username/digital-ocean-kubernetes-deploy.git

前のコマンドからの出力はありません。

次に、これまでに作成したすべてのファイルをGitHubリポジトリにコミットします。 まず、ファイルを追加します。

git add --all

次に、ファイルをリポジトリにコミットします。コミットメッセージは引用符で囲みます。

git commit -m "initial commit"

これにより、次のような出力が得られます。

Output[master (root-commit) db321ad] initial commit
 4 files changed, 47 insertions(+)
 create mode 100644 Dockerfile
 create mode 100644 index.html
 create mode 100644 kube/do-sample-deployment.yml
 create mode 100644 kube/do-sample-service.yml

最後に、ファイルをGitHubにプッシュします。

git push -u origin master

ユーザー名とパスワードの入力を求められます。 これを入力すると、次のような出力が表示されます。

OutputCounting objects: 7, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (7/7), done.
Writing objects: 100% (7/7), 907 bytes | 0 bytes/s, done.
Total 7 (delta 0), reused 0 (delta 0)
To github.com:your-github-account-username/digital-ocean-kubernetes-deploy.git
 * [new branch]      master -> master
Branch master set up to track remote branch master from origin.

GitHubリポジトリページに移動すると、そこにあるすべてのファイルが表示されます。 プロジェクトをGitHubに設定すると、CircleCIをCI/CDツールとして設定できるようになります。

ステップ7—CircleCIの構成

このチュートリアルでは、CircleCIを使用して、コードが更新されるたびにアプリケーションのデプロイを自動化するため、GitHubアカウントを使用してCircleCIにログインし、リポジトリを設定する必要があります。

まず、彼らのホームページ https://circleci.com にアクセスし、サインアップを押します。

GitHubを使用しているので、緑色のGitHubでサインアップボタンをクリックします。

CircleCIはGitHubの認証ページにリダイレクトします。 CircleCIは、プロジェクトの構築を開始できるようにするために、アカウントにいくつかの権限が必要です。 これにより、CircleCIは電子メールを取得し、キーとアクセス許可をデプロイしてリポジトリにフックを作成し、SSHキーをアカウントに追加できます。 CircleCIがデータをどのように処理するかについての詳細が必要な場合は、GitHub統合に関するドキュメントを確認してください。

CircleCIを承認すると、プロジェクトページにリダイレクトされます。 ここで、CircleCIでGitHubリポジトリを設定できます。 digital-ocean-kubernetes-deployリポジトリのエントリでプロジェクトのセットアップを選択します。 次に、 Faster:スターターCIパイプラインを新しいブランチオプションにコミットするを選択します。 これにより、プロジェクトの新しいcircleci-project-setupブランチが作成されます。

次に、CircleCI設定でいくつかの環境変数を指定します。 プロジェクトの設定を確認するには、ページの右上にあるプロジェクト設定ボタンを選択し、環境変数を選択します。 環境変数の追加を押して、新しい環境変数を作成します。

まず、DOCKERHUB_USERNAMEDOCKERHUB_PASSという2つの環境変数を追加します。これらは、後でイメージをDockerHubにプッシュするために必要になります。 値をそれぞれDockerHubのユーザー名とパスワードに設定します。

次に、KUBERNETES_TOKENKUBERNETES_SERVER、およびKUBERNETES_CLUSTER_CERTIFICATEの3つを追加します。

KUBERNETES_TOKENの値は、サービスアカウントユーザーを使用してKubernetesクラスターで認証するために以前に使用したローカル環境変数の値になります。 ターミナルを閉じた場合は、いつでも次のコマンドを実行してターミナルを再度取得できます。

kubectl get secret $(kubectl get secret | grep cicd-token | awk '{print $1}') -o jsonpath='{.data.token}' | base64 --decode

KUBERNETES_SERVERは、cicdサービスアカウントでログインしたときに--serverフラグとしてkubectlに渡した文字列になります。 これは、server:の後に、~/.kube/configファイル、またはKubernetesクラスターの初期設定時にDigitalOceanダッシュボードからダウンロードしたファイルkubernetes-deployment-tutorial-kubeconfig.yamlにあります。

KUBERNETES_CLUSTER_CERTIFICATEは、~/.kube/configファイルでも使用できるはずです。 これは、クラスターに関連するclustersアイテムのcertificate-authority-dataフィールドです。 長い文字列である必要があります。 必ずすべてコピーしてください。

これらの環境変数のほとんどには機密情報が含まれており、CircleCI YAML構成ファイルに直接配置するのは安全ではないため、これらの環境変数をここで定義する必要があります。

CircleCIがリポジトリの変更をリッスンし、環境変数が構成されたら、構成ファイルを作成します。

サンプルアプリケーションリポジトリ内に.circleciというディレクトリを作成します。

mkdir ~/do-sample-app/.circleci/

このディレクトリ内に、config.ymlという名前のファイルを作成し、お気に入りのエディタで開きます。

nano ~/do-sample-app/.circleci/config.yml

次のコンテンツをファイルに追加し、dockerhub-usernameをDockerHubのユーザー名に置き換えてください。

〜/ do-sample-app / .circleci / config.yml

version: 2.1
jobs:
  build:
    docker:
      - image: circleci/buildpack-deps:bullseye
    environment:
      IMAGE_NAME: dockerhub-username/do-kubernetes-sample-app
    working_directory: ~/app
    steps:
      - checkout
      - setup_remote_docker
      - run:
          name: Build Docker image
          command: |
            docker build -t $IMAGE_NAME:latest .
      - run:
          name: Push Docker Image
          command: |
            echo "$DOCKERHUB_PASS" | docker login -u "$DOCKERHUB_USERNAME" --password-stdin
            docker push $IMAGE_NAME:latest
workflows:
  version: 2
  build-deploy-master:
    jobs:
      - build:
          filters:
            branches:
              only: master

これにより、ワークフローbuild-deploy-masterが設定され、現在buildという単一のジョブがあります。 このジョブは、masterブランチへのコミットごとに実行されます。

buildジョブは、イメージcircleci/buildpack-deps:bullseyeを使用してステップを実行します。これは、公式のbuildpack-deps Dockerイメージに基づくCircleCIからのイメージですが、次のようないくつかの追加ツールがインストールされています。 Dockerバイナリ自体。

ワークフローには4つのステップがあります。

  • checkoutはGitHubからコードを取得します。
  • setup_remote_dockerは、ビルドごとにリモートの分離された環境をセットアップします。 これは、ジョブステップ内でdockerコマンドを使用する前に必要です。 これが必要なのは、Dockerイメージ内でステップが実行されているため、setup_remote_dockerが別のマシンを割り当ててそこでコマンドを実行するためです。
  • 最初のrunステップは、ローカル環境で以前に行ったように、イメージをビルドします。 そのためには、environment:IMAGE_NAMEで宣言した環境変数を使用しています。
  • 最後のrunステップでは、プロジェクト設定で構成した環境変数を使用してイメージをDockerhubにプッシュして認証します。

新しいファイルをリポジトリにコミットし、変更をアップストリームにプッシュします。

cd ~/do-sample-app/
git add .circleci/
git commit -m "add CircleCI config"
git push

これにより、CircleCIで新しいビルドがトリガーされます。 CircleCIワークフローは、イメージを正しくビルドしてDockerHubにプッシュします。

CircleCIワークフローを作成してテストしたので、Docker Hubから最新のイメージを取得し、変更が加えられたときに自動的にデプロイするようにDOKSクラスターを設定できます。

ステップ8—Kubernetesクラスターでのデプロイの更新

GitHubのmasterブランチに変更をプッシュするたびにアプリケーションイメージがビルドされてDockerHubに送信されるようになったので、Kubernetesクラスターのデプロイを更新して、新しいイメージを取得して使用できるようにします。展開のベースとして。

これを行うには、最初に展開に関する1つの問題を修正します。現在、latestタグが付いたイメージに依存しています。 このタグは、使用している画像のバージョンを示していません。 新しいイメージをDockerHubにプッシュするたびに上書きされるため、デプロイをそのタグに簡単にロックすることはできません。そのように使用すると、コンテナー化されたアプリケーションの再現性が失われます。

これについて詳しくは、 Vladislav Supalovの記事で、最新のタグに依存することがアンチパターンである理由について読むことができます。

これを修正するには、最初に~/do-sample-app/.circleci/config.ymlファイルのPush Docker Imageビルドステップにいくつかの変更を加える必要があります。 ファイルを開きます。

nano ~/do-sample-app/.circleci/config.yml

次に、強調表示された行をPush Docker Imageステップに追加します。

〜/ do-sample-app / .circleci / config.yml

...
      - run:
          name: Push Docker Image
          command: |
            echo "$DOCKERHUB_PASS" | docker login -u "$DOCKERHUB_USERNAME" --password-stdin
            docker tag $IMAGE_NAME:latest $IMAGE_NAME:$CIRCLE_SHA1
            docker push $IMAGE_NAME:latest
            docker push $IMAGE_NAME:$CIRCLE_SHA1
...

CircleCIには、デフォルトでいくつかの特別な環境変数が設定されています。 それらの1つはCIRCLE_SHA1で、これには構築中のコミットのハッシュが含まれています。 ~/do-sample-app/.circleci/config.ymlに加えた変更では、この環境変数を使用して、ビルド元のコミットでイメージにタグを付け、常に最新のビルドに最新のタグを付けます。 そうすれば、リポジトリに新しいものをプッシュするときにそれらを上書きすることなく、常に特定のイメージを利用できます。

ファイルを保存して終了します。

次に、デプロイメントマニフェストファイルをそのファイルを指すように変更します。 ~/do-sample-app/kube/do-sample-deployment.yml内で画像をdockerhub-username/do-kubernetes-sample-app:$COMMIT_SHA1として設定できる場合、これは小さな変更になりますが、kubectlは、 [を使用すると、マニフェスト内で変数置換を行いません。 X173X]。 これを説明するために、envsubstを使用できます。 envsubstは、GNU gettextプロジェクトの一部であるCLIツールです。 テキストを渡すことができ、テキスト内で一致する環境変数を持つ変数が見つかった場合は、その変数をそれぞれの値に置き換えます。 結果のテキストは、出力として返されます。

これを使用するには、デプロイメントを担当するbashスクリプトを作成します。 ~/do-sample-app/内にscriptsという名前の新しいフォルダーを作成します。

mkdir ~/do-sample-app/scripts/

そのフォルダ内にci-deploy.shという新しいbashスクリプトを作成し、お気に入りのテキストエディタで開きます。

nano ~/do-sample-app/scripts/ci-deploy.sh

その中に次のbashスクリプトを記述します。

〜/ do-sample-app / scripts / ci-deploy.sh

#! /bin/bash
# exit script when any command ran here returns with non-zero exit code
set -e

COMMIT_SHA1=$CIRCLE_SHA1

# Export it so it's available for envsubst
export COMMIT_SHA1=$COMMIT_SHA1

#  Since the only way for envsubst to work on files is using input/output redirection,
#  it's not possible to do in-place substitution, so you will save the output to another file
#  and overwrite the original with that one.
envsubst <./kube/do-sample-deployment.yml >./kube/do-sample-deployment.yml.out
mv ./kube/do-sample-deployment.yml.out ./kube/do-sample-deployment.yml

echo "$KUBERNETES_CLUSTER_CERTIFICATE" | base64 --decode > cert.crt

./kubectl \
  --kubeconfig=/dev/null \
  --server=$KUBERNETES_SERVER \
  --certificate-authority=cert.crt \
  --token=$KUBERNETES_TOKEN \
  apply -f ./kube/

ファイル内のコメントを使用して、このスクリプトを実行してみましょう。 まず、次のとおりです。

set -e

この行は、失敗したコマンドがbashスクリプトの実行を停止することを確認します。 そうすれば、1つのコマンドが失敗しても、次のコマンドは実行されません。

COMMIT_SHA1=$CIRCLE_SHA1
export COMMIT_SHA1=$COMMIT_SHA1

これらの行は、CircleCI$CIRCLE_SHA1環境変数を新しい名前でエクスポートします。 exportを使用して変数をエクスポートせずに宣言したばかりの場合、envsubstコマンドでは表示されません。

envsubst <./kube/do-sample-deployment.yml >./kube/do-sample-deployment.yml.out
mv ./kube/do-sample-deployment.yml.out ./kube/do-sample-deployment.yml

envsubstはインプレース置換を実行できません。 つまり、ファイルの内容を読み取ったり、変数をそれぞれの値に置き換えたり、出力を同じファイルに書き戻したりすることはできません。 したがって、出力を別のファイルにリダイレクトしてから、元のファイルを新しいファイルで上書きします。

echo "$KUBERNETES_CLUSTER_CERTIFICATE" | base64 --decode > cert.crt

CircleCIのプロジェクト設定で以前に作成した環境変数$KUBERNETES_CLUSTER_CERTIFICATEは、実際にはBase64でエンコードされた文字列です。 kubectlで使用するには、その内容をデコードしてファイルに保存する必要があります。 この場合、現在の作業ディレクトリ内のcert.crtという名前のファイルに保存しています。

./kubectl \
  --kubeconfig=/dev/null \
  --server=$KUBERNETES_SERVER \
  --certificate-authority=cert.crt \
  --token=$KUBERNETES_TOKEN \
  apply -f ./kube/

最後に、kubectlを実行しています。 このコマンドには、サービスアカウントをテストしたときに実行したものと同様の引数があります。 CircleCIでは現在の作業ディレクトリがプロジェクトのルートフォルダであるため、apply -f ./kube/を呼び出しています。 ./kube/~/do-sample-app/kubeフォルダーです。

ファイルを保存し、実行可能であることを確認します。

chmod +x ~/do-sample-app/scripts/ci-deploy.sh

次に、~/do-sample-app/kube/do-sample-deployment.ymlを編集します。

nano ~/do-sample-app/kube/do-sample-deployment.yml

コンテナイメージ値のタグを次のように変更します。

〜/ do-sample-app / kube / do-sample-deployment.yml

...
      containers:
        - name: do-kubernetes-sample-app
          image: dockerhub-username/do-kubernetes-sample-app:$COMMIT_SHA1
          ports:
            - containerPort: 80
              name: http

ファイルを保存して閉じます。 ここで、CI構成ファイルにいくつかの新しいステップを追加して、Kubernetesでのデプロイを更新する必要があります。

お気に入りのテキストエディタで~/do-sample-app/.circleci/config.ymlを開きます。

nano ~/do-sample-app/.circleci/config.yml

以前に作成したbuildジョブのすぐ下に、次の新しいジョブを記述します。

〜/ do-sample-app / .circleci / config.yml

...
          command: |
            echo "$DOCKERHUB_PASS" | docker login -u "$DOCKERHUB_USERNAME" --password-stdin
            docker tag $IMAGE_NAME:latest $IMAGE_NAME:$CIRCLE_SHA1
            docker push $IMAGE_NAME:latest
            docker push $IMAGE_NAME:$CIRCLE_SHA1
  deploy:
    docker:
      - image: circleci/buildpack-deps:bullseye
    working_directory: ~/app
    steps:
      - checkout
      - run:
          name: Install envsubst
          command: |
            sudo apt-get update && sudo apt-get -y install gettext-base
      - run:
          name: Install kubectl
          command: |
            curl -LO https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/linux/amd64/kubectl
            chmod u+x ./kubectl
      - run:
          name: Deploy Code
          command: ./scripts/ci-deploy.sh
...

新しいdeployジョブの最初の2つのステップは、いくつかの依存関係をインストールすることです。最初にenvsubst、次にkubectlです。 Deploy Codeステップは、デプロイスクリプトの実行を担当します。

次に、このジョブを前に作成したbuild-deploy-masterワークフローに追加します。 build-deploy-masterワークフロー構成内で、buildジョブの既存のエントリの直後に次の新しいエントリを記述します。

〜/ do-sample-app / .circleci / config.yml

...
workflows:
  version: 2
  build-deploy-master:
    jobs:
      - build:
          filters:
            branches:
              only: master
      - deploy:
          requires:
            - build
          filters:
            branches:
              only: master

これにより、deployジョブがbuild-deploy-masterワークフローに追加されます。 deployジョブは、masterへのコミットに対してのみ実行され、buildジョブが完了した後にのみ実行されます。

~/do-sample-app/.circleci/config.ymlの内容は次のようになります。

〜/ do-sample-app / .circleci / config.yml

version: 2.1
jobs:
  build:
    docker:
      - image: circleci/buildpack-deps:bullseye
    environment:
      IMAGE_NAME: dockerhub-username/do-kubernetes-sample-app
    working_directory: ~/app
    steps:
      - checkout
      - setup_remote_docker
      - run:
          name: Build Docker image
          command: |
            docker build -t $IMAGE_NAME:latest .
      - run:
          name: Push Docker Image
          command: |
            echo "$DOCKERHUB_PASS" | docker login -u "$DOCKERHUB_USERNAME" --password-stdin
            docker tag $IMAGE_NAME:latest $IMAGE_NAME:$CIRCLE_SHA1
            docker push $IMAGE_NAME:latest
            docker push $IMAGE_NAME:$CIRCLE_SHA1
  deploy:
    docker:
      - image: circleci/buildpack-deps:bullseye
    working_directory: ~/app
    steps:
      - checkout
      - run:
          name: Install envsubst
          command: |
            sudo apt-get update && sudo apt-get -y install gettext-base
      - run:
          name: Install kubectl
          command: |
            curl -LO https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/linux/amd64/kubectl
            chmod u+x ./kubectl
      - run:
          name: Deploy Code
          command: ./scripts/ci-deploy.sh
workflows:
  version: 2
  build-deploy-master:
    jobs:
      - build:
          filters:
            branches:
              only: master
      - deploy:
          requires:
            - build
          filters:
            branches:
              only: master

これで、ファイルを保存して終了できます。

変更がKubernetesのデプロイに実際に反映されるようにするには、index.htmlを編集します。 HTMLを次のような別のものに変更します。

〜/ do-sample-app / index.html

<!DOCTYPE html>
<title>DigitalOcean</title>
<body>
  Automatic Deployment is Working!
</body>

上記の変更を保存したら、変更したすべてのファイルをリポジトリにコミットし、変更をアップストリームにプッシュします。

cd ~/do-sample-app/
git add --all
git commit -m "add deploy script and add new steps to circleci config"
git push

新しいビルドがCircleCIで実行され、Kubernetesクラスターに変更が正常にデプロイされていることがわかります。

ビルドが完了するのを待ってから、前に実行したのと同じコマンドを実行します。

kubectl port-forward $(kubectl get pod --selector="app=do-kubernetes-sample-app" --output jsonpath='{.items[0].metadata.name}') 8080:80

URL localhost:8080でブラウザを開くか、curlリクエストを送信して、すべてが機能していることを確認します。 更新されたHTMLが表示されます。

curl localhost:8080

次の出力が表示されます。

Output<!DOCTYPE html>
<title>DigitalOcean</title>
<body>
  Automatic Deployment is Working!
</body>

これは、CircleCIを使用して自動展開を正常にセットアップしたことを意味します。

結論

これは、CircleCIを使用してDigitalOceanKubernetesにデプロイする方法に関する基本的なチュートリアルでした。 ここから、さまざまな方法でパイプラインを改善できます。 最初にできることは、複数のデプロイ用に単一のbuildジョブを作成することです。各デプロイメントは、異なるKubernetesクラスターまたは異なる名前空間にデプロイされます。 これは、開発/ステージング/本番環境に異なるGitブランチがあり、デプロイメントが常に分離されていることを確認する場合に役立ちます。

buildpack-depsを使用する代わりに、CircleCIで使用する独自のイメージを作成することもできます。 このイメージはそれに基づいている可能性がありますが、kubectlおよびenvsubstの依存関係がすでにインストールされている可能性があります。

KubernetesのCI/CDの詳細については、Kubernetes WebinarSeriesCI/ CDのチュートリアルをご覧ください。また、Kubernetesのアプリの詳細については、アプリケーションの最新化をご覧ください。 Kubernetes