tag:crieit.net,2005:https://crieit.net/boards/gooyeskotlintoy/feed 「kotlinで勉強がてら遊ぶ(Dockerも少し触る)」の投稿 - Crieit Crieitで「kotlinで勉強がてら遊ぶ(Dockerも少し触る)」ボードに投稿された最近の投稿 2024-04-18T17:04:30+09:00 https://crieit.net/boards/gooyeskotlintoy/feed tag:crieit.net,2005:PublicArticle/425a17b2d25200c271eec627ae8accc1 2024-04-18T17:04:30+09:00 2024-04-18T17:04:30+09:00 https://crieit.net/boards/gooyeskotlintoy/425a17b2d25200c271eec627ae8accc1 春コミの原稿やってました。時間が空いてしまったのですが続き。postgresへの接続設定を追加... <p>春コミの原稿やってました。時間が空いてしまったのですが続き。</p> <h1>postgresへの接続設定を追加</h1> <p>src/main/application.yamlを作成して以下を記入</p> <pre><code>spring: datasource: driver-class-name: org.postgresql.Driver url: jdbc:postgresql://db:5432/example username: postgres password: **** </code></pre> <p>ここでは仮置きとしてpasswordを直接書いちゃってますが、<br /> 危ないので本当はやってはだめです。</p> <p>中身の確認用にcompose.yamlに以下を追加。<br /> これを入れておくと、postgresの中身をそのまま見ることができます。</p> <pre><code># servicesの下 adminer: image: adminer restart: always ports: - 8082:8080 </code></pre> <p>初期化用のSQLを作り、</p> <p>db/init.sql</p> <pre><code>DROP TABLE IF EXISTS tamesi; CREATE TABLE IF NOT EXISTS tamesi ( id SERIAL PRIMARY KEY, name TEXT NOT NULL, age INTEGER NOT NULL ); </code></pre> <p>それを初期化用に設定</p> <pre><code> volumes: - db-data:/var/lib/postgresql/data # ↓を追加 - ./db/init.sql:/docker-entrypoint-initdb.d/init.sql </code></pre> <p>docker-entrypoint-initdb.dに置くと初期化してくれるらしい。</p> <p><a target="_blank" rel="nofollow noopener" href="https://hub.docker.com/_/postgres">公式ドキュメント</a></p> <p>で、エラーとか起きるので都度修正して、</p> <pre><code>plugins { id("org.springframework.boot") version "3.2.2" id("io.spring.dependency-management") version "1.1.4" kotlin("jvm") version "1.9.22" kotlin("plugin.spring") version "1.9.22" kotlin("plugin.jpa") version "1.9.22" # 追加 } </code></pre> <p><a target="_blank" rel="nofollow noopener" href="https://spring.io/guides/tutorials/spring-boot-kotlin">https://spring.io/guides/tutorials/spring-boot-kotlin</a></p> <pre><code>@Entity @Table(name = "tamesi") data class TamesiTableData( @Id @GeneratedValue(strategy = GenerationType.IDENTITY) # 追加 val id: Int?, # Int型に変更 val name: String, val age: Int ) </code></pre> <h1>実行</h1> <p>以下で実行。</p> <pre><code>$ docker compose up -d --build </code></pre> <p><a href="https://crieit.now.sh/upload_images/89d4eb5f7140cf6e660f101903b062ef6620d2fc10cae.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/89d4eb5f7140cf6e660f101903b062ef6620d2fc10cae.png?mw=700" alt="スクリーンショット 0006-04-18 16.52.21.png" /></a><br /> <a href="https://crieit.now.sh/upload_images/1c161d7c00c1901e26eac47879d612856620d3b84fcf2.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/1c161d7c00c1901e26eac47879d612856620d3b84fcf2.png?mw=700" alt="スクリーンショット 0006-04-18 16.52.42.png" /></a><br /> <a href="https://crieit.now.sh/upload_images/e7503fca37ca38868a398372c13fd5cb6620d38a7d11c.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/e7503fca37ca38868a398372c13fd5cb6620d38a7d11c.png?mw=700" alt="スクリーンショット 0006-04-18 16.52.36.png" /></a><br /> いい感じに動いてそうなのでsaveから</p> <pre><code>$ curl -X POST -H "Content-Type: application/json" -d '{"name":"taro", "age":11}' localhost:8081/save </code></pre> <p>レスポンスはこれ</p> <pre><code>{"name":"taro","age":11} </code></pre> <p>Adminerからも見てみる</p> <p><a href="https://crieit.now.sh/upload_images/a0e623bb16d6dd31eb2fb7e2379fcf146620d2ce8a141.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/a0e623bb16d6dd31eb2fb7e2379fcf146620d2ce8a141.png?mw=700" alt="スクリーンショット 0006-04-18 16.50.37.png" /></a><br /> <a href="https://crieit.now.sh/upload_images/d6b28493fc23c3abebe7e33c065ce1766620d2e40a795.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/d6b28493fc23c3abebe7e33c065ce1766620d2e40a795.png?mw=700" alt="スクリーンショット 0006-04-18 16.54.10.png" /></a><br /> OKっぽい</p> <p>取得の方も叩いてみる</p> <pre><code>$ curl "localhost:8081/get/1" </code></pre> <p>レスポンス</p> <pre><code>{"name":"taro","age":11} </code></pre> <p>OK!</p> <p>終了時は以下コマンドで</p> <pre><code>$ docker compose down -v </code></pre> <p>ところでこれって関数型プログラミングとか全然触れてないですね。</p> gooye-g tag:crieit.net,2005:PublicArticle/644cbd1826969d5f6bdfc45c913ed3dc 2024-03-14T01:03:40+09:00 2024-03-14T01:03:40+09:00 https://crieit.net/boards/gooyeskotlintoy/644cbd1826969d5f6bdfc45c913ed3dc DBを起動してマルチコンテナにしてみるぞPostgreSQLを起動する # The co... <h1>DBを起動してマルチコンテナにしてみるぞ</h1> <h2>PostgreSQLを起動する</h2> <pre><code> # The commented out section below is an example of how to define a PostgreSQL # database that your application can use. `depends_on` tells Docker Compose to # start the database before your application. The `db-data` volume persists the # database data between container restarts. The `db-password` secret is used # to set the database password. You must create `db/password.txt` and add # a password of your choosing to it before running `docker compose up`. </code></pre> <p>と言われているのでせっかくなのでPostgreSQLを使用します。</p> <p>Learning centerの"Multi-container applications"が参考になるかも。<br /> こちらの<a target="_blank" rel="nofollow noopener" href="https://github.com/docker/multi-container-app/blob/main/compose.yaml">サンプル</a>のcompose.yamlを見ます。</p> <p>どうやらservicesのすぐ下に複数定義するとMulti-containerになるようですね。<br /> サービスとコンテナの言葉の違い的なものがよくわからなかったのでchatGPTに聞いたらこんな感じでした。ほんとかどうか理解するには知識が足りない。</p> <p>*Dockerにおける「service」とは、コンテナーの実行単位を指します。コンテナーは、アプリケーションやサービスの独立した実行環境を提供するための軽量な仮想化技術です。そして、Docker Composeを使って複数のコンテ ナーをまとめて定義する場合、各コンテナーは1つ以上のサービスとして定義されます。</p> <p>具体的には、Docker Composeの設定ファイル(通常はdocker-compose.yml)で、services セクションの下に複数のサービスが定義されます。各サービスの定義には、そのサービスに関連するイメージ、ポートの公開、環境変 数の設定などが含まれます。そして、各サービスは1つ以上のコンテナーを起動します。つまり、コンテナーは実際にリソースを提供する実体であり、サービスはそのコンテナーの定義や管理を担当します。</p> <p>要するに、Dockerにおける「service」とは、論理的なグループ化や管理の単位であり、そのサービスによって実行される1つ以上のコンテナーを指します。一方で、コンテナーは実際にリソースを提供する単位です。*</p> <p>特に何も考えずにコメントどおりに <code>depends_on</code> の項目をコメントアウト、同様にservice直下のdbの項目もコメントアウトします。</p> <pre><code class="yaml"># Comments are provided throughout this file to help you get started. # If you need more help, visit the Docker compose reference guide at # https://docs.docker.com/go/compose-spec-reference/ # Here the instructions define your application as a service called "app". # This service is built from the Dockerfile in the current directory. # You can add other services your application may depend on here, such as a # database or a cache. For examples, see the Awesome Compose repository: # https://github.com/docker/awesome-compose services: app: container_name: tamesikotlin20240122comp # この行を追加してコンテナ名を指定する build: context: . # target: final # If your application exposes a port, uncomment the following lines and change # the port numbers as needed. The first number is the host port and the second # is the port inside the container. ports: - 8081:8080 depends_on: db: condition: service_healthy # The commented out section below is an example of how to define a PostgreSQL # database that your application can use. `depends_on` tells Docker Compose to # start the database before your application. The `db-data` volume persists the # database data between container restarts. The `db-password` secret is used # to set the database password. You must create `db/password.txt` and add # a password of your choosing to it before running `docker compose up`. db: image: postgres restart: always user: postgres secrets: - db-password volumes: - db-data:/var/lib/postgresql/data environment: - POSTGRES_DB=example - POSTGRES_PASSWORD_FILE=/run/secrets/db-password expose: - 5432 healthcheck: test: [ "CMD", "pg_isready" ] interval: 10s timeout: 5s retries: 5 volumes: db-data: secrets: db-password: file: db/password.txt </code></pre> <p>起動確認をします。<br /> 今回はバックグラウンド起動を試すために-dを実行します。</p> <pre><code>$ docker compose up -d --build </code></pre> <pre><code>Error response from daemon: invalid mount config for type "bind": bind source path does not exist: /host_mnt/Users/********/IdeaProjects/tamesi/db/password.txt zsh: exit 1 docker compose up -d --build </code></pre> <p>おっとよく読んでなかった、パスワード指定するためにファイル作れと書いてありますね 作って再実行。</p> <pre><code> ✔ Container tamesi-db-1 Healthy ✔ Container tamesikotlin20240122comp Started </code></pre> <p>起動に成功した様子。接続してみたいと思います。<br /> クライアントはDBeavweを使います。</p> <pre><code>$ brew install dbeaver-community </code></pre> <p>docker-compose.yamlの方に設定を加え、ポート5432を公開。</p> <pre><code> db: image: postgres restart: always user: postgres secrets: - db-password volumes: - db-data:/var/lib/postgresql/data environment: - POSTGRES_DB=example - POSTGRES_PASSWORD_FILE=/run/secrets/db-password expose: - 5432 # ここの下2行を追加 ports: - 5432:5432 </code></pre> <p>起動して接続確認。</p> <pre><code>$ docker compose up -d --build </code></pre> <p>OK!</p> <h1>kotlinの関数型言語の描き方がわからない</h1> <p>ところで私はAtomが死んでからメモ帳としてターミナル開いて素のvimを使ってるんですが、最近vim-plugの使い方を忘れてしまい、思い出しがてら新しいcolorschemeにしてみました。<br /> redditで検索しておすすめされてたEverforest、使いやすいです。それまでicebergを使ってたので、VISUALモードが見にくかったんですよね。</p> <p>閑話休題、関数型的なAPIの書き方の参考になるものを探します。が、なかなか難しいですね。</p> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://kotlinlang.org/docs/jvm-spring-boot-add-data-class.html#update-your-application">Kotlinの公式ドキュメントのチュートリアル</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://github.com/Kotlin/workshop">JetBrainのworkshop</a> (そんなのあるんだ……)</li> </ul> <p>とりあえず、controllerを実装してる例って無いですね……<br /> なんとなくのイメージで、Router Functionsのような例が出てくると思ったんですが違うようです。<br /> もしかして関数型プログラミングの学習としてkotlinを選んだのは良くなかったのかもしれない。kotlinで学ぶ関数型言語みたいな書籍が無い(英語ならある)時点で察するべきか……<br /> 最初からやり直すならHaskell、scala、Elixir、Lispみたいので始めるべきだったかもしれません。</p> <h1>なんとなくで書いてみる</h1> <p>chatGPTさんに聞きつつやってますが、この人結構な確率で嘘をいうのであくまで参考程度に、いろんな資料を斜め読みしつつとりあえずで実装していきます。<br /> なんとなくですが、<br /> - 「関数そのものを変数に代入できる」<br /> - 「数式っぽく扱うためにvoid関数を使わない」<br /> - 「ラムダ式を多用しつつデータを変形させる」<br /> - 「副作用を起こさない」<br /> - 「メソッドチェーンっぽい書き方をする」<br /> らへんなのかなと思います。</p> <p>副作用云々はDDDでも触れるのでそれっぽい感じでいいんですかね……</p> <h2>Controller</h2> <p>RESTFul APIを目指してこんな感じ</p> <pre><code>package jp.gooye.toy.tamesi.controller; import jp.gooye.toy.tamesi.model.TamesiDataResource import jp.gooye.toy.tamesi.model.TamesiResponse import jp.gooye.toy.tamesi.service.TamesiDataService import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.PathVariable import org.springframework.web.bind.annotation.PostMapping import org.springframework.web.bind.annotation.RestController @RestController class TamesiController( private val service: TamesiDataService ) { @GetMapping("/tamesi") fun welcome() = TamesiResponse(message = "hello world!!") @PostMapping("/save") fun create(tamesiDataResource: TamesiDataResource): TamesiDataResource { return service.save(tamesiDataResource) } @GetMapping("/get") fun read(@PathVariable id: String): TamesiDataResource { // 後で投げる例外は修正する return service.findById(id).orElseThrow() } } </code></pre> <h2>Service</h2> <pre><code>package jp.gooye.toy.tamesi.service import jp.gooye.toy.tamesi.model.TamesiDataResource import jp.gooye.toy.tamesi.repository.TamesiDataRepository import org.springframework.stereotype.Service import java.util.Optional @Service class TamesiDataService( val repository: TamesiDataRepository, val tableDataFactory: TamesiTableDataFactory, val resourceFactory: TamesiResourceFactory ) { fun findById(id: String): Optional<TamesiDataResource> { return repository.findById(id).map { resourceFactory.from(it) } } fun save(resource: TamesiDataResource): TamesiDataResource { return resourceFactory.from(repository.save(tableDataFactory.from(resource))) } } </code></pre> <h2>Factory</h2> <pre><code>package jp.gooye.toy.tamesi.service import jp.gooye.toy.tamesi.model.TamesiDataResource import jp.gooye.toy.tamesi.repository.TamesiTableData class TamesiResourceFactory { fun from(data: TamesiTableData): TamesiDataResource { return TamesiDataResource(data.name, data.age) } } </code></pre> <pre><code>package jp.gooye.toy.tamesi.service import jp.gooye.toy.tamesi.model.TamesiDataResource import jp.gooye.toy.tamesi.repository.TamesiTableData class TamesiTableDataFactory { fun from(resource: TamesiDataResource): TamesiTableData { return TamesiTableData(null, resource.name, resource.age) } } </code></pre> <h2>Repository</h2> <pre><code>package jp.gooye.toy.tamesi.repository import org.springframework.data.repository.CrudRepository interface TamesiDataRepository : CrudRepository<TamesiTableData, String> </code></pre> <h2>data</h2> <pre><code>package jp.gooye.toy.tamesi.repository import jakarta.persistence.Id import jakarta.persistence.Table @Table(name = "tamesi") data class TamesiTableData( @Id val id: String?, val name: String, val age: Int ) </code></pre> <p>とりあえず形にしましたが、これだけでは動きません。次回、DBの接続まわりとユニットテストに手を出していきます。</p> gooye-g tag:crieit.net,2005:PublicArticle/47b793ea895cb827d07cfc762938ebde 2024-03-04T00:19:05+09:00 2024-03-04T00:19:05+09:00 https://crieit.net/boards/gooyeskotlintoy/47b793ea895cb827d07cfc762938ebde 急遽北海道に一週間行ってたので時間が空いてしまった。悪天候の冬の札幌駅に人生ではじめて降り立っ... <p>急遽北海道に一週間行ってたので時間が空いてしまった。悪天候の冬の札幌駅に人生ではじめて降り立ったとき、エルデンリングの例のフォーマットで禁域って文字とドオォォォンみたいなSEが鳴った気がする。</p> <h1>Docker の公式チュートリアルを見つつ進める</h1> <p>といいつつbrewからdokcer desktop無いか確認はする。<br /> dockerのコマンドラインツールはあるけどdocker desktop無いな。公式サイト行きましょ</p> <p><a target="_blank" rel="nofollow noopener" href="https://hub.docker.com/">https://hub.docker.com/</a></p> <p>……動画のチュートリアルあるのか。こりゃ便利。英語だけど雰囲気でなんとかなんべ。大体見ます。<br /> <a href="https://crieit.now.sh/upload_images/7c5b45582a2a7507af5dc4cc11adf68a65e2d9c67b654.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/7c5b45582a2a7507af5dc4cc11adf68a65e2d9c67b654.png?mw=700" alt="スクリーンショット 0006-03-02 16.48.14.png" /></a></p> <p>この前作ったkotlinのapiのルートに移動、以下実行</p> <pre><code>$ docker init </code></pre> <p>結果</p> <pre><code> ~/IdeaProjects/toykotlin $ docker init Welcome to the Docker Init CLI! This utility will walk you through creating the following files with sensible defaults for your project: - .dockerignore - Dockerfile - compose.yaml - README.Docker.md Let's get started! ? What application platform does your project use? [Use arrows to move, type to filter] Go - suitable for a Go server application Python - suitable for a Python server application Node - suitable for a Node server application Rust - suitable for a Rust server application ASP.NET Core - suitable for an ASP.NET Core application PHP with Apache - suitable for a PHP web application Java - suitable for a Java application that uses Maven and packages as an uber jar > Other - general purpose starting point for containerizing your application Don't see something you need? Let us know! Quit </code></pre> <p>kotlinなのでOtherにします。Javaともちょっと迷ったけどMavenは使ってないしね。uber jarって何?届けてくれるの?</p> <pre><code>? What application platform does your project use? Other CREATED: .dockerignore CREATED: Dockerfile CREATED: compose.yaml CREATED: README.Docker.md ✔ Your Docker files are ready! Take a moment to review them and tailor them to your application. When you're ready, start your application by running: docker compose up --build Consult README.Docker.md for more information about using the generated files. </code></pre> <p>ファイルができました。中を見ます。<br /> docker desktopのLearning centerでもこのように言っている。<br /> <em>however, that the Dockerfile and compose.yaml file created for your project need additional changes. In this case, you may need to look up the Dockerfile reference⁠ and Compose file reference⁠ in our documentation.</em></p> <h2>.dockerignore</h2> <pre><code># Include any files or directories that you don't want to be copied to your # container here (e.g., local build artifacts, temporary files, etc.). # # For more help, visit the .dockerignore file reference guide at # https://docs.docker.com/go/build-context-dockerignore/ **/.DS_Store **/__pycache__ **/.venv **/.classpath **/.dockerignore **/.env **/.git (後略) </code></pre> <p>コンテナにコピーしたくないファイルをここに記述しろと書いてあります。大体必要なものは入ってるかな?</p> <h2>compose.yaml</h2> <pre><code># Comments are provided throughout this file to help you get started. # If you need more help, visit the Docker compose reference guide at # https://docs.docker.com/go/compose-spec-reference/ # Here the instructions define your application as a service called "app". # This service is built from the Dockerfile in the current directory. # You can add other services your application may depend on here, such as a # database or a cache. For examples, see the Awesome Compose repository: # https://github.com/docker/awesome-compose services: app: build: context: . target: final # If your application exposes a port, uncomment the following lines and change # the port numbers as needed. The first number is the host port and the second # is the port inside the container. # ports: # - 8080:8080 # The commented out section below is an example of how to define a PostgreSQL # database that your application can use. `depends_on` tells Docker Compose to # start the database before your application. The `db-data` volume persists the # database data between container restarts. The `db-password` secret is used # to set the database password. You must create `db/password.txt` and add # a password of your choosing to it before running `docker compose up`. # depends_on: # db: # condition: service_healthy # db: # image: postgres # restart: always # user: postgres # secrets: # - db-password # volumes: # - db-data:/var/lib/postgresql/data # environment: # - POSTGRES_DB=example # - POSTGRES_PASSWORD_FILE=/run/secrets/db-password # expose: # - 5432 # healthcheck: # test: [ "CMD", "pg_isready" ] # interval: 10s # timeout: 5s # retries: 5 # volumes: # db-data: # secrets: # db-password: # file: db/password.txt </code></pre> <p>ここが肝になります。docker 起動するときに必要な設定を全部ここに書いておけば、docker compose up --buildを実行するときに読んでくれます。詳しいことは<a target="_blank" rel="nofollow noopener" href="https://docs.docker.com/go/compose-spec-reference/">公式ドキュメント</a>見ろと書いてあります。<br /> 一番最初にやることはこれ</p> <pre><code>services: app: container_name: tamesikotlin20240122comp # この行を追加してコンテナ名を指定する </code></pre> <p>コンテナ名指定しないままbuildすると勝手に中二臭い名前にされます。</p> <p>次、ポート番号</p> <pre><code> # If your application exposes a port, uncomment the following lines and change # the port numbers as needed. The first number is the host port and the second # is the port inside the container. ports: - 8081:8080 </code></pre> <p>開きたいポートがあるならコメントアウトしろと書いてますね。<br /> 前回8080ポート指定でAPI実行したので、今回は8081をフォワーディングしてくれるように設定してみます。<br /> portsの指定場所はservices.app.portsです。字下げの位置に気をつけます。</p> <p>次。</p> <pre><code> # The commented out section below is an example of how to define a PostgreSQL # database that your application can use. </code></pre> <p>来ましたね。PostgreSQLを使ったマルチコンテナ的なやつ。一旦今回はDocker上でKotlin動かすことに集中し、ここは次回に回します。<br /> composeについてはここまで。</p> <h2>Dockerfile</h2> <pre><code># syntax=docker/dockerfile:1 # Comments are provided throughout this file to help you get started. # If you need more help, visit the Dockerfile reference guide at # https://docs.docker.com/go/dockerfile-reference/ # Want to help us make this template better? Share your feedback here: https://forms.gle/ybq9Krt8jtBL3iCk7 ################################################################################ # Pick a base image to serve as the foundation for the other build stages in # this file. # # For illustrative purposes, the following FROM command # is using the alpine image (see https://hub.docker.com/_/alpine). # By specifying the "latest" tag, it will also use whatever happens to be the # most recent version of that image when you build your Dockerfile. # If reproducability is important, consider using a versioned tag # (e.g., alpine:3.17.2) or SHA (e.g., alpine@sha256:c41ab5c992deb4fe7e5da09f67a8804a46bd0592bfdf0b1847dde0e0889d2bff). FROM alpine:latest as base ################################################################################ # Create a stage for building/compiling the application. # # The following commands will leverage the "base" stage above to generate # a "hello world" script and make it executable, but for a real application, you # would issue a RUN command for your application's build process to generate the # executable. For language-specific examples, take a look at the Dockerfiles in # the Awesome Compose repository: https://github.com/docker/awesome-compose FROM base as build RUN echo -e '#!/bin/sh\n\ echo Hello world from $(whoami)! In order to get your application running in a container, take a look at the comments in the Dockerfile to get started.'\ > /bin/hello.sh RUN chmod +x /bin/hello.sh ################################################################################ # Create a final stage for running your application. # # The following commands copy the output from the "build" stage above and tell # the container runtime to execute it when the image is run. Ideally this stage # contains the minimal runtime dependencies for the application as to produce # the smallest image possible. This often means using a different and smaller # image than the one used for building the application, but for illustrative # purposes the "base" image is used here. FROM base AS final # Create a non-privileged user that the app will run under. # See https://docs.docker.com/go/dockerfile-user-best-practices/ ARG UID=10001 RUN adduser \ --disabled-password \ --gecos "" \ --home "/nonexistent" \ --shell "/sbin/nologin" \ --no-create-home \ --uid "${UID}" \ appuser USER appuser # Copy the executable from the "build" stage. COPY --from=build /bin/hello.sh /bin/ # What the container should run when it is started. ENTRYPOINT [ "/bin/hello.sh" ] </code></pre> <p>hello worldしかしないDockerfileが置かれています。今回やりたいのはkotlinの起動なのでまるっと書き換えます。</p> <pre><code>FROM eclipse-temurin:17.0.9_9-jre # java 17でなんか良さそうなやつ # 適当に作業ディレクトリ WORKDIR /app # 作業内容全部コピー COPY . /app/. # buildはあらかじめ実行しておく前提 COPY build/libs/*.jar app.jar ENTRYPOINT ["java","-jar","/app/app.jar"] </code></pre> <p>これで実行</p> <pre><code>$ docker compose up --build </code></pre> <pre><code>[+] Running 1/0 ✔ Container tamesikotlin20240122comp Recreated 0.1s Attaching to tamesikotlin20240122comp tamesikotlin20240122comp | tamesikotlin20240122comp | . ____ _ __ _ _ tamesikotlin20240122comp | /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ tamesikotlin20240122comp | ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ tamesikotlin20240122comp | \\/ ___)| |_)| | | | | || (_| | ) ) ) ) tamesikotlin20240122comp | ' |____| .__|_| |_|_| |_\__, | / / / / tamesikotlin20240122comp | =========|_|==============|___/=/_/_/_/ tamesikotlin20240122comp | :: Spring Boot :: (v3.2.2) tamesikotlin20240122comp | tamesikotlin20240122comp | 2024-03-03T15:16:40.531Z INFO 1 --- [ main] j.gooye.toy.tamesi.TamesiApplicationKt : Starting TamesiApplicationKt v0.0.1-SNAPSHOT using Java 17.0.9 with PID 1 (/app/app.jar started by root in /app) tamesikotlin20240122comp | 2024-03-03T15:16:40.535Z INFO 1 --- [ main] j.gooye.toy.tamesi.TamesiApplicationKt : No active profile set, falling back to 1 default profile: "default" tamesikotlin20240122comp | 2024-03-03T15:16:41.425Z INFO 1 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port 8080 (http) tamesikotlin20240122comp | 2024-03-03T15:16:41.434Z INFO 1 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat] tamesikotlin20240122comp | 2024-03-03T15:16:41.435Z INFO 1 --- [ main] o.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/10.1.18] tamesikotlin20240122comp | 2024-03-03T15:16:41.458Z INFO 1 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext tamesikotlin20240122comp | 2024-03-03T15:16:41.459Z INFO 1 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 853 ms tamesikotlin20240122comp | 2024-03-03T15:16:41.761Z INFO 1 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port 8080 (http) with context path '' tamesikotlin20240122comp | 2024-03-03T15:16:41.771Z INFO 1 --- [ main] j.gooye.toy.tamesi.TamesiApplicationKt : Started TamesiApplicationKt in 1.601 seconds (process running for 1.936) </code></pre> <p>なんか動いた気がする<br /> じゃあcurlしましょ</p> <pre><code>$ curl localhost:8081/tamesi {"message":"hello world!!"} </code></pre> <p>動きました!<br /> 明日はもうちょっといろいろDocker周りをきれいにしたいとおもいます。</p> gooye-g tag:crieit.net,2005:PublicArticle/88d1c6619ecd10aaaa12a61f4cc350c4 2024-02-21T19:07:54+09:00 2024-03-04T00:22:32+09:00 https://crieit.net/boards/gooyeskotlintoy/88d1c6619ecd10aaaa12a61f4cc350c4 環境構築は昨日やったので今日はskeleton作っていきます公式ドキュメントのチュートリアルを... <p>環境構築は昨日やったので今日はskeleton作っていきます</p> <h1>公式ドキュメントのチュートリアルをチラ見しながら進める</h1> <p>公式ドキュメント、英語でも気にしない雰囲気わかれば良し<br /> <a target="_blank" rel="nofollow noopener" href="https://spring.io/guides/tutorials/spring-boot-kotlin">Building web applications with Spring Boot and Kotlin</a></p> <h2>initializr使ってskeletonを取得</h2> <p>Creating a New Projectの項目で<br /> Using the Initializr Websiteとコマンドライン、IntelliJ IDEA Ultimateを使う手順とかGradleじゃなくてMavenの手順とか紹介されているが、素直に一番上を使用していきます。</p> <p><a target="_blank" rel="nofollow noopener" href="https://start.spring.io/#!language=kotlin&type=gradle-project-kotlin">spring initializr</a></p> <p>いっつも思うんだけどinitializrのzrの部分、よくzerと書いてしまうけどスペルはzrが正しいんだよな。</p> <p>追加するよう言われているDependenciesは以下の通り</p> <div class="table-responsive"><table> <thead> <tr> <th>plugin</th> <th>内容</th> </tr> </thead> <tbody> <tr> <td>Spring Web</td> <td>おなじみのMVCするやつ</td> </tr> <tr> <td>Mustache</td> <td>意味は「口ひげ」web テンプレートシステム 一応いれる</td> </tr> <tr> <td>Spring Data JPA</td> <td>こちらもおなじみのJPA DBに関してはちょっとあとで自分でやりたいので今回は外しとく</td> </tr> <tr> <td>H2 Database</td> <td>同じく外しとく</td> </tr> <tr> <td>Spring Boot DevTools</td> <td>これもおなじみのあれ 開発中の変更がすぐ反映されるよ</td> </tr> </tbody> </table></div> <p><a href="https://crieit.now.sh/upload_images/2888b78d5481b52273715ea46f995bf165d5c1b0635fa.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/2888b78d5481b52273715ea46f995bf165d5c1b0635fa.png?mw=700" alt="spring initializr" /></a></p> <p>これでGENERATEボタンを押すと(Initializrで設定した名前).zipファイルがダウンロードされる。</p> <h2>作ったskeletonをintelliJで開いて編集する</h2> <p>公式チュートリアルではGradle解説みたいなこと言ってるのでIntelliJで開いていこう。</p> <p>zip解凍してこちらに移動。</p> <pre><code class="sh"># IntelliJのデフォのディレクトリに持っていく $ mv (解凍したtamesiフォルダ) ~/IdeaProjects </code></pre> <p>IntelliJでopenを選択し↑のディレクトリを指定。<br /> 今回入れたpluginとかmain関数とかHtmlControllerとか解説されてるけど作りたいのはAPIなので全部飛ばします。</p> <p>kotlinに触るのがガチ初めてなので文法とかは<a target="_blank" rel="nofollow noopener" href="https://www.tohoho-web.com/ex/kotlin.html">とほほ先生</a>にお世話になりつつ、書いたのがこれ。</p> <p>コントローラー (jp/gooye/toy/tamesi/controller/TamesiController.kt)</p> <p>```kt:jp/gooye/toy/tamesi/controller/TamesiController.kt<br /> package jp.gooye.toy.tamesi.controller;</p> <p>import jp.gooye.toy.tamesi.model.TamesiResponse<br /> import org.springframework.web.bind.annotation.GetMapping;<br /> import org.springframework.web.bind.annotation.RestController;</p> <p>@RestController<br /> class TamesiController {<br /> @GetMapping("/tamesi")<br /> fun welcome() = TamesiResponse(message = "hello world!!")<br /> }</p> <pre><code>リソース(jp/gooye/toy/tamesi/model/TamesiResponse.kt) ```kt:jp/gooye/toy/tamesi/model/TamesiResponse.kt package jp.gooye.toy.tamesi.model data class TamesiResponse(var message: String) </code></pre> <p>あ、jdkのバージョン指定とかIntelliJの設定とかgradleの導入手順とか書き忘れてるな……まあいいかjenvだけ念の為合わせとこ。<br /> intelliJの中でターミナル開いて</p> <pre><code class="sh">$ jenv local 17.0.9 $ java --version openjdk 17.0.9 2023-10-17 OpenJDK Runtime Environment Homebrew (build 17.0.9+0) OpenJDK 64-Bit Server VM Homebrew (build 17.0.9+0, mixed mode, sharing) </code></pre> <p>この2つのktファイルを追加して、intelliJの右上のぞうさんマークから実行してみる</p> <pre><code>gradle clean bootRun </code></pre> <p><a href="https://crieit.now.sh/upload_images/287524b283aa9750619ce722414e861965d5caec61e1d.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/287524b283aa9750619ce722414e861965d5caec61e1d.png?mw=700" alt="gradle clean bootRun" /></a></p> <p>springの起動ログが見えたら、</p> <pre><code> . ____ _ __ _ _ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v3.2.2) (中略) 2024-02-21T19:05:42.670+09:00 INFO 18060 --- [ restartedMain] j.gooye.toy.tamesi.TamesiApplicationKt : Started TamesiApplicationKt in 0.759 seconds (process running for 1.007) </code></pre> <p>curl!</p> <pre><code class="sh">$ curl localhost:8080/tamesi {"message":"hello world!!"} </code></pre> <p>ローカルで動くとこまで行きました。<br /> 明日はいよいよDockerに触ります。</p> gooye-g tag:crieit.net,2005:PublicArticle/01eb86ebb2340be99d58cc846a328a24 2024-02-20T20:21:10+09:00 2024-02-20T20:22:18+09:00 https://crieit.net/boards/gooyeskotlintoy/01eb86ebb2340be99d58cc846a328a24 環境構築 何が どれ PC iMac チップ Apple M1 OS Sono... <h1>環境構築</h1> <div class="table-responsive"><table> <thead> <tr> <th align="right">何が</th> <th align="right">どれ</th> </tr> </thead> <tbody> <tr> <td align="right">PC</td> <td align="right">iMac</td> </tr> <tr> <td align="right">チップ</td> <td align="right">Apple M1</td> </tr> <tr> <td align="right">OS</td> <td align="right">Sonoma</td> </tr> <tr> <td align="right">IDE</td> <td align="right">IntelliJ IDEA (Community Edition)</td> </tr> <tr> <td align="right">パッケージ管理システム</td> <td align="right">当然!「Homebrew」だッ!</td> </tr> <tr> <td align="right">javaバージョン管理</td> <td align="right">jenv</td> </tr> <tr> <td align="right">使用言語</td> <td align="right">kotlin 1.9.22</td> </tr> <tr> <td align="right">フレームワーク</td> <td align="right">spring boot</td> </tr> <tr> <td align="right">ターミナル</td> <td align="right">iTerm2</td> </tr> </tbody> </table></div> <h2>Homebrewはインストールされているところから開始</h2> <h3>各種パッケージ導入</h3> <p>特にバージョン違いの管理をする予定はないが手癖でいれる</p> <pre><code class="sh">$ brew install jenv </code></pre> <p>IDE</p> <pre><code class="sh">$ brew install intellij-idea-ce </code></pre> <p>JDK Apple M1に対応してるJDKを探しつつ、なぜかjava17も入れておく jenvの挙動試すの久々だったし</p> <pre><code class="sh">$ brew install java $ brew install openjdk@17 </code></pre> <p>M1向けjdkはzulu使えみたいなんあるけど<a target="_blank" rel="nofollow noopener" href="https://formulae.brew.sh/formula/openjdk@17">brewのページ</a>だとおkになってんだよな よくわからん</p> <p>jenvいれる<br /> <a target="_blank" rel="nofollow noopener" href="https://github.com/jenv/jenv">公式ドキュメント</a>を参考に</p> <pre><code class="sh">$ brew install jenv </code></pre> <p>メモ忘れたけど公式ドキュメント参考にdoctorとかして最終的にzshrcにはこれを追加</p> <pre><code>export PATH="/Users/sakamotokeika/.jenv/shims:${PATH}" export JENV_SHELL=zsh export JENV_LOADED=1 unset JAVA_HOME unset JDK_HOME source '/opt/homebrew/Cellar/jenv/0.5.6/libexec/libexec/../completions/jenv.zsh' jenv rehash 2>/dev/null jenv refresh-plugins jenv() { type typeset &> /dev/null && typeset command command="$1" if [ "$#" -gt 0 ]; then shift fi case "$command" in enable-plugin|rehash|shell|shell-options) eval `jenv "sh-$command" "$@"`;; *) command jenv "$command" "$@";; esac } </code></pre> <p>jenvにjdk追加</p> <pre><code class="sh">$ brew info openjdk@17 ==> openjdk@17: stable 17.0.9 (bottled) [keg-only] Development kit for the Java programming language [https://openjdk.java.net/](https://openjdk.java.net/) /opt/homebrew/Cellar/openjdk@17/17.0.9 (635 files, 304.9MB) Poured from bottle using the formulae.brew.sh API on 2024-02-13 at 18:57:08 From: https://github.com/Homebrew/homebrew-core/blob/HEAD/Formula/o/[email protected] License: GPL-2.0-only with Classpath-exception-2.0 ==> Dependencies Build: autoconf ✔, pkg-config ✔ Required: giflib ✔, harfbuzz ✔, jpeg-turbo ✔, libpng ✔, little-cms2 ✔ ==> Requirements Build: Xcode (on macOS) ✘ ==> Caveats For the system Java wrappers to find this JDK, symlink it with sudo ln -sfn /opt/homebrew/opt/openjdk@17/libexec/openjdk.jdk /Library/Java/JavaVirtualMachines/openjdk-17.jdk openjdk@17 is keg-only, which means it was not symlinked into /opt/homebrew, because this is an alternate version of another formula. If you need to have openjdk@17 first in your PATH, run: echo 'export PATH="/opt/homebrew/opt/openjdk@17/bin:$PATH"' >> ~/.zshrc For compilers to find openjdk@17 you may need to set: export CPPFLAGS="-I/opt/homebrew/opt/openjdk@17/include" ==> Analytics install: 18,294 (30 days), 59,204 (90 days), 273,258 (365 days) install-on-request: 15,800 (30 days), 49,966 (90 days), 180,903 (365 days) build-error: 101 (30 days) </code></pre> <p>おっふXcodeないやんけ</p> <pre><code class="sh">==> Requirements Build: Xcode (on macOS) ✘ </code></pre> <pre><code class="sh">% brew search Xcode ==> Formulae xcode-build-server xcode-kotlin xcodegen xcodes coder ==> Casks copilot-for-xcode swiftformat-for-xcode xcodeclangformat xcodes xscope If you meant "Xcode" specifically: Xcode can be installed from the App Store. </code></pre> <p>しゃあないAppStoreから落とすか</p> <pre><code class="sh">If you meant "Xcode" specifically: Xcode can be installed from the App Store. </code></pre> <p>今度こそadd</p> <pre><code class="sh">$ jenv add /opt/homebrew/opt/openjdk@17/libexec/openjdk.jdk/Contents/Home </code></pre> <p>動作確認</p> <pre><code class="sh">$ jenv versions * system (set by /Users/sakamotokeika/.jenv/version) 17.0 17.0.9 20.0 20.0.2 21.0 21.0.2 openjdk64-17.0.9 openjdk64-20.0.2 openjdk64-21.0.2 </code></pre> <p>適当なディレクトリほって確認</p> <pre><code class="sh">$ jenv local 17.0.9 $ jenv versions system 17.0 * 17.0.9 (set by /Users/sakamotokeika/dev/scala/.java-version) 20.0 20.0.2 21.0 21.0.2 openjdk64-17.0.9 openjdk64-20.0.2 openjdk64-21.0.2 </code></pre> <p>OK!</p> <p>kotlin</p> <pre><code class="sh">$ brew install kotlin </code></pre> <p>一旦ここまで</p> gooye-g