2021年1月29日金曜日

docker registry を LB 配下で動作させる場合は nginx を挟まなければならない

概要

例えば ELB や F5BIG-IP などのロードバランサを使って docker registry を動作させようとします
その場合にロードバランサ側で SSL アクセラレータを使って 443 -> 5000 などのバランシングをすると思います
その場合には間に nginx を挟んでヘッダの調整をしないとうまく動作しないことがあるようです

環境

  • Ubuntu 18.04
  • docker registry 2.7.2
  • nginx 1.14.0
  • docker 20.10.2

自分が遭遇した現象

docker push 時に止まり何度かリトライしたあとで 503 が返却されました
docker registry のログを見ても成功のログしか出ておらずおそらくロードバランサとの相性が悪いのだと思います

docker registry の起動

  • mkdir -p /mnt/registry
  • docker run -d -p 5000:5000 --restart=always --name registry -v /mnt/registry:/var/lib/registry registry:2

nginx の起動

  • apt -y install nginx
  • vim /etc/nginx/sites-enabled/default
server {
  listen *:80;
  server_name your.domain.com;
  server_tokens off;

  client_max_body_size 0;
  chunked_transfer_encoding on;

  location / {

    proxy_set_header Host $http_host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto https;
    proxy_set_header X-Forwarded-Ssl on;

    proxy_read_timeout 900;
    proxy_cache off;
    proxy_buffering off;
    proxy_request_buffering off;
    proxy_http_version 1.1;

    proxy_pass          http://localhost:5000;
  }
}

ロードバランサ側の設定

  • SSL アクセラレータを ON にする
  • 443 -> 80 にバランシングする
  • ロードバランサの IP などに対して A レコードなどを DNS に設定する

動作確認

docker login して問題なく push できるか確認してみましょう

最後に

proxy_set_header などの設定がないと registry 側が接続を中断しているのかなと思います

2021年1月28日木曜日

Ubuntu でクライアント証明書を発行する方法

概要

過去 に Mac で作成しました
今回は Ubuntu で作成してみたいと思います

環境

  • Ubuntu 18.04
  • openssl 1.1.1i

CA 用ディレクトリの作成

ここに鍵やクライアント証明書を作成していきます

  • sudo mkdir /CertificateAuthCA

ルートCAの作成

鍵を作成し鍵から証明書を作成します
CSR の情報は適当に入力します
今回の方法では鍵にパスフレーズの設定が必要になるので入力しましょう

  • sudo openssl genrsa -des3 -out ca.key 4096
  • sudo openssl req -new -x509 -days 3650 -key ca.key -out ca.crt

クライアント証明書の作成

こちらもまずは鍵を作成します
次に CSR を先に作成してその CSR と先ほど作成してルートCA の鍵と証明書を使ってクライアント証明書を作成します

  • sudo openssl genrsa -des3 -out client.key 2048
  • sudo openssl req -new -key client.key -out client.csr
  • sudo openssl x509 -req -days 365 -in client.csr -CA myca.crt -CAkey myca.key -set_serial 01 -out client.crt

nginx で動作確認するので pem ファイルも作成しておきます

  • sudo openssl x509 -in client.crt -outform PEM -out client.pem

Optional: サーバ証明書の作成

nginx で動作確認します
クライアント証明書は https が必須なので https で nginx が動作するためにサーバ証明書も作成しておきます

  • sudo apt -y install nginx
  • sudo mkdir /etc/nginx/certificates
  • sudo chown -R www-data:www-data /etc/nginx/certificates
  • cd /etc/nginx/certificates

サーバ証明書配置ディレクトリを作成してそこに作成します
鍵、CSR、証明書を作成します

  • sudo openssl genrsa -out domain.com.key 2048
  • sudo openssl req -new -sha256 -key domain.com.key -out domain.com.csr
  • sudo openssl x509 -in domain.com.csr -out domain.com.crt -req -signkey domain.com.key -days 3650

あとは nginx の設定をします

  • cd /etc/nginx/sites-available
  • sudo touch proxy.conf
  • sudo vim proxy.conf
ssl_client_certificate /CertificateAuthCA/ca.pem;
ssl_verify_client on;
  • sudo ln -s /etc/nginx/sites-available/proxy.conf /etc/nginx/sites-enabled/proxy.conf
  • sudo systemctl restart nginx
  • systemctl status nginx

動作確認

curl で動作確認します
必要になるのはクライアント証明書と鍵になります
また curl の場合は --key で指定する鍵情報に証明書の情報も含まれている必要があるので注意してください

  • cat client.crt client.key > client_curl_key.pem
  • curl -k --cert ./client.crt --key ./client_curl_key.pem https://localhost

ちなみに Ruby では以下のスクリプトで動作します

  • vim test.rb
require 'net/https'

https = Net::HTTP.new('localhost', 443)
https.use_ssl = true
https.cert = OpenSSL::X509::Certificate.new(File.read('./client.crt'))
https.key = OpenSSL::PKey::RSA.new(File.read('./client.key'), 'pass')
https.verify_mode = OpenSSL::SSL::VERIFY_NONE
https.verify_depth = 5
https.start {
  response = https.get('/')
  puts response.body
}
  • ruby test.rb

最後に

中間CA がある場合にはまた作業や挙動が異なると思います
証明書はクライアントごとに発行できるます
もし複数のクライアントで証明書を作成した場合は nginx 側では ca.pem の後ろに結合していけば OK です

参考サイト

2021年1月27日水曜日

Prometheus の blackbox_exporter を使ってみた

概要

blackbox_exporter はインスタンスの死活監視を行うためのエクスポータです
今回はインストール方法と簡単な使い方について紹介します

環境

  • Ubuntu 18.04
  • docker 20.10.2
  • Prometheus 2.24.1

nginx 起動

動作確認用の nginx を起動します
192.168.100.11 という IP のインスタンスで起動しています

  • sudo systemctl start nginx

blackbox.yml の作成

使用するモジュールやルールを設定します
実際に監視を行うサイトなどの情報は prometheus.yml 側に記載します
今回は Ping チェックとターゲットのサイトから 200 番系のレスポンスが返却されるかのチェックをします
ただこちらにはターゲットのサイトなどは記載しません

  • vim blackbox.yml
modules:
  icmp:
    prober: icmp
    timeout: 5s
  http_2xx:
    prober: http
    timeout: 5s
    http:
      valid_status_codes: []
      method: GET
      preferred_ip_protocol: "ip4"

blackbox_exporter の起動

先程作成した blackbox.yml を使ってコンテナを起動します
blackbox_exporter は 192.168.100.10 で起動しています

  • docker run --rm -d -p 9115:9115 --name blackbox_exporter -vpwd:/config prom/blackbox-exporter:master --config.file=/config/blackbox.yml

prometheus.yml の作成

次に prometheus.yml を定義します
blackbox_exporter の場所と実際に死活監視するターゲットサイトの情報はこちらに記載します

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

scrape_configs:
  - job_name: 'nginx_check'
    metrics_path: /probe
    params:
      module: [http_2xx]
    static_configs:
      - targets:
        - http://192.168.100.11 # nginx が起動しているインスタンス
    relabel_configs:
      - source_labels: [__address__]
        target_label: __param_target
      - source_labels: [__param_target]
        target_label: instance
      - target_label: __address__
        replacement: 192.168.100.10:9115 # blackbox_exporter が起動しているインスタンス

Prometheus の起動

prometheus.yml を使って起動します

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

動作確認

まず blackbox_exporter が起動しているノードにアクセスするとチェック結果が確認できます
今回であれば http://192.168.100.10:9115/ にアクセスすると以下のような結果が確認できます

また Prometheus にアクセスすると probe から始まるメトリックが取得できているのが確認できます

nginx を停止して probe_success の値が 0 になることを確認しましょう

おまけ: Ping チェックをする場合の prometheus.yml

job_name を追加すれば OK です
あとは Prometheus コンテナを再起動しましょう
メトリックは probe_icmp_duration_seconds あたりが追加になっています
probe_successjob="icmp_check" も追加になっているのでそれを見ても OK です

global:
  scrape_interval:     15s
  evaluation_interval: 15s

scrape_configs:
  - job_name: 'nginx_check'
    metrics_path: /probe
    params:
      module: [http_2xx]
    static_configs:
      - targets:
        - http://192.168.100.10
    relabel_configs:
      - source_labels: [__address__]
        target_label: __param_target
      - source_labels: [__param_target]
        target_label: instance
      - target_label: __address__
        replacement: 192.168.100.10:9115
  - job_name: 'icmp_check'
    metrics_path: /probe
    params:
      module: [icmp]
    static_configs:
      - targets:
        - 192.168.100.11
    relabel_configs:
      - source_labels: [__address__]
        target_label: __param_target
      - source_labels: [__param_target]
        target_label: instance
      - target_label: __address__
        replacement: 192.168.100.10:9115

最後に

blackbox_exporter を試してみました
ターゲットのサイトは prometheus.yml に書きますが実際にターゲットのサイトにアクセスしているのは blackbox_exporter のはずなのでネットワークのリーチャビリティなどには注意しましょう

参考サイト

2021年1月26日火曜日

docker-compose で nginx を使って proxy_pass をコンテナ名でアクセスする方法

概要

nginx の proxy_pass にコンテナ名を指定する方法と注意事項を紹介します
docker-compose で nginx と proxy_pass するアプリを定義します

環境

  • macOS 11.1
  • docker 20.10.2

ポイント

  • upstream は使わない
  • proxy_pass するアプリ側は 0.0.0.0 でバインドするようにする

Sinatra app

  • bundle init
  • vim Gemfile
gem "sinatra"
gem "thin"
  • bundle install
  • vim app.rb
require 'sinatra'

get '/' do
  'ok'
end

Dockerfile

  • vim Dockerfile
FROM ruby

ADD . /app
WORKDIR /app

RUN bundle install

CMD ["bundle", "exec", "ruby", "app.rb", "-o", "0.0.0.0"]

nginx default.conf

  • vim default.conf
server {
    listen 80;
    location / {
        proxy_pass http://app:4567/;
    }
}

Dockerfile-web

  • vim Dockerfile-web
FROM nginx

ADD ./default.conf /etc/nginx/conf.d/default.conf

docker-compose

  • vim docker-compose.yml
version: '3.8'
services:
  web:
    image: web
    ports:
      - 80:80
    depends_on:
      - app
  app:
    image: app:latest

ビルド

  • docker build -t app .
  • docker build -f Dockerfile-web -t web .

動作確認

  • docker-compose up -d
  • curl localhsot

2021年1月25日月曜日

APISpec の to_yaml で日本語が文字化けする場合の対処方法

概要

定義した spec 内で日本語を使っている場合は to_yaml を使わずに yaml.dump を使いましょう
to_yaml は内部的には yaml.dump を使っていますがオプションが使えないようになっています

環境

  • macOS 11.1
  • Python 3.8.7
    • apispec 4.0.0

文字化けするコード

with open(file_path, mode='w') as f:
    f.write(spec.to_yaml())

yaml.dump を使う

import yaml

with open(file_path, mode='w') as f:
    yaml.dump(spec.to_dict(), f, allow_unicode=True)


allow_unicode=True を忘れずに設定してください

おまけ: OrderedDict が入っている場合

to_dict した dict 内に OrderedDict が入っている場合以下の処理を追加しましょう

import yaml
from collections import OrderedDict

with open(file_path, mode='w') as f:
    represent_dict_order = lambda self, data:  self.represent_mapping("tag:yaml.org,2002:map", data.items())
    yaml.add_representer(OrderedDict, represent_dict_order)
    yaml.dump(spec.to_dict(), f, allow_unicode=True)

参考サイト

2021年1月24日日曜日

flask + apispec を使って OpenAPI3 の定義ファイルを自動生成する

概要

過去に flasgger を使って Swagger2.0 の定義ファイルを作成する方法を紹介しました
しかし flasgger では OpenAPI3 に完全に対応しておらず components.schame など自動で出力してくれません
今回は apispec を使って OpenAPI3 の定義ファイルを自動生成してみました

環境

  • macOS 11.1
  • Python 3.8.7
    • apispec 4.0.0
    • apispec-webframeworks 0.5.2
    • flask 1.1.2
    • marshmallow 3.10.0

インストール

  • pipenv install apispec apispec-webframeworks flask marshmallow

サンプルコード

  • vim app.py
import uuid

from apispec import APISpec
from apispec.ext.marshmallow import MarshmallowPlugin
from apispec_webframeworks.flask import FlaskPlugin
from flask import Flask
from marshmallow import Schema, fields


spec = APISpec(
    title="Swagger Petstore",
    version="1.0.0",
    openapi_version="3.0.2",
    plugins=[FlaskPlugin(), MarshmallowPlugin()],
)

class CategorySchema(Schema):
    id = fields.Int()
    name = fields.Str(required=True)


class PetSchema(Schema):
    categories = fields.List(fields.Nested(CategorySchema))
    name = fields.Str()


api_key_scheme = {"type": "apiKey", "in": "header", "name": "X-API-Key"}
spec.components.security_scheme("ApiKeyAuth", api_key_scheme)


app = Flask(__name__)


@app.route("/random")
def random_pet():
    """A cute furry animal endpoint.
    ---
    get:
      description: Get a random pet
      security:
        - ApiKeyAuth: []
      responses:
        200:
          description: Return a pet
          content:
            application/json:
              schema: PetSchema
    """
    pet_data = {
        "name": "sample_pet_" + str(uuid.uuid1()),
        "categories": [{"id": 1, "name": "sample_category"}],
    }
    return PetSchema().dump(pet_data)


with app.test_request_context():
    spec.path(view=random_pet)


if __name__ == "__main__":
    import json
    print(json.dumps(spec.to_dict(), indent=2))
    print(print(spec.to_yaml()))
    app.run(debug=True)
  • pipenv run python app.py

or

  • FLASK_APP=app.py pipenv run flask run

説明

基本は APISpec を作成してこれに必要な属性を追加していく感じです
今回は Flask + Marshmallow と連携するので plugins でそれぞれのプラグインを追加しています
components.schemas はデフォルトで表示してくれますが components.securitySchemes は表示してくれないので spec.components.security_scheme で追加しています

Flask のルーティングのコメント部分に各パスのパラメータやレスポンスの定義を直接記載します

MethodView を使う

MethodView を使うとルーティングをクラスとして管理することができます
こちらの方が管理しやすくなると思います

  • vim app.py
import uuid

from apispec import APISpec
from apispec.ext.marshmallow import MarshmallowPlugin
from apispec_webframeworks.flask import FlaskPlugin
from flask import Flask
from flask.views import MethodView
from marshmallow import Schema, fields


spec = APISpec(
    title="Swagger Petstore",
    version="1.0.0",
    openapi_version="3.0.2",
    plugins=[FlaskPlugin(), MarshmallowPlugin()],
)

class CategorySchema(Schema):
    id = fields.Int()
    name = fields.Str(required=True)


class PetSchema(Schema):
    categories = fields.List(fields.Nested(CategorySchema))
    name = fields.Str()


api_key_scheme = {"type": "apiKey", "in": "header", "name": "X-API-Key"}
spec.components.security_scheme("ApiKeyAuth", api_key_scheme)


app = Flask(__name__)


class RandomPet(MethodView):
    def get(self):
        """A cute furry animal endpoint.
        ---
        description: Get a random pet
        security:
        - ApiKeyAuth: []
        responses:
          200:
            description: Return a pet
            content:
              application/json:
                schema: PetSchema
        """
        pet_data = {
            "name": "sample_pet_" + str(uuid.uuid1()),
            "categories": [{"id": 1, "name": "sample_category"}],
            }
        return PetSchema().dump(pet_data)


random_pet_view = RandomPet.as_view("random")
app.add_url_rule(
    "/random",
    view_func=random_pet_view
)
with app.test_request_context():
    spec.path(view=random_pet_view)


if __name__ == "__main__":
    import json
    print(json.dumps(spec.to_dict(), indent=2))
    print(print(spec.to_yaml()))
    app.run(debug=True)
  • pipenv run python app.py

最後に

apispec と flask を組み合わせて OpenAPI3 のドキュメントを flask 内で定義し自動生成する方法を紹介しました
今回はすべて 1 つのファイルに記載しましたが MVC は分割して管理したほうが良いかなと思います
OpenAPI3 の定義ファイルも今回は標準出力に出しているだけなので、別のメインファイルを作成してそちらで spec の情報をファイルに出力するようにしても良いかなと思います

参考サイト

2021年1月23日土曜日

Gitlab で特定のブランチだけ CI を実行する方法

概要

only を使います
正規表現が使えるほか refs などと組み合わせるとブランチ名を直接指定することもできます

環境

  • Gitlab-ee 13.7.3

gitlab runner の準備

この記事を参考に構築してください

ベースの .gitlab-ci.yml

  • vim .gitlab-ci.yml
stages:
    - build
    - test

build_job:
  stage: build
  script:
    - echo "build"

test_job:
  stage: test
  script:
    - echo "test"

master ブランチだけ CI する

only.refs を使います

  • vim .gitlab-ci.yml
stages:
    - build
    - test

build_job:
  stage: build
  script:
    - echo "build"
  only:
    refs:
      - master

test_job:
  stage: test
  script:
    - echo "test"

feature/* ブランチだけ CI する

only と正規表現を組み合わせます

  • vim .gitlab-ci.yml
stages:
    - build
    - test

build_job:
  stage: build
  script:
    - echo "build"
  only:
    refs:
      - master

test_job:
  stage: test
  script:
    - echo "test"
  only:
    - /^feature\/.*$/

参考サイト

2021年1月22日金曜日

Gitlab で外部のコンテナレジストリと連携する方法

概要

Gitlab にはコンテナレジストリの機能があり簡単に有効にすることができます (参考)
今回は Gitlab 自体のコンテナレジストリの機能ではなく自分で構築したコンテナレジストリや PaaS のコンテナレジストリのような外部のコンテナレジストリと Gitlab を連携する方法を紹介します

環境

  • Gitlab-ee 13.7.3
  • registry 2.0

コンテナレジストリの構築

過去に docker/registry を使って構築したことがあるのでこれを参考に適当なインスタンスにコンテナレジストリを構築しました

gitlab.rb の編集

Gitlab 側から作成したコンテナレジストリを見るようにします


* vim /etc/gitlab/gitlab.rb

gitlab_rails['registry_enabled'] = true
gitlab_rails['registry_host'] = "192.168.100.11"
gitlab_rails['registry_port'] = "5000"
gitlab_rails['registry_api_url'] = "http://192.168.100.11:5000"


registry_hostregistry_port は Gitlab の UI 上で表示されるホストとポート名になります
実際に Gitlab が registry の API をコールするして情報を取得する設定は registry_api_url になるのでここに構築したレジストリのエンドポイントを入力しましょう
また当然ですが Gitlab -> registry API へのアクセスはできるようにしておきましょう

動作確認

reconfigure をかけて UI 上でちゃんとイメージの情報が見れるか確認します


* gitlab-ctl reconfigure

再起動できたら適当にイメージを push してみましょう
レジストリにアクセスできる環境からアクセスしましょう
タグ名はちゃんと Gitlab の命名規則に従いましょう


* docker pull alpine
* docker tag 192.168.100.11:5000/root/hoge2
* docker push 192.168.100.11:5000/root/hoge2

Gitlab の UI 上でイメージが確認できれば OK です

最後に

Gitlab で外部のコンテナレジストリと連携する方法を紹介しました
Gitlab のアプリと共存するとストレージなどを逼迫する恐れがあるのでコンテナレジストリは別インスタンスにしたいという場合には使える機能かなと思います

Gitlab のコンテナレジストリ機能だけを有効にしてそれと連携しても良いかなと思います
Gitlab のコンテナレジストリのクリーンアップポリシーなどを使うことができます

参考サイト

2021年1月21日木曜日

Omnibus Gitlab で外部の nginx と連携する方法

概要

Omnibus Gitlab にはデフォルトで nginx が付属しています
証明書や gitlab_workhorse へのプロキシをしてくれています
今回は付属の nginx を無効にして別の Ubuntu にインストールした nginx と Omnibus Gitlab を連携してみました

環境

  • Gitlab-ee 13.7.3
  • nginx 1.14.0 (on Ubuntu)

nginx の準備

今回は Ubuntu18 を準備してインストールしました
Apache Httpd などで代替することも可能です


* apt -y install nginx
* rm /etc/nginx/sites-enabled/default
* cd /etc/nginx/conf.d
* wget 'https://gitlab.com/gitlab-org/gitlab-recipes/-/raw/master/web-server/nginx/gitlab-omnibus-nginx.conf'
* vim /etc/nginx/conf.d/gitlab-omnibus-nginx.conf


upstream gitlab-workhorse {
  server 192.168.100.11:8081;
}


* systemctl daemon-reload
* systemctl restart nginx

デフォルトのコンフィグファイルを削除して Gitlab が配布している nginx の設定ファイルを配置します
一箇所だけ書き換える場所があり upstream の server 定義を後述する Omnibus Gitlab の gitlab_workhorse の LISTEN アドレスとポートに変更する必要があります
ここはデフォルトだとソケット通信になっているため tcp でのアクセスに変更する必要があります

gitlab.rb の編集

次に Omnibus Gitlab 側の設定変更をします
Omnibus Gitlab も Ubuntu にインストールしています

  • vim /etc/gitlab/gitlab.rb


external_url 'http://192.168.100.10'
nginx['enable'] = false

gitlab_workhorse['listen_network'] = "tcp"
gitlab_workhorse['listen_addr'] = "0.0.0.0:8081"
web_server['external_users'] = ['www-data']
gitlab_rails['trusted_proxies'] = ['192.168.100.10']


192.168.100.10 は nginx 側の IP になります
nginx は false にしましょう
そして gitlab_workhorselisten_addr0.0.0.0:8081 にして外部から接続できるようにします
web_server['external_users'] は nginx を apt でインストールしたい際のデフォルトユーザである www-data を指定します
gitlab_rails['trusted_proxies'] は信頼する外部からのプロキシの IP or CIDR を指定するので事前に作成した nginx の IP を記載しましょう
設定が完了したら reconfigure をかけます

  • gitlab-ctl reconfigure

動作確認

nginx 側の IP にブラウザからアクセスして Gitlab が動作することを確認しましょう
また Omnibus Gitlab の IP にアクセスしてアクセスできないことを確認しましょう

最後に

Omnibus Gitlab の nginx を外部に移行する方法を紹介しました
インスタンスを別にすることで管理もしやすくなると思います
また今回の設定は application_role な Gitlab をスケールするための最低限の設定になるので大規模な Gitlab を運用する場合には必須になるかなと思います

参考サイト

2021年1月20日水曜日

ActiveRecord 超入門

概要

ActiveRecord を使うときは大体 Rails や Sinatra などの Web フレームワークが絡んできます
データベースの操作をするようなバッチを作る場合は ActiveRecord 単体で使いたいことが出てくると思います
そんなときのために ActiveRecord のみで使う方法を紹介します

環境

  • macOS 11.1
  • Ruby 3.0.0p0
  • ActiveRecord 6.1.1

インストール

  • bundle init
  • vim Gemfile
gem "activerecord"
gem "mysql2"
  • bundle install

今回は MySQL に接続してみます

MySQL 側準備

以下の構成の test.users テーブルにアクセスします

  • mysql -u root test -e "show create table users\G"
Create Table: CREATE TABLE `users` (
  `id` int NOT NULL AUTO_INCREMENT,
  `first_name` varchar(128) COLLATE utf8mb4_general_ci DEFAULT NULL,
  `age` int DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci


データも適当に入れておきます


* mysql -u root test -e "select * from users\G"

*************************** 1. row ***************************
        id: 1
first_name: hawk
       age: 10
*************************** 2. row ***************************
        id: 2
first_name: snowlog
       age: 20
*************************** 3. row ***************************
        id: 3
first_name: hawksnowlog
       age: 30

モデルを作成する

establish_connection で接続先のデータベース情報を明記します
Rails などを使っている場合は config/database.yml に記載します

  • mkdir models
  • vim models/users.rb
require 'active_record'

class User < ActiveRecord::Base
  establish_connection(
    adapter:  "mysql2", 
    host:     "localhost",
    username: "root",
    password: "",
    database: "test",
  )
end

動作確認

作成したモデルを使用する動作確認用のスクリプトを作成します

  • vim app.rb
require './models/user.rb'

users = User.all
users.each do |user|
  puts user.id
  puts user.first_name
  puts user.age
end
  • bundle exec ruby app.rb

test.users テーブルの情報が表示されることを確認しましょう

Relation がある場合はどうなるか

外部キー制約が入ると一気に複雑になります
has_onebelongs_to など ActiveRecord::Base の基本的な使い方の知識が必要になります

テーブル修正

sports テーブルを作成し user はどれか一つのスポーツと紐付けます
sport.idusers.sport_id で外部キー制約を設けます


* create table sports (id int auto_increment, name varchar(255), primary key(id));
* alter table users add sport_id int;
* alter table users add foreign key (sport_id) references sports(id);
* insert into sports values (null, "baseball");
* insert into sports values (null, "soccer");
* insert into sports values (null, "basketball");
* update users set sport_id=1 where id=1;
* update users set sport_id=2 where id=2;
* update users set sport_id=3 where id=3;

  • select * from sports\G
*************************** 1. row ***************************
  id: 1
name: baseball
*************************** 2. row ***************************
  id: 2
name: soccer
*************************** 3. row ***************************
  id: 3
name: basketball
  • select * from users\G
*************************** 1. row ***************************
        id: 1
first_name: hawk
       age: 10
sport_id: 1
*************************** 2. row ***************************
        id: 2
first_name: snowlog
       age: 20
sport_id: 2
*************************** 3. row ***************************
        id: 3
first_name: hawksnowlog
       age: 30
sport_id: 3

スクリプト修正

新たに Sport クラスを追加します
establish_connect はもう少しうまく書けると思います
has_onebelongs_to でテーブル同士に関係性を持たせます

  • vim models/user.rb
require 'active_record'

class User < ActiveRecord::Base
  establish_connection(
    adapter:  "mysql2", 
    host:     "localhost",
    username: "root",
    password: "",
    database: "test",
  )
  belongs_to :sport
end

class Sport < ActiveRecord::Base
  establish_connection(
    adapter:  "mysql2", 
    host:     "localhost",
    username: "root",
    password: "",
    database: "test",
  )
  has_one :user
end
  • vim app.rb
require './models/user.rb'

users = User.joins(:sport).all
# users = User.includes(:sport).all
users.each do |user|
  puts user.id
  puts user.first_name
  puts user.age
  puts user.sport.name
end


外部キー先の情報も取得する場合は joins または includes を使います

  • bundle exec ruby app.rb

これで外部キーの参照先の情報も取得できるようになります

最後に

ActiveRecord を単体で使えるようになると理解も深まると思います
Rails などで使うときも幅が広がると思います

参考サイト

2021年1月19日火曜日

Pgbouncer 超入門

概要

最新版の PostgreSQL と Pgbouncer を組み合わせて使ってみました
インストール方法から紹介します

環境

  • Ubuntu 18.04
  • PostgreSQL 13.1
  • pgbouncer 1.15.0

Linuxbrew のインストール

最新版の PostgreSQL と Pgbouncer を使うので Linuxbrew 経由でそれぞれインストールします

  • /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
  • echo 'eval $(/home/linuxbrew/.linuxbrew/bin/brew shellenv)' >> /home/vagrant/.profile

PostgreSQL のインストール

  • brew install postgresql

Pgbouncer のインストール

  • brew install pgbouncer

pbbouncer 経由でアクセスさせるデータベースを定義する

pbbouncer 経由でアクセス可能なデータベースを定義します
デフォルトではどのデータベースにもアクセスできません
後述する test データベースにアクセスできるようにしましょう


* vim /home/linuxbrew/.linuxbrew/etc/pgbouncer.ini

[databases]
test = host=localhost port=5432 dbname=test

userlist.txt へユーザを追加

デフォルトだと pgbouncer へのアクセスは userlist.txt に記載されているユーザのみアクセスできます
今回は vagrant ユーザを使うので vagrant を追記します
パスワードはないので 2 つ目の文字列は空で設定します


* vim /home/linuxbrew/.linuxbrew/etc/userlist.txt

"vagrant" ""

PostgreSQL 起動

  • pg_ctl -D /home/linuxbrew/.linuxbrew/var/postgres start

Pgbouncer の起動

  • pgbouncer -d -q /home/linuxbrew/.linuxbrew/etc/pgbouncer.init

動作確認

  • psql postgres

postgres のプロンプトに変更します


* create database test;
* \connect test
* create table users (name varchar(40), age integer);
* \dt
* insert into users values ('hawk', 10);
* insert into users values ('snowlog', 20);

テーブルが作成できたらアクセスできるか確認します


* psql test -p 5432

これに Pgbouncer 経由でアクセスしてみます


* psql test -p 6432

test データベースのプロンプトに変更します
テーブルの一覧や select 文が発行できることを確認しましょう


* \dt
* select * from users

最後に

PostgreSQL に pgbouncer 経由でアクセスする方法を紹介しました
今回はホワイトリスト形式の ACL くらいしか設定しませんでしたが他にもプーリングの機能や PostgreSQL へのメトリックなども取得できます
大規模な接続になればなるほど必要になるツールになるかなと思います

参考サイト

2021年1月18日月曜日

Ubuntu 16.04LTS から 18.04LTS に do-release-upgrade

概要

Ubuntu を do-release-upgrade で 16.04 から 18.04 にアップグレードしてみました
特に詰まるところはなかったのですが備忘録として残しておきます 

環境

  • macOS 11.1
  • Virtualbox 6.1.16
  • vagrant 2.2.14

アップグレードコマンド

  • sudo apt update
  • sudo apt upgrade
  • sudo do-release-upgrade

いろいろ聞かれるが基本は Yes で OK です
最後に再起動を促されるので再起動後ログインしてバージョンを確認すれば OK です

  • cat /etc/issue

=> Ubuntu 18.04.5

2021年1月17日日曜日

Omnibus Gitlab でロールごとにインスタンスを作成して連携する方法

概要

前回 Gitlab の各種ロールについて調べました
今回はロールごとにインスタンスを作成して連携する方法を紹介します

環境

  • Ubuntu 18.04
  • Gitlab-ee 13.7.3

postgres_role と redis_master_role インスタンスの作成

まず PostgreSQL と Redis を管理するインスタンスを作成します

Gitlab の構築

こちらを参考にまずは Gitlab を各インスタンスにインストールしてください
なお postgres_role を作成する場合は EXTERNAL_URL を設定しないで作成してください

PostgreSQL に接続する gitlab ユーザのパスワード設定

  • gitlab-ctl pg-password-md5 gitlab

生成されたパスワードは後で使うのでメモしておきます

設定ファイル変更

PostgreSQL と Redis を起動するための設定をします
外部から接続する想定なので listen_address や認証情報を設定します
sql_user_password には先程作成したパスワードのハッシュ文字列を入力してください
また PostgreSQL12 から repmgr が使えないので false にしておきます

  • vim /etc/gitlab/gitlab.rb
roles ['postgres_role', 'redis_master_role']

repmgr['enable'] = false

postgresql['listen_address'] = '0.0.0.0'
postgresql['port'] = 5432
postgresql['sql_user'] = "gitlab"
postgresql['sql_user_password'] = "335158fc1aa26a831656b369e233217d"
postgresql['trust_auth_cidr_addresses'] = %w(0.0.0.0/0)

redis['port'] = 6379
redis['bind'] = '0.0.0.0'


* gitlab-ctl reconfigure
* gitlab-ctl status

5432 と 6379 が 0.0.0.0 で LISTEN していることを確認しましょう
以下のようにプロセスが起動していることを確認します


run: consul: (pid 24806) 116s; run: log: (pid 24842) 113s
run: logrotate: (pid 24707) 140s; run: log: (pid 24715) 139s
run: node-exporter: (pid 24822) 114s; run: log: (pid 24746) 133s
run: postgres-exporter: (pid 24835) 113s; run: log: (pid 24787) 119s
run: postgresql: (pid 23639) 454s; run: log: (pid 23638) 454s
run: redis: (pid 20593) 1216s; run: log: (pid 20604) 1213s
run: redis-exporter: (pid 24828) 113s; run: log: (pid 24764) 127s

application_role インスタンスの作成

次に Rails アプリが動作するインスタンスを作成します

Gitlab の構築

こちらを参考にまずは Gitlab を各インスタンスにインストールしてください

application_role を設定する

事前に作成した PostgreSQL と Redis に接続します
また起動するロールは application_role にします
IP の部分は先程作成した postgres_roleredis_master_role のインスタンスの IP を指定しましょう

application_role のみだと nginx が起動しないので nginx は別途有効にします

  • vim /etc/gitlab/gitlab.rb
roles ['application_role']

nginx['enable'] = true

gitlab_rails['db_username'] = "gitlab"
gitlab_rails['db_password'] = "335158fc1aa26a831656b369e233217d"
gitlab_rails['db_host'] = "192.168.100.11"
gitlab_rails['redis_host'] = "192.168.100.11"


再起動してプロセスの確認をしましょう
8080 が 0.0.0.0 で LISTEN していることも確認します


* gitlab-ctl reconfigure
* gitlab-ctl status

run: gitaly: (pid 103720) 29s; run: log: (pid 103719) 29s
run: gitlab-exporter: (pid 103881) 13s; run: log: (pid 103880) 13s
run: gitlab-workhorse: (pid 103857) 19s; run: log: (pid 103856) 19s
run: logrotate: (pid 98390) 2737s; run: log: (pid 22584) 67534s
run: node-exporter: (pid 23113) 67415s; run: log: (pid 22616) 67528s
run: puma: (pid 103888) 11s; run: log: (pid 103839) 23s
run: sidekiq: (pid 103898) 6s; run: log: (pid 103846) 21s

動作確認

application_role にアクセスして正常に Gitlab が動作するか確認しましょう
nginx を有効にしているので application_role インスタンスの IP にアクセスすれば Gitlab が動作しているのが確認できると思います

最後に

application_rolepostgres_role, redis_master_role にインスタンスを分けて Gitlab を起動する方法を紹介しました
ロールごとにインスタンスを分けることで冗長化できる他管理も楽になるかなと思います

今回の構成であれば application_role を増やして LB を追加すればアプリケーションレイヤーのスケールは簡単にできるようになると思います

参考サイト

2021年1月16日土曜日

Omnibus Gitlab の各種 role がどのプロセスを管理しているのか調べてみた

概要

Omunibus インストールした Gitlab では Gitlab にロールを設定することができます
ロールは簡単に言うと「アプリだけ」「データベースだけ」「キャッシュだけ」という感じでロール単体で動作させることができロールごとにインスタンスを準備することで HighAvailability を実現することができます

今回は導入という意味でまずロールの種類とロールに紐付いて動作するプロセスについて調べてみました

環境

  • Ubuntu 18.04
  • Gitlab-ee 13.7.3

準備: Gitlab の通常インストールと reconfigure

Omunibus インストールした Gitlab を各ロールのみにするにはまず普通に Gitlab をインストールし reconfigure する必要があります

まずは過去の記事を参考に Gitlab をインストールします


* apt -y update
* apt install -y curl openssh-server ca-certificates
* curl https://packages.gitlab.com/install/repositories/gitlab/gitlab-ee/script.deb.sh | sudo bash
* EXTERNAL_URL="http://192.168.100.10" apt -y install gitlab-ee

ここから各種ロールを試していきたいと思います
Gitlab をロール化するには /etc/gitlab/gitlab.rb の role セクションを変更していきます
変更後に reconfigure をかけて動作しているプロセスを調べてみました

redis_sentinel_role にする

  • vim /etc/gitlab/gitlab.rb
roles ['redis_sentinel_role']
redis['master_ip'] = '192.168.100.10'
redis['master_password'] = 'pass'
  • gitlab-ctl reconfigure
run: logrotate: (pid 88362) 3368s; run: log: (pid 22584) 57365s
run: node-exporter: (pid 23113) 57246s; run: log: (pid 22616) 57359s
run: sentinel: (pid 92832) 5s; run: log: (pid 91962) 393s


生成される sentinel のログと設定ファイルのパスは以下の通りです

  • ls /var/opt/gitlab/sentinel/sentinel.conf
  • ls /var/log/gitlab/sentinel/current

redis_master_role にする

  • vim /etc/gitlab/gitlab.rb
roles ['redis_master_role']
  • gitlab-ctl reconfigure
run: logrotate: (pid 92869) 48s; run: log: (pid 22584) 57645s
run: node-exporter: (pid 23113) 57526s; run: log: (pid 22616) 57639s
run: redis: (pid 93107) 30s; run: log: (pid 93106) 30s
run: redis-exporter: (pid 93123) 24s; run: log: (pid 93122) 24s


生成される sentinel のログと設定ファイルのパスは以下の通りです

  • ls /var/opt/gitlab/redis/redis.conf
  • ls /var/log/gitlab/redis/current

redis_replica_role にする

  • vim /etc/gitlab/gitlab.rb
roles ['redis_replica_role']
redis['master_ip'] = '192.168.100.10'
redis['master_password'] = 'pass'
  • gitlab-ctl reconfigure
run: logrotate: (pid 93574) 2104s; run: log: (pid 22584) 63301s
run: node-exporter: (pid 23113) 63182s; run: log: (pid 22616) 63295s
run: redis: (pid 93554) 3518s; run: log: (pid 93106) 5686s
run: redis-exporter: (pid 93123) 5680s; run: log: (pid 93122) 5680s


生成される sentinel のログと設定ファイルのパスは以下の通りです

  • ls /var/opt/gitlab/redis/redis.conf
  • ls /var/log/gitlab/redis/current

geo_primary_role にする

  • vim /etc/gitlab/gitlab.rb
roles ['geo_primary_role']
  • gitlab-ctl reconfigure
run: alertmanager: (pid 94196) 45s; run: log: (pid 94195) 45s
run: gitaly: (pid 93896) 77s; run: log: (pid 93895) 77s
run: gitlab-exporter: (pid 94162) 53s; run: log: (pid 94161) 53s
run: gitlab-workhorse: (pid 94124) 61s; run: log: (pid 94123) 61s
run: grafana: (pid 94219) 42s; run: log: (pid 94218) 42s
run: logrotate: (pid 93574) 2487s; run: log: (pid 22584) 63684s
run: nginx: (pid 94149) 55s; run: log: (pid 94148) 55s
run: node-exporter: (pid 23113) 63565s; run: log: (pid 22616) 63678s
run: postgres-exporter: (pid 94208) 44s; run: log: (pid 94207) 44s
run: postgresql: (pid 94013) 71s; run: log: (pid 94012) 71s
run: prometheus: (pid 94174) 51s; run: log: (pid 94173) 51s
run: puma: (pid 94105) 65s; run: log: (pid 94104) 65s
run: redis: (pid 93885) 80s; run: log: (pid 93106) 6069s
run: redis-exporter: (pid 93123) 6063s; run: log: (pid 93122) 6063s
run: sidekiq: (pid 94112) 63s; run: log: (pid 94111) 63s


デフォルトではすべてのプロセスが動作するようです

geo_secondary_role にする

  • vim /etc/gitlab/gitlab.rb
roles ['geo_secondary_role']
  • gitlab-ctl reconfigure
run: alertmanager: (pid 94196) 462s; run: log: (pid 94195) 462s
run: geo-logcursor: (pid 95654) 118s; run: log: (pid 95541) 142s
run: geo-postgresql: (pid 95424) 151s; run: log: (pid 95435) 150s
run: gitaly: (pid 93896) 494s; run: log: (pid 93895) 494s
run: gitlab-exporter: (pid 94162) 470s; run: log: (pid 94161) 470s
run: gitlab-workhorse: (pid 94124) 478s; run: log: (pid 94123) 478s
run: grafana: (pid 94219) 459s; run: log: (pid 94218) 459s
run: logrotate: (pid 93574) 2904s; run: log: (pid 22584) 64101s
run: nginx: (pid 94149) 472s; run: log: (pid 94148) 472s
run: node-exporter: (pid 23113) 63982s; run: log: (pid 22616) 64095s
run: postgres-exporter: (pid 94208) 461s; run: log: (pid 94207) 461s
run: postgresql: (pid 94013) 488s; run: log: (pid 94012) 488s
run: prometheus: (pid 94174) 468s; run: log: (pid 94173) 468s
run: puma: (pid 95694) 112s; run: log: (pid 94104) 482s
run: redis: (pid 93885) 497s; run: log: (pid 93106) 6486s
run: redis-exporter: (pid 93123) 6480s; run: log: (pid 93122) 6480s
run: sidekiq: (pid 95679) 114s; run: log: (pid 94111) 480s


geo_primary_role と比べて geo-logcursorgeo-postgresql が動作しているようです

postgres_role にする

  • vim /etc/gitlab/gitlab.rb
roles ['postgres_role']
  • gitlab-ctl reconfigure
run: consul: (pid 97826) 2682s; run: log: (pid 97840) 2681s
run: logrotate: (pid 98390) 2405s; run: log: (pid 22584) 67202s
run: node-exporter: (pid 23113) 67083s; run: log: (pid 22616) 67196s
run: postgres-exporter: (pid 94208) 3562s; run: log: (pid 94207) 3562s
run: postgresql: (pid 102922) 21s; run: log: (pid 102921) 21s
down: repmgrd: 0s, normally up, want up; run: log: (pid 103040) 15s


Gitlab 13 からは PostgreSQL 13 が使われており repmgrd が廃止になり Patroni を使うようになっているので有効にする必要があります

consul_role にする

  • vim /etc/gitlab/gitlab.rb
roles ['consul_role']
  • gitlab-ctl reconfigure
run: consul: (pid 97826) 2884s; run: log: (pid 97840) 2883s
run: logrotate: (pid 98390) 2607s; run: log: (pid 22584) 67404s
run: node-exporter: (pid 23113) 67285s; run: log: (pid 22616) 67398s

application_role にする

  • vim /etc/gitlab/gitlab.rb
roles ['application_role']
  • gitlab-ctl reconfigure
run: gitaly: (pid 103720) 29s; run: log: (pid 103719) 29s
run: gitlab-exporter: (pid 103881) 13s; run: log: (pid 103880) 13s
run: gitlab-workhorse: (pid 103857) 19s; run: log: (pid 103856) 19s
run: logrotate: (pid 98390) 2737s; run: log: (pid 22584) 67534s
run: node-exporter: (pid 23113) 67415s; run: log: (pid 22616) 67528s
run: puma: (pid 103888) 11s; run: log: (pid 103839) 23s
run: sidekiq: (pid 103898) 6s; run: log: (pid 103846) 21s

monitoring_role にする

  • vim /etc/gitlab/gitlab.rb
roles ['monitoring_role']
  • gitlab-ctl reconfigure
run: alertmanager: (pid 104334) 12s; run: log: (pid 104333) 12s
run: grafana: (pid 104346) 11s; run: log: (pid 104345) 11s
run: logrotate: (pid 98390) 2923s; run: log: (pid 22584) 67720s
run: node-exporter: (pid 23113) 67601s; run: log: (pid 22616) 67714s
run: prometheus: (pid 104322) 18s; run: log: (pid 104321) 18s

Tips: 存在しないロールを指定した場合

The following invalid roles have been set in 'roles': monitoring_role_dummy というエラーになります

Tips: ロールをしていしない場合のプロセス

un: alertmanager: (pid 104334) 339s; run: log: (pid 104333) 339s
run: gitaly: (pid 104690) 54s; run: log: (pid 104689) 54s
run: gitlab-exporter: (pid 104954) 30s; run: log: (pid 104953) 30s
run: gitlab-workhorse: (pid 104917) 38s; run: log: (pid 104916) 38s
run: grafana: (pid 104346) 338s; run: log: (pid 104345) 338s
run: logrotate: (pid 98390) 3250s; run: log: (pid 22584) 68047s
run: nginx: (pid 104941) 32s; run: log: (pid 104940) 32s
run: node-exporter: (pid 23113) 67928s; run: log: (pid 22616) 68041s
run: postgres-exporter: (pid 104984) 22s; run: log: (pid 104983) 22s
run: postgresql: (pid 104806) 48s; run: log: (pid 104805) 48s
run: prometheus: (pid 104322) 345s; run: log: (pid 104321) 345s
run: puma: (pid 104898) 42s; run: log: (pid 104897) 42s
run: redis: (pid 104679) 56s; run: log: (pid 104678) 56s
run: redis-exporter: (pid 104965) 28s; run: log: (pid 104964) 28s
run: sidekiq: (pid 104905) 40s; run: log: (pid 104904) 40s

まとめ

  • node-exporter, logrotate は必ず動作する模様
  • redis_master_roleredis_replica_role では動作するプロセスは同じ
  • postgres_role は PostgreSQL 12 から Patroni を使って冗長化するようになっている
  • 一番設定が面倒なロールは postgres_role かもしれない

参考サイト

2021年1月15日金曜日

自分のツイートを分析してみた2

概要

前回の続きで 20,000 ツイートに達したのでデータ分析してみました

環境

  • Ruby 3.0.0p0

全ツイート数

  • 21858

全ツイートをどれくらいの期間で行ったか

  • 2016/03/24 - 2021/01/12

1755 日分のツイートになります

月単位でどれくらいツイートを行ったか

月間最高ツイートは 2019/06 で 846 ツイートでした

日単位でどれくらいツイートを行ったか

日で最高ツイートは 2018/06/06 で 156 ツイートでした

月単位でどれくらいリツイートをされたか

最高でも月で 5 リツイートが一番多かったです

月単位でどれくらいファボをされたか

月間最高ファボは 2020/04 で 43 ファボでした

ツイートしているデバイスの割合

ほぼブラウザからツイートでした

よくツイートされている単語 Top100

  • t -> 1418
  • co -> 1394
  • https -> 1263
  • 感じ -> 865
  • ー -> 803
  • アプリ -> 793
  • 人 -> 616
  • 方法 -> 580
  • 自分 -> 563
  • ファイル -> 383
  • インストール -> 327
  • docker -> 322
  • あと -> 301
  • gt -> 292
  • 情報 -> 287
  • コンテナ -> 282
  • ゲーム -> 280
  • API -> 255
  • サーバ -> 249
  • アカウント -> 244
  • データ -> 243
  • コード -> 242
  • コマンド -> 224
  • 久しぶり -> 224
  • 記事 -> 223
  • 自動 -> 211
  • ruby -> 206
  • 気 -> 195
  • mac -> 187
  • s -> 181
  • 環境 -> 173
  • 別 -> 172
  • 動画 -> 170
  • 画面 -> 155
  • デフォルト -> 151
  • ポイント -> 150
  • ログイン -> 146
  • ドメイン -> 141
  • 複数 -> 139
  • ビルド -> 138
  • 名前 -> 133
  • ネットワーク -> 133
  • D -> 131
  • ユーザ -> 130
  • 個人 -> 129
  • k -> 128
  • ブログ -> 126
  • ツール -> 125
  • ページ -> 124
  • バージョン -> 123
  • アップデート -> 123
  • ケース -> 122
  • web -> 121
  • p -> 121
  • サイト -> 121
  • d -> 120
  • レベル -> 120
  • ホスト -> 119
  • google -> 116
  • ライブラリ -> 115
  • 他 -> 113
  • iPhone -> 108
  • モード -> 108
  • イベント -> 107
  • どん -> 106
  • 状態 -> 106
  • Mac -> 104
  • カード -> 104
  • 月 -> 104
  • デプロイ -> 103
  • wifi -> 102
  • クライアント -> 102
  • 文字 -> 101
  • UI -> 101
  • 変数 -> 98
  • 回線 -> 97
  • iOS -> 97
  • 次 -> 96
  • バグ -> 95
  • 仕組み -> 95
  • Ruby -> 93
  • 時代 -> 93
  • S -> 93
  • Google -> 93
  • 無料 -> 92
  • 最後 -> 92
  • タグ -> 92
  • or -> 90
  • ドキュメント -> 90
  • swift -> 89
  • コール -> 88
  • マイクラ -> 87
  • ローカル -> 87
  • クラス -> 87
  • 野球 -> 86
  • メソッド -> 86
  • golang -> 86
  • スタンプ -> 85
  • パスワード -> 84
  • 部分 -> 82

["ポイント", "d", "モード", "どん", "カード", "wifi", "回線", "バグ", "仕組み", "Ruby", "S", "最後", "コール", "野球", "パスワード"] が新規で登場していました

逆に ["Arduino", "xcode", "mini", "swagger", "画像", "端末", "プロジェクト", "Swift", "js", "使い方", "python", "ポート", "r", "デバイス", "ボタン"] が前回の Top100 からなくなっていました

最後に

30,000 ツイートしたら再度解析してみたいと思います
ちなみにフォロワー数はずっと横ばいです

参考サイト

2021年1月14日木曜日

docker で別のコンテナとリンクする場合は同じ network に参加させる必要がある

概要

--link オプションは使えないので同じネットワークに属するようにコンテナを作成しましょう

環境

  • macOS 11.1
  • docker 20.10.2

サンプル

test ネットワークの作成

  • docker network create test

test ネットワークに属する nginx コンテナの作成

  • docker run --network test --name web -d nginx

test ネットワークに属する動作確認用コンテナの作成

  • docker run --rm --network test ruby curl web

nginx の welcome ページが表示されることを確認しましょう

2021年1月13日水曜日

Zipkin のデータストレージに ElasticSearch を使う方法

概要

前回は In-Memory ストレージを使ったので再起動するとデータが消えてしました
今回はストレージに ElasticSearch を使ってデータの永続化を行ってみました

環境

  • Zipkin 2.23
  • ElasticSearch7.9.3

ネットワークの作成

  • docker create network test

このネットワークに Zipkin と ElasticSearch コンテナを属させます

ElasticSearch コンテナの起動

今回は zipkin が用意している ElasticSearch イメージを使って ElasticSearch コンテナを起動します


* docker run -d -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" --network test --name es docker.elastic.co/elasticsearch/elasticsearch:7.9.3
* curl 'localhost:9200/_cat/health'

green が返ってくることを確認しましょう

Zipkin コンテナの起動

STORAGE_TYPE=elasticsearch にして起動します


* docker run -d -p 9411:9411 -e STORAGE_TYPE=elasticsearch -e ES_HOSTS=es:9200 --network test --name zipkin openzipkin/zipkin

動作確認用のアプリは前回の Sinatra アプリを使用しています
これでアプリを使って Zipkin にトレース情報を送信してみると以下のようにちゃんと ElasticSearch 側にインデックスが作成されているのが確認できると思います
また何回かアクセスしてみると docs.count が増えるのも確認できると思います


health status index uuid pri rep docs.count docs.deleted store.size pri.store.size yellow open zipkin-span-2021-01-12 KqvMFHDPSamS4jRX52DQrQ 5 1 1 0 416b 416b


また http://localhost:9411/zipkin/ にアクセスして serviceName=zipkin-test で検索してもちゃんとトレース情報が確認できると思います


最後に

Zipkin + ElasticSearch を試してみました
STORAGE_TYPE を変更するだけで簡単に使うことができます
既存の ElasticSearch があるのであればそれをそのまま流用してもいいと思います

参考サイト

2021年1月12日火曜日

Sinatra で Zipkin 超入門

概要

zipkin はトレーシングシステムです
複数の Web アプリケーションを使っているようなシステムで 1 つのリクエストがどうのような経路を辿ったかを可視化したりすることができます

今回は Sinatra アプリケーションを使って Zipkin と連携してみました

環境

  • macOS 11.1
  • Ruby 3.0.0
    • zipkin-tracer 0.47.3
    • sinatra 2.1.0
  • Zipkin 2.23

Zipkin の起動

今回は docker を使います
jar でも動かせるようですが一番簡単な docker を使います

  • docker run -d -p 9411:9411 openzipkin/zipkin

http://localhost:9411/zipkin/ にアクセスすると zipkin の UI が起動しているのが確認できると思います

Sinatra アプリケーションの作成

zipkin-ruby という Rack のミドルウェアとして使えるトレーサライブラリがあるのでこれを使います
使い方は簡単で Sinatra アプリ内でミドルウェアを use するだけです

今回は zipkin に直接トレーサ情報を送信するので特に細かい設定はしていませんがエンドポイントが異なっていたり SQS や RabbitMQ に送信する場合は config を変更してください

  • bundle init
  • vim Gemfile
gem "zipkin-tracer"
gem "sinatra"
gem "thin"
  • bundle config path vendor
  • bundle install

アプリケーションを書いていきます
config.ru 内で rack のミドルウェアとして宣言します

  • vim config.ru
require './app'
require 'zipkin-tracer'
require 'rack'

config = {
  service_name: 'zipkin-test',
  json_api_host: 'http://localhost:9411',
  sample_rate: 1.0,
  sampled_as_boolean: false
}
use ZipkinTracer::RackHandler, config

run ZipkinTest


service_name は必須パラメータです
UI で検索する場合に必ず必要になります
json_api_host は先程 docker 上に構築した Zipkin のエンドポイントを指定します
sample_rate はトレースするリクエストの割合を指定します
0 から 1 の間で指定し、1.0 の場合はすべてのリクエストが Zipkin に送信されます
アクセスが多い場合などは割合を小さくすることで HTTP 通信によるオーバヘッドも小さくすることができます
sampled_as_boolean は false に設定しないと警告が出るので設定しておきます

  • vim app.rb
require 'sinatra'

class ZipkinTest < Sinatra::Base
  get '/' do
    'ok'
  end
end


Sinatra アプリ側は特に何もしません

  • bundle exec rackup config.ru

これで起動しましょう
localhost:9292 でアプリが起動しているのが確認できれば OK です

動作確認

  • curl localhost:9292

でアプリにアクセスしてみましょう
そしてその後で Zipkin の UI から serviceName=zipkin-test で検索すると以下のようにリクエストのトレース情報が格納されているのが確認できると思います
何かしらのクエリで検索しないと結果が表示されないので注意しましょう

最後に

Sinatra を使って Zipkin に入門してみました
主要な各言語にはライブラリが用意されているのでアプリケーションには簡単に導入することができるかなと思います

Zipkin 自体も docker で簡単に起動することができます
ただ今回は Zipkin のデータは永続化していないので永続化する場合はこの辺りを参考に格納ストレージを選択してください

あと気になったのは自分で管理できないような外部のリソースのトレースでそういった場合はどうするのか気になりました

参考サイト

2021年1月11日月曜日

docker swarm 上に cAdvisor をデプロイして swarm 上のコンテンのメトリックを取得してみる

概要

docker swarm 上で動作しているコンテナのメトリックを取得するのに cAdvisor を試してみました
検証ポイントとしては cAdvisor を各ホストで動作させる必要があるのか manager ノードだけで良いのかという点です

環境

  • macOS 11.1
  • docker 19.03.12

docker swarm の構築

こちらを参考に構築してください

cAdvisor のデプロイ: manager のみ

まずは cAdvisor を manager ノードのみにデプロイしてみます

  • docker run --volume=/:/rootfs:ro --volume=/var/run:/var/run:ro --volume=/sys:/sys:ro --volume=/var/lib/docker/:/var/lib/docker:ro --volume=/dev/disk/:/dev/disk:ro --publish=8080:8080 --detach=true --name=cadvisor google/cadvisor:latest

デプロイ完了後以下のエンドポイントでメトリックが取得できることを確認します
269 項目のメトリックが確認できます

  • curl http://localhost:8080/metrics

node1 側でコンテナを起動してみる

manager 側にデプロイした cAdvisor に変化があるか node1 側にコンテナを立てて確認してみます

  • docker run -d nginx

起動したら再度 manager 側にデプロイした cAdvisor のメトリックを確認してみます
メトリック数に変化がないことが確認できると思います

manager 側でコンテナを起動してみる

今度は manager ノード側で上記同様にコンテナを 1 つ起動してみます
すると取得できるメトリックが 336 に増えているのが確認できると思います

つまり cAdvisor は swarm クラスタであっても各ノードにデプロイしなければいけないことがわかります

最後に

swarm 上のコンテナのメトリックを取得したい場合は各ノードで cAdvisor を動作させる必要があることがわかりました
ノードが増えた場合に各ノードにある cAdvisor の値を横断的に見る方法として Prometheus を使う感じなのかなと思います

参考サイト

2021年1月10日日曜日

docker-machine で構築したホストのデーモンオプションを変更する方法

概要

docker-machine で構築した docker ホストは TinyCoreLinux になっています
dockerd も自動的に起動するのですがデーモンのオプションを設定する方法がわからなかったので紹介します

環境

  • macOS 11.1
  • docker-machine 0.16.2
  • Virtualbox 6.1.16r140961

docker-machine で docker ホストの作成

過去に紹介しているのでこちらの記事を参考に作成してみてください

docker ホストにログイン

  • docker-machine ssh master

daemon.json の作成

ここからは TinyCoreLinux 上での作業になります

  • sudo touch /etc/docker/daemon.json

metrics-addr の追加

  • sudo vi /etc/docker/daemon.json
{
  "metrics-addr" : "0.0.0.0:9323",
  "experimental" : true
}

dockerd の再起動

  • sudo /etc/init.d/docker restart

動作確認

  • curl localhost:9323/metrics

Prometheus のメトリック情報が取得できれば OK です

参考サイト

2021年1月9日土曜日

fluentd -> kafka -> logstash -> elasticsearch の順番でログを格納する方法

概要

fluentd -> kafka -> logstash -> elasticsearch という順番でログを確認する方法を紹介します
fluentd, kafka, elasticsearch の構築方法は過去に紹介しているので別記事を参照しています

環境

  • kafka 2.6.0
  • logstash 7.10.1
  • elasticsearch 6.4.0

fluent-kafka-plugin が動作する環境の構築

こちらを参考に構築してください
kafka と fluent-kafka-plugin がインストールされた fluent コンテナが起動している状態になれば OK です

elasticsearch の構築

こちらを参考に構築してください
9200 ポートで elasticsearch にアクセスできれば OK です

logstash のインストール

  • brew install logstash

logstash kafka input plugin のインストールと設定

ここが今回の肝になる部分です
logstash-integration-kafka を使います

  • logstash-plugin install logstash-integration-kafka

インストールが完了したら設定ファイルを作成していきます
kafka からデータを受け取って elasticsearch に流す設定を定義します

  • cp /usr/local/etc/logstash/logstash-sample.conf /usr/local/etc/logstash/logstash.conf
  • vim /usr/local/etc/logstash/logstash.conf
input {
  kafka {
    bootstrap_servers => "192.168.1.2:9092"
    topics => ["test"]
    decorate_events => true
  }
}

filter {
  json {
    source => "message"
  }
}

output {
  elasticsearch {
    hosts => ["http://192.168.1.2:9200"]
    index => "%{[@metadata][kafka][topic]}-%{+YYYY.MM.dd}"
  }
}


decorate_events を true にしないと [@metadata][kafka][topic] が output セクションで使えないので true にしています

また filter で json を使っています
どうやらデフォルトでは kafka から受け取ったログはすべて「message」というフィールドに格納されています
なのでそれぞれのフィールドに分割するために filter を挟んでいます

  • logstash -f /usr/local/etc/logstash/logstash.conf

起動に少し時間がかかりますが ERROR がでなければ OK です

動作確認

まずは適当なログを fluentd コンテナに送ります

  • docker run --rm --log-driver=fluentd --log-opt fluentd-address=192.168.1.2:24224 --log-opt tag="docker.{{.Name}}" alpine /bin/sh -c "while :;do echo \"{\\\"timestamp\\\":\\\"$(date)\\\",\\\"msg\\\":\\\"hello\\\"}\"; sleep 3; done;"


ある程度待った後に elasticsearch にインデックスが作成されているか確認しましょう

  • curl 'http://192.168.1.2:9200/_cat/indices?v'
  • curl 'http://192.168.1.2:9200/test-2021.01.06?pretty'

最後に

必ずしも kafka は必要ではないですがスケールによっては必要になります

参考サイト

2021年1月8日金曜日

ruby-kafka を使って kafka との証明書認証をサクっと確認する方法

概要

fluent-kafka-plugin で証明書を使って kafka に接続する場合にうまく接続できない場合があると思います
そんな場合は ruby-kafka を使って確認しましょう

概要

  • macOS 11.1
  • Ruby 3.0.0
  • ruby-kafka 1.3.0

準備

  • bundle init
  • vim Gemfile
gem "ruby-kafka"
  • bundle install

テストコード

  • vim test.rb
require 'kafka'

kafka = Kafka.new(
  ["kafka:9092"],
  ssl_ca_cert: File.read('./ca.pem'),
  ssl_client_cert: File.read('./cert.pem'),
  ssl_client_cert_key: File.read('./key.pem')
)
puts kafka

ret = kafka.deliver_message("Hello, World!", topic: "test")
puts ret


  • bundle exec ruby test.rb

これでエラーが出なければ OK です
証明書が間違っている場合などは OpenSSL のエラーなどが出ると思います

参考サイト