自建服务的cicd
- Published on
本文最近一次更新于 503 个天前,其中的内容很可能已经有所发展或是发生改变。
前言
记录下我的自建服务中的 cicd 实现。 我希望我的应用部署是自动化的,不用我每次手动打包,手动登录服务器部署代码。之前的大部分服务都是基于 docker
以及 docker-compose
部署的。但是每次都是手动打包加上登录服务器手动更新,有点麻烦。在双 11 有了性能主机作为打包编译的服务器以后,我就考虑用下牛刀杀鸡了,用一下热门了很多年的 k8s
生态部署应用。
整体实现
这一次我用的是 woodpecker 作为 ci
工具,从自建的 gitea
仓库拉取代码自动编译,再推送到 docker register
自建私有镜像中心。等待推送完镜像以后,在通过工作流程中的调用 argocd的接口通知 argocd拉取最新的更新,自动部署到 k3s
容器中。内部网络基于 k3s
的 ingress 绑定,外网环境使用 headscale
连接内部以及腾讯云服务器,通过 nginx
代理需要外网访问的服务。
应用 cicd 具体实现
以这个博客为例,我把 woodpecker 、 argocd以及博客打包用 dockerfile
都集成的博客项目内,这样就方便我统一管理博客从开发、编译、部署的流程。
woodpecker 配置
Woodpecker 是基于 droneci
的开源 fork 版本,语法使用上和我之前使用 drone 差别没那么大。
添加项目
开始阶段需要在 woodpecker 后台中添加 gitea 中的项目
配置项目
进入项目设置,运行流水线前先进行项目配置,在基本设置中需要先勾选受信任,方便在执行构建时挂载目录。
没有受信任的选项,请现将当前用户登记为 woodpeacker 管理员
when 的语法
when:
- event: push
branch: dev
path:
exclude: ['*.md', 'docs/**', '.workflow', 'config']
这部分的逻辑是基于 dev 分支 push
实践触发工作流,'*.md', 'docs/**', '.workflow', 'config'
这些文件的更新不会触发。
pipeline 的基本写法
pipeline 的标准写法就是 steps
后面带上任务名称,以及镜像名称,执行方式等操作。
steps:
build-image-xxx:
image: docker:20.10.3
volumes:
- /data:/var/
commands:
- echo 'hello world'
比如下面的写法其实就是我用 docker 镜像通过博客的 Dockerfile
打包了博客的镜像并推送到我的私有镜像服务中,这种是 docker in docker
的方式,因为 woodpecker
本身也是通过 docker-compose 部署在 docker 中。
由于是个人的家庭网络中使用,所以也不考虑使用
harbor
搭建,直接使用了官方docker register
搭建。如果是镜像仓库有账号密码的鉴权的逻辑,在推送到镜像仓库前,还需要先进行登录。
steps:
build-blog-image:
image: docker:20.10.3
volumes:
- /var/run/docker.sock:/var/run/docker.sock
environment:
IMAGE: 192.168.50.214:5134/remix-blog
commands:
- export DRONE_TAG=$(cat .tags) ${CI_COMMIT_TAG}
- docker build -f ./Dockerfile -t $IMAGE:$DRONE_TAG ./
- docker tag $IMAGE:$DRONE_TAG $IMAGE:latest
- docker push $IMAGE:$DRONE_TAG
- docker push $IMAGE:latest
推送到镜像仓库后,就是部署以及持续集成了。我的博客是部署在 k3s
中,所以考虑后使用本身就是云原生的 argocd
来进行持续集成。pipeline 在打包完镜像以后就需要通知 argocd 拉取最新镜像,更新 pod。
之前手动部署 k3s 应用的时候都是
kubectl apply -f app.yml
,有了 argocd 部署方便了很多。
完整的 woodpecker
配置如下:
when:
- event: push
branch: dev
path:
exclude: ['*.md', 'docs/**', '.workflow', 'config']
steps:
build:
image: docker:20.10.3
volumes:
- /var/run/docker.sock:/var/run/docker.sock
environment:
IMAGE: 192.168.50.214:5134/remix-blog
commands:
- export DRONE_TAG=$(cat .tags) ${CI_COMMIT_TAG}
- docker build -f ./Dockerfile -t $IMAGE:$DRONE_TAG ./
- docker tag $IMAGE:$DRONE_TAG $IMAGE:latest
- docker push $IMAGE:$DRONE_TAG
- docker push $IMAGE:latest
deploy:
image: 192.168.50.214:5134/argocd:latest
environment:
ARGOCD_SERVER: '192.168.50.17:32460'
ARGOCD_USERNAME: 'admin'
ARGOCD_PASSWORD: $ARGOCD_PASSWORD
APP_NAME: 'blog'
BRANCH: 'dev'
commands:
- echo 'start deploy'
- echo $ARGOCD_PASSWORD
- argocd login $ARGOCD_SERVER --insecure --username=$ARGOCD_USERNAME --password=$ARGOCD_PASSWORD
- argocd app sync $APP_NAME --revision $BRANCH
- argocd app wait $APP_NAME
secrets: [ argocd_password ]
argocd 镜像的 Dockerfile
官方没有合适的镜像命令行通知,所以就基于 argocd 官方镜像在生成可以执行命令行的镜像。我是部署在我的私有仓库中,所以在 deploy 的操作中是拉取 192.168.50.214:5134/argocd:latest
镜像。
FROM argoproj/argocd:v2.6.15 as builder
FROM ubuntu:18.04 as app
COPY /usr/local/bin/argocd /usr/local/bin/argocd
ENTRYPOINT [ "argocd" ]
生成这个镜像也是用的 woodpecker
生成 argocd:latest
镜像的 woopecker
配置如下:
steps:
build:
image: docker:20.10.3
volumes:
- /var/run/docker.sock:/var/run/docker.sock
environment:
IMAGE: 192.168.50.214:5134/argocd
commands:
- export VERSION_TAG=$(cat .tags)
- docker build -f ./Dockerfile -t $IMAGE:$VERSION_TAG ./
- docker tag $IMAGE:$VERSION_TAG $IMAGE:latest
- docker push $IMAGE:$VERSION_TAG
- docker push $IMAGE:latest
argocd 应用配置
设置应用
先登录到 argocd 后台中,添加应用。登录后在 Applications 页,点击 NEW APP
创建项目。
按照官方文档创建项目,设置项目的 git 仓库
博客中的 argocd 配置
这个其实就是配置对应仓库目录的 k8s
的 deployment
和 SVC
。例如在 argocd 中的路径设置的是 .worflow/argocd
那么在我的博客中的对应目录, deployment
和 SVC
的文件就放在目录中。
deployment
和SVC
的概念需要先学习下k8s
相关的概念
blog-deployment.yml
以及 blog-svc.yml
的配置如下
apiVersion: apps/v1
kind: Deployment
metadata:
name: blog-app
spec:
replicas: 1
selector:
matchLabels:
app: blog-app
template:
metadata:
labels:
app: blog-app
spec:
terminationGracePeriodSeconds: 30
containers:
- name: blog-app
image: 192.168.50.214:5134/remix-blog:latest
imagePullPolicy: "Always"
ports:
- containerPort: 3002
env:
- name: "REMOTE"
value: "git"
- name: "REDIS_URL"
value: "redis://default:nas1234%3F@192.168.50.120:6379"
resources:
limits:
cpu: "2"
memory: "2Gi"
requests:
cpu: "1"
memory: "512Mi"
apiVersion: v1
kind: Service
metadata:
name: blog-app
spec:
type: NodePort
ports:
- port: 3002
nodePort: 32428
selector:
app: blog-app
woodpeacker 推送到 argocd
argocd 会自动根据 git 仓库的更新情况重新下来镜像进行更新,但是仓库一般是优先于镜像的更新,所以就需要在镜像打包完成以后主动通知 argocd 镜像已经更新。 这里就用到 woodpeacker
的 deploy 中配置,通过 argocd 命令行调用同步的命令。
deploy:
image: 192.168.50.214:5134/argocd:latest
environment:
ARGOCD_SERVER: '192.168.50.17:32460'
ARGOCD_USERNAME: 'admin'
ARGOCD_PASSWORD: $ARGOCD_PASSWORD
APP_NAME: 'blog'
BRANCH: 'dev'
commands:
- echo 'start deploy'
- echo $ARGOCD_PASSWORD
- argocd login $ARGOCD_SERVER --insecure --username=$ARGOCD_USERNAME --password=$ARGOCD_PASSWORD
- argocd app sync $APP_NAME --revision $BRANCH
- argocd app wait $APP_NAME
secrets: [ argocd_password ]
设置环境变量
在 woodpecker 中的密钥栏,添加用于 argocd 登录的密钥,比如用户名和密码。设置密钥的时候,使用的小写字母,在使用的时候会转换为大写。
总结
整体编译的流程对于一个小的项目其实复杂了很多,使用这一套流程目的也是学习了解云原生的 cicd 过程。整体实践下来,相对 jenkins
还是方便很多,不需要去操作对应部署的服务器。