使用Docker和Jenkin进行自动化测试、部署、回滚

发布时间 2023-12-19 21:53:41作者: 东方来客

没有安装docker的可以通过docker install进行安装。

docker jenkins

运行jenkins

首先创建一个目录存储容器中的信息mkdir jenkins-blue,然后执行命令sudo chown -R 1000:1000 ./jenkins-blue/,否则可能会报错touch: cannot touch ‘/var/jenkins_home/copy_reference_file.log’: Permission denied,
其中用来改变./jenkins-blue/目录下所有文件和子目录的所有者和所属组。1000:1000代表所有者和所属组的用户ID和组ID。

  1. 创建network
    docker network create jenkins
  2. 创建jenkins-docker
docker run --name jenkins-docker --rm --detach \
  --privileged --network jenkins --network-alias docker \
  --env DOCKER_TLS_CERTDIR=/certs \
  --volume ./jenkins-blue/jenkins-docker-certs:/certs/client \
  --volume ./jenkins-blue/jenkins-data:/var/jenkins_home \
  --publish 2376:2376 \
  docker:dind --insecure-registry test.com

注意一下映射的目录是否存在。--insecure-registry test.com参数没有配置https才需要。
3. Dockerfile

FROM jenkins/jenkins:2.426.2-jdk17
USER root
RUN apt-get update && apt-get install -y lsb-release
RUN curl -fsSLo /usr/share/keyrings/docker-archive-keyring.asc \
  https://download.docker.com/linux/debian/gpg
RUN echo "deb [arch=$(dpkg --print-architecture) \
  signed-by=/usr/share/keyrings/docker-archive-keyring.asc] \
  https://download.docker.com/linux/debian \
  $(lsb_release -cs) stable" > /etc/apt/sources.list.d/docker.list
RUN apt-get update && apt-get install -y docker-ce-cli
USER jenkins
RUN jenkins-plugin-cli --plugins "blueocean docker-workflow"
  1. 构建docker镜像
docker build -t myjenkins-blueocean:2.426.2-1 .
  1. 运行jenkins
docker run --name jenkins-blueocean --restart=on-failure --detach \
  --network jenkins --env DOCKER_HOST=tcp://docker:2376 \
  --env DOCKER_CERT_PATH=/certs/client --env DOCKER_TLS_VERIFY=1 \
  --volume ./jenkins-blue/jenkins-data:/var/jenkins_home \
  --volume ./jenkins-blue/jenkins-docker-certs:/certs/client:ro \
  --publish 8080:8080 --publish 50000:50000 myjenkins-blueocean:2.426.2-1

通过ip:8080(记得开放一下服务器端口),访问Jenkins,密码保存在/path/to/jenkins-blue/jenkins-data/secrets/initialAdminPassword中。
安装Git pluginPipelinePipelineView
点击New Item添加一个多分支流水线的项目,在Branch Sources -> Add source -> Git中添加我们的项目git地址。
Jenkins会扫描分支中包含Jenkinsfile的分支,进行操作。

安装Jenkins 插件

  1. Pipeline Utility Steps,使用其中的readJson解析json字符串。
  2. Git plugin
  3. Pipeline: Stage Step
  4. Pipeline: Stage View Plugin

Jenkinsfile

// 可以用来声明全局变量
import groovy.transform.Field

String buildNumber = env.BUILD_NUMBER;
String timestamp = new Date().format('yyyyMMddHHmmss');
String projectName = env.JOB_NAME.split(/\//)[0];

userInputRegistryInfo = input(
    id: 'userInputRegistryInfo', message: 'Enter path of test reports:?',
    parameters: [
        string(description: 'Host of docker registry',
        name: 'host'),
        string(description: 'dockerImage of docker registry',
        name: 'dockerImage')
    ])

host = userInputRegistryInfo.host
dockerImage = userInputRegistryInfo.dockerImage

String version = "${buildNumber}-${timestamp}-${projectName}";

node {
    checkout scm;

    if(params.BuildType=='Rollback') {
        return rollback()
    } else if(params.BuildType=='Normal'){
        return normalCIBuild(version)
    } else {
        setScmPollStrategyAndBuildTypes(['Normal', 'Rollback']);
    }
}

def setScmPollStrategyAndBuildTypes(List buildTypes) {
    def propertiesArray = [
        // 构建参数
        parameters([choice(choices: buildTypes.join('\n'), description: '', name: 'BuildType')]),
        pipelineTriggers([[$class: "SCMTrigger", scmpoll_spec: "* * * * *"]]),
        // 禁止并行构建
        disableConcurrentBuilds()
    ];
    properties(propertiesArray);
}

@Field def inputAuthValue
def normalCIBuild(String version) {
    stage ('test & package') {
        sh('chmod +x ./mvnw && ./mvnw clean package')
    }

    stage('docker build') {
        inputAuthValue = getInputAuth()
        echo inputAuthValue.password

        sh("docker login ${host} -u ${inputAuthValue.username} -p ${inputAuthValue.password}")

        sh("docker build . -t ${host}/${dockerImage}:${version}")

        sh("docker push ${host}/${dockerImage}:${version}")
    }

    stage('deploy') {
        input 'deploy?'
        deployVersion(version)
    }
}

def deployVersion(String version) {
    // 链接服务器使用docker发版
    sh """ssh -o StrictHostKeyChecking=no root@${host.split(':')[0]} 'docker login ${host} -u ${inputAuthValue.username} -p ${inputAuthValue.password} && \
    docker rm -f container-name && \
    docker run --name container-name -d -p 8081:8080 ${host}/${dockerImage}:${version}'"""
}

def rollback() {
    stage('do rollback') {

        def getAllTagsUri = "/v2/${dockerImage}/tags/list";

        inputAuthValue = getInputAuth()
        sh("docker login ${host} -u ${inputAuthValue.username} -p ${inputAuthValue.password}")
        println "http://${host}${getAllTagsUri}"

        def responseJson = sh(script: "curl -u ${inputAuthValue.username}:${inputAuthValue.password} --url ${host}${getAllTagsUri}", returnStdout: true)

        // {name:xxx,tags:[tag1,tag2,...]}
        def response = readJSON text: responseJson
        def versionsStr = response.tags.join('\n');

        def rollbackVersion = input(
            message: 'Select a version to rollback',
            ok: 'OK',
            parameters: [choice(choices: versionsStr, description: 'version', name: 'version')])

        println rollbackVersion
        deployVersion(rollbackVersion)
    }
}

// 为Docker registry设置了认证,需要手动输入
def getInputAuth() {
    def inputAuth = input(
       id: 'getInputAuth', message: 'Enter username and password:?',
       parameters: [
          string(description: 'username of auth',
          name: 'username', trim: true),
          string(description: 'password of auth',
          name: 'password', trim: true )
       ])
    return inputAuth
}

Docker registry

  1. 安装 htpasswd生成一个登录registry用的账号密码
    yum install -y httpd-tools
  2. 生成mkdir -p /opt/registry-var/auth/用以保存密码
  3. 生成密码保存到htpasswd中htpasswd -Bbn username password >> /opt/registry-var/auth/htpasswd
    大B
  4. 生成mkdir -p /opt/registry-var/config用来映射registry的config
  5. 启动registry
docker run  -p 5001:5000 --restart=always  --name=registry  \
    -v /opt/registry-var/config/:/etc/docker/registry/	\
    -v /opt/registry-var/auth/:/auth/   \
    -e "REGISTRY_AUTH=htpasswd"  \
    -e "REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm" \
    -e REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd   \
    -v /opt/registry-var/:/var/lib/registry/ registry:2.8

如果没有配置https还需要修改一下vim /etc/docker/daemon.json,
vim中按下i变为插入模式,使用esc输入:wq保存内容并退出。

{
  "insecure-registries": [
    "xxx:port"
  ]
}

sudo deckerd 可以启动docker

插曲

  1. 服务器链接github慢,可以考虑521github.com,不过生产环境应该不考虑这种,可能不安全。
  2. xxx.pem文件使用mv cp命令移动到~/.ssh/目录下并命名为id_rsa可以实现ssh role@host连接到服务器。
  3. 运行ssh-add id_rsa.pem时可能没有启动ssh-add,可以运行
eval `ssh-agent -s`

启动一个ssh-agent进程,-s选项告诉ssh-agent将其相关信息输出到标准输出。
4. Docker registry如果没有配置https,可以在启动容器时在命令最后加--insecure-registry test.com,或者使用命令vim /etc/docker/daemon.json(见上描述)。
5. 使用ssh链接服务器如提示Warning: unprotected private key file!代表用户对私钥权限过大,可以使用命令chmod 400 ~/.ssh/id_rsa, 400代表所有者具有只读权限,其他用户没有任何权限。如需其他权限,可自行查阅。
6. 使用root用户操作Docker容器,windowsdocker exec -ti --user root {容器名} /bin/bash,linux可以尝试docker exec -ti --user root {容器名} sh
7. 使用PuTTYpem文件链接服务器,可以在安装目录下寻找PuTTYgen将pem文件转换为ppk。PuTTY添加ppk文件的路径为 Connection -> SSH -> Auth -> Credentials -> Privite key file。然后点击sava
8. 在上述第7条配置后使用WinSCP可以检测到配置进行利用。