20201018のdockerに関する記事は13件です。

Djangoチュートリアル(ブログアプリ作成)④ - ユニットテスト編

前回、Djangoチュートリアル(ブログアプリ作成)③ - 記事一覧表示編では管理サイトから作成した記事を一覧表示させるために、クラスベース汎用ビューを使いました。

このままアプリ内での記事作成、詳細、編集、削除といった CRUD 処理を追加したいところではありますが、グッとこらえてユニットテストを盛り込みましょう。

Django のテストについて

どんどん機能を追加していくのは楽しいですが、普段はテストを書いているでしょうか?

各種チュートリアルなどでDjangoの簡単なアプリを作れるようになった方でも、
少し自分なりにいじった時にエラーを引き起こしてしまう場合があるかと思います。
また、Djangoをrunserver等で起動した際には特にエラーが出力されなくても
実際に画面をブラウザ経由で動かした時にエラーに気づく場合もあるかと思います。

いくつかの操作を手動でテストするという方法はもちろんありますが、毎回そういったことを行うのは無駄という他ありません。

そこで、Djangoの機能を用いてユニットテストを行うことを推奨します。
DjangoではUnitTestクラスを用いてテストを自動化することができるので、
最初にテスト用のコードだけ書いてしまえば後は何度も同じことをする必要はありません。

テストの考えることは開発コードを考えるのと同じぐらい重要であり、
テストを作ってからアプリ動作のためのコードを書くという開発手法もあるぐらいです。

これを機にテストを行えるようになり、あなたのテスト時間を節約してアプリ本体をより改善することに労力を費やしましょう。

フォルダ構成について

この時点では下記のようなフォルダ構成になっているはずです。

.
├── blog
│   ├── __init__.py
│   ├── admin.py
│   ├── apps.py
│   ├── migrations
│   │   ├── 0001_initial.py
│   │   └── __init__.py
│   ├── models.py
│   ├── tests.py # 注目
│   ├── urls.py
│   └── views.py
├── db.sqlite3
├── manage.py
├── mysite
│   ├── __init__.py
│   ├── settings.py
│   ├── urls.py
│   └── wsgi.py
└── templates
    └── blog
        ├── index.html
        └── post_list.html

お気づきになられた方はいるかもしれませんが、blog ディレクトリ配下に tests.py というファイルが自動的に作成されています。

この tests.py の中に直接テストケースを作成していってもよいのですが、
model のテスト、view のテストとテストごとにファイルが分かれていた方が何かと管理しやすいので
下記のように tests ディレクトリを作成し、中にそれぞれ空ファイルを作成しておきましょう。
tests ディレクトリ内のファイルも実行されるように、中身はからの init.py ファイルも作成しておくのがポイントです。

.
├── blog
│   ├── __init__.py
│   ├── admin.py
│   ├── apps.py
│   ├── migrations
│   │   ├── 0001_initial.py
│   │   └── __init__.py
│   ├── models.py
│   ├── tests # 追加
│   │   ├── __init__.py
│   │   ├── test_models.py
│   │   ├── test_urls.py
│   │   └── test_views.py
......

なお、モジュールの名前は「test」で始めないと Django が認識してくれないので注意してください。

テストの書き方

Django では Python標準のTestCaseクラス(unittest.TestCase)を拡張した、
Django独自のTestCaseクラス(django.test.TestCase)を使います。
このクラスではアサーションというメソッドを使うことができ、返り値が期待する値であるかどうかをチェックする機能があります。

また、前述の通りテストモジュールは「test」という文字列で始まっている必要があるのと、
テストメソッドも「test」という文字列で始める必要があります(詳細は後述します)。

このルールを守ることで Django がテストメソッドをプロジェクト内から探し出し、自動で実行してくれるようになります。

test_models.py

それではまずは model のテストから作成していきましょう。
おさらいですが、blog/models.py に記述されている Post model はこのようになっています。

models.py
...

class Post(models.Model):
    title = models.CharField('タイトル', max_length=200)
    text = models.TextField('本文')
    date = models.DateTimeField('日付', default=timezone.now)

    def __str__(self): # Post モデルが直接呼び出された時に返す値を定義
        return self.title # 記事タイトルを返す

この model に対して、今回は次の3ケースでテストしましょう。

1.初期状態では何も登録されていないこと
2.1つレコードを適当に作成すると、レコードが1つだけカウントされること
3.内容を指定してデータを保存し、すぐに取り出した時に保存した時と同じ値が返されること

ではまずひとつめからです。

test_models.py を開き、必要なモジュールを宣言します。

test_models.py
from django.test import TestCase
from blog.models import Post

そしてテストクラスを作っていくのですが、必ず TestCase を継承したクラスにします。

test_models.py
from django.test import TestCase
from blog.models import Post

class PostModelTests(TestCase):

さて、この PostModelTest クラスの中にテストメソッドを書いていきます。
TestCase を継承したクラスの中で「test」で始めることで、
Django がそれはテストメソッドであることを自動で認識してくれます。
そのため、def の後は必ず test で始まるメソッド名を名付けましょう。

test_models.py
from django.test import TestCase
from blog.models import Post

class PostModelTests(TestCase):

  def test_is_empty(self):
      """初期状態では何も登録されていないことをチェック"""  
      saved_posts = Post.objects.all()
      self.assertEqual(saved_posts.count(), 0)

saved_posts に現時点の Post model を格納し、
assertEqual でカウント数(記事数)が「0」となっていることを確認しています。

さて、これで一つテストを行う準備が整いました。
早速これで一回実行していきましょう。

テストの実行は、manage.py が置いてあるディレクトリ (mysite内) で下記のコマンドを実行します。
実行すると、命名規則に従ったテストメソッドを Django が探し出し、実行してくれます。

(blog) bash-3.2$ python3 manage.py test
Creating test database for alias 'default'...
System check identified no issues (0 silenced).
.....
----------------------------------------------------------------------
Ran 1 tests in 0.009s

OK

一つのテストを実行し、エラーなく完了したことを意味しています。

ちなみに、先ほどは Post 内にデータが空 (=0) であることを確認しましたが、データが1つ存在していることを期待するようにしてみます。

test_models.py(一時的)
from django.test import TestCase
from blog.models import Post

class PostModelTests(TestCase):

  def test_is_empty(self):
      """初期状態だけど1つはデータが存在しているかどうかをチェック (error が期待される)"""  
      saved_posts = Post.objects.all()
      self.assertEqual(saved_posts.count(), 1)

この時の test 実行結果は下記のようになっています。

(blog) bash-3.2$ python3 manage.py test
Creating test database for alias 'default'...
System check identified no issues (0 silenced).
F
======================================================================
FAIL: test_is_empty (blog.tests.test_models.PostModelTests)
初期状態だけど1つはデータが存在しているかどうかをチェック (error が期待される)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/Users/masuyama/workspace/MyPython/MyDjango/blog/mysite/blog/tests/test_models.py", line 9, in test_is_empty
    self.assertEqual(saved_posts.count(), 1)
AssertionError: 0 != 1

----------------------------------------------------------------------
Ran 1 test in 0.002s

FAILED (failures=1)

AssertionError が出ており、期待される結果ではないためにテストは失敗していますね(実験としては成功です)。

Django のテストではデータベースへ一時的なデータの登録も create メソッドから実行できるので、
データを登録しないと確認できないような残りのテストも実行することができます。
下記に model のテストの書き方を載せておくので、参考にしてみてください。

test_models.py(全文)
from django.test import TestCase
from blog.models import Post

class PostModelTests(TestCase):

  def test_is_empty(self):
    """初期状態では何も登録されていないことをチェック"""  
    saved_posts = Post.objects.all()
    self.assertEqual(saved_posts.count(), 0)

  def test_is_count_one(self):
    """1つレコードを適当に作成すると、レコードが1つだけカウントされることをテスト"""
    post = Post(title='test_title', text='test_text')
    post.save()
    saved_posts = Post.objects.all()
    self.assertEqual(saved_posts.count(), 1)

  def test_saving_and_retrieving_post(self):
    """内容を指定してデータを保存し、すぐに取り出した時に保存した時と同じ値が返されることをテスト"""
    post = Post()
    title = 'test_title_to_retrieve'
    text = 'test_text_to_retrieve'
    post.title = title
    post.text = text
    post.save()

    saved_posts = Post.objects.all()
    actual_post = saved_posts[0]

    self.assertEqual(actual_post.title, title)
    self.assertEqual(actual_post.text, text)

test_urls.py

model 以外にも、urls.py に書いたルーティングがうまくいっているのかどうかを確認することもできます。
おさらいすると blog/urls.py はこのようになっていました。

blog/urls.py
from django.urls import path
from . import views

app_name = 'blog'

urlpatterns = [
    path('', views.IndexView.as_view(), name='index'),
    path('post_list', views.PostListView.as_view(), name='post_list'),
]

上記のルーティングでは /blog/ 以下に入力されるアドレスに従ったルーティングを設定しているので、
/blog/ 以下が ''(空欄) と 'post_list' であった時のテストをします。
それぞれのページへ view 経由でリダイレクトされた結果が期待されるものであるかどうかを、assertEqual を用いて比較してチェックします。

test_urls.py
from django.test import TestCase
from django.urls import reverse, resolve
from ..views import IndexView, PostListView

class TestUrls(TestCase):

  """index ページへのURLでアクセスする時のリダイレクトをテスト"""
  def test_post_index_url(self):
    view = resolve('/blog/')
    self.assertEqual(view.func.view_class, IndexView)

  """Post 一覧ページへのリダイレクトをテスト"""
  def test_post_list_url(self):
    view = resolve('/blog/post_list')
    self.assertEqual(view.func.view_class, PostListView)

ここまでで一旦テストを実行しておきましょう。
※先ほど、データベースが空である状態のテストをしたときと比べると
 データを登録するテストケースが増えているため
 テスト用のデータベース作成、消去の処理がメッセージに出力されていることが分かります

(blog) bash-3.2$ python3 manage.py test
Creating test database for alias 'default'...
System check identified no issues (0 silenced).
.....
----------------------------------------------------------------------
Ran 5 tests in 0.007s

OK
Destroying test database for alias 'default'...

test_views.py

最後に view のテストも行いましょう。

views.py はこのようになっていました。

views.py
from django.views import generic
from .models import Post  # Postモデルをimport

class IndexView(generic.TemplateView):
    template_name = 'blog/index.html'

class PostListView(generic.ListView): # generic の ListViewクラスを継承
    model = Post # 一覧表示させたいモデルを呼び出し

IndexView のテストでは、GET メソッドでアクセスした時にステータスコード 200(=成功) が返されることを確認します。

test_views.py
from django.test import TestCase
from django.urls import reverse

from ..models import Post

class IndexTests(TestCase):
  """IndexViewのテストクラス"""

  def test_get(self):
    """GET メソッドでアクセスしてステータスコード200を返されることを確認"""
    response = self.client.get(reverse('blog:index'))
    self.assertEqual(response.status_code, 200)

何か view でメソッドを追加したときは、
どんなにテストを書く時間がなくてもこれだけは最低限テストケースとして作成する癖をつけましょう。

ListView の方もテストをしていきます。

同じく 200 のステータスコードが返ってくることの確認はもちろん、
ここではデータ(記事)を2つ追加した後に記事一覧を表示させ、
登録した記事のタイトルがそれぞれが一覧に含まれていることを確認するテストを作成します。

なお、ここで少し特殊なメソッドを使います。
テストメソッドは「test」で始めるように前述しましたがsetUptearDownというメソッドが存在します。

setUpメソッドではテストケース内で使うデータの登録をし、
tearDownメソッドでは setUp メソッド内で登録したデータの削除を行えます。
(どちらも、どんなデータを登録するかは明示的に記述する必要があることには注意しましょう)

同じテストケースの中で何回もデータの登録をするような処理を書くのは手間&テストに時間がかかる要因になるので、
共通する処理は一箇所にまとめてしまおうというものです。

これらのメソッドを使い、test_views.py を作成するとこのようになります。

test_views.py
from django.test import TestCase
from django.urls import reverse

from ..models import Post

class IndexTests(TestCase):
  """IndexViewのテストクラス"""

  def test_get(self):
    """GET メソッドでアクセスしてステータスコード200を返されることを確認"""
    response = self.client.get(reverse('blog:index'))
    self.assertEqual(response.status_code, 200)

class PostListTests(TestCase):

  def setUp(self):
    """
    テスト環境の準備用メソッド。名前は必ず「setUp」とすること。
    同じテストクラス内で共通で使いたいデータがある場合にここで作成する。
    """
    post1 = Post.objects.create(title='title1', text='text1')
    post2 = Post.objects.create(title='title2', text='text2')

  def test_get(self):
    """GET メソッドでアクセスしてステータスコード200を返されることを確認"""
    response = self.client.get(reverse('blog:post_list'))
    self.assertEqual(response.status_code, 200)

  def test_get_2posts_by_list(self):
    """GET でアクセス時に、setUp メソッドで追加した 2件追加が返されることを確認"""
    response = self.client.get(reverse('blog:post_list'))
    self.assertEqual(response.status_code, 200)
    self.assertQuerysetEqual(
      # Postモデルでは __str__ の結果としてタイトルを返す設定なので、返されるタイトルが投稿通りになっているかを確認
      response.context['post_list'],
      ['<Post: title1>', '<Post: title2>'],
      ordered = False # 順序は無視するよう指定
    )
    self.assertContains(response, 'title1') # html 内に post1 の title が含まれていることを確認
    self.assertContains(response, 'title2') # html 内に post2 の title が含まれていることを確認

  def tearDown(self):
      """
      setUp で追加したデータを消す、掃除用メソッド。
      create とはなっているがメソッド名を「tearDown」とすることで setUp と逆の処理を行ってくれる=消してくれる。
      """
      post1 = Post.objects.create(title='title1', text='text1')
      post2 = Post.objects.create(title='title2', text='text2')

この状態でテストを実行すると model, url, view で合計 8 つのテストが実行されます。

(blog) bash-3.2$ python3 manage.py test
Creating test database for alias 'default'...
System check identified no issues (0 silenced).
........
----------------------------------------------------------------------
Ran 8 tests in 0.183s

OK
Destroying test database for alias 'default'...

これで、これまで書いたコードについてユニットテストを作成することができました。
他にも期待される template が呼び出されているかどうか等、
Django 独自のテスト方法を用いたテストで冗長的にチェックする方法もありますが
コードを書く前にテストを作成する癖をつけ、後々のチェックの手間を省くようにしていきましょう。

次回はアプリ内で記事を作成できるようにします。
今回覚えたユニットテストを先に書く TDD (テスト駆動開発) スタイルでいきましょう。

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

Penronseの環境構築 [Windows]

目的

数式からいい感じに図をつくる描画用ソフトPenronseを使いたくなった。
下記の記事を参考に、Windows10でPenronseを使用できる環境を構築してみる。
このソフト名は、ノーベル賞を受賞したペンローズさんを指してるのでしょうか?

記事:数式からいい感じに図表が生成できてしまう! Penroseを試す (SIGGRAPH 2020)

1. 環境構築

この節では、Windows10向けのPenroseの環境構築について説明する。

1.1 Docker

この節では、Dockerの環境構築について説明する。
WSLがWSL1と古かったのでWSL2に更新する[1]。Windows10で以前インストールしたUbuntu-18.04の設定をWSL1からWSL2に更新する。

$ wsl set-version Ubuntu-18.04 2

WSLの設定更新が完了後、Dockerの環境構築を行う。まず、Docker Hubをインストールする。BIOSの設定を変更しないとDocker Hubでエラーが発生してしまうので設定変更する。BIOSで「Intel VT-x」と「Intel Virtualization Technology」を有効にする[2]
以上で、Dockerの環境構築は完了である。

1.2 Penrose

この節では、Penroseの環境構築について説明する。
WSL2と設定したUbuntu-18.04のターミナルでPenroseの環境を構築していく。Penroseの環境を構築しようとするとnpmのインストールで失敗してしまう。素直にapt-getでnpmをインストールすると失敗するので、apptitudeを用いてnpmをインストールする[3]

$ sudo apt-get install aptitude
$ sudo aptitude install npm
$ which npm

Penroseのコンテナをダウンロードしセットアップする。npmで警告文がでるが無視する(たすけて...)。
ここまでくれば元記事と同じコマンドで実行できる環境が整った。

$ docker pull vanessa/penrose
$ cd penrose
$ make recreate-dev
$ make dev-build

2. 動作方法

この節では、Penroseの動作方法について説明する。
WSL2と設定したUbuntu-18.04のターミナルでPenroseのコンテナを実行する。

$ make start-dev

下のコマンドを実行することで結果を可視化できる。

$ make penrose set-theory-domain/tree.sub set-theory-domain/venn.sty set-theory-domain/setTheory.dsl
# ブラウザから http://localhost:3500 にアクセスする

a.png

3. まとめ

PenronseをWindows10で動作することができた。Penronseの使い方をサンプルコードをもとにまとめる予定である。

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

Docker でRails6とPostgreSQLの環境構築

Dockerで Rails6 + PostgreSQLで環境構築

自分の備忘録も兼ねてDockerでRails6 + PostgreSQLで環境構築のやり方を記事にして残しておきます。
今回はアプリ名をshopping_appとして作成していきます。

作成する環境の各バージョン

  • Ruby 2.7.2
  • Rails 6.0.3
  • PostgreSQL 13.0

ディレクトリ構造

 .
 ├── Dockerfile
 ├── docker-compose.yml
 └── shopping_app
     ├── Gemfile
     └── Gemfile.lock

Dockerfileの作成

まずはDockerfileの作成をします。

FROM ruby:2.7.2
ENV LANG C.UTF-8


RUN 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

RUN apt-get update -qq && \
    apt-get install -y build-essential \
            libpq-dev \
            nodejs \
            postgresql-client yarn

RUN mkdir /app
RUN mkdir /app/shopping_app

ENV APP_ROOT /app/shopping_app
WORKDIR $APP_ROOT

ADD ./shopping_app/Gemfile $APP_ROOT/Gemfile
ADD ./shopping_app/Gemfile.lock $APP_ROOT/Gemfile.lock

RUN bundle install

ADD . $APP_ROOT

docker-compose.ymlの作成

docker-compose.ymlを作成します。今回はportを1501に設定しています。

version: '3'
services:
  postgres:
    image: postgres
    ports:
      - "3306:3306"
    volumes:
      - ./tmp/db:/var/lib/postgresql/data #MacOSの場合
    environment:
      POSTGRES_USER: 'admin'
      POSTGRES_PASSWORD: 'admin-pass'
    restart: always
  app:
    build: .
    image: rails
    container_name: 'app'
    command: bundle exec rails s -p 1501 -b '0.0.0.0'
    ports:
      - "1501:1501"
    environment:
      VIRTUAL_PORT: 80
    volumes:
      - ./shopping_app:/app/shopping_app
    depends_on:
      - postgres
    restart: always

volumes:
  app_postgre:
    external: true

Gemfileの作成

source 'https://rubygems.org'
gem 'rails', '6.0.3'

Gemfile.lockは空のままでいいです。

コンテナをBuildしてappを作成

$ docker-compose run app rails new . --force --database=postgresql --skip-bundle

webpackerのinstall

docker-compose run app rails webpacker:install

Databaseの作成と設定

作成されたappのdatabase.ymlを設定します。

default: &default
  adapter: postgresql
  encoding: unicode
  # For details on connection pooling, see Rails configuration guide
  # https://guides.rubyonrails.org/configuring.html#database-pooling
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  # 以下を記述
  user: admin
  password: admin-pass
  host: postgres

imageをbuild

docker-compose build

Databaseの作成

docker-compose run app rails db:create

以上で構築完了です。
以下のコマンドでアプリを起動して

docker-compose up

http://localhost:1501/
にアクセスすれば以下の画面に表示されます。

スクリーンショット 2020-10-18 20.16.20.png

以上です。

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

ローカル環境のSwagger SpecをSwagger UIとSwagger Editorに反映させる手順

この記事は個人ブログの転載です。

Swaggerを利用することでREST APIの仕様を文章化できます。
今回はREST APIの仕様書であるSwagger Specの内容をSwagger UI、Swagger Editorに反映させる方法について紹介します。

下準備: Swagger Specをローカル環境に用意する

Swagger SpecとはREST APIの仕様を文章化したものです。
今回は~/swagger/sample.yamlというファイル名で、以下のようなSwagger Specを用意しました。

swagger: "2.0"
info:
  description: "This is a sample API."
  version: "1.0.0"
  title: "Sample API"
host: "localhost:3000"
basePath: "/api/v1"
tags:
- name: "todo"
  description: "Everything about todos"
schemes:
- "http"
paths:
  /todos/{todoId}:
    get:
      tags:
      - "todo"
      summary: "Find todo by ID"
      description: "Returns a single id"
      operationId: "getTodoById"
      produces:
      - "application/json"
      parameters:
      - name: "todoId"
        in: "path"
        description: "ID of todo to return"
        required: true
        type: "integer"
        format: "int64"
      responses:
        200:
          description: "successful operation"
        404:
          description: "Todo not found"

Swagger Specの情報をSwagger UIに反映させる方法

Swagger UIとはSwagger Specの情報を反映させた静的ページを生成するツールのことを言います。
Swagger UIはSwagger Specの情報を可視化するだけでなく、画面上からREST APIを実行する機能も提供しています。

今回はDockerを利用してSwagger UIの環境を構築します。

swagger-uiのインストール手順1を参考に、以下のコマンドを実行します。

$ cd ~/swagger

$ docker run -p 80:8080 -e SWAGGER_JSON=/sample.yaml -v ~/swagger/sample.yaml:/sample.yaml swaggerapi/swagger-ui

docker runコマンドについて補足説明をします。

-vオプションを利用して、ローカル環境(ホスト)の~/swagger/sample.yamlをコンテナの/sample.yamlにコピーします。
SWAGGER_JSONという環境変数にはSwagger UIに反映させるSwagger Specのファイル名を指定します。
-vオプションによってローカル環境のSwagger Specがコンテナの/sample.yamlに配置されているため、コンテナのSwagger UIにローカルのSwagger Specの内容が反映されます。
-pオプションでホストの80番ポートとコンテナの8080番ポートを紐づけているため、http://localhostにアクセスすると以下のような画面が表示されます。

image.png

Swagger Specの情報をSwagger Editorに反映させる方法

Swagger EditorとはSwagger Specを編集するエディタのことを言います。
Swagger Editorを利用することでSwagger Specを修正しながらSwagger UIの変化をリアルタイムで見られます。

Dockerを利用する方法

swagger-editorのインストール手順2を参考に、以下のコマンドを実行します。

$ cd ~/swagger

$ docker run -p 81:8080 -e SWAGGER_FILE=/sample.yaml -v ~/swagger/sample.yaml:/sample.yaml swaggerapi/swagger-editor

今回の例ではホストの81番ポートで紐付けを行っているため、http://localhost:81にアクセスすると以下のような画面が表示されます。

image.png

Visual Studio Codeのプラグインを利用する方法

Dockerを利用してSwagger Editorを起動する場合、デフォルトのままではSwagger Editor上で修正したSwagger Specの内容がローカルに同期されません。

Visual Studio Codeを利用している場合はSwagger Viewerというプラグインを利用する方法がオススメです。

Swagger Viewerを利用すると、以下のような感じでSwagger UIを見ながらSwagger SpecをVisual Studio Code上で修正できます。

image.png

さいごに

以上でSwagger UIとSwagger Editorの環境構築手順の紹介を終わります。

Twitter(@nishina555)やってます。フォローしてもらえるとうれしいです!

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

Dockerfileの作り方(基礎)

はじめに

Dockerは多くのIT開発企業で導入されている環境構築を楽にする技術です。
DockerfileはDockerImageの設計図のようなものです。
Dockerfileを自身で作成することで構築したい環境のDocker imageを作成できます。

Dockerfile、DockerImage、コンテナの関係

Dockerfile => DockerImage => コンテナ の順に作成します。

DockerImageの作成方法
Dockerfileが存在するディレクトリで docker build .

コンテナの作成方法
docker run (DockerImage名 or ID)

Dockerfileのインストラクション

  • FROM
  • RUN
  • CMD
  • ENTRYPOINT
  • COPY
  • ADD
  • ENV
  • WORKDIR

Dockerfile作成手順

FROM

ベースとなるイメージを決定するインストラクションです。
Dockerfileの最初に記述します。
FROMの後には基本的にOSを記述します。

FROM OS(Docker imageから選ぶ)

例)FROM ubuntu:latest
例)FROM ruby:2.5

RUN

FROMで記述したOSに対応するコマンドを実行するインストラクションです。
RUN毎にLayerが作成されます。(DockerImageは層を積み重ねていくように作成されます。)

RUN OSに対応するコマンド

例)FROM ubuntu:latest
   RUN touch test
=> DockerImageにはubuntuのLayerにtouch testのLayerが追加で作成されます。
(testというファイルを作成するDockerImageを作成しています。)

例)FROM ubuntu:latest
   RUN apt-get update && apt-get install -y \
       curl\
       nginx
=> DockerImageにはubuntuのLayerにcurlとnginxという2つのパッケージをインストールするLayerが追加作成されます。
(apt-get updateはインストールするパッケージを新しいものにするために記述し、apt-get installはパッケージをインストールするために記述します。)
(-yはインストール中にyesかnoかの質問をされても、全てyesとして進むために使用し、バックスラッシュはパッケージを見やすくするための改行のために使用しています。)

DockerfileをbuildしてDockerImageを一度作成したものはキャッシュされるため、追加で下に下にとRUNを記述していくことでbuildの時間を少なくすることができます。
※ただし、Layerは少ない方が望ましいため、最終的には、例のように&&や、\を用いた記述のように、RUNの記述をまとめて、RUNを少なくするようにします。

CMD

コンテナの実行コマンドを指定するインストラクション。
Dockerfileの最後に記述します。

CMD OSに対応するコマンド

例)FROM ubuntu:latest
   RUN apt-get update && apt-get install -y \
       curl\
       nginx
   CMD ["/bin/bash"]
=> Dockerfileをbuildし、DockerImageを作成した後、docker runによりコンテナ作成・実行した際に、/bin/bashが実行されるようになります。

ENTRYPOINT

CMDに近い役割を持つインストラクションです。

CMDはDockerImageを実行(docker run)する際に、コマンドを上書きできるものです。(例:docker run lsとすると上記CMD説明時の例ではbashではなく、lsが実行される。)

ENTRYPOINTを使うと、コマンドの上書きできなくなり、CMDはENTRYPOINTのオプションを記述する形となります。

ENTRYPOINT OSに対応するコマンド

例)FROM ubuntu:latest
   RUN apt-get update && apt-get install -y \
       curl\
       nginx
   ENTRYPOINT ["ls"]
   CMD ["--help"]
=> ENTRYPOINTにlsコマンドを記述することでdocker runの際にlsコマンド以外のコマンドが実行できなくなり、CMDはオプションの--helpが指定されています。

COPY

Dockerfileの入っているディレクトリ(BuildContextと言います)の中にあるファイルをDockerImageにコピーするインストラクションです。

Copy BuildContext内のファイル名

例)FROM ubuntu:latest
   RUN mkdir /new_dir
   COPY test /new_dir
   CMD ["/bin/bash"]
=> Dockerfileをbuildした際、DockerImageに/new_dirというディレクトリを作成し、/new_dirにtestというファイルがコピーさせるLayerがutubtuのLayerに追加作成されます。
   このDockerImageからコンテナを作成した際には、コンテナ内に/new_dirというディレクトリとtestというファイルが作成されています。

ADD

COPYのコマンドと似ていて、同じくコピーができるインストラクションです。

ADDとCOPYとの違い
ADDは圧縮ファイルをコピーした際、コピーと解凍の両方を行います。
COPYは単純なコピーのみです。
圧縮ファイルをコピー・解凍する時にはADD、圧縮ファイルが関係ない単純なコピーにはCOPYを使います。

Copy BuildContext内のファイル名

例)FROM ubuntu:latest
   ADD sample.tar /
   CMD ["/bin/bash"]
=> Dockerfileをbuildした際、DockerImageにsample.tarを/(ルート直下)にコピー・解凍するLayerを作成されます。
   コンテナを作成した際に、コンテナのルート直下にsample.tarが解凍されたものが作成されています。

ENV

環境変数を設定するインストラクションです。

ENV 環境変数

例)FROM ubuntu:latest
   ENV key1 value
   CMD ["/bin/bash"]
=> コンテナ作成時にkey1=valueという環境変数が設定されるDockerImageが作成されます。

WORKDIR

インストラクションの実行ディレクトリを変更するインストラクションです。

WORKIDIR ディレクトリ

例)FROM ubuntu:latest
   RUN mkdir sample_folder
   WORKDIR /sample_folder
   RUN touch sample_file
=> sample_folderというディレクトリを作り、sample_folder内でsample_fileを作成するLayerとなります。
  (WORKDIRを使ったことで、コンテナ内に作成した/sample_folderの中でRUNの実行ができます。)

参考

Udemy

かめれおん講師 「米国AI開発者がゼロから教えるDocker講座」

https://www.udemy.com/share/103aTRAEAdd1pTTHoC/

有料ですが、初学者の私にも非常に理解しやすかったです。

最後に

本投稿が初学者の復習の一助となればと幸いです。

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

[Microsoft] Azure Pipelines (VSTS) AgentをDockerあるいはAzure Container Instanceで動かす

Azure PipelinesエージェントをDockerで動かします。

Azure Pipelinesとは

Jenkinsのようなビルド/デプロイを行うサービスです。

Azure Pipelinesエージェントとは

Azure Pipelinesから指示を受け、実際にビルドやデプロイの処理が動くデーモンです。

Azure Pipelinesに無料版が付いてきますが、毎月の実行時間に上限があります。
自前でエージェントを用意することで、上限時間なく利用できます。

あらかじめ必要なもの

組織名

Azure DevOpsのURLを見てください。https://dev.azure.com/hogehoge のようになっていると思います。この hogehoge の部分です。

Personal Access Token

エージェントがAzure DevOpsへ接続するときに必要なものです。

こちらにPersonal Access Tokenの取得方法があります。

スコープとしては、以下を設定します。

  • Agent Pools
    • Read & manage
  • Code
    • Read
  • Connected server
    • Connected server
  • Deployment Groupos
    • Read & manage

Dockerで動かす

Micorosoft公式イメージはDeprecatedになっているので、拙いながらも作成しました。
sengokyu/azure-pipelines-agent:latest
いつものようにpullしてください。

docker pull sengokyu/azure-pipelines-agent

実行時に、あらかじめ用意しておいた組織名とPersonal Access Tokenを環境変数として与えてください。

変数名
ORG 組織名
TOKEN Personal Access Token
docker run -d -e TOKEN=${PAT} -e ORG=${ORG} sengokyu/azure-pipelines-agent

Azure Container Instanceで動かす

ARMテンプレートを使用してデプロイします。

ARMテンプレートのリソース定義部分です。全体はGitHubに置いてます。

template.json
  "resources": [
    {
      "name": "[parameters('containerGroupName')]",
      "type": "Microsoft.ContainerInstance/containerGroups",
      "apiVersion": "2019-12-01",
      "location": "[resourceGroup().location]",
      "properties": {
        "containers": [
          {
            "name": "[parameters('agent')]",
            "properties": {
              "image": "[variables('containerImage')]",
              "resources": {
                "requests": {
                  "cpu": "[parameters('cpu')]",
                  "memoryInGb": "[parameters('memoryInGb')]"
                }
              },
              "environmentVariables": [
                { "name": "TOKEN", "secureValue": "[parameters('token')]" },
                { "name": "ORG", "value": "[parameters('org')]" },
                { "name": "POOL", "value": "[parameters('pool')]" },
                { "name": "AGENT", "value": "[parameters('agent')]" }
              ]
            }
          }
        ],
        "osType": "Linux",
        "restartPolicy": "Never"
      }
    }
  ]

各パラメータを記載したファイルを作成します。

parameters.json
{
  "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#",
  "contentVersion": "1.0.0.0",
  "parameters": {
    "containerGroupName": { "value": "vsts-agent" },
    "pool": { "value": "Default" },
    "agent": { "value": "vsts-agent-on-aci" },
    "token": { "value": "YourPersonalAccessToken" },
    "org": { "value": "YourOrganizationName" },
    "cpu": { "value": 4 },
    "memoryInGb": { "value": 8 }
  }
}

azコマンドを実行します。

az deployment group create -f template.json -p @parameters.json -g ResourceGroupName
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ODBC ドライバーに依存した ASP.NET Framework アプリを Azure App Service に移行する

はじめに

(本記事は2020年10月の情報をもとに作成されています)
オンプレにある既存の社内システム等をクラウド移行する際に検討することの1つとして、IaaS に移行するか PaaS に移行するかの選択があると思います。IaaS と PaaS ともにメリットや制約がある中で PaaS を採用し、ビジネスを進める判断をすることも少なくないと思います。しかしながら、アプリが OS の機能等に深く依存している場合など、PaaS にアプリを移行できず、IaaS 上の仮想マシン等に移行せざるを得ないことも考えられます。そこで解決策の一つとして、アプリをコンテナ化して、OS 依存の部分をコンテナに入れ込み、PaaS でコンテナを運用することを考えてみます。
Azure の App Service では、Windows コンテナのサポートは長らく Preview として提供されましたが、Ignite 2020 の発表で、App Service の Windows コンテナサポートが一般提供(GA)となったので、今回はこちらを利用して、オンプレで稼働している社内システムの Azure PaaS への移行例を考えてみたいと思います。

今回は以下の画像のように、オンプレ環境で稼働する IIS サーバ上の ODBC に依存した ASP.NET Framework アプリを、アプリの変更を最小限に Azure の PaaS である App Service(Webapp)に移行する例を取り上げます。
image.png

既存の ASP.NET Framework アプリをコンテナ化する

現在持っている ASP.NET Framework アプリを Visual Studio で開発している場合、簡単な手順を踏むだけでアプリを Windows コンテナ化することができます。Visual Studio 2019・Visual Studio 2017(バージョン15.7以降)をお使いの場合は、Visual Studio Installer で必要なコンポーネントを入れ、 Docker Desktop を用意するだけで Docker サポートを利用可能です。(詳細は https://docs.microsoft.com/ja-jp/visualstudio/containers/overview?view=vs-2019 )余談ですが、ASP.NET Framework アプリは Linux コンテナには対応しておらず、Windows コンテナのみ利用できます。
実際に Docker サポートを利用する際は、以下の画像のように、コンテナ化したい ASP.NET Framework プロジェクトを開き、ソリューションエクスプローラーから Docker サポートを追加する操作をします。
image.png

Docker サポートを追加すると、Docker for Windows の起動チェックが入り、起動していない場合や Linux コンテナが起動している場合は Windows コンテナを起動させます。その後、プロジェクト内に以下のような Dockerfile が自動生成され、Docker コンテナのイメージ作成の準備は完了します。

Dockerfile
FROM mcr.microsoft.com/dotnet/framework/aspnet:4.8-windowsservercore-ltsc2019
ARG source
WORKDIR /inetpub/wwwroot
COPY ${source:-obj/Docker/publish} .

この時点で、ASP.NET Framework アプリの Windows コンテナ化が完成しているのですが、この状態ではただアプリが Windows で動いているだけなので ODBC に依存したアプリはデータベースへ接続できません。次に ODBC ドライバーをコンテナ内にインストールする処理を Dockerfile 内に追加します。手元の Windows 等では以下のような Powershell の Add-OdbcDsn 等を用いて、ODBC のインストールをすると思いますが、Dockerfile 上でその処理をそのまま書くとうまく動かずハマるので注意が必要です。

Powershell
Add-OdbcDsn -Name "DSNNAME" -DriverName "PostgreSQL Unicode" -DsnType "User" -Platform "32-bit" -SetPropertyValue @("Server=SERVERNAME.com","Database=DBNAME","SSLMode=require","Username=SOMEONE")

以下が、実際の Dockerfile での記述例です。RUN コマンドで Powershell を指定し -command の後ろに通常の Powershell コマンドを実行する方法ではなく、SHELL ["powershell", "-command"] を利用して RUN コマンドから直接 Powershell コマンドを書くようにするとエラー無くイメージ作成が可能です。

Dockerfile
FROM mcr.microsoft.com/dotnet/framework/aspnet:4.8-windowsservercore-ltsc2019
ARG source
WORKDIR /inetpub/wwwroot
COPY ${source:-obj/Docker/publish} .

SHELL ["powershell", "-command"]

#PostgreSQL 対応 ODBC ドライバーのダウンロード
ADD https://ftp.postgresql.org/pub/odbc/versions/msi/psqlodbc_12_01_0000-x86.zip /
ADD https://ftp.postgresql.org/pub/odbc/versions/msi/psqlodbc_12_01_0000-x64.zip /

#zip ファイルの展開とドライバーのインストール
RUN Expand-Archive -Path c:\psqlodbc_12_01_0000-x86.zip -DestinationPath c:\odbc\ ; \
    Start-Process c:\odbc\psqlodbc_x86.msi -Wait
RUN Expand-Archive -Path c:\psqlodbc_12_01_0000-x64.zip -DestinationPath c:\odbc64\ ; \
    Start-Process c:\odbc64\psqlodbc_x64.msi -Wait

#RUN powershell -command を利用するとパラメータ指定の部分でエラーとなる
#RUN powershell -command Add-OdbcDsn -Name "DSNNAME" -DriverName "PostgreSQL Unicode" -DsnType "User" -Platform "32-bit" -SetPropertyValue @("Server=SERVERNAME.com","Database=DBNAME","SSLMode=require","Username=SOMEONE")

#32bit
RUN Add-OdbcDsn -Name 'DSNNAME' -DriverName 'PostgreSQL Unicode' -DsnType 'System' -Platform '32-bit' -SetPropertyValue @('Server=SERVERNAME.com','Database=DBNAME','SSLMode=require','Username=SOMEONE')
#64bit
RUN Add-OdbcDsn -Name 'DSNNAMEx64' -DriverName 'PostgreSQL Unicode(x64)' -DsnType 'System' -Platform '64-bit' -SetPropertyValue @('Server=SERVERNAME.com','Database=DBNAME','SSLMode=require','Username=SOMEONE')

あとは、ビルドを実施しローカルでコンテナアプリが実行されることが確認できると思います。

コンテナ化したアプリをコンテナレジストリにアップロードし App Service で公開

ODBC ドライバーに依存した ASP.NET Framework アプリをコンテナ上で実行することができましたので、アプリを PaaS である App Service にデプロイする準備をします。今回は、コンテナのイメージを Azure Container Registry にプッシュし、そこから App Service へ自動デプロイがされるように構成します。(もちろん、イメージのプッシュ先を Docker Hub にしても同様のことが実現できます)
コマンドラインで Docker コマンドから Azure Container Registry へイメージをプッシュしても良いのですが、今回は Visual Studio を利用して Azure Container Registry へコンテナイメージをプッシュします。

ソリューションエクスプローラーで、コンテナ化するアプリのプロジェクトを右クリックし、メニューから「発行…」を選択してください。

image.png

次に発行先として Docker Container Registry をクリックしてください。
image.png

Azure コンテナレジストリーを選択し、イメージをプッシュしたい Azure Container Registry をお持ちのサブスクリプションの中から選択してください。ここでは、Microsoft アカウントのログイン等が求められる場合があります。また、お持ちの Azure サブスクリプションに Azure Container Registry が無い場合は、Azure ポータル上や Visual Studio 上で作成してください。(Azure Container Registry の作成方法はこちら: https://docs.microsoft.com/ja-jp/azure/container-registry/container-registry-get-started-portal )
image.png

Azure Container Registry へイメージをプッシュする準備ができましたら、「発行」ボタンを押し、イメージのプッシュを行います。
image.png

次に、実際にアプリを動かす App Service を作成します。Ignite 2020 の発表で Windows コンテナサポートが Premium V3 SKU でサポートされたとのことですので、今回はそちらを利用したいと思います。(App Service
関連のアップデートの詳細についてはこちらをご参照ください)
ポータル上で App Service の作成を選択し、OS が Windows の Docker コンテナーを作成する設定を選択してください。Preview 期間中は PC2~PC4 と呼ばれる Windows コンテナ用の App Service Plan SKU があったのですが、今回のアップデートで他と同じ Premium V3 SKU を選択するようになっています。(逆に Windows コンテナは Premium V3 インスタンスでしか現在作成できません。検証などは11月から設定される Dev/Test 価格を利用すると価格を抑えつつ検証が可能かと思われます。)

image.png

次の Docker タブでは、App Service で動かすコンテナのイメージソースを指定します。今回は先ほど作成した Azure Container Registry を指定してください。あとは既定の設定で App Service をデプロイしてください。

image.png

App Service のデプロイ後、ポータルの「コンテナーの設定」から、App Service でホストするコンテナのイメージを指定できます。また、「継続的なデプロイ」をオンにすると、イメージが更新されるたびに App Service で動くコンテナのイメージも自動的に更新できるように設定できます。
image.png

これで、ODBC ドライバーに依存した ASP.NET Framework アプリを App Service 上で動かすことができました。

おわりに

今回は、App Service Premium V3 SKU のインスタンスを利用し、ODBC ドライバーに依存した ASP.NET Framework アプリを Windows コンテナとして動かしました。既存のシステムをクラウドへ移行する手段として柔軟性や経済性に優れている PaaS を利用し、コンテナで動かすといったことが選択肢に考えられるようになったのは良いニュースだと思いました。
また App Service Premium V3 SKU では、リージョン VNET 統合や、Private Link といったアプリを閉域環境で動かすオプションも利用できますので、そちらも検討いただければと思います。

(本記事は2020年10月の情報をもとに作成されています)

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

【忘備録】EC2内にDockerでPython3環境を構築する

概要

内容

  • AWSのEC2にDockerを用いてPython環境を構築する
    • 今回はseleniumでのブラウザ自動操作のパッケージを含めてコンテナ化する

環境

  • OS:AWS Linux2
  • サービス:AWS(EC2)
  • タイプ:t2.micro

前提

  • 自分のアカウントから上記環境のEC2が立てられている状態

参考資料

この辺から一部抜き出して履行。

必要なファイル

  • Dockerfile
  • docker-compose.yml

工程

  • EC2インスタンスへ接続
  • インスタンスでインストールされているパッケージとパッケージキャッシュを更新
$ sudo yum update -y
  • 最新の Docker Community Edition パッケージをインストール
$ sudo amazon-linux-extras install docker
  • 次はDocker Composeをcurlでインストール。
$ sudo curl -L https://github.com/docker/compose/releases/download/1.21.0/docker-compose-$(uname -s)-$(uname -m) -o /usr/local/bin/docker-compose
$ sudo chmod +x /usr/local/bin/docker-compose
$ docker-compose --version

バージョンが表示されていたら成功

  • Dockerサービスを開始
$ sudo service docker start
  • ec2-user をdockerグループに追加し、今後コマンドで入力するsudoを省略する
$ sudo usermod -a -G docker ec2-user
  • 反映させるために一旦ログアウトし、再びEC2にログインする
  • ec2-user が sudo を使用せずに Docker コマンドを実行できることを確認
$ docker info

2.Dockerhubに登録

  • DockerHubにアカウント登録

  • リポジトリを作成

  • EC2でDockerHubにログインする

$ docker login
Login with your Docker ID to push and pull images from Docker Hub. If you don't have a Docker ID, head over to https://hub.docker.com to create one.
Username: アカウント名
Password: パスワード
Login Succeeded

3.Dockerイメージを作成するためのファイルを作成

  • 必要なファイル

    • Dockerfile
    • docker-compose.yml
  • Dockerfileを作成。

$ touch Dockerfile
  • vimで中身を編集
$ vim Dockerfile
  • 今回はpython3とブラウザの自動操作をするためのパッケージが入ったイメージコンテナを作成する。
Dockerfile
FROM python:3
USER root

RUN apt-get update
RUN apt-get -y install locales && \
localedef -f UTF-8 -i ja_JP ja_JP.UTF-8
ENV LANG ja_JP.UTF-8
ENV LANGUAGE ja_JP:ja
ENV LC_ALL ja_JP.UTF-8
ENV TZ JST-9
ENV TERM xterm

#各種パッケージをインストール
RUN apt-get install -y vim less
RUN pip install --upgrade pip
RUN pip install --upgrade beautifulsoup4
RUN pip install --upgrade chromedriver
RUN pip install --upgrade chromedriver-binary
RUN pip install --upgrade datetime
RUN pip install --upgrade pandas
RUN pip install --upgrade pyyaml
RUN pip install --upgrade requests
RUN pip install --upgrade selenium
  • 上記をコピーし:wqで保存する

  • 次はdocker-compose.ymlを作成

touch docker-compose.yml
  • これもvimで中身を編集。 vim docker-compose.yml
docker-compose.yml
version: '3'
services:
  python3:
    restart: always
    build: .
    container_name: 'scrayping'
    working_dir: '/root/'
    tty: true
    volumes:
      - ./opt:/root/opt
  • 上記をコピーし:wqで保存する

3.Dockerイメージを作成

  • docker-compose.ymlの内容に基づいてイメージを作成する
    • このとき「-t」でdockerhubでつけるアカウント名/イメージ名にタグを命名しておかないとDockerHubにpush出来ない。注意
$ docker-compose build -t {dockerhubのアカウント名/イメージ名}
  • イメーシを作成できたかチェック
$ docker images

REPOSITORY                  TAG                 IMAGE ID            CREATED             SIZE
shunsukenashiki/scrayping   latest              ed6df28c956e        About an hour ago   1.15GB

こんな感じで作られていればOK

4.Dockerイメージを元に、コンテナを作成

  • 作成したDocker imageを元にコンテナを作る
    • 「-d」をつける理由は、-dをつけないとバックグラウンドでコンテナが起動しないから
$ docker run -d --name #{任意のコンテナ名} -d -it {REPOSITORY名}
  • コンテナが作成され、起動されているかチェック。作成できていたら完了
$ docker ps

CONTAINER ID        IMAGE                       COMMAND             CREATED             STATUS              PORTS               NAMES
b42460c71e9c        shunsukenashiki/scrayping   "python3"           5 seconds ago       Up 4 seconds                            scrayping

5.作成したコンテナを実行する

  • 作成したコンテナを実行する
$ docker exec -it #{任意のコンテナ名} bash
  • Dockerfileに指定したバージョンが入っていれば完了
$ pip3 list

Package         Version
--------------- ---------
beautifulsoup4  4.9.1
certifi         2020.6.20
chardet         3.0.4
chromedriver    2.24.1
DateTime        4.3
idna            2.10
numpy           1.19.1
pandas          1.1.0
pip             20.2.1
python-dateutil 2.8.1
pytz            2020.1
requests        2.24.0
selenium        3.141.0
setuptools      49.2.1
six             1.15.0
soupsieve       2.0.1
urllib3         1.25.10
wheel           0.34.2
zope.interface  5.1.0

Doekerhubにアップロードする

  • 動作確認できたら、Dockerhubにpushしていく
$ docker push shunsukenashiki/{dockerhubのアカウント名/イメージ名}:latest
  • Dockerhubにちゃんと紐づけられていれば完了。あとはここからimageをpullすれば使いまわせる
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

たぶん動くから!DockerでHTMLページを表示する!

はじめに

この記事では Dockerfile を用いた起動と docker-compose を用いた起動で nginxHTML ファイルを表示するまでを記しています。
初心者のため間違い等ございましたらご指摘いただけると幸いです。

環境

macOS Catalina version 10.15.6
Docker version 19.03.13
Visual Studio Code version 1.42.1

前提条件

Dockerをインストールしていること。
インストールしていない場合は以下のリンクを参考にインストールしてみてください。
たぶん動くから!Docker始めてみよう!

Dockerfileを使った方法

Dockerfile を使った基本的な方法で起動していきます。

フォルダ構成

以下の様なフォルダ構成にしました。

フォルダ構成
root/
├── Dockerfile
├── conf
│   └── default.conf
└── src
    ├── css
    │   └── topPage.css
    ├── js
    │   └── topPage.js
    ├── nextPage.html
    └── topPage.html

各ファイル

各ファイルは以下の通りです。

Dockerfile
FROM nginx:latest

ADD ./conf/default.conf /etc/nginx/conf.d/default.conf
ADD ./src /usr/share/nginx/html

RUN echo "start nginx"
  • FROM nginx:latest : FROM はDockerのイメージのベースとなるものの指定です。今回は nginx の最新版を使うという意味です。
  • ADD {ローカルパス} {コンテナパス} : ローカルパスのものをコンテナパスに追加します。
  • RUN : Dockerビルド時にDockerのコンテナ内で実行するコマンドを指定します。
/conf/default.conf
server {
        listen 80;
        listen 443;
        server_name localhost;

        root /usr/share/nginx/html;
        index topPage.html topPage.html;

        location / {
                try_files $uri $uri/ /src/index.html$query_string;
        }

        error_page   500 502 503 504  /50x.html;
                 location = /50x.html {
                   root   /usr/share/nginx/html;
        }
}

default.confnginx で用いる設定ファイルです。
当記事では深くは触れません。

/src/topPage.html
<html>
  <meta charset="utf-8" />
  <link rel="stylesheet" href="./css/topPage.css">
  <script src="./js/topPage.js"></script>
  <body>
    <h1 style="color:red;">Hello Enginx with Docker!!</h1>
    <a href="./nextPage.html">next</a>
  </br>
  <p>css!</p>
  <button type="button" onclick="execute()">実行!!!
  </button>
  <div id="output"></div>
  </body>
</html>
src.nextPage.html
<html>
<body>
  <h1>nextPage!</h1>
  <a href="./topPage.html">back</a>
</body>
</html>

CSSとJavaScriptは動作するかどうかの確認のため適当に作成します。

/src/css/topPage.css
p {
  color : red;
}
/src/js/topPage.js
function execute() {
  var elem = document.getElementById("output");
  var now = new Date();
  var hour = now.getHours();
  var min = now.getMinutes();
  elem.innerHTML = "ただいまの時刻は" + hour + ":" + min + "です";
}

Dockerfileを使った起動

ファイルが用意できたらターミナルで Dockerfile が配置されているディレクトリへ移動し、 docker build コマンドでコンテナイメージを作成します。

$ docker build -t app:0.0.1 .
  • -t : タグ付けすることができます。タグ付することでコンテナの起動やイメージを削除するときにタグ名が割り当てられるので管理が楽になります。また、:コロンで区切ることでバージョンを指定することもできます。
  • . : Dockerfile が配置されているパスを指定します。今回は Dockerfile の配置されているディレクトリでコマンドを叩いているので . ですね。

コンテナイメージが作成できているかを確認します。

$ docker images
REPOSITORY              TAG                 IMAGE ID            CREATED             SIZE
app                     0.0.1               8ddbfbb33917        14 hours ago        133MB

app というコンテナイメージでバージョンが 0.0.1 の物が作成されていますね。

次にコンテナイメージから docker container run コマンドで実際にコンテナを起動させます。

$ docker container run --name app --rm -d -p 8080:80 app:0.0.1
  • --name app : appという名前で起動します。こちらもコンテナイマージをビルドするときと同じく、イメージ名を指定すると管理が楽になるので指定します。
  • --rm : コンテナを停止した際に自動的にコンテナを削除してくれます。
  • -d : コンテナをバックグラウンドで実行します。
  • -p {ローカルポート}:{コンテナポート} : ローカルのポートをコンテナのポートに転送します。
  • app:0.0.1 : 起動するコンテナイメージを指定します。

起動しているか確認します

$ docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                  NAMES
f64c1f7ef410        app:0.0.1           "/docker-entrypoint.…"   7 seconds ago       Up 6 seconds        0.0.0.0:8080->80/tcp   app

コンテナは起動している様ですね。
http://localhost:8080/ へアクセスしてみます。
スクリーンショット 2020-10-18 14.56.15.png
画面が表示され、CSSが動作していることも確認できますね!

「実行」ボタンを押してみます。
スクリーンショット 2020-10-18 14.57.04.png
JavaScriptも動作していますね!

「next」リンクをクリックしてみましょう。
スクリーンショット 2020-10-18 14.58.43.png
nextPageへのページ遷移もできましたね!

起動中のコンテナ内に入りたい場合は docker exec コマンドを使用します。

$ docker exec -it app bash -p

コンテナを停止させ、コンテナ一覧を確認します。

$ docker stop app
app
$ docker ps -a
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS                         PORTS               NAMES

停止していますね。
* -a : -aオプションは停止中のコンテナも表示してくれます。起動時に --rm オプションを付けていたので停止時に自動的にコンテナを削除してくれていますね。 --rm オプションを付けなかった場合どんどん停止されたコンテナが溜まっていくので注意です。削除したい場合は docker rm {CONTAINER ID} で削除してください。

docker-composeを使った方法

docker-conmpose はDockerをインストールすると一緒に入っています。
本来はwebサーバーやDBサーバー等、複数のコンテナの管理に用いると便利なのですが、私の技術が足りないため今回はnginxのみを乗せた docker-compose を作成します。

フォルダ構成

docker-compose.ymlを追加し、先ほどのDockerfileの構成をnginxフォルダ内に格納しました。

フォルダ構成
root/
├── docker-compose.yml
└── nginx
    ├── Dockerfile
    ├── conf
    │   ├── default.conf
    │   └── nginx.conf
    └── src
        ├── css
        │   └── topPage.css
        ├── js
        │   └── topPage.js
        ├── nextPage.html
        └── topPage.html

docker-compose.yml

docker-compose.yml でコンテナのイメージビルドからコンテナ起動までを定義します。

docker-compose.yml
version: '3'               #このdocker-compose.ymlファイルの"書式"のバージョン
services:                  #サービスについて定義する。
  nginx:                   #nginxについて定義するという宣言。
    build: ./nginx         #Dockerfileのパス
    image: app:0.0.2       #イメージ名を指定
    container_name: "app"  #コンテナ名を指定
    ports:                 #ポート公開内容を指定する。
      - "8080:80"          #コンテナ側で80だとホスト側で8080となる。
    volumes:               #ホストのフォルダをコンテナに追加する。 ホスト側:コンテナ側
      - ./nginx/conf/default.conf:/etc/nginx/conf.d/default.conf  #コンフィグファイルをコピー
      - ./nginx/src:/usr/share/nginx/html         #ソースをコピー

volumesDockerfile で記述している部分と被るので省略可能です。

DBサーバーや他のサービスに等を増やしたい場合は nginx と同列に追記していきます。

docker-composeを使った起動

docker-compose.yml が配置されているフォルダに移動し docker-compose up コマンドを使用します。

$ docker-compose up -d
Starting app        ... done
$ docker ps
CONTAINER ID        IMAGE                   COMMAND                  CREATED             STATUS              PORTS                               NAMES
94af0a7f210d        docker_plactice_nginx   "/docker-entrypoint.…"   2 minutes ago       Up About a minute   0.0.0.0:8080->80/tcp                app

起動が確認できますね!
Dockerfile での起動でポート等指定していたのに対し、 docker-compose での起動は楽ですね!

停止するときは docker-compose down コマンドを使います。

$ docker-compose down
Stopping app        ... done
$ docker ps -a
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES

起動中のコンテナはありませんね。

終わりに

当記事では docker-compose の便利さが全然引き出せていない状態で終わってしまいました。
今回は私の中で一旦整理したいという目的もあり、中途半端になって申し訳ありません。
次回は docker-compose で複数のコンテナの同時起動の記事が書けたらいいなぁ…。

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

たぶん動くから!DockerでnginxからHTMLページを表示しよう!

はじめに

この記事では Dockerfile を用いた起動と docker-compose を用いた起動で nginxHTML ファイルを表示するまでを記しています。
初心者のため間違い等ございましたらご指摘いただけると幸いです。

環境

macOS Catalina version 10.15.6
Docker version 19.03.13
Visual Studio Code version 1.42.1

前提条件

Dockerをインストールしていること。
インストールしていない場合は以下のリンクを参考にインストールしてみてください。
たぶん動くから!Docker始めてみよう!

Dockerfileを使った方法

Dockerfile を使った基本的な方法で起動していきます。

フォルダ構成

以下の様なフォルダ構成にしました。

フォルダ構成
root/
├── Dockerfile
├── conf
│   └── default.conf
└── src
    ├── css
    │   └── topPage.css
    ├── js
    │   └── topPage.js
    ├── nextPage.html
    └── topPage.html

各ファイル

各ファイルは以下の通りです。

Dockerfile
FROM nginx:latest

ADD ./conf/default.conf /etc/nginx/conf.d/default.conf
ADD ./src /usr/share/nginx/html

RUN echo "start nginx"
  • FROM nginx:latest : FROM はDockerのイメージのベースとなるものの指定です。今回は nginx の最新版を使うという意味です。
  • ADD {ローカルパス} {コンテナパス} : ローカルパスのものをコンテナパスに追加します。
  • RUN : Dockerビルド時にDockerのコンテナ内で実行するコマンドを指定します。
/conf/default.conf
server {
        listen 80;
        listen 443;
        server_name localhost;

        root /usr/share/nginx/html;
        index topPage.html topPage.html;

        location / {
                try_files $uri $uri/ /src/index.html$query_string;
        }

        error_page   500 502 503 504  /50x.html;
                 location = /50x.html {
                   root   /usr/share/nginx/html;
        }
}

default.confnginx で用いる設定ファイルです。
当記事では深くは触れません。

/src/topPage.html
<html>
  <meta charset="utf-8" />
  <link rel="stylesheet" href="./css/topPage.css">
  <script src="./js/topPage.js"></script>
  <body>
    <h1 style="color:red;">Hello Enginx with Docker!!</h1>
    <a href="./nextPage.html">next</a>
  </br>
  <p>css!</p>
  <button type="button" onclick="execute()">実行!!!
  </button>
  <div id="output"></div>
  </body>
</html>
src.nextPage.html
<html>
<body>
  <h1>nextPage!</h1>
  <a href="./topPage.html">back</a>
</body>
</html>

CSSとJavaScriptは動作するかどうかの確認のため適当に作成します。

/src/css/topPage.css
p {
  color : red;
}
/src/js/topPage.js
function execute() {
  var elem = document.getElementById("output");
  var now = new Date();
  var hour = now.getHours();
  var min = now.getMinutes();
  elem.innerHTML = "ただいまの時刻は" + hour + ":" + min + "です";
}

Dockerfileを使った起動

ファイルが用意できたらターミナルで Dockerfile が配置されているディレクトリへ移動し、 docker build コマンドでコンテナイメージを作成します。

$ docker build -t app:0.0.1 .
  • -t : タグ付けすることができます。タグ付することでコンテナの起動やイメージを削除するときにタグ名が割り当てられるので管理が楽になります。また、:コロンで区切ることでバージョンを指定することもできます。
  • . : Dockerfile が配置されているパスを指定します。今回は Dockerfile の配置されているディレクトリでコマンドを叩いているので . ですね。

コンテナイメージが作成できているかを確認します。

$ docker images
REPOSITORY              TAG                 IMAGE ID            CREATED             SIZE
app                     0.0.1               8ddbfbb33917        14 hours ago        133MB

app というコンテナイメージでバージョンが 0.0.1 の物が作成されていますね。

次にコンテナイメージから docker container run コマンドで実際にコンテナを起動させます。

$ docker container run --name app --rm -d -p 8080:80 app:0.0.1
  • --name app : appという名前で起動します。こちらもコンテナイメージをビルドするときと同じく、イメージ名を指定すると管理が楽になるので指定します。
  • --rm : コンテナを停止した際に自動的にコンテナを削除してくれます。
  • -d : コンテナをバックグラウンドで実行します。
  • -p {ローカルポート}:{コンテナポート} : ローカルのポートをコンテナのポートに転送します。
  • app:0.0.1 : 起動するコンテナイメージを指定します。

起動しているか確認します

$ docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                  NAMES
f64c1f7ef410        app:0.0.1           "/docker-entrypoint.…"   7 seconds ago       Up 6 seconds        0.0.0.0:8080->80/tcp   app

コンテナは起動している様ですね。
http://localhost:8080/ へアクセスしてみます。

スクリーンショット 2020-10-18 14.56.15.png

画面が表示され、CSSが動作していることも確認できますね!

「実行」ボタンを押してみます。

スクリーンショット 2020-10-18 14.57.04.png

JavaScriptも動作していますね!

「next」リンクをクリックしてみましょう。

スクリーンショット 2020-10-18 14.58.43.png

nextPageへのページ遷移もできましたね!

起動中のコンテナ内に入りたい場合は docker exec コマンドを使用します。

$ docker exec -it app bash -p

コンテナを停止させ、コンテナ一覧を確認します。

$ docker stop app
app
$ docker ps -a
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS                         PORTS               NAMES

停止していますね。
* -a : -aオプションは停止中のコンテナも表示してくれます。起動時に --rm オプションを付けていたので停止時に自動的にコンテナを削除してくれていますね。 --rm オプションを付けなかった場合どんどん停止されたコンテナが溜まっていくので注意です。削除したい場合は docker rm {CONTAINER ID} で削除してください。

docker-composeを使った方法

docker-conmpose はDockerをインストールすると一緒に入っています。
本来はwebサーバーやDBサーバー等、複数のコンテナの管理に用いると便利なのですが、私の技術が足りないため今回はnginxのみを乗せた docker-compose を作成します。

フォルダ構成

docker-compose.ymlを追加し、先ほどのDockerfileの構成をnginxフォルダ内に格納しました。

フォルダ構成
root/
├── docker-compose.yml
└── nginx
    ├── Dockerfile
    ├── conf
    │   ├── default.conf
    │   └── nginx.conf
    └── src
        ├── css
        │   └── topPage.css
        ├── js
        │   └── topPage.js
        ├── nextPage.html
        └── topPage.html

docker-compose.yml

docker-compose.yml でコンテナのイメージビルドからコンテナ起動までを定義します。

docker-compose.yml
version: '3'               #このdocker-compose.ymlファイルの"書式"のバージョン
services:                  #サービスについて定義する。
  nginx:                   #nginxについて定義するという宣言。
    build: ./nginx         #Dockerfileのパス
    image: app:0.0.2       #イメージ名を指定
    container_name: "app"  #コンテナ名を指定
    ports:                 #ポート公開内容を指定する。
      - "8080:80"          #コンテナ側で80だとホスト側で8080となる。
    volumes:               #ホストのフォルダをコンテナに追加する。 ホスト側:コンテナ側
      - ./nginx/conf/default.conf:/etc/nginx/conf.d/default.conf  #コンフィグファイルをコピー
      - ./nginx/src:/usr/share/nginx/html         #ソースをコピー

volumesDockerfile で記述している部分と被るので省略可能です。

DBサーバーや他のサービスに等を増やしたい場合は nginx と同列に追記していきます。

docker-composeを使った起動

docker-compose.yml が配置されているフォルダに移動し docker-compose up コマンドを使用します。

$ docker-compose up -d
Starting app        ... done
$ docker ps
CONTAINER ID        IMAGE                   COMMAND                  CREATED             STATUS              PORTS                               NAMES
94af0a7f210d        docker_plactice_nginx   "/docker-entrypoint.…"   2 minutes ago       Up About a minute   0.0.0.0:8080->80/tcp                app

起動が確認できますね!
Dockerfile での起動でポート等指定していたのに対し、 docker-compose での起動は楽ですね!

停止するときは docker-compose down コマンドを使います。

$ docker-compose down
Stopping app        ... done
$ docker ps -a
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES

起動中のコンテナはありませんね。

終わりに

当記事では docker-compose の便利さが全然引き出せていない状態で終わってしまいました。
今回は私の中で一旦整理したいという目的もあり、中途半端になって申し訳ありません。
次回は docker-compose で複数のコンテナの同時起動の記事が書けたらいいなぁ…。

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

調べては忘れるので、よく使うDockerコマンドをまとめてみた【随時更新】

Dockerコマンドを調べては、また使うときに忘れて。。。
こんな繰り返しをしていて、かなり時間の無駄をしているなと思ったので
今回、コマンドについてアウトプットして記憶に定着させようと思い記事にしています。
Dockerはただいま勉強中のため、コマンドについては随時更新予定です。

docker runコマンド

docker runコマンドは、新しいコンテナを起動させたいときに実行するコマンド。

docker run [オプション名] イメージ名

docker rmコマンド

docker rmコマンドは、docker runコマンドで起動したコンテナを削除するコマンドです。

docker rm 削除したいコンテナID

全てのコンテナを削除したい場合は、コマンドを実行する。
docker ps -a -qが実行されてコンテナ一覧を元に
削除を行ってくれる。

docker rm `docker ps -a -q`

参考URL

Dockerイメージとコンテナの削除方法

Docker超入門②〜Dockerの始め方〜【初心者向け】

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

wsl2/docker 環境で単体ロボットシミュレータをインストール/実行する!

概要

TOPPERS/箱庭WGでは,単体ロボットシミュレータを一般公開していますが,インストールの手間が結構かかります.そこで,wsl2/dockerを使って,一括インストーラ作りましたので,ご紹介します.早いマシンなら約10分くらいでインストールおわります.

前提

以下の環境はインストール済みであることを前提とします.

  • Windows10 Home or Pro(どちらでも大丈夫)
  • WSL2
  • docker

また,インストール実施前に docker は立ち上げておく必要あります.

$ sudo service docker start

インストール手順

手順は以下の通りです.

  1. 箱庭WGリポジトリ(hakoniwa-Unity-HackEV)をクローンする
  2. インストーラを叩く

箱庭WGリポジトリ(hakoniwa-Unity-HackEV)をクローンする

WSL2 上で,Windows上の任意のフォルダに移動し,作業用ディレクトリを作成/移動します.

$ mkdir toppers_work
$ cd toppers_work

クローンする.

$ git clone https://github.com/toppers/hakoniwa-Unity-HackEV.git

インストーラを叩く

以下のディレクトリに移動します.

$ cd hakoniwa-Unity-HackEV/docker/v850

インストーラを叩きます(約10分くらいと思います).

$ utils/install.bash

インストールが成功すると,以下の docker image が入っているはずです.

$ sudo docker images
REPOSITORY                  TAG                 IMAGE ID            CREATED             SIZE
kanetugu2015/athrill-v850   v1.1.1              05fbd93397be        4 hours ago         370MB
kanetugu2015/ev3rt-v850     v1.0.0              f0c6b1412652        6 days ago          1.78GB

ビルド方法

EV3RTのアプリケーションは,base_practice_1 というフォルダで作成しています.
ビルド方法は以下の通りです.

ビルド用 docker コンテナ起動

WSL2上から,以下のコマンドで ビルド用コンテナを起動します.

$ bash run-builder.bash
root@MyComputer:~#

ビルド実行

起動した docker コンテナ上で以下のコマンドを叩くだけ.

# ./clean_build.bash base_practice_1

シミュレーション実行方法

シミュレーションを実行するには,Unityアプリとathrillをそれぞれ起動する必要があります.

Unityアプリの実行

Unityアプリは,wsl2 上で,以下のコマンドで起動します.

$ ./utils/start-unity.bash single-robot

成功すると,以下のUIが起動します.

image.png

athrillの実行

athrillの実行は docker コンテナで行います.

WSL2上から,以下のコマンドで athrill 実行用コンテナを起動します.

$ bash run-athrill.bash base_practice_1

起動したら,athrill実行コマンドを叩くだけ.

# ./start-athrill.bash

デモ

シミュレーション実行のデモはこんな感じです.
pv-run4.gif

補足

注意点として,現状の WSL2は MMAP の不具合があるようです.
ですので,Unityとathrillの通信方式はUDP通信にしています.
WSL2のMMAP不具合が解消したら,MMAP方式の検討も進めたいと思います.

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

Docker+Springbootアプリの構築方法(基礎学習用)

はじめに

ここ最近、メンテナンスやらドキュメント作成やらでソースをゼロから構築する機会がなく、基礎的な部分について忘れがちな状況である。

そんなとき、たまたまみたこの動画をみた。

How to create Docker Image and run Java App (Spring Boot Jar) in a Docker Engine | Tech Primers(2017)
https://www.youtube.com/watch?v=FlSup_eelYE

動画の内容が非常にわかりやすく、そういえばシンプルにゼロから作ったことがないことを思い出しため、勉強用に構築作業を実施。

構築要件

Dockerコンテナ上でSpringbootアプリが動くようにする。
外部からRestAPIでアクセスできるようにする。

DockerDesktop のセットアップ

windows 環境でDockerを動かせるようにする。

インストーラー

こちらの公式からダウンロード可能
https://hub.docker.com/editions/community/docker-ce-desktop-windows
[Docker Desktop Installer.exe]を起動して手順に沿う

Docker起動

インストーラー後の再起動で起動する

ProxySetUp(必要に応じて)

~/.docker/config.json ファイルにproxies 以下を追加する。

{
  "credsStore": "desktop",
  "stackOrchestrator": "swarm",
  "credStore": "desktop",
  "proxies":
     {
       "default":
       {
         "httpProxy": "http://127.0.0.1:3001",
         "httpsProxy": "http://127.0.0.1:3001",
         "noProxy": "*.test.example.com,.example2.com"
       }
     }
}

<参考>
https://docs.docker.com/network/proxy/

Springboot 初期構築

○ SpringbootのProjectを作成
https://start.spring.io/

RestAPI を作りたいために、Dependency にRest Repositoryを追加。
※ 目的にあったライブラリを追加する。

image.png

設定をしたあと、GENERATEボタンを押下するとZIPファイルがダウンロードされる

 ローカルに配置

image.png

Rest用のサンプルモジュールを作成

poc.dockerspringboot配下にcontrollerパッケージを作成して、サンプルファイルを作成

package poc.dockerspringboot.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/rest/message/helloWorldOnDocker")
public class HelloWorldOnDocker {

    @GetMapping
    public String hello(){
        return "Hello from Docker Container";
    }
}

maven ビルドを実施

mvn clean install の実施
image.png

Docker File のセットアップ

Docker Fileの作成

プロジェクト直下にDockerfile を作成

image.png

作成例

FROM adoptopenjdk:11-jre-openj9
ADD target/docker-springboot.jar docker-springboot.jar
EXPOSE 8081
ENTRYPOINT ["java", "-jar", "docker-springboot.jar"]

FROM: ベースイメージの指定(Java)

OpenJDKの中で、商用でも使えるadoptopenjdkを利用する。
https://hub.docker.com/_/adoptopenjdk

ADD:モジュールの追加

生成したモジュールをDockerに追加する。
target配下にモジュールができているが、jar のファイル名が長いためアウトプット名称を[finalName]で指定する。

<build>
    <finalName>docker-springboot</finalName>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>

参考:spring-boot-maven-pluginの設定情報
https://spring.pleiades.io/spring-boot/docs/current/maven-plugin/reference/html/

EXPOSE: 外部公開用のポート

Dockerコンテナで開放したいポートを指定

コンテナ内部で、spring-boot 起動時のデフォルトポートは8080
この値を変更したい場合は、application.properties に、server.port=8082 などを追加

Docker起動時に、内部で起動しているポートと外部で公開するポートの紐づけを行う。

ENTRYPOINT: 起動ポイント

jar コマンドでモジュールを起動。
このコマンドを実行することで、組み込みサーバー(Tomcat)が起動してアクセスを受け付けるようになる。

コンテナ起動

# イメージ作成
docker build -f Dockerfile -t docker-spring-boot .

# 作成物の確認
docker images

# コンテナ起動
docker run -p 8081:8080 docker-spring-boot


アクセス確認

http://localhost:8081/rest/message/helloWorldOnDocker
image.png

さいごに

新しい技術的な発見はないものの、たまにこういった作業を実施すると思考がクリアになる。
あとから振り替えられるように、ガイドライン的に記載。

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