2020年8月7日金曜日

Gitlab.com で公開されている Gitlab の各種ドキュメントをビルドしてローカルで確認する方法

概要

例えば omnibus-gitlab に含まれるドキュメントを修正したい場合には Markdown を修正してローカルで動作確認してから pull-request を送りたくなります
今回はローカルでドキュメントをビルドする方法を紹介します

環境

  • macOS 10.15.6
  • Ruby 2.7.1p83
    • bundler 2.1.4
  • Nodejs 14.5.0
    • yarn 1.22.4

ビルドまでの流れ

Gitlab のドキュメントは複数のリポジトリに別れているためそれぞれ clone する必要があります
ドキュメント自体はそれぞれのプロダクトのリポジトリで Markdown で管理されておりスタイルは gitlab-docs というリポジトリで管理されています
つまり各プロダクトのドキュメントを clone してスタイルを管理しているリポジトリ上に紐付けてビルドすればローカルで確認できます

各種リポジトリの clone

作業用のディレクトリを作成して必要なリポジトリを clone します

  • mkdir build-gitlab-docs
  • cd build-gitlab-docs
  • git clone --depth 1 https://gitlab.com/gitlab-org/omnibus-gitlab.git
  • git clone --depth 1 https://gitlab.com/gitlab-org/gitlab-runner.git
  • git clone --depth 1 https://gitlab.com/gitlab-org/gitlab.git
  • git clone --depth 1 https://gitlab.com/gitlab-org/charts/gitlab.git charts
  • git clone --depth 1 https://gitlab.com/gitlab-org/gitlab-docs.git

スタイルのリポジトリに各リポジトリを紐付ける

次に gitlab-docs リポジトリに各リポジトリを紐付けます
紐付けると言ったもシンボリックリンクを作成するだけです

  • cd ..
  • ln -s $(pwd)/build-gitlab-docs/omnibus-gitlab/doc build-gitlab-docs/gitlab-docs/content/omnibus
  • ln -s $(pwd)/build-gitlab-docs/gitlab/doc build-gitlab-docs/gitlab-docs/content/ee
  • ln -s $(pwd)/build-gitlab-docs/gitlab-runner/docs build-gitlab-docs/gitlab-docs/content/runner
  • ln -s $(pwd)/build-gitlab-docs/charts/doc build-gitlab-docs/gitlab-docs/content/charts
  • cd build-gitlab-docs/gitlab-docs
  • ls -ltr content

ちゃんとフルパスでリンクされていれば OK です

yarn ビルド

まずは各種 static ファイルを生成します

  • yarn install --frozen-lockfile

bundle ビルド

次に生成した静的ファイルと Markdown を描画するための nanoc のインストールとビルドを行います

  • bundle update --bundler
  • bundle config path vendor
  • bundle install
  • bundle exec nanoc && bundle exec nanoc live

ローカルの bundler が 2.x 系になっていたので bundle update してバージョンを合わせています
bundler が 1.17.3 であれば不要です

ビルドが成功すれば localhost:3000 で確認できます

動作確認

localhost:3000 にアクセスして以下のようなドキュメントが確認できれば OK です

最後に

コントリビュータ向けの記事ですが gitlab のドキュメントをビルドする方法を紹介しました
ドキュメントを修正したい場合にはご活用ください

参考サイト

2020年8月6日木曜日

Gitlab で SMTP サーバに Gmail を設定する方法

概要

Gitlab で SMTP サーバに Gmail の SMTP サーバを使う方法を紹介します
アカウントが 2段階認証を有効にしている場合はアプリパスワードを使う点に注意しましょう

環境

  • GitLab Enterprise Edition 13.2.2-ee

gitlab.rb の編集

SMTP サーバの設定は gitlab 全体で一意です
gitlab.rb を編集して SMTP サーバの設定を入れましょう

gitlab_rails['smtp_enable'] = true
gitlab_rails['smtp_address'] = "smtp.gmail.com"
gitlab_rails['smtp_port'] = 587
gitlab_rails['smtp_user_name'] = "your-user-name@gmail.com"
gitlab_rails['smtp_password'] = "app password"
gitlab_rails['smtp_domain'] = "smtp.gmail.com"
gitlab_rails['smtp_authentication'] = "login"
gitlab_rails['smtp_enable_starttls_auto'] = true
gitlab_rails['smtp_tls'] = false
gitlab_rails['smtp_openssl_verify_mode'] = 'peer'

ポイントは smtp_password です
アプリパスワードを生成して設定しましょう

アプリパスワードについて

Google で二段階認証を有効にしている場合は Google アカウントのパスワードを gitlab.rb に直接設定することができません
その場合はアプリパスワードを使って専用のパスワードを生成してから gitlab.rb の smtp_password に設定しましょう

reconfigure

gitlab.rb 編集後は reconfigure が必要です

  • gitlab-ctl reconfigure

テスト送信

公式の手順でもそうなのですが gitlab-rails console コマンド使って送信するのが早いようです

  • gitlab-rails console

で Ruby の repl になったら以下のスクリプトを入力して実行しましょう

Notify.test_email('destination_email@address.com', 'Message Subject', 'Message Body').deliver_now

引数は左から「宛先アドレス」「メール本文」「メールタイトル」になります
From は smtp_user_name に指定したユーザになっていましたが Gmail を経由しているので X-Google-Original-From というヘッダが付与されておりここには gitlab@your.gilab.domain のような gitlab のドメイン情報が設定されていました
また Reply-To も gitlab のドメインになっていました

メールを受信してヘッダを確認してみると詳細がわかると思います

プロジェクトにユーザを Invite しても OK

プロジェクトから Members でユーザを Invite してもメールが送信できるのでそこで確認しても OK です

参考サイト

2020年8月5日水曜日

Gitlab のアラート管理機能を使ってみた

概要

Gitlab のアラート機能を使ってみました
簡単に言うと Gitlab 側でインシデントを管理できる機能です
Generics と Prometheus 連携できるアラートがあるのでどちらも試してみました

環境

  • GitLab Enterprise Edition 13.2.2-ee
  • Prometheus 2.20.0
    • AlertManager 0.21.0
    • node_exporter 1.0.1

アラートを有効にする

まずは Gitlab 側でアラート管理機能を有効にする必要があります
アラート管理機能は各プロジェクトごとに有効にする必要があります
左メニューの「Settings」->「Operations」と進みます

そして「Alerts」の項目を Expand すると「Active」というトグルスイッチがあるのでこれをオンにします
そして下にある「Save Changes」を選択します

すると Webhook URL と Authorization key が発行されるのでこれを使ってインシデントを情報を Gitlab に送信することができるようになります

テスト送信してみる

試してに仮想インシデント情報として適当なペイロードを送信してみます
「Alert test payload」に適当な JSON を入力し「Test alert payload」を選択します 

これで Webhook URL に対してペイロードが送信され Gitlab 上にインシデント情報が貯まっていきます

curl でテスト送信してみる

先程 UI で行ったことを curl でも行ってみます
リクエストは単純な Webhook リクエストなので POST で送信すれば OK です

curl -XPOST 'https://your.gitlab.url/root/test/alerts/notify.json' \
-H "Authorization: Bearer xxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d @- << EOF
{
  "key": "value from curl"
}
EOF

これで同様に curl からのインシデント情報も Gitlab に登録されました

プロジェクトに届いたアラートを確認する

プロジェクトに貯まったアラート情報を確認するには左メニューから「Operations」->「Alerts」と進みます
すると以下のようにテストで送信された 2 つのアラート情報が登録されているのが確認できると思います

UI と curl で送信したカスタムペイロードは各アラートをクリックすると詳細を確認できます

Prometheus からアラート情報を飛ばしてみる

先程は UI と curl からアラート情報を飛ばしましたが今度は Prometheus から飛ばしてみたいと思います
Prometheus には Webhook を飛ばせる機能がすでにあるのでそれを使って Gitlab 上にアラート情報を飛ばします

External Prometheus を有効にする

次にアラート管理機能の External Prometheus を有効にします
Active をオンにして Save Changes しましょう
API URL がブランクだと怒られるので Prometheus を構築した URL を入れましょう
ただローカル IP だと登録できないので適当な URL を指定したら OK だったので今回は適当な URL を指定しました
Authorization key が発行されない場合は「Reset key」を選択して再発行しましょう

Prometheus 側の設定と構築

Prometheus の構築はこちらを御覧ください
今回はローカルの Mac 上に Prometheus を構築しました
ポイントは alertmanager.yml でここに Gitlab で払い出された Webhook URL と Authorization key を指定します

prometheus.yml と alerts.rule は Prometheus サーバを起動するマシンに配置しましょう
alertmanager.yml は AlertManager を起動するマシンに配置しましょう

  • vim prometheus.yml
global:
  scrape_interval:     15s
  evaluation_interval: 15s

alerting:
  alertmanagers:
  - static_configs:
    - targets:
      - '192.168.100.1:9093'

rule_files:
  - 'alerts.rule'

scrape_configs:
  - job_name: 'worker'
    static_configs:
    - targets: ['192.168.100.10:9100']
  • vim alerts.rule
groups:
  - name: 'Check active memory bytes'
    rules:
    - alert: 'Worker memory usage alert'
      expr: node_memory_Active_bytes{instance="192.168.100.10:9100",job="worker"} > 800000000
      for: 5s
      annotations:
        summary: 'Worker memory usage is over 800MB'
  • vim alertmanager.yml
route:
  receiver: 'default_receiver'
receivers:
- name: 'default_receiver'
  webhook_configs:
    - http_config:
        bearer_token: xxxxxxxxxxxxxxxxxxxxxxxxxxx
      send_resolved: true
      url: https://your.gitlab.url/root/test/alerts/notify.json

監視対象の exporter の起動します (192.168.100.10)

  • docker run -d --net="host" --pid="host" quay.io/prometheus/node-exporter

アラートマネージャを起動します (192.168.100.10)

  • docker run -d -p 9093:9093 --name alertmanager -v $(pwd):/alertmanager prom/alertmanager --config.file=/alertmanager/alertmanager.yml

最後に Prometheus を起動します (192.168.100.1)

  • docker run -d -p 9090:9090 -v $(pwd):/prometheus-data prom/prometheus --config.file=/prometheus-data/prometheus.yml

動作確認

今回の条件はメモリの使用量を 800MB にすれば alertmanager が検知し Webhook を送信してくれる想定です

  • stress --vm 1 --vm-keep

--vm-keep を入れておかないとすぐにアラートが解消してしまい AlertManager が検知しないので --vm-keep を設定します
しばらくすると AlertManager 側にアラートが上がるのが確認できると思います (192.168.100.10:9093)

そして Gitlab 側のアラートの一覧を見るとちゃんと Webhook が送信されて一覧に登録されているのが確認できると思います

最後に

Gitlab のアラート管理機能を使ってみました
ユースケースとしてはプロジェクトのソースコードがデプロイされている環境で何かあったら Webhook を飛ばしてリソースのアラート管理やエラーのトレースなどを行う感じかなと思います
使いようによっては Google の StackDriver や iOS の Crashlytics のように使うこともできるかなと思います

参考サイト

2020年8月4日火曜日

今更ながら git rebase をちゃんと使えるようにする

概要

git rebase はマージとは違いヘッドの位置を移動することであたかも別ブランチの修正をマージしたかのように見せます
rebase を使ったほうがコミットログがキレイになる場合があります

しかしなぜか rebase 後は force push しなければならなかったり結局マージと同じことをしている場合がありうまく使えていないことがありました
なので今更ながらちゃんと使い方を把握しておこうと思います

準備

まずは動作確認するための環境を作成します
リモートリポジトリは Github を使います

master ブランチ

  • touch memo
  • vim memo
a
  • git add .
  • git commit -m "first commit"
  • git push -u origin master

b1 ブランチの作成

master ブランチから作成します

  • git branch b1
  • git checkout b1
  • vim memo
a
b
  • git add .
  • git commit -m "second commit from b1"
  • git push -u origin b2

b2 ブランチの作成

このブランチは b1 ブランチから更に生やします

  • git branch b2
  • git checkout b2
  • vim memo
a
b
c
  • git add .
  • git commit -m "third commit from b2"
  • git push -u origin b2

でここまでのブランチごとのコミットの状況は以下のようになります

なのこの図は Github の「Insights」->「Network」から確認できます

b1 ブランチで修正が入る

  • git checkout b1
  • vim memo
a
b
d
  • git add .
  • git commit -m "thrid commit from b1"

b2 ブランチを b1 ブランチに rebase してみる (force push)

やりたいことは先程の b1 ブランチの修正を b2 ブランチにマージではなく rebase することでコミットログをキレイにします

  • git checkout b2
  • git rebase b1

何も考えずに上記を行うと当然ですがコンフリクトして rebase できません
エラーの内容は以下の感じです

Auto-merging memo CONFLICT (content): Merge conflict in memo error: could not apply a811605… third commit from b2 Resolve all conflicts manually, mark them as resolved with "git add/rm <conflicted_files>", then run "git rebase –continue". You can instead skip this commit: run "git rebase –skip". To abort and get back to the state before "git rebase", run "git rebase –abort". Could not apply a811605… third commit from b2

ファイルにはコンフリクト部分が記載されています

a
b
<<<<<<< HEAD
d
=======
c
>>>>>>> a811605... third commit from b2

このコンフリクトを解消した上で rebase してみます

  • vim memo
a
b
c
d
  • git add .
  • git commit
  • git rebase --continue
  • git push -u origin b1

しかし以下のようにエラーが発生します

To https://github.com/hawksnowlog/test.git ! [rejected] b2 -> b2 (non-fast-forward) error: failed to push some refs to 'https://github.com/hawksnowlog/test.git' hint: Updates were rejected because the tip of your current branch is behind hint: its remote counterpart. Integrate the remote changes (e.g. hint: 'git pull …') before pushing again. hint: See the 'Note about fast-forwards' in 'git push –help' for details.

理由は単純でリモート側のコミットログとローカルで rebase したコミットログが異なるため push できず force push を余儀なくされている感じです
ではどうするかいうと結局 force push するしかなくなります

  • git push -f origin b2

一応これでコミットログ的には b1 の続きとして b2 が作成されていることになるのでキレイにはなります
しかし force push しなければならないので権限がない場合などこれだと対応できません

force push しないようにするには

ではコンフリクトありの rebase 時に force push しないようにするにはどうすれば良いか考えます
最初はコンフリクトしない rebase なら大丈夫かなと思ったんですが結局ローカル側のコミットID が変わってしまうようなので force push が必要になります
いろいろ考えたのですが rebase -> push を force push なしでやる場合にはリモート側にまだ push していないコミットがある場合にだけ使うしかないと思います
すでにリモート側に push しているコミットがあるブランチで rebase -> push する場合には force push は必須にするしかないと思います

Tips: やり直したい場合は

リモート側に hard reset しましょう

  • git reset --hard origin/b2

まとめ

merge と rebase の使い分けは以下の通りかなと思います

  • 基本は merge
  • リモート側にまだ push していないコミットがある場合に限り rebase
  • どうしても rebase したい場合は force push を使う

2020年8月3日月曜日

CentOS7 nodesource のリポジトリを使って nodejs をアップデートする方法

概要

nodesource は apt や yum を使って最新版の nodejs がインストールできるリポジトリです
古いリポジトリを使ってインストールした nodejs を最新版にする場合は最新版のリポジトリに更新してから再インストールする必要があります

環境

  • CentOS 7.8.2003
  • nodejs 8 -> 14

手順

  • curl -sL https://rpm.nodesource.com/setup_14.x | bash -
  • yum remove nodejs
  • yum celan all
  • yum -y install nodejs
  • yum -y install yarn

もしかすると remove は不要かもしれません

2020年8月2日日曜日

Flask-Migrate で alter table でカラムのサイズを変更する方法

概要

デフォルトではできないので設定を変更する必要があります

環境

  • macOS 10.15.5
  • MySQL 8.0.19
  • Python 3.8.3
  • Flask-SQLAlchemy 2.5.3
  • Flask-Migrate 2.5.3
  • Flask 1.1.2

方法

migrations/env.py 内の run_migrations_online メソッドを書き換えます
compare_type=True を設定しましょう

  • vim migrations/env.py
with connectable.connect() as connection:
    context.configure(
        connection=connection,
        target_metadata=target_metadata,
        process_revision_directives=process_revision_directives,
        **current_app.extensions['migrate'].configure_args,
        compare_type=True) # add

2020年8月1日土曜日

Flask-Marshmallow でカスタムフィールドを追加する方法

概要

Flask-Marshmallow を使って独自のカラム名を返却するようなスキーマを定義する方法を紹介します

環境

  • macOS 10.15.5
  • MySQL 8.0.19
  • Python 3.8.3
  • Flask-Migrate 2.5.3
  • Flask-SQLAlchemy 2.5.3
  • Flask-Marshmallow 0.13.0

スキーマ定義

ma.Integerma.String という感じで SQLAlchemy の fields と同じような定義ができます

class UserCustomSchema(ma.Schema):
    my_id = ma.Integer(attribute="id")
    my_name = ma.String(attribute="name")

attribute 引数を使うことで元々のカラム名を指定します

使い方

Flask と組み合わせて使う方法です
クエリを発行したあとで定義しておいたカスタムフィールドだけが返るスキーマで dump します

class UserCRUD():
  def custom_select(self):
      return UserCustomSchema(many=True).dump(User.query.all())

あとは Flask からアクセスするようにすれば OK です

from flask import Flask, jsonify
from flask_migrate import Migrate
from my_app.database import db, ma
from my_app.lib import UserCRUD

app = Flask(__name__)
app.debug = True
app.config['SQLALCHEMY_DATABASE_URI'] = "mysql+mysqldb://root:@localhost/test?charset=utf8"
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True
app.config['SQLALCHEMY_ECHO'] = True

db.init_app(app)
migrate = Migrate(app, db)
ma.init_app(app)

@app.route('/custom')
def custom_select():
    crud = UserCRUD()
    user = crud.custom_select()
    return jsonify(user)

これで確認すると [{"my_id":1,"my_name":"a"},{"my_id":2,"my_name":"b"},{"my_id":3,"my_name":"c"}] こんな感じでカスタムフィールドのみが記載されたレスポンスになってるのが確認できると思います

最後に

Flask-Marshmallow で既存のカラム名を使わずカスタムでレスポンスとして返却するカラム名を指定する方法を紹介しました
他にも指定可能な型やカスタムできる項目はあるのでスキーマの定義だけでレスポンスの形を制御できるのは便利な機能です

参考サイト