🕌

月5000円で月額約26万円相当のホスティング環境を手に入れた話

に公開

はじめに

はじめまして!株式会社カウンターワークスでエンジニア一年目としてフロントエンドエンジニアをしている福島です。
ジュニアは作って学べ!ということでコスト気にせず好きにホスティングできる環境がほしいなぁと思っていました。
今回、ある程度整ったオンプレ環境を作れたので紹介します。

もともとはKubernetesをメインに使って構築しようと思っていたのですが、なかなかハードかつtoo muchなのでDocker Swarmに切り替えました、、
ざっくり概要としては cloudflare tunnelで外部公開して、Docker Swarmでコンテナ管理、traefikで各コンテナにルーティングしています。

構成

なぜか自宅にまあまあなスペックの自作PC(Ubuntu24.04)が3台あるのでこちらがリソースになります。

インターネット
    ↓
Cloudflare (DNS + Proxy + Tunnel)
    ↓
自宅ルーター (192.168.11.0/24)
    ↓
┌─────────────────────────────────────────┐
│  Docker Swarm Cluster                   │
│                                         │
│  ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│  │ PC1      │ │ PC2      │ │ PC3      │ │
│  │ Master   │ │ Worker   │ │ Worker   │ │
│  └──────────┘ └──────────┘ └──────────┘ │
│                                         │
│  - Traefik (L7 Reverse Proxy)           │
│  - Applications (Rails, etc)            │
│  - Databases (MySQL, PostgreSQL)        │
│  - Monitoring (Dozzle)                  │
│  - CI/CD (GitHub Actions Runner)        │
└─────────────────────────────────────────┘

トラフィックフロー

1. ユーザー(ブラウザ)
   ↓ HTTPS
2. Cloudflare DNS (cloudflareでDNS登録してるドメイン)
   ↓ Cloudflare Proxy(DDoS防御、WAF)
3. Cloudflare Tunnel (暗号化トンネル)
   ↓ HTTP
4. MasterNode(PC1): Traefik 
   ↓ Host-based Routing
5. Docker Swarm Overlay Network (traefik-public)
   ↓ Service Discovery
6. Application Container
   ↓ TCP/3306
7. WorkerNode(PC2・PC3): いろんなアプリケーションやDB

技術スタック紹介

Cloudflareめちゃくちゃ安いかつ便利ですよね、一番好きなサービスかもしれないです。
また、カウンターワークスでもかなりお世話になっているサービスになります。
今回使用した技術やサービスを紹介します。

Cloudflare Tunnel

まず、外部公開するところではCloudflare Tunnelを使ってセキュアかつ簡単に公開できるようにしています。
cloudflare tunnelは好きなPCにcloudflaredをインストールしてcloudflareとのtunnelを作成し、(cloudflareでDNS管理している)ドメインに紐づけることで、対象のドメインに来たアクセスをcloudflareが最初に受け、その後cloudflareで設定したアクセス方法でクライアントとPCが接続できるようになります。
(cloudflare docsからお借りしました。)

こうすることで、固定IPを取得したり、自宅のルーターでポートを開放する必要がなくかなり楽に外部公開が可能です。
また、クライアント to Cloudflare間のリクエストは自動で暗号化してくれるのでSSL周りの設定も不要になります。
(通信プロトコルも選べるのでssh接続も可能で、サーバーに入って作業するときもtunnelを使っています)

ただ、複数のサービスをホスティングするとなったときに、都度tunnelにルートの追加やDNS設定を行わないといけなかったり、複数台のサーバー運用するときにtunnelを複数用意してcloudflareのproxy上で振り分け・管理するのが大変なので、MasterとなるPCをおいてすべてそこで受けた後にtraefikを使って各PC及びサービスにルーティングしています。

Traefikによるルーティング

traefikはリバースプロキシを設定できるOSSのソフトウェアで、ダッシュボードも立ち上がるので視覚的にも優しくルーティング管理できるので採用しました。
(後続のdocker-swarmとも連携しやすい)

中身としては、cloudflare tunnelで *.example.com をMasterPCの http://localhost:80 に繋いであげて、xxx.example.com にくるリクエストをすべてMasterPCに送ります。
MasterPCに来たリクエストをtraefikがRequestURLをみて、事前に紐づけられたrouteのruleから然るべきコンテナにリクエストを流しています。
docker-compose.swarm.yml

version: '3.8'

services:
  whoami2:
    image: traefik/whoami
    networks:
      - traefik-public
    deploy:
      replicas: 2
      placement:
        constraints:
          - node.role == worker
      labels: <= ここ
        - "traefik.enable=true"
        - "traefik.http.routers.testapp2.rule=Host(`xxx.example.com`)"
        - "traefik.http.services.testapp2.loadbalancer.server.port=80"

networks:
  traefik-public:
    external: true

この構成にすることで、traefikに対してサービスの追加をすればいいので、DNSレコードの追加やcloudflare tunnelの設定の追加などが不要になります。
またローカルネットワーク内でのポート管理なども気にする必要がなくなり、traefikのダッシュボード上でサービスの稼働も確認ができます。

ただ、これだけだと複数PCでのサービス管理がかなりしんどいです。
できるだけ楽にそれぞれのPCをクラスター化し、アプリケーションの管理できる方法がないかなと探していたらdocker swarmを見つけました。

Docker Swarm

Docker Swarmはかなりdocker composeライクにクラスター環境をつくれるものになります。

サービス名でのDNS解決

ローカルネットワークとdocker-compose などで他のPCリソース上のDBを指定すると
DATABASE_URL=mysql://user:pass@192.168.100.2:3306/db
みたいにPCに割り当てられたIP指定になるのですが、
docker swarmを使うことで
DATABASE_URL=mysql://user:pass@db-mysql-prod_mysql:3306/db
のように書くことができ、サービス名指定することができます。
これまで結構「あれ、このPCのIPなんだっけな」となっていたのですが、解決されました。

サービスの配置指定

PC1とPC3はGPUを積んでいるので、例えばGPUリソースを使いたいサービスをホスティングしたいとなれば、docker-compose.swarm.yml に以下の様に指定すれば指定が可能です

services:
  app:
    image: my-app:latest
    deploy:
      placement:
        constraints:
          - node.hostname == PC3

ローリングアップデートと冗長構成

そこまで本格的に運用しないのであれば特に不要かもしれませんが、docker swarmが自動的に

  • 新しいコンテナを起動
  • ヘルスチェック
  • 既存のコンテナを停止

とサービスを止めずに更新してくれます。

また、以下の様にyamlで指定すれば、レプリカも作成可能です。

services:
  app:
    image: my-app:latest
    deploy:
      replicas: 2  # 2つのコンテナを起動

CI/CD自動化

オンプレ環境ですが、ECSやCloud Runみたいにイメージをビルドして、自動でデプロイできるようにしたかったので、github actionsのrunnerをself-hostedにしてオンプレマシン上でイメージをビルド・マシン上のデプロイスクリプトを実行することで実現しています。

また、自身の管理していないgithubのorganizationなどのレポジトリから
自動デプロイしようとするときも、オンプレ環境側の秘匿情報を他の
organizationに晒さなくてよくなるので、一石二鳥ということで
self-hosted runnerをつくることに意味があります。

具体的なSelf-hosted Runnerのメリット:

  • 環境変数(DATABASE_URLなど)をGitHub Secretsに登録不要
  • デプロイスクリプトがローカルファイルを直接参照可能
  • ファイアウォール設定不要(内部ネットワークで完結)
  • クライアントの秘匿情報を外部に出さずに済む
デプロイフロー
開発者
  ↓ git push origin development
GitHub
  ↓ Webhook
Self-hosted Runner (PC1)
  ↓ 1. Checkout
  ↓ 2. Build (タグ: staging-{COMMIT_HASH})
  ↓ 3. Deploy (.env.staging 読み込み)
  ↓ 4. Migrate 
  ↓ 5. Health Check
完了

また、self-hosted runnerの設定は意外と簡単で、ここからRunnerを取得してきてgithub レポジトリと紐づけを行い、サービスのインストール〜起動だけで完結します

GitHubから最新版のRunnerをダウンロード

curl -o actions-runner-linux-x64-2.xxx.x.tar.gz -L \
  https://github.com/actions/runner/releases/download/v2.xxx.x/actions-runner-linux-x64-2.xxx.x.tar.gz

展開

tar xzf ./actions-runner-linux-x64-2.xxx.x.tar.gz
トークンの取得場所
リポジトリの Settings → Actions → Runners → New self-hosted runner
./config.sh --url https://github.com/your-org/your-repo \
  --token <GITHUB_PROVIDED_TOKEN>

runnerのサービスを起動

# systemdサービスとしてインストール
sudo ./svc.sh install
# サービス起動
sudo ./svc.sh start
# 状態確認
sudo ./svc.sh status

あとはgithub actionsの定義でruns-onに設定してあげる感じになります。

jobs:
  deploy:
    runs-on: self-hosted

Dozzle(Dockerログビューアー)

オンプレ環境でもCloudWatchみたいに楽にログを見たいし検索もしたいという要望を叶えてくれました。
Dozzleは

  • リアルタイム更新
  • 検索・フィルタ機能
  • 複数コンテナ同時表示

を可能にしてくれるOSSで、画像のようにいつでもWebからログが確認できます。

Cloudflare Access

Dozzleみたいに一般に公開したくないサービスもあります。
一つ一つ cloudflare workerとかでBasic認証を張ったりしてもいいのですが、もっと便利なものをcloudflareは用意してくれてました。
Cloudflare Zero TrustのAccess 機能になります。

このAccess機能はCloudflareでDNS管理していて、ProxyをONにしているドメインであれば利用することができ、ログイン方法の指定やポリシーを設定することができます。
例えば GoogleのOAuth認証を利用して、特定のドメインのメアドであればアクセスを許可するようなことができます。

ステージング環境とかでよくBasic認証を作成することが多いと思いますが、Cloudflareを利用してインフラレイヤーで解決するのも良さそうですね。

おわりに

個人的に早く社内の技術をキャッチアップしたり、バリューを発揮するには同じ技術でなにかアプリケーションを作ってみることが近道だと思っているので、この環境でつくってホスティングしようと思っています。

ちなみに各リソースと電気代、それがクラウドだったらの想定はこんな感じです

PC CPU モデル 物理コア 論理CPU メモリ合計 GPU GPU メモリ
PC1 AMD Ryzen 7 5700X 8 16 39 GiB NVIDIA GeForce RTX 3060 Ti 8 GiB
PC2 AMD Ryzen 5 1600X 6 12 31 GiB なし -
PC3 AMD Ryzen 7 5700X 8 16 31 GiB NVIDIA GeForce RTX 4060 8 GiB
合計 - 22 44 101 GiB - 16 GiB

(100W + 80W + 80W) × 24h × 30日 × 30円/kWh ÷ 1000
= 5,616円/月

もしもAWSのEC2だったら

合計コスト(us-east-1想定)
GPU付き g4dn.2xlarge ×2:$1,082.88 / 月
CPUのみ c7a.4xlarge ×1:$599.42 / 月

合計:
1,082.88 + 599.42 = $1,682.3 / 月

ドル円 156円として、🔥🔥月額約260,000円🔥🔥

停電怖い、、、😱

We are hiring!!

カウンターワークス では一緒に働く仲間を絶賛募集中です。
今後の更なる成長のためには圧倒的に仲間が不足しています。皆さまのご応募お待ちしております!
https://counterworks.co.jp/recruit/?utm_source=zenn&utm_medium=referral&utm_campaign=tech_blog&utm_content=0a90056ff23e10

Appendix

お世話になっているPCたち

COUNTERWORKS テックブログ

Discussion