Gitlab 8.x runner安装与配置

介绍

  Gitlab 8.x之后默认集成了Gitlab CI,意味着支持了持续集成相关功能。每一次集成操作都需要对应的runner来跑代码构建、测试、发布等操作。Runner实际上就是为Gitlab的持续集成指定一个环境。

安装

官方文档地址:https://docs.gitlab.com/runner/install/
  Gitlab Runner的版本需要跟Gitlab对应,这里有一个对照表。最新的版本对照表中并没有Gitlab8.X对应的Runner版本,查了一下Gitlab8.X对应的Runner版本为1.X,所以这里选择runner 1.11.2版本。

  这里运行Gitlab与Runner的环境均为CentOS,之前尝试在windows上安装runner,对接Linux上的Gitlab,发现在Gitlab runner运行的控制台出现乱码问题。

0. 准备

在opt下创建gitlab-runner目录并进入该目录,后续执行的操作与所有的资源都放在这个目录中

cd /opt
mkdir gitlab-runner
cd gitlab-runner/

1. 下载

下载安装资源到gitlab-runner目录中

sudo wget https://gitlab-ci-multi-runner-downloads.s3.amazonaws.com/v1.11.2/binaries/gitlab-ci-multi-runner-linux-386

2. 添加运行权限

sudo chmod +x gitlab-ci-multi-runner-linux-386

3. 创建用户

sudo useradd --comment 'GitLab Runner' --create-home gitlab-runner --shell /bin/bash

4. 安装

./gitlab-ci-multi-runner-linux-386 install --user=gitlab-runner --working-directory=/opt/gitlab-runner
sudo gitlab-ci-multi-runner-linux-386 start

配置

  经过上面的步骤,Runner就已经跑起来了,剩下的还需要Runner与项目对接起来。Runner的类型分为Shared, specific and group Runners。这里选择specific类型,即单独的项目使用。

  在Gitlab项目的setting-runner中,配置过程中会使用到url和token如下所示:

未分类

1.运行register命令

./gitlab-ci-multi-runner-linux-386 register

之后就按照提示就行了

2.输入url地址
3.输入token
4.输入描述,任意即可
5.输入标签,这里直接Enter跳过
6.选择Runner executor,这里选择shell

到这里就已经注册成功了,输入./gitlab-ci-multi-runner-linux-386 list就能看到上面的注册的条目。

官方文档地址:https://docs.gitlab.com/runner/register/index.html

其它

  上面两个步骤做完后,此时按理说Gitlab就能调用Runner跑持续集成了,实际当中还会碰到其它问题,整理如下。

权限问题

  如果在Gitlab的Build控制台上报无法创建文件夹、无法运行bash等,证明创建的GitLab Runner权限不够。
此时,我这里是修改GitLab Runner的权限跟root保持一致。

vim /etc/passwd

通过上面命令可以编辑用户对应的权限,我这里打开默认为gitlab-runner:x:601:601:GitLab Runner:/home/gitlab-runner:/bin/bash,权限组修改为跟root的一致gitlab-runner:x:0:0:GitLab Runner:/home/gitlab-runner:/bin/bash。(root的权限组名为0)

这里在另外一台机器上还碰到这样修改了也不好使的问题,最终gitlab-runner install的时候,直接指定为root,而不新创建用户。

环境问题

由于Runner运行需要环境支撑,比如git、node、npm等,需要在Runner所在的服务器上准备好所有的依赖。

Linux Node安装

# 下载
wget https://nodejs.org/dist/v8.11.3/node-v8.11.3-linux-x64.tar.xz
# 解压
tar -xf  node-v8.11.3-linux-x64.tar.xz
# 建立软链接,实现全局访问
ln -s /opt/gitlab-runner/node-v8.11.3-linux-x64/bin/node /usr/local/bin/node
ln -s /opt/gitlab-runner/node-v8.11.3-linux-x64/bin/npm /usr/local/bin/npm

此时,输入node -v就能看到node的版本了。

使用软连接方式可能对非root用户无效,可以转而使用配置环境变量的方式

# 修改配置文件
vim /etc/profile
#set for nodejs,新增NODE_HOME并放到PATH上
export JAVA_HOME=/opt/soft/java
export NODE_HOME=/opt/gitlab-runner/node-v8.11.3-linux-x64  
export CLASSPATH=$JAVA_HOME/lib:$JAVA_HOME/jre/lib
export PATH=$PATH:$JAVA_HOME/bin:$NODE_HOME/bin

在vim环境下点击i进入插入状态,编辑完成后按Esc键,然后输入 :wq 按回车保存退出。
备注:内外环境还需修改NPM的镜像源,比如修改为npm config set registry https://registry-npm.daojia-inc.com/

附录 部分GitLab-Runner常用命令

1.gitlab-runner帮助:gitlab-runner –help

2.gitlab-runner指定命令帮助:gitlab-runner –help

3.注册runner:gitlab-runner register

4.注销runner:gitlab-runner unregister

5.当前运行的runner:gitlab-runner list

6.启动runner:gitlab-runner start

7.停止runner:gitlab-runner stop

8.重启runner:gitlab-runner restart

9.查询runner状态:gitlab-runner status

CICD之logstash服务的Dockerfile使用Gitlab Runner打docker包

gitlab提交代码后,经gitlab Runner打docker包,推送到docker仓库,然后kubernetes选择版本更新

Dockerfile

FROM openjdk:8-jre-alpine

# ensure logstash user exists
RUN addgroup -S logstash && adduser -S -G logstash logstash

# install plugin dependencies
RUN apk add --no-cache 
# env: can't execute 'bash': No such file or directory
        bash 
        libc6-compat 
        libzmq

# grab su-exec for easy step-down from root
RUN apk add --no-cache 'su-exec>=0.2'

# https://www.elastic.co/guide/en/logstash/5.0/installing-logstash.html#_apt
# https://artifacts.elastic.co/GPG-KEY-elasticsearch
ENV LOGSTASH_PATH /usr/share/logstash/bin
ENV PATH $LOGSTASH_PATH:$PATH

# LOGSTASH_TARBALL="https://artifacts.elastic.co/downloads/logstash/logstash-5.5.0.tar.gz"

COPY logstash-5.5.0.tar.gz /logstash.tar.gz
RUN set -ex; 
    apk add --no-cache --virtual .fetch-deps 
        ca-certificates 
        gnupg 
        openssl 
        tar ; 
    dir="$(dirname "$LOGSTASH_PATH")"; 
    mkdir -p "$dir"; 
    tar -xf /logstash.tar.gz --strip-components=1 -C "$dir"; 
    rm logstash.tar.gz; 
    apk del .fetch-deps; 
    export LS_SETTINGS_DIR="$dir/config"; 
# if the "log4j2.properties" file exists (logstash 5.x), let's empty it out so we get the default: "logging only errors to the console"
    if [ -f "$LS_SETTINGS_DIR/log4j2.properties" ]; then 
        cp "$LS_SETTINGS_DIR/log4j2.properties" "$LS_SETTINGS_DIR/log4j2.properties.dist"; 
        truncate -s 0 "$LS_SETTINGS_DIR/log4j2.properties"; 
    fi; 
# set up some file permissions
    for userDir in 
        "$dir/config" 
        "$dir/data" 
    ; do 
        if [ -d "$userDir" ]; then 
            chown -R logstash:logstash "$userDir"; 
        fi; 
    done; 
    logstash --version

COPY docker-entrypoint.sh /
RUN chmod +x /docker-entrypoint.sh
COPY logstash-shipper.conf /
RUN mkdir -p /data/logs/sincedb
RUN chown logstash.logstash -R /data/logs/sincedb
WORKDIR /
ENTRYPOINT ["/docker-entrypoint.sh"]
CMD ["-f", "/logstash-shipper.conf"]

docker-entrypoint.sh

#!/bin/bash
set -e
mkdir -p /data/logs/sincedb
chown logstash.logstash -R /data/logs/sincedb

# first arg is `-f` or `--some-option`
if [ "${1#-}" != "$1" ]; then
    set -- logstash "$@"
fi

# Run as user "logstash" if the command is "logstash"
# allow the container to be started with `--user`
if [ "$1" = 'logstash' -a "$(id -u)" = '0' ]; then
    set -- su-exec logstash "$@"
fi

exec "$@"

logstash-5.5.0.tar.gz 从官方下载 https://www.elastic.co/cn/downloads/logstash

logstash-shipper.conf样例

input {
    file {
        path => [ "/data/logs/service/*/*.log"]
        type => "service"
        sincedb_path => "/data/logs/sincedb/service"
        codec => multiline {
            pattern => "^dddd-dd-dd dd:dd:dd.ddd .+"
            negate => true
            what => "previous"
            max_lines => 30
        }       
    }
    file {
        path => [ "/data/logs/web/*/access_log*.log"]
            codec => plain { format => "%{message}" }
        type => "web"
        sincedb_path => "/data/logs/sincedb/web"
    }
}
output {
    if [type] == 'service' {
        kafka {
            codec => plain { format => "%{message}" }
            bootstrap_servers => "139.219.*.*:9092"
        topic_id => "service"
        }
    }
    if [type] == 'web' {
        kafka {
                codec => plain { format => "%{message}" }
            bootstrap_servers => "139.219.*.*:9092"
        topic_id => "web"
        }
    }
}

service的日志开头是2017-12-01 12:01:01,所以pattern匹配时间,根据时间判断日志的起始点;web日志原封不动传过去,output到kafka集群,logstash-indexer从kafka获取日志后归入elasticsearch

logstash-indexer.conf示例

input {
        kafka {
                bootstrap_servers => "139.219.*.*:9092"
                topics => "service"
                type => "service"
        }
        kafka {
                bootstrap_servers =>"139.219.*.*:9092"
                topics => "web"
                type => "web"
        }
}
filter {
    if [type] != ['web'] {
        if "_grokparsefailure" in [tags] {
              drop { }
          }
        grok {
            match => {
                "message" => "%{TIMESTAMP_ISO8601:timestamp} %{GREEDYDATA}"
            }
        }
        date {
            match => ["timestamp","yyyy-MM-dd HH:mm:ss.SSS"]
            locale => "cn"
        }
    }
    if [type] == 'web' {
        if "_grokparsefailure" in [tags] {
              drop { }
          }
        grok {
                match => {
                    "message" => '%{IP} - - [%{HTTPDATE:time}] "%{WORD:methord} %{URIPATHPARAM:request} HTTP/%
{NUMBER:httpversion}" %{NUMBER:response} %{GREEDYDATA}'
                    }
            }
        date {
            match => ["time","dd/MMM/yyyy:HH:mm:ss +d+"]
            locale => "cn"
        }
    }
}
output {
        if [type] == 'service' {
                elasticsearch {
                        hosts => "172.16.1.1:9200"
                        index => "bbotte-service-%{+YYYY.MM.dd}"
                }
        }
        if [type] == 'web' {
                elasticsearch {
                        hosts => "172.16.1.1:9200"
                        index => "bbotte-web-%{+YYYY.MM.dd}"
                }
        }
}

最后就是gitlabci配置示例

# cat .gitlab-ci.yml
image: docker:latest

stages:
  - LogstashPubTest
  - LogstashPubProd

image-build-test:
  stage: LogstashPubTest
  script:
    - "current_date=`TZ='UTC-8' date +'%m%d%H%M'`"
    - "commit_sha=$CI_COMMIT_SHA"
    - "docker build -t bbotte.com:5000/logstash:$CI_COMMIT_REF_NAME-$current_date-${commit_sha:0:8} ."
    - "docker login -u admin -p 123456 bbotte.com:5000"
    - "docker push bbotte.com:5000/logstash:$CI_COMMIT_REF_NAME-$current_date-${commit_sha:0:8}"
  only:
    - test
image-build-master:
  stage: LogstashPubProd
  script:
    - "current_date=`TZ='UTC-8' date +'%m%d%H%M'`"
    - "commit_sha=$CI_COMMIT_SHA"
    - "docker build -t bbotte.com:5000/logstash:$CI_COMMIT_REF_NAME-$current_date-${commit_sha:0:8} ."
    - "docker login -u admin -p 123456 bbotte.com:5000"
    - "docker push bbotte.com:5000/logstash:$CI_COMMIT_REF_NAME-$current_date-${commit_sha:0:8}"
  only:
    - master

目录结构如下:

logstash$ ls -a
.   docker-entrypoint.sh  .git            logstash-5.5.0.tar.gz 
..  Dockerfile            .gitlab-ci.yml  logstash-shipper.conf