基于DockerSwarm+Jenkins构建企业级自动化CI/CD平台

1.环境规划

本教程使用4台虚拟机作为服务器,系统均为centos7.x版本。

角色 IP
Docker Swarm Manager 192.168.2.129
Docker Swarm Worker 192.168.2.130
Docker Swarm Worker 192.168.2.131
Docker/Jenkins/Harbor/Gitlab 192.168.2.132

2.Docker/Docker-Compose安装(所有服务器)

2.1 卸载旧版Dokcer

sudo yum remove docker docker-common container-selinux docker-selinux docker-engine
复制代码

2.2 安装依赖包

sudo yum install -y yum-utils device-mapper-persistent-data lvm2
复制代码

2.3 添加阿里云yum源

本教程中使用阿里云docker yum源,亦可使用网易163,清华等yum源。

sudo yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
复制代码

2.4 更新并安装Docker CE

sudo yum makecache fast
sudo yum -y install docker-ce
复制代码

2.5 启动Docker

sudo service docker start
复制代码

2.6 添加阿里云镜像加速器

阿里云镜像加速地址可在此获取[cr.console.aliyun.com/cn-hangzhou…] 。xxxx.mirror.aliyncs.com换成对应加速器地址即可。

sudo mkdir -p /etc/docker

sudo tee /etc/docker/daemon.json <<-'EOF'
{
   "registry-mirrors": ["https://xxxx.mirror.aliyuncs.com"]
}
EOF
#重新加载配置并重启docker
sudo systemctl daemon-reload
sudo systemctl restart docker
复制代码

2.7 安装Docker-Compose

docker-compose最新版本见官网文档[docs.docker.com/compose/ins…], 版本中使用版本为1.25.5

sudo curl -L "https://github.com/docker/compose/releases/download/1.25.5/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose

sudo chmod +x /usr/local/bin/docker-compose
复制代码

验证是否安装成功

image.png

3.Docker Swarm集群安装

在129,130,131三台服务,其中129位manager节点,130、131为worker节点。

3.1 修改Docker启动参数

vim /lib/systemd/system/docker.service

#ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock
ExecStart=/usr/bin/dockerd  --containerd=/run/containerd/containerd.sock


vim /etc/docker/daemon.json
{
   "registry-mirrors": ["https://bvmsud8x.mirror.aliyuncs.com"],
   "hosts": ["192.168.2.129:2375","unix:///var/run/docker.sock"]
}

systemctl daemon-reload
systemctl restart docker
复制代码

3.2 安装docker swarm

3.2.1 开启相应端口(与关闭防火墙二者选其一)

# TCP端口2377集群管理端口
# TCP与 UDP端口7946节点之间通讯端口
# TCP与 UDP端口4789 overlay 网络通讯端口
firewall-cmd --zone=public --add-port=2377/tcp --permanent
firewall-cmd --zone=public --add-port=7946/tcp --permanent
firewall-cmd --zone=public --add-port=7946/udp --permanent
firewall-cmd --zone=public --add-port=4789/tcp --permanent
firewall-cmd --zone=public --add-port=4789/udp --permanent
firewall-cmd --reload

复制代码

3.2.2 manager节点

在manager节点执行即可

#192.168.2.129为当前执行机器ip 如果多网卡必填
docker swarm init --advertise-addr 192.168.2.129
复制代码

执行结果如下:
image.png

3.2.3 worker节点

在worker节点执行

manager节点执行完命令后,可以从打印出的信息看到如下命令即可添加worker节点

docker swarm join --token SWMTKN-1-0a3ulzft9wrjbf8to4akangmajva2qptkrilmrofh8c35k3roh-ebei8ptaulxr88vnls5iq4qlc 192.168.2.129:2377
复制代码

image.png

3.2.4 验证是否成功

docker node ls #查看节点信息
复制代码

image.png

4.Docker私有仓库Harbor安装

harbor下载地址[github.com/goharbor/ha…]

本文中使用2.0.0离线版本。

4.1 下载安装包,解压

wget  https://github.com/goharbor/harbor/releases/download/v2.0.0/harbor-offline-installer-v2.0.0.tgz

tar -zvxf hharbor-offline-installer-v2.0.0.tgz
复制代码

4.2 安装harbor

修改harbor.yml文件

hostname: 192.168.2.132 #安装机器ip地址
#https:
#  # https port for harbor, default is 443
#  port: 443
#  # The path of cert and key files for nginx
#  certificate: /your/certificate/path
#  private_key: /your/private/key/path
复制代码

启动

#加载harbor配置
./prepare
#安装harbor
./install.sh
docker-compose up -d 启动
docker-compose stop 停止
docker-compose restart 重新启动
复制代码

image.png

4.3 验证安装

默认用户名密码 admin Harbor12345

image.png

4.4 docker配置私服地址

修改daemon.json,增加Docker私服ip配置。

vim /etc/docker/daemon.json

"insecure-registries":["192.168.2.132"]
复制代码

image.png

5.基于Docker的Jenkins安装

5.1 自定义Jenkins Docker镜像

5.1.1 定义Dockerfile,并制作镜像

可按需在镜像中增加所需环境。

FROM jenkins/jenkins:lts
MAINTAINER Trazen <trazen@126.com>
USER root
RUN echo '' > /etc/apt/sources.list.d/jessie-backports.list \
  && echo "deb http://mirrors.aliyun.com/debian jessie main contrib non-free" > /etc/apt/sources.list \
  && echo "deb http://mirrors.aliyun.com/debian jessie-updates main contrib non-free" >> /etc/apt/sources.list \
  && echo "deb http://mirrors.aliyun.com/debian-security jessie/updates main contrib non-free" >> /etc/apt/sources.list
#更新源并安装缺少的包
RUN apt-get update && apt-get install -y libltdl7
ARG dockerGid=999
RUN echo "docker:x:${dockerGid}:jenkins" >> /etc/group USER jenkins
RUN /bin/cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && echo 'Asia/Shanghai' >/etc/timezone
复制代码
#构建docker镜像
docker build -t trazen/jenkins:lts .
复制代码

5.1.2 定义docker-compose.yml

本教程中jenkins构建所需jdk,maven等环境直接挂载宿主机现有环境目录。亦可在容器中安装相应功能使用。

version: '3'
services:
  jenkins:
    image: 'trazen/jenkins:lts'
    container_name: jenkins
    restart: always
    ports:
      - '8080:8080'
      - '50000:50000'
    volumes:
      - '/home/jenkins/data:/var/jenkins_home' #宿主机jenkins数据目录挂载
      - '/usr/local/bin/docker-compose:/usr/local/bin/docker-compose' #宿主机docker-compose挂载
      - '/usr/bin/docker:/usr/bin/docker' #解决docker in docker
      - '/var/run/docker.sock:/var/run/docker.sock'
      - '/etc/localtime:/etc/localtime:ro'
      - '/opt/jdk1.8.0_161:/var/local/jdk1.8.0_161' #宿主机jdk挂载
      - '/opt/jdk1.8.0_161/bin/java:/usr/bin/jdk1.8.0_161/java'
      - '/usr/bin/mv:/usr/bin/mv'
      - '/opt/apache-maven-3.6.3:/var/local/apache-maven-3.6.3' #宿主机maven挂载
      - '/opt/apache-maven-3.6.3/conf/settings.xml:/var/jenkins_home/.m2/settings.xml'
      - '/opt/repository:/var/local/repository'
复制代码

5.1.3 定义启动脚本start_jenkins.sh

#!/bin/bash
#创建数据目录
mkdir -p /home/jenkins/data
chown -R 1000:1000 /home/jenkins/data
#启动jenkins
docker-compose up -d
复制代码

5.1.4 Jenkins插件安装

名称 说明
Blue Ocean PipelineUI
DingTalk
Docker Swarm Plugin Docker swarm
Folders Plugin
GitLab Plugin
Localization: Chinese (Simplified)
Pipeline Maven Integration
Pipeline
Groovy Postbuild
Pipeline Utility Steps pipelinemaven pom
Pipeline: Shared Groovy Libraries groovy library
Git Parameter git
Dashboard View
Generic Webhook Trigger Plugin
Gitlab Hook Plugin Gitlab Hook
Matrix Authorization Strategy Plugin
SSH Agent Plugin
SSH Credentials Plugin
SSH Pipeline Steps
Workspace Cleanup Plugin
Pipeline Utility Steps

5.2 制作jenkins slave镜像

5.2.1 定义jenkins slave Dockfile文件

次镜像主要为构建java语言maven项目环境,可自定义在Dockerfile中安装其他编译环境

FROM openjdk:8-jdk
LABEL MAINTAINER="Trazen <trazen@126.com>"

#####################################################
# arguments
ARG user=jenkins
ARG group=jenkins
ARG uid=1000
ARG gid=1000
ARG JENKINS_AGENT_HOME=/home/${user}
ARG JNLP_SLAVE_VERSION=3.28

#####################################################
# environments
ENV DEBIAN_FRONTEND noninteractive
ENV JENKINS_AGENT_HOME ${JENKINS_AGENT_HOME}
ENV JNLP_SLAVE_VERSION ${JNLP_SLAVE_VERSION}
ENV DOCKER_VERSION 18.09.0
ENV DOCKER_CHANNEL stable
ENV DOCKER_ARCH x86_64
ENV DIND_COMMIT 52379fa76dee07ca038624d639d9e14f4fb719ff

#####################################################
# create jenkins user
RUN groupadd -g ${gid} ${group} \
    && useradd -d "${JENKINS_AGENT_HOME}" -u "${uid}" -g "${gid}" -m -s /bin/bash "${user}"
RUN mkdir ${JENKINS_AGENT_HOME}/.jenkins


#####################################################
# 更新阿里云的stretch版本包源
RUN mv /etc/apt/sources.list /etc/apt/sources.list.bak && \
    echo "deb http://mirrors.163.com/debian/ buster main non-free contrib" > /etc/apt/sources.list && \
    echo "deb http://mirrors.163.com/debian/ buster-updates main non-free contrib" >> /etc/apt/sources.list  && \
    echo "deb http://mirrors.163.com/debian/ buster-backports main non-free contrib" >> /etc/apt/sources.list && \
    echo "deb-src http://mirrors.163.com/debian/ buster main non-free contrib" >> /etc/apt/sources.list && \
    echo "deb-src http://mirrors.163.com/debian/ buster-updates main non-free contrib" >> /etc/apt/sources.list && \
    echo "deb-src http://mirrors.163.com/debian/ buster-backports main non-free contrib" >> /etc/apt/sources.list && \
	echo "deb http://mirrors.163.com/debian-security/ buster/updates main non-free contrib" >> /etc/apt/sources.list  && \
	echo "deb-src http://mirrors.163.com/debian-security/ buster/updates main non-free contrib" >> /etc/apt/sources.list


#####################################################
# install required packages
# - build-essential: provide make and gcc which will be used in "npm install"
# - sshpass: allow ssh to other servers
# - bzip2: used by installing firefox
# - gnome-keyring: required by keytar
# - libsecret-1-dev: required by npm install rebuild keytar
# - dbus-x11: includes dbus-launch
# - libdbus-glib-1-2: used by firefox
# - libx11-dev libxkbfile-dev: required by theia
# - xvfb: required by cypress
# - iptables: required by docker
RUN apt-get update && apt-get install --no-install-recommends -y \
    openssh-server \
    vim curl wget rsync pax build-essential sshpass  zip jq locales \
    iptables

#####################################################
# configure locale
RUN echo "en_US.UTF-8 UTF-8" >> /etc/locale.gen \
    && /usr/sbin/locale-gen

#####################################################
# install jnlp slave jar
RUN curl --create-dirs -fsSLo /usr/share/jenkins/slave.jar https://repo.jenkins-ci.org/public/org/jenkins-ci/main/remoting/${JNLP_SLAVE_VERSION}/remoting-${JNLP_SLAVE_VERSION}.jar \
  && chmod 755 /usr/share/jenkins \
  && chmod 644 /usr/share/jenkins/slave.jar


#####################################################
# install docker
RUN set -eux; \
  groupadd docker; \
  useradd -g docker docker; \
  usermod -aG docker ${user}; \
  if ! wget -O docker.tgz "http://192.168.29.188:8888/linux/static/${DOCKER_CHANNEL}/${DOCKER_ARCH}/docker-${DOCKER_VERSION}.tgz"; then \
    echo >&2 "error: failed to download 'docker-${DOCKER_VERSION}' from '${DOCKER_CHANNEL}' for '${DOCKER_ARCH}'"; \
    exit 1; \
  fi; \
  \
  tar --extract \
    --file docker.tgz \
    --strip-components 1 \
    --directory /usr/local/bin/ \
  ; \
  rm docker.tgz; \
  \
  dockerd --version; \
  docker --version; \
  wget -O /usr/local/bin/dind "https://raw.githubusercontent.com/docker/docker/${DIND_COMMIT}/hack/dind"; \
  chmod +x /usr/local/bin/dind

#####################################################
# Clean apt cache
RUN rm -rf /var/lib/apt/lists/*

#####################################################
# the new "openjdk:8-jdk" base image put Java in "/usr/local/openjdk-8" folder
# we need a symlink
RUN mkdir -p /usr/java \
  && ln -s /usr/local/openjdk-8 /usr/java/openjdk-8 \
  && ln -s /usr/local/openjdk-8 /usr/java/default


###########
# Maven3.6.3
###########
ENV MAVEN_VERSION=3.6.3
RUN curl -fsSL http://192.168.29.188:8888/apache-maven-3.6.3-bin.tar.gz | tar xzf - -C /usr/share \
    && mv /usr/share/apache-maven-$MAVEN_VERSION /usr/share/maven \
    && ln -s /usr/share/maven/bin/mvn /usr/bin/mvn
ENV MAVEN_HOME=/usr/share/maven

###########
# Docker Compose
###########
RUN	curl -L http://192.168.29.188:8888/docker-compose-Linux-x86_64 -o /usr/local/bin/docker-compose && \
    chmod +x /usr/local/bin/docker-compose && \
    rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*


#####################################################
# define volume
VOLUME "${JENKINS_AGENT_HOME}" "/tmp" "/run" "/var/run"
WORKDIR "${JENKINS_AGENT_HOME}"
# switch back to root user
USER root

#####################################################
# expose and entrypoint
EXPOSE 22
COPY setup-entrypoint /usr/local/bin/setup-entrypoint
RUN chmod +x /usr/local/bin/setup-entrypoint
ENTRYPOINT ["/usr/local/bin/setup-entrypoint"]
复制代码

5.2.2 定义容器脚本setup-entrypoint

#!/bin/bash

set -ex

################################################################################
# Use for testing (not as Jenkins agent):
#  docker run <image/id> <command>
# Use as SSH agent:
#  docker run <image/id> [<public key>]
# Or provide in environment variables :
# * JENKINS_SLAVE_SSH_PUBKEY : SSH public key
#
# Use as regular JNLP agent:
#  docker run <image/id> [options] -url http://jenkins [SECRET] [AGENT_NAME]
# Optional environment variables :
# * JENKINS_TUNNEL : HOST:PORT for a tunnel to route TCP traffic to jenkins host, when jenkins can't be directly accessed over network
# * JENKINS_URL : alternate jenkins URL
# * JENKINS_SECRET : agent secret, if not set as an argument
# * JENKINS_AGENT_NAME : agent name, if not set as an argument
# * JENKINS_AGENT_WORKDIR : agent work directory, if not set by optional parameter -workDir
#
# Use as JNLP agent run in Docker Swarm:
#  docker run <image/id>
# Required environment variables :
# * DOCKER_SWARM_PLUGIN_JENKINS_AGENT_SECRET : jenkins secret if run as swarm agent
# * DOCKER_SWARM_PLUGIN_JENKINS_AGENT_JAR_URL : jenkins url if run as swarm agent
# * DOCKER_SWARM_PLUGIN_JENKINS_AGENT_JNLP_URL : jenkins jnlp url if run as swarm agent

write_key() {
  mkdir -p "${JENKINS_AGENT_HOME}/.ssh"
  echo "$1" > "${JENKINS_AGENT_HOME}/.ssh/authorized_keys"
  chown -Rf jenkins:jenkins "${JENKINS_AGENT_HOME}/.ssh"
  chmod 0700 -R "${JENKINS_AGENT_HOME}/.ssh"
}

echo "============ Environment Variables Start ===================================="
env
echo "============ Environment Variables End ======================================"

################################################################################
# start docker-in-docker
exec "$(which dind)" dockerd \
      --host=unix:///var/run/docker.sock \
      --host=tcp://0.0.0.0:2375 &

################################################################################
# determine which mode to run, default is jnlp
START_MODE=JNLP
if [ -n "$DOCKER_SWARM_PLUGIN_JENKINS_AGENT_JNLP_URL" ]; then
  START_MODE=SWARM
elif [ -n "$JENKINS_SLAVE_SSH_PUBKEY" ]; then
  START_MODE=SSH
elif [ $# -gt 0 ]; then
  echo "============ Command Line Start ===================================="
  echo $@
  echo "============ Command Line End ======================================"
  if [[ $1 == ssh-* ]]; then
    # only one parameter and it starts with "ssh-", so it's a SSH public key
    START_MODE=SSH
  elif [[ $1 == "/usr/sbin/sshd" ]]; then
    # docker plugin may start container with this command:
    # /usr/sbin/sshd -D -p 22 -o AuthorizedKeysCommand=/root/authorized_key -o AuthorizedKeysCommandUser=root
    START_MODE=SSH
  elif [[ $1 == "sh" ]]; then
    # if `docker run` only has one arguments, we assume user is running alternate
    # command like `bash` to inspect the image
    START_MODE=COMMAND
  fi
fi

echo ">>>>>> Docker container will be started in $START_MODE mode. <<<<<<"

################################################################################
# start mode: command
#    user run own command like bash
if [ "$START_MODE" = "COMMAND" ]; then
  exec "$@"

################################################################################
# start mode: jnlp
elif [ "$START_MODE" = "JNLP" ]; then
  # if -tunnel is not provided, try env vars
  case "$@" in
    *"-tunnel "*) ;;
    *)
    if [ ! -z "$JENKINS_TUNNEL" ]; then
      TUNNEL="-tunnel $JENKINS_TUNNEL"
    fi ;;
  esac

  # if -workDir is not provided, try env vars
  if [ ! -z "$JENKINS_AGENT_WORKDIR" ]; then
    case "$@" in
      *"-workDir"*) echo "Warning: Work directory is defined twice in command-line arguments and the environment variable" ;;
      *)
      WORKDIR="-workDir $JENKINS_AGENT_WORKDIR" ;;
    esac
  fi

  if [ -n "$JENKINS_URL" ]; then
    URL="-url $JENKINS_URL"
  fi

  if [ -n "$JENKINS_NAME" ]; then
    JENKINS_AGENT_NAME="$JENKINS_NAME"
  fi  

  if [ -z "$JNLP_PROTOCOL_OPTS" ]; then
    echo "Warning: JnlpProtocol3 is disabled by default, use JNLP_PROTOCOL_OPTS to alter the behavior"
    JNLP_PROTOCOL_OPTS="-Dorg.jenkinsci.remoting.engine.JnlpProtocol3.disabled=true"
  fi
  
  # if java home is defined, use it
  JAVA_BIN="java"
  if [ "$JAVA_HOME" ]; then
    JAVA_BIN="$JAVA_HOME/bin/java"
  fi

  # if both required options are defined, do not pass the parameters
  OPT_JENKINS_SECRET=""
  if [ -n "$JENKINS_SECRET" ]; then
    case "$@" in
      *"${JENKINS_SECRET}"*) echo "Warning: SECRET is defined twice in command-line arguments and the environment variable" ;;
      *)
      OPT_JENKINS_SECRET="${JENKINS_SECRET}" ;;
    esac
  fi
  
  OPT_JENKINS_AGENT_NAME=""
  if [ -n "$JENKINS_AGENT_NAME" ]; then
    case "$@" in
      *"${JENKINS_AGENT_NAME}"*) echo "Warning: AGENT_NAME is defined twice in command-line arguments and the environment variable" ;;
      *)
      OPT_JENKINS_AGENT_NAME="${JENKINS_AGENT_NAME}" ;;
    esac
  fi

  #TODO: Handle the case when the command-line and Environment variable contain different values.
  #It is fine it blows up for now since it should lead to an error anyway.

  exec $JAVA_BIN $JAVA_OPTS $JNLP_PROTOCOL_OPTS -cp /usr/share/jenkins/slave.jar hudson.remoting.jnlp.Main -headless $TUNNEL $URL $WORKDIR $OPT_JENKINS_SECRET $OPT_JENKINS_AGENT_NAME "$@"

################################################################################
# start mode: jnlp in swarm
elif [ "$START_MODE" = "SWARM" ]; then
  # this container is started with Jenkins Docker Swarm Plugin, need to start slave
  curl --connect-timeout 20 --max-time 60 -o slave.jar $DOCKER_SWARM_PLUGIN_JENKINS_AGENT_JAR_URL && \
    exec java -jar slave.jar -jnlpUrl $DOCKER_SWARM_PLUGIN_JENKINS_AGENT_JNLP_URL \
      -secret $DOCKER_SWARM_PLUGIN_JENKINS_AGENT_SECRET \
      -noReconnect -workDir $JENKINS_AGENT_HOME

################################################################################
# start mode: ssh
elif [ "$START_MODE" = "SSH" ]; then
  # this container is started with ssh?
  if [[ $JENKINS_SLAVE_SSH_PUBKEY == ssh-* ]]; then
    write_key "${JENKINS_SLAVE_SSH_PUBKEY}"
  fi
  if [[ $# -gt 0 ]]; then
    if [[ $1 == ssh-* ]]; then
      write_key "$1"
      shift 1
    else
      exec "$@"
    fi
  fi

  # ensure variables passed to docker container are also exposed to ssh sessions
  env | grep _ >> /etc/environment

  # start SSHd
  ssh-keygen -A
  exec /usr/sbin/sshd -D -e "${@}"
fi
复制代码
#构建docker镜像
docker build -t trazen/jenkins_slave_java .
复制代码

5.3 Jenkins与Docker Swarm的集成

Jenkins与Docker Swarm的集成依赖依赖于Jenkins Docker Swarm Plugin插件。
下图为本文中架构图,Jenkins Master单独安装在一台Docker环境中,亦可安装在Docker Swarm【需要注意的是Jenkins Master需要固定安装到所有Swarm Manager节点】,Docker Swarm中为Jenkins动态Slave节点。相比较于传统的Jenkins Master Slave架构,在节省服务器资源的同时也更加充分利用闲置资源。

image.png

5.3.1 集成配置

image.png

image.png

image.png

image.png

image.png

image.png

5.4.2 基于流水线的动态Jenkins Slave测试

新建一个流水线任务,pileline脚本如下:

pipeline {
   agent {
       label 'jnlp-slave-java'
   }
  
   stages {
      stage('Hello') {
         steps {
            echo 'Hello World'
            sh 'mvn -v'
            sh 'docker images'
            sleep 20
         }
      }
      
   }
  
}
复制代码

image.png

image.png

6.基于流水线发布SpringBoot项目到Docker Swarm集群

6.1 SpringBoot演示项目

image.png
DemoController.java

@RestController
public class DemoController {

    @GetMapping("/demo")
    public Object test(){
        return "springboot demo!";
    }
}

复制代码

Dockerfile

FROM openjdk:8-jre
MAINTAINER Trazen<trazen@126.com>
#设置时区
ENV TZ=Asia/Shanghai
RUN cp /usr/share/zoneinfo/$TZ /etc/localtime \
    && echo $TZ > /etc/timezone
COPY target/*.jar /app.jar
EXPOSE 18080
#jvm参数
ENV JAVA_OPTS="-Xms256m -Xmx2g"
ENTRYPOINT [ "sh", "-c", "java $JAVA_OPTS -Djava.security.egd=file:/dev/./urandom -jar /app.jar" ]
复制代码

docker-compose.yml

version: "3.8"
services:
  springboot-demo:
    image: "${DOCKER_IMAGE_URL}"
    ports:
     - "18080:18080"
    volumes:
      - /opt/logs:/opt/logs
    environment:
      - TZ=Asia/Shanghai
    deploy:
      mode: replicated
      #mode为replicated时,指定容器副本的数量
      replicas: 1
      #该服务分配了一个虚拟 IP(VIP)
      endpoint_mode: vip
      restart_policy:
        #失败重启
        condition: on-failure
        #资源限制
      resources:
        limits:
          #设置该容器最多只能使用 20% 的 CPU
          cpus: "0.2"
          #设置该容器最多只能使用 2M 的内存空间
          memory: "2G"
    networks:
      - hxcloud_network
networks:
  hxcloud_network:
    external: true
    name: hxcloud_network
复制代码

6.2 创建流水线任务

如下步骤新建流水线任务
image.png

image.png
流水线脚本:

def remote = [:]
remote.name = "root"
remote.host = "192.168.2.129"
remote.allowAnyHosts = true
pipeline {
    agent any
    environment {
          //git地址
          REPOSITORY="http://192.168.2.132:8090/trazen/springboot-demo.git"
          //harbor仓库地址
          HARBOR_URL="192.168.2.132:8081"
          //镜像namespace
          DOCKER_NAMESPACE="dev"
          //镜像名称
          DOCKER_NAME="springboot-demo"
          //打包环境
          PROFILE="dev"
          //swarm管理节点shell存放目录
          SWARM_SHELL_FOLDER="/home/shell"
    }
    options {
        //一时间最多只允许一个pipeline运行
        disableConcurrentBuilds()
        //保留最新的7个build log和1个artifact
        buildDiscarder(logRotator(numToKeepStr: '7',artifactNumToKeepStr: '1',daysToKeepStr: '5'))
    }
    parameters {
            gitParameter name: 'BRANCH_TAG',
                         type: 'PT_BRANCH_TAG',
                         branchFilter: 'origin/(release.*)',
                         defaultValue: 'master',
                         selectedValue: 'DEFAULT',
                         sortMode: 'ASCENDING_SMART',
    					 description: 'Select your branch or tag.'
    }
    stages {
      stage('获取代码') {
          steps {
             echo "start fetch code from ${REPOSITORY}"
             checkout([$class: 'GitSCM',
                  branches: [[name: "${params.BRANCH_TAG}"]],
                  doGenerateSubmoduleConfigurations: false,
                  extensions: [],
                  gitTool: 'Default',
                  submoduleCfg: [],
                  userRemoteConfigs: [[url: "${REPOSITORY}",credentialsId: 'gitlab-trazen',]]
                ])
          }
       }
       stage('Maven构建') {
           steps {
                     withMaven(maven : 'maven3.6.3'){
                         script {
                           sh 'mvn clean  install -e -U -Dmaven.test.skip=true  -P${PROFILE}'
                           
						   def pom = readMavenPom file:'pom.xml'
						   print pom.version
						   env.version = pom.version
                           
                         }
                     }
                 }

       }
       stage('构建&发布Docker镜像') {
           steps {
             script {
                echo "start build docker image ${HARBOR_URL}"
                docker.withRegistry('http://${HARBOR_URL}', 'harbor-tuyong'){
                                    
									def imageUrl = "${DOCKER_NAMESPACE}/${DOCKER_NAME}:${env.version}"
									def customImage = docker.build(imageUrl);
									customImage.push()
                                    
                              }
               }

           }
       }
       stage ('发布到Docker Swarm') {
           steps {
                    echo "run in Docker Swarm"
                    withCredentials([sshUserPrivateKey(credentialsId: 'ssh-129',keyFileVariable: 'identity',passphraseVariable: '',usernameVariable: 'userName')]){
                        script {
                                remote.user = userName
                                remote.identityFile = identity
                                try{
                                    sshCommand remote: remote, command: "sudo mkdir ${SWARM_SHELL_FOLDER}/${DOCKER_NAME}-${PROFILE}"
                                    sshPut remote: remote, from: "docker-compose.yml", into: "${SWARM_SHELL_FOLDER}/${DOCKER_NAME}-${PROFILE}"
                                    def IMAGE_URL = "${HARBOR_URL}/${DOCKER_NAMESPACE}/${DOCKER_NAME}:${env.version}"
                                    def COMPOSE_FOLDER = "/${DOCKER_NAME}-${PROFILE}"
                                    echo "开始部署应用--服务名称:${DOCKER_NAME}  镜像地址:${IMAGE_URL}"
                                    sshCommand remote: remote, command: "${SWARM_SHELL_FOLDER}/deploy-swarm.sh ${DOCKER_NAME} ${IMAGE_URL} ${COMPOSE_FOLDER}"
                                }
                                catch (exc) {
                                    echo '发布到Docker Swarm失败!'
                                    throw exc
                                }
                                finally{
                                    sshCommand remote: remote, command: "sudo rm -rf ${SWARM_SHELL_FOLDER}/${DOCKER_NAME}-${PROFILE}"
                                }

                         }

                  }
           }
       }
   }

   post {
      failure {
        echo '构建失败'
        script{
          log_url=env.JENKINS_URL+"blue/organizations/jenkins/"+env.JOB_NAME+"/detail/"+env.JOB_NAME+"/"+env.BUILD_NUMBER+"/pipeline#"
        }
        echo "${log_url}"
		    dingtalk (
                  robot: '43f773e2-a2bc-4fdd-80b0-e8c58f552d62',
                  type: 'LINK',
                  title: '构建失败❌',
                  text: [
                    "项目信息:${currentBuild.fullDisplayName} ",
                    "构建结果:${currentBuild.result}",
                    "构建时间:${currentBuild.durationString}",
                    "构建镜像:${DOCKER_NAMESPACE}/${DOCKER_NAME}:${env.version}"
                  ],
                  messageUrl: "${log_url}",
                  picUrl: 'http://www.iconsdb.com/icons/preview/soylent-red/x-mark-3-xxl.png'
                  )
      }

      success {
        echo '构建成功'
        script{
          log_url=env.JENKINS_URL+"blue/organizations/jenkins/"+env.JOB_NAME+"/detail/"+env.JOB_NAME+"/"+env.BUILD_NUMBER+"/pipeline#"
        }
		    dingtalk (
                  robot: '43f773e2-a2bc-4fdd-80b0-e8c58f552d62',
                  type: 'LINK',
                  title: '构建成功✅',
                  text: [
                    "项目信息:${currentBuild.fullDisplayName}",
                    "构建结果:${currentBuild.result}",
                    "构建时间:${currentBuild.durationString}",
                    "构建镜像:${DOCKER_NAMESPACE}/${DOCKER_NAME}:${env.version}"

                  ],
                  messageUrl: "${log_url}",
                  picUrl: 'http://icons.iconarchive.com/icons/paomedia/small-n-flat/1024/sign-check-icon.png'
                  )
      }
    }
}
复制代码

6.3 发布脚本以及环境初始化

6.3.1 Docker Swarm主节点定义发布脚本

deploy-swarm.sh

#!/usr/bin/env bash
cd $(dirname $0)

set -e
export APP_NAME=$1
export DOCKER_IMAGE_URL=$2
export COMPOSE_FILE_FOLDER=$3
docker stack deploy  -c .$COMPOSE_FILE_FOLDER/docker-compose.yml $APP_NAME
复制代码

6.3.2 Docker Swarm自定义网络

创建专用网络用于发布的项目。

docker network create --driver=overlay --subnet=172.20.0.0/16 --attachable hxcloud_network
复制代码
© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享