EKSを利用してKubernetesでSpring MVCをデプロイ(NLB + Auto Scaling)

ここではEKSを利用して、NLB配下にAutoScalingで起動するアプリケーションを起動するときの例について取り上げます。既にマネジメントコンソールやeksctlコマンドを通してクラスターは起動状態であるものとします。

PodがWorkloadsリソースの最小単位となります。それに対してReplicaSetによりPodのレプリカが作成され、指定した数のPodを維持し続けるようにします。そして、Deploymentが複数のReplicaSetを管理することで、ローリングアップデートやロールバック等を実現します。

MySQLやRedisのユーザー名やパスワード情報ははKubernetesのSecretの機能を利用してWebアプリケーションに渡します。 KubernetesのSecretでは環境変数として渡す方法とVolumeとしてマウントする方法がありますが、ここではSpring MVCのアプリケーションに対して環境変数として渡す方法について取り上げます。

以下の3つに分けて説明を行います。EKS以外のプラットフォームを使用している場合は、1の手順やその他適宜置き換えてください。

  1. EKSの設定
  2. Dockerイメージの準備
  3. Kubernetesの設定

EKSの設定

eksctlの準備

eksctlを最新バージョンにアップデート

$ brew upgrade eksctl && brew link --overwrite eksctl

eksctlをインストールしてない場合は以下の方法でインストール

$ brew tap weaveworks/tap
$ brew install weaveworks/tap/eksctl
$ eksctl version
0.18.0

(参照) eksctl の開始方法

VPCの準備

予め新規にVPCとプライベートサブネットを3つ作成し、プライベートサブネットのルーティングテーブルとして、0.0.0.0/0宛にNAT Gateway, VPC PeeringでRDS, ElastiCacheへのルーティングを設定します。 このプライベートサブネットはNLB配下のターゲットインスタンスを配置するためのものとなります。また、RDSやElastiCacheは異なるVPCにある既存のものを利用する前提となっています。

VPC Peeringを使用する場合は、作成するEKSクラスターのVPCを作成する際、CIDRの重複がないことなどVPC Peeringの制約に注意ください。

NLB用に各AZに計3つのパブリックサブネットを作成し、以下のタグを付与します。

  • kubernetes.io/cluster/mywebsite-cluster shared
  • kubernetes.io/role/elb 1

(参照) クラスター VPC に関する考慮事項

EKSクラスターの作成

以下のようにEKSクラスターを作成

$ eksctl create cluster \
	--name test-cluster \
	--region us-west-2 \
	--version 1.15 \
	--nodegroup-name mywebsite-ng \
	--node-type t3.medium \
	--nodes 3 \
	--nodes-min 2 \
	--nodes-max 4 \
	--ssh-access \
	--vpc-public-subnets "subnet-054211583a350c088,subnet-054211583a350c088,subnet-0864ac20958b3b37c" \
	--ssh-public-key my_main_key \
	--managed

作成に失敗すると、CloudFormationのスタックのロールバックに時間を要し、再度リクエストを投げられるようになるまで時間がかかります。

[✖]  creating CloudFormation stack "eksctl-mywebsite-cluster-cluster": AlreadyExistsException: Stack [eksctl-mywebsite-cluster-cluster] already exists
	status code: 400, request id: 411a194d-cf37-4197-ab3b-41fa9672cf80

クラスターは以下の方法で削除します。

$ eksctl delete cluster \
    --name mywebsite-cluster2 \
    --wait

スタックの状況はコマンドで確認できます。

$ aws cloudformation describe-stacks

以下のようにreadyの文字で作成完了していることを確認できます。

[ℹ]  eksctl version 0.18.0
[ℹ]  using region us-west-2
[✔]  using existing VPC (vpc-036234633815de38e) and subnets (private:[] public:[subnet-054211583a350c088 subnet-0864ac20958b3b37c])
[!]  custom VPC/subnets will be used; if resulting cluster doesn't function as expected, make sure to review the configuration of VPC/subnets
[ℹ]  using EC2 key pair %!!(MISSING)q(*string=<nil>)
[ℹ]  using Kubernetes version 1.15
[ℹ]  creating EKS cluster "mywebsite-cluster" in "us-west-2" region with managed nodes
[ℹ]  will create 2 separate CloudFormation stacks for cluster itself and the initial managed nodegroup
[ℹ]  if you encounter any issues, check CloudFormation console or try 'eksctl utils describe-stacks --region=us-west-2 --cluster=mywebsite-cluster'
[ℹ]  CloudWatch logging will not be enabled for cluster "mywebsite-cluster" in "us-west-2"
[ℹ]  you can enable it with 'eksctl utils update-cluster-logging --region=us-west-2 --cluster=mywebsite-cluster'
[ℹ]  Kubernetes API endpoint access will use default of {publicAccess=true, privateAccess=false} for cluster "mywebsite-cluster" in "us-west-2"
[ℹ]  2 sequential tasks: { create cluster control plane "mywebsite-cluster", create managed nodegroup "mywebsite-ng" }
[ℹ]  building cluster stack "eksctl-mywebsite-cluster-cluster"
[ℹ]  deploying stack "eksctl-mywebsite-cluster-cluster"
[ℹ]  building managed nodegroup stack "eksctl-mywebsite-cluster-nodegroup-mywebsite-ng"
[ℹ]  deploying stack "eksctl-mywebsite-cluster-nodegroup-mywebsite-ng"
[✔]  all EKS cluster resources for "mywebsite-cluster" have been created
[✔]  saved kubeconfig as "/Users/hayshogo/.kube/config"
[ℹ]  nodegroup "mywebsite-ng" has 3 node(s)
[ℹ]  node "ip-10-0-0-151.us-west-2.compute.internal" is ready
[ℹ]  node "ip-10-0-20-235.us-west-2.compute.internal" is ready
[ℹ]  node "ip-10-0-20-244.us-west-2.compute.internal" is ready
[ℹ]  waiting for at least 2 node(s) to become ready in "mywebsite-ng"
[ℹ]  nodegroup "mywebsite-ng" has 3 node(s)
[ℹ]  node "ip-10-0-0-151.us-west-2.compute.internal" is ready
[ℹ]  node "ip-10-0-20-235.us-west-2.compute.internal" is ready
[ℹ]  node "ip-10-0-20-244.us-west-2.compute.internal" is ready
[ℹ]  kubectl command should work with "/Users/hayshogo/.kube/config", try 'kubectl get nodes'
[✔]  EKS cluster "mywebsite-cluster" in "us-west-2" region is ready

NLBはServiceのリソースを作成しましたが、以下のようにエンドポイントを確認できます。

$ kubectl get service                                                         
NAME                TYPE           CLUSTER-IP       EXTERNAL-IP                                                                     PORT(S)                      AGE
kubernetes          ClusterIP      172.20.0.1       <none>                                                                          443/TCP                      72m
mywebsite-service   LoadBalancer   172.20.203.140   a25f46cd53643416d9bb376462b33f80-02aa51afd469eb5d.elb.us-west-2.amazonaws.com   80:30759/TCP,443:31461/TCP   18m

対象のドメインとNLBのFQDNの関連付けについて、Route53を利用している場合はAliasレコード、それ以外のレジストラを利用している場合はCNAMEレコードに登録しておきます。

Dockerイメージの準備

DockerHubからTomcatのイメージを利用してDockerイメージを作成します。

$ mkdir mywebsite && cd $_
$ mkdir conf

conf/以下のTomcatの設定ファイルとしてserver.xmlを配置します。

$ conf/server.xml

server.xmlは以下の内容で記載します。

  • server.xml
<?xml version="1.0" encoding="UTF-8"?>
<Server port="8005" shutdown="SHUTDOWN">
  <Listener className="org.apache.catalina.startup.VersionLoggerListener" />
  <Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" />
  <Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" />
  <Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />
  <Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener" />

  <GlobalNamingResources>
    <Resource name="UserDatabase" auth="Container"
              type="org.apache.catalina.UserDatabase"
              description="User database that can be updated and saved"
              factory="org.apache.catalina.users.MemoryUserDatabaseFactory"
              pathname="conf/tomcat-users.xml" />
  </GlobalNamingResources>

  <Service name="Catalina">
    <Connector port="80" protocol="HTTP/1.1"
               connectionTimeout="20000"
               redirectPort="8443" />
    <Engine name="Catalina" defaultHost="localhost">
      <Realm className="org.apache.catalina.realm.LockOutRealm">
        <Realm className="org.apache.catalina.realm.UserDatabaseRealm"
               resourceName="UserDatabase"/>
      </Realm>

      <Host name="localhost"  appBase="webapps"
            unpackWARs="true" autoDeploy="true">

        <Context path="/" docBase="hayashier-website-0.0.1-SNAPSHOT" reloadable="true" />
        <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
               prefix="localhost_access_log" suffix=".txt"
               pattern="%h %l %u %t &quot;%r&quot; %s %b" />

      </Host>
    </Engine>
  </Service>
</Server>

Dockerfileを作成します。

$ vim Dockerfile
  • Dockerfile
FROM tomcat:9.0.34-jdk11
MAINTAINER "Shogo Hayashi <goodheaven700@gmail.com>"

ADD hayashier-website-0.0.1-SNAPSHOT.war /usr/local/tomcat/webapps/
COPY conf/server.xml  /usr/local/tomcat/conf/server.xml

EXPOSE 80

CMD ["catalina.sh", "run"]

Spring MVCなどのように作成したwarファイルをDockerイメージを作成するディレクトリにコピーしてきます。

$ cp /Path/To/MavenProject/target/hayashier-website-0.0.1-SNAPSHOT.war .

ローカルでテストする場合は以下のようにビルドしてWebサーバを稼働させておきます。

$ docker build -t mywebsite .
$ docker run -p 80:80 --rm -it mywebsite/tomcat

正常に稼働していることを確認できます。また、http://localhost でアクセスできることも確認します。

$ docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS                          NAMES
0e5b508c18f6        mywebsite/tomcat    "catalina.sh run"   4 minutes ago       Up 4 minutes        0.0.0.0:80->80/tcp, 8080/tcp   nervous_khorana
$ docker exec -it 0e5b508c18f6 bash 
root@0e5b508c18f6:/usr/local/tomcat# 

動作に問題がない場合、ECRにレポジトリを予め作成しておき、こちらにイメージをpushします。以下はECRのコンソール上でも実行例として表示されるものとなります。

$ aws ecr get-login-password --region us-west-2 | docker login --username AWS --password-stdin xxxxxxxxxxxx.dkr.ecr.us-west-2.amazonaws.com/mywebsite
$ docker build -t mywebsite .
$ docker tag mywebsite:latest xxxxxxxxxxxx.dkr.ecr.us-west-2.amazonaws.com/mywebsite:latest
$ docker push xxxxxxxxxxxx.dkr.ecr.us-west-2.amazonaws.com/mywebsite:latest

Kubernetesの設定

以下の3つに分けて説明を行います。

  1. NLBの設定
  2. 機密情報の受け渡し設定
  3. コンテナ設定

NLBの設定

NLBを作成するため、service-nlb.yamlという名前で以下のようにマニフェストファイルを作成します。 ここではNLBのTLSリスナーを作成するために、事前に対象のドメインでSSL証明書をACMで発行済みであることを前提としています。

  • service-nlb.yaml
apiVersion: v1
kind: Service
metadata:
  name: mywebsite-service
  annotations:
    service.beta.kubernetes.io/aws-load-balancer-type: nlb
    service.beta.kubernetes.io/aws-load-balancer-ssl-cert: "arn:aws:acm:us-west-2:xxxxxxxxxxxx:certificate/c97dad88-731f-4cd0-939c-1565e7db1c6d"
    service.beta.kubernetes.io/aws-load-balancer-backend-protocol: "http"
    service.beta.kubernetes.io/aws-load-balancer-ssl-ports: "https"

spec:
  type: LoadBalancer
  selector:
    app: mywebsite
  ports:
    - name: http
      protocol: TCP
      port: 80
      targetPort: 80
    - name: https
      protocol: TCP
      port: 443
      targetPort: 80

機密情報の受け渡し設定

EKSでは現状ECSのようにSecret Managerに対応しておらず、アプリケーション上からSDKを通してしか利用することができません。

[EKS] [request]: AWS Secrets Manager / SSM Parameter Store #168

そのため、Kubernetesで稼働するアプリケーションに機密情報を渡すために、KubernetesのSecretリソースを利用します。

KubernetesのSecretの機能では、YAMLファイルを作成して、kubectl applyコマンドを使用する方法と、kubectl create secret generic <Secretリソース名>で直接コマンドで直接作成する方法があります。

YAMLファイルを通してSecretのリソースを利用する場合、環境変数のキーおよび値をBase64でエンコーディングしておく必要があります。

$ echo -n 'jdbc:mysql://xxxxxxxxxx.yyyyyyyyyy.us-west-2.rds.amazonaws.com:3306/mywebsite' | base64
amRiYzpteXNxbDovL3h4eHh4eHh4eHgueXl5eXl5eXl5eS51cy13ZXN0LTIucmRzLmFtYXpvbmF3cy5jb206MzMwNi9teXdlYnNpdGU=
$ echo -n 'sampleuser' | base64
c2FtcGxldXNlcg==
$ echo -n 'Test1234' | base64
VGVzdDEyMzQ=
$ echo -n 'zzzzzzzzzz.aaaaaa.ng.0001.usw2.cache.amazonaws.com' | base64
enp6enp6enp6ei5hYWFhYWEubmcuMDAwMS51c3cyLmNhY2hlLmFtYXpvbmF3cy5jb20=

エンコーディングした値をSecretリソースのマニフェストファイルで指定します。secret-mywebsite.yamlというような名前でファイルを作成します。

  • secret-mywebsite.yaml
apiVersion: v1
kind: Secret
metadata:
  name: mywebsite-secret
data:
  dbhost: amRiYzpteXNxbDovL3h4eHh4eHh4eHgueXl5eXl5eXl5eS51cy13ZXN0LTIucmRzLmFtYXpvbmF3cy5jb206MzMwNi9teXdlYnNpdGU=
  dbuser: c2FtcGxldXNlcg==
  dbpassword: VGVzdDEyMzQ=
  cachehost: enp6enp6enp6ei5hYWFhYWEubmcuMDAwMS51c3cyLmNhY2hlLmFtYXpvbmF3cy5jb20=

kubectl applyコマンドで適用します。

$ kubectl apply -f secret-mywebsite.yaml
secret/mywebsite-secret created

もしくは、YAMLファイルを使用せずに、kubectl create secret generic <Secretリソース名>で直接コマンドで行うこともできます。YAMLファイルを使用する際は、事前にBase64でエンコーディングする必要はありません。

$ kubectl create secret generic mywebsite-secret \
	--from-literal=dbhost='jdbc:mysql://xxxxxxxxxx.yyyyyyyyyy.us-west-2.rds.amazonaws.com:3306/mywebsite' \
	--from-literal=dbuser='sampleuser' \
	--from-literal=dbpassword='Test1234' \
	--from-literal=cachehost='zzzzzzzzzz.aaaaaa.ng.0001.usw2.cache.amazonaws.com'

Spring MVCの方でMySQLやRedisの設定で.propertiesファイルに設定を記載している場合、以下のように環境変数で取得した値を代入するように設定します。

  • db.properties
db.url=${DBHOST}
db.username=${DBUSER}
db.password=${DBPASSWORD}
  • cache.properties
cache.host=${CACHEHOST}
cache.port=6379
cache.password=
cache.timeout=

後述するようにPod設定部分に以下のように設定することでコンテナで稼働するアプリケーションに環境変数を渡します。env直下のnameで環境変数名、secretKeyRef直下のnameでSecretリソースのmetadata.nameで指定した名前、keyでSecretリソースのdata以下で指定したキーを指定します。

env:
- name: DBHOST
valueFrom:
    secretKeyRef:
    name: mywebsite-secret
    key: dbhost

コンテナ設定

Deploymentを通して起動する方法がKubernetesで推奨されている方法となります。そのため、ここではDeploymentリソースにより、ReplicaSetを作成し、目的のPodを立ち上げるという3層構成でコンテナを立ち上げます。

deployment-mywebiste.yamlという名前でDeploymentリソースのマニフェストファイルを作成します。spec以下でReplicaSetリソースの定義を行います。ReplicaSetリソース以下のspec以下でPodの定義を行います。

Pod定義以下のenvで、先程KubernetesのSecretリソースで作成した環境変数の設定を行っています。

resourcesでリソース制限を行っています。デフォルトではCPUとメモリーの制限のみが可能ですが、Device Pluginによりその他GPUなどのリソースも制限可能になります。

  • deployment-mywebiste.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: deployment-mywebsite
spec:
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxUnavailable: 0
      maxSurge: 3
  replicas: 3
  selector:
    matchLabels:
      app: mywebsite
  template:
    metadata:
      labels:
        app: mywebsite
    spec:
      containers:
        - name: mywebsite-container
          image: xxxxxxxxxxxx.dkr.ecr.us-west-2.amazonaws.com/mywebsite:v1
          ports:
            - containerPort: 80
          env:
          - name: DBHOST
            valueFrom:
              secretKeyRef:
                name: mywebsite-secret
                key: dbhost
          - name: DBUSER
            valueFrom:
              secretKeyRef:
                name: mywebsite-secret
                key: dbuser
          - name: DBPASSWORD
            valueFrom:
              secretKeyRef:
                name: mywebsite-secret
                key: dbpassword
          - name: CACHEHOST
            valueFrom:
              secretKeyRef:
                name: mywebsite-secret
                key: cachehost
          resources:
            requests:
              memory: 512Mi
              cpu: 250m
            limits:
              memory: 4096Mi
              cpu: 1000m

kubectl applyコマンドで適用します。

$ kubectl apply -f deployment-mywebsite.yaml 
deployment.apps/deployment-mywebsite created

以下のように作成したDeploymentリソースの詳細を確認できます。

$ kubectl get deployment
NAME                   READY   UP-TO-DATE   AVAILABLE   AGE
deployment-mywebsite   3/3     3            3           79m

kubectl describe deployment.apps/<Deployment name>コマンドで詳細を確認できます。

$ kubectl describe deployment.apps/deployment-mywebsite 
Name:                   deployment-mywebsite
Namespace:              default
CreationTimestamp:      Wed, 06 May 2020 23:44:45 +0900
Labels:                 <none>
Annotations:            deployment.kubernetes.io/revision: 1
Selector:               app=mywebsite
Replicas:               3 desired | 3 updated | 3 total | 3 available | 0 unavailable
StrategyType:           RollingUpdate
MinReadySeconds:        0
RollingUpdateStrategy:  0 max unavailable, 3 max surge
Pod Template:
  Labels:  app=mywebsite
  Containers:
   mywebsite-container:
    Image:      xxxxxxxxxxxx.dkr.ecr.us-west-2.amazonaws.com/mywebsite:v1
    Port:       80/TCP
    Host Port:  0/TCP
    Limits:
      cpu:     1
      memory:  4Gi
    Requests:
      cpu:     250m
      memory:  512Mi
    Environment:
      DBHOST:      <set to the key 'dbhost' in secret 'mywebsite-secret'>      Optional: false
      DBUSER:      <set to the key 'dbuser' in secret 'mywebsite-secret'>      Optional: false
      DBPASSWORD:  <set to the key 'dbpassword' in secret 'mywebsite-secret'>  Optional: false
      CACHEHOST:   <set to the key 'cachehost' in secret 'mywebsite-secret'>   Optional: false
    Mounts:        <none>
  Volumes:         <none>
Conditions:
  Type           Status  Reason
  ----           ------  ------
  Available      True    MinimumReplicasAvailable
  Progressing    True    NewReplicaSetAvailable
OldReplicaSets:  <none>
NewReplicaSet:   deployment-mywebsite-68b6fbcd95 (3/3 replicas created)
Events:
  Type    Reason             Age   From                   Message
  ----    ------             ----  ----                   -------
  Normal  ScalingReplicaSet  13m   deployment-controller  Scaled up replica set deployment-mywebsite-68b6fbcd95 to 3

起動したPodの詳細を確認できます。

$ kubectl describe pod/deployment-mywebsite-68b6fbcd95-bvjr9
Name:           deployment-mywebsite-68b6fbcd95-bvjr9
Namespace:      default
Priority:       0
Node:           ip-10-0-20-7.us-west-2.compute.internal/10.0.20.7
Start Time:     Wed, 06 May 2020 23:44:45 +0900
Labels:         app=mywebsite
                pod-template-hash=68b6fbcd95
Annotations:    kubernetes.io/psp: eks.privileged
Status:         Running
IP:             10.0.20.11
IPs:            <none>
Controlled By:  ReplicaSet/deployment-mywebsite-68b6fbcd95
Containers:
  mywebsite-container:
    Container ID:   docker://9ca22af04a231902b5e5e20377a3669aec9e6e617288e31fe99eca2ce032c648
    Image:          xxxxxxxxxxxx.dkr.ecr.us-west-2.amazonaws.com/mywebsite:v1
    Image ID:       docker-pullable://xxxxxxxxxxxx.dkr.ecr.us-west-2.amazonaws.com/mywebsite@sha256:54fbaaab81098aa99456fb4d4bedfc468f90d4398eabeec55ae1e86bdc01cd71
    Port:           80/TCP
    Host Port:      0/TCP
    State:          Running
      Started:      Wed, 06 May 2020 23:44:48 +0900
    Ready:          True
    Restart Count:  0
    Limits:
      cpu:     1
      memory:  4Gi
    Requests:
      cpu:     250m
      memory:  512Mi
    Environment:
      DBHOST:      <set to the key 'dbhost' in secret 'mywebsite-secret'>      Optional: false
      DBUSER:      <set to the key 'dbuser' in secret 'mywebsite-secret'>      Optional: false
      DBPASSWORD:  <set to the key 'dbpassword' in secret 'mywebsite-secret'>  Optional: false
      CACHEHOST:   <set to the key 'cachehost' in secret 'mywebsite-secret'>   Optional: false
    Mounts:
      /var/run/secrets/kubernetes.io/serviceaccount from default-token-5cr55 (ro)
Conditions:
  Type              Status
  Initialized       True 
  Ready             True 
  ContainersReady   True 
  PodScheduled      True 
Volumes:
  default-token-5cr55:
    Type:        Secret (a volume populated by a Secret)
    SecretName:  default-token-5cr55
    Optional:    false
QoS Class:       Burstable
Node-Selectors:  <none>
Tolerations:     node.kubernetes.io/not-ready:NoExecute for 300s
                 node.kubernetes.io/unreachable:NoExecute for 300s
Events:
  Type    Reason     Age   From                                              Message
  ----    ------     ----  ----                                              -------
  Normal  Scheduled  13m   default-scheduler                                 Successfully assigned default/deployment-mywebsite-68b6fbcd95-bvjr9 to ip-10-0-20-7.us-west-2.compute.internal
  Normal  Pulling    13m   kubelet, ip-10-0-20-7.us-west-2.compute.internal  Pulling image "xxxxxxxxxxxx.dkr.ecr.us-west-2.amazonaws.com/mywebsite:v1"
  Normal  Pulled     13m   kubelet, ip-10-0-20-7.us-west-2.compute.internal  Successfully pulled image "xxxxxxxxxxxx.dkr.ecr.us-west-2.amazonaws.com/mywebsite:v1"
  Normal  Created    13m   kubelet, ip-10-0-20-7.us-west-2.compute.internal  Created container mywebsite-container
  Normal  Started    13m   kubelet, ip-10-0-20-7.us-west-2.compute.internal  Started container mywebsite-container

作成したリソースの種類全体は以下のように確認できます。-Aオプションでnamespace全体を表示、-o wideオプションでより詳細に情報を表示します。

$ kubectl get all -A -o wide
NAMESPACE     NAME                                        READY   STATUS    RESTARTS   AGE   IP            NODE                                        NOMINATED NODE   READINESS GATES
default       pod/deployment-mywebsite-68b6fbcd95-bvjr9   1/1     Running   0          11h   10.0.20.11    ip-10-0-20-7.us-west-2.compute.internal     <none>           <none>
default       pod/deployment-mywebsite-68b6fbcd95-dbp8q   1/1     Running   0          11h   10.0.20.153   ip-10-0-20-227.us-west-2.compute.internal   <none>           <none>
default       pod/deployment-mywebsite-68b6fbcd95-fkjp8   1/1     Running   0          11h   10.0.0.234    ip-10-0-0-246.us-west-2.compute.internal    <none>           <none>
kube-system   pod/aws-node-28mxf                          1/1     Running   0          13h   10.0.0.246    ip-10-0-0-246.us-west-2.compute.internal    <none>           <none>
kube-system   pod/aws-node-55jpq                          1/1     Running   0          13h   10.0.20.7     ip-10-0-20-7.us-west-2.compute.internal     <none>           <none>
kube-system   pod/aws-node-94qdr                          1/1     Running   0          13h   10.0.20.227   ip-10-0-20-227.us-west-2.compute.internal   <none>           <none>
kube-system   pod/coredns-86d5cbb4bd-nzx5h                1/1     Running   0          13h   10.0.20.133   ip-10-0-20-7.us-west-2.compute.internal     <none>           <none>
kube-system   pod/coredns-86d5cbb4bd-xzkpk                1/1     Running   0          13h   10.0.20.159   ip-10-0-20-227.us-west-2.compute.internal   <none>           <none>
kube-system   pod/kube-proxy-jdsms                        1/1     Running   0          13h   10.0.0.246    ip-10-0-0-246.us-west-2.compute.internal    <none>           <none>
kube-system   pod/kube-proxy-qj4vq                        1/1     Running   0          13h   10.0.20.7     ip-10-0-20-7.us-west-2.compute.internal     <none>           <none>
kube-system   pod/kube-proxy-qqtxg                        1/1     Running   0          13h   10.0.20.227   ip-10-0-20-227.us-west-2.compute.internal   <none>           <none>

NAMESPACE     NAME                        TYPE           CLUSTER-IP       EXTERNAL-IP                                                                     PORT(S)                      AGE     SELECTOR
default       service/kubernetes          ClusterIP      172.20.0.1       <none>                                                                          443/TCP                      4d16h   <none>
default       service/mywebsite-service   LoadBalancer   172.20.203.140   a25f46cd53643416d9bb376462b33f80-02aa51afd469eb5d.elb.us-west-2.amazonaws.com   80:30759/TCP,443:31461/TCP   4d15h   app=mywebsite
kube-system   service/kube-dns            ClusterIP      172.20.0.10      <none>                                                                          53/UDP,53/TCP                4d16h   k8s-app=kube-dns

NAMESPACE     NAME                        DESIRED   CURRENT   READY   UP-TO-DATE   AVAILABLE   NODE SELECTOR   AGE     CONTAINERS   IMAGES                                                                 SELECTOR
kube-system   daemonset.apps/aws-node     3         3         3       3            3           <none>          4d16h   aws-node     602401143452.dkr.ecr.us-west-2.amazonaws.com/amazon-k8s-cni:v1.5.7     k8s-app=aws-node
kube-system   daemonset.apps/kube-proxy   3         3         3       3            3           <none>          4d16h   kube-proxy   602401143452.dkr.ecr.us-west-2.amazonaws.com/eks/kube-proxy:v1.15.11   k8s-app=kube-proxy

NAMESPACE     NAME                                   READY   UP-TO-DATE   AVAILABLE   AGE     CONTAINERS            IMAGES                                                            SELECTOR
default       deployment.apps/deployment-mywebsite   3/3     3            3           11h     mywebsite-container   xxxxxxxxxxxx.dkr.ecr.us-west-2.amazonaws.com/mywebsite:v1         app=mywebsite
kube-system   deployment.apps/coredns                2/2     2            2           4d16h   coredns               602401143452.dkr.ecr.us-west-2.amazonaws.com/eks/coredns:v1.6.6   eks.amazonaws.com/component=coredns,k8s-app=kube-dns

NAMESPACE     NAME                                              DESIRED   CURRENT   READY   AGE     CONTAINERS            IMAGES                                                            SELECTOR
default       replicaset.apps/deployment-mywebsite-68b6fbcd95   3         3         3       11h     mywebsite-container   xxxxxxxxxxxx.dkr.ecr.us-west-2.amazonaws.com/mywebsite:v1         app=mywebsite,pod-template-hash=68b6fbcd95
kube-system   replicaset.apps/coredns-86d5cbb4bd                2         2         2       4d16h   coredns               602401143452.dkr.ecr.us-west-2.amazonaws.com/eks/coredns:v1.6.6   eks.amazonaws.com/component=coredns,k8s-app=kube-dns,pod-template-hash=86d5cbb4bd

Error

[✖] AWS::EKS::Nodegroup/ManagedNodeGroup: CREATE_FAILED – "Nodegroup mywebsite-ng failed to stabilize: Internal Failure"

eksctl create clusterコマンドで上記のエラー。このCloudFormationからのエラーはサブネットのルーティングの設定が意図したものになっていなかった。

ModuleNotFoundError: No module named 'pip._internal.cli.main'

pipコマンドで何打っても上記エラーになる。pipの再インストールで解消。

$ curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py
$ python3 get-pip.py --force-reinstall

aws: error: argument operation: Invalid choice, valid choices are:

ECRで例示されたプッシュコマンドを打つと以下のエラー

$ aws ecr get-login-password --region us-west-2 | docker login --username AWS --password-stdin xxxxxxxxxxxx.dkr.ecr.us-west-2.amazonaws.com/mywebsite
usage: aws [options] <command> <subcommand> [<subcommand> ...] [parameters]
To see help text, you can run:

  aws help
  aws <command> help
  aws <command> <subcommand> help
aws: error: argument operation: Invalid choice, valid choices are:

AWS CLI 1.7.10およびv2で使用できる

  • https://docs.aws.amazon.com/cli/latest/userguide/cliv2-migration.html#cliv2-migration-ecr-get-login

The aws ecr get-login-password command is available in the AWS CLI version 1.17.10 and later, and the AWS CLI version 2.

v2をインストール

$ curl "https://awscli.amazonaws.com/AWSCLIV2.pkg" -o "AWSCLIV2.pkg"
$ sudo installer -pkg AWSCLIV2.pkg -target /

インストールが完了したことを確認

$ aws --version                               
aws-cli/2.0.10 Python/3.7.4 Darwin/18.7.0 botocore/2.0.0dev14

Installing the AWS CLI version 2 on macOS

Error syncing load balancer: failed to ensure load balancer: could not find any suitable subnets for creating the ELB

kubectl applyコマンドでNLB作成後、kubectl describe serviceコマンドでイベントを確認すると上記エラー

$ kubectl describe service mywebsite-service
Name:                     mywebsite-service
Namespace:                default
Labels:                   <none>
Annotations:              service.beta.kubernetes.io/aws-load-balancer-backend-protocol: http
                          service.beta.kubernetes.io/aws-load-balancer-ssl-cert: arn:aws:acm:us-west-2:xxxxxxxxxxxx:certificate/c97dad88-731f-4cd0-939c-1565e7db1c6d
                          service.beta.kubernetes.io/aws-load-balancer-ssl-ports: https
                          service.beta.kubernetes.io/aws-load-balancer-type: nlb
Selector:                 app=mywebsite
Type:                     LoadBalancer
IP:                       172.20.246.65
Port:                     http  80/TCP
TargetPort:               80/TCP
NodePort:                 http  30251/TCP
Endpoints:                10.0.0.163:80,10.0.20.48:80,10.0.20.80:80
Port:                     https  443/TCP
TargetPort:               80/TCP
NodePort:                 https  30417/TCP
Endpoints:                10.0.0.163:80,10.0.20.48:80,10.0.20.80:80
Session Affinity:         None
External Traffic Policy:  Cluster
Events:
  Type     Reason                  Age                  From                Message
  ----     ------                  ----                 ----                -------
  Normal   EnsuringLoadBalancer    4m52s (x9 over 20m)  service-controller  Ensuring load balancer
  Warning  SyncLoadBalancerFailed  4m52s (x9 over 20m)  service-controller  Error syncing load balancer: failed to ensure load balancer: could not find any suitable subnets for creating the ELB

ドキュメントに従って、NLB用のパブリックサブネットを作成し、以下のタグを付与

  • kubernetes.io/cluster/mywebsite-cluster shared
  • kubernetes.io/role/elb 1

解消される

$ kubectl describe service mywebsite-service                                              
Name:                     mywebsite-service
Namespace:                default
Labels:                   <none>
Annotations:              service.beta.kubernetes.io/aws-load-balancer-backend-protocol: http
                          service.beta.kubernetes.io/aws-load-balancer-ssl-cert: arn:aws:acm:us-west-2:xxxxxxxxxxxx:certificate/c97dad88-731f-4cd0-939c-1565e7db1c6d
                          service.beta.kubernetes.io/aws-load-balancer-ssl-ports: https
                          service.beta.kubernetes.io/aws-load-balancer-type: nlb
Selector:                 app=mywebsite
Type:                     LoadBalancer
IP:                       172.20.203.140
LoadBalancer Ingress:     a25f46cd53643416d9bb376462b33f80-02aa51afd469eb5d.elb.us-west-2.amazonaws.com
Port:                     http  80/TCP
TargetPort:               80/TCP
NodePort:                 http  30759/TCP
Endpoints:                10.0.0.163:80,10.0.20.48:80,10.0.20.80:80
Port:                     https  443/TCP
TargetPort:               80/TCP
NodePort:                 https  31461/TCP
Endpoints:                10.0.0.163:80,10.0.20.48:80,10.0.20.80:80
Session Affinity:         None
External Traffic Policy:  Cluster
Events:
  Type     Reason                  Age                  From                Message
  ----     ------                  ----                 ----                -------
  Warning  SyncLoadBalancerFailed  15m                  service-controller  Error syncing load balancer: failed to ensure load balancer: Error creating listener: "Error creating load balancer listener: \"CertificateNotFound: Certificate 'arn:aws:acm:us-west-2:xxxxxxxxxxxx:certificate/c97dad88-731f-4cd0-939c-1565e7db1c6d' not found\\n\\tstatus code: 400, request id: 6127c721-d11b-4108-b7aa-74c3590ebb2d\""
  Warning  SyncLoadBalancerFailed  14m                  service-controller  Error syncing load balancer: failed to ensure load balancer: Error creating load balancer listener: "CertificateNotFound: Certificate 'arn:aws:acm:us-west-2:xxxxxxxxxxxx:certificate/c97dad88-731f-4cd0-939c-1565e7db1c6d' not found\n\tstatus code: 400, request id: 5f977567-1152-4cb3-8cb7-b37a915894e3"
  Warning  SyncLoadBalancerFailed  14m                  service-controller  Error syncing load balancer: failed to ensure load balancer: Error creating load balancer listener: "CertificateNotFound: Certificate 'arn:aws:acm:us-west-2:xxxxxxxxxxxx:certificate/c97dad88-731f-4cd0-939c-1565e7db1c6d' not found\n\tstatus code: 400, request id: dab8610c-de7e-451e-8e1a-97ff40c4df0a"
  Warning  SyncLoadBalancerFailed  14m                  service-controller  Error syncing load balancer: failed to ensure load balancer: Error creating load balancer listener: "CertificateNotFound: Certificate 'arn:aws:acm:us-west-2:xxxxxxxxxxxx:certificate/c97dad88-731f-4cd0-939c-1565e7db1c6d' not found\n\tstatus code: 400, request id: e3f49179-a9a2-4f83-9046-1495e97d4e6a"
  Warning  SyncLoadBalancerFailed  13m                  service-controller  Error syncing load balancer: failed to ensure load balancer: Error creating load balancer listener: "CertificateNotFound: Certificate 'arn:aws:acm:us-west-2:xxxxxxxxxxxx:certificate/c97dad88-731f-4cd0-939c-1565e7db1c6d' not found\n\tstatus code: 400, request id: a6c1e5ea-e8a3-49ba-a3ef-b082cc7dc6a2"
  Warning  SyncLoadBalancerFailed  12m                  service-controller  Error syncing load balancer: failed to ensure load balancer: Error creating load balancer listener: "CertificateNotFound: Certificate 'arn:aws:acm:us-west-2:xxxxxxxxxxxx:certificate/c97dad88-731f-4cd0-939c-1565e7db1c6d' not found\n\tstatus code: 400, request id: 19785156-a185-4cbe-96e9-e3bda36f6686"
  Warning  SyncLoadBalancerFailed  9m42s                service-controller  Error syncing load balancer: failed to ensure load balancer: Error creating load balancer listener: "CertificateNotFound: Certificate 'arn:aws:acm:us-west-2:xxxxxxxxxxxx:certificate/c97dad88-731f-4cd0-939c-1565e7db1c6d' not found\n\tstatus code: 400, request id: b0d6355c-cd9f-47fc-89d9-92f26f40995c"
  Normal   EnsuringLoadBalancer    4m42s (x8 over 15m)  service-controller  Ensuring load balancer
  Normal   EnsuredLoadBalancer     4m40s                service-controller  Ensured load balancer

The connection to the server kubernetes.docker.internal:6443 was refused - did you specify the right host or port?

Docker for Macで稼働しているローカル環境のKubernetesのクラスターに対してkubectlコマンドを実行すると上記エラー。Kubernetesクラスターを起動して解消

<!--

rpc error: code = Unknown desc = Error response from daemon: pull access denied for mywebsite, repository does not exist or may require 'docker login': denied: requested access to the resource is denied

$ docker login
Authenticating with existing credentials...
Login Succeeded

-->

E: Unable to locate package telnet

Dockerイメージからコンテナを稼働後、シェル環境にアクセスしてもtelnetをインストールできない。

root@0e5b508c18f6:/usr/local/tomcat# apt-get install telnet
Reading package lists... Done
Building dependency tree       
Reading state information... Done
E: Unable to locate package telnet

apt-get updateコマンドを一度実行すれば良い

Request processing failed; nested exception is org.springframework.data.redis.RedisConnectionFailureException: Cannot get Jedis connection; nested exception is redis.clients.jedis.exceptions.JedisConnectionException: Could not get a resource from the pool

$ ip addr show en0
en0: flags=8863<UP,BROADCAST,SMART,RUNNING,SIMPLEX,MULTICAST> mtu 1500
	ether 18:65:90:cd:c3:ab
	inet 192.168.11.6/24 brd 192.168.11.255 en0
$ sudo ip address add 192.168.11.6/24 dev lo0 
Password:
Executing: /usr/bin/sudo /sbin/ifconfig lo0 add 192.168.11.6/24 

$ ip addr show lo0 
lo0: flags=8049<UP,LOOPBACK,RUNNING,MULTICAST> mtu 16384
	inet 127.0.0.1/8 lo0
	inet6 ::1/128
	inet6 fe80::1/64 scopeid 0x1
	inet 192.168.11.6/24 lo0

Springの接続先を上のIPアドレスに置き換え

ERROR 2003 (HY000): Can't connect to MySQL server on '192.168.11.6' (61)

  1. bind-addressによる制限を解除
$ mysqld --help --verbose | grep my.cnf
/etc/my.cnf /etc/mysql/my.cnf /usr/local/etc/my.cnf ~/.my.cnf 
                      my.cnf, $MYSQL_TCP_PORT, /etc/services, built-in default

/usr/local/etc/my.cnf

# Default Homebrew MySQL server config
[mysqld]
# Only allow connections from localhost
# bind-address = 127.0.0.1
  1. 権限テーブルでリモートからのアクセスを許可
mysql> create user 'root'@'%' identified by 'Test1234';
Query OK, 0 rows affected (0.01 sec)

mysql> select user , host from mysql.user;
+------------------+-----------+
| user             | host      |
+------------------+-----------+
| root             | %         |
| mysql.infoschema | localhost |
| mysql.session    | localhost |
| mysql.sys        | localhost |
| root             | localhost |
+------------------+-----------+
5 rows in set (0.00 sec)

mysql> GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' WITH GRANT OPTION;
Query OK, 0 rows affected (0.00 sec)

mysql> drop user root@localhost;
Query OK, 0 rows affected (0.01 sec)

mysql> select user , host from mysql.user;
+------------------+-----------+
| user             | host      |
+------------------+-----------+
| root             | %         |
| mysql.infoschema | localhost |
| mysql.session    | localhost |
| mysql.sys        | localhost |
+------------------+-----------+
4 rows in set (0.00 sec)
$ mysql -h 192.168.11.6 -uroot -p
Enter password: 
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 9
Server version: 8.0.19 Homebrew

Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> 

RDSだと以下のようになっているのでこちらの作業は不要

mysql> select user , host from mysql.user;
+------------------+-----------+
| user             | host      |
+------------------+-----------+
| hayashier        | %         |
| mysql.infoschema | localhost |
| mysql.session    | localhost |
| mysql.sys        | localhost |
| rdsadmin         | localhost |
+------------------+-----------+
5 rows in set (0.00 sec)

権限テーブルの設定ミスでログインできなくなった場合

以下のようなエラーでログインできなくなった場合

$ mysql -uroot -p    
Enter password: 
ERROR 1045 (28000): Access denied for user 'root'@'localhost' (using password: YES)

以下のコマンドを起動

$ mysqld_safe --skip-grant-tables
2020-05-01T01:13:38.6NZ mysqld_safe Logging to '/usr/local/var/mysql/186590cdc3ab.ant.amazon.com.err'.
2020-05-01T01:13:38.6NZ mysqld_safe Starting mysqld daemon with databases from /usr/local/var/mysql

--skip-grant-tablesオプションによる起動中は、以下のようなコマンドが使用できなくなる

mysql> CREATE USER 'root'@'localhost' IDENTIFIED BY 'Test1234';
ERROR 1290 (HY000): The MySQL server is running with the --skip-grant-tables option so it cannot execute this statement

以下の方法で回避して、権限テーブルを設定

mysql> CREATE TEMPORARY TABLE mysql.tmpUser SELECT * FROM mysql.user WHERE host="%" and user="root";
mysql> UPDATE mysql.tmpUser SET host="localhost" WHERE user = "root";
mysql> INSERT INTO mysql.user SELECT * FROM mysql.tmpUser;
mysql> drop user root@'%';

(参照) How to get all privileges back to the root user in MySQL?

My Twitter & RSS

Leave a Reply

Your email address will not be published. Required fields are marked *