Ghost是基于Node.js构建的开源博客平台。Ghost 具有易用的书写界面和体验,博客内容默认采用 Markdown 语法书写。由前 WordPress UI 部门主管 John O’Nolan 和 WordPress 高级工程师(女) Hannah Wolfe 创立,目的是为了给用户提供一种更加纯粹的内容写作与发布平台。
运行环境
本文使用传统安装方法,未使用从ghost1.x版本开始提供的安装工具ghost-cli
主机系统版本:CentOS 7.4
Ghost版本:Ghost-1.14.0
使用nginx反向代理ghost
数据库使用centos 7自带的mariadb
安装基础包
[root@localhost ~]# yum -y install bash-completion bash-completion-extras epel-release
安装nodejs
本文使用nodejs 长期支持LTS版
执行nodejs的nodejs脚本,这个脚本会自动在主机上面配置nodejs的yum仓库和软件包验证用的密钥
[root@localhost ~]# curl -sL https://rpm.nodesource.com/setup_7.x | sudo -E bash - #会生成一个 叫nodesource-el.repo的文件。
安装nodejs和依赖包
[root@localhost ~]# yum -y install gcc-c++ make nodejs #安装nodejs和其依赖包
使用cnpm替代npm,如果使用内地之外的服务器请忽略这一步。
[root@localhost ~]# npm install -g cnpm --registry=https://registry.npm.taobao.org #安装
淘宝的cnpm
配置mariadb数据库
安装
[root@localhost ~]# yum -y install mariadb-server
修改配置文件
[root@localhost ~]# vim /etc/my.cnf
[mysqld] #修改mysqld配置段
skip_name_resolve=on #跳过主机名解析
innodb_file_per_table=on #每个innodb表使用一个单独的文件储存
character-set-server=utf8 #修改mysql默认字符集为utf8
init_connect='SET NAMES utf8'
collation-server=utf8_general_ci
[client]
default-character-set=utf8 #强制客户端的字符集为utf8
[mysql]
default-character-set=utf8
启动mariadb并设置为开机启动
[root@localhost ~]# systemctl start mariadb #启动mariadb
[root@localhost ~]# systemctl enable mariadb #设置为开机自启动
执行mysql的安全脚本
[root@localhost ~]# mysql_secure_installation
Enter current password for root (enter for none): #输入root用户的密码,默认为空,直接回车。
OK, successfully used password, moving on...
Setting the root password ensures that nobody can log into the MariaDB
root user without the proper authorisation.
Set root password? [Y/n] Y #是否设置root密码
New password: #输入新密码
Re-enter new password: #再次输入新密码
Password updated successfully!
Reloading privilege tables..
... Success!
Remove anonymous users? [Y/n] Y #是否删除匿名账户
... Success!
Normally, root should only be allowed to connect from 'localhost'. This
ensures that someone cannot guess at the root password from the network.
Disallow root login remotely? [Y/n] Y #是否禁止root用户远程登陆
... Success!
By default, MariaDB comes with a database named 'test' that anyone can
access. This is also intended only for testing, and should be removed
before moving into a production environment.
Remove test database and access to it? [Y/n] Y #是否删除tset数据库
- Dropping test database...
... Success!
- Removing privileges on test database...
... Success!
Reloading the privilege tables will ensure that all changes made so far
will take effect immediately.
Reload privilege tables now? [Y/n] Y #是否保存上面的设置
... Success!
为Ghost创建数据库和用户
[root@localhost ~]# mysql -uroot -p
Enter password:
MariaDB [(none)]> CREATE DATABASE ghost; #创建一个叫ghost的库
MariaDB [(none)]> GRANT ALL PRIVILEGES ON ghost.* TO 'ghostblog'@'%' IDENTIFIED BY 'ghost123';
#创建为ghost用户授予ghost库的所有权限,如果没有这个用户将创建。
MariaDB [(none)]> FLUSH PRIVILEGES;
MariaDB [(none)]> quit
配置Ghost
下载Ghost压缩包,如果使用其他版本的ghost请去官网查找对应版本的压缩包下载路径
[root@localhost app]# mkdir /app #创建一个目录存放ghost文件
[root@localhost app]# cd /app
[root@localhost app]# wget -L https://github.com/TryGhost/Ghost/releases/download/1.20.3/Ghost-1.20.3.zip
解压软件
[root@localhost app]# yum -y install unzip #安装解压软件
[root@localhost app]# unzip Ghost-1.20.3.zip -d ghost
修改配置文件
[root@localhost ghost]# vim core/server/config/defaults.json
{
"url": "http://域名", //网站的域名
"server": {
"host": "127.0.0.1", //ghost服务的地址
"port": 2368 //服务端口
},
"privacy": false,
"useMinFiles": true,
"paths": {
"contentPath": "content/"
},
"storage": {
"active": "LocalFileStorage"
},
"scheduling": {
"active": "SchedulingDefault"
},
"logging": {
"level": "info",
"rotation": {
"enabled": true,
"period": "1d",
"count": 10
},
"transports": ["stdout"]
},
"spam": {
"user_login": {
"minWait": 600000,
"maxWait": 604800000,
"freeRetries": 4
},
"user_reset": {
"minWait": 3600000,
"maxWait": 3600000,
"lifetime": 3600,
"freeRetries": 4
},
"global_reset": {
"minWait": 3600000,
"maxWait": 3600000,
"lifetime": 3600,
"freeRetries":4
},
"global_block": {
"minWait": 3600000,
"maxWait": 3600000,
"lifetime": 3600,
"freeRetries":99
},
"private_block": {
"minWait": 3600000,
"maxWait": 3600000,
"lifetime": 3600,
"freeRetries":99
}
},
"caching": {
"frontend": {
"maxAge": 0
},
"301": {
"maxAge": 31536000
},
"customRedirects": {
"maxAge": 31536000
},
"favicon": {
"maxAge": 86400
},
"sitemap": {
"maxAge": 3600
},
"robotstxt": {
"maxAge": 3600000
}
}
}
修改工作模式
[root@localhost ghost]# vim core/server/config/index.js
var Nconf = require('nconf'),
path = require('path'),
_debug = require('ghost-ignition').debug._base,
debug = _debug('ghost:config'),
localUtils = require('./utils'),
env = process.env.NODE_ENV || 'development', #将development修改为production
_private = {};
设置数据库
[root@localhost ghost]# ls core/server/config/env/
config.development.json.bak config.production.json config.testing.json config.testing-mysql.json
[root@localhost ghost]# vim core/server/config/env/config.production.json
{
"database": {
"client": "mysql", //使用的数据库客户端
"connection": {
"host" : "127.0.0.1", //数据库IP地址
"user" : "ghostblog", //数据库用户名
"password" : "ghost123", //密码
"database" : "ghost", //数据库
"charset":"utf8"
}
"debug": "false"
},
"paths": {
"contentPath": "content/"
},
"logging": {
"level": "info",
"rotation": {
"enabled": true
},
"transports": ["file", "stdout"]
},
//配置邮件服务
"mail": {
"transport": "SMTP",
"options": {
"auth": {
"user": "", //邮箱账号
"pass": "" //邮箱密码
}
}
},
//对象云存储支持,ghost官方不支持使用对象存储,需要安装插件才能使用
//qn-store这款插件是给Ghost博客提供七牛云存储支持
"storage": {
"active": "qn-store",
"qn-store": {
"accessKey": "",
"secretKey": "",
"bucket": "", //bucket名称
"origin": "",
"fileKey": {
"safeString": "true",
"prefix": "YYYYMM/"
}
}
}
//upyun-ghost-store这款插件是给Ghost博客提供又拍云存储支持。
storage: {
active: 'upyun-ghost-store',
'upyun-ghost-store': {
bucket: 'my-bucket', //bucket名称
operator: 'somebody', //用户名
password: 'secret', //密码
domain: 'http://bucket.b0.upaiyun.com', //空间绑定的域名
filePath: '[blog]/YYYY/MM/' //文件远端保存地址
}
}
}
安装对象存储插件
在content文件夹中创建一个名为storage的文件夹将git仓库克隆至storage文件夹下
cd /app/ghost/content/storage
git clone https://github.com/Minwe/qn-store.git
安装依赖包
cd qn-store
npm instal
注:upyun-ghost-store的安装和qun-stor相同
安装
[root@localhost app]# cd ghost/
[root@localhost ghost]# ls
content Gruntfile.js LICENSE package.json README.md
core index.js MigratorConfig.js PRIVACY.md yarn.lock
[root@localhost ghost]# cnpm install --production #使用cnpm安装
初始化数据库
[root@localhost ghost]# cnpm install -g knex-migrator
[root@localhost ghost]# knex-migrator init
{ method: 'insert',
options: {},
timeout: false,
cancelOnTimeout: false,
bindings: [ '1.14', '1-og-twitter-post.js', '1.5' ],
__knexQueryUid: '02b804ea-5f0e-4d2e-892e-c7aff980fb3b',
sql: 'insert into `migrations` (`currentVersion`, `name`, `version`) values (?, ?, ?)' }
{ method: 'select',
options: {},
timeout: false,
cancelOnTimeout: false,
bindings: [ '1-add-backup-client.js' ],
__knexQueryUid: '79e0dbb6-8a65-4e1c-adfe-6c45f103bddc',
sql: 'select * from `migrations` where `name` = ?' }
{ method: 'insert',
options: {},
timeout: false,
cancelOnTimeout: false,
bindings: [ '1.14', '1-add-backup-client.js', '1.7' ],
__knexQueryUid: 'c9a885b5-5791-4edc-af46-8f78af0075d3',
sql: 'insert into `migrations` (`currentVersion`, `name`, `version`) values (?, ?, ?)' }
{ method: 'select',
options: {},
timeout: false,
cancelOnTimeout: false,
bindings: [ '1-add-permissions-redirect.js' ],
__knexQueryUid: '7b543633-99bf-48c9-bf46-6bbd11229183',
sql: 'select * from `migrations` where `name` = ?' }
{ method: 'insert',
options: {},
timeout: false,
cancelOnTimeout: false,
bindings: [ '1.14', '1-add-permissions-redirect.js', '1.9' ],
__knexQueryUid: 'f859c944-5c5c-4235-a8b0-e7c921631fa9',
sql: 'insert into `migrations` (`currentVersion`, `name`, `version`) values (?, ?, ?)' }
[2017-10-15 20:46:10] INFO Finished database init!
启动ghost
[root@localhost ghost]# node index.js
{ method: 'select',
options: {},
timeout: false,
cancelOnTimeout: false,
bindings: [ 'scheduled' ],
__knexQueryUid: 'acfa608c-1210-41b6-92fa-487c8c3674ca',
sql: 'select `id`, `published_at`, `created_at` from `posts` where `posts`.`status` = ?' }
[2017-10-15 12:46:38] INFO Ghost is running in production...
[2017-10-15 12:46:38] INFO Your blog is now available on http://域名:80/ #
[2017-10-15 12:46:38] INFO Ctrl+C to shut down #使用Ctrl+C 关闭ghost
[2017-10-15 12:46:38] INFO Ghost boot 2.01s #本次启动时间
查看主机监听的TCP端口
[root@localhost ~]# ss -ntl
State Recv-Q Send-Q Local Address:Port Peer Address:Port
LISTEN 0 128 *:80 *:*
LISTEN 0 128 *:22 *:*
LISTEN 0 128 127.0.0.1:2368 *:*
LISTEN 0 50 *:3306 *:*
LISTEN 0 128 :::80 :::*
LISTEN 0 128 :::22 :::*
配置nginx反向代理ghost
配置一个yum仓库,将仓库地址指向nginx官网
[root@localhost ~]# vim /etc/yum.repos.d/nginx.repo
[nginx]
name=nginx-repo
baseurl= http://nginx.org/packages/centos/$releasever/$basearch/
gpgcheck=0
enabled=1
安装nginx
[root@localhost ~]# yum -y install nginx
为ghost创建nginx配置文件
[root@localhost ~]# vim /etc/nginx/conf.d/ghost.conf
server {
listen 80 default_server;
listen [::]:80;
server_name www.guoziqiang.com;
location / {
proxy_set_header X-Real-IP $remote_addr; #定义一个请求首部,值为客户机的IP地址
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_pass http://127.0.0.1:2368;
}
}
启动nginx
[root@localhost ghost]# systemctl start nginx
[root@localhost ghost]# systemctl enable nginx
登陆
进入后台
使用pm2守护ghost进程
安装pm2
[root@localhost ghost]# npm install -g pm2
创建一个任务
[root@localhost ghost]# pm2 start index.js --name "ghost" NODE_ENV=production
-------------
__/\\\\\\\\\\\\\____/\\\\____________/\\\\____/\\\\\\\\\_____
_\/\\\/////////\\\_\/\\\\\\________/\\\\\\__/\\\///////\\\___
_\/\\\_______\/\\\_\/\\\//\\\____/\\\//\\\_\///______\//\\\__
_\/\\\\\\\\\\\\\/__\/\\\\///\\\/\\\/_\/\\\___________/\\\/___
_\/\\\/////////____\/\\\__\///\\\/___\/\\\________/\\\//_____
_\/\\\_____________\/\\\____\///_____\/\\\_____/\\\//________
_\/\\\_____________\/\\\_____________\/\\\___/\\\/___________
_\/\\\_____________\/\\\_____________\/\\\__/\\\\\\\\\\\\\\\_
_\///______________\///______________\///__\///////////////__
Production Process Manager for Node.js apps
with a built-in Load Balancer.
Start and Daemonize any application:
$ pm2 start app.js
Load Balance 4 instances of api.js:
$ pm2 start api.js -i 4
Monitor in production:
$ pm2 monitor
Make pm2 auto-boot at server restart:
$ pm2 startup
To go further checkout:
http://pm2.io
-------------
[PM2] Spawning PM2 daemon with pm2_home=/root/.pm2
[PM2] PM2 Successfully daemonized
[PM2] Starting /app/ghost/index.js in fork_mode (1 instance)
[PM2] Done.
[PM2][ERROR] script not found : /app/ghost/NODE_ENV=production
script not found : /app/ghost/NODE_ENV=production
┌──────────┬────┬──────┬──────┬────────┬─────────┬────────┬─────┬───────────┬──────┬──────────┐
│ App name │ id │ mode │ pid │ status │ restart │ uptime │ cpu │ mem │ user │ watching │
├──────────┼────┼──────┼──────┼────────┼─────────┼────────┼─────┼───────────┼──────┼──────────┤
│ ghost │ 0 │ fork │ 4533 │ online │ 0 │ 0s │ 99% │ 16.3 MB │ root │ disabled │
└──────────┴────┴──────┴──────┴────────┴─────────┴────────┴─────┴───────────┴──────┴──────────┘
保存任务并设置开机自启动
[root@localhost ghost]# pm2 save
[root@localhost ghost]# pm2 startup
查看任务
[root@localhost ghost]# pm2 list
┌──────────┬────┬──────┬──────┬────────┬─────────┬────────┬─────┬───────────┬──────┬──────────┐
│ App name │ id │ mode │ pid │ status │ restart │ uptime │ cpu │ mem │ user │ watching │
├──────────┼────┼──────┼──────┼────────┼─────────┼────────┼─────┼───────────┼──────┼──────────┤
│ ghost │ 0 │ fork │ 3114 │ online │ 0 │ 41s │ 0% │ 72.3 MB │ root │ disabled │
└──────────┴────┴──────┴──────┴────────┴─────────┴────────┴─────┴───────────┴──────┴──────────┘
Use `pm2 show <id|name>` to get more details about an app
查看某个任务的详细信息
[root@localhost ghost]# pm2 describe ghost #ghost为任务名,也可以写任务ID
Describing process with id 0 - name ghost
┌───────────────────┬───────────────────────────────────┐
│ status │ online │
│ name │ ghost │
│ restarts │ 0 │
│ uptime │ 2m │
│ script path │ /app/ghost/index.js │
│ script args │ N/A │
│ error log path │ /root/.pm2/logs/ghost-error-0.log │
│ out log path │ /root/.pm2/logs/ghost-out-0.log │
│ pid path │ /root/.pm2/pids/ghost-0.pid │
│ interpreter │ node │
│ interpreter args │ N/A │
│ script id │ 0 │
│ exec cwd │ /app/ghost │
│ exec mode │ fork_mode │
│ node.js version │ 6.11.4 │
│ watch & reload │ ✘ │
│ unstable restarts │ 0 │
│ created at │ 2017-10-15T13:57:25.418Z │
└───────────────────┴───────────────────────────────────┘
Code metrics value
┌────────────┬────────┐
│ Loop delay │ 2.53ms │
└────────────┴────────┘
Add your own code metrics: http://bit.ly/code-metrics
Use `pm2 logs ghost [--lines 1000]` to display logs
Use `pm2 monit` to monitor CPU and Memory usage ghost
启动pm2的web接口
[root@localhost ghost]# pm2 web
Launching web interface on 0.0.0.0:9615
[PM2][WARN] Applications pm2-http-interface not running, starting...
[PM2] App [pm2-http-interface] launched (1 instances)
[PM2] Process launched
┌────────────────────┬────┬──────┬──────┬────────┬─────────┬────────┬─────┬───────────┬──────┬──────────┐
│ App name │ id │ mode │ pid │ status │ restart │ uptime │ cpu │ mem │ user │ watching │
├────────────────────┼────┼──────┼──────┼────────┼─────────┼────────┼─────┼───────────┼──────┼──────────┤
│ ghost │ 0 │ fork │ 3114 │ online │ 0 │ 6m │ 0% │ 78.3 MB │ root │ disabled │
│ pm2-http-interface │ 1 │ fork │ 3249 │ online │ 0 │ 0s │ 90% │ 5.8 MB │ root │ disabled │
└────────────────────┴────┴──────┴──────┴────────┴─────────┴────────┴─────┴───────────┴──────┴──────────┘
Use `pm2 show <id|name>` to get more details about an app
pm2的些命令
$ pm2 start app.js # 启动app.js应用程序
$ pm2 start app.js -i 4 # cluster mode 模式启动4个app.js的应用实例,4个应用程序会自动进行负载均衡
$ pm2 start app.js --name="api" # 启动应用程序并命名为 "api"
$ pm2 start app.js --watch # 当文件变化时自动重启应用
$ pm2 start script.sh # 启动 bash 脚本
$ pm2 list # 列表 PM2 启动的所有的应用程序
$ pm2 monit # 显示每个应用程序的CPU和内存占用情况
$ pm2 show [app-name] # 显示应用程序的所有信息
$ pm2 logs # 显示所有应用程序的日志
$ pm2 logs [app-name] # 显示指定应用程序的日志
$ pm2 flush
$ pm2 web # 启动pm2的web 接口
$ pm2 stop all # 停止所有的应用程序
$ pm2 stop 0 # 停止 id为 0的指定应用程序
$ pm2 restart all # 重启所有应用
$ pm2 reload all # 重启 cluster mode下的所有应用
$ pm2 gracefulReload all # Graceful reload all apps in cluster mode
$ pm2 delete all # 关闭并删除所有应用
$ pm2 delete 0 # 删除指定应用 id 0
$ pm2 scale api 10 # 把名字叫api的应用扩展到10个实例
$ pm2 reset [app-name] # 重置重启数量
$ pm2 startup # 创建开机自启动命令
$ pm2 save # 保存当前应用列表
$ pm2 resurrect # 重新加载保存的应用列表
$ pm2 update # Save processes, kill PM2 and restore processes
$ pm2 generate # 生成一个示例json配置文件
$ pm2 info # 查看要给应用详细信息