Spring Boot アプリケーションをAWS EKSにデプロイ
Spring BootアプリケーションをAWS EKSにデプロイして、
LoadBalancer タイプのサービスで外部公開するまでの手順、備忘録
spring boot アプリケーションの作成
Docker で Spring Bootを参考にspring bootアプリケーションの実行可能jarファイルを作成
Docker image構築
事前にDocker実行環境を作成しておくこと
Install Docker Desktop on Windows | Docker Documentation
Dockerfileを作成し、イメージをbuild
$ cat Dockerfile FROM openjdk:8-jdk-alpine AS builder WORKDIR target/dependency ARG APPJAR=build/libs/*.jar COPY ${APPJAR} app.jar RUN jar -xf ./app.jar FROM openjdk:8-jre-alpine VOLUME /tmp ARG DEPENDENCY=target/dependency COPY --from=builder ${DEPENDENCY}/BOOT-INF/lib /app/lib COPY --from=builder ${DEPENDENCY}/META-INF /app/META-INF COPY --from=builder ${DEPENDENCY}/BOOT-INF/classes /app ENTRYPOINT ["java","-cp","app:app/lib/*","com.example.demo.DemoApplication"] $ docker build -t test .
ImageをECRにPush
事前にAWS CLIで以下コマンドやeksctlが利用できる環境を作成しておくこと
eksctl の開始方法 - Amazon EKS
リポジトリを作成
aws ecr create-repository \ --repository-name test \ --image-scanning-configuration scanOnPush=true \ --region ap-northeast-1
ログイン
aws ecr get-login-password | docker login --username AWS --password-stdin xxxxxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com
イメージのタグ付け
docker tag test:latest xxxxxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/test:latest
push
docker push xxxxxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/test:latest
EKS clusterの作成
eksctlコマンドで実施
今回は、t3.microインスタンスタイプのlinuxのみのワークロードクラスターで作成
eksctl create cluster \ --name prod \ --version 1.16 \ --region ap-northeast-1 \ --nodegroup-name standard-workers \ --node-type t3.micro \ --nodes 2 --nodes-min 2 \ --nodes-max 2 \ --ssh-access \ --ssh-public-key <SSH接続時の公開鍵> \ --managed
spring boot アプリケーションのデプロイメント定義を作成
kubectl create deployment test --image= xxxxxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/test --dry-run -o=yaml > deployment.yaml
デプロイメントの適用
kubectl apply -f deployment.yaml
作成されていることを確認
$ kubectl get all NAME READY STATUS RESTARTS AGE pod/test-55cfbd7855-frrx8 1/1 Running 0 5s
外部接続のためのロードバランサー サービス定義を作成
$ cat loadbalancer.yaml apiVersion: v1 kind: Service metadata: name: nginx-service spec: type: LoadBalancer selector: app: test ports: - protocol: TCP port: 8080 targetPort: 8080
serviceを適用
kubectl create -f loadbalancer.yaml
作成されていることを確認
$ kubectl get service/nginx-service NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE nginx-service LoadBalancer yy.yyy.yyy.yyy xxxxxxxxx.ap-northeast-1.elb.amazonaws.com 8080:31880/TCP 24s
アクセス!
参考
Docker で Spring Boot
Spring Boot Kubernetes
AWS CLI を使用した Amazon ECR の開始方法
EC2の自動停止スクリプト(Lambda+CloudWatch Events)
うはww また消し忘れて寝てるww 請求書((((;゚Д゚))))
となるのを避けるのを防ぐためのスクリプトのお話。 今回は、下記で実施
- Lambda
- bash
- awscli
- CloudWatch Events
前提
- 開発環境でaws cliが設定済み
- Lambdaで利用するロールは事前に作成済みとする
今回の場合だと、AWSLambdaBasicExecutionRole以外にも、
EC2を操作する権限や、パラメータストアから値を取得する権限が必要
シェルの用意
除外する方法もいろいろ。今回はAWS Systems Manager のパラメータストアを利用
組織とかで使うなら、tagやファイルの読み込みのほうが使いやすそう
#!/bin/bash #起動中インスタンスの一覧を取得 RUNNING_INSTANCE=$(aws ec2 describe-instances | jq -r '[.Reservations[].Instances[] | select(.State.Name == "running") | .InstanceId] | join(" ")') #除外対象とするインスタンスの一覧を取得 EXCLUDE_LIST=$(aws ssm get-parameter --name <設定するパラメータキー名> | jq -r '.Parameter.Value' | sed -e 's/,/ /g' ) #除外対象以外のインスタンスを停止 for e in ${RUNNING_INSTANCE[@]}; do if ! `echo ${EXCLUDE_LIST[@]} | grep -q "${e[@]}"` ; then aws ec2 stop-instances --instance-ids ${e[@]} else : fi done
パラメータストアの設定
aws ssm put-parameter \ --name "パラメータキー名" \ --value "除外対象のインスタンスID_1,除外対象のインスタンスID_2" \ --type StringList
Lamda関数の作成
bashの場合カスタムランタイムで起動する。すぐに終わるかと思ったが、 awsのチュートリアルで進めると、awscliやjqコマンドをたたく際に、そんなコマンドねーぞと怒られるので少し準備が必要。 Bashを動かす環境としてbash-lambda-layerなる便利なものが公開されていたのでこちらをlayerとして使わせていただきます<(_ _)>
アップロードするスクリプトファイルの作成
bash-lambda-layer example-basic.shを参考に用意したシェルを修正し、index.shとして保存
function handler () { set -e #起動中インスタンスの一覧を取得 RUNNING_INSTANCE=$(aws ec2 describe-instances | jq -r '[.Reservations[].Instances[] | select(.State.Name == "running") | .InstanceId] | join(" ")') #除外対象とするインスタンスの一覧を取得 EXCLUDE_LIST=$(aws ssm get-parameter --name exclude.ec2instance | jq -r '.Parameter.Value' | sed -e 's/,/ /g' ) #除外対象以外のインスタンスを停止 for e in ${RUNNING_INSTANCE[@]}; do if ! `echo ${EXCLUDE_LIST[@]} | grep -q "${e[@]}"` ; then aws ec2 stop-instances --instance-ids ${e[@]} else : fi done echo "{\"success\": true}" >&2 }
アップロードのためindex.shをzip化
zip function.zip index.sh
awscliでlambda関数の作成
$ aws lambda create-function \ --function-name terminate-ec2 \ --role <事前準備済みのroleを指定> \ --handler index.handler \ --runtime provided \ --timeout --> defaultの3秒は厳しいので適当な値を設定 \ --memory-size --> memoryもデフォルト128 MBだと結構時間がかかるのでry \ --layers arn:aws:lambda:ap-northeast-1:744348701589:layer:bash:8 \ --zip-file fileb://function.zip
起動確認
$ aws lambda invoke --function-name terminate-ec2 response.txt { "StatusCode": 200, "ExecutedVersion": "$LATEST" } $ cat response.txt {"success": true}
Cloudwatch Eventsの作成
時刻はUTCなので日本時間では+9 (夜中の3時に実行)
$ aws events put-rule --name "Ec2TerminateAtMidnight" --schedule-expression "cron(0 18 * * ? *)" --state ENABLED
CloudWatch Eventsから作成したLamba実行できるための権限付与
$ aws lambda add-permission \ --function-name "terminate-ec2" \ --statement-id "cwevents" \ --action 'lambda:InvokeFunction' \ --principal events.amazonaws.com \ --source-arn arn:aws:events:ap-northeast-1:<accountId>:rule/Ec2TerminateAtMidnight
targetの設定 (eventとlambda関数の紐づけ)
aws events put-targets --rule "Ec2TerminateAtMidnight" --targets Arn=arn:aws:lambda:ap-northeast-1:<accountId>:function:terminate-ec2,Id=1
こんな感じでできれば完成
参考
チュートリアル – カスタムランタイムの公開
gkrizek/bash-lambda-layer
AWS CLIを使ってEC2インスタンスの情報を取得する
log4j2を使用したwebアプリでのファイルへのログ出力
Webアプリケーションでログの制御をlog4j2を使用して行う方法についてのメモ。
以下はEclipseを使って作成した手順です。
環境
手順
1. jarファイルの用意
公式ページに行って、Log4j 2 のjarファイルを取得する。
Log4j 2.3はJavaの6がサポートされる最後のバージョンで以下のjarファイルが必要。
2.設定ファイル(log4j2.xml)について
設定ファイルがない場合は、デフォルト設定でのログ出力が行われる。
デフォルト設定を設定ファイルで記述した場合以下の記載となる。
<?xml version="1.0" encoding="UTF-8"?> <Configuration status="WARN"> <Appenders> <Console name="Console" target="SYSTEM_OUT"> <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/> </Console> </Appenders> <Loggers> <Root level="error"> <AppenderRef ref="Console"/> </Root> </Loggers> </Configuration>
Configuration
statusはlog4j2自体の内部ログの出力レベル設定。status="off"で出力停止
Appenders
どのようにLogEventを配信するかを定義する。
Console appenderは System.out または System.errへの出力
targetで標準出力か標準エラー出力かを定義する。
PatternLayout
出力形式の設定。細かな設定値はマニュアルに記載参照。
デフォルトのpatternの記載内容は以下
%d{HH:mm:ss.SSS} -> 時刻形式。%d{}で時刻形式を囲んで設定 %t -> スレッド名 %msg -> ログメッセージ hh:mm:ss.SSS [threadName] [logLevel] [loggerName]-[msg]のような形式で出力される
Loggers
Javaパッケージ毎に出力するログレベルやログの出力方法(Appender)を指定する。
設定ファイルがない場合、またはLogger name="hoge"の形でロガー名を指定しない場合は
Rootの設定を適用。 level="error"であればERROR以上のレベル(ERROR,FATAL)で出力を行う。
AppenderRef refで指定する名称にはAppenderのnameを使用する。
仮に、
<Loggers> <Root level="debug" ・・・> ・・・・ <Logger name="X" level="error" > ・・・・
の設定が存在した場合、下記のJavaパッケージは以下のレベルで出力を行う
X --> Xが適用 ERROR,FATAL
X.Y --> Xが適用 ERROR,FATAL
Z --> Rootが適用 DEBUG,INFO,WARN,ERROR,FATAL
3.ファイル出力のための設定ファイル(log4j2.xml)の準備
今回は、ファイル出力を行うため、RollingFileAppenderを用いて以下に設定
<?xml version="1.0" encoding="UTF-8"?> <Configuration status="off"> <Appenders> <RollingFile name="RollingFile" fileName="D:/testlogs/test.log" filePattern="D:/testlogs/test-%d{yyyy-MM-dd}-%i.zip"> <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger - %msg%n"/> <Policies> <OnStartupTriggeringPolicy /> <SizeBasedTriggeringPolicy size="20 MB" /> <TimeBasedTriggeringPolicy /> </Policies> <DefaultRolloverStrategy max="20"/> </RollingFile> </Appenders> <Loggers> <Root level="error"> <AppenderRef ref="RollingFile"/> </Root> </Loggers> </Configuration>
ログ出力先/名称
D:/testlogs/test.log
フルパス、相対パスどちらの指定も可能
ログローテート条件
以下の条件で新たなファイルを生成し、ログローテートを実施
- ログの開始日と日付が異なる場合
- 新たなJVMが起動した場合
- ファイルサイズが20MBを超えた場合
ローテートファイル形式
test-%d{yyyy-MM-dd}-%iの名称でzip圧縮(%iは連番でmaxは20)
4.Tomcatプロジェクトの作成
Eclipseのメニュー -> ファイル -> 新規 -> プロジェクト -> Tomcatプロジェクトを選択
プロジェクト名は任意の名前を入力
5.jarの配置と設定ファイル(log4j2.xml)の配置
WEB-INF/lib以下のファイルにダウンロードしたJarファイルを配置する。 また、log4j2.xmlをsrc直下に配置する。 配置したらeclipse上のプロジェクトフォルダ上で右クリックして更新。 jarファイルがパッケージ・エクスプローラーに表示されたら、 ファイルを選択して右クリック->ビルドパスへ追加。
6.ログを出力するためのサーブレットクラスの作成
「WEB_INF/src」フォルダの上で右クリック、新規 -> クラス を選択し、適当なパッケージ名とクラス名を入力
サンプルコード(LogOut.java)
package foo; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; public class LogOut extends HttpServlet { static Logger logger = LogManager.getLogger(LogOut.class.getName()); @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { logger.trace("Logged by logger.trace"); logger.debug("Logged by logger.debug"); logger.info ("Logged by logger.info"); logger.warn ("Logged by logger.trace"); logger.error("Logged by logger.error"); logger.fatal("Logged by logger.fatal"); } }
7.web.xmlの作成
WEB_INF/src/フォルダの上で右クリック、新規 -> ファイル、ファイル名をweb.xmlとして作成
<?xml version="1.0" encoding="ISO-8859-1"?> <web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5"> <servlet> <servlet-name>LogOut</servlet-name> <servlet-class>foo.LogOut</servlet-class> </servlet> <servlet-mapping> <servlet-name>LogOut</servlet-name> <url-pattern>/LogOut</url-pattern> </servlet-mapping> </web-app>
8.ブラウザでアクセスして確認
設定したERRORとFATALレベルのログがD:\testlogs\test.logに出力されていることを確認
23:06:02.434 [http-8080-1] ERROR foo.LogOut - Logged by logger.error 23:06:02.437 [http-8080-1] FATAL foo.LogOut - Logged by logger.fatal
log4j2.xmlのPoliciesにOnStartupTriggeringPolicyを設定しているためTomcatの再起動で、 ログがローテートされ圧縮される
参考
Centos 6への Tomcat7のインストール
Tomcatのおさらい
[Q] そもそも tomcatって?
[A] サーブレットコンテナ
[Q] サーブレットコンテナとは?
[A]サーブレットを呼び出して、実行する環境
[Q] 代表的なサーブレットコンテナって?
[A] よく聞くのは、Tomcat、Glassfish、Jetty...とか
[Q] サーブレットって?
[A] サーバー上で動作するJavaプログラム
[Q] サーブレットの役割って?
[A] 三層アーキテクチャのプレゼンテーション層、MVCの構造のController
インストール方法
yumのようなパッケージ管理コマンドを使ってインストールしたり、
公式サイトから、インストーラやバイナリの圧縮ファイルを取得し展開しても可。お好みで。
本番環境などで、脆弱性対応による、早急なupdateなどが発生する場合には、
リポジトリに対応する最新版がないこともあるので、後者のほうが楽と思われる。
tar.gzを使用してCentOSへインストールする場合の方法について、記載するが
大きく必要な手順としては
運用する際には当然他にも細かな設定がいろいろと必要になってくる。
環境
インストール
ランタイムと開発環境のインストール
# yum install java-1.7.0-openjdk # yum install java-1.7.0-openjdk-devel # java -version java version "1.7.0_201" OpenJDK Runtime Environment (rhel-2.6.16.0.el6_10-x86_64 u201-b00) OpenJDK 64-Bit Server VM (build 24.201-b00, mixed mode # javac -version javac 1.7.0_201
Tomcat を実行する専用ユーザーの作成
# useradd -s /sbin/nologin tomcat
バイナリをwgetで取得 (v7.0.92)
# wget https://www-eu.apache.org/dist/tomcat/tomcat-7/v7.0.92/bin/apache-tomcat-7.0.92.tar.gz
取得したtar.gzを展開
# tar -xf ~/apache-tomcat-7.0.92.tar.gz # mv ~/apache-tomcat-7.0.92 ~/tomcat-7.0.92 # mkdir /opt/tomcat # mv ~/tomcat-7.0.92/ /opt/tomcat # chown -R tomcat:tomcat /opt/tomcat/
環境変数の設定
# vi /etc/profile #下記の記載を追加 JRE_HOME=/usr/lib/jvm/jre CATALINA_HOME=/opt/tomcat/tomcat-7.0.92 export JRE_HOME CATALINA_HOME
/etc/profileの設定を反映
# source /etc/profile # echo $JRE_HOME $CATALINA_HOME
tomcatの軌道/停止
# sudo -u tomcat /opt/tomcat/tomcat-7.0.92/bin/startup.sh # sudo -u tomcat /opt/tomcat/tomcat-7.0.92/bin/shutdown.sh
あとは、ブラウザでインストールしたホスト名(ip):8080にアクセスすれば、
おなじみの猫の画面が見えるはず。いつも思うがこの猫あまりかわいくない。
シェルで2つのcsvファイルを一行ずつ読み込み結合
大量のレコードに対し、2つのcsvファイルをシェルで結合する方法。
やりたかったのは、right.csvの各行に対して、left.csvの組み合わせを結合し、
出力ファイル(output.csv)を生成すること。
left.csv
number,name 01,hoge 02,fuga 03,hogehoge 04,fugafuga
right.csv
taskNumber,taskName 100,eat 200,sleep 300,walk 400,work
output.csv
number,name,taskNumber,taskName 01,hoge,100,eat 01,hoge,200,sleep 01,hoge,300,walk 01,hoge,400,work (省略) . . 04,fugafuga,400,work
ループ処理で1行ずつテキストを読み込む構文
while read ブロック変数 do 処理 done < 読み込みファイル
サンプルスクリプト
#!/bin/bash #出力ファイルの存在チェック #存在していれば削除 if [ -e output.csv ]; then rm -f output.csv fi #headコマンドでheaderのみを取得 #結合してoutput.csvへ出力 leftHeader=$(head -n 1 left.csv) rightHeader=$(head -n 1 right.csv) echo "${leftHeader},${rightHeader}" >> output.csv #headerを除いて一行ずつループで取得 #結合してoutput.csvへ出力 tail -n +2 left.csv | while read line1 do number=`echo ${line1} | cut -d , -f 1` name=`echo ${line1} | cut -d , -f 2` tail -n +2 right.csv | while read line2 do taskNumber=`echo ${line2} | cut -d , -f 1` taskName=`echo ${line2} | cut -d , -f 2` echo "${number},${name},${taskNumber},${taskName}" >> output.csv done done
スクリプト書いたら、実行して気長に待つ。。
Android emulatorでtcpdumpを取得
社内プロキシを通すためにAndroidのパケットログを取得する必要があり、
方法を調べたので忘れないようにまとめておく。
web proxyであればFiddlerを使うなどいろいろな方法があると思うが、
最も手っ取り早いのはemulatorでtcpdumpを取得してWiresharkとかで確認する方法だと思う。
結論から言ってしまうと、下記のコマンドでtcpdumpを取得できる。
emulator -tcpdump <出力ファイルパス> -avd <3.で調べたemulator名>
環境
Android Studio | 3.1.3 |
OS | Windows7(64bit) |
手順
SDKのインストールディレクトリを確認
わからなければ、Android Studioを開きTools > SDK Managerから選択してパスを確認。起動するemulatorの名称を下記のコマンドで確認
emulator -list-avds
下記のコマンドでemulatorを起動
emulator -tcpdump <ダンプを出力するファイルパス> -avd <3.で調べたemulator名>
ダンプを取得するための操作をemulatorで実施
emulatorを終了
取得したファイルをwiresharkとかに取り込んで確認
参考
-Monitoring Android network traffic
-Start the emulator from the command line | Android Developers
Excel(VBA)でOracleとの連携(oo4o)
ExcelでOracle Databaseからデータを取得する機会があり、Oracleに詳しくないこともあり、 VBAでexcelに接続する方法を調べたので纏めておく。
主要な方法としては3つあるらしい
- ActiveXData Objects(以下ADO)からODBCドライバを使用する方法
- ADOからOLE DBドライバを使用する方法
- Oracle Objectsfor OLE(以下oo4o)を使用する方法
今回はoo4oを使用する方法についてまとめる。
環境
Excel | 2010(32bit) |
OS | Windows7(64bit) |
Oracle | Oracle Database Standard Edition One 11.2.0.4.v16 |
手順
公式サイトに行って、ODACパッケージをダウンロード。
http://www.oracle.com/technetwork/jp/database/windows/downloads/utilsoft-087491-ja.html
パッケージの中に含まれているOracle Objects for OLE(OO4O)を利用する。ODACパッケージを任意のディレクトリへ展開
展開先のディレクトリに存在するinstall.batを起動して、OO4Oをインストール
コマンド実行例ExcelでVBAプロジェクトの参照可能なライブラリファイルの追加
ツールから参照設定、Oracle InProc Server 5.0 Type LibraryにチェックしてOKVBAでマクロを作成
VBA サンプル
下記はOracleに接続して、ホスト名を取得し、セルに出力するサンプル
Sub Sample() 'セッション Dim OracleSession As Object 'データベース Dim OracleDB As Object 'SQL Dim sqlString As String 'ホスト名を取得 sqlString = " SELECT HOST_NAME FROM V$INSTANCE" 'セッションオブジェクト作成(OO4O) Set OracleSession = CreateObject("OracleInProcServer.XOraSession") 'tns名 user pass を指定 Set OracleDB = OracleSession.OpenDatabase("<tns>", "<user>" & "/" & "<pass>", 0&) ' 検索実行 Set objRe = OracleDB.CreateDynaset(sqlString, 0&) ' 0件チェック If objRe.EOF = False Then Dim i As Integer ' ホスト名のフィールドに検索結果が存在すればシートのA列1行目に取得したホスト名を出力 If Not IsNull(objRe.Fields("HOST_NAME").Value) Then Cells(1, "A").Value = objRe.Fields("HOST_NAME").Value End If End If '各オブジェクト開放 objRe.Close Set objRe = Nothing OracleDB.Close Set OracleDB = Nothing Set OracleSession = Nothing End Sub
コマンド実行例
(展開ディレクトリ) > install.bat oo4o c:\oracle orahome true
引数1: 個別にインストールするコンポーネント
引数2: ORACLE_HOME パス
引数3: ORACLE_HOME 名
引数4: 依存性のあるコンポーネントを合わせてインストールするかどうか