ZhengXingchi / ZhengXingchi.github.io

Apache License 2.0
0 stars 0 forks source link

服务器运维 #68

Open ZhengXingchi opened 4 years ago

ZhengXingchi commented 4 years ago

有次找工作的时候发现个人作品后端一直报错,遂进行排错 回来查看network,发现是mongoose连接不上,项目的mongodb是部署在docker-compose上面的 于是登上服务器查看docker ps,发现docker一直在restart。查看该containeId,然后docker logs conntainerId,发现出来无数logs,只好加一个限制docker logs --since 1m CONTAINER_ID(查看最近一分钟的logs)。仔细观察发现了 1.***aborting after fassert() failure 2.Fatal Assertion 50853 at src/mongo/db/storage/wiredtiger/wiredtiger_util.cpp 414 3.fatal log failure: No space left on device 最后觉得应该是内存爆了,于是df -a查看哪个目录爆了,最后查出是/var/lib/docker/overlay2(内存爆了连在linux上面敲指令的tab提醒都做不到了)

ZhengXingchi commented 4 years ago

ls -lh du -sh * du -sh /* --exclude proc df -h df -a 查看文件类型ls -l 或者 ll (ls -l 别名) 查看docker系统占用可以参考docker系统中/var/lib/docker/overlay2 占用很大,清理Docker占用的磁盘空间,迁移 /var/lib/docker 目录所解决的问题

怎么查看磁盘占用并进行处理Cannot find what is filling up disk space /dev/vda1 is 100% full

ZhengXingchi commented 4 years ago

接着我就把/var/lib/docker/overlay2直接rm -rf掉了,后来发现运行不了docker了,没办法只好卸载docker重新安装。参考docker官方文档。 首先查看已安装的docker安装包yum list installed|grep docker 停止docker服务systemctl stop docker 删除安装包
yum –y remove xxxxxxx yum –y remove xxxxxxx yum –y remove xxxxxxx 删除docker 镜像 rm -rf /var/lib/docker 然后进行重新安装

sudo yum install docker-ce docker-ce-cli containerd.io

运行docker sudo systemctl start docker

docker运行hello world sudo docker run hello-world

ZhengXingchi commented 4 years ago

docker logs

命令格式

$ docker logs [OPTIONS] CONTAINER
  Options:
        --details        显示更多的信息
    -f, --follow         跟踪实时日志
        --since string   显示自某个timestamp之后的日志,或相对时间,如42m(即42分钟)
        --tail string    从日志末尾显示多少行日志, 默认是all
    -t, --timestamps     显示时间戳
        --until string   显示自某个timestamp之前的日志,或相对时间,如42m(即42分钟)

查看指定时间后的日志,只显示最后100行: $ docker logs -f -t --since="2018-02-08" --tail=100 CONTAINER_ID

查看最近30分钟的日志: $ docker logs --since 30m CONTAINER_ID

查看某时间之后的日志: $ docker logs -t --since="2018-02-08T13:23:37" CONTAINER_ID

查看某时间段日志: $ docker logs -t --since="2018-02-08T13:23:37" --until "2018-02-09T12:23:37" CONTAINER_ID

ZhengXingchi commented 4 years ago

关于权限

Linux下 mkdir文件时出现 Permission denied 权限问题。此时最好不要采用chmod -R 777 [开放权限的目录]的方式,而是采用sudo mkdir [新建目录名]

修改只读文件

  1. vim [文件路径] 按住按键i,表示插入,改完设定的参数值 按住esc按钮,按住:wq!,再按住enter键结束

  2. 使用权限将文件可读写: sudo vim [文件路径]

ZhengXingchi commented 4 years ago

egg项目线上监测

  1. 在服务器端采用npm run dev 查看报错,慎用
  2. 采用egg的错误监控系统
  3. 采用egg的日志logs查看
ZhengXingchi commented 4 years ago

mongoDB中勒索病毒

1 数据库一定要设置强密码 2 勤备份 3 服务器设置IP黑白名单,关闭一切可以关闭的端口。如果访问服务的ip经常变,可以考虑修改一些服务的默认端口,减少被扫到的概率

ZhengXingchi commented 4 years ago

docker

进入容器内 `docker exec -it [容器ID] /bin/bash

ZhengXingchi commented 4 years ago

mongodb

mongodb查看日志 mongodb备份与恢复

ZhengXingchi commented 4 years ago

yarn

在服务器中运行yarn安装node_module遇到EACCES: permission denied 采用sudo yarn 提示 sudo: yarn:找不到命令 最后采用sudo npx yarn解决

ZhengXingchi commented 4 years ago

nginx

查看nginx是否启动

ps -C nginx -o pid或者 ps -ef | grep nginx

查看nginx的路径

ps aux|grep nginx nginx -t

部署静态html

location / {
     root   /usr/share/nginx/html;
     index  index.html index.htm;
}
 server
  {
    listen 10087;#监听端口
    server_name 192.168.111.236;
    location / {
            root /opt/gey_h5/dist;
            index index.html index.htm index.php;
        }
  }
server{
    listen 7006;
    server_name _;
    location / {
        root /opt/gwy_h5/dist;
        index index.html index.htm index.php;
    }
    location ^~ /pages/ {
        root /opt/gwy_h5/dist;
        index index.html index.htm index.php;
    }
    location ~* index$ {
        root /opt/gwy_h5/dist;
        index index.html index.htm index.php;
    }
}

通过测试发现路径是匹配到了但是nginx会去root下面根据url进行子路径的查找,这是找不到的,于是进行更改

# nginx代理单页应用
server{
    listen 7006;
    server_name _;
    location / {
        root /opt/gwy_h5/dist;
        index index.html index.htm index.php;
        try_files $uri $uri/ /index.html;
    }
}
ZhengXingchi commented 4 years ago

nginx单页配置反向代理实现跨域

参考Nginx反向代理,前端单页配置

前端单页配置

nginx前端单页应用配置比较简单,根目录的配置如下

location / {
  try_files $uri $uri/ /index.html;
}

如果是二级目录配置二级目录的的配置,例如url

location /url {
  try_files $uri $uri/ /url/index.html;
}

nginx反向代理配置

为了解决跨域问题经常会使用到反向代理,配置如下

# 一级目录
location / {
    # 配置地址
    proxy_pass http://proxy;
}

# 二级目录
location /api {
    # 配置地址
    proxy_pass http://proxy;

    # 代理设置
    proxy_redirect off;
    # 重定向url 一般用不到
    proxy_set_header X-Forwarded-For  $proxy_add_x_forwarded_for;
    # header设置 一般用不到
    proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504;
    # 容灾处理 一般用不到
    proxy_max_temp_file_size 0;
    proxy_buffer_size 4k;
    proxy_buffers 4 32k;
    proxy_busy_buffers_size 64k;
    proxy_temp_file_write_size 64k;
    # 上面相关配置都是缓存相关
    proxy_connect_timeout 90;
    proxy_send_timeout 90;
    proxy_read_timeout 90;
    # 连接请求时间配置
}
ZhengXingchi commented 4 years ago

nginx配置多个单页应用

vue-router+nginx 非根路径配置方法 nginx配置部署多个单页应用 踩坑

想要让不同的url访问不同的单页应用, 踩了两天坑 特记录如下 目的: host:port/a 访问a项目的index.html host:port/b 访问b项目的index.html 用alias可以轻松配置

location ^~/A {
            alias /XX/A/;//此处为A的路径
            index index.html;
            try_files $uri $uri/ /A/index.html;
  }
 location ^~/B {
            alias /XX/B/;//此处为B的路径
            index index.html;
            try_files $uri $uri/ /B/index.html;
 }

如果用root的话 先上结论 nginx的root配置只有在location / {} 下面才生效 在之后的location里面都不生效

location / {
    root /var/www/dist;  # 这样的配置是生效的
}
location /a {

    root /var/www/dist;  # 这样的配置是不生效的

}

所以要么将root配置在location外面, 要么配置在location / 里面

对于用react和vue打包之后的项目, 访问的都是index.html

因此把单个应用部署到服务器上的话 用的配置参数为

location / {
            root   /var/www/dist;
            index  index.html index.htm;
            try_files $uri $uri/ /index.html;
        }

这行代码的意思是 设定root文件夹为打包好的dist文件夹 如果访问根节点, 就返回index.html

如果/后面带有参数, 那么就把参数和root进行拼接之后去找资源

对于这行代码 如果请求是 /static/js/1.js

那么服务器就会去/var/www/dist/static/js/1.js 找这个文件

如果找不到这个文件, 就会返回try_files的内容. 在这里为/index/html

这个是普通的单页应用.

那么如果要实现不同的url访问不同的应用该怎么办呢

首先.root是不能设置为两个了. 因为前文已经提过, 要么在server中设置root 要么在location / 中设置. 及全文只能有一个root

那么就用代理的方法来了.

在nginx上启动两个本机服务(端口不开启防火墙的话是访问不到的

在/etc/nginx/con.d/文件夹下面有一个default.conf, 新建两个

vim server1.conf

server {

    listen 9090;

    location / {

        root /var/www/dist1;

        index  index.html index.htm;
        try_files $uri $uri/ /index.html;
    }

}

在创建一个server2.conf

vim server2.conf

server {

    listen 9091;

    location / {

        root /var/www/dist2;

        index  index.html index.htm;
        try_files $uri $uri/ /index.html;
    }

}

这两我们就在本机开了两个服务.分别访问两个不同的应用

然后在default里面设置

server {
        listen       80;
        server_name  localhost;
        location /a {
        proxy_pass http://127.0.0.1:9090;
        }

        location /b {
        proxy_pass http://127.0.0.1:9091;
        }
}

这样虽然可以初步实现了, 但是会发现静态资源根本访问不了.

因为静态资源请求的是/static/ 而这个url到了nginx这里是没有任何匹配项的. 所以要在配置文件里面做设置

在vue工程里面的 config/index.js里面的build下面有个assetsPublicPath 修改

build: {
    // Template for index.html
    index: path.resolve(__dirname, '../dist/index.html'),

    // Paths
    assetsRoot: path.resolve(__dirname, '../dist'),
    assetsSubDirectory: 'static',
    assetsPublicPath: '/a/',
......
}

另一个改成/b/ 这样访问静态资源的时候 第一个就会访问/a/static/ 这样就能找到了. 这样的首页已经可以显示了. 但是如果进行路由跳转的话 会发现 /a的主页 跳转的时候/a就会不见了. 所以在vue-router里面加上

export default new Router({
  mode: 'history',
  base: '/a/',
  routes: [...]
})

这样每次跳转的时候都会带上/a了

至此就可以在同一个服务器上通过nginx实现不同的url访问不同的应用了 并且互不干扰.

ZhengXingchi commented 4 years ago

nginx不错的文章

Nginx的rewrite(地址重定向)剖析

nginx配置location总结及rewrite规则写法

nginx location配置详细解释

死磕nginx系列--nginx 目录

Nginx可以做什么?看完这篇你就懂了

ZhengXingchi commented 4 years ago

nginx的rewrite

参考使用nginx的rewrite实现代理指定文件夹命令方法 使用nginx的rewrite实现代理指定文件夹命令方法

使用nginx代理Tomcat,Tomcat公布web的时候通常都是带着项目名称的。

比方项目名称为“aven"。那么公布之后就须要使用:http://127.0.0.1:8080/aven 来訪问,

怎样让别人直接通过IP即:http://127.0.0.1 来訪问呢?

能够使用rewrite命令来实现这个功能,配置文件例如以下:

server {
    listen      80;
    server_name  localhost;
    location / {
        rewrite ^/(.*)$ /aven/$1 last;
    }
    location ~* ^/aven/.*$ {
        proxy_pass http://127.0.0.1:8080;
    }
}

这样的配置方法在使用中还是有一些问题。

1、Url中还是会看到aven这个文件夹。

2、Session丢失了。

眼下还没找到好的解决方式。

ZhengXingchi commented 4 years ago

jenkins与nginx部署

使用Jenkins持续集成前端项目并自动化部署到Nginx服务器

利用Jenkins + nginx 实现前端项目自动构建与持续集成

Jenkins+Nginx+Github/Gitlab自动化构建部署前端项目

创建Jenkins构建触发器,代码提交至gitLab即自动触发构建

jenkins自动部署构建触发器(Enabled GitLab triggers)的高级选项中有一个Allowed branches,选择 Filter branches by regex并且填上Source Branch Regex为(dev|qiufeng/1\.0)则只有dev以及qiufeng1.0分支的提交会自动部署。

也可以使用插件generic-webhook-trigger-plugin 可参考一jenkins指定具体项目具体分支进行构建部署 可参考二Jenkins 实现 GitLab 特定分支提交时自动化构建 5083227-bcd8b182c3bcf29a

ZhengXingchi commented 4 years ago

npm script中&&和&

参考 npm script中&&和& 在bash命令或npm script中使用&来实现并发效果时,实际上是把&左侧的命令丢入后台运行,右侧剩余命令看做 整体 任务在前台运行,以此来实现并发效果。

而&&是串行执行两侧命令,先执行完左侧后再执行右侧。

切记!!! command1 & command2 && command3 并不是并发执行command1和command2后再执行command3

而是并发执行command1和command2 && command3

ZhengXingchi commented 4 years ago

egg线上部署问题检测

参看[egg-scripts日志输出目录]https://github.com/eggjs/egg/issues/3912 参看[线上环境无法打印日志到文件并且控制台也不能输出日志]https://github.com/eggjs/egg/issues/1208 两种方案一种是直接"start": "MOCK_HOME_DIR=./ egg-scripts start --daemon"更改整个logs文件的地址 还有一种是只更改stdoutstderr的地址egg-script原生支持更推荐"start": "egg-scripts start --stdout=./ --stderr=./ --daemon"

ZhengXingchi commented 4 years ago

vscode调试debug

参考egg+vscode调试

// .vscode/launch.json
{
    // Use IntelliSense to learn about possible attributes.
    // Hover to view descriptions of existing attributes.
    // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
    "version": "0.2.0",
    "configurations": [
        {
            "name": "launch egg",
            "type": "node",
            "request": "launch",
            "cwd": "${workspaceRoot}",
            "windows": {"runtimeExecutable": "npm.cmd"},
            "runtimeExecutable": "npm",
            "runtimeArgs": ["run","dev"],
            "console":"integratedTerminal",
            "protocol": "auto",
            "restart":true,
            "port":9229,
            "autoAttachChildProcesses": true
          }
    ]
}

参考VSCode 调试中 launch.json 配置不完全指南 经常使用 VSCode 的同学多多少少会看到项目中有这么一个文件夹 .vscode,这是一个在VSCode 软件上才能发挥作用的配置文件夹,它一般是跟随项目的,其用途与我们平时看到的 jsconfig.json, .editor.config 等相似,都是为了对工程做更多的约束,或者对代码做更多的规范化处理。

VSCode 内置了对 Node.js 的调试支持,如果你需要调试其他语言如 C++、PHP、Python 等,可以在 VSCode 的插件市场安装对应的插件。由于调试的技巧和配置在语言之间的差异不大,文本将以 Node.js 的调试为例进行讲解。

调试的官方基础文档在 这里,看图可以了解大致的操作步骤,如果你不了解 Node.js 的调试原理,可以先读一读我之前写的 这篇文章,有三四年了,不过内容还没过时。下面将给大家讲解调试时可能遇到的几种场景,以及对应的配置文件参数说明。 一起动手 如果你此刻正在电脑前,不妨先把 代码 克隆到本地,跟着一起动手操作,

# https://github.com/barretlee/debugging-in-vscode-tutorial
git clone git@github.com:barretlee/debugging-in-vscode-tutorial.git;
cd debugging-in-vscode-tutorial
npm install;

然后使用 VSCode 打开代码。

场景一,调试 Node.js 程序 这里的 Node.js 指的是使用原生 JS 编写的简单程序,看 Demo:

// File: src/index.js
require('http').createServer((req, res) => {
    if (req.url === '/') {
      fs.createReadStream(
        path.join(__dirname, '../index.html')
      ).pipe(res);
    } else {
      res.end(req.url);
    }
  }).listen(8001, () => {
    console.log('run at 8001');
  });

在程序第 3 行位置打一个断点: 下面就一起来看看,这样一个程序的调试配置是怎样的,可以打开 .vscode/launch.json,查看 name 为 调试 Node.js 程序 的一项,配置为:

{
  "name": "调试 Node.js 程序",
  "type": "node",
  "request": "launch",
  "program": "${workspaceFolder}/src/index.js"
}

很简单的三个参数,稍微解释下:

场景二:调试一个 TS Node 程序 与场景一稍微不同的是,我们当前的程序是一个 TypeScript 文件(.ts 后缀),注意我们的项目 package.json 中添加了 typescriptts-node 两个依赖,前者可以用来编译 ts 文件,后者可以执行 ts 文件。打开 launch.json 找到 name 为 调试 TS Node 程序 - args 的配置:

{
    "name": "调试 TS Node 程序 - args",
    "type": "node",
    "request": "launch",
    "runtimeExecutable": "node",
    "runtimeArgs": [
      "-r",
      "ts-node/register"
    ],
    "args": [
      "${workspaceFolder}/src/index.ts"
    ]
  }

在这里你看到了一个新参数 runtimeArgs,需要注意的是 runtimeArgs 是为 runtimeExecutable 环境提供的配置,而 args 是为程序提供的配置。这个 JSON 的意思是:通过 node 来启动 /src/index.ts,在启动时为 node 注入一个 ts-node/register 模块,以便可以执行 ts 类型的文件。实际执行代码为: node --inspect-brk=DEBUG_PORT -r ts-node/register ./src/index.ts 需要注意的是,虽然 ts-node 可以直接执行 .ts 文件,但由于 launch.json 中启动调试的时候,会默认加上一个 --inspect-brk=DEBUG_PORT 参数,而 ts-node 不支持这个参数,所以无法使用下面的方式进行调试:

{
    "name": "[错误]调试 TS Node 程序 - ts-node",
    "type": "node",
    "request": "launch",
    "runtimeExecutable": "${workspaceFolder}/node_modules/.bin/ts-node",
    "args": [
      "${workspaceFolder}/src/index.ts"
    ]
  }

当然我们还有其他的方式来启动调试:

{
    "name": "调试 TS Node 程序 - preTask",
    "type": "node",
    "request": "launch",
    "program": "${workspaceFolder}/out/index.js",
    "preLaunchTask": "tsc_build"
  }

这里多了一个新的配置参数 preLaunchTask,顾名思义,在 launch 调试之前先执行一个任务,这里就涉及到 .vscode/tasks.json 文件的配置了,我们在 tasks.json 中配置一个 labeltsc_build 的任务:

{
    "label": "tsc_build",
    "type": "typescript",
    "tsconfig": "tsconfig.json"
  }

在启动调试之前,会启动一个 typescript 的编译操作,将文件编译到 out 目录下(具体可查看 tsconfig.json 的编译配置),然后通过 node 来启动编译后的文件,由于编译允许生成 sourcemap 文件,所以调试的断点依然会在源码上:

场景三:调试已启动的 Node.js 程序 这里就要提到 request 的另外一个值 attach 了,如果你理解了 背后的原理,你会觉得这个单词用的十分贴切。我们先用 node 来启动 src 下的 index.jsnode ./src/index.js 此时如果我们想给这个程序断点调试,可以在 launch.json 中作如下配置:

{
    "name": "Attach to node",
    "type": "node",
    "request": "attach",
    "processId": "${command:PickProcess}"
  }

推荐使用 ${command:PickProcess} 作为processId 的值,因为 VSCode 会遍历所有的 Node PID 列出来让你选择,如下图所示: 图中第一个就是我们启动的程序,processId 为 59172,当然你也可以直接将配置中的值写死为 59172(并不推荐),这样就少了选择的过程了。

场景四:调试网页的 JS 代码 大家应该十分熟悉在 Chrome 中调试 JS 代码,不过 VSCode 允许你在安装了 Debugger for Chrome 插件后,直接在 VSCode 调试 JS 代码,让你的代码和调试融为一体,提升开发体验: 可以通过如下简单的配置进行调试:

{
    "name": "调试网页的 JS 文件",
    "request": "launch",
    "type": "chrome",
    "file": "${workspaceFolder}/index.html"
  }

注意,这里的 type 是 chrome,默认会启动一个 Chrome 浏览器(新用户)加载 file 字段对应的文件地址(通过 file://协议加载),文件中用到的 JS 都可以断点调试。当然你也可以起一个 Web Server 来调试 http:// 协议的文件,这里就需要设置 webRooturl 参数了,可自行 Google。

小结 这四个场景已经可以覆盖大部分的 JavaScript 调试了,其实还有几个比较实用的参数,如将 console 设置为 integratedTerminal,那么调试过程的信息会在 Terminal Panel 而不是 Debugger Panel 展示。具体可以阅读 VSCode 的 官方文档

ZhengXingchi commented 4 years ago

前端文件放alioss

由于项目使用egg进行搭建,刚开始搜索egg的静态资源方案,参考 Egg 最新的静态资源方案

后来想起来前端页面是通过umi build以后放到egg中的,所以只要搞定umi静态文件自动发布alioss就可以了 然后搜索到两个库 alonez/umi-plugin-alioss ahwgs/umi-plugin-alioss

关于alioss的CDN部署 参考使用阿里云OSS+CDN部署前端页面与加速静态资源

关于前端资源放alioss的自动化实现 前端资源文件(图片 css js)同步到阿里云OSS 为了实现自动化更新前端资源,前端资源已放到了阿里云OSS,实现自动上传。 思路: 1.因为使用的是php语言开发项目,自然会想到用php实现,用php实现目录遍历和文件遍历操作然后结合阿里云sdk进行上传操作,但是这样会有个问题,目录层级比较多,文件比较多,那么循环效率低。所有一开始就不打算使用这种方式。 2.网上开始寻找解决方法, 1)了解linux rsync 这个命令会自动监测文件目录的修改实现增量备份等,但是阿里云oss没有可操作linux ssh 端口,所有果断放弃了,不过也了解了我司的svn的更新,是使用rsync实现的。 2)网上找了个开源项目osssync 使用python和阿里云ossSDK开发。基于 inotify系统层级。项目很高,但是下载后发现代码比较老,阿里云的sdk已经很老了,不在维护,无法使用。放弃 3)ossfs 阿里云开发的同步工具 支持linux/mac系统。阿里云内网传输更快,切不计算流量地址https://bbs.aliyun.com/read/267052.html 总结: 多百度,多goole,寻找相对优秀的解决方案。不能局限于自己的语言,当然也要考虑时间和成本问题,做好权衡就好了。这样你也会从中学到了解到更多东西,不要一味的走老的套路。试试新东西。这样视野更广阔。

ZhengXingchi commented 4 years ago

nginx

在http 中加入

upstream myapp1 {
          server 54.223.18.148:8085;
          server 54.223.18.148:8087;
 }

修改server

    server {
        listen       8888;#默认端口是80,如果端口没被占用可以不用修改
        server_name  localhost;

        #charset koi8-r;

        #access_log  logs/host.access.log  main;
        root         D:\dist; #vue项目的打包后的dist

        location / {
            try_files $uri $uri/ @router;#需要指向下面的@router否则会出现vue的路由在nginx中刷新出现404
            index  index.html index.htm;
        }
        #对应上面的@router,主要原因是路由的路径资源并不是一个真实的路径,所以无法找到具体的文件
        #因此需要rewrite到index.html中,然后交给路由在处理请求资源
        location @router {
            rewrite ^.*$ /index.html last;
        }
        #拦截前端页面请求的路径XindianData,并转发到54.223.18.148:8085,54.223.18.148:8087
        location  ^~/XindianData/{
            proxy_pass http://myapp1;
        }
ZhengXingchi commented 4 years ago

前端部署架构无缝部署平滑部署

Keepalived+Nginx提供前端负载均衡+主从双机热备+自动切换 egg无缝部署 请教一下eggjs生产的热更新部署 所有 Node.js 应用都不建议热更新,而应该用负载均衡切换的方式来发布 一行 delete require.cache 引发的内存泄漏血案 前端负载均衡&架构 前端版本更新工程化静态资源版本更新与缓存 静态资源版本更新与缓存静态资源版本更新与缓存

[更新静态文件 需要重启服务?](https://github.com/eggjs/egg/issues/4283)

前端优化带来的思考,浅谈前端工程化 在前端性能优化中应用HTTP缓存的三部曲 变态的静态资源缓存与更新 大公司里怎样开发和部署前端代码? [前端工程与性能优化](https://github.com/fouber/blog/issues/3)

发布的机器把自己摘出负载均衡集群,然后 stop -> start,启动后检测下端口通了再把自己加入回负载均衡集群,分批这样依此发就行了

ZhengXingchi commented 4 years ago

githook

推到master需要密码或者管理员允许

为了安装 Hook ,你可以在 .git/hooks 中创建一个符号链接,或者简单地在更新后把它们复制到 .git/hooks 目录下。 例如新建auth文件夹用于存放sh,然后将.git/hooks中的commit-msg指向auth/commit-msg.sh

#!/bin/sh
cd ./auth
pwd
sh commit-msg.sh

参考Linux中执行 .sh 的方法

  1. Git 钩子:定制工作流
  2. Hook 实战:编写动作脚本
  3. 使用 Hook 将代码版本变动信息通过邮件发送python集成的邮件

参考GitHook 工具 —— husky介绍及使用

ZhengXingchi commented 4 years ago

X-Forwarded-For

参考HTTP 请求头中的 X-Forwarded-For 如何通过 X-Forwarded-For 拿到用户真实 IP

nginx

昨天一个开发找我帮忙配置一个nginx的转发,本来很容易的配置,但是坑了我好久才解决。。。需求大致是:

nginx上配有aaa.example.com的虚拟主机,现在需要将访问http://aaa.example.com/api/x.x/client/的请求转到http://bbb.example.com/api/x.x/client/,bbb.example.com的虚拟主机在另外一台nginx上,其中x.x表示位数不定的版本号,如:1.0或1.20.345都可能。请求转过去要求url保持不变

用rewrite转发的话,url会发生变化的,那就用proxy_pass吧,于是添加了如下的配置:


location ~ ^/api/([0-9]+)(\.[0-9]+)*/client/ {
    proxy_pass http://bbb.example.com;
}

在现有环境的nginx里添加这段配置之后,访问却始终转不过去,查看nginx日志也只能看到是404信息,并没有更多定位问题的信息。检查了许久也没找到原因,于是重新装了一台新nginx,里面只加上面这段配置,结果nginx是能够转发成功的,这说明单独来看这条location的配置是没有问题的,很有可能是现有环境nginx里的某些配置影响到了这个转发。 为了定位问题原因,我将aaa.example.com虚拟主机下的其他配置注意注释掉来调试,最后发现当注释掉proxy_set_header Host $http_host ;这条配置之后,就能成功转发了。这才注意到是反向代理配置的问题。现有环境中原有的配置也不能随便删掉,上网查了下原因,找到下面这种解决方案:

location ~ ^/api/([0-9]+)(\.[0-9]+)*/client/ {
    proxy_pass http://bbb.example.com;
    proxy_set_header Host $proxy_host;
}

即,在location里面添加一条proxy_set_header Host $proxy_host;配置。当Host设置为$http_host时,则不改变请求头的值,所以当要转发到bbb.example.com的时候,请求头还是aaa.example.com的Host信息,就会有问题;当Host设置为$proxy_host时,则会重新设置请求头为bbb.example.com的Host信息。

另外,关于proxy_pass转发url的参数,可以通过在location中用rewrite来做,所以完善后的配置如下:

location ~ ^/api/([0-9]+)(\.[0-9]+)*/client/ {
    rewrite /(.*)$ /$1 break;
    proxy_pass http://bbb.example.com;
    proxy_set_header Host $proxy_host;
} 

在location用rewrite改变了URI之后,proxy_pass将使用改变后的URI。上面例子(.*)是将所有参数传给$1,转发时/$1会拼接在http://bbb.example.com后面。 补习 自己之前并没有太留意nginx中proxy_set_header的设置,借这次遇到的问题,补习下功课。

proxy_set_header用来重定义发往后端服务器的请求头。

语法格式: proxy_set_header Field Value; Value值可以是包含文本、变量或者它们的组合。常见的设置如: proxy_set_header Host $proxy_host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $remote_addr;

注意:在nginx的配置文件中,如果当前模块中没有proxy_set_header的设置,则会从上级别继承配置。继承顺序为:http, server, location。

参考资料: 1 http://blog.csdn.net/a19860903/article/details/49914131 2 《精通Nginx》— 第4章 Nginx作为反向代理

http获取客户端真实ip的原理及利用X-Forwarded-For伪造客户端IP漏洞成因及防范

问题背景 在Web应用开发中,经常会需要获取客户端IP地址。一个典型的例子就是投票系统,为了防止刷票,需要限制每个IP地址只能投票一次。

如何获取客户端IP 在Java中,获取客户端IP最直接的方式就是使用request.getRemoteAddr()。这种方式能获取到连接服务器的客户端IP,在中间没有代理的情况下,的确是最简单有效的方式。但是目前互联网Web应用很少会将应用服务器直接对外提供服务,一般都会有一层Nginx做反向代理和负载均衡,有的甚至可能有多层代理。在有反向代理的情况下,直接使用request.getRemoteAddr()获取到的IP地址是Nginx所在服务器的IP地址,而不是客户端的IP。

HTTP协议是基于TCP协议的,由于request.getRemoteAddr()获取到的是TCP层直接连接的客户端的IP,对于Web应用服务器来说直接连接它的客户端实际上是Nginx,也就是TCP层是拿不到真实客户端的IP。

为了解决上面的问题,很多HTTP代理会在HTTP协议头中添加X-Forwarded-For头,用来追踪请求的来源。X-Forwarded-For的格式如下:

X-Forwarded-For: client1, proxy1, proxy2

X-Forwarded-For包含多个IP地址,每个值通过逗号+空格分开,最左边(client1)是最原始客户端的IP地址,中间如果有多层代理,每一层代理会将连接它的客户端IP追加在X-Forwarded-For右边。

下面就是一种常用的获取客户端真实IP的方法,首先从HTTP头中获取X-Forwarded-For,如果X-Forwarded-For头存在就按逗号分隔取最左边第一个IP地址,不存在直接通过request.getRemoteAddr()获取IP地址:

public String getClientIp(HttpServletRequest request) { String xff = request.getHeader("X-Forwarded-For"); if (xff == null) { return request.getRemoteAddr(); } else { return xff.contains(",") ? xff.split(",")[0] : xff; } }

另外,要让Nginx支持X-Forwarded-For头,需要配置:

proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

$proxy_add_x_forwarded_for会将和Nginx直接连接的客户端IP追加在请求原有X-Forwarded-For值的右边。

伪造X-Forwarded-For 一般的客户端(例如浏览器)发送HTTP请求是没有X-Forwarded-For头的,当请求到达第一个代理服务器时,代理服务器会加上X-Forwarded-For请求头,并将值设为客户端的IP地址(也就是最左边第一个值),后面如果还有多个代理,会依次将IP追加到X-Forwarded-For头最右边,最终请求到达Web应用服务器,应用通过获取X-Forwarded-For头取左边第一个IP即为客户端真实IP。

但是如果客户端在发起请求时,请求头上带上一个伪造的X-Forwarded-For,由于后续每层代理只会追加而不会覆盖,那么最终到达应用服务器时,获取的左边第一个IP地址将会是客户端伪造的IP。也就是上面的Java代码中getClientIp()方法获取的IP地址很有可能是伪造的IP地址,如果一个投票系统用这种方式做的IP限制,那么很容易会被刷票

如何防范 方法一

方法一:在直接对外的Nginx反向代理服务器上配置:

proxy_set_header X-Forwarded-For $remote_addr;

这里使用$remote_addr替代上面的$proxy_add_x_forwarded_for。$proxy_add_x_forwarded_for会在原有X-Forwarded-For上追加IP,这就相当于给了伪造X-Forwarded-For的机会。而$remote_addr是获取的是直接TCP连接的客户端IP(类似于Java中的request.getRemoteAddr()),这个是无法伪造的,即使客户端伪造也会被覆盖掉,而不是追加。

需要注意的是,如果有多层代理,那么只要在直接对外访问的Nginx上配置X-Forwarded-For为$remote_addr,内部层的Nginx还是要配置为$proxy_add_x_forwarded_for,不然内部层的Nginx又会覆盖掉客户端的真实IP。

方法二

另外一种方法是我在Tomcat源码中发现的:org.apache.catalina.valves.RemoteIpValve

实现思路:遍历X-Forwarded-For头中的IP地址,和上面方法不同的是,不是直接取左边第一个IP,而是从右向左遍历。遍历时可以根据正则表达式剔除掉内网IP和已知的代理服务器本身的IP(例如192.168开头的),那么拿到的第一个非剔除IP就会是一个可信任的客户端IP。这种方法的巧妙之处在于,即时伪造X-Forwarded-For,那么请求到达应用服务器时,伪造的IP也会在X-Forwarded-For值的左边,从右向左遍历就可以避免取到这些伪造的IP地址。这种方式本文就不提供具体实现代码了,有兴趣可以查看Tomcat源码。

ZhengXingchi commented 4 years ago

nginx负载均衡

# nginx.conf 中
upstream imacco{
    server 192.168.0.1:10001 weight=3 max_fails=2 fail_timeout =30;
    server 192.168.0.2:10002 weight=3 max_conns=10000;
    server 192.168.0.3:10003 weight=4 ;
    server 192.168.0.4:10004 backup;
}
server { 
    listen 443; 
    ssl on;
    server_name v3.imacco.com;
    ssl_certificate ssl/imacco.com.csr;
    ssl_certificate_key /usr/local/nginx/conf/imacco.com.key;
    ssl_session_timeout 5m;
    ssl_protocols  TLSv1 TLSv1.1 TLSv1.2;
    ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4;
    ssl_prefer_server_ciphers on;

    location / {
         proxy_pass http://v3.imacco.com; 
         proxy_set_header Host $host; 
         proxy_set_header X-Real-IP $remote_addr; 
         proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 
      } 
}

负载均衡理解

upstream imacco{
    # ip_hash指令,将同一用户引入同一服务器。
    ip_hash;
    # 30% 几率,配置允许请求失败的次数=3,fail_timeout=30S 内不会分配给192.168.0.1
    server 192.168.0.1:10001 weight=3 max_fails=2 fail_timeout =30; 
    # 30% 几率 分配最大连接数 max_conns =10000
    server 192.168.0.2:10002 weight=3 max_conns=10000;
    # 40% 几率 
    server 192.168.0.3:10003 weight=4 ;
    # backup 只有其他的都挂了才给192.168.0.4
    server 192.168.0.4:10004 backup;
}
ZhengXingchi commented 4 years ago

禅道

参考如何在Linux服务器上部署禅道 查看服务器的系统位数 uname -a 有x86 64就表示64位的,没有就表示32位的

cat /etc/redhat-release 查看操作系统版本号

下载9.8.2版本: sudo wget http://dl.cnezsoft.com/zentao/9.8.2/ZenTaoPMS.9.8.2.zbox_64.tar.gz 下载.10.5.stable版本: sudo wget https://www.zentao.net/sdl/ZenTaoPMS.10.5.stable.zbox_64.tar.gz

修改apache端口 /opt/zendao/zbox/zbox -ap 6001 修改mysql端口 /opt/zendao/zbox/zbox -mp 6002

测试服务器打开地址:http://192.168.111.236:6001/