20200823のdockerに関する記事は9件です。

【面倒臭がりさん必見】たった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. 好きな名前でフォルダを作成する
  2. 1で作成したフォルダの中に『docker_rails_react.sh』という空ファイルを作成する
  3. 2で作成したファイルに下記『シェルスクリプト本体』を全文コピーして保存する
  4. ターミナルを起動
  5. 1で作成したフォルダに移動し、コマンド『bash docker_rails_react.sh』を実行する
  6. Netflixを見て時間を潰す(数分〜数十分間)
  7. 『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生活を!

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

GithubActionでmasterプッシュ時にECRにimageをpushする

aws ECRにリポジトリを作る

  1. awsコンソールにログイン
  2. 検索窓から「ecr」と検索すると「Amazon Container Services」というのが出てくるのでクリック
  3. リポジトリを作成 スクリーンショット 2020-08-23 21.35.52.png
  4. 今回は「test」という名前でリポジトリを作成します(名前は任意の名前で大丈夫です!) スクリーンショット 2020-08-23 21.37.02.png

Github Actionsを動かすアクセスキー、シークレットキーを取得

  1. awsコンソールにログイン
  2. 検索窓から「iam」と検索
  3. ユーザー追加をクリック スクリーンショット 2020-08-23 21.27.13.png 4.名前に「github-actions」(名前は任意の名前で大丈夫です!)アクセスの種類は「プログラムによるアクセス」にチェックして次へ スクリーンショット 2020-08-23 21.27.54.png 5.「既存のポリシーを直接アタッチ」を選択→「AmazonEC2ContainerRegistryPowerUser」と検索すると権限が出てくるのでこちらをチェック スクリーンショット 2020-08-23 21.29.06.png
  4. あとは全て次へで大丈夫です。そしたら以下のような画面になるのでアクセスキーとシークレットキーをメモしておく スクリーンショット 2020-08-23 21.29.48.png

Githubにシークレットキーを登録

  1. githubの該当のリポジトリにいき下記3つの変数を追加する
  • AWS_ACCESS_KEY_ID(先ほど取得したアクセスキー)
  • AWS_SECRET_ACCESS_KEY(先ほど取得したシークレットキー)
  • AWS_ECR_REPO_NAME(ECRで作成したリポジトリ名*今回の場合はtestスクリーンショット 2020-08-23 21.40.33.png 上記のように変数を指定することで${{ secrets.キー名 }}とすることでその環境変数を使用できる

github actionsのymlを作成

.github/workflows/config.yml
name: 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-23 23.10.24.png

参考文献

GitHub ActionでDockerコンテナをビルドしてAmazon ECRに保存する

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

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を頻繁に書き換える可能性があるため

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

webpackerをdocker, docker-compose上で利用する

この記事は、https://newsdict.blogから移設しました。

Description

  railsのwebpackerをdocker-compose, docker環境で動作させる。

Requirements

Step

  1. webpackerの設定を行う
  2. Dockerfile (docker) で puma + webpacker どちらも待ち受けるように設定する
  3. javascript_pack_tagに置き換える
  4. cssをwebpacker上で動作するようにする

1. webpackerの設定を行う (はじめからrails6系の場合は不要です)

  1. Install webpacker $ rails webpacker:install

2. Dockerfile (docker) で puma + webpacker どちらも待ち受けるように設定する

  1. 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

  2. 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系の場合は不要です)

sample

4. cssをwebpacker上で動作するようにする

  1. cssをapp/javascript/cssに移動する (sample)

  2. cssをwebpackerからimportする (sample)

  3. CSSの読み込み方法をwebpacker用のタグstylesheet_pack_tagに書き換える (sample)
    [itemlink post_id="49"]

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

コマンドひとつで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.git

git cloneで必要なファイルを展開します

$ git clone https://github.com/naokit-dev/Rails6_PG_WP_on_docker.git

Setup

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 User

db: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のインストールに失敗することがある

イマイチ再現性なくて検証中です

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

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 で動かすタイミングで上記エラーに遭遇することがある。

対応策

  1. 開発時だけ設定ファイルに <source> を入れて、開発が終わったら消す
    • 愚直なパターン。
  2. Dockerfile の ARG などを活用して、開発ビルド時のみ <source> を入れるようにする
    • 開発以外のときは <source> が入らないように RUN をうまいこと書く。
  3. @type file など重複しない type で代用する
    • ファイルなど別の type 経由でデバッグする方法。
  4. 別のコンテナから起動して、コンテナ上で設定ファイルに <source> を追加する
    • 後述。

1 に関しては手間がかかってしまうほか、設定の消し忘れで FireLens のエラーを誘発してしまう恐れが常につきまとう。
2./3. は、開発用のロジックがプロダクション環境にも含まれてしまう問題がある。これをどう捉えるかは人次第だが、個人的には若干の気持ち悪さが残る。

主題: 別のコンテナから起動させる

4 の方法について: 基本的には 1. と似たアプローチだが、これをコンテナという仮想環境上で行う点が違いとなる。

Dockerfile
FROM <開発中のコンテナ>

# 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 側でエラーの発生を避ける方法があればこんなことをせずに済むのだけど、なにかご存知な方教えてください。

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

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 (4).png

それぞれで実行される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 に保存される。

したがって以下のことに気をつける必要があります。

  1. ホスト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
  2. 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の認証情報を共有した場合は
以下のようにする必要があります。

  1. コンテナ実行時に$HOME環境変数を適切に設定します。
  2. credential helperコンテナ内部のPATH上にマウントします
    -v $(which docker-credential-gloud:/usr/local/bin/docker-credential-gcloud)
  3. ホストOSの$HOME/.docker/config.jsonをコンテナ内にマウントします。
  4. credential helperが使う認証情報をコンテナ内部からアクセスできるようにします。(環境変数で渡すか鍵ファイルをマウントします)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Docker+nginxでHTTPSサーバを簡単に構築

DockerのVolumeをホスト側に持って証明書と設定ファイルとHTMLを置いて読ませる。
他の環境とセットで動かしたかったのでDocker-composeにしているが、もちろんDocker単体でも良い。

Let's encryptで証明書を取得し以下に格納しておく。
手順は他に参考記事がたくさんあるのでここでは触れず。
ホスト側の置き場所はDockerからアクセスできる場所ならどこでも良い。

./nginx/conf/certs

default.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を用意してホストに置いておく
./html

docker-composeをこのようにかく。

docker-compose.yml
nginx:
  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のままで良いなら不要
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Rails開発環境Template

プロジェクトフォルダの作成

build context内に以下のファイルを作成

Dockerfile
Gemfile
Gemfile.lock
docker-compose.yml

Dockerfileを編集

Dockerfile
FROM 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 install

Gemfileを編集

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

docker-compose.ymlを編集

docker-compose.yml
version: '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
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む