Raspberry Pi 4 に Ubuntu Desktop を入れる
Raspberry Pi 4 に Ubuntu Desktop を入れるときに苦労したので備忘録として書いておきます。
最近、Raspberry Pi に OSを入れるためのアプリケーションとして Raspberry Pi Imager が出てきて簡単に SD カードをフォーマットしたり OS をSD カードに入れられるようになりました。しかし、そんな中何故か Ubuntu が正常にインストールできないという状況に遭遇しました。ここでは発生したエラーとその対処法について書いていきます。
環境
- Raspberry Pi 4 Model B, RAM = 4 GB
- microSD card (Samsung, EVO Plus 128 GB)
microSD は Raspberry Pi で高速に動作するとの評判が多かった Samsung の microSD を使いました。
インストール手順
www.raspberrypi.org
上のリンクから Imager をダウンロードしてきて、OSを選択、Write をするだけです。手順はここのサイトにもありますが非常にシンプルでわかりやすいです。今回は Ubuntu Desktop 21.04, 64bit を選択しました
OSの書き込みは正常に終了したという通知が出て、microSD を Raspberry Pi に入れてさあ起動!と意気込みましたがそうは行きません。画面の下の方に 「Ubuntu」という文字(ロゴ?)が数秒出たあと消えます。その後はスクリーンには No signal という表示。。。その後は何分待ってもシグナルは復活しません。
状況の確認
これだと何が起きたのか全くわからないので Ubuntu Server 20.04LTS を microSD に入れ直してメッセージを見てみます。さっきと同じ手順で「Ubuntu Server 20.04LTS, 64bit」を選択してインストールします。Raspberry Pi で起動してみると先ほどと同様についたと思ったらすぐに「No signal」になります。消える直前に一瞬エラーメッセージが表示されていたのでそれをスマホで録画して確認してみると、
[FAILED] Failed to start Command from Kernel Command Line. See 'systemctl status kernel-command-line.service' for details. [DEPEND] Dependency failed for Command from Kernel Command Line (中略) [OK] Reached target Power-Off.
systemctl status kernel-command-line.service
で状況を確認しろって言うなら電源落とすな!と言いたくなりますが、このエラーでぐぐってみると、次のようなページが引っかかります。
bugs.launchpad.net
このタイムラインで#3の人がこのエラーについてコメントしています。どうやら Raspberry Pi 4 のバグのようですね。
どうやって解決するか
解決策について探してみると、下の方に
dd
使ってイメージを焼く- Raspberry Pi Imager の Advanced Option をリセットしてためす
という解決法が上げられていました。Advanced Option というのは OS を入れたときに SSH を有効にしておいたり、WiFiの情報を設定しておいたりできるRaspberry Pi Imager のオプションです。私はこれを設定しておいたのでもしかしてと思い、Raspberry Pi Imager を入れ直して試してみたところ正常に起動しました!
データベースの学習環境をDockerで作った話
データベースの勉強をするときに書いたクエリを実際に試すことはとても重要ですよね。しかし、私はこれまで書いたクエリを試すための環境構築が面倒で下のサイトを利用して SQL を実行していました。
www.db-fiddle.com
このサイトはとても便利なのですが、毎回 CREATE TABLE
から始めなければならなくてこれはこれで面倒なのと、やはりローカルに入れて勉強した方が深いところまで学ぶことができるのではないかと感じたので試してみることにしました。
Docker image の取得
PostgreSQL は DockerHub に公式イメージが公開されているのでそれを使います。バージョンに特にこだわりはないのと、最近 PostgreSQL の大規模なバク修正があったと聞いたので latest を使います。
$ docker pull postgres:latest
docker-compose.yaml の作成
毎回コンテナを起動するときにいろいろ指定するのは面倒なので全部 docker-compose.yaml に書いておきます。
version: '3' services: db: image: postgres container_name: db restart: always environment: POSTGRES_PASSWORD: your-password volumes: - ./data:/var/lib/postgresql/data - ./sql:/home
コンテナデータを永続化するためにホストのディレクトリ(./data
)をコンテナ内でデータベース情報の入るディレクトリ(/var/lib/postgresql/data
)にマウントします。また、SQLファイルを入れるディレクトリ(./sql
)もマウントしています。
構築した環境を使う
準備はできたのでコンテナを起動します。
$ docker-compose up -d $ docker-compose ps # コンテナが無事起動しているか確認 Name Command State Ports ------------------------------------------------------- db docker-entrypoint.sh postgres Up 5432/tcp
コンテナが無事に起動出来たら学習用のデータベースを作りましょう。
$ docker exec -it db /bin/bash root@92913b69db39:/# createdb -U postgres testdb root@92913b69db39:/# psql -U postgres -d testdb psql (13.1 (Debian 13.1-1.pgdg100+1)) Type "help" for help. testdb=# \q root@92913b69db39:/# exit $
今回は単に学習用なのでユーザーは postgres を使ってしまいます。
次に、このデータベースにテーブルを作成する SQL を実行します。
create_table.sql
CREATE TABLE testtable ( id INTEGER PRIMARY KEY, name VARCHAR(100) NOT NULL );
テーブルを作成するには
$ docker exec -it db psql -U postgres -d testdb -f /home/create_table.sql
を実行します。CREATE TABLE
という出力が出れば成功です。
最後に、SQL を実行するコマンドの効率化をしましょう。毎回実行するたびに上のコマンドを実行するのは面倒なのでシェルスクリプトを作ります。
runner.sh
#!/bin/bash docker exec -it db psql -U postgres -d testdb -f /home/$1
chmod +x runnser.sh
で実行権限を付与しておけば、
$ ./runner.sh create_table.sql
のように実行することができます。これで勉強がはかどりますね!
Python + Docker でデータ解析環境の管理
今回は実験データ自体の解析の話ではなく、どのような環境で解析を行ったのかという「環境」の管理について書きたいと思います。大学などで研究をしていて論文としてその成果をまとめる場合、関連するデータや解析プログラムは所属する大学もしくは研究室に整理して保存しておくことが一般的です。これはもちろん、論文に示されているデータがどのように解析されているのかあとから確認することができるようにしておくためです。
実験データの解析を例えば Python で行っている場合、解析の再現性を担保するためにはデータファイルのみならず解析を行っている環境(ライブラリのバージョンなど)も重要になってきます。そこで、この記事では Docker を使ってこの環境の管理をしたという話をします。
動作環境
- Windows10 Home
- WSL2 (Ubuntu 18.04)
Docker image の作成
FROM jupyter/base-notebook:python-3.8.6 COPY ./requirements.txt . RUN pip install -r requirements.txt EXPOSE 8888
base image としては jupyter notebook のイメージが Docker Hub にあるのでこれを使います。タグは latest
のものを使うと Docker Hub が更新されたときに中身が変わってしまうので python-3.8.6
を指定します。
解析に使うライブラリは requirements.txt
にバージョンを明記して書いておきます。
docker-compose.yaml の作成
実際にこのコンテナを運用するときには、ホストから Jupyter Notebook を編集するためにポートフォワーディングをしたり、解析データをマウントしたりします。これらはコンテナを起動するときに指定することは出来ますが、いちいち書くのは面倒なので docker-compose
を使います。
version: "3" services: my-jupyter: build: . image: my-jupyter:1.0.0 ports: - 8888:8888 volumes: - .:/home/jovyan/ command: jupyter lab
コンテナの実行
コンテナを動かすためには、
docker-compose up
を実行します。もしも Docker image がビルドされていないときにはビルドから開始します。http://localhost:8888
にアクセスしてJupyter Lab が起動すれば成功です。
GCP の Credential の読み込み
Google Cloud Platform では認証情報をJSON形式などでダウンロードすることで外部のアプリケーションから BigQuery などのサービスにアクセスすることができます。しかし、認証情報を読み込ませるのに苦労したので解決までのメモを残しておきます。
環境
- Ubuntu 20.04
- Go 1.13
どこで詰まったのか
ドキュメントにあるようにJSONまでのパスを環境変数にセットしました。
export GOOGLE_APPLICATION_CREDENTIALS=/home/.../credential.json
さあ、これでアクセスできると考えプログラムを走らせたところ
Response: { "error": "invalid_grant", "error_description": "Token has been expired or revoked." }
。。。いや、今発行したばかりなのに期限切れとか取り消されたとかなんだよ。試しに、.bashrc
にこの環境変数をセットして試してみましたがダメでした。echo $GOOGLE_APPLICATION_CREDENTIALS
で確認するとちゃんとセットした値が出てくるので環境変数には正しく設定されているんだよな。。。
どう解決したか
最終的に、これはプロジェクトのルートディレクトリに .env
を作ってそこに
GOOGLE_APPLICATION_CREDENTIALS=/home/.../credential.json
とセットし、main()
の中で.envファイルを読み込むとエラーがでなくなりました。なぜかはわかりません。
- 作者:クラウドエース株式会社 吉積 礼敏・他
- 発売日: 2019/04/19
- メディア: 単行本(ソフトカバー)
Django で Chart.js を使ってグラフを描く
Django は Python を使うことで簡単に Web アプリケーションを作ることができることから非常に人気のあるフレームワークです。しかし、高度なアニメーションなどは Python だけでは表現することが難しく、JavaScript の力を借りることが多い印象を受けます。この記事では、グラフ描画でよく用いられる Chart.js を Django のプロジェクト内で使う方法について紹介したいと思います。特に、Python 側で処理したデータを JavaScript に渡して、Chart.js で描画するという部分について書きます。
開発環境
Django と Chart.js を結ぶ django-chartjs
というライブラリがありますが、正直ここまでやらなくていいという感じを受けたので今回はこのライブラリ無しで実装を進めます。
準備
Django プロジェクトの準備はこれまでに何度か書いていますので、割愛します。詳しく知りたい方は以下の記事の「準備」までを参考にして作ってみてください。
pyhaya.hatenablog.com
Chart.js を使ってみる
ひとまず、Django の存在は最初にはできるだけ意識せず、HTML と JavaScript を使って適当なグラフを作ってみます。
{% load static %} <html lang="ja"> <header> <meta charset="utf-8"> <script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.2/Chart.bundle.js"></script> <script type="text/javascript" src="{% static 'visualize/js/draw.js' %}" defer></script> </header> <body> <div style="width: 50%; heigh: 50%;"> <canvas id="graph"></canvas> </div> </body> </html>
グラフだけを表示する HTML です。JavaScript のコードは
var ctx = document.getElementById("graph"); var myLineChart = new Chart(ctx, { type: "line", data: { labels: [ "Jan.", "Feb.", "Mar.", "Apr.", "May", "Jun.", "Jul.", "Aug.", "Sep.", "Oct.", "Nov.", "Dec." ], datasets: [ { data: [1, 2, 4, 9, 16, 25, 36, 49, 64, 81, 100, 121], borderColor: "salmon", backgroundColor: "rgba(0,0,0,0)", }, ], }, options: { title: { display: false, }, scales: { yAxes: [ { ticks: { suggestedMax: 130, suggestedMin: 0, stepSize: 10, callback: function (value, index, values) { return value; }, }, }, ], }, }, });
x軸を labels
に書いて、y軸の値を dataset
に書いて、それ以外の軸の設定を options
に書くというチュートリアルに載っていそうなコードです。
Django からデータを渡してグラフを描画する
では次に、Django からデータをもらってグラフを書くということをしてみます。これはユーザーがなにか操作をしてグラフを更新するような状況で、グラフにプロットされている値が不変でない状況です。
Django では HTML には context
という形でデータを渡すことができます。そのため、JavaScript のコードを HTML 内の script タグの中に全部入れるという解決策がまず思い浮かびます。しかし、JavaScript のコードが長くなると、ソースコードがごちゃごちゃになってしまいます。そこで、データを JavaScript の変数に受け渡すところだけ script タグ内に入れるようにすればコードはスッキリします。
{% load static %} <html lang="ja"> <header> <meta charset="utf-8"> <script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.2/Chart.bundle.js"></script> <script type="text/javascript" src="{% static 'visualize/js/draw.js' %}" defer></script> <script type="text/javascript">var data = {{ data }}</script> </header> <body> <div style="width: 50%; heigh: 50%;"> <canvas id="graph"></canvas> </div> </body> </html>
var data = {{ data }}
で Django から JavaScript へデータの受け渡しが行われます。
var ctx = document.getElementById("graph"); var myLineChart = new Chart(ctx, { type: "line", data: { labels: [ "Jan.", "Feb.", "Mar.", "Apr.", "May", "Jun.", "Jul.", "Aug.", "Sep.", "Oct.", "Nov.", "Dec." ], datasets: [ { data: data, // ここで data を使っている borderColor: "salmon", backgroundColor: "rgba(0,0,0,0)", }, ], }, options: { title: { display: false, }, scales: { yAxes: [ { ticks: { suggestedMax: 130, suggestedMin: 0, stepSize: 10, callback: function (value, index, values) { return value; }, }, }, ], }, }, });
肝心の Python コードは
views.py
from django.shortcuts import render from django.views import View # Create your views here. class Index(View): def get(self, request): context = {"data": [i*i for i in range(12)]} return render(request, "visualize/index.html", context)
です。これで画面をリフレッシュすると先程のグラフと同様のグラフが表示されます。
GraphQL から GitHub API を叩く
最近、GitHub API を使ってリポジトリ情報を取ることが多いのですが、REST API より GraphQL を使ったほうが必要な情報だけを一気に取れるということを耳にし、試してみることにしました。
Query を作成
GraphQL はクエリの書き方が特殊なので REST API を普段使う人にとっては最初とっつきにくい感じを受けます。最初に指定できるのは Query という分類をされているもので、GitHub API のドキュメントに一覧が載っています。
この中から欲しい情報を選ぶのが最初のステップです。ここでは user
にしてみましょう。上のリンクの一番下に user
の説明があります。変数として login
のみを指定できるようですね。これは String 型です。試しに私の情報を取ってみます。
{ user(login: "Hayashi-Yudai") }
指定できました。これだけでは実行しても指定した情報が足りずエラーが出てしまいますので情報を追加していきます。user
は User
型を返すと書いてある(user の横に青文字で User と書いてある)のでリンクをクリックして User 型がどのような情報をもっているのか見てみます。
commitComments
や followers
など色々あります。issues
というのがあるのでこれを取ってみましょう。引数として first
が取れるのでこれを10にして最初の10個を取ってみます。
{ user(login: "Hayashi-Yudai") { issues(first: 10) } }
issues
の中には edges
やnodes
などが入っています。こんな調子でどんどん深くまでたどっていくと、
{ user(login: "Hayashi-Yudai") { issues(first: 10) { nodes { body } } } }
というクエリを書くことができます。一番深いところにある body
は型が String なのでこれ以上深いところには何もありません。
Query を実行する
では最後に上で作成したクエリを Python で実行してみます。
import requests import os headers = {"Authorization": f"Bearer {os.environ.get('GITHUB_TOKEN')}"} def run(query): request = requests.post( 'https://api.github.com/graphql', json={'query': query}, headers=headers ) if request.status_code == 200: return request.json() else: raise Exception(f"Query failed to run by returning code of {request.status_code}. {query}") query = """ { user(login: "Hayashi-Yudai") { issues(first: 10) { nodes { body } } } } """ result = run(query)
GitHub Access Token は私の場合には環境変数に指定しているので os.environ.get
で取ってきてヘッダに入れています。result の中身を見てみると
{'data': { 'user': { 'issues': { 'nodes': [ {'body': 'When I run some codes, text garbling happened. I tried hello world program by\r\n\r\n* Python3\r\n* Java\r\n* C\r\n* C++ \r\n\r\nbut, only in C and C++, text garbling happened.'}, {'body': 'In HTML, the width of `th` component is assigned like "5%", but it does not reflected in the page.'}, {'body': 'add regularization and batch-normalization to transposed convolution layer'}, {'body': 'add the pooling layer'}, {'body': 'create UNet by components I prepared.'}, {'body': 'To training UNet model, I should change input images to one-hot representation'}, {'body': 'create training session'}, {'body': 'To evaluate the model, I should set metrics such that accuracy or loss'}, {'body': 'show training result as images'}, {'body': 'To split training and validation of the model, I should give the is_training parameter when running the session.'} ] } } }}
これを見てみると user -> issues -> nodes -> body という構造になっていてクエリで書いた構造と同じ構造でレスポンスが返ってきていることが分かります。
Django でログインページを作る
Python のフルスタックWebフレームワークとして有名な Django を使ってログインページを簡単に作ってみます。
準備
雛形の生成
Django のプロジェクトをセットアップします。
$ mkdir myapp $ cd myapp $ django-admin startproject config . $ python manage.py startapp myauth $ python manage.py startapp appmain $ python manage.py makemigrations $ python manage.py migrate $ python manage.py createsuperuser # ユーザー名とパスワードを設定する
これらを実行することで以下のようなディレクトリ構成が得られます。
$ tree -L 2 . ├── appmain # 内部ページ │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── migrations │ ├── models.py │ ├── tests.py │ ├── views.py ├── config # プロジェクト全体の設定 │ ├── __init__.py │ ├── asgi.py │ ├── settings.py │ ├── urls.py │ └── wsgi.py ├── db.sqlite3 ├── manage.py └── myauth # 今回ログインページをつくるところ ├── __init__.py ├── admin.py ├── apps.py ├── migrations ├── models.py ├── tests.py └── views.py
基本的な設定
config/settings.py
# ... LOGIN_REDIRECT_URL = "/" # ログイン後にどこに飛ばすか LOGIN_URL = "/myauth" # ログインページの URLを設定 # ... INSTALLED_APPS = [ "myauth.apps.MyauthConfig", # <- 追加 "appmain.apps.AppmainConfig", # <- 追加 "django.contrib.admin", "django.contrib.auth", "django.contrib.contenttypes", "django.contrib.sessions", "django.contrib.messages", "django.contrib.staticfiles", ] # ...
config/urls.py
from django.contrib import admin from django.urls import path, include urlpatterns = [ path("admin/", admin.site.urls), path("", include("appmain.urls")), path("myauth/", include("myauth.urls")), ]
appmain/urls.py
from django.urls import path from appmain import views urlpatterns = [path("", views.index, name="index")]
myauth/urls.py
from django.urls import path from myauth import views app_name = "myauth" urlpatterns = [path("", views.Login.as_view(), name="login")]
内部ページの実装
ログインしたあとに飛ぶページを作ります。これは今回の主題ではないので簡単に作ります。上の appmain/urls.py に設定したように、ルートページとして views.index
を表示するようにしているので、これを作ります。
appmain/views.py
from django.shortcuts import render from django.contrib.auth.decorators import login_required @login_required def index(request): return render(request, "appmain/index.html")
呼ばれたら index.html を表示するだけです。シンプルですね。
appmain/templates/appmain/index.html
<html> <head></head> <body>This is inner page</body> </html>
Django では HTML ファイルは慣習的に (app名)/templates/(app名)/ ディレクトリに入れるということになっていますので少しパス名が長くなります。
ログインページの実装
まずはログイン情報を入力するフォームを作ります。
myauth/forms.py
from django.contrib.auth.forms import AuthenticationForm class LoginForm(AuthenticationForm): def __init__(self, *args, **kwargs): super(LoginForm, self).__init__(*args, **kwargs)
Django には認証用のフォームを作るためのクラスが用意されているのでそれを継承すれば良いです。このフォームをビューに組み込みます。
myauth/views.py
from django.contrib.auth.views import LoginView from myauth.forms import LoginForm class Login(LoginView): form_class = LoginForm template_name = "myauth/index.html"
これでログインページの実装は9割方終わりです。あとは HTML ファイルを作ります。
myauth/templates/myauth/index.html
<html> <head> <title>Login Page</title> </head> <body> <h2>Login</h2> <form method="post" action="{% url 'myauth:login' %}"> {% csrf_token %} {{ form.username }} {{ form.password }} {% if form.errors %} <p>ユーザー名またはパスワードが一致しません</p> {% endif %} <input type="submit" value="login"/> </form> </body> </html>
ビューの中でフォームを取り込んでいるので、HTML内では LoginForm
の持つ変数を使うことができます。この記事では明示的にフォーム内で変数を定義することはしていませんが、AuthenticationForm
が username
とpassword
を定義しているのでそれを HTML 内で {{ form.username }}
のようにして使うことができます。
フォームのアクションとして myauth:login
を指定しています。これは「myauth」という名前空間内の「login」というビューにフォームの内容を送信するということを指定しています。「myauth」の urls.py 内を見直してみると
path("", views.Login.as_view(), name="login")
という記述があります。myauth:login
の login
は、この name="login"
を指しています。
実行してみる
では、今まで実装してきたコードを試してみます。
$ python manage.py runserver
として http://localhost:8000/myauth
にアクセスしてみます。
CSS は何も書いていないので非常にシンプルですがきちんとログインページが出来ています。一番最初に管理者としてユーザーを1人登録していますのでそのユーザーのユーザー名とパスワードを入力すれば内部ページに飛べます。間違っていれば ユーザー名またはパスワードが一致しません
という文字が表示されるはずです。