基于Docker容器的,Jenkins、GitLab构建持续集成CI

** 开发者将代码提交(push)到GitLab后,GitLab通过Hook通知jenkins,jenkins自动从GitLab中获取项目最新的源码进行集成和发布。

基于Docker,创建一个私有GitLab的容器,创建一个jenkins的容器**

1. 构建私有的GitLab容器

https://about.gitlab.com/installation/#centos-7,直接安装gitlab,不借助docker

通过docker-compose的方式安装gitlab,docker-compose这个命令需要单独安装,docker-compose会对指定目录下的docker-compose.yml文件进行执行,这个文件会一次性启动所编辑的镜像,及所要用到的参数,官方提供的yml文件内容如下:

#Install docker-compose
 sudo curl -L https://github.com/docker/compose/releases/download/1.17.0/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
docker-compose --version
version: '2'

services:
  redis:
    restart: always
    image: sameersbn/redis:latest
    command:
    - --loglevel warning
    volumes:
    - /srv/docker/gitlab/redis:/var/lib/redis:Z   #文件的挂载点

  postgresql:
    restart: always
    image: sameersbn/postgresql:9.6-2
    volumes:
    - /srv/docker/gitlab/postgresql:/var/lib/postgresql:Z   #文件的挂载点
    environment:
    - DB_USER=gitlab
    - DB_PASS=password
    - DB_NAME=gitlabhq_production
    - DB_EXTENSION=pg_trgm

  gitlab:
    restart: always
    image: sameersbn/gitlab:9.1.4
    depends_on:
    - redis
    - postgresql
    ports:
    - "10080:80"
    - "10022:22"
    volumes:
    - /srv/docker/gitlab/gitlab:/home/git/data:Z  #文件的挂载点
    environment:
    - DEBUG=false

    - DB_ADAPTER=postgresql
    - DB_HOST=postgresql
    - DB_PORT=5432
    - DB_USER=gitlab
    - DB_PASS=password
    - DB_NAME=gitlabhq_production

    - REDIS_HOST=redis
    - REDIS_PORT=6379

    - TZ=Asia/Kolkata
    - GITLAB_TIMEZONE=Kolkata

    - GITLAB_HTTPS=false
    - SSL_SELF_SIGNED=false

    - GITLAB_HOST=localhost
    - GITLAB_PORT=10080
    - GITLAB_SSH_PORT=10022
    - GITLAB_RELATIVE_URL_ROOT=
    - GITLAB_SECRETS_DB_KEY_BASE=long-and-random-alphanumeric-string
    - GITLAB_SECRETS_SECRET_KEY_BASE=long-and-random-alphanumeric-string
    - GITLAB_SECRETS_OTP_KEY_BASE=long-and-random-alphanumeric-string

    - GITLAB_ROOT_PASSWORD=
    - GITLAB_ROOT_EMAIL=

    - GITLAB_NOTIFY_ON_BROKEN_BUILDS=true
    - GITLAB_NOTIFY_PUSHER=false

    - GITLAB_EMAIL=notifications@example.com
    - GITLAB_EMAIL_REPLY_TO=noreply@example.com
    - GITLAB_INCOMING_EMAIL_ADDRESS=reply@example.com

    - GITLAB_BACKUP_SCHEDULE=daily
    - GITLAB_BACKUP_TIME=01:00

    - SMTP_ENABLED=false
    - SMTP_DOMAIN=www.example.com
    - SMTP_HOST=smtp.gmail.com
    - SMTP_PORT=587
    - SMTP_USER=mailer@example.com
    - SMTP_PASS=password
    - SMTP_STARTTLS=true
    - SMTP_AUTHENTICATION=login

    - IMAP_ENABLED=false
    - IMAP_HOST=imap.gmail.com
    - IMAP_PORT=993
    - IMAP_USER=mailer@example.com
    - IMAP_PASS=password
    - IMAP_SSL=true
    - IMAP_STARTTLS=false

    - OAUTH_ENABLED=false
    - OAUTH_AUTO_SIGN_IN_WITH_PROVIDER=
    - OAUTH_ALLOW_SSO=
    - OAUTH_BLOCK_AUTO_CREATED_USERS=true
    - OAUTH_AUTO_LINK_LDAP_USER=false
    - OAUTH_AUTO_LINK_SAML_USER=false
    - OAUTH_EXTERNAL_PROVIDERS=

    - OAUTH_CAS3_LABEL=cas3
    - OAUTH_CAS3_SERVER=
    - OAUTH_CAS3_DISABLE_SSL_VERIFICATION=false
    - OAUTH_CAS3_LOGIN_URL=/cas/login
    - OAUTH_CAS3_VALIDATE_URL=/cas/p3/serviceValidate
    - OAUTH_CAS3_LOGOUT_URL=/cas/logout

    - OAUTH_GOOGLE_API_KEY=
    - OAUTH_GOOGLE_APP_SECRET=
    - OAUTH_GOOGLE_RESTRICT_DOMAIN=

    - OAUTH_FACEBOOK_API_KEY=
    - OAUTH_FACEBOOK_APP_SECRET=

    - OAUTH_TWITTER_API_KEY=
    - OAUTH_TWITTER_APP_SECRET=

    - OAUTH_GITHUB_API_KEY=
    - OAUTH_GITHUB_APP_SECRET=
    - OAUTH_GITHUB_URL=
    - OAUTH_GITHUB_VERIFY_SSL=

    - OAUTH_GITLAB_API_KEY=
    - OAUTH_GITLAB_APP_SECRET=

    - OAUTH_BITBUCKET_API_KEY=
    - OAUTH_BITBUCKET_APP_SECRET=

    - OAUTH_SAML_ASSERTION_CONSUMER_SERVICE_URL=
    - OAUTH_SAML_IDP_CERT_FINGERPRINT=
    - OAUTH_SAML_IDP_SSO_TARGET_URL=
    - OAUTH_SAML_ISSUER=
    - OAUTH_SAML_LABEL="Our SAML Provider"
    - OAUTH_SAML_NAME_IDENTIFIER_FORMAT=urn:oasis:names:tc:SAML:2.0:nameid-format:transient
    - OAUTH_SAML_GROUPS_ATTRIBUTE=
    - OAUTH_SAML_EXTERNAL_GROUPS=
    - OAUTH_SAML_ATTRIBUTE_STATEMENTS_EMAIL=
    - OAUTH_SAML_ATTRIBUTE_STATEMENTS_NAME=
    - OAUTH_SAML_ATTRIBUTE_STATEMENTS_FIRST_NAME=
    - OAUTH_SAML_ATTRIBUTE_STATEMENTS_LAST_NAME=

    - OAUTH_CROWD_SERVER_URL=
    - OAUTH_CROWD_APP_NAME=
    - OAUTH_CROWD_APP_PASSWORD=

    - OAUTH_AUTH0_CLIENT_ID=
    - OAUTH_AUTH0_CLIENT_SECRET=
    - OAUTH_AUTH0_DOMAIN=

    - OAUTH_AZURE_API_KEY=
    - OAUTH_AZURE_API_SECRET=
    - OAUTH_AZURE_TENANT_ID=

在docker-compose.yml文件所在目录下,执行docker-compose up命令(docker-compose命令需要安装),如果之前没有下载过GitLab的镜像会自动下载,如果下载过就直接启动已有的容器。

这里会在docker创建一个新的网桥,gitlab所需要的gitlab容器和redis容器及postgresql容器都在这个网桥指定的网段内,可以通过docker network ls查看.

网桥的名称通常为你yml文件所在目录名称的小写加上下划线default,例如mygitlab_default。这个很有用,因为如果你想要容器内部互联,就需要指定所要连接的容器在一个网段内,例如jenkins容器如果想内部连接gitlab容器,就得都在mygitlab_default这个网桥提供的网段内,Docker默认会为容器指定在docker0这个网段内的一个ip,通常会是172.17.. 我的gitlab默认在172.18,所以需要在接下来创建jenkins容器并启动时,指定到18这个网段内,当然你想通过宿主机的ip和端口也是可以的

执行完成之后,gitlab容器的80端口绑定在了宿主机的10080端口,同时gitlab容器的22端口(ssh)绑定在宿主机10022端口。可以通过访问localhost:10080来访问私有仓库

2. 构建Jenkins容器

Jenkins容器安装Jenkins的rpm包,Jenkins依赖 JDK,所以需要在Jenkins的容器中安装配置jdk,本人使用jdk1.8,同时需要Jenkins的容器调用maven的打包命令,所以也需要配置安装maven,本人使用maven 3.3.9。

a.通过Dockerfile构建一个jenkins容器,基于centos7的镜像。



这里不推荐将一个基础镜像一次性打包成完整的应用镜像,建议构建三层镜像模型。
所谓三层镜像就是,基础镜像,中间件镜像,应用镜像。例如我要构建一个Jenkins 的镜像,打包一个基于Centos7 和一些常用工具的镜像就是基础镜像,例如叫Centos7-base。然后这里Jenkins依赖Java和Maven,所以在基于Centos7-base的基础上安装配置java及maven后,再打包的镜像为中间件镜像,例如叫Centos7-jdk8-mvn3,这层中间件的镜像我们为来的复用会很高,以后只要有基于java1.8和maven3的,就可以直接使用这个镜像进行构建。最后我们再在Centos7-jdk8-mvn3的基础上安装配置jenkins,最后打包运用在生产环境。

b.构建基础镜像,整合jdk及mvn。配置PATH ,安装所需要的工具,Dockerfile如下:

#base on centos 7 image

FROM daocloud.io/centos:7
MAINTAINER hoewon
LABEL JDK \ 1.8

COPY jdk1.8 /usr/local/jdk1.8/  \
             maven /usr/local/maven/

RUN yum install -y openssh-server openssh-clients git 

RUN yum install -y iproute  net-tools

ENV JAVA_HOME /usr/local/jdk1.8


ENV PATH $JAVA_HOME/bin:$PATH

EXPOSE 8080


ENTRYPOINT /bin/bash
将要复制进镜像里的jdk和maven文件放到与Dockerfile相同的目录下

执行命令打包镜像

#最后 . 是当前目录的意思,不可省略,或自定义Dockerfile所在目录

docker build -t repo/name:tag  .  

#成功后查看本地镜像

docker images 

c.通过基础镜像安装jenkins,也是通过Dockerfile

#A jenkins 2.7.4 on centos 7 image
#基于刚才做好的基础镜像
FROM 127.0.0.1:5000/env/centos7-jdk8-mvn:1.3
MAINTAINER hoewon
LABEL JDK1.8 \ Jenkins-2.7.4
#复制jenkins rpm至镜像
COPY jenkins.rpm /usr/local/

#创建jenkins用户,并更改密码

RUN useradd jenkins && echo "jenkins password"  > passwd jenkins --stdin

#安装jenkins

RUN rpm -ivh /usr/local/jenkins.rpm

#更改jenkins安装文件的所有者

RUN rpm -ql jenkins | xargs chown -R jenkins:jenkins

#创建jenkins日志文件

RUN touch /usr/lib/jenkins/jenkins-logs 

#更改日志文件所有者

RUN chown jenkins:jenkins /usr/lib/jenkins/jenkins-logs

#曝光8080端口

EXPOSE 8080

#启动容器时使用jenkins用户

USER jenkins

#定义用户空间为工作空间

WORKDIR /home/jenkins

#启动容器时启动jenkins

ENTRYPOINT java -jar /usr/lib/jenkins/jenkins.war &> /usr/lib/jenkins/jenkins-logs

d.运行jenkins容器,这里需要将container的网络设置跟GtiLab同网络的环境下,同时我们希望jenkins容器在用maven打包之后,在调用docker惊醒封装镜像,然后推送镜像至私有的镜像仓库,所以我们把宿主机的/usr/bin/docker 和/run/docker.sock 和所依赖的共享库文件,在启动容器时一并挂载至容器内部。

# --net 指定docker的网桥
 docker run -idt   --net mygitlab_default --name jenkins_c -p 8888:8080 -v /usr/lib64/libltdl.so.7:/usr/lib64/libltdl.so.7  -v /var/run/docker.sock:/var/run/docker.sock -v /usr/bin/docker:/usr/bin/docker 127.0.0.1:5000/myjenkins:1.0 /bin/bash

e.容器启动后,8080端口被绑定到了宿主机的8888端口,访问localhost:8888来访问jenkins
首次登录jenkins会需要输入jenkins的超级管理员密码,这个密码在jenkins第一次启动的日志中,可以进入jenkins容器,查看运行日志,也可以根据页面提示,直接去jenkins的家目录下一个.jenkins/secrets/init…..的文件内查看密码。登录后注册一个用户,并在系统管理-管理插件下,安装GitLab的插件。第一次运行jenkins会让你选择安装插件,选择第一个推荐的安装后,在管理插件的可选插件tab下搜索Gitlab Plugin 和Gitlab Hook Plugin插件进行安装,然后重启jenkins

3.构建私有的Docker 镜像仓库
拉取一个docker 的镜像仓库镜像,并运行该容器,容器运行后会绑定宿主机5000端口,我个人用的是daocloud.io提供的镜像,这个镜像仓库需要注册。

docker run \
         -e SETTINGS_FLAVOR=s3 \
         -e AWS_BUCKET=acme-docker \
         -e STORAGE_PATH=/registry \
         -e AWS_KEY=AKIAHSHB43HS3J92MXZ \
         -e AWS_SECRET=xdDowwlK7TJajV1Y7EoOZrmuPEJlHYcNP2k4j49T \
         -e SEARCH_BACKEND=sqlalchemy \
         -p 5000:5000 \
         daocloud.io/registry

运行成功后,可以访问http://localhost:5000/v2/_catalog 来查看本地仓库信息

4.构建持续集成环境
万事具备,下面我们开始利用gitlab,jenkins,来构建持续集成环境。

a.在gitlab中创建一个项目,项目类为public,创建后会有如下提示,这些提示用来告诉你如何通过客户端下载项目,并提交项目

#Command line instructions 
#Git global setup
git config --global user.name "jenkins"
git config --global user.email "15561581025@163.com"
#Create a new repository
git clone ssh://git@localhost:10022/jenkins/demo.git
cd demo
touch README.md
git add README.md
git commit -m "add README"
git push -u origin master
#Existing folder
cd existing_folder
git init
git remote add origin ssh://git@localhost:10022/jenkins/demo.git
git add .
git commit
git push -u origin master
#Existing Git repository
cd existing_repo
git remote add origin ssh://git@localhost:10022/jenkins/demo.git
git push -u origin --all
git push -u origin --tags

b.进入jenkins容器,然后通过git客户端,拉取项目。在拉取之前,需要用使用ssh-keygen来生成当前jenkins用户在jenkins容器的公钥密钥,ssh-keygen需要安装openssh。

ssh-keygen – t rsa

可以一路回车,默认生成在jenkins用户的家目录下的一个.ssh的文件夹,将.ssh/id_rsa.pub的内容全部复制

c.登录gitlab,右上角下拉列表下选择setting,然后在主页面有一个SSH Keys的tab页,将你刚才复制的公钥内容添加至Key这个文本框,然后点击Add key。这个步骤的意思是,如果你想让jenkins通过ssh协议从gitlab拉取项目,需要认证,配置的公钥就是允许jenkins容器的jenkins用户所运行的jenkins从gitlab通过ssh协议拉取项目(有点别扭,但是需要注意的是jenkins这个容器主机和jenkins用户,jenkins程序是jenkins用户启动的)。
(这里有一个没有解决的问题,就是关于拉取private类型的项目,需要做的ssh认证,需要公钥密钥匹配,但是没有实现,所以暂时项目都是public。如果public的项目的话,可以通过http协议进行拉取,但是需不需要配置ssh的公钥,我也没试过,因该是不需要把。)

在配置好key之后,我们回到jenkins容器,当前用户为jenkins。我们想通过git客户端拉取项目的第二个准备工作,需要配置git的全局配置。

git  config --global   user.name=”yourName”

我的gitlab 和jenkins的用户都是jenkins,这里应该填写的是git用户名

git config –global user.email=”your email”

这两个配置完成后,我们可以就可以拉取项目到本地了,以我自己demo为例

git clone ssh://git@localhost:10022/jenkins/eurka.git

d.现在拉取到当前目录的eurka是个空项目,我们把我们要上传的项目源码和Dockerfile(用来jenkins调用docker打包镜像用的)添加至这个目录,然后git add ,git commit ,git push 命令将本地文件添加提交并推送整合至gitlab这个项目的某个分支下,默认是master

e.提交完成后,我们开始配置jenkins,以jenkins用户登录jenkins,新建,选择 --构建一个*风格的软件项目然后确定

f.然后进入到这个项目的配置下,在源码管理选择Git,然后在Repository URL下,填入项目的ssh地址,注意这里不可一世用localhost,你可以填写主机的ip加上10022端口,也可以配置gitlab容器的ip地址加上22端口,我这里配置的是我的容器地址,例如ssh://git@172.18.0.4:22/jenkins/eurka.git,这里不要写10022端口!!!!因为是public项目,所以不需要添加认证
g.在构建触发器下,选择Build wher a chage is pushed to GitLab……….将后面的地址复制,然后在gitlab这个项目的Setting下的Integrations下,勾选Push Event,找到Add webhook,并填写刚才复制的路径,我这里写的依然是容器ip,http://172.18.0.5:8080/project/eurka,没有使用localhost:8888这个url,保存

h.回到jenkins的设置,在构建中下Execute shell下,编写shell脚本,这里就是gitlab被push后,通过webhook的方式让jenkins自动从gitlab下拉取项目之后,要执行的代码
默认拉取之后,会在这个项目的路径下,如果不确定自己当前的工作目录,可以通过echo $WORKDIR来查看当前位置,例如我的项目叫eurka,这个项目下我上传了一个maven项目,所以我如果想要对这个maven项目打包,我得进入这个项目目录,然后对这个项目下的pom文件进行操作,贴出我的shell:

#先打印当前位置
pwd
#进入到我的maven项目
cd spring-cloud-02-service
#maven打包
mvn clean compile package -Dmaven.test.skip=true
#回到上一级,因为上一级有Dockerfile文件
cd ../
#这里我先移除掉了我之前的镜像,然后再重新构建一个镜像,如果当前有#这个镜像运行的容器,需要先停止容器,移除容器,最后移除镜像
docker rmi 127.0.0.1:5000/auto/eurka:1.0 . || docker build -t 127.0.0.1:5000/auto/eurka:1.0 .
#将打包的镜像推送到本地的docker仓库
docker push 127.0.0.1:5000/auto/eurka:1.0
#查看本地是否打包成功
docker images
#最后可以执行docker run -itd –name container_name -p xxxx:xxxx  repo/im#g:tag /bin/bash让jenkins调用宿主机的docker,运行刚才打包好的镜像,不#出意外,应该在我们指定的端口上可以访问这个项目了。

最初我们在run jenkins容器的时候,将宿主机的docker文件挂载进了容器内部,这个操作很重要,但我也很迷糊,但可以肯定的是,jenkins容器所运行的docker及操作是针对宿主机的docker的

后续的持续集成,要基于kubernetes,kubernetes会自动的找其节点自动拉取刚push的镜像,并自动部署。未完待续

上一篇:Apache和Tomcat整合(一个Apache 不同域名处理多个不同业务)


下一篇:电子合同刷脸认证安全吗?电子合同实名认证方式有哪些?