AWS ALBを使用し、EC2インスタンスへの通信をHTTPS化しようとした際にはまったこと
やりたかったこと
AWSのALBを使用して、以下のようなHTTPS接続する構成を作ろうと考えていました。
- ALBとEC2インスタンス(Webサーバー)間の転送ルールにパスを使用
https://<ALBのDNS名>/webservice
のような形のみ転送を許可- その他の通信は「404 Not Found」を返すように設定
- セキュリティグループは、EC2インスタンスで使用していたものをALBに適用
はまったこと
https://<ALBのDNS名>/webservice
にアクセスすると、「404 Not Found」が表示される。
解決
許可するパスを /webservice から /webservice* に変更することで解決しました。
よくよく考えれば、https://<ALBのDNS名>/webservice/login_page
とかアクセスするので、許可しておかないと繋がらないですよね。。
以上です。
Gradle初心者によるGradle事始め(ver6.5.1版)
初めに
本記事は、QiitaのGradle初心者によるGradle事始めの記事を、Gradleのバージョン6.5.1用に書き直したものになります。
主な変更点
- コマンドの出力結果
- taskの記載方法
- compileライブラリの非推奨化
Gradleとは
Gadleとは、Groovyで記述するビルド自動化システムです。 JAVAで古くから使われていたAntやMavenはXMLで記述するのに対し、 GradleはGroovyで記述できるため柔軟で協力なビルド自動化システムといえます。
「規約によるビルド」
MavenyたGradleは「規約によるビルド」システムと言われています。 一方、古くからあるmakeやAntは「手続き的なビルド」システムと言われています。
「手続き的なビルド」はソースファイルがあるディレクトリや生成ファイルの 出力先のディレクトリを逐次指定する必要がありましたが、「規約によるビルド」はシステム側に一定のルール(規約)があり、 ビルドシステムを利用する我々はそのルールに則ってソースファイルなど配置します。 ルールに則っていればスクリプトが簡潔だというのが、「規約によるビルド」を採用しているGradleの強みといえるでしょう。
Gradleのインストール
下記の手順でGradleのインストールと確認を行います。
必要な環境
現在のGradle最新版は6.5.1になります。
GradleはJVM言語なので、Javaの環境が必要で、6.5.1はJAVA 8〜14のJDKもしくはJREが必要です。
本手順を最後まで実行するためにはJDKをご準備ください。
手元のPCは「openjdk version "11.0.7"」なので、以下ではこの環境で試します。
ダウンロード
GradleのダウンロードサイトからComplete distributionもしくはBinary only distributionをダウンロードします。
Complete distributionにはドキュメント類も含まれているので、今回はComplete distributionをダウンロードします。
解凍と環境変数設定
ダウンロードしたZIPファイルを適当なディレクトリに解凍して、解凍したディレクトリまでをGRADLE_HOME
という環境変数に設定します。
併せてGRADLE_HOMEのbinディレクトリまでをPATH
環境変数に追加します。
インストール出来たかテストする
コンソールを立ち上げてgradle -version
を実行し、Gradleがインストールできたかテストします。
Welcome to Gradle 6.5.1! Here are the highlights of this release: - Experimental file-system watching - Improved version ordering - New samples For more details see https://docs.gradle.org/6.5.1/release-notes.html ------------------------------------------------------------ Gradle 6.5.1 ------------------------------------------------------------ Build time: 2020-06-30 06:32:47 UTC Revision: 66bc713f7169626a7f0134bf452abde51550ea0a Kotlin: 1.3.72 Groovy: 2.5.11 Ant: Apache Ant(TM) version 1.10.7 compiled on September 1 2019 JVM: 1.8.0_221 (Oracle Corporation 25.221-b11) OS: Windows 10 10.0 x86
このようにバージョン情報が表示されればOKです。
Gradleを入れると、一緒にGroovyとAntが入る用ですね。
プロキシ設定
仕事場でGradleを使う場合、プロキシサーバを設定しないと後々困るのでここで設定します。
今回はユーザごとにGradleの設定を施す。
~/.gradle/gradle.properties
ファイルにプロキシ設定を記述します。
systemProp.http.proxyHost=xxx.xxx.xxx.xxx systemProp.http.proxyPort=9999 systemProp.https.proxyHost=xxx.xxx.xxx.xxx systemProp.https.proxyPort=9999
Gradleのキホン
設定が完了したので、本題に入ります。
以下では適当なところにGradleSample
というディレクトリを作成し、その中で各種のコマンドを実行します。
Gradleの入口
GradleはPATH環境変数に設定したディレクトリにあるgradle
コマンドからすべての操作を実行します。
(すでにgradle -version
は実行しましたね)
手始めに引数無しのgradle
コマンドを実行してみましょう。
Starting a Gradle Daemon (subsequent builds will be faster) > Task :help Welcome to Gradle 6.5.1. To run a build, run gradle <task> ... To see a list of available tasks, run gradle tasks To see a list of command-line options, run gradle --help To see more detail about a task, run gradle help --task <task> For troubleshooting, visit https://help.gradle.org Deprecated Gradle features were used in this build, making it incompatible with Gradle 7.0. Use '--warning-mode all' to show the individual deprecation warnings. See https://docs.gradle.org/6.5.1/userguide/command_line_interface.html#sec:command_line_warnings BUILD SUCCESSFUL in 7s 1 actionable task: 1 executed
引数無しのgradle
コマンドの簡単なヘルプが出力されます。
ビルドをしたければgradle <task>
を実行し、
利用可能なタスクを表示したければgradle tasks
を実行し、
コマンドラインオプションを表示したければgradle --help
を実行し、
それぞれのタスクについて詳細が知りたければgradle help --task <task>
を実行しろと言っています。
<task>
の部分には具体的なタスク名を記述します。
この「タスク」がGradleのキモとなる部分です。
次にこのタスクについてみてみます。
タスク
いまいるGradleSampleディレクトリにbuild.gradle
というファイル作って、テキストエディタで以下の内容を書き込みます。
task hello { doLast { println 'Hello world!' } }
見て迷うことはないでしょうが、そのスクリプトにいま「hello」というタスクがあり、そのタスクはHello world!
を出力します。
これをgradleコマンド実行します。
build.gradleはがデフォルトで読み込むビルドスクリプトですので、gradleコマンドにはスクリプトファイル名を指定する必要ありません。
また今回はログ出力を抑制して内容をスッキリさせるために-q
オプションをつけます。
実行するコマンドはgradle -q hello
です。
>gradle -q hello Hello world!
意図した結果が出力されました。
タスクには、ある処理のまとまりを記述できます。 このタスクを使って、「コンパイルをする」や「テストを実行する」など処理を行います。
タスクの依存関係
Gradleに限らず、ビルドシステムには「タスクの依存関係」は必要不可欠な概念です。
例えばテストを実行するためにはコンパイルが完了していなければなりません。
これは「テスト実行する」というタスクは「コンパイルをする」というタスクに依存している状態です。
この依存関係を、Gradleでは以下のとうに記述します。
task runCompile { doLast { println 'Run compile.' } } task executeTest(dependsOn: runCompile) { doLast { println 'Execute test.' } }
タスク名の後ろに(dependsOn: XXX)
を記述し、XXX
の部分に依存するタスク名を指定します。
このビルドスクリプトを実行します。
指定するタスクは「テストを実行する」ためgradle -q executeTest
です。
>gradle -q executeTest Run compile. Execute test.
executeTestを実行する前に、依存しているrunCompileを実行しているのが分かると思います。
タスクの一覧
Gradleはタスクを指定して実行することはわかっていただけたでしょうが、そのタスクはビルドスクリプトを開かなくてもgradle -q tasks
を実行すればわかります。
>gradle -q tasks ------------------------------------------------------------ Tasks runnable from root project ------------------------------------------------------------ Build Setup tasks ----------------- init - Initializes a new Gradle build. wrapper - Generates Gradle wrapper files. Help tasks ---------- buildEnvironment - Displays all buildscript dependencies declared in root project 'GradleSample'. components - Displays the components produced by root project 'GradleSample'. [incubating] dependencies - Displays all dependencies declared in root project 'GradleSample'. dependencyInsight - Displays the insight into a specific dependency in root project 'GradleSample'. dependentComponents - Displays the dependent components of components in root project 'GradleSample'. [incubating] help - Displays a help message. model - Displays the configuration model of root project 'GradleSample'. [incubating] outgoingVariants - Displays the outgoing variants of root project 'GradleSample'. projects - Displays the sub-projects of root project 'GradleSample'. properties - Displays the properties of root project 'GradleSample'. tasks - Displays the tasks runnable from root project 'GradleSample'. To see all tasks and more detail, run gradle tasks --all To see more detail about a task, run gradle help --task <task>
今回作成したビルドスクリプトの内容が表示されました。
おや?でも、肝心の確認したいタスクのexecuteTest
とrunCompile
がありません。
今度はgradle -q tasks --all
を実行し、すべての情報を出力します。
>gradle -q tasks --all ------------------------------------------------------------ Tasks runnable from root project ------------------------------------------------------------ Build Setup tasks ----------------- init - Initializes a new Gradle build. wrapper - Generates Gradle wrapper files. Help tasks ---------- buildEnvironment - Displays all buildscript dependencies declared in root project 'GradleSample'. components - Displays the components produced by root project 'GradleSample'. [incubating] dependencies - Displays all dependencies declared in root project 'GradleSample'. dependencyInsight - Displays the insight into a specific dependency in root project 'GradleSample'. dependentComponents - Displays the dependent components of components in root project 'GradleSample'. [incubating] help - Displays a help message. model - Displays the configuration model of root project 'GradleSample'. [incubating] outgoingVariants - Displays the outgoing variants of root project 'GradleSample'. projects - Displays the sub-projects of root project 'GradleSample'. properties - Displays the properties of root project 'GradleSample'. tasks - Displays the tasks runnable from root project 'GradleSample'. Other tasks ----------- executeTest prepareKotlinBuildScriptModel runCompile
Other tasks
のところにexecuteTest
とrunCompile
が表示されました。
これが「タスクの依存関係」を表しています。
他の依存関係の表現として行の最後の[XXXX]
があるのですが、これは後ほど説明しあmsう。
Gradleを使ったJavaのビルド
以下ではGradleを使ったJavaソースコードのビルド方法を記します。
先ほどまで使っていたbuild.gradle
は不要なので削除しましょう。
サンプルのプロジェクトを作成
Gradleはinit
タスクでJava/Groovy/Scalaの雛形プロジェクトを作成することができます。
gradle help --task init
を実行して、init
の使い方を調べます。
>gradle help --task init > Task :help Detailed task information for init Path :init Type InitBuild (org.gradle.buildinit.tasks.InitBuild) Options --dsl Set the build script DSL to be used in generated scripts. Available values are: groovy kotlin --package Set the package for source files. --project-name Set the project name. --test-framework Set the test framework to be used. Available values are: junit junit-jupiter kotlintest scalatest spock testng --type Set the type of project to generate. Available values are: basic cpp-application cpp-library groovy-application groovy-gradle-plugin groovy-library java-application java-gradle-plugin java-library kotlin-application kotlin-gradle-plugin kotlin-library pom scala-library swift-application swift-library Description Initializes a new Gradle build. Group Build Setup BUILD SUCCESSFUL in 2s 1 actionable task: 1 executed
今回はJavaのプロジェクトを作成したので、gardle init --type java-library
を実行します。
>gradle init --type java-library Select build script DSL: 1: Groovy 2: Kotlin Enter selection (default: Groovy) [1..2] Select test framework: 1: JUnit 4 2: TestNG 3: Spock 4: JUnit Jupiter Enter selection (default: JUnit 4) [1..4] Project name (default: GradleSample): Source package (default: GradleSample): > Task :init Get more help with your project: https://docs.gradle.org/6.5.1/userguide/java_library_plugin.html BUILD SUCCESSFUL in 12s 2 actionable tasks: 2 executed
言語やフレームワーク、プロジェクト名、ソースパッケージを選択できますが、今回は全てdefault
で実行します。
init
が完了すると、各種ファイルが生成されます。
│ .gitattributes │ .gitignore │ build.gradle │ gradlew │ gradlew.bat │ settings.gradle │ ├─.gradle │ ├─6.5.1 │ │ │ gc.properties │ │ │ │ │ ├─executionHistory │ │ │ executionHistory.bin │ │ │ executionHistory.lock │ │ │ │ │ ├─fileChanges │ │ │ last-build.bin │ │ │ │ │ ├─fileHashes │ │ │ fileHashes.bin │ │ │ fileHashes.lock │ │ │ │ │ └─vcsMetadata-1 │ ├─buildOutputCleanup │ │ buildOutputCleanup.lock │ │ cache.properties │ │ outputFiles.bin │ │ │ ├─checksums │ │ checksums.lock │ │ │ └─vcs-1 │ gc.properties │ ├─gradle │ └─wrapper │ gradle-wrapper.jar │ gradle-wrapper.properties │ └─src ├─main │ ├─java │ │ └─GradleSample │ │ Library.java │ │ │ └─resources └─test ├─java │ └─GradleSample │ LibraryTest.java │ └─resources
色々なディレクトリやファイルが作成されますが、重要なのはbuild.gradle
ファイルと、src
ディレクトリです。
(他のディレクトリとファイルは今回説明を省力します)
ビルドスクリプトの確認
作成されたbuild.gradle
ファイルを見てみます。
/* * This file was generated by the Gradle 'init' task. * * This generated file contains a sample Java Library project to get you started. * For more details take a look at the Java Libraries chapter in the Gradle * User Manual available at https://docs.gradle.org/6.5.1/userguide/java_library_plugin.html */ plugins { // Apply the java-library plugin to add support for Java Library id 'java-library' } repositories { // Use jcenter for resolving dependencies. // You can declare any Maven/Ivy/file repository here. jcenter() } dependencies { // This dependency is exported to consumers, that is to say found on their compile classpath. api 'org.apache.commons:commons-math3:3.6.1' // This dependency is used internally, and not exposed to consumers on their own compile classpath. implementation 'com.google.guava:guava:29.0-jre' // Use JUnit test framework testImplementation 'junit:junit:4.13' }
コメントが多いので今回はコメントを削除します。
plugins { id 'java-library' } repositories { jcenter() } dependencies { api 'org.apache.commons:commons-math3:3.6.1' implementation 'com.google.guava:guava:29.0-jre' testImplementation 'junit:junit:4.13' }
スッキリしました。
Javaプラグイン
plugins { id 'java-library' }
で、これを記述するとJavaのビルドに必要な各種タスクが読み込まれます。
他にも、.projectのようなEclipse固有の設定ファイルを生成するプラグインや、Groovyや、 Scalaなどもありますが、今回は省略します。
plugins { id 'eclipse' }
Groovy
plugins { id 'groovy' }
plugins { id 'scala' }
詳細は、Gradleの公式ユーザーガイドを参照してください。
リポジトリの指定
Javaのプログラムを書く上で、Sprong BootやJUnitやSLF4Jなどのライブラリを使用しないことはないでしょう。
Antの時代は各ライブラリの提供元のサイトから必要なJARファイルをダウンロードしてコンパイル時にローカルのパスを指定していましたが、Mavenの時代からそれはセントラルリポジトリと呼ばれるリモートに置かれ、必要に応じてビルドシステムが自動的にダウンロードするようになりました。
MavenであればMaven Central Repositoryからダウンロードします。
GradleはGadle固有のセントラルリポジトリがあるわけではなく、Mavenなど既存のセントラルリポジトリからライブラリをダウンロードします。
どのセントラルリポジトリを使用するかは、ビルドスクリプトの下記で指定します。
repositories { jcenter() }
これは「BintrayのJCenterにある依存関係を探すための定義済みリポジトリ」を意味します。
今回は、Mavenセントラルリポジトリを参照したいので、これを以下のように変更します。
repositories { mavenCentral() }
依存関係の定義
リポジトリからどのライブラリを使用するのかを定義するのが、ビルドスクリプトの下記の部分です。
dependencies { api 'org.apache.commons:commons-math3:3.6.1' implementation 'com.google.guava:guava:29.0-jre' testImplementation 'junit:junit:4.13' }
ここで言う「依存関係」はライブラリの「依存関係」です。
タスクの依存関係ではありません。
api
は、特に意図せずとも他のプロジェクトに依存関係を伝番させるライブラリ(compile
と同じ様な認識)です。
implementation
は、明示しない限り他のプロジェクトに依存関係を伝番させないライブラリです。テスト時とコンパイル時に使用します。
testImplementation
で指定するのはテスト時に使用するライブラリです。
詳しく知りたい方は、Gradle の compile, api, implementation とかについてを参照してください。
ライブラリの指定は、コロン区切りで'GroupId : ArtifactId : Version'
の3つが必要です。
この3つはMavenセントラルリポジトリにあるものを指定します。
タスクの確認
この状態でタスクの一覧を確認します。
>gradle tasks --all Starting a Gradle Daemon (subsequent builds will be faster) > Task :tasks ------------------------------------------------------------ Tasks runnable from root project ------------------------------------------------------------ Build tasks ----------- assemble - Assembles the outputs of this project. build - Assembles and tests this project. buildDependents - Assembles and tests this project and all projects that depend on it. buildNeeded - Assembles and tests this project and all projects it depends on. classes - Assembles main classes. clean - Deletes the build directory. jar - Assembles a jar archive containing the main classes. testClasses - Assembles test classes. Build Setup tasks ----------------- init - Initializes a new Gradle build. wrapper - Generates Gradle wrapper files. Documentation tasks ------------------- javadoc - Generates Javadoc API documentation for the main source code. Help tasks ---------- buildEnvironment - Displays all buildscript dependencies declared in root project 'GradleSample'. components - Displays the components produced by root project 'GradleSample'. [incubating] dependencies - Displays all dependencies declared in root project 'GradleSample'. dependencyInsight - Displays the insight into a specific dependency in root project 'GradleSample'. dependentComponents - Displays the dependent components of components in root project 'GradleSample'. [incubating] help - Displays a help message. model - Displays the configuration model of root project 'GradleSample'. [incubating] outgoingVariants - Displays the outgoing variants of root project 'GradleSample'. projects - Displays the sub-projects of root project 'GradleSample'. properties - Displays the properties of root project 'GradleSample'. tasks - Displays the tasks runnable from root project 'GradleSample'. Verification tasks ------------------ check - Runs all checks. test - Runs the unit tests. Other tasks ----------- compileJava - Compiles main Java source. compileTestJava - Compiles test Java source. prepareKotlinBuildScriptModel processResources - Processes main resources. processTestResources - Processes test resources. Rules ----- Pattern: clean<TaskName>: Cleans the output files of a task. Pattern: build<ConfigurationName>: Assembles the artifacts of a configuration. Pattern: upload<ConfigurationName>: Assembles and uploads the artifacts belonging to a configuration. BUILD SUCCESSFUL in 8s 1 actionable task: 1 executed
Java用のプラグインを指定しているので、Build tasks
などが追加されています。
ビルドする場合、build
タスク指定します。
ここで先ほど保留した依存するタスクの話をします。
build
タスクの右にある[assemble, check]
となっている部分が依存しているタスクです。
build
タスクの前にassemble
タスク(プロジェクトのすべてのアーカイブを構築)とcheck
タスク(プロジェクトのすべての検証タスクを実行)が実行されます。
assemble
タスクの前にはjar
タスク(当該プロジェクトのJARファイルを構築)が実行され、
jar
タスクの前にはclasses
タスク(製品クラスディレクトリを構築)が実行され、
classes
タスクの前にはcompileJava
タスク(javacを使って製品javaソースファイルをコンパイル)とprocessResources
タスク(製品リソースを製品クラスディレクトリにコピー)が実行されます。
規約
Gradleは「規約によるビルド」をするビルドシステムだと言いましたが、Javaプラグインを使用する場合は下記のようなディレクトリの規約があります。
ディレクトリ | 意味 |
---|---|
src/main/java | 製品のJavaリソース |
src/main/resources | 製品のリソース |
src/test/java | テストのJavaリソース |
src/test/java | テストのリソース |
ここでもう一度init
タスクで作成したディレクトリを見てみます。
└─src ├─main │ ├─java │ │ └─GradleSample │ │ Library.java │ │ │ └─resources └─test ├─java │ └─GradleSample │ LibraryTest.java │ └─resources
init
タスクで作成された「製品のJavaソース」ディレクトリと「テストのJavaソース」ディレクトリが、規約に則ったディレクトリだということが分かってもらえるかと思います。
ビルドの実行
処理を確認するために、ログレベルをinfoに変更するオプション-i
をつけてgradle build -i
で実行しましょう。
>gradle build -i -- 略 -- > Configure project : Evaluating root project 'GradleSample' using build file 'C:\Users\testuser\Desktop\test\GradleSample\build.gradle'. All projects evaluated. Selected primary task 'build' from project : Tasks to be executed: [task ':compileJava', task ':processResources', task ':classes', task ':jar', task ':assemble', task ':compileTestJava', task ':processTestResources', task ':testClasses', task ':test', task ':check', task ':build'] Tasks that were excluded: [] :compileJava (Thread[Execution worker for ':',5,main]) started. > Task :compileJava Caching disabled for task ':compileJava' because: Build cache is disabled Task ':compileJava' is not up-to-date because: Output property 'destinationDirectory' file C:\Users\testuser\Desktop\test\GradleSample\build\classes\java\main has been removed. Output property 'destinationDirectory' file C:\Users\testuser\Desktop\test\GradleSample\build\classes\java\main\GradleSample has been removed. Output property 'destinationDirectory' file C:\Users\testuser\Desktop\test\GradleSample\build\classes\java\main\GradleSample\Library.class has been removed. The input changes require a full rebuild for incremental task ':compileJava'. Full recompilation is required because no incremental change information is available. This is usually caused by clean builds or changing compiler arguments. Compiling with JDK Java compiler API. Created classpath snapshot for incremental compilation in 0.001 secs. :compileJava (Thread[Execution worker for ':',5,main]) completed. Took 0.138 secs. :processResources (Thread[Execution worker for ':',5,main]) started. > Task :processResources NO-SOURCE Skipping task ':processResources' as it has no source files and no previous output files. :processResources (Thread[Execution worker for ':',5,main]) completed. Took 0.001 secs. :classes (Thread[Execution worker for ':',5,main]) started. > Task :classes Skipping task ':classes' as it has no actions. :classes (Thread[Execution worker for ':',5,main]) completed. Took 0.0 secs. :jar (Thread[Execution worker for ':',5,main]) started. > Task :jar Caching disabled for task ':jar' because: Build cache is disabled Task ':jar' is not up-to-date because: Output property 'archiveFile' file C:\Users\testuser\Desktop\test\GradleSample\build\libs\GradleSample.jar has been removed. file or directory 'C:\Users\testuser\Desktop\test\GradleSample\build\resources\main', not found :jar (Thread[Execution worker for ':',5,main]) completed. Took 0.015 secs. :assemble (Thread[Execution worker for ':',5,main]) started. > Task :assemble Skipping task ':assemble' as it has no actions. :assemble (Thread[Execution worker for ':',5,main]) completed. Took 0.0 secs. :compileTestJava (Thread[Execution worker for ':',5,main]) started. > Task :compileTestJava Caching disabled for task ':compileTestJava' because: Build cache is disabled Task ':compileTestJava' is not up-to-date because: Output property 'destinationDirectory' file C:\Users\testuser\Desktop\test\GradleSample\build\classes\java\test has been removed. Output property 'destinationDirectory' file C:\Users\testuser\Desktop\test\GradleSample\build\classes\java\test\GradleSample has been removed. Output property 'destinationDirectory' file C:\Users\testuser\Desktop\test\GradleSample\build\classes\java\test\GradleSample\LibraryTest.class has been removed. The input changes require a full rebuild for incremental task ':compileTestJava'. Full recompilation is required because no incremental change information is available. This is usually caused by clean builds or changing compiler arguments. Compiling with JDK Java compiler API. Created classpath snapshot for incremental compilation in 0.002 secs. :compileTestJava (Thread[Execution worker for ':',5,main]) completed. Took 0.153 secs. :processTestResources (Thread[Execution worker for ':',5,main]) started. > Task :processTestResources NO-SOURCE Skipping task ':processTestResources' as it has no source files and no previous output files. :processTestResources (Thread[Execution worker for ':',5,main]) completed. Took 0.001 secs. :testClasses (Thread[Execution worker for ':',5,main]) started. > Task :testClasses Skipping task ':testClasses' as it has no actions. :testClasses (Thread[Execution worker for ':',5,main]) completed. Took 0.0 secs. :test (Thread[Execution worker for ':',5,main]) started. Gradle Test Executor 2 started executing tests. Gradle Test Executor 2 finished executing tests. > Task :test Caching disabled for task ':test' because: Build cache is disabled Task ':test' is not up-to-date because: Output property 'binaryResultsDirectory' file C:\Users\testuser\Desktop\test\GradleSample\build\test-results\test\binary has been removed. Output property 'binaryResultsDirectory' file C:\Users\testuser\Desktop\test\GradleSample\build\test-results\test\binary\output.bin has been removed. Output property 'binaryResultsDirectory' file C:\Users\testuser\Desktop\test\GradleSample\build\test-results\test\binary\output.bin.idx has been removed. Starting process 'Gradle Test Executor 2'. Working directory: C:\Users\testuser\Desktop\test\GradleSample Command: C:\Program Files\AdoptOpenJDK\jdk-11.0.7.10-hotspot\bin\java.exe -Dorg.gradle.native=false @C:\Users\testuser\AppData\Local\Temp\gradle-worker-classpath3706733076989153461txt -Xmx512m -Dfile.encoding=windows-31j -Duser.country=JP -Duser.language=ja -Duser.variant -ea worker.org.gradle.process.internal.worker.GradleWorkerMain 'Gradle Test Executor 2' Successfully started process 'Gradle Test Executor 2' Finished generating test XML results (0.001 secs) into: C:\Users\testuser\Desktop\test\GradleSample\build\test-results\test Generating HTML test report... Finished generating test html results (0.008 secs) into: C:\Users\testuser\Desktop\test\GradleSample\build\reports\tests\test :test (Thread[Execution worker for ':',5,main]) completed. Took 1.395 secs. :check (Thread[Execution worker for ':',5,main]) started. > Task :check Skipping task ':check' as it has no actions. :check (Thread[Execution worker for ':',5,main]) completed. Took 0.0 secs. :build (Thread[Execution worker for ':',5,main]) started. > Task :build Skipping task ':build' as it has no actions. :build (Thread[Execution worker for ':',5,main]) completed. Took 0.0 secs. BUILD SUCCESSFUL in 3s 4 actionable tasks: 4 executed
実行したタスクが列挙されています。
compileJava
から、build
まで実行されました。
最初は依存ライブラリのダウンロードが発生するかもしれません。
もしプロキシを使用する環境で実行した場合、設定の不備があるとダウンロードに失敗するはずなので、設定を見直してください。
ビルド時に生成されるファイルはbuild
ディレクトリにあります。
今回のテンプレートソースのJARファイルはbuild/libs/GradleSample.jar
です。
実行したタスクにtest
があるのでテストは実行されたはずですが、念のためにテストクラスでエラーを起こしてみます。
src/test/java/GradleSample/LibraryTest.java
を修正します。
修正前は下記のソースでした。
package GradleSample; import org.junit.Test; import static org.junit.Assert.*; public class LibraryTest { @Test public void testSomeLibraryMethod() { Library classUnderTest = new Library(); assertTrue("someLibraryMethod should return 'true'", classUnderTest.someLibraryMethod()); } }
これにエラーとなる処理を入れます。
package GradleSample; import org.junit.Test; import static org.junit.Assert.*; public class LibraryTest { @Test public void testSomeLibraryMethod() { Library classUnderTest = new Library(); assertTrue("someLibraryMethod should return 'true'", classUnderTest.someLibraryMethod()); assertTrue(false); } }
assertTrue(false);
でテストは失敗するはず。
gradle build -i
を実行します。
>gradle build > Task :test FAILED GradleSample.LibraryTest > testSomeLibraryMethod FAILED java.lang.AssertionError at LibraryTest.java:14 1 test completed, 1 failed FAILURE: Build failed with an exception. * What went wrong: Execution failed for task ':test'. > There were failing tests. See the report at: file:///C:/Users/testuser/Desktop/test/GradleSample/build/reports/tests/test/index.html * Try: Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights. * Get more help at https://help.gradle.org BUILD FAILED in 3s 4 actionable tasks: 1 executed, 3 up-to-date
テストが失敗しました。
なのでここまで最初に言った「JavaのソースコードをコンパイルしてJARファイルにまとめてテストを実行するまで」が達成しました。
終わりに
私は、開発の知識がまるで無い状態で開発の自動化(CICD)の業務を担当することになりました。
Gradleの開発自動化といわれても、Gradleって何?という状態だった私はパニックでした。
Gradleについて何から勉強してよいかわからない状態だったのですが、そんな時に上記の記事を見かけて学習の取っ掛かりにできました。
以上です。
Jenkinsからserverspecを実行しようとしたら、ポート確認で失敗した話
目的
serverspecの手動実行時のチェック結果と、Jenkinsから実行したチェック結果に差異が生じました。
まったく同じサーバー、ユーザー、コマンドで実施したが結果が違ったため原因を調査する。
前提条件
バージョン
・rake:13.0.1
・ruby:2.7.0p0
・Jenkins:2.220
AWS上にJenkisサーバ、webサーバーをそれぞれ準備
・公開鍵を登録し、特定のユーザーのみパスワード無しで、sshログインできる
・2サーバ間のセキュリティは全アクセスを許可済み
serverspecのチェック内容
require 'spec_helper' describe package('nginx') do it { should be_installed } end describe service('nginx') do it { should be_enabled } it { should be_running } end describe port(80) do it { should be_listening } end describe file('/usr/share/nginx/html/index.html') do it { should be_file } it { should exist } its(:content) { should match /^Hello, development ansible!!$/ } end
詳細事象
wedサーバー上でrake spec
を実行したところ、以下のように0 failuresで実行できました。
/home/ec2-user/.rbenv/versions/2.7.0/bin/ruby -I/home/ec2-user/.rbenv/versions/2.7.0/lib/ruby/gems/2.7.0/gems/rspec-support-3.9.2/lib:/home/ec2-user/.rbenv/versions/2.7.0/lib/ruby/gems/2.7.0/gems/rspec-core-3.9.1/lib /home/ec2-user/.rbenv/versions/2.7.0/lib/ruby/gems/2.7.0/gems/rspec-core-3.9.1/exe/rspec --pattern spec/localhost/\*_spec.rb Package "nginx" is expected to be installed Service "nginx" is expected to be enabled is expected to be running Port "80" is expected to be listening File "/usr/share/nginx/html/index.html" is expected to be file is expected to exist content is expected to match /^Hello, development ansible!!$/ Finished in 0.09714 seconds (files took 0.55084 seconds to load) 7 examples, 0 failures
しかし、Jenkinsで同じユーザーでログインし、rake spec
を実行したところ以下の結果になりました。
[SSH] executing... /home/ansible/.rbenv/versions/2.7.0/bin/ruby -I/home/ansible/.rbenv/versions/2.7.0/lib/ruby/gems/2.7.0/gems/rspec-support-3.9.2/lib:/home/ansible/.rbenv/versions/2.7.0/lib/ruby/gems/2.7.0/gems/rspec-core-3.9.1/lib /home/ansible/.rbenv/versions/2.7.0/lib/ruby/gems/2.7.0/gems/rspec-core-3.9.1/exe/rspec --pattern spec/localhost/\*_spec.rb failed /home/ansible/.rbenv/versions/2.7.0/bin/ruby -I/home/ansible/.rbenv/versions/2.7.0/lib/ruby/gems/2.7.0/gems/rspec-support-3.9.2/lib:/home/ansible/.rbenv/versions/2.7.0/lib/ruby/gems/2.7.0/gems/rspec-core-3.9.1/lib /home/ansible/.rbenv/versions/2.7.0/lib/ruby/gems/2.7.0/gems/rspec-core-3.9.1/exe/rspec --pattern spec/localhost/\*_spec.rb Package "nginx" is expected to be installed Service "nginx" is expected to be enabled is expected to be running Port "80" is expected to be listening (FAILED - 1) File "/usr/share/nginx/html/index.html" is expected to be file is expected to exist content is expected to match /^Hello, production ansible!!$/ Failures: 1) Port "80" is expected to be listening On host `localhost' Failure/Error: it { should be_listening } expected Port "80" to be listening /bin/sh -c ss\ -tunl\ \|\ grep\ -E\ --\ :80\\\ # ./spec/localhost/web_spec.rb:13:in `block (2 levels) in <top (required)>' Finished in 0.11224 seconds (files took 0.54982 seconds to load) 7 examples, 1 failure Failed examples: rspec ./spec/localhost/web_spec.rb:13 # Port "80" is expected to be listening [SSH] completed [SSH] exit-status: 1
まったく同じコマンドを実行したはずですが、1 failureになってしまいました。
どうも80番ポートのチェックで失敗しているように見えます。
切り分け
実施コマンドの確認
エラーメッセージを確認すると以下のメッセージが表示されていることから、 ssコマンドが実行されていることがわかりました。
/bin/sh -c ss\ -tunl\ \|\ grep\ -E\ --\ :80\\\
webサーバーの対象ユーザーからssコマンドを実行してみます。
$ ss -tunl Netid State Recv-Q Send-Q Local Address:Port Peer Address:Port udp UNCONN 0 0 0.0.0.0:725 0.0.0.0:* udp UNCONN 0 0 127.0.0.1:323 0.0.0.0:* udp UNCONN 0 0 0.0.0.0:68 0.0.0.0:* udp UNCONN 0 0 0.0.0.0:111 0.0.0.0:* udp UNCONN 0 0 [::]:725 [::]:* udp UNCONN 0 0 [::1]:323 [::]:* udp UNCONN 0 0 [fe80::4db:f6ff:fee3:8fec]%eth0:546 [::]:* udp UNCONN 0 0 [::]:111 [::]:* tcp LISTEN 0 128 0.0.0.0:80 0.0.0.0:* tcp LISTEN 0 128 0.0.0.0:22 0.0.0.0:* tcp LISTEN 0 100 127.0.0.1:25 0.0.0.0:* tcp LISTEN 0 128 0.0.0.0:111 0.0.0.0:* tcp LISTEN 0 128 [::]:80 [::]:* tcp LISTEN 0 128 [::]:22 [::]:* tcp LISTEN 0 128 [::]:111 [::]:*
当然実行できました。
次に、Jenkisサーバからsshコマンドにssコマンドを渡して実行してみます。
$ ssh <対象ユーザー>@<webサーバーIP> "ss -tunl" bash: ss: command not found
失敗しました。。コマンドが存在しない???
コマンドの渡し方が間違っていたのか確認しましたが、あってそうです。
ssコマンドをフルパスで実行してみます。
$ which ss /sbin/ss $ ssh <対象ユーザー>@<webサーバーIP> "/sbin/ss -tunl" Netid State Recv-Q Send-Q Local Address:Port Peer Address:Port udp UNCONN 0 0 0.0.0.0:725 0.0.0.0:* udp UNCONN 0 0 127.0.0.1:323 0.0.0.0:* udp UNCONN 0 0 0.0.0.0:68 0.0.0.0:* udp UNCONN 0 0 0.0.0.0:111 0.0.0.0:* udp UNCONN 0 0 [::]:725 [::]:* udp UNCONN 0 0 [::1]:323 [::]:* udp UNCONN 0 0 [fe80::4db:f6ff:fee3:8fec]%eth0:546 [::]:* udp UNCONN 0 0 [::]:111 [::]:* tcp LISTEN 0 128 0.0.0.0:80 0.0.0.0:* tcp LISTEN 0 128 0.0.0.0:22 0.0.0.0:* tcp LISTEN 0 100 127.0.0.1:25 0.0.0.0:* tcp LISTEN 0 128 0.0.0.0:111 0.0.0.0:* tcp LISTEN 0 128 [::]:80 [::]:* tcp LISTEN 0 128 [::]:22 [::]:* tcp LISTEN 0 128 [::]:111 [::]:*
実行できました!
どうもパスが通っていないように感じますね。
普通にコマンド実行する場合とssh越しにコマンド実行する場合で、環境変数に差があるか確認してみます。
<通常実行時(webサーバ)>
$ env XDG_SESSION_ID=4 HOSTNAME= SHELL=/bin/bash TERM=xterm HISTSIZE=1000 USER=ansible LS_COLORS=rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:mi=01;05;37;41:su=37;41:sg=30;43:ca=30;41:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31:*.tgz=01;31:*.arc=01;31:*.arj=01;31:*.taz=01;31:*.lha=01;31:*.lz4=01;31:*.lzh=01;31:*.lzma=01;31:*.tlz=01;31:*.txz=01;31:*.tzo=01;31:*.t7z=01;31:*.zip=01;31:*.z=01;31:*.Z=01;31:*.dz=01;31:*.gz=01;31:*.lrz=01;31:*.lz=01;31:*.lzo=01;31:*.xz=01;31:*.bz2=01;31:*.bz=01;31:*.tbz=01;31:*.tbz2=01;31:*.tz=01;31:*.deb=01;31:*.rpm=01;31:*.jar=01;31:*.war=01;31:*.ear=01;31:*.sar=01;31:*.rar=01;31:*.alz=01;31:*.ace=01;31:*.zoo=01;31:*.cpio=01;31:*.7z=01;31:*.rz=01;31:*.cab=01;31:*.jpg=01;35:*.jpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35:*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35:*.svg=01;35:*.svgz=01;35:*.mng=01;35:*.pcx=01;35:*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.m2v=01;35:*.mkv=01;35:*.webm=01;35:*.ogm=01;35:*.mp4=01;35:*.m4v=01;35:*.mp4v=01;35:*.vob=01;35:*.qt=01;35:*.nuv=01;35:*.wmv=01;35:*.asf=01;35:*.rm=01;35:*.rmvb=01;35:*.flc=01;35:*.avi=01;35:*.fli=01;35:*.flv=01;35:*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.yuv=01;35:*.cgm=01;35:*.emf=01;35:*.axv=01;35:*.anx=01;35:*.ogv=01;35:*.ogx=01;35:*.aac=01;36:*.au=01;36:*.flac=01;36:*.mid=01;36:*.midi=01;36:*.mka=01;36:*.mp3=01;36:*.mpc=01;36:*.ogg=01;36:*.ra=01;36:*.wav=01;36:*.axa=01;36:*.oga=01;36:*.spx=01;36:*.xspf=01;36: SUDO_USER=ec2-user SUDO_UID=1000 USERNAME=root PATH=/sbin:/bin:/usr/sbin:/usr/bin MAIL=/var/spool/mail/ec2-user PWD=/home/ec2-user LANG=en_US.UTF-8 SHLVL=1 SUDO_COMMAND=/bin/su ansible HOME=/home/ansible LOGNAME=ansible LESSOPEN=||/usr/bin/lesspipe.sh %s SUDO_GID=1000 _=/bin/env
<ssh越しにenvコマンド実行時(Jenkinsサーバ)>
$ ssh <対象ユーザー>@<webサーバーIP> "env" XDG_SESSION_ID=44 SHELL=/bin/bash SSH_CLIENT= USER=ansible MAIL=/var/mail/ansible PATH=/usr/local/bin:/usr/bin PWD=/home/ansible LANG=en_US.UTF-8 SHLVL=1 HOME=/home/ansible LOGNAME=ansible SSH_CONNECTION= LESSOPEN=||/usr/bin/lesspipe.sh %s XDG_RUNTIME_DIR=/run/user/1001 _=/usr/bin/env
予想通り、PATHに差異があることがわかりました。
<通常実行時(webサーバ)> PATH=/sbin:/bin:/usr/sbin:/usr/bin <ssh越しにenvコマンド実行時(Jenkinsサーバ)> PATH=/usr/local/bin:/usr/bin
解決
問題を解決すべく、ssh時の環境変数について調べたところ以下のブログが見つかりました。
ssh-env - ssh実行時に環境変数を設定/変更したい - spikelet days
上記ブログに記載してある以下の方法を試します。
まずは、webサーバの /etc/ssh/sshd_config を編集します。
109行目あたりにある PermitUserEnvironment no を PermitUserEnvironment yes に変更します。
#変更前の設定内容を確認 $ sudo grep PermitUserEnvironment /etc/ssh/sshd_config #PermitUserEnvironment no #PermitUserEnvironmentをyesに変更 $ sudo vi /etc/ssh/sshd_config #変更されていることを確認 $ sudo grep PermitUserEnvironment /etc/ssh/sshd_config PermitUserEnvironment yes
次に、~/.ssh/environment に環境変数を書く (VAR=VAL 形式で一行一変数で) を実行します。
sshでログインしたいユーザーのホームディレクトリ配下にある ~/.ssh に移動し、
設定したい環境変数を記載した environment ファイルを作成します。
今回は、コマンドを実行するために PATH= の値を変更したいので、 PATH=/sbin:/bin:/usr/sbin:/usr/bin を記載します。
#ホームディレクトリは以下の.sshディレクトリに移動 $ cd ~/.ssh #設定する環境変数(PATH=/sbin:/bin:/usr/sbin:/usr/bin)を記載 $ vi environment #記載内容の確認 $ cat environment PATH=/sbin:/bin:/usr/sbin:/usr/bin
これで、設定完了です。
試しに、Jenkinsサーバからsshコマンド越しにserverspecを実行してみます。
ssh <ユーザー名>@<IPアドレス> "ss -tunl" NetidState Recv-Q Send-Q Local Address:Port Peer Address:Port udp UNCONN 0 0 0.0.0.0:111 0.0.0.0:* udp UNCONN 0 0 0.0.0.0:724 0.0.0.0:* udp UNCONN 0 0 127.0.0.1:323 0.0.0.0:* udp UNCONN 0 0 0.0.0.0:68 0.0.0.0:* udp UNCONN 0 0 [::]:111 [::]:* udp UNCONN 0 0 [::]:724 [::]:* udp UNCONN 0 0 [::1]:323 [::]:* udp UNCONN 0 0 [fe80::4db:f6ff:fee3:8fec]%eth0:546 [::]:* tcp LISTEN 0 128 0.0.0.0:111 0.0.0.0:* tcp LISTEN 0 128 0.0.0.0:80 0.0.0.0:* tcp LISTEN 0 128 0.0.0.0:22 0.0.0.0:* tcp LISTEN 0 100 127.0.0.1:25 0.0.0.0:* tcp LISTEN 0 128 [::]:111 [::]:* tcp LISTEN 0 128 [::]:80 [::]:* tcp LISTEN 0 128 [::]:22 [::]:*
実行できました!!
次は、いよいよJenkinsのGUIからjobを実行してみます。
Started by user root Running as SYSTEM Building in workspace /var/lib/jenkins/workspace/serverspec jobtest [SSH] script: cd /tmp/serverspec_sample ~/.rbenv/shims/rake spec [SSH] executing... /home/ansible/.rbenv/versions/2.7.0/bin/ruby -I/home/ansible/.rbenv/versions/2.7.0/lib/ruby/gems/2.7.0/gems/rspec-support-3.9.2/lib:/home/ansible/.rbenv/versions/2.7.0/lib/ruby/gems/2.7.0/gems/rspec-core-3.9.1/lib /home/ansible/.rbenv/versions/2.7.0/lib/ruby/gems/2.7.0/gems/rspec-core-3.9.1/exe/rspec --pattern spec/localhost/\*_spec.rb Port "80" is expected to be listening Package "nginx" is expected to be installed Service "nginx" is expected to be enabled is expected to be running Port "80" is expected to be listening File "/usr/share/nginx/html/index.html" is expected to be file is expected to exist content is expected to match /^Hello, development ansible!!$/ Finished in 0.08618 seconds (files took 1.01 seconds to load) 8 examples, 0 failures [SSH] completed [SSH] exit-status: 0 Started calculate disk usage of build Finished Calculation of disk usage of build in 0 seconds Started calculate disk usage of workspace Finished Calculation of disk usage of workspace in 0 seconds Finished: SUCCESS
成功しました!!
余談
今回色々調べている中で、以下のようにパイプを使用して、ssh越しにコマンドを実行する方法があるのを知りました。
echo `ss -tunl` | ssh <ユーザー名>@<IPアドレス>
このコマンドだと、上記の解決を実施しなくてもコマンド実行できました。
これも環境変数が違うのかと思い、envコマンドを実行してみました。
$ echo "env" | ssh ansible@172.31.23.243 Pseudo-terminal will not be allocated because stdin is not a terminal. XDG_SESSION_ID=24 HOSTNAME=ansible-server.tk.com SHELL=/bin/bash HISTSIZE=1000 SSH_CLIENT=172.31.18.127 54540 22 USER=ansible MAIL=/var/spool/mail/ansible PATH=/home/ansible/.rbenv/shims:/home/ansible/.rbenv/bin:/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/home/ansible/.local/bin:/home/ansible/bin PWD=/home/ansible LANG=en_US.UTF-8 RBENV_SHELL=bash HISTCONTROL=ignoredups SHLVL=1 HOME=/home/ansible LOGNAME=ansible SSH_CONNECTION=172.31.18.127 54540 172.31.23.243 22 LESSOPEN=||/usr/bin/lesspipe.sh %s XDG_RUNTIME_DIR=/run/user/1001 _=/bin/env
今までのどれとも違う環境変数が出力されました。。
少し調べてみましたが、結論が出ませんでした。 時間のある時に調べて、原因がわかり次第追記しようと思います。
以上です。
AWS lambdaで英語の音声ファイルを日本語のテキストに翻訳する
やりたいこと
以下の画像のような流れで英語の音声ファイルを日本語のテキストに翻訳したい。
前回書いた記事で作成した機能を組み合わせたような形になります。
①英語の音声ファイルを手動でS3のinputバケットへアップロード
②inputバケットにファイルがアップロードされたことを検知し、文字起こし用Lambdaが実行される
③Amazon Transcribeが実行され、音声ファイルから文字起こしファイルを作成します
④文字起こし結果をoutput用バケットに格納する
⑤output用バケットにファイルが作成されたことを検知し、翻訳用Lambdaが実行される
⑥翻訳結果をtranslate用バケットに格納する
構築
必要なバケットを準備
input用とoutput用のバケットは、前回作成しているので今回はtranslate用バケットのみ作成します。
※作成方法自体は、全てのバケット共通です。
こいつですね。
1.S3のコンソール画面に移動します
2.バケットの作成をクリックします
4.画面左下の作成をクリックします
文字起こし用Lambdaの準備
文字起こし用のLambdaは、前回作成した以下のLambdaを使用します。
こいつですね。
import json import urllib.parse import boto3 import datetime s3 = boto3.client('s3') transcribe = boto3.client('transcribe') def lambda_handler(event, context): #バケット名を取得 bucket = event['Records'][0]['s3']['bucket']['name'] #オブジェクトのキーを取得 key = urllib.parse.unquote_plus(event['Records'][0]['s3']['object']['key'], encoding='utf-8') try: transcribe.start_transcription_job( #文字起こしのオブジェクト名を作成 TranscriptionJobName= datetime.datetime.now().strftime("%Y%m%d%H%M%S") + '_Transcription', LanguageCode='en-US', #文字起こし用の音声ファイルを取得 Media={ 'MediaFileUri': 'https://s3.ap-northeast-1.amazonaws.com/' + bucket + '/' + key }, #作成したファイルを保存するバケット OutputBucketName=<文字起こしファイルの保存用バケット> ) except Exception as e: print(e) print('Error getting object {} from bucket {}. Make sure they exist and your bucket is in the same region as this function.'.format(key, bucket)) raise e
Amazon Transcribeを使用し、音声を文字起こしします。
ちなみに、現在Amazon Transcribeに対応している言語は以下になります。
https://docs.aws.amazon.com/transcribe/latest/dg/what-is-transcribe.html
S3のinput用バケットがトリガーになっていることを確認し、完了です。
翻訳用lambdaの作成
今回のメインですね。
1. Lambdaのコンソールに移動します
2. 関数を作成をクリックします
3. 設計図の使用にチェックを入れ、フィルターにS3と入力します
4. 表示されたs3-get-object-pythonをクリックし、最下行の設定をクリックします
5. 関数名とロール名を入力し、ポリシーテンプレートからAmazon S3 オブジェクトの読み取り専用アクセス権限を選択します
6. バケットで自分で作成したoutput用バケットを選択します
7. 最下行の関数の作成をクリックします
8. 表示された関数コードを以下のコードに変更します
import json import urllib.parse import boto3 import datetime print('Loading function') s3 = boto3.client('s3') def lambda_handler(event, context): #バケット名を取得 bucket = event['Records'][0]['s3']['bucket']['name'] #オブジェクトのキーを取得 key = urllib.parse.unquote_plus(event['Records'][0]['s3']['object']['key'], encoding='utf-8') try: translate = boto3.client('translate') #翻訳前のオブジェクトを取得 input_text = s3.get_object(Bucket = bucket, Key = key) #jsonファイル内の翻訳対象が含まれるBodyのみを抜粋 input_text = json.loads(input_text["Body"].read().decode('utf-8')) #さらに翻訳対象絞り込み、英語から日本語に変換 response = translate.translate_text( Text=input_text["results"]["transcripts"][0]["transcript"], SourceLanguageCode="en", TargetLanguageCode="ja" ) except Exception as e: print(e) print('Error getting object {} from bucket {}. Make sure they exist and your bucket is in the same region as this function.'.format(key, bucket)) raise e #作成したファイルを保存するオブジェクト名 TranslateJobName= datetime.datetime.now().strftime("%Y%m%d%H%M%S") + '_Translate' #作成したファイルを保存するバケット名 OutputBucketName=<翻訳ファイルの保存用バケット> #オブジェクトの内容 output_text = response.get('TranslatedText') #翻訳した内容をtranslate用バケットに保存 response = s3.put_object(Bucket=OutputBucketName, Key=TranslateJobName, Body=output_text.encode()) return { 'statusCode': 200, 'body': json.dumps({ 'output_text': output_text }) }
9. 実行ロールの設定を変更します
IAM コンソールで <設定したロール名>ロールを表示します。をクリックします
ポリシーをアッタッチしますをクリックします
以下のポリシーを検索し、左側にチェックを入れ、右下のポリシーをアタッチをクリックします
・TranslateFullAccess
・AmazonS3FullAccess
以上で設定は完了(のはず)です。
実践
では、実際に試してみます。 使用する音声ファイルは、前回と同じAmazon Pollyのサンプルを使用します。
1. 音声ファイルをinput用のS3バケットにアップロードします。
2. Amazon Transcribeの画面を確認し、ジョブが実行されていることを確認します。
3. output用のS3バケットに文字起し結果のファイルが作成されていることを確認します
4. 少し待ち、translate用バケットにファイルが新規作成されていることを確認します
5. ファイルをダウンロードし、中身が
こんにちは。 外国語を話せますか。 一つの言語では十分ではありません。
となっていれば成功です!
もし、上手くいかない場合は、被疑箇所のLambdaのモニタリングのCloudWatch Logs Insightsから実行結果ログを確認し、問題を特定してください。
以上です。
-
リージョンはlambdaと同じにする必要があります↩
手を動かしながら学ぶサーバーレスハンズオンをやってみた
初めに
私が、AWSを学習したudemyの講座の講師をされていた
金澤さんのハンズオンだったので、凄く楽しみにしていたのですが、
ハンズオンに行けなかったため自己学習です。
本記事は、以下の勉強会で実施された内容です。
https://jawsug-bgnr.connpass.com/event/165065/
"サーバーレス"とは何か?何がうれしいのか?
開発者がやりたいこととやらなければいこと
サーバーレスでは、サーバの管理が不要なため、
開発者はユーザーに価値を届けることに集中できる!
サーバーレスアーキテクチャの特徴
- インフラのプロビジョニングや管理が不要
- ビルドインの高可用性
- 自動でスケール
- 価値に対する支払い
AWS Lamdbaで 日→英 翻訳します
シナリオ#1 日→英翻訳をすぐさまやりたい
- オリンピックのある今年は英語を使う機会も多い
- 英語の聞き取りは大丈夫、でも喋るのが。。
- でもいつその機会に遭遇するかはわからない
- コストは少しでも押さえたい
AWSにおけるComputeサービス
- EC2
- 自由度:高い
- 管理の手間:多い
- Lambda
- 自由度:低い
- 管理の手間:少ない
AWS Lambdaの特徴
- サーバーの
プロビジョニング/管理なし
で、プログラムを実行できるサービス - コードを実行するための準備、スケーリングなどは、Lambda側で実施
開発者の方はコードを書くことに集中
できる - 料金体系は
リクエストベース
- 実行回数 + 実行時間(単価は確保したメモリ量)
※無料枠あり
- 実行回数 + 実行時間(単価は確保したメモリ量)
ハンズオン
AWS Lambdaを用い、下記の2つの作業を進めていく
- Lambda Functionを作成し、"Hello World"と表示します
- Lambda Functionを修正し、Amazon Translateと連携します
Lambda Functionを作成し、"Hello World"と表示します
IAMユーザでログイン&初期設定
- 右上:
東京
リージョンを選択 - 画面左下の
English(US)
を日本語
に変更
- 右上:
AWS Lambda画面へ
- サービス検索窓にLambdaと入力し、下に出てくるLambdaをクリックします
Lambda Functionの作成
右上の橙色の
関数の作成
をクリックします一から作成
のままでOK関数の情報を入力し、右下の
関数の作成
をクリックします- 関数名:
translate-function
- ランタイム:
Python3.8
- 関数名:
少し下にスクロールするとソースコードが見えてくる
- 7行目の"Hello from Lambda!"の中身を何でも良いので
修正
します - 右上の
保存
ボタンをクリックします
Lambda Functionをテスト実行します
- 右上の
テスト
をクリックします - イベント名に
Test
と入力します - 右下の
作成
ボタンをクリックします - 右上の
テスト
ボタンをクリックします statusCode: 200,
が表示されれば成功です
- 右上の
Lambda Functionを修正し、Amazon Translateと連携します
- Lambda Function のIAMロールを修正します
- Lambda Function 画面の下の方にスクロールしてください
xxx ロール表示します
をクリックしますポリシーをアタッチします
をクリックします- 検索窓に
translate
と入力し、
TranslateFullAccess
の方にチェックを入れます - その後、右下の
ポリシーのアタッチ
をクリックします - Lambda画面に戻ります
- Lambda Functionのソースコードを修正
import json import boto3 def lambda_handler(event, context): translate = boto3.client('translate') input_text = '順調ですか?' response = translate.translate_text( Text=input_text, SourceLanguageCode='ja', TargetLanguageCode='en' ) output_text = response.get('TranslatedText') return { 'statusCode': 200, 'body': json.dumps({ 'output_text': output_text }) }
保存
>テスト
をクリックします- 翻訳結果が出ていればOKです
input_test の文字列を変更し、色々試してください
- 翻訳結果が出ていればOKです
翻訳 Web APIを作る
シナリオ#2 日→英 翻訳をWeb API化したい
- 翻訳したいときに常にマネージメントコンソールにログインできるとは限らない
- スマホからでもサクッと接続できるようにWeb API 化したい
- いつでも使えないと困るけど、冗長性のための設計はしている時間がない...
サーバーレスアーキテクチャでよく利用されるAWSサービス
- AWS Lambda
- Amazon API Gateway
- Amazon DynamoDB
- Amazon S3
- AWS Step Function
- Amazon SQS
- Amazon CloudWatch
- AWS X-Ray
Amazon API Gatewayの特徴
- サーバーのプロビジョニングや管理無しで、
APIを作成・管理
できるマネージドサービス - 可用性の担保、スケーリングなどのAPI管理で必要なことをAPI Gatewayに任せる事で、
開発者はビジネスの差別化につながる作業に集中
できます - 料金体系はリクエストベース(※REST APIの場合)
- 実行回数 + キャッシュメモリ + データ転送料金
ハンズオン
Amazon API GatewayとAWS lambdaを組み合わせて、外部から叩けるWeb APIを作って行きます。
import json import boto3 def lambda_handler(event, context): translate = boto3.client('translate') input_text = event['queryStringParameters']['input_text'] response = translate.translate_text( Text=input_text, SourceLanguageCode="ja", TargetLanguageCode="en" ) ...
-
アクション
>リソースの作成
をクリックします- "リソース名"を
translate
とし、リソースの作成
をクリックします
-
/translate
が選ばれている状態で、アクション
>メソッドの作成
をクリックします- "/translate"の下のプルダウンから、
GET
を選択し、チェックマーク
をクリックします Lambdaプロキシ統合の使用
をチェック
したのち、
"Lambda 関数"にtranslate-function
を指定し、保存をクリックします- "Lambda 関数に権限を追加する"ダイアログで、
OK
をクリックします
文字起こし + 翻訳パイプラインを作る
シナリオ#3 英文の文字起こしをしたい
- さっき聞き取りは問題ないといったけど、嘘でした...
- 喋ってもらった内容をテキスト化できないと辞書も引けない
- まず文字お越しするところだけでも自動化できないだろうか
Amazon Transcribeの特徴
- 音声をテキストにする文字お越しサービス
- 2019年11月に日本語対応
- 保存した音声ファイルやリアルタイム変換が可能 5
Amazon Simple Storage Service(Amazon S3)の特徴
高い耐久性
を持つオブジェクトストレージサービス99.999999999%
(イレブンナイン)- 標準で少なくとも3つのAZに格納されている
- 容量無制限、安価なストレージ
様々なAWSサービスと連携
- 例:ファイルが格納されたことをトリガーに非同期にLambda関数を呼ぶ
ハンズオン
AWS LambdaとAmazon Transcribeを組み合わせて、音声データを文字お越しするパイプラインを作ります
1.S3のバケットを作成する
- input用とoutput用に2つのS3バケットを作成します
-
サービス
>S3
と入力 >S3
を選択 -
バケットを作成する
をクリックします - バケット名を
yyymmdd-transcribe-input-yourname
とする6 リージョンは、lambdaと同じにしてください
2.Lambda Functionの作成
- AWS Lambdaの画面へ:
サービス
>lambda
と入力 >Lambda
を選択します -
関数の作成
をクリックします -
設計図の使用
> 検索窓にS3
と入力し、Enter
-
s3-get-object-python
を選択し、右下の設定
をクリックします - Function名:
transcribe-function
,
Role 名:transcribe-function-role
とし、下へ移動します - S3 トリガーの対象になる
input用のバケットを指定
します -
トリガーの有効化
にチェック
をつけます - 下部の
関数の作成
をクリックします - IAM ロールを修正する
Lambda Functionの下部からxxx ロールを表示
をクリックします -
ポリシーをアタッチします
をクリックします - 検索窓に
S3
と入力 >AmazonS3FullAccess
にチェックします - 検索窓をクリアし、
transcribe
と入力 >AmazonTranscribeFullAccess
にチェックします - ソースコードを以下のように修正し、
保存
するOutputBucketName
にはOutputバケット名を入力してください
import json import urllib.parse import boto3 import datetime s3 = boto3.client('s3') transcribe = boto3.client('transcribe') def lambda_handler(event, context): bucket = event['Records'][0]['s3']['bucket']['name'] key = urllib.parse.unquote_plus(event['Records'][0]['s3']['object']['key'], encoding='utf-8') try: transcribe.start_transcription_job( TranscriptionJobName= datetime.datetime.now().strftime("%Y%m%d%H%M%S") + '_Transcription', LanguageCode='en-US', Media={ 'MediaFileUri': 'https://s3.ap-northeast-1.amazonaws.com/' + bucket + '/' + key }, OutputBucketName='YYYYMMDD-transcribe-output-YourName' ) except Exception as e: print(e) print('Error getting object {} from bucket {}. Make sure they exist and your bucket is in the same region as this function.'.format(key, bucket)) raise e
3.パイプラインのテストを実施する
- 音声ファイルは下記URL(Amazon Pollyのサンプル)から、ダウンロードします
S3にmp3ファイルをアップロードする
input側のS3バケットでアップロード
をクリックしますファイルを追加
から先ほどDLしたmp3ファイルを指定し、アップロード
しますサービス
>transcribe
と入力 >Amazon Trabscribe
を選択します左上の
ハンバーガーメニュー
をクリックし >Transcription Jobs
をクリックしますJob Statusが
In progress
であることを確認し、Complete
になるまで待ちますJobが完了したら、Output側のS3を確認します
yyyymmddhhmmss_Transcription.jsonファイルがあれば成功です- Jsonファイルの中の resulte > transcripts > transcriptを確認します
“transcript”: “Hello. Do you speak a foreign language? One language is never enough.”と文字起こしされていると思います。{"jobName":"20200220074630_Transcription","accountId":"XXXXXXXXXXXX","results":{"transcripts":[{"transcript":"Hello. Do you speak a foreign language? One language is never enough."}],"items":[{"start_time":"0.04","end_time":"0.65","alternatives":[{"confidence":"0.9139","content":"Hello"}],"type":"pronunciation"},{"alternatives":[{"confidence":"0.0","content":"."}],"type":"punctuation"},{"start_time":"1.04","end_time":"1.14","alternatives":[{"confidence":"1.0","content":"Do"}],"type":"pronunciation"},{"start_time":"1.14","end_time":"1.27","alternatives":[{"confidence":"1.0","content":"you"}],"type":"pronunciation"},{"start_time":"1.27","end_time":"1.59","alternatives":[{"confidence":"1.0","content":"speak"}],"type":"pronunciation"},{"start_time":"1.59","end_time":"1.65","alternatives":[{"confidence":"0.9991","content":"a"}],"type":"pronunciation"},{"start_time":"1.65","end_time":"1.99","alternatives":[{"confidence":"1.0","content":"foreign"}],"type":"pronunciation"},{"start_time":"1.99","end_time":"2.59","alternatives":[{"confidence":"1.0","content":"language"}],"type":"pronunciation"},{"alternatives":[{"confidence":"0.0","content":"?"}],"type":"punctuation"},{"start_time":"2.88","end_time":"3.19","alternatives":[{"confidence":"0.9944","content":"One"}],"type":"pronunciation"},{"start_time":"3.19","end_time":"3.61","alternatives":[{"confidence":"0.991","content":"language"}],"type":"pronunciation"},{"start_time":"3.61","end_time":"3.75","alternatives":[{"confidence":"0.991","content":"is"}],"type":"pronunciation"},{"start_time":"3.75","end_time":"4.03","alternatives":[{"confidence":"1.0","content":"never"}],"type":"pronunciation"},{"start_time":"4.03","end_time":"4.48","alternatives":[{"confidence":"0.9079","content":"enough"}],"type":"pronunciation"},{"alternatives":[{"confidence":"0.0","content":"."}],"type":"punctuation"}]},"status":"COMPLETED"}
まとめ
ハンズオン資料がかなりしっかりしていたため、
特に困ることなく構築できました。
順序だてて丁寧に説明があるので、非常にわかりやすかったです。
こんな簡単に、システムを作れるとは面白いですね。 宿題 や +α があるので構築しようと思います。
AWS ご利用開始時に最低限おさえておきたい10のこと
初めに
本記事は、AWS White Belt Online Seminar のアウトプットになります。
AWS Well-Architected Framework(W-A)
AWS Well-Architected Framework(W-A)とは?
システム設計・運用の大局的な
考え方とベストプラクティス集
・AWSのソリューションアーキテクトと利用者が数多くの経験から作り上げたもの
・毎年アップデートされる
AWS Well-Architected Frameworkホワイトペーパー
内容
・運用の優秀性
・セキュリティ
・信頼性
・パフォーマンス効率
・コストの最適化
設計原則
・必要なキャパシティを勘に頼らない
・本番環境
でのテスト
を行う
・アーキテクチャ試行の回数を増やすために自動化
を取り入れる
・発展的な
アーキテクチャを受け入れる
・データ計測に基づいて
アーキテクチャを決定する
・本番環境で想定されるトラブルをあらかじめテスト
し、対策
する
質問と回答形式のベストプラクティス
・ベストプラクティスとしては、全項目網羅されている事
・リスクや改善点などを理解したうえで、明確な判断材料に基づいてあえて
ベストプラクティスを実施しないことは問題ない
設計構築時に”W-A”を実施
「ベストプラクティスの質問」を活用
・合計46個のベストプラクティスの質問に答えて、ワークロードとの差分(改善点・リスク)を把握する。
・ベストプラクティスを理解した上で、判断する 。
イメージは健康診断。改善、要改善項目
を見つけ対応の要不要
の判断をする。
※運用中のサービスのレビューも可能
AWS利用開始時に最低限おさえておきたいこと
セキュリティ(5/11項目抜粋)
・確認事項: ワークロードの認証情報と認証をどのように管理していますか。
ベストプラクティス:
rootアカウントには、必ずMFA(多要素認証)
を設定し、最小限の利用に留める(極力利用しない)。
普段の作業には、IAMユーザーを使用する。
・確認事項: AWSサービスへの人為的なアクセスをどのように制御していますか。
ベストプラクティス:
ユーザー各個人に固有の認証情報(IAMユーザー
)を払い出し、必要に応じた最小限の権限を付与する。
ユーザーグループを使うと、権限をまとめて管理できる。
・確認事項: AWSサービスへのプログラムによるアクセスを、どのように制御していますか。
ベストプラクティス:
認証情報をハードコーディングをせず、IAMロールを活用する。git-secrets
を使い認証情報を管理することができる。
※git-secret‥AWS Labsの認証情報をgitリポジトリにコミットすることを防ぐツール。
・確認事項: ワークロードのセキュリティイベントをどのように検知して調査していますか。
ベストプラクティス:
セキュリティ関連のログを取得し、一元的に監視と分析をする。CloudTrail
でAPI、GUい操作ログ、コマンド操作ログなどを取得可能。
何かあった際にログを確認するだけでなく、普段から監視することも大事。Amazon GuardDuty
を活用することで、通知の自動化も可能。
・確認事項: ネットワークをどのように保護していますか。
ベストプラクティス:
ネットワークACL
とSG(セキュリティグループ)
を活用することで、各レイヤでのセキュリティ対策や必要最低限のアクセス設定に絞ることが可能。
AWS WAF
やAWS Shield
を活用することで、よりセキュアなシステムを構築できる。
信頼性(2/9項目抜粋)
・確認事項: どのようにデータをバックアップしていますか。
ベストプラクティス:
バックアップを取得し、定期的なリカバリテストでRTO・RPOを満たすことを確認している。
EC2 AMI
とEBS スナップショット
、RDSの自動バックアップ機能
を活用する。
トラブル発生時を想定した、復旧テストを定期的に実施し、手順の確認をすることを推奨。
RTO‥インシデント発生から復旧までの「許容できるダウンタイムの最大時間」を指す。
RPO‥復旧すべきバックアップファイルの古さのこと。つまり、どの程度データの損失を許容できるのかを決める。
・確認事項: システムがコンポーネントのエラーに耐えられるようにどのように設計していますか。
ベストプラクティス:
マルチAZ
または、複数リージョン
でシステムが実行されている疎結合なアーキテクチャを採用している。
障害を監視し、自動的に回復する仕組みがある。
単一障害点の排除
がキーポイント。
コストの最適化(3/9項目抜粋)
・確認事項: AWS使用量とコストをどのようにモニタリングしていますか。
ベストプラクティス:
請求ダッシュボード
やAWS Cost Explorer
で積極的に使用量を把握し、分析している。
そのために、IAMユーザーの請求情報へのアクセス有効化
とコストエクスプローラーの有効化
をルートアカウントで設定する。
日頃から定期的に確認することを推奨。
AWS Budgets
を活用することで、設定した予算から超過した場合に通知させることができる。
・確認事項: コスト目標を達成するためにインスタンスタイプとサイズをどのように選択していますか。
ベストプラクティス:
メトリクスに基づいて、サイジングする。
そのために、AWS CloudWatch
でリソース状況を把握し、利用状況に応じた適切なインスタンスタイプなどを選択する。
リザーブドインスタンス
やAutoScaling
などの活用も検討する。
定期的に見直すことを推奨。
基本的には、新しいタイプ、ファミリーの方が安い。
・確認事項: コスト削減のために料金も出るはどのように選択していますか。
ベストプラクティス:
利用率を分析し、購入オプションを検討している。
・時間課金系サービス
を使い分けることを意識する。
リージョン毎の料金差も考慮している。
・より安価なリージョン
を選択できるような場合もある。
【CentOS7】Kernel panic について 【not syncing: No init found.】
内容
CentOSのISOファイルが問題なく使えるよねという確認がしたかっただけなので、
各種設定はデフォルトの推奨スペックのまま作成していました。
VMplayerにCentOS7.7をインストールしようとたしたら、ISOの読み込み中にカーネルパニック
が起きた。
検証
検索しても原因が、わからなかったためISOイメージの破損やVMplayerとCentOSの相性の問題かと考えました。
まず、同じイメージを使用して、VirtualBoxでサーバーを作成したところ問題なく作成できたので、
ISOイメージは破損していないことは確認できました。
VMplayerとの相性問題かと思い、CentOS 7.5、7.2、7.0 をインストールしてみました。
- CentOS 7.2
言語選択の画面が表示されました!
しかし、日本語を選択し「次へ」を押すとフリーズしました。。
しばらく放置していたら先に進みましたが、ディスクが検出されず進みませんでした。
- CentOS 7.0
他のバージョンと同じように起動したところ、以下のメッセージが表示されました。
どうやら、CentOS7.0をインストールするためには、メモリが512MB必要なようです!
結論
CentOSに対して、割り当てるメモリが足りていませんでした。
VMplayerのデフォルトの推奨スペックが256MBだったので、そのまま作成していたことが原因でした。
VirtualBoxのデフォルトの推奨スペックは1024MBなので、その違いで作成できたんですね。。
通常は、ある程度スペックを決めてから作成するのでまず引っかかりませんよね。
それにしても、7.0だけちゃんとメモリを要求するメッセージを表示してくれるんですね!
後継バージョンのはずの7.Xでは出てこないのは不思議です。
CentOSの最小RAM
以下のサイトを参考にさせていただくと、CentOS7のインストーラーを
動作させるためには、最小限の406MB必要だそうです。
512MBはCentOS-7の最小メモリ要件だそうですが、
全ての機能を使用するためには、1GB必要だそうです。
ソースがリンク切れしていたので、公式情報までは辿れませんでしたが、
今回色々試した感覚だとあっているような気がします!