肥大化したCI/CDの問題
以前、小さなPythonプロジェクトを管理するために、月額5ドルのDigitalOcean dropletでJenkinsを実行しようとしたことがありますが、それは悲劇でした。数分もしないうちに、Jenkinsがアイドル状態で1.5GB近いRAMを消費したため、LinuxのOOM (Out of Memory) killerによってプロセスが強制終了されました。個人開発者や小規模なチームにとって、従来のCI/CDツールは「牛刀をもって鶏を割く」ようなものに感じられることがよくあります。
ほとんどのエンタープライズ向けツールは、軽量なVPS環境ではなく、大規模なクラスター向けに設計されています。もしGiteaのようなGitサーバーをセルフホストしているなら、それと同じくらい効率的なパイプラインが欲しくなるはずです。待機中のRAM消費が100MB未満で、実際にコードをビルドするときだけCPUサイクルを消費するような、邪魔にならないシステムが必要です。
そこでDrone CIの出番です。これは完全にコンテナファーストのアーキテクチャで構築されています。パイプラインのすべてのステップは単なるDockerコンテナであり、起動してジョブを実行し、終了すると消えます。Giteaと組み合わせることで、合計RAMがわずか1GBのサーバーでも、GitHubのような体験を得ることができます。
なぜDrone CIとGiteaなのか?
GiteaはGoで書かれた軽量なGitサービスです。Raspberry Piでも動作するほど高速でありながら、洗練されたUIを提供します。Drone CIもこの哲学を共有しています。複雑なプラグインのネットワークの代わりに、Droneはリポジトリのルートにあるシンプルな.drone.ymlファイルを使用します。
この「Pipeline as Code」モデルにより、ビルドロジックはソフトウェアと共に進化します。Droneはコンテナネイティブであるため、ホストサーバーにNode.js、Python、Goなどを手動でインストールする必要はありません。プロジェクトでNode 20が必要な場合は、そのステップでnode:20イメージを指定するだけです。この分離により「自分のマシンでは動くのに」という問題を回避し、ホストOSをクリーンに保つことができます。
このスタックをマスターすることは大きなアドバンテージになります。Kubernetesのような急峻な学習曲線を辿ることなく、コンテナオーケストレーションと環境分離の基本原則を学ぶことができます。
軽量スタックのセットアップ
Docker Composeを使用して、GiteaとDrone CIの両方を1台のサーバーにデプロイします。共有ブリッジネットワークを使用することで、すべてのポートを外部に公開することなく、コンテナ間で安全に通信できます。
ステップ1:システム要件
1GBのRAMと1つのCPUコアを備えた基本的なUbuntu 22.04サーバーがあれば、このセットアップには十分です。DockerとComposeプラグインがインストールされていることを確認してください。また、OAuthコールバックを正しく処理するために、ドメイン名(例:ci.yourdomain.com)も必要です。
ステップ2:Docker Composeの設定
専用のディレクトリを作成し、設定ファイルを初期化します:
mkdir devops-stack && cd devops-stack
touch docker-compose.yml
以下の設定を挿入します。このセットアップでは、Giteaサービス、Drone Server(頭脳)、Drone Runner(実行部)を定義します。
version: '3.8'
services:
gitea:
image: gitea/gitea:1.21
container_name: gitea
restart: always
networks:
- ci-network
ports:
- "3000:3000"
- "222:22"
volumes:
- ./gitea:/data
drone-server:
image: drone/drone:2
container_name: drone-server
ports:
- "8080:80"
volumes:
- ./drone:/data
restart: always
networks:
- ci-network
environment:
- DRONE_GITEA_CLIENT_ID=${GITEA_CLIENT_ID}
- DRONE_GITEA_CLIENT_SECRET=${GITEA_CLIENT_SECRET}
- DRONE_GITEA_SERVER=http://gitea:3000
- DRONE_RPC_SECRET=${DRONE_RPC_SECRET}
- DRONE_SERVER_HOST=ci.yourdomain.com
- DRONE_SERVER_PROTO=http
drone-runner:
image: drone/drone-runner-docker:1
container_name: drone-runner
restart: always
networks:
- ci-network
depends_on:
- drone-server
volumes:
- /var/run/docker.sock:/var/run/docker.sock
environment:
- DRONE_RPC_PROTO=http
- DRONE_RPC_HOST=drone-server
- DRONE_RPC_SECRET=${DRONE_RPC_SECRET}
- DRONE_RUNNER_CAPACITY=2
- DRONE_RUNNER_NAME=main-runner
networks:
ci-network:
driver: bridge
ステップ3:GiteaとDroneの連携
Droneは独自のユーザーデータベースを持たず、認証をGiteaに依存します。まず、Giteaを起動します:
docker-compose up -d gitea
http://your-ip:3000にアクセスし、初期設定を完了させます。- ログインし、設定 -> アプリケーション -> OAuth2アプリケーション に移動します。
- 「Drone CI」という名前で新しいアプリケーションを作成します。
- リダイレクトURI を
http://ci.yourdomain.com/loginに設定します。 - クライアントID と クライアント機密情報 をすぐにメモしておきます。
ステップ4:シークレットの保護
.env ファイルを作成します。これにより、機密情報をメインのComposeファイルから分離できます。RunnerとServerが安全に通信できるように、RPCシークレットには強力なランダム文字列を使用してください。
GITEA_CLIENT_ID=your_id_here
GITEA_CLIENT_SECRET=your_secret_here
DRONE_RPC_SECRET=$(openssl rand -hex 16)
ステップ5:デプロイ
単一のコマンドでスタック全体を起動します:
docker-compose up -d
http://ci.yourdomain.com にアクセスしてください。Droneは認証のためにGiteaにリダイレクトします。「承認」をクリックすると、GiteaのリポジトリがDroneのダッシュボードに表示され、有効化できるようになります。
ステップ6:最初のパイプラインの作成
システムをテストするために、任意のリポジトリのルートに .drone.yml ファイルを追加します。以下は、Node.jsアプリケーションの実用的な例です:
kind: pipeline
type: docker
name: test-and-deploy
steps:
- name: install-dependencies
image: node:20-slim
commands:
- npm install
- npm test
- name: notify-success
image: plugins/webhook
settings:
urls: https://discord.com/api/webhooks/your-id
content: "ビルド成功!"
このコードをプッシュした瞬間、GiteaはDroneにWebhookを送信します。Droneは node:20-slim イメージをプルし、テストを実行し、終了後に後片付けを行います。このビルド中の総リソース使用量は?通常、200MBのRAM未満です。
まとめ
CI/CDをセルフホストするために、高額な費用や大量のリソースは必要ありません。Docker、Drone、Giteaを組み合わせることで、プライベートで高速、かつ驚くほど効率的なプロフェッショナルグレードのパイプラインを構築できました。もうクラウドプロバイダーのクレジットや、肥大化したJavaアプリケーションに縛られることはありません。
さらにステップアップするには、CaddyやNginxなどのリバースプロキシとLet’s Encryptの導入を検討してください。これによりHTTPSが提供され、標準の443ポートですべてを実行できるようになります。YAML構文に慣れたら、並列ビルドステップやプライベートレジストリへの自動Dockerイメージアップロードなどの高度な機能を試してみてください。

