一次docker镜像的解耦--onlyoffice
栏目: 数据库 · PostgreSQL · 发布时间: 5年前
内容简介:年初的时候就考虑过搭建个在线编辑excel的,之前试过很多这几天又有需求多人同时在线编辑office文件,同事找了个onlyoffice的用docker跑起来了. dockerhub上看了下镜像挺大的而且没有跳转到github上Dockerfile的超链接于是便有了解耦这个镜像的冲动
年初的时候就考虑过搭建个在线编辑excel的,之前试过很多 owncloud,seafile,nextcloud,kodexplorer
基本都是纯网盘或者不免费
这几天又有需求多人同时在线编辑office文件,同事找了个onlyoffice的用 docker 跑起来了. dockerhub上看了下镜像挺大的而且没有跳转到github上Dockerfile的超链接
稍微用docker history看了下发现里面一堆各种数据库都放同一个镜像里,根本没有解耦
于是便有了解耦这个镜像的冲动
先找官方的例子跑跑看
地址: https://api.onlyoffice.com/editors/demopreview
看了下官方都是js写的,所以选了个Nodejs的demo来跑,另外之前用docker帮过同学搭建过一个nodejs的小程序后台环境,对nodejs项目部署有点经验
选择 Node.js Example
解压后,修改配置 config/default.jsonm
,siteUrl 为 DocumentServer的地址
一个标准的nodejs项目根目录有package.json文件,里面有模块依赖
npm install
安装模块
node bin/www
即可运行起来
之前帮同学搭建小程序环境(同学是前端,所以用的nodejs写前后端)自己做了个nodejs的镜像,适合测试,每次主进程跑起来之前都会 npm install
,这里用我自己的镜像跑(后期npm的模块可以固化到镜像里,这里仅供快速搭建使用方便)
先简单起来一个试试 documentserver
docker run -d -p 9980:80 onlyoffice/documentserver
然后把修改过后整个demo挂载到镜像的 WORKDIR
跑起来
然后在官方的仓库找到了Dockerfile,好吧,不用去看 docker history --no-trunc
了
看了下Dockerfile整体过程是下面:
添加nodejs的源用于安装nodejs,安装 nodejs,postgres,redis-server,rabbitmq-server,supervisor
然后entrypoint脚本逻辑
https://github.com/ONLYOFFICE/Docker-DocumentServer/blob/master/run-document-server.sh 开始执行的是一堆目录的 mkdir,chown
然后根据变量值执行,假设此时符合条件进去
ONLYOFFICE_DATA_CONTAINER_HOST=${ONLYOFFICE_DATA_CONTAINER_HOST:-localhost} ... if [ ${ONLYOFFICE_DATA_CONTAINER_HOST} = "localhost" ]; then
然后执行函数 read_setting
里面是读取文件 /etc/onlyoffice/documentserver/local.json
的值
{ "services": { "CoAuthoring": { "sql": { "dbHost": "localhost", "dbName": "onlyoffice", "dbUser": "onlyoffice", "dbPass": "onlyoffice" }, "redis": { "host": "localhost" }, "token": { "enable": { "request": { "inbox": false, "outbox": false }, "browser": false }, "inbox": { "header": "Authorization" }, "outbox": { "header": "Authorization" } }, "secret": { "inbox": { "string": "secret" }, "outbox": { "string": "secret" }, "session": { "string": "secret" } } } }, "rabbitmq": { "url": "amqp://guest:guest@localhost" } }
如下,变量为空就使用文件里的值
POSTGRESQL_SERVER_HOST=${POSTGRESQL_SERVER_HOST:-$(${JSON} services.CoAuthoring.sql.dbHost)} POSTGRESQL_SERVER_PORT=${POSTGRESQL_SERVER_PORT:-5432} POSTGRESQL_SERVER_DB_NAME=${POSTGRESQL_SERVER_DB_NAME:-$(${JSON} services.CoAuthoring.sql.dbName)} POSTGRESQL_SERVER_USER=${POSTGRESQL_SERVER_USER:-$(${JSON} services.CoAuthoring.sql.dbUser)} POSTGRESQL_SERVER_PASS=${POSTGRESQL_SERVER_PASS:-$(${JSON} services.CoAuthoring.sql.dbPass)} RABBITMQ_SERVER_URL=${RABBITMQ_SERVER_URL:-$(${JSON} rabbitmq.url)} parse_rabbitmq_url REDIS_SERVER_HOST=${REDIS_SERVER_HOST:-$(${JSON} services.CoAuthoring.redis.host)} REDIS_SERVER_PORT=${REDIS_SERVER_PORT:-6379} DS_LOG_LEVEL=${DS_LOG_LEVEL:-$(${JSON_LOG} levels.nodeJS)}
parse_rabbitmq_url函数从变量 local.json
里的 rabbitmq.url
值分离成下面
RABBITMQ_SERVER_HOST=$host RABBITMQ_SERVER_USER=$user RABBITMQ_SERVER_PASS=$pass RABBITMQ_SERVER_PORT=$port
也就是把 amqp://guest:guest@localhost
分离成
amqp://${用户名}:${密码}@${host}:${port:=5672}
函数 update_log_settings
修改 $DS_LOG_LEVEL
,缺省值为文件的 /etc/onlyoffice/documentserver/log4js/production.json
的 levels.nodeJS
{ "appenders": [ { "type": "console", "layout": { "type": "pattern", "pattern": "[%d] [%p] %c - %.10000m" } } ], "replaceConsole": "true", "levels": { "nodeJS": "WARN" } }
然后执行函数 update_jwt_settings
满足条件就
if [ "${JWT_ENABLED}" == "true" ]
JWT_ENABLED
缺省为false
然后改local.json里的对应值,自行看脚本,这里不啰嗦,而且我没用jwt
接下来pgsql部分,数据存放变量为下
PG_ROOT=/var/lib/postgresql PG_VERSION=9.5 PG_NAME=main PGDATA=${PG_ROOT}/${PG_VERSION}/${PG_NAME}
POSTGRESQL_SERVER_HOST
缺省用local.json文件里的 .services.CoAuthoring.sql.dbhost
if [ ${POSTGRESQL_SERVER_HOST} != "localhost" ]; then update_postgresql_settings waiting_for_postgresql create_postgresql_tbl
如果 POSTGRESQL_SERVER_HOST
不是localhost就用local.json文件里的 .services.CoAuthoring.sql.dbhost
此时是使用外部的pgsql,有等待端口的函数
waiting_for_connection(){ until nc -z -w 3 "$1" "$2"; do >&2 echo "Waiting for connection to the $1 host on port $2" sleep 1 done }
外部pgsql数据库就绪后就执行db初始化的sql,sql文件位置 /var/www/onlyoffice/documentserver/server/schema/postgresql/createdb.sql
-- -- Create schema onlyoffice -- -- CREATE DATABASE onlyoffice ENCODING = 'UTF8' CONNECTION LIMIT = -1; -- ---------------------------- -- Table structure for doc_changes -- ---------------------------- CREATE TABLE IF NOT EXISTS "public"."doc_changes" ( "id" varchar(255) COLLATE "default" NOT NULL, "change_id" int4 NOT NULL, "user_id" varchar(255) COLLATE "default" NOT NULL, "user_id_original" varchar(255) COLLATE "default" NOT NULL, "user_name" varchar(255) COLLATE "default" NOT NULL, "change_data" text COLLATE "default" NOT NULL, "change_date" timestamp without time zone NOT NULL, PRIMARY KEY ("id", "change_id") ) WITH (OIDS=FALSE); -- ---------------------------- -- Table structure for task_result -- ---------------------------- CREATE TABLE IF NOT EXISTS "public"."task_result" ( "id" varchar(255) COLLATE "default" NOT NULL, "status" int2 NOT NULL, "status_info" int4 NOT NULL, "last_open_date" timestamp without time zone NOT NULL, "user_index" int4 NOT NULL DEFAULT 1, "change_id" int4 NOT NULL DEFAULT 0, "callback" text COLLATE "default" NOT NULL, "baseurl" text COLLATE "default" NOT NULL, PRIMARY KEY ("id") ) WITH (OIDS=FALSE); CREATE OR REPLACE FUNCTION merge_db(_id varchar(255), _status int2, _status_info int4, _last_open_date timestamp without time zone, _user_index int4, _change_id int4, _callback text, _baseurl text, OUT isupdate char(5), OUT userindex int4) AS $$ DECLARE t_var "public"."task_result"."user_index"%TYPE; BEGIN LOOP -- first try to update the key -- note that "a" must be unique UPDATE "public"."task_result" SET last_open_date=_last_open_date, user_index=user_index+1 WHERE id = _id RETURNING user_index into userindex; IF found THEN isupdate := 'true'; RETURN; END IF; -- not there, so try to insert the key -- if someone else inserts the same key concurrently, -- we could get a unique-key failure BEGIN INSERT INTO "public"."task_result"(id, status, status_info, last_open_date, user_index, change_id, callback, baseurl) VALUES(_id, _status, _status_info, _last_open_date, _user_index, _change_id, _callback, _baseurl) RETURNING user_index into userindex; isupdate := 'false'; RETURN; EXCEPTION WHEN unique_violation THEN -- do nothing, and loop to try the UPDATE again END; END LOOP; END; $$ LANGUAGE plpgsql;
如果此时使用的是容器内的pgsql则
chown -R postgres:postgres ${PG_ROOT} chmod -R 700 ${PG_ROOT}
不存在目录 PGDATA
就
local pg_conf_dir=/etc/postgresql/${PG_VERSION}/${PG_NAME} local postgresql_conf=$pg_conf_dir/postgresql.conf local hba_conf=$pg_conf_dir/pg_hba.conf mv $postgresql_conf $postgresql_conf.backup mv $hba_conf $hba_conf.backup pg_createcluster ${PG_VERSION} ${PG_NAME} PG_NEW_CLUSTER=true
rabbitmq一样, RABBITMQ_SERVER_HOST
值不是localhost就使用外部的,是localhost此时就 rm -rf /var/run/rabbitmq
来保证startup after container kill
redis的host不是localhost就使用
${JSON} -I -e "this.services.CoAuthoring.redis.host = '${REDIS_SERVER_HOST}'" ${JSON} -I -e "this.services.CoAuthoring.redis.port = '${REDIS_SERVER_PORT}'"
上面对比文件的json对不上,怀疑此处写错了.而且 redis 没设置密码,解耦的话应该改下onlyoffice的客户端连接参数加上密码
ONLYOFFICE_DATA_CONTAINER_HOST
上面都是符合条件就是localhost, 此时进else的话就是执行到行316行函数 waiting_for_datacontainer
just wait for remote data
read_setting
看了下默认的80端口是nginx占据的,此处应该是webserver
上面如果是localhost就会在容器里启动 pgsql,rabbitmq,redis
然后下面这段代码
if [ ${PG_NEW_CLUSTER} = "true" ]; then create_postgresql_db create_postgresql_tbl fi
如果PG是初始化的(也就是前面的一开始不存在目录 PGDATA
)
create_postgresql_db(){ sudo -u postgres psql -c "CREATE DATABASE onlyoffice;" sudo -u postgres psql -c "CREATE USER onlyoffice WITH password 'onlyoffice';" sudo -u postgres psql -c "GRANT ALL privileges ON DATABASE onlyoffice TO onlyoffice;" }
create_postgresql_tbl
就是上面的sql /var/www/onlyoffice/documentserver/server/schema/postgresql/createdb.sql
初始化pgsql
然后下面这段
if [ ${ONLYOFFICE_DATA_CONTAINER} != "true" ]; then waiting_for_postgresql waiting_for_rabbitmq waiting_for_redis update_nginx_settings update_supervisor_settings service supervisor start # start cron to enable log rotating service cron start fi
这里的 ONLYOFFICE_DATA_CONTAINER
值由于没有接触过onlyoffice不知道这个值干啥的
waiting开头的都是等服务端口起来
update_nginx_settings
看了下是修改nginx的配置文件(/etc/nginx/nginx.conf),否则改为缺省,下面只说缺省修改
-
worker_processes
开头的值修改环境变量,缺省为1 -
worker_connections
缺省改为$(ulimit -n)
-
access_log.*
改为access_log off;
(此处关掉access.log) - 如果存在文件
SSL_CERTIFICATE_PATH
(缺省为/var/www/onlyoffice/Data/certs/onlyoffice.crt
)和文件SSL_KEY_PATH
(缺省为/var/www/onlyoffice/Data/certs/onlyoffice.key
) - 接上: 就复制文件
/etc/onlyoffice/documentserver/nginx/onlyoffice-documentserver-ssl.conf.template
到$NGINX_ONLYOFFICE_CONF
(缺省为/etc/onlyoffice/documentserver/nginx/onlyoffice-documentserver.conf
) - 这里后面注意下,因为上一步是复制了文件的,后面可以直接对着template文件看
- 接上面的ssl,修改文件
$NGINX_ONLYOFFICE_CONF
里为
$SSL_CERTIFICATE_PATH
- 同上修改
- 开启http2,即替换
443 ssl
成443 ssl http2
- 如果
$SSL_DHPARAM_PATH
(缺省/var/www/onlyoffice/Data/certs/dhparam.pem
)可读把行# ssl_dhparam /etc/ssl/certs/dhparam.pem;
取消注释,不可读就删除这行 - 修改包含”ssl_verify_client”行后面的值为
${SSL_VERIFY_CLIENT}
-
${CA_CERTIFICATES_PATH}
存在在行”ssl_verify_client”下一行插入ssl_client_certificate ${CA_CERTIFICATES_PATH}
- 如果
$ONLYOFFICE_HTTPS_HSTS_ENABLED
为true修改max-age=31536000
的值改为$ONLYOFFICE_HTTPS_HSTS_MAXAGE
,不为true则删除包含max-age的行 - 以上逻辑都是第四条的存在俩文件,如果不存在俩文件则软连接
${NGINX_ONLYOFFICE_PATH}/onlyoffice-documentserver.conf.template
到${NGINX_ONLYOFFICE_PATH}/onlyoffice-documentserver.conf
,也就是此时不适用ssl - 下面不包含在上面的存在文件代码块里
- 存在文件
$NGINX_ONLYOFFICE_EXAMPLE_CONF
修改它里面的”linux”改成”docker” -
update_supervisor_settings
就是把supervisor的配置文件复制到它默认工作目录去 - 然后启动supervisor和cron
- 下面是349行后面全局执行的逻辑
- 启动nginx,这里想不通为啥跑个nginx,后面看看能看懂原因不
- 最后的
documentserver-generate-allfonts.sh
和documentserver-static-gzip.sh
一个是字体一个是压缩备份
整体思路有了,但是发现documentserver似乎好几个组件一起被supervisor跑的
root@918cd6b41d37:/etc/supervisor/conf.d# ll total 0 drwxr-xr-x 1 root root 279 Oct 26 15:16 ./ drwxr-xr-x 1 root root 30 Oct 26 08:31 ../ lrwxrwxrwx 1 root root 73 Oct 26 15:09 onlyoffice-documentserver.conf -> ../../onlyoffice/documentserver/supervisor/onlyoffice-documentserver.conf lrwxrwxrwx 1 root root 83 Oct 26 15:09 onlyoffice-documentserver-converter.conf -> ../../onlyoffice/documentserver/supervisor/onlyoffice-documentserver-converter.conf lrwxrwxrwx 1 root root 84 Oct 26 15:09 onlyoffice-documentserver-docservice.conf -> ../../onlyoffice/documentserver/supervisor/onlyoffice-documentserver-docservice.conf lrwxrwxrwx 1 root root 76 Oct 26 15:09 onlyoffice-documentserver-gc.conf -> ../../onlyoffice/documentserver/supervisor/onlyoffice-documentserver-gc.conf lrwxrwxrwx 1 root root 81 Oct 26 15:09 onlyoffice-documentserver-metrics.conf -> ../../onlyoffice/documentserver/supervisor/onlyoffice-documentserver-metrics.conf lrwxrwxrwx 1 root root 86 Oct 26 15:09 onlyoffice-documentserver-spellchecker.conf -> ../../onlyoffice/documentserver/supervisor/onlyoffice-documentserver-spellchecker.conf
所以entrypoint里启动supervisor的时候会启动onlyoffice
镜像里依赖安装太多了,然后找到了非docker安装的文档稍微清晰了些
https://helpcenter.onlyoffice.com/server/linux/document/index.aspx
安装了 onlyoffice-documentserver
后发现大体文件一样,解包rpm后更直观
yum install onlyoffice-documentserver
整体思路有了,documentserver的源码都放一起,里面几个组建都是supervisor跑
源码是js的,所以这几个组建分离解耦的话必须在nodejs作为基础镜像(so依赖啥的不知道解决起来麻烦不)
然后一些数据库啥的可以直接各自的官方镜像稍微挂载初始化 sql 啥的
由于对于nodejs不熟悉,同学上班也忙,无法帮我拆分项目,项目结构也不熟悉
目前是打算是把nginx,supervisor(来管理这些组件)放同一个镜像算了,感觉documentserver拆分得太散没必要
看了官方文档的环境要求介绍
Software requirements For Debian, Ubuntu and derivatives OS 64-bit Debian, Ubuntu or other compatible distribution with kernel version 3.13 or later Additional Requirements PostgreSQL: version 9.1 or later NGINX: version 1.3.13 or later Node.js: version 8.12.0 or later libstdc++6: version 4.8.4 or later Redis RabbitMQ For RHEL, CentOS and derivatives OS RHEL 7 or CentOS 7 Additional Requirements PostgreSQL: version 9.1 or later NGINX: version 1.3.13 or later Node.js: version 8.12.0 or later Redis RabbitMQ
发现官方源码clone下来居然1.7G,想了下还是ubuntu作为基础镜像,像官方那样去包管理去安装算了
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
游戏运营:高手进阶之路
饭大官人 / 电子工业出版社 / 2018-1-1 / 79.00元
《游戏运营:高手进阶之路》是一本系统的、成体系的、注重运营效能、强化系统思维、提升专业认知的书籍。《游戏运营:高手进阶之路》几乎完整覆盖了一个游戏运营人员日常工作中的方方面面,并从工作中具体的业务场景出发,归纳整理出各种解决问题的方法论。《游戏运营:高手进阶之路》为广大游戏从业者建立了完整的知识技能成长体系,包含两大岗位基本功—内容输出和协作推进,四大职业技能—活动策划、版本管理、用户运营、数据分......一起来看看 《游戏运营:高手进阶之路》 这本书的介绍吧!