20200907のdockerに関する記事は8件です。

Rails on Dockerの環境構築手順

環境

  • MacOS Catalina 10.15.6
  • Docker 2.3.0.3
  • Ruby 2.7.1
  • Rails 6.0.3.2
  • Bundler 2.1.4
  • Gem 3.1.2
  • Yarn 1.22.5

手順

予めDockerをインストールしておいて下さい。

Rubyイメージの取得

$ docker pull ruby

Dockerコンテナを起動

<NAME><DIRECTORY>は適時変更して下さい。

$ docker run -i -t --name <NAME> -p 3000:3000 -v "$PWD":<DIRECTORY> /bin/bash

Railsの導入

作業ディレクトリへ移動します。

$ cd <DIRECTORY>

次に、Gemfileの生成

$ bundle init

生成されていることを確認。

$ ls
Gemfile

編集用にvimをインストール。
(ホストPCのディレクトリから直接編集してもOK)

$ apt-get install vim-tiny

編集します。
# gem "rails"のコメントアウト#を外します。

$ vim.tiny Gemfile
編集後
# frozen_string_literal: true

source "https://rubygems.org"

git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }

gem "rails"

Gemをインストール。

$ bundle install

新規Railsプロジェクトの作成。

$ bundle exec rails new .

NodeJSをインストール。

$ apt-get install nodejs

Webpackerをインストールしますが、その前にYarnが必要なのでインストールします。
しかし、そのままapt-get installすると0.32+gitというバージョンでインストールされてしまい、
webpack:installした際に行われるyarnのバージョンチェック時にエラーが出てしまうようです。
参考文献: 【Rails】ArgumentError: Malformed version number string 0.32+gitでwebpacker:installが実行できない場合の対処方法

$ curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add -
$ echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list
$ apt-get update && apt-get install yarn

Webpackerをインストール。

rails webpacker:install

Rails Server起動

起動できるはずです。

$ bundle exec rails server

自分の環境の場合、うまくローカルで接続できなかったので明示的に指定すると無事接続できました。

$ bundle exec rails server -b 0.0.0.0
=> Booting Puma
=> Rails 6.0.3.2 application starting in development 
=> Run `rails server --help` for more startup options
Puma starting in single mode...
* Version 4.3.6 (ruby 2.7.1-p83), codename: Mysterious Traveller
* Min threads: 5, max threads: 5
* Environment: development
* Listening on tcp://0.0.0.0:3000
Use Ctrl-C to stop
Started GET "/" for 172.17.0.1 at 2020-09-07 09:47:16 +0000
Cannot render console from 172.17.0.1! Allowed networks: 127.0.0.0/127.255.255.255, ::1
   (1.5ms)  SELECT sqlite_version(*)
Processing by Rails::WelcomeController#index as HTML
  Rendering /usr/local/bundle/gems/railties-6.0.3.2/lib/rails/templates/rails/welcome/index.html.erb
  Rendered /usr/local/bundle/gems/railties-6.0.3.2/lib/rails/templates/rails/welcome/index.html.erb (Duration: 6.3ms | Allocations: 295)
Completed 200 OK in 21ms (Views: 12.1ms | ActiveRecord: 0.0ms | Allocations: 1651)

Yay! You're on Rails!

image.png

おわり

参考文献: 知識0から始めるRails on Docker

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

【AWS ECS】Fargateの「コマンドの上書き」

はじめに

ECSで起動タイプFargateのタスクを実行する際の「コマンドの上書き」という項目について、「実行時パラメータを渡せるようなものかな」と思って使おうとしました。
この理解でざっくり正解と言えますが、正しく使うにはdockerのENTRYPOINT、CMDコマンドの知識が必要で、自分のようにdockerを使いこなす前にFargateを使おうとした人にとってはハマりどころかと思います。
これらについて調べた内容を記載しています。

コマンドの上書き

ECSタスクの実行やタスクスケジュールの設定画面で「コンテナの上書き」という項目があり、ここで設定した内容をECSタスクに設定したコンテナ実行時に利用することができます。
00_container_override.png
「コンテナの上書き」の中に「コマンドの上書き」というフリー記述項目があります。
画面上のヒントメッセージに「コンテナに渡すCMD」とありますが、どういうことか次項で説明します。

dockerのENTRYPOINTとCMD

dockerfileで「ENTRYPOINT」と「CMD」という命令があり、どちらもコンテナ実行時(docker run時)に実行されるコマンドです。
「コマンドの上書き」の挙動を理解するためにこれらの理解が必要となります。

この2つは、コンテナ実行時に渡されるパラメータの扱いが異なります。
ENTRYPOINTはdocker runのパラメータをそのままENTRYPOINTのパラメータとして扱います。
CMDはdocker runのパラメータをCMD自体として扱います。(CMDの上書き)

ENTRYPOINT

コンテナ実行時に必ず実行されるコマンドと引数(省略可)を設定します。
[書式]

ENTRYPOINT ["実行可能なもの", "パラメータ1", "パラメータ2"]

[例]

ENTRYPOINT ["ping", "localhost"]
→pingコマンドにlocalhostという引数を渡して実行

CMD

コンテナ実行時のデフォルト実行コマンドと引数を設定します。
[書式]

CMD ["実行バイナリ", "パラメータ1", "パラメータ2"]

[例]

CMD ["ping", "localhost"]
→pingコマンドにlocalhostという引数を渡して実行

併用

ENTRYPOINTとCMDを併用する場合、CMDにはENTRYPOINTに渡すパラメータを設定します。
[書式]

ENTRYPOINT ["実行可能なもの", "パラメータ1", "パラメータ2"]
CMD ["パラメータ3", "パラメータ4"]

[例]
以下の2例はどちらも同じコマンドが実行される

ENTRYPOINT [“ping”]
CMD[“-c”,”4”,“localhost”]
→ping -c 4 localhost になる
ENTRYPOINT [“ping”,”-c”]
CMD[”4”,“localhost”]
→ping -c 4 localhost になる

実行時(docker run時)のパラメータ

docker run実行時に指定したパラメータはコンテナ定義のCMDとしてコンテナに渡され、コンテナ起動時にはこのCMDが採用されます。(CMDの上書き)
前項の「コマンドの上書き」はこの仕様に相当します

[書式]

docker run <コンテナ指定> パラメータ1 パラメータ2

[例]
コンテナ定義に「ENTRYPOINT [“ping”,”localhost”]」の設定がある場合、ENTRYPOINT + CMDの併用となる

docker run <コンテナ> -c 4
→ping localhost -c 4 になる

コンテナ定義に「CMD [“ping”,”localhost”]」の設定がある場合、CMDを上書きとなる

docker run <コンテナ> echo hoge
→pingは実行されず、echo hogeが実行される

コンテナ定義に「ENTRYPOINT [“ping”,”localhost”]」と「CMD [“-c”,”4”]」の設定がある場合、CMDを上書きとなる

docker run <コンテナ> -c 10
→ping localhost -c 10 になる

動作確認

前項での実行時パラメータの仕様通りに動くか、「コマンドの上書き」の動作確認をします。
ENTRYPOINTを設定したもの、CMDを設定したもの、両方設定したもの、の3つのコンテナ定義を作成し、それぞれコマンドの上書きなし・ありで実行した場合の系6パターンの出力を確認します。

テスト用ファイル

test_script.shを作成。シェルに渡された引数をechoする。

#!/bin/sh

# 引数の数をecho
echo arg length is $#

# 渡された引数を全てecho
for x
do
  echo arg is "$x"
done

コンテナ定義

[ENTRYPOINTを設定したもの]

FROM ubuntu
COPY . .
RUN chmod 777 test_script.sh

ENTRYPOINT ["sh", "test_script.sh", "ENTRYPOINT"]

[CMDを設定したもの]

FROM ubuntu
COPY . .
RUN chmod 777 test_script.sh

CMD ["sh", "test_script.sh", "CMD"]

[両方設定したもの]

FROM ubuntu
COPY . .
RUN chmod 777 test_script.sh

ENTRYPOINT ["sh", "test_script.sh"]
CMD ["BOTH"]

テスト実行

ENTRYPOINT

[コマンドの上書きなし]
03_entrypoint_1.png
[コマンドの上書きあり]
03_entrypoint_2.png
03_entrypoint_3.png
[結果]
・コマンド上書きありなしどちらでも、arg is ENTRYPOINTが必ず出力されている
 →ENTRYPOINTは必ず実行される
・コマンドの上書きを設定した場合、arg is override1, arg is override2が出力されている
 →ENTRYPOINTにパラメータを渡すCMDとして処理される

CMD

[コマンドの上書きなし]
04_cmd_1.png
[コマンドの上書きあり]
04_cmd_2.png
04_cmd_3.png
[結果]
・arg is CMDはコマンド上書き時に出力されない
 →コンテナ定義のCMDは無視され、コマンドの上書きの内容がCMDとして処理される

両方設定したもの

[コマンドの上書きなし]
05_both_1.png
[コマンドの上書きあり]
05_both_2.png
05_both_3.png
[結果]
・test_script.shのecho処理が実行されている
 →ENTRYPOINTは必ず実行される
・arg is BOTHはコマンド上書き時に出力されない
 →コンテナ定義のCMDは無視され、コマンドの上書きの内容がCMDとして処理される

調査結果

こちらで述べたように、Fargateの「コマンドの上書き」はコンテナ実行時にCMDとして渡されるものでした。
コンソールでの「コンテナに渡すCMD」というヒントメッセージがそのまま正解ですね。

所感としては、「コマンド」というワードがここではdockerの用語の「CMD」を表しているようで、自分はこの点が少しわかりにくさを感じました。
(英語表記でも「Command」のようです)
使うのにdocker知識が前提に感じるので、どうせなら「CMDの上書き」の方がわかりやすい気がします。

注意点

ECSの開発者ガイドで、コンテナ定義にENTRYPOINTがある場合とない場合の「コマンドの上書き」の設定について記載されていますが、誤解しそうな表現になっていると思います。
[ENTRYPOINTがない場合の説明]

If your container definition does not specify an ENTRYPOINT, the format should be a comma-separated list of non-quoted strings. For example:
/bin/sh,-c,echo,$DATE

[ENTRYPOINTがある場合の説明]

If your container definition does specify an ENTRYPOINT (such as sh,-c), the format should be an unquoted string, which is surrounded with double quotes and passed as an argument to the ENTRYPOINT command. For example:
while true; do echo $DATE > /var/www/html/index.html; sleep 1; done

「ENTRYPOINTがない場合はカンマ区切り」という点と、ENTRYPOINTがある場合のサンプルから、「ENTRYPOINTがある場合はカンマ区切り指定ではない」と自分には見えましたが、動作確認で実証したようにENTRYPOINTがあってもカンマ区切り指定可能でした。

参考

ECSの開発者ガイド
https://docs.aws.amazon.com/ja_jp/AmazonECS/latest/developerguide/ecs_run_task_fargate.html

Dockerfile リファレンス
http://docs.docker.jp/v1.11/engine/reference/builder.html

ENTRYPOINTは「必ず実行」、CMDは「(デフォルトの)引数」
https://pocketstudio.net/2020/01/31/cmd-and-entrypoint/

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

【GCP】Cloud BuildでCI/CDパイプラインを構築する。

はじめに

Cloud BuildはGCP上でCI/CDを行うためのサービスです。
特にコンテナを扱ったソリューションと相性がよく、
テストやビルドはGCP上にプロビジョニングされたコンテナ内で行われ、
Cloud FunctionsやCloud Runといった様々なコンピューティングサービス上にデプロイすることができます。

本記事では、

スクリーンショット 2020-09-04 18.08.30.png

の全てGCP内で完結するリソースを使ってコンテナアプリケーションにおけるCI/CD環境を構築したいと思います!
本記事で使用したソースコードは、こちらにあります。

開発環境

  • macOS Catalina 10.15.6
  • Docker Desktop stable 2.3.0.4
  • Google Cloud SDK 308.0

登場人物たち

Cloud Source Repositories

プライベートなGitリポジトリをホスティングできるサービスです。
https://cloud.google.com/source-repositories/docs?hl=ja

Container Registry

プライベートなDockerイメージをホスティングできるサービスです。
https://cloud.google.com/container-registry/docs?hl=ja

Cloud Run

マネージドなコンテナ実行プラットフォームです。
コンテナを手軽にサービスとしてデプロイ、公開可能です。
https://cloud.google.com/run/docs?hl=ja

Cloud Build

本記事の主役です。
冒頭にもあるように、マネージドなCI/CD環境を構築できます。
詳しくは後の項で説明しますが、ビルドコンテナ内でgcloudコマンド叩けたりと柔軟なシナリオが作成可能です。
https://cloud.google.com/cloud-build/docs?hl=ja

作ってみよう!

ローカルにGitリポジトリを作成

適当な空のディレクトリを作成し、git initを行います。

$ mkdir cloudbuild-sample && cd cloudbuild-sample
$ git init

サンプルアプリケーションの作成

Cloud Runにデプロイするアプリケーションを作成します。
AとBの和を返す簡単なアプリを考えます。

$ mkdir workspace
cloudbuild-sample/workspace/go.mod
module mymod

go 1.15
cloudbuild-sample/workspace/main.go
package main

import (
    "encoding/json"
    "fmt"
    "log"
    "mymod/utils"
    "net/http"
)

func main() {
    mux := http.NewServeMux()
    mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        j := json.NewDecoder(r.Body)
        var req struct {
            A int `json:"a"`
            B int `json:"b"`
        }

        j.Decode(&req)
        fmt.Fprintf(w, "%d\n", utils.Add(req.A, req.B))
    })

    log.Println("start server...")
    http.ListenAndServe(":8080", mux)
}

足し算の関数を定義するサブパッケージを作成します。

$ mkdir workspace/utils
cloudbuild-sample/workspace/utils/add.go
package utils

// Add return a + b
func Add(a int, b int) int {
    return a + b
}
cloudbuild-sample/workspace/utils/add_test.go
package utils

import "testing"

func TestSuccessAdd(t *testing.T) {
    expect := 3
    result := Add(2, 1)

    if expect != result {
        t.Fatalf("result is expected %d, got %d ", expect, result)
    }
}

func TestFailureAdd(t *testing.T) {
    t.Fatal("FAILURE!!")
}

CI実行時にテストの失敗を考慮されるかを確認するため、意図的に失敗するテストコードを作成しました。

さらに、公開用のDockerイメージファイルも作成します。

cloudbuild-sample/Dockerfile
FROM golang:1.15-alpine as builder

WORKDIR /opt/build

COPY go.* ./
RUN go mod download

COPY . ./

RUN CGO_ENABLED=0 GOOS=linux go build -mod=readonly -v -o app


FROM alpine:3.12
ENV PORT 8080

COPY --from=builder /opt/build/app /opt/app

WORKDIR /opt
CMD ["./app"]

お疲れ様です!
上記のアプリは、

$ curl -X POST -H "Content-Type: application/json" -d '{"a":41, "b":10}' http://localhost:8080
51

のようなリクエストに対して、aとbの和を返してくれます。

gcloudコマンドの下準備

前提として、既にGCPプロジェクトが作成済みでgcloudコマンドが使用できることを確認してください。
ここではパイプライン環境構築のためのGCPプロジェクト側での設定を行います。

まずは利用するサービスのAPIを有効化しましょう!

$ gcloud services enable sourcerepo.googleapis.com \
run.googleapis.com \
cloudbuild.googleapis.com \
containerregistry.googleapis.com

次にSource Repositoriesにリポジトリを作成し、リモートにセットします。

$ gcloud source repos create cloudbuild-sample
$ git remote add google https://source.developers.google.com/p/{YOUR_PROJECT_ID}/r/cloudbuild-sample

さらにリポジトリに対し認証情報を付与します。

$ git config --local credential.https://source.developers.google.com.helper gcloud.sh

ここまでのコードをpushできるか確認してましょう。

$ git add .
$ git commit -m 'first commit'
$ git push google master

次にCloud Buildのサービスアカウントに対しCloud Runへデプロイするための役割を付与します。

$ gcloud projects add-iam-policy-binding {YOUR_PROJECT_ID} \
--member serviceAccount:{YOUR_PROJECT_NUMBER}@cloudbuild.gserviceaccount.com \
--role roles/run.admin

$ gcloud projects add-iam-policy-binding {YOUR_PROJECT_ID} \ 
--member serviceAccount:{YOUR_PROJECT_NUMBER}@cloudbuild.gserviceaccount.com \
--role roles/iam.serviceAccountUser

お疲れ様です!
これで下準備は完了です。

Cloud Buildの構成ファイルを作成する。

Cloud Buildではテストやビルドの手順をYAMLファイルとして定義します。
簡単な例として、前項で作成したサンプルアプリケーションのテストを実行し、
成功したらデプロイ用のイメージをビルドする手順書を作成してみます。

cloudbuild-sample/cloudbuild.yaml
steps: # steps下にテスト〜デプロイまでの各手順を定義します。
  - name: 'golang:1.15-alpine' # コマンドを実行するコンテナイメージ
    id: 'do testing' # 各ステップを表す識別子
    entrypoint: '/bin/sh' # コンテナが実行される際のエントリーポイント
    env: # コンテナが実行される際に渡される環境変数
    - 'CGO_ENABLED=0'
    dir: 'workspace' # エントリーポイントが実行されるディレクトリ
    args: # エントリーポイントに対する引数
    - '-c'
    - |
        go mod download \
        && go test mymod/...
  - name: 'gcr.io/cloud-builders/docker' # 注1
    id: 'do building'
    args: # エントリーポイントは docker です。
    - 'build'
    - '-t'
    - 'gcr.io/$PROJECT_ID/sample_app:$SHORT_SHA' # 注2
    - '-f'
    - './Dockerfile'
    - './workspace'

設定しているオプションをよく見てみると、
普段のdocker runコマンドを構成するオプションと似ていることが確認できるかと思います。

注1
nameオプションに設定できるコンテナイメージですが、
クラウドビルダーと呼ばれるGCP側で用意された特別なコンテナイメージも利用可能です。
クラウドビルダーコンテナにはIAM等が適切に割り当てられた環境でgcloudコマンド等が実行できるため、
柔軟なパイプラインを作成することが可能になります。
利用可能なクラウドビルダーについては
https://cloud.google.com/cloud-build/docs/cloud-builders?hl=ja
を確認してみてください。

注2
$PROJECT_ID$SHORT_SHAといった、いくつかの変数はCloud Build側で適切な値に自動で置換されます。
今回の例で使用した
$PROJECT_IDは、GCPプロジェクトのID
$SHORT_SHAは、ビルドをトリガーしたリポジトリへのコミットID
です。
変数値の置換については、
https://cloud.google.com/cloud-build/docs/configuring-builds/substitute-variable-values?hl=ja
を確認してみてください。

それでは完全なCloud Buildの構成ファイルを作成してみましょう!
下記の構成ファイルでは、

  1. go testを実行しテストが全てパスされるかを確認
  2. デプロイ用のコンテナイメージをビルド
  3. ビルドされたコンテナイメージをGCRへpush
  4. GCRへプッシュされたイメージを使い、Cloud Runへサービスをデプロイ

を行います。

cloudbuild-sample/cloudbuild.yaml
steps:
  - name: 'golang:1.15-alpine'
    id: 'do testing'
    entrypoint: '/bin/sh'
    env:
    - 'CGO_ENABLED=0'
    dir: 'workspace'
    args:
    - '-c'
    - |
        go mod download \
        && go test mymod/...
  - name: 'gcr.io/cloud-builders/docker'
    id: 'do building'
    args:
    - 'build'
    - '-t'
    - 'gcr.io/$PROJECT_ID/sample_app:$SHORT_SHA'
    - '-f'
    - './Dockerfile'
    - './workspace'
  - name: 'gcr.io/cloud-builders/docker'
    id: 'do pushing image'
    args:
      - 'push'
      - 'gcr.io/$PROJECT_ID/sample_app:$SHORT_SHA'
  - name: 'gcr.io/cloud-builders/gcloud'
    id: 'do deploying'
    args:
    - 'run'
    - 'deploy'
    - 'sample-app'
    - '--image=gcr.io/$PROJECT_ID/sample_app:$SHORT_SHA'
    - '--region=asia-northeast1'
    - '--platform=managed'
    - '--allow-unauthenticated'

お疲れ様です!
パプリックなコンテナイメージとクラウドビルダーを使い分け様々なシナリオに対応できることが実感できたかと思います。
構成ファイルに関する正確な情報はこちらをご確認ください。
https://cloud.google.com/cloud-build/docs/build-config?hl=ja

Source Repositoriesへのpushでビルドをトリガーする。

最後に、Source Repositoriesへコードがプッシュされたときに自動的にCloud Buildが走るようトリガーを設定します。

$ gcloud beta builds triggers create cloud-source-repositories \
--repo cloudbuild-sample \
--branch-pattern="master" \
--build-config cloudbuild.yaml

cloudbuild-sampleリポジトリのmasterブランチにコードがpushされた際に、
cloudbuild.yamlに定義された内容のパイプラインが実行されます。

お疲れ様です!
作業は以上となります。
それではビルドの様子を確認していきたいと思います。

ビルドを実行してみよう!

これまでの内容をpushし、Cloud Buildが実行されるか確認してみます。

$ git add .
$ git commit -m 'second commit'
$ git push google master

下記のURLからビルドの履歴を確認できます。
https://console.cloud.google.com/cloud-build/builds?hl=ja

今回のビルドではgo testに失敗し、ビルドが途中で中断されていることが確認できるかと思います。

テストコードを修正し、もう一度ビルドを実行しましょう。

cloudbuild-sample/workspace/utils/add_test.go
package utils

import "testing"

func TestSuccessAdd(t *testing.T) {
    expect := 3
    result := Add(2, 1)

    if expect != result {
        t.Fatalf("result is expected %d, got %d ", expect, result)
    }
}

func TestFailureAdd(t *testing.T) {
    // t.Fatal("FAILURE!!") # コメントアウトしました。
}

再びpushします。

$ git add .
$ git commit -m 'fix test code'
$ git push google master

ビルドが正常に完了したことを確認します。
https://console.cloud.google.com/cloud-build/builds?hl=ja

さらに、正常にCloud Runへデプロイされたかを確認しましょう!
https://console.cloud.google.com/run?hl=ja

サービスのURLを確認し、curlを叩いてみます。

$ curl -X POST -H "Content-Type: application/json" -d '{"a":41, "b":10}' YOUR_CLOUD_RUN_URL
51

お疲れ様でした!

終わりに

本記事ではCloud Buildを使ってGo言語アプリケーションのCI/CDパイプラインを構築しました。
テストやビルドには様々なコンテナイメージを使えるためGo言語に限らず、柔軟なパイプラインを構築できるかと思います!
ありがとうございました!

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

【Docker】Nginxのconfで環境変数を使う

Rails&pumaで、開発と本番の両方の環境でSSL化をしたくなることがあると思います。
私の場合、ローカルは自己証明書、本番は認証局で登録されている正式な証明書を使いSSL化をしようかなと考えていました。そのためにはNginxのconfファイルに記載する各証明書のパスは開発と本番では別になるため、その部分だけ環境変数で使い分けをしようということになりました。
その時に行った実装手順をメモします。

前提

やり方は、環境変数名を記載したファイルから、環境変数に代入した値に変換されたファイルを生成するenvsubstというコマンドを使用します。

実装手順

環境変数は${SSL_CERTIFICATE_PATH}のように記述する。
※下記のファイルでは、${SSL_CERTIFICATE_PATH}${SSL_CERTIFICATE_KEY_PATH}の2つを記述しています。

docker/nginx/default.conf.template
upstream app {
  server unix:///app/tmp/sockets/puma.sock;
}

server {
  listen 80;
  server_name  _;
  return 301 https://$host$request_uri;
}

server {
  listen 443 ssl;
  server_name localhost;

  ssl_certificate ${SSL_CERTIFICATE_PATH}; # 環境変数
  ssl_certificate_key ${SSL_CERTIFICATE_KEY_PATH}; # 環境変数
  ssl_protocols       TLSv1 TLSv1.1 TLSv1.2;
  ssl_ciphers         HIGH:!aNULL:!MD5;

  access_log /var/log/nginx/access.log;
  error_log  /var/log/nginx/error.log;

  root /app/public;

  location / {
    proxy_pass http://app;
    proxy_set_header X-Real-IP $remote_addr;
    index index.html index.htm;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Host $http_host;
    proxy_redirect off;
  }

  client_max_body_size 100m;
  error_page 404             /404.html;
  error_page 505 502 503 504 /500.html;
  try_files  $uri/index.html $uri @app;
  keepalive_timeout 5;
}

Dockerfileにて、default.conf.templateを配置する。

Dockerfile
FROM nginx:1.16
RUN apt-get update && \
  apt-get install -y apt-utils \
  locales && \
  echo "ja_JP.UTF-8 UTF-8" > /etc/locale.gen && \
  locale-gen ja_JP.UTF-8
ENV LC_ALL ja_JP.UTF-8
# 初期状態の設定ファイル
ADD ./docker/nginx/nginx.conf /etc/nginx/nginx.conf
ADD ./docker/nginx/default.conf.template /etc/nginx/conf.d/default.conf.template

変換する環境変数をnginxサービスのcommandキーのように指定し、envsubstを動くようにする。
環境変数に代入する値はenvironmentキーで設定する。

docker-compose.yml
version: '2'
services:
    app:
# ・・・省略
    db:
# ・・・省略
  nginx:
    build:
      context: .
      dockerfile: ./docker/nginx/Dockerfile
    ports:
      - '80:80'
      - '443:443'
    volumes:
      - sockets:/app/tmp/sockets
      - ./docker/nginx/ssl:/etc/nginx/ssl
    depends_on:
      - app
    command: /bin/sh -c "envsubst '$$SSL_CERTIFICATE_PATH $$SSL_CERTIFICATE_KEY_PATH'< /etc/nginx/conf.d/default.conf.template > /etc/nginx/conf.d/default.conf && nginx -g 'daemon off;'"
    environment:
      SSL_CERTIFICATE_PATH: /etc/nginx/ssl/server.crt
      SSL_CERTIFICATE_KEY_PATH: /etc/nginx/ssl/server.key

# ・・・省略

下記のコマンドでコンテナを立ち上げる時、envsubstコマンドが実行され、環境変数名を記載したファイル(default.conf.template)から、環境変数に代入した値に変換されたファイル(default.conf)が生成されました。

$ docker-compose up -d

参考

envsubstを使ってDockerで設定ファイルに環境変数を埋め込めこむ汎用的なパターン - Qiita

さいごに

パスとファイル名さえ間違えなければ躓くことは無いのではないかと思います。
本番環境でもうまくいくようにこれから試してみます。

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

WindowsのDockerコンテナ上でmatplotlibを動作させる

はじめに

WindowsのDockerコンテナ上でmatplotlibを動作させる方法について記載します。
Windowsは、Windows 10 Pro 64Bit バージョン2004で試しました。これより古いバージョンでも試しました。
Docker Desktopは、WSL2に対応させても以前のままでもどちらも大丈夫です。
Dockerコンテナ内なので、そのままだとmatplotlib等のグラフはホスト側に表示されません。
ホスト側(Windows側)のディスプレイに表示させるようにするには、コンテナOSはlinux系だと思いますので、XServerをWindows側に立ち上げて、そのXServerに表示を流し込むようにする必要があります。

VcXsrvをインストール

まず、VcXsrvをインストールします。
インストールが終わったら、WindowsメニューからVcXsrvにあるXLaunchをクリックします。
デフォルトの設定のままでいけます。

Firewallの設定は不要のはずです。
また、同じPCで実行するなら不要なはずですが、認証等でエラーになるのであれば下記を試してみてください。
C:\Program Files\VcXsrvなどにある
X0.hostsというファイルを開いてホストのIPアドレスまたはホスト名を追記します。
このファイルは権限がないと編集できないので、プロパティ等で権限追加などを行ってください。
(ちなみに、VS Codeで開くと編集できます)

起動すると、画面右下にVcXsrv X Serverのアイコンが表示されるはずです。
そのアイコンを右クリックして表示されるメニューからApplicationsのxclockを選ぶと下記のようにxclockが起動するはずです!
image.png

docker-compose.yml

ディスプレイの設定として、docker-compose.ymlに下記を追加します。

docker-compose.yml
    environment:
      DISPLAY: host.docker.internal:0.0

日本語表示

日本語表示が必要であれば、Dockerfileに以下を追記してください。
fonts-ipafontとしていますが、もちろん別のフォントでも構いません。

RUN apt-get update && apt-get install -y fonts-ipafont

動作させたプログラム

動作させたプログラムとしてはこんな感じです。

test.py
import numpy as np
import matplotlib.pyplot as plt
x = np.arange(-3, 3, 0.1)
y = np.sin(x)
fig = plt.plot(x, y)
plt.show()

matplotlib

Docker内で上記プログラムを実行すると下記のようなグラフが表示されます。
image.png

おわりに

うまくグラフが表示されましたでしょうか!?
以上ですが、皆様の開発効率向上に寄与できれば幸いです。

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

【技術書まとめ】『Docker/Kubernetes 実践コンテナ開発入門』を読んだまとめ

第1章 Dockerの基礎

  • 構成ファイルとコマンドだけで環境が作れる。
  • アプリケーションと実行環境を同梱したデプロイがDocker流
  • Dockerを利用するメリット
    • 環境起因のトラブルを最小限にできる
      • 冪等性が保てる
      • いつなんど実行しても同じ結果が保証される
  • Docker Compose
    • yamlファイルで起動順を制御したりできる

第2章 Dockerコンテナのデプロイ

  • Dockerイメージとは
    • コンテナを具現化するためのテンプレート
$ docker image pull gihyodocker/echo:latest # イメージを取ってくる

$ docker container run -t -p 9000:8080 # コンテナ実行

$ curl http://localhost:9000/ # アクセスしてみる

$ docker stop $(docker container ls -q) # 停止
$ docker container stop $(docker container ls --filter "ancestor=example/echo" -q) # 停止

$ docker image build -t example/echo:latest . # dockerイメージを作成

$ docker image ls # dockerイメージを確認

$ docker container run -p 9000:8080 example/echo:latest # ポートフォワーディング
  • Dockerに対する操作
    • イメージとコンテナの2つに大別される。

Dockerイメージ

  • 「Dockerイメージをビルドする」
$ docker image --help # ヘルプを見る

$ docker search --limit 5 mysql # 検索する
  • imaegeIDはバージョン番号としての役割もある
  • latestはGitでいうこところのmasterブランチ
$ docker image tag example/echo:latest example/echo:0.1.0 # 特定地点をバージョン名などでタグ付けしておく

Dockerコンテナ

  • 実行・停止・破棄の3つの状態
$ docker container run -t -d --name gihyo-echo example/echo:latest # 名前付きコンテナをつくる(開発時に便利)
$ docker container run -it alpine:latest # ターミナルが表示されて文字が入力できる。仮想環境にログインしたように使える。

$ docker container ls -q # コンテナIDだけ抽出する
$ docker container ls --filter "name=echo1" # 名前でフィルターする
$ docker container ls -a # 終了したコンテナを含める

$ docker container rm f66f6f2013da # コンテナを破棄する
$ echo '{"version":100}' | docker container run -i --rm gihyodocker/jq:1.5 '.version' # そのときだけ必要なコンテナは --rm で即時破棄する

$ docker container logs -f &(docker container ls --filter "ancestor=jenkins" -q) # ログを見る

$ $ docker container exec -it echo sh # コンテナ内でコマンド実行する

$ docker container cp echo:/echo/main.go . # コンテナ内のものをコピーしてくる
$ docker container cp dummy.txt echo:/tmp # コンテナへコピーする

運用管理向けコマンド

  • prune
    • 実行していないコンテナを一括削除
$ docker container prune # 実行していないコンテナを一括削除
$ docker image prune # 実行していないイメージを一括削除

$ docker container stats # コンテナ単位でのリソース使用状況を見る
  • Docker Compose
    • docker同士を連携する
    • Jenkinsの例
docker-compose.yml
version: "3"
services:
  echo:
    image: example/echo:latest
    ports:
      - 9000:8080

$ docker-compose up -d # 起動
$ docker-compose down # 停止
$ docker-compose up -d --build # 毎回ビルドして裏で起動
$ docker-compose ps # 起動を確認
  • volumeはコピーでなく共有する

第3章 実用的なコンテナの構築とデプロイ

  • Dockerはアプリケーションとインフラをコンテナ単位で分離したもの
  • 1コンテナに1つの関心事
    • 「1分置きにHello!を出力する」など
    • レプリカとして複製された場合に副作用がないか
  • Dockerのポータビリティの限界
    • CPUアーキテクチャやOSの前提
    • ダイナミックリンクの課題
  • アプリケーションの挙動を環境変数で制御する

第4章 実践的なアプリケーション構築

SwarmでTODOアプリをつくる

$ docker container run -v ${PWD}:/root ch04/tododb:latest

とりあえずここまで。

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

LaravelをDockerで開発している際におこるPermission Deniedを回避する

LaradockではなくLaravelをDockerでローカル開発している際にPermission Deniedで/storage/logsの書き込みが失敗する事象が見られた。
この際の回避方法を残しておきます。

おそらくPermission Deniedになる原因はホストとクライアントとでUID、GIDが異なることで発生する。
docker-composeにより構成はNginx > php-fpmとしている場合以下のようにphp-fpm側のDockerfileにNginxのユーザーであるwww-dataに対して任意のUID、GIDが指定できるようにしておく。

Dockerfile
ARG PUID=1000
ARG PGID=1000

RUN echo "-> $PUID"
RUN echo "-> $PGID"

RUN groupmod -o -g $PGID www-data && \
    usermod -o -u $PUID -g www-data www-data

これをdocker-compose build時に自身のMacのUID、GIDをを差し込む。

$ docker-compose build --build-arg PUID=$(id -u) --build-arg PGID=$(id -g) app

これで無事ホストとクライアントのUID、GIDが同じになった。

おまけ

Makefileで上記のコマンドを設定していると$(id -u)が正しく解決できなかった。
以下のように書くとうまくいく

Makefile
build_app:
    $(eval UID := $(shell id -u))
    $(eval GID := $(shell id -g))
    @docker-compose build --build-arg PUID=$(UID) --build-arg PGID=$(GID) app
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

DockerでMySQLを5.7から8.0にアップデートする

これはなに

DockerのMySQLを5.7から8.0にアップデートする方法です。
docker composeを使ってる前提です。

手順

docker/mysql/Dockerfile
- FROM mysql:5.7
+ FROM mysql:8.0

docker/mysql/Dockerfileのバージョンを書き換えます。

docker-compose build mysql

docker-compose buildするとバージョンアップされます。
ダメそうなら
docker-compose build --no-cache mysql
で試してみてください。

再度mysqlコンテナを起動すると、自動でデータのアップグレードが行われ、MySQL8.0として使えるようになります。
一度データがアップグレードされてしまうと、過去のバージョンのMySQLでは動作しなくなってしまう可能性があるので注意してください。

Laravelで発生する問題の解消

mysql8からデフォルトのauth pluginが変更になっていて
それにlaravelが対応していなく以下のエラーが発生します。

SQLSTATE[HY000] [2054] The server requested authentication method unknown to the client (SQL: select * from `users` where 

そこで、以下の設定をmy.confに設定します。

my.conf
default-authentication-plugin = mysql_native_password

以上、 参考になりましたら幸いです。

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