20200521のdockerに関する記事は17件です。

Dockerコンテナ内のRで図をプロットする

はじめに

R の tidyverse パッケージ群を含む docker イメージとして rocker/tidyverse が有名です。ただし、これらのイメージは(恐らく)RStudio を使用する前提で作成されています。

私は RStudio を使用しておらず、以下のような経緯もあってコンテナ内の R でプロットを表示させる方法を調べてみました。

  1. R のパッケージのバージョンを固定したい
  2. tidyverse パッケージ群のインストールは時間がかかる
  3. rockerjp/tidyverse を使用すれば、自分で大量のパッケージをインストールしなくてすむため便利だろう
  4. コンテナ内の R でプロットを表示させたい

参考URL

これらのリンク先の設定と比較すると項目が少ないですが、恐らく使用した R のコンテナ内で設定されているからと思います。

実例その1:r-base イメージの場合

まず、R の公式イメージである r-base で試しました。試行錯誤した後の結果を示します。

設定項目

  • -u docker
    • これを指定しないとプロットが表示されませんでした
  • -e DISPLAY=$DISPLAY および -v /tmp/.X11-unix:/tmp/.X11-unix
    • 参考記事を参照
docker run --rm -it \
  -u docker \
  -e DISPLAY=$DISPLAY \
  -v /tmp/.X11-unix:/tmp/.X11-unix \
    r-base R
boxplot(iris$Sepal.Length ~ iris$Species)

unnamed-chunk-2-1.png

プロットされました。

実例その2:rockerjp/tidyverse イメージの場合

とりあえずチャレンジ

先程と異なり、ユーザー名は rstudio とします(元々のイメージ内で指定されています)。

docker run --rm -it \
  -u rstudio \
  -e DISPLAY=$DISPLAY -v /tmp/.X11-unix:/tmp/.X11-unix \
  rockerjp/tidyverse R
> boxplot(iris$Sepal.Length ~ iris$Species)
Error in (function (display = "", width, height, pointsize, gamma, bg,  : 
  X11 module cannot be loaded
In addition: Warning message:
In grSoftVersion() :
  unable to load shared object '/usr/local/lib/R/modules//R_X11.so':
  libXt.so.6: cannot open shared object file: No such file or directory

このようにエラーが出てしまい、プロットされませんでした。
libXt.so.6 が不足しているようです。

libxt6 パッケージを追加したイメージの場合

そこで、以下のような Docker ファイルを用意しました。

FROM rockerjp/tidyverse:latest

RUN apt-get update \
    && apt-get install -y libxt6

ビルドします。イメージ名は仮に mytidyverse とします。

docker build -t mytidyverse .
docker run --rm -it \
  -u rstudio \
  -e DISPLAY=$DISPLAY -v /tmp/.X11-unix:/tmp/.X11-unix \
  mytidyverse R
boxplot(iris$Sepal.Length ~ iris$Species)

図は省略しますが今度はプロットされました。

tidyverse がインストールされているので ggplot2 でも試してみます。

library(ggplot2)
ggplot(iris, aes(Species, Sepal.Length)) +
    geom_boxplot()

unnamed-chunk-9-1.png

これもプロットされました。

おわりに

rockerjp/tidyverse のコンテナ内で R を起動して、プロットすることに成功しました。これで自分で tidyverse パッケージ群をインストールせずに済みます。

公式イメージで libxt6 パッケージを追加してもらえれたら今回のような手間は減るのですが……(他力本願)

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

Goで書いたサーバーをCloud Runで動かす

Goで書いたサーバーをデプロイする先を探していて、Google App Engine(SE)ArukasHerokuなどを試してきたが、今回は新たにCloud Runを試したのでその手順をここに記しておく。

前提

  • Google Cloud Platformのアカウント(請求先の登録が必要)
  • Google Cloud Platformのプロジェクト作成
  • Google Cloud SDKのインストール
  • Dockerがインストールされていること+起動していること

この辺りの説明は省略。

Cloud Runの有効化(初回のみ)

enable cloud run

Cloud consoleでCloud Runのページを開き、Cloud Runを有効にしておく。フルマネージド環境で使う場合は「CLOUD RUNの使用を開始する」を選べばOK。

検証用サーバープログラムの準備

リクエストがくるとHello worldを返すだけのシンプルなHTTPサーバーを用意した。
Cloud Runで動かすコンテナは、環境変数PORTで指定されたポートでListenできるようにしておかなければいけないので、Listenするポート番号を引数で指定できるようにしている。(環境変数を直接参照しても良いが)

https://cloud.google.com/run/docs/reference/container-contract?hl=ja#port

main.go
package main

import (
    "fmt"
    "net/http"
    "os"
    "strconv"
)

func handler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Hello world\n")
}

func main() {
    port, _ := strconv.Atoi(os.Args[1])
    fmt.Printf("Starting server at Port %d", port)
    http.HandleFunc("/", handler)
    http.ListenAndServe(fmt.Sprintf(":%d", port), nil)
}


動作確認のため、ローカル3000版ポート指定で起動してみる。

go run main.go 3000

別のShellからcurlでリクエストを送ってみて、Hello worldが返ってきたらOK。サーバーを停止する。

curl http://localhost:3000/

Dockerイメージの作成

Dockerfile
FROM golang:latest as builder

ENV CGO_ENABLED=0
ENV GOOS=linux
ENV GOARCH=amd64
WORKDIR /go/src/github.com/yokoe/cloudrunexp
COPY . .
RUN go build main.go

# runtime image
FROM alpine
COPY --from=builder /go/src/github.com/yokoe/cloudrunexp /app

CMD /app/main $PORT

alpineをベースに、最小限のサーバーのイメージを作る。サーバー起動時の引数に環境変数$PORTを渡すことで、$PORT番号のポートでListenするようにする。

docker build -t cloudrunexp .

ビルドに失敗する場合はDockerが起動しているか確認する。

docker run -e "PORT=3000" -p 3000:3000 -t cloudrunexp

動作確認用に$PORT=3000で、サーバーを起動してみる。ポートフォワーディングをして、http://localhost:3000/でコンテナ内のサーバーにアクセスし、レスポンスが返ってくることを確認する。

curl http://localhost:3000/

Container RegistryへのPush

作成したイメージをGoogle Container Registry(GCR)にPushする。

Container Registry APIの有効化(初回のみ)

Container Registry APIを有効にしたことがない場合は、コンソールから有効にしておく。

gcrapi.png

Docker認証ヘルパーの有効化(初回のみ)

gcloud auth login
gcloud auth configure-docker

gcloudで認証し、Docker認証ヘルパーを有効にすることで、docker pushコマンドでGCRにイメージをPushできるようになる。

https://cloud.google.com/container-registry/docs/advanced-authentication?hl=ja

タグをつける

docker tag cloudrunexp gcr.io/go-server-exp/cloudrunexp:firstbuild

アップデートしていく時に、どのイメージがどのビルドかわかるようにタグをつけておく。今回は最初なのでfirstbuildというタグをつけた。

GCRにイメージをPushする

docker push gcr.io/go-server-exp/cloudrunexp:firstbuild

docker pushコマンドを使って、firstbuildタグのついたイメージをGCRにPushする。

スクリーンショット 2020-05-21 22.06.49.png

Pushに成功すると、Cloud consoleのContainer Registry画面でPushされたイメージのリストを確認できる。

サービスの作成

Container Registryでイメージの確認

スクリーンショット 2020-05-21 22.19.29.png

Container Registryのイメージのメニューから「Cloud Runへデプロイ」を選ぶと、Cloud Runのサービス作成画面が開く。

サービスの作成

create service

自分で管理しているGKE上で動かしたいわけではないので、フルマネージドを選ぶ。また、パブリック(誰でもアクセスできる)なウェブサーバーとして動かしたいので「未認証の呼び出しを許可」を選択。

スクリーンショット 2020-05-21 22.22.36.png

コンテナイメージが、先ほどContainer Registryで選んだイメージになっていることを確認して「作成」する。

スクリーンショット 2020-05-21 22.23.47.png

作成完了すると、早速利用可能となり、自動生成されたURLが表示される。アクセスしてみて、Hello worldが表示されれば成功。カスタムドメインを割り当てたい場合は、URL横のinfoボタンから、「MANAGE CUSTOM DOMAINS」に進み、マッピングを追加すれば簡単に利用できた。

なお、今回はサービスの設定、デプロイはWeb UI経由で行ったがCI経由、Terraformなどを使っても操作できる。

感想

GKEでDeploymentやService、Ingressの設定をするのに比べると非常に楽だった。カスタムドメインの割当ても簡単だったし、ちょっとしたHTTPサーバーを立ち上げるのには結構良さそう。

HerokuのDocker Deployも同様に簡単で良かったが、GCSやCloudSQLなどGCP系のサービスと組み合わせる場合はCloud Runも良さそう。

参考

https://cloud.google.com/container-registry/docs/quickstart?hl=ja

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

Laravel勉強 その1 サーバのセットアップ、DockerでのLaravel環境構築

目的

  • 勉強のため、Laravel 7.xの公式ドキュメントを読み解いていく。
  • 今回はそのための準備として、Dockerを使用して環境を構築する。

EC2の構築~セットアップ

今回は勉強なので、AWSの無料枠を利用する。
インスタンスタイプはt2.micro、OSはAmazon Linux2で作成。

参考

EC2(Amazon-Linux-2)にNginxを入れてブラウザで確認するまで2018冬 [画像で解説] Nginx編

rootでのPWログインをON

セキュリティ的には微妙なところもあるかもしれないが、いちいちsudo打たなければいけないのも面倒なので。。。

rootのPWを設定

# sudo su
# passwd

sshの設定ファイル変更

# vi /etc/ssh/sshd_config
PermitRootLogin yes
PasswordAuthentication yes

sshd再起動

# systemctl restart sshd

ec2-userの無効化

セキュリティ的に消した方がいいよ、という記事を見つけた。確かに。

# userdel ec2-user

EC2を日本時間と日本語に対応させる

# yum update -y
# timedatectl set-timezone Asia/Tokyo
# localectl set-locale LANG=ja_JP.UTF-8
# localectl set-keymap jp106

パッケージを自動更新させる

yum-cronのインストール

# yum install yum-cron -y

yum-cronとは?
 yum-cron パッケージは、アップデートを自動的に確認し、ダウンロードし、適用するための便利な方法を提供します。
 パッケージをインストールするとすぐに yum-cron パッケージの cron ジョブが有効になり、特別な設定は必要ありません。通常の日次 cron ジョブの実行時に、このジョブが実行します。

yum-cronを編集

  # vi /etc/yum/yum-cron.conf
/etc/yum/yum-cron.conf
apply_updates = yes

cronの自動起動をON

# systemctl status yum-cron
# systemctl start yum-cron
# systemctl enable yum-cron

Laravel開発環境構築

参考

Docker × PHP7.3 × Laravel環境作ってみた

Docker・Docker Composeのインストール

Dockerインストール
# amazon-linux-extras install docker

Dockerサービス起動
# systemctl start docker.service

サービスの起動確認
# systemctl status docker

Dockerの自動起動設定
# systemctl enable docker

試しにDockerコマンド実行
# docker info
Docker Composeインストール
# curl -L https://github.com/docker/compose/releases/download/1.25.4/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose
# chmod +x /usr/local/bin/docker-compose

Docker Composeのインストールを確認
# docker-compose --version
docker-compose version 1.25.4, build 8d51620a

ディレクトリ作成

以下の構成を想定しています。
project
├ docker-compose.yml
docker
│  ├ db
│  │  ├ data
│  │  ├ sql
│  │  └ my.cnf
│  ├ nginx
│  │  ├ default.conf
│  ├ php
│  │  ├ php.ini
│  │  └ Dockerfile
server

とのこと。(太字はディレクトリ)

# cd /var
# mkdir project
# mkdir project/docker project/server
# mkdir project/docker/{php,nginx,db}
# ll -d `find ./project`
drwxr-xr-x 4 root root 34 May 21 19:08 ./project
drwxr-xr-x 4 root root 30 May 21 19:08 ./project/docker
drwxr-xr-x 2 root root  6 May 21 19:08 ./project/docker/db
drwxr-xr-x 2 root root  6 May 21 19:08 ./project/docker/nginx
drwxr-xr-x 2 root root  6 May 21 19:08 ./project/docker/php
drwxr-xr-x 2 root root  6 May 21 19:08 ./project/server

別にvarじゃなくてもいいけど、なんとなくvarで。

swap領域の有効化

過去の経験上、これをやらないとこける。

# /bin/dd if=/dev/zero of=/var/swap.1 bs=1M count=1024
1024+0 records in
1024+0 records out
1073741824 bytes (1.1 GB) copied, 13.537 s, 79.3 MB/s

# /sbin/mkswap /var/swap.1
mkswap: /var/swap.1: insecure permissions 0644, 0600 suggested.
Setting up swapspace version 1, size = 1024 MiB (1073737728 bytes)
no label, UUID=8be78b55-de6d-4ed4-9f9a-7a48f3dc30ae

# /sbin/swapon /var/swap.1
swapon: /var/swap.1: insecure permissions 0644, 0600 suggested.

docker-compose.yml作成

docker-composer.yml
version: '3'

services:
  php:
    container_name: php
    build: ./docker/php
    volumes:
      - ./server:/var/www

  nginx:
    image: nginx
    container_name: nginx
    ports:
      - 80:80
    volumes:
      - ./server:/var/www
      - ./docker/nginx/default.conf:/etc/nginx/conf.d/default.conf
    depends_on:
      - php

  db:
    image: mysql:5.7
    container_name: db
    environment:
      MYSQL_ROOT_PASSWORD: ****(パスワードを記載しておく)
      MYSQL_DATABASE: test_db
      TZ: 'Asia/Tokyo'
    command: mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
    volumes:
      - ./docker/db/data:/var/lib/mysql
      - ./docker/db/my.cnf:/etc/mysql/conf.d/my.cnf
      - ./docker/db/sql:/docker-entrypoint-initdb.d
    ports:
      - 3306:3306

Dockerfile作成

phpコンテナの構築時に、以下のDockerfileを使用する。

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
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer

ENV COMPOSER_ALLOW_SUPERUSER 1

ENV COMPOSER_HOME /composer

ENV PATH $PATH:/composer/vendor/bin

WORKDIR /var/www

RUN composer global require "laravel/installer"

各行の処理については、Dockerfileリファレンスを参照。

php.ini作成

phpの設定ファイル。

php.ini
[Date]
date.timezone = "Asia/Tokyo"
[mbstring]
mbstring.internal_encoding = "UTF-8"
mbstring.language = "Japanese"

default.conf作成

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:9000;
    fastcgi_index  index.php;

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

php.ini作成

mysqlの設定ファイル。

my.cnf
[mysqld]
character-set-server=utf8mb4
collation-server=utf8mb4_unicode_ci

[client]
default-character-set=utf8mb4

Laravelプロジェクト作成

docker起動
# docker-compose up -d
(略)
Creating php     ... done
Creating db-host ... done
Creating nginx   ... done

起動の確認
# docker ps -a
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                               NAMES
9fdcb0da6745        nginx               "nginx -g 'daemon of…"   2 minutes ago       Up 2 minutes        0.0.0.0:80->80/tcp                  nginx
e1394b974a68        project_php         "docker-php-entrypoi…"   3 minutes ago       Up 2 minutes        9000/tcp                            php
cbc7a83db192        mysql:5.7           "docker-entrypoint.s…"   3 minutes ago       Up 2 minutes        0.0.0.0:3306->3306/tcp, 33060/tcp   db

phpコンテナに入る
# docker-compose exec php bash
root@eec8f5f9ebe2:/var/www#

Laravelプロジェクト作成
# laravel new
(略)
Application ready! Build something amazing.

Laravelまわりのディレクトリのパーミッション変更

# sudo chmod 2775 /var/project/server
# find /var/project/server -type d -exec sudo chmod 2775 {} +
# find /var/project/server -type f -exec sudo chmod 0664 {} +
# sudo chmod -R 777 /var/project/server/storage/
# sudo chmod -R 777 /var/project/server/bootstrap/cache

セキュリティグループの編集

今回環境を作成したEC2について、PORT:80からのインバウンド通信を許可するように編集する。

動作確認

動作確認をして、Laravelの白い画面が出てくればOK。

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

100日後に1人前になる新人エンジニア(1日目)

100日後に1人前になる新人エンジニア(1日目)

どうもこんばんは今日が1日目になります。
ちなみに昨日から始まっていますのでよかったらこちらからどうぞ
100日後に1人前になる新人エンジニア(0日目)

今日やったこと

本日より本格的に業務に取り組みました。
さっそくissueに取りかかり頑張るぞ...
と思ってましたが、

Dockerの使い方がわからん...
ということでまずそこで悪戦苦闘した前半。

その後Railsでフォームの改修に取り組みましたが...
プロジェクトのフォルダ多すぎて何が何だかわからんぞ:joy:

という始まり方でした。

少しずつ理解を深めていきアーキテクチャとかクラスごとの繋がりも分かってきたら
面白いんだろうなあって思っています。

Dockerあれこれ

今日はここからでした。いくつか使ったコマンドを

dockerコマンド
docker-compose build    #サービス(web,DB)のビルドを実行
docker-compose up       #コンテナを作成して、起動
docker-compose down     #コンテナを停止し、そのコンテナとネットワークを削除
docker-compose ps       #コンテナの一覧を表示 コンテナの状態がわかるよ

きっと他にもいっぱいあるので少しずつキャパを増やして行こう。

あれ..ファイルの変更時に情報が反映されないぞ...そんな時は

dockerコマンド
docker-compose build    #サービス(web,DB)のビルドを実行
docker-compose up -d    #デタッチド・モード:バックグラウンドでコンテナを実行し、新しいコンテナ名を表示

なるほどねー。これで大丈夫みたい。

なんで情報の変更を反映時はバックグラウンドで起動するんだろ?
あと変更した時にこれを行わないで画面リロードで良い時もあったけど何が違うんだ?
本日の疑問点です。明日までに解決します。

そしてコツコツとドキュメントを読んでいきます

--追記--
docker-compose.yml or ./docker/rails/Dockerfile に書かれている内容に変更があったら 再度buildする必要あり。疑問点は無事解決

Rails あれこれ

プロジェクトがとても大きい...
どんなroutesになってるのかこんがらがっちまったよ

・僕が知っていた方法...
 config/routes見れば全部書いてあるだろ!!
 って思ってたけど大変そうでした。よって以下の方法で確認

$ rails routes

他にも色々とあるようでして
Tipsを先輩社員の方からいただきました。

・localhost:3000/rails/info
 すげーroutes全部出てきたしちゃんと検索もできる!!

・docker-compose upから
ログが出力されているのでどのコントローラを使って
画面が遷移しているのかが確認できるよ

ありがたきアドバイスでした。

デバッグって

コードの途中にraiseを入れると...
アプリを立ち上げるとエラー画面になっているけど
その画面でコンソール?を打ち込むと各情報が得られました。すげー

例えばform controllerのコードの途中にraiseを入れておいて
アプリを立ち上げ→エラー画面からコンソールを入力
params と入力してみるとどんな値が送られているのかちゃんと確認できました。
明日から使います。ありがたや...:joy_cat:

他にもやり方は色々とあるはずなのでそこは確認してみます。

formに関してはまだ完成していないので続きは明日に回します。

今日のさいごに

今日が連載初日でした。
何事も積み重ねだと思うので一日ひとつづつ。

フィルヒースの言葉をいただきます。
Winning is habitualですね

1人前のエンジニアになるまであと99日

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

Docker ToolboxでのWindows10共有フォルダマウント方法 備忘録

Dockerを使用する際、ホスト⇔コンテナ間でファイルを共有したい時の方法になります。
sshよりローカルでいじりたい!という人向け。

使用ツール

Docker Toolbox v19.03.1
ホストOS:Windows 10 Home
コンテナOS:CentOS

本記事で使用するフォルダ名

ホストOS:folder_host
Dockerマシン:folder_machine
コンテナOS:folder_container

1.VirtualBoxの共有フォルダを設定する

VirtualBox->対象のDockerマシンの設定->共有フォルダー->追加
ホストOS側の共有させたいフォルダ絶対パスと、Dockerマシンに配置されることになる共有フォルダ名を設定する。

ここでは例として、ホストOS側のフォルダ名をfolder_host、Dockerマシンに配置される共有フォルダ名をfolder_machineとする
(Dockerマシンに/folder_machineが作られることになる)

2.Dockerマシンを再起動する(大事)

投稿時は記載し忘たが、Dockerマシンに共有フォルダを認識させるために必要。

再起動
docker-machine restart default

3.マウントを指定してdocker runする

-v Dockerマシンに配置される共有フォルダパス:コンテナで使用したいフォルダパス
例として、コンテナで使用したいフォルダ名をfolder_containerとする。

run例
docker run -d -v /folder_machine:/folder_container centos /sbin/init

あとはrunしたコンテナにexecでアタッチする。
例:docker exec -it <コンテナIDとか名前> /bin/bash

4.ホストOSとコンテナOSでファイルが共有できていることを確認する

ホストOS側のフォルダfolder_hostと、コンテナOS側のフォルダ:folder_containerが共有される。
テストはecho "test" > /folder_container/test.txt

雑記

本記事は備忘録になります。
検索すると色々なページが見つかりますが自分の環境でうまくいかず余計なことをしている気がして、
中でどういう動作をしているかわからないので少しでも動きを理解するために書きました。
Docker for Windowsに適したWindows 10がアップデートされるまでしょうがなくDocker Toolboxを使ってる人はどうぞ。

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

Dockerで構築したLaravel環境に、PHPStormでステップ実行デバッグを仕掛ける

元記事 Windows10 に PHP7+Laravel環境をDockerで構築
← 元元記事 最強のLaravel開発環境をDockerを使って構築する【新編集版】

目的

Dockerで構築したLaravel環境で実行されているPHPコードに対して、
JetBrains社の「PHPStorm」からステップ実行デバッグができるようにしたい。

ステップ実行デバッグとは

  • 今実行されているコードの好きな行で処理を停止させて、その瞬間の変数リストや変数の中身を覗き見する。
  • 1行ずつ実行させて、どこでエラー落ちするか追跡する。

といったことができる、例のアレ↓です。

image.png

概要

今回、PHP定番デバッグツール xdebug を使用して実現していきます。
(Laravel最新版には他にもイケてるデバッグツールがあると思いますが、諸事情で古いバージョンのLaravelを使用しなければならなかったので:sob:

PHPはサーバーサイドプログラミング言語なので、サーバー側に xdebug をインストールしてデバッグ情報を吐き出させるようにしないといけません。
加えて今回は、WindowsやMacの中に構築したDockerコンテナの中でサーバーを動かしているので、ちょっとだけ工夫が必要です。

環境

基本的に、冒頭に書いた元記事および、そのさらに元記事に準じます。
今回わたしのほうでは、Windows / Mac 両方で動作確認できました。

PHPStorm は最新版ならOKかと思います。

方法

サーバー側作業

PHPサーバーに xdebug インストール&有効化する設定を追記する。

PHPサーバーは今回 Docker で構築されるようになっているので、その設定ファイルをいじります。
2行ほど追記します。

docker-laravel\infrastructure\docker\php\Dockerfile
・
・
・
RUN apt-get update && \
  apt-get -y install git libicu-dev libonig-dev libzip-dev unzip locales && \
  apt-get clean && \
  rm -rf /var/lib/apt/lists/* && \
  locale-gen en_US.UTF-8 && \
  localedef -f UTF-8 -i en_US en_US.UTF-8 && \
  mkdir /var/run/php-fpm && \
  docker-php-ext-install intl pdo_mysql mbstring zip bcmath && \
  composer config -g process-timeout 3600 && \
  composer config -g repos.packagist composer https://packagist.org && \
# ↓↓↓ 追記 ↓↓↓
  pecl install xdebug && \
  docker-php-ext-enable xdebug && \
# ↑↑↑ 追記 ↑↑↑
  composer global require hirak/prestissimo
・
・
・

PHPサーバーの php.ini に xdebug 設定を追記する。

php.ini には「host.docker.internalというマシンからのデバッグ通信要求を受け付けてね」という設定を追加します。

host.docker.internalって、そんなアドレスのマシン持ってないぞ…?」
と思うかもですが、ご安心ください。
最近のバージョンの Docker Desktop をお使いであれば、自動でそういう設定をしてくれます。

これは Dockerコンテナ から見た Dockerホスト、つまりお手元の Windows や Mac を指し示すようになっています。
(Dockerコンテナの中からlocalhostとやるとDockerコンテナ自身を指してしまいDockerホストを辿れないので、こういう機能を用意してくれています。)

docker-laravel\infrastructure\docker\php\php.ini



[Assertion]
zend.assertions = 1

[mbstring]
mbstring.language = Japanese

# ↓↓↓ 追記 ↓↓↓
[xdebug]
xdebug.idekey="PHPStorm"
xdebug.remote_host = "host.docker.internal"
xdebug.default_enable = 1
xdebug.remote_autostart = 1
xdebug.remote_connect_back = 0
xdebug.remote_enable = 1
xdebug.remote_handler = "dbgp"
xdebug.remote_port = 9000
# ↑↑↑ 追記 ↑↑↑

サーバー環境を起動しておく。

これは元記事のまま、コマンド実行するだけです。

初めて実行する場合
cd docker-laravel/infrastructure
make create-project
すでにcreate-projectを実行したことがある場合
cd docker-laravel/infrastructure
make remake

コマンドが完了するまでお待ちください。

PHPStorm(クライアント)側作業

PHPStormを起動する。

ここで初めて起動してください。
もしすでに起動してdocker-laravelプロジェクトを開いてしまっていたら、再起動してください。
(Dockerビルド工程でインポートされたPHPライブラリ群がインクルードされていない場合があるので。)

PHPStormで docker-laravel プロジェクトを開く。

image.png

メインメニューから設定画面を開く。

image.png

左メニューからLanguages & FrameworksPHPを開く。

image.png

画面右側の + を押す。

ついでにInclude Path欄に、PHPライブラリ群がずらーっとインクルードされているか念のため確認してください。

image.png

docker-laravel を選択して OK を押す。

今回のプロジェクトのルートディレクトリを丸ごとパスに追加します。

image.png

左メニューからLanguages & FrameworksPHPDebugを開き、xdebugDebug port: 9000 にする。

最初から 9000 だったらそのままでOKです。
次へ進みましょう。

image.png

左メニューからLanguages & FrameworksPHPServersを開き + を押す。

image.png

以下の設定をして、最後に Apply ボタンを押す。

設定項目
Name docker-laravel
Host localhost
Port 9000
Debugger Xdebug
User path mappings チェックON

image.png

終わったら閉じてOKです。

メインウィンドウ右上の Add Configuration... を押す。

image.png

左ツリー上部の + を押して PHP Remote Debug を選ぶ。

image.png

以下の設定をして、最後に Apply ボタンを押す。

設定項目
Name docker-laravel
Filter debug connection by IDE key チェックON
Server docker-laravel
IDE key PHPStorm

image.png

終わったら閉じてOKです。

メインウィンドウ右上の 緑色の昆虫 ボタンを押す。

image.png

そうすると、

  • 緑色の昆虫ボタンの右のほうに赤い■ボタンが点灯します。
  • メインウィンドウ下部にデバッグ情報小窓が展開されます。

image.png

これで、デバッグ中の状態になりました。
デバッグをやめるには赤い■ボタンを押せばOKです。

ブラウザでPHPサーバーにアクセスしてみる。

http://localhost/

image.png

普通なら Laravel のサンプルページが開くはずですが、ずっとアクセス中のように見えます。

実は、デバッガーが割り込んで処理を途中で止めているのです!

デバッグ情報小窓→Debuggerタブを開く。

お、デバッグ情報が来てますね!

ただ、何やらエラーが表示されています。
「サーバーコードと手元のソースファイルが関連付けられていないから追跡できないよ!」と怒られています。
なので、設定してあげましょう。
すぐ下の行に表示されている Click to set up path mappings を押して下さい。

image.png

以下の設定をして OK を押す。

  1. この画面、出現当初はすごくウィンドウが小さいので、広げてあげてください。
  2. Use path mappings にチェックを入れます。
  3. 下部にプロジェクトファイルリストが出てくるので、File path on server: に示されているコードに相当するソースファイルを指定してあげます。

image.png

再度PHPサーバーにアクセスしてみる。

http://localhost/

image.png

あれ、今度はすんなりWEBページが表示されて終わってしまいました。
でも大丈夫、正常です!

ソースコードの該当行にブレークポイントを仕掛けてみる。

このページのソースコードは index.php ですね。
では index.php を開いて、停止させてみたい行の冒頭をクリックしてみてください。
赤い●が点灯します。

image.png

再々度PHPサーバーにアクセスしてみる。

http://localhost/

image.png

来ました!
54行目で処理が一時停止され、その瞬間の変数などが丸見えです!

次のブレークポイントまで進めるならimage.png
次の行、次の行、と一行ずつ進めていくならimage.png

といったことも可能です。

長くなってしまいましたが、今回はここまで。
お疲れ様でした:thumbsup:

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

Mac+Vagrant(CentOS7)+Docker+Jenkinsで優勝してみた

大蛇丸が料理をしたら、っていう発想ができるような人間になっていきたい今日この頃です。

目的

  • 仮想化の新潮流であるコンテナ技術をDockerを用いて理解したい
  • CI/CDツールの環境構築にも使用されているコンテナ技術でJenkinsを構築したい
  • できたらPCのローカル環境は綺麗なままにしたい

ターゲット

  • コンテナ技術を用いてCI/CDツールの導入を考えている方
  • これからDockerを触ってみたい方 ※Dockerの説明は行いません。本記事は環境構築のみです。

参考書籍

Docker/Kubernetes 実践コンテナ開発入門(著者:山田明憲)

設計

当初は参考書籍に記述されているように、Macに直接Dockerをインストールしようとしました。
しかし、Macに直接Dockerを入れてしまうとDockerの複数バージョンを管理するのが難しいです。
また、ローカル環境にDocker資材などが混じり汚れてしまいます。
スクリーンショット 2020-05-20 11.14.41.png
そこで仮想マシンを用意し、その中にDockerをインストールするようにしました。
これにより仮想マシン単位でDockerのバージョン管理ができます。
また、環境構築をソースコード(Vagrantfile)で管理できるため、2回目以降の構築時間短縮にも繋がります。
スクリーンショット 2020-05-20 11.17.58.png

構築

仮想マシンの準備

スクリーンショット 2020-05-20 11.55.21.png

VirtualBoxをインストール

作業環境:ホストOS

仮想化ソフトウェアのVirtualBoxをインストールします。(ホストOSに合わせてpkgをダウンロードしてください。)
※VagrantにはVirtualBoxは含まれていないので別途インストールする必要があります。

参考:MacにVirtualBoxをインストールしてみた

Vagrantをインストール

作業環境:ホストOS

仮想化ソフトウェアのラッパーツールであるVagrantをインストールします。(ホストOSに合わせてdmgをダウンロードしてください。)

仮想マシンを作成

CentOS7のBoxを追加

作業環境:ホストOS

仮想マシンのBox(イメージファイル)を追加します。
今回は公式から提供されているCentOS7のBoxを追加します。

# Vagrantインストール直後
$ vagrant box list
There are no installed boxes! Use `vagrant box add` to add some.

# CentOS7のBoxを追加
$ vagrant box add centos/7
==> box: Loading metadata for box 'centos/7'
    box: URL: https://atlas.hashicorp.com/centos/7
This box can work with multiple providers! The providers that it
can work with are listed below. Please review the list and choose
the provider you will be working with.

1) hyperv
2) libvirt
3) virtualbox
4) vmware_desktop

# 今回はVirtualBoxを使用しているので「3:virtualbox」を選択
Enter your choice: 3
==> box: Adding box 'centos/7' (v1708.01) for provider: virtualbox
    box: Downloading: https://vagrantcloud.com/centos/boxes/7/versions/1708.01/providers/virtualbox.box
==> box: Successfully added box 'centos/7' (v1708.01) for 'virtualbox'!

# Box追加後
$ vagrant box list
centos/7 (virtualbox, 1708.01)

初期設定

作業環境:ホストOS

仮想マシン構築に使用するVagrantfileを作成します。

# 今回はカレントディレクトリ直下に「centos7」というVagrantfileを格納するディレクトリを作成
$ mkdir centos7

# 作成したディレクトリの確認
$ ls -latr | grep centos7
drwxr-xr-x    5 ogino  staff       160  5 19 16:21 centos7

# 作成したディレクトリに移動
$ cd centos7/

# 初期設定を実行
$ vagrant init centos/7
A `Vagrantfile` has been placed in this directory. You are now
ready to `vagrant up` your first virtual environment! Please read
the comments in the Vagrantfile as well as documentation on
`vagrantup.com` for more information on using Vagrant.

# 初期設定によりVagrantfileが作成されたことを確認 ※他のファイルやディレクトリは後ほど説明します
$ ls -latr
total 8
drwxr-xr-x   4 ogino  staff   128  5 10 15:28 .vagrant
drwxr-xr-x   4 ogino  staff   128  5 19 15:56 key
-rw-r--r--   1 ogino  staff  3010  5 19 16:21 Vagrantfile ←VagrantfileができていればOK
drwxr-xr-x   5 ogino  staff   160  5 19 16:21 .
drwxr-xr-x+ 47 ogino  staff  1504  5 20 14:26 ..

# 作成したVagrantfileを元に仮想マシンの状態を確認
$ vagrant status
Current machine states:

default                   not created (virtualbox) ←まだ仮想マシンは作成されていないのでOK

The environment has not yet been created. Run `vagrant up` to
create the environment. If a machine is not created, only the
default provider will be shown. So if a provider is not listed,
then the machine is not created for that environment.

仮想マシンの操作

作業環境:ホストOS

Vagrantfileがあるディレクトリ上から仮想マシンを起動させます。

# Vagrantfileがあるディレクトリにいることを確認
$ pwd
/Users/ogino/centos7

# Vagrantfileがあることを確認
$ ls -latr Vagrantfile
-rw-r--r--  1 ogino  staff  3010  5 19 16:21 Vagrantfile

# 仮想マシンを起動
$ vagrant up
<中略>
==> default: Machine booted and ready! ←仮想マシンが起動したようなのでOK
==> default: Checking for guest additions in VM...
    default: No guest additions were detected on the base box for this VM! Guest
    default: additions are required for forwarded ports, shared folders, host only
    default: networking, and more. If SSH fails on this machine, please install
    default: the guest additions and repackage the box to continue.
    default:
    default: This is not an error message; everything may continue to work properly,
    default: in which case you may ignore this message.
==> default: Configuring and enabling network interfaces...
==> default: Rsyncing folder: /Users/ogino/centos7/ => /vagrant
==> default: Machine already provisioned. Run `vagrant provision` or use the `--provision`
==> default: flag to force provisioning. Provisioners marked to run always will still run.

# 仮想マシンの状態確認
vagrant status
Current machine states:

default                   running (virtualbox) ←起動中になっているのでOK

The VM is running. To stop this VM, you can run `vagrant halt` to
shut it down forcefully, or you can run `vagrant suspend` to simply
suspend the virtual machine. In either case, to restart it again,
simply run `vagrant up`.

次は仮想マシンにSSH接続でログインしてみます。

# 仮想マシンにSSH接続でログイン
$ vagrant ssh
Last login: Tue May 19 06:04:50 2020 from 10.0.2.2

# 仮想マシンのホスト名
$hostname
localhost.localdomain

# ログインユーザは「vagrant」
$ who
vagrant  pts/0        2020-05-20 05:48 (10.0.2.2)

# sudoコマンドも使用可能
$ sudo hostname
localhost.localdomain

# 仮想マシンからログアウト
# exit ←Macなら「control + d」でもログアウトできます
ログアウト
Connection to 127.0.0.1 closed.

最後に仮想マシンを停止させます。

# 仮想マシンを停止
vagrant halt
==> default: Attempting graceful shutdown of VM...

# 仮想マシンが停止していることを確認
vagrant status
Current machine states:

default                   poweroff (virtualbox) ←電源が落ちているのでOK

The VM is powered off. To restart the VM, simply run `vagrant up`

参考:MacにVagrantでCentOS7環境を作成

VirtualBoxのアプリケーションからも作成した仮想マシンを確認することができます。
(今回はVagrantを使用するので、GUIではなくCUIベースで環境構築を進めていきます。)
スクリーンショット 2020-05-20 14.46.22.png

仮想マシンのネットワーク設定

作業環境:ホストOS

Vagrantで作成した仮想マシンのネットワークは、
デフォルトだとホストオンリーアダプタといってホストとゲストの間でしか通信ができません。

それだと何かと不便なので、内部ネットワークを利用してホストとゲスト、ゲスト間同士の通信ができる
プライベートネットワークを設定しておきます。

# 仮想マシンにログイン
$ vagrant ssh
Last login: Tue May 19 06:04:50 2020 from 10.0.2.2

# net-tools系のコマンドは標準でインストールされないためyumを使ってインストール
# 全て「y:Yes」で回答してOK
$ sudo yum install net-tools

# ホストオンリーアダプタ時のipアドレス(et0)を確認
$ ifconfig

# 仮想マシンからログアウト
$ exit
logout
Connection to 127.0.0.1 closed.

# 仮想マシンを停止させる(Vagrantfileの修正内容を反映させるには再起動が必要なため)
$ vagrant halt
==> default: Attempting graceful shutdown of VM...

# Vagrantfileを修正
$ vi Vagrantfile
※修正内容は、以下の行のコメントマーク(#)を外します
# config.vm.network "forwarded_port", guest: 80, host: 8080
# config.vm.network "private_network", ip: "192.168.33.10"

# 仮想マシンを起動
$ vagrant up

# 仮想マシンにSSH接続
$ vagrant ssh
Last login: Wed May 20 06:27:38 2020 from 10.0.2.2

# プライベートネットワーク時のipアドレス(et1)を確認
$ ifconfig

参考:vagrantのネットワークについて

仮想マシンのユーザ設定

今回の環境構成だと、VagrantユーザのUID(1000)とJenkinsユーザのUID(1000)が衝突してしまう問題があります。

それを回避するためにVagrantユーザのUIDを別の値に変更します。

出典:公式 jenkins 配布コンテナは UID 1000 の jenkins ユーザーで動作する
参考:出来るだけ最短で Jenkins 環境を試したい(jenkins container + docker CE + CentOS7 + vagrant)

新規ユーザの作成

作業環境:仮想マシン

# 仮想マシンのPrivate IPアドレスをメモする
# ※新規ユーザで仮想マシンにアクセスする際はVagrantを経由しないためIPアドレスを指定する必要があります
$ ip addr show eth1

# 「USERNAME1」変数に追加するユーザ名を設定
$ USERNAME1=ok2

# ユーザを追加
$ sudo useradd -m -u 1001 -g vagrant $USERNAME1

# 追加したユーザに切り替える
$ sudo su - $USERNAME1

# カレントディレクトリが新規ユーザのホームディレクトリに切り替わっていることを確認
$ pwd
/home/ok2

# 「.ssh」ディレクトリを作成
$ mkdir .ssh

# 「.ssh」ディレクトリのアクセス権限を変更
$ chmod 700 .ssh/

# 新規ユーザからログアウト
$ exit
logout

# 新規ユーザのパスワードを設定
$ sudo passwd $USERNAME1
Changing password for user ok2.
New password:
Retype new password:
passwd: all authentication tokens updated successfully.

# Vagrantユーザからログアウト
$ exit
logout
Connection to 127.0.0.1 closed.
鍵の作成

作業環境:ホストOS

# 公開鍵/秘密鍵を作成
$ ssh-keygen -t rsa -f ./id_rsa

# 公開鍵の中身をメモする ※仮想マシン側にも鍵追加を行うため
$ cat id_rsa.pub
鍵の追加

作業環境:仮想マシン

# Vagrantユーザで仮想マシンにログイン
vagrant ssh
Last login: Wed May 20 09:03:38 2020 from 10.0.2.2

# 「USERNAME1」に追加ユーザ名を設定
$ USERNAME1=ok2

# 追加ユーザに切り替え
$ sudo su - $USERNAME1
Last login: Wed May 20 09:05:30 UTC 2020 on pts/0

# 「.ssh」ディレクトリに移動
$ cd .ssh

# 認証鍵リストにホストOSで作成した公開鍵を追加
$ vi authorized_keys

# 認証鍵リストを確認
$ ls -latr
total 4
drwx------. 3 ok2 vagrant  95 May 20 09:06 ..
-rw-r--r--. 1 ok2 vagrant 403 May 20 09:13 authorized_keys
drwx------. 2 ok2 vagrant  29 May 20 09:13 .

# 認証鍵リストのアクセス権限を変更
$ chmod 600 authorized_keys

# 認証鍵リストのアクセス権限が変更されていることを確認
$ ls -latr
total 4
drwx------. 3 ok2 vagrant  95 May 20 09:06 ..
-rw-------. 1 ok2 vagrant 403 May 20 09:13 authorized_keys
drwx------. 2 ok2 vagrant  29 May 20 09:13 .

# 追加ユーザからログアウト
$ exit
logout

# Vagrantユーザからログアウト
$ exit
logout
Connection to 127.0.0.1 closed.
新規ユーザでのログイン

作業環境:ホストOS

# 「USERNAME1」変数に追加ユーザを設定
$ USERNAME1=ok2

# SSH接続で仮想マシン(Private IPアドレス)に接続
# ※以下は、known_hostsに過去のアクセスログが残っていたためエラー
$ssh -i ./id_rsa $USERNAME1@192.168.33.10
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@    WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!     @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
<中略>
Host key verification failed.

# known_hosts内の情報を全て削除
$ vi /Users/ogino/.ssh/known_hosts

# 再度SSH接続
$ ssh -i ./id_rsa $USERNAME1@192.168.33.10
ユーザUIDの変更

作業環境:仮想マシン

# Vagrantが使用しているIDを確認(1000が指定されていること)
$ sudo id vagrant
uid=1000(vagrant) gid=1000(vagrant) groups=1000(vagrant)

# VagrantのUIDを1002に変更
$ sudo usermod -u 1002 vagrant

# Vagrantが使用しているIDを確認(1002が指定されていること)
$ sudo id vagrant
uid=1002(vagrant) gid=1000(vagrant) groups=1000(vagrant)

# ユーザ情報からも1000が使用されていないことを確認
$ grep 1000 /etc/passwd
vagrant:x:1002:1000:vagrant:/home/vagrant:/bin/bash
ok2:x:1001:1000::/home/ok2:/bin/bash

# 追加ユーザからログアウト
exit

上記の対応をすることで、Jenkins導入後にUIDの衝突を回避することができます。

Dockerの準備

スクリーンショット 2020-05-20 15.51.10.png

Dockerのインストール

作業環境:仮想マシン

コンテナ仮想化ソフトウェアのDockerを仮想マシンにインストールします。

# yumを最新にアップデート
$ sudo yum update ←全て「y:Yes」で回答

# Dockerのインストール
$ sudo yum -y install docker

# Dockerデーモンの起動
$ sudo systemctl start docker

# Dockerのブート時自動起動を設定
$ sudo systemctl enable docker
Created symlink from /etc/systemd/system/multi-user.target.wants/docker.service to /usr/lib/systemd/system/docker.service.

# Dockerのバージョンを確認
$ docker --version
Docker version 1.13.1, build 64e9980/1.13.1 ←無事にインストールできていることを確認

Dockerはインストール時点ではroot権限(sudoをつけて実行)がないと権限エラーが発生します。
毎度 sudo をつけるのは手間なので、実行ユーザをDockerグループに所属させます。

# Vagrantユーザで仮想マシンにログイン
$ vagrant ssh
Last login: Wed May 20 13:18:33 2020 from 10.0.2.2

# インストール時点ではsudoをつけて実行しないと権限エラーが発生
$ docker images
Got permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock: Get http://%2Fvar%2Frun%2Fdocker.sock/v1.26/images/json: dial unix /var/run/docker.sock: connect: permission denied

# Dockerグループを作成
$ sudo groupadd docker

# 現行ユーザをdockerグループに所属
$ sudo gpasswd -a $USER docker
ユーザ vagrant をグループ docker に追加

# dockerデーモンを再起動
$ sudo systemctl restart docker

# ログアウト
$ exit
logout
Connection to 192.168.33.10 closed.

# 再ログイン
$ vagrant ssh
Last login: Wed May 20 13:18:33 2020 from 10.0.2.2

# Dockerコマンドがsudoなしで実行できることを確認
$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE

参考:Dockerコマンドをsudoなしで実行する方法

Docker Composeのインストール

作業環境:仮想マシン

複数のコンテナを使う Docker アプリケーションを定義・実行するツールのDocker Composeをインストールします。
インストールするバージョンについては、公式ガイド を確認して、最新バージョン のものを入れるようにしてください。

# 2020/05/20時点の安定版は1.25.5
sudo curl -L "https://github.com/docker/compose/releases/download/1.25.5/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   638  100   638    0     0   1641      0 --:--:-- --:--:-- --:--:--  1640
100 16.7M  100 16.7M    0     0   785k      0  0:00:21  0:00:21 --:--:-- 1201k

# ダウンロードしたバイナリに、実行可能権限を付与
$ sudo chmod +x /usr/local/bin/docker-compose

# Docker Composeのインストールができたことを確認
$ docker-compose --version
docker-compose version 1.25.5, build 8a1c60f6

参考:Install Docker Compose on CentOS 7

Jenkinsの準備

スクリーンショット 2020-05-20 15.51.27.png

Jenkins(マスタ)コンテナの作成

docker-compose.ymlの作成

作業環境:仮想マシン

docker-compose.ymlは、Dockerイメージをビルドするための情報やコンテナ起動するための情報、
使用するDockerネットワークなどを記載する設定ファイルです。
※書籍にある jenkinsci イメージを使用するとプラグインのインストールでエラーになるので、今回は jenkins イメージを使用します。

docker-compose.yml
version: "3"
services:
  master:
    container_name: master
    image: jenkins/jenkins
    ports:
      - 8080:8080
    volumes:
      - ./jenkins_home:/var/jenkins_home:z

今回はこれをDocker実行環境(仮想マシン内)に作成していきます。

# 上記のdocker-compose.ymlをコピーして貼り付け
$ vi docker-compose.yml

Jenkinsの起動

作業環境:仮想マシン

# コンテナ起動
$ docker-compose up
<中略>
master    | touch: cannot touch '/var/jenkins_home/copy_reference_file.log': Permission denied
master    | Can not write to /var/jenkins_home/copy_reference_file.log. Wrong volume permissions?
master exited with code 1

# マウントしようとしたディレクトリで権限エラーが発生したため、所有ユーザをJenkins(1000)に変更
$ sudo chown 1000 -R jenkins_home/

# 再度コンテナ起動
$ docker-compose up
Starting master ... done
Attaching to master
<中略>
master    |
master    | *************************************************************
master    | *************************************************************
master    | *************************************************************
master    |
master    | Jenkins initial setup is required. An admin user has been created and a password generated.
master    | Please use the following password to proceed to installation:
master    |
master    | <ここに初回ログイン時に求められるパスワードが表示> ←初期設定時に使用するのでメモに残しておきます
master    |
master    | This may also be found at: /var/jenkins_home/secrets/initialAdminPassword
master    |
master    | *************************************************************
master    | *************************************************************
master    | *************************************************************
master    |
<中略>
master    | 2020-05-20 13:51:53.388+0000 [id=19]    INFO    hudson.WebAppMain$3#run: Jenkins is fully up and running ←起動完了

初期設定

作業環境:ホストOS

Jenkins(マスタ)コンテナを起動後に、http://<仮想マシンのPrivate IPアドレス>:8080/ にアクセスします。
コンテナ起動時にコンソールに表示されていたパスワードを入力します。
スクリーンショット 2020-05-20 23.07.38.png
Install suggested plugins を選択しプラグインをインストールします。
スクリーンショット 2020-05-20 23.11.29.png
ユーザ情報を登録します。
スクリーンショット 2020-05-20 23.17.55.png
JenkinsのURLを決められますが、デフォルトのまま次に進みます。
スクリーンショット 2020-05-20 23.18.54.png
セットアップ内容に不備がなければ Start using Jenkin を選択します。
スクリーンショット 2020-05-20 23.19.42.png
JenkinsのTop画面が表示されれば成功です。
スクリーンショット 2020-05-20 23.20.43.png

Jenkins(スレーブ)コンテナの作成

Jenkins(マスタ)コンテナのSSH鍵の作成

作業環境:コンテナ

Jenkinsのマスタ(コンテナ)からスレーブ(コンテナ)へ疎通が必要なため、
接続用に先ほど作成したマスタのSSH鍵を作成します。

# マスタコンテナ起動 ※オプションに -d をつけることでバックグラウンドで実行されます
$ docker-compose up -d
Starting master ... done

# コンテナ内でSSH鍵を作成
$ docker container exec -it master ssh-keygen -t rsa -C ""

作業環境:仮想マシン

コンテナのディレクトリを仮想マシン上にマウントしているので、仮想マシンから作成した公開鍵を確認します。

# 公開鍵を確認
$ sudo cat jenkins_home/.ssh/id_rsa.pub
<表示された公開鍵をメモ>

# 秘密鍵を確認
$ sudo cat jenkins_home/.ssh/id_rsa
<表示された秘密鍵をメモ>

docker-compose.ymlの作成

作業環境:仮想マシン

docker-compose.yml
version: "3"
services:
  master:
    container_name: master
    image: jenkins/jenkins
    ports:
      - 8080:8080
    volumes:
      - ./jenkins_home:/var/jenkins_home:z
    links:
      - slave01

  slave01:
    container_name: slave01
    image: jenkinsci/ssh-slave
    environment:
      - JENKINS_SLAVE_SSH_PUBKEY=<上記でメモした公開鍵>

Jenkins(マスタ)コンテナ作成時に使用したymlファイルに追記します。

# 上記のdocker-compose.ymlをコピーして貼り付け
$ vi docker-compose.yml

Jenkinsの起動

作業環境:仮想マシン

# コンテナ起動
$ docker-compose up -d

# 起動中のコンテナを確認
$ docker-compose ps
 Name                Command               State                 Ports
------------------------------------------------------------------------------------
master    /sbin/tini -- /usr/local/b ...   Up      50000/tcp, 0.0.0.0:8080->8080/tcp
slave01   setup-sshd                       Up      22/tcp ←今回追加したスレーブコンテナがあればOK

追加設定

作業環境:ホストOS

JenkinsのTopページから「Jenkinsの管理 - ノードの管理 - 新規ノード作成」を選択します。
ノード名には「Slave01」、Permanet Agentにはチェックをつけて、OKを選択します。
スクリーンショット 2020-05-20 23.42.41.png
詳細設定の各欄に必要な情報を入力します。
ノード名:slave01
リモートFSルート:/home/jenkins
起動方法:SSH経由〜
ホスト:slave01
認証情報:後ほど作成した認証情報
Host Key Verification Strategy:Non verifying〜
高度な設定 - Javaのパス:/usr/local/openjdk-8/bin/java
スクリーンショット 2020-05-21 0.09.44.png
認証情報の追加ボタンを押して、認証情報を追加します。
種類:SSH〜
ID:slave01
秘密鍵:Jenkins(マスタ)コンテナのSSH秘密鍵
パスフレーズ:SSH鍵作成時に設定したパスワード
スクリーンショット 2020-05-21 0.15.47.png
ノード一覧画面にmasterとslave01が表示されていれば環境構築終了です。
スクリーンショット 2020-05-21 0.18.05.png

最後に

今回はJenkinsの環境構築のみにフォーカスしています。

CI/CDやJenkinsをさらに理解したい場合は、
構築した環境を元に以下のJenkinsの入門チュートリアルなどを実践してみると良いかと思います。
参考:サルでもわかるJenkins入門〜自動テスト環境を構築しよう

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

alpineでbazelをビルドする

背景

  • alpineでbazelを使いたい
    • デフォルトのapkリポジトリに存在しない1
  • Bazeliskはalpineのサポートをしていない
  • コンパイルに時間がかかるのでビルド済みdockerイメージとして使えるようにしたい

コンパイルのためのdocker image

./compile.shでコンパイルしているgitリポジトリがあったので、参考に一部拝借・抜粋してdockerfileにしました。

bazel-build
FROM python:3.8-alpine3.11

ARG bazel_ver=3.1.0
ENV JAVA_HOME=/usr/lib/jvm/default-jvm \
    PATH="$JAVA_HOME/bin:${PATH}" \
    BAZEL_VERSION=3.1.0

RUN apk add --virtual .bazel_build --no-cache g++ gcc \
  bash zip unzip cmake make linux-headers openjdk8 && \
  wget -q "https://github.com/bazelbuild/bazel/releases/download/${BAZEL_VERSION}/bazel-${BAZEL_VERSION}-dist.zip" \
  -O bazel.zip && \
  mkdir "bazel-${BAZEL_VERSION}" && \
  unzip -qd "bazel-${BAZEL_VERSION}" bazel.zip && \
  rm bazel.zip && \
  cd "bazel-${BAZEL_VERSION}" && \
  sed -i -e 's/-classpath/-J-Xmx6096m -J-Xms128m -classpath/g' \
  scripts/bootstrap/compile.sh && \
  EXTRA_BAZEL_ARGS=--host_javabase=@local_jdk//:jdk ./compile.sh && \
  cp -p output/bazel /usr/bin/ && \
  cd ../ && rm -rf "bazel-${BAZEL_VERSION}" && \
  bazel version && \
  apk del --purge .bazel_build

あとはdocker buildしてpushするだけ

注意点

  1. ソースからコンパイルを行う場合は、openjdk11対応していないのでopenjdk8を入れる

  2. sedで置き換えている箇所はJavaのヒープ・メモリのサイズを指定している。指定しないとメモリを食い尽くして途中で落ちるケースがある。

The system is out of resources.
Consult the following stack trace for details.
java.lang.OutOfMemoryError: Java heap space

参考文献


  1. apkのtestingリポジトリに存在するが、導入したいモジュールがこのバージョンに対応してないため使用しない 

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

GitLab Runner+docker でCIがうまく動かないときの対処法メモTips

環境

iMacでgitlab-runner+dockerを動かしてandroidアプリのCI環境を作っています。
gitlab-runnerは社内ランからしかアクセスできない設定で、CI環境の入っているiMacは社内にあります。

うまく動かない事例とそれぞれの対処法

使っていないイメージが多すぎて悪さをしている。

apt-get updateやapt-get installしているところで

Error writing to output file - write (28: No space left on device) [IP: xxx.xxx.xxx.xxx xx]

やら

W: Some index files failed to download. They have been ignored, or old ones used instead.

やら

Reading package lists...
Building dependency tree...
Reading state information...
Package file is not available, but is referred to by another package.
This may mean that the package is missing, has been obsoleted, or
is only available from another source
E: Unable to locate package lib32stdc++6
E: Couldn't find any package by regex 'lib32stdc++6'
E: Unable to locate package lib32z1
E: Unable to locate package aapt
E: Package 'file' has no installation candidate

やら出た時


dockerの動いているマシンのターミナルから

docker image prune

で使っていないイメージを消してやる。

WiFiなど、ネットワーク環境が悪い

サブモジュールなどを手動でgit cloneするところで

$ git clone http://gitlab-ci-token:${CI_JOB_TOKEN}@gitlab.xxxxx/hoge/hugahuga.git hugahuga
Cloning into 'hugahuga'...
error: RPC failed; curl 18 transfer closed with outstanding read data remaining
fatal: the remote end hung up unexpectedly
fatal: early EOF
fatal: index-pack failed

と出た時

→ 
dockerの動いているマシンのWiFiが弱いのが原因っぽい。WiFiをつなぎなおしてやったり、ネットワーク環境を整えてやる。

gitlab-runnerやdockerがうまく動いていない。固まっている。

しょっぱなから

Running with gitlab-runner 12.2.0 (xxxxxxx)
  on iMac-docker xxxxxx
ERROR: Preparation failed: Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running? (executor_docker.go:983:120s)
Will be retried in 3s ...
ERROR: Preparation failed: Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running? (executor_docker.go:983:120s)
Will be retried in 3s ...
ERROR: Preparation failed: Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running? (executor_docker.go:983:120s)
Will be retried in 3s ...
ERROR: Job failed (system failure): Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running? (executor_docker.go:983:120s)

とか出てなにもできずにエラーになるとき

→dockerかgitlab-runnerのどちらかがうまく動いていないので、dockerとgitlab-runnerをそれぞれ再起動してやる。(本格的に固まっていてうんともすんとも言わずマシンの再起動になることも多々あり。)

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

Oracleいろいろ

Docker で Oracle を軽く確認したい場合。
11g であれば、以下ので成功した。
https://registry.hub.docker.com/r/wnameless/oracle-xe-11g-r2

docker run -d -p 49161:1521 -e ORACLE_ALLOW_REMOTE=true -e ORACLE_DISABLE_ASYNCH_IO=true wnameless/oracle-xe-11g-r2

12 は成功したことがない。

docker をクリアする場合、https://docs.docker.com/config/pruning/を参照

まず

docker image prune

To remove all images which are not used by existing containers, use the -a flag:

docker image prune -a

Oracle 良いクライアントソフト(Windowsのみ)

OSqlEdit Oracle SQLエディタ(有料)
http://www.hi-ho.ne.jp/a_ogawa/osqledit/
良い点:操作がいい。data-grid も実用性的。
悪い店:oracle cient をインストールする必要がある。

A5:SQL Mk-2(無料)
https://a5m2.mmatsubara.com/
良い点:oracle cient をインストールする必要なし。直接接続できる。
悪い店:操作感がイマイチ。

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

NestJs + Docker

プロジェクトをインストールして作成

一時的なDockerコンテナーを作成する

Make temporary docker container

sudo docker container run -it -v $(pwd):/app -w /app node:12.13.1-alpine /bin/sh

そのDockerコンテナ内に「nest-cli」をインストールして新しいプロジェクトを作成します

Inside that docker container, install the nest-cli and make a new project

$ npm i -g @nestjs/cli
$ nest new project-name

プロジェクトが作成されたら、プロジェクトの内部に入り、依存関係をインストールします

Once your project is created, get inside the project, and install dependencies

$ cd project
$ npm install
$ npm run start

完了したら、コンテナを終了できます。

Once done, you can exit the container.

プロジェクトファイルの所有者を変更します。

Change the owner od the project files.

sudo chown -R myuser:myuser <project-name>
開発モードを実行するようにDockerを構成する

プロジェクトディレクトリ内に「Dockerfile」を作成します

Inside the project directory, create a Dockerfile

FROM node:12.13.1-alpine 

WORKDIR /app

RUN apk update && \
    apk add git && \
    npm install -g npm && \
    npm install -g @nestjs/cli

ENV HOST 0.0.0.0
EXPOSE 3000

また、 docker-compose.yamlも作成します

and also create a docker-compose.yaml

version: '3'

services:
  nest:
    build: .
    tty: true
    command: npm run start:dev
    volumes:
      - .:/app
    ports:
      - "3000:3000"

作成後、 localhost:3000にアクセスすると、Hello Worldページが表示されます。

Upon creation, you can visit localhost:3000, you should see the Hello World page.

ウォッチをテストするには、 app.services.tsのgetHello()を編集します。編集したら、ページを更新して変更を確認できます。

To test the watch, you can edit the app.services.ts getHello(), once edited, you can refresh the page, to see the change.

新しいコントローラーを作成する
sudo docker-compose exec nest nest generate controller cats

これはプロジェクトの srcディレクトリの中にcatsディレクトリを作成します

This will create a cats directory inside the src directory of the project

私の例をコピーして貼り付けることができます

You can copy-paste my example

import { Controller, Get, Req, Param } from '@nestjs/common';

@Controller('cats')
export class CatsController {
    @Get()
    findAll():Object{
        var test = {id:1,name:"my cat"}
        return test;
    }
    @Get("filter")
    getFilter(@Req() req):Object{
        return {id:req.query.id,name:"my cat id"};
    }
    @Get(':id')
    findOne(@Param() params): string {
    return `This action returns a #${params.id} cat`;
    }
}

localhost:3000/

localhost:3000/filter?id=55

localhost:3000/20

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

Docker で Spring Boot

はじめまして。ドカっとドカ土香 ドカちゃんです♡
今回は、
Springドキュメントのガイド『Docker で Spring Boot』を見ながら進めてたんだけど、所々詰まったから手順を残しておくよ
気になったところはガイドを見てね。

準備ができたら
よーい よーい ドーンだYO!!
image.png

環境

・Windows 10 pro

前提条件

Windows 10でDockerを利用するには「Hyper-V」 が必要だよ。
Windows 10 の Professional 以上のエディションが必要で、Windows 10 Home には対応していないよ。

Hyper-Vを有効化

コントロール パネルを開いて、
プログラムと機能→左の[Windows の機能の有効化または無効化] →[Hyper-V] 選択→[OK]
Windowsの再起動をするよ。

Dockerのインストール

利用するには Docker IDを登録する必要があるよ。
https://hub.docker.com/

ここからインストールしていくよ。
インストールは簡単だったよ。
https://hub.docker.com/?overlay=onboarding
image.png
手順の中でPowerShellが必要になるよ。
インストールできたら、PowerShellを起動してコピペで入力していくだけだよ。
windows PowerShellのインストール

チュートリアルを完了してレジストリができていたらおkだよ。

Spring Boot

準備するもの

  • ファイル編集用にテキストエディターまたは IDE →メモ帳でおk
  • JDK 1.8 以降 →コマンドプロンプトで「jar」と打って使用方法が表示されたらおk インストール方法
  • Gradle 4+ →インストールサイト

PowerShellに次のコードを入力するよ。
git clone https://github.com/spring-guides/gs-spring-boot-docker.git
cd gs-spring-boot-docker/initial

(参考パス、C:ユーザー/ユーザー名/gs-spring-boot-docker/initial/)
src/main/java/hello/Application.javaを開くよ。
「Hello Docker World」を表示させたいなら以下に書き換えるよ。

package hello;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@SpringBootApplication
@RestController
public class Application {

  @RequestMapping("/")
  public String home() {
    return "Hello Docker World";
  }

  public static void main(String[] args) {
    SpringApplication.run(Application.class, args);
  }

}

 
gradlew buildをしていくよ。
Springのガイドでは以下を入力するよう記載されてるけどPowerShelldだとエラーがでるよ。
./gradlew build && java -jar build/libs/gs-spring-boot-docker-0.1.0.jar

エラー.
発生場所 行:1 文字:17
+ ./gradlew build && java -jar build/libs/gs-spring-boot-docker-0.1.0.j ...
+                 ~~
トークン '&&' は、このバージョンでは有効なステートメント区切りではありません。
    + CategoryInfo          : ParserError: (:) [], ParentContainsErrorRecordException
    + FullyQualifiedErrorId : InvalidEndOfLine

「&&」を「;」に書き換えると成功するよ。
./gradlew build ; java -jar build/libs/gs-spring-boot-docker-0.1.0.jar

8080に繋げて「Hello Docker World」が表示されたらOKだよ。
クリック→ http://localhost:8080/

コンテナー化

パス:~/gs-spring-boot-docker/initial/ 配下にDockerfileを作成するよ。
このあとのDockerイメージを作成するのに必要だよ。
拡張子はつけないよ。
①~③パターンあるよ。
初めて作る人は①から試して理解を深めていくといいよ。
面倒なら①だけでもいいし、最適化された③から実施してもいいよ。

①基本

Dockerfile.
FROM openjdk:8-jdk-alpine
ARG JAR_FILE=target/*.jar
COPY ${JAR_FILE} app.jar
ENTRYPOINT ["java","-jar","/app.jar"]

JDK8をベースとしているよ。
JAR ファイルを「app.jar」としてコンテナーに入れ、
ENTRYPOINT で実行しているよ。
 

PowerShellで以下を実行してイメージを作成するよ。
docker build --build-arg JAR_FILE=build/libs/*.jar -t springio/gs-spring-boot-docker .
「docker build [OPTION]には一つ以上の引数が必要です~」とメッセージが出てビルドできなかったけど、
端末再起動後に入力したら上手くいったよ。
成功すると以下のようなイメージで表示されるよ。

成功イメージ.
Sending build context to Docker daemon  21.11MB
Step  : FROM openjdk:8-jdk-alpine
 ---> 
Step  : ARG JAR_FILE=target/*.jar
 ---> Using cache
 ---> 
Step 5/6 : COPY ${JAR_FILE} app.jar
COPY failed: no source files were specified

②セキュリティ向上ver.

このままではセキュリティ上問題があるらしく、
非 root ユーザーとしてアプリを実行するように書き換えるよ。

Dockerfile.
FROM openjdk:8-jdk-alpine
RUN addgroup -S spring && adduser -S spring -G spring
USER spring:spring
ARG JAR_FILE=target/*.jar
COPY ${JAR_FILE} app.jar
ENTRYPOINT ["java","-jar","/app.jar"]

イメージ作成
docker build -t springio/gs-spring-boot-docker .

以下で実行すると
docker run -p 8080:8080 springio/gs-spring-boot-docker
ユーザー名を確認できるよ。

アプリケーションの起動ログでユーザー名を確認できます(最初の INFO ログの「開始者」に注意してください)

ログ.
  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v2.2.1.RELEASE)

2020-04-23 07:29:41.729  INFO 1 --- [           main] hello.Application  
...

③パフォーマンス向上ver.

次にSpring Boot fat の特性を活かしてパフォーマンスを上げるよ。また書き換えていくよ。

Spring Boot fat jar ファイルでは依存関係とアプリケーションリソースが明確に分離されており、その事実を使用してパフォーマンスを向上させることができます。

Dockerfile.
FROM openjdk:8-jdk-alpine
RUN addgroup -S spring && adduser -S spring -G spring
USER spring:spring
ARG DEPENDENCY=target/dependency
COPY ${DEPENDENCY}/BOOT-INF/lib /app/lib
COPY ${DEPENDENCY}/META-INF /app/META-INF
COPY ${DEPENDENCY}/BOOT-INF/classes /app
ENTRYPOINT ["java","-cp","app:app/lib/*","hello.Application"]

次にSpringのガイドでは下のコードで実行するように記載してあるけどPowerShellだとエラーが起こるよ。
mkdir -p build/dependency && (cd build/dependency; jar -xf ../libs/*.jar)

エラー.
式の終わりの ')' が存在しません。
発生場所 行:1 文字:30
+ mkdir -p build/dependency && (cd build/dependency; jar -xf ../libs/*. ...
+                              ~~~~~~~~~~~~~~~~~~~~
式は、パイプラインの最初の要素としてのみ許可されます。
発生場所 行:1 文字:73
+ ... r -p build/dependency && (cd build/dependency; jar -xf ../libs/*.jar)
+                                                                         ~
式またはステートメントのトークン ')' を使用できません。

「()」括弧を使えないみたいだよ。
macだと問題ないのかな?
かなり探したけど括弧を置き換えたり、有効化する方法は見つからなかったよ。。
よく見たら括弧外して処理を分けれるよ。
mkdir -p build/dependency
cd .\build\dependency\
jar -xf ../libs/*.jar
dependency配下に、BOOT-INF/lib とアプリケーションクラスを含む BOOT-INF/classes ディレクトリが作成されたよ。

Gradle を使用して Docker イメージを作成する

Gradle で Docker イメージを作成するよ。
ネームスペースにはdockaerのユーザー名を入力するよ。
(ちなみに、個人のイメージはネームスペースが必要で、公式イメージなら必要ないよ。)
リポジトリ名は自由に決めてね。
今回はリポジトリ名を「spring-boot-docker」にしておくよ。
./gradlew bootBuildImage --imageName=ネームスペース/spring-boot-docker

イメージを作成したときのログだよ。
images()の中身はイメージIDだよ。
image.png

「./gradlew」が使えないとエラーが出たら階層を間違えているからGradleのある場所に戻ればいいよ。
例:cd C:\Users\ユーザー名\gs-spring-boot-docker\initial

ローカルで作成したイメージを動かしてみるよ。
docker run -p 8080:8080 -t springio/gs-spring-boot-docker
image.png
image.png

DockerHubへプッシュ

docker imagesで作成したイメージ一覧を確認できるよ。
docker push ユーザー名/spring-boot-dockerでプッシュするよ。

(ちなみに docker rmi コマンドでイメージを削除できるよ。
削除するにはイメージ ID かイメージ名を使うよ。
・docker rmi -f 7d9495d03763
・docker rmi -f spring-boot-docker)

dockaerにログインしてリポジトリ一覧を見るとプッシュしたリポジトリが追加されているよ。
image.png

おわりだYO!!

間違っていたら指摘お願いするよ。

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

DockerのインストールからSpring BootでDockerHubへプッシュまで

はじめまして。ドカっとドカ土香 ドカちゃんです♡
Dockerの使い方あまりわかってないけど、
とりあえず使いながら理解していこうってことで、
Springドキュメントのガイド『Docker で Spring Boot』を見ながら進めてたんだけど、所々詰まったから手順を残しておくよ><
気になったところはガイドを見てね。

準備ができたら
よーい よーい ドーンだYO!!
image.png

環境

・Windows 10 pro

前提条件

Windows 10でDockerを利用するには「Hyper-V」 が必要だよ。
Windows 10 の Professional 以上のエディションが必要で、Windows 10 Home には対応していないよ。

Hyper-Vを有効化

コントロール パネルを開いて、
プログラムと機能→左の[Windows の機能の有効化または無効化] →[Hyper-V] 選択→[OK]
Windowsの再起動をするよ。

Dockerのインストール

利用するには Docker IDを登録する必要があるよ。
https://hub.docker.com/

ここからインストールしていくよ。
インストールは簡単だったよ。
https://hub.docker.com/?overlay=onboarding
image.png
手順の中でPowerShellが必要になるよ。
インストールできたら、PowerShellを起動してコピペで入力していくだけだよ。
windows PowerShellのインストール

チュートリアルを完了してレジストリができていたらおkだよ。

Spring Boot

準備するもの

  • ファイル編集用にテキストエディターまたは IDE →メモ帳でおk
  • JDK 1.8 以降 →コマンドプロンプトで「jar」と打って使用方法が表示されたらおk インストール方法
  • Gradle 4+ →インストールサイト

PowerShellに次のコードを入力するよ。
git clone https://github.com/spring-guides/gs-spring-boot-docker.git
cd gs-spring-boot-docker/initial

(参考パス、C:ユーザー/ユーザー名/gs-spring-boot-docker/initial/)
src/main/java/hello/Application.javaを開くよ。
「Hello Docker World」を表示させたいなら以下に書き換えるよ。

package hello;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@SpringBootApplication
@RestController
public class Application {

  @RequestMapping("/")
  public String home() {
    return "Hello Docker World";
  }

  public static void main(String[] args) {
    SpringApplication.run(Application.class, args);
  }

}

 
gradlew buildをしていくよ。
Springのガイドでは以下を入力するよう記載されてるけどPowerShelldだとエラーがでるよ。
./gradlew build && java -jar build/libs/gs-spring-boot-docker-0.1.0.jar

エラー.html
発生場所 行:1 文字:17
+ ./gradlew build && java -jar build/libs/gs-spring-boot-docker-0.1.0.j ...
+                 ~~
トークン '&&' は、このバージョンでは有効なステートメント区切りではありません。
    + CategoryInfo          : ParserError: (:) [], ParentContainsErrorRecordException
    + FullyQualifiedErrorId : InvalidEndOfLine

「&&」を「;」に書き換えると成功するよ。
./gradlew build ; java -jar build/libs/gs-spring-boot-docker-0.1.0.jar

8080に繋げて「Hello Docker World」が表示されたらOKだよ。
クリック→ http://localhost:8080/

コンテナー化

パス:~/gs-spring-boot-docker/initial/ 配下にDockerfileを作成するよ。
このあとのDockerイメージを作成するのに必要だよ。
拡張子はつけないよ。
①~③パターンあるよ。
初めて作る人は①から試して理解を深めていくといいよ。
面倒なら①だけでもいいし、最適化された③から実施してもいいよ。

①基本

Dockerfile.
FROM openjdk:8-jdk-alpine
ARG JAR_FILE=target/*.jar
COPY ${JAR_FILE} app.jar
ENTRYPOINT ["java","-jar","/app.jar"]

JDK8をベースとしているよ。
JAR ファイルを「app.jar」としてコンテナーに入れ、
ENTRYPOINT で実行しているよ。
 

PowerShellで以下を実行してイメージを作成するよ。
docker build --build-arg JAR_FILE=build/libs/*.jar -t springio/gs-spring-boot-docker .
「docker build [OPTION]には一つ以上の引数が必要です~」とメッセージが出てビルドできなかったけど、
端末再起動後に入力したら上手くいったよ。
成功すると以下のようなイメージで表示されるよ。

成功イメージ.html
Sending build context to Docker daemon  21.11MB
Step  : FROM openjdk:8-jdk-alpine
 ---> 
Step  : ARG JAR_FILE=target/*.jar
 ---> Using cache
 ---> 
Step 5/6 : COPY ${JAR_FILE} app.jar
COPY failed: no source files were specified

②セキュリティ向上ver.

このままではセキュリティ上問題があるらしく、
非 root ユーザーとしてアプリを実行するように書き換えるよ。

Dockerfile.html
FROM openjdk:8-jdk-alpine
RUN addgroup -S spring && adduser -S spring -G spring
USER spring:spring
ARG JAR_FILE=target/*.jar
COPY ${JAR_FILE} app.jar
ENTRYPOINT ["java","-jar","/app.jar"]

イメージ作成
docker build -t springio/gs-spring-boot-docker .

以下で実行すると
docker run -p 8080:8080 springio/gs-spring-boot-docker
ユーザー名を確認できるよ。

アプリケーションの起動ログでユーザー名を確認できます(最初の INFO ログの「開始者」に注意してください)

ログ.html
  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v2.2.1.RELEASE)

2020-04-23 07:29:41.729  INFO 1 --- [           main] hello.Application  
...

③パフォーマンス向上ver.

次にSpring Boot fat の特性を活かしてパフォーマンスを上げるよ。また書き換えていくよ。

Spring Boot fat jar ファイルでは依存関係とアプリケーションリソースが明確に分離されており、その事実を使用してパフォーマンスを向上させることができます。

Dockerfile.
FROM openjdk:8-jdk-alpine
RUN addgroup -S spring && adduser -S spring -G spring
USER spring:spring
ARG DEPENDENCY=target/dependency
COPY ${DEPENDENCY}/BOOT-INF/lib /app/lib
COPY ${DEPENDENCY}/META-INF /app/META-INF
COPY ${DEPENDENCY}/BOOT-INF/classes /app
ENTRYPOINT ["java","-cp","app:app/lib/*","hello.Application"]

次にSpringのガイドでは下のコードで実行するように記載してあるけどPowerShellだとエラーが起こるよ。
mkdir -p build/dependency && (cd build/dependency; jar -xf ../libs/*.jar)

エラー.html
式の終わりの ')' が存在しません。
発生場所 行:1 文字:30
+ mkdir -p build/dependency && (cd build/dependency; jar -xf ../libs/*. ...
+                              ~~~~~~~~~~~~~~~~~~~~
式は、パイプラインの最初の要素としてのみ許可されます。
発生場所 行:1 文字:73
+ ... r -p build/dependency && (cd build/dependency; jar -xf ../libs/*.jar)
+                                                                         ~
式またはステートメントのトークン ')' を使用できません。

「()」括弧を使えないみたいだよ。
macだと問題ないのかな?
かなり探したけど括弧を置き換えたり、有効化する方法は見つからなかったよ。。
よく見たら括弧外して処理を分けれるよ。
mkdir -p build/dependency
cd .\build\dependency\
jar -xf ../libs/*.jar
dependency配下に、BOOT-INF/lib とアプリケーションクラスを含む BOOT-INF/classes ディレクトリが作成されたよ。

Gradle を使用して Docker イメージを作成する

Gradle で Docker イメージを作成するよ。
ネームスペースにはdockaerのユーザー名を入力するよ。
(ちなみに、個人のイメージはネームスペースが必要で、公式イメージなら必要ないよ。)
リポジトリ名は自由に決めてね。
今回はリポジトリ名を「spring-boot-docker」にしておくよ。
./gradlew bootBuildImage --imageName=ネームスペース/spring-boot-docker

イメージを作成したときのログだよ。
images()の中身はイメージIDだよ。
image.png

「./gradlew」が使えないとエラーが出たら階層を間違えているからGradleのある場所に戻ればいいよ。
例:cd C:\Users\ユーザー名\gs-spring-boot-docker\initial

ローカルで作成したイメージを動かしてみるよ。
docker run -p 8080:8080 -t springio/gs-spring-boot-docker
image.png
image.png

DockerHubへプッシュ

docker imagesで作成したイメージ一覧を確認できるよ。
docker push ユーザー名/spring-boot-dockerでプッシュするよ。

(ちなみに docker rmi コマンドでイメージを削除できるよ。
削除するにはイメージ ID かイメージ名を使うよ。
・docker rmi -f 7d9495d03763
・docker rmi -f spring-boot-docker)

dockaerにログインしてリポジトリ一覧を見るとプッシュしたリポジトリが追加されているよ。
image.png

おわりだYO!!

間違っていたら指摘お願いするよ。

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

Rundeck ~ Dockerで環境構築からSlack通知まで ~

やること

  1. コンテナ起動
  2. プロジェクト / ジョブの作成
  3. Slack通知設定

環境

MacOS Catalina 10.15.4
Docker Desktop 2.3.0.2

Dockerコンテナ起動

こちらのDockerイメージで。
現在最新のVersion 3.2.6 をつかいます。
https://hub.docker.com/r/jordan/rundeck/

docker run --rm -p 4440:4440 jordan/rundeck:3.2.6 

docker run

docker pulldocker createdocker start を1発で行うやつ。
イメージがホストマシン上になければDocker Hubからpullしてきて、コンテナつくって、コンテナを起動します。

-it
-i と -tをまとめて書いてるものです。

-i or --interactive
=> ホスト側のシェルからコンテナ内を操作できるようにする。

-t or --tty
=> 仮想ttyの割当て。キーバインドを使用したり、色を出力したりシェル操作を便利に行うために指定。

こちらが非常にわかりやすいです。
https://teratail.com/questions/121780
https://teratail.com/questions/19477
https://teratail.com/questions/100044

-p 4440:4440

ホスト側のポート番号:コンテナ側のポート番号 をひも付けます。
ローカルのブラウザからlocalhost:4440でRundeckの設定画面を開けるようにします。

--rm

コンテナを停止したときに自動で削除もしてくれます。

ログイン

ブラウザかlocalhost:4440にアクセス
※docker run してからアクセスできるようになるまで少しだけ時間がかかります(30秒くらい??)

スクリーンショット 2020-05-20 8.32.54.png

Username: admin
Password: admin

でログイン
※ 初期設定がadmin

プロジェクト作成

スクリーンショット 2020-05-20 8.36.35.png

今回は名前だけ設定し作成

スクリーンショット 2020-05-20 8.41.40.png

SlackにAppをインストール

通知を可能にするために、Incomming WebhooksというアプリケーションをSlackへインストールします。

■Slack■

サイドメニューのApp > incomming webhooks で検索 > 追加

or

https://あなたのワークスペース.slack.com/apps/A0F7XDUAZ--incoming-webhook-?next_id=0

スクリーンショット 2020-05-20 9.06.11.png

■ブラウザ■
ブラウザがひらくので、

右上のワークスペースを選択 > Slackに追加

スクリーンショット 2020-05-20 9.08.26.png

通知先チャンネルを指定 > 追加

スクリーンショット 2020-05-20 9.09.58.png

Webhook URLをコピー

スクリーンショット 2020-05-20 9.12.05.png

■Rundeck■

Rundeckにもどり、Slack通知設定をします。

スクリーンショット 2020-05-20 8.47.02.png

設定ファイルが開くので、末尾に以下の1行を追記

project.plugin.Notification.SlackNotification.webhook_url=https://hooks.slack.com/services/FOO/BAR/hogefuga

https://hooks.slack.com/services/FOO/BAR/hogefugaの部分がさっきコピーしたご自身のWebhook URLになります。

スクリーンショット 2020-05-20 8.48.46.png

ジョブの作成

スクリーンショット 2020-05-21 5.36.24.png

適当な名前をつけて

スクリーンショット 2020-05-21 5.37.58.png

Workflowタブで

スクリーンショット 2020-05-21 5.39.00.png

いろいろなステップを追加できますが、今回はお手軽にコマンドで

スクリーンショット 2020-05-21 5.39.11.png

pwdでカレントディレクトリを表示させましょう。

スクリーンショット 2020-05-21 5.39.28.png

Scheduleタブから、時間やら定期実行やらをご自由に
※Cronの記法でも登録できます。

スクリーンショット 2020-05-21 5.40.26.png

日本の方はタイムゾーンを Asia/Tokyo

スクリーンショット 2020-05-21 5.40.42.png

Notificationsタブで通知設定をします。
成功時のSlackにチェックをいれます。

※今回はDockerイメージを利用しているので不要ですが、本来Slack通知を行うためにはプラグインファイルの設置などが必要になります。
参照: https://namionobkup.hatenablog.com/entry/2018/05/06/112439

※さきほどのWeb Hooks URLを設定しておかないと、以下の
~ Overrideの欄に記入しても無効でした。
こちらの記事に助けて頂きました:bow:
参照: https://obel.hatenablog.jp/entry/20190326/1553563656

スクリーンショット 2020-05-21 5.41.00.png

スクリーンショット 2020-05-21 5.41.23.png

ちなみに
ここからすぐにジョブを実行することも可能です。

スクリーンショット 2020-05-21 5.42.09.png

で、こんな感じでSlackに通知がきます

スクリーンショット 2020-05-21 6.10.15.png

さいごに

Dockerイメージが公開されているので、簡単に環境構築ができてありがたいです。
おしまい。

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

Raspberry Pi 4でKubernetesクラスターを構築する【ソフトウェア編】

この記事はRaspberry Pi 4でKubernetesクラスターを構築する【ハードウェア編】の続きです。

組み立て済みのデバイス

この記事でできるようになること

【ソフトウェア編】を読むと、Kubernetes(以下k8s)のクラスターを、Raspberry Pi上に構築できるようになります。
(ルーター等の解説は割愛)

クラスターは下記の構成で構築していきます。

  • Master Node * 1
    • Kubernetes v1.18
      • Pod Network: Flannel
  • Worker Node * 2

OSのインストール

Macを用いてセットアップしていきます。
下記の作業を3台分行います。

OSイメージのダウンロード

Downloads Raspbianから Raspbian Buster Liteをダウンロードします。
Screen Shot 2020-05-20 at 23.54.30.png
(Desktop版でもOKです)

$ cd ~/Downloads

# zipファイルを解凍
$ unzip 2020-02-13-raspbian-buster-lite.zip
Archive:  2020-02-13-raspbian-buster-lite.zip
  inflating: 2020-02-13-raspbian-buster-lite.img

microSDにOSを焼く

SDカードリーダーが必要です。
私はたまたま持っていた、Anker PowerExpand Direct 7-in-2を使用しました。

詳しい手順は、Copying an operating system image to an SD card using Mac OSに記載されています。

# マウント先の特定
$ diskutil list
/dev/disk0 (internal, physical):
   #:                       TYPE NAME                    SIZE       IDENTIFIER
   0:      GUID_partition_scheme                        *500.3 GB   disk0
   1:                        EFI EFI                     314.6 MB   disk0s1
   2:                 Apple_APFS Container disk1         500.0 GB   disk0s2

/dev/disk1 (synthesized):
   #:                       TYPE NAME                    SIZE       IDENTIFIER
   0:      APFS Container Scheme -                      +500.0 GB   disk1
                                 Physical Store disk0s2
   1:                APFS Volume Macintosh HD - Data     157.8 GB   disk1s1
   2:                APFS Volume Preboot                 79.4 MB    disk1s2
   3:                APFS Volume Recovery                528.1 MB   disk1s3
   4:                APFS Volume VM                      5.4 GB     disk1s4
   5:                APFS Volume Macintosh HD            11.1 GB    disk1s5

/dev/disk2 (external, physical):
   #:                       TYPE NAME                    SIZE       IDENTIFIER
   0:     FDisk_partition_scheme                        *31.9 GB    disk2
   1:             Windows_FAT_32 boot                    268.4 MB   disk2s1
   2:                      Linux                         31.6 GB    disk2s2

上記のコマンドで/dev/disk2がmicroSDだと分かりました。

# microSDをアンマウントする
$ diskutil unmountDisk /dev/disk2
Unmount of all volumes on disk2 was successful

次にイメージをmicroSDに焼きます。
注意してほしいのは、対象を間違えるとMac本体のデータが消えてしまうので、必ずmicroSDの番号に対して実行してください。

# OSイメージを焼く
$ sudo dd bs=1m if=2020-02-13-raspbian-buster-lite.img of=/dev/rdisk2; sync
1780+0 records in
1780+0 records out
1866465280 bytes transferred in 93.421984 secs (19978866 bytes/sec)

SSHを有効にする

Raspberry Piは、デフォルトではSSHができないようになっているので、有効にします。
microSDを挿し直してから実行してください。

$ cd /Volumes/boot

# 空のファイルを作成する
$ touch ssh

cgroupsの設定

enabling cgroup memory doesn't take effect

$ cd /Volumes/boot/
$ vim cmdline.txt

cgroup_enable=cpuset cgroup_enable=memory cgroup_memory=1を下記のように追加します。

cmdline.txt
dwc_otg.lpm_enable=2 console=serial0,115200 console=tty1 root=PARTUUID=7ee80803-02 rootfstype=ext4 cgroup_enable=cpuset cgroup_memory=memory cgroup_memory=1 elevator=deadline fsck.repair=yes rootwait quiet init=/usr/lib/raspi-config/init_resize.sh

Raspberry Piの起動

基盤の裏側にmicroSDスロットがあります。
挿入したら、USB Type-Cケーブルを挿し、給電します。

ここまでの作業を計3回行ってください。

各ソフトウェアのインストール

Macのターミナル等から、下記のコマンドを実行して、各Raspberry Piに割り当てられたIPアドレスを取得します。
下記の作業を3台分行います。

$ arp -a
? (192.168.13.1) at 58:27:8c:25:11:ea on en0 ifscope [ethernet]
? (192.168.13.2) at dc:a6:32:72:b9:50 on en0 ifscope [ethernet]
? (192.168.13.3) at dc:a6:32:72:73:9f on en0 ifscope [ethernet]
? (192.168.13.5) at dc:a6:32:72:ba:4 on en0 ifscope [ethernet]

下3つがRaspbery PiのIPアドレスです。(一番上はトラベルルーターのIPアドレス)

sshログイン

Raspbian OSはデフォルトでユーザー: pi パスワード: raspberry のユーザーが作成されています。

$ ssh pi@192.168.13.2

ユーザーを作成し、piユーザーを削除

piユーザーはパスワードが公開されているので、同じ権限の別のユーザーを作成し、piユーザーを削除します。

# ユーザー作成
$ sudo adduser butter

# piユーザーの権限を確認
$ groups pi
pi : pi adm dialout cdrom sudo audio video plugdev games users input netdev pi spi i2c gpio

# 作成したユーザーに同じ権限を付与
$ sudo usermod -G pi,adm,dialout,cdrom,sudo,audio,video,plugdev,games,users,input,netdev,spi,i2c,gpio butter

# 作成したユーザーに切り替える
$ su - butter

# piユーザーの削除
$ sudo userdel -r pi

Dockerのインストール

インストール方法の詳細はDocker公式ドキュメントをご覧下さい。

# 必要なパッケージのインストール
$ sudo apt-get update
$ apt-get install \
    apt-transport-https \
    ca-certificates \
    curl \
    gnupg-agent \
    software-properties-common

# GPGキーの追加
$ curl -fsSL https://download.docker.com/linux/debian/gpg | sudo apt-key add -

# リポジトリの追加
$ echo "deb [arch=armhf] https://download.docker.com/linux/$(. /etc/os-release; echo "$ID") \
    $(lsb_release -cs) stable" | \
    sudo tee /etc/apt/sources.list.d/docker.list

# Dockerエンジンのインストール
$ sudo apt-get update
$ sudo apt-get install docker-ce

Kubernetesのインストール

kubeadm, kubelet, kubectlをインストールします。
インストール方法の詳細はKubernetes公式ドキュメントをご覧下さい。

$ sudo apt-get update && sudo apt-get install -y apt-transport-https curl
$ curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo apt-key add -
$ cat <<EOF | sudo tee /etc/apt/sources.list.d/kubernetes.list
deb https://apt.kubernetes.io/ kubernetes-xenial main
EOF

$ sudo apt-get update
$ sudo apt-get install -y kubelet kubeadm kubectl
$ sudo apt-mark hold kubelet kubeadm kubectl

Swapの無効化

Swapが有効になっているとKubeletが起動しないため、Swapを無効化しておきます。

$ sudo swapoff -a

ここまでの作業を3台分繰り返してください。
これでクラスター構築の準備は完了と言っていいでしょう。

Kubernetesクラスターの構築

ここからはMasterNodeとWorkerNodeで別の作業をしていきます。

Master Nodeの構築

CNIプラグインはFlannelを使用します。
Flannelを使用する場合、--pod-network-cidr=10.244.0.0/16を指定する必要があります。

$ sudo kubeadm init --pod-network-cidr=10.244.0.0/16
W1228 06:06:27.607623    2273 validation.go:28] Cannot validate kube-proxy config - no validator is available
W1228 06:06:27.607962    2273 validation.go:28] Cannot validate kubelet config - no validator is available
[init] Using Kubernetes version: v1.17.0
[preflight] Running pre-flight checks
    [WARNING IsDockerSystemdCheck]: detected "cgroupfs" as the Docker cgroup driver. The recommended driver is "systemd". Please follow the guide at https://kubernetes.io/docs/setup/cri/
[preflight] Pulling images required for setting up a Kubernetes cluster
[preflight] This might take a minute or two, depending on the speed of your internet connection
[preflight] You can also perform this action in beforehand using 'kubeadm config images pull'
[kubelet-start] Writing kubelet environment file with flags to file "/var/lib/kubelet/kubeadm-flags.env"
[kubelet-start] Writing kubelet configuration to file "/var/lib/kubelet/config.yaml"
[kubelet-start] Starting the kubelet
[certs] Using certificateDir folder "/etc/kubernetes/pki"
[certs] Generating "ca" certificate and key
[certs] Generating "apiserver" certificate and key
[certs] apiserver serving cert is signed for DNS names [k8s-master kubernetes kubernetes.default kubernetes.default.svc kubernetes.default.svc.cluster.local] and IPs [10.96.0.1 192.168.13.101]
[certs] Generating "apiserver-kubelet-client" certificate and key
[certs] Generating "front-proxy-ca" certificate and key
[certs] Generating "front-proxy-client" certificate and key
[certs] Generating "etcd/ca" certificate and key
[certs] Generating "etcd/server" certificate and key
[certs] etcd/server serving cert is signed for DNS names [k8s-master localhost] and IPs [192.168.13.101 127.0.0.1 ::1]
[certs] Generating "etcd/peer" certificate and key
[certs] etcd/peer serving cert is signed for DNS names [k8s-master localhost] and IPs [192.168.13.101 127.0.0.1 ::1]
[certs] Generating "etcd/healthcheck-client" certificate and key
[certs] Generating "apiserver-etcd-client" certificate and key
[certs] Generating "sa" key and public key
[kubeconfig] Using kubeconfig folder "/etc/kubernetes"
[kubeconfig] Writing "admin.conf" kubeconfig file
[kubeconfig] Writing "kubelet.conf" kubeconfig file
[kubeconfig] Writing "controller-manager.conf" kubeconfig file
[kubeconfig] Writing "scheduler.conf" kubeconfig file
[control-plane] Using manifest folder "/etc/kubernetes/manifests"
[control-plane] Creating static Pod manifest for "kube-apiserver"
[control-plane] Creating static Pod manifest for "kube-controller-manager"
W1228 06:08:56.859476    2273 manifests.go:214] the default kube-apiserver authorization-mode is "Node,RBAC"; using "Node,RBAC"
[control-plane] Creating static Pod manifest for "kube-scheduler"
W1228 06:08:56.868671    2273 manifests.go:214] the default kube-apiserver authorization-mode is "Node,RBAC"; using "Node,RBAC"
[etcd] Creating static Pod manifest for local etcd in "/etc/kubernetes/manifests"
[wait-control-plane] Waiting for the kubelet to boot up the control plane as static Pods from directory "/etc/kubernetes/manifests". This can take up to 4m0s
[kubelet-check] Initial timeout of 40s passed.
[apiclient] All control plane components are healthy after 48.007975 seconds
[upload-config] Storing the configuration used in ConfigMap "kubeadm-config" in the "kube-system" Namespace
[kubelet] Creating a ConfigMap "kubelet-config-1.17" in namespace kube-system with the configuration for the kubelets in the cluster
[upload-certs] Skipping phase. Please see --upload-certs
[mark-control-plane] Marking the node k8s-master as control-plane by adding the label "node-role.kubernetes.io/master=''"
[mark-control-plane] Marking the node k8s-master as control-plane by adding the taints [node-role.kubernetes.io/master:NoSchedule]
[bootstrap-token] Using token: ul0n5s.x4ukjc7en79vt4o7
[bootstrap-token] Configuring bootstrap tokens, cluster-info ConfigMap, RBAC Roles
[bootstrap-token] configured RBAC rules to allow Node Bootstrap tokens to post CSRs in order for nodes to get long term certificate credentials
[bootstrap-token] configured RBAC rules to allow the csrapprover controller automatically approve CSRs from a Node Bootstrap Token
[bootstrap-token] configured RBAC rules to allow certificate rotation for all node client certificates in the cluster
[bootstrap-token] Creating the "cluster-info" ConfigMap in the "kube-public" namespace
[kubelet-finalize] Updating "/etc/kubernetes/kubelet.conf" to point to a rotatable kubelet client certificate and key
[addons] Applied essential addon: CoreDNS
[addons] Applied essential addon: kube-proxy

Your Kubernetes master has initialized successfully!

To start using your cluster, you need to run the following as a regular user:

  mkdir -p $HOME/.kube
  sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
  sudo chown $(id -u):$(id -g) $HOME/.kube/config

You should now deploy a pod network to the cluster.
Run "kubectl apply -f [podnetwork].yaml" with one of the options listed at:
  https://kubernetes.io/docs/concepts/cluster-administration/addons/

You can now join any number of machines by running the following on each node
as root:

  kubeadm join 192.168.13.2:6443 --token vpmuze.ina6swjhlxh57lds --discovery-token-ca-cert-hash sha256:fcda1034ebb1374f0a0b487cd349b29ac1c253e83e6344b4d567cf61123a509c

kubectlの設定

$ mkdir -p $HOME/.kube
$ sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
$ sudo chown $(id -u):$(id -g) $HOME/.kube/config

Pod Network Addonのインストール

$ kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/2140ac876ef134e0ed5af15c65e414cf26827915/Documentation/kube-flannel.yml
podsecuritypolicy.policy/psp.flannel.unprivileged created
clusterrole.rbac.authorization.k8s.io/flannel created
clusterrolebinding.rbac.authorization.k8s.io/flannel created
serviceaccount/flannel created
configmap/kube-flannel-cfg created
daemonset.apps/kube-flannel-ds-amd64 created
daemonset.apps/kube-flannel-ds-arm64 created
daemonset.apps/kube-flannel-ds-arm created
daemonset.apps/kube-flannel-ds-ppc64le created
daemonset.apps/kube-flannel-ds-s390x created

Worker Nodeの構築

Master Nodeを構築したときに出力されたコマンドをそのまま使用します。

$ sudo kubeadm join 192.168.13.2:6443 --token vpmuze.ina6swjhlxh57lds --discovery-token-ca-cert-hash sha256:fcda1034ebb1374f0a0b487cd349b29ac1c253e83e6344b4d567cf61123a509c
W1228 06:21:59.043224    3046 join.go:346] [preflight] WARNING: JoinControlPane.controlPlane settings will be ignored when control-plane flag is not set.
[preflight] Running pre-flight checks
    [WARNING IsDockerSystemdCheck]: detected "cgroupfs" as the Docker cgroup driver. The recommended driver is "systemd". Please follow the guide at https://kubernetes.io/docs/setup/cri/
[preflight] Reading configuration from the cluster...
[preflight] FYI: You can look at this config file with 'kubectl -n kube-system get cm kubeadm-config -oyaml'
[kubelet-start] Downloading configuration for the kubelet from the "kubelet-config-1.17" ConfigMap in the kube-system namespace
[kubelet-start] Writing kubelet configuration to file "/var/lib/kubelet/config.yaml"
[kubelet-start] Writing kubelet environment file with flags to file "/var/lib/kubelet/kubeadm-flags.env"
[kubelet-start] Starting the kubelet
[kubelet-start] Waiting for the kubelet to perform the TLS Bootstrap...

This node has joined the cluster:
* Certificate signing request was sent to apiserver and a response was received.
* The Kubelet was informed of the new secure connection details.

Run 'kubectl get nodes' on the control-plane to see this node join the cluster.

この作業をWorkerNode数分繰り返してください。

構築確認

Master Nodeで下記のコマンドを実行。

$ kubectl get nodes
NAME              STATUS   ROLES    AGE     VERSION
rp-kube-master    Ready    master   12m     v1.18.2
rp-kube-worker1   Ready    <none>   2m13s   v1.18.2
rp-kube-worker2   Ready    <none>   43s     v1.18.2

Worker NodeのROLESが<none>になっているので、ラベルを追加します。

$ kubectl label node rp-kube-worker1 node-role.kubernetes.io/worker=worker
node/rp-kube-worker1 labeled
$ kubectl label node rp-kube-worker2 node-role.kubernetes.io/worker=worker
node/rp-kube-worker2 labeled

$ kubectl get nodes
NAME              STATUS   ROLES    AGE     VERSION
rp-kube-master    Ready    master   12m     v1.18.2
rp-kube-worker1   Ready    worker   2m13s   v1.18.2
rp-kube-worker2   Ready    worker   43s     v1.18.2

これでクラスターの構築は完了です。

最終的な状態

Master Nodeにログインしたとき
Screen Shot 2020-05-21 at 3.07.35.png

Worker Nodeにログインしたとき
Screen Shot 2020-05-21 at 3.07.01.png

また、それぞれに静的IPアドレスを割り当てて、再起動等を繰り返してもIPが変わらないようにしたり、host名を変更したりしました。

まとめ

k8sのクラスター構築は初めてやったので、手順通りにやっただけではあるものの、かなり勉強になることが多かったです。
これから本格的に触れていこうと思うので、今後もk8sに関する記事を書いていければと思います。

参考記事

Raspberry PiでおうちKubernetes構築【論理編】
Raspberry Pi 4 でおうちKubernetesを作ろう(Raspbian Buster Lite対応版)
ラズパイ4で作るディスプレイ付きKubernetesクラスター
ラズパイでKubernetesクラスタを構築する

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

Raspberry Pi 4でKubernetesクラスターを構築する【ハードウェア編】

目的

業務でKubernetes(以下k8s)を扱うようになり、勉強のために1から構築します。
GKEやAKSを使うのではなく1から環境構築したくなり、過去にRaspberryPiでk8sのクラスターを構築している方の記事をみたことを思い出し、自分でもやってみることにしました。

この記事でできるようになること

【ハードウェア編】では、k8sのクラスター構築の準備として、以下の画像のようなデバイスを組み立てます。
必要な部品とそれらのリンクも記載するので、購入した上で手順通りに組み立てれば、k8s等をインストールするための準備は完了します。

最終的なゴール

【ハードウェア編】【ソフトウェア編】の両方を読み終えると、以下の環境を構築できるようになります。

  • Master Node * 1
    • Kubernetes v1.18
      • Pod Network: Flannel
  • Worker Node * 2

購入した部品

部品 個数 備考
Raspberry Pi 4 Model B/4GB 3個
Raspberry Pi 4 ヒートシンク 3個 アクリルケースに付属されているヒートシンクを使う場合は不要
microSDHC 32GB 3個
アクリルケース 1個 ヒートシンク、ピンセット、ドライバー付属
無線LAN トラベルルーター 1個
5ポート USB充電器 1個
スイッチングハブ 1個
USB Type-C ケーブル 3本 このリンクは3本組の商品です
Micro USBケーブル 2本 このリンクは2本組の商品です
0.5m LANケーブル 4本

その他に使用したもの

  • microSDカードリーダー
  • 両面テープ

組み立てる

Raspberry Pi 4開封

Raspbery Pi 3では基盤は袋で密閉されているようでしたが、4は剥き出しの状態で入っています。
箱を開けた時にびっくりして一瞬不安になりましたw

Raspberry Piをアクリルプレートに固定

アクリルプレートのフィルムを剥がすのが一番大変でしたw

Raspberry Piの裏面にmicroSDを挿入するスロットがあり、プレートはこのスロットに当たらないように作られています。
なので、それぞれの向きを間違えないように注意してください。

Raspberry Piにヒートシンクをつける

それぞれ大きさが異なり、貼り付ける位置が違うので注意してください。

USB充電器を設置

大きめの両面テープで固定しています。

スイッチングハブ、トラベルルーターを設置

トラベルルーターは少しずらして設置しています。
理由は、スイッチングハブのLANケーブルの爪が当たってしまい、設置する時に少し浮いてしまうからです。
トラベルルーターとスイッチングハブは、それぞれ両面テープで固定しています。

アクリルプレートを固定用のネジでつなげる

このときに、どうケーブリングするかイメージしながらプレートを固定していくといいと思います。
私は、正面にケーブルが出ているのがいやだったので、以下のような向きで固定しました。
(すべて正面から見た場合)

段数 設置物 固定方法
1~3段目 Raspberry Pi USBポートやLANポートが左側に来るように固定
4段目 USB充電器 USBポートが左側に来るように固定
5段目 スイッチングハブ、トラベルルーター LANポートが裏面に来るように固定

ケーブリング

正面

裏面

まとめ

基盤を触ったり、配線を考えたりするのが久しぶりだったので、結構ワクワクしました。
ルーターの設定やk8sのインストール、クラスターの構築は、【ソフトウェア編】で解説しているので、ぜひそちらもご覧ください。

参考記事

Raspberry PiでおうちKubernetes構築【物理編】
Raspberry Pi 4 でおうちKubernetesを作ろう(Raspbian Buster Lite対応版)
3日間クッキング【Kubernetes のラズペリーパイ包み “サイバーエージェント風”】

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

自分用: ネットワークのメモ

はじめに

ネットワークは前にやったきりで時間も経ってるため、だいぶ忘れてしまいました.
久しぶりにネットワークの復習と追加学習をしました.
その内容をここにメモします.
間違ってたりアドバイスあったら優しく教えてくれると助かります〜

TCP

L3までのおかげでパケットを相手に送信する事ができるようになったが、実際に届いたかどうかはわからない.
なので、相手はパケットが届いたら届いたよ!と、教えたりするルールを設けたのがTCP.(詳しくは省く

パケットを送る時はPORT番号を指定する必要がある.
TCPの場合、相手からのパケットも受け取る必要があるので、依頼元も今回の通信で使うPORT番号を指定する.
送信元PORT番号は[動的・プライベート ポート番号 (49152–65535)]から選ばれる.
つまり、このout用のport番号を超えた65535-49152=16383を超える同時な外部へのTCPでの接続は工夫しないと、基本的にできない.
UDPの場合は、送信元PORTが必要ないので、この動的ポート番号枯渇による起きる制限はない.

ARP

http://www5e.biglobe.ne.jp/aji/3min/26.html

ARPはL3のプロトコルで、ARP Requestをブロードキャストすることにより、ARP Requestに含まれている ARP Replyを頂ける.
ARP ReplyにリクエストされたMACアドレスが含まれた状態で届く.
これを利用することにより、ARP tableに宛先mac addressがない場合でもパケットを届けられるようになる.

ARPのusecaseその4ぐらい

手動でnic設定したstatic ip addressで一度も外部通信していない場合、bridgeやRouterのARP tableにmac addressが記録されていない.
その様な状態の時に、Routerは受け取った宛先 IPのMac AddressをARPを使って調べる.

firewall

firewallでよく80番ポートだけ開放するとかやります.
その場合、80番ポート以外のポートが記載されたパケットを全てロスする設定になっていると、動的ポート番号のパケットまで捨てられてしまいます.
そうした場合、TCPを利用したやりとりができなくなってしまいます.
なので、当たり前ですが、基本的にinとout用でセキュリティで考慮することや設定は変わります.
ちなみにUDPの場合は、response用のポートを動的ポート番号で確保する必要ないので、UDPリクエストしかしないと保証されているようなクライアントがいた場合、全てのportを閉じてもデータを外部に送れます.

NIC

eh0はnic0枚目、eth1はnic1枚目って事
ethN == nicって認識で基本大丈夫
物理レイヤーにパケットを送る場合はethに送信パケットを投げて、ethがそれをethのpeerに対して送る. (nicは基本イーサネットケーブル等を通じて、bridgeやrouter等、対となるnicと繋がっている.その対のことをpeerと呼ぶ.

Router作成の概念

http://redhatlinux.kt.fc2.com/cont/router.htm
このサイトが詳しい.
route -nの見方がわからない場合、このサイトを見るといい.
https://xtech.nikkei.com/it/article/COLUMN/20080520/303086/
http://linux.kororo.jp/cont/intro/dgate.php

コンバージェンスとか一旦置いといた、一番シンプルなRouterはNICから受け取ったパケットを別のNICに飛ばす技術であるip forwardだけで実現が可能.
linuxでip forwardを有効化するには、/etc/sysctl.confnet.ipv4.ip_forward=1を追記し、ネットワークを再起動すると、ip forwardの機能が有効化される.

ifconfig vs ip command

ifconfigはnet-toolsパッケージに含まれているコマンド.
その他にもよく使うroute, netstat, arpなどもnet-toolsパッケージに入っているコマンド.
net-toolsパッケージは現在非推奨になっている.

仮想bridgeの作り方

bridgeは以下のいずれからでも作成できる

  • bridge-utils
  • iproute2
  • netctl
  • systemd-networkd
  • NetworkManager

bridgeにはeth0等の実デバイスとveth等の仮想デバイスを接続することができます

仮想networkの作り方

仮想nicを作りまくって、仮想bridgeに繋げば、仮想プライベートネットワークは作れる.

しかし、このままだとプライベートネットワークがインターネットとやりとりできない.
直接eth0と繋ぐと、今度はeth0が専有されて、プライベートネットワークに所属していない奴らがインターネットに接続できなくなる.
なので、iptablesを使い、ipマスカレードする事によって、できる.
なので、docker等のbridgeのipマスカレードの設定を消すと、完全にネットワークから切り離せる.

ipマスカレード(NAPT

iptablesを使う事により、ipマスカレードを使える.
ipマスカレードを使うと、NAPTの機能はもちろん、port forwardや、securityとかも行える.

port forward

https://qiita.com/Ayaka14/items/449e2236af4b8c2beb81
こんな感じ
iptablesでもできる
特定のportに届いたパケットを特定のip addressのportに転送できる.
L2のproxyとして使える.
また、ネットワークセグメント同士を繋ぐのにも使える.
ネットワークセグメント同士を繋げば、VXLANが構築できる.

VXLAN

https://tech.uzabase.com/entry/2017/08/23/175813
このサイトが詳しい.

冗長化

基本的に対障害の為、単一のマイクロサービスを複数ホストに配置したいので、1マイクロサービスは複数ネットワークセグメント上に展開する.

VXLANの感想

結局はroutingの設定って感じがした.
routingの特定項目の設定とかにport forwardとか色んな特別な名前をつけてる感じ.

参考文献

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