EC2 インスタンスで運用している GitLab を、 GKE に移行してみた。（Kubernetes Engine + Cloud SQL + Cloud Filestore）

# はじめに

EC2 インスタンスで運用中の GitLab（Omnibus パッケージ）を　GKE 上に移行させました。
本投稿はその際メモしていた移行手順に、イメージ図や簡単なコメントを追記したものとなります。


また移行に際し、次の GitHub プロジェクトを参考にさせてもらいました。
https://github.com/sameersbn/docker-gitlab

<br /><br />ちなみにプロジェクト主である **sameersbn** さんですが、ご尊顔も公開されていました。
![](https://avatars2.githubusercontent.com/u/410147?s=400&v=4)

・・・この方は、いったい何者なんでしょ？


# 前提条件

- GKE に **リージョン クラスタ** を作成済み
- AWS VPC と GCP VPC 間は VPN で接続済み（[以前の記事参照](https://qiita.com/nagase/items/df18e308e6fc98e11aaa)）
- 作業用のローカルマシン（CentOS7）は Docker（docker-ce-18.03.1） が稼働済み
- 作業用のローカルマシンには、Google Cloud SDK（v212.0.0）、kubectl（v1.11.2） をインストール、初期設定済みであり、GKE クラスタに接続済み
- 移行元の GitLab バージョンは、移行先のバージョン（v11.1.4）と同一になるよう、移行作業前にアップグレード
- 下表の情報を想定して環境を構築

|項目|値|備考|
|---|---|---|
|プロジェクトID|my-project||
|リージョン|asia-northeast1|東京リージョン|
|VPC|aws-internal-connect|AWS VPC間でVPN接続済み|
|Cloud SQL インスタンスID|gitlab-pgsql||
|Cloud SQL データベース名|gitlabhq_production||
|Cloud SQL データベースバージョン|POSTGRES_9_6||
|Cloud SQL ユーザーID|gitlab||
|Cloud SQL Proxy デプロイ名|cloudsqlproxy||
|Cloud SQL Proxy サービス名|sqlproxy-gitlab||
|Cloud Filestore インスタンスID|nfs-server||
|Cloud Filestore リージョン|asia-east1|東京リージョンでは未提供の為|
|Cloud Filestore 共有名|NFSvol||
|GitLab デプロイ名、サービス名|gitlab||
|Redis デプロイ名、サービス名|redis||
|Active Directory IPアドレス|10.0.1.123|AWSで稼働中のAD|

# 実装イメージ

![topology.png](https://qiita-image-store.s3.amazonaws.com/0/94476/049ab363-f7b2-9ff7-9703-0db567cf77c5.png)


# 移行元 GitLab のバージョンアップ

EC2上で稼働中のGitLab（Omnibusパッケージ）のバージョンを、[この辺りの情報](https://namionobkup.hatenablog.com/entry/2018/07/09/235651)を参考にアップグレードする。
※9.5 ⇒ 11.1.4（2018.08現在の最新版）


# Cloud SQL に PostgreSQL インスタンス作成

GitLab が利用するデータベースには、Cloud SQL （データベースエンジン=PostgreSQL 9.6）を利用する。

### スクリプト作成

```bash:create-cloudsql-pgsql.sh
#!/bin/bash

INSTANCE_ID=gitlab-pgsql        #インスタンスID
REGION=asia-northeast1          #インスタンスを作成するリージョン
DATABASE_VERSION=POSTGRES_9_6   #データベースバージョン
NUMBER_CPUS=1                   #割当てvCPU数
MEMORY_SIZE=3840MiB             #割当てメモリ容量
STORAGE_TYPE=SSD                #ストレージの種類（HDD|SSD）
STORAGE_SIZE=10GB               #ストレージの容量（GB単位）

gcloud beta sql instances create $INSTANCE_ID \
    --region=$REGION \
    --database-version=$DATABASE_VERSION \
    --cpu=$NUMBER_CPUS \
    --memory=$MEMORY_SIZE \
    --storage-type=$STORAGE_TYPE \
    --storage-size=$STORAGE_SIZE \
    --storage-auto-increase
```

### スクリプト実行、確認

```bash
$ sh create-cloudsql-pgsql.sh

$ gcloud beta sql instances list
NAME          DATABASE_VERSION  LOCATION           TIER              ADDRESS         STATUS
gitlab-pgsql  POSTGRES_9_6      asia-northeast1-b  db-custom-1-3840  35.XXX.XXX.XXX  RUNNABLE
```

## デフォルトユーザー（postgres）にパスワード設定

インスタンスを作成すると、`postgres` ユーザーがデフォルトで登録されるため、そのパスワードを設定する。

### スクリプト作成

```bash:set-passwd.sh
#!/bin/bash

INSTANCE_ID=gitlab-pgsql      #インスタンスID
PASSWORD=<PASSWORD>           #'postgres' ユーザーのパスワード


gcloud sql users set-password postgres no-host \
    --instance=$INSTANCE_ID \
    --password=$PASSWORD
```

### スクリプト実行


```bash
$ sh set-passwd.sh
```

## インスタンスにデータベース作成

GitLab が利用するデータベース `gitlabhq_production` を作成する。

### スクリプト作成

```bash:add_sql_database.sh
#!/bin/bash

DATABASE_NAME=gitlabhq_production       #データベース名
INSTANCE_ID=gitlab-pgsql                #インスタンスID

gcloud sql databases create $DATABASE_NAME \
    --instance=$INSTANCE_ID
```

### スクリプト実行、確認

```bash
$ sh add_sql_database.sh

$ gcloud sql databases list --instance=gitlab-pgsql
NAME                 CHARSET  COLLATION
postgres             UTF8     en_US.UTF8
gitlabhq_production  UTF8     en_US.UTF8
```

## ユーザー作成

データベース接続ユーザー `gitlab` を作成する。

ちなみにこの `gitlab` ユーザーは、管理者権限を持つユーザー（cloudsqlsuperuser）として作成される。

### スクリプト作成

```bash:add_sql_user.sh
#!/bin/bash

USER_NAME=gitlab              #ユーザーID
HOST=%                        #アクセス許可ホスト
INSTANCE_ID=gitlab-pgsql      #インスタンスID
PASSWORD=<PASSWORD>           #ユーザーパスワード

gcloud sql users create $USER_NAME \
    --host=$HOST \
    --instance=$INSTANCE_ID \
    --password=$PASSWORD
```

### スクリプト実行、確認

```bash
$ sh add_sql_user.sh

$ gcloud sql users list --instance=gitlab-pgsql
NAME      HOST
gitlab
postgres
```

# Cloud SQL Proxy の Pod、Service の作成 

Cloud SQL のデータベースを Pod から直接接続して利用することも可能だが、接続管理（ホワイトリスト、SSL等）が煩雑になる。
Cloud SQL Proxy を利用することで、安全、簡単に接続できるようになる。

尚、独立した Pod として実装する方法は、[ここの情報](https://github.com/GoogleCloudPlatform/cloudsql-proxy/blob/master/Kubernetes.md)を参考にした。

## サービスアカウントの作成

Cloud SQL クライアントの役割を持つサービスアカントを作成し、サービスアカウントキーをローカルにダウンロードする。

### スクリプト作成

```bash:create-service-account.sh
#!/bin/bash

KEYFILE_NAME=$HOME/credentials.json #サービスアカウントの秘密鍵ファイルパス
PROJECT_ID=my-project               #プロジェクトID
ACCOUNT_NAME=cloudsql               #サービスアカウント名

#サービスアカウントの作成
gcloud iam service-accounts create $ACCOUNT_NAME \
    --display-name="$ACCOUNT_NAME"

#サービスアカウントへ役割（Cloud SQL クライアント）付与
gcloud projects add-iam-policy-binding $PROJECT_ID \
    --member="serviceAccount:$ACCOUNT_NAME@$PROJECT_ID.iam.gserviceaccount.com" \
    --role="roles/cloudsql.client"

#サービスアカウントキーの作成
gcloud iam service-accounts keys create $KEYFILE_NAME \
    --iam-account $ACCOUNT_NAME@$PROJECT_ID.iam.gserviceaccount.com
```

### スクリプト実行

```bash
$ sh create-service-account.sh
```

## 秘密鍵を GKE に登録

ダウンロードしたサービスアカウントキー（秘密鍵）を GKE の Secret に登録する。
GKE に登録することで、Pod（Cloud SQL Proxy）から参照できるようになる。

### スクリプト作成

```bash:create-secret.sh
#!/bin/bash

KEYFILE_NAME=$HOME/credentials.json     #秘密鍵ファイルパス

kubectl create secret generic service-account-token \
    --from-file=credentials.json=$KEYFILE_NAME
```

### スクリプト実行、確認

```bash
$ sh create-secret.sh

$ kubectl get secret
NAME                    TYPE      DATA      AGE
service-account-token   Opaque    1         4m
```

## Cloud SQL Proxy のデプロイ

今回 Cloud SQL Proxy は独立した Pod として運用するため、デプロイする必要がある。

### デプロイ定義ファイル作成

```yaml:deployment.yaml
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: cloudsqlproxy
spec:
  replicas: 1
  template:
    metadata:
      labels:
        app: cloudsqlproxy
    spec:
      containers:
       # Make sure to specify image tag in production
       # Check out the newest version in release page
       # https://github.com/GoogleCloudPlatform/cloudsql-proxy/releases
      - image: b.gcr.io/cloudsql-docker/gce-proxy:latest
       # 'Always' if imageTag is 'latest', else set to 'IfNotPresent'
        imagePullPolicy: Always
        name: cloudsqlproxy
        command:
        - /cloud_sql_proxy
        - -dir=/cloudsql
        - -instances=my-project:asia-northeast1:gitlab-pgsql=tcp:0.0.0.0:5432,my-project:asia-northeast1:app-mysql=tcp:0.0.0.0:3306
        - -credential_file=/credentials/credentials.json
        ports:
        - name: port-app-mysql
          containerPort: 3306
        - name: port-gitlab-pgsql
          containerPort: 5432
        volumeMounts:
        - mountPath: /cloudsql
          name: cloudsql
        - mountPath: /credentials
          name: service-account-token
      volumes:
      - name: cloudsql
        emptyDir:
      - name: service-account-token
        secret:
          secretName: service-account-token
```

`volumeMounts` には秘密鍵をマウントする定義があり、`command` で Cloud SQL のインスタンスへの接続が定義されていることが判る。

尚、上記 YAML ファイルでは `gitlab-pgsql` インスタンス以外に、今回は利用しない `app-mysql` インスタンスへの接続も定義している。
このように Cloud SQL Proxy は、Cloud SQL の複数インスタンスに対しプロキシすることもできる。

また必要が無ければ、`app-mysql` の接続に関する定義は削除しても構わない。


### デプロイ実行、確認

```bash
$ kubectl create -f deployment.sh

$ kubectl get pod
NAME                             READY     STATUS    RESTARTS   AGE
cloudsqlproxy-XXXXXXXXXX-XXXXX   1/1       Running   0          4m
```

## Cloud SQL Proxy の Service 作成

### サービス定義ファイル作成

今回の定義情報には、前述のプロキシ定義にある `app-mysql` への定義も記載している。
参考程度に記載しているだけなので、必要が無ければ削除しても構わない。

```yaml:service.yaml
apiVersion: v1
kind: Service
metadata:
  name: sqlproxy-app
spec:
  ports:
  - port: 3306
    targetPort: port-app-mysql
  selector:
    app: cloudsqlproxy
---
apiVersion: v1
kind: Service
metadata:
  name: sqlproxy-gitlab
spec:
  ports:
  - port: 5432
    targetPort: port-gitlab-pgsql
  selector:
    app: cloudsqlproxy
```

### サービス作成、確認

```bash
$ kubectl apply -f service.yaml

$ kubectl get svc
NAME                TYPE           CLUSTER-IP     EXTERNAL-IP   PORT(S)                     AGE
sqlproxy-app        ClusterIP      10.1.163.153   <none>        3306/TCP                    4m
sqlproxy-gitlab     ClusterIP      10.1.160.34    <none>        5432/TCP                    4m
```

# 永続ディスクの準備

GKE の **ゾーン クラスタ** は単一のゾーン内にノードが作成される為、永続ディスクに GCE ディスクを用いても特に問題は起きない。（ネット上の情報は、この方法で解説されている場合が多い。）


今回実装に用いる**リージョン クラスタ**では、ノードがリージョン内の複数のゾーン（例えば `asia-northeast1-a`、`asia-northeast1-b`、`asia-northeast1-c`）に作成されるため、**ゾーンを跨いでマウントすることが出来ない** GCE ディスクを利用すると問題が生じてしまう。

そこで今回の永続ディスクには **Cloud Filestore** を利用することにする。

尚、投稿時点で東京リージョンでは提供されていないため、距離的に近い台湾リージョン（asia-east1）に Filestore インスタンスを作成する。

## Cloud Filestore インスタンス作成

### スクリプト作成

```bash:create-instance.sh
#!/bin/bash

INSTANCE_ID=nfs-server            #インスタンスID
PROJECT_ID=my-project             #プロジェクトID
LOCATION=asia-east1-a             #ロケーション（≒ゾーン）
TIER=STANDARD                     #サービス層（種別） （STANDARD|PREMIUM）
SHARE_NAME=NFSvol                 #共有名
CAPACITY=1TB                      #割当て容量
VPC=aws-internal-connect          #VPC

gcloud beta filestore instances create $INSTANCE_ID \
    --project=$PROJECT_ID \
    --location=$LOCATION \
    --tier=$TIER \
    --file-share=name="$SHARE_NAME",capacity=$CAPACITY \
    --network=name="$VPC"
```

### スクリプト実行、確認

```bash
$ sh create-instance.sh

$ gcloud beta filestore instances list
INSTANCE_NAME  LOCATION      TIER      CAPACITY_GB  FILE_SHARE_NAME  IP_ADDRESS     STATE  CREATE_TIME
nfs-server     asia-east1-a  STANDARD  1024         NFSvol           10.32.XXX.XXX  READY  2018-08-21T02:56:54
```

## インスタンス内にディレクトリ作成

GCP上の適当なVMインスタンスへログイン後、共有ボリュームをマウントし、予め `gitlab`、`redis` ディレクトリを作成しておく。
ここで作成したディレクトリを、Pod の永続ディスクとして利用する。

```bash
#共有ボリュームマウント
$ sudo mount 10.32.XXX.XXX:/NFSvol /mnt

#gitlab, redis ディレクトリ作成
$ sudo mkdir /mnt/gitlab /mnt/redis

#ディレクトリ確認
$ ls /mnt
gitlab  lost+found  redis

#共有ボリュームのアンマウント
$ sudo umount /mnt
```

## Persistent Volume 作成

共有ボリュームに、２つの Persistent Volume（PV）を作成する。
（GitLabが利用する PV と、Redis が利用する PV）

### PV定義ファイル作成

```yaml:pv.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv-gitlab
  labels:
    app: pv-gitlab
  annotations:
    volume.beta.kubernetes.io/storage-class: "nfs"
spec:
  capacity:
    storage: 30Gi
  accessModes:
  - ReadWriteMany
  nfs:
    path: /NFSvol/gitlab
    server: 10.32.XXX.XXX
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv-redis
  labels:
    app: pv-redis
  annotations:
    volume.beta.kubernetes.io/storage-class: "nfs"
spec:
  capacity:
    storage: 30Gi
  accessModes:
  - ReadWriteMany
  nfs:
    path: /NFSvol/redis
    server: 10.32.XXX.XXX
```

### PV作成、確認

```bash
$ kubectl create -f pv.yaml

$ kubectl get pv
NAME        CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS    CLAIM                STORAGECLASS   REASON    AGE
pv-gitlab   30Gi       RWX            Retain           Bound     default/pvc-gitlab   nfs                      3m
pv-redis    30Gi       RWX            Retain           Bound     default/pvc-redis    nfs                      3m
```

## Persistent Volume Claim の作成

Pod のPV要求（Persistent Volume Claim:PVC）を２つ作成する。
※`selector`を定義し、要求するPVを限定している。

### PVC定義ファイル作成

```yaml:pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pvc-gitlab
spec:
  accessModes:
    - ReadWriteMany
  storageClassName: "nfs"
  resources:
    requests:
      storage: 30Gi
  selector:
    matchLabels:
      app: pv-gitlab
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pvc-redis
spec:
  accessModes:
    - ReadWriteMany
  storageClassName: "nfs"
  resources:
    requests:
      storage: 30Gi
  selector:
    matchLabels:
      app: pv-redis
```

### PVC 作成、確認

```bash
$ kubectl create -f pvc.yaml

$ kubectl get pvc
NAME         STATUS    VOLUME      CAPACITY   ACCESS MODES   STORAGECLASS   AGE
pvc-gitlab   Bound     pv-gitlab   30Gi       RWX            nfs            3m
pvc-redis    Bound     pv-redis    30Gi       RWX            nfs            3m
```


https://github.com/sameersbn/docker-gitlab

# Docker イメージのアップロード

Redis、GitLab の Docker イメージをローカルにダウンロード、タグ付けし、Container Registry にアップロードする。

## Redis イメージのアップロード

### Docker イメージ の Pull

```bash
$ docker pull sameersbn/redis:4.0.9-1
```

### イメージにタグ付け

```bash
$ docker tag sameersbn/redis:4.0.9-1 asia.gcr.io/my-project/redis:4.0.9-1
```

### イメージのアップロード、確認

```bash
$ gcloud docker -- push asia.gcr.io/my-project/redis:4.0.9-1

$ gcloud container images list-tags asia.gcr.io/my-project/redis
DIGEST        TAGS     TIMESTAMP
94872deed848  4.0.9-1  2018-07-28T23:45:25
```

## GitLab イメージのアップロード

### Docker イメージ の Pull

```bash
$ docker pull sameersbn/gitlab:11.1.4
```

### イメージにタグ付け

```bash
$ docker tag sameersbn/gitlab:11.1.4 asia.gcr.io/my-project/gitlab:11.1.4
```

### イメージのアップロード、確認

```bash
$ gcloud docker -- push asia.gcr.io/my-project/gitlab:11.1.4

$ gcloud container images list-tags asia.gcr.io/my-project/gitlab
DIGEST        TAGS    TIMESTAMP
9886c6781e11  11.1.4  2018-08-02T07:03:26
```

# Redis の準備

## Redis のデプロイ

### デプロイ定義ファイル作成

前述で作成した PVC `pvc-redis` を `volumes` に定義し、`volumeMounts` でマウント情報を定義する。

```yaml:deploy-redis.yaml
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  annotations:
    deployment.kubernetes.io/revision: "1"
  generation: 1
  labels:
    run: redis
  name: redis
  namespace: default
spec:
  replicas: 1
  selector:
    matchLabels:
      run: redis
  strategy:
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 1
    type: RollingUpdate
  template:
    metadata:
      creationTimestamp: null
      labels:
        run: redis
    spec:
      containers:
      - image: asia.gcr.io/my-project/redis:4.0.9-1
        imagePullPolicy: Always
        name: redis
        env:
        - name: TZ
          value: "Asia/Tokyo"
        ports:
        - containerPort: 6379
          protocol: TCP
        volumeMounts:
        - name: redis-data
          mountPath: "/var/lib/redis"
        resources:
          requests:
            cpu: 100m
        terminationMessagePath: /dev/termination-log
        terminationMessagePolicy: File
      volumes:
        - name: redis-data
          persistentVolumeClaim:
            claimName: "pvc-redis"
      dnsPolicy: ClusterFirst
      restartPolicy: Always
      schedulerName: default-scheduler
      securityContext: {}
      terminationGracePeriodSeconds: 30
```

### デプロイの作成、確認

```bash
$ kubectl create -f deploy-redis.yaml

$ kubectl get pod
NAME                     READY     STATUS    RESTARTS   AGE
redis-XXXXXXXXXX-XXXXX   1/1       Running   0          1m
```

## Redis サービスの作成

### サービス定義ファイルの作成

```yaml:service-redis.yaml
apiVersion: v1
kind: Service
metadata:
  name: redis
  namespace: default
  annotations:
    cloud.google.com/load-balancer-type: "internal"
  labels:
    run: redis
spec:
  type: LoadBalancer
  #loadBalancerIP: [IP-ADDRESS]
  ports:
  - port: 6379
    protocol: TCP
    targetPort: 6379
  selector:
    run: redis
```

### サービスの作成、確認

```bash
$ kubectl create -f service-redis.yaml

$ kubectl get svc
NAME      TYPE           CLUSTER-IP     EXTERNAL-IP   PORT(S)          AGE
redis     LoadBalancer   10.1.163.115   10.1.1.4      6379:31570/TCP   1m
```

# GitLab の準備

## 環境変数の設定

今回利用する GitLab イメージは、Podに環境変数を渡すことにより初期設定を行うことができる。
それら環境変数を **ConfigMap** と **Secret** に分けて設定しておく。 

### ConfigMap 定義ファイル作成

設定できる環境変数の一覧は[こちらのページ](https://github.com/sameersbn/docker-gitlab#available-configuration-parameters)に記載されている。

```yaml:gitlab-configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: gitlab-configmap
data:
  DEBUG: "false"
  DB_ADAPTER: postgresql
  DB_HOST: sqlproxy-service-gitlab-pgsql
  DB_PORT: "5432"
  DB_NAME: gitlabhq_production
  REDIS_HOST: redis
  REDIS_PORT: "6379"
  TZ: Asia/Tokyo
  GITLAB_TIMEZONE: Tokyo
  GITLAB_HTTPS: "false"
  SSL_SELF_SIGNED: "false"
  GITLAB_HOST: gitlab
  GITLAB_PORT: "80"
  GITLAB_SSH_PORT: "22"
  GITLAB_SECRETS_DB_KEY_BASE: long-and-random-alphanumeric-string
  GITLAB_SECRETS_SECRET_KEY_BASE: long-and-random-alphanumeric-string
  GITLAB_SECRETS_OTP_KEY_BASE: long-and-random-alphanumeric-string
  GITLAB_NOTIFY_ON_BROKEN_BUILDS: "true"
  GITLAB_NOTIFY_PUSHER: "false"
  GITLAB_EMAIL: gitlab@example.co.jp
  GITLAB_EMAIL_REPLY_TO: noreply@example.co.jp
  GITLAB_BACKUP_SCHEDULE: daily
  GITLAB_BACKUP_TIME: 01:00
  SMTP_ENABLED: "true"
  SMTP_DOMAIN: example.co.jp
  SMTP_HOST: smtp.gmail.com
  SMTP_PORT: "587"
  SMTP_STARTTLS: "true"
  SMTP_AUTHENTICATION: login
  OAUTH_ENABLED: "false"
  OAUTH_BLOCK_AUTO_CREATED_USERS: "true"
  OAUTH_AUTO_LINK_LDAP_USER: "false"
  OAUTH_AUTO_LINK_SAML_USER: "false"
  OAUTH_CAS3_LABEL: cas3
  OAUTH_CAS3_DISABLE_SSL_VERIFICATION: "false"
  OAUTH_CAS3_LOGIN_URL: /cas/login
  OAUTH_CAS3_VALIDATE_URL: /cas/p3/serviceValidate
  OAUTH_CAS3_LOGOUT_URL: /cas/logout
  OAUTH_SAML_LABEL: Our SAML Provider
  OAUTH_SAML_NAME_IDENTIFIER_FORMAT: urn:oasis:names:tc:SAML:2.0:nameid-format:transient
  LDAP_ENABLED: "true"
  LDAP_LABEL: example.local
  LDAP_HOST: 10.0.1.123
  LDAP_PORT: "389"
  LDAP_UID: sAMAccountName
  LDAP_METHOD: plain
  LDAP_ACTIVE_DIRECTORY: "true"
  LDAP_BASE: DC=example,DC=local
```

### ConfigMap 作成、確認

```bash
$ kubectl create -f gitlab-configmap.yaml

$ kubectl get configmap gitlab-configmap -o yaml
apiVersion: v1
data:
  DB_ADAPTER: postgresql
  DB_HOST: sqlproxy-service-gitlab-pgsql
  DB_NAME: gitlabhq_production
  DB_PORT: "5432"

  ･･･

  SMTP_STARTTLS: "true"
  SMTP_AUTHENTICATION: login
  SSL_SELF_SIGNED: "false"
  TZ: Asia/Tokyo
kind: ConfigMap
metadata:
  creationTimestamp: 2018-08-21T01:31:59Z
  name: gitlab-configmap
  namespace: default
  resourceVersion: "3616639"
  selfLink: /api/v1/namespaces/default/configmaps/gitlab-configmap
  uid: ff9dbd94-a4e1-11e8-aadc-42010a920042
```

### シークレット定義ファイル作成

環境変数は、全て上記 ConfigMap に記載することが可能だが、アカウントやパスワード、トークン等、セキュリティ上プレーンテキストで設定したくない情報は、**Secret** 定義に環境変数の値を BASE64 エンコードして記述することができる。

今回は Cloud SQL 、SMTPサーバ（Gmail）、ADサーバ（AWS側）の接続アカウント情報を、BASE64 エンコードして記載した。

```yaml:gitlab-secret.yaml
apiVersion: v1
kind: Secret
metadata:
  name: gitlab-secret
data:
  DB_USER: Z2l0bGFi                       #echo -n "gitlab" | base64
  DB_PASS: MWx4THhrRDg=                   #echo -n "<PASSWORD>" | base64
  SMTP_USER: aG9nZUBleGFtcGxlLmNvLmpw     #echo -n "hoge@example.co.jp" | base64
  SMTP_PASS: VDhkbTNuc0s=                 #echo -n "<PASSWORD>" | base64
  LDAP_BIND_DN: Q049QWRtaW5pc3RyYXRvcixDTj1Vc2VycyxEQz1leGFtcGxlLERDPWxvY2Fs #echo -n "CN=Administrator,CN=Users,DC=example,DC=local" | base64
  LDAP_PASS: TUczNHBkYw==                 #echo -n "<PASSWORD> | base64
```

### シークレット作成、確認

```bash
$ kubectl create -f gitlab-secret.yaml

$ kubectl get secret gitlab-secret -o yaml
apiVersion: v1
data:
  DB_PASS: MWx4THhrRDg=
  DB_USER: Z2l0bGFi
  LDAP_BIND_DN: Q049QWRtaW5pc3RyYXRvcixDTj1Vc2VycyxEQz1leGFtcGxlLERDPWxvY2Fs
  LDAP_PASS: TUczNHBkYw==
  SMTP_USER: aG9nZUBleGFtcGxlLmNvLmpw
  SMTP_PASS: VDhkbTNuc0s=
kind: Secret
metadata:
  creationTimestamp: 2018-08-21T01:30:02Z
  name: gitlab-secret
  namespace: default
  resourceVersion: "3616376"
  selfLink: /api/v1/namespaces/default/secrets/gitlab-secret
  uid: b9ffbc3e-a4e1-11e8-a346-42010a920024
type: Opaque
```

## GitLabのデプロイ

### デプロイ定義ファイル作成

前述で作成した PVC `pvc-gitlab` を `volumes` に定義し、`volumeMounts` でマウント情報を定義する。

また今回の GitLab は Internal からのみアクセス可能なサービスとして設定する為、コンテナポート 443（SSL）の定義は省略する。

```yaml:deploy-gitlab.yaml
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  annotations:
    deployment.kubernetes.io/revision: "1"
  generation: 1
  labels:
    run: gitlab
  name: gitlab
  namespace: default
spec:
  replicas: 1
  selector:
    matchLabels:
      run: gitlab
  strategy:
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 1
    type: RollingUpdate
  template:
    metadata:
      creationTimestamp: null
      labels:
        run: gitlab
    spec:
      containers:
      - image: asia.gcr.io/my-project/gitlab:11.1.4
        imagePullPolicy: Always
        name: gitlab
        envFrom:
        - configMapRef:
            name: gitlab-configmap
        - secretRef:
            name: gitlab-secret
        ports:
        - containerPort: 80
          protocol: TCP
        - containerPort: 22
          protocol: TCP
        volumeMounts:
        - name: gitlab-data
          mountPath: "/home/git/data"
        resources:
          requests:
            cpu: 100m
        terminationMessagePath: /dev/termination-log
        terminationMessagePolicy: File
      volumes:
        - name: gitlab-data
          persistentVolumeClaim:
            claimName: "pvc-gitlab"
      dnsPolicy: ClusterFirst
      restartPolicy: Always
      schedulerName: default-scheduler
      securityContext: {}
      terminationGracePeriodSeconds: 30
```

尚、今回はリソース要求（resources: requests:）、制限（resources: limits:）は GKE のデフォルト値である `cpu: 100m` のみ設定している。

※GKE では リソース要求は `cpu` のみ設定されており、制限は特に設定されていない。

```bash:Example
$ kubectl get LimitRange -o yaml
apiVersion: v1
items:
- apiVersion: v1
  kind: LimitRange
  metadata:

    ･･･

  spec:
    limits:
    - defaultRequest:
        cpu: 100m
      type: Container
kind: List

  ･･･
```

この辺りは利用ユーザー数やリソース消費状況（`kubectl top node`、`kubectl describe node`）を確認しつつ、適切にプランニングしたほうが良いだろう。

```yaml:Example
･･･
        resources:
          requests:
            cpu: 100m
            memory: 1Gi
          limits:
            cpu: 1000m
            memory: 2Gi
･･･
```

### デプロイ実行、確認

```bash
$ kubectl create -f deploy-gitlab.yaml

$ kubectl get pod
NAME                     READY     STATUS    RESTARTS   AGE
gitlab-XXXXXXXXX-XXXXX   1/1       Running   0          5m
```

## GitLab サービスの作成

### サービス定義ファイル作成

```yaml:service-gitlab.yaml
apiVersion: v1
kind: Service
metadata:
  name: gitlab
  namespace: default
  annotations:
    cloud.google.com/load-balancer-type: "internal"
  labels:
    run: gitlab
spec:
  type: LoadBalancer
  ports:
  - port: 80
    name: gitlab-http
    protocol: TCP
    targetPort: 80
  - port: 22
    name: gitlab-ssh
    protocol: TCP
    targetPort: 22
  selector:
    run: gitlab
```

### サービス作成、確認

```bash
$ kubectl create -f service-gitlab.yaml

$ kubectl get svc
NAME      TYPE           CLUSTER-IP    EXTERNAL-IP   PORT(S)                     AGE
gitlab    LoadBalancer   10.1.163.88   10.1.1.11     80:30488/TCP,22:30118/TCP   5m
```

# GitLab 起動確認

## DNS に GitLab を登録

GitLab サービスに割り当てられた IP アドレス（今回の例では `EXTERNAL-IP` に割り当てられた `10.1.1.11`）が名前解決できるよう、内部向け DNS サーバにホスト `gitlab.example.local` を登録する。

尚、内部向け DNS サーバへの登録方法は割愛。

## サーバにアクセス

ブラウザから http://gitlab/ （または http://gitlab.example.local/） にアクセスして、下図のページが表示されることを確認する。

![screencapture-gitlab-users-password-edit-2018-08-22-15_32_57.png](https://qiita-image-store.s3.amazonaws.com/0/94476/d39dd1c8-2115-262d-4657-2dd2a7deb64c.png)


尚、開いたページは管理者（root）パスワードを変更するよう促しているが、後述のデータ移行により移行元サーバから管理者アカウント情報も移行される為、そのままページを閉じて構わない。


# データ移行

## 移行元データをバックアップ

移行元の GitLab サーバにログインし、次のコマンドでデータをバックアップする。

```bash
$ sudo gitlab-rake gitlab:backup:create
```

デフォルトでは `/var/opt/gitlab/backups` 下に `XXXXXXXXXX_YYYY_MM_DD_11.1.4_gitlab_backup.tar` というファイル名で保存される。

## 移行先にデータ転送

上記バックアップファイルをGKE上のGitLab Pod の `/home/git/data/backups`（Cloud Filesore 上の `/<Mount Point>/gitlab/backups`）下に転送する。

※VMインスタンスに Cloud Filestore を NFS マウントして scp する等、**力業**で転送すること

## 移行先でリストア

`BACKUP` パラメータに、**ファイル名**から `_gitlab_backup.tar` を除いた文字列を指定する点に注意すること。

```bash
$ kubectl exec -it gitlab-XXXXXXXXXX-XXXXX /bin/bash

/home/git/gitlab# sudo -u git -H bundle exec rake RAILS_ENV=production gitlab:backup:restore BACKUP=XXXXXXXXXX_YYYY_MM_DD_11.1.4
```

リストア後、ブラウザから http://gitlab/ を開いてログインし、移行元から全プロジェクトが移行されていることを確認する。


# エンドユーザー側での作業

GitLab の移行が完了すれば利用者からもアクセスできるようになるが、エンドユーザー側のローカルリポジトリには移行元のリモートリポジトリが設定されたままだ。
このままでは移行元に対し `git pull`、`git push` してしまう恐れがあるので、リモートリポジトリに設定されている URI を、次の手順で変更してもらう。

```
リモートリポジトリの確認
> cd <ローカルプロジェクトのフォルダパス>
> git remote -v
origin  git@<移行元ホスト名>:foo/bar.git (fetch)
origin  git@<移行元ホスト名>:foo/bar.git (push)


リモートリポジトリの URI 変更
> git remote set-url origin git@gitlab:foo/bar.git


リモートリポジトリの変更確認
> git remote -v
origin  git@gitlab:foo/bar.git (fetch)
origin  git@gitlab:foo/bar.git (push)


fetch 実行
> git fetch origin
The authenticity of host 'gitlab (10.1.1.11)' can't be established.
ECDSA key fingerprint is SHA256:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX.
ECDSA key fingerprint is MD5:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX.
Are you sure you want to continue connecting (yes/no)? yes    ←'yes' 入力
```