preface
A long time ago, there was a cloud server, which hung its own Blog. Every time the code was published, it needed to manually pull the code on the server, which was very troublesome.
So, after a few twists and turns on Gitee’s Hook, the automatic deployment function was implemented.
Some answer
Why not use a more sophisticated tool? Examples: Jenkins, CI/CD.
1, resource problems, itself is a small server, there are not so many resources to do this.
2, do a simple, free, incense (is to do things).
Will it stop?
There will be short outages, and if it doesn’t look like outages, you need to change your strategy.
Do you support multiple copies?
Not supported because the author’s requirements are relatively simple and do not implement a multi-copy scheme.
The environment
- Docker
- PHP
- Nginx(Lua module support required)
- Lua
The sample code
talk-lucky/git-docker-deploy
Specific flow chart
The specific implementation
Project structure transformation
The goal here is to make the project structure consistent for subsequent scripts.
├── ├─ ├─ ├─ ├─ ├─ ├.txtCopy the code
Install Git on the host
Git is required because you need to pull code from the cloud.
Configuration automation project
This project can be put into the code repository.
This article places the project in the/SRC directory.
The specific structure and interpretation are as follows:
// SRC ├─ dockers │ ├─ Docker-come.yml // ├─ come.yml // SRC ├─ docker-come.yml // SRC ├─ docker composing │ ├─ mysql // │ ├── ├─ ├─ ├. Lua // Perform the lua scripts │ ├ ─ ─ the projects │ │ ├ ─ ─ api.demo.com │ │ │ ├ ─ ─ Dockerfile / / generate images and container Dockerfile │ │ │ └ ─ ─ the publish. Sh / / release script, Will pull the code, │ │ ├─ ├─ ├─ nginx │ ├─ nginx │ ├─ nginx.conf // /etc/nginx/nginx.conf │ ├─ webhook // nginx/nginx.conf │ ├─ ├─ └─ api.demo.com.txt // api.demo.com.txt // api.demo.com.txt // api.demo.com.txt // api.demo.com.txt // api.demo.com.txt // api.demo.com.txt // api.demo.com.txt // api.demo.com.txt // api.demo.com.txt // api.demo.com.txt // api.demo.com.txt // ├ ─ ─ the SRC/directory/code │ └ ─ ─ version. TXT / / project version, └─ ├─ SRC // code directory ├─ 1.txt // project ├─ 1.txt // project ├─ 1.txt // project ├─ 1.txt //Copy the code
Host machine installs Nginx
Because the container needs to be built, Nginx is installed directly on the host to forward requests.
Lua modules need to be installed.
Once installed, replace the default nginx.conf with the automatic configuration nginx.conf.
Docker host installs Docker and Docker compose
There’s no doubt about that.
Once installed, run docker compose up -d in the/SRC /dockers directory.
Configuration Gitee
Add WebHooks under Project Management →WebHooks.
Write Nginx Lua scripts
In order to accommodate multiple projects, there are routes to identify different projects.
The general structure is: http://webhook.demo.com/ project alias.
Also, each project has its own key when it has a callback.
So we need to define the mapping table of project and domain name, and the mapping table of project and key.
- You need to change the domain name and alias
local mapping = {
api = "api.demo.com",
admin = "admin.demo.com"
}
-- Key for each item
local password = {
api = "E491B741843DA372DE61FCF8EFBB5252",
admin = "E491B741843DA372DE61FCF8EFBB5252"
}
Copy the code
The next step is to determine whether the request matches the item and key.
Take a look at Gitee’s request headers, and if you’re on another platform, do your own research.
Request URL: <http://webhook.demo.com/api>
Request Method: POST
Content-Type: application/json
User-Agent: git-oschina-hook
X-Gitee-Token: E491B741843DA372DE61FCF8EFBB5252
X-Gitee-Timestamp: 1591321794260
X-Gitee-Ping: false
X-Gitee-Event: Push Hook
X-Git-Oschina-Event: Push Hook
Copy the code
It can be seen that you need to check whether the item corresponding to the route exists and whether the key exists.
local project = ngx.var.request_uri
project = string.gsub(project, "/"."")
if (not mapping[project]) then
ngx.say('error project')
ngx.log(ngx.ERR, 'webhook: error project')
return 0
end
if (not password[project]) then
ngx.say('error project')
ngx.log(ngx.ERR, 'webhook: error project')
return 0
end
Copy the code
When both the item and the key are present, it is necessary to determine whether the request body information is correct.
- Is it a master branch change
- Is the key correct?
The request body looks like this:
{
"ref": "refs/heads/master"."before": "5f6477bb191fd11fb98988d96c84c1070a76e771"."after": "7826fb922fb2583705441ec6f77d4ccde24b3d14"."total_commits_count": 1."commits_more_than_ten": false."created": false."deleted": false."compare": ""."commits": []."head_commit": {},
"repository": {},
"project": {},
"user_id": 0."user_name": ""."user": {},
"pusher": {},
"sender": {},
"enterprise": null."hook_name": "push_hooks"."hook_id": 373558."hook_url": ""."password": "E491B741843DA372DE61FCF8EFBB5252"."timestamp": "1591321794260"."sign": ""
}
Copy the code
As you can see from the request body above, you only need to determine the presence of refs/ Heads /master and key.
The concrete implementation is as follows:
After the request header is ok, determine if the request body is the master branch
ngx.req.read_body()
local requestBody = ngx.req.get_body_data()
if (not requestBody) then
ngx.say('error requestBody')
ngx.log(ngx.ERR, 'webhook: error requestBody')
return 0
end
if (not string.find(requestBody, 'refs/heads/master')) then
ngx.say('error branch')
ngx.log(ngx.ERR, 'webhook: error branch')
return 0
end
-- The request body also has the key, do the second check
if (not string.find(requestBody, password[project])) then
ngx.say('error password')
ngx.log(ngx.ERR, 'webhook: error password')
return 0
end
Copy the code
The next step is to execute the Shell script and create the container.
Currently, projects are placed in the/SRC directory, and each project has its own publishing script at/SRC /docker/project/ project/ publish.sh.
Find the version of the project
-- Currently all projects are placed in the SRC directory with the following structure:
-- / SRC/domain name/project code
local filePath = "/src/". mapping[project] .."/version.txt"
local file = io.open(filePath, "r")
local version = "v1"
if (file) then
io.input(file)
tmpVersion = io.read(a)if (not tmpVersion) then
version = tmpVersion
end
end
Execute Shell script to create container
os.execute("nohup bash /src/docker/project/". mapping[project] .."/publish.sh ". version .." > /var/log/nohup/nohup.log 2>&1 &")
Copy the code
Finally, the correct response is given.
ngx.say('success')
Copy the code
The script is as follows:
- You need to change the domain name and alias
local mapping = {
api = "api.demo.com",
admin = "admin.demo.com"
}
-- Key for each item
local password = {
api = "E491B741843DA372DE61FCF8EFBB5252",
admin = "E491B741843DA372DE61FCF8EFBB5252"
}
The structure of the request is
Get the project alias and corresponding key to match
Request header:
-- Request URL: <http://webhook.demo.com/api>
-- Request Method: POST
-- Content-Type: application/json
-- User-Agent: git-oschina-hook
-- X-Gitee-Token: E491B741843DA372DE61FCF8EFBB5252
-- X-Gitee-Timestamp: 1591321794260
-- X-Gitee-Ping: false
-- X-Gitee-Event: Push Hook
-- X-Git-Oschina-Event: Push Hook
local project = ngx.var.request_uri
project = string.gsub(project, "/"."")
if (not mapping[project]) then
ngx.say('error project')
ngx.log(ngx.ERR, 'webhook: error project')
return 0
end
if (not password[project]) then
ngx.say('error project')
ngx.log(ngx.ERR, 'webhook: error project')
return 0
end
After the request header is ok, determine if the request body is the master branch
ngx.req.read_body()
local requestBody = ngx.req.get_body_data()
if (not requestBody) then
ngx.say('error requestBody')
ngx.log(ngx.ERR, 'webhook: error requestBody')
return 0
end
if (not string.find(requestBody, 'refs/heads/master')) then
ngx.say('error branch')
ngx.log(ngx.ERR, 'webhook: error branch')
return 0
end
-- The request body also has the key, do the second check
if (not string.find(requestBody, password[project])) then
ngx.say('error password')
ngx.log(ngx.ERR, 'webhook: error password')
return 0
end
Find the version of the project
-- Currently all projects are placed in the SRC directory with the following structure:
-- / SRC/domain name/project code
local filePath = "/src/". mapping[project] .."/version.txt"
local file = io.open(filePath, "r")
local version = "v1"
if (file) then
io.input(file)
tmpVersion = io.read(a)if (not tmpVersion) then
version = tmpVersion
end
end
Execute Shell script to create container
os.execute("nohup bash /src/docker/project/". mapping[project] .."/publish.sh ". version .." > /var/log/nohup/nohup.log 2>&1 &")
ngx.say('success')
Copy the code
Write Dockerfile
Location: / SRC /dockers/project/ your project/ Dockerfile
Here the project code is copied directly into the container and composer install –no-dev is executed.
FROM imjcw/php-fpm:7.3.18-fpm-alpine COPY SRC /var/www/ HTML/WORKDIR /var/www/ HTML RUN set -e; \ apk update; \ apk add git; \ cd /var/www/html; \ rm -rf .git; \ composer install --no-dev; \ apk del git; \ rm -rf /var/cache/apk/*Copy the code
Write the publish. Sh
This specifies the container open port, which is reserved for Nginx use.
The general process is as follows:
- Into the project
- Pull the code
- Build the mirror
- Stop the container before
- Delete the previous container
- Building a new container
- Delete unnecessary containers and images
#! /bin/bash PROJECT="api.demo.com" VERSION="" if [ -n "$1" ]; then VERSION="$1" else VERSION="v1" fi PROJECT_DIR="/src/${PROJECT}" if [ ! -d "${PROJECT_DIR}" ]; ${PROJECT_DIR} git pull origin master # build image docker build -t imjcw/${PROJECT}:${VERSION} ./ # stop container docker stop ${PROJECT} docker rm ${PROJECT} # run container docker run -d --name ${PROJECT} --link mysql --link redis --net dockers_default -p 9000:9000 imjcw/${PROJECT}:${VERSION} # rm <none> images if test ! -z $(docker images -f "dangling=true" -q); then docker rmi $(docker images -f "dangling=true" -q) fiCopy the code
The last
In order to be able to achieve, specifically learn LUA, feel good.