本篇部署不是集群部署,适合个人或小公司项目使用。
GitHub由于是国外平台,代码拉取速度不是特别理想。所以使用了CODING作为代码托管平台。
Hyperf是一款Swoole框架,如果是Mac开发可以直接在宿主机安装Swoole扩展进行开发,如果在Mac上使用Docker的话速度会变慢。
如果用windows开发,可以使用WSL2,安装Docker进行开发。不过感觉还是慢,推荐使用PhpStorm的Deployment服务直接使用文件自动上传服务器,然后在服务器安装Docker环境进行开发。
创建基础项目
- 首先我们点击右上角,创建一个项目
进入项目后点击右上角,创建一个代码仓库
- 安装 Hyperf
- 安装方式
- 然后和我们刚刚创建的远程仓库关联(默认大家都有这个能力)
编写构建计划
这里先大概说下我们整体的部署流程。
首先检出分支,然后会根据分支下的Dockerfile生成镜像,然后推送镜像到coding的制品仓库。最后登录服务器拉取镜像进行部署。
流程清楚了那么我们就开始编写构建计划吧
- 创建构建计划
- 首先我们先创建一个构建计划,构建计划就是弄一套指令,让它按照我们自己设定的规则执行
- 然后选择构造计划模板,我们这里选择自定义模板
按照默认配置即可,这里我们先使用在线的Jenkinsfile,后续可以根据自己需求使用代码仓库中的Jenkinsfile
开始进行流程配置
- 生成镜像并推送
pipeline {
agent any
stages {
stage("检出") {
steps {
checkout([
$class: 'GitSCM',
branches: [[name: GIT_BUILD_REF]],
userRemoteConfigs: [[
url: GIT_REPO_URL,
credentialsId: CREDENTIALS_ID
]]])
}
}
stage('生成镜像') {
steps {
echo '生成镜像中……'
sh 'ls'
sh 'docker build -t ${IMAGE_NAME} -f Dockerfile ./'
echo '生成镜像完成'
}
}
stage('推送镜像') {
steps {
echo '推送镜像中...'
sh 'docker login -u image-xxxx -p ${DOCKER_TOKEN} xxx-docker.pkg.coding.net'
sh 'docker tag ${IMAGE_NAME} xxx-docker.pkg.coding.net/practice/blog/${IMAGE_NAME}'
sh 'docker push xxx-docker.pkg.coding.net/practice/blog/${IMAGE_NAME}'
echo '推送完成'
}
}
}
}
- 我们先来看生成镜像,因为Hyperf官方已经在项目中写好了Dockerfile,我们可根据自己需求更改直接使用就好。很简单就是执行了一个docker命令,但是里面有一个镜像名,这个我们拿出来定义一个变量然后在文件中就可以通过
${IMAGE_NAME}
这种方式来访问了。 - 定义变量:
镜像生成了,往哪儿推送?别急,我们先保存构建计划。
然后新建一个制品库,就是存放我们docker镜像的地方
创建成功后,如图
然后复制仓库中的登录和推送命令配置到我们构造计划文件中
我们这里选择使用令牌的方式进行登录,点击这个按钮,复制生成的令牌。替换刚才的登录命令
由于登录密码是敏感的,我们按照第一次创建环境变量的方式再次新增一个变量${DOCKER_TOKEN}
替换登录密码
到此为止,其实已经完成项目自动生成镜像并上传仓库。已经可以在服务器手动Docker镜像拉取部署了。(服务器需要已安装Docker,不然玩儿毛)
- 自动部署
stage('部署') {
steps {
echo '部署中...'
script {
def remote = [:]
remote.name = 'my-server'
remote.allowAnyHosts = true
// 主机地址
remote.host = '123.123.123.132'
remote.port = 22
// 用户名
remote.user = 'root'
// credentialsId: coding登录主机的秘钥
withCredentials([sshUserPrivateKey(credentialsId: 'coding的key', keyFileVariable: 'id_rsa')]) {
remote.identityFile = id_rsa
// 登录并拉取镜像
sshCommand remote: remote, command: "docker login -u image-xxxx -p ${DOCKER_TOKEN} xxx-docker.pkg.coding.net"
sshCommand remote: remote, command: "docker pull xxx-docker.pkg.coding.net/practice/blog/${IMAGE_NAME}:latest"
// 停止旧的服务,注意加 ||true,不然第一次部署会报错
sshCommand remote: remote, command: "docker stop ${PROJECT_NAME} || true"
sshCommand remote: remote, command: "docker rm ${PROJECT_NAME} || true"
// 启动新服务
sshCommand remote: remote, command: "docker run -d --restart always -p 10090:9501 -v /www/go_pocket_api.env:/opt/www/.env --name ${PROJECT_NAME} -d xxx-docker.pkg.coding.net/practice/blog/${IMAGE_NAME}:latest"
// 再启动一个容器,防止服务中断
sshCommand remote: remote, command: "docker stop ${PROJECT_NAME}2 || true"
sshCommand remote: remote, command: "docker rm ${PROJECT_NAME}2 || true"
sshCommand remote: remote, command: "docker run -d --restart always -p 10091:9501 -v /www/go_pocket_api.env:/opt/www/.env --name ${PROJECT_NAME}2 -d xxx-docker.pkg.coding.net/practice/practice/blog/${IMAGE_NAME}:latest"
}
}
echo '部署完成'
}
}
上部分配置参考了Hyperf作者文章
上面我们注意有个credentialsId,这个是coding能登录我们服务器的关键。这个东西会在condig读取秘钥,然后使用秘钥登录我们的服务器进行操作。
所以服务器需要支持ssh秘钥登录。
生成方式:
点击项目设置:
然后录入凭据,最后使用凭据id填入上面的credentialsId
最后我们还需要配置触发规则,也就是什么情况执行部署。推送到master,或者推送新标签后触发。
还有一点需要注意,由于自动部署需要访问我们的docker镜像仓库。所以我们需要把执行构建的服务器IP添加到docker镜像仓库的白名单里面。
(因为我们使用的免费的,所以使用了coding自己的服务器,基本够用。不够用可以够买coding提供的构建服务器。好像也不贵。)
首先复制构建服务器ip
写入到白名单
然后新增两个IP到白名单
保存即可
完整版Jenkinsfile
pipeline {
agent any
stages {
stage("检出") {
steps {
checkout([
$class: 'GitSCM',
branches: [[name: GIT_BUILD_REF]],
userRemoteConfigs: [[
url: GIT_REPO_URL,
credentialsId: CREDENTIALS_ID
]]])
}
}
stage('生成镜像') {
steps {
echo '生成镜像中……'
sh 'ls'
sh 'docker build -t ${IMAGE_NAME} -f Dockerfile ./'
echo '生成镜像完成'
}
}
stage('推送镜像') {
steps {
echo '推送镜像中...'
sh 'docker login -u image-xxxx -p ${DOCKER_TOKEN} xxx-docker.pkg.coding.net'
sh 'docker tag ${IMAGE_NAME} xxx-docker.pkg.coding.net/practice/blog/${IMAGE_NAME}'
sh 'docker push xxx-docker.pkg.coding.net/practice/blog/${IMAGE_NAME}'
echo '推送完成'
}
}
stage('部署') {
steps {
echo '部署中...'
script {
def remote = [:]
remote.name = 'my-server'
remote.allowAnyHosts = true
// 主机地址
remote.host = '123.123.123.132'
remote.port = 22
// 用户名
remote.user = 'root'
// credentialsId: coding登录主机的秘钥
withCredentials([sshUserPrivateKey(credentialsId: 'coding的key', keyFileVariable: 'id_rsa')]) {
remote.identityFile = id_rsa
// 登录并拉取镜像
sshCommand remote: remote, command: "docker login -u image-xxxx -p ${DOCKER_TOKEN} xxx-docker.pkg.coding.net"
sshCommand remote: remote, command: "docker pull xxx-docker.pkg.coding.net/practice/blog/${IMAGE_NAME}:latest"
// 停止旧的服务,注意加 ||true,不然第一次部署会报错
sshCommand remote: remote, command: "docker stop ${PROJECT_NAME} || true"
sshCommand remote: remote, command: "docker rm ${PROJECT_NAME} || true"
// 启动新服务
sshCommand remote: remote, command: "docker run -d --restart always -p 10090:9501 -v /www/go_pocket_api.env:/opt/www/.env --name ${PROJECT_NAME} -d xxx-docker.pkg.coding.net/practice/blog/${IMAGE_NAME}:latest"
// 再启动一个容器,防止服务中断
sshCommand remote: remote, command: "docker stop ${PROJECT_NAME}2 || true"
sshCommand remote: remote, command: "docker rm ${PROJECT_NAME}2 || true"
sshCommand remote: remote, command: "docker run -d --restart always -p 10091:9501 -v /www/go_pocket_api.env:/opt/www/.env --name ${PROJECT_NAME}2 -d xxx-docker.pkg.coding.net/practice/practice/blog/${IMAGE_NAME}:latest"
}
}
echo '部署完成'
}
}
}
}
需要注意,执行前需要先创建好env文件,因为Docker挂载时如果源文件不存在会自动创建目录。参考Docker挂载文件不存在情况
.env文件没想好有什么自动不需要创建的方式,写入到镜像环境变量中??
大家有好的思路可以交流
然后我们本地修改代码,推送到master或者推送标签根据自己设置的触发规则来。神奇的事情发生了。
Nginx配置
upstream test {
server 127.0.0.1:10090;
server 127.0.0.1:10091;
}
server {
listen 80;
server_name www.xxx.com;
location / {
proxy_pass http://test;
proxy_set_header Host $host:$server_port;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Real-PORT $remote_port;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
到此,部署完成。