[AWS CloudFormation] cloudwatchによるautorecovery

EC2インスタンスをCloudWatchでモニタリングし、基になるハードウェア障害またはAWSによる復旧を必要とする問題でインスタンスが正常に機能しなくなった事象を検知すると、CloudWatchが自動的にインスタンスを復旧する

cloudwatch-autorecovery.yml

AWSTemplateFormatVersion: "2010-09-09"
Description:
  Auto Recovery for EC2

# ------------------------------------------------------------#
# Input Parameters
# ------------------------------------------------------------# 
Parameters:
  EC2Instance:
    Type: AWS::EC2::Instance::Id

Resources:
# ------------------------------------------------------------#
#  AutoRecoveryAlarm
# ------------------------------------------------------------# 
  AutoRecoveryAlarm:
    Type: AWS::CloudWatch::Alarm
    Properties:
      AlarmName: !Sub "${EC2Instance}-autorecovery"
      Namespace: AWS/EC2
      MetricName: StatusCheckFailed_System
      Statistic: Minimum
      Period: 60
      EvaluationPeriods: 10
      ComparisonOperator: GreaterThanThreshold
      Threshold: 0
      AlarmActions:
        - !Sub "arn:aws:automate:${AWS::Region}:ec2:recover"
      Dimensions:
        - Name: InstanceId
          Value: !Ref EC2Instance

なるほど、これはいけるね

[AWS CloudFormation] ALB + EC2 2台構成

AWSTemplateFormatVersion: "2010-09-09"
Description:
  ALB and EC2 Instance Create

Metadata:
  "AWS::CloudFormation::Interface":
    ParameterGroups:
      - Label:
          default: "Project Name Prefix"
        Parameters:
          - PJPrefix
      - Label:
          default: "InternetALB Configuration"
        Parameters:
          - InternetALBName
      - Label:
          default: "EC2Instance Configuration"
        Parameters:
          - KeyPairName
          - EC2InstanceName
          - EC2InstanceAMI
          - EC2InstanceInstanceType
          - EC2InstanceVolumeType
          - EC2InstanceVolumeSize
          - SSHAccessSourceIP

    ParameterLabels:
      IneternetALBName:
        default: "InternetALBName"
      KeyPairName:
        default: "KeyPiarName"
      EC2InstanceName:
        default: "EC2 Name"
      EC2InstanceAMI:
        default: "EC2 AMI"
      EC2InstanceInstanceType:
        default: "EC2 InstanceType"
      EC2InstanceVolumeType:
        default: "EC2 VolumeType"
      EC2InstanceVolumeSize:
        default: "EC2 VolumeSize"
      SSHAccessSourceIP:
        default: "SSH AccessSourceIP"

# ------------------------------------------------------------#
# Input Parameters
# ------------------------------------------------------------# 
Parameters:
  PJPrefix:
    Type: String
#InternetALB
  InternetALBName:
    Type: String
    Default: "web"


#EC2Instance
  KeyPairName:
    Type: AWS::EC2::KeyPair::KeyName
    Default: ""
  EC2InstanceName:
    Type: String
    Default: "web"
  EC2InstanceAMI:
    Type: String
    Default: ""
  EC2InstanceInstanceType:
    Type: String
    Default: "t2.micro"
  EC2InstanceVolumeType:
    Type: String
    Default: "gp2"
  EC2InstanceVolumeSize:
    Type: String
    Default: "30"
  SSHAccessSourceIP:
    Type: String

Resources:
# ------------------------------------------------------------#
#  IAM Role for EC2
# ------------------------------------------------------------# 
  EC2IAMRole:
    Type: "AWS::IAM::Role"
    Properties:
      RoleName: !Sub "${PJPrefix}-${EC2InstanceName}-role"
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - "ec2.amazonaws.com"
            Action:
              - "sts:AssumeRole"

      Path: "/"
      ManagedPolicyArns:
        - "arn:aws:iam::aws:policy/service-role/AmazonEC2RoleforSSM"
        - "arn:aws:iam::aws:policy/AmazonEC2ReadOnlyAccess"

  EC2InstanceProfile:
    Type: "AWS::IAM::InstanceProfile"
    Properties:
      Path: "/"
      Roles:
        - Ref: EC2IAMRole
      InstanceProfileName: !Sub "${PJPrefix}-${EC2InstanceName}-profile"

# ------------------------------------------------------------#
#  EC2Instance AZ:A
# ------------------------------------------------------------#
  EC2Instance01:
    Type: "AWS::EC2::Instance"
    Properties:
      Tags:
        - Key: Name
          Value: !Sub "${PJPrefix}-${EC2InstanceName}-01"
      ImageId: !Ref EC2InstanceAMI
      InstanceType: !Ref EC2InstanceInstanceType
      KeyName: !Ref KeyPairName
      IamInstanceProfile: !Ref EC2InstanceProfile
      DisableApiTermination: false
      EbsOptimized: false
      BlockDeviceMappings:
        - DeviceName: /dev/xvda
          Ebs:
            DeleteOnTermination: true
            VolumeType: !Ref EC2InstanceVolumeType
            VolumeSize: !Ref EC2InstanceVolumeSize
      SecurityGroupIds:
        - !Ref ManagedSecurityGroup
        - !Ref WebSecurityGroup
      SubnetId: {"Fn::ImportValue": !Sub "${PJPrefix}-public-subnet-a"}
      UserData: !Base64 | 
        #! /bin/bash
        yum update -y

# ------------------------------------------------------------#
#  EC2Instance AZ:C
# ------------------------------------------------------------#
  EC2Instance02:
    Type: "AWS::EC2::Instance"
    Properties:
      Tags:
        - Key: Name
          Value: !Sub "${PJPrefix}-${EC2InstanceName}-02"
      ImageId: !Ref EC2InstanceAMI
      InstanceType: !Ref EC2InstanceInstanceType
      KeyName: !Ref KeyPairName
      IamInstanceProfile: !Ref EC2InstanceProfile
      DisableApiTermination: false
      EbsOptimized: false
      BlockDeviceMappings:
        - DeviceName: /dev/xvda
          Ebs:
            DeleteOnTermination: true
            VolumeType: !Ref EC2InstanceVolumeType
            VolumeSize: !Ref EC2InstanceVolumeSize
      SecurityGroupIds:
        - !Ref ManagedSecurityGroup
        - !Ref WebSecurityGroup
      SubnetId: {"Fn::ImportValue": !Sub "${PJPrefix}-public-subnet-c"}
      UserData: !Base64 | 
        #! /bin/bash
        yum update -y

# ------------------------------------------------------------#
#  SecurityGroup for Managed
# ------------------------------------------------------------#
  ManagedSecurityGroup:
    Type: "AWS::EC2::SecurityGroup"
    Properties:
      VpcId: {"Fn::ImportValue": !Sub "${PJPrefix}-vpc"}
      GroupName: !Sub "${PJPrefix}-managed-sg"
      GroupDescription: "-"
      Tags:
        - Key: "Name"
          Value: !Sub "${PJPrefix}-managed-sg"

# Rule
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 22
          ToPort: 22
          CidrIp: !Ref SSHAccessSourceIP

# ------------------------------------------------------------#
#  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 Web
# ------------------------------------------------------------#
  WebSecurityGroup:
    Type: "AWS::EC2::SecurityGroup"
    Properties:
      VpcId: { "Fn::ImportValue": !Sub "${PJPrefix}-vpc" }
      GroupName: !Sub "${PJPrefix}-web-sg"
      GroupDescription: "-"
      Tags:
        - Key: "Name"
          Value: !Sub "${PJPrefix}-web-sg"

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

# ------------------------------------------------------------#
#  ElasticIP for EC2Instance01
# ------------------------------------------------------------# 
  ElasticIP01:
    Type: "AWS::EC2::EIP"
    Properties:
      Domain: vpc

  ElasticIPAssociate01:
    Type: AWS::EC2::EIPAssociation
    Properties:
      AllocationId: !GetAtt ElasticIP01.AllocationId
      InstanceId: !Ref EC2Instance01

# ------------------------------------------------------------#
#  ElasticIP for EC2Instance02
# ------------------------------------------------------------# 
  ElasticIP02:
    Type: "AWS::EC2::EIP"
    Properties:
      Domain: vpc

  ElasticIPAssociate02:
    Type: AWS::EC2::EIPAssociation
    Properties:
      AllocationId: !GetAtt ElasticIP02.AllocationId
      InstanceId: !Ref EC2Instance02

# ------------------------------------------------------------#
#  Target Group
# ------------------------------------------------------------#
  TargetGroup:
    Type: "AWS::ElasticLoadBalancingV2::TargetGroup"
    Properties:
      VpcId: { "Fn::ImportValue": !Sub "${PJPrefix}-vpc"}
      Name: !Sub "${PJPrefix}-${InternetALBName}-tg"
      Protocol: HTTP
      Port: 80
      HealthCheckProtocol: HTTP
      HealthCheckPath: "/"
      HealthCheckPort: "traffic-port"
      HealthyThresholdCount: 2
      UnhealthyThresholdCount: 2
      HealthCheckTimeoutSeconds: 5
      HealthCheckIntervalSeconds: 10
      Matcher:
        HttpCode: 200
      Tags:
        - Key: Name
          Value: !Sub "${PJPrefix}-${InternetALBName}-tg"
      TargetGroupAttributes:
        - Key: "deregistration_delay.timeout_seconds"
          Value: 300
        - Key: "stickiness.enabled"
          Value: false
        - Key: "stickiness.type"
          Value: lb_cookie
        - Key: "stickiness.lb_cookie.duration_seconds"
          Value: 86400
      Targets:
        - Id: !Ref EC2Instance01
        - Id: !Ref EC2Instance02
          Port: 80


# ------------------------------------------------------------#
#  Internet ALB
# ------------------------------------------------------------#
  InternetALB:
    Type: "AWS::ElasticLoadBalancingV2::LoadBalancer"
    Properties:
      Name: !Sub "${PJPrefix}-${InternetALBName}-alb"
      Tags:
        - Key: Name
          Value: !Sub "${PJPrefix}-${InternetALBName}-alb"
      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

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

#EC2Instance01
  EC2Instance01ID:
    Value: !Ref EC2Instance01
    Export:
      Name: !Sub "${PJPrefix}-${EC2InstanceName}-01-id"

  EC2Instance01PrivateIp:
    Value: !GetAtt EC2Instance01.PrivateIp
    Export:
      Name: !Sub "${PJPrefix}-${EC2InstanceName}-01-private-ip"

  EC2Instance01EIP:
    Value: !GetAtt EC2Instance01.PublicIp
    Export:
      Name: !Sub "${PJPrefix}-${EC2InstanceName}-01-eip"

  EC2Instance01RoleName:
    Value: !Sub "${PJPrefix}-${EC2InstanceName}-01-role"
    Export:
      Name: !Sub "${PJPrefix}-${EC2InstanceName}-01-role-name"

#EC2Instance02
  EC2Instance02ID:
    Value: !Ref EC2Instance02
    Export:
      Name: !Sub "${PJPrefix}-${EC2InstanceName}-02-id"

  EC2Instance02PrivateIp:
    Value: !GetAtt EC2Instance02.PrivateIp
    Export:
      Name: !Sub "${PJPrefix}-${EC2InstanceName}-02-private-ip"

  EC2Instance02EIP:
    Value: !GetAtt EC2Instance02.PublicIp
    Export:
      Name: !Sub "${PJPrefix}-${EC2InstanceName}-02-eip"

  EC2Instance02RoleName:
    Value: !Sub "${PJPrefix}-${EC2InstanceName}-02-role"
    Export:
      Name: !Sub "${PJPrefix}-${EC2InstanceName}-02-role-name"

うん、ここまでは来る。
問題はこれから。

[AWS CloudFormation] ELB + EC2 2台構成を作る

prefix: 0.0.0.0/0
amiID: ami-0ab0bbbd329f565e6

AWSTemplateFormatVersion: "2010-09-09"
Description:
  ELB and EC2 Instance Create

Metadata:
  "AWS::CloudFormation::Interface":
    ParameterGroups:
      - Label:
          default: "Project Name Prefix"
        Parameters:
          - PJPrefix
      - Label:
          default: "InternetELB Configuration"
        Parameters:
          - InternetELBName
      - Label:
          default: "EC2Instance Configuration"
        Parameters:
          - KeyPairName
          - EC2InstanceName
          - EC2InstanceAMI
          - EC2InstanceInstanceType
          - EC2InstanceVolumeType
          - EC2InstanceVolumeSize
          - SSHAccessSourceIP

    ParameterLabels:
      IneternetELBName:
        default: "InternetELBName"
      KeyPairName:
        default: "KeyPiarName"
      EC2InstanceName:
        default: "EC2 Name"
      EC2InstanceAMI:
        default: "EC2 AMI"
      EC2InstanceInstanceType:
        default: "EC2 InstanceType"
      EC2InstanceVolumeType:
        default: "EC2 VolumeType"
      EC2InstanceVolumeSize:
        default: "EC2 VolumeSize"
      SSHAccessSourceIP:
        default: "SSH AccessSourceIP"

# ------------------------------------------------------------#
# Input Parameters
# ------------------------------------------------------------# 
Parameters:
  PJPrefix:
    Type: String
#InternetELB
  InternetELBName:
    Type: String
    Default: "web"


#EC2Instance
  KeyPairName:
    Type: AWS::EC2::KeyPair::KeyName
    Default: ""
  EC2InstanceName:
    Type: String
    Default: "web"
  EC2InstanceAMI:
    Type: String
    Default: ""
  EC2InstanceInstanceType:
    Type: String
    Default: "t2.micro"
  EC2InstanceVolumeType:
    Type: String
    Default: "gp2"
  EC2InstanceVolumeSize:
    Type: String
    Default: "30"
  SSHAccessSourceIP:
    Type: String

Resources:
# ------------------------------------------------------------#
#  IAM Role for EC2
# ------------------------------------------------------------# 
  EC2IAMRole:
    Type: "AWS::IAM::Role"
    Properties:
      RoleName: !Sub "${PJPrefix}-${EC2InstanceName}-role"
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - "ec2.amazonaws.com"
            Action:
              - "sts:AssumeRole"

      Path: "/"
      ManagedPolicyArns:
        - "arn:aws:iam::aws:policy/service-role/AmazonEC2RoleforSSM"
        - "arn:aws:iam::aws:policy/AmazonEC2ReadOnlyAccess"

  EC2InstanceProfile:
    Type: "AWS::IAM::InstanceProfile"
    Properties:
      Path: "/"
      Roles:
        - Ref: EC2IAMRole
      InstanceProfileName: !Sub "${PJPrefix}-${EC2InstanceName}-profile"

# ------------------------------------------------------------#
#  EC2Instance AZ:A
# ------------------------------------------------------------#
  EC2Instance01:
    Type: "AWS::EC2::Instance"
    Properties:
      Tags:
        - Key: Name
          Value: !Sub "${PJPrefix}-${EC2InstanceName}-01"
      ImageId: !Ref EC2InstanceAMI
      InstanceType: !Ref EC2InstanceInstanceType
      KeyName: !Ref KeyPairName
      IamInstanceProfile: !Ref EC2InstanceProfile
      DisableApiTermination: false
      EbsOptimized: false
      BlockDeviceMappings:
        - DeviceName: /dev/xvda
          Ebs:
            DeleteOnTermination: true
            VolumeType: !Ref EC2InstanceVolumeType
            VolumeSize: !Ref EC2InstanceVolumeSize
      SecurityGroupIds:
        - !Ref ManagedSecurityGroup
        - !Ref WebSecurityGroup
      SubnetId: {"Fn::ImportValue": !Sub "${PJPrefix}-public-subnet-a"}
      UserData: !Base64 | 
        #! /bin/bash
        yum update -y

# ------------------------------------------------------------#
#  EC2Instance AZ:C
# ------------------------------------------------------------#
  EC2Instance02:
    Type: "AWS::EC2::Instance"
    Properties:
      Tags:
        - Key: Name
          Value: !Sub "${PJPrefix}-${EC2InstanceName}-02"
      ImageId: !Ref EC2InstanceAMI
      InstanceType: !Ref EC2InstanceInstanceType
      KeyName: !Ref KeyPairName
      IamInstanceProfile: !Ref EC2InstanceProfile
      DisableApiTermination: false
      EbsOptimized: false
      BlockDeviceMappings:
        - DeviceName: /dev/xvda
          Ebs:
            DeleteOnTermination: true
            VolumeType: !Ref EC2InstanceVolumeType
            VolumeSize: !Ref EC2InstanceVolumeSize
      SecurityGroupIds:
        - !Ref ManagedSecurityGroup
        - !Ref WebSecurityGroup
      SubnetId: {"Fn::ImportValue": !Sub "${PJPrefix}-public-subnet-c"}
      UserData: !Base64 | 
        #! /bin/bash
        yum update -y


# ------------------------------------------------------------#
#  SecurityGroup for Managed
# ------------------------------------------------------------#
  ManagedSecurityGroup:
    Type: "AWS::EC2::SecurityGroup"
    Properties:
      VpcId: {"Fn::ImportValue": !Sub "${PJPrefix}-vpc"}
      GroupName: !Sub "${PJPrefix}-managed-sg"
      GroupDescription: "-"
      Tags:
        - Key: "Name"
          Value: !Sub "${PJPrefix}-managed-sg"

# Rule
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 22
          ToPort: 22
          CidrIp: !Ref SSHAccessSourceIP

# ------------------------------------------------------------#
#  SecurityGroup for ELB
# ------------------------------------------------------------#
  ELBSecurityGroup:
    Type: "AWS::EC2::SecurityGroup"
    Properties:
      VpcId: { "Fn::ImportValue": !Sub "${PJPrefix}-vpc" }
      GroupName: !Sub "${PJPrefix}-elb-sg"
      GroupDescription: "-"
      Tags:
        - Key: "Name"
          Value: !Sub "${PJPrefix}-elb-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 Web
# ------------------------------------------------------------#
  WebSecurityGroup:
    Type: "AWS::EC2::SecurityGroup"
    Properties:
      VpcId: { "Fn::ImportValue": !Sub "${PJPrefix}-vpc" }
      GroupName: !Sub "${PJPrefix}-web-sg"
      GroupDescription: "-"
      Tags:
        - Key: "Name"
          Value: !Sub "${PJPrefix}-web-sg"

# Rule
  WebSecurityGroupIngress:
    Type: "AWS::EC2::SecurityGroupIngress"
    Properties:
      IpProtocol: tcp
      FromPort: 80
      ToPort: 80
      SourceSecurityGroupId: !GetAtt [ ELBSecurityGroup, GroupId ]
      GroupId: !GetAtt [ WebSecurityGroup, GroupId ]

# ------------------------------------------------------------#
#  ElasticIP for EC2Instance01
# ------------------------------------------------------------# 
  ElasticIP01:
    Type: "AWS::EC2::EIP"
    Properties:
      Domain: vpc

  ElasticIPAssociate:
    Type: AWS::EC2::EIPAssociation
    Properties:
      AllocationId: !GetAtt ElasticIP01.AllocationId
      InstanceId: !Ref EC2Instance01

# ------------------------------------------------------------#
#  ElasticIP for EC2Instance02
# ------------------------------------------------------------# 
  ElasticIP02:
    Type: "AWS::EC2::EIP"
    Properties:
      Domain: vpc

  ElasticIPAssociate:
    Type: AWS::EC2::EIPAssociation
    Properties:
      AllocationId: !GetAtt ElasticIP02.AllocationId
      InstanceId: !Ref EC2Instance02

# ------------------------------------------------------------#
#  InternetELB
# ------------------------------------------------------------#
  InternetELB:
    Type: "AWS::ElasticLoadBalancing::LoadBalancer"
    Properties:
      LoadBalancerName: !Sub "${PJPrefix}-${InternetELBName}-elb"
      Scheme: "internet-facing"
      CrossZone: true
      HealthCheck:
        Target: "TCP:80"
        HealthyThreshold: 2
        UnhealthyThreshold: 2
        Interval: 30
        Timeout: 5
      ConnectionDrainingPolicy:
        Enabled: true
        Timeout: 300
      Listeners:
        - LoadBalancerPort: 80
          InstancePort: 80
          Protocol: HTTP
      Instances:
        - !Ref EC2Instance01
        - !Ref EC2Instance02
      SecurityGroups:
        - !Ref ELBSecurityGroup
      Subnets:
        - { "Fn::ImportValue": !Sub "${PJPrefix}-public-subnet-a" }
        - { "Fn::ImportValue": !Sub "${PJPrefix}-public-subnet-c" }

# ------------------------------------------------------------#
# Output Parameters
# ------------------------------------------------------------#
Outputs:
#InternetELB
  ELBDNSName:
    Value: !GetAtt InternetELB.DNSName
    Export:
      Name: !Sub "${PJPrefix}-${InternetELBName}-elb-dnsname"

#EC2Instance01
  EC2InstanceID:
    Value: !Ref EC2Instance01
    Export:
      Name: !Sub "${PJPrefix}-${EC2InstanceName}-01-id"

  EC2InstancePrivateIp:
    Value: !GetAtt EC2Instance01.PrivateIp
    Export:
      Name: !Sub "${PJPrefix}-${EC2InstanceName}-01-private-ip"

  EC2Instance01EIP:
    Value: !GetAtt EC2Instance01.PublicIp
    Export:
      Name: !Sub "${PJPrefix}-${EC2InstanceName}-01-eip"

  EC2InstanceRoleName:
    Value: !Sub "${PJPrefix}-${EC2InstanceName}-01-role"
    Export:
      Name: !Sub "${PJPrefix}-${EC2InstanceName}-01-role-name"

#EC2Instance02
  EC2InstanceID:
    Value: !Ref EC2Instance02
    Export:
      Name: !Sub "${PJPrefix}-${EC2InstanceName}-02-id"

  EC2InstancePrivateIp:
    Value: !GetAtt EC2Instance02.PrivateIp
    Export:
      Name: !Sub "${PJPrefix}-${EC2InstanceName}-02-private-ip"

  EC2Instance02EIP:
    Value: !GetAtt EC2Instance02.PublicIp
    Export:
      Name: !Sub "${PJPrefix}-${EC2InstanceName}-02-eip"

  EC2InstanceRoleName:
    Value: !Sub "${PJPrefix}-${EC2InstanceName}-02-role"
    Export:
      Name: !Sub "${PJPrefix}-${EC2InstanceName}-02-role-name"

$ ssh ec2-user@* -i ~/.ssh/*.pem

OK, Nice

[Docker] docker-composeでDjangoの開発環境構築

Dockerfile

FROM python:3

ENV PYTHONUNBUFFERED 1

RUN mkdir /code

WORKDIR /code

COPY requirements.txt /code/

RUN pip install -r requirements.txt

COPY . /code/

requirements.txt

Django==2.0
psycopg2

docker-compose.yml

version: '3'
services:
  db:
    image: postgres
  web:
    build: .
    command: python3 manage.py runserver 0.0.0.0:8000
    volumes:
      - .:/code
    ports:
      - "8000:8000"
    depends_on:
      - db

$ sudo docker-compose run web django-admin.py startproject webpj .
AttributeError: module ‘collections’ has no attribute ‘Iterator’

なんやこれーーーーーーーー

[Docker] docker-composeでRailsの開発環境構築

Dockerfile

FROM ruby:2.3.7

RUN apt-get update -qq && apt-get install -y build-essential nodejs

RUN mkdir /app

WORKDIR /app

COPY Gemfile /app/Gemfile

COPY Gemfile.lock /app/Gemfile.lock

RUN bundle install

COPY . /app

Gemfile

source 'https://rubygems.org'
gem 'rails', '5.0.0.1'

Gemfile.lockの作成
$ touch Gemfile.lock

docker-compose.yml

version: '3'
services:
  web:
    build: .
    command: bundle exec rails server -p 3000 -b 0.0.0.0
    ports:
      - 3000:3000
    volumes:
      - .:/app
    depends_on:
      - db
    tty: true
    stdin_open: true
  db:
    image: mysql:5.7
    volumes:
      - db-volume:/var/lib/mysql
    environment:
      MYSQL_ROOT_PASSWORD: password
volumes:
  db-volume:

ビルドの実行とRailsプロジェクト作成
$ sudo docker-compose run web rails new . –force –database=mysql
$ ls -la
total 84
drwxrwxr-x 12 vagrant vagrant 4096 Mar 19 02:21 .
drwxr-xr-x 16 vagrant vagrant 4096 Mar 19 01:57 ..
-rw-r–r– 1 root root 468 Mar 19 02:21 .gitignore
-rw-rw-r– 1 vagrant vagrant 211 Mar 19 02:00 Dockerfile
-rw-rw-r– 1 vagrant vagrant 1748 Mar 19 02:21 Gemfile
-rw-rw-r– 1 vagrant vagrant 4430 Mar 19 02:22 Gemfile.lock
-rw-r–r– 1 root root 374 Mar 19 02:21 README.md
-rw-r–r– 1 root root 227 Mar 19 02:21 Rakefile
drwxr-xr-x 10 root root 4096 Mar 19 02:21 app
drwxr-xr-x 2 root root 4096 Mar 19 02:22 bin
drwxr-xr-x 5 root root 4096 Mar 19 02:21 config
-rw-r–r– 1 root root 130 Mar 19 02:21 config.ru
drwxr-xr-x 2 root root 4096 Mar 19 02:21 db
-rw-rw-r– 1 vagrant vagrant 366 Mar 19 02:10 docker-compose.yml
drwxr-xr-x 4 root root 4096 Mar 19 02:21 lib
drwxr-xr-x 2 root root 4096 Mar 19 02:21 log
drwxr-xr-x 2 root root 4096 Mar 19 02:21 public
drwxr-xr-x 8 root root 4096 Mar 19 02:21 test
drwxr-xr-x 3 root root 4096 Mar 19 02:21 tmp
drwxr-xr-x 3 root root 4096 Mar 19 02:21 vendor

$ sudo docker run –rm rails_web ls -la /app
total 20
drwxr-xr-x 1 root root 4096 Mar 19 02:21 .
drwxr-xr-x 1 root root 4096 Mar 19 02:26 ..
-rw-rw-r– 1 root root 211 Mar 19 02:00 Dockerfile
-rw-rw-r– 1 root root 52 Mar 19 02:04 Gemfile
-rw-rw-r– 1 root root 0 Mar 19 02:04 Gemfile.lock
-rw-rw-r– 1 root root 366 Mar 19 02:10 docker-compose.yml

$ sudo docker-compose build
$ sudo docker run –rm rails_web ls -la /app

Railsで使用するデータベースの設定と作成
./config/database.yml

default: &default
  adapter: mysql2
  encoding: utf8
  pool: 5
  username: root
  password: password
  host: db

$ docker-compose up
$ sudo docker-compose ps
Name Command State Ports
————————————————————-
rails_db_1 docker-entrypoint.sh mysqld Exit 0
rails_web_1 bundle exec rails server – … Exit 1

$ sudo docker-compose up -d
$ sudo docker-compose ps
Name Command State Ports
———————————————————————————————–
rails_db_1 docker-entrypoint.sh mysqld Up 3306/tcp, 33060/tcp
rails_web_1 bundle exec rails server – … Up 0.0.0.0:3000->3000/tcp,:::3000->3000/tcp

$ sudo docker-compose run web rake db:create

$ sudo docker-compose run web bin/rails g scaffold User name:string
$ sudo docker-compose run web bundle exec rails db:migrate

### コンテナの停止、削除
$ sudo docker-compose stop
$ sudo docker-compose ps
$ sudo docker-compose down
$ sudo docker-compose up -d

なるほどー
Docker compose使うと複雑になるな…

dbのバキュームとは

VACUUM は、不要タプルが使用する領域を回収します。 PostgreSQLの通常動作では、削除されたタプルや更新によって不要となったタプルは、テーブルから物理的には削除されません。 これらのタプルはVACUUMが完了するまで存在し続けます。 そのため、特に更新頻度が多いテーブルでは、VACUUMを定期的に実行する必要があります。

[docker] docker-compose

アプリケーションのコンテナの他に、データベースなどのコンテナが必要な場合などに使用される

$ sudo docker-compose version
docker-compose version 1.16.1, build 6d1ac21
docker-py version: 2.5.1
CPython version: 2.7.13
OpenSSL version: OpenSSL 1.0.1t 3 May 2016

### docker composeでアプリケーション実行環境を立ち上げる手順
1. コンテナを立ち上げるのに必要なイメージを用意(dockerfile)
2. 各コンテナを起動する際の設定をdocker-compose.ymlに定義
3. docker-compose.ymlに置いてあるディレクトリ上でdocker

– コンテナ間に通信したい場合に、サービス名で通信できる

記述例

version: '3'
services:
  web:
    build: .
    ports:
      - "5000:5000"
    volumes:
      - .:/code
      - logvolume01:/var/log
  redis:
    image: redis
volumes:
  logvolume01: {}

うおおおおおおおおおお
なんとなくわかってきた

[docker] tmpfsを使ってみる

tmpfsはメモリ領域をコンテナにマウントする
コンテナやホストが停止すると、保持していたデータは解放されtmpfsマウントが取り除かれる

$ sudo docker run -itd –name tmpfs-c1 –mount type=tmpfs,destination=/cache busybox
$ sudo docker inspect tmpfs-c1
“Mounts”: [
{
“Type”: “tmpfs”,
“Source”: “”,
“Destination”: “/cache”,
“Mode”: “”,
“RW”: true,
“Propagation”: “”
}
],

$ sudo docker run -itd –name tmpfs-c1 –mount type=tmpfs,destination=/cache,tmpfs-size=500000000,tmpfs-mode=700 busybox
ファイルモードや容量などを指定できる

なるほど、あんまり使わなそうだな…

[docker] Bind Mount

Bind MountはPC上の任意のディレクトリをコンテナにマウントする
-v <ホスト上のディレクトリ>:<コンテナ上のディレクトリ>

$ touch test-file
$ ls -la
total 8
drwxr-xr-x 2 vagrant vagrant 4096 Mar 19 00:57 .
drwxrwxr-x 3 vagrant vagrant 4096 Mar 18 23:07 ..
-rw-rw-r– 1 vagrant vagrant 0 Mar 19 00:57 test-file

$ sudo docker run -itd –name bind-c1 -v “$(pwd)”:/bind_dir busybox
$ sudo docker exec bind-c1 ls -la /bind_dir
total 8
drwxr-xr-x 2 1000 1000 4096 Mar 19 00:57 .
drwxr-xr-x 1 root root 4096 Mar 19 01:12 ..
-rw-rw-r– 1 1000 1000 0 Mar 19 00:57 test-file
$ touch test-file2
$ sudo docker exec bind-c1 ls -la /bind_dir
total 8
drwxr-xr-x 2 1000 1000 4096 Mar 19 01:13 .
drwxr-xr-x 1 root root 4096 Mar 19 01:12 ..
-rw-rw-r– 1 1000 1000 0 Mar 19 00:57 test-file
-rw-rw-r– 1 1000 1000 0 Mar 19 01:13 test-file2

### -mountを指定したコンテナの起動
$ sudo docker run -itd –name bind-c2 –mount type=bind,source=”$(pwd)”,target=/bind_dir busybox
54deefdb625241544cc84326df8055e38c32a8d3e6b2a97c1d0db2bb260c693f
$ sudo docker exec bind-c2 ls -l /bind_dir
total 0
-rw-rw-r– 1 1000 1000 0 Mar 19 00:57 test-file
-rw-rw-r– 1 1000 1000 0 Mar 19 01:13 test-file2
※–mountオプションの方が読みやすい。また、間違えたディレクトリのバインドを防ぐ意味でも–mountの方が安全

### バインドマウントの設定確認
$ sudo docker inspect bind-c1
“Mounts”: [
{
“Type”: “bind”,
“Source”: “/home/vagrant/dev/docker/test/app”,
“Destination”: “/bind_dir”,
“Mode”: “”,
“RW”: true,
“Propagation”: “rprivate”
}
],

なるほど、bindの概念はvolumeと一緒でわりかし簡単だな

[docker] volumeを作成

### volumeの作成方法
$ sudo docker volume ls
$ sudo docker volume create my-vol
$ sudo docker volume inspect my-vol
[
{
“CreatedAt”: “2022-03-18T23:05:02Z”,
“Driver”: “local”,
“Labels”: {},
“Mountpoint”: “/var/lib/docker/volumes/my-vol/_data”,
“Name”: “my-vol”,
“Options”: {},
“Scope”: “local”
}
]

$ sudo docker volume rm my-vol

### volumeのマウント方法
$ sudo docker run -itd –name c1 -v vol1:/app busybox
$ sudo docker volume ls
$ sudo docker inspect c1

        "Mounts": [
            {
                "Type": "volume",
                "Name": "vol1",
                "Source": "/var/lib/docker/volumes/vol1/_data",
                "Destination": "/app",
                "Driver": "local",
                "Mode": "z",
                "RW": true,
                "Propagation": ""
            }
        ],

$ sudo docker exec -it c1 /bin/sh
/ # df
Filesystem 1K-blocks Used Available Use% Mounted on
overlay 40593612 36988012 3589216 91% /
tmpfs 65536 0 65536 0% /dev
tmpfs 2013052 0 2013052 0% /sys/fs/cgroup
shm 65536 0 65536 0% /dev/shm
/dev/sda1 40593612 36988012 3589216 91% /app
/dev/sda1 40593612 36988012 3589216 91% /etc/resolv.conf
/dev/sda1 40593612 36988012 3589216 91% /etc/hostname
/dev/sda1 40593612 36988012 3589216 91% /etc/hosts
tmpfs 2013052 0 2013052 0% /proc/acpi
tmpfs 65536 0 65536 0% /proc/kcore
tmpfs 65536 0 65536 0% /proc/keys
tmpfs 65536 0 65536 0% /proc/timer_list
tmpfs 65536 0 65536 0% /proc/sched_debug
tmpfs 2013052 0 2013052 0% /proc/scsi
tmpfs 2013052 0 2013052 0% /sys/firmware

/ # cd /app
/app # touch hogehoge
/app # exit

$ sudo docker run -itd –name c2 –mount source=vol1,target=/app busybox
$ sudo docker exec -it c2 /bin/sh
/ # ls -la /app/
total 8
drwxr-xr-x 2 root root 4096 Mar 18 23:16 .
drwxr-xr-x 1 root root 4096 Mar 18 23:18 ..
-rw-r–r– 1 root root 0 Mar 18 23:16 hogehoge

おおおお、マウントされてる…

### 既に存在するディレクトリへのvolumeのマウントについて
$ sudo docker run -itd –name c3 –mount source=vol2,destination=/var busybox
$ sudo docker inspect c3
“Mounts”: [
{
“Type”: “volume”,
“Name”: “vol2”,
“Source”: “/var/lib/docker/volumes/vol2/_data”,
“Destination”: “/var”,
“Driver”: “local”,
“Mode”: “z”,
“RW”: true,
“Propagation”: “”
}
],
$ sudo docker exec -it c3 /bin/sh
/ # ls -la /var/

–mount で読み取り専用(readonly)
-vフラグで読み取り専用

volumeのマウントタイプはDockerホスト上のDockerが管理する領域に自動的にディレクトリが作成されコンテナにマウントされる

なるほど、dockerの管理領域にディレクトリを作成するのがvolumeね。