20200430のdockerに関する記事は16件です。

【�database.yml】DBをDockerとrails server両方で使えるように�設定

Dokerで開発時に、docker-compose rundocker-compose run よりも早いrails serverで一時的に動作確認をしたい場合があるかと思う。
そういった場合にも、以下のconfig/database.yml内の記述だけで対応することができる。

検証環境

$ sw_vers
ProductName:    Mac OS X
ProductVersion: 10.15.3
BuildVersion:   19D76
$ ruby -v
ruby 2.5.1p57 (2018-03-29 revision 63029) [x86_64-darwin19]
$ rails -v
Rails 5.2.4.2

database.ymlについて

まず、database.ymlとは

config/database.yml(例)
default: &default
  adapter: mysql2
  encoding: utf8
  collation: utf8_general_ci
  pool: 5
  host: <%= ENV['MYSQL_HOST'] || 'localhost' %>
  username: <%= ENV['MYSQL_USERNAME'] || 'root' %>
  password: <%= ENV['MYSQL_PASSWORD'] || '' %>
  socket: /tmp/mysql.sock

主な内容

adapter: 使用するデータベースの種類(postgresql, mysql2等)

encoding: 文字コード

collation: MySQLの文字列と照合順序(ソート順)、”文字コード言語名比較法”で構成される

pool: 確保する接続プールの数

host: データベースが動作しているホスト名またはIPアドレス

username,password: データベースに接続するユーザー名・パスワード

socket: DB通信の接続口

比較演算子: ||

orと同じ働きをする。
Ruby 2.7.0 リファレンスマニュアル 演算子式

文法:

式 `||' 式
式 or 式
左辺を評価し、結果が真であった場合にはその値を返します。左辺の評価結果が偽であった場合には右辺を評価しその評価結果を返します。

config/database.ymlのhost,username,passwordの部分をこれで書くことで、ENV['MYSQL_HOST']等の環境変数がない場合に、localhost等を返してくれる。

終わりに。

最後まで読んで頂きありがとうございます:bow_tone1:
転職の為、未経験の状態からRailsを学習しております。正しい知識を着実に身に着け、実力のあるエンジニアになりたいと考えています。継続して投稿していく中で、その為のインプットも必然的に増え、成長に繋がるかと考えています。
今現在、初心者だからといって言い訳はできないですが、投稿の内容に間違っているところや、付け加えるべきところが多々あるかと思いますので、ご指摘頂けると幸いです。この記事を読んで下さりありがとうございました。

参考にさせて頂いた記事

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

初心者のKubernetes入門(書籍 Kubernetes 実践入門の写経から学ぶ)StatfulSet編

背景

個人的にインフラの知識以上にこれからのアプリケーションが動く環境を作ってデプロイしたりしてこれからの知識を身に着けたい。そしてより一層、自分の知識のアップデートをしたいと思いました。

その中でこの本に出会い、これから少しずつやったことを残し、未来の自分への手紙としてもあり、見つめ直せればと思いました。

引用や参考と今回の自分の勉強用の書籍の紹介

技術評論社『Kubernetes実践入門』のサンプルコード
Kubernetes実践入門 プロダクションレディなコンテナ&アプリケーションの作り方

実際の学びについて

書籍を読みながら、章ごとに少しずつ進めていきたいと思います。
GitHub のソースコードも使いながら学んで行きたいと思います。
この章の勉強は本当に書籍の写経が主になるかもしれません・・・

勉強開始

  1. StatufulSet を使った MySQL を定義 ※いまここ
  2. MySQL のデータ格納用に内部ストレージを割り当て
  3. 設定ファイルを Master、Slave 用に設定し配置
  4. バックアップ用に外部ストレージを割り当て
  5. Master-Slave 間でデータを同期

StatefulSet を使った MySQL を定義

以前写経で学んだ、 ConfigMap / Secret マニュフェストを使って、MySQL のコンフィグ情報を各 yaml ファイルに定義します。

cm.yaml
apiVersion: v1
data:
  MYSQL_DATABASE: mattermost
  MYSQL_USER: myuser
kind: ConfigMap
metadata:
  creationTimestamp: null
  name: common-env
secret.yaml
apiVersion: v1
data:
  MYSQL_PASSWORD: bXlwYXNzd29yZA==
  MYSQL_ROOT_PASSWORD: cm9vdHBhc3N3b3Jk
kind: Secret
metadata:
  creationTimestamp: null
  name: common-env
mysql-sts.yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: mysql
spec:
  serviceName: mysql
  selector:
    matchLabels:
      app: mysql
  updateStrategy:
    type: RollingUpdate
  replicas: 2
  template:
    metadata:
      labels:
        app: mysql
    spec:
      initContainers:
      - name: init-mysql
        image: k8spracticalguide/mysql:5.7.22
        command: ["bash", "/mnt/scripts/setup.sh"]
        volumeMounts:
        - name: confd
          mountPath: /mnt/conf.d
        - name: scripts
          mountPath: /mnt/scripts
        - name: initdb
          mountPath: /mnt/initdb
      containers:
      - name: mysql
        image: k8spracticalguide/mysql:5.7.22
        envFrom:
        - configMapRef:
            name: common-env
        - secretRef:
            name: common-env
        volumeMounts:
        - name: data
          mountPath: /var/lib/mysql
        - name: initdb
          mountPath: /docker-entrypoint-initdb.d
        - name: confd
          mountPath: /etc/mysql/conf.d
        - name: backup
          mountPath: /mnt/backup
      volumes:
      - name: initdb
        emptyDir: {}
      - name: confd
        emptyDir: {}
      - name: scripts
        configMap:
          name: mysql-scripts
      - name: backup
        persistentVolumeClaim:
          claimName: backup-mysql
  volumeClaimTemplates:
  - metadata:
      name: data
    spec:
      accessModes: ["ReadWriteOnce"]
      resources:
        requests:
          storage: 1Gi
マニュフェストを適用
$ kubectl apply -f cm.yaml -f secret.yaml -f mysql-sts.yaml
configmap/common-env created
secret/common-env created
statefulset.apps/mysql created
適用されているのを確認
$ kubectl get statefulsets
NAME    READY   AGE
mysql   2/2     74s

$ kubectl get po
NAME      READY   STATUS    RESTARTS   AGE
mysql-0   1/1     Running   0          77s
mysql-1   1/1     Running   0          73s

Headless Service を定義する

mysql-svc.yaml
apiVersion: v1
kind: Service
metadata:
  name: mysql
  labels:
    app: mysql
spec:
  ports:
  - port: 3306
    name: mysql
  clusterIP: None  # None にすることで Headless Serviceになる
  selector:
    app: mysql
適用されているのを確認
$ kubectl apply -f mysql-svc.yaml
service/mysql created

$ kubectl get svc
NAME         TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)    AGE
kubernetes   ClusterIP   10.96.0.1    <none>        443/TCP    24h
mysql        ClusterIP   None         <none>        3306/TCP   7s

ちゃんと動いているか確認する

$ kubectl get po -o wide
NAME      READY   STATUS    RESTARTS   AGE     IP          NODE             NOMINATED NODE   READINESS GATES
mysql-0   1/1     Running   0          5m3s    10.1.1.9    docker-desktop   <none>           <none>
mysql-1   1/1     Running   0          4m59s   10.1.1.10   docker-desktop   <none>           <none>

 kubectl run -ti --image=k8spracticalguide/busybox:1.28 dns-test --restart=Never --rm /bin/sh
If you don't see a command prompt, try pressing enter.

/ # nslookup mysql-0.mysql
Server:    10.96.0.10
Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.local

Name:      mysql-0.mysql
Address 1: 10.1.1.9 mysql-0.mysql.default.svc.cluster.local
/ # nslookup mysql-1.mysql
Server:    10.96.0.10
Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.local

Name:      mysql-1.mysql
Address 1: 10.1.1.10 mysql-1.mysql.default.svc.cluster.local

次は 3.7.5 章をやっていきます。

  1. StatufulSet を使った MySQL を定義
  2. MySQL のデータ格納用に内部ストレージを割り当て ※次回
  3. 設定ファイルを Master、Slave 用に設定し配置
  4. バックアップ用に外部ストレージを割り当て
  5. Master-Slave 間でデータを同期

最後に

前回うまくできなかったので、ご迷惑おかけしてしまいました。
次回以降はもっと大胆にうまくできることを祈りながら、写経 がんばります

今までの投稿

  1. 初心者のKubernetes入門(書籍 Kubernetes 実践入門の写経から学ぶ)Pod編
  2. 初心者のKubernetes入門(書籍 Kubernetes 実践入門の写経から学ぶ)NameSpace 編
  3. 初心者のKubernetes入門(書籍 Kubernetes 実践入門の写経から学ぶ)Label 編
  4. 初心者のKubernetes入門(書籍 Kubernetes 実践入門の写経から学ぶ)ReplicaSet 編
  5. 初心者のKubernetes入門(書籍 Kubernetes 実践入門の写経から学ぶ)Deployment 編
  6. 初心者のKubernetes入門(書籍 Kubernetes 実践入門の写経から学ぶ)Service 編
  7. 初心者のKubernetes入門(書籍 Kubernetes 実践入門の写経から学ぶ)ConfigMap 編
  8. 初心者のKubernetes入門(書籍 Kubernetes 実践入門の写経から学ぶ)Secret 編
  9. 初心者のKubernetes入門(書籍 Kubernetes 実践入門の写経から学ぶ)操作編
  10. 初心者のKubernetes入門(書籍 Kubernetes 実践入門の写経から学ぶ)体感編
  11. 初心者のKubernetes入門(書籍 Kubernetes 実践入門の写経から学ぶ)体感編パート2
  12. 初心者のKubernetes入門(書籍 Kubernetes 実践入門の写経から学ぶ)体感編パート3(Label操作)
  13. 初心者のKubernetes入門(書籍 Kubernetes 実践入門の写経から学ぶ)体感編パート3(OwnerReference 操作)
  14. 初心者のKubernetes入門(書籍 Kubernetes 実践入門の写経から学ぶ)マニュフェスト編
  15. 初心者のKubernetes入門(書籍 Kubernetes 実践入門の写経から学ぶ)マニュフェスト(ConfigMap)編
  16. 初心者のKubernetes入門(書籍 Kubernetes 実践入門の写経から学ぶ)通信編
  17. 初心者のKubernetes入門(書籍 Kubernetes 実践入門の写経から学ぶ)通信編 パート2
  18. 初心者のKubernetes入門(書籍 Kubernetes 実践入門の写経から学ぶ)通信編 パート3
  19. 初心者のKubernetes入門(書籍 Kubernetes 実践入門の写経から学ぶ)通信編 パート4
  20. 初心者のKubernetes入門(書籍 Kubernetes 実践入門の写経から学ぶ)公開編パート1(NodePort)
  21. 初心者のKubernetes入門(書籍 Kubernetes 実践入門の写経から学ぶ)公開編パート2(LoadBalancer)
  22. 初心者のKubernetes入門(書籍 Kubernetes 実践入門の写経から学ぶ)公開編パート3(Ingress)
  23. 初心者のKubernetes入門(書籍 Kubernetes 実践入門の写経から学ぶ)外部ストレージ編
  24. 初心者のKubernetes入門(書籍 Kubernetes 実践入門の写経から学ぶ)外部ストレージ編パート2
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Dockerを知らない素人がWindows10 homeでDocker-Toolboxを使ってDockerコンテナをRunするまでのお話。

前提条件

  • Dockerについて何も知らない社会人2年目の社員。
  • 上司から「Docker使うから今のうちに勉強しといて」とさっき言われた。。。
  • 備忘録とモチベ維持のため、Qiitaでアウトプットしつつ勉強を進める予定。

環境

  • 20万で買った4年目のNEC LAVIE(ノートPC)
    • Intel(R) Core(TM) i5-7Y54 CPU@ 1.20GHz 1.60GHz
    • RAM:8GB
    • Windows10 home 64bit
    • 最近起動が遅いのが悩み。(ログイン画面の表示が遅い) PC性能.png

Dockerとはなんぞや。。。

→コンテナ型の仮想環境を作ること!

仮想環境は、Anacondaで言うとBaseと異なる環境が作れる機能だから、PCベースで言うとPCの内側に別のPCが走ってるイメージ。内側のPCは外側のPCに影響されにくいからどんなPCに乗っけても動いてくれる頼りになるやつなんですね。

コンテナ型というのは、従来の仮想化と比べて良いという理解。従来の仮想化ではOSも含めて仮想化を行ってたけど、ファイル重い(OS分容量必要)し、起動遅いし(OSをたくさん起動するから)で扱いずらかった。これをマシーンリソースがかかるとかオーバーヘッドが生じるとかいう専門用語があるわけです。

コンテナ型は元のOSに乗せられるからファイル数軽いし、起動も早い。開発においては便利なツールなんですね。

従来の仮想化とDocker.png

従来でも元のOSを「ホストOS」、仮想側を「ゲストOS」なんて呼び分けてるけど、これはDockerでも同じなので覚えておいた方がいい(マウントの設定だったりで混乱する)

ただOSが減る分、元のPCから影響受けたりセキュリティ対策対策もちゃんとしないとだから、いいこと尽くめというわけでもないのは注意が必要ですね。

Docker環境構築するよ!

Dockerにも種類がある!

  • Docker Community Edition(Docker CE)

    • 無償版のDocker
    • とはいえ問題なく使える
      • Stable版…四半期ごとのリリース。安定したものが使える
      • Edge版…月1回のリリース。最新版を使える。
  • Docker Enterprise Edition(Docker EE)

    • 有償版Docker
    • Docker社認定のコンテナ・プラグインが使える
    • イメージのセキュリティあり
    • プライベートリポジトリあり

まぁ自主勉強ならDocker CEのStable版が良さそう。

さあインストールするよ!

インストールと言っても3種類くらいあるので個々人の環境によって選ぼう!
1. Docker for Windows
2. Docker for Mac
3. Docker Toolbox

1つ目のはWindowsじゃんやった~インストールしよっと思ったのもつかの間、これは「Windows 10 Pro」しか使えないので注意!しかもハイパーバイザーの設定も必要。出だしから難易度高いなぁ。Docker for Windows 10 Proって改名してもらいたい

2つ目のはMac用らしい...(筆者はマックのこと知らない)

3つ目のは我らがwindows 10 homeでも使えるやつ!早速インストール♪
https://github.com/docker/toolbox/releases
image.png

↑GitHubからインストール
このレガシーな雰囲気プンプンな香りに負けること無かれ。
インストール.png

↑見慣れた.exeインストーラー
インストールしていきますよ~

不明な発行元を「はい」して、デフォルトもまま突き進めばおk!
image.png
↑Docker Quickstart Terminalのショートカットをクリックして実行!
初回起動には時間がかかる。

image.png

↑これで準備完了!クジラのマークが目印!

Dockerを操作するよ!

まぁまずはversionを観察!
$ docker version
version情報の一覧が出てきます↓

$ docker version                                                                                                        Client:
 Version:           19.03.1
 API version:       1.40
 Go version:        go1.12.7
 Git commit:        74b1e89e8a
 Built:             Wed Jul 31 15:18:18 2019
 OS/Arch:           windows/amd64
 Experimental:      false

Server: Docker Engine - Community
 Engine:
  Version:          19.03.5
  API version:      1.40 (minimum version 1.12)
  Go version:       go1.12.12
  Git commit:       633a0ea838
  Built:            Wed Nov 13 07:28:45 2019
  OS/Arch:          linux/amd64
  Experimental:     false
 containerd:
  Version:          v1.2.10
  GitCommit:        b34a5c8af56e510852c35414db4c1f4fa6172339
 runc:
  Version:          1.0.0-rc8+dev
  GitCommit:        3e425f80a8c931f88e6d94a8c831b9d5aa481657
 docker-init:
  Version:          0.18.0
  GitCommit:        fec3683

なにはともあれHello World!

docker run hello-world

hello-worldというコンテナが立ち上がります!

$ docker run hello-world
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
0e03bdcc26d7: Pull complete                                                                                             Digest: sha256:8e3114318a995a1ee497790535e7b88365222a21771ae7e53687ad76563e8e76
Status: Downloaded newer image for hello-world:latest

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/

Hello from Docker!

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

kubernetesキホンのキ

1. はじめに

前回の投稿から、ご無沙汰してしまいました。前回、Dockerの導入編ということで、Dockerのインストール方法や、簡易的な使い方をまとめました。

今回は、その上位互換として知られる kubernetes の概念や用語について勉強したことをアウトプットしていきます。

2.kubernetesとは

kubernetesを一言でいうと、「コンテナを管理するためのシステム」です。
ざっくり言うと、「Docker 拡張バージョン」です。

Docker は単一マシンの上でコンテナを動作させるシステムでしたが、kubernetes は複数マシン上でコンテナを管理・統合させるためのシステムです。コンテナオーケストレーションといいます。

図2.1 は視覚的にDockerとkubernetesの差分を理解するために図示しました。
Dockerのみだと単一ホスト上で動作するコンテナの管理のみしかできませんが、kubernetesを利用することによって、複数のDockerホストをkubernetesのシステム上で管理することができるようになります。

kubernetesとDocker位置付け.png
図 2.1. Dockerとkubernetes

Docker でも Docker Swarm を利用することで複数マシン上でコンテナを管理することができますが、kubernetesの方が機能が充実していて実践に使用されているため、今回は kubernetesを取り扱っています。

3. kubernetes の用語

以下 kubernetes で使われている用語をまとめました。

No リソース名 意味・用途
1 Node ・コンテナを乗せる元のサーバ(物理マシン、VMどちらも)
2 Namespace ・仮想的なクラスタ
・開発者(チーム)それぞれのNamespaceを作ることでメインのNamaspaceを綺麗に保つことができる
3 Pod ・コンテナの集合体単位
・デプロイするコンテナの集合体になるので、密結合となるwebサーバ用コンテナとアプリケーションサーバ用コンテナをまとめると有効活用できる
4 ReplicaSet ・同じ仕様のPodを複数生成・管理
・Podのみでは単一のPodのデプロイしかできないため、複数のPodをデプロイする際に利用する
5 Deployment ・ReplicaSetの世代管理
・実運用では、ほとんどDeploymentの単位でデプロイを管理すること
6 Service ・Podにアクセスするための経路定義
・アクセスするPodを制御するために利用する
・L4層レベルでのアクセス制御が可能
7 Ingress ・Service公開の口
・L7層レベルでのアクセス制御が可能
・HTTP/HTTPSでのサービス公開をする際はServiceよりIngressをメインに使用する
8 ConfigMap ・構成ファイル、コマンドライン引数、環境変数、ポート番号などの設定情報を定義し、Podへ供給する
・うまく活用すると構成の変更や管理が容易にできる
9 PersistentVolume ・Podが利用するストレージを定義
・ストレージの実体である
10 PersistentVolumeClaim ・ストレージを論理的に抽象化したリソース
・PersistentVolumeを動的に確保する(必要な要領のみ)
11 StorageClass ・PersistentVolumeが確保するストレージの種類を定義(HDDやSSD等)
12 StatefulSet ・同じ仕様で一意性のあるPodを複数生成・管理
・継続的にデータを永続化するステートフルアプリケーション管理用リソース
13 job ・常駐目的ではない複数(もしくは単一)Podを生成し、正常終了するまで管理するリソース
・大規模計算やバッチ処理などで利用
14 Cronjob ・cron記法でスケジュール実行されるjob
15 Secret ・認証情報等の機密データを定義するリソース
・証明書や秘密鍵、パスワードなどの機密情報を管理する
16 Role ・Namespace内で操作可能なkubernetesリソースのルールを定義するリソース
・権限の管理に利用する
17 RoleBinding ・Roleとkubernetesリソースを利用するユーザーを紐付ける
18 ClusterRole ・クラスタ全体で操作可能なkubernetesリソースをルール定義する
19 ClusterRoleBinding ・ClusterRole と kubernetesリソースを利用するユーザーを紐付ける
20 ServiceAccount ・Pod に kubernetes リソースを操作させる際に利用するユーザー

3.まとめ

今回は、kubernetes の概念と用語についてまとめました。

時間があるときにこの用語を参考に実際に手を動かしながら、kubernetesの使い方を見ていきたいと思います。

その中でkubernetesのどういう部分が人気が高いのか考えていければと思います。うまくまとめられればqiitaにも投稿してみます。

また、最近マイクロサービスアーキテクチャを採用したシステムとkubernetesの相性がいいと言われますが、その意味も気にしながら手を動かしていこうと思います。

参考資料

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

dockerのコマンドメモ

記事の趣旨

dockerコマンドの覚え書き.

1. dockerのコマンド

docker search 検索キーワード

Docker Hubに登録されているイメージを,検索キーワードで検索する.

docker pull イメージ名

Docker Hubからイメージを取得する.

docker images

保有しているイメージの一覧を表示する.

docker run オプション イメージ名 コマンド

イメージを用いてコンテナを起動し,コマンドを実行する.かなりオプションが多いので代表的なものだけ以下に書いていく.全量はこちらを参照すること.

  • -d:コンテナをバックグラウンドで実行する
  • -i:ホストの標準入力をコンテナの標準入力にアタッチする
  • -t:コンテナに疑似TTYを割り当てる
  • -p xxx:yyy:ホストのxxx番ポートにコンテナのyyyポートを割り当てる
  • --name:コンテナに名前を割り当てる
  • --rm:コンテナ終了時に自動的に

-iとか-tとか入れないとどうなるの?という疑問は,こちらを読めば大体解消すると思う.

docker ps

コンテナの一覧を表示する.

docker attach コンテナ名

コンテナ内のPID-1のプロセスに接続する.離脱する場合はCtrl-p + qで.

docker exec オプション コンテナ名 コマンド

コンテナ内で新たなプロセスを立ち上げてコマンドを実行する.

docker build オプション パス

パスに置いてあるDockerfileという名称のファイルを用いてイメージをビルドする,オプションは以下.

  • -f:ビルドファイル名を明示する.
  • -t 名称:タグ:イメージにタグをつける

docker cp ホストパス コンテナ名:パス

ホストパスに存在するファイルをコンテナの指定パスにコピーする.

docker cp コンテナ名:パス ホストパス

先述とは逆に,コンテナの指定パスに存在するファイルをホストの指定パスにコピーする.

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

Django Sprint #3 ユーザーモデルのカスタマイズ

目次

1. Gitの運用
2. 各種設定
3. モデル概論
4. ユーザーモデルと認証、管理者

Gitの運用

Gitは初めて使う人にとっては少し難しく感じるかもしれません。その際は、言葉を追うより、「習うより慣れろ」的に実際に使ってみる方が分かりやすいでしょう。もし難しいようならGitを使わなくても開発はできます。

本チュートリアルは GitHub Flow を少し変更して行います。もしチームの別の運用ルールがあるのなら、そちらを優先させてください。

また、GitをGUI上で(コマンドラインからではなく、視覚的に)操作するには

をおすすめします。

試しに、READMEファイル(このプロジェクトの説明などを記入するファイル)を追加してみましょう。

branchとは

ブランチとは作業履歴を枝分かれさせて記録していくためのものです。試しに、以下のコマンドを叩いてみましょう。

$ git branch
* master

現在はmasterブランチしかないはずです。

2つのブランチ: master と develop

先ほど見たmasterブランチはプロジェクトのメインのブランチです。これを「常にデプロイ(公開)可能なもの」としましょう。つまり、テストも終わった「完成品」のみが許される神聖な場所です。
一方、普段の開発用のブランチをdevelopとします。これは開発用のメインのブランチで、公開はされません。

developブランチを切る

このdevelopブランチを作成しましょう。ブランチを切るのは次のコマンドです。

$ git checkout -b develop
Switched to a new branch 'develop'

一応ブランチの一覧を見ると、やはり

$ git branch
* develop
  master

このようになっています。*は今いるブランチを指します。
これをリモートに反映させましょう。

$ git push -u origin develop

オプション-uにより、ローカルリポジトリの現在のブランチの上流をorigin developに規定します。これを一度設定すると以後は

$ git push

のみでリモートのdevelopブランチにプッシュされることになります。
一度GitHubのページでdevelopブランチが正しく生成されているかどうか確認してみましょう。

実際の開発フロー

実際の開発はほとんど以下の繰り返しです。

  1. git pullでリモートの変更をローカルに反映
  2. git checkout -bでローカルに機能ブランチを作成
  3. git addgit commit で変更をローカルの機能ブランチに反映
  4. git push でローカル機能ブランチをリモートにアップ
  5. pull request を作成し、機能ブランチをdevelopブランチにmergeして良いかレビュー&修正
  6. GitHub上でmerge

参照 : 『プルリクエストを使った開発プロセス』

機能ブランチの作成

チームメンバーでまだGitに関する何のセットアップもしていない方は

を参照してください。

まず、リモートの変更をローカルに反映させます。

$ git checkout develop
$ git pull origin develop

次に、機能ブランチを切ります(作成します)。今回はブランチ名を「add-readme」とします。できるだけ、説明的なブランチ名にすると良いでしょう。

$ git checkout -b add-readme

add & commit

まず、機能を追加・変更・削除します。ここでは、codeディレクトリ直下に、README.mdというファイルを作ります。READMEとはプロジェクトの説明などを書く場所で、主に外部の人がこのプロジェクトがどのようなものなのか理解するために使うものです。ちなみに.mdはマークダウン記法を表す拡張子です。ファイルを作成し、ここでは内容を以下のように書き換えます。

/README.md
# Django Sprint
This is a tutorial for UTokyo Project Sprint.

各自好きなように書いてください。

次に、変更を保存、反映させます。

$ git add -A
$ git commit -m "Add README"

pull request

作業を完了したら、まずpushします。

$ git push origin add-readme

次に、pull request(プルリク)を送り、チームメンバーの確認段階に入ります。詳しい内容や手順は、

を参照してください。

これから

今後、細かいGit関連のコマンドは省略したいと思います。Git関連は慣れが重要なので、README.mdや次の設定変更などで一度使ってみることをおすすめします。

厄介なのは、コンフリクトを起こしたときです。まずGoogleで調べてみて、類似の事例がない場合は遠慮なくご相談ください。

各種設定

settings.py

一度settings.pyを開いてみてみましょう!
settings.pyはこのプロジェクトの「設定」を記述したものです。ここで各種設定を変更することができます。よく使うので、ざっと見ておくと良いでしょう。

タイムゾーンの変更

試しにタイムゾーン(時刻帯)を変更してみましょう。まず、settings.pyから以下の記述を探し出してください!

/config/settings.py
...
TIME_ZONE = 'UTC'
...

デフォルトではタイムゾーンは「UTC」、つまりロンドンのそれに設定されています。これを東京のものに変更するには以下のように書き換えてみれば良いです。

/config/settings.py
...
TIME_ZONE = 'Asia/Tokyo'
...

言語の変更

続いて言語を変更してみましょう。デフォルトでは「en-us」、つまり、英語(アメリカ)に設定されています。Djangoはたくさんの言語をサポートしており、幸いにも日本語にも対応しています。日本語に変更するには以下のように書き換えれば良いです。

/config/settings.py
...
LANGUAGE_CODE = 'ja'
...

モデル概論

MVCモデルとは

MVCモデルとは

  • モデル(Model): データベースとコントローラーの間で、データ処理を行うファイル群
  • ビュー(View): ブラウザとコントローラーの間で、リクエストの取得やHTMLなどのファイルの出力を担うファイル群
  • コントローラー(Controller): 中枢でモデルとビューからデータを受け取り、処理して返すファイル群

詳しくは

を参照してください。

DjangoにおけるMVCモデル

かなりややこしい表記の違いがあります。

  • モデル(Model): Djangoではモデル(Model)と呼ばれる
  • ビュー(View): Djangoではテンプレート(Template)と呼ばれる
  • コントローラー(Controller): Djangoではビュー(View)と呼ばれる

以後、ビューと書けば「コントローラー」を意味していると考えてください。

具体的なモデルファイル

具体的なモデルのファイルを見てみましょう。ここでは、サンプルとして他のプロジェクトから持ってきたものを見てみましょう。

...
class Post(models.Model):
    author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
    title = models.CharField(max_length=200)
    text = models.TextField()
    created_date = models.DateTimeField(default=timezone.now)
    published_date = models.DateTimeField(blank=True, null=True)

ブログアプリの記事のモデルをイメージしてください。このclass PostはExcelのシートのようなものです。一方、authortitleなどはExcelのシートの各カラム(列)に対応します。CharFieldなどはそのデータの型を表します。もしもモデルと別のモデルを組み合わせたいときはForeignKeyなどを使います。上の例ではauthorはPostモデルとユーザーモデルを繋ぐものです。このようなフィールドの種類とオプションは以下のドキュメントを参考にしてください。

ユーザーモデルと認証、管理者

Djangoにはユーザーモデルとその認証機能(ユーザー名、メールアドレス、パスワード)がデフォルトで付属しています。(パスワードはハッシュ化されて保存されます。)その点他のフレームワークよりも楽で、堅牢です。

ただし、注意しなければならないのは最初にユーザーモデルのカスタマイズを行わないと後から変更するのが難しくなる点です。不可能ではないのですが、面倒です。そこで、本チュートリアルでは最初に少しだけカスタマイズしておき、その後のカスタマイズを容易にしておきます。(実際に公式ドキュメントでも強く推奨されています。)

新しいアプリケーションの作成

まず、アプリケーションを作成します。Djangoにおけるアプリケーションとは、プロジェクトを構成する機能的な単位の一つです。再利用を簡単にするために機能ごとに分けることができます。しかし、本チュートリアルでは「cms」(Contents Management System)というアプリケーションにひとまず全てまとめようと思います。

$ docker-compose run --rm web django-admin startapp cms

これで、次のようなディレクトリ構造になったか確認してください。

code
├─ requirements.txt
├─ README.md
├─ manage.py
├─ config
│  ├─ __init__.py
│  ├─ asgi.py
│  ├─ settings.py
│  ├─ urls.py
│  └─ wsgi.py
├─ cms
│  ├─ migrations
│  ├─ __init__.py
│  ├─ admin.py
│  ├─ apps.py
│  ├─ models.py
│  ├─ tests.py
│  └─ views.py
├─ docker-compose.yml
└─ Dockerfile

先ほどみたような models.pyviews.py が見えていますね!実際にはこれらのファイルを書き換えることでWebを構築します。(ファイルを追加する場合もあります。)

ここで忘れずにアプリケーションを追加したという事実をDjangoに伝えましょう。

/settings.py
...
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'cms.apps.CmsConfig', #Added
]
...

tips : プロジェクト名を「config」にしたのは、このプロジェクトファイル全体を一つのアプリケーション(Djangoのアプリケーションとは別)と見たときに、「config」ディレクトリに設定に関する情報が含まれているからです。

models.pyの変更

モデルのファイルを書き換えますが、現時点ではコピペで構わないと思います。(もう少し簡潔な方法があるかもしれませんが、動作が確認されているのでひとまずこれで...)

/cms/models.py
from django.contrib.auth.base_user import (
    AbstractBaseUser, BaseUserManager,
)
from django.contrib.auth.models import PermissionsMixin
from django.contrib.auth.validators import UnicodeUsernameValidator
from django.core.mail import send_mail
from django.db import models
from django.utils import timezone
from django.utils.translation import gettext_lazy as _


# User-related
class UserManager(BaseUserManager):
    use_in_migrations = True

    def _create_user(self, email, password, **extra_fields):
        """
        Create and save a user with the given email and password.
        """
        if not email:
            raise ValueError('Users must have an email address')
        user = self.model(
            email=self.normalize_email(email),
            **extra_fields,
        )
        user.set_password(password)
        user.save(using=self._db)
        return user

    def create_user(self, email=None, password=None, **extra_fields):
        extra_fields.setdefault('is_staff', False)
        extra_fields.setdefault('is_superuser', False)
        return self._create_user(email, password, **extra_fields)

    def create_superuser(self, email=None, password=None, **extra_fields):
        extra_fields.setdefault('is_staff', True)
        extra_fields.setdefault('is_superuser', True)

        if extra_fields.get('is_staff') is not True:
            raise ValueError('Superuser must have is_staff=True.')
        if extra_fields.get('is_superuser') is not True:
            raise ValueError('Superuser must have is_superuser=True.')

        return self._create_user(email, password, **extra_fields)


class AbstractUser(AbstractBaseUser, PermissionsMixin):

    username_validator = UnicodeUsernameValidator()
    username = models.CharField(
        _('username'),
        max_length=150,
        unique=True,
        help_text=_('Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.'),
        validators=[username_validator],
        error_messages={
            'unique': _("A user with that username already exists."),
        },
    )

    first_name = models.CharField(_('first name'), max_length=150, blank=True)
    last_name = models.CharField(_('last name'), max_length=150, blank=True)

    email = models.EmailField(_('email address'), unique=True)

    is_staff = models.BooleanField(
        _('staff status'),
        default=False,
        help_text=_('Designates whether the user can log into this admin site.'),
    )
    is_active = models.BooleanField(
        _('active'),
        default=True,
        help_text=_(
            'Designates whether this user should be treated as active. '
            'Unselect this instead of deleting accounts.'
        ),
    )
    date_joined = models.DateTimeField(_('date joined'), default=timezone.now)

    objects = UserManager()

    EMAIL_FIELD = 'email'
    USERNAME_FIELD = 'username'
    REQUIRED_FIELDS = ['email']

    class Meta:
        verbose_name = _('user')
        verbose_name_plural = _('users')
        abstract = True

    def __str__(self):
        return self.email

    def clean(self):
        super().clean()
        self.email = self.__class__.objects.normalize_email(self.email)

    def email_user(self, subject, message, from_email=None, **kwargs):
        """Send an email to this user."""
        send_mail(subject, message, from_email, [self.email], **kwargs)


class User(AbstractUser):
    class Meta(AbstractUser.Meta):
        swappable = "AUTH_USER_MODEL"

(筆写追記)もしかしたら既存のAbstractUserを継承したUserを作れば済むかもしれません
実際にはこれが裏で記述されています。それを上書きしたような形です。具体的には、

  • username : ユーザー名
  • first_name : 名
  • last_name : 姓
  • email : メールアドレス
  • date_joined : 参加した日時
  • is_staff : 管理者権限を持つか?
  • is_active : 有効なアカウントか?

のカラムが設定されました。

最後に、「これをユーザーモデルとする」ことをDjangoに伝えます。settings.pyの最後の部分に

/settings.py
...
# Custom
AUTH_USER_MODEL = 'cms.User'

を付け足せば完了です。これ以降任意のタイミングでマイグレーションして大丈夫です。

admin.py

Djangoには管理者用ページが予め用意されています。これのおかげで、実際にコンソールをいじったり、SQL文を書くことなく、データを追加したり、更新したり、削除したりできます。

とりあえず、admin.pyを以下のように書き換えて、ユーザーモデルの変更を反映させましょう。

/cms/admin.py
from django import forms
from django.contrib import admin
from django.contrib.auth.models import Group
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
from django.contrib.auth.forms import ReadOnlyPasswordHashField

from .models import (
    User,
)


class UserCreationForm(forms.ModelForm):
    """A form for creating new users. Includes all the required
    fields, plus a repeated password."""
    password1 = forms.CharField(label='Password', widget=forms.PasswordInput)
    password2 = forms.CharField(label='Password confirmation', widget=forms.PasswordInput)

    class Meta:
        model = User
        fields = ('email',)

    def clean_password2(self):
        # Check that the two password entries match
        password1 = self.cleaned_data.get("password1")
        password2 = self.cleaned_data.get("password2")
        if password1 and password2 and password1 != password2:
            raise forms.ValidationError("Passwords don't match")
        return password2

    def save(self, commit=True):
        # Save the provided password in hashed format
        user = super().save(commit=False)
        user.set_password(self.cleaned_data["password1"])
        if commit:
            user.save()
        return user


class UserChangeForm(forms.ModelForm):
    """A form for updating users. Includes all the fields on
    the user, but replaces the password field with admin's
    password hash display field.
    """
    password = ReadOnlyPasswordHashField()

    class Meta:
        model = User
        fields = ('email', 'password', 'is_active', 'is_staff',)

    def clean_password(self):
        # Regardless of what the user provides, return the initial value.
        # This is done here, rather than on the field, because the
        # field does not have access to the initial value
        return self.initial["password"]


class UserAdmin(BaseUserAdmin):
    # The forms to add and change user instances
    form = UserChangeForm
    add_form = UserCreationForm

    # The fields to be used in displaying the User model.
    # These override the definitions on the base UserAdmin
    # that reference specific fields on auth.User.
    list_display = ('email', 'is_staff')
    list_filter = ('is_staff',)
    fieldsets = (
        (None, {'fields': ('email', 'password')}),
        ('Personal info', {'fields': ('username', 'last_name', 'first_name')}),
        ('Permissions', {'fields': ('is_staff',)}),
    )
    # add_fieldsets is not a standard ModelAdmin attribute. UserAdmin
    # overrides get_fieldsets to use this attribute when creating a user.
    add_fieldsets = (
        (None, {
            'classes': ('wide',),
            'fields': ('email', 'username', 'password1', 'password2'),
        }),
    )
    search_fields = ('email',)
    ordering = ('email',)
    filter_horizontal = ()


# Now register the new UserAdmin...
admin.site.register(User, UserAdmin)
# ... and, since we're not using Django's built-in permissions,
# unregister the Group model from admin.
admin.site.unregister(Group)

管理者は次のコマンドで生成できます。

$ docker-compose run --rm web python manage.py createsuperuser

その後にユーザー名、メールアドレス(架空で良い)、パスワードを要求されるので、指示通りに設定します。(これは後から使うので忘れないでください。)

マイグレーション

最後のステップはモデルが書き換わったことをデータベースに伝えることです。これをマイグレーションと良い、Djangoでは通常

  1. マイグレーションファイルの作成
  2. マイグレーションファイルの反映

の2ステップからなります。

$ docker-compose run --rm web python manage.py makemigrations 
$ docker-compose run --rm web python manage.py migrate

これで、初期設定は完了です!

管理者画面

http://localhost:8000/admin/ にアクセスすると、管理者のログイン画面になると思います。そこで、先ほど設定した「ユーザー名」と「パスワード」を入力すると、このような画面になるはずです。

スクリーンショット 2020-05-02 10.17.14.png

さて、このユーザー「追加」をクリックすると、簡単にユーザーが追加できます。一度、サンプルユーザーを追加してみましょう。

参照

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

application.propertiesの設定を環境変数を用いてlocal起動とDocker起動で切り替える

はじめに

下記の記事からの補足になる
【IntelliJ + Docker (APP+DB) + SpringBoot (Maven) 環境構築】
https://qiita.com/SSM3G/items/bd61970648d94ccb691a

docker-compose.ymlに環境変数を記載

environmentでDockerコンテナを起動する際に渡す環境変数を記述できる

docker-compose.yml
version: '3'
services:
  app:
    image: openjdk:14-jdk-alpine
    ports:
      - "80:8080"
    volumes:
      - .:/app
    working_dir: /app
    command: ./mvnw spring-boot:run
    environment:
      DATASOURCE: jdbc:mysql://db:3306/sample
  db:
# 省略

application.propertiesに環境変数を記載

下記部分
spring.datasource.url=${DATASOURCE:jdbc:mysql://localhost:3306/sample}

application.properties
spring.datasource.url=${DATASOURCE:jdbc:mysql://localhost:3306/sample}
spring.datasource.username=root
spring.datasource.password=password
spring.datasource.driver-class-name=com.mysql.jdbc.Driver

${環境変数:デフォルト値}と記載
環境変数が設定されている場合は環境変数を読み込む
設定されていない場合はデフォルト値を読み込む

SpringBoot起動

ローカル起動

環境変数は何も設定していないため
デフォルト値のjdbc:mysql://localhost:3306/sampleを読み込む

Docker起動

docker-compose.ymlでDATASOURCEという名で環境変数を設定しているため
appのコンテナ内ではこの環境変数が適応される

1.コンテナ内でSpringBootを起動
2.コンテナ内のapplication.propertiesを読み込む
3.環境変数のjdbc:mysql://db:3306/sampleを読み込む

screenshot.73.jpg
setで環境変数を確認したところ、ちゃんと設定されていました

おわり

${環境変数:デフォルト値}の書き方が全然わからなくてハゲそうだった
SprinBoot application.properties 環境変数 切り替え 変数 リテラル ${} シェル変数 とかで色々調べちまった

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

条件絞って Docker Image を削除する

grep と awk を組み合わせてやるぞ。

docker images | grep condition | awk '{print $3}' | xargs docker rmi  
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Comics reader ( onigoetz/comicsreader )

zip書庫対応

起動

docker run \
  --name=comics \
  --restart unless-stopped \
  -it -d \
  -v $(pwd):/comics \
  -p 8080:8080 \
  onigoetz/comicsreader

認証 (nginx)

htpasswd
user-name:{PLAIN}pass-w@rd
default.conf
server {
    listen       80;
    server_name  localhost;

    location / {
    auth_basic "Restricted";
    auth_basic_user_file /etc/nginx/htpasswd;

        proxy_pass http://192.168.xx.xx:8080;
    }

}
docker run -it --restart=always -d \
 --name nginx \
 -v $(pwd)/nginx/default.conf:/etc/nginx/conf.d/default.conf \
 -v $(pwd)/nginx/htpasswd:/etc/nginx/htpasswd \
 -p 80:8080 \
 nginx
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

macOS で Docker 内で動かした X11 アプリを表示させる

手順としては以下のような感じになります。

前準備

X11サーバ(XQuarts)をセットアップ。

  1. brew cask install xquartz とかで XQuarts を入れておく
  2. defaults write org.macosforge.xquartz.X11 enable_iglx -bool true でOpenGLも使えるようにする
  3. XQuarts を起動して「環境設定→ セキュリティ→ ネットワーク・クライアントからの接続を許可する」をチェック
  4. XQuarts を起動して「環境設定→ 入力→ OptionキーでAlt_LとAlt_Rを送信」をチェック(※X11アプリの実行自体には必須ではないがAltキーを使いたいので)

以上で準備OK。今後は XQuarts を起動さえしてあれば X11 が使えます。

上記2を XQuarts の起動後に実行していた場合は一度 XQuarts を再起動しといたほうが良いかも。あとは人によりますが、今後もX11アプリケーションをよく使うようなら XQuarts を自動起動しておくなど。

Docker で X11 アプリを実行してみる

実行時は DISPLAY 環境変数にホスト名を設定し、~/.Xauthority をボリュームマウントしてやればOKです。

docker run --rm -e DISPLAY="$(hostname):0" -v ~/.Xauthority:/root/.Xauthority alpine sh -c 'apk add --no-cache xeyes && xeyes'

とか、OpenGL を使ったデモなら↓こんな感じでとりあえず動きます。

docker run --rm -e DISPLAY="$(hostname):0" -v ~/.Xauthority:/root/.Xauthority alpine sh -c 'apk add --no-cache mesa-demos && glxgears'

注意点

自分がハマった点なのですが、XQuarts を初めて起動する以前に

docker run -v ~/.Xauthority:/root/.Xauthority 〜

を実行したことがあると、~/.Xauthority というパスに空ディレクトリが作成されてしまい、その後のX11アプリケーションの実行に支障が出ます。

もし以下のような状態になってしまっているようなら

$ ls -ld ~/.Xauthority*
drwxr-xr-x 2 kawaz staff 64 2020-04-30 13:11:11.716 /Users/kawaz/.Xauthority
-rw------- 1 kawaz staff 49 2020-04-30 13:16:24.357 /Users/kawaz/.Xauthority-n

rm -rf ~/.Xauthority* してから XQuarts の再起動をしましょう。

意図してその状態にしているなら docker run の引数を -v ~/.Xauthority-n:/root/.Xauthority に変えてやれば動きます。まぁ意図している人には釈迦に説法ですが、意図してない人が大抵だろうから通常は一度削除しとく対処で良いと思います。

参考

基本的には参考エントリと同じこと書いてますが ~/.Xauthority の辺りでハマったので個人的整理も兼ねてこのエントリを書きました。

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

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

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

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

最初に

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

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

本記事の内容を実践するのに必要な時間は、2時間です。
本章のスクリプトは、下記のGithubリポジトリのchapter-6というブランチにアップしております。

Githubリポジトリ

Djangoの基本構成

習うより慣れろという言葉があります。
プログラミングにおいても非常に重要な考え方でしょう。

不慣れな人もまずは書いてみる。そこから始めましょう。

git branch chapter-6でブランチを切り替えてから、作業を開始することを忘れないようにしてください。
masterで始めてしまうと、後から泣きます。

また、Djangoの基礎と題していますが、事細かに説明は記述しません。
わからないところは公式サイトを参考にしてください。

Djangoの基本構成

アプリケーション作成

Djangoでは、プロジェクトの配下に複数のアプリケーションを配置するという構成になります。
早速、作ってみましょう。

今回はタスク管理アプリケーションなので、tasksという名称にしましょう。

$ python manage.py startapp tasks

# 下記の構成でファイルが自動生成されます。
tasks
  │  admin.py
  │  apps.py
  │  models.py
  │  tests.py
  │  views.py
  │  __init__.py
  │
  └─migrations
          __init__.py

次に、追加したアプリケーションをDjangoに認識させる必要があります。
backend/backend/settings.pyというファイルにDjangoの設定を記述できるので、こちらに設定を追加します。

settings.py
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'tasks',    # <- 追加します。
]

MTVモデル

DjangoはMTVモデルを基本としています。
また、データベースの操作はSQLではなく、ORMを使用して行います。

参考記事: オブジェクト関係マッピング

MTVについて取り扱った記事は多く見受けられるのですが、あまりしっくりくるものがなかったので独自に書きます。

  • M: Model

オブジェクト指向プログラミングをご存知でしょうか?
すごく雑に言うと、実体をもつモノや、出来事を、オブジェクトとして取り扱い、オブジェクト同士の関連をもとにアプリケーションを構築していくという手法です。

Modelとは、このオブジェクト指向でいうところのオブジェクトに相当する概念です。
また、ModelはORMによって、データベースのテーブル定義としても使用されます。
完全にひとつのModelがひとつのテーブルと合致するというわけではありませんので、ご注意ください。

Modelの内容からデータベース定義を作成するコマンドが、python manage.py makemigrationsです。
そして、出来上がった定義をデータベースに反映するコマンドが、python manage.py migrateです。

  • T: Template

DjangoのTemplateは、雛形をもとに動的にHTMLを生成する機能です。
雛形の中には、Python変数と置き換えることを前提としたプレースホルダーを記述したり、分岐処理繰り返し処理のような簡単なプログラムを記述することもできます。
使用するテンプレートファイルを指定して、パラメータとしてdict型変数を渡すと、プレースホルダーとPython変数が置き換わり、動的にHTMLが生成されます。

Webというものは通常、ユーザからのHTTPリクエストを受けて、それに応じたHTTPレスポンスを返却します。
Djangoで開発するWebアプリケーションも、この仕組みを基本としています。

静的サイトにおいては、毎回決まったHTMLデータを返却するだけでよいので、単純にHTMLファイルを配置しておけばよいのですが、Webアプリケーションのような動的サイトでは、ユーザのHTTPリクエストの内容に応じて、HTTPレスポンスを動的に作り替える必要があります。

  • V: View

Webアプリケーションとしての根幹を担っています。
前項のTemplateで述べた、ユーザからのHTTPリクエストを受けて、それに応じたHTTPレスポンスを返却する機能です。
Viewはリクエストの内容をもとに、データベースの操作(CRUD)などを実行し、最後にHTTPレスポンスを返却します。
このHTTPレスポンスの返却のときに、必要に応じて前項のTemplateを使用することができます。

以下、少し話の本筋とはそれますが..
Viewの説明で、Templateを呼び出すものとして取り扱っている記事を見ることがあります。
Viewの本質はあくまでもHTTPリクエストをもとに処理を実行して、HTTPレスポンスを返却することにあり、Templateはいわばオマケ的要素でしかありません。
なお、本チュートリアルで開発するアプリケーションではHTMLではなくJSONを返却するので、Templateは使いません。

Django REST Framework

REST APIという言葉をご存知でしょうか?

REST APIとは、Web APIの設計思想です。
Web APIを作成するときは、こういう理念のもとに設計すべきというルールです。
REST APIとはあくまでも思想であり、こういう場合はこうしないといけない、という絶対的な正解がないため、深みにハマりすぎると抜け出せなくなります(※個人的感想)。

以下の記事にシンプルにまとめられているので、ご参考ください。

参考記事: 0からREST APIについて調べてみた


それでは、DjangoでREST APIを実装していきます。
Djangoには、Django REST FrameworkというREST APIの実装を助けてくれる強力なパッケージがあります。

Django REST Framework

インストール

それでは、導入していきましょう。
公式サイトのインストール手順に則っていきます。
まずはpipコマンドでパッケージを開発環境にインストールします。

$ pip install djangorestframework

DjangoにDjango REST Frameworkを認識させます。

settings.py
INSTALLED_APPS = [
    ...
    'rest_framework',    # <-- 追記します
]

Django REST Frameworkでは、Webブラウザ上で簡単にAPIを実行して試せるbrowsable APIという機能があります。
基本的に、APIを実行できるのはログインしているユーザだけになります。
browsable APIでもログインを必要とするので、ログイン機能が必要です。
ログインするためのViewとTemplateはDjango REST Frameworkがデフォルトでサポートしてくれているものを使用します。

Djangoのルーティングは下記のように作成していきます。
なお、ルーティングというのはURLとView関数を対応させることで、ルーターはルーティングをまとめる箱のことをいいます。

  1. アプリケーションごとにルーターを作成
  2. 各アプリケーションのルーターの中身=ルーティングを、プロジェクト全体のルーターに放り込む
backend/urls.py
"""backend URL Configuration

The `urlpatterns` list routes URLs to views. For more information please see:
    https://docs.djangoproject.com/en/3.0/topics/http/urls/
Examples:
Function views
    1. Add an import:  from my_app import views
    2. Add a URL to urlpatterns:  path('', views.home, name='home')
Class-based views
    1. Add an import:  from other_app.views import Home
    2. Add a URL to urlpatterns:  path('', Home.as_view(), name='home')
Including another URLconf
    1. Import the include() function: from django.urls import include, path
    2. Add a URL to urlpatterns:  path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.urls import path, include, re_path    # <-- include, re_pathを追加

urlpatterns = [
    path('admin/', admin.site.urls),
    re_path(r'^api-auth/', include('rest_framework.urls')),    # <-- ルーターを統合
]

'rest_framework.urls'というのは、rest_frameworkアプリケーション内にあるurlsという名前空間から、ルーティングをimportしてくることを指します。
もう少し直接的な表現をすると、urls.pyというファイルを探します。
そして、Djangoのルーティングの暗黙のルールとして、urls.pyの中にあるurlpatternsという配列型の変数をimportします。
from restframework.urls import urlpatternsみたいな感じですね。
実際に自分でurls.pyを作成していくと、感覚がつかめると思います。

では、python manage.py runserverを実施して、http://localhost:8000へアクセスしてみましょう。

image.png

URLが存在しませんというエラーが表示されました。
このアプリケーションでは、admin/^api-auth/(正規表現で^ついてます)しかルーティングされてないから、/にアクセスされても困るよって言われています。
これまでは、ルーティングを全くしていない状態だったため、実は本エラーページの代わりにDjangoのデフォルトページにリダイレクトされていたんですね。まだ開発始めてないよね?ようこそDjangoへ!的な流れです。

http://localhost:8000/api-auth/へアクセスしてみましょう。

image.png

またエラーページになりましたね。
実は、api-auth/にはルーティングは設定されておらず、api-auth/login/, api-auth/logout/(正規表現は省略)にのみ、ルーティングが設定されております。[name='login']は、このルーティングに対して、識別するための名前が設定されていることを表します。

http://localhost:8000/api-auth/login/にアクセスします。今度こそログイン画面が表示されるはずです。

image.png

ところでまだユーザが存在していないため、ログインできません。
一度Ctrl + cでサーバを停止して、コマンドからユーザを作成しましょう。

$ python manage.py createsuperuser

# エラーメッセージが表示されて...

...

django.db.utils.OperationalError: no such table: auth_user

アプリケーション内で作成された定義をデータベースに反映していないため、エラーが表示されます。
Django内でデフォルトで用意されているアプリケーションがあり、そのアプリケーションが必要とするテーブルが存在しないため、エラーとなっております。
migrateコマンドでデータベースを更新しましょう。

$ python manage.py migrate

Operations to perform:
  Apply all migrations: admin, auth, contenttypes, sessions
Running migrations:
  Applying contenttypes.0001_initial... OK
  Applying auth.0001_initial... OK
  Applying admin.0001_initial... OK
  Applying admin.0002_logentry_remove_auto_add... OK
  Applying admin.0003_logentry_add_action_flag_choices... OK
  Applying contenttypes.0002_remove_content_type_name... OK
  Applying auth.0002_alter_permission_name_max_length... OK
  Applying auth.0003_alter_user_email_max_length... OK
  Applying auth.0004_alter_user_username_opts... OK
  Applying auth.0005_alter_user_last_login_null... OK
  Applying auth.0006_require_contenttypes_0002... OK
  Applying auth.0007_alter_validators_add_error_messages... OK
  Applying auth.0008_alter_user_username_max_length... OK
  Applying auth.0009_alter_user_last_name_max_length... OK
  Applying auth.0010_alter_group_name_max_length... OK
  Applying auth.0011_update_proxy_permissions... OK
  Applying sessions.0001_initial... OK

もう一度、createsuperuserを実行してログインしましょう。

$ python manage.py createsuperuser

# Usernameとか聞かれるので適当に回答してください。

image.png

ログインには成功しているはずですが、ログイン成功時にリダイレクトする/accounts/profile/というURLが存在せず404エラーとなります。
ログインに成功すると、普通は何かしらのトップページへ遷移しますよね。その遷移先URLがデフォルトで/accounts/profile/となっています。
ただ、このURLに対してルーティングを設定していないので、404エラーが発生します。
このリダイレクト先URLは設定で任意のものに変更できます。

一旦、ここまででインストール自体は完了とします。

自作APIを作ってみる

それでは、実際にAPIを作っていきましょう。
tasksアプリケーション内に実装していきます。

<タスク>というリソースに対してアクセスするAPIを作っていきましょう。
/tasks/task/というURIで、<タスク>リソースにアクセスできるようにします。

URI メソッド 操作
/tasks/ GET タスクの一覧を取得する
/tasks/ POST タスクを新規作成する
/tasks/<id>/ GET 指定したタスクを取得する
/tasks/<id>/ PUT 指定したタスクの内容をすべて更新する
/tasks/<id>/ PATH 指定したタスクの内容を一部更新する
/tasks/<id>/ DELETE 指定したタスクを削除する

以下の手順で開発していきます。

  1. models.py (リソースを作成)
  2. serializers.py (JSON <-> Modelオブジェクト変換機能)
  3. views.py (URIへのアクセス時の処理を定義)
  4. urls.py (ルーティング)
  • models.py

タスクリソースを定義します。
現段階ではタスクとしての最低限の情報しか持たしていません。

models.py
from django.db import models


class Task(models.Model):
    class TaskStatus(models.IntegerChoices):
        ADDED = 10
        DOING = 20
        PENDING = 30
        DONE = 100

    title = models.CharField('Title', max_length=50)
    detail = models.TextField('Detail', max_length=500, null=True, blank=True)
    deadline = models.DateField('Deadline', null=True, blank=True)
    status = models.IntegerField('Status', choices=TaskStatus.choices, default=TaskStatus.ADDED)

    def __str__(self):
        return self.title

上記のモデル定義をデータベースへと反映させましょう。

# migrate関連のコマンドは、アプリケーション単位でも実行できます。
$ python manage.py makemigrations tasks
$ python manage.py migrate tasks
  • serializers.py

このファイルはstartappコマンドで自動生成されていないので、手動で作成してください。
なお、Serializerというのはどういうものか、簡単に触れておきます。
Djangoでは、ModelオブジェクトまたはQuerysetオブジェクトをORMを使用して操作します。
従来は、このModel(Queryset)オブジェクトをTemplateに渡してHTMLを動的生成して、HTTPレスポンスとして返却するというのが主な流れだったのですが、最近ではHTMLではなくJSONでデータの中身だけを返却することが増えました。
そこで、Model(Queryset)オブジェクトの中身を抜き出してJSONに変換(Serialize)するために、Serializerというものが開発されました1
このSerializerには、JSONからModel(Queryset)オブジェクトへ変換する、Desirializeという機能も含まれています。
HTTPリクエストをJSONで受け取り、DeserializeしてModel(Queryset)オブジェクトに変換、ORMで操作することができます。

当然、Serializerを作成しようと思うと、Serialize, Deserializeの設定を作成しないといけないのですが、Django REST Frameworkでは、モデル定義をもとにSerializerを自動生成することができます。

serializers.py
from rest_framework import serializers
from tasks.models import Task


class TaskSerializer(serializers.ModelSerializer):

    class Meta:
        model = Task
        fields = (
            "id", "title", "detail", "deadline", "status",
        )

ModelSerializerというクラスを継承して、Meta情報としてモデルおよび使用するフィールドを指定すると、Serializerができあがります。
このModelSerializerには、save()メソッドなど、Modelオブジェクトを操作するためのメソッドが既に用意されています。
つまり、Modelオブジェクトに変換してからORMで保存する、という処理が既に実装済みということです。

  • views.py

URIにアクセスした際の処理を実装していきます。

views.py
from rest_framework.viewsets import ModelViewSet
from tasks.models import Task
from tasks.serializers import TaskSerializer


class TaskViewSet(ModelViewSet):
    queryset = Task.objects.all()
    serializer_class = TaskSerializer

Serializerのときと同様に、ModelViewSetというクラスを継承して、いくつか追加情報を設定すると、Viewができあがります。
querysetというのは、このViewで取り扱う対象を、Querysetオブジェクトで指定しています。
serializer_classは、このViewで使用するSerializerを指定しています。先ほど作成したTaskSerializerを指定します。

Djangoには関数ViewとクラスベースViewという2種類が存在します。
基本的には、クラスベースViewを使用します。詳しくは以下の記事によくまとまっております。
参考記事: Djangoのクラスベースビューのas_viewて何なの?

  • urls.py

このファイルはstartappコマンドで自動生成されていないので、手動で作成してください。

urls.py
from rest_framework import routers
from tasks.views import TaskViewSet


router = routers.SimpleRouter()
router.register('', TaskViewSet)

urlpatterns = router.urls

ルーターに対して、ViewSetクラスを対応付けます。
内部的にどういうことが起こるかは、公式サイトを参考にしてください。
簡単にいうと、暗黙のルールに則って、URIとView関数が紐づけられます。

できあがったルーターを、アプリケーション全体のルーターに統合しましょう。

backend/urls.py
urlpatterns = [
    path('admin/', admin.site.urls),
    re_path(r'^api-auth/', include('rest_framework.urls')),
    re_path(r'^tasks/', include('tasks.urls')),    # <-- この行を追加してください。
]

さあ、これでAPIが実装できました!
http://localhost:8000/tasks/へアクセスしてみましょう。

image.png

データを入力して、POSTボタンを押してみましょう。

image.png
image.png

登録もできましたね :laughing:

http://localhost:8000/tasks/<id>/へアクセスすると、特定のタスクのデータのみを取得できることや、更新、削除ができることも確認してみてください。
※ PATCHメソッドを試したいときは、HTML Formではなく、Raw Dataから入力してください。

最後に

ちょっと長くなってきたので、ここで一回終了とします。

第四章後半では、設定ファイルの書き方、ロギングの使い方


  1. Djangoに限らない、広義のSerializerについては、こちらの記事をご参考ください。 

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

#docker sync start ( unison ) がエラーで動かない ( MacOS Catallina 10.15.3 )( Error getting mapped port, exit code 0 )

解決

gem uninstall docker-sync
gem install docker-sync
docker-sync clean

( rm ~/.docker-sync-global.yml )

docker-sync start

https://github.com/EugenMayer/docker-sync/issues/540#issuecomment-372992379

Error

$ docker-sync start
$ docker-sync start
          ok  Stopping sync container foo-bar-app
          ok  Starting unison for sync foo-bar-app
          ok  foo-bar-app container not running
          ok  starting foo-bar-app container
     command  docker start foo-bar-app && docker exec foo-bar-app supervisorctl restart unison
          ok  starting initial sync of foo-bar-app
     command  docker inspect --format='' foo-bar-app
     command  docker inspect --format='' foo-bar-app
       error  Error getting mapped port, exit code 0
     message  Template parsing error: template: :1:3: executing "" at <index (index .NetworkSettings.Ports "5000/tcp") 0>: error calling index: index of untyped nil
     command  unison -testserver /app_sync "socket://127.0.0.1:"

Original by Github issue

https://github.com/YumaInaura/YumaInaura/issues/3090

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

【自分用】4/30進捗

やったこと(後ほど追記します)

awsにdockerでデプロイ
EC2のElasticIPとインスタンスの作成・紐付け(ssh通信設定)

参考url
https://qiita.com/hagyyyy/items/959c115e0a5001972604
https://qiita.com/Atsushi_/items/f10a6790972528682d25

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

docker-compose 備忘録

コマンド

基本コマンド
$ docker-compose up <-d>
$ docker-compose down

デバッグ
$ docker exec -it <docker-compose name (work_app_1)> bash
$ docker-compose exec <service_name (app)> <command (go run main.go)>
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

BargeのDockerを最新化し、docker-composeを導入する

結論

Docker最新化の方法

$ sudo /etc/init.d/docker restart latest

docker-composeをインストールする方法

$ sudo wget "https://github.com/docker/compose/releases/download/1.25.5/docker-compose-$(uname -s)-$(uname -m)" -O /usr/local/bin/docker-compose
$ sudo chmod +x /usr/local/bin/docker-compose
$ sudo ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose

Bargeとは

image.png

Dockerホストに特化したLinuxディストリビューションです。

Dockerで軽量OSというとAlpine Linuxが思い浮かびますが、用途が全く異なります。BargeはDockerホスト向けで、Alpine LinuxはDockerイメージ向けです。

詳しい説明は公式や、分かりやすい記事に譲り、自分が使ってみた感想だけ書きます。

良いところ

  • 軽量で起動が高速
    最新のBarge2.13.0のisoイメージのサイズは 驚異の14MB
    UbuntuやCentOSのイメージファイルと比較すると、その軽量ぶりが際立ちます。
OS isoイメージファイルサイズ
CentOS 7.8 4.5GB
Ubuntu 20.04 2.5GB
Barge 2.13.0 14MB
  • 最初からDockerがインストールされている
    UbuntuやCentOSにはDockerがデフォルトで入っておらず、またOSのバージョンによってはインストール手順が複雑です。動作確認の前にインストールで躓いたことがある人も多いのではないでしょうか(経験談)。
    Bargeには最初からDockerが入っているため、起動すればすぐにDockerを使えます。また、Dockerのバージョンの切り替えも簡単なので、特定のバージョンをサクッと使いたいというニーズにも応えられます。

微妙なところ

  • パッケージ管理が独特
    Ubuntuでいうところのapt、CentOSでいうところのyumは、Bargeではpkgというコマンドです。
    FreeBSDのパッケージ管理コマンドもpkgという名前ですが、特に関係ないようです。
    他のディストリビューションでは難なく入れられる(またはデフォルトで入っている)パッケージが、簡単にインストールできないことがあります。(例:curl)

  • 最近更新されていない
    2020年4月時点で、最新のリリースバージョン2.14.0-rc2 で、リリース日は2019年8月です。
    それまでは少なくとも2月に1度以上はリリースされていたため、今後更新されない可能性もあります。

環境

Vagrant boxを利用して起動

[bargee@barge ~]$ cat /etc/os-release
NAME="Barge"
VERSION=2.13.0
ID=barge
ID_LIKE=busybox
VERSION_ID=2.13.0
PRETTY_NAME="Barge 2.13.0"
HOME_URL="https://github.com/bargees/barge-os"
BUG_REPORT_URL="https://github.com/bargees/barge-os/issues"

Dockerの最新化

Barge のデフォルトのDockerは古いです。CE版になる以前のもので、2016年頃のバージョンです。

[bargee@barge ~]$ docker --version
Docker version 1.10.3, build 20f81dd

アップデートが必要なので、以下のコマンドを打ちます。
ここで、/etc/init.d/は省略不可能です。通常のdockerコマンドとは異なることに注意してください。(通常のdockerコマンドがどこを指しているかは、which dockerで確認できます。)

[bargee@barge ~]$ sudo /etc/init.d/docker restart latest
docker[647]: Loading /etc/default/docker
WARNING: Possible Forward-Incompatibility of libnetwork
Once you upgrade Docker to v1.12 or newer, if you downgrade back,
you may need to initialize /var/lib/docker/network/files/local-kv.db.
Downloading v19.03.8 ...

少し時間がかかるので気長に待ちましょう。
Dockerデーモンの再起動が終わったら、バージョンが更新されていることを確認します。

[bargee@barge ~]$ docker --version
Docker version 19.03.8, build afacb8b7f0

latestとすると最新版のDockerに切り替わりますが、以下のように特定のバージョンを指定することもできます。

[bargee@barge ~]$ sudo /etc/init.d/docker restart v19.03.8

docker-composeのインストール

Barge にはdocker-composeはデフォルトで入っていません。公式サイトの手順 通りにインストールしようとすると、curlが無くて詰みます。sudo pkg install curlとしてもcurlはインストールできませんでした。
代わりにwgetがデフォルトで入っているため、wgetで代用します。2020年4月時点でのdocker-compose安定版の最新バージョン(1.25.5)をインストールします。

[bargee@barge ~]$ sudo wget "https://github.com/docker/compose/releases/download/1.25.5/docker-compose-$(uname -s)-$(uname -m)" -O /usr/local/bin/docker-compose
[bargee@barge ~]$ sudo chmod +x /usr/local/bin/docker-compose

curlのファイル保存先指定は-o
wgetのファイル保存先指定は-O
curl -Owget -oにはそれぞれ別の意味があって... ややこしいですね。
 
Bargeはusr/local/binにデフォルトではPATHが通っていないので、PATHが通っている/usr/binにシンボリックリンクを設定します。
バージョンを確認して、PATHを通せたことを確認します。

[bargee@barge ~]$ sudo ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose
[bargee@barge ~]$ docker-compose --version
docker-compose version 1.25.5, build 8a1c60f6

以上で、Barge に最新のDockerとdocker-composeをインストールできました。

参考

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

docker-composeでアプリサーバーコンテナからMySQLサーバーコンテナに接続する。

実行環境

Docker for Mac
Docker version 19.03.8

簡単な事だけどかなり手こずったのでまとめ。

Dockerfile

FROM ruby:2.7.1-buster

RUN gem install rails

# node.jsをインストール
RUN apt-get update && \
    apt-get install -y node.js

# yarnをインストール
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 rails webpacker:install
RUN yarn install --check-files

COPY Gemfile /Gemfile
COPY Gemfile.lock /Gemfile.lock

RUN bundle install

*開発環境には node.js、 yarn、 webpackerのインストールが必要なので先にインストールしておく。

docker-compose.yml

version: "3"
services:
  mysql:
    image: mysql:8.0.20
    command: --default-authentication-plugin=mysql_native_password
    environment:
      MYSQL_ROOT_PASSWORD: root
  app:
    build: .
    volumes:
      - ".:/app"
    ports:
      - "3000:3000"
    tty: true
    depends_on:
      - mysql
    working_dir: "/app"

これらを元にdocker-composeでコンテナを立ち上げていきます。

$ docker-compose up

立ち上がったらコンテナ内にアクセス。

$ docker exec -it (立ち上げたコンテナのNAMES) /bin/bash

mariadb-clientをインストール

$ apt install mariadb-client

mysqlにアクセス

$ mysql -u root -proot -h mysql

* docker-compose.yml参照
-u = ユーザーを指定
-p = 指定したユーザーのパスワード
-h = ホスト、mysqlコンテナの名前?
MySQL [(none)]>

*アクセス成功

一度ログアウトしconfig/database.ymlを編集します。
具体的にはdatabaseがsqliteになっていたのでmysql2に変更します。

# SQLite. Versions 3.8.0 and up are supported.
#   gem install sqlite3
#
#   Ensure the SQLite 3 gem is defined in your Gemfile
#   gem 'sqlite3'
#
default: &default
  adapter: mysql2
  encording: utf8
  username: root
  password: root
  host: mysql
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  timeout: 5000

development:
  <<: *default
  database: rails_development

# Warning: The database defined as "test" will be erased and
# re-generated from your development database when you run "rake".
# Do not set this db to the same as development or production.
test:
  <<: *default
  database: rails_test

production:
  <<: *default
  database: rails_production

Gemfileもsqliteからmysqlに変更

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

ruby '2.7.1'

gem 'rails', '~> 6.0.2', '>= 6.0.2.2'
# sqliteからmysql2へ
gem 'mysql2'
gem 'puma', '~> 4.1'
gem 'sass-rails', '>= 6'
gem 'webpacker', '~> 4.0'
gem 'turbolinks', '~> 5'
gem 'jbuilder', '~> 2.7'

~省略~
$ rake db:create
$ mysql -u root -proot -h mysql * もう一度mysqlに接続
$ show databases; * rake db:createが出来ているか確認
MySQL [(none)]> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| rails_development  |
| rails_test         |
| sys                |
+--------------------+
6 rows in set (0.011 sec)

一応ここまでやっとけば開発できると思う。完全に自分用で参考にならないかもしれません。
参考: Docker超入門 Part03 - 複数コンテナを動作させる

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