「Container」カテゴリーアーカイブ

MongoDBコンテナの起動時に初期データを登録してみる

MongoDBをちょっと検証で利用するのに便利な方法をご紹介します。
通常DBコンテナには永続ボリュームをアタッチしますが、DBへの接続方法をクイックに確認したい時は、その処理が煩わしく感じます。そんな時私は、コンテナ起動時に簡単なデータを登録して検証しています。
今回はMongoDBを使って、初期データを投入する方法を紹介します。

  • jsonファイルを作成する
  • jsonファイルを登録するスクリプトを作成する
  • コンテナイメージを作成する
  • コンテナを実行する

jsonファイルを作成する


登録するデータを配列構造で記述したjsonファイル(categories.json)を作成します。

[
  { "name": "スポーツ" },
  { "name": "文化" },
  { "name": "娯楽" },
  { "name": "食事" },
  { "name": "お酒" },
  { "name": "イベント" },
  { "name": "ドライブ" },
  { "name": "文化" }
]

jsonファイルを登録するスクリプトを作成する


コンテナ起動時に、categories.jsonのデータをMongoDBに登録するスクリプト(init.sh)を作成します。
recommendDBのcategoriesコレクションに、/docker-entrypoint-initdb.d/categories.jsonの配列構造データを登録しています。

mongoimport --authenticationDatabase admin --username root --password password --db recommend --collection categories --drop --file /docker-entrypoint-initdb.d/categories.json --jsonArray

mongoimportコマンドのドキュメントは以下URLになります。
https://docs.mongodb.com/manual/reference/program/mongoimport/
--dropは、データ登録時にそのコレクションがあった場合、一度削除してからデータを登録します。
--jsonArrayは、配列構造を扱うためのオプションです。

コンテナイメージを作成する


MongoDBを起動するためのDockerfileを作成します。
ビルド用ディレクトリ構造は以下の通りです。

$ tree .
.
├── Dockerfile
├── categories.json
└── init.sh

Dockerfileは以下の通りです。
mongoベースイメージを取得して、/docker-entrypoint-initdb.dに上記で作成したcategories.jsonとinit.shをコピーしています。/docker-entrypoint-initdb.dにスクリプトを配置することで、コンテナ起動時に実行されます。

FROM mongo:latest
COPY categories.json /docker-entrypoint-initdb.d/
COPY init.sh /docker-entrypoint-initdb.d/

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

$ docker build -t mongo-test .
Sending build context to Docker daemon  4.096kB
Step 1/3 : FROM mongo:latest
 ---> 3f3daf863757
Step 2/3 : COPY categories.json /docker-entrypoint-initdb.d/
 ---> Using cache
 ---> 209a1bede01c
Step 3/3 : COPY init.sh /docker-entrypoint-initdb.d/
 ---> Using cache
 ---> 68ffc7b95b98
Successfully built 68ffc7b95b98
Successfully tagged mongo-test:latest

コンテナを実行する


コンテナを実行します。今回はdocker-compose.yamlを作成しました。

version: '3'
services:
  db:
    container_name: db
    image: mongo-test:latest
    environment:
      MONGO_INITDB_ROOT_USERNAME: root
      MONGO_INITDB_ROOT_PASSWORD: password
    ports:
      - "27017:27017"

以下のコマンドで実行します。

$ docker-compose up -d
Creating network "wishy_default" with the default driver
Creating db ... done

以下のコマンドで確認してみましょう。データが登録されていることがわかります。

$ docker exec -it db /bin/bash
root@897eb9caf5c4:/# mongo -uroot -ppassword
> show dbs
admin      0.000GB
config     0.000GB
local      0.000GB
recommend  0.000GB
> use recommend
switched to db recommend
> db.categories.find()
{ "_id" : ObjectId("5ecbe8257399a6df837e7384"), "name" : "スポーツ" }
{ "_id" : ObjectId("5ecbe8257399a6df837e7385"), "name" : "娯楽" }
{ "_id" : ObjectId("5ecbe8257399a6df837e7386"), "name" : "食事" }
{ "_id" : ObjectId("5ecbe8257399a6df837e7387"), "name" : "お酒" }
{ "_id" : ObjectId("5ecbe8257399a6df837e7388"), "name" : "文化" }
{ "_id" : ObjectId("5ecbe8257399a6df837e7389"), "name" : "ドライブ" }
{ "_id" : ObjectId("5ecbe8257399a6df837e738a"), "name" : "文化" }
{ "_id" : ObjectId("5ecbe8257399a6df837e738b"), "name" : "イベント" }
> 

以上です!

OpenShift上のコンテナにroot権限でコマンドを実行する方法

OpenShift上のコンテナに対して、root権限でコマンドを実行する方法について紹介します。

  • コンテナに対してコマンドを実行する方法
  • root権限でコマンド実行する方法
  • root権限でコマンド実行するユースケース

コンテナに対してコマンドを実行する方法


OpenShiftでコンテナに対してコマンドを実行する方法を紹介します。

コンテナに対してコマンドを実行するために、以下のコマンドを利用することがあります。podに接続してインタラクティブにコマンドを実行したり、ワンライナーのコマンドをコンテナで実行することができます。ただし、コンテナ実行ユーザーでの操作になりますので、実行ユーザーがrootでない場合はroot操作を行うことができません

  • oc rsh
  • oc exec

これはシステムをセキュアに保つためですが、どうしてもroot権限で操作したいことがあったりします。本記事ではその方法について紹介します。

root権限でコマンド実行する方法


root権限でコマンド実行する方法について紹介します。以下のサポート問い合わせ履歴を参考にしました。

https://access.redhat.com/solutions/4539631

1. podが実行されているノードを確認する

$ oc get pods -o wide

2. container idを確認する

$ oc describe pod [pod名] | grep -I container

3. 手順1で確認したノードにログインし、コンテナのpidを確認する

$ pstree -p $(ps aux | awk “/[container id]/ {print \$2; exit}")
conmon(1408224)-+-dockerregistry(1408236)-+-{dockerregistry}(1408282)
                |                         |-{dockerregistry}(1408283)
                |                         |-{dockerregistry}(1408284)
                |                         |-{dockerregistry}(1408285)
                |                         |-{dockerregistry}(1408286)
                |                         |-{dockerregistry}(1408287)
                |                         |-{dockerregistry}(1408288)
                |                         |-{dockerregistry}(1408289)
                |                         |-{dockerregistry}(1408318)
                |                         |-{dockerregistry}(1409115)
                |                         |-{dockerregistry}(1409558)
                |                         |-{dockerregistry}(1415043)
                |                         `-{dockerregistry}(1429776)
                `-{conmon}(1408226)

4. コンテナに接続する

$ nsenter -a -t 1408236

5. コマンドを実行する

以下のコマンドを確認すると、rootユーザーでコマンド実行していることが確認できます。

$ whoami

root権限でコマンド実行するユースケースと体験談


本記事で紹介した操作は、大体が障害時の問題切り分けで使われることを想定しています。何故ならば、コンテナのイミュータブルな性質を無視していて、根本解決になっていないからです。

先日、OpenShiftを操作していたらdocker registryが機能していないことに気づきました。突き詰めると、docker registry podの/registryのオーナーがroot:rootになっていたことが原因でした。権限不足でレジストリへのpush/pullができない状態だったのです。

/registryのオーナー権限をchmodchownで変更すれば解決するのですが、oc rshコマンドで実行しようにも、コンテナ実行ユーザー(この時のユーザーは、1000170000でした)での操作になるのでchmodchownコマンドが実行できませんでした。

本当に原因が合っているか確かめるため、かつ即時回復が求められていたため、一時的にroot権限でコマンドを実行して回復しました。このように、問題切り分けの時には有効だと思いますので是非ご活用ください。

以上です!

KubeVirtとは?従来仮想基盤の代わりになるのか??

先日OpenShift4.4が発表され、OpenShift Virtualizationの機能がテックプレビューで追加されました。OpenShift Virtualizationは、Virtual Machine(VM)をOpenShift上で操作する機能です。こちらの動画ではOpenShift上にWindows VMを起動しています。

「OpenShift Virtualizationによって従来の仮想基盤は不要になるのか?」という疑問を抱いたため、ベースの技術であるKubeVirtについて調査しました。KubeVirtを紐解くことで、OpenShift Virtualizationを知る近道になるはず。

  • KubeVirtとは?
    • KubeVirtの概要
      • 従来仮想基盤との違い
      • コンテナとの違い
    • KubeVirtの構成
  • KubeVirtのメリット/デメリットは?
    • コンテナと比較したメリット
    • コンテナと比較したデメリット
    • 従来の仮想基盤と比較したメリット
    • 従来の仮想基盤と比較したデメリット
  • KubeVirtは従来の仮想基盤の代わりになるのか?

KubeVirtとは?


KubeVirtの概要


KubeVirtは、Kubernetes上でVMをコンテナとして操作するインタフェースです。Kubernetes上でVMを従来通り動かすのが目的ではなく、コンテナ化が難しい従来のVMをKubernete上のワークロードに適用しやすくすることが目的で作られました。

従来仮想基盤との違い


図の通りKubernetes上でVMがコンテナとして動きます。

従来の仮想基盤と大きく異なる点は、VMがKubernetesのワークロードとして稼働する点です。従って、スケジューリングやHWリソースの割り当ての方法が大きく変わります。レプリカセットやサービスなどのリソースと組み合わせて使うことができるようになる一方で、これまでのHWリソース割り当てができなくなる可能性があります。改修が必要なVMアプリケーションも今後出てくることが考えられます。

コンテナとの違い


コンテナと大きく異なる点は、イメージです。通常コンテナイメージでコンテナアプリケーションを実行しますが、KubeVirtではVMイメージから実行します。

ちなみにKubeVirtがサポートするイメージは現状以下のみになります。コンテナが再起動する度にデータが削除されてしまうので、通常はPVCにイメージを書き込んでディスクから起動します。

  • .img
  • .iso
  • .qcow2
  • 上記イメージの圧縮ファイル( .tar, .gz,  .xz )

引用元)https://kubevirt.io/user-guide/#/installation/image-upload

KubeVirtは、コンテナの中でlibvirtdがVMのライフサイクルを管理しています。だからこそVMのイメージでアプリケーションが起動しますし、コンテナなのに起動・停止・再起動ができるのです。

KubeVirtの構成


KubeVirtの構成についても調査しました。KubeVirtをデプロイすると以下のコンポーネントが配置されます。

  •  
    • virt-controller

VMのPodを作成します。kubeletによってPodがスケジュールされると、virt-handlerにCRDであるVM情報を連携します。

    • virt-handler

CRDであるVM情報が更新されたことを検知すると、そのVM情報をvirt-launcherに指示します。

    • virt-launcher

virt-handlerからCRDであるVM情報を受け取ると、それを基にlibvirtdを介してVMを起動します。

    • libvirtd

VMライフサイクルを管理します。

KubeVirtのメリット/デメリットとは?


思いつくメリット/デメリットを書き出してみました。もちろんこれ以外にもあると思いますので、参考程度に読んでいただければ幸いです。

コンテナと比較したメリット


サーバー型アプリケーションのコンテナ化が容易

サーバー型アプリケーションのコンテナ化が容易であることが挙げられます。これは1番の強みと言っても過言ではないと思います。
サーバー型アプリケーションをコンテナ化するには、必要なファイルを洗い出してベースイメージの仕様に合わせる必要があります。更には、アプリケーションやジョブクライアントなど、仕様を大きく変更しなければならない箇所も多く出てきます。一方、KubeVirtはサーバーイメージからOSごとコンテナ化できますので、そういった作業を比較的少なくすることができます。

アプリケーションがカーネルを共有しない

カーネルを共有しないという点もメリットとして挙げられます。厳密に言えば共有していますが、アプリケーションが直接利用するカーネルを他のアプリケーションが利用することがありません。
カーネルを独占することによってセキュリティをコンテナよりも担保し易くなるというメリットがあります。

VMの起動・停止・ライブマーグレーションが可能

メリットになるか賛否両論あると思いますが、コンテナなのに起動・停止・再起動・ライブマイグレーションが可能であることで、アプリケーションによってはメリットになると思います。

コンテナと比較したデメリット


資源効率が悪い

資源効率が悪いということがデメリットとして挙げられます。コンテナは必要最低限のモジュールのみインストールすればアプリケーションが実行できます。それと比較すると、KubeVirtはOSのイメージを利用するため、容量を多く使います。更にVMの数が増えるほど、VM間におけるOSモジュールの重複や不要なOSモジュールが増えてしまいます。このような観点でコンテナと比較すると資源効率は悪いです。

従来の仮想基盤と比較したメリット


複製・自動化が容易

複製・自動化が容易であることがメリットとして挙げられます。Kubernetes上でコンテナとして動くので、HPAやReplicaSetのようなリソースを使って運用の自動化ができます。

開発スピードの向上

開発スピードの向上が期待できます。これまで開発者はシステム管理者にサーバー作成を依頼していました。VMアプリケーションを開発者が自律的に作成できるようになるため、そのリードタイムを短縮できます。

VMとコンテナの連携が容易

Kubernetes上のコンテナとVMの連携が容易であることも挙げられます。通常のVMだと当然ながらKubernetesの外にいます。従って、コンテナと通信するにはコンテナを外部に公開する設定が必要でした。更にはKubernetes内で定義した名前をVMで扱うことができませんでした。Kubernetes内にVMを構築できることで、これらの考慮が不要になり、コンテナとの連携が比較的容易になります。

従来の仮想基盤と比較したデメリット


OSの設定変更が煩雑

KubeVirtでVMデプロイ後にOSの設定を変更しても、障害などで再作成された時に設定が消えてしまいます。設定変更にはイメージを編集し、VMを再度起動する必要があります。Immutableな性質によるメリットの裏返しです。但し、これは永続ボリュームを使うことで回避できますのでデメリットにはならないことが多いです。

KubeVirtは従来の仮想基盤の代わりになるのか?


結論から言うと、単純なマイグレーションつまり従来と同じ用途では代替ツールにはならないと思います。VMのライフサイクルがKubernetesで管理されるので、今までと作成方法や管理方法が異なります。様々な管理が自動化できる一方で、従来仮想基盤でできたことができなくなることも今後見つかってくると思います。だからこそ、今までと同じと考えるのは非常に危険です。

繰り返しになりますが、KubeVirtは従来の仮想基盤の代わりを目指している訳ではありません。従来のワークロードにImmutableな性質を持たせ、よりスケーラブルで柔軟なシステムにすることを目的としています。従来の仮想基盤から移行する場合は、運用の発想も大きく変える必要があるということがわかりました。

Goコンテナでフロントとバックエンドを分けてみる

今日はGo言語で簡単なwebアプリケーションを作成します。webとdbにコンテナを分けて、webページにdbから取得したデータを表示したいと思います。以下のように、マイクロサービスを意識して、コンテナ間通信をhttpリクエストで行いたいと思います。

以下の流れで作成します。

  • dbを作成する
  • webを作成する
    • ソースコードを作成する
      • main.goを作成する
      • layout.htmlを作成する
      • index.htmlを作成する
    • Dockerfileを作成する
  • コンテナを実行する
    • コンテナイメージの作成
    • Docker Composeファイルを作成する
    • コンテナを実行する

dbを作成する

 dbコンテナは以下の記事で作成したものを利用します。ここで作成したのは、httpリクエストを行うとjsonファイルを読み込んで出力するコンテナです。本来であればデータベースも作成しますが、今回はhttpリクエストでやり取りするのをメインにしているので、データはファイルにします。データベースを使うのはまた記事書きたいと思います。

go言語でjsonファイルを読み込んで表示してみる

webを作成する

ソースコードを作成する

main.goを作成する

 ポート8080で受け付けると、dbコンテナから取得したjsonコードをhtml形式で表示するwebのソースコード(main.go)を以下に示します。

package main

import (
    "encoding/json"
    "fmt"
    "log"
    "net/http"
    "github.com/gorilla/mux"
    "io/ioutil"
    "os"
        "html/template"
)

type category struct {
    Categories []struct{
      Name string `json:"name"`
    }`json:"categories"`
}

func main() {
    r := mux.NewRouter()
    r.Handle("/", index)

    //サーバー起動
    if err := http.ListenAndServe(":8080", r); err != nil {
        log.Fatal("ListenAndServe:", nil)
    }
}

var index = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    url := "http://db:8082/public"
    req, _ := http.NewRequest("GET", url, nil)
    client := new(http.Client)
    resp, _ := client.Do(req)
    defer resp.Body.Close()
    byteArray, _ := ioutil.ReadAll(resp.Body)
    var categories category
    if err := json.Unmarshal(byteArray, &categories); err != nil {
            log.Fatal(err)
            os.Exit(1)
    }
    generateHTML(w, categories, "layout", "index")
})

func generateHTML(writer http.ResponseWriter, data interface{}, filenames ...string) {
    var files []string
    for _, file := range filenames {
        files = append(files, fmt.Sprintf("templates/%s.html", file))
    }

    templates := template.Must(template.ParseFiles(files...))
    templates.ExecuteTemplate(writer, "layout", data)
}

 index = http.HandlerFuncでは、dbコンテナから取得したjsonコードを構造体categoryの変数categoriesに代入し、generateHTML関数を呼び出します。generateHTMLは引数で与えられた名前のhtmlファイル、ここではlayout.htmlとindex.htmlを使ってページを生成します。

layout.htmlを作成する

 generateHTML関数は、layout.htmlとindex.htmlを使ってページを生成します。layout.htmlは以下の通り、body以外のheaderのみを記述しています。layoutを作成することで、それぞれのhtmlのheaderを統一し、ソースコードの重複を回避できます。

{{ define "layout" }}

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>Test</title>
  </head>
  <body>
    {{ template "content" . }}
  </body>
</html>

{{ end }}

index.htmlを作成する

 このlayoutを使ったindex.htmlが以下になります。{{ define "content" }} と{{ end }}で囲んだhtml文がlayoutの{{ template "content" . }}、つまりはページの本文に記述されることになります。

{{ define "content" }}
  <center>
    {{ range .Categories }}
        {{ .Name }}<br>
    {{ end }}
  </center>
{{ end }}

Dockerfileを作成する

 コンテナイメージを作成するのに必要なDockerfileを作成します。ビルド用のディレクトリ構造は以下の通りです。

$ tree web
web
├── Dockerfile
└── src
    ├── main.go
    └── templates
        ├── index.html
        └── layout.html

 src配下に作成したソースコードを配置します。このディレクトリ構造に合わせて作成したDockerfileが以下になります。

FROM golang:latest

# コンテナ作業ディレクトリの変更
WORKDIR /go/src/web
# ホストOSの ./src の中身を作業ディレクトリにコピー
COPY ./src .
RUN go get -u github.com/gorilla/mux
RUN go build -o web
# ウェブアプリケーション実行コマンドの実行
CMD ["./web"]

コンテナを実行する

コンテナイメージを作成する

 上記で作成したwebとdbのコンテナイメージを以下のコマンドで作成します。

$ docker build -t web:latest web/
$ docker build -t db:latest db/

Docker Composeファイルを作成する

 webとdbのコンテナイメージからコンテナを実行しますが、webとdb間は通信できる必要があります。そこでdocker-compose.yamlファイルを作成します。以下にその内容を示します。

version: '3'
services:
  db:
    container_name: db
    image: db:latest
    ports:
      - "8082:8082"
  web:
    container_name: web
    image: web:latest
    depends_on:
      - db
    links:
      - db
    ports:
      - "8080:8080"

 docker-compose.yamlはservicesブロックの中に実行するコンテナ毎にブロックを作成します。今回はwebとdbコンテナだけなので、webブロックとdbブロックを作成しました。
 webのmain.goにurl := "http://db:8082/public"とurlを直接指定しました。従ってwebコンテナはホスト名dbをdbコンテナに名前解決できる必要があります。それを定義しているのがlinksです。

コンテナを実行する

 docker-compose.yamlファイルからコンテナを実行するにはdocker-composeコマンドを利用します。docker-compose.yamlを置いたディレクトリで以下のコマンドを実行します。

$ docker-compose up -d
Creating db ... done
Creating web ... done
$ docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED              STATUS              PORTS                    NAMES
da224d318f33        web:latest          "./web"             About a minute ago   Up About a minute   0.0.0.0:8080->8080/tcp   web
b245c4eb8320        db:latest           "./searcher"        About a minute ago   Up About a minute   0.0.0.0:8082->8082/tcp   db

 docker-compose.yamlで定義した内容で実行されていることがわかります。ブラウザでhttp://localhost:8080/にアクセスしてみましょう。jsonの中身が表示されていることがわかります。

以上です!なんとなくGo言語がわかってきたようなきてないような。。ちゃんと仕組みから理解した方が良さそうなので、またそういう記事も書きたいと思います!ソースコードが汚いのは許してください。。。

Mac OSにCode Ready Container(CRC)をインストールしてみる

OpenShiftを自分で動かしてみたので紹介致します。

以下の流れで紹介します。

  • CRCとは
  • CRCのダウンロード
  • CRCのセットアップ
  • CRCの起動

CRCとは

OpenShift4を個人用PCにインストールして動かすことができます。

CRCのダウンロード

まずはCRCを以下のサイトからダウンロードします。ダウンロードするにはRed Hatアカウントが必要になるので、お持ちでない方はアカウントを作成する必要があります。

CRCダウンロードサイト

以下赤枠をクリックしてダウンロードします。コラッタはmacOSを利用しているので、macOSのCRCをダウンロードしています。お使いのPCに合わせてダウンロードしてください。

完了すると以下のファイルがダウンロードされます。

  • crc-macos-amd64.tar.xz
  • pull-secret

CRCのセットアップ

以下のコマンドを実行して、ファイルを展開します。ディレクトリ名はcrc-macos-[バージョン]-amd64です。2020/05/18時点の最新は1.10.0みたいです。

$ tar xzvf crc-macos-amd64.tar.xz
$ cd crc-macos-1.7.0-amd64

ついでに、pull-secretもcrc-macos-1.7.0-amd64に移動します。

$ mv pull-secret crc-macos-1.7.0-amd64

以下のコマンドでCRCのセットアップを行います。コマンドのパスを通してあげることで、どこからでも実行できるようになりますが、今回はディレクトリにあるファイルを直接して実行します。 途中で求められるパスワードは自身の個人PCのパスワードです。

$ ./crc setup
INFO Checking if oc binary is cached
INFO Checking if podman remote binary is cached
INFO Checking if CRC bundle is cached in '$HOME/.crc'
INFO Checking if running as non-root
INFO Checking if HyperKit is installed
INFO Checking if crc-driver-hyperkit is installed
INFO Checking file permissions for /etc/resolver/testing
INFO Checking file permissions for /etc/hosts
INFO Setting file permissions for /etc/hosts
INFO Will use root access: change ownership of /etc/hosts
Password:
Setup is complete, you can now run 'crc start' to start the OpenShift cluster

CRCの起動

以下のコマンドでCRCの起動を行います。

$ ./crc start -p pull-secret

CRCの起動が完了すると、最後に以下のインフォメーションが表示されます。これがログイン方法になります。

INFO To access the cluster, first set up your environment by following 'crc oc-env' instructions
INFO Then you can access it by running 'oc login -u developer -p developer https://api.crc.testing:6443'
INFO To login as an admin, run 'oc login -u kubeadmin -p db9Dr-J2csc-8oP78-9sbmf https://api.crc.testing:6443'
INFO
INFO You can now run 'crc console' and use these credentials to access the OpenShift web console

実際にログインします。

$ oc login -u kubeadmin -p db9Dr-J2csc-8oP78-9sbmf https://api.crc.testing:6443
Login successful.

You have access to 56 projects, the list has been suppressed. You can list all projects with 'oc projects'

Using project "default".
$ oc get nodes
NAME                 STATUS    ROLES           AGE       VERSION
crc-pxt56-master-0   Ready     master,worker   73d       v1.16.2

コンソールを起動する場合は、以下のコマンドを実行します。

$ ./crc console

以上、CRCの導入手順と実行手順でした。OpenShiftに興味ある方は是非インストールしてみたください!