Foreverly

メモ帳

Kubernetes Application Update

303-app-updateの内容

Kubernetes Application Update

今回では、Kubernetesクラスタにデプロイされたアプリケーションを使用して更新する方法について説明しデプロイします。 また、アプリケーションのCanary Deploymentについても見ていきます。

Deploymentは、Podを管理するためのレプリカセットを作成します。 レプリカセット内のレプリカの数は、アプリケーションの要求に合わせて拡大縮小できます。 Deploymentを使用してdeployされたアプリケーションを更新するには、 Deploymentの構成を更新する必要があります。

この変更により、新しいレプリカセットが作成されます。 これは、以前のレプリカセットが縮小されている間にスケールアップされます。 これにより、アプリケーションのダウンタイムが発生しません。

kubectl rolling-update コマンドが ありますが、レプリカセットにのみ適用されます。 このコマンドからの更新は、クライアント側で行われました。 更新がサーバー側にあるので、Deploymentを使用してrolling-updateを行うことを強くお勧めします。

我々のユースケースでは、アプリケーションは最初に画像を使用します arungupta/app-upgrade:v1 。 次に、イメージを使用するようにDeploymentが更新されます arungupta/app-upgrade:v2 。 v1イメージは "Hello World!"を出力します。 v2イメージは「Howdy World!」を印刷します。 これらのイメージのソースコードimagesディレクトリにあります。

前提条件

この章の演習を行うには、Kubernetesクラスタ構成を展開する必要があります。 EKSベースのKubernetesクラスタを作成するには、AWS CLIを使用します。 EKSを使用せずにKubernetesクラスタを作成する場合は、代わりにkopsを使用できます。 この章の設定ファイルはすべてapp-updateディレクトリにあります。 この章のコマンドを実行する前に、そのディレクトリに移動してください。 Cloud9で作業している場合は、次のコマンドを実行します。

cd ~/environment/aws-workshop-for-kubernetes/03-path-application-development/303-app-update/

rolling-update

新しいリビジョンへのアップデート

アプリケーションを更新するには、既存のすべてのポッドを、別のバージョンのイメージを使用する新しいポッドに置き換える必要があります。 .spec.strategy デプロイメント設定で、古いポッドを新しいポッドに置き換えるための戦略を定義することができます。 このキーには次の2つの値があります。

  1. Recreate
  2. RollingUpdate (デフォルト)

この2つのデプロイ戦略を見てみましょう。

strategy を再作成する

既存のPodはすべて、新しいものが作成される前に強制終了されます。 設定ファイルは以下のようになります

apiVersion: apps/v1
kind: Deployment
metadata:
  name: app-recreate
spec:
  replicas: 5
  selector:
    matchLabels:
      name: app-recreate
  strategy:
    type: Recreate
  template:
    metadata:
      labels:
        name: app-recreate
    spec:
      containers:
      - name: app-recreate
        image: arungupta/app-upgrade:v1
        ports:
        - containerPort: 8080
  1. deploymentを作成
$ kubectl create -f templates/app-recreate.yaml --record
deployment "app-recreate" created

--recordこのdeploymentを開始するコマンドが確実に記録されます。 これは、アプリケーションがいくつかの更新を経て、バージョンとコマンドを関連付ける必要がある場合に便利です。

  1. deploymentsの履歴を取得
$ kubectl rollout history deployment/app-recreate
deployments "app-recreate"
REVISION  CHANGE-CAUSE
1         kubectl create --filename=templates/app-recreate.yaml --record=true
  1. サービスを公開する
$ kubectl expose deployment/app-recreate --port=80 --target-port=8080 --name=app-recreate --type=LoadBalancer
service "app-recreate" exposed
  1. サービスの詳細を取得する
$ kubectl describe svc/app-recreate
Name:                     app-recreate
Namespace:                default
Labels:                   name=app-recreate
Annotations:              <none>
Selector:                 name=app-recreate
Type:                     LoadBalancer
IP:                       100.65.43.233
LoadBalancer Ingress:     af2dc1f99bda211e791f402037f18a54-1616925381.eu-central-1.elb.amazonaws.com
Port:                     <unset>  80/TCP
TargetPort:               80/TCP
NodePort:                 <unset>  30158/TCP
Endpoints:                100.96.1.14:80,100.96.2.13:80,100.96.3.13:80 + 2 more...
Session Affinity:         None
External Traffic Policy:  Cluster
Events:
  Type    Reason                Age   From                Message
  ----    ------                ----  ----                -------
  Normal  CreatingLoadBalancer  24s   service-controller  Creating load balancer
  Normal  CreatedLoadBalancer   23s   service-controller  Created load balancer
  1. serviceにアクセス
$ curl http://af2dc1f99bda211e791f402037f18a54-1616925381.eu-central-1.elb.amazonaws.com
Hello World!
  1. 別の端末では、Podの状態を確認
$ kubectl get -w pods
app-v1-recreate-486400321-4rwzb   1/1       Running   0          9m
app-v1-recreate-486400321-fqh5l   1/1       Running   0          9m
app-v1-recreate-486400321-jm02h   1/1       Running   0          9m
app-v1-recreate-486400321-rl79n   1/1       Running   0          9m
app-v1-recreate-486400321-z89nm   1/1       Running   0          9m
  1. deploymentのイメージを更新します。
$ kubectl set image deployment/app-recreate app-recreate=arungupta/app-upgrade:v2
deployment "app-recreate" image updated
  1. Podのステータスが更新されます。すべてのPodが最初に終了し、新しいPodが作成されたことを示します。
$ kubectl get -w pods
NAME                              READY     STATUS    RESTARTS   AGE
app-v1-recreate-486400321-4rwzb   1/1       Running   0          9m
app-v1-recreate-486400321-fqh5l   1/1       Running   0          9m
app-v1-recreate-486400321-jm02h   1/1       Running   0          9m
app-v1-recreate-486400321-rl79n   1/1       Running   0          9m
app-v1-recreate-486400321-z89nm   1/1       Running   0          9m
app-v1-recreate-486400321-rl79n   1/1       Terminating   0         10m
app-v1-recreate-486400321-jm02h   1/1       Terminating   0         10m
app-v1-recreate-486400321-fqh5l   1/1       Terminating   0         10m
app-v1-recreate-486400321-z89nm   1/1       Terminating   0         10m
app-v1-recreate-486400321-4rwzb   1/1       Terminating   0         10m
app-v1-recreate-486400321-rl79n   0/1       Terminating   0         10m
app-v1-recreate-486400321-4rwzb   0/1       Terminating   0         10m
app-v1-recreate-486400321-fqh5l   0/1       Terminating   0         10m
app-v1-recreate-486400321-z89nm   0/1       Terminating   0         10m
app-v1-recreate-486400321-jm02h   0/1       Terminating   0         10m
app-v1-recreate-486400321-fqh5l   0/1       Terminating   0         10m
app-v1-recreate-486400321-fqh5l   0/1       Terminating   0         10m
app-v1-recreate-486400321-z89nm   0/1       Terminating   0         10m
app-v1-recreate-486400321-z89nm   0/1       Terminating   0         10m
app-v1-recreate-486400321-rl79n   0/1       Terminating   0         10m
app-v1-recreate-486400321-rl79n   0/1       Terminating   0         10m
app-v1-recreate-486400321-jm02h   0/1       Terminating   0         10m
app-v1-recreate-486400321-jm02h   0/1       Terminating   0         10m
app-v1-recreate-486400321-4rwzb   0/1       Terminating   0         10m
app-v1-recreate-486400321-4rwzb   0/1       Terminating   0         10m
app-v1-recreate-2362379170-fp3j2   0/1       Pending   0         0s
app-v1-recreate-2362379170-xxqqw   0/1       Pending   0         0s
app-v1-recreate-2362379170-hkpt7   0/1       Pending   0         0s
app-v1-recreate-2362379170-jzh5d   0/1       Pending   0         0s
app-v1-recreate-2362379170-k26sf   0/1       Pending   0         0s
app-v1-recreate-2362379170-xxqqw   0/1       Pending   0         0s
app-v1-recreate-2362379170-fp3j2   0/1       Pending   0         0s
app-v1-recreate-2362379170-hkpt7   0/1       Pending   0         0s
app-v1-recreate-2362379170-jzh5d   0/1       Pending   0         0s
app-v1-recreate-2362379170-k26sf   0/1       Pending   0         0s
app-v1-recreate-2362379170-xxqqw   0/1       ContainerCreating   0         0s
app-v1-recreate-2362379170-fp3j2   0/1       ContainerCreating   0         1s
app-v1-recreate-2362379170-hkpt7   0/1       ContainerCreating   0         1s
app-v1-recreate-2362379170-jzh5d   0/1       ContainerCreating   0         1s
app-v1-recreate-2362379170-k26sf   0/1       ContainerCreating   0         1s
app-v1-recreate-2362379170-fp3j2   1/1       Running   0         3s
app-v1-recreate-2362379170-k26sf   1/1       Running   0         3s
app-v1-recreate-2362379170-xxqqw   1/1       Running   0         3s
app-v1-recreate-2362379170-hkpt7   1/1       Running   0         4s
app-v1-recreate-2362379170-jzh5d   1/1       Running   0         4s

出力は、すべてのPodが最初に終了した後、新しいPodが作成されたことを示します。

  1. deploymentsの履歴を手に入れる
$ kubectl rollout history deployment/app-recreate
deployments "app-recreate"
REVISION  CHANGE-CAUSE
1         kubectl create --filename=templates/app-recreate.yaml --record=true
2         kubectl set image deployment/app-recreate app-recreate=arungupta/app-upgrade:v2
  1. 再度アプリケーションにアクセスする
$ curl http://af2dc1f99bda211e791f402037f18a54-1616925381.eu-central-1.elb.amazonaws.com
Howdy World!

出力にv2は、使用されているイメージのバージョンが表示されるようになりました。

Rolling update戦略

Podはローリングアップデートの方法で更新されます。 Rolling updateの実行方法を定義するには、次の2つのオプションのプロパティを使用できます。

  1. .spec.strategy.rollingUpdate.maxSurge 必要な数のポッドに作成できるポッドの最大数を指定します。値には、絶対数またはパーセンテージを使用できます。デフォルト値は25%です。
  2. .spec.strategy.rollingUpdate.maxUnavailable 更新処理中に使用できないポッドの最大数を指定します。

設定ファイルは以下のようになります

apiVersion: apps/v1
kind: Deployment
metadata:
  name: app-rolling
spec:
  replicas: 5
  selector:
    matchLabels:
      name: app-rolling
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 1
  template:
    metadata:
      labels:
        name: app-rolling
    spec:
      containers:
      - name: app-rolling
        image: arungupta/app-upgrade:v1
        ports:
        - containerPort: 8080

この場合、最大数のポッド上に1つ以上のポッドを作成することができ、更新プロセス中には1つのポッドしか利用できなくなります。

  1. deploymentを作成
$ kubectl create -f templates/app-rolling.yaml --record
deployment "app-rolling" created

再度、--recordこの展開を開始するコマンドが確実に記録されます。 これは、アプリケーションがいくつかの更新を経て、バージョンとコマンドを関連付ける必要がある場合に便利です。

  1. デプロイの履歴を取得する
$ kubectl rollout history deployment/app-rolling
deployments "app-rolling"
REVISION  CHANGE-CAUSE
1         kubectl create --filename=templates/app-rolling.yaml --record=true
  1. サービスを公開する
$ kubectl expose deployment/app-rolling --port=80 --target-port=8080 --name=app-rolling --type=LoadBalancer
service "app-rolling" exposed
  1. サービスの詳細を取得する
$ kubectl describe svc/app-rolling
Name:                     app-rolling
Namespace:                default
Labels:                   name=app-rolling
Annotations:              <none>
Selector:                 name=app-rolling
Type:                     LoadBalancer
IP:                       100.71.164.130
LoadBalancer Ingress:     abe27b4c7bdaa11e791f402037f18a54-647142678.eu-central-1.elb.amazonaws.com
Port:                     <unset>  80/TCP
TargetPort:               80/TCP
NodePort:                 <unset>  31521/TCP
Endpoints:                100.96.1.16:80,100.96.2.15:80,100.96.3.15:80 + 2 more...
Session Affinity:         None
External Traffic Policy:  Cluster
Events:
  Type    Reason                Age   From                Message
  ----    ------                ----  ----                -------
  Normal  CreatingLoadBalancer  1m    service-controller  Creating load balancer
  Normal  CreatedLoadBalancer   1m    service-controller  Created load balancer
  1. サービスへアクセス
$ curl http://abe27b4c7bdaa11e791f402037f18a54-647142678.eu-central-1.elb.amazonaws.com
Hello World!

出力はv1、イメージのバージョンが使用されていることを示します。

  1. 別の端末では、Podの状態を確認します。
$ kubectl get -w pods
NAME                           READY     STATUS    RESTARTS   AGE
app-rolling-1683885671-d7vpf   1/1       Running   0          2m
app-rolling-1683885671-dt31h   1/1       Running   0          2m
app-rolling-1683885671-k8xn9   1/1       Running   0          2m
app-rolling-1683885671-sdjk3   1/1       Running   0          2m
app-rolling-1683885671-x1npp   1/1       Running   0          2m
  1. deploymentのイメージを更新します。
$ kubectl set image deployment/app-rolling app-rolling=arungupta/app-upgrade:v2
deployment "app-rolling" image updated
  1. Podのステータスが更新されます。
$ kubectl get -w pods
NAME                           READY     STATUS    RESTARTS   AGE
app-rolling-1683885671-d7vpf   1/1       Running   0          2m
app-rolling-1683885671-dt31h   1/1       Running   0          2m
app-rolling-1683885671-k8xn9   1/1       Running   0          2m
app-rolling-1683885671-sdjk3   1/1       Running   0          2m
app-rolling-1683885671-x1npp   1/1       Running   0          2m
app-rolling-4154020364-ddn16   0/1       Pending   0         0s
app-rolling-4154020364-ddn16   0/1       Pending   0         1s
app-rolling-4154020364-ddn16   0/1       ContainerCreating   0         1s
app-rolling-1683885671-sdjk3   1/1       Terminating   0         5m
app-rolling-4154020364-j0nnk   0/1       Pending   0         1s
app-rolling-4154020364-j0nnk   0/1       Pending   0         1s
app-rolling-4154020364-j0nnk   0/1       ContainerCreating   0         1s
app-rolling-1683885671-sdjk3   0/1       Terminating   0         5m
app-rolling-4154020364-ddn16   1/1       Running   0         2s
app-rolling-1683885671-dt31h   1/1       Terminating   0         5m
app-rolling-4154020364-j0nnk   1/1       Running   0         3s
app-rolling-4154020364-wlvfz   0/1       Pending   0         1s
app-rolling-4154020364-wlvfz   0/1       Pending   0         1s
app-rolling-1683885671-x1npp   1/1       Terminating   0         5m
app-rolling-4154020364-wlvfz   0/1       ContainerCreating   0         1s
app-rolling-4154020364-qr1lz   0/1       Pending   0         1s
app-rolling-4154020364-qr1lz   0/1       Pending   0         1s
app-rolling-1683885671-dt31h   0/1       Terminating   0         5m
app-rolling-4154020364-qr1lz   0/1       ContainerCreating   0         1s
app-rolling-1683885671-x1npp   0/1       Terminating   0         5m
app-rolling-4154020364-wlvfz   1/1       Running   0         2s
app-rolling-1683885671-d7vpf   1/1       Terminating   0         5m
app-rolling-4154020364-vlb4b   0/1       Pending   0         2s
app-rolling-4154020364-vlb4b   0/1       Pending   0         2s
app-rolling-4154020364-vlb4b   0/1       ContainerCreating   0         2s
app-rolling-1683885671-d7vpf   0/1       Terminating   0         5m
app-rolling-1683885671-x1npp   0/1       Terminating   0         5m
app-rolling-1683885671-x1npp   0/1       Terminating   0         5m
app-rolling-4154020364-qr1lz   1/1       Running   0         3s
app-rolling-1683885671-k8xn9   1/1       Terminating   0         5m
app-rolling-1683885671-k8xn9   0/1       Terminating   0         5m
app-rolling-4154020364-vlb4b   1/1       Running   0         2s

出力は、新しいPodが作成された後、古いPodが作成された後、新しいPodが作成されたことを示します。

  1. デプロイの履歴を取得する
$ kubectl rollout history deployment/app-rolling
deployments "app-rolling"
REVISION  CHANGE-CAUSE
1         kubectl create --filename=templates/app-rolling.yaml --record=true
2         kubectl set image deployment/app-rolling app-rolling=arungupta/app-upgrade:v2
  1. アプリケーションに再度アクセスする
$ curl http://abe27b4c7bdaa11e791f402037f18a54-647142678.eu-central-1.elb.amazonaws.com
Howdy World!

出力にv2は、使用されているイメージのバージョンが表示されるようになりました。

以前のリビジョンへのロールバック

上記で説明したように、Deploymentをどのように展開したかの詳細は、 kubectl rollout history コマンドを使用して取得できます。 ロールバックするには、Deploymentの完全な履歴を取得できます。

$ kubectl rollout history deployment/app-rolling
deployments "app-rolling"
REVISION  CHANGE-CAUSE
1         kubectl create --filename=templates/app-rolling.yaml --record=true
2         kubectl set image deployment/app-rolling app-rolling=arungupta/app-upgrade:v2

次のコマンドを使用して以前のバージョンにロールバックします。

$ kubectl rollout undo deployment/app-rolling --to-revision=1
deployment "app-rolling" rolled back

サービスに再度アクセスしてください

$ curl http://abe27b4c7bdaa11e791f402037f18a54-647142678.eu-central-1.elb.amazonaws.com
Hello World!

出力はv1、イメージのバージョンが現在使用されていることを示します。

リソースの削除

kubectl delete deployment/app-recreate svc/app-recreate deployment/app-rolling svc/app-rolling

Canary deployment

Canaryデプロイメントを使用すると、少数のユーザーに変更を徐々に反映させることで、すべてのユーザーに展開する前に、新しいバージョンのアプリケーションを本番環境に展開することができます。 Kubernetesでこれを達成するにはいくつかの方法があります。

  1. Service、DeploymentおよびLabelの使用
  2. 入力コントローラの使用
  3. DNSコントローラの使用
  4. IstioまたはLinkerdの使用

今回はService、DeploymentおよびLabelの方法を見ていきます

Deployment, ServiceとLabels

異なるバージョンのイメージを使用した2つのデプロイメントが使用されます。 どちらの配置も同じポッドラベルを持ちますが、少なくとも1つのラベルが異なります。 一般的なポッドラベルは、サービスのセレクタとして表示されます。 レプリカの数を調整するために、異なるポッドラベルが使用されます。 新しいバージョンのDeploymentの1つのレプリカが古いバージョンとともにリリースされます。 しばらくの間エラーが検出されない場合、新しいバージョンのレプリカの数がスケールアップされ、古いバージョンのレプリカの数が縮小されます。 最終的に、古いバージョンは削除されます。

Deploymentとサービス定義

v1展開のバージョンを見てみましょう

apiVersion: apps/v1
kind: Deployment
metadata:
  name: app-v1
spec:
  replicas: 2
  selector:
    matchLabels:
      name: app
      version: v1
  template:
    metadata:
      labels:
        name: app
        version: v1
    spec:
      containers:
      - name: app
        image: arungupta/app-upgrade:v1
        ports:
        - containerPort: 8080

それは arungupta/app-upgrade:v1 イメージを使用します。 それは2つのラベル name: appversion: v1 v2展開のバージョンを見てみましょう

apiVersion: apps/v1
kind: Deployment
metadata:
  name: app-v2
spec:
  replicas: 2
  selector:
    matchLabels:
      name: app
      version: v2
  template:
    metadata:
      labels:
        name: app
        version: v2
    spec:
      containers:
      - name: app
        image: arungupta/app-upgrade:v2
        ports:
        - containerPort: 8080

それは別のイメージ arungupta/app-upgrade:v2 を使用します。 これには、Deploymentのバージョン v1 と一致する1つのラベル name: app があります。 これは、他のラベル v2 と似ていますが、異なる値 version: v2 を使用しています。 このラベルを使用するv1と、Deploymentの v1 バージョンをオーバーライドすることなく、このDeploymentを独自に拡張できます。

最後に、これらのデプロイメントを使用するサービス定義を見てみましょう。

apiVersion: v1
kind: Service
metadata:
  name: app-service
spec:
  selector:
    name: app
  ports:
  - name: app
    port: 80
  type: LoadBalancer

サービスは、アプリケーションの両方のバージョンに共通のラベルを使用します。これにより、両方のデプロイメントのポッドをサービスの一部にすることができます。

確認しましょう。

Canary Deploymentを作成

  1. Deploymentのv1バージョンをデプロイする
$ kubectl apply -f templates/app-v1.yaml
deployment "app-v1" created
  1. Deploymentのv2バージョンをデプロイする
$ kubectl apply -f templates/app-v2.yaml
deployment "app-v2" created

3. サービスをデプロイする

$ kubectl apply -f templates/app-service.yaml
service "app-service" created
  1. このサービスのPod listを確認してください
$ kubectl get pods -l name=app
NAME                      READY     STATUS    RESTARTS   AGE
app-v1-3101668686-4mhcj   1/1       Running   0          2m
app-v1-3101668686-ncbfv   1/1       Running   0          2m
app-v2-2627414310-89j1v   1/1       Running   0          2m
app-v2-2627414310-bgg1t   1/1       Running   0          2m

templates/app-service.yamlのサービス定義で指定されているポッドのみを選択するように、クエリーのname=appラベルを明示的に指定していることに注意してください。 v1バージョンからは2つのポッド、 v2バージョンからは2つのポッドがあります。 このサービスにアクセスすると、v1バージョンから50%、2vバージョンから50%の応答が得られます。

Canary Deploymentをスケール

v1バージョンとv2バージョンから含まれるポッドの数は、2つのデプロイメントを使用して個別に拡張できます。

  1. v2 deployment のレプリカ数を増やす
$ kubectl scale deploy/app-v2 --replicas=4
deployment "app-v2" scaled
  1. サービスの一部であるポッドを確認してください
$ kubectl get pods -l name=app
NAME                      READY     STATUS    RESTARTS   AGE
app-v1-3101668686-4mhcj   1/1       Running   0          6m
app-v1-3101668686-ncbfv   1/1       Running   0          6m
app-v2-2627414310-89j1v   1/1       Running   0          6m
app-v2-2627414310-8jpzd   1/1       Running   0          7s
app-v2-2627414310-b17v8   1/1       Running   0          7s
app-v2-2627414310-bgg1t   1/1       Running   0          6m

現在、4つのポッドはアプリケーションのバージョンv2から来ており、2つのポッドはアプリケーションのバージョンv1から来ていることがわかります。 したがって、ユーザーからのトラフィックの3分の2が新しいアプリケーションから提供されるようになります。

  1. v1バージョンのレプリカ数を0に減らす
$ kubectl scale deploy/app-v1 --replicas=0
deployment "app-v1" scaled
  1. サービスの一部であるPodを確認してください
$ kubectl get pods -l name=app
NAME                      READY     STATUS    RESTARTS   AGE
app-v2-2627414310-89j1v   1/1       Running   0          8m
app-v2-2627414310-8jpzd   1/1       Running   0          1m
app-v2-2627414310-b17v8   1/1       Running   0          1m
app-v2-2627414310-bgg1t   1/1       Running   0          8m

現在、すべてのポッドがDeploymentのv2バージョンを提供しています。

Canary Deploymentを削除する

上記で作成したすべてのリソースを削除するには、このコマンドを実行します。

$ kubectl delete svc/app-service deployment/app-v1 deployment/app-v2

Ingress Controller (まだ未実装)

デプロイメントとサービスを使用してトラフィックの適切なパーセンテージを達成するには、必要な数のポッドをスピンアップする必要があります。 たとえば、バージョンにv1ポッドのレプリカが4つある場合などです。 次に、バージョンに5%のトラフィックを向けるためにv2、1ポッドのv2バージョンのレプリカには16個のv1バージョンのレプリカがさらに必要になります。 これはリソースの最適な使用ではありません。 この問題を解決するには、Kubernetes Ingressによる重み付けトラフィックの切り替えが使用できます。

Zalandoによると Kubernetes Ingress Controller for AWSKubernetesIngressコントローラ

$ kubectl apply -f templates/ingress-controller.yaml
deployment "app-ingress-controller" created

Service Discovery for Microservices with Kubernetes

302-app-discovery

Service Discovery for Microservices with Kubernetes

ハードコードされたIPアドレスを使用するのではなく、 アプリケーション内のさまざまなマイクロサービスがサービス検出を使用してインフラストラクチャ内で互いにどのように位置付けられるかの例を示す。

前提条件

3つのマスターノードと5つのワーカーノードを持つクラスタを使用

アプリケーションアーキテクチャ

サンプルアプリケーションでは、次の3つのサービスを使用

  1. webapp :Webアプリケーションのマイクロサービスは greetername のマイクロサービスを使用して人のために挨拶を生成します。
  2. greeter :マイクロサービスは greet 、URLの名前/値のキーペアに基づいて挨拶を返します。
  3. nameid URLの名前/値のキーペアに基づいて人の名前を返すマイクロサービス。

これらのサービスは、Dockerイメージとして構築され、Kubernetesに配備されています。すべてのサービスはNode.jsアプリケーションとしてビルドされています。 サービスのソースコードhttps://github.com/arun-gupta/container-service-discovery/tree/master/services にあります。

これらの webapp サービスは namegreeter サービスと通信するに、以下の環境変数でサービスを構成する必要があります。 NAME_SERVICE_HOSTGREETER_SERVICE_HOST環境変数は、そのラベルではなく、ポッドまたはホストのIPアドレスなどの静的参照することによって、これらのサービスを参照してください。 その利点は 存在している name および/または greeter ポッドがもはや操作可能でなくなった場合、それが依存するサービスを継続して実行するために十分なリソースがクラスタにある場合、 webapp サービスは機能し続けます。

  1. NAME_SERVICE_HOST
  2. NAME_SERVICE_PORT
  3. NAME_SERVICE_PATH
  4. GREETER_SERVICE_HOST
  5. GREETER_SERVICE_PORT
  6. GREETER_SERVICE_PATH

3つの異なるサービスを持つ設定ファイルは、app.yml で定義されています。 webappサービスのレプリカセットには、次の環境変数があります。

spec:
  containers:
  - name: webapp-pod
    image: arungupta/webapp-service:latest
    env:
    - name: NAME_SERVICE_HOST
      value: name-service
    - name: NAME_SERVICE_PORT
      value: "8080"
    - name: NAME_SERVICE_PATH
      value: /
    - name: GREETER_SERVICE_HOST
      value: greeter-service
    - name: GREETER_SERVICE_PORT
      value: "8080"
    - name: GREETER_SERVICE_PATH
      value: /

環境変数は、アプリケーション構成で定義されているように、nameおよびgreeterサービスを指しています。 webapp サービス用のイングレスロードバランサは、次のフラグメントを使用して作成されます。

spec:
  selector:
    app: webapp-pod
  ports:
    - name: web
      port: 80
  type: LoadBalancer

全体として、サービスは次のように互いに通信します。

[Ingress LB (ELB)] → [WEBAPP] → /name-service → Name ↓ → /greeter-service → Greeter

アプリケーションのデプロイ

  1. アプリケーションをデプロイする

    $ kubectl create -f templates/app.yml service "name-service" created replicaset.extensions "name-rs" created service "greeter-service" created replicaset.extensions "greeter-rs" created service "webapp-service" created replicaset.extensions "webapp-rs" created

  2. サービスのリストを取得

$ kubectl get svc
NAME              CLUSTER-IP       EXTERNAL-IP        PORT(S)        AGE
greeter-service   100.64.44.23     <none>             8080/TCP       13s
kubernetes        100.64.0.1       <none>             443/TCP        23m
name-service      100.66.113.58    <none>             8080/TCP       13s
webapp-service    100.71.126.195   a5427e1288472...   80:31234/TCP   12s
  1. サービスの詳細情報を取得
$ kubectl describe svc/webapp-service
Name:           webapp-service
Namespace:      default
Labels:         <none>
Annotations:        <none>
Selector:       app=webapp-pod
Type:           LoadBalancer
IP:         100.71.126.195
LoadBalancer Ingress:   a5427e128847211e782280a896fc2bfc-283874069.us-east-1.elb.amazonaws.com
Port:           web 80/TCP
NodePort:       web 31234/TCP
Endpoints:      100.96.2.12:80
Session Affinity:   None
Events:
  FirstSeen LastSeen    Count   From            SubObjectPath   Type        Reason          Message
  --------- --------    -----   ----            -------------   --------    ------          -------
  30s       30s     1   service-controller          Normal      CreatingLoadBalancer    Creating load balancer
  29s       29s     1   service-controller          Normal      CreatedLoadBalancer Created load balancer

ロードバランサーがリクエストを受け入れるまで3分ほど待つ

アプリケーションへアクセス

ブラウザやcurlでアプリケーションにアクセスする

http://<host>
http://<host>?greet=ho
http://<host>?id=1
http://<host>?greet=ho&id=1

<host> はロードバランサのアドレスの入力値

$ kubectl get svc/webapp-service -o jsonpath={.status.loadBalancer.ingress[0].hostname}
a5427e128847211e782280a896fc2bfc-283874069.us-east-1.elb.amazonaws.com

アプリケーション削除

$ kubectl delete -f templates/app.yml

k8sクラスタのアップグレード

203-cluster-upgrades

Kubernetesクラスタのアップグレード

AWS上のKubernetesクラスタのアップグレードは、kopsで簡単にできます。 今回Kubernetesクラスターを2つの方法でアップグレードする方法を試します。

インプレースアップグレード

Kopsを使用すると、インプレースを使用してクラスタをアップグレードすることは簡単です。 バージョン1.6.10のKubernetesクラスタをセットアップし、kopsを使用して1.7.2への自動ローリングアップグレードを実行します。

クラスタを作成

次のようにKubernetes 1.6.10バージョンクラスタを作成します。

kops create cluster \
  --name example.cluster.k8s.local \
  --master-count 3 \
  --node-count 5 \
  --zones $AWS_AVAILABILITY_ZONES \
  --kubernetes-version=1.6.10 \
  --yes

マルチAZ配備のクラスタにより、クラスタのアップグレード中にポッドやサービスがダウンタイムを起こさないことが保証されます。

クラスタを検証

$ kops validate cluster --name example.cluster.k8s.local
Validating cluster example.cluster.k8s.local

INSTANCE GROUPS
NAME      ROLE  MACHINETYPE MIN MAX SUBNETS
master-eu-central-1a  Master  m3.medium 1 1 eu-central-1a
master-eu-central-1b  Master  m3.medium 1 1 eu-central-1b
master-eu-central-1c  Master  c4.large  1 1 eu-central-1c
nodes     Node  t2.medium 5 5 eu-central-1a,eu-central-1b,eu-central-1c

NODE STATUS
NAME            ROLE  READY
ip-172-20-112-170.eu-central-1.compute.internal master  True
ip-172-20-117-204.eu-central-1.compute.internal node  True
ip-172-20-54-176.eu-central-1.compute.internal  master  True
ip-172-20-55-115.eu-central-1.compute.internal  node  True
ip-172-20-63-241.eu-central-1.compute.internal  node  True
ip-172-20-71-25.eu-central-1.compute.internal master  True
ip-172-20-91-30.eu-central-1.compute.internal node  True
ip-172-20-93-220.eu-central-1.compute.internal  node  True

Your cluster example.cluster.k8s.local is ready

クラスタ内の異なるノードのバージョンを確認します。

$ kubectl get nodes
NAME                                              STATUS    ROLES     AGE       VERSION
ip-172-20-112-170.eu-central-1.compute.internal   Ready     master    7m        v1.6.10
ip-172-20-117-204.eu-central-1.compute.internal   Ready     node      6m        v1.6.10
ip-172-20-54-176.eu-central-1.compute.internal    Ready     master    7m        v1.6.10
ip-172-20-55-115.eu-central-1.compute.internal    Ready     node      6m        v1.6.10
ip-172-20-63-241.eu-central-1.compute.internal    Ready     node      6m        v1.6.10
ip-172-20-71-25.eu-central-1.compute.internal     Ready     master    7m        v1.6.10
ip-172-20-91-30.eu-central-1.compute.internal     Ready     node      6m        v1.6.10
ip-172-20-93-220.eu-central-1.compute.internal    Ready     node      6m        v1.6.10

クラスタ内の各ノードがバージョン1.6.10であることを示しています。

クラスタ構成の編集

1.6.10の既存のクラスタを1.7.4にアップグレードしましょう。クラスタ構成を編集します。

kops edit cluster example.cluster.k8s.local

これにより、クラスタ構成がテキストエディタで開きます。 kubernetesVersion ターゲットバージョン(この場合は1.7.4)にキーを設定し、設定ファイルを保存します。

以前の値:

kubernetesVersion: 1.6.10

以下に変更されます:

kubernetesVersion: 1.7.4

変更を保存してエディタを終了します。Kubernetesクラスタは設定を再読み込みする必要があります。 これは、次のコマンドを使用して、クラスタのローリング・アップデートを強制することで実行できます。

このプロセスには30〜45分かかることがあります。その時間中に更新なしでクラスタを離れることをお勧めします。

$ kops update cluster example.cluster.k8s.local
I1028 18:18:59.671912   10844 apply_cluster.go:420] Gossip DNS: skipping DNS validation
I1028 18:18:59.699729   10844 executor.go:91] Tasks: 0 done / 81 total; 36 can run
I1028 18:19:00.824806   10844 executor.go:91] Tasks: 36 done / 81 total; 15 can run
I1028 18:19:01.601875   10844 executor.go:91] Tasks: 51 done / 81 total; 22 can run
I1028 18:19:03.340103   10844 executor.go:91] Tasks: 73 done / 81 total; 5 can run
I1028 18:19:04.153174   10844 executor.go:91] Tasks: 78 done / 81 total; 3 can run
I1028 18:19:04.575327   10844 executor.go:91] Tasks: 81 done / 81 total; 0 can run
Will modify resources:
  LaunchConfiguration/master-eu-central-1a.masters.cluster.k8s.local
    UserData
                          ...
                            cat > kube_env.yaml << __EOF_KUBE_ENV
                            Assets:
                          + - 7bf3fda43bb8d0a55622ca68dcbfaf3cc7f2dddc@https://storage.googleapis.com/kubernetes-release/release/v1.7.4/bin/linux/amd64/kubelet
                          - - a9258e4d2c7d7ed48a7bf2e3c77a798fa51a6787@https://storage.googleapis.com/kubernetes-release/release/v1.6.10/bin/linux/amd64/kubelet
                          + - 819010a7a028b165f5e6df37b1bb7713ff6d070f@https://storage.googleapis.com/kubernetes-release/release/v1.7.4/bin/linux/amd64/kubectl
                          - - 0afe23fb48276ad8c6385430962cd237367b22f7@https://storage.googleapis.com/kubernetes-release/release/v1.6.10/bin/linux/amd64/kubectl
                            - 1d9788b0f5420e1a219aad2cb8681823fc515e7c@https://storage.googleapis.com/kubernetes-release/network-plugins/cni-0799f5732f2a11b329d9e3d51b9c8f2e3759f2ff.tar.gz
                            - c18ca557507c662e3a072c3475da9bd1bc8a503b@https://kubeupv2.s3.amazonaws.com/kops/1.7.1/linux/amd64/utils.tar.gz
                          ...


  LaunchConfiguration/master-eu-central-1b.masters.cluster.k8s.local
    UserData
                          ...
                            cat > kube_env.yaml << __EOF_KUBE_ENV
                            Assets:
                          + - 7bf3fda43bb8d0a55622ca68dcbfaf3cc7f2dddc@https://storage.googleapis.com/kubernetes-release/release/v1.7.4/bin/linux/amd64/kubelet
                          - - a9258e4d2c7d7ed48a7bf2e3c77a798fa51a6787@https://storage.googleapis.com/kubernetes-release/release/v1.6.10/bin/linux/amd64/kubelet
                          + - 819010a7a028b165f5e6df37b1bb7713ff6d070f@https://storage.googleapis.com/kubernetes-release/release/v1.7.4/bin/linux/amd64/kubectl
                          - - 0afe23fb48276ad8c6385430962cd237367b22f7@https://storage.googleapis.com/kubernetes-release/release/v1.6.10/bin/linux/amd64/kubectl
                            - 1d9788b0f5420e1a219aad2cb8681823fc515e7c@https://storage.googleapis.com/kubernetes-release/network-plugins/cni-0799f5732f2a11b329d9e3d51b9c8f2e3759f2ff.tar.gz
                            - c18ca557507c662e3a072c3475da9bd1bc8a503b@https://kubeupv2.s3.amazonaws.com/kops/1.7.1/linux/amd64/utils.tar.gz
                          ...


  LaunchConfiguration/master-eu-central-1c.masters.cluster.k8s.local
    UserData
                          ...
                            cat > kube_env.yaml << __EOF_KUBE_ENV
                            Assets:
                          + - 7bf3fda43bb8d0a55622ca68dcbfaf3cc7f2dddc@https://storage.googleapis.com/kubernetes-release/release/v1.7.4/bin/linux/amd64/kubelet
                          - - a9258e4d2c7d7ed48a7bf2e3c77a798fa51a6787@https://storage.googleapis.com/kubernetes-release/release/v1.6.10/bin/linux/amd64/kubelet
                          + - 819010a7a028b165f5e6df37b1bb7713ff6d070f@https://storage.googleapis.com/kubernetes-release/release/v1.7.4/bin/linux/amd64/kubectl
                          - - 0afe23fb48276ad8c6385430962cd237367b22f7@https://storage.googleapis.com/kubernetes-release/release/v1.6.10/bin/linux/amd64/kubectl
                            - 1d9788b0f5420e1a219aad2cb8681823fc515e7c@https://storage.googleapis.com/kubernetes-release/network-plugins/cni-0799f5732f2a11b329d9e3d51b9c8f2e3759f2ff.tar.gz
                            - c18ca557507c662e3a072c3475da9bd1bc8a503b@https://kubeupv2.s3.amazonaws.com/kops/1.7.1/linux/amd64/utils.tar.gz
                          ...


  LaunchConfiguration/nodes.cluster.k8s.local
    UserData
                          ...
                            cat > kube_env.yaml << __EOF_KUBE_ENV
                            Assets:
                          + - 7bf3fda43bb8d0a55622ca68dcbfaf3cc7f2dddc@https://storage.googleapis.com/kubernetes-release/release/v1.7.4/bin/linux/amd64/kubelet
                          - - a9258e4d2c7d7ed48a7bf2e3c77a798fa51a6787@https://storage.googleapis.com/kubernetes-release/release/v1.6.10/bin/linux/amd64/kubelet
                          + - 819010a7a028b165f5e6df37b1bb7713ff6d070f@https://storage.googleapis.com/kubernetes-release/release/v1.7.4/bin/linux/amd64/kubectl
                          - - 0afe23fb48276ad8c6385430962cd237367b22f7@https://storage.googleapis.com/kubernetes-release/release/v1.6.10/bin/linux/amd64/kubectl
                            - 1d9788b0f5420e1a219aad2cb8681823fc515e7c@https://storage.googleapis.com/kubernetes-release/network-plugins/cni-0799f5732f2a11b329d9e3d51b9c8f2e3759f2ff.tar.gz
                            - c18ca557507c662e3a072c3475da9bd1bc8a503b@https://kubeupv2.s3.amazonaws.com/kops/1.7.1/linux/amd64/utils.tar.gz
                          ...


  LoadBalancer/api.cluster.k8s.local
    Lifecycle              <nil> -> Sync

  LoadBalancerAttachment/api-master-eu-central-1a
    Lifecycle              <nil> -> Sync

  LoadBalancerAttachment/api-master-eu-central-1b
    Lifecycle              <nil> -> Sync

  LoadBalancerAttachment/api-master-eu-central-1c
    Lifecycle              <nil> -> Sync

Must specify --yes to apply changes

次のコマンドを使用して変更を適用します。

kops update cluster example.cluster.k8s.local --yes

出力します

$ kops update cluster example.cluster.k8s.local --yes
I1028 18:22:53.558475   10876 apply_cluster.go:420] Gossip DNS: skipping DNS validation
I1028 18:22:54.487232   10876 executor.go:91] Tasks: 0 done / 81 total; 36 can run
I1028 18:22:55.750674   10876 executor.go:91] Tasks: 36 done / 81 total; 15 can run
I1028 18:22:56.640322   10876 executor.go:91] Tasks: 51 done / 81 total; 22 can run
I1028 18:22:59.756888   10876 executor.go:91] Tasks: 73 done / 81 total; 5 can run
I1028 18:23:01.154703   10876 executor.go:91] Tasks: 78 done / 81 total; 3 can run
I1028 18:23:01.890273   10876 executor.go:91] Tasks: 81 done / 81 total; 0 can run
I1028 18:23:02.196422   10876 update_cluster.go:247] Exporting kubecfg for cluster
kops has set your kubectl context to example.cluster.k8s.local

Cluster changes have been applied to the cloud.

Changes may require instances to restart: kops rolling-update cluster

クラスタのアップグレード

次のコマンドを使用して、いずれかのノードで再起動が必要かどうかを判断します。

kops rolling-update cluster example.cluster.k8s.local

このコマンドは、次のように出力します。

NAME                  STATUS      NEEDUPDATE  READY MIN MAX NODES
master-eu-central-1a  NeedsUpdate 1           0     1   1   1
master-eu-central-1b  NeedsUpdate 1           0     1   1   1
master-eu-central-1c  NeedsUpdate 1           0     1   1   1
nodes                 NeedsUpdate 5           0     5   5   5

Must specify --yes to rolling-update.

このSTATUS列には、マスターノードとワーカーノードの両方を更新する必要があることが示されています。 次のコマンドを使用してローリングアップデートを実行します。

kops rolling-update cluster example.cluster.k8s.local --yeskops rolling-update cluster example.cluster.k8s.loc

このコマンドの出力が表示されます。

NAME                  STATUS      NEEDUPDATE  READY MIN MAX NODES
master-eu-central-1a  NeedsUpdate 1           0     1   1   1
master-eu-central-1b  NeedsUpdate 1           0     1   1   1
master-eu-central-1c  NeedsUpdate 1           0     1   1   1
nodes                 NeedsUpdate 5           0     5   5   5
I1028 18:26:37.124152   10908 instancegroups.go:350] Stopping instance "i-0c729296553079aab", node "ip-172-20-54-176.eu-central-1.compute.internal", in AWS ASG "master-eu-central-1a.masters.cluster.k8s.local".
I1028 18:31:37.439446   10908 instancegroups.go:350] Stopping instance "i-002976b15a2968b34", node "ip-172-20-71-25.eu-central-1.compute.internal", in AWS ASG "master-eu-central-1b.masters.cluster.k8s.local".
I1028 18:36:38.700513   10908 instancegroups.go:350] Stopping instance "i-0d4bd1a9668fab3e1", node "ip-172-20-112-170.eu-central-1.compute.internal", in AWS ASG "master-eu-central-1c.masters.cluster.k8s.local".
I1028 18:41:39.938149   10908 instancegroups.go:350] Stopping instance "i-0048aa89472a2c225", node "ip-172-20-93-220.eu-central-1.compute.internal", in AWS ASG "nodes.cluster.k8s.local".
I1028 18:43:41.019527   10908 instancegroups.go:350] Stopping instance "i-03787fa7fa77b9348", node "ip-172-20-117-204.eu-central-1.compute.internal", in AWS ASG "nodes.cluster.k8s.local".
I1028 19:14:50.288739   10908 instancegroups.go:350] Stopping instance "i-084c653bad3b17071", node "ip-172-20-55-115.eu-central-1.compute.internal", in AWS ASG "nodes.cluster.k8s.local".
I1028 19:16:51.339991   10908 instancegroups.go:350] Stopping instance "i-08da4ee3253afa479", node "ip-172-20-63-241.eu-central-1.compute.internal", in AWS ASG "nodes.cluster.k8s.local".
I1028 19:18:52.368412   10908 instancegroups.go:350] Stopping instance "i-0a7975621a65a1997", node "ip-172-20-91-30.eu-central-1.compute.internal", in AWS ASG "nodes.cluster.k8s.local".
I1028 19:20:53.743998   10908 rollingupdate.go:174] Rolling update completed!

クラスタを再度検証します。

$ kops validate cluster
Using cluster from kubectl context: example.cluster.k8s.local

Validating cluster example.cluster.k8s.local

INSTANCE GROUPS
NAME      ROLE  MACHINETYPE MIN MAX SUBNETS
master-eu-central-1a  Master  m3.medium 1 1 eu-central-1a
master-eu-central-1b  Master  m3.medium 1 1 eu-central-1b
master-eu-central-1c  Master  c4.large  1 1 eu-central-1c
nodes     Node  t2.medium 5 5 eu-central-1a,eu-central-1b,eu-central-1c

NODE STATUS
NAME            ROLE  READY
ip-172-20-101-20.eu-central-1.compute.internal  master  True
ip-172-20-106-93.eu-central-1.compute.internal  node  True
ip-172-20-109-10.eu-central-1.compute.internal  node  True
ip-172-20-41-77.eu-central-1.compute.internal node  True
ip-172-20-44-33.eu-central-1.compute.internal master  True
ip-172-20-75-132.eu-central-1.compute.internal  node  True
ip-172-20-85-128.eu-central-1.compute.internal  master  True
ip-172-20-93-108.eu-central-1.compute.internal  node  True

Your cluster example.cluster.k8s.local is ready

クラスタからノードのリストを取得

$ kubectl get nodes
NAME                                             STATUS    ROLES     AGE       VERSION
ip-172-20-101-20.eu-central-1.compute.internal   Ready     master    42m       v1.7.4
ip-172-20-106-93.eu-central-1.compute.internal   Ready     node      36m       v1.7.4
ip-172-20-109-10.eu-central-1.compute.internal   Ready     node      37m       v1.7.4
ip-172-20-41-77.eu-central-1.compute.internal    Ready     node      3m        v1.7.4
ip-172-20-44-33.eu-central-1.compute.internal    Ready     master    51m       v1.7.4
ip-172-20-75-132.eu-central-1.compute.internal   Ready     node      5m        v1.7.4
ip-172-20-85-128.eu-central-1.compute.internal   Ready     master    46m       v1.7.4
ip-172-20-93-108.eu-central-1.compute.internal   Ready     node      44s       v1.7.4

Blue/green Upgrade

Blue/green メソッドを使用してクラスターをアップグレードすることは、本質的にはより控えめであると考えられ、アプリケーションの高可用性を考慮します。 2つのk8sクラスタを設定します.1つは1.6.10バージョン、もう1つは1.7.2で、ポッドの展開とサービスは新しいクラスタに移行します。

シェルスクリプトでのリトライ処理とロック処理

サーバ運用で手動でApache再起動のような対応は辛いので アラートを検知したら自動でApache再起動を実施し、 メールで知らせてくれるスクリプトを書きたいと思い、 リトライ処理やロック処理をシェルスクリプトでどうやるか調べたのでまとめます。

リトライ処理

変数でリトライ回数とリトライ間隔を決めて whileループでカウントを1ずつ増やし、 sleepコマンドの引数にリトライ間隔の変数を与えることで リトライ処理を実装できた。

## リトライ処理
RETRY_COUNT=1    # リトライ回数
RETRY_INTERVAL=1 # リトライ間隔1秒

while [[ $COUNT -ne $RETRY_COUNT ]]
do
    COUNT=`expr $COUNT + 1`
    sleep $RETRY_INTERVAL
done

ロック処理

こちらの記事が参考になりました。https://heartbeats.jp/hbblog/2013/10/atomic03.html#more

シンボリックリンクを作成するときに、作成先にすでにファイルが存在するとエラーとなります。そのため、ロックファイルをシンボリックリンクとして作成を試みれば、ロックファイルが存在すればエラーが発生するため、ロックファイルが存在すると判断し処理を中断できます。ロックファイルが存在しなければ、ロックファイルがシンボリックリンクとして作成されます。「ロックファイルの確認」と「ロックファイルの作成」が同時に行われるため、安全にロックすることができるのです。普通のファイルとしてロックファイルを作成する方法での問題はシンボリックリンクの場合は起きません。

以下のようにシンボリックリンクを作成することでアトミックにロックファイルを作成することができます。

#!/bin/bash
LOCK_FILE=$HOME/tmp/file.lock

## ロックファイルの確認と作成
if ! ln -s $$ $LOCK_FILE; then
    echo "LOCKED"
    exit 0

このようにロックファイルの確認と作成を別々に行うとアトミックなファイルの扱いにはならないので要注意です。

## ロックファイルの確認
if [ -f $LOCK_FILE ]; then
    echo "LOCKED"
    exit 1
fi

それでは実際にリトライ処理とロック処理を使ってシェルスクリプトを書いてみましょう。 Nagios監視でcheck_httpが何度か失敗し、check_loadでも失敗したら Apache再起動を実行し、1分間はApache再起動が実行されないスクリプトを書いてみます。 これをCronなどで3分毎に実行させればWEBサーバで応答に時間がかかっている時の障害対応を自動化できますね。 ロックファイルを設置して排他制御を行うことで、Apache再起動が何度も実行されないように実装ができます。

#!/bin/bash

set -uo pipefail

HOSTNAME=$(uname -n)
MAIL_ADDRESS_TO="メールアドレス"
LOCK_FILE="$HOME/restart.lock"

RETRY_COUNT=5    # リトライ回数
RETRY_INTERVAL=5 # リトライ間隔5秒
COUNT=0

## ロックファイルの確認
## なければロックファイルの作成
if ! ln -s $$ $LOCK_FILE; then
    echo "LOCKED"
    exit 0
fi

/usr/lib64/nagios/plugins/check_http -w 5 -c 7 -H localhost -u "/" -s "</html>"
RETURN_HTTP=$?

if [[ ${RETURN_HTTP} -ne 0 ]]; then

    ## check_httpの応答判断のリトライ処理
    while [[ $COUNT -ne $RETRY_COUNT ]]
    do
        COUNT=`expr $COUNT + 1`
        sleep $RETRY_INTERVAL
        /usr/lib64/nagios/plugins/check_http -w 5 -c 7 -H localhost -u "/" -s "</html>"
        RETURN_HTTP=$?
        if [[ ${RETURN_HTTP} -eq 0 ]]; then
            exit 0
        else
            continue
        fi
    done

    ## リトライ5回繰り返してもcheck_http監視がOKでないなら以下の再起動処理に進む
    if [[ ${RETURN_HTTP} -ne 0 ]]; then
        /usr/lib64/nagios/plugins/check_load -w 2,2,2 -c 3,3,3
        RETURN_LOAD=$?
        if [[ ${RETURN_LOAD} -ne 0 ]]; then

            echo "/etc/init.d/httpd restart"
            RETURN=$?

            if [[ ${RETURN} -ne -0 ]]; then
                RESULT="Failed"
            else
                RESULT="Success"
            fi

            SUBJECT="${HOSTNAME} httpd restart ${RESULT}"
            BODY="${HOSTNAME} restart apache ${RESULT}"
            echo "${BODY}" | mail -s "${SUBJECT}" ${MAIL_ADDRESS_TO}

            ## 再起動後は1分スリープ後にロックファイルの削除
            sleep 60
            rm -f $LOCK_FILE
        fi
    fi
fi

障害対応の自動化で他によいものがあれば教えて下さい。 また、今回のアトミックなファイルの取扱いの考え方は自分ひとりではなかなか気づかなかったです。

Linuxサーバをセキュアな環境にする

Linuxサーバでセキュアな設定についてまとめてみました。 OSはCentOS6なので古いですが。。 Wordpress周りはまだ調べられてないので どこかでまとめたい。

アドレススキャン対策

ブロードキャスト宛のICMP Echo Requestに対して回答しない

vim /etc/sysctl.conf
net.ipv4.icmp_echo_ignore_broadcasts=1

OSやバージョンの確認

# yum -y install nmap
# nmap -O example.com

telnet example.com 80

まずはこれぐらいはやっておきたいセキュアな設定

  • OSはminimalで必要最小限でインストールする
  • システム管理者用のユーザを作成して利用する
  • yum -y update でパッケージを最新のものを利用する
  • 不要なサービスは停止する
    • audit(コマンドの監査をしない場合)
    • ip6tables(IPv6を使用しない場合)
    • netfs(NFSクライアントを使用しない場合)
    • postfix(SMTPサーバを使用しない場合)
  • rootユーザのログインを禁止する
    • コンソールからのrootログイン禁止
  # echo > /etc/securetty
  • SSHでのrootログイン禁止
  # vim /etc/ssh/ssh_config
  #PermitRootLogin yes

  PermitRootLogin no 
  • SSHを受け付けるPort番号を変更する
  # vim /etc/ssh/ssh_config
  Ports 20022
  • SSHサーバを再起動して設定を反映させる
  • ポートやファイアウォールの設定を確認する
    • netstat -atnp iptables -Ln

OSのセキュリティ

  • GRUBのパスワード設定
    • grub-md5-crypt を実行して出力内容を /boot/grub/grub.conf に書くコム
    • title で始まる行よりも前に password --md5 パスワード の書式で記述する
  • 一般ユーザのログイン管理
    • 不要なユーザのロック usermod -L hoge
    • 不要なユーザのロック解除 usermod -U hoge
    • シェルログインが不要なユーザの作成 useradd -s /sbin/nologon hoge
    • hogeユーザのログインシェルを /sbin/nologin に設定 usermod -s /sbin/nologin hoge
  • コンソールからのrootログイン禁止
    • echo > /etc/securetty
  • su コマンドを使えるユーザを制限
    • /etc/pam.d/su ファイルをrootユーザで開き、 「auth required pam_wheel.so use_uid」という行を追加する
    • これでwheelグループに追加されたユーザのみがsuコマンドを利用できるようになる
    • usermod -G wheel hoge でグループに所属させることが可能
  • sudoコマンドの利用設定
    • visudo を実行し /etc/sudoers を開く
    • hoge ALL=(ALL) ALL ですべてのroot権限が必要なコマンドの実行を許可する
  • Clam AntiVirusのインストール
    • yum install clamav
    • freshclam でウィルスデータベースのアップデート
    • ウィルススキャンはスキャンしたいディレクトリに移動し、clamscanコマンドを実行
    • cd /home して clamscan -r再帰的にディレクトリをスキャンする
    • スキャンしかしないので clamscan --remove で実行時にウィルスの自動削除もしてくれる

メールサーバのセキュリティ

  • ユーザログインの禁止
    • メールサーバを利用するだけのユーザならログインする必要ないのでデフォルトシェルを無効にしておく
    • useradd -s /sbin/nologin newuser

main.cfの主な設定

  • myhostname
    • ホスト名をFQDNで指定
    • myhostname = windsor.example.com
  • mydomain
    • メールサーバのドメイン名を指定
    • mydomain = example.com
  • myorigin
    • メールアドレスの[@]以降が指定されなかった時、デフォルトで保管する値を指定する
    • myorigin = $mydomain
  • inet_interfaces
  • mydestination
    • ローカル配送を行うドメイン名、つまりメールを受け取るドメイン名を指定
    • mydestination = $myhostname, localhost.$mydomain, localhost
  • mynetworks
    • 中継を許可するホストの存在する内部ネットワークアドレスを指定
    • ここに指定したアドレスからのメールは無条件で中継される
    • mynetworks = 192.168.11.0/24, 127.0.0.1/8
  • smtpd_banner
    • SMTPの応答コードに続いて出力されるバナー情報を指定
    • できるだけ情報は少ないほうがいいが、 $myhostname は削除しないようにすること
  • disabled_vrfy_command
    • STMPのVRFYコマンドを禁止にする
    • VRFYコマンドはメールサーバにどのようなアカウントがあるのかを知られる可能性がある
    • disable_vrfy_command = yes
  • smtpd_helo_required
    • SMTP開始時のHELO/EHLOコマンドを必須とするかどうかを指定
    • yesとするとスパムメールの抑制となる
    • smtpd_helo_required = yes
  • smtpd_recipient_restrictions
    • 通常は末尾にrejectを指定する
  • smtpd_sender_restictions
    • メールの送信元アドレスをチェックし、受信を拒否するかどうかを判断

設定の反映

service postfix reload

デフォルト値から変更されている項目のみを表示

postconf -n

FTPサーバのセキュリティ

CentOSではセキュアなvsftpdを使う

vsftpd.confの設定

  • バナーの表示
  • FTP接続確立時のバナーメッセージはftpd_bannerディレクティブで設定
    • ftpd_banner='FTP Login
    • バナーメッセージを格納したファイルを指定するbanner_fileによって無視せれるので注意
  • TCP Wrapperの利用有無
    • tcp_wraller=Yes
    • tcp_wraller=No
  • ユーザリストファイル
  userlist_file=/etc/vsftpd/user_list
  userlist_enable=YES
  userlist_deny=YES
  userlist_file=/etc/vsftpd/user_list
  userlist_enable=YES
  userlist_deny=NO
  • /etc/vsftpd/user_list にはFTP接続にかかわるユーザを登録。
  • userlist_enable=NO となっている場合 /etc/vsftpd/user_list 内に指定されたユーザのみFTP許可されるが、 /etc/vsftpd/ftpusers にユーザ名が指定されている場合はFTPログインを禁止されてしまう
  • ファイルのアップロード
    • FTPサーバをファイル配布用にするならアップロードは不要
    • write_enable=NO でアップロードが禁止になる
  • ユーザの設定
    • Linuxユーザのログインを許可するには local_enable=YES と設定するとLinuxシステムのユーザ名とパスワードを使ってFTPログインが可能になる
  • ホームディレクトリ以外に移動できないように設定
chroot_local_user=YES
#chroot_list_enable=YES
# (default follows)
chroot_list_file=/etc/vsftpd/chroot_list
  • 一部のユーザにはホームディレクトリ以外に移動できないように設定
    • chroot_list_enable=YES と設定して、chroot_list にユーザを列挙する
chroot_local_user=YES
chroot_list_enable=YES
# (default follows)
chroot_list_file=/etc/vsftpd/chroot_list
  • ホームディレクトリ配下のFTPログイン用のサブディレクトリにログインするようにする
  • 匿名FTPを許可の有無
    • デフォルトでは有効になっている
    • anonymous_enable=YES
    • 匿名FTPを使ってファイル配布したい場合は、匿名アカウントでのファイルアップロードを禁止しておく
    • anon_upload_enable=NO
    • write_enbale=NO なら anon_upload_enable=YES でもファイルのアップロードはできない
  • FTPSを使う
    • 証明書と秘密鍵をまとめてpemファイルを作る
cd /etc/pki/tls/certs
cp mycerts.crt mycerts.pem
cat mycerts.key >> mycerts.pem
  • vsftpd.confに以下の行を追加
ssl_enable=YES #SSLの有効化
rsa_cert_file=/etc/pki/tls/certs/mycerts.pem #サーバ証明書秘密鍵のパス
  • 通常のFTP接続も許可するなら以下の設定もする
  • SSL非対応のFTPクライアントからの利用も想定するときは必要
force_local_logins_ssl=NO #FTPログイン時にSSL接続を強制しないように設定
force_local_date_ssl=NO   #FTP転送時にSSL接続を強制しないように設定
  • vsftpdのログ
    • デフォルトでは/var/log/xferlog にファイル転送に関するログが記録される
xferlog_enable=YES
xferlog_std_format=YES
  • ファイル転送のログに加えて、サーバへの接続に関するログも出力されるようにする
xferlog_std_format=NO
vsftpd_log_file=/var/log/vsftpd.log
  • syslogへ出力するようにもできる
syslog_enable=YES

安全なSSHサーバの設定

/etc/ssh/sshd_config の設定

  • Portの変更
    • Port 20022 #portの待受けを22番以外にする
  • Protocolのバージョンを2にする
    • Protocol 2
  • rootログインを許可しない
    • PermitRootLogin no
  • パスワード認証を許可しない
    • PasswordAuthentication no
  • ログイン認証認証の失敗を許容できる回数を変更
    • MaxAuthTries 3

pythonでしたいと思ったことまとめ①

print()でカンマ区切りで表示したい

print(a, b, c, sep=',')

行末の改行を削除したい

string.rstrip()

文字列の末尾部分を除去したコピーを返す。引数 chars は除去される文字集合を指定する文字列。

特定の文字を除去したい

文字列.replace() メソッドを利用します。

以下で改行を除去できる。

remove_enter = jugemu.replace('\n', '')

csvファイルなどのカンマで文字列を分割したい

string.strip(',')

ファイルの中身に追記していきたい

a を使う、 w だと上書きしてしまう。

with open('output/hoge.log', 'a', encoding='utf-8') as f:

現在の時刻を取得する

datetimeモジュールを使う

from datetime import datetime

now = datetime.now()

日時.yearで日時の年の値を数値として取得できる。

print(str(now.year) + '年')

日時.monthで日時の月の値を数値として取得できる。

print(str(now.month) + '月')

日時.dayで日時の日の値を数値として取得できる。

print(str(now.day) + '日')

日時.hourで日時の時の値を数値として取得できる。

print(str(now.hour) + '時')

日時.minuteで日時の分の値を数値として取得できる。

print(str(now.minute) + '分')

日時.secondで日時の秒の値を数値として取得できる。

print(str(now.second) + '秒')

日時のオブジェクトを作成したい

datetime(年, 月, 日, 時, 分, 秒)と指定可能で時分秒を省略すると00:00:00になる。

one_day = datetime(2016, 1, 31, 10, 20, 30)

日時を文字列に変換したい

print(now.strftime('%Y/%m/%d'))  # -> 2016/01/10
print(now.strftime('%Y-%m-%d'))  # -> 2016-01-10
print(now.strftime('%Y年%m月%d日'))  # -> 2016年01月10日

strftime (フォーマット)を利用して変換できる

  • %Y: 西暦(4桁)の10進表記を表します。
  • %m: 0埋めした10進数で表記した月。
  • %d: 0埋めした10進数で表記した月中の日にち。
  • %H: 0埋めした10進数で表記した時 (24時間表記)。
  • %M: 0埋めした10進数で表記した分。
  • %S: 0埋めした10進数で表記した秒。

文字列を日付にしたい

strptime を使う。

from datetime import datetime

day_str = '2018/3/14 12:30:00'
dt = datetime.strptime(day_str, '%Y/%m/%d %H:%M:%S') # 第二引数には変換したい文字列に一致したフォーマットを渡す
print(dt) # 2018-03-14 12:30:00

X日前(後)の日付を表示したい

timedelta を使う

from datetime import datetime
from datetime import timedelta

olympic_day = datetime(2020, 7, 24)
before_3days = now - timedelta(days=3) # 3日前
after_3days = now + timedelta(days=3) # 3日後

Pathを結合したい

os.path.join(a, b, ...) で指定可能

Pathが存在するか確認したい

os.path.exists(path) で指定したpathが存在するか確認できる。

指定したpathがファイルかディレクトリか確認したい

ファイルなら

os.path.isfile(path)

ディレクトリなら

os.path.isdir(path)

指定されたディレクトリー内のファイル名と子ディレクトリー名をリストで返したい

os.walk(top) で返せる。

for文に引数root, dirs, filesを指定すると、そこに各ディレクトリーごとのデータが代入されていく。

import os

def directory_tree(target_path):
    for root, dirs, files in os.walk(target_path) :
        print(root)
        for dir in dirs:
            print('\t', dir)
        for file in files:
            print('\t', file)
  • root には、 ディレクトリのパス が代入。
  • dirs には、 root内に存在するサブディレクトリーのリスト が代入。
  • files には、 root内に存在するファイルのリスト が代入。

AWS Summit Tokyo 2018の聴いたセッションのメモ

聞いたセッションのメモ セッションだけ聞くなら会場ではなくストリーミング配信をみるのが快適ということに気がついてしまった。

オペレーションの最適化

  • ベストプラクティスに基づく準備
  • 適切なモニタリング
  • 適切な運用

  • Well-Architected framework

  • 課題
  • AWS Trusterd Advisor

  • Well-Architected framework クラウドアーキテクチャ設計と運用の考え方とベストプラクティス

  • 運用上の優秀正
  • 実現のための大原則
  • 確認のための質問

  • Operation as codeの実践

  • config,cloudwatc→lambdaで対処するオペレーションの自動化
  • 注釈でドキュメンテーションをする.デプロイしたらplaybookなりが更新される
  • 頻繁に小さく可逆な変更を加える。ワークロード側も変えられる設計にすべき
  • 頻繁に運用手順を見直そう。定期的にGamedayを実施して手順をレビューする。効果があるか、効率化できるポイントを探す。
  • 障害を予想する。机上演習Pre-mortemを実施する。障害シナリオとそのインパクトを理解。対応手順が効果的であることぉ確認、チームがそれらに詳しくなる。
  • イベントと障害対応から学ぶ。うまくいった対応も塩お愛した対応も振り返る。イベントから学んだことをチームで共有する。ログを集めて、可視化しておこう。athenaとか 確認のための質問 運用がビジネスにどんな影響をあたえるのか、運用が客観的に回るっているのか。
  • 運用における優先度を決める要因はなにか
  • どのようにアプリケーション

  • 実際のお客様環境によくある課題

    • タグを管理・活用しているか
    • デプロイ時に強制 tagつけ忘れないようにCloudformationその他のデプロイツールやiAMポリシーで縛る。AWS Service Catalogを利用
    • AWS Configで必須タグが付いているかをチェック。事前定義済ルールのrequired-tagsを利用。対象となるリソースタイプやタグを編集。
    • タグをキーにAWS Sytem Managerを使ってタグを使った管理もできる。リソースグループ化して管理。グループ化したリソースに対して一括処理も可能。
    • タグで頑張りすぎないことも大事。
    • タグの設定指針 awS Tagging Strategyをもよう。
  • メンテナンスの通知は受け取れているのか
    • メールを見逃す。特定の担当者にしか行かない。社内のチャット
    • aWS Personal Health Dashboard
    • リソースに対するメンテやissueが表示されるダッシュボード
    • CloudWatch Eventsと連携してイベントに対するアクションを実行
  • awS HEalth API & AWS Heallth Github repository
    • aws-health-tools を使えばチャットツールやsNS通知に自動化できる
    • 大体の連絡先の活用
      • 合うぃ急の連絡先、操作の連絡先、セキュリティの連絡先
  • 複数のアカウントをまとめて管理する
    • 部門ごとやアプリケーションごとにアカウントを作っているけど、ガバナンスを聞かせられない。
    • 請求統合をおこなっているけど、各アカウンとの利用状況が把握できない
    • 請求統合されたアカウント感でRIを管理はいや
    • AWS Oraganizationによえう請求統合がソリューション
    • Masterアカウントにまとめて一括請求、ボリュームディスカウントもこれに適用
  • AWS Organization
  • 一括請求のみを有効化
  • 複数のAWSアカウントに適用するポリシーを集中管理できる
  • AWSのサービスへのアクセス制御
  • AWSアカウントんお作成と管理の自動化
  • AWS Config連携、AWSFirewall Manager連携、SingleSignOn連携
  • Cost Explorer API
  • Cost Explorer API, CustomReport
  • csvだけでなく、jsonでも取れる
  • aws-cost-explorer-report
  • api使うとお金がかかる
  • Reservide Instanceの共有拒否設定
    • 本当は複数のアカウントでサイズ関係なくうまくばらすこともできるので、共有してほしい。
    • それでも共有したくないなら、RI割引共有の設定で共有したくないなら設定できる
  • AWSサポートは活用できているか
    • 複数のアカウント感でナレッジが共有されず、何度もの同じことを聞く
    • 社内で使っている課題管理システムと連携したい
    • サポートの調査が思うように進まない
  • AWSサポートAPI

    • サポート問い合わせの自動化や問題管理ツールとの連携
    • サポート問い合わせのエクスポート
    • Trusted Advisortとの自動連系
    • case_life_cycle.html, trustedadvisor.html
    • ビジネス以上でないと古ファンクションは使えない
  • サポート問い合わせのベストプラクティス

    • 一つのお問い合わせで関連性のない複数の質問をしない
    • 問い合わせ対象リソースの所有者が起票していることを確認する
    • 適切な緊急度を設定
    • 問いあわせ時には以下の内容を添付
事象の発生時間(タイムゾーン)
事象が発生したリソースの詳細(リージョン・リソースID・名前)
発生した事象の詳細:
SdKやCLIのエラ=であればそのエラー出力
可能であればAWSのCLIのの場合は--debug
  • AWS Trusted Advisor
    • リアルタイムガイダンスを提供してくれる
    • AWS Trusted AdvisorとCloudWatchの連携
    • AWS Limit Monitor 

Well-Architectを使って準備し、Trusted Advisorを使って環境を見直しておこう

Fargate

コンテナのメリット - パッケージング - 配布 - イミュータブルインフラストラクチャ

アプリケーション開発に集中

1台のサーバでDockerコンテナを使うのは簡単だが、サーバが増えると大変

  • ECS
    • コンテナはタスクという単位でタスクの配置やスケールのマネージド 動作イメージ ECS EC2インスタンスとタスクがある。これを提供してくれる ECSアーキテクチャの整理 Taskアプリケーションを構成する1つ以上のコンテナ(群)実行単位 Container Instance Taskが起動するEC2インスタンス Service ロングランニングアプリ用スケジューラTaskの数 Cluster

Fatgateはインスタンス管理不要  Fargateの上にTaskが起動。EC2インスタンスを増やすことなく仮想サーバー側の管理が不要 Taskだけがスケールされる

Codecommit codebuld ECR Fargate ECK

コンテナ管理にひつようなもの サービスディスカバリ Route53,ALB ロギング、モニタにリング CloudWatch セキュリティ IAM VPC パラメータストア スケジューリング タスクプレースメント

サービスディスカバリ サービス同士が疎結合になるように構成するのがベストプラクティス、生存時間が短いので各サービスが自身が接続する先を見つける必要がある ECSではALBと連携 ALBをエンドポイントとしてつかう ECAサービスディスカバリの利用 名前解決で、ALBを使わずRoute53のDNA名で自動的に登録することができる

ロギングとモニタにリング コンテナのアプリは外でSTDOUTに出力する コンテナインスタンスとかリソースよりサービスを管理するのがよい

CloudWatch awslogsでCloudWathLogsに送信される 1つのTask内に2種類のコンテナを定義する アプリケーションコンテナ ロギング用のコンテナ 一次共有ボリュームにログを出力しロギングコンテナがcloudwatch logsに転送 ロギング処理を別コンテナにまかせる サイドカー構成

Fargateはコンテナにインスタンスの管理は不要でタスクのみ。

Fargateより、より細やかなECSクラスタ管理をつげんできる ECSクラスタの状態変化をリアルタイムに検出する 監視システムにEventログを保存してくらすたの 状態を可視化する 特定のタスクが落ちたときに気づける状態変化に気づける

EventStreamによるリアルタイムなイベント検知 ECSクラスタの状態変化を通知する Evento通知の到達性は少なくとも1回

セキュリティ アクセス権限の考え方 IAMロールポリシーを使ってクラスターに対する権限や アプリケーションの権限、どのタスクがどのAWSリスースにアクセスできるか タスク管理に関する権限 ECRからのイメージのpull Clone

Taskに関するセキュリティ Task単位でIAMロールを割り当て Task単位でSecurity Groupを割り当て

Taskでどのようにパスワードをわたすのか Parameter Store,Secrets Managerを使ってKMSに置いてTaskに設定されたIAMロールの権限に応じて秘密情報を取得

スケジューリングとタスクプレースメント 管理が必要となる2つのレイヤー - CountainerInstanceのスケーリング Fargateは考慮不要。 - Taskのスケーリング  アプリケーションオートスケーリングを利用することでTaskのオートスケーリングを実現可能

EC2インスタンス使うときのスケールイン タスクの数が減る。→コンテナインスタンスを削除

Fargateのスケールイン

タスクのみがスケールインすればよい。

Target Trackingを利用したアプリケーションオートスケーリング メトリクスに対してターゲット値を設定 その値に近づくようにアプリケーションオートスケーリングが自動的にTaskを調整

Taksの配置を管理するには Taskga可動するために必要な条件は 必要なCPU、メモリ、ポートが割り当て可能化

どのようにTaskを配置したいのか 特定のAZに置く 特定のインスタンスタイプのみ

Task配置に関するロジック CPUメモリーポートとAZ、インスタンスタイプ、AMI ID 配置戦略を満たすか 最終的なTaskの配置場所を決定

タスク配置例

コンテナインスタンスのroll out タスクdefinitionの更新時に新しいAMI IDを利用してコンテナインスタンスに配置する制約をつける 1ホスト1コンテナ distinctinstqnce constaraint + event stream + lambda