20200713のAWSに関する記事は10件です。

Slack AppHomeでAWS環境構築モーダル作ってみた

Slack AppHomeは、アプリホーム画面を作れて便利ですね。
今回はAWS環境を構築する機能を作ってみました。
実装時のポイントなど書いていきます!これからAppHome使う方の参考になれば幸いです!

なぜ作ったのか

私の現場ではEC2を使って複数のプロジェクト環境を作ることがあります。ただ、今まではAWSコンソールからEC2/CloudWatchなどを設定する運用になってました。
環境構築手順は、CloudFormationでテンプレート化できますが、それでもAWSコンソールログインなど手間ですね。そこで、みんな慣れているSlackから環境作れるようにしました。

完成イメージ

aws-bot(CreateEC2).gif

AppHomeのボタンを押すと、モーダルが表示する仕組みにしました。
モーダルへの入力は、操作を簡略化したかったのでAMI選択程度にしました。
そして、処理開始と終了の連絡を特定のチャンネルに飛ばすようにしました。

(上のgifはデモ用なので、ami名やEIPなどは固定ですが、本物はami名/EIP取得して表示してます)

構成

slack_app_home.jpg

AWS Lambda(nodejs)で処理する形にしました。
今回のケースでは、4回イベントを処理するタイミングがあります。
それぞれの処理内容を説明します。

1.AppHome画面表示

AppHomeへの内容表示は、SlackAPI(views.publish)で行います。
一度送れば何度でも表示できます。ただ、表示内容を更新したい場合もあると思うので、AppHome表示時のapp_home_openedイベントをSubscribeして、毎回Publishして良いと思います。(subscribeには、自身のSlackアプリメニューのevent subscriptionsより購読設定ください。参考:Using the Slack Events API)

以下のtypeでイベント内容を判別できます。

{
  "body":{
    "event":{
      "type":"app_home_opened"
    }
  }
}

views.publishは以下のようなイメージです。

const home = {
    "type":"home",
    "blocks": [{
      "type": "section",
      "text": {
        "type": "mrkdwn",
        "text": "*Welcome!!* \nDo you want to create EC2?????"
      },
      "accessory": {
        "type": "button",
        "action_id": "固有のaction_ID",
        "text": {
          "type": "plain_text",
          "text": "createEC2"
        }
      }      
    }]
  }
const args = {
  'user_id': "SlackのユーザID",
  'view': JSON.stringify(home)
  'token': "slackアプリ登録時のbot token"
}
await viewsPublish(args)

SlackのユーザIDは、SlackAPIusers.infoを使えば、ユーザ名から取得できます。
参考:users.info
action_idについては後述します。

2.ボタン押下(AppHome)

ボタンはInteractiveComponentなので、まずはInteractiveComponentにRequestURLの登録が必要です。(https://api.slack.com/interactivity/handling)

Interactivityから送られるPayloadにはtrigger_idがあります。これを使うことで操作元に反応を返すことができます。なおtrigger_idの有効時間は3秒です。3秒以内に返さないと反応してくれません。
以下のようにモーダル内容とtrigger_idをviews.openに渡すとモーダル表示できます。参考:views.open

const modal = {
  "type": "modal",
  "title": {
    "type": "plain_text",
    "text": "CreateEC2"
  },
  "blocks": [
    {
      "type": "input",
      "block_id": "固有のBlockID",
      "element": {
        "type": "static_select",
        "action_id": "固有のActionID",
        "placeholder": {
          "type": "plain_text",
          "text": "Select a item",
          "emoji": true
        },
        "options":[
          ///
        ]
      },
    },
      ///
  ],
  "close": {
    "type": "plain_text",
    "text": "Cancel"
  },
  "submit": {
    "type": "plain_text",
    "text": "Save",
  },
}
const args = {
  'trigger_id': "受け取ったtrigger_id",
  'view': JSON.stringify(modal)
  'token': "slackアプリ登録時のbot token"
}
await viewsOpen(args)

ここでポイントは、block_idaction_idにそれぞれ一意な値を設定することです。
このあとモーダルから入力値を抽出するときにこの2つが必要となります。

3.モーダルOK押下(AppHome)

モーダルOKを押すと、typeがview_submissionのPayloadが送られてきます。
また、block_idaction_idの名称からモーダルの入力項目を取り出せます。
Payload中では以下のように入力値が格納されています。

{
  "view":{
    "state":{
      "values":{
        "設定したblock_id":{
          "設定したaction_id":{
            "value": "入力値"  // テキスト入力の場合
          }
        },
        "設定したblock_id":{
          "設定したaction_id":{
            "selected_option":{
              "value": "選択値"  // リスト選択の場合
            }
          }
        },
      }
    }
  }
}

3-1.スタック作成

AWS-SDKからCloudFormation(createStack)をコールします。
このとき、createStackNotificationARNsパラメータにSNSトピックARNを設定すると、スタック作成のイベント受け取れます。
スタック作成開始時と終了時に時間差でSlackチャンネル通知してあげると、裏でBotが頑張ってた感があって好きです^^
snsから来るイベントは以下のような感じです。

{
    "Records": [
        {
            "EventSource": "aws:sns",
            "EventVersion": "1.0",
            "EventSubscriptionArn": "arn:XXXXX",
            "Sns": {
                "Type": "Notification",
                "MessageId": "XXXXX",
                "TopicArn": "arn:XXXXX",
                "Subject": "AWS CloudFormation Notification",
                "Message": "StackId=XXXXXX",
                "Timestamp": "2020-XX-XXT02:50:11.963Z",
                "SignatureVersion": "1",
                "Signature": "XXXXXX",
                "SigningCertUrl": "XXXX",
                "UnsubscribeUrl": "XXXX",
                "MessageAttributes": {}
            }
        }
    ]
}

その通知が処理中なのか、完了なのかはMessage内容から判別しました。
上記は割愛してますが、メッセージ内には様々な情報が記載されています。このメッセージをパースすることで情報を取り出せます。(他にいい方法あれば教えて欲しいです・・私はsns初めてだったのでこれしか思いつきませんでした・・)

3-2.モーダルクローズ

モーダルを使う際は画面クローズも考慮必要です。
OKボタンを押しても画面クローズを明示的に指定しないとモーダルは閉じません。
ステータス200でresponse{"response_action": "clear"}を返せば、モーダルが閉じてくれます。
参考:Closing views
Chancelボタンと×ボタンはResponseなしでも閉じてくれます。
これらの操作イベントを受けたい場合は、モーダル作成時にnotify_on_close:trueを指定するとview_closedイベントを受け取れます。

4.スタック作成完了

前述したスタック作成完了の通知ですね。
SNS通知内にはStackNameがあるので、AWS-SDKのdescribeStacksdescribeStackResourcesでリソース詳細などを取得できます。
Slackへ通知メッセージは、BlockKitBuilderでいい感じのメッセージを作ってあげるといいですね。テンプレートも用意されているので、参考になります。

おわりに

SlackAppHomeを使ってAWS環境構築を試してみました。
AppHomeなどを使ってみて、ChatOpsを実現すれば運用負荷が減り、チームが本業に集中できますね。

あとAppHomeは、Slackのスマホアプリからも使えるのがいいですね!
SlackAPIがボタンやモーダルなど使いやすいコンポーネントを用意してくれている+スマホ版表示はやってくれるので、スマホ対応方法を考えなくてよいです^^
これで、電車で移動中にポチポチで環境構築できます^^

実装方法がわかれば簡単なので、ぜひいろいろ作ってみてください^^

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

AWS SDK のSSL証明書エラーが出た場合【Windows10】

  • よくある、実行環境にSSL証明書がない場合に発生するエラー
cURL error 60: SSL certificate problem: unable to get local issuer certificate (see https://curl.haxx.se/libcurl/c/libcurl-errors.html) in xxx
cURL error 77: error setting certificate verify locations:
  CAfile: xxx
  CApath: none (see https://curl.haxx.se/libcurl/c/libcurl-errors.html) in 
xxx on line 195
  • 解決策
    cURLサイトから証明書をダウンロード出来るらしい
    https://curl.haxx.se/docs/caextract.html
    サイト内の cacert.pem というリンクからダウンロード出来る
    ダウンロードしたら適当な場所に配置して、php.iniの設定
openssl.cafile='C:\tools\php\extras\ssl\cacert.pem'

apacheとかnginxならサービスを再起動
これで完了

参考: https://docs.aws.amazon.com/ja_jp/sdk-for-php/v3/developer-guide/faq.html

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

AWS初心者向け用語集①

業務でAWSを使うことになったので、お勉強中です。

AWS初心者が戸惑いがちな用語を、備忘録も兼ねて、なるべくかみ砕いた言葉で
まとめておきたいと思います。

EC2

・Amazon Elastic Compute Cloud (Amazon EC2) の略。
・Elasticとは、弾力のある、伸縮自在の、という意味。
・Amazonが提供している仮想サーバー構築サービス。
・簡単に言うと、Amazonが提供する、伸縮自在のクラウドサーバ。
・LinuxなどのOSを載せた仮想サーバーが作れる。
・EC2は稼働中(Running)のみ課金され、止まっているときは課金されない。
・使うときは必要なCPU/メモリなどのスペックを選んでインスタンスを作成する。

※ちなみに「EC」と付いているが、「ECサイト」とは関係ない。

リージョン、アベイラビリティーゾーン(AZ)

リージョン > アベイラビリティゾーン 

・日本には東京リージョンと大阪リージョンがある。
・サービス提供はリージョン単位。
・アベイラビリティゾーンは物理的に近距離に設置されたデータセンターの集まり。
・東京リージョンには4つのアベイラビリティゾーンがあり、データセンターは8箇所あるらしい。
・物理的に近い場所を選ぶことで、レスポンスタイムを短くすることができる。
・基本的に日本の会社は東京リージョンを選択する。災害対策として大阪を選ぶことも多い(マルチAZ)。

AMI

・Amazon Machine Imageの略。
・ソフトウェア構成 (オペレーティングシステム、アプリケーションサーバー、アプリケーションなど) を記録したテンプレート。
・要は、OSのテンプレート。
・アカウント間でAMIを共有したり、同じAMIを使って複数のインスタンスを作成したりできる。
・AWSマーケットプレイスで第三者が作ったAMIを購入することもできる(販売もできる!)。

IAM

・Amazon Identify and Access Managementの略。
・AWSの認証・認可サービス。
・IAMユーザー = IAM上で作成したユーザー。IAM管理者によってユーザーごとに権限設定が可能。

※AMIとIAMは名前がややこしいけど全然意味が違うので注意!

VPC(Virtual Private Cloud)

(参考にした本)
 ・この1冊で合格! AWS認定ソリューションアーキテクト - アソシエイト テキスト&問題集

  ↑Amazon unlimitedで無料で読めました!

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

Kubernetes入門 〜ローカル環境で触ってからEKSへのデプロイまで〜

この記事について

「Kubernetesを実際に触って勉強してみたい!」となって参考記事を探してみると、多くの記事がいきなりEKSやGKEといったクラウド上の本番環境を触るところからのスタートになっています。
「いきなり本番環境はちょっと……」「まずはローカル環境で軽く触って慣れてからクラウド上の環境を構築したい!」という方向けに、

  1. Kubernetesのアーキテクチャの勉強
  2. ローカルで実際に触ってみる
  3. EKSにデプロイ

という流れで、Kubernetesってこんな感じなんだ!という雰囲気を紹介したいと思います。

使用する環境・バージョン

  • OS : macOS Mojave 10.14.5
  • Docker.app : 2.2.0.5
  • Kubernetes : v1.15.5
  • eksctl : 0.23.0
  • aws-cli : 2.0.10

前提条件

Docker App, AWS CLIはインストール済みの状態からスタートします。

読者に求める前提知識

  • コンテナをある程度扱えること
  • コンテナオーケストレーションという概念について知っていること
  • VPC, セキュリティグループといったAWSの用語がある程度わかること
  • (ECSの知識があると所々の例え話がわかりやすくなりますが、必須ではありません)

Kubernetesのアーキテクチャ

まず、Kubernetesの構成要素について詳しく説明します。
k8s.png
画像出典:Step by Step Introduction to Basic Concept of Kubernetes
参考:順を追って学ぶKubernetesのキホン〜ローカル環境でKubernetesクラスターを作成してKubernetesの概念を理解する〜

Master

コンテナの起動・削除・カナリアデプロイといった、Kubernetesができるコンテナ管理・制御機能を担う頭脳部分です。
Masterを構成する主な要素は4つです。

  • API Server
  • etcd
  • Scheduler
  • Controller Manager

API Server

Kubernetesが扱うリソース(コンテナやボリュームなど)を操作するためのAPIがここで公開されています。
開発者(Devops)は、ターミナルでkubectlコマンドを使うことでこのAPIを叩き、リソースの作成・削除・制御を行います。

etcd

分散KVS(キーバリューストア)です。作成されたクラスター(コンテナを動かすサーバー群)の設定情報がここに保存されます。

Scheduler

コンテナの適切な配置場所を決定する機能を持ちます。
この機能があるがゆえに、「開発者は円滑な運用のために、どこのサーバーにコンテナを作成すべきか?」という設定を手動で行う必要がなくなり、アプリ側の開発に集中することができます。

Controller Manager

Kubernetesが扱うリソースの制御を行います。
例えば、「起動コンテナの数を保つ」「サーバーダウン時の通知・対応を行う」「PodとServiceの紐付け」などを行います。

参考:Kubernetes公式ドキュメント Kubernetesのコンポーネント

Cluster(クラスター)

コンテナを動かすサーバー群のことをKubernetesではクラスターと呼びます。
AWSのECSにおけるクラスターとほぼ同義です。

Node(ノード)

コンテナを動かすサーバー1つ1つのことをKubernetesではノードと呼びます。
クラスターとは「1つのクラスターの中に、任意の数のノードが含まれる」という関係になっています。
上記のMasterに管理される対象であることから、「Slave Node」と呼ばれることもあります。

ECSにおける、クラスター内のEC2インスタンスとほぼ同義です。
ローカルでKubernetesを動かす場合では、そのPCそのものが1つのノードという扱いになります。

ノードの中に作成されるリソースは主に4つです。

  • Pod
  • Kubelet
  • Container Engine
  • Kube Proxy

Pod

ノード上で動かすコンテナ(組)のことです。
Podを構成するコンテナは複数個でもOKです。例えば、「nginxのコンテナとアプリコンテナの2つをセットにして1つのPodにする」ということができます(ECSでのタスクと同様です)。

Kubelet

Masterからのリソース・スケジュール管理を受け付けるためのエージェントです。
ECSにおけるECS Container Agentとほぼ同義です。

Container Engine

Dockerといった、コンテナの作成・削除等の処理を実際に実行するエンジンです。

Kube Proxy

ノードが受け付けたユーザーからのリクエストを、適切なPodに割り当て転送するネットワークプロキシです。

参考:Kubernetes公式ドキュメント Kubernetesのコンポーネント

始める前の初期状態

Kubernetesを使っていない状態でも、MasterにあるAPIを叩くためのkubectlコマンドは存在します。Docker Appインストール時にkubectlコマンドも同時にインストールされているからです。

# このように、kubectlコマンドの存在がwhichコマンドで確認可能
$ which kubectl
/usr/local/bin/kubectl

しかし、コマンドのバージョン確認を行うと、以下のような表示になります。

$ kubectl version
Client Version: version.Info{Major:"1", Minor:"15", GitVersion:"v1.15.5", GitCommit:"20c265fef0741dd71a66480e35bd69f18351daea", GitTreeState:"clean", BuildDate:"2019-10-15T19:16:51Z", GoVersion:"go1.12.10", Compiler:"gc", Platform:"darwin/amd64"}
Unable to connect to the server: EOF

これは、「開発者クライアントの準備(=kubectlコマンド)は存在するが、それを受け付けるMasterのAPI Serverに接続できない(まだ存在しない)」という状態だということです。

このままではKubernetesを利用できません。そのため、次にKuberbetesを有効化して、ローカルにKubernetes Serverを立ち上げる操作を行います。

ローカルでのKubernetes有効化

Kubernetes有効化設定の手順

MacのメニューバーにあるDockerアイコンをクリックすると、以下のようなメニューが表示されます。
スクリーンショット 2020-07-04 18.33.37.png
ここから"Preferences"を選択すると、以下のような画面になります。
スクリーンショット 2020-07-04 18.35.11.png
左のバーから"Kubernetes"を選択します。すると、Docker AppにおけるKubernetesの設定画面が以下のように表示されます。
スクリーンショット 2020-07-04 18.35.22.png
この中から、"Enable Kubernetes"のチェックボックスに印を入れ、"Apply & Restart"で設定を保存します。

スクリーンショット 2020-07-04 18.38.26.png
このような画面になるのでしばらく待機します。
スクリーンショット 2020-07-04 18.41.40.png
ウィンドウ下部に「Kubernetes running」が表示されれば準備はOKです。

起動確認

本当にKubernetesのサーバーが立ち上がったかどうかをコマンドで確認します。

$ kubectl version
Client Version: version.Info{Major:"1", Minor:"15", GitVersion:"v1.15.5", GitCommit:"20c265fef0741dd71a66480e35bd69f18351daea", GitTreeState:"clean", BuildDate:"2019-10-15T19:16:51Z", GoVersion:"go1.12.10", Compiler:"gc", Platform:"darwin/amd64"}
Server Version: version.Info{Major:"1", Minor:"15", GitVersion:"v1.15.5", GitCommit:"20c265fef0741dd71a66480e35bd69f18351daea", GitTreeState:"clean", BuildDate:"2019-10-15T19:07:57Z", GoVersion:"go1.12.10", Compiler:"gc", Platform:"linux/amd64"}

先ほどはUnable to connect to the server: EOFと表示されていた部分にサーバーのバージョンが表示されています。無事に起動できたようです。

また、Kubernetesを有効化したことで、Masterを構成するAPI ServerやSchedulerといった機能を担うコンテナイメージがpullされていることも確認できます。

# 以下のイメージが新規にpullされた
$ docker images
docker/desktop-storage-provisioner                                           v1.0                605a0f683b7b        4 months ago        33.1MB
k8s.gcr.io/kube-apiserver                                                    v1.15.5             e534b1952a0d        8 months ago        207MB
k8s.gcr.io/kube-controller-manager                                           v1.15.5             1399a72fa1a9        8 months ago        159MB
k8s.gcr.io/kube-proxy                                                        v1.15.5             cbd7f21fec99        8 months ago        82.4MB
k8s.gcr.io/kube-scheduler                                                    v1.15.5             fab2dded59dd        8 months ago        81.1MB
docker/kube-compose-controller                                               v0.4.23             a8c3d87a58e7        13 months ago       35.3MB
docker/kube-compose-api-server                                               v0.4.23             f3591b2cb223        13 months ago       49.9MB
k8s.gcr.io/coredns                                                           1.3.1               eb516548c180        17 months ago       40.3MB
k8s.gcr.io/etcd                                                              3.3.10              2c4adeb21b4f        19 months ago       258MB
k8s.gcr.io/pause                                                             3.1                 da86e6ba6ca1        2 years ago         742kB

それなら、Masterを構成するコンテナがMac上で起動しているのか?と思ったのですが、docker psでは起動中のコマンドが確認できませんでした。

$ docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES

(おまけ)Kubernetes Dashboardの用意

「今のクラスターでは何が動いているのか?」「起動したPodはノードのリソース(CPUなど)をどれくらい食っているのか?」といった情報を、コマンドラインではなくGUIで確認できるようにするツールが存在します。それがKubernetes Dashboardです。
これはオプションツールなので、Kubernetesをインストールしただけの状態ではこれを持っていません。そのため、これを使いたい場合は個別にインストール・起動する必要があります。

Dashboardのリソース作成(≒インストール)

Kubernetes Dashboardは、Kubernetesで作られたサービス(アプリ)という形で配布されています。
そのため、Dashboardを立ち上げるための設定ファイル(Kubernetesではマニュフェストファイルという)を参照して、その内容をローカルに展開・起動するという形をとります。

kubetcl applyというコマンドで、マニュフェストファイルからDashboardのリソース作成を行います。-fオプションで、インターネット上に公開されているDashboardのマニュフェストファイルを指定しています。

$ kubectl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/v2.0.0-beta8/aio/deploy/recommended.yaml

namespace/kubernetes-dashboard created
serviceaccount/kubernetes-dashboard created
service/kubernetes-dashboard created
secret/kubernetes-dashboard-certs created
secret/kubernetes-dashboard-csrf created
secret/kubernetes-dashboard-key-holder created
configmap/kubernetes-dashboard-settings created
role.rbac.authorization.k8s.io/kubernetes-dashboard created
clusterrole.rbac.authorization.k8s.io/kubernetes-dashboard created
rolebinding.rbac.authorization.k8s.io/kubernetes-dashboard created
clusterrolebinding.rbac.authorization.k8s.io/kubernetes-dashboard created
deployment.apps/kubernetes-dashboard created
service/dashboard-metrics-scraper created
deployment.apps/dashboard-metrics-scraper created

参考:Kubernetes公式ドキュメント Web UI (Dashboard)

Dashboardログインのためのトークン取得

Dashboardにアクセスするためにはトークンによる認証が必要です。なので、そのトークンを確認します。
以下のコマンドを打って、default-token-*****という名前のものを探します。

$ kubectl -n kube-system get secret
NAME                                             TYPE                                  DATA   AGE
()
default-token-ss24c                              kubernetes.io/service-account-token   3      168m
()

ここではdefault-token-ss24cというものが見つかりました。これを元に今度は以下のコマンドを打ちます。

$ kubectl -n kube-system describe secret default-token-ss24c
Name:         default-token-ss24c
Namespace:    kube-system
Labels:       <none>
Annotations:  kubernetes.io/service-account.name: default
              kubernetes.io/service-account.uid: 92b6b004-dbf3-4593-adf0-fe2a2eb253f4

Type:  kubernetes.io/service-account-token

Data
====
ca.crt:     1025 bytes
namespace:  11 bytes
token:      *********************

ここに表示されているtokenをこの後利用するので、メモしておきましょう。

参考:Docker for Mac で Kubernetes をちょっと試す

プロキシサーバの起動

Dashboardサービスにアクセスするために、プロキシサーバを起動する。
以下のコマンドを打つことで、プロキシがフォアグラウンド稼働をします。(そのため、止めたくなった場合はCtrl+Cで停止できます。)

$ kubectl proxy
Starting to serve on 127.0.0.1:8001

ログイン

プロキシサーバを起動させた状態で、ブラウザで以下のアドレスにアクセスします。

http://localhost:8001/api/v1/namespaces/kubernetes-dashboard/services/https:kubernetes-dashboard:/proxy/ 

スクリーンショット 2020-07-04 21.24.52.png
Tokenを選択して、フォームに先ほど確認したトークンを入力します。

スクリーンショット 2020-07-04 21.33.41.png
このようなダッシュボード画面が表示されれば成功です。

後片付け

Dashboardのリソースを削除したい場合は、以下のコマンドで行えます。

$ kubectl delete -f https://raw.githubusercontent.com/kubernetes/dashboard/v2.0.0-beta8/aio/deploy/recommended.yaml

namespace "kubernetes-dashboard" deleted
serviceaccount "kubernetes-dashboard" deleted
service "kubernetes-dashboard" deleted
secret "kubernetes-dashboard-certs" deleted
secret "kubernetes-dashboard-csrf" deleted
secret "kubernetes-dashboard-key-holder" deleted
configmap "kubernetes-dashboard-settings" deleted
role.rbac.authorization.k8s.io "kubernetes-dashboard" deleted
clusterrole.rbac.authorization.k8s.io "kubernetes-dashboard" deleted
rolebinding.rbac.authorization.k8s.io "kubernetes-dashboard" deleted
clusterrolebinding.rbac.authorization.k8s.io "kubernetes-dashboard" deleted
deployment.apps "kubernetes-dashboard" deleted
service "dashboard-metrics-scraper" deleted
deployment.apps "dashboard-metrics-scraper" deleted

contextの選択

Kubernetesを使う準備が整いました。これでクラスター上にPod等のリソースを作ってアプリを構築していくことができます。

しかし、ここで問題になるのが構築していく対象の設定です。
例えば、「クラスターAとクラスターBが現在存在していて、今はクラスターAを使いたい」という状況では、開発者が使うkubectlコマンドでクラスターAのみがいじれないといけないわけです。
また、「クラスターA上でアプリチームがいじれるリソースと、同じクラスターAでインフラチームがいじれるリソースという風に触れる範囲を制御したい」という場合では、同じkubectlコマンドでも同じ権限(機能)を持たせてはいけないことになります。

この、「どのクラスターを、どの権限でkubectlコマンドで動かすのか」という設定集がcontextです。開発者は、手を入れたいリソースに合わせて、適切なcontextを選択・使用する必要があるわけです。

contextの確認・変更

以下のコマンドで、現在存在するcontext一覧を確認することができます。

$ kubectl config get-contexts
CURRENT   NAME                 CLUSTER          AUTHINFO         NAMESPACE
*         docker-desktop       docker-desktop   docker-desktop   
          docker-for-desktop   docker-desktop   docker-desktop

現在選択されているcontextは"docker-desktop"です。
"docker-for-desktop"に切り替えたい場合は、以下のようなコマンドを打ちます。

$ kubectl config use-context docker-for-desktop
Switched to context "docker-for-desktop".

$ kubectl config get-contexts
CURRENT   NAME                 CLUSTER          AUTHINFO         NAMESPACE
          docker-desktop       docker-desktop   docker-desktop   
*         docker-for-desktop   docker-desktop   docker-desktop

以下、ローカル上でのKubernetes操作は、この"docker-for-desktop"で行うこととします。

参考:[Kubernetes入門] kubectlのアクセス先(コンテキスト)を切り替える方法

ローカルで動いているクラスターの状態確認

Docker Appによって作られたクラスター(実態は、ローカルマシンというノード1台のみで構成)上には、デフォルトでどんな状態で作られているのかを確認してみましょう。

クラスター情報の確認

クラスターのMaster,DNSサーバーがどのアドレスで動いているのかが確認できます。

$ kubectl cluster-info
Kubernetes master is running at https://kubernetes.docker.internal:6443
KubeDNS is running at https://kubernetes.docker.internal:6443/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy

クラスター内部からこれらのアドレスにアクセスすることによって、サービスを利用することができます。

ノードの確認

一覧取得

クラスター上で動いているノード一覧は、kubectl get nodeコマンドで取得できます。

$ kubectl get node
NAME             STATUS   ROLES    AGE   VERSION
docker-desktop   Ready    master   24h   v1.15.5

詳細情報取得

特定ノードの詳細な状態が知りたい場合は、kubectl describe nodeで確認できます。

$ kubectl describe node <node-name>

Podの確認

一覧取得

クラスター上で動いているPod一覧も、kubectl get podコマンドで取得できます。

$ kubectl get pod -n kube-system
NAME                                     READY   STATUS    RESTARTS   AGE
coredns-5c98db65d4-qshvb                 1/1     Running   1          44h
coredns-5c98db65d4-zpbfq                 1/1     Running   1          44h
etcd-docker-desktop                      1/1     Running   0          44h
kube-apiserver-docker-desktop            1/1     Running   0          44h
kube-controller-manager-docker-desktop   1/1     Running   0          44h
kube-proxy-kd5kf                         1/1     Running   0          44h
kube-scheduler-docker-desktop            1/1     Running   0          44h
storage-provisioner                      1/1     Running   1          44h

注: オプション-nは、後述する「名前空間」を指定するコマンドです。上の例の場合、クラスター上のkube-systemという名前空間で動いているPod一覧が取得されています。

詳細情報取得

特定Podの詳細な状態が知りたい場合は、kubectl describe podで確認できます。

$ kubectl describe pods <pod-name>

ログ取得

Podででたログを確認したい場合は、以下のコマンドでログを標準出力に表示させることができます。

$ kubectl logs <pod-name>

Serviceの確認

Serviceとは

PodのIPアドレスは不定です。つまり、Pod起動のたびにIPアドレスが変化します。
そのため、そのPodを利用する側がIPアドレスの変化を気にすることなく、それらにアクセスするための単一エンドポイントが必要です。Kubernetesでその単一エンドポイントを作るための仕組みをサービスと呼びます。
(docker-composeにおいてコンテナのIPアドレスでアクセスするのではなく、サービスを作成してその名前でアクセスするのと同じです。)

一覧取得

kubectl get serviceコマンドで可能です。

$ kubectl get service -n default
NAME         TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
kubernetes   ClusterIP   10.96.0.1    <none>        443/TCP   24h

注: -nオプションの効果はPodのときと同じで、名前空間(後述)を指定するものです。

詳細情報取得

kubectl describe serviceで可能です。

$ kubectl describe service <service-name>

名前空間の確認

名前空間とは

名前空間とは、1つの物理クラスターを論理的に分割して仮想のクラスターを作成する機能のことです。

例えば、「クラスターA上でアプリBとアプリCを両方同時に、干渉しないように動かしたい」という場合は、クラスターA上にアプリB用、アプリC用の名前空間を作成し、その名前空間上にそれぞれのアプリのPodやサービスをデプロイすることで、クラスターAを仮想的に二つの別クラスター空間と扱うことができます。

一覧取得

kubectl get namespaceコマンドで、現在のクラスター上に存在する名前空間を確認することができます。

$ kubectl get namespace
NAME                   STATUS   AGE
default                Active   24h
docker                 Active   24h
kube-node-lease        Active   24h
kube-public            Active   24h
kube-system            Active   24h
kubernetes-dashboard   Active   21h

デフォルトで作られる名前空間は以下の4つです。

  • default
  • kube-public
  • kube-system
  • kube-node-lease

また、Docker Appがある場合は"docker"名前空間が、Kubernetes Dashboardを入れている場合は"kubernetes-dashboard"名前空間があります。

default名前空間

デフォルトの名前空間です。
名前空間の指定が必要なリソース作成時にそれを指定しないと、自動的にdefault名前空間が使われます。kubectl get等のコマンド実行時に-nオプションをつけないときも、default名前空間上の情報が取得されます。
デフォルトでは1つのサービスが存在します。

$ kubectl get all
NAME                 TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
service/kubernetes   ClusterIP   10.96.0.1    <none>        443/TCP   45h

このサービスは、Master(≒API Server)にリクエストを送るためのものです。
参考:What's the purpose of the default kubernetes service?

kube-public名前空間

全contextからアクセス可能なリソースを配置するための空間です。
作成初期にリソースは存在しません。

kube-system名前空間

Kubernetesシステムによって作成されたオブジェクト(API Serverなど)のための名前空間です。
ここに存在するリソースは以下の通りです。

$ kubectl -n kube-system get all
NAME                                         READY   STATUS    RESTARTS   AGE
pod/coredns-5c98db65d4-qshvb                 1/1     Running   1          44h
pod/coredns-5c98db65d4-zpbfq                 1/1     Running   1          44h
pod/etcd-docker-desktop                      1/1     Running   0          44h
pod/kube-apiserver-docker-desktop            1/1     Running   0          44h
pod/kube-controller-manager-docker-desktop   1/1     Running   0          44h
pod/kube-proxy-kd5kf                         1/1     Running   0          44h
pod/kube-scheduler-docker-desktop            1/1     Running   0          44h
pod/storage-provisioner                      1/1     Running   1          44h


NAME               TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)                  AGE
service/kube-dns   ClusterIP   10.96.0.10   <none>        53/UDP,53/TCP,9153/TCP   44h

NAME                        DESIRED   CURRENT   READY   UP-TO-DATE   AVAILABLE   NODE SELECTOR                 AGE
daemonset.apps/kube-proxy   1         1         1       1            1           beta.kubernetes.io/os=linux   44h

NAME                      READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/coredns   2/2     2            2           44h

NAME                                 DESIRED   CURRENT   READY   AGE
replicaset.apps/coredns-5c98db65d4   2         2         2       44h
  • pod/coredns: kube-dnsのサービスにアクセスがあったら、問い合わせが来るDNSサーバー
  • pod/etcd-docker-desktop: etcd本体
  • pod/kube-apiserver-docker-desktop: API Serverの本体
  • pod/kube-controller-manager-docker-desktop: Controller Managerの本体
  • pod/kube-proxy: サービスに接続するプロキシ
  • pod/kube-scheduler-docker-desktop: Schedulerの本体
  • pod/storage-provisioner: ストレージの割り当てを行う
  • service/kube-dns: クラスター内に内蔵されているDNSサービス

時折「-docker-desktop」と付いているのは、「クラスターdoccker-desktop用」という意味だと思われます。

参考:Kubernetes公式ドキュメント Kubernetesのコンポーネント

kube-node-lease名前空間

Kubernetes 1.13から導入。NodeLeaseが有効になっている場合、各ノードはこの名前空間にオブジェクトを一つ作って、それを更新し続けることで死活監視を行います。
参考:Kubernetes公式ドキュメント ノード

docker名前空間

Kubernetesには、専用のマニュフェストファイルからだけではなく、docker-compose.ymlからデプロイする機能が存在します。
この機能を提供するサービスはここに展開されているようです。
参考:Docker for MacでKubernetes構築 #composeとの統合

kubernetes-dashboard名前空間

Kubernetes Dashboardをデプロイするための名前空間です。

詳細情報取得

特定の名前空間の詳しい状態が知りたい場合は、kubectl describeを使用します。

$ kubectl describe namespace <name>
Name:         <name>
Labels:       <none>
Annotations:  <none>
Status:       Active

No resource quota.

No resource limits.

マニュフェストファイルを用いてアプリをデプロイ

ここからは、マニュフェストファイルでアプリ・コンテナの設定を記述して、それをローカルのKubernetes上にデプロイしていきたいと思います。
デプロイするアプリが1つだけの場合、名前空間はdefaultを使用するのが楽でしょう。
以下、特筆しない限り名前空間はdefaultです。

Podを作ってみる

まず手始めに、Podを単独で作ってみましょう。

マニュフェストファイル作成

pod.ymlという名前でマニュフェストファイルを作成し、以下のように記述します。
今回は、MySQLのコンテナPodを作ってみます。

pod.yml
apiVersion: v1
kind: Pod
metadata:
    name: k8s-mysql
    labels:
      app: myapp
      tier: db
spec:
    containers:
    - name: k8s-mysql
      image: mysql:5.7
      volumeMounts:
      - name: k8s-mysql-storage
        mountPath: /var/lib/mysql
      ports:
      - containerPort: 3306
      env:
      - name: MYSQL_ROOT_USER
        value: root
      - name: MYSQL_ROOT_PASSWORD
        value: pass
      - name: MYSQL_DATABASE
        value: sampledb
    volumes:
    - name: k8s-mysql-storage
      hostPath:
        path: /Users/myname/Desktop/k8s

記述の意味は以下の通りです。

pod.yml
# KubernetesのAPI Serverのバージョン
apiVersion: v1
# マニュフェストファイルで何を作ろうとしているか。
kind: Pod
# Podにつける識別子
metadata:
    # Pod名
    name: k8s-mysql
    # この場合「key:app, value:myapp」「key:tier, value:db」という2つのラベルが付く
    labels:
      app: myapp
      tier: db
# 作成するPodの定義
spec:
    # 所属するコンテナ
    containers:
      # コンテナの名前
    - name: k8s-mysql
      # 使用するイメージ
      image: mysql:5.7
      # マウントするボリュームの名前と、マウントさせるディレクトリパス(コンテナ内)
      volumeMounts:
      - name: k8s-mysql-storage
        mountPath: /var/lib/mysql
      # containerPortで、Podコンテナの何番ポートを開けるかを指定
      ports:
      - containerPort: 3306
      # コンテナに渡す環境変数
      env:
      - name: MYSQL_ROOT_USER
        value: root
      - name: MYSQL_ROOT_PASSWORD
        value: pass
      - name: MYSQL_DATABASE
        value: sampledb
    # 所属するボリューム
    volumes:
      # ボリューム名
    - name: k8s-mysql-storage
      # hostPathの場合、Podをホストするノード(ローカルの場合Kubernetesを動かしているPC)のどこのディレクトリにデータを保存するかの指定
      hostPath:
        path: /Users/myname/Desktop/k8s

参考:kubernetesによるDockerコンテナ管理入門
volumeについて参考:Kubernetesで使えるボリューム・タイプのチートシート

ローカルに存在するコンテナイメージを使いたい場合のマニュフェストファイルの記述

Docker Hubといったリポジトリに公開していない、自分のPCの中にあるイメージを使ってPodを作りたいという場合もあるでしょう。
その場合は、マニュフェストファイルを以下のように書きます。

pod.yml
(略)
      image: my-local-image-name
      # イメージを公開リポジトリから取らないという設定記述
      imagePullPolicy: Never
(略)

参考:kubenetesでローカルコンテナイメージからコンテナを作成する方法

デプロイ

作成したマニュフェストファイルを元にリソースを作る場合は、kubectl createコマンドを使用します。

$ kubectl create -f pod.yml

後片付け

削除するときはkubectl deleteを使います。

$ kubectl delete -f pod.yml

なお、Podが削除されても、マウントされたボリューム(今回の場合はローカルPCの/Users/myname/Desktop/k8s)の中身はそのまま残ります。

serviceを作ってみる

先ほど作成したMySQLのPodにエンドポイントを作るために、サービスを作成します。

マニュフェストファイルの作成

Pod作成のときと同様、まずはマニュフェストファイルを作成します。

service.yml
apiVersion: v1
kind: Service
metadata:
  name: k8s-mysql-service
  labels:
    app: myapp
spec:
  type: NodePort
  ports:
    - port: 3306
      targetPort: 3306
      protocol: TCP
  selector:
    app: myapp
    tier: db

記述の意味は以下の通りです。

service.yml
apiVersion: v1
kind: Service
metadata:
  # サービス名
  name: k8s-mysql-service
  labels:
    app: myapp
# 作成するサービスの定義
spec:
  # 全てのノードの3306番ポートにアクセスすることでこのサービスにアクセスされるようになる
  type: NodePort
  ports:
      # サービスがlistenするポート
    - port: 3306
      # サービスが転送するpodのlistenポート
      targetPort: 3306
      # 使用プロトコル
      protocol: TCP
  # 以下のlabelがついているpodをサービスの対象に含める(今回の場合、上記で作成したpodにつけた2つのラベルを指定)
  selector:
    app: myapp
    tier: db

参考:Kubernetes道場 9日目 - Serviceについて

デプロイ

Pod同様、kubectl createで作成します。

ただ、今回の場合はサービスと、その対象のPodの2つを作成する必要があります。
kubectl create-fコマンドでマニュフェストファイルを複数指定してもいいのですが、面倒な場合は、マニュフェストファイルを一つのディレクトリにまとめて、そのディレクトリ以下にあるyamlファイル全てを作成対象にするというやり方をとります。

# ./dirディレクトリ以下にあるファイル全てを参照する
$ kubectl create -f ./dir

./dir以下のymlファイルを全て見て、リソースを作る。

サービスにアクセスできるか確認

クラスター内でサービスに正しくアクセスできるかどうか確認してみましょう。

クラスター中にあるどれか1つのpodの中にログインして確かめてみます。
Podへのログインは以下のコマンドで行えます。

$ kubectl exec -it <podname> -- /bin/bash

まずは、サービスに接続するためにはどんなドメインを使えばいいのかを調べてみましょう。

# podの中で実行
# dig, nslookupといったDNS接続確認を行うコマンドをインストール
$ apt update
$ apt get install dnsutils

# サービス名でnslookup
$ nslookup k8s-mysql-service
Server:         10.96.0.10
Address:        10.96.0.10#53

Name:   k8s-mysql-service.default.svc.cluster.local
Address: 10.110.179.176

# サービス名でdig
$ dig k8s-mysql-service

; <<>> DiG 9.11.5-P4-5.1+deb10u1-Debian <<>> k8s-mysql-service
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NXDOMAIN, id: 34032
;; flags: qr rd ra; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
; COOKIE: 4529e3e33e3e0a28 (echoed)
;; QUESTION SECTION:
;k8s-mysql-service.             IN      A

;; Query time: 34 msec
;; SERVER: 10.96.0.10#53(10.96.0.10)
;; WHEN: Mon Jul 06 17:16:29 UTC 2020
;; MSG SIZE  rcvd: 58

どちらのコマンドでも、マニュフェストファイルで指定したサービス名k8s-mysql-serviceでアクセスできることが確認できました。

また、nslookupコマンドで取得できたIPアドレス10.110.179.176は、kubectl describeで確認できるサービスのIPと一致します。

# ローカルで実行
$ kubectl describe service k8s-mysql-service
#(略)
IP:                       10.110.179.176
#(略)

この名前解決を利用して、クラスター内からMySQLサービスに接続してみましょう。

# Pod内で実行
# mysqlサービスへのアクセス確認のため、sqlクライアントのインストールを行う
$ apt-get install default-mysql-client
$ mysql -h k8s-mysql-service -u root -p
Enter password:
MySQL [(none)]>

このように、サービス名を使うことで接続できました。

参考:Kubernetesの名前解決を確認する

serviceにMacのローカルホストを接続

Kubernetes Dashboardのように、起動しているサービスにクラスタ外のMacから接続したい!という状況があると思います。
例えばweb-serviceという名前のサービスを作り、そのサービスにMacのLocalhostを繋ぎブラウザ閲覧したいとします。
この場合、マニュフェストファイルを以下のように作ります。

web.yml
apiVersion: v1
kind: Service
metadata:
  name: web-service
  labels:
    app: myapp
spec:
  # ここのtypeをLoadBalancerにすることが重要
  type:
    LoadBalancer
  ports:
    - port: 9090
      targetPort: 9090
      protocol: TCP
  selector:
    app: myapp
    tier: web

このサービスをデプロイして、kubectl getで情報を確認してみます。

$ kubectl get service
()
NAME                  TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)          AGE
service/web-service   NodePort    10.101.53.215   localhost     8080:31392/TCP   4s

この場合、web-serviceにはMacのhttp://localhost:8080でアクセスすることができます。

サービスのTypeについて

今回、NodeTypeやLoadBalancerといった種類のサービスを扱いましたが、それぞれのネットワーク構成については、以下の記事がわかりやすいので紹介しておきます。
参考:Kubernetes ネットワーク構成

developmentを作ってみる

Pod定義でコンテナを増やしていってもいいのですが、今後の運用面を考えてコンテナはdevelopmentという形でデプロイしましょう。

developmentとは

developmentは、ReplicaSet(レプリカセット)を管理するための仕組みです。

まず、レプリカセットについて説明します。
例えば、同じPodを複数個作って冗長化したい!という場合は、Pod単独をいくつも繰り返し作るのではなく、レプリカセットという、「Podと作りたい個数を指定することで、指定Podのコピーを指定数だけ作る」機能で作ります。
(ECSでサービスを作成する際に、タスクを同時に何個起動させるかを選択するのと思想は同じです)

また、Podの内容を更新したい!というときは、レプリカセット単独ではなく、development中のレプリカセットという形でデプロイすることで、Podの自動更新・ロールバック等の運用操作が簡単にできるようになります。

それぞれの関係としては、Deployment → ReplicaSet → Podという包含関係になります。
つまり、developmentの中にレプリカセット(≒複数個のPodのコピー)を入れることで、バージョン管理や、複数個のPodをまとめて運用しやすくしているのです。

参考:Kubernetes: Deployment の仕組み

マニュフェストファイルの作成

先ほど作ったMySQLのPodをdevelopmentに直してみます。
以下のようなファイルを作ります。

dev.yml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: mysql-development
spec:
  replicas: 2
  selector:
    matchLabels:
      app: myapp
  template:
    metadata:
      labels:
        app: myapp
        tier: db
    spec:
      containers:
      - name: k8s-mysql
        image: mysql:5.7
        volumeMounts:
        - name: k8s-mysql-storage
          mountPath: /var/lib/mysql
        ports:
        - containerPort: 3306
        env:
        - name: MYSQL_ROOT_USER
          value: root
        - name: MYSQL_ROOT_PASSWORD
          value: pass
        - name: MYSQL_DATABASE
          value: sampledb
      volumes:
      - name: k8s-mysql-storage
        hostPath:
          path: /Users/myname/Desktop/k8s

記述の意味は以下の通りです。

dev.yml
apiVersion: apps/v1
kind: Deployment
metadata:
  # developmentの名前
  name: mysql-development
# developmentで作る状態について記述
spec:
  # レプリカセットの数
  replicas: 2
  # developmentが管理するリソースのラベル
  selector:
    matchLabels:
      app: myapp
  # developmentで管理するものの一覧
  template:
    # Pod, volumeにつけるラベル 
    metadata:
      labels:
        app: myapp
        tier: db
    # 以下はPod定義のときと同じ記述
    spec:
      containers:
      - name: k8s-mysql
        image: mysql:5.7
        volumeMounts:
        - name: k8s-mysql-storage
          mountPath: /var/lib/mysql
        ports:
        - containerPort: 3306
        env:
        - name: MYSQL_ROOT_USER
          value: root
        - name: MYSQL_ROOT_PASSWORD
          value: pass
        - name: MYSQL_DATABASE
          value: sampledb
      volumes:
      - name: k8s-mysql-storage
        hostPath:
          path: /Users/myname/Desktop/k8s

参考:Kubernetes公式ドキュメント Deployment
参考:DeploymentとServiceをyamlファイルで定義する

デプロイ・後片付け

Podやサービス同様、kubectl create/deleteを使って行います。

$ kubectl create -f dev.yml
$ kubectl delete -f dev.yml

EKSにデプロイ

ローカルで簡単な操作ができてコツをつかんだところで、いよいよ本番環境に乗せていきます。
今回は、AWSのEKSを利用します。

EKS上にクラスターを作成

クラスター作成の2つのやり方比較

まずは、EKS上にクラスターを作成します。
やり方は2つあり、1つはAWSのWebコンソール上で作成操作を行うやり方、もう1つはターミナルからeksctlコマンドを用いて作成操作を行うやり方です。

今回はeksctlコマンドを用いた方法でクラスター作成を行います。なぜかというと設定が圧倒的に楽だからです。
eksctlを用いることで、以下の設定が自動的に行われます。

  • EKSクラスターに属するノードに与えるIAMロール作成
  • VPCネットワーク/サブネット/ルートテーブル/IGW/NATゲートウェイの作成
  • セキュリティグループの作成
  • EKSクラスターヘのkubectlの接続

Webコンソールでクラスター作成を行なった場合、このような煩雑な設定を手動でやる必要があります。複雑な構成にしたいのではない場合は、eksctlコマンドを使用するのが効率的でしょう。

参考:「eksctl」コマンドを使ったAmazon EKS構築入門

eksctlコマンドのインストール

EKSにクラスターを作るためのeksctlコマンドをインストールします。
(できたクラスターを操作するのは、ローカルのときと同様にkubectlコマンドです。eksctlにできるのはあくまでクラスター関連の操作だけです。)

ターミナルで以下のコマンドを実行します。

$ brew tap weaveworks/tap
$ brew install weaveworks/tap/eksctl

#ダウンロードできたかは以下のコマンドで確認
$ which eksctl
/usr/local/bin/eksctl

参考:AWS公式ドキュメント eksctl コマンドラインユーティリティ

クラスター作成コマンド実行

早速コマンドを打ってクラスターを作成します。

$ eksctl create cluster \
      --vpc-cidr 10.0.0.0/16 \
      --name eks-sample \
      --region ap-northeast-1 \
      --version 1.14 \
      --nodegroup-name sample-workers \
      --node-type t2.micro \
      --nodes 1 \
      --nodes-min 1 \
      --nodes-max 3 \
      --managed

オプションの意味は以下の通りです。

  • vpc-cidr: 新規作成するVPCのCIDR
  • name: クラスター名
  • region: リージョン。東京リージョンを使いたいのでap-northeast-1を指定
  • version: Kubernetesのバージョン
  • nodegroup-name: ノードグループ(EKSクラスターに属するEC2インスタンスノード集団のこと)の名前
  • node-type: ノードに使うマシンのタイプ
  • nodes: 起動時点でのノード数
  • nodes-min: 最小ノード数
  • nodes-max: 最大ノード数
  • managed: ノードグループをWebコンソール上で表示・いじれるようにする

参考:eksctlを使った簡単Amazon EKS環境構築
参考:[アップデート] EKSがマネジメントコンソールおよびCLIでのワーカーノードの作成・管理をサポートしました

実行には結構時間がかかりますので気長に待ちましょう。

結果

クラスターがちゃんとできてます。
スクリーンショット 2020-07-11 0.24.28.png
コマンドでもその結果が確認できます。

# EKS上のクラスター一覧
$ eksctl get cluster
NAME            REGION
eks-sample      ap-northeast-1

# eks-sampleというクラスター上にあるnodegroupの確認
$ eksctl get nodegroup --cluster eks-sample
CLUSTER         NODEGROUP       CREATED                 MIN SIZE        MAX SIZE        DESIRED CAPACITY        INSTANCE TYPE   IMAGE ID
eks-sample      sample-workers  2020-07-11T04:57:37Z    1               3               1                       t2.micro

クラスターができるに当たって、以下のものが自動で作成されました。

  • VPC: 新しいものが1つ
  • サブネット: ap-northeast-1にある3つのAZにそれぞれ2個ずつ
  • ルートテーブル: パブリックサブネット用が1つ、プライベートサブネット用が3つのAZにそれぞれ1つずつ、新しいVPCのデフォルトルートテーブル1つの計5つ
  • インターネットゲートウェイ: 新しいVPC用に1つ
  • Elastic IP: 新しいVPCのNATゲートウェイ用に1つ
  • NATゲートウェイ: 1つ
  • セキュリティグループ: 新しいVPCのデフォルト1つと、EKSが作ったもの3つ

↓サブネットの様子
スクリーンショット 2020-07-11 0.26.27.png
↓ルートテーブルの様子
スクリーンショット 2020-07-11 0.27.06.png
↓セキュリティグループの様子
スクリーンショット 2020-07-11 0.29.13.png

結果的に、以下のような構成の環境が出来上がっています。
amazon-eks-on-aws-architecture-diagram.7fdf06380021e6dc7c622d298d99e3c1154163bc.png
画像出典:EKS公式ドキュメント スケーラブルなモジュール式 Amazon EKS アーキテクチャ

kubectlの設定

EKS上のクラスターにリソースをデプロイするのは、ローカル同様kubectlだという話は先ほどしました。
しかし、そのためにはkubectlコマンドで、ローカルではなくてEKSクラスターを選択するような設定が必要になります。

contextの確認

「どのクラスターをkubectlでいじるか」という設定は上述したcontextの選択で行います。
EKSクラスター作成後、存在するcontext一覧を確認してみましょう。

$ kubectl config get-contexts
CURRENT   NAME                                              CLUSTER                               AUTHINFO                                          NAMESPACE
          docker-desktop                                    docker-desktop                        docker-desktop                                    
          docker-for-desktop                                docker-desktop                        docker-desktop                                    
*         myname@eks-sample.ap-northeast-1.eksctl.io   eks-sample.ap-northeast-1.eksctl.io   myname@eks-sample.ap-northeast-1.eksctl.io

なんと、EKSクラスターをいじる用のcontextが自動作成・選択されていました。これはeksctlでクラスターを作成したからこそ行われたものです。
(WebコンソールでEKSクラスターを作成したら、これも手動で行います。eksctlコマンドの便利さがこれでわかりますね。)

kubeconfigの確認

なぜeksctlコマンドを実行しただけで、自動でEKS用のcontextが追加・選択されていたのでしょうか。
それはコマンド実行時にkubeconfigという設定ファイルが自動で編集されていたからです。

kubeconfigファイルの中身を見てみましょう。以下のようにコマンドを打ってみます。

$ kubectl config view

apiVersion: v1
clusters:
- cluster:
    certificate-authority-data: seeeeeeeeecret
    server: https://kubernetes.docker.internal:6443
  name: docker-desktop
- cluster:
    # EKSコンソールで確認できる認証機関の値
    certificate-authority-data: seeeeeeeeecret
    # EKSコンソールで確認できるAPIサーバーエンドポイントのアドレス
    server: https://********************.***.ap-northeast-1.eks.amazonaws.com
  name: eks-sample.ap-northeast-1.eksctl.io

contexts:
- context:
    cluster: docker-desktop
    user: docker-desktop
  name: docker-desktop
- context:
    cluster: docker-desktop
    user: docker-desktop
  name: docker-for-desktop
- context:
    cluster: eks-sample.ap-northeast-1.eksctl.io
    user: myname@eks-sample.ap-northeast-1.eksctl.io
  name: myname@eks-sample.ap-northeast-1.eksctl.io

current-context: myname@eks-sample.ap-northeast-1.eksctl.io
kind: Config
preferences: {}

users:
- name: docker-desktop
  user:
    client-certificate-data: seeeeeeeeecret
    client-key-data: seeeeeeeeecret
- name: myname@eks-sample.ap-northeast-1.eksctl.io
  user:
    exec:
      apiVersion: client.authentication.k8s.io/v1alpha1
      args:
      - token
      - -i
      - eks-sample
      command: aws-iam-authenticator
      env:
      - name: AWS_STS_REGIONAL_ENDPOINTS
        value: regional
      - name: AWS_DEFAULT_REGION
        value: ap-northeast-1

これは$HOME/.kube/configにある設定ファイルの中身がそのまま出力されている様子です。何もしていないのに、明らかにAWS用の設定と思われる箇所が存在します。
このように、kubeconfigに、EKSのクラスターに接続できるようなcontext設定が自動で追加されていることがここからもわかります。

kubectlの認証トークン設定

ローカル側でkubectlの対象クラスターをEKSに向けることはできました。しかし、EKS側ではkubectlコマンドでアクセスしてきた人が正規の権限を持った開発者であるとどのように認識しているのでしょうか。

EKSでは、クライアントにIAM認証情報から得たトークンをkubectlコマンド実行時に付与してもらい、そのトークンを見ることで、正規の権限を持った人かどうかを判定しています。

eks-iam.png
画像出典:EKS公式ドキュメント クラスター認証の管理

そのため、EKSクラスターを操作するためのcontextで、kubectlコマンド実行時に認証トークンを渡すように設定する必要があります。
やり方は2つあります。aws-iam-authenticatorコマンドを使う方法とaws eks get-tokenコマンドを使う方法です。

aws-iam-authenticatorコマンドの方法

デフォルトではこちらの方法になっています。aws-iam-authenticatorコマンドがインストール済みの方はこちらを使えば余計な手間がかからなくてよいでしょう。
kubeconfigの以下の記述の部分が該当箇所です。

$HOME/.kube/config
      args:
      - token
      - -i
      - eks-sample
      command: aws-iam-authenticator

aws eks get-tokenコマンドの方法

aws-iam-authenticatorコマンドはないがawsコマンドはある!という方はこちらの方法をとれば何もインストールしなくても大丈夫です。
上記のkubeconfig($HOME/.kube/configに存在)の記述部分を、以下のように書き換えます。

$HOME/.kube/config
      #args:
      #- token
      #- -i
      #- eks-sample
      #command: aws-iam-authenticator

      args:
      - eks
      - get-token
      - --cluster-name
      - eks-sample
      command: aws

参考:[アップデート]EKSを使う際にaws-iam-authenticatorが不要になりました!

kubectlがEKSクラスターを向いていることを実際のコマンドで確認

ここまでの準備ができたところで、ノード一覧を取得してみましょう。

$ kubectl get node
NAME                                             STATUS   ROLES    AGE   VERSION
ip-10-0-13-242.ap-northeast-1.compute.internal   Ready    <none>   85s   v1.15.11-eks-908ff6

明らかにAWS上のリソースが取得できました。eksctlコマンドで指定した通り、ノードが1つ作成されていることがわかります。

EKSクラスター上にリソース作成

kubectlコマンドでEKSクラスターを扱えるようになったので、いよいよEKS上にリソースを作成してみましょう。

マニュフェストファイルの修正(適宜)

もしもデプロイするコンテナのイメージとして、ECR上のイメージを使いたい!という場合は、マニュフェストファイルのコンテナイメージを指定する箇所を、ECRのリポジトリURIに書き換えればOKです。

dev.yml
# 以下は一例です
(略)
image: your-aws-account-id.dkr.ecr.ap-northeast-1.amazonaws.com/myapp/api:v1
(略)

デプロイ/削除

ローカル同様、kubectl create/deleteコマンドを実行します。

$ kubectl create -f dev.yml
deployment.apps/myapp-mysql created
$ kubectl delete -f dev.yml
deployment.apps/myapp-mysql deleted

また、LoadBalancer型のサービスをデプロイした場合、自動的にELBが作成→当該サービスとの紐付けが行われています。
例えば、LoadBalancer型のサービスがあるとき、kubectl getコマンドを実行し情報を確認してみます。

$ kubectl get service
NAME         TYPE           CLUSTER-IP      EXTERNAL-IP                                                                    PORT(S)          AGE
()
myservice    LoadBalancer   172.20.78.107   *******.ap-northeast-1.elb.amazonaws.com   9090:31806/TCP   7m25s

EXTERNAK-IPの部分に表示されているドメインは、自動作成されたELBのアドレスです。
そのため、http://*******.ap-northeast-1.elb.amazonaws.com:9090にブラウザからアクセスすれば、そのサービスにWebからアクセスすることができます。
参考:Amazon EKS のチュートリアルで Kubernetes を理解する #02 アプリのデプロイ

後片付け

使い終わったらEKS環境を片付けましょう。
デプロイしたリソースをkubectl deleteするのはもちろん、余計な費用がかからないようにEKSクラスターも以下のように削除しましょう。

$ eksctl delete cluster --name=eks-sample

これを実行することで、クラスター作成時に自動で作られたVPCやNATゲートウェイなども全てなくなります。また、kubeconfigに書かれたEKS用の設定記述も自動で消去されます。

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

Railsコマンド叩くとコンテナが落ちてたけどECSのメモリとCPUの設定を変えたら直った話

結論

下記のようにECSのコンテナメモリとCPUユニット数を変更したら直った

  • コンテナメモリ:300MiB → 1024MiB
  • コンテナCPU:500 → 512ユニット数 image.png

image.png
(フリー画像です)

事象

  • コンテナに入ってRailsコマンドを叩くとコンテナが落ちる
  • 502 Bad Gatewayで画面表示さえされない時もあった
    • なぜか本番環境では落ちてなかった
    • ステージング環境だけ落ちてた → ECSのインスタンスが落ちては自動立ち上げの無限ループ
  • Rails 6系にバージョンアップするとステージング環境の画面も表示されるようになった
    • 相変わらずRailsコマンドを叩くとコンテナは落ちた

EC2のインスタンスタイプ

  • 本番環境:m4.large
  • ステージング環境:t3.large

参考資料

試したことメモ

  • Railsコマンドテスト
    • rails -v は問題なかった。ほかRailsコマンドはダメなのでRailsアプリ内のスクリプトが動くとだめなのかも?
    • spring killしてRailsコマンド実施
    • springを起動しない設定の DISABLE_SPRING=1 を環境変数に設定してRailsコマンド実施
    • 直接指定 → $ DISABLE_SPRING=1 RAILS_ENV=staging ./bin/rails console
  • 環境変数とか見直し
  • Railsバージョンを上げてみた
  • Railsコマンドが走ってた場所まで戻して試してみた
  • topで実行プロセス見てみた → springじゃね?に繋がる
  • タスク定義の不要なやつを削除してみた
  • ほかAWSで紐づけている設定見直し
  • ECSのメモリ制限をハード制限・ソフト制限ともに3000MiBに設定

まとめ

多分ECSコンテナの要領不足。な気がします。
メモリよりCPUユニット数の方が決定打でした。

EC2のインスタンスタイプも適当に設定しちゃダメだと痛感しました。
インフラ周り疎いので詳しい方いたら教えてください。

余談

ちなみにコンテナCPUを500 → 1024に設定するとタスク定義が更新できなくなってしまった。
適切なユニット数を設定するのが大事みたい。
→ CPU1024MiBにすると、ECS常時2インスタンスだとして、切り替えのタイミングで2→4になり1024MiB*4が要求されて要領を超えてしまってた

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

DBサーバーの秘密鍵(mykey-private.pem)の作成方法

「AWS 踏み台サーバー経由のSSH」
上記、記事ありがとうございます。私は超初心者で、『Amazon Web Services 基礎からのネットワーク&サーバー構築』の改訂3版の通り進めていて、P148の秘密鍵のところで、躓いてしまいました。 上記に
>インスタンス②の秘密鍵はmy-key-private.pem なので、
とありますが、テキストでは、このインスタンスの秘密鍵を作成する場面はなかったと思います。

そこで、戻ってインスタンス①の秘密鍵と同じようにmy-key-private.pem を作成しようとしているのですが、うまくいきません。
1)既にできているセキュリティグループDB-SGに基づいて、インスタンス②の秘密鍵を作るか、
2)DB-SGを削除して、その作成からすすめて秘密鍵を作るか、
 どちらでも、結構です。何らかのヒントでもいただければ幸いです。

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

AWS基本事項メモ(主にEC2関連)

AMI(Amazon Machine Image)とは

インスタンス軌道に必要なテンプレート情報が入ったOSのイメージ。サーバーのテンプレートのようなもの。

AWSやサードパーティのAMIの他、カスタムAMIを作ることも可能。
カスタムAMIから、複数のインスタンスを作ることも可能。

インスタンス

EC2から立てられたサーバーのこと。
以下はインスタンスを立てる時に必要な事項

インスタンスタイプ

サーバーのスペックを定義したもの。

「m5.xlarge」

  • インスタンスタイプの名前の表記の意味
    • 「m」 インスタンスファミリー(どのような特徴を持ったインスタンスかの種類を表したもの)
    • 「5」 インスタンス世代(基本的に新しい世代がコスパが良い場合が多いので良い)
    • 「xlarge」 インスタンスサイズ(スペックの高低)

ストレージ

サーバにくっつけるデータの保管場所。EC2には以下の2種類ある。

  • EBS
    • 高い可用性と耐久性を持つストレージ
    • 他のインスタンスにも付け替え可能。
    • EC2インスタンスをstop/terminateしてもデータの保持が可能。
    • S3を利用してスナップショットを保存可能
    • EBSの別料金がかかる
    • OSやDBなどの永続性と耐久性が必要なデータを置く
    • HDDのようなイメージ
  • インスタンスストア
    • インスタンス専用のストレージ
    • 他のインスタンスには付け替えられない。
    • EC2インスタンスをstop/terminateするとデータが消える
    • 追加料金はかからない。
    • 無くなってはいけないデータは置けない
    • 一時ファイル、キャッシュなど無くなっても構わないデータに向く

ssh

SucureShellの略で安全に通信を行って、ネットワークに接続された機器を遠隔操作するための通信手段。
例えば自分のPCからssh接続でAWSのEC2インスタンス内に入って作業することができる。
サーバ内に公開鍵を置いておき、秘密鍵を持っているユーザーのみ接続できる。
EC2作成時に作成できるpemファイルなどはこの秘密鍵

ポート番号

IPアドレスがインターネット上でコンピュータを識別するアドレスなのに対して、ポート番号はプログラムのアドレスと表現することができる。
プログラムごとにポート番号が決まっている。
IPアドレスで、コンピュータを指定し、ポート番号でそのコンピュータ内の通信したい対象のプログラムを指定するイメージ。

例えばssh接続であるサーバに接続したいとき、

  • 接続したいサーバAのIPアドレス 
    • 178.10.0.10
  • そのサーバで走っているプログラムにそれぞれ割り当てられているポート番号が
    • sshサーバ(具体的にはsshdプログラム)が22
    • httpプログラム(サーバ)が80
    • SMPTプログラム(サーバ)が25

の場合、
自分のPCから「178.10.0.10」のポート番号22に接続する」と通信を送ると、サーバAにssh接続する。

ウェルノウンポート番号と言って大体のサーバで標準的なポート番号は決まっていて、例えば

  • sshサーバが22
  • httpサーバが80
  • httpsサーバは443
  • SMPTサーバが25
  • 以上となっている。逆にクライアント側ポート番号は動的に決められたりする。
サーバ内で以下のコマンドを実行すると、ポート番号一覧が取得できる。
sudo lsof -i -n -P //-n:ipアドレスとホスト名に変換しないオプション -P:ポート番号をサービス名に変換しないオプション。

ファイアーウォール設定(セキュリティグループ)

ES2インスタンスではセキュリティ上デフォルトではインバウンド(サーバへのアクセス)はssh接続のポート番号22への接続以外は受け付けないようになっている。サーバを外部公開するにはhttp80かhttps443のポートへの接続を受け付けるようにセキュリティグループを設定する必要がある。

Elastic IP アドレス

インターネット経由でアクセス可能な固定グローバルIPアドレスを取得し、インスタンスに付与できるサービス。

通常EC2インスタンス(サーバ)は停止、再起動をするとIPアドレスが変わってしまう為、これでIPアドレスを固定化する。
インスタンスを削除するまでは基本的にずっと固定のIPアドレスを使える。
インスタンスと紐づけられていて、そのインスタンスが起動中は無料、そうでない場合は有料
逆に言うとElastic IPアドレスが使われていないと課金されるので、使わない場合は都度解放する。

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

水樹奈々さん公式サイトも使う? S3,Cloudfont,Route53をコマンド一発でデプロイするslsテンプレ

水樹奈々さんのHPがAWSを使っていると話題になりましたが、Route53・S3・Cloudfrontを連携させる設定はAWS初見の人では難しく感じると思います。私がそうでした。

今はSeverless Frameworkを使い、一発でデプロイするファイルを作成して運用しているので、公開します。
Githubはこちらデモページはこちらです。
テンプレは以下の環境で動作・デプロイされます。適宜読み替えてください。

  • umihi.coというドメインをroute53で管理している。
  • us-east-1のACMにてumihi.co*.umihi.coを1つとして証明書を発行している。
  • サブドメインsls-static-website.umihi.coとして静的サイトをデプロイする

以下のファイルを作成し、AcmCertificateArnと、ドメイン各所の書き換えを行いsls deployでデプロイします。分かりやすさ重視でなるべくハードコーディングしています。

serverless.yaml
service: sls-static-website

provider:
  name: aws
  runtime: provided
  stage: dev
  region: ap-northeast-1

resources:
  Resources:

    WebsiteBucket:
      Type: AWS::S3::Bucket
      Properties:
        BucketName: sls-static-website.umihi.co
        WebsiteConfiguration:
          IndexDocument: index.html
          ErrorDocument: 404.html
      DeletionPolicy: Retain

    WebsiteCloudfront:
      Type: AWS::CloudFront::Distribution
      DependsOn:
      - WebsiteBucket
      Properties:
        DistributionConfig:
          Origins:
          - DomainName: sls-static-website.umihi.co.s3-website-ap-northeast-1.amazonaws.com
            Id: S3Origin
            CustomOriginConfig:
              HTTPPort: '80'
              HTTPSPort: '443'
              OriginProtocolPolicy: http-only
          Enabled: true
          HttpVersion: 'http2'
          DefaultRootObject: index.html
          Aliases:
          - sls-static-website.umihi.co
          DefaultCacheBehavior:
            AllowedMethods:
            - GET
            - HEAD
            Compress: true
            TargetOriginId: S3Origin
            ForwardedValues:
              QueryString: true
              Cookies:
                Forward: none
            ViewerProtocolPolicy: redirect-to-https
          PriceClass: PriceClass_All
          ViewerCertificate:
            AcmCertificateArn: arn:aws:acm:us-east-1:123456789:certificate/xxxxxxxx-yyyy-zzzz-aaaa-123456789 # must be in us-east-1
            SslSupportMethod: sni-only

    WebsiteDNSName:
      Type: AWS::Route53::RecordSetGroup
      Properties:
        HostedZoneName: umihi.co. # you need (.)period at the end
        RecordSets:
        - Name: sls-static-website.umihi.co. # you need (.)period at the end
          Type: A
          AliasTarget:
            HostedZoneId: Z2FDTNDATAQYW2 #  This is always the hosted zone ID when you create an alias record that routes traffic to a CloudFront distribution.
            # DNSName: ddddxxxxyyyyzzzzz.cloudfront.net # you can also hard-code like this
            DNSName:
              Fn::GetAtt: [ WebsiteCloudfront, DomainName ]

作成時にハマったのは

  • route53のHostedZoneNameRecordSets.Nameの値には末尾にピリオドが必要がある。
  • S3にアップロード・更新しても、Cloudfrontがキャッシュを持つと反映されないので都度Invalidationする必要がある
  • CloudfrontのOrigin設定のDomainNameで補完で候補に表示されるsls-static-website.umihi.co.s3.amazonaws.comではなく、 Static Website Hostingのエンドポイントのsls-static-website.umihi.co.s3-website-ap-northeast-1.amazonaws.comを使わないと、この構成だとリダイレクトのループになった。

リダイレクト問題自体は解決せず分からなかったのですが仮に解決したら、どちらのエンドポイントを使うべきかはこちらが参考になりました。

バケットにファイルを手動でアップロードすると、公開する作業が必要になります。serverless-s3-syncを使い設定すればデプロイ時にローカルファイルを常に同期することが可能になり、公開作業も不要になります。Githubレポジトリのserverless.ymlにはその設定も含まれているので、参考にしてください。

参考

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

静的サイトの鉄板構成であるS3,Cloudfont,Route53をコマンド一発でデプロイするslsテンプレ

Route53・S3・Cloudfrontを連携させる設定はAWS初見の人では難しく感じると思います。私がそうでした。

今はSeverless Frameworkを使い、一発でデプロイするファイルを作成して運用しているので、公開します。
Githubはこちらデモページはこちらです。
テンプレは以下の環境で動作・デプロイされます。適宜読み替えてください。

  • umihi.coというドメインをroute53で管理している。
  • us-east-1のACMにてumihi.co*.umihi.coを1つとして証明書を発行している。
  • サブドメインsls-static-website.umihi.coとして静的サイトをデプロイする

以下のファイルを作成し、AcmCertificateArnと、ドメイン各所の書き換えを行いsls deployでデプロイします。分かりやすさ重視でなるべくハードコーディングしています。

serverless.yaml
service: sls-static-website

provider:
  name: aws
  runtime: provided
  stage: dev
  region: ap-northeast-1

resources:
  Resources:

    WebsiteBucket:
      Type: AWS::S3::Bucket
      Properties:
        BucketName: sls-static-website.umihi.co
        WebsiteConfiguration:
          IndexDocument: index.html
          ErrorDocument: 404.html
      DeletionPolicy: Retain

    WebsiteCloudfront:
      Type: AWS::CloudFront::Distribution
      DependsOn:
      - WebsiteBucket
      Properties:
        DistributionConfig:
          Origins:
          - DomainName: sls-static-website.umihi.co.s3-website-ap-northeast-1.amazonaws.com
            Id: S3Origin
            CustomOriginConfig:
              HTTPPort: '80'
              HTTPSPort: '443'
              OriginProtocolPolicy: http-only
          Enabled: true
          HttpVersion: 'http2'
          DefaultRootObject: index.html
          Aliases:
          - sls-static-website.umihi.co
          DefaultCacheBehavior:
            AllowedMethods:
            - GET
            - HEAD
            Compress: true
            TargetOriginId: S3Origin
            ForwardedValues:
              QueryString: true
              Cookies:
                Forward: none
            ViewerProtocolPolicy: redirect-to-https
          PriceClass: PriceClass_All
          ViewerCertificate:
            AcmCertificateArn: arn:aws:acm:us-east-1:123456789:certificate/xxxxxxxx-yyyy-zzzz-aaaa-123456789 # must be in us-east-1
            SslSupportMethod: sni-only

    WebsiteDNSName:
      Type: AWS::Route53::RecordSetGroup
      Properties:
        HostedZoneName: umihi.co. # you need (.)period at the end
        RecordSets:
        - Name: sls-static-website.umihi.co. # you need (.)period at the end
          Type: A
          AliasTarget:
            HostedZoneId: Z2FDTNDATAQYW2 #  This is always the hosted zone ID when you create an alias record that routes traffic to a CloudFront distribution.
            # DNSName: ddddxxxxyyyyzzzzz.cloudfront.net # you can also hard-code like this
            DNSName:
              Fn::GetAtt: [ WebsiteCloudfront, DomainName ]

作成時にハマったのは

  • route53のHostedZoneNameRecordSets.Nameの値には末尾にピリオドが必要がある。
  • S3にアップロード・更新しても、Cloudfrontがキャッシュを持つと反映されないので都度Invalidationする必要がある
  • CloudfrontのOrigin設定のDomainNameで補完で候補に表示されるsls-static-website.umihi.co.s3.amazonaws.comではなく、 Static Website Hostingのエンドポイントのsls-static-website.umihi.co.s3-website-ap-northeast-1.amazonaws.comを使わないと、この構成だとリダイレクトのループになった。

リダイレクト問題自体は解決せず分からなかったのですが仮に解決したら、どちらのエンドポイントを使うべきかはこちらが参考になりました。

バケットにファイルを手動でアップロードすると、公開する作業が必要になります。serverless-s3-syncを使い設定すればデプロイ時にローカルファイルを常に同期することが可能になり、公開作業も不要になります。Githubレポジトリのserverless.ymlにはその設定も含まれているので、参考にしてください。

参考

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

【Capistrano,Unicorn】ArgumentError: directory for pid=/var/www/badsuru/current/shared/tmp/pids/unicorn.pid not writableが書き込み権限の問題ではなかった

はじめに

Capistranoを用いた自動デプロイ中、タイトルのエラーが出てほぼ1日を費やしました・・・
結論、大したことではなく自分にがっかりしてしまいましたが、同じようなエラーで悩む方の手助けになれば幸いです。

対象者

  • 初学者
  • Capistrano設定中の方
  • Unicorn使用者

開発環境

  • Rails 6.0.3.1
  • ruby 2.7.1
  • unicorn 5.4.1
  • AWS Amazon Linux AMI 2018.03.0 (HVM), SSD Volume Type

この記事を通じて得られること

  • タイトルのエラーの原因・解決方法 ※あくまで1つのエラーの解決方法であることをご了承ください。エラーの原因によっては違う解決方法になることが考えられます。

結論(解決方法)

unicorn.rbの設定記述ミスです。以下の通り変更しました。
開発中のアプリのパスが違うために、unicorn.pidを作成する/var/www/badsuru/current/shared/tmp/pids/ディレクトリが見つからず、タイトルのエラーを吐き出していました。

変更前

unicorn.rb
//サーバ上でのアプリケーションコードが設置されているディレクトリを変数に入れておく
app_path = File.expand_path('../../', __FILE__)

//アプリケーションサーバの性能を決定する
worker_processes 1

// アプリケーションの設置されているディレクトリを指定
working_directory app_path

(以下省略)

変更後

unicorn.rb
//サーバ上でのアプリケーションコードが設置されているディレクトリを変数に入れておく
app_path = File.expand_path('../../../', __FILE__)

//アプリケーションサーバの性能を決定する
worker_processes 1

// アプリケーションの設置されているディレクトリを指定
// currentを指定
working_directory "#{app_path}/current"

(以下省略)

詳細

Capistarnoの自動設定ファイルを記述し、いざ実行したところ、以下のエラーが吐き出されました。

00:44 unicorn:start
      01 $HOME/.rbenv/bin/rbenv exec bundle exec unicorn -c /var/www/myapp/current/config/unicorn.rb -E deployment -D 
      01 bundler: failed to load command: unicorn (/var/www/myapp/shared/bundle/ruby/2.7.0/bin/unicorn)
      01 ArgumentError: directory for pid=/var/www/myapp/current/shared/tmp/pids/unicorn.pid not writable
      01   /var/www/myapp/shared/bundle/ruby/2.7.0/gems/unicorn-5.4.1/lib/unicorn/configurator.rb:100:in `block in reload'
      01   /var/www/myapp/shared/bundle/ruby/2.7.0/gems/unicorn-5.4.1/lib/unicorn/configurator.rb:96:in `each'
      01   /var/www/myapp/shared/bundle/ruby/2.7.0/gems/unicorn-5.4.1/lib/unicorn/configurator.rb:96:in `reload'
      01   /var/www/myapp/shared/bundle/ruby/2.7.0/gems/unicorn-5.4.1/lib/unicorn/configurator.rb:77:in `initialize'
      01   /var/www/myapp/shared/bundle/ruby/2.7.0/gems/unicorn-5.4.1/lib/unicorn/http_server.rb:77:in `new'
      01   /var/www/myapp/shared/bundle/ruby/2.7.0/gems/unicorn-5.4.1/lib/unicorn/http_server.rb:77:in `initialize'
      01   /var/www/myapp/shared/bundle/ruby/2.7.0/gems/unicorn-5.4.1/bin/unicorn:126:in `new'
      01   /var/www/myapp/shared/bundle/ruby/2.7.0/gems/unicorn-5.4.1/bin/unicorn:126:in `<top (required)>'
      01   /var/www/myapp/shared/bundle/ruby/2.7.0/bin/unicorn:23:in `load'
      01   /var/www/myapp/shared/bundle/ruby/2.7.0/bin/unicorn:23:in `<top (required)>'
      01 master failed to start, check stderr log for details
(省略)

当初、unicorn.pid not writableと記述があったので、権限周りのエラーかと思い、releases,current,shared...などなど様々なディレクトリに書き込み権限を与えても解決されず、途方にくれていました。

また、mkdir pidsコマンド等で予めディレクトリを作成しなければならないという情報をググって見つけて試したけど上手く行かず・・・
見直したつもりの設定ファイルの記述を丁寧に見直したら結論の間違えに気が付きました。

推測になってしまいますが、unicornの起動と共にunicorn.pidsファイルを設定ディレクトリ配下に作成するのですが、unicornを実行させるアプリケーションのディレクトリ設定が間違えている状態です。unicorn.pidsファイルを作成したいのだけど、そのディレクトリにも辿りつけないから、見つからないというメッセージの代わりに、タイトルのエラーが吐き出されるようです。確かに、エラーの解決法を探している時も、設定ファイルの記述を指摘する記事もあったなあ・・・

終わりに

エラーが出て、指摘通りの内容を修正してもまだ出る時は、エラー文と違うミスの可能性も十分に考えられること。自分が記述してきたファイルをしっかり見直ししようという教訓になりました。

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