Deploying the LAMP Platform
šŸ’¾

Deploying the LAMP Platform

Components of the LAMP Platform

The mindLAMP app interfaces with the much broader LAMP Platform to provide all of the functionality and features you can interact with within the app. The LAMP Platform is open source and its components are detailed below; to see technical materials such as source code or compiling/testing routines, please visit the components' corresponding repository.

There are different ways for your organization to self-deploy the LAMP Platform, and because these components are interoperable, your organization has the choice between only self-deploying one, a combination, or all of these components together.

ā†’ Be sure to open the toggles on the left-hand side to learn more about what each component does.

Please read our deployment recommendations before proceeding.

āš ļø
Deployment Recommendations

Backend

Database
  • The noSQL database used for modern sensor data collection is CouchDB, but currently for legacy data support, Microsoft SQL Server must also be configured as part of deployment.
  • Data backup must be manually configured as different organizations will expect different sizes and frequency of data storage, along with variance in data policies.
  • These database components are not built or maintained as part of the LAMP Platform but are required for its usage.
Server
  • The server acts as an event bus that connects the different components of the Platform together on the data plane and provides a control plane.
  • It vends the LAMP Application Programming Interface (API) by which clients connect to and use the LAMP Platform.
  • The API enables data harmonization, synchronization, and integration with other platforms and databases through a standardized data format and set of actions with built-in credential management.
  • The API is defined using OpenAPI and JSONSchema to markup extensible interfaces.
  • The industry encryption standards AES-256 and TLSv1.3 facilitate secure storage and transmission of data in a HIPAA, COPPA, and GDPR-compliant manner.
Service Worker
  • This component allows external apps and tools to be notified of changes to data from other users through sent notifications and events.
  • This component schedules push notifications to be delivered to mobile devices, as well as manages email/SMS notification delivery.
  • Optionally, it also schedules and executes scripts written in Python, Javascript, or R, as part of intervention delivery and data analysis. This feature can be disabled.

When: If your organization is required to ensure that all data is securely managed and complies with internal policies or regulations.

Prerequisites: Your organization will need to provision computing resources and encrypted snapshot storage and dedicate personnel to maintenance, updates, and security (i.e. data breach detection).

Options: You can use the existing mindLAMP Backend hosted by the Division of Digital Psychiatry (IRB permitting) with your customized Native App or Frontend.

Frontend

App UI
  • The user interface for the mindLAMP app, accessible from the Web, iOS, and Android. For creating and using cognitive tests or other activities, please see the LAMP-activities repository.
  • This component allows administrators, researchers, clinicians, participants, and patients to access and manage their mindLAMP configuration and data.
  • It displays visualizations and can configure clinics and studies to specified requirements, and makes available activities and interventions to patients or participants, with push notifications to schedule them per user.
Integrated Development Environment
  • This component is optional.
  • This component allows authorized developers and data scientists to build, test, and deploy algorithms that work with the LAMP Platform in a secure manner.
  • This component may be deployed multiple times to support different development needs or users.
  • We additionally recommend JupyterLab for Python data analysis, RStudio for R data analysis, and Visual Studio Code for TypeScript development.
  • Integrating development and data analysis with the LAMP Platform deployment increases data throughput and security, but access to these instances should be guarded carefully to avoid data leaks.

When: If your organization would like to create customized user experiences for your clients or clinicians.

Prerequisites: Your organization will need to provision a content delivery system to serve the user interface.

Options: You can use the existing mindLAMP App UI designed by the Division of Digital Psychiatry with your customized Native App or Backend.

Native App

Smartphone (iOS, Android)
  • This component is used to collect high precision sensor measurements from the numerous sensors outlined in the LAMP API documentation.
  • By embedding a miniature version of the LAMP server and database, it supports active bi-directional synchronization between two devices (i.e. watch and phone, or phone and cloud).
  • By embedding the App UI, patients and participants can interact with clinic or study-scheduled survey instruments and cognitive assessments to record active data and metadata.
Wearable (Apple Watch, Google wearOS)
  • This component is used to collect high precision sensor measurements from the numerous sensors outlined in the LAMP API documentation.
  • By embedding a miniature version of the LAMP server and database, it supports active bi-directional synchronization between two devices (i.e. watch and phone, or phone and cloud).

When: if your organization would like to include support for additional sensor instruments, such as third party medical devices.

Prerequisites: Your organization will need to purchase membership with Apple and/or Google's developer program, submit modifications to the app for App Store review, and dedicate personnel to maintenance and updates.

Options: You can use the existing mindLAMP Native App submitted by the Division of Digital Psychiatry and vetted by Apple and Google with your customized Frontend or Backend.

šŸ’”

Even if your organization chooses to make modifications to any of these components when self-deploying them, they remain compatible with one-another. For example, if one organization self-deploys a new Frontend user experience or adds medical devices support to the Native App, it remains compatible with another organization's self-deployed Backend.

Requirements of the LAMP Platform

The components of the LAMP Platform rely on IT infrastructure that can be self-hosted by your organization (sometimes called "on-prem") or hosted by a cloud provider such as Amazon or Google.

šŸš§

If you'd like to deploy your organization's instance of the LAMP Platform on a cloud provider, please consult with your legal and IT departments first, and always ensure that you have signed a Business Associate Agreement (BAA) with the cloud provider to comply with HIPAA.

Factors that influence provisioning of compute and storage resources.

There are a few factors that need to be accounted when determining how to deploy the components explained above, as well as how high your monthly costs may be.

  1. Computing: The components of the LAMP Platform are highly efficient and won't need much computing power; however, at minimum they require a single dual-core node with at least 2 gigabytes of memory.
  2. Storage: The data collection rate for the LAMP Platform varies significantly depending on usage needs, spanning from a minimum of 250 gigabytes of low-throughput (HDD) storage to a recommended 4 terabytes of high-throughput (SSD) storage.
    1. If you are collecting digital phenotyping data, using the integrated development environments, or scheduling intervention delivery/data analysis scripts, the minimum requirements WILL NOT be sufficient.
  3. Network: The network bandwidth of the LAMP Platform varies significantly depending on your organization's size and study/clinic requirements; at minimum you must have single virtual private endpoint (i.e. firewall) with a bandwidth of at least 1 Gbps, to a recommended 10 Gbps to adequately handle sustained multi-user loads.

Example use-cases and their associated cloud computing costs.

When hosting the LAMP Platform with a cloud provider, your monthly costs may vary significantly based on the requirements explained previously. Here are two example configurations with the Amazon Web Services (AWS) cloud provider:

  1. "I run a digital clinic with about ~15 patients and don't intend to use advanced features such as digital phenotyping (sensor data) collection or Automations."
    1. Computing: 1 node, 2 vCPU, 2 GB RAM
    2. Storage: 250 GB, no snapshots
    3. Network: Up to 10 Gbps
    4. Total Cost: ~$35.00/mo
  2. "I run multiple research studies with 200+ patients and intend to collect digital phenotyping data at a high frequency, develop and use machine learning algorithms through the Automations framework, and more advanced features."
    1. Computing: 2 nodes, 16 vCPU, 32 GB RAM
    2. Storage: 8.0 TB, automated monthly snapshots
    3. Network: 25 Gbps
    4. Total Cost: ~$1,500.00/mo
āš ļø

Please note that it is currently not yet possible to configure specific sensors to operate at specific frequencies. This feature is coming soon but not available today.

When self-hosting the LAMP Platform (that is, "on-prem"), it's difficult to determine the monthly costs and maintenance needs. Please reach out to us for a consultation if you or your organization would like assistance with self-hosting.

šŸš§

Though self-hosting may appear to be cheaper, the "hidden costs" should not be neglected in: ensuring redundant storage, no system down-time, regular storage backups, HIPAA-compatible encryption during flight and at rest, preventing network bottlenecks, regular system maintenance, and more. We do not recommend self-hosting unless your organization already has the infrastructure to correctly do so.

Provisioning the LAMP Platform

image

BIDMC Digital Psychiatry's LAMP Platform

The LAMP Platform is cloud-agnostic and can be hosted on Amazon Web Services, Microsoft Azure, Google Cloud, any other cloud provider, or self-hosted on-prem.

The LAMP Platform requires computing and storage resources (i.e. AWS EC2 and EBS), along with external network access; managed services from cloud providers tend to provide robust and maintenance-free bring-up for the LAMP Platform and are recommended over self-hosting on-prem. Furthermore, additional services such as Amazon CloudFront or CloudFlare allow less expensive and more effective caching and edge content delivery of the mindLAMP Dashboard and UI without the need for multi-region deployments.

A single Docker Stack file (docker-compose.yml) is used to automate single-node or multi-node deployments, and requires Docker Swarm (multi-node for cloud testing, integration, and production usage, or single-node for local testing or smaller deployments). Though possible to use Kubernetes or Terraform to manage larger scale deployments, the LAMP Platform has only been tested with Docker Swarm.

āš ļø

We provide additional examples for (1) local development, and (2) Amazon Web Services. If you or your organization have further questions about choosing a cloud provider or deploying on-prem, please contact us.

Segmenting Compute Resources

image

BIDMC Digital Psychiatry's LAMP Platform

When developing or testing the LAMP Platform, it is useful and effective to segment compute resources and develop natively in the cloud (or on-prem). Consider carefully how your organization expects to the use the LAMP Platform and segregate computing resources (nodes) to reduce cost and improve performance.

The LAMP Platform source code repositories are hosted on GitHub and use the GitHub Actions and GitHub Packages features to quickly perform continuous testing, integration, and deployment. As code is pull requested from a feature branch into the master branch, it should pass a round of code review and pass any/all unit tests. Once the code is merged, the automated CI/CD will prepare and upload Docker images.

We highly recommend the use of Portainer to remotely manage your Docker Swarm nodes and container health, as well as Traefik for managing service mesh routing and in-flight TLS encryption; additional Docker Stack files and instructions are provided for both. The Traefik router interfaces with Docker Swarm and your DNS provider to automatically manage internal and external access to services, according to the configuration in the Docker Stack files provided. To monitor node health and container resource metrics, we recommend Netdata and have included it in the stack file below.

šŸ’”

If you'd like to follow along but would like to test the deployment out first, or don't have provisioned resources yet, consider using Play With Docker, a free service from the Docker team where you can provision a temporary Swarm cluster.

Preparing Resources on Amazon Web Services

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.

For EC2 instances only:

 1. create EC2 instance
				-> use AMI: AWS Linux 2
				-> instance type: t2.medium (SQL Server requires >2GB memory)
				-> create a new IAM role with the `AmazonSSMManagedInstanceCore` policy
				-> enable termination protection
				-> recommended 1TB storage WITH encryption enabled
				-> configure security group
						-> MX: TCP 25 from anywhere
						-> SSH: TCP 22 from my IP
						-> HTTP: TCP 80 from anywhere
						-> HTTPS: TCP 443 from anywhere
						-> Docker Daemon: TCP 2375 from my IP
						-> Docker Daemon: TCP 2375 from 18.216.130.88/32
						-> Docker Machine: TCP 2376 from my IP
						-> Docker Swarm: TCP 2377 from anywhere
						-> Docker Swarm: TCP 7946 from anywhere
						-> Docker Swarm: UDP 7946 from anywhere
						-> Docker Overlay: UDP 4789 from anywhere
				-> 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 DNS binding in Route53
 4. ! you may need to manually change db username and load the setup script !

ā†’ Ensure that anyone that has access to the TCP listening socket is a trusted user since access to the docker daemon is root-equivalent.

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.
    1. 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"] }'
  4. Alternatively, manually open a secure SSH tunnel session.
    1. 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'

Deploying the LAMP Platform

āš ļø

You must have a configured Docker Swarm cluster to continue. Please follow all steps below in the exact order specified, though you may skip optional steps.

Prerequisites

šŸ”„

This step is required.

Configure firewall rules as needed to avoid directly opening ports on your nodes, and instead appropriately route traffic through ports 80 (HTTP) and 443 (HTTPS).

1. Create a new network called public to connect all externally accessible services.

docker network create --driver overlay --attachable public
2. Using your DNS provider of choice, provision a domain name (here we use example.com to represent your domain name and 1.1.1.1 to represent your node's IP address).
ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¬ā”€ā”€ā”€ā”€ā”€ā”€ā”¬ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¬ā”€ā”€ā”€ā”€ā”€ā”€ā”
ā”‚ RECORD NAME   ā”‚ TYPE ā”‚ VALUE   ā”‚ TTL  ā”‚
ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”¤
ā”‚ example.com   ā”‚ A    ā”‚ 1.1.1.1 ā”‚ 3600 ā”‚ 
ā”‚ *.example.com ā”‚ A    ā”‚ 1.1.1.1 ā”‚ 3600 ā”‚
ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”“ā”€ā”€ā”€ā”€ā”€ā”€ā”“ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”“ā”€ā”€ā”€ā”€ā”€ā”€ā”˜
3. If you have multiple nodes in your cluster, be sure to configure alternate IP address values for all DNS records.

For example, on AWS Route53, ROUND-ROBIN refers to Multivalue Answer response types without health-check enabled.

ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¬ā”€ā”€ā”€ā”€ā”€ā”€ā”¬ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¬ā”€ā”€ā”€ā”€ā”€ā”€ā”¬ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¬ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”
ā”‚ RECORD NAME   ā”‚ TYPE ā”‚ VALUE   ā”‚ TTL  ā”‚ MODE        ā”‚ ALIAS   ā”‚
ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤
ā”‚ example.com   ā”‚ A    ā”‚ 1.1.1.1 ā”‚ 3600 ā”‚ ROUND-ROBIN ā”‚ node-01 ā”‚
ā”‚ example.com   ā”‚ A    ā”‚ 2.2.2.2 ā”‚ 3600 ā”‚ ROUND-ROBIN ā”‚ node-02 ā”‚
ā”‚ *.example.com ā”‚ A    ā”‚ 1.1.1.1 ā”‚ 3600 ā”‚ ROUND-ROBIN ā”‚ node-01 ā”‚
ā”‚ *.example.com ā”‚ A    ā”‚ 2.2.2.2 ā”‚ 3600 ā”‚ ROUND-ROBIN ā”‚ node-02 ā”‚
ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”“ā”€ā”€ā”€ā”€ā”€ā”€ā”“ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”“ā”€ā”€ā”€ā”€ā”€ā”€ā”“ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”“ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜

Cloud Mesh Router

āš ļø

This step is optional and can be skipped. We recommend deploying Traefik as a Cloud Mesh Router to make it easier to connect services and components, generate SSL certificates for encrypting traffic, diagnose traffic issues, and capture access logs for auditing.

Docker Stack: traefik.yml
version: "3.7"
services:
  traefik:
    image: traefik:latest
    command:
      - "--log.level=INFO"
      - "--accesslog=true"
      - "--api=true"
      - "--api.dashboard=true"
      - "--providers.docker=true"
      - "--providers.docker.swarmMode=true"
      - "--providers.docker.exposedByDefault=false"
      - "--providers.file.directory=/traefik-config"
      - "--providers.file.watch=true"
      - "--entrypoints.web.address=:80"
      - "--entrypoints.websecure.address=:443"
      - "--entrypoints.websecure.http.tls.certResolver=default"
      - "--entrypoints.web.http.redirections.entryPoint.to=websecure"
      - "--entrypoints.web.http.redirections.entryPoint.scheme=https"
      - "--entrypoints.web.http.redirections.entryPoint.permanent=true"
      - "--certificatesResolvers.default.acme.email=administrator@example.com"
      - "--certificatesResolvers.default.acme.storage=/data/acme.json"
      - "--certificatesResolvers.default.acme.tlsChallenge=true"
    volumes:
      - "/var/run/docker.sock:/var/run/docker.sock:ro"
      - "traefik-ssl:/data/"
    ports:
      - target: 80
        protocol: tcp
        published: 80
        mode: ingress
      - target: 443
        protocol: tcp
        published: 443
        mode: ingress
    networks:
      - public
    deploy:
      mode: replicated
      labels:
        traefik.enable: 'true'
        traefik.http.routers.traefik_dashboard.entryPoints: 'websecure'
        traefik.http.routers.traefik_dashboard.service: 'api@internal'
        traefik.http.routers.traefik_dashboard.rule: 'Host(`router.console.example.com`)'
        traefik.http.services.traefik_dummy.loadbalancer.server.port: 19999
      placement:
        constraints:
          - node.role == manager
networks:
  public:
    external: true
volumes:
  traefik-ssl:
docker stack deploy --compose-file traefik.yml router
šŸ’”

We recommend deploying Portainer as a Swarm Management Console to make it easier to troubleshoot failed deployments, rapidly test and integrate new components, and effectively monitor container logs and health. Read this documentation to learn more about Portainer and how to configure and use it.

LAMP Platform

šŸ”„

This step is required.

Create a /data folder in the node that will be hosting the database(s).

You must first generate two cryptographically secure hexadecimal strings. Substitute these strings in the stack file below as indicated by the environment variables after the #.

openssl rand -hex 8 # COUCHDB_PASSWORD
openssl rand -hex 32 # ROOT_KEY
Docker Stack: lamp.yml
version: '3.7'
services:
  server:
    image: bidmcdigitalpsychiatry/lamp-server:latest
    environment:
      HTTPS: 'off'
      SCHEDULER: 'off'
      ROOT_KEY: '32_BIT_ENCRYPTION_KEY_HERE'
      CDB: 'https://admin:DB_PASSSWORD_HERE@database/'
      PUSH_API_KEY: 'YOUR_PUSH_KEY_HERE'
      PUSH_API_GATEWAY: 'YOUR_PUSH_GATEWAY_HERE'
    networks:
      - public
    deploy:
      mode: replicated
      labels:
        traefik.enable: 'true'
        traefik.http.routers.lamp_server.entryPoints: 'websecure'
        traefik.http.routers.lamp_server.rule: 'Host(`api.example.com`)'
        traefik.http.routers.lamp_server.tls.certresolver: 'default'
        traefik.http.services.lamp_server.loadbalancer.server.port: 3000
      placement:
        constraints:
          - node.role == manager
  database:
    image: apache/couchdb:latest
    volumes:
      - /data/couchdb:/opt/couchdb/data
    networks:
      - public
    environment:
      COUCHDB_USER: 'admin'
      COUCHDB_PASSWORD: 'DB_PASSWORD_HERE'
    deploy:
      mode: replicated
      labels:
        traefik.enable: 'true'
        traefik.http.routers.lamp_database.entryPoints: 'websecure'
        traefik.http.routers.lamp_database.rule: 'Host(`db.example.com`)'
        traefik.http.routers.lamp_database.tls.certresolver: 'default'
        traefik.http.services.lamp_database.loadbalancer.server.port: 5984
      placement:
        constraints:
          - node.role == manager
networks:
  public:
    external: true
šŸ’”

If you've deployed the Swarm Management Console, log into your swarm cluster and navigate to the Stack tab on the left sidebar. Paste the contents of the stack file into the editor pane and tap "Deploy", instead of running the command below.

docker stack deploy --compose-file lamp.yml lamp

Testing the LAMP Platform

Once you've deployed the LAMP-server and LAMP-database, you'll be able to use the mindLAMP app (either on your mobile device or in a desktop browser) to connect to your instance.

šŸŒŸ

All data is encrypted before communication between your browser or the app to and from your newly deployed server. No data will be communicated with any other server, including the default API server at api.lamp.digital or other third party services.

Create a Researcher and a Participant to verify the setup works as expected. To jumpstart your instance of the LAMP Platform and test surveys or other activities, follow the instructions in

and import the file below into the Researcher you just created.

lamp_example_survey_battery_export.json113.1KB

Was there something we didn't cover, or need more help? Let us know by making a post in the LAMP Community, or contact us directly. Thank you for your contribution! šŸŒŸ Page last updated on October 15th, 2020.