20200522のdockerに関する記事は3件です。

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 -v

Dockerのコマンド(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に興味はあるけれど、なかなか最初の一歩が踏み出せない方の参考になれば幸いです。

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

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するまで保持される。

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

【SPAチュートリアル】Django (Django REST Framework) + Nuxt.js (Vuetify) で作る実践レベルのWebアプリケーション - 4. バックエンド(Django)の基礎 (2) -

※本記事は【SPAチュートリアル】Django (Django REST Framework) + Nuxt.js (Vuetify) で作る実践レベルのWebアプリケーションの第四章です。

本チュートリアルを実践すれば、簡単なアプリケーションをフルスクラッチで開発できるようになります。
この記事は第四章の"バックエンド(Django)の基礎"です。
技術的な指摘、わかりにくい点など、何でも気軽にコメントください:raised_hand:

最初に

本章は3部構成といたします。

  • 第一部
    1. Djangoの基本構成
    2. Django REST Framework
  • 第二部 :arrow_left:本記事はここです。
    1. 設定ファイルとロギング
  • 第三部
    1. ユーザ管理と認証
    2. 権限管理

本章のスクリプトは、下記のGithubリポジトリのchapter-4というブランチにアップしております。

Githubリポジトリ

設定ファイル

Djangoでは、アプリケーションの設定を記述するファイルがあります。
それがsettings.pyです。
設定ファイルに記述した内容は、from django.conf import settingsで呼び出して使用できます。

データベースの接続先などもここに記述します。
デフォルトではSQLite3に接続するように記述されています。

第一部でインストールしたDjango REST Frameworkの挙動も、この設定ファイルからカスタマイズできます。
詳しくは公式サイトを参考にしくてださい。

以降、必要に応じて設定ファイルをカスタマイズします。

設定ファイルの場所を変更

先ほどデータベースの接続先も設定ファイルに記述していると説明しました。
では、本番環境と開発環境でデータベースの接続先を変えたいという場合には、どうすればよいのでしょうか?
他にも、DEBUG = TrueALLOWED_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.pybackend/develop.pyという設定ファイルを2つ用意することになります。

単純に2つ設定ファイルを作成すると重複する内容が増えてしまうので、backend/base.pyというファイルを用意して、環境に依存しない共通設定を記述します。
そして、bakcend/product.pybackend/develop.pyは、このbackend/base.pyをimportして、環境に依存する設定のみを
記述します。

以下の手順で、設定ファイルを整理してみましょう。

  1. backend/settings.pybackend/base.pyにリネーム
  2. backend/product.pyを作成
  3. backend/develop.pyを作成
backend/product.py
from .base import *

DEBUG = False
backend/develop.py
from .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.py
ALLOWED_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.py
REST_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.py
REST_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.py
JWT_AUTH = {
    'JWT_SECRET_KEY': SECRET_KEY,
}

実行確認

さて、それではpython manage.py runserverを実行して、http://127.0.0.1:8000/tasks/へアクセスしてみます。

image.png

ログインしていないので、認証処理ではじかれるようになりましたね。
少し小さくてわかりにくいですが、右上にログインボタンがありますのでクリックしてログインしてみましょう。
今度はちゃんと表示されるはずです。

さらに確認 (※重要)

使用しているブラウザによって異なるのですが、Chromeを想定して進めていきます。
ブラウザでF12キーを押すと、開発者用ツールが画面右側に開きますので、開いてください。
そうすると、Networkと言うタブがありますので、そちらをクリックしてください。

image.png

ここから、ブラウザが実行した通信の結果を見ることができます。
どんなリクエストを飛ばしているのか、どんなレスポンスを受け取っているのかが見れます。
この中に、tasks/へのリクエストが存在しているはずですので、詳細を見てみましょう。
何も表示されていない場合は、F5などで一度画面をリロードしてください。

image.png

右ペインを下へスクロールしていくと、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.py
REST_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.py
from 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.py
class 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

image.png

snake_caseのフィールドがcamelCaseに変換されていますね!

まとめ

設定ファイルを使って、プラグインの挙動を変更することに成功しました。
乱暴な言い方をすると、設定ファイルはただの定数置き場みたいなものです。
当然、自分のアプリケーションで使うための設定を記述しておいて、処理中で呼び出して使用しても構いません。
一元化しておきたい環境情報などは、設定ファイルに記載して管理するようにしましょう。

ロギング

※未作成

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