chore(workflow,ci): update docker build process and add multi-arch support

Update the GitHub Actions workflow to include Go setup, multi-arch builds, and push multi-arch Docker images. Also, refactor the CSV service to improve logging and caching mechanism.
This commit is contained in:
wood chen 2024-11-16 05:34:37 +08:00
parent 981759be39
commit 61e84d87b8
3 changed files with 69 additions and 16 deletions

View File

@ -19,9 +19,22 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout - name: Checkout code
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v4
with:
go-version: '1.23'
- name: Build for amd64
run: |
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -a -installsuffix cgo -o bin/amd64/random-api .
- name: Build for arm64
run: |
CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -a -installsuffix cgo -o bin/arm64/random-api .
- name: Set up QEMU - name: Set up QEMU
uses: docker/setup-qemu-action@v3 uses: docker/setup-qemu-action@v3
@ -34,11 +47,11 @@ jobs:
username: woodchen username: woodchen
password: ${{ secrets.ACCESS_TOKEN }} password: ${{ secrets.ACCESS_TOKEN }}
- name: Build and push - name: Build and push multi-arch image
uses: docker/build-push-action@v5 uses: docker/build-push-action@v5
with: with:
context: . context: .
file: Dockerfile file: Dockerfile.run
push: true push: true
tags: woodchen/${{ env.IMAGE_NAME }}:latest tags: woodchen/${{ env.IMAGE_NAME }}:latest
platforms: linux/amd64,linux/arm64 platforms: linux/amd64,linux/arm64
@ -53,8 +66,8 @@ jobs:
host: ${{ secrets.SERVER_HOST }} host: ${{ secrets.SERVER_HOST }}
username: root username: root
key: ${{ secrets.SERVER_SSH_KEY }} key: ${{ secrets.SERVER_SSH_KEY }}
source: "public.zip" source: 'public.zip'
target: "/tmp" target: '/tmp'
- name: Execute deployment commands - name: Execute deployment commands
uses: appleboy/ssh-action@master uses: appleboy/ssh-action@master

23
Dockerfile.run Normal file
View File

@ -0,0 +1,23 @@
FROM --platform=$TARGETPLATFORM alpine:latest
WORKDIR /root/
# 安装必要的包
RUN apk --no-cache add ca-certificates tzdata tini
# 创建日志目录并设置权限
RUN mkdir -p /var/log/random-api && chmod 755 /var/log/random-api
# 根据目标平台复制对应的二进制文件
ARG TARGETARCH
COPY bin/${TARGETARCH}/random-api .
COPY public ./public
COPY public /tmp/public
COPY start.sh /start.sh
RUN chmod +x /start.sh
EXPOSE 5003
# 使用 tini 作为初始化系统
ENTRYPOINT ["/sbin/tini", "--"]
CMD ["/start.sh"]

View File

@ -28,11 +28,19 @@ func InitializeCSVService() error {
return fmt.Errorf("failed to load CSV paths: %v", err) return fmt.Errorf("failed to load CSV paths: %v", err)
} }
// 预加载所有CSV内容 // 获取一个CSVPathsCache的副本避免长时间持有锁
Mu.RLock() Mu.RLock()
defer Mu.RUnlock() pathsCopy := make(map[string]map[string]string)
for prefix, suffixMap := range CSVPathsCache { for prefix, suffixMap := range CSVPathsCache {
pathsCopy[prefix] = make(map[string]string)
for suffix, path := range suffixMap {
pathsCopy[prefix][suffix] = path
}
}
Mu.RUnlock()
// 使用副本进行初始化
for prefix, suffixMap := range pathsCopy {
for suffix, csvPath := range suffixMap { for suffix, csvPath := range suffixMap {
selector, err := GetCSVContent(csvPath) selector, err := GetCSVContent(csvPath)
if err != nil { if err != nil {
@ -113,51 +121,55 @@ func LoadCSVPaths() error {
} }
func GetCSVContent(path string) (*models.URLSelector, error) { func GetCSVContent(path string) (*models.URLSelector, error) {
log.Printf("开始获取CSV内容: %s", path)
Mu.RLock() Mu.RLock()
selector, exists := csvCache[path] selector, exists := csvCache[path]
Mu.RUnlock() Mu.RUnlock()
if exists { if exists {
log.Printf("从缓存中获取到CSV内容: %s", path)
return selector, nil return selector, nil
} }
var fileContent []byte var fileContent []byte
var err error var err error
// 获取环境变量中的基础URL
baseURL := os.Getenv(config.EnvBaseURL) baseURL := os.Getenv(config.EnvBaseURL)
if baseURL != "" { if baseURL != "" {
// 如果设置了基础URL构建完整的URL
var fullURL string var fullURL string
if strings.HasPrefix(baseURL, "http://") || strings.HasPrefix(baseURL, "https://") { if strings.HasPrefix(baseURL, "http://") || strings.HasPrefix(baseURL, "https://") {
// 如果baseURL已经包含协议,直接使用
fullURL = utils.JoinURLPath(baseURL, path) fullURL = utils.JoinURLPath(baseURL, path)
} else { } else {
// 如果没有协议,添加https://
fullURL = "https://" + utils.JoinURLPath(baseURL, path) fullURL = "https://" + utils.JoinURLPath(baseURL, path)
} }
log.Printf("尝试从URL获取: %s", fullURL) log.Printf("尝试从URL获取: %s", fullURL)
// 创建HTTP客户端
client := &http.Client{ client := &http.Client{
Timeout: config.RequestTimeout, Timeout: config.RequestTimeout,
} }
resp, err := client.Get(fullURL) resp, err := client.Get(fullURL)
if err != nil { if err != nil {
log.Printf("HTTP请求失败: %v", err)
return nil, fmt.Errorf("HTTP请求失败: %w", err) return nil, fmt.Errorf("HTTP请求失败: %w", err)
} }
defer resp.Body.Close() defer resp.Body.Close()
if resp.StatusCode != http.StatusOK { if resp.StatusCode != http.StatusOK {
log.Printf("HTTP请求返回非200状态码: %d", resp.StatusCode)
return nil, fmt.Errorf("HTTP请求返回非200状态码: %d", resp.StatusCode) return nil, fmt.Errorf("HTTP请求返回非200状态码: %d", resp.StatusCode)
} }
fileContent, err = io.ReadAll(resp.Body) fileContent, err = io.ReadAll(resp.Body)
if err != nil { if err != nil {
log.Printf("读取响应内容失败: %v", err)
return nil, fmt.Errorf("读取响应内容失败: %w", err) return nil, fmt.Errorf("读取响应内容失败: %w", err)
} }
log.Printf("成功读取到CSV内容长度: %d bytes", len(fileContent))
} else { } else {
// 如果没有设置基础URL从本地文件读取 // 如果没有设置基础URL从本地文件读取
fullPath := filepath.Join("public", path) fullPath := filepath.Join("public", path)
@ -170,6 +182,8 @@ func GetCSVContent(path string) (*models.URLSelector, error) {
} }
lines := strings.Split(string(fileContent), "\n") lines := strings.Split(string(fileContent), "\n")
log.Printf("CSV文件包含 %d 行", len(lines))
uniqueURLs := make(map[string]bool) uniqueURLs := make(map[string]bool)
var fileArray []string var fileArray []string
for _, line := range lines { for _, line := range lines {
@ -180,11 +194,14 @@ func GetCSVContent(path string) (*models.URLSelector, error) {
} }
} }
log.Printf("处理后得到 %d 个唯一URL", len(fileArray))
selector = models.NewURLSelector(fileArray) selector = models.NewURLSelector(fileArray)
Mu.Lock() Mu.Lock()
csvCache[path] = selector csvCache[path] = selector
Mu.Unlock() Mu.Unlock()
log.Printf("CSV内容已缓存: %s", path)
return selector, nil return selector, nil
} }