如何离线安装ansible

在有网络的情况下,ansible还是很好安装的。但如果你的生产环境有很严格的网络要求,不能够连接外网,你又需要在生产环境上使用ansible。那只有使用离线的方式来安装。但很可惜的是,ansible官方提供的安装包,比如rpm包,并没有包含它所需要的依赖,直接安装是无法使用的。因此需要找个方法自己把所有的依赖解决。

解决的方法有很多,这里列一个比较简单的。首先,找一台能够上网的机器,并且拥有和你的生产服务器有相同linux版本(你的开发环境或测试环境一定有这样的机器)。然后在上头安装对应的工具(二选一):

  • yum-downloadonly
  • Yumdownloader

yum-downloadonly

安装 “downloadonly” 插件:

(RHEL5)
# yum install yum-downloadonly

(RHEL6)
# yum install yum-plugin-downloadonly

在运行yum install时,使用–downloadonly”选项:

yum install --downloadonly --downloaddir=<directory> <package>

确认你需要的package和对应的dependency包已经被保存在了你设置的下载目录。

注意:

  • 在使用插件之前,请检查/etc/yum/pluginconf.d/downloadonly.conf以确认此插件是“enabled = 1”
  • 这仅适用于“yum install / yum update”而不适用于“yum groupinstall”。你可以使用“yum groupinfo”来确认group中的包含软件包,再用yum install下载。
  • 如果仅指定包名称,则下载最新的可用包(如sshd)。否则,您可以指定完整的软件包名称和版本(例如httpd-2.2.3-22.el5)。
  • 如果不使用–downloaddir选项,文件将默认保存在/var/cache/yum/inrhel-{arch}-channel/packages
  • 如果需要,您可以使用相同的命令下载多个软件包。

Yumdownloader

如果你想获取已安装的软件包,那么请使用yumdownloader。

安装yum-utils软件包:

# yum install yum-utils

运行命令,然后运行所需的软件包:

# yumdownloader <package>

注意:

  • 包默认直接保存在当前工作目录中; 也可以使用–destdir选项来指定一个存储位置。
  • 如果您需要下载依赖关系,请务必添加–resolve。

将CentOS升级至最新版本CentOS7.4教程

最近,最新版本的CentOS 7.4发布了。CentOS 7.0,7.1和7.2的所有用户都可以将他们的系统升级到最新版本。

本快速指南将解释您需要更新CentOS或将CentOS升级到最新版本的步骤。

使用“更新”选项,只需一个操作即可将所有CentOS系统软件升级到最新版本。

请注意,yum操作不建议使用“-y”。当然,在允许yum进行之前,您有一些时间来查看要在系统上安装的软件包,方法是使用“yum update”。

在CentOS的早期版本中,我们需要恢复所有的程序和数据,但现在使用CentOS 7,我们可以直接升级,也就是说,意想不到的情况仍然是可能的,因此升级过程之前进行数据备份。

将CentOS升级至最新版本CentOS7.4教程

1. 检查你的CentOS版本。

# cat /etc/redhat-release
CentOS Linux release 7.1.1503 (Core)

2. 备份重要数据和目录(例如:/ etc,/ var,/ opt)

我建议,对于VMware虚拟机,请采用一个好的VMware快照或运行操作系统和数据的完整备份。(MySQL,Apache,NGINX,DNS等),可以查看本站如何备份数据的教程.

3. 用yum更新升级。

# yum clean all
# yum update

4. 用下面的命令重新启动服务器。

# reboot

5. 确认您的系统已成功升级

# cat /etc/redhat-release
CentOS Linux release 7.4.1611 (Core)

我希望这篇文章为您提供一些关于如何更新CentOS或升级CentOS操作系统的想法和基本指导。

未分类

注意:检查您的系统,确保它正常运行,并验证升级之前安装的每个服务。

ansible-playbook组件解析及操作全解

一、ansible-playbook介绍

playbook是由一个或多个”play”组成的列表。play的主要功能在于将事先归为一组的主机装扮成事先通过ansible中的task定义好的角色。从根本上来将,所谓的task无法是调用ansible的一个module。将多个paly组织在一个playbook中,即可以让他们联通起来按事先编排的机制同唱一台大戏。

1、playbook基础组件

hosts playbook中的每一个paly的目的都是为了让某个或某些以某个指定用户的身份执行任务。hosts用于指定要执行指定任务的主机,其可以是一个或多个由冒号分割主机组。

user remote_user则用于指定远程主机上的执行任务的用户。

任务列表:

play的主体部分是task list. task list中的各任务按次序逐个在hosts中指定的所有主机上执行,即在所有主机上完成第一个任务后再开始第二个。

action

任务执行过程

handlers

用于当前关注的资源发生变化时采取一定指定的操作

2、实例

[root@node1 playbook]# cat web.yml
- hosts: test  \主机组,在/etc/ansible/hosts定义
  remote_user: root  \远端执行任务的用户
  tasks: \任务
  - name: install httpd  \任务描述
    command: yum -y install httpd  \调用ansible的command模块安装httpd
  - name: provide httpd.conf \任务描述
copy: src="/root/httpd.conf" dest="/etc/httpd/conf/httpd.conf" \调用ansible的copy模块,httpd安装完成后将事先准备好的httpd.conf文件复制到/etc/httpd/conf目录下
    tags: conf  \给此任务打标记,可单独执行标记的任务,使用 ansible-playbook -C 命令执行
    notify:  \文件内容变更通知
    - server restart  \通知到指定的任务
  - name: server start  \任务描述
    service: name=httpd state=started enabled=true \调用ansible的service模块的属性定义安装完成httpd以后httpd服务的管理
  handlers: \定义接受关注的资源变化后执行的动作
  - name: server restart  \任务描述
    service: name=httpd state=restarted   \当关注的资源发生变化后调用service模块,采取的响应的动作

执行过程如下:

[root@node1 playbook]# ansible-playbook web.yml 

PLAY [test] ******************************************************************* 

GATHERING FACTS *************************************************************** 
ok: [172.16.2.13]

TASK: [install httpd] ********************************************************* 
changed: [172.16.2.13]

TASK: [provide httpd.conf] **************************************************** 
changed: [172.16.2.13]

TASK: [server start] ********************************************************** 
changed: [172.16.2.13]

NOTIFIED: [server restart] **************************************************** 
changed: [172.16.2.13]

PLAY RECAP ******************************************************************** 
172.16.2.13                : ok=5    changed=4    unreachable=0    failed=0

二、ansible的roles介绍:

ansible的roles用于层次性、结构化地组织palybook。roles能够根据层次型结构自动装载变量文件、tasks及handlers等。要使用roles只需要playbook中使用include指令即可。

rules的组成:

root@node1 playbook]# tree  roles/
roles/ \ansible所有的信息都放到此目录下面对应的目录中
└── nginx  \角色名称
    ├── default  \为当前角色设定默认变量时使用此目录,应当包含一个main.yml文件;
    ├── files  \存放有copy或script等模块调用的文件
    ├── handlers \此目录总应当包含一个main.yml文件,用于定义各角色用到的各handler
    ├── meta \应当包含一个main.yml,用于定义角色的特殊设定及其依赖关系;1.3及以后版本支持
    ├── tasks \至少包含一个名为main.yml的文件,定义了此角色的任务列表,可使用include指令
    ├── templates \template模块会自动在此目录中寻找Jinja2模板文件
    └── vars  \应当包含一个main.yml文件,用于定义此角色用到的变量

roles介绍完了,那么我们就利用ansible的roles来配置nginx

1、首先按照上面的要求创建要用到的目录

[root@node1 playbook]# mkdir -pv roles/nginx/{tasks,files,templates,handlers,vars,meta,default}

2、准备nginx配置文件

准备nginx.conf配置文件,使用模板文件配置

[root@node1 playbook]# cd roles/nginx/templates/
[root@node1 ~]# ansible all -m setup | grep ansible_processor_cores
        "ansible_processor_cores": 1,  \获取ansible的要调用的相关函数
[root@node1 playbook]# cd roles/nginx/templates/   \模板文件一定要放到此目录     
[root@node1 templates]# vim nginx.conf 
worker_processes {{  ansible_processor_cores }};  \调用获取到的函数

准备nginx的default.conf文件

[root@node1 playbook]# ls -l roles/nginx/files/
-rw-r--r--. 1 root root 1290 Nov 12  2014 default.conf

3、准备nginx的rpm包

[root@node1 playbook]# ls -l  roles/nginx/files/
-rw-r--r--. 1 root root   1290 Nov 12  2014 default.conf
-rw-r--r--. 1 root root 319456 Mar 29 20:44 nginx-1.4.7-1.el6.ngx.x86_64.rpm

4、在tasks目录中配置任务列表

[root@node1 playbook]# cd  roles/nginx/tasks/
[root@node1 tasks]# vim  main.yml 
  - name: copy nginx.rpm
    copy: src=nginx-1.4.7-1.el6.ngx.x86_64.rpm  dest=/tmp/nginx-1.4.7-1.el6.ngx.x86_64.rpm
  - name: install nginx
    shell: yum -y  install /tmp/nginx-1.4.7-1.el6.ngx.x86_64.rpm
  - name: provides nginx.conf
    template: src=nginx.conf  dest=/etc/nginx/nginx.conf
    tags: nginxconf
    notify:
    - server restart
  - name: provides default.conf
    copy: src=default.conf dest=/etc/nginx/conf.d/default.conf 
    tags: nginxconf
  - name: server start
    service: name=nginx enabled=true state=started

5、在handlers目录中配置定义handler信息

[root@node1 playbook]# cd roles/nginx/handlers/
[root@node1 handlers]# vim  main.yml 
- name: server restart
  service: name=nginx  state=restarted

6、在roles同一级目录中创建site.yml文件

[root@node1 playbook]# cat site.yml 
- hosts: nginx
  remote_user: root
  roles:
  - nginx

7、应用配置

[root@node1 playbook]# ansible-playbook site.yml 

PLAY [nginx] ****************************************************************** 

GATHERING FACTS *************************************************************** 
ok: [172.16.2.13]

TASK: [nginx | copy nginx.rpm] ************************************************ 
ok: [172.16.2.13]

TASK: [nginx | install nginx] ************************************************* 
changed: [172.16.2.13]

TASK: [nginx | provides nginx.conf] ******************************************* 
changed: [172.16.2.13]

TASK: [nginx | provides default.conf] ***************************************** 
changed: [172.16.2.13]

TASK: [nginx | server start] ************************************************** 
changed: [172.16.2.13]

NOTIFIED: [nginx | server restart] ******************************************** 
changed: [172.16.2.13]

PLAY RECAP ******************************************************************** 
172.16.2.13                : ok=7    changed=5    unreachable=0    failed=0

8、在node2主机上查看nginx是否已启动

[root@node2 ~]# ss -tpln | grep 80
LISTEN     0      128                       *:80                       *:*      users:(("nginx",8934,8),("nginx",8936,8))

9、roles目录总体结构

[root@node1 playbook]# tree roles/
roles/
└── nginx
    ├── default
    ├── files
    │  ├── default.conf
    │  └── nginx-1.4.7-1.el6.ngx.x86_64.rpm
    ├── handlers
    │  └── main.yml
    ├── meta
    ├── tasks
    │  └── main.yml
    ├── templates
    │  └── nginx.conf
    └── vars

Ansible基于服务树进行分组全量接口调用

Ansible APi

说明

品茶:代码是基于我们的服务树结构进行构建,如果需要自己构建相应服务树则可以根据group host inventory进行自行构建。我们中带有中文,所以命令行模式需要2.0才可以调中文,1.9需要改代码。直接调模块不受影响。

Info

ansible2.0更贴近于ansible cli的常用命令执行方式,不同于上一版本只能发送单个命令或playbook;而更推荐用户在调用ansibleAPI的时候,将playbook的每个task拆分出来,获取每个task的结果。能够跟灵活处理在执行批量作业过程中的各种反馈。

Info 2

将执行操作的队列模型,包含各类环境参数设置,归结到“ansible.executor.task_queue_manager”类中
将执行过程中的各个task的设置,或者说playbook中的编排内容,归结到“ansible.playbook.play”中

Import Packge

from collections import namedtuple #有命元组
from ansible.parsing.dataloader import DataLoader #数据解析
from ansible.vars import VariableManager # 变量管旦
from ansible.inventory import Inventory # 主机配置信息
from ansible.playbook.play import Play # 剧本
from ansible.executor.task_queue_manager import TaskQueueManager # 任务消息队列
from ansible.plugins.callback import CallbackBase #回调

Info

  • inventory –> 由ansible.inventory模块创建,用于导入inventory文件
  • variable_manager –> 由ansible.vars模块创建,用于存储各类变量信息
  • loader –> 由ansible.parsing.dataloader模块创建,用于数据解析
  • options –> 存放各类配置信息的数据字典
  • passwords –> 登录密码,可设置加密信息
  • stdout_callback –> 回调函数

Example Code

# #coding:utf8
import json
import sys

from ansible.runner import Runner
from ansible.inventory.group import Group
from ansible.inventory.host import Host
from ansible.inventory import Inventory
from ansible import playbook
from ansible import callbacks
from ansible import utils

from cmdb import groups


class CmdbInventory(object):
    '''
    Get ansible.inventory for cmdb parse tree
    '''

    def __init__(self):

        self.cmdbs = groups()
        self.inventory = self.init_inventory()

    def init_inventory(self, inventory=Inventory(host_list=[])):
        '''

        :param inventory: default param, init cmdb Tree info.
        :return: ansible.inventory type
        '''

        for name in self.cmdbs:
            if name == "_meta": # 主机变量,暂不处理
                pass
            g = Group(name=name)

            # 设置组环境变量
            if self.cmdbs[name].get("vars", None):
                vars = self.cmdbs[name]["vars"]
                for k,v in vars.iteritems():
                    g.set_variable(k, v)

            # 添加主机进主机组
            if self.cmdbs[name].get("hosts", None):
                hosts = self.cmdbs[name]["hosts"]
                for host in hosts:
                    h = Host(name=host)
                    g.add_host(h)

            inventory.add_group(g)

        # 处理子组
        for name in self.cmdbs:
            if self.cmdbs[name].get("children", None):
                children = self.cmdbs[name]["children"]
                for child in children:
                    g = inventory.get_group(name)
                    child = inventory.get_group(child)
                    g.add_child_group(child)

        # 处理主机的环境变量
        hostvars = self.cmdbs.get("_meta",{}).get("hostvars", {})
        if hostvars:
           for host in hostvars:
               inve_host = inventory.get_host(host)
               for k, v in hostvars[host].iteritems():
                   inve_host.set_variable(k, v)

        return inventory

class Ansible(object):
    def __init__(self, transport="paramiko", module_name="ping",
                 module_args="", pattern="", remote_user="",
                 remote_pass="", play_book=False, yml_path=""):
        '''
        Run a ansible task
        :param transport: paramiko, ssh, smart
        :param module_name: ansible module name
        :param module_args: ansible module args
        :param pattern: ansible pattern
        :param remote_user: transport user
        :param remote_pass: transport password
        :return: ansible task result
        '''

        if not remote_user or not remote_pass:
            raise ValueError("Ansible class need params remote_user, remote_pass")

        if play_book:
            if not yml_path:
                raise ValueError("playbook need params yml_path")
        else:
            if not module_name or not pattern:
                raise ValueError("Ad-hoc need params module_name, pattern")

        if transport not in ("paramiko", "ssh", "smart"):
            raise ValueError("params transport not in paramiko, ssh, smart.")

        self.transport = transport
        self.module_name = module_name
        self.module_args = module_args
        self.pattern = pattern.decode("utf-8")  # 这是因为中文问题
        self.remote_user = remote_user
        self.remote_pass = remote_pass
        self.play_book = play_book
        self.yml_path = yml_path

        # A 通过解析方式(这是一种单独的方式)
        # ci = CmdbInventory()
        # inventory = ci.inventory

        # B 通过脚本方式(这是一种全量的方式,可通过修改文件名)
        self.inventory = Inventory(host_list="cmdb.py")

    def task(self):
        '''Ansible Ad-Hoc'''

        try:
            runner = Runner(
                transport=self.transport,
                module_name=self.module_name,
                module_args=self.module_args,
                pattern=self.pattern,
                forks=10,
                inventory=self.inventory,
                remote_user=self.remote_user,
                remote_pass=self.remote_pass
            )
            result =  runner.run()
            return True, result
        except Exception as e:
            return False, str(e)

    def playbook(self):
        stats = callbacks.AggregateStats()
        playbook_cb = callbacks.PlaybookCallbacks(verbose=utils.VERBOSITY)
        runner_cb = callbacks.PlaybookRunnerCallbacks(stats=stats, verbose=utils.VERBOSITY)

        # B 通过脚本方式(这是一种全量的方式,可通过修改文件名)
        inventory = Inventory(host_list="cmdb.py")

        pb = playbook.PlayBook(
            inventory=self.inventory,
            playbook=self.yml_path,
            stats=stats,
            callbacks=playbook_cb,
            runner_callbacks=runner_cb,
            check=True,
            transport=self.transport,
            remote_user=self.remote_user,
            remote_pass=self.remote_pass
        )

        result = pb.run()
        return True, result

if __name__ == "__main__":
    ansible = Ansible(remote_user="",
                      remote_pass="",
                      play_book=True,
                      yml_path="playbooks/ping.yml")
    # result = ansible.task()
    result = ansible.playbook()
    print json.dumps(result, indent=4)

gitlab基本维护和使用

基本介绍

GitLab是一个自托管的Git项目仓库,可以自己搭建个人代码管理的仓库,功能与github类似。

安装

下载 gitlab下载地址: https://about.gitlab.com/downloads/

安装依赖的包

sudo yum install curl-devel
sudo yum install expat-devel
sudo yum install gettext-devel
sudo yum install openssl-devel
sudo yum install zlib-devel
sudo yum install perl-devel
sudo yum install curl
sudo yum install openssh-server
sudo yum install openssh-clients
sudo yum install postfix
sudo yum install cronie

Ubuntu系统使用apt-get方式安装依赖包。

使用gitlab官网的脚本安装

curl -sS https://packages.gitlab.com/install/repositories/gitlab/gitlab-ce/script.rpm.sh | sudo bash

或者使用gitlab的yum安装gitlab

sudo yum install gitlab-ce

安装完毕后,使用Web登录

技术分享

进入gitlab的管理页面,进行常用的分组,工程,用户等功能点的维护。

技术分享

安装完gitlab后的运维操作

初次配置服务

sudo gitlab-ctl reconfigure

启动服务

sudo gitlab-ctl start

停止服务

sudo gitlab-ctl stop

重启服务

sudo gitlab-ctl restart

检查服务的日志信息

检查redis的日志

sudo gitlab-ctl tail redis

检查postgresql的日志

sudo gitlab-ctl tail postgresql

检查gitlab-workhorse的日志

sudo gitlab-ctl tail gitlab-workhorse

检查logrotate的日志

sudo gitlab-ctl tail logrotate

检查nginx的日志

sudo gitlab-ctl tail nginx

检查sidekiq的日志

sudo gitlab-ctl tail sidekiq

检查unicorn的日志

sudo gitlab-ctl tail unicorn   

检查服务状态

sudo gitlab-ctl status

一般服务状态显示信息

显示格式:

状态 : 进程名称:(进程ID)运行时间(秒);进程的日志服务进程和运行时间

run: gitlab-workhorse: (pid 4752) 10759s; run: log: (pid 1077) 13185s
run: logrotate: (pid 12616) 3557s; run: log: (pid 1079) 13185s
run: nginx: (pid 4764) 10758s; run: log: (pid 1076) 13185s
run: postgresql: (pid 4770) 10757s; run: log: (pid 1073) 13185s
run: redis: (pid 4778) 10757s; run: log: (pid 1072) 13185s
run: sidekiq: (pid 4782) 10756s; run: log: (pid 1075) 13185s
run: unicorn: (pid 4786) 10756s; run: log: (pid 1074) 13185s
状态   说明

run    运行状态
down   服务停止

常见的问题

页面显示500,Whoops, something went wrong on our end.
500

Whoops, something went wrong on our end.

Try refreshing the page, or going back and attempting the action again.

Please contact your GitLab administrator if this problem persists.

如何检查和定位问题?

使用命令检查所有服务的状态

sudo gitlab-ctl status

检查服务状态如下

run: gitlab-workhorse: (pid 4752) 10862s; run: log: (pid 1077) 13288s
run: logrotate: (pid 16553) 59s; run: log: (pid 1079) 13288s
run: nginx: (pid 4764) 10861s; run: log: (pid 1076) 13288s
run: postgresql: (pid 4770) 10860s; run: log: (pid 1073) 13288s
run: redis: (pid 4778) 10860s; run: log: (pid 1072) 13288s
run: sidekiq: (pid 4782) 10859s; run: log: (pid 1075) 13288s
run: unicorn: (pid 4786) 10859s; run: log: (pid 1074) 13288s

定位问题

从服务状态信息中显示数据库postgresql的状态是down,即服务停止。

检查数据库postgresql的运行日志,检查出现什么错误?

$ sudo gitlab-ctl tail postgresql
==> /var/log/gitlab/postgresql/state <==

==> /var/log/gitlab/postgresql/current <==
2017-12-21_02:49:51.42192 FATAL: terminating connection due to administrator command
2017-12-21_02:49:51.42194 FATAL: terminating connection due to administrator command
2017-12-21_02:49:51.42194 LOG: autovacuum launcher shutting down
2017-12-21_02:49:51.42287 FATAL: terminating connection due to administrator command
2017-12-21_02:49:51.42289 FATAL: terminating connection due to administrator command
2017-12-21_02:49:51.42463 LOG: shutting down
2017-12-21_02:49:51.59345 LOG: database system is shut down
2017-12-21_02:50:43.38811 LOG: database system was shut down at 2017-12-21 02:49:51 GMT
2017-12-21_02:50:43.41991 LOG: database system is ready to accept connections
2017-12-21_02:50:43.42055 LOG: autovacuum launcher started

日志显示,数据库的访问权限应该是只有用户本身有读写执行的权限,用户组和其他用户不能有权限。

修改数据库数据的权限后,检查服务运行正常。

了解了问题的定位和解决方式,其他问题也很容易在日志中发现和解决,问题可能是磁盘空间少,用户权限错误或者其他原因。

gitlab管理员密码忘记,怎么重置密码
Gitlab 修改root用户密码

使用rails工具打开终端

sudo gitlab-rails console production

查询用户的email,用户名,密码等信息,id:1 表示root账号

user = User.where(id: 1).first

重新设置密码

user.password = ‘新密码‘
user.password_confirmation = ‘新密码‘ 

保存密码

user.save!

完整的操作ruby脚本

user = User.where(id: 1).first
user.password = ‘新密码‘
user.password_confirmation = ‘新密码‘
user.save!

然后使用重置过的密码重新登录。

git 客户端下载地址:https://git-scm.com/download/win
Linux有关的可以直接yum 安装或者apt-get 安装

Centos: yum install git

Ubuntu: apt-get install git

一种 gitlab 版本管理策略

背景

  • 目前负责小组内源码的版本管理,将目前的我们的源码管理策略做一番梳理和分享,以期共同进步。

流程

  1. 先建立主分支master

  2. Owner基于master建立开发分支xxx-dev-v1,xxx-release-v1,分别是第一期开发版本,第一期发布版本。

  3. xxx-dev-v1供开发人员拉取并开发,功能点开发完成且单元测试通过后,将开发代码提交到xxx-dev-v1上。

  4. 测试人员测试xxx-dev-v1的代码,测试通过后,由本人将xxx-dev-v1的代码合并到xxx-release-v1,待发布。发布前若测试人员发现bug,则重复3、4步。

  5. 发布时,由Owner将xxx-release-v1合并到master分支,合并后master分支打tag,如master> git tag 20171220-v1.0,然后运行自动发布脚本进行发布。

  6. 项目是迭代开发,第一期完成后该进行第二期开发,命名方式相同,依次类推。

  7. 如果在开发V2的过程中,需要紧急修复V1(线上)中的漏洞,则重复3、4、5步,并在最后将master的代码合并到V2上,以保证V2的代码最新;如评估后不紧急,可放在V2中开发。

注意

  1. 务必保证master是线上最新版本。

  2. 可改进的地方:经过一段时间的策略使用,认为目前的问题在于角色分工上不够明确,具体表现在两个问题:(1)开发人员代码开发并提交dev分支后,应该提起dev到release的合并请求,由Owner审核后通过,而不是Owner发起合并(2)测试人员在测试没有bug后应提起release合并到master的请求,而不是由Owner发起合并。

使用 Docker 搭建 Gitlab + Jenkins + SonarQube 的 PHP 持续集成环境

对于开源 PHP 项目,现在比较成熟的一套持续集成方案是使用 Github + TravisCI + StyleCI + Scrutinizer + coveralls,不过这套方案如果想要用于私有项目的话就抓狂了,个个要买套餐,其中很多还不便宜。而且对于公司内使用的项目来说,内部搭建的 Gitlab 方案更为常见,对于这种情况,我们可以使用 Gitlab + Jenkins + SonarQube 来进行代替。

安装 SonarQube

$ docker pull postgres

$ docker run --name db -e POSTGRES_USER=sonar -e POSTGRES_PASSWORD=sonar -d postgres

$ docker pull sonarqube

$ docker run --name sq --link db -e SONARQUBE_JDBC_URL=jdbc:postgresql://db:5432/sonar -p 9000:9000 -d sonarqube

执行完毕上面的命令后通过浏览器进入 SonarQube,默认用户名和密码都是 admin,进去后会有一段引导,里面会让你生成一个 access token,这个后面的配置 Jenkins 时会用到。

如果没有记下来的话,可以点右上角的用户头像里面的 My Account > Security 标签中可以生成一个新的。

配置 Jenkins

Jenkins 需要在全局的 系统设置 里面添加 SonarQube Server,填下对应的访问地址和上一步获取的 access token 即可。服务器地址填写 localhost 可能会有问题,填 ip 会比较好些。

然后需要在 系统管理 的 Global Tool Configuration 菜单中配置 SonarQube Scanner 安装,这个直接选择自动安装就好了,十分方便。

这两步配好之后就到对应的项目配置中添加构建步骤,下拉选择 Execute SonarQube Scanner,然后对于 2.1 版本以上的 SonarQube Scanner 就只需要配置 Analysis properties 这一项就可以了,比较常用的参数包括 sonar.projectKey (用来确定 该项目在 SonarQube 中叫什么名字) 和 sonar.sources=(用来指定需要扫描的目录)。

配完之后选择构建即可,可以去当前构建的 Console Output 里面查看有没有报错,正常执行完成的话,在 SonarQube 项目面板中就可以看到一个新增的命名为配置的 sonar.projectKey 的 项目了。

注意点

  • SonarPHP 自定义检查规则需要用 java 来写扩展,比较新的版本内置了 psr2 的规则基本够用,内置的 Quality Profiles 是可以复制一个出来进行自定义的
  • Sonar 嗅探出的一些问题可能实际上并没有什么影响,比如变量名中含有 ‘pwd’ 等,如果原本使用方式确实合理则可适当忽略

GitLab 升级历程

在完成了GitLab的部署、汉化、备份、恢复后,就要接着考虑gitlab的升级了。接触的这段时间gitlab不断的进行迭代更新,所以如果有实用的新功能或严重的bug修复时,必然要考虑GitLab的更新。

一、下载新版本的RPM包

途径1:通过清华开源镜像站

查看清华开源镜像站,暂时还没有我需要的10.0.4的rpm包。

如果有(比如后面有了10.0.4的包),则直接获取该包。

wget https://mirrors.tuna.tsinghua.edu.cn/gitlab-ce/yum/el7/gitlab-ce-10.0.4-ce.0.el7.x86_64.rpm

途径2:从官方获取RPM包后上传到/root目录下

官方下载:https://packages.gitlab.com/gitlab/gitlab-ce/

因为要在CentOS7上更新的版本是10.0.4,故找到gitlab-ce-10.0.4-ce.0.el7.x86_64.rpm下载到本地后,通过Bitvise SSH Client工具将rpm包上传到gitlab虚拟机的/root目录下。

说明:从下载速度和方便程度来说,走清华的镜像站的方式更优,但是我2017/10/20想下载gitlab10.0.4时,发现清华的镜像站最新只有10.0.3,故采取“途径2”获取最新的安装包。

二、更新gitlab

2.1 关闭部分gitlab服务

gitlab-ctl stop unicorn
gitlab-ctl stop sidekiq
gitlab-ctl stop nginx

2.2 升级

rpm -Uvh gitlab-ce-10.0.4-ce.0.el7.x86_64.rpm

2.3 重新配置gitlab

gitlab-ctl reconfigure

2.4 重启gitlab

gitlab-ctl restart

使用管理员账户登录后可以看到gitlab的版本号已经从10.0.2升到了10.0.4。

未分类

三、更新汉化补丁

3.1 安装git

yum install -y git

3.2 克隆获取汉化版本库

下载最新的汉化包

cd
git clone https://gitlab.com/xhang/gitlab.git

如果是要下载老版本的汉化包,需要加上老版本的分支,比如今天已经是10.0.4,我依旧想下载10.0.2,可以运行下面的语句。

git clone https://gitlab.com/xhang/gitlab.git -b v10.0.2-zh

3.3 查看该汉化补丁的版本

cat gitlab/VERSION

3.4 停止gitlab服务

gitlab-ctl stop

3.5 切换到gitlab汉化包所在的目录

cd /root/gitlab

3.6 比较汉化标签和原标签,,导出patch用的diff文件到/root下

git diff v10.0.4 v10.0.4-zh > ../10.0.4-zh.diff

3.7 回到/root目录

cd

3.8 将10.0.4-zh.diff作为补丁更新到gitlab中

yum install patch -y
patch -d /opt/gitlab/embedded/service/gitlab-rails -p1 < 10.0.4-zh.diff

3.9 启动gitlab

gitlab-ctl start

3.10 重新配置gitlab

gitlab-ctl reconfigure

使用管理员账户登录后可以看到gitlab已经完成了汉化。

未分类

Debian 环境下安装配置

GitLab是一个利用Ruby on Rails开发的开源应用程序,实现一个自托管的Git项目仓库,可通过Web界面进行访问公开的或者私人项目。
它拥有与GitHub类似的功能,能够浏览源代码,管理缺陷和注释。可以管理团队对仓库的访问,它非常易于浏览提交过的版本并提供一个文件历史库。

前言

公司目前的开发架构中,使用了gitlab来统一管理上线发布代码的操作。鉴于是之前的前辈搭建的,所以打算自己手动安装一次,来熟悉相关的配置。

安装

中文版gitlab网站:https://www.gitlab.com.cn/installation/
英文版gitlab网站:https://about.gitlab.com/downloads/

选择对应的操作系统

我这里选择 Debian 9 版本,因为要在服务器上进行搭建,长期运行。综合考虑后采用 debian系统,稳定,快速。

开始安装

安装过程非常简单, 按照页面中的执行步骤,依次执行命令即可。

1. 安装配置依赖项

如想使用 Postfix 来收发邮件,在安装期间请选择’Internet Site’. 您也可以用 exim4 或者配置外部的SMTP服务,使用SMTP发送邮件。
上面的安装期间,我没有选择internet site 选择的是本地local

sudo apt-get install curl openssh-server ca-certificates postfix

2. 添加GitLab仓库,并安装到服务器上

curl -sS https://packages.gitlab.com/install/repositories/gitlab/gitlab-ee/script.deb.sh | sudo bash

安装的过程中会有个蓝色的窗口提示你输入名称,这个是gitlab的访问域名,因为默认通过nginx进行解析的时候,指向的是你配置的这个域名,并且通过80端口。安装完成后可以看nginx的conf文件。

sudo apt-get install gitlab-ce

3. 启动GitLab

这个会加载所有的配置,启动相应的服务。所以说,和网络上其他的安装教程来比,新版本安装起来实在是太爽了。

sudo gitlab-ctl reconfigure

配置

更换域名

未分类

如上图,域名被替换为域名了,但是之前是字符串git@debian…,因为在安装的时候,蓝色窗口弹出的时候直接按了回车使用了默认值。修改方法如下:

打开配置文件

vim /etc/gitlab/gitlab.rb

找到第7行的external_url将值进行修改后保存

3 ## GitLab URL
4 ##! URL on which GitLab will be reachable.
5 ##! For more details on configuring external_url see:
6 ##! https://docs.gitlab.com/omnibus/settings/configuration.html#configuring-the-external-url-for-gitlab
7 external_url 'http://192.168.186.136'

重新启动gitlab,完成域名修改。

gitlab-ctl reconfigure

添加 ssh 公钥

因为在gitlab上使用git命令进行操作,需要进行身份验证,依赖于ssh公钥,所以需要将ssh公钥加入到自己的配置文件中,

ssh-keygen

下面命令得到的内容就是公钥

cat ~/.ssh/id_rsa.pub

CentOS 7.x上gitlab搭建教程(https可用,邮件可用)

知识要求

  • nginx基础知识

搭建感想

注:以下是我搭建gitlab时的思考,需要nginx的基础知识,Docker的基础知识才容易理解,与下面的搭建过程是独立的,不感兴趣可直接略过。

其实gitlab已经搭建并用了一年多了,现在所有的项目管理都通过gitlab完成。但是一直以来都有2个问题:

  • 80端口被系统的nginx占用了,所以只能监听非80端口;
  • 443端口也被系统的nginx占用,所以也一直没增加对https的支持;

最近正在尝试对所有已有的服务Docker化,一方面想让gitlab的搭建更简单些,另一方面也把这两个问题都处理掉。

于是就做了两个Docker容器: nginx和gitlab,相当于nginx和gitlab运行在局域网的不同主机,所以端口上没冲突。nginx是对外的服务器,它做一层反向代理到gitlab就能让gitlab提供对外的服务。
然而。。。这个做法却带来了一个新问题:gitlab需要的是22,80,443端口,80与443通过反向代理解决了,22却没办法解决。因为正常来讲,宿主机的SSH肯定也在使用,所以gitlab的SSH监听端口映射到宿主机会有冲突。

当然了,解决办法还是有的,不过非常繁琐。我们做Docker的目的不就是为了降低布署难度吗?如果使用Docker的配置比在宿主机上还繁琐,那使用起来就没太大意义了。

于是gitlab就没有放在Docker中,而是直接搭在宿主机上。

搭建过程

安装

gitlab的安装是很简单的,先下载安装包:

wget https://mirrors.tuna.tsinghua.edu.cn/gitlab-ce/yum/el7/gitlab-ce-10.2.5-ce.0.el7.x86_64.rpm

安装:

rpm -Uvh gitlab-ce-10.2.5-ce.0.el7.x86_64.rpm

配置

gitlab内置了nginx的服务,所以默认会占用80和443端口。一般来说,我们做WEB开发,服务器上早就安装了nginx或者apache,把80和443端口给占了,所以需要做些修改防止冲突。

简单的修改端口是不可行的,比如80改成85,当查看gitlab项目时,下图中的项目地址会变成http://git.papamk.com:85/papamk/groupbill这样,看着就不舒服。启用https则在使用过程中则会出现其他的问题。这里不一一论述,直接说明正确的配置方法。

未分类

具体步骤:

1、首先确保你的服务器已经有运行nginx,并且该nginx监听宿主机的80和443端口。(如果你原来使用的apache,请通过nginx反向代理到apache,这样apache原来的服务仍然可用)。

2、编辑/etc/gitlab/gitlab.rb:

# 编辑对外的域名(gitlab.papamk.com请添加A记录指向本服务器的公网IP):
external_url 'http://gitlab.papamk.com/'

# 禁用`gitlab`内置的`nginx`:
nginx['enable'] = false
# 修改成与nginx运行时的用户一致
web_server['external_users'] = ['www']

修改监听方式和监听地址(如果nginx与gitlab都在宿主机上,不用改也行;如果nginx在docker中,则需要修改)

gitlab_workhorse['listen_network'] = "tcp"
# 下面的172.18.147.173为本机IP,根据实际情况修改,不能为localhost或者127.0.0.1,否则docker访问不到
gitlab_workhorse['listen_addr'] = "172.18.147.173:8181" 

最后执行下面命令让配置生效:

$gitlab-ctl reconfigure

3、配置nginx

增加gitlab.conf的配置(所有需要注意的地方都加了中文注释):

upstream gitlab-workhorse {
  server 172.18.147.173:8181; #根据实际情况修改
}

## Normal HTTP host
server {
  listen 80;
  listen [::]:80 default_server;
  server_name gitlab.papamk.com; ## 修改成自己的域名;
  server_tokens off; ## Don't show the nginx version number, a security best practice
  root /opt/gitlab/embedded/service/gitlab-rails/public;

  ## See app/controllers/application_controller.rb for headers set

  ## Individual nginx logs for this GitLab vhost
  access_log  /home/wwwlogs/gitlab_access.log; # 根据实际情况修改
  error_log   /home/wwwlogs/gitlab_error.log; # 根据实际情况修改

  location / {
    client_max_body_size 0;
    gzip off;

    ## https://github.com/gitlabhq/gitlabhq/issues/694
    ## Some requests take more than 30 seconds.
    proxy_read_timeout      300;
    proxy_connect_timeout   300;
    proxy_redirect          off;

    proxy_http_version 1.1;

    proxy_set_header    Host                $http_host;
    proxy_set_header    X-Real-IP           $remote_addr;
    proxy_set_header    X-Forwarded-For     $proxy_add_x_forwarded_for;
    proxy_set_header    X-Forwarded-Proto   $scheme;

    proxy_pass http://gitlab-workhorse;
  }
}

该配置根据官网提供的配置修改而来: https://gitlab.com/gitlab-org/gitlab-recipes/blob/master/web-server/nginx/gitlab-omnibus-nginx.conf。

重启nginx:

$sudo service nginx restart

4、登陆浏览器,这时可以看到安装成功了,如下图所示:

首次登陆会要求重置管理员密码,管理员的默认用户名为root。

未分类

https的支持

1、使用Let’s encrypt申请免费的SSL证书,该项目提供了一个叫certbot/certbot-auto的工具获取证书,执行下面命令获取工具:

$wget https://dl.eff.org/certbot-auto
$chmod +x certbot-auto

执行下面命令生成生成gitlab.papamk.com的证书,其中–agree-tos表示同意协议,–email [email protected]为自己的email。

$./certbot-auto --agree-tos --email [email protected] certonly --webroot -w /opt/gitlab/embedded/service/gitlab-rails/public/ -d gitlab.papamk.com 

执行成功后,可通过以下命令查看下证书位置,nginx需要引用这些文件。(一般位于/etc/letsencrypt/live/gitlab.papamk.com/目录)

$./certbot-auto certificates

2、修改/etc/gitlab/gitlab.rb,对外的URL改为https:

external_url 'https://gitlab.papamk.com'

执行重新配置命令以便让新配置生效:

$gitlab-ctl reconfigure

3、修改前面的nginx的gitlab.conf的配置,全部替换成下面的内容(所有需要注意的地方都加了中文注释):

upstream gitlab-workhorse {
  server 172.18.147.173:8181; #根据实际情况修改
}
## Redirects all HTTP traffic to the HTTPS host
server {
  ## Either remove "default_server" from the listen line below,
  ## or delete the /etc/nginx/sites-enabled/default file. This will cause gitlab
  ## to be served if you visit any address that your server responds to, eg.
  ## the ip address of the server (http://x.x.x.x/)
  listen 0.0.0.0:80;
  listen [::]:80 ipv6only=on default_server;
  server_name gitlab.papamk.com; ## 改成自己的域名
  server_tokens off; ## Don't show the nginx version number, a security best practice
  return 301 https://$http_host$request_uri;
  access_log  /home/wwwlogs/gitlab_access.log; # 根据实际情况修改
  error_log   /home/wwwlogs/gitlab_error.log; # 根据实际情况修改
}
## HTTPS host
server {
  listen 0.0.0.0:443 ssl;
  listen [::]:443 ipv6only=on ssl default_server;
  server_name gitlab.papamk.com; ## 改成自己的域名
  server_tokens off; ## Don't show the nginx version number, a security best practice
  root /opt/gitlab/embedded/service/gitlab-rails/public;

  ## Strong SSL Security
  ## https://raymii.org/s/tutorials/Strong_SSL_Security_On_nginx.html & https://cipherli.st/
  ssl on;
  ssl_certificate /etc/letsencrypt/live/gitlab.papamk.com/fullchain.pem;
  ssl_certificate_key /etc/letsencrypt/live/gitlab.papamk.com/privkey.pem;

  # GitLab needs backwards compatible ciphers to retain compatibility with Java IDEs
  ssl_ciphers "ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:ECDHE-RSA-DES-CBC3-SHA:AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES256-SHA:AES128-SHA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!MD5:!PSK:!RC4";
  ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
  ssl_prefer_server_ciphers on;
  ssl_session_cache shared:SSL:10m;
  ssl_session_timeout 5m;

  ##   sudo openssl dhparam -out /etc/ssl/certs/dhparam.pem 4096
  # ssl_dhparam /etc/ssl/certs/dhparam.pem;

  access_log  /home/wwwlogs/gitlab_access.log; # 根据实际情况修改
  error_log   /home/wwwlogs/gitlab_error.log; # 根据实际情况修改
  location / {
    client_max_body_size 0;
    gzip off;

    ## https://github.com/gitlabhq/gitlabhq/issues/694
    ## Some requests take more than 30 seconds.
    proxy_read_timeout      300;
    proxy_connect_timeout   300;
    proxy_redirect          off;

    proxy_http_version 1.1;

    proxy_set_header    Host                $http_host;
    proxy_set_header    X-Real-IP           $remote_addr;
    proxy_set_header    X-Forwarded-Ssl     on;
    proxy_set_header    X-Forwarded-For     $proxy_add_x_forwarded_for;
    proxy_set_header    X-Forwarded-Proto   $scheme;
    proxy_pass http://gitlab-workhorse;
  }
}

重启nginx:

$sudo service nginx restart

4、定期更新ssl证书

Let’s encrypt提供的证书,有效期为90天,到期后执行如下命令即可继续使用:

$./certbot-auto renew

将该命令加入到crontab,每小时刷新一次,就不用担心过期了。先将该命令移到系统目录下:

$sudo mv certbot-auto /usr/bin/certbot-auto

然后执行crontab -e,添加一行:

0 */1 * * *  /usr/bin/certbot-auto renew

发送邮件的支持

官网已经给出详细的配置说明和各大邮件服务提供商的示例,不再对配置做说明:
https://docs.gitlab.com/omnibus/settings/smtp.html。

我自己使用163邮箱做测试,而官网示例没有,这里给出参考配置:

gitlab_rails['gitlab_email_from'] = '[email protected]' # 替换成实际的email
gitlab_rails['smtp_enable'] = true                                                      
gitlab_rails['smtp_address'] = "smtp.163.com"                                          
gitlab_rails['smtp_port'] = 465                                                        
gitlab_rails['smtp_user_name'] = "[email protected]" # 替换成实际的email                               
gitlab_rails['smtp_password'] = "xxx"  # 替换成实际的密码                                          
#gitlab_rails['smtp_domain'] = "163.com"                                                
gitlab_rails['smtp_authentication'] = "login"                                          
gitlab_rails['smtp_enable_starttls_auto'] = true                                        
gitlab_rails['smtp_tls'] = true 

一般来说,服务提供商的SMTP邮箱都会限制每日发送次数(250封左右),所以推荐有兴趣的同学用Postfix自建邮箱服务器,或者使用mailgun这种专业的邮件服务提供商。

如果是阿里云上的服务器,作为客户端使用第三方的SMTP配置,会发现被禁止连接SMTP的25端口,需要使用465或者587。

安全相关

  • root的密码建议设置得复杂些;

  • 管理员登陆后,推荐开启注册邮箱验证, 防恶意注册,见:https://docs.gitlab.com/ce/security/user_email_confirmation.html。