20200806のdockerに関する記事は13件です。

【ECS】nginxとphp-fpmが連携したコンテナをECSで動かす

概要

ECS及びECRでコンテナを動かす
ころころUIが変わるので文字ベースで必要な作業をメモ

ゴール

下記で作ったコンテナイメージをECSで動かしphpinfoが見られる
LBは通さずインスタンス直通
https://qiita.com/chanP_yamazaki/items/1397f4899f7768c1ed60

プッシュ元のEC2インスタンスにECRへのアクセス権限を付与

・「AmazonEC2ContainerRegistryFullAccess」ポリシーがアタッチされたIAMRoleを設定する

ECRのリポジトリを作る

・Amazon Container Service -> Amazon ECR -> Repositories へ
・リポジトリを作成
 名前は任意

ECRへコンテナイメージをプッシュする

・先ほど作成したリポジトリをクリック
・プッシュコマンドの表示をクリック
・手順1の「認証トークンを取得し、レジストリに対して Docker クライアントを認証します。」のコマンドをコピーして実行

手順1
$ aws ecr get-login-password --region リージョン | docker login --username AWS --password-stdin アカウントID.dkr.ecr.リージョン.amazonaws.com

※ get-login-passwordはaws-cliのv1.17.10以降でないと実行できません
その場合は下記
$ aws ecr get-login --region リージョン --no-include-email
 ↑で出力された物をコピーして実行
$ docker login -u AWS -p XXXXXXX https://アカウントID.dkr.ecr.リージョン.amazonaws.com

・手順2は既にイメージをビルド済みであればスキップ

・手順3でビルド済みのコンテナイメージ名を確認してタグを付ける
 これをnginxとphp-fpmそれぞれで実施

手順3
$ docker images
$ docker tag コンテナイメージ名:latest アカウントID.dkr.ecr.リージョン.amazonaws.com/リポジトリ名:プッシュする際のイメージ名
例: docker tag project_nginx:latest アカウントID.dkr.ecr.リージョン.amazonaws.com/test:nginx

$ docker images
プッシュしたいコンテナイメージにecr用のタグがついていればOK

・手順4でタグ付したコンテナイメージをECRへプッシュ

手順4
$ docker push アカウントID.dkr.ecr.リージョン.amazonaws.com/test

一つずつも指定可能
$ docker push アカウントID.dkr.ecr.リージョン.amazonaws.com/test:php-fpm
$ docker push アカウントID.dkr.ecr.リージョン.amazonaws.com/test:nginx

・プッシュ後の確認とお掃除
ECRを確認してプッシュしたイメージが一覧に出てくるかを確認
下記コマンドでECRからログアウト
※なにもしなければ12時間でログアウトするけど不要ならログアウトした方が安全

$ docker logout アカウントID.dkr.ecr.リージョン.amazonaws.com

ECSで行う作業の全体像を把握

・タスク定義
 docker-compose.ymlに相当する設定
 これをGUIで設定していくイメージ

・ECSクラスター作成
 ECS経由で立ち上げるEC2インスタンスに関する設定

・ECSクラスター -> サービス作成
 クラスターで立ち上げたEC2インスタンスに対して、どのタスクをどのように配置するかを設定する

ECSのタスクを定義する

※記載がないパラメータは変更しないものとする

・タスク名をつける
 後から変えられないので注意
・コンテナの追加
 これがまさにdocker-compose.ymlに相当している
 ▼ nginx

名称 設定値
コンテナ名 nginx
イメージ ECRにプッシュしたnginxコンテナの「イメージのURI」をコピーして貼り付け
メモリ制限 必要な分だけ設定。全コンテナの合計値がインスタンスのメモリサイズを超えないように注意
ポートマッピング 今回はLBを通さない & httpアクセスなので80:80を設定
スタートアップ依存順序 depends_on設定。コンテナ名「php-fpm」、状態「START」を設定
ネットワーク設定 リンクに「php-fpm:php-fpm」を設定
ストレージとログ ログ設定の「Auto-configure CloudWatch Logs」をONに (立ち上げに失敗した際にログを追えるようになる

 他は最小構成では設定不要

 ▼ php-fpm

名称 設定値
コンテナ名 php-fpm
イメージ ECRにプッシュしたphp-fpmコンテナの「イメージのURI」をコピーして貼り付け
メモリ制限 必要な分だけ設定。全コンテナの合計値がインスタンスのメモリサイズを超えないように注意
ポートマッピング php-fpmは外に開ける必要無いので設定不要
ストレージとログ ログ設定の「Auto-configure CloudWatch Logs」をONに (立ち上げに失敗した際にログを追えるようになる

 他は最小構成では設定不要

・他は設定せず作成

ECSクラスターを作成する

・Amazon Container Service -> Amazon ECS -> クラスター へ
・クラスターの作成

ステップ 名称 設定値
クラスターテンプレートの選択 クラスターテンプレートの選択 EC2 Linux + ネットワーキング
クラスターの設定 クラスター名 test-cluster
インスタンスの設定各種 インスタンスタイプや数は必要に応じて
AmiId 特に理由がなければ今は 「AmazonLinux2」
キーペア コンテナの思想上、インスタンスに入って何かを操作する必要はないので 「なし」 
ネットワーキング vpc、サブネットは必要に応じた設定を
ネットワーキング - セキュリティグループ ポートは80を開ける。IPは必要なものを
Auto assign public IP Use Subnet setting

・作成実行
 実行したクラスターの「ECSインスタンス」タブで、指定した個数のインスタンスが立ち上がり、ステータスがACTIVEになったことを確認

コンテナを立ち上げる

・サービスタブを開いて「作成」

ステップ 名称 設定値
サービスの設定 起動タイプ EC2
タスク定義 ファミリー 先ほど「タスク定義」で作ったタスクを指定
タスク定義 リビジョン latest(最新バージョン)を指定
クラスター test-cluster
サービス名 適当に
サービスタイプ REPLICA (DAEMONだとどう変わるか後で調査
タスク数 1
ネットワーク構成 LBを通す場合はここで設定。今回は何も設定せず次へ
Auto Scalingオプション EC2インスタンスの数をAutoScalingさせたいならここで設定。今回は何も設定せず次へ

・サービスの作成
・作成したサービスのタスクタブにRUNNINGのタスクが出来あがれば成功
・EC2のpublicIPかDNS名にアクセスしてphpinfoが確認できればOK!

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

Ubuntu 16.04をインストールしたAlibaba Cloud ECS上でDockerを使ってMastodonを設定する

このチュートリアルでは、Ubuntu 16.04をインストールしたAlibaba Cloud Elastic Compute Service (ECS)上でMastodonを設定します。

本ブログは英語版からの翻訳です。オリジナルはこちらからご確認いただけます。一部機械翻訳を使用しております。翻訳の間違いがありましたら、ご指摘いただけると幸いです。

前提条件

  • Alibaba Cloud Elastic Compute Service (ECS)を有効にし、有効な支払い方法を確認する必要があります。新規ユーザーの場合は、新規アカウントに300ドル~1200ドル相当のAlibaba Cloudクレジットを獲得できます。ECSインスタンスのセットアップ方法がわからない場合は、こちらのチュートリアルまたはクイックスタートガイドを参照してください。ECSインスタンスは、少なくとも2GBのRAMと2コアプロセッサを搭載している必要があります。
  • Alibaba Cloudから登録されたドメイン名。すでにAlibaba Cloudまたは他のホストからドメインを登録している場合は、そのドメインネームサーバーレコードを更新することができます。
  • ドメイン名は、あなたのAlibaba Cloud ECSのIPアドレスを指している必要があります。
  • Alibaba CloudのVNCコンソールまたはPCにインストールされているSSHクライアントにアクセスします。
  • サーバーのホスト名を設定し、root権限を持つユーザーを作成します。

サーバーの設定

パッケージのインストールを進める前に、以下のコマンドを使用してUbuntuシステムをアップデートしてください。このコマンドを実行するには、root 以外のユーザから sudo 権限でログインすることを忘れないでください。

# sudo apt update && sudo apt upgrade

Docker CEをインストールするためには、サポートされているファイルを取得するためにsoftware-properties-commonパッケージが必要です。software-properties-commonをインストールするには、以下のコマンドを実行します。

# sudo apt-get install software-properties-common -y

Docker CEのインストールには、Apt-transport-https、ca-certificates、curlが必要です。これらをインストールするには、以下のコマンドを実行します。

# sudo apt-get install apt-transport-https -y 
# sudo apt-get install ca-certificates -y 
# sudo apt-get install curl -y 

Docker CEとDocker Composeのインストール

以下のコマンドを実行して、Docker用のGPGキーを追加します。

# curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -

以下のコマンドを実行して、GPG キーのフィンガープリントを確認します。

# sudo apt-key fingerprint 0EBFCD88

以下のコマンドを実行してDockerリポジトリを追加します。

# sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable”

以下のコマンドを実行してシステムをアップデートし、追加されたリポジトリをロードします。

# sudo apt update

以下のコマンドを実行してDockerをインストールします。

# sudo apt install docker-ce

以下のコマンドを実行して、ユーザ名をdockerグループに追加します。

# sudo adduser aareez docker 

現在のシェルセッションを閉じて、新しいセッションを開始してください。そうしないとDockerを実行できず、パーミッションエラーが表示される可能性があります。

以下のコマンドを実行して、dockerが正しく実行されているかどうかを確認します。

# docker run hello-world

Docker Composeをダウンロードしてインストールするには、以下のコマンドを実行します。

# sudo curl -L https://github.com/docker/compose/releases/download/1.21.2/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose

Docker Composeファイルのパーミッションを設定するには、以下のコマンドを実行します。

# sudo chmod +x /usr/local/bin/docker-compose

Mastodonをインストール

以下のコマンドを実行して、GitリポジトリからMastodonをクローンします。

# git clone https://github.com/tootsuite/mastodon  

以下のコマンドを実行して、ダウンロードしたディレクトリに移動してください。

# cd mastodon

以下のコマンドを実行して、.env.production.sampleを.env.productionにコピーします。

# cp .env.production.sample .env.production

秘密鍵を生成するには、Dockerイメージをビルドして実行する必要があります。ビルドするには、以下のコマンドを実行します。

# docker-compose build

Dockerイメージの構築に成功しました。設定に必要な3つの秘密鍵を生成する必要があります。SECRET_KEY_BASEを生成するには、以下のコマンドを実行します。

# SECRET_KEY_BASE=$(docker-compose run --rm web bundle exec rake secret)

以下のコマンドを実行して、.env.productionファイルにSECRET_KEY_BASEの値を挿入します。

# sed -i -e "s/SECRET_KEY_BASE=/&${SECRET_KEY_BASE}/" .env.production

OTP_SECRETを生成するには、以下のコマンドを実行します。

# OTP_SECRET=$(docker-compose run --rm web bundle exec rake secret)

以下のコマンドを実行して、.env.productionファイルにSECRET_KEY_BASEの値を挿入します。

# sed -i -e "s/OTP_SECRET=/&${OTP_SECRET}/" .env.production

PAPERCLIP_SECRETを生成するには、以下のコマンドを実行します。

# PAPERCLIP_SECRET=$(docker-compose run --rm web bundle exec rake secret)

以下のコマンドを実行して、.env.productionファイルにPAPERCLIP_SECRETの値を挿入します。

# sed -i -e "s/PAPERCLIP_SECRET=/&${PAPERCLIP_SECRET}/" .env.production

LOCAL_DOMAINの値を更新する必要があります。そのためには、テキストエディタで.env.productionファイルを開く必要があります。以下のコマンドを実行して、.env.productionファイルを開きます。

# sudo nano ~/mastodon/.env.production

LOCAL_DOMAIN変数を見つけ、その値をexample.comからECSのドメイン名またはIPアドレスに変更し、更新したファイルを保存します。

mastodonに移動します。

# cd mastodon

上記で変更したので、再度Dockerイメージをビルドする必要があります。ビルドするには、以下のコマンドを実行します。

# docker-compose build

を実行してマイグレーションを実行します。

# docker-compose run --rm web rails db:migrate

物事をよりスムーズにするためには、アセットを事前にコンパイルする必要があります。そのためには、以下のコマンドを実行します。

# docker-compose run --rm web rails assets:precompile

以下のコマンドを実行してコンテナを実行します。

# docker-compose up -d

Nginxサーバーのインストールと設定

Nginxの設定を作成してMastodonを動作させ、SSL証明書をインストールするには、Nginxサーバーをインストールする必要があります。

以下のコマンドを実行してnginxサーバーをインストールします。

# sudo apt-get install nginx

以下のコマンドを実行して、デフォルトサイトのNginx設定を削除する必要があります。

# sudo rm /etc/nginx/sites-available/default

以下のコマンドを実行して、デフォルトサイトのシンボリックリンクを削除します。

# sudo rm /etc/nginx/sites-enabled/default

以下のコマンドを実行して、Mastodon用のNginx設定ファイルを作成します。

# sudo touch /etc/nginx/sites-available/mastodon

以下のコマンドを実行して、Mastodonのシンボリックリンクを作成します。

# sudo ln -s /etc/nginx/sites-available/mastodon /etc/nginx/sites-enabled/mastodon

以下のコマンドを実行して、Mastodon Nginxの設定ファイルをnanoのテキストエディタで開きます。

# sudo nano /etc/nginx/sites-available/mastodon

開いているファイルに以下のテキストをコピーペーストし、変更内容を保存します。

map $http_upgrade $connection_upgrade {
  default upgrade;
  ''      close;
}

server {
  listen 80;
  listen [::]:80;
  server_name softpedia.xyz;
  root /home/mastodon/live/public;
  # Useful for Encrypt
  location /.well-known/acme-challenge/ { allow all; }
  location / { return 301 https://$host$request_uri; }
}

server {
  listen 443 ssl http2;
  listen [::]:443 ssl http2;
  server_name softpedia.xyz;

  ssl_protocols TLSv1.2;
  ssl_ciphers HIGH:!MEDIUM:!LOW:!aNULL:!NULL:!SHA;
  ssl_prefer_server_ciphers on;
  ssl_session_cache shared:SSL:10m;

  ssl_certificate     /etc/letsencrypt/live/softpedia.xyz/fullchain.pem;
  ssl_certificate_key /etc/letsencrypt/live/softpedia.xyz/privkey.pem;

  keepalive_timeout    70;
  sendfile             on;
  client_max_body_size 80m;

  root /home/mastodon/live/public;

  gzip on;
  gzip_disable "msie6";
  gzip_vary on;
  gzip_proxied any;
  gzip_comp_level 6;
  gzip_buffers 16 8k;
  gzip_http_version 1.1;
  gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;

  add_header Strict-Transport-Security "max-age=31536000";

  location / {
    try_files $uri @proxy;
  }

  location ~ ^/(emoji|packs|system/accounts/avatars|system/media_attachments/files) {
    add_header Cache-Control "public, max-age=31536000, immutable";
    try_files $uri @proxy;
  }

  location /sw.js {
    add_header Cache-Control "public, max-age=0";
    try_files $uri @proxy;
  }

  location @proxy {
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto https;
    proxy_set_header Proxy "";
    proxy_pass_header Server;

    proxy_pass http://127.0.0.1:3000;
    proxy_buffering off;
    proxy_redirect off;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection $connection_upgrade;

    tcp_nodelay on;
  }

  location /api/v1/streaming {
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto https;
    proxy_set_header Proxy "";

    proxy_pass http://127.0.0.1:4000;
    proxy_buffering off;
    proxy_redirect off;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection $connection_upgrade;

    tcp_nodelay on;
  }

  error_page 500 501 502 503 504 /500.html;
}

SSL証明書をインストール

Let's Encrypt with Certbotを使ってSSL証明書をインストールします。そのためには、以下の手順を実行します。

パッケージを更新します。

# sudo apt-get update

software-properties-commonをインストールします。

# sudo apt-get install software-properties-common.

以下のコマンドを使用して、サートボットのリポジトリを追加します。

# sudo add-apt-repository ppa:certbot/certbot

追加された certbot リポジトリを読み込むようにパッケージを更新します。

# sudo apt-get update

SSL証明書発行前にapacheを停止します。

# sudo systemctl stop apache2

以下のコマンドでpython-certbot-apacheをインストールします。

# sudo apt-get install python-certbot-apache

以下のコマンドを実行して、Let's Encrypt SSLを発行してもらいます。

# sudo certbot --apache -d softpedia.xyz

オプション2を選択してリンクをhttpsにリダイレクトし、SSLのバーチャルホスト設定を更新します。apacheサーバを再起動します。

# sudo systemctl start apache2

あなたのウェブサイトにアクセスするには、https://your_domain_name.tld

image.png

これで完了です。Alibaba Cloud ECSサーバーにMastodonをインストールすることに成功しました。データベースのデフォルトパスワードを変更し、管理者アカウントを設定することで安全にインストールできます。

アリババクラウドは日本に2つのデータセンターを有し、世界で60を超えるアベラビリティーゾーンを有するアジア太平洋地域No.1(2019ガートナー)のクラウドインフラ事業者です。
アリババクラウドの詳細は、こちらからご覧ください。
アリババクラウドジャパン公式ページ

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

Docker 公式ドキュメントの Rails Quickstart 完全解説

Docker の公式ドキュメントに Rails の Quickstart があります:
Quickstart: Compose and Rails | Docker Documentation

こちらのドキュメントを参考に Rails を始める方が増えており、
Tetatail で結構な件数の質問に回答しました。

この Quickstartは説明が不十分だったり間違いがあったりで
Rails に慣れてないと行間を読んで正しい手順を踏むことは難しいでしょう。

この記事で指摘している Quickstart の間違いなどは、すでに GitHub 上で PR を投稿しています。
Yukihiko shinoda/update quickstart rails by yukihiko-shinoda · Pull Request #11215 · docker/docker.github.io

ここで Rails の Quickstart についてまとめて解説しておきます。

この Quickstart を実施するために必要なツール

この解説を理解するために必要な基礎知識

この Quickstart を理解するためには、次の基礎知識が必要です:

理解するために必要ないツール・知識

  • Rails チュートリアル
    • Rails チュートリアルで解説されていることと、この Quickstart で得られる知識は、大部分が別の内容です
    • この Quickstart を理解する上で必要な Rails に関する知識はこの記事内で解説します

この Quickstart は何をするためのものなの?

この Quickstart ガイドでは、Docker Compose を使用して Rails / PostgreSQL アプリを設定および実行する方法を理解します。

この Quickstart はどんなことをしているの?

この Quickstart は次の手順を実施します:

  1. Define the project
    • Docker イメージのビルドに必要なファイルを作成します
  2. Build the project
    • 手順 1. で準備したファイルをもとに Rails の実行環境を起動して Rails を動かし新規 Rails アプリケーションを作成します
      • このとき自動的に Docker イメージがビルドされます
    • 新規作成した Rails アプリケーションを使い Docker イメージをビルドし直します
  3. Connect the database
    • Rails の実行環境とデータベースのサービスを一度に起動します
  4. View the Rails welcome page!
    • ブラウザで Rails の welcome page が表示されることを確認します

この Quickstart が理解しづらい原因

この Quickstart は手順 1. Define the project で準備するファイルやその内容が
相当な量であるにも関わらず、それらの目的についての解説が不十分であるためです。

  • 上記の手順 1. Define the project で準備するファイルの内容を最初からすべて理解することは難しいです
    • 準備するファイルの内容が次の目的を兼ねる内容になっており、どちらの目的のための内容なのか分かりづらいため
      • 手順 2. Build the project で行う新規 Rails アプリケーション作成のための内容
      • 最終的な Rails アプリケーション実行のための内容
  • 手順 2. Build the project で Rails の実行環境のためのイメージを再度ビルドし直しています
  • 手順 2. のコマンドが誤っています
  • Dockerfile に不要な内容が含まれています
  • docker-compose.yml
    • 誤っています
    • 不要な内容が含まれています

以降に、各手順で注意すべきポイントを解説します。

各手順の解説

Define the project

この手順では、Docker イメージのビルドに必要なファイルを作成します。

この手順を終えると、プロジェクトのディレクトリー内には次のようなファイルがあるはずです:

project/
+---docker-compose.yml
+---Dockerfile
+---entrypoint.sh
+---Gemfile
+---Gemfile.lock

ここで、初めてこの Quickstart を実施する人は
次の理由により混乱してしまうことでしょう:

  • ファイルの内容が誤っていたり、不要な内容が含まれているため
  • ここで準備するファイルやその内容が相当な量であるにも関わらず、それらの目的についての解説が不十分であるため

そこで、この記事では Dockerfiledocker-compose.yml の内容を次のように修正します:

Dockerfile
FROM ruby:2.5
RUN apt-get update -qq && apt-get install -y nodejs
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"]

# Start the main process.
CMD ["rails", "server", "-b", "0.0.0.0"]
docker-compose.yml
version: '3.8'
services:
  db:
    image: postgres
    environment:
      POSTGRES_PASSWORD: password
  web:
    build: .
    volumes:
      - .:/myapp
    ports:
      - "3000:3000"
    depends_on:
      - db

各ファイルの目的の整理

ここで準備するファイルは次の 2 つの目的のためです:

  • 手順 2. Build the project で行う新規 Rails アプリケーション作成のため
  • 手順 3. Connect the database で行う最終的な Rails アプリケーション実行のため

どのファイルがどの目的のためのファイルなのかは、次の通りです:

ファイル 目的
docker-compose.yml 手順 2, 3.
Dockerfile 手順 2, 3.
entrypoint.sh 手順 3.
Gemfile 手順 2.
Gemfile.lock 手順 2.

まず、手順 2. のために必要なファイルだけを準備できないの?

手順 2. のために必要なファイルだけを準備すると、次のようになります:

Dockerfile

手順 2. で必要な処理以外をコメントアウトします:

Dockerfile
FROM ruby:2.5
# RUN apt-get update -qq && apt-get install -y nodejs
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"]

# # Start the main process.
# CMD ["rails", "server", "-b", "0.0.0.0"]
GemfileCOPY して bundle install を実行する理由

この後の手順 2. では rails new というコマンドを使い、
新規 Rails アプリケーションを作成します。

rails new コマンドを使うためには、Rails をインストールする必要があります。
Rails は「Gem パッケージ」という形式で配布されています。
「Gem パッケージ」とは、Ruby のプログラムを配布するための形式です。

参考: パッケージ - 意味・説明・解説 : ASCII.jpデジタル用語辞典

Gem パッケージをインストールするためには、
bundler という、Gem パッケージを一度にインストールするためのツールの
bundle install コマンドを使います。

bundle install コマンドは Gemfile というファイルを参照してパッケージをインストールします。

ここで出てきた命令一覧:

/myapp を新規作成して WORKDIR に指定する理由

ここで、/myapp ディレクトリーを作成して作業ディレクトリーとして指定しています。
これは、ベースイメージとして指定されている ruby:2.5 は、規定の作業ディレクトリーである / には
大切なディレクトリーがたくさんあり、
Rails アプリケーションのコードを展開したり、後述する「bind マウント」を行うと
様々な不具合が起きるためです。

ベースイメージの作業ディレクトリーを調べる方法は次の通りです:

FROM ruby:2.5
RUN pwd
$ docker-compose build
db uses an image, skipping
Building web
Step 1/2 : FROM ruby:2.5
 ---> a5c07a6f3bb6
Step 2/2 : RUN pwd
 ---> Running in 43f0381dd652
/

参考: Answer: Is it possible to show the `WORKDIR` when building a docker image?

ここで出てきた命令一覧:

Gemfile

Gemfile は Quickstart で提示されている内容をそのまま準備します:

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

Gemfile には、次のような情報を記述します:

  • Gem パッケージをどこからダウンロードしてインストールするか
  • インストールする Gem パッケージの一覧

この場合は標準の Gem パッケージ配信サーバーである https://rubygems.org から
Rails のバージョン 5 をインストールします。

参考: Bundler: The best way to manage a Ruby application's gems

docker-compose.yml

続いて、docker-compose.yml です。

手順 2. で必要な処理以外をコメントアウトします:

docker-compose.yml
version: '3'
services:
  # db:
  #   image: postgres
  #   environment:
  #     POSTGRES_PASSWORD: password
  web:
    build: .
    volumes:
      - .:/myapp
    # ports:
    #   - "3000:3000"
    # depends_on:
    #   - db
build 命令の解説

db サービスはここでは必要ありませんので、コメントアウトしました。
また、web サービス内で手順 2. で必要な設定は buildvolume のみです。

build は、先ほど作成した DockerfileGemfile を使ってイメージをビルドするために必要です。
build: . という指定は、
この web サービスのコンテナーのもととなるイメージとして、
docker-compose.yml があるディレクトリーに存在するファイルを
コンテキストとしてビルドしたイメージを使うことになります。

参考: build | Compose file version 3 reference | Docker Documentation

volumes 命令の解説

volumes は、この設定がないと、
手順 2. で rails new コマンドで作成する新規 Rails アプリケーションが、ホスト側のディレクトリーに残りません。

- .:/myapp という volumes の設定は
ホスト側の docker-compose.yml があるディレクトリーをコンテナー内のディレクトリツリーの /myapp に関連付けます。
こうすることで、手順 2. で実行する rails new コマンドで作成された新規 Rails アプリケーションが
ホスト側のディレクトリーに出力されるようになります。

このような volume の設定を bind といいます。

参考:
Use bind mounts | Docker Documentation
volumes | Compose file version 3 reference | Docker Documentation

まとめ

手順 2. のために必要なファイルだけを準備すると、次のようになります:

project/
+---docker-compose.yml
+---Dockerfile
+---Gemfile

Dockerfile の中で、手順 2. に不要なコードをコメントアウトしたので
Gemfile.lock は必要なくなりました。

Build the project

新規 Rails アプリケーションの作成

冒頭で実行する次のコマンドについて解説します:

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

このコマンドは間違っており、正しくは次のコマンドです:

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

上記のコマンドは docker-compose --no-deps run webrails new . --force --database=postgresql に分けて考えます。

docker-compose --no-deps run web

上記のコマンドは docker-compose.yml で定義した web サービスのコンテナーを起動して実行します。

docker run コマンドの書式は次の通りです:

docker-compose run [options] [-v VOLUME...] [-p PORT...] [-e KEY=VAL...] [-l KEY=VALUE...] SERVICE [COMMAND] [ARGS...]

--no-deps: Compose ファイル内の depends_on などで関係づけられたサービスを開始しません

つまり、--no-depsdocker-compose.yml 内の db サービスのコンテナーを起動しないための設定です。
(この時点ではデータベースは必要ないため)

docker-compose run コマンドは、
コンテナーを起動するためのイメージがまだビルドされていなければ、自動的にビルドを行います。

Dockerfile 内で、コンテナーを実行したときの CMD が定義されていますが、
このトレーニングのように docker-compose run コマンドでサービス名の後にコマンドを記述した場合は
CMD の内容は無視され、代わりにコマンドラインから入力した内容が実行されます。

rails new . --force --database=postgresql

このコマンドは新規 Rails アプリケーションを作成します。

rails new コマンドの書式は次の通りです:

rails new APP_PATH [options]

-f, [--force]: すでに存在するファイルを上書きします
-d, [--database=DATABASE] 選択したデータベース向けに初期設定します
選択肢: mysql / postgresql / sqlite3 / oracle / frontbase / ibm_db / sqlserver / jdbcmysql / jdbcsqlite3 / jdbcpostgresql / jdbc
省略時: sqlite3

このコマンドによって Gemfile を新規 Rails アプリケーション用の内容に上書きするので
--force オプションがない場合、Gemfile を上書きするかどうかの確認が表示されます。

イメージの再ビルド

手順 1. で、この解説記事に従って 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 "$@"

また、Dockerfile の一部コメントアウトしていた部分を、次のようにコメントインします:

Dockerfile
FROM ruby:2.5
RUN apt-get update -qq && apt-get install -y nodejs
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"]

# Start the main process.
CMD ["rails", "server", "-b", "0.0.0.0"]

まだコメントアウトしたままの命令がありますが、
これらは手順 4. を終えた後、Rails アプリケーションのイメージを作成するときに必要となる命令です。
手順 4. を終えた後で解説します。

次のコマンドを実行すると、イメージが再ビルドされます:

docker-compose build
Dockerfile の解説
ENTRYPOINT に Shell Script を設定し CMD でコマンドを指定する理由

Rails Server の二重起動を防ぐための排他ファイルである server.pid
何らかの原因で残ってしまった場合を想定して、
コンテナー起動時、Rails Server を起動する前に
entrypoint.sh 内で server.pid を削除する処理を実行しています。

ここで重要なのは、ENTRYPOINTCMD 命令を使った Dockerfile のデザインパターンで
イメージからコンテナーを起動したときの規定の動作を定義していることです。

ENTRYPOINTCMD を両方とも配列形式で与えると、すべての配列の要素を並べたコマンドが実行されます。

参考: Understand how CMD and ENTRYPOINT interact | Dockerfile reference | Docker Documentation

この例のように、ENTRYPOINT に Shell Script を設定し、
その Shell Script のコード内の最後で exec "$@" を呼び出す設計は
Dockerfile のデザインパターンとして広く使われています。
憶えておいて損はありません。

こうすることで、CMD で指定したコマンドを実行する前に何らかの初期化処理を実行することができるのです。

参考:

ここで出てきた命令一覧:

bundle install の前に Gemfile.lockCOPY する理由

このようにしないと、イメージを再ビルドしたときに、
Rails アプリケーションが開発時とは異なる動作や不具合を起こす可能性があります。
開発時とは異なるバージョンの Gem パッケージを使って動作することになる可能性があるためです。

Gemfile と Gemfile.lock は、どちらもインストールする Gem パッケージを管理するためのファイルですが、
それぞれ次のように扱います:

  • Gemfile
    • 開発者が目的に合わせて内容を編集します
    • プロジェクトが直接呼び出す Gem パッケージを定義します
    • 特別な理由がない限りバージョンを固定しません
  • Gemfile.lock
    • 開発者が直接編集せず、Gemfile の内容をもとにコマンドでコンパイルして更新します
    • プロジェクトが直接呼び出さない Gem パッケージも含めた、すべての必要な Gem パッケージが定義されます
    • すべてのパッケージがバージョンまで固定されています

bundle install コマンドを実行したとき、Gemfile.lock がなく、Gemfile のみが存在すると、
Gemfile で指定されている Gem パッケージの最新バージョンがインストールされます。

Gemfile.lock が存在し、Gemfile に特に更新がなければ
Gemfile.lock の内容に従って、開発時と同じバージョンの Gem パッケージがインストールされます。

参考:

nodejs をインストールする理由

Rails は JavaScript 実行環境である Node.JS を利用しており、
nodejs をインストールしていないと、Rails Server 実行時に次のようなエラーが発生します:

web_1  | /usr/local/bundle/gems/execjs-2.7.0/lib/execjs/runtimes.rb:58:in `autodetect': Could not find a JavaScript runtime. See https://github.com/rails/execjs for a list of available runtimes. (ExecJS::RuntimeUnavailable)
どうして再ビルドが必要なの?

rails new コマンドで上書きされた Gemfile には、
Rails 本体の他にも Rails アプリケーションを動作させるために必要な多くのパッケージが記載されています。

しかし、先ほどの docker-compose run コマンドでビルドされたイメージには、
Rails 本体しかインストールされていません。
これは、docker-compose run コマンドでビルドを行ったときの Gemfile には Rails しか記述されていなかったためです。

なお、ここで次のような記述があります:

This, and changes to the Gemfile or the Dockerfile, should be the only times you’ll need to rebuild.

この意味についてはすべての手順を終えた後で解説しますので、
学習効率を考慮して、ひとまず次の手順に進むことをお奨めします。

Connect the database

ここでは 2 つの設定を行っています:

  • Rails アプリケーションのデータベース接続設定を db サービスに接続できるように変更
  • db サービスに Rails アプリケーションが利用するデータベースを新規作成

Rails アプリケーションのデータベース接続設定を db サービスに接続できるように変更

手順 2. Build the project で生成された config/database.ymldefault: &default に次の要素を追加します:

config/database.yml
  host: db
  username: postgres
  password: password

この設定を行わずに docker-compose up を実行しても、ブラウザーでアクセスすると次のようなエラーが表示されます:

PG::ConnectionBad
could not connect to server: No such file or directory Is the server running locally and accepting connections on Unix domain socket "/var/run/postgresql/.s.PGSQL.5432"?

2020-08-06_13h03_31.png

このエラーメッセージは、Rails アプリケーションが
ローカルに PostgreSQL が起動している前提で接続しようとして失敗していることを示しています。

この場合、config/database.yml を更新してブラウザーを再読み込みするだけではエラーは解消しません。
これは、Rails がデータベース接続設定を読み込むのは Rails Server の起動時であるためです。
データベース接続設定を読み込み直すためには、
ターミナルで Ctrl + C を入力してコンテナーを停止し、docker-compose up を再度実行します。

db サービスに Rails アプリケーションが利用するデータベースを新規作成

docker-compose.yml があるディレクトリーでもう 1 つターミナルを開き、次のコマンドを実行します:

docker-compose run web rake db:create

この設定を行わずに docker-compose up を実行しても、ブラウザーでアクセスすると次のようなエラーが表示されます:

ActiveRecord::NoDatabaseError
FATAL: database "myapp_development" does not exist

2020-08-06_13h06_15.png

View the Rails welcome page!

次のような画面が表示されましたでしょうか?:

2020-08-06_14h41_27.png

この後の開発フローについて

Stop the application

この Quickstart では、アプリケーションを停止するときは、docker-compose down の利用が奨められています。
この記事に従って docker-compose.yml を修正した場合、
docker-compose down を実行すると、データベースの内容にアクセスできなくなります。

データベースの内容を保持したいときは、
docker-compose up を行ったターミナルで Ctrl + C を入力するか、
別のターミナルで docker-compose stop を実行すると、次に起動したときもデータベースの内容は保持されます。

基本的には、開発のためのデータを保持する必要がある場合は PC 内に残すのではなく、
factory_bot をはじめとする
データベースフィクスチャーを定義するための Gem パッケージを使ってコードに残します。
データの再利用の必要性を感じたら、コードに残す習慣をつけましょう。

参考: テストフィクスチャとは何? Weblio辞書

Rebuild the application

ここでは、イメージの再ビルドが必要な場合について述べられています。
ここで述べられている「イメージの再ビルドが必要な場合」とは、
「開発者が開発中に再ビルドを行うこと」を指しており、
「本番環境にデプロイするためのイメージをビルドすること」は指していません。

このことを理解するために、まずは「本番環境にデプロイするためのイメージをビルドすること」を考えてみます。

本番環境にデプロイするためのイメージのビルド

手順 1. Define the projectDockerfile コメントアウトしていた場合は、COPY . /myapp をコメントインします:

Dockerfile
FROM ruby:2.5
RUN apt-get update -qq && apt-get install -y nodejs
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"]

# Start the main process.
CMD ["rails", "server", "-b", "0.0.0.0"]

COPY . /myapp は Rails アプリケーションのコードをビルドしたイメージに含めます。
本番環境では GitHub などのバージョン管理リポジトリーからコードをデプロイするのではなく、
Docker Hub などの Docker レジストリーから事前にビルドしたイメージをデプロイすることを想定しています。

参考: Docker Registry | Docker Documentation

Gemfile, Gemfile.lock と一緒に一度に COPY してはいけないの?

Gemfile, Gemfile.lockCOPY を分けているのは、bundle install に時間がかかるためです。

Docker イメージのビルドは、命令毎に結果がキャッシュされるので、
変更の可能性が低い命令、実行に時間がかかる命令は
なるべく Dockerfile の上に記述した方が再ビルド時の効率が良くなります。

COPY . /myapp 以降の命令はそれほどビルドに時間がかかりません。
もし、/myappGemfile, Gemfile.lock と一緒に一度に COPY していた場合、
COPY . /myapp 以降の命令を再ビルドしたいときであっても、
Rails アプリケーションのコードを更新していると bundle install が実行されることになり
ビルドに時間がかかってしまいます。

開発者が開発中に再ビルド

Quickstart では、手順 2. Build the project
この Docker を使った Rails 開発のワークフローに関する重要なことが述べられていまました:

This, and changes to the Gemfile or the Dockerfile, should be the only times you’ll need to rebuild.

これは、日本語訳すると、大体次のような意味です:

このときと、Gemfile もしくは Dockerfile を変更したときだけが、再ビルドが必要なタイミングとなるはずです。

これは、docker-compose.ymlvolume/myapp をホストから bind マウントしているので
開発のためにコンテナーを起動したとき、イメージに含めた Rails アプリケーションのコードではなく
ホスト側の最新の Rails アプリケーションのコードが実行されるためです。

Q & A

Docker 公式ドキュメントの Quickstart に関する質問

rails new コマンドでファイルが生成されたログが出力されるのに、実際にファイルが生成されません

Windows 10 でバージョンが 1709 までだと、
Docker Compose で相対パスを指定したときに想定通りにマウントされない不具合があるようです。
Windows Update の適用をおすすめします。
Ruby on Rails - dockerでRuby on Railsコンテナ起動時、Could not find public_suffix-4.0.5 in any of the sources エラー|teratail

ディレクトリーの名前を myapp 以外に変更すると rails new コマンド実行時にエラーが発生します

正確にすべてのディレクトリー名を変更すればエラーは発生しません。
どこかで書き換えを誤っていないか確認しましょう。
Ruby on Rails - ローカルディレクトリにrails newをしてもファイルが存在しない|teratail

Docker と Rails による Web サービス実行に関する質問

エラーメッセージの意味が何を指しているのかわかりません

複数のサービスを起動するときはコンテキストとなるディレクトリーを分けて設計していますか?
コンテキストとなるディレクトリーを分けると、原因の範囲が狭まり調査しやすくなります。
Ruby on Rails - Docker + Railsの No such file or directory エラーの解決方法について|teratail

パッケージが足りないエラーが表示されるので gem install しても直りません

Docker が動いているコンピューターに gem をインストールしていませんか?
gem のインストールはコンテナ内で行う必要があります。
Ruby on Rails - docker-compose upがうまくいかない|teratail

db サービス以外のデータベースに接続したいです

Rails の database.yml と環境変数でデータベース接続情報を変更して起動します。
MySQL - [Docker, Rails, Nginx, MySQL, EC2]docker-compose run app rails db:createするとエラーが出る。|teratail

multi-stage builds にしたら bundle: not found でイメージがビルドできなくなりました

ベースイメージが Ruby のイメージではないステージで rubybundler を使おうとしていませんか?
Ruby on Rails - "docker-compose build"実行時のエラー "Service 'web' failed to build: The command '/bin/sh -c bundle instal"|teratail

Gemfile はあるのに bundle install すると Could not locate Gemfile というエラーメッセージが表示されます

コマンドは Docker が動いているコンピューターで実行しているか、
それともコンテナの中で実行しているのかを確認し、
そこに Gemfile があるかを確認しましょう
GitHub - Dockerを用いたローカル環境構築で、Could not locate Gemfileが表示される|teratail

Dockerで起動したサーバーにlocalhost:3000でアクセスすると「このページは動作していません。」と表示されます

Docker で起動した web サービスのログを確認して、ログが増えるかどうかを確認します。
増えなければ、ファイアウォールの問題などの可能性があります。
MySQL - dockerで起動したサーバーにlocalhost:3000でアクセスしようとするが「このページは動作していません。」と返ってくる|teratail

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

docker-composeでjenkins構築

目的

「Docker/Kubernetes実践コンテナ入門」を読んでいて、
手順通りでは進まない箇所があったのでエラーを出さず進めるために書きます。

修正箇所

コメントアウトで修正箇所と気になったとことをメモしています。

docker-compose.yml
version: "3"
services:
  master:
    container_name: master
    # jenkins:2.60.3からjenkins/jenkins:ltsに修正します。
    image: jenkins/jenkins:lts
    ports:
      - 8080:8080
    volumes:
      - ./jenkins_home:/var/jenkins_home
    links:
      - slave01

  slave01:
    container_name: slave01
    # slaveのイメージはこのままでOK。
    image: jenkinsci/ssh-slave
    environment:
    # id_rsa.pubのキーをコピーします。
      - JENKINS_SLAVE_SSH_PUBKEY=ssh-rsa AXXXXXXXX

気になって調べた所

  1. imageの取得元であるDockerHubのjenkins公式ページに行くと、使い方が書いてありました。

    To use the latest LTS: docker pull jenkins/jenkins:lts

  2. jenkinsci/ssh-slaveはそのままで使えました。
    slaveの方もこのままで良いのか疑心暗鬼になったので、調べたら
    DockerHubにページが有りました。

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

【Docker for Windows】コンテナ内で急にnpmコマンドが使えなくなった

環境

コンテナ内

Node.js npm
14.6.0 6.14.6

Docker desktop(Windows10)

image.png

困ったこと

WindowsのDocker環境でLaravel6+Vue.jsの開発をしている際、特にソースを変更していないにも関わらずnpm run devで以下のエラーが出るようになりました。

root@e11dd10f07d4:/var/www/html# npm run dev
npm ERR! code ENOSYS
npm ERR! syscall read
npm ERR! errno -38
npm ERR! ENOSYS: function not implemented, read

npm ERR! A complete log of this run can be found in:
npm ERR!     /root/.npm/_logs/2020-08-06T08_36_06_323Z-debug.log

コンテナを再起動しても事象は変わらず、npm installも同じエラーで実行不可です。

解決策

タスクトレイの?からRestart...を押下してDocker desktopを再起動すると、コンテナ内で上記エラーが発生しなくなりました。

参考

コンテナの中で身に覚えのないエラーが起きた場合はとりあえず再起動するのも手かもしれないです。

https://github.com/docker/for-win/issues/5955

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

【dokcer】EC2インスタンスでnginxとphp-fpmコンテナを連携させる

概要

EC2インスタンス上でdockerとdocker-composeをインストールして動かす方法メモ
OS: amazonLinux2

ゴール

EC2のEIPに直アクセス(LB通さない)してphpinfoを見られる

dockerインストール

インストール -> 起動 -> ec2-userでも動かせるように権限調整

$ sudo su -
# yum update -y
# amazon-linux-extras install -y docker
# systemctl enable docker
# systemctl start docker
# usermod -aG docker ec2-user

docker-composeインストール

docker-composeのバージョンは適時調整する
バージョン情報まで表示できたらOK!

# curl -L https://github.com/docker/compose/releases/download/1.24.1/docker-compose-Linux-x86_64 -o /usr/local/bin/docker-compose
# chmod 755 /usr/local/bin/docker-compose
# docker-compose -v
docker-compose version 1.24.1, build 4667896b

nginxコンテナを作ってアクセス確認

以下はec2-userで操作

$ mkdir project
$ cd project/
$ mkdir -p docker/nginx
$ vi docker/nginx/Dockerfile
$ vi docker-compose.yml
$ docker-compose up -d
EIPにアクセスして「welcome to nginx」が見れればOK!
docker/nginx/Dockerfile
FROM nginx:alpine
ENV LANG ja_JP.UTF-8
docker-compose.yml
version: "3"

services:
  nginx:
    build: ./docker/nginx
    ports:
      - 80:80

nginxコンテナに自前のindex.htmlを用意

$ vi docker/nginx/index.html
$ vi docker/nginx/Dockerfile
$ docker-compose --log-level CRITICAL down --rmi all --volumes
$ docker-compose up -d
EIPにアクセスして用意したhtmlファイルに変わっていればOK
docker/nginx/index.html
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
    body {
        width: 35em;
        margin: 0 auto;
        font-family: Tahoma, Verdana, Arial, sans-serif;
    }
</style>
</head>
<body>
<h1>Hogehoge</h1>
<p>Hogehoge</p>
</body>
</html>
docker/nginx/Dockerfile
FROM nginx:alpine
ENV LANG ja_JP.UTF-8

ADD ./index.html /usr/share/nginx/html/index.html

php-fpmコンテナを用意

nginxとの連携はまだ、とりあえずphp-fpmコンテナを立ち上げてphpが動かせることまで確認

$ mkdir -p docker/php-fpm
$ vi docker/php-fpm/Dockerfile
$ vi docker-compose.yml
$ docker-compose --log-level CRITICAL down --rmi all --volumes
$ docker-compose up -d
$ docker-compose exec php-fpm sh

※php-fpmコンテナで実行して、バージョン情報が表示されればOK
/var/www/html # php -v
PHP 7.1.33 (cli) (built: Oct 25 2019 07:25:49) ( NTS )
Copyright (c) 1997-2018 The PHP Group
Zend Engine v3.1.0, Copyright (c) 1998-2018 Zend Technologies
docker/php-fpm/Dockerfile
FROM php:7.1-fpm-alpine
ENV LANG ja_JP.UTF-8

nginxコンテナとphp-fpmコンテナを連携

$ vi index.php
$ cp index.php docker/nginx
$ cp index.php docker/php-fpm
$ vi docker/nginx/Dockerfile
$ vi docker/php-fpm/Dockerfile
$ vi docker/nginx/default.conf
$ vi docker-compose.yml
$ docker-compose --log-level CRITICAL down --rmi all --volumes
$ docker-compose up -d
EIPにアクセスしてphpinfoが表示されればOK!
index.php
<?php
echo 'hogehoge';
phpinfo();
docker/nginx/Dockerfile
FROM nginx:alpine
ENV LANG ja_JP.UTF-8

ADD ./index.php /project/index.php
ADD ./default.conf /etc/nginx/conf.d/default.conf
docker/php-fpm/Dockerfile
FROM php:7.1-fpm-alpine
ENV LANG ja_JP.UTF-8

ADD ./index.php /project/index.php
docker/nginx/default.conf
server {
    listen       80;
    server_name  _;

    root /project;
    index index.php;

    location / {
        try_files $uri $uri/ /index.php$is_args$args;
    }

    location ~ \.(php|xml)$ {
        fastcgi_pass  php-fpm:9000;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $document_root/$fastcgi_script_name;
        include       fastcgi_params;
    }
}
docker-compose.yml
version: "3"

services:
  nginx:
    build: ./docker/nginx
    ports:
      - 80:80
    depends_on:
      - php-fpm
  php-fpm:
    build: ./docker/php-fpm
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Dockerで最速Django、MariaDB、Gunicornの開発環境構築

docker-composeを用いて、DJangoの開発環境を整える、docker-compose.ymlを自分で作成しました。

https://github.com/yuta-hidaka/Docker-Django-MariaDB-Gunicorn

こちらを使って、Djangoの開発環境を整えます。

目次

1.レポジトリのクローン
2.設定ファイル(.env)/Requirements.txtのコピー
3.イメージのbuild
3.localhostにアクセス
4.settings.pyを編集
5.ご意見募集中!!!
5.おわりに

1. レポジトリのクローン

git clone https://github.com/yuta-hidaka/Docker-Django-MariDB-Gunicorn.git/ hogehoge

2. 設定ファイル(.env)/Requirements.txtのコピー

.envで必要な情報を書き換えてください
requirements.txtで必要なmoduleを調整してください

cd hogehoge
cp .env.sample .env
cp django_data/requirements.txt.sample django_data/requirements.txt

3. イメージのbuild

cd hogehoge
docker-compose up --build

4. localhostにアクセス

表示されていることを確認
image.png

5. settings.pyを編集

接続先をMariaDBに変更し、Djnagoの初期設定に従って進んでください。

6. ご意見募集中!!!

Docker初心者が作ったファイルのため、改善意見や、これはダメなんじゃない?
といった意見をお待ちしています!!

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

コンテナレジストリ上にPREN(PostgreSQL、ReactJS、Express、NodeJS)アプリのCI/CDを設定する

このガイドでは、Alibaba Cloudのコンテナレジストリを使用してPREN(PostgreSQL, ReactJS, Express, NodeJS)アプリケーション用のCICDパイプラインをセットアップします。

本ブログは英語版からの翻訳です。オリジナルはこちらからご確認いただけます。一部機械翻訳を使用しております。翻訳の間違いがありましたら、ご指摘いただけると幸いです。

Alibaba Cloud Tech Share執筆者 テミダヨ・オイェデレ(Temidayo Oyedele)著 Tech Shareは、技術的な知識やベストプラクティスをクラウドコミュニティ内で共有することを奨励するAlibaba Cloudのインセンティブプログラムです。

Jenkinsは、自己完結型のオープンソース自動化サーバであり、ソフトウェアの構築、テスト、配信、デプロイに関連するあらゆる種類のタスクを自動化するために使用することができます。

Jenkinsは、ネイティブシステムパッケージ、Dockerを通じてインストールすることができ、Javaランタイム環境(JRE)がインストールされているマシンであれば、スタンドアロンでも実行することができます。

Dockerは、開発者やシステム管理者がコンテナを使ってアプリケーションを開発、デプロイ、実行するためのプラットフォームです。Linuxのコンテナを使ってアプリケーションをデプロイすることをコンテナ化と呼びます。コンテナは新しいものではありませんが、アプリケーションを簡単にデプロイするための使用は新しいものです。

このガイドでは、PREN (PostgreSQL, ReactJS, Express, NodeJS) アプリケーションのための継続的インテグレーションとデプロイのパイプラインを設定します。Jenkinsを使用してGitHub上のコード変更を検出し、継続的なデリバリを行い、PRENアプリはDockerを使用してコンテナ化し、Alibaba Cloud Container Registryにプッシュします。今回使用するPRENアプリはこちらのGitHubでホストされています。

前提条件と要件

1、アリババクラウドのアカウント
2、新たにデプロイされたAlibaba Cloud Ubuntu 16.04 64ビットサーバーインスタンス
3、最低2GBのRAM(Docker化されたアプリサイズのため2GBを強く推奨
4、最低20GBのディスク容量(ただし40GBを推奨)
5、インスタンス・セキュリティ・グループはポート8080での侵入を許可しなければなりません。
6、Alibaba Cloud Elastic Container Registryのリポジトリ

ステップ1:Jenkinsの設定

Alibabaクラウドのエラスティックコンピュートサービス上にJenkinsサーバをセットアップします。Alibaba Cloud ECSコンソールにログインします。インスタンスが起動していない場合は、こちらのリンクをクリックしてAlibaba Cloudのコンピュートサービスを起動させます。

JenkinsをインストールするにはJava実行環境(JRE)が必要なので、まずはそれをインストールします。

Javaプログラムのコンパイルなどが必要になることもあるので、Java実行環境も含まれているJDK(Java開発キット)をインストールするのが良いと思います。唯一の欠点はサイズが大きくなることです。

SSHキーペアを使用してローカルターミナルからECSインスタンスにSSHするか、設定や好みの方法に応じてVNCコンソール経由でログインし、以下の手順でJenkinsをインストールします。

Oracle PPAを追加し、以下のコマンドを実行してパッケージインデックスを更新します。

sudo apt-get update
sudo apt-get install software-properties-common
sudo add-apt-repository ppa:webupd8team/java 
sudo apt-get update

あとでadd-apt-repositoryを使うことができるように、ソフトウェアのプロパティの共通部分をインストールする必要があります。最後に、JDK 8を実行してインストールします。

 sudo apt-get install oracle-java8-installer

注意:Jenkinsは特にJavaのバージョン8がインストールされている必要があります。

以下のコマンドでリポジトリキーをシステムに追加します。

wget -q -O - https://pkg.jenkins.io/debian/jenkins-ci.org.key | sudo apt-key add -

次に、Debian パッケージのリポジトリのアドレスをサーバの sources.list に追加します。

echo deb https://pkg.jenkins.io/debian-stable binary/ | sudo tee /etc/apt/sources.list.d/jenkins.list

この両方が揃ったら、apt-getが新しいリポジトリを使うようにupdateを実行します。

sudo apt-get update

最後にJenkinsとその依存関係をインストールします。

sudo apt-get install jenkins

serviceを使って、Jenkinsを起動します。

sudo service jenkins start

Jenkinsのステータスは以下のコマンドで確認できます。

sudo service jenkins status

出力の中にJenkinsのオートメーションサーバのようなものが実行されているのがわかるはずです。

image.png

次に、Jenkinsのウェブコンソールからインストールの設定を行います。http://internet_ip_address_of_your_instance:8080 にアクセスして Jenkins のウェブコンソールに移動します。ポート8080はJenkinsのデフォルトポートです。

すると「Unlock Jenkins」というページが表示されるはずですが、そこには初期パスワードの場所が赤文字で表示されています。これに似たようなものが表示されます。

image.png

VNCターミナルまたはECSインスタンスにログインしたローカルターミナルウィンドウに移動して、パスワードを表示するためにcatコマンドを使用します。

sudo cat /var/lib/jenkins/secrets/initialAdminPassword

ターミナルから出力された32文字の英数字のパスワードをコピーして、Jenkinsコンソールの「Administrator password」欄に貼り付け、「Continue」をクリックします。次の画面では、提案されたプラグインをインストールするか、特定のプラグインを選択するかのオプションが表示されます。ここでは、提案されたプラグインをインストールするオプションを選択することにします。

image.png

インストールが完了すると、最初の管理ユーザーを設定するように促されます。このステップをスキップして、上で使用した初期パスワードを使用して管理者として続行することも可能ですが、ユーザーを作成するために少し時間がかかります。

覚えておく必要がある詳細を記入し、保存して次に進みます。

image.png

この段階では、このようなページが表示されるはずです。

image.png

Save and Finishをクリックし、次のページでStart using Jenkinsをクリックします。ダッシュボードにリダイレクトされます。この段階でインストールは完了です。

ステップ2:Dockerのセットアップ

まだVNCまたはあなたのターミナル/コマンドプロンプトからECSインスタンス上で。有効なdockerダウンロードを確実に取得するために、公式DockerリポジトリのGPGキーをシステムに追加します。

curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -

APTソースにDockerリポジトリを追加します。

sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" 

次に、新しく追加したリポジトリのDockerパッケージでパッケージデータベースを更新します。

sudo apt-get update

最後に、このコマンドでDockerをインストールします。

sudo apt-get install -y docker-ce

これでDockerがインストールされ、デーモンが起動し、起動時にプロセスが起動するようになったはずです。を使って実行していることを確認してください。

sudo service docker status

ステップ3: JenkinsでGitHubのWebhookを設定する

WebブラウザからJenkinsダッシュボード「http://ECS_URL:8080」
にアクセスし、新規項目をクリックして新規Jenkinsジョブを作成します。適切な名前を入力し、フリースタイルプロジェクトを選択し、OKをクリックして次のページに移動します。

image.png

次のページでは、ビルドトリガーの下で、トリガーのビルドをリモートで行うボックスにチェックを入れてください(例:スクリプトから)、認証トークンを提供する必要があります。自分にとっては覚えやすく、他の人にとってはかなり難しいであろう、任意のトークンを記入してください(パスワードのようなもの)。このトークンは、Jenkinsサーバーへのプッシュイベント(ここではGitHubから)を行うために必要です。入力欄の下にあるURLに注目してください。ビルドURLと呼ぶことにしますが、これは後ほどGitHubのwebhookに必要になります。

image.png

ビルドセクションでは、ドロップダウンから[ビルドステップの追加]と[シェルの実行]を選択します。テキストエリアに以下のコマンドを貼り付けます。

set +x
if [ -d HelloBooks-Alibaba ]; then rm -rf HelloBooks-Alibaba; fi
git clone https://github.com/babadee001/HelloBooks-Alibaba 
cd HelloBooks-Alibaba 
sudo docker build -t hellobooks . 
sudo docker login --username=yourusername registry-intl.yourregion.aliyuncs.com -p $password
sudo docker tag [ImageId] registry-intl.yourregion.aliyuncs.com/yournamespace/yourrepositoryname:[tag]
sudo docker push registry-intl.yourregion.aliyuncs.com/yournamespace/yourrepositoryname:[tag]

私が使用しているサンプルプロジェクトでこのガイドに従っている場合、最初の5つのコマンドはそのままにしておいて、それ以外の場合は適宜修正してください。次の3つのコマンドは、Alibaba ECR (Elastic Container Registry) リポジトリから直接取得したものです。これについては、ECRを設定する次のステップ(ステップ4)で詳しく説明します。

Set +xは、コマンドとその引数が印刷されたり、ターミナルに返されたりしないことを確認します。次の行では、プロジェクトフォルダが存在するかどうかをチェックし、真であれば削除します。これにより、常に更新されたレポフォルダがあることを確認します。次に、レポをクローンして、プロジェクトフォルダかディレクトリにcd(ディレクトリ変更)します。その後、イメージをビルドするためのdockerコマンドを実行します。

最後に、Jenkinsジョブを保存する前に、環境変数を追加する必要があります。Build Environmentの下のUse secret text(s) or file(s)ボックスにチェックを入れます。新しいセクションがそのすぐ下に表示されます(Bindings)。変数フィールドに秘密のテキストの名前を入力します。Add」ドロップダウンをクリックし、「secret text」を選択します。追加]をクリックし、[Jenkins]をクリックします。kindをsecret textに変更し、ECR Dockerのログインパスワードを貼り付けます。IDフィールドを記入します(この名前は、後でシークレットファイルを参照するために使用するものになるので注意してください。保存します。

image.png

GitHub、Gitlab、Bitbucket などのような多くのソースコードホスティングサービスは、新しいコミットがリポジトリにプッシュされたときにウェブフックを送信する機能を持っています。ウェブフックは通常、JSONペイロードを含むHTTP POSTリクエストです。

デフォルトでは、Jenkins 2.x以降では「Prevent Cross Site Request Forgery exploits」というセキュリティ機能が有効になっており、有効な「crumb」トークンが関連付けられていないすべてのPOSTリクエストを拒否します。これは素晴らしいセキュリティ機能です。

ほとんどの Git ホスティングサービスには crumb を取得したり処理したりする仕組みがないので、デフォルトの設定で Jenkins をインストールした場合には、POST リクエストがブロックされてしまいます。"Prevent Cross Site Request Forgery exploits" を手動で無効にするか、サービス固有のプラグインをインストールする必要があります。

Bitbucket や GitHub のような多くの一般的なホスト用に作られたプラグインで、Crumb Exclusions を提供しています。これらのプラグインは CSRF が有効になっていることを確認し、プッシュ通知を行うことができますが、このチュートリアルの目的のために、サードパーティ製のプラグインをインストールするのではなく、手動で CSRF を無効にします。

JenkinsダッシュボードでCSRFを無効にするには、Jenkinsダッシュボードからmanage Jenkinsをクリックし、グローバルセキュリティを設定します。Authorizationの下で上にスクロールし、Logged-in users can do anythingを選択し、Allow anonymous read accessボックスにチェックを入れます。続けて上にスクロールすると、CSRF保護が表示されるはずなので、チェックを外します。保存して、これで完了です。

最後に、イメージをコンテナレジストリに送信するためのdockerコマンドにはsudoer権限が必要なので、Jenkinsユーザにsudo権限を与える必要があります。これは、以下のコマンドを実行して sudoers ファイルを修正することで行うことができます。

visudo -f /etc/sudoers

そして、ファイルの最後に以下の行を追加して保存します。

jenkins ALL= NOPASSWD: ALL

GitHub のリポジトリに移動し、左のナビゲーションペインから設定タブをクリックして webhooks をクリックします。

「add webhook」をクリックして Jenkins の webhook を作成します。ペイロードのURLは、ステップ3のJenkinsビルドのURLになります。Just the pushイベントボックスにチェックを入れます。このチュートリアルの目的のために、私たちはちょうどそれを行います。Let me select individual events boxを選択することで、プロジェクトに合ったイベントを探索して設定することができます。次に、作成するWebhookを追加をクリックします。これで、このリポジトリに変更(プッシュ)があったときに、Jenkinsジョブがビルドできるようになります。

image.png

ステップ4:コンテナレジストリの設定

ECR ダッシュボードに移動し、管理をクリックします。リポジトリセクションのプッシュイメージまでスクロールします。以下のようなものが表示されるはずです。

sudo docker login --username=yourusername registry-intl.yourregion.aliyuncs.com
sudo docker tag [ImageId] registry-intl.yourregion.aliyuncs.com/yournamespace/yourrepositoryname:[tag]
sudo docker push registry-intl.yourregion.aliyuncs.com/yournamespace/yourrepositoryname:[tag]

先ほどのJenkinsジョブでビルドステップに追加したものです。

この段階では、アプリケーションに簡単な変更を加えてGitHubにプッシュすることで、全体のセットアップをテストすることができます。GitHubは変更内容をJenkinsに通知し、JenkinsジョブはdockerイメージをビルドしてECRリポジトリに送信します。これはECRリポジトリのダッシュボードで確認できます。管理をクリックしてからタグをクリックします。

image.png

結論

これで、すべての設定と実行が完了しました。

ECRにログインしてイメージを取り出し、アプリケーションを起動すると、8000番ポートで動作します。また、KubernetesやSwarmのようなコンテナクラスタにデプロイすることもできます。

アリババクラウドは日本に2つのデータセンターを有し、世界で60を超えるアベラビリティーゾーンを有するアジア太平洋地域No.1(2019ガートナー)のクラウドインフラ事業者です。
アリババクラウドの詳細は、こちらからご覧ください。
アリババクラウドジャパン公式ページ

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

AlibabaクラウドにJenkins Slave Poolを実装してCI/CDパイプラインのプロビジョニングを可能にする方法

この記事では、動的で並列なCI/CDパイプラインのプロビジョニングを可能にするAlibaba Cloud上に分散型でDocker化されたJenkins Slave Poolを実装する方法を共有しています。

本ブログは英語版からの翻訳です。オリジナルはこちらからご確認いただけます。一部機械翻訳を使用しております。翻訳の間違いがありましたら、ご指摘いただけると幸いです。

ハイブリッドCI/CDとは

ハイブリッドCI/CDは、オンプレミスとパブリッククラウドを組み合わせた分散型のプラットフォームで、CI/CDを目的としています。ハイブリッドCI/CDは、基本的にプライベートクラウドとパブリッククラウドの間でワークロードを制御し、ニーズとコストのバランスを取ります。

高レベルでは、以下の図のようなアーキテクチャになっています。

image.png

ハイブリッドCI/CDのメリット

スケーラビリティと柔軟性

オンプレミスでも一定レベルのスケーラビリティは提供されていますが、これはデータセンターごとのものであり、予算に大きく依存します。また、あるシステムが予想以上にリソースを消費すると、他のすべてのシステムに影響が出ることがよくあります。そのため、予算の境界線をどのように設定するかが厄介な問題となります。パブリッククラウドサービスでは、より大きなクラウドインフラでより大きなスケーラビリティを提供することができます。CI/CDのジョブは通常、分離された短命のものであり、一般的にステートレスであることを考慮すると、パブリッククラウドサービスの方が拡張性が高く、CI/CDジョブをパブリック・クラウドに移行することで、組織はコア・サービスのためにスケーラビリティを確保することができます。

費用対効果

パブリッククラウドの方が、集中管理のコストをすべてのユーザーが共有するため、経済的な費用対効果が高くなる可能性が高くなります。

セキュリティ

純粋なパブリッククラウドソリューションと比較して、Hybrid CI/CDは、適用可能なCI/CDジョブにのみアクセスできるようにデータを常に制限することができるため、セキュリティ要求に対する感受性を確保しています。

Dockerがビルドスレーブとしてホストする理由

リソースの利用

Dockerがリソースを動的に割り当ててくれるので、複数のJenkins Slavesを並行して走らせることができます。これにより、CI/CDパイプラインのスループットが飛躍的に向上します。

アイソレーション

Dockerは、アプリケーションとリソースが分離・分離されていることを保証します。Jenkins Slaveが本当に徹底的にクリーンアップするかどうかを気にすることなく、ターゲットコンテナを破棄するだけでよいのです。

環境の標準化

標準イメージを継承したCI/CD環境を簡単に構築できます。

Jenkinsスレーブコンテナの実装

Jenkins MasterからJenkins Slavesへの通信プロトコルは2種類あります。Java Web Start(別名JNLP)とSSHです。SSH は Blocking I/O で暗号化されているため、スケーラビリティが制限されますが、ロードバランサへの依存度が低いため、最も一般的な方法です。そこで今回はsshスレーブの種類についてのみ解説します。

Jenkikikns Slave の docker イメージはプロジェクトによって環境が異なる場合があるので、具体的にビルドすることをお勧めします。https://github.com/jenkinsci/docker-ssh-slave に素晴らしい例が掲載されていますので、参考にしてみてください。

Dockerfile
    FROM openjdk:8-jdk
    LABEL MAINTAINER="Nicolas De Loof <nicolas.deloof@gmail.com>"

    ARG user=jenkins
    ARG group=jenkins
    ARG uid=1000
    ARG gid=1000
    ARG JENKINS_AGENT_HOME=/home/${user}

    ENV JENKINS_AGENT_HOME ${JENKINS_AGENT_HOME}

    RUN groupadd -g ${gid} ${group} \
        && useradd -d "${JENKINS_AGENT_HOME}" -u "${uid}" -g "${gid}" -m -s /bin/bash "${user}"

    # setup SSH server
    RUN apt-get update \
        && apt-get install --no-install-recommends -y openssh-server \
        && rm -rf /var/lib/apt/lists/*
    RUN sed -i /etc/ssh/sshd_config \
            -e 's/#PermitRootLogin.*/PermitRootLogin no/' \
            -e 's/#RSAAuthentication.*/RSAAuthentication yes/'  \
            -e 's/#PasswordAuthentication.*/PasswordAuthentication no/' \
            -e 's/#SyslogFacility.*/SyslogFacility AUTH/' \
            -e 's/#LogLevel.*/LogLevel INFO/' && \
        mkdir /var/run/sshd

    VOLUME "${JENKINS_AGENT_HOME}" "/tmp" "/run" "/var/run"
    WORKDIR "${JENKINS_AGENT_HOME}"

    COPY entrypoint.sh /usr/local/bin/entrypoint.sh

    EXPOSE 22

    ENTRYPOINT ["entrypoint.sh"]

Jenkins Slave entrypoint.sh

    #!/bin/bash -ex

    write_key() {
        mkdir -p "${JENKINS_AGENT_HOME}/.ssh"
        echo "$1" > "${JENKINS_AGENT_HOME}/.ssh/authorized_keys"
        chown -Rf jenkins:jenkins "${JENKINS_AGENT_HOME}/.ssh"
        chmod 0700 -R "${JENKINS_AGENT_HOME}/.ssh"
    }

    if [[ $JENKINS_SLAVE_SSH_PUBKEY == ssh-* ]]; then
    write_key "${JENKINS_SLAVE_SSH_PUBKEY}"
    fi
    if [[ $# -gt 0 ]]; then
    if [[ $1 == ssh-* ]]; then
        write_key "$1"
        shift 1
    else
        exec "$@"
    fi
    fi

    # ensure variables passed to docker container are also exposed to ssh sessions
    env | grep _ >> /etc/environment

    ssh-keygen -A
    exec /usr/sbin/sshd -D -e "${@}"

Jenkinsマスタ設定

すでにJenkins Masterを設定したことがある方を想定しています。Alibaba Cloud上で新規に構築する場合は、以下の三部作の記事も参考にしてください。

その後、Jenkins Dashboard / Manage Jenkins / Manage Pluginsに移動し、"Docker Plugin "を検索してインストールします。

次に、Jenkins Dashboard / Manage Jenkins / Configure systemに移動し、dockerの下のDocker URLにECSのパブリックIPアドレスとdockerリモートAPIのポートを記入します。デフォルトでは2375です。test connection "ボタンがあるので、接続が成功するかどうか試してみることができます。

その後、「Add Docker Template」を選択し、「docker template」をクリックします。そして、以下のように詳細を記入します。

  • Dockerイメージ: jenkinsci/ssh-slave
  • リモートファイリングシステムのルート:/home/jenkins
  • ラベル: ssh-slave
  • 証明書: the public key you have injected for ssh-slave container

すべての設定が完了したら、「保存」ボタンをクリックします。

テスト構成

「New item」に移動し、「ssh-slave-test」という名前のフリースタイルプロジェクトを作成します。

「Restrict」の下に、スレーブテンプレートで与えたラベル名を入力します。ここでは「ssh-slave」とします。

Build で execute shell オプションを選択し、以下のように echo 文を入力します。

    echo "hello {JOB_NAME}"

このジョブを保存して「今すぐビルド」をクリックすると、以下のような出力が得られるはずです。

    hello ssh-slave-test

次やることは?

上記のすべてが終わったら、コードリポジトリと統合して、実世界のCI/CDジョブを作成します。また、実際の要件に応じてdockerイメージをカスタマイズすることもできます。

アリババクラウドは日本に2つのデータセンターを有し、世界で60を超えるアベラビリティーゾーンを有するアジア太平洋地域No.1(2019ガートナー)のクラウドインフラ事業者です。
アリババクラウドの詳細は、こちらからご覧ください。
アリババクラウドジャパン公式ページ

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

docker runに失敗したコンテナにログインして、デバッグする方法

docker runが失敗したコンテナの中身を確認したい場合、docker commitで失敗したコンテナをイメージ化して、ログインします。

docker commit <コンテナID or コンテナ名> debug && docker run --rm -it debug sh
  • docker runに失敗したコンテナのコンテナIDは、docker ps -aで調べられます。
  • "debug" がコンテナイメージ名となります。不要になったら、docker rmi debugで消しましょう。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Docker Vue Go

Docker

Dockerマウントできないよ〜

ERROR: for nginx  Cannot start service nginx: OCI runtime create failed: container_linux.go:349: starting container process caused "process_linux.go:449: container init caused \"rootfs_linux.go:58: mounting \\\"/Users/ishidashogo/practice/goPractice/etc/nginx/nginx.conf\\\" to rootfs 
\\\"/var/lib/docker/overlay2/ac829670cc63c6bd9749802860c0b6dd9619d56f1883525650acc81db4dc4ee1/merged\\\" at \\\"/var/lib/docker/overlay2/ac829670cc63c6bd9749802860c0b6dd9619d56f1883525650acc81db4dc4ee1/merged/etc/nginx/nginx.conf\\\" caused \\\"not a directory\\\"\"": unknown: Are you trying to mount a directory onto a file (or vice-versa)? Check if the specified host path exists and is the expected type

マウントのディレクトリが違うみたいでした。
docker-compose.ymlファイル内で指定しているvolumeの場所に指定してある
欲しいファイルがないことによるエラーみたいでした。

https://qiita.com/hanakok/items/6bfcfbb8b7b4df877178

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

【ElasticSearch Operator編】Kubernetes Operatorコードリーディング

ElasticSearch Operatorとは

ElasticSearch Operatorとは、ElasticSearch向けのKubernetes Operatorであり、OpenshiftのClusterLoggingの機能の一部として動作するOperatorである。

ElasticSearchを自動運用するためのOperationがこのOperatorとして実装されている。

openshift/elasticsearch-operatorというGithubリポジトリのためOpenshiftコミュニティによりメンテされているようである。

openshift/elasticsearch-operator

【補足】elasticsearch-operatorのコード量

Goのファイルが2500強あり、コードが1000k lineにも及ぶ

❯ cloc .
    3297 text files.
    3170 unique files.                                          
     367 files ignored.

github.com/AlDanial/cloc v 1.80  T=10.38 s (283.8 files/s, 117872.8 lines/s)
--------------------------------------------------------------------------------
Language                      files          blank        comment           code
--------------------------------------------------------------------------------
Go                             2539          81218         112652         974429
~
--------------------------------------------------------------------------------
SUM:                           2945          89341         127274        1006349
--------------------------------------------------------------------------------

vendorディレクトリ内で同様に見てみると、950 k lineはvendor関連コードという風に取れるため、実質20 k lineくらいがelasticsearch-operatorのコードといえそう。

❯ cloc .
    3070 text files.
    2948 unique files.                                          
     343 files ignored.

github.com/AlDanial/cloc v 1.80  T=7.37 s (371.4 files/s, 161408.3 lines/s)
--------------------------------------------------------------------------------
Language                      files          blank        comment           code
--------------------------------------------------------------------------------
Go                             2411          77974         111744         956992
~
--------------------------------------------------------------------------------
SUM:                           2738          85146         124716         980068
--------------------------------------------------------------------------------

ElasticSearchのReconcile処理詳解

Reconcile処理概要

  • elasticsearchのNode(Elasticsearchクラスタのメンバ)ごとに、正常性確認を行う

  • Elasticsearch nodeごとにdeploymentを持っていて、そのdeploymentごとにreconcileを行う

  • deployment内のpodステータスを確認する際に、コンテナのステータスも確認しreconcileを行っている。

Reconcile処理に関連するコードの呼び出しフロー

主な処理の流れとしては

  • controller/erlasticsearch packageのadd()関数
  • controller packageのreconcileHandler経由でReconcile()関数が呼ばれ
  • さらにk8shandler内のReconcile()が呼ばれ
  • KubernetesのDeployment, Pod, ContainerレベルのReconcileが行われる

となっている。(図1 下記の黒矢印部分)

Untitled.png
図1. Reconcile処理に関連するコードの呼び出しフロー図

上記のコールグラフは下記のツールを使用して描画した。インタラクティブに処理を追えるが処理が重いため上記1枚に留めた。

https://github.com/ofabry/go-callvis

処理の入り口 elasticsearch package内のadd()により、Controllerを登録

add()のコード(図2)をみると、reconcilerを与えることで新しくcontrollerを追加する、という関数である。

ここでElasticSearchをReconcileするためのcontrollerが生成される。

carbon-2.png
図2. add()の定義

このadd()によって追加されたcontrollerのReconcileが、いわゆるReconcilation Loopと呼ばれる、KubernetesならびにKubernetes Operatorの主な役割である。

Reconcillation Loop

Reconcile()の定義は以下に記載されている。

openshift/elasticsearch-operator

図1で示した通り、Reconcile()からk8shandler package内のReconcile()を呼び出している。(図3)

carbon_(2).png
図3. Reconcile()の実装の一部抜粋

このk8shandler.Reconcile()では、主にKubernetesリソースレベルでのReconcileを行う。

具体的には、

  • ServiceAccount
  • RBAC
  • ConfigMaps
  • Prometheus向けのServiceMonitor, PrometheusRule

などがある。

また、これに加えて

  • Elasticsearch Cluster
  • IndexManagement

についてのReconcile処理が定義されている。

これらについて、CreateOrUpdateHoge()という関数が用意されており、該当のリソースがなければ作り、理想状態じゃなければ更新をかける処理(Reconcilation Loop)が実装されていることになる。

CreateOrUpdateHoge郡の該当のコードは下記である。

openshift/elasticsearch-operator

ここでは、メインのReconcile処理であるCreateOrUpdateElasticsearchCluster()についてさらにDeep Diveすることにする。

ElasticSearchのReconcile処理であるCreateOrUpdateElasticsearchCluster()の実装確認

ここまで説明した通り、CreateOrUpdateElasticsearchCluster()とはElasticSearchを理想状態に保つための関数である。

実際にはDeploymentとして動作するためElasticSearch Deploymentを理想状態にすることをReconcileといっても大きな齟齬はないと思われる。
carbon-3.png

具体的な処理が気になる場合は、下記の処理から追うことができる。

openshift/elasticsearch-operator

ここでは、ElasticSearchに不具合が起きた場合にどのようにPodに対してReconcileを行うのかについて注目する。

k8shandler package内のcluster.goにて、ScheduledForUpgradeを監視することで、更新が必要なノード(elasticsearchクラスタのメンバの意でありk8s nodeではない)をupgradeNodesとして記憶している。(ScheduledForUpgradeの更新のされ方は後述して補足する)

openshift/elasticsearch-operator

上記でupgradeNodeとして認識されたNodeはk8shandler.UpgradeStatus()にて、Reconcile処理が適用される。

Reconcile処理内における、具体的な実装を確認

UpdateClusterStatus() 内にて、status.go内のupdateNodeConditions()を呼ぶことで、いよいよelasticsearch Nodeごとのreconcile処理に入る

openshift/elasticsearch-operator

Podの正常性を確認

Podがスケジュールされているかを確認

「Podがscheduleされていつつ、podのConditionがFalse」となっている場合に、podをunschedulableとしてフラグ付する。

unschedulableフラグがたった場合は後続処理である、Pod内のコンテナステータスを確認していく。

該当コードは下記

openshift/elasticsearch-operator

Pod内コンテナのステータスを確認

Pod内のelasticsearchコンテナの、StateがWaitingかTerminatedかをまず確認する

この場合は、updatePodNotReadyCondition()を呼ぶことで、Podが壊れている正確なReasonとMessageを付加している。

もう1つのproxyコンテナについても同様に確認をしている。

openshift/elasticsearch-operator

ディスク使用率観点での正常性確認

コンテナの正常性確認が終わるとディスク使用量観点の確認を行う

ディスク使用量が閾値を超えているかどうかを確認し、Disk Watermark Highもしくは、Disk Watermark Lowなどに引っかかっているかを確認する。

openshift/elasticsearch-operator

まとめ

  • Openshiftで用いられるElastichSearch Operatorの役割について、PodのReconcile部分に焦点を当てて実装を確認した。

  • 実際のReconcile部分に限っていうとElasticsearch特有という部分は余り目立たず、他のOperatorのコードを読む上でも同様に読み進めるための材料として読むことは有用と感じる。

  • Operatorhub.ioにおいてelasticsearch-operatorは公開されておらず、"Elastic Cloud on Kubernetes" Operatorが公開されているため、こちらとの関連やその差分などについても考察の余地があるが、OperatorHub.ioに登録されている方がよりデファクトであると現時点では考えられる。そのため、本記事で紹介したelasticsearch-operatorはOpenshiftに特化した内容になっていると考えるのが妥当である。

https://operatorhub.io/operator/elastic-cloud-eck

参考資料

  • Elasticsearch OperatorのGoDoc

elasticsearch-operator - GoDoc

  • openshift/elasticsearch-operator Github

https://github.com/openshift/elasticsearch-operator

  • openshiftのcluster-loggingのリポジトリ

https://github.com/openshift/cluster-logging-operator

【補足】ScheduledForUpgradeの更新のされ方

まずk8shandler package内のdeployment.goでdeploymentの状態を監視している

openshift/elasticsearch-operator

node.isChanged()にて、純粋にdeploymentのdesiredとcurrentを比較することで、更新が必要かを判断している。

node.isChanged()がtrueとなった場合に、api.ElasticsearchNodeStatus.UpgradeStatus.ScheduledForUpgradeというフラグを立てることで、reconcilationLoop内で再起動処理をフックする仕組みになっている。

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

DockerでRailsチュートリアルのローカル開発環境構築(Rails 6 + PostgreSQL + Webpack)

はじめに

最新(2020.8.5現在)のRailsチュートリアルではRails 6が使用されており、
これに対応した開発環境構築をDockerでやってみたいと思います。

個人開発アプリの開発環境構築の際に、
私は新しいものが好きだから...とRails 6を導入しました。

ところが...このRails 6からJavaScriptのモジュールバンドラーにWebpackが導入されたことにより、
BootstrapやFontawesomeといったツールの導入、管理方法が変わるだけでなく、
そもそも環境構築の際もRails 5では必要ない手順が必要だったりと、
Rails 6とWebpackの壁に盛大にぶつかることとなりました...

せっかくなので、今回の試みを経て、知識、経験の整理定着を図りたいと思います。

また、実際にRailsチュートリアルを脱線しながら再走し、学びを深められたらと思っています。

個人開発アプリ
mdClip <オンラインmarkdownエディタ>

以前Rails 5の環境構築をDockerでやってみた記事
はじめてのDockerでRails開発環境構築 [NGINX + Rails 5 (puma) + PostgreSQL] - Qiita

環境

  • Mac OS (Mojave)
  • Docker for Mac
  • Ruby 2.6.3-alpine
  • Rails 6.0.3
  • PostgreSQL 11.0-alpine

構成ファイル

以下の5つのファイルをワークスペースに用意します
Dockerfile, docker-compose.yml, Gemfile, Gemfile.lock, entrypoint.sh

https://github.com/naokit1030/sample_app_on_docker.git

git clone -b create-docker-files https://github.com/dev-naokit/sample_app_on_docker.git

Dockerfile

  • イメージの軽量化のため"alpine"を使用

  • nodejs, yarnはWebpack導入に必要

  • postgresqlだけでは駄目で、postgresql-devも必要

  • bundle installの-j4オプションでbundle installが高速化される

  • bundle install後、不要ファイル削除はイメージサイズの削減に貢献

FROM ruby:2.6.3-alpine

ENV LANG=ja_JP.UTF-8
ENV TZ=Asia/Tokyo
ENV ROOT=/myapp \
    GEM_HOME=/bundle \
    BUNDLE_PATH=$GEM_HOME
ENV BUNDLE_BIN=$BUNDLE_PATH/bin
ENV PATH /app/bin:$BUNDLE_BIN:$PATH


WORKDIR $ROOT

RUN apk update && \
    apk upgrade && \
    apk add --no-cache \
        gcc \
        g++ \
        libc-dev \
        libxml2-dev \
        linux-headers \
        make \
        nodejs \
        postgresql \
        postgresql-dev \
        tzdata \
        yarn && \
    apk add --virtual build-packs --no-cache \
        build-base \
        curl-dev

COPY Gemfile $ROOT
COPY Gemfile.lock $ROOT

RUN bundle install -j4
# 不要ファイル削除
RUN rm -rf /usr/local/bundle/cache/* /usr/local/share/.cache/* /var/cache/* /tmp/* && \
apk del build-packs

COPY . $ROOT

# Add a script to be executed every time the container starts.
COPY entrypoint.sh /usr/bin/
RUN chmod +x /usr/bin/entrypoint.sh
ENTRYPOINT ["sh", "/usr/bin/entrypoint.sh"]
EXPOSE 3000

docker-compose.yml

  • macでストレージのアクセスが遅い問題に対応するためvolumeに:cahedオプションを追加
  • webpack-dev-serverを別コンテナで起動させて、hot reloadに対応させています. (ファイル構成に変更があった場合にブラウザのリロード、必要に応じてjavascript周りのコンパイルをしてくれる)
  • dbのhost, user, passwordは後で再利用
version: '3'

services:
  db:
    image: postgres:11.0-alpine
    volumes:
      - postgres:/var/lib/postgresql/data:cached
    environment:
      - TZ=Asia/Tokyo
    ports:
      - '5432:5432'
    environment:
      PGDATA: /var/lib/postgresql/data/pgdata
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: password
      POSTGRES_INITDB_ARGS: '--encoding=UTF-8 --locale=ja_JP.UTF-8'
      TZ: Asia/Tokyo
  app:
    build: .
    command: ash -c "rm -f tmp/pids/server.pid && ./bin/rails s -p 3000 -b '0.0.0.0'"
    volumes:
      - .:/myapp:cached
      - rails_cache:/myapp/tmp/cache
      - bundle:/bundle:cached
    tmpfs:
      - /tmp
    tty: true
    stdin_open: true
    ports:
      - "3000:3000"
    environment:
      RAILS_ENV: development
      NODE_ENV: development
      DATABASE_HOST: db
      DATABASE_PORT: 5432
      DATABASE_USER: postgres
      DATABASE_PASSWORD: password
      WEBPACKER_DEV_SERVER_HOST: webpacker
    depends_on:
      - db
      - webpacker
    links:
      - db
      - webpacker
  webpacker:
    build: .
    command: ./bin/webpack-dev-server
    volumes:
      - .:/myapp:cached
    environment:
      RAILS_ENV: development
      NODE_ENV: development
      WEBPACKER_DEV_SERVER_HOST: 0.0.0.0
    tty: false
    stdin_open: false
    ports:
      - '3035:3035'

volumes:
  rails_cache:
  postgres:
  bundle:

Gemfile

source 'https://rubygems.org'
gem 'rails',      '6.0.3'

Gemfile.lock

空のファイルで良いのでtouchコマンドのみ

touch 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 "$@"

手順

Rails 6.0.3をインストールします

docker-compose run app rails new . --force --no-deps --database=postgresql --skip-bundle

Gemfile

Railsチュートリアルに準じて以下のように変更
(Development, test環境でもPostgreSQLを使用するように変更してあります。)

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

gem 'rails',      '6.0.3'
gem 'puma',       '4.3.4'
gem 'pg',         '1.1.4'
gem 'sass-rails', '5.1.0'
gem 'webpacker',  '4.0.7'
gem 'turbolinks', '5.2.0'
gem 'jbuilder',   '2.9.1'
gem 'bootsnap',   '1.4.5', require: false

group :development, :test do
  gem 'byebug',  '11.0.1', platforms: [:mri, :mingw, :x64_mingw]
end

group :development do
  gem 'web-console',           '4.0.1'
  gem 'listen',                '3.1.5'
  gem 'spring',                '2.1.0'
  gem 'spring-watcher-listen', '2.0.1'
end

group :test do
  gem 'capybara',                 '3.28.0'
  gem 'selenium-webdriver',       '3.142.4'
  gem 'webdrivers',               '4.1.2'
  gem 'rails-controller-testing', '1.0.4'
  gem 'minitest',                 '5.11.3'
  gem 'minitest-reporters',       '1.3.8'
  gem 'guard',                    '2.16.2'
  gem 'guard-minitest',           '2.4.6'
end

group :production do
end

# Windows ではタイムゾーン情報用の tzinfo-data gem を含める必要があります
#gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]

Gemfile.lockをアップデート(必要ないかもしれません)

docker-compose run app bundle update

webpackerのインストール

Rails 6ではwebpackerが必要ですが、
このままではインストールされていないためインストールします

docker-compose run app rails webpacker:install

データベースの設定

config/database.yml
host, username, passwordをdocker-compose.ymlと一致させる

default: &default
  adapter: postgresql
  encoding: unicode
  host: db
  username: postgres
  password: password
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>

#省略...

docker-compose build

DockerfileやGemfileを変更する度に'build'が必要です
bundle installも実施されるため少し時間がかかります

docker-compose build

いよいよコンテナを起動

docker-compose up

データベース作成

このままだとDBがないよと言われるので、development環境のDBを作成

docker-compose run app rake db:create

動作確認

docker psで起動しているコンテナ確認
DB, Rails, webpack-dev-serverの3つのコンテナが起動しています。

$ docker ps
CONTAINER ID        IMAGE                            COMMAND                  ...              PORTS                              NAMES
1fb4f53d5652        sample_app_on_docker_app         "sh /usr/bin/entrypo…"   ...    0.0.0.0:3000->3000/tcp             sample_app_on_docker_app_1
ccd40c018d53        sample_app_on_docker_webpacker   "sh /usr/bin/entrypo…"   ...    3000/tcp, 0.0.0.0:3035->3035/tcp   sample_app_on_docker_webpacker_1
74392532098a        postgres:11.0-alpine             "docker-entrypoint.s…"   ...    0.0.0.0:5432->5432/tcp             sample_app_on_docker_db_1

ブラウザでlocalhost:3000にアクセスすると

お疲れさまでした"Yay! You’re on Rails!"

Trouble shoot

check_yarn_integrity関連

コンテナをdocker-compose upで起動したときに出るエラー

app_1        | ========================================
app_1        |   Your Yarn packages are out of date!
app_1        |   Please run `yarn install --check-files` to update.
app_1        | ========================================
app_1        | 
app_1        | 
app_1        | To disable this check, please change `check_yarn_integrity`
app_1        | to `false` in your webpacker config file (config/webpacker.yml).
app_1        | 
app_1        | 
app_1        | yarn check v1.16.0
app_1        | info Visit https://yarnpkg.com/en/docs/cli/check for documentation about this command.
app_1        | 
app_1        | 
app_1        | Exiting
sample_app_on_docker_app_1 exited with code 1

config/webpacker.yml
以下のように変更します

#...省略

development:
  <<: *default
  compile: false # true -> falseに変更

  # Verifies that correct packages and versions are installed by inspecting package.json, yarn.lock, and node_modules
  check_yarn_integrity: true

#省略...

"webpack-dev-server" not found

同じくコンテナをdocker-compose upで起動したときに出るエラー

webpacker_1  | yarn run v1.16.0
webpacker_1  | error Command "webpack-dev-server" not found.
webpacker_1  | info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.
sample_app_on_docker_webpacker_1 exited with code 1

package.jsonにwebpack-dev-serverが記述されていないはずなので
webpack-dev-serverをインストール

docker-compose run app yarn add webpack-dev-server

おまけ

イメージサイズ 477MB
先人の知恵を多々お借りして軽量化したつもりです。
Dockerを学び始めた頃に作ったのイメージは1.5GBほどありました...

$docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
sample_app_on_docker_app latest 84aed607a3d2 31 minutes ago 477MB
sample_app_on_docker_webpacker latest 84aed607a3d2 31 minutes ago 477MB

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