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

相關文章
相關標籤/搜索