- 投稿日:2019-05-22T23:37:27+09:00
UploadしてPlotするだけの簡単なhttp経由の可視化ツールを作った
Just Upload & Plot. Easy visualize tool via http.
UplodしてPlotするだけの簡単なhttp経由の可視化ツールです。
csvまたはxlsファイルをブラウザ上のUpload欄に上げるだけでさまざまな形式のグラフを描きます。
csv, xlsを作成時の注意
- 1行目の1列目はx軸のタイトルになります。
- 1列目の2行目以降はx軸になります。
- 1行目の2列目以降は凡例になります。
- 2行目以降の2列目以降がデータになります。
- ファイル名はグラフタイトルになります。
- ファイル名に
_が含まれている時、最初の_で区切られて、前半部分がグラフタイトル、後半部分がy軸のタイトルになります。対応しているグラフ形式
- 'Line'
- 'Bar'
- 'Histogram'
- 'Pie'
- 'Polar'
- 'Box'
- 'Heatmap'
- '3D Scatter'
- '3D Surface'
- '2D Histogram'
対応予定のグラフ形式
- 'Contour'
- 'Candlestick'
Install
pip
あとでpypi登録予定
Github
GitHub u1and0/uplot
クローン後、$ python uplot.pyDockerhub
$ sudo docker pull u1and0/uplot $ sudo docker run -d -p 8880:8880 u1and0/uplotUSAGE
- サーバーを立ち上げたらブラウザに
http//:localhost:8880と打ち込みます。- csvかxlsで作成したファイルをドラッグ・アンド・ドロップしてグラフ種類を選択します。
ScreenShots
作り方
python製
dash (= plotly + flask)でHTMLパーツを配置していって、plotlyで可視化。サーバーをflaskで立てる。uplot.py#!/usr/bin/env python3 import base64 import datetime import io import os from collections import defaultdict import dash from dash.dependencies import Input, Output, State import dash_core_components as dcc import dash_html_components as html import dash_table import pandas as pd import plotly.graph_objs as go external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css'] app = dash.Dash(__name__, external_stylesheets=external_stylesheets) CHART_LIST = [ 'Line', 'Bar', 'Histogram', 'Pie', 'Polar', 'Box', 'Heatmap', # 'Contour', # 'Candlestick', '3D Scatter', '3D Surface', '2D Histogram', ] CHART_LIST.sort() app.layout = html.Div( [ # File upload bunner dcc.Upload( id='upload-data', children=html.Div(['Drag and Drop or ', html.A('Select Files')]), style={ 'width': '100%', 'height': '60px', 'lineHeight': '60px', 'borderWidth': '1px', 'borderStyle': 'dashed', 'borderRadius': '5px', 'textAlign': 'center', 'margin': '10px' }, # Allow multiple files to be uploaded multiple=True), html.H6('chart-type'), dcc.Dropdown(id='chart-type', options=[{ 'label': i, 'value': i } for i in CHART_LIST], value='Line'), html.H6('x-axis'), dcc.RadioItems(id='xaxis-type', options=[{ 'label': i, 'value': i } for i in ['linear', 'log', 'category']], value='linear', labelStyle={'display': 'inline-block'}), html.H6('y-axis'), dcc.RadioItems(id='yaxis-type', options=[{ 'label': i, 'value': i } for i in ['linear', 'log', 'category']], value='linear', labelStyle={'display': 'inline-block'}), html.Div(id='the_graph'), html.Div(id='output-data-upload'), ], ) def data_graph( df, filename, chart_type, xaxis_type, yaxis_type, ): """アップロードされたデータのグラフを描画""" basename = os.path.splitext(filename)[0] # ファイル名の1つ目の'_'で区切って、グラフタイトルとY軸名に分ける if '_' in basename: title, yaxis_name = basename.split('_', 1) # ファイル名に'_'がなければグラフタイトル、Y軸名ともにファイル名 else: title, yaxis_name = basename, basename def args(i): """graph_objs helper func""" return {'x': df.index, 'y': df[i], 'name': i} # チャートの種類をディクショナリで分岐 # 内包表記でdfの列の数だけトレース data = { 'Line': [go.Scatter(args(i)) for i in df.columns], 'Bar': [go.Bar(args(i)) for i in df.columns], 'Histogram': [go.Histogram(x=df[i], name=i, opacity=.5) for i in df.columns], 'Pie': [ go.Pie(labels=df.index, values=df[i], name=i, domain={'column': list(df.columns).index(i)}) for i in df.columns ], 'Polar': [ go.Scatterpolar( r=df[i], theta=df.index, name=i, ) for i in df.columns ], 'Heatmap': [go.Heatmap(x=df.index, y=df.columns, z=df.values)], 'Box': [go.Box(y=df[i], name=i) for i in df.columns], # 'Contour': [go.Contour(x=df.index, y=df.columns, z=df.values)] '3D Scatter': [ go.Scatter3d(x=df.index, y=df.columns, z=df[i], name=i) for i in df.columns ], '3D Surface': [ go.Surface(x=df.index, y=df.columns, z=df.values, name=yaxis_name, contours=go.surface.Contours( z=go.surface.contours.Z(show=True, usecolormap=True, highlightcolor="#42f462", project=dict(z=True)))), ], '2D Histogram': [go.Histogram2d(x=df.iloc[:, 0], y=df.iloc[:, 1])] } # チャートの種類でレイアウトを分岐 # 分岐にはdefaultdictを使い、デフォルトはlambda式で返す layout = defaultdict( # default layout lambda: go.Layout(title=go.layout.Title(text=title), xaxis={ 'type': xaxis_type, 'title': df.index.name, 'rangeslider': dict(visible=False), }, yaxis={ 'type': yaxis_type, 'title': yaxis_name, }, margin={ 'l': 60, 'b': 50 }, hovermode='closest'), # other layout { 'Histogram': go.Layout(title=title, xaxis={'title': 'Value'}, yaxis={'title': 'Count'}, barmode='overlay', hovermode='closest'), 'Pie': go.Layout(title=go.layout.Title(text=title), grid={ 'columns': len(df.columns) - 1, 'rows': 1 }, hovermode='closest') }) return dcc.Graph(id='the_graph', figure={ 'data': data[chart_type], 'layout': layout[chart_type] }) def data_table(df): """アップロードされたデータの表を描画""" df.reset_index(inplace=True) # indexもテーブルに含めるため data = df.to_dict('records') columns = [{'name': _i, 'id': _i} for _i in df.columns] return dash_table.DataTable(data=data, columns=columns) def parse_contents(contents, filename, date, chart_type, xaxis_type, yaxis_type): content_type, content_string = contents.split(',') decoded = base64.b64decode(content_string) try: if 'csv' in filename: # Assume that the user uploaded a CSV file df = pd.read_csv(io.StringIO(decoded.decode('utf-8')), index_col=0, parse_dates=True) elif 'xls' in filename: # Assume that the user uploaded an excel file df = pd.read_excel(io.BytesIO(decoded), index_col=0, parse_dates=True) except Exception as e: print(e) return html.Div(['There was an error processing this file.']) return html.Div([ data_graph(df, filename, chart_type, xaxis_type, yaxis_type), html.H5(filename), html.H6(datetime.datetime.fromtimestamp(date)), data_table(df), html.Hr(), # horizontal line # For debugging, display the raw contents provided by the web browser html.Div('Raw Content'), html.Pre(contents[0:200] + '...', style={ 'whiteSpace': 'pre-wrap', 'wordBreak': 'break-all' }) ]) @app.callback(Output( 'output-data-upload', 'children', ), [ Input('upload-data', 'contents'), Input('chart-type', 'value'), Input('xaxis-type', 'value'), Input('yaxis-type', 'value'), ], [State('upload-data', 'filename'), State('upload-data', 'last_modified')]) def update_output(list_of_contents, chart_type, xaxis_type, yaxis_type, list_of_names, list_of_dates): if list_of_contents is not None: children = [ parse_contents(c, n, d, chart_type, xaxis_type, yaxis_type) for c, n, d in zip(list_of_contents, list_of_names, list_of_dates) ] return children if __name__ == '__main__': app.run_server(debug=True, host='0.0.0.0', port=8880)
- 投稿日:2019-05-22T21:47:37+09:00
Dockerfileのbuild時の留意点備忘録
- 投稿日:2019-05-22T19:48:00+09:00
Rancherコンテナー管理プラットフォーム(V2.2.3)のハンズオン手順
Rancherのインストール(Single Node構成)
★★今回のハンズオンには、こちらからインストール済のサーバーを提供致します為、実施不要★★
SSL証明書オプション:デフォルトは自己署名証明書(Rancherが自動生成)コマンド:rootユーザーに切替docker run -d --restart=unless-stopped \ -p 80:80 -p 443:443 \ -v /data/rancher:/var/lib/rancher/ \ rancher/rancher:stableKubernetesクラスター構築と管理
・RancherへのログインURLとアカウント情報は下記となります。
※メールの内容をご参照ください。
※ログイン用のアカウント情報は、admin/adminとなっています。
受講者No(NN) ログインURL ・自己署名証明書を利用しています為、ワーニング画面が表示されますが、下記の画面のようにワーニングエラーを無視して接続してください。
・ユーザーとパスワードを入力してください。
※UI言語については、日本語版も対応されていますが、今回のハンズオンの説明資料は、English版で作成されています。
クラスター構築
・「Add Cluster」画面に、必要な情報を入力して、「Create」ボタンを押下
入力情報
ー構築方式: Amazon EC2アイコン
ーCluster Name: NN-ks ※NNの部分は受講者番号(01~07)に置き換えーName Prefix: NN- ※NNの部分は受講者番号(01~07)に置き換え
ーCount: 3 ※クラスタのノード数
ーTemplate: aws-ec2-node ※こちらで事前に用意してあるAWS認証情報が入ってるノードテンプレートです。用意されてない場合は、ノードテンプレートの設定手順で事前にご設定してください。
ーetcd: ☑️ ※etcdのノード数は奇数にする必要がある
ーControl Plane: ☑️ ※kube-Controlのノード数
ーWorker: ☑️ ※ワーカーのノード数ーCloud Provider: Amazon
・クラスタのStateが「Active」になってれば、作成完了
・クラスタのダッシュボードを確認
・Rancherの管理対象の階層の確認(クラスタレベル→プロジェクトレベル)
クラスター証明書の更新
・クラスタダッシュボード画面に、「…」から、「Rotate Certificates」メニューをクリックします。
・「Rotate all service certificates」を選択して、Saveを押下します。※全てのコンポーネントの証明書を更新します。
・クラスタのアップデート中の赤いワーニングメッセージ消えるまで待ちます。
クラスターのバックアップとリストア
・クラスタダッシュボード画面に、「…」から、「Snapshot Now」メニューをクリックします。
・クラスタダッシュボード画面に、「…」から、「Restore Snapshot」メニューをクリックします。
・Snapshotリスト、先ほどバックアップしたSnapShotを確認して、一番日付が古いSnapshotを選択して、Saveを押下し、リストアさせます。
・クラスタのアップデート中の赤いワーニングメッセージ消えるまで待ちます。
クラスターのバージョンアップ
・クラスタダッシュボード画面に、「…」から、「Edit」メニューをクリックします。
・クラスタの編集画面の「Kubernetes Version」のところの利用可能のバージョンを確認します。
クラスターのバージョンアップには、時間がかかります為、今回のハンズオンには、実行しません。興味ある方は別途で行ってください。
クラスターのノードのスケールアウトとスケールイン
■スケールアウト:1つのWorkerノードを追加します。
・クラスタダッシュボード画面に、「…」から、「Edit」メニューをクリックします。
・クラスタ編集画面に、「Add Node Pool」ボタンをクリックします。
・追加した「Node Pools」行に、必要な情報を入力して、「Save」ボタンを押下
入力情報
ーName Prefix: NN- ※NNの部分は受講者番号(01~07)に置き換え
ーCount: 1
ーTemplate: aws-ec2-node ※こちらで事前に用意してあるAWS認証情報が入ってるノードテンプレートです。
ーWorker: ☑️ ※ワーカーのノード数
・ノードの作成中の赤いワーニングメッセージ消えるまで待ちます。
※AWS上にEC2を作成して、インスタンスの初期化処理を行う為、少し、時間かかります。
ワーカーの追加の為、クラスタは使用可能の状態です。待っている間に色々触ってください。
・クラスタのノード管理画面に作成したノード情報を確認します。
■スケールイン:追加したのWorkerノードを削除します。
・クラスタダッシュボード画面に、「…」から、「Edit」メニューをクリックします。
・クラスタ編集画面に、追加した「Node Pools」行の最後の「ー」ボタンをクリックして、「Save」ボタンを押下
※Workerしか☑️してない行です。削除対象を間違わないようにしてください。
・クラスタのノード管理画面に削除後のノード情報を確認します。
クラスターのプライベートなDockerレジストリ追加(オプショナル:プライベートなDockerレジストリを持つ、且つ興味ある方はやってください)
・クラスタダッシュボード画面に、「…」から、「Edit」メニューをクリックします。
・クラスタ編集画面に、下記の画面操作ように、プライベートなDockerレジストリ追加して、「Save」ボタンを押下
クラスタ配下のプロジェクト管理
・クラスタ階層のメニューバーに、「Projects/NameSpaces」メニューをクリックし、Projects管理画面に「Add Project」を押下
・Project追加画面に、下記の情報を入力し、「Create」ボタンを押下
ーProject Name:InfraTeam
・メニューバーに、クラスター配下に、InfraTeamプロジェクトが入っていることを確認し、InfraTeamプロジェクトをクリックします
・「NameSpaces」をクリックし、「Add Namespace」ボタンを押下
・Namespace追加画面に、下記の情報を入力し、「Create」ボタンを押下
ーName:InfraTeamSub1
※「GKE」と「Custom」アイコンからのクラスタ作成いついて、最後に時間の余裕があれば、デモします
ユーザー認証
ユーザー認証方式 - 認証プロバイダ経由の認証
・Globalのメニュー階層で、「Authentication」メニューをクリック
・Authentications設定画面に、対象の認証プロバイダを選択して、接続情報や設定情報を入力して、保存します。※認証プロバイダは用意していない為、接続までの操作はハンズオン対象外です。興味ある方は、別途に試してください。
ユーザー認証方式 - ローカル認証
・Globalのメニュー階層で、「Users」メニューをクリック
構築したnn-ksクラスタにクラスタ管理者ユーザー(infra-admin)を追加
・「Users」画面に、右上の「Add User」ボタンを押下し、下記の画面のように、ユーザーを作成します。
・クラスタダッシュボード画面に、「…」から、「Edit」メニューをクリックします。
・権限付与。クラスタ編集画面に、「Add Member」ボタンを押下し、追加された入力行に作成したユーザーを「Owner」として入力し、「Save」ボタンを押下
構築したnn-ksクラスタにプロジェクト管理者ユーザー(project-admin)を追加
・「Users」画面に、右上の「Add User」ボタンを押下し、下記の画面のように、ユーザーを作成します。
・メニューバーに、クラスター配下に、InfraTeamプロジェクトが入っていることを確認し、InfraTeamプロジェクトをクリックします
・「Member」メニューをクリックし、「Add Member」ボタンを押下
・権限付与。プロジェクトメンバー追加画面に、下記の画面ように入力して、「Add Member」ボタンを押下
クラスタ管理者ユーザー(infra-admin)の動作確認
・infra-adminユーザーでログインして、「クラスター証明書の更新」ができることを確認します。※手順は、「クラスター証明書の更新」をご参照ください。
プロジェクト管理者ユーザー(project-admin)の動作確認
・project-adminユーザーでログインして、「NameSpaces作成」ができることを確認します。
・「NameSpaces」をクリックし、「Add Namespace」ボタンを押下
アプリデプロイとアプリ管理
・adminユーザーでログインして、下記の手順を行います。
nginxアプリをデプロイ
・メニューバーに、クラスター配下のInfraTeamをクリックし、InfraTeamプロジェクトに遷移します。
・右上の「Deploy」ボタンをクリックし、開く「Deploy Workload」画面に、必要な情報入力して、「Launch」ボタンを押下
入力情報
ーName:nginx-test
ーDocker Image:nginx:1.9
クラスタの外部からnginxアプリにアクセス - NodePort方式
・メニューバーの下の「Service Discovery」タブをクリックして、「Service Discovery」画面を開きます。
・「Service Discovery」画面の右上の「Add Record」ボタンを押下し、開く「Add Record」画面の「Show advanced options」をさらにクリックします。
・「Add Record」画面に、下記のような内容を入力して、「Create」ボタンを押下。
・nginx-nodeportサービスが作成されていることを確認し、サービス名のnginx-nodeport直下のportのリンクをクリックし、Nginx画面が表示されることを確認。
クラスタの外部からnginxアプリにアクセス - LoadBalancer方式
・メニューバーの下の「Service Discovery」タブをクリックして、「Service Discovery」画面を開きます。
・「Service Discovery」画面の右上の「Add Record」ボタンを押下し、開く「Add Record」画面の「Show advanced options」をさらにクリックします。
・「Add Record」画面に、下記のような内容を入力して、「Create」ボタンを押下。
・nginx-lbサービスが作成されていることを確認し、サービス名のnginx-lb直下のportのリンクをクリックし、Nginx画面が表示されることを確認。
※LoadBalancerはAWS側で非同期で作成されるため、アクセスできるまで2、3分かかる場合があります。クラスタの外部からnginxアプリにアクセス - Ingress方式
・メニューバーの下の「Load Balancing」タブをクリックし、開く「Load Balancing」画面の右上の「Add Ingress」ボタンをクリックします。
・「Add Ingress」画面に、下記のような内容を入力して、「Create」ボタンを押下。
・nginx-ingressが作成されていることを確認し、URLのリンクをクリックし、Nginx画面が表示されることを確認。
nginxアプリのスケールアウト
・メニューバーの下の「Workloads」タブをクリックし、開く「Workloads」画面のリストに、nginx-testのリンクをクリックします。
・開く「Workload: nginx-test」画面に、「+」ボタンを押下します。
・nginx-testアプリのPODが2つになっていることを確認します。
nginxアプリのバージョンアップとロールバック
・「Workloads」画面のリストに、nginxアプリの行の「…」から、「Edit」メニューをクリックします。
「Upgrade Service」画面に、Docker Imageを「nginx:1.10」に変更して、「Upgrade」ボタンを押下します。
・「Workloads」画面のリストに、nginxアプリが「nginx:1.10」に変更されていることを確認します。
・「Workloads」画面のリストに、nginxアプリの行の「…」から、「Rollback」メニューをクリックします。
・一番古いバージョン(初期のバージョン)を選択して、「Rollback」バタンを押下
・「Workloads」画面のリストに、nginxアプリが「nginx:1.9」に戻っていることを確認します。
アプリのデバッグ ー コンテナー接続
・メニューバーの下の「Workloads」タブをクリックし、開く「Workloads」画面のリストに、nginx-testのリンクをクリックします。
・開く「Workload: nginx-test」画面に、任意のPODの行の「…」から、「Execute Shell」リンクをクリックします。
アプリのデバッグ ー コンテナーログ参照
・開く「Workload: nginx-test」画面に、任意のPODの行の「…」から、「View Logs」リンクをクリックします。
永続化ストレージの使用
Storage Classの作成
・クラスタメニューの階層に、「Storage」→「Storage Classes」メニューをクリックします。
・開く「Storage」画面の右上の「Add Class」ボタンをクリックします。開く「Add Storage Class」画面に、NameとProvisionerを入力して、「Save」ボタンをクリックします
Storage Classの使用(Storage Classに指定しているProvisionerで動的にPersistent Volumeを作成させる)
・プロジェクト(infraTeam)メニューの階層に、Workloads画面に、右上の「Deploy」ボタンをクリックします。
・開く「Deploy Workload」画面、下記のように、設定情報を入力して、「Add Volume」ボタンをクリックします。
・「Add a Volume Claim Template」メニューをクリックします。
・「Add Volume Claim Template」画面、下記のように、設定情報を入力して、「Define」ボタンをクリックします。
・「Deploy Workload」画面、「Mount Point」を入力して、「Launch」ボタンをクリックします。
※aws側で、Provisionerで動的にVolumeを生成して、PODがいるインスタンスにマウントしますため、少しかかります。Storage Classによる動的に生成したPVの確認
・クラスタメニューの階層に、「Storage」→「Persistent Volumes」メニューをクリックします。動的に生成したPVを確認します。
・PVのリンクをクリックして、PVの詳細を確認します。
運用監視機能
モニタリング機能の有効化
・クラスタメニューの階層に、「Tools」→「Monitoring」メニューをクリックします。
・下記の操作ように、デフォルトで搭載されているモニタリング機能を有効にします。
・下記の赤いワーニングが消えるまで、待ちます。※モーニングの関連リーソスがクラスタにデプロイされています。
モニタリング機能の確認
・クラスタのダッシュボード上に、メトリックのサマリー情報を確認
・Grafana上に、メトリック情報を確認
・Grafana上に、先ほどの手順にデプロイしたアプリのメトリックも確認
アラートの連携・通知先の設定
・クラスタメニューの階層に、「Tools」→「Notifiers」メニューをクリックします。
・「Add Notifier」ボタンをクリックします
・「Notifier」画面、下記のように、設定情報を入力し、「TEST」ボタンでテストしてから、「Add」ボタンをクリックします。
入力情報(SlackのWebhookとチャンネルはこちら用意してある下記のものを設定)
ーName:slack-test
ーURL:Slack側のhookのURL
ーDefault Channel:#rancher-alert
アラート機能の確認
・クラスタメニューの階層に、「Tools」→「Alerts」メニューをクリックします。
・デフォルトで、etcd、kube-componetns、eventとnodeなどの基本のアラートグループとアラートルールが設定されています。
アラートグループとアラートルールをカスタマイズで作成
・「Cluster Alert Groups」画面の右上の「Add Alert Group」ボタンをクリックします。
・「Add Alert Group」画面、下記のように設定情報を入力し、「Create」ボタンをクリックします。
ーExpression:sum(node_load1) by (instance) / count(node_cpu_seconds_total{mode="system"}) by (instance) * 100
・Slackのチャンネルにメッセージが通知されるメッセージを確認します。約1分間後に通知されます。
カタログ機能
※カタログ機能について、最後に時間の余裕があれば、デモします。
- 投稿日:2019-05-22T18:39:46+09:00
Dockerの使い方
Dockerの基本概念
Dockerとはアプリケーションを開発・移動・実行するためのプラットフォームです。
設計思想は開発者が簡単にアプリケーションを動かす環境を作る。Docker Container
- 実際動かすアプリケーションの環境
- コンテナ毎にリソースをもつ
- 仮想環境と同じような考え方ですが、コンテナはアプリケーション単位
- 整合性の高いアプリケーションや、それに関する環境設定を同じコンテナに設定すればよい
Docker Image
- コンテナを実行するためのファイルシステム
- 仮想環境のイメージファイルと相当
- バージョン管理はGitlabなどのWebサービスを利用してもよいし、ローカルでも管理出来ます。
Docker File
- イメージを作成するための設定ファイル。
- Imageの環境構築手順を書けます。
- ソースファイルと一緒に管理し、デプロイする度に呼び、該当バージョンのイメージを作成
Docker yaml
- DockerFileとほば同じ役割
- DockerFileより、書きやすい
- その他は勉強中。。。
Dockerのインストール
最新版をダウンロードして、インストールだけ
検証手順:https://qiita.com/kurkuru/items/127fa99ef5b2f0288b81Docker Fileの作り方
- ファイル名「Dockerfile」としてファイルを新規作成
FROMでベースになるイメージを指定RUN、CMDなどで実行コマンドを必要に応じて追加FROM python:3.6 COPY . /app WORKDIR /app RUN pip install -r /app/requirements.txt CMD python DemoApp/src/run_server.pyDocker Imageの使う方
DockerFileからイメージを作成する
docker build -t admin/httpd .現在のフォルダにあるDockerFileにより、「admin/httpd」という名前のイメージを作成する
コンテナからイメージを作成する
docker commit demo-container demo/img:qaコンテナ「demo-container」をベースに名前「demo/img」タグ「qa」のイメージを作成する
イメージ一覧の表示
docker images現在ローカルにあるイメージの一覧を表示する
イメージの削除
docker rmi demo/img名前「demo/img」のイメージをローカルから削除する
イメージの更新
docker pull demo/img名前「demo/img」のイメージの最新版を取得
Docker Containerの使う方
既存コンテナの一覧を表示
docker ps各コンテナの状態も見えます。
イメージファイルからコンテナを生成し、起動する
docker run admin/httpd名前「admin/httpd」のイメージでコンテナを生成し、起動する
よく使うオプション:
--nameコンテナ名を指定-d起動後にバックグランドで実行-i起動時に STDOUT を開きっぱなしにしてくれる-e環境変数の指定-pポートの開放-vホストディレクトリをコンテナにマウントする作成したコンテナに対する操作
- コンテナを起動
docker start admin/httpd
- コンテナを停止
docker stop admin/httpd
- コンテナを再起動
docker restart admin/httpdコンテナを削除
docker rm admin/httpd削除前に停止する必要があります。
Docker yamlの作り方
学習中
https://qiita.com/yuta-ushijima/items/d3d98177e1b28f736f04
https://qiita.com/zembutsu/items/9e9d80e05e36e882caaa参考リンク
https://qiita.com/curseoff/items/a9e64ad01d673abb6866
https://www.slideshare.net/zembutsu/docker-images-containers-and-lifecycle
https://qiita.com/tera_shin/items/8a43e904bd15990d3129#%EF%BC%97%E3%82%B3%E3%83%B3%E3%83%86%E3%83%8A%E4%B8%80%E8%A6%A7後続の調査方向:Kubernetes
- 投稿日:2019-05-22T17:15:34+09:00
MacでCassandra環境をローカルで構築する
概要
環境構築の備忘録。
Cassandra をローカルで動かしたかったので、そのための手順を残しておく。手順
Cassandra 本体の準備
今回はDockerで動かしてみる
docker-compose.ymlversion: '2' services: cassandra: image: cassandra:3.9 container_name: cassandra ports: - "9042:9042" environment: - "MAX_HEAP_SIZE=256M" - "HEAP_NEWSIZE=128M" restart: always volumes: - ./out/cassandra_data:/var/lib/cassandra0※ 細かい設定は適宜変更
cql(Cassandra版のSQL) をインストール
1. pip をインストール
※ インストールされている場合はスキップで OK
$ sudo easy_install pip2. pip で cql をインストール
$ sudo pip install cql私の環境の場合は以下のエラーが出た。
ERROR: Cannot uninstall 'six'. It is a distutils installed project and thus we cannot accurately determine which files belong to it which would lead to only a partial uninstall.オプションを付けることで回避できた。
$ sudo pip install cql -I six3. Cassandra へ接続
Docker を立ち上げてから接続
$ cqlsh Connected to Test Cluster at 127.0.0.1:9042. [cqlsh 5.0.1 | Cassandra 3.9 | CQL spec 3.4.2 | Native protocol v4] Use HELP for help. cqlsh>
- 投稿日:2019-05-22T17:15:34+09:00
MacでCassandraをローカルに構築する
概要
環境構築の備忘録。
Cassandra をローカルで動かしたかったので、そのための手順を残しておく。手順
Cassandra 本体の準備
今回はDockerで動かしてみる
docker-compose.ymlversion: '2' services: cassandra: image: cassandra:3.9 container_name: cassandra ports: - "9042:9042" environment: - "MAX_HEAP_SIZE=256M" - "HEAP_NEWSIZE=128M" restart: always volumes: - ./out/cassandra_data:/var/lib/cassandra0※ 細かい設定は適宜変更
cql(Cassandra版のSQL) をインストール
1. pip をインストール
※ インストールされている場合はスキップで OK
$ sudo easy_install pip2. pip で cql をインストール
$ sudo pip install cql私の環境の場合は以下のエラーが出た。
ERROR: Cannot uninstall 'six'. It is a distutils installed project and thus we cannot accurately determine which files belong to it which would lead to only a partial uninstall.オプションを付けることで回避できた。
$ sudo pip install cql -I six3. Cassandra へ接続
Docker を立ち上げてから接続
$ cqlsh Connected to Test Cluster at 127.0.0.1:9042. [cqlsh 5.0.1 | Cassandra 3.9 | CQL spec 3.4.2 | Native protocol v4] Use HELP for help. cqlsh>
- 投稿日:2019-05-22T16:04:38+09:00
Redis Commander を Dockerで
Redis Commander
Redis CommanderはブラウザでRedisに接続できるものですが
Dockerコンテナで dbIndex 0 と dbIndex 1 など,複数表示するのにはどうすればいいか?
こんな感じにしたい
試した環境
VirtualBoxの中の CentOS Linux release 7.6.1810 (Core)
こうすれば出来ました
docker-compose.yml
version: '3' services: redis: container_name: redis-container image: redis:5 command: redis-server --appendonly yes --requirepass foobared volumes: - /home/vagrant/redis-data/redis:/data ports: - "6379:6379" environment: TZ: JST-9 redisCommander: container_name: redis-commander-container image: rediscommander/redis-commander:latest environment: REDIS_HOSTS: Index0:redis-container:6379:0:foobared,Index1:redis-container:6379:1:foobared TZ: JST-9 ports: - "8081:8081" depends_on: - redisポイント1
DockerHubのドキュメントにDockerの欄があって
REDIS_PORT
REDIS_HOST
REDIS_SOCKET
REDIS_TLS
REDIS_PASSWORDREDIS_PASSWORD これを書こうとしてましたが、接続できない X(
redisCommander: image: rediscommander/redis-commander:latest environment: - REDIS_HOSTS=local:redis:6379 - REDIS_PASSWORD=foobaredError: Ready check failed: NOAUTH Authentication required. にも同じ失敗した人がいました。
ちゃんと Valid host strings として
label:hostname:port:dbIndex:password書式がありました。
ポイント2
dbIndex 複数は、どうやって定義するのか?
Specify multiple hosts with labels の例をみると , で区切ってたので
, 区切りで続けて書けば出来ました。
REDIS_HOSTS: Index0:redis-container:6379:0:foobared,Index1:redis-container:6379:1:foobared
- 投稿日:2019-05-22T15:21:54+09:00
(No.5) おじさんが、LINE風アプリを開発する - メッセージの送受信を考えてみるよ
前回までのあらすじ
- GitHubを利用して、おじさんの成果物を公開する準備が整いつつあるよ
- 目標を立てたよ、LINE風アプリが作れるようになることを目指すよ
メッセージを送受信するイメージ(もうそう)
おじさん、LINE風の仕組みをつくるために、まずは、メッセージの送受信を妄想してみるよ。
メッセージを宛先(スマホやPC)に直接送信する案と、問題点について
- おじさん、下図のようにスマホ同士で、直接メッセージを送信すれば良いと考えたよ。
- おじさんからのメッセージ「こんにちは」を、Aさんのスマホに送信するイメージだよ。
![]()
- でもLINEを調べてみると、スマホやPCやタブレットなど、複数の環境で、過去に受信済みのメッセージを見ることが出来るらしいんだ・・・。
- 後でAさんがPCやタブレットにLINEをインストールするケースを考えると、Aさんのスマホに直接メッセージを投げる方法では実現出来ないことかも・・・。
- 「こんにちは」は、PCやタブレットを導入する前に、スマホに送信したメッセージなのだから・・・。
- 単純におじさんが送信するメッセージを、宛先のスマホやPCに送信する方法だと、LINEとは異なる動きのアプリになりそうなんだ。(おじさんの考えた案は、トランシーバのようなものに近いかもだよ・・・)
![]()
問題を解決するアイディア
- 前述の案だと、随分と後になってアプリをインストールした端末上では、過去のメッセージを見ることが出来ない問題が出てきたよ。
- この問題は、宛先の端末に直接メッセージを送信することによって、発生する問題と思うんだよ。
- 解決方法は、おじさんの送信したメッセージを、誰でも見れるような場所に置いておく必要があると思うんだ。(町内掲示板みたいなイメージだよ)
- 下図のアイディアでは、おじさんのメッセージを入れておく、ナゾ箱を設置することにしたんだよ。
- 直接相手にメッセージを送信するのではなくて、ナゾの箱におじさんはメッセージを送信することにしたよ。
- 宛先のAさんは、気が向いたときにナゾ箱の中身を確認して、メッセージが届いていることを確認すれば良いんだよ。
- この方法だと、スマホからメッセージを読むことも可能だし、後で購入したPC・タブレットから読むことも可能だよね。
- メッセージを読み書きすることが出来るような、共有の箱、ナゾ箱を設置する必要性を感じたんだよね(照)。
![]()
ナゾ箱
- ここまでで、メッセージを読み書きする箱の必要性がわかった(ような)気がするよ。
- ナゾ箱に、手紙を入れておくわけにもいかないから、ナゾ箱=コンピュータであることは、おじさんでも想像が出来るよ。
- おじさんのコンピュータをナゾ箱として設置するのは、ちょっと気が引けるよ。 ずっと電源入れておかなくちゃいけないし、侵入されたら、おじさんの恥ずかしいファイルが流出しそうで怖いよ・・・。
- ナゾ箱について調べてみたけど、今回のようにメッセージを書き残したり、公開したり、何かしらの機能やサービスを提供するコンピュータのことを、世間では「サーバ」と呼ぶらしいよ。
- 以降、機能やサービスを提供するコンピュータのことを「サーバ」と呼ぶことにするね。おじさん、横文字にもなれてきた気がするよ(照)。
- 続いて、サーバをどうやって準備すれば良いのか考えることにするよ。
サーバを準備するにあたって懸念すること
- ここまでの振返り
- サーバと呼ばれるコンピュータの必要性がわかった気がするよ。たぶん。
- おじさんのメッセージを書き込みしたり、誰かにメッセージを読み込ませたりする目的で、サーバを利用するよ。
- おじさんがサーバを準備するにあたって懸念すること
- サーバ=コンピュータってことは、サーバも様々な種類があるってことだよ。
- おじさんのコンピュータはApple製だよ(自慢だよ)。
- おじさんのような素人が、コンピュータを楽に・便利に活用するには、OS(オペレーティングシステム)が必要だよ。
- コンピュータも製造するメーカが様々だけど、OSも種類が様々あるみたいだよ・・・。
- おじさんが現在利用しているmacOS
- Windows
- Linuxなんて呼ばれるものもあるみたいだよ・・・
- 所有するコンピュータや、これから調達するサーバによって、開発する方法や考慮しておくべきことが変化しそうだよ。
- 素人のおじさんでも想像出来るけど、例えば、WindowsとmacOSでは、異なる点が色々ありそうな気がするよ。
- 実際、Windows用のアプリを、そのままmacOSに持ってきても動作しないよね?
- おじさんのQiitaでの目的は、おじさんが自習して、職を得ることだけど、おじさん以外の人もおじさんと同じことが出来るように記録を残したいんだよ。(よくばりすぎかな?)
- 調達するサーバによって、プログラムの作り方や、考慮が変化するとなると、おじさん個人では対応しきれないよ・・・。
- サーバやOSを1種類に限定するとしても、そもそもおじさんの持っているコンピュータはApple製で、OSはmacOSだよ。
- おじさん以外の人が、おじさんと同じ作業を追体験するには、おじさんと同じコンピュータとmacOSが必要になるってことだよ。
- コンピュータやOSの違いが問題になりそうだよ。
懸念点のまとめ
- サーバの種類やOSによって、その上で動作するプログラムの作り方や考慮が変化するかもだよ。
- おじさん以外の人でもおじさんの作業を追体験出来るようにしたいよ。
- でも、おじさんのコンピュータはApple製で、OSはmacOSだよ。
- おじさんのコンピュータと条件が異なるコンピュータを利用する場合は、おじさんが意図しない問題が出てくるかもしれないし、それを事前に検証することも出来ないよ。
- Windowsは対象外になってしまうよ。
- アプリ開発時に利用するおじさんのコンピュータと、不特定多数に公開するサーバと、おじさんの作業を追体験するおじさん以外の人のコンピュータのそれぞれを、可能な限り同じ条件に合わせたいんだよ。
開発環境(おじさん)、サーバ、おじさん以外の人のコンピュータの条件を可能な限り合わせるには
- ネットサーフィン(汗)だけが、得意なおじさん。色々調べたところ、次のような方法があることがわかったよ。
- 仮想機械、仮想マシン、バーチャルマシンを利用する方法
- コンテナを利用する方法
あとがき
- コンピュータやOSの種類による影響を最小限にしたいので、仮想環境やコンテナについて、少しずつ調べていくよ。
- サーバを準備して、複数の環境からメッセージを送受信出来るようにしたいと考えているよ。
- 先は長そうだけど、いつか出来ると信じて取り組むとするよ。おじさん、無駄に時間だけはあるからね(涙)。
- どこかの誰かが出来ていることは、おじさんにも出来ることだと、昔励まされたことがあったよ。それを今思い出したよ(涙)。
参照・メモ
サーバ
ハイパーバイザ
仮想機械
What is a Container?
Get Started, Part 1: Orientation and setup
Docker overview
Docker道場「Dockerの基本概念」0825インフラ勉強会資料
Docker ドキュメント日本語化プロジェクト
AWSでDockerを扱うためのベストプラクティス
- 投稿日:2019-05-22T14:20:47+09:00
AWS CodeBuildのコンテナから、RDSへ直接マイグレーションを実行する方法
概要
サービスを全部ECS + Fargateで行うサービスで、簡単かつCoolにマイグレーション実行する方法を記します。
具体的にはCodePipeline & CodeBuildを設定し、ビルド中にマイグレーションが走るように設定します。この記事が役に立ちそうな人
- ECS + Fargateでサービスを構築しようとしている人
- サービスにはRDSを用いているが、マイグレーションをどこから行おうか悩んでいる人
背景
マイグレーションをいかにCoolに行うかはサーバーサイドの至上命題と言っても過言ではありません。(?)
前述の通りECS + Fargateでサービスを構築すると、FargateにはSSHできないのは周知の事実です。
よってコンテナに入れないため、フレームワークの機能を用いたマイグレーションを実行できません。
そして、マイグレーションは必ずソースコードのデプロイ前に実行される必要があります。以上のことから、ソースコードのデプロイを行うCodePipelineのフローでマイグレーションを行うフェーズを追加し、マイグレーション含めたアプリケーションのデプロイを管理できたらCoolだなと思った次第です。
こちらの方法で実はその例が示されているのですが、タスク定義をjsonで書く必要があり、ステージング環境などの環境ごと対応が手間のように見受けられました。
もっと簡単でセキュアな方法はないかと考えていたところ、CodeBuildのドキュメントにCodeBuildが特定VPC, サブネットで実行できると書いていました。
RDSにはプライベートサブネットからしかアクセスできないようにしていましたが、これならCodeBuildインスタンスに立ち上げたコンテナから、マイグレーションを直接実行できるのでは? と思いついたわけです。
実際に
プライベートサブネット上に分離された Amazon RDS データベース内のデータに対して、ビルドから統合テストを実行する。
とあるように、RDSへのアクセスが想定されているようです。
目的
- CodePipelineのフェーズ中にmigrationフェーズを追加し、ソースコードのデプロイに加えてマイグレーションも制御したい
- ECSタスク定義を書きたくない
- もちろんセキュリティは維持する
- フレームワークのマイグレーション機能を使いたい 1
インフラ構成の説明
これが全貌ではありませんが、プライベートサブネットについてはこの構成です。AZの冗長化は端折っています。
ECSのセキュリティグループとは別のSGを用意し、CodeBuildにはそれを設定しておきます。セキュリティグループ3にはセキュリティグループ1のインバウンドトラフィックを許可しておきます。
セキュリティグループ1には特に設定は不要です。具体的な設定
CodeBuild
上記画像のようなVPCに関する設定があります。
ここにインフラ構成で記した通りの設定をしておきます。あと環境変数
AWS_ENVIRONMENTを設定しておきます。
これでどの環境のデータベースへ接続するかを、laravelのenvファイルの読み込みを切り替えることで選択できるようになります。
つまり後述のbuildspecは全環境で共通利用できます。加えて使用するbuildspecは後述の
buildspec-migration.ymlを指定してください。buildspec
実例を示します。
buildspec-migration.ymlversion: 0.2 phases: install: runtime-versions: docker: 18 commands: - echo Install started on `date` - cat /etc/issue - curl -L "https://github.com/docker/compose/releases/download/1.21.0/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose && chmod +x /usr/local/bin/docker-compose - docker-compose --version - docker -v - pwd && ls -l build: commands: - echo Build started on `date` - echo Building the Docker image... - sed -e 's/AWS_ENVIRONMENT/$AWS_ENVIRONMENT/g' docker-compose-migration.yml > docker-compose.yml - docker-compose build - docker-compose up -d - docker ps - docker exec laravel-api bash /project/build.sh post_build: commands: - echo Build completed on `date` - echo Start migrate - docker exec laravel-api php project/artisan migrate私はdocker-composeを用いてコンテナを立ち上げています。2
sed -e 's/AWS_ENVIRONMENT/$AWS_ENVIRONMENT/g' docker-compose-migration.yml
これで環境ごとにdocker-compose.ymlを生成します。これでこのymlは全環境で使いまわせます。
docker exec laravel-api bash /project/build.sh
ここではcomposer installを行うだけのシェルを実行しています。でないとmigrateが動かないので。
docker exec laravel-api php project/artisan migrate
ここでついにmigrationを実行しています。docker-compose
私はdocker-composeを用いているので、その設定も例を示します。
docker-compose-migration.ymlversion: '3.3' services: # web - api api: build: ./alpine-apache-php7 container_name: laravel-api environment: - APP_ENV=AWS_ENVIRONMENT volumes: - "./project:/project" ports: - "18080:80"
APP_ENVには設定すればdevelopmentなどの任意のテキストを入れられるので、読み込む環境設定ファイルの制御ができます。CodePipeline
パイプラインの例を示します。
上記のようにECRへのビルドフェーズと、マイグレーション含むデプロイフェーズは分けています。
こうすることでマイグレーションとデプロイが自動的に実行されないようになります。参考文献
LaravelをElastic BeanstalkからFargateに移行しました
https://qiita.com/hareku/items/73f7730c1adc01bbe5a0AWS公式CodeBuildのVPCサポートについてのドキュメント
https://docs.aws.amazon.com/ja_jp/codebuild/latest/userguide/vpc-support.html
フレームワークはLaravelを使っています。多分他のフレームワーク、言語でも同じことができるはずです。 ↩
どこかのタイミングからCodeBuildでdocker-composeを動かすとエラーを吐くようになりました。どうやら標準イメージを使うようになってから追加の設定が必要になったようです。このように別のソフトをインストールするときは、CodeBuildの環境設定において
Enable this flag if you want to build Docker images or want your builds to get elevated privileges.にチェックを入れないとエラーとなります。注意してください。 ↩
- 投稿日:2019-05-22T13:09:20+09:00
WordPress on Dockerを下層ディレクトリで動かす
背景
- 色々やり方がでてきますがやりたいことが微妙に違うので備忘録として。
- WordPressをAPIとして使いたいが、APIサーバーは分けたくない。
- 下層ディレクトリ
wpにDockerを使用してWordPressをインストールしたいときに環境構築に若干躓いた。環境
- macOS Mojave
- Docker for Mac
やりたいこと
- 公式のWordPressイメージだとドキュメント直下にしかWordPressを置けない
- そのためdocker-compose.ymlとdocker-entrypoint.shを編集する
docker-compose.ymlの構成
version: "3.3" services: db: image: mysql:5.7 volumes: - db_data:/var/lib/mysql restart: always environment: MYSQL_ROOT_PASSWORD: somewordpress MYSQL_DATABASE: wordpress MYSQL_USER: wordpress MYSQL_PASSWORD: wordpress wordpress: depends_on: - db volumes: - ./:/var/www/html - ./docker-entrypoint.sh:/usr/local/bin/docker-entrypoint.sh image: wordpress:latest working_dir: /var/www/html ports: - 80:80 restart: always environment: WORDPRESS_DB_HOST: db:3306 WORDPRESS_DB_USER: wordpress WORDPRESS_DB_PASSWORD: wordpress WORDPRESS_SUBDIRECTORY: wp node: # 使わないなら不要 image: node:8.15.1-alpine #Nodeは各々のVersionで user: node working_dir: /home/node/app volumes: - ./:/home/node/app #package.jsonがあるディレクトリをmount volumes: db_data: {}docker-entrypoint.sh
#!/bin/bash set -euo pipefail # usage: file_env VAR [DEFAULT] # ie: file_env 'XYZ_DB_PASSWORD' 'example' # (will allow for "$XYZ_DB_PASSWORD_FILE" to fill in the value of # "$XYZ_DB_PASSWORD" from a file, especially for Docker's secrets feature) file_env() { local var="$1" local fileVar="${var}_FILE" local def="${2:-}" if [ "${!var:-}" ] && [ "${!fileVar:-}" ]; then echo >&2 "error: both $var and $fileVar are set (but are exclusive)" exit 1 fi local val="$def" if [ "${!var:-}" ]; then val="${!var}" elif [ "${!fileVar:-}" ]; then val="$(< "${!fileVar}")" fi export "$var"="$val" unset "$fileVar" } if [[ "$1" == apache2* ]] || [ "$1" == php-fpm ]; then +追加 if ! [ -z "$WORDPRESS_SUBDIRECTORY" ]; then +追加 # force relative path +追加 WORDPRESS_SUBDIRECTORY=`echo $WORDPRESS_SUBDIRECTORY | sed 's/^\///g'` +追加 mkdir -p $WORDPRESS_SUBDIRECTORY +追加 cd $WORDPRESS_SUBDIRECTORY +追加 fi if [ "$(id -u)" = '0' ]; then case "$1" in apache2*) user="${APACHE_RUN_USER:-www-data}" group="${APACHE_RUN_GROUP:-www-data}" # strip off any '#' symbol ('#1000' is valid syntax for Apache) pound='#' user="${user#$pound}" group="${group#$pound}" ;; *) # php-fpm user='www-data' group='www-data' ;; esac else user="$(id -u)" group="$(id -g)" fi if [ ! -e index.php ] && [ ! -e wp-includes/version.php ]; then # if the directory exists and WordPress doesn't appear to be installed AND the permissions of it are root:root, let's chown it (likely a Docker-created directory) if [ "$(id -u)" = '0' ] && [ "$(stat -c '%u:%g' .)" = '0:0' ]; then chown "$user:$group" . fi echo >&2 "WordPress not found in $PWD - copying now..." if [ -n "$(ls -A)" ]; then echo >&2 "WARNING: $PWD is not empty! (copying anyhow)" fi sourceTarArgs=( --create --file - --directory /usr/src/wordpress --owner "$user" --group "$group" ) targetTarArgs=( --extract --file - ) if [ "$user" != '0' ]; then # avoid "tar: .: Cannot utime: Operation not permitted" and "tar: .: Cannot change mode to rwxr-xr-x: Operation not permitted" targetTarArgs+=( --no-overwrite-dir ) fi tar "${sourceTarArgs[@]}" . | tar "${targetTarArgs[@]}" echo >&2 "Complete! WordPress has been successfully copied to $PWD" if [ ! -e .htaccess ]; then # NOTE: The "Indexes" option is disabled in the php:apache base image cat > .htaccess <<-'EOF' # BEGIN WordPress <IfModule mod_rewrite.c> RewriteEngine On RewriteBase / RewriteRule ^index\.php$ - [L] RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule . /index.php [L] </IfModule> # END WordPress EOF chown "$user:$group" .htaccess fi fi # allow any of these "Authentication Unique Keys and Salts." to be specified via # environment variables with a "WORDPRESS_" prefix (ie, "WORDPRESS_AUTH_KEY") uniqueEnvs=( AUTH_KEY SECURE_AUTH_KEY LOGGED_IN_KEY NONCE_KEY AUTH_SALT SECURE_AUTH_SALT LOGGED_IN_SALT NONCE_SALT ) envs=( WORDPRESS_DB_HOST WORDPRESS_DB_USER WORDPRESS_DB_PASSWORD WORDPRESS_DB_NAME WORDPRESS_DB_CHARSET WORDPRESS_DB_COLLATE "${uniqueEnvs[@]/#/WORDPRESS_}" WORDPRESS_TABLE_PREFIX WORDPRESS_DEBUG WORDPRESS_CONFIG_EXTRA ) haveConfig= for e in "${envs[@]}"; do file_env "$e" if [ -z "$haveConfig" ] && [ -n "${!e}" ]; then haveConfig=1 fi done # linking backwards-compatibility if [ -n "${!MYSQL_ENV_MYSQL_*}" ]; then haveConfig=1 # host defaults to "mysql" below if unspecified : "${WORDPRESS_DB_USER:=${MYSQL_ENV_MYSQL_USER:-root}}" if [ "$WORDPRESS_DB_USER" = 'root' ]; then : "${WORDPRESS_DB_PASSWORD:=${MYSQL_ENV_MYSQL_ROOT_PASSWORD:-}}" else : "${WORDPRESS_DB_PASSWORD:=${MYSQL_ENV_MYSQL_PASSWORD:-}}" fi : "${WORDPRESS_DB_NAME:=${MYSQL_ENV_MYSQL_DATABASE:-}}" fi # only touch "wp-config.php" if we have environment-supplied configuration values if [ "$haveConfig" ]; then : "${WORDPRESS_DB_HOST:=mysql}" : "${WORDPRESS_DB_USER:=root}" : "${WORDPRESS_DB_PASSWORD:=}" : "${WORDPRESS_DB_NAME:=wordpress}" : "${WORDPRESS_DB_CHARSET:=utf8}" : "${WORDPRESS_DB_COLLATE:=}" # version 4.4.1 decided to switch to windows line endings, that breaks our seds and awks # https://github.com/docker-library/wordpress/issues/116 # https://github.com/WordPress/WordPress/commit/1acedc542fba2482bab88ec70d4bea4b997a92e4 sed -ri -e 's/\r$//' wp-config* if [ ! -e wp-config.php ]; then awk ' /^\/\*.*stop editing.*\*\/$/ && c == 0 { c = 1 system("cat") if (ENVIRON["WORDPRESS_CONFIG_EXTRA"]) { print "// WORDPRESS_CONFIG_EXTRA" print ENVIRON["WORDPRESS_CONFIG_EXTRA"] "\n" } } { print } ' wp-config-sample.php > wp-config.php <<'EOPHP' // If we're behind a proxy server and using HTTPS, we need to alert Wordpress of that fact // see also http://codex.wordpress.org/Administration_Over_SSL#Using_a_Reverse_Proxy if (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https') { $_SERVER['HTTPS'] = 'on'; } EOPHP chown "$user:$group" wp-config.php elif [ -e wp-config.php ] && [ -n "$WORDPRESS_CONFIG_EXTRA" ] && [[ "$(< wp-config.php)" != *"$WORDPRESS_CONFIG_EXTRA"* ]]; then # (if the config file already contains the requested PHP code, don't print a warning) echo >&2 echo >&2 'WARNING: environment variable "WORDPRESS_CONFIG_EXTRA" is set, but "wp-config.php" already exists' echo >&2 ' The contents of this variable will _not_ be inserted into the existing "wp-config.php" file.' echo >&2 ' (see https://github.com/docker-library/wordpress/issues/333 for more details)' echo >&2 fi # see http://stackoverflow.com/a/2705678/433558 sed_escape_lhs() { echo "$@" | sed -e 's/[]\/$*.^|[]/\\&/g' } sed_escape_rhs() { echo "$@" | sed -e 's/[\/&]/\\&/g' } php_escape() { local escaped="$(php -r 'var_export(('"$2"') $argv[1]);' -- "$1")" if [ "$2" = 'string' ] && [ "${escaped:0:1}" = "'" ]; then escaped="${escaped//$'\n'/"' + \"\\n\" + '"}" fi echo "$escaped" } set_config() { key="$1" value="$2" var_type="${3:-string}" start="(['\"])$(sed_escape_lhs "$key")\2\s*," end="\);" if [ "${key:0:1}" = '$' ]; then start="^(\s*)$(sed_escape_lhs "$key")\s*=" end=";" fi sed -ri -e "s/($start\s*).*($end)$/\1$(sed_escape_rhs "$(php_escape "$value" "$var_type")")\3/" wp-config.php } set_config 'DB_HOST' "$WORDPRESS_DB_HOST" set_config 'DB_USER' "$WORDPRESS_DB_USER" set_config 'DB_PASSWORD' "$WORDPRESS_DB_PASSWORD" set_config 'DB_NAME' "$WORDPRESS_DB_NAME" set_config 'DB_CHARSET' "$WORDPRESS_DB_CHARSET" set_config 'DB_COLLATE' "$WORDPRESS_DB_COLLATE" for unique in "${uniqueEnvs[@]}"; do uniqVar="WORDPRESS_$unique" if [ -n "${!uniqVar}" ]; then set_config "$unique" "${!uniqVar}" else # if not specified, let's generate a random value currentVal="$(sed -rn -e "s/define\(\s*(([\'\"])$unique\2\s*,\s*)(['\"])(.*)\3\s*\);/\4/p" wp-config.php)" if [ "$currentVal" = 'put your unique phrase here' ]; then set_config "$unique" "$(head -c1m /dev/urandom | sha1sum | cut -d' ' -f1)" fi fi done if [ "$WORDPRESS_TABLE_PREFIX" ]; then set_config '$table_prefix' "$WORDPRESS_TABLE_PREFIX" fi if [ "$WORDPRESS_DEBUG" ]; then set_config 'WP_DEBUG' 1 boolean fi if ! TERM=dumb php -- <<'EOPHP' <?php // database might not exist, so let's try creating it (just to be safe) $stderr = fopen('php://stderr', 'w'); // https://codex.wordpress.org/Editing_wp-config.php#MySQL_Alternate_Port // "hostname:port" // https://codex.wordpress.org/Editing_wp-config.php#MySQL_Sockets_or_Pipes // "hostname:unix-socket-path" list($host, $socket) = explode(':', getenv('WORDPRESS_DB_HOST'), 2); $port = 0; if (is_numeric($socket)) { $port = (int) $socket; $socket = null; } $user = getenv('WORDPRESS_DB_USER'); $pass = getenv('WORDPRESS_DB_PASSWORD'); $dbName = getenv('WORDPRESS_DB_NAME'); $maxTries = 10; do { $mysql = new mysqli($host, $user, $pass, '', $port, $socket); if ($mysql->connect_error) { fwrite($stderr, "\n" . 'MySQL Connection Error: (' . $mysql->connect_errno . ') ' . $mysql->connect_error . "\n"); --$maxTries; if ($maxTries <= 0) { exit(1); } sleep(3); } } while ($mysql->connect_error); if (!$mysql->query('CREATE DATABASE IF NOT EXISTS `' . $mysql->real_escape_string($dbName) . '`')) { fwrite($stderr, "\n" . 'MySQL "CREATE DATABASE" Error: ' . $mysql->error . "\n"); $mysql->close(); exit(1); } $mysql->close(); EOPHP then echo >&2 echo >&2 "WARNING: unable to establish a database connection to '$WORDPRESS_DB_HOST'" echo >&2 ' continuing anyways (which might have unexpected results)' echo >&2 fi fi # now that we're definitely done writing configuration, let's clear out the relevant envrionment variables (so that stray "phpinfo()" calls don't leak secrets from our code) for e in "${envs[@]}"; do unset "$e" done fi exec "$@"操作
docker-compose up -dを実行したあと、localhost/wpにアクセスでWordPressインストール画面が出ます。
エラーが出る場合は
chmod +x docker-entrypoint.shを実行する。
Nodeを使いたいときは
docker-compose run node npm install等のコマンドを叩いてください。
参考
Dockerの公式WordPressイメージを使い、下層ディレクトリにある既存のWordPressを動かす - Docker入門を大いに参考にさせていただきました(というかこちらの方が遥かに良記事です)。
- 投稿日:2019-05-22T11:58:54+09:00
Dockerで"A server is already running"でコンテナから抜けてしまうが、server.pidが見つからない場合
環境
macOS High Sierra 10.13.6
Doker Version: 18.09.2
rails 5.2.1問題
docker-compose upでコンテナを立ち上げたところ、途中までうまくいくが、最後以下のようになりコンテナを抜けてしまう。
しかしtmp/pids/server.pidが見つからない。=> Booting Puma => Rails 5.2.1 application starting in development => Run `rails server -h` for more startup options A server is already running. Check /[rootディレクトリ]/tmp/pids/server.pid. [rootディレクトリ]_1 | Exiting解決法
Ctrl-cのあとdocker-compose stopでコンテナを停止させた後、もう一度doker-compose upで立ち上げる。
立ち上げ〜コンテナから抜けてしまう間に、別ウィンドウで素早く以下コマンドを入力。$ docker-compose exec [rootディレクトリ] /bin/bash ~@~:/[rootディレクトリ]# rm tmp/pids/server.pid1行目でコンテナ内のbashに入り、2行目でファイルを削除。
考察
自分の場合、ローカルにはtmp/pids/server.pidが見つからなかったが、コンテナ内には存在していた。
コンテナを停止している間はコンテナ内のbashに入れなかったのでこうしたアホなやり方になってしまった。
しかしこれで立ち上げることはできました。
- 投稿日:2019-05-22T11:34:28+09:00
Python・Redis・Nginxを使って始めるDocker Tutorial
Docker Tutorial — Getting Started with Python, Redis, and Nginx.という海外の記事が凄くdocker初学者向けで、人気だったので分かりやすく翻訳してみました。
はじめに
これはDockerコンテナに関する入門チュートリアルです。この記事を読み終わると、ローカルマシン上でDockerを使う方法を知る事が出来ます。
この記事ではPython、Redis、Nginxコンテナを実行します。
今回、この記事の読者は上記の3つの技術の基本概念に精通していると仮定しています。
シェルを使った例を沢山使うので、ターミナルを用意して読み進めて下さい!Dockerとは何か
Dockerとは、ソフトウェアコンテナ内のアプリケーションの開発を自動化する為に作られたオープンソースツールです。
このDockerの概念を理解する一番簡単な方法は、現実世界の運送コンテナと比較することでしょう。
ここでは、あくまで一例として読んでください。昔、運送会社は次のような問題に直面していました。
・どうやって、異なるタイプの品物(食べ物、化学薬品、ガラス製品、レンガなど)を並べて輸送すればいいのか
・同じ車両を使って、どうやって異なるサイズの荷物を運べばいいのかしかし、コンテナを導入した後にこれらの問題を解決する事が出来ました。
レンガはガラスの上に置き、化学製品は食べ物の隣に置き、異なるサイズの荷物はあらかじめ規定されたコンテナに入れ、同じ車両を使って荷物を下ろしたり積んだりする事が出来るようになりました。理解出来たでしょうか?それでは、ソフトウェア開発におけるコンテナに話を戻します。
アプリケーションを開発する際に、ライブラリやWebサーバー、データベースなどの関係を意識してコードを書くと思いますが、実行環境がそれぞれのコンピュータで異なる時にアプリケーションが作動しなくなる経験をした事があるかもしれません。
しかし、この問題はアプリケーションとシステムを分離させることにより解決する事が出来ます。
普通の仮想化とは何が違うのか?
従来は、仮想マシンを使って上記の問題を回避していました。しかし、この仮想マシンの問題は、ホストOS上にある“余分なOS”が数GBの容量を使ってしまう事でした。ほとんどの場合、サーバーはさらに多くのスペースを占有する仮想マシンをホストすることになってしまいます(余談ですが今現在、大半のクラウドサーバーサービスはその余分なスペースの分を請求しています)
また、もう一つの重大な欠点は起動が遅い事です。DockerはホストOSの別々のプロセスとして、OSのカーネルを全てのコンテナをまたいで共有することにより、上記のような問題を全て解決してくれます。
Dockerが唯一のコンテナ化プラットフォームではないですが、現時点では市場で最も大きく、ユーザー数も多いです。
なぜDockerが必要なのか
下記にメリットをまとめておきました。
・開発プロセスの迅速にする事ができる
・アプリケーションを手軽にまとめられる
・local/dev/staging/production環境で同じ動作が可能になる
・簡単で分かりやすい監視が出来る
・拡張が簡単になる開発プロセスの迅速にする事ができる
PostgreSQL, Redis, Elasticsearchのような外部のアプリをシステム上にインストールする必要はありません。コンテナを起動させるだけで実行する事が出来ます!
Dockerを使うことによって、同じアプリの異なるバージョンを同時に実行する事も出来ます。
例えば、PostgreSQLの古いバージョンを新しいバージョンに手動で移行させる必要があるとします。最新の外部のアプリを使った新しいマイクロサービスを作りたい時に、マイクロサービスの設計でこのような状況になることは頻繁にあります。この時、1つのホストOS上で同じアプリの異なるバージョンを同時に実行するのは非常に困難です。しかし、Dockerならこの問題を完璧に解決することが出来ます。Dockerを使えばサービスと外部のアプリをそれぞれ分離した独自の環境を構築出来るからです。
アプリケーションを手軽にまとめられる
Dockerを使えば、アプリケーションを1つのカタマリとして運用することが出来ます。
大半のプログラミング言語、フレームワーク、オペレーションシステムはそれぞれパッケージマネージャを持っています。たとえもしそれぞれのパッケージマネージャでアプリをまとめたとしても、他のシステムでそのアプリを使うのは難しいです。
しかし、Dockerなら異なるホストやクラウドサービスの垣根を越えて、あるフォーマットに統一されたイメージを作ることによって他のシステムでもアプリを使う事が出来ます。
local/dev/staging/production環境で同じ動作が可能になる
どんな技術にもヒューマンファクタがあるため、Dockerはlocal/dev/staging/productionが全て同じ環境であることを保証することは出来ません。
しかし、OSやシステムの依存関係によって起こるエラーの確率をほとんどゼロに減らしてくれます。正しい方法でDocker imageをビルドする事が出来れば、同じOSやシステム上の依存関係を持ったアプリを使う事が出来ます!
*ヒューマンファクタ・・・人間や組織・機械・設備等で構成されるシステムが、安全かつ経済的に動作・運用できるために考慮しなければならない人間側の要因のこと。一言でいえば「人的要因」である。
簡単で分かりやすい監視が出来る
起動しているコンテナのログファイルを読むための統一された方法がDockerにはあります。
どこにログファイルを保存しておくかも覚える必要はなく、外部のログドライバーとモニターを1つの場所にまとめておく事が出来ます。拡張が簡単になる
設計上Dockerは環境変数の設定、TCP/UDPポートを通した通信などの主要な原則に沿って使う事が出来ます。
正しくアプリを完成させればDockerの中だけではなく、色々な所にスケールを広げる事が簡単に出来ます。対応プラットフォーム
DockerのネイティブプラットフォームはLinuxです。これはLinuxカーネルに基づいてDockerが作られているからです。しかし、macOSやWindows上で使う事が出来ます。
Linuxとの唯一の違いはmacOSとWindows上のDockerは小さな仮想マシンにカプセル化されている事ですが、現在では、macOSとWindows上のDockerは本来のDockerと同じくらい使いやすくなっています。インストール
Dockerのインストール方法はここから確認する事が出来ます。
(もしLinuxでDockerを実行しているなら、以下のすべてのコマンドをrootとして実行するか、ユーザーをdockerグループに追加して再ログインする必要があります。)
sudo usermod -aG docker $(whoami)`用語
・コンテナ
必要なソフトウェアをまとめたインスタンスのことです。コンテナはイメージから作られ、他のコンテナや外部と通信するためにポートとボリュームを公開することが出来ます。また、コンテナは簡単に短時間でキルしたり削除したり再構築したりすることが出来ます。
そして、コンテナは状態を保持しません。・イメージ
コンテナの元となる要素のことです。イメージによって構築する時に時間がかかることがあります。一方、コンテナはイメージからすぐに構築する事が出来ます。・ポート
元々の意味ではTCP / UDPポートという意味です。ですが、もっと分かりやすく理解するために、他のコンテナやホストOSから接続可能な外部のネットワークと通信する時に必要なものと考えてください。・ボリューム
Dockerにおける共有フォルダのことです。コンテナを作ると初期化されます。ボリュームはデータを保持しておくために設計されており、コンテナの動作には影響されません。・レジストリ
イメージを保存しているサーバーのことです。Githubと比較すると分かりやすいかもしれません。
ローカルでイメージを使う時にレジストリからイメージをpullしたり、ローカルで作ったイメージをレジストリにpushする事が出来ます。・DockerHub
Docker Inc.が提供するWebインターフェイスを備えたレジストリのことです。様々なソフトウェアを含むDockerイメージを多数保存しています。
Docker Hubは、Dockerチーム、またはオリジナルのソフトウェア製造者と共同で作成された「公式の」Dockerイメージのソースです。
公式のイメージはそれらの潜在的な脆弱性をリストにまとめてあります。このリストはDockerHubにログインしているすべてのユーザーが閲覧する事が出来ます。
無料と有料の両方のアカウントがあります。
パブリックイメージは無制限に持つことができますが、プライベートイメージは1つのアカウントに1つまでです。
Docker Hubと非常によく似たサービスに、Docker Storeもあります。評価、レビューなどが掲載されています。Example 1: hello world
それでは、最初のコンテナを動かしてみましょう!
docker run ubuntu /bin/echo 'Hello world'コンソールの出力結果Unable to find image 'ubuntu:latest' locally latest: Pulling from library/ubuntu 6b98dfc16071: Pull complete 4001a1209541: Pull complete 6319fc68c576: Pull complete b24603670dc3: Pull complete 97f170c87c6f: Pull complete Digest:sha256:5f4bdc3467537cbbe563e80db2c3ec95d548a9145d64453b06939c4592d67b6d Status: Downloaded newer image for ubuntu:latest Hello world・docker runはコンテナを実行するためのコマンドです。
・ubuntuは今実行しているイメージです。たとえば、UbuntuOSのイメージを使う時に、イメージを指定すると、Dockerは最初にDockerホスト上のイメージを探します。イメージがローカルに存在しない場合、そのイメージはパブリックイメージレジストリ=Docker Hubから取得してきます。
・
/bin/echo 'Hello world'は作ったコンテナの中で実行されるコマンドです。このコンテナは単に「Hello world」と表示して実行を停止します。それでは、Dockerコンテナ内でシェルを作ってみましょう!
docker run -i -t --rm ubuntu /bin/bash・
-tは、作成するコンテナ内に疑似的なターミナルを割り当てる設定をしてます・
-iは、コンテナーの標準入力(STDIN)を取得し、双方向の接続をする設定をしてます。・
-rmは、プロセスが終了する時にコンテナを削除する設定をしてます。デフォルトだと、コンテナは削除されません。このrmの設定によって作られたコンテナは、シェルのセッションを維持する間ずっと残りますが、リモートサーバーとのssh通信などのようにセッションが無くなると削除されます。セッション終了後もコンテナを実行し続けたい場合は、デーモン化する必要があります。
docker run --name daemon -d ubuntu /bin/sh -c "while true; do echo hello world; sleep 1; done"・
--name daemonは「daemon」という名前でコンテナに割り当てる設定をしてます。名前を明示的に指定しない場合、Dockerは自動的に名前を生成し割り当てます。・
-dはバックグラウンドでコンテナを実行させる設定をしてます。(つまり、デーモン化です)それでは今どんなコンテナを持っているのか見てみましょう。
docker ps -aコンソールの出力結果CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 1fc8cee64ec2 ubuntu "/bin/sh -c 'while..." 32 seconds ago Up 30 seconds daemon c006f1a02edf ubuntu "/bin/echo 'Hello ..." About a minute ago Exited (0) About a minute ago gifted_nobel・
docker psはコンテナをリスト表示するためのコマンドです。・
-aは全てのコンテナは表示させるためのコマンドです。(docker psだけだと起動中のコンテナしかリスト表示されません)
docker psをしてみると、現在2つのコンテナを持っていることが分かります。*
gifted_nobelは、「Hello,World」と出力させた、最初に作ったコンテナです。 (このコンテナの名前は自動的に生成されます。ご自身のコンピュータによって名前は異なります。)*
daemonは、デーモンとして作った3番目のコンテナです。注:2番目のコンテナ(対話型シェルを持つコンテナ)はありません。何故なら、
-rmオプションを設定しているからです。そのため、2番目のコンテナは実行された時に自動的に削除されています。それでは、ログを見てデーモンコンテナが何をしているのか見てみましょう!
docker logs -f daemonコンソールの出力結果... hello world hello world hello world・
docker logを使うとdockerのログを取得することが出来ます。
・-fを使うとログの出力結果を追跡することが出来ます。では、デーモンコンテナを停止してみましょう!
docker stop daemon停止しているかどうか確かめて見てください。
docker ps -aコンソールの出力結果CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 1fc8cee64ec2 ubuntu "/bin/sh -c 'while..." 5 minutes ago Exited (137) 5 seconds ago daemon c006f1a02edf ubuntu "/bin/echo 'Hello ..." 6 minutes ago Exited (0) 6 minutes ago gifted_nobelコンテナが停止していますね。もう一度、起動させてみましょう。
docker start daemon起動しているか確かめてみてください。
docker ps -aコンソールの出力結果CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 1fc8cee64ec2 ubuntu "/bin/sh -c 'while..." 5 minutes ago Up 3 seconds daemon c006f1a02edf ubuntu "/bin/echo 'Hello ..." 6 minutes ago Exited (0) 7 minutes ago gifted_nobel起動出来ていますね。では、今度はもう一度停止して、手動で消してみましょう!
docker stop daemon docker rm <your first container name> docker rm daemon全てのコンテナを消したい場合は、次のコマンドを使用します。
docker rm -f $(docker ps -aq)・
docker rmはコンテナを削除するためのコマンドです。
・-f $()は実行中のコンテナを停止させてます(つまり、強制的に消すという事です)。
・-q(ps用) はコンテナIDのみを表示させるためのコマンドです。Example 2: 環境変数とボリューム
この例を始める前に、私のGithubレポジトリ から新たなファイルを追加してください。
私のレポジトリをクローンするか、このリンクをクリックしてファイルをダウンロードしてください。
それでは、ここからはNginxのような、もっと難易度の高いコンテナを作っていきましょう!
examples/nginxにカレントディレクトリを切り替えて下さい!
docker run -d --name "test-nginx" -p 8080:80 -v $(pwd):/usr/share/nginx/html:ro nginx:latest・
-pは、HOST PORT(8080)をCONTAINER PORT(80)としてマッピングしてます。・
-vは、HOST DIRECTORY(pwd)をCONTAINER DIRECTORY(/usr/share/nginx/html:ro)にボリュームをマウントさせてます。注:このコマンドは非常にコンピュータに負荷がかかりますが、環境変数とボリュームを説明するためにあえて分かりやすくしています。実際の開発では、こんな風に手動でDockerコンテナをスタートさせたりせずにオーケストレーションサービスを使うか、カスタムスクリプトを書いてスタートさせます。
コンソールの出力結果Unable to find image 'nginx:latest' locally latest: Pulling from library/nginx 683abbb4ea60: Pull complete a470862432e2: Pull complete 977375e58a31: Pull complete Digest: sha256:a65beb8c90a08b22a9ff6a219c2f363e16c477b6d610da28fe9cba37c2c3a2ac Status: Downloaded newer image for nginx:latest afa095a8b81960241ee92ecb9aa689f78d201cff2469895674cec2c2acdcc61c重要:
runコマンドは絶対パスのみ受け付けます。この例では、カレントディレクトリの絶対パスとして$(pwd)を使ってます。また、
/example/nginx/index.html(コンテナの中に/usr/share/nginx/htmlディレクトリとしてマウントされてるファイル)を変更してページを更新することも出来ます。それでは、test-nginxコンテナに関する情報を入手しましょう。
docker inspect test-nginxこのコマンドはDockerのインストールに関するシステム全体の情報を表示します。
この情報には、カーネルのバージョン、コンテナーとイメージの数、公開ポート、マウント済みボリュームなどが含まれます。Example 3: 初めてのDockerfile
DockerイメージをビルドするためにはDockerfileを作る必要があります。Dockerfileとはコンテナなどの説明や実行させたい命令などを書いておくテキストファイルのことです。
今回の例で使う命令の説明はこんな感じです。
FROM:ベースとなるイメージを設定するRUN:コンテナ内でコマンドを実行ENV:環境変数を設定するWORKDIR:作業ディレクトリを設定するVOLUME:ボリュームのマウントする場所を設定するCMD:コンテナを※実行可能コンテナとして設定する※「実行可能コンテナ」とは、他のコンテナのベースイメージとして利用するのではなく、docker runで直接実行することを目的としたコンテナイメージのこと。
詳細はDockerfile referenceで見ることが出来ます。
では、URLを使ってWebサイトの中身を取得するイメージを作り、テキストファイルに保存してみましょう!
『SITE_URL』という環境変数を経由してWebサイトのURLをパスする必要があります。実行結果のファイルはボリュームとしてマウントされて、ディレクトリの中に配置されます。
Dockerfileという名前のファイルを
examples/curlディレクトリの中に配置して、次のような内容を書いてください。DockerfileFROM ubuntu:latest RUN apt-get update \ && apt-get install --no-install-recommends --no-install-suggests -y curl \ && rm -rf /var/lib/apt/lists/* ENV SITE_URL http://example.com/ WORKDIR /data VOLUME /data CMD sh -c "curl -Lk $SITE_URL > /data/results"Dockerfileの準備が出来ました。それでは、実際にイメージをビルドしてみましょう!
examples/curlディレクトリに移動して、次のコマンドを実行してイメージをビルドしてください。docker build . -t test-curlコンソールの出力結果Sending build context to Docker daemon 3.584kB Step 1/6 : FROM ubuntu:latest ---> 113a43faa138 Step 2/6 : RUN apt-get update && apt-get install --no-install-recommends --no-install-suggests -y curl && rm -rf /var/lib/apt/lists/* ---> Running in ccc047efe3c7 Get:1 http://archive.ubuntu.com/ubuntu bionic InRelease [242 kB] Get:2 http://security.ubuntu.com/ubuntu bionic-security InRelease [83.2 kB] ... Removing intermediate container ccc047efe3c7 ---> 8d10d8dd4e2d Step 3/6 : ENV SITE_URL http://example.com/ ---> Running in 7688364ef33f Removing intermediate container 7688364ef33f ---> c71f04bdf39d Step 4/6 : WORKDIR /data Removing intermediate container 96b1b6817779 ---> 1ee38cca19a5 Step 5/6 : VOLUME /data ---> Running in ce2c3f68dbbb Removing intermediate container ce2c3f68dbbb ---> f499e78756be Step 6/6 : CMD sh -c "curl -Lk $SITE_URL > /data/results" ---> Running in 834589c1ac03 Removing intermediate container 834589c1ac03 ---> 4b79e12b5c1d Successfully built 4b79e12b5c1d Successfully tagged test-curl:latest
docker buildはローカルで新しいイメージをビルドしてます。-tはnameタグをイメージに取り付けてます。これで新しいイメージを持つことが出来ました。ここで、今現在持っているイメージを一覧で見てみましょう。
docker imagesコンソールの出力結果REPOSITORY TAG IMAGE ID CREATED SIZE test-curl latest 5ebb2a65d771 37 minutes ago 180 MB nginx latest 6b914bbcb89e 7 days ago 182 MB ubuntu latest 0ef2e08ed3fa 8 days ago 130 MBイメージからコンテナを作ったり、実行させる事が出来ます。デフォルトのパラメータを使ってやってみましょう。
docker run --rm -v $(pwd)/vol:/data/:rw test-curl実行した結果がファイルに保存されたことを確認するには、次のコマンドを実行します。
cat ./vol/resultsFacebook.comで試してみましょう。
docker run --rm -e SITE_URL=https://facebook.com/ -v $(pwd)/vol:/data/:rw test-curl実行した結果がファイルに保存されたことを確認するには、次のコマンドを実行します。
cat ./vol/results効率よくイメージを作るためのbest practice
必要なコンテキストのみを含める :
.dockerignoreファイルを使用する (gitの.gitignoreのように)不要なパッケージをインストールしない :
余分なディスク容量が消費されます。キャッシュを使用する:
Dockerfileの最後に大きな変更を加えるコンテキスト(プロジェクトのソースコードなど)を追加します。Dockerキャッシュを効果的に利用します。ボリュームに注意する:
ボリューム内のデータが何であるかを覚えておく必要があります。ボリュームは永続的であり、コンテナーと共存しないため、次のコンテナーは前のコンテナーによって作成されたボリュームのデータを使用します。環境変数を使用する(RUN、EXPOSE、VOLUMEなど):
Dockerfileを変更しやすくなります。Alpineイメージについて
多くのDockerイメージ(イメージのバージョン)がAlpine Linux上に作成されています。これは容量を分散化させてDockerイメージの全体サイズを縮小するためです。
Redis, Postgresなどのサードパーティサービスには、Alpineベースのイメージを使うことをオススメします。
Appイメージにはbuildpackベースのイメージを使用してください。コンテナ内でデバッグするのが簡単になりますし、システム全体に必要な要素が事前にインストールされてます。どのベースイメージを使用するかを決めるだけではなく、このように1つの基本イメージを使用することによって最大の効果を得ることができます。これは、キャッシュがより効果的に使用されるからです。
Example 4:コンテナ同士を接続してみよう
Docker composeを使っていきます。
Docker composeとはコンテナ同士を接続するためのCLIです。
pip経由でインストールする事が出来ます。sudo pip install docker-compose今回の例では、PythonコンテナとRedisコンテナを接続してみたいと思います。
version: '3.6' services: app: build: context: ./app depends_on: - redis environment: - REDIS_HOST=redis ports: - "5000:5000" redis: image: redis:3.2-alpine volumes: - redis_data:/data volumes: redis_data:example/composeにカレントディレクトリを移動して、次のコマンドを実行してみてください。
docker-compose upコンソールの結果Building app Step 1/9 : FROM python:3.6.3 3.6.3: Pulling from library/python f49cf87b52c1: Pull complete 7b491c575b06: Pull complete b313b08bab3b: Pull complete 51d6678c3f0e: Pull complete 09f35bd58db2: Pull complete 1bda3d37eead: Pull complete 9f47966d4de2: Pull complete 9fd775bfe531: Pull complete Digest: sha256:cdef88d8625cf50ca705b7abfe99e8eb33b889652a9389b017eb46a6d2f1aaf3 Status: Downloaded newer image for python:3.6.3 ---> a8f7167de312 Step 2/9 : ENV BIND_PORT 5000 ---> Running in 3b6fe5ca226d Removing intermediate container 3b6fe5ca226d ---> 0b84340fa920 Step 3/9 : ENV REDIS_HOST localhost ---> Running in a4f9a1d6f541 Removing intermediate container a4f9a1d6f541 ---> ebe63bf5959e Step 4/9 : ENV REDIS_PORT 6379 ---> Running in fd06aa65fd33 Removing intermediate container fd06aa65fd33 ---> 2a581c31ff4f Step 5/9 : COPY ./requirements.txt /requirements.txt ---> 671093a12829 Step 6/9 : RUN pip install -r /requirements.txt ---> Running in b8ea53bc6ba6 Collecting flask==1.0.2 (from -r /requirements.txt (line 1)) Downloading https://files.pythonhosted.org/packages/7f/e7/08578774ed4536d3242b14dacb4696386634607af824ea997202cd0edb4b/Flask-1.0.2-py2.py3-none-any.whl (91kB) Collecting redis==2.10.6 (from -r /requirements.txt (line 2)) Downloading https://files.pythonhosted.org/packages/3b/f6/7a76333cf0b9251ecf49efff635015171843d9b977e4ffcf59f9c4428052/redis-2.10.6-py2.py3-none-any.whl (64kB) Collecting click>=5.1 (from flask==1.0.2->-r /requirements.txt (line 1)) Downloading https://files.pythonhosted.org/packages/34/c1/8806f99713ddb993c5366c362b2f908f18269f8d792aff1abfd700775a77/click-6.7-py2.py3-none-any.whl (71kB) Collecting Jinja2>=2.10 (from flask==1.0.2->-r /requirements.txt (line 1)) Downloading https://files.pythonhosted.org/packages/7f/ff/ae64bacdfc95f27a016a7bed8e8686763ba4d277a78ca76f32659220a731/Jinja2-2.10-py2.py3-none-any.whl (126kB) Collecting itsdangerous>=0.24 (from flask==1.0.2->-r /requirements.txt (line 1)) Downloading https://files.pythonhosted.org/packages/dc/b4/a60bcdba945c00f6d608d8975131ab3f25b22f2bcfe1dab221165194b2d4/itsdangerous-0.24.tar.gz (46kB) Collecting Werkzeug>=0.14 (from flask==1.0.2->-r /requirements.txt (line 1)) Downloading https://files.pythonhosted.org/packages/20/c4/12e3e56473e52375aa29c4764e70d1b8f3efa6682bef8d0aae04fe335243/Werkzeug-0.14.1-py2.py3-none-any.whl (322kB) Collecting MarkupSafe>=0.23 (from Jinja2>=2.10->flask==1.0.2->-r /requirements.txt (line 1)) Downloading https://files.pythonhosted.org/packages/4d/de/32d741db316d8fdb7680822dd37001ef7a448255de9699ab4bfcbdf4172b/MarkupSafe-1.0.tar.gz Building wheels for collected packages: itsdangerous, MarkupSafe Running setup.py bdist_wheel for itsdangerous: started Running setup.py bdist_wheel for itsdangerous: finished with status 'done' Stored in directory: /root/.cache/pip/wheels/2c/4a/61/5599631c1554768c6290b08c02c72d7317910374ca602ff1e5 Running setup.py bdist_wheel for MarkupSafe: started Running setup.py bdist_wheel for MarkupSafe: finished with status 'done' Stored in directory: /root/.cache/pip/wheels/33/56/20/ebe49a5c612fffe1c5a632146b16596f9e64676768661e4e46 Successfully built itsdangerous MarkupSafe Installing collected packages: click, MarkupSafe, Jinja2, itsdangerous, Werkzeug, flask, redis Successfully installed Jinja2-2.10 MarkupSafe-1.0 Werkzeug-0.14.1 click-6.7 flask-1.0.2 itsdangerous-0.24 redis-2.10.6 You are using pip version 9.0.1, however version 10.0.1 is available. You should consider upgrading via the 'pip install --upgrade pip' command. Removing intermediate container b8ea53bc6ba6 ---> 3117d3927951 Step 7/9 : COPY ./app.py /app.py ---> 84a82fa91773 Step 8/9 : EXPOSE $BIND_PORT ---> Running in 8e259617b7b5 Removing intermediate container 8e259617b7b5 ---> 55f447f498dd Step 9/9 : CMD [ "python", "/app.py" ] ---> Running in 2ade293ecb25 Removing intermediate container 2ade293ecb25 ---> b85b4246e9f8 Successfully built b85b4246e9f8 Successfully tagged compose_app:latest WARNING: Image for service app was built because it did not already exist. To rebuild this image you must use `docker-compose build` or `docker-compose up --build`. Creating compose_redis_1 ... done Creating compose_app_1 ... done Attaching to compose_redis_1, compose_app_1 redis_1 | 1:C 08 Jul 18:12:21.851 # Warning: no config file specified, using the default config. In order to specify a config file use redis-server /path/to/redis.conf redis_1 | _._ redis_1 | _.-``__ ''-._ redis_1 | _.-`` `. `_. ''-._ Redis 3.2.12 (00000000/0) 64 bit redis_1 | .-`` .-```. ```\/ _.,_ ''-._ redis_1 | ( ' , .-` | `, ) Running in standalone mode redis_1 | |`-._`-...-` __...-.``-._|'` _.-'| Port: 6379 redis_1 | | `-._ `._ / _.-' | PID: 1 redis_1 | `-._ `-._ `-./ _.-' _.-' redis_1 | |`-._`-._ `-.__.-' _.-'_.-'| redis_1 | | `-._`-._ _.-'_.-' | http://redis.io redis_1 | `-._ `-._`-.__.-'_.-' _.-' redis_1 | |`-._`-._ `-.__.-' _.-'_.-'| redis_1 | | `-._`-._ _.-'_.-' | redis_1 | `-._ `-._`-.__.-'_.-' _.-' redis_1 | `-._ `-.__.-' _.-' redis_1 | `-._ _.-' redis_1 | `-.__.-' redis_1 | redis_1 | 1:M 08 Jul 18:12:21.852 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128. redis_1 | 1:M 08 Jul 18:12:21.852 # Server started, Redis version 3.2.12 redis_1 | 1:M 08 Jul 18:12:21.852 # WARNING overcommit_memory is set to 0! Background save may fail under low memory condition. To fix this issue add 'vm.overcommit_memory = 1' to /etc/sysctl.conf and then reboot or run the command 'sysctl vm.overcommit_memory=1' for this to take effect. redis_1 | 1:M 08 Jul 18:12:21.852 # WARNING you have Transparent Huge Pages (THP) support enabled in your kernel. This will create latency and memory usage issues with Redis. To fix this issue run the command 'echo never > /sys/kernel/mm/transparent_hugepage/enabled' as root, and add it to your /etc/rc.local in order to retain the setting after a reboot. Redis must be restarted after THP is disabled. redis_1 | 1:M 08 Jul 18:12:21.852 * The server is now ready to accept connections on port 6379 app_1 | * Serving Flask app "app" (lazy loading) app_1 | * Environment: production app_1 | WARNING: Do not use the development server in a production environment. app_1 | Use a production WSGI server instead. app_1 | * Debug mode: on app_1 | * Running on http://0.0.0.0:5000/ (Press CTRL+C to quit) app_1 | * Restarting with stat app_1 | * Debugger is active! app_1 | * Debugger PIN: 170-528-240このリンクをクリックしてブラウザで確認してみてください。
docker-composeの使い方は今回の趣旨とはズレるので、割愛させて頂きます。
最初はDocker Hubのイメージで試してみるのがいいでしょう。もし、自分のイメージを作りたい時は上記に乗せた「効率よくイメージを作るためのbest practice」を参照してみてください。
docker-composeを使うことに関していうと、docker-compose.ymlを書く時になるべく分かりやすい名前をボリュームにつけてください。
これをやるだけで、将来ボリュームを調べる時に様々な問題に苦しまずに済みます。version: '3.6' services: ... redis: image: redis:3.2-alpine volumes: - redis_data:/data volumes: redis_data:この場合、redis_dataは
docker-compose.ymlファイル内で名付けられた名前となります。
最終的なボリューム名は、プロジェクト名を接頭辞につけたものとなります。ボリュームを見てみましょう。
docker volume lsコンソールの結果DRIVER VOLUME NAME local apptest_redis_dataDocker Way
Dockerにはいくつかの制限や必要条件があり、それらはシステム(コンテンの中に入っているアプリ)の構成によって変化します。もちろん、これらの制限を無視したり、一時的に対処したりする事も出来ますが、Dockerの恩恵を100%受ける事が出来なくなってしまいます。
私は下記の条件に従うことを強くオススメします。
1アプリケーション= 1コンテナ
プロセスをフォアグラウンドで実行する(systemd、upstart、その他の類似のツールは使用しないでください)。
データをコンテナに入れずに、ボリュームを使用してください。
SSHを使用しないでください(コンテナーの中に入る必要がある場合は、docker execコマンドを使用できます)。
コンテナの中で手動で設定しないで下さい
最後に
最後にまとめると、DockerはIDEやGitのように開発者にとって必須なツールとなりました。すぐに使えて、豊富なサービスを備えているサービスと言えます。
Dockerは、プロジェクトの規模や複雑さに関係なく、色々な場面で使用できます。はじめにcomposeとSwarmから始めて、プロジェクトが拡大したらAmazon Container ServicesやKubernetesなどのクラウドサービスに移行する事が出来ます。
貨物輸送で使用される実際のコンテナと同様に、コードをDockerコンテナに入れると、より迅速で効率的なCI / CDプロセスを構築するのに役立ちます。
これは、ギーク達によって促進されている単なる技術トレンドではありません。
DockerはPayPal、Visa、Swisscom、General Electric、Splinkなどの大企業で設計時にすでに使用されています!
- 投稿日:2019-05-22T08:56:55+09:00
Go+Postgres環境をDockerComposeで作ってみる
前回、Goの環境を作りました。
Webサーバーらしく、DBアクセスとかをする前に、DockerComposeで環境を整えようかと思います。環境
- ホストOS:Windows + VirtualBOX + Vagrant
- ゲストOS:CentOS7+Docker
ディレクトリ構成
ゲストOSのディレクトリ構成は以下の通り
- ~/docker/
- docker-compose.yml
- Dockerfile
- main.go → 前回作成したもの
各種ファイル
docker-compose.ymlversion: '2' services: go-web: build: . ports: - "8080:8080" links: - go-db working_dir: /develop/go environment: - "DB_NAME=postgres" - "DB_USER=postgres" - "DB_PASSWORD=postgres" volumes: - "./:/develop/go:z" go-db: image: postgres:latest ports: - "5432:5432" environment: - "POSTGRES_USER=postgres" - "POSTGRES_PASSWORD=postgres" volumes: - "/data/db:/var/lib/postgresql/data:z"FROM golang:latest RUN go get github.com/gin-gonic/gin EXPOSE 8080 CMD ["go", "run", "main.go"]立ち上げてみる
- 前回作成したmain.goを上記の2ファイルと同じ場所に格納
docker-compose upを実行最後に
今回、Postgresコンテナを立ち上げてはいるが、使っていないため今後はWebサーバーらしくDBアクセス等のコードも書いてみようと思う。
- 投稿日:2019-05-22T08:13:05+09:00
Prometheusで永続化ディレクトリがpermission deniedになる時の対処法
問題
Prometheus公式のDocekrイメージを使い、以下の設定でPrometheusをデプロイ。
docker-compose.ymlvolumes: - /data/prometheus:/prometheusデプロイ後にログを確認すると、
err="opening storage failed: lock DB directory: open /prometheus/lock: permission denied"というエラーでコンテナが停止していた。
原因
Prometheus公式のDockerfile
Dockerfile... RUN mkdir -p /prometheus && \ chown -R nobody:nogroup etc/prometheus /prometheus USER nobody EXPOSE 9090 .../data/prometheusのディレクトリ情報 (例)
$ ls -l /data drwxrwxr-x 3 user1 users 4096 5月 21 00:00 prometheus/Dckerfileを見るとUSER nobodyが設定されている。
そのためnobodyユーザでディレクトリに書き込みが行われるが、
マウントした/data/prometheusディレクトリの所有者がnobodyではなかったためエラーになってしまった。ちなみに、Prometheusコンテナの/prometheusディレクトリのパーミッションは、
デフォルトで以下のようになっている。$ ls -l / drwxr-xr-x 3 nobody nogroup 4096 May 21 00:00 prometheus解決策
1. 実行ユーザをrootに変更する
rootユーザでPrometheusを実行する。
rootユーザで書き込みが行われるためエラーにならないが、
コンテナに必要以上の権限を持たせてしまうためおすすめしない。docker-compose.ymlに
user: rootを追記docker-compose.ymlversion: "2" services: prometheus: image: prom/prometheus:v2.9.2 container_name: prometheus user: root #追記 ports: - 9090:9090 volumes: - /data/prometheus:/prometheus2. その他ユーザにread, write権限を追加する
誰でも読み書きできるようになってしまうので注意が必要。
chmodでパーミッションを変更
$ sudo chmod 777 /data/prometheus3. ディレクトリの所有者を変更する
chownで所有者を変更
$ sudo chown nobody /data/prometheus合わせてディレクトリのグループを管理者のグループに設定しておく。
$ sudo chgrp monitor /data/prometheus参考
- 投稿日:2019-05-22T07:59:12+09:00
SSH proxy コマンドの使い方について
ある機器へSSHアクセスする際、他のサーバ(jump server)を経由しないといけない場合は、SSHのproxyコマンドは有効である。簡単な環境でその使い方について説明する。
環境
- OS X: Mojave
- Docker desktop ver 2.0.0.3
環境構築
仮に以下のようなな環境ある。ユーザが
target serverへアクセスしたい。もしくは、targetサーバとscpでファイル交換したい。しかし、セキュリティ制限によって、ユーザから直接にtarget serverへアクセスするのは許可されていない。かならずproxy serverを経由する。一番シンプルなのは、順次にproxyサーバへログインしてから、targetサーバへログインして、作業さうる毎回2段ログインすると大変である。より簡単な方法はSSHのproxyコンマンドを使うことである。簡単にSSH proxyコマンドを説明するために、以下のNWをdockerイメージを使い説明する。
+-------------+ +--------------+ +---------------+ | ユーザ +---+ proxy server +---+ target server | +-------------+ +--------------+ +---------------+Dockerイメージ
まず、簡単に環境をつくるため、sshサーバを動くdockerイメージを作成する。
以下のDockerfileを準備する。
FROM centos:centos7 # OpenSSH サーバをインストールする RUN yum -y install openssh-server openssh-clients # root でログインできるようにする RUN sed -ri 's/^#PermitRootLogin yes/PermitRootLogin yes/' /etc/ssh/sshd_config # root のパスワードを 設定 RUN echo 'rootpassword!' | passwd --stdin root # 使わないにしてもここに公開鍵を登録しておかないとログインできない RUN ssh-keygen -t rsa -N "" -f /etc/ssh/ssh_host_rsa_key RUN ssh-keygen -t ecdsa -N "" -f /etc/ssh/ssh_host_ecdsa_key RUN ssh-keygen -t ed25519 -N '' -f /etc/ssh/ssh_host_ed25519_key # sshd の使うポートを公開する EXPOSE 22 # sshd を起動する CMD ["/usr/sbin/sshd", "-D"]必要に応じて
rootpassword!を変更し、Dockerfileと同じフォルダーでイメージを作成する。$ docker build -t centos7-sshd .成功すると、以下のようなイメージが出来上がる。
bash-3.2# docker images centos7-sshd REPOSITORY TAG IMAGE ID CREATED SIZE centos7-sshd latest d9c6d7216802 22 hours ago 299MBこのイメージを使い、2つコンテナーを起動させる。なお、
proxyサーバを従来のport 22の代わりに、20022を待ち受けるようにした。$ docker run -d --hostname centos7-sshd-proxy -p 20022:22 --name centos7-sshd-proxy centos7-sshd:latest $ docker run -d --hostname centos7-sshd-target --name centos7-sshd-target centos7-sshd:latestここで2つsshサーバのコンテナが動くようになる。次回、コンテナーを起動したいときは簡単に
startでできる。$ docker start centos7-sshd-proxy centos7-sshd-target2段ログイン
前節で作った2つのコンテナのSSH portは以下のようになる。
+-------------+ +--------------+ +---------------+ | ユーザ +----(20022)-+ proxy server +---(22)-+ target server | +-------------+ +--------------+ +---------------+ユーザがホストから127.0.0.2:20022へ
proxyサーバへアクセスできる。$ ssh -p 20022 root@127.0.0.1 Enter passphrase for key '/Users/<省略>/.ssh/id_rsa': root@127.0.0.1's password: X11 forwarding request failed on channel 0 Last login: Mon May 20 11:33:29 2019 from gateway更に、そこからport 22で
targetサーバへSSHログインできる。ここで、proxyからtargetへアクセスする際、docker内部のNWを使う。そのIPは以下のように確認できる$ docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' centos7-sshd-target 172.17.0.3このIPを使い、
targetへSSHをしてみる[root@centos7-sshd-proxy ~]# ssh root@127.17.0.3 <省略> root@127.17.0.3's password: Last login: Mon May 20 11:40:34 2019 from gateway [root@centos7-sshd-proxy ~]#proxyサーバを経由してtargetへログインする環境を構築して、確認できた。
proxyコマンド
上のように2段ログインより、proxyコマンドを使うとこより、直接にtargetへログインすることができる。
bash-3.2# ssh -o ProxyCommand='ssh -p 20022 -W %h:%p root@127.0.0.1' root@172.17.0.3 root@127.0.0.1's password: root@172.17.0.3's password: Last login: Mon May 20 11:47:38 2019 from 172.17.0.2 [root@centos7-sshd-target ~]#パスワードが2回に聞かれ、1回目はproxyサーバのもので、2回目はtargetのものである。proxyコマンドは以下のようなフォーマットになる。
ssh -o ProxyCommand='ssh -l root -p 20022 -W %h:%p <proxyユーザ>@<proxyサーバ>' <targetユーザ>@<targetアドレス>もうちょっと遊ぶ
とりあえず、やりたいことはできた。しかし、よく
%h:%pはデフォルトホスト名とデフォルトポートだと説明されるが、少し意味わからない。ちょっといじってみたbash-3.2# ssh -o ProxyCommand='ssh -p 20022 -W 172.17.0.3:22 root@127.0.0.1' xxx root@127.0.0.1's password: root@xxx's password: Last login: Mon May 20 11:57:13 2019 from 172.17.0.2 [root@centos7-sshd-target ~]#これで、proxy経由でtargetへログインできた。それに、途中でなぞのの
root@xxx's password:表示になっている。もうちょっと調べてみる。まず、
-Wについてsshのmanは以下のように書いてある-W ホスト :ポート クライアントの標準入力と標準出力を、安全な通信路を通してホスト のポート 上に接続します。このオプションは、-N ,-T ,ExitOnForwardFailureおよびClearAllForwardingsを暗黙のうちに含んでいますが、これらは設定ファイルまたは-o オプションにより上書きすることができます。
-Wはフォワードの設定するものである。今回の目的であれば、proxyサーバへログインした後、全てinput/output は %h:%p へフォワードされている。すなわち、ユーザからの操作はすべてtargetへフォワードされている。なお、%h,%pはProxyCommand中の特別意味を持っている。%hはProxyCommandの後(本来接続したい)のホストで、%pはその接続の時にポート番号である。%hは明示的に書かれている場合、ホストはあまり意味がもっていなくなる(上のxxx)sshログインと同様に、
scpとProxyコマンドを使い、`targetサーバ`とフィアルやりとりも可能$ scp -o ProxyCommand='ssh -p 20022 -W %h:%p root@127.0.0.1' test.txt root@172.17.0.3:/tmp/まとめ
簡単にsshサーバのdockerイメージをサクセスおよびそのイメージを使い、sshのproxyコマンドの追加たを説明した。
参考
- 投稿日:2019-05-22T02:45:14+09:00
DockerImageをカスタマイズしてDockerHubに登録する
DockerImageをカスタマイズしDockerHubに登録する
はじめに
この記事は、公式で公開されているDockerImageに一部アプリケーションの設定に変更を加えたDockerImageを作成し、DockerHubに登録する手順を記したものです。
私自身Dockerはあまり詳しくなく、デフォルトのDockerImageに少しだけ変更を加えてコンテナを建てる場合にはどうすればいいのかが分からずに、色々調べたり試したりしてみて最終的に実践した方法をまとめています。この記事の対象者
- DockerImageを少しだけ変えて保存したんだけれども、どうするんだ?って人
この記事を見てできること
- DockerImageに変更を加えたものを新たにDockerImageとして登録できる
- 上記のDockerImageをDockerHubに登録できる
なぜDockerImageをカスタマイズしたくなったのか
具体的には、以下の作業を実施している時に、困りごとが2つありました。
Docker-composeでECS上にコンテナを起動してログ収集・分析基盤(ELK)を構築する公式で公開されているDockerImageだとメモリの関係でコンテナが起動しない
最初は、EC2のt2.smallインスタンス(メモリ2GB)の中でElasticsearchとLogstashを起動しようと試みていました。しかし、公式で公開されているDockerImageの初期設定では、これらのアプリケーションで占有するメモリがそれぞれ1GBでした。
つまり、そのままのDockerImageではElasticsearchとLogstashが両方起動出来ずに、コンテナが落ちてしまいます(ぴったり2GBだし起動出来るじゃん!!となる訳もありませんでした…)。そこで、コンテナ起動前にアプリケーションで占有するメモリの設定を変更する必要がありました。
コンテナを起動する度に、特定のファイルを都度配置する必要があった(面倒)
Logstashではconfファイルの設定に基づいて、アプリケーションが動作します。公式で公開されているDockerImageからコンテナを起動した時点では、このconfファイルが配置されておりません。そのため、コンテナ起動後に手動でconfファイルを配置するという、イケていない運用をしていました。
対応策として、Dockerのボリューム(Data Volume)を使用してマウントすることで、データを永続化すればいいじゃんという話もあるかと思います。しかし、ECインスタンス上でコンテナを起動しており、単純にローカルのディレクトリとコンテナ間でボリュームをマウントすることが出来ませんでした(ただ単に知識不足というのもあります。またEBSボリュームも使いたくなかった)。
そんなことがあり、「このDockerImageをベースとして、メモリの設定ファイルだけ書き換えたいな・・・、このconfファイルをコンテナ起動時に配置したいな・・・」と思うことが度々ありました。
方法:設定ファイルの書き換え & confファイルの配置
docker-composeで環境変数を設定する
元々docker-composeを使って、ECSでコンテナを起動していたため、docker-compose.ymlの環境変数の設定を追記しました。アプリケーションのメモリの設定については、こちらで対応できました。DockerImageをカスタマイズしてDockerHubに登録する
既にconfファイルが配置された状態のDockerImageを作成して、docker-composeで読み込むイメージにしてしまおうという発想に至りました。confファイルの配置については、こちらで対応できました。手順
- docker-composeで環境変数を設定する
単純に環境変数(environment)に設定するだけでした。 環境変数って、どこまで設定できて・設定できないものは何なのかイマイチ分かっていません。公式ページやググった結果、環境変数を設定している事例を漁りながら設定していました。この辺は何を見ればわかるのでしょうか…docker-compose.ymlversion: "2" services: elasticsearch: image: docker.elastic.co/elasticsearch/elasticsearch:7.0.0 mem_limit: 1g ports: - "9200:9200" - "9300:9300" environment: - discovery.type=single-node - bootstrap.memory_lock=true - "ES_JAVA_OPTS=-Xms512m -Xmx512m" ulimits: memlock: soft: -1 hard: -1 nofile: soft: 65536 hard: 65536 networks: - elastic-stack logstash: image: gogogonkun/logstash_pub mem_limit: 768m ports: - "5044:5044" environment: - "LS_JAVA_OPTS=-Xms512m -Xmx512m" networks: - elastic-stack networks: elastic-stack:
- DockerImageをカスタマイズしてDockerHubに登録する
こっちが本題だったような… まぁ気にせずに…以下のページが参考になりました。
独自のDockerImageを作成する
DockerImageをDockerHubに登録の仕方まずは編集したいDockerImageからコンテナを起動する。
docker run -it gogogonkun/logstash bash適当にファイルを配置したり、編集したりする
vi /usr/share/logstash/pipeline/movhis.conf input { http { port => 5044 response_headers => { "Access-Control-Allow-Origin" => "*" "Content-Type" => "text/plain" "Access-Control-Allow-Headers" => "Origin, X-Requested-With, Content-Type, Accept" } } } ※省略変更内容から新たにDockerImageを作成
docker container ps docker container commit [CONTAINER ID] [イメージ名(DockerHubのリポジトリ名と合わせる)]:[タグ] 例:docker container commit a94665bc5f13 gogogonkun/logstash_pub:latestDockerHubへイメージをプッシュする
docker push gogogonkun/logstash_pub:latestDockerHubに登録できています。
docker-composeで起動するコンテナのイメージとして使用する場合には、登録されたリポジトリ名を指定すると、カスタマイズしたDockerImageが起動します。おわりに
今回の対応方法が正しいかどうかは分かりません。詳しい人が居たら教えて欲しいです。あと、日本語ガバガバなのは大目に見つつもご指摘いただければ嬉しいです。































































































































