Preparing Resources on AWS
☁️

Preparing Resources on AWS

The following instructions will result in a Docker Swarm setup prepared to deploy LAMP. You will need to continue following the instructions after this phase to successfully configure LAMP.

Digital Psychiatry's AWS Network Diagram for the LAMP Platform
Digital Psychiatry's AWS Network Diagram for the LAMP Platform

For EC2 Instances Only:

You must replace the value of MY_DNS_NAME in the commands below.

 1. Create an EC2 instance.
				-> AMI: AWS Linux 2
				-> Instance Type: [MINIMUM] t2.medium
				-> Create a new IAM role with the `AmazonSSMManagedInstanceCore` policy.
				-> Enable termination protection.
				-> Create a [RECOMMENDED] 1TB EBS volume WITH encryption enabled.
				-> Configure security group:
						-> HTTP: TCP 80 from anywhere
						-> HTTPS: TCP 443 from anywhere
						-> Docker Daemon: TCP 2375 from this Security Group
						-> Docker Machine: TCP 2376 from this Security Group
						-> Docker Swarm: TCP 2377 from this Security Group
						-> Docker Swarm: TCP 7946 from this Security Group
						-> Docker Swarm: UDP 7946 from this Security Group
						-> Docker Overlay: UDP 4789 from this Security Group
				-> Create a new key pair and keep it private.
				-> Allocate and assign an elastic IP to the instance.
 2. Run the following commands in the instance.
				sudo yum -y update && sudo yum -y install docker -y
				sudo usermod -a -G docker ec2-user
				sudo hostnamectl set-hostname <MY_DNS_NAME>
				sudo printf "[Service]\nExecStart=\nExecStart=/usr/bin/dockerd -H tcp://0.0.0.0:2375 -H fd:// --containerd=/run/containerd/containerd.sock\n" | sudo tee /etc/systemd/system/docker.service.d/override.conf
				sudo systemctl daemon-reload && sudo service docker restart
				sudo docker swarm init
 3. Create your DNS binding in Route53.
CloudFormation Stack

This stack is a work-in-progress and should NOT be used for production needs.

AWSTemplateFormatVersion: 2010-09-09
Description: stuff
Parameters:
  DomainName:
    Description: Domain name
    Type: String
  InstanceType:
    Description: EC2 instance type
    Type: String
    Default: t2.nano
    AllowedValues: [a1.2xlarge, a1.4xlarge, a1.large, a1.medium, a1.metal, 
    a1.xlarge, c1.medium, c1.xlarge, c3.2xlarge, c3.4xlarge, c3.8xlarge, 
    c3.large, c3.xlarge, c4.2xlarge, c4.4xlarge, c4.8xlarge, c4.large, c4.xlarge, 
    c5.12xlarge, c5.18xlarge, c5.24xlarge, c5.2xlarge, c5.4xlarge, c5.9xlarge, 
    c5.large, c5.metal, c5.xlarge, c5d.12xlarge, c5d.18xlarge, c5d.24xlarge, 
    c5d.2xlarge, c5d.4xlarge, c5d.9xlarge, c5d.large, c5d.metal, c5d.xlarge, 
    c5n.18xlarge, c5n.2xlarge, c5n.4xlarge, c5n.9xlarge, c5n.large, c5n.xlarge, 
    cc1.4xlarge, cc2.8xlarge, cg1.4xlarge, cr1.8xlarge, d2.2xlarge, d2.4xlarge, 
    d2.8xlarge, d2.xlarge, f1.16xlarge, f1.2xlarge, f1.4xlarge, g2.2xlarge, 
    g2.8xlarge, g3.16xlarge, g3.4xlarge, g3.8xlarge, g3s.xlarge, g4dn.12xlarge, 
    g4dn.16xlarge, g4dn.2xlarge, g4dn.4xlarge, g4dn.8xlarge, g4dn.xlarge, 
    h1.16xlarge, h1.2xlarge, h1.4xlarge, h1.8xlarge, hi1.4xlarge, hs1.8xlarge, 
    i2.2xlarge, i2.4xlarge, i2.8xlarge, i2.xlarge, i3.16xlarge, i3.2xlarge, 
    i3.4xlarge, i3.8xlarge, i3.large, i3.metal, i3.xlarge, i3en.12xlarge, 
    i3en.24xlarge, i3en.2xlarge, i3en.3xlarge, i3en.6xlarge, i3en.large, 
    i3en.metal, i3en.xlarge, inf1.24xlarge, inf1.2xlarge, inf1.6xlarge, inf1.xlarge, 
    m1.large, m1.medium, m1.small, m1.xlarge, m2.2xlarge, m2.4xlarge, m2.xlarge, 
    m3.2xlarge, m3.large, m3.medium, m3.xlarge, m4.10xlarge, m4.16xlarge, 
    m4.2xlarge, m4.4xlarge, m4.large, m4.xlarge, m5.12xlarge, m5.16xlarge, 
    m5.24xlarge, m5.2xlarge, m5.4xlarge, m5.8xlarge, m5.large, m5.metal, m5.xlarge, 
    m5a.12xlarge, m5a.16xlarge, m5a.24xlarge, m5a.2xlarge, m5a.4xlarge, 
    m5a.8xlarge, m5a.large, m5a.xlarge, m5ad.12xlarge, m5ad.16xlarge, m5ad.24xlarge, 
    m5ad.2xlarge, m5ad.4xlarge, m5ad.8xlarge, m5ad.large, m5ad.xlarge, m5d.12xlarge, 
    m5d.16xlarge, m5d.24xlarge, m5d.2xlarge, m5d.4xlarge, m5d.8xlarge, m5d.large, 
    m5d.metal, m5d.xlarge, m5dn.12xlarge, m5dn.16xlarge, m5dn.24xlarge, m5dn.2xlarge, 
    m5dn.4xlarge, m5dn.8xlarge, m5dn.large, m5dn.xlarge, m5n.12xlarge, m5n.16xlarge, 
    m5n.24xlarge, m5n.2xlarge, m5n.4xlarge, m5n.8xlarge, m5n.large, m5n.xlarge, 
    p2.16xlarge, p2.8xlarge, p2.xlarge, p3.16xlarge, p3.2xlarge, p3.8xlarge, 
    p3dn.24xlarge, r3.2xlarge, r3.4xlarge, r3.8xlarge, r3.large, r3.xlarge, 
    r4.16xlarge, r4.2xlarge, r4.4xlarge, r4.8xlarge, r4.large, r4.xlarge, r5.12xlarge, 
    r5.16xlarge, r5.24xlarge, r5.2xlarge, r5.4xlarge, r5.8xlarge, r5.large, 
    r5.metal, r5.xlarge, r5a.12xlarge, r5a.16xlarge, r5a.24xlarge, r5a.2xlarge, 
    r5a.4xlarge, r5a.8xlarge, r5a.large, r5a.xlarge, r5ad.12xlarge, r5ad.16xlarge, 
    r5ad.24xlarge, r5ad.2xlarge, r5ad.4xlarge, r5ad.8xlarge, r5ad.large, 
    r5ad.xlarge, r5d.12xlarge, r5d.16xlarge, r5d.24xlarge, r5d.2xlarge, r5d.4xlarge, 
    r5d.8xlarge, r5d.large, r5d.metal, r5d.xlarge, r5dn.12xlarge, r5dn.16xlarge, 
    r5dn.24xlarge, r5dn.2xlarge, r5dn.4xlarge, r5dn.8xlarge, r5dn.large, r5dn.xlarge, 
    r5n.12xlarge, r5n.16xlarge, r5n.24xlarge, r5n.2xlarge, r5n.4xlarge, r5n.8xlarge, 
    r5n.large, r5n.xlarge, t1.micro, t2.2xlarge, t2.large, t2.medium, t2.micro, 
    t2.nano, t2.small, t2.xlarge, t3.2xlarge, t3.large, t3.medium, t3.micro, t3.nano, 
    t3.small, t3.xlarge, t3a.2xlarge, t3a.large, t3a.medium, t3a.micro, t3a.nano, 
    t3a.small, t3a.xlarge, u-12tb1.metal, u-18tb1.metal, u-24tb1.metal, u-6tb1.metal, 
    u-9tb1.metal, x1.16xlarge, x1.32xlarge, x1e.16xlarge, x1e.2xlarge, x1e.32xlarge, 
    x1e.4xlarge, x1e.8xlarge, x1e.xlarge, z1d.12xlarge, z1d.2xlarge, z1d.3xlarge, 
    z1d.6xlarge, z1d.large, z1d.metal, z1d.xlarge]
    ConstraintDescription: must be a valid EC2 instance type.
  LatestAmiId:
    Type: AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>
    Default: /aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2
Resources:
  EC2SecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: default
      SecurityGroupIngress: 
        - IpProtocol: tcp
          CidrIp: 0.0.0.0/0
          FromPort: 80
          ToPort: 80
        - IpProtocol: tcp
          CidrIpv6: ::/0
          FromPort: 80
          ToPort: 80
        - IpProtocol: tcp
          CidrIp: 0.0.0.0/0
          FromPort: 443
          ToPort: 443
        - IpProtocol: tcp
          CidrIpv6: ::/0
          FromPort: 443
          ToPort: 443
      SecurityGroupEgress: 
        - IpProtocol: "-1"
          CidrIp: 0.0.0.0/0
  EC2SecurityGroupIngress:
    Type: AWS::EC2::SecurityGroupIngress
    DependsOn: !Ref EC2SecurityGroup
    Properties:
      GroupName: !Ref EC2SecurityGroup
      IpProtocol: "-1"
      SourceSecurityGroupName: !Ref EC2SecurityGroup
  EC2Instance01:
    Type: AWS::EC2::Instance
    Properties:
      InstanceType: !Ref InstanceType
      ImageId: !Ref LatestAmiId
      EbsOptimized: true
      SecurityGroupIds: [!Ref EC2SecurityGroup]
      SourceDestCheck: true
      IamInstanceProfile: AmazonSSMRoleForInstancesQuickSetup
      BlockDeviceMappings: 
        - DeviceName: /dev/xvda
          Ebs: 
            Encrypted: false
            VolumeSize: 30
            VolumeType: gp2
            DeleteOnTermination: true
        - DeviceName: /dev/sdf
          Ebs: 
            Encrypted: true
            VolumeSize: 4096
            VolumeType: gp2
            DeleteOnTermination: false
      NetworkInterfaces:
        - NetworkInterfaceId: !Ref ENI01
          DeviceIndex: 0
          DeleteOnTermination: false
  EC2Instance02:
    Type: AWS::EC2::Instance
    Properties:
      InstanceType: !Ref InstanceType
      ImageId: !Ref LatestAmiId
      EbsOptimized: true
      SecurityGroupIds: [!Ref EC2SecurityGroup]
      SourceDestCheck: true
      IamInstanceProfile: AmazonSSMRoleForInstancesQuickSetup
      BlockDeviceMappings: 
        - DeviceName: /dev/xvda
          Ebs: 
            Encrypted: false
            VolumeSize: 30
            VolumeType: gp2
            DeleteOnTermination: true
        - DeviceName: /dev/sdf
          VirtualName: ephemeral0
      NetworkInterfaces:
        - NetworkInterfaceId: !Ref ENI02
          DeviceIndex: 0
          DeleteOnTermination: false
  ENI01:
    Type: AWS::EC2::NetworkInterface
    Properties:
      PrivateIpAddress: !GetAtt EC2Instance01.PrivateIp
      PrivateIpAddresses: 
        - PrivateIpAddress: !GetAtt EC2Instance01.PrivateIp
          Primary: true
      SubnetId: !GetAtt EC2Instance02.SubnetId
      SourceDestCheck: true
      GroupSet: 
        - !Ref EC2SecurityGroup
  ENI02:
    Type: AWS::EC2::NetworkInterface
    Properties:
      PrivateIpAddress: !GetAtt EC2Instance02.PrivateIp
      PrivateIpAddresses: 
        - PrivateIpAddress: !GetAtt EC2Instance02.PrivateIp
          Primary: true
      SubnetId: !GetAtt EC2Instance02.SubnetId
      SourceDestCheck: true
      GroupSet: 
        - !Ref EC2SecurityGroup
  EIP01:
    Type: AWS::EC2::EIP
    Properties:
      InstanceId: !Ref EC2Instance01
  EIP02:
    Type: AWS::EC2::EIP
    Properties:
      InstanceId: !Ref EC2Instance02
  DNSZone:
    Type: AWS::Route53::HostedZone
    Properties:
      Name: !Ref DomainName
  DNSEntries:
    Type: AWS::Route53::RecordSetGroup
    Properties:
      HostedZoneId: !Ref DNSZone
      RecordSets:
        - Name: !Ref DomainName
          Type: A
          TTL: 300
          MultiValueAnswer: true
          SetIdentifier: node-01
          ResourceRecords: 
            - !Ref EIP01
        - Name: !Ref DomainName
          Type: A
          TTL: 300
          MultiValueAnswer: true
          SetIdentifier: node-02
          ResourceRecords: 
            - !Ref EIP02
        - Name: !Sub "*.${DomainName}"
          Type: A
          TTL: 300
          MultiValueAnswer: true
          SetIdentifier: node-01
          ResourceRecords: 
            - !Ref EIP01
        - Name: !Sub "*.${DomainName}"
          Type: A
          TTL: 300
          MultiValueAnswer: true
          SetIdentifier: node-02
          ResourceRecords: 
            - !Ref EIP02
        - Name: !Sub "node-01.${DomainName}"
          Type: A
          TTL: 300
          MultiValueAnswer: true
          SetIdentifier: node-01
          ResourceRecords: 
            - !Ref EIP01
        - Name: !Sub "node-02.${DomainName}"
          Type: A
          TTL: 300
          MultiValueAnswer: true
          SetIdentifier: node-02
          ResourceRecords: 
            - !Ref EIP02

AWS SSM Instructions

We recommend disabling all SSH (port 22) or remote access to any EC2 instances you configure. Use AWS Systems Session Manager (AWS SSM) to access your node. Additionally, follow the instructions below on your local computer to securely communicate with the instance(s).

  1. Install the AWS CLI SSM plugin.
  2. Configure SSH to support AWS SSM tunneling.
  3. Alternatively, manually open a port forwarding tunnel.
  4. aws ssm start-session \
    	--target $(aws ec2 describe-instances \
    		--filter "Name=tag:Name,Values=node-01" \
    		--query "Reservations[].Instances[?State.Name == 'running'].InstanceId[]" \
    		--output text) \
    	--document-name AWS-StartPortForwardingSession \
    	--parameters '{ "portNumber": ["22"], "localPortNumber": ["9999"] }'
  5. Alternatively, manually open a secure SSH tunnel session.
  6. aws ssm start-session \
    	--target $(aws ec2 describe-instances \
    		--filter "Name=tag:Name,Values=node-01" \
    		--query "Reservations[].Instances[?State.Name == 'running'].InstanceId[]" \
    		--output text) \
    	--document-name AWS-StartSSHSession \
    	--parameters 'portNumber=%p'