使用docker搭建gitlab初体验+数据备份

一. 背景

作为程序员,像GitHub这种好工具是必须得十分了解的,但是有时GitHub并不能满足我们所有的需求,就如作者所在的公司,我们的代码都是商业性的产品,不可能放到GitHub的开放仓库中的,而申请GitHub私人仓库需要钱。这就陷入了尴尬的局面,那有没有一种既能具有GitHub一样的功能,又能保护隐私免费的管理工具呢?答案是肯定的,感谢程序员伟大的开源精神,我们有了GitLab!!!今天笔者在这里就跟大家分享一下自己使用docker搭建GitLab的过程吧,这其中踩了一些坑,希望看过这篇文章的人不用在踩我踩过的坑了!

二. 环境介绍

服务器信息:

CPU : 2
DISK : 30G
RAM : 4G
OS : Linux centos7-0 3.10.0-229.el7.x86_64

这里笔者使用的是自己公司的服务器,也可以使用虚拟机进行搭建

三. 搭建过程

1. 安装docker

因为我们是使用docker搭建的,所以需要先安装docker,docker支持不同的OS,具体的安装信息这里不做详细介绍,可以自己的操作系统,参考官方的安装指南进行安装。http://www.docker.io

2. 安装GitLab及相关组件

GitLab需要用到数据库来存储相关数据,所以需要在安装GitLab的同时安装数据库,这里使用的是postgresql和redis。我在查找相关的镜像,之后发现有很多现成的镜像,这里我使用的sameersbn镜像。但是有一点我认为不是很好的是:这个镜像没有把redis、postgresql集成到gitlab的容器里面,需要先单独pull这两个镜像run一下,然后再pull gitlab的镜像进行安装。

使用如下命令分别拉取最新的镜像:

docker pull sameersbn/redis
docker pull sameersbn/postgresql
docker pull sameersbn/gitlab

这里有第一个坑:因为我们默认都是从docker的官方仓库中拉去镜像,但是由于国内访问国外的网站有墙,而且速度也是十分的慢,所以需要代理。这里推荐Daocloud加速器 https://www.daocloud.io/ 免费使用,但是需要先注册,登录成功后,找到加速器执行相关命令即可。笔者亲测速度明显快很多!

使用如下命令运行postgresql镜像:

docker run --name postgresql -d   
-e 'DB_NAME=gitlabhq_production'   
-e 'DB_USER=gitlab' 
-e 'DB_PASS=password'   
-e 'DB_EXTENSION=pg_trgm'   
-v /home/root/opt/postgresql/data:/var/lib/postgresql   
sameersbn/postgresql

这里需要解释的是:

(1). 以上是一条命令,反斜杠是为了在命令内换行方便阅读,如果不喜欢,也可以写在一行。
(2). -e后面跟的都是容器的环境参数,都是在制作镜像的时候指定好的,所以不要去改动。
(3). -v后面是添加数据卷,这样在容器退出的时候数据就不会丢失,其中 /home/root/opt/postgresql/data是作者自己创建的文件夹,读者可以自己自定义,后面的部分是容器内的文件路径,需要保持不变。
(4). 命令执行成功之后会在控制台显示一串容器的编号,可以使用命令docker ps查看刚刚启动的容器。

使用如下命令运行redis镜像:

docker run --name redis -d   
-v /home/root/opt/redis/data:/var/lib/redis   
sameersbn/redis

这里跟启动postgresql一样。

使用如下命令运行GitLab镜像:

docker run --name gitlab -d 
--link postgresql:postgresql --link redis:redisio 
-p 10022:22 -p 10080:80 
-e 'GITLAB_PORT=10080' 
-e 'GITLAB_SSH_PORT=10022' 
-e 'GITLAB_SECRETS_DB_KEY_BASE=long-and-random-alpha-numeric-string'
-e 'GITLAB_SECRETS_SECRET_KEY_BASE=long-and-random-alpha-numeric-string' 
-e 'GITLAB_SECRETS_OTP_KEY_BASE=long-and-random-alpha-numeric-string'
-e 'GITLAB_HOST=服务器地址' 
-e 'GITLAB_EMAIL=邮箱地址' 
-e 'SMTP_ENABLED=true' 
-e 'SMTP_DOMAIN=www.sina.com' 
-e 'SMTP_HOST=smtp.sina.com'  
-e 'SMTP_STARTTLS=false'  
-e 'SMTP_USER=邮箱地址' 
-e 'SMTP_PASS=邮箱密码' 
-e 'SMTP_AUTHENTICATION=login' 
-e 'GITLAB_BACKUP_SCHEDULE=daily' 
-e 'GITLAB_BACKUP_TIME=10:30' 
-v /home/root/opt/gitlab/data:/home/git/data 
sameersbn/gitlab

这里需要解释的是:

(1). 网上又很多教程讲关于使用docker安装GitLab,但是讲的不全面,至少我按照他们的方法安装时不能正常运行,这里是第三个坑:一定要加上如下环境参数:

-e 'GITLAB_SECRETS_DB_KEY_BASE=long-and-random-alpha-numeric-string'
-e 'GITLAB_SECRETS_SECRET_KEY_BASE=long-and-random-alpha-numeric-string' 
-e 'GITLAB_SECRETS_OTP_KEY_BASE=long-and-random-alpha-numeric-string'

有关于这三个环境参数的含义:

未分类

我个人的理解是用来进行加密的key。

(2). 上面有关SMTP的环境参数是配置邮箱的,需要填上对应的邮箱信息,我使用的是新浪邮箱,读者可以根据自己的邮箱进行填写。

(3). 使用GitLab需要两个端口,一个是web端口,一个是SSH端口用于push代码的所以一下代码进行端口映射和指定:

-p 10022:22 -p 10080:80 
-e 'GITLAB_PORT=10080' 
-e 'GITLAB_SSH_PORT=10022' 

(4). GitLab有自带的备份,这里可以通过如下进行配置:

-e 'GITLAB_BACKUP_SCHEDULE=daily' 
-e 'GITLAB_BACKUP_TIME=10:30' 

指定的是每天10:30进行备份。

说到这里基本上GitLab就搭建好了,这里还有一个小坑就是:运行这些容器的时候可以把代码写进shell脚本中,然后通过脚本进行运行,不然直接在终端打的话很麻烦。

一下就是笔者安装完后的截图,直接访问:http://服务器地址:10080 即可,首次访问可能会出现错误页面,刷新几下页面就可以了然后在修改密码默认用户名:root 之后就可以正常使用。

未分类

未分类

未分类

四. 备份

我们可以使用GitLab自带的备份功能,在启动容器的时候就进行设置,然后再使用GitLab的 app:rake gitlab:backup:restore命令进行恢复,这里网上的教程都有说明可以参考以下网站:
sameersbn的GitHub wiki:
https://github.com/sameersbn/docker-gitlab#automated-backups
这个是官方的所以比较全面,里面还有关于各种环境参数的介绍。

这里作者使用的是如下的备份方法:
因为我们在运行postgresql、redis和GitLab的时候都使用了本地的文件夹进行了数据的持久化,而且我们实际需要备份的数据都在本地了,那么其实就可以直接使用rsync命令备份本地的这些卷(刚刚的文件夹)即可,无需再去深入到GitLab内部。如果搭建的GitLab崩溃了,或者服务器崩溃了,直接再使用docker再搭一个,在把刚刚的卷跟对应的postgresql、redis和GitLab内的数据文件夹进行映射即可。这是也不需要修改之前的启动命令,十分的方便而且作者自己测试过,发现能够达到要求,原先的仓库、用户的SSH信息等都在。

在 GitLab CI 中使用 Docker 构建 Go 项目

介绍

这篇文章是我在 CI 环境(特别是在 Gitlab 中)的 Docker 容器中构建 Go 项目的研究总结。我发现很难解决私有依赖问题(来自 Node/.NET 背景),因此这是我写这篇文章的主要原因。如果 Docker 镜像上存在任何问题或提交请求,请随时与我们联系。

dep

由于 dep 是现在管理 Go 依赖关系的最佳选择,因此在构建前之前运行 dep ensure。

注意:我个人不会将我的 vendor/ 文件夹提交到源码控制,如果你这样做,我不确定这个步骤是否可以跳过。

使用 Docker 构建的最好方法是使用 dep ensure -vendor-only。 见这里。

Docker 构建镜像

我第一次尝试使用 golang:1.10,但这个镜像没有:

  • curl
  • git
  • make
  • dep
  • golint

我已经创建好了用于构建的镜像(github / dockerhub),我会保持更新,但我不提供任何担保,因此你应该创建并管理自己的 Dockerhub。

内部依赖关系

我们完全有能力创建一个有公共依赖关系的项目。但是如果你的项目依赖于另一个私人 Gitlab 仓库呢?

在本地运行 dep ensure 应该可以和你的 git 设置一起工作,但是一旦在 CI 上不适用,构建就会失败。

Gitlab 权限模型

这是在 Gitlab 8.12 中添加的,这个我们最关心的有用的功能是在构建期提供的 CI_JOB_TOKEN 环境变量。

这基本上意味着我们可以像这样克隆依赖仓库:

git clone https://gitlab-ci-token:${CI_JOB_TOKEN}@gitlab.com/myuser/mydependentrepo

然而,我们希望使这更友好一点,因为 dep 在试图拉取代码时不会奇迹般地添加凭据。

我们将把这一行添加到 .gitlab-ci.yml 的 before_script 部分。

before_script:
  - echo -e "machine gitlab.comnlogin gitlab-ci-tokennpassword ${CI_JOB_TOKEN}" > ~/.netrc

使用 .netrc 文件可以指定哪个凭证用于哪个服务器。这种方法可以避免每次从 Git 中拉取(或推送)时输入用户名和密码。密码以明文形式存储,因此你不应在自己的计算机上执行此操作。这实际用于 Git 在背后使用 cURL。 在这里阅读更多。

项目文件

Makefile

虽然这是可选的,但我发现它使事情变得更容易。

配置这些步骤意味着在 CI 脚本(和本地)中,我们可以运行 make lint、make build 等,而无需每次重复步骤。

GOFILES = $(shell find . -name '*.go' -not -path './vendor/*')
GOPACKAGES = $(shell go list ./...  | grep -v /vendor/)

default: build

workdir:
    mkdir -p workdir

build: workdir/scraper

workdir/scraper: $(GOFILES)
    GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -o workdir/scraper .

test: test-all

test-all:
    @go test -v $(GOPACKAGES)

lint: lint-all

lint-all:
    @golint -set_exit_status $(GOPACKAGES)

.gitlab-ci.yml

这是 Gitlab CI 魔术发生的地方。你可能想使用自己的镜像。

image: sjdweb/go-docker-build:1.10

stages:
  - test
  - build

before_script:
  - cd $GOPATH/src
  - mkdir -p gitlab.com/$CI_PROJECT_NAMESPACE
  - cd gitlab.com/$CI_PROJECT_NAMESPACE
  - ln -s $CI_PROJECT_DIR
  - cd $CI_PROJECT_NAME
  - echo -e "machine gitlab.comnlogin gitlab-ci-tokennpassword ${CI_JOB_TOKEN}" > ~/.netrc
  - dep ensure -vendor-only

lint_code:
  stage: test
  script:
    - make lint

unit_tests:
  stage: test
  script:
    - make test

build:
  stage: build
  script:
    - make

缺少了什么

我通常会用我的二进制文件构建 Docker 镜像,并将其推送到 Gitlab 容器注册库中。

你可以看到我正在构建二进制文件并退出,你至少需要将该二进制文件(例如生成文件)存储在某处。

docker部署logstash

logstash

使用ElasticSearch,需要将MySQL内的数据同步到ElasticSearch中去。根据网上文章,觉得logstash属于比较好的同步工具。

不想被logstash环境的搭建与配置困扰。使用docker制作一个镜像,然后可以做到到处运行

Dockerfile

基础镜像:

选择的是dockerhub的logstash。文档地址logstash

镜像文件:

FROM logstash:5

#安装input插件
RUN logstash-plugin install logstash-input-jdbc
#安装output插件
RUN logstash-plugin install logstash-output-elasticsearch
#容器启动时执行的命令.(CMD 能够被 docker run 后面跟的命令行参数替换)
CMD ["-f", "/some/config-dir/logstash-mysql-es.conf"]

build镜像:

docker build -t my-logstash .

条件准备:

创建config目录,创建配置文件logstash-mysql-es.conf
同步MySQL需要MySQL驱动。为了挂载目录的时候,只挂载一个目录。我们把mysql驱动与配置文件放到同一个目录下。

未分类

启动容器:

docker run -d --name logstashmysql -v /root/logstash/config:/some/config-dir/ bc8551a7b495

查看日志:

docker logs -f --tail=30 logstashmysql

CentOS下安装Redis并设置密码外网访问

在windows下,下载redis直接运行redis-server.exe即可,方便快捷。

未分类
redis windows

Centos下安装redis稍微复杂一点。

redis的官网 https://redis.io/

先获取到redis

http://download.redis.io/releases/redis-4.0.9.tar.gz

然后解压编译安装

tar xzf redis-4.0.9.tar.gz
cd redis-4.0.9
make
make install

未分类

编译安装完成后启动Redis

redis-server

也可以通过初始化脚本启动Redis,在编译后的目录utils文件夹中有

redis_init_script

首先将初始化脚本复制到/etc/init.d 目录中,文件名为 redis_端口号(这个mv成了redis_6379),其中端口号表示要让Redis监听的端口号,客户端通过该端口连接Redis。然后修改脚本中的 REDISPORT 变量的值为同样的端口号。

未分类

然后建立存放Redis的配置文件目录和存放Redis持久化的文件目录

/etc/redis 存放Redis的配置文件

/var/redis/端口号 存放Redis的持久化文件(这里是 /var/redis/6379 )

修改配置文件

将配置文件模板 redis-4.0.9/redis.conf 复制到 /etc/redis 目录中,以端口号命名(如 6379.conf ),然后对其中的部分参数进行编辑。

daemonize yes 使Redis以守护进程模式运行
pidfile /var/run/redis_端口号.pid 设置Redis的PID文件位置
port 端口号 设置Redis监听的端口号
dir /var/redis/端口号 设置持久化文件存放位置
#requirepass foobared 若需要设置密码就把注释打开,改成你要设置的密码
bind 127.0.0.1   将其默认的127.0.0.1改为0.0.0.0(代表不做限制),这样外网就能访问了

现在也可以使用下面的命令来启动和关闭Redis了

/etc/init.d/redis_6379 start

/etc/init.d/redis_6379 stop

redis随系统自动启动

chkconfig redis_6379 on

通过上面的操作后,以后也可以直接用下面的命令对Redis进行启动和关闭了,如下

service redis_6379 start

service redis_6379 stop

这样系统重启,Redis也会随着系统启动自动启动起来。

那么怎么停止Redis呢?

上面的stop方法可以停止redis,但是考虑到 Redis 有可能正在将内存中的数据同步到硬盘中,强行终止 Redis 进程可能会导致数据丢失。正确停止Redis的方式应该是向Redis发送SHUTDOWN命令,方法为:

redis-cli SHUTDOWN

当Redis收到SHUTDOWN命令后,会先断开所有客户端连接,然后根据配置执行持久化,最后完成退出。
Redis可以妥善处理 SIGTERM信号,所以使用 kill Redis 进程的 PID也可以正常结束Redis,效果与发送SHUTDOWN命令一样。

如果需要外网访问,首先检查是否被防火墙挡住

然后在配置文件中将bind配置项默认的127.0.0.1改为0.0.0.0

使用腾讯云可能需要在腾讯云控制台对端口进一步设置。

redis刷磁盘可能会导致瞬时无法连接

业务日志监控中报告, 每天会有大约250次连接redis失败.
通过strace追踪发现.故障的时间点时写磁盘时间超过了10s.一般在10-15s之间. redis第二次重试使用的是10s.

这个实例所有的操作都是INCR, fdatasync 会block写.

strace -Ttt -f -p 11302 -T -e  trace=fdatasync

11309 10:21:31.153900 fdatasync(116)    = 0 <0.034295>

11309 10:21:32.078747 fdatasync(116)    = 0 <7.592478>

11309 10:21:39.774959 fdatasync(116)    = 0 <10.098802>

11309 10:21:49.990623 fdatasync(116)    = 0 <2.026147>

11309 10:21:52.129676 fdatasync(116)    = 0 <0.002802>

治标:

超时时间改为15s.

治本:

正在用watchdog抓一下超过5s的堆栈.

堆栈:

[11302 | signal handler] (1499754857)

— WATCHDOG TIMER EXPIRED —

/usr/local/bin/redis-server-2.8 10.160.86.216:6699(logStackTrace+0x3e)[0x445ace]

/lib64/libpthread.so.0(write+0x2d)[0x7f19ef3b06fd]

/lib64/libpthread.so.0(+0xf710)[0x7f19ef3b1710]

/lib64/libpthread.so.0(write+0x2d)[0x7f19ef3b06fd]

/usr/local/bin/redis-server-2.8 10.160.86.216:6699(flushAppendOnlyFile+0x4e)[0x44116e]

/usr/local/bin/redis-server-2.8 10.160.86.216:6699(serverCron+0x3b7)[0x41bb17]

/usr/local/bin/redis-server-2.8 10.160.86.216:6699(aeProcessEvents+0x1e9)[0x416b69]

/usr/local/bin/redis-server-2.8 10.160.86.216:6699(aeMain+0x2b)[0x416deb]

/usr/local/bin/redis-server-2.8 10.160.86.216:6699(main+0x31d)[0x41e49d]

/lib64/libc.so.6(__libc_start_main+0xfd)[0x7f19ef02cd5d]

/usr/local/bin/redis-server-2.8 10.160.86.216:6699[0x415bd9]

[11302 | signal handler] (1499754857) ——–
fdatasync会在某个时间点超过10s.

看来因为写磁盘堵塞了, 把机械硬盘换成了SSD, 解决了。

redis分布式锁实践

分布式锁在多实例部署,分布式系统中经常会使用到,这是因为基于jvm的锁无法满足多实例中锁的需求,本篇将讲下redis如何通过Lua脚本实现分布式锁,不同于网上的redission,完全是手动实现的

我们先来看一个无锁的情况下会导致什么问题:

这是一个普通的更新用户年龄的功能,各层代码如下,访问controller层,一个更新,一个查询

未分类

这是service层,我们使用contdownlatch发令枪来模拟线程同时并发的情况,发令枪设为32,即32个线程同时去请求修改年龄,

未分类

这里使用线程池来提交多线程任务,看代码知道,这里我们已经有了判断年龄的操作,当查询用户查询大于0时,才去调更新用户年龄-1的方法,等下看看有没有用

未分类

这里是sql,可以看到两个sql,一个查询用户年龄,一个会执行用户年龄每次减1 ,

未分类

这里是用户数据,我们可以看到,用户UID为UR12324的用户,他的年龄是30,接着我们来调32个线程来操作减他年龄

未分类

我们请求下这个方法

未分类

然后看看结果:

未分类

未分类

可以看到库中年龄已被减为-2,在未加锁的情况下,查询较验并没有什么作用,此时如果加个synchronized或lock锁肯定能避免这种情况,但我们本文讨论的是多实例或分布式环境中,此加锁方式仍然会产生问题,感兴趣的可以试下是不是

下面我们开始实现一个redis分布式锁,来避免这种情况发生,先说说实现思路:

1、线程请求访问前先调用加锁的方法,加锁就去里生成一个随机数同时保存在线程本地变量和redis的某key中,此key设有效期为200ms,具体值根据业务执行时间自行调整,加锁成功;

2、其它线程试着访问拿出它本地变量与redis中某key进行比较,如果不一致,则说明有锁,此线程休眠一段时间,再试着加锁;

3、加锁成功的线程在操作结束后删掉它持有锁(用lua实现,保证原子性,在它比对和删除锁的过程中,其它线程不会加锁成功),让其它线程再次加锁以执行任务;

说明:锁的时间为200ms可预防线程挂掉之后死锁,200ms后会自动释放

下面看看我们写的锁代码:

片段1:使用redislock 实现lock来复写它的方法

未分类

片段2:试着加锁的方法

未分类

片段3:解锁方法,此处首先从线程本地变量获取它的随机数,然后调用lua脚本,与redis中key相比较,如果相同则删除,否则返回0;

未分类

此为lua脚本方法,用此方法可以保证判断和删除的原子性,在此过程中没有线程可以操作此key

未分类

到此为止,我们锁基本写完,来测试下有没有用:

未分类

我们在此方法前后分别加入加锁和解锁方法,使用方式和lock锁一样, 我们重新把年龄恢复到30后来测试一下吧

先看看日志

未分类

这里可以看到各个线程争夺锁的情况,再看看执行结果

未分类

未分类

这里我们可以看到虽然是32个线程并发执行,但此值并不会变为负数,加锁成功.

我们可以看到最后2个线程并没有执行方法

未分类

未分类

在具体生产环境中,比如典型的用户余额扣减,我们可以用用户UID作KEY,这样就不会造成100个用户,500个线程争夺一个锁的情况发生,100个用户会有100个锁,此时假如每个用户5个请求,一个锁只处理5个线程

大大提高锁的效率.

此时说明加锁成功,大家可以在分布式环境中测试更明显,有关极端情况下解锁失败后应该做什么也可以由我们自己决定,比redission要灵活,带锁的redis最好是单实例,在集群中可能会出问题,有机会我们再用zk实现下.

redis数据迁移方案

一、主从复制

二、数据文件拷贝

1、copy dump.rdb 到目标主机dump.rdb存储目录下后启动redis服务

$ scp /var/lib/redis/dump.rdb user@ip:/var/lib/redis/dump.rdb
# 登录目标主机后重启redis服务
$ docker restart redis

2、启动aof模式,copy appendonly.aof 到目标主机 redis 数据存储目录下后

$ redis-cli config set appendonly yes
$ scp /var/lib/redis/appendonly.aof user@ip:/var/lib/redis/appendonly.aof
$ redis-cli config set appendonly no
# 登录目标主机后加载数据
$ docker restart redis

设置Redis最大占用内存

设置Redis最大占用内存

Redis需要设置最大占用内存吗?如果Redis内存使用超出了设置的最大值会怎样?

设置Redis最大占用内存

Redis设置最大占用内存,打开redis配置文件,找到如下段落,设置maxmemory参数,maxmemory是bytes字节类型,注意转换。修改如下所示:

# In short... if you have slaves attached it is suggested that you set a lower
# limit for maxmemory so that there is some free RAM on the system for slave
# output buffers (but this is not needed if the policy is 'noeviction').
#
# maxmemory <bytes>
maxmemory 268435456

本机服务器redis配置文件路径:/etc/redis/6379.conf,由于本机自带内存只有1G,一般推荐Redis设置内存为最大物理内存的四分之三,所以设置0.75G,换成byte是751619276.

可以在CentOS下输入命令:find / -name redis查找redis目录:

[root@iZ94r80gdghZ ~]# find / -name redis
/usr/share/nginx/html/blog.tanteng.me/wp-content/cache/supercache/blog.tanteng.me/tag/redis
/etc/redis
/var/redis

Redis配置文件一般在etc下的redis安装目录下。

Redis使用超过设置的最大值

如果Redis的使用超过了设置的最大值会怎样?我们来改一改上面的配置,故意把最大值设为1个byte试试。

# output buffers (but this is not needed if the policy is 'noeviction').
#
# maxmemory <bytes>
maxmemory 1

打开debug模式下的页面,提示错误:OOM command not allowed when used memory > ‘maxmemory’.

设置了maxmemory的选项,redis内存使用达到上限。可以通过设置LRU算法来删除部分key,释放空间。默认是按照过期时间的,如果set时候没有加上过期时间就会导致数据写满maxmemory。

如果不设置maxmemory或者设置为0,64位系统不限制内存,32位系统最多使用3GB内存。

LRU是Least Recently Used 近期最少使用算法。

  • volatile-lru -> 根据LRU算法生成的过期时间来删除。
  • allkeys-lru -> 根据LRU算法删除任何key。
  • volatile-random -> 根据过期设置来随机删除key。
  • allkeys->random -> 无差别随机删。
  • volatile-ttl -> 根据最近过期时间来删除(辅以TTL)
  • noeviction -> 谁也不删,直接在写操作时返回错误。

如果设置了maxmemory,一般都要设置过期策略。打开Redis的配置文件有如下描述,Redis有六种过期策略:

# volatile-lru -> remove the key with an expire set using an LRU algorithm
# allkeys-lru -> remove any key accordingly to the LRU algorithm
# volatile-random -> remove a random key with an expire set
# allkeys-random -> remove a random key, any key
# volatile-ttl -> remove the key with the nearest expire time (minor TTL)
# noeviction -> don't expire at all, just return an error on write operations

那么打开配置文件,添加如下一行,使用volatile-lru的过期策略:

maxmemory-policy volatile-lru

保存文件退出,重启redis服务。

info命令查看Redis内存使用情况

如服务器Redis所在目录:/usr/local/redis-3.0.7/src

在终端输入./redis-cli,打开Redis客户端,输入info命令。

出来如下信息:

[root@iZ94r80gdghZ src]# ./redis-cli
127.0.0.1:6379> info
# Server
redis_version:3.0.7
redis_git_sha1:00000000
redis_git_dirty:0
redis_build_id:f07a42660a61a05e
redis_mode:standalone
os:Linux 3.10.0-327.10.1.el7.x86_64 x86_64
arch_bits:64
multiplexing_api:epoll
gcc_version:4.8.5
process_id:2165
run_id:8ec8a8dc969d6e2f2867d9188ccb90850bfc9acb
tcp_port:6379
uptime_in_seconds:668
uptime_in_days:0
hz:10
lru_clock:15882419
config_file:/etc/redis/6379.conf

# Clients
connected_clients:1
client_longest_output_list:0
client_biggest_input_buf:0
blocked_clients:0

# Memory
used_memory:816232
used_memory_human:797.10K
used_memory_rss:7655424
used_memory_peak:816232
used_memory_peak_human:797.10K
used_memory_lua:36864
mem_fragmentation_ratio:9.38
mem_allocator:jemalloc-3.6.0

# Persistence
loading:0
rdb_changes_since_last_save:0
rdb_bgsave_in_progress:0
rdb_last_save_time:1458722327
rdb_last_bgsave_status:ok
rdb_last_bgsave_time_sec:-1
rdb_current_bgsave_time_sec:-1
aof_enabled:0
aof_rewrite_in_progress:0
aof_rewrite_scheduled:0
aof_last_rewrite_time_sec:-1
aof_current_rewrite_time_sec:-1
aof_last_bgrewrite_status:ok
aof_last_write_status:ok

# Stats
total_connections_received:1
total_commands_processed:0
instantaneous_ops_per_sec:0
total_net_input_bytes:14
total_net_output_bytes:0
instantaneous_input_kbps:0.00
instantaneous_output_kbps:0.00
rejected_connections:0
sync_full:0
sync_partial_ok:0
sync_partial_err:0
expired_keys:0
evicted_keys:0
keyspace_hits:0
keyspace_misses:0
pubsub_channels:0
pubsub_patterns:0
latest_fork_usec:0
migrate_cached_sockets:0

# Replication
role:master
connected_slaves:0
master_repl_offset:0
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0

# CPU
used_cpu_sys:0.30
used_cpu_user:0.29
used_cpu_sys_children:0.00
used_cpu_user_children:0.00

# Cluster
cluster_enabled:0

# Keyspace
db0:keys=1,expires=1,avg_ttl=425280

其中used_memory:816232,仅用了0.7M左右。

本文在网上查阅资料,在阿里云CentOS主机实践。

自动布署服务器环境,并利用 GIt 实现本地代码自动同步到服务器!

这里说的自动布署是两方面的,第一部分是脚本自动布署服务器环境,第二部份是自动布署代码,完成这两部分,在我理解,就算是完成自动部署了。

我们要做的,就是本地写完代码提交 git 后,使用git push 自动将代码推送到测试或生产环境的站点目录。 好的,开工吧!

LNMP线上环境自动布署脚本

使用 此脚本 可在一台全新的 Ubuntu 14.04 LTS 或者 Ubuntu 16 上自动部署适合 Laravel 使用的 LNMP 生产环境。 按照此 文档 安装即可。

但是此方法在使用中会有一些小问题

1、网易镜像加速后会出现一些安装错误 可能是网易镜像没有更新完全。 需要将网易镜像地址更新为其它地址,可以参考 Ubuntu 官方模版 来更新镜像。 我使用的是阿里云的镜像替换了网易的镜像。

vi /etc/apt/sources.list

更新为

deb http://mirrors.aliyun.com/ubuntu/ xenial main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu/ xenial-security main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu/ xenial-updates main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu/ xenial-backports main restricted universe multiverse
##測試版源
deb http://mirrors.aliyun.com/ubuntu/ xenial-proposed main restricted universe multiverse
# 源碼
deb-src http://mirrors.aliyun.com/ubuntu/ xenial main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ xenial-security main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ xenial-updates main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ xenial-backports main restricted universe multiverse
##測試版源
deb-src http://mirrors.aliyun.com/ubuntu/ xenial-proposed main restricted universe multiverse
# Canonical 合作夥伴和附加
deb http://archive.canonical.com/ubuntu/ xenial partner
deb http://extras.ubuntu.com/ubuntu/ xenial main

2、正常安装后Nginx 启动前需要把 apache 卸载掉,然后再启动 Nginx .

apt-get purge apache2
service nginx restart

3、redis 默认没有启动

service redis-server start  // 启动 redis
service redis-server status  // 查看 redis
service redis-server  stop  // 停止 redis

配置Git自动部署

创建Git远程仓库

我们用一个独立的路径来做远程仓库。 然后在仓库的路径下,创建一个git裸仓库:

cd /home/ubuntu/repo/
git init --bare blog.git

git 默认是禁止 push 的,所有要设置允许 push:

vi config

修改或添加如下内容:

[receive]
        denyCurrentBranch = ignore

编辑自动部署脚本

自动部署用到 git hooks ,在 git 路径下有个 hooks 文件夹,里面有一些示例。我们把 post-update.sample 重命名为 post-update ,并进行编辑:

mv post-update.sample post-update
vi post-update
#!/bin/sh
#
# An example hook script to prepare a packed repository for use over
# dumb transports.
#
# To enable this hook, rename this file to "post-update".

#exec git update-server-info

unset GIT_DIR

NowPath=`pwd`

DeployPath="/home/ubuntu/www/blog"

cd $DeployPath
git pull origin master
composer install

cd $NowPath

echo 'deploy success'
exit 0

这样每当 push master 分支到服务器时,都会自动切换到 DeployPath ,也就是 Nginx root 路径,执行 git pull origin master 从仓库拉去最新 master 分支,并执行 composer install ,如果项目 composer 有变更则安装,没有变更则不会安装。

部署路径初始化

先把空的仓库克隆到 home/ubuntu/www/ 路径下 :

git clone  /home/ubuntu/repo/blog.git

服务器的配置就级别完成了,此 blog 即为你的站点目录

推送git仓库

进入本地共享文件夹,执行

git clone root@server_ip:/home/ubuntu/repo/blog.git blog_back

将远程的空仓库克隆下来。名称为 blog_back,防止与本地 blog 目录冲突。 ok,现在我们来创建一个项目

本地创建项目并提交 Git

composer create-project --prefer-dist laravel/laravel blog
mv blog_back/.git  blog/.git  //将 .git 目录拷贝过来即可在此目录操作 git
cd  blog
git add -A
git commit -m 'init'
git push origin master //此时输入服务器密码即可将本地项目推送到服务器上

这里我使用的是默认的 root 用户,我们可以单独设置一个 git 用户,也可以使用公钥的形式,类似于 github 的方式。

安装 Composer

打开命令行并依次执行下列命令安装最新版本的 Composer:

php -r "copy('https://install.phpcomposer.com/installer', 'composer-setup.php');"
//下载安装脚本 - composer-setup.php - 到当前目录。
php composer-setup.php //执行安装过程。
php -r "unlink('composer-setup.php');"  //删除安装脚本

执行第一条命令下载下来的 composer-setup.php 脚本将简单地检测 php.ini 中的参数设置,如果某些参数未正确设置则会给出警告;然后下载最新版本的 composer.phar 文件到当前目录。 打开命令行窗口并执行如下命令将前面下载的 composer.phar 文件移动到 /usr/local/bin/ 目录下面:

sudo mv composer.phar /usr/local/bin/composer

进程监控器 Supervisor 配置

项目中有些脚本需要在后台运行,比如队列、Horizon ,Supervisor可以监控后台脚本的运行,再产生异常或是停止后自动重启,保证了脚本不会被异常中断。定时任备也可以直接用 Supervisor 来执行,Supervisor 还可以写入执行成功或失败的日志,方便查看。 此脚本默认安装了 supervisor ,我们只需设置好启动即可。

配置

在 /etc/supervisor/conf.d/ 下新建一个配置文件 horizon.conf,写入以下内容

[program:horizon]
process_name=%(program_name)s_%(process_num)02d
command=php /home/ubuntu/www/site/artisan horizon  //启动脚本命令。
autostart=true  //随着supervisord的启动而启动
autorestart=true //自动重启
user=ubuntu  // 用户组
numprocs=1 // 启动进程,根据脚本决定
redirect_stderr=true  //重定向stderr到stdout
stdout_logfile=/var/log/supervisor/horizon.log  //日志 注意日志目录的权限

接下来就可以启动 supervisord 了。

supervisord -c /etc/supervisord.conf  //启动
supervisorctl shutdown //关闭
supervisorctl reload  //重新载入配置

好了。到这里自动布署就完成了,接下来就是尽情的敲代码吧!

【Git学习笔记】用git pull取回远程仓库某个分支的更新,再与本地的指定分支自动merge

git pull的作用是,从远程库中获取某个分支的更新,再与本地指定的分支进行自动merge。完整格式是:

$ git pull <远程库名> <远程分支名>:<本地分支名>  

比如,取回远程库中的develop分支,与本地的develop分支进行merge,要写成:

git pull origin develop:develop  

如果是要与本地当前分支merge,则冒号后面的<本地分支名>可以不写。

git pull origin develop  

通常,git会将本地库分支与远程分支之间建立一种追踪关系。比如,在git clone的时候,所有本地分支默认与远程库的同名分支建立追踪关系。也就是说,本地的master分支自动追踪origin/master分支。因此,如果当前处于本地develop分支上,并且本地develop分支与远程的develop分支有追踪关系,那么远程的分支名可以省略:

git pull origin  

其实,git pull 命令等同于先做了git fetch ,再做了git merge。即:

git fetch origin develop  
git checkout develop  
git merge origin/develop  

好多人不建议使用git pull,喜欢自己merge,以便万一自动merge出错的时候可以解决冲突。