[Docker] 基于 Gitee 和 WebHooks 构建可以自动更新代码的 Docker 镜像

前言

最近一段时间 Yogurt 在考虑代码写完之后更新到服务器的问题。原先的做法是写完代码之后,调试没问题了直接打个压缩包,然后通过 stfp 上传到云服务器,然后再部署运行。如下图流程:

服务器端
启动项目
解压项目
部署项目
开发端
打包压缩项目
代码编写
代码调试
开始
sftp上传到云服务器
结束

看上去流程没啥问题,但其实部分流程理论上是不需要的——例如:打包压缩项目和解压项目。这两个环节只是为了将开发端的代码传递到服务器端而存在的。而且再加上上传,感觉被浪费的时间就有点多了。

正常来说,我们项目的更新基本都是增量的,很少说会从零开始覆盖更新。但通过打包压缩的方式,每次打包的都是完整的项目,随着开发时间的推移,项目文件也会变得越来越大,每次都要重新打包上传,花费的时间就非常多了。同时最重要的是,项目的版本也不太好管理。

以上还只是针对服务器不多的情况,如果需要多容器多设备做负载均衡,那么这套流程所花费的时间就得乘以部署服务器的数量了。一来每次的更新时效无法保证,二来万一更新的项目出了问题需要回滚版本时就十分的麻烦了。

因此,Yogurt 一直都在思考是否有更好的方式来部署项目,一来方便省心,二来又能保证时效。这两天通过查阅相关的资料和自己做了较多的测试以后,想到了一个解决方案。整理如下,以备后查。

开发环境及工具

开发端

信息说明
操作系统版本Microsoft Windows 10 专业工作站 10.0.19042 64位
开发工具Visual Studio Code

服务器端

信息说明
操作系统版本Ubuntu 18.04 64位
Docker 版本18.09.7
Python 版本Python 3.x

工具

信息说明
仓库Gitee.com
钩子WebHooks

操作步骤

Step 1 创建项目文件夹

从部署的角度来说,最方便的管理方式就是把项目打包成一个 Docker 的镜像,方便后期分发。同时也可以通过 Docker 来相对直观的管理项目的运行情况。

mkdir 你的项目名称

Step 2 创建用于部署推送的仓库

这里 Yogurt 是为了方便区分开发环境和生产环境而单独设置的仓库,仅推送测试完毕的项目文件。自行在 Gitee.com 上创建即可。

Step 3 创建部署公钥

由于直接采用 http 的方式 clone 仓库需要账号密码登录,这对自动执行代码 pull 命令来说是非常不利的,因此需要选择采用 SSH 的方式来 pull 代码,进而则需要创建公钥了。
在这里插入图片描述
点击 +添加部署公钥 后,将进入下图所示页面
在这里插入图片描述
我们需要生成一个 SSH 公钥。这步操作不管在开发端 Windows 上生成和在服务器端 Ubuntu 上生成都是一样的,只要生成了我们就可以使用。这里以 Windows 为例。

ssh-keygen -t ed25519 -C "你的邮箱"

按照提示完成三次回车,即可生成 ssh key。

在这里插入图片描述
在 Windows 上,你可以在 C:\Users\你的主机名\.ssh 中找到生成的 公钥私钥

Ubuntu 上可以在 /root/.ssh 中找到

在这里插入图片描述
不带后缀的为私钥,带 .pub 的为公钥。这时可以通过右键使用记事本打开 公钥
在这里插入图片描述
将公钥内容全部复制到 添加部署公钥 的页面中。
在这里插入图片描述
设置好标题,点击添加即可。添加完成后效果如下:
在这里插入图片描述
再通过 SSH 测试一下公钥的效果

ssh -T git@gitee.com

在这里插入图片描述
出现 successfully 字样则说明公钥生效了。

公钥和私钥一次生成可以在任何一台设备上使用,只要在 Gitee 上填写的公钥跟私钥是对应的即可。

Step 4 创建 WebHooks

同页面下,找到 WebHooks 选项。
在这里插入图片描述
点击 WebHooks 。这里可以添加很多个 Webhooks 接口,根据需要自行添加即可。
在这里插入图片描述
WebHooks 的原理是当仓库触发了指定事件时,将事件信息发送到我们自定义的 Url 上。例如我们 Push 了最新的代码到仓库,那么仓库接收到该事件后,将此事件执行成功的消息转发 Post 到我们自定义的 Url 上,我们根据接收到的消息来执行相应的操作。

值得注意的是,WebHooks 转发是不支持内网的,因此需要准备一个域名,或者将服务部署到云服务器中使用固定 IP 来访问,或者其他能够让 Gitee Post 消息给你的途径。

这里是把这个流程提前说明了,实际上可以在部署镜像之后再来设置的。毕竟最终在哪一台服务器上接收 WebHooks 消息,才填写哪个 Url 的。

Step 5 编写 Dockerfile 前的准备

这里以一个前端项目为例。

Step 5-1 Pull 项目仓库

测试项目是使用 Node.js + Vue3.js 写的,将 Builddist 文件内的所有文件 PushStep 2 创建好的仓库中。
在这里插入图片描述
然后在 Step 1 创建好的项目文件夹中通过 SSH_URL Git Clone 下来。
在这里插入图片描述

cd 你的项目文件夹
sudo git clone git@gitee.com:你的SSH_URL

在这里插入图片描述
然后将项目名称修改为 html

mv 你的仓库名称 html

这里 Yogurt 使用的是 Nginx 作为 Web 服务器,它的默认根目录是 /var/www/html。我们要提前把项目放到这个根目录下,为了 Dokcerfile 好写一点,就提前将其命名为 html

通过仓库拉取的目的有二。一是需要通过 git clone 来获取初始的项目;二是通过 git clone 来获取 .git 文件,后面自动执行 git pull 命令就可以了。

Step 5-2 密钥

创建 .ssh 文件夹,把 Step 3 生成的 公钥密钥 文件复制进去

cd 你的项目文件夹
mkdir .ssh
sudo cp /root/.ssh/id_ed25519 .
sudo cp /root/.ssh/id_ed25519.pub .
Step 5-3 ssh 配置

创建 ssh_config 文件,输入以下内容:

vim ssh_config
# This is the ssh client system-wide configuration file.  See
# ssh_config(5) for more information.  This file provides defaults for
# users, and the values can be changed in per-user configuration files
# or on the command line.

# Configuration data is parsed as follows:
#  1. command line options
#  2. user-specific file
#  3. system-wide file
# Any configuration value is only changed the first time it is set.
# Thus, host-specific definitions should be at the beginning of the
# configuration file, and defaults at the end.

# Site-wide defaults for some commonly used options.  For a comprehensive
# list of available options, their meanings and defaults, please see the
# ssh_config(5) man page.

Host *
#   ForwardAgent no
#   ForwardX11 no
#   ForwardX11Trusted yes
#   PasswordAuthentication yes
#   HostbasedAuthentication no
#   GSSAPIAuthentication no
#   GSSAPIDelegateCredentials no
#   GSSAPIKeyExchange no
#   GSSAPITrustDNS no
#   BatchMode no
#   CheckHostIP yes
#   AddressFamily any
#   ConnectTimeout 0
#   StrictHostKeyChecking ask
#   IdentityFile ~/.ssh/id_rsa
#   IdentityFile ~/.ssh/id_dsa
#   IdentityFile ~/.ssh/id_ecdsa
#   IdentityFile ~/.ssh/id_ed25519
#   Port 22
#   Protocol 2
#   Ciphers aes128-ctr,aes192-ctr,aes256-ctr,aes128-cbc,3des-cbc
#   MACs hmac-md5,hmac-sha1,umac-64@openssh.com
#   EscapeChar ~
#   Tunnel no
#   TunnelDevice any:any
#   PermitLocalCommand no
#   VisualHostKey no
#   ProxyCommand ssh -q -W %h:%p gateway.example.com
#   RekeyLimit 1G 1h
    SendEnv LANG LC_*
    HashKnownHosts yes
    GSSAPIAuthentication yes
# 主要是下面这个,其他的默认就可以了
    StrictHostKeyChecking no

ssh_config 这份文件在 ubuntu 系统中是有的,可以直接复制出来,然后在末尾加上 StrictHostKeyChecking no 就可以了。路径是 /etc/ssh/ssh_config

因为一般来说由于安全性的考虑,首次连接 ssh 的时候都是需要手动输入 yes 确认密钥的,但是我们在 Docker 内部署好的程序是全自动运行的,不能被中断,因此需要提前关掉确认参数。

为了安全起见,一定要记得先复制出来再修改。

Step 5-4 Webhooks 服务

原理其实很简单,我们只要部署一个 Web 服务用于接收消息,然后执行 git pull 命令即可。这里 Yogurt 使用的是 python + flask + tornado。代码示例如下:

vim WebHooks.py
# -*- coding: utf-8 -*-

from flask import Flask
from flask import request
from tornado.wsgi import WSGIContainer
from tornado.httpserver import HTTPServer
from tornado.ioloop import IOLoop

from datetime import datetime
from os import popen
from os import path

webApp = Flask(__name__)

@webApp.route('/push', methods = [ 'POST' ])
def Push():
    '''Gitee WebHooks
    '''

    '''维护参数'''
    WEBHOOKS_PASSWORD = '你的 Webhooks 密码'
    SSH_URL = '你的仓库 ssh_url'
    FETCH_NAME = 'refs/heads/master'

    '''执行部分'''
    result = { 'state': '40000', 'message': '请求失败' }

    headers = request.headers
    data = request.json

    # 校验请求头
    headersCheck = {
        'User-Agent': 'git-oschina-hook',
        'X-Gitee-Token': WEBHOOKS_PASSWORD,  # 在 gitee 中设置的 WebHook 密码
        'X-Gitee-Event': 'Push Hook'         # 只接受仓库推送钩子事件
    }
    itemCount = 0
    for item in headers:
        key, value = item
        if key in headersCheck:
            if headersCheck[key] != value:
                result = {
                	'state': '40001',
                	'message': '请求头内容异常',
                	'datetime': str(datetime.now())
                }
                return result
            itemCount += 1

    if itemCount == 0:
        result = {
        	'state': '40002',
        	'message': '请求头内容异常',
        	'datetime': str(datetime.now())
        }
        return result

    # 只接受指定仓库的推送
    if data['project']['ssh_url'] == SSH_URL:
        # 只接受指定分支的推送
        if data['ref'] == FETCH_NAME:
            # 执行 shell 脚本更新代码
            # 由于是通过 SSH 更新的仓库, 因此无需通过网址来拉取
            cmdList = [
                'cd {}'.format(path.abspath(path.dirname(__file__))),
                'git pull'
            ]
            cmd = ' && '.join(cmdList)
            with popen(cmd, 'r') as process: execResult = process.read()

            # 2021-12-04 14:08 Yogurt_cry 可以加上发一封邮件到指定邮箱
            #                             或者发到指定钉钉、企业微信或者同步推送到指定网站
            #                             目前此需求还不强烈, 到时用到再说

            # 执行完毕后返回执行结果
            result = {
            	'state': '20000',
            	'message': execResult,
            	'datetime': str(datetime.now())
            }
            return result

    result['datetime'] = str(datetime.now())
    return result

if __name__ == '__main__':
    httpServer = HTTPServer(WSGIContainer(webApp))
    httpServer.listen(5000)
    IOLoop.instance().start()

很简单的处理逻辑,没啥复杂的,就不多说了。应该看代码就可以了。

反正只要实现接收功能就可以了,不必拘泥于使用什么语言。

Step 5-5 Docker 启动命令

由于 Docker 启动时需要同时启动两个服务,因此我们需要准备一个 .sh 文件来存放这些命令。

vim EntryPoint.py
#!/bin/bash

nohup python3 /var/www/html/WebHooks.py &
nginx -g 'daemon off;'

第一行是用于启动 WebHooks.py 的,为了方便执行 git pull 语句,就把这个文件放到了 html 目录下了。

第二行是为了启动 Nginx

Step 6 编写 Dockerfile

准备好 Step 5 的相关文件后,项目文件目录应该是长这样的:
在这里插入图片描述

你的项目
┝━ .ssh
│  ┝━ id_ed25519
│  ┕━ id_ed25519.pub
┝━ EntryPoint.sh
┝━ WebHooks.py
┝━ html
┕━ ssh_config

然后我们需要创建一个 Dockerfile 文件。

vim Dockerfile
# VERSION 0.0.1
# 基于 ubuntu 18.04 创建
FROM ubuntu:18.04
# 作者
MAINTAINER 你的名字 <你的邮箱>

# 基本环境配置
RUN sed -i s@/archive.ubuntu.com/@/mirrors.aliyun.com/@g /etc/apt/sources.list && \
    ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && \
    echo 'Asia/Shanghai' >/etc/timezone && \
    apt-get clean && \
    apt-get update && \
    # 安装必要软件
    apt-get install -y python3 python3-pip nginx git && \
    # 配置 pip
    pip3 install -i https://pypi.mirrors.ustc.edu.cn/simple/ pip -U && \
    # 安装 python 必要依赖
    pip3 install -i https://pypi.mirrors.ustc.edu.cn/simple/ flask tornado && \
    # 删除不必要的环境
    rm -rf /var/cache/apk/* && \
    rm -rf /var/lib/apt/lists/* && \
    apt-get remove -y python3-pip && \
    apt-get autoremove -y

# 复制 ssh 配置文件, 避免出现首次链接请求验证公钥的问题
COPY ssh_config /etc/ssh/ssh_config

# 复制 git 公钥和私钥文件
COPY .ssh /root/.ssh

# 修改 git 私钥权限
WORKDIR /root/.ssh
RUN chmod 700 id_ed25519

# 克隆前端项目包
WORKDIR /var/www
RUN rm -rf html
COPY html html

# 克隆 WebHooks.py
WORKDIR /var/www/html
COPY WebHooks.py WebHooks.py

# 克隆 EntryPoint.sh
WORKDIR /root
COPY EntryPoint.sh EntryPoint.sh
RUN chmod +x EntryPoint.sh

ENTRYPOINT ["/root/EntryPoint.sh"]

#CMD ["sh", "-c", "python3 /var/www/html/WebHooks.py && nginx -g 'daemon off;'"]
#CMD ["sh", "-c", "nginx -g 'daemon off;'"]

# Nginx 端口
EXPOSE 80
# Python 端口
EXPOSE 5000

Step 7 打包镜像

编写完 Dockerfile 后就可以创建镜像了。

cd 你的项目文件夹
sudo docker build -t="镜像名称" .

镜像名称只能小写

等待镜像构建结束即可。

Step 8 创建容器

sudo docker run -itd --name 容器名 -p 前端项目端口:80 -p Webhooks端口:5000 镜像名称

以上容器就部署完毕了,接下来的一些端口转发,服务器部署等等操作就根据实际情况来处理就可以了。记得把最后确定的 Webhooks Url 添加到 Gitee 上就好。

然后也可以自己注册一下 Docker 账号,把自己做好的 Docker 镜像上传到 Docker 官网上,部署多台设备的时候可以直接使用 docker pull 项目名:latest 来创建容器,省时省力。具体实操可自行查询相关大神的文献。

后记

希望以上能对跟 Yogurt 有相同需求的开发者有一丝帮助

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值