preface

This note is a refinement of the project in the previous chapter

Optimization scheme:

  1. Multiple microservices can be selected for simultaneous release in a Jenkins project
  2. Multiple production servers can be deployed simultaneously in a Jenkins project
  3. Each microservice is deployed as a cluster with high availability

Jenkins+Docker+SpringCloud cluster deployment process optimization

  • Instead of manually building services one by one, deploy them in a loop
  • Load balancing is implemented in a cluster

Modify the configurations of all microservices

Modify the eruka configuration

You need to implement a mutual registration function to modify the configuration of eureka_server

spring:
  application:
    name: EUREKA-HA

---
server:
  port: 10086
spring:
  # specified profile = eureka - server1
  profiles: eureka-server1
eureka:
  instance:
    If profile=eureka-server1, host name =eureka-server1
    hostname: 47.108189.123.
  client:
    service-url:
      Register with eureka-server1, eureka-server2
      defaultZone: http://47.108.189.123:10086/eureka/,http://47.108.190.228:10086/eureka/

---
server:
  port: 10086
spring:
  profiles: eureka-server2
eureka:
  instance:
    hostname: 47.108190.228.
  client:
    service-url:
      defaultZone: http://47.108.189.123:10086/eureka/,http://47.108.190.228:10086/eureka/
Copy the code

When starting the microservice, add the parameter: spring.profiles. Active to read the corresponding configuration

Other services then simply change the registered address

Modify zuul, Admin_service, gathering configurations

This is the address of two servers added from one server in the configuration

http://47.108.189.123:10086/eureka/,http://47.108.190.228:10086/eureka/
Copy the code

Upload the modified file to the repository

Design the construction parameters of Jenkins cluster project

Create pipeline Project (Cluster Edition)

Select the SCMPlug-ins are required because of multiple selectionExtended Choice ParameterThe installationExtended Choice ParameterThe plug-in

The pipeline project starts by adding a plain == string argument ==Add the == multiple == argument

I’m going to save and I’m going to build the project and now I have multiple options

Write a multi-option traversal Jenkins script

Multiple options for code review

// git certificate ID
def git_auth = "41580d48-d4c3-4116-9a71-4d4d777c5753"
// Git url
def git_url = "[email protected]: maomao_group/tensquare_back. Git." "
// Version number of the mirror
def tag = "latest"
// Harbor url
def harbor_url = "192.168.188.115:85"
// Mirror library project name
def harbor_project = "tensquare"
// Harbor's login credential ID
def harbor_auth = "c0e6642d-bab9-4978-be45-955e5130fae2"

node {
    // Get the name of the currently selected project
    def selectedProjectNames = "${project_name}".split(",")

   stage('Pull code') {
      checkout([$class: 'GitSCM'.branches: [[name: "*/${branch}"]], extensions:[].userRemoteConfigs: [[credentialsId: "${git_auth}".url: "${git_url}"]]])
   }
   stage('Code Review') {
        for(int i=0; i<selectedProjectNames.length; i++){// tensquare_eureka_server@10086
            def  projectInfo = selectedProjectNames[i];
            // The name of the project currently traversed
            def currentProjectName = "${projectInfo}".split("@") [0]
            // Project port currently traversed
            def currentProjectPort = "${projectInfo}".split("@") [1]
            
            // The SonarQubeScanner tool environment that defines the current Jenkins is viewed in the global tools configuration
            def scannerHome = tool 'sonar-scanner'
            // Reference the current JenkinsSonarQube environment in the system configuration view
            withSonarQubeEnv('sonarqube') {
                 sh """ cd ${currentProjectName} ${scannerHome}/bin/sonar-scanner """
            }
        }
   }
   stage('Compile, install common subproject') {
        sh "mvn -f tensquare_common clean install"
   }
   stage('Compile, install common subproject, upload image') {

         sh "mvn -f ${project_name} clean package dockerfile:build"

         // Define the mirror name
         def imageName = "${project_name}:${tag}"

         // Label the image
         sh "docker tag ${imageName} ${harbor_url}/${harbor_project}/${imageName}"

         // Push the image to Harbor
         withCredentials([usernamePassword(credentialsId: "${harbor_auth}".passwordVariable: 'password'.usernameVariable: 'username')]) {
             // Log in to Harbor
             sh "docker login -u ${username} -p ${password} ${harbor_url}"

             // Image upload
             sh "docker push ${harbor_url}/${harbor_project}/${imageName}"

             sh "Echo image uploaded successfully"
         }

         // Deploy the project
         sshPublisher(publishers: [sshPublisherDesc(configName: 'tomcat_server'.transfers: [sshTransfer(cleanRemote: false.excludes: ' '.execCommand: "/opt/jenkins_shell/deploy.sh $harbor_url $harbor_project $project_name $tag $port".execTimeout: 120000.flatten: false.makeEmptyDirs: false.noDefaultExcludes: false.patternSeparator: '[and] +'.remoteDirectory: ' '.remoteDirectorySDF: false.removePrefix: ' '.sourceFiles: ' ')].usePromotionTimestamp: false.useWorkspaceInPromotion: false.verbose: false)]}}Copy the code

What has changed is that

  • useSplit (", ")Separating project names (because they are separated by,)
  • In code review, you need to use traversal loops to check the code for each service
  • The for loop iterates through multiple selected services,lenghtTo obtain the length of the
  • Because the project name @port, you need to get the name and port, respectively, for Sonarqube to enter the corresponding space

Build the project through multiple selection after uploading jenkinsfile to the repository

Test two items firstConduct an Eruka reviewZuul review was conductedSince the build will fail later just to test if the traversal option succeeds, don’t worry

Complete multi-option microservice construction and upload image

This step is used to build and upload the image because you can comment out the code for deploying the application first

// git certificate ID
def git_auth = "41580d48-d4c3-4116-9a71-4d4d777c5753"
// Git url
def git_url = "[email protected]: maomao_group/tensquare_back. Git." "
// Version number of the mirror
def tag = "latest"
// Harbor url
def harbor_url = "192.168.188.115:85"
// Mirror library project name
def harbor_project = "tensquare"
// Harbor's login credential ID
def harbor_auth = "c0e6642d-bab9-4978-be45-955e5130fae2"

node {
    // Get the name of the currently selected project
    def selectedProjectNames = "${project_name}".split(",")

   stage('Pull code') {
      checkout([$class: 'GitSCM'.branches: [[name: "*/${branch}"]], extensions:[].userRemoteConfigs: [[credentialsId: "${git_auth}".url: "${git_url}"]]])
   }
   stage('Code Review') {
        for(int i=0; i<selectedProjectNames.length; i++){// tensquare_eureka_server@10086
            def  projectInfo = selectedProjectNames[i];
            // The name of the project currently traversed
            def currentProjectName = "${projectInfo}".split("@") [0]
            // Project port currently traversed
            def currentProjectPort = "${projectInfo}".split("@") [1]

            // The SonarQubeScanner tool environment that defines the current Jenkins is viewed in the global tools configuration
            def scannerHome = tool 'sonar-scanner'
            // Reference the current JenkinsSonarQube environment in the system configuration view
            withSonarQubeEnv('sonarqube') {
                 sh """ cd ${currentProjectName} ${scannerHome}/bin/sonar-scanner """
            }
        }
   }
   stage('Compile, install common subproject') {
        sh "mvn -f tensquare_common clean install"
   }
   stage('Compile, install common subproject, upload image') {
        for(int i=0; i<selectedProjectNames.length; i++){// tensquare_eureka_server@10086
             def  projectInfo = selectedProjectNames[i];
             // The name of the project currently traversed
             def currentProjectName = "${projectInfo}".split("@") [0]
             // Project port currently traversed
             def currentProjectPort = "${projectInfo}".split("@") [1]

             sh "mvn -f ${currentProjectName} clean package dockerfile:build"

             // Define the mirror name
             def imageName = "${currentProjectName}:${tag}"

             // Label the image
             sh "docker tag ${imageName} ${harbor_url}/${harbor_project}/${imageName}"

             // Push the image to Harbor
             withCredentials([usernamePassword(credentialsId: "${harbor_auth}".passwordVariable: 'password'.usernameVariable: 'username')]) {
             // Log in to Harbor
             sh "docker login -u ${username} -p ${password} ${harbor_url}"

             // Image upload
             sh "docker push ${harbor_url}/${harbor_project}/${imageName}"

             sh "Echo image uploaded successfully"
             }

         // Deploy the project
         // sshPublisher(publishers: [sshPublisherDesc(configName: 'tomcat_server', transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: "/opt/jenkins_shell/deploy.sh $harbor_url $harbor_project $project_name $tag $port", execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: '', remoteDirectorySDF: false, removePrefix: '', sourceFiles: '')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)])}}}Copy the code
  • The code review loop can be taken directly to the build image
  • Just change the project name to variable

testIf it succeeds, the loop is fine

The remote deployment of multiple microservice servers is complete

At this point, we need to add a second remote Tomcat server

Go to Jenkins system configuration, find Publish over SSH, add an SSH Server

Copy the public key to the remote server ssh-copy-id 192.168.188.114Copy the code

Click Test connection, “Success” indicates successful remote connection

vim /etc/docker/daemon.json

{
"registry-mirrors": ["https://zydiol88.mirror.aliyuncs.com"]."insecure-registries": ["192.168.188.115:85"]
}

systemctl restart docker
Copy the code

Add multiple options to pipeline projectsSame as before, so NO more description After saving, Jenkinsfile is written

// git certificate ID
def git_auth = "41580d48-d4c3-4116-9a71-4d4d777c5753"
// Git url
def git_url = "[email protected]: maomao_group/tensquare_back. Git." "
// Version number of the mirror
def tag = "latest"
// Harbor url
def harbor_url = "192.168.188.115:85"
// Mirror library project name
def harbor_project = "tensquare"
// Harbor's login credential ID
def harbor_auth = "c0e6642d-bab9-4978-be45-955e5130fae2"

node {
    // Get the name of the currently selected project
    def selectedProjectNames = "${project_name}".split(",")
    // Get the name of the currently selected server
    def selectedServers = "${publish_server}".split(",")

   stage('Pull code') {
      checkout([$class: 'GitSCM'.branches: [[name: "*/${branch}"]], extensions:[].userRemoteConfigs: [[credentialsId: "${git_auth}".url: "${git_url}"]]])
   }
   stage('Code Review') {
        for(int i=0; i<selectedProjectNames.length; i++){// tensquare_eureka_server@10086
            def  projectInfo = selectedProjectNames[i];
            // The name of the project currently traversed
            def currentProjectName = "${projectInfo}".split("@") [0]
            // Project port currently traversed
            def currentProjectPort = "${projectInfo}".split("@") [1]

            // The SonarQubeScanner tool environment that defines the current Jenkins is viewed in the global tools configuration
            def scannerHome = tool 'sonar-scanner'
            // Reference the current JenkinsSonarQube environment in the system configuration view
            withSonarQubeEnv('sonarqube') {
                 sh """ cd ${currentProjectName} ${scannerHome}/bin/sonar-scanner """
            }
        }
   }
   stage('Compile, install common subproject') {
        sh "mvn -f tensquare_common clean install"
   }
   stage('Compile, install common subproject, upload image') {
        for(int i=0; i<selectedProjectNames.length; i++){// tensquare_eureka_server@10086
             def  projectInfo = selectedProjectNames[i];
             // The name of the project currently traversed
             def currentProjectName = "${projectInfo}".split("@") [0]
             // Project port currently traversed
             def currentProjectPort = "${projectInfo}".split("@") [1]

             sh "mvn -f ${currentProjectName} clean package dockerfile:build"

             // Define the mirror name
             def imageName = "${currentProjectName}:${tag}"

             // Label the image
             sh "docker tag ${imageName} ${harbor_url}/${harbor_project}/${imageName}"

             // Push the image to Harbor
             withCredentials([usernamePassword(credentialsId: "${harbor_auth}".passwordVariable: 'password'.usernameVariable: 'username')]) {
             // Log in to Harbor
             sh "docker login -u ${username} -p ${password} ${harbor_url}"

             // Image upload
             sh "docker push ${harbor_url}/${harbor_project}/${imageName}"

             sh "Echo image uploaded successfully"
             }

         // Iterate over all servers and deploy them separately
         for(int j=0; j<selectedServers.length; j++) {// Get the name of the server currently traversed
                def currentServerName = selectedServers[j]

                Active = Eureka -server1/eureka-server2
                def activeProfile = "--spring.profiles.active="

                // Read different Eureka configurations according to different service names
               if(currentServerName=="tomcat_server") {
                    activeProfile = activeProfile+"eureka-server1"
               }else if(currentServerName=="tompig_server"){
                    activeProfile = activeProfile+"eureka-server2"
               }

                // Deploy the project
                sshPublisher(publishers: [sshPublisherDesc(configName: "${currentServerName}".transfers: [sshTransfer(cleanRemote: false.excludes: ' '.execCommand: "/opt/jenkins_shell/deployCluster.sh $harbor_url $harbor_project $currentProjectName $tag $currentProjectPort $activeProfile".execTimeout: 120000.flatten: false.makeEmptyDirs: false.noDefaultExcludes: false.patternSeparator: '[and] +'.remoteDirectory: ' '.remoteDirectorySDF: false.removePrefix: ' '.sourceFiles: ' ')].usePromotionTimestamp: false.useWorkspaceInPromotion: false.verbose: false)])

         }
                echo ${currentProjectName} finish compiling, build image}}}Copy the code

Write a new deploycluster. sh script based on the deploy

#! /bin/bash
Accept external parameters
harbor_url=The $1
harbor_project=$2
project_name=$3
tag=$4
port=A $5
profile=$6

imageName=$harbor_url/$harbor_project/$project_name:$tag

echo "$imageName"

If the container exists, delete it
containerId=`docker ps -a | grep -w ${project_name}:${tag}  | awk '{print $1}'`
if [ "$containerId"! =""];then
    # Stop the container
    docker stop $containerId

    # delete container
    docker rm $containerId

        echo "Container deleted successfully"
fi

# Check whether the mirror exists and delete it
imageId=`docker images | grep -w $project_name  | awk '{print $3}'`

if [ "$imageId"! =""];then

    # delete mirror
    docker rmi -f $imageId

        echo "Mirror deleted successfully"
fi

# login Harbor
docker login -u maomao -p Xiaotian123 $harbor_url

# Download image
docker pull $imageName

# start container
docker run -di -p $port:$port $imageName $profile

echo "Container started successfully"

Copy the code
  • You only need to add a new variable profile to store the new location variable activeProfile
  • Select a different ERuka configuration when starting the container

When you’re done, transfer the script to another server

SCP deployCluster. Sh [email protected]: / opt/jenkins_shell chown + x deployCluster. ShCopy the code

Deploy the project to see if you can deploy two EruKas on two serversThe build was successful and the Eruka cluster was deployed on the Tomcat and Tompig servers, respectively Open your browser to view the cluster, which has two instances

Conclusion error

  • References to variables must be made using “” I have a variable with an enclosing” causing an error

  • Curly braces must be open, if not closed will also be wrong
  • I changed the pipeline variable name to publish_server, but it didn’t change in the script, causing an error

Deploy the remaining three microservices to both servers simultaneously

This is easy, because you’ve eliminated all errors earlier and you just need to build the other three microservices besides Eruka All microservices are deployed on two servers, forming a microservice cluster

Nginx+Zuul cluster implements high availability gateway

At this time, we need to consider how the front-end website can connect to the two gateways through nGINx load balancing

Configure nginx

  • Configure a load balancing pool. The address in the load balancing pool is the zuul gateway address
  • Proxy_pass Select a different zuulServer
Upstream zuulServer{server 192.168.188.113:10020 weight=1; Server 192.168.188.114:10020 weight = 1; } server { listen 85 default_server; listen [::]:85 default_server; server_name _; root /usr/share/nginx/html; include /etc/nginx/default.d/*.conf; location / { proxy_pass http://zuulServer/; }Copy the code

Restart the Nginx

 systemctl restart nginx
Copy the code

Modify the front-end Nginx access address

  • Change the front-end access address to the address of the load balancer
'use strict'
module.exports = {
  NODE_ENV: '"production"'./ / BASE_API: '" http://192.168.207.131:7300/mock/5c0b42c85b4c7508d4dc568c/1024"
  BASE_API: '" http://47.108.190.228:85" // Gateway of the administrator
}
Copy the code

Commit the code to the repository after modification access

NICE! The project is successfully deployed and the front and back ends can work together. And you’re done!