gitlab部署与基本使用

环境

  • 系统:centos7
  • 内核:3.10.0-693.el7.x86_64
  • 配置:4G/8G

软件包

https://packages.gitlab.com/gitlab/gitlab-ce

选择自己需要的包

下载安装

1、rpm安装

[root@localhost ~]# wget https://packages.gitlab.com/gitlab/gitlab-ce/packages/el/7/gitlab-ce-10.2.3-ce.0.el7.x86_64.rpm 
[root@localhost ~]# rpm -ivh gitlab-ce-10.2.3-ce.0.el7.x86_64.rpm

2、yum安装

配置yum环境

[root@localhost ~]# cat /etc/yum.repos.d/gitlab.repo
[gitlab-ce]
name=gitlab-ce   
baseurl=http://mirrors.tuna.tsinghua.edu.cn/gitlab-ce/yum/el7   
repo_gpgcheck=0   
gpgcheck=0   
enabled=1   
gpgkey= 
[root@localhost ~]# yum clean all
[root@localhost ~]# yum -y install gitlab-ce

3、自动安装

[root@localhost ~]# curl -s https://packages.gitlab.com/install/repositories/gitlab/gitlab-ce/script.rpm.sh | sudo bash

依赖关系安装

安装curl、policycoreutils、openssh-server、openssh-clients,安装postfix以便发送邮件

[root@localhost ~]# yum install curl policycoreutils openssh-server openssh-clients postfix  -y

关闭防火墙firewalld

修改gitlab配置文件

[root@localhost ~]# egrep -v "^$|^#" /etc/gitlab/gitlab.rb 
external_url 'http://192.168.192.148'

重新配置应用程序,每次修改配置文件都要执行此命令,重新加载配置文件

[root@localhost ~]# gitlab-ctl reconfigure

gitlab管理

启动

[root@localhost ~]# gitlab-ctl start

关闭

[root@localhost ~]# gitlab-ctl stop

状态

[root@localhost ~]# gitlab-ctl status

重启

[root@localhost ~]# gitlab-ctl restart

列出所有服务

[root@localhost ~]# gitlab-ctl service-list

显示配置

[root@localhost ~]# gitlab-ctl show-config

默认配置文件位置说明

  • 主配置文件:/etc/gitlab/gitlab.rb

  • 日志地址:/var/log/gitlab

  • 服务地址:/var/opt/gitlab

  • 仓库地址:/var/opt/gitlab/git-data

gitlab页面操作

访问

192.168.192.48默认端口80,首次登陆需要为root用户创建一个不少于8位的密码

关闭注册,避免一些不必要的麻烦

项目和群组有一定的关联性,一般以群组区分不同的项目

创建群组

未分类

群组创建名称最好是一个可识别的名称,根据重要等级不同,可以选择不同的可见性

创建项目

未分类

选择关联的组,后边指定项目名称,可见等级根据不同的项目可以做调整

创建用户

未分类

邮箱中会收到一个邮件,用户通过此邮件进行密码设置

未分类

关联用户、组,给与用户不同的权限

未分类

添加测试文件

未分类

在下方写入相关代码

未分类

未分类

下载代码

安装git

[root@localhost ~]# yum -y install git

未分类

可以git表示部署成功

gitlab-创建简单的项目

1.新建目录

$ mkdir testproject
$ cd testproject

2.新建项目文件

$ touch README.md

3.初始化项目

$ git init

4.添加远程仓库:

$ git remote add origin [email protected]:deploy/testproject.git
  git remote add origin [email protected]:<组>/<项目名称>.git

远程仓库必须存在此组。  

5.加入需要版本管理的文件

$ git add ./README.md
$ git commit -am 'init project'

6.提交

$ git push origin master
Counting objects: 3, done.
Writing objects: 100% (3/3), 202 bytes | 0 bytes/s, done.
Total 3 (delta 0), reused 0 (delta 0)

7.在gitlab dashboard就可以看到项目已经提交成功

未分类

Docker Compose实例之nginx反向代理GitLab

在上一篇文章(Docker快速搭建GitLab私有仓库)中探索了如何用docker实现最简单的GitLab服务。但是现实场景中往往会遇到复杂的情况和需求,光用docker指令可能就比较繁琐了。

举个例子???? 如下图所示,在一个服务器上要部署一个GitLab,N个其它服务(那N个服务或许还要与GitLab进行隔离),如果用docker 指令一个一个的run起来,管理起来可就麻烦了。 网络示意图

未分类

当然docker作为流行工具,不会让我们那么累,我们借助Docker Compose的文件来描述这个多容器的配置,并通过一条命令就能启动这些容器。

Docker Compose 的配置文件 docker-compose.yml 如下:

version: '3.6'
services:
  gitlab: # gitlab服务名
    container_name: gitlab-site # gitlab 容器名
    image: gitlab/gitlab-ce:latest
    environment:
      GITLAB_OMNIBUS_CONFIG: |
        external_url 'https://gitlab.example.com'  # git域名
        unicorn['worker_timeout'] = 60
        unicorn['worker_processes'] = 2
        nginx['enable'] = true
        nginx['client_max_body_size'] = '250m'
        nginx['redirect_http_to_https'] = true
        nginx['ssl_certificate'] = "/etc/ssl/gitlab.example.com.crt" # 加密证书文件
        nginx['ssl_certificate_key'] = "/etc/ssl/gitlab.example.com.key"
        nginx['ssl_ciphers'] = "ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256"
        nginx['ssl_prefer_server_ciphers'] = "on"
        nginx['ssl_protocols'] = "TLSv1.1 TLSv1.2"
        nginx['ssl_session_cache'] = "builtin:1000  shared:SSL:10m"
        nginx['listen_addresses'] = ["0.0.0.0"]
        nginx['http2_enabled'] = true
    volumes:
      - "/srv/gitlab/config:/etc/gitlab"         # 此处使用的是绝对路径
      - "/srv/gitlab/logs:/var/log/gitlab"
      - "/srv/gitlab/data:/var/opt/gitlab"
      - "/srv/ssl:/etc/ssl"                         # 加密证书文件路径映射
    networks:
      - git-network  # 使用 git-network 网络,与 other-network 相隔离
other-app: # 用 nginx 模拟的其它服务
    container_name: other-app-nginx
    image: nginx:stable-alpine
    volumes:
      - "./nginx-conf-site:/etc/nginx/conf.d:ro"     # 此处使用的是相对路径
      - "./contents:/usr/share/nginx/html/sites:ro" # 相对的是 docker-compose.yml 的位置
    networks:
      - other-network   # 使用 other-network 网络,与 git-network 相隔离
  nginx-reverse:  # nginx 反向代理服务
    container_name: nginx-reverse
    depends_on:    # 反向代理依赖的服务名
      - gitlab  
      - other-app
    image: nginx:stable-alpine
    ports:
      - "443:443"
      - "80:80"
    volumes:
      - "./nginx-conf-reverse:/etc/nginx/conf.d:ro"  # 此处使用的是相对路径
      - "/srv/ssl:/etc/ssl:ro"   # 此处使用的是绝对炉具
    networks:  # nginx反向代理使用的网络
      - git-network     # gitlab使用的网络
      - other-network  # 其它app使用的网络
networks:  # 声明网络
  git-network:  
  other-network:
nginx反向代理的配置文件1: git-reverse.conf ,放在与docker-compose.yml 所在目录相对的 nginx-conf-reverse 目录下,作用是将对 https://gitlab.example.com 的访问进行转发

server{
    listen      443 ssl http2;    # 监听 443 端口
    listen [::]:443 ssl http2;
    server_name gitlab.example.com;

    ssl_certificate        /etc/ssl/gitlab.example.com.crt;
    ssl_certificate_key    /etc/ssl/gitlab.example.com.key;
    ssl_protocols TLSv1.2 TLSv1.1 TLSv1;

    location / {
      proxy_pass https://gitlab-site;   # 转发给名为 "gitlab-site" 的 容器
    }
}

nginx反向代理的配置文件2: other-reverse.conf ,放在与docker-compose.yml 所在目录相对的 nginx-conf-reverse 目录下, 作用是将对 http://other.example.com 的访问进行转发

server{
    listen      80;   # 监听 80 端口
    server_name other.example.com;  # 其它服务的域名
    location / {
      proxy_pass http://other-app-nginx;  # 转发到"其它"服务
    }
}
本例中的“其它服务”由一个nginx静态网站模拟,其配置文件other-site.conf放在与docker-compose.yml 所在目录相对的 nginx-conf-site 目录下:

server {
    listen 80;
    server_name other.example.com;
    location / {
        root   /usr/share/nginx/html/sites/other-site;
        index  index.html;
    }
}

当然,上述配置中用 nginx 模拟的“其它服务” 可以是任意的网络服务,你不需要的话,把这部分删掉也没问题。

最后,执行一个指令

sudo docker-compose up -d

GitLab服务、其它服务、Nginx反向代理 就会依次启动起来。

Docker Compose 编排 DevOps 工具

Docker nginx 反向代理 设置 介绍了通过 nginx 反向代理关联容器。此为真实的使用场景。通过 Gitea 作为代码管理工具;Kanboard 作为任务管理;Jenkins 作为 CI 工具。这样的组合比较适合小型团队使用,相比起 GitLab 这种巨无霸来说,部署简单,使用简单。

准备

安装 Docker

$ curl -fsSL get.docker.com -o get-docker.sh
$ sudo sh get-docker.sh

<output truncated>

If you would like to use Docker as a non-root user, you should now consider
adding your user to the "docker" group with something like:

sudo usermod -aG docker your-user

Remember to log out and back in for this to take effect!

WARNING: Adding a user to the "docker" group grants the ability to run
        containers which can be used to obtain root privileges on the
        docker host.
        Refer to https://docs.docker.com/engine/security/security/#docker-daemon-attack-surface
        for more information.

安装 Docker Compose

$ sudo curl -L https://github.com/docker/compose/releases/download/1.19.0/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose
$ sudo chmod +x /usr/local/bin/docker-compose
$ docker-compose --version

注:Docker 以及 Docker Compose 的安装,官方文档讲得非常清晰,在此不再赘述。

docker-compose.yml 文件

 version: "3.5"

services:
  mysql:
    image: mysql:latest
    container_name: mysql
    ports:
      - "3306:3306"
    networks:
      - devops
    environment:
      - MYSQL_ROOT_PASSWORD=/run/secrets/db_root_password
    volumes:
      - type: bind
        source: ./mysql/conf.d
        target: /etc/mysql/conf.d
      - type: bind
        source: ./mysql/data
        target: /var/lib/mysql
      # - ./mysql/conf.d:/etc/mysql/conf.d
      # - ./mysql/data:/var/lib/mysql
    secrets:
      - db_root_password
    restart: always

  gitea:
    image: gitea/gitea:latest
    container_name: gitea
    ports:
      - "10080:3000"
      - "10022:22"
    networks:
      - devops
    environment:
      - VIRTUAL_HOST=git.vking.io
      - VIRTUAL_PORT=3000
      - GITEA_CUSTOM=/etc/gitea
    depends_on: 
      - mysql
    volumes:
      - type: bind
        source: ./gitea
        target: /data
      - type: bind
        source: ./gitea/custom
        target: /etc/gitea
      # - ./gitea:/data
      # - ./gitea/custom:/etc/gitea
    restart: always

  task:
    image: kanboard/kanboard:latest
    container_name: kanboard
    ports:
      - "8888:80"
    networks:
      - devops
    environment:
      - VIRTUAL_HOST=task.vking.io
      - VIRTUAL_PORT=80
    volumes:
      - type: bind
        source: ./kanboard/data
        target: /var/www/app/data
      - type: bind
        source: ./kanboard/plugins
        target: /var/www/app/plugins
      # - ./kanboard/data:/var/www/app/data
      # - ./kanboard/plugins:/var/www/app/plugins
    restart: always

  jenkins:
    image: jenkins/jenkins:lts
    container_name: jenkins
    ports:
      - "8081:8080"
      - "50000:5000"
    networks:
      - devops
    environment:
      - VIRTUAL_HOST=jenkins.vking.io
      - VIRTUAL_PORT=8080
    volumes:
      - type: bind
        source: ./jenkins/data
        target: /var/jenkins_home
      # - ./jenkins/data:/var/jenkins_home
    restart: always

  nginx:
      image: jwilder/nginx-proxy:alpine
      container_name: nginx
      ports:
        - "80:80"
      depends_on: 
        - gitea
        - task
        - jenkins
      networks:
        - devops
      volumes:
        - type: bind
          source: /var/run/docker.sock
          target: /tmp/docker.sock
        # - /var/run/docker.sock:/tmp/docker.sock
      restart: always

secrets:
  db_root_password:
    file: ./mysql/my_secret.txt

networks:
  devops:
    name: devops-network

注: 通过 volumes bind 方式挂载的外部文件/目录,如果不存在的话,不会自动创建。

使用

  • MySQL 的管理员密码,通过 mysql/my_my_secret.txt 设置,构建容器的时候会自动加载并设置。
  • 不同 services 管理的域名,通过环境变量设置 VIRTUAL_HOST=域名;VIRTUAL_PORT=端口
  • 创建镜像并执行 docker-compose up -d
  • 删除容器及 volumn 数据 docker-compose down -v

后记

因为通过反向代理隐藏了暴露端口的细节,如果没有外部注册的域名的话,还需要通过 Dnsmasq 进行内部域名解析。

CentOS7 安装 Docker 和 Docker-compose

注意需要使用 root 账户或者可以使用 sudo 的账户

1、安装 Docker

# 安装依赖
sudo yum install -y yum-utils 
  device-mapper-persistent-data 
  lvm2

# 添加docker下载仓库
sudo yum-config-manager 
    --add-repo 
    https://download.docker.com/linux/centos/docker-ce.repo

# 安装docker-ce
sudo yum install docker-ce

# 启动docker-ce
sudo systemctl start docker

# 验证
sudo docker --version

sudo docker run hello-world

2、安装 docker-compose

sudo curl -L "https://github.com/docker/compose/releases/download/1.23.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose

sudo chmod +x /usr/local/bin/docker-compose

docker-compose --version

关于docker-Compose基本使用

简介

Compose 是一个用户定义和运行多个容器的 Docker 应用程序。在 Compose 中你可以使用 YAML 文件来配置你的应用服务。然后,只需要一个简单的命令,就可以创建并启动你配置的所有服务。
使用 Compose 基本会有如下三步流程:

  • 在 Dockfile 中定义你的应用环境,使其可以在任何地方复制。
  • 在 docker-compose.yml 中定义组成应用程序的服务,以便它们可以在隔离的环境中一起运行。
  • 最后,运行dcoker-compose up,Compose 将启动并运行整个应用程序。

安装docker-Compose

目前有两种主流安装方式,笔者使用了第一种方式。

第一种

下载最新的docker-compose文件

curl -L https://github.com/docker/compose/releases/download/1.16.1/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose

下载完成后需要对/usr/local/bin/docker-compose目录进行赋权

chmod +x /usr/local/bin/docker-compose

测试结果

docker-compose --version

输出

docker-compose version 1.16.1, build 6d1ac21

第二种

通过pip方式安装

pip install docker-compose

前提是需要你的服务器已经装了pip组件

卸载

第一种

rm /usr/local/bin/docker-compose

第二种

pip uninstall docker-compose

使用

我们这里以kafka为例

version: '2'

services:
  zoo1:
    # 依赖于wurstmeister/zookeeper镜像,本地无则自动下载
    image: wurstmeister/zookeeper
    restart: unless-stopped
    hostname: zoo1
    # 映射端口
    ports:
      - "2181:2181"
    # 容器名称
    container_name: zookeeper
  kafka1:
    # 依赖于wurstmeister/kafka镜像
    image: wurstmeister/kafka
    # 映射端口
    ports:
      - "9092:9092"
    # 目录挂载 【容器目录:宿主机目录】
    volumes:
      - /var/log/kafka/logs:/var/docker/kafka/logs
    # 配置环境变量
    environment:
      KAFKA_ADVERTISED_HOST_NAME: localhost
      KAFKA_ZOOKEEPER_CONNECT: "zoo1:2181"
      KAFKA_BROKER_ID: 1
      KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1
      KAFKA_CREATE_TOPICS: "stream-in:1:1,stream-out:1:1"
    # 解决服务启动顺序问题,例如下面容器会先确定zoo1和redis两个服务,最后才启动kafka1服务
    depends_on:
      - zoo1
      - redis(实际无该容器)
    # 容器名称
    container_name: kafka

执行docker-compose

首先将docker-compos.yml上传至服务器,然后进入目录执行:

docker-compose up -d

则开始后台构建服务

如果想单独启动一个服务,你可以:

docker-compose up -d 指定服务名称

例子:docker-compose up -d zoo1

服务排编案例

version: "3"
  services:
    # 指定服务名称
    #服务注册与发现中心
    simonEureka:
      image: simon/eureka-server:2.0.1-SNAPSHOT
      hostname: simonEureka
      ports:
        - "8100:8100"
    #配置中心    
    simonConfig:
      image: simon/config-server:2.0.1-SNAPSHOT
      hostname: simonConfig
      ports:
        - "8101:8101"
      depends_on:
        - simonEureka
      # always – 不管退出状态码是什么始终重启容器。当指定always时,docker daemon将无限次数地重启容器。容器也会在daemon启动时尝试重启,不管容器当时的状态如何。
      # no – 容器退出时不要自动重启。这个是默认值。
      # on-failure[:max-retries] – 只在容器以非0状态码退出时重启。可选的,可以退出docker daemon尝试重启容器的次数。
      # unless-stopped - 不管退出状态码是什么始终重启容器,不过当daemon启动时,如果容器之前已经为停止状态,不要尝试启动它。
      restart: always
    #路由网关  
    apigateway:
      image: simon/apigateway:2.0.1-SNAPSHOT
      ports:
        - "8102:8102"
      depends_on:
        - simonEureka
        - simonConfig
      restart: always
    #监控平台  
    admin:
      image: simon/admin:2.0.1-SNAPSHOT
      ports:
        - "8103:8103"
      depends_on:
        - simonEureka
        - simonConfig
      restart: always

这个时候我们服务器simon目录的文件应该如下:

apigateway:2.0.1-SNAPSHOT.jar
admin:2.0.1-SNAPSHOT.jar
config-server:2.0.1-SNAPSHOT.jar
eureka-server:2.0.1-SNAPSHOT.jar
docker-compose.yml

注意点

如果我们的yml文件不是docker-compose.yml时我们在进行服务排编是需要指定yml文件名称。

docker-compose -f docker-kafka.yml up -d

当我们遇到服务启动需要先后顺序时,我们可以对docker-compose.yml根据服务的先后顺序进行拆分。

常用的docker-compose命令

未分类

docker部署ELK(logstash、elasticsearch、kibana),监控日志

由于是首次部署,第一次想着是单独部署logstash、elasticsearch、kibana,然后通过配置实现日志的监控,以下为部署步骤,但是最终失败,只能采取docker-compose来部署,以下内容可以略过,仅作为参考。

一、每个单独部署

先部署elasticsearch,因为logstash要设置日志输出位置,而输出位置正是elasticsearch,所以需要先部署启动elasticsearch,logstash才能部署并启动成功。

1、elasticsearch部署

1.制作elasticsearch镜像

docker pull docker.elastic.co/elasticsearch/elasticsearch:6.5.4

2.创建并启动elasticsearch容器

docker run -p 9200:9200 -p 9300:9300 -e “discovery.type=single-node” -d docker.elastic.co/elasticsearch/elasticsearch:6.5.4

通过本地浏览器访问localhost:9200,返回如下内容,则证明elasticsearch部署成功

未分类

官方参考:https://www.elastic.co/guide/en/elasticsearch/reference/current/docker.html

2、logstash部署

1.制作logstash镜像

docker pull docker.elastic.co/logstash/logstash:6.5.4

2.创建并启动logstash容器

docker run --rm -it -p 4560:4560 -v /home/xijie/app/mylogstash/pipeline/:/usr/share/logstash/pipeline/ -d docker.elastic.co/logstash/logstash:6.5.4

注意文件pipeline的映射,在pipeline中需要创建logstash的配置文件logstash-springboot.conf,该文件内容如下:

input {
    tcp {
        port => 4560
        codec => json_lines
    }
}
output{
  elasticsearch { hosts => ["localhost:9200"] }
  stdout { codec => rubydebug }
}

注意output中设置了elasticsearch的连接地址:localhost:9200

官方参考文章:https://www.elastic.co/guide/en/logstash/current/docker-config.html

3、kibana部署

1.制作镜像

docker pull docker.elastic.co/kibana/kibana:6.5.4

2.创建并启动容器

docker run -p 5601:5601 --mount type=bind,src=/home/xijie/app/mykibana/kibana.yml,dst=/usr/share/kibana/config/kibana.yml -d mysql/mysql-server:5.7

二、docker-compose一起部署

参考文章:https://www.jianshu.com/p/c2f6e80b2756

1、第一步在docker上安装ELK

1.创建目录

mkdir /home/xijie/app/myelk

2.从github上拉取部署elk所需资料

$ git clone https://github.com/deviantony/docker-elk.git

下载完毕的资料目录如下:

未分类

3.进入刚下载的文件夹内

$ cd docker-elk

4.通过docker-compose创建并启动容器

$ docker-compose up -d

5.这个时候通过docker ps可以看到logstash、elasticsearch、kibana容器已经创建并且启动。

未分类

可以看该elk容器的默认端口为:

  • 5000: Logstash TCP input.
  • 9200: Elasticsearch HTTP
  • 9300: Elasticsearch TCP transport
  • 5601: Kibana

Kibana的web入口:http://localhost:5601

6.接下来进行参数配置,来实现springboot通过ELK查看日志信息。

修改logstash的配置,

进入logstash配置文件所在目录:

cd /home/xijie/app/myelk/dokcer-elk/logstash/pipeline

打开配置文件:

vim logstash.conf

修改内容如下:

input{
        tcp {
                mode => "server"
                port => 5000
                codec => json_lines
                tags => ["data-http"]
        }
}
filter{
    json{
        source => "message"
        remove_field => ["message"]
    }
}
output{
    if "data-http" in [tags]{
        elasticsearch{
                hosts=> ["elasticsearch:9200"]
                index => "data-http-%{+YYYY.MM.dd}"
                }
        stdout{codec => rubydebug}
    }
}

注:

input标签为logstash进数据接口,filter标签为数据过滤器,output为数据出去接口。

input标签使用的是tcp,说明springboot客户端需要将日志传递到该接口,该接口正是logstash服务器接口。

filter将message字段去掉,只是为了当展示springboot的http请求接口的数据更加规整,而不是全部展示在message字段中。

output标签将数据传递给了elasticsearch,这里使用了if,当判断所出数据为所指定tag,才进行下面的配置。特别要注意index的配置,该值在kibana中需要使用,这里指定的index值为:data-http,要注意该值与tag是没有关系的,要注意区分。

未分类

参考文章:https://www.elastic.co/guide/en/logstash/current/index.html

这个时候重启elk即可。

进入docker-elk目录

/home/xijie/app/myelk/docker-elk

然后重启

docker-compose restart

关于elasticsearch、logstash、kibana的配置都在对应目录下的config文件夹中的.yml文件中,只需要修改该文件即可。

2、springboot日志系统配置logstash

1.pom中配置logstash

<dependency>
        <groupId>net.logstash.logback</groupId>
         <artifactId>logstash-logback-encoder</artifactId>
         <version>5.2</version>
</dependency>

2.application.properties文件中配置如下:

#logstash日志收集地址,即logstash服务器地址
logstash.ip_port=172.168.0.165:5000
#日志保存级别
logging.all.level=info
#日志保存地址,该值与logstash没关系,是当日志存在本地File文件内的文件夹地址
logging.levelfile=/home/logs/data-center-service

3.logback.xml文件内容如下,该文件在resources目录

<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="false">
    <property resource="application.properties"></property>
    <!--定义日志文件的存储地址 勿在 LogBack 的配置中使用相对路径-->
    <property name="LOG_HOME" value="${logging.levelfile}" />
    <property name="LOG_LEVEL" value="${logging.all.level}" />
    <!-- 控制台输出 -->
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50}:%L - %msg  %n</pattern>
            <charset>UTF-8</charset>
        </encoder>
    </appender>
    <!-- 按照每天生成日志文件 -->
    <appender name="FILE"  class="ch.qos.logback.core.rolling.RollingFileAppender">
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!--日志文件输出的文件名-->
            <FileNamePattern>${LOG_HOME}/data-center-service.log.%d{yyyy-MM-dd}.log</FileNamePattern>
            <!--日志文件保留天数-->
            <MaxHistory>30</MaxHistory>
        </rollingPolicy>
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
            <charset>UTF-8</charset>
        </encoder>
        <!--日志文件最大的大小-->
        <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
            <MaxFileSize>100MB</MaxFileSize>
        </triggeringPolicy>
    </appender>
    <appender name="logstash" class="net.logstash.logback.appender.LogstashTcpSocketAppender">
        <destination>${logstash.ip_port}</destination>
        <encoder charset="UTF-8" class="net.logstash.logback.encoder.LogstashEncoder" />
        <queueSize>1048576</queueSize>
        <keepAliveDuration>5 minutes</keepAliveDuration>
        <!--<customFields>{"application-name":"data-repo-interface"}</customFields>-->
        <!--<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>INFO</level>
        </filter>
        <filter class="ch.qos.logback.core.filter.EvaluatorFilter">
            <evaluator> <!– 默认为 ch.qos.logback.classic.boolex.JaninoEventEvaluator –>
                <expression>return message.contains("billing");</expression>
            </evaluator>
            <OnMatch>ACCEPT</OnMatch>
            <OnMismatch>DENY</OnMismatch>
        </filter>-->
    </appender>

    <logger name="elk_logger" level="INFO" additivity="false">
        <appender-ref ref="logstash"/>
    </logger>

    <!--<logger name="com.zaxxer" level="${LOG_LEVEL}"/>-->
    <!--<logger name="org.apache.ibatis" level="${LOG_LEVEL}"/>-->
    <!--<logger name="org.mybatis.spring" level="${LOG_LEVEL}"/>-->
    <!--<logger name="org.springframework" level="${LOG_LEVEL}"/>-->
    <!--<logger name="java.sql.Connection" level="${LOG_LEVEL}"/>-->
    <!--<logger name="java.sql.Statement" level="${LOG_LEVEL}"/>-->
    <!--<logger name="java.sql.PreparedStatement" level="${LOG_LEVEL}"/>-->

    <!-- 日志输出级别 -->
    <root level="${LOG_LEVEL}">
        <appender-ref ref="STDOUT" />
        <appender-ref ref="FILE" />
        <!--<appender-ref ref="logstash" />-->
    </root>
</configuration>

在上述配置文件中

<appender name="logstash" class="net.logstash.logback.appender.LogstashTcpSocketAppender">
        <destination>${logstash.ip_port}</destination>
        <encoder charset="UTF-8" class="net.logstash.logback.encoder.LogstashEncoder" />
        <queueSize>1048576</queueSize>
        <keepAliveDuration>5 minutes</keepAliveDuration>
        <!--<customFields>{"application-name":"data-repo-interface"}</customFields>-->
        <!--<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>INFO</level>
        </filter>
        <filter class="ch.qos.logback.core.filter.EvaluatorFilter">
            <evaluator> <!– 默认为 ch.qos.logback.classic.boolex.JaninoEventEvaluator –>
                <expression>return message.contains("billing");</expression>
            </evaluator>
            <OnMatch>ACCEPT</OnMatch>
            <OnMismatch>DENY</OnMismatch>
        </filter>-->
    </appender>

    <logger name="elk_logger" level="INFO" additivity="false">
        <appender-ref ref="logstash"/>
    </logger>

是logstash的主要配置。注意该配置中name=”elk_logger”,这样的指定,即

Logger elkLogger = LoggerFactory.getLogger("elk_logger");

当使用slf4j生成Logger时,只要指定Tag为“elk_logger”的日志输出或打印,都会上传到logstash服务器,并存储到elasticsearch,用kibana查看。

3、配置kibana,现在只要服务器通过指定的Tag打印日志,日志信息将会上传logstash解析,并且存储到elasticsearch,然后只需要kibana配置对应的elasticsearch的index即可看到所需的日志信息。

通过浏览器访问kibana,http://localhost:5601

由于本人是通过虚拟机部署的服务,而且虚拟机的ip为172.168.0.165,所以在宿主机中通过访问http://172.168.0.165:5601即可。

未分类

然后点击左侧的management模块。

未分类

接下来点击Index Patterns,创建index pattern

未分类

接下来填入信息,在index pattern框中填入上述创建ElK时logstash配置文件中的index信息“data-http-*”

未分类

填写的同时,下面会显示出elasticsearch中已经有该index得数据,(注意:在配置kibana时,应该先运行服务器,让服务区打印出对应的index日志,elasticsearch中也保存了该日志,这时才能配置kibana成功)。

然后点击右侧的next step。

未分类

在Time Filter field name列表中选择@timestamp,然后按下Create index pattern按钮及创建成功。

这时点击左侧的Discover模块,选中data-http-*标签即可。在右侧就显示出了日志信息。如下图:

未分类

这样整个ELK与日志系统就搭建完毕了。

4、接下讲一下我们工程中关于http接口日志的配置

由于我们提供的服务器接口是上游,是给其他部门服务的,这里会牵扯到大量的专业数据,而为了避免数据问题的纠纷与接口错误问题的排查,所以需要将特定的接口请求数据保存,这样可以通过resquest与response来排查到底是哪个部门的问题。

1.使用了AOP对每次请求进行日志拦截。

定义SysLogAspect类,该类内容如下:

package com;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.SpringContextUtil;
import com.LoggerEntity;
import com.MailService;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.reflect.Field;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

@Aspect
@Component
@Slf4j
public class SysLogAspect {

    Logger elkLogger = LoggerFactory.getLogger("elk_logger");


    /**
     * 开始时间
     */
    private long startTime = 0L;
    /**
     * 结束时间
     */
    private long endTime = 0L;

//  @Autowired
//  private ILogService logService;

    public static Map<String, Object> getKeyAndValue(Object obj) {
        Map<String, Object> map = new HashMap<>();
        // 得到类对象
        Class userCla = (Class) obj.getClass();
        /* 得到类中的所有属性集合 */
        Field[] fs = userCla.getFields();
        for (int i = 0; i < fs.length; i++) {
            Field f = fs[i];
            f.setAccessible(true); // 设置些属性是可以访问的
            Object val = new Object();
            try {
                val = f.get(obj);
                // 得到此属性的值
                map.put(f.getName(), val);// 设置键值
            } catch (IllegalArgumentException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }

        }
        return map;
    }

    //通过该注解来判断该接口请求是否进行cut
    @Pointcut("@annotation(com.hongdaoai.datastore.common.annotation.SysLog)")
    public void cutController() {
    }

    @Before("cutController()")
    public void doBeforeInServiceLayer(JoinPoint joinPoint) {
        log.debug("doBeforeInServiceLayer");
        startTime = System.currentTimeMillis();
    }

    @After("cutController()")
    public void doAfterInServiceLayer(JoinPoint joinPoint) {
        log.debug("doAfterInServiceLayer");
    }

    @Around("cutController()")
    public Object recordSysLog(ProceedingJoinPoint joinPoint) throws Throwable {

        RequestAttributes ra = RequestContextHolder.getRequestAttributes();

        ServletRequestAttributes sra = (ServletRequestAttributes) ra;

        HttpServletRequest request = sra.getRequest();

        HttpServletResponse response = sra.getResponse();

        //ELK日志实体类
        LoggerEntity elkLog = new LoggerEntity();

        //应用程序名称
        elkLog.setApplicationName(SpringContextUtil.getApplicationName());

        //profile.active
        elkLog.setProfileActive(SpringContextUtil.getActiveProfile());

        // 请求的类名
        elkLog.setClassName(joinPoint.getTarget().getClass().getName());

        // 请求的方法名
        elkLog.setMethodName(joinPoint.getSignature().getName());

        //请求完整地址
        elkLog.setUrl(request.getRequestURL().toString());

        //请求URI
        elkLog.setUri(request.getRequestURI());

        //请求类型
        elkLog.setRequestMethod(request.getMethod());

        String queryString = request.getQueryString();

        Object[] args = joinPoint.getArgs();

        String params = "";

        //获取请求参数集合并进行遍历拼接
        if (args.length > 0) {

            if ("POST".equals(request.getMethod())) {

                //param
                Object object = args[0];

                Map<String, Object> map = new HashMap<>();

                Map paramMap = getKeyAndValue(object);

                map.put("param", paramMap);

                if (args.length > 1) {

                    object = args[1];

                    Map bodyMap = getKeyAndValue(object);

                    map.put("body", bodyMap);
                }

                params = JSON.toJSONStringWithDateFormat(map, "yyyy-MM-dd HH:mm:ss", SerializerFeature.UseSingleQuotes);

            } else if ("GET".equals(request.getMethod())) {

                params = queryString;

            }
        }

        //请求参数内容(param)
        elkLog.setRequestParamData(params);

        //客户端IP
        elkLog.setClientIp(request.getRemoteAddr());

        //终端请求方式,普通请求,ajax请求
        elkLog.setRequestType(request.getHeader("X-Requested-With"));

        //sessionId
        elkLog.setSessionId(request.getRequestedSessionId());

        //请求时间
//        elkLog.setRequestDateTime(new Date(startTime));
        elkLog.setRequestDateTime(new Date());

        Object result = null;

        try {

            // 环绕通知 ProceedingJoinPoint执行proceed方法的作用是让目标方法执行,这也是环绕通知和前置、后置通知方法的一个最大区别。
            result = joinPoint.proceed();

        } catch (Exception e) {

            endTime = System.currentTimeMillis();

            //请求耗时(单位:毫秒)
            elkLog.setSpentTime(endTime - startTime);

            //接口返回时间
            elkLog.setResponseDateTime(new Date(endTime));

            //请求时httpStatusCode代码
            elkLog.setHttpStatusCode(String.valueOf(response.getStatus()));

            String elkLogData = JSON.toJSONStringWithDateFormat(elkLog, "yyyy-MM-dd HH:mm:ss.SSS");

            elkLogger.error(elkLogData);

//            log.error(e.getMessage(), e);

            throw e;

//            return result;

        }

        endTime = System.currentTimeMillis();

        //请求耗时(单位:毫秒)
        elkLog.setSpentTime(endTime - startTime);

        if (!elkLog.getMethodName().equals("getBookOriginalContent")) {

            //接口返回数据
            elkLog.setResponseData(JSON.toJSONStringWithDateFormat(result, "yyyy-MM-dd HH:mm:ss", SerializerFeature.UseSingleQuotes));

        }

        //接口返回时间
        elkLog.setResponseDateTime(new Date(endTime));

        //请求时httpStatusCode代码
        elkLog.setHttpStatusCode(String.valueOf(response.getStatus()));

//        if (SpringContextUtil.getActiveProfile().equals("prod")) {
        String s = JSON.toJSONStringWithDateFormat(elkLog, "yyyy-MM-dd HH:mm:ss.SSS");
        elkLogger.info(s);

        return result;
    }


}

SpringContextUtil类内容如下:

package com;

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

import java.util.Locale;

@Component
public class SpringContextUtil implements ApplicationContextAware {

    private static ApplicationContext context = null;

    // 传入线程中
    public static <T> T getBean(String beanName) {
        return (T) context.getBean(beanName);
    }

    // 国际化使用
    public static String getMessage(String key) {
        return context.getMessage(key, null, Locale.getDefault());
    }

    /// 获取应用程序名称
    public static String getApplicationName() {
        return context.getEnvironment().getProperty("spring.application.name");
    }

    /// 获取当前环境
    public static String getActiveProfile() {
        return context.getEnvironment().getActiveProfiles()[0];
    }

    /* (non Javadoc)
     * @Title: setApplicationContext
     * @Description: spring获取bean工具类
     * @param applicationContext
     * @throws BeansException
     * @see org.springframework.context.ApplicationContextAware#setApplicationContext(org.springframework.context.ApplicationContext)
     */
    @Override
    public void setApplicationContext(ApplicationContext applicationContext)
            throws BeansException {
        this.context = applicationContext;
    }
}

LoggerEntity类内容如下:

package com;

import lombok.Data;

import java.io.Serializable;
import java.util.Date;

//@Entity
//@Table(name = "t_logger_infos")
@Data
public class LoggerEntity implements Serializable {

    //应用程序名
    private String applicationName;

    //spring.profiles.active
    private String profileActive;

    //客户端请求ip
    private String clientIp;

    //客户端请求路径
    private String uri;

    //客户端请求完整路径
    private String url;

    //请求方法名
    private String methodName;

    //请求类名
    private String className;

    //终端请求方式,普通请求,ajax请求
    private String requestType;

    //请求方式method,post,get等
    private String requestMethod;

    //请求参数内容,json
    private String requestParamData;

    //请求body参数内容,json
    private String requestBodyData;

    //请求接口唯一session标识
    private String sessionId;

    //请求时间
    private Date requestDateTime;

    //接口返回时间
    private Date responseDateTime;

    //接口返回数据json
    private String responseData;

    //请求时httpStatusCode代码,如:200,400,404等
    private String httpStatusCode;

    //请求耗时秒单位
    private long spentTime;

}

SysLog注解类如下:

package com;

import java.lang.annotation.*;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SysLog {

    boolean isLog() default true;

}

在SysLogAspect切面类中使用@SysLog注解来判断该接口是否进行日志传递。

所以在我们写的接口中只要在方法上加入该注解,即可控制日志是否上传。

如该Controller中所定义接口:

@SysLog(isLog = true)
@RequestMapping(value = "/getDataList", method = RequestMethod.POST)
public ApiResult getInputDataList(@RequestBody VersionVo vo) {
    return success();
}

ELK搭建环境之配置logstash监听文件并利用grok插件进行切割利于统计

一、配置ELK环境在logstash和elasticsearch之间建立通信,并能查看对应的索引、日志信息

1.本地下载logstash6.4.2运行包,不会装请移步 (ElasticSearch+LogStash+Kibana)ELK搭建在Mac/Linux系统上6.4.2版本

2.解压并进行基础配置,配置如下:

默认配置文件在安装目录 logstash6.4.2/config/logstash-sample.conf

# 监听本地目录下的test.log日志文件
input {
  file {
      path => ["/Users/chenhailong/daily-test/test.log"]
  }
}
# 配置日志发送的目标elasticsearch服务
output {
  elasticsearch {
    hosts => ["http://xxxx.xx.xxx.xx:9200"]
    index => "grok-test"
    user => "username"
    password => "password"
  }
}

3.确保elasticsearch服务是可以访问的,(开启代理、翻墙、防火墙、IP不通可能造成无法通信)

在浏览器键入对应地址:http://xxxx.xx.xxx.xx:9200

显示如下:

{
  "name" : "wqbVDEq",
  "cluster_name" : "elasticsearch",
  "cluster_uuid" : "lq8YkTH4Q7eslqvd0pn9Kg",
  "version" : {
    "number" : "6.4.2",
    "build_flavor" : "default",
    "build_type" : "tar",
    "build_hash" : "04711c2",
    "build_date" : "2018-09-26T13:34:09.098244Z",
    "build_snapshot" : false,
    "lucene_version" : "7.4.0",
    "minimum_wire_compatibility_version" : "5.6.0",
    "minimum_index_compatibility_version" : "5.0.0"
  },
  "tagline" : "You Know, for Search"
}

4.在客户端保证有效的日志文件,日志记录有序的生成。

客户端的

日志文件为:/Users/chenhailong/daily-test/test.log

日志格式为:2018-12-31,16:12:1546245470 INFO sdklog &event_id=10003&uuid=90b7786d-a905-48aa-93e9-bc1cf64ffce2&event_type=2&device_id=37037&session_id=1545899195566_37037&uid=2D9CBA4701227157E050840AF2422B52&date=2018-12-27&time=16:26:36&ts=1545899196409&

在需要测试时,执行脚本追加日志记录务必保证日志文件中有新的追加记录,确保监听到了新的数据。否则,没有新数据可能看不到效果

5.启动logstash

#指定配置文件启动

./bin/logstash -f ../conf/logstash-sample.conf

6.如果一切顺利的话,可以在kibana控制台看到相关信息

a. kibana 》系统管理 》 Index Mangement 》 可以搜索查看到对应的 “grok-test” 索引名称
b. kibana 》系统管理 》 Index Patterns 》可以创建索引前缀
c. kibana 》发现 》 可以搜索到 “grok-test”索引文件下的所有日志记录,如下

未分类

二:对日志进行切割

1.前期了解:

https://github.com/logstash-plugins/logstash-patterns-core/blob/master/patterns/grok-patterns

2.在kibana后台 》开发工具 》 Grok Debugger进行测试,如下图

不会用,请移步 使用kibana的开发工具console做一些elasticsearch的基本查询和设置

未分类

3.日志切割请参考,注意格式

%{TIMESTAMP_ISO8601:date} %{LOGLEVEL:level} %{WORD:type} &event_id=%{NUMBER:event_id}&uuid=%{USERNAME:uuid}&event_type=%{INT:event_type}&device_id=%{INT:device_id}&session_id=%{USERNAME:session_id}&uid=%{WORD:uid}&date=%{USERNAME:ymd}&time=%{TIME:time}&ts=%{NUMBER:ts}&url=%{URI:url}

4.修改配置文件,增加grok插件

# Sample Logstash configuration for creating a simple
# Beats -> Logstash -> Elasticsearch pipeline.

input {
  file {
      path => ["/Users/chenhailong/daily-test/test.log"]
  }
}

filter {
  #定义数据的格式
  grok {
    match => { "message" => "%{TIMESTAMP_ISO8601:date} %{LOGLEVEL:level} %{WORD:type} &event_id=%{NUMBER:event_id}&uuid=%{USERNAME:uuid}&event_type=%{INT:event_type}&device_id=%{INT:device_id}&session_id=%{USERNAME:session_id}&uid=%{WORD:uid}&date=%{USERNAME:ymd}&time=%{TIME:time}&ts=%{NUMBER:ts}&url=%{URI:url}"}
  }
}

output {
  elasticsearch {
    hosts => ["http://xxx.xx.xxx.xx:9200"]
    index => "grok-test"
    user => "username"
    password => "password"
  }
}

5.打开kibana 》 发现 》 搜索grok-test索引

会发现日志已经按照设定的格式进行切切割。这样就可以进行数据的报表分析,更加直观化。更有效的利用日志数据。

不局限于日常日志,也可以针对性的记录相关业务数据。

未分类

6.配置文件中还可以增加多种插件配合处理

具体可以移步查看官方文件,更加详细,更加专业

https://www.elastic.co/guide/en/logstash/current/filter-plugins.html

至此:ELK的学习使用就告一段落,有需要再去细致研究。

Linux文本处理命令grep

1. 查找单个关键字

举例:查找Baiduspider访问日志,并输出行号。

# grep -n Baiduspider /usr/local/nginx1.14/logs/access.log

3371:180.76.15.137 - - [31/Dec/2018:05:05:46 +0800] "GET /asset/detail/show/RG9ja2VyIENFIDE4LjA5IOS9v+eUqERvY2tlcmZpbGXlronoo4 HTTP/1.1" 200 4748 "-" "Mozilla/5.0 (compatible; Baiduspider/2.0; +http://www.baidu.com/search/spider.html)"
4570:180.76.15.11 - - [08/Jan/2019:16:57:04 +0800] "GET /asset/detail/show/RG9ja2VyIENFIDE4LjA5IOS9v+eUqERvY2tlcmZpbGXlronoo4 HTTP/1.1" 200 7754 "-" "Mozilla/5.0 (compatible; Baiduspider/2.0; +http://www.baidu.com/search/spider.html)"
4693:180.76.15.20 - - [09/Jan/2019:05:29:54 +0800] "GET /asset/detail/show/Q2VudE9TNy41IE15U1FMOC4wLjEzIFJQTea6kOeggee8luivke HTTP/1.1" 200 16691 "-" "Mozilla/5.0 (compatible; Baiduspider/2.0; +http://www.baidu.com/search/spider.html)"
4924:180.76.15.160 - - [10/Jan/2019:22:30:28 +0800] "GET /asset/detail/show/SlF1ZXJ5My4zLjEg6YCJ5oup5Zmo HTTP/1.1" 200 10049 "-" "Mozilla/5.0 (compatible; Baiduspider/2.0; +http://www.baidu.com/search/spider.html)"
...

2. 查找多个关键字

举例:查找Baiduspider、bingbot、Googlebot访问日志,并输出行号。

# grep -n -E 'Baiduspider|bingbot|Googlebot' /usr/local/nginx1.14/logs/access.log

5620:203.208.60.0 - - [14/Jan/2019:10:26:09 +0800] "GET / HTTP/1.1" 200 17232 "-" "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)"
5639:180.76.15.21 - - [14/Jan/2019:16:39:35 +0800] "GET / HTTP/1.1" 200 17232 "-" "Mozilla/5.0 (compatible; Baiduspider/2.0; +http://www.baidu.com/search/spider.html)"
5888:157.55.39.40 - - [17/Jan/2019:13:45:05 +0800] "GET / HTTP/1.1" 200 17232 "-" "Mozilla/5.0 (compatible; bingbot/2.0; +http://www.bing.com/bingbot.htm)"
...

3. 多文件查找关键字

举例:查找10.100.78.12服务器多天的监控日志。

# grep 10.100.78.12 2018-12-10.bjyz.network.monitor.log 2018-12-11.bjyz.network.monitor.log

2018-12-10.bjyz.network.monitor.log:[2018-12-10]|[13:10:00]|10.100.78.12|active
2018-12-11.bjyz.network.monitor.log:[2018-12-11]|[13:10:00]|10.100.78.12|down

4. 递归查找关键字

举例:递归查找10.100.78.12服务器多天的监控日志。

grep -r 10.100.78.12

2018-12-10.bjyz.network.monitor.log:[2018-12-10]|[13:10:00]|10.100.78.12|active
2018-12-11.bjyz.network.monitor.log:[2018-12-11]|[13:10:00]|10.100.78.12|down
2018-12-12.bjyz.network.monitor.log:[2018-12-12]|[13:10:00]|10.100.78.12|active

5. 查找指定进程

举例:查找java进程。

# ps -ef | grep java

6. 查找进程个数

举例:查找java进程个数。

# ps -ef | grep -c java

Redis Cluster 集群

一. 介绍

  Redis集群搭建的方式有多种,例如使用zookeeper等,但从redis 3.0之后版本支持redis-cluster集群,Redis-Cluster采用无中心结构,每个节点保存数据和整个集群状态,每个节点都和其他所有 节点连接。redis cluster自动master + slave复制和读写分离,master+slave高可用和主备切换,支持多个master的hash slot支持数据分布式存储。

二. Redis Cluster配置属性

cluster-enabled <yes/no>

cluster-config-file <filename>:这是指定一个文件,供cluster模式下的redis实例将集群状态保存在那里,包括集群中其他机器的信息,比如节点的上线和下限,故障转移,不是我们去维护的,给它指定一个文件,让redis自己去维护的

cluster-node-timeout <milliseconds>:节点存活超时时长,超过一定时长,认为节点宕机,master宕机的话就会触发主备切换,slave宕机就不会提供服务

三. 创建和配置

3.1 创建目录

/etc/redis(存放redis的配置文件)
/var/redis/6379(存放redis的持久化文件)

mkdir -p /etc/redis-cluster
mkdir -p /var/log/redis
mkdir -p /var/redis/7001

3.2 修改配置文件

(redis.conf 重命名 7001.conf)

port 7001
cluster-enabled yes
cluster-config-file /etc/redis-cluster/node-7001.conf
cluster-node-timeout 15000
daemonize yes
pidfile /var/run/redis_7001.pid
dir /var/redis/7001
logfile /var/log/redis/7001.log
bind 192.168.0.5
appendonly yes

存放路径如图所示:

未分类

将上面的配置文件(注意修改配置属性),在各虚拟机/etc/redis下依次放2个,分别为:

192.168.0.5: 7001.conf 7002.conf
192.168.0.6: 7003.conf 7004.conf
192.168.0.7: 7005.conf 7006.conf

3.3 启动脚本配置

cp /usr/local/redis-3.2.8/utils/redis_init_script /etc/init.d/redis_7001
vi /etc/init.d/redis_7001

#!/bin/sh
#
# Simple Redis init.d script conceived to work on Linux systems
# as it does use of the /proc filesystem.
# chkconfig:   2345 90 10

# description:  Redis is a persistent key-value database

# 每个启动脚本内,都修改对应的端口号
REDISPORT=7001
EXEC=/usr/local/bin/redis-server
CLIEXEC=/usr/local/bin/redis-cli

PIDFILE=/var/run/redis_${REDISPORT}.pid
CONF="/etc/redis/${REDISPORT}.conf"

case "$1" in
    start)
        if [ -f $PIDFILE ]
        then
                echo "$PIDFILE exists, process is already running or crashed"
        else
                echo "Starting Redis server..."
                $EXEC $CONF
        fi
        ;;
    stop)
        if [ ! -f $PIDFILE ]
        then
                echo "$PIDFILE does not exist, process is not running"
        else
                PID=$(cat $PIDFILE)
                echo "Stopping ..."
                $CLIEXEC -p $REDISPORT shutdown
                while [ -x /proc/${PID} ]
                do
                    echo "Waiting for Redis to shutdown ..."
                    sleep 1
                done
                echo "Redis stopped"
        fi
        ;;
    *)
        echo "Please use start or stop as first argument"
        ;;
esac

分别在每个虚拟机/etc/init.d下,放6个启动脚本,分别为:

  • 192.168.0.5: redis_7001 redis_7002
  • 192.168.0.6: redis_7003 redis_7004
  • 192.168.0.7: redis_7005 redis_7006

然后分别在3台机器上,启动6个redis实例

[root@sw01 utils]# cd /etc/init.d/
[root@sw01 utils]# ./redis_7001 start
[root@sw01 utils]# ps -ef | grep redis
root     26227  1  0 Jan21 ?  00:03:23 /usr/local/bin/redis-server 192.168.0.5:7002 [cluster]
root     26355  1  0 Jan21 ?  00:03:08 /usr/local/bin/redis-server 192.168.0.5:7001 [cluster]
root     26659 26254  0 04:44 pts/0    00:00:00 grep redis

四. 安装ruby创建集群

只需要在一台服务器安装即可

3.1 下载安装

[root@sw01 utils]# wget https://cache.ruby-lang.org/pub/ruby/2.3/ruby-2.3.1.tar.gz
[root@sw01 utils]# tar -zxvf ruby-2.3.1.tar.gz
[root@sw01 utils]# ./configure -prefix=/usr/local/ruby
[root@sw01 utils]# make && make install
[root@sw01 utils]# cd /usr/local/ruby
[root@sw01 utils]# cp bin/ruby /usr/local/bin
[root@sw01 utils]# cp bin/gem /usr/local/bin

3.2 centos安装较高版本ruby2.2+(RVM 安装)

centos安装较高版本ruby2.2+(RVM 安装)

3.3 Redis配置集群遇到问题及解决方法

Redis配置集群遇到问题及解决方法

3.4 创建集群

[root@sw01 utils]# cp /usr/local/redis-3.2.8/src/redis-trib.rb /usr/local/bin
[root@sw01 utils]# redis-trib.rb create --replicas 1 192.168.0.5:7001 192.168.0.5:7002 192.168.0.6:7003 192.168.0.6:7004 192.168.0.7:7005 192.168.0.7:7006
输入:yes
[root@sw01 utils]# redis-trib.rb check 192.168.0.5:7001
>>> Performing Cluster Check (using node 192.168.0.5:7001)
S: bcb8d971ed0875c173f50af950f6b7590b79510d 192.168.0.5:7001
   slots: (0 slots) slave
   replicates dedb9de54b51aa09106c772d743456b31e5d884f
M: 9617ff44c32b9afe9414724057cfdbeb65b492ab 192.168.0.7:7006
   slots:10923-16383 (5461 slots) master
   1 additional replica(s)
M: bb4313206fbacf94992f3b1de33e82373f9eb769 192.168.0.6:7003
   slots:5461-10922 (5462 slots) master
   1 additional replica(s)
S: 1aa3316590a71b6f788a31cebc109a76b654c5fb 192.168.0.7:7005
   slots: (0 slots) slave
   replicates 9617ff44c32b9afe9414724057cfdbeb65b492ab
S: f5648903d98526ee798b7aa7dfcc3f747652a229 192.168.0.5:7002
   slots: (0 slots) slave
   replicates bb4313206fbacf94992f3b1de33e82373f9eb769
M: dedb9de54b51aa09106c772d743456b31e5d884f 192.168.0.6:7004
   slots:0-5460 (5461 slots) master
   1 additional replica(s)
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.

配置完成