
gitea与woodpecker的cicd环境
gitea与woodpecker的cicd环境
之前使用gitea与Jenkins构建出来一个ci/cd的环境,但是过程就像老太太的裹脚布一样又臭又长,而且Jenkins对于容器化的支持其实并不好,走了很多弯路,而 GitLab 占用的资源过多,不适合我的个人场景。换成 Podman + Woodpecker 之后,不仅资源占用小,也能专注项目本身,这就是我想要的简洁、高效的 CI/CD 环境。于是有了这一篇博客。
环境配置
提示
开发环境: debian 12 podman 4.3.1 podman-composer 1.0.3 rustc 1.87.0
所用容器镜像: quay.io/podman/stable latest
docker.gitea.com/gitea 1.24.1-rootless docker.io/library/debian stable-slim
docker.io/library/debian bookworm-slim
docker.io/woodpeckerci/woodpecker-server v3
docker.io/woodpeckerci/woodpecker-agent v3
docker.io/woodpeckerci/plugin-git 2.6.5
docker.io/library/rust 1.87.0
部署环境: debian bookworm-slim podman 4.3.1
注意:这里的容器镜像可直接通过此命令进行获取,后面的版本均是tag。
podman pull <name>:<tag>
这里就不列出debian的安装方法了,需要的读者自行搜索如何安装debian(其实只要debian系的几乎都可以应用这篇文章)。
环境安装
首先需要安装podman:
apt install podman
podman -v
弹出版本号即为成功,podman安装的时候会提醒你需要打开KVM,这个前提是你装在物理机上面,如果安装在虚拟机上面就完全不用,可以直接使用podman就可以了。 安装podman-compose:
apt install podman-compose
podman-compose -v
弹出版本号即为成功。 安装rust:rust官网安装
ci准备
首先我们需要准备一个示例项目供ci/cd运行,以下是示例项目的目录结构:
├── Cargo.lock
├── Cargo.toml
├── Dockerfile
├── .git
├── .gitignore
├── README.md
├── src
│ └── main.rs
├── .woodpecker
│ └── main-branch.yaml
└── .woodpecker.yaml
首先我们来说明一下这个目录较为重要的文件:
Dockerfile这个是用生产镜像构建。.woodpecker目录以及.woodpecker.yaml是项目的ci/cd构建配置文件 其他的文件通过cargo均会创建,接下来是创建项目的步骤。
创建Rust项目
这个的前提是完成了Rust的环境安装。 运行以下命令创建项目与进入对应项目的文件夹。
cargo new ci-cd-test
cd ci-cd-test
以下所有的配置都在ci-cd-test中进行。 依赖配置(Cargo.toml):
[package]
name = "ci-cd-test"
version = "0.1.0"
edition = "2024"
[dependencies]
actix-web = "4.11.0"
将配置复制进创建好的Cargo.toml然后运行:
cargo check
接着就是示例程序的内容:
use actix_web::{get, App, HttpResponse, HttpServer, Responder};
#[get("/")]
async fn hello() -> impl Responder {
HttpResponse::Ok().body("Hello from Actix!")
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
println!("Server starting at http://0.0.0.0:8080");
HttpServer::new(|| {
App::new().service(hello)
})
.bind(("0.0.0.0", 8080))?
.run()
.await
}
#[cfg(test)]
mod tests {
use super::*;
use actix_web::{test,http, body::to_bytes, App};
#[actix_web::test]
async fn test_hello_route() {
let app = test::init_service(App::new().service(hello)).await;
let req = test::TestRequest::get().uri("/").to_request();
let resp = test::call_service(&app, req).await;
assert_eq!(resp.status(), http::StatusCode::OK);
let body_bytes = to_bytes(resp.into_body()).await.unwrap();
assert_eq!(body_bytes, "Hello from Actix!");
}
}
将示例程序复制进src/main.rs,然后运行:
cargo check
cargo test
没有问题我们接着下一步。
配置ci/cd文件
创建目录结构
mkdir .woodpecker
touch .woodpecker/main-branch.yaml
touch .woodpecker.yaml
配置文件.woodpecker.yaml:
pipeline: .woodpecker/
配置文件.woodpecker/main-branch.yaml:
when:
- event: [push, pull_request]
branch:
- main
steps:
- name: test
image: docker.io/library/rust:1.87.0
commands:
- echo testing
- cargo test
- name: build
image: docker.io/library/rust:1.87.0
depends_on:
- test
commands:
- echo building
- cargo build --release
- ls -lh target/release/ci-cd-test
- name: containize
image: quay.io/podman/stable
depends_on:
- build
environment:
CONTAINER_HOST: tcp://<你的局域网IP>:3333
commands:
- echo containize
- podman --remote rmi ci-cd-test:latest || true
- podman --remote build -t ci-cd-test:latest .
- name: deploy
image: quay.io/podman/stable
depends_on:
- containize
environment:
CONTAINER_HOST: tcp://<你的局域网IP>:3333
commands:
- echo deloying
- podman --remote stop cicd || true
- podman --remote rm -f cicd || true
- podman --remote run -d --name cicd -p 8080:8080 ci-cd-test:latest
这里需要解释几个点:
when结构:
when:
- event: [push, pull_request]
branch:
- main
这里指的是pipeline的触发只能是push,pull_request事件,目标分支是main。 2. steps结构:
steps:
- name: test
image: docker.io/library/rust:1.87.0
commands:
- echo testing
- cargo test
这里是定义流水线步骤的,以下是模板:
steps:
- name: <步骤名称>
image: <镜像名称>
commands:
- <需要执行的命令>
这里的镜像名称是每个步骤用的容器都是隔离的,因此镜像可以随着步骤的变化而变化(但是产物是共享的,后面会提到),并且绑定的是你宿主机的podman所以用的和下载的镜像都是宿主机的(后面会提到)。 你会看见这里有一个podman --remote这个主要用于连接宿主机的podman,因为在build阶段和deploy阶段都要使用podman并且指向都是宿主机,而且这是一个podman-in-podman环境,所以无法直接使用sock进行通信(有可行的方法但是不安全)。 3. 整体步骤解析: 1. 首先运行测试 2. 其次运行构建命令 3. 得到构建产物之后,运行生产镜像命令 4. 通过生产镜像对容器进行部署
配置运行镜像文件Dockerfile
创建Dockerfile:
touch Dockerfile
文件内容:
FROM debian:bookworm-slim
# 复制构建好的二进制文件
COPY target/release/ci-cd-test /usr/local/bin/ci-cd-test
# 公开服务端口,比如 8080
EXPOSE 8080
# 启动二进制
CMD ["ci-cd-test"]
将文件内容复制粘贴到Dockerfile里面
至此示例项目创建完成。接下来我们进行镜像下载。
镜像下载
运行以下命令进行镜像下载,podman配置的时候拒绝推断镜像来源,需要显性标注,如:docker.io/woodpeckerci/woodpecker-server:v3(这里就表明了要从docker.io上面安装):
podman pull quay.io/podman/stable:latest
podman pull docker.gitea.com/gitea:1.24.1-rootless
podman pull docker.io/library/debian:stable-slim
podman pull docker.io/library/debian:bookworm-slim
podman pull docker.io/woodpeckerci/woodpecker-server:v3
podman pull docker.io/woodpeckerci/woodpecker-agent:v3
podman pull docker.io/woodpeckerci/plugin-git:2.6.5
podman pull docker.io/library/rust:1.87.0
镜像较大和有些镜像本身有点大,所以需要耐心等待,如果受制于网络原因无法在docker.io下载,可以通过镜像站下载(需要改变docker.io或者是docker.io/library)。
整体环境配置以及部署
首先去到你想要管理的目录,然后创建gitea和woodpecker的容器配置文件夹
mkdir gitea
mkdir woodpecker
gitea配置
进入gitea文件夹,创建docker-compose.yml文件,config和data文件夹:
cd gitea
mkdir config
mkdir data
touch docker-compose.yml
docker-compose.yml内容如下:
version: "3"
services:
server:
image: docker.gitea.com/gitea:1.24.1-rootless
restart: always
volumes:
- ./data:/var/lib/gitea:U
- ./config:/etc/gitea:U
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
ports:
- "3000:3000"
- "2222:2222"
这里需要解释一下:U,因为我们运行的镜像是一个rootless镜像,所以需要用户权限同步。那么就需要使用:U去注明。它会自动且递归地将挂载点内所有文件的所有权(UID 和 GID)更改为容器内当前运行用户的 UID 和 GID。 将这个内容复制进docker-compose.yml然后运行:
podman-compose up
这里使用了非后台运行,以便检查错误(运行前请注意端口是否给占用)。运行之后,访问:<你的ip>:3000查看网页是否呈现:
这里可以直接配置你需要选定的数据库,这里为了方便我们来选择SQLite3,然后点击立即安装,稍等片刻之后,会看到这个页面:
点击这个注册账号,并填上信息,并点击注册账号:
(目前我尚未验证这个邮箱是否有效,所以写了一个假邮箱) 注册完成之后会进入到这个界面:
然后我们来创建一个组织,并将其命名为ci-test: 
接着点击创建组织,然后会看到这个界面:
然后我们点击仓库列表的+号,就会进入仓库创建界面,然后我们将仓库名称配置成rust-ci-cd-test,然后点击创建仓库:
创建完成会进入到这个界面:
接着我们点击右上角的头像,头像会弹出一个下拉菜单,单击设置:
然后点击应用,下拉到底看到创建新的 OAuth2 应用程序这一栏:
内容如下,这个http://192.168.2.142:8000/authorize里面的192.168.2.142需要更换成你的ip:
然后点击创建应用,创建成功之后会跳转到这个页面:
此时需要把红框里面的值都要复制下来,后面有用。目前为止,gitea配置已经完成了。 完成之后我们需要将gitea结束掉,然后让他在后台运行:
podman-compose up -d
woodpecker配置
进入woodpecker文件夹,创建docker-compose.yml文件,woodpecker_config和woodpecker_data文件夹:
cd woodpecker
mkdir woodpecker_config
mkdir woodpecker_data
touch docker-compose.yml
然后我们还需要生成一串加密密钥:
openssl rand -hex 32
他长这样:
7361384a1f1f2b7cbc0c58de259ca76a385e21f9ee27b074d723102d7fba1a78
需要把类似的这一串字符放到WOODPECKER_AGENT_SECRET这里面来 接着我们还要获取宿主机的podman sock:
podman info --format '{{.Host.RemoteSocket.Path}}'
获取到的结果:
/run/user/1000/podman/podman.sock
docker-compose.yml的文件内容:
services:
woodpecker-server:
image: docker.io/woodpeckerci/woodpecker-server:v3
ports:
- 8000:8000
volumes:
- woodpecker-server-data:/var/lib/woodpecker/
environment:
- WOODPECKER_OPEN=true
- WOODPECKER_HOST=http://<你的ip>:8000
- WOODPECKER_GITEA=true
- WOODPECKER_GITEA_URL=http://<你的ip>:3000
- WOODPECKER_GITEA_CLIENT=<上面复制的客户端ID>
- WOODPECKER_GITEA_SECRET=<上面复制的客户端SECRET>
- WOODPECKER_AGENT_SECRET=<刚刚通过ssl生成的加密密钥>
woodpecker-agent:
image: docker.io/woodpeckerci/woodpecker-agent:v3
command: agent
restart: always
depends_on:
- woodpecker-server
volumes:
- woodpecker-agent-config:/etc/woodpecker
- <通过命令行获取到的podman sock>:/var/run/docker.sock:U
environment:
- WOODPECKER_SERVER=woodpecker-server:9000
- WOODPECKER_AGENT_SECRET=<刚刚通过ssl生成的加密密钥>
volumes:
woodpecker-server-data:
driver: local
driver_opts:
type: 'bind'
o: 'bind'
device: './woodpecker_data'
woodpecker-agent-config:
driver: local
driver_opts:
type: 'bind'
o: 'bind'
device: './woodpecker_config'
将上面需要替换的字段替换完成之后,把他复制进docker-compose.yml 然后运行:
podman-compose up
运行之后,访问<你的ip>:8000,你会看到一个:
然后单击前往授权,然后会跳转到这个页面:
然后点击应用授权,之后你就会进入后台:
接着我们点击添加仓库,然后点击启用:
点击启用之后,你会看到:
至此,woodpecker的配置就完成了。
仓库配置
我们首先要去gitea配置文件中添加这一条(路径就是/<你存放gitea的路径>/gitea/config/app.ini):
[webhook]
ALLOWED_HOST_LIST = <你的ip>, <你的ip>:8000
其次我们要生成ssh密钥:
ssh-keygen -t ed25519 -C "admin@example.com"
生成出来的结果:
Generating public/private ed25519 key pair.
Enter file in which to save the key (/home/lexon/.ssh/id_ed25519): /home/lexon/.ssh/cicdtest #这里是写你期望将密钥存放的地方
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/lexon/.ssh/cicdtest
Your public key has been saved in /home/lexon/.ssh/cicdtest.pub
The key fingerprint is:
SHA256:KwA2jcAUWrX8kNdQ9nYQ1/okh23r69JcCm7hE5Rb1SA admin@example.com
The key's randomart image is:
+--[ED25519 256]--+
|+oo.. ..o o.E....|
|.+ + o + . o .. o|
|. = * . . o .= . |
| . o + . .* * |
| . . S . O . |
| . . = o .|
| . . o B o |
| . * = |
| . +o. |
+----[SHA256]-----+
然后将/<你的密钥地址>/cicdtest.pub里面的内容打印出来,然后登录gitea,点击头像,然后点击设置:
然后我们找到:
点击增加密钥,我们会看到这个界面,将公钥和名称填写上去,然后点击增加密钥:
完成之后,我们给本地的ssh添加一个alias,文件路径~/.ssh/config,没有的话直接创建一个,内容如下:
Host gitea
HostName <你的ip>
User git
Port 2222
IdentityFile <密钥路径>
将内容替换之后写入文件。 之后,我们再去到示例代码的文件目录:
cd ci-cd-test
提交文件内容:
git add .
git commit -m "ci:test"
git branch -m main
添加远程库:
git remote set-url origin gitea:/ci-test/rust-ci-cd-test.git
至此,仓库正式配置完成。
ci/cd测试
先开启podman远程登录:
podman system service --time=0 tcp:0.0.0.0:3333 &
然后接着上一步完成之后直接推送:
git push origin main
之后登录<你的ip>:8000看到流水线运行成功了就证明流程正式完成了。 这个状态就是正在运行流水线,因为第一次运行需要获取资源,所以比较慢:
这个状态就表示构建完成了:
接着可以在podman看到构建好的镜像:
服务容器也在运作:
服务都可访问:
至此,整一套单机部署的ci/cd流程正式结束。
总结与展望
总的来说这一套方案比起Jenkins来说要好不少,而且比起gitlab消耗的资源更低,不过在部署中还是发现一些现象:
- 在流水线构建过程中貌似不能使用
podman - 为了演示,将构建服务器和部署服务器二合一了,在生产来说是需要分离的。
woodpecker看起来功能较为简陋,但是基本够用,本教程还没有演示如何使用插件,目前笔者还在探索中。 最后,这个方案对于一些小团队来说还是很实用的,避免付费的同时,代价至少也是比较明显,就是必须设置一台机器用于ci的,还能有效地管理容器化,未来打算探索:
- 探索其插件系统
- 集成容器仓库
- 集成流量监控
- 探索分布式极限
- 探索分环境部署,分支部署。
补充
如果发现了:
这表明你的gitea的app.ini文件的Webhook配置有问题,所以需要检查一下