Let run Ghost in Docker in support Ali cloud OSS

As used herein, "Attribution 4.0 International (CC BY 4.0)" license agreement, welcome to reprint, modify or re-use, but need to specify the source. Attribution 4.0 International (CC BY 4.0)

Author: Su Yang

Created: March 14 2020
Statistics Word Count: 8133 words
Reading Time: 17 minutes to read
this article link: https://soulteary.com/2020/03/14/ghost-running-in-docker-supports-alibaba-cloud -oss.html


Let run Ghost in Docker in support Ali cloud OSS

In recent content management background to optimize the use of Ghost as a line, use the line as a system, unlike internal MIS, reliability and application performance requires a certain degree of protection.

Solve performance problems, the simplest solution is to scale horizontally, and we know that if you want to make a service to achieve the level of scalability, in addition to the application you want to run a separate state lasting Outsider, document storage must be done persistence , objects in the cloud storage platform is a good file persistence solution.

Ghost is a typical single application, v3.x version of the document is actually not much container, and describes how to use Aliyun OSS document is not, toss the process is still very interesting, record, hoping to help to behind demand students.

EDITORIAL

Official documents using custom-party storage part is actually written is not very good:

  1. Boast about the validity of the document, although the content mentioned in support Ali cloud, but the list of Ali cloud OSS plugin for only in version 1.x, which Ali cloud SDK is relatively old, when Node environment is also very old.
  2. Custom Document lack of technical details, as well as a complete description of the need to verify through practice and read the source code.
  3. Absolutely no mention of how the mirror in the container, especially an official mirror with plug-ins.

This article, to solve the above problems through the container opposite the flow of the program.

Previous article "talk from custom Ghost image optimization Dockerfile" , BUG repair Ghost Chinese input methods have mentioned, "How Ghost of a container package", interested students can understand the next.

After "repeated cross jump" stepped on a pile pit, relatively safe, low-cost maintenance program is to prepare for the Ghost to the current version of the plug-in storage, and a mirrored image of the container based on the official patch.

Before writing plug-ins, you need to confirm the official version of Node environment, in order to determine the symbol syntax:

docker run --rm -it --entrypoint /usr/local/bin/node ghost:3.9.0-alpine -v

After executing the above command, you will get v12.16.1 result, it appears that can be used directly async/awaitreduce the amount of code to write plug-ins.

Write OSS store plug-in

Reference official templates , as well as complete storage Ali cloud OSS SDK plug-in about ten minutes to get the relevant code I've uploaded to GitHub , if the need for secondary packaging, can use and reference.

/**
 * Ghost v3 Storage Adapter (Aliyun OSS)
 * @author soulteary([email protected])
 */

const AliOSS = require("ali-oss");
const GhostStorage = require("ghost-storage-base");
const { createReadStream } = require("fs");
const { resolve } = require("path");

class AliOSSAdapter extends GhostStorage {
  constructor(config) {
    super();
    this.config = config || {};
    this.oss = new AliOSS({
      region: config.region,
      accessKeyId: config.accessKeyId,
      accessKeySecret: config.accessKeySecret,
      bucket: config.bucket
    });

    this.ossURL = `${config.bucket}.${config.region}.aliyuncs.com`;
    this.regexp = new RegExp(`^https?://${this.ossURL}`, "i");
    this.domain = config.domain || null;
    this.notfound = config.notfound || null;
  }

  async exists(filename, targetDir = this.getTargetDir("/")) {
    try {
      const { status } = await this.oss.head(resolve(targetDir, filename));
      return status === 404;
    } catch (err) {
      return false;
    }
  }
  delete() {
    // it's unnecessary
    // Ghost missing UX
  }
  serve() {
    return function(req, res, next) {
      next();
    };
  }

  async read(options) {
    try {
      const { meta } = await this.oss.head(options.path);
      if (meta && meta.path) {
        return meta.path;
      } else {
        return this.notfound;
      }
    } catch (err) {
      console.error(`Read Image Error ${err}`);
      return this.notfound;
    }
  }

  async save(image, targetDir = this.getTargetDir("/")) {
    try {
      const filename = await this.getUniqueFileName(image, targetDir);
      const { url } = await this.oss.put(filename, createReadStream(image.path));

      if (url && url.indexOf(`://${this.ossURL}`) > -1) {
        return this.domain ? url.replace(this.regexp, this.domain) : url;
      } else {
        return this.notfound;
      }
    } catch (err) {
      console.error(`Upload Image Error ${err}`);
      return this.notfound;
    }
  }
}

module.exports = AliOSSAdapter;

Content here supported configurations include:

{
  "storage": {
    "active": "ghost-aliyun-oss-store",
    "ghost-aliyun-oss-store": {
      "accessKeyId": "YOUR_ACCESS_KEY_ID",
      "accessKeySecret": "YOUR_ACCESS_SERCET",
      "bucket": "YOUR_BUCKET_NAME",
      "region": "oss-cn-beijing",
      "domain": "https://your-public-domian",
      "notfound": "https://s3-img.meituan.net/v1/mss_3d027b52ec5a4d589e68050845611e68/ff/n0/0k/4n/3s_73850.jpg"
    }
  }
}

Which domianis optional, if you need to use the CDN domain, configure in this field.

OSS package mirroring support plug-ins

In order to ensure the operation of mirroring performance is high enough, relatively small in size, we need to use docker multistage build program.

First define the base image, and install Ghost Aliyun OSS plugin just wrote.

FROM ghost:3.9.0-alpine as oss
LABEL maintainer="[email protected]"
WORKDIR $GHOST_INSTALL/current
RUN su-exec node yarn --verbose add ghost-aliyun-oss-store

Mirror operation is then used in the definition.

FROM ghost:3.9.0-alpine
LABEL maintainer="[email protected]"
COPY --chown=node:node --from=oss $GHOST_INSTALL/current/node_modules $GHOST_INSTALL/current/node_modules
RUN mkdir -p $GHOST_INSTALL/current/content/adapters/storage/
RUN echo "module.exports = require('ghost-aliyun-oss-store');" > $GHOST_INSTALL/current/content/adapters/storage/ghost-aliyun-oss-store.js

The previous two articles reference, if you want to solve the problem "can not be normal Chinese input" and extract the contents of the building, you can add the following in the mirror:

COPY ./docker-assets/admin-views  $GHOST_INSTALL/current/core/server/web/admin/views
COPY ./docker-assets/built/assets $GHOST_INSTALL/current/core/built/assets

If you do not want to configure a separate abstract file, you can add the following content.

RUN set -ex; \
    su-exec node ghost config storage.active ghost-aliyun-oss-store; \
    su-exec node ghost config storage.ghost-aliyun-oss-store.accessKeyId YOUR_ACCESS_KEY_ID; \
    su-exec node ghost config storage.ghost-aliyun-oss-store.accessKeySecret YOUR_ACCESS_SERCET; \
    su-exec node ghost config storage.ghost-aliyun-oss-store.bucket YOUR_BUCKET_NAME; \
    su-exec node ghost config storage.ghost-aliyun-oss-store.region oss-cn-beijing; \
    su-exec node ghost config storage.ghost-aliyun-oss-store.domain https://your-public-domian; \
    su-exec node ghost config storage.ghost-aliyun-oss-store.notfound https://s3-img.meituan.net/v1/mss_3d027b52ec5a4d589e68050845611e68/ff/n0/0k/4n/3s_73850.jpg; \
    su-exec node ghost config privacy.useUpdateCheck false; \
    su-exec node ghost config privacy.useGravatar false; \
    su-exec node ghost config privacy.useRpcPing false; \
    su-exec node ghost config privacy.useStructuredData false; \

Of course, in order to more easily update the content, abstracted as a separate file is a better option, as the following example the preparation of such config.production.jsonprofiles.

{
  "server": {
    "port": 2368,
    "host": "0.0.0.0"
  },
  "privacy": {
    "useUpdateCheck": false,
    "useGravatar": false,
    "useRpcPing": false,
    "useStructuredData": false
  },
  "storage": {
    "active": "ghost-aliyun-oss-store",
    "ghost-aliyun-oss-store": {
      "accessKeyId": "YOUR_ACCESS_KEY_ID",
      "accessKeySecret": "YOUR_ACCESS_SERCET",
      "bucket": "baai-news-upload",
      "region": "oss-cn-beijing",
      "domain": "https://your-public-domian",
      "notfound": "https://s3-img.meituan.net/v1/mss_3d027b52ec5a4d589e68050845611e68/ff/n0/0k/4n/3s_73850.jpg"
    }
  }
}

Full container layout file

The above we talked a lot of customization options, so a minimum of available container layout configuration is what it? In fact, probably less than ten lines, it is sufficient to meet our basic needs.

FROM ghost:3.9.0-alpine as oss
WORKDIR $GHOST_INSTALL/current
RUN su-exec node yarn --verbose add ghost-aliyun-oss-store

FROM ghost:3.9.0-alpine
LABEL maintainer="[email protected]"
COPY --chown=node:node --from=oss $GHOST_INSTALL/current/node_modules $GHOST_INSTALL/current/node_modules
RUN mkdir -p $GHOST_INSTALL/current/content/adapters/storage/
RUN echo "module.exports = require('ghost-aliyun-oss-store');" > $GHOST_INSTALL/current/content/adapters/storage/ghost-aliyun-oss-store.js

The above content is saved Dockerfile, if you need other features, refer to the contents of the above with appropriate modifications.

docker build -t soulteary/ghost-with-oss:3.9.0 -f Dockerfile .

Implementation of the above command, wait a moment, a is derived from the official Ghost Mirror, mirror on the support OSS container building is completed.

How to use the mirror

Here is a complete layout file for your reference, if you do not want to use Traefik, only need to port can be individually exposed.

As Traefik how to use, refer to my previous article , after the familiar, you will discover a new world.

version: "3.6"

services:

  ghost-with-oss:
    image: soulteary/ghost-with-oss:3.9.0
    expose:
      - 2368
    environment:
      url: https://ghost.lab.io
      database__client: mysql
      database__connection__host: ghost-db
      database__connection__port: 3306
      database__connection__user: root
      database__connection__password: ghost
      database__connection__database: ghost
      NODE_ENV: production
    volumes:
      # 这里参考前篇文章,或者本篇文章内容,选择性使用
      # 解决 Ghost 中文输入的问题
      # - ./docker-assets/built/assets:/var/lib/ghost/versions/current/core/built/assets:ro
      # - ./docker-assets/admin-views:/var/lib/ghost/current/core/server/web/admin/views:ro
      - ./config.production.json:/var/lib/ghost/config.production.json    
    extra_hosts:
      - "ghost.lab.io:127.0.0.1"
    networks:
      - traefik
    labels:
      - "traefik.enable=true"
      - "traefik.docker.network=traefik"
      - "traefik.http.routers.ghostweb.entrypoints=http"
      - "traefik.http.routers.ghostweb.middlewares=https-redirect@file"
      - "traefik.http.routers.ghostweb.rule=Host(`ghost.lab.io`)"
      - "traefik.http.routers.ghostssl.middlewares=content-compress@file"
      - "traefik.http.routers.ghostssl.entrypoints=https"
      - "traefik.http.routers.ghostssl.tls=true"
      - "traefik.http.routers.ghostssl.rule=Host(`ghost.lab.io`)"
      - "traefik.http.services.ghostbackend.loadbalancer.server.scheme=http"
      - "traefik.http.services.ghostbackend.loadbalancer.server.port=2368"

networks:
  traefik:
    external: true

The above content is saved docker-compose.yml, use docker-compose up -dto start the application, last visited domain name defined in the configuration to start using OSS to support this feature Ghost.

Of course, if you do not have online databases can also be used docker-composeto start a database:

version: '3'
services:

  db:
    image: mysql:5.7
    container_name: ghost-db
    expose:
      - 3306
    networks:
      - traefik
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: ghost
    volumes:
      - ./localdb:/var/lib/mysql

networks:
  traefik:
    external: true

At last

In this part, to encapsulate Ghost customized image based on a simple illustration of how an official mirror is extended and simple demonstration of Docker Multistage Build, and Aliyun OSS Ghost 3.x version of how to use.

Perhaps the next content will talk, Ghost does not support this type of application originally SSO single sign of how rapid access SSO.

--EOF

Guess you like

Origin yq.aliyun.com/articles/749679