Finflux Tech Series – Ghost Blog Deployment

by | Aug 2, 2021

#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:latestLABEL maintainer="dev@finflux.co"COPY ./website/ /appCOPY ./server.conf /etc/nginx/conf.d/yourapp.confEXPOSE 8080CMD ["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: 1task_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