月度归档:2014年11月

GitLab搭建与维护(基于docker镜像sameersbn/docker-gitlab)

tag: git, gitlab, subversion, sameersbn/docker-gitlab

1. 阅读本文基础

  • 熟悉git使用
  • 熟悉docker

2. GitLab简介

2.1. 概述

  • GitLab 是一个用于仓库管理系统的开源项目。使用Git作为代码管理工具,并在此基础上搭建起来的web服务。Github是公共的git仓库,而Gitlab适合于搭建企业内部私有git仓库
  • 官网:
    https://about.gitlab.com/
    https://github.com/gitlabhq/gitlabhq
    
  • GIT与SVN的比较:
    http://www.liaoxuefeng.com/wiki/0013739516305929606dd18361248578c67b8067c8c017b000/001374027586935cf69c53637d8458c9aec27dd546a6cd6000
    http://www.oschina.net/news/12542/git-and-svn
    
  • 截止本文创建时间,GitLab最新版本为v7.4.3

2.2. 架构

  • 示意图:
  • gitlab_arch

2.3. 组件

  • 前端:Nginx,用于页面及Git tool走http或https协议
  • 后端:Gitlab服务,采用Ruby on Rails框架,通过unicorn实现后台服务及多进程
  • SSHD:开启sshd服务,用于用户上传ssh key进行版本克隆及上传。注:用户上传的ssh key是保存到git账户中
  • 数据库:目前仅支持MySQL和PostgreSQL
  • Redis:用于存储用户session和任务,任务包括新建仓库、发送邮件等等
  • Sidekiq:Rails框架自带的,订阅redis中的任务并执行

3. GitLab安装部署

3.1. 官方支持的方式

3.2. 采用docker镜像安装GitLab

3.2.1. 简介

3.2.2. 架构图

docker_gitlab_arch

3.2.3. 下载镜像

  • docker pull sameersbn/gitlab:7.4.3 # 下载gitlab镜像
  • docker pull sameersbn/mysql:latest # 下载gitlab所用到的mysql镜像
  • docker pull sameersbn/redis:latest # 下载gitlab所用到的redis镜像

3.2.4. 安装

3.2.4.1. 启动redis

  • 命令:
    docker run \
    	--name=gitlab_redis \
    	-tid \
    	sameersbn/redis:latest
    

3.2.4.2. 启动mysql

  • mkdir -p /opt/gitlab/mysql
  • 命令:
    docker run \
    	--name=gitlab_mysql \
    	-tid \
    	-e 'DB_NAME=gitlabhq_production' \
    	-e 'DB_USER=gitlab' \
    	-e 'DB_PASS=password' \
    	-v /opt/gitlab/mysql:/var/lib/mysql \
    	sameersbn/mysql:latest
    

3.2.4.3. 启动gitlab

  • mkdir -p /opt/gitlab/data /opt/gitlab/log
  • 命令:
    docker run \
    	--name='gitlab' \
    	-itd \
    	--link gitlab_mysql:mysql \
    	--link gitlab_redis:redisio \
    	-e 'GITLAB_PORT=80' \
    	-e 'GITLAB_SSH_PORT=22' \
    	-e 'GITLAB_HOST=gitlab.example.com' \
    	-v /var/run/docker.sock:/run/docker.sock \
    	-v $(which docker):/bin/docker \
    	-v /opt/gitlab/data:/home/git/data \
    	-v /opt/gitlab/log:/var/log/gitlab \
    	sameersbn/gitlab:7.4.3
    
    
    上述是开启一个基本gitlab。
    完整(包含LDAP、EMAIL):
    docker run \
    	--name='gitlab' \
    	-itd \
    	--link gitlab_mysql:mysql \
    	--link gitlab_redis:redisio \
    	-e 'GITLAB_PORT=80' \
    	-e 'GITLAB_SSH_PORT=22' \
    	-e 'LDAP_ENABLED=true' \
    	-e 'LDAP_HOST=192.168.1.1' \
    	-e 'LDAP_PORT=389' \
    	-e 'LDAP_UID=sAMAccountName' \
    	-e 'LDAP_METHOD=plain' \
    	-e 'LDAP_BIND_DN=test@example.com' \
    	-e 'LDAP_PASS=passwd' \
    	-e 'LDAP_BASE=OU=example_users,DC=example-family,DC=com' \
    	-e 'LDAP_ACTIVE_DIRECTORY=true' \
    	-e 'LDAP_ALLOW_USERNAME_OR_EMAIL_LOGIN=false' \
    	-e 'GITLAB_HOST=gitlab.example.com' \
    	-e 'SMTP_ENABLED=true' \
    	-e 'SMTP_DOMAIN=example.com' \
    	-e 'SMTP_HOST=192.168.1.2' \
    	-e 'SMTP_PORT=25' \
    	-e 'SMTP_STARTTLS=false' \
    	-v /var/run/docker.sock:/run/docker.sock \
    	-v $(which docker):/bin/docker \
    	-v /opt/gitlab/data:/home/git/data \
    	-v /opt/gitlab/log:/var/log/gitlab \
    	sameersbn/gitlab:7.4.3
    

这一步骤会耗时几分钟,因为这一步会做一些初始化操作,例如导入数据表结构等。可以通过docker logs gitlab来查看安装过程。同理,mysql、redis容器也可以通过docker logs gitlab_mysql和docker logs gitlab_redis来查看启动信息。

注意:上面创建的3个容器必须位于同一台宿主上,因为–link gitlab_mysql:mysql –link gitlab_redis:redisio就是将同个宿主上的容器做链接

  • 当然,redis和mysql也支持使用ip+端口,不用–link,带上环境变量即可,方法是:
    完整:
    docker run \
    	--name='gitlab' \
    	-itd \
    	--net=none \
    	--hostname='gitlab.example.com' \
    	-e 'DB_TYPE=mysql' \
    	-e 'DB_HOST=192.168.3.1' \
    	-e 'DB_PORT=3356' \
    	-e 'DB_NAME=gitlabhq_production' \
    	-e 'DB_USER=gitlab' \
    	-e 'DB_PASS=passwd' \
    	-e 'REDIS_HOST=192.168.3.2' \
    	-e 'REDIS_PORT=6402' \
    	-e 'UNICORN_WORKERS=20' \
    	-e 'GITLAB_PORT=80' \
    	-e 'GITLAB_SSH_PORT=22' \
    	-e 'LDAP_ENABLED=true' \
    	-e 'LDAP_HOST=192.168.3.3' \
    	-e 'LDAP_PORT=389' \
    	-e 'LDAP_UID=sAMAccountName' \
    	-e 'LDAP_METHOD=plain' \
    	-e 'LDAP_BIND_DN=test@example.com' \
    	-e 'LDAP_PASS=passwd' \
    	-e 'LDAP_BASE=OU=example_users,DC=example-family,DC=com' \
    	-e 'LDAP_ACTIVE_DIRECTORY=true' \
    	-e 'LDAP_ALLOW_USERNAME_OR_EMAIL_LOGIN=false' \
    	-e 'GITLAB_HOST=gitlab.example.com' \
    	-e 'SMTP_ENABLED=true' \
    	-e 'SMTP_DOMAIN=example.com' \
    	-e 'SMTP_HOST=192.168.3.4' \
    	-e 'SMTP_PORT=25' \
    	-e 'SMTP_STARTTLS=false' \
    	-v /var/run/docker.sock:/run/docker.sock \
    	-v $(which docker):/bin/docker \
    	-v /opt/gitlab/data:/home/git/data \
    	-v /opt/gitlab/log:/var/log/gitlab \
    	sameersbn/gitlab:7.4.3
    
    sameersbn/gitlab:7.4.3不支持redis的任何验证,只能无密码使用
    

目前发现sameersbn/gitlab:7.4.3有一个非常坑的地方:容器启动可能连不上数据库,是因为容器启动时会探测数据库是否可用,而探测的方法是mysqladmin连接DB_HOST的3306端口,而不是DB_PORT指定的端口,已提交issue给作者。

3.2.4.4. 给gitlab容器配置IP

  • pipework br1 gitlab 192.168.1.1/24@192.168.1.254

在gitlab容器启动(docker run)时可以加上-p参数来将容器里的端口映射到宿主上,但个人比较倾向给容器配置一个独立IP,因此上述命令没有采用-p来做端口映射,但是-e ‘GITLAB_PORT=80’ -e ‘GITLAB_SSH_PORT=22’ -e ‘GITLAB_HOST=gitlab.example.com’依然要指定,否则gitlab无法使用

3.2.4.5. 安装已完成,可以打开页面

  • url: gitlab.example.com
    账户:root
    密码:5iveL!fe
    
  • 效果:gitlab_example

3.2.4.6. 加入开机启动

  • 加入/etc/rc.local
    echo 'docker start gitlab_redis' >> /etc/rc.local
    echo 'docker start gitlab_mysql' >> /etc/rc.local
    echo 'docker start gitlab' >> /etc/rc.local
    echo 'pipework br1 gitlab 192.168.1.1/24@192.168.1.254' >> /etc/rc.local
    

4. GitLab API wrappers

  • 官方推荐:https://about.gitlab.com/applications/
  • python:pyapi-gitlab
    基本使用:
    pip install pyapi-gitlab
    import gitlab
    git = gitlab.Gitlab("http://gitlab.example.com", token="EHBLkwhr_WYzn-sXNnNs")		# token即在页面上Profile settings->Account里能看到自动生成好的Private token
    git.getusers()
    
  • 顺便推荐个Git本身的python api模块:https://github.com/FriendCode/gittle

其他很多git库的实现都太底层了,和linux的git命令完全不一样,一点也不友好。但目前发现这个模块上传下载代码只支持SSH,不支持HTTP,因此我还是更喜欢使用linux git命令

5. GitLab使用的FAQ

5.1. git代码发布支持2种方法:ssh和http(s)

  • ssh:必须添加ssh key才能发布
  • http:使用用户名、密码,若接入了LDAP,就是LDAP中的账户密码
  • https:暂未测试过

5.2. 限制git上传的单个文件大小

  • 若gitlab是通过sameersbn/gitlab:7.4.3镜像创建的,那么可以通过环境变量NGINX_MAX_UPLOAD_SIZE进行限制

5.3. 通过API进行一些操作例如创建Project,得到成功的响应,但却没有立即生效,过了几秒才生效

  • 由于GitLab为异步架构,Ruby on Rails收到创建Project请求后将该任务推送到Redis中,但是没有等待执行完毕,而是直接返回成功。后台Sidekiq从Redis订阅任务并实时执行,但由于执行需要时间,而API返回很快,因此会造成这种情况发生。目前没有其他解决办法,只能在自己程序逻辑里sleep几秒进行重试

5.4. 页面上”Profile settings->SSH Keys”与”Project Settings里Deploy Keys”的区别

  • Profile settings->SSH Keys:是用户全局的Key,具有对该用户所有项目仓库进行上传下载(push、clone)的权限
  • Project Settings里Deploy Keys:针对某个项目,具有下载(clone)的权限,而不具备上传(push)的权限

5.5. gitlab默认时区是UTC

  • sameersbn/gitlab:7.4.3镜像无法对时区进行修改,若是手动安装的gitlab,可以通过修改一个.rb文件来生效,具体没有测试过,有需要的可以自行百度搜下。

时区是UTC主要影响的是数据库里关于时间字段的值(created_at、updated_at等),对用户git仓库是不影响的,因为git仓库的创建、代码更新都是用户通过git工具自行操作,因此是用户自己的时区。

6. GitLab维护

6.1. 数据路径、日志路径

  • 由于gitlab容器在启动时已将宿主/opt/gitlab/data、/opt/gitlab/log目录挂载到容器里,因此可以在宿主上进入这2个目录中查看,另外可以通过docker logs查看。

6.2. 高可用

  • 官方有篇关于GitLab高可用的文章:https://about.gitlab.com/high-availability/
    有1/3都在讲述高可用的利弊以及不同程度的高可用带来的收益及可能引发的更多问题,说的挺有道理的。由于笔者也才刚开始使用GitLab,因此暂不测试多机load balance或failover。
    
  • 2种备份恢复方式
    1. 备份配置、仓库、数据库。
    2. 给文件系统做快照。
    官方认为第二种方法比起第一种更快,因为可以省去人为介入restore的麻烦
    XFS文件系统支持快照
    
    有2篇文章可以拜读下:
    http://www.icicletech.com/blog/gitlab-backup-made-easy 数据库数据备份到本地
    http://doc.gitlab.com/ce/raketasks/backup_restore.html 官方的,和上述其实一样
    
  • 我的备份方案:
    一. 简介:每天固定时间备份数据(仅备份MySQL和GitLab仓库重要文件,其他不备份)。若数据丢失,需要恢复,则将最近备份的数据用来恢复,会将所有数据,包括本地文件(仓库等文件)、数据库一同还原回备份时刻。
    
    二. 具体操作:
    	1. 备份:
    docker run \
    	--name='gitlab_backup' \
    	-it \
    	--rm \
    	--link gitlab_mysql:mysql \
    	--link gitlab_redis:redisio \
    	-v /var/run/docker.sock:/run/docker.sock \
    	-v $(which docker):/bin/docker \
    	-v /opt/gitlab/data:/home/git/data \
    	-v /opt/gitlab/log:/var/log/gitlab \
    	sameersbn/gitlab:7.4.3 app:rake gitlab:backup:create
    
    	   过程能在屏幕上看到,备份会自动打成tar包放入/opt/gitlab/data/backups里。可以自行对该tar包做压缩,例如gzip 时间戳_gitlab_backup.tar。
    	   若$?=0表示备份成功,然后将生成的文件使用gzip压缩为.tar.gz,然后rsync推到存储上
           通过rsync命令传到一台备份存储上
    	2. 恢复:
    docker run \
    	--name='gitlab_restore' \
    	-it \
    	--rm \
    	--link gitlab_mysql:mysql \
    	--link gitlab_redis:redisio \
    	-v /var/run/docker.sock:/run/docker.sock \
    	-v $(which docker):/bin/docker \
    	-v /opt/gitlab/data:/home/git/data \
    	-v /opt/gitlab/log:/var/log/gitlab \
    	sameersbn/gitlab:7.4.3 app:rake gitlab:backup:restore
    
           屏幕上会将/opt/gitlab/data/backups中的所有文件和目录列出来,复制粘贴要恢复的tar包文件名,敲入回车即开始恢复。
    	   注意:恢复时会将当前数据库中的所有表先删掉再导入备份tar包的里sql文件,因此此步要小心。
    	   
    	   若redis、mysql是使用环境变量带入gitlab容器的,备份和恢复命令也类似,将启动gitlab的命令复制过来,修改下--name,添加一个--rm,CMD改为gitlab:backup:create或gitlab:backup:restore即可
    
    三. 注意:
        1. 用户配置的key(即data/.ssh)是保存在数据库里,在gitlab服务启动时候会从数据库里加载并导入到.ssh里,被gitlab-shell管理
        2. 由于ssh目录(保存着服务器sshd服务启动时自己生成的ssh_key)不备份,因此还原时候若这个目录里有文件,则不去重新生成ssh_key,若不存在,则重新生成。若重新生成,用户使用ssh协议进行git push,会报错告知不合法的认证
        3. 备份只会备份3个目录:repositories、db、uploads,然后会额外生成一个backup_information.yml文件
        4. gitlab-satellites和tmp都是临时目录,因此不参与备份
        5. docker run --rm不会影响容器退出状态的输出
        6. 还原时候会将数据库里的表先DROP表再创建,因为sql文件在INSERT之前会drop table if exists
        7. 还原时候,若存在要恢复的目录,则会将原来的目录mv成.old.时间戳

[MySQL FAQ]系列 — 从MyISAM转到InnoDB需要注意什么

问题
当前,绝大多数业务场景用InnoDB已经完全能搞定了,越来越多的业务从MyISAM转向InnoDB引擎,那么有哪些注意事项呢?

分析
当了解完两种引擎的不同之处,很轻松的就能知道有哪些关键点了。
总的来说,从MyISAM转向InnoDB的注意事项有:

1、MyISAM的主键索引中,可以在非第一列(非第一个字段)使用自增列,而InnoDB的主键索引中包含自增列时,必须在最前面;这个特性在discuz论坛中,被设计用于“抢楼”功能,因此,若有类似的业务,则无法将该表从MyISAM转成InnoDB,需要自行变通实现(我们则是将其改到Redis中实现);
2、不带条件频繁统计全表总记录数时(SELECT COUNT(*) FROM TAB),InnoDB相对较慢,而MyISAM则飞快;不过,如果是基于索引条件的统计,则二者相差不大;
3、InnoDB在5.6以前不支持全文索引,不过这个相信无所谓,没什么人会在MySQL里直接跑全文索引,尤其是对中文的全文索引(前阵子有开发同学提需求直接被我否了),确实有需要的话,可以采用Sphinx、Lucene等其他方案实现;
4、一次性导入大量数据并且后续还要进行加工处理的,可以先导入到MyISAM引擎表中,经过一通加工处理完后,再导入InnoDB表(我曾经在业务中用此方法提高数据批量导入及处理效率);
5、InnoDB不支持LOAD TABLE FROM MASTER语法(不过应该也很少人使用吧);
从MyISAM转成InnoDB可以享受的好处则有:

1、完整事务特性支持,以及更高的数据并发存取效率,即更高的TPS;
2、数据库实例异常重启后,InnoDB表能自动修复,而且速度相对更快,而MyISAM需要被触发才能修复,且相对耗时可能多4~5倍甚至更多;
3、更高的数据读取性能,因为InnoDB把数据及索引同时缓存在内存中,而MyISAM只缓存了索引;
4、InnoDB支持外键(不过在MySQL中,应该很少人用到外键);
两个引擎间的重要区别详情见下:

MyISAM引擎的特点:
1、堆组织表;
2、不支持事务;
3、数据文件和索引文件分开存储;
4、支持全文索引;
5、主键索引和二级索引完全一样都是B+树的数据结构,只有是否唯一的区别(主键和唯一索引有唯一属性,其他普通索引没有唯一属性。B+树叶子节点存储的都是指向行记录的row pointer);
6、有特殊计数器记录当前记录数;
7、不支持Crash recovery;
8、索引文件很容易损坏;
InnoDB引擎的特点

1、索引组织表;
2、支持事务;
3、数据文件和索引文件存储在同一个表空间中;
4、在5.6以前,不支持全文索引;
5、主键和二级索引数据结构一样都是B+树,但叶子节点存储的键值不一样(主键的叶子节点存储整行数据,因此也称为聚集索引;而二级索引的叶子节点存储的是主键的键值)
5、支持Crash recovery;
6、相同数据量时,InnoDB表空间文件大小约为MyISAM引擎的1.5~2倍;
关于InnoDB、MyISAM两种引擎的对比测试,可以参考Percona的这个对比:http://www.percona.com/blog/2007/01/08/innodb-vs-myisam-vs-falcon-benchmarks-part-1/

[MySQL FAQ]系列 — 如何将两个表名对调

问题
有位同学问我,在类似pt-osc场景下,需要将两个表名对调,怎么才能确保万无一失呢?

分析
估计其他同学就笑了,表名对掉还不简单吗,相互RENAME一下嘛。

但是,我们想要的是同时完成表名对调,如果是先后的对掉,可能会导致有些数据写入失败,那怎么办?

其实也不难,从MySQL手册里就能找到方法,那就是:同时锁定2个表,不允许写入,然后对调表名。

我们通常只锁一个表,那么同时锁两个表应该怎么做呢,可以用下面的方法:

LOCK TABLES t1 WRITE, t2 WRITE;
ALTER TABLE t1 RENAME TO t3;
ALTER TABLE t2 RENAME TO t1;
ALTER TABLE t3 RENAME TO t2;
UNLOCK TABLES;
看到了吧,其实很简单,两个表同时加表级写锁,然后用ALTER语法改名就可以了。

废话挺多的,谢谢各位客官耐心看完 :)