最近帮助好几个人将应用进行容器化,并使用 GitHub Actions 进行持续部署,虽然非常简单,但是属于会者不难。没有做过的人在一开始并不知道如何着手,所以记录一下。

几个在线例子

换脸服务 (Python Flask)

https://face-swap-scnx.onrender.com/,这是一个 Python Flask 项目。

源代码见: https://github.com/JeffTrain/face-swap

暂未开源的 Java Spring Boot 例子

https://brickpets-user-service-latest-3q8s.onrender.com,这是一个 Java Spring Boot 项目。

相关文章见:《通过 Bean 的方式扩展 Spring 应用,使其同时支持多个授权服务颁发的令牌。 - Jeff Tian的文章 - 知乎

微信公众号自动回复服务(nodejs)

详见《调用 AWS Bedrock 服务,为微信公众号消息提供自动回复功能。 - Jeff Tian的文章 - 知乎 》。这是一个 NodeJs 项目。
源代码见: https://github.com/Jeff-Tian/bedrock

另一个 Python Flask 项目例子

这是一个英语学习的项目,也是使用 Python Flask 开发。源代码见: https://github.com/lichanping/flaskStudy,本文将以这个项目为例详解相关的代码提交。

步骤详解

应用容器化

首先,需要将应用进行容器化,即写一个 Dockerfile 文件。以上面的 Python Flask 项目为例,虽然应用代码完全由别人开发,但我其实不需要关心应用逻辑,只需要针对其项目特点,写一个 Dockerfile 即可。完整的代码提交见: https://github.com/lichanping/flaskStudy/commit/6d8354ffbc678e6fea8c5fc052364e262f00d4ff,这里做一个分解:

Dockerfile

写了这样一个 Dockerfile: dockerfile

syntax=docker/dockerfile:1

FROM python:3.10-slim-buster

RUN apt update && apt install -y cmake g++ make ffmpeg libsm6 libxext6 wget

WORKDIR /python-docker

COPY requirements.txt requirements.txt RUN pip3 install -r requirements.txt

COPY . .

ENV IN_DOCKER=true

CMD [ python3, -m , flask, --app, app, run, --host, 0.0.0.0]

如果是一个 nodejs 项目,以前面的 bedrock 项目为例,Dockerfile 是这样的: dockerfile FROM node:18.3.0-alpine

ENV TIME_ZONE=Asia/Shanghai

RUN mkdir -p /usr/src/app && apk add --no-cache tzdata && echo ${TIME_ZONE} > /etc/timezone && ln -sf /usr/share/zoneinfo/${TIME_ZONE} /etc/localtime

WORKDIR /usr/src/app

RUN npm i --registry=https://registry.npm.taobao.org

COPY . /usr/src/app

RUN yarn

EXPOSE 8080

CMD yarn start

如果是 Java SpringBoot 项目,以前面提到的未开源的项目为例,其 Dockerfile 是这样的: dockerfile FROM maven as build WORKDIR /code COPY . /code/ RUN mvn package

FROM openjdk:11.0.16-jdk-slim-buster EXPOSE 8080 ENV TZ=Asia/Shanghai WORKDIR /app COPY --from=build /code/target/*.jar . CMD java -jar *.jar

准备构建容器镜像、推送到仓库的脚本

不管是什么项目,这个脚本都是一样的,因为它只需要一个 Dockerfile。不妨为该脚本起个名字,叫 dockerize.sh shell docker build -t $2/flask-study:$1 . docker images docker run --network host -e CI=true -d -p 127.0.0.1:5000:5000 --name flask-study $2/flask-study:$1 docker ps | grep -q flask-study docker ps -aqf name=flask-study$ docker push $2/flask-study:$1 docker logs $(docker ps -aqf name=flask-study$) curl localhost:5000 || docker logs $(docker ps -aqf name=flask-study$) docker kill flask-study || echo flask-study killed

可以看到要执行该脚本,需要传入一些参数,分别是容器镜像的标签、要推送到的容器仓库的用户名。

在 CICD 中调用 dockerize.sh

如果使用 GitHub Actions,那不妨建一个文件: .github/workflows/cicd.yml,然后,输入以下内容: yaml name: cicd on: push: branches: [ master ] pull_request: branches: [ master, dockerize ]

jobs:

build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - run: echo ${{secrets.DOCKER_PASSWORD}} | docker login -u ${{secrets.DOCKER_USERNAME}} --password-stdin - run: git_hash=$(git rev-parse --short $GITHUB_SHA) - run: sh dockerize.sh ${{ github.sha }} ${{secrets.DOCKER_USERNAME}} - run: sh dockerize.sh latest ${{secrets.DOCKER_USERNAME}} - run: curl ${{secrets.RENDER_DEPLOY_WEBHOOK_URL}}

可以看到,在这个 CICD 流水线中,需要一些秘密值,分别是 Docker 仓库的用户名和密码,以及一个部署 Webhook 地址,这个我们后面再讲。

在 GitHub 中设置秘密值

将以上的秘密值设置在仓库里:
image.png

部署

以前写过很多文章,都是将应用部署到 k8s 环境的,尤其是强烈推荐了 Okteto。但是 Okteto 也没有免费计划了,所以现在推荐 render 这个平台,对第一个例子的部署,详见《在 Python Flask 网站中对接身份认证平台,以Keycloak举例 - Jeff Tian的文章 - 知乎 》。
在 Render 中创建一个 Web Service,将镜像地址填写上就可以了:
image.png
image.png

将部署 Webhook 回填到 GitHub Secrets

到 Render 的控制面板里,找到部署 Webhook:
image.png
回填至 GitHub Secret:
image.png
这样,每当你有代码提交,就可以自动持续部署到 Render 平台了。
是不是非常简单?