CentOS 7安装Harbor Docker Registry

Harbor是VMware公司开源的企业级Docker Registry,在原生Docker Registry的基础上增加了一些安全、访问控制、管理等功能以满足企业对于镜像仓库的需求。Harbor以docker-compose的规范形式组织各个组件,并通过docker-compose工具进行启停。

安装docker-compose

[root@node1 /root/harbor]#curl -L https://github.com/docker/compose/releases/download/1.13.0/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose
[root@node1 /root/harbor]#chmod +x /usr/local/bin/docker-compose
[root@node1 /root/harbor]#docker-compose version
docker-compose version 1.13.0, build 1719ceb
docker-py version: 2.2.1
CPython version: 2.7.13
OpenSSL version: OpenSSL 1.0.1t  3 May 2016

修改harbor.cfg配置文件里的hostname参数为本机ip

hostname = 172.172.20.33

开始安装harbor

[root@node1 /root/harbor]#./install.sh 

[Step 0]: checking installation environment …

Note: docker version: 1.12.6

Note: docker-compose version: 1.13.0

[Step 1]: loading Harbor images …
dd60b611baaa: Loading layer [==================================================>] 133.2 MB/133.2 MB
0bfc226dc2e8: Loading layer [==================================================>] 1.536 kB/1.536 kB
66c3231118d2: Loading layer [==================================================>] 17.69 MB/17.69 MB
fe2c778bb727: Loading layer [==================================================>] 17.69 MB/17.69 MB
Loaded image: vmware/harbor-jobservice:v1.1.2                                   ] 196.6 kB/17.69 MB
fe4c16cbf7a4: Loading layer [==================================================>] 128.9 MB/128.9 MB
c4a8b7411af4: Loading layer [==================================================>] 60.57 MB/60.57 MB
3f117c44afbb: Loading layer [==================================================>] 3.584 kB/3.584 kB
3569f62067e2: Loading layer [==================================================>] 17.86 MB/17.86 MB
Loaded image: vmware/nginx:1.11.5-patched                                       ] 196.6 kB/17.86 MB
Loaded image: photon:1.0
4a050fccec52: Loading layer [==================================================>] 12.16 MB/12.16 MB
d918d73369ec: Loading layer [==================================================>]  17.3 MB/17.3 MB
22898836924e: Loading layer [==================================================>] 15.87 kB/15.87 kB
Loaded image: vmware/notary-photon:server-0.5.0                                 ]    512 B/15.87 kB
a39bd6a7f897: Loading layer [==================================================>] 10.95 MB/10.95 MB
6f79b8337a1f: Loading layer [==================================================>]  17.3 MB/17.3 MB
74bbd0e81dd0: Loading layer [==================================================>] 15.87 kB/15.87 kB
Loaded image: vmware/notary-photon:signer-0.5.0                                 ]    512 B/15.87 kB
2df722677b4c: Loading layer [==================================================>] 7.062 MB/7.062 MB
e5338f288c70: Loading layer [==================================================>] 7.062 MB/7.062 MB
Loaded image: vmware/harbor-adminserver:v1.1.2                                  ]  98.3 kB/7.062 MB
b79e6c985050: Loading layer [==================================================>] 21.26 MB/21.26 MB
568e827ac2db: Loading layer [==================================================>] 7.168 kB/7.168 kB
e120e08d1ae8: Loading layer [==================================================>] 12.92 MB/12.92 MB
c678c146825f: Loading layer [==================================================>] 9.728 kB/9.728 kB
835ee5702bce: Loading layer [==================================================>]  2.56 kB/2.56 kB
eaf7ac0e9e24: Loading layer [==================================================>] 21.26 MB/21.26 MB
Loaded image: vmware/harbor-ui:v1.1.2                                           ] 229.4 kB/21.26 MB
c8ef72937018: Loading layer [==================================================>] 67.93 MB/67.93 MB
01e57c31fb31: Loading layer [==================================================>] 3.584 kB/3.584 kB
ae8312f0516f: Loading layer [==================================================>] 3.072 kB/3.072 kB
47b646017cc6: Loading layer [==================================================>] 3.072 kB/3.072 kB
Loaded image: vmware/harbor-log:v1.1.2                                          ]    512 B/3.072 kB
5d6cbe0dbcf9: Loading layer [==================================================>] 129.2 MB/129.2 MB
435f2dfbd884: Loading layer [==================================================>] 344.6 kB/344.6 kB
814d7b59f0cc: Loading layer [==================================================>] 4.657 MB/4.657 MB
aae399245bd0: Loading layer [==================================================>] 1.536 kB/1.536 kB
21e2ae955f72: Loading layer [==================================================>] 33.84 MB/33.84 MB
a2d0f7b84059: Loading layer [==================================================>] 25.09 kB/25.09 kB
819fa6af55b8: Loading layer [==================================================>] 3.584 kB/3.584 kB
78914c99a468: Loading layer [==================================================>] 167.7 MB/167.7 MB
36e79c658afb: Loading layer [==================================================>] 6.144 kB/6.144 kB
f73503aca003: Loading layer [==================================================>] 9.216 kB/9.216 kB
a21b39f6da59: Loading layer [==================================================>] 1.536 kB/1.536 kB
2f0fcce131fa: Loading layer [==================================================>]  7.68 kB/7.68 kB
cbf999ad70ad: Loading layer [==================================================>] 4.608 kB/4.608 kB
8005207f317c: Loading layer [==================================================>] 4.608 kB/4.608 kB
Loaded image: vmware/harbor-db:v1.1.2                                           ]    512 B/4.608 kB
69c25b821c78: Loading layer [==================================================>] 22.79 MB/22.79 MB
5b403ac6f7ea: Loading layer [==================================================>] 3.584 kB/3.584 kB
9e2e304b5fe5: Loading layer [==================================================>] 2.048 kB/2.048 kB
Loaded image: vmware/registry:2.6.1-photon                                      ]    512 B/2.048 kB
78dbfa5b7cbc: Loading layer [==================================================>] 130.9 MB/130.9 MB
5f70bf18a086: Loading layer [==================================================>] 1.024 kB/1.024 kB
8deec01122be: Loading layer [==================================================>] 344.6 kB/344.6 kB
574ab36807f2: Loading layer [==================================================>] 1.536 kB/1.536 kB
d8f2cde2eef8: Loading layer [==================================================>] 20.48 kB/20.48 kB
eaa3924b054e: Loading layer [==================================================>]  5.12 kB/5.12 kB
8aa2c772121c: Loading layer [==================================================>] 184.3 MB/184.3 MB
c3014bbccb0b: Loading layer [==================================================>] 8.704 kB/8.704 kB
978a35efaa8c: Loading layer [==================================================>] 4.608 kB/4.608 kB
c2385ae7d6e5: Loading layer [==================================================>]  16.6 MB/16.6 MB
Loaded image: vmware/harbor-notary-db:mariadb-10.1.10                           ] 196.6 kB/16.6 MB


[Step 2]: preparing environment …
Generated and saved secret to file: /data/secretkey
Generated configuration file: ./common/config/nginx/nginx.conf
Generated configuration file: ./common/config/adminserver/env
Generated configuration file: ./common/config/ui/env
Generated configuration file: ./common/config/registry/config.yml
Generated configuration file: ./common/config/db/env
Generated configuration file: ./common/config/jobservice/env
Generated configuration file: ./common/config/jobservice/app.conf
Generated configuration file: ./common/config/ui/app.conf
Generated certificate, key file: ./common/config/ui/private_key.pem, cert file: ./common/config/registry/root.crt
The configuration files are ready, please use docker-compose to start the service.


[Step 3]: checking existing instance of Harbor …


[Step 4]: starting Harbor …
Creating network "harbor_harbor" with the default driver
Creating harbor-log … 
Creating harbor-log … done
Creating harbor-db … 
Creating harbor-adminserver … 
Creating registry … 
Creating harbor-db
Creating registry
Creating harbor-adminserver … done
Creating harbor-ui … 
Creating harbor-ui … done
Creating nginx … 
Creating harbor-jobservice … 
Creating nginx
Creating harbor-jobservice … done

✔ —-Harbor has been installed and started successfully.—-

Now you should be able to visit the admin portal at http://172.172.20.33. 
For more details, please visit https://github.com/vmware/harbor .

安装完毕后使用docker-compose查看

[root@node1 /root/harbor]#docker-compose ps
       Name                     Command               State                                Ports                               
——————————————————————————————————————————
harbor-adminserver   /harbor/harbor_adminserver       Up                                                                       
harbor-db            docker-entrypoint.sh mysqld      Up      3306/tcp                                                         
harbor-jobservice    /harbor/harbor_jobservice        Up                                                                       
harbor-log           /bin/sh -c crond && rm -f  …   Up      127.0.0.1:1514->514/tcp                                          
harbor-ui            /harbor/harbor_ui                Up                                                                       
nginx                nginx -g daemon off;             Up      0.0.0.0:443->443/tcp, 0.0.0.0:4443->4443/tcp, 0.0.0.0:80->80/tcp 
registry             /entrypoint.sh serve /etc/ …   Up      5000/tcp                    

如果需要修改配置重新安装执行以下操作

docker-compose down -v
./prepare
./install.sh

harbor的关闭和启动

[root@node1 /root/harbor]#docker-compose down -v
Stopping harbor-jobservice … done
Stopping nginx … done
Stopping harbor-ui … done
Stopping harbor-adminserver … done
Stopping registry … done
Stopping harbor-db … done
Stopping harbor-log … done
Removing harbor-jobservice … done
Removing nginx … done
Removing harbor-ui … done
Removing harbor-adminserver … done
Removing registry … done
Removing harbor-db … done
Removing harbor-log … done
Removing network harbor_harbor
[root@node1 /root/harbor]#
[root@node1 /root/harbor]#docker-compose ps
Name   Command   State   Ports 
——————————
[root@node1 /root/harbor]#
[root@node1 /root/harbor]#docker-compose up -d
Creating network "harbor_harbor" with the default driver
Creating harbor-log … 
Creating harbor-log … done
Creating registry … 
Creating harbor-adminserver … 
Creating harbor-db … 
Creating harbor-adminserver
Creating registry
Creating harbor-adminserver … done
Creating harbor-ui … 
Creating harbor-ui … done
Creating nginx … 
Creating harbor-jobservice … 
Creating nginx
Creating harbor-jobservice … done
[root@node1 /root/harbor]#
[root@node1 /root/harbor]#docker-compose ps
       Name                     Command               State                                Ports                               
——————————————————————————————————————————
harbor-adminserver   /harbor/harbor_adminserver       Up                                                                       
harbor-db            docker-entrypoint.sh mysqld      Up      3306/tcp                                                         
harbor-jobservice    /harbor/harbor_jobservice        Up                                                                       
harbor-log           /bin/sh -c crond && rm -f  …   Up      127.0.0.1:1514->514/tcp                                          
harbor-ui            /harbor/harbor_ui                Up                                                                       
nginx                nginx -g daemon off;             Up      0.0.0.0:443->443/tcp, 0.0.0.0:4443->4443/tcp, 0.0.0.0:80->80/tcp 
registry             /entrypoint.sh serve /etc/ …   Up      5000/tcp          

通过浏览器访问harbor,默认用户名和密码是admin/Harbor12345

http://172.172.20.33

未分类

未分类

Ansible roles实现LAMP架构部署wordpress

为何使用Ansible及Ansible特点? Agentless(去中心化) Stupied Simple SSH by default YAML no code,定制剧本 基于python实现 模块化:调用特定模块 ,完成特定任务,支持自定义模块

整个role目录的结构:

.
├── ansible.cfg
├── hosts
├── httpd-php_roles.retry
├── httpd-php_roles.yml
├── roles
│ ├── httpd
│ │ ├── files
│ │ │ ├── mysql_wp.sh
│ │ │ ├── wordpress-4.7.4-zh_CN.tar.gz
│ │ │ ├── wordpress.conf
│ │ │ └── wp-config.php
│ │ ├── handlers
│ │ │ └── main.yml
│ │ ├── tasks
│ │ │ └── main.yml
│ │ └── vars
│ ├── mariadb
│ │ ├── defaults
│ │ ├── files
│ │ ├── handlers
│ │ │ └── main.yml
│ │ ├── tasks
│ │ │ └── main.yml
│ │ ├── templetes
│ │ └── vars
│ └── php
│ ├── defaults
│ ├── files
│ ├── handlers
│ ├── meta
│ ├── tasks
│ │ └── main.yml
│ ├── tmpletes
│ └── vars
└── wp-config.php

1. 定义hosts目标主机:

vim /etc/ansible/hosts
[httpd]
192.168.5.111

2. 创建httpd相关的目录:

mkdir httpd/{tasks,vars,files} -pv

3. 进入tasks目录创建并编辑main.yml

---
- name: install httpd
  yum: name=httpd state=present
- name: conf httpd
  copy: src=wordpress.conf dest=/etc/httpd/conf.d/
- name: file wordpress
  copy: src=wordpress-4.7.4-zh_CN.tar.gz dest=/var/www/html
- name: tar wordpress.tar.gz
  shell: "tar -xf wordpress-4.7.4-zh_CN.tar.gz && chown -R apache.apache wordpress && mv wordpress-4.7.4-zh_CN.tar.gz /usr/local/src"
  args:
    chdir: /var/www/html 
  notify: start httpd  #当上面的执行完成后会触发handler定义的同名的动作 
- name: conf wp-config.php
  copy: src=wp-config.php dest=/var/www/html/wordpress
- name: mysql_wp.sh
  script: mysql_wp.sh
  notify: restart httpd 
- name: restart httpd
    service: name=httpd state=restarted

4. 将所需的文件放入roles/httpd/files中,文件中的内容下面用到会提及

files/
├── mysql_wp.sh
├── wordpress-4.7.4-zh_CN.tar.gz
├── wordpress.conf
└── wp-config.php

5. 在handlers中编辑main.yml定义触发的动作

---
- name: start httpd
  service: name=httpd state=started
- name: restart httpd
  service: name=httpd state=restarted

6. httpd的已经完成,接下来定义php模块的内容,同样在roles中创建php相关目录:

mkdir php/{tmpletes,tasks,files,handlers,meta,defaults,vars} -pv

7. 编辑tasks中的main.yml文件,这里只是安装了一个php模块:

---
- name: install php
  yum: name=php state=present

8. 定义mariadb相关的内容的目录:

mkdir mariadb/{templetes,handlers,vars,files,defaults,tasks} -vp

9. 编辑roles/mariadb/tasks/main.yml文件,顺带安装一下几个模块:

---
- name: install mariadb
  yum: name=mariadb-server state=present
- name: install httpd-mysql
  yum: name=php-mysql state=present
- name: install php-mbstring
  yum: name=php-mbstring state=present
- name: restart mariadb 
  service: name=mariadb state=restarted

10. wordpress程序需要手动更改一个配置文件,事先编辑好直接推送wordpress/目录下,这就是roles/httpd/files中的wp-conf.php,需要更改内容如下:

define('DB_NAME', 'wordpress');

/** MySQL数据库用户名 */
define('DB_USER', 'wpuser');

/** MySQL数据库密码 */
define('DB_PASSWORD', 'wppd');

/** MySQL主机 */
define('DB_HOST', 'localhost');

11. 上面定义的数据库需要数据库等需要自行创建,将其写脚本files/mysql_wp.sh文件:

#!/bin/bash
#
mysql -uroot -e " 
        CREATE DATABASE IF NOT EXISTS wordpress CHARACTER  SET 'utf8';
        GRANT ALL ON wordpress.* TO 'wpuser'@'localhost' IDENTIFIED BY 'wppd'; "

12. 与roles同级创建一个yml文件,将这三个项目整合起来运行:

cd /etc/ansible
vim httpd-php_roles.yml
---
- hosts: httpd
  remote_user: root
  roles:
  - { role: php, tags: [ php-tag,ap-tag ] }
  - { role: mariadb, tags: [ mariadb-tag,ap-tag ] }
  - { role: httpd, tags: [ httpd-tag,ap-tag ] }

13. 向定义的目标主机进行发布程序:

~]# ansible-playbook /etc/ansible/httpd-php_roles.yml

只要目标主机yum源可用,没有遇到报错基本OK.

未分类

访问验证一下:

未分类

git工作流(Gitflow/gitlab代码权限管理)

现状

团队之前使用SVN进行代码管理,也没有很好的利用分支管理代码版本。版本冲突问题比较严重,版本库里的代码不能作为稳定代码。
开发人员永远不知道生产上代码长啥样(环境上是编译后的jar),提测需要跟测试版本比较,上生产需要跟生产版本比较,混乱的一匹。

基于以上原因(尽管svn也有办法解决版本问题),直接在团队里推行了git版本管理,部署了gitlab做管理工具,并参考了网上各种资料以及以前公司的处理经验,制定了一套代码管理方案。

解决方案

基于gitlab进行的代码权限、流程管理

代码分支

  • master分支 生产代码版本
  • qa分支 测试代码版本
  • dev-xxx 开发代码版本(xxx表示版本号)

gitlab角色

未分类

代码开发管理流程图

未分类

Gitlab 配置外部PostgreSQL

1. 简介

上篇文章介绍了安装GitLab方式,由于默认使用的postgreSQL数据库,不能通过gui工具更直白的看到表结构,如果需要深入了解数据库资源通过Shell 方式的话这无疑是个令人恼火的方式。当然,GitLab这里我们可以配置非包装安装的数据库。下面简单的介绍下步骤。

2. 新增PostgreSQL 超级用户

如果自己有超级用户的话可以跳过

终端输入:

#是否安装postgreSQL 有的话只需要安装postgresql-contrib
psql --version

#安装psql 和 psql contrib 模块
sudo apt-get install postgresql postgresql-contrib

# 创建超级用户
sudo -u postgres createuser --superuser [USERNAME]
sudo -u postgres psql
#录入密码
passwrod [USERNAME]
q

3. 修改GitLab 配置

目录: /etc/gitlab/gitlab.rb

postgresql['enable'] = false
gitlab_rails['db_adapter'] = 'postgresql'
gitlab_rails['db_encoding'] = 'utf8'
gitlab_rails['db_host'] = '127.0.0.1'
gitlab_rails['db_port'] = '5432'
#上个步骤创建的用户
gitlab_rails['db_username'] = 'USERNAME'
gitlab_rails['db_password'] = 'PASSWORD'

4. 配置GitLab使其有效

sudo gitlab-ctl reconfigure

#检查是否使用自定义配置
sudo gedit  /opt/gitlab/embedded/service/gitlab-rails/config/database.yml

5. 使用GUI 工具查看数据资源

工具篇详见另一篇文章: http://blog.csdn.net/jason_5991/article/details/77868781

使用docker构建部署django+mysql项目(docker-compose)

这里需要升级docker版本,因为centos7 yum源默认自带的docker版本无法使用compose,详情见: http://nanguawu.me/container/5013.html

容器部署目录结构:

[root@vm2 web_django]# tree -L 2
.
├── db
│   ├── auto.cnf
│   ├── ca-key.pem
│   ├── ca.pem
│   ├── client-cert.pem
│   ├── client-key.pem
│   ├── data01
│   ├── ib_buffer_pool
│   ├── ibdata1
│   ├── ib_logfile0
│   ├── ib_logfile1
│   ├── mysql
│   ├── performance_schema
│   ├── private_key.pem
│   ├── public_key.pem
│   ├── server-cert.pem
│   ├── server-key.pem
│   └── sys
├── docker-compose.yml
├── Dockerfile
├── manage.py
├── requirements.txt
└── website
    ├── __init__.py
    ├── settings.py
    ├── urls.py
    └── wsgi.py
6 directories, 21 files

requirements.txt文件内容:

django==1.10.8
MySQL-python

Dockfile文件内容:

FROM python:2.7
MAINTAINER Larryliang "[email protected]"
ENV PYTHONUNBUFFERD 1
RUN mkdir /code
RUN mkdir /code/db
RUN mkdir /code/website
WORKDIR /code
ADD requirements.txt /code/
RUN  pip install -r requirements.txt -i https://pypi.douban.com/simple   --trusted-host pypi.douban.com
ADD . /code/

docker-compose.yml文件内容:

version: '3'
services:
  db:
    image: mysql
    expose: 
      - "3306"
    volumes:
      - ./db:/var/lib/mysql
    environment:
      - MYSQL_DATABASE=data01
      - MYSQL_ROOT_PASSWORD=data01
  web:
    build: .
    command: python manage.py runserver 0.0.0.0:8000
    volumes:
      - .:/code
    ports:
      - "8000:8000"
    depends_on:
      - db

Build项目

[root@vm2 web_django]# docker version
Client:
 Version:      17.07.0-ce
 API version:  1.31
 Go version:   go1.8.3
 Git commit:   8784753
 Built:        Tue Aug 29 17:42:01 2017
 OS/Arch:      linux/amd64
Server:
 Version:      17.07.0-ce
 API version:  1.31 (minimum version 1.12)
 Go version:   go1.8.3
 Git commit:   8784753
 Built:        Tue Aug 29 17:43:23 2017
 OS/Arch:      linux/amd64
 Experimental: false
[root@vm2 web_django]# docker-compose  build
db uses an image, skipping
Building web
Step 1/10 : FROM python:2.7
 ---> 8a90a66b719a
Step 2/10 : MAINTAINER Larryliang "[email protected]"
 ---> Using cache
 ---> 580a124b98a8
Step 3/10 : ENV PYTHONUNBUFFERD 1
 ---> Using cache
 ---> 87f8ce3ca0a2
Step 4/10 : RUN mkdir /code
 ---> Using cache
 ---> 83f26201a74f
Step 5/10 : RUN mkdir /code/db
 ---> Using cache
 ---> 06c6e22dd86a
Step 6/10 : RUN mkdir /code/website
 ---> Using cache
 ---> c0abb5fd2218
Step 7/10 : WORKDIR /code
 ---> Using cache
 ---> 942e3b5a8e20
Step 8/10 : ADD requirements.txt /code/
 ---> Using cache
 ---> 0a4a0bd7b379
Step 9/10 : RUN pip install -r requirements.txt -i https://pypi.douban.com/simple   --trusted-host pypi.douban.com
 ---> Using cache
 ---> 09600fc029fd
Step 10/10 : ADD . /code/
 ---> 11773f943a61
Successfully built 11773f943a61
Successfully tagged webdjango_web:latest

创建项目和APP

[root@vm2 web_django]# docker-compose  run web django-admin.py startproject website  .
Creating network "webdjango_default" with the default driver
Pulling db (mysql:latest)...
latest: Pulling from library/mysql
ad74af05f5a2: Already exists
0639788facc8: Pull complete
de70fa77eb2b: Pull complete
724179e94999: Pull complete
50c77fb16ba6: Pull complete
d51f459239fb: Pull complete
937bbdd4305a: Pull complete
35369f9634e1: Pull complete
f6016aab25f1: Pull complete
5f1901e920da: Pull complete
fdf808213c5b: Pull complete
Digest: sha256:96edf37370df96d2a4ee1715cc5c7820a0ec6286551a927981ed50f0273d9b43
Status: Downloaded newer image for mysql:latest
Creating webdjango_db_1 ... 
Creating webdjango_db_1 ... done

修改配置website/settings.py如下,之后重新build

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'data01',
        'USER': 'root',
        'PASSWORD': 'data01',
        'HOST': 'db',
        'PORT': '3306',
    }
}

启动容器:

[root@vm2 web_django]# docker-compose  up
Starting webdjango_db_1 ... 
Starting webdjango_db_1 ... done
Recreating webdjango_web_1 ... 
Recreating webdjango_web_1 ... done
Attaching to webdjango_db_1, webdjango_web_1
db_1   | 2017-09-07T12:22:23.513202Z 0 [Warning] TIMESTAMP with implicit DEFAULT value is deprecated. Please use --explicit_defaults_for_timestamp server option (see documentation for more details).
db_1   | 2017-09-07T12:22:23.515941Z 0 [Note] mysqld (mysqld 5.7.19) starting as process 1 ...
db_1   | 2017-09-07T12:22:23.612191Z 0 [Note] InnoDB: PUNCH HOLE support available
db_1   | 2017-09-07T12:22:23.612240Z 0 [Note] InnoDB: Mutexes and rw_locks use GCC atomic builtins
db_1   | 2017-09-07T12:22:23.612247Z 0 [Note] InnoDB: Uses event mutexes
db_1   | 2017-09-07T12:22:23.612252Z 0 [Note] InnoDB: GCC builtin __atomic_thread_fence() is used for memory barrier
db_1   | 2017-09-07T12:22:23.612256Z 0 [Note] InnoDB: Compressed tables use zlib 1.2.3
db_1   | 2017-09-07T12:22:23.612260Z 0 [Note] InnoDB: Using Linux native AIO
db_1   | 2017-09-07T12:22:23.612510Z 0 [Note] InnoDB: Number of pools: 1
db_1   | 2017-09-07T12:22:23.612640Z 0 [Note] InnoDB: Using CPU crc32 instructions
db_1   | 2017-09-07T12:22:23.699132Z 0 [Note] InnoDB: Initializing buffer pool, total size = 128M, instances = 1, chunk size = 128M
db_1   | 2017-09-07T12:22:23.706780Z 0 [Note] InnoDB: Completed initialization of buffer pool
db_1   | 2017-09-07T12:22:23.708851Z 0 [Note] InnoDB: If the mysqld execution user is authorized, page cleaner thread priority can be changed. See the man page of setpriority().
db_1   | 2017-09-07T12:22:23.721021Z 0 [Note] InnoDB: Highest supported file format is Barracuda.
db_1   | 2017-09-07T12:22:24.210121Z 0 [Note] InnoDB: Creating shared tablespace for temporary tables
db_1   | 2017-09-07T12:22:24.210959Z 0 [Note] InnoDB: Setting file './ibtmp1' size to 12 MB. Physically writing the file full; Please wait ...
db_1   | 2017-09-07T12:22:24.884250Z 0 [Note] InnoDB: File './ibtmp1' size is now 12 MB.
db_1   | 2017-09-07T12:22:24.937435Z 0 [Note] InnoDB: 96 redo rollback segment(s) found. 96 redo rollback segment(s) are active.
db_1   | 2017-09-07T12:22:24.937508Z 0 [Note] InnoDB: 32 non-redo rollback segment(s) are active.
db_1   | 2017-09-07T12:22:24.938536Z 0 [Note] InnoDB: Waiting for purge to start
db_1   | 2017-09-07T12:22:24.989070Z 0 [Note] InnoDB: 5.7.19 started; log sequence number 12143700
db_1   | 2017-09-07T12:22:24.989543Z 0 [Note] InnoDB: Loading buffer pool(s) from /var/lib/mysql/ib_buffer_pool
db_1   | 2017-09-07T12:22:24.992481Z 0 [Note] Plugin 'FEDERATED' is disabled.
db_1   | 2017-09-07T12:22:25.148161Z 0 [Note] Found ca.pem, server-cert.pem and server-key.pem in data directory. Trying to enable SSL support using them.
db_1   | 2017-09-07T12:22:25.186490Z 0 [Warning] CA certificate ca.pem is self signed.
db_1   | 2017-09-07T12:22:25.189451Z 0 [Note] Server hostname (bind-address): '*'; port: 3306
db_1   | 2017-09-07T12:22:25.189543Z 0 [Note] IPv6 is available.
db_1   | 2017-09-07T12:22:25.189564Z 0 [Note]   - '::' resolves to '::';
db_1   | 2017-09-07T12:22:25.189595Z 0 [Note] Server socket created on IP: '::'.
db_1   | 2017-09-07T12:22:25.314065Z 0 [Note] InnoDB: Buffer pool(s) load completed at 170907 12:22:25
db_1   | 2017-09-07T12:22:25.320253Z 0 [Warning] 'user' entry 'root@localhost' ignored in --skip-name-resolve mode.
db_1   | 2017-09-07T12:22:25.320383Z 0 [Warning] 'user' entry 'mysql.sys@localhost' ignored in --skip-name-resolve mode.
db_1   | 2017-09-07T12:22:25.320534Z 0 [Warning] 'db' entry 'performance_schema mysql.session@localhost' ignored in --skip-name-resolve mode.
db_1   | 2017-09-07T12:22:25.320580Z 0 [Warning] 'db' entry 'sys mysql.sys@localhost' ignored in --skip-name-resolve mode.
db_1   | 2017-09-07T12:22:25.337153Z 0 [Warning] 'proxies_priv' entry '@ root@localhost' ignored in --skip-name-resolve mode.
db_1   | 2017-09-07T12:22:25.486920Z 0 [Warning] 'tables_priv' entry 'user mysql.session@localhost' ignored in --skip-name-resolve mode.
db_1   | 2017-09-07T12:22:25.486995Z 0 [Warning] 'tables_priv' entry 'sys_config mysql.sys@localhost' ignored in --skip-name-resolve mode.
db_1   | 2017-09-07T12:22:25.741875Z 0 [Note] Event Scheduler: Loaded 0 events
db_1   | 2017-09-07T12:22:25.742708Z 0 [Note] mysqld: ready for connections.
db_1   | Version: '5.7.19'  socket: '/var/run/mysqld/mysqld.sock'  port: 3306  MySQL Community Server (GPL)
db_1   | 2017-09-07T12:22:25.742789Z 0 [Note] Executing 'SELECT * FROM INFORMATION_SCHEMA.TABLES;' to get a list of tables using the deprecated partition engine. You may use the startup option '--disable-partition-engine-check' to skip this check. 
db_1   | 2017-09-07T12:22:25.742799Z 0 [Note] Beginning of list of non-natively partitioned tables
db_1   | 2017-09-07T12:22:25.877821Z 0 [Note] End of list of non-natively partitioned tables

或者放到后台启动:

[root@vm2 web_django]# docker-compose  up -d
Starting webdjango_db_1 ... 
Starting webdjango_db_1 ... done
Starting webdjango_web_1 ... 
Starting webdjango_web_1 ... done

测试结果:

[root@vm2 web_django]# curl  http://192.168.100.120:8000/ 
<!DOCTYPE html>
<html lang="en"><head>
  <meta http-equiv="content-type" content="text/html; charset=utf-8">
  <meta name="robots" content="NONE,NOARCHIVE"><title>Welcome to Django</title>
  <style type="text/css">
    html * { padding:0; margin:0; }
    body * { padding:10px 20px; }
    body * * { padding:0; }
    body { font:small sans-serif; }
    body>div { border-bottom:1px solid #ddd; }
    h1 { font-weight:normal; }
    h2 { margin-bottom:.8em; }
    h2 span { font-size:80%; color:#666; font-weight:normal; }
    h3 { margin:1em 0 .5em 0; }
    h4 { margin:0 0 .5em 0; font-weight: normal; }
    table { border:1px solid #ccc; border-collapse: collapse; width:100%; background:white; }
    tbody td, tbody th { vertical-align:top; padding:2px 3px; }
    thead th {
      padding:1px 6px 1px 3px; background:#fefefe; text-align:left;
      font-weight:normal; font-size:11px; border:1px solid #ddd;
    }
    tbody th { width:12em; text-align:right; color:#666; padding-right:.5em; }
    #summary { background: #e0ebff; }
    #summary h2 { font-weight: normal; color: #666; }
    #explanation { background:#eee; }
    #instructions { background:#f6f6f6; }
    #summary table { border:none; background:transparent; }
  </style>
</head>
<body>
<div id="summary">
  <h1>It worked!</h1>
  <h2>Congratulations on your first Django-powered page.</h2>
</div>
<div id="instructions">
  <p>
    Of course, you haven't actually done any work yet. Next, start your first app by running <code>python manage.py startapp [app_label]</code>.
  </p>
</div>
<div id="explanation">
  <p>
    You're seeing this message because you have <code>DEBUG = True</code> in your Django settings file and you haven't configured any URLs. Get to work!
  </p>
</div>
</body></html>

观察容器状态:

[root@vm2 web_django]# docker ps  
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                    NAMES
2d549e242606        webdjango_web       "python manage.py ..."   7 minutes ago       Up 50 seconds       0.0.0.0:8000->8000/tcp   webdjango_web_1
ce18133de30c        mysql               "docker-entrypoint..."   About an hour ago   Up 51 seconds       3306/tcp                 webdjango_db_1
[root@vm2 web_django]# docker top 2d549e242606  #查看容器中进程
UID                 PID                 PPID                C                   STIME               TTY                 TIME                CMD
root                20355               20335               0                   08:28               ?                   00:00:00            python manage.py runserver 0.0.0.0:8000
root                20433               20355               1                   08:28               ?                   00:00:05            /usr/local/bin/python manage.py runserver 0.0.0.0:8000
[root@vm2 web_django]# docker exec -it  2d549e242606 df -Th  #容器中执行一条命令并返回
Filesystem          Type     Size  Used Avail Use% Mounted on
overlay             overlay   27G  3.9G   24G  15% /
tmpfs               tmpfs    489M     0  489M   0% /dev
tmpfs               tmpfs    489M     0  489M   0% /sys/fs/cgroup
/dev/mapper/cl-root xfs       27G  3.9G   24G  15% /code
shm                 tmpfs     64M     0   64M   0% /dev/shm
tmpfs               tmpfs    489M     0  489M   0% /sys/firmware
[root@vm2 web_django]# docker diff  2d549e242606  #查看容器变化
C /usr
C /usr/local
C /usr/local/lib
C /usr/local/lib/python2.7
A /usr/local/lib/python2.7/decimal.pyc
A /usr/local/lib/python2.7/UserList.pyc
A /usr/local/lib/python2.7/argparse.pyc
A /usr/local/lib/python2.7/BaseHTTPServer.pyc
C /usr/local/lib/python2.7/wsgiref
A /usr/local/lib/python2.7/wsgiref/__init__.pyc
A /usr/local/lib/python2.7/wsgiref/simple_server.pyc
A /usr/local/lib/python2.7/wsgiref/util.pyc
A /usr/local/lib/python2.7/wsgiref/handlers.pyc
A /usr/local/lib/python2.7/wsgiref/headers.pyc
A /usr/local/lib/python2.7/imghdr.pyc
C /usr/local/lib/python2.7/email
C /usr/local/lib/python2.7/email/mime
A /usr/local/lib/python2.7/email/mime/nonmultipart.pyc
A /usr/local/lib/python2.7/email/mime/message.pyc
A /usr/local/lib/python2.7/email/mime/base.pyc
A /usr/local/lib/python2.7/email/mime/multipart.pyc
A /usr/local/lib/python2.7/email/mime/audio.pyc
A /usr/local/lib/python2.7/email/mime/image.pyc
A /usr/local/lib/python2.7/email/mime/text.pyc
A /usr/local/lib/python2.7/sndhdr.pyc
[root@vm2 web_django]# docker inspect 2d549e242606 
[
    {
        "Id": "2d549e2426067461adc1d635bb5f41db464bf0b8d57fbe57ddd9b5381c67e24e",
        "Created": "2017-09-07T12:22:22.395256812Z",
        "Path": "python",
        "Args": [
            "manage.py",
            "runserver",
            "0.0.0.0:8000"
        ],
        "State": {
            "Status": "running",
            "Running": true,
            "Paused": false,
            "Restarting": false,
            "OOMKilled": false,
            "Dead": false,
            "Pid": 20355,
            "ExitCode": 0,
            "Error": "",
            "StartedAt": "2017-09-07T12:28:57.89394279Z",
            "FinishedAt": "2017-09-07T12:28:52.523766859Z"
        },
        ...
        ...
            "Networks": {
                "webdjango_default": {
                    "IPAMConfig": null,
                    "Links": null,
                    "Aliases": [
                        "web",
                        "2d549e242606"
                    ],
                    "NetworkID": "fc66e543785ee4abb611ad6b3319717fb0f6af56fd710358990e0f0309d34b59",
                    "EndpointID": "54ef82cd8b8bcea0abe60e4c9428ab6401839ac1df6972be6da9b75923372994",
                    "Gateway": "172.18.0.1",
                    "IPAddress": "172.18.0.3",
                    "IPPrefixLen": 16,
                    "IPv6Gateway": "",
                    "GlobalIPv6Address": "",
                    "GlobalIPv6PrefixLen": 0,
                    "MacAddress": "02:42:ac:12:00:03",
                    "DriverOpts": null
                }
            }
        }
    }
]

关闭容器:

[root@vm2 web_django]# docker-compose  down 
Stopping webdjango_web_1 ... done
Stopping webdjango_db_1  ... done
Removing webdjango_web_1     ... done
Removing webdjango_web_run_5 ... done
Removing webdjango_web_run_4 ... done
Removing webdjango_web_run_3 ... done
Removing webdjango_web_run_2 ... done
Removing webdjango_web_run_1 ... done
Removing webdjango_db_1      ... done
Removing network webdjango_default

至此django环境搭建测试完毕。

CentOS 7.3 上用 docker 部署 redis 介绍

Redis最新的版本已经是4.0.1了,我查了下镜像也更新了。于是在本地部署体验下,当然,这篇文章不是来介绍Redis 4.0的新功能,而是来介绍如何用docker来部署的入门级课程。

1. Docker 安装启动

$ yum -y install docker-io
$ service docker start
$ chkconfig docker on

2. 下载镜像

$ docker pull redis

3. 启动容器

这里我把容器的映射建立在/docker/redis/data,/docker/redis/conf目录下面,这两个目录自己创建,配置文件redis.conf从别的途径获取的,启动前,需要对目录加入白名单,不然启动会失败,错误为没有权限

$ chcon -Rt svirt_sandbox_file_t /docker/redis/data

启动语句如下

docker run --name redis -p 6379:6379 
-v /docker/redis/conf/redis.conf:/usr/local/etc/redis/redis.conf 
-v /docker/redis/data:/data 
-d redis redis-server /usr/local/etc/redis/redis.conf

去掉上面的-d参数,可以看见启动日志,如果启动失败,则可以看见错误的日志,也可以用命令查看日志

$ docker logs redis,redis是容器的名字

4. 关闭防火墙

firewall-cmd --zone=public --add-port=6379/tcp --permanent
systemctl restart firewalld

5. 先在本地启动redis客户端

$ docker run -it --link redis:redis --rm redis redis-cli -h redis -p 6379

或者

$ docker exec -it redis /bin/bash
> redis-cli

未分类

6. 用工具进行连接

常用的工具是redis desktop manager,可以很好的管理redis,也可以在上面执行管理的命令。

Redis 复制、Sentinel的搭建和原理说明

背景:

Redis-Sentinel是Redis官方推荐的高可用性(HA)解决方案,当用Redis做Master-slave的高可用方案时,假如master宕机了,Redis本身(包括它的很多客户端)都没有实现自动进行主备切换,而Redis-sentinel本身也是一个独立运行的进程,它能监控多个master-slave集群,发现master宕机后能进行自动切换,更多的信息见前一篇说明。它的主要功能有以下几点:

  • 不时地监控redis是否按照预期良好地运行;
  • 如果发现某个redis节点运行出现状况,能够通知另外一个进程(例如它的客户端);
  • 能够进行自动切换。当一个master节点不可用时,能够选举出master的多个slave(如果有超过一个slave的话)中的一个来作为新的master,其它的slave节点会将它所追随的master的地址改为被提升为master的slave的新地址。

Redis-Replication

1、搭建

复制的配置很简单,就一个参数:

slaveof <主数据库IP> <端口>

可以添加在配置文件里,也可以在命令行中执行。如主数据库IP是192.168.200.25 端口是6379:(配置多台从数据库的方法也一样)

slaveof 192.168.200.25 6379

注意:通过命令行进行的复制,在主从断开或则主从重启之后复制信息会丢失,即不能保证持久复制,需要再次执行slaveof。但是在配置文件里写死slaveof不会有该问题。默认情况下从库是只读的,不能进行修改,需要修改需要设置配置文件中的slave-read-only为no。在命令行里执行slaveof no one可以让一个从库变成主库。

2、原理(执行步骤)

①从数据库向主数据库发送sync命令。

②主数据库接收sync命令后,执行BGSAVE命令(保存快照),创建一个RDB文件,在创建RDB文件期间的命令将保存在缓冲区中。

③当主数据库执行完BGSAVE时,会向从数据库发送RDB文件,而从数据库会接收并载入该文件。

④主数据库将缓冲区的所有写命令发给从服务器执行。

⑤以上处理完之后,之后主数据库每执行一个写命令,都会将被执行的写命令发送给从数据库。

注意:在Redis2.8之前,主从断线或则重启之后再重连接,都需要做一次完整的sync操作(5步骤),即使断线期间只有几条的更新操作或则是没有操作,导致系统资源极度浪费。Redis2.8之后,会用一个psync来替换sync,不会进行完成的sync操作,只需要同步断线期间的记录。相关参数:repl-backlog-size、repl-backlog-ttl

大致的示意图如下:

未分类

3、相关的参数,注释掉的参数都是使用默认值。

################################# REPLICATION #################################
#复制选项,slave复制对应的master。
# slaveof <masterip> <masterport>

#如果master设置了requirepass,那么slave要连上master,需要有master的密码才行。masterauth就是用来配置master的密码,这样可以在连上master后进行认证。
# masterauth <master-password>

#当从库同主机失去连接或者复制正在进行,从机库有两种运行方式:1) 如果slave-serve-stale-data设置为yes(默认设置),从库会继续响应客户端的请求。2) 如果slave-serve-stale-data设置为no,除去INFO和SLAVOF命令之外的任何请求都会返回一个错误”SYNC with master in progress”。
slave-serve-stale-data yes

#作为从服务器,默认情况下是只读的(yes),可以修改成NO,用于写(不建议)。
slave-read-only yes

#是否使用socket方式复制数据。目前redis复制提供两种方式,disk和socket。如果新的slave连上来或者重连的slave无法部分同步,就会执行全量同步,master会生成rdb文件。有2种方式:disk方式是master创建一个新的进程把rdb文件保存到磁盘,再把磁盘上的rdb文件传递给slave。socket是master创建一个新的进程,直接把rdb文件以socket的方式发给slave。disk方式的时候,当一个rdb保存的过程中,多个slave都能共享这个rdb文件。socket的方式就的一个个slave顺序复制。在磁盘速度缓慢,网速快的情况下推荐用socket方式。
repl-diskless-sync no

#diskless复制的延迟时间,防止设置为0。一旦复制开始,节点不会再接收新slave的复制请求直到下一个rdb传输。所以最好等待一段时间,等更多的slave连上来。
repl-diskless-sync-delay 5

#slave根据指定的时间间隔向服务器发送ping请求。时间间隔可以通过 repl_ping_slave_period 来设置,默认10秒。
# repl-ping-slave-period 10

#复制连接超时时间。master和slave都有超时时间的设置。master检测到slave上次发送的时间超过repl-timeout,即认为slave离线,清除该slave信息。slave检测到上次和master交互的时间超过repl-timeout,则认为master离线。需要注意的是repl-timeout需要设置一个比repl-ping-slave-period更大的值,不然会经常检测到超时。
# repl-timeout 60

#是否禁止复制tcp链接的tcp nodelay参数,可传递yes或者no。默认是no,即使用tcp nodelay。如果master设置了yes来禁止tcp nodelay设置,在把数据复制给slave的时候,会减少包的数量和更小的网络带宽。但是这也可能带来数据的延迟。默认我们推荐更小的延迟,但是在数据量传输很大的场景下,建议选择yes。
repl-disable-tcp-nodelay no

#复制缓冲区大小,这是一个环形复制缓冲区,用来保存最新复制的命令。这样在slave离线的时候,不需要完全复制master的数据,如果可以执行部分同步,只需要把缓冲区的部分数据复制给slave,就能恢复正常复制状态。缓冲区的大小越大,slave离线的时间可以更长,复制缓冲区只有在有slave连接的时候才分配内存。没有slave的一段时间,内存会被释放出来,默认1m。
# repl-backlog-size 5mb

#master没有slave一段时间会释放复制缓冲区的内存,repl-backlog-ttl用来设置该时间长度。单位为秒。
# repl-backlog-ttl 3600

#当master不可用,Sentinel会根据slave的优先级选举一个master。最低的优先级的slave,当选master。而配置成0,永远不会被选举。
slave-priority 100

#redis提供了可以让master停止写入的方式,如果配置了min-slaves-to-write,健康的slave的个数小于N,mater就禁止写入。master最少得有多少个健康的slave存活才能执行写命令。这个配置虽然不能保证N个slave都一定能接收到master的写操作,但是能避免没有足够健康的slave的时候,master不能写入来避免数据丢失。设置为0是关闭该功能。
# min-slaves-to-write 3

#延迟小于min-slaves-max-lag秒的slave才认为是健康的slave。
# min-slaves-max-lag 10

4、总结

Redis目前的复制是异步的,只保证最终一致性,而不是强一致性(主从数据库的更新还是分先后,先主后从)。要是一致性要求高的应用,目前还是读写都在主库上去。

Redis-Sentinel

需要对redis和sentinel的配置文件有rewrite的权限。

1、搭建

(环境:redis服务3个实例10086、10087、10088;sentinel服务3个监控:20086、20087、20088)

sentinel是一个”监视器”,根据被监视实例的身份和状态来判断该执行何种操作。通过给定的配置文件来发现主服务器的,再通过向主服务器发送的info信息来发现该主服务器的从服务器。Sentinel 实际上就是一个运行在 Sentienl 模式下的 Redis 服务器,所以我们同样可以使用以下命令来启动一个 Sentinel实例。运行方式如下:

redis-sentinel /path/to/sentinel.conf

参数配置文件:

port 20086      #默认端口26379

dir "/tmp"

logfile "/var/log/redis/sentinel_20086.log"

daemonize yes

#格式:sentinel <option_name> <master_name> <option_value>;#该行的意思是:监控的master的名字叫做T1(自定义),地址为127.0.0.1:10086,行尾最后的一个2代表在sentinel集群中,多少个sentinel认为masters死了,才能真正认为该master不可用了。
sentinel monitor T1 127.0.0.1 10086 2  

#sentinel会向master发送心跳PING来确认master是否存活,如果master在“一定时间范围”内不回应PONG 或者是回复了一个错误消息,那么这个sentinel会主观地(单方面地)认为这个master已经不可用了(subjectively down, 也简称为SDOWN)。而这个down-after-milliseconds就是用来指定这个“一定时间范围”的,单位是毫秒,默认30秒。
sentinel down-after-milliseconds T1 15000

#failover过期时间,当failover开始后,在此时间内仍然没有触发任何failover操作,当前sentinel将会认为此次failoer失败。默认180秒,即3分钟。
sentinel failover-timeout T1 120000

#在发生failover主备切换时,这个选项指定了最多可以有多少个slave同时对新的master进行同步,这个数字越小,完成failover所需的时间就越长,但是如果这个数字越大,就意味着越多的slave因为replication而不可用。可以通过将这个值设为 1 来保证每次只有一个slave处于不能处理命令请求的状态。
sentinel parallel-syncs T1 1

#sentinel 连接设置了密码的主和从
#sentinel auth-pass <master_name> xxxxx

#发生切换之后执行的一个自定义脚本:如发邮件、vip切换等
##sentinel notification-script <master-name> <script-path>     ##不会执行,疑问?
#sentinel client-reconfig-script <master-name> <script-path>  ##这个会执行

注意:要是参数配置的是默认值,在sentinel运行时该参数会在配置文件文件里被删除掉,直接不显示。也可以在运行时用命令SENTINEL SET command动态修改,后面说明。

很显然,只使用单个sentinel进程来监控redis集群是不可靠的,当sentinel进程宕掉后(sentinel本身也有单点问题,single-point-of-failure)整个集群系统将无法按照预期的方式运行。所以有必要将sentinel集群,这样有几个好处:

  • 即使有一些sentinel进程宕掉了,依然可以进行redis集群的主备切换;
  • 如果只有一个sentinel进程,如果这个进程运行出错,或者是网络堵塞,那么将无法实现redis集群的主备切换(单点问题);
  • 如果有多个sentinel,redis的客户端可以随意地连接任意一个sentinel来获得关于redis集群中的信息。

本文开启sentinel集群用了3个实例,保证各个端口和目录不一致,配置文件如下:

sentinel_20086.conf :

port 20086

dir "/var/lib/sentinel_20086"

logfile "/var/log/redis/sentinel_20086.log"

daemonize yes

sentinel monitor T1 127.0.0.1 10086 2

sentinel down-after-milliseconds T1 15000

sentinel failover-timeout T1 120000

sentinel parallel-syncs T1 1

#发生切换之后执行的一个自定义脚本:如发邮件、vip切换等
#sentinel notification-script <master-name> <script-path>

sentinel_20087.conf :

port 20087

dir "/var/lib/sentinel_20087"

logfile "/var/log/redis/sentinel_20087.log"

daemonize yes

sentinel monitor T1 127.0.0.1 10086 2

sentinel down-after-milliseconds T1 15000

sentinel failover-timeout T1 120000

sentinel parallel-syncs T1 1

#发生切换之后执行的一个自定义脚本:如发邮件、vip切换等
#sentinel notification-script <master-name> <script-path>

sentinel_20088.conf :

port 20088

dir "/var/lib/sentinel_20086"

logfile "/var/log/redis/sentinel_20088.log"

daemonize yes

sentinel monitor T1 127.0.0.1 10086 2

sentinel down-after-milliseconds T1 15000

sentinel failover-timeout T1 120000

sentinel parallel-syncs T1 1

#发生切换之后执行的一个自定义脚本:如发邮件、vip切换等
#sentinel notification-script <master-name> <script-path>

疑问:这里的参数 sentinel notification-script 好像切换的时候不会执行,参数sentinel client-reconfig-script 倒是会执行,可以用这个参数来替换上面的参数。

启动sentinel:

root@zhoujinyi:/etc/redis# redis-sentinel /etc/redis/sentinel_20086.conf 
root@zhoujinyi:/etc/redis# redis-sentinel /etc/redis/sentinel_20087.conf 
root@zhoujinyi:/etc/redis# redis-sentinel /etc/redis/sentinel_20088.conf 

注意:当一个master配置为需要密码才能连接时,客户端和slave在连接时都需要提供密码。master通过requirepass设置自身的密码,不提供密码无法连接到这个master。slave通过masterauth来设置访问master时的密码。客户端需要auth提供密码,但是当使用了sentinel时,由于一个master可能会变成一个slave,一个slave也可能会变成master,所以需要同时设置上述两个配置项,并且sentinel需要连接master和slave,需要设置参数:sentinel auth-pass xxxxx。

启动后各个sentinel的日志信息如下:

3462:X 08 Jun 18:07:54.820 # Sentinel runid is b44bb512b3b756c97f48aff1dc37b54a30659ee9
3462:X 08 Jun 18:07:54.820 # +monitor master T1 127.0.0.1 10086 quorum 2  #主加入监控
3462:X 08 Jun 18:07:54.823 * +slave slave 127.0.0.1:10087 127.0.0.1 10087 @ T1 127.0.0.1 10086 #检测到一个slave并添加进slave列表
3462:X 08 Jun 18:07:54.823 * +slave slave 127.0.0.1:10088 127.0.0.1 10088 @ T1 127.0.0.1 10086 #检测到一个slave并添加进slave列表
3462:X 08 Jun 18:07:59.515 * +sentinel sentinel 127.0.0.1:20087 127.0.0.1 20087 @ T1 127.0.0.1 10086 #增加了一个sentinel
3462:X 08 Jun 18:08:01.820 * +sentinel sentinel 127.0.0.1:20088 127.0.0.1 20088 @ T1 127.0.0.1 10086 #增加了一个sentinel

关于更多的信息见:

+reset-master <instance details> -- 当master被重置时.
    +slave <instance details> -- 当检测到一个slave并添加进slave列表时.
    +failover-state-reconf-slaves <instance details> -- Failover状态变为reconf-slaves状态时
    +failover-detected <instance details> -- 当failover发生时
    +slave-reconf-sent <instance details> -- sentinel发送SLAVEOF命令把它重新配置时
    +slave-reconf-inprog <instance details> -- slave被重新配置为另外一个master的slave,但数据复制还未发生时。
    +slave-reconf-done <instance details> -- slave被重新配置为另外一个master的slave并且数据复制已经与master同步时。
    -dup-sentinel <instance details> -- 删除指定master上的冗余sentinel时 (当一个sentinel重新启动时,可能会发生这个事件).
    +sentinel <instance details> -- 当master增加了一个sentinel时。
    +sdown <instance details> -- 进入SDOWN状态时;
    -sdown <instance details> -- 离开SDOWN状态时。
    +odown <instance details> -- 进入ODOWN状态时。
    -odown <instance details> -- 离开ODOWN状态时。
    +new-epoch <instance details> -- 当前配置版本被更新时。
    +try-failover <instance details> -- 达到failover条件,正等待其他sentinel的选举。
    +elected-leader <instance details> -- 被选举为去执行failover的时候。
    +failover-state-select-slave <instance details> -- 开始要选择一个slave当选新master时。
    no-good-slave <instance details> -- 没有合适的slave来担当新master
    selected-slave <instance details> -- 找到了一个适合的slave来担当新master
    failover-state-send-slaveof-noone <instance details> -- 当把选择为新master的slave的身份进行切换的时候。
    failover-end-for-timeout <instance details> -- failover由于超时而失败时。
    failover-end <instance details> -- failover成功完成时。
    switch-master <master name> <oldip> <oldport> <newip> <newport> -- 当master的地址发生变化时。通常这是客户端最感兴趣的消息了。
    +tilt -- 进入Tilt模式。
    -tilt -- 退出Tilt模式。

2、原理

①sentinel集群通过给定的配置文件发现master,启动时会监控master。通过向master发送info信息获得该服务器下面的所有从服务器。

②sentinel集群通过命令连接向被监视的主从服务器发送hello信息(每秒一次),该信息包括sentinel本身的ip、端口、id等内容,以此来向其他sentinel宣告自己的存在。

③sentinel集群通过订阅连接接收其他sentinel发送的hello信息,以此来发现监视同一个主服务器的其他sentinel;集群之间会互相创建命令连接用于通信,因为已经有主从服务器作为发送和接收hello信息的中介,sentinel之间不会创建订阅连接。

④sentinel集群使用ping命令来检测实例的状态,如果在指定的时间内(down-after-milliseconds)没有回复或则返回错误的回复,那么该实例被判为下线。

⑤当failover主备切换被触发后,failover并不会马上进行,还需要sentinel中的大多数sentinel授权后才可以进行failover,即进行failover的sentinel会去获得指定quorum个的sentinel的授权,成功后进入ODOWN状态。如在5个sentinel中配置了2个quorum,等到2个sentinel认为master死了就执行failover。

⑥sentinel向选为master的slave发送SLAVEOF NO ONE命令,选择slave的条件是sentinel首先会根据slaves的优先级来进行排序,优先级越小排名越靠前。如果优先级相同,则查看复制的下标,哪个从master接收的复制数据多,哪个就靠前。如果优先级和下标都相同,就选择进程ID较小的。

⑦sentinel被授权后,它将会获得宕掉的master的一份最新配置版本号(config-epoch),当failover执行结束以后,这个版本号将会被用于最新的配置,通过广播形式通知其它sentinel,其它的sentinel则更新对应master的配置。

①到③是自动发现机制:

  • 以10秒一次的频率,向被监视的master发送info命令,根据回复获取master当前信息。
  • 以1秒一次的频率,向所有redis服务器、包含sentinel在内发送PING命令,通过回复判断服务器是否在线。
  • 以2秒一次的频率,通过向所有被监视的master,slave服务器发送当前sentinel,master信息的消息。

④是检测机制,⑤和⑥是failover机制,⑦是更新配置机制。

注意:因为redis采用的是异步复制,没有办法避免数据的丢失。但可以通过以下配置来使得数据不会丢失:min-slaves-to-write 1 、 min-slaves-max-lag 10。一个redis无论是master还是slave,都必须在配置中指定一个slave优先级。要注意到master也是有可能通过failover变成slave的。如果一个redis的slave优先级配置为0,那么它将永远不会被选为master,但是它依然会从master哪里复制数据。

3、运行测试

上面已经搭好了一个简单的测试环境:redis服务3个实例10086(M)、10087(S)、10088(S);sentinel服务3个监控:20086、20087、20088
现在进行一个故障转移的操作:0点30分14秒kill掉10086,Sentinel日志信息:

3466:X 09 Jun 00:30:29.067 # +sdown master T1 127.0.0.1 10086                      ##进入主观不可用(SDOWN)
3466:X 09 Jun 00:30:29.169 # +odown master T1 127.0.0.1 10086 #quorum 2/2          ##投票好了,达到了quorum,进入客观不可用(ODOWN)
3466:X 09 Jun 00:30:29.169 # +new-epoch 1                                          ##当前配置版本被更新
3466:X 09 Jun 00:30:29.169 # +try-failover master T1 127.0.0.1 10086               ##达到failover条件,正等待其他sentinel的选举
3466:X 09 Jun 00:30:29.179 # +vote-for-leader e106f1eaffdaa10babef3f5858a7cb8d05ffe9ea 1 ##选举
3466:X 09 Jun 00:30:29.183 # 127.0.0.1:20088 voted for e106f1eaffdaa10babef3f5858a7cb8d05ffe9ea 1 ##选举
3466:X 09 Jun 00:30:29.184 # 127.0.0.1:20086 voted for e106f1eaffdaa10babef3f5858a7cb8d05ffe9ea 1 ##选举
3466:X 09 Jun 00:30:29.241 # +elected-leader master T1 127.0.0.1 10086             ##执行failover
3466:X 09 Jun 00:30:29.242 # +failover-state-select-slave master T1 127.0.0.1 10086 ##开始要选择一个slave当选新master
3466:X 09 Jun 00:30:29.344 # +selected-slave slave 127.0.0.1:10088 127.0.0.1 10088 @ T1 127.0.0.1 10086 ##找到了一个适合的slave来担当新master
3466:X 09 Jun 00:30:29.344 * +failover-state-send-slaveof-noone slave 127.0.0.1:10088 127.0.0.1 10088 @ T1 127.0.0.1 10086 ##当把选择为新master的slave的身份进行切换
3466:X 09 Jun 00:30:29.447 * +failover-state-wait-promotion slave 127.0.0.1:10088 127.0.0.1 10088 @ T1 127.0.0.1 10086
3466:X 09 Jun 00:30:30.206 # +promoted-slave slave 127.0.0.1:10088 127.0.0.1 10088 @ T1 127.0.0.1 10086
3466:X 09 Jun 00:30:30.207 # +failover-state-reconf-slaves master T1 127.0.0.1 10086  ##Failover状态变为reconf-slaves
3466:X 09 Jun 00:30:30.273 * +slave-reconf-sent slave 127.0.0.1:10087 127.0.0.1 10087 @ T1 127.0.0.1 10086 ##sentinel发送SLAVEOF命令把它重新配置,重新配置到新主
3466:X 09 Jun 00:30:31.250 * +slave-reconf-inprog slave 127.0.0.1:10087 127.0.0.1 10087 @ T1 127.0.0.1 10086 ##slave被重新配置为另外一个master的slave,但数据复制还未发生
3466:X 09 Jun 00:30:31.251 * +slave-reconf-done slave 127.0.0.1:10087 127.0.0.1 10087 @ T1 127.0.0.1 10086 ##slave被重新配置为另外一个master的slave并且数据复制已经与master同步
3466:X 09 Jun 00:30:31.340 # -odown master T1 127.0.0.1 10086  ##离开客观不可用(ODOWN)
3466:X 09 Jun 00:30:31.340 # +failover-end master T1 127.0.0.1 10086  ##failover成功完成
3466:X 09 Jun 00:30:31.341 # +switch-master T1 127.0.0.1 10086 127.0.0.1 10088 ##master的地址发生变化
3466:X 09 Jun 00:30:31.341 * +slave slave 127.0.0.1:10087 127.0.0.1 10087 @ T1 127.0.0.1 10088 ##检测到一个slave并添加进slave列表
3466:X 09 Jun 00:30:31.351 * +slave slave 127.0.0.1:10086 127.0.0.1 10086 @ T1 127.0.0.1 10088
3466:X 09 Jun 00:30:46.362 # +sdown slave 127.0.0.1:10086 127.0.0.1 10086 @ T1 127.0.0.1 10088 ##原主进入主观不可用状态

通过日志信息看到,15秒(down-after-milliseconds)之后进行了failvoer操作,最后操作成功,10088变成了新主,可以通过info sentinel和sentinel maters查看主的信息。把原主开起来,日志信息:

3466:X 09 Jun 01:00:35.306 # -sdown slave 127.0.0.1:10086 127.0.0.1 10086 @ T1 127.0.0.1 10088  ##离开主观不可用状态
3466:X 09 Jun 01:00:45.249 * +convert-to-slave slave 127.0.0.1:10086 127.0.0.1 10086 @ T1 127.0.0.1 10088 ## 检测到一个slave并添加进slave列表

通过日志看到,原主起来之后变成了从。这里可以发现在redis配置文件(可写权限)的最后被添加了:

# Generated by CONFIG REWRITE
slaveof 127.0.0.1 10088

在新主上操作,可以同步复制到从库:

root@zhoujinyi:~# redis-cli -p 10088
127.0.0.1:10088> set dxy dxy
OK
127.0.0.1:10088> get dxy
"dxy"
127.0.0.1:10088> 
root@zhoujinyi:~# redis-cli -p 10086
127.0.0.1:10086> get dxy
"dxy"
127.0.0.1:10086> 
root@zhoujinyi:~# redis-cli -p 10087
127.0.0.1:10087> get dxy
"dxy"

上面测试说明sentinel自动failover成功。要是kill掉一个sentinel实例会怎么样?可以看日志:

3466:X 09 Jun 01:14:51.039 # +sdown sentinel 127.0.0.1:20088 127.0.0.1 20088 @ T1 127.0.0.1 10087  ##进入主观不可用
3466:X 09 Jun 01:15:32.610 # -sdown sentinel 127.0.0.1:20088 127.0.0.1 20088 @ T1 127.0.0.1 10087  ##进入客观不可用
3466:X 09 Jun 01:15:34.497 * -dup-sentinel master T1 127.0.0.1 10087 #duplicate of 127.0.0.1:20088 or a79f189986ab9d3940de48099e18a99abef4d595  ##删除指定master上的冗余sentinel时 (当一个sentinel重新启动时,可能会发生这个事件)
3466:X 09 Jun 01:15:34.498 * +sentinel sentinel 127.0.0.1:20088 127.0.0.1 20088 @ T1 127.0.0.1 10087  ##检测到一个sentinel,并进入列表

说明sentinel实例也被其他sentinel监视(上面介绍了各个sentinel相互通信),防止sentinel单点故障。通过日志看到这么多信息,这里需要注意下下面的概念:

① Leader选举:

其实在sentinels故障转移中,仍然需要一个“Leader”来调度整个过程:master的选举以及slave的重配置和同步。当集群中有多个sentinel实例时,如何选举其中一个sentinel为leader呢?

在配置文件中“can-failover”“quorum”参数,以及“is-master-down-by-addr”指令配合来完成整个过程。

A) “can-failover”用来表明当前sentinel是否可以参与“failover”过程,如果为“YES”则表明它将有能力参与“Leader”的选举,否则它将作为“Observer”,observer参与leader选举投票但不能被选举;

B) “quorum”不仅用来控制master ODOWN状态确认,同时还用来选举leader时最小“赞同票”数;

C) “is-master-down-by-addr”,在上文中以及提到,它可以用来检测“ip + port”的master是否已经处于SDOWN状态,不过此指令不仅能够获得master是否处于SDOWN,同时它还额外的返回当前sentinel本地“投票选举”的Leader信息(runid);

每个sentinel实例都持有其他的sentinels信息,在Leader选举过程中(当为leader的sentinel实例失效时,有可能master server并没失效,注意分开理解),sentinel实例将从所有的sentinels集合中去除“can-failover = no”和状态为SDOWN的sentinels,在剩余的sentinels列表中按照runid按照“字典”顺序排序后,取出runid最小的sentinel实例,并将它“投票选举”为Leader,并在其他sentinel发送的“is-master-down-by-addr”指令时将推选的runid追加到响应中。每个sentinel实例都会检测“is-master-down-by-addr”的响应结果,如果“投票选举”的leader为自己,且状态正常的sentinels实例中,“赞同者”的自己的sentinel个数不小于(>=) 50% + 1,且不小与,那么此sentinel就会认为选举成功且leader为自己。

在sentinel.conf文件中,我们期望有足够多的sentinel实例配置“can-failover yes”,这样能够确保当leader失效时,能够选举某个sentinel为leader,以便进行failover。如果leader无法产生,比如较少的sentinels实例有效,那么failover过程将无法继续。

② failover过程:

在Leader触发failover之前,首先wait数秒(随即0~5),以便让其他sentinel实例准备和调整(有可能多个leader??),如果一切正常,那么leader就需要开始将一个salve提升为master,此slave必须为状态良好(不能处于SDOWN/ODOWN状态)且权重值最低(redis.conf中)的,当master身份被确认后,开始failover

A)“+failover-triggered”: Leader开始进行failover,此后紧跟着“+failover-state-wait-start”,wait数秒。

B)“+failover-state-select-slave”: Leader开始查找合适的slave

C)“+selected-slave”: 已经找到合适的slave

D) “+failover-state-sen-slaveof-noone”: Leader向slave发送“slaveof no one”指令,此时slave已经完成角色转换,此slave即为master

E) “+failover-state-wait-promotition”: 等待其他sentinel确认slave

F)“+promoted-slave”:确认成功

G)“+failover-state-reconf-slaves”: 开始对slaves进行reconfig操作。

H)“+slave-reconf-sent”:向指定的slave发送“slaveof”指令,告知此slave跟随新的master

I)“+slave-reconf-inprog”: 此slave正在执行slaveof + SYNC过程,如过slave收到“+slave-reconf-sent”之后将会执行slaveof操作。

J)“+slave-reconf-done”: 此slave同步完成,此后leader可以继续下一个slave的reconfig操作。循环G)

K)“+failover-end”: 故障转移结束

L)“+switch-master”:故障转移成功后,各个sentinel实例开始监控新的master。

4、命令查看、修改

查看:

①:info命令

127.0.0.1:20086> info
# Server
redis_version:3.0.0   #版本号
redis_git_sha1:00000000
redis_git_dirty:0
redis_build_id:e7768317ba5bdca5
redis_mode:sentinel  #开启模式
os:Linux 3.16.0-71-generic x86_64  #系统位数
arch_bits:64
multiplexing_api:epoll
gcc_version:4.8.2
process_id:2767        #线程ID
run_id:319d8c58b9bf26c26ca040b53bdc0764a543648b
tcp_port:20086         #端口
uptime_in_seconds:923  #允许时间
uptime_in_days:0
hz:11
lru_clock:6041117
config_file:/etc/redis/sentinel_20086.conf   #配置文件

# Sentinel
sentinel_masters:1    
sentinel_tilt:0
sentinel_running_scripts:0
sentinel_scripts_queue_length:0
master0:name=T1,status=ok,address=127.0.0.1:10087,slaves=2,sentinels=3  #主name,主ip,多少个slave,多少个sentinel

也可以单个显示:info server、info sentinel。

②:sentinel masters,显示被监控的所有master以及它们的状态。要是有多个master就显示多个(复用,监控多个redis,即一个配置文件写多个),例子就1个master

127.0.0.1:20086> SENTINEL masters
1)  1) "name"   #master name
    2) "T1"
    3) "ip"     #master ip
    4) "127.0.0.1"
    5) "port"   #master port
    6) "10087"
    7) "runid"
    8) "508e7de9f5aa4fdb70126d62a54392fbefc0b11b"
    9) "flags"
   10) "master"
   11) "pending-commands"
   12) "0"
   13) "last-ping-sent"
   14) "0"
   15) "last-ok-ping-reply"
   16) "261"
   17) "last-ping-reply"
   18) "261"
   19) "down-after-milliseconds"  #ping的响应时间
   20) "15000"
   21) "info-refresh"
   22) "620"
   23) "role-reported"
   24) "master"
   25) "role-reported-time"
   26) "1205058"
   27) "config-epoch"             #配置文件版本号
   28) "2"
   29) "num-slaves"               #从的数量
   30) "2"
   31) "num-other-sentinels"      #除本身外还有多少个sentinel
   32) "2"
   33) "quorum"                   #投票数量
   34) "2"
   35) "failover-timeout"         #failover超时时间
   36) "120000"
   37) "parallel-syncs"           #多少个从同步
   38) "1"

③:sentinel master ,显示指定master的信息和状态。

127.0.0.1:20086> sentinel master T1
 1) "name"
 2) "T1"
 3) "ip"
 4) "127.0.0.1"
 5) "port"
 6) "10087"
 7) "runid"
 8) "508e7de9f5aa4fdb70126d62a54392fbefc0b11b"
 9) "flags"
10) "master"
11) "pending-commands"
12) "0"
13) "last-ping-sent"
14) "0"
15) "last-ok-ping-reply"
16) "909"
17) "last-ping-reply"
18) "909"
19) "down-after-milliseconds"
20) "15000"
21) "info-refresh"
22) "5820"
23) "role-reported"
24) "master"
25) "role-reported-time"
26) "1501345"
27) "config-epoch"
28) "2"
29) "num-slaves"
30) "2"
31) "num-other-sentinels"
32) "2"
33) "quorum"
34) "2"
35) "failover-timeout"
36) "120000"
37) "parallel-syncs"
38) "1"

④:sentinel slaves ,显示指定master的所有slave以及它们的状态。

127.0.0.1:20086> sentinel slaves T1
1)  1) "name"
    2) "127.0.0.1:10088"
    3) "ip"
    4) "127.0.0.1"
    5) "port"
    6) "10088"
    7) "runid"
    8) "380a4d9e32aefd3a00c7a64ba8bce451643044f1"
    9) "flags"
   10) "slave"
   11) "pending-commands"
   12) "0"
   13) "last-ping-sent"
   14) "0"
   15) "last-ok-ping-reply"
   16) "15"
   17) "last-ping-reply"
   18) "15"
   19) "down-after-milliseconds"
   20) "15000"
   21) "info-refresh"
   22) "7558"
   23) "role-reported"
   24) "slave"
   25) "role-reported-time"
   26) "1934978"
   27) "master-link-down-time"
   28) "0"
   29) "master-link-status"
   30) "ok"
   31) "master-host"
   32) "127.0.0.1"
   33) "master-port"
   34) "10087"
   35) "slave-priority"
   36) "100"
   37) "slave-repl-offset"
   38) "361068"
2)  1) "name"
    2) "127.0.0.1:10086"
    3) "ip"
    4) "127.0.0.1"
    5) "port"
    6) "10086"
    7) "runid"
    8) "9babf78ee2b420d2671b12f93b68c4d19a5edf08"
    9) "flags"
   10) "slave"
   11) "pending-commands"
   12) "0"
   13) "last-ping-sent"
   14) "0"
   15) "last-ok-ping-reply"
   16) "15"
   17) "last-ping-reply"
   18) "15"
   19) "down-after-milliseconds"
   20) "15000"
   21) "info-refresh"
   22) "7558"
   23) "role-reported"
   24) "slave"
   25) "role-reported-time"
   26) "1934978"
   27) "master-link-down-time"
   28) "0"
   29) "master-link-status"
   30) "ok"
   31) "master-host"
   32) "127.0.0.1"
   33) "master-port"
   34) "10087"
   35) "slave-priority"
   36) "100"
   37) "slave-repl-offset"
   38) "361068"

⑤:sentinel get-master-addr-by-name ,返回指定master的ip和端口,如果正在进行failover或者failover已经完成,将会显示被提升为master的slave的ip和端口。

27.0.0.1:20086> sentinel get-master-addr-by-name T1
1) "127.0.0.1"
2) "10087"

⑥:sentinel reset :重置名字匹配该正则表达式的所有的master的状态信息,清除其之前的状态信息,以及slaves信息。比如删除一个slave或则sentinel时候,先关闭停止想要删除的进程,再执行:

sentinel reset *

⑦:sentinel failover 强制sentinel执行failover,并且不需要得到其他sentinel的同意。但是failover后会将最新的配置发送给其他sentinel。

127.0.0.1:20086> sentinel failover T1
OK
127.0.0.1:20086> sentinel get-master-addr-by-name T1
1) "127.0.0.1"
2) "10088"         #主被切换了 

⑧:查看其他sentinel信息

sentinel sentinels T1

⑨:检查sentinel监控是否正确

sentinel ckquorum T1

⑩:配置文件丢失,重写配置文件

sentinel flushconfig

修改:包括参数

①:sentinel monitor ,监控一个新的redis master(这时通过sentinel masters可以看到多个)

127.0.0.1:20086> SENTINEL MONITOR T2 127.0.0.1 10089 2
OK

②:sentinel remove 命令sentinel放弃对某个master的监听。删掉上一个加的:

127.0.0.1:20086> sentinel remove T2
OK

③:sentinel set 这个命令很像Redis的CONFIG SET命令,用来改变指定master的配置。支持多个。

127.0.0.1:20086> sentinel masters
1)     ...
   37) "parallel-syncs"
   38) "1"
127.0.0.1:20086> sentinel set T1 parallel-syncs 2  #格式
OK
127.0.0.1:20086> sentinel masters
1)  ...
   37) "parallel-syncs"
   38) "2"

注意:只要是配置文件中存在的配置项,都可以用SENTINEL SET命令来设置。这个还可以用来设置master的属性,比如说quorum(票数),而不需要先删除master,再重新添加master。

5、增加或删除Sentinel

增加一个sentinel很简单,直接配置好参数开启一个sentinel即可。添加时最好一个接着一个添加,这样可以预防网络隔离带来的问题,可以每个30秒添加一个sentinel。通过SENTINEL MASTER mastername(T1)中的num-other-sentinels来查看是否成功添加sentinel。删除一个sentinel稍微复杂一点,sentinel永远不会删除一个已经存在过的sentinel,即使它已经与组织失去联系。遵循如下步骤:

  • 停止所要删除的sentinel

  • 发送一个SENTINEL RESET * 命令给所有其它的sentinel实例,如果你想要重置指定master上面的sentinel,只需要把*号改为特定的名字,注意,需要一个接一个发,每次发送的间隔不低于30秒。

  • 检查一下所有的sentinels是否都有一致的当前sentinel数。使用SENTINEL MASTER mastername 来查询。

首先 kill 掉一个sentinel

127.0.0.1:20086> sentinel master T1
 1) "name"
 2) "T1"
 3) "ip"
 4) "127.0.0.1"
 5) "port"
 6) "10088"
 ...
31) "num-other-sentinels"
32) "2"
...
127.0.0.1:20086> sentinel reset T1  #重新导入或则执行下面的
(integer) 1
127.0.0.1:20086> sentinel reset *   #因为只有监视一个主,所以和上面一致
(integer) 1
127.0.0.1:20086> sentinel masters
1)  1) "name"
    2) "T1"
    3) "ip"
    4) "127.0.0.1"
    5) "port"
    6) "10088"
...
...
   31) "num-other-sentinels"        #sentinel slave的数量
   32) "1"
...

6、删除旧master或者不可达slave

要永久地删除掉一个slave(有可能它曾经是个master),你只需要发送一个SENTINEL RESET master命令给所有的sentinels,它们将会更新列表里能够正确地复制master数据的slave。 遵循如下步骤:

  • 停止所要删除的redis slave。

  • 发送一个SENTINEL RESET * 命令给所有其它的sentinel实例,如果你想要重置指定master上面的slave,只需要把*号改为特定的名字。

  • 检查一下所有的sentinels是否都有一致的当前sentinel数。使用SENTINEL MASTER mastername 来查询。

首先 kill 掉一个slave

127.0.0.1:20086> sentinel masters
1)  1) "name"
    2) "T1"
    3) "ip"
    4) "127.0.0.1"
    5) "port"
    6) "10088"
...
   29) "num-slaves"                   #多少个slave
   30) "2"
...
127.0.0.1:20086> sentinel reset T1    #重新导入或则执行下面的
(integer) 1
127.0.0.1:20086> sentinel reset *     #和上面一致
(integer) 1
127.0.0.1:20086> sentinel masters
1)  1) "name"
    2) "T1"
    3) "ip"
    4) "127.0.0.1"
    5) "port"
    6) "10088"
...
   29) "num-slaves"                   #多少个slave
   30) "1"
...

注意:要是再次开启关闭掉的redis slave会继续当成一个slave,若要彻底关闭slave,则需要修改关闭掉的redis配置文件中最后的:

# Generated by CONFIG REWRITE
slaveof 127.0.0.1 10088        #关闭改参数

7、总结

Redis-Sentinel是Redis官方推荐的高可用性(HA) 解决方案,Redis-sentinel本身也是一个独立运行的进程,它能监控多个master-slave集群,发现master宕机后能进行自动切换。Sentinel可以监视任意多个主服务器(复用),以及主服务器属下的从服务器,并在被监视的主服务器下线时,自动执行故障转移操作。

未分类

为了防止sentinel的单点故障,可以对sentinel进行集群化,创建多个sentinel。

未分类

分享一个删除redis中指定key模式的数据的shell脚本

有很多场景,我们都需要删除redis中某些具有相似特征的key,即使是线上环境也是。如果key数量很小容易处理,如果这些key很多很多,必须通过scan命令循环扫描一一删除,如果直接执行keys命令会堵死redis服务。下面这个脚本就是通过循环扫码key再删除,直至结束。

redis-del-keys.sh

#!/bin/bash
##redis主机IP
host=$1
##redis端口
port=$2
##key模式
pattern=$3
##游标
cursor=0
##退出信号
signal=0

##循环获取key并删除
while [ $signal -ne 1 ]
    do
        echo "cursor:${cursor}"
        sleep 2
        ##将redis scan得到的结果赋值到变量
        re=$(redis-cli -h $host -p $p -c  scan $cursor count 1000 match $pattern)
        ##以换行作为分隔符
        IFS=$'n' 
        #echo $re
        echo 'arr=>'
        ##转成数组
        arr=($re)
        ##打印数组长度
        echo 'len:'${#arr[@]}
        ##第一个元素是游标值
        cursor=${arr[0]}
        ##游标为0表示没有key了
        if [ $cursor -eq 0 ];then
            signal=1
        fi
        ##循环数组
    for key in ${arr[@]}
        do
            echo $key
            if [ $key != $cursor ];then
                echo "key:"$key
                ##删除key
                redis-cli -h $host -p $port -c del $key >/dev/null  2>&1
            fi
    done
done
echo 'done'

使用方式:

./redis-del-keys.sh localhost 6379 user:*

表示删除本机6379端口的redis中user:开头的所以key。

redis动态扩展内存

需求:将redis内存从1G扩展到3G,不中断服务

1、打开客户端

# redis-cli -p 6391

2、查看当前值

redis 127.0.0.1:6391> config get maxmemory
1) "maxmemory"
2) "1073741824"

3、设置内存为3G

redis 127.0.0.1:6391> config set maxmemory 3221225472

4、查看修改后的值

redis 127.0.0.1:6391> config get maxmemory
1) "maxmemory"
2) "3221225472"

5、修改配置文件,让配置重启有效

# vim /nh/redis/6391/conf/redis.conf
maxmemory 3221225472

LINUX的bash如何给shell脚本传参数

bash命令后边可以跟任意的参数,具体要如何操作?

执行“vi test.sh”创建一个新的shell脚本。

脚本test.sh的内容如下:

#!/bin/sh
name=$1
echo "the ${name} !"

给新建的test.sh的脚本赋可执行权限,命令为“chmod 755 test.sh”。执行可以看到结果.

如果想判断参数为空则中止执行,可以

if [ "$1" = "" ]; then
        echo -e "请提供参数."
        exit 1
fi

或:

if [ $# == 0 ];then
    echo "没有带参数";
else
       echo "带了$#个参数"
fi

或:

if [ "$1" ];then
    echo "带参数";
else
    echo "没有带参数 "
fi

上面的代码其实和使用if结构的-z参数是一样的,都是用于检测字符串是符不空值。因此也可以换成使用-z参数来判断。示例代码如下 :

if [ -z "$1" ];then
    echo "没有带参数";
else
    echo "带参数"
fi

详解:

“name=$1″中$1为系统提供的位置参数,$0代表程序的名称,[$1/$2/…]从1开始为传递的参数。

linux系统除了提供位置参数还提供内置参数,内置参数如下: 

$# ----传递给程序的总的参数数目  

$? ----上一个代码或者shell程序在shell中退出的情况,如果正常退出则返回0,反之为非0值。   

$* ----传递给程序的所有参数组成的字符串。   

$n ----表示第几个参数,$1 表示第一个参数,$2 表示第二个参数 ...   $0 ----当前程序的名称   

$@----以"参数1" "参数2" ... 形式保存所有参数   

$$ ----本程序的(进程ID号)PID   

$! ----上一个命令的PID