- 投稿日:2020-03-10T23:05:10+09:00
Win10 HomeのWSL2+DockerでGUIのROS1/ROS2をセットアップする方法
@karaage0703さんが「Docker上でGUIのROS1/ROS2を一瞬でセットアップする方法」にて紹介してくださったDockerイメージ(tiryoh/ros-desktop-vncとtiryoh/ros2-desktop-vnc)がWindows 10 Home上のWSL2で問題なく動いたので紹介します。
使い方(簡略版)
DockerがインストールされたWindows 10で以下のコマンドを実行し、Webブラウザでhttp://127.0.0.1:6080にアクセスすると、Dockerコンテナ内にアクセスできるVNCクライアントが立ち上がります。
docker run -p 6080:80 --shm-size=512m tiryoh/ros2-desktop-vnc:dashing環境
OS
使用したOSはWindows 10 Home Insider Previewのバージョン2004、OSビルドは19564.1000です。
WSL2のインストールについては@aki4000さんの「Windows10 HomeとWSL2でdocker-composeができるようにする」が大変わかりやすかったです。
Docker
使用したDocker Desktop for Windowsのバージョンは2.2.2.0(43066)です。
使い方
DockerがインストールされたWindows 10で以下のコマンドを実行します。
docker run -p 6080:80 --shm-size=512m tiryoh/ros2-desktop-vnc:dashing初回起動時はWindowsのセキュリティ警告が出ました。
アクセスを許可するとDockerコンテナ内でサーバが立ち上がるのを確認できます。
Webブラウザでhttp://127.0.0.1:6080にアクセスすると、VNCクライアント経由でターミナルを立ち上げたり、各種の操作ができます。Gazeboも起動できます。
- 投稿日:2020-03-10T22:14:45+09:00
【RSpec】System Specが動作するRails+Docker開発環境を構築する
はじめに
Rspec3.7以降で実装された"System Spec"を動作できるRails+Docker環境の作り方をまとめました。
基本コピペで進められるように書きましたので、初学者の方であっても「まず動く」状態まで持っていけると思います。
この記事では、Docker、docker-composeがすでにインストールされた状態からスタートします。私は、自PC(Mac)のホストOSに「Docker for Mac」をインストールしています。
著者動作環境における、Docker、docker-composeのバージョンは以下の通りです。
$ docker --version Docker version 19.03.5, build 633a0ea$ docker-compose -v docker-compose version 1.25.4, build 8d51620aDockerでのRails環境の構築
まずは、Docker公式が用意してくれているRals用のクイックスタート「Quickstart: Compose and Rails」をベースに、Railsが動作するDocker環境を作成していきます。
一部、公式のファイル内容に変更を加えていますので、ご注意ください。
早速始めていきましょう。作業ディレクトリ用意し、cdで移動します。
$ mkdir sample_app $ cd sample_appQuickstartに則り、sample_app内に下記5ファイルを作成します。
/sample_app$ touch Dockerfile docker-compose.yml Gemfile Gemfile.lock entrypoint.shディレクトリ構造/sample_app ├── Dockerfile ├── Gemfile ├── Gemfile.lock ├── entrypoint.sh └── docker-compose.yml各ファイルを下記の通り編集します。5ファイルのうち、docker-compose.yml以外は、Quickstart公式のコピペです。vim等、お好きなテキストエディタをお使いください。
DockerfileFROM ruby:2.5 RUN apt-get update -qq && apt-get install -y nodejs postgresql-client RUN mkdir /myapp WORKDIR /myapp COPY Gemfile /myapp/Gemfile COPY Gemfile.lock /myapp/Gemfile.lock RUN bundle install COPY . /myapp # 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"]Gemfilesource 'https://rubygems.org' gem 'rails', '~>5'Gemfile.lock(空のまま)entrypoint.sh#!/bin/bash set -e # Remove a potentially pre-existing server.pid for Rails. rm -f /myapp/tmp/pids/server.pid # Then exec the container's main process (what's set as CMD in the Dockerfile). exec "$@"docker-compose.ymlversion: '3' services: db: image: postgres volumes: - ./tmp/db:/var/lib/postgresql/data environment: - "POSTGRES_USER=xxxx" # 追記 - "POSTGRES_PASSWORD=xxxx" # 追記 web: build: . command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'" volumes: - .:/myapp ports: - "3000:3000" depends_on: - db environment: - "SELENIUM_DRIVER_URL=http://selenium_chrome:4444/wd/hub" # 追記 selenium_chrome: image: selenium/standalone-chrome-debug # 追記上記5ファイルのうち、docker-compose.ymlのみ、編集で手を加えています。具体的には、
・postgresにユーザー名、パスワードを指定
・System Specを動作させるためのコンテナイメージ「selenium/standalone-chrome-debug」を追加ファイルを作成したら、Railsアプリを新規作成します。
$ docker-compose run web rails new . --force --no-deps --database=postgresql/sample_app$ ls Dockerfile README.md bin db lib public tmp Gemfile Rakefile config docker-compose.yml log storage vendor Gemfile.lock app config.ru entrypoint.sh package.json testrails newに伴い、Gemfileにデフォルトのgemがインストールされました。
Gemfileに編集を加えます。具体的には、
・"rspec-rails"を追加
・"chromedriver-helper"を削除Gemfile# 省略 group :development, :test do # Call 'byebug' anywhere in the code to stop execution and get a debugger console gem 'byebug', platforms: [:mri, :mingw, :x64_mingw] gem 'rspec-rails' # 追記 end group :test do # Adds support for Capybara system testing and selenium driver gem 'capybara', '>= 2.15' gem 'selenium-webdriver' # Easy installation and use of chromedriver to run system tests with Chrome # gem 'chromedriver-helper' # 削除 end #省略Gemfileを更新したため、再ビルドします。
$ docker-compose buildこれで、新しいGemfileでbundle installが実行されました。
続いて、configディレクトリ内のdatabase.ymlを編集します。
config/database.ymldefault: &default adapter: postgresql encoding: unicode host: db username: xxxx password: xxxx pool: 5 development: <<: *default database: myapp_development test: <<: *default database: myapp_testDBを作成します。
$ docker-compose run web rails db:create Starting sample_app_db_1 ... done Created database 'myapp_development' Created database 'myapp_test'コンテナを起動します。このタイミングで、selenium/standalone-chrome-debugのプルが実行されます。
$ docker-compose up -d Pulling selenium_chrome (selenium/standalone-chrome-debug:)... latest: Pulling from selenium/standalone-chrome-debug . . .イメージプルが終了しましたら、docker-composeが正常に立ち上がっているか、確認してみましょう。
$ docker-compose ps Name Command State Ports ----------------------------------------------------------------------------------------------- sample_app_db_1 docker-entrypoint.sh postgres Up 5432/tcp sample_app_selenium_chrome_1 /opt/bin/entry_point.sh Up 4444/tcp, 5900/tcp sample_app_web_1 entrypoint.sh bash -c rm - ... Up 0.0.0.0:3000->3000/tcpweb,db,selenium_chromeの3つが立ち上がっていれば、OKです。ついでに、http://localhost:3000 にアクセスし、いつもの「Yay! You’re on Rails!」が表示されているか、確認しましょう。
また、RSpecが正確にインストールされているかも確認してみましょう。
$ docker-compose run web rspec -v Starting sampleApp_db_1 ... done RSpec 3.9 - rspec-core 3.9.1 - rspec-expectations 3.9.0 - rspec-mocks 3.9.1 - rspec-rails 3.9.0 - rspec-support 3.9.2各バージョンは状況により異なるかと思いますが、RSpec 3.7以上のバージョンであれば、System Specを動作させることができます。
RSpecの初期設定
無事、必要なgemが揃った状態のDockerコンテナを用意できたので、ここからは中身の諸々の設定をいじっていきます。
まず、Railsにデフォルトで配置されているtestディレクトリは使用しないため、削除してしまいましょう。
$ rm -rf testRSpecをインストールします。
$ docker-compose run web rails generate rspec:installこれにより、以下のディレクトリ構造に対して、計3ファイルが生成・配置されます。
ディレクトリ構造/sample_app ├── .rspec └── spec ├── rails_helper.rb └── spec_helper.rb.rpsecファイルを編集します。2行目のコードを追記してください。これにより、テスト結果の表示形式が変更されます。
.rspec--require spec_helper --format documentationデフォルトでは、”失敗したテスト”のみがターミナル上に出力されるのですが、この変更を行うことにより、”成功・失敗両方のテスト”の結果を出力してくれるようになります。必須ではありませんが、設定しておくことをオススメします。
続いて、spec/rails_helper.rbについて、25行目あたりに書かれている下記コードのコメントアウトを外してください。
spec/rails_helper.rb# 省略 Dir[Rails.root.join('spec', 'support', '**', '*.rb')].each { |f| require f } # 省略これにより、後ほど定義するCapybara用の設定ファイルを読み込めるようになります。
specファイルの自動生成機能OFF
デフォルトでは、controllerやviewを作成したタイミングで、対応するspecファイル(=テストコードを書くためのファイル)が自動生成されます。
今回は初学者にとっての混乱を防ぐため、自動生成はOFFにしておきます。config/application.rbに下記コードを追加します。
config/application.rbequire_relative 'boot' require 'rails/all' # Require the gems listed in Gemfile, including any gems # you've limited to :test, :development, or :production. Bundler.require(*Rails.groups) module Myapp class Application < Rails::Application # Initialize configuration defaults for originally generated Rails version. config.load_defaults 5.2 # Settings in config/environments/* take precedence over those specified here. # Application configuration can go into files in config/initializers # -- all .rb files in that directory are automatically loaded after loading # the framework and any gems in your application. ### 追記 ###################################### config.generators do |g| g.test_framework :rspec, view_specs: false, helper_specs: false, controller_specs: false, routing_specs: false, request_specs: false end ############################################## end endCapybaraの初期設定
Capybaraに設定を加えるため、capybara.rbファイルを作ります。
$ mkdir spec/support $ touch spec/support/capybara.rbspec/support/capybara.rbrequire 'capybara/rspec' RSpec.configure do |config| config.before(:each, type: :system) do driven_by :selenium, using: :headless_chrome, options: { browser: :remote, url: ENV.fetch("SELENIUM_DRIVER_URL"), desired_capabilities: :chrome } Capybara.server_host = 'web' Capybara.app_host='http://web' end endこれで、SystemSpecが動作する環境が整いました。
System Spec実践
それでは実際に、静的ページを用いてSystemSpecを使用してみましょう。
今回は、
・rootページにアクセスし、"Hello World!"が表示されていることを検証
・rootページに、"Helpページ"へのリンクが置かれていることを検証
・リンク先のHelpページに、"This is the help page."が表示されていることを検証という流れのテストを書いてみたいと思います。
下準備として、home,helpを持ったStaticPagesコントローラーを作成します。
$ docker-compose run web rails generate controller StaticPages home helpルートとビューをいじります。
config/routes.rbRails.application.routes.draw do root 'static_pages#home' get '/help', to: 'static_pages#help' endapp/views/static_pages/home.html.erb<h1>StaticPages#home</h1> <p><%= link_to "Help", help_path %></p>app/views/static_pages/help.html.erb<h1>This is the help page.</h1>下準備が終わったため、specファイルを書いていきます。specディレクトリ下にsystemディレクトリを作成し、その中に"homes_spec.rb"を配置します。
$ mkdir spec/system $ touch spec/system/homes_spec.rbspec/system/homes_spec.rbを編集します。
spec/system/homes_spec.rbrequire 'rails_helper' RSpec.describe 'Home', type: :system do it 'shows greeting' do # root_pathへアクセス visit root_path # ページ内に'Hello World!'が含まれているかを検証 expect(page).to have_content 'Hello World!' # 'Help'文字列をクリック click_on 'Help' # ページ内に'This is the help page.'が含まれているかを検証 expect(page).to have_content 'This is the help page.' end endいよいよ動かします!今回用意した環境でSystem Specを動かすためには、webコンテナ内に入る必要があります。
$ docker-compose exec web bashrspecは、"rails spec"コマンドで実行できます。
webコンテナ内$ rails spec /usr/local/bin/ruby -I/usr/local/bundle/gems/rspec-core-3.9.1/lib:/usr/local/bundle/gems/rspec-support-3.9.2/lib /usr/local/bundle/gems/rspec-core-3.9.1/exe/rspec --pattern spec/\*\*\{,/\*/\*\*\}/\*_spec.rb Home Capybara starting Puma... * Version 3.12.4 , codename: Llamas in Pajamas * Min threads: 0, max threads: 4 * Listening on tcp://web:45873 shows greeting Finished in 2.33 seconds (files took 8.35 seconds to load) 1 example, 0 failures無事、テストが通ればOKです!!
スクリーンショット保存機能
ちなみにですが、System Specには、「失敗時の画面をスクリーンショットで自動保存する」という便利機能があります。
ビューをいじって、わざとテストを失敗させてみましょう。
app/views/static_pages/home.html.erb<h1>StaticPages#home</h1> <p><%= link_to "Hel", help_path %></p> # "Help"をタイポしてしまったケースwebコンテナ内$ rails spec . . . Home Capybara starting Puma... * Version 3.12.4 , codename: Llamas in Pajamas * Min threads: 0, max threads: 4 * Listening on tcp://web:44173 shows greeting (FAILED - 1) Failures: 1) Home shows greeting Failure/Error: click_on 'Help' Capybara::ElementNotFound: Unable to find link or button "Help" [Screenshot]: tmp/screenshots/failures_r_spec_example_groups_home_shows_greeting_612.png . . .「'Help'が見つかりません!」とメッセージが出され、tmp/screenshotsにエラーが発生したタイミングでのブラウザ画面が保存されています。
"Help"と書いたつもりのところが"Hel"になってしまっているのが、確認できます。
最後に
以上です!不明な点、間違っている点などがありましたら、私のTwitter(@ddpmntcpbr)のDMまで連絡頂けると幸いです。
- 投稿日:2020-03-10T22:03:17+09:00
「Kubernetes実践入門」でKubernetesの知識をキャッチアップされる方へ
先日、書籍「Kubernetes実践入門」でKubernetesの門を叩きました。
ステップバイステップで本格的な構成に近づけていく流れとなっており、個人的にはこの書籍でかなりkubernetesのことを理解できました。
具体的には、簡単なKubernetesクラスタの構築から始まり、セキュリティの設定やFluentd/Elastcsearch/Kibanaでのログ集約環境の構築、Prometheus/Grafanaでのメトリクス監視といったところまで学べます。
高額な研修などを受講しなくても、この書籍で学習すれば、右も左も分からないという状態からは脱して、現場で「もがける」状態になれる。そんな感覚を持ちました。
ただ、中にはスムーズにいかないところもありました。書籍「Kubernetes実践入門」でKubernetesに入門される方に、私と同じような苦しみを回避していただきたいと思い、この記事を書いてみました。
なお、本記事にはmac(macOS Catalina)でKubernetes(minikube)を動作させる場合の問題についてのみ記載されています。他のOSでは試しておりません。
私の個人的なメモも含まれますが、参考になればと思います。
全体を通して
書籍のソースコード
https://github.com/kubernetes-practical-guide/examples
トラブルシューティングのためにPodの状態を確認する方法
書籍に書いてある通りにやっているはずなのに、うまく動かない・・・ということが何度もあると思います。そんな場合に参考にしてください。
以下のコマンドでは、各Podの定義情報に加え、コンテナ生成などのイベント履歴を確認できます。イベント履歴にエラーの状況や原因が記録されていることがあります。
$ kubectl describe podsPod上(コンテナ上)のアプリのログを確認するには、
$ kubectl get podでPodのコンテナ名を確認してから、
$ kubectl logs <Podのコンテナ名>で、Podのログを確認しましょう。
この時、以下のような仕組みで、ログがコマンドラインに出力されます。
アプリが標準出力/標準エラーにログを出力する。
↓
このログを、Dockerランタイムがノードにログファイルとして保存する。
↓
kubectl logs <Podのコンテナ名>を実行すると、Kubernetes APIサーバーが対象ノードのkubeletを呼び出す。
↓
ノードのkubletがログファイルを読み出し、Kubernetes APIサーバーにログファイルの内容を返す。
↓
コマンドラインにログファイルの内容が出力される。Podのコンテナにログインして、コンテナ内部でコマンドを実行したい場合は、以下のとおりです。
$ kubectl exec -it <Podのコンテナ名> /bin/bashPodが起動するのを待つときは、以下のコマンドで状況をウォッチすると良いです。
$ kubectl get pod -wうまくいかないときは・・・
うまくいかないときは、原因の切り分けが難しくなるので、関連するオブジェクトを削除して作り直した方が良いです。
第1章 Hello Kubernetes world! コンテナオーケストレーションとKubernetes
特に困ることはありませんでした。とっても分かりやすいです。
第2章 Kubernetesを構築する
私のmac(macOS Catalina)では、kubectlとminikubeのインストールで少し手間取りました。
書籍ではHomebrewを使った手順が紹介されていますが、実際にはエラーが発生して使えませんでした。エラー内容は理由は省略しますが、結論としては解決は無理そうでした。
そこで、Kubernetes公式サイトの手順どおりインストールしました。具体的には以下の通りです。
kubectrlのインストール手順
書籍で指定されているバージョン(v1.11.3)をインストールする場合、手順は以下の通りです。
$ curl -LO https://storage.googleapis.com/kubernetes-release/release/v1.11.3/bin/darwin/amd64/kubectl $ chmod +x ./kubectl $ sudo mv ./kubectl /usr/local/bin/kubectl $ kubectl version --client公式サイト:https://kubernetes.io/docs/tasks/tools/install-kubectl/#install-kubectl-on-macos
minikubeのインストール手順
書籍で指定されているバージョン(v0.28.2)を取得する場合、VirtualBoxをインストールした後、以下の手順を実施します。
$ curl -Lo minikube https://storage.googleapis.com/minikube/releases/v0.28.2/minikube-darwin-amd64 $ chmod +x minikube $ sudo mkdir -p /usr/local/bin/ $ sudo install minikube /usr/local/bin/ $ minikube version公式サイト:https://kubernetes.io/docs/tasks/tools/install-kubectl/#install-kubectl-on-macos
第3章 Kubernetes上にアプリケーションをデプロイする
この書籍のメインとなる章です。この章だけで全ページの25%くらいを占めており、ものすごく長いです。
特に分かりづらかったところについて、私の理解を記載させていただきます。
「3.5 クラスタ内のアプリケーション間で通信する」
Headless Service
何と言うか、直感的に理解しづらかったのがHeadless Serviceという概念でした。
普通のServiceはClusetIPというIPアドレスを持っていて、外部からClusterIPにアクセスされると、Serviceは配下のPod達に処理を振り分けます(ロードバランシング)。
一方、Headless Serviceは、ClusterIPを持たず、外部からDNSリクエストされると、配下のPod達のIPアドレスの一覧を呼び出し元に返します。
参考:https://cstoku.dev/posts/2018/k8sdojo-09/
「Pod名.Service名」というホスト名と、そのPodのIPアドレスがDNSに登録されます。
つまり、普通のServiceは「頭(head)」として配下のPod達にロードバランシングするのですが、Headless Serviceには、そういう「頭(head)」が無い、ということだと理解しました。
Headless Serviceの呼び出し元が、Headless Service配下の特定のPodにリクエストを送りたい場合に使われるようです。たとえばMySQLをMaster/Slave構成で構築する場合には、どのPodがMasterで、どのPodがSlaveかを呼び出し元が意識する必要があります。書籍では、Headless Serviceのこのような特徴を利用して、MySQLのMaste/Slave構成を構築しています(StatefulSetはHeadless Serviceを使って実現される、と解釈しました)。
「3.6 アプリケーションを外部に公開する」
NodePort
私は、書籍の説明だけでは「NodePort」なるものの概念を理解できませんでした。
簡単に言うと、特定のポート番号(これがNodePort)に送られてきた外部リクエストを、そのポート番号に紐づけられたNodePortサービスにkubernetes内部で転送する、というものらしいです。
具体的に言いますと、外部からのリクエストは以下の経路を辿ります。
- クライアントがHTTPリクエストを送信
↓(送信)
- kubernetesクラスターのIPアドレス:NodePortのポート番号(NodePortサービスのyaml項目:
nodePort) でリクエストを受ける。↓(転送)
- NodePortサービスのIPアドレス:同サービスのポート番号(NodePortサービスのyaml項目:
port)↓(転送)
- NodePortサービスが管理するPodのIPアドレス:同Podのポート番号(NodePortサービスのyaml項目:
targetPort)Podで動作するアプリを、かなりお手軽に外部に公開できるそうですね。しっかりとしたセキュリティが要求される場合には適さないと思いますが、ライトな用途であれば良さそうです。
Ingress
Ingressについても書籍の説明だけではよく分かりませんでした・・・。
IngressはServiceの一種ではありません(yamlで
kind:Ingressと指定します)。
Serviceの前に配置される、クラスター内部のロードバランサーです。こちら(15ページ目)の図が直感的には分かりやすいと思います。
https://www.slideshare.net/nobu0001/kubernetes-119605097Ingressは以下の機能を持ちます。
ルーティング
アクセスされるホスト名と、転送先のServiceの組み合わせを定義しておきます。その定義に従って、特定のホスト名に送られたリクエストを、特定のServiceに転送します。これにより、ルーティングを実現します。負荷分散
複数ノードに存在するServiceに、リクエストを振り分けます。これにより、負荷分散を実現します。外部ロードバランサー
NodePortやIngressとは別に、外部ロードバランサーという概念があります。(Ingressも外部に置けるらしいですが・・)
こちら(14ページ目)の図が直感的には分かりやすいと思います。
https://www.slideshare.net/nobu0001/kubernetes-119605097ただ、Ingressや外部ロードバランサーも、NodePortの仕組みを使っているように見えますが、どうなんでしょうか。また、分かったときに追記したいと思います。
「3.7.3 ストレージの準備」でのNFSサーバー構築手順
macOSにバンドルされているNFSサーバーを利用する手順があるのですが、macOS Catalinaではうまくいきませんでした。
書籍には以下の手順が記載されています。
$ sudo mkdir /share $ sudo chmod 777 /share $ minikube ip 192.168.99.100 $ sudo vi /etc/exports /share -mapall=nobody:wheel -network 192.168.99.0 -mask 255.255.255.0しかし、このまま実行しようとすると、以下のとおりうまくいきませんでした。
$ sudo mkdir /share mkdir: /share: Read-only file systemmacOS Catalinaからファイルシステムが変更されたため、こうなってしまうようです。
※詳しくは以下をご覧ください。大変参考になります。
https://applech2.com/archives/20190610-read-only-system-volume-apfs-refresh.htmlそこで、NFSとして共有するディレクトリを、Catalinaのファイルシステムのルールに従って設ける必要があります。具体的には以下の2点です。
ポイント1
NFSの共有ディレクトリを、ホームディレクトリの直下あたりにつくりましょう(例:
~/nfs_share)。
~/Documentsなどの配下に作ってしまうと、ルール違反となってしまうようです。参考:https://www.firehydrant.io/blog/nfs-with-docker-on-macos-catalina/
その結果、Podの
mysql-1でinit-slave.shが実行される時に、/mnt/backupにDBダンプを出力しようとするのですが、Permission deniedとなり、Podmysql-1がスレーブとして正常に起動しません。一通り構築した後の動作確認で悲しいことになりますので、ご注意ください。
ポイント2
macOS側の
/etc/exportsと、kubernetes側のmysql-pv.yamlにはNFS共有ディレクトリのパスを指定しますが、先頭に/System/Volumes/Data/を付けましょう。例:
/System/Volumes/Data/Users/your_user_name/nfs_share上記に従って書籍を読み替えますと、以下のようになります。
macOSのターミナル$ mkdir ~/nfs_share $ chmod 777 ~/nfs_share $ minikube ip 192.168.99.100 $ sudo vi /etc/exports /System/Volumes/Data/Users/your_user_name/nfs_share -mapall=nobody:wheel -network 192.168.99.0 -mask 255.255.255.0mysql-pv.yamlapiVersion: v1 kind: PersistentVolume metadata: name: backup spec: capacity: storage: 10Gi accessModes: - ReadWriteMany persistentVolumeReclaimPolicy: Retain storageClassName: nfs mountOptions: - hard nfs: # ★★★こちらです!★★★ path: /System/Volumes/Data/Users/your_user_name/nfs_share server: 192.168.99.1nfsのPVC(PersistentVolumeClaim)を削除する方法
nfsのPVC(PersistentVolumeClaim)を誤って作成してしまい、削除したい場合は、Protectionを外す必要があります。以下の記事が大変参考になりました。
https://qiita.com/devs_hd/items/8cbf834c504e57fbe1ffStatefulSetのボリュームの削除
Master/Slave構成のMySQLを構築するときに、うまくいかなくてやり直そうとしました。その際、MySQLのデータファイルを保存するボリュームを削除できず、困りました。(テーブルを再度作ろうとすると、ERROR 1050 (42S01) at line 1: Table 'test' already exists と出てしまいました。)
具体的には、単に
kubectl delete -f mysql-sts.yamlとしてStatefulSetを削除しただけでは、Podが削除されるだけで、マウントされているボリュームは削除されませんでした。そこで、
$ kubectl get pvc # 上記コマンドで調べたpvc名を使って、pvcを削除する。 $ kubectl delete pvc {pvc名}とすると、削除されました。PersistentVolumeClaim(pvc)を削除すると、それに紐付くPersistentVolume(pv)も併せて削除されました。つまり、
kubectl delete pv {pv名}は不要でした。参考:https://kubernetes.io/ja/docs/tasks/run-application/delete-stateful-set/
第8章 アプリケーションを運用する
EFKスタックの構築(Elasticsearch, Fluentd, Kibana)
結論から言いますと、物理メモリ8Gの私のmacでは、efkは動きませんでした・・・。
おまけ(書籍に登場するコマンドたち)
私の復習用です。簡単なコマンドは省略していますので100%ではないですが、書籍中の80%くらいのコマンドについては記載されているはずです。
長すぎてタイプミスしやすいものもありますので、コピペ用途などで使ってください。
第3章 Kubernetes上にアプリケーションをデプロイする
$ kubectl create deployment mattermost-preview --image k8spracticalguide/mattermost-preview:4.10.2 $ kubectl expose --type NodePort --port 8065 deployment mattermost-preview $ minikube service mattermost-preview $ kubectl get event -w -o custom-columns=KIND:.involvedObject.kind,NAME:.metadata.name,SOURCE:.source.component,REASON:.reason,MESSAGE:.message $ kubectl create deployment dive-mattermost-preview --image k8spracticalguide/mattermost-preview:4.10.2 --v=8 $ kubectl get deploy,rs,pod -v=6 2>&1 | grep -e dive-mattermost -e https $ kubectl get po -w | grep -e dive-mattermost -e NAME $ kubectl scale rs $(kubectl get rs|grep dive-mattermost|awk '{print $1}') --replicas=1 -v=6 2>&1 | grep -e mattermost -e https $ kubectl delete po --all -v=6 2>&1 | grep DELETE $ kubectl get rs $(kubectl get rs|grep dive-mattermost|awk '{print $1}') -o template --template='{{.spec.selector.matchLabels}}' $ kubectl get po $(kubectl get po|grep dive-mattermost|head -n 1|awk '{print $1}') -o template --template='{{.metadata.labels}}' $ kubectl edit po $(kubectl get po|grep dive-mattermost|head -n 1|awk '{print $1}') $ kubectl get po {直前のコマンドの実行結果に表示される、PodのID} -o template --template='{{.metadata.labels}}' $ kubectl get po | grep -e dive-mattermost -e NAME $ kubectl edit po {直前のコマンドの実行結果に表示される、PodのID} $ kubectl get rs dive-mattermost-preview-7785c477c9 -o template --template='{{.metadata.ownerReferences}}' $ kubectl delete deploy dive-mattermost-preview --cascade=false -v=8 $ kubectl create deploy mattermost --image nyandora/mattermost:4.10.2 -o yaml --dry-run > mattermost-deploy.yaml $ kubectl create deploy db --image k8spracticalguide/mysql:5.7.22 -o yaml --dry-run > db-deploy.yaml $ kubectl create cm common-env -o yaml --dry-run --from-literal MYSQL_USER=myuser --from-literal MYSQL_PASSWORD=mypassword --from-literal MYSQL_DATABASE=mattermost > cm.yaml $ curl -L -O https://raw.githubusercontent.com/kubernetes-practical-guide/examples/master/ch3.4.2.2/config.json $ kubectl create secret generic common-env -o yaml --dry-run --from-literal MYSQL_ROOT_PASSWORD=rootpassword --from-literal MYSQL_PASSWORD=mypassword > secret.yaml # applyはcreateと違い、初回作成だけでなく変更にも使える。 $ kubectl apply -f . $ kubectl logs $(kubectl get po | grep mattermost | awk '{print $1}') $ kubectl get po -o wide $ kubectl run test1 -i --rm --image k8spracticalguide/busybox:1.28 --restart=Never -- ping -c 1 172.17.0.7 # Labelセレクタで、app=dbに該当するPodの情報を取得。 $ kubectl get po -l app=db $ kubectl apply -f db-service.yaml $ kubectl get svc,ep mattermost-db $ kubectl run -i --rm test2 --image=k8spracticalguide/busybox:1.28 --restart=Never -- nslookup mattermost-db $ kubectl create svc externalname ext-mattermost-db --external-name example.com $ kubectl get svc,ep ext-mattermost-db -o wide $ kubectl run -i --rm test4 --image=k8spracticalguide/busybox:1.28 --restart=Never -- nslookup headless-test $ kubectl expose --type NodePort --port 8065 deploy mattermost --dry-run -o yaml > mattermost-service.yaml $ kubectl apply -f mattermost-service.yaml $ kubectl get svc mattermost -o wide $ minikube ip $ curl http://$(minikube ip):$(kubectl get svc mattermost -o jsonpath="{.spec.ports[0].nodePort}") $ minikube addons enable ingress $ kubectl get deploy -n kube-system -w $ kubectl apply -f mattermost-ingress.yaml $ curl http://chat.$(minikube ip).nip.io $ kubectl run -ti --image k8spracticalguide/busybox:1.28 dns-test --restart=Never --rm /bin/sh $ kubectl run mysql-client --image=k8spracticalguide/mysql:5.7.22 -i --rm --restart=Never -- \ mysql -h mysql-0.mysql --user=root --password=rootpassword <<EOF CREATE TABLE mattermost.test (msg VARCHAR(64)); INSERT INTO mattermost.test VALUES ('hello'); EOF $ kubectl run mysql-loop --image=k8spracticalguide/mysql:5.7.22 -ti --rm --restart=Never -- \ /bin/bash -ic "while sleep 1; do mysql -h mysql-read --user=root --password=rootpassword \ -e 'SELECT @@server_id, msg from mattermost.test'; done"第5章 アプリケーションを更新する
$ kubectl create serviceaccount my-service $ kubectl get sa $ kubectl run -it --rm --restart=Never --serviceaccount "my-service" --image k8s.gcr.io/hyperkube-amd64:v1.13.3 kubectl sh-4.4# env | grep KUBERNETES_SERVICE sh-4.4# ls /var/run/secrets/kubernetes.io/serviceaccount/ $ kubectl create rolebinding my-service-edit --clusterrole edit --serviceaccount default:my-service $ kubectl get rolebinding -o wide sh-4.4# kubectl run mynginx --image k8spracticalguide/nginx:1.15.5 sh-4.4# kubectl delete deploy mynginx sh-4.4# kubectl get pods -n kube-system $ kubectl delete rolebinding my-service-edit $ kubectl create clusterrolebinding my-service-view --clusterrole view --serviceaccount default:my-service sh-4.4# kubectl get pods -n kube-system sh-4.4# kubectl get deploy,pod,svc --all-namespaces sh-4.4# kubectl get secret $ kubectl create rolebinding my-service-edit --clusterrole edit --serviceaccount default:my-service --dry-run -o yaml $ kubectl get sa my-service -o yaml 〜〜略〜〜 secrets: - name: my-service-token-6q87b $ kubectl get secret my-service-token-6q87b -o yaml第6章 アプリケーションの安定性をあげる
$ kubectl get pod -l app=mattermost -o wide -w & $ kubectl get endpoints -l app=mattermost -w & $ kubectl apply -f mattermost-deploy.yaml $ kubectl get pod -l app=mysql -o wide -w & $ kubectl get endpoints -l app=mysql -w & $ kubectl apply -f mysql-sts.yaml $ kubectl scale deployment mattermost --replicas 3 $ kubectl get po -l app=mattermost $ minikube addons enable metrics-server $ kubectl get pod -n kube-system -l k8s-app=metrics-server $ kubectl run cpu-max --image=k8spracticalguide/busybox:1.28 --requests=cpu=50m --limits=cpu=100m -- dd if=/dev/zero of=/dev/null $ kubectl autoscale deployment cpu-max --cpu-percent=70 --min=1 --max=10 $ kubectl get horizontalpodautoscaler $ kubectl describe hpa cpu-max第7章 アプリケーションのセキュリティを強化する
$ minikube delete $ minikube start --extra-config=kubelet.network-plugin=cni --kubernetes-version=v1.11.3 $ kubectl apply -f "https://cloud.weave.works/k8s/net?k8s-version=$(kubectl version | base64 | tr -d '\n')" $ kubectl get deployment -n kube-system $ sudo kubectl port-forward -n kube-system deployment/nginx-ingress-controller 80 $ kubectl run -it --rm --image="k8spracticalguide/busybox:1.28" --restart=Never test -- wget -T5 "http://mattermost:8065" $ kubectl label namespace kube-system system=true第8章 アプリケーションを運用する
$ minikube ssh $ sudo find /var/log/pods | grep mattermostefk
$ minikube delete $ minikube start --kubernetes-version=v1.11.3 --memory 5012 $ kubectl apply -f . $ minikube addons enable efk $ kubectl get po -n kube-system -w $ kubectl logs -n kube-system kibana-logging-8ldj4 $ kubectl describe pod -n kube-system kibana-logging-6v9stPrometheus
$ minikube addons enable metrics-server $ kubectl top node $ kubectl top pod $ minikube start --kubernetes-version=v1.11.3 --extra-config=kubelet.authentication-token-webhook=true $ minikube service prometheus $ kubectl exec -it mysql-0 /bin/bash # mysql -u root -p mysql> CREATE USER 'exporter'@'%' IDENTIFIED BY 'exporterpassword' WITH MAX_USER_CONNECTIONS 3; mysql> GRANT PROCESS, REPLICATION CLIENT, SELECT ON *.* TO 'exporter'@'%'; $ echo -n "exporter:exporterpassword@(mysql.default.svc.cluster.local:3306)/" | base64Grafana
PromQL
sum(mysql_info_schema_table_size{schema="mattermost"}) by (table)
- 投稿日:2020-03-10T20:23:27+09:00
docker-composeのコマンドをよく使う順にまとめる
めっちゃ使う
コンテナの起動
$ docker-compose up -dコンテナの停止
$ docker-compose stopコンテナ内でコマンドを実行
$ docker-compose exec {サービス名} {コマンド} $ docker-compose exec web rails db:migrate $ docker-compose exec web /bin/bashけっこう使う
削除
コンテナ・ネットワーク・イメージ・ボリュームを停止して削除します。DBとか全部消える。
$ docker-compose down $ docker-compose down --rmi all #イメージも全部消すビルド
Dockerイメージからコンテナを作ります。1回目の起動前にやる。削除の後とか。
$ docker-compose build再起動
たぶん
stop→startと同じ$ docker-compose restartログの確認
$ docker-compose logs $ docker-compose logs -ft #ログをリアルタイムで垂れ流すたまに使う
コンテナの一覧を表示
$ docker-compose psイメージの一覧を表示
$ docker-compose images参考
- 投稿日:2020-03-10T16:51:01+09:00
Docker Composeにおける各種ファイルの変更時の反映
はじめに
- ここでは、docker-composeによるコンテナ構成時の、各種ファイル更新時の反映方法についてまとめる。
- 以下のファイルが対象。
- docker-compose.yaml
- Dockerfile
- ソースコード等
docker-compose.yaml変更を反映
- イメージ:再構築の必要なし。
- コンテナ:再構築の必要あり。
docker-compose up -dDockerfileの変更を反映
- イメージ:再構築の必要あり。
docker-compose build- コンテナ:再構築の必要あり。
docker-compose up -dソースコード等の変更を反映
- イメージ:再構築の必要あり。
docker-compose build- コンテナ:再構築の必要あり。
docker-compose up -d- 以下、補足
- ローカルのボリュームとコンテナのボリュームをマウントしてる場合はこれに限らず。 (コンテナに対してローカル領域をマウントしているのでイメージを作り直す必要はない)
- Dockerfileの中でローカルのソースコードを読み込んでいる処理がある場合は、イメージの再構築が必要。 (イメージ内でソースコードを抱えているため、ローカル側のソースコードの修正が反映しないので、作り直す必要がある)
- 投稿日:2020-03-10T15:15:46+09:00
ローカル環境にSentryを立ち上げる
概要
Sentry On-Premise がdocker-composeを導入してくれたおかげで、セットアップがだいぶラクになっていた。
楽すぎて書くまでもないくらいなのだけれども、立ち上げるまでをメモしておく。手順
0. 前提
- Dockerのメモリが 2400MB 以上必要
- Dockerの初期設定だとこれに引っかかってうまく起動できない場合があるので、割り当てメモリをチェックしておく
- Docker Desktopの場合、 [Settings] -> [Advanced] -> [Memory] の順で確認可能
- 少し多めに割り当てておくのがオススメ。2000MB前後になっている場合、3000MBにするとか
1. Sentry On-Premise をダウンロードする
- Git管理するほどのものじゃないのでzipダウンロードで良いと思います。
2. configファイルを更新する
- 追加設定が必要な場合のみ。 この手順は省略しても起動させられる。
- onpremise/sentry の中にある下記ファイルをコピーして
examleを削除することで、設定を上書きできる
config.example.yml
- GitHub連携、通知メールサーバー変更、ファイルストレージ変更など
requirements.example.yml
- 追加プラグインライブラリを導入する場合はここ
sentry.conf.example.yml
- DB・Redis・Cache設定など
3.
install.shを起動する
- すべての面倒くさい手順をこの
install.shがやってくれるようになりました
- ただ時間はかかる…
- このスクリプトのログは
/onpremise-master直下に吐き出される
- スクリプトが終了し、かつ、ログの末尾に↓が表示された場合、正常に準備が完了している
---------------- You're all done! Run the following command to get Sentry running: docker-compose up -dトラブルシューティング
- Dockerの割り当てメモリが足りない場合は↓のログとともにFAILするので、0の手順を再確認すること
Checking minimum requirements... FAIL: Expected minimum RAM available to Docker to be 2400 MB but found 1980 MB4.
docker-compose up -d実行
- ルートディレクトリで
upを実行する- ブラウザから見られるようになるまでに少し時間が必要になる。3分くらい待つと
![]()
5. ブラウザで
localhost:9000にアクセス
- 正常に見られれば
![]()
- この後のプロジェクト設定などは他記事をご参照ください
- 投稿日:2020-03-10T14:55:16+09:00
[Windows] Dockerを使用してホスト環境を汚さずにAngularの開発環境を構築する
TL;DR
- Node.jsの開発環境をDockerをまとめたかった
- ローカル環境にはDocker Desktopとvisual studio codeのみインストール
- Angularでng newから書いている記事がなかったので書いた。これをベースに開発環境を育てていってほしい
環境
- Windows10
- Docker Desktop 2.2.0.3
- docker-composeはDocker Desktopに同梱
- visual studio code 1.42.1[Dockerの拡張機能を使用]
完成したリポジトリ
https://github.com/MegaBlackLabel/angular-docker-sample
ファイル
\---node-angular-sample │ .gitignore │ docker-compose.yml │ docker-entrypoint.sh │ DockerfileDockerfileFROM node:lts COPY docker-entrypoint.sh ./ COPY ./frontend ./ RUN npm install -g @angular/cli WORKDIR /frontend EXPOSE 4200 ENTRYPOINT ["/docker-entrypoint.sh"]docker-compose.ymlversion: '3' services: node: build: . environment: NG_CLI_ANALYTICS: "ci" NG_CLI_ANALYTICS: "false" ports: - "4200:4200" volumes: - "./frontend:/frontend" - nodemodules:/frontend/node_modules tty: true volumes: nodemodules: driver: "local"
- マウントしているfrontendについては初回起動時に作成する
- NG_CLI_ANALYTICS、NG_CLI_ANALYTICSはnpm ci実行時に途中で確認メッセージが出て止まらないようにするために設定
docker-entrypoint.sh#start SQL Server, start the script to create/setup the DB #!/bin/bash FILE="/frontend/package.json" if [ -e $FILE ]; then npm ci fi /bin/bash
- package.jsonがあるときはnpm ciを実行する
- /bin/bashを実行してコンテナが停止しないようにする
マウントするフォルダの作成
docker-composeに記載しているホストのマウント先のフォルダを作成(無いとdocker-compose起動時にエラー発生)mkdir frontend\node_modulesdocker-composeビルド
コンテナのビルドを実施docker-compose builddocker-compose起動
ビルドしたコンテナをバックグラウンドで起動docker-compose up -dAngularのプロジェクト作成
起動しているコンテナにアタッチしてコマンド実行cd .. ng new frontend --skipGit=true※、これでfrontendフォルダにAngularプロジェクトが作成されます
Angular起動
外部から見えるようにhostを設定します# attach shell ng serve --host=0.0.0.0docker-compose終了
コンテナを初期化して終了docker-compose down -vAngularプロジェクト作成後のフォルダ構成(.git内は省略)
\---node-angular-sample │ .gitignore │ docker-compose.yml │ docker-entrypoint.sh │ Dockerfile │ └─frontend │ .editorconfig │ .gitignore │ angular.json │ browserslist │ karma.conf.js │ package-lock.json │ package.json │ README.md │ tsconfig.app.json │ tsconfig.json │ tsconfig.spec.json │ tslint.json │ ├─e2e │ │ protractor.conf.js │ │ tsconfig.json │ │ │ └─src │ app.e2e-spec.ts │ app.po.ts │ ├─node_modules └─src │ favicon.ico │ index.html │ main.ts │ polyfills.ts │ styles.scss │ test.ts │ ├─app │ app.component.html │ app.component.scss │ app.component.spec.ts │ app.component.ts │ app.module.ts │ ├─assets │ .gitkeep │ └─environments environment.prod.ts environment.tsgit cloneして使う場合
初回時にマウント先のフォルダを作成mkdir frontend\node_modules docker-compose buildまとめ
この構成だとホストにNode.jsをインストールしないで初めからDockerの開発環境で進められる。
ただし、作成済みのプロジェクトをgit cloneして使う際にはnode_modulesを作成する必要があるので注意。以上
- 投稿日:2020-03-10T14:17:03+09:00
Docker Hub pushメモ
docker pullないしdocker build(docker-compose build)で引っ張ってくる/構築するビルド済みコンテナ(コンテナイメージ)のホスティングハブ。ビルド時間をダウンロード時間に変えられる(階層別キャッシュあり)。プロジェクトがコンパクト&ポータブルになる。
- アカウントを作成(初回のみ)
- 新規リポジトリを作成(USERNAME/IMAGE_NAME)
- タグ名を指定してビルド(
-t,--tag)
docker build --tag USERNAME/IMAGE_NAME .- Docker Hubにログイン(初回のみ)
docker login- Docker Hubにアップロード(push)
docker push USERNAME/IMAGE_NAME- 確認
docker pull USERNAME/IMAGE_NAMEDockerfileでFROM USERNAME/IMAGE_NAMEdocker-compose.ymlでimage: USERNAME/IMAGE_NAME相当な容量食うだろうに無料しゅごい...(無料の場合プライベートは1リポジトリのみ、料金体系)。
参考
- 投稿日:2020-03-10T13:43:38+09:00
Docker on Linux でホストPCのホスト名を取得する
ブリッジネットワーク使用時、Windows, Macでは、コンテナ内から
host.docker.internalが解決でき、それでホストPCと繋げられるそうです。が、なぜかLinuxでは、20200310現在、その機能がありません。そのうちできるようになりそうな気配はありますが・・・。
それまでは、この方法で、コンテナ立ち上げ時にホストIPを渡せば良いみたいです。
docker run -it --add-host=host.docker.internal:$(ip route | grep docker0 | awk '{print $9}') debian bash
- 投稿日:2020-03-10T13:29:01+09:00
docker の コンソールが固まる。フリーズ・停止して動かない。それ Control + S のせいなら Control + Q で解除できない? ( #Mac #Docker )
- 投稿日:2020-03-10T11:39:58+09:00
MacBookProでDockerを導入する
MacBookProでdockerを導入して、
・イメージをプルしてくる
・環境を編集する
・イメージをプッシュする(docker-hubに、ECRに)
- 投稿日:2020-03-10T10:54:29+09:00
DB接続がない状態でassets:precompileを行う
本番とは違う環境だったり、Dockerfile内でassets:precompileを行ったりするときにDB接続でエラーになるときがある。これを回避する方法ってあるのかなと思ったので調べてみた
activerecord-nulldb-adapterを使う
github
https://github.com/nulldb/nulldbgem 'activerecord-nulldb-adapter'config/database.ymldefault: &default adapter: <%= ENV['DB_ADAPTER'] ||= "mysql2" %>database.ymlに環境変数で
DB_ADAPTERを指定する。$ DB_ADAPTER=nulldb bundle exec rake assets:precompile上記を実行すればDB接続なしでprecompileできる
参考
Rails × ECS でオートスケーリング&検証環境の自動構築
https://tech.medpeer.co.jp/entry/2018/06/20/080000
- 投稿日:2020-03-10T10:31:41+09:00
Pythonアプリケーションにおすすめなコンテナイメージ
Pythonアプリケーションの実行環境としてどのDockerイメージを選べばいいのか、迷ったことはありませんか。自分は毎回悩んでから公式イメージに落ち着くという判断になりがちだったので、ここではっきりさせておこうと思い比較してみることにしました。皆さんの参考になれば嬉しいです。
イメージ選択の評価項目
イメージを選ぶとき、以下の観点があると思います。
観点 内容 安定性 頻繁にファイル構成や内容が変わるようでは長期的な使用は難しいため、ある程度の安定性が必要です(あまりないとは思いますが)。
そのため、公式に近いイメージを使いたいところです。イメージサイズ 一番定量的に評価できるのがイメージのサイズになります。これは小さければ小さいほどありがたいですね。 セキュリティのアップデート セキュリティのアップデートはOSのディストリビューションごとに異なりますが、いつまでサポートしているのかを確認する必要があります。 Pythonがインストール済みか 自分でインストールすることも可能ですが、元からインストールされているほうが手間とビルド時の時間を節約できます。 メジャーなディストリビューションの場合
標準的なディストリビューションを使用すれば安定性とセキュリティアップデートのサポート期間も確認できるため非常に安定しています。
しかし、Pythonのアプリケーションを実行するには不要な共通パッケージも多く含まれており、イメージサイズが大きくなりがちです。また、Pythonの最新バージョンは自身でインストールする必要があります。Alpine Linuxの場合
イメージを小さくするならalpine-linuxが真っ先に候補に上がるのではないでしょうか。しかし、以下のリンクの記事によるとalpineを使用することで最終的にイメージの容量増加やビルド時間の増大など様々な問題が発生してしまうようです。
Using Alpine can make Python Docker builds 50× slower記事で言及されている内容をまとめると以下のようになります。
- pipでPyPIのライブラリをインストールする際、エラーが起きてしまう可能性がある。alpineではGNUバージョンのCライブラリではなくmusl Cライブラリが使用されているのが原因。
- GNUバージョンで使用できるbinary形式のものがPyPIではメジャーだが、muslの環境で使用する場合は用意されているbinaryを使用できず、ほとんどコンパイルし直さなければならない。そのためビルド時間が伸びてしまう。
- musl Cライブラリを使用していることによる予想し辛いバグが出る可能性が存在する
そのため、alpineは素のイメージサイズは小さいのですが、Pythonの実行環境としては不安が残ります。
Docker公式のPythonイメージ
PythonをDockerで使う場合は基本的にこのイメージを使う人が多いのではないでしょうか。大まかにタグの種類は以下になります。
イメージ種類 最新の一つ前のタグ(2020/03時点) サイズ 備考 alpine 3.8.1-alpine3.11 109MB こちらにもAlpineのイメージが存在しますが、上記に記載したデメリットがあるためお勧めではありません。 buster 3.8.1-buster 933MB Debianの最新バージョンであるBusterをベースとしたイメージです。多くの基本的なパッケージがインストールされているため軽量ではありませんが、汎用的な用途の場合はこちらを使うといいでしょう。 buster-slim 3.8.1-slim-buster 193MB Debianのイメージの内の共通パッケージを省き、その分イメージサイズが小さくなったバージョンです。Pythonアプリケーションのみを使用するのであれば問題はなさそうです。 タグを比較してみると、メインのDebianイメージに加えてslimバージョンも存在していることが分かります。こちらはPythonの実行に特化したイメージで、それ以外の多くの機能をバッサリ落としているようです。python-dockerページの注意書きに、「Python実行用途のみでイメージサイズに厳しい制限がある場合にslimを使用し、それ以外ではデフォルトのイメージをお勧めします」と記載してありました。コンテナ内でどこまで実行するかによって選択が変わってきますね。
結論
上記の検討結果から、Pythonアプリケーションをシンプルに動作させるのに適したDockerImageはDebian Buster slimという結論に至りました。イメージサイズは198MBで中々コンパクトですね。
- 投稿日:2020-03-10T10:20:31+09:00
AWS ECSのビルド〜デプロイをCodePipelineで自動化した
Docker + ECS + RailsのプロジェクトでCodePipelineを使用してデプロイまでを自動化したので、その知見をまとめたい。(ブルーグリーンデプロイではなく、通常のデプロイ時の方法)
RailsのプロジェクトだけれどCodePipeline基本的な使い方は、他の言語でもそれほど変わらないと思う
デプロイの流れ
githubにpushすれば自動でデプロイが開始される。デプロイは以下の流れで行うように作った。
- GitHubの特定のブランチ(masterなど)にpushする
- pushされたことがCodepipelineに通知されビルドが開始
- docker-composeを利用して、Dockerをbuildする
- Dockerイメージタグにコミット番号を付与して一意にする
- ビルドが完了したらECRにpushする
- ビルド完了後にECSにデプロイ通知がいく
CodePipelineの設定
CodePipelineはソース管理、ビルド、デプロイをパーツのようにつなげてCD/CIを管理することができるAWSのサービス。以下のサービスをつなぎ合わせて連携することができる
- CodeCommit
- CodeBuild
- CodeDeploy
CodeCommit
まずはGithubで特定のリポジトリにpushされたときに検知できるようにする。ここではmasterがpushsされたときにビルドされる設定した。
ちなみにCodeCommitはGithub以外にも、ECRやS3などと連携することもできる。CodeCommit自体にコードを管理させることも可能。
CodeBuild
CodeBuildではビルドプロジェクトというものを作成する。このビルドプロジェクトはOS環境や、ビルドコマンドを記載するbuildspec.ymlのパスを設定していく。ようはビルドの設定を管理している感じだ。
Ubuntuでイメージが最新バージョンのものを使っておけば特に問題はないかと思う。buildspec.ymlはGithubにあげたプロジェクトに入れておく。そのパスをビルドプロジェクトで設定すればOK
buildspec.yml
ビルドするコマンドをyamlに書いていく。ビルドは以下のような流れで行う。
- ECRからDockerのイメージを取得
- コミットハッシュを取得する(コミットハッシュはDockerイメージタグとして使用する)
- .envに環境変数を追加していく
- docker-composeを利用してビルドする
- dbのmigrateを行う
- デプロイを通知する
version: 0.2 phases: install: runtime-versions: docker: 18 pre_build: commands: - echo -------- Logging in to Amazon ECR... -------- - aws --version - $(aws ecr get-login --region $AWS_DEFAULT_REGION --no-include-email) - REPOSITORY_URI=$AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/hogehoge - COMMIT_HASH=$(echo $CODEBUILD_RESOLVED_SOURCE_VERSION | cut -c 1-7) - IMAGE_TAG=${COMMIT_HASH:=latest} build: commands: - echo -------- Build started on `date` -------- - echo -------- Building the Docker image... -------- - echo AWS_ACCESS_KEY=$AWS_ACCESS_KEY >> .env - echo AWS_SECRET_KEY=$AWS_SECRET_KEY >> .env - echo ECS_ENV_NAME=$ECS_ENV_NAME >> .env - docker-compose -f docker-compose-$ECS_ENV_NAME.yml build - docker-compose -f docker-compose-$ECS_ENV_NAME.yml run --name hogehoge-image web sh -c "bundle exec rake db:create && bundle exec rake db:migrate" - docker commit hogehoge-image $REPOSITORY_URI:latest - docker tag $REPOSITORY_URI:latest $REPOSITORY_URI:$IMAGE_TAG post_build: commands: - echo -------- Build completed on `date` -------- - echo -------- Pushing the Docker images... -------- - docker push $REPOSITORY_URI:latest - docker push $REPOSITORY_URI:$IMAGE_TAG - echo [\{\"name\":\"hogehoge\",\"imageUri\":\"$REPOSITORY_URI:$IMAGE_TAG\"\}] > imagedefinitions.json artifacts: files: imagedefinitions.jsonbuildspec.ymlはpre_build、build、post_buildという3段階で処理を行う。一個ずつ分解して説明していく。
pre_build
ビルドする前の下準備。 GithubのURLとか、コミットハッシュとかをあとで使うので変数に入れている。ちなみにコミットハッシュはDockerイメージタグとして後で使う。
build
- echo AWS_ACCESS_KEY=$AWS_ACCESS_KEY >> .env - echo AWS_SECRET_KEY=$AWS_SECRET_KEY >> .env - echo S3_BUCKET=$S3_BUCKET >> .env環境変数を.envに書き込むようにしている。僕のRailsプロジェクトでは.envで環境変数を管理しており、CodeBuildでもdocker-composeが使用したいという理由でこの形にしている。このやり方がベストプラクティスではないような気がするので、もっと良い方法を見つけたい。
ちなみに環境変数はSystem Managerで管理している。環境変数についてはのちほどもう少し詳しく記載する。
- docker-compose -f docker-compose-$ECS_ENV_NAME.yml build - docker-compose -f docker-compose-$ECS_ENV_NAME.yml run --name hogehoge-image web sh -c "bundle exec rake db:create && bundle exec rake db:migrate" - docker commit hogehoge-image $REPOSITORY_URI:latest - docker tag $REPOSITORY_URI:latest $REPOSITORY_URI:あとはdocker-composeでビルドして、migrateして、Dockerイメージをcommitしているだけ。
ちなみに僕はDockerfile内に
assets:precompileを行っているため、buildspec.ymlにはコマンドが書いていない。post_build
ECSにデプロイするためには最終的にimagedefinitions.jsonというファイルを作成する必要がある。
- docker push $REPOSITORY_URI:latest - docker push $REPOSITORY_URI:$IMAGE_TAG - echo [\{\"name\":\"hogehoge\",\"imageUri\":\"$REPOSITORY_URI:$IMAGE_TAG\"\}] > imagedefinitions.json artifacts: files: imagedefinitions.jsonimagedefinitions.jsonはnameとimageUirを関連付けたjsonを書いていく。複数環境あるときは当たり前だけど複数書いていく。
- name
- ECSのコンテナ名
- imageUri
- ECRのURL
ECSのタスク定義との関連は以下のようになる。
補足
環境変数について
環境変数は秘匿化する必要があるためSystem Managerでパラメータを管理するようにした。
安全な文字列を選択してパラメータを設定する。「名前」欄で設定した値をCodeBuildで使用する。
System Managerで設定した値をbuildspec.ymlで使用するために、CodeBuildに環境変数として設定する必要がある。これを設定しておくとbuildspec.ymlの中で
$HOGEHOGEという値で使用できるようになる。
- 名前
- buildspec.ymlで使用する環境変数名
- 値
- System Managerで設定した名前
- 入力
- 『パラメータ』を選択する
- echo AWS_ACCOUNT_ID=$AWS_ACCOUNT_ID >> .env - echo AWS_ACCESS_KEY=$AWS_ACCESS_KEY >> .env - echo AWS_SECRET_KEY=$AWS_SECRET_KEY >> .envこれでbuildspec.ymlにファイルの中でCodeBuildで設定した環境変数が利用できるようになる
デプロイしたときにタスク定義のバージョンは更新されていく
デプロイされるとimagedefinitions.jsonで設定したコンテナ名とイメージのURLでタスク定義のイメージが変更され、リビジョンが新しく更新されていく。タスクをリビジョンで管理するメリットとして「切り戻しが簡単になる」という点がある。
もしも本番で障害が発生したとき場合にもリビジョンを戻すだけで動作する。ただしDBのカラム変更などしているときは、DBをロールバックする必要もあるので注意が必要。
CodePipelineからデプロイを実行する
なんらかの理由でソースコードをpushせずにデプロイしたい場合は、Codepipelineの画面から直接行うことができる
終わり
ECSは少人数の開発にこそ向いていると思う。ECSでスケールアップから障害復旧までまかせ、CodePipelineでデプロイを自動化しておけばインフラの運用をそれほど考慮しなくて済むようになる。アプリケーション層に集中して開発ができるようになる。
まだまだECSやCodePipelineに対しての知見が足りないので、また気づきがあったら書いていきたい
- 投稿日:2020-03-10T10:20:31+09:00
Rails + Docker + ECSのビルド〜デプロイをCodePipelineで自動化した
Docker + ECS + RailsのプロジェクトでCodePipelineを使用してデプロイまでを自動化したので、その知見をまとめたい。(ブルーグリーンデプロイではなく、通常のデプロイ時の方法)
RailsのプロジェクトだけれどCodePipeline基本的な使い方は、他の言語でもそれほど変わらないと思う
デプロイの流れ
githubにpushすれば自動でデプロイが開始される。デプロイは以下の流れで行うように作った。
- GitHubの特定のブランチ(masterなど)にpushする
- pushされたことがCodepipelineに通知されビルドが開始
- docker-composeを利用して、Dockerをbuildする
- Dockerイメージタグにコミット番号を付与して一意にする
- ビルドが完了したらECRにpushする
- ビルド完了後にECSにデプロイ通知がいく
CodePipelineの設定
CodePipelineはソース管理、ビルド、デプロイをパーツのようにつなげてCD/CIを管理することができるAWSのサービス。以下のサービスをつなぎ合わせて連携することができる
- CodeCommit
- CodeBuild
- CodeDeploy
CodeCommit
まずはGithubで特定のリポジトリにpushされたときに検知できるようにする。ここではmasterがpushsされたときにビルドされる設定した。
ちなみにCodeCommitはGithub以外にも、ECRやS3などと連携することもできる。CodeCommit自体にコードを管理させることも可能。
CodeBuild
CodeBuildではビルドプロジェクトというものを作成する。このビルドプロジェクトはOS環境や、ビルドコマンドを記載するbuildspec.ymlのパスを設定していく。ようはビルドの設定を管理している感じだ。
Ubuntuでイメージが最新バージョンのものを使っておけば特に問題はないかと思う。buildspec.ymlはGithubにあげたプロジェクトに入れておく。そのパスをビルドプロジェクトで設定すればOK
buildspec.yml
ビルドするコマンドをyamlに書いていく。ビルドは以下のような流れで行う。
- ECRからDockerのイメージを取得
- コミットハッシュを取得する(コミットハッシュはDockerイメージタグとして使用する)
- .envに環境変数を追加していく
- docker-composeを利用してビルドする
- dbのmigrateを行う
- デプロイを通知する
version: 0.2 phases: install: runtime-versions: docker: 18 pre_build: commands: - echo -------- Logging in to Amazon ECR... -------- - aws --version - $(aws ecr get-login --region $AWS_DEFAULT_REGION --no-include-email) - REPOSITORY_URI=$AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/hogehoge - COMMIT_HASH=$(echo $CODEBUILD_RESOLVED_SOURCE_VERSION | cut -c 1-7) - IMAGE_TAG=${COMMIT_HASH:=latest} build: commands: - echo -------- Build started on `date` -------- - echo -------- Building the Docker image... -------- - echo AWS_ACCESS_KEY=$AWS_ACCESS_KEY >> .env - echo AWS_SECRET_KEY=$AWS_SECRET_KEY >> .env - echo ECS_ENV_NAME=$ECS_ENV_NAME >> .env - docker-compose -f docker-compose-$ECS_ENV_NAME.yml build - docker-compose -f docker-compose-$ECS_ENV_NAME.yml run --name hogehoge-image web sh -c "bundle exec rake db:create && bundle exec rake db:migrate" - docker commit hogehoge-image $REPOSITORY_URI:latest - docker tag $REPOSITORY_URI:latest $REPOSITORY_URI:$IMAGE_TAG post_build: commands: - echo -------- Build completed on `date` -------- - echo -------- Pushing the Docker images... -------- - docker push $REPOSITORY_URI:latest - docker push $REPOSITORY_URI:$IMAGE_TAG - echo [\{\"name\":\"hogehoge\",\"imageUri\":\"$REPOSITORY_URI:$IMAGE_TAG\"\}] > imagedefinitions.json artifacts: files: imagedefinitions.jsonbuildspec.ymlはpre_build、build、post_buildという3段階で処理を行う。一個ずつ分解して説明していく。
pre_build
ビルドする前の下準備。 GithubのURLとか、コミットハッシュとかをあとで使うので変数に入れている。ちなみにコミットハッシュはDockerイメージタグとして後で使う。
build
- echo AWS_ACCESS_KEY=$AWS_ACCESS_KEY >> .env - echo AWS_SECRET_KEY=$AWS_SECRET_KEY >> .env - echo S3_BUCKET=$S3_BUCKET >> .env環境変数を.envに書き込むようにしている。僕のRailsプロジェクトでは.envで環境変数を管理しており、CodeBuildでもdocker-composeが使用したいという理由でこの形にしている。このやり方がベストプラクティスではないような気がするので、もっと良い方法を見つけたい。
ちなみに環境変数はSystem Managerで管理している。環境変数についてはのちほどもう少し詳しく記載する。
- docker-compose -f docker-compose-$ECS_ENV_NAME.yml build - docker-compose -f docker-compose-$ECS_ENV_NAME.yml run --name hogehoge-image web sh -c "bundle exec rake db:create && bundle exec rake db:migrate" - docker commit hogehoge-image $REPOSITORY_URI:latest - docker tag $REPOSITORY_URI:latest $REPOSITORY_URI:あとはdocker-composeでビルドして、migrateして、Dockerイメージをcommitしているだけ。
ちなみに僕はDockerfile内に
assets:precompileを行っているため、buildspec.ymlにはコマンドが書いていない。post_build
ECSにデプロイするためには最終的にimagedefinitions.jsonというファイルを作成する必要がある。
- docker push $REPOSITORY_URI:latest - docker push $REPOSITORY_URI:$IMAGE_TAG - echo [\{\"name\":\"hogehoge\",\"imageUri\":\"$REPOSITORY_URI:$IMAGE_TAG\"\}] > imagedefinitions.json artifacts: files: imagedefinitions.jsonimagedefinitions.jsonはnameとimageUirを関連付けたjsonを書いていく。複数環境あるときは当たり前だけど複数書いていく。
- name
- ECSのコンテナ名
- imageUri
- ECRのURL
ECSのタスク定義との関連は以下のようになる。
補足
環境変数について
環境変数は秘匿化する必要があるためSystem Managerでパラメータを管理するようにした。
安全な文字列を選択してパラメータを設定する。「名前」欄で設定した値をCodeBuildで使用する。
System Managerで設定した値をbuildspec.ymlで使用するために、CodeBuildに環境変数として設定する必要がある。これを設定しておくとbuildspec.ymlの中で
$HOGEHOGEという値で使用できるようになる。
- 名前
- buildspec.ymlで使用する環境変数名
- 値
- System Managerで設定した名前
- 入力
- 『パラメータ』を選択する
- echo AWS_ACCOUNT_ID=$AWS_ACCOUNT_ID >> .env - echo AWS_ACCESS_KEY=$AWS_ACCESS_KEY >> .env - echo AWS_SECRET_KEY=$AWS_SECRET_KEY >> .envこれでbuildspec.ymlにファイルの中でCodeBuildで設定した環境変数が利用できるようになる
デプロイしたときにタスク定義のバージョンは更新されていく
デプロイされるとimagedefinitions.jsonで設定したコンテナ名とイメージのURLでタスク定義のイメージが変更され、リビジョンが新しく更新されていく。タスクをリビジョンで管理するメリットとして「切り戻しが簡単になる」という点がある。
もしも本番で障害が発生したとき場合にもリビジョンを戻すだけで動作する。ただしDBのカラム変更などしているときは、DBをロールバックする必要もあるので注意が必要。
CodePipelineからデプロイを実行する
なんらかの理由でソースコードをpushせずにデプロイしたい場合は、Codepipelineの画面から直接行うことができる
終わり
ECSは少人数の開発にこそ向いていると思う。ECSでスケールアップから障害復旧までまかせ、CodePipelineでデプロイを自動化しておけばインフラの運用をそれほど考慮しなくて済むようになる。アプリケーション層に集中して開発ができるようになる。
まだまだECSやCodePipelineに対しての知見が足りないので、また気づきがあったら書いていきたい
- 投稿日:2020-03-10T09:08:08+09:00
DockerのインストールとWordPressの起動 - さくらのクラウド超入門・サーバ編(4)
概要
前回はサーバの安全性を高めるため TCP Wrapper で SSH のアクセス制限 をしました。次は、クラウドを使ってインフラ(IaaS)の操作を行う前段階(の続き)です。
実際のアプリケーション動作環境を想像しやすいように、開発環境から整えます。コンテナを使った開発・実行環境である Docker をセットアップし、WordPress (PHP+MySQL)のコンテナを Docker Compose で実行する手順と、サーバに対する電源操作手順を学びます。
解説
アプリケーション開発と操作を簡単にする Docker とは?
Docker 普及前後で、サーバを準備した後の環境構築方法が大きく変わりました。Apache(アパッチ) や PHP(ピーエイチピー) などウェブサーバや言語開発環境を整える前に、今は「Docker」(ドッカー)をセットアップする方法があります。Docker があれば、yum (ヤム)や apt (アプト)を使わなくても、1つのホスト上で複数のサーバ機能や開発言語の環境を同居できます。 Docker についての詳細は、 こちらのスライド資料をご覧ください 。
Docker は、アプリケーションを簡単迅速に開発・移動・実行するためのプラットフォーム(プログラム群)です。Docker Engine(エンジン)というプログラムを通して、アプリケーションをコンテナ(と呼ばれる、名前空間やリソースを分離する技術の組み合わせた、プロセス間の隔離という「特別な状態」)として操作できます。Docker は 2013 年に dotCloud, Inc.(当時のドットクラウド社、現在 はDocker, Inc.)によってオープンソースとして公開されており、誰でも自由に使えます。
Docker でコンテナを操作するには Docker Engine をセットアップする必要があります。セットアップ後は
dockerコマンドを使うだけで、コンテナ化したアプリケーションを操作できます。Docker のインストール用スクリプトの利用
CentOS 7 での Docker インストールは、 [get.docker.com])(https://get.docker.com) で配布されているインストール用スクリプトの利用が便利です。覚えておくのは get.docker.com というホスト名のみ。これをブラウザで開くと、インストール用スクリプトが表示されていて、さらに中にインストール用のコマンドも書かれています。
Docker Engine の正確な手順は、 パッケージ等のセットアップ が必要です。本番向けではないテストや検証用途であれば、Docker Engine のリポジトリと常に最新安定版の Docker Engine をセットアップする方法が簡単です(逆に言いますと、インストールする時点で Docker のバージョンが変わってしまうため、本番環境での利用は推奨されていません)。
インストールするには、サーバにログイン後、以下のコマンドを実行します。
curl -fsSL https://get.docker.com -o get-docker.sh sh get-docker.shコマンド実行直後は 20 秒間の待機時間です。その段階で
Ctrl+Cをキー入力するとインストールを中断できます。進行後は、セットアップが完了するまで待ちます。セットアップが終わったら、Docker Engine デーモン(dockerd)の起動と、サーバ起動時に自動起動するコマンドを実行します。
systemctl start docker systemctl enable dockerそれから、
docker versionコマンドを実行し、Docker のバージョン情報にEngine:の項目が出るかどうかを確認します。もし Engine の項目が表示されずCannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?が出る場合、Docker のセットアップが完了していない可能性があります。あるいは、 Docker Engine が停止している場合は、起動します。以上で Docker のセットアップ作業は完了です。
Docker で Nginx を動かす
Nginx コンテナを起動して、ウェブサーバが表示できるかどうか確認しましょう。
次のコマンドは
nginx:alpineという Nginx が Alpine Linux (アルパイン・リナックス)上で動作する小さな Docker イメージを使い、--detach(デタッチ)モードというバックグラウンドでコンテナを実行し、かつ、--publish 8080:80でホスト側ポート8080をコンテナ内のポート80に割り当て(マッピング)し、--nameでコンテナ名をmynginxとします。docker run --detach --publish 8080:80 --name mynginx nginx:alpineそれから
docker psコマンドを実行すると、mynginxという名前でコンテナが実行中(StatusがUp)になっているのがわかります。この状態で、ウェブブラウザを使い、
http://<サーバのIPアドレス>:8080を実行します。次のような「Welcome to nginx!」(nginxへようこそ!)と表示されれば正常です。
実行しおわった後の nginx コンテナは停止・削除します。
docker kill mynginx docker rm mynginxDocker Compose のインストール
WordPress (ワードプレス)のようなアプリケーションは、複数のプログラムが動作します。複数のコンテナを扱うには、Docker Engine を単体で使うのよりも、Docker Compose(コンポーズ)という名前のツールと一緒に使うのが便利です。コンテナに関する情報などを YAML (ヤムル)ファイルに記述しておきます。
Docker Compose をインストールした環境であれば、コマンド
docker-compose upを実行するだけで、複数のプログラムを同時に制御できます。Docker Compose のインストール方法は複数ありますが、ここでは yum を使ってパッケージをインストールします。
yum -y install docker-composeインストール後は、次のようにして、バージョン情報が表示されるかどうかを確認しましょう。
# docker-compose version docker-compose version 1.18.0, build 8dd22a9 docker-py version: 2.6.1 CPython version: 3.6.8 OpenSSL version: OpenSSL 1.0.2k-fips 26 Jan 2017Docker Compose で WordPress を動かす
Docker Compose でプログラムを動かすには、まず作業用のディレクトリを作成し、移動します。ここでは
mywpをいう名前のディレクトリを作成して移動します。mkdir mywp cd mywpそれから、
vi docker-compose.yamlなどを実行し、同じディレクトリ内に YAML ファイルを作成します。内容は以下の通りにします( 参考: Docker Hub の wordpress イメージのドキュメント(英語) )。yamlversion: '3.1' services: wordpress: image: wordpress restart: always ports: - 80:80 environment: WORDPRESS_DB_HOST: db WORDPRESS_DB_USER: exampleuser WORDPRESS_DB_PASSWORD: examplepass WORDPRESS_DB_NAME: exampledb volumes: - wordpress:/var/www/html db: image: mysql:5.7 restart: always environment: MYSQL_DATABASE: exampledb MYSQL_USER: exampleuser MYSQL_PASSWORD: examplepass MYSQL_RANDOM_ROOT_PASSWORD: '1' volumes: - db:/var/lib/mysql volumes: wordpress: db:ここまで準備すると、同じディレクトリ内に
docker-compose.yamlが出来ています。その状態で、次のコマンドを実行し、 Docker イメージのダウンロード(pull) と、Docker Compose のサービスとして複数のコンテナを同時実行します。docker-compose pull docker-compose up -dそして、
docker-compose psコマンドを実行し、2つのコンテナ(mywp_wordpress_1とmywp_db_1)のStateがUpになっているのを確認します。# docker-compose ps Name Command State Ports ------------------------------------------------------------------------------- mywp_db_1 docker-entrypoint.sh mysqld Up 3306/tcp, 33060/tcp mywp_wordpress_1 docker-entrypoint.sh apach ... Up 0.0.0.0:80->80/tcp今度はウェブブラウザで
http://<サーバのIPアドレス>を実行します。今回の docker-compose.yaml の設定では、WordPress のwordpressコンテナを、ホスト側のポート 80 に割り当て(マッピング)するようにしました。そのため、ポート番号の入力は不要です(ウェブブラウザは、デフォルトのポート 80 を自動的に表示します)。この図のように、WordPress 初期画面が表示されたら、初期設定をしてみましょう。
- 言語一覧では「日本語」を探し、「続ける」をクリックします。
- 次の画面では必要情報を入力の上「WordPressをインストール」をクリックします。
さらに次の画面では「ログイン」をクリックします。
そして、ユーザ名とパスワードを入力して「ログイン」しましょう。WordPress の管理画面にログインできたら、サイト名を確認したり、投稿したり、操作してみましょう。
また、
http://<サーバのIPアドレス>を実行し、 WordPress のコンテンツが表示されるのも確認しましょう。以上で Docker Compose を使った WordPress のセットアップが完了しました。
サーバ電源操作と WordPress の動作確認
あとは、サーバが再起動しても表示されるかどうか、仮想マシンの停止・再起動を行い確認します。
さくらのクラウドのコントロールパネルを、ウェブブラウザで開き、「サーバ」一覧で対象サーバの情報を確認します。それから「サーバの行」をクリックして、行頭にあるチェックボックスに印を入れ、「電源操作」→「シャットダウン」をクリックします。
確認画面が表示されますので、作業対象のサーバが違っていないのを確認し、「シャットダウン」をクリックします。
操作確認ダイアログでは「実行」をクリックします。
そうすると、シャットダウン(停止)命令がサーバに対して送られます。ステータスが「成功」になるまで待ってから、次の確認画面は「閉じる」をクリックします。
そのままお待ちいただくと、サーバの状態を表す「緑色」のステータス(起動)が、「赤色」(準備中)から「灰色」(停止)に変わります。もし変わらなければ、コントロールパネル上の再読込ボタンをクリックします。
停止後は、再び起動しましょう。再びサーバをクリックしてチェックを入れ、「電源操作」→「起動」をクリックします。
対象のサーバを確認し、「起動」ボタンをクリックします。
あとは、確認ダイアログでは「実行」をクリックし、次の画面ではステータスが「成功」になってから「閉じる」をクリックします。しばらく待ち、サーバが起動状態になれば SSH の接続も出来ますし、WordPress も自動的に起動します。
ブラウザから
http://<サーバのIPアドレス>を開き、 WordPress のサイトが表示できるかどうか確認します。振り返り
Docker でコンテナを実行するには、Docker Engineのセットアップが必須です。 get.docker.com のセットアップ用スクリプトを使って手軽にセットアップし、Nginx ウェブサーバをコンテナで起動しました。 また、複数のコンテナを簡単に扱うためには、Docker Compose が便利であり、WordPress アプリケーションを Docker Compose で実行しました。あわせて、サーバの電源操作の仕方も学びました。
次回以降は、ここで作成した環境を使って、サーバのコピー方法やディスクの操作方法、クローンの作成の仕方を学びます。
参考情報
- 投稿日:2020-03-10T09:08:08+09:00
DockerのインストールとWordPressの起動 - さくらのクラウド超入門・サーバ編(3)
概要
前回はサーバの安全性を高めるため TCP Wrapper で SSH のアクセス制限 をしました。次は、クラウドを使ってインフラ(IaaS)の操作を行う前段階(の続き)です。
実際のアプリケーション動作環境を想像しやすいように、開発環境から整えます。コンテナを使った開発・実行環境である Docker をセットアップし、WordPress (PHP+MySQL)のコンテナを Docker Compose で実行する手順と、サーバに対する電源操作手順を学びます。
解説
アプリケーション開発と操作を簡単にする Docker とは?
Docker 普及前後で、サーバを準備した後の環境構築方法が大きく変わりました。Apache(アパッチ) や PHP(ピーエイチピー) などウェブサーバや言語開発環境を整える前に、今は「Docker」(ドッカー)をセットアップする方法があります。Docker があれば、yum (ヤム)や apt (アプト)を使わなくても、1つのホスト上で複数のサーバ機能や開発言語の環境を同居できます。 Docker についての詳細は、 こちらのスライド資料をご覧ください 。
Docker は、アプリケーションを簡単迅速に開発・移動・実行するためのプラットフォーム(プログラム群)です。Docker Engine(エンジン)というプログラムを通して、アプリケーションをコンテナ(と呼ばれる、名前空間やリソースを分離する技術の組み合わせた、プロセス間の隔離という「特別な状態」)として操作できます。Docker は 2013 年に dotCloud, Inc.(当時のドットクラウド社、現在 はDocker, Inc.)によってオープンソースとして公開されており、誰でも自由に使えます。
Docker でコンテナを操作するには Docker Engine をセットアップする必要があります。セットアップ後は
dockerコマンドを使うだけで、コンテナ化したアプリケーションを操作できます。Docker のインストール用スクリプトの利用
CentOS 7 での Docker インストールは、 [get.docker.com])(https://get.docker.com) で配布されているインストール用スクリプトの利用が便利です。覚えておくのは get.docker.com というホスト名のみ。これをブラウザで開くと、インストール用スクリプトが表示されていて、さらに中にインストール用のコマンドも書かれています。
Docker Engine の正確な手順は、 パッケージ等のセットアップ が必要です。本番向けではないテストや検証用途であれば、Docker Engine のリポジトリと常に最新安定版の Docker Engine をセットアップする方法が簡単です(逆に言いますと、インストールする時点で Docker のバージョンが変わってしまうため、本番環境での利用は推奨されていません)。
インストールするには、サーバにログイン後、以下のコマンドを実行します。
curl -fsSL https://get.docker.com -o get-docker.sh sh get-docker.shコマンド実行直後は 20 秒間の待機時間です。その段階で
Ctrl+Cをキー入力するとインストールを中断できます。進行後は、セットアップが完了するまで待ちます。セットアップが終わったら、Docker Engine デーモン(dockerd)の起動と、サーバ起動時に自動起動するコマンドを実行します。
systemctl start docker systemctl enable dockerそれから、
docker versionコマンドを実行し、Docker のバージョン情報にEngine:の項目が出るかどうかを確認します。もし Engine の項目が表示されずCannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?が出る場合、Docker のセットアップが完了していない可能性があります。あるいは、 Docker Engine が停止している場合は、起動します。以上で Docker のセットアップ作業は完了です。
Docker で Nginx を動かす
Nginx コンテナを起動して、ウェブサーバが表示できるかどうか確認しましょう。
次のコマンドは
nginx:alpineという Nginx が Alpine Linux (アルパイン・リナックス)上で動作する小さな Docker イメージを使い、--detach(デタッチ)モードというバックグラウンドでコンテナを実行し、かつ、--publish 8080:80でホスト側ポート8080をコンテナ内のポート80に割り当て(マッピング)し、--nameでコンテナ名をmynginxとします。docker run --detach --publish 8080:80 --name mynginx nginx:alpineそれから
docker psコマンドを実行すると、mynginxという名前でコンテナが実行中(StatusがUp)になっているのがわかります。この状態で、ウェブブラウザを使い、
http://<サーバのIPアドレス>:8080を実行します。次のような「Welcome to nginx!」(nginxへようこそ!)と表示されれば正常です。
実行しおわった後の nginx コンテナは停止・削除します。
docker kill mynginx docker rm mynginxDocker Compose のインストール
WordPress (ワードプレス)のようなアプリケーションは、複数のプログラムが動作します。複数のコンテナを扱うには、Docker Engine を単体で使うのよりも、Docker Compose(コンポーズ)という名前のツールと一緒に使うのが便利です。コンテナに関する情報などを YAML (ヤムル)ファイルに記述しておきます。
Docker Compose をインストールした環境であれば、コマンド
docker-compose upを実行するだけで、複数のプログラムを同時に制御できます。Docker Compose のインストール方法は複数ありますが、ここでは yum を使ってパッケージをインストールします。
yum -y install docker-composeインストール後は、次のようにして、バージョン情報が表示されるかどうかを確認しましょう。
# docker-compose version docker-compose version 1.18.0, build 8dd22a9 docker-py version: 2.6.1 CPython version: 3.6.8 OpenSSL version: OpenSSL 1.0.2k-fips 26 Jan 2017Docker Compose で WordPress を動かす
Docker Compose でプログラムを動かすには、まず作業用のディレクトリを作成し、移動します。ここでは
mywpをいう名前のディレクトリを作成して移動します。mkdir mywp cd mywpそれから、
vi docker-compose.yamlなどを実行し、同じディレクトリ内に YAML ファイルを作成します。内容は以下の通りにします( 参考: Docker Hub の wordpress イメージのドキュメント(英語) )。yamlversion: '3.1' services: wordpress: image: wordpress restart: always ports: - 80:80 environment: WORDPRESS_DB_HOST: db WORDPRESS_DB_USER: exampleuser WORDPRESS_DB_PASSWORD: examplepass WORDPRESS_DB_NAME: exampledb volumes: - wordpress:/var/www/html db: image: mysql:5.7 restart: always environment: MYSQL_DATABASE: exampledb MYSQL_USER: exampleuser MYSQL_PASSWORD: examplepass MYSQL_RANDOM_ROOT_PASSWORD: '1' volumes: - db:/var/lib/mysql volumes: wordpress: db:ここまで準備すると、同じディレクトリ内に
docker-compose.yamlが出来ています。その状態で、次のコマンドを実行し、 Docker イメージのダウンロード(pull) と、Docker Compose のサービスとして複数のコンテナを同時実行します。docker-compose pull docker-compose up -dそして、
docker-compose psコマンドを実行し、2つのコンテナ(mywp_wordpress_1とmywp_db_1)のStateがUpになっているのを確認します。# docker-compose ps Name Command State Ports ------------------------------------------------------------------------------- mywp_db_1 docker-entrypoint.sh mysqld Up 3306/tcp, 33060/tcp mywp_wordpress_1 docker-entrypoint.sh apach ... Up 0.0.0.0:80->80/tcp今度はウェブブラウザで
http://<サーバのIPアドレス>を実行します。今回の docker-compose.yaml の設定では、WordPress のwordpressコンテナを、ホスト側のポート 80 に割り当て(マッピング)するようにしました。そのため、ポート番号の入力は不要です(ウェブブラウザは、デフォルトのポート 80 を自動的に表示します)。この図のように、WordPress 初期画面が表示されたら、初期設定をしてみましょう。
- 言語一覧では「日本語」を探し、「続ける」をクリックします。
- 次の画面では必要情報を入力の上「WordPressをインストール」をクリックします。
さらに次の画面では「ログイン」をクリックします。
そして、ユーザ名とパスワードを入力して「ログイン」しましょう。WordPress の管理画面にログインできたら、サイト名を確認したり、投稿したり、操作してみましょう。
また、
http://<サーバのIPアドレス>を実行し、 WordPress のコンテンツが表示されるのも確認しましょう。以上で Docker Compose を使った WordPress のセットアップが完了しました。
サーバ電源操作と WordPress の動作確認
あとは、サーバが再起動しても表示されるかどうか、仮想マシンの停止・再起動を行い確認します。
さくらのクラウドのコントロールパネルを、ウェブブラウザで開き、「サーバ」一覧で対象サーバの情報を確認します。それから「サーバの行」をクリックして、行頭にあるチェックボックスに印を入れ、「電源操作」→「シャットダウン」をクリックします。
確認画面が表示されますので、作業対象のサーバが違っていないのを確認し、「シャットダウン」をクリックします。
操作確認ダイアログでは「実行」をクリックします。
そうすると、シャットダウン(停止)命令がサーバに対して送られます。ステータスが「成功」になるまで待ってから、次の確認画面は「閉じる」をクリックします。
そのままお待ちいただくと、サーバの状態を表す「緑色」のステータス(起動)が、「赤色」(準備中)から「灰色」(停止)に変わります。もし変わらなければ、コントロールパネル上の再読込ボタンをクリックします。
停止後は、再び起動しましょう。再びサーバをクリックしてチェックを入れ、「電源操作」→「起動」をクリックします。
対象のサーバを確認し、「起動」ボタンをクリックします。
あとは、確認ダイアログでは「実行」をクリックし、次の画面ではステータスが「成功」になってから「閉じる」をクリックします。しばらく待ち、サーバが起動状態になれば SSH の接続も出来ますし、WordPress も自動的に起動します。
ブラウザから
http://<サーバのIPアドレス>を開き、 WordPress のサイトが表示できるかどうか確認します。振り返り
Docker でコンテナを実行するには、Docker Engineのセットアップが必須です。 get.docker.com のセットアップ用スクリプトを使って手軽にセットアップし、Nginx ウェブサーバをコンテナで起動しました。 また、複数のコンテナを簡単に扱うためには、Docker Compose が便利であり、WordPress アプリケーションを Docker Compose で実行しました。あわせて、サーバの電源操作の仕方も学びました。
次回以降は、ここで作成した環境を使って、サーバのコピー方法やディスクの操作方法、クローンの作成の仕方を学びます。
参考情報
- 投稿日:2020-03-10T08:03:06+09:00
DockerイメージにLinuxbrewをDockerfileでインストールする
この記事は株式会社クロノスの「~2020年春~勝手にやりますアドベントカレンダー」の7日目の記事です!
はじめに
自分のパソコンでは環境が出来上がっている状態で
みんなで環境構築をやろう! 教えよう! となったとき。
みなさんならどうするでしょうか。細かい差異も見逃さないために、自分のパソコンに入っているパッケージをアンインストールして、環境構築の実演をしてみせるでしょうか。
(自分のパソコンの環境をいじるのは面倒くさいので)細かいことは気にせず、手順書を作って見守る(適宜困ったことがあったら助ける)でしょうか。
今回は、「自分の環境を汚したくないけど、確実に環境構築をやってもらうために実演をしたい」ってときに
Dockerコンテナ使えないかなーと色々調べてみた結果は紹介させてもらいます。
※ あまり実践的ではないです。知れること・知れないこと
※ 結果的にやっていることはubuntu18.04のイメージに
LinuxbrewをDockerfileでインストールする方法になります。知れること
- ubuntuのイメージにLinuxbrewをDockerfileを使ってインストールする方法
- 素人目線のDockerfileの書き方のポイント
知れないこと
- windowsで環境構築を実演する際のDockerイメージの作り方
実現方法の考え方
おおよそmacでは
Homebrew使って、パッケージインストールして、環境構築等するだろうという偏見仮定の元、
「LinuxbrewをLinuxのDockerイメージに入れたら、見かけ上はmacでHomebrew使うのと変わらなくない? ほらコマンドもbrewで変わらないし……」
という安直な考えで、Linuxbrewの利用を考えます。Linuxbrew
LinuxbrewとはHomebrewのLinux版(名前そのまま)です。
2019年2月から本家 Homebrew 2.0.0 にてLinuxbrewが正式にサポートされるとのことなので(homebrew-2.0.0)、使わない手はないです。参考
LinuxbrewでUbuntu18.04のパッケージ管理
Linuxbrewのススメ目標
作成したイメージのDockerコンテナに入って
$ brew doctorとコマンドを叩いて、一発で
Your system is ready to brew.と出てくることを目指します。ホームディレクトリにLinuxbrewをインストールするDockerfile
こちらは道半ばバージョンです。
Linuxbrewは/home/linuxbrew/.linuxbrew下にインストールすることが推奨されています。
上記デフォルトのディレクトリにインストールすることで、ビルド済みバイナリがあるパッケージはバイナリをインストールできるようになります。
そのためインストール場所がデフォルトでないとbrew doctorでWarningが出ます。ただ、Dockerfileで
/home/linuxbrew/.linuxbrew配下にLinuxbrewをインストールするには、私が試した限り、少々特殊なことをする必要がありました。
こちらはこちらで「素直にDockerfileを書いたバージョン」として、記載しておきたいと思います。こちらの方法だと
/home/[ユーザ名]/.linuxbrew/にインストールされることになります。Warningが出るだけでLinuxbrewは普通に利用可能です。FROM ubuntu:18.04 LABEL maintainer beeeeyan #環境変数を設定 ENV DEBIAN_FRONTEND=noninteractive ENV USER beeeeyan ENV HOME /home/${USER} ENV SHELL /bin/bash ENV PW password # 種々インストール RUN apt-get update && \ apt-get install -y --no-install-recommends \ vim \ sudo \ locales \ build-essential \ ca-certificates \ curl \ file \ git && \ # 一般ユーザーアカウント追加 useradd -m ${USER} && \ # 一般ユーザーにsudo権限を付与 gpasswd -a ${USER} sudo && \ # 一般ユーザーのパスワードを設定 echo "${USER}:${PW}" | chpasswd && \ # ログインシェルを指定 sed -i.bak -r s#${HOME}:\(.+\)#${HOME}:${SHELL}# /etc/passwd && \ # localの設定 locale-gen en_US.UTF-8 # コマンドを実行するUSERを変更 USER ${USER} # 作業ディレクトリを指定 WORKDIR ${HOME} # Linuxbrewをインストール RUN sh -c "$(curl -fsSL https://raw.githubusercontent.com/Linuxbrew/install/master/install.sh)" && \ # pathの設定 echo 'export PATH=${HOME}/.linuxbrew/bin:$PATH' >> .bash_profile説明
素人目線のDockerfileの書き方にも触れています。
今回の「Linuxbrewのインストール」に深く関わる箇所には気持ち最初に☆付けました。LABEL maintainer beeeeyanMAINTAINER(作成者情報)はDocker 1.13から非推奨(deprecated)となっているそうです。LABELで書きましょう。
参考 : 1.13以降はMAINTAINERの代わりにLABELを使うようにしようENV DEBIAN_FRONTEND=noninteractive
brew doctorで出てくるエラーを潰す仮定でlocalesをインストールすることにしたのですが(後述)、localesをインストールしようとするとapt-get install自体が止まったので設定しました。詳しくは以下参照ください。
参考 : Docker Ubuntu18.04でtzdataをinstallするときにtimezoneの選択をしないでinstallするRUN apt-get update && \ apt-get install -y --no-install-recommends \ここいらへんは呪文ですね。
--no-install-recommendsの意味を知らなかったのですが「指定したもの以外余計なものは入れない」ということみたいです。
参考 : #Linux #Ubuntu #docker #Dockerfile のこれは何? apt-get install --no-install-recommendsvim \ sudo \ locales \ build-essential \ ca-certificates \ curl \ file \ git && \ ~省略~ # localの設定 locale-gen en_US.UTF-8
vimとsudo
あると便利かな、くらいで入れています。sudoに関しては「完成版」では必須です。
locales…(省略)…locale-gen en_US.UTF-8
brew doctorとコマンド叩いて、以下のようなエラーが出たのでインストール&設定しました。warning: setlocale: LC_ALL: cannot change locale (en_US.UTF-8): No such file or directory …(省略)
☆
build-essentialとca-certificatesとcurlとfileとgit
調べてもらったらわかりますが、build-essentialとcurlとfileとgitについては、Linuxbrewをインストールするときに公式で先にインストールするよう指定されているパッケージです。
ポイントは ca-certificatesを指定するところにあります。(これがないとcurlなどを使っても通信ができません)このパッケージは普段は自動でインストールされるものです。勘のいい方はお分かりかと思いますがapt-get installに--no-install-recommendsを指定した影響で明示的に書いておく必要が出てきたパッケージです。
参考 : 公式# 一般ユーザーアカウント追加 useradd -m ${USER} && \ # 一般ユーザーにsudo権限を付与 gpasswd -a ${USER} sudo && \ # 一般ユーザーのパスワードを設定 echo "${USER}:${PW}" | chpasswd && \ # ログインシェルを指定 sed -i.bak -r s#${HOME}:\(.+\)#${HOME}:${SHELL}# /etc/passwd && \☆ 一般ユーザを追加している部分
ユーザを追加する理由はrootユーザでLinuxbrewをインストールしようとするエラーになるからです。参考先の情報からsedコマンドの書き方は多少調整しました。
参考 : Dockerで開発環境を仮想化する# コマンドを実行するUSERを変更 USER ${USER} # 作業ディレクトリを指定 WORKDIR ${HOME}コマンドを実行するユーザとディレクトリを作成したユーザに変更しています。
後は公式のインストールコマンド叩いて
.bash_profileにLinuxbrewのパス(今回だと/home/[ユーザ名]/.linuxbrew/bin)を指定するだけです。なぜ「素直な書き方」では
/home/linuxbrew/.linuxbrewにインストールできないのか?素直な書き方だとビルド中に以下のような応答が求められます。
==> Select the Homebrew installation directory - Enter your password to install to /home/linuxbrew/.linuxbrew (recommended) - Press Control-D to install to /home/[ユーザ名]/.linuxbrew - Press Control-C to cancel installationここで
Enter your passwordできなくてつみます。
ただ不思議とControl-Dの挙動はしてくれて、/home/[ユーザ名]/.linuxbrewにインストールされるという寸法です。[完成版]
/home/linuxbrew/.linuxbrewにLinuxbrewをインストールするDockerfileほんの少しトリッキーなことをしたのですが、以下でうまくいきました。
FROM ubuntu:18.04 LABEL maintainer beeeeyan #環境変数を設定 ENV DEBIAN_FRONTEND=noninteractive ENV USER beeeeyan ENV HOME /home/${USER} ENV SHELL /bin/bash ENV PW password # 種々インストール RUN apt-get update && \ apt-get install -y --no-install-recommends \ vim \ sudo \ locales \ build-essential \ ca-certificates \ curl \ file \ git && \ # 一般ユーザーアカウント追加 useradd -m ${USER} && \ # 一般ユーザーにsudo権限を付与 gpasswd -a ${USER} sudo && \ # 一般ユーザーのパスワードを設定 echo "${USER}:${PW}" | chpasswd && \ # ログインシェルを指定 sed -i.bak -r s#${HOME}:\(.+\)#${HOME}:${SHELL}# /etc/passwd && \ # localの設定 locale-gen en_US.UTF-8 && \ # linuxbrewの「Alternative Installation」を実行 git clone https://github.com/Homebrew/brew /home/linuxbrew/.linuxbrew/Homebrew && \ mkdir /home/linuxbrew/.linuxbrew/bin && \ ln -s /home/linuxbrew/.linuxbrew/Homebrew/bin/brew /home/linuxbrew/.linuxbrew/bin && \ eval $(/home/linuxbrew/.linuxbrew/bin/brew shellenv) # コマンドを実行するUSERを変更 USER ${USER} # 作業ディレクトリを指定 WORKDIR ${HOME} # Linuxbrew関連のフォルダ作成 RUN echo ${PW} | sudo -S mkdir -p /home/linuxbrew/.linuxbrew/etc \ /home/linuxbrew/.linuxbrew/include \ /home/linuxbrew/.linuxbrew/lib \ /home/linuxbrew/.linuxbrew/opt \ /home/linuxbrew/.linuxbrew/sbin \ /home/linuxbrew/.linuxbrew/share \ /home/linuxbrew/.linuxbrew/var/homebrew/linked \ /home/linuxbrew/.linuxbrew/var/homebrew/locks \ /home/linuxbrew/.linuxbrew/Cellar && \ # 権限変更 echo ${PW} | sudo -S chown -R ${USER} /home/linuxbrew/.linuxbrew/etc \ /home/linuxbrew/.linuxbrew/include \ /home/linuxbrew/.linuxbrew/lib \ /home/linuxbrew/.linuxbrew/opt \ /home/linuxbrew/.linuxbrew/sbin \ /home/linuxbrew/.linuxbrew/share \ /home/linuxbrew/.linuxbrew/var/homebrew/linked \ /home/linuxbrew/.linuxbrew/Cellar \ /home/linuxbrew/.linuxbrew/Homebrew \ /home/linuxbrew/.linuxbrew/bin \ /home/linuxbrew/.linuxbrew/var/homebrew/locks && \ # パスの設定 echo 'export PATH="/home/linuxbrew/.linuxbrew/bin:$PATH"' >> .bash_profile && \ # パスの反映 . ~/.bash_profile && \ # brew doctorの実行 brew doctorポイントは公式の「Alternative Installation」の部分です。
# linuxbrewの「Alternative Installation」を実行 git clone https://github.com/Homebrew/brew /home/linuxbrew/.linuxbrew/Homebrew && \ mkdir /home/linuxbrew/.linuxbrew/bin && \ ln -s /home/linuxbrew/.linuxbrew/Homebrew/bin/brew /home/linuxbrew/.linuxbrew/bin && \ eval $(/home/linuxbrew/.linuxbrew/bin/brew shellenv)「Alternative Installation」で説明されている内容は、任意の場所に
Linuxbrewをインストールする際によく利用されるコマンドです。これをデフォルトの場所にインストールする方式で利用します。
つまり「Alternative Installation」に書かれているインストール場所~/.linuxbrew/Homebrewを/home/linuxbrew/.linuxbrew/Homebrewに変更します。(変更した状態のものが上記コマンドです)# Linuxbrew関連のフォルダ作成 RUN echo ${PW} | sudo -S mkdir -p /home/linuxbrew/.linuxbrew/etc \ /home/linuxbrew/.linuxbrew/include \ /home/linuxbrew/.linuxbrew/lib \ /home/linuxbrew/.linuxbrew/opt \ /home/linuxbrew/.linuxbrew/sbin \ /home/linuxbrew/.linuxbrew/share \ /home/linuxbrew/.linuxbrew/var/homebrew/linked \ /home/linuxbrew/.linuxbrew/var/homebrew/locks \ /home/linuxbrew/.linuxbrew/Cellar && \ # 権限変更 echo ${PW} | sudo -S chown -R ${USER} /home/linuxbrew/.linuxbrew/etc \ /home/linuxbrew/.linuxbrew/include \ /home/linuxbrew/.linuxbrew/lib \ /home/linuxbrew/.linuxbrew/opt \ /home/linuxbrew/.linuxbrew/sbin \ /home/linuxbrew/.linuxbrew/share \ /home/linuxbrew/.linuxbrew/var/homebrew/linked \ /home/linuxbrew/.linuxbrew/Cellar \ /home/linuxbrew/.linuxbrew/Homebrew \ /home/linuxbrew/.linuxbrew/bin \ /home/linuxbrew/.linuxbrew/var/homebrew/locks && \後半は
brew doctorと打って怒られる部分を先回りして処理しています。
sudoで求められるパスワードはechoと-Sオプションで乗り越え、
前半は必要なフォルダの作成・後半はフォルダの権限の変更(rootユーザではLinuxbrewが利用できないので)、、といった具合です。
権限の変更はエラーでは${whoami}でしたが、Dockerfileの統一感を考えて${USER}でここでは書きました。# brew doctorの実行 brew doctorパスを追加して、最後に一回
brew doctorを実行しておきます。
一回目のbrew doctorでは、一部brewのツールらしきものがインストールされます。(一発でYour system is ready to brewと出てこない!)コンテナを立ち上げるまで〜〜
ビルドします。(試行錯誤する段階では、私はAutomated Build Dockerでやってました)
Dockerfileがカレントディレクトリに存在する状態で以下コマンドを実行$ docker build -t [イメージ名の指定] .コンテナの起動
$ docker run --name [コンテナ名の指定] -it -d [イメージ名] /bin/bash起動中のコンテナに入る
※ちょっとした話ですが--loginをつけておかないと.bash_profileに指定した設定が反映されないのでご注意ください。(パスが反映されずbrew doctorが実行できない…)起動中のコンテナに入る
$ docker exec -it [コンテナ名] /bin/bash --login今回の目標達成です!
参考
DockerでCentOS7のコンテナを用意してLinuxbrewをインストールする
expectコマンドに言及されていて、私も格闘しましたが難しかったです。Dockerfileを書くときに気をつけていること10選
Dockerfileの書き方の参考。LinuxbrewでUbuntu18.04のパッケージ管理
Linuxbrewについて
- 投稿日:2020-03-10T00:20:42+09:00
コーディング未経験のPO/PdMのためのRails on Dockerハンズオン vol.10 - TDD & Test Automation -
はじめに
記念すべき第10回目(ドドーン)。
前回まででサインアップ、サインインの機能を作っていきました。
ただどんどんアプリケーションを作っていくうちに、テスト、面倒になってきましたね。デグレも気になるし...ということで今回は少しアプリ開発を離れまして、TDD(Test Driven Development)、そしてテスト自動化を体得していきましょう!
前回のソースコード
前回のソースコードはこちらに格納してます。今回のハンズオンからやりたい場合はこちらからダウンロードしてください。
TDD
まずはTDDをご紹介。
特にアジャイル開発をやったりすると耳にする単語ですよね。
Test Driven Development、日本語だとテスト駆動開発です。
なんちゃら駆動開発って色々あるんですが、まぁ『なんちゃら』部分を第1に考えた開発って感じで、TDDの場合はテストファーストで開発をしていくって意味ですね。TDDでは『レッド』『グリーン』『リファクタリング』の3つのフェーズを辿ります。
レッド
『レッド』はテストが通らないフェーズです。
TDDではまず機能をコーディングする前に期待動作のテストコードを書きます。
当然、機能をコーディングしていないのですからテストは通りません。この状態が『レッド』です。
TDDでは、この『レッド』のフェーズから機能をコーディングしていって『グリーン』のフェーズをめざします。グリーン
『グリーン』はテストがとりあえず通ったフェーズです。
『レッド』フェーズからアプリをコーディングしていってテストコードが全て通った状態が『グリーン』フェーズです。リファクタリング
『リファクタリング』はテストが通っていてコードとしても良い状態になったフェーズです。
『グリーン』フェーズは、『とりあえず動く』という状態です。
この『グリーン』な状態を保ちつつ、コードをより可読性高く、効率的に変更していくフェーズが『リファクタリング』です。
TDDではテスト自動化を行いますが、これがあるからこそ『動く』状態をキープしながらよりよいコードをめざせます。TDDでは、機能やユーザーストーリー単位にこれらの3つのフェーズで開発を進めていくことで、デグレなくよいコードを作り上げることができます。
BDD
TDDの派生として、BDD(Behavior Driven Development)があります。
日本語では『振る舞い駆動開発』と言われています。これは、TDDよりもより要望に近いテストを記述するフォーマットのようなものと覚えてもらえればよいかと思います。
開発の進め方はTDDと同じで、テストコードを記述して『レッド』『グリーン』『リファクタリング』をしていきます。BDDでは、
Given、When、Thenの3つをテストケースとして定義します。
Given: 前提条件です。「サインイン済みのユーザーが」って感じです。When: 操作や入力です。「プロフィールページでサインアウトリンクをクリックしたとき」って感じです。Then: 期待結果です。「未サインイン状態になりトップページに遷移すること」って感じです。はい、例を出してますが、BDDにのっとると例えば
サインイン済みのユーザーが、プロフィールページでサインアウトリンクをクリックしたとき、未サインイン状態になりトップページに遷移すること
というようなテスト項目を作れます。このようにBDDはよりユーザー目線の振る舞いをテスト項目として定義する考え方です。
このハンズオンでは、このBDDを使ってテストを定義し、テストコードを書いていきます。
テスト自動化
さて、アジャイル開発やTDD/BDDを採用する場合、テスト自動化が必須です。
というよりも一回作ったらもう絶対に追加で開発をしないシステム(どんなシステム?)以外は必須だと思っています。
リファクタリングをしていったり新しい機能を作っていくときに、全てのテストケースをいちいち人間の手でやっていくのは、時間的にも人員的にも不可能だとわかりますよね。そこで、このハンズオンでもテスト自動化を導入していきます。
Rails(Ruby)には使いやすいテストフレームワークRSpecがあります。
またプログラムでWeb画面を操作するSeleniumとこれらをよりコーディングしやすくラッピングしてくれるCapybaraというフレームワークがあります。
このハンズオンでは、この3つのツールを使って実際にユーザーが操作しているのと同じ状態をテストするE2E(End to End)テストを自動化してみます。必要なライブラリ類をインストールする
まずはテスト自動化を実行するために必要なライブラリ類をDockerイメージやRailsアプリにインストールしていきます。
最初に、Dockerコンテナ内でブラウザを立ち上げられないとE2Eテストが行えないので、Dockerfileを編集してDockerイメージにブラウザをインストールします。
今回はChromeを立ち上げてテストできるようにします。DockerfileFROM ruby:2.6.5-alpine3.11 ENV HOME="/app" ENV LANG=C.UTF-8 ENV TZ=Asia/Tokyo WORKDIR $HOME RUN apk update && \ apk upgrade && \ apk add --no-cache \ gcc \ g++ \ less \ libc-dev \ libxml2-dev \ linux-headers \ make \ nodejs \ postgresql \ postgresql-dev \ tzdata \ yarn && \ + apk add --no-cache \ + chromium \ + chromium-chromedriver \ + dbus \ + mesa-dri-swrast \ + ttf-freefont \ + udev \ + wait4ports \ + xorg-server \ + xvfb \ + zlib-dev && \ apk add --virtual build-packs --no-cache \ build-base \ curl-dev COPY Gemfile $HOME COPY Gemfile.lock $HOME RUN bundle install && \ apk del build-packs COPY . $HOME EXPOSE 3000 CMD ["rails", "server", "-b", "0.0.0.0"]これらのパッケージを追加することでDockerコンテナ内でChromeブラウザを起動させてテストすることができるようになります。
次にRailsアプリに必要なライブラリを追加していきます。
Gemfile... group :development, :test do gem 'byebug', platforms: [:mri, :mingw, :x64_mingw] gem 'pry-rails' + + # For test automation + gem 'rspec-rails', '~>3.9' + gem 'capybara' + gem 'selenium-webdriver' end ...ここまでで一度Dockerイメージを再ビルドしましょう。
$ docker-compose build必要なライブラリ類がDockerイメージにインストールされた状態になります。
RSpecの初期設定をする
次にRailsアプリにRSpecの初期インストールをしていきます。
まず、ビルドしたDockerイメージからDockerコンテナを立ち上げて、rails g rspec:installコマンドを実行します。$ docker-compose up -d $ docker-compose exec web ash# rails g rspec:install Running via Spring preloader in process 131 create .rspec create spec create spec/spec_helper.rb create spec/rails_helper.rb色々とファイルが出来上がりました。それぞれの役割は以下の通りです。
.rspec: RSpecを実行するにあたりimportする設定ファイルを定義しているspec/spec_helper.rb: RSpecの設定ファイルspec/rails_helper.rb: RSpecのRails要素をプラスした設定ファイル
rails_helper.rbはspec_helper.rbをオーバーライドしている間柄ですね。ここで
.rspecを少し編集します。.rspec- --require spec_helper + --require rails_helperこれでデフォルトで
rails_helper.rbが設定ファイルとして読み込まれるようになりました。
rails_helper.rbにRSpecで利用するWebドライバーの設定をしていきます。spec/rails_helper.rbrequire 'spec_helper' ENV['RAILS_ENV'] ||= 'test' require File.expand_path('../config/environment', __dir__) abort("The Rails environment is running in production mode!") if Rails.env.production? require 'rspec/rails' # Dir[Rails.root.join('spec', 'support', '**', '*.rb')].each { |f| require f } begin ActiveRecord::Migration.maintain_test_schema! rescue ActiveRecord::PendingMigrationError => e puts e.to_s.strip exit 1 end + + Capybara.register_driver :selenium_chrome_headless do |app| + options = ::Selenium::WebDriver::Chrome::Options.new + options.add_argument('--no-sandbox') + options.add_argument('--headless') + options.add_argument('--disable-gpu') + options.add_argument('--disable-dev-shm-usage') + options.add_argument('--window-size=1680,1050') + Capybara::Selenium::Driver.new(app, browser: :chrome, options: options) + end RSpec.configure do |config| + # Driver setting for system tests. + config.before(:each, type: :system) do + driven_by :selenium_chrome_headless + end + + config.before(:each, type: :system, js: false) do + driven_by :rack_test + end config.fixture_path = "#{::Rails.root}/spec/fixtures" config.use_transactional_fixtures = true config.infer_spec_type_from_file_location! config.filter_rails_from_backtrace! end
Capybara.register_driverでWebドライバーの設定を新しく作ります。
ちょっと書き方が独特な気がすると思いますが、こういうものなのだと思ってください。笑
途中に--headlessという設定がありますが、headlessは目に見える形でブラウザを立ち上げることなくブラウザテストを行うことができるものです。(ヘッドレス Chrome ことはじめ | Web | Google Developers)
これによってテストの実行速度が速くなったりするので是非使いましょう。
RSpec.configureで2つのパターンのWebドライバー設定をしています。
config.before(:each, type: :system)はシステムテストが実行される都度設定されるものことを意味しています。(システムテストはRSpecの用語でE2Eテストと同義と思っていただいていいかと)
driven_byでドライバーを指定するのですが、デフォルトでは先ほど上で定義したselenium_chrome_headlessが、js: falseの場合にはrack_testが設定されることがわかりますね。
rack_testは高速にテストができるのですがjavascriptの機能などを見ることはできないドライバーです。
javascriptの確認をする必要がなく高速にテストを終えたいケースや、javascriptを動かしたくないテストケースに使えます。また、このままでは
rails gコマンドを実行したときに意図しないテストファイルが自動生成されてしまいます。
面倒なのでテスト関連のファイルが生成できないようにします。config/application.rb... module App class Application < Rails::Application config.load_defaults 6.0 # Don't generate system test files. config.generators.system_tests = nil # Timezone config.time_zone = 'Tokyo' config.active_record.default_timezone = :local # Language config.i18n.default_locale = :ja + + # Don't create test files atomatically. + config.generators do |g| + g.test_framework :rspec, + fixtures: false, + view_specs: false, + helper_specs: false, + routing_specs: false, + controller_specs: false, + request_specs: false + end end endここまででRSpecでE2Eテストを実行する初期設定ができました!
テストが実行できるか試してみる
まずは試しにここまでの設定でちゃんとテスト自動化ができるのか試してみましょう。
まず、RSpecのシステムテストを実行するファイルを格納する
spec/systemディレクトリを作成し、その中にsample_spec.rbの名前のサンプルファイルを用意してみましょう。# mkdir spec/system # touch spec/system/sample_spec.rbRSpecは
_spec.rbをテストコードと判断して実行するようになっているのでお忘れなきよう。では
sample_spec.rbに「トップページにアクセスできること」を確認するテストコードを記述していきます。spec/system/sample_spec.rbfeature "サンプルテスト", type: :system do scenario "トップページにアクセスできること" do visit root_path expect(current_path).to eq root_path end endテストコード自体は
scenarioブロックに囲まれた部分です。featureは複数のscenarioを束ねるものでtype: :systemオプションをつけることでシステムテストとして実行することを宣言しています。「トップページにアクセスできること」シナリオのテストコードの中身もちょっと紹介します。
visit root_pathでroot_path、つまり/にアクセスしています。
expect([検査対象]).to [期待結果]で[検査対象]が[期待結果]であるかどうかを検査します。
今回の検査対象はcurrent_pathです。これは今表示されているページのパスのことです。期待結果がroot_pathなので、現在表示されているページが/であればOK、そうでなければNGになります。実はここで使っている
visitやcurrent_pathはCapybaraのおかげでわかりやすい言葉で使えるようになっています。Capybaraの使い方は「使えるRSpec入門・その4「どんなブラウザ操作も自由自在!逆引きCapybara大辞典」 - Qiita」などがわかりやすいと思います。あとは全力で公式ドキュメントよむ。このように操作と検査が一つのシナリオの中に記述され、検査が通るかどうかでOK/NGが判断されます。
もちろん、操作も検査も1つのシナリオの中で複数記述することができます。では、このテストを実行してみましょう。
# rspec spec/system/sample_spec.rb Capybara starting Puma... * Version 4.3.1 , codename: Mysterious Traveller * Min threads: 0, max threads: 4 * Listening on tcp://127.0.0.1:40933 . Finished in 16.97 seconds (files took 16.66 seconds to load) 1 example, 0 failuresRSpecのテストを実行するときは
rspecコマンドを使います。今回の例のようにファイル名を指定することで、そのテストファイルのみが実行されます。
ディレクトリを指定した場合は、そのディレクトリの全てのテストコードが、rspecだけの場合はspecディレクトリの全てのテストファイルが実行されるます。最後に
1 example, 0 failuresと表示されています。exampleはシナリオ数、failuresはそのうちNGだった数を表すので、今回は1つのテストシナリオが実行され全てOKであったことがわかります。試しに、エラーになったときにどうなるか試してみましょう。
spec/system/sample_spec.rbfeature "サンプルテスト", type: :system do scenario "トップページにアクセスできること" do visit root_path - expect(current_path).to eq root_path + expect(current_path).to eq sign_up_path end endCapybara starting Puma... * Version 4.3.1 , codename: Mysterious Traveller * Min threads: 0, max threads: 4 * Listening on tcp://127.0.0.1:37237 F Failures: 1) サンプルテスト トップページにアクセスできること Failure/Error: expect(current_path).to eq sign_up_path expected: "/sign_up" got: "/" (compared using ==) # ./spec/system/sample_spec.rb:4:in `block (2 levels) in <main>' Finished in 4.23 seconds (files took 6.85 seconds to load) 1 example, 1 failure Failed examples: rspec ./spec/system/sample_spec.rb:2 # サンプルテスト トップページにアクセスできること
1 example, 1 failureなのでNGになっていることがわかりますね。
途中の内容をみてみると、expected: "/sign_up"に対してgot: "/"であるためにNGになっていることが分かります。想定通りテストがNGになりました。RSpecのシステムテストではテストがNGになった場合、自動的にスクリーンショットを保存して置いてくれます。そのファイルは
tmp/screenshots/に保存されます。
ただ、ファイルをみると真っ白。本当はroot_pathにアクセスしているのでトップページが表示されていてほしいですよね。
実はこれRSpecのバグっぽいんですよね...(Rails アプリケーションの不安定なテストを撲滅したい 〜system spec のデバッグ方法とテストを不安定にさせる要因〜 - あらびき日記)ということでスクリーンショットがちゃんと表示されるようにヘルパーを作ってみましょう!
スクリーンショットを正しく表示させる
まずはテストを実行するときにヘルパーファイルを読み取るようにします。
これはrails_helper.rbでコメントアウトを外すだけでOKです。spec/rails_helper.rb... - # Dir[Rails.root.join('spec', 'support', '**', '*.rb')].each { |f| require f } + Dir[Rails.root.join('spec', 'support', '**', '*.rb')].each { |f| require f } ...これで、
spec/support/**/*.rbのファイルがテスト実行時に読み込まれるようになります。
ヘルパーファイルを格納しておくディレクトリを作って、スクリーンショットを表示させるためのヘルパーとしてvisible_screenshot_helper.rbを作りましょう!# mkdir -p spec/support/helpers # touch spec/support/helpers/visible_screenshot_helper.rbvisible_screenshot_helper.rbmodule VisibleScreenshotHelper extend ActiveSupport::Concern included do |example_group| example_group.after do take_failed_screenshot end end def take_failed_screenshot return if @is_failed_screenshot_taken super @is_failed_screenshot_taken = true end end RSpec.configure do |config| config.include VisibleScreenshotHelper, type: :system end「Rails アプリケーションの不安定なテストを撲滅したい 〜system spec のデバッグ方法とテストを不安定にさせる要因〜 - あらびき日記」の記事を参考にしました。
これでもう一度テストを実行してみましょう。スクリーンショットがちゃんと表示されるようになるはずです。テスト開始前にスクリーンショットを削除する
こうやってテストしているとスクリーンショットが溜まっていっちゃいますよね...
ということでテスト実行の直前にスクリーンショットを一度全て削除するようにrails_helper.rbに設定を追加します。spec/rails_helper.rb... RSpec.configure do |config| # Driver setting for system tests. config.before(:each, type: :system) do driven_by :selenium_chrome_headless end config.before(:each, type: :system, js: false) do driven_by :rack_test end + + # Delete screenshots before starting new tests + config.before(:all) do + FileUtils.rm_rf(Rails.root.join('tmp', 'screenshots'), secure: true) + end config.fixture_path = "#{::Rails.root}/spec/fixtures" config.use_transactional_fixtures = true config.infer_spec_type_from_file_location! config.filter_rails_from_backtrace! endこれを追加するだけです。
config.before(:all)はテスト実行前に1度だけ実行されることを意味しています。
FileUtils.rm_rfはRubyプログラムとしてrm -rfをやるためのモジュールです。
「【Rails】RSpecのSystem Test実行前に前回テスト時のScreenshotを削除しておく - Qiita」これでスクリーンショットが溢れかえる心配がなくなりました♪
(オプション)iTerm2でスクリーンショットを表示する
ちょっと裏技みたいな感じですが、iTerm2の場合、webコンテナにとある環境変数を与えるとテストNGのタイミングでiTerm2上にスクリーンショットを表示してくれるようになります。
docker-compose.ymlversion: '3' services: db: image: postgres:12.1-alpine environment: - TZ=Asia/Tokyo volumes: - ./tmp/db:/var/lib/postgresql/data web: build: . volumes: - .:/app ports: - 3000:3000 depends_on: - db + environment: + - RAILS_SYSTEM_TESTING_SCREENSHOT=inlineたったこれだけ。この環境変数を適用するために一度コンテナを再起動してRSpecを実行してみましょう。
# exit$ docker-compose down $ docker-compose up -d $ docker-compose exec web ash# rspec spec/system/sample_spec.rb
ちゃんとスクリーンショットが表示されていますね。
RSpecのスクリーンショットはどのファイルなのか探すのが面倒だったりするので、テストシナリオと紐づいてiTerm2で表示してくれるのは非常に助かります。本日はここまでにしましょう!
後片付け
では後片付けしていきますー。
今回は特にDBにデータも保存していないのでDBを初期化する必要もありませんね。
そういえば、RSpecはテスト実行時に保存されたデータはテストシナリオごとに削除されるようになっているので、テストコード内でデータを保存したとしてもDBの初期化は必要ないんです。(しかもRSpecでテストをする場合、勝手にRAILS_ENVがtestで実行されるので、developmentのDBを汚染することもないんです。)と、いうことで今回はコンテナだけ落として終了です!
# exit$ docker-compose downあ、あと、
sample_spec.rbは今回のサンプル用だったので消しておきましょー。
あ、ついでにスクリーンショットも。$ rm spec/system/sample_spec.rb $ rm tmp/screenshots/*まとめ
今回はTDD/BDDを説明させていただきました。
Red → Green → リファクタリング の流れと、
Given、When、Then で受入条件を考えていく方法を紹介しましたね。さらにテスト自動化を実現するために、
RSpecを導入しました。(SeleniumとCapybaraも)
そして初めてのテストコードを書いて実行することができましたね!次回は、実際にここまでつくってきたアプリのテストコードをコーディングします。
その中でテストコードの書き方を覚えていきましょう!では、次回も乞うご期待!ここまでお読みいただきありがとうございました!
本日のソースコード
Reference
- TDDがうまくいかないときは、BDD形式でバックログを書いてみる | Raksul ENGINEERING
- 【Rails】こわくない!TDD/BDD・テスト自動化はじめの一歩ハンズオン! - Qiita
- 使えるRSpec入門・その4「どんなブラウザ操作も自由自在!逆引きCapybara大辞典」 - Qiita
- Rails アプリケーションの不安定なテストを撲滅したい 〜system spec のデバッグ方法とテストを不安定にさせる要因〜 - あらびき日記
Other Hands-on Links

































