运维上线应用:
before:
开发同学提交代码->开发同学打包文件给运维->运维同学收到后,手动部署到服务器;
after:
开发同学提交代码->自动部署到服务器;
手动部署带来的一系列问题:
- 基于快速交付的大背景下,效率低下;
- 手动部署容易出现人为失误;
- 开发与运维频繁沟通,产生内耗;
- 运维人员必须登录服务器,出现安全问题;
1.Gitlab搭建
cat docker-compose.ymlversion: '2'services:gitlab:image: gitlab/gitlab-ce:11.3.6-ce.0restart: alwaysnetwork_mode: "bridge"environment:GITLAB_OMNIBUS_CONFIG: |external_url 'http://gitlab.monkey.com'gitlab_rails['time_zone'] = 'Asia/Shanghai'ports:- "8088:80"volumes:- ./data:/var/opt/gitlab- ./logs:/var/log/gitlab- ./config:/etc/gitlab
2.Jenkins搭建
cat docker-compose.ymlversion: '3.2'services:jenkins:image: jenkinsci/blueocean:latestrestart: alwaysprivileged: trueuser: rootcontainer_name: jenkinsnetwork_mode: "host"environment:- TZ=Asia/Shanghaivolumes:- $PWD/jenkins-data:/var/jenkins_home- /var/run/docker.sock:/var/run/docker.sockports:- "8080"- "50000"
3.ansible
docker push monkeyyoung/ansible
4.实战:
在实际运用中,Jenkinsfile文件通常提交在各个git项目的根目录中,这样会出现一个问题,当git项目多了之后(特别是微服务),开发同学提交代码时或多或少会干涉到Jenkinsfile文件。
这里我们需要把Jenkinsfile从git项目中分离出来,并将所有项目的Jenkinsfile文件统一到一个Git项目中管理。
安装插件分离插件:
Pipeline: Multibranch with defaults 
新增Multibranch Pipeline with defaults 任务:

配置git地址:
可根据设置发现分支或标签,以及过滤规则
配置默认Jenkinsfile:
这里的Script ID 是系统设置中 Managed files 中的文件ID

默认Jenkinsfile:
看过文件后需要注意,${job_name} 参数对应你的git地址中的项目名称,如果有分支也需要对应的分支名目录
#!/usr/bin/env groovyimport groovy.transform.Field@Field def job_name=""node(){// 获取当前job名称。也可以按需自定义job_name="${env.JOB_NAME}".replace('%2F', '/').split('/')job_name=job_name[0]// 自定义workspaceworkspace="workspace/${job_name}/${env.BRANCH_NAME}"ws("$workspace"){dir("pipeline"){// clone Jenkinsfile项目git url:'http://gitlab.yshome.com:8081/root/jenkinsfile_out.git'// 根据job name、构建分支,自动加载对应的Jenkinsfiledef check_groovy_file="${job_name}/${env.BRANCH_NAME}/Jenkinsfile"load "${check_groovy_file}"}}}// 该脚本的作用:clone包含所有Jenkinsfile的代码库,根据项目名称load对应的Jenkinsfile.
配置webhook:
安装:Multibranch Scan Webhook Trigger 插件
gitlab webhook:
Jenkinsfile参考:
//项目类型:WEB
pipeline {
agent any
environment {
APP_NAME = 'pro_im'
BRANCH = 'pro'
HARBOR_HOST = '******' //镜像仓库地址
DOCKER_IMAGE = 'yshome' //镜像仓库名
LOGDIR = '/opt/logs/im/'
PORT = '7015'
docker_network = 'docker-network-app'
Jenkins_workspace = '/var/jenkins_home/workspace'
Jenkins_base_workspace = '/home/docker-jenkins/jenkins-data/workspace'
CHAT_WEBHOOK_URL='https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key'
CHAT_CONTENT_TYPE='Content-Type: application/json'
CHAT_WEBHOOK_KEY='*******'
}
stages {
stage('打印环境变量') {
steps{
sh '''
printenv
'''
}
}
stage('微信开始通知') {
steps{
sh 'printenv'
sh """
curl "${CHAT_WEBHOOK_URL}=${CHAT_WEBHOOK_KEY}" \
-H "${CHAT_CONTENT_TYPE}" \
-d '{
"msgtype": "markdown",
"markdown": {
"content": "#### Jenkins构建通知:\n>项目: <font color='comment'>${APP_NAME}</font>\n>分支: <font color=#C71585>${GIT_BRANCH}</font>\n>状态: <font color='blue'>开始</font>"
}
}'
"""
}
}
stage('编译打包') {
agent {
docker {
image 'maven:3-alpine'
args '-v /root/.m2:/root/.m2'
}
}
steps {
echo '2. 代码编译打包阶段.'
sh 'mvn clean package -Dmaven.test.skip=true'
}
}
stage('构建镜像'){
agent any
steps {
withCredentials([[$class: 'UsernamePasswordMultiBinding', credentialsId:'AliHarbor', usernameVariable: 'USERNAME', passwordVariable: 'PASSWORD']]){
sh '''
cp -rf ${Jenkins_workspace}/${APP_NAME}/pipeline/${APP_NAME}/Dockerfile .
docker login ${HARBOR_HOST} -u ${USERNAME} -p ${PASSWORD}
docker build -t ${HARBOR_HOST}/${DOCKER_IMAGE}/${APP_NAME}:${TAG_NAME} .
docker push ${HARBOR_HOST}/${DOCKER_IMAGE}/${APP_NAME}:${TAG_NAME}
docker rmi ${HARBOR_HOST}/${DOCKER_IMAGE}/${APP_NAME}:${TAG_NAME}
'''
}
}
}
stage('部署'){
agent {
docker {
image 'ansible:latest'
args "-v /root/.ssh:/root/.ssh -v ${Jenkins_base_workspace}/${APP_NAME}/pipeline/${APP_NAME}/ansible/conf:/tmp "
}
}
steps {
sh '''
ansible-playbook -i ${Jenkins_workspace}/${APP_NAME}/pipeline/${APP_NAME}/ansible/hosts -e LOGDIR=${LOGDIR} -e PORT=${PORT} -e HARBOR_HOST=${HARBOR_HOST} -e DOCKER_IMAGE=${DOCKER_IMAGE} -e Tag=${TAG_NAME} -e BRANCH=${BRANCH} -e APP_NAME=${APP_NAME} -e docker_network=${docker_network} ${Jenkins_workspace}/${APP_NAME}/pipeline/${APP_NAME}/ansible/main.yml
'''
}
}
}
post {
success {
sh """
curl "${CHAT_WEBHOOK_URL}=${CHAT_WEBHOOK_KEY}" \
-H "${CHAT_CONTENT_TYPE}" \
-d '{
"msgtype": "markdown",
"markdown": {
"content": "#### Jenkins构建通知:\n>项目: <font color='comment'>${APP_NAME}</font>\n>分支: <font color=#C71585>${GIT_BRANCH}</font>\n>状态: <font color=#32CD32>成功</font>"
}
}'
"""
}
failure {
sh """
curl "${CHAT_WEBHOOK_URL}=${CHAT_WEBHOOK_KEY}" \
-H "${CHAT_CONTENT_TYPE}" \
-d '{
"msgtype": "markdown",
"markdown": {
"content": "#### Jenkins构建通知:\n>项目: <font color='comment'>${APP_NAME}</font>\n>分支: <font color=#C71585>${GIT_BRANCH}</font>\n>状态: <font color=#FF0000>失败</font>"
}
}'
"""
}
}
}
Dockerfile:
FROM telegant/oraclejdk:1.8
#FROM fansin/oraclejdk:latest
MAINTAINER MonkeyYoung <448119056@qq.com>
ENV LANG en_US.UTF-8
ENV LANGUAGE en_US:en
ENV LC_ALL en_US.UTF-8
ENV TZ=Asia/Shanghai
RUN yum -y install nmap-ncat
ADD BaseRoute/target/im.jar /opt/apps/im.jar
EXPOSE 7015/tcp
ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/opt/apps/im.jar","--spring.profiles.active=pro","--netty.port=7015"]
ansible/main.yml
---
- hosts: "{{ BRANCH }}"
remote_user: root
tasks:
- name: mkdir_dir
shell: 'mkdir -p /home/app/{{ APP_NAME }}'
- name: copy_file
template: src=/tmp/{{ item }}.j2 dest=/home/app/{{ APP_NAME }}/{{ item }}
with_items:
- docker-compose.yml
- name: stop_docker-compose
shell: 'docker-compose -f /home/app/{{ APP_NAME }}/docker-compose.yml stop'
ignore_errors: True #忽略错误
- name: test_container
shell: 'docker ps -a | grep {{ APP_NAME }}'
register: ps_result #注册变量
ignore_errors: True #忽略错误
- name: remove_docker
shell: 'docker rm {{ APP_NAME }}'
when: ps_result | succeeded
- name: start_docker-compose
shell: 'docker-compose -f /home/app/{{ APP_NAME }}/docker-compose.yml up -d'
ansible/conf/docker-compose.yml.j2
version: '3.2'
services:
{{ APP_NAME }}:
image: {{ HARBOR_HOST }}/{{ DOCKER_IMAGE }}/{{ APP_NAME }}:{{ Tag }}
ports:
- "{{ PORT }}:{{ PORT }}"
container_name: "{{ APP_NAME }}"
restart: always
networks:
- docker-network
volumes:
- /home/app/{{ APP_NAME }}/logs/:{{ LOGDIR }}
- /etc/localtime:/etc/localtime
networks:
docker-network:
external:
name: {{ docker_network }}
