嘘~ 正在从服务器偷取页面 . . .

论部署后端项目


1. 获得一个服务器

我们可以自己在腾讯云上购买一个云服务器,之后我们就可以对它进行随意的折腾。如果资金紧缺,可以在本地使用虚拟机,再分配 ip 进行映射使用。

这里我们就直接使用科协的服务器举例,那么首先,我们不可能在整个服务器上都配置你的环境。我们需要一个虚拟机,然后我们在虚拟机上对环境进行配置。这里我们使用 ESXi,ESXi 是 vmware 推出的一款优秀的服务器级别的虚拟机。它与我们常用的虚拟机不同的是,日常使用的虚拟机是需要依赖于一个操作系统的,比如在 Windows 上使用 Vmware,或者 Linux 上使用 virtualbox。而 ESXi 不依赖于任何操作系统,它本身就可以看作一个操作系统,然后可以在它上面安装系统。

1.1 创建一个虚拟机

有了 ESXi 我们可以直接在上面创建一个虚拟机:

这里我创建了一个2核4G的虚拟机(就和学生特惠的一样)

记得在驱动部分选择我们下载好的镜像文件

之后我们便可以直接在 ESXi 上直接使用 bash

当然,如果你觉得 ESXI 的界面实在过于丑陋,你可以直接去 Vmware 上进行连接。

使用 Vmware 对服务器进行连接

在进行各种配置之后,我们的虚拟机就创建好了。但是有一个问题,这个虚拟机是没有 ip 地址的!这就导致我们只能在内网连接虚拟机才能使用,所以我们需要给我们的虚拟机分配 ip了。

1.2 分配 ip

分配 ip 的话我们首先得修改一个 centos 的配置(这个东西坑了我一晚上,因为 ubuntu 是不需要配置的)。

我们首先需要进入 /etc/sysconfig/network-scripts/ 进入网卡配置的文件夹,之后把 ifcfg-ens192 里面的 ONBOOT 改成 yes,这样我们就可以分配 ip 了。

对配置文件进行修改

使用 systemctl restart network,重启网卡(ESXI 在重启的时候也需要进入虚拟机执行这个命令,才能重新被分配 IP)。

之后我们使用 ip addr 查看我们的 Mac 地址和当前的 ip 地址,通过图片可以知道,我们的 Mac 地址是 00:0c:29:26:60:1b,ip 地址目前为空,也就表示我们还没进行地址的分配。

查看网络

说到地址的分配,我们就会使用 openwrt 进行简化操作:

给我们的虚拟机分配一个 ip 192.168.1.33

然后我们使用 systemctl restart network 重启网卡配置之后,我们就可以正常获取 ip 了。当然,如果担心,可以直接用 ip addr 检查一下。我们会用 DHCP 进行 ip 分配。**DHCP(Dynamic Host Configuration Protocol,动态主机配置协议)**是一个局域网的网络协议,使用 UDP 协议工作,主要有两个用途:给内部网络或网络服务供应商自动分配IP地址,给用户或者内部网络管理员作为对所有计算机作中央管理的手段。

可以看到,我们的 ip 已经被分配上去了

说明我们的 ip 已经分配上去了,过期时间是无限。如果不放心,可以使用 Xshell 进行测试。

openwrt 里显示我们已经分配好了

1.3 端口转发

如果我们单纯使用局域网,那么我们就只能在目前服务器 lan(Local Area Network,即局域网) 的网络下进行访问。这个时候我们就采取端口转发,相当于我们在局域网的请求都转发到到 wan(Wide Area Network,即广域网) 上。因为科协的路由器的 wan 口对应的是校园网,这样只要连接上校园网,用户就可以进行访问了。

要注意的是,服务器默认是开启22端口的,如果我们以后需要转发其他端口我们得先在防火墙中开放端口:

在 openwrt 防火墙中端口转发的设置

这样,位于 lan 内部的这个 ip 的 22 端口就转发到了路由器对应 ip 的 7500 端口。

这样我们就可以使用转发的 ip 和端口进行登录

2. 部署 jar 包

2.1 配置环境

2.1.1 安装 jre

首先我们需要配置 jre(Java runtime environment) 这样我们才能运行 Java 程序。

在这之前我们需要安装一个 wget 命令,可以使用 yum install wget 来安装它。

#查看版本
yum list |grep java

#安装jre
yum install java java-devel

#配置环境变量
vi /etc/profile

#set java environment
JAVA_HOME=/usr/lib/jvm/java-1.6.0-openjdk-1.6.0.171-7.b10.e17.x86_64
JRE_HOME=$JAVA_HOME/jre
CLASS_PATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar:$JRE_HOME/lib
PATH=$PATH:$JAVA_HOME/BIN:$JRE_HOME/bin
export JAVA_HOME JRE_HOME CLASS_PATH PATH

#使环境变量即时生效
source /etc/profile

2.1.2 安装 Docker

因为在 centos 中配置 mysql 实在是太麻烦了,我们就直接使用 docker 了

首先是对 docker 的安装

#使用国内源一键安装命令
curl -sSL https://get.daocloud.io/docker | sh

#设置仓库
sudo yum install -y yum-utils \
  device-mapper-persistent-data \
  lvm2
  
#选择国内的一些源地址,设置清华大学源
sudo yum-config-manager \
    --add-repo \
    https://mirrors.tuna.tsinghua.edu.cn/docker-ce/linux/centos/docker-ce.repo
    
#安装 Docker Engine-Community
sudo yum install docker-ce docker-ce-cli containerd.io

#启动 Docker
sudo systemctl start docker

2.1.3 安装 MySQL

#拉取 mysql 的镜像
docker pull mysql

#创建数据挂载点(即存储数据的地方,这样即便mysql容器删除了,数据还是在)
docker volume create mysql_data

#运行 mysql 容器
#如果是需要开放外网端口,则不建议直接开放 3306 端口,以免造成安全问题。最好的方式是使用 navicat 的 ssh 桥接登录模式
#$PWD 表示当前目录的环境变量参数
#对于 MySQL 的容器来说,password 是一个特殊的环境参数,
docker run -p 7001: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:latest

#进入容器 /bin/bash
docker exec -it mysql bash

#进入 mysql
mysql -u root -p
#之后会提示你输入你设定的 root password

#mysql 授权
GRANT ALL PRIVILEGES ON *.* TO 'root'@'%'WITH GRANT OPTION;
#刷新
FLUSH PRIVILEGES;
  • –name 镜像名称;

  • -p映射端口;

  • -v /mysql/datadir:/var/lib/mysql 设置本地挂载点;

  • -e MYSQL_ROOT_PASSWORD=123456 设置密码;

  • -d mysql 容器名称。

这样我们在本机已经配置好了一个 mysql 的环境,但是这个环境只能在本机启动。这个时候我们就需要使用端口转发,这样我们也能在校园网内使用 navicat 操作 mysql 了。

#查看防火墙的状态
firewall-cmd --state;

#如果没有开启,输入命令
systemctl start firewalld.service;

#开启对应端口
firewall-cmd --zone=public --add-port="对应端口"/tcp --permanent;

#重启防火墙
systemctl restart firewalld.service;

#重新载入配置
firewall-cmd --reload;

进行端口转发之后,我们就可以连接上了

2.1.4 安装 rz sz

安装这个之后我们就可以直接从我们本机直接上传文件到 ssh 连接的服务器上

这个对于我们上传 jar 包是必要的

#安装 rz
yum install -y lrzsz.x86_64

#使用
rz

2.2 部署项目

2.2.1 上传 jar 包

在 Spring-Boot 中,jar 包内置了一个 tomcat。tomcat 简单地说就是一个运行 Java 的网络服务器,可以让别人也能访问我们的前端页面。所以我们把 jar 上传到我的服务器之后,只需要在后台一运行就可以跑了

首先是通过 idea 进行 package 操作

通过可视化操作,我们快速打包

如果想要修改 jar 的名称,可以直接在 maven 包中进行改动

我们将打包好的 jar 包拷贝下来,使用 rz 命令进行上传,这样就可以了:

有时候使用 rz 可能会出错,这个时候再试一次基本就可以了

2.2.2 nohup

#当前ssh窗口被锁定,可按CTRL + C打断程序运行,或直接关闭窗口,程序退出
java -jar shareniu.jar

#java -jar shareniu.jar &
java -jar shareniu.jar &

#nohup 意思是不挂断运行命令,当账户退出或终端关闭时,程序仍然运行
#当用 nohup 命令执行作业时,缺省情况下该作业的所有输出被重定向到nohup.out的文件中,除非另外指定了输出文件
nohup java -jar shareniu.jar &

#command >out.txt command 的输出重定向到 out.txt 文件,即输出内容不打印到屏幕上,而是输出到 out.txt 文件中
nohup java -jar shareniu.jar >temp.txt &

2.2.3 Screen

当然,我们可以使用更加优雅的 Screen 方法。

GNU Screen 是一款由 GNU 计划开发的用于命令行终端切换的自由软件。用户可以通过该软件同时连接多个本地或远程的命令行会话,并在其间自由切换。

GNU Screen 可以看作是窗口管理器的命令行界面版本。它提供了统一的管理多个会话的界面和相应的功能,可以类比 Windows 开多个桌面,熟练使用之后比直接后台运行更加优雅。

yum install -y screen

screen -S yourname -> 新建一个叫yourname的session

screen -ls -> 列出当前所有的session

screen -r yourname -> 回到yourname这个session

screen -d yourname -> 远程detach某个session

#通过快捷键 ctrl+a+d 快速从我们创建的 screen 中回到我们的主窗口中
#从我个人的角度来看,使用 screen 无疑是更加优雅的一种方法
#但是 nohup 的好处在于生成日志文件,方便随时查看

如果还想使用端口转发进行访问,需要提前开启对应端口的防火墙

2.2.4 配置 IPV4

运行 jar 包时,使用指令 java -jar 时,如果涉及 IPV4,一定要把 IPv4 参数放前面,jar 包参数置后,不然 IPv4 不生效

java -Djava.net.preferIPv4Stack=true -jar test.jar

3. 部署 laravel

在经历两个晚上的小崩溃之后,最后还是发现还是使用宝塔界面更加方便。手动配置 PHP 环境的时候,不知道为什么就会出现未知的错误(甚至需要你使用 gcc 手动编译)。再加上国内糟糕的编程论坛环境,导致这成一个非常麻烦的点。

(不过好像 ubuntu 只需要正常安装就可以了,不明白问什么 centos 需要编译)

3.1 配置宝塔面板

#安装最新的宝塔面板
yum install -y wget && wget -O install.sh http://download.bt.cn/install/install_6.0.sh && sh install.sh

#如果想要进行端口转发,首先得先开启 8888 端口,之后得删除一个配置文件
rm -f /www/server/panel/data/admin_path.pl

安装之后会有给定的提示信息

之后我们就可以直接搭建 lnmp 环境(Linux Nginx MySQL PHP),然后我们就可以开始准备 laravel 项目的构建了。

耐心等待后台安装

我们可以在设置中调整我们的用户名和密码

3.2 配置 git

因为 laravel 跟 jar 包不同,我们需要的是一个完整的项目,所以最好的办法是使用 git 将我们的项目 clone 到服务器上。

#开启 Wandisco GIT 源,这样能保证下载到较新版本的 git
vi /etc/yum.repos.d/wandisco-git.repo

#这样就在 /etc/yum.repos.d/ 目录新建了一个 yum 源文件,我们修改它的配置
[wandisco-git]
name=Wandisco GIT Repository
baseurl=http://opensource.wandisco.com/centos/7/git/$basearch/
enabled=1
gpgcheck=1
gpgkey=http://opensource.wandisco.com/RPM-GPG-KEY-WANdisco

#导入 GPG keys
rpm --import http://opensource.wandisco.com/RPM-GPG-KEY-WANdisco

#安装 git
yum install git

#查看版本号
git --version

我安装的 git 版本为2.31.1

#配置 git 对应的用户名和邮箱
git config --global user.name "Your Name"
git config --global user.email "youremail@yourdomain.com"

#通过以下命令生成我们的公钥
ssh-keygen -t rsa

之后将我们的公钥添加到我们 GitHub 的 ssh 中就可以使用了。

使用 xftp 打开我们的公钥文件夹(需要提前设置显示隐藏文件)

我们的项目也成功克隆

3.3 配置 Composer

#Composer 依赖 PHP-cli,该应用已经在搭建 LNMP 环境的时候安装了
yum -y update
cd /tmp
curl -sS https://getcomposer.org/installer | PHP

#在执行第三步之前,我们先换源,这里使用阿里源
composer config -g repo.packagist composer https://mirrors.aliyun.com/composer/

#让 composer 全局可用
mv composer.phar /usr/local/bin/composer

3.4 安装 redis

#安装 redis 依次执行以下命令
yum install epel-release
yum install redis -y
systemctl start redis.service
systemctl enable redis

#查看运行状态
systemctl status redis.service

#测试 redis
redis-cli ping

3.5 配置 laravel

接下来就是比较麻烦的一个地方了,因为 PHP 默认禁止了一堆函数,所以在部署的使用我们可能需要开启一些。这里我们根据报错,一边看一边添加对应的配置。

#如果觉得 clone 下来的文件名太长,可以使用 mv 进行更改
#首先我们先进入我们的项目文件夹,你们可能还需要一个 fileinfo 的配置并开启 putenv()、proc_open
composer install

#在安装失败之后,我们使用更新命令
composer update

可知,我们缺少 putenv() 函数

缺少 fileinfo
缺少 proc_open()

P.S. 分享一个小笑话,之前在部署中,遇到了 undefined name 的错误。在 StackOverFlow 上查询还以为是版本的问题,结果对 Composer 进行换源之后,再执行 composer update 就可以了。

#复制 .env.example 为 .env
cp .env.example .env

#生成 APP_KEY
PHP artisan key:generate

#打开.env 进行配置
vi ./.env

#需更改的配置如下,依照备注进行修改
APP_NAME=你的APP名字
APP_ENV=production # 这里设为生成环境
APP_KEY=base64:前面生成的key不用改
APP_DEBUG=false # 关闭调试模式
APP_URL=你的主机地址
LOG_CHANNEL=stack
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=larabbs
DB_USERNAME=root
DB_PASSWORD=你的数据库密码
BROADCAST_DRIVER=log
CACHE_DRIVER=redis  # 这里使用redis
SESSION_DRIVER=file
SESSION_LIFETIME=120
QUEUE_DRIVER=redis # 这里使用redis
.
.
.
MAIL_DRIVER=smtp
MAIL_HOST=smtp.qq.com
MAIL_PORT=25
MAIL_USERNAME=你的QQ邮箱
MAIL_PASSWORD=你的smtp服务的密码
MAIL_ENCRYPTION=tls
MAIL_FROM_ADDRESS=你的邮箱地址
MAIL_FROM_NAME=LaraBBS
.
.
.
BAIDU_TRANSLATE_APPID=你的百度翻译APPID
BAIDU_TRANSLATE_KEY=你的百度翻译KEY

#将数据库进行迁移
PHP artisan migrate

#运行 passport:install 命令来创建生成安全访问令牌时所需的加密密钥,同时,这条命令也会创建用于生成访问令牌的「个人访问」客户端和「密码授权」客户端
PHP artisan passport:install

3.6 配置 Nginx

在 /www/server/Nginx/conf 中配置 Nginx 的配置文件

server {
    listen 80;
    server_name localhost;
    root /home/laravel/sast;

    add_header X-Frame-Options "SAMEORIGIN";
    add_header X-XSS-Protection "1; mode=block";
    add_header X-Content-Type-Options "nosniff";

    index index.PHP index.html index.htm;

    charset utf-8;

    location / {
        try_files $uri $uri/ /index.PHP?$query_string;
    }

    location = /favicon.ico { access_log off; log_not_found off; }
    location = /robots.txt  { access_log off; log_not_found off; }

    error_page 404 /index.PHP;

    location ~ \.PHP$ {
        root /home/laravel/sast;
        fastcgi_split_path_info ^(.+\.PHP)(/.+)$;
        fastcgi_pass 127.0.0.1:9000;
        fastcgi_index index.PHP;
        fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
        include fastcgi_params;
    }

    location ~ /\.(?!well-known).* {
        deny all;
    }
}

3.7 Crontab 配置

#对 crontab 进行配置
export EDITOR=vi && crontab -e

* * * * * PHP /home/vagrant/Code/larabbs/artisan schedule:run >> /dev/null 2>&1

3.8 启动 laravel

PHP artisan serve --port 8080 --host 0.0.0.0
#--host 防止 ip 无法被访问
#注意,这个只是在开发环境使用的运行命令,最多支持三个并发量

4. 使用 CICD

对于 CICD,最常见的操作就是持续的部署和集成,包括自动部署,代码审计,单元测试。

配置好之后,只需要对应的分支出现 push 或者 pr,就可以自动执行命令的操作。

4.1 GitHub Action

GitHub 自带 GitHub Action,方便我们进行操作。但是因为 GitHub 的服务器还是在国外,所以在传输的过程中,速率是及其底下的。经过测试,16MB 的 jar 包可能都需要传输近15分钟。

可能的解决方法在于:

  1. 能否在本地使用 docker 打包进入服务器,然后自动拉取镜像运行;
  2. 使用国内的软件进行替代。

示例步骤如下:

# This workflow will build a package using Maven and then publish it to GitHub packages when a release is created
# For more information see: https://github.com/actions/setup-java/blob/main/docs/advanced-usage.md#apache-maven-with-a-settings-path

name: Maven Package

on:
  push:
    branches: [ master ]
  pull_request:
    branches: [ master ]

jobs:
  build:

    runs-on: ubuntu-latest
    #运行环境设置在最新的 ubuntu 系统中
    permissions:
      contents: read
      packages: write

    steps:
      #设置 JDK 为11
    - uses: actions/checkout@v2
    - name: Set up JDK 11
      uses: actions/setup-java@v2
      with:
        java-version: '11'
        distribution: 'temurin'
        server-id: github # Value of the distributionManagement/repository/id field of the pom.xml
        settings-path: ${{ github.workspace }} # location for the settings.xml file
        
      #name 参数表示操作步骤的名字
      #uses 表示其中需要导入的包

    - name: Build with Maven
      #使用 maven 对应命令构建 jar 包
      run: mvn -B package --file pom.xml

    - name: ssh-scp-deploy
      # You may pin to the exact commit or the version.
      # uses: marcodallasanta/ssh-scp-deploy@acf9c1fefcda34c0b8b0cb0c0c1b521bf130d8b6
      uses: garygrossgarten/github-action-scp@release
      #scp 是 secure copy 的缩写, scp 是 linux 系统下基于 ssh 登陆进行安全的远程文件拷贝命令
      with:
#         host: ${{ secrets.HOST }}
#         username: ${{ secrets.SSH_USER }}
#         source: "target/nginxtest-ver*.jar"
#         target: "/var/www/test/nginxtest.jar"
#         key: ${{ secrets.SSH_PRIVATE }} # optional
        # Local file path
        local: target/nginxtest-ver.0.01.jar # optional, default is ./
        # Remote file path
        remote: /var/www/test/nginxtest.jar # optional, default is ~/
        # Remote server address
        host: ${{ secrets.HOST }}
        # Remote server user
        username: ${{ secrets.SSH_USER }}
        # User password
        privateKey: ${{ secrets.SSH_PRIVATE }} # optional
    - name: SSH My Action
      #远程执行 Linux 命令
      # You may pin to the exact commit or the version.
      # uses: GPTED/SSH-My-Action@4a6b3371064070202476914c9a7332bd23fd44ee
      uses: GPTED/SSH-My-Action@0.1
      with:
        # SSH username
        USER: ${{ secrets.SSH_USER }} # optional, default is root
        # SSH host
        HOST: ${{ secrets.HOST }}
        # SSH private key
        PRIVATE_KEY: ${{ secrets.SSH_PRIVATE }}
        # Command to be executed
        CMD: | # default is echo "$(whoami)" login at $(date) from Github Action!
          cd /var/www/test && nohup java -jar nginxtest.jar &
        

    #对于一些敏感参数,比如服务器的 ip, username, key
    #可以设置在 secret 中,并在服务器中保存
    #可以单独创建一个用户,并对其设置私钥和公钥,因为是从 GitHub Action 来连接 Linux 服务器

4.2 Coding CICD

在 Coding 中选择 Java + Spring + Docker 进行配置,但是因为 Coding 中的 CICD 是 gradle,所以有些设置需要我们自己更改。

修改构建命令为 maven 指令

因为 maven 中配置了多环境,所以使用mvn clean package -P dev,使用 dev profile 打包 jar。

(不会使用 docker 可以先参考这篇文章

#基础镜像使用 Java jdk11
FROM openjdk:11

#作者
MAINTAINER cxy621 <cxyphp@gmail.com>

# VOLUME 指定了临时文件目录为/tmp。
# 其效果是在主机 /var/lib/docker 目录下创建了一个临时文件,并链接到容器的/tmp
VOLUME /tmp

#定义工作目录
WORKDIR /app
#把项目中的所有东西都复制到工作目录下面
COPY . .

# 将 jar 包添加到容器中并更名为 app.jar
ADD target/*.jar /app/app.jar

#改变容器时区
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
RUN echo 'Asia/Shanghai' >/etc/timezone

#端口号
EXPOSE 10000

#运行 jar 包
ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app/app.jar"]

之后在 Coding 中的 docker hub 创建一个仓库存储项目的镜像,除此之外,还需要在项目管理中添加你的私钥。

将凭据 ID 放入

注意:这里的密钥需要转换成 RSA 格式,可以使用 Sourcetree 对私钥进行转换。

在 Docker 的仓库管理中,我们需要生成一个个人令牌,对应的参数需要写入 CICD 的全局变量中。

使用操作指引进入个人令牌管理页面

生成个人令牌

对应用户名、密码、服务地址

将变量配置在全局变量中

修改凭证

图形化界面转文本编辑如下:

pipeline {
  agent any
  stages {
    stage('检出') {
      steps {
        checkout([$class: 'GitSCM',
        branches: [[name: GIT_BUILD_REF]],
        userRemoteConfigs: [[
          url: GIT_REPO_URL,
          credentialsId: CREDENTIALS_ID
        ]]])
      }
    }
    stage('编译') {
      agent {
        docker {
          reuseNode 'true'
          registryUrl 'https://coding-public-docker.pkg.coding.net'
          image 'public/docker/openjdk:11'
        }

      }
      steps {
        sh 'mvn clean install package'
      }
    }
    stage('构建镜像并推送到 CODING Docker 制品库') {
      steps {
        sh "docker build -t ${CODING_DOCKER_IMAGE_NAME}:${DOCKER_IMAGE_VERSION} -f ${DOCKERFILE_PATH} ${DOCKER_BUILD_CONTEXT}"
        useCustomStepPlugin(key: 'coding-public:artifact_docker_push', version: 'latest', params: [image:"${CODING_DOCKER_IMAGE_NAME}:${DOCKER_IMAGE_VERSION}",repo:'nginxtest',properties:'[]'])
      }
    }
    stage('部署到远端服务') {
      steps {
        script {
          def remoteConfig = [:]
          remoteConfig.name = "my-remote-server"
          remoteConfig.host = "${REMOTE_HOST}"
          remoteConfig.port = "${REMOTE_SSH_PORT}".toInteger()
          remoteConfig.allowAnyHosts = true

          withCredentials([
            sshUserPrivateKey(
              credentialsId: "${REMOTE_CRED}",
              keyFileVariable: "privateKeyFilePath"
            ),
            usernamePassword(
              credentialsId: "${CODING_ARTIFACTS_CREDENTIALS_ID}",
              usernameVariable: 'CODING_DOCKER_REG_USERNAME',
              passwordVariable: 'CODING_DOCKER_REG_PASSWORD'
            )
          ]) {
            // SSH 登陆用户名
            remoteConfig.user = "${REMOTE_USER_NAME}"
            // SSH 私钥文件地址
            remoteConfig.identityFile = privateKeyFilePath

            // 请确保远端环境中有 Docker 环境
            sshCommand(
              remote: remoteConfig,
              command: "docker login -u ${CODING_DOCKER_REG_USERNAME} -p ${CODING_DOCKER_REG_PASSWORD} ${CODING_DOCKER_REG_HOST}",
              sudo: true,
            )
			
			// 删除之前的容器,开启新的容器
            sshCommand(
              remote: remoteConfig,
              command: "docker rm -f nginxtest | true",
              sudo: true,
            )

            // DOCKER_IMAGE_VERSION 中涉及到 GIT_LOCAL_BRANCH / GIT_TAG / GIT_COMMIT 的环境变量的使用
            // 需要在本地完成拼接后,再传入到远端服务器中使用
            DOCKER_IMAGE_URL = sh(
              script: "echo ${CODING_DOCKER_REG_HOST}/${CODING_DOCKER_IMAGE_NAME}:${DOCKER_IMAGE_VERSION}",
              returnStdout: true
            )

            sshCommand(
              remote: remoteConfig,
              // 可以根据需要对命令进行修改
              command: "docker run -d -p 11000:10000 --name nginxtest ${DOCKER_IMAGE_URL}",
              sudo: true,
            )

            echo "部署成功,请到 http://${REMOTE_HOST}:11000 预览效果"
          }
        }

      }
    }
  }
  environment {
    CODING_DOCKER_REG_HOST = "${CCI_CURRENT_TEAM}-docker.pkg.${CCI_CURRENT_DOMAIN}"
    CODING_DOCKER_IMAGE_NAME = "${PROJECT_NAME.toLowerCase()}/${DOCKER_REPO_NAME}/${DOCKER_IMAGE_NAME}"
  }
}

5. Azure

  1. 在选择订阅中需要选中 B1S;
  2. 地区可以选择 East Asia/Japan(现在好像没有港区的选项了);
  3. 在出入站规则中统一有限级,开起所有端口。

100$ 的额度大概能支撑 480G 的流量,对于平时使用还是需要注意,作为生产力内容是足够使用了。

6. Nginx

6.1 结构内容

#user  nobody;

#== 工作进程数,一般设置为cpu核心数,可以设置为 auto
worker_processes  1;

#error_log  logs/error.log;
#error_log  logs/error.log  notice;
#error_log  logs/error.log  info;

#pid        logs/nginx.pid;


events {

    #==最大连接数,一般设置为 cpu*2048
    worker_connections  1024;
}


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"';

    #access_log  logs/access.log  main;

    sendfile        on;
    #tcp_nopush     on;

    #keepalive_timeout  0;
    
    #==客户端链接超时时间
    keepalive_timeout  65;

    #gzip  on;

    #当配置多个server节点时,默认server names的缓存区大小就不够了,需要手动设置大一点
    server_names_hash_bucket_size 512;

    #server表示虚拟主机可以理解为一个站点,可以配置多个server节点搭建多个站点
    #每一个请求进来确定使用哪个server由server_name确定
    server {
        #站点监听端口
        listen       8800;
        # 可以使用正则表达式进行匹配 
        listen		[::]:8800
        #站点访问域名
        server_name  localhost;
        
        #编码格式,避免url参数乱码
        charset utf-8;

        #access_log  logs/host.access.log  main;

        #location用来匹配同一域名下多个URI的访问规则
        #比如动态资源如何跳转,静态资源如何跳转等
        #location后面跟着的/代表匹配规则
        location / {
            # 站点根目录,可以是相对路径,也可以使绝对路径
            root   html;
            # 默认主页
            index  index.html index.htm;
            
            # 转发后端站点地址,一般用于做软负载,轮询后端服务器
            #proxy_pass http://10.11.12.237:8080;

            # 拒绝请求,返回 403,一般用于某些目录禁止访问
            #deny all;
            
            # 允许请求
            #allow all;
            
            add_header 'Access-Control-Allow-Origin' '*';
            add_header 'Access-Control-Allow-Credentials' 'true';
            add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
            add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type';
            # 重新定义或者添加发往后端服务器的请求头
            # 给请求头中添加客户请求主机名
            proxy_set_header Host $host;
            # 给请求头中添加客户端IP
            proxy_set_header X-Real-IP $remote_addr;
            #将$remote_addr变量值添加在客户端“X-Forwarded-For”请求头的后面,并以逗号分隔。 如果客户端请求未携带“X-Forwarded-For”请求头,$proxy_add_x_forwarded_for变量值将与$remote_addr变量相同  
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            #给请求头中添加客户端的Cookie
            proxy_set_header Cookie $http_cookie;
            #将使用代理服务器的主域名和端口号来替换。如果端口是80,可以不加。
            proxy_redirect off;
            
            # 浏览器对 Cookie 有很多限制,如果 Cookie 的 Domain 部分与当前页面的 Domain 不匹配就无法写入。
            # 所以如果请求 A 域名,服务器 proxy_pass 到 B 域名,然后 B 服务器输出 Domian=B 的 Cookie,
            # 前端的页面依然停留在 A 域名上,于是浏览器就无法将 Cookie 写入。
            
			# 不仅是域名,浏览器对 Path 也有限制。我们经常会 proxy_pass 到目标服务器的某个 Path 下,
            # 不把这个 Path 暴露给浏览器。这时候如果目标服务器的 Cookie 写死了 Path 也会出现 Cookie 无法写入的问题。
            
            #设置 “Set-Cookie” 响应头中的 domain 属性的替换文本,其值可以为一个字符串、正则表达式的模式或一个引用的变量
            #转发后端服务器如果需要 Cookie 则需要将 cookie domain 也进行转换
            否则前端域名与后端域名不一致cookie就会无法存取
        #配置规则:proxy_cookie_domain serverDomain(后端服务器域) nginxDomain(nginx服务器域)
            proxy_cookie_domain localhost .testcaigou800.com;
            
            #取消当前配置级别的所有proxy_cookie_domain指令
            #proxy_cookie_domain off;
            #与后端服务器建立连接的超时时间。一般不可能大于75秒;
            proxy_connect_timeout 30;
        }

        #error_page  404              /404.html;

        # redirect server error pages to the static page /50x.html
        #
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }

    }
    
  # 当需要对同一端口监听多个域名时,使用如下配置,端口相同域名不同,server_name 也可以使用正则进行配置
  # 但要注意 server 过多需要手动扩大 server_names_hash_bucket_size 缓存区大小
  server {
    listen 80;
    server_name www.abc.com;
    charset utf-8;
    location / {
      proxy_pass http://localhost:10001;
    }
  }
    
  server {
    listen 80;
    server_name aaa.abc.com;
    charset utf-8;
    location / {
      proxy_pass http://localhost:20002;
    }
  }
}

6.1.1 全局块

全局块是默认配置文件从开始到 events 块之间的一部分内容,主要设置一些影响 Nginx 服务器整体运行的配置指令,因此,这些指令的作用域是 Nginx 服务器全局。

通常包括配置运行 Nginx 服务器的用户(组)、允许生成的 worker process 数、Nginx 进程 PID 存放路径、日志的存放路径和类型以及配置文件引入等。

# 指定可以运行 nginx 服务的用户和用户组,只能在全局块配置
# user [user] [group]
# 将user指令注释掉,或者配置成 nobody 的话所有用户都可以运行
user nobody nobody;
# user 指令在 Windows 上不生效,如果你制定具体用户和用户组会报小面警告
# nginx: [warn] "user" is not supported, ignored in D:\software\nginx-1.18.0/conf/nginx.conf:2

# 指定工作线程数,可以制定具体的进程数,也可使用自动模式,这个指令只能在全局块配置
# worker_processes number | auto;
# 列子:指定 4个工作线程,这种情况下会生成一个 master 进程和 4个 worker 进程
worker_processes 4;

# 指定pid文件存放的路径,这个指令只能在全局块配置
# pid logs/nginx.pid;

# 指定错误日志的路径和日志级别,此指令可以在全局块、http 块、server 块以及 location 块中配置。
# 其中 debug 级别的日志需要编译时使用 --with-debug 开启 debug 开关
# error_log [path] [debug | info | notice | warn | error | crit | alert | emerg] 
# error_log  logs/error.log  notice;
# error_log  logs/error.log  info;

6.1.2 events 块

events 块涉及的指令主要影响 Nginx 服务器与用户的网络连接。常用到的设置包括是否开启对多 worker process下的网络连接进行序列化,是否允许同时接收多个网络连接,选取哪种事件驱动模型处理连接请求,每个 worker process 可以同时支持的最大连接数等。

这一部分的指令对 Nginx 服务器的性能影响较大,在实际配置中应该根据实际情况灵活调整。

# 当某一时刻只有一个网络连接到来时,多个睡眠进程会被同时叫醒,但只有一个进程可获得连接。如果每次唤醒的进程数目太多,会影响一部分系统性能。在 Nginx 服务器的多进程下,就有可能出现这样的问题。
# 开启的时候,将会对多个 Nginx 进程接收连接进行序列化,防止多个进程对连接的争抢
# 默认是开启状态,只能在 events 块中进行配置
# accept_mutex on | off;

# 如果 multi_accept 被禁止了,Nginx 一个工作进程只能同时接受一个新的连接。否则,一个工作进程可以同时接受所有的新连接。 
# 如果 nginx 使用 kqueue 连接方法,那么这条指令会被忽略,因为这个方法会报告在等待被接受的新连接的数量。
# 默认是 off 状态,只能在 event 块配置
# multi_accept on | off;

# 指定使用哪种网络 IO 模型
# method 可选择的内容有:select、poll、kqueue、epoll、rtsig、/dev/poll 以及 eventport
# 一般操作系统不是支持上面所有模型的。
# 只能在events块中进行配置
# use method
# use epoll

# 设置允许每一个worker process同时开启的最大连接数,当每个工作进程接受的连接数超过这个值时将不再接收连接
# 当所有的工作进程都接收满时,连接进入 logback,logback 满后连接被拒绝
# 只能在 events 块中进行配置
# 注意:这个值不能超过超过系统支持打开的最大文件数,也不能超过单个进程支持打开的最大文件数,具体可以参考这篇文章:https://cloud.tencent.com/developer/article/1114773
# connection 一般是 CPU * 1024
worker_connections  1024;

6.1.3 http 块

http 块是 Nginx 服务器配置中的重要部分,代理、缓存和日志定义等绝大多数的功能和第三方模块的配置都可以放在这个模块中。http 块中可以包含自己的全局块,也可以包含 server 块,server 块中又可以进一步包含 location 块。

可以在 http 全局块中配置的指令包括文件引入、MIME-Type 定义、日志自定义、是否使用 sendfile 传输文件、连接超时时间、单连接请求数上限等。

# 常用的浏览器中,可以显示的内容有 HTML、XML、GIF 及 Flash 等种类繁多的文本、媒体等资源,浏览器为区分这些资源,需要使用MIME Type。换言之,MIME Type 是网络资源的媒体类型。Nginx 服务器作为 Web 服务器,必须能够识别前端请求的资源类型。
# mime.types 包括各种格式的映射关系

# include 指令,用于包含其他的配置文件,可以放在配置文件的任何地方,但是包含进来的配置文件一定符合配置规范
# 将 mime.types 包含进来,mime.types 和 nginx.config 同级目录,不同级的话需要指定具体路径
include  mime.types;

# 配置默认类型,如果不加此指令,默认值为 text/plain。
# 此指令还可以在 http 块、server 块或者 location 块中进行配置。
default_type  application/octet-stream;

# access_log 配置,此指令可以在 http 块、server 块或者 location 块中进行设置
# 在全局块中,我们介绍过 errer_log 指令,其用于配置 Nginx 进程运行时的日志存放和级别,此处所指的日志与常规的不同,它是指记录 Nginx 服务器提供服务过程应答前端请求的日志
# access_log path [format [buffer=size]]
# 如果要关闭 access_log,你可以使用下面的命令
# access_log off;

# log_format 指令,用于定义日志格式,此指令只能在 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  logs/access.log  main;

# 开启关闭 sendfile 方式传输文件,可以在 http 块、server 块或者 location 块中进行配置
# sendfile  on | off;

# 设置 sendfile 最大数据量,此指令可以在 http 块、server 块或 location 块中配置
# sendfile_max_chunk size;
# 其中,size 值如果大于 0,Nginx 进程的每个 worker process 每次调用 sendfile() 传输的数据量最大不能超过这个值(这里是128k,所以每次不能超过128k);如果设置为0,则无限制。默认值为0。
# sendfile_max_chunk 128k;

# 配置连接超时时间,此指令可以在http块、server块或location块中配置。
# 与用户建立会话连接后,Nginx服务器可以保持这些连接打开一段时间
# timeout,服务器端对连接的保持时间。默认值为 75s;header_timeout,可选项,在应答报文头部的Keep-Alive域设置超时时间:“Keep-Alive:timeout= header_timeout”。报文中的这个指令可以被Mozilla或者Konqueror识别。
# keepalive_timeout timeout [header_timeout]
# 下面配置的含义是,在服务器端保持连接的时间设置为 120s,发给用户端的应答报文头部中 Keep-Alive 域的超时时间设置为 100s。
keepalive_timeout 120s 100s

# 配置单连接请求数上限,此指令可以在 http 块、server 块或 location 块中配置。
# Nginx 服务器端和用户端建立会话连接后,用户端通过此连接发送请求。指令 keepalive_requests 用于限制用户通过某一连接向 Nginx 服务器发送请求的次数。默认是 100
# keepalive_requests number;

6.1.4 server 块

server 块和“虚拟主机”的概念有密切联系。

虚拟主机,又称虚拟服务器、主机空间或是网页空间,它是一种技术。该技术是为了节省互联网服务器硬件成本而出现的。这里的“主机”或“空间”是由实体的服务器延伸而来,硬件系统可以基于服务器群,或者单个服务器等。虚拟主机技术主要应用于 HTTP、FTP 及 EMAIL 等多项服务,将一台服务器的某项或者全部服务内容逻辑划分为多个服务单位,对外表现为多个服务器,从而充分利用服务器硬件资源。从用户角度来看,一台虚拟主机和一台独立的硬件主机是完全一样的。

在使用 Nginx 服务器提供 Web 服务时,利用虚拟主机的技术就可以避免为每一个要运行的网站提供单独的 Nginx 服务器,也无需为每个网站对应运行一组 Nginx 进程。虚拟主机技术使得 Nginx 服务器可以在同一台服务器上只运行一组 Nginx 进程,就可以运行多个网站。

在前面提到过,每一个 http 块都可以包含多个 server 块,而每个 server 块就相当于一台虚拟主机,它内部可有多台主机联合提供服务,一起对外提供在逻辑上关系密切的一组服务(或网站)。

和 http 块相同,server 块也可以包含自己的全局块,同时可以包含多个 location 块。在 server 全局块中,最常见的两个配置项是本虚拟主机的监听配置和本虚拟主机的名称或IP配置。

listen

listen 表示监听的端口,可以使用正则表达式进行匹配,也可以同时监听一个端口。

listen 127.0.0.1:8000;  # 只监听来自 127.0.0.1 这个 IP,请求 8000 端口的请求
listen 127.0.0.1; # 只监听来自 127.0.0.1 这个 IP,请求 80 端口的请求(不指定端口,默认 80)
listen 8000; # 监听来自所有 IP,请求 8000 端口的请求
listen *:8000; # 和上面效果一样
listen localhost:8000; # 和第一种效果一致
server_name

虽然 listen 也可以指定监听的 IP + port,但是一般在 server_name 中指定 IP/域名

listen       10000;
server_name  localhost *.cxy621.sast;

多个域名使用空格分离,可以使用 * 通配符。在出现重复匹配的时候拥有一套自己的优先级规则。

  • 准确匹配 server_name;
  • 通配符在开始时匹配 server_name 成功;
  • 通配符在结尾时匹配 server_name 成功;
  • 正则表达式匹配 server_name 成功。

6.1.5 location 块

location 跟的是待匹配的请求 uri。可以是不含正则表达的字符串,如 /myserver.php 等;也可以是包含有正则表达的字符串,如 .php$(表示以.php结尾的URL)等。

  • “=”,用于标准 uri 前,要求请求字符串与 uri 严格匹配。如果已经匹配成功,就停止继续向下搜索并立即处理此请求。
  • “^~”,用于标准 uri 前,要求 Nginx 服务器找到标识 uri 和请求字符串匹配度最高的 location 后,立即使用此 location 处理请求,而不再使用 location 块中的正则 uri 和请求字符串做匹配。
  • “~”,用于表示 uri 包含正则表达式,并且区分大小写。
  • “~*”,用于表示 uri 包含正则表达式,并且不区分大小写。注意如果uri包含正则表达式,就必须要使用“~”或者“~*”标识。
root & index

用于设置请求寻找资源的跟目录,此指令可以在 http 块、server 块或者 location 块中配置。由于使用 Nginx 服务器多数情况下要配置多个 location 块对不同的请求分别做出处理,因此该指令通常在 location 块中进行设置。

index 用于指定首页显示的内容,一般为 index.html

6.2 负载均衡

负载均衡默认是轮询,按时间顺序逐一分配到不同的后端服务器。

upstream nacos-cluster {
    ip_hash; # 每个请求按访问 ip 的 hash 结果分配,这样每个访客固定访问一个后端服务器,可以解决 session 的问题
    server 127.0.0.1:8845 weight=10;
    # 指定轮询几率,weight 和访问比率成正比,用于后端服务器性能不均的情况。
    server 127.0.0.1:8846;
    server 127.0.0.1:8847;
    fair; # 按后端服务器的响应时间来分配请求,响应时间短的优先分配。
}

server {
    listen       10000;
    server_name  localhost;

    location /nacos {
        # 转发到对应地址
        proxy_pass http://nacos-cluster;
        # http://localhost:10000/nacos-cluster hui'tiao
    }
}

每个设备的状态设置为:

  1. down:表示单前的 server 暂时不参与负载;
  2. weight:默认为 1。weight越大,负载的权重就越大;
  3. max_fails:允许请求失败的次数默认为 1。当超过最大次数时,返回 proxy_next_upstream 模块定义的错误;
  4. fail_timeout:max_fails 次失败后,暂停的时间;
  5. backup:其它所有的非 backup 机器 down 或者忙的时候,请求 backup 机器。所以这台机器压力会最轻。

6.3 include

通过在不同块中 include 其他配置文件,可以做到分离的作用。在主块中调用 include test.conf;

# test.conf 文件
server {
    listen 10040;
    server_name localhost;

    location / {
        root E:\Blog\Hexo\Blog_ver1.0\public;
        index index.html;
    }
}

参考文章

  1. Centos7 网卡配置——动态与静态
  2. Centos7.0 安装 Java JRE 环境
  3. Centos7 开放防火墙端口
  4. rz 与 sz
  5. Linux jar 包后台运行
  6. Ubuntu 20.04 安装 JDK 11的过程
  7. Coding 平台进行 SpringBoot 项目持续化集成部署
  8. Sourcetree 官网
  9. Azure Build
  10. nginx 之 proxy_pass 详解
  11. Nginx 配置文件详解
  12. Nginx 负载均衡配置
  13. Nginx 的配置,域名分发与负载均衡

文章作者: 陈鑫扬
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 陈鑫扬 !
评论
  目录