- 投稿日:2020-08-23T23:40:24+09:00
【面倒臭がりさん必見】たった1つのコマンドでRails + React環境構築
突然ですが、環境構築は好きですか?
...私は苦手です。
端が丸まってくっついちゃったサランラップぐらい苦手です。恐らく、あなたがこの記事を開いたということは
少なからず環境構築に苦手意識を持っているのではないでしょうか。「もっと気軽に環境構築できたらいいのにな...」
というあなたの心の声を受け
今回は『コマンド1つでRailsとReactの環境構築ができるシェルスクリプト』をつくりました。たった1つのコマンドを叩くだけ!
あとは『Netflix』で好きな動画でも見ていれば
勝手に環境構築が終わっているという"夢の世界"がここにあります!さらに!
環境構築終了後、すぐに開発が始められるよう
Rails側のトップページとReact側のエントリーポイントも自動で生成される完全親切設計版です!
Reactのコンポーネントが初めからゴリゴリ書ける状態になっています。以下、本文
前提必須条件
Dockerがインストール済みであること
主にインストールされるもの
インストールされるもの バージョン Ruby 2.6.3 Rails 6.0.3.2 Node.js 10.x PostgreSQL 12.3 foreman(Gem) 0.87.2 インストールされるReact周りのパッケージ群
- redux
- react-redux
- react-router-dom
- redux-devtools-extension
- redux-form
- redux-thunk
- axios
- babel-plugin-root-import
- redux-toolkit
- material-ui
使い方の手順(この記事のメイン)
- 好きな名前でフォルダを作成する
- 1で作成したフォルダの中に『docker_rails_react.sh』という空ファイルを作成する
- 2で作成したファイルに下記『シェルスクリプト本体』を全文コピーして保存する
- ターミナルを起動
- 1で作成したフォルダに移動し、コマンド『bash docker_rails_react.sh』を実行する
- Netflixを見て時間を潰す(数分〜数十分間)
- 『Compiled successfully.』がターミナルに表示されたらhttp://localhost:3000/にアクセス
完※macOS動作検証済み
シェルスクリプト本体
docker_rails_react.sh
#!/bin/bash ENV APT_KEY_DONT_WARN_ON_DANGEROUS_USAGE=DontWarn APP_NAME=$(basename `pwd`) echo APP_NAME: $APP_NAME UPPER_APP_NAME=`echo $APP_NAME | tr "[:lower:]" "[:upper:]"` echo UPPER_APP_NAME: $UPPER_APP_NAME # make Procfile.dev cat <<'EOF' > Procfile.dev web: bundle exec rails s -p 3000 -b '0.0.0.0' webpacker: bin/webpack-dev-server EOF # make Dockerfile cat <<EOF > Dockerfile FROM ruby:2.6.3 RUN apt-get update -qq && apt-get install -y nodejs postgresql-client # install yarn RUN apt-get update && apt-get install -y curl apt-transport-https wget && \\ curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - && \\ echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list && \\ apt-get update && apt-get install -y yarn # install Node.js RUN curl -sL https://deb.nodesource.com/setup_10.x | bash - && \\ apt-get install -y nodejs RUN mkdir /$APP_NAME WORKDIR /$APP_NAME COPY Gemfile /$APP_NAME/Gemfile COPY Gemfile.lock /$APP_NAME/Gemfile.lock RUN bundle install RUN bundle exec rails webpacker:install RUN bundle exec rails webpacker:install:react COPY . /$APP_NAME # Add a script to be executed every time the container starts. COPY entrypoint.sh /usr/bin/ RUN chmod +x /usr/bin/entrypoint.sh ENTRYPOINT ["entrypoint.sh"] EXPOSE 3000 # Start the main process. CMD ["rails", "server", "-b", "0.0.0.0"] EOF # make Gemfile cat <<'EOF' > Gemfile source 'https://rubygems.org' gem 'rails', '~> 6.0', '>= 6.0.3.2' EOF # make Gemfile.lock touch Gemfile.lock # make docker-compose.yml cat <<EOF > docker-compose.yml version: '3' services: db: image: postgres volumes: - ./tmp/db:/var/lib/postgresql/data environment: POSTGRES_PASSWORD: password web: build: . command: bash -c "rm -f tmp/pids/server.pid && bundle exec foreman start -f Procfile.dev" volumes: - .:/$APP_NAME ports: - "3000:3000" - "3035:3035" environment: ${UPPER_APP_NAME}_DB_HOST: db ${UPPER_APP_NAME}_DB_USERNAME: postgres ${UPPER_APP_NAME}_DB_PASSWORD: password ${UPPER_APP_NAME}_DEVELOPMENT_DB: ${APP_NAME}_development ${UPPER_APP_NAME}_TEST_DB: ${APP_NAME}_test depends_on: - db EOF # make entrypoint.sh cat <<EOF > entrypoint.sh #!/bin/bash set -e # Remove a potentially pre-existing server.pid for Rails. rm -f /$APP_NAME/tmp/pids/server.pid # Then exec the container's main process (what's set as CMD in the Dockerfile). exec "\$@" EOF echo "docker-compose run web rails new . --force --no-deps -–skip-turbolinks --webpack=react --database=postgresql" docker-compose run web rails new . --force --no-deps -–skip-turbolinks --webpack=react --database=postgresql # fix config/database.yml echo "fix config/database.yml" rm -f config/database.yml cat <<EOF > config/database.yml default: &default adapter: postgresql encoding: unicode host: <%= ENV['${UPPER_APP_NAME}_DB_HOST'] %> username: <%= ENV['${UPPER_APP_NAME}_DB_USERNAME'] %> password: <%= ENV['${UPPER_APP_NAME}_DB_PASSWORD'] %> pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> development: <<: *default database: <%= ENV['${UPPER_APP_NAME}_DEVELOPMENT_DB'] %> test: <<: *default database: <%= ENV['${UPPER_APP_NAME}_TEST_DB'] %> production: <<: *default database: <%= ENV['${UPPER_APP_NAME}_DB'] %> username: <%= ENV['${UPPER_APP_NAME}_DB_USERNAME'] %> password: <%= ENV['${UPPER_APP_NAME}_DB_PASSWORD'] %> EOF # fix config/webpacker.yml cat config/webpacker.yml | sed "s/host: localhost/host: 0.0.0.0/g" > __tmpfile__ cat __tmpfile__ > config/webpacker.yml rm __tmpfile__ # fix config/routes.rb rm -f config/routes.rb cat <<'EOF' > config/routes.rb Rails.application.routes.draw do # For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html root "top#show" end EOF # make .babelrc .babelrc cat <<'EOF' > .babelrc { "plugins": [ [ "babel-plugin-root-import", { "paths": [ { "rootPathSuffix": "./app/javascript/src", "rootPathPrefix": "~/" }, ] } ] ] } EOF # fix config/webpack/environment.js rm -f config/webpack/environment.js cat <<'EOF' > config/webpack/environment.js const { environment } = require('@rails/webpacker') environment.splitChunks(); module.exports = environment EOF # make top_controller.rb cat <<'EOF' > app/controllers/top_controller.rb class TopController < ApplicationController def show end end EOF # fix application.html.erb rm -f app/views/layouts/application.html.erb cat <<EOF > app/views/layouts/application.html.erb <!DOCTYPE html> <html> <head> <title>$APP_NAME</title> <%= csrf_meta_tags %> <%= csp_meta_tag %> <%= javascript_pack_tag 'application' %> </head> <body style="margin: 0;"> <div id="root"> <%= yield %> </div> </body> </html> EOF # make top.html.erb mkdir -p app/views/top cat <<'EOF' > app/views/top/show.html.erb <%= javascript_packs_with_chunks_tag 'index' %> EOF # make entrypoint cat <<'EOF' > app/javascript/packs/index.jsx import React from 'react'; import { render } from 'react-dom'; import { Provider } from 'react-redux'; import { createStore, applyMiddleware } from 'redux'; import thunk from 'redux-thunk' import { Router, Route, Switch, IndexRoute, useLocation } from 'react-router-dom'; import { createBrowserHistory } from 'history'; import { composeWithDevTools } from 'redux-devtools-extension'; // reducer import rootReducer from '~/reducers/'; // Component import Top from '~/components/tops/'; const middleWares = [thunk]; const enhancer = process.env.NODE_ENV === 'development' ? composeWithDevTools(applyMiddleware(...middleWares)) : applyMiddleware(...middleWares); const store = createStore(rootReducer, enhancer); const customHistory = createBrowserHistory(); render( <Provider store={store}> <Router history={customHistory}> <Route render={({ location }) => ( <Switch location={location}> <Route exact path='/' component={Top} /> </Switch> )}/> </Router> </Provider>, document.getElementById('root') ) EOF # add foreman-gem echo "gem 'foreman', '~> 0.87.2'" >> Gemfile docker-compose run web bundle install docker-compose build echo "install package" echo "docker-compose run web yarn add redux react-redux react-router-dom redux-devtools-extension redux-form redux-thunk axios @babel/preset-react babel-plugin-root-import @reduxjs/toolkit @material-ui/core" docker-compose run web yarn cache clean docker-compose run web yarn add redux react-redux react-router-dom redux-devtools-extension redux-form redux-thunk axios @babel/preset-react babel-plugin-root-import @reduxjs/toolkit @material-ui/core echo "docker-compose run web rake db:create" docker-compose run web rake db:create # make styles/images dir mkdir -p app/javascript/src/styles mkdir -p app/javascript/src/images # make component mkdir -p app/javascript/src/components/tops cat <<'EOF' > app/javascript/src/components/tops/index.jsx import React from 'react'; import { useSelector, useDispatch } from 'react-redux'; import { makeStyles } from '@material-ui/core/styles'; import Typography from '@material-ui/core/Typography'; import { topInitial } from '~/modules/tops/'; const useStyles = makeStyles(theme => ({ successed: { color: '#036ab5', }, })); const Top = () => { const classes = useStyles(); const dispatch = useDispatch(); const topState = useSelector(state => state.top); React.useEffect(() => { dispatch(topInitial({ initial: true })); }, []); return ( <> <Typography variant="h1" align="center">Hello Rails on React</Typography> { topState.initial && <Typography variant="h5" align="center" className={ classes.successed }>SUCCESSED</Typography> } </> ) } export default Top; EOF # make module mkdir -p app/javascript/src/modules/tops cat <<'EOF' > app/javascript/src/modules/tops/index.jsx import { createSlice } from '@reduxjs/toolkit'; const initialState = { initial: false, } const top = createSlice({ name: 'top', initialState, reducers: { topInitial(state, action) { const { initial } = action.payload; return { ...state, initial: initial, } } } }) export const { topInitial } = top.actions export default top.reducer; EOF # make reducer mkdir -p app/javascript/src/reducers cat <<'EOF' > app/javascript/src/reducers/index.jsx import { combineReducers } from 'redux'; import { reducer as formReducer } from 'redux-form'; import top from '~/modules/tops/'; export default combineReducers({ form: formReducer, top, }); EOF echo "docker-compose up" docker-compose up初期生成ファイルの仕様(気になる人向け)
- 状態管理はReduxを使用
- ファイル構成はDucksパターンを使用
- Redux Toolkitを使用
- Redux Formを使用
- Material-UIを使用
- React関連のソースパスを『.babelrc』ファイルで管理
- webpackにsplitChunksを使用
- foremanを使用し、railsサーバーとwebpack-dev-serverを同一コンテナで起動
- top_controllerのshowアクションをRootに設定
- database.ymlの環境変数名にはフォルダ名が適用される
- ページタイトルにはフォルダ名が適用される
- TypeScript未対応
ディレクトリ構成(気になる人向け)
任意のフォルダ ├── app │ ├── controllers │ │ └── top_controller.rb │ ├── javascript │ │ ├── packs │ │ │ ├── application.js │ │ │ └── index.jsx │ │ └── src │ │ ├── components │ │ │ └── tops │ │ │ └── index.jsx │ │ ├── images │ │ ├── modules │ │ │ └── tops │ │ │ └── index.jsx │ │ ├── reducers │ │ │ └── index.jsx │ │ └── styles │ └── views │ ├── layouts │ │ └── application.html.erb │ └── top │ └── show.html.erb ├── config │ ├── database.yml │ ├── routes.rb │ ├── webpack │ │ └── environment.js │ └── webpacker.yml ├── docker_rails_react.sh ├── docker-compose.yml ├── Dockerfile ├── entrypoint.sh ├── Gemfile ├── Gemfile.lock ├── package.json ├── Procfile.dev └── yarn.lock※最適化されていない(無駄な処理がある)のはご容赦ください
では、楽しいRails on React生活を!
- 投稿日:2020-08-23T23:15:09+09:00
GithubActionでmasterプッシュ時にECRにimageをpushする
aws ECRにリポジトリを作る
- awsコンソールにログイン
- 検索窓から「ecr」と検索すると「Amazon Container Services」というのが出てくるのでクリック
- リポジトリを作成
![]()
- 今回は「test」という名前でリポジトリを作成します(名前は任意の名前で大丈夫です!)
![]()
Github Actionsを動かすアクセスキー、シークレットキーを取得
- awsコンソールにログイン
- 検索窓から「iam」と検索
- ユーザー追加をクリック
4.名前に「github-actions」(名前は任意の名前で大丈夫です!)アクセスの種類は「プログラムによるアクセス」にチェックして次へ
5.「既存のポリシーを直接アタッチ」を選択→「AmazonEC2ContainerRegistryPowerUser」と検索すると権限が出てくるのでこちらをチェック
![]()
- あとは全て次へで大丈夫です。そしたら以下のような画面になるのでアクセスキーとシークレットキーをメモしておく
![]()
Githubにシークレットキーを登録
- githubの該当のリポジトリにいき下記3つの変数を追加する
- AWS_ACCESS_KEY_ID(先ほど取得したアクセスキー)
- AWS_SECRET_ACCESS_KEY(先ほど取得したシークレットキー)
- AWS_ECR_REPO_NAME(ECRで作成したリポジトリ名*今回の場合は
test)上記のように変数を指定することで
${{ secrets.キー名 }}とすることでその環境変数を使用できるgithub actionsのymlを作成
.github/workflows/config.ymlname: Build and Push on: push: branches: - master jobs: build-and-push: runs-on: ubuntu-18.04 timeout-minutes: 300 steps: - uses: actions/checkout@v1 - name: Configure AWS Credentials uses: aws-actions/configure-aws-credentials@v1 with: aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} aws-region: ap-northeast-1 - name: Login to Amazon ECR id: login-ecr uses: aws-actions/amazon-ecr-login@v1 - name: Build, tag, and push image to Amazon ECR env: ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }} ECR_REPOSITORY: ${{ secrets.AWS_ECR_REPO_NAME }} run: | docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:latest . docker push $ECR_REGISTRY/$ECR_REPOSITORY:latest確認!
- aws ECRにリポジトリを作る
- Github Actionsを動かすアクセスキー、シークレットキーを取得
- Githubにシークレットキーを登録
- github actionsのymlを作成
上記4点ができたら
githubにプッシュしてみてくださいするとgithub actionsが動きリポジトリにimageがプッシュされてれば完了です
参考文献
- 投稿日:2020-08-23T22:28:55+09:00
docker環境で遅いbundle installを劇的に早くする
目的
newsdict.io(docker環境上のrails)でbundle installの時間を早めたい
方法
- あまりバージョン変更がない最も重たいgemをベースのdocker imageに入れて置きgemを流用できるようにしておく
- nokogiriを使っていたので、[--use-system-libraries]を使う
- bundle install を並列で実行する
実際のコード
- あまりバージョン変更がない最も重たいgemをベースのdocker imageに入れて置きgemを流用できるようにしておく
https://github.com/newsdict/docker_rails/blob/master/Dockerfile#L70
- ベースとして使ったdocker image
https://hub.docker.com/repository/docker/newsdict/rails
- nokogiriを使っていたので、[--use-system-libraries]を使う
https://github.com/yubele/newsdict.io/blob/9747c9a4d0a69f8f251f1deb9d6b776856281a2b/Dockerfile#L25
- bundle install を並列で実行する
https://github.com/yubele/newsdict.io/blob/9747c9a4d0a69f8f251f1deb9d6b776856281a2b/Dockerfile#L24
Q&A
bundle installした結果をdocker imageのすればいいのでは?
Gemfileを頻繁に書き換える可能性があるため
- 投稿日:2020-08-23T20:43:36+09:00
webpackerをdocker, docker-compose上で利用する
Description
railsのwebpackerをdocker-compose, docker環境で動作させる。
Requirements
- gem rails 済
- 最新のyarnインストール済み (最新のyarn install方法)
Step
- webpackerの設定を行う
- Dockerfile (docker) で puma + webpacker どちらも待ち受けるように設定する
- javascript_pack_tagに置き換える
- cssをwebpacker上で動作するようにする
1. webpackerの設定を行う (はじめからrails6系の場合は不要です)
- Install webpacker
$ rails webpacker:install2. Dockerfile (docker) で puma + webpacker どちらも待ち受けるように設定する
rails s と webpackerどちらも起動できるEntrypoint 用のシェルをつくる sample1 sample2
$ mkdir -p src/provisioning/startup/web
$ wget https://raw.githubusercontent.com/newsdict/newsdict.io/master/src/provisioning/startup/web/startup.sh -O src/provisioning/startup/web/startup.sh
$ mkdir -p src/provisioning/startup/
$ wget https://raw.githubusercontent.com/newsdict/newsdict.io/master/src/provisioning/startup/bootstrap.sh -O src/provisioning/startup/bootstrap.sh
Docker越しに待ち受けるためのwebpacker用ポートの設定などを行う
*.envにIPを設定する (ブラウザからwebpackerにアクセスできるようにする) * (sample)
```webpack-dev-server
WEBPACK_DEV_SERVER_PUBLIC=http://a.b.c.d:3035
**Gemfileにdotenvを入れる**
$ echo "gem 'dotenv-rails', require: 'dotenv/rails-now'" >> Gemfile
$ bundle install
**開発環境でのアセットをwebpackerに向ける** ([sample](https://github.com/newsdict/newsdict.io/blob/cf9bddd5b06139912c5ad2422c9fbc69b72c4560/config/environments/development.rb#L62))
config/environments/development.rb:
if ENV['WEBPACK_DEV_SERVER_PUBLIC'].present?
config.action_controller.asset_host = ENV['WEBPACK_DEV_SERVER_PUBLIC']
end
```webpackerの待ち受けるfqdnを設定する (sample)
config/webpack/development.js:
const merge = require('webpack-merge')
module.exports = merge(environment.toWebpackConfig(), {
devServer: {
public: process.env.WEBPACK_DEV_SERVER_PUBLIC
}
})
3. jsをwebpacker用のタグjavascript_pack_tagに置き換える (はじめからrails6系の場合は不要です)
4. cssをwebpacker上で動作するようにする
- 投稿日:2020-08-23T15:24:45+09:00
コマンドひとつでRails6の開発環境構築(Rails + PostgreSQL + Webpack on Docker) - すべての駆け出しさんに使ってほしい -
何ができるか
ターミナルのコマンド一つで、Docker上のRailsローカル開発環境が構築できます
source setup.shなぜやるか
結論から言うと
よくわからないまま必要のないところで消耗するよりも、
既存の効率化された物を利用して本来解決すべき問題に取り組んでほしいからこれからRailsでwebアプリ開発を学ぶ多くの人に使っていただきたい
より良いものするためのご意見、ご指摘もお待ちしております新しい学習方法の開拓のために
"teratailで2日間ひたすら質問に応える試み"を経て得た気づきをもとにしています
- Railsチュートリアルの影響か、Rails6でポートフォリオ作成に取り組み、Webpackの役割を理解せず、以前のSprocketを利用するケースとの混同が多く見られる。
- Rails6環境にてWebpack管理下にBootstrapを正しくインストールできていないケースが多い。
- Dockerを学ぶ過程でツギハギのdocker-compose.ymlを作成することに注力しているケースがある(私もそうでした)が、その行為はおそらく、初学者にとって効率が悪く、容易に環境構築が行える、開発環境の共有、統一が行えるというDockerのメリットをスポイルしているように見える。
- 自分が開発したい環境に合ったイメージを、自分のローカル環境にインポートし、
docker-composeを経由して操作が行える、デバッグが行えるといったスキルのほうが優先するべきだと感じる。以上から、次のような目的で動いております
- とにかく簡単にDocker上のローカル開発環境を手に入れられる
- Rails 6 特有の躓きポイントを極力意識させない
- 初学者の個人開発アプリで導入することの多いBootstrapをスムーズに導入できるベースを作る(需要がありそうならBootstrap導入すらも自動化)
個人的な動機としては最近学んだLinuxの知識を活用したいという点もあります
環境
目標環境(Docker上)
- Ruby 2.7.1
- Rails 6.0.3
- PostgreSQL 11.0
- Webpack (Hot-reload対応, JS, CSSコンパイル対応化)
動作確認環境
- Mac OS Mojave 10.14.6
- Docker 2.3.0.4
手順
前提
Dockerがインストールされている
git clone
以下のレポジトリに必要ファイルを用意しました
https://github.com/naokit-dev/Rails6_PG_WP_on_docker.gitgit cloneで必要なファイルを展開します
$ git clone https://github.com/naokit-dev/Rails6_PG_WP_on_docker.gitSetup
setup.shのあるディレクトリに移動し、ターミナルで
$ source setup.sh以上です、あとは待つだけ
実行している
setup.shの内容は以下になります#!/bin/bash # echo "docker-compose run app rails new . --force --no-deps --database=postgresql --skip-bundle" docker-compose run app rails new . --force --no-deps --database=postgresql --skip-bundle # 一旦ここでbuildしておかないと次のステップに進めない echo "docker-compose build" docker-compose build # webpackerをinstall echo "docker-compose run app rails webpacker:install" docker-compose run app rails webpacker:install # Yarnの設定(check_yarn_integrity: true => false) エラーを回避 echo "set check_yarn_integrity: false" sed -icp 's/check_yarn_integrity: true/check_yarn_integrity: false/g' config/webpacker.yml # docker-compose.ymlの内容に合わせてdatabase.ymlを修正 echo "copy config files" mv temp_files/copy_database.yml config/database.yml # rake db:create echo "docker-compose run app rake db:create" docker-compose run app rake db:create # webpackでCSSをコンパイルする仕様に echo "create CSS for Webpack" mkdir app/javascript/stylesheets touch app/javascript/stylesheets/application.scss mv temp_files/copy_application.html.erb app/views/layouts/application.html.erb echo 'import "../stylesheets/application.scss";' >> app/javascript/packs/application.js # 一時ファイルの削除 echo "clean temp filse" rm -r temp_files rm config/webpacker.ymlcp問題なければ
docker-compose upでコンテナを立ち上げた後
ブラウザでlocalhost:3000にアクセスするとYay! You’re on Rails!
CSSコンパイルの仕様
以下の2つの方法が併用できます
よくわからないということであれば①の方法をとってください
以後解説を加える場合はそれを前提にします① Webpackでコンパイル
app/javascript/stylesheets配下にapplication.scssが作成されています
app/views/layouts/application.html.erbの<head>内に
<%= stylesheet_pack_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>が記述されていますのでここにコンパイルされたCSSが出力されます別のstyle sheetを作成して使用する場合は
app/javascript/stylesheets/custom.scssというように作成し
app/javascript/packs/application.jsに
import "../stylesheets/custom.scss";を追記します② Sprocketでアセットコンパイル
Rails6よりも前のバージョン同様
app/assets/stylesheets配下のCSSに記述しますJavaScriptについてはRails6ではデフォルトでWebpackでコンパイルする仕様です
結果①の場合、画像のみassets/imagesに格納し、Sprocketでコンパイルする仕様となっています動作確認(必要に応じて実施してください)
適当に
scaffoldしますdocker-compose exec app rails g scaffold Userdb:migrate
docker-compose exec app rails db:migrateブラウザで
localhost:3000/usersにアクセスし適切なビューが表示させることを確認
app/javascript/stylesheets/application.scssに以下を追記しsaveするbody { background-color: red; }するとwebpack-dev-serverが自動で変更を検出しコンパイルします
そしてブラウザも自動でリロードしてくれるのでブラウザ上に上記変更が反映されるはずです便利!!
Bootstrapのインストール
以下の記事の手順でインストールします
DockerでRailsチュートリアルのローカル開発環境構築 - WebpackでBootstrapとFont Awesomeを導入 - - Qiita需要があれば自動化してみます
既知の問題
webpack-dev-serverのインストールに失敗することがある
イマイチ再現性なくて検証中です
- 投稿日:2020-08-23T15:02:29+09:00
AWS FireLens 用 fluentd コンテナの開発時のみ <source> を追加する
問題
FireLens でユーザー独自の fluentd を使う場合、設定ファイルに以下のような <source> セクションが自動で追加される。
fluent.conf<source> @type unix path /var/run/fluent.sock </source> <source> @type forward bind 127.0.0.1 port 24224 </source>設定ファイルに上記のような記述が既にされていた場合、重複が生じてしまい以下のようなエラーが発生してしまう。
[error]: unexpected error error_class=Errno::EADDRINUSE error=#<Errno::EADDRINUSE: Address in use - bind(2) for "0.0.0.0" port 24224>fluentd コンテナをローカル環境などで開発する段階においては、何らかの <source> がないと入力が得られずテストができないため、ECS で動かすタイミングで上記エラーに遭遇することがある。
対応策
- 開発時だけ設定ファイルに <source> を入れて、開発が終わったら消す
- 愚直なパターン。
- Dockerfile の ARG などを活用して、開発ビルド時のみ <source> を入れるようにする
- 開発以外のときは <source> が入らないように RUN をうまいこと書く。
@type fileなど重複しない type で代用する
- ファイルなど別の type 経由でデバッグする方法。
- 別のコンテナから起動して、コンテナ上で設定ファイルに <source> を追加する
- 後述。
1 に関しては手間がかかってしまうほか、設定の消し忘れで FireLens のエラーを誘発してしまう恐れが常につきまとう。
2./3. は、開発用のロジックがプロダクション環境にも含まれてしまう問題がある。これをどう捉えるかは人次第だが、個人的には若干の気持ち悪さが残る。主題: 別のコンテナから起動させる
4 の方法について: 基本的には 1. と似たアプローチだが、これをコンテナという仮想環境上で行う点が違いとなる。
DockerfileFROM <開発中のコンテナ> # localhost を listen する設定を追加 RUN sed -e '1i<source>' \ -e '1i @type forward' \ -e '1i port 24224' \ -e '1i bind 0.0.0.0' \ -e '1i</source>' \ -i <設定ファイルへのパス>上記をビルドして実行すれば、fluentd 本体の設定ファイルには手を加えることなく、ローカル開発時のみ <source> を追加することができる。これにより fluent-cat で自由にデバッグ入力が可能となる。
また、上記例では forward type を指定したが、sample (dummy) type を指定することで fluent-cat が不要になる。
https://docs.fluentd.org/input/sampleリポジトリにしたのでよければどうぞ。
https://github.com/tsubasaogawa/aws-firelens-fluentd-dev-kitむすび
本手法の欠点として、FROM で fluentd コンテナを指定している都合上、 fluentd コンテナにビルドが生じるたびに本コンテナのビルドも必要になる。頻繁に fluentd コンテナをビルドするような開発スタイルだと、多少の手間がどうしても発生してしまう。
願わくば FireLens 側でエラーの発生を避ける方法があればこんなことをせずに済むのだけど、なにかご存知な方教えてください。
- 投稿日:2020-08-23T11:33:37+09:00
docker loginの認証はどこで実行されるか
TL; DR
答えは、
dockerコマンドを実行するOS/ユーザ です。より詳細な仕組みを知りたい方は以下を読んでください。
dockerコマンドは3種類ある
はじめに
dockerコマンドは一つでないという話からはじめます。
ホストOSがLinux以外の場合、
dockerデーモンは仮想OS (macOSならHyperKit, WindowsならHyper-VまたはWSL2)上のLinuxで実行されています。dockerコマンドは以下の三ヶ所から呼び出すことができます
- ホストOS
- 仮想OS
- コンテナ内部(ソケットマウント方式でdocker in dockerしている場合)
それぞれで実行されるdockerコマンドは実行されるOS上の普通のプログラムで、
dockerデーモンに対して通信をすることで実際のコンテナを実行します。つまり、ホストOS上の
dockerコマンドと仮想OS上のdockerコマンドは
全く別のバイナリということになります。(当たり前ですが)docker login
docker loginはDocker HubやGCR, ECRなどのコンテナレジストリにログインするコマンドです。
ログインすることで、コンテナのpushやprivate レポジトリからのpullができるようになります。さてログイン処理はどのように実行されるのでしょうか。筆者が調べた限りでは以下のような仕様のようです。
- (dockerデーモンではなく)dockerコマンドを実行したOSで実行される
- 認証情報(の設定)は
$HOME/.docker/config.jsonに保存される。したがって以下のことに気をつける必要があります。
ホストOS上でログイン状態は、root以外の別のユーザあるいは仮想OS上あるいはコンテナ内のdockerからは利用できません。
- ホストOS上で
docker loginしておいても、 仮想OS上のdockerからその認証情報を用いたpush/pullはできません。- ホストOS上の別ユーザからもpush/pullはできません。(例外として
sudoによるpush/pullは可能です。これはsudoでは環境変数(特に$HOME)がユーザのものが引き継がれ、かつrootは~/.docker/config.jsonが読み出し可能だからです。)
- OK:
sudo docker pull ${private_image}- NG:
echo docker pull ${private_image} | sudo su root
docker loginコマンドをsudoで実行すると$HOME/.docker/config.jsonの所有者がrootとなります。
(ドキュメントにはsudo docker loginした場合は/root/.docker/config.jsonに保存されると書いてありますが、少なくともmacOS上では$HOME/.docker/config.jsonに保存されることを確認しました。)credential helper
docker loginの認証状態をいい感じに管理してくれるのがcredential helperと呼ばれるプログラムです。
これを使うと、docker loginのパスワードをOSのキーチェーンツールに保存したり、
AWSやGCPの認証コマンドを用いる事ができます。
~/.docker/config.jsonに設定を書くと利用できます。以下の例ではデフォルトのhelperとして
desktopを使い、gcr.ioなど特定のサイトではgloudを使うという設定です。{ "credsStore": "desktop", "credHelpers": { "asia.gcr.io": "gcloud", "eu.gcr.io": "gcloud", "gcr.io": "gcloud", "marketplace.gcr.io": "gcloud", "staging-k8s.gcr.io": "gcloud", "us.gcr.io": "gcloud" } }credential helperは
docker-credential-のprefixがついた普通のプログラムです。$ docker-credential- docker-credential-desktop docker-credential-gcloud docker-credential-osxkeychain仕組みも非常に簡単で、引数に
get、標準入力にログインしたいレポジトリのURLを渡すと認証情報を返してくれます。$ echo https://gcr.io | docker-credential-gcloud get { "Secret": "{ここは認証トークンなのでマスクしました}", "Username": "_dcgcloud_token" }したがって、こちらの設定・状態もdockerコマンドを実行するOSやユーザごとに独立しています。
docker in dockerで認証情報を共有したい場合
上記の理由から、dockerの認証情報は実行OS,ユーザごとに独立であるという事がわかりました。
したがって、docker in dockerを実現したい場合に、ホストLinuxの認証情報を共有した場合は
以下のようにする必要があります。
- コンテナ実行時に
$HOME環境変数を適切に設定します。- credential helperコンテナ内部のPATH上にマウントします
-v $(which docker-credential-gloud:/usr/local/bin/docker-credential-gcloud)- ホストOSの
$HOME/.docker/config.jsonをコンテナ内にマウントします。- credential helperが使う認証情報をコンテナ内部からアクセスできるようにします。(環境変数で渡すか鍵ファイルをマウントします)
- 投稿日:2020-08-23T08:41:23+09:00
Docker+nginxでHTTPSサーバを簡単に構築
DockerのVolumeをホスト側に持って証明書と設定ファイルとHTMLを置いて読ませる。
他の環境とセットで動かしたかったのでDocker-composeにしているが、もちろんDocker単体でも良い。Let's encryptで証明書を取得し以下に格納しておく。
手順は他に参考記事がたくさんあるのでここでは触れず。
ホスト側の置き場所はDockerからアクセスできる場所ならどこでも良い。./nginx/conf/certsdefault.confを作成し./conf.dに置く。
HTTP 80番は使わないので書かず。server { listen 443 ssl; server_name your.hostname; # 自分で取得したホスト名に変更 ssl_certificate /etc/nginx/certs/server.crt; ssl_certificate_key /etc/nginx/certs/server.key; location / { root /usr/share/nginx/html; } }これで完了。スタートさせる。
(必要なら)
htmlを用意してホストに置いておく
./htmldocker-composeをこのようにかく。
docker-compose.ymlnginx: restart: always image: nginx volumes: - ./html:/usr/share/nginx/html:ro - ./conf.d:/etc/nginx/conf.d - ./nginx/conf/certs:/etc/nginx/certs ports: - "8080:443" # デフォルト443のままで良いなら不要
- 投稿日:2020-08-23T00:35:39+09:00
Rails開発環境Template
プロジェクトフォルダの作成
build context内に以下のファイルを作成
Dockerfile Gemfile Gemfile.lock docker-compose.ymlDockerfileを編集
DockerfileFROM ruby:2.5 RUN apt-get update && apt-get install -y \ build-essential \ libpq-dev \ node.js \ yarn # 作業ディレクトリに移動(無ければ自動で作成します) WORKDIR /app #build context内のGemfileとGemfile.lockをコピー COPY Gemfile Gemfile.lock /app/ #Gemをインストール RUN bundle installGemfileを編集
Gemfilesource 'https://rubygems.org' gem 'rails', '~>5.2'docker-compose.ymlを編集
docker-compose.ymlversion: '3' services: web: build: . command: bundle exec rails s -p 3000 -b '0.0.0.0' volumes: - .:/app ports: - 3000:3000 depends_on: - db tty: true stdin_open: true db: image: mysql:5.7 volumes: - db-volume:/var/lib/mysql environment: MYSQL_ROOT_PASSWORD: password volumes: db-volume:コンテナを起動する
$docker-compose -up -dコンテナの中に入ってセットアップ
$docker-compose exec web bash








