GIG

赴くままに技術を。

プライベートコンテナレジストリ(Harbor)を構築

Azure CLIベースでHarborの構築メモ。 流れとしては以下となります。

  1. コンテナレジストをデプロイするVNetを作成 2.コンテナレジストリを構築 3.踏み台サーバにGUI環境を構築しHarborにアクセス 4.DockerイメージをHarborにプッシュ
1. コンテナレジストをデプロイするVNetを作成

リソースグループを作成

az group create --name labprivcr --location japaneast

次に仮想ネットワークとサブネットを作成

$ az network vnet create --resource-group labprivcr --name vnetforprivcr --address-prefixes 10.0.0.0/16 --subnet-name subnet1 --subnet-prefixes 10.0.0.0/24
2.コンテナレジストリを構築

要件を見ると、4 CPU / メモリ 8GB / ディスク 160 GB とあるので、B4ms サイズ (4 vCPU / 16 Gib メモリ) を選択 Harbor docs | Harbor Installation Prerequisites

Harbor 用に仮想マシンを作成 ローカルでのみ利用する想定なので、パブリックIPアドレスは付与していません。

$ az vm create --resource-group labprivcr --name privcr --image UbuntuLTS --vnet-name vnetforprivcr --subnet subnet1 --admin-username azureuser --ssh-key-values ~/.ssh/id_rsa.pub --public-ip-address "" --size Standard_B4ms

プライベートDNSゾーンを設定し、仮想ネットワークにリンク。 az network private-dns link vnet create の際に、--registration-enabled false としているのは、後々 VNetピアリングしたとき、そこにデプロイしたAKSノードも登録されてしまうため、必要なものだけ登録するよう、falseを設定しています。

$ az network private-dns zone create --resource-group labprivcr --name labprivcr.com
$ az network private-dns link vnet create --resource-group labprivcr --name labprivcrlink --zone-name labprivcr.com --virtual-network vnetforprivcr --registration-enabled false
$ az network private-dns record-set a add-record --resoruce-group labprivcr --zone-name labprivcr.com --record-set-name privcr --ipv4-address 10.0.0.4

踏み台サーバをデプロイ こちらはパブリックIPアドレスを付与します。

$ az vm create --resource-group labprivcr --name jumpbox --image UbuntuLTS --vnet-name vnetforprivcr --subnet subnet1 --admin-username azureuser --ssh-key-values ~/.ssh/id_rsa.pub

踏み台サーバがデプロイできたら、踏み台サーバ→プライベートコンテナレジストリVMSSH接続します。

$ scp -p ~/.ssh/id_rsa azureuser@xxx.xxx.xxx.xxx:/home/azureuser/.ssh/
$ ssh azureuser@xxx.xxx.xxx.xxx
$ ssh azureuser@privcr

Dockerをインストール

参考; Install Docker Engine on Ubuntu | Docker Documentation

$ sudo apt-get update
$ sudo apt-get install apt-transport-https ca-certificates curl gnupg-agent software-properties-common
$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
$ sudo apt-key fingerprint 0EBFCD88
$ sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
$ sudo apt-get update
$ sudo apt-get install docker-ce docker-ce-cli containerd.io
$ sudo docker run hello-world
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
0e03bdcc26d7: Pull complete 
Digest: sha256:1a523af650137b8accdaed439c17d684df61ee4d74feac151b5b337bd29e7eec
Status: Downloaded newer image for hello-world:latest

Hello from Docker!
This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:
 1. The Docker client contacted the Docker daemon.
 2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
    (amd64)
 3. The Docker daemon created a new container from that image which runs the
    executable that produces the output you are currently reading.
 4. The Docker daemon streamed that output to the Docker client, which sent it
    to your terminal.

To try something more ambitious, you can run an Ubuntu container with:
 $ docker run -it ubuntu bash

Share images, automate workflows, and more with a free Docker ID:
 https://hub.docker.com/

For more examples and ideas, visit:
 https://docs.docker.com/get-started/

一般ユーザでもdockerコマンドが実行できるよう、dockerグループに追加

$ sudo usermod -aG docker `whoami`
azureuser@privcr:~$ exit
azureuser@privcr:~$ docker ps -a
CONTAINER ID   IMAGE         COMMAND    CREATED          STATUS                      PORTS     NAMES
218ed82c7541   hello-world   "/hello"   55 seconds ago   Exited (0) 51 seconds ago             brave_agnesi

Docker-composeをインストール

参考; Install Docker Compose | Docker Documentation

$ sudo curl -L "https://github.com/docker/compose/releases/download/1.27.4/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
$ sudo chmod +x /usr/local/bin/docker-compose
$ docker-compose --version
docker-compose version 1.27.4, build 40524192

HarborをHTTPSアクセス可能なように自己証明書を作成

これもほとんど公式ドキュメント通り

参考; Harbor docs | Configure HTTPS Access to Harbor

CA証明書の秘密鍵 ca.key を作成

$ mkdir cert && cd cert
$ openssl genrsa -out ca.key 4096

CA証明書 ca.crt を作成

$ openssl req -x509 -new -nodes -sha512 -days 3650 -subj "/C=JP/ST=Tokyo/L=Tokyo/O=lab/OU=lab/CN=privcr.labprivcr.com" -key ca.key -out ca.crt

このとき、Can't load /home/azureuser/.rnd into RNG とエラーになったので、以下の情報に沿って、/etc/ssl/openssl.cnfRANDFILE 行をコメントアウト

参考; Can't load ./.rnd into RNG · Issue #7754 · openssl/openssl · GitHub

サーバ証明書秘密鍵 privcr.labprivcr.com.key を作成

$ openssl genrsa -out privcr.labprivcr.com.key 4096

証明書署名要求 privcr.labprivcr.com.csr を作成

$ openssl req -sha512 -new -subj "/C=JP/ST=Tokyo/L=Tokyo/O=lab/OU=lab/CN=privcr.labprivcr.com" -key privcr.labprivcr.com.key -out privcr.labprivcr.com.csr

x509 v3 拡張属性向けの設定ファイルを生成

$ cat > v3.ext <<-EOF
authorityKeyIdentifier=keyid,issuer
basicConstraints=CA:FALSE
keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
extendedKeyUsage = serverAuth
subjectAltName = @alt_names

[alt_names]
DNS.1=privcr.labprivcr.com
DNS.2=privcr
IP.1=10.0.0.4
EOF

サーバ証明書 privcr.labprivcr.com.crt を作成

$ openssl x509 -req -sha512 -days 3650 -extfile v3.ext -CA ca.crt -CAkey ca.key -CAcreateserial -in privcr.labprivcr.com.csr -out privcr.labprivcr.com.crt

サーバ証明書秘密鍵を配置

$ sudo mkdir -p /data/cert
$ sudo cp privcr.labprivcr.com.crt /data/cert/
$ sudo cp privcr.labprivcr.com.key /data/cert/

Docker向けに privcr.labprivcr.com.crt を privcr.labprivcr.com.cert に変換し、配置

$ openssl x509 -inform PEM -in privcr.labprivcr.com.crt -out privcr.labprivcr.com.cert
$ sudo mkdir -p /etc/docker/certs.d/privcr.labprivcr.com
$ sudo cp privcr.labprivcr.com.cert /etc/docker/certs.d/privcr.labprivcr.com/
$ sudo cp privcr.labprivcr.com.key /etc/docker/certs.d/privcr.labprivcr.com/
$ sudo cp ca.crt /etc/docker/certs.d/privcr.labprivcr.com/

更新を反映させるため、dockerを再起動

$ sudo systemctl restart docker

Harborをインストール

$ wget https://github.com/goharbor/harbor/releases/download/v2.1.2/harbor-online-installer-v2.1.2.tgz
$ tar xvf harbor-online-installer-v2.1.2.tgz 
$ cd harbor
$ cp harbor.yml.tmpl harbor.yml
$ vi harbor.yml
diff harbor.yml harbor.yml.tmpl
5c5
< hostname: privcr.labprivcr.com
---
> hostname: reg.mydomain.com
17,18c17,18
<   certificate: /etc/docker/certs.d/privcr.labprivcr.com/privcr.labprivcr.com.cert
<   private_key: /etc/docker/certs.d/privcr.labprivcr.com/privcr.labprivcr.com.key
---
>   certificate: /your/certificate/path
>   private_key: /your/private/key/path

インストール

$ sudo ./install.sh
...
Creating harbor-log ... done
Creating registryctl   ... done
Creating harbor-portal ... done
Creating registry      ... done
Creating redis         ... done
Creating harbor-db     ... done
Creating harbor-core   ... done
Creating harbor-jobservice ... done
Creating nginx             ... done
✔ ----Harbor has been installed and started successfully.----

ログイン可能か確認。デフォルトのパスワードは、harbor.yml に記載のある通り、 Harbor12345

$ docker login privcr.labprivcr.com
Username: admin
Password:
WARNING! Your password will be stored unencrypted in /home/yuri/.docker/config.json.
Configure a credential helper to remove this warning. See
https://docs.docker.com/engine/reference/commandline/login/#credentials-store

Login Succeeded
3.踏み台サーバにGUI環境を構築しHarborにアクセス

Web UIがあるので、踏み台サーバにGUI環境を構築し確認

UbuntuはAzureのドキュメント通り

参考; Azure の Linux VM にリモート デスクトップを使用する - Azure Virtual Machines | Microsoft Docs

$ sudo apt-get update
$ sudo apt-get install xfce4

$ sudo apt-get install xrdp
$ sudo systemctl enable xrdp

$ echo xfce4-session >~/.xsession
$ sudo systemctl restart xrdp

ログインユーザのパスワードを設定

sudo passwd azureuser
Enter new UNIX password:
Retype new UNIX password:
passwd: password updated successfully

Webブラウザが入っていないので、Firefoxをインストール

$ sudo apt-get install firefox

3389/TCPを踏み台サーバのNSGなどで許可 その後、RDP接続にてGUI環境が利用できます。

先に作成したCA証明書を踏み台サーバにコピーし、Firefoxにインポートします。

  • Settings > Privacy & Security > View Certificates...
  • Import...

あとはFirefoxからhttps://privcr.labprivcr.comにアクセス

f:id:hermesian:20210120220408p:plain
harbor1

4.DockerイメージをHarborにプッシュ

次にHarborにDockerイメージをプッシュしてみます。

踏み台サーバに、プライベートコンテナレジストリと同様の手順でDockerをインストール

CA証明書をインストール

$ scp yuri@privcr:/home/yuri/cert/ca.crt .
$ sudo cp ca.crt /usr/local/share/ca-certificates/
$ sudo update-ca-certificates

Harborにログインし、Dockerイメージをプッシュ libraryというプロジェクトはプリセットのものらしいです。

$ docker login privcr.labprivcr.com
Username: admin
Password:
WARNING! Your password will be stored unencrypted in /home/azureuser/.docker/config.json.
Configure a credential helper to remove this warning. See
https://docs.docker.com/engine/reference/commandline/login/#credentials-store

Login Succeeded
$ docker tag hello-world:latest privcr.labprivcr.com/library/hello-world:v1
$ docker push privcr.labprivcr.com/library/hello-world:v1
The push refers to repository [privcr.labprivcr.com/library/hello-world]
9c27e219663c: Pushed
v1: digest: sha256:90659bf80b44ce6be8234e6ff90a1ac34acbeb826903b02cfa0da11c82cbc042 size: 525

Web UIからも確認できました。

f:id:hermesian:20210120220751p:plain
harbor2

AKSでネットワークパケットキャプチャを取得する

どこそこへつながらないや、つながるけどたまに切れるといった場面でネットワークパケットキャプチャを取得することがあるかと思います。 AKSの場合にどうやって取得するのかまとめてみました。

AKSノードにて取得

対象AKSノードにSSH接続し取得する方法で、一番簡単で取得可能な方法になります。 反面、解析する際には !(ip.addr == 168.63.129.16) 等、フィルタリングして目的の通信を見る必要があります。

参考; IP アドレス 168.63.129.16 とは | Microsoft Docs

  1. AKSノードにSSH接続する

AKSノードに到達可能な仮想ネットワークに踏み台サーバを用意するか、以下のヘルパーPod経由で対象AKSノードにSSH接続

Azure Kubernetes Service (AKS) クラスター ノードへの SSH 接続 - Azure Kubernetes Service | Microsoft Docs

2.ネットワークパケットキャプチャを取得する

sudo tcpdump -i any -s 0 -vvv -w `hostname`.pcap
...
対象のアクセスや操作を実行後、[Ctrl]+[C] により完了

3.対象ノードからネットワークパケットキャプチャを引き上げる

AKSノードからヘルパーPod/踏み台サーバへ scp コマンドによりコピーし、次いで、ローカル環境にコピー ヘルパーPodからローカル環境にコピーするには以下のように実施

kubectl cp $(kubectl get pod -l run=aks-ssh -o jsonpath='{.items[0].metadata.name}'):/(パケットキャプチャ結果).pcap (パケットキャプチャ結果).pcap

Podから取得

フィルタリングが楽であるためにPodからネットワークパケットキャプチャを取得できることが望ましいですが、 distrolessイメージの利用など、なるべくtcpdumpコマンドなど省いて作られたイメージを使うことが推奨事項になっています。

本命は、Ephemeral Container (kubectl debug コマンド) により対象のPodを変更させず、ネットワーク名前空間を共有する一時的なPodから取得する方法です。

Debug Running Pods | Kubernetes

上記はAlphaステージの機能のため、AKSなどのマネージドKubernetesでは対応していない状況です (GKEのアルファクラスターなら可能?)。 それに代わるものとして、ksniffというツールが現時点ではよさそうな印象があります。

GitHub - eldadru/ksniff: Kubectl plugin to ease sniffing on kubernetes pods using tcpdump and wireshark

# 1.5.0 では問題があり (https://github.com/eldadru/ksniff/issues/82)、1.4.2を利用します
$ wget https://github.com/eldadru/ksniff/releases/download/v1.4.2/ksniff.zip
$ unzip ksniff.zip
$ sudo make install
$ kubectl sniff -p (Pod名) -n (名前空間名) -o (出力先ファイル名).pcap
...
対象のアクセスや操作を実行後、[Ctrl]+[C] により完了
````

AKSノードにnode-shellでログインする

AKSにてノード (仮想マシンスケールセットインスタンス) に接続するとき、Podをデプロイしてそれを経由し、ノードにアクセスが必要。 しかし、ちょっと面倒なので、kubectl-node-shellを使ってみる。

github.com

krewと呼ばれるkubectlのプラグイン管理ツールによりインストールできる。

❯ kubectl krew index add kvaps https://github.com/kvaps/krew-index
❯ kubectl krew install kvaps/node-shell

使ってみると、実行中のコンテナも見れたりする。

❯ kubectl get nodes       
NAME                                STATUS   ROLES   AGE   VERSION
aks-nodepool1-31996622-vmss000000   Ready    agent   9h    v1.18.10
aks-nodepool1-31996622-vmss000001   Ready    agent   9h    v1.18.10
akswinp000000                       Ready    agent   9h    v1.18.10

❯ kubectl node-shell aks-nodepool1-31996622-vmss000000
spawning "nsenter-bsxuel" on "aks-nodepool1-31996622-vmss000000"
If you don't see a command prompt, try pressing enter.

root@aks-nodepool1-31996622-vmss000000:/# docker ps -a
CONTAINER ID        IMAGE                                          COMMAND                  CREATED             STATUS              PORTS               NAMES
879f57bf3305        alpine                                         "nsenter --target 1 …"   16 seconds ago      Up 16 seconds                           k8s_nsenter_nsenter-bsxuel_default_74057696-8b44-49a1-bc5f-31e2666efc12_0
...(省略)...

動作としては、alpineイメージのコンテナから、nsenterコマンドにてpidが1を指定してノードに入っている。 そのため、Windows ノードでは残念ながら動作しなかった。

❯ kubectl get pods -o wide   
NAME             READY   STATUS              RESTARTS   AGE    IP            NODE            NOMINATED NODE   READINESS GATES
nsenter-21fo6d   0/1     ContainerCreating   0          138m   10.240.0.66   akswinp000000   <none>           <none>

Windows コンテナ

Windows コンテナとは

Windows Server 2016よりサポートされたWindowsコンテナ、Linuxコンテナはまずまず不自由なく使うぐらいは触っているけど、 いまいち利用用途など見いだせず、敬遠し続けていた。

ということで、入門してみます。

2つの分離モード

Linuxと異なる点として、「プロセス分離」、「Hyper-V分離」の2つがある点が挙げられます。 前者は、Linuxコンテナと同様に、ホストのカーネルは共有する形態で、後者はホスト上に軽量の仮想マシンを挟み、その上でコンテナを動かす形態。

後者のメリットは、動作するWindowsコンテナのバージョンがホストOSのバージョンに制約されないことと、 Linuxコンテナ(いわゆる Linux Container on Windows, LCOW)もこれにより実現。

デフォルトは、プロセス分離

PS> docker info
 ...
 Default Isolation: process
 ...

環境構築

IIS をデプロイして確認してみる

使う Dockerfileはこちら。

gist41fe3ca2d129c29074aa68d939bb94e6

ポイントは、 * エスケープ文字がデフォルトではバッククォートであるため、 escape で変更 (Windowsの場合、バッククォートでディレクトリを分けるため) * LogMonitorにより、ETW形式のログを標準出力に出すように設定 * ServiceMonitorを用いて、IISをサービスとして管理

PS>docker build -t iis:v1 https://gist.githubusercontent.com/yuriwoof/41fe3ca2d129c29074aa68d939bb94e6/raw/3db06040b7acc9ad940a3d60c9ea3b4d062befb0/Dockerfile.iis
PS>docker run -d -p 8000:80 --name iis iis:v1

f:id:hermesian:20201103223157p:plain
iis

IISアクセスログも見れる

PS> docker logs -f iis
<Source>EtwEvent</Source><Time>2020-11-03T13:31:33.000Z</Time><Provider idGuid="{7E8AD27F-B271-4EA2-A783-A47BDE29143B}"/><DecodingSource>DecodingSourceXMLFile</DecodingSource><Execution ProcessID="8480" ThreadID="9152" /><Level>Information</Level><Keyword>0x8000000000000000</Keyword><EventID Qualifiers="6200">6200</EventID><EventData><EnabledFieldsFlags>2478079</EnabledFieldsFlags><date>2020-11-03</date><time>13:31:30</time><c-ip>172.16.3.4</c-ip><cs-username>-</cs-username><s-sitename>W3SVC1</s-sitename><s-computername>34c7189aaf3d</s-computername><s-ip>172.18.242.44</s-ip><cs-method>GET</cs-method><cs-uri-stem>/iisstart.png</cs-uri-stem><cs-uri-query>-</cs-uri-query><sc-status>304</sc-status><sc-win32-status>0</sc-win32-status><sc-bytes>143</sc-bytes><cs-bytes>530</cs-bytes><time-taken>2</time-taken><s-port>80</s-port><csUser-Agent>Mozilla/5.0+(Windows+NT+10.0;+Win64;+x64)+AppleWebKit/537.36+(KHTML,+like+Gecko)+Chrome/86.0.4240.183+Safari/537.36</csUser-Agent><csCookie>-</csCookie><csReferer>http://localhost:8000/</csReferer><cs-version>-</cs-version><cs-host>-</cs-host><sc-substatus>0</sc-substatus><CustomFields></CustomFields></EventData>

クラスターオートスケール機能

大分昔に更新して以来すっかり更新を放置していたので、久しぶりに更新してみようと。

前回からすると、お仕事も一新 Azure を触る機会が多くなりました。 いろいろと検証するのですが、書き溜めたメモがなかなか放流できずじまいに。

今回は、Azure Kubernetes Service (AKS) でたまに聞かれることがあるクラスターオートスケール機能について書いてみます。

Azure Kubernetes Service (AKS) でのアプリケーションの需要を満たすようにクラスターを自動的にスケーリング

Kubernetes のオートスケール機能

オートスケールの対象は、PodとNode です。
Pod は、負荷に応じて、Pod 数を増減させる水平オートスケールと、Pod 数ではなく割り当てる CPU / メモリリソースを調整する垂直オートスケール機能があります。
Node の方は、クラスターオートスケール という機能で、負荷状況ではなく、 Pod が割り当てられない (Pending) になった時点で Node を追加する仕組みになります。
現時点で、 AKS でサポートしているのは、水平オートスケール機能とクラスターオートスケール機能になります。

検証

それでは検証してみます。 まずは AKS クラスターを構築します。

$ az group create --name labca --location japaneast
$ az aks create \
  --resource-group labca \
  --name labca \
  --node-count 1 \
  --node-vm-size Standard_DS2_v2 \
  --enable-vmss \
  --enable-cluster-autoscaler \
  --min-count 1 \
  --max-count 3

Standard_DS2_v2 なので、1 ノード当たり 2 vCPU (2000m) 持つことになります (実際、割り当て可能なのは、1900m)。

$ az aks get-credentials -g labca -n labca
$ kubectl get nodes
NAME                                STATUS   ROLES   AGE   VERSION
aks-nodepool1-24609861-vmss000000   Ready    agent   12m   v1.15.10

それでは、以下のようなシナリオで Node が追加されることを確認してみます。

  • 水平オートスケール (HorizontalpodAutoscaler) により、CPU 負荷が 50 % 以上を超えた場合、Pod を最大 5 つまでスケールアウトさせる
  • 対象のPodでは、絶えず負荷がかかるようにしておく

まずは水平オートスケールリソースを作成

$ cat << EOF | kubectl apply -f -
apiVersion: autoscaling/v1
kind: HorizontalPodAutoscaler
metadata:
  name: stress-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: stress
  minReplicas: 1
  maxReplicas: 5
  targetCPUUtilizationPercentage: 50
EOF

次いでオートスケール対象のPodを作成

$ cat << EOF | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
  name: stress
  namespace: default
spec:
  replicas: 1
  selector:
    matchLabels:
      app: stress
  template:
    metadata:
      labels:
        app: stress
    spec:
      containers:
      - name: stress-test
        image: busybox
        command: ["dd", "if=/dev/zero", "of=/dev/null"]
        resources:
          limits:
            cpu: 500m
          requests:
            cpu: 500m
EOF

Pod の増加を見てみます。 他の管理系のPodがあるため、2つ (cpu : 1000m)分のみしかRunningになりませんでした。

kubectl get pods
NAME                      READY   STATUS    RESTARTS   AGE
stress-864dc999dd-cpbk2   1/1     Running   0          65s
stress-864dc999dd-khfxp   1/1     Running   0          2m
stress-864dc999dd-lnggt   0/1     Pending   0          5s
stress-864dc999dd-ngbq7   0/1     Pending   0          5s

次いでクラスターオートスケールの状態を確認すると、Eventsにスケールアップのイベントが発生していることが分かります(vmss サイズを2に変更した旨)。

$ kubectl describe configmap -n kube-system cluster-autoscaler-status
...(省略)...
Events:
  Type    Reason         Age   From                Message
  ----    ------         ----  ----                -------
  Normal  ScaledUpGroup  80s   cluster-autoscaler  Scale-up: setting group aks-nodepool1-24609861-vmss size to 2
  Normal  ScaledUpGroup  80s   cluster-autoscaler  Scale-up: group aks-nodepool1-24609861-vmss size set to 2

しばらく待つと、Nodeが追加されました。

$ kubectl get nodes
NAME                                STATUS   ROLES   AGE   VERSION
aks-nodepool1-24609861-vmss000000   Ready    agent   25m   v1.15.10
aks-nodepool1-24609861-vmss000001   Ready    agent   35s   v1.15.10

それに伴い、PendingになっていたPodもデプロイされ、5つすべてRunningになります。

$ kubectl get pods
NAME                      READY   STATUS    RESTARTS   AGE
stress-864dc999dd-cpbk2   1/1     Running   0          15m
stress-864dc999dd-ffrtq   1/1     Running   0          10m
stress-864dc999dd-khfxp   1/1     Running   0          16m
stress-864dc999dd-lnggt   1/1     Running   0          14m
stress-864dc999dd-ngbq7   1/1     Running   0          14m

以上にようにアプリケーションの負荷に伴い、インフラ側でも柔軟に対応させることが可能となります。
Nodeの増減に関してより細かい設定を行うことも可能です。

クラスター オートスケーラーの設定の変更

GitLab CI環境を自己証明書を使って構築する

※自己証明書を使うのは検証目的に留めておいて、本番運用では証明書の購入が必要。

2018/5/12追記

  • gitlab omnibusインストールのパラメータ修正(gitlab_shell_ssh_port)
  • ジョブとしてDockerイメージをビルドするため、gitlab-runner registerでdocker.sockのマウントを指定

検証環境

検証した環境は以下。

パッケージ バージョン 備考
Docker for Mac(Edge) 18.05.0-ce-rc1-mac63 Macでk8sを触るためEdgeにしている
GitLab Community Edition 10.7.0-ce.0
GitLab Runner v10.7.0

GitLabをDocker Composeで立てる

フォルダ構成は以下。

.
├── docker-compose.yml
└── volume
    ├── certs
    ├── conf
    ├── data
    └── logs

volume/certs配下に自己証明書を作成して、配備する。 ここで作成した証明書はGitLab Runnerを登録する際に利用する。しかしその時にSAN(Subject Altanative Name)を指定して作成しないと以下のようなエラーが出てしまう。

ERROR: Registering runner... failed                 runner=cTGFEKNu status=couldn't execute POST against https://192.168.1.9/api/v4/runners: Post https://192.168.1.9/api/v4/runners: x509: cannot validate certificate for 192.168.1.9 because it doesn't contain any IP SANs

そのため、GitLab用途で証明書を作成する際には、SANを設定する。Macでは/System/Library/OpenSSL/openssl.cnfにあるので、以下のように秘密鍵と証明書を生成する(ただし、192.168.1.9はホストサーバ(Mac)側に割り当たったIPアドレスで、固定である方が望ましい...)。

$ cd volume/certs/
$ openssl req -newkey rsa:4096 -days 365 -nodes -x509 -subj "/C=JP/ST=CHIBA/L=CHIBA/O=NAN/OU=NAN/CN=192.168.1.9" -extensions v3_ca -config <( cat /System/Library/OpenSSL/openssl.cnf <(printf "[v3_ca]\nsubjectAltName='IP:192.168.1.9'")) -keyout server.pem -out server.crt
Generating a 4096 bit RSA private key
.............................................................++
...............................................................................................++
  • docker-compose.ymlは以下。
    • GITLAB_OMUNIbUS_CONFIG以下には、gitlab.rbで指定可能なパラメータを並べることができる
    • TLSの設定はnginx[]の箇所。
    • rootユーザの初期パスワードはgitlab_rails['initial_root_password']で指定したpasswordである
    • GitLab RunnerからGitLabへの登録を行うトークンもあらかじめgitlab_rails['initial_shared_runners_registration_token']でtokenと指定している
version: "3"
services:
  gitlab:
    image: gitlab/gitlab-ce:10.7.0-ce.0
    container_name: gitlab
    restart: always
    environment:
      GITLAB_OMNIBUS_CONFIG: |
        external_url "https://192.168.1.8"
        gitlab_rails['time_zone'] = "Asia/Tokyo"
        gitlab_rails['initial_root_password'] = "password"
        gitlab_rails['initial_shared_runners_registration_token'] = "token"
        gitlab_rails['gitlab_shell_ssh_port'] = 2222
        unicorn['worker_timeout'] = 60
        unicorn['worker_processes'] = 3
        logging['logrotate_frequency'] = "weekly"
        logging['logrotate_rotate'] = 52
        logging['logrotate_compress'] = "compress"
        logging['logrotate_method'] = "copytruncate"
        nginx['listen_port'] = 443
        nginx['redirect_http_to_https'] = true
        nginx['ssl_certificate'] = "/etc/ssl/certs/gitlab/server.crt"
        nginx['ssl_certificate_key'] = "/etc/ssl/certs/gitlab/server.pem"
        nginx['ssl_protocols'] = "TLSv1.1 TLSv1.2"
        # Add any other gitlab.rb configuration options if desired
    ports:
      - "443:443"
      - "2222:22"
    volumes:
      - ./volume/conf:/etc/gitlab
      - ./volume/certs:/etc/ssl/certs/gitlab
      - ./volume/logs:/var/log/gitlab
      - ./volume/data:/var/opt/gitlab
    logging:
      options:
        max-size: "4M"
        max-file: "14"

起動する。

$ docker-compose up -d
Starting gitlab ... done

しばらくするとWebブラウザからhttps://192.168.1.9で接続可能になる(起動未完了時は502エラーページが表示される)。 起動完了後は、docker-compose.ymlで指定したように、ユーザIDroot、パスワードpasswordでログインできる。

GitLab Runnerを登録する

フォルダ構成は下記。

.
├── docker-compose.yml
└── volume
    ├── certs
    │   └── 192.168.1.9.crt
    └── config

ここで192.168.1.9.crtとは、GitLab構築時に作成した証明書を(GitLabのアドレス).crtとして格納する(参考情報)。

  • docker-compose.ymlは以下。
    • ジョブを実行するExecuterとしてホストマシン上のDockerコンテナ上で行いたいため、ホストマシン側のDockerエンドポイント(/var/run/docker.sock)を指定している。ただし、MacではこのUNIXソケットの利用をサポートしていない(?)とのこと。
version: '3'
services:
  runner:
    image: gitlab/gitlab-runner:v10.7.0
    container_name: gitlab-runner
    restart: always
    volumes:
      - ./volume/config:/etc/gitlab-runner
      - ./volume/certs:/etc/gitlab-runner/certs
      - /var/run/docker.sock:/var/run/docker.sock
    logging:
      options:
        max-size: "4M"
        max-file: "14"

起動する。

$ docker-compose up -d
Recreating gitlab-runner ... done

ついで、Runnerを登録する。registerで指定可能なオプションはgitlab-runner register --helpで確認できる(分量が非常に多い)。 また、登録されると./volume/config/config.tomlという設定ファイルが出力されるので、これをあらかじめ設定しておき、registerの引数で渡すこともできる。

$ docker exec gitlab-runner gitlab-runner register -n -r token --executor docker --docker-image alpine:latest --url https://192.168.1.8/ --docker-volumes /var/run/docker.sock:/var/run/docker.sock
Running in system-mode.

Registering runner... succeeded                     runner=token
Runner registered successfully. Feel free to start it, but if it's running already the config should be automatically reloaded!

以上でめでたくGitLab検証環境できた。

SwaggerでWeb APIを作る - 非同期実行 (その1)

これまでは同期処理(リクエストを送ると処理が実行され、レスポンスが返答されるまで待つ処理)であったが、処理が長いものなどは非同期で処理を実行しなくてはならない。PythonではCeleryというライブラリで実現できる。 ここではまず環境構築(Flask, Celeryの連携確認)まで行う。

Celeryとは

  • 非同期のタスク/ジョブキュー管理が可能
  • 複数のBrokerをサポート
    • RabbitMQが推奨
  • Result Backendをしているするとタスクのステータスや実行結果を取得可能
  • OpenStackでも採用実績あり

インストール

前提の環境としては、CentOS7を用いる。これをVirtualBox+Vagrantで環境から用意する。

$ vagrant init centos/7
$ cp Vagrantfile{,.bak}
$ vim Vagrantfile
...(省略)...
config.vm.network "forwarded_port", guest: 5672, host: 5672
 config.vm.network "forwarded_port", guest: 15672, host: 15672
...(省略)...
$ vagrant up
$ vagrant ssh
  • rootパスワードはvagrantが初期値となっている
$ su -
Password: 
# rpm -Uvh http://download.fedoraproject.org/pub/epel/7/x86_64/e/epel-release-7-9.noarch.rpm'
  • RabbitMQのインストールおよび自動起動設定を行う
# yum -y install rabbitmq-server
# systemctl enable rabbitmq-server.service
# systemctl list-unit-files -t service | grep rabbit
rabbitmq-server.service                       enabled
  • Pythonのインストール
    • Pythonのバージョンを切り替えやすいため、anyenv(pyenv)で入れておく
$ sudo yum -y install git zlib-devel bzip2 bzip2-devel readline-devel sqlite sqlite-devel openssl-devel xz xz-devel
$ git clone https://github.com/riywo/anyenv ~/.anyenv
$ echo 'export PATH="$HOME/.anyenv/bin:$PATH"' >> ~/.bash_profile 
$ echo 'eval "$(anyenv init -)"' >> ~/.bash_profile 
$ exec $SHELL -l
$ anyenv install pyenv
$ exec $SHELL -l
$ pyenv install 3.5.4
$ pyenv versions
* system (set by /home/vagrant/.anyenv/envs/pyenv/version)
  3.5.4
$ pyenv global 3.5.4
$ pyenv versions
  system
* 3.5.4 (set by /home/vagrant/.anyenv/envs/pyenv/version)
  • Celery, flaskのインストール
$ pip install celery flask
  • RabbitMQの設定
  • ユーザ, バーチャルホスト, ユーザタグ, 権限を設定
  • 権限は、設定、読み書き全ての権限を付与
# rabbitmqctl add_user vagrant vagrant
Creating user "vagrant" ...
...done.
# rabbitmqctl add_vhost vagrant
Creating vhost "vagrant" ...
...done.
# rabbitmqctl set_user_tags vagrant vagrant_tag
Setting tags for user "vagrant" to [vagrant_tag] ...
...done.
# rabbitmqctl set_permissions -p vagrant vagrant ".*" ".*" ".*"
Setting permissions for user "vagrant" in vhost "vagrant" ...
...done.

FlaskからCeleryを使う

以下の順番で公式チュートリアルを読んでから手を動かし始めた

  1. First Steps with Celery
  2. Celery Based Background Tasks

ほとんどチュートリアル通りの内容。

celery_flask.py

config.py

app.py

次にこれを実行するが、その前にCeleryワーカーを起動していないくてはならない。celery workerコマンドを使うとフォアグラウンドで実行するので、celery multiコマンドを使って、バックグラウンドで実行する

$ sudo mkdir -p /var/run/celery
$ sudo mkdir -p /var/log/celery
$ sudo chown -R vagrant. /var/run/celery/
$ sudo chown -R vagrant. /var/log/celery/
$ celery multi start w1 -A app -l info --pidfile=/var/run/celery/%n.pid --logfile=/var/log/celery/%n%I.log

最後にターミナルをもう1つ開いてタスクが投げ込まれるかを見てみる。

  • ターミナル1
$ python
Python 3.5.4 (default, Aug 24 2017, 12:02:53)
[GCC 4.8.5 20150623 (Red Hat 4.8.5-11)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from app import add_together
>>> add_together.delay(1,3)
<AsyncResult: 0ee3b370-561c-4e9f-a978-58635b00df89>
  • ターミナル2
$ tailf /var/log/celery/w1-1.log
...(省略)...
[2017-09-16 02:24:23,203: INFO/ForkPoolWorker-1] Task app.add_together[74dabaad-aa7e-4dee-aab1-a505deaeb76f] succeeded in 0.0009727960004966008s: 4