1. 获得一个服务器
我们可以自己在腾讯云上购买一个云服务器,之后我们就可以对它进行随意的折腾。
如果资金紧缺,可以在本地使用虚拟机,再分配 ip 进行映射使用。
这里我们就直接使用科协的服务器举例,那么首先,我们不可能在整个服务器上都配置你的环境。我们需要一个虚拟机,然后我们在虚拟机上对环境进行配置。这里我们使用 ESXi,ESXi 是 vmware 推出的一款优秀的服务器级别的虚拟机。它与我们常用的虚拟机不同的是,日常使用的虚拟机是需要依赖于一个操作系统的,比如在 Windows 上使用 Vmware,或者 Linux 上使用 virtualbox。而 ESXi 不依赖于任何操作系统,它本身就可以看作一个操作系统,然后可以在它上面安装系统。
1.1 创建一个虚拟机
有了 ESXi 我们可以直接在上面创建一个虚拟机:
当然,如果你觉得 ESXI 的界面实在过于丑陋,你可以直接去 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 进行简化操作:
然后我们使用 systemctl restart network 重启网卡配置之后,我们就可以正常获取 ip 了。当然,如果担心,可以直接用 ip addr 检查一下。我们会用 DHCP 进行 ip 分配。**DHCP(Dynamic Host Configuration Protocol,动态主机配置协议)**是一个局域网的网络协议,使用 UDP 协议工作,主要有两个用途:给内部网络或网络服务供应商自动分配IP地址,给用户或者内部网络管理员作为对所有计算机作中央管理的手段。
说明我们的 ip 已经分配上去了,过期时间是无限。如果不放心,可以使用 Xshell 进行测试。
1.3 端口转发
如果我们单纯使用局域网,那么我们就只能在目前服务器 lan(Local Area Network,即局域网) 的网络下进行访问。这个时候我们就采取端口转发,相当于我们在局域网的请求都转发到到 wan(Wide Area Network,即广域网) 上。因为科协的路由器的 wan 口对应的是校园网,这样只要连接上校园网,用户就可以进行访问了。
要注意的是,服务器默认是开启22端口的,如果我们以后需要转发其他端口我们得先在防火墙中开放端口:
这样,位于 lan 内部的这个 ip 的 22 端口就转发到了路由器对应 ip 的 7500 端口。
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.8.0-openjdk-1.8.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 包拷贝下来,使用 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 对应的用户名和邮箱
git config --global user.name "Your Name"
git config --global user.email "youremail@yourdomain.com"
#通过以下命令生成我们的公钥
ssh-keygen -t rsa
之后将我们的公钥添加到我们 GitHub 的 ssh 中就可以使用了。
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
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分钟。
可能的解决方法在于:
- 能否在本地使用 docker 打包进入服务器,然后自动拉取镜像运行;
- 使用国内的软件进行替代。
示例步骤如下:
# 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 中配置了多环境,所以使用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 创建一个仓库存储项目的镜像,除此之外,还需要在项目管理中添加你的私钥。
注意:这里的密钥需要转换成 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
暂时先在这里开坑,防止自己忘记脚本的命令
wget -N --no-check-certificate https://raw.githubusercontent.com/veip007/scripts/master/xray.sh && chmod +x xray.sh && bash xray.sh