admin

docker pull 通过 docker-group 既可以拉取 docker-hosted 镜像又可以拉取 docker-proxy 的镜像。

由于 nexus3 的限制,如果只代理至 docker-group 的话,无法推送镜像,所以需要将推送代理至 docker-hosted

默认的 GETHEAD 请求代理至 docker-group,其余的 PUT POST 请求代理至 docker-group

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
http {

map $request_method $repo_name {
default hosted;
GET group;
HEAD group;
}

server {

location /v2/ {
client_max_body_size 8000M;
rewrite /v2/(.*) /repository/docker-$repo_name/v2/$1 break;
proxy_pass http://${IP}:8081;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Real-PORT $remote_port;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}

}

Proto & Buf

  • buf.yaml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# yaml-language-server: $schema=https://json.schemastore.org/buf.json

version: v2
deps:
- buf.build/googleapis/googleapis
- buf.build/envoyproxy/protoc-gen-validate
- buf.build/grpc-ecosystem/grpc-gateway
lint:
use:
- BASIC
ignore_only:
IMPORT_NO_PUBLIC:
- common/common.proto
breaking:
use:
- FILE
  • buf.gen.yaml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
# yaml-language-server: $schema=https://json.schemastore.org/buf.gen.json

version: v2
plugins:
# https://github.com/protocolbuffers/protobuf-go
- local: protoc-gen-go
out: gen_go
opt:
- paths=source_relative

# https://github.com/grpc/grpc-go
- local: protoc-gen-go-grpc
out: gen_go
opt:
- paths=source_relative

# github.com/envoyproxy/protoc-gen-validate
- local: protoc-gen-validate
out: gen_go
opt:
- paths=source_relative
- lang=go

# github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-grpc-gateway
# - local: protoc-gen-grpc-gateway
# out: gen_go
# opt:
# - paths=source_relative

# github.com/go-kratos/kratos/cmd/protoc-gen-go-http/v2
- local: protoc-gen-go-http
out: gen_go
opt:
- paths=source_relative

# github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2
- local: protoc-gen-openapiv2
strategy: all
out: gen_doc
opt:
- allow_merge=true
- include_package_in_tags=true
- fqn_for_openapi_name=true
- use_go_templates=true
- disable_default_errors=true
- enums_as_ints=true
- output_format=yaml
- preserve_rpc_order=true

流水线

服务连接管理

webui

构建集群

webui

生成并发布 Proto

api-pb 所生成的 go 代码推送至 api-go 仓库,以便 goprivate 使用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
defaultWorkspace: api_pb

sources:
api_pb:
type: codeup
endpoint: git@codeup.aliyun.com:starudream/os/api-pb.git
certificate:
type: serviceConnection
serviceConnection: ${云效Codeup服务连接ID}
triggerEvents:
- push
api_go:
type: codeup
endpoint: git@codeup.aliyun.com:starudream/os/api-go.git
certificate:
type: serviceConnection
serviceConnection: ${云效Codeup服务连接ID}
triggerEvents:
- tagPush # 防止 push 时触发该工作流

stages:
proto:
jobs:
gen:
timeoutMinutes: 30
runsOn:
group: private/${私有构建集群ID}
steps:
gen:
step: CustomEnvironmentBuild
with:
image: registry.cn-shanghai.aliyuncs.com/starudream/go-dev
privateRegistry: true
certificate:
type: serviceConnection
serviceConnection: ${ACR服务连接ID}
run: |
go version
buf --version
make clean gen
tar -czf pb_gen_${PIPELINE_ID}_${BUILD_NUMBER}.tar.gz gen_go gen_doc
mkdir -p /cache/proto/ && mv -f pb_gen_${PIPELINE_ID}_${BUILD_NUMBER}.tar.gz /cache/proto/

pub:
timeoutMinutes: 30
runsOn:
group: private/${私有构建集群ID}
vm: true
needs: gen
steps:
go:
step: Command
with:
run: |
pushd /data/yunxiao/cache/proto/ >/dev/null
rm -rf ${PIPELINE_ID}_${BUILD_NUMBER} && mkdir -p ${PIPELINE_ID}_${BUILD_NUMBER}
tar -xzf pb_gen_${PIPELINE_ID}_${BUILD_NUMBER}.tar.gz -C ${PIPELINE_ID}_${BUILD_NUMBER}
mv -f ${WORK_SPACE}/api_go/.git ${PIPELINE_ID}_${BUILD_NUMBER}/gen_go/.git

pushd ${PIPELINE_ID}_${BUILD_NUMBER}/gen_go >/dev/null
git config --global user.name "ci-bot"
git config --global user.email "ci-bot@mail.starudream.cn"
git add . && git commit -m "${CI_COMMIT_ID_1} -> ${CI_COMMIT_TITLE_1}" && git push
popd >/dev/null

rm -rf ${PIPELINE_ID}_${BUILD_NUMBER}
webui

GoPrivate

Configuration

1
2
3
4
go env -w CGO_ENABLED=0
go env -w GO111MODULE=on
go env -w GOPRIVATE=codeup.aliyun.com
go env -w GOPROXY=https://goproxy.cn,direct

ssh key

1
replace codeup.aliyun.com/starudream/os/api-go => codeup.aliyun.com/starudream/os/api-go.git

需要手动指定版本,不能更新,比较麻烦。

netrc

  • windows: $HOME/_netrc
  • linux: $HOME/.netrc

可以使用 go get -u 自动更新。

Backend

1
2
3
4
5
6
7
8
module codeup.aliyun.com/starudream/os/backend

go 1.23.0

require (
codeup.aliyun.com/starudream/os/api-go v0.0.0-20241111023212-d66b05b24117
codeup.aliyun.com/starudream/os/lib-go v0.0.0-20241111082247-43cf457f0683
)

docker-compose.yaml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
networks:
ollama:
external: true

services:
ollama:
image: ollama/ollama:0.4.1
container_name: ollama
restart: always
networks:
- ollama
deploy:
resources:
reservations:
# noinspection ComposeUnknownValues
devices:
- driver: nvidia
capabilities: [ gpu ]
environment:
- OLLAMA_KEEP_ALIVE=24h
volumes:
- "${COMPOSE_DATA_DIR:-/data}/ollama/ollama:/root/.ollama"
ports:
- "11434:11434"

ollama-webui:
image: ghcr.io/open-webui/open-webui:0.3.35
container_name: ollama-webui
restart: always
networks:
- ollama
environment:
- OLLAMA_BASE_URL=http://ollama:11434
- ENV=dev
- WEBUI_AUTH=False
- WHISPER_MODEL_AUTO_UPDATE=False
- RAG_EMBEDDING_MODEL_AUTO_UPDATE=False
- RAG_RERANKING_MODEL_AUTO_UPDATE=False
volumes:
- "${COMPOSE_DATA_DIR:-/data}/ollama/webui:/app/backend/data"
ports:
- "8080:8080"

拉取模型

  • https://ollama.com/library
  • https://ollama.com/library/qwen2.5
1
docker exec -it ollama bash
1
2
3
ollama list
ollama pull qwen2.5
ollama list

使用模型

打开 http://${IP}:8080 访问 webui,左上角选择模型 qwen2.5

webui
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
+-----------------------------------------------------------------------------------------+
| NVIDIA-SMI 565.57.01 Driver Version: 565.57.01 CUDA Version: 12.7 |
|-----------------------------------------+------------------------+----------------------+
| GPU Name Persistence-M | Bus-Id Disp.A | Volatile Uncorr. ECC |
| Fan Temp Perf Pwr:Usage/Cap | Memory-Usage | GPU-Util Compute M. |
| | | MIG M. |
|=========================================+========================+======================|
| 0 Tesla T4 Off | 00000000:00:10.0 Off | 0 |
| N/A 33C P0 25W / 70W | 5255MiB / 15360MiB | 0% Default |
| | | N/A |
+-----------------------------------------+------------------------+----------------------+
| 1 Tesla T4 Off | 00000000:00:11.0 Off | 0 |
| N/A 24C P8 9W / 70W | 3MiB / 15360MiB | 0% Default |
| | | N/A |
+-----------------------------------------+------------------------+----------------------+

+-----------------------------------------------------------------------------------------+
| Processes: |
| GPU GI CI PID Type Process name GPU Memory |
| ID ID Usage |
|=========================================================================================|
| 0 N/A N/A 326540 C ...unners/cuda_v12/ollama_llama_server 5252MiB |
+-----------------------------------------------------------------------------------------+

查看 GPU 信息

1
lspci | grep -i nvidia

驱动

1
yum install -y kernel-devel gcc

Test

1
nvidia-smi

容器工具包

  • Install Guide
  • USTC Mirrors

Install

1
2
curl -s -L https://mirrors.ustc.edu.cn/libnvidia-container/stable/rpm/nvidia-container-toolkit.repo | \
sed 's#nvidia.github.io#mirrors.ustc.edu.cn#g' | tee /etc/yum.repos.d/nvidia-container-toolkit.repo
1
yum install -y nvidia-container-toolkit

Configure

1
nvidia-ctk runtime configure --runtime=docker

equivalent to edit /etc/docker/daemon.json add following content:

1
2
3
4
5
6
7
8
{
"runtimes": {
"nvidia": {
"args": [],
"path": "nvidia-container-runtime"
}
}
}

then restart docker daemon

1
systemctl restart docker

Test

1
docker run --rm --runtime=nvidia --gpus all ubuntu:24.04 nvidia-smi
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
+-----------------------------------------------------------------------------------------+
| NVIDIA-SMI 565.57.01 Driver Version: 565.57.01 CUDA Version: 12.7 |
|-----------------------------------------+------------------------+----------------------+
| GPU Name Persistence-M | Bus-Id Disp.A | Volatile Uncorr. ECC |
| Fan Temp Perf Pwr:Usage/Cap | Memory-Usage | GPU-Util Compute M. |
| | | MIG M. |
|=========================================+========================+======================|
| 0 Tesla T4 Off | 00000000:00:10.0 Off | 0 |
| N/A 24C P8 8W / 70W | 3MiB / 15360MiB | 0% Default |
| | | N/A |
+-----------------------------------------+------------------------+----------------------+
| 1 Tesla T4 Off | 00000000:00:11.0 Off | 0 |
| N/A 25C P8 9W / 70W | 3MiB / 15360MiB | 0% Default |
| | | N/A |
+-----------------------------------------+------------------------+----------------------+

+-----------------------------------------------------------------------------------------+
| Processes: |
| GPU GI CI PID Type Process name GPU Memory |
| ID ID Usage |
|=========================================================================================|
| No running processes found |
+-----------------------------------------------------------------------------------------+

Import

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#!/usr/bin/env bash

set -e

REGISTRY="hub.starudream.local"

function load() {
if [ ! -f "$1" ]; then
echo "file not found: $1"
exit 1
fi
res=$(docker image load -i "$1" 2>/dev/null | grep "Loaded image:" || true)
images=${res//Loaded image: /}
for image in $images; do
echo "-> loaded $image"
if [ -z $REGISTRY ]; then
continue
fi
cnt=$(echo "$image" | awk -F/ "{print NF-1}")
name=$image
case $cnt in
0)
name="library/$image"
;;
1) ;;
*)
part=${image%/*}
left=${part%/*}
name=${image/$left\//}
;;
esac
docker image tag "$image" "$REGISTRY/$name"
docker image push "$REGISTRY/$name"
echo "=> pushed $image to $REGISTRY/$name"
docker image rm "$REGISTRY/$name"
docker image rm "$image" || true
done
}

cnt=$(grep -c $REGISTRY /etc/hosts || true)
if [ "$cnt" -eq 0 ]; then
REGISTRY=""
elif [ "$PUSH_REGISTRY" = 0 ]; then
REGISTRY=""
fi

if [ -z "$1" ]; then
files=$(find . -type f -name "*.tar.gz" | sort -u)
for file in $files; do
echo ">> found $file"
load "$file"
done
else
load "$1"
fi

Export

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#!/usr/bin/env bash

set -e

CUR_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
ROOT_DIR=$(dirname "$CUR_DIR")

mkdir -p "$ROOT_DIR"/images

function save() {
if [ ! -f "$1" ]; then
echo "file not found: $1"
exit 1
fi
images=$(docker-compose -f "$1" config --images | sort -u)
for image in $images; do
name=${image////-}
name=${name//:/_}
if [ -f "$ROOT_DIR/images/$name.tar.gz" ]; then
echo "-> exist $name.tar.gz, skip"
continue
fi
docker image pull --platform linux/amd64 "$image"
echo "=> save $image to $name.tar.gz"
docker image save "$image" | gzip > "$ROOT_DIR/images/$name.tar.gz"
done
}

if [ -z "$1" ]; then
files=$(find . -type f -name "docker-compose*.yaml" | sort -u)
for file in $files; do
echo ">> found $file"
save "$file"
done
else
save "$1"
fi