Kubernetes2 Advent Calendar 2018 3日目の記事です.
はじめに
kubernetesのマニフェストを管理するときに一人でapplyをしていたが,サービスが増えてくると管理が難しくなってきた.特にservice.yamlだけapplyするのを忘れることがあったり,どれをapplyしたかわからなくなったり...etc
その問題を少しでも解決するためにkustomizeとgitlab-ciを使う.
もっといいフローやツールも必ずあるだろう,改善すべきところもあるだろう.
今回は私が今,学生のチーム開発でkubernetesのマニフェストを管理するときにやている解決策を紹介する.
問題はなにか?
- kubernetesのマニフェストを複数書くのが辛い
- kubernetesへの手動デプロイが辛い
解決策
上の2つの問題を解決する
kubernetesのマニフェストを書くのが辛い
この見出しは過去記事を少しアレンジしたものになります
kustomizeはもとのyamlからパッチを当てるように変更を加えることができるツールである
今回はyamlを抽象化したものをbaseディレクトリ配下において実際にkubernetesに反映させるものに具体的に書いていくようにしていこうと思う.
ディレクトリ構成は以下のようになる
$ tree
.
|-- README.md
|-- k8s_base
| |-- deployment.yaml
| |-- kustomization.yaml
| `-- service.yaml
|-- service1
| |-- Dockerfile
| |-- k8s_overlay
| | |-- containers.yaml
| | |-- kustomization.yaml
| | `-- replicas.yaml
| `-- service1.go
`-- service2
|-- Dockerfile
|-- k8s_overlay
| |-- containers.yaml
| |-- kustomization.yaml
| `-- replicas.yaml
`-- service2.go
ベースとなるマニフェストを作成する
k8_baseにはマニフェストを抽象化したものが入っている
以下の例ではk8s_base配下のファイルをまとめて書いている
# kustomization.yaml
resources:
- service.yaml
- deployment.yaml
--
# deployment.yaml
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: sample
spec:
replicas: 1
template:
metadata:
labels:
app: sample
spec:
imagePullSecrets:
- name: gitlab
--
# service.yaml
apiVersion: v1
kind: Service
metadata:
name: sample
labels:
app: sample
spec:
type: LoadBalancer
ports:
- port: 8080
protocol: TCP
targetPort: 80
selector:
app: sample
実際に使うときはファイルを分けて使う
ちなみにimagePullSecrets
がgitlabになっているのは私がgitlabで開発することが多いからである
githubなどを使うときは消したほうがいいだろう
これでベースとなるファイルを作成することができた
次にservice1/k8s_overlay配下を見ていく
具体的なマニフェストを作成する
# kustomization.yaml
bases:
- ../../k8s_base
namePrefix: service1-
commonLabels:
app: service1-sample
patches:
- containers.yaml
- replicas.yaml
configMapGenerator:
- name: app-conf
literals:
- PORT=8080
--
# containers.yaml
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: sample
spec:
template:
spec:
containers:
- name: service1
image: registry.gitlab.com/sample/service1:latest
ports:
- containerPort: 8080
- envFrom:
- configMapRef:
name: app-conf
--
# replicas.yaml
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: sample
spec:
replicas: 1
上の例のようにdeployment,service,kustomization.yamlを記述する
次にビルドをする
$ kustomize build service1/k8s_overlay/
するとビルドされる
$ kustomize build service1/k8s_overlay/
apiVersion: v1
data:
PORT: "8080"
kind: ConfigMap
metadata:
creationTimestamp: null
labels:
app: service1-sample
name: service1-app-conf-httk99d7g5
---
apiVersion: v1
kind: Service
metadata:
labels:
app: service1-sample
name: service1-sample
spec:
ports:
- port: 8080
protocol: TCP
targetPort: 80
selector:
app: service1-sample
type: LoadBalancer
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
labels:
app: service1-sample
name: service1-sample
spec:
replicas: 1
selector:
matchLabels:
app: service1-sample
template:
metadata:
labels:
app: service1-sample
spec:
containers:
- image: registry.gitlab.com/sample/service1:latest
name: service1
ports:
- containerPort: 8080
- envFrom:
- configMapRef:
name: service1-app-conf-httk99d7g5
imagePullSecrets:
- name: gitlab
これでservice1はビルドすることができた.
同様にservice2の方もkustomizeのマニフェストを書いて上げれば
同じようなマニフェストを出力することができる
ちなみにname: service1-app-conf-httk99d7g5
のランダムな文字列はconfigMapGenerator
の中身を編集すると別の文字列が追加される.
そうするとkubectl apply
するときに環境変数なども変更させることができる
多分secretもできてimagePullSecrets:
もkustomizeで管理できると思う.
kubernetesにデプロイする
kubernetesにデプロイするには
$ kustomize build service1/k8s_overlay/ | kubectl apply -f - --prune -l app=service1-sample
お好みで-n
をつけてあげるといいと思う.
(実は--pruneの挙動をあまり理解できてないのでおまじない的な感じになっている)
これで
- コンテナの
image
を変更したいときはcontainers.yamlを変更 - 環境変数を追加,編集したいときはkustomization.yamlの
configMapGenerator
に記述 - 複数のPodの数を変更したいときはreplicas.yamlの
replicas
を変更
をやったあとにkustomize build
とkubectl apply
をやれば変更することができる
まとめ
抽象度を上げることにより複数のサービス適用させることができる.
もちろん本来の使い方である開発環境,本番環境というふうに使うこともいいだろう
kubernetesへの手動デプロイが辛い
今までのことは過去記事やkustomizeのgithubを見るとわかると思う
yamlの記述量は減らすことができたのではないだろうか?
ちなみにkubernetesのマニフェストは一つにまとめられるのでkustomizeを使うのであれば
service.yamlだけapplyするのを忘れることは無くなると思う.
それでもサービスが多くなるとapplyするのが辛くなってくるだろう
そこでCI時にapplyしてしまえば解決するのではないだろうか?
これはkustomizeのworkflowsに例が書いてある.
今回はこちらを参考にデプロイ作業をできるだけ楽になるようにしてみた
gitlab-ciを使用してci時にデプロイするようにしたい
今回使うもの
- gitlab
- gcloud
- gke
以上の3つを使う
gcloudとgkeに関してはあらかじめapplyできる権限をgitlabのuserに与えておく必要がある.
.gitlab-ci.yamlでCIを作成する
stages:
- test
- deploy
k8s-deploy:
image: sasenomura/gcloud-kustomize
stage: deploy
script:
- echo $GKE_SERVICE_KEY > key.json
- gcloud auth activate-service-account $GKE_SERVICE_MAIL --key-file=key.json
- gcloud config set project $GKE_PROJECT_ID
- gcloud container clusters get-credentials $GKE_CLUSTER_NAME --zone $GKE_CLUSTER_ZONE
- kubectl config current-context
- kustomize build service1/k8s_overlay/ | kubectl apply -f - --prune -l app=service1-sample
only:
- master
imageに関してはkustomizeが入ったimageをあらかじめ登録しておいてdocker pull
するようにしている
gcloudにアクセスするときに使用するものをあらかじめgitlabの変数に設定しておく
と基本的なことはこんな感じで作成する.
only:master
でmasterにマージされたときのみ実行されるようになっている
もう少し応用的なことをやる
実際にはサービスによってプロジェクトが別れているので
$ tree
.
|-- k8s_base
| |-- .git
| |-- deployment.yaml
| |-- kustomization.yaml
| `-- service.yaml
|-- service1
| |-- .git
| |-- Dockerfile
| |-- k8s_overlay
| | |-- containers.yaml
| | |-- kustomization.yaml
| | `-- replicas.yaml
| `-- service1.go
`-- service2
|-- .git
|-- Dockerfile
|-- k8s_overlay
| |-- containers.yaml
| |-- kustomization.yaml
| `-- replicas.yaml
`-- service2.go
のようにそれぞれに
k8s_baseに.git
もあり,service1,2にもそれぞれ.git
があるようにしてservice1,2には,.gitlab-ci.yamlがあると便利になると思う.
そうするとservice1,2配下にある.gitlab-ci.yamlは以下のようになった
stages:
- test
- deploy
k8s-deploy:
image: sasenomura/gcloud-kustomize
stage: deploy
script:
- echo $GKE_SERVICE_KEY > key.json
- gcloud auth activate-service-account $GKE_SERVICE_MAIL --key-file=key.json
- gcloud config set project $GKE_PROJECT_ID
- gcloud container clusters get-credentials $GKE_CLUSTER_NAME --zone $GKE_CLUSTER_ZONE
- kubectl config current-context
- git clone <k8s_baseのリモートリポジトリ>
- kustomize build service1/k8s_overlay/ | kubectl apply -f - --prune -l app=service1-sample
only:
- master
やってることは
- gcloudにログインしてkubectlを使えるようにする
-
git clon
eでk8s_baseをもってくる -
kustomize build
とkubectl apply
まとめ(考察)
コードの記述量が少なくなると考えたがそれはそれぞれのサービスと,抽象度により変わるので一概に記述量が少なくなるとは言えないと考える
普段コードを書く人はcontainers.yamlだけ見て編集することが可能で,インフラとバックエンド,フロントを分けることができたと考える.
ただkustomizeが想定した使い方とは少し外れているので
今後どんなデメリットがあるのかは考える必要があるだろう
追記
ちなみに
ciのユーザがほぼ全ての権限をもっているなら以下のようにしてもいいのではないだろうか?
k8s-init:
image: sasenomura/gcloud-kustomize
stage: test
script:
- echo $GKE_SERVICE_KEY > key.json
- gcloud auth activate-service-account $GKE_SERVICE_MAIL --key-file=key.json
- gcloud config set project $GKE_PROJECT_ID
- gcloud container clusters get-credentials $GKE_CLUSTER_NAME --zone $GKE_CLUSTER_ZONE
- kubectl config current-context
- |-
if test $(kubectl get namespaces | grep $CI_PROJECT_NAME | wc -l) -eq 0; then
kubectl create namespace $CI_PROJECT_NAME
kubectl create secret docker-registry gitlab --docker-server=https://registry.gitlab.com/ --docker-username=$CI_REGISTRY_USER --docker-password=$CI_REGISTRY_PASSWORD --docker-email=$CI_REGISTRY_MAIL --namespace=$CI_PROJECT_NAME
fi
ciでnamespaceがあるかどうか確認して,もしなければnamespaceを作成したあとにgitlabレジストリをsecretで登録しておくような処理を行うとnamespaceを作成する手間も省ける.
これを使うかどうかは難しいところ....理由としてはgitlab userにnamespaceを作る権限があるからだ,つまりciのyamlを編集できる人は任意のnamespaceを作成することができる
権限には注意したほうがいい(2回目)
参考
- https://github.com/kubernetes-sigs/kustomize
- https://dev.classmethod.jp/cloud/aws/kustomize-yaml-how-to/
ありがとうございました.