简介
Ghost是一个流行的开源博客平台(Open source blogging platform),基于 Node.js 构建,博客内容默认采用 Markdown 语法书写,给用户提供一种更加纯粹的内容写作与发布平台。
Ghost的部署和运维需要一定的Web开发基础,利用Docker技术可以大大简化Ghost的部署和更新。Docker Hub上面也提供了Ghost官方镜像
使用Docker镜像,不懂得Node.Js的同学也可以分分钟在本地或阿里云容器服务上搭建起一个单节点的Ghost博客,但是它还有很多不足,比如缺省没有中文界面,无法简单的配置站点参数,基于SQLite数据库和本地文件的存储的方式只支持单节点部署,等等。
本文将通过一个具体的例子,向大家介绍如何基于中文的Ghost来构建一个优化过的Ghost镜像,使用MySQL和OSS对象存储作为存储后端;并利用阿里云容器服务的能力部署一个可配置、可伸缩的、高可用Ghost博客集群。
文章中的一些原理和技巧对构建其他类型的容器化应用也有借鉴意义。
本文中示例代码可以通过Github上面的代码仓库 https://github.com/AliyunContainerService/ghost-image 获得
优化Ghost镜像
Ghost 中文网 为国内用户提供了中文版的Ghost安装包,不但提供了中文汉化、还支持阿里云OSS等存储方式。感谢开源社区的贡献!
我们首先利用官方[Dockerfile](https://github.com/docker-library/ghost/blob/master/Dockerfile
)参考并构建一个针对阿里云和国内环境优化的Ghost Docker image.
我们会做几个改动
- 官方Ghost image基于Node.Js官方Docker镜像,由于Node.Js NPM仓库和Debian的软件源都在国外,对国内开发者而言,漫长的等待和超时错误大大影响我们的开发效率和心情。我们会切换到基于国内软件源镜像仓库的Node.Js image,能够大大提高Docker镜像构建速度减少错误。
- 官方Ghost image基于Ghost英文版,我们要替换成为最新的Ghost中文版
- 构建一个可伸缩、高可用的分布式系统而言,本地数据存储是最大的障碍。我们会增加对MySQL数据库和阿里云OSS文件存储的支持
- 一个设计良好的Docker Image可以方便的进行参数化配置,与其他Docker Image组装方便的构建微服务化的应用,我们会演示如何将Ghost镜像和MySQL容器通过容器连接关联在一起
好了,不说了上代码,下面代码基于官方Ghost的Dockerfile进行修改
Dockerfile
FROM registry.aliyuncs.com/acs-sample/node:4.2
RUN groupadd user && useradd --create-home --home-dir /home/user -g user user
RUN set -x \
&& apt-get update \
&& apt-get install -y --no-install-recommends curl ca-certificates \
&& rm -rf /var/lib/apt/lists/*
# grab gosu for easy step-down from root
# Add the gosu 1.7 from file to avoid timeout when fetching from Github
ADD gosu-amd64 /usr/local/bin/gosu
ENV GHOST_SOURCE /usr/src/ghost
WORKDIR $GHOST_SOURCE
ENV GHOST_VERSION 0.7.4
RUN buildDeps=' \
gcc \
make \
python \
unzip \
' \
&& set -x \
&& apt-get update && apt-get install -y $buildDeps --no-install-recommends || true && rm -rf /var/lib/apt/lists/* \
&& curl -sSL "http://dl.ghostchina.com/Ghost-${GHOST_VERSION}-zh-full.zip" -o ghost.zip \
&& unzip ghost.zip \
&& cnpm install --production \
&& apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false -o APT::AutoRemove::SuggestsImportant=false $buildDeps \
&& rm ghost.zip \
&& cnpm cache clean \
&& rm -rf /tmp/npm*
ENV GHOST_CONTENT /var/lib/ghost
RUN mkdir -p "$GHOST_CONTENT" && chown -R user:user "$GHOST_CONTENT"
VOLUME $GHOST_CONTENT
COPY docker-entrypoint.sh /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]
ENV NODE_ENV production
RUN chown -R user $GHOST_SOURCE
ADD config.example.js config.example.js
EXPOSE 2368
CMD ["npm", "start"]
和官方Ghost image相比,值得注意的地方是
- 把node:4.2-slim,替换为registry.aliyuncs.com/acs-sample/node:4.2,后一个Docker Image利用阿里云的Debian镜像和淘宝NPM仓库,可以加速image构建,非常适合大家开发使用。大家也可根据自己的实际情况参考Dockerfile定制自己的基础镜像
- 将设置uid/git的小工具gosu从Github下载转变为从本地文件添加,原因你懂得。
- 将Ghost下载地址转变为中文版路径: http://dl.ghostchina.com/Ghost-${GHOST_VERSION}-zh-full.zip
- 将npm命令替换为cnpm以使用淘宝NPM仓库
- 设置以production方式运行Ghost
- 用修改过config.example.js覆盖源配置示例文件,以支持参数化配置、MySQL/OSS存储支持和Container linking 支持
config.example.js 配置
// # Ghost Configuration
// Setup your Ghost install for various [environments](http://support.ghost.org/config/#about-environments).
// Ghost runs in `development` mode by default. Full documentation can be found at http://support.ghost.org/config/
var path = require('path'),
config;
config = {
// ### Production
// When running Ghost in the wild, use the production environment.
// Configure your URL and mail settings here
production: {
url: process.env.GHOST_URL || '',
mail: {},
// 配置MySQL 数据库
database: {
client: 'mysql',
connection: {
'user': process.env.GHOST_MYSQL_USER || process.env.MYSQL_ENV_MYSQL_USER || 'ghost',
'password': process.env.GHOST_MYSQL_PASSWORD || process.env.MYSQL_ENV_MYSQL_PASSWORD || 'changeme',
'host': process.env.GHOST_MYSQL_HOST || 'mysql',
'port': process.env.GHOST_MYSQL_PORT || 3306,
'database': process.env.GHOST_MYSQL_DATABASE || process.env.MYSQL_ENV_MYSQL_DATABASE || 'ghost_db',
charset : 'utf8'
}
},
server: {
host: '127.0.0.1',
port: '2368'
},
// 参考文档: http://www.ghostchina.com/ghost-0-5-5-chinese-edition-released/
storage: {
provider: 'oss',
bucketname: process.env.OSS_BUCKET || 'root',
ACCESS_KEY: process.env.OSS_ACCESS_KEY_ID || 'changeme',
SECRET_KEY: process.env.OSS_ACCESS_KEY_SECRET || 'changeme',
root: process.env.OSS_ROOT || '/image/',
endpoint: process.env.OSS_ENDPOINT || 'http://oss-cn-hangzhou.aliyuncs.com', //阿里云的上传端点是分地域的,需要单独设置
prefix: process.env.OSS_PREFIX || 'http://your-bucket-name.oss-cn-hangzhou.aliyuncs.com' //阿里云的 OSS HTTP 地址
},
paths: {
contentPath: process.env.GHOST_CONTENT
}
}
};
module.exports = config;
这里介绍一些帮助把容器变成可以参数化和可组合的一些方法。
如果大家希望自己构建Ghost镜像,可以使用下面的方法
git clone https://github.com/AliyunContainerService/ghost-image.git
cd ghost-image
docker build -t my-sample/ghost:0.74 .
或者直接利用阿里云Hub从Github自动构建的Docker镜像
docker pull registry.aliyuncs.com/acs-sample/ghost:0.7.4
利用阿里云容器服务部署Ghost集群
阿里云容器服务提供了非常简单的方法来部署容器化应用,支持简单的单镜像应用和使用Docker Compose模板定义的复合容器化应用。
本文为了构建Ghost集群,我们会使用Docker Compose模板描述一个由Ghost服务和MySQL服务构成的Ghost集群部署方案。
ghost:
image: registry.aliyuncs.com/acs-sample/ghost:0.7
links:
- db:mysql
ports:
- "2368"
environment:
- GHOST_URL=http://ghost.${ACS_DEFAULT_DOMAIN}
- OSS_BUCKET=acs-sample-ghost
- OSS_ACCESS_KEY_ID=***********
- OSS_ACCESS_KEY_SECRET=***********
- OSS_PREFIX=http://acs-sample-ghost.oss-cn-beijing.aliyuncs.com
- OSS_ENDPOINT=http://oss-cn-beijing.aliyuncs.com
labels:
aliyun.routing.port_2368: 'ghost'
aliyun.scale: '3'
restart: always
db:
image: registry.aliyuncs.com/acs-sample/mysql:5.7
environment:
- MYSQL_ROOT_PASSWORD=root-password
- MYSQL_DATABASE=blog
- MYSQL_USER=ghost
- MYSQL_PASSWORD=ghost_db_password
restart: always
注:
- 在具体实现中您需要根据自己的实际情况配置相应博客域名,OSS参数,MySQL参数等等。
- 关于OSS的使用,可以参见互联网上的资料使用OSS云存储服务为ghost网站静态资源加速
在Ghost的Docker Compose模板中,我们定义了两个服务
-
db服务:会基于
registry.aliyuncs.com/acs-sample/mysql:5.7
镜像创建容器,它就是一个官方的MySQL镜像没有任何修改。
-
ghost服务:会基于上文定义的registry.aliyuncs.com/acs-sample/ghost:0.7
镜像创建容器
- ghost服务通过mysql别名来连接db服务构造的容器,并通过环境变量来获得相应的数据库服务配置。值得注意的是,阿里云容器服务支持跨节点容器的链接。
- ghost服务包含了对OSS对象存储的配置
由于阿里云容器服务提供了一个的容器集群,我们可以方便的部署由多个实例构成的ghost服务,在阿里云容器服务提供在Docker Compose基础上的简单扩展,可以在部署时指定服务容器实例数量,这是由下面的label来定义的
aliyun.scale: '3'
由于Ghost需要配置博客站点的域名或IP地址,但在阿里云容器服务中,容器是根据节点使用情况和其他约束动态部署到集群上面的;为了让用户不必关系具体部署的节点IP,我们可以用使用阿里容器服务内置的web应用路由,将用户期望的域名和应用路由规则关联到一起。
在阿里云容器服务中,每个集群都有一个SLB服务对外提供Web服务。同时容器服务会为每个集群提供一个缺省域名绑定到集群的SLB上。比如下面一个label就是将用户的虚拟域名“ghost”和容器2368端口绑定到一起,这样所有访问 http://ghost.****.alicontainer.com
的请求都会转发给容器的2368端口。 如果用户提供了完整的全域名,比如“www.my-sample-ghost.com”,这样只要用户把域名绑定到SLB上也可以提供相同的路由转发能力。
aliyun.routing.port_2368: 'ghost'
如需了解如何将指定域名绑定到容器集群的SLB实例上请参见帮助文档
如需在Docker Compose模板中引用集群SLB的缺省域名,可以使用环境变量ACS_DEFAULT_DOMAIN
来获得,可以参考本文提供的实例。
关于阿里云容器服务对Docker Compose的支持和扩展,可以参见容器编排用户文档和标签说明
操作步骤
1. 创建Ghost编排模板
我们首先要创建一个Ghost编排模板

2. 部署Ghost应用
然后基于刚才创建的编排模板,部署blog应用

3. 查看Ghost容器应用
部署成功后,blog应用已经出现在应用列表中

我们可以下钻,看到Ghost应用已经有3个容器在运行了,这些容器会做服务的负载均衡,并保证应用的高可用性;我们可以简单的访问服务的访问端点来直接测试我们新建的博客站点

4. 访问Ghost应用
经过简单的配置Ghost,一个新的博客系统成功部署在阿里云上了。注意我们所有用户上传的图片都会保存到阿里云的OSS上,不但可以在多个Ghost博客实例中共享文件存储,还可以利用OSS大大节省用户存储成本和网络带宽。

总结
本文利用Ghost中文化应用描述了一个利用容器服务构建和部署云原生应用的一些实践。几个要点如下:
设计层面 :我们应该分离容器应用的业务逻辑和持久化状态。把应用所需的持久化服务资源,通过配置或者服务连接的方式在部署时绑定具体服务。这样业务逻辑(比如Ghost服务)可以是无状态的服务,可以弹性伸缩和动态迁移;另外通过部署的解耦,可以在在开发,测试、生成环境使用不同的服务实例,提升了对复用和灵活性。我们利用12 factor应用的一些建议结合Docker容器的实践做了一些示例,将Ghost持久化数据库访问、文件存储让外部的MySQL和OSS对象存储来完成,并可以方便的通过环境变量和容器连接进行配置和服务绑定。
构建层面 :利用Dockerfile来进行容器化应用的打包和版本管理是最佳实践。在实践中,对于中国特色的网络情况,我们可以利用国内的镜像站点加速镜像构建效率。
部署层面 :容器化部署运维已经逐渐被人们接受。然而动态的管理大量容器应用,也给开发和运维团队带来新的挑战。阿里云容器服务是为解决上述问题出现的。可以直接利用Docker image和Compose模板来部署容器化服务,管理容器服务的生命周期。同时提供了一些基础服务,比如支持Web应用的弹性路由服务,可以简单的将动态的容器化web应用暴露出用户可访问的地址。
想了解更多容器服务的内容,请点击 https://www.aliyun.com/product/containerservice