From f3e78423f8fd1a21ca2f5413e1820da78bb0ca95 Mon Sep 17 00:00:00 2001 From: Marvin Zhang Date: Sun, 28 Jul 2019 15:27:44 +0800 Subject: [PATCH] updated Dockerfile --- .dockerignore | 3 +- Dockerfile | 72 +-- backend/conf/config.yml | 8 +- backend/model/node.go | 6 +- backend/services/node.go | 21 +- backend/services/task.go | 9 +- docker_init.sh | 24 +- .../layout/components/Sidebar/index.vue" | 75 ++++ "frontend\032src/views/login/index.vue" | 411 ++++++++++++++++++ frontend/.dockerignore | 1 + frontend/.env.production | 2 +- frontend/Dockerfile | 23 + frontend/conf/crawlab.conf | 5 + frontend/src/assets/logo.png | Bin 0 -> 27484 bytes .../src/views/layout/components/Navbar.vue | 15 +- 15 files changed, 608 insertions(+), 67 deletions(-) create mode 100644 "frontend\032src/views/layout/components/Sidebar/index.vue" create mode 100644 "frontend\032src/views/login/index.vue" create mode 100644 frontend/.dockerignore create mode 100644 frontend/Dockerfile create mode 100644 frontend/conf/crawlab.conf create mode 100644 frontend/src/assets/logo.png diff --git a/.dockerignore b/.dockerignore index 5900766d7..4207a9513 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,4 +1,5 @@ .idea logs *.log -node_modules/ \ No newline at end of file +dist/ +**/node_modules/ \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 3b07591da..fc10b586d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,45 +1,59 @@ +FROM golang:1.12 AS backend-build + +WORKDIR /go/src/app +COPY ./backend . + +ENV GO111MODULE on +ENV GOPROXY https://mirrors.aliyun.com/goproxy/ + +RUN go install -v ./... + +FROM node:8.16.0-alpine AS frontend-build + +ADD ./frontend /app +WORKDIR /app + +# install frontend +RUN npm install -g yarn && yarn install --registry=https://registry.npm.taobao.org + +RUN npm run build:prod + # images FROM ubuntu:latest -# source files -ADD . /opt/crawlab +ADD . /app # set as non-interactive ENV DEBIAN_FRONTEND noninteractive -# environment variables -ENV NVM_DIR /usr/local/nvm -ENV NODE_VERSION 8.12.0 -ENV WORK_DIR /opt/crawlab - -# install pkg +# install packages RUN apt-get update \ - && apt-get install -y curl git net-tools iputils-ping ntp nginx python3 python3-pip \ - && apt-get clean \ - && cp $WORK_DIR/crawlab.conf /etc/nginx/conf.d \ + && apt-get install -y curl git net-tools iputils-ping ntp ntpdate python3 python3-pip \ && ln -s /usr/bin/pip3 /usr/local/bin/pip \ && ln -s /usr/bin/python3 /usr/local/bin/python -# install nvm -RUN curl https://raw.githubusercontent.com/creationix/nvm/v0.24.0/install.sh | bash \ - && . $NVM_DIR/nvm.sh \ - && nvm install v$NODE_VERSION \ - && nvm use v$NODE_VERSION \ - && nvm alias default v$NODE_VERSION -ENV NODE_PATH $NVM_DIR/versions/node/v$NODE_VERSION/lib/node_modules -ENV PATH $NVM_DIR/versions/node/v$NODE_VERSION/bin:$PATH +# install backend +RUN pip install scrapy pymongo bs4 requests -i https://pypi.tuna.tsinghua.edu.cn/simple -# install frontend -RUN npm install -g yarn \ - && cd /opt/crawlab/frontend \ - && yarn install +# copy backend files +COPY --from=backend-build /go/src/app . +COPY --from=backend-build /go/bin/crawlab /usr/local/bin -# install backend -RUN pip install -U setuptools -i https://pypi.tuna.tsinghua.edu.cn/simple \ - && pip install -r /opt/crawlab/crawlab/requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple +# install nginx +RUN apt-get -y install nginx -# start backend +# copy frontend files +COPY --from=frontend-build /app/dist /app/dist +COPY --from=frontend-build /app/conf/crawlab.conf /etc/nginx/conf.d + +# working directory +WORKDIR /app/backend + +# frontend port EXPOSE 8080 + +# backend port EXPOSE 8000 -WORKDIR /opt/crawlab -ENTRYPOINT ["/bin/sh", "/opt/crawlab/docker_init.sh"] \ No newline at end of file + +# start backend +CMD ["/bin/sh", "/app/docker_init.sh"] \ No newline at end of file diff --git a/backend/conf/config.yml b/backend/conf/config.yml index 0d575bfc1..3e7aa234d 100644 --- a/backend/conf/config.yml +++ b/backend/conf/config.yml @@ -1,7 +1,11 @@ +api: + address: "localhost:8000" mongo: host: localhost port: 27017 db: crawlab_test + username: "" + password: "" redis: network: tcp address: "localhost:6379" @@ -11,10 +15,10 @@ log: server: host: 0.0.0.0 port: 8000 - master: "Y" + master: "N" secret: "crawlab" spider: - path: "/Users/yeqing/projects/crawlab/spiders" + path: "/app/spiders" task: workers: 4 other: diff --git a/backend/model/node.go b/backend/model/node.go index 7bdffb1c7..01bbd3f71 100644 --- a/backend/model/node.go +++ b/backend/model/node.go @@ -21,8 +21,9 @@ type Node struct { // 前端展示 IsMaster bool `json:"is_master"` - UpdateTs time.Time `json:"update_ts" bson:"update_ts"` - CreateTs time.Time `json:"create_ts" bson:"create_ts"` + UpdateTs time.Time `json:"update_ts" bson:"update_ts"` + CreateTs time.Time `json:"create_ts" bson:"create_ts"` + UpdateTsUnix int64 `json:"update_ts_unix" bson:"update_ts_unix"` } func (n *Node) Save() error { @@ -40,6 +41,7 @@ func (n *Node) Add() error { defer s.Close() n.Id = bson.NewObjectId() n.UpdateTs = time.Now() + n.UpdateTsUnix = time.Now().Unix() n.CreateTs = time.Now() if err := c.Insert(&n); err != nil { debug.PrintStack() diff --git a/backend/services/node.go b/backend/services/node.go index 0c2c95276..90f941e4b 100644 --- a/backend/services/node.go +++ b/backend/services/node.go @@ -16,10 +16,11 @@ import ( ) type Data struct { - Mac string `json:"mac"` - Ip string `json:"ip"` - Master bool `json:"master"` - UpdateTs time.Time `json:"update_ts"` + Mac string `json:"mac"` + Ip string `json:"ip"` + Master bool `json:"master"` + UpdateTs time.Time `json:"update_ts"` + UpdateTsUnix int64 `json:"update_ts_unix"` } type NodeMessage struct { @@ -193,9 +194,8 @@ func UpdateNodeStatus() { } // 如果记录的更新时间超过60秒,该节点被认为离线 - if time.Now().Sub(data.UpdateTs) > 60*time.Second { + if time.Now().Unix()-data.UpdateTsUnix > 60 { // 在Redis中删除该节点 - if err := database.RedisClient.HDel("nodes", data.Mac); err != nil { log.Errorf(err.Error()) return @@ -284,10 +284,11 @@ func UpdateNodeData() { // 构造节点数据 data := Data{ - Mac: mac, - Ip: ip, - Master: IsMaster(), - UpdateTs: time.Now(), + Mac: mac, + Ip: ip, + Master: IsMaster(), + UpdateTs: time.Now(), + UpdateTsUnix: time.Now().Unix(), } // 注册节点到Redis diff --git a/backend/services/task.go b/backend/services/task.go index 51b596a7c..4b75a0dad 100644 --- a/backend/services/task.go +++ b/backend/services/task.go @@ -124,10 +124,15 @@ func ExecuteShellCmd(cmdStr string, cwd string, t model.Task, s model.Spider) (e cmd.Stdout = fLog cmd.Stderr = fLog - // 添加环境变量 + // 添加默认环境变量 cmd.Env = append(cmd.Env, "CRAWLAB_TASK_ID="+t.Id) cmd.Env = append(cmd.Env, "CRAWLAB_COLLECTION="+s.Col) + // 添加任务环境变量 + for _, env := range s.Envs { + cmd.Env = append(cmd.Env, env.Name + "=" + env.Value) + } + // 起一个goroutine来监控进程 ch := TaskExecChanMap.ChanBlocked(t.Id) go func() { @@ -393,7 +398,7 @@ func GetTaskLog(id string) (logStr string, err error) { } logStr = "" - if IsMaster() { + if IsMasterNode(task.NodeId.Hex()) { // 若为主节点,获取本机日志 logBytes, err := GetLocalLog(task.LogPath) logStr = string(logBytes) diff --git a/docker_init.sh b/docker_init.sh index 2404580cf..587e8af32 100755 --- a/docker_init.sh +++ b/docker_init.sh @@ -1,16 +1,10 @@ #!/bin/sh -case $1 in - master) - cd $WORK_DIR/frontend \ - && npm run build:prod \ - && service nginx start - python $WORK_DIR/crawlab/flower.py >> /opt/crawlab/flower.log 2>&1 & - python $WORK_DIR/crawlab/worker.py >> /opt/crawlab/worker.log 2>&1 & - cd $WORK_DIR/crawlab \ - && gunicorn --log-level=DEBUG -b 0.0.0.0 -w 8 app:app - ;; - worker) - python $WORK_DIR/crawlab/app.py >> /opt/crawlab/app.log 2>&1 & - python $WORK_DIR/crawlab/worker.py - ;; -esac \ No newline at end of file + +# replace default api path to new one +jspath=`ls /app/dist/js/app.*.js` +cat ${jspath} | sed "s/localhost:8000/${CRAWLAB_API_ADDRESS}/g" > ${jspath} + +# start nginx +service nginx start + +crawlab \ No newline at end of file diff --git "a/frontend\032src/views/layout/components/Sidebar/index.vue" "b/frontend\032src/views/layout/components/Sidebar/index.vue" new file mode 100644 index 000000000..9388680b3 --- /dev/null +++ "b/frontend\032src/views/layout/components/Sidebar/index.vue" @@ -0,0 +1,75 @@ + + + + + diff --git "a/frontend\032src/views/login/index.vue" "b/frontend\032src/views/login/index.vue" new file mode 100644 index 000000000..f21507944 --- /dev/null +++ "b/frontend\032src/views/login/index.vue" @@ -0,0 +1,411 @@ + + + + + + + diff --git a/frontend/.dockerignore b/frontend/.dockerignore new file mode 100644 index 000000000..c2658d7d1 --- /dev/null +++ b/frontend/.dockerignore @@ -0,0 +1 @@ +node_modules/ diff --git a/frontend/.env.production b/frontend/.env.production index 58fe7f33d..ccefa580a 100644 --- a/frontend/.env.production +++ b/frontend/.env.production @@ -1,2 +1,2 @@ NODE_ENV='production' -VUE_APP_BASE_URL='http://114.67.75.98:8000/api' +VUE_APP_BASE_URL='http://localhost:8000/api' diff --git a/frontend/Dockerfile b/frontend/Dockerfile new file mode 100644 index 000000000..52ecb5495 --- /dev/null +++ b/frontend/Dockerfile @@ -0,0 +1,23 @@ +FROM node:8.16.0-alpine AS frontend-build + +ADD . /app +WORKDIR /app + +# install frontend +RUN npm install -g yarn \ + && yarn install --registry=https://registry.npm.taobao.org + +RUN npm run build:prod + +FROM alpine + +#RUN apk update +RUN apk add nginx +COPY --from=frontend-build /app/dist /app/dist +COPY --from=frontend-build /app/conf/crawlab.conf /etc/nginx/conf.d +#RUN nginx -s start +#COPY ./dist /usr/share/nginx/html + +#EXPOSE 80 +#EXPOSE 8080 + diff --git a/frontend/conf/crawlab.conf b/frontend/conf/crawlab.conf new file mode 100644 index 000000000..c3da703f3 --- /dev/null +++ b/frontend/conf/crawlab.conf @@ -0,0 +1,5 @@ +server { + listen 8080; + root /app/dist; + index index.html; +} diff --git a/frontend/src/assets/logo.png b/frontend/src/assets/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..b7473016a1b996cc19bbb106b23e127cd0fa903d GIT binary patch literal 27484 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 Mru(!;#pd<@0skeoW&i*H literal 0 HcmV?d00001 diff --git a/frontend/src/views/layout/components/Navbar.vue b/frontend/src/views/layout/components/Navbar.vue index e4339dc50..e9cef8386 100644 --- a/frontend/src/views/layout/components/Navbar.vue +++ b/frontend/src/views/layout/components/Navbar.vue @@ -4,7 +4,7 @@ - {{$t('User')}} + {{username}} @@ -19,12 +19,12 @@ - - English - 中文 + + English + @@ -44,7 +44,12 @@ export default { ...mapGetters([ 'sidebar', 'avatar' - ]) + ]), + username () { + if (!this.$store.getters['user/userInfo']) return this.$t('User') + if (!this.$store.getters['user/userInfo'].username) return this.$t('User') + return this.$store.getters['user/userInfo'].username + } }, methods: { toggleSideBar () {