FargateのvCPUを理解する

タスクサイズ -> タスクメモリ(MiB)
タスクサイズ -> タスクCPU(単位)
タスクサイズ -> コンテナの定義 -> メモリ制限 -> ハード制限
タスクサイズ -> コンテナの定義 -> メモリ制限 -> ソフト制限
タスクサイズ -> コンテナの定義 -> CPUユニット数

### ハード制限(Memory)
コンテナに適用されるハードウェアのメモリ制限。上限を超えると強制的に終了となる。
ウェブアプリケーションだと300~500MBがおすすめとのこと

### ソフト制限(memoryReservation)
システムメモリ競合時に維持されるメモリ上限 
状況に応じてハード制限(設定されている場合)かインスタンス自体の利用可能上限まで消費
memory (<= memoryReservation) <= instance memory Fargateの動作ベース https://docs.aws.amazon.com/ja_jp/AmazonECS/latest/developerguide/task-cpu-memory-error.html サポートされる CPU 値は、128CPU 単位 (0.125vCPUs) と10240CPU 単位 (10vCPUs)の間 ### CPU value 256(.25vCPU): 512, 1024, 2048 512(.5vCPU): 1024, 2048, 3072, 4096 1024(1vCPU): 2048, 3072, 4096, 5120, 6144, 7168 2048(2vCPU): Between 4096 and 16384 in increments of 1024 4096(4vCPU): Between 8192 and 30720 in increments of 1024 うーむ、何をベースにvCPU, Memoryを決めたら良いかイマイチわからんな。。

[AWS CloudFormation] ECS + Fargate

AWSTemplateFormatVersion: "2010-09-09"
Description:
  Fargate for ECS Create

Metadata:
  "AWS::CloudFormation::Interface":
    ParameterGroups:
      - Label:
          default: "Project Name Prefix"
        Parameters:
          - PJPrefix
      - Label:
          default: "InternetALB Configuration"
        Parameters:
          - InternetALBName
          - TargetGroupName
      - Label:
          default: "Fargate for ECS Configuration"
        Parameters:
          - ECSClusterName
          - ECSTaskName
          - ECSTaskCPUUnit
          - ECSTaskMemory
          - ECSContainerName
          - ECSImageName
          - ECSServiceName
          - ECSTaskDesiredCount

    ParameterLabels:
      IneternetALBName:
        default: "InternetALBName"
      TargetGroupName:
        default: "TargetGroupName"
      ECSClusterName:
        default: "ECSClusterName"
      ECSTaskName:
        default: "ECSTaskName"
      ECSTaskCPUUnit:
        default: "ECSTaskCPUUnit"
      ECSTaskMemory:
        default: "ECSTaskMemory"
      ECSContainerName:
        default: "ECSContainerName"
      ECSImageName:
        default: "ECSImageName"
      ECSServiceName:
        default: "ECSServiceName"
      ECSTaskDesiredCount:
        default: "ECSTaskDesiredCount"

# ------------------------------------------------------------#
# Input Parameters
# ------------------------------------------------------------# 
Parameters:
  PJPrefix:
    Type: String

#InternetALB
  InternetALBName:
    Type: String
    Default: "alb"

#TargetGroupName
  TargetGroupName:
    Type: String
    Default: "tg"

#ECSClusterName
  ECSClusterName:
    Type: String
    Default: "cluster"

#ECSTaskName
  ECSTaskName:
    Type: String
    Default: "task"

#ECSTaskCPUUnit
  ECSTaskCPUUnit:
    AllowedValues: [256, 512, 1024, 2048, 4096]
    Type: String
    Default: "256"

#ECSTaskMemory
  ECSTaskMemory:
    AllowedValues: [ 256, 512, 1024, 2048, 4096 ]
    Type: String
    Default: "512"

#ECSContainerName
  ECSContainerName:
    Type: String
    Default: "container"

#ECSImageName
  ECSImageName:
    Type: String
    Default: ""

#ECSServiceName
  ECSServiceName:
    Type: String
    Default: "service"

#ECSTaskDesiredCount
  ECSTaskDesiredCount:
    Type: Number
    Default: 1

Resources:
# ------------------------------------------------------------#
#  SecurityGroup for ALB
# ------------------------------------------------------------#
  ALBSecurityGroup:
    Type: "AWS::EC2::SecurityGroup"
    Properties:
      VpcId: { "Fn::ImportValue": !Sub "${PJPrefix}-vpc" }
      GroupName: !Sub "${PJPrefix}-alb-sg"
      GroupDescription: "-"
      Tags:
        - Key: "Name"
          Value: !Sub "${PJPrefix}-alb-sg"

# Rule
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 80
          ToPort: 80
          CidrIp: "0.0.0.0/0"

        - IpProtocol: tcp
          FromPort: 443
          ToPort: 443
          CidrIp: "0.0.0.0/0"

# ------------------------------------------------------------#
#  SecurityGroup for ECS Service
# ------------------------------------------------------------#
  ECSSecurityGroup:
    Type: "AWS::EC2::SecurityGroup"
    Properties:
      VpcId: { "Fn::ImportValue": !Sub "${PJPrefix}-vpc" }
      GroupName: !Sub "${PJPrefix}-ecs-sg"
      GroupDescription: "-"
      Tags:
        - Key: "Name"
          Value: !Sub "${PJPrefix}-ecs-sg"

# Rule
  ECSSecurityGroupIngress:
    Type: "AWS::EC2::SecurityGroupIngress"
    Properties:
      IpProtocol: tcp
      FromPort: 80
      ToPort: 80
      SourceSecurityGroupId: !GetAtt [ ALBSecurityGroup, GroupId ]
      GroupId: !GetAtt [ ECSSecurityGroup, GroupId ]

# ------------------------------------------------------------#
#  Target Group
# ------------------------------------------------------------#
  TargetGroup:
    Type: "AWS::ElasticLoadBalancingV2::TargetGroup"
    Properties:
      VpcId: { "Fn::ImportValue": !Sub "${PJPrefix}-vpc"}
      Name: !Sub "${PJPrefix}-${TargetGroupName}"
      Protocol: HTTP
      Port: 80
      TargetType: ip


# ------------------------------------------------------------#
#  Internet ALB
# ------------------------------------------------------------#
  InternetALB:
    Type: "AWS::ElasticLoadBalancingV2::LoadBalancer"
    Properties:
      Name: !Sub "${PJPrefix}-${InternetALBName}"
      Tags:
        - Key: Name
          Value: !Sub "${PJPrefix}-${InternetALBName}"
      Scheme: "internet-facing"
      LoadBalancerAttributes:
        - Key: "deletion_protection.enabled"
          Value: false
        - Key: "idle_timeout.timeout_seconds"
          Value: 60
      SecurityGroups:
        - !Ref ALBSecurityGroup
      Subnets:
        - { "Fn::ImportValue": !Sub "${PJPrefix}-public-subnet-a" }
        - { "Fn::ImportValue": !Sub "${PJPrefix}-public-subnet-c" }

  ALBListener:
    Type: "AWS::ElasticLoadBalancingV2::Listener"
    Properties:
      DefaultActions:
        - TargetGroupArn: !Ref TargetGroup
          Type: forward
      LoadBalancerArn: !Ref InternetALB
      Port: 80
      Protocol: HTTP

# ------------------------------------------------------------#
# ECS Cluster
# ------------------------------------------------------------# 
  ECSCluster:
    Type: "AWS::ECS::Cluster"
    Properties:
      ClusterName: !Sub "${PJPrefix}-${ECSClusterName}"

# ------------------------------------------------------------#
#  ECS LogGroup
# ------------------------------------------------------------#
  ECSLogGroup:
    Type: "AWS::Logs::LogGroup"
    Properties:
      LogGroupName: !Sub "/ecs/logs/${PJPrefix}-ecs-group"

# ------------------------------------------------------------#
#  ECS TaskDefinition
# ------------------------------------------------------------#
  ECSTaskDefinition:
    Type: "AWS::ECS::TaskDefinition"
    Properties:
      Cpu: !Ref ECSTaskCPUUnit
      ExecutionRoleArn: !Sub "arn:aws:iam::${AWS::AccountId}:role/ecsTaskExecutionRole"
      Family: !Sub "${PJPrefix}-${ECSTaskName}"
      Memory: !Ref ECSTaskMemory
      NetworkMode: awsvpc
      RequiresCompatibilities:
        - FARGATE

#ContainerDefinitions
      ContainerDefinitions:
        - Name: !Sub "${PJPrefix}-${ECSContainerName}"
          Image: !Ref ECSImageName
          LogConfiguration:
            LogDriver: awslogs
            Options:
              awslogs-group: !Ref ECSLogGroup
              awslogs-region: !Ref "AWS::Region"
              awslogs-stream-prefix: !Ref PJPrefix
          MemoryReservation: 128
          PortMappings:
            - HostPort: 80
              Protocol: tcp
              ContainerPort: 80

# ------------------------------------------------------------#
#  ECS Service
# ------------------------------------------------------------#
  ECSService:
    Type: AWS::ECS::Service
    DependsOn: ALBListener
    Properties:
      Cluster: !Ref ECSCluster
      DesiredCount: !Ref ECSTaskDesiredCount
      LaunchType: FARGATE
      LoadBalancers:
        -
          TargetGroupArn: !Ref TargetGroup
          ContainerPort: 80
          ContainerName: !Sub "${PJPrefix}-${ECSContainerName}"
      NetworkConfiguration:
       AwsvpcConfiguration:
           AssignPublicIp: ENABLED
           SecurityGroups:
             - !Ref ECSSecurityGroup
           Subnets:
             - { "Fn::ImportValue": !Sub "${PJPrefix}-public-subnet-a" }
             - { "Fn::ImportValue": !Sub "${PJPrefix}-public-subnet-c" }
      ServiceName: !Sub "${PJPrefix}-${ECSServiceName}"
      TaskDefinition: !Ref ECSTaskDefinition

# ------------------------------------------------------------#
# Output Parameters
# ------------------------------------------------------------#
Outputs:
#InternetALB
  ALBDNSName:
    Value: !GetAtt InternetALB.DNSName
    Export:
      Name: !Sub "${PJPrefix}-${InternetALBName}-dnsname"

#ECSClusterName
  ECSClusterName:
    Value: !Sub "${PJPrefix}-${ECSClusterName}"
    Export:
      Name: !Sub "${PJPrefix}-${ECSClusterName}-name"

#ECSClusterARN
  ECSClusterARN:
    Value: !GetAtt ECSCluster.Arn
    Export:
      Name: !Sub "${PJPrefix}-${ECSClusterName}-arn"

#ECSLogGroup
  ECSLogGroupName:
    Value: !Sub "/ecs/logs/${PJPrefix}-ecs-group"
    Export:
      Name: !Sub "${PJPrefix}-ecs-group-name"

うおおおおおおおおおお
なんかcloudformation理解してきたああああああああああああああ

AWS Fargateとは

Amazon Elastic Container Service (ECS) と Amazon Elastic Kubernetes Service (EKS) で動作するホストマシンを意識せずにコンテナを実行できる環境
AWS Fargateを利用すればコンテナと実行環境の2重管理が不要になるコンテナ向けサーバレスコンピューティング
EC2インスタンス、スケーリング、集合体のクラスターを管理する必要がなくなる

### Fargeteのメリット
– ホストマシンのOS、ミドルウェアの構築が必要ない
– インスタンスタイプやクラスター管理が不要
– AutoScaling
– インフラストラクチャの設計や設定の手間を省く

### Fargateのデメリット
パブリックIPの固定割り当てができない
sshやdocker execが使えない

### コスト
EC2より1~2割高

### 実践
fargateでtask definitionを作成します。

fargateで起動まではできた。VPCやsubnet、Security Groupなどは設定するのでEC2とあまり変わらないような気がするが、EC2のインスタンスタイプなどは設定しないので、そこはよしなにやってくれる

ECSで複数コンテナを動かしたい

まず、ローカルから環境を作ります。
$ tree
.
├── backend
│   └── index.js
├── docker
│   ├── express
│   │   └── Dockerfile
│   └── nuxt
│   └── Dockerfile
├── docker-compose.yml
└── frontend

5 directories, 4 files

backend/index.js

import express from 'express';
const app = express();
app.get('/', (req, res) => res.send('hello-world'));

frontend/pages/index.vue

<template>
	<div class="container">
		{{name}}
	</div>
</template>

<script>
export default {
	data() {
		return {
			name: null
		}
	},
	async asyncData({$axios}) {
		const res = await $axios.$get('hello');
		return {name:res}
	}
}
</script>

docker/express/Dockerfile

FROM node:14.4.0-alpine3.12
WORKDIR /app
COPY ./backend/package*.json ./
RUN npm ci
EXPOSE 8888
CMD ["npm", "run", "dev"]

docker/nuxt/Dockerfile

FROM node:14.4.0-alpine3.12
WORKDIR /app
ENV NUXT_HOST 0.0.0.0
COPY ./frontend/package*.json ./
RUN npm ci
EXPOSE 3000
CMD ["npm", "run", "dev"]

docker-compose.yml

version: "3"
services:
  front:
    tty: true
    build:
      context: .
      dockerfile: ./docker/nuxt/Dockerfile 
    volumes:
      - ./frontend:/app
      - /app/node_modules
    ports:
      - 3000:3000
  backend:
    tty: true
    build:
      context: .
      dockefile: ./docker/express/Dockerfile
    volumes:
      - ./backend:/app
      - /app/node_modules
    ports:
      - 8888:8888

### ECSの構成
タスクとはコンテナがまとまって動く単位
サービスとは複数コンテナの集合体、タスクを並列に動かすことができる

サービスの構成
– フロントエンドサービス
– バックエンドサービス

うーむ、なんかやりたいことと違うような気がするな…

[AWS] ALBを経由してECSを接続する

1. availability zone a, cのpublic subnetを2つ作っておき、target group, albを作成
2. create serviceの際に、application loadbalancerを選択し、作成したloadbalancerを選ぶ

3.作成されたalbのdnsを叩くと、コンテナに接続される

EC2 container instancesで2にすると、albでserviceを作成すると、ec2も2つ起動しているので、冗長化構成でできているように見えるが… どうなんだろうか….

次は複数コンテナをtask definitionで設定する方法だな

[AWS ECS]ローカルのimageの更新からECS更新までの流れ

### image更新してecrにpush
$ sudo docker run –publish=8080:80 hpscript_fixed
$ sudo docker cp src/index.html a59fc6eee21a:/var/www/html/index.html
$ sudo docker stop a59fc6eee21a
$ sudo docker ps -a
$ sudo docker commit serene_lamport hpscript_v3

$ sudo docker images
$ sudo docker tag hpscript_v3:latest ***.dkr.ecr.ap-northeast-1.amazonaws.com/hpscript:v3
$ sudo docker push ***.dkr.ecr.ap-northeast-1.amazonaws.com/hpscript:v3

### Task Definitions
Create new revisionでECRのレポジトリとtagを指定

### Clusterでserviceをupdate
既にあるtaskで80番ポートが使用されているので、既にあるtaskを削除して新しいtaskをrunしないといけない。
どうやってもダウンタイムは発生してしまう。

OK 次はOpenSearchをやろう

[AWS ECS] ECRのimageの使い方

### 前準備
ローカル(vagrant)のdocker imageをaws ECRにpush済の状態とします。

### ECS clusterの登録
cluster作成時にコンテナの登録で、ECRのimage:tagを登録する
***.dkr.ecr.ap-northeast-1.amazonaws.com/hpscript:latest
L container nameは任意の名前で良い

後は、通常のECRの作成の手順
– タスク定義の登録(コンテナの設定)
– タスクの実行環境となるクラスターの作成(EC2, VPC, セキュリティグループなど)
-サービスの作成(タスクとクラスターを紐づける)
EC2が生成される

後は、imageを更新する場合だな

docker imageをaws ecrにpushする手順

1.awsのECRでレポジトリを作成
2.ubuntuにawscliをインストール
$ sudo apt install awscli
$ pip3 install –upgrade awscli

3. AWS IAMでPowerAccessUser権限のユーザを作成し、access key id, secret access keyをコピー

4. cli configure設定
$ aws configure
AWS Access Key ID [None]: ***********
AWS Secret Access Key [None]: ***********
Default region name [None]: ap-northeast-1
Default output format [None]: json
$ cat ~/.aws/credentials

5. login
aws ecr get-login-password –region ap-northeast-1 | sudo docker login –username AWS –password-stdin ******.dkr.ecr.ap-northeast-1.amazonaws.com
Login Succeeded

6. タグ付け
$ sudo docker tag hpscript_test:latest ******.dkr.ecr.ap-northeast-1.amazonaws.com/hpscript:latest

7. push
$ sudo docker push ******.dkr.ecr.ap-northeast-1.amazonaws.com/hpscript:latest

ECR repositoryにpushされています

aws configureの設定をしないと以下のようなエラーが出るので注意
Unable to locate credentials. You can configure credentials by running “aws configure”.

後はECSでdefinitionとclusterを作成してserviceを作れば良いのだな。

ECSの登録方法

1.タスク定義の登録(コンテナの設定)
2.タスクの実行環境となるクラスターの作成(EC2, VPC, セキュリティグループなど)
3.サービスの作成(タスクとクラスターを紐づける)
4.EC2が生成される

コンテナの設定のところを、独自のコンテナで作りたい。
どうやらECRにimageをpushして、そのimageをそのまま使うようだ。

AWS ECRとは

ECSとはElastic Container Registryの略、Dockerのコンテナイメージを保存しておくレジストリ

Edit permission

$ chmod 400 KEYPAIR.pem
$ ssh -i KEYPAIR.pem ec2-user@PublicIP
$ docker pull amazon/amazon-ecs-sample
Using default tag: latest
latest: Pulling from amazon/amazon-ecs-sample
72d97abdfae3: Pull complete
9db40311d082: Pull complete
991f1d4df942: Pull complete
9fd8189a392d: Pull complete
Digest: sha256:36c7b282abd0186e01419f2e58743e1bf635808231049bbc9d77e59e3a8e4914
Status: Downloaded newer image for amazon/amazon-ecs-sample:latest
docker.io/amazon/amazon-ecs-sample:latest
$ REPOSITORY=your-repository-uri
$ docker tag amazon/amazon-ecs-sample:latest $REPOSITORY
$ EC2_REGION=`wget -q -O – http://169.254.169.254/latest/meta-data/placement/availability-zone | sed s/.$//`
$ `aws ecr get-login –region $EC2_REGION –no-include-email`
$ docker push $REPOSITORY

ECS
-> Task definition -> EC2

{
  "family": "ecs-demo",
  "containerDefinitions": [
    {
      "volumesFrom": [],
      "memory": 128,
      "extraHosts": null,
      "dnsServers": null,
      "disableNetworking": null,
      "dnsSearchDomains": null,
      "portMappings": [
        {
          "hostPort": 80,
          "containerPort": 80,
          "protocol": "tcp"
        }
      ],
      "hostname": null,
      "essential": true,
      "entryPoint": null,
      "mountPoints": [],
      "name": "ecs-sample",
      "ulimits": null,
      "dockerSecurityOptions": null,
      "environment": [],
      "links": null,
      "workingDirectory": null,
      "readonlyRootFilesystem": null,
      "image": "YOUR-REPOSITORY-URI:latest",
      "command": null,
      "user": null,
      "dockerLabels": null,
      "logConfiguration": null,
      "cpu": 0,
      "privileged": null
    }
  ],
  "volumes": []
}

ECSCluster

ECRからpushしてimageを作ることが出来る

なるほど、ECSとECRはなんと無くわかったので、後は実践するのみですね。