zshuangyan / blog

我的个人博客
2 stars 0 forks source link

持续集成开发(CI/CD):使用Jenkins, Github 和Docker #6

Open zshuangyan opened 6 years ago

zshuangyan commented 6 years ago

一个软件系统通常具有多个模块,譬如一个购物网站,它可以分为商品信息管理,购物车管理,用户登录/登出,地址管理等模块。项目经理可以把不同模块交给不同的开发人员负责,以提高项目组内部的并发程度,就像操作系统中的多线程。这样一来,项目就需要有一个master(主干)分支,每个开发人员开始他负责的模块时,就从主干分支切分出一个新的特性分支来进行开发,当开发完成后提交合入主干分支的请求,项目经理执行代码合入操作。

项目经理怎样确保合入的代码是OK的呢?他可以发动整个项目组对这个特性的代码进行一遍Code Review,然后部署服务,执行完测试用例确保没有问题后再决定合入。这样一来,每一次代码合入都会占用项目经理不少时间。那么怎样把这个过程自动化,交给机器来做呢?

Jenkins可以用来实现代码的自动构建,测试和部署。比如我们需要对一个web项目的代码更新和合入执行自动测试,那么首先需要在Jekins中创建一个项目,然后把这个项目关联到我们放在Github上代码的路径,并且配置相应的触发规则(例如接收到代码合入请求),最后配置执行的操作。配置完成后,当项目组的成员小A提交合入申请时,Github就会触发Jekins进行构建操作。

下面我们搭建一个Jenkins玩一玩吧。官网上安装Jekins很简单,首先需要安装Java8环境依赖,然后下载war后使用java -jar jenkins.war --httpPort=8080,但是因为我的服务器使用的是Java7,刚好服务器已经部署好了Docker,于是便从docker hub上拉下Jenkins镜像直接在容器中运行Jekins了,命令很简单只有两行

docker pull jekins
docker run -d -p 49001:8080 -v $PWD/jenkins:/var/jenkins_home:z -t jenkins

但是发现容器运行几秒后就挂掉了,使用docker logs命令查看日志发现上报下面的错误

touch: cannot touch '/var/jenkins_home/copy_reference_file.log': Permission denied
Can not write to /var/jenkins_home/copy_reference_file.log. Wrong volume permissions?

原因在于Jenkins的镜像文件Dockerfile中配置了uid=1000,而容器的本地数据卷中文件/目录的权限是和宿主机上一致的,因此宿主机上uid=1000的用户需要拥有对该目录控制权,把该目录的拥有者设置为uid=1000的用户即可:

sudo chown -R 1000 $PWD/jenkins

具体定位过程参考了:

通过浏览器访问http://server-ip-address:49001 ,网页显示如下:
image

我们需要从运行的输出信息中找到相应的密钥,通过docker logs我们可以看到容器运行的历史输出,找到对应的密钥后输入就可以进入下一步了,接下来就是选择安装Jekins支持的插件,我们选择jenkins推荐安装的插件,Git Plugin被包含在其中,除了这些插件外,我们还需要安装GitLab Plugin。

插件安装完成后,注册管理员账号,然后我们就进入了Jekins的管理界面: image

我们需要在jenkins中配置两个Credentials,一个是SSH Username with private key类型的,用于通过ssh连接到gitlab;另一个是Gitlab API token类型的,用于从Gitlab获取元数据。

在本地使用ssh-keygen生成一对公钥和私钥,在gitlab的个人账号下的ssh配置中添加生成的公钥,在jenkins新增Credentials关联到生成的私钥文件 image

另外我们在gitlab的个人账号下新增一个Access Token后,新增Credential,配置gitlab生成的token值 image

然后我们在系统配置的gitlab配置中,设置gitlab连接的URL和Credential image

点击新建一个项目,输入项目名称,选择自由项目类型,然后在源码管理中配置项目的源码位置,Credential选择之前配置的ssh的那个 image

我们还要在gitlab中配置一个webhook,当gitlab中有push或者merge事件时,触发jenkins构建 image

配置webhook完成后,点击Test,选择Push事件进行测试,发现返回Error 403 anonymous is missing the Job/Build Permission,我们需要进入jenkins的全局安全管理中配置匿名用户的任务构建权限,再次测试发现返回200,在jenkins上也可以看到相应的构建任务。 image

注意:docker版本的jenkins构建总是出现如下异常,但是换到本地环境就OK了。docker版的异常信息

Started by GitLab push by 张双燕
Building in workspace /var/jenkins_home/workspace/test
 > git rev-parse --is-inside-work-tree # timeout=10
Fetching changes from the remote Git repository
 > git config remote.origin.url git@gitlab.gridsum.com:data-engineering/impala-toolbox/IML-predictor.git # timeout=10
Fetching upstream changes from git@gitlab.gridsum.com:data-engineering/impala-toolbox/IML-predictor.git
 > git --version # timeout=10
using GIT_SSH to set credentials 
 > git fetch --tags --progress git@gitlab.gridsum.com:data-engineering/impala-toolbox/IML-predictor.git +refs/heads/*:refs/remotes/origin/*
ERROR: Timeout after 10 minutes
ERROR: Error fetching remote repo 'origin'
hudson.plugins.git.GitException: Failed to fetch from git@gitlab.gridsum.com:data-engineering/impala-toolbox/IML-predictor.git
    at hudson.plugins.git.GitSCM.fetchFrom(GitSCM.java:862)
    at hudson.plugins.git.GitSCM.retrieveChanges(GitSCM.java:1129)
    at hudson.plugins.git.GitSCM.checkout(GitSCM.java:1160)
    at hudson.scm.SCM.checkout(SCM.java:495)
    at hudson.model.AbstractProject.checkout(AbstractProject.java:1276)
    at hudson.model.AbstractBuild$AbstractBuildExecution.defaultCheckout(AbstractBuild.java:560)
    at jenkins.scm.SCMCheckoutStrategy.checkout(SCMCheckoutStrategy.java:86)
    at hudson.model.AbstractBuild$AbstractBuildExecution.run(AbstractBuild.java:485)
    at hudson.model.Run.execute(Run.java:1735)
    at hudson.model.FreeStyleBuild.run(FreeStyleBuild.java:43)
    at hudson.model.ResourceController.execute(ResourceController.java:97)
    at hudson.model.Executor.run(Executor.java:405)
Caused by: hudson.plugins.git.GitException: Command "git fetch --tags --progress git@gitlab.gridsum.com:data-engineering/impala-toolbox/IML-predictor.git +refs/heads/*:refs/remotes/origin/*" returned status code 128:
stdout: 
stderr: Terminated
fatal: Could not read from remote repository.

Please make sure you have the correct access rights
and the repository exists.

    at org.jenkinsci.plugins.gitclient.CliGitAPIImpl.launchCommandIn(CliGitAPIImpl.java:1996)
    at org.jenkinsci.plugins.gitclient.CliGitAPIImpl.launchCommandWithCredentials(CliGitAPIImpl.java:1715)
    at org.jenkinsci.plugins.gitclient.CliGitAPIImpl.access$300(CliGitAPIImpl.java:72)
    at org.jenkinsci.plugins.gitclient.CliGitAPIImpl$1.execute(CliGitAPIImpl.java:405)
    at hudson.plugins.git.GitSCM.fetchFrom(GitSCM.java:860)
    ... 11 more
ERROR: Error fetching remote repo 'origin'
Finished: FAILURE