20200830のdockerに関する記事は14件です。

Dockerの備忘録

Dockerデーモン

コンテナを管理する永続的なプロセスでありDocker デーモンはユーザーからのDockerコマンドによる命令を実行することでコンテナの起動や再起動、停止などの管理をしている。

Docker Hub

Dockerイメージのレジストリサービス。
Dockerイメージの公開、検索、ダウンロードすることができる。

Dockerイメージ

コンテナ実行に必要なファイルをまとめたファイルシステム。
AUSFなどの特殊なファイルシステムが使用されている。
イメージ上のデータはレイヤで構成され読み取り専用。

イメージの一覧表示

% docker images

新しいイメージを作るコマンド

## % docker tag 元のイメージ名 新しいイメージ名
% docker tag docker/whalesay my_whalesay

## % docker tag 元のイメージ名 新しいイメージ名:タグ
% docker tag docker/whalesay my_whalesay:num1

下の場合は「num1」と言うタグ名がつけられた「my_whalesay」というイメージが作られる。

イメージの詳細情報を表示するコマンド

## % docker inspect 対象のイメージ若しくはイメージID
% docker inspect my_whalesay

イメージを削除するコマンド

## % docker rmi 対象のイメージ若しくはイメージID
% docker rmi docker/whalesay

イメージを強制削除するコマンド

## docker rmi -f 対象のイメージ若しくはイメージID
% docker rmi -f docker/whalesay

イメージを取得するコマンド

## % docker pull 取得したいイメージ
% docker pull docker/whalesay

latestが新しいイメージでは無い可能性もあるので詳しくは公式参照。

Dockerfile

イメージの定義ファイル。Dockerfileからイメージをビルドすることをイメージビルドという。

## FROMはイメージを作る際、元のイメージを指定する今回はdocker/whalesay:latest
FROM docker/whalesay:latest
## RUMはイメージビルドの際にコマンドを指定する
RUN apt-get -y update && apt-get install -y fortunes
## CMDはコンテナが作成された後で実行するコマンドを指定する命令
CMD /usr/games/fortune | cowsay

Dockerfileからイメージをビルドするコマンド

## % docker build -t イメージ名 .
% docker build -t docker-whale .

-tはビルドしたイメージに名前をつける今回はdocker-whale。
「.」はビルドコンテキストの設定でイメージを作成する際にアクセスできるディレクトリや、ファイルの範囲を示すもの。今回はカレントディレクトリを示す.(ピリオド)。

## % docker build --no-cache -t イメージ名 .
% docker build --no-cache -t docker-whale .

上記は再度Dockerfileでイメージビルドする際、キャッシュが適用され更新されず、
新しいパッケージがインストールされないのを防ぐコマンド。
キャッシュが使わずに更新することができる。

DockerHubにログインする

% docker login
Username DockerHubのユーザー名
Password: DockerHubのパスワード

成功したら「Login Succeeded」と表示される。

DockerHubにおけるタグ付けルール

## % docker tag 元のイメージ名 リポジトリ名:タグ名
% docker tag docker-whale user/docker-whale:num1

DockerHubの「user/docker-whale」というリポジトリにpushするのでこのような記述になる。
タグ名を指定しない場合はlatestタグになる。
リポジトリ名を間違えるとpushできないので注意!

DockerHubにイメージをpushする

## % docker push リポジトリ名:タグ名
% docker push user/docker-whale:num1

ここでエラーが出た場合、再度リポジトリ名が合っているか確認する。
DockerHubのリポジトリにイメージがpushできていることを確認する。

DockerHubからイメージをpullする

## % docker pull イメージ名:タグ名
% docker pull user/docker-whale:num1

docker imagesコマンドでイメージがpullできていることを確認する。

nginxのコンテナを立ち上げるコマンド

## % docker run --name コンテナ名 -d -p ホスト側ポート番号:コンテナのポート番号 イメージ名
% docker run --name some-nginx -d -p 8080:80 some-content-nginx

--neme は起動するコンテナに名前をつけるオプション。
-d はデタッチモードでコンテナの実行をバックグラウンドで行うもの。入力が無い場合、実行した画面で他の操作ができなくなるため、入力することを推奨。
-p はコンテナのポートをコンテナ外に設定するオプション。ここでは8080が外部に公開するポート番号。
正常に実行された場合、http://localhost:8080/ でnginxが起動していることが確認できる。

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

Windows Containerを使ってみて、独自イメージビルド時の注意点

この記事は富士通システムズウェブテクノロジーの社内技術コミュニティで、「イノベーション推進コミュニティ」
略して「いのべこ」が企画する、いのべこ夏休みアドベントカレンダー 2020の12日目の記事です。
本記事の掲載内容は私自身の見解であり、所属する組織を代表するものではありません。
ここまでお約束 :wink:

Windows Containerとは

Windows ContainerとはWindows10またはWindows Server(2016以降)で利用できる、
Windwosベースのコンテナのことです。

docker runでWindowsが動き出すのはちょっと感動です。もちろん、Windows環境でしか利用できませんが・・・

本記事では、Windwos ServerでWindows Containerの機能をインストールし、起動~Dockerfileによる独自イメージの作成を行った際に、ハマったこと・Windows ContainerならではのDockerfileの書き方の工夫を書き残します。

Windows Containerを使用するための環境の容易には、以下のサイトを参考にしています
https://docs.microsoft.com/ja-jp/virtualization/windowscontainers/quick-start/set-up-environment?tabs=Windows-Server

環境

  • Windows Server 2019 Standard 1809 (ビルド番号:17763.1397)

基本イメージ

Windows Containerには、いくつかの基本イメージがあります。
https://docs.microsoft.com/ja-jp/virtualization/windowscontainers/manage-containers/container-base-images

イメージごとに、インストールされているコンポーネントが異なり、サイズもそれに応じて異なります。

基本イメージ名 サイズ
mcr.microsoft.com/windows 4.2G
mcr.microsoft.com/windows/servercore 3.7G
mcr.microsoft.com/windows/nanoserver 251M
mcr.microsoft.com/windows/iotcore 307M

※すべてタグは「10.0.17763.1217-amd64」を指定しています。

タグについて

ホストしているWindows Serverのビルド番号(今回の例だと、17763.1397)と、コンテナのタグのビルド番号(10.0.{ビルド番号}-xxxx-amd64の部分)が異なると、コンテナは起動しません。

例えば

ホストのビルド番号が14393.*で、コンテナのビルド番号が17763.1217の場合、コンテナの起動はブロックされます。
私の環境だと、そもそもpullするときに指定したタグ名: Pulling from windows no matching manifest for windows/amd64 10.0.17763 in the manifest list entriesというエラーが出て、pullできなかったです

これは、Windows Containerと、ホストしているサーバのカーネルは共有されるためです。
公式ページの説明によると、「起動するかもしれませんが・・・」と書かれておりますが、起動させることすらできませんでした。
https://docs.microsoft.com/ja-jp/virtualization/windowscontainers/deploy-containers/version-compatibility?tabs=windows-server-2004%2Cwindows-10-2004#windows-server-containers
Windows Updateにより、ホストしているサーバのビルド番号に変更があった場合には、コンテナも再ビルドが必要になると覚えておいたほうがよさそうです。

ベースイメージの使い分け

各種ベースイメージには、インストールされているコンポーネントが異なり、公式ページでは以下のように案内されています。
https://docs.microsoft.com/ja-jp/virtualization/windowscontainers/manage-containers/container-base-images#guidelines

  • アプリケーションに完全な .NET フレームワークは必要ですか。
    • この質問への答えが「はい」の場合、Windows Server Core をターゲットにすることをお勧めします。
  • .NET Core に基づいて Windows アプリを構築していますか。
    • この質問への答えが「はい」の場合、Nanoserver をターゲットにすることをお勧めします。
  • IoT アプリケーションを構築していますか。
    • この質問への答えが「はい」の場合、IoT Core をターゲットにすることをお勧めします。
  • アプリに必要な依存関係が、Windows Server Core コンテナー イメージに不足していますか。
    • この質問への答えが「はい」の場合、Windows をターゲットにしてみることをお勧めします。 このイメージは、他の基本イメージよりもサイズがはるかに大きく、多数のコア Windows ライブラリ (GDI ライブラリなど) が含まれています。

こんな感じのイメージでしょうか。

image.png

そのほかにも、NanoserverにはPowerShellがインストールされていないなどの相違点があるようでした。

docker run 実行時の注意点(Volume指定時)

volume指定時、マウント先をD:\などに指定しようとするとエラーになります。
C:\以下にマウントされるようにdocker run xxxx -v ${pwd}:C:\share xxxxと指定してあげる必要があります。

Dockerfileの書き方

Dockerfileの書き方について、詰まったことを書き記しておきます。

パッケージマネージャーが弱い

基本的に、Windowsにはyum/aptのような高性能なパッケージマネージャーがありません
PackageManagementという機能があるので、それを使えば必要そうなのはある程度取れそう・・・?
ProviderもChocolateyとかを選べるので、あとはリポジトリの中に欲しいのを探してみてください。

wingetもWindows Serverでも使えるようになったりするかしら?

RUNコマンドの形式

RUNコマンドは、cmd.exeに対して実行されるようで、cmd.exeで実行してうまく動かないようなコマンドは失敗します(悲しいかな・・・)

Linuxコンテナの場合、以下のように書いたりすると思います

FROM alpine:latest

# パッケージマネージャでいろいろインストールして、そのあと本当に必要なコンポーネントをインストールする。みたいな
RUN apk update \
 && apk add --no-cache curl\
 && curl -L "https://golang.org/dl/go1.15.linux-amd64.tar.gz" -o /tmp/go1.15.linux-amd64.tar.gz \
 && mkdir /go
 && tar /tmp/go1.15.linux-amd64.tar.gz -C /go
 && apk del curl \
 && rm -f /tmp/go1.15.linux-amd64.tar.gz

イメージサイズを縮小するためにこのようにするかと思いますが、当然Windowsですがコマンドが変わってきます。

コマンドプロンプトでやるように、&で区切ってコマンドを記述していきます。

Windows版
RUN powershell.exe Invoke-WebRequest -Uri "https://golang.org/dl/go1.15.windows-amd64.zip"  -Outfile ".\go1.15.windows-amd64.zip" \
  & mkdir C:\go \
  & powershell.exe Expand-Archive .\go1.15.windows-amd64.zip -DestinationPath C:\go \
  & del .\go1.15.windows-amd64.zip

powershell内のコマンドをたたくとき、いちいちpowershellを指定しないといけないので少し面倒です。
1行で実行したいときには、powershell化したりすることを検討したほうがいいかなと思ってます。

ADDコマンドについて

RUNコマンドなどの場合は、cmd.exeで実行しているので¥区切りでコマンドを記述する必要がありますが、ADDコマンドは/区切りでパスを記述しないとエラーになります。。。ドウシテ :sob:
おそらく、Dockerfile側の都合なので、仕方ないのかなと思います。

例えば、こんなDockerfileを書いたとします。

Windows版
FROM mcr.microsoft.com/windows/servercore:10.0.17763.1217-amd64

RUN mkdir C:\app
ADD test.exe C:\app

これでビルドをかけると、

Sending build context to Docker daemon   2.56kB
Step 1/3 : FROM mcr.microsoft.com/windows/servercore:10.0.17763.1217-amd64
 ---> 503b39abeba7
Step 2/3 : RUN mkdir C:\app
 ---> Using cache
 ---> e2b88aa43ff5
Step 3/3 : ADD test.exe C:\app
failed to copy files: failed to copy file: mkdir \\?\Volume{7d8c5343-b2a5-499b-add3-7f392ece5972}\C:.: The directory name is invalid.

こんな感じのログで異常終了します。なので、かならず以下のようにしましょう

Windows版
FROM mcr.microsoft.com/windows/servercore:10.0.17763.1217-amd64

RUN mkdir C:\app
ADD test.exe C:/app # ADDの時は/区切り!覚える!

追加ソフトウェアのインストール時

Package Managerに目当てのソフトウェアがない場合、exeやmsiをダウンロードしてきて、ビルド時に実行する必要があります。
その際、以下のようにすることでコマンドラインからsilentインストールができます。

※基本的には、下記の記事の受け売りです
https://qiita.com/lanevok/items/270c4ee73dba1d77e1f9

msi形式の場合

C:\ msiexec /i インストール対象.msi /passive /norestart

exeの場合

C:\ インストール対象.exe /s /v"/qn REBOOT=ReallySuppress ADDLOCAL=ALL"

上記の方法でうまくいかないものは、個別のオプションがあったりするので、インストールしたいコンポーネントごとにオプションを変えたりしてみてください。

感想

触ってみて、起動が若干遅い(ウィルススキャンソフトとかの影響らしいです。Windows Defenderだけでも結構遅い・・・)し、
パッケージ系のインストールがどうしても難ありな感じしかなくて、たぶん検証用途だったり、
開発環境用のサーバ構築用途ぐらいにしか使わないなぁと・・・
もう少しフットプリントが軽くなったりエコシステムが出そろってくると、メリットも出てくるのかもしれませんが。

とはいえ、コンテナ起動時のメモリ使用量も、windowsベースコンテナで起動直後に80MB前後、
dockerdも30MB程度しかメモリを消費していないところを見ると、VMをポコポコ立ち上げるよりはよっぽど軽量です。

これを機に、Windows Server系の資産をコンテナに置き換えられたらいいなと思う今日この頃でした。

それにしても、Windows Containerなのか、Windows Server Containerなのか。どっちなんでしょう。
Windows Serverで動かしたらWindows Server Containerになるとかそういうあれですかね?

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

最近(2020/08)のDocker for Macのファイルアクセス遅い問題事情メモ

調べたことまとめる。

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

WSL2を使ったDokcerで、`Aws::S3::Errors::RequestTimeTooSkewed` のエラー対処法

環境

  • WSL2 (Windows 10 Home)
  • Docker for Windows

エラー

Docker上で、AWSと連携しようとしたときに、

Aws::S3::Errors::RequestTimeTooSkewed: The difference between the request time and the current time is too large.

のエラーが出た

原因

WSL2で動くDockerコンテナの時刻が大幅にずれているのが原因

Windows ホスト側

$ data 
Sun Aug 30 18:16:29 JST 2020

コンテナのシェル内

$ data
Sun Aug 28 11:16:29 JST 2020

解決法

はじめに

Aws::S3::Errors::RequestTimeTooSkewedのエラーはTimezoneが違う場合でも発生するので、
docker-compose.yml 内でtimezone をJSTに設定しとく。

docker-compose.yml
services:
  web: 
   environment:
     TZ: Asia/Tokyo

つぎに

WSL2の時刻を data --set コマンドで同期させる。
まず、コンテナのシェル内でdataコマンドを使うときに、 operation not permitted にならないように、権限設定をしとく必要がある。

docker-compose.yml
services:
  web:
   privileged: true

つぎに

コンテナのシェル内で、dataコマンドを使って日付を変更する

$ data --set "2020-08-30 18:16:29"

で完了。

他に試したこと

  1. hwclock -s を用いて無理やり時間を変えれるらしい。

-> 下記のエラーが出て、少し解決しようとしてみたが、謎すぎて諦めた

$ hwclock -s
hwclock: Cannot access the Hardware Clock via any known method.
hwclock: Use the --debug option to see the details of our search for an access method

参考リンク

https://qiita.com/sadakan5/items/4c0a394fd1d6be34efb2

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

WEB爆速の貴公子を目指して Lighthouse-ciを動かしてみる

はじめに

速さが足りない!!

image.png

という訳で、サイトの速度を改善していくために計測が必要ですよね?

定期的に計測する方法を探しているときに Lighthouse-ciを見つけました。

その中でも計測結果をダッシュボードで可視化させチームでの速度改善に取り組むために

LHCI Serverについて話していこうと思います。

どんなものかをざっくりと公式の画像つきで紹介します。

LHCI Server を用意するために必要なもの

  • Node v10 LTS以降
  • データベースストレージ(sqlite、mysql、またはpostgresql)

公式の参考画像

image.png
image.png

引用元:LHCI Server

できること

  • Lighthouseの計測結果を記録することができる
  • コミット単位で計測結果を見ることができる
  • 今までの計測結果をグラフとして可視化することができる
  • ローカル環境や本番URLどちらも計測することができる
  • 計測ごとの詳細の差分を可視化することができる

とりあえず公式をみながら手を動かしていった方がイメージつきやすいと思うのでやってみましょう。

やることリスト

  • 公式をみながらLHCI Serverを準備する
    • herokuでやってみよう
    • dockerでやってみよう
  • LHCI Serverに計測するプロジェクトを登録
    • 計測のための設定ファイルを書く
  • GithubActionsでpush時に計測してみる

LHCI Serverを準備する

公式のREADMEの方にherokuとdockerでさくっと立ち上げる方法が書いてあるのでやってみましょう

下準備として下記が使えるようになってますでしょうか?参考記事と公式のドキュメントを添付しておきます。

  • heroku CLI
  • docker

参考

どちらもデプロイするものは同じなので好きな方を選んでもらえればいいと思います。

herokuでやってみる

こちらを利用する場合まずherokuにアカウントを登録しましょう

Heroku はアプリの構築、提供、監視、スケールに役立つクラウドプラットフォームで、アイデアを出してから運用を開始するまでのプロセスを迅速に進めることが可能です。また、インフラストラクチャの管理の問題からも解放されます。
公式から引用(https://jp.heroku.com/what#summary)

herokuを使えばいろんな言語でさくっとデプロイできますね。Rails チュートリアルでも利用されてるやつですね。

利用できるやつの例

  • 言語

    • Node
    • Ruby
    • Python
    • Java
    • PHP
    • Go
    • Scala
    • Clojure
  • データベース

    • Redis
    • PostgreSQL

早速手を動かしましょう!

公式のREADMEの内容を触っていこうと思います。

  • リポジトリを準備します。
# 作業ディレクトリとリポジトリの準備
mkdir lhci-heroku && cd lhci-heroku && git init
# LHCI Serverに必要なファイルを取ってきます。
curl https://raw.githubusercontent.com/GoogleChrome/lighthouse-ci/master/docs/recipes/heroku-server/package.json > package.json
curl https://raw.githubusercontent.com/GoogleChrome/lighthouse-ci/master/docs/recipes/heroku-server/server.js > server.js
# コミット
git add -A && git commit -m 'Initial commit'

ダウンロードしてくるファイルは下記のような内容になります。

package.json
{
  "name": "lhci",
  "version": "1.0.0",
  "main": "server.js",
  "dependencies": {
    "@lhci/server": "0.4.x",
    "pg": "^7.12.1",
    "pg-hstore": "^2.3.3"
  }
}
server.js
/**
 * @license Copyright 2019 Google Inc. All Rights Reserved.
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
 */
'use strict';

const {createServer} = require('@lhci/server');

console.log('Starting server...');
createServer({
  port: process.env.PORT,
  storage: {
    storageMethod: 'sql',
    sqlDialect: 'postgres',
    sqlConnectionSsl: true,
    sqlConnectionUrl: process.env.DATABASE_URL,
  },
}).then(({port}) => console.log('Listening on port', port));

sqlDialect でデータベースの種類と sqlConnectionUrlでデータベースのURLを指定していますね。

LHCI Serverではデータベースの接続としてURLを利用します。

postgresの例 公式

postgres://{ユーザー名}:{パスワード}@{ホスト名}:{ポート番号}/{DB名}

さて早速herokuにデプロイしてみましょう!!

heroku CLIをここから使用していきます。

# heroku上に新しいプロジェクトを作成します。
heroku create
# プロジェクトでpostgresqlを使えるようにします。
heroku addons:create heroku-postgresql:hobby-dev
# herokuにデプロイしてみます。
git push heroku master
# 実行確認し、デプロイされたアプリを開きます。
heroku ps:scale web=1
heroku open

このような画面が確認できればOKです。

スクリーンショット 2020-08-31 3.06.21.png

なんか lhci wizard とか書いてますね、このコマンドは LHCI Serverに計測したいプロジェクトを登録するのに必要なコマンドになります。

早速herokuの lhci serverにプロジェクトを登録したい方はこちら

dockerでやってみる

dockerとはさくらナレッジさんの記事が個人的にわかりやすいので添付しておきます。

Dockerは、インフラ関係やDevOps界隈で注目されている技術の一つで、Docker社が開発している、コンテナ型の仮想環境を作成、配布、実行するためのプラットフォームです。
引用元: Docker入門(第一回)~Dockerとは何か、何が良いのか~

docker でLHCI Serverを動かしてみましょう。
こちらも公式のREADMEの内容を触っていこうと思います。

Docker Hubにimageが上がっているので使っていきます。

patrickhulce/lhci-server

https://hub.docker.com/r/patrickhulce/lhci-server

今回は docker-compose.ymlを利用する方法で準備していきましょう

mkdir lhci-docker && cd lhci-docker && touch docker-compose.yml
docker-compose.yml
version: '3'
services:
  lhserver:
    image: patrickhulce/lhci-server
    ports:
      - '9001:9001'
    volumes:
      - lhci-data:/data
volumes:
  lhci-data:

patrickhulce/lhci-serverはデフォルトだとsqliteを利用しているので データ保存先の/data を永続化しています。

patrickhulce/lhci-server中をみてみましょう

lighthouserc.json
{
  "ci": {
    "server": {
      "port": 9001,
      "storage": {
        "storageMethod": "sql",
        "sqlDialect": "sqlite",
        "sqlDatabasePath": "/data/lhci.db"
      }
    }
  }
}

sqlDialect で sqlite

sqlDatabasePathで sqliteの保存先が書いてあります。登録先をheroku同様postgresなどに変更することもできます。

ローカル環境で起動してみる

docker-compose up -d

http://localhost:9001/
こちらにアクセス

このような画面になればOKです。

スクリーンショット 2020-08-31 3.06.21.png

なんか lhci wizard とか書いてますね、このコマンドは LHCI Serverに計測したいプロジェクトを登録するのに必要なコマンドになります。

計測結果を記録してみる

lhci serverで先ほどからデプロイ完了した後に出てくる、lhci wizardこちらのコマンドを使用してプロジェクトの登録からlhci autorunというコマンドを利用してプロジェクトを計測しlhci serverに計測結果を登録しましょう。

プロジェクトのLHCI Serverへの登録

登録したいプロジェクトのディレクトリで作業していきましょう

# lhci serverのバージョンを確認する
curl https://{lhci serverのURL}/version # Make sure you can connect to your server.
0.x.x

僕の場合は 0.44 と返ってきました

# 取得したバージョンに対応した物をインストールします。
# 僕の場合だと0.44が返ってきたので...
npm install -g @lhci/cli@0.4.4
# lhci wizardコマンドを使用
lhci wizard
# 何個か質問がきます。
# lhci serverに新規でプロジェクトを登録します
? Which wizard do you want to run? new-project
# lhci serverのURLを登録
? What is the URL of your LHCI server? https://your-lhci-server.example.com/
# lhci serverに登録するプロジェクト名を登録
? What would you like to name the project? My Favorite Project
# プロジェクトのコードが管理されている場所 自分のソースがあるリポジトリのUR
? Where is the project's code hosted? https://github.com/GoogleChrome/lighthouse-ci
# 対象のブランチ名
? What branch is considered the repo's trunk or main branch? master

これらのコマンドを実行し終わると

  • build token
  • admin token

が出てきますのでメモしておきましょう

Created project My Favorite Project (XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX)!
Use build token XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX to connect.
Use admin token XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX to manage the project.

この時点で確認してみると 今回僕は testという名前でプロジェクトを登録しました。
しかしこの状態ではサイトの計測は行われていないので計測していこうと思います。

スクリーンショット 2020-08-31 4.20.08.png

プロジェクトの計測

計測のために必要なlighthouserc.jsというファイルを書いていきましょう

lighthouserc.js
module.exports = {
  ci: {
    upload: {
      target: 'lhci',
      serverBaseUrl: 'https://your-lhci-server-url.example.com',
      token: 'Your *build token* goes here', // could also use LHCI_TOKEN variable instead
    },
  },
};

今回は lhci serverに計測結果を記録するので
targetはlhci
serverBaseUrl
ここで登録したURLを記載

? What is the URL of your LHCI server? https://your-lhci-server.example.com/

token

ここのtokenを記載

Use build token XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX to connect.

この状態ではどこのURLを計測すればいいのか記載されていないため追記してみましょう

静的サイトを対象に計測する時

https://github.com/GoogleChrome/lighthouse-ci/blob/master/docs/getting-started.md#modifications-for-sites-without-a-build-step

lighthouserc.js
module.exports = {
  ci: {
    collect: {
      staticDistDir: './dist',
      url: ['/'],
    },
    upload: {
      target: 'lhci',
      serverBaseUrl: 'https://your-lhci-server-url.example.com',
      token: 'Your *build token* goes here', // could also use LHCI_TOKEN variable instead
    },
  },
};

staticDistDirに静的ファイルがある場所を選択します。
urlに計測したいURLを記載します。

静的なサイトではない場合

https://github.com/GoogleChrome/lighthouse-ci/blob/master/docs/getting-started.md#modifications-for-sites-with-a-custom-server

lighthouserc.js
module.exports = {
  ci: {
    collect: {
      startServerCommand: 'rails server -e production',
      url: ['http://localhost:3000/'],
    },
    upload: {
      target: 'lhci',
      serverBaseUrl: 'https://your-lhci-server-url.example.com',
      token: 'Your *build token* goes here', // could also use LHCI_TOKEN variable instead
    },
  },
};

startServerCommand にサーバーの起動のコマンドを入れてあげます 上記は railsの例です。
url に計測したいURLを記載します。

指定したURLで計測

本番のURLとか実在するサイトに対して行う場合は、startServerCommandは必要ないため
URL指定だけでも計測可能です。

lighthouserc.js
module.exports = {
  ci: {
    collect: {
      url: ['URL']
    },
    upload: {
      target: 'lhci',
      serverBaseUrl: 'https://your-lhci-server-url.example.com',
      token: 'Your *build token* goes here', // could also use LHCI_TOKEN variable instead
    },
  },
};

計測

# 計測
lhci autorun

デフォルトだと計測回数は3回計測されます。

計測回数を変えたい時は、

lighthouserc.js
    collect: {
      numberOfRuns: 1,
    },

numberOfRuns に回数を指定すれば問題ないです

当たり前ですが計測回数を増やせばそれだけ計測にかかる時間は増えます

スクリーンショット 2020-08-31 5.34.01.png

Github Actionsで計測する

GithubActionsとは
プッシュ、Issue、リリースなどのGitHubプラットフォームのイベントをトリガーとしてワークフローを起動しましょう。コミュニティが開発・保守し、ユーザが熟知・愛用しているサービスについて、対応するアクションを組み合わせて設定できます。
引用元: https://github.co.jp/features/actions

これを利用すれば好きなタイミングで自動的に計測を行ってくれますね。

今回はリポジトリpush時に作動する簡単なciファイルが、Lighthouse-ciの公式に書いてあるのでそちらを利用していきます。

https://github.com/GoogleChrome/lighthouse-ci

github/workflows/ci.yml
name: CI
on: [push]
jobs:
  lighthouseci:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - uses: actions/setup-node@v1
      - run: npm install && npm install -g @lhci/cli@0.4.4
      - run: npm run export
      - run: lhci autorun

僕の場合は サンプルから 

  • npm run build -> npm run export
  • npm install -g @lhci/cli@0.4.x -> npm install -g @lhci/cli@0.4.4

変更しています。

いざpush!!

スクリーンショット 2020-08-31 5.39.50.png

無事CI実行できましたね

まとめ

  • Lighthouseの計測結果を記録することができる
  • コミット単位で計測結果を見ることができる
  • 今までの計測結果をグラフとして可視化することができる
  • ローカル環境や本番URLどちらも計測することができる
  • 計測ごとの詳細の差分を可視化することができる

他にもassertを設定したり。

Basic認証を設定したりできるので色々試してみてください。

  • push 時のタイミングで計測し一定を下回ったら失敗するようにしてみたり
  • スケジューリングで毎日一定時間に本番のURLを計測してみたり

などいろんな使い方があるのかと思います。

また記録が保存されているデータベースの中身をみに行くと面白いです。

僕自身も使い始めたばかりなので知っている方色々教えてくださると助かります。

長い間お付き合いいただき,ありがとうございました。

いざ!WEB爆速の貴公子へと

image.png

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

Intel入ってるNASでVAAPIを使う

きっかけ

nasneでTVを観ているのですが,

  • 視聴に専用のソフトが必要
  • Macで視聴するには,
    • Parallels/VMware Fusionなどのような,高性能な仮想化ソフトウェアを用いてPCTV Plusで視聴しないといけない.
    • 非公式なMac用ソフトウェアが必要.
  • そもそもnasneのHDDが死んだときに録画データも死ぬ.当然市販のレコーダーだって同様.
    • 市販の録画機では冗長性0.

などの問題があり,市販のエンドユーザ向け録画機ではまともに利用できません.

そこで,録画サーバを構築しようと思ったのですが,サーバでトランスコード/エンコードができないと,
帯域・ストレージリソースを大量に消費してしまいますよね.
そうなると,HWエンコーダなどの支援がほしいところです.

NAS

現在,私はSynology DS218+というNASを持っています.
このNASは,IntelのCeleron J3355というCPUが内蔵されています.このため,Synology公式でDockerに対応しています.
また,このCPU,Intel公式を見てみると,QSVに対応している模様です.
https://ark.intel.com/content/www/jp/ja/ark/products/95597/intel-celeron-processor-j3355-2m-cache-up-to-2-5-ghz.html
つまり,DockerからうまいことHWエンコーダを呼び出せば,QSVを利用して高速エンコードができるようになります.

目的

しかし,やはり実際にエンコードを走らせてみないと,まともに使えるのかわかりません.
そこで,本記事においての目的としては,録画サーバを組む前段階として,
Intel入ってるNASでは,HWエンコードがどのくらいの性能なのか?とします.

用意したもの

  • DS218+

    • おそらくQSV対応のIntel CPUを積んでいて,以下の条件に合致していれば,他のNASでも使えると思います.
    • NASにSSH可能
    • Dockerインストール済み
    • 後述するスクリプト
    • DSMのLinuxカーネル4.4.59+
      $ uname -r
      4.4.59+
    
  • 30分アニメを録画したtsファイル(hoge.ts)

  • 非公式なので,何があっても自分でなんとかする強い気持ち

検証

IntelCPUが入っている本NASにおいては,encordデバイスが/dev/dri:/dev/driにあります.
他のIntelCPUが内蔵されているものも同様かと思われます.
これをコンテナに渡してやることで,コンテナ内にて,ホストのHWエンコーダを利用できます.

方法

こちらを参考にしました.→https://timothybasanov.com/2018/12/08/hardware-accelerated-h264-encoding-synology-nas.html

今回は,VAAPIでQSVを使います.
このため,VAAPIに対応したffmpegなDockerコンテナで検証します.

以下のスクリプトを書きました.

encord_test.sh
#!/bin/sh
host_tsdir=/volume1/video
container_tsdir=/tmp
host_mp4dir=/volume1/video/mp4
container_mp4dir=/media
movie_name=hoge

sudo docker run --rm \
--device /dev/dri:/dev/dri \
-v ${host_tsdir}:${container_tsdir} \
-v ${host_mp4dir}:${container_mp4dir} \
jrottenberg/ffmpeg:vaapi \
-hwaccel vaapi -hwaccel_output_format vaapi \
-i ${container_tsdir}/${movie_name}.ts \
-vf 'scale_vaapi=w=1280:h=720' \
-c:v h264_vaapi \
${container_mp4dir}/${movie_name}.mp4

結果

エンコード速度とCPU負荷の検証,エンコード前後でのファイルサイズの比較を行いました.

実行速度

まず,スクリプトを実行したときの実行結果です.
4倍以上,安定してエンコードできているのがわかります.非常に高速です.

./encord_test.sh
(省略)
Output #0, mp4, to '/media/hoge.mp4':
  Metadata:
    encoder         : Lavf58.20.100
    Stream #0:0: Video: h264 (h264_vaapi) (avc1 / 0x31637661), vaapi_vld, 1280x720 [SAR 1:1 DAR 16:9], q=-1--1, 29.97 fps, 30k tbn, 29.97 tbc
    Metadata:
      encoder         : Lavc58.35.100 h264_vaapi
    Stream #0:1: Audio: aac (mp4a / 0x6134706D), 48000 Hz, stereo, fltp, 128 kb/s
    Metadata:
      encoder         : Lavc58.35.100 aac
frame= 1996 fps=142 q=-0.0 size=   42240kB time=00:01:06.46 bitrate=5206.1kbits/s dup=14 drop=0 speed=4.72x

処理負荷

このとき,どのくらいのCPUリソースを利用しているのかみてみました.
ちなみにリソース監視は,Prometheus+NetData+GrafanaをDockerで建てて,フルコンテナ運用しています.
スクリーンショットはGrafanaのダッシュボードです.
概ね40%ちょっとくらいのCPU使用率で安定しています.
image.png

圧縮率

今回,1920x1080のtsファイルを,1280x720のmp4(h264)に変換したため,割と小さいサイズになっています.
概ね半分以下のサイズに抑えられています.
image.png

まとめ

非常に実用的な速度で,実用的なリソース消費で,消費電力も(おそらく)そこまでではなさそうなので,
IntelのQSVが使えるNASをお持ちであれば,エンコードサーバにするのもありだと思います.
今回,録画サーバにおけるエンコード性能として検証しましたが,systemdで適当に動かすなり,
cronでスケジューリングなりすれば,NASに溜めておいたホームビデオの自動エンコードなども可能になると思います.
また,まだまだffmpegの設定を調整すれば,さらなる容量削減や,インターレース解除などもできると思いますし,
そこあたりは今後の課題とします.いいffmpegオプションなどありましたらぜひ教えて下さい.

録画サーバを組むとしたら

チューナデバイスとしては,PX-S1UDを考えています.このチューナ,ドライバがPLEX公式から出ています.
また,Linuxのディストリ依存などがなく,DSMでも動く模様(https://qiita.com/xingoxu/items/c813c4ed048116f653a6#%E3%82%A2%E3%83%B3%E3%83%86%E3%83%8A%E8%A8%AD%E7%BD%AE )です.良かった.
当初は,エンコード性能・省電力性能が高いRaspberry Pi4を買おうか迷っていましたが,NAS一台で完結しそうです.
また,録画サーバのソフトウェア周りも実はコンテナ化して運用可能です.
https://github.com/l3tnun/docker-mirakurun-epgstation
つまるところ,NAS一台で,録画サーバまでフルコンテナ運用ができそうです.
万一,Docker内サーバの環境が壊れても,別にいくつかのバックアップをちゃんと取るようにしているので,
ある程度の冗長性が確保できそうです.かなり安定性の高い録画システムが組めるのではないでしょうか.

おまけ

1920x1080で出力した結果がほしいという声が聞こえてきそうなので,検証してみました.

実行速度

Output #0, mp4, to '/media/hoge.mp4':
  Metadata:
    encoder         : Lavf58.20.100
    Stream #0:0: Video: h264 (h264_vaapi) (avc1 / 0x31637661), vaapi_vld, 1920x1080 [SAR 1:1 DAR 16:9], q=-1--1, 29.97 fps, 30k tbn, 29.97 tbc
    Metadata:
      encoder         : Lavc58.35.100 h264_vaapi
    Stream #0:1: Audio: aac (mp4a / 0x6134706D), 48000 Hz, stereo, fltp, 128 kb/s
    Metadata:
      encoder         : Lavc58.35.100 aac
frame=52707 fps= 85 q=-0.0 Lsize= 2034563kB time=00:29:18.59 bitrate=9477.6kbits/s dup=14 drop=0 speed=2.85x

処理負荷

ダウンコンバートしていないためか,多少,CPU負荷が少なくなっています.概ね40%行かないくらいのCPU使用率ですね.
image.png

圧縮率

ほとんど圧縮できていませんね.でもffmpegの設定をよしなに調整すればちゃんと圧縮できそうです.
image.png

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

alpine-linuxでcode-serverを立ち上げる

Alpine-Linux上でcode-serverを立ち上げる

Pythonの開発環境を構築する必要があり調査を実施。
以下のDockerfileで起動し、それとなく使える事が判明した。
ベースイメージはpythonオフィシャルの3.7-alpineを使用したが、通常のalpine-linuxでも起動できると思う。

FROM python:3.7-alpine

RUN apk --no-cache --update add alpine-sdk bash libstdc++ libc6-compat npm libx11-dev libxkbfile-dev libsecret-dev && \
    npm config set unsafe-perm true && \
    npm install -g code-server

ENTRYPOINT ["code-server", "--auth", "none", "--bind-addr", "0.0.0.0:8080"]
CMD [""]

注意事項

  • ポート番号は8080なので、docker起動時にポートのマッピングが必要
  • 鍵情報を作らないとブラウザ等からクリップボード等のアクセスに問題がある模様?insecureだと警告がでる。
  • スリム化は行っていない。現状、docker images上で944MBと表示される。
  • 認証も外しているので、インターネット等外部からアクセスするようなところでの起動は自己責任で。
  • その他、使い込んでいないので、他にどのような問題があるかは不明。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Amazon ECRがOCI ArtifactをサポートしてHelm chartsも管理できるようになったよ

はじめに

2020/8/28 に Amazon ECR が OCI Artifact をサポートしました。
これにより Helm charts や OPA Bundles など、コンテナイメージ以外の
コンテンツタイプ (アーティファクト) を ECR で保存、配布できるようになります。

OCI Artifact Support In Amazon ECR
https://aws.amazon.com/jp/blogs/containers/oci-artifact-support-in-amazon-ecr/

例えば組織内で安全に Helm charts を共有しようとした場合に
これまでは ChartMuseum 等を使用して、プライベートな
Helm chart registry を構築する必要がありましたが、ECR だけで管理できるようになります。

8/17の AWS Container Day at KubeCon で Developer Preview として発表されたばかりだった
認識ですが、2週間もたたずに正式リリース?となったようです。
今回のローンチの対象は SDK と CLI のサポートで、マネジメントコンソール側の変更は
今年後半にリリース予定とのこと。

ほぼドキュメント通りの手順になってしまいますが、やってみます。

作業環境

  • Helm: v3.3.0
  • AWS CLI: 2.0.43
  • Kubernetes: 1.16.5 (Docker Desktop)

Helm のインストール

クライアント端末に Helm 3 がインストールされていない場合はインストールしておきます。
Github のリリースページからバイナリを取得、インストールスクリプトの使用、
パッケージマネージャー経由など複数のインストール方法が準備されています。

Installing Helm
https://helm.sh/docs/intro/install/

$ helm version
version.BuildInfo{Version:"v3.3.0", GitCommit:"8a4aeec08d67a7b84472007529e8097ec3742105", GitTreeState:"dirty", GoVersion:"go1.14.7"}

Helm 3 における OCI のサポートは 現在 experimental な機能とみなされているため
HELM_EXPERIMENTAL_OCI を環境変数に設定する必要があります。

export HELM_EXPERIMENTAL_OCI=1

ECR レポジトリの作成と認証

AWS CLI で作成します。
特に追加のオプション等は不要で、いつもどおり作成できます。

$ aws ecr create-repository --repository-name helm-test
{
    "repository": {
        "repositoryArn": "arn:aws:ecr:ap-northeast-1:123456789012:repository/helm-test",
        "registryId": "123456789012",
        "repositoryName": "helm-test",
        "repositoryUri": "123456789012.dkr.ecr.ap-northeast-1.amazonaws.com/helm-test",
        "createdAt": "2020-08-29T12:22:02+09:00",
        "imageTagMutability": "MUTABLE",
        "imageScanningConfiguration": {
            "scanOnPush": false
        },
        "encryptionConfiguration": {
            "encryptionType": "AES256"
        }
    }
}

以下のコマンドで Helm クライアントを ECR に認証します。

$ aws ecr get-login-password | helm registry login \
> --username AWS --password-stdin 123456789012.dkr.ecr.region.amazonaws.com
Login succeeded

aws ecr get-login コマンドは非推奨になっており、AWS CLI v2 では削除されているため
aws ecr get-login-password を使用します。

参考:AWS CLIでECRにログインする時はget-loginではなくget-login-passwordを使おう
https://qiita.com/hayao_k/items/3e4c822425b7b72e7fd0

Chart の作成

helm create コマンドで Chart の雛形を作成することができます。

$ mkdir helm-test 
$ cd helm-test

$ helm create mychart
Creating mychart

今回はこの雛形をそのまま利用します。

ECR へ Push

helm chart save コマンドで Chart ディレクトリをローカルキャッシュに保存します。
この際、リモートの ECR の URI を使用して、エイリアスを作成します。
このあたりは、コンテナイメージを push するときと同じ感覚です。

$ helm chart save mychart mychart
ref:     mychart:0.1.0
digest:  c455f9c99430ee099ea66999a40a0978947ccfd76e6922cdb8a911491326b0ed
size:    3.5 KiB
name:    mychart
version: 0.1.0
0.1.0: saved

$ helm chart save mychart 123456789012.dkr.ecr.ap-northeast-1.amazonaws.com/helm-test:mychart
ref:     123456789012.dkr.ecr.ap-northeast-1.amazonaws.com/helm-test:mychart
digest:  4865989e73df2c14b15ee2a1befa11673e455cc5a728239a05f5bc12b13cadb3
size:    3.5 KiB
name:    mychart
version: 0.1.0
mychart: saved

$ helm chart list
REF                                                             NAME    VERSION DIGEST  SIZE    CREATED
123456789012.dkr.ecr.ap-northeast-1.amazonaws.com/helm-te...    mychart 0.1.0   4865989 3.5 KiB 28 seconds
mychart:0.1.0                                                   mychart 0.1.0   4865989 3.5 KiB About a minute

helm chart push コマンドで ECR に push できます。

$ helm chart push 123456789012.dkr.ecr.ap-northeast-1.amazonaws.com/helm-test:mychart
The push refers to repository [123456789012.dkr.ecr.ap-northeast-1.amazonaws.com/helm-test]
ref:     123456789012.dkr.ecr.ap-northeast-1.amazonaws.com/helm-test:mychart
digest:  4865989e73df2c14b15ee2a1befa11673e455cc5a728239a05f5bc12b13cadb3
size:    3.5 KiB
name:    mychart
version: 0.1.0
mychart: pushed to remote (1 layer, 3.5 KiB total)

今回のリリースではマネジメントコンソールに変更が入っていないため、
現在は普通に Docker イメージが格納されているだけのように見えます。

image.png

push した Chart を確認すると、適切な artifactMediaType で保存されていることがわかります。

$ aws ecr batch-get-image --repository-name helm-test --image-ids imageTag=mychart --query 'images[].imageManifest'
[
    "{\"schemaVersion\":2,\"config\":{\"mediaType\":\"application/vnd.cncf.helm.config.v1+json\",\"digest\":\"sha256:65a07b841ece031e6d0ec5eb948eacb17aa6d7294cdeb01d5348e86242951487\",\"size\":141},\"layers\":[{\"mediaType\":\"application/tar+gzip\",\"digest\":\"sha256:acd033c1b722f7dcbfb38a909c422540c584feef52ad4aedb8289eb074638f27\",\"size\":3562}]}"
]

ECR から Chart を pull する

pull を確認するため、helm chart remove コマンドで ローカルキャッシュ上の Chart を削除します。

$ helm chart remove 123456789012.dkr.ecr.ap-northeast-1.amazonaws.com/helm-test:mychart
mychart: removed

$ helm chart remove mychart:0.1.0
0.1.0: removed

helm chart pull コマンドで pull します。

$ helm chart pull 123456789012.dkr.ecr.ap-northeast-1.amazonaws.com/helm-test:mychart
mychart: Pulling from 123456789012.dkr.ecr.ap-northeast-1.amazonaws.com/helm-test
ref:     123456789012.ecr.ap-northeast-1.amazonaws.com/helm-test:mychart
digest:  4865989e73df2c14b15ee2a1befa11673e455cc5a728239a05f5bc12b13cadb3
size:    3.5 KiB
name:    mychart
version: 0.1.0
Status: Downloaded newer chart for 123456789012.dkr.ecr.ap-northeast-1.amazonaws.com/helm-test:mychart

$ helm chart list
REF                                                             NAME    VERSION DIGEST  SIZE    CREATED
123456789012.dkr.ecr.ap-northeast-1.amazonaws.com/helm-te...    mychart 0.1.0   4865989 3.5 KiB 9 hours

Chart のデプロイ

pull した Chart を使用して Kubernetes にデプロイするには
helm chart export コマンドを使用して一度ローカルに export する必要があります。

helm chart export 123456789012.dkr.ecr.ap-northeast-1.amazonaws.com/helm-test:mychart --destination ./chart-install
ref:     123456789012.dkr.ecr.ap-northeast-1.amazonaws.com/helm-test:mychart
digest:  4865989e73df2c14b15ee2a1befa11673e455cc5a728239a05f5bc12b13cadb3
size:    3.5 KiB
name:    mychart
version: 0.1.0
Exported chart to chart-install\mychart/

helm install コマンドで Namespce: helm-test に Chart をインストールします。

$ cd chart-install
$ kubectl create ns helm-test
namespace/helm-test created

$ helm install mychart ./mychart -n helm-test
NAME: mychart
LAST DEPLOYED: Sun Aug 30 11:28:24 2020
NAMESPACE: helm-test
STATUS: deployed
REVISION: 1
NOTES:
1. Get the application URL by running these commands:
  export POD_NAME=$(kubectl get pods --namespace helm-test -l "app.kubernetes.io/name=mychart,app.kubernetes.io/instance=mychart" -o jsonpath="{.items[0].metadata.name}")
  echo "Visit http://127.0.0.1:8080 to use your application"
  kubectl --namespace helm-test port-forward $POD_NAME 8080:80

問題なくデプロイできました。

$ kubectl get all -n helm-test
NAME                           READY   STATUS    RESTARTS   AGE
pod/mychart-5965fbff94-xxnd2   1/1     Running   0          32s

NAME              TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE
service/mychart   ClusterIP   10.99.155.110   <none>        80/TCP    32s

NAME                      READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/mychart   1/1     1            1           32s

NAME                                 DESIRED   CURRENT   READY   AGE
replicaset.apps/mychart-5965fbff94   1         1         1       32s

$ helm list -n helm-test
NAME    NAMESPACE       REVISION        UPDATED                                 STATUS          CHART           APP VERSION
mychart helm-test       1               2020-08-30 11:28:24.6691172 +0900 JST   deployed        mychart-0.1.0   1.16.0

削除は helm uninstall コマンドを使用します。

$ helm uninstall mychart -n helm-test
release "mychart" uninstalled

$ kubectl delete ns helm-test
namespace "helm-test" deleted

Chart を ECR から削除する

ECR 上の Chart を削除する手順は、Docker イメージの時と変わりません。

$ aws ecr batch-delete-image --repository-name helm-test --image-ids imageTag=mychart
{
    "imageIds": [
        {
            "imageDigest": "sha256:4865989e73df2c14b15ee2a1befa11673e455cc5a728239a05f5bc12b13cadb3",
            "imageTag": "mychart"
        }
    ],
    "failures": []
}

コンソールから削除を行うことも可能です。
image.png

参考

Pushing a Helm chart
https://docs.aws.amazon.com/AmazonECR/latest/userguide/push-oci-artifact.html

以上です。
参考になれば幸いです。

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

[docker][nginx] nginxで簡易ALBを作る

開発、テスト用に複数のアプリを立ち上げたかったので、簡単な ALBツールを作ってみました。(環境はAWS)
ツールを作ることにしたのは、以下のような制約があったからです。

制約:
- サーバ外へ任意のポート公開ができない (80/443のみ)
- ALB 立ち上げ禁止 (本来ならコレ)

アプローチとしては、docker を使っているので nginx のデフォルトのコンテナを使って実現することを考えます。
元々やりたいのはホスト上で複数のアプリを並行稼働させることなので、それぞれ別のポートを割り振って立ち上げておいて、nginx でホスト名によって振分けする形を目指します。

simple-alb.png

振分け先は同じホストで動いている他のコンテナなので、nginx からみるとコンテナ内からホストへ接続することになります。

simple-alb-network.png

なお、ツールとしての簡易版なので、docker イメージを新たに作ったり、プラグイン追加はしないという範囲で進めます。

作成したツールは GitHub に置きました。
https://github.com/batatch/simple-alb

概要

簡易版とはいいつつ、設定内容は AWS ALB のものを模して定義ファイルを用意して、そこから nginx の設定を生成→docker 実行のような構成を目指します。

手順:

$ make build    # 定義ファイルから nginx 設定ファイルを生成
$ make up       # docker-compose で設定ファイルを割り当てて nginx のコンテナを起動
$ make down     # docker-compose で nginx のコンテナを停止

定義ファイル:

alb.yml
---
http:
  listen: 80                       # リスナー、今回は HTTPのみ
  rules:
    - if:                          # ALB の IF 条件
        host: app01.example.com    # ホスト名マッチング
        pathes: [ "/" ]            # パスマッチング
      then:                        # ALB の THEN 文
        forward:                   # 転送設定
          name: tg-app01
          targets:                 # 転送先(複数)、ターゲットグループのようなイメージ
            - target: http://docker0:21080
              weight: 30
            - target: http://docker0:22080
          stickiness: true
    :

手順は Makefile にまとめます。
好みですが、これが最も手軽で分かりやすいと思います。
それから、上記の定義ファイルを元に、以下のような nginx の設定ファイルを作成します。

nginx/conf.d/default.conf
upstream target1 {
    server http://docker0:21080;
    server http://docker0:22080;
}
server {
    listen 80;
    server_name app01.example.com;
       :
    location / {
        proxy_pass http://target1;
    }
}

枠組みはこんな感じ。

テンプレートエンジン

設定ファイルを自動生成するので、なんらかのテンプレートエンジンが欲しいと思って、Ansible などで使われている Python の Jinja2 を使ってみます。

以下のような簡単な Python スクリプトで、テンプレートファイルと、設定ファイルの YAML ファイルから変換結果を得られるようにしました。

j2.py
import sys
import yaml
from jinja2 import Template, Environment, FileSystemLoader

def _j2(templateFile, configFile):
    env = Environment(loader=FileSystemLoader('.', encoding='utf_8'))
    tpl = env.get_template(templateFile)

    with open(configFile) as f:
        conf = yaml.load(f)

    ret = tpl.render(conf)
    print(ret)

if __name__ == '__main__':
    if (len(sys.argv) <= 2):
        print("Usage: j2.pl <template file> <config file>")
        sys.exit(-1)
    _j2(sys.argv[1], sys.argv[2])

コマンドラインはこんな感じ。

$ python j2.pl template.conf.j2 param.yml > output.conf

dockerコンテナからホストへの通信

これがなかなか面倒で、Windowsや Macの Docker環境であれば host.docker.internal で、コンテナ内→ホストへの接続ができるようですが、Linux ではそのような方法が用意されていません。

Linux では docker0 というインターフェースでホスト/コンテナ間をつないでいるらしいので、ホスト側で docker0 に振られた IPアドレスを取得し、環境変数化して docker 起動時に渡すようにしました。

$ env DOCKER0_ADDRESS=$( ip route | awk '/docker0/ {print $9}' ) \
  docker-compose up -d
docker-compose.yml
version: '3'
services:
  alb:
    image: nginx:stable
      :
    extra_hosts:
      - "docker0:${DOCKER0_ADDRESS}"

docker-compose.yml にて extra_hosts にマッピングを書いておくと、コンテナ起動時にコンテナ内の /etc/hosts にホスト名とIPアドレスのマッピングが追記されるので、nginx の設定で名前参照できるようです。

/etc/hosts
----
172.17.0.1      docker0

nginx でロードバランサ

nginx でロードバランサを設定するには http/upstream でターゲットのグループを定義して、http/server/location の proxy_pass で upstream 名を指定する。。想定だったのですが、エラーで起動しません。

どうやら nginx の無償版では upstream 内に書かれたホスト名の DNS解決(resolver) が使えないようです。
(nginx に有償版/無償版があるのを、今回初めて知りました。。)

この件についてまとめられた記事です。
下の Qiita の記事で UNIX ソケットを使う方法が紹介されています。
プラグイン使ったり、docker イメージ作成しないという制約に合っていたので、今回はこちらを採用しました。(設定ファイルが長くなりますが)

Nginxの名前解決についてまとめ
https://ktrysmt.github.io/blog/name-specification-of-nginx/
nginxのupstreamコンテキストで有償のresolveオプションを使わずに動的にDNS解決する
https://qiita.com/minamijoyo/items/183e51a28a3a9d79182f

nginx/conf.d/default.conf
upstream tg-app01 {
    server unix:/var/run/nginx_tg-app01_1;  # (2-1) tg-app01の 1つめのターゲット
    server unix:/var/run/nginx_tg-app01_2;  # (2-2) tg-app01の 2つめのターゲット
}
server {
    listen 80;
    server_name app01.example.com;
        :
    location / {
        proxy_pass http://tg-app01;  # (1) upstream tg-app01 を参照
    }
}
server {
    listen unix:/var/run/nginx_tg-app01_1;  # (2-1) 1つめのターゲットの参照先
    server_name app01.example.com;
        :
    location / {
        proxy_pass http://docker0:21080;
    }
}
server {
    listen unix:/var/run/nginx_tg-app01_2;  # (2-2) 2つめのターゲットの参照先
    server_name app01.example.com;
        :
    location / {
        proxy_pass http://docker0:22080;
    }
}

Websocket の疎通

今回 Websocket を通す必要があったので、以下の設定を行います。
各 server ブロックに必要なようです。

nginx/conf.d/default.conf
# これと
map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      close;
}
    :
server {
    listen 80;
    server_name app02.example.com;

    # ここから
    proxy_set_header Host               $host;
    proxy_set_header X-Forwarded-For    $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Host   $host;
    proxy_set_header X-Forwarded-Server $host;
    proxy_set_header X-Real-IP          $remote_addr;

    proxy_http_version          1.1;
    proxy_set_header Upgrade    $http_upgrade;
    proxy_set_header Connection $connection_upgrade;
    # ここまで

    location / {
        proxy_pass http://tg-app02;
    }
}

nginx 設定ファイルのテンプレート

ここまでの内容を踏まえて、テンプレート定義は以下のようになりました。
細かいですが、+α でデフォルトパターンの指定や固定レスポンスの設定も入っています。

src/default.conf.j2
## http listener settings
map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      close;
}
{% for rule in http.rules %}
{%- if rule.then.forward %}
upstream {{ rule.then.forward.name }} {
    {%- if rule.then.forward.stickiness %}
    ip_hash;
    {%- endif %}
    {%- for tg in rule.then.forward.targets %}
    server {{ 'unix:/var/run/nginx_%s_%d' % (rule.then.forward.name, loop.index) }}{{ ' weight=%d' % tg.weight if tg.weight else '' }};
    {%- endfor %}
}
{%- endif %}
server {
    listen {{ http.listen }}{{ '  default_server' if rule.if.default_server }};
    server_name {{ rule.if.host }};
    {% if rule.then.forward %}
    proxy_set_header Host               $host;
    proxy_set_header X-Forwarded-For    $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Host   $host;
    proxy_set_header X-Forwarded-Server $host;
    proxy_set_header X-Real-IP          $remote_addr;

    proxy_http_version          1.1;
    proxy_set_header Upgrade    $http_upgrade;
    proxy_set_header Connection $connection_upgrade;
    {% endif %}
    {%- for path in rule.if.pathes %}
    location {{ path }} {
        {%- if rule.if.headers %}
        {%- for header in rule.if.headers %}
        if ($http_{{ header|replace('-','_')|lower() }} = "{{ rule.if.headers[header] }}") {
            proxy_pass http://{{ rule.then.forward.name }};
            break;
        }
        {%- endfor %}
        {%- else %}
        proxy_pass http://{{ rule.then.forward.name }};
        {%- endif %}
    }
    {%- endfor %}
    {%- if rule.then.response %}
    location / {
       {%- if rule.then.response.content_type %}
       default_type {{ rule.then.response.content_type }};
       {%- endif %}
       return {{ rule.then.response.code }}{{ ' \'%s\'' % rule.then.response.message if rule.then.response.message }};
    }
    {%- endif %}
}
{%- if rule.then.forward %}
{%- for tg in rule.then.forward.targets %}
server {
    listen {{ 'unix:/var/run/nginx_%s_%d' % (rule.then.forward.name, loop.index) }};
    server_name {{ rule.if.host }};
    {% if rule.then.forward %}
    proxy_set_header Host               $host;
    proxy_set_header X-Forwarded-For    $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Host   $host;
    proxy_set_header X-Forwarded-Server $host;
    proxy_set_header X-Real-IP          $remote_addr;

    proxy_http_version          1.1;
    proxy_set_header Upgrade    $http_upgrade;
    proxy_set_header Connection $connection_upgrade;
    {% endif %}
    location / {
        proxy_pass {{ tg.target }};
    }
}
{%- endfor %}
{%- endif %}
{% endfor %}

docker-compose 設定は以下のとおり。
nginx の設定フォルダを外部ボリュームにマウントして、設定を反映させます。

docker-compose.yml
version: '3'
services:
  alb:
    image: nginx:stable
    ports:
      - "80:80"
    volumes:
      - ./conf.d:/etc/nginx/conf.d
    extra_hosts:
      - "docker0:${DOCKER0_ADDRESS}"
    restart: always

動作確認は以下のとおり。

$ make build    # 設定ファイル変換
$ make up       # nginx コンテナ起動

$ curl http://localhost:80 -i -H "Host:app01.example.com"  # ホスト名を設定してアクセス
HTTP/1.1 200 OK
Server: nginx/1.18.0
Date: Sat, 29 Aug 2020 17:26:23 GMT
Content-Type: text/html
Content-Length: 1863
Connection: keep-alive
Last-Modified: Wed, 11 Mar 2020 05:22:13 GMT
ETag: "747-5a08d6b34ab40"
Accept-Ranges: bytes

<!DOCTYPE html>
<html>
    :

まとめ

AWS ALB っぽい構成要素を YAML 定義に書いて、複雑な nginx の設定を書かなくていいので、管理もしやすくなると思います。
ビルドやプラグインのインストールも不要なので、DockerHub から nginx のイメージの取得さえできれば簡単に ALB 気分になれます。

nginx の設定の勉強にもなりました。(無償版/有償版も知らなった。。)

もっと調べ込んで頑張れば、ALB 本家の設定パターンを実現できるかもしれないですね。(今回はできるだけ手軽に使えるレベルにしたかったので、作り込みはしないようにしました)

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

【Rails環境構築】Docker + Rails + MySQL (初心者も30分でOK!)

Dockerを使った環境構築

「環境構築はコードを書くよりも難しい」と聞いたことがあります。

Dockerは簡単!

何故かと言うと、、、
コピペするだけでいいから
ただし、理解するまで少し時間がかかるかも、、、
まずは簡単な概要から

Docker

仮想環境を構築するための道具

コンテナ

仮想環境そのもの
(DockerはDockerエンジンの上にコンテナが動く)

イメージ

Dockerコンテナを実行するために必要なもの。

環境構築の手順

①.プロジェクト(ディレクトリ)を作成し、移動する

$ mkdir アプリ名
$ cd アプリ名

②.Dockerfile、docker-compose.yml、Gemfile、Gemfile.lockを作成する

$ touch Dockerfile docker-compose.yml Gemfile Gemfile.lock

③.エディタを開き、Dockerfile、docker-compose.yml、Gemfileを下記からコピペする

Dockerfile

Dockerのイメージを自動で生成してくれるファイル

FROM ruby:2.5
RUN apt-get update -qq && apt-get install -y build-essential libpq-dev nodejs

RUN mkdir /myapp
WORKDIR /myapp

COPY Gemfile /myapp/Gemfile
COPY Gemfile.lock /myapp/Gemfile.lock

RUN bundle install
COPY . /myapp

FROM : 使用するイメージとコマンド
RUN : コマンドの実行
WORKDIR : 作業ディレクトリの設定
COPY : コピー元(ホスト側)とコピー先(仮想環境側)のファイルまたはディレクトリを指定

Gemfile

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

docker-compose.yml

version: '3'

services:
    db:
        image: mysql:5.7
        environment:
            MYSQL_USER: root
            MYSQL_ROOT_PASSWORD: password
        ports:
            - "3306:3306"
        volumes:
            - ./db/mysql/volumes:/var/lib/mysql

    web:
        build: .
        command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"
        volumes:
            - .:/myapp
            - gem_data:/usr/local/bundle
        ports:
            - 3000:3000
        depends_on:
            - db 
        tty: true
        stdin_open: true

 volumes:
 gem_data:

version : docker-composeのバージョン。
depends_on : 依存関係を示していて起動順を制御できる。ここでは「db→web」へ起動する。

Docker composeとは

Docker composeとは、複数のコンテナから成るサービスを構築・実行する手順を自動化し、管理を容易にする機能。Docker composeでは、composeファイルを用意してコマンドを一回実行することで、そのファイルから設定を読み込んですべてのコンテナを起動することができる。

serviceについて

Docker composeでは、アプリケーションを動かすための各要素をserviceと呼んでいる。通常はweb(rails)とdb(mysql)と名付ける。

rm -f tmp/pids/server.pidについて

pidとはプロセスIDのことである。pidは、開発用Webサーバーを起動するときにtmp/pids/server.pidに書き込まれ、終了するときに削除される。server.pidにpidが書かれているとサーバーが起動中と判断されてしまう。

portsについて

ports:
  - 3000:3000

上記は、コンテナ内のポート3000番をホストの3000番にマッピングするという意味。これにより、コンテナ内のWebサーバーへhttp://localhost:3000でアクセスできるようになる。

volumeについて

volumes:
   - ./db/mysql/volumes:/var/lib/mysql

は、ホストの./db/mysql/volumesをコンテナ内の/var/lib/mysqlにマウントするという意味。簡単にいうと、Dockerのコンテナと、ローカルを同期している。

volumes :
   - .:/myapp

.がホストのディレクトリ全てを意味し、それをコンテナ内のmyappにマウントしている。

④.下記コマンドを実行する

$ docker-compose run web rails new .--force --database=mysql --skip-bundle

runコマンドではimageの構築から、コンテナの構築・起動までしてくれる。
引数にサービスを指定する必要あり。
docker-compose.ymlのvolumes:- .:/myappの部分でdockerとローカルのディレクトリを同期するように設定しているため、このコマンドを実行後、同時にローカルにも同様のファイルが生成される。

⑤.config/database.yml内の(password: password、host: db)に変更する

default: &default
   adapter: mysql2
   encoding: utf8
   pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
   username: root
   password: password   #passwordを追記しました
   host: db         #localhostからdbに変更しました

⑥.下記コマンドを実行する

$ docker-compose build

Gemfileが更新されたときbuildを実行する

⑦.下記コマンドを実行する

$ docker-compose up -d

docker-compose.yml通りにコンテナが起動する。
docker-compose.ymlの変更を反映させる際にも実行。
-dオプションはバックグラウンド起動。

⑧.下記コマンドを実行する

$ docker-compose run web rails db:create

データベースを作成する。
docker-compose run webでローカルからコマンドを実行できる。(コンテナに入る必要はない)

⑨.下記コマンドを実行し、2つのコンテナが立ち上がっているか確認する。(完成!)

$ docker ps

現在起動しているコンテナを表示するコマンド。
localhost:3000にアクセスするといつものYay!You're on Rails!が表示される。

 コンテナを削除したい場合は下記を実行する

$ docker-compose down

docker psでコンテナが削除されているか確認する。
またコンテナを生成・起動する場合はdocker-compose up -dを実行する。

まとめ

かなり簡単だったんじゃないかなと思います!
これでアプリ開発していきましょう!:thumbsup:

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

初めてopenvinoのface_detectionに触て、及び出会った問題

環境:
centOS7
Docker19.03.12
python3.8.5

OpenVINO™ Model Server---face_detection

https://github.com/openvinotoolkit/model_server

#最新モデルサーバイメージをダウンロード
docker pull openvino/ubuntu18_model_server:latest

#モデルをダウンロード
curl --create-dirs.
https://download.01.org/opencv/2020/openvinotoolkit/2020.2/open_model_zoo/models_bin/3/face-detection-retail-0004/FP32/face-detection-retail-0004.xml 
https://download.01.org/opencv/2020/openvinotoolkit/2020.2/open_model_zoo/models_bin/3/face-detection-retail-0004/FP32/face-detection-retail-0004.bin 
-o model/face-detection-retail-0004.xml -o model/face-detection-retail-0004.bin

# 9000ポートでgRPC用のコンテナを起動
docker run -d -v $(pwd)/model:/models/face-detection/1 -e LOG_LEVEL=DEBUG -p 9000:9000 
openvino/ubuntu18_model_server /ie-serving-py/start_server.sh ie_serving model --model_path /models/face-detection --model_name face-detection --port 9000  --shape auto

#クライアントスクリプトのダウンロード
curl https://raw.githubusercontent.com/openvinotoolkit/model_server/master/example_client/client_utils.py -o client_utils.py 
https://raw.githubusercontent.com/openvinotoolkit/model_server/master/example_client/face_detection.py -o face_detection.py  
https://raw.githubusercontent.com/openvinotoolkit/model_server/master/example_client/client_requirements.txt -o client_requirements.txt

#分析するイメージをダウンロード
curl --create-dirs 
https://raw.githubusercontent.com/openvinotoolkit/model_server/master/example_client/images/people/people1.jpeg
-o images/people1.jpeg

#クライアント依存関係のインストール
pip install -r client_requirements.txt

#結果のフォルダを作成
mkdir results

#推論を実行し、結果を新しく作成したフォルダに保存
python face_detection.py --batch_size 1 --width 600 --height 400 --input_images_dir images --output_dir results

問題点は:

ModuleNotFoundError: No module named 'cv2’
解決:

pip install opencv-python

ModuleNotFoundError: No module named 'grpc'
解決:

python -m pip install grpcio

ModuleNotFoundError: No module named 'tensorflow'
解決:

pip install --upgrade --ignore-installed tensorflow

ModuleNotFoundError: No module named 'tensorflow_serving'
解決:

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

In file './docker-compose.yml', the service name True must be a quoted string, i.e. 'True'.

DockerでRailsの環境構築中 docker-compose run web rails new . --force --no-deps --database=postgresql --skip-bundle をしたら、In file './docker-compose.yml', the service name True must be a quoted string, i.e. 'True'.のエラーが出たのでメモします。

原因

誤字

e: '3'
services:
  db:
    image: postgres
    volumes:
      - ./tmp/db:/var/lib/postgresql/data
  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

e: '3'

ここをversion: '3'に修正したら、動いた

修正後

version: '3'
services:
  db:
    image: postgres
    volumes:
      - ./tmp/db:/var/lib/postgresql/data
  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
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Python製WebアプリケーションをDockerで運用する

前回:EmbyをDockerで運用する
これはそろそろDockerを使えるようになりたい筆者の試行錯誤の軌跡です。

Python製WebアプリケーションをDockerで

前回はメディアストリーミングサービスのEmbyをコンテナに移行しました。
Untitled(11).png

今回はPython製Webアプリケーションを展開できるDockerイメージを作り、
Web Socket使用のアプリケーションとDjangoアプリケーションをDockerに移行します。
ついでに静的コンテンツもコンテナに移動し、VMサーバーを閉じます。
Untitled(12).png

静的コンテンツ

VMサーバーにあったものをDockerホストに移動し、コンテナ起動時に配信ディレクトリへバインドしました。

docker-compose.yml
    deploy:
        image: nginx
        restart: always
        ports:
            - 80:80
            - 443:443
        volumes:
            - /home/fclef/docker/server/deploy/nginx/conf:/etc/nginx/conf.d
+           - /home/fclef/docker/server/deploy/content/html:/usr/share/nginx/html

Python製アプリケーションのDocker運用

Web Socket使用アプリケーションもPython製(bottle)なので、Python製アプリケーションを簡単に展開できるコンテナを目指します。

コンテナ設計

僕はよくPythonでWebアプリケーションを作ります。
なので、アプリケーションごとイメージに固めてしまうというよりは、
どんなPythonアプリケーションでも動かせる土台をイメージ化し、
具体的なアプリケーション自体はgitの情報をコンテナ起動時に渡して、コンテナ内でクローン、展開させます。
Untitled(9) (1).png

Dockerfile

僕がPythonで何かを作るときは必ずPipenvを使います。
また、Webアプリケーションのときはソケット化してnginxで配信するので、
イメージの時点でPipenv, nginxをインストールしておきます。

Dockerfile
FROM python:3.8-slim-buster

# 依存パッケージのインストール
RUN apt-get -y update
RUN apt-get -y upgrade
RUN apt-get install -y git make build-essential libssl-dev zlib1g-dev libbz2-dev libreadline-dev libs
qlite3-dev wget curl llvm libncurses5-dev libncursesw5-dev xz-utils tk-dev libffi-dev nginx

# Pyenvのインストール
RUN git clone https://github.com/yyuu/pyenv.git /.pyenv
ENV PYENV_ROOT /.pyenv
ENV PATH $PYENV_ROOT/bin:$PATH
RUN pyenv init - | bash
# Pipenvのインストール
RUN pip install pipenv
ENV PIPENV_VENV_IN_PROJECT 1
ENV PIPENV_YES true

RUN mkdir /app # アプリケーション配備ディレクトリ
RUN mkdir /script # 各種スクリプト配置ディレクトリ

COPY up.sh /script # 起動スクリプト

# 起動スクリプト内でgitプロジェクトをappにクローンするので移動しておく
WORKDIR /app
# Webアプリケーションの配信はメインWebサーバを介するリバースプロキシなので、80ポートだけ空けておく
EXPOSE 80
RUN chmod +x /script/up.sh
ENTRYPOINT [ "/script/up.sh" ] # コンテナ起動時、起動スクリプトup.shを実行する

コンテナ起動スクリプト

アプリケーションのgit情報はコンテナ起動時に環境変数で渡します。
僕の環境ではなぜかsshでのgitのクローンができないため、httpsでクローンする前提です。
クローンしたら、プロジェクトルートディレクトリにあるPipfileを解析し、必要なPythonバージョンを決定します。
Pythonライブラリの中には、依存するプログラムがインストールされている必要があるケースがあるので、
それらをインストールするためのスクリプト(dependencies.sh)をコンテナ起動時にバインドしておき、ここで呼び出します。
Python製Webアプリケーションのソケット化はgunicornを使うので、gunicornもインストールしておきます。

up.sh
#!/bin/bash

# 環境変数からgit情報を取得する
gitCloneUrl=${GIT_CLONE_URL}
gitUserName=${GIT_USER_NAME}
gitPassword=${GIT_PASSWORD}
gitCloneTarget=`echo ${gitCloneUrl} | sed -r "s#^(https?://)(.*)#\1${gitUserName}:${gitPassword}@\2#g
"`

# プロジェクト名を取得する
projectName=`echo ${gitCloneUrl} | sed -r "s#.*/(\w+)\.git#\1#g"`
echo "■ ■ ■ PROJECT NAME <${projectName}> ■ ■ ■"

git clone ${gitCloneTarget}

# Pipfileからpythonバージョンを取得する
cd ${projectName}
pythonVersion=`grep python_version Pipfile | sed -r 's/python_version = "(.+)"/\1/g'`
echo "■ ■ ■ PYTHON VERSION <${pythonVersion}> ■ ■ ■"

# インストールするPythonライブラリの中に他プログラムに依存するものがある場合は、ここでインストールしておく。
if [ -f /script/dependencies.sh ]; then
    source /script/dependencies.sh
fi

pipenv --python ${pythonVersion}
pipenv install
pipenv install gunicorn

curPath=`pwd`
export APP_ROOT=${curPath}

chmod +x /script/run.sh
service nginx start
/script/run.sh # アプリケーション起動スクリプト
while true; do sleep 1000; done

コンテナの起動

例として、Djangoアプリケーションを起動する構成をお見せします。

docker-compose.yml抜粋
django:
    image: pipenv-gunicorn
    restart: always
    environment:
        GIT_CLONE_URL: https://xxxxxxxx/user/project.git
        GIT_USER_NAME: user
        GIT_PASSWORD: password
    volumes:
        - /home/fclef/docker/server/app/dependencies.sh:/script/dependencies.sh
        - /home/fclef/docker/server/app/run.sh:/script/run.sh
        - /home/fclef/docker/server/app/app.conf:/etc/nginx/conf.d/nginx_gunicorn.co
nf
    depends_on:
        - deploy
        - gitlab
dependencies.sh
apt-get -y --no-install-recommends install libpq-dev
run.sh
cd /app/project

source .venv/bin/activate

python manage.py collectstatic --noinput
python manage.py makemigrations
python manage.py migrate

deactivate

/app/project/.venv/bin/gunicorn \
          --access-logfile /var/log/socket_success.log \
          --error-logfile /var/log/socket_error.log \
          --workers 1 \
          --bind unix:/run/socket.sock \
          config.wsgi:application
app.conf
server {
    listen       80;
    listen [::]:80;
    server_name xxxx.xxx.xxx;

    root /app;

    location /static {
        alias /app/project/static;
    }

    location / {
        include /etc/nginx/proxy_params;
        proxy_pass http://unix:/run/socket.sock;
    }

    location ~ ^/apple-touch-icon(.*)\.png$ {
        root /app/project/;
        rewrite ^/apple-touch-icon(.+)\.png$ /static/img/apple-touch-icon.png break;
    }
}

アプリケーション本体はコンテナ起動時にgitからクローン、
データは別コンテナのPosgreSQLに保存してあるので、
このコンテナは何度作成し直しても正常に運用状態のサービスを展開できます。

基本的に上記イメージと起動方法で大抵のPythonアプリケーションは動かせるようになりました。
gitクローンからデプロイまでの手順が単純なのでアプリケーションを外出しして再利用しやすいイメージが作成できましたが、
環境構築が複雑なアプリケーションの場合は、アプリケーションごとイメージにしてしまった方が保守性があがるかもしれません。

以上、VMサーバー群をコンテナに移行するシリーズでした。

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

S3 と同等の機能を持つ MinIOの使用方法 docker-compose を使用

概要

諸所諸々の事情(契約の問題や予算など)でAWSのサービスが使えない状況でも開発するためにdockerで使えるようにしてみました。
タイトルはMinIOですがほかにDynamoDB,MySQLも使えるようにしてあります。

事前準備

  • dockerをまず導入します。windows Homeの場合は Docker Quickstart Terminal を使います。
  • アプリの方はPythonを使っているため pythonのインストール(記事作成時は3.7です)。
    • JupyterNotebookなどでも可能です。
  • boto3

docker-compose

  • 今回は docker-compose で起動させるためymlを作成します。
    • 公式を参考に作成します。
    • そのままではInvalid command line arguments: use path style endpoint for FS setup.のエラーになってしまったのでこちらを参考に修正しました。
    • あくまで開発用のため1つだけにしていますが本来は4つnodeを使うようです。
  • MinIO以外にMySQLとDynamoDBも使えるようにしています。
  • 最終的なファイルは以下になります。

  • docker-compose.yml

version: '3.7'
services:
  dynamodb-local:
    build: ./dynamodb
    volumes:
      - dynamodb_data:/home/dynamodblocal/data
    ports:
      - 8000:8000
    command: -jar DynamoDBLocal.jar -dbPath ./data
  mysql:
    image: mysql:5.7
    command: mysqld --character-set-server=utf8 --collation-server=utf8_unicode_ci
    volumes:
      - db_data:/var/lib/mysql
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: 'password'
    ports:
      - 3306:3306
  minio1:
    image: minio/minio:RELEASE.2020-08-27T05-16-20Z
    volumes:
      - data1-1:/data1
      - data1-2:/data2
    ports:
      - "9000:9000"
    environment:
      MINIO_ACCESS_KEY: minio
      MINIO_SECRET_KEY: minio123
    command: server data1
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"]
      interval: 30s
      timeout: 20s
      retries: 3
volumes:
  dynamodb_data:
  db_data: {}
  data1-1:
  data1-2:
  • DynamoDBのDockerファイル
FROM amazon/dynamodb-local

WORKDIR /home/dynamodblocal

# UID=1000 → DynamoDB Local の実行ユーザ
RUN mkdir data && chown -R 1000 data
  • 起動すると以下のようにエンドポイントが表示されているのでメモ等取っておきます。
    • Docker Quickstart Terminal の場合はIPアドレスの部分をdocker 起動時に表示されるIPに読み替えてください。
minio1_1          | Endpoint:  http://172.20.0.4:9000  http://127.0.0.1:9000
minio1_1          |
minio1_1          | Browser Access:
minio1_1          |    http://172.20.0.4:9000  http://127.0.0.1:9000
  • http://192.168.99.100:9000/minio/ (Docker Quickstart Terminal のためIPアドレスが違っています)にアクセスすることでブラウザ上でバケットの中などの確認や作成などを行うことができます。

S3機能の実装

  • bucketの作成~ファイルアップロードまでのソースは以下のようになります。
  • S3にアップロードする際のディレクトリ構成を os.path.join で作成すると \\で接続されてしまうのでreplaceで変換しています。
import boto3
import os
from datetime import datetime

endpoint_url = os.getenv('S3_ENDPOINT', 'http://192.168.99.100:9000/')
aws_access_key_id = os.getenv('S3_ACCESS_KEY_ID', 'minio')
aws_secret_access_key_id = os.getenv('S3_SECRET_ACCESS_KEY_ID', 'minio123')

s3 = boto3.resource(
    's3',
    aws_access_key_id=aws_access_key_id,
    aws_secret_access_key=aws_secret_access_key_id,
    endpoint_url=endpoint_url
)

bucket=s3.Bucket('testbukcet')
bucket.create()
bucketPath=os.path.join('dir',datetime.now().strftime('%Y%m%d%H%M%S'),'out','test.csv')
filePath=bucketPath.replace('\\','/')
bucket.upload_file('test.csv',filePath)

  • ブラウザで確認すると以下のように確認できます。 image.png
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む