KubernetesおよびVSTSを使用したDevOps。 パート1:地域の歴史

最近、コンテナ、Docker、Kubernetesについてよく話します。 この背景に対して、同僚はますますマイクロソフトの技術がどこにあるのかと尋ね始めましたか? 説明するために、コリン・デンボフスキーからのこの記事のセットを含むいくつかの資料を見つけました。 Docker、Kubernetes、そして当社のテクノロジーなど、すべて揃っています。 Habraの読者にとっても面白いと思う。 だから、最初の部分の翻訳に会います。







私のブログを読んでいただければ、私は一般的なコンテナ、特にDockerのファンであることをご存知でしょう。 ソフトウェアをベアメタルにインストールしたのはいつですか? たぶんラップトップ上でしかありませんが、それでもチャンスはわずかです。 仮想化により、データセンターのリソースに対する考え方が根本的に変わり、密度と効率が大幅に向上しました。 スチールコンテナの密度を高める次の段階では、物理サーバー上にはVMのみが配置され、VM自体にはコンテナが配置されます。 すぐに、私たちのほとんどはサーバーレベルだけでなく、VMレベルでも動作しなくなり、すべてのワークロードがコンテナーに移動します。 しかし、これは見方です。



一連の記事「コンテナについて語る」:



1. 高速展開コンテナ

2. KubernetesおよびVSTSを使用したDevOps。 パート1:ローカル履歴。

3. KubernetesおよびVSTSを使用したDevOps。 パート2:クラウド履歴。

4. Kubernetesの容量が無限大ノード。



パッケージングアプリケーションの観点からコンテナーのすべての利点にもかかわらず、多くはまだ運用環境でコンテナーを実行する方法を理解していません。 1つのコンテナをインストールすることは、どの開発者にとっても興味深く刺激的な経験になりますが、コンテナをスケーリングしたり、クラスタ内でコンテナを組み合わせたりすることはどうでしょうか? コンテナをどのように見ますか? 障害を特定して修正する方法は? ここでは、コンテナオーケストレーションの問題にスムーズに進みます。



この記事では、Kubernetesとminikubeを使用したローカル開発アプローチについて説明します。 パート2では、AzureのKubernetesクラスター用のCI / CDパイプラインの作成について説明します。



戦場-オーケストレーション



MesosKubernetes、 Docker Swarm Modeの 3つの一般的なコンテナオーケストレーションシステムがあります 。 (少なくとも今のところ)誰かの旗の下に立つことを強くお勧めしません。概念的にはすべて同じです。 それらはすべて、「コードとしての構成」の概念を使用して、複数のコンテナーを複数のノードにデプロイします。 Kubernetesは、私の意見では、DevOpsの分野で真のブレークスルーとなる多くの機能を提供します。 構成カード(ConfigMaps)秘密(Secrets) 、および名前空間(namespaces)です。



詳細に入ることなく、名前空間を使用すると、1つのクラスター内に異なる論理環境を作成できます。 例として、テスト用にPROD環境の小さなコピーを実行できるDEV名前空間を次に示します。 名前空間は、マルチテナンシーまたはさまざまなセキュリティコンテキストの実装にも使用されます。 構成カード(ConfigMaps)およびシークレット(秘密)を使用すると、コンテナーの外部に構成を保存できます。つまり、特定の環境用の特定のコードをイメージ自体に埋め込むことなく、1つのイメージを異なるコンテキストで実行できます。



Kubernetesワークフロー(ワークフロー)およびパイプライン(パイプライン)



この記事では、Kubernetes中心の開発へのアプローチを示します。 最初の部分では開発ワークフローを、2番目の部分ではDevOpsパイプラインを見ていきます。 幸いなことに、 MiniKube (VM上で実行されるKubernetesホストが1 つあるクラスター)のおかげで、ラップトップで本格的なクラスターを操作できます! つまり、実稼働クラスターに接続することなく、クラスターテクノロジ(ConfigMapsなど)を活用できます。



それでは、開発者のワークフローを見てみましょう。 次のようなものになります。



  1. コードを開発します。
  2. Dockerfileまたはdocker-composeコマンドを使用して生成されたファイルのバッチに基づいてイメージを作成します。
  3. MiniKubeでサービスを起動します(画像からコンテナを起動します)。


実践が示すように、Visual Studio 2017(および(または)VS Code)、Docker、MiniKubeのおかげで、このパスに落とし穴はありません。



次に、作成から始めてDevOpsパイプラインに進みます。 ソースファイルとDockerfilesに基づいて、プライベートコンテナーレジストリに登録されているイメージが作成されます。 次に、新しいイメージを開始/展開するためにKubernetesクラスターに構成を転送する必要があります。 AzureとVSTSのおかげで、数回クリックするだけでDevOpsパイプラインを作成できます! しかし、これは記事の第2部のトピックであり、開発者のワークフローを調査しています。



開発環境の準備



デモンストレーションでは、Windowsを使用しますが、MacまたはLinuxでも設定は似ています。 ローカル開発環境を展開するには、以下をインストールする必要があります。



  1. Docker
  2. クベクトル
  3. ミニクベ


リンクを使用して、インストールを完了することができます。 その過程で、Hyper-VでMiniKubeを起動すると小さな問題が発生しました。デフォルトでは、MiniKubeのVMを作成するMiniKube起動コマンドは、最初に見つかったHyper-V仮想ネットワークに接続します。 複数のネットワークがあり、MiniKubeが内部ネットワークに接続されていたため、障害が発生しました。 Hyper-Vコンソールでminikubeという新しい仮想ネットワークを作成し、それが外部ネットワークであることを確認しました。 MiniKube VMを作成するには、次のコマンドを使用しました。



c: cd \ minikube start --vm-driver hyperv --hyperv-virtual-switch minikube
      
      





c:\ルートディレクトリに移動するには、cdコマンドを実行する必要がありました。これがないと、MiniKubeはVMを作成できません。



外部ネットワークがWi-Fiアクセスポイントに接続されています。 つまり、新しいネットワークに接続すると、minikube VMは新しいIPアドレスを取得します。 毎回kubeconfigを更新する代わりに、hostsファイルにホスト行を追加しました(Windowsではc:\ windows \ system32 \ drivers \ etc \ hosts):kubernetes、IPは取得したminikube VMのIPアドレスですminikube ipコマンド kubeconfigを更新するには、次のコマンドを使用します。



 kubectl config set-cluster minikube --server=https://kubernetes:8443 --certificate-authority=c:/users/<user>/.minikube/ca.crt
      
      





ここで、<user>はユーザー名です。 したがって、certは.minikubeディレクトリに作成されたca.crtファイルを指します。



これで、新しいネットワークに接続するときに、hostsファイルのIPアドレスを更新するだけで、kubectlコマンドは引き続き機能します。 証明書はkubernetesという名前のノードに対して生成されるため、この名前を使用します。



すべてが正常に機能する場合、kubectl get nodesコマンドに対する簡潔な回答が得られます。



 PS:\> kubectl get nodes NAME STATUS AGE VERSION minikube Ready 11m v1.6.4
      
      





Kubernetes UIを起動するには、minikubeダッシュボードコマンドを入力するだけです。 ブラウザで次のウィンドウが開きます。







最後に、minikubeドッカーコンテキストを「再利用」するには、次のコマンドを実行します。



 minikube docker-env | Invoke-Expression
      
      





これにより、minikubeドッカーソケットが共有されます。 docker psコマンドを実行すると、いくつかの作業コンテナーに関する情報を取得できます。これらは基本的なKubernetesシステムコンテナーです。 また、minikubeクラスターによって起動できる画像をここで作成する可能性も意味します。



これで、単一ノードのクラスターができました。 開発を開始できます!



コードに移動します



少し前、私のブログにAzureとVSTSを使用したAureliaプロジェクトの開発に関する記事を投稿しました。 そして、すでにいくつかの既製の.NET Coreサイトがあったので、それらをKubernetesクラスターで実行することにしました。 このリポジトリのクローンを作成し、Dockerブランチをチェックアウトします 。 いくつかのファイルをリポジトリに追加して、Dockerイメージを作成し、Kubernetes構成を指定できるようにしました。 それがどのように見えるか見てみましょう。



docker-compose.ymlファイルは、2つの画像の複合アプリケーションを定義します:apiとフロントエンド:



 version: '2' services: api: image: api build: context: ./API dockerfile: Dockerfile frontend: image: frontend build: context: ./frontend dockerfile: Dockerfile
      
      





各サービスのDockerfileは可能な限り単純です。ASP.NETCore 1.1イメージから開始し、アプリケーションファイルをコンテナーにコピーし、ポート80を開いて、それぞれのエントリポイントとしてdotnet app.dll(各サイトのfrontend.dllおよびapi.dll)を開始しますコンテナ:



 FROM microsoft/aspnetcore:1.1 ARG source WORKDIR /app EXPOSE 80 COPY ${source:-obj/Docker/publish} . ENTRYPOINT ["dotnet", "API.dll"]
      
      





イメージの作成を準備するには、dotnetのrestore、build、およびpublishコマンドを実行すると、プロジェクトが組み立てられて公開されます。 これで、画像の作成に進むことができます。 既製のイメージがある場合は、minikubeクラスターでそれらを実行するようにKubernetesサービスを構成できます。



画像作成



画像を作成する最も簡単な方法は、Visual Studioを使用することです。 docker-composeプロジェクトをスタートアッププロジェクトとして構成し、実行します。 画像が作成されます。 Visual Studioで作業していない場合は、リポジトリのルートディレクトリから次のコマンドを実行してイメージを作成します。



 cd API dotnet restore dotnet build dotnet publish -o obj/Docker/publish cd ../frontend dotnet restore dotnet build dotnet publish -o obj/Docker/publish cd .. docker-compose -f docker-compose.yml build
      
      





docker imagesコマンドを実行すると、minikubeコンテナーと、フロントエンドおよびAPIのイメージが表示されます。







サービス宣言-コードとしての構成



これで、クラスターで実行するサービスを指定できます。 私の意見では、Kubernetesの利点の1つは、スクリプトを実行する代わりに環境を宣言する必要があることです。 このような宣言型モデルは、命令型モデルよりもはるかに優れており、Chef、Puppet、PowerShell DSCのおかげでより広く普及しています。 Kubernetesでは、実行するサービスとそれらの展開方法を指定できます。 単純なyamlファイルを使用して、さまざまなKubernetesオブジェクトを定義できます。 apiとfrontendの2つのサービスを宣言します。 サーバーサービス(バックエンド)は通常、クラスター外では利用できませんが、この場合、デモコードは1ページのアプリケーションであるため、APIサービスは外部からアクセスできる必要があります。



サービスのリストはめったに変更されません;これらはクラスターで利用可能なサービスです。 ただし、サービスを構成する基になるコンテナー(Kubernetesではポッドと呼ばれます)は変更されます。 更新時およびスケーリング時に変更されます。 Deploymentコンストラクトは、サービスを構成するコンテナを管理するために使用されます。 サービスとデプロイメントは非常に密接に関連しているため、それらを単一のファイルに入れました。 つまり、サービス/デプロイメントフロントエンドのファイル(k8s / app-demo-frontend-minikube.yml)と、サービス/デプロイメントAPIのファイル(k8s / app-demo-backend-minikube.yml)があります。 必要と思われる場合は、サービスと展開の定義を別々のファイルに配置できます。 app-demo-backend.ymlファイルの内容を調べます:



 apiVersion: v1 kind: Service metadata: name: demo-backend-service labels: app: demo spec: selector: app: demo tier: backend ports: - protocol: TCP port: 80 nodePort: 30081 type: NodePort --- apiVersion: apps/v1beta1 kind: Deployment metadata: name: demo-backend-deployment spec: replicas: 2 template: metadata: labels: app: demo tier: backend spec: containers: - name: backend image: api ports: - containerPort: 80 imagePullPolicy: Never
      
      





注:





クライアントサービス(フロントエンドサービス)の定義はほとんど同じですが、構成のために少し構成する必要があります。 それがどのように見えるか見てみましょう。



 spec: containers: - name: frontend image: frontend ports: - containerPort: 80 env: - name: "ASPNETCORE_ENVIRONMENT" value: "Production" volumeMounts: - name: config-volume mountPath: /app/wwwroot/config/ imagePullPolicy: Never volumes: - name: config-volume configMap: name: demo-app-frontend-config
      
      





注:





構成管理



これでコンテナのイメージがいくつか作成され、minikubeで実行できます。 しかし、始める前に、構成について説明しましょう。 あなたが私のスピーチを聞いたり、私のブログを読んだら、あなたは私が「一度作って、何度も展開する」という概念の主要な宣伝者の一人であることを知っています。 これが適切なDevOpsの基本原則です。 Kubernetesとコンテナの場合、状況は似ています。



ただし、このためには、コンパイル済みコードの外部に構成を配置する必要があります。つまり、構成ファイルなどのメカニズムが必要になります。 IISまたはAzureアプリに展開する場合は、web.configファイル(DotNet Coreの場合はappsettings.jsonになります)を使用して、環境ごとに異なる値を指定できます。 しかし、コンテナはどうですか? すべてのアプリケーションコードはコンテナ内にあるため、構成ファイルの異なるバージョンを使用することはできません。そうしないと、コンテナ自体の異なるバージョンが必要になります。つまり、1回限りの作成の原則に違反します。



幸いなことに、シークレットやconfigMaps(Kubernetesコンセプト)と組み合わせて、プラガブルボリューム(コンテナコンセプト)を使用できます。 KubernetesでconfigMaps(基本的にキーと値のペア)またはシークレット(マスクまたは非表示のキーと値のペア)を指定し、コンテナー内のボリュームを接続することで単純にマウントできます。 炉の定義は同じままなので、これは非常に強力なツールですが、異なるconfigMapがある場合、異なる構成を取得します! クラウドクラスターに展開し、名前空間を使用して開発環境と運用環境を分離するときに、これがどのように機能するかを確認します。



configMapsは、コードメソッドとして構成を使用して指定することもできます。 configMapの構成は次のとおりです。



 apiVersion: v1 kind: ConfigMap metadata: name: demo-app-frontend-config labels: app: demo tier: frontend data: config.json: | { "api": { "baseUri": "http://kubernetes:30081/api" } }
      
      





注:





余談:静的ファイルへのシンボリックリンクの問題



configMapsを使用して構成ファイルをマウントすると、実際に1つの問題が発生しました。コンテナー内では、パス/app/www/config/config.jsonがシンボリックリンクで終わります。 Anthony Chuの優れた記事で、コンテナでconfigMapを使用するというアイデアを探り、Startup.csファイルで使用するためにapplication.jsonファイルを接続した方法を説明しました。



明らかに、彼はスタートアップファイルのシンボリックリンクの問題に遭遇しませんでした。 ただし、デモクライアントアプリケーションの場合、SPAアプリケーションで使用される構成ファイルを作成します。クライアント側にあるため、構成ファイルは、単にhtmlまたはjsとしてDotNet Coreアプリケーションから提供する必要があります。 問題ありません。 既にStartupにUseStaticFiles呼び出しがあるので、ファイルを提供するだけですよね? 残念ながら、そうではありません。 ほとんどの場合、このファイルから最初の数バイトのみを転送します。



これを理解するために数日を費やしました。 Githubには特別なトピックがあり、興味があれば読むことができます 。 つまり、シンボリックリンクの長さは、ファイル自体の長さではなく、ファイルパスの長さです。 StaticFilesミドルウェアは、ファイルが要求されるとFileInfo.Lengthバイトを読み取りますが、この長さはファイルの全長と等しくないため、最初の数バイトのみが返されます。 この問題を回避するためにFileProviderツールを作成しました。



Kubernetesでの画像の起動



minikubeで新しく作成したサービスを開始するには、kubectlを使用して構成を適用するだけです。 コマンドのリスト(強調表示された行)は次のとおりです。



 PS:\> cd k8s PS:\> kubectl apply -f .\app-demo-frontend-config.yml configmap "demo-app-frontend-config" created PS:\> kubectl apply -f .\app-demo-backend-minikube.yml service "demo-backend-service" created deployment "demo-backend-deployment" created PS:\> kubectl apply -f .\app-demo-frontend-minikube.yml service "demo-frontend-service" created deployment "demo-frontend-deployment" created
      
      





これでサービスができました! minikubeダッシュボードコマンドでダッシュボードを開き、サービスのステータスが緑色であることを確認できます。







クライアントサービスに接続するには、アドレスhttp:// kubernetes:30080を入力します。









リスト(value1およびvalue2)は、APIサービスによって返される値です。つまり、クライアントはminikubeのサーバーサービスに正常に接続できました。



コンテナの更新



コードを更新した後、コンテナを再作成する必要があります。 構成を更新した後、kubectl applyコマンドを再度実行してconfigMapを更新します。 次に、開発環境で高可用性を必要としないため、実行中のポッドを削除してレプリケーションサービスに再起動させますが、今回は更新された構成および(または)コードを使用します。 もちろん、実稼働環境では、このような自由は受け入れられません。第2部では、KubernetesクラスターでCI / CDを操作するときに順次更新を実行する方法を示します。



しかし、開発環境では、囲炉裏のリストを取得し、それらをすべて削除してから、Kubernetesが(新しい識別子で)コンテナを魔法のように再起動するのを監視します。 その結果、更新されたコンテナーを取得します。



 PS:> kubectl get pods NAME READY STATUS RESTARTS AGE demo-backend-deployment-951716883-fhf90 1/1 Running 0 28m demo-backend-deployment-951716883-pw1r2 1/1 Running 0 28m demo-frontend-deployment-477968527-bfzhv 1/1 Running 0 14s demo-frontend-deployment-477968527-q4f9l 1/1 Running 0 24s PS:> kubectl delete pods demo-backend-deployment-951716883-fhf90 demo -backend-deployment-951716883-pw1r2 demo-frontend-deployment-477968527-bfzhv demo-frontend-deployment-477968527-q4f9l pod "demo-backend-deployment-951716883-fhf90" deleted pod "demo-backend-deployment-951716883-pw1r2" deleted pod "demo-frontend-deployment-477968527-bfzhv" deleted pod "demo-frontend-deployment-477968527-q4f9l" deleted PS:> kubectl get pods NAME READY STATUS RESTARTS AGE demo-backend-deployment-951716883-4dsl4 1/1 Running 0 3m demo-backend-deployment-951716883-n6z4f 1/1 Running 0 3m demo-frontend-deployment-477968527-j2scj 1/1 Running 0 3m demo-frontend-deployment-477968527-wh8x0 1/1 Running 0 3m
      
      





囲炉裏自体はすでに異なっているため、囲炉裏のIDが変更されることに注意してください。 クライアント側に切り替えると、更新されたコードが表示されます。



おわりに



Kubernetesの機能と、このプラットフォームがコードとしてのインフラストラクチャの概念を促進する方法に本当に感銘を受けました。 minikubeを使用して、クラスターをラップトップにローカルに展開し、できるだけ運用環境に近い環境で開発を開始できます。これは常に良い考えです。 本番コンテナで使用されるものと同様のシークレットおよびconfigMapsカードを使用できます。 全体として、これは優れた開発アプローチであり、最初からベストプラクティスに従うことができます。






追伸この記事を説明してくれたKonstantin Kichinsky( Quantum Quintum )に感謝します。



All Articles