目录
- show case
- docker 和jenkins 介绍
- 使用docker部署jenkins
- jenkins 的一些常用配置
- 搭建一个自动集成环境
- 总结
docker 和jenkins 介绍
- Docker是目前最流行的虚拟化技术
- Jenkins是一个开放易用的持续集成工具
Centos 7 安装docker
# 卸载旧版本(如果安装过旧版本的话)
sudo yum remove docker docker-common docker-selinux docker-engine
# 安装需要的软件包, yum-util 提供yum-config-manager功能,另外两个是devicemapper驱动依赖的
sudo yum install -y yum-utils device-mapper-persistent-data lvm2
# 设置yum源
sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
# 可以查看所有仓库中所有docker版本,并选择特定版本安装
yum list docker-ce --showduplicates | sort -r
# 安装docker
sudo yum install docker-ce
# 启动并加入开机启动
sudo systemctl start docker
sudo systemctl enable docker
#验证安装是否成功(有client和service两部分表示docker安装启动都成功了)
docker version
安装nginx
docker pull nginx:latest # 最新版本
docker pull nginx:1.11.1 # 指定版本
通过docker images
查看当前的镜像
可以通过镜像id,删除镜像:
docker rmi 8cf1bfb43ff5
注:这里也只能删除没有创建容器的镜像,如果镜像被使用了,就不能删除
运行容器
docker run --name nginx-test -p 8080:80 -d nginx
通过docker ps
查看运行中的容器
可以看出容器id为e889f0283dea
, 然后停止容器
docker stop e889f0283dea # 停止容器
此时再通过docker ps
已经看不到运行中的容器了,但是可以通过docker ps -a
查看stop的容器。
可以访问nginx服务了:http://127.0.0.1:8080/
删除容器
docker rm e889f0283dea # 删除指定id的容器
再回顾一下前面启动容器的命令:
docker run --name nginx-test -p 8080:80 -d nginx
,我们再启动的时候指定了name参数,所以在执行命令的时候,可以用name
替代id,比如删除容器可以写为docker rm nginx-test
。启动容器的命令中还有
-d
参数代表在“后台运行”。而
-p
是容器端口映射到本机的端口的参数。
现在,如果我们想修改nginx文件,应该怎么做呢?
- 首先能想到的是进入容器
docker exec -it e889f0283dea bash
# 进入容器之后,找到nginx配置文件目录
cd /etc/nginx/conf.d/
接下来按照nginx配置修改即可。
这个方式有点不靠谱
我们用数据卷,加上-v参数
docker run --name nginx-test -p 8080:80 \
-v /home/docker-nginx/nginx.conf:/etc/nginx/nginx.conf \
-v /home/docker-nginx/log:/var/log/nginx \
-d \
nginx
这样的话, 直接可以在本地修改配置,不用进入到容器里面了,而且能做到配置持久化
。
Dockerfile
想想,如果哪天,需要对nginx 配置ssl证书,感觉需求会一直变动,为了对操作步骤自动化,引入Dockerfile
. 创建如下目录结构(可以从本项目中看到):
- config目录中包含了nginx的配置文件
- ssl目录用来保存ssl证书。
实现简单的Dockerfile文件如下:
FROM nginx:1.11.1
RUN mkdir -p /etc/nginx/ssl \
mkdir -p /var/www
COPY ./ssl/ /etc/nginx/ssl/
此镜像继承自nginx:1.11.1
, 后面可以配置一些自己的特殊需求。
build镜像
docker build -t my/nginx:v1 .
后面启动nginx,就可以直接用my/nginx:v1
镜像了。
docker-compose
如果想进一步自动化,可以通过配置docker-compose 新建一个docker-compose.yml
文件,内容如下:
version: '3'
services:
web:
build:
context: ./services/nginx
dockerfile: Dockerfile
ports:
- "8081:8081"
- "80:80"
volumes:
- ./services/nginx/config:/etc/nginx/conf.d
- ./www:/var/www
docker-compose 常用的操作
# build Dockerfile
docker-compose build
# 启动, -d代表后台启动
docker-compose up -d
# stop
docker-compose down
# 进入容器内部
docker-compose exec nginx bash
目前这些都还是单机的,如果想用集群,那么有
Kubernetes(k8s)和Docker swarm
目前k8s完全胜出。
小技巧:快速批量删除 docker 镜像或容器
Docker 本身并没有提供批量删除的功能,当有大量的镜像或者容器需要删除的时候,手动的一个一个删就比较麻烦了。
1. 直接删除所有镜像或容器
# 直接删除所有镜像
docker rmi `docker images -q`
# 直接删除所有容器
docker rm `docker ps -aq`
2. 按条件筛选之后删除
# 按条件筛选之后删除镜像
docker rmi `docker images | grep xxxxx | awk '{print $3}'`
# 按条件筛选之后删除容器
docker rm `docker ps -a | grep xxxxx | awk '{print $1}'`
使用docker部署jenkins
创建如下docker-compose.yml文件
version: '3'
services:
web:
build:
context: ./services/nginx
dockerfile: Dockerfile
ports:
- "8081:8081"
- "80:80"
volumes:
- ./services/nginx/config:/etc/nginx/conf.d
- ./www:/var/www
jenkins:
image: jenkins:latest
environment:
JAVA_OPTS: -Dorg.apache.commons.jelly.tags.fmt.timeZone=Asia/Shanghai
ports:
- "50000:50000"
- "9001:8080"
volumes:
- ./app/jenkins:/var/jenkins_home
- ~/.ssh:/var/jenkins_home/.ssh
ps: 值得注意的是environment
部分的JAVA_OPTS
配置,能解决时区问题。
配置jenkins
的nginx
server {
listen 80; # Listen on port 80 for IPv4 requests
server_name localhost;
gzip on;
gzip_min_length 1000;
gzip_types application/json text/css application/x-javascript;
keepalive_timeout 65;
client_max_body_size 2m;
#this is the jenkins web root directory (mentioned in the /etc/default/jenkins file)
# root /var/run/jenkins/war/;
# access_log /var/log/nginx/jenkins/access.log;
# error_log /var/log/nginx/jenkins/error.log;
location ~ "^/static/[0-9a-fA-F]{8}\/(.*)$" {
#rewrite all static files into requests to the root
#E.g /static/12345678/css/something.css will become /css/something.css
rewrite "^/static/[0-9a-fA-F]{8}\/(.*)" /$1 last;
}
location /userContent {
#have nginx handle all the static requests to the userContent folder files
#note : This is the $JENKINS_HOME dir
root /var/lib/jenkins/;
if (!-f $request_filename){
#this file does not exist, might be a directory or a /**view** url
rewrite (.*) /$1 last;
break;
}
sendfile on;
}
location @jenkins {
sendfile off;
proxy_pass http://jenkins:8080;
proxy_redirect default;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_max_temp_file_size 0;
#this is the maximum upload size
client_max_body_size 10m;
client_body_buffer_size 128k;
proxy_connect_timeout 90;
proxy_send_timeout 90;
proxy_read_timeout 90;
proxy_buffer_size 4k;
proxy_buffers 4 32k;
proxy_busy_buffers_size 64k;
proxy_temp_file_write_size 64k;
}
location / {
# Optional configuration to detect and redirect iPhones
if ($http_user_agent ~* '(iPhone|iPod)') {
rewrite ^/$ /view/iphone/ redirect;
}
try_files $uri @jenkins;
}
}
通过docker-compose up
启动。 然后访问http://localhost
,即可进入jenkins。
依次安装即可。 ps:安装过程可能会遇到一些问题,比如: 安装jenkins时出现 No such plugin: cloudbees-folder的解决办法
jenkins 的一些常用配置
系统设置
- 可以配置主目录
- JenkinsURL
- 邮件SMTP
- ...
权限配置
权限配置误操作了怎么办?
ps: 配置的时候需要慎重,不然误点了会导致进不了系统了。 万一真的误点了,可以参考这里 https://www.cnblogs.com/sker/p/9255368.html
当然也可以备份里面找到jenkins的config.xml 配置文件,找到如下几行,恢复重启即可。
推荐插件
- Backup plugin | 备份
- description setter plugin
- Global Variable String Parameter
- Hidden Parameter plugin
- Readonly Parameter plugin
- Mask Passwords
- Rebuilder
- ......
节点配置
目前安装完jnekins之后,此时执行任务的话都是在jenkins的docker环境下面的,想象一下我们此时需要用npm
要怎么办呢?
- 可以在shell脚本中用
docker run
命令, 用专门的npm镜像。可以参考:https://blog.csdn.net/xiaoxiangzi520/article/details/88842200/ - 也可以通过配置节点,然后将其它机器做了jenkins的节点。
这样新建的job可以使用这个节点来打包了
备份
方式很多,我采用了一个定时任务的job
https://github.com/sue445/jenkins-backup-script
可以做到每日备份。
搭建一个自动集成环境
- 创建job
- 参数
- 构建环境
- 构建
- 构建后操作
创建job
参数
构建环境
构建
构建脚本
#!/bin/bash
function func_log_print(){
local level="$1"
local msg="$2"
case $level in
error) echo -e "\033[41;30;1m${msg}\033[0m";;
warn) echo -e "\033[43;30;1m${msg}\033[0m";;
info) echo -e "\033[47;30;1m${msg}\033[0m";;
concern) echo -e "\033[42;30;1m${msg}\033[0m";;
*) echo "${msg}";;
esac
}
function check_or_exit(){
if [ $? -ne 0 ] ; then
echo "error code : $?"
echo $1
exit 127
fi
}
echo;
func_log_print warn "------------------------Build Script Start ------------------------------\n\n"
func_log_print warn "------------------------Argument parse ------------------------------\n\n"
while [[ $# > 1 ]]; do
key="$1"
case $key in
-gitrepo)
GIT_REPO="$2"
shift
;;
-tag)
GIT_TAG="$2"
shift
;;
-build_type)
BUILD_TYPE="$2"
shift
;;
*)
# unknown option
func_log_print warn "warning--->unknown option"
;;
esac
shift # past argument or value
done
if [ -z $GIT_REPO ]; then
echo "git地址不能为空"
exit 127
fi
# tag为空的时候默认master
if [ -z $GIT_TAG ]; then
GIT_TAG='master'
fi
projectName=${GIT_REPO##*/}
projectName=${projectName%%.*}
func_log_print warn "gitrepo ---> $GIT_REPO"
func_log_print warn "tag ---> $GIT_TAG"
func_log_print warn "------------------------ pull source ------------------------------\n\n"
cd ${WORKSPACE}
rm -rf $projectName # 删除项目
git clone $GIT_REPO -b ${GIT_TAG}
cd $projectName
npm install
func_log_print warn "------------------------ 生成test 报告 ------------------------------\n\n"
npm test
func_log_print warn "------------------------ 上传test 报告 ------------------------------\n\n"
mkdir ${BUILD_ID}
mv testResultsProcessorResult.html ${BUILD_ID}/testResultsProcessorResult.html
sed -i "" "s/\/Users\/gtts\/Documents\/workspace\/workspace\/jstest\/jest-html-reporter\/style/http:\/\/192.168.1.26:8000\/style/g" `grep -rl \/Users\/gtts\/Documents\/workspace\/workspace\/jstest\/jest-html-reporter ./`
sed -i "" "s/\/Users\/gtts\/Documents\/workspace\/workspace\/jstest\/jest-html-reporter\/test/https:\/\/github.com\/Hargne\/jest-html-reporter\/tree\/master\/test/g" `grep -rl \/Users\/gtts\/Documents\/workspace\/workspace\/jstest\/jest-html-reporter ./`
cp -r ${BUILD_ID} ~/Desktop/jstest
echo "report: http://192.168.1.26:8000/${BUILD_ID}/testResultsProcessorResult.html username: ${BUILD_USER} buildtype: ${BUILD_TYPE}"
func_log_print warn "------------------------ done. ------------------------------\n\n"
构建后操作
想要这样的输出效果:可以方便看测试报告,构建的用户名和构建环境
构建后操作
- 安装插件:
description setter plugin
- 构建脚本输出:
echo "report: http://192.168.1.26:8000/${BUILD_ID}/testResultsProcessorResult.html username: ${BUILD_USER} buildtype: ${BUILD_TYPE}"
-
通过正则,可以用html输出
构建后可以触发通知等其它事件
其它强调点
- job执行过程中,如果需要做一些其它审批,可以通过异步请求暂停,日中中输出一个url供用户去审批,审批完了之后再继续执行
- 定时、hooks触发
- 发短信、邮件通知结果
- 因为是本地搭建,没法使用Gitlab hooks功能,所以可以采用定时任务的策略,来定时构建
总结
- docker 和jenkins 介绍
- 使用docker部署jenkins
- jenkins 的一些常用配置
- 搭建一个自动集成环境