データベースにデータを保存するアプリケーションは、機能開発やバグ等でのデプロイ時にほとんどのケースで、最初にデータのマイグレーションを実施する必要があります。

goのデータベースマイグレーションツール

goのデータベースマイグレーションツールは色々あります。よく利用するのは、gooseとgolang-migrateです。

goose

goのデータベースマイグレーションツールとしてよく名前に上がるものにgooseがあります。多くのドライバ、S3、Google Storageなどへの移行をサポートしています。

golang-migrate

golang-migrateは、多くのドライバをサポートしています。up, down, forceというシンプルなコマンドをサポートしています。

マイグレーションのアプローチ

データ移行用のプログラムと通常稼働時のロジックは、分けるように実装します。コンテナ化をベースに考える場合には、データ移行コンテナ、通常稼働用のコンテナという形で実装します。

コードは共通化するがエントリポイントを分ける

ソースコードを書いていると、データベースアクセスするロジックなど共通になる部分が多くなるので、同一プロジェクトレイアウトで扱えるような仕組みが嬉しいです。下記例では、migrationコマンドでDBマイグレーションを行い、serverコマンドでREST APIサーバとして起動するようなバイナリを作っています。DBのマイグレーションは、InitContainerで実行する形で行っています。

 
{{- $containerPort := 80 -}}
{{- $appName := "sample-usr-api" -}}
{{- $serviceName := "sample-user-api-svc" -}}
{{- $deploymentName := "sample-user-api-deployment" -}}
apiVersion: v1
kind: Service
metadata:
  name: {{ $serviceName }}
spec:
  type: ClusterIP
  selector:
    app: {{ $appName }}
  ports:
    - protocol: TCP
      nodePort: null
      port: 8080
      targetPort: {{ $containerPort }}
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ $deploymentName }}
spec:
  selector:
    matchLabels:
      app: {{ $appName }}
  replicas: 1
  template:
    metadata:
      labels:
        app: {{ $appName }}
    spec:
      initContainers:
        - name: migration-task
          image: {{ .Values.userapi.repository }}:{{ .Values.container.tag }}
          command: [ "./sample-api", "migrate", "up" ]
          env:
            - name: POSTGRES_USER
              value: apiadmin
            - name: POSTGRES_PASSWORD
              value: apiadmin
            - name: POSTGRES_HOST
              value: "sample-postgres-svc"
            - name: POSTGRES_PORT
              value: "5432"
            - name: DB_NAME
              value: "sample_db"
      containers:
        - name: sample-user-api
          image: {{ .Values.userapi.repository }}:{{ .Values.container.tag }}
          command: [ "./sample-api", "server" ]
          ports:
          - containerPort: {{ $containerPort }}
          env:
            - name: POSTGRES_USER
              value: apiadmin
            - name: POSTGRES_PASSWORD
              value: apiadmin
            - name: POSTGRES_HOST
              value: "sample-postgres-svc"
            - name: POSTGRES_PORT
              value: "5432"
            - name: DB_NAME
              value: "sample_db"

commandで実行できるようにしておけば、InitContainerで紹介したコンテナを起動して、マイグレーションを行っているロジックをJobとして切り出すこともできるので、コマンドベースで作成しておくと同一DBへのロジックは共通化でき、エントリポイントを分けることができるのでおすすめです。お疲れさまでした。