2021年2月28日日曜日

kubernetes に Ingress をデプロイして外部からアクセスできるようにしてみる

概要

k8s で Service に対して外部からアクセスできるようにする場合は基本的には Ingress が必要になります
過去に minikube を使った Ingress の設定方法は紹介しました
今回は kubeadm で構築した環境に Ingress をデプロイしてみたいと思います

環境

  • Ubuntu18.04
  • kubernetes v1.20.4

Nginx Ingress Controller のデプロイ

今回は Nginx Ingress Controller を使います

Nginx Ingress Controller の取得

  • git clone https://github.com/nginxinc/kubernetes-ingress/
  • cd kubernetes-ingress/deployments
  • git checkout v1.10.0

RBAC のデプロイ

  • kubectl apply -f common/ns-and-sa.yaml
  • kubectl apply -f rbac/rbac.yaml
  • kubectl apply -f rbac/ap-rbac.yaml
namespace/nginx-ingress created
serviceaccount/nginx-ingress created

clusterrole.rbac.authorization.k8s.io/nginx-ingress created
clusterrolebinding.rbac.authorization.k8s.io/nginx-ingress created

clusterrole.rbac.authorization.k8s.io/nginx-ingress-app-protect created
clusterrolebinding.rbac.authorization.k8s.io/nginx-ingress-app-protect created

nginx の共通設定のデプロイ

  • kubectl apply -f common/default-server-secret.yaml
  • kubectl apply -f common/nginx-config.yaml
  • kubectl apply -f common/ingress-class.yaml
secret/default-server-secret created

configmap/nginx-config created

ingressclass.networking.k8s.io/nginx created

カスタムリソースのデプロイ

  • kubectl apply -f common/crds/k8s.nginx.org_virtualservers.yaml
  • kubectl apply -f common/crds/k8s.nginx.org_virtualserverroutes.yaml
  • kubectl apply -f common/crds/k8s.nginx.org_transportservers.yaml
  • kubectl apply -f common/crds/k8s.nginx.org_policies.yaml
  • kubectl apply -f common/crds/k8s.nginx.org_globalconfigurations.yaml
  • kubectl apply -f common/global-configuration.yaml
customresourcedefinition.apiextensions.k8s.io/virtualservers.k8s.nginx.org created

customresourcedefinition.apiextensions.k8s.io/virtualserverroutes.k8s.nginx.org created

customresourcedefinition.apiextensions.k8s.io/transportservers.k8s.nginx.org created

customresourcedefinition.apiextensions.k8s.io/policies.k8s.nginx.org created

customresourcedefinition.apiextensions.k8s.io/globalconfigurations.k8s.nginx.org created

globalconfiguration.k8s.nginx.org/nginx-configuration created

Ingress Controller のデプロイ

  • kubectl apply -f deployment/nginx-ingress.yaml
  • kubectl apply -f daemon-set/nginx-ingress.yaml
deployment.apps/nginx-ingress created

daemonset.apps/nginx-ingress created

service のデプロイ

  • kubectl create -f service/nodeport.yaml
service/nginx-ingress created

これでホストのどこかのポートで待ち受けることできます
今回は 31451 と 30023 で LISTEN しています

  • kubectl get svc -n nginx-ingress
NAME            TYPE       CLUSTER-IP      EXTERNAL-IP   PORT(S)                      AGE
nginx-ingress   NodePort   10.107.240.71   <none>        80:31451/TCP,443:30023/TCP   4m38s

Ingress Controller が動作しているか確認

  • kubectl get pods --namespace=nginx-ingress
AME                            READY   STATUS    RESTARTS   AGE
nginx-ingress-f69f79478-5gl72   1/1     Running   0          55m
nginx-ingress-k29kh             1/1     Running   0          42m

今回は daemonSets もデプロイしているので 2 つあります

使ってみる

Pod と Service をデプロイします
そして Service に対して Ingress 経由でアクセスできるようにしてみます

  • vim apple.yml
kind: Pod
apiVersion: v1
metadata:
  name: apple-app
  labels:
    app: apple
spec:
  containers:
    - name: apple-app
      image: hashicorp/http-echo
      args:
        - "-text=apple"
---

kind: Service
apiVersion: v1
metadata:
  name: apple-service
spec:
  selector:
    app: apple
  ports:
    - port: 5678
  • vim ingress.yml
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: example-ingress
  annotations:
    kubernetes.io/ingress.class: "nginx"
spec:
  rules:
  - host: test.app.com
    http:
      paths:
        - path: /apple
          backend:
            serviceName: apple-service
            servicePort: 5678
  • kubectl apply -f apple.yml
  • kubectl create -f ingress.yml

最小構成の場合ワーカー側のノードの IP で LISTEN しています
なのでノードの IP にアクセスすればどの IP でもアクセスできます
アクセスする際は host ベースの振り分けをしていので Host を指定するようにしましょう

  • curl -H "Host: test.app.com" node1:31451/apple

もちろんノードの IP に DNS を設定して FQDN でアクセスすれば Host ヘッダなしでアクセスできるようになります

トラブルシューティング: うまく pods が起動しない

自分が遭遇したエラーは error retrieving k8s version: Get "https://10.96.0.1:443/version?timeout=32s": dial tcp 10.96.0.1:443: i/o timeout というエラーで nginx-ingress-controller の pods から ClusterIP を経由して k8s の API をコールするところでエラーになりました
原因は k8s を構築する際の flannel の設定で --pod-network-cidr=10.244.0.0/16 を忘れていたのと指定したネットワークがホストのネットワークと被っていたせいでうまく通信できなかったのが原因でした

最後に

Nginx Ingress Controller をデプロイして外部から k8s 環境にデプロイしたアプリにアクセスできるようにしてみました
結構複雑なので使いこなすとなるとかなり大変かなと思います
今回のように master/node の 2 台最小構成なのであれば NodePort を使って外部からアクセスするようになります

参考サイト

2021年2月27日土曜日

kubeadm で構築した k8s で coredns がうまく起動しない場合の対処方法

概要

k8s 構築時に coredns がうまく起動しないケースがありました
対処方法を紹介します

環境

  • Ubuntu18.04
  • kubernetes v1.20.4

ホストの DNS の設定を見直す

Ubuntu18.04 であれば /etc/resolv.conf に記載されているリゾルバ名がローカルなどになっているとうまく動作しないことがあるようです
8.8.8.8 などに変更してみましょう
変更方法はこちらで紹介しています

/etc/hosts にノードの情報を記載する

ホスト名を master や node1 にしている場合はそれらのホスト名で自信を名前解決できる必要があります
/etc/hosts などに記載して対応しましょう

  • vim /etc/hosts
127.0.0.1       localhost master

cgroupdriver に systemd を設定する

これはあまり関係ないかもしれないのですが docker を使っている場合に kubeadm で構築時に警告が出ます
systemd を使うように修正するには以下のように修正します

  • vim /etc/docker/daemon.json
{
  "exec-opts": ["native.cgroupdriver=systemd"]
}
  • systemctl daemon-reload
  • systemctl restart docker

動作確認

これで再度 kubeadm init/join してみましょう
うまく coredns が起動できていれば OK です

  • kubectl get all --all-namespaces
NAMESPACE     NAME                                 READY   STATUS    RESTARTS   AGE
kube-system   pod/coredns-74ff55c5b-ltv9j          1/1     Running   0          23m
kube-system   pod/coredns-74ff55c5b-nl5st          1/1     Running   0          23m
kube-system   pod/etcd-master                      1/1     Running   0          23m
kube-system   pod/kube-apiserver-master            1/1     Running   0          23m
kube-system   pod/kube-controller-manager-master   1/1     Running   0          23m
kube-system   pod/kube-flannel-ds-bn4s2            1/1     Running   0          22m
kube-system   pod/kube-flannel-ds-z7nvs            1/1     Running   0          22m
kube-system   pod/kube-proxy-h6sfj                 1/1     Running   0          23m
kube-system   pod/kube-proxy-tj8p6                 1/1     Running   0          23m
kube-system   pod/kube-scheduler-master            1/1     Running   0          23m
NAMESPACE     NAME                 TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)                  AGE
default       service/kubernetes   ClusterIP   10.96.0.1    <none>        443/TCP                  23m
kube-system   service/kube-dns     ClusterIP   10.96.0.10   <none>        53/UDP,53/TCP,9153/TCP   23m
NAMESPACE     NAME                             DESIRED   CURRENT   READY   UP-TO-DATE   AVAILABLE   NODE SELECTOR            AGE
kube-system   daemonset.apps/kube-flannel-ds   2         2         2       2            2           <none>                   22m
kube-system   daemonset.apps/kube-proxy        2         2         2       2            2           kubernetes.io/os=linux   23m
NAMESPACE     NAME                      READY   UP-TO-DATE   AVAILABLE   AGE
kube-system   deployment.apps/coredns   2/2     2            2           23m
NAMESPACE     NAME                                DESIRED   CURRENT   READY   AGE
kube-system   replicaset.apps/coredns-74ff55c5b   2         2         2       23m

2021年2月26日金曜日

Omnibus Gitlab のオブジェクトストレージの機能を使ってみた

概要

Omnibus Gitlab にはアップロードした画像や CI でビルドした成果物などをオブジェクトストレージにアップロードする機能があります
デフォルトではローカルストレージに保存するため容量が逼迫するおそれがありますがオブジェクトストレージ機能を使うことで回避することができます
今回はオブジェクトストレージ機能の使い方の紹介をしたいと思います
なお今回はニフクラのオブジェクトストレージを使っています
S3 や Google Cloud Storage でも代用可能です

環境

  • Gitlab-ee 13.9.1

バケットの作成

8 つ作成します
公式のドキュメントを見る限り現時点では 8 つの成果物でオブジェクトストレージ機能が使えるようです

バケットの名前はシステムで一意なのですでに使わている場合は別の名前をつけましょう

gitlab.rb の編集

オブジェクトストレージを使う設定を記載します
認証情報と先程作成したバケット情報を記載します

  • vim /etc/gitlab/gitlab.rb
external_url 'https://gitlab.example.com'

gitlab_rails['object_store']['enabled'] = true
gitlab_rails['object_store']['proxy_download'] = true
gitlab_rails['object_store']['connection'] = {
  "provider" => "AWS",
  "region" => "jp-west-1",
  "aws_access_key_id" => "xxxxxxxxxx",
  "aws_secret_access_key" => "xxxxxxxxxxxx",
  "endpoint" => "https://jp-west-1.storage.api.nifcloud.com",
  "aws_signature_version" => 2
}
gitlab_rails['object_store']['objects']['artifacts']['bucket'] = 'gitlab-artifacts'
gitlab_rails['object_store']['objects']['external_diffs']['bucket'] = 'gitlab-external-diffs'
gitlab_rails['object_store']['objects']['lfs']['bucket'] = 'gitlab-lfs-objects'
gitlab_rails['object_store']['objects']['uploads']['bucket'] = 'gitlab-uploads'
gitlab_rails['object_store']['objects']['packages']['bucket'] = 'gitlab-packages'
gitlab_rails['object_store']['objects']['dependency_proxy']['bucket'] = 'gitlab-dependency-proxy'
gitlab_rails['object_store']['objects']['terraform_state']['bucket'] = 'gitlab-terraform-state'
gitlab_rails['object_store']['objects']['pages']['bucket'] = 'gitlab-pages'

設定反映

  • gitlab-ctl reconfigure

動作確認

一番簡単な uploads のバケットを使ってみます
適当に issue を作成しコメントにファイルをアップロードしてみましょう

これでオブジェクトストレージ側を見てみるとファイルがアップロードされているのが確認できると思います

注意点

ファイルをアップロードしたコメントを削除してもオブジェクトストレージ側にあるファイルは削除されませんでした
おそらく別の箇所などからもリンクされている可能性があるので削除していないのかと思いますがオブジェクトストレージ側にはファイルが溜まり続ける可能性があるので従量課金の場合は注意しましょう

最後に

今回は uploads だけ試しましたが dependency_proxyterraform_state などもあるので興味があればどの機能に対応しているか調べてみると良いかなと思います

参考サイト

2021年2月25日木曜日

LB 配下で複数の gitlab を動作させる場合は /etc/gitlab も共有しなければならない

概要

過去に複数の gitlab を LB 配下にぶら下げる方法を紹介しました
もし ssh で git clone する場合は鍵情報を複数の gitlab で共有しないと Host key verification failed. というエラーが発生します

環境

  • Gitlab-ee 13.8.4

対処方法

いろいろありますがコンテナの場合は /etc/gitlab を nfs ボリュームで共有するのが良いと思います
docker-compose であれば以下のような定義を追加しましょう (以下抜粋)

まず /etc/gitlab を共有する nfs ボリュームを作成します

volumes:
  etc_gitlab:
    driver_opts:
      type: nfs
      o: "addr=,rw,nfsvers=4"
      device: ":/"
...

あとはこれを使って gitlab コンテナの /etc/gitlab 領域をマウントします

services:
  gitlab:
    image: gitlab/gitlab-ee:13.8.4-ee.0
    ports:
      - "22:22"
      - "80:80"
    volumes:
      - etc_gitlab:/etc/gitlab # <- here
      - logs:/var/log/gitlab
...

また nfs は no_root_squash で作成しましょう

2021年2月24日水曜日

flask-sqlalchemy でユニークキーを作成する方法

概要

dbUniqueConstraint を使います
__table_args__ にタプルで設定します

環境

  • flask-sqlalchemy 2.4.4

サンプルコード

from flask_sqlalchemy import SQLAlchemy

db = SQLAlchemy()

class Table1(db.Model):
    col1 = db.Column(db.Integer)
    col2 = db.Column(db.String(128))
    __table_args__ = (db.UniqueConstraint(
        "col1",
        "col2",
        name="unique_idx_col1_col2"),)

MySQL では NULL はユニーク扱いにならないので注意

2021年2月23日火曜日

gitlab-runner をコンテナで動作させる方法

概要

過去 にプロセスで動作させる方法を紹介しました
今回はコンテナで起動する方法を紹介します

環境

  • Gitlab-ee 13.8.4
  • gitlab-runner 13.8.0

gitlab-runner コンテナの起動

  • docker volume create gitlab-runner-config
  • docker run -d --name gitlab-runner --restart always -v /var/run/docker.sock:/var/run/docker.sock -v gitlab-runner-config:/etc/gitlab-runner gitlab/gitlab-runner:latest

設定ファイルは永続化するためにコンテナボリュームを作成します

register する

  • docker run --rm -it -v gitlab-runner-config:/etc/gitlab-runner gitlab/gitlab-runner:latest register

コンテナ版でも register は必要になります
register する際に先程作成したコンテナボリューム上に設定ファイルが配置されるようにします

untagged jobs も動作するようにする

テスト用の gitlab-ci.yml を作成した際によくタグなしで定義することがあります
デフォルトだとタグなしはジョブ実行の対象外なのでタグなしでもジョブが実行されるようにします
設定は登録した Runner の設定変更から可能です

動作確認

適当に .gitlab-ci.yml を作成して試しましょう
なお以下は shell executor を想定しています

  • vim .gitlab.ci.yml
stages:
    - test
test_job:
  stage: test
  script:
    - hostname

おまけ: docker executor として登録する場合には

register 時に「docker」を指定することはもちろんですが --privileged モードも必要になります
コンテナボリューム内に保存した設定ファイルを直接編集して再起動してあげましょう

  • docker cp gitlab-runner:/etc/gitlab-runner/config.toml .
  • vim config.toml
  • docker cp ./config.toml gitlab-runner:/etc/gitlab-runner/config.toml
  • docker restart gitlab-runner

参考サイト

2021年2月22日月曜日

application_role の Gitlab を docker-compose で立ち上げる

概要

過去に application_role を使って Gitlab を構築する方法を紹介しました
今回はコンテナで application_role な Gitlab を構築し各種コンポーネントと連携する docker-compose.yml を作成してみました

環境

  • Ubuntu 18.04
  • docker 20.10.3
  • Gitlab 13.6.7

docker-compose.yml

  • vim docker-compose.yml
version: "3.6"

services:
  gitlab:
    image: gitlab/gitlab-ee:13.6.7-ee.0
    ports:
      - "22:22"
      - "80:80"
    volumes:
      - config:/etc/gitlab
      - logs:/var/log/gitlab
      - dot_ssh:/var/opt/gitlab/.ssh
      - uploads:/var/opt/gitlab/gitlab-rails/uploads
      - shared:/var/opt/gitlab/gitlab-rails/shared
      - builds:/var/opt/gitlab/gitlab-ci/builds
      - git-data:/var/opt/gitlab/git-data
    environment:
      GITLAB_OMNIBUS_CONFIG: |
        roles ['application_role']

        external_url 'https://gitlab.example.com'
        nginx['enable'] = true
        nginx['listen_port'] = 80
        nginx['listen_https'] = false

        gitlab_rails['registry_enabled'] = true
        gitlab_rails['registry_host'] = "registry-gitlab.example.com"
        gitlab_rails['registry_api_url'] = "http://registry:5000"

        postgresql['enable'] = false
        gitlab_rails['db_adapter'] = 'postgresql'
        gitlab_rails['db_encoding'] = 'unicode'
        gitlab_rails['db_host'] = 'postgres'
        gitlab_rails['db_password'] = 'xxxxxxx'

        redis['enable'] = false
        gitlab_rails['redis_host'] = "redis"

        prometheus['enable'] = false

        gitlab_workhorse['prometheus_listen_addr'] = "0.0.0.0:9229"
        gitaly['prometheus_listen_addr'] = "0.0.0.0:9236"
        node_exporter['listen_address'] = '0.0.0.0:9100'
        gitlab_exporter['listen_address'] = '0.0.0.0'
        gitlab_exporter['listen_port'] = '9168'
        sidekiq['listen_address'] = '0.0.0.0'
        puma['listen'] = '0.0.0.0'
        puma['port'] = 8080
        gitlab_rails['monitoring_whitelist'] = ['127.0.0.0/8', '172.30.1.0/24']
        gitlab_rails['prometheus_address'] = 'prometheus:9090'
        nginx['status']['options'] = {
          "server_tokens" => "off",
          "access_log" => "off",
          "allow" => "172.30.1.0/24",
          "deny" => "all",
        }
    restart: always
    depends_on:
      - redis
  gitlab-runner:
    image: gitlab/gitlab-runner:v13.6.0
    restart: always
  registry:
    image: registry:2
    volumes:
      - registry:/var/lib/registry
    environment:
      REGISTRY_STORAGE_DELETE_ENABLED: 'true'
    restart: always
  registry_web:
    image: nginx:1.19.7
    ports:
      - "81:80"
    volumes:
      - ./registry_nginx/default.conf:/etc/nginx/conf.d/default.conf
    restart: always
    depends_on:
      - registry
    restart: always
  postgres:
    image: postgres:11.10
    volumes:
      - postgres:/var/lib/postgresql/data/pgdata
      - ./postgres/init.sql:/docker-entrypoint-initdb.d/init.sql
    environment:
      POSTGRES_PASSWORD: xxxxxxx
      PGDATA: /var/lib/postgresql/data/pgdata
  redis:
    image: redis:6
    volumes:
      - redis:/data
    depends_on:
      - postgres
    restart: always
    command: ["redis-server", "--appendonly", "yes"]
  prometheus:
    image: prom/prometheus:v2.25.0
    ports:
      - "9090:9090"
    volumes:
      - ./prometheus:/prometheus-data
    restart: always
    command: ["--config.file=/prometheus-data/prometheus.yml"]
 alertmanager:
    image: prom/alertmanager:v0.21.0
    volumes:
      - ./alertmanager:/alertmanager
    ports:
      - "9093:9093"
    restart: always
    command: ["--config.file=/alertmanager/alertmanager.yml"]
    user: "0:0"
  grafana:
    image: grafana/grafana:7.4.2
    ports:
      - "3000:3000"
    volumes:
      - grafana:/var/lib/grafana
      - ./grafana/grafana.ini:/etc/grafana/grafana.ini
    restart: always

volumes:
  config:
    driver: local
  logs:
    driver: local
  dot_ssh:
    driver: local
  uploads:
    driver: local
  shared:
    driver: local
  builds:
    driver: local
  git-data:
    driver: local
  registry:
    driver: local
  postgres:
    driver: local
  redis:
    driver: local
  grafana:
    driver: local

networks:
  default:
    attachable: true
    ipam:
     driver: default
     config:
       - subnet: 172.30.1.0/24
         gateway: 172.30.1.1

registry

  • mkdir registry
  • vim registry/default.conf
server {
  listen *:80;
  server_name registry-gitlab.example.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://registry:5000;
  }
}

PostgreSQL

  • mkdir postgres
  • vim postgres/init.sql
create user gitlab with password 'xxxxxxx';
create database gitlabhq_production owner gitlab;
alter role gitlab with superuser;

Grafana

  • mkdir grafana
  • vim grafana/grafana.ini
[server]
root_url = http://192.168.100.10:3000

[auth.gitlab]
enabled = true
allow_sign_up = true
client_id = xxxxx
client_secret = xxxxx
scopes = api
auth_url = https://gitlab.example.com/oauth/authorize
token_url = https://gitlab.example.com/oauth/token
api_url = https://gitlab.example.com/api/v4
allowed_groups =
[auth.basic]
enabled = false
disable_login_form = true

Prometheus

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

alerting:
  alertmanagers:
  - static_configs:
    - targets:
      - alertmanager:9093

rule_files:
  - '/prometheus-data/alerts.rule'

scrape_configs:
  - job_name: nginx
    static_configs:
      - targets:
        - gitlab:8060
  - job_name: gitlab-workhorse
    static_configs:
      - targets:
        - gitlab:9229
  - job_name: gitlab-rails
    metrics_path: "/-/metrics"
    static_configs:
      - targets:
        - gitlab:8080
  - job_name: gitlab-sidekiq
    static_configs:
      - targets:
        - gitlab:8082
  - job_name: gitlab_exporter_database
    metrics_path: "/database"
    static_configs:
      - targets:
        - gitlab:9168
  - job_name: gitlab_exporter_sidekiq
    metrics_path: "/sidekiq"
    static_configs:
      - targets:
        - gitlab:9168
  - job_name: gitlab_exporter_metrics
    metrics_path: "/metrics"
    static_configs:
      - targets:
        - gitlab:9168
  - job_name: gitaly
    static_configs:
      - targets:
        - gitlab:9236
  • vim prometheus/alerts.rule
groups:
  - name: 'gitlab-rails database connections'
    rules:
    - alert: 'Too many database connections'
      expr: gitlab_database_connection_pool_connections{job="gitlab-rails"} > 100
      for: 5s
      annotations:
        summary: 'instance: {{ $labels.instance }}, value: {{ $value }}'

Alertmanager

  • mkdir alertmanager
  • vim alertmanager/alertmanager.yml
route:
  receiver: 'slack'
receivers:
- name: 'slack'
  slack_configs:
    - api_url: 'https://hooks.slack.com/services/xxxxx/xxxxxx/xxxxxxxxxxxxxx'
      channel: '#general'
      text: "{{ .CommonAnnotations.summary }}"
      send_resolved: true

解説

今回の構成は application_role の上に LB やリバースプロキシがある前提になります
https を受けるのはそれらの LB やリバースプロキシになります
Gitlab 側は 80 番で受ける想定になっています

application_role な gitlab コンテナはスケールできるように 5 箇所 (/var/opt/gitlab/.ssh, /var/opt/gitlab/gitlab-rails/uploads, /var/opt/gitlab/gitlab-rails/shared, /var/opt/gitlab/gitlab-ci/builds, /var/opt/gitlab/git-data) をコンテナボリュームでマウントしています
これにより必要なデータをコンテナボリュームで共有しています
もし docker swarm などを使ってホストをまたぐ場合は nfs を使ってボリュームを作成してください
/etc/gitlab は永続化しておかないとコンテナが再起動した際にうまく動作しないので永続化しましょう
/var/log/gitlab は過去のログが見れるようにしています

Gitlab の設定は gitlab.rb を書かずに GITLAB_OMNIBUS_CONFIG にすべて設定しています
PostgreSQL や Redis の参照はこの中にすべて記載します

application_role 以外のコンポーネント (PostgreSQL, Redis, registry, Prometheus, Grafana, Alertmanager) はすべてコンテナで作成します
各コンポーネントの設定ファイルは volumes でマウントしてコンテナに渡す方式にしています
また永続化が必要な領域はすべてコンテナボリュームを作成してマウントしています

Gitlab コンテナを起動した際にデータベースのマイグレーションが走るのでデータベースが起動してから Gitlab コンテナが起動するように depends_on で制御しています

PostgreSQL のバージョンが少し古い 11.10 なのは Gitlab が pg_dump 可能なバージョンが最新版に対応していないためです

22 番ポートを EXPOSE していますがホスト側ですでに 22 番ポートを LISTEN している場合は EXPOSE するポートを変更するとホスト側の sshd の LISTEN ポートを 22 以外に変更してください
また registry 用で nginx を起動します
すでに Gitlab コンテナで 80 番ポートを使っているので 81 番ポートを EXPOSE するようにしています
このあたりは別ホストがある場合は Gitlab コンテナが動作していないホストで registry 用の nginx を起動して 80 番ポートで EXPOSE しても OK です
gitlab コンテナを冗長化することもできますが EXPOSE するポートが重複するのでその場合は Swarm などを組んで複数のホストを準備してください

Grafana の Gitlab OAuth 用の client_idclient_secret はどうしても Gitlab 構築後に Admin Area からアプリケーションを作成する必要があるのでアプリケーション作成後に設定しなおし grafana コンテナを再起動しましょう

ネットワークでは ipam でアドレス帯を決めています
nginx の設定で IP もしくは CIDR を必ず指定する必要があるためです

動作確認

  • docker-compose up -d

ですべてのコンテナが起動するか確認します
問題なく起動したら external_url にアクセスして Gitlab が動作しているか確認しましょう
502 などになる場合は puma が立ち上がっているのかやログを確認してエラーが出ていないか確認しましょう

  • docker-compose exec gitlab gitlab-ctl status
  • docker-compose logs -f gitlab

トラブルシューティング: クリーンアップ

もし初回起動でミスした場合は誤った設定がデータベースのボリュームコンテナに残る可能性があるのですべてのコンテナを削除後にボリュームコンテナも削除してから再度起動するようにしてください

  • docker-compose down
  • docker volume rm $(docker volume ls -q)
  • docker-compose up -d

トラブルシューティング: SKIP=registry

バックアップを取得する際は SKIP=registry を入れないとエラーになります

  • docker-compose exec gitlab gitlab-backup create SKIP=registry

2021年2月21日日曜日

docker login で Error saving credentials: error storing credentials でログインできない場合の対処方法

概要

docker login しようとすると credentials が保存できないと言われてエラーになります 
遭遇したエラーの全文は以下の通りです

Error saving credentials: error storing credentials - err: exit status 1, out: `Cannot autolaunch D-Bus without X11 $DISPLAY`

環境

  • Ubuntu 18.04
  • docker 19.03.6

対処方法

必要なパッケージが足りていないのでインストールしましょう

  • apt install -y gnupg2 pass

2021年2月20日土曜日

Omnibus Gitlab で Grafana を外部に移行して OAuth 連携する方法

概要

前回 Promethues を外部に移行してみました
今回は Grafana を移行してみたいと思います
また Grafana のログインは Gitlab の OAuth を使います

環境

  • GitLab Enterprise Edition 13.8.0-ee
  • Grafana 7.4.1

Gitlab で OAuth アプリケーションの作成

「Admin Area」->「Applications」->「New Application」から作成します

で作成します

RedirectURI の 192.168.100.11 が Grafana の IP になります
Grafana の構築方法は後述します
URI の /login/gitlab は必須の設定になります
固定の値なので別の URI にしないようにしましょう

作成された Application ID と Secret はあとで使うのでメモしておきます

Grafana の構築

今回はコンテナで起動します
まずは Grafana の設定ファイルを作成します
Gitlab の OAuth と連携する設定を記載します

  • vim grafana.ini
[server]                                                                                                                                       
root_url = http://192.168.100.11:3000
[auth.gitlab]
enabled = true
allow_sign_up = true
client_id = xxxxxxxxxxxxxxxx
client_secret = xxxxxxxxxxxxxxxx
scopes = api
auth_url = https://example.gitlab.com/oauth/authorize
token_url = https://example.gitlab.com/oauth/token
api_url = https://example.gitlab.com/api/v4
allowed_groups =

root_url は callback URL として Gitlab に送信するので Gitlab でアプリケーション作成の際に設定した callback URL と同じ IP を入力しましょう
client_idclient_secret は先程作成したアプリケーションの Application ID と Secret を入力しましょう
example.gitlab.com の部分は自信の Gitlab の URL に置き換えてください
allow_sign_up=true を設定することで Grafana 側にユーザ情報がない場合は自動で作成してくれます


設定ファイルが作成できたらコンテナを起動しましょう

  • docker run -d --user 0 -v $(pwd)/data:/var/lib/grafana -v $(pwd)/grafana.ini:/etc/grafana/grafana.ini -p 3000:3000 --name=grafana grafana/grafana

動作確認

http://192.168.100.11:3000/login にアクセスるとログイン画面が表示されます

これで「Sign in with GitLab」をクリックすると Gitlab の認証情報で Grafana にログインできるようになります

おまけ: ログインフォームを消す方法

  • vim grafana.ini
[auth.basic]
enabled = false
disable_login_form = true
  • docker restart grafana

参考サイト

2021年2月19日金曜日

Gitlab でデータベースを外部のサーバに設定する方法 (コンテナ編)

概要

前回はバイナリインストールした PostgreSQL に移行する方法を紹介しました
今回はコンテナとして立ち上げた PostgreSQL に移行する方法を紹介します

環境

  • GitLab Enterprise Edition 13.8.0-ee
  • PostgreSQL 13.2

コンテナで PostgreSQL 起動

  • mkdir /mnt/postgresql
  • docker network create db
  • docker run -d --name postgres --network db -p 5432:5432 -e POSTGRES_PASSWORD=xxxxxxxxxx -e PGDATA=/var/lib/postgresql/data/pgdata -v /mnt/postgresql:/var/lib/postgresql/data postgres

gitlab ユーザの作成と gitlabhq_production データベースの作成

  • docker run -it --rm --network db postgres psql -h postgres -U postgres
create user gitlab with password 'xxxxxxxxxx';
create database gitlabhq_production owner gitlab;
alter role gitlab with superuser;

gitlab.rb の編集

  • vim /etc/gitlab/gitlab.rb
postgresql['enable'] = false
gitlab_rails['db_adapter'] = 'postgresql'
gitlab_rails['db_encoding'] = 'unicode'
gitlab_rails['db_host'] = '192.168.100.10'
gitlab_rails['db_password'] = 'xxxxxxxxxx'

192.168.100.10 は PostgreSQL コンテナを起動したインスタンスの IP です

再起動

  • gitlab-ctl reconfigure

おまけ: SQL をコンテナ起動時に実行する方法

  • vim init.sql
create user gitlab with password 'xxxxxxxxxx';
create database gitlabhq_production owner gitlab;
alter role gitlab with superuser;
  • docker run -d --name postgres --network db -p 5432:5432 -e POSTGRES_PASSWORD=asdfghjkl -e PGDATA=/var/lib/postgresql/data/pgdata -v $(pwd)/init.sql:/docker-entrypoint-initdb.d/init.sql -v /mnt/postgresql:/var/lib/postgresql/data postgres

2021年2月18日木曜日

docker registry で 405 エラー

概要

docker registry でタグを削除しようとした際に 405 エラーになりました
対処方法を紹介します

環境

  • docker 20.10.2
  • docker registry 2.0

エラー全文

time="2021-02-16T01:03:47.920102603Z" level=error msg="response completed with error" err.code=unsupported err.message="The operation is unsupported." go.version=go1.11.2 http.request.host="192.168.100.10:5000" http.request.id=24492a42-9d30-4608-8783-a42bf0c722bb http.request.method=DELETE http.request.remoteaddr="192.168.100.10:59988" http.request.uri="/v2/root/test/manifests/sha256:e4b315ad03a1d1d9ff0c111e648a1a91066c09ead8352d3d6a48fa971a82922c" http.request.useragent="GitLab/13.8.0-ee" http.response.contenttype="application/json; charset=utf-8" http.response.duration=13.745856ms http.response.status=405 http.response.written=78 vars.name="root/test" vars.reference="sha256:e4b315ad03a1d1d9ff0c111e648a1a91066c09ead8352d3d6a48fa971a82922c" 

対処方法

起動する際に REGISTRY_STORAGE_DELETE_ENABLED=true にしましょう

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

2021年2月17日水曜日

エプソンPX-404A で successful-ok-ignored-or-substituted-attributes (Bigsur)

概要

エプソンの PX-404A が Bigsur にアップデートしたところ使えなくなりました
対処方法を紹介します

環境

  • macOS 11.2.1
  • プリンタ エプソン PX-404A

エラー詳細

プリンタを追加しようとすると以下のようなエラーになります

  • 選択した装置の追加中にエラーが起きました。successful-ok-ignored-or-substituted-attributes

ドライバのインストール

https://www.epson.jp/dl_soft/readme/31836.htm に Bigsur 用の最新ドライバがあるのでダウンロードしてインストールしましょう
HomeIJP2_1085J_41.dmg がダウンロードできました

2021年2月16日火曜日

Alertmanager で通知を一時的にオフにする方法

概要

メンテナンスなどのときに使えます
Alertmanager の Silence を使います

環境

  • Promethues 2.24.1
  • Alertmanager 0.21.0

Alertmanager の起動

192.168.100.10:9093 で起動します

  • mkdir -p alertmanager/data
  • cd alertmanager
  • vim alertmanager.yml
route:
  receiver: 'containers_notification'
receivers:
- name: 'slack'
  slack_configs:
    - api_url: 'https://hooks.slack.com/services/xxxxxx/xxxxxx/xxxxxxxxx'
      channel: '#general'
      text: "{{ .CommonAnnotations.summary }}"
      send_resolved: true
  • docker run -d -p 9093:9093 --name alertmanager -v $(pwd):/alertmanager prom/alertmanager --config.file=/alertmanager/alertmanager.yml

Promethues の起動

192.168.100.11:9090 で起動します

  • mkdir promethues
  • cd prometheus
  • vim promethues.yml
global:
  scrape_interval:     15s
  evaluation_interval: 15s

alerting:
  alertmanagers:
  - static_configs:
    - targets:
      - 192.168.100.10:9093

rule_files:
  - 'alerts.rule'

scrape_configs:
  - job_name: node
    static_configs:
      - targets:
        - 192.168.100.12:9100

192.168.100.12:9100 が監視するノードで node_exporter を起動しておきます

  • vim alerts.rule
groups:
  - name: 'Test monitoring'
    rules:
    - alert: 'cpu idel alert'
      expr: node_cpu_seconds_total{cpu="0", instance="192.168.100.12:9100", job="node", mode="idle"} > 1
      for: 5s
      annotations:
        summary: 'instance: {{ $labels.instance }}, value: {{ $value }}'
  • docker run -d -p 9090:9090 --name promethues -v $(pwd):/prometheus-data prom/prometheus --config.file=/prometheus-data/prometheus.yml

とりあえず通知が来ることを確認する

idel が 1 以上なので必ず通知が来ると思います
slack に通知が来ることを確認しましょう

一旦アラートを解消する

  • vim alerts.rule
groups:
  - name: 'Test monitoring'
    rules:
    - alert: 'cpu idel alert'
      expr: node_cpu_seconds_total{cpu="0", instance="192.168.100.12:9100", job="node", mode="idle"} < 1
      for: 5s
      annotations:
        summary: 'instance: {{ $labels.instance }}, value: {{ $value }}'
  • docker restart promethues

Slack にアラート解除の通知が来ることを確認します

Silences を登録する

UI から登録できます
開始時間から終了時間の間にアラートしないようにできます

http://192.168.100.10:9093/#/silences

右上に Silence を登録できるボタンがあるのでクリックして作成します
デフォルトだと現在時刻から 2 時間が設定されています
Matchers にマッチしたアラートを無視するようになるので無視したいアラートのマッチ情報を記載しましょう
あとは User と Comment を適当に記載すれば OK です

Matchers はアラートに表示される key/value の値を設定します

登録が完了すると以下のようになります

動作確認

Silence 登録後に再度 alerts.rules を変更してアラートを発泡してみます
すると今度は Slack に通知が来ないのが確認できると思います
Alertmanager の Silence の項目を見るとちゃんと無視しているアラートとしても確認できます

ちなみにアラートが解消されていない状態で Silence が Expire になるとアラートが飛んで来るのでちゃんと解消されていることを確認してから Expire にしましょう

おまけ: API で Silence を登録する

curl http://localhost:9093/api/v1/silences -d '{
  "matchers": [
    {
      "name": "alername",
      "value": "cpu idel alert",
      "isRegex": false
    }
  ],
  "startsAt": "2021-02-15T04:00:00.000000000Z",
  "endsAt": "2021-02-15T05:00:00.000000000Z",
  "createdBy": "api",
  "comment": "test silence",
  "status": {
    "state": "active"
  }
}'

参考サイト

2021年2月15日月曜日

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

概要

Omnibus Gitlab は内部で Prometheus のプロセスを管理しています
今回は Prometheus サーバを構築しその Prometheus サーバと Gitlab を連携する方法を紹介します

環境

  • Ubuntu 18.04
  • Omnibus Install Gitlab-ee 13.8.0
  • Prometheus 2.24.1

Gitlab 側の exporter の設定

まずは gitlab.rb を編集します
Omnibus Install した Gitlab の場合いろいろな場所に exporter が設置されています
それらすべての exporter を外部に設置する Prometheus からアクセスできるようにします
exporter によって設定する項目がかなりことなるので注意が必要です
192.168.100.10 は外部に設置する Prometheus の IP アドレスになります

  • vim /etc/gitlab/gitlab.rb
prometheus['enable'] = false

# for gitlab_workhorse_exporter (9229/metrics)
gitlab_workhorse['prometheus_listen_addr'] = "0.0.0.0:9229"

# for gitaly_exporter (9236/metrics)
gitaly['prometheus_listen_addr'] = "0.0.0.0:9236"

# for node_exporter (9100/metrics)
node_exporter['listen_address'] = '0.0.0.0:9100'

# for gitlab_exporter (9168/database, 9168/metrics, 9168/sidekiq)
gitlab_exporter['listen_address'] = '0.0.0.0'
gitlab_exporter['listen_port'] = '9168'

# for redis_exporter (9121/metrics)
# redis_exporter['listen_address'] = '0.0.0.0:9121'

# for postgres_exporter (9187/metrics)
postgres_exporter['listen_address'] = '0.0.0.0:9187'

# for gitlab-sidekiq, gitlab-rails exporter (8082/metrics, 8080/-/metrics)
gitlab_rails['monitoring_whitelist'] = ['127.0.0.0/8', '192.168.100.10']
gitlab_rails['prometheus_address'] = '192.168.100.10:9090'
puma['listen'] = '0.0.0.0'
puma['port'] = 8080
sidekiq['listen_address'] = '0.0.0.0'

# for nginx exporter (8060/metrics)
nginx['status']['options'] = {
  "server_tokens" => "off",
  "access_log" => "off",
  "allow" => "192.168.100.10",
  "deny" => "all",
}

設定変更したら reconfigure しましょう

  • gitlab-ctl reconfigure

ちなみに今回は redis-server はすでに外部に移行しており外部のサーバに exporter を設置していないためコメントアウトしています
もし外部に移行した他のコンポーネントがありかつそこに exporter が設置してある場合は後述する promethues.yml にそこのエンドポイントを記載しましょう

Prometheus サーバの構築

次に Prometheus サーバを構築します
ポイントは外部に公開した Omnibus Gitlab の exporter にちゃんとアクセスできることです
192.168.100.11 は各種 exporter が起動している Omnibus Gitlab の IP になります
もし redis のようにすでに別のインスタンスに移行しておりそこに exporter がある場合はそのエンドポイントを記載しましょう

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

alerting:
  alertmanagers:
  - static_configs:
    - targets:
      # - alertmanager:9093

rule_files:

scrape_configs:
  - job_name: nginx
    static_configs:
      - targets:
        - 192.168.100.11:8060
  - job_name: postgres
    static_configs:
      - targets:
        - 192.168.100.11:9187
  - job_name: node
    static_configs:
      - targets:
        - 192.168.100.11:9100
  - job_name: gitlab-workhorse
    static_configs:
      - targets:
        - 192.168.100.11:9229
  - job_name: gitlab-rails
    metrics_path: "/-/metrics"
    static_configs:
      - targets:
        - 192.168.100.11:8080
  - job_name: gitlab-sidekiq
    static_configs:
      - targets:
        - 192.168.100.11:8082
  - job_name: gitlab_exporter_database
    metrics_path: "/database"
    static_configs:
      - targets:
        - 192.168.100.11:9168
  - job_name: gitlab_exporter_sidekiq
    metrics_path: "/sidekiq"
    static_configs:
      - targets:
        - 192.168.100.11:9168
  - job_name: gitlab_exporter_metrics
    metrics_path: "/metrics"
    static_configs:
      - targets:
        - 192.168.100.11:9168
  - job_name: gitaly
    static_configs:
      - targets:
        - 192.168.100.11:9236

あとは Prometheus サーバを起動します
今回はコンテナで起動していますがプロセスで起動しても OK です

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

動作確認

Promethues の管理画面にアクセスして Status -> Targets を確認しましょう
すべてのステータスがグリーンになっていれば OK です

Grafana 連携

今回の場合 Promethues は外部に移行しましたが Grafana は相変わらず Omnibus Gitlab と同じ場所にあります
その場合 Grafana は Promethues が同じ場所にあると思って動作し続けるのでうまく動作しません
なので Grafana のデータソースなどを修正する必要があります
なおこの作業は Grafana の admin 権限が必要になるので Omnibus Gitlab の Grafana で admin ログインできるようにしておきましょう (参考)

まずデータソースを新規で作成します
URL には Promethues のエンドポイントを入力しましょう

次に既存のダッシュボードをコピーします
以下では Gitaly のダッシュボードをコピーしています
コピーする理由はデフォルトで用意されている Grafana のダッシュボードは readonly になっているためデータソースなどが変更できないためです
(デフォルトの設定はファイルに記載されているので直接そのファイルを編集してデータソースを変更しても OK かなと思います)

コピーしたダッシュボードのデータソースを変更します

あとはダッシュボード内にあるペインのデータソースも変更します
ダッシュボードのデータソースを変更してもペイン側は自動で書き換わらないので手動でそれぞれのペインのデータソースも変更します

こんな感じでデータが見えるようになれば OK です

BadGateway が出る場合はデータソースが正しく追加されていない可能性があります
Garafana から Promethues のエンドポイントにうまく疎通できているか確認してから再度データソースを追加してみてください

参考サイト