20200119のdockerに関する記事は19件です。

LaradockでLaravelの環境構築するもMysqlが立ち上がらない!

開発環境

  • macOS Mojave 10.14.4
  • Laravel6.(php7.3以上)
  • Mysql8.

環境構築:参考記事
初心者でもLaradockでLaravelの環境構築をエラーなしで行おう!(Mac ver)


初心者なりに環境構築で詰まったところを共有します。
記事を参考に進めていく途中

docker-compose up -d nginx mysql phpmyadmin

mysqlが立ち上がらない
Docker再起動しても立ち上がらない!
困った!

エラー内容

Recreating laradock_mysql_1            ... error
ERROR: for mysql  Cannot start service mysql:
driver failed programming external connectivity on endpoint 
laradock_mysql_1(aa02fd7a36f486c3c62720a98798b7625cd855e49b5c4ef25b26a425e1299e0c): 
Error starting userland proxy: listen tcp 0.0.0.0:3306: bind: address already in use

3306が既に使われているってことか?

ググる...

Laradock公式にこんな記述が

スクリーンショット 2020-01-19 23.37.45.png

docker-compose.ymlの中のports欄を消せばよいとのお告げ

docker-compose.yml
### MySQL ################################################
    mysql:
      build:
        context: ./mysql
        args:
          - MYSQL_VERSION=${MYSQL_VERSION}
      environment:
        - MYSQL_DATABASE=${MYSQL_DATABASE}
        - MYSQL_USER=${MYSQL_USER}
        - MYSQL_PASSWORD=${MYSQL_PASSWORD}
        - MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD}
        - TZ=${WORKSPACE_TIMEZONE}
      volumes:
        - ${DATA_PATH_HOST}/mysql:/var/lib/mysql
        - ${MYSQL_ENTRYPOINT_INITDB}:/docker-entrypoint-initdb.d
      ports:
        - "${MYSQL_PORT}:3306"
      networks:
        - backend

ports: - "${MYSQL_PORT}:3306"
これを削除してみる...

再度docker-compose up -d nginx mysql phpmyadmin

スクリーンショット 2020-01-19 23.43.06.png

できた!ありがとうLaradock公式!!

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

悲劇!LaradockでLaravelの環境構築するもMysqlが立ち上がらない!

開発環境

  • macOS Mojave 10.14.4
  • Laravel6.(php7.3以上)
  • Mysql8.

環境構築:参考記事
初心者でもLaradockでLaravelの環境構築をエラーなしで行おう!(Mac ver)


初心者なりに環境構築で詰まったところを共有します。
記事を参考に進めていく途中

docker-compose up -d nginx mysql phpmyadmin

mysqlが立ち上がらない
Docker再起動しても立ち上がらない!
困った!

エラー内容

Recreating laradock_mysql_1            ... error
ERROR: for mysql  Cannot start service mysql:
driver failed programming external connectivity on endpoint 
laradock_mysql_1(aa02fd7a36f486c3c62720a98798b7625cd855e49b5c4ef25b26a425e1299e0c): 
Error starting userland proxy: listen tcp 0.0.0.0:3306: bind: address already in use

3306が既に使われているってことか?

ググる...

Laradock公式にこんな記述が

スクリーンショット 2020-01-19 23.37.45.png

docker-compose.ymlの中のports欄を消せばよいとのお告げ

docker-compose.yml
### MySQL ################################################
    mysql:
      build:
        context: ./mysql
        args:
          - MYSQL_VERSION=${MYSQL_VERSION}
      environment:
        - MYSQL_DATABASE=${MYSQL_DATABASE}
        - MYSQL_USER=${MYSQL_USER}
        - MYSQL_PASSWORD=${MYSQL_PASSWORD}
        - MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD}
        - TZ=${WORKSPACE_TIMEZONE}
      volumes:
        - ${DATA_PATH_HOST}/mysql:/var/lib/mysql
        - ${MYSQL_ENTRYPOINT_INITDB}:/docker-entrypoint-initdb.d
      ports:
        - "${MYSQL_PORT}:3306"
      networks:
        - backend

ports: - "${MYSQL_PORT}:3306"
これを削除してみる...

再度docker-compose up -d nginx mysql phpmyadmin

スクリーンショット 2020-01-19 23.43.06.png

できた!ありがとうLaradock公式!!

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

Docker で Cron を設定しようとしたときにハマったこと

こんな感じの Dockerfile で Cron が動くかな、と思って作業を開始しました:

Dockerfile
FROM tomcat

ADD crontab /var/spool/crontab/root

ADD run.sh /bin/run.sh
CMD ["sh","/bin/run.sh"]
crontab
* * * * * echo "test" >> /var/tmp/test
run.sh
/etc/init.d/cron start

tail -f /dev/null

はたして、crontab に記載した通りに動きません。シェルにログインして確認してみましょう:

# ps -aef | grep cron
root        63    57  0 12:49 pts/0    00:00:00 grep cron

Cron デーモンが動いていません。たたき起こしてみましょう:

# cron
bash: cron: command not found

そもそも cron が入っていませんので、それは動きませんね。

Cron を追加する

今回はベースイメージが debian 系なので、apt install cron でインストールしてみましょう:

apt install cron
Reading package lists... Done
Building dependency tree
Reading state information... Done
E: Unable to locate package cron

今度はパッケージが見つからないようです。ソースリストを確認します:

/etc/apt/sources.list
# deb http://snapshot.debian.org/archive/debian/20191224T000000Z stretch main
deb http://deb.debian.org/debian stretch main
# deb http://snapshot.debian.org/archive/debian-security/20191224T000000Z stretch/updates main
deb http://security.debian.org/debian-security stretch/updates main
# deb http://snapshot.debian.org/archive/debian/20191224T000000Z stretch-updates main
deb http://deb.debian.org/debian stretch-updates main

main レポジトリのみが有効化されており、公式にサポートされるソフトウェア以外はインストールできなくなっています。universe レポジトリを有効化してあげる必要がありますね。

このままソースリストを編集したいところですが、現状は vim すら入っていないので一度 Dockerfile を修正しておきましょう:

FROM tomcat

ADD sources.list /etc/apt/sources.list
RUN apt update
RUN apt install -y cron

ADD crontab /var/spool/crontab/root

ADD run.sh /bin/run.sh
CMD ["sh","/bin/run.sh"]
sources.list
# deb http://snapshot.debian.org/archive/debian/20191224T000000Z stretch main
deb http://deb.debian.org/debian stretch main universe
# deb http://snapshot.debian.org/archive/debian-security/20191224T000000Z stretch/updates main universe
deb http://security.debian.org/debian-security stretch/updates main universe
# deb http://snapshot.debian.org/archive/debian/20191224T000000Z stretch-updates main universe
deb http://deb.debian.org/debian stretch-updates main universe

できたイメージからインスタンスを再生製して、シェルにログインします。状態を確認してみましょう:

# ps -aef | grep cron
root        46     0  0 13:23 ?        00:00:00 /usr/sbin/cron
root        80    10  0 13:43 pts/0    00:00:00 grep cron

# crontab -l
no crontab for root

Cron デーモンは稼働していますが、crontab が読み込まれていません。あれ?

Crontab を読み込ませる

たしかに Cron の設定ファイルは /var/spool/cron/[ユーザ名] に保存されますが、ファイルを追加しただけでは Crontab をインストールしてくれません。 そのため、Dockerfile は以下のように修正する必要があります:

FROM tomcat

ADD sources.list /etc/apt/sources.list
RUN apt update
RUN apt install -y cron

ADD crontab /var/spool/crontab/root
RUN crontab /var/spool/crontab/root

ADD run.sh /bin/run.sh
CMD ["sh","/bin/run.sh"]

crontab [ファイル名] を利用して、配置したファイルを元に crontab をインストールしています。なんだか冗長ですが致し方ありません。最後に、インストールに成功したか確認してみましょう:

# crontab -l
* * * * * echo "test" >> /var/tmp/test

# tail -f /var/tmp/test
test
test

大丈夫そうですね! ちなみに、以下のように crontab をインラインで記載することで構成をシンプルにすることができます:

(crontab -l; echo " * * * * * echo 'test' >> /var/tmp/test") | crontab

参考リンク

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

Windows10 HomeでDockerを使うときのメモ

はじめに

普段の業務での開発環境はUbuntuなのですが、わけあって私物のwin10 Homeで開発をする機会ができたので、そのときの環境構築手順のメモです。
普段業務で散々お世話になっているので、いい機会だと思い初投稿。

やりたいこと

  • Pythonの開発環境を作りたいが、Anacondaは結構な頻度でハマるポイントに出会うので使いたくない
  • 普段使う開発環境(Ubuntu 18.04 LTS)になるべく近付けたい

ということで、Docker入れてubuntuのコンテナをpythonのバージョン毎に個別に建てるのが何も考えずに出来てきっと楽。
pythonのライブラリはrequirement.txt作って使いまわし、python自体のバージョンはDockerfileの時点で書き換えるつもり。

0. 実行時の環境

作業開始時点で環境構築に影響がありそうなのは以下くらい。

  • Windows10 Home
  • Git for Windows(2.18.0.windows.1)

基本的にGit bash使って作業しています。

1. Dockerのインストール

Docker for WindowsはHomeだと使えないので、Docker Toolboxをインストールする。
https://github.com/docker/toolbox/releasesから最新版のインストーラをダウンロードし実行。記事作成時は19.03.1でした。
終了後、以下の3つのアイコンがデスクトップに作成されているはずなので、一番右のDocker Quickstart Terminalを実行。
01.PNG

ターミナルが立ち上がって初期設定が始まるので、しばらく待つ。DockerのクジラのAAが無事出れば多分無事成功

2. Docker周りの準備

無事Dockerのインストールが終了したので、ubuntuのイメージを持ってくる

$ docker pull ubuntu:18.04

無事pull出来ていれば、以下のコマンドで確認できる。

$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
ubuntu              18.04               ccc6e87d482b        3 days ago          64.2MB

3. Python環境の準備

上記imageをベースにpythonの開発環境が整ったimageを作成するため、適当なディレクトリを作り、Dockerfileを用意。
よく使うDockerfileの中身はこんな感じ。RUNたくさん使うとレイヤーが増えて良くないとか見た気もするけど、自分で使うだけなので気にしない方針。
python3.8とかにしたい場合は、Dockerfileの中身のpythonの部分だけ書き換えれば多分問題ないです。

FROM ubuntu:18.04

RUN apt update && apt upgrade -y
RUN apt install vim python3.7 python3.7-distutils curl -y
RUN ln -s /usr/bin/python3.7 /usr/bin/python
RUN curl "https://bootstrap.pypa.io/get-pip.py" -o "get-pip.py" && python get-pip.py
RUN apt autoremove

上記を元にimageを作成。

# imageの作成
$ docker build -t <image name> .

# 作成したimageの確認(今回はu18_py37という名前で作成)
$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
u18_py37            latest              86bc6cf8e1a9        7 hours ago         214MB
ubuntu              18.04               ccc6e87d482b        3 days ago          64.2MB

4. 実行

上記imageを使ってコンテナをたてる。

$ docker run --net host --name test -v //c/Users/<User Name>/docker:/wrk -it u18_py37 bash

おわりに

とりあえず環境構築自体はこんな感じでしょうか。
ここのやり方良くない等、ご指摘あればよろしくお願いします。
作った環境で初めてwebAPI叩くコード書いたりするのですが、それはまたそのうち別記事で。

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

Windows10 HomeでのDockerを使ったpython環境構築手順メモ

はじめに

普段の業務での開発環境はUbuntuなのですが、わけあって私物のwin10 Homeで開発をする機会ができたので、そのときの環境構築手順のメモです。
普段業務で散々お世話になっているので、いい機会だと思い初投稿。

やりたいこと

  • Pythonの開発環境を作りたいが、Anacondaは結構な頻度でハマるポイントに出会うので使いたくない
  • 普段使う開発環境(Ubuntu 18.04 LTS)になるべく近付けたい

ということで、Docker入れてubuntuのコンテナをpythonのバージョン毎に個別に建てるのが何も考えずに出来てきっと楽。
pythonのライブラリはrequirement.txt作って使いまわし、python自体のバージョンはDockerfileの時点で書き換えるつもり。

0. 実行時の環境

作業開始時点で環境構築に影響がありそうなのは以下くらい。

  • Windows10 Home
  • Git for Windows(2.18.0.windows.1)

基本的にGit bash使って作業しています。

1. Dockerのインストール

Docker for WindowsはHomeだと使えないので、Docker Toolboxをインストールする。
https://github.com/docker/toolbox/releasesから最新版のインストーラをダウンロードし実行。記事作成時は19.03.1でした。
終了後、以下の3つのアイコンがデスクトップに作成されているはずなので、一番右のDocker Quickstart Terminalを実行。
01.PNG

ターミナルが立ち上がって初期設定が始まるので、しばらく待つ。DockerのクジラのAAが無事出れば多分無事成功

2. Docker周りの準備

無事Dockerのインストールが終了したので、ubuntuのイメージを持ってくる

$ docker pull ubuntu:18.04

無事pull出来ていれば、以下のコマンドで確認できる。

$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
ubuntu              18.04               ccc6e87d482b        3 days ago          64.2MB

3. Python環境の準備

上記imageをベースにpythonの開発環境が整ったimageを作成するため、適当なディレクトリを作り、Dockerfileを用意。
よく使うDockerfileの中身はこんな感じ。RUNたくさん使うとレイヤーが増えて良くないとか見た気もするけど、自分で使うだけなので気にしない方針。
python3.8とかにしたい場合は、Dockerfileの中身のpythonの部分だけ書き換えれば多分問題ないです。

FROM ubuntu:18.04

RUN apt update && apt upgrade -y
RUN apt install vim python3.7 python3.7-distutils curl -y
RUN ln -s /usr/bin/python3.7 /usr/bin/python
RUN curl "https://bootstrap.pypa.io/get-pip.py" -o "get-pip.py" && python get-pip.py
RUN apt autoremove

上記を元にimageを作成。

# imageの作成
$ docker build -t <image name> .

# 作成したimageの確認(今回はu18_py37という名前で作成)
$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
u18_py37            latest              86bc6cf8e1a9        7 hours ago         214MB
ubuntu              18.04               ccc6e87d482b        3 days ago          64.2MB

4. 実行

上記imageを使ってコンテナをたてる。

$ docker run --net host --name test -v //c/Users/<User Name>/docker:/wrk -it u18_py37 bash

おわりに

とりあえず環境構築自体はこんな感じでしょうか。
ここのやり方良くない等、ご指摘あればよろしくお願いします。
作った環境で初めてwebAPI叩くコード書いたりするのですが、それはまたそのうち別記事で。

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

Autosar文書を読む(準備)

自動車用のOS、通信規約、道具類などを規定しているAutosarは、ISO OSEKとISO/IEC POSIXの自動車産業における設計例である。

WTO/TBT協定によれば、国際的な調達は、ISO, IEC, ITUなどの国際規格を仕様記述の出発点として参照することを推奨している。

Autosarも、ISO OSEK及びISO/IEC POSIXを参照している。自動車分野はISOのTC22で審議しておりISOの単独文書。POSIXはIT分野でISO/IEC JTC1 SC22で審議しておりISOとIECの合同文書。

IEEEはEthernet, Wi-Fiなどを規格化し、ISO, IECと協力して関連規格を出版している。ISO, IEC, IEEEの共同文書も相当数ある。

SAEはアメリカの自動車技術会相当の組織。misraはイギリスにある自動車ソフトウェア団体。

それぞれの名称の出現頻度は下記。

standard count organization
osek 176 ISO TC22
POSIX 96 ISO/IEC JTC1 SC22
ISO 2297
IEC 138
IEEE 300
sae 723 Society of Automotive Engineers
misra 410

背景

Toppersプロジェクトで ISO Osekに対応したオープンソースを発行する事業に参加し、標準化、安全分析、MISRA C適合、訓練などに参加してきた。また、TOPPERSプロジェクトのAutosar環境であるATKを実機なしで模擬試験できるソフトウェアを追っかけている。

TOPPERS/ATK2カーネル向け実機レス環境(athrill2)
https://qiita.com/kanetugu2018/items/75dadc340f3db2a344ab

OS及びC言語及びTCP/IP通信ソフトウェアのC言語規格およびPOSIXへの適合試験を行ってきた。

Posix Test Suite docker downloads, tar, install
https://qiita.com/kaizen_nagoya/items/f1e24be04a2405ede00f

Osekに基づいたAutosarへの対応においてもいくつかの視点で評価を行ってきた。
当初は、現在のclassic版だけであったが、Adaptive版についてもPOSIX対応の延長で対応してきた。

初めて検討する分野では、まず参考文献の参考文献を洗い出し、入手可能性を検討する。
参考文献の入手は時間がかかったり、お金がかかたりすることがある。そのため、最初に着手しておかないと、費用の見積もりを過小評価してしまうかもしれないからである。

参考文献の参考文献の入手性を確認したら、入手できた範囲の文献の用語頻度一覧と、略語集を作る。

今回、用語頻度一覧から着手しているのは、これまでに一度は入手可能性を確認した文書であることと、ISO/IEC JTC1 SC7から、ISO TC22への国際的なliaison(情報交換担当)として両委員会の公式文書をReviewしてきた過程において、各文書のNormative Referenceの入手可能性を検討してきた経緯があるからである。

文書入手

Autosar単語帳(作業中)
https://qiita.com/kaizen_nagoya/items/0927727a94b157df2df8

目次から一つづつファイルを入手しようとすると、最新ではない文書にぶつかったり、文書が一覧に見当たらないという現象に出会った。

そこで、上記URLの最後に記載した、一覧で一括入手できる方法によった。

PDFのText変換

辞書作成のために、PDFをText変換した。

プログラミングの用語一覧の場合には、数字や_などの記号で、変数名、関数名、定数名で使うことができる文字を含んだ、なるべく長い文字列で一覧を作成する。

標準文書では、数字や_, -, などの記号を省略し、単語として同じ意味で使っている言葉はなるべく頻度を集計できるような方向での処理を行う。ただし、複数形、過去形などの変化系はあえて統合せず、文字列として現れるままで集計している。

これは、過去の難度かの複数形、過去形などの統合の処理では、別の意味で使っている用語を、文字列の並びがたまたま同じになるために同一の語として集計してしまったり、結果として分析のための精度が作業量に比較して向上しないという経験則による。

作業はdocker上で行った。どこからでも作業を再開できるようにdocker hubに保存している。

用語頻度

Autosar 単語検索 略号等
https://qiita.com/kaizen_nagoya/items/f8cb1295104b7d58363d

英語(38)  複合語と複数の単語の一分類 https://qiita.com/kaizen_nagoya/items/219fd0bde3731f0881ed

作業記録 Autosar単語帳の分割の抜け漏れ
https://qiita.com/kaizen_nagoya/items/15b4011a68ce0ba81e6e

略号一覧

略号のfull spellingの確認のため、全文書の略号一覧を統合中である。
参考文献一覧の作成と一緒に作業中。

Autosar文書、参考文献、略号一覧(作成中)
https://qiita.com/kaizen_nagoya/items/2325b0156bc7fcf5a96d

普段はこの作業から始めるが、今回は逆順である。
これまで、複合語と複数の単語に分類することは、あまり意識してこなかった。
今回は、プログラミングですぐに用いることを想定するのではなく、プログラミングの変数名などを含むため、複合語を分解し、技術的な概念分布を検討することを目的とすることにした。

英語(2) まぎらわしい、間違えやすい、行き違いの多い略号worst 10(候補24)
https://qiita.com/kaizen_nagoya/items/0bff5dbb72208053489b

英語(3) 仮説・検証(88)用語の衝突(用語・用例募集中)
https://qiita.com/kaizen_nagoya/items/6a8eb7ffaa45eeb16624

道具類

作業経過で利用している道具類は次の通りである。それぞれの作業で出たエラー類は別記事として整理している。

docker

今日のdocker error : Error response from daemon: error while creating mount source path
https://qiita.com/kaizen_nagoya/items/1ff3550c4ef1c545ce12

ubuntu

bash

今日のbashエラー:No such file or directory(解決)
https://qiita.com/kaizen_nagoya/items/4bcf2ebdce3b475a808a

shell の種類と版
https://qiita.com/kaizen_nagoya/items/ab715ffeceb8617f11ca

pdftotext

PDFファイルの文字誤変換
https://qiita.com/kaizen_nagoya/items/49ce6e8cead58c646534

sed

今日のsed error:unknown option to `s'
https://qiita.com/kaizen_nagoya/items/8e8c4efdb6869f965b54

awk

表計算ソフト

grep

grep 操作間違いなどhttps://qiita.com/kaizen_nagoya/items/2f14e895175908c296ca

macOSテキストエディタ, mi.app

四角い領域の切り取り、切り貼り
https://qiita.com/kaizen_nagoya/items/ba3661d4e224acdf1b04

参考文献(reference)

Autosar Guidelines C++14 example code compile list(1-169)
https://qiita.com/kaizen_nagoya/items/8ccbf6675c3494d57a76#_reference-3a2b9cfd1b05f6a444c2

Autosarの課題
https://qiita.com/kaizen_nagoya/items/617d10b0e34143030600

英語(1) プログラマが知っているとよい英単語の語源
https://qiita.com/kaizen_nagoya/items/9de6d47c47e2c211222b

文書履歴(document history)

ver. 0.01 初稿 20200119
ver. 0.02 URL追記 20200120

このエントリーをはてなブックマークに追加

https://b.hatena.ne.jp/guide/bbutton

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

DockerでMediaWikiとVisualEditorを入れたログ

DockerでMediaWikiを入れたときのメモ。Docker初めて触った。

  • 扱うもの
    • MediaWiki(1.34.0)の導入(Dockerでごり押す)
    • parsoidの導入(Dockerでごり押す)
    • VisualEditorの導入
    • いくつかの拡張機能の導入
  • 扱わないもの
    • MySQL
      • 既に入っている状況下に構築したので
    • RESTBase
      • むずすぎて投げた

環境

  • ホストOS
    • Ubuntu 18.04.3 LTS

手順

すべてsudo権限下で行ったが、別に要らない可能性もある(知らない)。

Dockerインストール済みを想定。

ホスト:dir#とは、ホストマシン上のディレクトリdirでsudo権限でbashコマンドを実行するの意味。ディレクトリは省略された場合どこでコマンドを実行してもよい。

MediaWikiのDockerイメージを見つける

ホスト:# docker search mediawiki
NAME                                   DESCRIPTION                                     STARS               OFFICIAL            AUTOMATED
mediawiki                              MediaWiki is a free software open source wik…   201                 [OK]
synctree/mediawiki                     Docker packaging for MediaWiki. Based on off…   79                                      [OK]
wikimedia/mediawiki                    MediaWiki is the wiki package used by Wikipe…   50
kristophjunge/mediawiki                MediaWiki with VisualEditor plugin and Parso…   29                                      [OK]
以下省略

とりあえず安直にmediawikiを使うことにした。既に色々入っているイメージとかあるみたいだけど、最初に入れたこれで最後まで要求が満たされたので結局これを採用した。

とりあえずMediaWikiのサーバーを建ててみる

まずは目当てのイメージをダウンロードしてくる

別にやらなくてもrun時に自動的にダウンロードされるかもしれない。

ホスト:# docker pull mediawiki

コンテナを新造して立ち上げる

ホスト:# docker run -d --net=host --name mw mediawiki

-dを付けることでバックグラウンドで起動する。

--net=hostでホストのコンピュータと同じネットワーク上で起動する。今回は同マシン上で建っているparsoidサーバーにアクセスするためにこれを指定した。

--name mwで名前をmwにする。名前は何でもいい。省略すると適当に変な名前が振られる。

中に入ってみる

このコマンドで、Dockerの中でbashを起動できる。

docker exec -it mw bash

コンテナ内のファイルシステムをホスト上で直接書き換えられるようにしてみる

おもむろにホスト側にjsonを色々できるjqをインストールする。

ホスト:# apt install jq

さっきrunしたmwの情報を以下のコマンドで見れる。

ホスト:# docker container inspect mw

これを実行するとJson形式のデータが得られるので、次のようにディレクトリを取り出せる。

ホスト:# docker container inspect mw | jq -r '.[0].GraphDriver.Data.MergedDir'
/var/lib/docker/overlay2/c39bc935abfaaedc65e96d7b6cb7985db77687f7d62b867e4a1a6bd059c94734/merged

mergedの中はコンテナの内部からリアルタイムに参照されるルートディレクトリである。

ホスト:/var/lib/docker/overlay2/c39bc935abfaaedc65e96d7b6cb7985db77687f7d62b867e4a1a6bd059c94734/merged# ls
bin  boot  dev  etc  home  lib  lib64  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var

ポート変更

エディタとか色々と入っていなくて萎えるので、ホスト側のファイルシステム上に見えているコンテナのファイルシステムの本体を直接書き換えて済ます。

ホスト:merged# nano etc/apache2/ports.conf

好きなように変えることができる。

編集
- Listen 80
+ Listen 12345

Dockerの機能の方でポートフォワーディングもできるみたいだけど、理解するのが面倒なので置いておく。

コンテナ再起動

docker restart mw

接続

アクセスしてみるとこんなのが出る

20200119-192555-001451.png

インストール

20200119-192940-001452.png

この辺はウィザードに従ってうまいことやる。Dockerで環境ごと入れたのでそんなに困らないんじゃないかと思う。

データベースはホスト上で既に動いているMySQLを使用したので「MariaDB、MySQLまたは互換製品」を選択。

「データベースのホスト」には127.0.0.1を指定。

データベースとユーザの用意は以下の情報に従って作成する。

> CREATE DATABASE wikidb;
> CREATE USER 'wikiuser'@'localhost' IDENTIFIED BY 'password';
> GRANT ALL PRIVILEGES ON wikidb.* TO 'wikiuser'@'localhost' WITH GRANT OPTION;

「オプション→拡張機能」欄はよくわからないので全部チェックを入れた。

全部うまくいくと「データベースは正常にセットアップされました」と出てLocalSettings.phpをダウンロードさせられるので、ダウンロードして配置する。

配置は、面倒なのでTerapadで開いてコピーしたあと、配置先のファイルをnanoで開いてコマンドプロンプトに右クリックで貼り付けた。

ホスト:merged# nano var/www/html/LocalSettings.php

インストールが成功していればトップページが次のようにWikiらしい表示になる。

20200119-195453-001453.png

いくつかの拡張機能のインストール

「特別ページ→バージョン情報→インストール済み拡張機能」を見てみる。ここがハイフンになっている奴が導入されていないという声が上がったので、インストールする。

20200119-195938-001454.png

とりあえず欲しいものを手動で手あたり次第ダウンロードし、圧縮ファイルを一つのディレクトリにまとめてホストコンピュータに転送する。

仮に~/mediawikiにまとめた場合、以下のようにすると拡張機能のインストールが完了する。

ホスト:merged# d='~/mediawiki'
ホスト:merged# for f in $(ls -1 "$d"); do tar -xzf "$d/$f" -C var/www/html/extensions; done

VisualEditorを導入する

VisualEditorの導入は闇である。しかし、導入に成功した時の恩恵も大きい。

VisualEditorを利用するにはparsoidというサーバー用にポートを1個確保しなければならない。これはマシン内でのみ通信されるので、ポートを外部に向けて公開する必要はない。

VisualEditorのインストール

いきなりVisualEditorをインストールする。

ホスト:marged/var/www/html/extensions# git clone https://gerrit.wikimedia.org/r/p/mediawiki/extensions/VisualEditor.git
ホスト:marged/var/www/html/extensions# cd VisualEditor
ホスト:marged/var/www/html/extensions/VisualEditor# git checkout remotes/origin/REL1_34
ホスト:marged/var/www/html/extensions/VisualEditor# git submodule update --init

インストール完了。

VisualEditorの設定

共通設定ファイルを編集する。

ホスト:marged/var/www/html# nano LocalSettings.php
末尾に追加
wfLoadExtension('VisualEditor');
$wgDefaultUserOptions['visualeditor-enable'] = 1;
$wgHiddenPrefs[] = 'visualeditor-enable';
$wgVirtualRestConfig['modules']['parsoid'] = [
  'url' => 'http://localhost:12346/',
  'domain' => 'localhost',
];

ここで、12346はparsoidのポート番号である。domainにはポートまでは書かない。URLの末尾には/を入れる。

これで動いた。

parsoidを建てる

parsoidもDockerで建てる。

# docker search parsoid
NAME                  DESCRIPTION                                     STARS               OFFICIAL            AUTOMATED
thenets/parsoid       Containerized MediaWiki parsoid service.        4
benhutchins/parsoid   Container to run MediaWiki's Parsoid            2                                       [OK]
formwandler/parsoid   Parsoid image for the VisualEditor of MediaW…   1
irasnyd/parsoid       Docker container of the Mediawiki Parsoid ap…   1                                       [OK]

よくわからんがとりあえずthenets/parsoidを使う。

コンテナを製造。公式にはrun時のパラメータで色々できることが書いてあるが、それをするとポートの変更方法がなんかよくわかんなかったので使わない。

ホスト:# docker run -d --net=host --name ps thenets/parsoid:0.10

parsoidを設定

thenets/parsoidの中では全然コマンドが打てないのでこっちもファイルシステムに直接アクセスする。

docker container inspect ps | jq -r '.[0].GraphDriver.Data.MergedDir'

中に入るとこうなっている。

ホスト:merged# ls
bin  dev  etc  home  lib  media  mnt  opt  proc  root  run  run-parsoid.sh  sbin  srv  sys  tmp  usr  var

run-parsoid.shを編集する

ホスト:merged# nano run-parsoid.sh
先頭付近を編集
  #!/bin/bash

+ PARSOID_DOMAIN_localhost=http://localhost:12345/api.php
+ 
  set -e
中腹付近を編集
          # Allow override of port/interface:
-         serverPort: 8000
+         serverPort: 12346
          #serverInterface: '127.0.0.1'

ここで、12345はMediaWikiのポート番号、12346はparsoidのポート番号である。

あとは再読み込みする。

docker restart ps

VisualEditorの導入が完了

これでVisualEditorの導入が完了した。

20200119-212510-001455.png

Parsoidとの連携がうまくいっていないとここで404が出たとか言われた。そのときはURLの指定が間違っていた。

「ブラウザ→MediaWiki API→Parsoid」のうちどの部分の呼び出しが404になっているのか注意しなければ、どこが間違っているのかが把握できない。

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

Manage SSL/TLS certificate with certbot + nginx on Ubuntu18.04

certbot作法について、2020/1/19時点で調べた結果をまとめておく。
nginxの使用を想定。

HTTPを使用するHTTP-01方式での証明書取得についてのみ記述する。

OS: Ubuntu18.04
WebServer: Nginx

オンラインドキュメント

Ubuntu18.04での流れ: https://certbot.eff.org/lets-encrypt/ubuntubionic-nginx

User Guideにコマンド詳細: https://certbot.eff.org/docs/

証明書管理の流れ

サブコマンドで書くと

  1. register: アカウント登録
  2. certonly: 証明書取得
  3. install: WebServer設定
  4. renew: 証明書更新
  5. revoke: 証明書の失効
  6. unregister: アカウントの無効化

という流れになる。

1.は初回に一度だけ実行する。
2.,3.は証明書を取得するドメインに応じて実行する。
4.を自動化して証明書更新をし続ける。
5.,6.は必要に応じて行う。

インタラクティブモード

register,certonly,installでは、必須オプション不足時にインタラクティブで入力を促してくる。
初回のみの作業なので、インタラクティブモードを活用するのもあり。

テストサーバーの指定

--test-certオプションをつける。以降のコマンドサンプルにはつけて記述する。

テストサーバーを利用する際は、本番用ドメインと競合しないようにtest-cert.examle.comのようなテスト用サブドメインを用意して試すのがよい。

パッケージインストール

Cerbot PPAを利用する。

sudo apt-get update
sudo apt-get install software-properties-common
sudo add-apt-repository universe
sudo add-apt-repository ppa:certbot/certbot
sudo apt-get update

sudo apt-get install certbot python-certbot-nginx

certbot register: アカウント登録

とばした場合、certbot certonly実行時に行われる。

% certbot --test-cert register
Saving debug log to /var/log/letsencrypt/letsencrypt.log
# Email address登録
# 登録しない場合は、 --register-unsafely-without-email をつける。
Enter email address (used for urgent renewal and security notices) (Enter 'c' to
cancel): admin@example.com

# 利用規約の同意。同意必須。
# --agree-tos をつけることで同意にしてスキップ可能。
# テストサーバーの場合、https://~staging~ となる。
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Please read the Terms of Service at
https://letsencrypt.org/documents/LE-SA-v1.2-November-15-2017.pdf. You must
agree in order to register with the ACME server at
https://acme-staging-v02.api.letsencrypt.org/directory
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
(A)gree/(C)ancel: A

# EFFとのメールアドレス共有を許可するか。
# --eff-email: 共有許可
# --no-eff-email: 共有しない
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Would you be willing to share your email address with the Electronic Frontier
Foundation, a founding partner of the Let's Encrypt project and the non-profit
organization that develops Certbot? We'd like to send you email about our work
encrypting the web, EFF news, campaigns, and ways to support digital freedom.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
(Y)es/(N)o: N

IMPORTANT NOTES:
 - Your account credentials have been saved in your Certbot
   configuration directory at /etc/letsencrypt. You should make a
   secure backup of this folder now. This configuration directory will
   also contain certificates and private keys obtained by Certbot so
   making regular backups of this folder is ideal.

オプション指定でノンインタラクティブで実行可能。

# Email登録なしでのコマンド例
certbot --test-cert --noninteractive register --register-unsafely-without-email --agree-tos

certbot certonly: 証明書取得

certbot --test-cert certonly
Saving debug log to /var/log/letsencrypt/letsencrypt.log

# 取得方法を指定する。後述。
# nginx: --nginx
# standalone: --standalone
# webroot: --webroot
How would you like to authenticate with the ACME CA?
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1: Nginx Web Server plugin (nginx)
2: Spin up a temporary webserver (standalone)
3: Place files in webroot directory (webroot)
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Select the appropriate number [1-3] then [enter] (press 'c' to cancel): 1
Plugins selected: Authenticator nginx, Installer None

# ドメイン入力
# --domain DOMAIN
Please enter in your domain name(s) (comma and/or space separated)  (Enter 'c'
to cancel): test-cert.example.com
Obtaining a new certificate
Performing the following challenges:
http-01 challenge for test-cert.example.com
Waiting for verification...
Cleaning up challenges

# Congratulations!で成功
IMPORTANT NOTES:
 - Congratulations! Your certificate and chain have been saved at:
   /etc/letsencrypt/live/test-cert.example.com/fullchain.pem
   Your key file has been saved at:
   /etc/letsencrypt/live/test-cert.example.com/privkey.pem
   Your cert will expire on 2020-04-18. To obtain a new or tweaked
   version of this certificate in the future, simply run certbot
   again. To non-interactively renew *all* of your certificates, run
   "certbot renew"

/etc/letsencrypt/live/{domain_name}/ に最新の証明書および秘密鍵へのリンクが作成される。
WebServerはこのシンボリックリンクを参照するようにする。

取得方法について

certbot certonlyで問われる取得方法。
それぞれで作業が異なるので、nginx-plugin, standalone, webrootについて書く。

nginx-plugin

--nginxオプション指定で動作。
nginxの設定変更無しに証明書の取得・更新が可能。
また、証明書更新時にはnginxのリロードも行う。
nginxが常駐するインスタンス内でcertbotを動かす場合に使用できる。
apt install python-certbot-nginxなどでプラグインのインストールが必要。

standalone

--standaloneオプション指定で動作。
certbot自身がHTTPサーバーとなって作業する。
80番ポートを使うので、nginxが80番ポートを使用してる場合は、証明書取得・更新時にはnginxを停止しておく必要がある。

webroot

--webrootオプション指定で動作。
HTTPサーバーの役割をnginxなどに任せて作業する。
http://test-cert.example.com/.well-known/acme-challenge/ 配下にファイルを作成するので、/.well-known/acme-challenge/パスに対応するサーバーインスタンス内でのディレクトリを教える必要がある。

例えば、以下のようなnginx.confであれば、webrootのディレクトリ指定には、/var/www/htmlを指定する。

# example
server {
    listen 80;
    location ^~ /.well-known/acme-challenge/ {
        default_type "text/plain";
        root /var/www/html;
    }
}

certbot install: WebServer設定

WebServerのVirtualHostやhttp->https redirectなどの設定を行う。
nginx-pluginが必要。
certbot installの代わりに、nginx.confを自分で書き換えてもよい。

% certbot --test-cert install
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Plugins selected: Authenticator None, Installer nginx

# VirtualHost選択
Which certificate would you like to install?
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1: test-cert.example.com
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Press 1 [enter] to confirm the selection (press 'c' to cancel): 1   
Deploying Certificate to VirtualHost /etc/nginx/sites-enabled/default

# http->https redirect有無
Please choose whether or not to redirect HTTP traffic to HTTPS, removing HTTP access.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1: No redirect - Make no further changes to the webserver configuration.
2: Redirect - Make all requests redirect to secure HTTPS access. Choose this for
new sites, or if you're confident your site works on HTTPS. You can undo this
change by editing your web server's configuration.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Select the appropriate number [1-2] then [enter] (press 'c' to cancel): 2
Redirecting all traffic on port 80 to ssl in /etc/nginx/sites-enabled/default

certbot certificates: 証明書一覧

取得済み証明書を確認する場合に使用。

certbot --test-cert certificates
Saving debug log to /var/log/letsencrypt/letsencrypt.log

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Found the following certs:
  Certificate Name: test-cert.example.com
    Domains: test-cert.example.com
    Expiry Date: 2020-04-18 09:24:30+00:00 (INVALID: TEST_CERT)
    Certificate Path: /etc/letsencrypt/live/test-cert.example.com/fullchain.pem
    Private Key Path: /etc/letsencrypt/live/test-cert.example.com/privkey.pem
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

httpsアクセスできることを確認

httpsでアクセスしてみる。
テストサーバーで発行された証明書は、無効な証明書となっている。

certbot renew: 証明書の更新

更新期限30日未満の証明書のみが更新の対象となる。

# --force-renewalをつけると期限30日前でも更新可能だが、基本使わない
% certbot --test-cert renew
Saving debug log to /var/log/letsencrypt/letsencrypt.log

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Processing /etc/letsencrypt/renewal/test-cert.example.com.conf
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Cert not yet due for renewal

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

The following certs are not due for renewal yet:
  /etc/letsencrypt/live/test-cert.example.com/fullchain.pem expires on 2020-04-18 (skipped)
No renewals were attempted.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

更新の自動化

apt install certbotで、cronもしくはsystemd timerで行われるようになる。
cronの設定ファイルには、systemdが無い場合に動作するようにコマンドが書かれている。

証明書取得方法に応じて、書き換えが必要となる。

nginx−plugin

プラグインによって、nginx -s reloadの実行がされるので設定書き換えは不要。

standalone

nginx停止が必要なので、 --pre-hook,--post-hookを使う。

# /usr/sbin/service利用時
certbot -q renew --pre-hook "/usr/sbin/service nginx stop" --post-hook "/usr/sbin/service nginx start"
# supervisord利用時
certbot -q renew --pre-hook "/usr/bin/supervisorctl stop nginx" --post-hook "/usr/bin/supervisorctl start nginx"

webroot

--post-hookでnginx -s reloadを使用する。

certbot -q renew --post-hook "/usr/sbin/nginx -s reload"

certbot revoke: 証明書の失効

revokeすると、証明書など関連ファイルが削除される。

%  certbot --test-cert revoke --cert-path /etc/letsencrypt/archive/test-cert.example.com/cert1.pem 
Saving debug log to /var/log/letsencrypt/letsencrypt.log

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Would you like to delete the cert(s) you just revoked?
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
(Y)es (recommended)/(N)o: Y

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Deleted all files relating to certificate test-cert.example.com.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Congratulations! You have successfully revoked the certificate that was located
at /etc/letsencrypt/archive/test-cert.example.com/cert1.pem

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

certbot unregister: アカウント削除

% certbot --test-cert unregister
Saving debug log to /var/log/letsencrypt/letsencrypt.log

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Are you sure you would like to irrevocably deactivate your account?
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
(D)eactivate/(A)bort: D

IMPORTANT NOTES:
 - Account deactivated.

certbot同時実行時の安全性について

同時実行できない。安心。
Lock Files: https://certbot.eff.org/docs/using.html#id5

This means that by default two instances of Certbot will not be able to run in parallel.

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

Synology DockerでJenkinsをセットアップするには

前提

Synology NAS DS218+で遊んでいます。
NAS :Synology DiskStation 218+
    RAM:10GB(2GB+8GB)
    HDD:4TB + 3TB

概要

Jenkins-blueoceanをセットアップした時の初期パスワードの確認方法がわからなかったのが、やっと分かったのでメモ

ローカルの場合

ローカルでの初期パスワードの確認は、terminalで
cat $HOME/jenkins_home/secrets/initialAdminPassword
を入力し、表示されるパスワードを利用します。

Synology Docker Jenkinsの場合

Synologyとsshで接続してこのコマンドを入力しても見つかりません。
Synology Dockerのコンテナから、セットアップしたJenkinsの詳細を表示し、詳細画面の[概要][プロセス][ログ][端末]から「端末」を選択。
作成ボタンを押下したら表示される[bash]を選択。
terminalが表示されるので、
cat var/jenkins_home/secrets/initialAdminPassword
を入力し、表示されるパスワードを利用します。

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

Kubernetesの構築

kubernetes-construction

常駐先の社内勉強会用の記事でしたが、全体に公開してみます。(全体公開は初です。)
こうした方がいいよがあればコメントください。

$マークはコピペしやすい様にあえて付けていません。

Kubernetes + Nvidia Docker の構築(まだ途中です。)

検証済み環境

OS ARCH docker Ver. ESXi
Ubuntu 18.04 amd64 Docker version 19.03.5, build 633a0ea838 6.7 Custom ISO
Ubuntu 18.04 ppc64le Docker version 18.06.1-ce, build e68fc7a None

ppc64le はIBM Power System AC922 POWER9で検証

コンテナイメージはそれぞれのアーキテクチャに合わせる必要があります。

docker のインストール(docker-ce)

既存のアンインストール

sudo apt-get remove docker docker-engine docker.io containerd runc

docker-ce のインストール

bash
# パッケージの更新
sudo apt-get update
# 必要モジュールのインストール
sudo apt-get install -y \
    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

# リポジトリの追加
# amd64
sudo add-apt-repository \
   "deb [arch=amd64] https://download.docker.com/linux/ubuntu \
   $(lsb_release -cs) \
   stable"

# ppc64le
sudo add-apt-repository \
   "deb [arch=ppc64el] https://download.docker.com/linux/ubuntu \
   $(lsb_release -cs) \
   stable"

# パッケージの更新
sudo apt-get update

# dockerのインストール
sudo apt-get install -y docker-ce

# ユーザーの追加
sudo usermod -aG docker $USER

# dockerサービスの自動起動
sudo systemctl enable docker

# dockerサービスの起動
sudo systemctl start docker


補足情報

bash
# インストール可能なリスト
apt-cache madison docker-ce

# バージョンを指定してインストール
sudo apt-get install docker-ce=<VERSION_STRING> docker-ce-cli=<VERSION_STRING> containerd.io

kubernetes インストール

リポジトリにキーの登録

bash
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"

リポジトリの更新

bash
sudo apt update
sudo apt install -y kubeadm

スワップ OFF

bash
## 都度適応?
## /etc/fstabを削除すると泣くことになる -> なぜかdockerが立ち上がらなくなる
sudo swapoff -a

kubeadm でセットアップ

bash
sudo kubeadm init --pod-network-cidr=10.244.0.0/16

kubernetes config の追加

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

iptables の編集

sudo sysctl net.bridge.bridge-nf-call-iptables=1

これをしないとcore-dnsの pod が起動しない

flannel.yaml の追加

kubectl -f flannel.yml

LoadBranser の構築

metallb.yamlを適用する

kubectl apply metallb.yaml

LAN 内の IP に割り振る yaml を apply する

metallb-config.yaml
---
apiVersion: v1
kind: ConfigMap
metadata:
  namespace: metallb-system
  name: config
data:
  config: |
    address-pools:
    - name: default
      protocol: layer2
      addresses:
      - xxx.xxx.xxx.xxx-xxx.xxx.xxx.xxx # この部分をLAN内のIPに変更する

Master Nodes にもデプロイする場合(シングルマスタノード構成の場合とか)

kubectl taint nodes 名前空間 node-role.kubernetes.io/master:NoSchedule-

名前空間部分はkubectl get nodeで取得できるNameを設定(Master の Name)

bash
kubectl describe node node名

# ~~ 中略 ~~
Taints:             <none>
# ~~ 中略 ~~
# 上記になっていることを確認

Master へ Node を追加する

bash
sudo kubeadm join xxx.xxx.xxx.xxx:6443 --token xxxxxx.xxxxxxxxxxxxx --discovery-token-ca-cert-hash sha256:xxxxxxx

Master を構築した時に最後に表示される--token --discovery-token-ca-cert-hashを設定する

Token が不明になった場合

bash
# tokenが不明になった場合は再度発行すれば良い
kubeadm token create --print-join-command

Node が追加されたか確認

bash
kubectl get nodes
NAME           STATUS   ROLES    AGE     VERSION
Master         Ready    master   3m11s   v1.17.1
Node1          Ready    <none>   2m10s   v1.17.1

DashBorad(v2.0.0-rc2) のデプロイ

kubernetes リポジトリから直接デプロイする場合はクラスタ内部からしかアクセス出来ないため、
NodePort を設定したrecommended.yamlをデプロイする。
ただし、リポジトリから直接デプロイし、NodePort を設定しても
issue にある様なエラーが発生するためここではすでに用意しているdashboard/recommended.yamlを使用する
また、その際に証明書の設定をする必要があるため、下記を実施すること

自己証明書の発行

bash
mkdir certs
openssl req -nodes -newkey rsa:2048 -keyout certs/dashboard.key -out certs/dashboard.csr -subj "/C=/ST=/L=/O=/OU=/CN=kubernetes-dashboard"
openssl x509 -req -sha256 -days 365 -in certs/dashboard.csr -signkey certs/dashboard.key -out certs/dashboard.crt

すでに定義されている分を削除(直接デプロイした場合)

bash
# 削除
kubectl -n kubernetes-dashboard delete secret kubernetes-dashboard-certs

証明書を pod から使用できる様にする

  • certs/dashboard.crt
  • certs/dashboard.csr
  • certs/dashboard.key

上記の証明書ファイルを Secret オブジェクトへ設定できる形にする

bash
# base64形式で設定する必要があるのでbase64コマンドで設定する
# それぞれのファイルで実施する
# certs/dashboard.crt
# certs/dashboard.csr
# certs/dashboard.key
cat certs/dashboard.crt | base64
cat certs/dashboard.csr | base64
cat certs/dashboard.key | base64

recommended.yaml の編集

recommended.yaml
---
apiVersion: v1
data:
  dashboard.crt: base64に変換された.crtファイルを記述
  dashboard.csr: base64に変換された.crsファイルを記述
  dashboard.key: base64に変換された.keyファイルを記述
kind: Secret
metadata:
  name: kubernetes-dashboard-certs
  namespace: kubernetes-dashboard
  labels:
    k8s-app: kubernetes-dashboard
type: Opaque

apply する!

kubectl apply -f dashboard/recommended.yaml

ログイン用のトークンを設定

ログイン画面には token or kubeconfig が求められます。
今回は Token を用いてログインする方法を記載します。

admin-user.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
  name: admin-user
  namespace: kubernetes-dashboard
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
  name: admin-user
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: cluster-admin
subjects:
  - kind: ServiceAccount
    name: admin-user
    namespace: kubernetes-dashboard
bash
kubectl apply -f admin-user.yaml
kubectl get secret -n kubernetes-dashboard | grep admin
  admin-user-token-xxxx             kubernetes.io/service-account-token   3
kubectl describe secret -n kubernetes-dashboard admin-user-token-xxxxx

表示されたtokenをコピーし、

トークンを選択し、トークンを入力にペーストし、サインインを行う。

dashboard

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

Docker+Laravelでタスクスケジューラ

目的

通常サーバのタスクスケジューラはcronにて行うが、

  • 少し面倒であること
  • Laravelにタスクスケジューラの機能があること

から、画面を作成して画面経由でタスクスケジュール設定を行えるものを作成してみる。

前提

下記の環境で実装した。

  • PHP7.1.x
  • Laravel6.x+AdminLTE3
  • PostgreSQL11.4
  • Docker phpfpm

サンプル仕様

サーバのタスクスケジュールのため、画面操作するためにログインが必要とした。

タスク自体は別途実装済みであるとして、タスク名と実行スケジュールを設定する画面を
用意する。

タスクの実行はLaravelにタスクスケジューラをcron経由で実行するものとして、タスクスケジューラの設定を先の画面で設定したものを反映させるものとする。

タスクの実装

詳細は省略するが、タスクの雛形をartisanで作成できる。

% php artisan make:command タスク名

とりあえず、タスクが動いたか確認したいため、下記のタスクを実装した。

<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;
use Illuminate\Support\Facades\Log;

class Shout extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'command:shout';

〜省略〜

    /**
     * Execute the console command.
     *
     * @return mixed
     */
    public function handle()
    {
        Log::info("start shout!!");
    }
}

見ての通り、実行されたらログが出力されるだけのタスクである。

モデル定義

定義するモデルはScrapingCommandである。
migrateの内容を下記に示す。

 ?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class CreateScrapingCommandsTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('scraping_commands', function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->string("name",200);
            $table->string("cmd",200);
            $table->integer("cron1")->nullable();
            $table->integer("cron2")->nullable();
            $table->integer("cron3")->nullable();
            $table->integer("cron4")->nullable();
            $table->integer("cron5")->nullable();
            $table->integer("flg")->default(0);
            $table->softDeletes();
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('scraping_commands');
    }
}

画面実装

ログイン画面

AdminLTEのものをそのまま使用した。

タスク一覧画面

登録されているタスクの一覧表示している。

タスクスケジュールはcronと同じ形式である。スケジュールとは別に有効/無効の設定が可能である。
img_tsk2.png

タスク設定画面

タスクの設定を行う。コマンド名はartisanで使用するコマンド名(\$signatureに設定しているもの)を設定する。

設定内容はScrapingCommandモデルに保存される。
img_tsk3.png

Laravel側のスケジュール定義

LaravelのタスクスケジュールはApp\Console\Kernelのscheduleメソッドで定義するため、
同メソッドを下記のように修正した。

<?php

namespace App\Console;

use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Support\Facades\Log;
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
use App\ScrapingCommand;

class Kernel extends ConsoleKernel
{
〜省略〜
    /**
     * Define the application's command schedule.
     *
     * @param  \Illuminate\Console\Scheduling\Schedule  $schedule
     * @return void
     */
    protected function schedule(Schedule $schedule)
    {
        $lst = ScrapingCommand::where("flg",1)->get();
        foreach($lst as $rec) {
            Log::info("cmd:".$rec->cmd);
            Log::info("cron:".$rec->cronStr());
            $schedule->command($rec->cmd)->cron($rec->cronStr());
        }
    }
〜省略〜
}

Cron側の設定

Cron側はLaravelのタスクスケジューラを実行するだけなので、

* * * * * /usr/local/bin/php /svr/app/artisan schedule:run >> /var/log/cron.log 2>&1

(注)/svr/appはLaravelをインストールしたディレクトリ  

本来はcrontab -e で上記のファイルを作成することになるが、今回はDockerなので、
rootファイルとして作成しておく。

以上で、要望を実装済みであるが、今回はDockerで実装したため、コンテナ上でcronのインストールが必要になる。

そこで、cronのインストールはDockerイメージ作成時に行う。よってDockerファイルは

FROM php:fpm

# For composer
RUN apt-get update \
    && apt-get install -y gcc make zlib1g-dev libzip-dev libpng-dev unzip libmcrypt-dev libjpeg-dev libfreetype6-dev \
    && docker-php-ext-install zip \
    && docker-php-ext-configure gd --with-freetype-dir=/usr/include/ --with-jpeg-dir=/usr/include/ --with-png-dir=/usr/include \
    && docker-php-ext-install -j$(nproc) gd

# Install composer
RUN php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" \
  && php composer-setup.php \
  && php -r "unlink('composer-setup.php');" \
  && mv composer.phar /usr/local/bin/composer

# Set composer environment
ENV COMPOSER_ALLOW_SUPERUSER 1
ENV COMPOSER_HOME /composer
ENV PATH $PATH:/composer/vendor/bin

# Install laravel installer
RUN composer global require "laravel/installer"

# PHP's DB setting
RUN apt-get update \
    && apt-get install -y libpq-dev \
    && docker-php-ext-install pdo_mysql pdo_pgsql

# Install Node.js
RUN curl -sL https://deb.nodesource.com/setup_6.x | bash - \
    && apt-get update \
    && apt-get install -y nodejs

RUN cp /usr/share/zoneinfo/Asia/Tokyo /etc/localtime
RUN apt-get install -y cron

RUN mkdir /debugbar
RUN chmod go+w /debugbar
WORKDIR /svr

とした。

次にcrontabの登録とcron起動が必要になるが、今回は手動で行うものとした。
よって、上記で作成したイメージからコンテナを起動して、コンテナ上で

% cp root /var/spool/cron/crontab
% service cron start

を実行する。
rootファイルはあらかじめ/svrにコピーしておくこと。

考察

cronの設定って結構面倒なものである。何よりわざわざサーバにログインして作業しないと行けないのである。
この仕組みだと、画面で設定するだけなので大分楽である。タスクはLaravel上で動くため、
DB接続とかログ出力とかの考慮が必要なくなる。

問題点は

  • crontabの設定とcronの起動をコンテナ起動後手動で行わなければならない。
    Dockerイメージでcronの起動もできるらしいが、今回は割愛した
  • 5分毎等のスケジュールに対応できてない
    これは実装の問題であるが。。。

サンプルで実装したプロジェクトはGitHubにアップしたので、必要であれば参照していただきたい。

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

pip環境のdockerでjupyter notebookを使いたかった(opticspy)

背景

  • Dockerで環境構築してpythonコードを書く際,画像の結果を見るのにjupyter notebookが楽
  • opticspyというモジュールを使ったシミュレーションをしようとした時,pip installが前提となっていた
  • Jupyterは普段anaconda環境下で使っているので,どうしたら良いか調べながら対処した

Dockerfile

FROM continuumio/anaconda3:latest


RUN apt-get update && apt-get upgrade -y \
 && apt-get install -y \
    git \
    make \
    build-essential \
    libssl-dev \
    zlib1g-dev \
    libbz2-dev \
    libreadline-dev \
    libsqlite3-dev \
    wget \
    curl \
    llvm \
    libncurses5-dev \
    libncursesw5-dev \
    xz-utils \
    tk-dev \
    libffi-dev \
    liblzma-dev \
    vim

RUN conda create -n p37 python=3.7 pip

実行コード

terminal.sh
$ docker run -it -p 8881:8881 -v /hogehoge/:/mnt/ --name pip_in_conda pip_in_conda:1.0
$ source activate p37
$ pip install jupyter
$ pip install environment_kernels
$ jupyter notebook --generate-config
$ vim /root/.jupyter/jupyter_notebook_config.py

vimで追記する内容は以下の通り

jupyter_notebook_config.py
c.NotebookApp.kernel_spec_manager_class='environment_kernels.EnvironmentKernelSpecManager'
c.EnvironmentKernelSpecManager.env_dirs=['/opt/conda/envs/']
terminal.sh
$ pip install cffi
$ pip install numpy
$ pip install git+git://github.com/Sterncat/opticspy.git@master
$ pip install pyyaml
$ jupyter notebook --port 8881 --ip=0.0.0.0 --allow-root

opticspyのサンプルコード実行の様子

screencapture-127-0-0-1-8881-notebooks-mnt-05-lens-design-opticspy-sample-double-gauss-sample01-ipynb-2020-01-19-15_23_23.png

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

DockerでTesseractの環境を構築してみた。。。

はじめに

Googleが開発したTesseract(テッセラクト)の環境をDockerを使用して作成します。

目次

  1. aptでインストール
  2. Gitからソースを持って来てコンパイル
  3. まとめ

1. aptで簡単にインストール

公式のGitHubを参考にします。

FROM ubuntu:18.04

RUN apt-get update \
    && apt-get upgrade -y \
    && apt-get install -y \
    tesseract-ocr \
    libtesseract-dev \
    && apt-get clean \
    && rm -rf /var/lib/apt/lists/*

Dockerfileのビルド
docker build --rm -f "tesseract/Dockerfile" -t tesseract:apt-install "tesseract"

ビルドが完了したらコンテナを立ち上げます。
tesseract 4.0.0-beta.1がインストールされていることが確認できます。

tesseractのコンテナ
root@99207ebd4115:/# tesseract -v
tesseract 4.0.0-beta.1
 leptonica-1.75.3
  libgif 5.1.4 : libjpeg 8d (libjpeg-turbo 1.5.2) : libpng 1.6.34 : libtiff 4.0.9 : zlib 1.2.11 : libwebp 0.6.1 : libopenjp2 2.3.0

 Found AVX2
 Found AVX
 Found SSE
root@99207ebd4115:/# 

2. Gitからソースを持って来てコンパイル

次に公式のgitからソースコードを持って来てコンパイルしましょう。
公式のGitHubを参考にします。
ビルドに20分ほどかかります。

FROM ubuntu:18.04

RUN apt-get update \
    && apt-get upgrade -y \
    && apt-get install -y \
    automake \
    ca-certificates \
    g++ \
    git \
    libtool \
    make \
    pkg-config \
    wget \
    libicu-dev \
    libpango1.0-dev \
    libcairo2-dev \
    && apt-get install -y --no-install-recommends \
    asciidoc \
    docbook-xsl \
    xsltproc \
    && apt-get clean \
    && rm -rf /var/lib/apt/lists/*

WORKDIR /opt

ENV TESSERACT_VER="4.1"

RUN wget http://www.leptonica.org/source/leptonica-1.79.0.tar.gz \
    && git clone -b ${TESSERACT_VER} https://github.com/tesseract-ocr/tesseract.git --single-branch \
    && tar -zxvf leptonica-1.79.0.tar.gz

WORKDIR /opt/leptonica-1.79.0

RUN ./configure \
    && make \
    && make install

WORKDIR /opt/tesseract

RUN ./autogen.sh \
    && ./configure \
    && make \
    && make install \
    && ldconfig \
    && make training \
    && make training-install

Dockerfileのビルド
docker build --rm -f "tesseract/Dockerfile" -t tesseract:compiling "tesseract"

ビルドが完了したらコンテナを立ち上げます。
tesseract 4.1.1-rc2-22-g08899がインストールされていることが確認できます。

tesseractのコンテナ
oot@76fa9e82e37d:/opt# tesseract -v
tesseract 4.1.1-rc2-22-g08899
 leptonica-1.79.0
  libpng 1.6.34 : zlib 1.2.11
 Found AVX2
 Found AVX
 Found FMA
 Found SSE
root@76fa9e82e37d:/opt# 

3. まとめ

Dockerを使用することで、簡単にTesseractの環境が手に入りました。
モデルのトレーニング方法なども追加で書く予定です。
ありがとうございました。

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

[Docker入門]コンテナにsshでアクセスするための設定メモ

[目的]

Dockerの使い方を勉強していて、sshで接続するところで苦労したので、やり方をメモっておきます。初心者が試行錯誤した結果なので、もっと良い方法があると思います。もしご存知でしたら教えていただけますと幸いです。

[やりたいこと]

以下やりたいことです。

  • 公式のubuntu18.04のイメージを使って、sshが設定済みのイメージを作成したい。
  • sshでのアクセスでは公開鍵認証を使用したい。
  • 必要な設定はビルド時、つまりDockerfileで完了させたい。
  • コンテナを起動したら直ぐに、sshコマンドでホスト側からアクセスできる状態にしたい。

以下やりたく無いことです。

  • コンテナにアクセスして、手動で直接設定を修正することはやりたくない。

[この記事でわかること]

ビルド時にsshの設定を完了させて、コンテナを起動した時点でホスト側から公開鍵を使ってsshでアクセスできるようにする方法がわかります。

[環境]

[ホスト側]

  • MacOS Mojave
  • Docker Desktop Community 2.1.0.5

[コンテナ側]

  • ubuntu18.04

[Dockerfile]

作成したDockerfileです。公開鍵(id_rsa.pub)をイメージにコピーしていますが、id_rsa.pubの配置場所は、Dockerfileがあるフォルダ下作成したconfigフォルダです。

Dockerfile
# どのイメージを基にするか
FROM ubuntu:18.04

# 作成したユーザの情報
LABEL maintainter="kuboshu83"

# sshサーバをインストールします
RUN apt-get update && apt-get install -y openssh-server
# これが無いとsshdが起動しないっぽい
RUN mkdir /var/run/sshd
# rootのパスワードをrootpasswdに設定します。ただし、公開鍵認証でのアクセスなので使用しませんが。。
RUN echo 'root:rootpassws' | chpasswd
# sshのrootでのアクセスを許可します。ただし、パスワードでのアクセスは無効
RUN sed -i 's/#PermitRootLogin prohibit-password/PermitRootLogin prohibit-password/' /etc/ssh/sshd_config
# sshのポートを22 => 20022に変更します
RUN sed -i 's/#Port 22/Port 20022/' /etc/ssh/sshd_config
# ホスト側にある公開鍵をイメージ側に登録します
COPY ./config_files/id_rsa.pub /root/.ssh/authorized_keys

EXPOSE 20022
CMD ["/usr/sbin/sshd", "-D"]

[接続方法]

<イメージのビルド>

以下のコマンドでビルドします。今回はイメージの名前とタグをそれぞれdefault_repodefault_tagにしています。

docker build -t default_repo:default_tag .

<コンテナの起動>

以下のコマンドで作成したイメージからコンテナを起動します。

docker run -itd -p 20021:20022 default_repo:default_tag

<ホスト側からコンテナに接続>

ホスト側からは以下のコマンドで接続できます。ホスト(localhost)の20021ポートに接続すると、コンテナ側の20022ポートに
転送されます。

ssh root@localhost -p 20021

[まとめ]

ubuntu18.04を基にsshでアクセスできるイメージをビルドし、ホスト側からsshでアクセスできるようになりました。

[参考]

Dockerドキュメント:sshdデーモン用サービスのDocker化

[補足]

色々試している中で、上以外の方法でも起動できたので一応メモっておきます。ざっくりいうと、sshdの起動をDockerfileで行わず、sshdの再起動用のシェルスクリプト(setup.sh)を作成して、コンテナ起動時に.bashrcで実行するというものです。この方法だと、setup.shに書き込む内容によって、色々設定が可能なので個人的には便利だと思っています。このような方法をやって良いか悪いかは今の僕の知識では判断できませんが(^^;

<作成したファイル>

この方法では、Dockerfileとsshd再起動用のシェルスクリプト(setup.sh)を作成します。

Dockerfile

Dockerfile
# どのイメージを基にするか
FROM ubuntu:18.04

# 作成したユーザの情報
LABEL maintainter="kuboshu83"

# RUN: docker buildする時に実行される
RUN apt-get update && \
apt-get install -y --no-install-recommends openssh-server

# ビルド時にsshd_configを修正
RUN sed -i 's/#PermitRootLogin without-password/PermitRootLogin yes/' /etc/ssh/sshd_config
RUN sed -i 's/#Port 22/Port 20022/' /etc/ssh/sshd_config
# ビルド時に公開鍵を登録
COPY ./config_files/id_rsa.pub /root/.ssh/authorized_keys

# コンテナの起動時にsshdを再起動するようにする
COPY ./config_files/setup.sh /root/
RUN  chmod a+x /root/setup.sh
RUN  echo "/root/setup.sh" >> /root/.bashrc

setup.sh

setup.shの配置場所は、Dockerfileを配置したディレクトリ下に作成したconfigファイルです。

./config/setup.sh
#!/bin/bash 
echo "restart ssh service"
service ssh restart

<接続方法>

接続方法は先ほどと同じです。

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

k8s環境ハンズオン webアプリケーション構築実践編

k8s環境ハンズオン webアプリケーション構築実践編

本セクションではminikubeの中に三層アプリを構築してきます。

  • デバッグ用イメージ作成
  • DBサーバーのイメージ作成
  • DBサーバーの構築(ストレージ)
  • DBサーバーの構築(Pod)
  • DBサーバーの構築(Pod+Secret)
  • DBサーバーの構築(StatefulSet)
  • DBサーバーの構築(HeadlessService)
  • DBサーバーの構築(初期化)
  • APサーバーのイメージ作成
  • APサーバーの構築(Pod+Secret)
  • APサーバーの構築(Deployment)
  • APサーバーの構築(Service)
  • WEBサーバーのイメージ作成
  • WEBサーバーの構築(Pod)
  • WEBサーバーの構築(Pod+ConfigMap)
  • WEBサーバーの構築(Deployment)
  • WEBサーバーの構築(Service)
  • Webアプリケーションの公開(Ingress)

作成するアプリ

minikubeの中に三層アプリを構築

web→ap→db→localstorage


デバッグ用イメージ作成

実装
1. 以下の3ファイルを作成
mongodb-org-4.0.repo
Dockerfile
debug-pod.yml

確認
Docker
1. Dockerで作成したイメージを実行
2. 実行中コンテナに入る
3. 必要なコマンドが実行可能なことを確認

Kubernetes
1. Podを作成

mongodb-org-4.0.repo
[mongodb-org-4.0]
name=MongoDB Repository
baseurl=https://repo.mongodb.org/yum/redhat/$releasever/mongodb-org/4.0/x86_64/
gpgcheck=1
enabled=1
gpgkey=https://www.mongodb.org/static/pgp/server-4.0.asc
FROM centos:7

COPY . /tmp/debug

RUN  \
# Install MongoDB shell, tools.
mv /tmp/debug/mongodb-org-4.0.repo /etc/yum.repos.d; \
yum install -y mongodb-org-shell-4.0.5 mongodb-org-tools-4.0.5; \
# Install ip , ifconfig command.
yum install -y iproute net-tools; \
# Install jq
curl -o /usr/local/bin/jq -L https://github.com/stedolan/jq/releases/download/jq-1.6/jq-linux64; \
chmod +x /usr/local/bin/jq; \
# Delete cache files.
yum clean all;
debug-pod.yml
apiVersion: v1
kind: Pod
metadata:
  name: debug
  namespace: default
spec:
  containers:
    - name: debug
      image: debug
      imagePullPolicy: Never
      command:
        - "sh"
        - "-c"
      args:
        - |
          while true
          do
            sleep 5
          done

上記3ファイルをvmにうつし、ビルドコマンド実行
(dockerfileにビルド申請を投げかける)

docker build -t debug .

debugイメージの確認

docker image ls

debugイメージを実行する

docker run -it debug sh

入り込んだコンテナで下記のコマンドでipを確認

ip;mongo;ifconfig;

mongodbが正しく動いている確認したら
コンテナを削除する。

docker container prune

続いてk8sで動作させるマニフェストファイルapplyする

kubectl apply -f debug-pod.yml
#動作確認
kubectl get pod

中に入って動作確認する

kubectl exec -it debug sh
ip;mongo;ifconfig;

exit;
#動作確認
kubectl get pod

DBサーバーのイメージ作成

実装
1. 以下の3ファイルを作成
.dockerignore
docker-entrypoint.sh
Dockerfile

確認
1. Dockerで作成したイメージを実行
2. 実行中コンテナに入る
3. MongoDBに接続できることを確認

**/.git
**/.DS_Store
**/node_modules
#! /bin/sh
INIT_FLAG_FILE=/data/db/init-completed
INIT_LOG_FILE=/data/db/init-mongod.log

start_mongod_as_daemon() {
echo 
echo "> start mongod ..."
echo 
mongod \
  --fork \
  --logpath ${INIT_LOG_FILE} \
  --quiet \
  --bind_ip 127.0.0.1 \
  --smallfiles;
}

create_user() {
echo
echo "> create user ..."
echo
if [ ! "$MONGO_INITDB_ROOT_USERNAME" ] || [ ! "$MONGO_INITDB_ROOT_PASSWORD" ]; then
  return
fi
mongo "${MONGO_INITDB_DATABASE}" <<-EOS
  db.createUser({
    user: "${MONGO_INITDB_ROOT_USERNAME}",
    pwd: "${MONGO_INITDB_ROOT_PASSWORD}",
    roles: [{ role: "root", db: "${MONGO_INITDB_DATABASE:-admin}"}]
  })
EOS
}

create_initialize_flag() {
echo
echo "> create initialize flag file ..."
echo
cat <<-EOF > "${INIT_FLAG_FILE}"
[$(date +%Y-%m-%dT%H:%M:%S.%3N)] Initialize scripts if finigshed.
EOF
}

stop_mongod() {
echo
echo "> stop mongod ..."
echo
mongod --shutdown
}

if [ ! -e ${INIT_LOG_FILE} ]; then
  echo 
  echo "--- Initialize MongoDB ---"
  echo 
  start_mongod_as_daemon
  create_user
  create_initialize_flag
  stop_mongod
fi

exec "$@"
FROM alpine:3.9

COPY docker-entrypoint.sh /usr/local/bin

RUN \
adduser -g mongodb -DH -u 1000 mongodb; \
apk --no-cache add mongodb=4.0.5-r0; \
chmod +x /usr/local/bin/docker-entrypoint.sh; \
mkdir -p /data/db; \
chown -R mongodb:mongodb /data/db;

VOLUME /data/db

EXPOSE 27017

ENTRYPOINT [ "docker-entrypoint.sh" ]
CMD [ "mongod" ]

セットしたdockerfileをビルドする。

docker build -t weblog-db:v1.0.0 .

dockerをデーモン起動する。

docker run -d weblog-db:v1.0.0

起動しているかコンテナ一覧確認

[root@localhost ~]# docker container ls
CONTAINER ID        IMAGE                           COMMAND                   CREATED              STATUS              PORTS               NAMES
46aa0fa9b017        2d3813851e87                    "kube-scheduler --bi…"    About a minute ago   Up About a minute                       k8s_kube-scheduler_kube-scheduler-minikube_kube-system_31d9ee8b7fb12e797dc981a8686f6b2b_6
88649a10f187        weblog-db:v1.0.0                "docker-entrypoint.s…"    3 minutes ago        Up 3 minutes        27017/tcp           gifted_spence

コンテナの中に入ってmongodbが動作しているか確認

docker exec -it gifted_spence sh

mongodbが正しく動いている確認

show dbs

dockerコンテナ停止して削除

[root@localhost ~]# docker stop gifted_spence
gifted_spence
[root@localhost ~]# docker system prune

DBサーバーの構築(ストレージ)

実装
1. 以下のファイルを作成
weblog-db-storage.yml

確認
※必要なフォルダが作成されていること

  1. PersistentVolume、PersistentVolumeClaimを作成
  2. 作成したリソースを確認
weblog-db-storage.yml
apiVersion: v1
kind: PersistentVolume
metadata:
  name: storage-volume
  namespace: default
  labels:
    app: weblog
    type: storage
spec:
  storageClassName: slow
  capacity:
    storage: 1Gi
  accessModes:
  - ReadWriteMany
  hostPath:
    path: "/data/storage"
    type: Directory

---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: storage-claim
  namespace: default
  labels:
    app: weblog
    type: storage
spec:
  storageClassName: slow
  accessModes:
  - ReadWriteMany
  resources:
    requests:
      storage: 1Gi

DBサーバーの構築(Pod)

実装
1. 以下のファイルを作成
weblog-db-pod.yml

確認
※必要なフォルダが作成されていること
※作成されたフォルダは空であること

  1. PersistentVolume, PersistentVolumeClaim, Podを作成
  2. MongoDBのファイルが作成されていることを確認
  3. 作成したPodへ入る
  4. MongoDBへ接続確認
weblog-db-pod.yml
apiVersion: v1
kind: PersistentVolume
metadata:
  name: storage-volume
  namespace: default
  labels:
    app: weblog
    type: storage
spec:
  storageClassName: slow
  capacity:
    storage: 1Gi
  accessModes:
  - ReadWriteMany
  hostPath:
    path: "/data/storage"
    type: Directory

---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: storage-claim
  labels:
    app: weblog
    type: storage
spec:
  storageClassName: slow
  accessModes:
  - ReadWriteMany
  resources:
    requests:
      storage: 1Gi

---
apiVersion: v1
kind: Pod
metadata:
  name: mongodb
  namespace: default
  labels:
    app: weblog
    type: database
spec:
  containers:
  - name: mongodb
    image: weblog-db:v1.0.0
    imagePullPolicy: Never
    command:
    - "mongod"
    - "--bind_ip_all"
    volumeMounts:
    - mountPath: /data/db
      name: storage
  volumes:
  - name: storage
    persistentVolumeClaim:
      claimName: storage-claim

DBサーバーの構築(Pod+Secret)

実装
1. keyfile(ランダム文字列)を作成
2. Secretリソースを作成
3. SecretリソースのYAMLを取得
4. weblog-db-pod.ymlへマージ
5. Secretリソースを削除

確認
※必要なフォルダが作成されていること
※作成されたフォルダは空であること

  1. PersistentVolume, Pod, Secretを作成
  2. 作成したPodへ入る
  3. MongoDBへ接続
  4. 設定したユーザー名/パスワードで認証
  5. DB一覧を表示
DFCytNO0mwawyBs25EOwptvC1TicPzI39KkojjhfE7HeR/VvryeNwqdOvOwiZ9jP/pIWIeYUYiktZXfXhOOlyMBEwUCGr+umbmEeududNy4hQCsVe0YOSlX07Xqa//o+mz4Z8pEchN0wUahqpHD5ieFyoMNIVaEcFPF43+3vZovw7M4xaY+xhhnW5etgm/c2g9GaGlJ4aXPXJbHQTylscnivBrmWiIY+JAPc+oFGJL5I3OCiSR1d66gG99ysof76Rtx0MvYgWdX9rSe16odaI3IWtmhI2CLbWpS994nYcWTCbtor3fMC1cs8nNRWy1un+JbbPooMqiRObxRncHap2X+pOKtjYPg+YKX1Htf+XyaH7DrFdZMnsVbAtAgV6Cf67IZym65p8LjjFkhZTbn5ZMgjQinwerkB+QC5hmaQQhAu6z5te+kPrsQg4PQ/JCRL7vweZEzddwlCo8NYGUJXaSG0Lg2k7wDx9oHLTuA/Telit2VrfgRbU6Y6f6FEOczjKnHKgHT09RJa56wdihgHreyWbVCC7BfdFJ2zRj1fG5YEgpkA3pgMsuvUDi6Ai6tNVe3O3jAqCmXghpFbryi9XYgtDsQkpGPRUZyLvNBj5k8qBoiYkiHPWvyIbyB9SX0pcyP10R8zR0TB8pXUZb3Xh1p4VDMTtg5u8Wwf56pIrWe3KYj20P2wWyQ3glDo5GMJxUdoubTxOun2+V0J6Oc0dKlmweqEjAbakJiDdyvty2OggnbgRSNYZInNKVR9nbnPUE/kCSMbNeE5hVVFC0zx6FKHkdxiZFEv8bP/LR4hY49OEZa9vuPRBn2LqflucO7wm5OnD8mN3PY8LlpN6SWCUTGJ2qfwm36vjRKpHxnFH6IcyDVwmbPulCMEnfnL5rKJ/4bZGiE1SQobQGYD/Gbq1UFWok0FMfTFV1wetQfVv32SISJ6PUJM2iThBJWDPNVTmKf28Dlq2NaLFMd1ugKxAWP2fN9YOgw6ZuiiRB/6UbIM7Ms2FWfytGp2oYcy94XP
weblog-db-pod+secret.yml
apiVersion: v1
kind: PersistentVolume
metadata:
  name: storage-volume
  namespace: default
  labels:
    app: weblog
    type: storage
spec:
  storageClassName: slow
  capacity:
    storage: 1Gi
  accessModes:
  - ReadWriteMany
  hostPath:
    path: "/data/storage"
    type: Directory

---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: storage-claim
  labels:
    app: weblog
    type: storage
spec:
  storageClassName: slow
  accessModes:
  - ReadWriteMany
  resources:
    requests:
      storage: 1Gi

---
apiVersion: v1
kind: Secret
metadata:
  name: mongo-secret
  namespace: default
  labels:
    app: weblog
    type: database
type: Opaque
data:
  root_username: YWRtaW4=
  root_password: UGFzc3cwcmQ=
  keyfile: REZDeXROTzBtd2F3eUJzMjVFT3dwdHZDMVRpY1B6STM5S2tvampoZkU3SGVSL1Z2cnllTndxZE92T3dpWjlqUC9wSVdJZVlVWWlrdFpYZlhoT09seU1CRXdVQ0dyK3VtYm1FZXVkdWROeTRoUUNzVmUwWU9TbFgwN1hxYS8vbyttejRaOHBFY2hOMHdVYWhxcEhENWllRnlvTU5JVmFFY0ZQRjQzKzN2Wm92dzdNNHhhWSt4aGhuVzVldGdtL2MyZzlHYUdsSjRhWFBYSmJIUVR5bHNjbml2QnJtV2lJWStKQVBjK29GR0pMNUkzT0NpU1IxZDY2Z0c5OXlzb2Y3NlJ0eDBNdllnV2RYOXJTZTE2b2RhSTNJV3RtaEkyQ0xiV3BTOTk0blljV1RDYnRvcjNmTUMxY3M4bk5SV3kxdW4rSmJiUG9vTXFpUk9ieFJuY0hhcDJYK3BPS3RqWVBnK1lLWDFIdGYrWHlhSDdEckZkWk1uc1ZiQXRBZ1Y2Q2Y2N0laeW02NXA4TGpqRmtoWlRibjVaTWdqUWlud2Vya0IrUUM1aG1hUVFoQXU2ejV0ZStrUHJzUWc0UFEvSkNSTDd2d2VaRXpkZHdsQ284TllHVUpYYVNHMExnMms3d0R4OW9ITFR1QS9UZWxpdDJWcmZnUmJVNlk2ZjZGRU9jempLbkhLZ0hUMDlSSmE1NndkaWhnSHJleVdiVkNDN0JmZEZKMnpSajFmRzVZRWdwa0EzcGdNc3V2VURpNkFpNnROVmUzTzNqQXFDbVhnaHBGYnJ5aTlYWWd0RHNRa3BHUFJVWnlMdk5CajVrOHFCb2lZa2lIUFd2eUlieUI5U1gwcGN5UDEwUjh6UjBUQjhwWFVaYjNYaDFwNFZETVR0ZzV1OFd3ZjU2cElyV2UzS1lqMjBQMndXeVEzZ2xEbzVHTUp4VWRvdWJUeE91bjIrVjBKNk9jMGRLbG13ZXFFakFiYWtKaURkeXZ0eTJPZ2duYmdSU05ZWkluTktWUjluYm5QVUUva0NTTWJOZUU1aFZWRkMweng2RktIa2R4aVpGRXY4YlAvTFI0aFk0OU9FWmE5dnVQUkJuMkxxZmx1Y083d201T25EOG1OM1BZOExscE42U1dDVVRHSjJxZndtMzZ2alJLcEh4bkZINkljeURWd21iUHVsQ01FbmZuTDVyS0ovNGJaR2lFMVNRb2JRR1lEL0dicTFVRldvazBGTWZURlYxd2V0UWZWdjMyU0lTSjZQVUpNMmlUaEJKV0RQTlZUbUtmMjhEbHEyTmFMRk1kMXVnS3hBV1AyZk45WU9ndzZadWlpUkIvNlViSU03TXMyRldmeXRHcDJvWWN5OTRYUAo=

---
apiVersion: v1
kind: Pod
metadata:
  name: mongodb
  namespace: default
  labels:
    app: weblog
    type: database
spec:
  containers:
  - name: mongodb
    image: weblog-db:v1.0.0
    imagePullPolicy: Never
    args:
    - "mongod"
    - "--auth"
    - "--bind_ip_all"
    env:
    - name: "MONGO_INITDB_ROOT_USERNAME"
      valueFrom:
        secretKeyRef:
          name: mongo-secret
          key: root_username
    - name: "MONGO_INITDB_ROOT_PASSWORD"
      valueFrom:
        secretKeyRef:
          name: mongo-secret
          key: root_password
    - name: "MONGO_INITDB_DATABASE"
      value: "admin"
    volumeMounts:
    - mountPath: /data/db
      name: storage
    - mountPath: /home/mongodb
      name: secret
  volumes:
  - name: storage
    persistentVolumeClaim:
      claimName: storage-claim
  - name: secret
    secret:
      secretName: mongo-secret
      items:
      - key: keyfile
        path: keyfile
        mode: 0700

DBサーバーの構築(StatefulSet)

実装
1. 以下のファイルを作成
weblog-db-statefulset.yml

確認
※必要なフォルダが作成されていること
※作成されたフォルダは空であること

  1. PersistentVolume, Secret, StatefulSetを作成
  2. 作成したPodへ入る
  3. MongoDBへ接続
  4. 設定したユーザー名/パスワードで認証
  5. DB一覧を表示
weblog-db-statefulset.yml
apiVersion: v1
kind: PersistentVolume
metadata:
  name: storage-volume-0
  namespace: default
  labels:
    app: weblog
    type: storage
spec:
  storageClassName: slow
  capacity:
    storage: 1Gi
  accessModes:
  - ReadWriteMany
  hostPath:
    path: "/data/pv0000"
    type: Directory

---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: storage-volume-1
  namespace: default
  labels:
    app: weblog
    type: storage
spec:
  storageClassName: slow
  capacity:
    storage: 1Gi
  accessModes:
  - ReadWriteMany
  hostPath:
    path: "/data/pv0001"
    type: Directory

---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: storage-volume-2
  namespace: default
  labels:
    app: weblog
    type: storage
spec:
  storageClassName: slow
  capacity:
    storage: 1Gi
  accessModes:
  - ReadWriteMany
  hostPath:
    path: "/data/pv0002"
    type: Directory

---
apiVersion: v1
kind: Secret
metadata:
  name: mongo-secret
  namespace: default
  labels:
    app: weblog
    type: database
type: Opaque
data:
  root_username: YWRtaW4=
  root_password: UGFzc3cwcmQ=
  keyfile: REZDeXROTzBtd2F3eUJzMjVFT3dwdHZDMVRpY1B6STM5S2tvampoZkU3SGVSL1Z2cnllTndxZE92T3dpWjlqUC9wSVdJZVlVWWlrdFpYZlhoT09seU1CRXdVQ0dyK3VtYm1FZXVkdWROeTRoUUNzVmUwWU9TbFgwN1hxYS8vbyttejRaOHBFY2hOMHdVYWhxcEhENWllRnlvTU5JVmFFY0ZQRjQzKzN2Wm92dzdNNHhhWSt4aGhuVzVldGdtL2MyZzlHYUdsSjRhWFBYSmJIUVR5bHNjbml2QnJtV2lJWStKQVBjK29GR0pMNUkzT0NpU1IxZDY2Z0c5OXlzb2Y3NlJ0eDBNdllnV2RYOXJTZTE2b2RhSTNJV3RtaEkyQ0xiV3BTOTk0blljV1RDYnRvcjNmTUMxY3M4bk5SV3kxdW4rSmJiUG9vTXFpUk9ieFJuY0hhcDJYK3BPS3RqWVBnK1lLWDFIdGYrWHlhSDdEckZkWk1uc1ZiQXRBZ1Y2Q2Y2N0laeW02NXA4TGpqRmtoWlRibjVaTWdqUWlud2Vya0IrUUM1aG1hUVFoQXU2ejV0ZStrUHJzUWc0UFEvSkNSTDd2d2VaRXpkZHdsQ284TllHVUpYYVNHMExnMms3d0R4OW9ITFR1QS9UZWxpdDJWcmZnUmJVNlk2ZjZGRU9jempLbkhLZ0hUMDlSSmE1NndkaWhnSHJleVdiVkNDN0JmZEZKMnpSajFmRzVZRWdwa0EzcGdNc3V2VURpNkFpNnROVmUzTzNqQXFDbVhnaHBGYnJ5aTlYWWd0RHNRa3BHUFJVWnlMdk5CajVrOHFCb2lZa2lIUFd2eUlieUI5U1gwcGN5UDEwUjh6UjBUQjhwWFVaYjNYaDFwNFZETVR0ZzV1OFd3ZjU2cElyV2UzS1lqMjBQMndXeVEzZ2xEbzVHTUp4VWRvdWJUeE91bjIrVjBKNk9jMGRLbG13ZXFFakFiYWtKaURkeXZ0eTJPZ2duYmdSU05ZWkluTktWUjluYm5QVUUva0NTTWJOZUU1aFZWRkMweng2RktIa2R4aVpGRXY4YlAvTFI0aFk0OU9FWmE5dnVQUkJuMkxxZmx1Y083d201T25EOG1OM1BZOExscE42U1dDVVRHSjJxZndtMzZ2alJLcEh4bkZINkljeURWd21iUHVsQ01FbmZuTDVyS0ovNGJaR2lFMVNRb2JRR1lEL0dicTFVRldvazBGTWZURlYxd2V0UWZWdjMyU0lTSjZQVUpNMmlUaEJKV0RQTlZUbUtmMjhEbHEyTmFMRk1kMXVnS3hBV1AyZk45WU9ndzZadWlpUkIvNlViSU03TXMyRldmeXRHcDJvWWN5OTRYUAo=

---

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: mongo
  namespace: default
  labels:
    app: weblog
    type: database
spec:
  selector:
    matchLabels:
      app: weblog
      type: database
  serviceName: db-svc
  replicas: 3
  template:
    metadata:
      name: mongodb
      namespace: default
      labels:
        app: weblog
        type: database
    spec:
      terminationGracePeriodSeconds: 10
      containers:
      - name: mongodb
        image: weblog-db:v1.0.0
        imagePullPolicy: Never
        args:
        - "mongod"
        - "--auth"
        - "--clusterAuthMode=keyFile"
        - "--keyFile=/home/mongodb/keyfile"
        - "--replSet=rs0"
        - "--bind_ip_all"
        env:
        - name: "MONGO_INITDB_ROOT_USERNAME"
          valueFrom:
            secretKeyRef:
              name: mongo-secret
              key: root_username
        - name: "MONGO_INITDB_ROOT_PASSWORD"
          valueFrom:
            secretKeyRef:
              name: mongo-secret
              key: root_password
        - name: "MONGO_INITDB_DATABASE"
          value: "admin"
        volumeMounts:
        - mountPath: /data/db
          name: storage
        - mountPath: /home/mongodb
          name: secret
      volumes:
      - name: secret
        secret:
          secretName: mongo-secret
          items:
          - key: keyfile
            path: keyfile
            mode: 0700
  volumeClaimTemplates:
  - metadata:
      name: storage
    spec:
      storageClassName: slow
      accessModes:
      - ReadWriteMany
      resources:
        requests:
          storage: 1Gi

DBサーバーの構築(HeadlessService)

実装
1. 以下のファイルを作成
weblog-db-fullset.yml

確認
※必要なフォルダが作成されていること
※作成されたフォルダは空であること

  1. PersistentVolume, Secret, StatefulSet, Serviceを作成
  2. 作成したPodへ入る
  3. MongoDBを初期化
  4. レプリカセット構築できていることを確認
weblog-db-fullset.yml
apiVersion: v1
kind: PersistentVolume
metadata:
  name: storage-volume-0
  namespace: default
  labels:
    app: weblog
    type: storage
spec:
  storageClassName: slow
  capacity:
    storage: 1Gi
  accessModes:
  - ReadWriteMany
  hostPath:
    path: "/data/pv0000"
    type: Directory

---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: storage-volume-1
  namespace: default
  labels:
    app: weblog
    type: storage
spec:
  storageClassName: slow
  capacity:
    storage: 1Gi
  accessModes:
  - ReadWriteMany
  hostPath:
    path: "/data/pv0001"
    type: Directory

---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: storage-volume-2
  namespace: default
  labels:
    app: weblog
    type: storage
spec:
  storageClassName: slow
  capacity:
    storage: 1Gi
  accessModes:
  - ReadWriteMany
  hostPath:
    path: "/data/pv0002"
    type: Directory

---
apiVersion: v1
kind: Secret
metadata:
  name: mongo-secret
  namespace: default
  labels:
    app: weblog
    type: database
type: Opaque
data:
  root_username: YWRtaW4=
  root_password: UGFzc3cwcmQ=
  keyfile: REZDeXROTzBtd2F3eUJzMjVFT3dwdHZDMVRpY1B6STM5S2tvampoZkU3SGVSL1Z2cnllTndxZE92T3dpWjlqUC9wSVdJZVlVWWlrdFpYZlhoT09seU1CRXdVQ0dyK3VtYm1FZXVkdWROeTRoUUNzVmUwWU9TbFgwN1hxYS8vbyttejRaOHBFY2hOMHdVYWhxcEhENWllRnlvTU5JVmFFY0ZQRjQzKzN2Wm92dzdNNHhhWSt4aGhuVzVldGdtL2MyZzlHYUdsSjRhWFBYSmJIUVR5bHNjbml2QnJtV2lJWStKQVBjK29GR0pMNUkzT0NpU1IxZDY2Z0c5OXlzb2Y3NlJ0eDBNdllnV2RYOXJTZTE2b2RhSTNJV3RtaEkyQ0xiV3BTOTk0blljV1RDYnRvcjNmTUMxY3M4bk5SV3kxdW4rSmJiUG9vTXFpUk9ieFJuY0hhcDJYK3BPS3RqWVBnK1lLWDFIdGYrWHlhSDdEckZkWk1uc1ZiQXRBZ1Y2Q2Y2N0laeW02NXA4TGpqRmtoWlRibjVaTWdqUWlud2Vya0IrUUM1aG1hUVFoQXU2ejV0ZStrUHJzUWc0UFEvSkNSTDd2d2VaRXpkZHdsQ284TllHVUpYYVNHMExnMms3d0R4OW9ITFR1QS9UZWxpdDJWcmZnUmJVNlk2ZjZGRU9jempLbkhLZ0hUMDlSSmE1NndkaWhnSHJleVdiVkNDN0JmZEZKMnpSajFmRzVZRWdwa0EzcGdNc3V2VURpNkFpNnROVmUzTzNqQXFDbVhnaHBGYnJ5aTlYWWd0RHNRa3BHUFJVWnlMdk5CajVrOHFCb2lZa2lIUFd2eUlieUI5U1gwcGN5UDEwUjh6UjBUQjhwWFVaYjNYaDFwNFZETVR0ZzV1OFd3ZjU2cElyV2UzS1lqMjBQMndXeVEzZ2xEbzVHTUp4VWRvdWJUeE91bjIrVjBKNk9jMGRLbG13ZXFFakFiYWtKaURkeXZ0eTJPZ2duYmdSU05ZWkluTktWUjluYm5QVUUva0NTTWJOZUU1aFZWRkMweng2RktIa2R4aVpGRXY4YlAvTFI0aFk0OU9FWmE5dnVQUkJuMkxxZmx1Y083d201T25EOG1OM1BZOExscE42U1dDVVRHSjJxZndtMzZ2alJLcEh4bkZINkljeURWd21iUHVsQ01FbmZuTDVyS0ovNGJaR2lFMVNRb2JRR1lEL0dicTFVRldvazBGTWZURlYxd2V0UWZWdjMyU0lTSjZQVUpNMmlUaEJKV0RQTlZUbUtmMjhEbHEyTmFMRk1kMXVnS3hBV1AyZk45WU9ndzZadWlpUkIvNlViSU03TXMyRldmeXRHcDJvWWN5OTRYUAo=

---
apiVersion: v1
kind: Service
metadata:
  name: db-svc
  namespace: default
  labels:
    app: weblog
    type: database
spec:
  ports:
  - port: 27017
    targetPort: 27017
  clusterIP: None
  selector:
    app: weblog
    type: database

---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: mongo
  namespace: default
  labels:
    app: weblog
    type: database
spec:
  selector:
    matchLabels:
      app: weblog
      type: database
  serviceName: db-svc
  replicas: 3
  template:
    metadata:
      name: mongodb
      namespace: default
      labels:
        app: weblog
        type: database
    spec:
      terminationGracePeriodSeconds: 10
      containers:
      - name: mongodb
        image: weblog-db:v1.0.0
        imagePullPolicy: Never
        args:
        - "mongod"
        - "--auth"
        - "--clusterAuthMode=keyFile"
        - "--keyFile=/home/mongodb/keyfile"
        - "--replSet=rs0"
        - "--bind_ip_all"
        env:
        - name: "MONGO_INITDB_ROOT_USERNAME"
          valueFrom:
            secretKeyRef:
              name: mongo-secret
              key: root_username
        - name: "MONGO_INITDB_ROOT_PASSWORD"
          valueFrom:
            secretKeyRef:
              name: mongo-secret
              key: root_password
        - name: "MONGO_INITDB_DATABASE"
          value: "admin"
        volumeMounts:
        - mountPath: /data/db
          name: storage
        - mountPath: /home/mongodb
          name: secret
      volumes:
      - name: secret
        secret:
          secretName: mongo-secret
          items:
          - key: keyfile
            path: keyfile
            mode: 0700
  volumeClaimTemplates:
  - metadata:
      name: storage
    spec:
      storageClassName: slow
      accessModes:
      - ReadWriteMany
      resources:
        requests:
          storage: 1Gi

DBサーバーの構築(初期化)

  1. デバッグ用Podを起動
  2. 初期化スクリプトをデバッグ用Podへコピー
  3. デバッグ用Podへ入る
  4. MongoDBへ接続してプライマリを確認して切断
  5. 初期化スクリプトを修正(必要であれば)
  6. 初期化スクリプトを実行
  7. いずれかのMongoDBに接続してデータが入ったことを確認
adduser.js
// use weblog
db.createUser({
  user: "user",
  pwd: "welcome",
  roles: [{
    role: "readWrite", db: "weblog"
  }]
})
drop.js
db.posts.drop();
db.users.drop();
db.privileges.drop();
init.sh
# Create user 
mongo mongodb://mongo-0.db-svc:27017/weblog -u admin -p Passw0rd --authenticationDatabase admin ./adduser.js

# Create collection & insert initial data
mongo mongodb://mongo-0.db-svc:27017/weblog -u admin -p Passw0rd --authenticationDatabase admin ./insert.js
insert.js
//
// posts table
//
db.posts.createIndex({ url: 1 }, { unique: true, background: true });
db.posts.insertMany([{
  url: "/2017/05/hello-nodejs.html",
  published: new Date(2017, 4, 2),
  updated: new Date(2017, 4, 2),
  title: "ようこそ Node.js の世界へ",
  content: "Node.js は おもしろい!",
  keywords: ["Node.js"],
  authors: ["Yuta Sato"]
}, {
  url: "/2017/06/nodejs-basic.html",
  published: new Date(2017, 5, 12),
  updated: new Date(2017, 5, 12),
  title: "Node.js の 基本",
  content: "ちょっと難しくなってきた!?",
  keywords: ["Node.js"],
  authors: ["Yuta Sato"]
}, {
  url: "/2017/07/advanced-nodejs.html",
  published: new Date(2017, 7, 8),
  updated: new Date(2017, 7, 8),
  title: "Node.js 応用",
  content: "Node.js で Excel ファイルが触れるなんて!!",
  keywords: ["Node.js"],
  authors: ["Yuta Sato"]
}]);

//
// users table
//
db.users.createIndex({ email: 1 }, { unique: true, background: true });
db.users.insertMany([{
  email: "yuta.sato@sample.com",
  name: "Yuta Sato",
  password: "77d1fb804f4e1e6059377122046c95de5e567cb9fd374639cb96e7f5cc07dba1", //"qwerty", // "77d1fb804f4e1e6059377122046c95de5e567cb9fd374639cb96e7f5cc07dba1"
  role: "owner"
}]);

//
// privileges table
//
db.privileges.createIndex({ role: 1 }, { unique: true, background: true });
db.privileges.insertMany([
  { role: "default", permissions: ["read"] },
  { role: "owner", permissions: ["readWrite"] }
]);

APサーバーのイメージ作成

実装
1. 以下のファイルを作成

ROOT
│
│ .dockerignore
│ docker-entrypoint.sh
│ Dockerfile
│
│    --- 以下は Node.js アプリ Application フォルダに配置---
│ app.js
│ package.json
│ yarn.lock
│
├─ api
├─ config
├─ lib
├─ log
├─ public
├─ routes
└─ views

確認
※DBサーバーが起動して初期化まで済んでいること

  1. MongoDBのプライマリを確認
  2. MongoDBに対するService, Endpointsを作成(kubectl apply)
  3. Dockerで作成したイメージを実行(docker run)
  4. 作成したNode.jsアプリケーションコンテナへ接続(curl)
  5. MongoDB接続用Service, Endpointsを削除(kubectl delete)

APサーバーの構築(Pod+Secret)

実装
1. 以下のファイルを作成
weblog-app-pod.yml

確認
※DBサーバーが起動して初期化まで済んでいること

  1. Secret, Podを作成(kubectl apply)
  2. デバッグPodを作成して入る(kubectl exec)
  3. APサーバーPodへ接続確認(curl)
weblog-app-pod.yml
apiVersion: v1
kind: Secret
metadata:
  name: mongo-secret
  namespace: default
  labels:
    app: weblog
    type: database
type: Opaque
data:
  root_username: YWRtaW4=
  root_password: UGFzc3cwcmQ=
  weblog_username: dXNlcg==     # user
  weblog_password: d2VsY29tZQ== # welcome
  keyfile: REZDeXROTzBtd2F3eUJzMjVFT3dwdHZDMVRpY1B6STM5S2tvampoZkU3SGVSL1Z2cnllTndxZE92T3dpWjlqUC9wSVdJZVlVWWlrdFpYZlhoT09seU1CRXdVQ0dyK3VtYm1FZXVkdWROeTRoUUNzVmUwWU9TbFgwN1hxYS8vbyttejRaOHBFY2hOMHdVYWhxcEhENWllRnlvTU5JVmFFY0ZQRjQzKzN2Wm92dzdNNHhhWSt4aGhuVzVldGdtL2MyZzlHYUdsSjRhWFBYSmJIUVR5bHNjbml2QnJtV2lJWStKQVBjK29GR0pMNUkzT0NpU1IxZDY2Z0c5OXlzb2Y3NlJ0eDBNdllnV2RYOXJTZTE2b2RhSTNJV3RtaEkyQ0xiV3BTOTk0blljV1RDYnRvcjNmTUMxY3M4bk5SV3kxdW4rSmJiUG9vTXFpUk9ieFJuY0hhcDJYK3BPS3RqWVBnK1lLWDFIdGYrWHlhSDdEckZkWk1uc1ZiQXRBZ1Y2Q2Y2N0laeW02NXA4TGpqRmtoWlRibjVaTWdqUWlud2Vya0IrUUM1aG1hUVFoQXU2ejV0ZStrUHJzUWc0UFEvSkNSTDd2d2VaRXpkZHdsQ284TllHVUpYYVNHMExnMms3d0R4OW9ITFR1QS9UZWxpdDJWcmZnUmJVNlk2ZjZGRU9jempLbkhLZ0hUMDlSSmE1NndkaWhnSHJleVdiVkNDN0JmZEZKMnpSajFmRzVZRWdwa0EzcGdNc3V2VURpNkFpNnROVmUzTzNqQXFDbVhnaHBGYnJ5aTlYWWd0RHNRa3BHUFJVWnlMdk5CajVrOHFCb2lZa2lIUFd2eUlieUI5U1gwcGN5UDEwUjh6UjBUQjhwWFVaYjNYaDFwNFZETVR0ZzV1OFd3ZjU2cElyV2UzS1lqMjBQMndXeVEzZ2xEbzVHTUp4VWRvdWJUeE91bjIrVjBKNk9jMGRLbG13ZXFFakFiYWtKaURkeXZ0eTJPZ2duYmdSU05ZWkluTktWUjluYm5QVUUva0NTTWJOZUU1aFZWRkMweng2RktIa2R4aVpGRXY4YlAvTFI0aFk0OU9FWmE5dnVQUkJuMkxxZmx1Y083d201T25EOG1OM1BZOExscE42U1dDVVRHSjJxZndtMzZ2alJLcEh4bkZINkljeURWd21iUHVsQ01FbmZuTDVyS0ovNGJaR2lFMVNRb2JRR1lEL0dicTFVRldvazBGTWZURlYxd2V0UWZWdjMyU0lTSjZQVUpNMmlUaEJKV0RQTlZUbUtmMjhEbHEyTmFMRk1kMXVnS3hBV1AyZk45WU9ndzZadWlpUkIvNlViSU03TXMyRldmeXRHcDJvWWN5OTRYUAo=

---
apiVersion: v1
kind: Pod
metadata:
  name: nodeapp
  namespace: default
  labels:
    app: weblog
    type: application
spec:
  containers:
  - name: node
    image: weblog-app:v1.0.0
    imagePullPolicy: Never
    ports:
    - containerPort: 3000
    env:
    - name: "MONGODB_USERNAME"
      valueFrom:
        secretKeyRef:
          name: mongo-secret
          key: weblog_username
    - name: "MONGODB_PASSWORD"
      valueFrom:
        secretKeyRef:
          name: mongo-secret
          key: weblog_password
    - name: "MONGODB_HOSTS"
      value: "mongo-0.db-svc:27017,mongo-1.db-svc:27017,mongo-2.db-svc:27017,"
    - name: "MONGODB_DATABASE"
      value: "weblog"
    - name: "MONGODB_REPLICASET"
      value: "rs0"

APサーバーの構築(Deployment)

実装
1. 以下のファイルを作成
weblog-app-deployment.yml

確認
※DBサーバーが起動して初期化まで済んでいること

  1. Secret, Deploymentを作成(kubectl apply)
  2. デバッグPodを作成して入る(kubectl exec)
  3. APサーバーPodへ接続確認(curl)
weblog-app-deployment.yml
apiVersion: v1
kind: Secret
metadata:
  name: mongo-secret
  namespace: default
  labels:
    app: weblog
    type: database
type: Opaque
data:
  root_username: YWRtaW4=
  root_password: UGFzc3cwcmQ=
  weblog_username: dXNlcg==     # user
  weblog_password: d2VsY29tZQ== # welcome
  keyfile: REZDeXROTzBtd2F3eUJzMjVFT3dwdHZDMVRpY1B6STM5S2tvampoZkU3SGVSL1Z2cnllTndxZE92T3dpWjlqUC9wSVdJZVlVWWlrdFpYZlhoT09seU1CRXdVQ0dyK3VtYm1FZXVkdWROeTRoUUNzVmUwWU9TbFgwN1hxYS8vbyttejRaOHBFY2hOMHdVYWhxcEhENWllRnlvTU5JVmFFY0ZQRjQzKzN2Wm92dzdNNHhhWSt4aGhuVzVldGdtL2MyZzlHYUdsSjRhWFBYSmJIUVR5bHNjbml2QnJtV2lJWStKQVBjK29GR0pMNUkzT0NpU1IxZDY2Z0c5OXlzb2Y3NlJ0eDBNdllnV2RYOXJTZTE2b2RhSTNJV3RtaEkyQ0xiV3BTOTk0blljV1RDYnRvcjNmTUMxY3M4bk5SV3kxdW4rSmJiUG9vTXFpUk9ieFJuY0hhcDJYK3BPS3RqWVBnK1lLWDFIdGYrWHlhSDdEckZkWk1uc1ZiQXRBZ1Y2Q2Y2N0laeW02NXA4TGpqRmtoWlRibjVaTWdqUWlud2Vya0IrUUM1aG1hUVFoQXU2ejV0ZStrUHJzUWc0UFEvSkNSTDd2d2VaRXpkZHdsQ284TllHVUpYYVNHMExnMms3d0R4OW9ITFR1QS9UZWxpdDJWcmZnUmJVNlk2ZjZGRU9jempLbkhLZ0hUMDlSSmE1NndkaWhnSHJleVdiVkNDN0JmZEZKMnpSajFmRzVZRWdwa0EzcGdNc3V2VURpNkFpNnROVmUzTzNqQXFDbVhnaHBGYnJ5aTlYWWd0RHNRa3BHUFJVWnlMdk5CajVrOHFCb2lZa2lIUFd2eUlieUI5U1gwcGN5UDEwUjh6UjBUQjhwWFVaYjNYaDFwNFZETVR0ZzV1OFd3ZjU2cElyV2UzS1lqMjBQMndXeVEzZ2xEbzVHTUp4VWRvdWJUeE91bjIrVjBKNk9jMGRLbG13ZXFFakFiYWtKaURkeXZ0eTJPZ2duYmdSU05ZWkluTktWUjluYm5QVUUva0NTTWJOZUU1aFZWRkMweng2RktIa2R4aVpGRXY4YlAvTFI0aFk0OU9FWmE5dnVQUkJuMkxxZmx1Y083d201T25EOG1OM1BZOExscE42U1dDVVRHSjJxZndtMzZ2alJLcEh4bkZINkljeURWd21iUHVsQ01FbmZuTDVyS0ovNGJaR2lFMVNRb2JRR1lEL0dicTFVRldvazBGTWZURlYxd2V0UWZWdjMyU0lTSjZQVUpNMmlUaEJKV0RQTlZUbUtmMjhEbHEyTmFMRk1kMXVnS3hBV1AyZk45WU9ndzZadWlpUkIvNlViSU03TXMyRldmeXRHcDJvWWN5OTRYUAo=

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nodeapp
  namespace: default
  labels:
    app: weblog
    type: application
spec:
  replicas: 3
  selector:
    matchLabels:
      app: weblog
      type: application
  strategy:
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 1
    type: RollingUpdate
  revisionHistoryLimit: 14
  template:
    metadata:
      name: nodeapp
      namespace: default
      labels:
        app: weblog
        type: application
    spec:
      containers:
      - name: node
        image: weblog-app:v1.0.0
        imagePullPolicy: Never
        ports:
        - containerPort: 3000
        env:
        - name: "MONGODB_USERNAME"
          valueFrom:
            secretKeyRef:
              name: mongo-secret
              key: weblog_username
        - name: "MONGODB_PASSWORD"
          valueFrom:
            secretKeyRef:
              name: mongo-secret
              key: weblog_password
        - name: "MONGODB_HOSTS"
          value: "mongo-0.db-svc:27017,mongo-1.db-svc:27017,mongo-2.db-svc:27017,"
        - name: "MONGODB_DATABASE"
          value: "weblog"
        - name: "MONGODB_REPLICASET"
          value: "rs0"

APサーバーの構築(Service)

実装
1. 以下のファイルを作成
weblog-app-fullset.yml

確認
※DBサーバーが起動して初期化まで済んでいること

  1. Secret, Deployment, Serviceを作成(kubectl apply)
  2. デバッグPodを作成して入る(kubectl exec)
  3. APサーバーへService経由で接続(curl)
  4. APサーバーのいずれかにログ出力されていることを確認
weblog-app-fullset.yml
apiVersion: v1
kind: Secret
metadata:
  name: mongo-secret
  namespace: default
  labels:
    app: weblog
    type: database
type: Opaque
data:
  root_username: YWRtaW4=
  root_password: UGFzc3cwcmQ=
  weblog_username: dXNlcg==     # user
  weblog_password: d2VsY29tZQ== # welcome
  keyfile: REZDeXROTzBtd2F3eUJzMjVFT3dwdHZDMVRpY1B6STM5S2tvampoZkU3SGVSL1Z2cnllTndxZE92T3dpWjlqUC9wSVdJZVlVWWlrdFpYZlhoT09seU1CRXdVQ0dyK3VtYm1FZXVkdWROeTRoUUNzVmUwWU9TbFgwN1hxYS8vbyttejRaOHBFY2hOMHdVYWhxcEhENWllRnlvTU5JVmFFY0ZQRjQzKzN2Wm92dzdNNHhhWSt4aGhuVzVldGdtL2MyZzlHYUdsSjRhWFBYSmJIUVR5bHNjbml2QnJtV2lJWStKQVBjK29GR0pMNUkzT0NpU1IxZDY2Z0c5OXlzb2Y3NlJ0eDBNdllnV2RYOXJTZTE2b2RhSTNJV3RtaEkyQ0xiV3BTOTk0blljV1RDYnRvcjNmTUMxY3M4bk5SV3kxdW4rSmJiUG9vTXFpUk9ieFJuY0hhcDJYK3BPS3RqWVBnK1lLWDFIdGYrWHlhSDdEckZkWk1uc1ZiQXRBZ1Y2Q2Y2N0laeW02NXA4TGpqRmtoWlRibjVaTWdqUWlud2Vya0IrUUM1aG1hUVFoQXU2ejV0ZStrUHJzUWc0UFEvSkNSTDd2d2VaRXpkZHdsQ284TllHVUpYYVNHMExnMms3d0R4OW9ITFR1QS9UZWxpdDJWcmZnUmJVNlk2ZjZGRU9jempLbkhLZ0hUMDlSSmE1NndkaWhnSHJleVdiVkNDN0JmZEZKMnpSajFmRzVZRWdwa0EzcGdNc3V2VURpNkFpNnROVmUzTzNqQXFDbVhnaHBGYnJ5aTlYWWd0RHNRa3BHUFJVWnlMdk5CajVrOHFCb2lZa2lIUFd2eUlieUI5U1gwcGN5UDEwUjh6UjBUQjhwWFVaYjNYaDFwNFZETVR0ZzV1OFd3ZjU2cElyV2UzS1lqMjBQMndXeVEzZ2xEbzVHTUp4VWRvdWJUeE91bjIrVjBKNk9jMGRLbG13ZXFFakFiYWtKaURkeXZ0eTJPZ2duYmdSU05ZWkluTktWUjluYm5QVUUva0NTTWJOZUU1aFZWRkMweng2RktIa2R4aVpGRXY4YlAvTFI0aFk0OU9FWmE5dnVQUkJuMkxxZmx1Y083d201T25EOG1OM1BZOExscE42U1dDVVRHSjJxZndtMzZ2alJLcEh4bkZINkljeURWd21iUHVsQ01FbmZuTDVyS0ovNGJaR2lFMVNRb2JRR1lEL0dicTFVRldvazBGTWZURlYxd2V0UWZWdjMyU0lTSjZQVUpNMmlUaEJKV0RQTlZUbUtmMjhEbHEyTmFMRk1kMXVnS3hBV1AyZk45WU9ndzZadWlpUkIvNlViSU03TXMyRldmeXRHcDJvWWN5OTRYUAo=

---
apiVersion: v1
kind: Service
metadata:
  name: app-svc
  namespace: default
  labels:
    app: weblog
    type: application
spec:
  ports:
  - port: 3000
    targetPort: 3000
  selector:
    app: weblog
    type: application

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nodeapp
  namespace: default
  labels:
    app: weblog
    type: application
spec:
  replicas: 3
  selector:
    matchLabels:
      app: weblog
      type: application
  strategy:
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 1
    type: RollingUpdate
  revisionHistoryLimit: 14
  template:
    metadata:
      name: nodeapp
      namespace: default
      labels:
        app: weblog
        type: application
    spec:
      containers:
      - name: node
        image: weblog-app:v1.0.0
        imagePullPolicy: Never
        ports:
        - containerPort: 3000
        env:
        - name: "MONGODB_USERNAME"
          valueFrom:
            secretKeyRef:
              name: mongo-secret
              key: weblog_username
        - name: "MONGODB_PASSWORD"
          valueFrom:
            secretKeyRef:
              name: mongo-secret
              key: weblog_password
        - name: "MONGODB_HOSTS"
          value: "mongo-0.db-svc:27017,mongo-1.db-svc:27017,mongo-2.db-svc:27017,"
        - name: "MONGODB_DATABASE"
          value: "weblog"
        - name: "MONGODB_REPLICASET"
          value: "rs0"

WEBサーバーのイメージ作成

実装
1. 以下のファイルを作成
ROOT
docker-entrypoint.sh
Dockerfile

※nginx.confは添付を利用

確認
※Kubernetes上でDBサーバーとAPサーバーが起動していること

  1. APサーバーへアクセスするServiceを作成
  2. Webサーバーコンテナ起動
  3. 外部からブラウザでアクセスして画面確認
nginx.conf
user  nginx;
worker_processes  auto;

error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;


events {
    worker_connections  1024;
}


http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;

    sendfile        on;

    keepalive_timeout  65;

    server_tokens   off;

    proxy_cache_path /var/cache/nginx keys_zone=STATIC:10m max_size=1g inactive=10d;
    proxy_temp_path  /var/cache/nginx/tmp;

    server {
        listen        80;

        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-Host $host;
        proxy_set_header X-Forwarded-Server $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

        location / {
            proxy_pass http://${APPLICATION_HOST}/;
        }

        location /public/ {
            proxy_pass http://${APPLICATION_HOST}/public/;
            proxy_ignore_headers Cache-Control Expires;
            proxy_buffering on;
            proxy_cache STATIC;
            proxy_cache_valid any 10d;
            add_header X-Nginx-Cache $upstream_cache_status;
        }
    }

    # include /etc/nginx/conf.d/*.conf;
}
docker-entrypoint.sh
#! /bin/sh

envsubst '$$APPLICATION_HOST' \
  < /home/nginx/nginx.conf \
  > /etc/nginx/nginx.conf

exec "$@"
FROM nginx:1.17.2-alpine

COPY . /home/nginx

RUN cd /home/nginx; \
    mv docker-entrypoint.sh /usr/local/bin; \
    chmod +x /usr/local/bin/docker-entrypoint.sh;

ENTRYPOINT ["docker-entrypoint.sh"]
CMD ["nginx", "-g", "daemon off;"]



weblog-app-service.yml
apiVersion: v1
kind: Service
metadata:
  name: nodeapp
  namespace: default
  labels:
    env: study
spec:
  type: NodePort
  selector:
    app: weblog
    type: application
  ports:
  - port: 3000
    targetPort: 3000
    nodePort: 30000

WEBサーバーの構築(Pod)

実装
1. 以下のファイルを作成
weblog-web-pod.yml

確認
※DBサーバーが起動して初期化まで済んでいること
※APサーバーが起動していること

  1. ConfigMap, Deployment, Serviceを作成(kubectl apply)
  2. 作成したWebサーバーPodのIPアドレスを確認(kubectl get)
  3. デバッグPodを作成して入る(kubectl exec)
  4. Webサーバーへ接続(curl)
  5. 接続したWebサーバーにアクセスログがあることを確認
weblog-web-pod.yml
apiVersion: v1
kind: Pod
metadata:
  name: nginx
  namespace: default
  labels:
    app: weblog
    type: frontend
spec:
  containers:
  - name: nginx
    image: weblog-web:v1.0.0
    imagePullPolicy: Never
    ports:
    - containerPort: 80
    env:
    - name: "APPLICATION_HOST"
      value: "app-svc:3000"

WEBサーバーの構築(Pod+ConfigMap)

実装
1. 以下のファイルを作成
weblog-web-pod+configmap.yml

確認
※DBサーバーが起動して初期化まで済んでいること
※APサーバーが起動していること

  1. ConfigMap, Podを作成(kubectl apply)
  2. WebサーバーPodに入ってConfigMapを利用していることを確認(kubectl exec)
  3. WebサーバーPodのIPアドレスを確認(kubectl get)
  4. デバッグPodを作成して入る(kubectl exec)
  5. WebサーバーPodへ接続確認(curl)
weblog-web-pod+configmap.yml
apiVersion: v1
kind: ConfigMap
metadata:
  name: nginx-config
  namespace: default
  labels:
    app: weblog
    type: frontend
data:
  nginx.conf: |
    user  nginx;
    worker_processes  auto;

    error_log  /var/log/nginx/error.log warn;
    pid        /var/run/nginx.pid;


    events {
        worker_connections  1024;
    }


    http {
        include       /etc/nginx/mime.types;
        default_type  application/octet-stream;

        log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                          '$status $body_bytes_sent "$http_referer" '
                          '"$http_user_agent" "$http_x_forwarded_for"';

        access_log  /var/log/nginx/access.log  main;

        sendfile        on;

        keepalive_timeout  65;

        server_tokens   off;

        proxy_cache_path /var/cache/nginx keys_zone=STATIC:10m max_size=1g inactive=10d;
        proxy_temp_path  /var/cache/nginx/tmp;

        server {
            listen        80;

            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-Host $host;
            proxy_set_header X-Forwarded-Server $host;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

            location / {
                proxy_pass http://${APPLICATION_HOST}/;
            }

            location /public/ {
                proxy_pass http://${APPLICATION_HOST}/public/;
                proxy_ignore_headers Cache-Control Expires;
                proxy_buffering on;
                proxy_cache STATIC;
                proxy_cache_valid any 10d;
                add_header X-Nginx-Cache $upstream_cache_status;
            }
        }

        # include /etc/nginx/conf.d/*.conf;
        # ConfigMap
    }

---
apiVersion: v1
kind: Pod
metadata:
  name: nginx
  namespace: default
  labels:
    app: weblog
    type: frontend
spec:
  containers:
  - name: nginx
    image: weblog-web:v1.0.0
    imagePullPolicy: Never
    ports:
    - containerPort: 80
    env:
    - name: "APPLICATION_HOST"
      value: "app-svc:3000"
    volumeMounts:
    - name: config-volume
      mountPath: /home/nginx
  volumes:
  - name: config-volume
    configMap:
      name: nginx-config

WEBサーバーの構築(Deployment)

実装
1. 以下のファイルを作成
weblog-web-deployment.yml

確認
※DBサーバーが起動して初期化まで済んでいること
※APサーバーが起動していること

  1. ConfigMap, Deploymentを作成(kubectl apply)
  2. WebサーバーPodのIPアドレスを確認(kubectl get)
  3. デバッグPodを作成して入る(kubectl exec)
  4. WebサーバーPodいずれかへ接続確認(curl)
weblog-web-deployment.yml
apiVersion: v1
kind: ConfigMap
metadata:
  name: nginx-config
  namespace: default
  labels:
    app: weblog
    type: frontend
data:
  nginx.conf: |
    user  nginx;
    worker_processes  auto;

    error_log  /var/log/nginx/error.log warn;
    pid        /var/run/nginx.pid;


    events {
        worker_connections  1024;
    }


    http {
        include       /etc/nginx/mime.types;
        default_type  application/octet-stream;

        log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                          '$status $body_bytes_sent "$http_referer" '
                          '"$http_user_agent" "$http_x_forwarded_for"';

        access_log  /var/log/nginx/access.log  main;

        sendfile        on;

        keepalive_timeout  65;

        server_tokens   off;

        proxy_cache_path /var/cache/nginx keys_zone=STATIC:10m max_size=1g inactive=10d;
        proxy_temp_path  /var/cache/nginx/tmp;

        server {
            listen        80;

            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-Host $host;
            proxy_set_header X-Forwarded-Server $host;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

            location / {
                proxy_pass http://${APPLICATION_HOST}/;
            }

            location /public/ {
                proxy_pass http://${APPLICATION_HOST}/public/;
                proxy_ignore_headers Cache-Control Expires;
                proxy_buffering on;
                proxy_cache STATIC;
                proxy_cache_valid any 10d;
                add_header X-Nginx-Cache $upstream_cache_status;
            }
        }

        # include /etc/nginx/conf.d/*.conf;
        # ConfigMap
    }

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
  namespace: default
  labels:
    app: weblog
    type: frontend
spec:
  replicas: 3
  selector:
    matchLabels:
      app: weblog
      type: frontend
  strategy:
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 1
    type: RollingUpdate
  revisionHistoryLimit: 14
  template:
    metadata:
      name: nginx
      namespace: default
      labels:
        app: weblog
        type: frontend
    spec:
      containers:
      - name: nginx
        image: weblog-web:v1.0.0
        imagePullPolicy: Never
        ports:
        - containerPort: 80
        env:
        - name: "APPLICATION_HOST"
          value: "app-svc:3000"
        volumeMounts:
        - name: config-volume
          mountPath: /home/nginx
      volumes:
      - name: config-volume
        configMap:
          name: nginx-config

WEBサーバーの構築(Service)

実装
1. 以下のファイルを作成
weblog-web-fullset.yml

確認
※DBサーバーが起動して初期化まで済んでいること
※APサーバーが起動していること

  1. ConfigMap, Deploymentを作成(kubectl apply)
  2. デバッグPodを作成して入る(kubectl exec)
  3. WebサーバーServiceへ接続確認(curl)
weblog-web-fullset.yml
apiVersion: v1
kind: Service
metadata:
  name: web-svc
  namespace: default
  labels:
    app: weblog
    type: frontend
spec:
  ports:
  - port: 80
    targetPort: 80
  selector:
    app: weblog
    type: frontend

---
apiVersion: v1
kind: ConfigMap
metadata:
  name: nginx-config
  namespace: default
  labels:
    app: weblog
    type: frontend
data:
  nginx.conf: |
    user  nginx;
    worker_processes  auto;

    error_log  /var/log/nginx/error.log warn;
    pid        /var/run/nginx.pid;


    events {
        worker_connections  1024;
    }


    http {
        include       /etc/nginx/mime.types;
        default_type  application/octet-stream;

        log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                          '$status $body_bytes_sent "$http_referer" '
                          '"$http_user_agent" "$http_x_forwarded_for"';

        access_log  /var/log/nginx/access.log  main;

        sendfile        on;

        keepalive_timeout  65;

        server_tokens   off;

        proxy_cache_path /var/cache/nginx keys_zone=STATIC:10m max_size=1g inactive=10d;
        proxy_temp_path  /var/cache/nginx/tmp;

        server {
            listen        80;

            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-Host $host;
            proxy_set_header X-Forwarded-Server $host;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

            location / {
                proxy_pass http://${APPLICATION_HOST}/;
            }

            location /public/ {
                proxy_pass http://${APPLICATION_HOST}/public/;
                proxy_ignore_headers Cache-Control Expires;
                proxy_buffering on;
                proxy_cache STATIC;
                proxy_cache_valid any 10d;
                add_header X-Nginx-Cache $upstream_cache_status;
            }
        }

        # include /etc/nginx/conf.d/*.conf;
        # ConfigMap
    }

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
  namespace: default
  labels:
    app: weblog
    type: frontend
spec:
  replicas: 3
  selector:
    matchLabels:
      app: weblog
      type: frontend
  strategy:
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 1
    type: RollingUpdate
  revisionHistoryLimit: 14
  template:
    metadata:
      name: nginx
      namespace: default
      labels:
        app: weblog
        type: frontend
    spec:
      containers:
      - name: nginx
        image: weblog-web:v1.0.0
        imagePullPolicy: Never
        ports:
        - containerPort: 80
        env:
        - name: "APPLICATION_HOST"
          value: "app-svc:3000"
        volumeMounts:
        - name: config-volume
          mountPath: /home/nginx
      volumes:
      - name: config-volume
        configMap:
          name: nginx-config

Webアプリケーションの公開(Ingress)

実装
1. 以下のファイルを作成
weblog-ingress.yml

確認
※DBサーバーが起動して初期化まで済んでいること
※APサーバーが起動していること
※Webサーバーが起動していること

  1. Ingressを作成(kubectl apply)
  2. minikubeへ接続確認(ブラウザ)
weblog-ingress.yml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: entrypoint
  namespace: default
  annotations:
    kubernetes.io/ingress.class: "nginx"
    nginx.ingress.kubernetes.io/ssl-redirect: "false"
  labels:
    app: weblog
    type: entrypoint
spec:
  rules:
  - http:
      paths:
      - path: /
        backend:
          serviceName: web-svc
          servicePort: 80
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

GCP+DockerでHTTPSな静的ページを公開する

概要

なにかかっこいいのでポートフォリオサイトが欲しかったというのと,GCPとDockerまともに触ったことがなかったのでこういう風に公開しました.
これはポートフォリオに限らず,一般的な静的ページなら当てはめられます.

手順

1. ドメインを取得してIPと紐づける

僕はGoogleDomainsで取得して設定しました.

2. Google Compute Engine(GCE) のインスタンスを立てる

OSはdebian9を選択する.
httpとhttpsのportを開放することを忘れずに.

3. GCE 上に Docker をインストールする

$ apt-get install curl
$ curl -fsSL https://get.docker.com -o get-docker.sh
$ sudo sh get-docker.sh
$ systemctl start docker

4. GCE 上に git をインストールする

ローカルのソースコードを GCE 上にコピーします.

$ apt-get install git
$ git clone https://github.com/[Your Account]/[Your Repository].git

プライベートリポジトリなら,Githubに鍵登録をしましょう.
Github以外の方法でソースコードをコピーしても問題ありません.

ディレクトリ構成

.
├── Dockerfile
├── docker-compose.yml
└── src
    ├── css 
    ├── favicon.ico
    ├── img
    ├── index.html
    └── js

5. GCE 上に Docker で HTTPS な Nginx を立てる

https-portalを利用します.
https://github.com/SteveLTN/https-portal

これは普通はproxyとしてHTTPS化するものですが,今回はproxyとしてではなくhttp-portl自体のNginxから静的ファイルを配信します.
git でクローンしたディレクトリに入りイメージをビルドします.

公開したいテンプレートsrc/var/www/vhosts/www.[Your Domain].com/に配置しコンテナにコピーします.
srcを変更した場合、変更がそのまま反映されます.
こうすることで,imageをビルドしなおすことなくページ内容の変更を反映させることができます.

証明書をボリュームにマウントしておき永続化します.
コンテナを立ち上げるため証明書を取得すると発行制限がかかる場合があり,僕は1週間くらい発行することができなくなりました.

$ docker build ./ -t  portfolio-https
$ docker run -v {srcへの絶対パス}:/var/www/vhosts/www.[Your Domain].com/ -v /data/ssl_certs:/var/lib/https-portal -p 80:80 -p 443:443 portfolio-https

もしくは

docker-compose.yml
version: "3"

services:
  https-portal:
    image: steveltn/https-portal:1
    container_name: portfolio-https-portal
    ports:
      - "80:80"
      - "443:443"
    restart: always
    environment:
      STAGE: "production"
      DOMAINS: >-
        www.[Your Domain].com
    volumes:
      - ./src:/var/www/vhosts/www.[Your Domain].com
      - /data/ssl_certs:/var/lib/https-portal
docker-compose up -d

まとめ

以上の手順でページを公開できたと思います.

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

TruffleBox (React&Truffle)を用いたDockerでのdapps(ブロックチェーンアプリ)の開発環境の構築

やったこと

  • Docker上でTrufflebox(React&Truffle)を用いたdappsの開発環境を構築した。
  • ホスト側のIPアドレスを設定することで、Dockerのコンテナからホストで立ち上がっているganacheに接続できるようにした。

今回の成果

231us-n7dpv.gif

環境構築手順

各種ファイルの用意

まずは、docker-compose.ymlとDockerfileを用意します。
スクリーンショット 2020-01-19 11.03.12.png

docker-compose.yml

docker-compose.yml
version: '3'

services:
  truffle:
    build: 
      context: ./truffle/
      dockerfile: Dockerfile
    volumes:
      - ./truffle:/usr/src/app
    command: sh -c "cd client && yarn start"
    ports:
      - "8003:3000"

DockerFile

Dockerfile
FROM node:8-alpine  

RUN apk add --update alpine-sdk
RUN apk add --no-cache git python g++ make \
    && npm i -g --unsafe-perm=true --allow-root truffle 

WORKDIR /usr/src/app

コマンドの実行

$ docker-compose build
$ docker-compose run truffle truffle unbox react
✔ cleaning up temporary files
✔ Setting up box

truffle-config.jsの編集

truffle-config.js
const path = require("path");

module.exports = {
  // See <http://truffleframework.com/docs/advanced/configuration>
  // to customize your Truffle configuration!
  networks: {
    development: {
      host: "10.200.10.1",
      port: 7545,
      network_id: "*" // Match any network id
    }
  },
  contracts_build_directory: path.join(__dirname, "client/src/contracts")
};

ホストOS側でのIPアドレスの設定

ganacheを使って、ローカル開発環境上でEthereumのブロックチェーンを構築するのですが、
Mac(ホストOS)上で立ち上がってるganacheにDockerのコンテナ上のtruffleからアクセスできるようにするために、Mac側のIPアドレスを独自に設定します。

MacのTerminalで
$ sudo ifconfig lo0 alias 10.200.10.1/24
$ ifconfig
lo0:
    inet 127.0.0.1 netmask 0xff000000 
        ...
    inet 10.200.10.1 netmask 0xffffff00 

これにより、10.200.10.1でDockerからホストOSへアクセスできるようになった。
参考記事: https://qiita.com/ynii/items/262d2344b9e1ef4d2d88

netmaskとganacheの設定

参考記事に従って、設定してください。
参考記事: https://qiita.com/kane-hiro/items/b1381cc1c8dd5559a9d2

ganacheの設定で、一つだけ追加の設定が必要です。
上で設定したIPアドレス:10.200.10.1でサーバーを立ててほしいので、その設定を行います。
設定 -> server -> lo0:10.200.10.1に変更
スクリーンショット 2020-01-19 12.36.37.pngスクリーンショット 2020-01-19 12.36.51.png

truffle-config.jsonの設定

truffleが参照するganacheサーバーの情報(IPとポート)について、記述していきます。

truffle-config.js
const path = require("path");

module.exports = {
  // See <http://truffleframework.com/docs/advanced/configuration>
  // to customize your Truffle configuration!
  networks: {
    development: {
      host: "10.200.10.1",
      port: 7545,
      network_id: "*" // Match any network id
    }
  },
  contracts_build_directory: path.join(__dirname, "client/src/contracts")
};

migrateとフロントの立ち上げ

$ docker-compose run truffle truffle migrate
$ docker-compose up

ハマったポイント

DockerFileの記述はかなりハマりました。

npm install truffleのパーミッションエラーを回避する

$ npm i -g truffle
...
EACCES: permission denied, open '/root/.config/truffle/config.json

何も考えずにnpm i -g truffleすると、エラーを吐くと思います。
これは、

  • Dockerでnpm installするときは、rootでインストールを始めようとする
  • --unsafe-permオプションをtrueにしないとrootでnpm installができないようになっている?(rootでのインストールが推奨されていないから???)

参考記事: https://qiita.com/village_21/items/8ed91270271261752c8a

gitとかpythonを入れましょう。

何もいれないと、truffle unbox reactでエラーを吐きます。

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

Pytestの実行時にDockerコンテナを起動する

Pytestのテスト実行前にDockerコンテナを起動することを考える。
周辺ミドルウェアとのインテグレーションテストが容易に行えるようになり、環境やテストデータや冪等性を得られるメリットがある。

例としてテスト実行時にPostgreSQLコンテナを起動し、データを投入してみる。

備考

コード例の前に、ユニットテストの中でDockerコンテナを使用することの制約などについて考えてみる。

  • Dockerデーモンが起動している必要がある

    -> 当然といえば当然だが重要。コンテナの中でテスト実行したいようなケースにはハードルが1つ増える(?)

  • コンテナ起動まで待機する必要あり

    • コンテナ自体の起動は高速だが、内部のプロセスが起動完了して使用可能になるまでは数秒かかる場合が多い。
    • テスト関数が増えてくると全テストケース実行時間が大幅に増加してしまう
    • なるべく軽量なコンテナイメージを使う方が良い
    • コンテナの標準出力をウォッチすることで準備完了になるまで適切に待機するよう工夫する

使用ライブラリ

  • pytest: ユニットテスト
  • docker: Docker APIのラッパー - docker - PyPI
  • SQLAlchemy: ORM
  • psycopg2: PostgreSQL接続ドライバ
$ pip install docker pytest SQLAlchemy psycopg2-binary

構成

├── main.py
├── models.py
└── tests
    ├── __init__.py
    ├── conftest.py
    └── test_main.py

メイン処理/DBモデル

  • models.py
models.py
from sqlalchemy import Column, Integer, String
from sqlalchemy.ext.declarative import declarative_base


Base = declarative_base()


class Staff(Base):
    __tablename__ = 'staff'
    id = Column(Integer, primary_key=True)
    name = Column(String)
  • main.py
main.py
from sqlalchemy.orm import Session
from models import Staff


def add_staff(engine, name):
    session = Session(bind=engine)
    staff = Staff()
    staff.name = name
    session.add(staff)
    session.commit()

テストコード

SQLAlchemyのengineをフィクスチャにすることでテスト関数で使用できるようにするのがポイント。

  • conftest.py
tests/conftest.py
import time
import pytest
import docker
from sqlalchemy import create_engine


@pytest.fixture()
def pg_conf():
    """PostgreSQLの設定を管理"""
    host = '127.0.0.1'
    port = 5432
    dbname = 'pytest'
    user = 'testuser'
    password = 'test'
    pg_conf = {'host': host,
               'port': port,
               'dbname': dbname,
               'user': user,
               'password': password,
               'url': f'postgresql://{user}:{password}@{host}/{dbname}'}
    return pg_conf


@pytest.fixture()
def engine(pg_conf):
    return create_engine(pg_conf['url'])


@pytest.fixture(autouse=True)
def pg_container(pg_conf):
    """PostgreSQLコンテナを起動する"""
    client = docker.from_env()
    container = client.containers.run(image='postgres:11.6-alpine',
                                      tty=True,
                                      detach=True,
                                      auto_remove=True,
                                      environment={'POSTGRES_DB': pg_conf['dbname'],
                                                   'POSTGRES_USER': pg_conf['user'],
                                                   'POSTGRES_PASSWORD': pg_conf['password']},
                                      ports={pg_conf['port']: '5432'})
    # コンテナが準備完了になるまで待機
    while True:
        log = container.logs(tail=1)
        if 'database system is ready to accept connections' in log.decode():
            break
        time.sleep(0.5)
    yield  # ここでテストに遷移
    container.kill()

コンテナ内の標準出力をチェックしているが、待機間隔が短すぎる(0.4秒間隔以下)とエラーが発生してしまった。
少し猶予を持った待機時間にしたほうが良さそう。

  • test_main.py
test_main.py
from sqlalchemy.orm import Session
from models import Base, Staff
from main import add_staff


def test_add(engine):
    # レコードを1件追加
    Base.metadata.create_all(bind=engine)  # テーブルを作成
    add_staff(engine=engine,
              name='alice')
    # 追加したレコードをチェック
    session = Session(bind=engine)
    assert session.query(Staff.id).filter_by(name='alice').first() == (1,)
    session.close()

実行結果

$ pytest --setup-show tests/ -v -s
========================================= test session starts =========================================platform linux -- Python 3.8.1, pytest-5.3.3, py-1.8.1, pluggy-0.13.1 -- /home/skokado/.local/share/virtualenvs/sandbox-pTebjwBw/bin/python3.8
cachedir: .pytest_cache
rootdir: ***
collected 1 item

tests/test_pg.py::test_add
        SETUP    F pg_conf
        SETUP    F pg_container (fixtures used: pg_conf)
        SETUP    F engine (fixtures used: pg_conf)
        tests/test_main.py::test_add (fixtures used: engine, pg_conf, pg_container)PASSED
        TEARDOWN F engine
        TEARDOWN F pg_container
        TEARDOWN F pg_conf

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

k8s環境ハンズオン k8s各種リソース解説編

k8s各種リソース解説編

本セクションでは主要なKubernetesリソースの使い方を学習していきます。

  • Pod
  • ReplicaSet
  • Deployment
  • Service
  • ConfigMap
  • Secret
  • PersistentVolume
  • PersistentVolumeClaim
  • StatefulSet
  • Ingress

Kubernetesを使った開発を行っていく上で基本となるリソースの使い方を学習していきます。

各リソースの特徴や記述方法について学習し、使えるようになることを目指しましょう!


Pod

podとは、最小単位。同一環境で動作するdockerコンテナ集合

1つのpodに複数のdockerコンテナが入れられる。

  • マニフェストファイル書き方
pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: sample
spec:
  containers:
  - name: nginx
    image: nginx:1.17.2-alpine
    volumeMounts:
    - name: storage
      mountPath: /home/nginx
  volumes:
  - name: storage
    hostPath:
      path: "/data/storage"
      type: Directory

imagePullpolicyの各種オプション

種別 説明
Always 毎回リポジトリからダウンロードを行う
Never ローカルのイメージを利用
ifNotpresent ローカルに存在すればローカルを利用し存在しなければリポジトリからダウンロード

マニフェスト設定は下記の通り

種別 説明
spec:containers:name コンテナ名指定
spec:containers:image イメージ名指定
spec:containers:command コンテナへ引き渡すエントリポイントのコマンドを指定
spec:containers:args コンテナへ引き渡すcmdコマンドを指定
spec:containers:env コンテナへ引き渡す環境変数を指定
spec:containers:volumeMounts コンテナへマウントするストレージを指定

主要なspecは containersとvolumes

spec:volumes
マウントしたいストレージ先は状況に応じて選択

spec:volumes:name
ストレージ名を指定

spec:volumes:hostPath
保存先がpod実行サーバのフォルダ

spec:volumes:hostPath:typeの各種オプション

種別 説明
Directory 存在するディレクトリ
DirectoryOrCreate ディレクトリが存在しなければ作成
File 存在するファイル
FileOrCreate ファイルが存在しなければ作成

spec:volumes:nfs
保存先がnfsサーバのフォルダ

spec:volumes:configmap
configmapリソースをファイルとしてマウントさせる

spec:volumes:sercret
sercretリソースをファイルとしてマウントさせる。

spec:volumes:emptydir
一時的な空フォルダ

演習:pod作成
- ホストにフォルダ、ファイルを作成
- 作成したフォルダをマヌントしたpodマニフェストファイルを作成
- リソース作成

テストファイルとしてmessageファイルを/data/storage配下に置く。

vi /data/storage/message
hello k8s

下記のマニフェストファイルを作業ディレクトリ配下に置く

pod.yml
apiVersion: v1
kind: Pod
metadata:
  name: sample
spec:
  containers:
  - name: nginx
    image: nginx:1.17.2-alpine
    volumeMounts:
    - name: storage
      mountPath: /home/nginx
  volumes:
  - name: storage
    hostPath:
      path: "/data/storage"
      type: Directory

マニフェストファイルを元にpod作成する。

kubectl apply -f pod.yml

作成したpodがマウントされているか確認する。

kubectl exec -it pod/sample sh

続いてマウントしているはずであるフォルダに移動する。

cd /home/nginx/
ls
cat message

うまくいったらpodを削除。

kubectl delete -f pod.yml

ReplicaSet

ReplicaSetとは、podの集合。podをスケールできる。

1つのpodに複数のdockerコンテナが入れられる。

  • マニフェストファイル

主要なspecはreplicas,selector,template

replicaset.yml
apiVersion: apps/v1
kind: ReplicaSet
metadata:
  name: nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      app: web
      env: study
  template:
    metadata:
      name: nginx
      labels:
        app: web
        env: study
    spec:
      containers:
      - name: nginx
        image: nginx:1.17.2-alpine

上記のマニフェストファイルを作業ディレクトリに配置してreplicaset.ymlを作成する。

kubectl apply -f replicaset.yml

作成したreplicaset.ymlの動作確認をする。

kubectl get all
NAME                    DESIRED   CURRENT   READY   AGE
replicaset.apps/nginx   3         0         0       9s

スケールアウトを試し見る。
replicasの数値を2にしてみる(更新)

NAME                    DESIRED   CURRENT   READY   AGE
replicaset.apps/nginx   2         0         0       9s

削除。

kubectl delete -f replicaset.yml

Deployment

deployment.yml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
  annotations:
    kubernetes.io/change-cause: "First release."
spec:
  replicas: 2
  selector:
    matchLabels:
      app: web
      env: study
  revisionHistoryLimit: 14
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 1
  template:
    metadata:
      name: nginx
      labels:
        app: web
        env: study
    spec:
      containers:
      - name: nginx
        image: nginx:1.17.2-alpine

上記のマニフェストファイルを作業ディレクトリに配置してDeploymentを作成する。

kubectl apply -f deployment.yml

作成したDeploymentの動作確認をする。

kubectl get all

ロールアウトを試みる。(更新)

kubectl rollout history deploy/nginx

初回は何も表示されない

deployment.extensions/nginx
No rollout history found.

vi deployment.yml
kubernetes.io/change-cause: ここの箇所を書き換えて再apply

kubectl rollout history deploy/nginx

一度試みたロールアウトの履歴が確認できる。

deployment.extensions/nginx
REVISION  CHANGE-CAUSE
1         second release.

ロールバックを試みる。(巻き戻り)

kubectl rollout undo deploy/nginx

kubernetes.io/change-cause: ここの箇所を書き換えて再apply

kubectl rollout history deploy/nginx

削除。

kubectl delete -f deployment.yml

Service

service.yml
apiVersion: v1
kind: Pod
metadata:
  name: nginx
  labels:
    app: web
    env: study
spec:
  containers:
  - name: nginx
    image: nginx:1.17.2-alpine

---
apiVersion: v1
kind: Service
metadata:
  name: web-svc
spec:
  type: NodePort
  selector:
    app: web
    env: study
  ports:
  - port: 80
    targetPort: 80
    nodePort: 30000

上記のマニフェストファイルを作業ディレクトリに配置してservice作成する。

kubectl apply -f service.yml

作成したserviceの動作確認をする。

kubectl get all
NAME                 TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)        AGE
service/kubernetes   ClusterIP   10.96.0.1        <none>        443/TCP        16h
service/web-svc      NodePort    10.101.233.192   <none>        80:30000/TCP   48s

仮想サーバのホストにポートを指定する。
192.168.1.10:30000

うまくいったらpodを削除。

kubectl delete -f service.yml

ConfigMap

(24) 07.jpg
(24) 11.jpg
(24) 12.jpg
![(24) 20.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99495/cbba8851-3db3-cd3b-a71a-e0be261ea68c.jp

configmap.yml
apiVersion: v1
kind: ConfigMap
metadata:
  name: sample-config
  namespace: default
data:
  sample.cfg: |
    user: tsuyoshi.tanaka
  type: "application"

---
apiVersion: v1
kind: Pod
metadata:
  name: sample
  namespace: default
spec:
  containers:
  - name: sample
    image: nginx:1.17.2-alpine
    env:
    - name: TYPE
      valueFrom:
        configMapKeyRef:
          name: sample-config
          key: type
    volumeMounts:
    - name: config-storage
      mountPath: /home/nginx
  volumes:
  - name: config-storage
    configMap:
      name: sample-config
      items:
      - key: sample.cfg
        path: sample.cfg

上記のマニフェストファイルを作業ディレクトリに配置してconfigmap作成する。

kubectl apply -f configmap.yml

作成したconfigmapの動作確認をする。

kubectl get pod
kubectl exec -it sample sh

うまくいったらpodを削除。

kubectl delete -f configmap.yml

Secret

(25) 08.jpg
(25) 13.jpg
(25) 14.jpg
(25) 18.jpg
(25) 20.jpg
(25) 23.jpg
(25) 26.jpg
![(25) 28.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99495/fc4be54e-0d61-2a77-5bb5-3c8a6b774314.jp

sercret.yml
apiVersion: v1
kind: Secret
metadata:
  name: sample-secret
data:
  message: SGVsbG8gV29ybGQgIQ==     # echo -n 'Hello World !' | base64
  keyfile: WU9VUi1TRUNSRVQtS0VZ     # cat ./keyfile | base64

---
apiVersion: v1
kind: Pod
metadata:
  name: sample
  namespace: default
spec:
  containers:
  - name: sample
    image: nginx:1.17.2-alpine
    env:
    - name: MESSAGE
      valueFrom:
        secretKeyRef:
          name: sample-secret
          key: message
    volumeMounts:
    - name: secret-storage
      mountPath: /home/nginx
  volumes:
  - name: secret-storage
    secret:
      secretName: sample-secret
      items:
      - key: keyfile
        path: keyfile

上記のマニフェストファイルを作業ディレクトリに配置してsercret作成する。

kubectl apply -f secret.yml

鍵ファイルを使って、sercretを暗号化する。

kubectl create secret generic sample-sercret

作成したsercretの動作確認をする。

kubectl get secret

作成したsercretの詳細な動作確認をする。

kubectl get secret/sample-sercret -o yaml

secret.ymlを削除。

kubectl delete -f secret.yml

永続データ(PersistentVolume,PersistentVolumeClaim)

(26) 08.jpg
(26) 16.jpg
(26) 22.jpg
(26) 26.jpg
(26) 27.jpg
(26) 34.jpg
(26) 35.jpg

storage.yml
apiVersion: v1
kind: PersistentVolume
metadata:
  name: volume-01
  labels:
    env: study
spec:
  storageClassName: slow
  accessModes:
  - ReadWriteOnce
  capacity:
    storage: 1Gi
  persistentVolumeReclaimPolicy: Retain
  hostPath:
    path: "/data/storage"
    type: Directory

---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: volume-claim
  labels:
    env: study
spec:
  storageClassName: slow
  accessModes:
  - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi

上記のマニフェストファイルを作業ディレクトリに配置してstorage作成する。

kubectl apply -f storage.yml

storage.ymlの動作を確認する。

kubectl get pvc,pv

マウントされていることが確認できる。

NAME                                 STATUS    VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS   AGE
persistentvolumeclaim/volume-claim   Pending                                      slow           73s

NAME                         CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS    CLAIM   STORAGECLASS   REASON   AGE
persistentvolume/volume-01   1Gi        RWO            Retain           Pending           slow                    74s

削除。

kubectl delete -f storage.yml

StatefulSet

(27) 08.jpg

(27) 12.jpg

statefulset.yml
apiVersion: v1
kind: PersistentVolume
metadata:
  name: volume-01
spec:
  storageClassName: slow
  accessModes:
  - ReadWriteMany
  capacity:
    storage: 1Gi
  persistentVolumeReclaimPolicy: Retain
  hostPath:
    path: /data/storage
    type: Directory

---
apiVersion: v1
kind: Service
metadata:
  name: sample-svc
spec:
  clusterIP: None
  selector:
    app: web
    env: study
  ports:
  - port: 80
    targetPort: 80

---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: nginx
spec:
  replicas: 1
  selector:
    matchLabels:
      app: web
      env: study
  revisionHistoryLimit: 14
  serviceName: sample-svc
  template:
    metadata:
      name: nginx
      labels:
        app: web
        env: study
    spec:
      containers:
      - name: nginx
        image: nginx:1.17.2-alpine
        volumeMounts:
        - name: storage
          mountPath: home/nginx
  volumeClaimTemplates:
  - metadata:
      name: storage
    spec:
      storageClassName: slow
      accessModes:
      - ReadWriteMany
      resources:
        requests:
          storage: 1Gi

上記のマニフェストファイルを作業ディレクトリに配置してstatefulset.yml作成する。

kubectl apply -f statefulset.yml

作成したstatefulset.ymlの動作確認をする。

kubectl get all

続いて、centospodを起動してアクセスする。

kubectl run debug --image-centos:7 -it --rm --restart=Never -- sh

うまくいったらstatefulset.ymlを削除。

kubectl delete -f statefulset.yml

Ingress

(28) 10.jpg

(28) 15.jpg

(28) 17.jpg

(28) 20.jpg

apiVersion: v1
kind: Service
metadata:
  name: web-svc
spec:
  selector:
    app: web
    env: study
  ports:
  - port: 80
    targetPort: 80

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
spec:
  replicas: 2
  selector:
    matchLabels:
      app: web
      env: study
  template:
    metadata:
      name: nginx
      labels:
        app: web
        env: study
    spec:
      containers:
      - name: nginx
        image: nginx:1.17.2-alpine

---
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: frontend
  annotations:
    kubernetes.io/ingress.class: "nginx"
    nginx.ingress.kubernetes.io/ssl-redirect: "false"
spec:
  rules:
  - http:
      paths:
      - path: /
        backend:
          serviceName: web-svc
          servicePort: 80

上記のマニフェストファイルを作業ディレクトリに配置してingress作成する。

kubectl apply -f ingress.yml

作成したingressの動作確認をする。

kubectl get ing,svc,deploy

下記のように外部アドレスが表示されない場合、設定されるまで数分待つ。

NAME                          HOSTS   ADDRESS   PORTS   AGE
ingress.extensions/frontend   *                 80      3m8s

NAME                 TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE
service/kubernetes   ClusterIP   10.96.0.1       <none>        443/TCP   15h
service/web-svc      ClusterIP   10.110.116.66   <none>        80/TCP    3m8s

NAME                          READY   UP-TO-DATE   AVAILABLE   AGE
deployment.extensions/nginx   0/2     2            0           3m9s

うまくいったらingress.ymlを削除。

kubectl delete -f ingress.yml

本セクションではKubernetesリソースの使い方を学習しました。
このまとめでは各リソースのサンプルコードおよびコード中にポイントとなる箇所をまとめます。

◆ワークロード

○Pod

apiVersion: v1
kind: Pod
metadata:
name: sample
spec:
containers:
- name: nginx
image: nginx:1.17.2-alpine
volumeMounts:
- name: storage
mountPath: /home/nginx
volumes:
- name: storage
hostPath:
path: "/data/storage"
type: Directory

○ReplicaSet

apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: nginx
spec:
replicas: 3
selector:
matchLabels:
app: web
env: study
template: # Podと同じものを記述
metadata:
name: nginx
labels:
app: web
env: study
spec:
containers:
- name: nginx
image: nginx:1.17.2-alpine

○Deployment

apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
annotations:
kubernetes.io/change-cause: "First release."
spec:
replicas: 2
selector:
matchLabels:
app: web
env: study
revisionHistoryLimit: 14 # デフォルト 10
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1 # 余分に生成してよいPod数
maxUnavailable: 1 # 一度に消失してよいPod数
template:
metadata:
name: nginx
labels:
app: web
env: study
spec:
containers:
- name: nginx
image: nginx:1.17.2-alpine

○StatefulSet

apiVersion: apps/v1
kind: StatefulSet
metadata:
name: nginx
spec:
replicas: 1
selector:
matchLabels:
app: web
env: study
revisionHistoryLimit: 14
serviceName: sample-svc # HeadlessServiceと紐づける
template:
metadata:
name: nginx
labels:
app: web
env: study
spec:
containers:
- name: nginx
image: nginx:1.17.2-alpine
volumeMounts:
- name: storage
mountPath: home/nginx
volumeClaimTemplates: # PersistentVolumeClaimと同じものを記述
- metadata:
name: storage
spec:
storageClassName: slow
accessModes:
- ReadWriteMany
resources:
requests:
storage: 1Gi

◆サービス

○Service

apiVersion: v1
kind: Service
metadata:
name: web-svc
spec:
type: NodePort # ClasterIP(デフォルト), NodePort, LoadBalancer, ExternalName
selector:
app: web
env: study
ports:
- port: 80 # 受付ポート
targetPort: 80 # 転送先ポート
nodePort: 30000 # ノード上にマッピングするポート(type: NodePort 時のみ)

○Ingress

apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: frontend
annotations:
kubernetes.io/ingress.class: "nginx"
nginx.ingress.kubernetes.io/ssl-redirect: "false"
spec:
rules:
- http:
paths:
- path: /
backend:
serviceName: web-svc
servicePort: 80

◆設定

○ConfigMap

apiVersion: v1
kind: ConfigMap
metadata:
name: sample-config
data:
sample.cfg: |
user: tsuyoshi.tanaka
type: "application"

○Secret

apiVersion: v1
kind: Secret
metadata:
name: sample-secret
data:
message: SGVsbG8gV29ybGQgIQ== # echo -n 'Hello World !' | base64
keyfile: WU9VUi1TRUNSRVQtS0VZ # cat ./keyfile | base64

◆ストレージ

○PersistentVolume

apiVersion: v1
kind: PersistentVolume
metadata:
name: volume-01
labels:
env: study
spec:
storageClassName: slow
accessModes:
- ReadWriteOnce # ReadWriteOnce, ReadOnlyMany, ReadWriteMany
capacity:
storage: 1Gi
persistentVolumeReclaimPolicy: Retain # Retain, Delete, Recycle(非推奨)
hostPath:
path: "/data/storage"
type: Directory

○PersistentVolumeClaim

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: volume-claim
labels:
env: study
spec:
storageClassName: slow
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi

記述のポイントはおさえられたでしょうか?
これから実践に入っていく際、基本となりますので忘れたときにはここへ立ち返って復習しながら進めましょう!

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