AWS Fargate BlueGreenDeployment

はじめに

AWS FargateはContainerインスタンスの管理をAWSにお任せすることができるサービスです。

現状、ECS(LaunchType EC2)を使っているのですが、JenkinsからECSにBlueGreenDeployするときにecs-deployを使っています。
ecs-deployはaws cliとjqには依存していますがshellだけで書かれてるので持ち運びが便利なんですね。

ecs-deployはFargateに対応していないので対応させてみました。

https://github.com/uzresk/ecs-deploy.git

使い方

1. aws cliはFargateに対応しているバージョンをお使いください。

ちなみに私の環境はこちら

aws-cli/1.14.7 Python/2.7.12 Linux/4.9.62-21.56.amzn1.x86_64 botocore/1.8.11

2. コマンドはecs-deployと全く同じです

./ecs-deploy -c [cluster-name] -n [service-name] -i [registry-url]:[tag] -t 300 -r us-east-1

デフォルトのタイムアウトは90秒なのですが、終わらないことが何回かあったので少し長めにしておくのがおススメです。

実行結果

Using image name: xxxx.dkr.ecr.ap-northeast-1.amazonaws.com/xxxx:0.0.1-SNAPSHOT
Current task definition: arn:aws:ecs:us-east-1:xxxx:task-definition/xxxx:25
Current requires compatibilities FARGATE
New task definition: arn:aws:ecs:us-east-1:xxxx:task-definition/xxxx:26
Service updated successfully, new task definition running.
Waiting for service deployment to complete...
Service deployment successful.

変更点

Fargateが追加されたことによりrequiresCompatibilitiesの指定を引き継ぐようにしたのと、
cpu, memoryの設定も合わせて引き継ぐようにしました。
LaunchTypeがEC2の場合はcpu,memoryは設定されません。

[root@ip-10-0-0-100 ecs-deploy]# git diff
diff --git a/ecs-deploy b/ecs-deploy
index 637e793..8ad1cb1 100755
--- a/ecs-deploy
+++ b/ecs-deploy
@@ -261,11 +261,17 @@ function createNewTaskDefJson() {
     fi

     # Default JQ filter for new task definition
-    NEW_DEF_JQ_FILTER="family: .family, volumes: .volumes, containerDefinitions: .containerDefinitions"
+    NEW_DEF_JQ_FILTER="family: .family, volumes: .volumes, containerDefinitions: .containerDefinitions, requiresCompatibilities: .requiresCompatibilities"

     # Some options in task definition should only be included in new definition if present in
     # current definition. If found in current definition, append to JQ filter.
-    CONDITIONAL_OPTIONS=(networkMode taskRoleArn placementConstraints)
+    LAUNCH_TYPE=$(echo "$TASK_DEFINITION" | jq -r '.taskDefinition.requiresCompatibilities[0]')
+    echo "Current requires compatibilities $LAUNCH_TYPE"
+    if [ $LAUNCH_TYPE == FARGATE ]; then
+      CONDITIONAL_OPTIONS=(networkMode taskRoleArn executionRoleArn placementConstraints memory cpu)
+    else
+      CONDITIONAL_OPTIONS=(networkMode taskRoleArn executionRoleArn placementConstraints)
+    fi
     for i in "${CONDITIONAL_OPTIONS[@]}"; do
       re=".*${i}.*"
       if [[ "$DEF" =~ $re ]]; then

おわりに

もう少し動作確認したらプルリクエスト送ろうと思いますが、だいぶメンテされていないようなので多分マージされない気がします。。。

続きを読む

CentOS7上に構築したKubernetesでイメージのダウンロードに失敗する

CentOS7上に構築したKubernetesでイメージのダウンロードに失敗する

Kubernetesで遊ぼうとしてハマった。
まず、こちらの記事を参考にしつつ、AWS上にCentOS7のインスタンスを立ち上げてkubernetesの試験環境を構築した。
Kubernetesでクラスタ環境構築手順(1) – masterの作成 – Qiita

しかし、いざpodをデプロイしようとしたところContainerCreatingのまま進まず、以下のようなエラーが。

[centos@ip-172-20-1-31 ~]$ kubectl run nginx --image=nginx:1.11.3                                                                                                                                                                             
deployment "nginx" created
[centos@ip-172-20-1-31 ~]$ kubectl describe pod nginx                                                                                                                                                                                         
Name:           nginx-752720876-1cgfj
Namespace:      default
Node:           master/172.20.1.31
Start Time:     Mon, 11 Dec 2017 02:08:46 +0000
Labels:         pod-template-hash=752720876
                run=nginx
Status:         Pending
IP:
Controllers:    ReplicaSet/nginx-752720876
Containers:
  nginx:
    Container ID:
    Image:              nginx:1.11.3
    Image ID:
    Port:
    State:              Waiting
      Reason:           ContainerCreating
    Ready:              False
    Restart Count:      0
    Volume Mounts:
      /var/run/secrets/kubernetes.io/serviceaccount from default-token-kj7lz (ro)
    Environment Variables:      <none>
Conditions:
  Type          Status
  Initialized   True 
  Ready         False 
  PodScheduled  True 
Volumes:
  default-token-kj7lz:
    Type:       Secret (a volume populated by a Secret)
    SecretName: default-token-kj7lz
QoS Class:      BestEffort
Tolerations:    <none>
Events:
  FirstSeen     LastSeen        Count   From                    SubObjectPath   Type            Reason          Message
  ---------     --------        -----   ----                    -------------   --------        ------          -------
  6s            6s              1       {default-scheduler }                    Normal          Scheduled       Successfully assigned nginx-752720876-1cgfj to master
  6s            6s              1       {kubelet master}                        Warning         FailedSync      Error syncing pod, skipping: failed to "StartContainer" for "POD" with ErrImagePull: "image pull failed for registry.access.re
dhat.com/rhel7/pod-infrastructure:latest, this may be because there are no credentials on this request.  details: (open /etc/docker/certs.d/registry.access.redhat.com/redhat-ca.crt: no such file or directory)"

証明書/etc/docker/certs.d/registry.access.redhat.com/redhat-ca.crtが見つからないらしい。
これ自体はシンボリックリンクになっていて、リンク先が存在しない模様。

[centos@ip-172-20-1-252 ~]$ ls -al /etc/docker/certs.d/registry.access.redhat.com/
total 0
drwxr-xr-x. 2 root root 27 Dec 11 00:17 .
drwxr-xr-x. 5 root root 75 Dec 11 00:17 ..
lrwxrwxrwx. 1 root root 27 Dec 11 00:17 redhat-ca.crt -> /etc/rhsm/ca/redhat-uep.pem
[centos@ip-172-20-1-252 ~]$
[centos@ip-172-20-1-252 ~]$ ls /etc/ | grep rhsm
[centos@ip-172-20-1-252 ~]$

色々試してマスターノードにpython-rhsmをインストールして解決した。
証明書もインストールされて、イメージもダウンロードできるようになった。

$ sudo yum install python-rhsm

参考:https://access.redhat.com/ja/node/1379303
(python-rhsmを再インストールするとroot証明書を再インストールできるとの記載。そもそもこのパッケージ入ってなかった)

個人的に遊んでるだけなので、証明書の検証はスキップしても構わないのだけど、insecure的な設定をどこからできるのかがわからなかった。

これでやっとKubernetes触れる。。

続きを読む

ALB Ingress Controller を使う

この記事では、 ALB Ingress Controller について書きます。

zalando-incubator/kube-ingress-aws-controller については、 Kubernetes2 Advent Calendar 2017 7日目 @mumoshu 先生の記事で、 書かれていますので、そちらを参照して下さい :bow:

WHY

Kubernetes on AWS で運用している場合、 Kubernetes の Service を作成すると、 AWS の Classic Load Balancer が作成されます。 Classic Load Balancer 以外に Application Load Balancer を利用したい場合が以下のような時にあります。

  • http2 を利用したい
  • /blog などリソース毎に向き先を区切る

Kubernetes on AWS を利用する方は既に AWS を使いだおしている方が大半だと思います。既存のアプリケーションを Kubernetes へ移行しようとした際に、 既に ALB(Application Load Balancer) を利用していたのが、 Kubernetes へ移行したら ELB (Classic Load Balancer) になって http2 無くなりましたというのはパフォーマンスにも影響を与えます。

そこで ALB Ingress Controller を利用することで、 ALB が使えます。

ALB Ingress Controller

The ALB Ingress Controller satisfies Kubernetes ingress resources by provisioning Application Load Balancers.

ALB Ingress Controller は、 Kubernetes の ingress を作成したタイミングで、 Application Load Balancer を作成します。

Design

image.png

The following diagram details the AWS components this controller creates. It also demonstrates the route ingress traffic takes from the ALB to the Kubernetes cluster.

Design に ALB が作られるまでの流れと、トラフィックの流れが書かれています。

Ingress Creation

Kubernetes 上に ingress を一つ作った時の流れ

[1]: The controller watches for ingress events from the API server. When it finds ingress resources that satisfy its requirements, it begins the creation of AWS resources.

[1] ALB Ingress Controller は、 Kubernetes の API Server からの Event を監視し、該当の Event を検知したら AWS のリソースを作成し始める。

[2]: An ALB (ELBv2) is created in AWS for the new ingress resource. This ALB can be internet-facing or internal. You can also specify the subnets its created in using annotations.

[2] ALB を作成する。 annotation を指定することで、サブネットやインターネット向けか内部向けかも決めることができる。

[3]: Target Groups are created in AWS for each unique Kubernetes service described in the ingress resource.

[3] ALB の向き先となるターゲットグループは、 ingress に記述された Service ごとに AWS で作成。

[4]: Listeners are created for every port detailed in your ingress resource annotations. When no port is specified, sensible defaults (80 or 443) are used. Certificates may also be attached via annotations.

[4] リスナは、 ingress の annotation で指定したポート用に作成されます。ポートが指定されていない場合、80または443を使用。 ACM も使用することもできる。

[5]: Rules are created for each path specified in your ingress resource. This ensures traffic to a specific path is routed to the correct Kubernetes Service.

[5] 入力リソースで指定された各パスに対してルールが作成され、特定のパスへのトラフィックが正しい Kubernetes の Service にルーティングされる。

Ingress Traffic

This section details how traffic reaches the cluster.

As seen above, the ingress traffic for controller-managed resources starts at the ALB and reaches the Kubernetes nodes through each service’s NodePort. This means that services referenced from ingress resource must be exposed on a node port in order to be reached by the ALB.

ALB から始まり、各サービスの NodePort を通じて Kubernetes ノードに到達するようになっている。 ALB を使ったサービスを公開するためには、 ingress と NodePort を使った Service の二つが必要になる。

How it Works

  • alb-ingress-controller 用の IAM を作成
  • ALB 作る際に、 sg と subnet を自動でアサインされるように、 subnet にタグの設定
  • AWS の IAM 情報と CLUSTER_NAME を secrets に入れる
  • default サーバーという一旦 target group アサインできるテンポラリのサービスを建てる
  • alb-ingress-controller を deploy する

alb-ingress-controller 用の IAM を作成

Role Permissions

AWS を操作するため、専用の IAM が必要になります。必要になるリソースは例と以下に記載されています。

IAM Policy

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "acm:DescribeCertificate",
                "acm:ListCertificates"
            ],
            "Resource": "*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "ec2:AuthorizeSecurityGroupIngress",
                "ec2:CreateSecurityGroup",
                "ec2:CreateTags",
                "ec2:DeleteSecurityGroup",
                "ec2:DescribeInstances",
                "ec2:DescribeSecurityGroups",
                "ec2:DescribeSubnets",
                "ec2:DescribeTags",
                "ec2:ModifyInstanceAttribute",
                "ec2:RevokeSecurityGroupIngress"
            ],
            "Resource": "*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "elasticloadbalancing:AddTags",
                "elasticloadbalancing:CreateListener",
                "elasticloadbalancing:CreateLoadBalancer",
                "elasticloadbalancing:CreateRule",
                "elasticloadbalancing:CreateTargetGroup",
                "elasticloadbalancing:DeleteListener",
                "elasticloadbalancing:DeleteLoadBalancer",
                "elasticloadbalancing:DeleteRule",
                "elasticloadbalancing:DeleteTargetGroup",
                "elasticloadbalancing:DescribeListeners",
                "elasticloadbalancing:DescribeLoadBalancers",
                "elasticloadbalancing:DescribeRules",
                "elasticloadbalancing:DescribeTags",
                "elasticloadbalancing:DescribeTargetGroups",
                "elasticloadbalancing:DescribeTargetHealth",
                "elasticloadbalancing:ModifyListener",
                "elasticloadbalancing:ModifyLoadBalancerAttributes",
                "elasticloadbalancing:ModifyRule",
                "elasticloadbalancing:ModifyTargetGroup",
                "elasticloadbalancing:RegisterTargets",
                "elasticloadbalancing:RemoveTags",
                "elasticloadbalancing:SetSecurityGroups",
                "elasticloadbalancing:SetSubnets"
            ],
            "Resource": "*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "iam:GetServerCertificate",
                "iam:ListServerCertificates"
            ],
            "Resource": "*"
        }
    ]
}

ALB 作る際に、 sg と subnet を自動でアサインされるように、 subnet にタグの設定

Subnet Selection

ingress の annotation か auto-detection で、 各ALBを作成するサブネットを決定。

  • annotation: alb.ingress.kubernetes.io/subnets に、 subnet ID または NAME タグを使用して指定
  • auto-detection: annotation の指定はなく、自動検出で ALB を作成

auto-detection を有効にするためには、以下の tag を追加します。 ALB を作る際に subnet が二つ必要なため、二つ tag をつける。

  • kubernetes.io/role/alb-ingress=
  • kubernetes.io/cluster/$CLUSTER_NAME=shared

    • $CLUSTER_NAMEalb-ingress-controller.yamlCLUSTER_NAME 環境変数と一致させる必要がある

設定例

image

AWS の IAM 情報と CLUSTER_NAME を Secrets に入れる

namespace name key description
kube-system alb-ingress-controller AWS_ACCESS_KEY_ID credentials of IAM user for alb-ingress-controller
kube-system alb-ingress-controller AWS_SECRET_ACCESS_KEY credentials of IAM user for alb-ingress-controller
kube-system alb-ingress-controller CLUSTER_NAME cluster name
  • 登録方法

k8sec を使って Sercrets に登録します。

$ k8sec set alb-ingress-controller KEY=VALUE -n kube-system
  • 確認
$ k8sec list alb-ingress-controller -n kube-system
NAME            TYPE    KEY         VALUE
alb-ingress-controller  Opaque  AWS_ACCESS_KEY_ID   "hoge"
alb-ingress-controller  Opaque  AWS_SECRET_ACCESS_KEY   "fuga"
alb-ingress-controller  Opaque  CLUSTER_NAME        "Ooops"

ingress に必要になる Default backend サービスを建てる

kubectl Deployments

alb-ingress-controller を使うために必要になる Default backend サービスを建てる。 alb-ingress-controller を利用する ingress は、全て Default backend を指す。

$ kubectl create -f https://raw.githubusercontent.com/coreos/alb-ingress-controller/master/examples/default-backend.yaml

alb-ingress-controller を deploy する

  • alb-ingress-controller manifest ファイルをダウンロードする
$ wget https://raw.githubusercontent.com/coreos/alb-ingress-controller/master/examples/alb-ingress-controller.yaml
  • Secrets に追加したものを manifest file に反映する
        envFrom:
        - secretRef:
            name: alb-ingress-controller
  • AWS_REGION を設定する
- name: AWS_REGION
  value: ap-northeast-1
  • Deploy alb-ingress-controller
$ kubectl apply -f alb-ingress-controller.yaml  
  • log で起動できているか確認できる。
$ kubectl logs -n kube-system 
    $(kubectl get po -n kube-system | 
    egrep -o alb-ingress[a-zA-Z0-9-]+) | 
    egrep -o '[ALB-INGRESS.*$'
[ALB-INGRESS] [controller] [INFO]: Log level read as "", defaulting to INFO. To change, set LOG_LEVEL environment variable to WARN, ERROR, or DEBUG.
[ALB-INGRESS] [controller] [INFO]: Ingress class set to alb
[ALB-INGRESS] [ingresses] [INFO]: Build up list of existing ingresses
[ALB-INGRESS] [ingresses] [INFO]: Assembled 0 ingresses from existing AWS resources

上手く動かない場合ははここを true にすると良い。 AWS の制限で止められている可能性もありえる。

        - name: AWS_DEBUG
          value: "false"

これで ALB Ingress Controller の準備は完了

実際に ALB 作成してみる

alb-ingress-controller にある echo server を元にやってみる。基本的に以下、二点を抑えるだけで ALB
を利用できる。

  • ingress と NodePort を使った Service
  • ingress の annotation の設定

echoservice

alb-ingress-controller にある sample を元に echoserver を建ててみる。

$ kubectl apply -f https://raw.githubusercontent.com/coreos/alb-ingress-controller/master/examples/echoservice/echoserver-namespace.yaml &&
kubectl apply -f https://raw.githubusercontent.com/coreos/alb-ingress-controller/master/examples/echoservice/echoserver-service.yaml &&
kubectl apply -f https://raw.githubusercontent.com/coreos/alb-ingress-controller/master/examples/echoservice/echoserver-deployment.yaml

Namespace を切って、 NodePort で開放する Service と Deployment が作られる。

$ kubectl get all -n echoserver
NAME                             READY     STATUS    RESTARTS   AGE
po/echoserver-2241665424-xm1rt   1/1       Running   0          10m

NAME             TYPE       CLUSTER-IP     EXTERNAL-IP   PORT(S)        AGE
svc/echoserver   NodePort   100.65.13.23   <none>        80:31509/TCP   10m

NAME                DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
deploy/echoserver   1         1         1            1           10m

NAME                       DESIRED   CURRENT   READY     AGE
rs/echoserver-2241665424   1         1         1         10m
  • ingress file を取得する
wget https://raw.githubusercontent.com/coreos/alb-ingress-controller/master/examples/echoservice/echoserver-ingress.yaml
  • annotation の設定(オプション)

Annotations に全部使える ALB の option が書いてある。

alb.ingress.kubernetes.io/scheme: internet-facing # or 'internal'
alb.ingress.kubernetes.io/connection-idle-timeout: # Defauflt 60
alb.ingress.kubernetes.io/subnets: # subnet ID か Name 
alb.ingress.kubernetes.io/security-groups: # sg ID か Name Default 0.0.0.0/0 
alb.ingress.kubernetes.io/listen-ports: '[{"HTTP":80,"HTTPS": 443}]' # Default 80
alb.ingress.kubernetes.io/certificate-arn: arn:aws:acm:ap-northeast-1:hoge:certificate/UUID # ACM 利用する場合
alb.ingress.kubernetes.io/healthcheck-path: # Default "/"
alb.ingress.kubernetes.io/healthcheck-port: # Default Traffic port
alb.ingress.kubernetes.io/healthcheck-interval-seconds: # Default 15
alb.ingress.kubernetes.io/healthcheck-protocol: # Default HTTP
alb.ingress.kubernetes.io/healthcheck-timeout-seconds: # Default 5
alb.ingress.kubernetes.io/healthy-threshold-count: # Default 2
alb.ingress.kubernetes.io/unhealthy-threshold-count: # Default 2
alb.ingress.kubernetes.io/successCodes: # Default 200
alb.ingress.kubernetes.io/tags:  # Tag を入れる
  • ingress を建てる
$ kubectl apply -f echoserver-ingress.yaml

とりあえず default のままでいい場合は下記のコマンド

$ kubectl apply -f https://raw.githubusercontent.com/coreos/alb-ingress-controller/master/examples/echoservice/echoserver-ingress.yaml
  • ALB が作成された log を確認して見る
$ kubectl logs -n kube-system 
  $(kubectl get po -n kube-system | 
  egrep -o alb-ingress[a-zA-Z0-9-]+) | 
  egrep -o '[ALB-INGRESS.*$' | 
  grep 'echoserver/echoserver'
[ALB-INGRESS] [echoserver/echoserver] [INFO]: Start ELBV2 (ALB) creation.
[ALB-INGRESS] [echoserver/echoserver] [INFO]: Completed ELBV2 (ALB) creation. Name: hogefuga-echoserver-ech-2ad7 | ARN: arn:aws:elasticloadbalancing:ap-northeast-1:0000:loadbalancer/app/hogefuga-echoserver-ech-2ad7/17fd1481cb40fcc2
[ALB-INGRESS] [echoserver/echoserver] [INFO]: Start TargetGroup creation.
[ALB-INGRESS] [echoserver/echoserver] [INFO]: Succeeded TargetGroup creation. ARN: arn:aws:elasticloadbalancing:ap-northeast-1:0000:targetgroup/hogefuga-31509-HTTP-c3a0606/9914a217042c4006 | Name: hogefuga-31509-HTTP-c3a0606.
[ALB-INGRESS] [echoserver/echoserver] [INFO]: Start Listener creation.
[ALB-INGRESS] [echoserver/echoserver] [INFO]: Completed Listener creation. ARN: arn:aws:elasticloadbalancing:ap-northeast-1:0000:listener/app/hogefuga-echoserver-ech-2ad7/17fd1481cb40fcc2/0fe42e9436e45013 | Port: 80 | Proto: HTTP.
[ALB-INGRESS] [echoserver/echoserver] [INFO]: Start Rule creation.
[ALB-INGRESS] [echoserver/echoserver] [INFO]: Completed Rule creation. Rule Priority: "1" | Condition: [{    Field: "host-header",    Values: ["echoserver.example.com"]  },{    Field: "path-pattern",    Values: ["/"]  }]
[ALB-INGRESS] [echoserver/echoserver] [INFO]: Fetching Targets for Target Group arn:aws:elasticloadbalancing:ap-northeast-1:0000:targetgroup/hogefuga-31509-HTTP-c3a0606/9914a217042c4006
[ALB-INGRESS] [echoserver/echoserver] [INFO]: Fetching Rules for Listener arn:aws:elasticloadbalancing:ap-northeast-1:0000:listener/app/hogefuga-echoserver-ech-2ad7/17fd1481cb40fcc2/0fe42e9436e45013
[ALB-INGRESS] [echoserver/echoserver] [INFO]: Ingress rebuilt from existing ALB in AWS
  • URL を確認
$ kubectl describe ing -n echoserver echoserver
Name:             echoserver
Namespace:        echoserver
Address:          hogefuga-echoserver-ech-2ad7-126540505.ap-northeast-1.elb.amazonaws.com
Default backend:  default-http-backend:80 (100.96.27.7:8080)
Rules:
  Host                    Path  Backends
  ----                    ----  --------
  echoserver.example.com  
                          /   echoserver:80 (<none>)
Annotations:
Events:
  Type    Reason  Age   From                Message
  ----    ------  ----  ----                -------
  Normal  CREATE  2m    ingress-controller  Ingress echoserver/echoserver
  Normal  CREATE  2m    ingress-controller  hogefuga-echoserver-ech-2ad7 created
  Normal  CREATE  2m    ingress-controller  hogefuga-31509-HTTP-c3a0606 target group created
  Normal  CREATE  2m    ingress-controller  80 listener created
  Normal  CREATE  2m    ingress-controller  1 rule created
  Normal  UPDATE  2m    ingress-controller  Ingress echoserver/echoserver

ここからさらに踏み込んで external DNS の設定がありますが今回は、ALB までで閉じます。
最後に cURL で確認して終了です。

$ curl hogefuga-echoserver-ech-2ad7-126540505.ap-northeast-1.elb.amazonaws.com
CLIENT VALUES:
client_address=10.1.93.88
command=GET
real path=/
query=nil
request_version=1.1
request_uri=http://hogefuga-echoserver-ech-2ad7-126540505.ap-northeast-1.elb.amazonaws.com:8080/

SERVER VALUES:
server_version=nginx: 1.10.0 - lua: 10001

HEADERS RECEIVED:
accept=*/*
host=hogefuga-echoserver-ech-2ad7-126540505.ap-northeast-1.elb.amazonaws.com
user-agent=curl/7.43.0
x-amzn-trace-id=Root=1-5a2d4e2f-5545b75b74003cd80e5134bb
x-forwarded-for=192.168.100.10
x-forwarded-port=80
x-forwarded-proto=http
BODY:
-no body in request-
  • 最後は、削除
$ kubectl delete ns echoserver
namespace "echoserver" deleted

ALB も削除される。

$ curl hogefuga-echoserver-ech-2ad7-126540505.ap-northeast-1.elb.amazonaws.com
curl: (6) Could not resolve host: hogefuga-echoserver-ech-2ad7-126540505.ap-northeast-1.elb.amazonaws.com

続きを読む

CodeBuildの実行結果をslackに通知する

はじめに

Globis Advent Calendar10日目は、弊社のエンジニアチームを支えるインフラ技術をお伝えいたします。
開発スピードをさらに促進するために、Blue Green Deployment, Infrastrucure as Code, ChatOps など新しい運用思想を積極的に取り入れて、手動でのオペレーションを極力なくしています。
今回は、弊社で実運用しているChatOpsの一例として、CodeBuildの実行結果をslackに通知する方法を、可能な範囲で具体的にお伝えいたします。

設定方法

AWS Lambdaの設定

コード

import os
import json
import urllib.request

URL = os.environ['WEBHOOK_URL']


def send_slack(obj):  ## slackに通知
    method = "POST"
    headers = {"Content-Type" : "application/json"}
    js = json.dumps(obj).encode("utf-8")
    request = urllib.request.Request(URL, data=js, method=method, headers=headers)
    with urllib.request.urlopen(request) as response:
        response_body = response.read().decode("utf-8")


def check_status(status):
    fail = False
    if status == 'IN_PROGRESS':
        color = '#008000'  ## green
    elif status == 'SUCCEEDED':
        color = '#00BFFF'  ## deepskyblue
    else:
        color = '#FF00FF'  ## magenta
        fail = True
    return color, fail


def parse_event(event):  ## cloudwatch event から渡された event(dict型)をパース、slackに渡すobjectを生成。
    detail = event['detail']
    status = detail['build-status']
    initiator = detail['additional-information']['initiator']
    log = detail.get('additional-information', {}).get('logs', {}).get('deep-link', 'Not Exist')
    color, fail = check_status(status)
    fields = [{'title': 'Initiator', 'value': initiator, 'short': False},
              {'title': 'Status', 'value': status, 'short': False},
              {'title': 'LogLink', 'value': log, 'short': False}]
    obj = {'attachments': [{'color': color, 'fields': fields}]}
#    if fail == True:  ## Fail時にチャンネルメンション飛ばしたい時はコメントを外す。
#        obj['attachments'][0]['text'] = '<!channel>'
    return obj


def lambda_handler(event, context):  ## lambdaから最初にcallされる関数。
    obj = parse_event(event)
    send_slack(obj)
    return

環境変数の設定

CloudWatch Event の設定

slackでの通知結果

  • ワンクリックでビルドログを眺めることができます。

運用事例のご紹介

  • AWS Athenaのテーブルにクエリを実行し、中間テーブルを生成するETL処理のバッチ
  • Ruby on Rails アプリケーションデプロイ時の db:migrate 処理
  • 最近はCodeBuildをVPC内で実行できるようになったので、利用できる幅が広がっています!

おわりに

いかがでしたでしょうか。python初心者(僕)でもChatOpsに貢献できます。
グロービスではSREエンジニアを募集しています!
インフラの運用担当だけど手作業を自動化したいと考えている方、開発者だけどインフラも含めてコードで管理したい方、一緒に理想のインフラを作ってみませんか?

続きを読む

Now Available: A New AWS Quick Start Reference Deployment for CJIS

Now Available: A New AWS Quick Start Reference Deployment for CJIS | AWS Security Blog. AWS Security Blog Now Available: A New AWS Quick Start Reference Deployment for CJIS As part of t… 続きを表示. AWS Security Blog Now Available: A New AWS Quick Start Reference Deployment for CJIS … 続きを読む

AWS re:Invent 2017 HPC(+α)関連セッション資料リンク集

AWS re:Invent 2017に行ってきました。これから各種報告会で話すにあたり、自分の参加したセッションの資料リンク集を作りました。せっかくなので公開しておきます。主にHPC関連で、少しDeep LearningやBig Data関連を含みます。

AMF304 – Optimizing Design and Engineering Performance in the Cloud for Manufacturing

CMP207 – High Performance Computing on AWS

AMF305 – Autonomous Driving Algorithm Development on Amazon AI

STG205 – #EarthonAWS: How NASA Is Using AWS

STG206 – Big Data: Data Lakes and Data Oceans

CMP323 – AWS Batch: Easy and Efficient Batch Computing on AWS

CMP333 – How to Get the HPC Best-in-class Performance via Intel Xeon Skylake Processors and AWS C5 Instances with Ease of Deployment Through Rescale

CMP306 – Architectures for HPC/HTC Workloads on AWS

CMP402 – Accelerate Your C/C++ Applications with Amazon EC2 F1 Instances

MCL212-R2 – [REPEAT 2] NEW LAUNCH! AWS DeepLens workshop: Building Computer Vision Applications

続きを読む

ゼロからはじめるServerless Java Container

日頃AWSやその他クラウドサービスを使ってインテグレーションしていく中で、 https://github.com/awslabs を定期的にウォッチしているのですが、その中で Serverless Java Container が気になったので試してみました。

https://github.com/awslabs/aws-serverless-java-container

Serverless Java Container is 何?

簡単に言うと、API GatewayとLambdaを使ったサーバレスアプリケーションを Jersey, Spark, Spring Frameworkといったフレームワークを使って作るためのライブラリです。

このライブラリを利用することで、「つなぎ」となる必要最低限のコードを書いてあげさえすれば、あとはいつも通り、フレームワークの流儀に沿ってアプリケーションを実装していくだけで、Lambda上で動くハンドラができあがる、というシロモノです。
絶対不可欠なライブラリではありませんが、あると便利なので、一考の価値はあると思います。


で、このAdvent Calendarにエントリした時には気づいていなかったのですが、AWSの中の人がこのライブラリについて詳しく紹介しているスライド・動画があることに気づきました :neutral_face:
「AWS Dev Day Tokyo 2017」で登壇された時のものみたいですね。

というわけで、このライブラリについての詳細な解説については上記を参考にしてもらうとして、今回のエントリでは、以下のような違いを出しつつ、このライブラリを使ってアプリケーションを作ってみることにします。

  • SAMやCloudFormationなどを使わずに、ゼロから構築してみる
  • よくあるPetStoreアプリケーションではなく、Hello, worldアプリケーションを作る
  • ビルドにはMavenではなくGradleを使う

試してみる

アプリケーションの雛形を作る

Spring Initialzrから新規にGradleプロジェクトを作っていきます。
必要な入力項目は以下のとおり。

項目 入力値
Group com.example
Artifact demo
Dependencies DevTools

「Generate Project」を押すと、プロジェクトの雛形がZipファイルで作られるので、展開後のディレクトリをワークスペースとします。

依存関係にServerless Java Containerを追加

今回の主役となるライブラリを追加します。執筆時点での最新バージョンは0.8のようでした。

build.gradle
    compile('com.amazonaws.serverless:aws-serverless-java-container-spring:0.8')

READMEにしたがってConfigとLambdaHandlerを作成

Serverless Java ContainerリポジトリのREADMEの「Spring support」のセクションを参考にして、アプリケーションとLambdaの「つなぎ」となる部分のコードを作っていきます。

まずはコンフィグ。

com.example.demo.AppConfig.java
@Configuration
@ComponentScan("com.example.demo")
public class AppConfig {
}

続いてハンドラ。

com.example.demo.LambdaHandler.java
public class LambdaHandler implements RequestHandler<AwsProxyRequest, AwsProxyResponse> {

    private static class Singleton {

        static SpringLambdaContainerHandler<AwsProxyRequest, AwsProxyResponse> handler = instance();

        static SpringLambdaContainerHandler<AwsProxyRequest, AwsProxyResponse> instance() {
            try {
                return SpringLambdaContainerHandler.getAwsProxyHandler(AppConfig.class);
            } catch (final ContainerInitializationException e) {
                throw new RuntimeException("Cannot get Spring Lambda Handler", e);
            }
        }
    }

    @Override
    public AwsProxyResponse handleRequest(AwsProxyRequest awsProxyRequest, Context context) {
        return Singleton.handler.proxy(awsProxyRequest, context);
    }
}

LambdaHandler の方は、READMEの通りに実装するとコンパイルエラーになってしまうので、少し修正しました。
SpringLambdaContainerHandler.getAwsProxyHandler がチェック例外 ContainerInitializationException を投げるので、そのままフィールドとして初期化できないんですよね…。

コントローラを作る

準備が終わったので、アプリケーション本体を作っていきます。
とは言え、今回は簡単なHello, Worldアプリケーションなので、これだけです。

com.example.demo.controller.DemoController.java
@RestController
public class DemoController {

    @GetMapping("/hello")
    public String hello(@RequestParam(required = false) Optional<String> message) {
        return "Hello, " + message.orElse("world") + "!";
    }
}

普通のSpringアプリケーションのコードですね。

Lambda用のパッケージの作成

下記ドキュメントを参考に、Lambda用のパッケージを作ります。
http://docs.aws.amazon.com/ja_jp/lambda/latest/dg/create-deployment-pkg-zip-java.html

実は、普段Lambda関数を作る時はランタイムとしてNode.jsを使うことが多く、Javaランタイムを使うのは始めてでした。
build.gradle に以下を追加すればOKです。

build.gradle
task buildZip(type: Zip) {
    from compileJava
    from processResources
    into('lib') {
        from configurations.runtime
    }
}

build.dependsOn buildZip

Lambda関数を作ってパッケージをアップロード

Lambda関数を作って、パッケージをアップロードします。

ここで関数をテストする場合、イベントテンプレートとして「API Gateway AWS Proxy」を選択すればよいです。
選択して出てくるテンプレート中で、実装したアプリケーションに合わせて

  • "queryStringParameters""message": "好きな文字列"
  • "httpMethod""GET"
  • "path""/hello"

としてそれぞれ変更してください(下図)。

lambda.png

API Gatewayと連携させる

仕上げに、APIを作り、デプロイします。

今回は、ルートの直下にプロキシリソースを作ってしまいます(プロキシリソースの呼び出し先のLambda関数は、上記で作成したLambda関数を指定してください)

apigw2.png

テストの際は、クエリ文字列として message=好きな文字列(今回はServerless) を指定し、GETメソッドを呼び出すと、Springのコントローラが発火し、

Hello, Serverless!!

がレスポンスとして返ってくることが確認できます。

また、APIをデプロイした後は、curl コマンドなどでアクセスしても、きちんとレスポンスが取得できることが確認します。

$ curl "https://xxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/dev/hello?message=Serverless"

まとめ

繰り返しになってしまいますが、Serverless Java Containerライブラリ、簡単に言うと、API GatewayのLambdaプロキシ利用時に、普通のSpringアプリケーションとしてハンドラを実装できるようにするためのラッパー、という感じだと思います。
API Gatewayと組み合わせて力を発揮するライブラリですね。
最初は、Lambda上でSpringを使う時の足回りを面倒見てくれるライブラリかな?と思ったのですが、ちょっとイメージしていたものとはズレていました…。

現実的には、RDBに依存したアプリケーションの場合のコネクションの話1など、既存のWebアプリケーションがそのまま載せ替えられるか?というと検討ポイントはありそうですが、使い慣れたフレームワークをサーバレスアプリケーション化する場合には、こういったライブラリの活用もよいのかなーと思いました。

おまけ:起動時間など

LambdaのランタイムとしてJavaを使っている場合、起動時間も少し気になるところだと思うので、メモしておきます。

ちなみに、メモリの設定は512MBです。

正確なベンチマークは取得できていませんが、今回のアプリケーションで確認した範囲においては、10ミリ秒前後の処理時間で済むみたいです(コンテナが起動して、ApplicationContextが初期化済みになっている場合ですが)。

続きを読む

KubernetesでAWS ALBを自動作成する〜ついでにRoute53 Record Setも

kube-ingress-aws-controllerを使います。

kube-ingress-aws-controllerとは

Zalandoが公開している、Kubernetes用のIngress Controllerの一つです。

ZalandoはKubernetes界隈では著名な、ヨーロッパでファッションECをやっている企業です。
Kubernetesコミュニティへの様々な形で貢献していて、今回紹介するkube-aws-ingerss-controllerや先日紹介したexternal-dnsもその一つです。

何かできるのか

KubernetesユーザがAWSを全く触らずとも

  • ALBの自動作成
  • ALBに割り当てるTLS証明書(ACM管理)を自動選択

をしてくれます。

使い方

KubernetesのIngressリソースを普段通りつくります。

kubectl create -f myingress.yaml

すると、1~2分ほどでIngressリソースに書いたホスト名でインターネットからアクセスできるようになります。

open https://myingress.exapmle.com

Ingress Controllerとは

KubernetesのIngressはL7ロードバランサのスペックのようなもので、そのスペックから実際にL7ロードバランサをセットアップするのがIngress Controllerの役割です。

coreos/alb-ingress-controllerとの違い

coreos/alb-ingress-controller

  • Ingressリソース一つに対して、1 ALBをつくります

zalando-incubator/kube-ingress-aws-controller

  • ACM証明書のドメイン一つに対して、ALBを割り当てます
  • 同じドメイン名に対するルートを含むIngressリソースは、一つのALBにまとめられます
  • ALBのターゲットグループにEC2インスタンスを割り当てるところまでしかやってくれない!ので、実際にIngressとして利用するためには他のIngress Controllerを併用する必要があります

kube-ingress-aws-controllerのセットアップ手順

Security Groupの作成

kube-ingress-aws-controllerはALBに割り当てるSecurity Groupまでは自動作成してくれないので、AWSコンソール等で作成します。

kube-ingerss-aws-controllerのドキュメントにはCloudFormationを使った手順がかいてあります。

同等のSecurity Groupをawscliで作る場合は以下のようなコマンドを実行します。

CLUSTER_NAME=...
VPC_ID=vpc-...

aws ec2 create-security-group \
  --description ${CLUSTER_NAME}-kube-aws-ingress-controller-alb \
  --group-name ${CLUSTER_NAME}-kube-aws-ingress-controller-alb \
  --vpc-id $VPC_ID | tee sg.json

SG_ID=$(jq -r '.GroupId' sg.json)

aws ec2 create-tags --resources $SG_ID --tags \
  "Key=\"kubernetes.io/cluster/$CLUSTER_NAME\",Value=owned" \
  "Key=\"kubernetes:application\",Value=kube-ingress-aws-controller"

aws ec2 authorize-security-group-ingress \
  --group-id $SG_ID \
  --ip-permissions '[{"IpProtocol": "tcp", "FromPort": 80, "ToPort": 80, "IpRanges": [{"CidrIp": "0.0.0.0/0"}]}, {"IpProtocol": "tcp", "FromPort": 443, "ToPort": 443, "IpRanges": [{"CidrIp": "0.0.0.0/0"}]}]'

aws ec2 describe-security-groups --group-id $SG_ID

# あとで: 不要になったらクラスタやVPCの削除前に以下のように削除
aws ec2 delete-security-group --group-id $SG_ID

IAMポリシーの割当

kube-ingerss-aws-controllerのドキュメントにIAM Policy Statementの一覧がかいてありますが、要約すると

  • CloudFormationスタックのCRUD権限
  • ACM証明書、VPC、RouteTable、Subnet、Security Group、AutoScalingGroup、EC2 InstanceのGet/List/Describe権限
  • ALBのCRUD権限

が必要です。

kube-awsのcluster.yamlの場合は、以下のように書きます。

worker:
  nodePools:
  - iam:
      policy:
        statements:
        - effect: Allow
          actions:
          - "autoscaling:DescribeAutoScalingGroups"
          - "autoscaling:AttachLoadBalancers"
          - "autoscaling:DetachLoadBalancers"
          - "autoscaling:DetachLoadBalancerTargetGroup"
          - "autoscaling:AttachLoadBalancerTargetGroups"
          - "elasticloadbalancing:AddTags"
          - "elasticloadbalancing:DescribeLoadBalancers"
          - "elasticloadbalancing:CreateLoadBalancer"
          - "elasticloadbalancing:DeleteLoadBalancer"
          - "elasticloadbalancing:DescribeListeners"
          - "elasticloadbalancing:CreateListener"
          - "elasticloadbalancing:DeleteListener"
          - "elasticloadbalancing:DescribeTags"
          - "elasticloadbalancing:CreateTargetGroup"
          - "elasticloadbalancing:DeleteTargetGroup"
          - "elasticloadbalancing:DescribeTargetGroups"
          - "elasticloadbalancingv2:DescribeTargetGroups"
          - "elasticloadbalancingv2:DescribeLoadBalancers"
          - "elasticloadbalancingv2:CreateLoadBalancer"
          - "elasticloadbalancingv2:DeleteLoadBalancer"
          - "elasticloadbalancingv2:DescribeListeners"
          - "elasticloadbalancingv2:CreateListener"
          - "elasticloadbalancingv2:DeleteListener"
          - "elasticloadbalancingv2:DescribeTags"
          - "elasticloadbalancingv2:CreateTargetGroup"
          - "elasticloadbalancingv2:DeleteTargetGroup"
          - "ec2:DescribeInstances"
          - "ec2:DescribeSubnets"
          - "ec2:DescribeSecurityGroup"
          - "ec2:DescribeRouteTables"
          - "ec2:DescribeVpcs"
          - "acm:ListCertificates"
          - "acm:DescribeCertificate"
          - "iam:ListServerCertificates"
          - "iam:GetServerCertificate"
          - "cloudformation:Get*"
          - "cloudformation:Describe*"
          - "cloudformation:List*"
          - "cloudformation:Create*"
          - "cloudformation:Delete*"
          resources:
          - "*"

kube-aws-ingress-controllerをデプロイ

$ kubectl apply -f kube-aws-ingress-controller.yaml
kube-aws-ingress-controller.yaml
apiVersion: apps/v1beta1
kind: Deployment
metadata:
  name: kube-ingress-aws-controller
  namespace: kube-system
  labels:
    application: kube-ingress-aws-controller
    component: ingress
spec:
  replicas: 1
  selector:
    matchLabels:
      application: kube-ingress-aws-controller
      component: ingress
  template:
    metadata:
      labels:
        application: kube-ingress-aws-controller
        component: ingress
    spec:
      containers:
      - name: controller
        image: registry.opensource.zalan.do/teapot/kube-ingress-aws-controller:latest
        env:
        - name: AWS_REGION
          value: ap-northeast-1

併用するIngress Controllerをデプロイ

今回はskipperを使ってみます。

apiVersion: extensions/v1beta1
kind: DaemonSet
metadata:
  name: skipper-ingress
  namespace: kube-system
  labels:
    component: ingress
spec:
  selector:
    matchLabels:
      application: skipper-ingress
  updateStrategy:
    type: RollingUpdate
  template:
    metadata:
      name: skipper-ingress
      labels:
        component: ingress
        application: skipper-ingress
    spec:
      hostNetwork: true
      containers:
      - name: skipper-ingress
        image: registry.opensource.zalan.do/pathfinder/skipper:latest
        ports:
        - name: ingress-port
          containerPort: 9999
          hostPort: 9999
        args:
          - "skipper"
          - "-kubernetes"
          - "-kubernetes-in-cluster"
          - "-address=:9999"
          - "-proxy-preserve-host"
          - "-serve-host-metrics"
          - "-enable-ratelimits"
          - "-experimental-upgrade"
          - "-metrics-exp-decay-sample"
          - "-kubernetes-https-redirect=true"
        resources:
          limits:
            cpu: 200m
            memory: 200Mi
          requests:
            cpu: 25m
            memory: 25Mi
        readinessProbe:
          httpGet:
            path: /kube-system/healthz
            port: 9999
          initialDelaySeconds: 5
          timeoutSeconds: 5

WorkerノードのSecurity Group設定変更

今回はskipperをつかうことにしたので、kube-ingress-aws-controllerが作成したALBからアクセスする先はskipper(がいるEC2インスタンス)になります。

Security GroupへALBからskipperがいるEC2インスタンスへの通信をブロックしたままだとGateway Timeoutになってしまいます。そうならないように、ALB用につくったSGから、WorkerノードのSGへの9999番ポート(kube-ingress-aws-controllerと組み合わせて使うskipperのhostPortに指定した)の通信を許可しましょう。

ALB側SGのOutboundを絞っていないのであれば、Worker側SGのInboundを追加すればOKです。

Ingressリソースの作成

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: nginx
spec:
  rules:
  - host: nginx-ingress.example.com
    http:
      paths:
      - backend:
          serviceName: nginx
          servicePort: http

---

apiVersion: v1
kind: Service
metadata:
  name: nginx
spec:
  type: ClusterIP
  ports:
  - name: http
    port: 80
    targetPort: 80
  selector:
    app: nginx

---

apiVersion: apps/v1beta1
kind: Deployment
metadata:
  name: nginx
spec:
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - image: nginx
        name: nginx
        ports:
        - containerPort: 80

ログの確認

$ k logs kube-ingress-aws-controller-6bbd8f9d6c-bcqph
2017/12/06 12:33:52 starting /bin/kube-ingress-aws-controller
2017/12/06 12:33:54 controller manifest:
2017/12/06 12:33:54     kubernetes API server:
2017/12/06 12:33:54     Cluster ID: k8s3
2017/12/06 12:33:54     vpc id: vpc-12345678
2017/12/06 12:33:54     instance id: i-07e29f841f676ca00
2017/12/06 12:33:54     auto scaling group name: k8s3-Nodepool1-MMF7MXKI9350-Workers-BZWB5IAV7JW8
2017/12/06 12:33:54     security group id: sg-8368a6fa
2017/12/06 12:33:54     private subnet ids: []
2017/12/06 12:33:54     public subnet ids: [subnet-12345678 subnet-23456789]
2017/12/06 12:33:54 Start polling sleep 30s

30秒経過後、以下のようにCloudFormationスタックが作成される。

2017/12/06 12:34:24 Found 1 ingresses
2017/12/06 12:34:24 Found 0 stacks
2017/12/06 12:34:24 Have 1 models
2017/12/06 12:34:24 creating stack for certificate "arn:aws:acm:ap-northeast-1:myawsaccountid:certificate/64f33935-05ac-4e6b-b1ae-58973d556a74" / ingress ["kube-system/nginx"]
2017/12/06 12:34:25 stack "arn:aws:cloudformation:ap-northeast-1:myawsaccountid:stack/k8s3-b9dbfe3/caf1f3a0-da81-11e7-9e21-500c28b97482" for certificate "arn:aws:acm:ap-northeast-1:myawsaccountid:certificate/64f33935-05ac-4e6b-b1ae-58973d556a74" created
2017/12/06 12:34:25 Start polling sleep 30s

作成されたAWSリソースの確認

1〜2分待ってスタックがCREATE_COMPLETE状態になれば成功。

コンソールでCloudFormationスタックのResourcesの内容を見ると、何がつくられたのかがわかる。

image.png

つくられるのは以下の4つ。

  • HTTPListener: 80番ポート用のListener
  • HTTPSListener: 443番ポート用のListener
  • LB: ALB
  • TG: kube-ingress-aws-controllerがデプロイされているノードが登録されたTargetGroup

ALB

リスナー

443番ポート用のListenerには、Ingressリソースに書いたドメインに対応するACM証明書が選択されています。

今回は*.example.com用のワイルドカード証明書を事前に用意しておいたのですが、Ingressにnginx-ingress.example.comというホスト名を設定したところ、ちゃんとワイルドカード証明書を探し出してくれました(かしこい)。

image.png

ターゲットグループ

kube-aws-ingress-controllerがデプロイされたノードのASGをコンソールでみてみると、ターゲットグループに割り当てられていました。EC2インスタンスを直接TargetGroupに登録していくような方法だとインスタンスが落ちた場合などが怖いですが、ちゃんとしてますね。

image.png

Route53 RecordSetの作成

これだけだとALBが作成されただけなので、nginx-ingress.example.comでアクセスできないはずです。

しかし、昨日デプロイしたexternal-dnsがIngressリソースとALBを検知して、勝手にRecordSetをつくってくれていました。

stern_external-dns.log
external-dns-768686fd4c-zpnlx external-dns time="2017-12-06T12:43:53Z" level=info msg="Desired change: CREATE nginx-ingress.example.com A"
external-dns-768686fd4c-zpnlx external-dns time="2017-12-06T12:43:53Z" level=info msg="Desired change: CREATE nginx-ingress.example.com TXT"
external-dns-768686fd4c-zpnlx external-dns time="2017-12-06T12:43:53Z" level=info msg="Record in zone example.com. were successfully updated"

image.png

ちゃんとALBへのA(lias)レコードを作成してくれていますね。

 インターネットからアクセスしてみる

nginx-ingress.example.comにブラウザからアクセスしてみて、以下のようなnginxのウェルカムページが表示されてば成功です。おつかれさまでした。

image.png

まとめ

kube-ingress-aws-controllerを使うと、Kubernetesユーザはkubectl createするだけでALBとRecordSetをよしなにセットアップしてくれます。
ALBの作成・管理やRoute53 RecordSetの作成のためにいちいちインフラエンジニアを呼び出したくない!というようなセルフサービス好きの会社さんでは特に役立つのではないでしょうか?!

トラブルシューティング

unable to get details for instance “i-0346d738155e965d8”

IAMポリシーが足りないときに出るエラーです。

$ k logs kube-ingress-aws-controller-7f7974ff58-6bvv8
2017/12/06 07:11:51 starting /bin/kube-ingress-aws-controller
2017/12/06 07:11:53 unable to get details for instance "i-0346d738155e965d8": NoCredentialProviders: no valid providers in chain. Deprecated.
    For verbose messaging see aws.Config.CredentialsChainVerboseErrors

required security group was not found

Security Groupがないか、またはSecurityGroupのタグが間違っているか、EC2インスタンスにkubernetes.io/cluster/クラスタ名=ownedというタグがついていない場合のエラーです。

$ k logs kube-ingress-aws-controller-7f7974ff58-xqgrq
2017/12/06 08:10:40 starting /bin/kube-ingress-aws-controller
2017/12/06 08:10:41 required security group was not found

CloudFormationで「At least two subnets in two different Availability Zones must be specified」

KubernetesのWorkerノードのASGが単一のAZに割り当てられているときのエラー。ALBの仕様で、最低2つのAZが必要。

kube-ingress-aws-controller-7f7974ff58-rp7t8 controller 2017/12/06 12:01:06 Start polling sleep 30s
kube-ingress-aws-controller-7f7974ff58-rp7t8 controller 2017/12/06 12:01:36 Found 1 ingresses
kube-ingress-aws-controller-7f7974ff58-rp7t8 controller 2017/12/06 12:01:37 Found 0 stacks
kube-ingress-aws-controller-7f7974ff58-rp7t8 controller 2017/12/06 12:01:37 Have 1 models
kube-ingress-aws-controller-7f7974ff58-rp7t8 controller 2017/12/06 12:01:37 creating stack for certificate "arn:aws:acm:ap-northeast-1:myaccountid:certificate/64f33935-05ac-4e6b-b1ae-58973d556a74" / ingress ["kube-system/nginx"]
kube-ingress-aws-controller-7f7974ff58-rp7t8 controller 2017/12/06 12:01:37 stack "arn:aws:cloudformation:ap-northeast-1:myaccountid:certificate:stack/k8s3-b9dbfe3/360e1830-da7d-11e7-99f7-500c596c228e" for certificate "arn:aws:acm:ap-northeast-1:myaccountid:certificate:certificate/64f33935-05ac-4e6b-b1ae-58973d556a74" created

image.png

instance is missing the “aws:autoscaling:groupName” tag

ASG以外で作ったEC2インスタンスにkube-ingress-aws-controllerがデプロイされてしまったときのエラー。

$ k logs kube-ingress-aws-controller-7f7974ff58-m6ss2
2017/12/06 12:25:59 starting /bin/kube-ingress-aws-controller
2017/12/06 12:25:59 instance is missing the "aws:autoscaling:groupName" tag

kube-aws-ingress-controllerは、デフォルトではASGに設定されたSubnetをALBのSubnetに流用する。
そのためにASGを探すとき、EC2インスタンスについたaws:autoscaling:groupNameというASGが自動的につけてくれるタグをヒントにするため、ASG以外でつくったEC2インスタンスではこのエラーが出てしまう。

Ref: Spot Fleet support · Issue #105 · zalando-incubator/kube-ingress-aws-controller

Issueも出ているが、まだASG以外は対応していない。ワークアラウンドとしては、kube-ingress-aws-controllerのaffinityでASGでつくったノードにだけスケジュールされるようにすることが考えられる。

kube-awsの場合、awsNodeLabels機能をオンにすると、ASGでつくったノードには”kube-aws.coreos.com/autoscalinggroup”というラベルが付与されるので、それを前提にすると以下のようなaffinityをかけばOK。

              affinity:
                nodeAffinity:
                  requiredDuringSchedulingIgnoredDuringExecution:
                    nodeSelectorTerms:
                    - matchExpressions:
                      - key: "kube-aws.coreos.com/autoscalinggroup"
                        operator: "Exists"

504 Gateway Time-out

ALB経由でnginxにアクセスしようとしてこのエラーがかえってきた場合、ALB用につくったセキュリティグループからkube-aws-ingress-controllerが動いているEC2インスタンスへのアクセスを許可できていない可能性があります。

EC2インスタンス側のSGに、ALB用SGからの9999番ポート(kube-ingress-aws-controllerと組み合わせて使うskipperのhostPortに指定した)への通信をを許可するようなInboundルールを追加しましょう。

続きを読む

Istio Ingress on Kubernetes on AWS

自己紹介


Istio?

  • “An open platform to connect, manage, and secure microservices. https://istio.io
  • Kubernetesで動くService Meshの一つ

Istio Ingress?

  • istio-ingress-controller
  • KubernetesのIngress Controllerの一種
  • Ingress … L7ロードバランシング(の設定)
  • Ingress Controller … Ingressリソースの内容に応じてL7ロードバランサをプロビジョニングする

アーキテクチャ

image.png

ref: @kelseyhightower-san, https://github.com/kelseyhightower/istio-ingress-tutorial#architecture

  • Nginx Ingresesとかと同じ構成
  • Istio Ingress Node Pool
    • 例: インターネットに公開する場合

      • インターネットに公開したL4、L7ロードバランサ経由(Classic Load Balancer, Application Load Balancer)で、プライベートサブネット内ノードのIstio Ingress Controllerへ
      • AWS Network Load Balancer経由でパブリックサブネット内ノードのIstio Ingress Controllerへ

Istio Ingressでできないこと

以下はIstio Ingressの責任範囲外

  • AWS Security Groupの設定

    • 手動、Terraform, CloudFormation、kube-awsなどのクラスタプロビジョニングツールを使う
  • AWS ELB, ALBのプロビジョニング
    • 手動、Terraform, CloudFormationなどで管理
    • Istio Ingressと他のIngress Controllerを併用する(kube-ingress-aws-controller)
  • AWS Route53 Record Setの作成

典型的なistioのインストール手順

前提として、RBACは有効に、istio専用のネームスペースをつくり、デフォルトのzipkin以外で分散トレースするとして・・・

helm installと、立て続けにhelm upgradeを実行する必要があるところがわかりづらい。


まずhelm installでCRD(Customer Resource Definition)をつくり、

$ helm install incubator/istio --set rbac.install=true --namespace istio-system --devel

helm upgradeでCRD以外をつくる。

helm upgrade eponymous-billygoat incubator/istio --reuse-values --set istio.install=true --devel

kubectlを打つのが面倒なので、kubensでistio-systemを選択。

alias k=kubectl
alias kn=kubens

kn istio-system

istio configmapにistioが接続するzipkinのホスト名が書いてあるので、それを変更。

k edit configmap istio

デフォルトでは以下のようになっている。

# Zipkin trace collector
zipkinAddress: zipkin:9411

Stackdriverにトレースを流す場合

zipkinAddress: stackdriver-zipkin.kube-system:9411

dd-zipkin-proxy+Datadog APMにトレースを流す場合、
iptablesで$MAGIC_IP:9411->$HOST_IP:9411にredirectさせてから、

zipkinAddress: 169.254.169.256:9411

Istio Ingressを試す


Ingressのプロキシ先バックエンドサービスをデプロイする

いつものKubernetesへのDeployment + istioctl kube-inject

httpbinをデプロイする。

$ git clone git@github.com:istio/istio.git
$ cd istio/samples/httpbin
$ kn istio-ingress-test
$ k apply -f <(istioctl kube-inject -f samples/httpbin/httpbin.yaml)

k apply -f httpbin.yaml のかわりに k apply -f <(istioctl kube-inject -f httpbin.yaml)としてIstio Sidecarの定義を付け加えている。


Kubernetes Ingressリソースの作成

cat <<EOF | kubectl create -f -
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: simple-ingress
  annotations:
    kubernetes.io/ingress.class: istio
spec:
  rules:
  - http:
      paths:
      - path: /.*
        backend:
          serviceName: httpbin
          servicePort: 8000
EOF

kind: IngressとあるとおりKubenetesの一般的なIngressリソースをつくるだけ。

Istio Ingress ControllerがこのIngressを検知して、よしなにEnvoyの設定変更をしてくれる。Nginx Ingress Controllerの場合よしなに設定変更されるのがNginxという違い。


Istio Ingressからバックエンドへのルーティング

あとは、Istioで一般に使うRouteRuleを設定することで、リクエストをルーティングする。Istio “Ingress”に特別なことはない。


istioのRouteRuleとは?

RouteRuleはistio管理下のService A・B間でどういうルーティングをするかという設定を表す。

Service A —istio w/ RouteRule—> Service B


Istio Ingress + RouteRule

Istio Ingressに対してRouteRuleを設定する場合、基本的にsourceがistio-ingressになる(kubectl –namespace istio get svcするとistio-ingressというServiceがいるが、それ)。

Istio Ingress —istio w/ RouteRule—> Backend Service A


Istio Ingress + RouteRuleの例

例えば、バックエンド障害時のIstio Ingressの挙動を確認したい場合。以下のようなRouteRuleで、istio ingressから全バックエンドへのリクエストを遮断する・・・というのもルーティングの範疇。

cat <<EOF | istioctl create -f -
## Deny all access from istio-ingress
apiVersion: config.istio.io/v1alpha2
kind: RouteRule
metadata:
  name: deny-route
spec:
  destination:
    name: httpbin
  match:
    # Limit this rule to istio ingress pods only
    source:
      name: istio-ingress
      labels:
        istio: ingress
  precedence: 1
  route:
  - weight: 100
  httpFault:
    abort:
      percent: 100
      httpStatus: 403 #Forbidden for all URLs
EOF

Istio Ingressでできること

クラスタ内部向けのRouteRule同様、specに色々かいて活用すると以下のようなことができる。

  • Cookieが特定のパターンにマッチしたらサービスv2、そうでなければサービスv1にルーティング

    • ユーザがアクセスするWebサービス、Web APIのA/Bテスト
    • β版Web UIの先行公開
  • バックエンドの重み付け(route[].weight)
    • ミドルウェアやライブラリの更新を一部リクエストで試したい
  • レートリミット
    • 重いWeb APIに全体で100 req/sec以上のリクエストが送られないように
  • リトライ(httpReqRetries)
    • 不安定なサービスへのリクエストが失敗したら、Istio Ingressレイヤーでリトライ。クライアント側でリトライしなくて済む
  • タイムアウト(httpReqTimeout)

あわせて読みたい: Istio / Rules Configuration


まとめ

  • 同じL7のALBなど単体ではできないL7ロードバランシングが実現できます
  • User-Facingなサービスへのデプロイを柔軟にできて、アプリやミドルウェアの更新起因の大きな障害の予防になる

AWSの場合は使わなさそうな機能

使わなくていいものをうっかり使ってしまわないように。


Secure Ingress

https://istio.io/docs/tasks/traffic-management/ingress.html#configuring-secure-ingress-https

Istio IngressがTLS終端してくれる機能。SNIに対応してない(Envoyレベルで)とか、証明書の管理が面倒(AWS ACMよりも)などの点から、使うことはなさそう。


あわせて読みたい

続きを読む