Help us understand the problem. What is going on with this article?

kubernetesの管理を少しでも楽にしたい

More than 1 year has passed since last update.

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配下のファイルをまとめて書いている

k8s_base/*.yaml
# 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配下を見ていく

具体的なマニフェストを作成する

service1/k8s_service/*yaml
# 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 buildkubectl 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を作成する

.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
    - 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は以下のようになった

.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

やってることは
1. gcloudにログインしてkubectlを使えるようにする
2. git cloneでk8s_baseをもってくる
3. kustomize buildkubectl 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回目)

参考

ありがとうございました.

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away