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.