- 投稿日:2020-02-11T23:29:23+09:00
dockerでCentOSのイメージを使ってみる
環境情報
- OS: Linux (centos 7.7.1908)
- docker: 19.03.5
手順
centos7のイメージを取得
docker pull centos:7実行結果
[root@centos-sample aky100200]# docker pull centos:7 7: Pulling from library/centos ab5ef0e58194: Pull complete Digest: sha256:4a701376d03f6b39b8c2a8f4a8e499441b0d567f9ab9d58e4991de4472fb813c Status: Downloaded newer image for centos:7 docker.io/library/centos:7 [root@centos-sample aky100200]#イメージの確認
docker images実行結果
[root@centos-sample aky100200]# docker images REPOSITORY TAG IMAGE ID CREATED SIZE centos 7 5e35e350aded 3 months ago 203MB hello-world latest fce289e99eb9 13 months ago 1.84kB [root@centos-sample aky100200]#起動
docker run -it -d --name centos7 centos:7実行結果
[root@centos-sample aky100200]# docker run -it -d --name centos7 centos:7 fdc5ffc971420c60228330c1b9f7b4030878836e74f5590614774e8d35518694 [root@centos-sample aky100200]#プロセスの起動を確認
docker ps実行結果
[root@centos-sample aky100200]# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES fdc5ffc97142 centos:7 "/bin/bash" About a minute ago Up About a minute centos7 [root@centos-sample aky100200]#コンテナ内コマンドの実行
docker exec -it centos7 /bin/bash実行結果
[root@centos-sample aky100200]# docker exec -it centos7 /bin/bash [root@fdc5ffc97142 /]#まとめ
イメージの作成ができた。
参考
- 投稿日:2020-02-11T23:20:36+09:00
Docker概要
Dockerについて
コンテナ型の仮想環境を提供するソフトウェア。
主要な3つの機能
Dockerイメージを作る機能(build)
DockerFileというファイルに、インフラ情報(ライブラリや、ミドルウェアのインストールが記述されたファイル)を1つにまとめて、Dockerイメージを作ることができる。
もしくは、Dockerコマンドを使って、手動で作ることも可能。Dockerイメージを共有する機能(ship)
Dockerのイメージは、Dockerレジストリで共有することができる。
Docker Hubと呼ばれる、公式レジストリがある。
DockerHubには、Nginxや、ruby,pythonなどの公式イメージが置いてある。それらをコマンド1つでローカルPCのDocker上に構築することができる。
自作したイメージも、自由にDocker Hubに公開することができる。Dockerコンテナを動かす機能(run)
Dockerイメージを元にして、コンテナを作成することができる。
コンテナの起動/停止/削除は、Dockerコマンドを使うことによって可能となる。メリット:
- Dockerをインストールしている環境であれば、どこでも動くので、「開発、テスト環境では動いていたが、本番環境で動かない」というリスクを減らすことができる。
- 動作がとても軽い
- 自分自身も簡単に環境構築できるし、Dockerイメージを共有することにより、複数人での開発環境を簡単に揃えることができる。
- 投稿日:2020-02-11T23:03:11+09:00
【 Docker+Nginx+Django+RDS】WEBアプリができるまで⑦ユーザーごとのデータ登録できるようにする〜削除編
前置き
独学で、子供の成長アプリを作った時のことを、記録として残していきます。
間違っているところなどあれば、ご連絡お願いします。
①Djangoのようこそページへたどり着くまで
②NginxでDjangoのようこそページへたどり着くまで
③カスタムユーザーを作ってadminにたどり着く
④ログインログアウトをしよう
⑤ユーザー登録(サインイン)機能を作ろう
⑥ユーザーごとのデータ登録できるようにする〜CRU編
⑦ユーザーごとのデータ登録できるようにする〜削除編<--今ここGoal
ユーザーごとのデータを登録できるようにする。残りのDの部分
削除するだけなら簡単なのですが、、、View
kids_profile_delete関数を追加。
IDを受け取って、delete()するだけ。簡単。views.pyfrom django.contrib.auth import login as dj_login from .forms import SignUpForm, LoginForm, KidsProfileForm from django.shortcuts import render, redirect from django.contrib.auth.views import LoginView, LogoutView from django.contrib.auth.decorators import login_required from .models import KidsProfile # サインアップ画面 def signup(request): (略) #ログインページ class login_mypage(LoginView): (略) #ログアウトページ class logout(LogoutView): (略) #マイページページ @login_required def mypage(request): (略) #子供情報追加 @login_required def kids_profile_add(request): (略) #子供情報編集 @login_required def kids_profile_edit(request, kidsProfileId): (略) #子供情報削除 @login_required def kids_profile_delete(request, kidsProfileId): KidsProfile.objects.get(pk=kidsProfileId).delete() return redirect('users:mypage')HTML
削除前に「削除してもいいですか?」と確認するポップアップを実装。
ポップアップはbootstrapのmodalで実現。
modalを表示するのは難しくないが、「一覧上にある削除ボタンが持っているID情報」を
modal内の削除ボタンに引き継ぐのが難しい。modalを起動させるbuttonにdata-**形式(今回はdata-pkとdata-url)で持ち回らせ、
modal側はjsのonclickイベントにて受け取る形で実装。このjsはココでしか使わないので、blockコメント化。
extra_jsという名前でbase.htmlで待ち受けておく。mypage.html{% extends 'base.html' %} {% block extra_js %} <script> $(function() { $('.del_confirm').on('click', function () { $("#del_pk").text($(this).data("pk")); $('#del_url').attr('href', $(this).data("url")); $(this).attr('href', href); }); }); </script> {% endblock extra_js %} {% block content %} <div class="col-md-12 col-lg-5"> <h2>My Page</h2> <br> <br> <table class = "table"> <tr> <td>ユーザー名</td> <td>{{ user }}</td> </tr> </table> <table class = "table table-hover"> <thead class = "thead-dark"> <tr> <th>名前</th> <th>性別</th> <th>誕生日</th> <th></th> <th></th> </tr> </thead> {% for kids_profile in kidsProfiles %} <tr> <td>{{ kids_profile.name }}</td> <td>{{ kids_profile.get_gender_display }}</td> <td>{{ kids_profile.birthday }}</td> <td><a href="{% url 'users:kids_profile_edit' kids_profile.id %}" class="btn btn-success btn-sm">編集</a></td> <td><button type="button" class="btn btn-danger btn-sm del_confirm" data-toggle="modal" data-target="#delete_modal" data-pk="{{ kids_profile.pk }}" data-url="{% url 'users:kids_profile_delete' kids_profile.pk %}">削除</button></td> </tr> {% endfor%} <tr> <td></td> <td></td> <td></td> <td></td> <td><a href="{% url 'users:kids_profile_add' %}" class="btn btn-primary btn-sm" role="button">追加</a></td> </tr> </table> <div class="modal fade" id="delete_modal" tabindex="-1" role="dialog" aria-labelledby="label1" aria-hidden="true"> <div class="modal-dialog modal-dialog-centered"> <div class="modal-content"> <div class="modal-body"> 削除しても良いですか? </div> <div class="modal-footer"> <button type="button" class="btn btn-secondary" data-dismiss="modal">キャンセル</button> <a href="#" id="del_url" class="btn btn-danger">削除</a> </div> </div> </div> </div> </div> {% endblock content %}base.html{% load static %} {% bootstrap_css %} {% bootstrap_javascript jquery='full' %} <html> <head> <title>kids Growth</title> <link rel="stylesheet" href="{% static 'css/kidsGrowth.css' %}"> {% block extra_js %}{% endblock %} </head> <body> <!-- Navigation --> <nav class="navbar navbar-expand-sm navbar-dark bg-dark mt-3 mb-3"> <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNav4" aria-controls="navbarNav4" aria-expanded="false" aria-label="Toggle navigation"> <span class="sr-only">メニュー</span> <span class="navbar-toggler-icon"></span> </button> <a class="navbar-brand" href="">Kids Growth</a> <div class="collapse navbar-collapse" id="navbarNav4"> <ul class="navbar-nav"> <li class="nav-item"> <a class="nav-link" href="">メニュー1</a> </li> <li class="nav-item"> <a class="nav-link" href="">メニュー2</a> </li> <li class="nav-item"> <a class="nav-link" href="">メニュー3</a> </li> <li class="nav-item"> <a class="nav-link" href="{% url 'users:mypage'%}">マイページ</a> </li> {% if user.is_authenticated %} <li class="nav-item"> <a class="nav-link" href="{% url 'users:logout'%}">ログアウト</a> </li> {% else %} <li class="nav-item"> <a class="nav-link" href="{% url 'users:login'%}">ログイン</a> </li> {% endif %} </ul> </div> </nav> <div class="content container"> {% block content %} {% endblock %} </div> </body> </html>動かすよ〜
2つ目のレコードを消します。
modalはポワワーンと出ます。bootstrapの機能です。
(これを消すよ、と書いてあげてもよかったかも。その場合は、引き渡し項目を増やす必要あり。)
modal内の「削除」を押すと、データを消してからmypageにリダイレクトします。
参考
- 投稿日:2020-02-11T22:54:03+09:00
dockerでHelloWorldする
dockerでHelloWorldする
環境情報
- OS: Linux (centos 7.7.1908)
- docker: 19.03.5
手順
イメージの一覧を確認する
docker imagesまだ何もないことを確認
実行結果[root@centos-sample aky100200]# docker images REPOSITORY TAG IMAGE ID CREATED SIZE [root@centos-sample aky100200]#Hello-Worldの準備
hello-worldイメージをpullする
docker pull hello-world実行結果
[root@centos-sample aky100200]# docker pull hello-world Using default tag: latest latest: Pulling from library/hello-world 1b930d010525: Pull complete Digest: sha256:9572f7cdcee8591948c2963463447a53466950b3fc15a247fcad1917ca215a2f Status: Downloaded newer image for hello-world:latest docker.io/library/hello-world:latest [root@centos-sample aky100200]#イメージが取得できているか確認
docker imageshello-worldが追加されている
[root@centos-sample aky100200]# docker images REPOSITORY TAG IMAGE ID CREATED SIZE hello-world latest fce289e99eb9 13 months ago 1.84kB [root@centos-sample aky100200]#Hello-World
実行
docker run hello-world実行結果
[root@centos-sample aky100200]# 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/ [root@centos-sample aky100200]#「Hello from Docker!」が出力されている。
まとめ
次はlinuxイメージを作りたい。
参考
- 投稿日:2020-02-11T22:27:41+09:00
【 Docker+Nginx+Django+RDS】WEBアプリができるまで⑥
前置き
独学で、子供の成長アプリを作った時のことを、記録として残していきます。
間違っているところなどあれば、ご連絡お願いします。
①Djangoのようこそページへたどり着くまで
②NginxでDjangoのようこそページへたどり着くまで
③カスタムユーザーを作ってadminにたどり着く
④ログインログアウトをしよう
⑤ユーザーごとのデータ登録できるようにする〜CRU編Goal
ユーザーごとのデータを登録できるようにする。CRUDのうちCRUまでいきます。
作るのは、ユーザーごとに子供の情報を登録できるようにします。Model
名前、性別に加えて、あとで身長体重の成長曲線が書けるよう、生年月日を持ちます。
Modelを直したら、マイグレーションを実施。(コマンドは省略)models.pyfrom django.db import models from django.core.mail import send_mail from django.contrib.auth.base_user import BaseUserManager from django.contrib.auth.models import PermissionsMixin from django.contrib.auth.base_user import AbstractBaseUser from django.utils.translation import ugettext_lazy as _ from django.conf import settings from django.utils import timezone class UserManager(BaseUserManager): (略) class User(AbstractBaseUser, PermissionsMixin): (略) class KidsProfile(models.Model): GENDER_CHOICES = ( ('1', '女性'), ('2', '男性'), ) user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE) name = models.CharField(max_length=20, blank=False) gender = gender = models.CharField(max_length=2, choices=GENDER_CHOICES, blank=False) birthday = models.DateField(null=False, blank=False) def __str__(self): title = self.name return titleモデルを追加したら、adminで操作できるようにしましょう。
admin.pyfrom django.contrib import admin from django.contrib.auth.admin import UserAdmin from django.contrib.auth.forms import UserChangeForm, UserCreationForm from django.utils.translation import ugettext_lazy as _ from .models import User, KidsProfile #モデル追加 class MyUserChangeForm(UserChangeForm): (略) class MyUserCreationForm(UserCreationForm): (略) class MyUserAdmin(UserAdmin): (略) admin.site.register(User, MyUserAdmin) admin.site.register(KidsProfile) #追加Form
pythonのDateFieldはyyyy-mm-dd形式なので、入力時のバリデーションが面倒。
GUI的に入力しやすくするため、datetimepickerを入れます。このフォームを入力するのはログインしてからなので、
Modelでは保有しているuser情報はFormでは見せないようにしてます。
(user情報はviewの中でシステム的に持ち回る)forms.pyfrom django import forms from django.contrib.auth import get_user_model from django.contrib.auth.forms import AuthenticationForm from django.contrib.auth.forms import UserCreationForm from .models import KidsProfile import bootstrap_datepicker_plus as datetimepicker User = get_user_model() class SignUpForm(UserCreationForm): (略) class LoginForm(AuthenticationForm): (略) class KidsProfileForm(forms.ModelForm): class Meta: model = KidsProfile fields = ('name', 'gender', 'birthday') widgets = { 'birthday': datetimepicker.DatePickerInput( format='%Y-%m-%d'), } def __init__(self, *args, **kwargs): user = kwargs.pop('user', '') super(KidsProfileForm, self).__init__(*args, **kwargs)requirement.txtDjango==2.2.2 psycopg2==2.8.4 uwsgi==2.0.17 django-bootstrap4==1.1.1 django-bootstrap-datepicker-plus==3.0.5 #追加setting.pyINSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'users.apps.UsersConfig', 'bootstrap4', 'bootstrap_datepicker_plus', #追加 ]View
kids_profile_add関数では、POSTで受け取ったformに加えて
request.userで取得したユーザー情報を付与して保存してます。kids_profile_edit関数では、それに加えて変更したい情報を事前に表示しておきたいので、
データのIDをURL経由で受け取ります。users/views.py#子供情報追加 @login_required def kids_profile_add(request): user_name = request.user if request.method == 'POST': form = KidsProfileForm(request.POST, user = user_name) if form.is_valid(): data = form.save(commit=False) data.user = user_name data.save() return redirect('users:mypage') else: form = KidsProfileForm(user = user_name) return render(request, 'users/kids_profile_add.html', {'form': form}) #子供情報編集 @login_required def kids_profile_edit(request, kidsProfileId): kidsProfileData = KidsProfile.objects.get(pk=kidsProfileId) if request.method == 'POST': form = KidsProfileForm(request.POST) if form.is_valid(): data = form.save(commit=False) data.id = kidsProfileData.id data.user = request.user data.save() return redirect('users:mypage') else: form = KidsProfileForm( { 'name' : kidsProfileData.name , 'gender' : kidsProfileData.gender , 'birthday' : kidsProfileData.birthday }, ) return render(request, 'users/kids_profile_edit.html', {'form': form})HTML
_addも_editも、テンプレートはほぼ一緒。
{{ form.media }}を書かないと、datetimepickerが動かないので書く。kids_profile_add.html{% extends 'base.html' %} {% block content %} <!--日付入力フォーマット用--> {{ form.media }} <div class="col-md-12 col-lg-5"> <h2>New data</h2> <form method="POST" class="post-form">{% csrf_token %} {% bootstrap_form form %} <button type="submit" class="save btn btn-primary">Save</button> </form> </div> {% endblock %}kids_profile_edit.html{% extends 'base.html' %} {% block content %} <!--日付入力フォーマット用--> {{ form.media }} <div class="col-md-12 col-lg-5"> <h2>Data Edit</h2> <form method="POST" class="post-form">{% csrf_token %} {% bootstrap_form form %} <button type="submit" class="save btn btn-primary">Save</button> </form> </div> {% endblock %}ユーザーに紐づく子供の情報をTable形式で表示します。
子供の情報は複数人分が登録されるので、for文でレコード分だけ表示します。mypage.html{% extends 'base.html' %} {% block content %} <div class="col-md-12 col-lg-5"> <h2>My Page</h2> <br> <br> <table class = "table"> <tr> <td>ユーザー名</td> <td>{{ user }}</td> </tr> </table> <table class = "table table-hover"> <thead class = "thead-dark"> <tr> <th>名前</th> <th>性別</th> <th>誕生日</th> <th></th> <th></th> </tr> </thead> {% for kids_profile in kidsProfiles %} <tr> <td>{{ kids_profile.name }}</td> <td>{{ kids_profile.get_gender_display }}</td> <td>{{ kids_profile.birthday }}</td> <td><a href="{% url 'users:kids_profile_edit' kids_profile.id %}" class="btn btn-success btn-sm">編集</a></td> <td><button type="button" class="btn btn-danger btn-sm del_confirm">削除</button></td> </tr> {% endfor%} <tr> <td></td> <td></td> <td></td> <td></td> <td><a href="{% url 'users:kids_profile_add' %}" class="btn btn-primary btn-sm" role="button">追加</a></td> </tr> </table> </div> {% endblock content %}urls
新しいページを作ったので、URLのパスを通す。
kidsProfileIdをurlsで指定することで、URLを使ってHTMLとPythonで変数を受け渡す。urls.pyapp_name = 'users' urlpatterns = [ path('', views.mypage, name='mypage'), path('signup/', views.signup, name='signup'), path('mypage/', views.mypage, name='mypage'), path('login/', views.login_mypage.as_view(), name='login'), path('logout/', views.logout.as_view(), name='logout'), path('kids_profile_add/', views.kids_profile_add, name='kids_profile_add'), #追加 path('kids_profile_edit/<kidsProfileId>', views.kids_profile_edit, name='kids_profile_edit'),#追加 ]動かす!
登録がない状態。
登録画面。datetimepickerだと、こんな感じで表示される。
ちなみにスマホでやると、ちょっとやり辛い。解消方法はあるようだが、試してはいない。
編集すると、こんな感じ。各項目、元々の情報が入った状態で、変更可能なフォームが表示される。
登録されると、こんな感じ。この時点では削除ボタンはダミー。
- 投稿日:2020-02-11T22:27:41+09:00
【 Docker+Nginx+Django+RDS】WEBアプリができるまで⑥ユーザーごとのデータ登録できるようにする〜CRU編
前置き
独学で、子供の成長アプリを作った時のことを、記録として残していきます。
間違っているところなどあれば、ご連絡お願いします。
①Djangoのようこそページへたどり着くまで
②NginxでDjangoのようこそページへたどり着くまで
③カスタムユーザーを作ってadminにたどり着く
④ログインログアウトをしよう
⑤ユーザー登録(サインイン)機能を作ろう
⑥ユーザーごとのデータ登録できるようにする〜CRU編<--今ここ
⑦ユーザーごとのデータ登録できるようにする〜削除編Goal
ユーザーごとのデータを登録できるようにする。CRUDのうちCRUまでいきます。
作るのは、ユーザーごとに子供の情報を登録できるようにします。Model
名前、性別に加えて、あとで身長体重の成長曲線が書けるよう、生年月日を持ちます。
Modelを直したら、マイグレーションを実施。(コマンドは省略)models.pyfrom django.db import models from django.core.mail import send_mail from django.contrib.auth.base_user import BaseUserManager from django.contrib.auth.models import PermissionsMixin from django.contrib.auth.base_user import AbstractBaseUser from django.utils.translation import ugettext_lazy as _ from django.conf import settings from django.utils import timezone class UserManager(BaseUserManager): (略) class User(AbstractBaseUser, PermissionsMixin): (略) class KidsProfile(models.Model): GENDER_CHOICES = ( ('1', '女性'), ('2', '男性'), ) user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE) name = models.CharField(max_length=20, blank=False) gender = gender = models.CharField(max_length=2, choices=GENDER_CHOICES, blank=False) birthday = models.DateField(null=False, blank=False) def __str__(self): title = self.name return titleモデルを追加したら、adminで操作できるようにしましょう。
admin.pyfrom django.contrib import admin from django.contrib.auth.admin import UserAdmin from django.contrib.auth.forms import UserChangeForm, UserCreationForm from django.utils.translation import ugettext_lazy as _ from .models import User, KidsProfile #モデル追加 class MyUserChangeForm(UserChangeForm): (略) class MyUserCreationForm(UserCreationForm): (略) class MyUserAdmin(UserAdmin): (略) admin.site.register(User, MyUserAdmin) admin.site.register(KidsProfile) #追加Form
pythonのDateFieldはyyyy-mm-dd形式なので、入力時のバリデーションが面倒。
GUI的に入力しやすくするため、datetimepickerを入れます。このフォームを入力するのはログインしてからなので、
Modelでは保有しているuser情報はFormでは見せないようにしてます。
(user情報はviewの中でシステム的に持ち回る)forms.pyfrom django import forms from django.contrib.auth import get_user_model from django.contrib.auth.forms import AuthenticationForm from django.contrib.auth.forms import UserCreationForm from .models import KidsProfile import bootstrap_datepicker_plus as datetimepicker User = get_user_model() class SignUpForm(UserCreationForm): (略) class LoginForm(AuthenticationForm): (略) class KidsProfileForm(forms.ModelForm): class Meta: model = KidsProfile fields = ('name', 'gender', 'birthday') widgets = { 'birthday': datetimepicker.DatePickerInput( format='%Y-%m-%d'), } def __init__(self, *args, **kwargs): user = kwargs.pop('user', '') super(KidsProfileForm, self).__init__(*args, **kwargs)requirement.txtDjango==2.2.2 psycopg2==2.8.4 uwsgi==2.0.17 django-bootstrap4==1.1.1 django-bootstrap-datepicker-plus==3.0.5 #追加setting.pyINSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'users.apps.UsersConfig', 'bootstrap4', 'bootstrap_datepicker_plus', #追加 ]View
kids_profile_add関数では、POSTで受け取ったformに加えて
request.userで取得したユーザー情報を付与して保存してます。kids_profile_edit関数では、それに加えて変更したい情報を事前に表示しておきたいので、
データのIDをURL経由で受け取ります。users/views.py#子供情報追加 @login_required def kids_profile_add(request): user_name = request.user if request.method == 'POST': form = KidsProfileForm(request.POST, user = user_name) if form.is_valid(): data = form.save(commit=False) data.user = user_name data.save() return redirect('users:mypage') else: form = KidsProfileForm(user = user_name) return render(request, 'users/kids_profile_add.html', {'form': form}) #子供情報編集 @login_required def kids_profile_edit(request, kidsProfileId): kidsProfileData = KidsProfile.objects.get(pk=kidsProfileId) if request.method == 'POST': form = KidsProfileForm(request.POST) if form.is_valid(): data = form.save(commit=False) data.id = kidsProfileData.id data.user = request.user data.save() return redirect('users:mypage') else: form = KidsProfileForm( { 'name' : kidsProfileData.name , 'gender' : kidsProfileData.gender , 'birthday' : kidsProfileData.birthday }, ) return render(request, 'users/kids_profile_edit.html', {'form': form})HTML
_addも_editも、テンプレートはほぼ一緒。
{{ form.media }}を書かないと、datetimepickerが動かないので書く。kids_profile_add.html{% extends 'base.html' %} {% block content %} <!--日付入力フォーマット用--> {{ form.media }} <div class="col-md-12 col-lg-5"> <h2>New data</h2> <form method="POST" class="post-form">{% csrf_token %} {% bootstrap_form form %} <button type="submit" class="save btn btn-primary">Save</button> </form> </div> {% endblock %}kids_profile_edit.html{% extends 'base.html' %} {% block content %} <!--日付入力フォーマット用--> {{ form.media }} <div class="col-md-12 col-lg-5"> <h2>Data Edit</h2> <form method="POST" class="post-form">{% csrf_token %} {% bootstrap_form form %} <button type="submit" class="save btn btn-primary">Save</button> </form> </div> {% endblock %}ユーザーに紐づく子供の情報をTable形式で表示します。
子供の情報は複数人分が登録されるので、for文でレコード分だけ表示します。mypage.html{% extends 'base.html' %} {% block content %} <div class="col-md-12 col-lg-5"> <h2>My Page</h2> <br> <br> <table class = "table"> <tr> <td>ユーザー名</td> <td>{{ user }}</td> </tr> </table> <table class = "table table-hover"> <thead class = "thead-dark"> <tr> <th>名前</th> <th>性別</th> <th>誕生日</th> <th></th> <th></th> </tr> </thead> {% for kids_profile in kidsProfiles %} <tr> <td>{{ kids_profile.name }}</td> <td>{{ kids_profile.get_gender_display }}</td> <td>{{ kids_profile.birthday }}</td> <td><a href="{% url 'users:kids_profile_edit' kids_profile.id %}" class="btn btn-success btn-sm">編集</a></td> <td><button type="button" class="btn btn-danger btn-sm del_confirm">削除</button></td> </tr> {% endfor%} <tr> <td></td> <td></td> <td></td> <td></td> <td><a href="{% url 'users:kids_profile_add' %}" class="btn btn-primary btn-sm" role="button">追加</a></td> </tr> </table> </div> {% endblock content %}urls
新しいページを作ったので、URLのパスを通す。
kidsProfileIdをurlsで指定することで、URLを使ってHTMLとPythonで変数を受け渡す。urls.pyapp_name = 'users' urlpatterns = [ path('', views.mypage, name='mypage'), path('signup/', views.signup, name='signup'), path('mypage/', views.mypage, name='mypage'), path('login/', views.login_mypage.as_view(), name='login'), path('logout/', views.logout.as_view(), name='logout'), path('kids_profile_add/', views.kids_profile_add, name='kids_profile_add'), #追加 path('kids_profile_edit/<kidsProfileId>', views.kids_profile_edit, name='kids_profile_edit'),#追加 ]動かす!
登録がない状態。
登録画面。datetimepickerだと、こんな感じで表示される。
ちなみにスマホでやると、ちょっとやり辛い。解消方法はあるようだが、試してはいない。
編集すると、こんな感じ。各項目、元々の情報が入った状態で、変更可能なフォームが表示される。
登録されると、こんな感じ。この時点では削除ボタンはダミー。
- 投稿日:2020-02-11T22:15:52+09:00
CentOS7にDockerをインストールする
CentOSにDockerをインストールする
事前準備
AzureでCentOSのVMを作成しておく。
作成したマシン情報
- OS: Linux (centos 7.7.1908)
- マシン名: centos-sample
手順
クライアントのWindowsからteratermで接続する。
rootユーザに変更
sudo su実行結果
[aky100200@centos-sample ~]$ sudo su We trust you have received the usual lecture from the local System Administrator. It usually boils down to these three things: #1) Respect the privacy of others. #2) Think before you type. #3) With great power comes great responsibility. [sudo] password for aky100200: [root@centos-sample aky100200]#パッケージのインストール
- yum-utils
- device-mapper-persistent-data
- lvm2
yum install -y yum-utils device-mapper-persistent-data lvm2実行結果
[root@centos-sample aky100200]# yum install -y yum-utils device-mapper-persistent-data lvm2 Loaded plugins: fastestmirror, langpacks Determining fastest mirrors base | 3.1 kB 00:00 extras | 2.5 kB 00:00 openlogic | 2.9 kB 00:00 updates | 2.6 kB 00:00 (1/5): openlogic/7/x86_64/primary_db | 33 kB 00:00 (2/5): base/7/x86_64/group_gz | 165 kB 00:00 (3/5): extras/7/x86_64/primary_db | 159 kB 00:00 (4/5): updates/7/x86_64/primary_db | 6.7 MB 00:00 (5/5): base/7/x86_64/primary_db | 6.0 MB 00:00 Package yum-utils-1.1.31-52.el7.noarch already installed and latest version Package device-mapper-persistent-data-0.8.5-1.el7.x86_64 already installed and latest version Package 7:lvm2-2.02.185-2.el7_7.2.x86_64 already installed and latest version Nothing to do [root@centos-sample aky100200]#Dockerリポジトリのインストール
yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo実行結果
root@centos-sample aky100200]# yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo Loaded plugins: fastestmirror, langpacks adding repo from: https://download.docker.com/linux/centos/docker-ce.repo grabbing file https://download.docker.com/linux/centos/docker-ce.repo to /etc/yum.repos.d/docker-ce.repo repo saved to /etc/yum.repos.d/docker-ce.repo [root@centos-sample aky100200]#docker-ceのインストール
yum -y install docker-ce docker-ce-cli containerd.io実行結果
[root@centos-sample aky100200]# yum -y install docker-ce docker-ce-cli containerd.io Loaded plugins: fastestmirror, langpacks Loading mirror speeds from cached hostfile Resolving Dependencies --> Running transaction check ---> Package containerd.io.x86_64 0:1.2.10-3.2.el7 will be installed --> Processing Dependency: container-selinux >= 2:2.74 for package: containerd.io-1.2.10-3.2.el7.x86_64 ---> Package docker-ce.x86_64 3:19.03.5-3.el7 will be installed --> Processing Dependency: libcgroup for package: 3:docker-ce-19.03.5-3.el7.x86_64 ---> Package docker-ce-cli.x86_64 1:19.03.5-3.el7 will be installed --> Running transaction check ---> Package container-selinux.noarch 2:2.107-3.el7 will be installed --> Processing Dependency: policycoreutils-python for package: 2:container-selinux-2.107-3.el7.noarch ---> Package libcgroup.x86_64 0:0.41-21.el7 will be installed --> Running transaction check ---> Package policycoreutils-python.x86_64 0:2.5-33.el7 will be installed --> Processing Dependency: setools-libs >= 3.3.8-4 for package: policycoreutils-python-2.5-33.el7.x86_64 --> Processing Dependency: libsemanage-python >= 2.5-14 for package: policycoreutils-python-2.5-33.el7.x86_64 --> Processing Dependency: audit-libs-python >= 2.1.3-4 for package: policycoreutils-python-2.5-33.el7.x86_64 --> Processing Dependency: python-IPy for package: policycoreutils-python-2.5-33.el7.x86_64 --> Processing Dependency: libqpol.so.1(VERS_1.4)(64bit) for package: policycoreutils-python-2.5-33.el7.x86_64 --> Processing Dependency: libqpol.so.1(VERS_1.2)(64bit) for package: policycoreutils-python-2.5-33.el7.x86_64 --> Processing Dependency: libapol.so.4(VERS_4.0)(64bit) for package: policycoreutils-python-2.5-33.el7.x86_64 --> Processing Dependency: checkpolicy for package: policycoreutils-python-2.5-33.el7.x86_64 --> Processing Dependency: libqpol.so.1()(64bit) for package: policycoreutils-python-2.5-33.el7.x86_64 --> Processing Dependency: libapol.so.4()(64bit) for package: policycoreutils-python-2.5-33.el7.x86_64 --> Running transaction check ---> Package audit-libs-python.x86_64 0:2.8.5-4.el7 will be installed ---> Package checkpolicy.x86_64 0:2.5-8.el7 will be installed ---> Package libsemanage-python.x86_64 0:2.5-14.el7 will be installed ---> Package python-IPy.noarch 0:0.75-6.el7 will be installed ---> Package setools-libs.x86_64 0:3.3.8-4.el7 will be installed --> Finished Dependency Resolution Dependencies Resolved ================================================================================ Package Arch Version Repository Size ================================================================================ Installing: containerd.io x86_64 1.2.10-3.2.el7 docker-ce-stable 23 M docker-ce x86_64 3:19.03.5-3.el7 docker-ce-stable 24 M docker-ce-cli x86_64 1:19.03.5-3.el7 docker-ce-stable 39 M Installing for dependencies: audit-libs-python x86_64 2.8.5-4.el7 base 76 k checkpolicy x86_64 2.5-8.el7 base 295 k container-selinux noarch 2:2.107-3.el7 extras 39 k libcgroup x86_64 0.41-21.el7 base 66 k libsemanage-python x86_64 2.5-14.el7 base 113 k policycoreutils-python x86_64 2.5-33.el7 base 457 k python-IPy noarch 0.75-6.el7 base 32 k setools-libs x86_64 3.3.8-4.el7 base 620 k Transaction Summary ================================================================================ Install 3 Packages (+8 Dependent packages) Total download size: 89 M Installed size: 368 M Downloading packages: (1/11): container-selinux-2.107-3.el7.noarch.rpm | 39 kB 00:00 (2/11): audit-libs-python-2.8.5-4.el7.x86_64.rpm | 76 kB 00:00 (3/11): checkpolicy-2.5-8.el7.x86_64.rpm | 295 kB 00:00 warning: /var/cache/yum/x86_64/7/docker-ce-stable/packages/docker-ce-19.03.5-3.el7.x86_64.rpm: Header V4 RSA/SHA512 Signature, key ID 621e9f35: NOKEY Public key for docker-ce-19.03.5-3.el7.x86_64.rpm is not installed (4/11): docker-ce-19.03.5-3.el7.x86_64.rpm | 24 MB 00:00 (5/11): libcgroup-0.41-21.el7.x86_64.rpm | 66 kB 00:00 (6/11): libsemanage-python-2.5-14.el7.x86_64.rpm | 113 kB 00:00 (7/11): policycoreutils-python-2.5-33.el7.x86_64.rpm | 457 kB 00:00 (8/11): python-IPy-0.75-6.el7.noarch.rpm | 32 kB 00:00 (9/11): setools-libs-3.3.8-4.el7.x86_64.rpm | 620 kB 00:00 (10/11): docker-ce-cli-19.03.5-3.el7.x86_64.rpm | 39 MB 00:01 (11/11): containerd.io-1.2.10-3.2.el7.x86_64.rpm | 23 MB 00:02 -------------------------------------------------------------------------------- Total 38 MB/s | 89 MB 00:02 Retrieving key from https://download.docker.com/linux/centos/gpg Importing GPG key 0x621E9F35: Userid : "Docker Release (CE rpm) <docker@docker.com>" Fingerprint: 060a 61c5 1b55 8a7f 742b 77aa c52f eb6b 621e 9f35 From : https://download.docker.com/linux/centos/gpg Running transaction check Running transaction test Transaction test succeeded Running transaction Installing : libcgroup-0.41-21.el7.x86_64 1/11 Installing : setools-libs-3.3.8-4.el7.x86_64 2/11 Installing : 1:docker-ce-cli-19.03.5-3.el7.x86_64 3/11 Installing : audit-libs-python-2.8.5-4.el7.x86_64 4/11 Installing : python-IPy-0.75-6.el7.noarch 5/11 Installing : libsemanage-python-2.5-14.el7.x86_64 6/11 Installing : checkpolicy-2.5-8.el7.x86_64 7/11 Installing : policycoreutils-python-2.5-33.el7.x86_64 8/11 Installing : 2:container-selinux-2.107-3.el7.noarch 9/11 Installing : containerd.io-1.2.10-3.2.el7.x86_64 10/11 Installing : 3:docker-ce-19.03.5-3.el7.x86_64 11/11 Verifying : checkpolicy-2.5-8.el7.x86_64 1/11 Verifying : policycoreutils-python-2.5-33.el7.x86_64 2/11 Verifying : 3:docker-ce-19.03.5-3.el7.x86_64 3/11 Verifying : libsemanage-python-2.5-14.el7.x86_64 4/11 Verifying : 2:container-selinux-2.107-3.el7.noarch 5/11 Verifying : python-IPy-0.75-6.el7.noarch 6/11 Verifying : audit-libs-python-2.8.5-4.el7.x86_64 7/11 Verifying : containerd.io-1.2.10-3.2.el7.x86_64 8/11 Verifying : 1:docker-ce-cli-19.03.5-3.el7.x86_64 9/11 Verifying : setools-libs-3.3.8-4.el7.x86_64 10/11 Verifying : libcgroup-0.41-21.el7.x86_64 11/11 Installed: containerd.io.x86_64 0:1.2.10-3.2.el7 docker-ce.x86_64 3:19.03.5-3.el7 docker-ce-cli.x86_64 1:19.03.5-3.el7 Dependency Installed: audit-libs-python.x86_64 0:2.8.5-4.el7 checkpolicy.x86_64 0:2.5-8.el7 container-selinux.noarch 2:2.107-3.el7 libcgroup.x86_64 0:0.41-21.el7 libsemanage-python.x86_64 0:2.5-14.el7 policycoreutils-python.x86_64 0:2.5-33.el7 python-IPy.noarch 0:0.75-6.el7 setools-libs.x86_64 0:3.3.8-4.el7 Complete! [root@centos-sample aky100200]#インストールの確認
dockerコマンドのパスを確認する。
パスが確認できればOK。which docker実行結果
[root@centos-sample aky100200]# which docker /bin/docker [root@centos-sample aky100200]#dockerdの起動
実行してエラーが出なければOK。
systemctl start docker実行結果
[root@centos-sample aky100200]# systemctl start docker [root@centos-sample aky100200]#自動起動設定
systemctl enable docker実行結果
root@centos-sample aky100200]# systemctl enable docker Created symlink from /etc/systemd/system/multi-user.target.wants/docker.service to /usr/lib/systemd/system/docker.service. [root@centos-sample aky100200]#これでdockerが使えるようになった。
参考
- 投稿日:2020-02-11T22:00:06+09:00
自分のDockerコンテナIDをホスト名に設定する
ホスト名をキーとして扱うサービスに対応するために、Docker コンテナ内から、自分のコンテナ ID を取得して
docker-0b2e383f
のようなホスト名を設定する。#!/bin/bash docker="$(egrep ':/docker/[0-9a-f]+$' /proc/self/cgroup | head -1)" docker="${docker##*/}" hostname "docker-${docker:0:8}"https://gist.github.com/kawanet/a44493c0c3942b4b8f5d768f5a304564
- 投稿日:2020-02-11T21:17:46+09:00
Vue.js + Docker Compose で ホットリロードしながら開発する環境をつくる
はじめに
個人の開発用に Vue.js の動作環境を Docker に移行してみたら、とても捗ったのでメモがてら投稿します。
手順
前提
- Vue.js のプロジェクトはできている
npm run serve
で、 Vue.js が立ち上がるように設定されている1. Dockerfile を作成
プロジェクトの直下に作成します。
FROM node:lts-alpine WORKDIR /app COPY package*.json ./ RUN npm install COPY . . EXPOSE 8080 CMD ["npm", "run", "serve"]これで、 Vue.js を動作させる Docker Image を作成することができます。
2. docker-compose.yml を作成
こちらもプロジェクト直下に作成します。
docker-compose.ymlversion: "3" services: app: container_name: web build: context: . dockerfile: Dockerfile ports: - 8080:8080 volumes: - .:/app
volues
でプロジェクトを Docker コンテナにマウントさせることで、npm run serve
の Hot Road が利くようになります。起動する
プロジェクト直下で下記コマンドを実行します。
docker-compose up
localhost:8080
へアクセスすると、 Vue.js が起動しているのが確認できると思います。終わりに
Dockerはありがたいですね。
少しでもあなたの為になれば幸いです。
- 投稿日:2020-02-11T21:12:55+09:00
【 Docker+Nginx+Django+RDS】WEBアプリができるまで⑤
前置き
独学で、子供の成長アプリを作った時のことを、記録として残していきます。
間違っているところなどあれば、ご連絡お願いします。
①Djangoのようこそページへたどり着くまで
②NginxでDjangoのようこそページへたどり着くまで
③カスタムユーザーを作ってadminにたどり着く
④ログインログアウトをしようGoal
サインイン機能を作ろう
Form
Djangoはログインログアウトは簡単に実装できる。(あと簡単なCRUDも)
サインインも機能は実装されているが、Templateはないので自分たちで作る。
カスタムユーザーを推奨しているし、ユーザーを管理するために欲しい情報は
ビジネスロジック寄りであるということだろう。UserCreationFormを継承することでサインインの基本機能を継承。
Userはget_user_model()をしないとエラーになる。
カスタムユーザーにしているので、明示しないと見つからないのだと思われる。forms.pyfrom django.contrib.auth import get_user_model from django.contrib.auth.forms import AuthenticationForm from django.contrib.auth.forms import UserCreationForm User = get_user_model() class SignUpForm(UserCreationForm): class Meta: model = User fields = ('email',) def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) for field in self.fields.values(): field.widget.attrs['class'] = 'form-control'View
HTMLからPOSTで受け取って、フォームを返す・・・という基本形。
users/view.pyfrom django.contrib.auth import login as dj_login from .forms import SignUpForm from django.shortcuts import render, redirect # サインアップ画面 def signup(request): if request.method == 'POST': form = SignUpForm(request.POST) if form.is_valid(): user = form.save() dj_login(request, user) return redirect(to='/') else: form = SignUpForm() return render(request, 'users/signup.html', {'form': form})HTML
ログインページとあまり変わらない。Formとbootstrapに任せる。
users/signup.html{% extends 'base.html' %} {% block content %} <div class="col-md-12 col-lg-5"> <h2>ユーザー登録</h2> <form method="POST" class="post-form">{% csrf_token %} {% bootstrap_form form %} <button type="submit" class="save btn btn-primary">登録</button> </form> </div> {% endblock %}urls
signupページのページを追加。
signupにはLoginページからリンクを貼っている。詳細は1つ前の回を参照。users/urls.pyapp_name = 'users' urlpatterns = [ path('', views.mypage, name='mypage'), path('signup/', views.signup, name='signup'), #追加 path('mypage/', views.mypage, name='mypage'), path('login/', views.login_mypage.as_view(), name='login'), path('logout/', views.logout.as_view(), name='logout'), ]確認
Signup画面。入力のためのバリデーションコメントも付いてる。
(邪魔な場合は消せる。)
- 投稿日:2020-02-11T21:12:55+09:00
【 Docker+Nginx+Django+RDS】WEBアプリができるまで⑤ユーザー登録(サインイン)機能を作ろう
前置き
独学で、子供の成長アプリを作った時のことを、記録として残していきます。
間違っているところなどあれば、ご連絡お願いします。
①Djangoのようこそページへたどり着くまで
②NginxでDjangoのようこそページへたどり着くまで
③カスタムユーザーを作ってadminにたどり着く
④ログインログアウトをしよう
⑤ユーザー登録(サインイン)機能を作ろう<--今ここ
⑥ユーザーごとのデータ登録できるようにする〜CRU編
⑦ユーザーごとのデータ登録できるようにする〜削除編Goal
サインイン機能を作ろう
Form
Djangoはログインログアウトは簡単に実装できる。(あと簡単なCRUDも)
サインインも機能は実装されているが、Templateはないので自分たちで作る。
カスタムユーザーを推奨しているし、ユーザーを管理するために欲しい情報は
ビジネスロジック寄りであるということだろう。UserCreationFormを継承することでサインインの基本機能を継承。
Userはget_user_model()をしないとエラーになる。
カスタムユーザーにしているので、明示しないと見つからないのだと思われる。forms.pyfrom django.contrib.auth import get_user_model from django.contrib.auth.forms import AuthenticationForm from django.contrib.auth.forms import UserCreationForm User = get_user_model() class SignUpForm(UserCreationForm): class Meta: model = User fields = ('email',) def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) for field in self.fields.values(): field.widget.attrs['class'] = 'form-control'View
HTMLからPOSTで受け取って、フォームを返す・・・という基本形。
users/view.pyfrom django.contrib.auth import login as dj_login from .forms import SignUpForm from django.shortcuts import render, redirect # サインアップ画面 def signup(request): if request.method == 'POST': form = SignUpForm(request.POST) if form.is_valid(): user = form.save() dj_login(request, user) return redirect(to='/') else: form = SignUpForm() return render(request, 'users/signup.html', {'form': form})HTML
ログインページとあまり変わらない。Formとbootstrapに任せる。
users/signup.html{% extends 'base.html' %} {% block content %} <div class="col-md-12 col-lg-5"> <h2>ユーザー登録</h2> <form method="POST" class="post-form">{% csrf_token %} {% bootstrap_form form %} <button type="submit" class="save btn btn-primary">登録</button> </form> </div> {% endblock %}urls
signupページのページを追加。
signupにはLoginページからリンクを貼っている。詳細は1つ前の回を参照。users/urls.pyapp_name = 'users' urlpatterns = [ path('', views.mypage, name='mypage'), path('signup/', views.signup, name='signup'), #追加 path('mypage/', views.mypage, name='mypage'), path('login/', views.login_mypage.as_view(), name='login'), path('logout/', views.logout.as_view(), name='logout'), ]確認
Signup画面。入力のためのバリデーションコメントも付いてる。
(邪魔な場合は消せる。)
- 投稿日:2020-02-11T20:47:21+09:00
【 Docker+Nginx+Django+RDS】WEBアプリができるまで④
前置き
独学で、子供の成長アプリを作った時のことを、記録として残していきます。
間違っているところなどあれば、ご連絡お願いします。
①Djangoのようこそページへたどり着くまで
②NginxでDjangoのようこそページへたどり着くまで
③カスタムユーザーを作ってadminにたどり着くGoal
ログインログオフ機能を作ろう
HTML(下準備)
Djangoのtemplateをアプリごとに分けるのが面倒になりそうなので、
テンプレートは一箇所に置くことにする。最終形は以下(細かいものは省略)。. ├── docker-compose.yml ├── nginx │ ├── conf │ │ └── mysite_nginx.conf │ └── uwsgi_params ├── src │ ├── manage.py │ ├── mysite │ │ ├── settings.py │ │ ├── urls.py │ │ └── wsgi.py │ ├── templates │ │ ├── base.html │ │ └── users │ │ ├── login.html │ │ ├── mypage.html │ │ └── signup.html │ └── users │ ├── admin.py │ ├── apps.py │ ├── forms.py │ ├── models.py │ ├── urls.py │ └── views.py ├── static │ └── 略 └── web ├── Dockerfile └── requirements.txttemplateの場所を教えてあげるために、setting.pyを修正する。
setting.pyTEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [os.path.join(BASE_DIR, 'templates')], #変更 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ 'django.template.context_processors.debug', 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', ], }, }, ]CSSはbootstrapが手っ取り早くて良い。
bootstrapはdjango用のものがあるので、それを使う。requirement.txtDjango==2.2.2 psycopg2==2.8.4 uwsgi==2.0.17 django-bootstrap4==1.1.1 #追加settingはAPPSに追加するのと、Templateの指定にも追加する。
setting.pyINSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'users.apps.UsersConfig', 'bootstrap4', #追加 ] (略) TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [os.path.join(BASE_DIR, 'templates')], 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ 'django.template.context_processors.debug', 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', ], 'builtins':[ #追加 'bootstrap4.templatetags.bootstrap4', ], }, }, ]HTML(本番)
ベースのHTML。bootstrapでレスポンシブ対応ぽくしている。
base.html{% load static %} {% bootstrap_css %} {% bootstrap_javascript jquery='full' %} <html> <head> <title>kids Growth</title> <link rel="stylesheet" href="{% static 'css/kidsGrowth.css' %}"> {% block extra_js %}{% endblock %} </head> <body> <!-- Navigation --> <nav class="navbar navbar-expand-sm navbar-dark bg-dark mt-3 mb-3"> <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNav4" aria-controls="navbarNav4" aria-expanded="false" aria-label="Toggle navigation"> <span class="sr-only">メニュー</span> <span class="navbar-toggler-icon"></span> </button> <a class="navbar-brand" href="">Kids Growth</a> <div class="collapse navbar-collapse" id="navbarNav4"> <ul class="navbar-nav"> <li class="nav-item"> <a class="nav-link" href="">メニュー1</a> </li> <li class="nav-item"> <a class="nav-link" href="">メニュー2</a> </li> <li class="nav-item"> <a class="nav-link" href="">メニュー3</a> </li> <li class="nav-item"> <a class="nav-link" href="{% url 'users:mypage'%}">マイページ</a> </li> {% if user.is_authenticated %} <li class="nav-item"> <a class="nav-link" href="{% url 'users:logout'%}">ログアウト</a> </li> {% else %} <li class="nav-item"> <a class="nav-link" href="{% url 'users:login'%}">ログイン</a> </li> {% endif %} </ul> </div> </nav> <div class="content container"> {% block content %} {% endblock %} </div> </body> </html>マイページ。(ユーザー名が出るだけ)
ログイン後に飛んでくるところとして指定。
{{ user }}はあとでViewから引き渡します。users/mypage.html{% extends 'base.html' %} {% block content %} <div class="col-md-12 col-lg-5"> <h2>My Page</h2> <br> <br> <table class = "table"> <tr> <td>ユーザー名</td> <td>{{ user }}</td> </tr> </table> </div> {% endblock content %}formという名前でフォームを引き渡して、
bootstrap_formで綺麗に整形するだけ。
ユーザー登録のリンクを用意してますが、その機能は次で。users/login.html{% extends 'base.html' %} {% block content %} {{ form.media }} <div class="col-md-12 col-lg-5"> <h2>Login</h2> <form action="" method="POST"> {{ form.non_field_errors }} {% bootstrap_form form %} <hr> <button type="submit" class="btn btn-success btn-lg btn-block" >ログイン</button> {% csrf_token %} </form> <a href="{% url 'users:signup' %}">ユーザー登録</a> </div> {% endblock %}Form
FormとしてAuthenticationFormを継承することで、
ログイン機能を実装します。これだけ。便利。users/form.pyclass LoginForm(AuthenticationForm): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs)View
ログインとログアウトは専用のViewがあるので継承する。
@login_requiredを指定した関数はログインした状態じゃないと呼ばれない。
ひとまずはログインしているユーザーの名前を取得して、templateに返すだけのお仕事。ちなみにログインのクラスをloginにすると、このあとSignup関数を作るときに
規定のネーミングとバッティングするのかエラーが外れず、ややカッコ悪いネーミング。users/view.pyfrom .forms import LoginForm from django.shortcuts import render, redirect from django.contrib.auth.views import LoginView, LogoutView from django.contrib.auth.decorators import login_required #ログイン class login_mypage(LoginView): form_class = LoginForm template_name = 'users/login.html' #ログアウト class logout(LogoutView): template_name = 'users/login.html' #マイページ @login_required def mypage(request): user_name = request.user params = { 'user' : user_name, } return render(request, 'users/mypage.html',params)urls
プロジェクトのurlsで、アプリごとのurlsを読み込む。
mysite/urls.pyfrom django.contrib import admin from django.urls import path, include urlpatterns = [ path('admin/', admin.site.urls), path('',include('users.urls')), ]アプリごとに定義したURLパターンごとにViewを決める。
users/urls.pyapp_name = 'users' urlpatterns = [ path('', views.mypage, name='mypage'), path('mypage/', views.mypage, name='mypage'), path('login/', views.login_mypage.as_view(), name='login'), path('logout/', views.logout.as_view(), name='logout'), ]Setting.py
以下を追加する。ログインするときのURLはここだよ〜
ログインしたらここに最初にアクセスしてね〜
ログアウトしたらなんの画面を見せるよ〜
ということを決める。setting.pyLOGIN_URL = 'users:login' LOGIN_REDIRECT_URL = 'users:mypage' LOGOUT_REDIRECT_URL = 'users:login'出来上がり
ログイン画面と、ログインした後の画面。
- 投稿日:2020-02-11T20:47:21+09:00
【 Docker+Nginx+Django+RDS】WEBアプリができるまで④ログインログアウトをしよう
前置き
独学で、子供の成長アプリを作った時のことを、記録として残していきます。
間違っているところなどあれば、ご連絡お願いします。
①Djangoのようこそページへたどり着くまで
②NginxでDjangoのようこそページへたどり着くまで
③カスタムユーザーを作ってadminにたどり着く
④ログインログアウトをしよう<--今ここ
⑤ユーザー登録(サインイン)機能を作ろう
⑥ユーザーごとのデータ登録できるようにする〜CRU編
⑦ユーザーごとのデータ登録できるようにする〜削除編Goal
ログインログオフ機能を作ろう
HTML(下準備)
Djangoのtemplateをアプリごとに分けるのが面倒になりそうなので、
テンプレートは一箇所に置くことにする。最終形は以下(細かいものは省略)。. ├── docker-compose.yml ├── nginx │ ├── conf │ │ └── mysite_nginx.conf │ └── uwsgi_params ├── src │ ├── manage.py │ ├── mysite │ │ ├── settings.py │ │ ├── urls.py │ │ └── wsgi.py │ ├── templates │ │ ├── base.html │ │ └── users │ │ ├── login.html │ │ ├── mypage.html │ │ └── signup.html │ └── users │ ├── admin.py │ ├── apps.py │ ├── forms.py │ ├── models.py │ ├── urls.py │ └── views.py ├── static │ └── 略 └── web ├── Dockerfile └── requirements.txttemplateの場所を教えてあげるために、setting.pyを修正する。
setting.pyTEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [os.path.join(BASE_DIR, 'templates')], #変更 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ 'django.template.context_processors.debug', 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', ], }, }, ]CSSはbootstrapが手っ取り早くて良い。
bootstrapはdjango用のものがあるので、それを使う。requirement.txtDjango==2.2.2 psycopg2==2.8.4 uwsgi==2.0.17 django-bootstrap4==1.1.1 #追加settingはAPPSに追加するのと、Templateの指定にも追加する。
setting.pyINSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'users.apps.UsersConfig', 'bootstrap4', #追加 ] (略) TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [os.path.join(BASE_DIR, 'templates')], 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ 'django.template.context_processors.debug', 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', ], 'builtins':[ #追加 'bootstrap4.templatetags.bootstrap4', ], }, }, ]HTML(本番)
ベースのHTML。bootstrapでレスポンシブ対応ぽくしている。
base.html{% load static %} {% bootstrap_css %} {% bootstrap_javascript jquery='full' %} <html> <head> <title>kids Growth</title> <link rel="stylesheet" href="{% static 'css/kidsGrowth.css' %}"> {% block extra_js %}{% endblock %} </head> <body> <!-- Navigation --> <nav class="navbar navbar-expand-sm navbar-dark bg-dark mt-3 mb-3"> <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNav4" aria-controls="navbarNav4" aria-expanded="false" aria-label="Toggle navigation"> <span class="sr-only">メニュー</span> <span class="navbar-toggler-icon"></span> </button> <a class="navbar-brand" href="">Kids Growth</a> <div class="collapse navbar-collapse" id="navbarNav4"> <ul class="navbar-nav"> <li class="nav-item"> <a class="nav-link" href="">メニュー1</a> </li> <li class="nav-item"> <a class="nav-link" href="">メニュー2</a> </li> <li class="nav-item"> <a class="nav-link" href="">メニュー3</a> </li> <li class="nav-item"> <a class="nav-link" href="{% url 'users:mypage'%}">マイページ</a> </li> {% if user.is_authenticated %} <li class="nav-item"> <a class="nav-link" href="{% url 'users:logout'%}">ログアウト</a> </li> {% else %} <li class="nav-item"> <a class="nav-link" href="{% url 'users:login'%}">ログイン</a> </li> {% endif %} </ul> </div> </nav> <div class="content container"> {% block content %} {% endblock %} </div> </body> </html>マイページ。(ユーザー名が出るだけ)
ログイン後に飛んでくるところとして指定。
{{ user }}はあとでViewから引き渡します。users/mypage.html{% extends 'base.html' %} {% block content %} <div class="col-md-12 col-lg-5"> <h2>My Page</h2> <br> <br> <table class = "table"> <tr> <td>ユーザー名</td> <td>{{ user }}</td> </tr> </table> </div> {% endblock content %}formという名前でフォームを引き渡して、
bootstrap_formで綺麗に整形するだけ。
ユーザー登録のリンクを用意してますが、その機能は次で。users/login.html{% extends 'base.html' %} {% block content %} {{ form.media }} <div class="col-md-12 col-lg-5"> <h2>Login</h2> <form action="" method="POST"> {{ form.non_field_errors }} {% bootstrap_form form %} <hr> <button type="submit" class="btn btn-success btn-lg btn-block" >ログイン</button> {% csrf_token %} </form> <a href="{% url 'users:signup' %}">ユーザー登録</a> </div> {% endblock %}Form
FormとしてAuthenticationFormを継承することで、
ログイン機能を実装します。これだけ。便利。users/form.pyclass LoginForm(AuthenticationForm): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs)View
ログインとログアウトは専用のViewがあるので継承する。
@login_requiredを指定した関数はログインした状態じゃないと呼ばれない。
ひとまずはログインしているユーザーの名前を取得して、templateに返すだけのお仕事。ちなみにログインのクラスをloginにすると、このあとSignup関数を作るときに
規定のネーミングとバッティングするのかエラーが外れず、ややカッコ悪いネーミング。users/view.pyfrom .forms import LoginForm from django.shortcuts import render, redirect from django.contrib.auth.views import LoginView, LogoutView from django.contrib.auth.decorators import login_required #ログイン class login_mypage(LoginView): form_class = LoginForm template_name = 'users/login.html' #ログアウト class logout(LogoutView): template_name = 'users/login.html' #マイページ @login_required def mypage(request): user_name = request.user params = { 'user' : user_name, } return render(request, 'users/mypage.html',params)urls
プロジェクトのurlsで、アプリごとのurlsを読み込む。
mysite/urls.pyfrom django.contrib import admin from django.urls import path, include urlpatterns = [ path('admin/', admin.site.urls), path('',include('users.urls')), ]アプリごとに定義したURLパターンごとにViewを決める。
users/urls.pyapp_name = 'users' urlpatterns = [ path('', views.mypage, name='mypage'), path('mypage/', views.mypage, name='mypage'), path('login/', views.login_mypage.as_view(), name='login'), path('logout/', views.logout.as_view(), name='logout'), ]Setting.py
以下を追加する。ログインするときのURLはここだよ〜
ログインしたらここに最初にアクセスしてね〜
ログアウトしたらなんの画面を見せるよ〜
ということを決める。setting.pyLOGIN_URL = 'users:login' LOGIN_REDIRECT_URL = 'users:mypage' LOGOUT_REDIRECT_URL = 'users:login'出来上がり
ログイン画面と、ログインした後の画面。
- 投稿日:2020-02-11T18:57:04+09:00
DockerでRailsの環境構築
docker-composeでrails環境を構築します。データベースはpostgresqlを使用します。
Setup
以下のような構成からスタートします。
./ |- docker-compose.yml |- rails-app/ |- DockerfileDockerfileの作成
DockerfileFROM ruby:2.5.0 RUN apt-get update && apt-get install -y build-essential libpq-dev postgresql-client vim less RUN gem install rails -v '5.2.1' RUN mkdir /app WORKDIR /apprailsのバージョンは5.2.1を指定しています。
マウント先のディレクトリとしてappディレクトリを作成しておきます。docker-compose.ymlの作成
docker-compose.ymlversion: "3" services: rails-app: build: rails-app ports: - "3001:3001" environment: - "DATABASE_HOST=db" - "DATABASE_PORT=5432" - "DATABASE_USER=postgres" - "DATABASE_PASSWORD=<パスワード>" links: - db volumes: - "./rails-app:/app" stdin_open: true db: image: postgres:10.1 ports: - "5432:5432" environment: - "POSTGRES_USER=postgres" - "POSTGRES_PASSWORD=<パスワード>"ポートは3001番を指定していますが、3000番でもOKです。
コンテナの起動
docker-compose build docker-compose up -d
コンテナにログインします。
docker-compose exec rails-app bash
以降はコンテナ内で操作を行います。
Railsプロジェクトの作成
cd /app rails new . -d postgresql -BT
-B
でbundle installをスキップし、-T
でテストファイルの作成をスキップしています。
Gemfileにtherubyracerを追加します。Gemfilegem 'therubyracer', platforms: :rubyこの状態でbundle installします。
bundle install —-path vendor/bundleこれでプロジェクトが作成されました。
データベースの設定
config/database.yml
を編集します。
- defaultにhost, port, username, passwordを追加
- データベース名を rails_app_development に変更
config/database.ymldefault: &default adapter: postgresql encoding: unicode # For details on connection pooling, see Rails configuration guide # http://guides.rubyonrails.org/configuring.html#database-pooling pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> + host: <%= ENV.fetch("DATABASE_HOST") { "localhost" } %> + port: <%= ENV.fetch("DATABASE_PORT") { 5432 } %> + username: <%= ENV.fetch("DATABASE_USER") { "root" } %> + password: <%= ENV.fetch("DATABASE_PASSWORD") { "password" } %> development: <<: *default - database: app_development + database: rails_app_developmentデータベースを作成します。
bundle exec rails db:create
起動
IPアドレスとポートを指定してサーバを起動します。
rails s -b 0.0.0.0 -p 3001ブラウザで
http://localhost:3001/
を開いて、正しく起動できているか確認します。
ホストPCとdockerコンテナ間でプロジェクトディレクトリを共有しているので(./rails-app:/app)、編集はホストPCで(vscode等で!)、実行は環境構築したコンテナで、という開発ができます。
幸せになりましょう。
- 投稿日:2020-02-11T18:38:04+09:00
AWS CLIでECRにログインする時はget-loginではなくget-login-passwordを使おう
aws ecr get-login は非推奨に
コマンドリファレンスに記載されています。
Note: This command is deprecated. Use get-login-password instead.
AWS CLI Command Reference - get-login
https://docs.aws.amazon.com/cli/latest/reference/ecr/get-login.html
aws ecr get-login
コマンドの出力結果は以下のようになります。$ aws ecr get-login --no-include-email docker login -u AWS -p <password> https://<aws_account_id>.dkr.ecr.<region>.amazonaws.comdocker login コマンド全体が標準出力されるため便利ではあるのですが
そのまま実行してしまうと、パスワードがシェルのhistoryやログファイルに
残ってしまうというリスクがあります。aws ecr get-login-password を使う
AWS CLI Command Reference - get-login-password
https://docs.aws.amazon.com/cli/latest/reference/ecr/get-login-password.html
aws ecr get-login-password
コマンドはログイン用のパスワードのみが標準出力されます。$ aws ecr get-login-password <password>docker login コマンドは
--password-stdin
というオプションが利用可能で
パスワードを標準入力から読み込ませることができます。
以下のようにaws ecr get-login-password
の結果を渡すことで、
履歴やログにパスワードを残さず済みます。$ aws ecr get-login-password | docker login --username AWS --password-stdin https://<aws_account_id>.dkr.ecr.<region>.amazonaws.com Login SucceededProvide a password using STDIN
https://docs.docker.com/engine/reference/commandline/login/#provide-a-password-using-stdin利用可能なAWS CLIのバージョン
get-login-password コマンドは AWS CLI Version 1.17.10 以降、
または2020/2/10にGAとなった Version 2で利用することができます。注意点としては AWS CLI Version 2では get-loginコマンドは削除されているため利用できません。
Version 1.17.10 以降では 下位互換性のため、引き続き利用可能です。The older aws ecr get-login command is still available in the AWS CLI version 1 for backward compatibility.
Breaking Changes – Migrating from AWS CLI version 1 to version 2
AWS CLI version 2 replaces ecr get-login with ecr get-login-password
https://docs.aws.amazon.com/cli/latest/userguide/cliv2-migration.html#cliv2-migration-ecr-get-login以上です。
参考になれば幸いです。
- 投稿日:2020-02-11T17:27:46+09:00
Laradockでの環境構築からSeleniumでお手軽スクレイピング
概要
タイトル通りです。
対象者
スクレイピングするのに、Seleniumで
妥協していいややりたいんだ!って人。Laradockとは
Laravel + Dockerです。
Laravelとは
Laravelです。
Dockerとは
Dockerです。
Seleniumとは
ウェブアプリケーションの自動化を目的としたブラウザ用のテストツール群です。
しかしながら本記事で解説するようにスクレイピングを行うにあたっても利用できます。
指定したウェブサイトへアクセスし、指定したページに遷移し、指定した情報を読み取ってくるなどの動きが出来ます。
あくまでテスト用ツールなので、スクレイピングを主目的とするならイケてるPythonとかのそういうツールを使った方がいいです。
Selenium最高です。Selenium使いましょう。今回スクレイピングで利用するツールの概要
- Selenium Server
クライアントとドライバーの中継役として働くサーバーです。Javaのコードで実装されています。- Chrome Driver
ブラウザ操作に関する命令を仲介してくれます。 Chromeじゃなくてもいいですが、今回はChromeを使用します。他にもFirefox、IEなどがあります。- php-webdriver
Chrome Driverを動かすためのライブラリ。PHPでは公式にドライバーを動かせるツールが無い為、Facebookが作ったらしいです。Seleniumを使用したスクレイピングのしくみ
ざっくり説明すると、
プログラム => PHP WeDriver => Selenium Server => Chrome Driver => Chrome => スクレイピング対象Webサイトみたいな感じですかね!知らんけど。
環境構築
LaravelとDocker
LaravelとDockerの環境構築については私の書いたズボラなこちらの記事を参考にしてください。
https://qiita.com/heiheiyoyo/items/0ff035d018a1ce268c69Selenium
LaravelとDockerの環境構築はできたと思うので、早速SeleniumのコンテナをDockerで立ち上げましょう!
Seleniumコンテナの立ち上げについて必要な記述はLaradockのdocker-compose.ymlとDockerfileに既に記載されてますので、以下のコマンドを叩くだけでOKです。
$ docker-compose up -d selenium立ち上がったか確認しましょう。
以下のようにSeleniumコンテナが立ち上がっていればOKです。$ docker ps d05e612e2baf laradock_selenium "/opt/bin/entry_poin…" 3 minutes ago Up 3 minutes 0.0.0.0:4444->4444/tcp laradock_selenium_1 以下省略...ちなみLaradock内のSeleniumのDockerfileを見てみると、以下のようになっており、デフォルトでベースのイメージがselenium/standalone-chromeにとされているので、今回の環境構築の手間が省けました。
FROM selenium/standalone-chrome LABEL maintainer="Edmund Luong <edmundvmluong@gmail.com>" EXPOSE 4444php-webdriver
php-webdriverはまだインストールされていないので、ここで準備しておきます。
プロジェクト直下にあるcomposer.jsonというファイルに追記していきます。
この、composer.jsonというのはインストールしたいパッケージを記述しておき、後からコマンドを叩いた時に、まとめてインストールする為のファイルです。json形式で記述します。
ちなみに、require-devというのは本番に必要のない開発用のパッケージを記述する場所です。
本番でも必要な場合はrequireに記述しましょう。
今回はとりあえず、require-devに記述しておきます。
バージョンの書き方など詳しく知りたければ調べてください。
以下のリンクが参考になります。
Access Japan
https://access-jp.co.jp/blogs/development/256とりあえず今回は最新版でインストールします。
追記前
"require-dev": { "facade/ignition": "^1.4", "fzaninotto/faker": "^1.4", "mockery/mockery": "^1.0", "nunomaduro/collision": "^3.0", "phpunit/phpunit": "^8.0" },追記後
"require-dev": { "facade/ignition": "^1.4", "fzaninotto/faker": "^1.4", "mockery/mockery": "^1.0", "nunomaduro/collision": "^3.0", "phpunit/phpunit": "^8.0", "php-webdriver/webdriver": "*" },composer updateコマンドを実行します。
composer updateはcomposer.jsonの情報を元に各ファイルを最新版にアップデートするらしいです。
これでプロジェクト内のvenderディレクトリにphp-webdriverというディレクトリが出来たと思います。
この中に実際にブラウザを操作するためのファイルがいろいろと入っています。この中にChromeDriver.phpに定義されているChromeDriverクラスというものがドライバーの起動等を担ってくれるのですが、今回はコイツを使わず、継承元であるRemoteWebDriverというクラスを直接使っていきます。このRemoteWebDriverクラスにはスクレイピングに必要な処理(どのURLにアクセスするとか、どのページのどの要素を読み込むか等々)が色々と定義されています。(正確にはRemoteWebDriverはWebDriverインターフェースを実装しているので、定義自体はインターフェースで行われています。)
$ composer update Loading composer repositories with package information Updating dependencies (including require-dev) Package operations: 1 install, 0 updates, 1 removal - Removing facebook/webdriver (1.7.1) - Installing php-webdriver/webdriver (1.7.1): Downloading (100%) php-webdriver/webdriver suggests installing ext-SimpleXML (For Firefox profile creation) Writing lock file Generating optimized autoload files > Illuminate\Foundation\ComposerScripts::postAutoloadDump > @php artisan package:discover --ansi Discovered Package: facade/ignition Discovered Package: fideloper/proxy Discovered Package: laravel/tinker Discovered Package: nesbot/carbon Discovered Package: nunomaduro/collision Package manifest generated successfully.ここまでくれば環境構築は終了ですが、最後にプロジェクト内にvendor/autoload.phpのファイルが存在するかをチェックしておいて下さい。なければcomposer install等を実行し、作っておいて下さい。
あとで使いますが、autoload.phpをrequireするだけで、vendor配下のファイルを自動で読み込めるので、便利です。実装
クローラの作成
さあ実装に入っていきますが、まずはスクレイピングを行うにあたっての本体的な役割にあたるクローラというプログラムを作っていきましょう。
参考
クローラ(Crawler)とは、ウェブ上の文書や画像などを周期的に取得し、自動的にデータベース化するプログラムである。「ボット(Bot)」、「スパイダー」、「ロボット」などとも呼ばれる。(Wikipedia)
今回は手軽に実装する為、スクレイピングの開始から終了までを一つのファイルにまとめたものを作っていきます。(Laravelのコマンドクラスを使います。)
本格的にクローラを運用するなら、クローラの構成は、クローリングの処理はコマンドクラスとジョブクラスに分け、非同期処理を行う。さらに他クラスでDBの永続化処理を行うというような実装ができますが、それはまた別の機会にでもやりたいと思います。(やらんけど)
コマンドクラスの作成
以下のコマンドでコマンドクラスを作りましょう。「php artisan hogehoge(例)」 の「hogehoge」に当たる部分を自作していきます。自作したコマンドを叩けば、クローラの処理が実行されるという感じにしていきたいと思います。
デフォルトでapp/Console/Commands/に作成されます。
$ php artisan make:command SeleniumTestCommand Console command created successfully.作成されたコマンドクラスは以下のようになっていると思います。
<?php namespace App\Console\Commands; use Illuminate\Console\Command; class SeleniumTestCommand extends Command { /** * The name and signature of the console command. * * @var string */ protected $signature = 'command:name'; /** * The console command description. * * @var string */ protected $description = 'Command description'; /** * Create a new command instance. * * @return void */ public function __construct() { parent::__construct(); } /** * Execute the console command. * * @return mixed */ public function handle() { // } }このコマンドクラスを書き換えていきましょう。
ソースコード
先ほど作成したコマンドクラスを適当に以下のように書き換えて下さい。
<?php namespace App\Console\Commands; require base_path(). '/vendor/autoload.php'; use Illuminate\Console\Command; use Facebook\WebDriver\WebDriverBy; use Facebook\WebDriver\Chrome\ChromeOptions; use Facebook\WebDriver\Remote\RemoteWebDriver; use Facebook\WebDriver\Remote\DesiredCapabilities; use Facebook\WebDriver\WebDriverExpectedCondition; class SeleniumTestCommand extends Command { /** * The name and signature of the console command. * * @var string */ protected $signature = 'scrape:selenium-test'; /** * The console command description. * * @var string */ protected $description = 'Seleniumでスクレイピングをするよ'; /** * Create a new command instance. * * @return void */ public function __construct() { parent::__construct(); } /** * Execute the console command. * * @return mixed */ public function handle() { // クロームの機能を管理するクラスのインスタンス化 $options = new ChromeOptions(); // クローム起動時のオプション格納 $options->addArguments([ '--no-sandbox', '--headless' ]); // Chromeブラウザを起動 $caps = DesiredCapabilities::chrome(); $caps->setCapability(ChromeOptions::CAPABILITY, $options); // ブラウザを実行するプラットフォームを指定。クロームとのセッションがスムーズになる?? $caps->setPlatform("LINUX"); // これはSelenium Serverの置いてあるURLなのかな $host = 'http://localhost:4444/wd/hub'; try { // なんかよく起動できずに落ちたので、retry()でくくる $driver = retry(3, function () use ($host, $caps) { // chrome ドライバーの起動、ウイーーーーーーーーーーーン return RemoteWebDriver::create($host, $caps, 60000, 60000); }, 1000); // Y◯hoo!さんのニュースサイトに潜入します $driver->get('https://news.yahoo.co.jp'); dump($driver->getCurrentUrl()); // ページタイトル「Yahoo!ニュース」が現れるまで待ちます $driver->wait()->until( WebDriverExpectedCondition::titleIs('Yahoo!ニュース') ); // トップページのトピックをリンクを取得していきます $topics_counts = count($driver->findElement(WebDriverBy::className('topicsList_main')) ->findElements(WebDriverBy::className('topicsListItem'))); // リンク集 $links = []; for ($i=0; $i <= $topics_counts -1; $i++) { $links[] = $driver->findElement(WebDriverBy::className('topicsList_main')) ->findElements(WebDriverBy::className('topicsListItem'))[$i] ->findElement(WebDriverBy::tagName('a')) ->getAttribute('href'); } // リンクの数だけアクセス foreach ($links as $link) { // リンクが取得できているか dump($link); // URLにアクセス $driver->get($link); // ページタイトルに「Yahoo!ニュース」が含まれるものが現れるまで待ちます $driver->wait()->until(WebDriverExpectedCondition::titleContains('Yahoo!ニュース')); // 記事のタイトルをクローリングします $article_title = $driver->findElement(WebDriverBy::className('pickupMain_articleTitle')) ->getText(); // 記事のタイトルが取得できているか dump($article_title); } // 処理終了 return; } catch (\Exception $e) { echo 'エラーによりスクレイピングが失敗しました。ERROR MESSAGE : '.$e->getErrorMessage().' TRACE : '.$e->getTraceAsString(); } finally { $driver->quit(); } } }実行
以下のコマンドで実行しましょう。
$ php artisan scrape:selenium-testうまくいけば、ターミナル常にdumpの出力がされて
サイトトップページURL 記事のページのURL 記事のタイトル 記事のページのURL 記事のタイトル 記事のページのURL 記事のタイトル ...みたいになると思います。
うまくいかなければ、、、
てへぺろ。解説
※後半から畳み掛けるように雑になると思います。
ChromeOptionsクラスのインスタンス化
ChromeOptionsクラスはChromeの機能を管理するクラスです。
$options = new ChromeOptions();Chrome起動時に渡すコマンド引数の設定
以下のように指定します。headlessはヘッドレスモードで起動する設定で、no-sandboxはセキュリティを下げ、スリルを味わってスクレイピングしたい人向けの設定です。
addArguments()でChromeOptionsクラスのクラス変数argumentsにarrayで引数を渡しています。
argumentsはChromeの起動時に適用されます。$options->addArguments([ '--no-sandbox', '--headless' ]);参考オプション一覧
- user-data-dir... ユーザープロファイルの設定。 Chromeは複数のユーザーを使い分けられるらしいので、ユーザー毎にブックマーク、履歴、パスワードなどをプロファイル毎に管理できるのだと思います。
- --proxy-server... プロキシサーバーの設定。 プロキシサーバーはクライアントとWebサイトの中継役を行います。セキュリティの向上などが見込まれます。
- --headless... ヘッドレスモード。ブラウザのUIなしで起動できます。
- --no-sandbox... サンドボックスの外でプロセスを動作させる。
- window-size... 画面幅指定。
- --start-maximized... 画面幅を最大化して起動します。
- --user-agent... 他のブラウザや端末に偽装できます。(なりすませます。)
- --incognito... シークレットモードで起動。履歴とかが残らない。アダルトサイトのスクレイピングとかにいいんじゃないっすかね。(誰がやんねん)
- --single-process... シングルプロセスで起動します。通常はタブ、サイト毎のマルチプロセスで起動するっぽいのですが、場合によってはメモリの使用量とか負担になるので使用するといいかもしれません。
- --disable-javascript... JavaScriptを無効にします。
- --disable-popup-blocking... ポップアップブロックを無効にします。コード内でアラート等に対応する必要がなくなりそうなので、便利ですね。
- --enable-logging... ログ出力を有効化します。ファイルはChrome\Application\chrome_debug.logで作成されるみたいです。
- --log-level... ログレベルを設定できます。
- --dump-histograms-on-exit... 終了時に各種統計情報をログファイルへ出力するという設定です。
オプションについては以下のサイトがよくまとめられており、参考になります。
起動オプション - Google Chrome まとめWiki
http://chrome.half-moon.org/43.html#y3a4f50eその他Chromeの設定
今回は設定していませんが、例えば何かしらのファイルをスクレイピングで取得したいとします。
その場合はダウンロードしたファイルの保存先を以下のように設定します。download.default_directoryはデフォルトのダウンロードディレクトリを指定するオプションです。
ちなみにsetExperimentalOption()はChromeOptions APIを介してまだ公開されていないChromeDriverオプションを試す為に使用します。(実験的なオプションを指定する的な。)
prefは起動時の引数とは異なり、起動後?の設定画面の項目のことを指しています。$options->setExperimentalOption('prefs', [ 'download.default_directory' => "任意のパス", ]);設定の反映〜ドライバー立ち上げ
まずは、ドライバーを起動させる際の設定をしています。
// Chromeブラウザを起動 $caps = DesiredCapabilities::chrome(); $caps->setCapability(ChromeOptions::CAPABILITY, $options); // ブラウザを実行するプラットフォームを指定。クロームとのセッションがスムーズになる?? $caps->setPlatform("LINUX"); // これはSelenium Serverの置いてあるURLなのかな $host = 'http://localhost:4444/wd/hub';次にドライバーを起動させます。
このタイミングで設定情報を渡してあげて下さい。
ちなみに原因不明ですが、Curl error thrown for http POST to...
みたいなエラーを吐き捨てられることがよくあったので、リトライでくくっています。(要らないかも)// なんかよく起動できずに落ちたので、retry()でくくる $driver = retry(3, function () use ($host, $caps) { // chrome ドライバーの起動、ウイーーーーーーーーーーーン return RemoteWebDriver::create($host, $caps, 60000, 60000); }, 1000);いざ、クローリング開始
ドライバーの立ち上げに成功したら、Webサイトへアクセスします。
今回は天下のY◯hoo!さんのニュースサイトにお邪魔しようと思います。
トップページにアクセスして話題になっているトピックのページに遷移し、記事のタイトルを読み取ってくるという流れでいきます。// Y◯hoo!さんのニュースサイトに潜入します $driver->get('https://news.yahoo.co.jp'); // ページタイトル「Yahoo!ニュース」が現れるまで待ちます $driver->wait()->until( WebDriverExpectedCondition::titleIs('Yahoo!ニュース') );トップページの左上部あたりに注目のニュース?的なトピックがあるので、その数をカウントしています。
// トップページのトピックをリンクを取得していきます $topics_counts = count($driver->findElement(WebDriverBy::className('topicsList_main')) ->findElements(WebDriverBy::className('topicsListItem')));classNameの名前を指定し、トピックの数だけ該当するaタグのhrefを配列に格納していきます。
// リンク集 $links = []; for ($i=0; $i <= $topics_counts -1; $i++) { $links[] = $driver->findElement(WebDriverBy::className('topicsList_main')) ->findElements(WebDriverBy::className('topicsListItem'))[$i] ->findElement(WebDriverBy::tagName('a')) ->getAttribute('href'); }リンクが集まれば、リンクの数だけそのページにアクセスしていきましょう。
さらにリンク先に記事のタイトル文章が記載されているので、それを読み取ってきます。// リンクの数だけアクセス foreach ($links as $link) { // リンクが取得できているか dump($link); // URLにアクセス $driver->get($link); // ページタイトルに「Yahoo!ニュース」が含まれるものが現れるまで待ちます $driver->wait()->until(WebDriverExpectedCondition::titleContains('Yahoo!ニュース')); // 記事のタイトルをクローリングします $article_title = $driver->findElement(WebDriverBy::className('pickupMain_articleTitle')) ->getText(); // 記事のタイトルが取得できているか dump($article_title); }大部分の処理はこれで終了です。
補足
ChromeDriverを起動させましたが、場合によっては処理が終了したにも関わらず、プロセスが残り続けることがあるみたいです。
なんか嫌なので、quit()で終了させるようにしましょう。
finallyの中に組み込めば必ず実行されると思います。} finally { $driver->quit(); }はい!
ということでお手軽にスクレイピングができました!
現場からは以上です!
- 投稿日:2020-02-11T16:48:01+09:00
【 Docker+Nginx+Django+RDS】WEBアプリができるまで③
前置き
独学で、子供の成長アプリを作った時のことを、記録として残していきます。
間違っているところなどあれば、ご連絡お願いします。
①Djangoのようこそページへたどり着くまで
②NginxでDjangoのようこそページへたどり着くまでGoal
カスタムユーザー設定を入れて、admin画面までたどり着く
その前に・・・
Djangoの設定を日本版にする。
setting.pyLANGUAGE_CODE = 'ja' TIME_ZONE = 'Asia/Tokyo'まずはユーザー管理用のアプリを作る
startappでusersアプリを作る。
docker-compose run web python ./manage.py startapp usersすると、こんな感じになる。
. ├── docker-compose.yml ├── nginx │ ├── conf │ │ └── mysite_nginx.conf │ └── uwsgi_params ├── src │ ├── manage.py │ ├── mysite │ │ ├── __init__.py │ │ ├── __pycache__ │ │ │ ├── __init__.cpython-37.pyc │ │ │ ├── settings.cpython-37.pyc │ │ │ ├── urls.cpython-37.pyc │ │ │ └── wsgi.cpython-37.pyc │ │ ├── settings.py │ │ ├── urls.py │ │ └── wsgi.py │ ├── static │ └── users │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── migrations │ │ └── __init__.py │ ├── models.py │ ├── tests.py │ └── views.py └── web ├── Dockerfile └── requirements.txtDjangoでアプリを作ったら、settingに作ったことを教えてあげる。
settings.pyINSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'users.apps.UsersConfig', #追加 ]カスタムユーザーを作ったので、そのことを教えてあげる。
settings.pyAUTH_USER_MODEL = 'users.User' #追加ユーザーカスタマイズ
AbstractBaseUserでデフォルトのユーザークラスをオーバーライド。
デフォルトだとID&PASSだが、mailアドレスをID扱いにしている。users/model.pyfrom django.db import models from django.core.mail import send_mail from django.contrib.auth.models import PermissionsMixin from django.contrib.auth.base_user import AbstractBaseUser from django.utils.translation import ugettext_lazy as _ from django.utils import timezone from django.contrib.auth.base_user import BaseUserManager class UserManager(BaseUserManager): """ユーザーマネージャー.""" use_in_migrations = True def _create_user(self, email, password, **extra_fields): """Create and save a user with the given username, email, and password.""" if not email: raise ValueError('The given email must be set') email = self.normalize_email(email) user = self.model(email=email, **extra_fields) user.set_password(password) user.save(using=self._db) return user def create_user(self, email, 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, password, **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 User(AbstractBaseUser, PermissionsMixin): """カスタムユーザーモデル.""" email = models.EmailField(_('email address'), unique=True) first_name = models.CharField(_('first name'), max_length=30, blank=True) last_name = models.CharField(_('last name'), max_length=150, blank=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 = 'email' REQUIRED_FIELDS = [] class Meta: verbose_name = _('user') verbose_name_plural = _('users') def get_full_name(self): """Return the first_name plus the last_name, with a space in between.""" full_name = '%s %s' % (self.first_name, self.last_name) return full_name.strip() def get_short_name(self): """Return the short name for the user.""" return self.first_name 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)モデルを追加したら、adminを修正する。
users/admin.pyfrom django.contrib import admin from django.contrib.auth.admin import UserAdmin from django.contrib.auth.forms import UserChangeForm, UserCreationForm from django.utils.translation import ugettext_lazy as _ from .models import User class MyUserChangeForm(UserChangeForm): class Meta: model = User fields = '__all__' class MyUserCreationForm(UserCreationForm): class Meta: model = User fields = ('email',) class MyUserAdmin(UserAdmin): fieldsets = ( (None, {'fields': ('email', 'password')}), (_('Personal info'), {'fields': ('first_name', 'last_name')}), (_('Permissions'), {'fields': ('is_active', 'is_staff', 'is_superuser', 'groups', 'user_permissions')}), (_('Important dates'), {'fields': ('last_login', 'date_joined')}), ) add_fieldsets = ( (None, { 'classes': ('wide',), 'fields': ('email', 'password1', 'password2'), }), ) form = MyUserChangeForm add_form = MyUserCreationForm list_display = ('email', 'first_name', 'last_name', 'is_staff') list_filter = ('is_staff', 'is_superuser', 'is_active', 'groups') search_fields = ('email', 'first_name', 'last_name') ordering = ('email',) admin.site.register(User, MyUserAdmin)モデルを作ったら、migrationする。
docker-compose run web python ./manage.py makemigrationsdocker-compose run web python ./manage.py migratesuperユーザー作成
docker-compose run web python ./manage.py createsuperusercreatesuperuserを入れると、ID (今回だとメールアドレス)とPasswordを求められる。
管理者権限のユーザーなので、秘密にする。メールアドレス: admin@example.com Password: Password (again): Superuser created successfully.Dockerで立ち上げよう①
Dockerを起動
docker-compose uplocalhost:8000/adminへアクセスする。
表示されるけど、CSSが効いてない…
静的ファイルの設定
Djangoで参照するSTATIC_ROOTを指定する。
setting.pySTATIC_ROOT = '/static'DockerでStaticフォルダを指定する。
docker-compose.ymlversion: "3" services: nginx: image: nginx:1.13 ports: - "8000:8000" volumes: - ./nginx/conf:/etc/nginx/conf.d - ./nginx/uwsgi_params:/etc/nginx/uwsgi_params - ./static:/static depends_on: - web db-postgres: image: postgres web: build: ./web volumes: - ./src:/code - ./static:/static #ここを追加 expose: - "8000" depends_on: - db-postgres command: uwsgi --socket :8000 --module mysite.wsgicollectstaticでプロジェクト内に入っているSTATICファイルの中身を
最上位のSTATICに集約する。docker-compose run web python ./manage.py collectstaticDockerで立ち上げよう②
localhost:8000/adminへアクセスする。
ちゃんとCSSが効いた!最後はこんな感じ。
. ├── docker-compose.yml ├── nginx │ ├── conf │ │ └── mysite_nginx.conf │ └── uwsgi_params ├── src │ ├── manage.py │ ├── mysite │ │ ├── __init__.py │ │ ├── __pycache__ │ │ │ ├── __init__.cpython-37.pyc │ │ │ ├── settings.cpython-37.pyc │ │ │ ├── urls.cpython-37.pyc │ │ │ └── wsgi.cpython-37.pyc │ │ ├── settings.py │ │ ├── urls.py │ │ └── wsgi.py │ ├── static │ └── users │ ├── __init__.py │ ├── __pycache__ │ │ ├── __init__.cpython-37.pyc │ │ ├── admin.cpython-37.pyc │ │ ├── apps.cpython-37.pyc │ │ └── models.cpython-37.pyc │ ├── admin.py │ ├── apps.py │ ├── migrations │ │ ├── 0001_initial.py │ │ ├── __init__.py │ │ └── __pycache__ │ │ ├── 0001_initial.cpython-37.pyc │ │ └── __init__.cpython-37.pyc │ ├── models.py │ ├── tests.py │ └── views.py ├── static │ └── admin │ ├── css │ │ ├── autocomplete.css │ │ ├── base.css │ 〜〜〜この中にいっぱい入っている。省略。 └── web ├── Dockerfile └── requirements.txt参考
- 投稿日:2020-02-11T16:48:01+09:00
【 Docker+Nginx+Django+RDS】WEBアプリができるまで③カスタムユーザーを作ってadminにたどり着く
前置き
独学で、子供の成長アプリを作った時のことを、記録として残していきます。
間違っているところなどあれば、ご連絡お願いします。
①Djangoのようこそページへたどり着くまで
②NginxでDjangoのようこそページへたどり着くまで
③カスタムユーザーを作ってadminにたどり着く<--今ここ
④ログインログアウトをしよう
⑤ユーザー登録(サインイン)機能を作ろう
⑥ユーザーごとのデータ登録できるようにする〜CRU編
⑦ユーザーごとのデータ登録できるようにする〜削除編Goal
カスタムユーザー設定を入れて、admin画面までたどり着く
その前に・・・
Djangoの設定を日本版にする。
setting.pyLANGUAGE_CODE = 'ja' TIME_ZONE = 'Asia/Tokyo'まずはユーザー管理用のアプリを作る
startappでusersアプリを作る。
docker-compose run web python ./manage.py startapp usersすると、こんな感じになる。
. ├── docker-compose.yml ├── nginx │ ├── conf │ │ └── mysite_nginx.conf │ └── uwsgi_params ├── src │ ├── manage.py │ ├── mysite │ │ ├── __init__.py │ │ ├── __pycache__ │ │ │ ├── __init__.cpython-37.pyc │ │ │ ├── settings.cpython-37.pyc │ │ │ ├── urls.cpython-37.pyc │ │ │ └── wsgi.cpython-37.pyc │ │ ├── settings.py │ │ ├── urls.py │ │ └── wsgi.py │ ├── static │ └── users │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── migrations │ │ └── __init__.py │ ├── models.py │ ├── tests.py │ └── views.py └── web ├── Dockerfile └── requirements.txtDjangoでアプリを作ったら、settingに作ったことを教えてあげる。
settings.pyINSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'users.apps.UsersConfig', #追加 ]カスタムユーザーを作ったので、そのことを教えてあげる。
settings.pyAUTH_USER_MODEL = 'users.User' #追加ユーザーカスタマイズ
AbstractBaseUserでデフォルトのユーザークラスをオーバーライド。
デフォルトだとID&PASSだが、mailアドレスをID扱いにしている。users/model.pyfrom django.db import models from django.core.mail import send_mail from django.contrib.auth.models import PermissionsMixin from django.contrib.auth.base_user import AbstractBaseUser from django.utils.translation import ugettext_lazy as _ from django.utils import timezone from django.contrib.auth.base_user import BaseUserManager class UserManager(BaseUserManager): """ユーザーマネージャー.""" use_in_migrations = True def _create_user(self, email, password, **extra_fields): """Create and save a user with the given username, email, and password.""" if not email: raise ValueError('The given email must be set') email = self.normalize_email(email) user = self.model(email=email, **extra_fields) user.set_password(password) user.save(using=self._db) return user def create_user(self, email, 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, password, **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 User(AbstractBaseUser, PermissionsMixin): """カスタムユーザーモデル.""" email = models.EmailField(_('email address'), unique=True) first_name = models.CharField(_('first name'), max_length=30, blank=True) last_name = models.CharField(_('last name'), max_length=150, blank=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 = 'email' REQUIRED_FIELDS = [] class Meta: verbose_name = _('user') verbose_name_plural = _('users') def get_full_name(self): """Return the first_name plus the last_name, with a space in between.""" full_name = '%s %s' % (self.first_name, self.last_name) return full_name.strip() def get_short_name(self): """Return the short name for the user.""" return self.first_name 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)モデルを追加したら、adminを修正する。
users/admin.pyfrom django.contrib import admin from django.contrib.auth.admin import UserAdmin from django.contrib.auth.forms import UserChangeForm, UserCreationForm from django.utils.translation import ugettext_lazy as _ from .models import User class MyUserChangeForm(UserChangeForm): class Meta: model = User fields = '__all__' class MyUserCreationForm(UserCreationForm): class Meta: model = User fields = ('email',) class MyUserAdmin(UserAdmin): fieldsets = ( (None, {'fields': ('email', 'password')}), (_('Personal info'), {'fields': ('first_name', 'last_name')}), (_('Permissions'), {'fields': ('is_active', 'is_staff', 'is_superuser', 'groups', 'user_permissions')}), (_('Important dates'), {'fields': ('last_login', 'date_joined')}), ) add_fieldsets = ( (None, { 'classes': ('wide',), 'fields': ('email', 'password1', 'password2'), }), ) form = MyUserChangeForm add_form = MyUserCreationForm list_display = ('email', 'first_name', 'last_name', 'is_staff') list_filter = ('is_staff', 'is_superuser', 'is_active', 'groups') search_fields = ('email', 'first_name', 'last_name') ordering = ('email',) admin.site.register(User, MyUserAdmin)モデルを作ったら、migrationする。
docker-compose run web python ./manage.py makemigrationsdocker-compose run web python ./manage.py migratesuperユーザー作成
docker-compose run web python ./manage.py createsuperusercreatesuperuserを入れると、ID (今回だとメールアドレス)とPasswordを求められる。
管理者権限のユーザーなので、秘密にする。メールアドレス: admin@example.com Password: Password (again): Superuser created successfully.Dockerで立ち上げよう①
Dockerを起動
docker-compose uplocalhost:8000/adminへアクセスする。
表示されるけど、CSSが効いてない…
静的ファイルの設定
Djangoで参照するSTATIC_ROOTを指定する。
setting.pySTATIC_ROOT = '/static'DockerでStaticフォルダを指定する。
docker-compose.ymlversion: "3" services: nginx: image: nginx:1.13 ports: - "8000:8000" volumes: - ./nginx/conf:/etc/nginx/conf.d - ./nginx/uwsgi_params:/etc/nginx/uwsgi_params - ./static:/static depends_on: - web db-postgres: image: postgres web: build: ./web volumes: - ./src:/code - ./static:/static #ここを追加 expose: - "8000" depends_on: - db-postgres command: uwsgi --socket :8000 --module mysite.wsgicollectstaticでプロジェクト内に入っているSTATICファイルの中身を
最上位のSTATICに集約する。docker-compose run web python ./manage.py collectstaticDockerで立ち上げよう②
localhost:8000/adminへアクセスする。
ちゃんとCSSが効いた!最後はこんな感じ。
. ├── docker-compose.yml ├── nginx │ ├── conf │ │ └── mysite_nginx.conf │ └── uwsgi_params ├── src │ ├── manage.py │ ├── mysite │ │ ├── __init__.py │ │ ├── __pycache__ │ │ │ ├── __init__.cpython-37.pyc │ │ │ ├── settings.cpython-37.pyc │ │ │ ├── urls.cpython-37.pyc │ │ │ └── wsgi.cpython-37.pyc │ │ ├── settings.py │ │ ├── urls.py │ │ └── wsgi.py │ ├── static │ └── users │ ├── __init__.py │ ├── __pycache__ │ │ ├── __init__.cpython-37.pyc │ │ ├── admin.cpython-37.pyc │ │ ├── apps.cpython-37.pyc │ │ └── models.cpython-37.pyc │ ├── admin.py │ ├── apps.py │ ├── migrations │ │ ├── 0001_initial.py │ │ ├── __init__.py │ │ └── __pycache__ │ │ ├── 0001_initial.cpython-37.pyc │ │ └── __init__.cpython-37.pyc │ ├── models.py │ ├── tests.py │ └── views.py ├── static │ └── admin │ ├── css │ │ ├── autocomplete.css │ │ ├── base.css │ 〜〜〜この中にいっぱい入っている。省略。 └── web ├── Dockerfile └── requirements.txt参考
- 投稿日:2020-02-11T16:42:25+09:00
RaspiにUbuntu入れてその上にDocker入れてコンテナからpythonでGPIO制御
はじめに
めちゃくちゃややこしいタイトルになってしまいましたがこんなイメージです
環境
開発PC:Mac Mojave
RaspBerry Pi4 メモリ4GB
SDカード16GBUbuntu19.10をSDに書き込む
https://ubuntu.com/download/raspberry-pi
から64bit版をダウンロードしてbalenaEtcharを使って書き込みました。dockerをUbuntuにインストール
Ubuntuにsshかなんかで入って
sudo apt-get install docker-ceでインストールできます。
sudo usermod -aG docker ubuntuでsudo使わなくてもdockerできます。
ベースイメージを作る
開発PCでpython scriptを書いてからDockerfileでdockerImageにコピーしてもよいのですが、わかりづらかったので一度Dockerfileでベースを作ってから、コンテナに入りそのコンテナからイメージを作ります。
Dockerfileです
Raspberry Pi で TensorFlow する Docker 環境構築
を参考にさせていただきました、この記事ほぼほぼ上を参考にしました。
rpi.gpioを入れたかったのですが入れ方よくわかんなかったのでそのまま記述してますFROM resin/rpi-raspbian:stretch RUN echo "deb http://mirrordirector.raspbian.org/raspbian/ stretch main contrib non-free rpi firmware" > /etc/apt/sources.list RUN apt-get update -y RUN apt-get install -y --no-install-recommends \ vim git less wget \ build-essential \ libatlas-base-dev \ python3-pip python3-dev python3-setuptools\ python3-scipy python3-h5py \ libraspberrypi-bin \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* RUN pip3 install --upgrade pip setuptools RUN pip3 install rpi.gpioあとはbuildします
docker build -t rpi/rpi:0.1 .docker imagesでできてることを確認しましょう!できてるはずです。
コンテナを起動してpythonスクリプトを書く
先ほど作ったイメージからコンテナを起動します。
bash
docker run --name rpi_test -ti --privileged rpi/rpi:0.1 /bin/bash
で、コンテナの中に入るので、欲しいものをインストールします。nano派なのでsudo apt-get update sudo apt-get install nanohomeに移動してscriptを作ります。
この辺ほんとは作業ディレクトリとか設定するのがよいのだろうな・・・cd home nano test.py
GPIO17にLEDをさして適当にLチカするプログラムです。
import RPi.GPIO as GPIO import time GPIO.setmode(GPIO.BOARD) GPIO.setup(11, GPIO.OUT) while True: GPIO.output(11, True) time.sleep(2) GPIO.output(11, False) time.sleep(2)スクリプトを保存したら
exitでログアウトしてください
docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES e700d8490be8 rpi/rpi:0.1 "/usr/bin/entry.sh /…" 11 minutes ago Exited (0) 6 seconds ago rpi_testコンテナが止まっています。このコンテナからイメージを作るには
docker commit rpi_test rpi/rpi:0.2 docker images REPOSITORY TAG IMAGE ID CREATED SIZE rpi/rpi 0.2 50f7f2a406e7 2 minutes ago 622MBscriptが保存してあるイメージができました。
起動
sudo docker run --privileged -it -d --name rpi -w /home rpi/rpi:0.2 python3 test.pyバックグラウンドでコンテナが動いてLちかし続けます!
止めたい時は docker stop rpiでとまりますまとめ
センサーデータとかもそのうちとりたい
- 投稿日:2020-02-11T16:20:16+09:00
【 Docker+Nginx+Django+RDS】WEBアプリができるまで②
前置き
独学で、子供の成長アプリを作った時のことを、記録として残していきます。
間違っているところなどあれば、ご連絡お願いします。
①Djangoのようこそページへたどり着くまでGoal
NginxからDjangoのようこそページへたどり着く
uWsgiの設定
NginxとDjangoを繋ぐためのuwsgiを入れる。
requirement.txtDjango==2.2.2 psycopg2==2.8.4 uwsgi==2.0.17docker-composeも変更する。
nginxのコンテナ設定追加と、webコンテナのコマンド・ポート設定を変更する。docker-compose.ymlversion: "3" services: nginx: #ここを追加する image: nginx:1.13 ports: - "8000:8000" volumes: - ./nginx/conf:/etc/nginx/conf.d - ./nginx/uwsgi_params:/etc/nginx/uwsgi_params - ./static:/static #staticも繋ぐ depends_on: - web db-postgres: image: postgres web: build: ./web volumes: - ./src:/code expose: - "8000" #ポートフォワードじゃなくて普通にport空ける depends_on: - db-postgres command: uwsgi --socket :8000 --module mysite.wsgi #uwsgi起動に変更Nginxの設定
こんなファイル体系になるように、nginx配下を作っていく
. ├── docker-compose.yml ├── nginx │ ├── conf │ │ └── mysite_nginx.conf │ └── uwsgi_params ├── src │ ├── manage.py │ ├── mysite │ │ ├── __init__.py │ │ ├── __pycache__ │ │ │ ├── __init__.cpython-37.pyc │ │ │ ├── settings.cpython-37.pyc │ │ │ ├── urls.cpython-37.pyc │ │ │ └── wsgi.cpython-37.pyc │ │ ├── settings.py │ │ ├── urls.py │ │ └── wsgi.py │ └── static └── web ├── Dockerfile └── requirements.txtconfファイルで、静的レスポンスと動的レスポンスを振り分けてるイメージ。
conf/mysite_nginx.confupstream django { ip_hash; server web:8000; } server { listen 8000; server_name 127.0.0.1; charset utf-8; client_max_body_size 75M; location /static { alias /static; } location / { uwsgi_pass django; include /etc/nginx/uwsgi_params; } }uwsgi_paramsは、そのまま。
uwsgi_paramsuwsgi_param QUERY_STRING $query_string; uwsgi_param REQUEST_METHOD $request_method; uwsgi_param CONTENT_TYPE $content_type; uwsgi_param CONTENT_LENGTH $content_length; uwsgi_param REQUEST_URI $request_uri; uwsgi_param PATH_INFO $document_uri; uwsgi_param DOCUMENT_ROOT $document_root; uwsgi_param SERVER_PROTOCOL $server_protocol; uwsgi_param REQUEST_SCHEME $scheme; uwsgi_param HTTPS $https if_not_empty; uwsgi_param REMOTE_ADDR $remote_addr; uwsgi_param REMOTE_PORT $remote_port; uwsgi_param SERVER_PORT $server_port; uwsgi_param SERVER_NAME $server_name;Dockerで立ち上げよう
requirement.txtを直したら、buildしておく。
docker-compose buildDockerを起動
docker-compose uplocalhost:8000へアクセスする
Djangoのrunserverと同じように、初期画面が表示されたら成功!
参考
- 投稿日:2020-02-11T16:20:16+09:00
【 Docker+Nginx+Django+RDS】WEBアプリができるまで②NginxでDjangoのようこそページへたどり着く
前置き
独学で、子供の成長アプリを作った時のことを、記録として残していきます。
間違っているところなどあれば、ご連絡お願いします。
①Djangoのようこそページへたどり着くまで
②NginxでDjangoのようこそページへたどり着くまで<--今ここ
③カスタムユーザーを作ってadminにたどり着く
④ログインログアウトをしよう
⑤ユーザー登録(サインイン)機能を作ろう
⑥ユーザーごとのデータ登録できるようにする〜CRU編
⑦ユーザーごとのデータ登録できるようにする〜削除編Goal
NginxからDjangoのようこそページへたどり着く
uWsgiの設定
NginxとDjangoを繋ぐためのuwsgiを入れる。
requirement.txtDjango==2.2.2 psycopg2==2.8.4 uwsgi==2.0.17docker-composeも変更する。
nginxのコンテナ設定追加と、webコンテナのコマンド・ポート設定を変更する。docker-compose.ymlversion: "3" services: nginx: #ここを追加する image: nginx:1.13 ports: - "8000:8000" volumes: - ./nginx/conf:/etc/nginx/conf.d - ./nginx/uwsgi_params:/etc/nginx/uwsgi_params - ./static:/static #staticも繋ぐ depends_on: - web db-postgres: image: postgres web: build: ./web volumes: - ./src:/code expose: - "8000" #ポートフォワードじゃなくて普通にport空ける depends_on: - db-postgres command: uwsgi --socket :8000 --module mysite.wsgi #uwsgi起動に変更Nginxの設定
こんなファイル体系になるように、nginx配下を作っていく
. ├── docker-compose.yml ├── nginx │ ├── conf │ │ └── mysite_nginx.conf │ └── uwsgi_params ├── src │ ├── manage.py │ ├── mysite │ │ ├── __init__.py │ │ ├── __pycache__ │ │ │ ├── __init__.cpython-37.pyc │ │ │ ├── settings.cpython-37.pyc │ │ │ ├── urls.cpython-37.pyc │ │ │ └── wsgi.cpython-37.pyc │ │ ├── settings.py │ │ ├── urls.py │ │ └── wsgi.py │ └── static └── web ├── Dockerfile └── requirements.txtconfファイルで、静的レスポンスと動的レスポンスを振り分けてるイメージ。
conf/mysite_nginx.confupstream django { ip_hash; server web:8000; } server { listen 8000; server_name 127.0.0.1; charset utf-8; client_max_body_size 75M; location /static { alias /static; } location / { uwsgi_pass django; include /etc/nginx/uwsgi_params; } }uwsgi_paramsは、そのまま。
uwsgi_paramsuwsgi_param QUERY_STRING $query_string; uwsgi_param REQUEST_METHOD $request_method; uwsgi_param CONTENT_TYPE $content_type; uwsgi_param CONTENT_LENGTH $content_length; uwsgi_param REQUEST_URI $request_uri; uwsgi_param PATH_INFO $document_uri; uwsgi_param DOCUMENT_ROOT $document_root; uwsgi_param SERVER_PROTOCOL $server_protocol; uwsgi_param REQUEST_SCHEME $scheme; uwsgi_param HTTPS $https if_not_empty; uwsgi_param REMOTE_ADDR $remote_addr; uwsgi_param REMOTE_PORT $remote_port; uwsgi_param SERVER_PORT $server_port; uwsgi_param SERVER_NAME $server_name;Dockerで立ち上げよう
requirement.txtを直したら、buildしておく。
docker-compose buildDockerを起動
docker-compose uplocalhost:8000へアクセスする
Djangoのrunserverと同じように、初期画面が表示されたら成功!
参考
- 投稿日:2020-02-11T15:57:01+09:00
【 Docker+Nginx+Django+RDS】WEBアプリができるまで①
前置き
独学で、子供の成長アプリを作った時のことを、記録として残していきます。
開発環境はMac。DBはRDSで Postgresを使います。
間違っているところなどあれば、ご連絡お願いします。Goal
Djangoのようこそページへたどり着く
前提
DockerとDocker-composeは開発環境に入っていること。
MacだとHomebrewを使えば簡単に入る。Dockerの設定
たぶん、あまり捻りは無い。
DockerfileFROM python:3.7 ENV PYTHONUNBUFFERED 1 RUN mkdir /code WORKDIR /code ADD requirements.txt /code/ RUN pip install -r requirements.txt ADD . /code/requirement.txtDjango==2.2.2 psycopg2==2.8.4docker-compose.ymlversion: "3" services: db-postgres: image: postgres web: container_name: web build: ./web #webの中のmanage.pyを見に行くよ volumes: - ./src:/code #srcの中をコンテナ内の/codeに繋げるよ ports: - "8000:8000" #portフォワード depends_on: - db-postgres #dbは上で指定したやつ command: python manage.py runserver 0.0.0.0:8000 #Django開発用サーバ起動この時点のファイルはこんな感じ。
. ├── docker-compose.yml ├── src └── web ├── Dockerfile └── requirements.txtDjangoの設定
まずはプロジェクトを作る。
docker-compose.ymlがあるとこで実行。
最後のピリオドを忘れないこと。docker-compose run web django-admin startproject mysite .すると、以下のようにDjangoの各ファイルが作られる。
. ├── docker-compose.yml ├── src │ ├── manage.py │ └── mysite │ ├── __init__.py │ ├── settings.py │ ├── urls.py │ └── wsgi.py └── web ├── Dockerfile └── requirements.txtDjango上でDBの設定をする。
今回はAWSにPostgresのRDSを作ってあるので、そこに繋げる。
ローカルからAWSのRDSへアクセスする場合は、RDSの設定で
パブリックアクセシビリティを「はい」にしないとダメなので注意。setting.pyDATABASES = { 'default': { 'ENGINE': 'django.db.backends.postgresql', 'NAME': 'postgres', 'USER': '*****', 'PASSWORD': '*****', 'HOST': '*****', 'PORT': 5432, } }さあ確認
Dockerを起動
docker-compose up
- 投稿日:2020-02-11T15:57:01+09:00
【 Docker+Nginx+Django+RDS】WEBアプリができるまで①Djangoのようこそページへたどり着くまで
前置き
独学で、子供の成長アプリを作った時のことを、記録として残していきます。
開発環境はMac。DBはRDSで Postgresを使います。
間違っているところなどあれば、ご連絡お願いします。①Djangoのようこそページへたどり着くまで<--今ここ
②NginxでDjangoのようこそページへたどり着くまで
③カスタムユーザーを作ってadminにたどり着く
④ログインログアウトをしよう
⑤ユーザー登録(サインイン)機能を作ろう
⑥ユーザーごとのデータ登録できるようにする〜CRU編
⑦ユーザーごとのデータ登録できるようにする〜削除編Goal
Djangoのようこそページへたどり着く
前提
DockerとDocker-composeは開発環境に入っていること。
MacだとHomebrewを使えば簡単に入る。Dockerの設定
たぶん、あまり捻りは無い。
DockerfileFROM python:3.7 ENV PYTHONUNBUFFERED 1 RUN mkdir /code WORKDIR /code ADD requirements.txt /code/ RUN pip install -r requirements.txt ADD . /code/requirement.txtDjango==2.2.2 psycopg2==2.8.4docker-compose.ymlversion: "3" services: db-postgres: image: postgres web: container_name: web build: ./web #webの中のmanage.pyを見に行くよ volumes: - ./src:/code #srcの中をコンテナ内の/codeに繋げるよ ports: - "8000:8000" #portフォワード depends_on: - db-postgres #dbは上で指定したやつ command: python manage.py runserver 0.0.0.0:8000 #Django開発用サーバ起動この時点のファイルはこんな感じ。
. ├── docker-compose.yml ├── src └── web ├── Dockerfile └── requirements.txtDjangoの設定
まずはプロジェクトを作る。
docker-compose.ymlがあるとこで実行。
最後のピリオドを忘れないこと。docker-compose run web django-admin startproject mysite .すると、以下のようにDjangoの各ファイルが作られる。
. ├── docker-compose.yml ├── src │ ├── manage.py │ └── mysite │ ├── __init__.py │ ├── settings.py │ ├── urls.py │ └── wsgi.py └── web ├── Dockerfile └── requirements.txtDjango上でDBの設定をする。
今回はAWSにPostgresのRDSを作ってあるので、そこに繋げる。
ローカルからAWSのRDSへアクセスする場合は、RDSの設定で
パブリックアクセシビリティを「はい」にしないとダメなので注意。setting.pyDATABASES = { 'default': { 'ENGINE': 'django.db.backends.postgresql', 'NAME': 'postgres', 'USER': '*****', 'PASSWORD': '*****', 'HOST': '*****', 'PORT': 5432, } }さあ確認
Dockerを起動
docker-compose up
- 投稿日:2020-02-11T15:24:14+09:00
あんまり頭を使わないで構築するRailsAPI on Docker
すでに多くの記事がありますが、APIモードのRailsがDocker上で動くところまで、必要最小限の情報でまとめてみます。RubyMineでの設定も最後にちょっと書いてあります。
参考: 公式 Quickstart: Compose and Rails
ファイル準備
アプリケーションを作成したいディレクトリで設定ファイルを作成します。
$ touch Gemfile Gemfile.lock Dockerfile docker-compose.yml entrypoint.sh各ファイルの内容を埋めます。
Gemfile
source 'https://rubygems.org' gem 'rails', '~>6'
Dockerfile
FROM ruby:2.6 RUN apt-get update -qq && apt-get install -y nodejs postgresql-client RUN mkdir /app WORKDIR /app COPY Gemfile /app/Gemfile COPY Gemfile.lock /app/Gemfile.lock RUN bundle install COPY . /app # Add a script to be executed every time the container starts. COPY entrypoint.sh /usr/bin/ RUN chmod +x /usr/bin/entrypoint.sh ENTRYPOINT ["entrypoint.sh"] EXPOSE 3000 # Start the main process. CMD ["rails", "server", "-b", "0.0.0.0"]entrypoint.sh#!/bin/bash set -e # Remove a potentially pre-existing server.pid for Rails. rm -f /app/tmp/pids/server.pid # Then exec the container's main process (what's set as CMD in the Dockerfile). exec "$@"docker-compose.ymlversion: '3' services: db: image: postgres volumes: - ./tmp/db:/var/lib/postgresql/data ports: - 5438:5432 app: build: . command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'" volumes: - .:/app ports: - "3008:3000" depends_on: - db environment: LANG: C.UTF-8 BUNDLE_PATH: vendor/bundle RAILS_ENV: development DB_USER: postgres DB_NAME: app_db DB_PASSWORD: password DB_HOST: dbファイルの内容ちょっと補足
- Rubyは2.6、Railsは6系です
- 公開するポートはrailsが3008、postgresqlが5438です
- 後述するdatabase.ymlではwebサービスに設定した環境変数を読んで設定を行います
作る
$ docker-compose run app rails new . --force --no-deps --database=postgresql --api$ docker-compose buildしたら一通りRailsが必要なファイルを作ってくれます。自動で作成された
database.yml
を編集します。database.ymldefault: &default adapter: postgresql encoding: unicode host: <%= ENV['DB_HOST'] %> username: <%= ENV['DB_USER'] %> password: <%= ENV['DB_PASSWORD'] %> pool: 5 development: <<: *default database: app_development test: <<: *default database: app_test$ docker-compose run app rails db:create動作確認
$ docker-compose uphttp://localhost:3008/で動いてるはずです。
補足: RubyMine使ってる方、ちょろっと設定編
インタプリタの設定
Preferences > Languages & Frameworks > Ruby SDK and Gems
で、追加 > Docker Composeをクリック > OK
読み込みが完了したら、該当のSDKをチェックし、APPLYして完了です。補完がいい感じに効くようになります。
DB設定
Database > 追加 > Data Source > PostgreSQL
- Host: localhost
- Port: 5438
- User: postgres
- Password: password
で接続ができるはずです。うまくいかない場合は、タブから適切なスキーマを選択したり、DBが起動しているか確かめたりしてください。
- 投稿日:2020-02-11T14:32:04+09:00
docker-composeでLaravel環境構築
はじめに
dockerによってLaravelの環境構築を行います。laradockは使いません。
以前はlaradockを使っていましたが、
・プロジェクトが必要以上に大きくなる
・dockerの知識がほとんどなくても環境構築できでしまう
これらの点からlaradockを使わない方法を試していきます。laradockはとりあえずdockerでLaravelを立ち上げたい方、知識が全くない方は使うとよいかもしれません。
以前にlaradockによるlaravelの環境構築の記事を書きましたので、laradockを使いたい方はこちらを→https://qiita.com/rope19181/items/da31dc2cd6097315fa10
環境・仕様
・nginx上でPHP実行環境を構築し、Laravel新規プロジェクト作成。
・mysqlとwebGUIとしてphpmyadminも使えるようにします。環境、仕様は以下の通りです。
Docker version 19.03.5
docker-compose version 1.24.1
php 7.2-fpm
mysql 5.7構成
最終的にこのような構成となります。
project
├ docker
│ ├ db
│ │ ├ data
│ │ ├ my.conf
│ │ └ sql
│ │
│ ├ nginx
│ │ └ default.conf
│ │
│ └ php
│ ├ Dockerfile
│ └ php.ini
├ myapp
└ docker-compose.yml今回はprojectをルートディレクトリとしています。
./myapp
はlaravelのプロジェクトを置くディレクトリとなります。docker-compose.ymlの作成
docker-composeでは複数のコンテナを管理、構築するためにdocker-compose.ymlに各コンテナの環境設定を定義します。
docker-compose.ymlversion: '3' services: php: container_name: php build: ./docker/php volumes: - ./myapp/:/var/www nginx: image: nginx container_name: nginx ports: - 80:80 volumes: - ./myapp/:/var/www - ./docker/nginx/default.conf:/etc/nginx/conf.d/default.conf depends_on: - php db: image: mysql:5.7 container_name: db-host environment: MYSQL_ROOT_PASSWORD: root MYSQL_DATABASE: database MYSQL_USER: docker MYSQL_PASSWORD: docker TZ: 'Asia/Tokyo' command: mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci volumes: - ./docker/db/data:/var/lib/mysql - ./docker/db/my.cnf:/etc/mysql/conf.d/my.cnf - ./docker/db/sql:/docker-entrypoint-initdb.d ports: - 3306:3306 phpmyadmin: image: phpmyadmin/phpmyadmin container_name: phpmyadmin environment: - PMA_ARBITRARY=1 - PMA_HOST=db-host - PMA_USER=docker - PMA_PASSWORD=docker links: - db ports: - 8080:80 volumes: - /sessions各項目について簡単に説明します。
詳しくは公式リファレンスを見てみましょう。https://docs.docker.com/compose/compose-file/image
ローカルやリモートにあるイメージを指定します。ローカルにない場合リモートからpullされます。
今回はnginx、mysql、phpmyadminはローカルのイメージを指定しています。
build
イメージをbuildする際に参照するファイルを指定します。
要はイメージを構築する場合はこちらを使うわけです。またbuildとimageを両方指定することはできません。今回はphpコンテナの構築でDockerfileを使うため、そのディレクトリのパスを指定しています。
volumes
ボリュームとしてマウントするパスを指定します。
ホスト:コンテナ
またアクセスモードを読み取り専用する場合ホスト:コンテナ:ro
とします。今回はアクセスモードを指定しないため
rw
となり、書き込みもできるボリュームです。ports
公開(expose)するポートをします。
ホスト:コンテナ
とします。ポートが被る場合はここを変更します。
links
コンテナにリンクさせるサービス名を指定します。
注)コンテナ名(container_name
)ではなくサービス名今回はphpmyadminのコンテナにてdb-hostコンテナのサービス名である
db
を指定しています。phpのDockerfileの作成
phpコンテナではLaravelの実行のためにcomposerのインストールします。
公式(https://getcomposer.org/download/) から下の部分を#Composer install
のところに貼り付けます。DockerfileFROM php:7.2-fpm COPY php.ini /usr/local/etc/php/ RUN apt-get update \ && apt-get install -y zlib1g-dev mariadb-client \ && docker-php-ext-install zip pdo_mysql #Composer install RUN php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" RUN php -r "if (hash_file('sha384', 'composer-setup.php') === 'c5b9b6d368201a9db6f74e2611495f369991b72d9c8cbd3ffbc63edff210eb73d46ffbfce88669ad33695ef77dc76976') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;" RUN php composer-setup.php RUN php -r "unlink('composer-setup.php');" RUN mv composer.phar /usr/local/bin/composer ENV COMPOSER_ALLOW_SUPERUSER 1 ENV COMPOSER_HOME /composer ENV PATH $PATH:/composer/vendor/bin WORKDIR /var/www RUN composer global require "laravel/installer"PHP設定ファイルの作成
docker/php/にphp.iniを作成します。
php.ini[Date] date.timezone = "Asia/Tokyo" [mbstring] mbstring.internal_encoding = "UTF-8" mbstring.language = "Japanese"nginxの設定ファイルの作成
docker/nginxにdefault.confを作成します。
default.confserver { listen 80; index index.php index.html; root /var/www/public; location / { root /var/www/public; index index.html index.php; } location ~ \.php$ { try_files $uri =404; fastcgi_split_path_info ^(.+\.php)(/.+)$; fastcgi_pass php:9000; fastcgi_index index.php; include fastcgi_params; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; fastcgi_param PATH_INFO $fastcgi_path_info; } }dockerの起動
下記コマンドでdockerを起動させます。初回は結構時間がかかります。
docker-composeではup
によってイメージの構築、コンテナの起動を行いますが、初回はキャッシュがないため--build
をつけます。-d
オプションによってバックグラウンドで起動させます。$ docker-compose up -d --buildlaravelプロジェクトの作成
まずはphpコンテナに入ります。コンテナに入る場合は
exec
コマンドを実行します。$ docker-compose exec php bashlaravelの新規プロジェクトを作成します。
composer create-project --prefer-dist "laravel/laravel myapplaravelプロジェクトの環境設定
laravelプロジェクト内の.envやconfig/detabase.phpのデータベースの設定を変更します。
DB名、ユーザ名、パスワードなどを先ほどdocker-compose.ymlに設定した値に変更しましょう。動作確認
nginx、php
http://localhost にアクセスしlaravelのホームページが表示されればOK。
mysql、phpmyadmin
phpコンテナに入り、migrateします。
$ docker-compose exec php bash $ php artisan migratehttp://localhost:8080 にアクセスし、phpmyadminが立ち上がりDB内に2つのテーブルが作成されていれば成功。
最後に
laradockも簡単にできますが、少しの知識があればこちらの方法のほうがシンプルで分かりやすいと思います。
laradockを使いたい方はこちら→https://qiita.com/rope19181/items/da31dc2cd6097315fa10
- 投稿日:2020-02-11T14:32:04+09:00
dockerでLaravel環境構築
はじめに
dockerによってLaravelの環境構築を行います。できるだけシンプルな構成を目指します。laradockは使いません。
自分は以前laradockを使っていましたが、
・プロジェクトが必要以上に大きくなる
・dockerの知識がほとんどなくても環境構築できでしまう
これらの点からlaradockを使わない方法を試していきます。laradockはとりあえずdockerでLaravelを立ち上げたい方、知識が全くない方は使うとよいかもしれません。
以前にlaradockによるlaravelの環境構築の記事を書きましたので、laradockを使いたい方はこちらを→https://qiita.com/rope19181/items/da31dc2cd6097315fa10
環境・仕様
・nginx上でPHP実行環境を構築し、Laravel新規プロジェクト作成。
・mysqlとwebGUIとしてphpmyadminも使えるようにします。・環境、仕様は以下の通りです。
Docker version 19.03.5
docker-compose version 1.24.1
php 7.2-fpm
mysql 5.7構成
最終的にこのような構成となります。
project
├ docker
│ ├ db
│ │ ├ data
│ │ ├ my.conf
│ │ └ sql
│ │
│ ├ nginx
│ │ └ default.conf
│ │
│ └ php
│ ├ Dockerfile
│ └ php.ini
├ myapp
└ docker-compose.yml今回はprojectをルートディレクトリとしています。
./myapp
はlaravelのプロジェクトを置くディレクトリとなります。docker-compose.ymlの作成
docker-composeでは複数のコンテナを管理、構築するためにdocker-compose.ymlに各コンテナの環境設定を定義します。
docker-compose.ymlversion: '3' services: php: container_name: php build: ./docker/php volumes: - ./myapp/:/var/www nginx: image: nginx container_name: nginx ports: - 80:80 volumes: - ./myapp/:/var/www - ./docker/nginx/default.conf:/etc/nginx/conf.d/default.conf depends_on: - php db: image: mysql:5.7 container_name: db-host environment: MYSQL_ROOT_PASSWORD: root MYSQL_DATABASE: database MYSQL_USER: docker MYSQL_PASSWORD: docker TZ: 'Asia/Tokyo' command: mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci volumes: - ./docker/db/data:/var/lib/mysql - ./docker/db/my.cnf:/etc/mysql/conf.d/my.cnf - ./docker/db/sql:/docker-entrypoint-initdb.d ports: - 3306:3306 phpmyadmin: image: phpmyadmin/phpmyadmin container_name: phpmyadmin environment: - PMA_ARBITRARY=1 - PMA_HOST=db-host - PMA_USER=docker - PMA_PASSWORD=docker links: - db ports: - 8080:80 volumes: - /sessions各項目について簡単に説明します。
詳しくは公式リファレンスを見てみましょう。https://docs.docker.com/compose/compose-file/image
ローカルやリモートにあるイメージを指定します。ローカルにない場合リモートからpullされます。
今回はnginx、mysql、phpmyadminはリモートのイメージを指定しています。
build
イメージをbuildする際に参照するファイルを指定します。
要はイメージを構築する場合はこちらを使うわけです。またbuildとimageを両方指定することはできません。今回はphpコンテナの構築でDockerfileを使うため、そのディレクトリのパスを指定しています。
volumes
ボリュームとしてマウントするパスを指定します。
ホスト:コンテナ
またアクセスモードを読み取り専用する場合ホスト:コンテナ:ro
とします。今回はアクセスモードを指定しないため
rw
となり、書き込みもできるボリュームです。ports
公開(expose)するポートをします。
ホスト:コンテナ
とします。ポートが被る場合はここを変更します。
links
コンテナにリンクさせるサービス名を指定します。
注)コンテナ名(container_name
)ではなくサービス名今回はphpmyadminのコンテナにてdb-hostコンテナのサービス名である
db
を指定しています。phpのDockerfileの作成
phpコンテナではLaravelの実行のためにcomposerのインストールします。
公式(https://getcomposer.org/download/) から下の部分を#Composer install
のところに貼り付けます。DockerfileFROM php:7.2-fpm COPY php.ini /usr/local/etc/php/ RUN apt-get update \ && apt-get install -y zlib1g-dev mariadb-client \ && docker-php-ext-install zip pdo_mysql #Composer install RUN php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" RUN php -r "if (hash_file('sha384', 'composer-setup.php') === 'c5b9b6d368201a9db6f74e2611495f369991b72d9c8cbd3ffbc63edff210eb73d46ffbfce88669ad33695ef77dc76976') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;" RUN php composer-setup.php RUN php -r "unlink('composer-setup.php');" RUN mv composer.phar /usr/local/bin/composer ENV COMPOSER_ALLOW_SUPERUSER 1 ENV COMPOSER_HOME /composer ENV PATH $PATH:/composer/vendor/bin WORKDIR /var/www RUN composer global require "laravel/installer"PHP設定ファイルの作成
docker/php/にphp.iniを作成します。
php.ini[Date] date.timezone = "Asia/Tokyo" [mbstring] mbstring.internal_encoding = "UTF-8" mbstring.language = "Japanese"nginxの設定ファイルの作成
docker/nginxにdefault.confを作成します。
default.confserver { listen 80; index index.php index.html; root /var/www/public; location / { root /var/www/public; index index.html index.php; } location ~ \.php$ { try_files $uri =404; fastcgi_split_path_info ^(.+\.php)(/.+)$; fastcgi_pass php:9000; fastcgi_index index.php; include fastcgi_params; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; fastcgi_param PATH_INFO $fastcgi_path_info; } }dockerの起動
下記コマンドでdockerを起動させます。初回は結構時間がかかります。
docker-composeではup
によってイメージの構築、コンテナの起動を行いますが、初回はキャッシュがないため--build
をつけます。-d
オプションによってバックグラウンドで起動させます。$ docker-compose up -d --buildlaravelプロジェクトの作成
まずはphpコンテナに入ります。コンテナに入る場合は
exec
コマンドを実行します。$ docker-compose exec php bashlaravelの新規プロジェクトを作成します。
composer create-project --prefer-dist "laravel/laravel myapplaravelプロジェクトの環境設定
laravelプロジェクト内の.envやconfig/detabase.phpのデータベースの設定を変更します。
DB名、ユーザ名、パスワードなどを先ほどdocker-compose.ymlに設定した値に変更しましょう。動作確認
nginx、php
http://localhost にアクセスしlaravelのホームページが表示されればOK。
mysql、phpmyadmin
phpコンテナに入り、migrateします。
$ docker-compose exec php bash $ php artisan migratehttp://localhost:8080 にアクセスし、phpmyadminが立ち上がりDB内に2つのテーブルが作成されていれば成功。
docker-composeコマンド小まとめ
よく使うものをまとめます。
up
コンテナの構築、起動をします。
似たものとしてイメージの構築のみを行う
build
、コンテナの起動のみを行うstart
などがありますが、docker-composeではとりあえずup
をしとけばいいと思います。
Dockerfileを更新した時などはbuild
しましょう。$ docker-compose up -d $ docker-compose up -d --build #初回の起動などbuildもしたいときdown
up
で作られたものを削除します。down
だけだとコンテナとネットワークの削除を行います。オプションによってその範囲を指定します。$ docker-compose down
コンテナ、イメージ、ボリュームそしてネットワークすべてを削除したい場合は
$ docker-compose down --rmi all --volumesexec
起動中のコンテナでコマンドを実行
$ docker-compose exec コンテナ名 実行するコマンド $ docker-compose exec コンテナ名 bash #コンテナに入る場合最後に
laradockも簡単にできますが、少しの知識があればこちらの方法のほうがシンプルで分かりやすいと思います。
laradockを使いたい方はこちら→https://qiita.com/rope19181/items/da31dc2cd6097315fa10
- 投稿日:2020-02-11T13:40:35+09:00
reCAPTCHAのサイトを毎日自動スクレイピングする(1/7)
RPAによる業務改善をするプロジェクトに参加していたのですが…
RPAツールがあまりにも高額(720万円/年)だったため、pythonで置き換えました。
※少々フェイクあり要件
概要
- とあるサイトにログインし、
特定ワード
で検索した結果をダウンロードし、データを取得します。- INPUTとなる
特定ワード
はAmazonS3に毎日置かれます。- 取得したデータを整形し、S3上の別位置に置くことがOUTPUTとなります。
- なお、サイトの会社に連絡し、スクレイピングの許可はいただいています。
難点
- その特定サイトにはログイン時にreCAPTCHAが仕込まれています(!)
- その特定サイトではAnglarJSが使われています
- ダウンロードするときには、クリックをしなければなりません
- 毎日実行できるようにしなければなりません
技術選定
(かなり試行錯誤したのですが)結論だけ書くと、下記のようになりました。
なかなか大変だった…
- pythonのseleniumを使ってデータを取得
- 「2captcha」というサービスを使ってreCAPTCHAを突破
- Dockerコンテナ化。Xvfbを使って仮想ディスプレイを立ち上げ、アプリを実行
- AWS batchを使って毎日起動させる
Story
下記のような流れで開発していきました。
- python環境構築
- サイトのスクレイピング機構を作る
- ダウンロードしたファイル(xls)を加工し、最終成果物(csv)を作成するようにする
- S3からのファイルダウンロード / S3へのファイルアップロードをつくる
- 2captchaを実装
- Dockerコンテナで起動できるようにする
- AWS batchに登録
それぞれのページでQiitaは分けようかと思います。
1.環境構築
まずは通常のpython環境の構築です!
今回は
python3.6.3
を使います。ターミナルはデフォルトのbash
です。
今後 複数のバージョンのpythonを使いたくなることを考え、pyenv
とpyenv-virtualenv
を使って専用のpython環境を作ろうと思います。pyenvのインストール
git clone https://github.com/pyenv/pyenv.git ~/.pyenv echo 'export PYENV_ROOT="$HOME/.pyenv"' >> ~/.bash_profile echo 'export PATH="$PYENV_ROOT/bin:$PATH"' >> ~/.bash_profile echo -e 'if command -v pyenv 1>/dev/null 2>&1; then\n eval "$(pyenv init -)"\nfi' >> ~/.bash_profile続いて、python3.6をインストール
pyenv install 3.6.3
pyenv-virtualenvのインストール
必要なのかどうかよくわかっていないのですが、誰かが良いと言っていたので pyenv-virtualenvも入れます。
git clone https://github.com/pyenv/pyenv-virtualenv.git $(pyenv root)/plugins/pyenv-virtualenv echo 'eval "$(pyenv virtualenv-init -)"' >> ~/.bash_profileそして今回のためのvirtualenvを作成。
pyenv virtualenv 3.6.3 myprojectディレクトリの作成
今回のプロジェクトで使うディレクトリを準備しておきます。
(このへんも本当は試行錯誤しながら変えていったのですが)mkdir myproject cd myproject pyenv local myprojectこの
myproject
以下に、下記のようにフォルダとファイルを準備します。├── app │ ├── drivers seleniumドライバーを置く │ └── source │ └── scraping.py 処理 └── tmp ├── files │ └── download スクレイピングでダウンロードしたファイルを置く └── logs ログ(seleniumログなど)続きはまた。
- 投稿日:2020-02-11T13:40:35+09:00
reCAPTCHAのサイトを毎日自動スクレイピングする (1/7: python環境構築)
RPAによる業務改善をするプロジェクトに参加していたのですが…
RPAツールがあまりにも高額(720万円/年)だったため、pythonで置き換えました。
そのときの仕事をまとめたものです。要件
概要
- とあるサイトにログインし、
特定ワード
で検索した結果をダウンロードし、データを取得します。- INPUTとなる
特定ワード
はAmazonS3に毎日置かれます。- 取得したデータを整形し、S3上の別位置に置くことがOUTPUTとなります。
- なお、サイトの会社に連絡し、スクレイピングの許可はいただいています。
難点
- その特定サイトにはログイン時にreCAPTCHAが仕込まれています(!)
- その特定サイトではAnglarJSが使われています
- ダウンロードするときには、クリックをしなければなりません
- 毎日実行できるようにしなければなりません
技術選定
(かなり試行錯誤したのですが)結論だけ書くと、下記のようになりました。
なかなか大変だった…
- pythonのseleniumを使ってデータを取得
- 「2captcha」というサービスを使ってreCAPTCHAを突破
- Dockerコンテナ化。Xvfbを使って仮想ディスプレイを立ち上げ、アプリを実行
- AWS batchを使って毎日起動させる
Story
下記のような流れで開発していきました。
- python環境構築
- サイトのスクレイピング機構を作る
- ダウンロードしたファイル(xls)を加工し、最終成果物(csv)を作成するようにする
- S3からのファイルダウンロード / S3へのファイルアップロードをつくる
- 2captchaを実装
- Dockerコンテナで起動できるようにする
- AWS batchに登録
それぞれのページでQiitaは分けようかと思います。
1.環境構築
まずは通常のpython環境の構築です!
今回は
python3.6.3
を使います。ターミナルはデフォルトのbash
です。
今後 複数のバージョンのpythonを使いたくなることを考え、pyenv
とpyenv-virtualenv
を使って専用のpython環境を作ろうと思います。pyenvのインストール
git clone https://github.com/pyenv/pyenv.git ~/.pyenv echo 'export PYENV_ROOT="$HOME/.pyenv"' >> ~/.bash_profile echo 'export PATH="$PYENV_ROOT/bin:$PATH"' >> ~/.bash_profile echo -e 'if command -v pyenv 1>/dev/null 2>&1; then\n eval "$(pyenv init -)"\nfi' >> ~/.bash_profile続いて、python3.6をインストール
pyenv install 3.6.3
pyenv-virtualenvのインストール
必要なのかどうかよくわかっていないのですが、誰かが良いと言っていたので pyenv-virtualenvも入れます。
git clone https://github.com/pyenv/pyenv-virtualenv.git $(pyenv root)/plugins/pyenv-virtualenv echo 'eval "$(pyenv virtualenv-init -)"' >> ~/.bash_profileそして今回のためのvirtualenvを作成。
pyenv virtualenv 3.6.3 myprojectディレクトリの作成
今回のプロジェクトで使うディレクトリを準備しておきます。
(このへんも本当は試行錯誤しながら変えていったのですが)mkdir myproject cd myproject pyenv local myprojectこの
myproject
以下に、下記のようにフォルダとファイルを準備します。├── app │ ├── drivers seleniumドライバーを置く │ └── source │ └── scraping.py 処理 └── tmp ├── files │ └── download スクレイピングでダウンロードしたファイルを置く └── logs ログ(seleniumログなど)続きはこちら。
https://qiita.com/kamyu1201@github/items/a07c7d175c051b8ab4c0
- 投稿日:2020-02-11T12:08:29+09:00
railsの環境構築 その1
はじめに
これからdockerで開発環境の構築からAWSデブロイまで数回に分けてまとめてみます
私が使った有料教材
Dockerについて
Dockerについては下記のサイトを見てください。
* 入門 DockerDockerでrailsの環境構築
参考
作業手順
ディレクトリを作成しそこで作業
mkdir ror_app
Dockerfileを作成
、Dockerfile内に下記を記述
touch DockerfileFROM ruby:2.5 RUN apt-get update -qq && apt-get install -y nodejs postgresql-client RUN mkdir /myapp WORKDIR /myapp COPY Gemfile /myapp/Gemfile COPY Gemfile.lock /myapp/Gemfile.lock RUN bundle install COPY . /myapp # Add a script to be executed every time the container starts. COPY entrypoint.sh /usr/bin/ RUN chmod +x /usr/bin/entrypoint.sh ENTRYPOINT ["entrypoint.sh"] EXPOSE 3000 # Start the main process. CMD ["rails", "server", "-b", "0.0.0.0"]コマンド
FROM: ベースとなるDocker Image を指定します。 COPY: Docker内へホストのファイル/ディレクトリをコピーします。 COPY は基本的に2つの引数を設定します。1つ目はホスト側のディレクトリ、2つ目はDocker側のディレクトリです。 RUN: Docker内でコマンドを実行します。 CMD: Docker起動時にデフォルトで実行されるコマンドを定義します。 WORKDIR: Dockerfileでコマンドを実行する際に基準となるディレクトリを設定します。 EXPOSE: コンテナ起動時に公開することを想定されているポートを記述します ENTRYPOINT: 指定されたコマンドを実行します。
GemfileとGemfile.lockの作成
- rails newするときに必要
touch Gemfile && touch Gemfile.locksource 'https://rubygems.org' gem 'rails', '~>5'
entrypoint.sh
を作成
- 特定のserver.pidファイルが存在するときにサーバーが再起動しないようにするRails固有の問題を修正するエントリポイントスクリプトを提供します。このスクリプトは、コンテナが開始されるたびに実行されます。
touch entrypoint.sh
entrypoint.sh#!/bin/bash set -e # Remove a potentially pre-existing server.pid for Rails. rm -f /myapp/tmp/pids/server.pid # Then exec the container's main process (what's set as CMD in the Dockerfile). exec "$@"
docker-compose.yml
の作成touch docker-compose.ymldocker-compose.ymlversion: '3' services: db: image: postgres ports: - '5432:5432' volumes: - postgresql-data:/var/lib/postgresql/data web: build: . command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'" volumes: - .:/myapp ports: - "3000:3000" depends_on: - db volumes: postgresql-data: driver: localdocker上でrailsのファイルを作成
docker-compose run web rails new . --force --no-deps --database=postgresql # rails new . とすることで、カレントディレクトリ配下にrailsファイルが作成される --forceで事前に作成したファイルを上書き、今回はGemfile等gem 'pg'のバージョン変更
gem 'pg', '~> 0.20.0'イメージをbuild
docker-compose buildすると
ror_app_web:latestになります。databese.ymlの書き換え
default: &default adapter: postgresql encoding: unicode host: db username: postgres password: pool: 5 # host,username,passwordを追加しました。 hostをdbにすることでdocker-compose.ymlのdbを指定 本番環境や開発環境でdatabase関連のエラーが良く出てきます。 特に本番環境をAWSにするとproduciton:配下を書き換えないといけません。データベースの作成
docker-compose run web rake db:createサービスの起動
docker-compose up # 終了はCtrl+Cブラウザで
localhost:3000
にアクセスするとrailsの初期画面が出現したと思います。docker-composeコマンドまとめ
ローカル環境 docker環境 rails s docker-compose up rails db:migrate docker-compose run web rails db:migrate bundle install docker-compose run web bundle install rails g model モデル名 docker-compose run web rails g model モデル名 git push
railsの初期画面が出てきたらgit pushしておきましょう。
お疲れさまでした。
- 投稿日:2020-02-11T12:00:06+09:00
DockerのImageの保存先を変更する方法
動機
Surface Book 2 15''をWindows 10とUbuntu 18.04のデュアルブートにしたが、Surface Book 2 15''はSSD 256GBモデルのため、容量が足りない。そこで、仕方なく256GBのSDカードを購入。
このSDカードにDockerのイメージを保存できたら最高なんじゃないか??
という発想です。
方法
色んなサイトの方法を試したが、なかなかうまく行く方法が見つからなかったので、成功した方法の共有メモとして残しておきます。
/etc/init.d/docker stopmv /var/lib/docker /[新しいDockerのイメージ保存先]ln -s /[新しいDockerのイメージ保存先]/ /var/lib/docker/etc/init.d/docker start