Upgrade to Pro — share decks privately, control downloads, hide ads and more …

クジラに乗ったRuby: Dockerを使って、便利な開発環境の構築

クジラに乗ったRuby: Dockerを使って、便利な開発環境の構築

イービルマーシャンズ: https://evilmartians.jp/

出雲ルビーミートアップ: https://izumonomadrubymeetup.peatix.com/

Andrey Novikov

November 11, 2023
Tweet

More Decks by Andrey Novikov

Other Decks in Programming

Transcript

  1. アンドレイといいます バックエンドエンジニア フロントエンドもできます Ruby, Go, TypeScriptを使っています SQL, Dockerfiles, TypeScript, bash…

    オープンソースの大ファン ルビージェムをいくつかを作成して、維持しています 1年前ロシアから日本に引っ越しました 大阪の近くに日本のくらしを楽しんでいます 原付を乗っています。スーパーカブです。 ママチャリも乗って、子供を幼稚園に連れていきます。
  2. 火星で作ったオープンソース Yabeda: Ruby application instrumentation framework Lefthook: git hooks manager

    AnyCable: a real-time server for Rails and beyound PostCSS: A tool for transforming CSS with JavaScript Imgproxy: Fast and secure standalone server for resizing and converting remote images A Figma plugin that ensures UI text is readable by leveraging the new APCA algorithm Overmind: Process manager for Procfile-based applications and tmux 詳しくは evilmartians.com/oss
  3. 開発環境って何? 1. オペレーティングシステム 2. 依存関係ライブラリー (OpenSSLなど ) 3. ルビー 4.

    ルビージェム 5. データベース (PostgreSQL/MySQL, Redis) 6. ソースコードエディタ /IDE 7. git 8. …
  4. 開発環境って何? 1. オペレーティングシステム 2. 依存関係ライブラリー (OpenSSLなど ) 3. ルビー 4.

    ルビージェム 5. データベース (PostgreSQL/MySQL, Redis) 6. ソースコードエディタ /IDE 7. git 8. …
  5. 開発環境の種類 1. ローカル 便利ですが、設定は面倒になるかも、 OSによっては問題になるかも 2. 仮想マシン (Vagrantなど ) 重たいです。

    RAMをたくさん使います。 3. コンテナ (Dockerなど ) 軽いですが、便利な設定は簡単とは言えないです。 今日の話題です。 4. リモート 高速なインターネットが必要です。遅いと DXが悪くなります。 5. 未来のこと (WebAssembly?) Stackblitzのようなプロジェクトは WebAssemblyでブラウザーの中で全てを実現していますが、 Rubyはまだ使えないみたいんです。
  6. ローカル開発環境設定のよくある仕方 1. 手順が記載された READMEファイル 長所 : 少なくとも文書があります 短所 : 長くて面倒くさい、見逃しやすい、古いことが多い

    , 一つの OSだけに対応しています 2. Bash scripts / Makefiles 長所 : 一度に実行できます 短所 : 普段一つの OSだけに対応しています、古いことが多い 3. Docker 長所 : 一度に実行できます、 OSに依存しません、 OSを汚染しません 短所 : 遅くて、不便、毎日使わないと古くなります
  7. 火星流の Dockerの設定の一覧 1. Dockerのイメージは Rubyとライブラリだけを含めています 2. ジェムは別のボリュームにインストールされています 3. データベースも別のボリュームです 4.

    いろいろな性能の設定 5. dip: 便利なコマンドラインツール 6. dip: シェルとの統合 7. Railsテンプレートでマイグレーションスクリプト
  8. 原本の記事 : Ruby on Whales: Dockerizing Ruby and Rails development

    日本語翻訳 : クジラに乗った Ruby: Evil Martians流 Docker+Ruby/Rails開発環境構築 原本 日本語翻訳
  9. Dockerの環境の問題解決 : Dockerfile ` ` # syntax=docker/dockerfile:1 ARG RUBY_VERSION ARG

    DISTRO_NAME=bullseye FROM ruby:$RUBY_VERSION-slim-$DISTRO_NAME ARG DISTRO_NAME # Common dependencies RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \ --mount=type=cache,target=/var/lib/apt,sharing=locked \ --mount=type=tmpfs,target=/var/log \ rm -f /etc/apt/apt.conf.d/docker-clean; \ echo 'Binary::apt::APT::Keep-Downloaded-Packages "true";' > /etc/apt/apt.conf.d/keep-cache; \ apt-get update -qq \ && DEBIAN_FRONTEND=noninteractive apt-get install -yq --no-install-recommends \ build-essential \ gnupg2 \ curl \ less \ git # Install PostgreSQL dependencies ARG PG_MAJOR RUN curl -sSL https://www.postgresql.org/media/keys/ACCC4CF8.asc | \ gpg --dearmor -o /usr/share/keyrings/postgres-archive-keyring.gpg \ && echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/postgres-archive-keyring.g $DISTRO_NAME-pgdg main $PG_MAJOR | tee /etc/apt/sources.list.d/postgres.list > /dev/null RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \ --mount=type=cache,target=/var/lib/apt,sharing=locked \ --mount=type=tmpfs,target=/var/log \ apt-get update -qq && DEBIAN_FRONTEND=noninteractive apt-get -yq dist-upgrade && \ DEBIAN_FRONTEND=noninteractive apt-get install -yq --no-install-recommends \ libpq-dev \ postgresql-client-$PG_MAJOR # Install NodeJS and Yarn ARG NODE_MAJOR ARG YARN_VERSION=latest RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \ --mount=type=cache,target=/var/lib/apt,sharing=locked \ --mount=type=tmpfs,target=/var/log \ apt-get update && \ apt-get install -y curl software-properties-common && \ curl -fsSL https://deb.nodesource.com/gpgkey/nodesource.gpg.key | apt-key add - && \ echo "deb https://deb.nodesource.com/node_${NODE_MAJOR}.x $(lsb_release -cs) main" | tee /etc/apt/source apt-get update && \ DEBIAN_FRONTEND=noninteractive apt-get install -yq --no-install-recommends nodejs RUN npm install -g yarn@$YARN_VERSION # Application dependencies # We use an external Aptfile for this, stay tuned COPY Aptfile /tmp/Aptfile RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \ --mount=type=cache,target=/var/lib/apt,sharing=locked \ --mount=type=tmpfs,target=/var/log \ apt-get update -qq && DEBIAN_FRONTEND=noninteractive apt-get -yq dist-upgrade && \ DEBIAN_FRONTEND=noninteractive apt-get install -yq --no-install-recommends \ $(grep -Ev '^\s*#' /tmp/Aptfile | xargs) # Configure bundler ENV LANG=C.UTF-8 \ BUNDLE_JOBS=4 \ BUNDLE_RETRY=3 # Store Bundler settings in the project's root ENV BUNDLE_APP_CONFIG=.bundle # Uncomment this line if you want to run binstubs without prefixing with `bin/` or `bundle exec` # ENV PATH /app/bin:$PATH # Upgrade RubyGems and install the latest Bundler version RUN gem update --system && \ gem install bundler # Create a directory for the app code RUN mkdir -p /app WORKDIR /app # Document that we're going to expose port 3000 EXPOSE 3000 # Use Bash as the default command CMD ["/bin/bash"] Ruby on Whales: Dockerfile
  10. Dockerのビルド高速化 RUN --mount のドキュメントを参照してください。 Dockerのレイヤーキャッシュをよく効くために、 RUN --mount を使っています。 ` `

    # Application dependencies # We use an external Aptfile for this, stay tuned COPY Aptfile /tmp/Aptfile RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \ --mount=type=cache,target=/var/lib/apt,sharing=locked \ --mount=type=tmpfs,target=/var/log \ apt-get update -qq && DEBIAN_FRONTEND=noninteractive apt-get -yq dist- DEBIAN_FRONTEND=noninteractive apt-get install -yq --no-install-recomm $(grep -Ev '^\s*#' /tmp/Aptfile | xargs) ` ` Dockerfile RUN --mount docs
  11. docker-compose.yml ` ` x-app: &app build: context: . args: RUBY_VERSION:

    '3.2.2' PG_MAJOR: '15' NODE_MAJOR: '18' image: example-dev:1.0.0 environment: &env NODE_ENV: ${NODE_ENV:-development} RAILS_ENV: ${RAILS_ENV:-development} tmpfs: - /tmp - /app/tmp/pids x-backend: &backend <<: *app stdin_open: true tty: true volumes: - ..:/app:cached - bundle:/usr/local/bundle - rails_cache:/app/tmp/cache - node_modules:/app/node_modules - packs:/app/public/packs - packs-test:/app/public/packs-test - history:/usr/local/hist - ./.psqlrc:/root/.psqlrc:ro - ./.bashrc:/root/.bashrc:ro environment: &backend_environment <<: *env REDIS_URL: redis://redis:6379/ DATABASE_URL: postgres://postgres:postgres@postgres:5432 WEBPACKER_DEV_SERVER_HOST: webpacker MALLOC_ARENA_MAX: 2 WEB_CONCURRENCY: ${WEB_CONCURRENCY:-1} BOOTSNAP_CACHE_DIR: /usr/local/bundle/_bootsnap XDG_DATA_HOME: /app/tmp/cache YARN_CACHE_FOLDER: /app/node_modules/.yarn-cache HISTFILE: /usr/local/hist/.bash_history PSQL_HISTFILE: /usr/local/hist/.psql_history IRB_HISTFILE: /usr/local/hist/.irb_history EDITOR: vi depends_on: &backend_depends_on postgres: condition: service_healthy redis: condition: service_healthy services: rails: <<: *backend command: bundle exec rails web: <<: *backend command: bundle exec rails server -b 0.0.0.0 ports: - '3000:3000' depends_on: webpacker: condition: service_started sidekiq: condition: service_started sidekiq: <<: *backend command: bundle exec sidekiq postgres: image: postgres:14 volumes: - .psqlrc:/root/.psqlrc:ro - postgres:/var/lib/postgresql/data - history:/usr/local/hist environment: PSQL_HISTFILE: /usr/local/hist/.psql_history POSTGRES_PASSWORD: postgres ports: - 5432 healthcheck: test: pg_isready -U postgres -h 127.0.0.1 interval: 5s redis: image: redis:6.2-alpine volumes: - redis:/data ports: - 6379 healthcheck: test: redis-cli ping interval: 1s timeout: 3s retries: 30 webpacker: <<: *app command: bundle exec ./bin/webpack-dev-server ports: - '3035:3035' volumes: - ..:/app:cached - bundle:/usr/local/bundle - node_modules:/app/node_modules - packs:/app/public/packs - packs-test:/app/public/packs-test environment: <<: *env WEBPACKER_DEV_SERVER_HOST: 0.0.0.0 YARN_CACHE_FOLDER: /app/node_modules/.yarn-cache volumes: bundle: node_modules: history: rails_cache: postgres: redis: packs: packs-test: Ruby on Whales: docker-compose.yml
  12. Dockerの環境の問題解決 : ボリューム Rubyのジェム NPMのパッケージ データベースのファイル キャッシュなど さらに、コンテナ内の /tmp とアプリの

    tmp/pids に tmpfsを利用されています。 volumes: # … - bundle:/usr/local/bundle - rails_cache:/app/tmp/cache - node_modules:/app/node_modules - packs:/app/public/packs - packs-test:/app/public/packs-test # … ` ` ` `
  13. Dockerの環境の問題解決 : サービス Docker Composeの設定でいろいろなサービスが定義されています。 rails server と rails console

    には異なるサービスが使われています。例えば、サーバーの起動用には、 公開するポート番号を定義します。 サービスは dipの設定では使われています。 DRYの為、拡張のサービス定義も使われています。 x-app と x-backend ヘルスチェック rails db:migrate は依存するデータベースサービスの準備が整うまで待機するようにします。 ` ` ` ` ` ` ` ` ` `
  14. Dockerの環境の問題解決 : 拡張のサービス定義 x-app: &app build: context: . args: #

    Ruby, Node, PostgreSQL versions image: example-dev:1.0.0 environment: &env NODE_ENV: ${NODE_ENV:-development} RAILS_ENV: ${RAILS_ENV:-development} tmpfs: - /tmp - /app/tmp/pids x-backend: &backend <<: *app stdin_open: true tty: true volumes: # see previous slides environment: &backend_environment <<: *env REDIS_URL: redis://redis:6379/ services: rails: <<: *backend command: bundle exec rails web: <<: *backend command: bundle exec rails server -b 0 ports: - '3000:3000' depends_on: webpacker: condition: service_started webpacker: <<: *app command: bundle exec ./bin/webpack-dev- ports: - '3035:3035' environment: <<: *env
  15. Dockerの環境の問題解決 : 設定 # Application configuration to save memory MALLOC_ARENA_MAX:

    2 WEB_CONCURRENCY: ${WEB_CONCURRENCY:-1} # Store caches in Docker volumes BOOTSNAP_CACHE_DIR: /usr/local/bundle/_bootsnap XDG_DATA_HOME: /app/tmp/cache YARN_CACHE_FOLDER: /app/node_modules/.yarn-cache # Convenience devtools HISTFILE: /usr/local/hist/.bash_history PSQL_HISTFILE: /usr/local/hist/.psql_history IRB_HISTFILE: /usr/local/hist/.irb_history
  16. Dip: 不足している DXを追加し直す dip は Docker Composeに基づいたコマンドラインツールです。 短くて、便利なコマンドを追加できます dip provision

    というコマンドで、アプリの環境を再構築できます。 シェルとの統合 ` ` dip rails c # 😎 # instead of docker-compose run --rm app bundle exec rails c # 😫 ` ` eval "$(dip console)" rails c # Dockerの中で起動します 😎 github.com/bibendi/dip
  17. dip.yml ` ` version: '7.1' # Define default environment variables

    to pass # to Docker Compose environment: RAILS_ENV: development compose: files: - .dockerdev/compose.yml project_name: example_demo interaction: # This command spins up a Rails container with the required dependencies (such as da # and opens a terminal within it. runner: description: Open a Bash shell within a Rails container (with dependencies up) service: rails command: /bin/bash # Run a Rails container without any dependent services (useful for non-Rails scripts bash: description: Run an arbitrary script within a container (or open a shell without d service: rails command: /bin/bash compose_run_options: [ no-deps ] # A shortcut to run Bundler commands bundle: description: Run Bundler commands service: rails command: bundle compose_run_options: [ no-deps ] # A shortcut to run RSpec (which overrides the RAILS_ENV) rspec: description: Run RSpec commands service: rails environment: RAILS_ENV: test command: bundle exec rspec rails: description: Run Rails commands service: rails command: bundle exec rails subcommands: s: description: Run Rails server at http://localhost:3000 service: web compose: run_options: [service-ports, use-aliases] yarn: description: Run Yarn commands service: rails command: yarn compose_run_options: [ no-deps ] psql: description: Run Postgres psql console service: postgres default_args: anycasts_dev command: psql -h postgres -U postgres 'redis-cli': description: Run Redis console service: redis command: redis-cli -h redis provision: - dip compose down --volumes - dip compose up -d postgres redis - dip bash -c bin/setup Ruby on Whales: DIP config
  18. DIPの設定:コマンド interaction: bundle: service: rails command: bundle compose_run_options: [ no-deps

    ] # Don't launch database for `bundle install` rspec: service: rails environment: RAILS_ENV: test # overrides the RAILS_ENV command: bundle exec rspec rails: service: rails command: bundle exec rails subcommands: s: service: web compose: run_options: [service-ports, use-aliases]
  19. 試してみよう! OS Docker と Docker Compose Ruby (サポートされているバージョン ) dip

    ジェム 必要な既にインストールされているソフトウェア bundle exec rails app:template \ LOCATION='https://railsbytes.com/script/z5OsoB' ruby-on-whales repo
  20. 原本の記事 : Ruby on Whales: Dockerizing Ruby and Rails development

    日本語翻訳 : クジラに乗った Ruby: Evil Martians流 Docker+Ruby/Rails開発環境構築 原本 日本語翻訳
  21. ありがとうございました! @Envek @Envek @Envek @Envek github.com/Envek @evilmartians @evilmartians @evilmartians_jp (日本語

    ) @evil.martians evilmartians.jp 我々のヤバいブログ : evilmartians.com/chronicles! @hachi8833さんが作ってくれた日本語の翻訳があります! このスライドは次のアドレスでご覧できます : envek.github.io/izumorb-kujira-ni-notta-ruby