20191223のdockerに関する記事は11件です。

escriptを動かすDockerfileのサンプル

  • mix escript.build で生成されるバイナリを動かしたい
  • 面倒なことは避けたい
  • コード変更した後短い時間でimage作成したい
  • imageのサイズは小さくしたい

を満たすDockerfileを作りました。
手元で動かすだけであれ必要はないのだけれども、github actionsで利用したかったので image 化しました。

できあがったものは niku/nwiki にあります。
docker image ls によると 257MB のようです。
あんまり小さくはないけれど、これ以上がんばる気力がなかった。

Elixir Advent Calendar 2019で埋まっていないところがあったので参加させてもらいました :pray:

方針

  • 面倒なことは避けたい
  • コード変更した後短い時間でimage作成したい
    • 上から変更の頻度が低い順に並べる。アプリケーションのコードが変更されたら、アプリケーションのレイヤーだけビルドされるように構築した。
    • apt-get
    • mix が共通で利用する hex と rebar パッケージのインストール、コンパイル
    • mix がアプリケーションで利用するパッケージをインストール、コンパイル
    • アプリケーションを読みこみ、コンパイル
  • imageのサイズは小さくしたい
    • MultiStageビルドを利用
    • escriptをビルドする環境と、escriptを動かす環境を分けることでビルド時に必要で動かすときには不要な依存を保持しなくともよい
    • escript化したらerlangの実行環境のみでよい(elixirは必要ない)
    • ビルドする環境と動かす環境は同じOS、CPUでないと動かない点には注意すること

Dockerfile

#
# Building stage
#
FROM elixir:slim
ENV LANG=C.UTF-8 \
    MIX_ENV=prod

# os dependency
# mix global dependency
RUN set -xe \
    && apt-get update \
    && apt-get install -y --no-install-recommends \
    git \
    ca-certificates \
    && rm -rf /var/lib/apt/lists/* \
    && mix local.hex --force \
    && mix local.rebar --force

# mix dependency
COPY mix.exs mix.lock ./
RUN set -xe \
    && mix deps.get --only prod \
    && mix deps.compile

# application code
COPY lib ./lib/
RUN set -xe \
    mix escript.build

#
# Runtime stage
#
# elixir:slim based on erlang:22-slim
FROM erlang:22-slim
ENV LANG=C.UTF-8
COPY --from=0 nwiki .
ENTRYPOINT ["./nwiki"]
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【環境構築】Docker + Rails6 + Vue.js + Vuetifyの環境構築手順

はじめに

Docker + Rails6 + Vue.js + Vuetifyの開発環境構築手順をまとめました。

以下の記事を参考にさせて頂きました!ありがとうございます:bow_tone1:

環境

OS: macOS Catalina 10.15.1
zsh: 5.7.1
Ruby: 2.6.5
Rails: 6.0.2
Docker: 19.03.5
docker-compose: 1.24.1
Vue: 2.6.10
vue-router: 2.6.10
vuex: 3.1.2
vuetify: 2.1.0

1.準備

作成するアプリケーション名はhogeappとします。
まずは以下ファイルを作成して下さい。

Dockerfile

yarnが必要になるので、Dockerfileに反映しています。

hogeapp/Dockerfile
FROM ruby:2.6.5

RUN apt-get update -qq && \
  apt-get install -y build-essential \
  libpq-dev \
  nodejs \
  vim

RUN apt-get update && apt-get install -y curl apt-transport-https wget && \
  curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - && \
  echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list && \
  apt-get update && apt-get install -y yarn

RUN mkdir /app_name
ENV APP_ROOT /app_name
WORKDIR $APP_ROOT

COPY ./Gemfile $APP_ROOT/Gemfile
COPY ./Gemfile.lock $APP_ROOT/Gemfile.lock
RUN bundle install

COPY entrypoint.sh /usr/bin/
RUN chmod +x /usr/bin/entrypoint.sh
ENTRYPOINT [ "entrypoint.sh" ]
ADD . $APP_ROOT

docker-compose.yml

hogeapp/docker-compose.yml
version: "3"
services:
  db:
    image: mysql:5.7
    environment:
      MYSQL_ROOT_PASSWORD: password
      MYSQL_DATABASE: root
    volumes:
      - db-data:/var/lib/mysql
    ports:
      - "3306:3306"
  web:
    build: .
    command: /bin/sh -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"
    volumes:
      - .:/app_name
    ports:
      - "3000:3000"
    links:
      - db
    tty: true
    stdin_open: true
    depends_on:
      - db
  data:
    image: busybox
    volumes:
      - db-data:/var/lib/mysql
    tty: true
volumes:
  db-data:
    driver: local

Gemfile

hogeapp/Gemfile
source 'https://rubygems.org'
git_source(:github) { |repo| "https://github.com/#{repo}.git" }

gem 'rails', '~> 6.0.2', '>= 6.0.2.1'

Gemfile.lock

中身は空で作成します。

hogeapp/Gemfile.lock

entrypoint.sh

hogeapp/entrypoint.sh
#!/bin/bash
set -e

# Remove a potentially pre-existing server.pid for Rails.
rm -f /app_name/tmp/pids/server.pid

# Then exec the container's main process (what's set as CMD in the Dockerfile).
exec "$@"

これで必要なファイルが揃ったので、次はRailsアプリの作成です。

2.Railsアプリの作成

rails new

$ docker-compose run web rails new . --force --database=mysql --skip-bundle

※このエラーが出たら

Error response from daemon: OCI runtime create failed: container_linux.go:346: starting container process caused "exec: \"rails\": executable file not found in $PATH": unknown

$ docker-compose build

※何らかのgemが不足しているようなエラーが出たら

自分は以下のようなエラーが発生しました。

Could not find public_suffix-4.0.1 in any of the sources

$ docker-compose run web bundle install

一度bundle installを試してみて下さい。

database.ymlの変更

config/database.yml
default: &default
  adapter: mysql2
  encoding: utf8mb4
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  username: root
  password: password #ここを変更
  host: db #ここを変更

コンテナが立ち上がるか確認

docker-compose up -dだとログが見えないので、無事に環境構築が完了するまでは-dなしが良いと思います。

$ docker-compose up

db:create

無事にコンテナが立ち上がったら、DBを作成しましょう。

$ docker-compose exec web rails db:create

Yay! You’re on Rails!

localhost:3000にアクセスして確認してみましょう。

image.png

これでRailsはOKなので、次はVue.jsです!

3.Vue.jsの導入

webpackerのインストール

$ docker-compose exec web rails webpacker:install

Vue.jsのインストール

$ docker-compose exec web rails webpacker:install:vue

Vue.jsとの連携を確認

Railsでコントローラーを作ってみて、Vue.jsと連携出来るかを確認してみます。

$ docker-compose exec web rails g controller home index
app/views/home/index.html.erb
<%= javascript_pack_tag 'hello_vue' %>
<%= stylesheet_pack_tag 'hello_vue' %>

このように変更し、hello_vue.jsを読み込みます。

※Vue.jsのインストール時にhello_vue.jsはデフォルトで作成されています。

Railsのルーティングを設定

config/routes.rb
root to: 'home#index'

ブラウザで確認

localhost:3000にアクセスし、下記画面が出力されているか確認してみて下さい。

image.png

これでVue.jsの導入はOKですが、他の単一ファイルコンポーネントも作成し、読み込めるかどうかを確認しておきます。

4.他の単一ファイルコンポーネントが読み込めるかどうか確認する

Top.vueの作成

app/javascript/components/ディレクトリを作成し、その中にTop.vueを作成します。

Top.vueの中身は以下のようにしました。

app/javascript/components/Top.vue
<template>
  <section id="top">
    <h1>This is Top.vue!</h1>
  </section>
</template>

<script>
  export default {
    name: 'Top'
  }
</script>

<style>
  h1 {
    text-align: center;
  }
</style>

app.vueを変更

app/javascript/app.vue
<template>
  <div id="app">
    <p>{{ message }}</p>
    <Top/> //追記
  </div>
</template>

<script>
import Top from "./components/Top"; //追記

export default {
  data: function () {
    return {
      message: "Hello Vue!"
    }
  },
  components: {
    Top, //追記
  }
}
</script>
...以下略

ブラウザで確認

再度読み込みすると、以下のような画面になっているはずです。

image.png

これできちんと単一ファイルコンポーネントが読み込まれていることが確認できたので、Vue.jsはOKです。

次はラスト!Vuetifyの導入です。

5.Vuetifyの導入

Vuetifyのインストール

$ docker-compose exec web yarn add vuetify -D

hello_vue.jsに追記

hello_vue.js
import Vue from 'vue'
import Vuetify from 'vuetify' //追加
import "vuetify/dist/vuetify.min.css" //追加
import App from '../app.vue'

Vue.use(Vuetify) //追加

const vuetify = new Vuetify(); //追加

document.addEventListener('DOMContentLoaded', () => {
  const app = new Vue({
    vuetify, //追加
    render: h => h(App)
  }).$mount()
  document.body.appendChild(app.$el)

  console.log(app)
})

application.html.erbの変更

application.html.erbでVuetify用にフォントとアイコンの読み込みと不要な箇所の削除を行います。

app/views/layouts/application.html.erb
  <head>
...略
    <link href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900" rel="stylesheet">
    <link href="https://cdn.jsdelivr.net/npm/@mdi/font@4.x/css/materialdesignicons.min.css" rel="stylesheet">
...略
  </head>

下記タグは不要なので削除しておきます。

app/views/layouts/application.html.erb
<%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %>

これで準備が整ったので、あとはVuetifyのコンポーネントが反映されるかどうか、作成して確認してみましょう。

Header.vueの作成

新規にHeader.vueを作成し、<v-app-bar>を組み込んでみます。

app/javascript/components/Header.vue
<template>
  <header id="header">
    <v-app-bar>
      <v-app-bar-nav-icon></v-app-bar-nav-icon>
      <v-toolbar-title>This is Header.vue</v-toolbar-title>
    </v-app-bar>
  </header>
</template>

<script>
  export default {
    name: 'Header',
  }
</script>

app.vueを変更

Header.vueを読み込みつつ、全体をまとめている<div><v-app>タグに変更しておきます。

app/javascript/app.vue
<template>
  <v-app id="app"> //divから変更
    <Header/> //追記
    <h1>This is app.vue</h1> //一応追記。どっちでもいいです
    <p>{{ message }}</p>
    <Top/>
  </v-app>
</template>

<script>
import Header from "./components/Header"; //追記
import Top from "./components/Top";

export default {
  data: function () {
    return {
      message: "Hello Vue!"
    }
  },
  components: {
    Header, //追記
    Top,
  }
}
</script>

これで再度localhost:3000にアクセスし、問題ないかを確認してみます。

image.png

無事に画像のようなヘッダーが表示されていれば完了です!

以上です!お疲れ様でした!:clap:

おわりに

最後まで読んで頂きありがとうございました:bow_tone1:

どなたかのお役に立てれば幸いです:relaxed:

参考にさせて頂いたサイト(いつもありがとうございます)

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

サーバレス環境でbotを動かす(Go & Cloud Run)

こんにちは!私(@f_yamagami)です!
この記事は、All About Group(株式会社オールアバウト) Advent Calendar 2019 クリスマスイブの記事です。

前談(The・前談)

クリスマスイブですね。マジあれですね
みなさんはクリスマスプレゼントに何が欲しいですか?
ゲーム? お金? 休み?
やっぱりエンジニアならサーバですか?
あれ、そこのエンジニアさん
どうしてサーバと聞いて苦い顔をされていらっしゃるんですか?
え? サーバ障害の対応で、せっかくのクリスマスディナーが冷えてしまい、
恋人との関係も冷えてしまった過去が?ざまあみ(ry
※私はサーバにはなんの思い出もないです。

ということで、今回は会社の開発合宿で先輩エンジニアに手取り足取りしていただきながら、サーバレスの環境でbatch処理を行った話を書いていきます。
[先輩のQiitaはこちら↓]
https://qiita.com/y_hideshi/items/085a92746578b5b7f01d

今回作ったのは、先輩のQiitaにもある通り、
定期的にGCPの確約利用割引の期日の自動チェック & アラートあげるbotです。

確約利用割引とは

確約利用割引はGCEの契約を1年間または3年間の支払いを確約する代わりに、特定の量の vCPU、メモリ、GPU、ローカル SSD を割引価格で購入できるものです。

マシンタイプやコア数、適用期間などを指定したコミットメントというものを購入すると、その分の利用分は確約利用割引が適用されるというものです。
雑な説明にはなりますが、年間パスポートのようなものです。
最初に一括で代金をを支払うわけではないので、通常の年間パスポートとは少し違いますが、下記の例を見るとだいたい年間パスポートみたいなものだという計算になると思います。

例:8コア用のコミットメントを購入
- 4コアを使用した月 → 8コア用の確約された利用料金が請求される
- 8コアを使用した月 → 8コア用の確約された利用料金が請求される
- 12コアを使用した月 → 8コア用の確約された利用料金 + 4コア分は通常料金で請求される

詳しくは公式のドキュメントでご確認ください。
https://cloud.google.com/compute/docs/instances/signing-up-committed-use-discounts?hl=ja

前談2 (準備的な)

まず、定期的にGCPの確約利用割引の期日の自動チェック & アラートあげるbotを作るにあたって、実現したいことの認識合わせを先輩としました。

  • 今ある課題
    • 契約している確定利用割引の更新日が近づくと、メールでのアラートは来るけど見落としそう
    • 見落として更新を忘れると通常価格での支払いとなり、損してないけど損した気分になるので嫌だ
  • 実現したいこと
    • 契約している確定利用割引の更新日が近づくと、Slackで通知させたい!

某総理大臣と某大統領のごとく、直接会談の結果、完全に認識が一致しました。
次に、実現方法について考えました。
確定利用割引のコミットメントはAPIで内容が確認できることがわかったので、
下記のように、私は考えました。

1.下記のような簡単なアプリを作る
 ・コミットメントの終了日を取得するAPIを叩いて値を取得
 ・取得したコミットメントの終了日が1週間以内か確認
 ・1週間以内ならSlackのAPIを叩いて通知

2.上記アプリをどっかのサーバに置いてcronで1日1回実行

私「先輩! こんな感じで考えてるんですけど、どこのサーバに置きますか?
てか、新しいサーバを立てますか?」

1先輩「なるほど、君はどこのサーバにアプリを置くか迷っているようだけど、全てが違う。
サーバをどこに置くか以前に、サーバを立てる必要があるのかを考えたか?
貴様の悩むべきところは、サーバ代の金銭的負担だけでなく、
サーバ管理が必要となり、技術的負債まで残してしまうことを、1mmたりとも
考慮できていない己の無知に気が付いていないことではないのか?

あまりの衝撃的なお言葉に、なんとおっしゃっていたのかは正確には忘れてしまいましたが、
実際のところは「え?サーバは使わないでCloud Runでしてみない?」だった気もします。

ということで、今回はサーバレス環境で実装することになりました。
また、私は
- コミットメントの終了日を取得するAPIを叩いて値を取得
- 取得したコミットメントの終了日が近いかの確認
の部分を実装したので、その辺り中心に書こうと思います。

システム構成

(略)
※先輩のQiitaにて詳細に紹介済み
https://qiita.com/y_hideshi/items/085a92746578b5b7f01d#%E3%82%B7%E3%82%B9%E3%83%86%E3%83%A0%E6%A7%8B%E6%88%90

実装

今回はgo言語で実装しました。

main.go
/* 
 * importは省略しています。
 * 愚直に公式のExamplesに沿って認証を通していきます。
 * 認証にApplication Default Credentialsを利用したサンプルです。
 */
ctx := context.Background()

c, err := google.DefaultClient(ctx, compute.CloudPlatformScope)
if err != nil {
  log.Fatal(err)
}

computeService, err := compute.New(c)
if err != nil {
  log.Fatal(err)
}

// プロジェクトとリージョンを指定
project := "my-project"
region := "my-region"

if err := req.Pages(ctx, func(page *compute.CommitmentList) error {

    // 今日の日付
    today_str, _ := time.Parse("20060102", time.Now())

    for _, commitment := range page.Items {
        // 文字列 → 日付型
        endDate, _ := time.Parse("2006-01-02T15:04:05Z07:00", commitment.EndTimestamp)

        // 何日前か比較したいリスト
        dayArray := [3]int{7, 14, 30}
        for _, x := range dayArray {
            // 今日の日付からx日先の日付をcompareDateに設定する
            compareDate := today_str.AddDate(0, 0, x)

            // 日付型を整えた上で、アラートを出すべき日かを確認する。
            if (endDate.Format("2006-01-02") == compareDate.Format("2006-01-02")){
                message_str := "確約利用割引 `" + commitment.Name + "`の期限は `" + endDate.Format("2006-01-02") + "`までです:alert:"
                // pub/subにmessageを公開する
                PubsubFunc(message_str)
            }
        }
    }
    return nil
}); err != nil {
        log.Fatal(err)
}
PubsubFunc関数.go
func PubsubFunc(message_str string) {
    ctx := context.Background()

    client, err := pubsub.NewClient(ctx, "my-client")
    if err != nil {
        log.Fatal(err)
    }

    topicName := "slack-topics"
    topic = client.Topic(topicName)

    exists, err := topic.Exists(ctx)
    if err != nil {
        log.Fatal(err)
    }
    if !exists {
        log.Printf("Topic %v doesn't exist - creating it", topicName)
    }
    // slackに投稿するために必要な要素を設定する
    atr := map[string]string{
        "Color":"warning",
        "Fallback":"メッセージ通知タイトル",
        "AuthorName":"Cloud Run",
        "AuthorSubname":"クラウドラン",
        "AuthorLink":"https://console.cloud.google.com/~~~",
        "AuthorIcon":"https://repository-images.githubusercontent.com/~~~",
        "Text":message_str,
        "Footer":"slackapi",
        "FooterIcon":"https://platform.slack-edge.com/img/~~~.png",
        "WebhookUrl":"https://hooks.slack.com/services/~~~",
    }
    topic.Publish(ctx, &pubsub.Message{Data: []byte("payload"), Attributes: atr})
}

※特にmain.goの方で、ネストが深くなっていたり、無理やりな部分があり、見苦しく申し訳ないです。

最後に

今まではPHPでコードを書いて、すでに構築されているk8sの環境にリリースしたことぐらいしかなく、
go言語もサーバレス環境も全てが新鮮でとても勉強になりました。

また、PubSubにメッセージを公開する部分は、ほとんど先輩に教えていただきました。
実装をGCRにあげるところや環境周りでも大変お世話になり、とても感謝しております。
ありがとうございました。


  1. 私の妄想上での発言です。 

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

neo4jをdocker-composeで使いたい

はじめに

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

brewでmacにneo4j入れたけどうまくいかなかったのでDockerでやったやつのメモ

docker-compose

docker-compose.yml
version: '3'
services:
    neo4j:
        image: neo4j
        ports:
            - 7474:7474
            - 7687:7687
        volumes: 
            - ./neo4j/data:/data
            - ./neo4j/logs:/logs
            - ./neo4j/conf:/conf

imageはneo4jのですね。そのまんまです

ぽーとは7474,7687を通します。

7474はwebインターフェースで7687はプログラムから制御する際に使用するようです。

これで

$ docker-compose up -d

実行して

localhost:7474にアクセスすることで、webインターフェースにアクセスできます。

スクリーンショット 2019-12-23 15.23.40.png

やったね!!

参考

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

Dockerの再入門

仕事でDockerを使うのは2016年から4年近く経ちました。会社の技術雑談会のきっかけで今まで、断片的に習うDockerについての知識を整理してみます。この文章はdockerの操作コマンドやDockerfileの書き方より本来はなんでdockerというものが作られるのか、dockerの後ろに使われる技術を掘り下げます。

目次

  • ITインフラの進化を振り返り
  • 仮想化
  • Docker
  • Dockerの特徴

〜ITインフラの進化を振り返り〜

ITインフラがコンピューターの発明から約70年となっていろんな進化を遂げた。その中に、ITインフラのリソースをいかに効率的に利用できるよう、いろんな技術が誕生しました。現在のサーバ仮想化の原点は1970年代にメインフレームに使われる仮想化技術となり、そのあと、1999ごろにVMWare社の誕生によるx86 CPUサーバ仮想化も一気に進めることになりました。

alt
図は[1]を参考しています。

クラウド時代において不可欠の〜仮想化〜

仮想化といっても、いろんな仮想化が存在しております。ネットワーク仮想化する、サーバ仮想化、アプリケーション仮想化などの多数のキーワードがあります。ここでサーバー仮想化をメインにまとめます。

サーバ仮想化

サーバ仮想化とは、1台の物理サーバ上に複数のサーバとみなしで稼働させることです。
サーバ仮想化をすることで、サーバ台数の減少でコスト削減につながります。
では、サーバ仮想化はどういう手法があるでしょう。ネットで仮想化を検索すると、必ずホスト型とハイパーバイザー型があるという言葉が出てきます。
しかし、wikipediaのハイパーバイザーの定義をみると、実際にはこの二つ型全部ハイパーバイザーとなるといえるでしょう。
そもそも、ハイパーバイザー[2]とはコンピュータ用語における、コンピュータの仮想化技術のひとつである仮想機械(バーチャルマシン)を実現するための、制御プログラムである。仮想化モニタや仮想化OSと呼ばれることもある。
wikipediaの定義によると、ハイパーバイザーは二つのタイプがあります。

  • Type 1(「ネイティブ」または「ベアメタル」)ハイパーバイザ

ハイパーバイザがハードウェア上で直接動作し、全てのOS(ゲストOS)はそのハイパーバイザ上で動作する方式を指す。狭義の「ハイパーバイザ」はこちらのみを指す。

製品:Microsoft Hyper-V、Citrix XenServer

alt

  • Type 2(「ホスト」)ハイパーバイザ

ハードウェア上でまず別のOSが稼働し(このOSをホストOSと呼ぶ)、その上でハイパーバイザが(ホストOSのアプリケーションとして)稼働し、更にはハイパーバイザの上で更に別のOS(このOSをゲストOSと呼ぶ)を稼働させる方法である。狭義においては、Type 2はハイパーバイザには含まれない。

製品:オラクルのVirtualBox、パラレルスのParallels WorkstationとParallels Desktop

alt

よく言われるホスト型仮想化はハイパーバイザーのType2、ハイパーバイザーはType1ということでしょう。

Type 1はリソースの完全分離し、各仮想サーバは直接ハードウェアとやりとりするので、処理スピードが早いというメリットがあります。一方で、導入コストが高いというデメリットもあります。

Type 2は仮想化ソフトウェアをホストOSにインストールすれば、すぐに仮想サーバを構築できることで、導入コストが少ないメリットがありますが、仮想サーバはホストOSを介してハードウェアとやりとりので、オーバヘッドが大きくなて処理スピードが落ちるというデメリットがあります。

Linux container (LXC)

通常、物理サーバー上にインストールしたホストOSでは、1つのOS上で動く複数のアプリケーションは、同じシステムリソースを使います。このとき、動作する複数のアプリケーションは、データを格納するディレクトリを共有し、サーバーに設定された同じIPアドレスで通信します。そのため、複数のアプリケーションで使用しているミドルウェアやライブラリのバージョンが異なる場合などは、お互いのアプリケーションが影響を受けないよう注意が必要です[3]。
サーバ仮想化をすることで、OS丸ごとに分離し、各仮想OSに一個のアプリケーションを実装することは完全に上記の課題を解決するのが可能ですが、そうすると、物理サーバの利用率が非常に悪いでしょう。そのため、ここでハイパーバイザーによりハイレベルで軽量の仮想化技術が誕生しました。

LXC(英語: Linux Containers)[4]は、1つのLinuxカーネルを実行しているコントロールホスト上で、複数の隔離されたLinuxシステム(コンテナ)を走らせる、OSレベル仮想化のソフトウェアである。

Linuxカーネルがcgroupsという機能を提供を利用することで、リソース(CPU、メモリ、ブロックI/O、ネットワークなど)の制限と優先順位付けが可能になっており、そのために仮想マシンを使用する必要がない。また、名前空間の隔離機能を利用すれば、アプリケーションから見たオペレーティング・システムの環境を完全に隔離することができるため、プロセスツリー、ネットワーク、ユーザー識別子、マウント(英語版)されたファイルシステムを仮想化することができる。

LXCはカーネルのcroupsと隔離された名前空間のサポートを組み合わせることで、アプリケーションのために隔離された環境を提供する。

cgroup

cgroups (control groups) [5]とは、プロセスグループのリソース(CPU、メモリ、ディスクI/Oなど)の利用を制限・隔離するLinuxカーネルの機能。"process containers" という名称で Rohit Seth が2006年9月から開発を開始し、2007年に cgroups と名称変更され、2008年1月に Linux カーネル 2.6.24 にマージされた。それ以来、多くの機能とコントローラが追加された。

Linux man[6]の説明は以下の通りです。

Control groups, usually referred to as cgroups, are a Linux kernel feature which allow processes to be organized into hierarchical groups whose usage of various types of resources can then be limited and monitored. The kernel's cgroup interface is provided through a pseudo-filesystem called cgroupfs. Grouping is implemented in the core cgroup kernel code, while resource tracking and limits are implemented in a set of per-resource-type subsystems (memory, CPU, and so on).

Subsystemsはカーネルのリソースモジュールとして理解すればいいだと思います。cgroupがコントロールするSubsystemsは以下の通りです。

  cpu (since Linux 2.6.24; CONFIG_CGROUP_SCHED)
          Cgroups can be guaranteed a minimum number of "CPU shares"
          when a system is busy.  This does not limit a cgroup's CPU
          usage if the CPUs are not busy.  For further information, see
          Documentation/scheduler/sched-design-CFS.txt.

          In Linux 3.2, this controller was extended to provide CPU
          "bandwidth" control.  If the kernel is configured with CON‐
          FIG_CFS_BANDWIDTH, then within each scheduling period (defined
          via a file in the cgroup directory), it is possible to define
          an upper limit on the CPU time allocated to the processes in a
          cgroup.  This upper limit applies even if there is no other
          competition for the CPU.  Further information can be found in
          the kernel source file Documentation/scheduler/sched-bwc.txt.

   cpuacct (since Linux 2.6.24; CONFIG_CGROUP_CPUACCT)
          This provides accounting for CPU usage by groups of processes.

          Further information can be found in the kernel source file
          Documentation/cgroup-v1/cpuacct.txt.

   cpuset (since Linux 2.6.24; CONFIG_CPUSETS)
          This cgroup can be used to bind the processes in a cgroup to a
          specified set of CPUs and NUMA nodes.

          Further information can be found in the kernel source file
          Documentation/cgroup-v1/cpusets.txt.

   memory (since Linux 2.6.25; CONFIG_MEMCG)
          The memory controller supports reporting and limiting of
          process memory, kernel memory, and swap used by cgroups.

          Further information can be found in the kernel source file
          Documentation/cgroup-v1/memory.txt.

   devices (since Linux 2.6.26; CONFIG_CGROUP_DEVICE)
          This supports controlling which processes may create (mknod)
          devices as well as open them for reading or writing.  The
          policies may be specified as allow-lists and deny-lists.
          Hierarchy is enforced, so new rules must not violate existing
          rules for the target or ancestor cgroups.

          Further information can be found in the kernel source file
          Documentation/cgroup-v1/devices.txt.

   freezer (since Linux 2.6.28; CONFIG_CGROUP_FREEZER)
          The freezer cgroup can suspend and restore (resume) all pro‐
          cesses in a cgroup.  Freezing a cgroup /A also causes its
          children, for example, processes in /A/B, to be frozen.

          Further information can be found in the kernel source file
          Documentation/cgroup-v1/freezer-subsystem.txt.

   net_cls (since Linux 2.6.29; CONFIG_CGROUP_NET_CLASSID)
          This places a classid, specified for the cgroup, on network
          packets created by a cgroup.  These classids can then be used
          in firewall rules, as well as used to shape traffic using
          tc(8).  This applies only to packets leaving the cgroup, not
          to traffic arriving at the cgroup.

          Further information can be found in the kernel source file
          Documentation/cgroup-v1/net_cls.txt.

   blkio (since Linux 2.6.33; CONFIG_BLK_CGROUP)
          The blkio cgroup controls and limits access to specified block
          devices by applying IO control in the form of throttling and
          upper limits against leaf nodes and intermediate nodes in the
          storage hierarchy.

          Two policies are available.  The first is a proportional-
          weight time-based division of disk implemented with CFQ.  This
          is in effect for leaf nodes using CFQ.  The second is a throt‐
          tling policy which specifies upper I/O rate limits on a
          device.

          Further information can be found in the kernel source file
          Documentation/cgroup-v1/blkio-controller.txt.

   perf_event (since Linux 2.6.39; CONFIG_CGROUP_PERF)
          This controller allows perf monitoring of the set of processes
          grouped in a cgroup.

          Further information can be found in the kernel source file
          tools/perf/Documentation/perf-record.txt.

   net_prio (since Linux 3.3; CONFIG_CGROUP_NET_PRIO)
          This allows priorities to be specified, per network interface,
          for cgroups.

          Further information can be found in the kernel source file
          Documentation/cgroup-v1/net_prio.txt.

   hugetlb (since Linux 3.5; CONFIG_CGROUP_HUGETLB)
          This supports limiting the use of huge pages by cgroups.

          Further information can be found in the kernel source file
          Documentation/cgroup-v1/hugetlb.txt.

   pids (since Linux 4.3; CONFIG_CGROUP_PIDS)
          This controller permits limiting the number of process that
          may be created in a cgroup (and its descendants).

          Further information can be found in the kernel source file
          Documentation/cgroup-v1/pids.txt.

   rdma (since Linux 4.11; CONFIG_CGROUP_RDMA)
          The RDMA controller permits limiting the use of RDMA/IB-spe‐
          cific resources per cgroup.

          Further information can be found in the kernel source file
          Documentation/cgroup-v1/rdma.txt.

例:CPU利用率を制限

cgroupの操作はcgroupfsというファイルシステムを経由で行います。基本的にLinuxが起動する際に、cgroupfsを自動的にmountをする。

$ mount | grep cgroup
cgroup on /sys/fs/cgroup/unified type cgroup2 (rw,nosuid,nodev,noexec,relatime,nsdelegate)
cgroup on /sys/fs/cgroup/systemd type cgroup (rw,nosuid,nodev,noexec,relatime,xattr,name=systemd)
cgroup on /sys/fs/cgroup/cpu,cpuacct type cgroup (rw,nosuid,nodev,noexec,relatime,cpu,cpuacct)
cgroup on /sys/fs/cgroup/blkio type cgroup (rw,nosuid,nodev,noexec,relatime,blkio)
cgroup on /sys/fs/cgroup/hugetlb type cgroup (rw,nosuid,nodev,noexec,relatime,hugetlb)
cgroup on /sys/fs/cgroup/memory type cgroup (rw,nosuid,nodev,noexec,relatime,memory)
cgroup on /sys/fs/cgroup/freezer type cgroup (rw,nosuid,nodev,noexec,relatime,freezer)
cgroup on /sys/fs/cgroup/pids type cgroup (rw,nosuid,nodev,noexec,relatime,pids)
cgroup on /sys/fs/cgroup/perf_event type cgroup (rw,nosuid,nodev,noexec,relatime,perf_event)
cgroup on /sys/fs/cgroup/net_cls,net_prio type cgroup (rw,nosuid,nodev,noexec,relatime,net_cls,net_prio)
cgroup on /sys/fs/cgroup/devices type cgroup (rw,nosuid,nodev,noexec,relatime,devices)
cgroup on /sys/fs/cgroup/rdma type cgroup (rw,nosuid,nodev,noexec,relatime,rdma)
cgroup on /sys/fs/cgroup/cpuset type cgroup (rw,nosuid,nodev,noexec,relatime,cpuset)

ここから、cgroupでアプリのCPU使用率を制限する操作を行います。すべての操作はubuntu18.04で行います。

  1. まず、簡単な無限ループのcプログラムを作成します。
loop_sample_cpu.c
#include <stdio.h>

int main(){
        while(1){
        }
}

プログラムをコンパイルします。

$ gcc -o loop_sample_cpu loop_sample_cpu.c

実行します。

$./loop_sample_cpu

CPU利用率を確認する。loop_sample_cpuの利用率はほぼ100%に近いです。

zhenbin@zhenbin-VirtualBox:~$ top

top - 14:51:45 up 28 min,  1 user,  load average: 0.29, 0.08, 0.02
Tasks: 175 total,   2 running, 140 sleeping,   0 stopped,   0 zombie
%Cpu(s): 98.6 us,  1.4 sy,  0.0 ni,  0.0 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
KiB Mem :  8345424 total,  6421164 free,   875200 used,  1049060 buff/cache
KiB Swap:  2097148 total,  2097148 free,        0 used.  7187568 avail Mem 

  PID USER      PR  NI    VIRT    RES    SHR S %CPU %MEM     TIME+ COMMAND      
10403 zhenbin   20   0    4372    764    700 R 95.0  0.0   0:19.75 loop_sample_cpu  
 9547 zhenbin   20   0 3020252 278368 108992 S  2.3  3.3   0:09.34 gnome-shell  
10342 zhenbin   20   0  870964  38352  28464 S  1.0  0.5   0:00.88 gnome-termi+ 
 9354 zhenbin   20   0  428804  95048  61820 S  0.7  1.1   0:02.25 Xorg         
  922 root      20   0  757084  82776  45764 S  0.3  1.0   0:00.50 dockerd      
    1 root      20   0  159764   8972   6692 S  0.0  0.1   0:01.06 systemd      
    2 root      20   0       0      0      0 S  0.0  0.0   0:00.00 kthreadd     
    3 root       0 -20       0      0      0 I  0.0  0.0   0:00.00 rcu_gp       
    4 root       0 -20       0      0      0 I  0.0  0.0   0:00.00 rcu_par_gp   
    6 root       0 -20       0      0      0 I  0.0  0.0   0:00.00 kworker/0:0+ 
    7 root      20   0       0      0      0 I  0.0  0.0   0:00.15 kworker/u2:+ 
    8 root       0 -20       0      0      0 I  0.0  0.0   0:00.00 mm_percpu_wq 
    9 root      20   0       0      0      0 S  0.0  0.0   0:00.13 ksoftirqd/0  
   10 root      20   0       0      0      0 I  0.0  0.0   0:00.16 rcu_sched    
   11 root      rt   0       0      0      0 S  0.0  0.0   0:00.00 migration/0  
   12 root     -51   0       0      0      0 S  0.0  0.0   0:00.00 idle_inject+ 
   13 root      20   0       0      0      0 I  0.0  0.0   0:00.11 kworker/0:1+ 

  1. cgroupにCPU制限を追加する /sys/fs/cgroup/cpu,cpuacctの下に、フォルダを作成する。
$ cd /sys/fs/cgroup/cpu,cpuacct
$ mkdir loop_sample_cpu
$ cd loop_sample_cpu

loop_sample_cpuのPIDをcgroupに追加する。ここで、rootユーザしか行われない。

$ sudo su
$ echo 10403 > cgroup.procs

CPUの制限を追加する。loop_sample_cpuのフォルダにいくつかリソースの制限タイプがありますが、今回には操作対象はこの二つです。ほかの項目の意味は[7]をご参考となります。

  • cpu.cfs_period_us
       cgroup による CPU リソースへのアクセスを再割り当てする一定間隔をマイクロ秒単位 (µs、ただしここでは "us" と表示) で指定します。cgroup 内のタスクが 1 秒あたり 0.2 秒間、単一の CPU にアクセスできる必要がある場合には、cpu.cfs_quota_us を 200000に、cpu.cfs_period_us を 1000000 に設定してください。cpu.cfs_quota_us パラメーターの上限は 1 秒、下限は 1000 マイクロ秒です。

  • cpu.cfs_quota_us
       cgroup 内の全タスクが (cpu.cfs_period_us で定義された) 一定の期間に実行される合計時間をマイクロ秒単位 (µs、ただしここでは "us" と表示) で指定します。クォータによって指定された時間を cgroup 内のタスクがすべて使い切ってしまうと、その期間により指定されている残りの時間はタスクがスロットリングされ、次の期間まで実行を許可されなくなります。cgroup 内のタスクが 1 秒あたり 0.2 秒間、単一の CPU にアクセスできる必要がある場合には cpu.cfs_quota_us を 200000 に、cpu.cfs_period_us を 1000000 に設定します。クォータおよび期間のパラメーターは CPU ベースで動作する点に注意してください。プロセスが 2 つの CPU を完全に使用できるようにするには、たとえば、cpu.cfs_quota_us を 200000 に、 cpu.cfs_period_us を 100000 に設定します。
    cpu.cfs_quota_us の値を -1 に設定すると、cgroup が CPU 時間制限を順守しないことを示します。これは、全 cgroup のデフォルト値でもあります (root cgroup は除く)。

CPUを1コアに20%に制限します。(50msごとに10msのCPU時間をしか利用できない)

$ echo 10000 > cpu.cfs_quota_us
$ echo 50000 > cpu.cfs_period_us

loop_sample_cpuのCPU利用率が20%に制限されています。

zhenbin@zhenbin-VirtualBox:~$ top

top - 15:06:05 up 42 min,  1 user,  load average: 0.40, 0.72, 0.57
Tasks: 181 total,   2 running, 146 sleeping,   0 stopped,   0 zombie
%Cpu(s): 23.8 us,  1.0 sy,  0.0 ni, 75.2 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
KiB Mem :  8345424 total,  6366748 free,   912068 used,  1066608 buff/cache
KiB Swap:  2097148 total,  2097148 free,        0 used.  7134248 avail Mem 

  PID USER      PR  NI    VIRT    RES    SHR S %CPU %MEM     TIME+ COMMAND      
10403 zhenbin   20   0    4372    764    700 R 19.9  0.0  12:16.90 loop_sample_cpu  
 9547 zhenbin   20   0 3032212 287524 111556 S  1.7  3.4   0:18.14 gnome-shell  
 9354 zhenbin   20   0  458868 125556  77832 S  1.3  1.5   0:06.06 Xorg         
10342 zhenbin   20   0  873156  40500  28464 S  1.0  0.5   0:03.34 gnome-termi+ 
 9998 zhenbin   20   0 1082256 120516  36164 S  0.3  1.4   0:01.92 gnome-softw+ 
    1 root      20   0  159764   8972   6692 S  0.0  0.1   0:01.12 systemd      
    2 root      20   0       0      0      0 S  0.0  0.0   0:00.00 kthreadd     
    3 root       0 -20       0      0      0 I  0.0  0.0   0:00.00 rcu_gp       
    4 root       0 -20       0      0      0 I  0.0  0.0   0:00.00 rcu_par_gp   
    6 root       0 -20       0      0      0 I  0.0  0.0   0:00.00 kworker/0:0+ 
    7 root      20   0       0      0      0 I  0.0  0.0   0:00.24 kworker/u2:+ 
    8 root       0 -20       0      0      0 I  0.0  0.0   0:00.00 mm_percpu_wq 
    9 root      20   0       0      0      0 S  0.0  0.0   0:00.16 ksoftirqd/0  
   10 root      20   0       0      0      0 I  0.0  0.0   0:00.22 rcu_sched    
   11 root      rt   0       0      0      0 S  0.0  0.0   0:00.00 migration/0  
   12 root     -51   0       0      0      0 S  0.0  0.0   0:00.00 idle_inject+ 
   14 root      20   0       0      0      0 S  0.0  0.0   0:00.00 cpuhp/0      

Namespace

名前空間[8]は、 グローバルシステムリソースを抽象化層で覆うことで、 名前空間内のプロセスに対して、 自分たちが専用の分離されたグローバルリソースを持っているかのように見せる仕組みである。 グローバルリソースへの変更は、 名前空間のメンバーである他のプロセスには見えるが、 それ以外のプロセスには見えない。 名前空間の一つの利用方法はコンテナーの実装である。

例:Network Namespace

Network Namespaceを利用して一個のNICに二つ仮想のネットワークを作り出すことが可能です。

  1. Network Namespaceを作成する
zhenbin@zhenbin-VirtualBox:~$ sudo unshare --uts --net /bin/bash
root@zhenbin-VirtualBox:~# hostname container001
root@zhenbin-VirtualBox:~# exec bash
root@container001:~# ip link set lo up
root@container001:~# ifconfig
lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        inet6 ::1  prefixlen 128  scopeid 0x10<host>
        loop  txqueuelen 1000  (Local Loopback)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
root@container001:~# echo $$ # $$はコマンド自身の PID (プロセスID)が設定される変数
1909
  1. 仮想ネットワークインターフェースリンクを作成します。仮想ネットワークインターフェスの説明は[9]をご参考。 新しいshellを立ち上げる。リンクペーアを作成する。
$ sudo ip link add veth0 type veth peer name veth1

veth1を先ほど作成したnetwork namespaceに割り当て。

$ sudo ip link set veth1 netns 1909

veth0のセットアップする

$ sudo ip address add dev veth0 192.168.100.1/24
$ sudo ip link set veth0 up

container001のshellでveth1をセットアップする。

$ sudo ip address add dev veth1 192.168.100.2/24
$ sudo ip link set veth1 up

ホストとcontainer001の間に通信できるようになりました。

zhenbin@zhenbin-VirtualBox:~$ ping 192.168.100.2
PING 192.168.100.2 (192.168.100.2) 56(84) bytes of data.
64 bytes from 192.168.100.2: icmp_seq=1 ttl=64 time=0.019 ms
64 bytes from 192.168.100.2: icmp_seq=2 ttl=64 time=0.037 ms
^C
--- 192.168.100.2 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1001ms
rtt min/avg/max/mdev = 0.019/0.028/0.037/0.009 ms

LXCを本格に活用するDockerの登場

初期のバージョンのDockerはLXCをコンテナ実行ドライバとして利用していたが、Docker v0.9ではオプションとなり、Docker v1.10でサポートが終了した。それ以降のDockerはDocker社が開発したlibcontainerというライブラリを利用してcgroupとnamespaceをコントロールします。

ここで、一つ重要なポイントがあります。Dockerというものが仮想化の技術ではないこと!どちらというと、cgroupとnamespaceの管理ツールとしての存在です。開発者やサーバ運用の人がもっと簡単にlinuxカーネルが提供している仮想化機能を利用できるようなツールです。
さらに、DockerfileとDocker Hubの存在による、アプリケーションのカプセル化と移植性にも向上させました!

ここで、dockerの一般的なコマンドよりdockerのリソースの隔離とコントロールの特性を強調したいと思います。

例:dockerでcontainerのCPU使用率を制限する。

  1. アプリケーションを含むdocker containerを作成する。

先ほどで作成したloop_sample_cpu.cというプログラムを含むubuntuベースのdocker imageを作成する。

FROM ubuntu

RUN apt update && apt install -y gcc

WORKDIR /src

COPY loop_sample_cpu.c .

RUN gcc -o loop_sample_cpu loop_sample_cpu.c

CMD ./loop_sample_cpu

docker imageをビルドする。

docker build -t ubuntu_cpu .
  1. CPU制限しないコンテナを起動する。
docker run -d ubuntu_cpu 

CPU利用率をみてみましょう。

zhenbin@zhenbin-VirtualBox:~/workspace/presentation$ top

top - 17:06:45 up 43 min,  1 user,  load average: 0.89, 0.56, 0.37
Tasks: 178 total,   2 running, 142 sleeping,   0 stopped,   0 zombie
%Cpu(s): 99.0 us,  1.0 sy,  0.0 ni,  0.0 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
KiB Mem :  8345424 total,  6156972 free,   894060 used,  1294392 buff/cache
KiB Swap:  2097148 total,  2097148 free,        0 used.  7184360 avail Mem 

  PID USER      PR  NI    VIRT    RES    SHR S %CPU %MEM     TIME+ COMMAND                                                              
 8853 root      20   0    4372    804    740 R 94.0  0.0   0:15.97 loop_sample_cpu                                                      
 1253 zhenbin   20   0 3020528 278012 108704 S  2.0  3.3   0:31.41 gnome-shell                                                          
 1056 zhenbin   20   0  424560  90824  55364 S  1.3  1.1   0:09.92 Xorg                                                                 
 1927 zhenbin   20   0  877384  44356  28584 S  1.3  0.5   0:08.01 gnome-terminal-                                                      
    1 root      20   0  225292   9040   6724 S  0.0  0.1   0:01.62 systemd                                                              
    2 root      20   0       0      0      0 S  0.0  0.0   0:00.00 kthreadd                                                             
    3 root       0 -20       0      0      0 I  0.0  0.0   0:00.00 rcu_gp                                                               
    4 root       0 -20       0      0      0 I  0.0  0.0   0:00.00 rcu_par_gp                                                           
    6 root       0 -20       0      0      0 I  0.0  0.0   0:00.00 kworker/0:0H-kb                                                      
    8 root       0 -20       0      0      0 I  0.0  0.0   0:00.00 mm_percpu_wq                                                         
    9 root      20   0       0      0      0 S  0.0  0.0   0:00.29 ksoftirqd/0                                                          
   10 root      20   0       0      0      0 I  0.0  0.0   0:00.31 rcu_sched                                                            
   11 root      rt   0       0      0      0 S  0.0  0.0   0:00.00 migration/0                                                          
   12 root     -51   0       0      0      0 S  0.0  0.0   0:00.00 idle_inject/0                                                        
   14 root      20   0       0      0      0 S  0.0  0.0   0:00.00 cpuhp/0                                                              
   15 root      20   0       0      0      0 S  0.0  0.0   0:00.00 kdevtmpfs                                                            
   16 root       0 -20       0      0      0 I  0.0  0.0   0:00.00 netns                                                                

  1. CPUを制限するコンテナを起動する。
docker run -d --cpu-period=50000 --cpu-quota=10000 ubuntu_cpu 

CPUの利用率をチェックしてみましょう。

zhenbin@zhenbin-VirtualBox:~$ top

top - 17:08:50 up 45 min,  1 user,  load average: 0.77, 0.68, 0.45
Tasks: 178 total,   2 running, 141 sleeping,   0 stopped,   0 zombie
%Cpu(s): 25.8 us,  2.3 sy,  0.0 ni, 71.9 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
KiB Mem :  8345424 total,  6160808 free,   892384 used,  1292232 buff/cache
KiB Swap:  2097148 total,  2097148 free,        0 used.  7188556 avail Mem 

  PID USER      PR  NI    VIRT    RES    SHR S %CPU %MEM     TIME+ COMMAND      
 9066 root      20   0    4372    800    740 R 19.9  0.0   0:04.36 loop_sample+ 
 1253 zhenbin   20   0 3017968 275536 106144 S  3.0  3.3   0:32.83 gnome-shell  
 1056 zhenbin   20   0  422000  88336  52876 S  2.7  1.1   0:10.59 Xorg         
 1927 zhenbin   20   0  877380  44468  28584 S  2.0  0.5   0:08.54 gnome-termi+ 
  580 root      20   0  776548  46696  24888 S  0.3  0.6   0:02.71 containerd   
 1202 zhenbin   20   0  193504   2912   2536 S  0.3  0.0   0:03.92 VBoxClient   
 1461 zhenbin   20   0  441756  22836  17820 S  0.3  0.3   0:00.09 gsd-wacom    
 1475 zhenbin   20   0  670048  23676  18316 S  0.3  0.3   0:00.29 gsd-color    
    1 root      20   0  225292   9040   6724 S  0.0  0.1   0:01.65 systemd      
    2 root      20   0       0      0      0 S  0.0  0.0   0:00.00 kthreadd     
    3 root       0 -20       0      0      0 I  0.0  0.0   0:00.00 rcu_gp       
    4 root       0 -20       0      0      0 I  0.0  0.0   0:00.00 rcu_par_gp   
    6 root       0 -20       0      0      0 I  0.0  0.0   0:00.00 kworker/0:0+ 
    8 root       0 -20       0      0      0 I  0.0  0.0   0:00.00 mm_percpu_wq 
    9 root      20   0       0      0      0 S  0.0  0.0   0:00.30 ksoftirqd/0  
   10 root      20   0       0      0      0 I  0.0  0.0   0:00.32 rcu_sched    
   11 root      rt   0       0      0      0 S  0.0  0.0   0:00.00 migration/0  

/sys/fs/cgroup/cpu,cpuacctをチェックすると、dockerというフォルダが作られることがわかりました。

zhenbin@zhenbin-VirtualBox:/sys/fs/cgroup/cpu,cpuacct$ ls
cgroup.clone_children  cpuacct.usage             cpuacct.usage_percpu_user  cpu.cfs_quota_us  notify_on_release  user.slice
cgroup.procs           cpuacct.usage_all         cpuacct.usage_sys          cpu.shares        release_agent
cgroup.sane_behavior   cpuacct.usage_percpu      cpuacct.usage_user         cpu.stat          system.slice
cpuacct.stat           cpuacct.usage_percpu_sys  cpu.cfs_period_us          docker            tasks

ここで、dockerでcgroupとnamespaceを利用することでアプリケーションがランタイムのリソースを制限することがわかりました。

Dockerの特徴

  • 上記の説明のようにdockerはあくまで管理ツールで、つまり、OSの一つのプロセスということです。そのため、バーチャルマシーンより起動が早い。
  • 移植性がいい。Dockerfileがアプリケーションが実行するような環境(ライブラリ、パラメータ)を記載するので、どこでも同じアプリケーションを再現できること
  • 一個のコンテナに一個のプロセスを実行すること。基本的にはdocker containerには複数のプロセスを実行させるのがおすすめしません。複数プロセスを実行させると、結局、アプリケーションをホストに同じ環境に実行することと変わらない、リソースの分離ができなくなる。
  • kernelやハードウェアの変更ができない。ホスト上に実行するcontainerはホストのカーネルを共有するので、カーネルモジュールの変更を行うと、すべてのcontainerに影響があります。あと、ハードウェアに対しても制限が多い。例えば、usbなどの操作にはかなり問題があります。
  • docker container内部のファイルシステムはメモリ上に保存するので、containerが削除されると、データも削除されますので、データ永続化をしたい場合にはホストのファイルシステムをcontainerにマウントしないといけない。でも、一般的にdockerに実行するアプリケーションがstatelessにするのがおすすめです。

参考URL

[1] https://blogs.itmedia.co.jp/itsolutionjuku/2017/10/1it_1.html
[2] https://ja.wikipedia.org/wiki/%E3%83%8F%E3%82%A4%E3%83%91%E3%83%BC%E3%83%90%E3%82%A4%E3%82%B6
[3] https://codezine.jp/article/detail/11336
[4] https://ja.wikipedia.org/wiki/LXC
[5] https://ja.wikipedia.org/wiki/Cgroups
[6] http://man7.org/linux/man-pages/man7/cgroups.7.html
[7] https://access.redhat.com/documentation/ja-jp/red_hat_enterprise_linux/6/html/resource_management_guide/sec-cpu
[8] https://linuxjm.osdn.jp/html/LDP_man-pages/man7/namespaces.7.html
[9] https://gihyo.jp/admin/serial/01/linux_containers/0006

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

Dockerで作る、最強の移動型開発環境

まえがき

この記事は富士通システムズウェブテクノロジー Advent Calendar 2019 24日目の投稿です
(お約束)
記事の内容は全て個人の見解であり、執筆内容は執筆者自身の責任です。所属する組織は関係ありません。

目的

皆さん、コーディングは好きですか?私は好きです。
ただ、電車での移動中にPCを鞄から取り出して、IDEを立ち上げて・・・は電車の中などではちょっと難しいですし、
少し恥ずかしいですよね?

そんな時、スマホでコーディングができたらなぁ。
なんて思いませんか?

そんな時に役立つものをご紹介したいと思います

利用しているOSSの紹介

  • coder
    • ブラウザ上でほぼそのままのVSCodeが使えるクラウドIDEサービス
    • 拡張機能もそのまま使えるものが多い
    • 公式Dockeイメージが公開されており、手早く利用開始できます。

環境構成

ハイパー雑な構成イメージはこんな感じです。

code-server.png

  • code-serverはメインのコンテナです。ここの上に開発環境をセットアップします
  • other containerは、その他ミドル(DBなど)を動作させるコンテナです

環境を試したPCは以下

  • macOS Catalina 10.15.2
  • docker 19.03.5
  • docker-compose 1.24.1

完成品

docker-composeを使用して、デプロイできるようにしてあります。

docker-compose.ymlの中身です
version: '3'

services:
  coder-server:
    build:
      context: .
      dockerfile: coder/Dockerfile
    volumes:
      - ./code-server/coder.json:/home/coder/.local/share/code-server/coder.json # code-serverの設定ファイル
      - ./code-server/languagepacks.json:/home/coder/.local/share/code-server/languagepacks.json # code-serverの設定ファイル
      - ./code-server/User:/home/coder/.local/share/code-server/User # code-serverの設定ファイル
      - ./code-server/extensions:/home/coder/.local/share/code-server/extensions # code-serverの拡張機能
      - ./project:/home/coder/project # projectディレクトリ 
      - /var/run/docker.sock:/var/run/docker.sock # docker sock
    ports: 
      - 8080:8080 # exported ports
    environment:
      PASSWORD: P@sw0rd # 認証パスワード
    entrypoint: "code-server --allow-http --no-auth"
  postgres:
    # アプリから使用するミドルなど
    image: postgres:9.6-alpine
    ports:
      - 15432:5432
    environment:
      POSTGRES_USER: root
      POSTGRES_PASSWORD: P@s5w0rd
      POSTGRES_DB: pg_data
      POSTGRES_INITDB_ARGS: "--encoding=UTF-8"
    restart: always

coder/Dockerfileの中身です
# 公式イメージをそのままベースイメージとして使用します。
# ここでビルドしたイメージに、自分の開発に必要なバイナリをインストールしていきます
FROM codercom/code-server AS baseImage

USER root

# DinDではないのですが、ホストのDocker daemonを共有して、自由にコンテナを建てられるようにします
# docker-cliのみインストールします
RUN apt-get update -y \
 && apt-get install -y \
    apt-transport-https \
    ca-certificates \
    curl \
    gnupg-agent \
    software-properties-common \
 && curl -fsSL https://download.docker.com/linux/ubuntu/gpg | apt-key add - \
 && add-apt-repository \
   "deb [arch=amd64] https://download.docker.com/linux/ubuntu \
   $(lsb_release -cs) \
   stable" \
 && apt-get update -y && apt-get install -y docker-ce-cli \
 && apt-get clean && rm -rf /var/lib/apt/lists/*

FROM baseImage

# ここから、アプリの開発環境に必要なものを入れていきます

# scalaの開発環境を作るため、Javaとsbtをインストールしていきます

ENV JAVA_HOME /usr/java/jdk1.8.232/
ENV SBT_HOME /usr/lib/sbt

# 毎回 sudo するのが面倒なので、rootで実行しちゃいます
USER root

# adoptOpenJDKをバイナリでダウンロードしてきてインストールします
RUN mkdir -p ${JAVA_HOME} \
 && wget -q -O - https://github.com/AdoptOpenJDK/openjdk8-binaries/releases/download/jdk8u232-b09/OpenJDK8U-jdk_x64_linux_hotspot_8u232b09.tar.gz | \
    tar zxf - -C ${JAVA_HOME} --strip=1
# sbtをバイナリでダウンロードしてきてインストールします
RUN mkdir ${SBT_HOME} \
 && wget -q -O - https://piccolo.link/sbt-1.3.5.tgz \
  | tar zxf - -C ${SBT_HOME} --strip=1

USER coder
# パスを設定して完成!
ENV PATH $PATH:${JAVA_HOME}/bin:${SBT_HOME}/bin

実行してみる

docker-composeを実行してみます!

% docker-compose up -d
Creating network "docker_default" with the default driver
Creating docker_coder-server_1 ... done
Creating docker_postgres_1     ... done

% docker ps
CONTAINER ID        IMAGE                 COMMAND                  CREATED             STATUS              PORTS                     NAMES
c3655e1c8d6a        docker_coder-server   "code-server --allow…"   48 seconds ago      Up 47 seconds       0.0.0.0:8080->8080/tcp    docker_coder-server_1
4803094ccaef        postgres:9.6-alpine   "docker-entrypoint.s…"   48 seconds ago      Up 47 seconds       0.0.0.0:15432->5432/tcp   docker_postgres_1

postgresコンテナと、coder-serverコンテナが起動しましたね!
5.PNG

早速ブラウザからアクセスしてみましょう!

1.png

パスワードの入力が求められるので、PASSWORD に設定したものを入力して、Enterします

おめでとうございます。あなたの開発環境ができました!

2.png

デフォルトでgitなどもインストールされているため、リポジトリをクローンしてきて、開発を開始することができます!

iPhoneから確認

iPhoneからも確認できるか試してみましょう

4.PNG

同様にしてログイン

以前プロジェクトやフォルダを開いていると、以下のようにワークスペースが自動的に開かれます

5.PNG

ターミナル操作などもできます

左下の歯車アイコン>Command Palette>Open New Terminal

7.PNG

8.PNG

iPhoneのデフォルトのキーボードだと、ショートカットやタブが使用できないので、
外付けのキーボードでの使用をお勧めします。

最後に

  • MavenやGradleを使った開発をされる方は、DockerfilのFROM baseImage 以降から、sbtのインストールを除外すれば問題ないでしょう。
    (リポジトリにwrapperがコミットされている場合)
    • wrapperがない場合は、ビルドツールも同時にインストールしてしまえばいいと思います
  • その他開発言語の場合でも、Javaなどと同様にバイナリをダウンロードして展開・パスを通す方法が早いし確実だと思います。
  • UserLAndなどを使用して、開発環境を整えている方もいらっしゃったりするようですが、結局vimしか使えなかったり結構しんどそうなので、使い慣れたVSCodeがそのまま使えるっていうのはやっぱりいいなぁと。
    • 最悪このままクラウドのサーバ上にIDEを起動しておけばいつでも開発できる?!
    • ただし今の設定のままだとセキュリティがザルなので、絶対インターネット公開はしないでくださいね
  • ほんとは、Remote Developmentを使って開発環境は別コンテナにしたかったんだけど、いったいなぜ使用できないのか・・・
((h = (s = "めりくり", n = true, r = Math.floor(Math.random() * s.length), t1 = s.substr(r, 1)) => n ? (rp = 2) => (t1 + h(s.replace(t1, ""), false)).repeat(rp) : t1) => `${h()(1)}${h()(1)}`)()
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Docker上のMariaDBにCSVファイルをインポート

# ローカルにあるファイルをdockerにコピー
$ docker cp {csv-file-name}.csv {docker-container}:{path}

# dockerに入る
$ docker exec -it {docker-container} mysql -u {user} -p

インポート

> LOAD DATA LOCAL INFILE 'path/{csv-file-name}.csv' 
INTO TABLE zo_job_tags 
FIELDS 
  TERMINATED BY ',' 
  OPTIONALLY ENCLOSED BY '"' 
  ESCAPED BY '';
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

byebugでよく使うコマンド

バッチ処理のコンテナに入る

$ docker attach container_name

よく使うコマンド

byebugで止めた場所を確認

(byebug) list

byebugコマンド一覧を表示

(byebug) help

next

(byebug) n

次のループに回す

(byebug) c

byebugの終了

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

【VScodeマン】ローカル環境汚さずに、簡単に自己学習環境用意したい!!(remote-container)

「〇〇の勉強したい!!」

そう思ったら、環境を用意しなきゃ!!
ですが、その度に必要な環境を全てインストールしていたら、
ローカルの環境がごちゃごちゃに!(glitchとかあるんですが?みたいなのは考えないでください)

ローカル環境汚さないといえば、Dockerがありますよね。
ただ、Dockerでコンテナ建てて、ローカルで書いたファイルをマウントして実行して、、、
え、コマンドでしか実行できないの?え、デバッグできないの?
普段windowsでVSやVScodeで開発しているマン(linuxあんまり詳しくない)は、使いづら!と思ったのでした。

そんなあなたに!

VScodeがこんな拡張機能を出してました。(確かstableは6月くらい?)

Developing inside a Container

VScode使ってコンテナの中で開発することができるようになるという拡張です。

ということで、これ使ってnode+Expressでwebサーバ作ってみよ~

結果から言うと、ローカルにできるのは、ソースファイルたちがいるフォルダのみで、VScodeの機能を利用しつつ開発できます。

プリリクイジット

準備

  • Dockerfile

を用意します。よく分からなくても、Docker Hubにいろいろ上がってますので、探してみてください。

今回はこちらです。上述した記事で紹介されていたものです。

FROM node:10-buster


ENV DEBIAN_FRONTEND=noninteractive

ARG USER_UID=1000
ARG USER_GID=$USER_UID

# Configure apt and install packages
RUN apt-get update \
    && apt-get -y install --no-install-recommends apt-utils dialog 2>&1 \
    #
    # Verify git and needed tools are installed
    && apt-get install -y git procps \
    #
    # Remove outdated yarn from /opt and install via package
    # so it can be easily updated via apt-get upgrade yarn
    && rm -rf /opt/yarn-* \
    && rm -f /usr/local/bin/yarn \
    && rm -f /usr/local/bin/yarnpkg \
    && apt-get install -y curl apt-transport-https lsb-release \
    && curl -sS https://dl.yarnpkg.com/$(lsb_release -is | tr '[:upper:]' '[:lower:]')/pubkey.gpg | apt-key add - 2>/dev/null \
    && echo "deb https://dl.yarnpkg.com/$(lsb_release -is | tr '[:upper:]' '[:lower:]')/ stable main" | tee /etc/apt/sources.list.d/yarn.list \
    && apt-get update \
    && apt-get -y install --no-install-recommends yarn \
    #
    # Install eslint globally
    && npm install -g eslint \
    #
    # Create a non-root user to use if preferred - see https://aka.ms/vscode-remote/containers/non-root-user.
    && if [ "$USER_GID" != "1000" ]; then groupmod node --gid $USER_GID; fi \
    && if [ "$USER_UID" != "1000" ]; then usermod --uid $USER_UID node; fi \
    # [Optional] Add sudo support for non-root users
    && apt-get install -y sudo \
    && echo node ALL=\(root\) NOPASSWD:ALL > /etc/sudoers.d/node \
    && chmod 0440 /etc/sudoers.d/node \
    #
    # Clean up
    && apt-get autoremove -y \
    && apt-get clean -y \
    && rm -rf /var/lib/apt/lists/*

# Switch back to dialog for any ad-hoc use of apt-get
ENV DEBIAN_FRONTEND=

次に、

  • Remote-Containers

というVScodeの拡張機能をインストールいただきたいです。

image.png

(ついでに"Docker"という拡張機能もご一緒にインストールお願いします。便利なので)

では先ほどクローンしていただいた方は、そのフォルダをオープンして、いざ始めてゆきます。

.devcontainer.json

image.png

こんなフォルダ構成。git関係はお好きにどうぞ。そして、左下に何やら怪しい緑色が!!!!

ここを押すとRemote-Containers拡張で追加されたコマンドを見ることができます。

初めに、今回の拡張機能を用いる上で必要なファイルをプロジェクトに追加してあげます。

VScodeのデバッグ設定とかで追加するlaunch.jsonとかと同じノリで、

コンテナを立ち上げる目的の設定ファイル .devcontainer.json をファイルを追加する必要があります。

左下の緑ボタンをクリック!!

image.png

↑のようにコマンドが出てきます。今回は

Remote-Containers: Add Development Container Configuration Files...

を実行します。続けて、From 'Dockerfile'を選択してください(今回は既存のDockerfileをもとに環境を作るため)

image.png

このように、ファイルが追加されているはずです。

このファイルの細かい設定については、流石にすべてを説明するのは厳しいので、

https://code.visualstudio.com/docs/remote/containers#_devcontainerjson-reference

を参照ください。

今回は、こちらをまるっとコピペしていただければ幸いです。

devcontainer.json
{
    "name": "docker-vscode-demo",

    "context": "..",

    "dockerFile": "..\\Dockerfile",

    "appPort": [3000],

    "runArgs": [
        "-u", "node"
    ],

    "settings": {
        "terminal.integrated.shell.linux": "/bin/bash"
    },

    "postCreateCommand": "yarn install",

    "extensions": [
        "dbaeumer.vscode-eslint"
    ]
}

ここまでで、環境構築は終わりです!!いざ、コンテナを建ててみましょう!!

いざ、!!

緑ボタン→Remote-Containers: Reopen in Containerを実行!

(初回起動時はimageのインストールなどあり結構時間がかかるので注意)
↑めっちゃ時間かかってたのですが、11月のupdateでこんなの来てました。

Remote - Containers: Improved performance when creating a container and new options for devcontainer.json.

image.png

左下の緑ボタンに注目!!

image.png

これが出ていれば成功です、コンテナが起動され、シェル(bash)にアクセスしています。

image.png

nodeはローカルではなく、このコンテナの中にインストールされてます。

PowerShellとかCMDにて、

docker ps

を実行するとコンテナが走っていることを確認できます。

ではでは、今回はExpressを使って二秒でウェブアプリ作りましょう。

Webアプリ作るぞい

以下のコードをシェルで実行!!以上!!(Expressの話なので、今回はコマンドだけです)

  1. sudo npm install express-generator -g
  2. express --view=pug tekitoWeb
  3. cd tekitoWeb
  4. npm install
  5. set DEBUG=tekitoWeb:*
  6. npm start  

そしたら、
http://localhost:3000にアクセス!

image.png

これでコンテナにアプリができました、めでたしめでたし。
(画像は4000番にアクセスしていますが、3000をほかで使っていたため編集しているためです。皆様は3000でどうぞ)

デバッギング

左の虫マークをクリックし、

image.png

歯車をクリックして、Node.jsを選択

すると、.vscode/launch.json ファイルが追加されていると思います。

準備おーけーなので、デバッグしましょう。

/tekitoWeb/routes/index.js

のを編集。してデバッグポイントを置く。

image.png

f5をおしてデバッグ実行!!

image.png

待機している、、、VScodeに戻ると、、、

image.png

ちゃんと止まってらーー!!もっかいf5

image.png

表示できらー!!

終りに

どうでしょうか。

ここおかしくねーか!?とかうごかね!!とか何言ってんだこいつ。。。的なことあったらコメントなりメールなりスラックなりで教えて頂けると非常にありがたいです。

まじVScodeしゅごい。。。もうこれでええやん。

リリースが6月なので、何番煎じや、ていう記事ですが、、、

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

Deploy on Kubernetesをやってみる

Deploy on Kubernetesをやってみる

Deploy on Kubernetes のサイトを参考に環境を用意していきます。
https://docs.docker.com/docker-for-mac/kubernetes/

この内容を進めるには、事前にDocker Desktop for Mac > Getting startedをやっておく必要があります。
上記内容については、Qiitaの記事Get started with Docker Desktop for Macをやってみるでも実践してますので、参考までに。

1. docker-compose.ymlを作成

version: '3.3'

services:
  web:
    image: dockersamples/k8s-wordsmith-web
    ports:
     - "80:80"

  words:
    image: dockersamples/k8s-wordsmith-api
    deploy:
      replicas: 5
      endpoint_mode: dnsrr
      resources:
        limits:
          memory: 50M
        reservations:
          memory: 50M

  db:
    image: dockersamples/k8s-wordsmith-db

2. mystackという名前でスタックをデプロイ

docker stack deployコマンドで作成したdocker-compose.ymlファイルを指定して何やらやるらしい。
docker stack deployコマンドが何をするものなのか、わからなかったので調べてみました。

https://docs.docker.com/engine/reference/commandline/stack_deploy/

Deploy a new stack or update an existing stack
新しいスタックをデプロイまたは既存のスタックを更新する

ここで指定しているコマンドのオプションについても理解しておきたいと思います。

オプション デフォルト 説明
--namespace Kubernetesで利用される名前空間
--compose-file , -c docker-compose.ymlファイルのパスまたは'-'(stdin(標準入力)からの入力)を指定
--orchestrator 利用するオーケストレータを指定する。(kubernetes/swarm/all)
$ docker stack deploy --compose-file docker-compose.yml --orchestrator=kubernetes mystack
Waiting for the stack to be stable and running...
web: Ready      [pod status: 1/1 ready, 0/1 pending, 0/1 failed]
words: Ready        [pod status: 5/5 ready, 0/5 pending, 0/5 failed]
db: Ready       [pod status: 1/1 ready, 0/1 pending, 0/1 failed]

Stack mystack is stable and running

$ docker stack services --orchestrator kubernetes mystack
ID                  NAME                MODE                REPLICAS            IMAGE                             PORTS
7a516f2b-250        mystack_db          replicated          1/1                 dockersamples/k8s-wordsmith-db    
7a5850d1-250        mystack_web         replicated          1/1                 dockersamples/k8s-wordsmith-web   *:80->80/tcp
7a765a71-250        mystack_words       replicated          5/5                 dockersamples/k8s-wordsmith-api   

--namespaceオプションを使ったところ以下のメッセージが出たため、とりあえず使わないことにしました。

namespaces "my-app" not found

3. 起動したので動きを確認する

Deploy on Kubernetesには、deployしたあとのことについて触れられていませんでした。

なので実際に使い方が合っているのかは分かりませんが、とりあえず試した内容を記録します。

  web:
    image: dockersamples/k8s-wordsmith-web
    ports:
     - "80:80"

にあるように、80ポートを使用したWebサーバーがあるので、ここにアクセスしてみます。

http://localhost

以下のような画面が表示されます。

screencapture-localhost-2019-12-23-07_24_10.png

画面には特にボタンやリンクなどが無い。。。。

4. dockersampleをどうやって使ったらいいのか確認してみる

どうやら、画面をリロードする度に、WebAPIを実行して単語(word)を取得し、レゴブロックのような見た目のところにバインドして画面に表示しているようです。

/words/noun/words/adjective,/words/verbというAPIが用意されているみたいです。

<!DOCTYPE html>
<html lang="en" ng-app="lab">
<head>
  <meta charset="utf-8">
  <title>dockercon EU 18</title>
  <link rel="stylesheet" href="style.css">
</head>

<body>
  <div class="logo"><img src="images/logo.svg" style="width:50%"/></div>

<div class="sentence" ng-controller="LabCtrl">
  <div class="line line1 slide-in">
  <span class="result adjective slide-in">
    <span class="word slide-in" ng-bind="adjective1.word"></span>
    <span class="hostname" ng-bind="adjective1.hostname"></span>
  </span>
  <span class="result noun slide-in">
    <span class="word" ng-bind="noun1.word"></span>
    <span class="hostname" ng-bind="noun1.hostname"></span>
  </span>
  </div>
  <div class="line line2 slide-in">
  <span class="result verb slide-in">
    <span class="word" ng-bind="verb.word"></span>
    <span class="hostname" ng-bind="verb.hostname"></span>
  </span>
  </div>
  <div class="line line3 slide-in">
  <span class="result adjective slide-in">
    <span class="word" ng-bind="adjective2.word"></span>
    <span class="hostname" ng-bind="adjective2.hostname"></span>
  </span>
  <span class="result noun slide-in">
    <span class="word" ng-bind="noun2.word"></span>
    <span class="hostname" ng-bind="noun2.hostname"></span>
  </span>
  </div>
</div>

<div class="footer"><img src="images/homes.png" /></div>
</body>

<script src="angular.min.js"></script>
<script src="app.js"></script>
</html>
"use strict";

var lab = angular.module('lab', []);

lab.controller('LabCtrl', function ($scope, $http, $timeout) {
  $scope.noun1 = "";
  $scope.noun2 = "";
  $scope.adjective1 = "";
  $scope.adjective2 = "";
  $scope.verb = "";

  getWord($http, $timeout, '/words/noun', function(resp) {
    $scope.noun1 = word(resp);
  });
  getWord($http, $timeout, '/words/noun', function(resp) {
    $scope.noun2 = word(resp);
  });
  getWord($http, $timeout, '/words/adjective', function(resp) {
    var adj = word(resp);
    adj.word = adj.word.charAt(0).toUpperCase() + adj.word.substr(1)
    $scope.adjective1 = adj;
  });
  getWord($http, $timeout, '/words/adjective', function(resp) {
    $scope.adjective2 = word(resp);
  });
  getWord($http, $timeout, '/words/verb', function(resp) {
    $scope.verb = word(resp);
  });
});

function getWord($http, $timeout, url, callback) {
  $http.get(url).then(callback, function(resp) {
    $timeout(function() {
      console.log("Retry: " + url);
      getWord($http, $timeout, url, callback);
    }, 500);
  });
}

function word(resp) {
  return {
    word: resp.data.word,
    hostname: resp.headers()["source"]
  };
}

5. 使い終わったらコンテナを停止する

$ docker stack rm --orchestrator=kubernetes mystack
Removing stack: mystack

以上でDeploy on Kubernetesのお試しは終了です。

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

GCP+Docker+GPUでrosを動かす (1)

この記事はGPUを搭載したPCを持っていない人で、GPUを使ってrosのGazeboを用いた機械学習をしたいという人向けに書きました。
今回は、GCPを用いてインスタンスを作成し、そのインスタンスのデスクトップ環境にDockerのGUIを出すところまでをやります。

GCPとは

GCPとは元々Google製のPaaSとして知られる Google App Engine を中心に、EC2相当の Google Compute Engine、NoSQLであるCloud Datastore、ビッグデータ解析ツールのBigQueryなど、様々な製品をひとまとめにした環境です。これを用いた仮想PC環境を構築することでGPUを持っていない人でもGPUを用いた作業ができるようになります。

GCPでインスタンスを作成

こちらの記事を参考に作業を進めてください。
今回作成したインスタンスのスペックは以下の通りです。
コア数:8
メモリ数:52GB
GPU:Tesla K80

VNC環境の構築

インスタンスを作成したら、以下のコマンドを実行する。

sudo apt update
sudo  apt upgrade
sudo apt install -y \
     gnome-core \
     gnome-panel \
     vnc4server

全てのインストールが完了したら、vncserverを起動する。(初回起動時にパスワードを登録する)

vncserver

その後、vncserverを一旦停止し、設定を変更する。

vncserver -kill :1
vim .vnc/xstartup

xstartupの編集内容
unset SESSION_MANAGERの#を外します。
[ -x /etc/vnc/xstartup] 以下の全ての行の先頭に#を付けます。
一番下に以下の設定を追加します。
metacity &
gnome-settings-daemon &
gnome-panel &

これでVNCサーバーから起動できるようになる。

Dockerのインストール

以下のコマンドを実行してDockerをインストールする。

sudo apt update
sudo apt install -y \
    apt-transport-https \
    ca-certificates \
    curl \
    software-properties-common
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
sudo apt-key fingerprint 0EBFCD88
sudo add-apt-repository \
   "deb [arch=amd64] https://download.docker.com/linux/ubuntu \
   $(lsb_release -cs) \
   stable"
sudo apt update
sudo apt install -y docker-ce
docker version

これでDockerのインストールが完了する。

NVIDIAのドライバインストール

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

wget https://developer.download.nvidia.com/compute/cuda/repos/ubuntu1804/x86_64/cuda-ubuntu1804.pin
sudo mv cuda-ubuntu1804.pin /etc/apt/preferences.d/cuda-repository-pin-600
sudo apt-key adv --fetch-keys https://developer.download.nvidia.com/compute/cuda/repos/ubuntu1804/x86_64/7fa2af80.pub
sudo add-apt-repository "deb http://developer.download.nvidia.com/compute/cuda/repos/ubuntu1804/x86_64/ /"
sudo apt-get update
sudo apt-get -y install cuda
sudo apt-get -y install cuda-drivers

以下のコマンドで動作確認をしてください。

sudo docker run --runtime=nvidia --rm nvidia/cuda:9.0-base nvidia-smi

実行

VNCserverを起動する。

vncserver

私の場合、VNC Viewer for Google Chromeを用いています。
その後、このgitに従って、以下のコマンドを実行する。

sudo docker pull ikeyasu/reinforcement-learning
sudo docker run --runtime=nvidia -it -p 6080:6080 ikeyasu/reinforcement-learning:latest

VNC Viewer for Google Chromeでhttp://localhost:6080に接続するとブラウザ上にターミナルが表示される。
ここに以下のコマンドを入力するとroboschoolのサンプルプログラムが動く。

python3 $ROBOSCHOOL_PATH/agent_zoo/demo_race2.py

スクリーンショット (32).png

最終的には、上記の図のようにGCPのVMを動かしているVNC上で動いているDockerのguiブラウザにroboschoolが実装されます。
ここまでできたので次回はrosでgazeboをやってみようかと思う。

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