Argo CD で Kubernetes 上のアプリを公開する
- 公開日
- カテゴリ:ArgoCD
- タグ:Kubernetes,ArgoCD,学習メモ

Kubernetes で動かしているアプリを外から開けるようにするには、Deployment のほかに Service・Ingress が必要になる。
この記事では、これらの YAML を Argo CD が見ている Git のディレクトリに追加して push し、kubectl apply を実行せずに http://localhost/ でページが表示される状態にする。
contents
- 前提
- 今回作る構成
- Ingress Controller(Contour)を入れる
- Git に置く YAML
- push して反映する
- ConfigMap を変えて push する
- 全体像
- 用語メモ
- 参考 URL
前提
GitHub の private リポジトリ argocd-study に web/deployment.yaml(nginx の Deployment web。replicas: 1)があり、Argo CD の Application web がそれを default namespace に入れている状態から始める。Argo CD は v3.5.5。
Application web の spec.syncPolicy には automated・selfHeal: true・prune: true が書いてあり、Git に push すれば人が操作しなくても反映される。
$ kubectl get applications -n argocd
NAME SYNC STATUS HEALTH STATUS
web Synced Healthy
$ kubectl get deployment,pods -n default
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/web 1/1 1 1 2d
NAME READY STATUS RESTARTS AGE
pod/web-557577df75-5m5lr 1/1 Running 2 2d
クラスタは kind で作ったもので、localhost の 80 番ポートへのアクセスが 1 つ目の Worker Node の 80 番に届く設定(kind の extraPortMappings)にしてある。この時点では、Worker Node の 80 番で待っているプログラムがないので、http://localhost/ を開いても何も返らない。
$ curl -i http://localhost/
curl: (52) Empty reply from server
この記事で使う Argo CD の用語。
| 言葉 | 意味 |
|---|---|
| Sync | Git の YAML をクラスタに apply すること。自分で kubectl apply を実行する代わりに、Argo CD が実行する |
Synced / OutOfSync | Git の YAML とクラスタの中身を比べた結果。同じなら Synced、違えば OutOfSync |
Healthy / Progressing | クラスタに入れたものが動いているかを、Argo CD が種類ごとに判定した結果(kubectl get applications の HEALTH STATUS 列)。Deployment なら Pod が全部準備できていれば Healthy、途中なら Progressing。Application 全体の Health は、中のもので一番悪いものになる |
| Argo CD の画面 | ブラウザで開く管理画面。kubectl port-forward svc/argocd-server -n argocd 8080:443 を実行して https://localhost:8080 を開く |
今回作る構成
Git の web/ ディレクトリに置くファイルは 4 つ。
| ファイル | 作るもの | 役割 |
|---|---|---|
web/configmap.yaml | ConfigMap web-html | ブラウザに返すページ(index.html)の内容 |
web/deployment.yaml | Deployment web(変更) | nginx の Pod を作る定義。ページの置き場所に ConfigMap を差し込む |
web/service.yaml | Service web | nginx の Pod への宛先(名前と固定の IP) |
web/ingress.yaml | Ingress web | 外から来たリクエストを、どの Service に渡すかの決まり |
これとは別に、外から来たリクエストを受けてクラスタの中の Pod に渡すプログラム(Ingress Controller)が必要になる。実務では、Ingress Controller のようなクラスタの設備も、アプリとは別の Application にして Argo CD に入れさせる(Git で管理する)。この記事では学習のために Git には置かず、kubectl apply で入れる。Argo CD が反映するものをアプリの YAML だけにして、push したときに何がクラスタに入ったかを見やすくするため。
Ingress Controller(Contour)を入れる
クラスタの中の Pod には、クラスタの中だけで通じる IP アドレスが付いている。外のブラウザからその IP には届かない。外から届くようにするには、「外から来たリクエストを受けて、中の Pod に渡す」プログラムが必要で、これを Ingress Controller と呼ぶ。Ingress Controller は Kubernetes に含まれておらず、あとから選んで入れる。
今回は Contour を入れる。Contour は 2 つのプログラムでできている。
| プログラム | 役割 | 動く場所 |
|---|---|---|
envoy | Worker Node の 80 番(と 443 番)で待ち、受けたリクエストを Pod に転送する(リバースプロキシ) | Worker Node ごとに 1 つの Pod(DaemonSet) |
contour | クラスタの Ingress を読んで、envoy に転送先の設定を流し込む | Pod 2 つ(Deployment) |
公式の install YAML を URL で apply する。namespace projectcontour、Deployment contour、DaemonSet envoy、Service、CRD、証明書を作る Job などが 1 ファイルに入っている(2026-10-17 時点で v1.33.7)。
$ kubectl apply -f https://projectcontour.io/quickstart/contour.yaml
$ kubectl get pods -n projectcontour
NAME READY STATUS RESTARTS AGE
contour-859c5dcfdc-7ht29 1/1 Running 0 23s
contour-859c5dcfdc-z4ndk 1/1 Running 0 23s
contour-certgen-v1-33-7-xkd58 0/1 Completed 0 23s
envoy-fd26n 2/2 Running 0 23s
envoy-qngq7 2/2 Running 0 23s
contour が 2 つ、envoy が Worker Node の数だけ(2 つ)できる。contour-certgen-... は contour と envoy の間の通信に使う証明書を作って終了した Job で、Completed のままでよい。envoy の 2/2 は、Pod の中にコンテナが 2 つ(envoy 本体と、停止時の処理をする shutdown-manager)あることを表す。
kind 向けの設定を 1 つ追加する
Argo CD は、Ingress の Health を「Ingress に入口のアドレスが書き込まれているか」で判定する(公式ドキュメントでは status.loadBalancer.ingress が空でないこと)。クラウドでは、クラウドが用意した入口の IP アドレスを Contour が Ingress に書き込むが、手元の kind にはそれがない。そのままでは Ingress が Progressing のままになり、Application web 全体の Health も Progressing になる。
Contour には、書き込むアドレスを指定する設定 ingress-status-address がある。Contour の設定ファイルは ConfigMap contour(projectcontour namespace)の contour.yaml で、install YAML が作ったものは内容がすべてコメントになっている。これを次の YAML で置き換える。
apiVersion: v1
kind: ConfigMap
metadata:
name: contour
namespace: projectcontour
data:
contour.yaml: |
ingress-status-address: 127.0.0.1
contour は設定ファイルを起動時に読むので、apply したあとに起動し直す。
$ kubectl apply -f contour-configmap.yaml
# => configmap/contour configured
$ kubectl rollout restart deployment contour -n projectcontour
# => deployment.apps/contour restarted
ここまでで、Worker Node の 80 番で envoy が待つようになった。ただし、どこに渡すかの決まり(Ingress)がまだないので、http://localhost/ はまだ通らない。
Git に置く YAML
ConfigMap: ページの内容
image に入っている nginx の初期ページではなく、自分で書いた HTML を返したい。image を作り直さずに済ませるため、ファイルの内容を ConfigMap に置いて Pod に差し込む。
web/configmap.yaml。data の下が「ファイル名: 内容」で、| は次の行からの複数行をそのまま文字列にする YAML の書き方。
apiVersion: v1
kind: ConfigMap
metadata:
name: web-html
data:
index.html: |
<h1>Hello from Git</h1>
Deployment: ConfigMap をページの置き場所に差し込む
ConfigMap を置いただけでは nginx は読まない。Deployment の YAML に、「ConfigMap web-html を、コンテナの /usr/share/nginx/html(nginx がページを読むディレクトリ)に置く」と書く。
web/deployment.yaml。変更したのは spec.template.spec の下の 2 か所で、containers の中に volumeMounts、containers と並べて volumes を追加した。
apiVersion: apps/v1
kind: Deployment
metadata:
name: web
spec:
replicas: 1
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
spec:
containers:
- name: nginx
image: nginx:1.27
ports:
- containerPort: 80
volumeMounts:
- name: html
mountPath: /usr/share/nginx/html
volumes:
- name: html
configMap:
name: web-html
volumes: Pod に「htmlという名前で、ConfigMapweb-htmlの内容をディスクのように見せる」volumeMounts: コンテナの/usr/share/nginx/htmlに、そのhtmlを置く。ディレクトリの中身は ConfigMap にあるファイル(index.html)だけになる
Deployment の template(Pod の定義)が変わるので、push すると Pod が新しいものに入れ替わる。
Service: Pod への宛先
Pod の IP アドレスは、Pod が入れ替わるたびに変わる。入口の envoy に「この IP に渡す」とは書けない。Service は、「ラベル app: web が付いた Pod」への宛先で、名前(web)と変わらない IP(ClusterIP)が付く。
web/service.yaml。selector は Deployment の template.metadata.labels と同じ app: web。Service の 80 番に来たものを、Pod の targetPort(80 番。nginx が待っているポート)に渡す。
apiVersion: v1
kind: Service
metadata:
name: web
spec:
selector:
app: web
ports:
- port: 80
targetPort: 80
Ingress: リクエストをどの Service に渡すか
envoy は Worker Node の 80 番で待っているが、「どのリクエストをどの Service に渡すか」を知らない。それを書くのが Ingress。
web/ingress.yaml。「パスが / で始まるリクエスト(Prefix。つまり全部)は、Service web の 80 番に渡す」という 1 つの決まりだけを書く。
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: web
spec:
rules:
- http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: web
port:
number: 80
Ingress はデータで、これを読んで envoy に設定を流し込むのが contour。
push して反映する
4 ファイルを 1 回の push で GitHub に入れる。
$ git status --short
M web/deployment.yaml
?? web/configmap.yaml
?? web/ingress.yaml
?? web/service.yaml
$ git add web
$ git commit -m "Add configmap, service, ingress"
$ git push
Argo CD は GitHub からファイルを定期的に取得する。既定では 2〜3 分に 1 回で、設定で変えられる。(すぐに反映したいときは、Argo CD の画面の REFRESH ボタンを押す)
別のターミナルで default namespace の Pod を -w 付きで表示しておくと、新しい Pod が作られて古い Pod が Terminating になるのが見える。
$ kubectl get pods -n default -w
NAME READY STATUS RESTARTS AGE
web-557577df75-5m5lr 1/1 Running 2 2d
web-9f744d4b9-799pm 0/1 Pending 0 0s
web-9f744d4b9-799pm 0/1 ContainerCreating 0 0s
web-9f744d4b9-799pm 1/1 Running 0 1s
web-557577df75-5m5lr 1/1 Terminating 2 2d
default namespace に何ができたかを確認する。
$ kubectl get configmap,deployment,pods,svc,ingress -n default
NAME DATA AGE
configmap/kube-root-ca.crt 1 2d13h
configmap/web-html 1 50s
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/web 1/1 1 1 2d13h
NAME READY STATUS RESTARTS AGE
pod/web-9f744d4b9-799pm 1/1 Running 0 50s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 2d13h
service/web ClusterIP 10.96.45.25 <none> 80/TCP 50s
NAME CLASS HOSTS ADDRESS PORTS AGE
ingress.networking.k8s.io/web <none> * 127.0.0.1 80 50s
$ kubectl get applications -n argocd
NAME SYNC STATUS HEALTH STATUS
web Synced Healthy
configmap/web-html、service/web、ingress.networking.k8s.io/webが、Git に書いたとおりの名前で作られた。Applicationwebのpathはディレクトリ(web)を指しているので、そこに追加したファイルは Application を変えなくても対象になる- Pod の名前が
web-557577df75-...からweb-9f744d4b9-...に変わった。Deployment のtemplateが変わったため - Ingress の
ADDRESSが127.0.0.1になっている。Contour に書かせた入口のアドレスで、これが入っているので Argo CD の Health がHealthyになる
http://localhost/ を開くと、web/configmap.yaml に書いた HTML が返る。
$ curl http://localhost/
<h1>Hello from Git</h1>

クラスタに対して自分で実行したのは Contour の apply だけで、アプリ(Deployment)とその公開に必要な設定(ConfigMap・Service・Ingress)は git push だけで入った。
Argo CD の画面で Application web を開くと、Application の下に ConfigMap・Service・Deployment・Ingress の 4 つが並び、Deployment の下に新旧の ReplicaSet と Pod がぶら下がっている。

ConfigMap を変えて push する
web/configmap.yaml の HTML を変えて push し、Pod を入れ替えずにページが変わることを確認する。
$ git diff
- <h1>Hello from Git</h1>
+ <h1>Hello again from Git</h1>
$ git commit -am "Change greeting"
$ git push
Argo CD が ConfigMap を apply しても、Pod は入れ替わらない。Pod が入れ替わるのは Deployment の template が変わったときだけで、ConfigMap の変更では template は変わらない。
代わりに、Pod のコンテナに置いてある index.html が少し遅れて新しい内容に置き換わる。Node で動く kubelet が定期的に ConfigMap を見直してファイルを差し替えるためで、この記事の環境では Sync から 1 分ほどかかった。nginx はリクエストのたびにファイルを読むので、差し替わった時点でページが変わる。
$ curl http://localhost/
<h1>Hello again from Git</h1>
$ kubectl get pods -n default
NAME READY STATUS RESTARTS AGE
web-9f744d4b9-799pm 1/1 Running 0 7m54s

Pod の名前も AGE も変わっていない。
全体像
自分が git push したあと、Argo CD が 4 つを apply した状態。curl http://localhost/ のリクエストは envoy が受けて、Ingress の決まりどおりに Service web の Pod へ転送される。(図の角が二重の箱は動くプログラム、それ以外の箱は etcd に保存されたデータ)
リクエストの通り道は、localhost の 80 番 → Worker Node の 80 番 → envoy → (Ingress の決まり)→ Service web → Pod web-xxxxx の nginx、の順。
用語メモ
| 用語 | 読み方 | 意味 |
|---|---|---|
| Ingress Controller | イングレスコントローラー | 外から来たリクエストを受けて、クラスタの中の Pod に渡すプログラム。Kubernetes には含まれず、Contour などを選んで入れる |
| Ingress | イングレス | どのリクエストをどの Service に渡すかの決まり。etcd に保存されるデータで、Ingress Controller が読んで動く |
| Service | サービス | ラベルで選んだ Pod への宛先。名前と固定の IP(ClusterIP)が付く |
| ConfigMap | コンフィグマップ | 設定値やファイルの内容を入れておくデータ。Volume として Pod に差し込める |
| Envoy | エンボイ | Contour が使っているリバースプロキシ。実際にリクエストを受けて転送する |
まとめ
- Application の
pathが指す Git のディレクトリに YAML を追加して push すれば、Application を変えなくてもクラスタに入る - 外から Pod に届くには、Ingress Controller(クラスタの設備として
kubectl applyで入れる)と、Ingress・Service(Git に置く)の両方が必要 - ConfigMap の内容を Git で変えて push しても Pod は入れ替わらず、kubelet がファイルだけを差し替える
参考 URL
- [official] Getting Started - Contour
- [official] Ingress - Contour
- [official] Resource Health - Argo CD
- [official] Ingress | Kubernetes
- [official] Ingressコントローラー | Kubernetes
- [official] Service | Kubernetes
- [official] ConfigMap | Kubernetes

