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

【環境構築】Docker rails 備忘録

Docker使ってrailsでアプリを作成した際の備忘録

手順1

ファイル作成
既にDockerをインストールしているので
使用しているディレクトリの配下に任意の名前でディレクトリを作成し
以下作成したディレクトリにファイル準備。
・Gemfile
・Gemfile.lock
・Dockerfile
・docker-compose.yml
※ターミナルでのファイル作成コマンド:touch XXXXXX(※XXXに作成するファイル名)

手順2

作成したファイルの内容を編集

Gemfile
source 'https://rubygems.org'
gem 'rails', 5.2.2
Dockerfile
FROM ruby:2.5
RUN apt-get update -qq && apt-get install -y build-essential libpq-dev nodejs
RUN mkdir /sample
WORKDIR /sample

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

RUN bundle install
COPY . /sample
docker-compose.yml
version: '3'

services:
    db:
        image: mysql:5.7
        environment:
            MYSQL_USER: root
            MYSQL_ROOT_PASSWORD: password
        ports:
            - "3306:3306"
        volumes:
            - ./db/mysql/volumes:/var/lib/mysql

    web:
        build: .
        command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"
        volumes:
            - .:/sample
            - gem_data:/usr/local/bundle
        ports:
            - 3000:3000
        depends_on:
            - db 
        tty: true
        stdin_open: true

volumes:
  gem_data:

手順3

Dockerコマンドを実行し、railsアプリケーションを作成
※Dockerを起動していない場合は次のコマンドを実行:
docker run -d -p 80:80 docker/getting-started

・rails new コマンド(DBにmysqlを指定)
docker-compose run web rails new . --force --database=mysql --skip-bundle

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

【docker】Node.jsのコンテナ立ち上げてサンプルアプリを動かしたい

この記事の目標

dockerのコンテナ立ち上げて、このアプリ↓をローカル環境で立ち上げる

https://github.com/justadudewhohacks/face-api.js

GitHubからリポジトリcloneする

ローカルの任意のディレクトリにcloneする

$ git clone https://github.com/justadudewhohacks/face-api.js

docker hubからnode.jsのイメージpullしてくる

alpineが軽量でオススメらしいので、v14のをpullする

$ docker pull node:14-alpine

イメージをpullできたことの確認

$ docker images

コンテナ立ち上げる

docker run -it -v $PWD:/workspace -p 8100:3000 --name node-face-api node:14-alpine /bin/ash
  • -itで、コンテナ内で入力可能な状態にする
  • -v $PWD:/workspaceで、カレントディレクトリのファイルをコンテナ内の「workspace」ディレクトリにマウントするように指定
    • そのため、リポジトリをcloneしたディレクトリで作業する必要あり
    • $PWD$(pwd)といった指定方法もありらしい
  • -p 8100:3000で、ホスト側のポート8100とコンテナ側のポート3000を繋ぐ
    • このホスト側のポート番号は空いているものを適宜使う
    • コンテナ側はよしなに設定
    • ポート使用状況はMacの場合「ネットワークユーティリティ」で確認できる
  • --name node-face-apiで、コンテナ名を「node-face-api」に設定
  • node:14-alpineで、イメージとバージョン(TAG?)の指定
  • /bin/ashで、コンテナ内で使うコマンドを指定

今回は使ってないが、以下のオプションもよく使われる

  • --rmで、コンテナから出た時にコンテナ削除

※ちなみに、コマンドの記述順は、以下の通りらしい

$ docker run [OPTIONS] IMAGE [COMMAND] [ARG...]

起動時にエラーが起こって上手くコンテナに入れなかった場合、以下の手順でコンテナ削除し、
runコマンド見直してから再度試す

# コンテナ一覧表示
$ docker ps -a

# 起動(作成)に失敗したコンテナのIDを指定してコンテナ削除
$ docker rm [CONTAINER ID]

コンテナ内でアプリケーションを立ち上げる

コンテナ作成して中に入ると、ホストのファイル群がworkspaceディレクトリ内にあるはずなので、確認する

$ cd workspace
$ ls

あとは、face-ap.jsのチュートリアル通りに進めていくだけ

$ cd examples/examples-browser
$ npm install
$ npm start

ホストマシンのポートは8100に指定したので、以下にアクセス

http://localhost:8100/

それっぽいページが表示されればOK!

参考

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

Ruby on Rails,AWS, Docker, CircleCIでポートフォリオを作成してみた

初めに

今回はRuby on Railsをメインに、AWS,Docker,CircleCIなど近年人気の高まっているインフラ技術を使用してポートフォリオを作成してみました。
本記事では、実装した機能や苦労した点、参考にした記事などを紹介していきたいと思います。
皆様のポートフォリオ作りに少しでもお役に立てれば幸いです。
今後、ポートフォリオをアップデートした際にはこちらの記事も随時アップデートしていきます。(2021年1月26日時点)

アプリの概要

レシピと料理に使った材料の投稿ができるアプリです。
レシピに乗っているあの材料はどこで手に入るんだろう??
料理初心者の誰もが感じる疑問を解消すべく、レシピ投稿機能のほか、料理に使用した材料の購入場所を投稿できるアプリを製作しました。

URLは下記になります、よかったら見て行ってください!
URL: https://www.cooknavi.xyz/
(レスポンシブデザインにも対応しておりますので、スマートフォンからの閲覧も可能です!)

GitHub: https://github.com/yutatsune/cooknavi

主な使用イメージ

トップページからゲストログイン(閲覧用)をクリック
「レシピを見てみる」または「材料を探してみる」をクリックし、一覧画面へ遷移します
cooknavi1
ログインが完了したら、レシピまたは材料の一覧画面の右下から新規投稿ができます
必要な項目を入力して、投稿するをクリック
cooknavi2
材料投稿画面にて、郵便番号を入力すると自動で住所が入力されます
cooknavi3
材料投稿画面で入力した住所の経度・緯度を自動で取得して、GoogleMapに表示されます
cooknavi4
投稿詳細画面でいいねをすることができます
cooknavi5

使用技術

  • フロントエンド
    • jQuery 1.12.4
    • HTML/CSS/Haml/Sass
  • バックエンド
    • ruby 2.6.5
    • Ruby on Rails 6.0.0
    • Google Maps API
  • インフラ
    • CircleCI
    • Docker 19.03.13/docker-compose 1.27.4
    • nginx 1.12.2
    • mysql 5.7.31
    • AWS ( EC2, ALB, ACM, RDS, Route53, VPC, IAM )
  • その他使用ツール
    • Visual Studio Code
    • draw.io

インフラ構成図

aws

VPCの構築、EC2へのデプロイ、RDSの導入、ELBの設定、ドメイン取得からのRoute53の設定、ACMでのSSL証明書の取得など基本的な部分は導入できました
今後は、S3や自動デプロイを導入していきたいです!

機能一覧

機能 概要
ユーザー管理機能 新規登録・ログイン・ログアウトできます
簡単ログイン機能 トップ画面のゲストログイン(閲覧用)をクリックすることで、簡単にログインできます
投稿機能 レシピまたは材料を画像を5枚までつけて投稿できます
投稿詳細表示機能 各投稿画面が詳細ページで見ることができます
投稿編集・削除機能 投稿者本人のみ投稿編集・削除できます
ユーザー一覧表示機能 登録したユーザーの一覧を見ることができます
ユーザー情報編集機能 ログイン中のユーザーでアカウント本人であればプロフィール編集できます
フォロー機能 ユーザー一覧画面から各ユーザーのフォローできます
フォロー一覧表示機能 フォローしているまたはフォローされているユーザーを見ることができます
いいね機能 投稿詳細ページからいいねすることできます
いいね一覧機能 いいねした投稿の一覧を見ることができます
コメント機能 投稿詳細ページから非同期通信でコメントできます
住所自動入力機能 材料投稿画面にて、郵便番号を入力するだけで住所が自動で入力されます
マップ表示機能 材料投稿画面で入力した住所の経度・緯度を自動で取得し、マップで表示することができます
レスポンシブデザイン スマートフォン向けの表示に対応

工夫した点

とにかく、単なるレシピ投稿アプリにならないように下記を頑張って実装しました!

  • 料理に使った材料の投稿機能
  • 住所自動入力機能
  • GoogleMapsAPI連携

レシピを投稿したユーザーが、使った材料をどこで買ったかも投稿できるようにしました
通販でいいんじゃ。。。というツッコミはなしでお願いします笑
実際にお店で色々見たりする楽しみだってありますから!

今後、新しい機能を思いついたら実装してみたいとおもいます

DB設計

database

各テーブルについて

テーブル名 説明
users 登録ユーザー情報
relationships フォロー・フォロワーのユーザー情報
recipes レシピの投稿情報
images レシピの投稿画像情報
comments 投稿レシピへのコメント情報
recipe_likes 投稿レシピへのいいね情報
materials 材料の投稿情報
material_images 材料の投稿画像情報
material_comments 投稿材料へのコメント情報
material_likes 投稿材料へのいいね情報

苦労した点

フロントエンド

  • デザインについて

まず、デザインをどうすれば良いかで悩みました!
最終的には、フルスクリーンレイアウトやタイル型レイアウト等に落ち着きました

  • JavaScript(jQuely)全般

スライドメニューやGoogleMapの表示などが大変でした!
これからも勉強を続けて、もっとカッコよく実装できるようになりたいです

バックエンド

  • いいね・フォロー機能のアソシエーション

投稿機能など、初めのうちに学習した機能とは違い、アソシエーションの組み方が独特でした
しかし、一度理解してしまえばあとはスムーズに実装できました

  • GoogleMapsAPI連携

これがとにかく苦労しました
GoogleMapが表示されない、JavaScriptがRubyの値を参照するにはどうするかなど課題がありました
これは、gonというgemを使うことによって解決できました!
最終的に実装できて本当によかったです!

  • RSpecによるテスト

意外と盲点だったのがこちら
書き方が独特で新たに覚えなければいけないことが多いです
こちらは現在進行中で更新中です!

AWS

  • EC2インスタンス内の環境構築

Rails6以降はYarnが必須であることがわかっていなくて、プリコンパイルのところで2日くらい詰まりました。。。
皆さんはお気をつけを!

  • RDS設定

環境構築が終わった後にいざ試しに登録してみるとエラーが。。。
原因は日本語が登録できない設定になっていたことでした
これを直すには、パラメーターグループの設定を変更すれば解決するはずです
【AWS】RDSで作成したMySQLのDBに日本語が保存できないを解決 | Rails on Docker

Docker

  • 環境構築

Dockerはやはりこれに尽きると思います。
dockerファイルの記載やdocker-compose.ymlの設定など覚えることがたくさんありました
あと、Rails6以降はYarnとwebpackerのインストールが必須となります
【Rails6】Docker+Rails6+puma+nginx+mysql【環境構築*初心者必見】

CircleCI

  • 導入とyml設定

まず、GitHubとの連携の仕方がよく分からなくて苦戦しました
原因はymlファイルをgitのmasterにプッシュしてないからでした。。。
【CircleCI】CircleCI 2.0からはじめる個人での簡単なCI導入方法 - githubとの連携まで
今ではなんとか自動テストまでは導入できました
今後は自動デプロイも試していきたいです

参考にした学習教材・記事

とにかく参考になりそうなものは色々と試していきました!
ここでは使ってみてよかったものを紹介していきます

UI/UX

ポートフォリオのデザインに悩んだら読んでみてください!
HTMLやCSSの基礎の他、フルスクリーンレイアウトやタイル型レイアウト、レスポンシブデザインなどとても参考になりました

スライドメニューの作り方や画像のスライドショーなど、実践的に使えるテクニックが紹介されていました
jQueryを使うなら読んでみて損はないかと思います!

Ruby on Rails

AWS

こちらの記事がすごく分かり易かったです!
画像が豊富に使われていて、初めてAWSを使う人でも問題なくできます
手順通りに行うだけで、VPC,EC2,RDS,Route53,ACMなど基本的な環境が構築可能

VPCの構築やEC2の立ち上げ方など、基礎的なことを一通り学ぶことができます
まず最初にこちらを読んでみると良いのではないでしょうか

Docker/docker-compose

まず、学習の手始めとしてこちらを学習しました
Dockerとは何か?というところからdocker-composeの構築の仕方まで学ぶことができます

私はまだ受けていないのですが、こちらの講座がわかりやすいと評判なので紹介させていただきます

CircleCI

今後の課題

  • Vue.jsなどの導入
  • ECSによるコンテナデプロイ
  • CircleCIの自動デプロイ(CDパイプラインの構築)
  • テストコードの充実
  • いいね・フォロー機能のajax化
  • さらなる機能の拡張

など、まだまだできるところはあると感じました!
今後の転職活動の状況と相談しながら引き続き改善していきたいと思います

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

Windows10(WSL2)でRails/Dockerの開発環境を構築したい

まず、Windows10でWSL2を使えるようにする。

公式ドキュメント通りに進める

私はUbuntuを入れました。(Ubuntu 20.04 LTSで大丈夫です。)

Docker EngineとDocker-ComposeをUbuntuにインストールする。

Docker Engineを先に入れてから、Docker-composeを入れる。

理由:On Linux systems, first install the Docker Engine for your OS as described on the Get Docker page, then come back here for instructions on installing Compose on Linux systems.

開発ディレクトリについて(重要)

WSL2 環境において開発ディレクトリをどこに置くかは重要です。

開発ディレクトリを Windowsファイルシステム側(Linuxパス: /mnt/c/...)に置いた場合、ファイル IO が異常に遅く、一部 Docker 環境ではネットワーク通信に不具合が発生するなどの問題が起こります。
そのため、基本的には \wsl$\Ubuntu-20.04\home<ユーザ名>(Linuxパス: /home/<ユーザ名>)など、Linuxファイルシステム側に開発ディレクトリを置く必要があります。
開発ディレクトリがLinuxファイルシステム側に置いてあれば、Dockerプロジェクトも安定・軽快に動かすことができます。(少なくとも今のところは)

Docker EngineをUbuntuに入れる

エラーが出た場合

~$ sudo docker run hello-world
docker: Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?.
See 'docker run --help'.

私はsudo service docker startを使うと動きました。(参考)

Docker-ComposeをUbuntuに入れる

Docker-composeを使用して、Railsをインストールする。

Docker社のクイックスタートを参考にして入れる

エラーが出た場合

W: Failed to fetch http://deb.debian.org/debian/dists/buster/InRelease  Temporary failure resolving 'deb.debian.org'
W: Failed to fetch http://security.debian.org/debian-security/dists/buster/updates/InRelease  Temporary failure resolving 'security.debian.org'
W: Failed to fetch http://deb.debian.org/debian/dists/buster-updates/InRelease  Temporary failure resolving 'deb.debian.org'
W: Some index files failed to download. They have been ignored, or old ones used instead.
Reading package lists...
Building dependency tree...
Reading state information...
E: Unable to locate package nodejs
E: Unable to locate package postgresql-client
ERROR: Service 'web' failed to build : The command '/bin/sh -c apt-get update -qq && apt-get install -y nodejs postgresql-client' returned a non-zero code: 100

どうやらコンテナのDNS設定によるものらしい。

私は、この通りに/etc/wsl.confと/etc/resolve.confを編集したら動きました!!

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

Docker X Laravel X ngrok ローカル環境を一時的に外部へ公開する

仕事で使うかな?と思って導入したら結局使わなかったので投稿して供養します。。

ngrok とは

https://ngrok.com

localhostで動いているサーバーを外部(LAN外)からアクセスできるようにリレーしてくれるツールです。

無料で使用できます。

docker-compose.yml
services:
  # ...

  ngrok:
    image: wernight/ngrok
    ports:
      - ${WEB_PORT:-4040}:4040
    environment:
      NGROK_AUTH: ${NGROK_AUTH}
      NGROK_PROTOCOL: http
      NGROK_PORT: web:80
      # SSLの場合
      # NGROK_PROTOCOL: https
      # NGROK_PORT: web:443

https://dashboard.ngrok.com/get-started/setup
サインインして authtoken を取得します。

.env

NGROK_AUTH=hogehogehogehogehoge

ちなみにサインインなしだとセッションが2時間までになります。

$ docker-compose up -d

http://localhost:4040

表示されているURLをクリックすると確認できます。

ScreenShot 2021-01-27 20.35.25.png

表示されるURLをクリックすると...

ScreenShot 2021-01-27 20.36.05.png

LaravelのWelcome画面が表示されました!

IMG_6801.PNG

外部のPCやスマホからアクセスできてるのでokです!
また、コンテナを破棄すれば接続できなくなります。(まぁ当たり前ですが...)

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

Docker × Laravel × ngrok ローカル環境を一時的に外部へ公開する

仕事で使うかな?と思って導入したら結局使わなかったので投稿して供養します。。

ngrok とは

https://ngrok.com

localhostで動いているサーバーを外部(LAN外)からアクセスできるようにリレーしてくれるツールです。

無料で使用できます。

環境

  • PHP: 8.0.1
  • Laravel: 8.23.1

Docker X Laravelとは

Docker Composeを使用してシンプルなLaravelのローカル開発環境を構築するツールです。

導入

docker-compose.yml
services:
  # ...

  ngrok:
    image: wernight/ngrok
    ports:
      - ${WEB_PORT:-4040}:4040
    environment:
      NGROK_AUTH: ${NGROK_AUTH}
      NGROK_PROTOCOL: http
      NGROK_PORT: web:80
      # SSLの場合
      # NGROK_PROTOCOL: https
      # NGROK_PORT: web:443

https://dashboard.ngrok.com/get-started/setup
サインインして authtoken を取得します。

.env

NGROK_AUTH=hogehogehogehogehoge

ちなみにサインインなしだとセッションが2時間までになります。

$ docker-compose up -d

http://localhost:4040

表示されているURLをクリックすると確認できます。

ScreenShot 2021-01-27 20.35.25.png

表示されるURLをクリックすると...

ScreenShot 2021-01-27 20.36.05.png

LaravelのWelcome画面が表示されました!

IMG_6801.PNG

外部のPCやスマホからアクセスできてるのでokです!
また、コンテナを破棄すれば接続できなくなります。(まぁ当たり前ですが...)

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

開発環境でRailsのログをFluentdでElasticsearchに送ってKibanaで見るための環境をDockerで作る

目的

fluentdやkibanaの使い方を知らなかったので、勉強のために開発環境にDockerでfluentdやkibanaを触れる環境を作ってみました。
この記事のゴールは、fluentdを通してElasticsearchにログを送りKibanaでRailsのログを見ることです。

記事の最後まで進めると、Kibanaでこんな風にRailsのログが表示されます。
スクリーンショット 2021-01-27 18.37.57.png

概要

104429333-d7f65f80-55c8-11eb-9f20-81ea36226fcb.png

フォルダ構成

トップにあるDockerfileはRails用です。
dockerフォルダにはElasticsearch、fluentd、Kibana、MySQL、NginxのDockerfileをおいています。

$ ls
Dockerfile docker-compose.yml docker/ entrypoint.sh
$ tree -L 2 docker
docker
├── elasticsearch
│   └── Dockerfile
├── fluentd
│   ├── Dockerfile
│   └── fluent.conf
├── kibana
│   └── Dockerfile
├── mysql
│   └── Dockerfile
└── nginx
    ├── Dockerfile
    └── nginx.conf

バージョン

対象 バージョン
ruby 2.7.2
rails 6.1
mysql 5.7
nginx 1.19.6
elasticsearch 7.10.1
kibana 7.10.1
fluentd 1.12-1
docker 20.10.2

Rails環境作成

まずはRailsを動かすためにファイルを作っていきます。DBの設定もここで書いていきます。

1. まずは以下のコマンドで必要なファイルを作ります

touch {Dockerfile,Gemfile,Gemfile.lock,entrypoint.sh,docker- 
 compose.yml,.dockerignore}

2. Dockerfileの中身を書いていきます

手順1で作ったDockerfileに以下のコードを書いてください

FROM ruby:2.7-slim-buster

RUN apt-get update -qq && \
    apt-get install -y --no-install-recommends \
    vim locales build-essential \
    libpq-dev libmariadb-dev curl npm && \
    npm install -g yarn n && \
    n 15.6

ENV APP_ROOT /app
WORKDIR $APP_ROOT

COPY Gemfile Gemfile.lock $APP_ROOT/

RUN gem update --system && gem install bundler:2.1.4

RUN RAILS_ENV=development bundle install && yarn install --frozen-lockfile


COPY . $APP_ROOT/
COPY entrypoint.sh /usr/bin/
RUN chmod +x /usr/bin/entrypoint.sh
ENTRYPOINT ["entrypoint.sh"]
EXPOSE 3000

3. Gemfileの作成

手順1で作ったGemfileに以下を記述

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

ruby '2.7.2'

gem 'rails', '~>6'

4. entrypoint.sh

手順1で作ったentrypoint.shに以下を記述

#!/bin/bash
set -e
rm -f /app/tmp/pids/server.pid
exec "$@"

5. dockerフォルダを作ってそこにMySQLのDockerfileを作成する

docker/mysqlフォルダ作成し

mkdir -p docker/mysql

mysql用のDockerfileを作成し

touch docker/mysql/Dockerfile

docker/mysql/Dockerfileには以下を記述します

FROM mysql:5.7

6. docker-compose.ymlを作成

version: "3.7"
services:
  db:
    build: ./docker/mysql/
    environment:
      MYSQL_DATABASE: root
      MYSQL_ALLOW_EMPTY_PASSWORD: "yes"
      TZ: Asia/Tokyo
    expose:
      - 3306
    volumes:
      # 初期データを投入するSQLが格納されているフォルダ
      - ./docker/mysql/mysql_init:/docker-entrypoint-initdb.d
      # 永続化するときにマウントするフォルダ
      - ./docker/mysql/mysql_data:/var/lib/mysql
  app:
    build: .
    command: /bin/sh -c "rm -f /app/tmp/pids/server.pid && bundle exec rails s -b '0.0.0.0'"
    environment:
      DATABASE_HOST: db
      DATABASE_PORT: 3306
      DATABASE_USER: root
      RAILS_ENV: development
    expose:
      - 3000
    port:
      - 3000:3000
    depends_on:
      - db
    #ポート待受していないコンテナがすぐ終了してしまうのを防ぐ
    tty: true
    # コンテナの標準に入力をオープンにする
    stdin_open: true
    volumes:
      - .:/app

7. .dockerignoreファイルに以下を記述

tmp/
!tmp/pids/
node_modules/
vendor/bundle/

8. Dockerでrails newを実行

docker-compose run --no-deps app rails new . --force --database=mysql

これの実行が完了するまで数分の時間がかかるので気長に待ちましょう...

9. DBの作成

先程のコマンドの実行が完了したらconfig/database.ymlというファイルが作られています。そのファイルのhostを以下のように編集します。
localhostからdbという名前に変えてください。

default: &default
  adapter: mysql2
  encoding: utf8mb4
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  username: root
  password:
- host: localhost
+ host: db

そしたらrailsをDockerの立ち上げて

docker-compose up --build

別のターミナルでDBを作成するコマンドを実行します。

$ docker-compose run app rails db:create
Starting rails-monitoring_test_db_1 ... done
Created database 'app_development'
Created database 'app_test'

これが完了したら、http://localhost:3000 にアクセスしてみましょう。

この画面が出たら起動成功です!

スクリーンショット 2021-01-27 16.50.15.png

Nginxを作成

続いてNginxの環境を作って行きます。

1. Nginxに関する設定を置くフォルダを作成

mkdir -p docker/nginx/

2. Nginx用のDockerfileと設定ファイルを作成

touch docker/nginx/{Dockerfile,nginx.conf}

3. NginxのDockerfileを書きます

docker/nginx/Dockerfileに以下を記述

FROM nginx:1.19.6
# インクルード用のディレクトリ内を削除
RUN rm -f /etc/nginx/conf.d/*

# Nginxの設定ファイルをコンテナにコピー
COPY ./nginx.conf /etc/nginx/

4. docker/nginx/nginx.confに設定を書きます

user  nginx;
worker_processes  auto;

events {
    worker_connections  1024;
}

http {
    upstream app {
        server unix:///app/tmp/sockets/puma.sock;
    }

    log_format ltsv 'time:$time_iso8601\t'
                    'remote_addr:$remote_addr\t'
                    'request_method:$request_method\t'
                    'request_length:$request_length\t'
                    'request_uri:$request_uri\t'
                    'https:$https\t'
                    'uri:$uri\t'
                    'query_string:$query_string\t'
                    'status:$status\t'
                    'bytes_sent:$bytes_sent\t'
                    'body_bytes_sent:$body_bytes_sent\t'
                    'referer:$http_referer\t'
                    'useragent:$http_user_agent\t'
                    'forwardedfor:$http_x_forwarded_for\t'
                    'request_time:$request_time\t'
                    'upstream_response_time:$upstream_response_time\t'
                    'host:$host';

    server {
        listen 80;
        server_name localhost;

        root /app/public;
        try_files  $uri/index.html $uri @app;

        access_log /var/log/nginx/access.log ltsv;
        error_log /var/log/nginx/error.log;

        location @app {
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header Host $http_host;
            proxy_pass http://app;
        }
    }
}

5. config/puma.rbを編集

rails newをしたときにconfig/puma.rbというファイルが作成されています。
puma.rbファイルの一番下に以下のコードを記述します。

# Allow puma to be restarted by `rails restart` command.
plugin :tmp_restart

# ↓これを記述
bind "unix://#{Rails.root}/tmp/sockets/puma.sock"

またpuma.rbの以下の行をコメントアウトします

# Specifies the `port` that Puma will listen on to receive requests; default is 3000.
#
# ↓この行をコメントアウト
- port ENV.fetch("PORT") { 3000 }
+ # port ENV.fetch("PORT") { 3000 }

6. docker-compose.ymlを編集

docker-compose.ymlのapp部分について編集していきます。
1つは、書いてあったここの部分の -b '0.0.0.0'を削除します。

- command: /bin/sh -c "rm -f /app/tmp/pids/server.pid && bundle exec rails s -b '0.0.0.0'"
+ command: /bin/sh -c "rm -f /app/tmp/pids/server.pid && bundle exec rails s"

2つめは、volumesに - tmp-data:/app/tmp/socketsを追加です。

    volumes:
      - .:/app
      - tmp-data:/app/tmp/sockets

3つめは、portsの指定を削除です

    ports:
      - 3000:3000

続いては、nginxについての記述を追加します

  nginx:
    build: ./docker/nginx/
    # Nginxをforegroundで動かすため、daemonをoff
    command: nginx -g 'daemon off;'
    volumes:
      - ./docker/nginx.conf:/etc/nginx/conf.d/default.conf
      - tmp-data:/app/tmp/sockets
    ports:
      - 80:80
    links:
      - app

volumes:
    tmp-data:

上記のコードを追加した結果、docker-compose.yml 全体は今こうなっています

version: "3.7"
services:
  db:
    build: ./docker/mysql/
    environment:
      MYSQL_DATABASE: root
      MYSQL_ALLOW_EMPTY_PASSWORD: "yes"
      TZ: Asia/Tokyo
    expose:
      - 3306
    volumes:
      # 初期データを投入するSQLが格納されているフォルダ
      - ./docker/mysql/mysql_init:/docker-entrypoint-initdb.d
      # 永続化するときにマウントするフォルダ
      - ./docker/mysql/mysql_data:/var/lib/mysql
  app:
    build: .
    command: /bin/sh -c "rm -f /app/tmp/pids/server.pid && bundle exec rails s"
    environment:
      DATABASE_HOST: db
      DATABASE_PORT: 3306
      DATABASE_USER: root
      RAILS_ENV: development
    expose:
      - 3000
    depends_on:
      - db
    #ポート待受していないコンテナがすぐ終了してしまうのを防ぐ
    tty: true
    # コンテナの標準に入力をオープンにする
    stdin_open: true
    volumes:
      - .:/app
      - tmp-data:/app/tmp/sockets

  nginx:
    build: ./docker/nginx/
    # Nginxをforegroundで動かすため、daemonをoff
    command: nginx -g 'daemon off;'
    volumes:
      - ./docker/nginx.conf:/etc/nginx/conf.d/default.conf
      - tmp-data:/app/tmp/sockets
    ports:
      - 80:80
    links:
      - app

volumes:
    tmp-data:

7. Nginx込みでdocker-compose.ymlを立ち上げてみる

docker-compose up --build

起動が確認できたら http:localhost/ にアクセスしてみましょう。:3000 なしのURLにアクセスできたら成功です!

スクリーンショット 2021-01-27 16.50.15.png

Elasticsearchの作成

1. docker/elasticsearch/Dockerfileの作成

mkdir docker/elasticsearch/
touch docker/elasticsearch/Dockerfile

docker/elasticsearch/Dockerfileには以下の記述だけです

FROM elasticsearch:7.10.1

2. elasticsearchをdocker-compose.ymlに追加

  elasticsearch:
    build: ./docker/elasticsearch/
    environment:
      - discovery.type=single-node
      - cluster.name=docker-cluster
      - bootstrap.memory_lock=true
      - ES_JAVA_OPTS=-Xms512m -Xmx512m
    ports:
      - 9200:9200
    volumes:
      - ./docker/elasticsearch/data:/usr/share/elasticsearch/data

3. Dockerを立ち上げて動作確認

docker-compose up

起動が確認できたら http://localhost:9200/ にアクセスしてみましょう。こんな画面が出たら成功です!
スクリーンショット 2021-01-27 17.46.25.png

fluentdの作成

1. docker/fluentd/Dockerfileを作成します

mkdir -p docker/fluentd/
touch docker/fluentd/{Dockerfile,fluent.conf}

docker/fluentd/Dockerfileには以下を記述します。

FROM fluent/fluentd:v1.12-1

USER root
RUN gem install fluent-plugin-elasticsearch

2. docker/fluentd/fluent.confを作成します

以下を記述すればOKです。

<source>
  @type forward
  port 24224
  bind 0.0.0.0
</source>

<filter nginx>
  @type parser
  key_name log
  <parse>
    @type ltsv
  </parse>
</filter>

<match nginx>
  @type elasticsearch
  host elasticsearch
  buffer_type memory
  port 9200
  index_name fluentd
  type_name nginx
  logstash_format true
  logstash_prefix nginx.access
</match>

<filter rails>
  @type parser
  key_name messages
  <parse>
    @type json
  </parse>
</filter>

<match rails>
  @type elasticsearch
  host elasticsearch
  buffer_type memory
  port 9200
  type_name rails
  logstash_format true
  logstash_prefix rails.access
</match>

3. docker-compose.ymlにfluentdを追加

  fluentd:
    build: ./docker/fluentd/
    volumes:
      - ./docker/fluentd/fluent.conf:/fluentd/etc/fluent.conf
    ports:
      - 24224:24224
    depends_on:
      - elasticsearch

続けて既存のdocker-compose.ymlにも編集を加えます

まずはappのdepends_onの箇所に - fluentd を加えます。

  app:
    depends_on:
      - db
      - fluentd # これを追加

次にnginxも編集します。

  nginx:
    build: ./docker/nginx/
    # Nginxをforegroundで動かすため、daemonをoff
    command: nginx -g 'daemon off;'
    volumes:
      - ./docker/nginx.conf:/etc/nginx/conf.d/default.conf
      - tmp-data:/app/tmp/sockets
    ports:
      - 80:80
    links:
      - app
      # ↓ここから下を追加
      - fluentd
    logging: # ロギング(ログ保存)ドライバをfluentdが行う
      driver: fluentd
      options:
        tag: nginx
        # 初期接続をブロックしない
        fluentd-async-connect: "true"

fluentdの設定はこれで完了です。次のKibanaの設定で最後になります!

Kibanaの作成

1. docker/kibana/Dockerfileを作成します

mkdir -p docker/kibana/
touch docker/kibana/Dockerfile

docker/kibana/Dockerfileには以下を記述します。

FROM kibana:7.10.1

2. docker-compose.ymlにkibanaを追加します

  kibana:
    build: ./docker/kibana/
    ports:
      - 5601:5601
    depends_on:
      - elasticsearch

これで完成です!

上記までの記述でdocker-compose.ymlは以下のようになりました

version: "3.7"
services:
  db:
    build: ./docker/mysql/
    environment:
      MYSQL_DATABASE: root
      MYSQL_ALLOW_EMPTY_PASSWORD: "yes"
      TZ: Asia/Tokyo
    expose:
      - 3306
    volumes:
      # 初期データを投入するSQLが格納されているフォルダ
      - ./docker/mysql/mysql_init:/docker-entrypoint-initdb.d
      # 永続化するときにマウントするフォルダ
      - ./docker/mysql/mysql_data:/var/lib/mysql

  app:
    build: .
    command: /bin/sh -c "rm -f /app/tmp/pids/server.pid && bundle exec rails s"
    environment:
      DATABASE_HOST: db
      DATABASE_PORT: 3306
      DATABASE_USER: root
      RAILS_ENV: development
    expose:
      - 3000
    depends_on:
      - db
      - fluentd
    #ポート待受していないコンテナがすぐ終了してしまうのを防ぐ
    tty: true
    # コンテナの標準に入力をオープンにする
    stdin_open: true
    volumes:
      - .:/app
      - tmp-data:/app/tmp/sockets

  nginx:
    build: ./docker/nginx/
    # Nginxをforegroundで動かすため、daemonをoff
    command: nginx -g 'daemon off;'
    volumes:
      - ./docker/nginx.conf:/etc/nginx/conf.d/default.conf
      - tmp-data:/app/tmp/sockets
    ports:
      - 80:80
    links:
      - app
      - fluentd
    logging:
      # ロギング(ログ保存)ドライバをfluentdが行う
      driver: fluentd
      options:
        tag: nginx
        # 初期接続をブロックしない
        fluentd-async-connect: "true"

  elasticsearch:
    build: ./docker/elasticsearch/
    environment:
      - discovery.type=single-node
      - cluster.name=docker-cluster
      - bootstrap.memory_lock=true
      - ES_JAVA_OPTS=-Xms512m -Xmx512m
    ports:
      - 9200:9200
    volumes:
      - ./docker/elasticsearch/data:/usr/share/elasticsearch/data

  fluentd:
    build: ./docker/fluentd/
    volumes:
      - ./docker/fluentd/fluent.conf:/fluentd/etc/fluent.conf
    ports:
      - 24224:24224
    depends_on:
      - elasticsearch

  kibana:
    build: ./docker/kibana/
    ports:
      - 5601:5601
    depends_on:
      - elasticsearch

volumes:
    tmp-data:

ここまでできたら動作確認をしてみましょう。

docker-compose up

Kibanaは http://localhost:5601/app/home/ にアクセスして以下のような画面が表示されたら成功です!

スクリーンショット 2021-01-27 17.44.05.png

KibanaでIndexPatternを作成する

RailsのログをKibanaで見れるようにするためにもう少しだけRailsに手を加えます。

1. gemを追加

Gemfileに以下の2つを追加してください

# Railsのログをfluentdに送る
gem 'act-fluent-logger-rails'
gem 'lograge'

2. config/application.rbに設定追加

  class Application < Rails::Application
    # Initialize configuration defaults for originally generated Rails version.
    config.load_defaults 6.1

    # ↓これを追加
    # Fluentdのログ設定
    config.log_level = :info
    config.logger = ActFluentLoggerRails::Logger.new
    config.lograge.enabled = true
    config.lograge.formatter = Lograge::Formatters::Json.new
    # ↑ここまで
  end

3. config/fluent-logger.ymlファイルを作成

touch config/fluent-logger.yml

config/fluent-logger.ymlファイルに以下のコードを追記します。

development:
  fluent_host:   fluentd
  fluent_port:   24224
  tag:           'rails'
  messages_type: 'string'

test:
  fluent_host:   '127.0.0.1'
  fluent_port:   24224
  tag:           'rails'
  messages_type: 'string'

production:
  fluent_host:   '127.0.0.1'
  fluent_port:   24224
  tag:           'rails'
  messages_type: 'string'

またRailsのいろいろなログを見るためにscaffoldでCRUD処理を追加しておきます。

docker-compose run app rails g scaffold User name:string
docker-compose run app rails db:migrate

これでRails側の設定は完了です。

再度 docker-compose up --buildをして起動しましょう。

http://localhost/users このページにアクセスしてScaffoleで作られたページが見れたら成功です。

スクリーンショット 2021-01-27 18.30.00.png

IndexPatternを作成

1. kibanaにアクセス

docker-compose up --build

これでKibanaが起動したら
http://localhost:5601/app/management/kibana/indexPatterns/create
にアクセスします。

先程のScaffoldで作られたページ(http://localhost/users )にアクセスをすると、Railsが動くようになり、その結果Kibanaでraisl.access-**というIndex patternを作れるようになります。

スクリーンショット 2021-01-27 18.31.13.png

2. Index pattern nameというテキストフォームに rails.access-*を入力してNext spepに進みます

スクリーンショット 2021-01-27 18.34.11.png

3. Time fieldの値を @timestampにして「Create index pattern」ボタンを押して完成です

スクリーンショット 2021-01-27 18.34.25.png

4. IndexPatterの作成が終わったらログを実際に見てみましょう

http://localhost:5601/app/discover/ にアクセスすると、以下のように直近15分のログが確認できます。

スクリーンショット 2021-01-27 18.37.57.png

これでKibanaでRailsのログを見るための環境をDockerで作ることに成功しました。

おわりに

これでRails×MySQL×Nginx×Fluentd×Elaasticsearch×Kibbanaの環境ができました。

fluentdやKibanaを触ってみることが目的でしたが、Dockerの勉強にもなって楽しかったです。まだ触りたてなので、もっと適切な記述方法などがあればコメントで優しく教えていただけたら幸いです。

参考:https://chulip.org/entry/2019/08/18/233205, https://qiita.com/zgmf_mbfp03/items/0697cc827efa89e5d93e

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

ruby3.0 rails6.1 postgresql + redisなdocker環境を構築する

ruby:latest使ってるので
当然最新rubyになりますが、、、笑

redisはおまけ,
自分はsidekiqに使うので入れておきました。
いらなかったら消してください。

ディレクトリ構造

app
├─ Dockerfile
├─ Gemfile  
├─ Gemfile.lock
└─ docker-compose.yml

各ファイルの中身

Dockerfile
FROM ruby:latest

RUN curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - && \
  echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list && \
  apt-get update -qq && apt-get install -y \
  nodejs \
  yarn \
  imagemagick \
  build-essential \
  libpq-dev \
  postgresql-client

WORKDIR /app

COPY Gemfile Gemfile.lock /app/

RUN bundle install

docker-compose.yml
version: '3'

volumes:
  db-data:
  redis:
  bundle:
  node_modules:

services:
  web:
    build: .
    command: >
      bash -c "rm -f tmp/pids/server.pid &&
               bundle exec rails s -p 3000 -b '0.0.0.0'"
    ports:
      - '3000:3000'
    volumes:
      - '.:/app'
      - 'bundle:/usr/local/bundle:cached'
      - 'node_modules:/app/node_modules'
      - '/app/vendor'
      - '/app/tmp'
      - '/app/log'
      - '/app/.git'
    environment:
      - 'DATABASE_PASSWORD=postgres'
    tty: true
    stdin_open: true
    depends_on:
      - db
      - redis
    links:
      - db

  db:
    image: postgres
    environment:
      - 'POSTGRES_USER=postgres'
      - 'POSTGRES_PASSWORD=postgres'
    volumes:
      - 'db-data:/var/lib/postgresql/data'

  redis:
    image: redis:latest
    command: redis-server
    ports:
      - 6379:6379
    volumes:
      - redis:/data

node_modules,vendor,tem,log,.gitはup高速化のためvolumeを作成しました。

Gemfile
source 'https://rubygems.org'
gem 'rails', '~> 6.1'

Gemfile.lock
# 何も書かないでね

アプリ立ち上げ

appディレクトリにて以下のコマンドでapp立ち上げ

rspec使うので skip-testしてますがそこら辺はお好みで!
--forceは強制上書き、
--no-depsはリンクしたサービスを起動しない設定。
--webpackerはwebpacker:installをやってくれます。

docker-compose run web rails new . --force --no-deps --database=postgresql --skip-test --webpacker

webpackerがインストールできました!
となれば成功です。

次にdatabase.ymlを修正しましょう。

database.yml
default: &default
  adapter: postgresql
  encoding: unicode
  host: db
  user: postgres
  port: 5432
  password: <%= ENV.fetch("DATABASE_PASSWORD") %>
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>

development:
  <<: *default
  database: product_register_development

test:
  <<: *default
  database: product_register_test

production:
  <<: *default
  database: product_register_production
  username: product_register
  password: <%= ENV['PRODUCT_REGISTER_DATABASE_PASSWORD'] %>

修正したら、db:createを実行

docker-compose run web rake db:create

createできました!
となったら

docker-compose up

でcontainerとserver立ち上げて成功です!

docker-compose exec web bash

でコンテナ入って確認してみましょう。
※僕はtest_appという名前でアプリを作成しました。

スクリーンショット 2021-01-27 19.30.14.png

CMDとかENTRYPOINTとかcommand:とかvolume:とかcontainer_name:とか好きにカスタマイズしてください。

日々精進。

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

docker-composeを使ってDocker上にLaravelを構築する方法(Nginx & php-fpm & MySQL)

docker-composeを使ってDocker上にLaravelを構築する方法。WebサーバーはNignx + php-fpmを使用。

目次

  1. Laravelをローカルに構築
  2. .envファイルの設定
  3. dockerにcomposerのデータをコピー
  4. docker-compose.ymlを作成
  5. Dockerfileの作成
  6. PHPの構成
  7. NginxとPHP-FPM
  8. Nginxの設定
  9. MySQLの設定
  10. コンテナ起動
    1. コンテナの起動状況の確認
    2. ブラウザで確認
  11. (補足)ブラウザ表示でエラーが出た場合の対処法


1. Laravelをローカルに構築する

1-1. githubからクローン

git clone https://github.com/laravel/laravel.git <プロジェクト名>


1-2. composerを使ってバンドルをインストールする。

$ composer update

composer.jsonに書いてあるバンドルがズラーっとインストールされる。

1-3 .envファイルの設定

まずは.envファイルを作成する。(.env.exampleをコピーする)

cp .env.example .env

次にキーを作成する。

$ php artisan key:generate

MySQLと接続するためにDBの設定を変更する。

DB_CONNECTION=mysql
DB_HOST=db
DB_PORT=3306
DB_DATABASE=laravel
DB_USERNAME=root
DB_PASSWORD=******

DB_DATABASEとDB_PASSWORDは次で作成するdocker-composer.ymlと合わせる。


(補足)composerでプロジェクトを作成する方法

以下リンクのNo3まででローカルで動くLaravelの環境を構築できる。

Laravelプロジェクトの作り方


2. dockerにcomposerのデータをコピー

プロジェクトのルートディレクトリに移動後、以下を実行。

$ docker run --rm -v $(pwd):/app composer install
  • composerイメージからコンテナを作成しinstallを実行。
  • 現在のディレクトリをコンテナ内の/appにマウント。
  • コンテナ起動後にコンテナを削除する。

composerの必要なデータのみローカルの現在のディレクトリにコピーする。

バンドルが記述されたcomposer.lockがローカルのルートディレクトリに追加される。

docker runコマンド

docker run [オプション] イメージ [コマンド] [引数...]

--rmオプション

コンテナ終了時、自動的に削除。
composerのイメージとコンテナは不要なため。必要なデータのみマウントしたボリュームにコピーしておく。

-vオプション

ボリュームのマウント。
ローカルのディレクトリをコンテナ内のディレクトリにマウントする。

--volumeの略。

▼ボリュームのマウント方法

-v [ホスト側ソース:]コンテナ側送信先[:<オプション>]

  • -v $(pwd):/app
  • ホスト側ソースは絶対パスか、ボリューム名で指定。
    • $(pwd):ホスト側の現在のディレクトリパス
    • /app: コンテナ内のappディレクトリ


起動するイメージの指定

imageにcomposerを指定。タグを指定していない場合はlatest

dockerはまず、localでcomposer:latestを探す。なければdocker hubから指定のイメージを探す。

docker hun公式 composer image


コマンド実行例

$ docker run --rm -v $(pwd):/app composer install
Unable to find image 'composer:latest' locally
latest: Pulling from library/composer
8bb409133b6b: Pull complete 
Digest: sha256:447516936298f9f63d0b718d1a21bd70066b6c206e174cd29938c79b8eefe7b1
Status: Downloaded newer image for composer:latest
No lock file found. Updating dependencies instead of installing from lock file. Use composer update over composer install if you do not have a lock file.
Loading composer repositories with package information
Updating dependencies
Lock file operations: 105 installs, 0 updates, 0 removals
  - Locking asm89/stack-cors (v2.0.2)
  - Locking brick/math (0.9.2)
  (省略)
Writing lock file
Installing dependencies from lock file (including require-dev)
Nothing to install, update or remove
77 package suggestions were added by new dependencies, use `composer suggest` to see details.
Generating optimized autoload files
> Illuminate\Foundation\ComposerScripts::postAutoloadDump
> @php artisan package:discover --ansi
Discovered Package: facade/ignition
Discovered Package: fideloper/proxy
Discovered Package: fruitcake/laravel-cors
Discovered Package: laravel/sail
Discovered Package: laravel/tinker
Discovered Package: nesbot/carbon
Discovered Package: nunomaduro/collision
Package manifest generated successfully.
74 packages you are using are looking for funding.
Use the `composer fund` command to find out more!

ボリュームをマウントしているので、作成されたファイルがローカルに保存される。

image.png


3. docker-compose.ymlを作成する

$ vim docker-compose.yml

vimエディタでiをクリックしてinsertモードに入り、以下をコピペ。

コピペ後は、esc + :wqで保存して終了。

(参考)vimの使い方

docker-compose.yml

docker-compose.yml
version: '3'
services:

  #PHP Service
  app:
    build:
      context: .
      #dockerfile: Dockerfile
    image: app_php-fpm
    container_name: appdocker
    restart: unless-stopped
    tty: true
    environment:
      SERVICE_NAME: app
      SERVICE_TAGS: dev
    working_dir: /var/www
    volumes:
      - ./:/var/www
      - ./php/local.ini:/usr/local/etc/php/conf.d/local.ini
    networks:
      - app-network
    links:
      - webserver
      - db

  #Nginx Service
  webserver:
    image: nginx:alpine
    container_name: webserver
    restart: unless-stopped
    tty: true
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./:/var/www
      - ./nginx/conf.d/:/etc/nginx/conf.d/
    networks:
      - app-network

  #MySQL Service
  db:
    image: mysql:5.7.22
    container_name: db
    restart: unless-stopped
    tty: true
    ports:
      - "3306:3306"
    environment:
      MYSQL_DATABASE: laravel
      MYSQL_ROOT_PASSWORD: password
      SERVICE_TAGS: dev
      SERVICE_NAME: mysql
    volumes:
      - dbdata:/var/lib/mysql/
      - ./mysql/my.cnf:/etc/mysql/my.cnf
    networks:
      - app-network

#Docker Networks
networks:
  app-network:
    driver: bridge
#Volumes
volumes:
  dbdata:
    driver: local


4. Dockerfileの作成

$ vim Dockerfile

vimエディタでiをクリックしてinsertモードに入り、以下をコピペ。

コピペ後は、esc + :wqで保存して終了。

Dockerfile
FROM php:7.3-fpm

# Copy composer.lock and composer.json
COPY composer.lock composer.json /var/www/

# Set working directory
WORKDIR /var/www

# Install dependencies
RUN apt-get update && apt-get install -y \
    build-essential \
    libpng-dev \
    libjpeg62-turbo-dev \
    libfreetype6-dev \
    locales \
    zip \
    jpegoptim optipng pngquant gifsicle \
    vim \
    unzip \
    git \
    curl \
    libonig-dev \
    libzip-dev

# Clear cache
RUN apt-get clean && rm -rf /var/lib/apt/lists/*

# Install extensions
RUN docker-php-ext-install pdo_mysql zip exif pcntl
RUN docker-php-ext-configure gd --with-gd --with-freetype-dir=/usr/include/ --with-jpeg-dir=/usr/include/ --with-png-dir=/usr/include/
RUN docker-php-ext-install gd

# Install composer
RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer

# Add user for laravel application
RUN groupadd -g 1000 www
RUN useradd -u 1000 -ms /bin/bash -g www www

# Copy and change existing application directory permissions
COPY --chown=www:www . /var/www

# Change current user to www
USER www

# Expose port 9000 and start php-fpm server
EXPOSE 9000
CMD ["php-fpm"]

FROM php:7.2-fpm
ベースイメージにphp:7.2-fpmを指定。

▼php:7.2-fpm
PHP-FPMがインストールされているDebianベースのイメージ。


5. PHPの構成

ディレクトリPHPの中にlocal.iniを作成する。

$ mkdir php
$ vim php/local.ini
local.ini
upload_max_filesize=40M
post_max_size=40M

例として、PHPの構成として、アップロードできるファイルサイズ上限を40Mに設定している。

PHPの構成が完了したら、NginxでFastCGIサーバーとしてPHP-FPMを使用するように設定する。


NginxとPHP-FPM

NignxはApachと異なりPHPを直接処理できない。このため、PHP-FPMと接続してPHPを処理できるようにする

PHP-FPMはFast-CGI。Fast-CGIとは、WEBサーバー上でプログラムを動かすためのCGI(Common Gateway Interface)の改良版。Fastは速いの意味。

次のNginxの設定では、Fast-CGIとしてPHP-FPMを使うための設定を記述する。


6. Nginxの設定

nginx/conf.dディレクトリにapp.confファイルを作成する。

$ mkdir -p nginx/conf.d
$ vim nginx/conf.d/app.conf

vimエディタでiをクリックしてinsertモードに入り、以下をコピペ。

コピペ後は、esc + :wqで保存して終了。

app.conf
server {
    listen 80;
    index index.php index.html;
    error_log  /var/log/nginx/error.log;
    access_log /var/log/nginx/access.log;
    root /var/www/public;
    location ~ \.php$ {
        try_files $uri =404;
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_pass app:9000;
        fastcgi_index index.php;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param PATH_INFO $fastcgi_path_info;
    }
    location / {
        try_files $uri $uri/ /index.php?$query_string;
        gzip_static on;
    }
}

conf.d
.dはディレクトリ名であることを明示。
nginxではconf.d配下にあるすべての.confファイルを読み込む設定がされている。

設定ファイルの読み込み指示例
include /etc/nginx/conf.d/*.conf;

server
httpのサーバーの設定。

index
indexディレクティブ。URIが/で終わる場合(ディレクトリが指定されている)場合にリダイレクトするファイルを指定する。

indexディレクティブの例
index index.php index.html;

リクエストされたURIが/hoge/の場合に、index.phpがあれば、hoge/index.phpにリダイレクトする。ない場合はhoge/index.htmlにリダイレクトする。

デフォルトはindex index.htmlとなっている。

listen 80
listenディレクティブ。nginxサーバーが使用するポート番号に80を指定。

error_log
エラーログを書き込むためのファイルを指定。

access_log
アクセスログを書き込むためのファイルを指定。

root
ドキュメントルートのディレクトリを指定する。
「/var/www/public」を指定している。

location
特定のuriでリクエストがきた場合の処理を設定。prefixを使って指定する(完全一致や正規表現など)。

指定したuriに一致したら{ }内の処理を実行する。

location <prefix> <uri> {
    処理
}

▼正規表現の指定

prefix 内容
なし 前方一致
= 完全一致
~ 正規表現(大文字・小文字を区別する)
~* 正規表現(大文字・小文字を区別しない)

実例(PHP-FPMの設定)

以下はNginxをPHP-FPMに接続する設定。

    location ~ \.php$ {
        try_files $uri =404;
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_pass app:9000;
        fastcgi_index index.php;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param PATH_INFO $fastcgi_path_info;
    }

正規表現で、\.php$ (末尾が.php)なら処理を実行。

・try_files

指定したパスの存在確認をする。(if文に似た処理)
上記であれば、.phpにアクセスした場合は404を返す。

・fastcgi_split_path_info

リクエストで渡されたURIを2分割する。

fastcgi_split_path_info ^(正規表現1)(正規表現2)$;

▼正規表現1
$fastcgi_script_nameの値になる。
ここでは、(.+.php)なので、「*.php」が該当。

▼正規表現2
$fastcgi_path_infoの値になる。
ここでは、(/.+)なので、*.php/以下が該当。

▼実例
例えばリクエストが/show.php/article/0001の場合以下のようになる。

$fastcgi_script_name=$document_root/show.php
$fastcgi_path_info=article/0001

・fastcgi_pass アドレス

Fast-CGIサーバーのアドレスを指定する。

・include ファイル

指定したファイルを読み込む。
ここでは設定ファイルとしてfastcgi_paramsを読み込んでいる。

DebianやUbuntuの場合設定ファイルはデフォルトで/etc/nginx/fastcgi_paramsにある。
NGINX PHP FastCGI Example

・fastcgi_index ファイル

URIのパスがスラッシュ/で終わる(ディレクトリを指定)場合に開くファイルを指定。

・fastcgi_param SCRIPT_FILENAME ファイル

実行するファイル名を指定する。

$document_root$fastcgi_script_nameなので、ドキュメントルートの後に、$fastcgi_script_nameの値が続く。

▼$fastcgi_script_name
fastcgi_split_path_infoの一つ目の正規表現が該当する。つまり「*.php」となり、リクエストされたURIと一致するphpファイルが実行される。

fastcgi_indexとfastcgi_param SCRIPT_FILENAME

渡されたURIによって使用するphpファイルを切り替える処理になる。

fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME /home/www/scripts/php$fastcgi_script_name;

上記設定に対して、/page.phpが渡された場合、SCRIPT_FILENAMEが処理され、/home/www/scripts/php/page.phpが実行される。

一方、URIの終わりがスラッシュで、/page.php/の場合、fastcgi_indexが処理され、home/www/scripts/php/index.phpが実行される。

・fastcgi_param PATH_INFO

パスの情報。
ここではリクエストされたパスの.php以降の内容が入る。

・(参考)Nginx http_fastgis_modules


2つ目のlocationの処理内容

location / {
        try_files $uri $uri/ /index.php?$query_string;
        gzip_static on;
    }

リクエストされたURIが/*(前方一致)の場合、処理を実行する。

try_files $uri $uri/ /index.php?

ファイルの存在確認をしあれば実行。なければ次のファイルの存在確認を行う。
存在確認する優先順位は、$uri, $uri/, /index.php?となる。

gzip_static on;

gzipで圧縮済みのファイル「.gz」があればそちらを配信する。


7. MySQLの設定

mysqlディレクトリの中にmy.cnfファイルを作成する。

$ mkdir mysql
$ vim mysql/my.cnf

vimエディタでiをクリックしてinsertモードに入り、以下をコピペ。

コピペ後は、esc + :wqで保存して終了。

app.conf
[mysqld]
general_log = 1
general_log_file = /var/lib/mysql/general.log

ここではMySQLのログの設定をしている。

・general_log

値を1(or ON)にするとログを出力する。
0(or OFF or 記述なし)の場合はログ出力をしない。

・general_log_file

指定したファイルにログを出力する。


7. コンテナ起動

docker-compose.ymlのあるディレクトリで以下を実行する。

$ docker-compose up -d app

サービスappを起動すれば、linkでwebserver, dbもつながっているため同時に起動する。

-dは デタッチド・モード。バックグラウンドでコンテナを実行するため、コンテナ起動後もそのターミナルでコマンドが打てる状態になる。

▼(補足)サービスを指定しない場合
サービスの指定がなければ、docker-composeに記述されたすべてのサービスが起動する。

コンテナの起動状況の確認

$ docker psコマンドで起動中のコンテナ一覧を表示できる。

$ docker ps
CONTAINER ID   IMAGE                       COMMAND                  CREATED          STATUS          PORTS                                      NAMES
04012cd50d18   mysql:5.7.22                "docker-entrypoint.s…"   6 minutes ago    Up 6 minutes    3306/tcp, 0.0.0.0:3306->3306/tcp           db
6d0cd9138856   app_php-fpm                 "docker-php-entrypoi…"   6 minutes ago    Up 6 minutes    9000/tcp                                   app
f02cf8788728   nginx:alpine                "/docker-entrypoint.…"   6 minutes ago    Up 6 minutes    0.0.0.0:80->80/tcp, 0.0.0.0:443->443/tcp   webserver

docker-composeのサービスで定義した、app, webserver, dbが起動している。

ブラウザで確認

localhost:80を入力するとLaravelのwelcome.blade.phpが表示される。

image.png


(補足)ブラウザ表示でエラーが出た場合の対処法

以下のようなエラーが出たらPHPのバージョンを上げ、インストールするバンドルやエクステンションを変更することで解決できる。

Fatal error: Composer detected issues in your platform: Your Composer dependencies require a PHP version ">= x.x.x". You are running y.y.y. in /var/www/vendor/composer/platform_check.php on line 24

PHPバージョンエラー対処法



以上。

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

【Dcoker・Laravel】エラー対処法 Fatal error: Composer detected issues in your platform: Your Composer dependencies require a PHP version

DockerでLaravelを起動した際に、以下のエラーが発生した時の対処法。

Fatal error: Composer detected issues in your platform: Your Composer dependencies require a PHP version ">= 7.3.0". You are running 7.2.34. in /var/www/vendor/composer/platform_check.php on line 24

Composerを使うために必要なPHPのバージョンが7.3.0以上である必要があるのに、低すぎるというエラー。

PHPのバージョンを上げればいい。

問題のDockerfile

ベースイメージにphpの7.2を指定してある。

Dockerfile
FROM php:7.2-fpm

# Copy composer.lock and composer.json
COPY composer.lock composer.json /var/www/

# Set working directory
WORKDIR /var/www

# Install dependencies
RUN apt-get update && apt-get install -y \
    build-essential \
    libpng-dev \
    libjpeg62-turbo-dev \
    libfreetype6-dev \
    locales \
    zip \
    jpegoptim optipng pngquant gifsicle \
    vim \
    unzip \
    git \
    curl

# Clear cache
RUN apt-get clean && rm -rf /var/lib/apt/lists/*

# Install extensions
RUN docker-php-ext-install pdo_mysql mbstring zip exif pcntl
RUN docker-php-ext-configure gd --with-gd --with-freetype-dir=/usr/include/ --with-jpeg-dir=/usr/include/ --with-png-dir=/usr/include/
RUN docker-php-ext-install gd

# Install composer
RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer

# Add user for laravel application
RUN groupadd -g 1000 www
RUN useradd -u 1000 -ms /bin/bash -g www www

# Copy existing application directory contents
COPY . /var/www

# Copy existing application directory permissions
COPY --chown=www:www . /var/www

# Change current user to www
USER www

# Expose port 9000 and start php-fpm server
EXPOSE 9000
CMD ["php-fpm"]

対応方法

  1. ベースイメージのバージョンを上げる。(FROM php:7.3-fpm)
  2. バンドルでlibonig-devとlibzip-devを追加。(apt-get install -yのところ)
  3. エクステンションのmbstringを削除(libonigが代替する)

修正後のDockerfile

Dockerfile
FROM php:7.3-fpm

# Copy composer.lock and composer.json
COPY composer.lock composer.json /var/www/

# Set working directory
WORKDIR /var/www

# Install dependencies
RUN apt-get update && apt-get install -y \
    build-essential \
    libpng-dev \
    libjpeg62-turbo-dev \
    libfreetype6-dev \
    locales \
    zip \
    jpegoptim optipng pngquant gifsicle \
    vim \
    unzip \
    git \
    curl \
    libonig-dev \
    libzip-dev

# Clear cache
RUN apt-get clean && rm -rf /var/lib/apt/lists/*

# Install extensions
RUN docker-php-ext-install pdo_mysql zip exif pcntl
RUN docker-php-ext-configure gd --with-gd --with-freetype-dir=/usr/include/ --with-jpeg-dir=/usr/include/ --with-png-dir=/usr/include/
RUN docker-php-ext-install gd

# Install composer
RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer

# Add user for laravel application
RUN groupadd -g 1000 www
RUN useradd -u 1000 -ms /bin/bash -g www www

# Copy existing application directory contents
COPY . /var/www

# Copy existing application directory permissions
COPY --chown=www:www . /var/www

# Change current user to www
USER www

# Expose port 9000 and start php-fpm server
EXPOSE 9000
CMD ["php-fpm"]

以上で完了。


PHPのバージョンがcomposerよりも高い場合。

上記はPHPのバージョンが指定よりも低かったために発生したエラー。

仮に、PHPのバージョンが高すぎてダウングレードを要求された場合、ダウングレードせずとも、composerの設定ファイルを書き換えることで対応できる。(らしい)

対応方法

composer.jsonのconfigに以下を追加

    "config": {
        (省略)
        "platform-check": false
    },

これのみ。
以上で、エラーを拾っていた機能をOFFにできる。


(補足)エラーを実行している処理

ちなみに、エラーを拾っている処理が書かれているのは、vendor/composer/platform_check.php

image.png

platform_check.php
<?php

// platform_check.php @generated by Composer

$issues = array();

if (!(PHP_VERSION_ID >= 70300)) {
    $issues[] = 'Your Composer dependencies require a PHP version ">= 7.3.0". You are running ' . PHP_VERSION . '.';
}

if ($issues) {
    if (!headers_sent()) {
        header('HTTP/1.1 500 Internal Server Error');
    }
    if (!ini_get('display_errors')) {
        if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') {
            fwrite(STDERR, 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . implode(PHP_EOL, $issues) . PHP_EOL.PHP_EOL);
        } elseif (!headers_sent()) {
            echo 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . str_replace('You are running '.PHP_VERSION.'.', '', implode(PHP_EOL, $issues)) . PHP_EOL.PHP_EOL;
        }
    }
    trigger_error(
        'Composer detected issues in your platform: ' . implode(' ', $issues),
        E_USER_ERROR
    );
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Vagrantからlocalhostを経由してdockerコンテナのAPIをコールする

はじめに

vagrantとdockerを併用して開発を行っています。
(vagrantの中にdockerを立てているとかではなく、それぞれ同じ階層で独立している)

なぜこんな複雑な環境になっているのかはさておき、
同じような問題で悩んでいる方がもしかしたらいるかもしれないので
備忘録として手順を残しておきたいと思います。

やりたいことのイメージ

vagrant → ローカル → dockerコンテナ(APIサーバー)

vagrantの仮想環境からAPIを呼び出し、
localhostを経由してdockerコンテナのAPIを実行します。

環境構築

ここでは概要しか載せません。
具体的な環境構築手順は検索してください。

vagrant

VirtualBoxを利用して構築します。
一般的な構築で問題ありません。

docker

docker-compose.yml
ローカルホストの8081ポートをdockerコンテナの80ポートに接続します。
(ローカルホストのポート番号は自由に変更してください)

version: '3.5'
services:
  nginx:
    build:
      (省略)
    environment:
      TZ: Asia/Tokyo
    ports:
      - "8081:80"
      - "4431:443"
    depends_on:
      - php-fpm

  php-fpm:
    build:
      (省略)

この時点でできていること

vagrantとlocalhostの繋ぎ込み

実は初期設定でポートフォワーディングができている。
vagrant(ゲストOS)からlocalhost(ホストOS)を参照する際には10.0.2.2のIPを利用する。

VirtualBox仮想マシンのネットワークはデフォルトでNATとなっている。 VirtualBox の場合、NAT ネットワークアダプタには 10.0.2.0/24 の IP アドレスが割り当てられ、ゲスト OS から見える ホスト OS の IP アドレスには 10.0.2.2 が設定される仕様。
したがってホストOS上に建てたサーバーにゲストOSからアクセスしたい場合は、http://10.0.2.2とアクセスすれば普通に見ることができる。
https://garbagetown.hatenablog.com/entry/20100313/1270039659

※ちなみに
localhost(ホストOS)からvagrant(ゲストOS)の繋ぎ込みは127.0.0.1:2222のIPを利用。
詳細はlocalhostでvagrant ssh-configで確認可能。
Workbenchをvagrant内のmysqlに接続したいときなどに便利。

❯ vagrant ssh-config                                                                                                                                                                             [12:45:47]
Host default
  HostName 127.0.0.1
  User vagrant
  Port 2222
  UserKnownHostsFile /dev/null
  StrictHostKeyChecking no
  PasswordAuthentication no
  IdentityFile (省略)
  IdentitiesOnly yes
  LogLevel FATAL

localhostとdockerコンテナの繋ぎ込み

前述のdocker-compose.ymlで設定した通り、
localhost:8081にアクセスすればdockerコンテナの80ポートに接続できる。

結論

vagrant→ローカルホスト→dockerコンテナのAPIを呼び出すには
vagrant から10.0.2.2のIPを利用すればOK

参考

https://garbagetown.hatenablog.com/entry/20100313/1270039659

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

Don't Panic: Kubernetes and Docker の日本語訳

https://kubernetes.io/blog/2020/12/02/dont-panic-kubernetes-and-docker/
の日本語訳です。仕事で読む機会があったので、せっかくなので残しておきます。
なお、私は翻訳のプロではなく、またわかりやすさのため直訳ではなく意訳する場合があります。
疑問点があれば、公式サイトを参照してください。

K8sはv1.20より新しいバージョンでDockerをサポートしません。 (deprecating Docker

パニックを起こす必要はありません。想像するような抜本的なものではないのです。

TL;DR 
RuntimeとしてのDockerは、Kubernetesのために開発されたCRI(Container Runtime Interface)を利用しているRuntimeを選んだ結果として使用出来なくなります。しかし、Dockerによって生成されたImageはこれからも、今までもそうだったように、みなさんのClusterで使用可能です。

もし、あなたがKubernetesのエンドユーザーであるならば、多くの変化はないでしょう。これはDockerの死を意味するものではありませんし、開発ツールとしてDockerを使用するべきでない、使用することは出来ないと言っているのでもありません。Dockerはコンテナを作成するのに便利なツールですし、docker buildコマンドで作成されたImageはKubernetesクラスタ上でこれからも動作可能なのです。

もし、GKE、EKS、AKSといったマネージドK8sサービスを使っているのなら、Workerノードがサポート対象のRuntimeを使用しているか、Dockerのサポートが将来のK8sバージョンで切れる前に確認しておく必要があるでしょう。
もし、ノードをカスタマイズしているのなら、環境やRuntimeの仕様に合わせて更新する必要があるでしょう。サービスプロバイダと確認し、アップグレードのための適切なテストと計画を立ててください。

もし、ご自身でClusterを管理しているのなら、やはり問題が発生する前に必要な対応を行う必要があります。v1.20の時点で、Dockerの使用についての警告メッセージが表示されるようになります。将来のK8sリリースでDockerのRuntimeとしての使用がサポートされなくなれば、containerdCRI-Oといった他の適切なRuntimeに切り替える必要があります(現時点では、v1.22を予定しています。)。切り替える際、そのRuntimeが現在使用しているDockerDaemonの設定をサポートすることを確認してください。(Loggingなど)

では、なぜ混乱が生じ、誰もが恐怖に駆られているのか。

ここで議論になっているのは2つの異なる場面についてであり、それが混乱の原因になっています。Kubernetesクラスタの内部では、Container runtimeと呼ばれるものがあり、それはImageをPullし起動する役目を持っています。Dockerはその選択肢として人気があります(他にはcontainerdやCRI-Oが挙げられます。)が、しかしDockerはそれ自体がKubernetesの一部として設計されているわけではありません。これが問題の原因となっています。

お分かりかと思いますが、ここで”Docker”と呼んでいるものは、ある1つのものではなく、その技術的な体系の全体であり、その一部には"containerd"と呼ばれるものもあり、これはそれ自体がハイレベルなContainer runtimeとなっています。Dockerは素晴らしいもので、便利です。なぜなら、多くのUXの改善がされており、それは人間が開発を行うための操作を簡単にしているのです。しかし、それらはKubernetesに必要なものではありません。Kubernetesは人間ではないからです。
このhuman-friendlyな抽象化レイヤが作られてために、結果としてはKubernetesクラスタはDockershimと呼ばれるほかのツールを使い、必要な要件を満たす必要が出てきました。このツールはcontainerdと呼ばれます。これは素晴らしいとは言えません。なぜなら、我々がメンテする必要のあるものが増えますし、それは問題が発生する要因ともなります。ここで実際に怒っているものは、Dockershimを(最も早い場合で)v1.23でkubeletから除外することです。そのため、結果としてDockerのサポートがなくなるということなのです。
ここで、containersがDockerに含まれているなら、なぜDockershimが必要なのかと疑問に思われるかたもいるでしょう。

DockerはCRI(Container Runtime Interface)に準拠していません。もしそうであればShimは必要ないのですが、現実はそうでありません。
しかし、これは世界の終わりでありません、心配しないでください。みなさんがしなければならないことはContainer runtimeをDockerから他のRuntimeに切り替えるだけなのです。

1つ注意すべきことは、クラスタで行われる処理のなかでDocker socket(/var/run/docker.sock)
に依存する部分がある場合、他のRuntimeへ切り替えるとこの部分が働かなくなるでしょうか。このパターンはDocker in Dockerと呼ばれます。このような場合の対応方法はたくさんあります。kanikoimgbuildahなどです。

開発者にとって、この変更は何を意味するのか。Dockerfileはこれからも有効なのか。これからもDockerでビルドを行ってよいのか。

この変更は、Dockerを直接操作している多くのみなさんとは別の場面に影響を与えるでしょうか。
みなさんが開発を行う際に使用しているDockerと、Kubernetesクラスタの内部で使われているDocker runtimeは関係ありません。これがわかりにくいかもしれません。開発者にとって、Dockerはこれからも便利なものであり、このアナウンスがあった前と変わらないでしょう。DockerでビルドされたImageは、決してDockerでだけ動作するというわけではありません。それはOCI Image(Open Container Initiative)と呼ばれるものです。あらゆるOCI準拠のImageは、それを何のツールでビルドしたかは問題ではなく、Kubernetesから見れば同じものなのです。containerdもCRI-Oも、そのようなImageをPullし、起動することが出来ます。
これがコンテナの仕様について、共通の仕様を策定している理由なのです。

さて、この変更は決定しています。いくつかの問題は発生するかもしてませんが、決して壊滅的なものではなく、ほとんどの場合は良い変化となるでしょうか。Kubernetesをどのように使用しているかによりますが、この変更が特に何の影響も及ぼさない人もいるでしょうし、影響がとても少ない場合もあります。長期的に見れば、物事を簡単にするのに役立つものです。
もし、この問題がまだわかりにくいとしても、心配しないでください。Kubernetesでは多くのものが変化しており、その全てに完璧に精通している人など存在しません。
経験の多寡や難易度にかかわらず、どんなことでも質問してください。我々の目標は、全ての人が将来の変化について、可能な限りの知識と理解を得られることです。
このブログが多くの質問の答えとなり、不安を和らげることができればと願っています。

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

Dockerでインターネットへの出口の無いコンテナを立てる

インターネットへの出口の無いネットワークを作成

docker network create --internal --subnet <ip> no-internet

コンテナ実行時

docker run --network no-internet <コンテナイメージ> <コマンド>
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

docker volume 操作

# volumeのリスト
docker volume ls

# volumeの情報確認
# バーチャルマシン上の保存先が出るので実際にそのpathは存在しない
docker volume inspect <volume name>

# 削除
docker volume rm <volume name>

# どのコンテナでも使われていないvolumeを削除
docker volume prune
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む