Real World .NET Core on Kubernetes

Real World .NET Core on Kubernetes

1fe26e11357f3ba7250b6668ca61309f?s=128

Mayuki Sawatari

October 18, 2019
Tweet

Transcript

  1. None
  2. (ハンドルネーム) https://github.com/mayuki

  3. Kubernetes環境の構築と.NET Coreアプリを 乗せる際のポイントと実際に使用しての知見

  4. サービスと構成 Kubernetes / Amazon EKS の選択について 基本方針 Kubernetes環境構築あれこれ CIとデプロイ ロギング

    .NET Core にまつわるいろいろ
  5. Lifebear 手帳アプリ 2019年4月にiOS版リニューアル

  6. None
  7. None
  8. Webアプリケーション APIサーバー (新, 旧) Worker バッチ

  9. Amazon EKS (Kuberentes) Amazon EC2 (Linux / EKS Optimized AMI)

    Application Load Balancer Aurora, ElastiCache (Redis), S3 など
  10. None
  11. 難しいことをやりすぎない できるだけシンプルにする クラスターに状態は持たない

  12. Kubernetesというエコシステム Amazon ECSの概念とKubernetesの概念 2018年末に東京リージョンでEKSがGA

  13. AWSのサービスとの親和性、サポート Kubernetesやインフラに興味がない

  14. None
  15. YAMLと付き合う クラスターとnamespace構成 利用している関連ツール リソースプランニング

  16. たくさんのYAMLと環境ごとの差分 共通化しつつ差分を扱えるツールの必要性 Helm Kustomize

  17. Overlay という概念で複数環境を扱える 出力結果はあくまでコネコネしたYAML

  18. 引用: github.com/kubernetes-sig/kustomize

  19. 引用: github.com/kubernetes-sig/kustomize

  20. スタンダードになりそう 追加知識が多くない 一括で共通の値を設定できたり便利

  21. クラスターはアプリ+環境単位 namespaceはメイン環境+ブランチ等のサブ環境

  22. kube2iam aws-alb-ingress-controller external-dns

  23. PodにIAMロールをアタッチできる拡張 稼働するアプリ単位で権限コントロール apiVersion: apps/v1 kind: Deployment metadata: name: my-app annotations:

    iam.amazonaws.com/role: EksPodRole-MyApplication 参考: サービスアカウントの IAM ロール
  24. ALB Ingress Controller external-dns

  25. apiVersion: extensions/v1beta1 kind: Ingress metadata: name: my-app annotations: kubernetes.io/ingress.class: alb

    略: ... # ロードバランサーに割り当てる DNS 名 external-dns.alpha.kubernetes.io/hostname: api.dev.example.com spec: rules: - http: paths: - path: /SubSystem/* backend: serviceName: my-api-subsystem servicePort: 80 - path: /* backend: serviceName: my-api servicePort: 80
  26. apiVersion: extensions/v1beta1 kind: Ingress metadata: name: my-app annotations: kubernetes.io/ingress.class: alb

    略: ... # ロードバランサーに割り当てる DNS 名 external-dns.alpha.kubernetes.io/hostname: api-branch-feature-1.dev.example.com spec: rules: - http: ... hostnameを書き換えて別環境用に作成
  27. 専用のゾーンを切っておく

  28. 簡単ブランチ開発環境

  29. # base/deployment.yaml apiVersion: apps/v1 kind: Deployment ... # base/ingress.yaml apiVersion:

    extensions/v1beta1 kind: Ingress metadata: name: my-app annotations: # ロードバランサーに割り当てる DNS 名 external-dns.alpha.kubernetes.io/hostname: dev.example.com ... # base/kustomization.yaml resources: - deployment.yaml - ingress.yaml
  30. # overlay/branch/kustomization.yaml namespace: dev-branch-feature-1 commonAnnotations: k8s.example.com/gitBranch: dev-branch-feature-1 bases: - ../../base/

    patches: - ingress-patch.yaml # overlay/branch/ingress-patch.yaml - op: replace path: /metadata/annotations/external-dns.alpha.kubernetes.io~1hostname value: dev-branch-feature-1.example.com
  31. # 完成品YAML namespace: dev-branch-feature-1 apiVersion: apps/v1 kind: Deployment metadata: annotations:

    k8s.example.com/gitBranch: dev-branch-feature-1 ... --- namespace: dev-branch-feature-1 apiVersion: extensions/v1beta1 kind: Ingress metadata: name: my-app annotations: k8s.example.com/gitBranch: dev-branch-feature-1 external-dns.alpha.kubernetes.io/hostname: dev-branch-feature-1.example.com ...
  32. Podに対するCPUとメモリの割り当て量 CPUの割り当ては1コア時間以下が基本 https://cloud.google.com/blog/products/gcp/kubernetes-best-practices-resource-requests-and-limits

  33. カツカツにしすぎるとデプロイ時の余剰がない CoreDNSとかアプリ以外の分 Kubeletに割り当てる分

  34. None
  35. CIとデプロイにはAzure Pipelinesを利用 Releaseパイプライン Self-host agent

  36. Spinnaker: Continuous Deployment Prow: Kubernetes based CI/CD AWS CodeBuild/CodePipeline

  37. Dockerビルドする ECRにDockerイメージをpushする Kubernetes用YAMLを生成、成果物として保存 生成したYAMLを適用する (開発環境デプロイ)

  38. Release パイプラインを使用 1. 成果物にある生成されたYAMLをダウンロード 2. annotation内のリリース番号をsedで雑置換 3. kubectl apply -f

    manifest.prod.yaml 4. kubectl rollout status で待機
  39. やっている手順はそれだけ

  40. リソースにラベルを振って個々でapply apiVersion: apps/v1 kind: Deployment metadata: labels: app: myapp k8s.example.com/type:

    deployment kubectlでラベルを指定し特定のリソースだけ反映
  41. Approval (承認) 機能

  42. ビルドはDockerビルド+YAMLを生成 デプロイはkubectl apply フレーバーはかけてあるけど仕組みは単純にした

  43. None
  44. ログについて考えると ログを出力する側 ログを集めて取り扱う側

  45. 一般的なセオリー通り出力は標準出力へ 構造化ロギング JSONで吐き出す

  46. Microsoft.Extensions.Loggingのログプロバイダー https://github.com/mayuki/JsonStreamLogger 1 ログレコード、1 JSON行としてStreamに出力 logger.Log(LogLevel.Information, "[{Id}] is {Hello}", 12345,

    "Konnnichiwa"); logger.Log(LogLevel.Warning, new EventId(987, "NanikaEvent"), "[{Id}] is {Hello}", 67890, "Nya-n"); logger.LogError(ex, "[{Id}] is {ExceptionType}: {ExceptionMessage}", 77777, ex.GetType().FullName, ex.Message); {"Category":"Test","LogLevel":2,"EventId":{"Id":0,"Name":null},"State":{"Id":12345,"Hello":"Konnnichiwa"},"Exception":null,"Message" :"[12345] is Konnnichiwa"} {"Category":"Test","LogLevel":3,"EventId":{"Id":987,"Name":"NanikaEvent"},"State":{"Id":67890,"Hello":"Nya- n"},"Exception":null,"Message":"[67890] is Nya-n"} {"Category":"Test","LogLevel":4,"EventId":{"Id":0,"Name":null},"State":{"Id":77777,"ExceptionType":"System.Exception","ExceptionMess age":"Yabai"},"Exception":{"Name":"System.Exception","Message":"Yabai","StackTrace":"(snip)","InnerException":null}},"Message":"[777 77] is System.Exception: Yabai"}
  47. アプリが実際に使うのはILogger をラップしたもの

  48. ロガーはすべて登録しておいて設定で無効化 - Logging__Console__LogLevel__Default: None - Logging__JsonStream__LogLevel__Default: Trace { "Logging": {

    "Console": { "LogLevel": { "Default": "Debug“ } }, "JsonStream": { "LogLevel": { "Default": "None“ } } } }
  49. Kubernetesとコンテナーのログ Datadog Log Management

  50. JSONログの取り扱い 高度なタグベースでの検索 お値段には注意

  51. セオリーに従う 構造化ロギング Datadog Log Management便利

  52. None
  53. コンテナー化 .NET Coreアプリの設定 .NET Coreでワーカーアプリを作る Pod内からPodの関連情報を取得する システムリソースの枯渇 Readiness/Liveness Probe

  54. ローカルファイルシステムに極力頼らない 起動する場所や仕方は様々、設定で柔軟に ログを吐く Windowsの挙動に依存しない

  55. appsettings.jsonとConfigMap apiVersion: apps/v1 kind: Deployment spec: template: spec: containers: -

    name: api envFrom: - configMapRef: name: aspnetcore-environment-variables volumeMounts: - name: app-secrets mountPath: /app/secrets readOnly: true apiVersion: v1 kind: ConfigMap metadata: name: aspnetcore-environment-variables data: # 開発環境 NETCORE_ENVIRONMENT: Development ASPNETCORE_ENVIRONMENT: Development
  56. appsettings.jsonとSecret public static IWebHost BuildWebHost(string[] args) => WebHost.CreateDefaultBuilder(args) .ConfigureAppConfiguration((hostContext, configure)

    => { configure // k8s 上では /app/secrets にシークレットをマウントするお気持ち .AddJsonFile($"secrets/appsettings.json", optional: true); }) apiVersion: apps/v1 kind: Deployment spec: template: spec: containers: - name: api volumeMounts: - name: app-secrets mountPath: /app/secrets readOnly: true volumes: - name: lifebear-app-secrets secret: secretName: app-secrets optional: true
  57. 起動時に設定をログに出力しておく https://gist.github.com/mayuki/0d84d3d0b2bf8f089f69b042f4b650b8 ConnectionStrings:Default: server=Server;user id=User;password=<Secret Hash=1079a1>;database=MyDatabase MyApp:Hello: Konnichiwa! MyApp:Nantoka:Kantoka: True

    Application started. Press Ctrl+C to shut down. Hosting environment: Production { "MyApp": { "Hello": "Konnichiwa!", "Nantoka": { "Kantoka": true } }, "ConnectionStrings": { "Default": "server=Server;user id=User;password=NankaPassword;database=MyDatabase" } }
  58. ジョブキューワーカーのようなものを作る MicroBatchFramework https://github.com/Cysharp/MicroBatchFramework

  59. ロギングの口が提供されている ライフサイクルを扱える apiVersion: apps/v1 kind: Deployment spec: template: spec: #

    シャットダウン要求を受けてからアプリが kill されるまでの猶予時間(秒) terminationGracePeriodSeconds: 60
  60. 自身の名前やannotationなどを取得したいケース Downward API Kubernetes API Pripod apiVersion: v1 kind: Pod

    spec: containers: - name: test-container env: - name: MY_NODE_NAME valueFrom: fieldRef: fieldPath: spec.nodeName - name: MY_POD_NAME valueFrom: fieldRef: fieldPath: metadata.name - name: MY_POD_NAMESPACE valueFrom: fieldRef: fieldPath: metadata.namespace
  61. Podの情報を簡単に取得できるライブラリ https://github.com/mayuki/Pripod ラベルやアノテーションも取得できる Console.WriteLine($"Pod: {Pod.Current.Namespace}/{Pod.Current.Name} @ {Pod.Current.NodeName}"); Console.WriteLine("Labels:"); foreach (var

    keyValue in Pod.Current.Labels) { Console.WriteLine($" - {keyValue.Key}: {keyValue.Value}"); } IsRunningOnKubernetes: True Pod: default/consoleapp1-595b95b5f7-xsdjc @ docker-for-desktop Labels: - pod-template-hash: 1516516193 - run: consoleapp1 Deployment: default/consoleapp1
  62. 雑に一台のサーバーにPodを起動しまくると リソースが枯渇することがある https://github.com/dotnet/corefx/issues/32024

  63. Kubernetesのヘルスチェック機構に対応させる Readiness Probe: アプリが準備できているか Liveness Probe: アプリが正常に動作しているか

  64. ASP .NET CoreにはHealth Check APIがある using Microsoft.Extensions.Diagnostics.HealthChecks; services.AddHealthChecks() .AddCheck("Readiness", ()

    => HealthCheckResult.Healthy()) .AddCheck<ApiHealthCheck>("Liveness"); ... app.UseHealthChecks("/health/readiness", new HealthCheckOptions { Predicate = check => check.Name == "Readiness", }); app.UseHealthChecks("/health/liveness", new HealthCheckOptions { Predicate = check => check.Name == "Liveness", });
  65. ASP .NET CoreにはHealth Check APIがある public class ApiHealthCheck : IHealthCheck

    { public async Task<HealthCheckResult> CheckHealthAsync( HealthCheckContext context, CancellationToken cancellationToken = default ) { // 外部サービスへの接続性をチェックする var service = NantokaServiceClient.Instance; await service.GetNantokaKantokaAsync(); return HealthCheckResult.Healthy(); // ヨシ! } }
  66. コンテナー化されると若干事情が変わる 基本は大きく変わらない

  67. None
  68. 無理ない範囲でやっていき Kubernetesはインフラをアプリに寄せる コンテナー前提な.NET Coreアプリを作る

  69. Kubernetes AWS/EKS ベストプラクティス 2019.2 https://speakerdeck.com/mumoshu/eksbesutopurakuteisu2019-dot-2-number-jawsdays Kubernetes Failure Stories https://srcco.de/posts/kubernetes-failure-stories.html Kubernetes上でアプリケーションを運用するまで

    の道のり https://speakerdeck.com/shmurata/the-way-to-run-applications-with-kubernetes
  70. None
  71. None