guest@blog.cmj.tw: ~/posts $

Pipeline


New Job ~

因為新的工作開始學習新的技能。從無到有開始架設一個 Development Pipeline : Git, CI/CD 到 Cloud 環境。 目前使用的 Stack 是:nginx (host) / gitea (docker) / drone (docker) / postgres (docker),網路環境則是:

  • host 透過 mDNS 方式讓使用者透過 hostname 連線
  • 所有 web 服務透過 nginx 轉址到真實 docker port
  • gitea 對外開放 web (8003) 跟 ssh (2222)
  • drone 對外開放 web (8004)
  • postgres 僅 docker 內部可見

Git Server

主要選用 gitea 當作 Git Server:這是用 Go-lang 撰寫的輕量化 Git Server solution,輕量化也是我主要挑選的原因。 在比較 GitLab 還是覺得輕量化的 Web Server 應該才會是主流。

架設方面也蠻簡單:主要透過 docker-compose 就可以快速建立一個 gitea 環境

version: "2"

networks:
  gitea:
    external: false

services:
  server:
    image: gitea/gitea:latest
    environment:
      - USER_UID=1000
      - USER_GID=1000
    restart: always
    networks:
      - gitea
    volumes:
      - /var/lib/gitea:/data
    ports:
      - "80:3000"
      - "2222:22"

透過這樣設定,絕大多數的 gitea 設定檔都會在本機端的 /var/lib/gitea 下,像是 gitea 環境設定的 app.ini 就在 /var/lib/gitea/gitea/conf/ 路徑之下。

Drone

在安裝 drone 的時候遇到比較多的困難:首先 drone 是跟 git 透過 OAuth2 作認證跟使用 Webhook 來監控 git event、drone 跟 drone-runner 之間的互動,所以會同時使用到 docker 內部溝通與外部網路。在使用上 drone 建議使用 postgres 當作是 backend SQL storage,因此相關的 docker-compose 設定會是:

postgres:
  container_name: postgres
  image: postgres
  environment:
    - POSTGRES_USER=USR
    - POSTGRES_PASSWORD=PWD
    - POSTGRES_DB=drone
drone.local:
  container_name: drone.local
  image: drone/drone:1.6
  depends_on:
    - gitea
    - postgres
  ports:
    - "8004:80"
  volumes:
    - /var/lib/docker/drone:/var/lib/drone
    - /var/run/docker.sock:/var/run/docker.sock
  environment:
    - DRONE_SERVER_HOST=drone.local
    - DRONE_SERVER_PROTO=http
    # gitea setting
    - DRONE_GITEA_SERVER=http://git.local:8003/
    - DRONE_GITEA_CLIENT_ID=....
    - DRONE_GITEA_CLIENT_SECRET=....
    # DB backend
    - DRONE_DATABASE_DRIVER=postgres
    - DRONE_DATABASE_DATASOURCE=postgres://USR:PWD@postgres:5432/drone?sslmode=disable
    # CI/CD gitea user and password
    - DRONE_GIT_ALWAYS_AUTH=true
    - DRONE_GIT_USERNAME=GIT_USR
    - DRONE_GIT_PASSWORD=GIT_PWD
    # drone-agent secret_key
    - DRONE_RUNNER_CAPACITY=4
    - DRONE_RPC_SECRET=SECRET
  links:
    - "gitea:git.local"
drone-runner:
  container_name: drone-runner
  image: drone/drone-runner-docker:1.2
  command: agent
  depends_on:
    - drone.local
  volumes:
    - /var/lib/docker/drone-runner:/var/lib/drone
    - /var/run/docker.sock:/var/run/docker.sock
  links:
    - "gitea:git.local"
  environment:
    - DRONE_RPC_HOST=drone.local
    - DRONE_RPC_PROTO=http
    - DRONE_RPC_SECRET=SECRET

從設定檔可以看到執行的順序:gitea 跟 postgres 無關、drone 需要在兩者都好才會啟動、drone-runner 則會等 drone 啟動完成。 如果不想要將使用者的帳號、密碼寫死在 docker-compose.yml 中的話,可以使用 access token 替代:將 token 帶入到 DRONE_GIT_USERNAMEDRONE_GIT_PASSWORD 帶入 x-oauth-token 即可。

Network

在網路上設定上,因為 gitea 跟 drone 都有 Web UI 讓使用者操作。因此我使用 mDNS 當作是網域名稱讓使用者可以透過瀏覽器存取 。但是在 container 中沒辦法透過 mDNS 解析成 IP 位址,因此透過 docker-compose 中的 links 設定將網域名稱跟 container 做連結。

但是在 drone-runner 中這種方式就無法成功:因為 drone-runner 在 pipeline 中開啟的新 container 使用獨立的網路環境而導致 links 並沒有效果。因此需要在 docker host 上建立一個 DNS server 並且設定 /etc/docker/daemon.json 將所有 docker 額外設定一個 DNS server,這樣所有 docker container 都可以透過這個 DNS server 來解析 mDNS IP 位址。

mDNS

因為環境內沒有公共的 DNS server 可以使用,所以使用 mDNS 來讓大家可以直接存取到 Pipeline 的環境:使用 Avahi 當作 mDSN Server 控制。然而原生的 Avahi 只提供一個 domain name 使用,所以參考網路上的方式寫一個 avahi-alias@.service 服務,自動註冊新對 mDSN hostname:

[Unit]
Description = Publish %I as alias for %H.local via Avahi
Requires=avahi-daemon.service
After=avahi-daemon.service

[Service]
Type = simple
ExecStartPre=/usr/local/bin/pipeline_dns %I
ExecStart=/bin/bash -c "/usr/bin/avahi-publish -a -R %I $(ifconfig enp12s0 | awk -F ' *|:' '/inet /{print $3}')"

[Install]
WantedBy = multi-user.target

其中:使用 ifconfig 偵測特定 iface 當作對外介面、透過 awk 解析出 IP 位址並透過 avahi-publish 註冊 hostname。