20191129のdockerに関する記事は15件です。

Docker ComposeでLaravel6.5の環境を作成

少しDockerComposeについて勉強してみたので
試しにLaradockを使わずにDocker ComposeでLaravelの環境構築をしてみます

作成する環境

Laravel 6.5.2
php7.3
mysql 8.0.18
nginx 1.17

ディレクトリ構成

project
├ docker-compose.yml
├ docker
|  ├ php
│  │  ├ php.ini
│  │  └ Dockerfile
│  ├ nginx
│  │  └ default.conf
│  └ mysql
│     ├ conf.d
│     │  └ default_authentication.cnf
│     └ data
└ src

PHP用のDockerfileを作成

docker/php/Dockerfile
FROM php:7.3-fpm
COPY php.ini /usr/local/etc/php/

RUN apt-get update \
  && apt-get install -y zlib1g-dev libzip-dev mariadb-client \
  && docker-php-ext-install zip pdo_mysql

#Composer install
RUN php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
RUN php -r "if (hash_file('sha384', 'composer-setup.php') === 'a5c698ffe4b8e849a443b120cd5ba38043260d5c4023dbf93e1558871f1f07f58274fc6f4c93bcfd858c6bd0775cd8d1') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;"
RUN php composer-setup.php
RUN php -r "unlink('composer-setup.php');"
RUN mv composer.phar /usr/local/bin/composer
# rootでのcomposerコマンド実行を許可
ENV COMPOSER_ALLOW_SUPERUSER 1
# composerのグローバルパッケージがインストールされるディレクトリの指定
ENV COMPOSER_HOME /composer
# composerのパスを通す
ENV PATH $PATH:/composer/vendor/bin
# laravelインストール
RUN composer global require "laravel/installer"

RUN curl -SL https://deb.nodesource.com/setup_13.x | bash
RUN apt-get install -y nodejs && \
  npm install -g npm@latest && \
  npm install -g @vue/cli

WORKDIR /var/www

php.iniの設定

docker/php/php.ini
; timezone
date.timezone = Asia/Tokyo

; error reporing
log_errors = On
error_log = /dev/stderr
display_errors = Off

; mbstring
mbstring.language = "Japanese"

timezoneを東京に
errorログを標準出力エラーに設定
デフォルトで使用する言語を日本語に設定

nginxの設定ファイルを記述

docker/nginx/default.conf
server {
  listen 80;

  root  /var/www/public;
  index index.php;

  location / {
    try_files $uri $uri/ /index.php$is_args$args;
  }

  location ~ \.php$ {
    fastcgi_split_path_info ^(.+\.php)(/.+)$;
    fastcgi_pass   php-fpm:9000; 
    fastcgi_index  index.php;

    include        fastcgi_params;
    fastcgi_param  SCRIPT_FILENAME $document_root$fastcgi_script_name;
    fastcgi_param  PATH_INFO $fastcgi_path_info;
  }
}

fastcgi_pass php-fpm:9000;のphp-fpmにはdocker-compose.ymlで定義したサービス名を指定します。

mysql

MySQL8系のデフォルト認証形式を変更するための設定ファイルを作成します

docker/mysql/conf.d/default_authentication.cnf
[mysqld]
default_authentication_plugin= mysql_native_password

続いて、mysqlのデータを永続化するためのディレクトリを作成します

$ mkdir /docker/mysql/data

docker-compose.ymlの作成

docker-compose.yml
version: '3'
services:
  nginx:
    image: nginx:1.17-alpine # nginxのimageにnginx:1.17-alpineを指定
    container_name: "nginx"
    ports:
      - "8080:80" # ホストの8080ポートでnginxコンテナの80にアクセス
    volumes:
      - ./src:/var/www # ホストのsrcをnginxコンテナの/var/wwwにマウント
      - ./docker/nginx/default.conf:/etc/nginx/conf.d/default.conf # 作成したdefault.confを/etc/nginx/conf.d/default.confにマウント
    depends_on: # コンテナの依存関係を定義する
      - php-fpm # php-fpmの起動後にnginxを起動
  php-fpm:
    build: ./docker/php # ./docker/php/Dockerfileをbuildしてイメージ作成
    container_name: "php-fpm"
    volumes:
      - ./src:/var/www # ホストのsrcをphp-fpmコンテナの/var/wwwにマウント
    links: # コンテナと他のサービスを繋げる
      - db
    depends_on:
      - db # dbの起動後にphp-fpmを起動
  db:
    image: mysql:8.0.18 # mysqlのimageにmysql:8.0.18を指定
    container_name: "db"
    volumes:
      - ./docker/mysql/conf.d:/etc/mysql/conf.d # mysqlのせってファイルをマウント
      - ./docker/mysql/data:/var/lib/mysql # mysqlのデータ永続化
    ports:
      - 3306:3306
    environment:
      MYSQL_DATABASE: hogehoge
      MYSQL_USER: hoge
      MYSQL_PASSWORD: hoge
      MYSQL_ROOT_PASSWORD: root
      TZ: "Asia/Tokyo"
    command: mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci

コンテナ起動

$ docker-compose build
$ docker-compose up -d

laravelのプロジェクトファイル作成

$ docker-compose exec php-fpm composer create-project --prefer-dist "laravel/laravel=6.5.2" .

確認

これでhttp://localhost:8080にアクセスするとlaravelのページが表示されます
スクリーンショット 2019-11-29 20.37.04.png

 laravelの認証画面まで作ってみる

$ docker-compose exec php-fpm composer require laravel/ui
$ docker-compose exec php-fpm php artisan ui vue --auth
$ docker-compose exec php-fpm npm run dev

php artisan make:auth をしたら怒られました、、、laravel6で変更されたんですね

このためにDockerfileで下記を書いています

RUN curl -SL https://deb.nodesource.com/setup_13.x | bash
RUN apt-get install -y nodejs && \
  npm install -g npm@latest && \
  npm install -g @vue/cli

再度確認

http://localhost:8080/register
無事ユーザー登録ページが表示されました
スクリーンショット 2019-11-29 20.36.34.png

登録してみる

怒られた、、、
スクリーンショット 2019-11-29 20.40.57.png

laravelの.envでDBの設定を忘れていたので設定

src/.env
DB_CONNECTION=mysql
DB_HOST=db
DB_PORT=3306
DB_DATABASE=hogehoge
DB_USERNAME=hoge
DB_PASSWORD=hoge

DB_HOST
DB_DATABASE
DB_USERNAME
DB_PASSWORD
をdocker-compose.ymlで定義したものに変更してください
DB_HOSTはDBコンテナのサービス名です

認証用のテーブルも作り忘れていたので作成

$ docker-compose exec php-fpm php artisan migrate

再度作成!

いけた!!!
スクリーンショット 2019-11-29 22.48.49.png

まだまだDocker勉強中で間違っている所があるかと思いますので
ご指摘ありましたらよろしくお願いいたします🙇‍♂️

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

libsassのalpine向けバイナリーの提供状況

node-sassにバンドルされるlibsassはalpine向けにもバイナリーを配布している。が条件あり。

  • 利用してるNode.jsのversionによって、alpine向けに配布をしてるバージョンの範囲が変わる
  • 開発版サポートが先に提供中止になる流れ
    • 次回or 次々回リリースでは v11向けのalpineバイナリー提供がなくなるっぽい

Node v13: 最新版 v4.13.0 から配布

https://github.com/sass/node-sass/releases/tag/v4.13.0

Node v12: v4.12.0から配布

https://github.com/sass/node-sass/releases/tag/v4.12.0

Node v11: v4.10.0から配布

https://github.com/sass/node-sass/releases/tag/v4.10.0

  • おそらくv4.13.0が最後の配布versionに

Node v10: v4.9.0から配布

https://github.com/sass/node-sass/releases/tag/v4.9.0

Node v9: v4.6.0からv4.10.0まで配布

https://github.com/sass/node-sass/releases/tag/v4.6.0

  • v4.11.0 以降は提供されず
  • 次世代の開発バージョンに配布開始したversionが最後の配布versionに

v8: v4.5.3~

備考

alpineを使う利点

  • イメージサイズが公式イメージと比べて 1/10以下
    • 10.15.1だと、897MB=> 70.7MB
  • インストール所要時間の削減

サポートしてないnode.js alpineイメージへnode-sassをインストールしたい

gypなどを入れればok

apk add --no-cache --virtual .gyp python make g++ \
    && apk --no-cache add avahi-dev \
    && yarn global add mdns \
    && apk del .gyp
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

chromedriver/seleniumコンテナで.pfxキー認証が必要なサイトにアクセスできるdockerfileを書く

RでWEBスクレープ、慣れるとひょひょひょーいと書けてホントに便利です。

で、たまにあるんですよねちゃんとセキュリティしてらっしゃる会社さん。
実に素晴らしい事なのですが、レア過ぎてなかなか情報が無かったので突破法だけ書きたいと思います。

なぜできないか

  1. .pfx 証明書ファイルを自動で追加する方法がわからん - これが出る
    Screenshot 2019-11-29 at 19.12.32.png

  2. 追加できたんだけど、サイトにアクセスする度にブラウザがポップアップを開く(自動クリックめんどい)
    Screenshot 2019-11-29 at 19.14.01.png

対策は:

1. dockerfile内でユーザレベルで追加する
2. chrome policyで、証明書の自動セレクトを有効に

です。

dockerfile 作ろう

まずは dockerfile と同じ階層に下記のファイルを用意します。

/keys_dir.
key_password.txt - .pfxファイルのパスワードを平文で
key.pfx
policy.json - chrome policy 設定ファイル (下記)

Chrome policy ファイルの中身はこちら

policy.json
{
    "AutoSelectCertificateForUrls": ["{\"pattern\":\"*\",\"filter\":{}}"]
}

上記を用意したら、dockerfileを作成します。

FROM selenium/standalone-chrome-debug

# chrome policy
COPY keys/policies.json /etc/opt/chrome/policies/managed/
# key 関連
COPY keys/* /home/seluser/keys/

# 必要なツールをインストール
RUN sudo apt-get update
RUN sudo apt-get install libnss3-tools

# 証明書をユーザに追加
RUN mkdir -p /home/seluser/.pki/nssdb
RUN certutil -d /home/seluser/.pki/nssdb -N
RUN pk12util -d sql:/home/seluser/.pki/nssdb -i /home/seluser/keys/key.pfx -w /home/seluser/keys/key_password.txt

あとはお好きな名前をつけてビルドします

build.sh
docker build --rm --force-rm -t mytools/selenium-withcert .

実行しましょう。
僕はダウンロードに使うことが多いのでこのように -v でマウントします。

run.sh
docker run -d --restart always -v /dev/shm:/dev/shm -p 4444:4444 -p 5900:5900 -v ~/sel_dl:/home/seluser/Downloads --name selenium_cert mytools/selenium-withcert

以上です!!!
けっこう情報が無くて調べるの苦労しました。
いつか誰かの役に立つはず...

おまけ

たぶん関係ないと思うんですが、もし上でうまく行かなかった方のために、
参考までに他に僕がしてるprefs/argsの設定も晒しておきます。
(まさかのRだぞわっはっは。python/java他の方は読み替えをお願いします)

RSelenium_chromedriver.R
chrome_prefs = 
  list(  
    "profile.default_content_settings.popups" = 0L,
    "download.prompt_for_download" = FALSE,
    "download.directory_upgrade" = TRUE,
    "safebrowsing.enabled" = FALSE,
    "safebrowsing.disable_download_protection" = TRUE,
    "acceptSslCerts" = TRUE
  )

chrome_args = 
  c('--ignore-certificate-errors', 
    '--ignore-urlfetcher-cert-requests', 
    '--no-sandbox', 
    '--disable-gpu', 
    '--disable-web-security', 
    '--user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100')

eCaps_withhead = 
  list(chromeOptions = 
         list(
           prefs = chrome_prefs,
           args = chrome_args )
        )

# 初期化
remDr <- remoteDriver(
  remoteServerAddr = sel_ip,
  port = sel_port,
  browserName = "chrome",
  extraCapabilities = eCaps_withhead
)

[EOF]

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

【Rails】Docker+Rails環境でpryの`edit`コマンドが使えなかったので、使えるようにした

はじめに

DockerとRailsを使った開発環境でpryのeditコマンドが効かなかったので対処してみたときの記録です。

※Docker環境下での話です。ローカルでは普通に動作しましたので、割愛します。

この記事が役に立つ方

  • あれ?なんかeditコマンド効かなくね?と思っている方

この記事のメリット

  • Docker環境下でpryのeditコマンドが使えるようになる

環境

  • macOS Catalina 10.15.1
  • zsh: 5.7.1
  • Ruby: 2.6.5
  • Rails: 5.2.3
  • Docker: 19.03.5
  • docker-compose: 1.24.1

pryのeditコマンドとは?

以下、公式のWikiです。
Editor integration · pry/pry Wiki · GitHub

The edit command is used to invoke your default editor. This command will load your file once you have finished editing it (unless you pass the -n or --no-reload flag to edit).

pryの画面でそのままデフォルトのエディタを開き、ファイルの編集が出来るコマンドです。

これまでpryの画面上からファイルをいじったりはしていなかったのですが、デバッグ効率が上がるかなと思ったので導入します。

【事前準備】

こちらのドキュメントに従ってDocker環境下でgem pry-railsを使うための準備は済ませておきました。

Using pry-rails with Docker · GitHub

上記ドキュメントの流れは以下の通りです。

docker-compose.yml
app:
  tty: true
  stdin_open: true

上記設定を追加。

Gemfile
group: :development
  gem 'pry-rails'
end

gemを追加。

docker-compose build

ビルド。

docker-compose up

起動。

docker ps

RailsアプリのCONTAINER IDNAMESを確認

docker attach `CONTAINER ID``NAMES`

binding.pry
※好きなところで

これで普通にpryでデバッグ可能な状態になります。

とりあえずeditコマンド使ってみようとする

※Docker環境下です。

とりあえずeditコマンドを叩いてみます。

[1] pry(main)> edit
Error: Please set Pry.config.editor or export $VISUAL or $EDITOR

するとエラー発生
Pry.config.editorか、環境変数$VISUAL$EDITORを設定してねという内容。

先程のWikiの、Setting the default editorのくだりに設定方法が記載されていたので、その通りに進めてみます。

1. ~/.pryrcを作成・設定

pryの設定ファイルは.pryrcです。
今回はなかったので作成しました。

以下設定方法についてです。
Editor integration · pry/pry Wiki · GitHub

今回エディタはvimを使いたかったので、以下のように設定。

~/.pryrc
Pry.config.editor = proc { |file, line| "vim #{file}:#{line}" }

該当のファイルと行に飛んでくれる仕様に例にならってみましたが、うまくいきません。

ローカルの設定ファイルは読み込まれないんですね。

2.Railsアプリのディレクトリ内に.pryrcを配置する。

下記記事を参照し、Railsアプリのルートディレクトリに.pryrcを配置してみることにしました。

docker-compose上のRailsのデバッグを行う - My External Storage

./.pryrc
Pry.config.editor = proc { |file, line| "vim #{file}:#{line}" }

※設定は先程と同様です。

もう一回試します。

[1] pry(main)> edit
Error: `vim ファイル名:行数` gave exit status: 127

別のエラー発生。
惜しい!コマンド自体は正しそうに見えますが、127なるエラーが。

何これ?と思って調べると、
「コマンドが見つからない、もしくはタイポ」
らしいと判明。

...ということは、そもそもvimがない?

3.vimをインストールする

なければいくらコマンドが正しくても、どうしようもないですよね。

調べると、こちらの記事を発見しました。
Docker — docker コンテナの中で vim が使えない場合 - Qiita

Dockerfile
apt-get install vim

上記を追記。

docker-compose build

docker-compose up


rails cなどでpry起動。

[1] pry(main)> edit
#=>vim起動

無事起動!

おわりに

最後まで読んで頂きありがとうございました:bow_tone1:

vimがないという環境もあるのかとびっくりしましたが、ローカルとDocker環境の境目を意識するいい経験になりました:point_up:

これでデバッグ効率が上がりそうです:relaxed:

参考にさせて頂いたサイト(いつもありがとうございます)

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

稼働中のコンテナのシェルに入りたい~attachとexecの違い~

環境

・windows 10
・Docker version 19.03.5

目的とゴール

・attachとexecの違いが分かるようになる
・起動中のコンテナのシェルに入れるようになる

attachとexecの違い

attach

稼働中のコンテナに入るコマンド。
コンテナで実行中のプロセスが開始される。
つまり、docker psで表示されるCOMMANDのコマンドが実行される。

exec

現在稼働しているコンテナで新たなプロセスを開始できるコマンド。
シェルに入りたい場合は下記コマンドを実行する。
docker exec -it XXXXXXXXXX /bin/bash

つまり稼働中のシェルに入りたい場合は、
①docker psでCOMMANDが/bin/bashの場合
②それ以外の場合

の2パターンに分かれることになります。

パターン1:docker psでCOMMANDが/bin/bashの場合

docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
72a12e05690c        ubuntu              "/bin/bash"         29 hours ago        Up 8 seconds                           hungry_bassi

実行中のプロセスは/bin/bashとなります。
こういう時はattachしてあげればシェルに入れます。

> docker attach 72a12e05690c
root@72a12e05690c:/#  

ちなみにSTATUSがExitedの場合は、docker start -ai XXXXXXXXで入れますね。
attachはあくまで稼働中のコンテナのプロセスに入るものであって、止まっているものについては再度スタートしてあげる必要があります。

パターン2:それ以外の場合

docker psでCOMMANDが/bin/bash以外の場合はどうでしょうか。

>docker ps -a
CONTAINER ID        IMAGE               COMMAND              CREATED             STATUS                      PORTS                NAMES
205974b714d2        httpd               "httpd-foreground"   20 hours ago        Up 5 seconds                0.0.0.0:80->80/tcp   nostalgic_maxwell

この場合、attachしてもダメです。
稼働中のコンテナのコマンドは"httpd-foreground"であって、シェルではないからです。

こういう時はexecコマンドを使って新たにプロセスを開始します。
/bin/bashを指定してあげるわけです!

>docker exec -it 205974b714d2 /bin/bash

おまけ:稼働中"ではない"コンテナのシェルに入るには

パターン1 そもそもコンテナがない場合

docker run -it 【コンテナID】/bin/bash 

/bin/bashを指定して、イメージから新たに立ち上げてあげればいいですね。

パターン2 コンテナがあるが、Exitedの場合

docker start -ai 【コンテナID】

スタートしてあげる必要がありますね。
もちろんCOMMAND/bin/bashであることが前提となります。

COMMAND/bin/bash以外の場合には、一旦スタートしてその後
docker exec -it XXXXXXXXXX /bin/bash
でシェルに入りましょう!

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

kubernetes on minikubeで、DockerイメージがPullできない件について

Minikubeとは

ローカル環境で(比較的)簡単にkubernetes環境を構築できるツール・仮想環境で、VirtualBoxなどの上で動作します。
詳しくは、公式ドキュメント等をご確認ください

どハマりしたポイント

今回、なんと丸2日ハマった問題への対応策と、何をチェックすべきなのかの備忘録として記載します。
なので、説明不足とかあります。 追記の希望等々はコメント欄までよろしくお願いしますmm

環境

  • Minkube (with inside Docker) feat. Kubernetes
  • MacOS Catalina
  • Docker for MacOS

やりたかった事

  1. Minikubeを起動し、 eval $(minikube docker-env)
  2. ローカルディスク上にDockerイメージをbuild(docker build ...)
  3. kubectl apply ...
  4. kubectl get podでRunningになってればOK

のはずだった。。。 PullError なんてみたくなたかった

うまく行かないポイント

1. docker build ... -t xxxxのxxxxが間違ってる

  • これは単純でわかりやすいです。名前が不一致なので探しきれず、リモートを探すもさらに見つけきれず。。。って言う状態
  • pruneコマンド等でイメージを削除して作り直しましょう

2. imagePullPolicyがAlwaysになってる

  • kubectl edit deployment xxxxxでチェック
  • これがAlwaysになってると、ローカルのDockerイメージが古いかもしれないからと、リモートを検索してエラーになります。
  • IfNotPresent (ローカルになかったらリモート探してね)。またはNever(ローカルが正だ!)を設定する事で回避

3. eval $(minikube docker-env)が無効になってる

  • 意外と気づかない
  • 契機はわからないけど、PCがスリープに入ったりしたくらいでも無効になる模様
  • イメージ名も合ってる、IfNotPresentも設定したのにPullエラーが起きる様なら疑うべき
  • 対応策
    1. kubectl delete deployment xxxx
    2. kubectl delete service xxxx
    3. kubectl get pod / service / deploymentでお亡くなりになっている事を確認
    4. minikube stop
    5. minikube start
    6. eval $(minikube docker-env)
    7. kubectl apply ...

終わり

仮想化技術はトラブルにずば抜けて強く、チーム開発でも導入にもたつかないメリットがとっても魅力的ですが、目に見えないしログにも現れない’繋がり’の問題が発生すると厄介だなと思い知らされました。。。

この問題は今回の様にローカルの開発環境だからこそ発生する問題で、通常通り稼働しているkubernetes環境では発生しないものと思われます。

トラブルシューティングツールか何か合ったら嬉しいけど、、、大体のケースでは上手くいくはずって事なのかな

作成した方がいたらコメントで教えてください(笑

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

今更Dockerの便利さに気がついたけど、最終的によく分からなくなった話

はじめに

現実逃避のために、自分の遊びの環境をsakura vpsに作ることにしたがsakuraでは環境が保存できないらしいので、代案手段として、Dockerでやってみようと思い立ちました。

やりたいこと

Docker上に、Nginxのサーバーを立ててSSL化する。

いつもの更地化。

  • OSを再インストールして、更地にします。(CentOS7) 2019-11-26_14h26_00.png

Dockerのインストール

個人利用なので、無償版のDocker-ceを入れます。

  • コンソールからログイン用のユーザを作って。
  • 忘れちゃいけない。yumを更新。
$sudo yum update
  • 必要なパッケージをインストールします。
$sudo yum install -y yum-utils device-mapper-persistent-data lvm2
  • Docker CEをインストールするため、yumのリポジトリを追加します。
$sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
  • Dockerインストール

最新バージョン入れます。

$sudo yum install docker-ce

インストール完了。

Dockerを起動します。

$ sudo systemctl start docker

Nginxを入れて起動してみる。

Nginxイメージを使ってコンテナを起動します、外部からはポート:8080でアクセスするようにして起動。

$ sudo docker container run -itd --name nginx-container -p 8080:80 nginx

ブラウザからURLを指定して、起動を確認。
2019-11-26_16h28_58.png

Dockerから状態をみるとNginxが起動してることが確認が取れました。

$ sudo docker ps -a
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                  NAMES
7a5f699f72db        nginx               "nginx -g 'daemon of…"   6 hours ago         Up 6 hours          0.0.0.0:8080->80/tcp   nginx-container

DockerのNginxを停止する。

$ docker stop  7a5f699f72db
7a5f699f72db
$ docker ps -a
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS                     PORTS               NAMES
7a5f699f72db        nginx               "nginx -g 'daemon of…"   6 hours ago         Exited (0) 2 seconds ago                       nginx-container

STATUSがExitedで停止してます。ブラウザから見ても停止していました。
ここでは、[CONTAINER ID]を指定しましたが、[NAMES]を指定してしても停止できます。

Dockerからnginxのコンテナを削除する。

$ docker rm 7a5f699f72db
7a5f699f72db
$ docker ps -a
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES

停止と同様に、ここでは[CONTAINER ID]を指定しましたが、[NAMES]を指定してしても削除できます。

DockerからNginxのイメージを削除する。

現在のイメージを確認します。

$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
nginx               latest              231d40e811cd        3 days ago          126MB

Nginxのイメージが存在しているので、削除します。

$ docker rmi 231d40e811cd
Untagged: nginx:latest
Untagged: nginx@sha256:50cf965a6e08ec5784009d0fccb380fc479826b6e0e65684d9879170a9df8566
Deleted: sha256:231d40e811cd970168fb0c4770f2161aa30b9ba6fe8e68527504df69643aa145
Deleted: sha256:dc8adf8fa0fc82a56c32efac9d0da5f84153888317c88ab55123d9e71777bc62
Deleted: sha256:77fcff986d3b13762e4777046b9210a109fda20cb261bd3bbe5d7161d4e73c8e
Deleted: sha256:831c5620387fb9efec59fc82a42b948546c6be601e3ab34a87108ecf852aa15f

$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE

イメージが削除されました。
ここでは[IMAGE ID]を指定しましたが、[REPOSITORY]を指定してしても削除できます。

綺麗になりました。めっちゃ便利です。
なんで今まで使ってなかったのか悔やまれます。

Dockerの構造

Dockerの構造として、イメージとコンテナという概念があり、イメージが概念で、コンテナが実態のようです。
クラスとインスタンスの関係と同様のようです。
無題 2.png

NginxをHTTPS化したなと思って、Docker Hubを見てたら、Certbotはあったのですが、連携の仕方がわからず、なんかいい方法が無いものかと思って調べていたら、Docker HubにCentOSのイメージもあることに気が付きました。つまり、今までDockerの上にNginxやなんやらかんやらの載せて管理するのかなと思っていましたが、Dockerの上にOS載せて、その上でNginxを載せられる?もしそうならもっと楽に構築できるじゃないかと思い立ちました。
Dockerのイメージ変更.png

CentOSを起動する

イメージを取得する。

$ sudo docker pull centos:centos7
centos7: Pulling from library/centos
ab5ef0e58194: Pull complete
Digest: sha256:4a701376d03f6b39b8c2a8f4a8e499441b0d567f9ab9d58e4991de4472fb813c
Status: Downloaded newer image for centos:centos7
docker.io/library/centos:centos7
$sudo docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
centos              centos7             5e35e350aded        2 weeks ago         203MB

イメージを起動して、ログインする。(exitするとDockerのCentOSから脱出できます。)

$ sudo docker run -it -d --name centos7 5e35e350aded
docker ps -a
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                  NAMES
9927906158a2        centos              "/bin/bash"              10 seconds ago      Up 9 seconds                               centos7
$ sudo docker exec -it centos7 /bin/bash
[root@9927906158a2 /]# ls
bin  etc   lib    lost+found  mnt  proc  run   srv  tmp  var
dev  home  lib64  media       opt  root  sbin  sys  usr

コンテナのCentOSにNginxをインストールする

Docker上のCentOS上のyumを更新します。
Nginxをインストールします。NginxのInstallation instructionsにしたがってインストールします。

$ yum update
$ yum install yum-utils

/etc/yum.repos.d/nginx.repoファイルを作成します。

$ vi /etc/yum.repos.d/nginx.repo

記載内容

nginx.repo
[nginx-stable]
name=nginx stable repo
baseurl=http://nginx.org/packages/centos/$releasever/$basearch/
gpgcheck=1
enabled=1
gpgkey=https://nginx.org/keys/nginx_signing.key
module_hotfixes=true

[nginx-mainline]
name=nginx mainline repo
baseurl=http://nginx.org/packages/mainline/centos/$releasever/$basearch/
gpgcheck=1
enabled=0
gpgkey=https://nginx.org/keys/nginx_signing.key
module_hotfixes=true

Nginxのインストールし、起動しようとした処で、D-Busの権限エラーが発生しました。

$ yum-config-manager --enable nginx-mainline
$ yum install nginx
$ systemctl start nginx
Failed to get D-Bus connection: Operation not permitted

調べて見ると、 docker runする際に、--privilegedのオプションと、公開するにあたってポートの公開が必要なようです。
そこで、今のイメージを保存して、上記の2つのオプションをつけて起動し直すことにしました。
手順としては、今のコンテナを停止させてから、イメージを作成します。

$ sudo docker ps -a
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
4073d82ac893        5e35e350aded        "/bin/bash"         9 minutes ago       Up 9 minutes                            centos7
$ sudo docker stop 4073d82ac893
$ sudo docker ps -a
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS                        PORTS               NAMES
4073d82ac893        5e35e350aded        "/bin/bash"         13 minutes ago      Exited (137) 12 seconds ago                       centos7

イメージのステータスがExitedになり、止まった状態になったので、イメージを作成します。

docker commit NAMES(または、CONTAINER ID) 作成イメージ名

$ sudo docker commit 4073d82ac893 centos7n
$ sudo docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
centos7n            latest              0c30b8b87f37        19 seconds ago      347MB
centos              centos7             5e35e350aded        2 weeks ago         203MB

作成したイメージの先程のオプションつけて起動します。Exitedを起動します。

$ sudo docker run -d --privileged -p 80:80 -p 443:443 centos7n /sbin/init
$ sudo docker ps -a
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS                        PORTS                  NAMES
550d5d2474b9        centos7n            "/sbin/init"        9 seconds ago       Up 8 seconds                  0.0.0.0:80->80/tcp   stupefied_moser
4073d82ac893        5e35e350aded        "/bin/bash"         23 minutes ago      Exited (137) 10 minutes ago                          centos7
$ sudo docker exec -it stupefied_moser /bin/bash
$ systemctl start nginx

今度は無事起動でき、外部からアクセスできことを確認しました。次にやりたいのはHTTPS化です。
以前、使ったLet's EncryptからSSL証明書を取得してHTTPS化します。

HTTPS化する。

Certbotをインストールします。certbotの手順を見ながらインストールします。

$ yum -y install yum-utils
$ yum-config-manager --enable rhui-REGION-rhel-server-extras rhui-REGION-rhel-server-optional
$ yum install certbot python2-certbot-nginx
Loaded plugins: fastestmirror, ovl
Loading mirror speeds from cached hostfile
 * base: ftp-srv2.kddilabs.jp
 * extras: ftp-srv2.kddilabs.jp
 * updates: ftp-srv2.kddilabs.jp
No package certbot available.
No package python2-certbot-nginx available.
Error: Nothing to do

なんかエラーが出ました。パッケージがないとのこと。
epelを追加して、再度インストールでうまくいきました。

$ yum install epel-release
$ yum install certbot python2-certbot-nginx

証明書を取得します。
管理者のメールアドレスが求められるので入力します。

$ certbot --nginx
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Plugins selected: Authenticator nginx, Installer nginx
Enter email address (used for urgent renewal and security notices) (Enter 'c' to

ライセンスに同意するか聞かれるので、Aを入力します。

Starting new HTTPS connection (1): acme-v02.api.letsencrypt.org

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Please read the Terms of Service at
https://letsencrypt.org/documents/LE-SA-v1.2-November-15-2017.pdf. You must
agree in order to register with the ACME server at
https://acme-v02.api.letsencrypt.org/directory
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
(A)gree/(C)ancel:

メールアドレスをシェアするとか言ってるので、Nを入力します。

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Would you be willing to share your email address with the Electronic Frontier
Foundation, a founding partner of the Let's Encrypt project and the non-profit
organization that develops Certbot? We'd like to send you email about our work
encrypting the web, EFF news, campaigns, and ways to support digital freedom.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

ドメイン名を入力します。(Let's Encryptでドメイン指定する際はwwwをつけた状態にします。)

No names were found in your configuration files. Please enter in your domain

エラーが発生した。

Obtaining a new certificate
Resetting dropped connection: acme-v02.api.letsencrypt.org
Performing the following challenges:
http-01 challenge for ドメイン名
Using default address 80 for authentication.
Waiting for verification...
Challenge failed for domain ドメイン名
http-01 challenge for ドメイン名
Cleaning up challenges
Some challenges have failed.

IMPORTANT NOTES:
 - The following errors were reported by the server:

   Domain: ドメイン名
   Type:   connection
   Detail: Fetching
   http://ドメイン名/.well-known/acme-challenge/dlxpxIAt0fMvR90N0bBWnTTcAvu_XTHSV6jyKOC872M:
   Connection refused

   To fix these errors, please make sure that your domain name was
   entered correctly and the DNS A/AAAA record(s) for that domain
   contain(s) the right IP address. Additionally, please check that
   your computer has a publicly routable IP address and that no
   firewalls are preventing the server from communicating with the
   client. If you're using the webroot plugin, you should also verify
   that you are serving files from the webroot path you provided.
 - Your account credentials have been saved in your Certbot
   configuration directory at /etc/letsencrypt. You should make a
   secure backup of this folder now. This configuration directory will
   also contain certificates and private keys obtained by Certbot so
   making regular backups of this folder is ideal.

[http://ドメイン名/.well-known/acme-challenge/]にアクセスしようとしてるようなので、Nginx設定ファイルの編集して場所を設定します。

以下にアクセスの場所を作ります。
/var/www/acme-challenge

設定ファイルをバックアップを作成し、編集します。
console
$ cp /etc/nginx/conf.d/default.conf default.conf.org
$ vi /etc/nginx/conf.d/default.conf

[location ^~ /.well-known/acme-challenge/]の振替先を追加

default.conf
server {
    listen       80;
    server_name  localhost;

    #charset koi8-r;
    #access_log  /var/log/nginx/host.access.log  main;

    location / {
        root   /usr/share/nginx/html;
        index  index.html index.htm;
    }

    # 追加した
    location ^~ /.well-known/acme-challenge/ {
        root /var/www/acme-challenge;
    }

    #error_page  404              /404.html;

    # redirect server error pages to the static page /50x.html
    #
    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
-- INSERT --

Nginxをリスタートします。certbotを再び起動しドメイン名を登録します。

$ systemctl restart nginx
$ certbot --nginx
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Plugins selected: Authenticator nginx, Installer nginx
Starting new HTTPS connection (1): acme-v02.api.letsencrypt.org
No names were found in your configuration files. Please enter in your domain
name(s) (comma and/or space separated)  (Enter 'c' to cancel): ドメイン名
Obtaining a new certificate
Performing the following challenges:
http-01 challenge for ドメイン名
Using default address 80 for authentication.
Waiting for verification...
Cleaning up challenges
Could not automatically find a matching server block for ドメイン名. Set the `server_name` directive to use the Nginx installer.

IMPORTANT NOTES:
 - Unable to install the certificate
 - Congratulations! Your certificate and chain have been saved at:
   /etc/letsencrypt/live/ドメイン名/fullchain.pem
   Your key file has been saved at:
   /etc/letsencrypt/live/ドメイン名/privkey.pem
   Your cert will expire on 2020-02-27. To obtain a new or tweaked
   version of this certificate in the future, simply run certbot again
   with the "certonly" option. To non-interactively renew *all* of
   your certificates, run "certbot renew"

成功したら、再び設定ファイルを編集し、SSL接続の設定を追加します。

listen 443 ssl;
ssl_certificate /etc/letsencrypt/live/ドメイン名/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/ドメイン名/privkey.pem;

$ vi /etc/nginx/conf.d/default.conf
default.conf
server {
    listen       80;
    server_name  localhost;

    # 追加した
    listen 443 ssl;
    ssl_certificate     /etc/letsencrypt/live/ドメイン名/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/ドメイン名/privkey.pem;

    #charset koi8-r;
    #access_log  /var/log/nginx/host.access.log  main;

    location / {
        root   /usr/share/nginx/html;
        index  index.html index.htm;
    }

    location ^~ /.well-known/acme-challenge/ {
        root /var/www/acme-challenge;
    }


    #error_page  404              /404.html;

Nginxを再起動して、HTTPSで接続の確認します。

systemctl restart nginx

無事HTTPSで接続できました。
2019-11-29_16h00_31.png

が、なんか違う…こんなに面倒な設定は、Dockerに任せたいはずだった。

自分がやりたかったことを一回整理

  • 環境を破壊したりするので、状態を保存して、適度なタイミングで復帰したい。
  • 自家製のコンテンツも環境と同じように切り替えられるようにしたい。
  • ログは、コンテンツ扱いにすると消えるので、指定の場所に保存したい。

もう一回構成を含めて検討してきます。

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

Kubernetes v1.16

環境構築

クラスター構成管理ツールをインストール

kind, minikube, microkube などから、どれかを選択するか、
GKE, AKS, EKS などから、どれかを選ぶ。
Kubernetes v1.16 が選べるものの中から選ぶ。

kind

iKind() {
  curl -sLo kind https://github.com/kubernetes-sigs/kind/releases/download/$(
    git ls-remote --tags --refs https://github.com/kubernetes-sigs/kind.git | tail -1 | awk -F/ '{print $3}'
  )/kind-$(uname)-amd64
  chmod +x kind
  sudo mv kind /usr/local/bin
} && iKind

minikube

xdg-open https://kubernetes.io/ja/docs/tasks/tools/install-minikube/

microk8s

xdg-open https://microk8s.io/docs/

Docker をインストール

iDocker() {
  curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
  sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
  sudo apt install -y docker-ce apt-transport-https
  sudo usermod -aG docker $(whoami)
} && iDocker
$SHELL -l

kubectl をインストール

iKubectl() {
  curl -LO https://storage.googleapis.com/kubernetes-release/release/$(
    curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt
  )/bin/linux/amd64/kubectl
  chmod +x kubectl
  sudo mv kubectl /usr/local/bin
} && iKubectl

nodejs をインストール

iNodejs() {
  curl -o- https://raw.githubusercontent.com/creationix/nvm/$(
    git ls-remote --tags --refs https://github.com/nvm-sh/nvm.git | grep -P 'v0.\d\d' | tail -1 | awk -F/ '{print $3}'
  )/install.sh | bash
  . ~/.bashrc
  LATEST=$(nvm ls-remote | grep 'Latest LTS' | tail -1 | awk '{print $1}')
  nvm install $LATEST
  nvm alias default $LATEST
  nvm use $LATEST
  node --version
  npm --version
  id
} && iNodejs

環境変数

example() {
  export KINDNAME=example
  export KINDHTTP=80
  export KINDHTTPS=443
  export KINDCONFIG=multinode.yaml
  curl -O https://raw.githubusercontent.com/jobscale/_/master/cloud/k8s/multinode.yaml
  exposedDeployment() {
    kubectl create deployment $1 --image $2
    kubectl expose deployment $1 --name $1 --type LoadBalancer --port $3 --target-port $4
    [[ "$5" != "0" ]] && kubectl autoscale deployment $1 --cpu-percent 50 --min $5 --max 20
  }
} && example

クラスターを作成

createKind() {
  rm -fr ~/.kube/config
  ln -sfn kind-config-$KINDNAME ~/.kube/config
  kind create cluster --config $KINDCONFIG --name $KINDNAME
} && createKind

全てのノードが Ready になるのを待つ

kubectl get nodes -o wide -w

全てのポッドが Running になるのを待つ

kubectl get pods -A -o wide -w

名前空間を設定

createNamespace() {
  kubectl create namespace standard
  kubectl config set-context $(kubectl config current-context) --namespace standard
  kubectl apply -f https://raw.githubusercontent.com/jobscale/_/master/cloud/k8s/limitrange-limits.yaml
} && createNamespace

ノードのリソースを確認

kubectl describe nodes

メトリクスサーバーをデプロイ

metricsServer() {
  git clone https://github.com/kubernetes-sigs/metrics-server.git
  kubectl apply -f metrics-server/deploy/1.8+
} && metricsServer

ロードバランサーをデプロイ

metalLB() {
  METAL_VERSION=$(git ls-remote --tags --refs https://github.com/danderson/metallb.git | tail -1 | awk -F/ '{print $3}')
  echo -e "\n MetalLB ${METAL_VERSION} \n"
  kubectl apply -f https://raw.githubusercontent.com/google/metallb/${METAL_VERSION}/manifests/metallb.yaml
  kubectl apply -f https://git.io/km-config.yaml
} && metalLB

イングレスをデプロイ

ingressNginx() {
  kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/master/deploy/static/mandatory.yaml
  kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/master/deploy/static/provider/cloud-generic.yaml
} && ingressNginx

管理ダッシュボードをデプロイ

adminDashboard() {
  kubectl apply -f https://raw.githubusercontent.com/jobscale/_/master/cloud/k8s/kubernetes-dashboard.yaml
  kubectl apply -f https://raw.githubusercontent.com/jobscale/_/master/cloud/k8s/admin-user-service-account.yaml
} && adminDashboard

アプリをデプロイ

アプリをデプロイ

wetty をデプロイ

exposedDeployment wetty jobscale/wetty 443 3000 0

laravel をデプロイ

exposedDeployment laravel jobscale/laravel 80 80 3

ラーメンタイマーをデプロイ

exposedDeployment ramen-timer jobscale/ramen-timer 80 80 3

wordpress をデプロイ

exposedDeployment wordpress jobscale/wordpress 80 80 3

django をデプロイ

exposedDeployment django jobscale/django 80 80 3

dokuwiki をデプロイ

exposedDeployment dokuwiki jobscale/dokuwiki 80 80 3

tomcat をデプロイ

exposedDeployment tomcat tomcat 80 8080 3

ポート転送

sudo -E kubectl port-forward --address 0.0.0.0 svc/tomcat 80:80
xdg-open http://127.0.0.1

管理ダッシュボード

トークンの表示

token() {
  kubectl -n kube-system describe secret $(
    kubectl -n kube-system get secret | grep admin-user | awk '{print $1}'
  )
} && token

ポート転送

sudo -E kubectl -n kubernetes-dashboard port-forward --address 0.0.0.0 svc/kubernetes-dashboard 443:443

google-chrome-stable

google-chrome-stable https://127.0.0.1

おわりに

いらないやつもありますが、あえて書きました。
各自、自分の環境に合わせて読みかえてください。

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

verdaccio + Docker + GCE でprivateなnpmレジストリを立てる

概要

社内のいくつかのアプリに共通で組み込むため、npmライブラリを作ったものの、
npmにpublishして全世界に公開する訳にもいかないので、verdaccioを使って社内に公開可能なnpmレジストリを作成する

verdaccio

ローカルにnpmのレジストリを構築するためのツール。
docker-examplesdocker run するだけで構築可能なサンプルが豊富にあるため、
今回はこの中から Docker + Apache + Verdaccio を選択してGCE上に構築していく

構築手順

1. GCEインスタンスの起動

無料枠内で収めたいので、とりあえず1番小さいインスタンス
ディストリビューションはUbuntuを選択した

  • MachineType: f1-micro
  • Image: Ubuntu 18.04 LTS
  • Allow HTTP traffic
gcloud beta compute --project=<your_project_id> instances create verdaccio \
--zone=asia-northeast1-b --machine-type=f1-micro --tags=http-server,https-server \
--image=ubuntu-1804-bionic-v20191113 --image-project=ubuntu-os-cloud \
--boot-disk-size=10GB --boot-disk-type=pd-standard --boot-disk-device-name=verdaccio

GCEが起動したらSSH接続し、以降の作業はインスタンス内で実施する

2. docker + docker-compose をインストールする

3. verdaccioのサンプルをcloneし、docker-composeする

$> git clone https://github.com/verdaccio/docker-examples.git
$> cd docker-examples/apache-verdaccio/
$> sudo docker-compose up -d
.
.
.
Creating verdaccio ... done
Creating apache-verdaccio_apacheproxy_1 ... done

4. 起動に成功したらブラウザからアクセスしてみる

  • GCEインスタンスに割り当てられたExternal IPをクリックしてブラウザでアクセス image.png

httpsだとアクセスできないので、httpに修正してアクセスする

  • こんな感じの画面が表示されたら成功

image.png

運用手順

  • ここからはローカルPCに戻って作業する

1. npmライブラリをpublishしてみる

  • ライブラリのdistフォルダ内で以下を実行してpublishする
$> npm set registry http://<external_ip>
$> npm publish --registry http://<external_ip>

2. レジストリを確認してみる

  • verdaccioに再度アクセスしてみる

  • publishしたライブラリが表示されていれば成功

image.png

image.png

3. ライブラリをインストールしてみる

$> npm install --save my-library --registry http://<external_ip>
  • 正しくライブラリがインストールされたら成功
  • 他の一般公開されているライブラリと同じように扱うことができる
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

kindで軽量テスト用Kubernetesクラスタを作る&運用する時のTIPS

この記事は、Kubernetes3 Advent Calendarの第1日目です。

Kubernetes最軽量構築ツールkindのよさ、初回起動方法、仕組み、TIPS(ホストディレクトリのマウント)、メンテナンス方法を紹介します。仕掛けがわかるととても楽しいですよ。

image.png

kindのよさ

以下のような良さがあるKubernetes最軽量構築ツールです。

  • 軽量 = 前提としてDockerがインストールされたホストが一つあれば済む
  • クリーン = ホスト環境を何も汚さない
  • 作業ほぼゼロ = コマンド一発で作成、削除可能

まずkindを動かしてみるまで

参考になるドキュメント

本家Quick start

kindを動かす環境の前提

  • dockerがインストールされている
  • kubectlがインストールされている

今回は検証のため、Windows上のVirtualBox上のVMでDocker本体を動かして確認しましたが、私自身Ubuntu上でもMacでも同じ構成を作った実績があります。

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

以下はすべてDockerインストール済みのVM上で実行します。

curl -Lo ./kind https://github.com/kubernetes-sigs/kind/releases/download/v0.6.0/kind-$(uname)-amd64
chmod +x ./kind
sudo mv ./kind /usr/bin/kind

クラスタ構築

kind create cluster

image.png

docker上に一つだけコンテナが起動します。

image.png

これは、control-planeというKubernetesのマスターノードに相当するコンテナが起動した状態です。

Kubernetesでは、デフォルト設定でマスターノードにコンテナを配置しないように制御されますので、それ以外のワーカーノードも必要になります。
一度今のクラスタを削除して、再度ワーカーノード込みで作り直してみましょう。

クラスタ削除

kind delete cluster

はい、全部消えました。とてもクリーンです。

ワーカーノードを含めたインストール

kind用の設定ファイルを新規作成し、ワーカーノードを追加することを記載します。

kind.yaml
kind: Cluster
apiVersion: kind.sigs.k8s.io/v1alpha3
nodes:
- role: control-plane
- role: worker

この設定を使ってクラスタを作成します。

kind create cluster --config kind.yaml

image.png

処理の中にJoining worker nodes処理が増えているのがわかります。
コンテナが2つ作成されています。

image.png

kubectlで操作する

クラスタ作成後に表示されるkubectl cluster-infoをたたくと、ちゃんとコンテキストが追加されていることがわかります。

~$ kubectl cluster-info --context kind-kind

Kubernetes master is running at https://127.0.0.1:38921
KubeDNS is running at https://127.0.0.1:38921/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy

ではこのコンテキストを使ってkubectlで操作しましょう。

$ kubectl config use-context kind-kind
Switched to context "kind-kind".

実際にコンテナを起動してみます。

$ kubectl run --generator=run-pod/v1 test --image=nginx
pod/test created

動いたかどうかを確認してみます。

~$ kubectl get po
NAME   READY   STATUS    RESTARTS   AGE
test   1/1     Running   0          2m39s

コンテナの中にも入ってみます。

$ kubectl exec -ti test /bin/bash
root@test:/# ls
bin  boot  dev  etc  home  lib  lib64  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var

コンテナを消してみます。

$ kubectl delete po test
pod "test" deleted

kindの仕組み

2重のコンテナエンジン

Dockerの中にKubernetesのマスターノード(kind-control-plane)やワーカーノード(kind-worker)が起動し、それぞれの中にまたcontainerdというコンテナエンジンが起動する形になっています。

ユーザがKubernetes上に作るPodなどのコンテナは、この内部のcontainerdの中に作られます。だから、クラスタを削除したいときはこのcontainerdをまるごと消せばよく、ホストのDockerを汚さないので非常にクリーンに運用できるわけです。

image.png

安定した構築方法を採用している

Kubernetesの構築の仕方の中では老舗に部類される、安定した構築ツールkubeadmを内部で使っています。
したがって、kubeadmでできることはこのkindでもできるという嬉しさもあります。

kindのTIPS

ホストのディスクをkind上のpodに見せる

kindの仕組みで説明した通り、kindは2重のコンテナエンジン(Docker/containerd)で起動されています。コンテナエンジン自体はホストのディレクトリをコンテナに見せる機能を持っていますが、2重のコンテナエンジンの設定を一括でやってくれるわけではありません。
したがって、ホストのファイルをkind上のコンテナに見せるには、以下のような2重の設定が必要になります。

  • kind自体が使っているdockerにホストのディレクトリAをBという名前でマウントする設定をする
  • kind内部のcontainerdに、BというディレクトリをCという名前でマウントする

では、ホスト上の/mnt/test/というディレクトリをコンテナに見せてみます。
/mnt/test/はこうなっています。

$ ls /mnt/test/
myfile.txt

このmyfile.txtをPodに見せるのがゴールです。

ではまずkindの設定です。extraMountsというフィールドで設定します。

kind2.yaml
kind: Cluster
apiVersion: kind.sigs.k8s.io/v1alpha3
nodes:
- role: control-plane
- role: worker
  extraMounts:
  - containerPath: /test
    hostPath: /mnt/test

次にコンテナの設定です。

container.yaml
apiVersion: v1
kind: Pod
metadata:
  name: nginx
spec:
  containers:
  - image: nginx
    name: nginx
    volumeMounts:
    - mountPath: /test
      name: test-volume
  volumes:
  - name: test-volume
    hostPath:
      path: /test
      type: Directory

それぞれは以下の①②に対応しています。

image.png

クラスタの作り直し

kind create cluster --config kind2.yaml

確認

$ docker exec -it kind-worker ls /test
myfile.txt
$ kubectl apply -f container.yaml
pod/nginx created
$ kubectl exec pod/nginx -it ls /test
myfile.txt

ちゃんとPodからホスト上のmyfile.txtが見えました。
アプリのログなどのファイルを永続化したい場合に、このテクニックでホストのディレクトリに書き込むことで、kindやその上のPodが消えてもファイルを残すことができます。

ホストのディスクをkind上のapi-server等に見せる

api-serverなどKubernetes自身が持つサービスにもホストのディスクをマウントすることができます。api-serverなどはコンテナとして起動されるという仕組みになっているので、今回もやはり前節と同じで、2重コンテナ構造のため2階層にディレクトリをマウントするという設定がそれぞれ必要となります。

ただし、今回はkind起動時の設定ファイルkind3.yamlのみで設定可能です。理由は、kubeadmを利用しているkindでは、そのapi-serverなどへの設定も同じファイルで記述できるためです。

kind3.yaml
kind: Cluster
apiVersion: kind.sigs.k8s.io/v1alpha3
nodes:
- role: control-plane
  extraMounts:
  - containerPath: /test
    hostPath: /mnt/test
- role: worker
kubeadmConfigPatches:
- |
  apiVersion: kubeadm.k8s.io/v1beta2
  kind: ClusterConfiguration
  metadata:
    name: config
  apiServer:
    extraVolumes:
    - name: "test"
      hostPath: /test
      mountPath: /test

ポイントは2つ。

  • まず、api-servercontrol-planeというノードに配置されますので、今回はextraMountscontrol-planeに記載します。。
  • 次にこのKubeadmConfigPatchの部分です。ここにkubeadmへの設定の差分が書けるというのがkindの嬉しさの一つでもあります。ディレクトリをマウントする設定のフィールドは、extraVolumesです。このお作法は、kubeadmのドキュメントを参考にできます。

さて再掲ですが、ホストのディレクトリはこうなっています。

$ ls /mnt/test/
myfile.txt

では、実際にクラスタを構築して確認してみます。

$ kind create cluster --config kind3.yaml

確認

$ kubectl exec -n kube-system kube-apiserver-kind-control-plane
myfile.txt

ちゃんとマウントされましたね。api-serverからaudit logなどをホストに書き出したいときにぴったりです。
もうこんなに設定ファイル一個だけでかんたんにKubernetesクラスタがいじれると、楽しくてしょうがなくなりますね

もしVM上のDockerがこんなエラーをはいたら

docker: Error response from daemon: cgroups: cannot find cgroup mount destination: unknown.

VMのcgroupの使われ方が少し変なようなので、以下をたたくと回避できます。

sudo mkdir /sys/fs/cgroup/systemd
sudo mount -t cgroup -o none,name=systemd cgroup /sys/fs/cgroup/systemd

kindのメンテナンス

コンテナイメージを追加する

ホスト上でアプリを開発して、アプリを同梱したコンテナをPodとしてkind上で実行したいときがあります。しかし、kind内のcontainerdはホストのdockerのイメージを直接読めませんので、そのままではPodが起動しません。
ホストのイメージをkind内のcontainerdにアップロードする便利コマンドがあるので、それを使います。

kind load docker-image <image:tag>

注意が必要なのは、これをすることによってcontrol-planeやworkerすべての内部にイメージがアップロードされるため、ノード数分ディスクを消費することです。

もし特定のノード、たとえばワーカーだけにイメージをアップロードしたい場合は以下のようにします。

kind load docker-image <image:tag> --nodes=<nodename>

複数のノードにアップロードする場合には、ノード名をコンマで連結して入れればよいです。

kind load docker-image <image:tag> --nodes=<nodename1>,<nodename2>

コンテナイメージを消す

ディスクを圧迫してきたら、使わないコンテナイメージを削除することをお勧めします。

まず確認する

$ docker exec kind-control-plane crictl images
IMAGE                                TAG                 IMAGE ID            SIZE
k8s.gcr.io/coredns                   1.6.2               bf261d1579144       44.2MB
k8s.gcr.io/etcd                      3.3.15-0            b2756210eeabf       248MB
k8s.gcr.io/kube-apiserver            v1.16.3             6750aac993260       185MB
k8s.gcr.io/kube-controller-manager   v1.16.3             74308f46cf2d3       128MB
k8s.gcr.io/kube-proxy                v1.16.3             2388adcec48cf       103MB
k8s.gcr.io/kube-scheduler            v1.16.3             0f7390a0dfb32       105MB
docker.io/kindest/kindnetd           <none>              aa67fec7d7ef7       32.4MB
k8s.gcr.io/pause                     3.1                 da86e6ba6ca19       746kB
test                                 0.1                 xxxxxxxxxxxxx       1.0GB

見つけたイメージを消す

docker exec kind-control-plane crictl rmi test:0.1

同じことをworkerに対しても実行する

kind-control-planeのところをkind-worker-1などに変えると同じ事が実行できます。

ログを一括して吐き出す

Kubernetes周りのサービス(たとえばapi-server)のログを出力したいとき、便利コマンドが用意されています。

kind export logs

まとめ

kindは軽量、クリーンなKubernetesテスト環境で、とても便利です。さまざまなKubernetes関係の開発者が利用していることも知られています。
TIPSで紹介したように、うまくすればホストのディレクトリもマウントできます。
開発者は「本番系には使わないでね」、と言っていますので、使い方は気を付けたほうがよさそうです。

参考情報

https://qiita.com/it__chago/items/c7d057d968da934199da
https://speakerdeck.com/ytaka23/cloud-native-developer-jp-13th
https://qiita.com/sourjp/items/281d2189516823950291

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

VSCode拡張機能の「Cloud Code」

概要

Googleが提供するVSCodeでkubernetes(GKE)の作成、デプロイなどを簡単に行えるようになるツールです。
日本語ではまだβになっていますが、英語版では正式リリースになっています。
Googleが開発していますが、AWSやAzureのマネージドKubernetesにも対応しているようです。

環境

  • MacOSX
    • Catalina 10.15.1
  • Visual Stadio Code
    • Version: 1.40.2
  • gcloud
    • Google Cloud SDK 271.0.0
    • 認証済み
  • Docker
    • version 19.03.5
  • Skaffold

How to Use

インストールから実際の使い方まで

インストール

Cloud Codeで検索すると出てきます。
(画像はすでにインストール済み)
スクリーンショット 2019-11-29 10.26.53.png

実際の動作


GKEクラスターの作成

コマンドパレットにCloud Codeと入力すると候補が出ます。
Cloud Code:Create GKE clusterを選択します。
スクリーンショット 2019-11-29 10.31.32.png

クラスターの設定画面が表示されるので順次設定していきます。

  • Step1 プロジェクトIDの設定
    クラスターを作成するプロジェクトIDを入力します。

スクリーンショット 2019-11-29 10.36.09.png

  • Step2 リージョン/ゾーンの選択
    リージョンでasia-northeast1を選択します。

スクリーンショット 2019-11-29 10.40.18.png

  • Step3 詳細設定
    クラスターの名前やノード数、ノードインスタンスの種類を設定します。

スクリーンショット 2019-11-29 10.43.32.png

入力したらCreate Clusterボタンを押下してしばらく待ちます。

クラスター作成中・・・
スクリーンショット 2019-11-29 10.50.00.png

クラスター作成完了
スクリーンショット 2019-11-29 10.58.56.png

作成したGKEクラスターの確認

インストールするとアイコンが追加されます。
これでKubernetesのクラスターを確認できます。
スクリーンショット 2019-11-29 12.04.21.png
クラスター作っただけなんで中身は何もありません。

新しいクラスターの作成

Cloud Code: New Applicationを選択すると新しいクラスターが作成できます。
スクリーンショット 2019-11-29 12.13.30.png

なんでもいいんですが、とりあえずGoにしました。
スクリーンショット 2019-11-29 12.16.14.png

ルートディレクトリを決めます。
スクリーンショット 2019-11-29 12.19.13.png

デフォルトでは${HOME}/cloudcode-projects/go-hellow-world-1になっていました。
VSCodeが今開いてるフォルダにしろ
ファイルが作成されます。
スクリーンショット 2019-11-29 12.23.22.png

作成したクラスターの実行

Cloud Code: deployを選択します。
スクリーンショット 2019-11-29 12.45.10.png

ローカルのDockerを利用するためDefaultを選択します。
スクリーンショット 2019-11-29 12.59.36.png

コンテナのビルドやリソースのデプロイが行われます

ビルドデプロイログ
Running: skaffold run --enable-rpc -v info --rpc-http-port 59310 --filename skaffold.yaml
Running: skaffold dev -v info --rpc-http-port 59346 --filename skaffold.yaml
Running: skaffold run --enable-rpc -v info --rpc-http-port 59353 --filename skaffold.yaml
Running: skaffold run --enable-rpc -v info --rpc-http-port 59363 --filename skaffold.yaml
starting gRPC server on port 50051
starting gRPC HTTP server on port 59363
Using kubectl context: docker-desktop
Generating tags...
 - go-hello-world -> go-hello-world:latest
Checking cache...
Tags generated in 96.073µs
 - go-hello-world: Not found. Building
Found [docker-desktop] context, using local docker daemon.
Building [go-hello-world]...
Cache check complete in 2.724232184s
Sending build context to Docker daemon  57.34kB

Step 1/8 : FROM golang:1.13
1.13: Pulling from library/golang
16ea0e8c8879: Pulling fs layer
50024b0106d5: Pulling fs layer
ff95660c6937: Pulling fs layer
9c7d0e5c0bc2: Pulling fs layer
2a19d2e6789c: Pulling fs layer
bd9cab5aeb31: Pulling fs layer
0ed236c95063: Pulling fs layer
2a19d2e6789c: Waiting
bd9cab5aeb31: Waiting
0ed236c95063: Waiting
9c7d0e5c0bc2: Waiting
ff95660c6937: Verifying Checksum
ff95660c6937: Download complete
50024b0106d5: Verifying Checksum
50024b0106d5: Download complete
9c7d0e5c0bc2: Verifying Checksum
9c7d0e5c0bc2: Download complete
16ea0e8c8879: Verifying Checksum
16ea0e8c8879: Download complete
0ed236c95063: Verifying Checksum
0ed236c95063: Download complete
16ea0e8c8879: Pull complete
50024b0106d5: Pull complete
ff95660c6937: Pull complete
bd9cab5aeb31: Verifying Checksum
bd9cab5aeb31: Download complete
2a19d2e6789c: Verifying Checksum
2a19d2e6789c: Download complete
9c7d0e5c0bc2: Pull complete
2a19d2e6789c: Pull complete
bd9cab5aeb31: Pull complete
0ed236c95063: Pull complete
Digest: sha256:f9de064473fb30c66bc0d2ddf2cf9a4a9bd38cbd2c5e59ce3bdf8af7b8747a57
Status: Downloaded newer image for golang:1.13
 ---> a2e245db8bd3
Step 2/8 : RUN go get -u -v github.com/go-delve/delve/cmd/dlv
 ---> Running in e6193a3a906d
[91mgithub.com/go-delve/delve (download)
[0m[91mgithub.com/go-delve/delve/pkg/dwarf/loclist
[0m[91mgithub.com/go-delve/delve/vendor/go.starlark.net/internal/spell
[0m[91mgithub.com/go-delve/delve/vendor/github.com/mattn/go-isatty
[0m[91mgithub.com/go-delve/delve/vendor/gopkg.in/yaml.v2
[0m[91mgithub.com/go-delve/delve/pkg/goversion
[0m[91mgithub.com/go-delve/delve/vendor/golang.org/x/sys/unix
[0m[91mgithub.com/go-delve/delve/vendor/github.com/cosiner/argv
[0m[91mgithub.com/go-delve/delve/pkg/dwarf/util
[0m[91mgithub.com/go-delve/delve/vendor/golang.org/x/arch/arm64/arm64asm
[0m[91mgithub.com/go-delve/delve/pkg/dwarf/op
[0m[91mgithub.com/go-delve/delve/pkg/dwarf/godwarf
[0m[91mgithub.com/go-delve/delve/pkg/dwarf/frame
[0m[91mgithub.com/go-delve/delve/pkg/config
[0m[91mgithub.com/go-delve/delve/pkg/dwarf/line
[0m[91mgithub.com/go-delve/delve/pkg/dwarf/reader
[0m[91mgithub.com/go-delve/delve/vendor/golang.org/x/arch/x86/x86asm
[0m[91mgithub.com/go-delve/delve/vendor/golang.org/x/crypto/ssh/terminal
[0m[91mgithub.com/go-delve/delve/vendor/github.com/peterh/liner
[0m[91mgithub.com/go-delve/delve/vendor/go.starlark.net/syntax
[0m[91mgithub.com/go-delve/delve/vendor/github.com/sirupsen/logrus
[0m[91mgithub.com/go-delve/delve/pkg/version
[0m[91mgithub.com/go-delve/delve/vendor/github.com/spf13/pflag
[0m[91mgithub.com/go-delve/delve/pkg/logflags
[0m[91mgithub.com/go-delve/delve/vendor/go.starlark.net/resolve
[0m[91mgithub.com/go-delve/delve/pkg/proc
[0m[91mgithub.com/go-delve/delve/vendor/go.starlark.net/internal/compile
[0m[91mgithub.com/go-delve/delve/vendor/github.com/spf13/cobra
[0m[91mgithub.com/go-delve/delve/vendor/go.starlark.net/starlark
[0m[91mgithub.com/go-delve/delve/pkg/proc/winutil
[0m[91mgithub.com/go-delve/delve/pkg/proc/linutil
[0m[91mgithub.com/go-delve/delve/service/api
[0m[91mgithub.com/go-delve/delve/pkg/proc/core/minidump
[0m[91mgithub.com/go-delve/delve/pkg/proc/gdbserial
[0m[91mgithub.com/go-delve/delve/service
[0m[91mgithub.com/go-delve/delve/pkg/proc/core
[0m[91mgithub.com/go-delve/delve/pkg/proc/native
[0m[91mgithub.com/go-delve/delve/service/debugger
[0m[91mgithub.com/go-delve/delve/service/rpc1
[0m[91mgithub.com/go-delve/delve/service/rpc2
[0m[91mgithub.com/go-delve/delve/service/rpccommon
[0m[91mgithub.com/go-delve/delve/pkg/terminal/starbind
[0m[91mgithub.com/go-delve/delve/pkg/terminal
[0m[91mgithub.com/go-delve/delve/cmd/dlv/cmds
[0m[91mgithub.com/go-delve/delve/cmd/dlv
[0m ---> 9c239a04a8c2
Step 3/8 : WORKDIR /src/hello-world
 ---> Running in 4d80d86a1faa
 ---> d787f60f40eb
Step 4/8 : COPY go.mod go.sum ./
 ---> 8b3ab925a778
Step 5/8 : RUN go mod download
 ---> Running in 28efd6611102
 ---> 1c589d3b4d0a
Step 6/8 : COPY . ./
 ---> 5c6d6d2c96ef
Step 7/8 : RUN go build -o /app -v ./cmd/hello-world
 ---> Running in 5ccba50bc0d6
[91mhello-world/cmd/hello-world
[0m ---> df16dd9586b8
Step 8/8 : ENTRYPOINT ["dlv", "exec", "/app", "--continue", "--accept-multiclient", "--api-version=2", "--headless", "--listen=:3000", "--log"]
 ---> Running in c0e015fa2785
 ---> 836c47e159b2
Successfully built 836c47e159b2
Successfully tagged go-hello-world:latest
Build complete in 54.230696113s
Tags used in deployment:
 - go-hello-world -> go-hello-world:836c47e159b26b333365a1c23efe5ffbf1a1ac4474ef19f94dae8c613a6278ee
   local images can't be referenced by digest. They are tagged and referenced by a unique ID instead
Starting deploy...
 - deployment.apps/go-hello-world created
 - service/go-hello-world-external created
Deploy complete in 618.346232ms
You can also run [skaffold run --tail] to get the logs
There is a new version (1.0.1) of Skaffold available. Download it at https://storage.googleapis.com/skaffold/releases/latest/skaffold-darwin-amd64

Waiting for Deployment 'go-hello-world' to rollout...
Waiting for IP address of Service 'go-hello-world-external'.

Publicly exposed service endpoints in the application:
go-hello-world-external: http://localhost:80


No ingress endpoints found in the application.
Application deployed successfully.

最後にアクセスするためのアドレスが出ます。

go-hello-world-external: http://localhost:80

このURLにアクセスするとデプロイされたアプリにアクセスできます。
スクリーンショット 2019-11-29 13.08.30.png

デプロイ後の構成はKUBERNETES EXPLORERで見ることができます。
スクリーンショット 2019-11-29 13.12.23.png

感想

kubernetesは単純に動作させたりするだけでもそこそこ長い(当社比)yaml書かなければならなかったり、動作後のログや構成なんか見るのも大変なのですが、こうして各モジュールの状態とか簡単に見られるようになります。
k9sというツールもあるみたいなので、そちらの方もチェックしてみようかと思います。

なんだこのアイコンは
k9s

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

DockerでGoのRestAPIを作ってみた(ホットリロード対応)

dockerでGolangのAPIを作りたいと思いましてやってみました。
ちなみに、dockerはホットリロードさせたかったので、docker-composeを使っています。

環境

それぞれのバージョンはこちら。
Dockerについての説明とかは省きます。

$ docker-compose --version
docker-compose version 1.24.1, build 4667896b

$ go version
go version go1.13.4 darwin/amd64

■ echo 4.1.11 (ルーティングが便利になるかな、と)
  公式はこちら https://echo.labstack.com/guide
  以下によると、RestAPIに最適化されているらしー。
   https://rightcode.co.jp/blog/become-engineer/go-flamework

■ oxequa/realize
  ホットリロードを行うために入れてみた。

ファイルの準備

適当なフォルダに以下の3ファイルを用意します。

Docker

Dockerfile

# 公式 golang ランタイムをイメージとして使用
FROM golang:1.13

# ソース入れる用のディレクトリ作成(名前はなんでもいいけど、この後の「app」は全部合わせる)
RUN mkdir /app 

# ソース入ってるのここだよ
WORKDIR /app

# ホスト側のカレントにあるファイルをソースフォルダにコピーするよ
COPY . /app

# echo と oxequa/realize のライブラリ取ってくるよ(ぼちぼち時間かかるよ...)
RUN go get -u github.com/labstack/echo/...
RUN go get github.com/oxequa/realize

docker-compose.yml

docker-compose.yml
version: '3'

services:
  api:
    build: .

    #buildが指定されている場合は作成イメージの名前になる
    #build指定なし:このイメージを元に作成される(ex.mysqlとか)
    image: sample_api:0.1

    # ポートフォワーディング(ホスト側:docker側)
    # ホスト側のブラウザで実行するときは http://localhost:1000 で見れるようになる
    ports:
      - "1000:1323"

    # (ホスト側:docker側)dockerの「/app」フォルダとホストのカレントを繋げるよ。
    # ホストの変更が反映されるようになる
    volumes:
      - .:/app

    # デフォルトのコマンドをこれに変える。ホットリロードを有効にする?
    command: realize start --run --no-config

    tty: true

Golang

server.go

server.go
package main

import (
    "net/http"

    "github.com/labstack/echo"
)

func main() {
    e := echo.New()
    routing(e) 
    e.Logger.Fatal(e.Start(":1323"))
}

/* ルーティングを行う */
func routing(e *echo.Echo) {
    e.GET("/", hello)
    e.GET("/:name", greeting)
}

/* http:/~/ の時 */
func hello(c echo.Context) error {
    return c.JSON(http.StatusOK, map[string]string{"message": "hello"})
}

/* http:/~/(名前) の時 */
func greeting(c echo.Context) error {
    /* c.Param("name") とすることで、URLの:name と対応させて取得 */
    return c.JSON(http.StatusOK, map[string]string{"message": "hello " + c.Param("name")})
}

Docker起動

上記ファイルを保管したフォルダに移動して、コマンド実行
(「-d」オプションつけてバックグラウンド実行しない理由は後述。つけても大丈夫)

 $ docker-compose up

これで、ローカルのブラウザからhttp://localhost:1000ってしたら
{message:hello}が。

http://localhost:1000/taroってしたら
{message:hello taro}って帰ってくるはず。

詰まった時の小ネタ

docker-compose up でやってみる
 どういうことかというと、「-d」を取ることでエラーの内容を教えてくれる。

 あずきはコマンド実行後、ターミナルをそのまま使いたかったので、おまじないのように「-d」をつけてました。
 で、エラーで動かないんだけど(docker ps にあがってこない)なんでか分かんなかった。

 原因は同じフォルダ内に「func main()~」が書かれたファイルがいたってことだった。
 試行錯誤しながら作ってたので、バックグランド実行だと気づかなくて何時間も無駄にした。

 でも、苦労したことは忘れない。きっと。

参考

https://qiita.com/y-ohgi/items/671de11f094f72a058b1
https://www.fox-hound.tech/1179/
https://qiita.com/prgseek/items/e557a371d7bd1f57b9b1
https://qiita.com/TsutomuNakamura/items/7e90e5efb36601c5bc8a
http://docs.docker.jp/engine/articles/dockerfile_best-practice.html
https://qiita.com/zembutsu/items/9e9d80e05e36e882caaa
API関連
https://ken-aio.github.io/post/2019/01/30/golang-echo/

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

【Kotlin for Server-Side】KtorのプロジェクトをDockerで起動する

Kotlin for Server-Side

ServerSideの現状を以下に軽くまとめてみました。

Frameworks for Server-side Development with Kotlin

  • Spring
    • Javaでの有名なWebアプリケーションFramework
  • Vert.x
    • JVM上でReactiveアプリケーションを作成する為のToolkit
  • Ktor
    • JetBrains社が作成しているWebアプリケーションFramework
  • kotlinx.html
    • Kotlin DSL for HTML

今回は上記の中でも Ktor を試して見たいと思います。

:computer:環境構築


Ktor Project Generator で空のプロジェクトを作成し、ダウンロードします。

何も変更しない状態で「Build」を選択しプロジェクトをダウンロードします。
ダウンロードしたプロジェクトのディレクトリ構成は以下の様になってました。

.
├── build.gradle
├── gradle
│   └── wrapper
│       ├── gradle-wrapper.jar
│       └── gradle-wrapper.properties
├── gradle.properties
├── gradlew
├── gradlew.bat
├── resources
│   ├── application.conf
│   └── logback.xml
├── settings.gradle
└── src
    └── Application.kt

※ ダウンロードしたプロジェクトのGradleのVerが 4.10 となっており、後述する Gradle Shadow を使用する際に
Versionが古いと怒られるので gradle-wrapper.properties のgradleのversionを 5.6.4 に変更してます。

また、build.gradleApplication.kt は以下の様になってました。

build.gradle
buildscript {
    repositories {
        jcenter()
    }

    dependencies {
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
    }
}

apply plugin: 'kotlin'
apply plugin: 'application'

group 'com.example'
version '0.0.1-SNAPSHOT'
mainClassName = "io.ktor.server.netty.EngineMain"

sourceSets {
    main.kotlin.srcDirs = main.java.srcDirs = ['src']
    test.kotlin.srcDirs = test.java.srcDirs = ['test']
    main.resources.srcDirs = ['resources']
    test.resources.srcDirs = ['testresources']
}

repositories {
    mavenLocal()
    jcenter()
}

dependencies {
    compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
    compile "io.ktor:ktor-server-netty:$ktor_version"
    compile "ch.qos.logback:logback-classic:$logback_version"
    testCompile "io.ktor:ktor-server-tests:$ktor_version"
}
package com.example

import io.ktor.application.*
import io.ktor.response.*
import io.ktor.request.*

fun main(args: Array<String>): Unit = io.ktor.server.netty.EngineMain.main(args)

@Suppress("unused") // Referenced in application.conf
@kotlin.jvm.JvmOverloads
fun Application.module(testing: Boolean = false) {
}

:pencil: Dockerで起動できる様にする


こちらを参考に環境構築を行います。

Gradle Shadow

Jarを生成してくれる、GradleのPlugin。これを使ってjarを生成しDocker内で起動します。

build.gradle を以下に修正

buildscript {
    repositories {
        jcenter()
        maven {
        url 'https://plugins.gradle.org/m2/'
        }
    }

    dependencies {
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
        classpath "com.github.jengelman.gradle.plugins:shadow:5.2.0"
    }
}

apply plugin: 'kotlin'
apply plugin: 'application'
apply plugin: 'com.github.johnrengelman.shadow'

group 'com.example'
version '0.0.1-SNAPSHOT'
mainClassName = "io.ktor.server.netty.EngineMain"

sourceSets {
    main.kotlin.srcDirs = main.java.srcDirs = ['src']
    test.kotlin.srcDirs = test.java.srcDirs = ['test']
    main.resources.srcDirs = ['resources']
    test.resources.srcDirs = ['testresources']
}

repositories {
    mavenLocal()
    jcenter()
    maven { url "https://dl.bintray.com/kotlin/ktor" }
}

dependencies {
    compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
    compile "io.ktor:ktor-server-netty:$ktor_version"
    compile "ch.qos.logback:logback-classic:$logback_version"
    testCompile "io.ktor:ktor-server-tests:$ktor_version"
}

shadowJar {
    baseName = 'my-application'
    classifier = null
    version = null
}

以下のDockerfileをプロジェクト直下に追加

FROM openjdk:8-jdk

RUN mkdir /app
WORKDIR /app

ADD . $WORKDIR
RUN ./gradlew wrapper # gradle wrapper関連を諸々アップデート

起動時にkickするshellを作成 bin/start_server.sh

#!/bin/bash
set -e
source ~/.bashrc

./gradlew build
cp ./build/libs/my-application.jar .
java -server -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap -XX:InitialRAMFraction=2 -XX:MinRAMFraction=2 -XX:MaxRAMFraction=2 -XX:+UseG1GC -XX:MaxGCPauseMillis=100 -XX:+UseStringDeduplication -jar my-application.jar

docker-compose.yml を以下の内容で作成する。

version: '3'

services:
  app:
    build:
      context: .
      dockerfile: ./Dockerfile
    ports:
      - '8080:8080'
    volumes:
      - .:/app
    command:
      bin/start_server.sh

あとは docker-compose up で起動し、http://0.0.0.0:8080 にアクセスできればOKです。
作成したプロジェクトはこちらにアップしてます。

:bomb: バッドノウハウ


* What went wrong:
A problem occurred evaluating root project 'ktor-demo'.
> Failed to apply plugin [class 'com.github.jengelman.gradle.plugins.shadow.ShadowBasePlugin']
   > This version of Shadow supports Gradle 5.0+ only. Please upgrade.

gradleのバージョンが古い。こちらを参考に、
gradle/wrapper/gradle-wrapper.properties のversionを変更し、./gradlew wrapper を実行する
gradleのversionはこちらで確認できる。


* What went wrong:
Execution failed for task ':compileKotlin'.
> Kotlin could not find the required JDK tools in the Java installation '/usr/lib/jvm/java-1.8-openjdk/jre' used by Gradle. Make sure Gradle is running on a JDK, not JRE.

エラーメッセージの通りで、JREではなくJDKを指定する。
参考のリポジトリのDockerfileが ROM openjdk:8-jre-alpine になっている為、openjdk:8-jdk に修正する。

:link: 参考になったURL


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

WEBアプリケーション開発における旬の技術調査

はじめに

私は、約3年勤めたSIerを退職し、タイへ2ヶ月、オーストラリアへ1年の語学留学へ行ってました。
日本に戻ってきて、カンファレンスに参加したり、元職場の先輩と飲みに行って現状を探ったりしたところ、本当に1年ちょっとしか経ってないの!!?ってぐらい進化していたので、自分用にまとめようと思います。

間違いもたくさんあるので、その都度直していこうと思います。

  • 以下、今回まとめようと思っているもの
    • Docker
    • TypeScript
    • spring-boot
    • React
    • Angular
    • AWS

Docker

本当に衝撃的だった。
過去にアーキテクチャ設計メンバーとして働いていた自分にとって、こんな画期的なもの、がなぜ3年前には存在しなかったのか呪ったほどである。

想像してみてほしい。
開発メンバー50人以上の大規模PJ、フロントエンドとバックエンドで異なるIDEによる開発環境と実行環境、協力会社と社員でセキュリティレベルが異なりダウンロードできないファイル。
地獄である。手間である。平気で1日の工数として割り当てられる。無駄。

それがすべてDockerで解決される。
DockerFile、docker-composeファイルを作成して配布・起動すれば、一発で全く同じ仮想環境が作られる。

TypeScript

ざっくりいうと、静的型付け言語のJavaScriptって感じ?
バックエンドメインで開発してきた自分としては、動的型付け言語の型推測とかは慣れないので助かるし、デバック前に型の不一致によるエラーが検出できるのはメリットだと思う。(行数は増えるけど)
また、babelでEcmaScriptのVersion?を気にしなくていいっていうのも大きいメリットだなって思う。
最終的に実装対象に合わせたJavaScriptに変換されるのも、すっごいJavaっぽい。

spring-boot

Springの設定ファイル不要版?っていう風に聞いた。by元先輩社員
Springで最も理解できないxmlファイルを書かなくていいっていうだけで神に聞こえる。ただ、アノテーションなどの基本的なSpringの機能は同じみたい。まだ、あまり調べていないがすっごくきになるし、早く使ってみたい。

React

Facebookが作ったJavaSpringフレームワークらしい。
対抗馬にAngularがあるが、最近はReactのほうが勢いがあるらしい。by元先輩社員
ただ、先日参加したJJUGではAngularとSpring-bootの開発デモが行われていたし、Angularの方がネットでもよく見るので、少し混乱気味。
実際触ってみて感触を確かめたいので、両方で試作アプリを作ってみる予定。

ざっくり見た感じ、JavaScriptフレームワークでHTMLとJavaScript部分がJavaのインターフェースと実装みたいな関係になったのかなって印象。(器だけ作って中身は別ファイルでって意味)
個人的には大歓迎。
HTML内のidとか要素をわざわざ気にして、JavaScriptで取り出してーってしているの、、、馬鹿らしいなーって思ってたので。

AWS

いたるところで見る。
これも抑えておきたい(抑えておかなければならない)技術の一つなのは間違いない。
ちょっと個人で触ってみるのと、企業で採用するのでは使用する機能や規模が大きく異なる気はするが、とりあえず個人で、無料で使用できる範囲で試してみたいと思う。

まとめ

たった1年ちょっとで旬の変わるIT業界の恐ろしさを肌で感じた。
前職では、目の前にあるタスクをやっていくので精一杯で、カンファレンスに参加したり、技術について勉強したりする余裕がなかったんだけど、この機会にしっかり学んで、一人でもサーバーからフロントエンドまで開発できるようにしとこうと思う。

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

dockerコンテナ内で最新のgitをソースコードからインストールする

はじめに

本記事では、dockerコンテナ内でgitの最新版をソースコードからインストールする方法について説明します。

全体の流れ

以下の7ステップで、gitの最新版をソースコードからインストールします。
以下は作業は全てrootユーザで実施しています。

  1. apt-getをupdateとupgradeする
  2. コンテナに最初から入っているgitをアンインストールする
  3. ビルドに必要なパッケージをインストールする
  4. gitのソースコードを公式リポジトリからダウンロードする
  5. ビルドする
  6. gitのパスを通す
  7. 動作確認

apt-getをupdateとupgradeする

既に実施していれば以下は不要です。

# apt-get update
# apt-get upgrade

コンテナに最初から入っているgitをアンインストールする

# apt-get remove git

ビルドに必要なパッケージをインストールする

# apt-get install libcurl4-gnutls-dev libexpat1-dev gettext libz-dev libssl-dev unzip

gitのソースコードを公式リポジトリからダウンロードする

任意の作業ディレクトリにgitの公式リポジトリから最新のソースコードをダウンロードします。
ダウンロードにはwgetコマンドを使います。
本記事では、 /tmp を作業ディレクトリとします。

cd /tmp
# wget https://github.com/git/git/archive/vX.X.X.zip

「X.X.X」には最新のバージョンを入れてください。
最新のバージョンは、公式リポジトリの「branch master」→「tags」から確認できます。

(例)
2019年11月29日時点での最新は、2.24.0なので、
v2.24.0
となります。

ビルドする

# unzip vX.X.X.zip 
# cd git-X.X.X/
# make prefix=/usr/local all
# make prefix=/usr/local install

gitのパスを通す

.bashrcを編集するので、vimをインストールしておきます。
なお、編集さえできればよいので、エディタや手段は何でもよいです。

# apt-get install vim
# cd /
# vim .bash_profile
# source .bash_profile

.bash_profileの中に以下の1行を書き込みます

export PATH=$PATH:/tmp/git

動作確認

適当な場所で以下を入力し、意図するバージョンが返ってくれば成功です

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