20191219のdockerに関する記事は22件です。

k8sの運用ってどうなの?

はじめに

この記事は NTTテクノクロス Advent Calendar 2019 の22日目の記事です。

こんにちは、NTTテクノクロスでOSSクラウド基盤トータルサービスの運用を担当している土屋(@hrfm-tsu)と大野です。

その他、KubernetesやOpenStackを中心としてクラウド基盤の設計や構築を行っています。

今回は社内k8s環境を運用する際に気になったオートヒーリング後の運用について、ちょっとした知見や確認した事を共有します。

Kubernetesの特徴

まず、Kubernetes(k8s)とはコンテナ化したアプリケーションのデプロイ、スケーリング、および管理を行うための、オープンソースのコンテナオーケストレーションシステムであり、マイクロサービスをベースとしたアプリケーションの実装方法としてよく活用されています。
今回は、いくつか存在するk8sの特徴の中で、「回復性」に着目してみたいと思います。

回復性とは?

k8sの大きな特徴として宣言的設定があります。イミュータブルなインフラを作る基本的な考え方で、「システムのあるべき理想状態」を設定ファイルにて宣言することで、障害が発生してもオートヒーリング(自動復旧)してくれます。例えば、k8sでは定義ファイルはマニフェストと呼ばれ、YAML等で記述しますが、replicas: 3と設定することでk8sクラスタ内に常に3Podを稼働させるように宣言できます。

無8png.png

  • マニフェストの例
apiVersion: apps/v1
kind: ReplicaSet
metadata:
  name: replicaset
spec:
  replicas: 3
  selector:
      matchLabels:
        app: replicaset-test
  template:
    metadata:
      labels:
        app: replicaset-test
        ver: "1.0"
    spec:
      containers:
      - name: pod-test
        image: nginx

自動復旧時のPodの起動Workerはどう決まる?

k8s環境ではkube-schedulerがPodをk8s Workerにバインドする役割を担っていますが、新規Pod作成時だけではなく、オートヒーリング時の移動先k8s Workerの選定にも同様のロジック (kube-schesulerがWorker情報が未割当のPodを検知してkube-apiserverにリクエストを送りスケジューリングする) が適用されます。
他の方のQiitaにわかりやすい記載があったので参考までに掲載します。

無題.png

参考
kube-schedulerのソースコードを読みながらPodがNodeにBindされるまでを理解する
kubernetes: スケジューラの動作

障害Workerの復旧後にPodの再配置(リバランス)は行われる?

残念ながら行われません。そのため、Worker毎のPod起動数に偏りが発生します。
(k8s初心者の私はk8sでは良い感じに再配置までやってくれると期待をしていましたが。。。)

それでは、再配置をしたい場合にどうすれば良いか?

  • 方法1:手動でPodを削除してオートヒーリングを発動させる(強引)
  • 方法2:タグ名を更新したコンテナを rollout機能で無停止での更新をする(大変)
  • 方法3:Descheduler for Kubernetes機能に任せる

他にもあるかもしれませんが、今回はDeschedulerをターゲットに進めます。

Deschedulerを動かしてみる!

Deschedulerの動作概要としては

①クラスタ内のWorkerの状態を確認
無題.png

②再配置対象のPodを選定/削除
無題.png
createNodePodsMapによって削除可能なPod(再配置する対象Pod)の一覧を作る

③再配備(新規作成)を既存のスケジューラ(kube-scheduler)にお願いする
無題.png

では、実際に見ていきます。

事前の状態はMaster1台、Worker2台構成で、

[centos@k8s-master ~]$ kubectl get node
NAME          STATUS   ROLES    AGE     VERSION
k8s-master    Ready    master   6d23h   v1.16.3
k8s-worker1   Ready    <none>   6d23h   v1.16.3
k8s-worker2   Ready    <none>   4h3m    v1.16.3
[centos@k8s-master ~]$

30個のPodがWorker1,2に分散して配置されていることとします
1.png

[centos@k8s-master ~]$ kubectl get pod -o wide | grep k8s-worker1 | wc -l
14
[centos@k8s-master ~]$ kubectl get pod -o wide | grep k8s-worker2 | wc -l
16

Worker02を停止してオートヒーリングを発動させてみます
9.png

[centos@k8s-worker2 ~]$ sudo shutdown -h now
Connection to k8s-worker2 closed by remote host.
Connection to k8s-worker2 closed.

[centos@k8s-master ~]$ kubectl get node
NAME          STATUS     ROLES    AGE     VERSION
k8s-master    Ready      master   6d23h   v1.16.3
k8s-worker1   Ready      <none>   6d23h   v1.16.3
k8s-worker2   NotReady   <none>   4h8m    v1.16.3 ★

Worker01に自動復旧したことがわかります
10.png

[centos@k8s-master ~]$ kubectl get pod -o wide | grep k8s-worker1 | wc -l
30 ★

[centos@k8s-master ~]$ kubectl get pod -o wide | grep k8s-worker2 | wc -l
16

ここまでの状態で一度Deschedulerを起動してみます

[centos@k8s-master ~]$ git clone https://github.com/kubernetes-incubator/descheduler
Cloning into 'descheduler'...
remote: Enumerating objects: 119, done.
remote: Counting objects: 100% (119/119), done.
remote: Compressing objects: 100% (117/117), done.
remote: Total 59287 (delta 10), reused 11 (delta 2), pack-reused 59168
Receiving objects: 100% (59287/59287), 63.61 MiB | 10.46 MiB/s, done.
Resolving deltas: 100% (29113/29113), done.
Checking out files: 100% (25856/25856), done.

[centos@k8s-master ~]$ cd descheduler/kubernetes/

[centos@k8s-master kubernetes]$ kubectl apply -f rbac.yaml
clusterrole.rbac.authorization.k8s.io/descheduler-cluster-role created
serviceaccount/descheduler-sa created
clusterrolebinding.rbac.authorization.k8s.io/descheduler-cluster-role-binding created

[centos@k8s-master kubernetes]$ kubectl apply -f configmap.yaml
configmap/descheduler-policy-configmap created

[centos@k8s-master kubernetes]$ kubectl apply -f job.yaml
job.batch/descheduler-job created

[centos@k8s-master kubernetes]$ kubectl get job -n kube-system
NAME              COMPLETIONS   DURATION   AGE
descheduler-job   1/1           27s        43s ★

[centos@k8s-master kubernetes]$ kubectl get pod -o wide | grep k8s-worker1 | wc -l
30

[centos@k8s-master kubernetes]$ kubectl get pod -o wide | grep k8s-worker2 | wc -l
16

Worker02は障害中(NotReady)なので再配置されないことがわかります(想定通り)

次に、Worker03をクラスタに追加します
7.png

[centos@k8s-worker3 ~]$ sudo kubeadm join 192.168.200.4:6443 --token q6wptd.z1ro8vthndp5oklc     --discovery-token-ca-cert-hash sha256:aa702475624ffd50f17dbb3c98cbb2717b904e35f1b8925e68714e0d560c275e
[preflight] Running pre-flight checks
        [WARNING IsDockerSystemdCheck]: detected "cgroupfs" as the Docker cgroup driver. The recommended driver is "systemd". Please follow the guide at https://kubernetes.io/docs/setup/cri/
        [WARNING SystemVerification]: this Docker version is not on the list of validated versions: 19.03.5. Latest validated version: 18.09
        [WARNING Hostname]: hostname "k8s-worker3" could not be reached
        [WARNING Hostname]: hostname "k8s-worker3": lookup k8s-worker3 on 8.8.8.8:53: no such host
[preflight] Reading configuration from the cluster...
[preflight] FYI: You can look at this config file with 'kubectl -n kube-system get cm kubeadm-config -oyaml'
[kubelet-start] Downloading configuration for the kubelet from the "kubelet-config-1.16" ConfigMap in the kube-system namespace
[kubelet-start] Writing kubelet configuration to file "/var/lib/kubelet/config.yaml"
[kubelet-start] Writing kubelet environment file with flags to file "/var/lib/kubelet/kubeadm-flags.env"
[kubelet-start] Activating the kubelet service
[kubelet-start] Waiting for the kubelet to perform the TLS Bootstrap...

This node has joined the cluster:
* Certificate signing request was sent to apiserver and a response was received.
* The Kubelet was informed of the new secure connection details.

Run 'kubectl get nodes' on the control-plane to see this node join the cluster.

[centos@k8s-worker3 ~]$

[centos@k8s-master kubernetes]$ kubectl get node
NAME          STATUS     ROLES    AGE     VERSION
k8s-master    Ready      master   7d1h    v1.16.3
k8s-worker1   Ready      <none>   7d1h    v1.16.3
k8s-worker2   NotReady   <none>   6h14m   v1.16.3
k8s-worker3   Ready      <none>   40s     v1.16.3 ★

再度Deschedulerを実行してみます
(job名が被らないようにjob.yamlの内容を一部変更)

[centos@k8s-master kubernetes]$ kubectl apply -f job.yaml
job.batch/descheduler-job1 created

[centos@k8s-master kubernetes]$ kubectl get job -n kube-system
NAME              COMPLETIONS   DURATION   AGE
descheduler-job   1/1           27s         1h
descheduler-job1  1/1           29s        43s  ★

今度は、状態がReadyであるWorker3に再配置されました
6.png

[centos@k8s-master kubernetes]$ kubectl get pod -o wide | grep k8s-worker1 | wc -l
16 ★
[centos@k8s-master kubernetes]$ kubectl get pod -o wide | grep k8s-worker2 | wc -l
16
[centos@k8s-master kubernetes]$ kubectl get pod -o wide | grep k8s-worker3 | wc -l
14 ★

Zabbixと連携させてDeschedulerを自動起動できるか?

手動実行では面倒なので自動で実行してみたくなりました。

例えば、以下のユースケースを想定してみます。

Workerに障害が発生し、Podが起動するWorkerに偏りが発生する。
偏ったWorkerでCPU使用率が上昇する。
Zabbixが検知し再配置を実行する。

実際にやってみます。


下記記事にもありますがCronJobにて定期的にDeschedulerをキックする方法もありますが、今回は既存監視でZabbixを使っているためこの方法で確認します

参考
Kubernetesのノード障害時の挙動とノード復帰後の再スケジューリング

事前の状態は先ほどと同じく
Master1台、Worker2台、30個のPodがWorker1,2に分散して配置されていることとします

[centos@k8s-master ~]$ kubectl get node -o wide
NAME          STATUS   ROLES    AGE     VERSION   INTERNAL-IP     EXTERNAL-IP   OS-IMAGE                KERNEL-VERSION               CONTAINER-RUNTIME
k8s-master    Ready    master   7d19h   v1.16.3   192.168.200.4   <none>        CentOS Linux 7 (Core)   3.10.0-693.17.1.el7.x86_64   docker://19.3.5
k8s-worker1   Ready    <none>   7d19h   v1.16.3   192.168.200.5   <none>        CentOS Linux 7 (Core)   3.10.0-693.17.1.el7.x86_64   docker://19.3.5
k8s-worker2   Ready    <none>   23h     v1.16.3   192.168.200.6   <none>        CentOS Linux 7 (Core)   3.10.0-693.17.1.el7.x86_64   docker://19.3.5
[centos@k8s-master ~]$

[centos@k8s-master ~]$ kubectl get pod -o wide | grep k8s-worker1 | wc -l
16

[centos@k8s-master ~]$ kubectl get pod -o wide | grep k8s-worker2 | wc -l
14

[centos@k8s-master ~]$ kubectl get job -n kube-system -o wide
No resources found in kube-system namespace. 

Zabbixへ設定する
trigeger設定.png
Action設定.png
Action設定2.png

Worker02で障害(サーバ停止)を発生させる

[centos@k8s-worker2 ~]$ sudo shutdown -h now
Connection to k8s-worker2 closed by remote host.
Connection to k8s-worker2 closed.

[centos@k8s-master ~]$ kubectl get node -o wide
NAME          STATUS     ROLES    AGE     VERSION   INTERNAL-IP     EXTERNAL-IP   OS-IMAGE                KERNEL-VERSION               CONTAINER-RUNTIME
k8s-master    Ready      master   7d19h   v1.16.3   192.168.200.4   <none>        CentOS Linux 7 (Core)   3.10.0-693.17.1.el7.x86_64   docker://19.3.5
k8s-worker1   Ready      <none>   7d19h   v1.16.3   192.168.200.5   <none>        CentOS Linux 7 (Core)   3.10.0-693.17.1.el7.x86_64   docker://19.3.5
k8s-worker2   NotReady   <none>   23h     v1.16.3   192.168.200.6   <none>        CentOS Linux 7 (Core)   3.10.0-693.17.1.el7.x86_64   docker://19.3.5

[centos@k8s-master ~]$ kubectl get pod -o wide | grep k8s-worker1 |wc -l
30 ★
[centos@k8s-master ~]$ kubectl get pod -o wide | grep k8s-worker2 |wc -l
14

その後、Worker03を追加する(手順省略)

Worker02の障害によりWorker01にPodが移動したため
Worker01のCPU使用率が増加する
(今回は試験簡略化のため以下コマンドを実行)

[centos@k8s-worker1 ~]$ stress --cpu 4 --timeout 2m
stress: info: [24413] dispatching hogs: 4 cpu, 0 io, 0 vm, 0 hdd
stress: info: [24413] successful run completed in 120s

CPU使用率の上昇を検知し、ZabbixからDeschedulerが自動で実行している

[centos@k8s-master ~]$ kubectl get job -n kube-system
NAME               COMPLETIONS   DURATION   AGE
descheduler-job1   1/1           27s        43s ★

正しくDeschedulerが動きPodの再配置が行われている

[centos@k8s-master ~]$ kubectl get pod -o wide | grep k8s-worker1 | wc -l
16 ★
[centos@k8s-master ~]$ kubectl get pod -o wide | grep k8s-worker2 | wc -l
14
[centos@k8s-master ~]$ kubectl get pod -o wide | grep k8s-worker3 | wc -l
14 ★

感想

単に機能を動かしてみるレベルでは簡単ですが、
実際に障害を想定した運用を行うために(例えば)以下のような知識や設計が必要になってきます。

  • kube-schedulerのフィルタや優先度付けの仕組みと設定内容を理解すること
  • Deschedulerを実行する条件/前提を明確にすること
    • 例えば、CPU負荷だけの条件ならばPodをautoscalingさせることも一つの手段か?
  • 障害時にどこまで自動復旧するか。自動復旧の範囲を明確にすること
    • 例えば、Cluster Autoscaler機能を使い、リソースが枯渇して既存のWorkerにpodがschedulingできない場合にWorker追加まで自動復旧させる?

様々なk8sの機能やエコシステムの理解(キャッチアップ)だけでも大変ですが、要件に沿った組み合わせ(設計)はより大変だと感じました。

おわりに

今回は、k8s初心者が社内k8s基盤の運用を考えた時に、気になった事の一つであるPod障害時の運用方法について簡単に紹介してみました。
今後もk8sに関連した投稿をしていきたいと思います。

明日は、@yuitomoさんのディープラーニング関連の記事です。
お楽しみに~!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Windows 10 proにDocker環境を構築する

全体の手順

  1. PCスペックの確認
  2. Hyper-Vの有効化
  3. installerの取得
  4. インストール

PCスペックの確認

  1. [Windows] - [設定] - [システム] -[バージョン情報]
  2. 以下の条件を満たしていることを確認
    • システムの種類:64ビット
    • エディション: Windows10 Pro

Hyper-Vの有効化

  1. [コントロールパネル]-[プログラム]-[windowsの機能の有効かまたは無効化]
  2. Hyper-Vを有効にする
  3. PCを再起動する

image.png

Docker for windows をインストールする

installerをゲットするにははDocker Hubにログインが必要。
IDはpublicリポジトリ名と合わせて表示されるので慎重に考えましょう。

Docker Hub

アカウントを作成してsign inすると
Docker for windowsのインストール案内がでました。

設定は全てdefaultで進めて、install完了後に再起動。
再起動後にPowerShellを起動し、以下のコマンドを実行

docker --version

versionが表示されればinstall OKです

次回

  • Dockerの起動
  • コンテナの作成

参考リンク

Install Docker Desktop on Windows
Docker for Windowsをインストール

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

とりあえずつかってみようぜDockerを

はじめに

いなたつアドカレの十九日目の記事です。

もうあと1週間を切りましたね。。。

ネタも切れてますが。。。。

先日Dockerの簡単な導入をハンズオンで開催したので、その資料を供養しようかと、、、

雰囲気ですら理解せずに使ってみる

コンテナとか仮想でうんたらとかとりあえずおいといて使ってみようよの会ですね。

dockerの愉快なコマンドたち

  • docker run
    • コンテナを引っ張ってきて起動するコマンド
  • docker exec
    • コンテナ内でコマンドを実行するコマンド
  • docker ps
    • コンテナの起動状況を確認するコマンド
  • docker stop
    • コンテナを止めるコマンド(ころころする)
  • docker rm
    • コンテナを削除するコマンド(成仏してクレメンス)
  • docker start
    • コンテナを蘇生するコマンド(ザオリク?ドラクエしらねぇ)

$ docker stop [CONTAINER_NAME]でコンテナさんの命を断ちます、しかし、この世界は蘇生ができる世界なので成仏するまでは復活する可能性があります。
しかし$ docker rm [CONTAINER_NAME]でお亡くなりになっているコンテナさんに成仏していただくことができます。
そして、お亡くなりになっているコンテナに$ docker start [CONTAINER_NAME]をすることで、蘇生できます。

docker run のオプション

  • iオプション -i
    • ホスト -> コンテナ の入出力をつなげる
  • tオプション -t
    • コンテナ -> ホスト の入出力をつなげる
  • dオプション -d
    • バックグラウンド実行
  • vオプション -v
    • ファイルを共有する
  • pオプション -p
    • ポートをつなげる
  • wオプション -w
    • 作業ディレクトリを指定する
  • nameオプション --name
    • コンテナに名前をつける
  • rmオプション --rm
    • コンテナをstopしたら自動でrmする

オプションitdはハッピーセット

docker run の実際の例

$ docker run -itd --name python -v $(PWD):/py -p 8000:8000 -w="/py" python

$ docker run -itd --name [CONTAINER NAME] -v $(PWD):[CONTAINER DIRCTORY] -p [LOCAL PORT]:[CONTAINER PORT] -w="[CONTAINER DIRCTORY]" [IMAGE NAME]

$(PWD) はローカルのカレントディレクトリを展開(pwdコマンドの実行)

docker-compose

docker-compose.ymlにコンテナの概要を記述し、Dockerファイルを用いてコンテナの詳細を定義する。
とりあえず触ってみようか

こまんど

  • docker-compose run CONTAINER_NAME COMMAND
    • 指定したコンテナでコマンドを実行する (up より前に)
  • docker-compose up
    • コンテナを全て起動する、ログ一生吐き続けるので -d をつけよう
  • docker-compose exec CONTAINER_NAME COMMAND
    • 指定したコンテナでコマンドを実行する (up より後に)
  • docker-compose down
    • docker-composeで管理している全てのコンテナに対してstopとrmをする
  • docker-compose ps
    • コンテナの状態の確認
  • docker-compose logs CONTAINER_NAME
    • 指定コンテナのログをみる

つかう

  • docker-compose up -d
  • docker-compose ps
  • docker-compose exec nginx bash
  • docker-compose down

おまけ

便利なやつ

  • docker stop \$(docker ps -a -q) && docker rm \$(docker ps -a -q)
    • 全てのコンテナを亡きものにする、ころころして成仏していただく
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

DockerでUbuntu18.04+Pythonの機械学習用の環境を1時間で用意するチャレンジをしてみた

はじめに

この記事はとりあえずなんでも Advent Calendar 2019の21日目の記事です。

とある日に思った

Windows環境にPythonとpyTorchを入れて適当に機械学習を回していたのですが、突然
「あー、自由にいじれるLinuxがほしい。でもVMはなんかかっこ悪いし、重たいしやだ(偏見)」
「DockerでUbuntu入れてみるとDockerの勉強になったりするのでは?」
「なんかモダンな環境構築っぽい(知識少なすぎて他の構築方法VMくらいしか知らない)」
「1時間あるし、構築チャレンジするか!!」
と、完全に思いつきだけで構築チャレンジをしてみました。
なので、Win10にDockerを使ってUbuntu環境を作る、です。
その時のメモを公開します。

自分のスペック

・VM構築経験なし(なのに偏見持ち)
・Linux初心者(実は初めてのLinux環境構築)
・Docker初心者(1回触ったくらい)

という初心者丸出しのスペックなので、相当躓きました。
躓いた箇所を全部メモしておいたので、温かい目で見てください。

1時間もかかるの?

そんなことありません。ある程度知っている方がやれば1時間どころか15分くらいで終わると思います。
躓いて、調べて、PC再起動して、野暮用で時間を消費して、間にこのメモを書きつつ実行してを全て含めて、1時間です。
(記事にするにあたって色々MarkDownで囲っただけなので、コマンドのメモとかも全部その1時間内に書いています)
タイマーを使って何度か時間を確認していたので、時間軸でお楽しみください。

それでは構築スタート。

https://www.docker.com/get-started
からDocker Desktopを入手
サインインが必要
適当にサインアップしてゲット

~2分~

installer起動して、すべてデフォルトでポチポチ
その間にUbuntuを起動する方法を調べておく
https://weblabo.oscasierra.net/docker-ubuntu1604/

    docker pull ubuntu:16.04
    docker run -it -d --name ubuntu1604 ubuntu:16.04

でOKっぽいが、18.04を使うか
install完了にサインアウトが必要らしく、投げ出される

~5分~

PC起動待ち

~7分~

Hyper-V無効化されていると言われる
有効化したいので再起動
なんか更新プログラムも走って2回再起動した
野暮用もあり時間を消費する

~24分~

コマンドプロンプトから

docker pull ubuntu:18.04

を打ち込む

Error response from daemon: Get https://registry-1.docker.io/v2/: net/http: request canceled while waiting for conne    ction (Client.Timeout exceeded while awaiting headers)

dockerのsettingsからプロキシ設定する
リトライしてok

~27分~

docker run -it -d --name ubuntu1804 ubuntu:18.04
docker exec -it ubuntu1604 /bin/bash

bashが起動できた!

~28分~

pythonが入っていないので入れることにした
https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/install-linux-python.html

$ apt-get install python3
    E: Unable to locate package python3

https://qiita.com/hatorijobs/items/c503840c13672e12d188
え、vimも入っていないの…

    $apt update

Connection refusedする。IPは91.189.88.24 の80番ポート

    $ping 91.189.88.24
    ping: command not found

え、pingも入っていないの…!?
まぁつながらないのは99%くらいプロキシだろうからいいか。

    $export HTTP_PROXY=[使っているプロキシ]
    $export HTTPS_PROXY=[使っているプロキシ]

これでも駄目。
https://qiita.com/48saaan/items/47f8d9cd3321c3bcfce6
aptは小文字らしい。Case Sensitiveなの知らなかった。

    $export http_proxy=[使っているプロキシ]
    $export https_proxy=[使っているプロキシ]
    $apt update

いけた。

~40分~

    $apt install vim
    $apt install python3.8

いけたいけた。

ここでお菓子を取りに行って時間を消費。

~48分~

pipを入れようと思う

    $curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py

curlないの!?

    $apt install curl
    $curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py

★ちなみにcurlは大文字のHTTP_PROXYを参照する…

    $python3.8 get-pip.py
    ModuleNotFoundError: No module named 'distutils.util'

https://www.bioerrorlog.work/entry/install-pip-pip3-ubuntu
pip3はget-pip.pyから入れられないのか…

    $apt install python3-pip

入った。
試しにnumpyとか入れてみるか。

    $pip3 install numpy

★16.04の版ではここでエラー。python2系と3系が同居してしまっていた。
ImportError: cannot import name main
https://qiita.com/Suzukaze31/items/e6d15ddd9ffcd5e6c246
二つ目の対処をやればOKだった。

めでたくUbuntu+python環境完成!!!

~57分~

調子に乗ってtorch入れる

    $pip install torch

確認する。

$python3
    >>>import torch
    >>>x=torch.ones(2,2)
    >>>print(x)
    >>>exit(0)

2x2の単位行列が作れたことを確認したのでOK。
ここで60分。

終わりに

今の時代こんなに簡単に環境構築できるとは。そして自分のスキルのなさを痛感しました。
dockerfile書けとか言われてもまだ書けないので、今度書いてみます。
なんにせよ、好きにいじれるLinux環境をゲットできたので、色々やってLinux勉強しようと思います。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Rails, Dockerのマルチステージビルドに失敗した時の対応

問題

  • dockerのマルチステージビルドにてbundle installの内容をコピーしていた
    • COPY --from=builder /usr/local/bundle /usr/local/bundle
    • ここについての詳細は、Railsのdockerイメージを小さくする方法などで調べてください
  • Bundler 2.1.1, RubyGem 3.1.1がリリースされたことで、dockerビルド時のバージョンが勝手にアップデートされ、Railsが起動できなくなった

対応

  • /usr/local/lib もコピーする
COPY --from=builder /usr/local/lib /usr/local/lib
COPY --from=builder /usr/local/bundle /usr/local/bundle

詳細

エラーの詳細

  • nokogiriなどの with native extension なgemが全て読み込まれなくなっていた
    • 以下のようなエラーが発生
      • bundle実行時
        • Could not find nokogiri-1.10.7 in any of the sources
      • gem listなど実行時
        • Ignoring nokogiri-1.10.7 because its extensions are not built. Try: gem pristine nokogiri --version 1.10.7

対応について

  • /usr/local/lib をコピーしたものとしていないものを比較すると、/usr/local/lib/ruby/site_rubyの容量が変わっていたため、このあたりにnative extension周りのものが含まれると思われる(ここの詳細はわかっていません)
    • /usr/local/libではなく、/usr/local/lib/rubyのみのコピーでも動作可能
    • /usr/local/lib/ruby配下はsite_ruby以外に容量の変化はなかったため、/usr/local/lib/ruby/site_rubyまで指定する必要はないように思われる
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

sbtを使ってDockerize

dockerizeに便利なプラグインを追加

sbt-native-packager:https://github.com/sbt/sbt-native-packager

plugins.sbt
addSbtPlugin("com.typesafe.sbt" % "sbt-native-packager" % "1.5.1")

プラグインを有効化

build.sbt
enablePlugins(JavaAppPackaging)

Dockerfile自動生成

# Dockerfileを自動生成
$ sbt docker:stage

target/docker/stage/opt/Dockerfileが作成される

おまけ

# Dockerイメージを生成
$ sbt docker:publishLocal
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

DockerとDocker ComposeをUbuntu18.04にインストールする。2019年冬

概要

2019年12月にUbuntu18.04にDockerをインストールする手順についてまとめます。
なるべく新しいのを入れます。

環境

% cat /etc/lsb-release
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=18.04
DISTRIB_CODENAME=bionic
DISTRIB_DESCRIPTION="Ubuntu 18.04.3 LTS"

とりあえず何もしないでaptで入れられるやつ

% sudo apt update
% sudo apt install docker docker-compose
% docker -v
Docker version 18.09.7, build 2d0083d
% docker-compose -v
docker-compose version 1.17.1, build unknown

なるべく新しいのを入れる

これらの方がが新しいみたいなのでこちらを使います。
「とりあえず何もしないでaptで入れられるやつ」でインストールしましたがそれらはなかったことにして、Docker系は何もインストールしていない状態からの手順を書きます

Get Docker Engine - Community for Ubuntu

https://docs.docker.com/install/linux/docker-ce/ubuntu/

詳細は上記URLの公式ドキュメントを参照してください。
実行したコマンドだけ書きます。

% sudo apt 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 add-apt-repository \
   "deb [arch=amd64] https://download.docker.com/linux/ubuntu \
   $(lsb_release -cs) \
   stable"
% sudo apt update
% sudo apt install docker-ce docker-ce-cli containerd.io
% docker -v
Docker version 19.03.5, build 633a0ea838

docker: Got permission denied と出る場合は以下のコマンドを実行する。

% sudo usermod -aG docker ${USER}

上記コマンドを実行した後、一度exitしてからログインしなおせばエラーが出なくなる。

Install Docker Compose

https://docs.docker.com/compose/install/

詳細は上記URLの公式ドキュメントを参照してください。
実行したコマンドだけ書きます。
上述の通りDockerのインストールは済んだ状態で進めます。

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

/usr/binln を作っておくと親切だと思うけど必須ではない。

% sudo ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose

まとめ

https://docs.docker.com/ の手順に従い、新しいDockerがUbuntu18.04にインストールできました

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

GitLab CI/CDによるDocker環境へのパイプライン考察

この記事は富士通システムズウェブテクノロジー Advent Calendarの20日目の記事です。
本記事の掲載内容は私自身の見解であり、所属する組織を代表するものではありません。

はじめに

GitLab CI/CD 便利ですよね。Gitリポジトリへのコミット等をトリガーに自動処理を実行できます。
自由度が高いので何でもできるのですが、逆に自由すぎて迷うことも多々あります。
本記事では、Docker環境を活用したCI/CDの型について模索してみたいと思います。

この延長で、社員が気軽にアイデアを試せる下記のような環境を作ることを目指しています。

  • Webサービスを動かすマシンを自分で用意しなくていい
  • サーバOSにログインしてインストール・セットアップ作業をしなくていい
  • GitリポジトリにコードをコミットするだけでWebサービスが動く

※ちなみに筆者はKubernetesについては実践経験がありません。スモールスタート主義なので、Dockerだけだと無理だ・・・と実感してから検討したい派。実はまだDocker Composeも使っていません。が、Docker Composeはさすがに使いたくなってきたところ・・・。

環境

本記事の試行錯誤の環境は下記のとおりです。

  • GitLab環境
    • GitLab.com
  • ビルドサーバ (CPU: 2core, MEM: 4GB)
    • CentOS 7.7
    • Docker CE 19.03.5
    • GitLab Runner 12.5.0
  • デプロイ先サーバ (CPU: 2core, MEM: 4GB)
    • CentOS 7.7
    • Docker CE 19.03.5

※実際には社内ネットワークからのインターネットアクセスには認証プロキシを突破するための設定が必要ですが、本記事では本質ではないため割愛しています。

CI/CDパイプラインの設計

Dockerのポータビリティを活かしたい、言い換えると、OSに直接何かをインストールすることを極力避けたいです。

  • Webサービスの構成要素はすべてDockerコンテナにする
  • アプリケーションのビルド環境にもDockerコンテナを使う

この路線でCI/CDパイプラインを設計すると下記のようになります。

  • ビルド用コンテナをビルドする
  • ↑のビルド用コンテナを使ってアプリケーションをビルドし、Webサービスコンテナにする
  • Webサービスコンテナを別サーバであるデプロイ先サーバ上に起動する

0.ビルド用コンテナをビルドする準備

まずはビルドサーバを構築します。
CentOSにDockerをインストールする手順はDocker本家に詳しく書かれているので割愛します。
また、GitLab Runnerをインストールする手順についてもGitLab本家をご参照ください。

ここではDockerコンテナを使って自動ビルドできるようにしたいため、Docker ExecutorモードでGitLab Runnerをregisterします。(やはり本家の手順書が参考になります。)

ただ、ちょっと待てよ。ビルド用コンテナをビルドするためのコンテナは何にすればいいのか?
そもそも、Dockerコンテナの中でDockerイメージをビルドできるのか?など疑問が次々と出てきます。

それで色々と勉強したのですが、主に下記記事はたいへん参考にさせていただきました。

まとめると、Dockerコンテナの中でDockerイメージをビルドする方法は下記の通り:

  • Docker in Docker、Docker outside of Docker、Daemon-less Image Builder の3方式がある
  • Docker in Docker(DinD)は権限が強すぎて危なそう
  • Daemon-less Image Builderはまだあんまり事例がない
  • CIで使う時はDocker outside of Docker(DooD)でいいんじゃないか?という情報が多い

ということで素直にDocker outside of Docker(DooD)方式を採用することにします。

Docker outside of Docker(DooD)をするには具体的に何をすればいいのでしょうか?
どうやらDocker Hubにdockerという名前のコンテナがあるらしく、このコンテナはDockerの中でDockerを使うためのコンテナのようです(DinD、DooD兼用)。素晴らしい!(素晴らしいが名前が極めて紛らわしい!)
このdockerコンテナを使った上で、「コンテナ側からホストのdocker.sock (/var/run/docker.sock)をマウント」すればいいらしいです。(この仕組みについては、まだ人様に説明できるほど理解できていません。)

GitLab Runnerのregisterは下記の手順で実施しました

# gitlab-runner register -n \
--url <GitLab URL> \
--registration-token <token> \
--executor docker \
--description <サーバ名等> \
--tag-list "docker-build" \
--docker-image "docker:latest" \
--docker-volumes /var/run/docker.sock:/var/run/docker.sock
  • 4行目 --executor docker はDocker Executorとしてregisterするという意味
  • 6行目 --tag-list "docker-build"docker-buildというタグ名で登録という意味
  • 7行目 --docker-image "docker:latest" がDockerの中でDockerを使うためのコンテナを指定
  • 8行目 --docker-volumes /var/run/docker.sock:/var/run/docker.sock がコンテナ側からホストのdocker.sockマウント

1.ビルド用コンテナをビルドする

Docker outside of Docker(DooD)の環境ができたので、早速ビルド用のコンテナを作ってみることにします。
言語は何でもいいのですが、私が一番馴染みがあるJavaをGradleでビルドするコンテナを作ります。

GitLabに新規プロジェクトを作成します。ここでは名前を「gradle-container」とします。
このプロジェクトには下記2ファイルを配置してコミットします。

Dockerfile
FROM gradle:6.0.1-jdk13
.gitlab-ci.yml
stages:
    - package

package:
    stage: package
    tags:
        - docker-build
    script:
        - docker login -u gitlab-ci-token -p ${CI_BUILD_TOKEN} ${CI_REGISTRY}
        - docker build -t ${CI_REGISTRY_IMAGE} .
        - docker push ${CI_REGISTRY_IMAGE}
  • tagsに指定しているdocker-buildは、register時に指定したタグ名

これらのファイルをコミットすると自動ビルドが実行され、見事ビルド用コンテナができあがります。
※この例は、Docker Hubに置いてあるgradleコンテナを持ってきて、そのままGitLab Registryに格納しているだけですが、ビルド環境にカスタマイズを入れたい場合にこのDockerfileを修正するだけなのでCI/CDの型としては重宝します。

2.ビルド用コンテナを使ってアプリケーションをビルドし、Webサービスコンテナにする

上記で作成したビルド用コンテナを使って、次にアプリケーションをビルドします。

GitLabに新規プロジェクトを作成します。ここでは名前を「sampleapp」とします。
自動ビルドを制御する下記2ファイルを作成してコミットします。
(アプリケーションのソースコードは割愛します。素のServlet/JSPを想定。)

gitlab-ci.yml
stages:
  - build

build:
  stage: build
  image: <ビルド用コンテナのURL>:latest
  tags:
      - docker-build
  script:
      - gradle war
  artifacts:
      paths:
          - build/libs/*.war
build.gradle
apply plugin: 'eclipse-wtp'
apply plugin: 'war'

repositories {
    jcenter()
}

dependencies {
    providedCompile 'javax.servlet:javax.servlet-api:3.1.0' 
}

sourceCompatibility = '1.8'
targetCompatibility = '1.8'
  • buildジョブのimageにビルド用コンテナを指定しています

これで、自分で作ったビルド用コンテナを使ってアプリケーションのビルドに成功しました。
続いて、このアプリケーションをWebサービスコンテナにしましょう。

同じGitLabプロジェクトに下記を追加します。

gitlab-ci.yml
stages:
    - build
    - package

build:
    <省略(上記参照)>

package:
    stage: package
    tags:
        - docker-build
    script:
        - docker login -u gitlab-ci-token -p ${CI_BUILD_TOKEN} ${CI_REGISTRY}
        - docker build -t ${CI_REGISTRY_IMAGE} .
        - docker push ${CI_REGISTRY_IMAGE}
    dependencies:
        - build
Dockerfile
FROM tomcat

COPY build/libs/*.war /usr/local/tomcat/webapps/
  • アプリケーションをWebサービスとして動かすためにTomcatを使います。Dockerfileではtomcatコンテナをベースに、ひとつ前のジョブで作成したアプリケーションのWARファイルを同梱しています。

自分で作ったビルド用コンテナを使ってアプリケーションをビルドし、それをWebサービスコンテナにすることに成功しました。

3.Webサービスコンテナを別サーバであるデプロイ先サーバ上に起動する

最後に上記で作成したWebサービスコンテナをデプロイします。

さて、GitLab CI/CDを使ってデプロイ先サーバ上でWebサービスコンテナを起動するにはどうすればいいでしょうか?
デプロイ先サーバにGitLab Runnerを入れてしまうことを考えましたが、アプリケーションの実行環境にはあまり入れたくありません。
軽く調べた結果、GitLab RunnerにSSH Executorというモードがあります。これを使えば、ビルドサーバ上から別サーバを操作できそうです。接続のための情報もビルドサーバに保存されるので、GitLab上にパスワードを設定するよりは安全なのではないかと。

GitLab Runner SSH Executorのregister手順は割愛しますが、対話型で接続先のサーバのIPアドレス、ユーザ、パスワードまたは鍵ファイルを指定するだけです。タグ名はdocker-sshにしたと仮定します。

さて、実際にデプロイを行うスクリプトを書いていきましょう。
※デプロイと言っても、ここでは簡単にdocker runで起動する程度に留めます。

GitLabプロジェクトを新規作成します。ここではプロジェクト名は「sampleapp-deploy」とします。

.gitlab-ci.yml
stages:
    - deploy

deploy_review_app:
    tags:
        - docker-ssh
    stage: deploy
    variables:
        GIT_STRATEGY: none
    script:
        - sudo docker login -u gitlab-ci-token -p ${CI_BUILD_TOKEN} ${CI_REGISTRY}
        - sudo docker pull <sampleappコンテナのURL>:latest
        - sudo docker rm -f sampleapp
        - sudo docker run --name sampleapp -d -p 8080:8080 <sampleappコンテナのURL>:latest
    environment:
        name: review
        url: http://xxx.xxx.xxx.xxx:8080/
        on_stop: stop_review_app
    when: manual

stop_review_app:
    tags:
        - docker-ssh
    stage: deploy
    variables:
        GIT_STRATEGY: none
    script:
        - sudo docker stop sampleapp
    when: manual
    environment:
        name: review
        action: stop

    environment:
        name: review
        action: stop
  • デプロイと停止はマニュアルジョブとして定義しました。
  • GitLab CI/CDのEnvironment機能を活用して、デプロイと停止が対になるように設定しています。
  • デプロイした際に、GitLabのUIからアプリの画面を直接起動できるURLを設定しています。
  • ※初回実行に失敗するバグがあります(これがDocker Composeを使いたい理由の第一位)

パイプライン考察

GitLab CI/CDによるDocker環境へのパイプラインの型をまとめると下図の通りです。

20191219dockerpipeline.png

  • ソースコードをビルドしてコンテナにするまでをビルドプロジェクト(CI)、デプロイ先サーバにデプロイするのはデプロイプロジェクト(CD)と、プロジェクト分割している。実際は複数モジュールを組み合わせてデプロイすることや、複数環境にデプロイすることなどを考慮しなければならない。その時の柔軟性を考慮すると、この分割の仕方が良いと思っている。
  • 上記を実現するためには、ビルドプロジェクトの成果物には環境依存の情報を含めてはいけない。Dockerコンテナ起動時に環境変数として渡すか、マウント先のファイルで制御できるようにする。
  • SSH Executorがベストソリューションなのか自信がない。Docker Executor上でAnsibleによる制御の方が良い気もしているが、その場合、サーバへの接続情報はGitLabのSECRET VARIABLES等を使うのだろうか?
  • 今後の検討事項
    • Webサービスを複数コンテナ構成にする(アプリケーションとデータベース等)
    • 検証環境と本番環境といった複数デプロイ環境に対応する。いや、ブランチごとに別環境としてデプロイする場合にはどうすればいいか?
    • デプロイパイプラインを特定の人しか実行できないようにするにはどうすればいいか?
    • 監視、ログなどを共通的に実現できないか?
    • 環境を汚さないのはいいけど遅い!

こんな感じでやりたいことを消化していくと、いつかKubernetesが欲しいと思うようになる日が来るのだろうか?

おわりに

この半年間、少しずつ時間をかけてGitLab CI/CDとDockerを実際に使ってみて、環境を汚さないって素晴らしい!すべてをコードで記述するって素晴らしい!と感動しています。
これから、実際のプロジェクトでこういった技術を使う機会が増えてくればいいなぁ。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Docker上で mysqlコンテナにログインする方法

Docker上でMySQLにログインするには、docker composeのコマンドと一緒にmysqlのログインコマンドを入力する必要があります。

Docker上では以下のコマンドでMySQLにログインできます。

$ docker exec -it [コンテナ名] mysql -u root -p

では、確認していきます。
まず、docker-compose psコマンドで、MySQLのコンテナが起動していることを確認します。

$ docker-compose ps
指定されたパスが見つかりません。
  Name                Command               State                  Ports
---------------------------------------------------------------------------------------
composer   /bin/sh /docker-entrypoint ...   Exit 0
laravel    docker-php-entrypoint /usr ...   Exit 0
mysql      docker-entrypoint.sh mysql ...   Up       0.0.0.0:33306->3306/tcp, 33060/tcp
nginx      nginx -g daemon off;             Up       0.0.0.0:8080->80/tcp
php-fpm    docker-php-entrypoint php-fpm    Up       9000/tcp

コンテナ名(Name)がmysqlになっているコンテナが起動中のmysqlコンテナです。

さっそく、ログインしてみます。

ターミナル
$ docker exec -it mysql mysql -u root -p
Enter password:

ログイン成功後に、生成されているデータベースを一覧表示できていることがわかります。

実行結果
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 9

~(省略)~

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql>show datases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| sys                |
+--------------------+
5 rows in set (0.01 sec)

以上です。
Docker勉強中で分からないことが多いので、積極的に勉強中です!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

SpringBoot1.5 + Gradle4.4 + Java8 + Docker 環境を Java11 に対応させる

この記事は、Java Advent Calendar 2019 15日目の記事です。

はじめに

Java8の無償サポートが2019年1月に切れてから一年ほど放置してましたが、
今回時間ができたので8以降初のLTSであるJava11への更新を行いました
その時のメモです

と書き始めようとしましたが、OpenJDK8は2023年6月までサポートされるらしいので、
OracleJDKからOpenJDKに変えるだけでよかったのでは......
でもやってしまったので記録しときます

JDKのアップデート

AdoptOpenJDK11をインストール

以下のコマンドを入れると最新のJDKがインストールされる

brew cask install adoptopenjdk

しかし、現状ではJDK13が入ってしまうので、LTS版であるJDK11を入れるにはバージョンを指定する必要がある
ということで公開されているバージョンを検索

$ brew search adoptopenjdk
==> Casks
adoptopenjdk                       adoptopenjdk11-openj9-jre          adoptopenjdk12-openj9              adoptopenjdk13-jre                 adoptopenjdk8                      adoptopenjdk8-openj9-large
adoptopenjdk10                     adoptopenjdk11-openj9-jre-large    adoptopenjdk12-openj9-jre          adoptopenjdk13-openj9              adoptopenjdk8-jre                  adoptopenjdk9
adoptopenjdk11                     adoptopenjdk11-openj9-large        adoptopenjdk12-openj9-jre-large    adoptopenjdk13-openj9-jre          adoptopenjdk8-openj9
adoptopenjdk11-jre                 adoptopenjdk12                     adoptopenjdk12-openj9-large        adoptopenjdk13-openj9-jre-large    adoptopenjdk8-openj9-jre
adoptopenjdk11-openj9              adoptopenjdk12-jre                 adoptopenjdk13                     adoptopenjdk13-openj9-large        adoptopenjdk8-openj9-jre-large

今回入れたいのはJDK11なので、以下のコマンドでバージョンを指定してインストール

brew cask install adoptopenjdk11

すでに通っているPATHをJDK11に変更

export JAVA_HOME=`/usr/libexec/java_home -v 11`

バージョンを確認

$ java -version
openjdk version "11.0.5" 2019-10-15
OpenJDK Runtime Environment AdoptOpenJDK (build 11.0.5+10)
OpenJDK 64-Bit Server VM AdoptOpenJDK (build 11.0.5+10, mixed mode)

IntelliJのProjectSDKにJDK11を指定

Command + ; でProjectStructureを開き、ProjectSDKとProjectLunguageLebelにJDK11を指定

Gradleをアップデート

Gradle4.4はJDK11に対応していないため、アップデートする必要がある
5.0以降のバージョンは対応しているようだが、折角なので現在の最新である6.0.1にしてみる

gradle-wrapper.propertiesの更新

gradle/wrapper/gradle-wrapper.properties に記載されているバージョンを更新すればプロジェクトに適用されるGradleのバージョンが変更される
今回は以下の 4.4 の部分を 6.0.1 に変更するだけ

distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-bin.zip

gradlewを更新

それだけだとgradlewに変更が伝播しないので、gradlewを生成し直す

./gradlew wrapper

gradle.buildを更新

各種フレームワーク・ライブラリをJDK11に対応したものに変更していく
基本的には全てgradle.buildに記載されているので、それを修正していけばよい

Javaのバージョン指定を変更

各指定の意味は公式に記載されているので、必要な物を指定する

sourceCompatibility = 11
targetCompatibility = 11

Gradle6系の記載方法に変更

jarの指定方法が変更になっているので更新
詳細は公式を参照のこと

jar {
    baseName = 'example'
    version = '1.0.0-SNAPSHOT'
}

上記だったものを以下に変更

bootJar {
    archiveBaseName = "example"
    archiveVersion = "1.0.0-SNAPSHOT"
}

SpiringBootのバージョンを変更

Java11を利用するにはSpringBoot2.1.x以上が必要
折角なので現在の最新である2.2.2にしてみる
基本的には公式のマイグレーションガイドに従えばいい

buildscript {
    ext {
        springBootVersion = '1.5.13.RELEASE'
    }
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
    }
}

SpringBootが自動で依存関係管理プラグインを適用しなくなったので、プラグインも追加する

apply plugin: 'io.spring.dependency-management'

メインクラスの指定方法が変わっているので、それも変更

springBoot {
    mainClassName = "jp.co.example.Application"
}

ライブラリのバージョンを更新

MavenRepositoryを見ながらライブラリのバージョンを更新していく

SpringBoot周りのライブラリ

SpringBoot周りのライブラリはバージョンを変数で指定しておくと楽
ただし、一部SpringBootのバージョンと異なる場合があるので注意が必要

dependencies {
    compile("org.springframework.boot:spring-boot-starter-web:${springBootVersion}")
    compile("org.springframework.boot:spring-boot-starter-jdbc:${springBootVersion}")
    compile("org.springframework.boot:spring-boot-starter-security:${springBootVersion}")
    compile("org.springframework.boot:spring-boot-starter-actuator:${springBootVersion}")

    testCompile("org.springframework.boot:spring-boot-starter-test:${springBootVersion}")
    testCompile("org.springframework.security:spring-security-test:5.2.1.RELEASE")
}

lombok

依存関係の指定方法が変わっているため、普通にバージョンだけ指定して更新するとビルド時にlombokで自動生成されるメソッドが見つからないとエラーが出る
公式を参考にして、以下の指定を追加する

compileOnly('org.projectlombok:lombok:1.18.10')
annotationProcessor('org.projectlombok:lombok:1.18.10')

また、@Valueを使っていると、Jacksonでのデシリアライズがうまく機能しないエラーが発生する
project直下に lombok.config というファイルを作って以下を記載することで回避できる
その他の設定については公式のマイグレーションガイドに記載がある

lombok.addJavaxGeneratedAnnotation = false
lombok.addLombokGeneratedAnnotation = true
lombok.noArgsConstructor.extraPrivate = false
lombok.anyConstructor.addConstructorProperties = true

config.stopBubbling = true

jjwt

一部のアルゴリズムを利用する際にJava11で廃止されたAPIに依存しているため、
Java8時点で利用していてJava11に移行してきた場合、依存関係を追加する必要がある
公式を参考に以下を追加する

compile('javax.xml.bind:jaxb-api:2.3.0')
compile('com.sun.xml.bind:jaxb-core:2.3.0')
compile('com.sun.xml.bind:jaxb-impl:2.3.0')

その他

よしなに更新する
dependencies 以外で指定しているライブラリについても忘れず更新すること
jacoco の更新を忘れて時間を無駄にしました

Java11で廃止になったAPIに対応する

この記事を参考に廃止になったAPIを修正していく

SpringBoot2.0で廃止になったクラスを修正する

ビルドしてみるとSpringBoot周りで廃止されたクラスが複数出てくるので、それを修正していく
公式のマイグレーションガイドを参考にする

今回はエラーメッセージを制御していた org.springframework.boot.autoconfigure.web 周りのクラスだけが対象だった
以下のメソッドを

@RequestMapping(value = "/error")
public ErrorResponse error(HttpServletRequest request, HttpServletResponse response) {
  ServletRequestAttributes attributes = new ServletRequestAttributes(request);
  Throwable error = errorAttributes.getError(attributes);
  return convertException(error);
}

こうする

@RequestMapping(value = "/error")
public ErrorResponse error(WebRequest request) {
  Throwable error = errorAttributes.getError(request);
  return convertException(error);
}

テストしてみる

ここまでやるととりあえずビルドが通るようになるので、テストを回してデグレが起きていないか確認する
今回は特に問題なさそう
Warnの解消は後回しにして、とりあえずJDK11対応済みのアプリをDockerに固めてデプロイすることを目指す

Dockerfileを変更する

Dockerfileはベースイメージの変更だけでよい
どのイメージを選択すればいいかはこの記事が参考になる
今回はおすすめの通り、adoptopenjdk/openjdk11:alpine-slim を選択する

FROM adoptopenjdk/openjdk11:alpine-slim

その他

CircleCIの設定を変更する

CI/CDツールを利用している場合はそちらの設定も忘れず変更すること
テスト環境構築時のベースイメージの指定等、忘れがち

SpringBootActuator

SpringBootActuatorで自動生成される全てのエンドポイントに/actuatorのprefixが追加されているので注意が必要
特に /health を利用しているとデプロイがうまくいかなくなる
その他の変更点はこの記事が参考になる

さいごに

以上の修正でとりあえずデプロイできる状態にはなっているはずです
何か問題が起きれば適宜追記していきます
「こっちの書き方の方が正しい」「これだとバグる可能性がある」等あれば是非教えてください

参考

mac上にhomebrewでAdoptOpenJDK11をインストール

Gradle wrapper のバージョン更新についてのメモ

Spring Boot 1.5.10 → Spring Boot 2.0.0 にしたときの覚書

Spring Boot 2.0 Migration Guide

Lombok Project

Java 11 リリース後のオススメ Docker イメージを考える

Spring Boot 2.0のActuator、とりあえず動かすために知っておきたい変更点3つ

Javaエンジニアが Java 11 リリースに向けて備えておくべきこと

ObjectMapper can't deserialize without default constructor after upgrade to Spring Boot 2

Lombok Changelog

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

SpringBoot1.5 + Gradle4.4 + Java8 + Docker 環境を JDK11 に対応させる

はじめに

Java8の無償サポートが2019年1月に切れてから一年ほど放置してましたが、
今回時間ができたので8以降初のLTSであるJava11への更新を行いました
その時のメモです

と書き始めようとしましたが、OpenJDK8は2023年6月までサポートされるらしいので、
OracleJDKからOpenJDKに変えるだけでよかったのでは......
でもやってしまったので記録しときます

JDKのアップデート

AdoptOpenJDK11をインストール

以下のコマンドを入れると最新のJDKがインストールされる

brew cask install adoptopenjdk

しかし、現状ではJDK13が入ってしまうので、LTS版であるJDK11を入れるにはバージョンを指定する必要がある
ということで公開されているバージョンを検索

$ brew search adoptopenjdk
==> Casks
adoptopenjdk                       adoptopenjdk11-openj9-jre          adoptopenjdk12-openj9              adoptopenjdk13-jre                 adoptopenjdk8                      adoptopenjdk8-openj9-large
adoptopenjdk10                     adoptopenjdk11-openj9-jre-large    adoptopenjdk12-openj9-jre          adoptopenjdk13-openj9              adoptopenjdk8-jre                  adoptopenjdk9
adoptopenjdk11                     adoptopenjdk11-openj9-large        adoptopenjdk12-openj9-jre-large    adoptopenjdk13-openj9-jre          adoptopenjdk8-openj9
adoptopenjdk11-jre                 adoptopenjdk12                     adoptopenjdk12-openj9-large        adoptopenjdk13-openj9-jre-large    adoptopenjdk8-openj9-jre
adoptopenjdk11-openj9              adoptopenjdk12-jre                 adoptopenjdk13                     adoptopenjdk13-openj9-large        adoptopenjdk8-openj9-jre-large

今回入れたいのはJDK11なので、以下のコマンドでバージョンを指定してインストール

brew cask install adoptopenjdk11

すでに通っているPATHをJDK11に変更

export JAVA_HOME=`/usr/libexec/java_home -v 11`

バージョンを確認

$ java -version
openjdk version "11.0.5" 2019-10-15
OpenJDK Runtime Environment AdoptOpenJDK (build 11.0.5+10)
OpenJDK 64-Bit Server VM AdoptOpenJDK (build 11.0.5+10, mixed mode)

IntelliJのProjectSDKにJDK11を指定

Command + ; でProjectStructureを開き、ProjectSDKとProjectLunguageLebelにJDK11を指定

Gradleをアップデート

Gradle4.4はJDK11に対応していないため、アップデートする必要がある
5.0以降のバージョンは対応しているようだが、折角なので現在の最新である6.0.1にしてみる

gradle-wrapper.propertiesの更新

gradle/wrapper/gradle-wrapper.properties に記載されているバージョンを更新すればプロジェクトに適用されるGradleのバージョンが変更される
今回は以下の 4.4 の部分を 6.0.1 に変更するだけ

distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-bin.zip

gradlewを更新

それだけだとgradlewに変更が伝播しないので、gradlewを生成し直す

./gradlew wrapper

gradle.buildを更新

各種フレームワーク・ライブラリをJDK11に対応したものに変更していく
基本的には全てgradle.buildに記載されているので、それを修正していけばよい

Javaのバージョン指定を変更

各指定の意味は公式に記載されているので、必要な物を指定する

sourceCompatibility = 11
targetCompatibility = 11

Gradle6系の記載方法に変更

jarの指定方法が変更になっているので更新
詳細は公式を参照のこと

jar {
    baseName = 'example'
    version = '1.0.0-SNAPSHOT'
}

上記だったものを以下に変更

bootJar {
    archiveBaseName = "example"
    archiveVersion = "1.0.0-SNAPSHOT"
}

SpiringBootのバージョンを変更

Java11を利用するにはSpringBoot2.1.x以上が必要
折角なので現在の最新である2.2.2にしてみる
基本的には公式のマイグレーションガイドに従えばいい

buildscript {
    ext {
        springBootVersion = '1.5.13.RELEASE'
    }
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
    }
}

SpringBootが自動で依存関係管理プラグインを適用しなくなったので、プラグインも追加する

apply plugin: 'io.spring.dependency-management'

メインクラスの指定方法が変わっているので、それも変更

springBoot {
    mainClassName = "jp.co.example.Application"
}

ライブラリのバージョンを更新

MavenRepositoryを見ながらライブラリのバージョンを更新していく

SpringBoot周りのライブラリ

SpringBoot周りのライブラリはバージョンを変数で指定しておくと楽
ただし、一部SpringBootのバージョンと異なる場合があるので注意が必要

dependencies {
    compile("org.springframework.boot:spring-boot-starter-web:${springBootVersion}")
    compile("org.springframework.boot:spring-boot-starter-jdbc:${springBootVersion}")
    compile("org.springframework.boot:spring-boot-starter-security:${springBootVersion}")
    compile("org.springframework.boot:spring-boot-starter-actuator:${springBootVersion}")

    testCompile("org.springframework.boot:spring-boot-starter-test:${springBootVersion}")
    testCompile("org.springframework.security:spring-security-test:5.2.1.RELEASE")
}

lombok

lombokを利用している場合注意が必要
依存関係の指定方法が変わっているため、普通にバージョンだけ指定して更新するとビルド時にlombokで自動生成されるメソッドが見つからないとエラーが出る
公式を参考にして、以下の指定を追加する

compileOnly('org.projectlombok:lombok:1.18.10')
annotationProcessor('org.projectlombok:lombok:1.18.10')

その他

よしなに更新する
dependencies 以外で指定しているライブラリについても忘れず更新すること
jacoco の更新を忘れて時間を無駄にしました

廃止になったクラスを修正する

ビルドしてみるとSpringBoot周りで廃止されたクラスが複数出てくるので、それを修正していく
公式のマイグレーションガイドを参考にする

今回はエラーメッセージを制御していた org.springframework.boot.autoconfigure.web 周りのクラスだけが対象だった
以下のメソッドを

@RequestMapping(value = "/error")
public ErrorResponse error(HttpServletRequest request, HttpServletResponse response) {
  ServletRequestAttributes attributes = new ServletRequestAttributes(request);
  Throwable error = errorAttributes.getError(attributes);
  return convertException(error);
}

こうする

@RequestMapping(value = "/error")
public ErrorResponse error(WebRequest request) {
  Throwable error = errorAttributes.getError(request);
  return convertException(error);
}

テストしてみる

ここまでやるととりあえずビルドが通るようになるので、テストを回してデグレが起きていないか確認する
今回は特に問題なさそう
Warnの解消は後回しにして、とりあえずJDK11対応済みのアプリをDockerに固めてデプロイすることを目指す

Dockerfileを変更する

Dockerfileはベースイメージの変更だけでよい
どのイメージを選択すればいいかはこの記事が参考になる
今回はおすすめの通り、adoptopenjdk/openjdk11:alpine-slim を選択する

FROM adoptopenjdk/openjdk11:alpine-slim

その他

CircleCIの設定を変更する

CI/CDツールを利用している場合はそちらの設定も忘れず変更すること
テスト環境構築時のベースイメージの指定等、忘れがち

SpringBootActuator

SpringBootActuatorで自動生成される全てのエンドポイントに/actuatorのprefixが追加されているので注意が必要
特に /health を利用しているとデプロイがうまくいかなくなる
その他の変更点はこの記事が参考になる

さいごに

以上の修正でとりあえずデプロイできる状態にはなっているはずです
「こっちの書き方の方が正しい」「これだとバグる可能性がある」等あれば是非教えてください

参考

mac上にhomebrewでAdoptOpenJDK11をインストール

Gradle wrapper のバージョン更新についてのメモ

Spring Boot 1.5.10 → Spring Boot 2.0.0 にしたときの覚書

Spring Boot 2.0 Migration Guide

Lombok Project

Java 11 リリース後のオススメ Docker イメージを考える

Spring Boot 2.0のActuator、とりあえず動かすために知っておきたい変更点3つ

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Docker コンテナを CGroup V2 なホストLinuxで動かす

Fedora 31 などでCGroup V2 (unified CGroup 階層) がデフォルトになっているので、そういうLinuxホストでpodman を用いて動かす話です。手順としてはUbuntu/Debian向けに書いてあります。fedora 31ならここに書いてあるように 作業すれば(下記のpodman-compose以外は)できるはずです。

Project Atomic のAPTソースの設定

Ubuntuの場合(rootで)

apt-get install software-properties-common
add-apt-repository ppa:projectatomic/ppa

とすると /etc/apt/sources.list.d/projectatomic-ubuntu-ppa-eoan.list が出来るので、それをエディタで開いてリリース名(eoanなど)をdiscobionicに変える。これは上記PPAがbionicとdiscoのパッケージしか用意していないからである。

Debian Bullseyeの場合(rootで)

/etc/apt/sources.listの末尾
deb [ allow-insecure=yes ] http://ppa.launchpad.net/projectatomic/ppa/ubuntu disco main

を追加。

Project Atomicからパッケージインストール (Ubuntu&Debian共通)

apt-get -t disco install buildah podman skopeo slirp4netns containernetworking-plugins (discoは前項のリリース名と同じにすること)

crun のインストール

podman 標準のruncがCGroup V2非対応であるから、CGroup V2対応のcrunをインストールする。

Debian の場合

apt-get -t bullseye install crun

Ubuntu の場合

https://packages.debian.org/testing/main/crun からdebファイルをダウンロードして、dpkg -iまたはgdebiを用いてインストールする。

設定ファイルの変更

  1. https://github.com/projectatomic/registries/blob/master/registries.fedora の内容を /etc/containers/registries.conf にコピーする
  2. /usr/share/containers/libpod.conf の中の runtime = "runc" の等号の右辺を"crun"に変更し保存する

上記の作業で docker ナントカのかわりにpodman なんとか と打てば同じことができる(ただしsudoではなくてrootログインが必要かも知れない)。一般ユーザーでのpodman使用は公式にはできることになっているがうまくいかなかった。

podman-compose

docker-compose に対応するコマンドは上記の作業ではインストールされない。それでは困る場合 pip3 install -U podman-compose を用いてpodman-composeコマンドをインストールする。CGroup V2なUbuntu Eoan上でLaTeXで年賀状を作る に示されたdockerコンテナを podman-composeで動かし年賀状を作成することができた。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

別ドメインのWebページのホストをdockerの力でEC2インスタンス1つに統合したときの話

このエントリは学生LT Advent Calendar 2019 25日目(最後)の記事です。

最後がこんなド初心者でいいのか分からない、誕生日なので許しておくれ

本エントリでは、いろんな辛さからWebページの配信をOS直下のnginxからdockerのnginxとかに移設した話をします。長いかも。

背景

1年以上前にドメインを取得して以降、用途に合わせて以下の2つのドメインにコンテンツを追加しています。

  • huequica.xyz ブログ兼ポートフォリオ
  • works.huequica.xyz 作ったおもちゃを動かす場所

この2つのドメインにコンテンツを配信するのに今までGitHub Pagesと自前で立てたEC2インスタンスに分断して配信を行っていました。
以下の図がわかりやすいかと思います。
1.001.png

さて、これを運用してきて半年以上が経過したところで幾つかのつらい事案を抱えることになりました。

なにがつらい?

以下の事情があり、けっこう悩みのタネになっていました。

GitHub Pages、単純にアップロードがしんどい

Jekyllでの生成、運用をしていた時期がありましたがデザインが気に入らなかったので自分ですべて生成しています。

そのため手元でコンテンツをBuildして、それをリポジトリに突っ込んでpushしてようやく公開される形になっていますが、ファイルパスが大文字小文字を識別していたり?とかで404が帰ってくることがあったりしてつらかったです。

最大の問題は pushする前の事前チェックができない ことで、pushしてはアクセスして確認みたいな地獄みたいなことをしていたので大変つらいです。マジで。

EC2インスタンスの環境が再現しづらい

某PHP製のWebアプリを開発した際、デプロイにVPS的なものが必要だった為とりあえずEC2インスタンスを取得していましたが、環境の不揃いが原因のバグが発生したりした為これもつらさの原因になっていました。
アプリケーションのビジネスロジックの問題ではなく権限エラーが大半のためFixがしづらく余計にしんどかったです。

証明書の更新の自動化

調べるとCronでスクリプトを定期で回して更新させるパターンが多いみたいですが、手元で動かすのに失敗していたままほったらかしになっていた為手動更新をしていました。

おまけに毎回コマンドを忘れているので調べる手間もかかってダルい。これもつらかったです。


といった感じで環境を見直すには十分だろう、ということで以下の構成に変更しました。

おニュー環境

dockerであればMacやWindowsであっても簡単に環境を揃えることができ、また新しくドメインを増やす形でOSSのSaaSサービスを展開することも簡単にできるようになります。そのため今回docker、及び管理を楽にするためにdocker-composeを使いました。

構成は超短的に表すと以下になります。

2.002.png

今回の場合huequica.xyzworks.huequica.xyz の2つのドメインの行き先をEC2の1つに集中させ、URLで識別してdockerコンテナの中のnginxに流しています。

ただし、dockerの中にはHTTPアクセスを識別して仕分ける(リバースプロキシ)みたいな機能は無いのでそっちは別で用意することになります。ここで出てくるのが nginx-proxyです。

nginx-proxy

jwilder/nginx-proxy - GitHub

nginx-proxy はリバースプロキシの仕組みを超簡単に導入するためのdockerイメージです。
ここからは実際のdocker-compose.ymlと図を交えて説明します。

実際のコンテナ構成

だいたいで図にすると以下になります。
h.001.png

まずはHTTPの仕分け役をしているproxyのymlから見ていきます。

proxy/docker-compose.yml
version: "3.5"

services:
  nginx-proxy:
    container_name: nginx-proxy
    image: jwilder/nginx-proxy
    ports:
      - 80:80
      - 443:443
    volumes:
      - /var/run/docker.sock:/tmp/docker.sock:ro
      # nginx-proxy-companionに共有させる
      - certs:/etc/nginx/certs:ro
      - vhost:/etc/nginx/vhost.d
      - html:/usr/share/nginx/html
    networks:
      - proxy-network
    labels:
      - "com.github.jrcs.letsencrypt_nginx_proxy_companion.nginx_proxy"

  # SSL証明書発行しないのであれば不要
  letsencrypt-nginx:
    container_name: letsencrypt-nginx
    image: jrcs/letsencrypt-nginx-proxy-companion
    privileged: true
    depends_on: 
      - nginx-proxy
    volumes:
      - certs:/etc/nginx/certs:rw
      - vhost:/etc/nginx/vhost.d
      - html:/usr/share/nginx/html
      - /var/run/docker.sock:/var/run/docker.sock:ro
    restart: always

volumes:
  certs:
  vhost:
  html:

networks:
  proxy-network:
    name: proxy_network

キモになるのはこの3つです。

  • networks の項目で他のディレクトリのdocker-composeとの連携場所を作る
  • nginx-proxyには上述のネットワークに所属させる
  • SSL証明書を発行する場合は volumes を共有させる(していないと警告が出る)

次にportfolioのymlです。

portfolio/docker-compose.yml
version: "3.5"

services:
  app:
    container_name: portfolio_nginx
    image: nginx
    volumes:
      - ./content:/usr/share/nginx/html
      - ./conf.d:/etc/nginx/conf.d
      - ./nginx.conf:/etc/nginx/nginx.conf
    environment:
      - VIRTUAL_HOST=huequica.xyz
      - LETSENCRYPT_HOST=huequica.xyz
      - LETSENCRYPT_EMAIL=hoge@dogemail.com
    networks:
      - proxy-network

networks:
  proxy-network:
    name: proxy_network

environmentの項目でVIRTUAL_HOSTという見慣れない変数を作っていますが、これが一番大事です。
具体的には、前述のnginx-proxyが拾ったHTTPはVIRTUAL_HOSTのアドレスを元に制御してレスポンスを中継します。そのため、ローカルで稼働させる場合はlocalhostに書き換えるなどしないとなりません。

また、先程のproxy内でSSL証明書用のコンテナを入れている場合はLETSENCRYPT_HOSTLETSENCRYPT_EMAILを追加するとSSL証明書の取得、定期更新を自動で行います。

あとはproxyで設定したnginx-proxyのいるネットワークに、コンテンツ配信用のnginxコンテナを所属させることも必要になります。


さきほどとあんまり変わりませんが、worksの方のymlも掲載しておきます。

works/docker-compose.yml
version: "3.5"

services:
  works_nginx:
    container_name: works_nginx
    image: nginx
    volumes:
      - ./content:/usr/share/nginx/html
      - ./conf.d:/etc/nginx/conf.d
      - ./nginx.conf:/etc/nginx/nginx.conf
    environment:
      - VIRTUAL_HOST=works.huequica.xyz
      - LETSENCRYPT_HOST=works.huequica.xyz
      - LETSENCRYPT_EMAIL=hoge@dogemail.com
    networks:
      - proxy-network

networks:
  proxy-network:
    name: proxy_network

worksもやることは同じです。 VIRTUAL_HOSTLETSENCRYPT_HOSTにドメインを設定して、LETSENCRYPT_EMAILに更新用のメールアドレスを渡してやるだけです。

ただし、PHPやRubyのコンテナと連携して動的なものを返そうと考えている方はそのコンテナとnginxコンテナのみをつなぐ為のネットワークの新設、所属が必要になるのでそれだけ注意してください。

これによって解決したこと

  • 事前のコンテンツチェックができるようになった
  • 証明書が自動更新になり、定期更新など何も気にしなくて良くなった
  • 環境の統一がとれたことにより、デバッグがすこぶる楽になった
  • GitLabなどを自前で動かすこともできるようになった

などなど、夢が広がりまくりです。dockerに感謝

これからの課題

いままでworksで配信していたクソアプリがAPIと静的コンテンツの2分化の必要がありそうな気がしてきているのでそれのコード改修などがあります。なので、Rubyコンテナをworksのdocker-compose.ymlに追加してとかしなければなりません…

最後に

誕生日プレゼント、待ってます。
https://www.amazon.jp/hz/wishlist/ls/3FGFSE5EZKEM3?ref_=wl_share

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Slurm HPCクラスタとKubernetesを同居させてみた(後編)

はじめに

こんにちは、(株)日立製作所 研究開発グループ サービスコンピューティング研究部の小林です。

本日のアドベントカレンダーは、HPCジョブスケジューラSlurmとコンテナオーケストレータKubernetes同居クラスタを構築する投稿の後編です。Singularityベースのワークロードを双方から実行可能な環境を構築しきり、サンプルプログラムを実行してみます。

アドベントカレンダーではHPCジョブスケジューラの一つであるPBS関する記事もあるので興味がある方は要チェックです。

手順

概要

構築手順の概要を示します。本日は手順2以降を紹介します。前回の投稿はこちらです。

  1. Slurmクラスタを構築する
    1. マスタ/ワーカ共通準備
    2. Slurmマスタの構築
    3. Slurmワーカの構築
  2. Singularityを導入する (本日はここから)
    1. Singularityの導入
    2. Singularity-CRI(Sycri)の導入
  3. K8sクラスタを構築する
    1. マスタ/ワーカ共通準備
    2. K8sマスタの構築
    3. K8sワーカの構築
    4. CNIの設定

2. Singularityを導入する(マスタ/ワーカ共通)

ここではコンテナランタイムのSingularityを導入します。コンテナランタイムと一口に言っても、高レイヤと低レイヤの2つがあります。Singularityはコンテナ自体の操作を提供する低レイヤのランタイムを指しますが、ここではKubernetesなどとのコンテナオーケストレータ向けにAPIを提供する高レイヤのランタイムとしてSingularity-CRIも含めます。

2.1. Singularityの導入

基本的には公式のインストールページを参考にします。執筆当時の最新バージョンはv3.4.0です。

執筆当時のGO最新版はv1.14ですが、さしあたり筆者が使い慣れたv1.13をGOの公式を参考にインストールしています。

# 依存するツール・ライブラリのインストール
sudo apt-get install -y \
    build-essential \
    libssl-dev \
    uuid-dev \
    libgpgme11-dev \
    squashfs-tools \
    libseccomp-dev \
    wget \
    pkg-config \
    git \
    cryptsetup-bin
# GO1.13のインストール
sudo add-apt-repository ppa:longsleep/golang-backports && \
sudo apt-get update && \
sudo apt-get install -y golang-go

次にSingularityをインストールします。

git clone https://github.com/sylabs/singularity.git && \
     cd singularity && \
     git checkout v3.4.0
./mconfig && \
    make -C ./builddir && \
    sudo make -C ./builddir install
sudo apt-get install -y socat

2.2. Singularity-CRI(Sycri)の導入

続いてSycri公式のGitHubを参考にインストールします。

git clone https://github.com/sylabs/singularity-cri.git && \
   cd singularity-cri && \
   git checkout tags/v1.0.0-beta.5 -b v1.0.0-beta.5 && \
   make && \
   sudo make install

Sycriサービスはデーモンとして起動させるので公式ページを参考にサービスファイルを/etc/systemd/system/sycri.serviceに追加します。

[Unit]
Description=Singularity-CRI
After=network.target
StartLimitIntervalSec=0

[Service]
Type=simple
Restart=always
RestartSec=1
ExecStart=/usr/local/bin/sycri
Environment="PATH=/usr/local/libexec/singularity/bin:/bin:/sbin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin"

[Install]
WantedBy=multi-user.target

サービスを起動します。

sudo systemctl start sycri && sudo systemctl enable sycri

実行後、sycriサービスが正常起動しているか確認します。

sudo systemctl status sycri

3. K8sクラスタを構築する

最後にKubernetesクラスタを構築します。手順はUbuntuへのK8sインストール記事Step. 3-6を参考にしています。
ただしこの記事ではコンテナランタイムがDockerになっているのでSingularityの公式ページを参考に修正します。

3.1. マスタ/ワーカ共通準備

Kubernetesレポジトリをaptに追加します。

curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo apt-key add
sudo apt-add-repository "deb http://apt.kubernetes.io/ kubernetes-xenial main"
sudo apt-get install -y kubeadm kubelet kubectl

Kubernetesの実行に必要なサーバ設定を施します。
下記のSwapの停止に関してはサーバ再起動後も設定が維持するために/etc/fstabを修正する方法もあります。

# Swapの停止
sudo swapoff -a
# NW周りの設定. lsmodの結果が何も表示されなければmodprobe以下を実行
lsmod | grep br_netfilter
sudo modprobe br_netfilter 
sudo cat /proc/sys/net/bridge/bridge-nf-call-iptables # 1が出力されることを確認
sudo cat /proc/sys/net/bridge/bridge-nf-call-ip6tables # 1が出力されることを確認
sudo sysctl net.ipv4.ip_forward=1

コンテナランタイム変更設定を/var/default/kubeletに追加します。

KUBELET_EXTRA_ARGS=--container-runtime=remote \
 --container-runtime-endpoint=unix:///var/run/singularity.sock \
 --image-service-endpoint=unix:///var/run/singularity.sock

3.2. K8sマスタの構築

Kubernetesマスタサーバ関連のサービスを起動します。執筆者環境ではCRIとPodネットワークのCidrを指定をする必要がありました。また、Sycriサービスが正常に起動していることを確認してください。

sudo kubeadm init --cri-socket unix:///var/run/singularity.sock --pod-network-cidr=10.92.0.0/16

コマンドが正常に終了すればワーカで実行すべきコマンド例が出るのでメモします。

> kubeadm join 192.168.X.Y:6443 --token 111aaa.9999xxxxxxx \
    --discovery-token-ca-cert-hash sha256:XXXXXXXXXXXXXXX

Kubectlを利用するためのコンフィグを整備します。これもkubeadmが正常終了すると出力されます。

mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config

試しにkubectl get svcなどを叩いてエラー以外が帰って来ることを確認します。

3.3. K8sワーカの構築

マスタで得られたkubeadm join hogehogeコマンドに--cri-socket unix:///var/run/singularity.sockを加えて実行します。

kubeadm join 192.168.X.Y:6443 --token 111aaa.9999xxxxxxx \
    --discovery-token-ca-cert-hash sha256:XXXXXXXXXXXXXXX \
    --cri-socket unix:///var/run/singularity.sock

3.4. CNIの設定

K8sではL2 over L3ネットワーク構築のためにCNI(今回はCalico)を導入する必要があります。CNIを導入することで、現状はkubectl get pod --all-namespacesを実行するとpending状態のcorednsも有効になります。

通常はCNI導入はkubectlコマンド一発で入る場合が多いのですが、今回の環境ではCNIが利用するいくつかのパスがインストールに伴い作成されずCNIが立ち上がりませんでした。そこで以下のコマンドを先にマスタワーカの両方で実行しておきます。

sudo mkdir -p /var/lib/cni/networks && \
sudo mkdir -p /ect/cni/net.d && \
sudo mkdir -p /var/lib/calico && \
sudo mkdir -p /run/calico && \
sudo touch /var/lib/calico/nodename

また、マスタとワーカの両方でHostnameの設定も/etc/hostname/etc/hostsでなされているか確認します。
特に/etc/hostsでは以下のようにします。

127.0.0.1    localhost.localdomain localhost
127.0.1.1    YOUR_HOSTNAME # 適宜ホスト名に変更

最後にCalicoをインストールします。

kubectl apply -f https://docs.projectcalico.org/v3.8/manifests/calico.yaml

CalicoCoreDNSコンテナが起動しない場合は、kubectl logs SOME_POD_NAMEkubectl describe pod SOME_POD_NAMEで原因を追求して対応します。コンテナイメージがPullできない場合にはCalicoコンテナやCoreDNSのlogや各ノードのJournalを参照してコンテナ利用するパスの有無や権限を適宜変更します。

以上の手順を実行すると、Slurmコマンドであるsinfoと、kubernetesコマンドであるkubectlが同居した環境を構築できます。

使ってみる

Slurm with Singularity

このサイトを参考にSingularityコンテナをSlurmから実行してみましょう。コマンドはマスタサーバから実行しています。

まずは利用可能なSlurmのパーティションを確認します。

sinfo
> PARTITION AVAIL  TIMELIMIT  NODES  STATE NODELIST
debug*       up   infinite      1   idle koba-slurm2

debugという名前のidle状態のパーティションがワーカノードに割り当てられていることがわかります。次にSlurmのジョブスクリプトsingularity.sbatchを定義します。上の参考サイトのスクリプトについて、パーティション#SBATCH -pと実行するsingularityコマンドを改変しています。

#!/bin/bash
#SBATCH -J singularity_test
#SBATCH -o singularity_test.out
#SBATCH -e singularity_test.err
#SBATCH -p shared
#SBATCH -t 0-00:30
#SBATCH -N 1
#SBATCH -c 1
#SBATCH --mem=4000

# Singularity command line options
singularity pull --name hello-world.sif shub://vsoch/hello-world
singularity exec hello-world.sif cat /etc/os-release

実行してみます。

sbatch singularity.sbatch

結果をNFSなどに出していない場合、ジョブが実行されたノード上に出るのでワーカノードにログインしてホームディレクトリを見るとsingularity_test.outsingularity_test.errがあります。

Slurmによるジョブ制御とSingularityのコンテナ実行が連携できていることがわかります。

Kubernetes with Singularity

K8sについてはSycriユーザドキュメント 7.1.を参考にしてみましょう。
こちらもマスタノードで実行します。

これまでの手順を行うとサーバ上にSycriのGitレポジトリクローンがあると思います。リポジトリのexample/k8s/hello-kubernetes.yamlを利用します。

kubectl apply -f examples/k8s/hello-kuberentes.yaml
kubectl get pod
# > hello-kubernetes-8764bc78f-86t2x   1/1     Running             0          2m10s
# > hello-kubernetes-8764bc78f-nq2pb   1/1     Running             0          2m10s
# > hello-kubernetes-8764bc78f-xjrxx   1/1     Running             0          2m9s

kubectl get svcでサービス公開されたポートを確認し叩いてみるとサービスが提供できていることがわかります。

curl koba-slurm2:SOME_PORT
# > Hello, World!

まとめ

以上の手順でSlurmKubernetesSingularityのワークロード実行可能な環境を構築できました。

現状はこのような実行環境のユースケースは少ないと思いますが、将来的にHPCWireの記事で期待されているような新しいAIワークロードのニーズが生まれるかもしれませんし、
その際には単なる同居ではなくHPCジョブスケジューラとKubernetes双方のジョブスケジューリングを統合する機能が必要になる可能性があります。
SylabsGitHub/WLM Operatorとみなすこともできるかもしれませんね。

今後の関連動向についても引き続き情報発信をしていきたいと思います。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Slurm HPCクラスタとKubernetesを同居させてみた(前編)

はじめに

こんにちは、(株)日立製作所 研究開発グループ サービスコンピューティング研究部の小林です。

DockerやKubernetesに代表されるコンテナ技術がWebアプリ開発を始め様々な場面で使われるようになりました。最近ではHPC向けのコンテナランタイムとしてSingularityが開発され、より一層コンテナ活用の幅が広がりそうですね。

本日のアドベントカレンダーでは、HPCジョブスケジューラの一つであるSlurmクラスタとコンテナオーケストレータのKubernetesクラスタを同居させSingularityベースのワークロードを双方から実行可能な環境を構築してみます。

アドベントカレンダーではHPCジョブスケジューラの一つであるPBS関する記事もあるので興味がある方は要チェックです。

なぜ同居させるのか

データ分析技術の多様化

従来のデータ分析に関するいくつかの主要な動向として、HPCクラスタを用いた大規模バッチ処理や並列同期型のシミュレーションなどが挙げられます。また、新しいデータ分析の潮流として機械学習や深層学習も一般的になりつつあります。
機械学習に関する技術開発は近年一層活発に行われ、開発技術がコンテナイメージとして公開されています。更にはコンテナのスケーラビリティや可搬性/再現性を最大化するコンテナオーケストレータとしてKubernetesが台頭し、コンテナを用いた機械学習のワークフローを管理するKubeflowなどの便利技術なども続々と開発されています。

HPCジョブスケージューラへのコンテナ統合

一方で、従来のHPCクラスタを用いたバッチ/パラレルジョブにコンテナ技術を取り入れる動きがあります。HPCジョブスケジューラの一つであるSlurmではコンテナ化されたジョブの実行をサポートし始めたり、HPCでの実行を想定したコンテナランタイムであるSingularityなども登場したことを受け、HPCジョブスケジューラとコンテナの距離は縮まっています。

HPCジョブとコンテナアプリのワークロード統合

更には、Kubernetes側からも従来のHPCジョブとコンテナベースの処理のワークロード統合を示唆するブログ記事や、Singularity用のKubernetes対応コンテナランタイムインターフェース(CRI)としてSingularity-CRIが開発されています。

特に、Singularity-CRIの登場により、HPCクラスタの多量のリソースの以下のような柔軟な活用が可能になります。

  • 従来のHPCジョブ実行
  • 実験作業の再現性確保が容易なコンテナベースのHPCジョブ実行
  • GPUなどの潤沢なリソースを投入した高スケールなコンテナベースのデータ分析

構成

SlurmとKubernetes(K8s)の同居クラスタを作ります。UbuntuベースのマスタサーバにはSlurmとK8sのマスタとして役割を、ワーカサーバには同じくSlurmとK8sのワーカとしての役割を実行させます。

また、K8sのコンテナランタイムには、一般的なDocker(Containerd)ではなくSingularityを使います。

fig1.png

手順

概要

手順の概要を示します。マスタサーバとワーカサーバではデーモンや依存ソフトで共通するものも多いので、作業が分岐する場合は適宜言及します。

  1. Slurmクラスタを構築する
    1. マスタ/ワーカ共通準備
    2. Slurmマスタの構築
    3. Slurmワーカの構築 (本日はここまで)
  2. Singularityを導入する(マスタ/ワーカ共通)
    1. Singularityの導入
    2. Singularity-CRI(Sycri)の導入
  3. K8sクラスタを構築する
    1. マスタ/ワーカ共通準備
    2. K8sマスタの構築
    3. K8sワーカの構築
    4. CNIの設定

1. Slurmクラスタを構築する

1.1. マスタ/ワーカ共通準備

Slurmサーバの構築方法は公式のQuickStartを参照します。
依存ツールのライブラリが足りないエラーが起きたのでこちらのサイトも参考にしています。

まずは認証用のMUNGEをインストールします。

sudo apt update
sudo apt install -y munge libmunge-dev libmunge2
sudo systemctl start munge && sudo systemctl enable munge

MUNGEはデーモンを起動すると認証用の秘密鍵を/etc/munge/munge.keyに作成します。この秘密鍵はSlurmクラスタのマスタ・ワーカで共通のものを使うので1.3.にてマスタの鍵をコピーします。また、MUNGE/var/log/munge以下にログを出力しますが、フォルダ所有者がユーザMUNGEでない場合起動に失敗する場合があります。

最新版のSlurmを入手してインストールします。執筆時点での最新版はslurm-19.05.4です。
インストール前に、依存するライブラリ等を導入していきます。

sudo apt install -y build-essential python

続いてSlurmをソースコードからビルドします。ビルド後にMUNGEのライブラリに参照できないエラーが出たのでconfigureのフラグを追加しました。

wget https://download.schedmd.com/slurm/slurm-19.05.4.tar.bz2
tar --bzip -x -f slurm*tar.bz2
cd slurm-19.05.4
sudo ./configure --with-munge="/usr/bin/munge"
sudo make && sudo make install

Slurm関連のサービスファイルを/etc/systemd/system/以下にコピーします。

sudo cp /home/ubuntu/slurm-19.05.4/etc/*.service /etc/systemd/system/

また、公式のSuper Quick Start7 NOTEにあるように、Slurm用のユーザを作成します。

sudo adduser slurm

Slurmユーザは、/var/run/spool, /var/log/syslogへの参照や書込権限が必要なのでグループなりユーザに権限を付与します。環境によってポリシーが異なると思いますが、rootグループなどにslurmユーザを加えて適宜必要なフォルダにグループ権限を与えるなどの対応があると思います。

1.2. Slurmマスタの構築

Slurmマスタサーバを構築します。構築前にMUNGEサービスが正常に動いていることをsystemctl status mungeなどで確認しておきましょう。
マスタサーバ構築では、Slurmクラスタのコンフィグ作成が主な作業になります。コンフィグの作成はSlurmが提供するWebGUIに必要事項を入力して出力されるファイルを/usr/local/etc/slurm.confに配置します。

私はUbuntuサーバ版を入れてしまったので、他のPCからブラウザアクセスして作成することにしました。設定のためだけにapache2を入れてhttp://HOSTNAME:80/html2/configurator.easy.htmlにアクセスします。

sudo apt install -y apache2
sudo ln -s /home/ubuntu/slurm-19.05.4/doc/html /var/www/html/html2

サーバの設定に必要なリソース情報は下記のコマンドで適宜取得します。

# CPU関連
lscpu | grep -E '^Thread|^Core|^Socket|^CPU\('
> CPU(s):              2
> Thread(s) per core:  1
> Core(s) per socket:  1
> Socket(s):           2
# メインメモリ関連
free -m
>             total        used        free      shared  buff/cache   available
> Mem:           7976         102        5514           0        2359        7570
> Swap:             0           0           0

ひとしき入力し終えたらsubmitボタンを押して得られる情報を/usr/local/etc/slurm.confとして出力します。
マスタサーバではslurmctldをサービスとて起動します。

sudo systemctl start slurmctld && sudo systemctl enable slurmctld

起動後にCan't open PID file /var/run/slurmctld.pid (yet?) after start: No such file or directoryなどのエラーが出る場合がありますが、適宜ユーザやフォルダの権限を見直します。

1.3. Slurmワーカの構築

ワーカで実施するべき作業は大きく2つです。

  • MUNGEの秘密鍵をマスタサーバから入手し利用
  • Slurmのコンフィグをマスタサーバから入手し利用

作業がうまく行かない場合は、マスタサーバとワーカサーバのMUNGEサービスやslurm関連サービスがキチンとActiveになっているか適宜確認します。

MUNGEインストール後、マスタサーバのmunge.keyをコピーしてMUNGEサービスを再起動します。

sudo scp SOME_USER@SLURM_MASTER:/etc/munge/munge.key SONE_USER@SLURM_WORKER:/ect/munge/munge.key
sudo systemctl restart munge

エラー無くサービスが起動できたことを確認します。

Slurmをインストール(サービスファイルのコピー含め)したのち、Slurmコンフィグをコピーして利用します。

sudo scp SOME_USER@SLURM_MASTER:/usr/local/etc/slurm.conf SONE_USER@SLURM_WORKER:/usr/local/etc/slurm.conf
sudo chown root:root /usr/local/etc/slurm.conf
sudo start slurmd && sudo systemctl enable slurmd

ここまで完了し、SlurmマスタサーバでMUNGESlurmCtldが、ワーカサーバでMUNGESlurmdが正常稼働していること確認してからマスタサーバでsinfoを実行します。AVAILupならSlurmクラスタの起動完了です。

sinfo
> PARTITION AVAIL  TIMELIMIT  NODES  STATE NODELIST
> debug*       up   infinite      1   idle test-slurm2

前編のおわりに

本日の投稿ではSlurmKubernetesの同居クラスタ構築に関して、システム構成とSlurmクラスタの構築手順を紹介しました。後編の投稿ではSingularityとSingularity-CRIを利用して稼働するKubernetesクラスタの構築手順とクラスタのサンプル実行を紹介したいと思います。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

SAP CP(トライアル) の CF で Docker を使ってみようとしたら、うまく行かなかった話

はじめに

この記事は SAP Advent Calendar 2019 の12月19日分の記事として執筆しています

こんにちわ、株式会社KYOSOでエンジニアをしている上田といいます。
よろしくおねがいします。

本記事は、Cloud Foundry は、 Docker Image に対応していると聞いて
SAP Cloud Platform へデプロイを試みて失敗したときのトラブルシュートです。

デプロイが出来ない理由は、トライアル環境で試したから! と推測するまでの問題点と解決策をまとめました。

トライアル以外の環境での実施、またはトライアル環境であっても時期次第で成功する可能性があることをご理解ください。
「いやいや、自分はトライアル環境でも成功したよ!」と言った成功体験等ございましたら
お話等伺えましたら嬉しく思います。

環境

  • SAP Cloud Platform( trial ): Cloud Foundry
  • PC: Macbook Pro(2017)
  • CFコマンド: cf version 6.35.2+88a03e995.2018-03-15

問題1

cfコマンドの接続先(API エンドポイント)が間違っている

自分の場合、会社のCloudFoundry環境 を利用していたこともあり
cfコマンドは、トライアルとは別のAPIエンドポイントに接続している状態でした。

解決策

以下のコマンドで APIエンドポイントの変更すれば解決

cf login -a <APIエンドポイントのURL>

または

cf api <APIエンドポイントのURL>
cf login

APIエンドポイントのURLは、SAP Cloud Platform Cockpit から確認出来ます。
スクリーンショット 2019-12-19 09.29.15.png

問題2

cf コマンドを実行した際のエラー内容が不明

解決策

CF_TRACE=trueを環境変数に設定して、詳細なログが見えるようにする。

環境変数CF_TRACE の値を true にすると、
CloudFoundry を操作するためのAPI呼び出しが見えるようになるので
どのステップでエラーが発生しているのか、コンソール上に表示されます。

問題3

SAP CP で Docker Image のデプロイが出来ない

解決策

以下のコマンドで、Diego( Cloud Foundry のコンテナ管理システム )を有効にする。

cf enable-feature-flag diego_docker

トライアル環境では、初期状態として Diego が無効な状態のようで、明示的に有効化する必要があるようです。
※このコマンド実行時に権限不足で実行出来ないことが発覚、 トライアル環境では Docker Image の利用は出来ないと判断しました。。。

エラー内容

image.png

最後に

流石にトライアル環境で Docker 利用は考えが甘かったかなと反省しつつ
自分が遊ぶ環境を会社に買ってもらえないかチラ見する所存です。

参考URL

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Dockerの立ち上げ、まだコピペでやってるの?ちゃんと理解したくない?

はじめに

詳しい人には気になる表現があるかもしれませんが、どうか気にしないでください:runner_tone3:

この記事は、Dockerで開発環境を構築したいけど、 いつもコマンドコピペでやっていて実はよくわかってない!! という方に向けて、細かーいところをふんわりと解説したいというモチベーションで書いています。

ここでは、サンプルとしてNode.jsの環境を取り上げます。Node.jsは、JavaScriptのフレームワークですね!Vue.jsの開発などでも大活躍のフレームワークです!

Dockerを使うモチベーション

世の中には便利な開発環境がたくさんありますね。Node.jsもそのひとつです。この賢い人をインストールしてあげるだけで、簡単便利な開発環境ができあがります。

では、この席をどこに用意するといいのでしょうか?

ご自身の PC に環境を整えて上げてもいいのですが、この手のフレームワークはいろいろな部品群との連携がとても大切になっており、またそれらのバージョンを揃えてあげることが大切です。そんなときには、 Docker でご自身のPCを汚さず、 Node.js ちょっと飽きたなー、違ったなー、というときに、いつでも綺麗サッパリできるようにしましょう、

Docker 環境を作る

Docker 自体のの詳しい説明はここでは避けます。ご自身の PC を綺麗に保つために、いつでも綺麗サッパリできるような受け皿を、 PC 上に構築してあげる仕組みです。 Docker を使う上では、イメージとコンテナを知っておく必要があります。ちょっと調べてみたところ、

  • イメージ : 開発環境の土台となる情報が詰まったもの
  • コンテナ : イメージから作った開発環境

という感じの書き方がされたサイトがいくつか見つかりました。
個人的に、よくわからなかったので、私なりの解釈をしてみました。こんな感じです。

  • イメージ : OSのインストールDVD、工場出荷時に戻すときに使うDVD
  • コンテナ : 空のパソコン買ってきて、DVDでOSをインストールした状態、または買ってきてすぐのパソコン

Dockerfile を用意する

Dockerfile は、Docker イメージをどう定義するか、そのイメージをコンテナにするときについでにやっておいてほしい設定、などを書いておき、上から順に全部ガーーーッとやっちゃってくださいという、台本みたいなもの。パソコン屋さんにカスタマイズをお願いする注文書みたいな感じでしょうか。

この OS 入れて〜
このソフト入れて〜
ネットワークの設定はこんな感じで〜
スタートアップの設定はこれで〜

という感じのことを書きます。

Dockerfile の書き方

ベースのイメージを決めよう

Dockerfileは基本文法として
命令 引数
のペアで書いていきます。ツラツラと書き下し、上から順に実行するというものです。
まずベースとする Docker Image を決めます。
まず、Node.jsは、それを何に使うのかによって、どのバージョンを使うのか、考えた方が良いです。例えばVue.jsの開発用として使いたいのであれば、Vue.jsのサイトで、推奨バージョンが確認できると思います。

とりあえずなんか入れてみよう、ということで、ここではVue.jsを対象として書いてみます。バージョンは適宜必要なものに変えてもらうと良いと思います。
Dockerfile にまず次の一行を書きましょう。

FROM node:8.16.1-alpine

これは node を DockerHub で探してきて、 8.16.1-alpine というタグがついているイメージをプルしてきてください。という注文です。

ここまでで新出単語が3つほどありましたね。ひとつづつ確認します。

  • node.js

    • JavaScript の実行環境です。 JavaScript はブラウザが解釈してほげほげするというのが昔からのやり方で、ブラウザによって解釈の仕方が違うから困った困ったという歴史があり、そこから ECMAScript という共通化の動きが云々カンヌン…。そうやってブラウザ側は共通化できて JavaScript が定着してきたけど、サーバーサイドは相変わらず Java とか PHP とか覚えないといけないのか…。という悲しい人を増やさないよう、「あなたの使える JavaScript をサーバーでも使えるようにしてあげるよ」という優しい仕組みが Node.js です。 Node.js はクライアントサイドでもサーバーサイドでも共通して JavaScript を使えるようにしてくれるので、 Node.js をベースとしたフレームワークは数多くあります。
  • DockerHub

    • Docker イメージを公開しているサービスです。いろいろな開発環境をサクッと用意できるよう、ある程度出来上がったイメージがたくさん用意されています。 Docker 公式のサービスなので、 Dockerflie からサクッと取得できます。
  • alpine

    • 軽量 Linux ディストリビューションのひとつです。世の中にはたくさんの Linux があります。そんな中、これまではあまり注目されていなかったのですが(たぶん)、 Docker の普及とともに、コンテナ単位のサイズを節約できる 軽量 Linux ディストリビューションが日の目を見ました。その代表格が alpine です。

セットアップしよう

ここからは alpine Linux が動作している PC を CUI で使っているんだ・・・・!!という想像をしながら書いていきます。 CUI はいわゆる黒いアイツです。

まず、今後の作業をするディレクトリに入ります。

WORKDIR /app

WORKDIR は、いま居るディレクトリを変更してね、という命令です。
/app ディレクトリに入ったら、 Vue-cli をインストールします。

ここでは、Vue.jsの環境を例に挙げているので、後でDockerを動かすサンプル用に、Vue-Cliを入れます。

RUN apk update &&\
    npm install -g npm vue-cli

RUN は以降のコマンドを CUI で実行してね、という命令です。

  • apk
    • alpine Linux で採用されているパッケージマネージャーです。パッケージマネージャーは、有名ドコロのツールたちを管理してくれる便利なお方です。整理整頓をしてくれるお優しいお方です。
  • npm
    • Node.js 関連のパッケージマネージャーです。こちらも整理整頓をしてくれるお方です。 Node.js 関連が専門です。

つまり、

apk update

でパッケージマネージャー apk を最新の状態にして、

npm install -g npm vue-cli

で vue-cli をインストールします。
-g はグローバル指定のことですが、 Node.js のプロジェクトの説明をしないといけないので、一旦気にせず行きましょう。ちなみにこの npm ですが、 node.js のイメージを選んだ時点で最初から入っているのですぐに使うことができるのです。突然出てきたと、びっくりしないで大丈夫です。

ポートの開放

今回例に挙げている Vue.js は Web アプリの開発環境です。つまり、 Web サーバーとして動作し、ブラウザからアクセスできるような感じにしていきます。そのため、 Docker コンテナで動いている alpine Linux と、ご自身の PC とをつないであげる必要があります。それぞれは別のマシンとして動いているので、お互いが情報をやり取りできるようにして上げる必要があります。こういう場合、ポートを開放してあげることで Vue.js で作った Webアプリ をご自身の PC のブラウザからチェックすることができるようになります。Node.jsはWeb系で使われることが多いので、多くのケースでこの設定は必要なのではないでしょうか。

このポート番号は何番でもいいのですが、とりあえず9000番を開けておきます。

dockerfile
EXPOSE 9000

Dockerfile完成

これまでのことをまとめると、次のDockerfileが完成します。

dockerfile
FROM node:8.16.1-alpine
WORKDIR /app
RUN apk update &&\
    npm install -g npm vue-cli
EXPOSE 9000

しかし、このままではコンテナは出来上がっても何もできません。起動しても何もプログラムが動いていない状態です。つまり、何もできません。そこで、コンテナ側でシェルを用意しておき、あとからそこにつないで上げることで、手元の PC からコンテナを操作することができます。
これは次の一行で OK です。

CMD ["/bin/sh"]

あらためてまとめると

dockerfile
FROM node:8.16.1-alpine
WORKDIR /app
RUN apk update &&\
    npm install -g npm vue-cli
EXPOSE 9000
CMD ["/bin/sh"]

となります。これを適当なエディタで記入し、 Dockerfile という名前で保存しておきます。

Dockerfile をビルドしてローカルに Docker イメージを作る

Dockerfile はカスタマイズ用の注文書と言いました。注文書ができあがったので、注文書どおりにセットアップDVDをつくってあげましょう。これを入れたら工場出荷時にもどるよ、というようなDVDです。

$ docker build -t vue_app_image .

ここで急に docker というコマンドが登場しました。そうです、 Docker を動かすには、 Docker をインストールする必要があるのです(当たり前)。Docker のインストールはいろいろな先人の知恵を借りてなんとか攻略してください。 Docker for Mac とか Docker for Windows とかいろいろ便利になっているので、簡単にできるはずです。

では、コマンドの説明をします。

  • dockerは Docker を使うぜ、というだけです。
  • build は Dockerfile に従って作業するぜ、というコマンドです。
  • -t は タグ付けするぜ、という意味です。 --tag です。
  • vue_app_image は完成したイメージのタグの名前です。自由に決めることができます。
  • . は打ち間違えではありません。いまいるディレクトリにある Dockerfile を使うよ、という意味です。つまり、これまでに用意した Dockerfile がおいてある場所でこのコマンドを実行する必要があります。(パスを入れればどこからでもできますが、この方が簡単です。

無事に成功すると、 Docker イメージが出来上がります。イメージが出来ているかどうかは、次のコマンドで確認することができます。

$ docker image -ls

Docker イメージを使って、 Docker コンテナを作る

Docker イメージというセットアップ DVD みたいなものができたので、 パソコンを用意してインストールしてあげましょう。これが Docker コンテナをつくるということです。パソコンをいっぱい買えば(コマンドを何回も実行すれば)環境はいっぱい作れます。

$ docker run -v `pwd`:/app -p 9000:9000 --name vue_app -it -d vue_app_image

またコマンドの解説をします。

  • docker は Docker を使うぜ、ということです。(もういい)
  • run はコンテナを作るぜ、ということです。
  • -v は volume の意味です。ご自身の PC の特定のディレクトリと、コンテナ内の特定のディレクトリを紐付けることで、ファイルの編集をどちらでもできるようにします。
  • pwd は Unix 系 OS でよく使われるコマンドです。今いるディレクトリのパスを取得することができます。 Print Working Directory という意味です。
  • : これは -v オプションと連携しており、 : の前に指定したディレクトリをご自身の PC 側、 : の後ろに指定したディレクトリをコンテナ側として認識し、それらを紐付けますという意味です。
  • /app はコンテナ側のディレクトリです。このディレクトリと現在のディレクトリ (pwd) が紐付けられます。
  • -p はポート番号を紐付けるよ、という意味です。
  • 9000:9000 はご自身の PC 側とコンテナ側のポート番号の紐付け指定です。
  • --name はコンテナの名前を指定するよ、というオプションです。
  • vue_app はコンテナの名前です。なんでも OK です。
  • -it はインタラクティブモードで起動するよ、という意味です。 Dockerfile の最後に記述したシェルにつなぐということです。
  • -d はコンテナをバックグラウンドで起動するよ、という意味です。デタッチの意味です。
  • vue_app_image はコンテナのもととなるイメージのタグ名です。

ここでボリュームの紐付けについて触れておきます。
Docker コンテナはあくまでご自身の PC 上で動作する別の環境です。したがって、その環境内で操作したいファイルは通常、そのコンテナ内で作る必要があります。しかし、 Web アプリケーションを作るために書いたたくさんのHTMLやJavaScriptのファイルをコンテナ内に残しておくと、コンテナを誤って消してしまったときなどに枕を濡らすことになります。コンテナ側で GitHub にプッシュしてもいいですが、そういうファイルの管理はご自身の PC 側でやったほうがなにかと便利ですよね。そこで、手元の特定のディレクトリと、コンテナ内の特定のディレクトリを紐付け、ファイルのやり取りを簡単に行えるようにしましょう。

出来上がったコンテナは次のコマンドで確認することができます。

$ docker ps -a

Docker コンテナに入って作業を開始しよう

では早速用意した Docker コンテナを起動して接続し、作業を開始しましょう。
次のコマンドでこれが実現します。

docker exec -it vue_app sh

だいたいこれまでと同じ感じですね。

  • docker は Docker を使うz(ry
  • exec はコンテナを実行するぜ、という意味
  • -it はインタラクティブモードで起動するぜという意味
  • vue_app は起動させるコンテナの名前
  • sh はコンテナのシェルを使うぜ、という意味

これで、コンテナ側のシェルが操作できる状態になっていると思います。 ls コマンドなどで、さきほど紐付けたディレクトリに入って、ローカルと連携しているかどうか、適当なファイルを作って確かめてみてください。

ここまでで、実はDockerの用意は完了です。
ここからはVue.jsを使ったサンプルを動かしてみようと思います。

Vue-cli を使って Vue のプロジェクトを立てよう

Vue-cli とこれまで何も説明もなく登場させていました。すいません。これは、 cli (command line interface) のことで、 Vue に必要ないろいろなことを簡単にできるようにしてくれる親切なツールです。
https://cli.vuejs.org/

ここでは vue-cli3 は使いません。 (特に理由はありません) cli3 を使いたい場合は vue-cli のインストールを

npm install -g @vue/cli

としてもらえば OK です。

ここでは Vue-cli で用意されているテンプレートから webpack を使ってみます。
作業ディレクトリ (/app) に移動し、以下のコマンドを入力します。

/app:$ vue init webpack
/app:$ npm install

webpack のプロジェクトを新規作成しますよ、 ということです。
これで webpack のテンプレートに入っている初期のファイルがザバーっと自動的に生成されると思います。この辺りの説明は省きます。

次に、 config/index.js を編集します。これは、 vue-cli で HTTP アクセスさせるときに開放しているポートを使わないと、手元の PC からは見えないからです。

host: 'localhost',
port: 8080,

host: '0.0.0.0',
port: 9000,

に変更してください、 Dockerfile に指定して開放したポート番号です。また、 localhost も変更しています。これは、まずはこんなもんだと思っておいてください。

次に、 node 環境の実行です。

/app:$ npm run dev

これですべて完了です。
http://localhost:9000 にアクセスして webpack が動いていることを確認してください。

これで、Dockerのコマンドたちが何をしているのか、少しクリアになったかと思います。何かのお役に立てそうなら、ぜひストックを。今後も少しずつ内容はブラッシュアップします。

長々とお付き合いいただき、ありがとうございました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

MySQL8.0で全文検索(mecab)を試してみる。

この記事はディップ Advent Calendar 2019の19日目の記事です。

まえがき

DockerでMySQL8.0の全文検索機能をお試しする環境をつくってみます。
そもそも全文検索って何なのさ、というのは以下の記事がとても参考になります。
検索エンジンはいかにして動くのか?

なお、コンテナ作成後のSQLは、ホストからMySQL Workbenchで接続・実行しています。
※dockerコンテナの中からmysql clientで接続して、最後の全文検索用SQLを投げようとしたところ、日本語を入力した途端に文字が消えるという事象に遭遇し、断念。。

構築手順

フォルダ構成
.
├── docker   
│   └── mysql8.0-mecab
│       ├── Dockerfile
│       ├── conf.d
│       │   └── my.cnf
│       └── initdb.d
│           └── init.sql
├── docker-compose.yml

上のようなフォルダ構成で、下記のファイルを用意していきます。

docker-compose.yml

my.cnfはローカルからコンテナの/etc/mysql/conf.dにマウントさせてます。
また、/docker-entrypoint-initdb.dにマウントされたシェルスクリプトやSQLが初回起動時に実行されます。
今回はmecabプラグインのインストールや検証用テーブル作成用のSQLを配置しました(後述)。

docker-compose.yml
version: '3.3'
services:
  mysql80-mecab:
    build: ./docker/mysql8.0-mecab
    container_name: mysql8.0-mecab
    environment:
      MYSQL_DATABASE: sample_db
      MYSQL_USER: jabe
      MYSQL_PASSWORD: jabe123
      MYSQL_ROOT_PASSWORD: jabe123
      TZ: 'Asia/Tokyo'
    ports:
      - "3306:3306"
    volumes:
      - ./docker/mysql8.0-mecab/conf.d:/etc/mysql/conf.d
      - ./docker/mysql8.0-mecab/initdb.d:/docker-entrypoint-initdb.d

Dockerfile

mecabのインストールをします。また、echo "dicdir=/var/lib/mecab/dic/ipadic-utf8" > /etc/mecabrc の部分でmecabの単語辞書をIPA辞書に切り替えています。

Dockerfile
FROM mysql:8.0
RUN apt-get update && apt-get install -y        \
    mecab                                       \
    libmecab-dev                                \
    mecab-ipadic-utf8                           \
    locales                                     \
&&  locale-gen ja_JP.UTF-8                      \
&&  echo "dicdir=/var/lib/mecab/dic/ipadic-utf8" > /etc/mecabrc
ENV LANG        ja_JP.UTF-8
ENV LC_CTYPE    ja_JP.UTF-8

my.cnf

MySQLにmecabrc(mecabの設定ファイル。前述のDockerfileで単語辞書のパスを記載しました。)のパスを通します。
また、mecabがトークン解析をする際のトークンの分割単位を決めます。

my.cnf
[mysql]
default-character-set=utf8mb4
[client]
default-character-set=utf8mb4
[mysqld]
# mecabrcファイルの格納先
loose-mecab-rc-file=/etc/mecabrc
# mecabが解析時にトークン分割する際のトークンサイズを指定
innodb_ft_min_token_size=1
# デフォルト認証プラグインの設定
default-authentication-plugin = mysql_native_password

collation-server=utf8mb4_unicode_ci
character-set-server=utf8mb4
skip-character-set-client-handshake

初期化用SQL

前述のコンテナ初回起動時に実行させるSQLです。ここでは以下をやっています。

  1. mecabプラグインのインストール
  2. テーブル作成&初期データ投入

特に、fulltext (street_address) with parser mecab で全文検索用のindexを作成します。

init.sql
use sample_db;
/*** 1. mecabプラグインのインストール ***/
install plugin mecab soname 'libpluginmecab.so';

/*** 2. テーブル初期化 ***/
drop table if exists employee;
create table employee
(
        id                          int unsigned auto_increment not null primary key
    ,   name                        varchar(40)
    ,   street_address              varchar(90)
    ,   fulltext (street_address)   with parser mecab
) engine=innodb character set utf8mb4;

insert into employee (id, name, street_address) values (1, "太郎","東京都新宿区");
insert into employee (id, name, street_address) values (2, "次郎","京都府京都市北区");
insert into employee (id, name, street_address) values (3, "三郎","愛媛県松山市");
insert into employee (id, name, street_address) values (4, "四郎","香川県高松市");
insert into employee (id, name, street_address) values (5, "五郎","徳島県徳島市");
insert into employee (id, name, street_address) values (6, "花子","高知県高知市");

コンテナ起動&確認

docker-composeコマンドでコンテナの起動、確認します。

# 起動
$ docker-compose up -d
# 起動確認
$ docker-compose ps 
# ログチェック
$ docker-compose logs

DB確認

コンテナ起動後、MySQLにログインし、以下が確認できればOKです!

  1. mecabプラグインがあること。
  2. テーブル作成&初期データ登録ができてること。
MySQLの状態確認。
Execute:
> show create table employee

+ ---------- + ----------------- +
| Table      | Create Table      |
+ ---------- + ----------------- +
| employee   | CREATE TABLE `employee` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(40) DEFAULT NULL,
  `street_address` varchar(90) DEFAULT NULL,
  PRIMARY KEY (`id`),
  FULLTEXT KEY `street_address` (`street_address`) /*!50100 WITH PARSER `mecab` */ 
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci |
+ ---------- + ----------------- +
1 rows

Execute:
> select * from employee

+ ------- + --------- + ------------------- +
| id      | name      | street_address      |
+ ------- + --------- + ------------------- +
| 1       | 太郎    | 東京都新宿区            |
| 2       | 次郎    | 京都府京都市北区         |
| 3       | 三郎    | 愛媛県松山市            |
| 4       | 四郎    | 香川県高松市            |
| 5       | 五郎    | 徳島県徳島市            |
| 6       | 花子    | 高知県高知市            |
+ ------- + --------- + ------------------- +
7 rows

いざ、全文検索。

「京都」を検索して「東京都」がでなければ、できあがり。

Execute:
> select * from employee
where match (street_address)
against ('京都' in natural language mode)

+ ------- + --------- + ------------------- +
| id      | name      | street_address      |
+ ------- + --------- + ------------------- +
| 2       | 次郎    | 京都府京都市北区        |
+ ------- + --------- + ------------------- +
2 rows

あとがき

最後までお付き合い頂きありがとうございました。
不備、おかしなところあれば、ご指摘頂けますと幸いです。
また、まえがきで遭遇した事象について有力情報お待ちしておりますm(_ _)m

参考記事

以下を参考にさせていただきました。感謝です。
https://dev.mysql.com/doc/refman/8.0/en/fulltext-search-mecab.html
https://qiita.com/clustfe/items/3f5160ce77c5db9c7b30
https://qiita.com/ucan-lab/items/b094dbfc12ac1cbee8cb
https://qiita.com/motoki_giants/items/0c3b9d174edef9410310
https://qiita.com/furu8ma/items/75e5b1df29fef04ec7f1

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWS入門、ついでにDocker入門

目次

1.概要
2.Dockerとは
3.動かしてみよう
4.管理してみよう


1.概要

・AWS/Docker初心者が
 AWSを使ってDockerを動かしてみました。

・AWSを使って様々な製品、技術に触れるハードルが下がればと思います。


2.Dockerとは

コンテナ型の仮想化環境を
作成、配布、実行するためのプラットフォーム

ホストOS上で仮想化するので
○起動が高速、環境構築が簡単・・・
△ホストOSと同一のOSしか使えない・・・
Docker比較.png

Dockerの起動イメージ

Dockerイメージ.png


3.Dockerを動かしてみよう

(1)AWS EC2の立ち上げ
(2)Dockerをインストール
(3)DockerイメージからDockerコンテナを動かす

動作イメージ.png

(1)AWS EC2の立ち上げ
EC2コンソール上からインスタンスを立ち上げる。
(今回はAmazon Linux 2)

(2)Dockerをインストール
AWS提供リポジトリからDockerが取得できる。
(数秒で完了)

docker1.log
$ sudo yum install docker
読み込んだプラグイン:extras_suggestions, langpacks, priorities, update-motd
~中略~
インストール:docker.x86_64 0:18.09.9ce-2.amzn2
依存性関連をインストールしました:
  containerd.x86_64 0:1.2.6-1.amzn2              
  libcgroup.x86_64 0:0.41-21.amzn2   
  pigz.x86_64 0:2.3.4-1.amzn2.0.1                
  runc.x86_64 0:1.0.0-0.1.20190510.git2b18fe1.amzn2
完了しました!

(3)DockerイメージからDockerコンテナを動かしてみる
「Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world」
→ローカルにhello-worldイメージが存在しないため、
 DockerHubのライブラリから取得。

docker2.log
$sudo docker run hello-world
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
1b930d010525: Pull complete 
Digest: sha256:4fe721ccc2e8dc7362278a29dc660d833570ec2682f4e4194f4ee23e415e1064
Status: Downloaded newer image for hello-world:latest

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

4.Dockerを管理してみよう

(1)DockerHub
(2)Amazon ECS/ECR

(1)DockerHub
Dockerイメージを公開・共有できるサービス
DockerHub.png

(2)Amazon ECS/ECR
ECS:Amazon Elastic Container Service
Dockerコンテナの管理サービス。

ECS1.png
ECS2.png
ECS3.png
ECS$.png

ECR:Amazon Elastic Container Registry
Dockerイメージの管理サービス。
スクリーンショット 2019-12-19 3.05.59.png

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

DockerのMaxscaleでホストにマウントしたログをログローテーション (2)

cronのDockerコンテナからcronを動かして別のDockerコンテナのlogroate実行

動作確認環境

AWS Workspaces
Amazon Linux2 (Time zone: Asia/Tokyo (JST, +0900))
Maxscale 2.4.4

ディレクトリ

home/username/logrotate
|--log
|  |--maxscale
|  |  |--maxscale.log
|--maxscale
|  |--maxscale_logrotate
|--maxscale-dockerfiles
|  |--Dockerfile
|--cron-logrotate-docker-dockerfiles
|  |--Dockerfile
|  |--maxscale-cron
FROM mariadb/maxscale:2.4.4

RUN apt-get update && apt-get -y install logrotate

#RUN mv /etc/cron.daily/logrotate /etc/cron.hourly/logrotate
#ADD maxscale-cron /etc/cron.d/maxscale-cron
#RUN chmod 0644 /etc/cron.d/maxscale-cron
maxscale/maxscale_logrotate
/var/log/maxscale/maxscale.log {
  su maxscale maxscale
  create 644 maxscale maxscale
  rotate 3
  missingok
  compress
  delaycompress
  sharedscripts
  size 1K
  dateext
  dateformat %Y%m%d%H%M%S
  postrotate
    kill -USR1 `cat /var/run/maxscale/maxscale.pid`
  endscript
}
cron-logrotate-docker-dockerfiles/Dockerfile
FROM alpine:3.10.3

RUN apk --update add docker && rm -rf /var/cache/apk/*

COPY maxscale-cron /etc/crontabs/root

# cronのdaemonを起動(log level=1, foreground)
CMD crond -l 1 -f
cron-logrotate-docker-dockerfiles/maxscale-cron
*/1 * * * * docker exec maxscale /usr/sbin/logrotate -f /etc/logrotate.d/maxscale_logrotate >> /var/log/cron.log 2>&1

Dockerイメージ作成

$ mkdir -p log/maxscale
$ chmod 777 log/maxscale
$ mkdir maxscale
$ mkdir maxscale-dockerfiles
$ mkdir cron-logrotate-docker-dockerfiles

$ cd maxscale-dockerfiles
$ docker build -t maxscale-log:2.4.4 .
$ cd ..
$ cd cron-logrotate-docker-dockerfiles
$ docker build -t base:1.0 .

動作確認

$ cd ..
$ sudo chown root:root maxscale/maxscale_logrotate

$ docker run -d \
--name base \
-v /var/run/docker.sock:/var/run/docker.sock \
base:1.0

# MaxscaleのDockerコンテナ起動
$ docker run \
-d \
--name maxscale \
-v $(pwd)/log/maxscale:/var/log/maxscale \
-v $(pwd)/maxscale/maxscale_logrotate:/etc/logrotate.d/maxscale_logrotate \
maxscale-log:2.4.4 \
maxscale -d -U maxscale -l file

# ログ確認
$ ls -l log/maxscale/

合計 8
-rw-r--r-- 1 chrony ssh_keys   0 12月 19 01:40 maxscale.log
-rw-r--r-- 1 chrony ssh_keys 707 12月 19 01:39 maxscale.log20191218163900.gz
-rw-r--r-- 1 chrony ssh_keys 187 12月 19 01:40 maxscale.log20191218164000
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

お手軽防犯システム「Ban-Ken Johnny」を作ってみた?

MAN WITH A MISSION
頭はオオカミ、体は人間の究極の生命体。
彼らは自分たちの音楽で世界征服を目指す、最高にアツい集団だ。

そんな「MAN WITH A MISSION」のGt,Vo,Rap ジャン・ケン・ジョニー(Jean-Ken Johnny)と番犬をかけあわせた、というのが「Ban-Ken Johnny(番犬ジョニー)」の由来である。
オオカミと番犬、イヌ科の奇跡のコラボレーション!
センスしかないですね(笑)
ファンクラブ会員限定の10周年記念ツアー当たって欲しいなぁ...

はい、前置きはこれぐらいにして「Ban-Ken Johnny」の機能を紹介します。

「Ban-Ken Johnny」の機能

  • 人感センサーで人の気配を感知したら、スマホにアラートメッセージを送る
  • 自分が家にいる間はセンサーが反応して欲しくないので、監視機能のON/OFFを切り替えできる。
  • 毎回家出る時にon/offするのはメンドイので、家の外からオンラインでアラート通知をON/OFFできる

「Ban-Ken Johnny」にできることはざっとこれぐらいです。
イメージがつきやすいように「Ban-ken Johnny」のデモをしました。

1.監視機能をON

fullsizeoutput_e4.jpeg
監視機能はOFFになっているので、startボタンを押してONにします。
fullsizeoutput_e7.jpeg
ONになりました。

2.家に誰か侵入(ラズパイの人感センサーに反応)

監視機能がONの状態で、
5DWEZ6kzT%+aQLpI5QcDxQ.jpg
家に設置されているラズペリーパイの前を誰かが横切ります(なぜか物悲しい写真ですね...笑)

そうすると、
fullsizeoutput_df.jpeg
自分のスマホに通知が飛んできました!
まあこんな感じです笑
デモでイメージを掴んでいただいたところで、「Ban-Ken Johnny」の仕組みを紹介します。

「Ban-Ken Johnny」の概要

Ban-Ken Johnny.jpg

図だけ見てもよくわからないと思うので、ここからは仕組みを少しだけ詳しく説明して行こうと思います。
興味ないよーという方は編集後記まで読み飛ばしてください

1.Mattermost

Mattermostは自分が侵入者検知メッセージを受け取るために使用します。
別にSlackでも良かったのですが、勉強のためにも自分でチャット環境を用意してみることにしました。
今回MattermostはEC2上でdockerを使って動かします。
個人利用なので、preview版を使うことにしました。
mattermost/mattermost-preview
preview版は一つのコンテナ内に、mattermostアプリとmysqlが動作していて、コンテナ一つを動かすだけでmattermostを使用できます。

加えてmattermostの前段にnginxも置いてみることにしました。(理由はなんとなくカッコイイから)
Mattermostは親切なので、Mattermost用のnginx.confを用意してくれています。
これをありがたく使わせてもらいます。

それらを踏まえると、mattermostを動かすためのdocker-compose.ymlはこんな感じになります。

docker-compose.yml
version: "3"
services: 
  nginx:
    image: nginx:1.17
    container_name: nginx
    volumes: 
      - "./etc/nginx:/etc/nginx"
      - "./var/log/nginx:/var/log/nginx"
    ports: 
     - 8080:80
    links: 
     - mattermost

  mattermost:
    image: mattermost/mattermost-preview
    container_name: mattermost
    volumes: 
      - "./mysql:/var/lib/mysql"

ディレクトリ構成はこんな感じです。

./mattermost
├── ./docker-compose.yml
├── ./etc
│   └── ./etc/nginx
│       └── ./etc/nginx/nginx.conf
├── ./mysql
└── ./var
    └── ./var/log
        └── ./var/log/nginx

今回はEC2で動かして、その前段にELBとRoute53を置きます(ELB置くならnginxいるのか?というツッコミはなしで)
Route53で設定したドメイン名(ban-ken-johnny.example.comとします)にアクセスしてみます
mattermost.png

はい、無事表示されました。
(よくわからないところで凝り性を発揮してしまい、https対応のためにAWS Certificate Manager使って証明書発行したり、ALBでHTTPtoHTTPS対応したりしたんですが、長くなるのでまたの機会に書けたらなぁと...)

とりあえずアカウントを作成してログインします。
ログインしてやったこととしては、「Ban-Ken Johnny」がアラートメッセージを送って来れるように、Incominng Webhookの設定と、プッシュ通知サーバーの設定をしました。
ただ自前でpush通知用サーバーを用意するほど余裕がなかったので、Mattermostが用意してくれているテスト確認用のサーバーを使うことにしました。
あんまり通知飛ばさないと思うのでMattermostさんご容赦を...

さて、もろもろを終えると「Ban-Ken Johnny」からWebhook経由でメッセージを送り、スマホに通知がくるということができるようになります。
6C8105F5-A908-49DA-906A-656923A55571.png

A4C0AB61-DA9A-4680-A77D-88C42AFF4F06.png
イイ感ジデスネ、YES!YES!

2. Flask App

オンラインからをアラート通知をon/offにするために、アラートの状態を管理してくれるアプリをFlaskで実装しました。
本当に簡単なアプリで、機能としては

  • /start/stopのGETリクエストがきたら、DBのstatusカラムの値を変更する

という本当にただそれだけのアプリです。
このアプリもdockerを使ってEC2上で動かします。

docker-compose.yml
version: "3"
services: 
  nginx:
    image: nginx:1.17
    container_name: nginx_flask
    volumes: 
      - "./etc/nginx:/etc/nginx"
    ports: 
     - 8081:80
    links: 
     - flask-app

  flask-app:
    build: ../
    image: flask-app
    container_name: flask-app
    environment:
      - DB_USER=root
      - DB_PASSWORD=setting_password
      - DB_HOST=mysql_flask
      - DB_NAME=flaskdb
    links: 
      - mysql

  mysql:
    container_name: mysql_flask
    image: mysql:5.7
    environment:
      - MYSQL_ROOT_PASSWORD=setting_password
      - MYSQL_DATABASE=flaskdb
    volumes:
      - ./sql:/docker-entrypoint-initdb.d
    command: "mysqld --character-set-server=utf8 --collation-server=utf8_unicode_ci"

ディレクトリ構成はこんな感じです。

./flask-app
├── ./Dockerfile
├── ./config
│   └── ./config/config.py
├── ./definitions
│   ├── ./definitions/__init__.py
│   └── ./definitions/database.py
├── ./docker
│   ├── ./docker/docker-compose.yml
│   ├── ./docker/etc
│   │   └── ./docker/etc/nginx
│   │       └── ./docker/etc/nginx/nginx.conf
│   └── ./docker/sql
│       └── ./docker/sql/init_ddl.sql
├── ./models
│   ├── ./models/__init__.py
│   ├── ./models/alert_status.py
│   └── ./models/dao
│       ├── ./models/dao/__init__.py
│       └── ./models/dao/alert_status.py
├── ./requirements.txt
└── ./server.py

これにより、アラートの状態を管理してくれる簡単なAPIが完成しました。

これぐらいならAmazon API Gateway → AWS lambda → Amazon DynamoDBの完全サーバレス構成にできそうなので、時間ができたらサーバレス構成に変更して見たいと思います。

3. S3(Front End)

スマホからオンラインでアラートのON/OFFを命令するためのフロントエンドは、Amazon S3 での静的ウェブサイトのホスティングを使って動かすことにしました。
正直フロント技術はほとんど触ってきていないところで、かつデザインのセンスもないのでパパッと手軽に実装して、S3にデプロイしちゃいました。
本当はVue.jsとか使いたいし、CSSも初心者感を無くしたいし...
そんなのは夢のまた夢で、イケてない見た目になっちゃいました...
ごめんねジョニー...

4. RaspberryPi

さあ最後に、ラズパイを触っていきます。
「Ban-Ken Johnny」を作るために、今回初めてラズパイを購入しました。
今年一年頑張った自分へのクリスマスプレゼントですね

ラズパイには、「人感センサーをつけて、人の気配を検知したらMattermostにアラート通知する」という、根幹の部分を担ってもらいます。
このプロセスは常時動きながら家を監視しておいて欲しいので、systemdを使ってデーモンプロセスとして動かすことにしました。

プロセスとして動かすpythonのスクリプトはこんな感じ。

ban-ken-johnny.py
#!/usr/bin/python3

import requests
import json
from time import sleep
from datetime import datetime
import dateutil.tz as tz
import sys
import RPi.GPIO as GPIO

GPIO_PIN = 18

GPIO.setmode(GPIO.BCM)
GPIO.setup(GPIO_PIN, GPIO.IN)

STOPPED_STATUS = 2
WEBHOOK_URL = 'https://ban-ken-johnny.example.com/hooks/abcdefghijklmnopqrstuvwxyz'

def is_alert_stopped():
    response = requests.get("https://ban-ken-johnny.example.com:8080/getAlertStatus")
    content = json.loads(str.strip(response.content.decode("utf-8")))

    if int(content['status']) == STOPPED_STATUS:
        return True

    return False


def post_alert():
    headers = {'content-type': 'application/json; charset=UTF-8'}
    now = datetime.today().astimezone(tz.gettz('Asia/Tokyo')).strftime('%Y/%m/%d %H:%M')
    payload = {
                    "text": "侵入者ヲ検知シマシタ!:wolf:",
                    "channel": "town-square",
                    "attachments": [
                        {
                            "text": "**Time:** "+now,
                            "mrkdwn_in": ["text"]
                        }
                    ]
              }
    response = requests.post(WEBHOOK_URL, data=json.dumps(payload), headers=headers)
    res = {
        "status" : str(response.status_code),
        "mesage" : response.content.decode("utf-8")
    }
    return json.dumps(res)


if __name__ == "__main__":
    try:
        while True:
            if GPIO.input(GPIO_PIN) == GPIO.HIGH:
                if is_alert_stopped() == False:
                    post_alert()
                sleep(10)
    except Exception as err:
        print(str(err))
        sys.exit(1)
    finally:
        GPIO.cleanup()

このpythonプロセスをsystemdを使ってデーモンプロセスとして登録するために、ban-ken-johnny.serviceファイルを用意します。

ban-ken-johnny.service
[Unit]
Description=Ban-Ken Johnny
Requires=network.target

[Service]
Type=simple
ExecStart=/opt/ban_ken_johnny.py
Restart=always

[Install]
WantedBy=multi-user.target

このban-ken-johnny.service/etc/systemd/system直下に置き、ban-ken-johnny.pyは、serviceファイルのExecStartで指定しているように、/opt直下に置きます。
準備ができたらコマンドを実行して、ban-ken-johnny.serviceを起動します

#rootにpython-dateutilをインストール
$ sudo pip3 install python-dateutil
$ sudo systemctl daemon-reload
$ sudo systemctl start ban-ken-johnny.service
#OSが起動したらプロセスも自動起動する様にする
$ sudo systemctl enable ban-ken-johnny.service

プロセスのステータスを確認してみます。

$ sudo systemctl status ban-ken-johnny.service
● ban-ken-johnny.service - Ban-Ken Johnny
   Loaded: loaded (/etc/systemd/system/ban-ken-johnny.service; enabled; vendor preset: enabled)
   Active: active (running) since Wed 2019-12-12 21:20:20 JST; 8s ago
 Main PID: 3010 (ban_ken_johnny.)
    Tasks: 1 (limit: 2200)
   Memory: 11.7M
   CGroup: /system.slice/ban-ken-johnny.service
           └─3010 /usr/bin/python3 /opt/ban_ken_johnny.py

12月 12 21:20:20 raspberrypi systemd[1]: Started Ban-Ken Johnny.

ちゃんと動いてますね?
あとは人感センサーが反応したら、ちゃんと通知がくるか確認するだけです!

notification-test.jpeg

B6ED2F5E-2211-4236-B674-9E0E2C598BEC.png

無事動作しました!!
ちゃんとON/OFFにしたがって、通知を飛ばしてきてくれます!!
ヤッタゼ!!

これで「Ban-Ken Johnny」は完成です。
お手軽と言いながら、地味に時間かかっちゃいました...
これで家の留守中も安心です?

編集後記

完成して使い始めたは良いものの、なぜか2分置きにGPIOからinputを検知してしまうという不具合が発生しました。
そのため2分置きに「侵入者ヲ検知シマシタ!?」と送信してきます。
鬼メッセージ送信機へと成り下がってしまったジョニーをどうにかして直したいのですが、ラズパイ初心者の私にはお手上げ状態です...
アドベントカレンダーの締め切りもあったので、すぐに届くセンサーをAmazonで選んで購入したのですが、そのセンサーが悪かったのかもしれません...
今見たら誰も評価してないし...

一応GitHubにも公開しているので、参考までに...
トリアエズ有識者ノ皆サマ、是非オ助ケヲ!!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Python3系のAWS CLIと.NET Core v2.1の入ったCentOSイメージを作る

背景

仕事で.NET Coreのソフトをコンテナ化する機会があったので、そのまとめとPython2系が2020年1月1日に廃止されるので、Python3系の導入についても触れていこうと思います。

二番煎じかもしれませんが、ご了承ください。

内容

以下の構成でDockerのイメージを作成します。

  • baseImage : CentOS
  • Python 3系
  • .NET Core v2.1

導入

(1) DockerfileをClone

まず、以下のリポジトリをクローンしてください。

# ファイルをClone
$ git clone https://github.com/susu-to-susu/Dockerfile_aws_dotnet.git

(2)イメージの作成

Dockerfileの場所まで移動して、buildしてイメージを作成してください。

# ディレクトリ移動
$ cd Dockerfile_windows_dotnet

# イメージの作成
$ docker build -t dotnet_aws .

(3)コンテナの作成

以下のコマンドでコンテナを作成します。

# コンテナの作成
$ docker run --name dotnet_aws -itd dotnet_aws bash

(4)動作確認

以下のコマンドでコンテナにログインをしてaws-cliとdotnetCoreの確認をします。

# アタッチ(ログイン)
$ docker attach dotnet_aws

[root@eb395ae3adad /]# dotnet --version
2.1.509
[root@eb395ae3adad /]# aws --version
aws-cli/1.16.305 Python/3.6.8 Linux/4.9.184-linuxkit botocore/1.13.41

解説

Dockerfileの中身は以下のようになっています。

# Sample Dockerfile

# Indicates that the centos image will be used as the base image.
FROM centos:latest

# Install Python
RUN yum update -y
RUN yum upgrade -y
RUN yum install -y wget
RUN yum install -y python3

# AWS CLI Settings
RUN wget https://bootstrap.pypa.io/get-pip.py
RUN python3 get-pip.py

# Install awscli
# RUN pip install awscli
RUN pip3 install awscli --upgrade --user
ENV PATH $PATH:/root/.local/bin

# Metadata indicating an image maintainer.
LABEL maintainer="susu-susu@github.com"

# dotnet Core v2.1
RUN yum install -y dotnet-sdk-2.1

やっていることは非常に簡単で、python3とdotnetCore v2.1をyumでインストールしています。

ENV PATH $PATH:/root/.local/bin

ここで気を付けることは、pip3でaws-cliをインストールした場合、デフォルトでは/root/.local/binにコマンドが格納されるのですが、環境変数に$PATHが適応されてません。少なくとも自分がやった時はそうでした。

そのため、ENVに書く必要があります。

ターゲットとしては、dotnetCoreをWindows以外で利用したい。コンテナ化して使いたいって場合に最適です。

何か誤字脱字ありましたら、ご指摘頂けると幸いです。

参考リンク

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む