- 投稿日:2020-05-22T19:56:15+09:00
Docker入門 - インストールからコンテナの起動まで
この記事でやること
- GCPで作成したUbuntuVMにDockerをインストールする。
- DockerHubよりコンテナイメージをダウンロードする。
- ダウンロードしたイメージからコンテナを作成し動かす。
環境
以下の内容でGCPでVMインスタンスを作成しています。
カテゴリ 設定 リージョン us-central 1(アイオワ) ゾーン us-central1-a マシンファミリー 汎用 シリーズ N1 マシンタイプ n1-standard-1 (vCPU x 1、メモリ 3.75 GB) OS Ubuntu 16.04 LTS ディスク 標準の永続ディスク(30GB) ファイアウォール HTTP、HTTPSトラフィックを許可するにチェック Dockerをインストールする
作成したVMにsshでログインし、以下の手順を実行します。
リポジトリの更新とパッケージの最新化
はじめに作成したVMに対して、リポジトリの更新とパッケージの最新化を行います。
以下のコマンドを実行します。$ sudo apt update $ sudo apt upgradeインストール
公式で紹介されている手順を参考にしています。以下のコマンドを実行します。
# 1. 必要なツールをインストール $ sudo apt install apt-transport-https ca-certificates curl gnupg-agent software-properties-common # 2. Docker公式のGPGキーを追加 $ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add - # 3. リポジトリの設定 $ sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" # 4. Dockerをインストール $ sudo apt install docker-ce docker-ce-cli containerd.io # 5. インストールされているか確認(バージョンが表示されていれば成功です) $ sudo docker -vDockerのコマンド(
docker ***
)はすべてルート権限が必要です。
以降の例では省略していますが、Dockerを使うときは常にsudo
で実行するようにしてください。コンテナを起動して"Hello from Docker!"する
Dockerを使用する準備ができたので、実際にDockerイメージを取得してコンテナを起動させます。
イメージとコンテナの違い
Dockerは「コンテナ」と呼ばれるホストOSとは隔離された領域で、別のOSやApacheなどのアプリケーションを動かしています。そのコンテナの元となるのが「イメージ」です。
イメージはコンテナを構築するためのライブラリなどが格納されており、これをもとにDockerはコンテナを作ります。
- イメージ: 設計図
- コンテナ: 設計図(イメージ)をもとにできた製品
と考えるとイメージしやすいと思います。
イメージの取得
コンテナのベースとなるイメージは自分で作成するかDocker Hubから取得します。Docker Hubには公式をはじめ、たくさんのイメージが登録されています。
今回は動作確認として、hello-worldイメージを取得します。
イメージの取得はdocker pull
コマンドを使用します。書式は以下です。$ docker pull [オプション] 名前[:タグ]今回は、オプションはなしで名前はhello-worldです。
以下のコマンドを実行してください。(タグについては後述しますが、省略してOKです)$ docker pull hello-world以下のコマンドを実行し、REPOSITORYにhello-worldが登録されていれば正しく取得できています。
$ docker images REPOSITORY TAG IMAGE ID CREATED SIZE hello-world latest bf756fb1ae65 4 months ago 13.3kBイメージのタグ
Dockerのイメージにはタグが付けられ、イメージ名の後ろに
:
で区切って指定します。
タグは主にバージョン管理のために使われ、docker pull
コマンドでもタグを指定してイメージを取得できます。
タグは省略可能で、省略したときはlatest
を指定したのと同じことになります。# 以下のコマンドの実行結果は同じ $ docker pull hello-world $ docker pull hello-world:latestつまり、先ほどの
docker pull hello-world
は暗黙的に最新版を指定していたわけです。イメージからコンテナを起動する
ダウンロードしたhello-worldイメージをベースに、コンテナを起動します。
コンテナの起動はdocker run
コマンドで行います。書式は以下です。$ docker run [オプション] イメージ [コマンド] [引数...]以下のコマンドを実行してください。
実行後に下記と同じメッセージが表示されたら、成功しています。$ docker run hello-world Hello from Docker! This message shows that your installation appears to be working correctly. To generate this message, Docker took the following steps: 1. The Docker client contacted the Docker daemon. 2. The Docker daemon pulled the "hello-world" image from the Docker Hub. (amd64) 3. The Docker daemon created a new container from that image which runs the executable that produces the output you are currently reading. 4. The Docker daemon streamed that output to the Docker client, which sent it to your terminal. To try something more ambitious, you can run an Ubuntu container with: $ docker run -it ubuntu bash Share images, automate workflows, and more with a free Docker ID: https://hub.docker.com/ For more examples and ideas, visit: https://docs.docker.com/get-started/次に、コンテナが作成されていること確認するため
docker ps
コマンドでコンテナの一覧を表示します。書式は以下です。$ docker ps [オプション]先ほど
docker run
コマンドを実行したことでコンテナが生成・起動されましたが、hello-worldコンテナはメッセージの出力が終わると稼働を停止してしまいます。そこで-a
オプションを追加して停止しているコンテナ情報も表示させます。
以下のコマンドを実行してください。$ docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES e2f7f512e093 hello-world "/hello" 3 seconds ago Exited (0) 2 seconds ago blissful_black「IMAGE: hello-world」から、このコンテナが「hello-worldイメージ」をベースに作成されたことがわかります。
コンテナおよびイメージの削除
最後に、今回使用したコンテナとイメージをDockerから削除しましょう。
まずはコンテナを削除します。コマンドと書式は以下です。$ docker rm [オプション] コンテナID [コンテナID...]削除に指定するコンテナIDは、
docker ps -a
コマンドで表示される「CONTAINER ID」を参照してください。
IDはとても長いですが、他のコンテナIDと衝突しなければ最初の3文字くらいまで省略してもOKです。
以下のコマンドを実行してください。コンテナIDは人によって異なりますので注意してください。$ docker rm e2f次にイメージの削除です。まずコマンドと書式です。
$ docker rmi [オプション] イメージ [イメージ...]コンテナの削除が
docker rm
でイメージの削除がdocker rmi
です。まぎらわしいので注意してください。
イメージの削除は「IMAGE ID」かイメージ名を指定します。IMAGE IDを指定して削除する場合はdocker images
で該当イメージのIDを参照してください。
以下のコマンドを実行してhello-worldイメージを削除します。$ docker rmi hello-worldおわりに
今回はDockerのインストールからイメージの取得、コンテナの作成および起動までを行いました。
Dockerに興味はあるけれど、なかなか最初の一歩が踏み出せない方の参考になれば幸いです。
- 投稿日:2020-05-22T11:28:52+09:00
Dockerビルドで git cloneで証明書エラーが発生するときの対応
DockerでPHPの環境作っていたらエラーに遭遇
Using version ^3.1 for laravel/installer ./composer.json has been created Loading composer repositories with package information Updating dependencies (including require-dev) Package operations: 16 installs, 0 updates, 0 removals - Installing symfony/process (v5.0.8): Downloading (failed) Downloading (failed) Downloading (failed) Failed to download symfony/process from dist: The "https://api.github.com/repos/symfony/process/zipball/3179f68dff5bad14d38c4114a1dab98030801fd7" file could not be downloaded: SSL operation failed with code 1. OpenSSL Error messages: error:1416F086:SSL routines:tls_process_server_certificate:certificate verify failed Failed to enable crypto failed to open stream: operation failed Now trying to download from source - Installing symfony/process (v5.0.8): Cloning 3179f68dff Installation failed, deleting ./composer.json. [RuntimeException] Failed to clone https://github.com/symfony/process.git via https, ssh protocols, aborting. - https://github.com/symfony/process.git Cloning into '/composer/vendor/symfony/process'... fatal: unable to access 'https://github.com/symfony/process.git/': server certificate verification failed. CAfile: none CRLfile: noneどうやら、以下でソースをgitからcloneしてくる処理でコケたらしい。
composer global require "laravel/installer"
対策
DockerfileのRUNコマンドに
export GIT_SSL_NO_VERIFY=1
を追記RUN export GIT_SSL_NO_VERIFY=1 \ && composer global require "laravel/installer"ちなみに
export
コマンドはターミナルからログアウトもしくはexitするまで保持される。
- 投稿日:2020-05-22T07:26:19+09:00
【SPAチュートリアル】Django (Django REST Framework) + Nuxt.js (Vuetify) で作る実践レベルのWebアプリケーション - 4. バックエンド(Django)の基礎 (2) -
※本記事は【SPAチュートリアル】Django (Django REST Framework) + Nuxt.js (Vuetify) で作る実践レベルのWebアプリケーションの第四章です。
本チュートリアルを実践すれば、簡単なアプリケーションをフルスクラッチで開発できるようになります。
この記事は第四章の"バックエンド(Django)の基礎"です。
技術的な指摘、わかりにくい点など、何でも気軽にコメントください最初に
本章は3部構成といたします。
- 第一部
- Djangoの基本構成
- Django REST Framework
- 第二部 本記事はここです。
- 設定ファイルとロギング
- 第三部
- ユーザ管理と認証
- 権限管理
本章のスクリプトは、下記のGithubリポジトリのchapter-4というブランチにアップしております。
設定ファイル
Djangoでは、アプリケーションの設定を記述するファイルがあります。
それがsettings.py
です。
設定ファイルに記述した内容は、from django.conf import settings
で呼び出して使用できます。データベースの接続先などもここに記述します。
デフォルトではSQLite3に接続するように記述されています。第一部でインストールしたDjango REST Frameworkの挙動も、この設定ファイルからカスタマイズできます。
詳しくは公式サイトを参考にしくてださい。以降、必要に応じて設定ファイルをカスタマイズします。
設定ファイルの場所を変更
先ほどデータベースの接続先も設定ファイルに記述していると説明しました。
では、本番環境と開発環境でデータベースの接続先を変えたいという場合には、どうすればよいのでしょうか?
他にも、DEBUG = True
やALLOWED_HOSTS = []
という設定がありますが、これはセキュリティに関わる大事な設定なので、開発環境と本番環境とで同じ設定では困ります。ここで一度manage.pyをみてください。
manage.py#!/usr/bin/env python """Django's command-line utility for administrative tasks.""" import os import sys def main(): os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'backend.settings') # <-- ここに注目してください try: from django.core.management import execute_from_command_line except ImportError as exc: raise ImportError( "Couldn't import Django. Are you sure it's installed and " "available on your PYTHONPATH environment variable? Did you " "forget to activate a virtual environment?" ) from exc execute_from_command_line(sys.argv) if __name__ == '__main__': main()
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'backend.settings')
というところに注目してください。
DJANGO_SETTINGS_MODULEという環境変数から、設定ファイルの場所を取得しています。
よって、DJANGO_SETTINGS_MODULEという環境変数の値を変更することで、設定ファイルの場所を変更できます。これによってどういうことができるかというと、
開発環境の場合はDJANGO_SETTINGS_MODULE = 'backend.develop'
、本番環境の場合はDJANGO_SETTINGS_MODULE = 'backend.product'
と設定することにより、環境に応じた設定ファイルを読み込むことができます。
この場合ですと、backend/prdouct.py
、backend/develop.py
という設定ファイルを2つ用意することになります。単純に2つ設定ファイルを作成すると重複する内容が増えてしまうので、
backend/base.py
というファイルを用意して、環境に依存しない共通設定を記述します。
そして、bakcend/product.py
とbackend/develop.py
は、このbackend/base.py
をimportして、環境に依存する設定のみを
記述します。以下の手順で、設定ファイルを整理してみましょう。
backend/settings.py
をbackend/base.py
にリネームbackend/product.py
を作成backend/develop.py
を作成backend/product.pyfrom .base import * DEBUG = Falsebackend/develop.pyfrom .base import * DEBUG = True環境変数を操作して、どういう違いがあるかを見てみましょう。
backend/develop.py
を参照する場合はこれまでと変わらないので省略します。
DJANGO_SETTINGS_MODULE = 'backend.product'
の状態でrunserver
してみます。$ python manage.py runserver CommandError: You must set settings.ALLOWED_HOSTS if DEBUG is False.と、エラーが出ましたね。デバッグモードでないのであれば、実行を許可するホストを指定しろと怒られます。
では、設定をし直して再度http://127.0.0.1:8000/tasks/
にアクセスしてみましょう!backend/product.pyALLOWED_HOSTS = ['127.0.0.1']$ python manage.py runserver Performing system checks... System check identified no issues (0 silenced). April 30, 2020 - 16:13:50 Django version 3.0.5, using settings 'backend.product' # <-- 実はここに使用している設定ファイルが表示されます。 Starting development server at http://127.0.0.1:8000/ Quit the server with CTRL-BREAK.アクセスした結果、CSSとJavaScriptが適用されていない状態で表示されるはずです。
これは、デバッグモードにすると、Djangoに内蔵されているデフォルトのStaticファイルローダーが停止するからです。
デバッグモードをFalseにすると、NginxやApacheなどのWebサーバを介して配信している前提になります。
そのため、CSSやJavaScriptも、Webサーバを介して別途配信しているとみなされます。
デバッグモードのときは、開発用Webサーバが勝手にCSSやJavaScriptを探し出して配信してくれていたんですね。DRFに関する設定
ここまでDjango REST Frameworkについて、何も設定をせずにきました。
設定をしていきましょう。
- 認証
base.pyREST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES': [ 'rest_framework.authentication.BasicAuthentication', 'rest_framework.authentication.SessionAuthentication', ], 'DEFAULT_PERMISSION_CLASSES': [ 'rest_framework.permissions.IsAuthenticated', ], }下記の内容を設定しています。
- ベーシック認証とセッション認証を、認証処理として使用する
- このアプリケーションではデフォルトで認証を求める(ゲストユーザを認めない)
さて、本チュートリアルでは、トークン認証という方式を追加して、使用します。
参考記事: Cookie(Session)での認証と Token での認証の違いについてトークン認証でまず大事になるのが、トークンの暗号化方式を決めることです。
今回は最も主流であるJWT(ジョット)を使用します。
Django REST FrameworkではデフォルトではJWTトークンによる認証をサポートしていません。
そのため、django-rest-framework-jwtを追加します。$ pip install djangorestframework-jwtそれでは、設定をカスタマイズしていきます。
その前に少しだけ寄り道します。ミドルウェア
Djangoでは、HTTPリクエストの受取時、HTTPレスポンスの返却時に、処理を挟み込むことができます。
これがミドルウェアと呼ばれる機能です。
認証処理もこのミドルウェアの一部として取り扱われます。他にも、パーサー(parser)などがミドルウェアとしてよく使用されます。
Pythonでは変数をスネークケース(snake_case)で記述しますが、JavaScriptではキャメルケース(camelCase)で記述します。
そのため、リクエスト受取時、レスポンス返却時に、変数名を変換する必要があります。
この変換処理を実行してくれるのが、パーサーです。JWTの設定
本題に戻ります。
まず、ミドルウェア(Django REST framework JWT)をインストールします。
pip install djangorestframework-jwt
次に、設定ファイルに使用するミドルウェアを記述しましょう。
base.pyREST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES': [ 'rest_framework_jwt.authentication.JSONWebTokenAuthentication', # <-- この行を追加 'rest_framework.authentication.BasicAuthentication', 'rest_framework.authentication.SessionAuthentication', ] }認証に使うミドルウェアはひとつである必要はありません。
Aの方式で認証に失敗したら、次はBの方式でトライする、という具合で複数のミドルウェアを使用できます。
JWTを使うにあたって、セキュリティを考慮してトークン生成に使うシークレットキーを設定しておきましょう。
Djangoではプロジェクト生成時にランダムな文字列を生成して、SECRET_KEY
という名前でsettings.py
に配置してくれています。
今回はbase.py
にリネームしているので、base.py
の中にSECRET_KEY = ****
があるはずです。base.pyJWT_AUTH = { 'JWT_SECRET_KEY': SECRET_KEY, }実行確認
さて、それでは
python manage.py runserver
を実行して、http://127.0.0.1:8000/tasks/
へアクセスしてみます。ログインしていないので、認証処理ではじかれるようになりましたね。
少し小さくてわかりにくいですが、右上にログインボタンがありますのでクリックしてログインしてみましょう。
今度はちゃんと表示されるはずです。さらに確認 (※重要)
使用しているブラウザによって異なるのですが、Chromeを想定して進めていきます。
ブラウザでF12
キーを押すと、開発者用ツールが画面右側に開きますので、開いてください。
そうすると、Networkと言うタブがありますので、そちらをクリックしてください。ここから、ブラウザが実行した通信の結果を見ることができます。
どんなリクエストを飛ばしているのか、どんなレスポンスを受け取っているのかが見れます。
この中に、tasks/
へのリクエストが存在しているはずですので、詳細を見てみましょう。
何も表示されていない場合は、F5
などで一度画面をリロードしてください。右ペインを下へスクロールしていくと、Request Headersというエリアがあります。
この中にAuthorization
という項目がありません。
JWTのようなトークン認証を実行する場合、Request Headersには前述のAuthorization
という項目にトークンを設定して、リクエストを送ります。
今回は実はJWTによる認証ではなく、セッション認証によってログインに成功しています。
JWTを使った認証は、この後で構築するNuxt.jsからリクエストを飛ばす際に使用します。JWTによる認証はDRFのデフォルト機能ではないので、今回使用したデフォルトのログイン機能ではJWTを想定していないのは当然ですね。
パーサーの設定
ミドルウェアの項目で少し触れてパーサーの設定をします。
今回は、Python, JavaScript間の記述ルールのギャップを埋めるために、パーサーを設定します。
djangorestframework-camel-case
pip install djangorestframework-camel-case
でインストールします。次に、設定ファイルに追記します。
base.pyREST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES': [ 'rest_framework_jwt.authentication.JSONWebTokenAuthentication', 'rest_framework.authentication.BasicAuthentication', 'rest_framework.authentication.SessionAuthentication', ], 'DEFAULT_PERMISSION_CLASSES': [ 'rest_framework.permissions.IsAuthenticated', ], # ↓↓↓ここから追記↓↓↓ 'DEFAULT_RENDERER_CLASSES': ( 'djangorestframework_camel_case.render.CamelCaseJSONRenderer', 'djangorestframework_camel_case.render.CamelCaseBrowsableAPIRenderer', ), 'DEFAULT_PARSER_CLASSES': ( 'djangorestframework_camel_case.parser.CamelCaseFormParser', 'djangorestframework_camel_case.parser.CamelCaseMultiPartParser', 'djangorestframework_camel_case.parser.CamelCaseJSONParser', ), }さて、このまま
python manage.py runserver
してもよいのですが、/tasks
のレスポンスの中にスネークケースのフィールドがないので、変化がわかりません。
そこで、created_at
, "updated_at"というフィールドを追加してみましょう。models.pyfrom django.db import models class Task(models.Model): ... # 下記の2行を追加 created_at = models.DateTimeField('Create date time', auto_now_add=True, null=True) updated_at = models.DateTimeField('Update date time', auto_now=True, null=True) ...serializers.pyclass TaskSerializer(serializers.ModelSerializer): class Meta: model = Task fields = ( "id", "title", "detail", "deadline", "status", "created_at", "updated_at", )マイグレーションして、実行してみましょう。
python manage.py makemigrations
python manage.py migrate
python manage.py runserver
snake_case
のフィールドがcamelCase
に変換されていますね!まとめ
設定ファイルを使って、プラグインの挙動を変更することに成功しました。
乱暴な言い方をすると、設定ファイルはただの定数置き場みたいなものです。
当然、自分のアプリケーションで使うための設定を記述しておいて、処理中で呼び出して使用しても構いません。
一元化しておきたい環境情報などは、設定ファイルに記載して管理するようにしましょう。ロギング
※未作成