20200218のdockerに関する記事は14件です。

SEしてるけど実はあんまりコード書いたことないんだよねって人に捧ぐ、Rails on Dockerハンズオン vol.8 - Sign up -

はじめに

第8回目です。前回、Userモデルを作り上げユーザー詳細ページを作成するところまでいきました。
今回は、ユーザーをUIから登録すべくSign upページを作っていきたいと思います。

いままではRailsコンソールからモデルを操作してユーザーを作成していましたが、今回からはMVCを連携させてユーザーを作成させるようにします。

Sign upページの作成

今回はSign upページでユーザーのnameemailpasswordを入力してもらい、登録完了後そのユーザーのユーザー詳細ページに遷移させます。
まずは、このアプリを実現するために必要なファイルや設定を作成していきます。

今回、Sign upページにアクセスするためのRouting、Action、Viewと、実際にUserをDBに登録するRouting、Actionが必要になります。

Scaffoldを思い出してみてください。
あの時、新規にユーザーを作成するには/users/newのページにアクセスしており、newアクションがありました。
そこで登録ボタンを押すと、/usersのURLにPOSTメソッドでリクエストが飛びcreateアクションがDBにユーザーを保存していました。

では、これらの設定を元にRouting、Action、Viewをコーディングしていきましょう!

Routing

まず、users#newusers#createへのルーティングを作成します。
しかし、Sign upページと呼んでいるのですから/users/newのようなURLでは少しカッコつきません。
今回は/sign_upにGETリクエストしたときにusers#newにルーティングされるように定義していきましょう。
users#createへのルーティングも/sign_upにPOSTリクエストしたときに定義します。

config/routes.rb
Rails.application.routes.draw do
  root 'static_pages#home'

  get   '/sign_up',  to: 'users#new',    as: :sign_up
  post  '/sign_up',  to: 'users#create', as: :create_user
  resources :users, only: [:show]
end

追加した行は以下の通り。

get   '/sign_up',  to: 'users#new',    as: :sign_up
post  '/sign_up',  to: 'users#create', as: :create_user

読んでいきましょう。
これは、resoucesroot以外のルーティングの書き方としてとても一般的なものです。
static_pages#homeを作った時もこんな感じのルーティングが自動生成されたことを覚えていますか?

書き方は以下の通りです。

[method] '[path]', to: '[controller_name]#[action_name]', as: '[route_name]'

このコーディングで
pathmethodでリクエストされた場合、controller_nameコントローラーのaction_nameアクションにルーティングする。アプリのコードとしてroute_name_pathと表現された場合は、このルーティングを表すことと定義する。」
ということをRailsアプリケーションに定義立てています。

route_nameはシンボル(:sign_upのように:を頭につけてる文字)で書いてます。シンボルは複数あっては困るものなどに使用するそうです(「Rubyのシンボルと文字列の違い | UX MILK)。
ルートの名前も重複しては困りますので、シンボルを使っています。

ルーティングの設定はこれで完了です。次はコントローラーに新しくアクションを用意しましょう。

Action

次にUserコントローラーにnewアクションを追加します。

app/controllers/users_controller.rb
class UsersController < ApplicationController
  def show
    @user = User.find(params[:id])
  end

  # newアクションを追加する
  def new
    @user = User.new
  end

  # createアクションをとりあえず追加しておく
  def create
  end
end

newアクションでは、インスタンス変数@userUser.newを代入しています。
これはView側でUserモデルを扱ってフォームを作っていくために空のモデルが必要なのです。
詳しくはView側で説明しますので、ひとまずそのままコピペでお願いします。

createアクションはとりあえず定義だけしておきます。処理はのちのち。

View

ではでは、Viewファイルを作っていきます。
newアクションでrenderされるViewなのでnew.html.erbファイルを作成します。

# touch app/views/users/new.html.erb
app/views/users/new.html.erb
<div class="container">
  <h1 class="my-5">Sign up</h1>

  <%= form_with model: @user, url: create_user_path, local: true do |form| %>
    <div class="form-group">
      <%= form.label :name %>
      <%= form.text_field :name, class: "form-control" %>
    </div>
    <div class="form-group">
      <%= form.label :email %>
      <%= form.text_field :email, class: "form-control" %>
    </div>
    <div class="form-group">
      <%= form.label :password %>
      <%= form.password_field :password, class: "form-control" %>
    </div>

    <div class="form-group mt-5">
      <%= form.submit "Sign up!", class: "form-control btn btn-primary" %>
    </div>
  <% end %>
</div>

構文自体はそこまで難しくないですね。http://localhost:3000/sign_upにアクセスしてみましょう。
image.png
なかなかぽいページになったんじゃないでしょうか?
password_confirmationは使わないことにしました。「登録フォームにおけるパスワード確認用の入力欄は必要か | UX MILK」の記事を参考にしまして。
いくつかrubyコードが埋め込まれているので説明していきますね。

form_with

こういうのをビューヘルパー(View helper)っていいます。
form_with』ヘルパーはformタグを生成してくれるヘルパーです。
modelをオプションに指定することでそのモデルオブジェクトのためのフォームを作ってくれるようになります。上のコードのようにブロック形式でform変数を使うことで、例えばform.input_field :nameとするだけで@username属性に関するinputフィールドを作ってくれるようになるので、これは超便利です。

urlオプションはフォームの送信先を定義します。create_user_pathroutes.rbusers#createへのルーティングにつけた名前ですね。
このようにコーディングすることでcreateアクションにフォームを送信できるようになります。

また、local: trueオプションも付けています。form_withヘルパーではデフォルトでAjax通信でフォームをポストします。つまり、画面遷移なしでフォームをリクエストするんです。これはこれで便利な場面もあって今時な感じなんですが、今回はユーザーの新規作成が成功したらそのユーザーのマイページに画面遷移させたいので、local: trueオプションでAjax通信をオフにしています。
ちなみに『Ajax』は『エイジャックス』または『アジャックス』と読むそうです。僕は『エイジャックス』って読んでます。

今回のform_withは実際にクライアントに返却される場合は以下のように変換されて返却されています。

<%= form_with model: @user, url: create_user_path, local: true do |form| %>
  ...
<% end %>

<form action="/sign_up" accept-charset="UTF-8" method="post">
  <input type="hidden" name="authenticity_token" value="xxxxxxxxxx">
  ...
</form>

authenticity_tokenが第3回の時に少し触れた、CSRFトークンですね。XSSを防いでくれるやつです。

label

labelはラベルを付けてくれるやつです。ページで『お名前』とか『メールアドレス』と出ているやつのことです。

<%= form.label :name %>

<label for="user_name">お名前</label>

text_field

text_fieldinput type="text"タグでテキストフィールドを作り出してくれます。
使い方も簡単で、text_field [attribute_name], [options]なだけです。
今回はBootstrapのform-control classをオプションで付けてあげています。

<%= form.text_field :name, class: "form-control" %>

<input class="form-control" type="text" name="user[name]" id="user_name">

nameidも勝手に付けてくれているのがいいですよね。
ここでid="user_name"を付与してくれているので、labelfor="user_name"と紐付けができるようになっています。

password_field

passwordを入力するフォームのところではpassword_fieldを使いました。
これはtext_fieldと似ていますが、input type="password"タグのフィールドを作ってくれるやつです。

<%= form.password_field :password, class: "form-control" %>

<input class="form-control" type="password" name="user[password]" id="user_password">

input type="password"はパスワード用の入力フィールドを作り出せるので、入力した文字が『●』でマスク化されるようになります。

submit

submitタグはフォーム送信用のボタンを作ってくれるタグです。input type="submit"を作ってくれるんですね。

<%= form.submit "Sign up!", class: "form-control btn btn-primary" %>

<input type="submit" name="commit" value="Sign up!" class="form-control btn btn-primary" data-disable-with="Sign up!">

HTMLの方にあるdata-disable-withはダブルクリック抑止のためのパラメータですね。RailsのERBを使えばこういった必要だけどついつい忘れてしまいがちなHTMLオプションも自動で付与してくれるので積極的に利用するといいと思います。

これ以外にもいろいろなヘルパーが用意されているので、HTMLのあのタグはどうやって書けばいいのか、と思った時は直接HTMLタグを書くのではなく一度Railsでの書き方をググってみるといいでしょう!

これでViewがいい感じに作れました。次は「Sign up!」ボタンを押した時の処理をコーディングしていきます。

Createアクションをコーディング

ここまででnew.html.erbのフォームからcreateアクションにフォームをリクエストできるようになりました。
ここからはcreateアクションでリクエストされたフォームのデータからDBにUserを登録できるようにします。

では、先ほど"とりあえず"作ったcreateアクションをコーディングしていきます。

app/controllers/users_controller.rb
class UsersController < ApplicationController
  ...
  def create
    @user = User.new(user_params)
    if @user.save
      redirect_to @user
    else
      render :new
    end
  end

  private
    def user_params
      params.require(:user).permit(:name, :email, :password)
    end
end

はい。初見だとけっこう複雑に見えるんじゃないかなーと思います。
1行ずつみていきましょう。

@user = User.new(user_params)

まず、@userUser.new(user_params)を代入しています。
user_paramsとは何でしょう?

private
  def user_params
    params.require(:user).permit(:name, :email, :password)
  end

user_paramsはここで定義されている関数ですね。
params.require(:user).permit(:name, :email, :password)の部分は『ストロングパラメーター(Strong parameter)』と呼ばれています。Railsが用意しているこのストロングパラメーターは意図しないデータのPOSTを防いでくれます。

params.require([model_name]).permit([attribute_names])

これでリクエスト内のパラメーター(params)からmodel_nameモデルに紐づくデータのみを抽出し、さらにその中からattribute_namesに指定されているデータのみを抽出して返却します。
それ以外のデータはドロップします。これによってユーザーのいたずらで意図しないデータが作られてしまうことを防ぐということです。
例えばモデルにはこっそりadminフラグみたいなものがあって1のユーザーはなんでもできちゃう権限を持っているとします。いたずらっ子のユーザーがそれに気づいてSign upページにdeveloper toolsとかを使ってadmin=1のフォームを追加してきたりしたとします。ここでストロングパラメーターを使っていない場合はadmin=1でユーザーが作成されてしまいアプリが乗っ取られてしまう、みたいなことが起きちゃうんですね。
ストロングパラメーターはそういった意図しないフォームからのリクエストを無視するようになるので、安全なアプリケーションの作成に不可欠なものなのです。

今回はUserモデルのnameemailpassword以外のデータは無視することを定義しています。

また、この関数はprivateより下に書かれています。これはクラス内からのみアクセスできる関数であることを定義しています。

さて、ここまでで@useruser_paramsで抽出されたデータが初期値として設定されたUserモデルオブジェクトが代入される、ということになります。

if @user.save
  redirect_to @user
else
  render :new
end

次は、この@usersaveして条件分岐しています。ModelのCRUDの時にも話がでてきましたがsaveメソッドはバリデーションを通りデータをDBに保存できた場合trueを、バリデーションに引っかかった場合falseを返却します。
その結果を元にtrueの場合はredirect_to @userfalseの場合はrender :newが実行されます。

redirect_to @user

redirect_toメソッドは引数にGETメソッドでリダイレクトします。
引数には@userを指定してます。これは、user_path(@user)と同義です。user_pathshowメソッドにつけられている名前です。@userには今DB保存が成功したUserモデルオブジェクトが入っていますので、今保存したユーザーのユーザー詳細ページに遷移させています。

render :new

falseの場合はこちらが実行されます。
renderメソッドは指定したアクションのViewファイルをレンダリングするメソッドです。(いろいろと使い方はありますが、今回はこんな説明で)
通常アクションは自分の名前と同じViewファイルをレンダリングするようになっていますが、renderメソッドを使うことで別のアクションのViewファイルをレンダリングするようにすることもできます。
この場合は、newアクションのViewファイルがレンダリングされます。さらに@userには今falseになったUserモデルオブジェクトが入った状態でnew.html.erbに引き渡されます。
そのため、レンダリングされたとしても今入力してた属性データは再表示されるはずだし、@user.errorsの中にはsaveに失敗した理由も残っているわけです。new.html.erb側を少し更新すればエラーメッセージを表示することもできます。

new.html.erbでエラーメッセージを表示できるように更新

ということでnew.html.erbを更新して、saveでエラーが発生した場合はerrors.full_messagesが表示されるようにしてみましょう。

app/views/users/new.html.erb
...
<h1 class="my-5">Sign up</h1>

<%# ここから追加 %>
<% if @user.errors.any? %>
  <div class="alert alert-danger">
    <ul class="mb-0">
      <% @user.errors.full_messages.each do |msg| %>
        <li><%= msg %></li>
      <% end %>
    </ul>
  </div>
<% end %>
<%# ここまで追加 %>

<%= form_with model: @user, url: create_user_path, local: true do |form| %>
...

これも少し説明します。

<% if @user.errors.any? %>
  ...
<% end %>

まず一番外側を囲っている部分です。
any?メソッドは対象が"空"でないかどうかを検証して「空でない」場合にtrueを返します。
今回は@user.errorsに対してany?メソッドを適用しているので、validationによるエラーがある場合にtrueになります。

<div class="alert alert-danger">
  <ul class="mb-0">
    <% @user.errors.full_messages.each do |msg| %>
      <li><%= msg %></li>
    <% end %>
  </ul>
</div>

@user.errors.any?trueの場合にはこの部分が表示されることになります。
ulタグはリストを表示するためのタグですね。liタグがulタグのアイテムとして『・』を頭につけた箇条書き形式で表示されるようになっています。
liタグは以下のコードに囲まれています。

<% @user.errors.full_messages.each do |msg| %>
  <li><%= msg %></li>
<% end %>

@user.errors.full_messagesはバリデーションの回でも使ったので覚えている人が多いと思います。
バリデーションでエラーが起きた時にユーザーにそのまま見せれるようなエラーメッセージが配列で格納されていたやつです。
それをeachでひとつずつ取り出し、msgに格納して、@user.errors.full_messagesがなくなるまでブロックの中の処理を繰り返します。
ブロックの中の処理は<li><%= msg %></li>だけなので、ここで@user.errors.full_messagesのエラーメッセージを一つずつliタグで囲って表示してあげていますね。
結果として、全てのエラーメッセージが箇条書きの形式で表示されるようになると期待できます。

ここまでできたら実際に動作確認をしてみましょう!

動作確認(エラー編)

まずはエラーの場合の動作確認をしてみます。
以前Userモデルのバリデーションを確認したときのように、いくつかのエラーケースでエラーメッセージが表示されるかどうかを確認していきましょう。

存在性チェック

Userモデルではnameemailpassword全てが入力必須(presence)な項目です。
何も入力していなければエラーになるはず。
image.png
実際に何も入力せずに「Sign up!」ボタンを選択したページがこちらです。
「お名前」「メールアドレス」「パスワード」について、それぞれエラーが出ていてUserの作成がうまくいかなかったことがわかりますね。

文字数チェック

nameemailにはそれぞれ最大50文字、255文字の制限を設けていました。
それ以上の文字数でエラーになるか確認してみましょう。

51文字と256文字の文字列は↓をお使いください。

Name
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa

Email
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb@sample.com

image.png
文字数制限のエラーが出ていますね。
ちなみに「パスワード」には適当にパスワードを入力して「Sign up!」ボタンを選択したので「パスワード」に関するエラーは出ていないです。「パスワード」はtype="password"のフィールドを使っているので、エラー時に元の値が反映されないようになっているんですね。

フォーマットチェック

emailにはフォーマットの検証もいれていました。例えば「@」がない場合はメールアドレスとしておかしいのでちゃんとエラーが返却されるか確認してみましょう。
image.png
ちゃんとエラーになっていますね。

重複チェック

最後にemailの重複チェックもみてみましょう。

とりあえずjohn@sample.comemailをもつユーザーをRailsコンソールから作っておきます。
前回までに作ったユーザーなどがいればそれを使っていただいても構いません。

> User.create(name: "John Smith", email: "john@sample.com", password: "password")

ユーザーを作ったので、同じメールアドレスを大文字とかにしてもエラーになるかを確認してみましょう。

image.png
これもエラーになりましたね!

ここまでチェックすれば、バリデーションはちゃんと効いていて、バリデーションエラーが起きた時のエラーハンドリングも正常に動作していることが確認できましたね。

動作確認(正常編)

ではいよいよユーザーを実際にUIから作ってみましょう。

今回は以下のユーザーを作ってみます。

name: Taro Tanaka
email: taro@sample.com
password: taro1234

image.png
ユーザー詳細ページに遷移しましたね!さらに今入力したユーザー情報もちゃんと表示されています!

Welcomeメッセージを表示する

ユーザーが作成されて、ユーザー詳細ページにも遷移できたのですが何か物足りないですね。
そうだ、Welcomeメッセージがないんだ。

ユーザー詳細ページは、Welcomeページというわけではないので今のまま装飾していけばいいのですが、会員登録した時に限ってでも「ようこそ!」みたいなメッセージがほしいですよね。

こういう一時的にページにメッセージを表示したいときに使えるRailsの機能としてflashがあります。
flashはControllerで設定してあげます。今回はcreateアクションでsaveに成功した時に表示されるようにしてみましょう。
createアクションを通ってshow.html.erbが表示された場合はWelcomeメッセージが表示されますが、通常show.html.erbが表示される場合はcreateアクションは通らずshowアクションだけを通るので、例えばリロードすればメッセージが消えるという寸法です。

app/controllers/users_controller.rb
...
def create
  @user = User.new(user_params)
  if @user.save
    # 以下の1行を追加する
    flash[:success] = "サインアップありがとう!"
    redirect_to user_path(@user)
  else
    render :new
  end
end
...

Controller側の設定はこんな感じです。flash[:success]のような形でメッセージのkeyを定義して= 文字列の形でvalueを与えます。

View側でこれを受け取って、flashがある場合だけ表示するように、show.html.erbも更新しましょう。

app/views/users/show.html.erb
<div class="container my-5">
  <%# ここから追加 %>
  <% flash.each do |msg_type, msg| %>
    <div class="alert alert-<%= msg_type %>"><%= msg %></div>
  <% end %>
  <%# ここまで追加 %>

  <%= @user.name %>
  <br>
  <%= @user.email %>
</div>

今回はflash[:success] = "サインアップありがとう!"と定義しているので、msg_typesuccessが、msgサインアップありがとう!が格納されます。
alert-<%= msg_type %>alert-successとなるわけですね。Bootstrapでalert-successのCSSが用意されているのでそれを適用するためにflashのkeyをsuccessにしていたわけです。
おんなじようにエラー系のメッセージにしたい場合はdangerにしてあげたりするとViewファイルをいじらなくてもエラーっぽいCSSを適用することができます。

では実際に試してみましょう。

次は花子さんを作ってみます。http://localhost:3000/sign_upにアクセスして以下のデータで登録してみましょう。

Name: Hanako Yamada
Email: hanako@sample.com
Password: hanako1234

image.png
Welcomeメッセージが表示されました!

この状態でリロードしてみてください。
image.png
メッセージが消えましたね。期待通りの動作です。

パスワードの表示非表示を制御する

あとそうだ。先ほどUX的にpassword_confirmationは使いませんという話をしました。
しかし、今のままではパスワードが非表示しか対応していないので、ユーザーは自分の思ったパスワードが正しく入力できているか確認できません。
ただ、周りから見えないようにするのが目的でマスク化しているので、常に平文にすることもUX的によろしくないでしょう。

ということで、「パスワードを表示する」チェックボックスを用意して、チェックが入っている間だけ表示されるようにしようと思います。
まず、new.html.erbにチェックボックスを追加します。
今回は@userに紐づかないフォーム要素になるのでcheck_box_tagを利用します。これはinput type="check_box"タグを作ってくれるやつです。

app/views/users/new.html.erb
...
<div class="form-group">
  <%= form.label :password %>
  <%= form.password_field :password, class: "form-control" %>      
</div>
<div class="form-check">
  <%= check_box_tag :visible_password, :visible, false, class: "form-check-input" %>
  <%= label_tag :visible_password, "パスワードを表示する" %>
</div>
...

...
<div class="form-group">
  <label for="user_password">パスワード</label>
  <input class="form-control" type="password" name="user[password]" id="user_password">      
</div>
<div class="form-check">
  <input type="checkbox" name="visible_password" id="visible_password" value="visible" class="form-check-input">
  <label for="visible_password">パスワードを表示する</label>
</div>
...

はい。これでチェックボックスを追加することができました。
image.png

ただ今のままではただのチェックボックスにチェックを入れたり外したりできるだけです。
このチェックボックスの状態に合わせて「パスワード」のフィールドをマスク化したりマスク解除したりしていきましょう。
どうやるかというと、今マスク化されているのはinputtypepasswordになっているからです。他のinputのフィールドは普通に文字が見えていますが、これはtypetextになっているからです。
なので、チェックボックスの状態に合わせて、チェックが入っている場合はtype="text"、チェックが入っていない場合はtype="password"と切り替えることができれば、パスワードの表示非表示を制御できるはずです。

これを実装するには、フロントエンドで動作するjavascript(jQuery)を使います。

ごちゃごちゃするといけないので、application.jsではなくファイルを追加してそちらでコーディングしていきましょう。

# touch app/javascript/packs/visible_password.js
app/javascript/packs/visible_password.js
$(function() {
  $("#visible_password").change(function() {
    type = "password"
    if ($(this).prop("checked")) { 
      type = "text"
    }
    $("#user_password").attr("type", type)
  })
})

まず、型として$(function() { ~ })で囲みまして、{}の中に処理をコーディングしていきます。

$("#visible_password").change(function() {
  ...
})

最初に$("#visible_password").changeと書きました。これはidvisible_passwordinputタグに変化が起きたときに処理されることを意味しています。こういった何かが起こったときに処理が実行されることを『トリガー』とか『発火する』とか言うので覚えておきましょう!
今回の場合はidvisible_passwordinputの変化をトリガーにする、とか、変化が起きた時に発火する、みたいな表現をするってことです。

type = "password"

処理の中をみると、まず最初にtype = "password"の変数定義をしていますね。

if ($(this).prop("checked")) { 
  type = "text"
}

その後、if ($(this).prop("checked"))の条件分岐を書いています。
$(this)はjQueryの特殊な書き方だと思いますが、トリガーの元になった要素のことです。
prop("checked")はその要素のチェックボックスがチェックされているかを取得するメソッドなので、$(this).prop("checked")visible_passwordのチェックボックスにチェックが入っていればtrue、そうでなければfalseになります。
つまりif文の部分は、「チェックボックスにチェックが入っていればtype変数を"text"に更新する」、ことが記述されています。

$("#user_password").attr("type", type)

最後に、$("#user_password")、つまりiduser_passwordの要素(パスワードのinputタグ)にattr("type", type)をしています。
attrはその要素の属性を取得したり更新したりするメソッドです。引数が2つの場合は1つ目の引数の属性を2つ目の引数の値に更新します。今回の場合はid="user_password"typeを変数typeに更新しています。

変数typeはデフォルトではpassword、チェックボックスにチェックが入っている場合はtextになるので、チェックボックスにチェックを入れるとパスワードが表示される仕組みです。

さて、実際に使えるようにするには、このファイルを読み込めるようにする必要があります。
このファイルはアプリケーションの全てのページで使うのではなく、Sign upページでのみ利用するようなファイルなので、new.html.erbで読み込みを定義してあげます。

app/views/users/new.html.erb
...
<%= javascript_pack_tag 'visible_password' %>

Sign upページをリロードして試してみましょう!

image.png

チェックなし??チェックあり

image.png

期待通りの動作になりましたね!

トップページからリンクさせる

最後にトップページの「Sign up now!」ボタンからSign upページにリンクを貼ってあげましょう。

前回時点ではapp/views/static_pages/home.html.erbの該当のボタンのコードは以下のようになっていますね。

app/views/static_pages/home.html.erb
...
<%= link_to "Sign up now!", "#", class: "btn btn-lg btn-primary mt-5" %>
...

この"#"の部分がリンク先になるので、これをルーティングで名前づけしたsign_up_pathに更新します。

app/views/static_pages/home.html.erb
...
<%= link_to "Sign up now!", sign_up_path, class: "btn btn-lg btn-primary mt-5" %>
...

はい、これで完了です!
トップページ(http://localhost:3000/)にアクセスして「Sign up now!」ボタンをクリックしてみましょう。ちゃんとSign upページに遷移するはずです。

後片付け

いつものように、次回に向けてデータを消します。

$ docker-compose down
$ docker-compose run --rm web rails db:migrate:reset

DBコンテナが立ち上がった状態だと思うのでdownさせます。

$ docker-compose down

まとめ

今回は、サインアップページとユーザー登録機能を作っていきました。
今までと比べると少し複雑なControllerのコーディングをしましたね。バリデーションの結果に合わせてエラーメッセージを表示させてみたり、登録完了後のウェルカムメッセージを表示させたりしてみました。

また、jQueryを使ってパスワードの表示非表示をクライアントサイドで変更するコーディングもやってみました。
jQuery(javascript)はWebアプリケーションを作る上では欠かせない技術ですのでぜひ覚えてくださいね。

さて、今回はユーザー登録はできたものの、サインイン(ログイン)してそのユーザー独自の機能を提供する、ということはできていません。例えば、今のまま開発を進めてしまっては誰もが全てのユーザーの情報をみれてしまったり更新できてしまったりします。
そこで次回は、セッション管理をすることでサインイン機能を実装してみようと思います。

では、次回も乞うご期待!ここまでお読みいただきありがとうございました!

Reference

Links

Vol.1 - Introduction -
Vol.2 - Hello, Rails on Docker -
Vol.3 - Scaffold, RESTful, MVC -
Vol.4 - Static pages -
Vol.5 - Model and CRUD -
Vol.6 - Model validation -
Vol.7 - Secure password -
・ Vol.8 - Sign up - ?この記事

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

Docker上でPHP拡張モジュール『GD』を有効化する

この記事ではDocker上でPHP拡張モジュール『GD』を有効化する方法をお伝えします。

どうしてGDの有効化が必要になったか

LaravelとVueを用いたSPAを勉強したく、『Vue + Vue Router + Vuex + Laravelで写真共有アプリを作ろう』に取り組んでいました。

(9)写真投稿APIでテストを実行した際に
Error: Call to undefined function Illuminate\Http\Testing\imagecreatetruecolor().
というエラーで行き詰まり、原因を調査したところPHP拡張モジュール『GD』の有効化が必要との情報にたどり着きました。

環境

version
PHP 7.4.1
Laravel 6.15.0
Docker for Mac 19.03.5

GD有効化の方法

DockerファイルにGDの有効化、必要なライブラリのインストールの部分を記載することでGDを有効化できます。
有効化した後、再ビルドすることで正しく動作するようになります。

まずはGDが使えない状態のDockerfileをご紹介します。

Dockerfile
FROM php:7.4.1-fpm

COPY install-composer.sh /
RUN apt-get update \
  && apt-get install -y wget git unzip libpq-dev \
  && : 'Install Node.js' \
  &&  curl -sL https://deb.nodesource.com/setup_12.x | bash - \
  && apt-get install -y nodejs \
  && : 'Install PHP Extensions' \
  && docker-php-ext-install -j$(nproc) pdo_pgsql \
  && : 'Install Composer' \
  && chmod 755 /install-composer.sh \
  && /install-composer.sh \
  && mv composer.phar /usr/local/bin/composer

WORKDIR /var/www/html/vuesplash

【参考】
Vue + Vue Router + Vuex + Laravelで写真共有アプリを作ろう (3) SPA開発環境とVue Router

これを下記のように変更します。

:Dockerfile
FROM php:7.4.1-fpm

COPY install-composer.sh /
RUN apt-get update \
  && apt-get install -y wget git unzip libpq-dev libfreetype6-dev libjpeg62-turbo-dev libpng-dev \
  && : 'Install Node.js' \
  && curl -sL https://deb.nodesource.com/setup_12.x | bash - \
  && apt-get install -y nodejs \
  && : 'Install PHP Extensions' \
  && docker-php-ext-install -j$(nproc) pdo_pgsql \
  && docker-php-ext-configure gd --with-freetype --with-jpeg \
  && docker-php-ext-install -j$(nproc) gd \
  && : 'Install Composer' \
  && chmod 755 /install-composer.sh \
  && /install-composer.sh \
  && mv composer.phar /usr/local/bin/composer

WORKDIR /var/www/html/vuesplash

変更点は3行です。


まずは5行目。

-  && apt-get install -y wget git unzip libpq-dev \
+  && apt-get install -y wget git unzip libpq-dev libfreetype6-dev libjpeg62-turbo-dev libpng-dev \

libfreetype6-devlibfreetyep6-devlibjpeg62-turbo-devlibpng-devの4つのライブラリを追加しています。

【参考】
docs/php at master · docker-library/docs


2点目の変更点は、変更後のファイル11行目に新たに一行挿入しています。

+  && docker-php-ext-configure gd --with-freetype --with-jpeg \

PHP7.4系ではdocker-php-ext-configureの引数が変更されているので注意が必要です。

【参考】
【PHP】Docker PHP7.4系でgdをインストールしてimagecreatefromjpegを使う
docs/php at master · docker-library/docs


最後に変更後のファイルの12行目のように、GDのインストールコマンドを追加して変更完了です。

+  && docker-php-ext-install -j$(nproc) gd \

【参考】
docs/php at master · docker-library/docs

まとめ

Laravelを勉強しているものの、基本的な言語仕様は同じだろうと高をくくっており、PHP自体の構成や拡張モジュールについては全く勉強しておらず、長時間ハマり続けてしまいました…

これを機に基本的な言語仕様や拡張モジュールについても勉強していければと思います。

また、長時間ハマり続けた別の理由として、古い情報がうまくいかない場合が多かったこともあるので、今回の記事のように苦労して解決したことは積極的に記事にしていきたいです。

まだまだPHPもLaravelもDockerも未熟なので、誤りがありましたらご指摘いただけると幸いです。

参考

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

docker-airflow で ログイン認証機能を設定する

スクリーンショット 2020-02-18 20.11.10.png

docker-airflowでログイン機能を設定したい人向け。
docker-airflowの場合、公式ドキュメントの手順通り実行すると、スクリプトでのユーザー登録でこける。

sqlite3.OperationalError: no such table: users

なので、自力でpasswordのハッシュ値生成して、直接ユーザー登録する必要があるので、手順をメモ。
CeleryExecutorの前提で書くけど、LocalExecutorでも同じだと思う。

1. airflow.conf を編集する

${PROJECT}/config/airflow.cnf[webserver] の項目のauthの設定(airflow-1.10.9ではL300あたり)を以下のようにする

~略~

[webserver]

~略^

# Set to true to turn on authentication:
# https://airflow.apache.org/security.html#web-authentication
authenticate = True
auth_backend = airflow.contrib.auth.backends.password_auth 

とりあえず、これでログイン必須になる。アクセスするとログイン画面は表示される。

2. パスワードのハッシュを取得

pythonスクリプトで取得する。ハッシュが取れればなんでもok。

# なければ、flask-bcryptをインストール
pip install flask-bcrypt

# pythonスクリプトを実行しハッシュを取得
python
>>> from flask_bcrypt import generate_password_hash
>>> generate_password_hash('your_password', 12)

# 出力されたハッシュ値をメモ

3. postgreSQLに直接ユーザー情報を登録する

直接postgreSQLでuser登録を行う。

# コンテナ一覧表示
docker ps

# postgreSQLのコンテナにsshログイン
docker exec -it ${PSQL_CONTAINER_ID} bash

# airflowユーザーでpsqlにログイン
psql -U airflow

# ユーザーをinsert
INSERT INTO users (username, email, superuser, password) VALUES ('your_name', 'your_email', True, '2で取得したハッシュ値');

これでdockerを再ビルドして、起動すればok。

参考

Fail to create user on users table on Docker Webserver Container

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

Dockerのログをrsyslogで出力する

概要

Dockerのログをrsyslogで出力する方法。

関連

環境

  • CentOS 7.7
  • rsyslog 8.24.0-41.el7_7.2

Dockerの設定

Dockerもしくはdocker-composeで、以下のパラメータを指定し、rsyslogにログが渡るようにする。
ログには、This is a example.という文字列が出力されるようにする。

設定内容 Docker docker-compose
syslogを使う --log-driver=syslog logging.driver
ログの種類の分類 --log-opt syslog-facility={facility名} logging.options.syslog-facility
ログやファイル名に使用する名前 --log-opt tag={任意の文字列} logging.options.tag

dockerコマンドの場合

$ sudo docker run --rm \
    --name sample-container \
    --log-driver=syslog \
    --log-opt syslog-facility=daemon \
    --log-opt tag=sample-app/{{.Name}}/{{.ID}} \
    -it ${Dockerイメージ名} \
    /bin/bash -c 'echo "This is a example."'

docker-composeの場合

docker-compose.yml
version: '3'
services:
  sample-container:
    image: sample-image
    command: 'echo "This is a example."'
    logging:
      driver: syslog
      options:
        syslog-facility: daemon
        tag: sample-app/{{.Name}}/{{.ID}}
変数 意味
{{.Name}} コンテナ名
{{.ID}} コンテナID

rsyslogの設定

rsyslogのtemplateという機能を使って、ログファイル名の指定をする。
/var/log/docker/sample-app_年月日.logというログファイル名になるようにする。

/etc/rsyslog.d/10-docker.conf

/etc/rsyslog.d/10-docker.conf
$template DockerLogs, "/var/log/docker/%programname%_%$year%%$month%%$day%.log"

if $syslogfacility-text == 'daemon' and $programname contains 'sample-app' then -?DockerLogs
& stop
変数 意味
$template templateを使うための宣言
DockerLogs template名。任意の文字列でOK
%programname% ログを出力しているプログラム名
$programname ログを出力しているプログラム名
$syslogfacility-text --log-opt syslog-facility もしくは logging.options.syslog-facility に指定した文字列

その他の変数は以下を参照
rsyslog.confの文法

if $syslogfacility-text == 'daemon' and $programname contains 'sample-app' then -?DockerLogs

$syslogfacility-textdaemonで、$programnamesample-appという文字列が含まれるときのみログを出す。
$syslogfacility-text--log-opt syslog-facility もしくは logging.options.syslog-facilityと、$programname--log-opt tag もしくは logging.options.tag と合わせる必要がある。

rsyslogのrestart

/etc/rsyslog.d/10-docker.confを変更したあとはrestartが必要。

$ sudo systemctl restart rsyslog

出力されるログ

Dockerもしくはdocker-composeを実行。
実際に出力されるログ。

  • Format: /var/log/docker/{%programname%}_{%$year%%$month%%$day%}.log
  • e.g.): /var/log/docker/sample-app_20200218.log

sample-app/{コンテナ名}/{コンテナID}の部分は、--log-opt tag もしくは logging.options.tag に指定した文字列。

/var/log/docker/sample-app_20200218.log
Feb 18 17:03:19 example-hostname sample-app/sample-container/{コンテナID}[{dockerdのプロセスID}]: This is a example.

コンテナIDのみを抽出してログファイル名にする

nomatch-Modeを使って、文字列を抽出してファイル名に使用する方法。
この方法を利用して、コンテナIDのみをログファイル名にしてみる。

rsyslogの設定

/etc/rsyslog.d/10-docker.confに以下のように設定する。

/etc/rsyslog.d/10-docker.conf
$template DockerLogs, "/var/log/docker/%syslogtag:R,ERE,1,FIELD:sample-app/sample-container/(.*)\[(.*)\]:--end%.log"

if $syslogfacility-text == 'daemon' and $programname contains 'sample-app' then -?DockerLogs
& stop

"/var/log/docker/%syslogtag:R,ERE,1,FIELD:sample-app/sample-container/(.*)\[(.*)\]:--end%.log"の部分の説明。

変数 意味
/var/log/docker/ ログファイルを出力する場所
%syslogtag% --log-opt tag もしくは logging.options.tag に指定した文字列。正規表現で抽出する対象文字列となる。
R nomatchを利用するときはここはR
ERE 正規表現のタイプ。BRE of ERE。EREはBREを拡張したモード。通常こちらを選択する。
1 submatchの番号
FIELD nomatchモードの指定。枠下参照。
sample-app/sample-container/(.*)\[(.*)\]: 「R」の前の文字列(この場合は%syslogtag%)からこの正規表現で文字列を抽出し、ファイル名に使用する
--end%.log 最後に必ず書く

今回の設定の場合は、%syslogtag%から正規表現sample-app/sample-container/(.*)\[(.*)\]:で抽出した文字列をファイル名として使用するということになる。

nomatchモードについて

正規表現でマッチするものがなかった場合に返す、デフォルトの文字列をModeによって指定する。
各Modeごとに返す文字列は以下のようになっているが、FIELDを指定した場合は、正規表現対象の文字列全てが返される。(この場合は%syslogtag%)

Mode Returned
DFLT "**NO MATCH**"
BLANK "" (empty string)
ZERO "0"
FIELD full content of original field

引用元:Property Replacer nomatch mode

今回のFIELDの正規表現の場合の挙動

%syslogtag% = sample-app/sample-container/f76f6af73829[27081]

から、以下の正規表現で抽出をする。

sample-app/sample-container/(.*)\[(.*)\]:

submatchは1を指定しているため、1つ目の(.*)のみが抽出される。
つまり、コンテナIDであるf76f6af73829が抽出される。

結果、以下がファイル名となる。

/var/log/docker/f76f6af73829.log

rsyslogのrestart

/etc/rsyslog.d/10-docker.confを変更したあとはrestartが必要。

$ sudo systemctl restart rsyslog

出力されるログ

Dockerもしくはdocker-composeを実行。
この設定で実際に出力されるログ。

  • Format: /var/log/docker/sample-app/{コンテナ名}/{コンテナID}.log
  • e.g.): /var/log/docker/f76f6af73829.log

sample-app/{コンテナ名}/{コンテナID}の部分は、--log-opt tag もしくは logging.options.tag に指定した文字列。

Feb 18 17:21:23 example-hostname sample-app/sample-container/f76f6af73829[27081]: This is a example.

正規表現にマッチしない場合

rsyslogの設定

/etc/rsyslog.d/10-docker.confの正規表現の一部を、sample-app_nomatchとしてマッチしないようにしてみる。

/etc/rsyslog.d/10-docker.conf
$template DockerLogs, "/var/log/docker/%syslogtag:R,ERE,1,FIELD:sample-app_nomatch/sample-container/(.*)\[(.*)\]:--end%.log"

if $syslogfacility-text == 'daemon' and $programname contains 'sample-app' then -?DockerLogs
& stop

rsyslogのrestart

/etc/rsyslog.d/10-docker.confを変更したあとはrestartが必要。

$ sudo systemctl restart rsyslog

出力されるログ

Dockerもしくはdocker-composeを実行。
この設定で実際に出力されるログ。

  • Format: /var/log/docker/sample-app/{コンテナ名}/{コンテナID}[{dockerdのプロセスID}].log
  • e.g.): /var/log/docker/sample-app/sample-container/de64dadc7e97\[27081\]\:.log

sample-app/{コンテナ名}/{コンテナID}の部分は、--log-opt tag もしくは logging.options.tag に指定した文字列。

Feb 18 17:21:23 example-hostname sample-app/sample-container/de64dadc7e97[27081]: This is a example

正規表現がマッチせず、FIELDをしているので、%syslogtag%の中身がそのままファイル名となる。
前述したように、DFLTの場合はファイル名が**NO MATCH**.logZEROの場合は0.logがファイル名となる。BLANKの場合はログファイルが出力されない。

参考

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

Dockerコマンド自分用メモ

dockerfileをビルドしてイメージを作成

docker-compose build

コンテナの起動(イメージがない場合は構築も)

docker-compose up -d

コンテナの停止

docker-compose stop

コンテナを停止して削除

docker-compose down

実行中のプロセス確認

docker ps

プロセス確認

docker ps -a

コンテナ全削除

docker rm -f `docker ps -a -q`

イメージ確認

docker images

イメージ全削除

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

laravelでのメール通知実装(SendGridAPI)

laravelでお問い合わせフォームを作成し、入力された内容をメールに通知する必要があったので、そこで学習したことを残しておきます。(WebAPI経由)

SendGrid APIKeyの取得

まずはSendGridのアカウントを作成する。
APIキーを発行する。発行ページがなかなかわかりづらいが、マイページの左側のメニューバーのSettings内にある。

スクリーンショット 2020-01-21 18.51.42.png

「CreateAPIKey」をクリックし、下記の画面に移ったら、APIキーに名前をつける。ここでは"laravel"。メールを送るだけなので、API Key Permissionsは Restrictedにしておく。
スクリーンショット 2020-01-21 18.54.40.png

ここもメールを送るだけならMail Sendのみで良い。Create&Viewへ。

スクリーンショット 2020-01-21 18.55.11.png

APIKeyが表示されるはず。クリックするだけでコピー可能。一度しか表示されないのでコピーを忘れずに。

スクリーンショット 2020-01-21 18.55.22.png

envファイルにAPIキー、メールドライバ(ここではSendGrid)、Fromアドレス、From名を記述する。
デフォルトではSendGridをドライバーに指定できないが、パッケージを作ってくださった方がいるので、composerを使ってインストール。
ありがたいドライバー様(問題あれば消します。)

MAIL_DRIVER=sendgrid
SENDGRID_API_KEY='コピーしたAPIKey'
MAIL_FROM_ADDRESS=送信元メールアドレス
MAIL_FROM_NAME="送信元の名前"

mailable作成

いよいよ実装部分。Mailableクラスを作成する。
php artisan make:mail SendGridSample
App/Mail/SendGridSampleというファイルができる。これを編集する
(忘れずに先ほどのありがたいドライバー様のuseも記述しておく。)
buildメソッドを作り、その中に処理を書いていく。

fromはenvファイルに書いているのでここに書かなくても良い。

SendGridSample.php
<?php

namespace App\Mail;

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;
use Sichikawa\LaravelSendgridDriver\SendGrid;

class SendGridSample extends Mailable
{
    use SendGrid;

    /**
     * Create a new message instance.
     *
     * @return void
     */
     // 引数で受け取ったデータ用の変数
     protected $contact;

    public function __construct($contact)
    {
        // 引数で受け取ったデータを変数にセット
        $this -> contact = $contact;
    }

    /**
     * Build the message.
     *
     * @return $this
     */
    public function build()
    {
        return $this
        ->view('emails.email') //呼び出すテンプレートを指定
        ->subject('お問い合わせ内容確認') //件名
        ->with(['contact' => $this -> contact]) //withオプションでセットしたデータをテンプレートに渡す
        ]);
    }
}



service.phpファイルに、envのSENDGRID_API_KEYの使用を記述する。

service.php
[  
    'sendgrid' => [
      'api_key' => env('SENDGRID_API_KEY')
   ]
];

メールテンプレート作成

お名前、メールアドレス、受信したお問い合わせ内容を返す簡単なもの。

  <div class="row">
    <h1>お問い合わせ内容を受け付けました。</h1>
  </div>

<br>
・お名前<br>
{{ $contact['name'] }}様<br>
<br>
・メールアドレス<br>
{{ $contact['email'] }}<br>
<br>
・お問い合わせ内容<br>
{!! nl2br(e($contact['post'])) !!}<br>

  <div class="row">
    <p>お問い合わせありがとうございました。</p>
  </div>

ContactControllerを編集

ContactController.php
<?php

// 作成したメールクラスをuseする
use Illuminate\Support\Facades\Mail;
use Sichikawa\LaravelSendgridDriver\SendGrid;



    public function send(Request $request)
    {
        \Mail::to($request -> email)
        ->send(new SendGridSample($request));

    }

これでお問い合わせ完了メールを送ることができる。

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

WordPressのソースをいじってみる(Docker調整編)

PHP出来るけど全然WordPressわからないので使ってみる(環境準備編)でサクッとDockerで環境を用意しました。
ただ、この方法だとWordPressのソースコードをいじることが難しいので少し修正します。

コンテナの停止

$ docker-compose down

これで今動いているコンテナを停止します。

docker-compose.yamlの書き換え

docker-compose.yaml
version: '2'

services:
   db:
     image: mysql:5.7
     container_name: test-wp-db
     volumes:
       - db_data:/var/lib/mysql
     restart: always
     environment:
       MYSQL_ROOT_PASSWORD: wordpress
       MYSQL_DATABASE: wordpress
       MYSQL_USER: wordpress
       MYSQL_PASSWORD: wordpress

   wordpress:
     image: wordpress:latest
     container_name: test-wp
     depends_on:
       - db
     ports:
       - "8000:80"
     restart: always
     environment:
       WORDPRESS_DB_HOST: db:3306
       WORDPRESS_DB_PASSWORD: wordpress
     volumes:
       - ./html:/var/www/html
volumes:
    db_data:

wordpress部分にvolumesを追加しています。
これによりローカルの./htmlにwordpressのソースがマウントされます。

コンテナの起動

$ docker-compose up -d

書き換えてみる

./html/index.phpの頭にexit;と書いてみます。
これでhttp://localhost:8000にアクセスするとワードプレスの画面でなく、真っ白画面になることが確認できたらOKです。

まとめ

DockerのWordPress環境で、WordPressのソースコードをいじれるようにしました。
これでさらにいろいろ試せますね。

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

dockerで環境構築する際にrails5.2.0環境以降でcredential.ymlを編集する方法

はじめに

初投稿です。よろしくお願いします。
忘備録として投稿します。

内容

docker-compose.ymlにvimを入れてインストールしたはずなのにcredential.ymlを編集しようとしても暗号化されたまま編集できませんでした。
他にも方法があるかもしれませんが、全くの初心者レベルの人が解決できると仮定してその道筋を投稿します。

開発環境

docker 19.03.5
docker-compose 1.25.4
rails 5.2.4.1
ruby 2.5.1
nginx 1.15.8
Mysql 5.7

docker-compose.yml

docker-compose.yml
version: '3'
services:
  app: &app_base
    build:
      context: .
    env_file:
      - ./environments/db.env
    command: bundle exec puma -C config/puma.rb
    volumes:
      - .:/webapp
      - public-data:/webapp/public
      - tmp-data:/webapp/tmp
      - log-data:/webapp/log
    depends_on:
      - db
    tty: true
    stdin_open: true

  db:
    image: mysql:5.7
    env_file:
      - ./environments/db.env
    volumes:
      - db-data:/var/lib/mysql
    ports:
      - "4306:3306"

  web:
    build:
      context: containers/nginx
    volumes:
      - public-data:/webapp/public
      - tmp-data:/webapp/tmp
    ports:
      - 80:80
    depends_on:
      - app
volumes:
  public-data:
  tmp-data:
  log-data:
  db-data:

Dockerfile

FROM ruby:2.5.1

# リポジトリを更新し依存モジュールをインストール
RUN apt-get update -qq && \
    apt-get install -y build-essential \
                       nodejs \
                       vim    

# ルート直下にwebappという名前で作業ディレクトリを作成(コンテナ内のアプリケーションディレクトリ)
RUN mkdir /webapp
WORKDIR /webapp

# ホストのGemfileとGemfile.lockをコンテナにコピー
ADD Gemfile /webapp/Gemfile
ADD Gemfile.lock /webapp/Gemfile.lock

# bundle installの実行
RUN bundle install

# ホストのアプリケーションディレクトリ内をすべてコンテナにコピー
ADD . /webapp

# puma.sockを配置するディレクトリを作成
RUN mkdir -p tmp/sockets

docker-compose build→up -d

コンテナをbuildしてvimインストールして起動したが、あれ? credential.yml編集できない...

なぜだ....

vimインストール出来ているか見てみよう!

作業ディレクトリでwhich vimコマンド

which vim
/usr/bin/vim

インストールはされているな.....どうやって起動すれば良いんだ

コンテナの中に入ってみよう

コンテナの中に入ればできるかも....

docker-compose exec app bash
                    ⬆️docker-composeのrails名

そうするとroot@というところに入るので以下のコマンドを打ちましょう

EDITOR=vim bin/rails credentials:edit

credentialに入れた!

credentials.yml.enc
#   aws:
#   access_key_id: 123
#   secret_access_key: 345

# Used as the base secret for all MessageVerifiers in Rails, including the one protecting cookies.
secret_key_base: 8be8e637d755f79c799048bed8be0c...

最後に

手順を書いてある記事が少なかったので少しでも参考になればと思います。
何かあればご意見ください!

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

FluentdとDockerとKinesis Firehose その2(fluent.conf作成編)

はじめに

前回の続きです。
fluent.confを書いてローカルからkinesis data firehoseに流し込むところまでです

前回からのDockerfileの修正点

公式をみていたら

"3. Customize Dockerfile to install plugins (optional)"の節を見落としていました。
fluent.confの場所をCOPYする場所が最後の方だったので、修正しました。

修正後はこんな感じになっています。(ほとんど公式通り)

FROM fluent/fluentd:v1.9-1

USER root

# install plugin
RUN apk add --update-cache --virtual .build-deps sudo build-base ruby-dev \
    && gem install fluent-plugin-kinesis -v 3.0.0 --no-document \
    && gem sources --clear-all \
    && apk del .build-deps \
    && rm -rf /var/cache/apk/* \
    /home/fluent/.gem/ruby/*/cache/*.gem

# set timezone (Alpine)
RUN apk --update-cache add tzdata && \
    cp /usr/share/zoneinfo/Asia/Tokyo /etc/localtime && \
    apk del tzdata && \
    rm -rf /var/cache/apk/*

COPY ./kinesis.conf /fluentd/etc/fluent.conf

USER fluent

つづいて、fluent.confですが下記の感じにしてみました

kinesis.conf
<source>
  @type  forward
  @id    input1
  @label @mainstream
  port   24224
</source>

<filter **>
  @type stdout
</filter>

<label @mainstream>
  <match kinesis.**>
    @type kinesis_firehose
    @id  output_docker1
    region ap-northeast-1
    aws_key_id your_aws_access_key_id
    aws_sec_key your_aws_secret_key
    delivery_stream_name test-kinesis-firehose-fluentd
    <buffer tag,time>
      @type file
      path /fluentd/log/kinesis
      timekey 30
      timekey_wait 30
      timekey_use_utc true
    </buffer>
    <format>
      @type json
    </format>
  </match>
  <match **>
    @type file
    @id   output1
    path         /fluentd/log/data.*.log
    symlink_path /fluentd/log/data.log
    append       true
    time_slice_format %Y%m%d
    time_slice_wait   10m
    time_format       %Y%m%dT%H%M%S%z
    <format>
      @type json
    </format>
  </match>
</label>

fluent.confの定義から抜粋すると

指定方法 説明
source インプットを定義する所
match アウトプットを定義する所
label 内部ルーティング用のアウトプットとフィルターのグループに関する定義
@include 他のファイルを含める命令
filter イベント処理のパイプラインを決定する命令

となっているので、読み方としては

入力

  • foward: tcp経由で来たイベントを取得する(内部でfluent-catを使用するfluent-loggerというライブラリなどで送信)
  • label: @mainstreamというlabelで定義されたoutputに紐づくことをさす
  • port: 24224で受け付けます
  • ip: 指定していないとデフォルト "0.0.0.0"

フィルター

  • stdout: 標準出力

2020-02-14T10:48:53+09:00 docker.test {"key":"value"}

こんな感じで処理される

出力

  • match kinesis.**: "kiensis.hoge"というイベントで出力しますよという定義
  • aws_key_id, aws_sec_key: kinesisにアクセスできる権限を持ったcredentialを定義(ECS等の場合はRole)
  • type: kinesis_firehose
  • delivery_stream_name: kinesis data firehoseのdelivery_stream_name
  • buffer: 出力ファイルを分割する定義. * path*を定義しないとkinesis_firehoseに飛ばしてくれない
  • timekey: 30秒たったらbufferとしてpath以下に吐き出す設定

は公式のまんまです。debug用としてデータが吐き出されるかみてるだけです。

ハマったのはbufferは設定したもののpathを設定してなかったので、ファイルが吐き出されずに、吸い取られる(flush)されることなく永遠にkinesis data firehoseに送り込まなかったことでした。

あと、confがイケてるかチェックするには
fluentd --dry-run -c confの場所
でできます。

/fluentd/log/kinesis $ echo '{"I say":"hello"}' | fluent-cat kinesis.test
/fluentd/log/kinesis $ echo '{"I say":"hello"}' | fluent-cat kinesis.test
/fluentd/log/kinesis $ echo '{"I say":"hello"}' | fluent-cat kinesis.test
/fluentd/log/kinesis $ echo '{"I say":"hello"}' | fluent-cat kinesis.test
/fluentd/log/kinesis $ pwd
/fluentd/log/kinesis
/fluentd/log/kinesis $ ls -la
total 16
drwxr-xr-x    6 fluent   nogroup        192 Feb 18 11:56 .
drwxr-xr-x   14 fluent   nogroup        448 Feb 18 11:51 ..
-rw-r--r--    1 fluent   nogroup         21 Feb 18 11:56 buffer.b59ed0d18a89e7ab3ccaa887b7ccfb789.log
-rw-r--r--    1 fluent   nogroup         86 Feb 18 11:56 buffer.b59ed0d18a89e7ab3ccaa887b7ccfb789.log.meta
-rw-r--r--    1 fluent   nogroup        126 Feb 18 11:56 buffer.b59ed0d197a62770a411b37f39a5d3f3a.log
-rw-r--r--    1 fluent   nogroup         86 Feb 18 11:56 buffer.b59ed0d197a62770a411b37f39a5d3f3a.log.meta
/fluentd/log/kinesis $ cat buffer.b59ed0d197a62770a411b37f39a5d3f3a.log
��{"I say":"hello"}
��{"I say":"hello"}
��{"I say":"hello"}
��{"I say":"hello"}
��{"I say":"hello"}
��{"I say":"hello"}

こんな感じでbufferが吐き出されて、30秒ほどまつと吸い取られます。

kinesisのモニタリングをみると

スクリーンショット 2020-02-18 13.10.56.png

s3をみると

スクリーンショット 2020-02-18 13.10.25.png

できてました。
次回はECSにのっけます。

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

【docker】mysqlのコンテナがバグで立ち上がらなくなった

状況

dockerでmysqlのコンテナが全く起動できなくなってしまいました。

db            | 2020-02-18 11:55:23+09:00 [Note] [Entrypoint]: Entrypoint script for MySQ
db            | 2020-02-18 11:55:23+09:00 [Note] [Entrypoint]: Switching to dedicated use
db            | 2020-02-18 11:55:23+09:00 [Note] [Entrypoint]: Entrypoint script for MySQ
db            | 2020-02-18T02:55:23.964250Z 0 [Warning] TIMESTAMP with implicit DEFAULT verver option (see documentation for more details).
db            | 2020-02-18T02:55:23.970525Z 0 [Note] mysqld (mysqld 5.7.29) starting as p
db            | 2020-02-18T02:55:23.987944Z 0 [Note] InnoDB: PUNCH HOLE support available
db            | 2020-02-18T02:55:23.988018Z 0 [Note] InnoDB: Mutexes and rw_locks use GCC
db            | 2020-02-18T02:55:23.988026Z 0 [Note] InnoDB: Uses event mutexes
db            | 2020-02-18T02:55:23.988030Z 0 [Note] InnoDB: GCC builtin __atomic_thread_
db            | 2020-02-18T02:55:23.988033Z 0 [Note] InnoDB: Compressed tables use zlib 1
db            | 2020-02-18T02:55:23.988037Z 0 [Note] InnoDB: Using Linux native AIO
db            | 2020-02-18T02:55:23.988249Z 0 [Note] InnoDB: Number of pools: 1
db            | 2020-02-18T02:55:23.988361Z 0 [Note] InnoDB: Using CPU crc32 instructions
db            | 2020-02-18T02:55:23.990183Z 0 [Note] InnoDB: Initializing buffer pool, to
db            | 2020-02-18T02:55:23.997519Z 0 [Note] InnoDB: Completed initialization of 
db            | 2020-02-18T02:55:23.999353Z 0 [Note] InnoDB: If the mysqld execution userhe man page of setpriority().
db            | 2020-02-18T02:55:24.090309Z 0 [ERROR] [FATAL] InnoDB: Table flags are 0 i
db            | 2020-02-18 11:55:24 0x7fb2e0664740  InnoDB: Assertion failure in thread 1
db            | InnoDB: We intentionally generate a memory trap.
db            | InnoDB: Submit a detailed bug report to http://bugs.mysql.com.
db            | InnoDB: If you get repeated assertion failures or crashes, even
db            | InnoDB: immediately after the mysqld startup, there may be
db            | InnoDB: corruption in the InnoDB tablespace. Please refer to
db            | InnoDB: http://dev.mysql.com/doc/refman/5.7/en/forcing-innodb-recovery.ht
db            | InnoDB: about forcing recovery.
db            | 02:55:24 UTC - mysqld got signal 6 ;
db            | This could be because you hit a bug. It is also possible that this binary
db            | or one of the libraries it was linked against is corrupt, improperly buil
db            | or misconfigured. This error can also be caused by malfunctioning hardwar
db            | Attempting to collect some information that could help diagnose the probl
db            | As this is a crash and something is definitely wrong, the information
db            | collection process might fail.
db            |
db            | key_buffer_size=8388608
db            | read_buffer_size=131072
db            | max_used_connections=0
db            | max_threads=151
db            | thread_count=0
db            | connection_count=0
db            | It is possible that mysqld could use up to
db            | key_buffer_size + (read_buffer_size + sort_buffer_size)*max_threads = 681
db            | /lib/x86_64-linux-gnu/libc.so.6(abort+0x16a)[0x7fb2de9cf42a]
db            | mysqld(+0x699b25)[0x5562a20f0b25]
db            | mysqld(_ZN2ib5fatalD1Ev+0x12d)[0x5562a2b60d8d]
db            | mysqld(+0x11b68f1)[0x5562a2c0d8f1]
db            | mysqld(_Z6fil_ioRK9IORequestbRK9page_id_tRK11page_size_tmmPvS8_+0x2b0)[0x5562a2c17110]
db            | mysqld(_Z13buf_read_pageRK9page_id_tRK11page_size_t+0xce)[0x5562a2bcc33e]
db            | mysqld(_Z16buf_page_get_genRK9page_id_tRK11page_size_tmP11buf_block_tmPKcmP5mtr_tb+0x4aa)[0x5562a2b9b57a]
db            | mysqld(_Z31trx_rseg_get_n_undo_tablespacesPm+0x143)[0x5562a2b3f1f3]      
db            | mysqld(+0x698c99)[0x5562a20efc99]
db            | mysqld(_Z34innobase_start_or_create_for_mysqlv+0x2f3d)[0x5562a2b0c06d]   
db            | mysqld(_Z24ha_initialize_handlertonP13st_plugin_int+0x4f)[0x5562a216b0ff]
db            | mysqld(+0xb8c0f6)[0x5562a25e30f6]
db            | mysqld(_Z40plugin_register_builtin_and_init_core_sePiPPc+0x2f0)[0x5562a25e6300]
db            | mysqld(+0x6bbece)[0x5562a2112ece]
db            | mysqld(_Z11mysqld_mainiPPc+0xc71)[0x5562a2114a71]
db            | /lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf1)[0x7fb2de9bb2e1]  
db            | mysqld(_start+0x2a)[0x5562a210aeaa]
db            | The manual page at http://dev.mysql.com/doc/mysql/en/crashing.html contains
db            | information that should help you find out what is causing the crash.

ログを見てみるとなにやら恐ろしげなことが書いてあります。

This could be because you hit a bug. It is also possible that this binary
or one of the libraries it was linked against is corrupt, improperly built or misconfigured. This error can also be caused by malfunctioning hardwar Attempting to collect some information that could help diagnose the problem As this is a crash and something is definitely wrong, the information collection process might fail.

どうやらmysqlのバグが原因のようです。なんらかの原因によりクラッシュしたみたいです。

何度、やり直してもこのエラーが出ます。
このコンテナを削除しまったく新しくmysqlのコンテナを作り直してもダメです。

とりあえず解決

こちらを参考に対処しました。
https://github.com/laradock/laradock/issues/1173

結局mysqlのイメージ、コンテナ、data volumeを削除してから再起動して解決しました。

イメージの削除

docker imagesで一覧を取得します。

$ docker images
REPOSITORY   TAG    IMAGE ID       CREATED
mysql        5.7    c4f186b9e038   2 weeks ago   435MB

コンテナを停止させてからdocker rmi (IMAGE ID)で削除します。

$ docker rmi c4f186b9e038

data volumeの削除

data volumeとしてマウントしているフォルダを削除します。docker-compose.ymlのvolumesで指定のファイルになります。

docker
 ├─mysql
 │  ├─conf.d
 │  └─data ←削除

立ち上げてみます

$ docker-compose up -d --build

自分はこれで無事起動することができました。

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

【docker】ERROR:OCI runtime create failedが出る。

状況

Dockerfile
FROM php:7.3-fpm
COPY php.ini /usr/local/etc/php/

RUN apt-get update \
  && apt-get install -y zlib1g-dev libzip-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') === 'e0012edf3e80b6978849f5eff0d4b4e4c79ff1609dd1e613307e16318854d24ae64f26d17af3ef0bf7cfb710ca74755a') { 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

RUN composer global require "laravel/installer"

php:7.3-fpmのDockerfile下のように書き、docker-compose up -d --buildすると、次のようなエラーが出る。

ERROR: for php-fpm  Cannot start service php-fpm: OCI runtime create failed: container_linux.go:344: starting container process caused "chdir to cwd (\"/var/www/html\") set in config.json failed: permission denied": unknown
ERROR: Encountered errors while bringing up the project.

chdir to cwd (\"/var/www/html\")などを見るとphp-fpmの作業ディレクトリの問題だろうと思いDockerfileを修正する。

Dockerfile
WORKDIR /var/www 

今回はアプリを/var/wwwに置いているので、そこを作業ディレクトリに指定してみる。
再びdocker-compose up -dしてみると無事コンテナが起動する。

原因

単純にDockerfileに作業ディレクトリの指定がなく、/var/www/htmlとなっていたこと。
なぜ/var/www/htmlとなっていたかはわからない。デフォルトではそうなるのだろうか。

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

Laravel Graphql Server using Lighthouse-PHP

みなさんこんにちは、最近行方不明になって申し訳ありませんが、仕事で忙しくて、ブログを更新する時間を見つけることができませんでした

Hello everybody, sorry I´ve been missing lately, but I´ve been busy with work, and couldn´t find the time to update the blog

始めましょう

Dockerコンテナを使用してlaravelをインストールする

Install laravel using docker container

Create Laravel Project using Docker

初期移行を実行する

Run Initial Migrations

sudo docker-compose exec app-server php artisan migrate
Lighthouse PHPをインストールする

Install lighthouse-php

lighthouse-phpをインストールする

docker run --rm -v $(pwd):/app composer require nuwave/lighthouse

graphqlのデフォルトスキーマを公開する

Publish default schema for graphql

sudo docker-compose exec app-server php artisan vendor:publish --provider="Nuwave\Lighthouse\LighthouseServiceProvider" --tag=schema

app-serverで、docker-composeファイルで使用する名前を忘れずに変更してください

in app-server, remember to change for the name you use on your docker-compose file

Graphql Playgroundをインストールする

次の手順はオプションですが、推奨されますが、graphql-playgroundをインストールします

Next step is optional, but recommended, install the graphql-playground

docker run --rm -v $(pwd):/app composer require mll-lab/laravel-graphql-playground

遊び場のURLはhttp://localhost/graphql-playground

url for the playground is http://localhost/graphql-playground

構成

コンテナをもう一度実行し、 docker-compose up --build

graphqlのデフォルト設定をエクスポートします

run the containers once more, docker-compose up --build

export the graphql default config

docker-compose exec app-server php artisan vendor:publish --provider="Nuwave\Lighthouse\LighthouseServiceProvider" --tag=config
突然変異とクエリ

クエリを実行する前に、データを挿入する必要があります。最初に突然変異を作成します

Before doing a query, we need to insert data, lets first create a mutation

sudo docker-compose exec app-server php artisan lighthouse:mutation createUser

これにより、 app \ graphql \ mutationsフォルダー内にCreateUser.phpが作成されます。

簡単なリゾルバを追加しましょう

This will create a CreateUser.phpinside the app\graphql\mutations folder

Lets add a simple resolver

public function __invoke($rootValue, array $args, GraphQLContext $context, ResolveInfo $resolveInfo)
    {
        $args["password"] = Hash::make($args["password"]);

        return User::create($args);
    }

schema.graphqlファイル内にルート変更を追加します

Add the route mutation inside the schema.graphql file

type Mutation {
    createUser(name: String!, email: String!, password: String!): User
}

これで、不眠症を使用しているので、簡単な突然変異を作成できます。これが私の突然変異の作成方法です

または、http:// localhost / graphql-playground のプレイグラウンドを使用できます

With this, you can create a simple mutation, since I´m using insomnia, this is how I create my mutation

or you can use the playground at http://localhost/graphql-playground

Insomnia POST : http://localhost/graphql
Playground : http://localhost/graphql-playground

mutation{
  createUser(name:"My Name",email:"my@email.com",password:"mypassword"){
    id,
    name,
  }
}

200を取得すると、応答がすでにjsonであることがわかります。クエリリクエストを作成してユーザーを確認できます。

Once you get the 200, you can see the response is already a json, we can check the users by going making a query request

POST: http://localhost/graphql
Playground : http://localhost/graphql-playground

query{
  users{
    data{
      id,
      name,
      email
    }
  }
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Lighthouse-PHPを使用したLaravel Graphqlサーバー

みなさんこんにちは、最近行方不明になって申し訳ありませんが、仕事で忙しくて、ブログを更新する時間を見つけることができませんでした

Hello everybody, sorry I´ve been missing lately, but I´ve been busy with work, and couldn´t find the time to update the blog

始めましょう

Dockerコンテナを使用してlaravelをインストールする

Install laravel using docker container

Create Laravel Project using Docker

初期移行を実行する

Run Initial Migrations

sudo docker-compose exec app-server php artisan migrate
Lighthouse PHPをインストールする

Install lighthouse-php

lighthouse-phpをインストールする

docker run --rm -v $(pwd):/app composer require nuwave/lighthouse

graphqlのデフォルトスキーマを公開する

Publish default schema for graphql

sudo docker-compose exec app-server php artisan vendor:publish --provider="Nuwave\Lighthouse\LighthouseServiceProvider" --tag=schema

app-serverで、docker-composeファイルで使用する名前を忘れずに変更してください

in app-server, remember to change for the name you use on your docker-compose file

Graphql Playgroundをインストールする

次の手順はオプションですが、推奨されますが、graphql-playgroundをインストールします

Next step is optional, but recommended, install the graphql-playground

docker run --rm -v $(pwd):/app composer require mll-lab/laravel-graphql-playground

遊び場のURLはhttp://localhost/graphql-playground

url for the playground is http://localhost/graphql-playground

構成

コンテナをもう一度実行し、 docker-compose up --build

graphqlのデフォルト設定をエクスポートします

run the containers once more, docker-compose up --build

export the graphql default config

docker-compose exec app-server php artisan vendor:publish --provider="Nuwave\Lighthouse\LighthouseServiceProvider" --tag=config
突然変異とクエリ

クエリを実行する前に、データを挿入する必要があります。最初に突然変異を作成します

Before doing a query, we need to insert data, lets first create a mutation

sudo docker-compose exec app-server php artisan lighthouse:mutation createUser

これにより、 app \ graphql \ mutationsフォルダー内にCreateUser.phpが作成されます。

簡単なリゾルバを追加しましょう

This will create a CreateUser.phpinside the app\graphql\mutations folder

Lets add a simple resolver

public function __invoke($rootValue, array $args, GraphQLContext $context, ResolveInfo $resolveInfo)
    {
        $args["password"] = Hash::make($args["password"]);

        return User::create($args);
    }

schema.graphqlファイル内にルート変更を追加します

Add the route mutation inside the schema.graphql file

type Mutation {
    createUser(name: String!, email: String!, password: String!): User
}

これで、不眠症を使用しているので、簡単な突然変異を作成できます。これが私の突然変異の作成方法です

または、http:// localhost / graphql-playground のプレイグラウンドを使用できます

With this, you can create a simple mutation, since I´m using insomnia, this is how I create my mutation

or you can use the playground at http://localhost/graphql-playground

Insomnia POST : http://localhost/graphql
Playground : http://localhost/graphql-playground

mutation{
  createUser(name:"My Name",email:"my@email.com",password:"mypassword"){
    id,
    name,
  }
}

200を取得すると、応答がすでにjsonであることがわかります。クエリリクエストを作成してユーザーを確認できます。

Once you get the 200, you can see the response is already a json, we can check the users by going making a query request

POST: http://localhost/graphql
Playground : http://localhost/graphql-playground

query{
  users{
    data{
      id,
      name,
      email
    }
  }
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【ハンズオン】Docker+KubernetesでHelmを使ってみよう

noname.png

本記事ではKubernetesの周辺技術の一つとして使われているHelmを、実際に触って理解をしたい方向けにハンズオンを掲載しています。

やること

  • DockerでKubernetes構築(kubernetes-dashboardのデプロイ)
  • Helmでnginxのデプロイ

対象者

・Kubernetes、Helmの概要を知りたい方
・概要だけでなくKubernetes、Helmを実際に動かして理解したい方
・Kubernetesはなんとなく分かってきたけど、Helmはまだ触ったことがない方

前提(バージョン)

docker desktop 2.2.0.3

スクリーンショット 2020-02-18 0.38.27.png

Kubernetes v1.15.5

スクリーンショット 2020-02-18 0.37.22.png

kubectl

$  kubectl version --short --client
Client Version: v1.17.1

helm

$ helm version
version.BuildInfo{Version:"v3.1.0", GitCommit:"b29d20baf09943e134c2fa5e1e1cab3bf93315fa", GitTreeState:"clean", GoVersion:"go1.13.8"}

本ハンズオンを行う上での注意事項

本ハンズオンでは、Helm3を利用しています。
Helm2で行った場合、本記事で記載している方法と実行方法が異なることがあるためできる限り上記のバージョンに合わせてハンズオンを実施いただくことを推奨します。

Kubernetesを使ってみる

Kubernetesとは、デプロイ、スケーリング、アプリやコンテナの運用自動化を実現するオープンソースのプラットフォームです。
リソースを定義したyaml形式のファイルを利用してアプリやミドルウェアを構築します。
またリソースの使用状態に合わせて安定的にスケーリングさせることが可能です。

1.Docker Desktop for Macをインストールする。

https://hub.docker.com/editions/community/docker-ce-desktop-mac
「Get Docker」を押下する。
スクリーンショット 2020-02-17 23.28.28.png

2.kubernetesを有効にする

dockerアプリからpreferencesを開き。
スクリーンショット 2020-02-17 23.37.52.png

kubernetesを有効にする。
Enable Kubernetesにチェックを入れてapplyをクリック
(※kubernetesの起動に少々時間がかかります。)
スクリーンショット 2020-02-17 23.39.15.png

3.起動確認 context切り替え

「docker-desktop」 が存在することを確認します。

$ kubectl config get-contexts
CURRENT   NAME                 CLUSTER          AUTHINFO         NAMESPACE
*         docker-desktop       docker-desktop   docker-desktop   

次にcontextを切り替えます。

$ kubectl config use-context docker-desktop
Switched to context "docker-desktop".

4.kubectlコマンドでkubernetes-dashboardのデプロイ

kubectlコマンドでkubernetes-dashboardというアプリケーションをデプロイしてみましょう。

$ kubectl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/v2.0.0-rc5/aio/deploy/recommended.yaml
namespace/kubernetes-dashboard created
serviceaccount/kubernetes-dashboard created
service/kubernetes-dashboard created
secret/kubernetes-dashboard-certs created
secret/kubernetes-dashboard-csrf created
secret/kubernetes-dashboard-key-holder created
configmap/kubernetes-dashboard-settings created
role.rbac.authorization.k8s.io/kubernetes-dashboard created
clusterrole.rbac.authorization.k8s.io/kubernetes-dashboard created
rolebinding.rbac.authorization.k8s.io/kubernetes-dashboard created
clusterrolebinding.rbac.authorization.k8s.io/kubernetes-dashboard created
deployment.apps/kubernetes-dashboard created
service/dashboard-metrics-scraper created
deployment.apps/dashboard-metrics-scraper created

5.起動確認

localhostとKubernetes API Serverの間にプロキシサーバーまたはアプリケーションレベルのゲートウェイを作成して、アプリケーションにアクセスします。

$ kubectl proxy
Starting to serve on 127.0.0.1:8001

ブラウザから以下のURLでアクセスしてください。
http://localhost:8001/api/v1/namespaces/kubernetes-dashboard/services/https:kubernetes-dashboard:/proxy/
※「8001」というポート番号だけは5の手順で行った「127.0.0.1:8001」の8001に合わせてください。仮に「127.0.0.1:8002」だった場合、「http://localhost:8002/api/v1/namespaces/kubernetes-dashboard/services/https:kubernetes-dashboard:/proxy/」 でアクセスを行ってください。

アクセスをすると以下のような画面になるかと思います。
スクリーンショット 2020-02-17 23.55.31.png

6.トークンの取得

ダッシュボード へアクセスするにはアクセストークンを発行する必要があります。
ターミナルを新規ウィンドウで立ち上げて以下のコマンドを実行して取得しましょう。

$ kubectl -n kube-system describe secret $(kubectl -n kube-system get secret | grep eks-admin | awk '{print $1}')

結果がたくさん出てくるかと思いますが、どこでも良いので「token: 」の値だけまるっとコピーしてください。

7.ダッシュボード へアクセス

「トークン」を選択し、先程の値を貼り付けて「サインイン」を押下してください。

Helmを使ってみる

Kubernetesのパッケージマネージャーおよびアプリケーション管理ツールであり、複数のKubernetesリソースをChartと呼ばれるファイルにし、パッケージ管理することが可能です。シンプルなコマンドで複数回、Kubernetesリソースを展開したい場合や、他のアプリケーションおよびサービスの特定のバージョンを使用して、アプリケーションの依存関係を管理したい時に利用されます。

1.Helmのインストール

Homebrewでインストールします。

$ brew install kubernetes-helm

インストールされたか確認しましょう。

$ helm version
version.BuildInfo{Version:"v3.1.0", GitCommit:"b29d20baf09943e134c2fa5e1e1cab3bf93315fa", GitTreeState:"clean", GoVersion:"go1.13.8"}

念のためhelmのレポジトリを最新化させます。

$ helm repo update
Hang tight while we grab the latest from your chart repositories...
...Successfully got an update from the "stable" chart repository
...Successfully got an update from the "bitnami" chart repository
Update Complete. ⎈ Happy Helming!⎈ 

2.bitnamiをHelmクライアント環境へ追加する

helmレポジトリにbitnamiを追加します。このようにhelmレポジトリに資材が追加されることにより、yamlファイルを使用せずとも複数回同じリソースを展開することができるようになります。
※bitnamiとは?
BitnamiはWordPressやRedmineなどのウェブアプリケーションをPHPやMySQLなどのミドルウェアとともに一式設定済みでパッケージ化して提供してくれるソリューションです。

$ helm repo add bitnami https://charts.bitnami.com/bitnami
"bitnami" has been added to your repositories

3.新しいリリースの作成とKubernetesにデプロイ

Kubernetesにnginxをデプロイします。

$ helm install nginxserver bitnami/nginx 
NAME: nginxserver
LAST DEPLOYED: Tue Feb 18 00:10:51 2020
NAMESPACE: default
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
Get the NGINX URL:

  NOTE: It may take a few minutes for the LoadBalancer IP to be available.
        Watch the status with: 'kubectl get svc --namespace default -w nginxserver'

  export SERVICE_IP=$(kubectl get svc --namespace default nginxserver --template "{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}")
  echo "NGINX URL: http://$SERVICE_IP/"

4.確認

デプロイしたnginxを確認しましょう。

$ kubectl get pods -l app.kubernetes.io/name=nginx
NAME                           READY   STATUS    RESTARTS   AGE
nginxserver-7f8f4c554d-xfmzc   1/1     Running   0          5m26s

$ kubectl get service nginxserver
NAME          TYPE           CLUSTER-IP      EXTERNAL-IP   PORT(S)                      AGE
nginxserver   LoadBalancer   10.104.187.33   localhost     80:30421/TCP,443:31118/TCP   5m45s

ブラウザで「localhost」と入力して表示が以下のようになっていればデプロイ完了です。
スクリーンショット 2020-02-17 22.45.57.png

お掃除

最後にデプロイしたリソースを削除しましょう。

$ kubectl delete -f https://raw.githubusercontent.com/kubernetes/dashboard/v2.0.0-rc5/aio/deploy/recommended.yaml
$ kubectl delete svc nginxserver

もっと実践的にKubernetes周辺技術を学びたい方へ

Amazon EKS Workshopの記載されている手順にしたがってハンズオンすることをお勧めします。
https://eksworkshop.com/

上記のハンズオンでは、

  • Kubernetes
  • Helm
  • EKS
  • RBAC
  • Elasticsearch
  • Grafana

など今求められているKubernetesの周辺技術を網羅的に学習できるため大変お勧めです。
時間があればぜひ試してみてください。

おまけ

最初に注意事項でも書きましたが、Helm2ハンズオンを行いたい場合は以下の手順を参考ください。※古いバージョンとなるため動作は保証できませんのでご理解ください。

1.Helmのインストール

$ cd ~/downloads
$ curl https://raw.githubusercontent.com/kubernetes/helm/master/scripts/get > get_helm.sh
$ chmod +x get_helm.sh
$ ./get_helm.sh

2.tillerのインストール

helmコマンドを使用して、tillerをクラスターにインストールします。
これでクラスター内のリソースを管理するためのアクセスが許可されます。
※tillerとは?
Helmでリソースをサーバ側にchartをデプロイした場合、
chartが直接デプロイされるわけでなく、tillerという形でデプロイ先のサーバに置かれる

$ helm init --docker-desktop  tiller

3.Helmレポジトリへの追加

$ helm repo add bitnami https://charts.bitnami.com/bitnami
"bitnami" has been added to your repositories

4.nginxをインストールする。

ここがhelm3と異なっておりオプション「 --name 」を指定する必要があります。

$ helm install --name mywebserver bitnami/nginx
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む