kubernetesゼロから入門してみたので備忘録作ります

# ナニコレ

個人的な備忘録です。忘れた時に手早く探せるように...随時加筆です。間違ってるところもあるかもしれないでの、間違ったらご指摘していらだければ幸いです。

# 抽象的なもの

以下のものは[kubernetsのチュートリアル](https://kubernetes.io/ja/docs/tutorials/kubernetes-basics/)から引用：（個人的な理解も含めるので、間違ったらご指摘していらだければ幸いです）。

ホームページにあるように、k8sの特徴の一つは*Planet Scale*です。この特徴に相応しく、k8sの設計もまるで1個1個の惑星を繋いでいくようなものでした。

![k8s_01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2476308/96cb19fa-b8eb-37d4-b574-b2f1571ab1ca.png)

- `cluster`は全てのものを入れる容器のようなもの。バーチャルネットワークを作り、外部からアクセスできないようにする。
    - `control plane`はdeploymentを通してnodeなどを制御する中枢
        - `deployment`はどのようにnodeをコントロールするかの設定ファイルのようなもの
    - `node`はk８sのwoker machine。仮想マシンでも物理マシンでも構わないようです。
        - `kubelet`はnodeとmasterの間の通信を担当するプロセス。podとコンテナの管理もしている。
        - `pod`は実際にコンテナアプリを入れるもの
            - 中には複数のcontainerと共有スペース(volume)を持つことができる
            - nodeに障害が発生した場合、podはclusterないの別のnodeにスケジュールされる
    - `service`はどのようにpodを外部に公開するかを定義する抽象的概念
        - podは死んだり復活したりする（例えばnodeが死んだ時別のnodeにスケジュールする）ので、serviceは`LabelSelector`を使ってpodを抽象的に特定する。


# minikube / kubernets

まずは[このページ](https://minikube.sigs.k8s.io/docs/start/)へ行ってインストール

そして`kubectl`に関して普通に入れていいですが、簡単なやりかたでminikube経由で入れる
```bash
minikube kubectl -- get po -A
```

そしてalias設定、`.bashrc`などで永続化しても良さそう
```bash
alias kubectl="minikube kubectl --"
```

clusterを作る
```
minikube start
```

deploymentを作る
```bash
kubectl create deployment <deployment_name> --image=name:tag
```

local imageを使う
参考記事：https://miyazi888.hatenablog.com/entry/2022/05/22/194940
```bash
minikube image load image:tag
```

deploymentsの詳細を見る
何かを見る時はgo-templateを使って詳細を絞るのも良さそう。
参考記事：https://k69blog.com/posts/k8s_go_template/
```bash
kubectl describe deployments
```

deploymentのimageを更新
```bash
kubectl set image deployment/<deployment_name> <container_name>=image:tag
```

指定した revision まで　rollback
参考記事：https://tech-lab.sios.jp/archives/18550
```bash
kubectl rollout undo deployment <deployment_name> --to-revision=1
```

# gce

*前提*： gcloud というグーグルのcliをインストールしていること


auto pilotモードのclusterを作る
```bash
gcloud container clusters create-auto hello-cluster --region=<COMPUTE_REGION>
```

localからkubectlでgkeを操作できように
```bash
gcloud container clusters get-credentials gke-cloud-sql-quickstart --region us-central1
```

cloud buildを使ってソースコードからアプリをコンテナ化、そして出来たコンテナをArtifact Registoryなどのcloudにアップする。（すでにartifactにrepoが作成されている前提で話します）
artifactにpushするにはimage nameがかなり特徴的な書き方をしなければならない
```yaml
# cloudbuild.yaml
steps: # steps で build　して
  - name: "gcr.io/cloud-builders/docker"
    args:
      [
        "build",
        "-t",
        "<region>-docker.pkg.dev/<project_id>/<repo_name>/<dir_name>:tag",
        "-f",
        "./Dockerfile",
        ".",
      ]
images: "<region>-docker.pkg.dev/<project_id>/<directory>/image:tag" # images　で自動 push
```
書き終わったら gcloud で実行
```bash
gcloud builds submit --config ./cloudbuild.yaml ./
```

そして、yamlを書いてdeployment、serviceを作ってapply、など、普通k８s作業に入るので割愛。ちゃんとconsoleで可視化されているので結構みやすいと見た。

## 別のgcpサービスと連携

主に cloud sql と Memorystore(redis) の連携

### redis

まずはredis、redisは簡単で、redisのインスタンスを[コンソール上](https://cloud.google.com/memorystore/docs/redis?hl=ja)で作れば直接VPCネットワーク経由で繋げられる（auto pilod限定かも、スタンダードは--enable-ip-aliasが必要らしい）
golangで例を挙げると
```go
RDB = redis.NewClient(&redis.Options{
    Addr: rdbURI,
})
```
ここの　rdbURI を　redis instanceの`プライマリ エンドポイント`に置き換えれば直接つながる、とても楽。

### cloud sql

cloud sqlは結構ややこしい。ここれは mysql の例を使う。
まず[docsに従って](https://cloud.google.com/sql/docs/mysql?hl=ja)cloud sql instanceを作る。

作り終わったら[こちらのdocs](https://cloud.google.com/sql/docs/mysql/connect-instance-kubernetes?hl=ja#go)を見ながら着実に進みましょう。

auto pilotなら前準備なしですが、スタンダートなら Workload Identity をまず有効にしないといけない。（しなくても別の方法で接続できますが、安全性が下がるってdocsにも書いてあった）

そして実際に手を動かす前に、少し手順のおさらいをしましょう。

大雑把に行くと、gkeとcloud sqlの連携は`サービスアカウント(Service Account)`というものによって結び付けられている(binding)。

このサービスアカウント（以下グーグルの習慣に乗ってSAと呼ぶ）というのは、どのアプリがどのgcpのapiを使えるかを制御するもの（セキュリティ）。

そして中には`GSA`(google service account)というプロジェクト規模なものと`KSA`(kubernetes service account)というgke規模なものがある。なので、gkeからcloud sqlをアクセスするには、まず
1. cloud sqlをアクセスできる`GSA`を作る
1. sql操作する予定の`KSA`を作る
1. `GSA`と`KSA`を結びつける（bind）
1. cloud-sql-proxyを使って接続

では参ろう（初見で結構苦戦した私でござる

まずGSAを作る
```bash
# まずアカウント作る
gcloud iam service-accounts create <GSA_account_name> --display-name="gsa account display name"

# cloud sqlの権限を付与
gcloud projects add-iam-policy-binding <YOUR_PROJECT_ID> \
  --member="serviceAccount:<GSA_account_name>@<YOUR_PROJECT_ID>.iam.gserviceaccount.com" \
  --role="roles/cloudsql.client"

# logging書き込み権限付与
gcloud projects add-iam-policy-binding <YOUR_PROJECT_ID> \
  --member="serviceAccount:<GSA_account_name>@<YOUR_PROJECT_ID>.iam.gserviceaccount.com" \
  --role="roles/logging.logWriter"
```

続いてyamlを書いてKSAを作る
```yaml
# service-account.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
  name: <YOUR-KSA-NAME>
```
書いたらcmdで実行
```bash
kubectl apply -f service-account.yaml
```

GSA と KSA の binding です
```bash
gcloud iam service-accounts add-iam-policy-binding \
    --role="roles/iam.workloadIdentityUser" \
    --member="serviceAccount:<YOUR_PROJECT_ID>.svc.id.goog[<YOUR-K8S-NAMESPACE>/<YOUR-KSA-NAME>]" \
    <GSA_account_name>@<YOUR_PROJECT_ID>.iam.gserviceaccount.com
```
まだ終わってません、KSA側でもアノテーションをつけてバインドを完了させます
```bash
kubectl annotate serviceaccount \
    <YOUR-KSA-NAME> \
    iam.gke.io/gcp-service-account=<GSA_account_name>@<YOUR_PROJECT_ID>.iam.gserviceaccount.com
```

これで cli の作業は完了、これからは　deployment の作業に入る
実際に繋ぐコードはgolangのgormを予想して書きます
```yaml
# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: <deployment-name>
spec:
  selector:
    matchLabels:
      app: <app-lable>
  template:
    metadata:
      labels:
        app: <app-lable>
    spec:
      serviceAccountName: <YOUR-KSA-NAME>
      containers:
      - name: <your-container-name>
        image: <your-image-url>
        ports:
          - containerPort: <expose-port>
        env:
          - name: PORT
            value: "<expose-port>" # env の中は必ず文字列でなければいけない
          - name: DB_URI # 実際に　gorm で繋ぐためのものをかく、注意が必要なのは、cloud-sql-proxy使っているので直接localhostに繋ぎまず
            value: "<username>:<password>@tcp(127.0.0.1:3306)/<db_name>?charset=utf8mb4&parseTime=true"
      - name: cloud-sql-proxy # 重要： アプリのコンテナ以外、cloud-sql-proxyのコンテナが必要
        image: gcr.io/cloudsql-docker/gce-proxy:latest # image は　gce-proxy　を使う、公式チュートリアルでは gcr.io/cloud-sql-connectors/cloud-sql-proxy:latest を使う文章もあったが、こっちの方がいいです
        command:
          - "/cloud_sql_proxy"
          - "--log_debug_stdout=true"
          # - "-enable_iam_login" # この行は普通使わない、自動IAMログインが必要のときだけつける、必要のない時につけちゃうと繋がらないので注意
          - "-instances=<INSTANCE_CONNECTION_NAME>=tcp:3306"
        securityContext:
          runAsNonRoot: true
```
実際に繋ぐところのコードも見てみましょう。
公式は `kubectl secret`を使って環境変数に値を付与しているが、別に直接書いても繋がるので、安全面を考慮しながら好みでいいと思う。
```go
dbURI := os.Getenv("DB_URI")

db, err := gorm.Open(mysql.New(mysql.Config{
    DSN:                     dbURI,
    DontSupportRenameIndex:  true,
    DontSupportRenameColumn: true,
}), &gorm.Config{
    DisableForeignKeyConstraintWhenMigrating: true,
    CreateBatchSize:                          1000,
})
```

これで`kubectl apply -f deployment.yaml`すれば動くはずです。