Docker+Jenkins+Nginx+SpringBoot自动化部署项目

Docker经过linux的namespace实现资源隔离、cgroups实现资源控制,经过写时复制机制(copy-on-write)实现了高效的文件操做,在实际开发中可用于提供一次性的环境、微服务架构的搭建、统一环境的部署。javascript

前言

虽然Docker已是风靡全球的容器技术了,统一环境避免环境配置问题上是Docker的主要吸引点之一,但使用时详细仍是会遇到很多问题的,好比我的搭建时曾思考过这些问题:css

  • Jenkins官网既然有Docker上安装Jenkins的流程了,那我该怎么使用Jenkins容器呢?
  • 若是使用Jenkins容器,我该怎么经过Jenkins容器部署SpringBoot项目?是经过Jenkins容器与SpringBoot容器中的文件交互进行项目部署吗?这能作到吗?又或是把SpringBoot项目放到Jenkins容器中管理,那Jenkins中又要安装git、maven等一堆东西,这一点都不方便。
  • 使用IDEA Docker插件均可以直接本地链接到服务器的Docker建立镜像并运行容器了,为何还须要Jenkins?

在实际搭建部署中也找到了与上相对应的答案:html

  • 若是使用Jenkins容器,这将使得部署更加麻烦,因Jenkins每每须要配置Maven、git等一系列变量,应另寻出路。Jenkins既然是一款脚本CI工具,而Docker也有本身的脚本,我应该从Docker脚本集成到Docker中这方面考虑。
  • 在实际开发中,Jenkins可能不只须要项目的部署,还须要进行开发人员的鉴权,如开发人员A只能查看部署指定项目,管理员能够查看部署全部项目,但Docker主要用于镜像构建与容器运行,没法像Jenkins同样获取github/gitlab代码,也没法进行开发人员的鉴权,因此Docker能够在Jenkins中只扮演简化部署过程的一个角色。
  • 虽然IDEA插件能够直接把本地打包成功的项目部署服务器Dcoker并建立镜像运行容器,但为了安全还须要建立Docker CA认证下载到本地再进行服务器上的Docker链接,十分不便捷

环境准备

当探索到自我提问的答案时,便肯定了各组件的主要职责:java

  • Jenkins:接收项目更新信息并进行项目打包与Docker脚本的执行
  • Docker:安装所需应用镜像与运行容器
  • git:项目信息同步

搭建环境流程:node

  1. 安装JDKlinux

  2. 安装Mavennginx

  3. 安装gitgit

  4. 安装Jenkins(该步骤以前的可参考Jenkins安装并部署Java项目完整流程) 若有权限问题可将/etc/sysconfig/jenkins文件JENKINS_USER修改成root或手动赋权github

  5. Centos安装Docker(Install Docker Engine - Community)spring

  6. 安装DockerCompose

    sudo curl -L "https://github.com/docker/compose/releases/download/1.25.0/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
    sudo chmod +x /usr/local/bin/docker-compose
    sudo ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose
    docker-compose --version
    复制代码

    使用DockerCompose可省去容器增多时需屡次执行docker run的麻烦

具体步骤

  • 配置文件

    1. SpringBoot项目Dockerfile

    FROM java:8
    
    MAINTAINER Wilson
    
    # 统一容器与服务器时间
    ENV TZ=Asia/Shanghai
    RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
    
    #复制target/docker-spring-boot.jar到容器app目录下
    COPY ./target/docker-spring-boot.jar app/docker-spring-boot.jar
    EXPOSE 8080
    
    ENTRYPOINT ["java","-jar","app/docker-spring-boot.jar"]
    # docker build -t docker-spring-boot/latest .
    复制代码

    2. 配置docker-compose.yml

    version: '3.7'
    services:
      app:
        restart: always
        build: ./
        hostname: docker-spring-boot
        container_name: docker-spring-boot
        image: docker-spring-boot/latest
    # 端口不对外开放
    # ports:
    # - 8080:8080
        volumes:
          - ./volumes/app:/app
      nginx:
        depends_on:
          - app
        container_name: docker-nginx
        hostname: docker-nginx
        image: nginx:1.17.6
        environment:
          TZ: Asia/Shanghai
        restart: always
        expose:
          - 80
        ports:
          - 80:80
        links:
          - app
        volumes:
          - ./volumes/nginx/nginx.conf:/etc/nginx/nginx.conf
          - ./volumes/nginx/conf.d:/etc/nginx/conf.d
          - ./volumes/nginx/logs:/var/log/nginx
    复制代码

    3. Nginx

    • ./volumes/nginx/nginx.conf

      user nginx;
      worker_processes 2; #设置值和CPU核心数一致
      error_log /etc/nginx/error.log crit; #日志位置和日志级别
      pid /etc/nginx/nginx.pid;
      
      events
      {
        use epoll;
        worker_connections 65535;
      }
      http{
          include mime.types;
          default_type application/octet-stream;
          log_format main '$remote_addr - $remote_user [$time_local] "$request" '
                          '$status $body_bytes_sent "$http_referer" '
                          '"$http_user_agent" "$http_x_forwarded_for" "$http_cookie"';
      
          access_log  /var/log/nginx/access.log main;
          #charset utf8;
      
          server_names_hash_bucket_size 128;
          client_header_buffer_size 32k;
          large_client_header_buffers 4 32k;
          client_max_body_size 8m;
      
          sendfile on;
          tcp_nopush on;
          keepalive_timeout 60;
          tcp_nodelay on;
          fastcgi_connect_timeout 300;
          fastcgi_send_timeout 300;
          fastcgi_read_timeout 300;
          fastcgi_buffer_size 64k;
          fastcgi_buffers 4 64k;
          fastcgi_busy_buffers_size 128k;
          fastcgi_temp_file_write_size 128k;
          gzip on;
          gzip_min_length 1k;
          gzip_buffers 4 16k;
          gzip_http_version 1.0;
          gzip_comp_level 2;
          gzip_types text/plain application/x-javascript text/css application/xml;
          gzip_vary on;
      
          #limit_zone crawler $binary_remote_addr 10m;
          #server虚拟主机的配置
          include /etc/nginx/conf.d/*.conf;
      
      }
      复制代码
    • ./volumes/nginx/conf.d目录下的default.conf

      upstream application {
         server docker-spring-boot:8080;
      }
      
      server{
        listen 80;#监听端口
        server_name localhost;#域名
        access_log /var/log/nginx/nginx-spring-boot.log;
        location / {
            proxy_pass   http://application;
            proxy_set_header Host $host:$server_port;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header REMOTE-HOST $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        }
      }
      复制代码
  • Jenkins部署执行流程

    • maven打包Spring Boot项目为project.jar
    • 根据是否以第一次项目部署执行如下不一样的流程:
      • 如当前挂载卷已含项目jar(即非第一次运行),则运行如下步骤:
      1. 拷贝project.jar覆盖挂载卷中的project.jar
      2. 从新运行SpringBoot项目容器
      • 如当前挂载卷不含项目jar(即非第一次运行),则运行如下步骤:
      1. 建立挂载卷目录
      2. 拷贝project.jar到挂载卷中
      3. 经过docker-compose读取docker-compose.yml配置建立镜像启动容器

    Jenkins脚本(若是Nginx配置更改较多也可添加Nginx容器重启指令):

    cd /var/lib/jenkins/workspace/docker-spring-boot/spring-boot-nginx-docker-demo
    mvn clean package
    if [ -e "./volumes/app/docker-spring-boot.jar" ]
      then rm -f ./volumes/app/docker-spring-boot.jar \
            && cp ./target/docker-spring-boot.jar ./volumes/app/docker-spring-boot.jar \
    		&& docker restart docker-spring-boot \
            && echo "update restart success"
      else mkdir volumes/app -p \
            && cp ./target/docker-spring-boot.jar ./volumes/app/docker-spring-boot.jar \
    		&& docker-compose -p docker-spring-boot up -d \
            && echo "first start"
    fi
    复制代码

    docker-compose up指令能够进行镜像的安装,因此也省去了只用docker指令时须要提早准备好镜像相关指令的麻烦。

结果测试

  • 查看容器是否皆已启动:docker ps
    在这里插入图片描述
  • SpringBoot容器运行结果查看:如容器开放了8080端口则可经过http://url:8080/swagger-ui.html测试,也可经过查看Jenkins工做空间下/volumes/app的SpringBoot日志校验结果(SpringBoot日志的路径配置我的设置为app/logs目录下,前文已把容器中的app目录挂载到当前项目的volumes/app目录下)
    在这里插入图片描述
  • Nginx容器运行结果查看:访问http://url/swagger-ui.html测试是否Nginx容器已成功连通SpringBoot容器并进行了反向代理,也可经过查看Jenkins工做空间下/volumes/nginx/logs的Nginx日志校验结果
    在这里插入图片描述
  • 添加或删除controller接口再进行推到git,查看更改的接口是否可访问

SpringBoot集群搭建

如需将SpringBoot经过容器集群搭建,只需进行如下更改:

  • docker-compose.yml添加SpringBoot项目冗余,更改冗余容器名,区分日志挂载路径,冗余项目更改容器名

    version: '3.7'
    services:
      app:
        restart: always
        build: ./
        hostname: docker-spring-boot
        container_name: docker-spring-boot
        image: docker-spring-boot/latest
        volumes:
          - ./volumes/app/docker-spring-boot.jar:/app/docker-spring-boot.jar
          - ./volumes/app/logs:/app/logs
      app-bak:
        restart: always
        build: ./
        hostname: docker-spring-boot
        container_name: docker-spring-boot-bak
        image: docker-spring-boot/latest
        volumes:
          - ./volumes/app/docker-spring-boot.jar:/app/docker-spring-boot.jar
          - ./volumes/app/logs-bak:/app/logs
      nginx:
        depends_on:
          - app
        container_name: docker-nginx
        hostname: docker-nginx
        image: nginx:1.17.6
        environment:
          TZ: Asia/Shanghai
        restart: always
        expose:
          - 80
        ports:
          - 80:80
        links:
          - app
          - app-bak
        volumes:
          - ./volumes/nginx/nginx.conf:/etc/nginx/nginx.conf
          - ./volumes/nginx/conf.d:/etc/nginx/conf.d
          - ./volumes/nginx/logs:/var/log/nginx
    复制代码
  • nginx更改default.conf的upstream,添加冗余容器配置

    upstream application {
       server docker-spring-boot:8080 fail_timeout=2s max_fails=2 weight=1;
       server docker-spring-boot-bak:8080 fail_timeout=2s max_fails=2 weight=1;
    }
    
    server{
      listen 80;#监听端口
      server_name localhost;#域名
      access_log /var/log/nginx/nginx-spring-boot.log;
      location / {
          proxy_pass   http://application;
          proxy_connect_timeout 2s;
          proxy_set_header Host $host:$server_port;
          proxy_set_header X-Real-IP $remote_addr;
          proxy_set_header REMOTE-HOST $remote_addr;
          proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      }
    }
    复制代码
  • Jenkins添加冗余容器重启脚本

    BUILD_ID=DONTKILLME
    cd /var/lib/jenkins/workspace/docker-spring-boot/spring-boot-nginx-docker-demo
    mvn clean package
    if [ -e "./volumes/app/docker-spring-boot.jar" ]
      then rm -f ./volumes/app/docker-spring-boot.jar \
            && cp ./target/docker-spring-boot.jar ./volumes/app/docker-spring-boot.jar \
    		&& docker-compose -p docker-spring-boot up -d \
    		&& docker restart docker-spring-boot \
    		&& docker restart docker-spring-boot-bak \
    		&& docker restart docker-nginx \
            && echo "update restart success"
      else mkdir volumes/app -p \
            && cp ./target/docker-spring-boot.jar ./volumes/app/docker-spring-boot.jar \
    		&& docker-compose -p docker-spring-boot up -d \
            && echo "first start"
    fi
    复制代码

测试集群效果:

  • volumes/app放置了不一样容器的日志,如该例子的logs、logs-bak
  • 中止任一SpringBoot容器docker stop docker-spring-boot,仍可经过url/api经过Nginx访问

能够看出容器配置集群的如下优势:

  • 安全性高,每个应用都只属一个容器,经过特定配置才可与主机、其它容器交互
  • 统一配置文件,简单粗暴的方式解决端口、路径、版本等配置问题,如该项目即便运行了2个8080端口的SpringBoot容器而不需担忧端口的冲突、暴露问题,一切都在容器内解决
  • 省略手动应用安装,易于迁移,因为版本、配置、环境等都已配置在Docker的配置文件中,因此不用担忧更换机器后出现的各类配置、环境问题,且经过镜像拉取与容器运行能够省略如Nginx、Redis、Mysql等应用的安装与配置
    在这里插入图片描述

附项目地址

spring-boot-nginx-docker-demo

相关文章
相关标签/搜索