全棧式部署:SpringCloud 微服務+Docker + Vue + nginx 前後端一站式部署

solocoder發表於2019-10-26

本文將帶大家從一臺全新的 CentOS 伺服器開始,部署一套完整的前後端分離專案。操作步驟非常詳細,新手友好型文章~

後端技術棧:SpringCloud 微服務架構 + Redis + RabbitMQ + MySql;

前端技術棧:Vue + ElementUI;

部署工具:後端用 Docker 容器化部署,並把命令封裝成指令碼自動執行;前端用 Nginx 做代理。

一、前期準備

系統版本為:CentOS 7.6 64位。

1.1 連線伺服器

要對遠端伺服器進行操作,首先要連上伺服器才行。開啟命令列工具,輸入以下命令,再輸入伺服器例項的密碼,即可遠端連線到伺服器。

ssh root@xxx.xx.xx.xx
複製程式碼

@ 之後是伺服器的公網 ip,假如伺服器 ip 地址為:120.456.78.123,那麼連線伺服器的命令為:

ssh root@120.456.78.123
複製程式碼

敲回車之後會讓輸入密碼,輸入正確的密碼即可。

1.2 安裝 Docker

Docker 容器化部署的優點就不多說了,直接上乾貨如何安裝 Docker。

下面直接把程式碼框裡的命令拷貝到命令列執行就可以。

先移除舊版本(如果有):

sudo yum remove docker \
                  docker-client \
                  docker-client-latest \
                  docker-common \
                  docker-latest \
                  docker-latest-logrotate \
                  docker-logrotate \
                  docker-selinux \
                  docker-engine-selinux \
                  docker-engine
複製程式碼

安裝一些必要的工具:

sudo yum install -y yum-utils device-mapper-persistent-data lvm2
複製程式碼

新增軟體源資訊:

sudo yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
複製程式碼

更新 yum 快取:

sudo yum makecache fast
複製程式碼

安裝 Docker-ce:

sudo yum -y install docker-ce
複製程式碼

啟動 Docker 後臺服務

sudo systemctl start docker
複製程式碼

測試執行 hello-world(這步可要可不要,目的是驗證身份安裝 Docker 成功,如果成功會列印出 Hello World)

docker run hello-world
複製程式碼

1.3 安裝 mysql

拉取 mysql5.7 版本的映象

docker pull mysql:5.7
複製程式碼

執行 MySql

docker run -p 3306:3306 --name mysql -v $PWD/conf:/etc/mysql/conf.d -v $PWD/logs:/logs -v $PWD/data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 -d mysql:5.7
複製程式碼

命令說明:

  • -p 3306:3306:將容器的 3306 埠對映到主機的 3306 埠。
  • -v $PWD/conf:/etc/mysql/conf.d:將主機當前目錄下的 conf/my.cnf 掛載到容器的 /etc/mysql/my.cnf。
  • -v $PWD/logs:/logs:將主機當前目錄下的 logs 目錄掛載到容器的 /logs。
  • -v $PWD/data:/var/lib/mysql :將主機當前目錄下的data目錄掛載到容器的 /var/lib/mysql 。
  • **-e MYSQL_ROOT_PASSWORD=123456:**初始化 root 使用者的密碼。

注意在執行之前切換到合適的目錄,因為 MySql 容器對映到本機的目錄是對映的當前目錄的相對目錄。比如當前目錄為 /root/abc ,那執行完上面的命令後,會在 /root/abc 下建立 MySql 掛載出來的目錄和檔案。

1.4 安裝 redis

也可以直接執行執行命令,如果系統檢測到沒有安裝此映象,則會拉取安裝,再執行。

下載並執行 redis:4.0.8 :

docker run -p 6379:6379 -t -dit redis:4.0.8
複製程式碼

1.5 安裝 RabbitMQ

跟上面一樣也是直接執行執行命令安裝並執行 rabbitmq:3.7.7

docker run -d --hostname my-rabbit -p 5672:5672 -p 15672:15672 rabbitmq:3.7.7-management
複製程式碼

到此部署微服務後端專案的前期環境準備工具已經做完。如果要部署前端還需要安裝 Nginx,這個在前端部署的章節再講。

二、後端部署

部署後端需要做的工作有兩個,一個是修改每個微服務模組的配置檔案 application.yml ,另一個是編寫 Dockerfile

先看目錄結構:

全棧式部署:SpringCloud 微服務+Docker + Vue + nginx 前後端一站式部署

一共有 5 個模組,其中 common 是純 java 程式碼用於各模組公共程式碼的提取,剩下四個每個是一個獨立的微服務模組,所以我們要部署 eurekausereducationgateway 四個模組,也就是最後會執行四個獨立的 docker 容器。

具體的業務邏輯就不做過多說明了,本文只講部署。

2.1 配置檔案 application.yml

為了本地除錯和伺服器部署互不影響,我們把原來的 application.yml 拆分為三個檔案:

  • application.yml :總配置,指定應該用下面哪個配置
  • application-dev.yml :開發環境配置
  • application-pro.yml :生成環境配置

另外為了方便,把 Dockerfile 也放到同級目錄下。如圖:

全棧式部署:SpringCloud 微服務+Docker + Vue + nginx 前後端一站式部署

下面是三個配置檔案的程式碼:

application.yml

spring:
  profiles:
    active: pro

複製程式碼

application-dev.yml

eureka:
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka/
  instance:
    prefer-ip-address: true

server:
  port: 8899

spring:
  application:
    name: education
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    username: root
    password: 123456
    url: jdbc:mysql://127.0.0.1/edu?characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai
  jpa:
    show-sql: true
  # 如果欄位值為null則不返回
  jackson:
    default-property-inclusion: non_null

  rabbitmq:
    host: localhost
    port: 5672
    username: guest
    password: guest

  redis:
    port: 6379
    database: 0
    host: 127.0.0.1
    password:
    jedis:
      pool:
        max-active: 8
        max-wait: -1ms
        max-idle: 8
        min-idle: 0
    timeout: 5000ms

複製程式碼

application-pro.yml

eureka:
  client:
    service-url:
      defaultZone: ${SPRING-CLOUD-EUREKA-ZONE}
  instance:
    prefer-ip-address: true

server:
  port: 8899

spring:
  application:
    name: education
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    username: root
    password: 123456
    url: jdbc:mysql://${SPRING-CLOUD-MYSQL-HOST}/${SPRING-CLOUD-DB-NAME}?characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai
  jpa:
    show-sql: true
  # 如果欄位值為null則不返回
  jackson:
    default-property-inclusion: non_null

  rabbitmq:
    host: ${SPRING-CLOUD-RABBIT-MQ-HOST}
    port: 5672
    username: guest
    password: guest

  redis:
    port: 6379
    database: 0
    host: ${SPRING-CLOUD-REDIS-HOST}
    password:
    jedis:
      pool:
        max-active: 8
        max-wait: -1ms
        max-idle: 8
        min-idle: 0
    timeout: 5000ms
複製程式碼

這個專案配置比較全,redis、rabbitMQ、mysql、jpa 都有配置。

dev 跟 pro 的配置差不多,只是把 dev 中的 localhost127.0.0.1 這兩個本地的地址,換成了諸如 ${SPRING-CLOUD-EUREKA-ZONE}${SPRING-CLOUD-RABBIT-MQ-HOST} 等變數。

那這些變數是在哪設定的呢?待會兒編寫 Dockerfile 的時候就可以看到,會在 Dockerfile 裡設定這幾個變數為環境變數,當啟動 Docker 容器的時候,程式就會讀取到 Dockerfile 中設定的值並應用到專案中。

2.2 編寫Dockerfile

下面是 education 模組的 Dockerfile

FROM java:8
VOLUME /tmp
ADD education.jar app.jar
RUN bash -c 'touch /app.jar'

ENV SPRING-CLOUD-EUREKA-ZONE http://123.456.789.10:8761/eureka/
ENV SPRING-CLOUD-MYSQL-HOST 123.456.789.10
ENV SPRING-CLOUD-DB-NAME edu
ENV SPRING-CLOUD-RABBIT-MQ-HOST 123.456.789.10
ENV SPRING-CLOUD-REDIS-HOST 123.456.789.10

ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"]
EXPOSE 8899
複製程式碼

簡單的解釋下每句話的作用

FROM java:8 : 指定基礎映象,必須為第一個命令

VOLUME /tmp : 用於指定持久化目錄

ADD education.jar app.jar : 將本地檔案 education.jar 新增到容器中並命名為 app.jar。注意這裡的 education.jar 要換成你自己專案打包出來的 jar 包名字,如果你打的包名字叫 abc.jar,那應該這麼寫:ADD abc.jar app.jar

RUN bash -c 'touch /app.jar' : 在映象容器中執行的命令,執行 jar 包。

ENV *** : 這幾行以 ENV 開頭的是設定環境變數,還記得上面 application-pro.yml 檔案裡的那幾個 ${} 變數嗎?就是在這裡設定的。把該填地址的地方都換成這臺伺服器的公網 ip 地址,本配置假設你伺服器的地址是 123.456.789.10 ,用的時候把這個地址換成你自己伺服器地址即可。

ENTRYPOINT *** : 配置容器,使其可執行化。

EXPOSE 8899 : 對外暴露 8899 埠。這個埠要和專案的配置檔案中 server.port 設定的埠一致。

總結一下,Dockerfile 檔案你要改的,只有三個地方:

  • 第三行 education.jar 換成你打包出來的 jar 包名字
  • ENV 開頭的環境變數自己根據 application-pro.yml 的配置進行設定
  • 最後一行 EXPOSE 8899 改成你專案的埠號

再貼一個模組的 Dockerfile 作對比:

FROM java:8
VOLUME /tmp
ADD gateway.jar app.jar
RUN bash -c 'touch /app.jar'

ENV SPRING-CLOUD-EUREKA-ZONE http://123.456.789.10:8761/eureka/
ENV SPRING-CLOUD-REDIS-HOST 123.456.789.10

ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"]
EXPOSE 8888
複製程式碼

按照這種方法,把每個需要打包的微服務專案的 Dockerfile 寫好。

2.3 打包

配置好三個 applicaiton.yml 和 Dockerfile(其實打包這一步用不到 Dockerfile),在專案的根目錄下執行以下命令打包:

mvn clean package -Dmaven.test.skip=true
複製程式碼

看到控制檯輸出 SUCCESS 則打包成功

全棧式部署:SpringCloud 微服務+Docker + Vue + nginx 前後端一站式部署

打完的包在 專案目錄/target 裡,如圖:

全棧式部署:SpringCloud 微服務+Docker + Vue + nginx 前後端一站式部署

檢查一下 jar 包的大小,如果是好幾十 M 就沒啥問題,如果是幾百 K 那就是打包配置有問題,檢查 pom.xml 檔案的 <build> 標籤配置是否正確。

<build> 配置:

<build>
    <plugins>
      <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
        <executions>
          <execution>
            <goals>
              <goal>repackage</goal>
            </goals>
          </execution>
        </executions>
      </plugin>
      <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
      </plugin>
    </plugins>
    <finalName>education</finalName>
  </build>
複製程式碼

2.4 編寫自動化指令碼

寫完 Dockerfile 檔案還要執行命令打包成 image 映象,還需要執行起容器,兩行程式碼雖然不多但每次寫還是麻煩,把它封裝成一個 sh 指令碼,每次直接執行指令碼省時省力。

新建 education_deploy.sh 檔案,把兩行程式碼放進去:

docker build -t education .
docker run -p 8899:8899 -t -dit --restart=always education
複製程式碼

注意第一行最後有個點 . ,如果你想把打出來的映象名叫 abc,對外暴露的埠是 6666,則應該這樣寫:

docker build -t abc .
docker run -p 6666:6666 -t -dit --restart=always abc
複製程式碼

2.4 上傳到伺服器

新建一個資料夾,併為每個微服務模組新建一個資料夾用於存放 jar 包、Dockerfile 和自動化部署檔案,目錄結構如圖:

全棧式部署:SpringCloud 微服務+Docker + Vue + nginx 前後端一站式部署

把 back_end 這個資料夾整體壓縮,壓縮命令為:

tar -cvf back_end.tar ./back_end
複製程式碼

執行完後會在目錄下看到 back_end.tar 壓縮檔案。

使用 ssh 命令登入到伺服器,根目錄下新建一個 edu 資料夾,進入這個資料夾,檢視當前目錄,記住這個目錄

//新建資料夾 edu
mkdir edu

//進入到 edu 資料夾內
cd edu

//檢視當前目錄,會輸出:/root/edu
pwd
複製程式碼

記住你要傳到伺服器的位置:/root/edu,再回到本機剛才壓縮 back_end.tar 檔案的目錄,執行以下命令把 back_end.tar 傳送到伺服器的 /root/edu 目錄下:

scp back_end.tar root@123.456.789.10:/root/edu
複製程式碼

上傳完之後,在伺服器解壓

tar -xvf back_end.tar
複製程式碼

然後依次進入到各個模組的資料夾內,執行 sh 指令碼

sh ./education_deploy.sh
複製程式碼

都執行完之後後端微服務就部署完啦!

2.5 常用 Docker 命令

部署完還要檢驗是否部署正確,先從 Docker 開始檢查,最後在瀏覽器輸入介面地址看能否調通。

下面的命令都在 CentOS 伺服器上執行。

檢視當前執行的容器,看 mysql、redis、rabbitMQ 和你自己的專案是否執行

docker ps
複製程式碼

全棧式部署:SpringCloud 微服務+Docker + Vue + nginx 前後端一站式部署

檢視日誌

想進入到容器中檢視日誌:

docker logs 容器id
複製程式碼

比如想檢視容器id為 378af204f7bc 的容器日誌,應該執行:

docker logs 378af204f7bc
複製程式碼

如果容器執行了很長時間,將會產生非常多的日誌,直接使用 docker logs 會把所有日誌都列印出來,可不可以只列印最後多少行或者從某個時間之後的日誌呢?當然可以。

檢視指定時間後的日誌,只顯示最後100行:

docker logs -f -t --since="2019-10-24" --tail=100 CONTAINER_ID
複製程式碼

只列印最後50行日誌:

docker logs --tail=50 容器id
複製程式碼

檢視最近30分鐘的日誌:

docker logs --since 30m 容器id
複製程式碼

檢視某時間之後的日誌:

docker logs -t --since="2019-10-24T13:23:37" CONTAINER_ID
複製程式碼

檢視某時間段日誌:

docker logs -t --since="2019-10-24T13:23:37" --until "2019-10-25T12:23:37" CONTAINER_ID
複製程式碼

停止和啟動

停止容器

docker stop 容器id
複製程式碼

檢視所有容器(包括已經停止的)

docker ps -a
複製程式碼

重新啟動容器(容器被停止了,但還未被刪除)

docker start 容器id
複製程式碼

刪除容器和映象

如果專案有改動,最好把之前的容器、映象都刪掉,再執行新的容器。

先停止容器

docker stop 容器id
複製程式碼

再刪除容器

docker rm 容器id
複製程式碼

檢視 image 映象

docker images
複製程式碼

刪除映象

docker rmi 映象id
複製程式碼

三、前端部署

先把前端專案打包,壓縮為 tar 檔案,傳送到伺服器上,在伺服器解壓。

要用 nginx 做代理需要先安裝 nginx

3.1 安裝 nginx

先下載安裝基礎庫

yum -y install gcc gcc-c++ autoconf pcre pcre-devel make automake
yum -y install wget httpd-tools vim
複製程式碼

安裝 nginx

sudo yum install nginx
複製程式碼

3.2 配置 nginx

nginx 的配置檔案目錄

nginx主配置檔案
/etc/nginx/nginx.conf
/etc/nginx
/etc/nginx/conf.d
/etc/nginx/conf.d/default.conf
複製程式碼

我們來看 nginx.conf 預設的配置是什麼樣

# For more information on configuration, see:
#   * Official English Documentation: http://nginx.org/en/docs/
#   * Official Russian Documentation: http://nginx.org/ru/docs/

user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log;
pid /run/nginx.pid;

# Load dynamic modules. See /usr/share/doc/nginx/README.dynamic.
include /usr/share/nginx/modules/*.conf;

events {
    worker_connections 1024;
}

http {
    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;

    sendfile            on;
    tcp_nopush          on;
    tcp_nodelay         on;
    keepalive_timeout   65;
    types_hash_max_size 2048;

    include             /etc/nginx/mime.types;
    default_type        application/octet-stream;

    # Load modular configuration files from the /etc/nginx/conf.d directory.
    # See http://nginx.org/en/docs/ngx_core_module.html#include
    # for more information.
    include /etc/nginx/conf.d/*.conf;

    server {
        listen       80 default_server;
        listen       [::]:80 default_server;
        server_name  _;
        root         /usr/share/nginx/html;

        # Load configuration files for the default server block.
        include /etc/nginx/default.d/*.conf;

        location / {
        }

        error_page 404 /404.html;
            location = /40x.html {
        }

        error_page 500 502 503 504 /50x.html;
            location = /50x.html {
        }
    }

# Settings for a TLS enabled server.
#
#    server {
#        listen       443 ssl http2 default_server;
#        listen       [::]:443 ssl http2 default_server;
#        server_name  _;
#        root         /usr/share/nginx/html;
#
#        ssl_certificate "/etc/pki/nginx/server.crt";
#        ssl_certificate_key "/etc/pki/nginx/private/server.key";
#        ssl_session_cache shared:SSL:1m;
#        ssl_session_timeout  10m;
#        ssl_ciphers HIGH:!aNULL:!MD5;
#        ssl_prefer_server_ciphers on;
#
#        # Load configuration files for the default server block.
#        include /etc/nginx/default.d/*.conf;
#
#        location / {
#        }
#
#        error_page 404 /404.html;
#            location = /40x.html {
#        }
#
#        error_page 500 502 503 504 /50x.html;
#            location = /50x.html {
#        }
#    }

}
複製程式碼

修改 nginx.conf 的 server 節點,我們依然監聽 80埠,改 server_name 為你的域名,然後修改 locationroot 為前端檔案所在目錄,index 為入口檔案。

location / {
    root	/root/edu/front_end/;
    index	index.html index.htm;
}
複製程式碼

只改這兩處即可,別的地方不要動。

server {
    listen       80 default_server;
    listen       [::]:80 default_server;
    server_name  www.abc.cn abc.cn;
    root         /usr/share/nginx/html;

  # Load configuration files for the default server block.
    include /etc/nginx/default.d/*.conf;

    location / {
        root	/root/edu/front_end/;
        index	index.html index.htm;
    }

        error_page 404 /404.html;
        location = /40x.html {
    }

        error_page 500 502 503 504 /50x.html;
        location = /50x.html {
    }
}
複製程式碼

改完儲存成功之後,檢查 nginx 配置,結果出現 successful 表示配置檔案沒有語法錯誤

nginx -t -c /etc/nginx/nginx.conf
複製程式碼

重新載入配置

nginx -s reload -c /etc/nginx/nginx.conf
複製程式碼

3.3 nginx 報錯整理

[error] open() "/var/run/nginx.pid" failed

重新載入配置時有時會報錯:nginx: [error] open() "/var/run/nginx.pid" failed (2: No such file or directory)

解決方法:依次執行下面兩行程式碼

sudo nginx -c /etc/nginx/nginx.conf
nginx -s reload
複製程式碼

瀏覽器訪問報 403

用瀏覽器訪問域名,報 403 錯誤,要具體看到底是哪裡出錯了可以檢視 nginx 錯誤日誌,錯誤日誌在哪放著呢? nginx.conf 檔案裡指明瞭:error_log /var/log/nginx/error.log;

用 cat 命令檢視檔案內容

cat /var/log/nginx/error.log
複製程式碼

如果報了 Permission denied,有很大可能是當前登入使用者跟 nginx.conf 檔案第一行宣告的使用者不匹配。

 connect() to 127.0.0.1:8000 failed (13: Permission denied)....
複製程式碼

user nginx; 改為 user root; 再次重新載入配置一般就可以解決。


以上就是部署前後端的全部內容,大佬們有問題可以在評論區交流。

相關文章