#docker #nginx #ecs #efs #page-speed

Idea

We had two options for our blogging

  1. Subscribe to ghost.org plan
  2. Host our own on aws

As we want to host our blog on a subdirectory https://finflux.co/blog, the first option was costly for us and hence we opted for the second option.

Our main website https://finflux.co is written in php + html5 + css and  served using Nginx. So we finalised on  following stack to cohost website and ghost using docker containers

  1. Nginx (pagespeed/nginx-pagespeed:latest)
  2. php-fpm (bitnami/php-fpm:latest)
  3. ghost (ghost:3-alpine)
  4. mysql as ghost backend
  5. Pagespeed - for faster web experience

To run locally

We modified Nginx server config to go to ghost blog On local, we want two things to happen. On http://localhost website should serve and on http://localhost/blog, the ghost blog should trigger. We did changes to docker-compose and  .

docker-compose.yml


version: '2'

networks:
  app-tier:
    driver: bridge
    
services:
  phpfpm:
    image: 'bitnami/php-fpm:latest'
    networks:
      - app-tier
    volumes:
      - web-content:/app
  nginx:
    image: 'pagespeed/nginx-pagespeed:latest'
    depends_on:
      - phpfpm
      - ghost
    networks:
      - app-tier
    ports:
      - '80:8080'
    volumes:
      - ./server.conf:/etc/nginx/conf.d/yourapp.conf
      - ./website/:/app  
  ghost:
    image: ghost:3-alpine
    restart: always
    networks:
      - app-tier
    ports:
      - 8082:2368
    environment:
      # see https://docs.ghost.org/docs/config#section-running-ghost-with-config-env-variables
      database__client: mysql
      database__connection__host: host.docker.internal
      database__connection__user: root
      database__connection__password: xxxxxx
      database__connection__database: ghost 
      url: http://localhost/blog   

volumes:
  web-content:  

nginx server.conf

server {
  listen 0.0.0.0:8080;
  root /app;

  error_page 404 /notfound.html;

  location / {
    # try_files $uri $uri/index.php;  
     
    index  index.php;     
  }

  location ~ \.php$ {
    root /app;

    fastcgi_pass phpfpm:9000;
    fastcgi_index index.php;
    # include the fastcgi_param setting
    include fastcgi.conf;
    
  }

  pagespeed on;
  pagespeed FileCachePath /var/cache/ngx_pagespeed;
     # Ensure requests for pagespeed optimized resources go to the pagespeed handler
    # and no extraneous headers get set.
    location ~ "\.pagespeed\.([a-z]\.)?[a-z]{2}\.[^.]{10}\.[^.]+" {
      add_header "" "";
    }
    location ~ "^/pagespeed_static/" { }
    location ~ "^/ngx_pagespeed_beacon$" { }


}

To run on Production

On production, we did following additional steps

  1. ECS to run docker containers
  2. ECR image of Finflux website
  3. Used SSM to store database password
  4. AWS logs for logging
  5. AWS efs(Elastic File Storage) - Ghost persistent storage
  6. SES as ghost email service

Dockerfile


FROM pagespeed/nginx-pagespeed:latest

LABEL maintainer="dev@finflux.co"

COPY ./website/ /app
COPY ./server.conf /etc/nginx/conf.d/yourapp.conf

EXPOSE 8080

CMD ["nginx", "-g", "daemon off;"]

docker-compose.yml

version: '3'

services:
  phpfpm:
    image: 'bitnami/php-fpm:latest'
    volumes:
      - web-content:/app
    logging:
      driver: awslogs
      options:
          awslogs-group: logging-group
          awslogs-region: ap-south-1
          awslogs-stream-prefix: website-phpfpm      
  nginx:
    image: 'xyz.dkr.ecr.ap-south-1.amazonaws.com/finflux-website:${IMG_TAG}'
    links:
      - phpfpm
      - ghost    
    depends_on:
      - phpfpm
      - ghost
    ports:
      - '80:8080'
    volumes:
      - web-content:/app
    logging:
      driver: awslogs
      options:
          awslogs-group: logging-group
          awslogs-region: ap-south-1
          awslogs-stream-prefix: website-nginx       
  ghost:
    image: ghost:3-alpine
    ports:
      - 8080:2368
    volumes:
      - ghost-efs:/var/lib/ghost/content
    environment:
      # see https://docs.ghost.org/docs/config#section-running-ghost-with-config-env-variables
      TZ: "${TIME_ZONE}"
      database__client: mysql
      database__connection__host: ${DB_HOST}
      database__connection__user: ${DB_USER}
      database__connection__database: ${DB_NAME}
      mail__transport: SMTP
      mail__options__port: 587
      mail__options__host: ${SMTP_HOST}
      mail__options__auth__user: ${SMTP_USER}
      mail__options__auth__pass: ${SMTP_PASS}
      mail__from: ${SMTP_EMAIL}
      url: ${GHOST_URL}
    logging:
      driver: awslogs
      options:
          awslogs-group: logging-group
          awslogs-region: ap-south-1
          awslogs-stream-prefix: website-ghost 
volumes:
  web-content: 
  ghost-efs:           

ecs-params.yml

version: 1
task_definition:
  task_role_arn: "arn:aws:iam::xyz:role/ECS-TASK-EXECUTION-ROLE"
  task_execution_role: "arn:aws:iam::xyz:role/ECS-TASK-EXECUTION-ROLE"
  services:
    ghost:
      mem_reservation: 1024MB
      secrets:
        - value_from: "arn:aws:ssm:ap-south-1:xyz:parameter/website.dbpassword"
          name: "database__connection__password"
    nginx:
      mem_reservation: 512MB      
    phpfpm:
      mem_reservation: 256MB
  efs_volumes:
    - name: ghost-efs
      filesystem_id: <efs-volumeId>
      root_directory: /website