1. はじめに
CI/CDという言葉は知っていましたが、なかなか勉強するまでには至りませんでした。それが仕事で使うことになり、とうとう勉強を始めることにしました。
とはいえ、どこから手をつければいいのか分かりません。調べると記事はたくさんあり、手順も丁寧に書かれています。ただ、どれも「こう書けば動く」が中心で、なぜpushしただけで最後まで流れるのかは、読んでもいまいち分かりませんでした。
幸い、今はAIという優秀なパートナーがいます。そこで今回は、AI(Claude Code)に助けてもらいながら、自分の環境で1つずつ検証していきました。
まず作ったのは、GitHubにpushすると、GitHub ActionsがCloudflare Workersへ自動で公開してくれるしくみです。画面で操作したのは3つ、書いたファイルも3つだけで、pushすると公開まで勝手に進んでいきます。実際に動かしてみても、やはり疑問は「何で流れるの?」、これだけでした。
この記事では、手順に加えて、なぜ最後まで自動で流れるのかを図で説明します。同じところでもやもやしている人の参考になればうれしいです。
なお、記事の内容はすべて自分の環境で動かして確かめていますが、CI/CDは初心者です。間違いなどがあれば、コメントで教えていただけるとうれしいです。
2. 作ったもの
GitHubのmainブランチにpushすると、約30秒で https://test.tobaru.net のページが更新されるしくみです。ページはHTMLファイル1つだけの、いちばん小さなものにしました。
ひとことで言うと、GitHubに置いたファイルを、GitHubのロボット(Actions)がCloudflareまで運んでくれます。運ぶときにCloudflareのドアを開けるので、ロボットに合鍵(APIトークン)を預けてあります。
自分で用意したサーバーはありません。DNSのレコードとhttpsの証明書も、手では作っていません。
3. 環境
| 項目 | 内容 |
|---|---|
| GitHub | Freeプラン、Privateリポジトリ |
| Cloudflare | Freeプラン |
| ドメイン | Cloudflare Registrarで取得したtobaru.net(DNSもCloudflare) |
| デプロイの道具 |
cloudflare/wrangler-action@v4、wrangler 4 |
| 作業したPC | Windows 11、VS Code、Git |
自分のPCにNode.jsやwranglerは入れていません。wranglerはGitHub Actionsの中でだけ動きます。
ドメインのDNSがCloudflareにあることが、後で出てくる「1行でDNSと証明書ができる」の条件になります(5章の(5))。
4. 手順
画面で操作したのは(1)〜(3)の3つだけです。(4)はファイルを書くだけ、(5)はpushするだけです。
(1) GitHubにリポジトリを作る
- 名前は
tobaru-net-test、Privateにしました - README・.gitignore・ライセンスは付けず、空のリポジトリにします(ローカルで作ったファイルをそのままpushするため)
(2) CloudflareでAPIトークンを作る
- 右上の人のアイコン → プロフィール → APIトークン → トークンを作成する
- テンプレート「Cloudflare Workers を編集する」を使う
- 範囲を絞る
- アカウントリソース: 自分のアカウントだけ
- ゾーンリソース: 特定のゾーン →
tobaru.netだけ
- 作成する。トークンは一度しか表示されないので、そのまま(3)に貼る
範囲を絞っておくと、万一トークンが漏れても、できることは「Workersの更新」と「tobaru.netの設定」だけです。
(3) GitHubのSecretsに登録する
リポジトリの Settings → Secrets and variables → Actions → New repository secret で、2つ登録します。
| Name | 値 |
|---|---|
CLOUDFLARE_API_TOKEN |
(2)で作ったトークン |
CLOUDFLARE_ACCOUNT_ID |
CloudflareのアカウントID |
(4) ファイルを3つ書く
tobaru-net-test/
├─ public/
│ └─ index.html … 見せるページそのもの
├─ wrangler.jsonc … Cloudflareへの注文書(何を・どこで出すか)
└─ .github/
└─ workflows/
└─ deploy.yml … Actionsへの指示書(いつ・何をするか)
public/index.htmlは、ふつうのHTMLです(中身は省略)。
wrangler.jsoncです。ふつうのJSONはコメントが書けませんが、jsonc(JSON with Comments)は//で書けます。
{
// Cloudflare側でのWorkerの名前(名札)。
// この名前のWorkerがなければ新しく作り、あれば中身を置き換える
"name": "tobaru-net-test",
// Workersの動き方の基準日
"compatibility_date": "2026-10-01",
// 静的なファイル(HTML・画像など)の置き場所。この中身がそのままサイトに並ぶ
"assets": {
"directory": "./public"
},
// どのURLで出すか。custom_domain: true でDNSレコードと証明書も作ってくれる
"routes": [
{ "pattern": "test.tobaru.net", "custom_domain": true }
],
// xxx.workers.dev というCloudflareのURLでは公開しない
"workers_dev": false
}
.github/workflows/deploy.ymlです。
name: Deploy
# いつ動くか(イベントの条件)
on:
push:
branches: [main] # mainブランチにpushされたとき
workflow_dispatch: # Actionsタブの「Run workflow」ボタンで手動でも動かせる
jobs:
deploy:
runs-on: ubuntu-latest # GitHubが数十秒だけ貸してくれるLinux
permissions:
contents: read
steps:
# 1. このcommitのファイル一式をランナーに取り出す
- uses: actions/checkout@v5
# 2. wranglerをインストールして動かす(commandを書かなければ wrangler deploy)
- name: Deploy to Cloudflare Workers
uses: cloudflare/wrangler-action@v4
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }} # 金庫の合鍵
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} # どのアカウントに送るか
wranglerVersion: "4"
(5) pushする
git init -b main
git add -A
git commit -m "最初のページとデプロイ設定"
git remote add origin https://github.com/<ユーザー名>/tobaru-net-test.git
git push -u origin main
pushしてから約30秒で、https://test.tobaru.netが開けるようになりました。10秒おきに確かめると、最初の3回はつながらず、4回目で200が返ってきました。
5. なぜ最後まで自動で流れるのか
答えを先に書くと、何かがずっと監視しているわけではありません。「イベントが起きたら、こう動く」という小さな決まりが、前の結果を受け取ってつながっているだけです。
pushしてから公開されるまでを、5つの段階に分けました。
画像をクリックすると、大きく表示されます。
ここからは、私が分からなかったところを順に説明します。
(1) GitHubはフォルダを監視しているの?
監視はしていません。pushやプルリクエストなどのイベントが起きたときにだけ、GitHubがそのcommitの.github/workflows/を開きます。中のYAMLを全部読んで、on:の条件に合うものだけを動かします。
それが分かる例です。
| こんなとき | デプロイは動く? |
|---|---|
README.mdだけ変えてpush |
動く。deploy.ymlは変わっていないが、「mainへのpush」というイベントが起きたから |
deploy.ymlを書き換えたが、pushしていない |
動かない。GitHubは自分のPCの中を見ていない |
deploy.ymlを書き換えてpush |
書き換えた後の内容で動く。そのcommitのファイルを読むから |
| 別のブランチにpush | 動かない。on:がbranches: [main]だから |
| プルリクエストをmainにマージ | 動く。マージするとmainにcommitが入り、mainへのpushと同じになるから |
「受付の人がマニュアルをずっと見張っている」のではありません。「呼び鈴が鳴ったらマニュアル棚を開いて、今回の用件に当てはまるページを探す」イメージです。マニュアル棚が.github/workflows/、ページの見出しがon:です。
(2) ファイルを指定していないのに、なぜwrangler.jsoncが使われるの?
deploy.ymlのデプロイの手順には、「どのファイルを送るか」を書いていません。それでも動くのは、書かなかったときの決まり(既定値)が2段階あるからです。
- wrangler-actionの決まり:
commandを書かなければdeployを実行する(action.ymlに「Defaults to "deploy"」とあります)。workingDirectoryも書かなければ、リポジトリのいちばん上で実行する - wranglerの決まり:
wrangler deployは、実行したフォルダでwrangler.jsonc/wrangler.json/wrangler.tomlを探して読む
しかも、wrangler.jsoncそのものは送られません。「何を・どこに・どんな名前で」の指示として読まれ、送られるのはassets.directoryに書いたpublic/の中身です。
決まった名前・決まった場所に置けば何も書かなくても動き、変えたいときだけ書く。この考え方を「設定より規約(Convention over Configuration)」と呼ぶそうです。(1)の.github/workflows/も同じです。
(3) Cloudflareで何も作っていないのに、なぜWorkerができるの?
wrangler.jsoncの"name": "tobaru-net-test"で、「この名前のWorkerを、この中身にして」と頼んでいるからです。Cloudflareは、その名前のWorkerがなければ新しく作り、あれば中身を置き換えます。上の図のaltのところです。
どのアカウントに作るかはaccountId、入っていいかはapiTokenで伝えています。どちらもSecretsから渡しているので、deploy.ymlには値そのものを書いていません。
(4) Secretsは何のため?
Secretsは合鍵を入れておく金庫です。deploy.ymlには${{ secrets.CLOUDFLARE_API_TOKEN }}と、「金庫のこの引き出しを使って」とだけ書きます。合鍵そのものはどのファイルにも書かないので、コードを人に見せても漏れません。
(5) 1行でDNSと証明書までできるの?
できました。routesに"custom_domain": trueと書くと、「このホスト名をまるごとこのWorkerにする」意味になり、CloudflareがDNSのレコードとhttpsの証明書を作ってくれます。
ただし、次の2つがそろっているからできることです。
-
tobaru.netのDNSが、同じCloudflareアカウントにある(Cloudflareが自分でレコードを書き込める) - APIトークンに
tobaru.netを触る権限がある(4章の(2)でゾーンを絞って許可した)
DNSが別の会社にあるドメインでは、この1行だけでは済みません。
custom_domainを付けずに"pattern": "test.tobaru.net/api/*"のように書くと、「このURLに来たアクセスだけWorkerが受け取る」という別の使い方になります。この場合、DNSのレコードは自分で作ります。
6. 警告が出たところ
最初は、他の記事と同じくcloudflare/wrangler-action@v3を使っていました。デプロイは成功しましたが、Actionsの結果のAnnotationsに、警告1件とお知らせ1件が出ました。
(1) Node.js 20 is deprecated(警告)
Node.js 20 is deprecated. The following actions target Node.js 20 but are being forced to run on Node.js 24: cloudflare/wrangler-action@v3.
wrangler-action@v3はNode.js 20向けに作られていて、GitHub側がNode.js 20を廃止していく途中なので、Node.js 24で動かしているという知らせです(GitHubのお知らせ)。今は動いていますが、将来動かなくなるおそれがあります。
調べるとv4が出ていて、action.ymlにusing: "node24"とありました。入力の項目(apiToken・accountIdなど)は同じなので、@v3を@v4に変えるだけで警告が消えました。
この修正もdeploy.ymlを1文字直してpushしただけです。しくみそのものの設定を変えた場合も、ページを変えた場合と同じ流れで反映されます(5章の(1)の表の3行目)。
(2) ubuntu-latestがUbuntu 26に移る(お知らせ)
The ubuntu-latest label will migrate to Ubuntu 26 beginning October 19, 2026.
runs-on: ubuntu-latestの中身が、順に新しいUbuntuに切り替わるという案内です(runner-imagesのIssue)。今回のワークフローはwranglerを動かすだけなので、そのままにしています。
(3) nameを変えるときの注意
つまずいたわけではありませんが、しくみから考えると注意が必要だと分かったところです。
nameを変えてpushすると、Cloudflareは名前が変わったとは受け取りません。「その名前のWorkerがないので新しく作る」と受け取ります(5章の(3))。古いWorkerは残ったままになるので、変えるときは古いほうの片付けも必要です。
7. Cloudflareの画面で確かめる
Cloudflareの画面では何も作っていませんが、pushしたあとに開くと、次の2つができていました。
(1) Workersの一覧
「Workers & Pages」を開くと、wrangler.jsoncのnameで付けたtobaru-net-testがあり、routesで頼んだtest.tobaru.netがつながっていました。
「デプロイメントを表示」を開くと、pushした回数ぶんの履歴が並んでいます。GitHubのcommitと1回ずつ対応しています。
(2) DNSのレコード
tobaru.netのDNSのページには、自動で作られたtestのレコードがありました。自分では1つも作っていないのに、本当にレコードが出ていてびっくりしました。
| 見えているもの | 意味 |
|---|---|
| 左端の鍵のアイコン | 自動で作られたレコードなので、ここからは編集できない |
| タイプ「Worker」 | AやCNAMEではなく、「このWorkerに届けて」という特別なレコード |
| コンテンツ「tobaru-net-test」 |
wrangler.jsoncのname。名札でつながっている |
| 「プロキシ済み」 | アクセスはいったんCloudflareが受け取る |
外のDNS(1.1.1.1)にtest.tobaru.netを聞いてみると、返ってくるのはCloudflareのIPアドレス(104.21.x.xなど)でした。「プロキシ済み」なので、サーバーの場所ではなくCloudflareのアドレスが返ります。応答のヘッダーにはServer: cloudflareとCF-RAY: …-NRTがあり、東京の拠点から返っていることも分かりました。
8. まとめ
最初は、なぜ自動で最後まで流れるのかが、いまいち理解できませんでした。でも、監視という大掛かりな仕組みではありませんでした。イベントが起きたときの動きが決まっていて、その決まりどおりに動くと、流れるように最後までつながる。それが分かったとき、美しいなーと思いました。DNSの画面に、自分では作っていないレコードが出ていたときも感動しました。
今回分かったことを並べると、次の3つです。
- GitHubは監視していない。イベントが起きたときに
.github/workflows/を読み、on:に合うものだけを動かす - 書いていないことは既定値で動く。wrangler-actionは
deployを実行し、wranglerは決まった名前のwrangler.jsoncを探す - Cloudflareは
nameでWorkerを見分け、なければ作り、あれば置き換える。custom_domain: trueならDNSと証明書も作る
次は、プルリクエストを作ったらチェックだけを動かし、mainにマージしたらデプロイする形(CIとCDの分離)を試す予定です。チェックも、pull_requestというイベントに決まりを足すだけで作れるはずです。




