20201223のdockerに関する記事は10件です。

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 BatchMWAAなど)を利用すべきところかもしれませんが、一旦はData Pipeline上でコンテナイメージを実行する方法を調べて実装しました。

また、「DataPipelineを使ってdockerコンテナを定期実行してみる」という記事を見つけたのですが、とりあえずの運用のためにECSクラスターを導入するのも大変なので、Data Pipelineで起動したEC2インスタンス自体にDockerを実行させる方法にしています。

実装方法

次のような方針で実装しました。

  • 前提としてGithub Actionsを利用して、Dockerイメージ自体はECRに既にpushされている状態
  • AMIは素のAmazon Linux 2 (idは ami-00f045aed21a55240 64 ビット x86) を利用する
  • 認証情報などは、パラメータストアにkmsで暗号化した形で保存し、そこから取得して環境変数やファイル形式でイメージ内に渡す
    • 詳しくは公式ドキュメントの「AWS Systems Manager Parameter Store で AWS KMS を使用する方法」を読んでください
    • 余談ですが「余力があれば、環境変数よりコマンドラインのオプションとして渡したほうがいい。一般的にコマンドラインパーサーのほうがバリデーションがしっかりしているため」というアドバイスも貰っています

ShellCommandActivityの実装

次のようなシェルスクリプトで実装しました。以下が注意点です。

  • aws ssm get-parameterで、テキストのみ取得するためのオプションはこちらの記事を参考にした
  • ほとんどのバッチでBigQueryを利用しているため、GCPのcredentialsをファイルに吐き出してイメージ内に渡している
  • Dockerイメージ内でawscliやAWS SDKを利用する場合、AWS_ACCESS_KEY_IDAWS_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でのバッチ管理を止めずに、コンテナ移行を進めることができます。道のりは長いですががんばります。

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

Docker + DynamoDB local + C++ の環境構築と実践まで

環境構築

今回使うものはこちら

  • Docker
  • Dynamodb local
  • AWS CLI バージョン 2
  • vcpkg
  • AWS SDK for C++

また、C++ のビルド環境には VC++ を使用します。

Docker の導入

Docker は、コンテナを用いてアプリケーションをすばやく構築、テスト、デプロイできるソフトウェアプラットフォームです。

https://www.docker.com/

ダウンロード

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 7287ab3

DynamoDB 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 607c8a202aac

AWS 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.bat

AWS SDK for C++ を取得する

vcpkg を使って、ライブラリを取得しましょう。

> ./vcpkg/vcpkg install aws-sdk-cpp:x64-windows

VC++ にインテグレートする

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 は 簡単にレコードを追加したり、削除したりできるので、プロトタイピングにもおススメですね。

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

kubernetes を本番運用していくために Sorry Page イメージを作っておくと幸せになれるかもしれない話

:christmas_tree: この記事は WESEEK Advent Calendar 2020 25日目の記事です。:christmas_tree:

はじめに

皆さん Kubernetes (以後 k8s) 使ってますよね!最近では本番環境上でも k8s を採用する事例を見かけるようになってきました。

今回は前向きな夢の話ではなく、ちょっと泥臭いお話です。

Sorry Page って何?

下記の画像のような「サービスがメンテナンス中だよ」ということを訪問者にお伝えするページです。

サンプルは弊社で提供しているサービスの GROWI.cloud の Sorry Page です。

244138daa7d01a7766e52de0a380e4c4.png

宣伝: 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.yaml
apiVersion: 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 という流れでリクエストが受け渡される、という前提とします。

Step1.png

deployment.yaml を k8s クラスタに apply して、Sorry Page イメージを deploy します

kubectl apply -f kubernetes/deployment.yaml

反映されると、下記の図のように GROWI.cloud Sorry Page Pod に向け変えができるような状態になります。

002.png

この状態で GROWI.cloud Service の内容を kubectl edit ... で書き換えることで、向け先を変えます。

003.png

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 イメージの紹介をしました。

  1. Sorry Page イメージの作り方
  2. 作った Sorry Page イメージを k8s クラスタにデプロイする方法
  3. Sorry Page Pod へのアクセス先の切り替え方法

いざというときには助かるかもしれませんが、なるべく Sorry Page イメージに頼らないで、サービス運用ができるように目指して行きたいですね :christmas_tree:

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

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:latest

Dockerfileの設定

我々のチームでは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"}

参考にした記事

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

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
  1. ユーザをroot に戻す
  2. OSに合わせた方法で証明書をインストール(この場合は debian)
    • ここでは ca-certificates パッケージはインストールしていません(jenkins イメージではインストール済)
    • 多くの場合 ca-certificates パッケージが既にインストールされているため、まずは存在すること前提で呼び出すのがおすすめです。稀にインストールされていない場合がありエラーとなりますので、その場合は、 ca-certificates パッケージ のインストールも手順に組み込むと良いです
  3. ユーザを元に戻す
    • 元のイメージと使用感を合わせるために、実行ユーザは元に戻しておいた方が良いです

これで、正しくオレオレ証明書を認識するイメージが出来上がります。

さいごに

今回は、SSL証明書の登録を行う際に、イメージを小さくする方法や、初見イメージに対して組み込む流れを書いてみました。
この辺りは手探りな部分もあるので、もっと良い方法があれば、改善していきたいです。

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

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でデバイスを操作しています。

作業の流れ

architecture.png

  1. Dockerアプリケーションの作成
    開発用PCでDockerアプリケーションを開発します。

  2. Dockerイメージのプッシュ
    Dockerイメージを作成してAmazon Elastic Container Registry (ECR)にプッシュします。

  3. docker-composeのアップロード
    docker-composeを作成してAmazon Simple Storage Service (S3)にアップロードします。

  4. 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.7
    
    username@ubuntu:~$ python3.7 --version
    Python 3.7.5
    
  • Raspberry 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.9
    

dockerのインストール

デバイスに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 pass
    

    GPGキーを追加します。

    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 docker
    
    username@ubuntu:~$ docker --version
    Docker version 20.10.1, build 831ebea
    
  • Raspberry Pi(Ubuntu Server 20.04 32bit)の場合

    Dockerの公式リポジトリにUbuntu20.04 armhf用のパッケージがないので、docker.ioでインストールします。

    sudo apt install docker.io
    #サービス起動
    sudo systemctl start docker
    #サービス自動起動
    sudo systemctl enable docker
    
    username@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/myCompose

ggc_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.py
from flask import Flask

app = Flask(__name__)

@app.route("/")
def hello():
    return "Hello World from Docker Container!"

if __name__ == "__main__":
    app.run()

必要なPythonのパッケージを記したファイルです。今回はFlaskのみ利用します。

requirements.txt
Flask

Dockerのイメージを作成する際のコマンドを記したファイルです。
32bit arm用のイメージに必要なファイルやパッケージをインストールしています。

Dockerfile
FROM 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にプッシュします。

リポジトリの作成

  1. 左上のサービスからElastic Container Registryに進み、「Repositories」→「リポジトリを作成」をクリックします。
    docker-24.png

  2. 任意のリポジトリ名を入力し、「リポジトリを作成」をクリックします。
    docker-25.png

イメージの作成とプッシュ

  1. PCのDocker Desktop on Windowsを起動しておきます。

  2. リポジトリのページに移動し、「プッシュコマンドの表示」をクリックします。
    docker-31.png

  3. Dockerアプリケーションの作成で作成したDockerfileのあるディレクトリでコマンドプロンプトを開き、表示されたコマンドを順番に実行します。
    docker-32.png

  4. ECRにdockerイメージがプッシュされたことを確認します。
    イメージのURIをこの後の手順で使用するので控えておきます。
    docker-37.png

3. docker-composeのアップロード

docker-composeの作成

PCでdocker-composeを作成します。
以下の内容をdocker-compose.ymlという名前で保存してください。
<image-uri>の部分はイメージの作成とプッシュで控えておいたURIで置き換えてください。

docker-compose.yml
version: '3'
services:
  web:
    image: "<image-uri>"
    ports:
      - "5000:5000"

S3バケットの作成

  1. 左上のメニューからS3へ進み、「バケット」→「バケットを作成」をクリックします。
    docker-26.png

  2. 任意のバケット名を入力し、「バケットを作成」をクリックします。
    docker-27.png

docker-composeのアップロード

  1. 作成したバケットをクリックし「アップロード」をクリックします。
    docker-28.png

  2. 「ファイルを追加」をクリックしdocker-composeの作成で作成したdocker-compose.ymlをアップロードします。
    docker-29.png

4. Dockerアプリケーションのデプロイ

ロールの作成とアタッチ

GreengrassがS3バケット、ECRにアクセスするために必要なロールを作成して、Greengrassグループにアタッチします。

ポリシーの作成

  1. 「IAM」→「ポリシー」と進み、「ポリシーの作成」をクリックします。
    docker-7.png

  2. 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>/*" 
            }
        ]
    }
    

    docker-17.png

  3. 任意の名前と説明を入力して「ポリシーの作成」をクリックします。
    docker-16.png

  4. 同様の手順で、以下の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": "*"
            }
        ]
    }
    

ロールの作成

  1. 「IAM」→「ロール」と進み、「ロールの作成」をクリックします。
  2. ユースケースの選択でGreengrassを選択し、次へ進みます。
    docker-19.png

  3. ポリシーの作成で作成した2つのポリシーを選択します。
    docker-20.png

  4. タグは設定せずに次へ進みます。

  5. 任意の名前と説明を入力してロールを作成します。
    docker-21.png

ロールのアタッチ

  1. Greengrassのグループへ進み、「設定」→「ロールの追加」をクリックします。
    docker-22.png

  2. ロールの作成で作成したロールを選択して「保存」をクリックします。
    docker-23.png

デプロイと動作確認

  1. グループのページに進み、「コネクタ」→「コネクタの追加」をクリックします。
    docker-33.png

  2. 「Docker Application Deployment」を選択し、「次へ」をクリックします。
    docker-34.png

  3. S3に保存したdocker-compose.ymlと、ユーザー設定で作成した、デバイス上でdocker-composeを保存するディレクトリを指定して、「追加」をクリックします。
    docker-35.png

  4. グループのページに進み、デプロイをクリックします。
    docker-30.png

  5. デプロイ完了後、しばらく待ってからPCでhttp://<デバイスのIPアドレス>:5000にアクセスします。
    ※デバイスとPCが同一のネットワークに属している必要があります。

  6. 以下の画面が表示されたら成功です。
    docker-38-2.png
    ※うまくいかない場合はデバイスの/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に従ってプロキシ設定を行います。

  1. まずディレクトリを作成し、そのディレクトリ内にファイルを作成します。

    sudo mkdir -p /etc/systemd/system/docker.service.d
    sudo vi /etc/systemd/system/docker.service.d/http-proxy.conf
    
  2. 下記のフォーマットで HTTP_PROXYHTTPS_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/"
    
  3. 設定を反映するためにデーモンをリロードし、再起動します。

    sudo systemctl daemon-reload
    sudo systemctl restart docker
    
  4. 設定内容が反映されているか確認します。

    # 一般ユーザ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/
    

参考

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

既存ネットワークに後から作成したコンテナを接続する方法

したいこと

  • 1つのdocker-compose.ymlに複数コンテナの記述をするのではなく、既存ネットワークに後から作成したコンテナを接続したい。

参考

作成

net1というネットワークを作成
docker create network net1

接続

docker-compose.yml
version: "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: net1
docker-compose.yml
version: "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
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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プロジェクトとして参照している状態に出来たらなどとも考えていますが、
これらはおいおいチャレンジして見たいと想います。

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

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して実験できたのでそちらのほうが得るものが大きかったです。:thumbsup:
やっぱりDockerは触って失敗したら即破棄→再構築できるので便利だなと再確認できました。

以上!!

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