- 投稿日:2020-12-23T19:12:09+09:00
AWS Data PipelineのShellCommandActivityでDockerイメージを(暫定的に)動かす
背景・目的
我々のチームでは、以前からAWS Data Pipelineを利用して、次のような形でETLのプログラムを管理していました。
- RubyやPythonや必要なツールが入ったAMIを用意する
- ETLプログラムはチーム管理サーバーのGitlabで管理し、masterブランチへのpush時にJenkinsでzipで固めてS3にアップロードする
- Data PipelineのShellCommandActivityでEC2サーバーを立ち上げ、S3からソースコードをダウンロードして実行する
ただ、数年間の運用を経て、以下のような問題点が出てきています。
- AMIのバージョンアップが面倒で、できればコンテナ化してDockerfileで管理したいこと。Data Pipeline自体にはDockerイメージの実行機能が無いこと
- 全社的にGithubのOrganizationが標準になったが準拠できておらず、独自管理のGitlabやJenkinsが負担になっていること
- (これは我々の使い方が悪いのですが)Data Pipelineを単なるcron機能しか使っておらず、バッチの依存関係が定義できていないこと
ただ、システム全体を一気に移行するのは大変なので、次のような方針で実行しようとしています。
- まず(特に新規の)バッチ処理をコンテナ化してGithubに移行し、Github ActionsでECRにpushする
- ひとまずData Pipelineでそのコンテナイメージを実行するようにする
- その後、DataPipeline自体は後で適切なワークフローエンジンとコンテナ実行基盤を利用した形に移行する
一旦Data Pipelineを利用することにしたのは、バッチの再実行方法など運用が今までと大きく変わらないようにして、無理なくプロジェクトを進めるのが目的です。そのためあまりきれいな形ではなく、より適切なサービス(AWSではAWS BatchやMWAAなど)を利用すべきところかもしれませんが、一旦はData Pipeline上でコンテナイメージを実行する方法を調べて実装しました。
また、「DataPipelineを使ってdockerコンテナを定期実行してみる」という記事を見つけたのですが、とりあえずの運用のためにECSクラスターを導入するのも大変なので、Data Pipelineで起動したEC2インスタンス自体にDockerを実行させる方法にしています。
実装方法
次のような方針で実装しました。
- 前提としてGithub Actionsを利用して、Dockerイメージ自体はECRに既にpushされている状態
- AMIは素のAmazon Linux 2 (idは
ami-00f045aed21a5524064 ビット x86) を利用する- 認証情報などは、パラメータストアにkmsで暗号化した形で保存し、そこから取得して環境変数やファイル形式でイメージ内に渡す
- 詳しくは公式ドキュメントの「AWS Systems Manager Parameter Store で AWS KMS を使用する方法」を読んでください
- 余談ですが「余力があれば、環境変数よりコマンドラインのオプションとして渡したほうがいい。一般的にコマンドラインパーサーのほうがバリデーションがしっかりしているため」というアドバイスも貰っています
ShellCommandActivityの実装
次のようなシェルスクリプトで実装しました。以下が注意点です。
aws ssm get-parameterで、テキストのみ取得するためのオプションはこちらの記事を参考にした- ほとんどのバッチでBigQueryを利用しているため、GCPのcredentialsをファイルに吐き出してイメージ内に渡している
- Dockerイメージ内でawscliやAWS SDKを利用する場合、
AWS_ACCESS_KEY_IDとAWS_SECRET_ACCESS_KEYとしてコンテナ内に渡す必要がある。事前に適切な権限を持ったIAMユーザーを作る必要があるset -euを行うことで、エラーが出た行で処理を止めることができる。詳しくは「シェルスクリプトを書くときはset -euしておく」を参照。- awscli2のインストール方法がx86かARMかでも違うので、公式ドキュメントを参照してください
もう少しスッキリ書けそうな気もしますが、ひとまずこれで行ってます。
set -eu account="{AWSアカウントID}" region="{リージョン名}" repository="{ECRのリポジトリ名}" tag="latest" batch="{Dockerの引数}" sudo yum -y update # パラメータストアから環境変数や設定ファイルを読み込む IMAGE_AWS_ACCESS_KEY_ID=`aws ssm get-parameter --name "{パラメータストアのキー名}" \ --with-decryption --region "${region}" --output text --query Parameter.Value` IMAGE_AWS_SECRET_ACCESS_KEY=`aws ssm get-parameter --name "{パラメータストアのキー名}" \ --with-decryption --region "${region}" --output text --query Parameter.Value` aws ssm get-parameter --name "{パラメータストアのキー名}" --with-decryption \ --region "${region}" --output text --query Parameter.Value > /tmp/bigquery.json # `aws ecr get-login-password`を使う方式がAWS CLIのバージョン2からなのでインストールする # DataPipelineの再実行時はファイルが残っているので、削除コマンドが無いとエラーが起きる場合がある rm -rf ./aws curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip" unzip awscliv2.zip sudo ./aws/install --update # Dockerをインストールして起動 sudo amazon-linux-extras install -y docker sudo service docker start # ECRにログインしてイメージをpullする aws ecr get-login-password --region "${region}" | \ sudo docker login --username AWS --password-stdin "https://${account}.dkr.ecr.${region}.amazonaws.com" sudo docker pull "${account}.dkr.ecr.${region}.amazonaws.com/${repository}:${tag}" # 環境変数や設定ファイルを渡して実行する sudo docker run --env "AWS_ACCESS_KEY_ID=${IMAGE_AWS_ACCESS_KEY_ID}" \ --env "AWS_SECRET_ACCESS_KEY=${IMAGE_AWS_SECRET_ACCESS_KEY}" \ --env "GOOGLE_APPLICATION_CREDENTIALS=/credential/bigquery.json" -v "/tmp/bigquery.json:/credential/bigquery.json:ro" \ "${account}.dkr.ecr.${region}.amazonaws.com/${repository}" "${batch}"Resource Roleの設定方法
もう一つ設定で困るのが、EC2インスタンスに付与されるResource Roleです。基本的にはDockerイメージ内で処理は実行されるので、そこで利用するIAMユーザーに適切に権限を持たせればいいのですが、ECRやパラメータストアを参照できる権限を付与する必要があります。
- AmazonEC2RoleforDataPipelineRoleなどのDataPipeline自体のロギング等に必要なポリシー
- AmazonEC2ContainerRegistryReadOnlyなどのECSからpullするためのポリシー
- パラメータストアからデータを取得するためのロール(以下に記載)
- 復号のために、暗号化で利用したkmsも許可する必要があります
- 特に重要な情報なので、ここは厳しく権限を設定しています
パラメータストアからデータを取得するためのポリシー{ "Version": "2012-10-17", "Statement": [ { "Sid": "VisualEditor0", "Effect": "Allow", "Action": "ssm:DescribeParameters", "Resource": "*" }, { "Sid": "VisualEditor1", "Effect": "Allow", "Action": "ssm:GetParameters", "Resource": "arn:aws:ssm:ap-northeast-1:{アカウントID}:parameter/{適切なパラメータストアの階層}/*" }, { "Sid": "VisualEditor2", "Effect": "Allow", "Action": "ssm:GetParameter", "Resource": "arn:aws:ssm:ap-northeast-1:{アカウントID}:parameter/{適切なパラメータストアの階層}/*" }, { "Sid": "VisualEditor3", "Effect": "Allow", "Action": [ "kms:Decrypt" ], "Resource": [ "arn:aws:kms:ap-northeast-1:{アカウントID}:key/{復号化キー}" ] } ] }まとめ
これらの設定で、今まで通りData Pipelineでのバッチ管理を止めずに、コンテナ移行を進めることができます。道のりは長いですががんばります。
- 投稿日:2020-12-23T19:08:14+09:00
Docker + DynamoDB local + C++ の環境構築と実践まで
環境構築
今回使うものはこちら
- Docker
- Dynamodb local
- AWS CLI バージョン 2
- vcpkg
- AWS SDK for C++
また、C++ のビルド環境には VC++ を使用します。
Docker の導入
Docker は、コンテナを用いてアプリケーションをすばやく構築、テスト、デプロイできるソフトウェアプラットフォームです。
ダウンロード
Docker のサイトからインストーラをダウンロードしましょう。
※この記事では、Docker Desktop for Windows を使用します。インストール
Docker Desktop requires Windows 10 Pro/Enterprise (16299+) or Windows 10 Home (18362.1040+).
インストール中に、上のようなエラーメッセージが出る場合は、Windows Update を確認してみてください。アップデート後に、もう一度インストールを試してみましょう。バージョン確認
コマンドラインからDocker のバージョンを確認してみましょう。インストールに成功していれば、以下のような出力が得られるはずです。
> docker --version Docker version 20.10.0, build 7287ab3DynamoDB local の導入
ダウンロード可能なバージョンの Amazon DynamoDB (DynamoDB local)では、DynamoDB ウェブサービスにアクセスせずに、アプリケーションを開発してテストすることができます。代わりに、データベースはコンピュータ上で自己完結型となります。
イメージを取得する
Docker を使って、DynamoDB local イメージを取得しましょう。
Amazon が公開している DynamoDB local のDocker イメージがありますので、
docker pullコマンドを使って取得します。> docker pull amazon/dynamodb-localコンテナを起動する
Docker を使って、DynamoDB local を起動しましょう。
docker runコマンドを使って起動します。> docker run -p 8000:8000 amazon/dynamodb-localこれで DynamoDB local のコンテナが起動しました。わかりやすくポート番号は8000を使用しました。
コンテナを確認する
docker psコマンドを使うと、現在起動しているコンテナのリストが出力されます。> docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 607c8a202aac amazon/dynamodb-local "java -jar DynamoDBL…" 2 seconds ago Up 7 seconds 0.0.0.0:8000->8000/tcp frosty_goldstineコンテナを終了する
docker killコマンドを使うと、起動しているコンテナを終了させることができます。このとき、コンテナIDを指定します。> docker kill 607c8a202aacAWS CLI バージョン 2 の導入
AWS CLI には、DynamoDB local を操作するためのコマンドラインツールが含まれています。
※今回はテーブル情報の読み出しのためだけに利用したいと思います。(C++ からのリクエストが正しく通っているかを確認するため。)事前にインストールしておきましょう。
https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/install-cliv2-windows.html初回設定
1度だけこちらの4項目を設定しておきましょう。
それぞれ、設定内容はダミーでもよいですが、忘れないように覚えておく必要があります。もし忘れてしまったら設定しなおしましょう。
> aws configure AWS Access Key ID [****************XXXX]: (ローカルで使用するのでダミーのキーIDでよい) AWS Secret Access Key [****************XXXX]: (ローカルで使用するのでダミーのアクセスキーでよい) Default region name [us-west-2]:(ローカルで使用するのでダミーのリージョンでよい) Default output format [json]:動作確認(DynamoDB local のテーブルリストを取得する)
DynamoDB local にアクセスして、動作確認をしてみましょう。
まだテーブルを作っていないので、以下のような JSON が取得できるはずです。> aws dynamodb list-tables --endpoint-url http://localhost:8000 { "TableNames": [] }AWS SDK for C++ の導入
今回は、C++ からDynamoDB local にアクセスするので、AWS SDK for C++ というライブラリを使用します。
vcpkg をインストールする
vcpkg は、C++ のコマンド ライン パッケージ マネージャーです。 これにより、Windows、Linux、および macOS でのサードパーティ ライブラリの取得とインストール作業を大幅に簡素化できます。
https://github.com/Microsoft/vcpkg
github から取得し、ブートストラップを実行しましょう。
> git clone https://github.com/microsoft/vcpkg > ./vcpkg/bootstrap-vcpkg.batAWS SDK for C++ を取得する
vcpkg を使って、ライブラリを取得しましょう。
> ./vcpkg/vcpkg install aws-sdk-cpp:x64-windowsVC++ にインテグレートする
vcpkg を VC++ にインテグレートします。
これによって、先ほどダウンロードしたAWS SDK for C++ のためのインクルードパスの設定や、.lib のリンクを自動で行ってくれるようになります。> ./vcpkg/vcpkg integrate install※ただし、コーディング中のインテリセンスを働かせるためには、手動でインクルードパスを通す必要がありました。
また、インテグレートを解除したい場合は、以下のコマンドを使います。
> ./vcpkg/vcpkg integrate removeコーディング
主要なデータベース操作と、対応する AWS SDK for C++ の関数名を挙げておきます。
AWS のサイトにもサンプルコードがあるので、参考にしてみてください。
用途 関数名 テーブル追加 Aws::DynamoDB::DynamoDBClient::CreateTable テーブル削除 Aws::DynamoDB::DynamoDBClient::DeleteTable アイテム取得 Aws::DynamoDB::DynamoDBClient::GetItem アイテム追加 Aws::DynamoDB::DynamoDBClient::PutItem アイテム更新 Aws::DynamoDB::DynamoDBClient::UpdateItem アイテム削除 Aws::DynamoDB::DynamoDBClient::DeleteItem C++ Code Samples for Amazon DynamoDB
https://docs.aws.amazon.com/code-samples/latest/catalog/code-catalog-cpp-example_code-dynamodb.htmlテーブル作成(create-table)
#include <aws/core/Aws.h> #include <aws/core/utils/Outcome.h> #include <aws/dynamodb/DynamoDBClient.h> #include <aws/dynamodb/model/AttributeDefinition.h> #include <aws/dynamodb/model/CreateTableRequest.h> #include <aws/dynamodb/model/KeySchemaElement.h> #include <aws/dynamodb/model/ProvisionedThroughput.h> #include <aws/dynamodb/model/ScalarAttributeType.h> #include <iostream> // ここで、予め設定しておいたアクセスキーを使います const Aws::String AWS_ACCESS_KEY_ID = "XXXXXXXXXXXXXXXXXXXX"; const Aws::String AWS_SECRET_ACCESS_KEY = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"; // 作成したいテーブルの名前を決める const Aws::String table("Game"); // ここで、予め設定しておいたリージョン名を使います const Aws::String region("us-west-2"); // エンドポイントを指定する Aws::Client::ClientConfiguration clientConfig; clientConfig.requestTimeoutMs = 1000; clientConfig.region = region; clientConfig.endpointOverride = "http://localhost:8000"; const Aws::DynamoDB::DynamoDBClient dynamoClient(Aws::Auth::AWSCredentials(AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY), clientConfig); // テーブル作成リクエスト Aws::DynamoDB::Model::CreateTableRequest req; Aws::DynamoDB::Model::AttributeDefinition haskKey; // "Name"という名前の属性を定義する、"Name"は文字列型である haskKey.SetAttributeName("Name"); haskKey.SetAttributeType(Aws::DynamoDB::Model::ScalarAttributeType::S); req.AddAttributeDefinitions(haskKey); // "Name" をハッシュキー(パーティションキー)として扱うようにスキーマを定義する Aws::DynamoDB::Model::KeySchemaElement keyscelt; keyscelt.WithAttributeName("Name").WithKeyType(Aws::DynamoDB::Model::KeyType::HASH); req.AddKeySchema(keyscelt); // 適当なキャパシティユニットを設定する Aws::DynamoDB::Model::ProvisionedThroughput thruput; thruput.WithReadCapacityUnits(5).WithWriteCapacityUnits(5); req.SetProvisionedThroughput(thruput); // テーブル名を指定する req.SetTableName(table); // テーブル作成リクエストをDynamoDB local に送信する(この関数はブロックするので注意!) const Aws::DynamoDB::Model::CreateTableOutcome& result = dynamoClient.CreateTable(req); if (result.IsSuccess()) { std::cout << "Table \"" << result.GetResult().GetTableDescription().GetTableName() << " created!" << std::endl; } else { std::cout << "Failed to create table: " << result.GetError().GetMessage(); }AWS CLI を使って、テーブルが作成できているかの確認をしてみましょう。
うまく作成できていれば、以下のような JSON が取得できるはずです。> aws dynamodb list-tables --endpoint-url http://localhost:8000 { "TableNames": [ "Game" ] }アイテム追加(put-item)
#include <aws/core/Aws.h> #include <aws/core/utils/Outcome.h> #include <aws/core/auth/AWSCredentialsProvider.h> #include <aws/dynamodb/DynamoDBClient.h> #include <aws/dynamodb/model/AttributeDefinition.h> //#include <aws/dynamodb/model/CreateTableRequest.h> #include <aws/dynamodb/model/PutItemRequest.h> #include <aws/dynamodb/model/PutItemResult.h> #include <iostream> //(途中省略...) Aws::DynamoDB::Model::PutItemRequest req; // 対象のテーブル名を指定する req.SetTableName(table); Aws::DynamoDB::Model::AttributeValue av; // "Name" が 文字列型"Switch" av.SetS("Switch"); req.AddItem("Name", av); // "State" が 文字列型"off" av.SetS("off"); req.AddItem("State",av); // アイテム追加リクエストをDynamoDB local に送信する(この関数はブロックするので注意!) const Aws::DynamoDB::Model::PutItemOutcome result = dynamoClient.PutItem(req); if (!result.IsSuccess()) { std::cout << result.GetError().GetMessage() << std::endl; return 1; } std::cout << "Done!" << std::endl;AWS CLI を使って、テーブルの内容を出力してみましょう。
aws dynamodb scanコマンドにテーブル名を渡すと、以下のようにテーブルの内容を JSON で取得できます。> aws dynamodb scan --table-name Game --endpoint-url http://localhost:8000 { "Items": [ { "State": { "S": "off" }, "Name": { "S": "Switch" } } ], "Count": 1, "ScannedCount": 1, "ConsumedCapacity": null }おわりに
非常に長い道のりでしたが、DynamoDB local を C++ から操作することができました。AWS SDK for C++ は扱いやすく、DynamoDB を操作するための一通りのAPI が揃っています。これを導入すれば、C++ プログラマにとっては DynamoDB の敷居が一気に下がるのではないでしょうか。
DynamoDB local は 簡単にレコードを追加したり、削除したりできるので、プロトタイピングにもおススメですね。
- 投稿日:2020-12-23T17:28:26+09:00
kubernetes を本番運用していくために Sorry Page イメージを作っておくと幸せになれるかもしれない話
この記事は WESEEK Advent Calendar 2020 25日目の記事です。
![]()
はじめに
皆さん Kubernetes (以後 k8s) 使ってますよね!最近では本番環境上でも k8s を採用する事例を見かけるようになってきました。
今回は前向きな夢の話ではなく、ちょっと泥臭いお話です。
Sorry Page って何?
下記の画像のような「サービスがメンテナンス中だよ」ということを訪問者にお伝えするページです。
サンプルは弊社で提供しているサービスの GROWI.cloud の Sorry Page です。
宣伝: GROWI.cloud は、Markdown で書ける wiki GROWI を簡単に使い始めることができるサービスです。GROWI や同時編集機能を使うための HackMD を構築する手間を大幅に削減できますので、GROWI.cloud を是非ご利用ください。
なんでやろうと思ったの? (モチベーション)
k8s を運用していくにあたって、「落ちないサービス」や「メンテナンス時間が発生しないサービス」を構築することが理想的だとは思います。(だって、そういう仕組みが用意されているんだもの)
ですが、どうしても一回ユーザからのアクセスを止めてから作業を実施した方が安全なこともあります。(なるべく止めないように試行錯誤するにしたとしても)
それに加え、いざというときにアクセスを止める手段があるという保険的な安心感も得られます。
Sorry Page イメージの作り方
では Sorry Page のイメージを作るための準備を進めていきましょう。
こんな感じのディレクトリ構成でファイルを用意します。
. ├── README.md ├── assets │ ├── css │ │ └── Sorry Page に必要な CSS │ ├── favicon.ico │ ├── img │ │ └── Sorry Page に必要な 画像 │ ├── js │ └── Sorry Page に必要な JS ├── docker │ ├── Dockerfile │ └── nginx │ └── front.conf.template ├── index.html └── kubernetes └── deployment.yaml各種ファイルの内容を紹介していきます。
index.html,assetsは Sorry Page 自体の HTML, CSS, JS などを書いておきます。
Dockerfileは基本的に nginx イメージをベースに nginx 用のコンフィグファイル、index.html, assets が含まれるように COPY や ADD をしています。docker/Dockerfile# HOW TO BUILD: # $ docker build -t growi-cloud-sorry: -f docker/Dockerfile . FROM weseek/nginx-auth-ldap:xxxxx LABEL maintainer="XXX XXXX <xxxxxxx>" LABEL description="This image is used for running to serve application files" LABEL app=growi-cloud LABEL role=release LABEL component=nginx COPY docker/nginx/front.conf.template /etc/nginx/conf.d/default.conf.template COPY . /usr/share/nginx/html/ # kubernetes のヘルスチェック用のファイル RUN touch /usr/share/nginx/html/.health-check
front.conf.templateは/usr/share/nginx/html配下のファイルを配信できるシンプルな設定にしています。docker/nginx/front.conf.template# BASED ON nginx:1.13.6-alpine /etc/nginx/conf.d/default.conf server { listen 80; server_name localhost; gzip off; # Cache-controlを付ける add_header Cache-Control "max-age=0, public"; location / { root /usr/share/nginx/html; # redirect to https if accessed via http if ($http_x_forwarded_proto = "http") { return 301 https://$host$request_uri; } add_header Cache-Control "max-age=0, public"; index index.html index.htm; } error_page 500 502 503 504 /50x.html; location = /50x.html { root /usr/share/nginx/html; } }
kubernetesディレクトリ配下は、k8s クラスタに Sorry Page イメージを deploy するときに使う manifest です。
deployment.yamlの image の部分はexample.comで置換しています。kubernetes/deployment.yamlapiVersion: extensions/v1beta1 kind: Deployment metadata: labels: app: growi-cloud-sorry name: growi-cloud-sorry namespace: growi-cloud spec: progressDeadlineSeconds: 600 replicas: 1 revisionHistoryLimit: 10 selector: matchLabels: app: growi-cloud-sorry strategy: rollingUpdate: maxSurge: 1 maxUnavailable: 0 type: RollingUpdate template: metadata: labels: app: growi-cloud-sorry spec: containers: - image: example.com/growi/growi-cloud-sorry:master imagePullPolicy: Always name: growi-cloud-sorry-demo ports: - containerPort: 80 protocol: TCP resources: {} terminationMessagePath: /dev/termination-log terminationMessagePolicy: File restartPolicy: Alwaysイメージのビルド
Dockerfileの中にコメントとして書きましたが、作成したプロジェクト用のディレクトリトップでdocker buildを実行します$ docker build -t growi-cloud-sorry -f docker/Dockerfile .k8s クラスタにデプロイして、Sorry Page に誘導する
想定する構成は下記の通りです。色々と省略していますが、サービスに対するアクセスの導線は
ユーザ -> Ingress -> Service -> Podという流れでリクエストが受け渡される、という前提とします。
deployment.yamlを k8s クラスタに apply して、Sorry Page イメージを deploy しますkubectl apply -f kubernetes/deployment.yaml反映されると、下記の図のように GROWI.cloud Sorry Page Pod に向け変えができるような状態になります。
この状態で GROWI.cloud Service の内容を
kubectl edit ...で書き換えることで、向け先を変えます。
Service は
selectorの部分をgrowi-cloud-sorryに書き換えます。apiVersion: v1 kind: Service metadata: name: my-service spec: selector: app: growi-cloud # <- ここを growi-cloud-sorry に書き換える (省略)...アクセス先の切替方法
ここは非常に議論を呼びそうですが、 GKE Ingress の書き換えを実施すると反映に多少時間がかかるので、切替方法は用途に応じて実施するのが良いと思います。
- 即座にメンテ画面に変えたい場合は Service を書き換えて切り替える
- 時間に余裕がある場合(計画メンテなど)には GKE Ingress 側を書き換えて切り替える
最後に
今回は下記の流れで Sorry Page イメージの紹介をしました。
- Sorry Page イメージの作り方
- 作った Sorry Page イメージを k8s クラスタにデプロイする方法
- Sorry Page Pod へのアクセス先の切り替え方法
いざというときには助かるかもしれませんが、なるべく Sorry Page イメージに頼らないで、サービス運用ができるように目指して行きたいですね
![]()
- 投稿日:2020-12-23T16:16:50+09:00
GitHub Actionsでprivate repositoryのPythonライブラリを含むDockerイメージをビルドする
背景・目的
Github ActionsでPythonのイメージをビルドするとき、別のprivate repositoryにある社内用ライブラリを持ってくる方法についてまとめました。
既に同僚の @elyunim26 の「GitHub Actionsでビルドするコンテナ内でGitHubのprivate repositoryをセキュアに参照する」という記事があったのですが、私はマシンユーザーではなくデプロイキーを利用していたため、安全にssh認証をする方法が必要でした。Build-time secretsを使うことでsshキーも安全にイメージのビルドに利用できます。
実装方法
簡単に説明すると、以下のような手順で実現できます。
- Github Actionsでsecretsからデプロイキー(秘密鍵)を読み込む
- DockerのBuildkitの機能を使ってセキュアに受け渡す
- 通常通り
pip installを行うデプロイキーの設定
まず、ライブラリ側のリポジトリにデプロイキーを設定する必要があります。デプロイキーの設定方法はGithubの公式ドキュメントを参照してください。
Github Actionsの設定
次のような要素に気をつける必要があります。トラブルシューティングには(Pythonとnode.jsという違いはありますが)GitHub Actions で private repository の node module をインストールするが役に立ちました。
GITHUBで始まるsecretsは禁止されている- secretsでは改行が使えないため、事前に改行を
\nなどに置換した形で登録して、sedで置換するなどの回避方法が必要以下は設定例です。我々のチームではAWSを利用しているため、後半にECRにpushするコードも含まれています。
github_actions_ecr.yml- name: Build, tag, and push image to Amazon ECR id: build-image env: ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }} ECR_REPOSITORY: ${{ steps.extract_repository.outputs.repository }} IMAGE_TAG: ${{ github.sha }} DEPLOY_KEY_GITHUB: ${{ secrets.DEPLOY_KEY_GITHUB }} run: | echo ${DEPLOY_KEY_GITHUB} > .deploy_key sed -i -e "s#\\\\n#\n#g" .deploy_key chmod 600 .deploy_key DOCKER_BUILDKIT=1 docker build --secret id=ssh,src=.deploy_key \ -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG \ -t $ECR_REGISTRY/$ECR_REPOSITORY:latest \ . rm .deploy_key docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG docker push $ECR_REGISTRY/$ECR_REPOSITORY:latestDockerfileの設定
我々のチームではpoetryを使うことが多いので
pyproject.tomlを利用していますが、requirements.txtを使う場合でも同様の設定でいけるはずです。Dockerfile# syntax=docker/dockerfile:experimental FROM python:3.7 # ssh-keyscanの実行のため、元にするイメージによってはopenssh-clientのインストールが必要 RUN apt-get install -y openssh-client git COPY ./pyproject.toml /app/pyproject.toml COPY ./src /app/src WORKDIR /app RUN mkdir -m 700 $HOME/.ssh RUN ssh-keyscan -H github.com > $HOME/.ssh/known_hosts RUN --mount=type=secret,id=ssh,dst=$HOME/.ssh/id_rsa \ pip install --upgrade pip && \ pip install . && rm pyproject.toml
pyproject.tomlでは次のように記述されています。これはpoetryの公式ドキュメントにもある通り、poetry addコマンドでリポジトリを指定することで作成されるものです。pyproject.toml[tool.poetry.dependencies] {ライブラリ名} = {git = "ssh://git@github.com/{ユーザー名}/{ライブラリ名}.git", rev = "main"}参考にした記事
- 投稿日:2020-12-23T16:11:46+09:00
Dockerイメージとオレオレ証明書組み込みレシピ その2
SSL 自己署名証明書と Docker レシピ その2
日立グループ OSS AdventCalendar #2 24日目は、OSSソリューションセンタ 神山 から投稿します。
前回記事(1枚目1日目)にて、Dockerイメージに証明書を組み込む方法を記載しました。今回は、その応用編をいくつか書いていきたいと思います。
応用編: マルチステージ・ビルドで組み込む
マルチステージ・ビルドとは、Docker 17.05 移行で導入された機能で、イメージのビルドを複数のステージに分けて実行できる機能です。マルチステージ・ビルド登場以前は、ビルド時のみ必要なパッケージもイメージに含まれてしまい、イメージの小容量化が難しかったのですが、マルチステージ・ビルドでは、多段階にビルドを分離することで、最終的に配布するイメージ(実行環境)からビルド時にのみ必要なパッケージを分離することが容易となります。
どのOSにしても、ca-certificates パッケージなど、何かしら インストールする必要があり、最小主義で環境を作りたい場合は、ちょっと気持ち悪い状態になります。
そんな時は、Docker のマルチステージ・ビルド を用いるときれいなイメージを作れます。
FROM ubuntu as certs RUN apt-get update -y && apt-get install -y ca-certificates COPY self-signed.cert /usr/local/share/ca-certificates/extra/self-signed.crt RUN update-ca-certificates FROM ubuntu COPY --from=certs /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crtこれは、1回目のビルドで通常通り証明書の登録ツール(update-ca-certificates)のインストールとオレオレ証明書のコピー及び登録をします。
そして2回目のビルドでは、update-ca-certificates の出力結果(/etc/ssl/certs/ca-certificates.crt) のみを新しいイメージに持ってきます。最終的なイメージは、次のようなきれいなイメージになります。
- オレオレ証明書を含む、システムが信頼する証明書(/etc/ssl/certs/ca-certificates.crt) は存在する
- update-ca-certificates はインストールされていない
- 登録のための一時ファイル(/usr/local/share/ca-certificates/extra/self-signed.crt) は存在しない
マルチステージ・ビルドはきれいなイメージを作りたいにはとても便利です。
応用編: 初見のイメージに証明書を登録する(Jenkins イメージ の例)
ここまでOSやミドルウェアをベースとする場合に、どうやって証明書を登録するかを扱ってきましたが…
実際は特定のサービス(Jenkinsなど…)で証明書を登録したいことも多いかと思います。
自己流なのでもしかしたら効率悪い・邪道な方法かもしれないです。もしもっと良い方法があればコメントでご指摘ください!試しに Jenkins を例に、証明書を登録する方法を調べてみます。
証明書の登録は root 権限で行う必要がありますが、多くのイメージは、実行権限が root 以外になっています。そこでまずはイメージの作りを見ていくのですが…
Docker Hub で Jenkins を見ると Dockerfile が公開されておりません。こういうときは、Docker イメージ自体を調べてみます。
docker pull jenkins docker history jenkins | grep "USER"このように、jenkins イメージを取得し、history コマンドでどのようにイメージが作られたかを確認します。
この際、今回知りたいコンテナの実行ユーザーを設定している "USER" で絞り込みます。すると、
<missing> 2 years ago /bin/sh -c #(nop) USER [jenkins] 0Bこのような形で、"jenkins" ユーザーに設定されていることがわかります。
次にOSを特定したいのですが、Dockerfile が公開されていない場合、あたりを付けて調べてみるしかないです。
私はよく、docker run jenkins aptといった具合に、パッケージマネージャをたたいてみて、コマンドの有無で調べています。
たまにパッケージマネージャすらも消してしまうような、ギリギリを攻めたイメージがあり、この方法で特定できないこともありますが…経験的にはこの方法でほぼ特定できました。ここまでわかると、後は 次のような Dockerfile を書いて、ビルドしてあげればOKです。
FROM jenkins USER root COPY self-signed.cert /usr/local/share/ca-certificates/extra/self-signed.crt RUN update-ca-certificates USER jenkins
- ユーザをroot に戻す
- OSに合わせた方法で証明書をインストール(この場合は debian)
- ここでは ca-certificates パッケージはインストールしていません(jenkins イメージではインストール済)
- 多くの場合 ca-certificates パッケージが既にインストールされているため、まずは存在すること前提で呼び出すのがおすすめです。稀にインストールされていない場合がありエラーとなりますので、その場合は、 ca-certificates パッケージ のインストールも手順に組み込むと良いです
- ユーザを元に戻す
- 元のイメージと使用感を合わせるために、実行ユーザは元に戻しておいた方が良いです
これで、正しくオレオレ証明書を認識するイメージが出来上がります。
さいごに
今回は、SSL証明書の登録を行う際に、イメージを小さくする方法や、初見イメージに対して組み込む流れを書いてみました。
この辺りは手探りな部分もあるので、もっと良い方法があれば、改善していきたいです。
- 投稿日:2020-12-23T13:11:44+09:00
GreengrassでDockerアプリケーションをデプロイする
はじめに
会社の業務でAWS IoT Greengrassについて勉強したので、Ubuntu搭載デバイスとAWS IoTを利用したアプリケーションを紹介したいと思います。
今回はGreengrassでDockerアプリケーションをデプロイする方法12を紹介します。過去の記事:
UbuntuにAWS IoT Greengrassをインストールする
GreengrassのLambdaを作成してエッジデバイスにデプロイする※先日、Greengrass V23がリリースされましたが、この記事ではV1を使用します。
環境
動作確認済デバイス(OS)
e-RT3 Plus F3RP70-2L4(Ubuntu 18.04 32bit)
横河電機のエッジコントローラです。AWS IoT Greengrassの認定デバイス5に登録されています(e-RT3のページはこちら)。Raspberry Pi 4 Model B (Ubuntu Server 20.04 32bit)
これらのデバイスでは armhf アーキテクチャのパッケージが動作します。
また、Windows 10 搭載のPCでデバイスを操作しています。作業の流れ
Dockerアプリケーションの作成
開発用PCでDockerアプリケーションを開発します。Dockerイメージのプッシュ
Dockerイメージを作成してAmazon Elastic Container Registry (ECR)にプッシュします。docker-composeのアップロード
docker-composeを作成してAmazon Simple Storage Service (S3)にアップロードします。Dockerアプリケーションのデプロイ
必要なロールをGreengrass Groupに付与し、Dockerアプリケーションのデプロイメントコネクタ1を使用してDockerアプリケーションをデバイスにデプロイします。Dockerコンテナが起動し、アプリケーションが稼働します。準備
AWS CLIのインストール
PCにAWS CLIをインストールします。
インストール方法は以下のガイドを参照してください。
Windows での AWS CLI バージョン 2 のインストール、更新、アンインストール初回使用時には設定を行う必要があります。詳しくは以下のガイドを参照してください。
設定の基本 - AWS Command Line Interface※PCがproxy環境下にある場合はproxy設定が必要です。詳しくはコマンドのプロキシ設定をご覧ください。
Docker Desktop on Windowsのインストール
PCにDocker Desktop on Windowsをインストールします。
インストール方法はこちらのページを参照してください。※PCがproxy環境下にある場合はproxy設定が必要です。詳しくはこちらのページをご覧ください。
Python3.7のインストール
デバイスにGreengrassでDockerアプリケーションをデプロイするために必要なPython3.7をインストールします。
※デバイスがproxy環境下にある場合はproxy設定が必要です。詳しくはこちらをご覧ください。
e-RT3の場合
sudo apt update sudo apt install python3.7username@ubuntu:~$ python3.7 --version Python 3.7.5Raspberry Pi (Ubuntu Server 20.04 32bit)の場合
aptでインストールできないので、ソースからビルドしてインストールします。
ビルドに必要なパッケージをインストールします。sudo apt update sudo apt install build-essential libbz2-dev libdb-dev \ libreadline-dev libffi-dev libgdbm-dev liblzma-dev \ libncursesw5-dev libsqlite3-dev libssl-dev \ zlib1g-dev uuid-dev公式サイトからPython3.7のソースをダウンロードしてビルドします。
wget https://www.python.org/ftp/python/3.7.9/Python-3.7.9.tar.xz tar xvf Python-3.7.9.tar.xz cd Python-3.7.9 ./configure make sudo make altinstall cd ~インストールの成功を確認します。
username@ubuntu:~$ python3.7 --version Python 3.7.9dockerのインストール
デバイスにdockerとdocker-composeをインストールします。
docker
e-RT3の場合
最新版をインストールするために、公式ドキュメント6に従ってインストールします。
dockerのインストールに必要なパッケージをインストールします。sudo apt update sudo apt install \ apt-transport-https \ ca-certificates \ curl \ gnupg-agent \ software-properties-common資格情報7に必要なパッケージをインストールします。
sudo apt install passGPGキーを追加します。
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -フィンガープリントの下8桁で検索し、キーが追加されたことを確認します。
username@ubuntu:~$ sudo apt-key fingerprint 0EBFCD88 pub rsa4096 2017-02-22 [SCEA] 9DC8 5822 9FC7 DD38 854A E2D8 8D81 803C 0EBF CD88 uid [ unknown] Docker Release (CE deb) <docker@docker.com> sub rsa4096 2017-02-22 [S]リポジトリを追加します。
sudo add-apt-repository \ "deb [arch=armhf] https://download.docker.com/linux/ubuntu \ $(lsb_release -cs) \ stable"dockerエンジンをインストールします。
sudo apt update sudo apt install docker-ce docker-ce-cli containerd.io #サービス起動 sudo systemctl start docker #サービス自動起動 sudo systemctl enable dockerusername@ubuntu:~$ docker --version Docker version 20.10.1, build 831ebeaRaspberry Pi(Ubuntu Server 20.04 32bit)の場合
Dockerの公式リポジトリにUbuntu20.04 armhf用のパッケージがないので、docker.ioでインストールします。
sudo apt install docker.io #サービス起動 sudo systemctl start docker #サービス自動起動 sudo systemctl enable dockerusername@ubuntu:~$ docker --version Docker version 19.03.8, build afacb8b7f0インストールの成功を確認します。
テスト用のイメージでコンテナを起動し、以下のようなメッセージが表示されたら成功です。username@ubuntu:~$ sudo docker run hello-world Hello from Docker! This message shows that your installation appears to be working correctly. ...※デバイスがproxy環境下にある場合はproxy設定が必要です。詳しくはdockerのプロキシ設定をご覧ください。
docker-compose
pipを利用してインストールします8。
必要なパッケージをインストールします。
sudo apt update sudo apt install libffi-dev libssl-dev python3-dev python3-venv仮想環境を作成してpipでインストールします。
python3 -m venv venv source venv/bin/activate (venv) username@ubuntu:~$ pip install wheel (venv) username@ubuntu:~$ pip install docker-compose (venv) username@ubuntu:~$ sudo cp venv/bin/docker-compose /usr/bin (venv) username@ubuntu:~$ deactivateインストールの成功を確認します。
username@ubuntu:~$ docker-compose --version docker-compose version 1.27.4, build unknownユーザー設定
GreengrassからデプロイされたDockerアプリケーションを実行するユーザーの設定を行います。
デフォルトではggc_userとなるので、今回はこのユーザーを使用します。S3バケットからダウンロードしたdocker-composeを保存するディレクトリを作成し、アクセス権を設定します。
sudo mkdir /home/ggc_user/myCompose sudo chown ggc_user:ggc_group /home/ggc_user/myCompose sudo chmod 700 /home/ggc_user/myComposeggc_userをdockerグループに追加します。
sudo usermod -aG docker ggc_user一度ログアウトして設定を反映させます。
1. Dockerアプリケーションの作成
PCでDockerアプリケーションを作成します。
今回は、アクセスすると「Hello World from Docker Container!」というメッセージを表示するPythonのwebアプリケーションを作成します。
以下の3つのファイルを同一のフォルダに保存してください。Flaskを利用したPythonのプログラムです。アクセスすると「Hello World from Docker Container!」というメッセージを表示します。
app.pyfrom flask import Flask app = Flask(__name__) @app.route("/") def hello(): return "Hello World from Docker Container!" if __name__ == "__main__": app.run()必要なPythonのパッケージを記したファイルです。今回はFlaskのみ利用します。
requirements.txtFlaskDockerのイメージを作成する際のコマンドを記したファイルです。
32bit arm用のイメージに必要なファイルやパッケージをインストールしています。DockerfileFROM arm32v7/python:3.7-alpine WORKDIR /usr/src/app ENV FLASK_APP app.py ENV FLASK_RUN_HOST 0.0.0.0 COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt COPY . . CMD ["flask", "run"]2. Dockerイメージのプッシュ
Dockerイメージを作成してECRにプッシュします。
リポジトリの作成
イメージの作成とプッシュ
PCのDocker Desktop on Windowsを起動しておきます。
Dockerアプリケーションの作成で作成したDockerfileのあるディレクトリでコマンドプロンプトを開き、表示されたコマンドを順番に実行します。
ECRにdockerイメージがプッシュされたことを確認します。
イメージのURIをこの後の手順で使用するので控えておきます。
3. docker-composeのアップロード
docker-composeの作成
PCでdocker-composeを作成します。
以下の内容をdocker-compose.ymlという名前で保存してください。
<image-uri>の部分はイメージの作成とプッシュで控えておいたURIで置き換えてください。docker-compose.ymlversion: '3' services: web: image: "<image-uri>" ports: - "5000:5000"S3バケットの作成
docker-composeのアップロード
「ファイルを追加」をクリックしdocker-composeの作成で作成したdocker-compose.ymlをアップロードします。
4. Dockerアプリケーションのデプロイ
ロールの作成とアタッチ
GreengrassがS3バケット、ECRにアクセスするために必要なロールを作成して、Greengrassグループにアタッチします。
ポリシーの作成
JSONタブをクリックして以下のJSONを入力し、S3バケットへのアクセスを許可します。
<bucket-name>の部分はS3バケットの作成で作成したS3バケット名に置き換えてください。
入力が完了したら「ポリシーの確認」をクリックします。{ "Version": "2012-10-17", "Statement": [ { "Sid": "AllowAccessToComposeFileS3Bucket", "Action": [ "s3:GetObject" ], "Effect": "Allow", "Resource": "arn:aws:s3:::<bucket-name>/*" } ] }同様の手順で、以下のJSONを使用してECRへのアクセスを許可するポリシーを作成します。
<region>、<account-id>の部分は使用しているリージョンとアカウントIDで、<repository-name>の部分はリポジトリの作成で作成したリポジトリ名で置き換えてください。{ "Version": "2012-10-17", "Statement": [ { "Sid": "AllowGetEcrRepositories", "Effect": "Allow", "Action": [ "ecr:GetDownloadUrlForLayer", "ecr:BatchGetImage" ], "Resource": [ "arn:aws:ecr:<region>:<account-id>:repository/<repository-name>" ] }, { "Sid": "AllowGetEcrAuthToken", "Effect": "Allow", "Action": "ecr:GetAuthorizationToken", "Resource": "*" } ] }ロールの作成
- 「IAM」→「ロール」と進み、「ロールの作成」をクリックします。
ポリシーの作成で作成した2つのポリシーを選択します。
タグは設定せずに次へ進みます。
ロールのアタッチ
ロールの作成で作成したロールを選択して「保存」をクリックします。
デプロイと動作確認
S3に保存したdocker-compose.ymlと、ユーザー設定で作成した、デバイス上でdocker-composeを保存するディレクトリを指定して、「追加」をクリックします。
デプロイ完了後、しばらく待ってからPCで
http://<デバイスのIPアドレス>:5000にアクセスします。
※デバイスとPCが同一のネットワークに属している必要があります。以下の画面が表示されたら成功です。
※うまくいかない場合はデバイスの/greengrass/ggc/var/log/以下にあるログを確認してください。補足
環境により設定は異なりますが、参考までに今回私が行ったproxy設定を紹介します。
コマンドのプロキシ設定
Windowsのコマンドプロンプトで以下の内容を入力します。
set HTTP_PROXY=http://username:password@example.com:port/ set HTTPS_PROXY=http://username:password@example.com:port/dockerのプロキシ設定
Dockerのドキュメント9に従ってプロキシ設定を行います。
まずディレクトリを作成し、そのディレクトリ内にファイルを作成します。
sudo mkdir -p /etc/systemd/system/docker.service.d sudo vi /etc/systemd/system/docker.service.d/http-proxy.conf下記のフォーマットで
HTTP_PROXY、HTTPS_PROXYの環境変数をファイルに書き込み、保存します。
どちらか一方のみで良い場合やNO_PROXYの設定が必要な場合など、環境に合わせて適宜内容を変更してください。/etc/systemd/system/docker.service.d/http-proxy.conf[Service] Environment="HTTP_PROXY=http://username:password@example.com:port/" Environment="HTTPS_PROXY=http://username:password@example.com:port/"設定を反映するためにデーモンをリロードし、再起動します。
sudo systemctl daemon-reload sudo systemctl restart docker設定内容が反映されているか確認します。
# 一般ユーザusernameの場合 username@ubuntu:~$ sudo systemctl show --property=Environment docker Environment=HTTP_PROXY=http://username:password@example.com:port/ HTTPS_PROXY=http://username:password@example.com:port/参考
- 投稿日:2020-12-23T11:58:42+09:00
既存ネットワークに後から作成したコンテナを接続する方法
したいこと
- 1つの
docker-compose.ymlに複数コンテナの記述をするのではなく、既存ネットワークに後から作成したコンテナを接続したい。参考
作成
net1というネットワークを作成docker create network net1接続
docker-compose.ymlversion: "3" services: potmum: image: tukiyo3/potmum:20170807 restart: always volumes: - /etc/localtime:/etc/localtime - ./db/production.sqlite3:/srv/potmum/db/production.sqlite3 - ./attachment_files/:/srv/potmum/public/attachment_files/ environment: PRIVATE_MODE: 0 USE_ATTACHMENT_FILE: 1 COLOR_THEME: "green" # USE_GITHUB: 0 USE_DEVELOPER: 1 networks: default: external: name: net1docker-compose.ymlversion: "3" services: nginx: image: nginx:alpine restart: always volumes: - ./nginx/htpasswd:/etc/nginx/htpasswd - ./nginx/default.conf:/etc/nginx/conf.d/default.conf ports: - 80:80 networks: default: external: name: net1
- 投稿日:2020-12-23T11:51:10+09:00
dockerでmysqlが立ち上がらなくなった。
問題点
dockerで開発しようと、docker compose up -dしようとしたら、こんなのが出た。
failed to solve: rpc error: code = Unknown desc = failed to solve with frontend dockerfile.v0: failed to create LLB definition: failed to authorize: failed to fetch anonymous token: Get https://auth.docker.io/token?scope=repository%3Alibrary%2Fmysql%3Apull&service=registry.docker.io: net/http: TLS handshake timeout解決方法
こちらで直りました。
docker-compose build --no-cache docker compose up -d
- 投稿日:2020-12-23T02:26:17+09:00
Node-REDなコンテナをワンタッチビルドするためのレポジトリの構造
Node-RED Advent Caldendar 2020 12/18の記事です。
Node-REDのフロー管理、どうしていますか?
私はフローファイルをgit管理するように最近なりました。@high-uさん Node-RED Advent Caldendar 2018記事: Node-RED で git 使ってる?
そしてこれを毎回同じ品質で検証環境を立ち上げたいという主旨で、
もうやってるよ!という方もいらっしゃると思いますが軽くまとめて行こうと思います。Gitレポジトリの準備
レポジトリは2つにしました。
1つのレポジトリにFlowを置いても良いのですが、今回はNode-REDで動くロボット学習キットのTJBotのVirtual版、VirtualTJBotプロジェクトの派生版で、ローカルで動くVirtualTJBot-starter-containerをサンプルに紹介します。
repository 説明 https://github.com/tjbotfan/virtual-tjbot-handson/ Node-REDプロジェクトのレポジトリ https://github.com/tjbotfan/virtual-tjbot-starter-container DockerFileを置いたレポジトリ DockerFileを置いたレポジトリの準備
https://github.com/tjbotfan/virtual-tjbot-starter-containerでは、DockerFileの他にgit submoduleでNode-REDプロジェクのトレポジトリをリンクしています。
submoduleとすることでgit cloneしたときには手元に一緒に中身がダウンロードされるけれど、
git checkout時にはsubmodule内に影響されないという点が便利です。(Node-REDのフローはあくまでも別プロジェクトであり管理は別)Submoduleと一緒に手元にレポジトリを持ってくるときは
$ git clone --recursive https://github.com/tjbotfan/virtual-tjbot-starter-containerとします。
取得したあとのレポジトリは以下のようになります。
$ cd virtual-tjbot-starter-container/ $ ls -lR .: 合計 8 -rw-rw-r-- 1 phadmin phadmin 1069 12月 23 02:17 Dockerfile -rw-rw-r-- 1 phadmin phadmin 196 12月 23 02:17 README.md drwxrwxr-x 2 phadmin phadmin 112 12月 23 02:18 node-red-settings ./node-red-settings: 合計 44 -rw-rw-r-- 1 phadmin phadmin 228 12月 23 02:18 README.md -rw-rw-r-- 1 phadmin phadmin 29165 12月 23 02:18 flow.json -rw-rw-r-- 1 phadmin phadmin 1 12月 23 02:18 flow_cred.json -rw-rw-r-- 1 phadmin phadmin 1087 12月 23 02:18 package.jsonあとはDockerfile上でCOPYを使ってflow.jsonをコピーするようDockerfile内に記述をしておくことで、
docker buildするたびにコンテナはNode-REDレポジトリからFlowを持ってきてくれます。さいごに
Node-REDというよりもコンテナよりな記事でしたが、
Node-REDフローをプロジェクト化することで、バージョン管理がしやすくコンテナ化するときも便利に使えるということが、
まとまった記事って見かけないなと言うことで書いてみました。欲を言うと、コンテナで起動したNode-REDからもNode-REDプロジェクトとして参照している状態に出来たらなどとも考えていますが、
これらはおいおいチャレンジして見たいと想います。
- 投稿日:2020-12-23T01:38:15+09:00
Dockerの思い出しついでにlaravel動作環境を構築してみた
導入
ちょっと前に取り組んでいたdockerの内容を思い出しついでにlaravelの動作環境構築にチャレンジしてみました。
過去にdocker laravel 環境構築とかでググった参考サイトをもとに環境構築したことはありますが、
alpineオフィシャルイメージ(クリーンインストール状態?)を環境構築のスタートとし、PHP動作環境を構築 + nginxを導入 + laravelのインストールnginxとの連携までを行いました。
無事環境構築に成功したので、今回の総括の意味を込めて振返り記事を作成しました。完成した内容は以下のリポジトリで公開してます
※ ツッコミどころ満載だと思います
https://github.com/AiRKING-no9/nginxBuildEnvOnAlpine投稿者のすぺっく
- 仕事 or 趣味でwebブラウザのフロント関連とPHPのサーバーサイドアプリケーションをさわる
- 仕事で使う開発環境はvirtualbox or dockerで動作する環境を提供してもらってた。フロントソースを動作させる上で開発環境に調整必要そうならググって調整加えるくらいならやってた
- Linuxで動いているサーバーに入ってコマンド叩いてちょこちょこ触れる
- クリーンインストールされた状態のLinux系OSに対してPHP動作環境構築とかやったことない
自分のホストOS汚したくなくてalpineのオフィシャルイメージからコンテナ立ち上げ→node突っ込んでフロントソースコンパイルを過去やってみた程度- nginxを手順に沿って導入してwelcome画面をみて満足した
nginxの細かい設定とか設定ファイルの内容よく知らない- dockerfileとdocker-composeファイルをある程度調べながらdockerコンテナの構築はできる
つまづいた・悩んだポイント
その1: パッケージマネージャー(apk)経由でPHP7をインストールしようとしたら見つからない
今回 2020/11月頃時点で最新だったalpine3.12を利用しましたが、最新のalpineバージョンを利用するとPHP本体とPHP関連のパッケージが見つからなかった...
最終的にapkが参照するリポジトリを追加することで無事apk add パッケージ名でPHP本体とPHP関連のパッケージがインストールできました。
※ 今回はhttp://dl-cdn.alpinelinux.org/alpine/edge/communityのURLを/etc/apk/repositoriesファイルに追記することでリポジトリが追加されうまくインストールできました。その2 : php-fpmをインストール成功 →
php-fpm not foundになるapk add php-fpm ですんなりインストールできたと思ったら php-fpm -v を実行してもphp-fpm not foundが表示されてしまう...
結果としてphp-fpm7 という名前で /usr/sbin/ ディレクトリ配下にインストールされていた。
自分の場合1文字多くなるけどphp-fpm7でいいやと思ったのでそのままphp-fpm7でコマンド実行をつづけたが、
どうしてもphp-fpmで実行したいという人はphp-fpm7を対象にphp-fpm命名でシンボリックリンクを作成すればOKです。今回の件を通して、apkのインストール先ディレクトリどこ?とか色々調べましたが、
調べている中で、binディレクトリsbinディレクトリの役割があることを知りました。
Macで何気なくbinディレクトリとか触ってたけど役割あるなんて知らなかったんよ(´・ω:;.:...
以下、調査時に参考にした資料その3 : laravelとnginxの連携がうまく行かない
nginxの導入→ welcome画面の表示がうまくいき、composer経由でlaravelプロジェクトの作成までは順調に進めた。
しかし、nginxとlaravelを連携させようとするとnginxがlaravelのページ出力を返してくれなかった。
うまく行かなった原因として、nginxの設定ファイルとphp-fpmの設定ファイルを編集することでうまく応答してくれるようになった。
nginxとかphp-fpmの設定全く知らなかったのでここが一番解決まで時間かかった。
- nginxの設定ファイル(/etc/nginx/conf.d/default.conf)
server { listen 80; server_name localhost; access_log /var/log/nginx/laravelProject/access.log main; error_log /var/log/nginx/laravelProject/error.log warn; # 以下でlaravelプロジェクトのpublicを参照するように設定 root /www/laravelProject/public; location / { index index.php index.html; try_files $uri $uri/ /index.php$is_args$args; } # phpファイル実行時のfastCGIの接続設定 location ~ \.php$ { fastcgi_pass unix:/var/run/php-fpm/php-fpm.sock; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name; include fastcgi_params; } }
- php-fpmの設定ファイル(/etc/php7/php-fpm.d/www.conf)
[www] user = nginx group = nginx listen =/var/run/php-fpm/php-fpm.sock listen.owner = nginx listen.group = nginx pm = dynamic pm.max_children = 5 pm.start_servers = 2 pm.min_spare_servers = 1 pm.max_spare_servers = 3その4 : ホストとコンテナのディレクトリ同期を行うと空ディレクトリになる
dockerfile内ではじめは
composer create-project laravel/laravel=5.5 プロジェクト名でコンテナ内でlaravelプロジェクトが作成され動作することを確認していました。
ある程度のlaravel動作環境が構築できたので、ホストとコンテナを同期ディレクトリでマウント → 同期ディレクトリ先にlaravelプロジェクトインストールを実行したところ空ディレクトリになってしまいました。最終的にコンテナを一旦構築完了したあとに
docker exec経由でlaravelプロジェクトの作成コマンドを実行することで同期ディレクトリ内にうまく展開することができましたが、
正直今回のやり方が正解だとは思わないので現在も原因調査中です。
※ コンテナ構築中に同期ディレクトリ触るとダメなのかな?総括
いままでLinux系OSがクリーンインストールされた状態からここまで環境構築したことがなかったので、いろいろと苦戦しました。
が、当初の目標を達成できたことで学べた内容も新鮮でした。途中で折れない・諦めない心大事。
正直alpineベースのPHP動作環境コンテナが構築できるオフィシャル・アンオフィシャルイメージがあるのでそちらをベースイメージに利用すればここまでやる必要がないのですが、
必要最低限のミドルウェアを導入して環境構築できた謎の自己満足は満たされました。
なによりLinux系OSを学ぶ上でクリーンインストールされた状態から色々Tryして実験できたのでそちらのほうが得るものが大きかったです。
やっぱりDockerは触って失敗したら即破棄→再構築できるので便利だなと再確認できました。以上!!


























