メインコンテンツにスキップ

Macでキーボードのキーリピートを有効にする

· 1分の読み時間
Haril Song
Owner, Software Engineer at 42dot

Macでは、特定のキーをしばらく押し続けると、ウムラウトなどの特殊文字入力ウィンドウが表示されることがあります。これは、Vimのようなエディタでコードナビゲーションを行う際に非常に邪魔になることがあります。

defaults write -g ApplePressAndHoldEnabled -bool false

上記のコマンドを実行し、アプリケーションを再起動すると、ウムラウトなどの特殊文字入力ウィンドウが表示されなくなり、キーリピートが有効になります。

Dockerネットワーク

· 8分の読み時間
Haril Song
Owner, Software Engineer at 42dot

概要

Dockerコンテナは隔離された環境で実行されるため、デフォルトでは互いに通信できません。しかし、複数のコンテナを1つのDockerネットワークに接続することで、相互に通信が可能になります。この記事では、異なるコンテナ間の通信を実現するためのネットワーク設定方法について探ります。

ネットワークの種類

Dockerネットワークは、目的に応じてbridgehostoverlayなどのさまざまなネットワークドライバーをサポートしています。

  • bridge: 単一ホスト内の複数のコンテナ間で通信を可能にします。
  • host: コンテナをホストコンピュータと同じネットワークで実行するために使用されます。
  • overlay: 複数のホスト上で実行されるコンテナ間のネットワーキングに使用されます。

ネットワークの作成

docker network createコマンドを使用して、新しいDockerネットワークを作成しましょう。

docker network create my-net

新しく追加されたネットワークは、docker network lsコマンドを使用して確認できます。-dオプションを指定しなかったため、デフォルトのbridgeネットワークとして作成されたことが確認できます。

ネットワークの詳細

docker network inspectコマンドを使用して、新しく追加されたネットワークの詳細を確認しましょう。

docker network inspect my-net
[
{
"Name": "my-net",
"Id": "05f28107caa4fc699ea71c07a0cb7a17f6be8ee65f6001ed549da137e555b648",
"Created": "2022-08-02T09:05:20.250288712Z",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": {},
"Config": [
{
"Subnet": "172.18.0.0/16",
"Gateway": "172.18.0.1"
}
]
},
"Internal": false,
"Attachable": false,
"Ingress": false,
"ConfigFrom": {
"Network": ""
},
"ConfigOnly": false,
"Containers": {},
"Options": {},
"Labels": {}
}
]

Containersセクションを確認すると、このネットワークに接続されているコンテナがないことがわかります。

コンテナをネットワークに接続する

まず、oneという名前のコンテナを実行しましょう。

docker run -it -d --name one busybox
# af588368c67b8a273cf63a330ee5191838f261de1f3e455de39352e0e95deac4

コンテナを実行する際に--networkオプションを指定しない場合、デフォルトでbridgeネットワークに接続されます。

情報

busyboxは、テスト目的に最適な軽量のコマンドラインライブラリであり、Dockerが公式に提供しています。

docker network inspect bridge
#...
"Containers": {
"af588368c67b8a273cf63a330ee5191838f261de1f3e455de39352e0e95deac4": {
"Name": "one",
"EndpointID": "44a4a022cc0f5fb30e53f0499306db836fe64da15631f2abf68ebc74754d9750",
"MacAddress": "02:42:ac:11:00:02",
"IPv4Address": "172.17.0.2/16",
"IPv6Address": ""
}
},
#...
]

次に、docker network connectコマンドを使用して、oneコンテナをmy-netネットワークに接続しましょう。

docker network connect my-net one

my-netネットワークの詳細を再確認すると、oneコンテナがContainersセクションに追加され、IPアドレス172.18.0.2が割り当てられていることがわかります。

docker network inspect my-net
[
{
"Name": "my-net",
"Id": "05f28107caa4fc699ea71c07a0cb7a17f6be8ee65f6001ed549da137e555b648",
"Created": "2022-08-02T09:05:20.250288712Z",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": {},
"Config": [
{
"Subnet": "172.18.0.0/16",
"Gateway": "172.18.0.1"
}
]
},
"Internal": false,
"Attachable": false,
"Ingress": false,
"ConfigFrom": {
"Network": ""
},
"ConfigOnly": false,
"Containers": {
"af588368c67b8a273cf63a330ee5191838f261de1f3e455de39352e0e95deac4": {
"Name": "one",
"EndpointID": "ac85884c9058767b037b88102fe6c35fb65ebf91135fbce8df24a173b0defcaa",
"MacAddress": "02:42:ac:12:00:02",
"IPv4Address": "172.18.0.2/16",
"IPv6Address": ""
}
},
"Options": {},
"Labels": {}
}
]

コンテナをネットワークから切断する

コンテナは同時に複数のネットワークに接続できます。oneコンテナは最初にbridgeネットワークに接続されていたため、現在はmy-netbridgeの両方のネットワークに接続されています。

docker network disconnectコマンドを使用して、oneコンテナをbridgeネットワークから切断しましょう。

docker network disconnect bridge one

2つ目のコンテナを接続する

次に、twoという名前のコンテナをmy-netネットワークに接続しましょう。

今回は、コンテナを実行する際に--networkオプションを使用して接続するネットワークを指定します。

docker run -it -d --name two --network my-net busybox
# b1509c6fcdf8b2f0860902f204115017c3e2cc074810b330921c96e88ffb408e

my-netネットワークの詳細を確認すると、twoコンテナがIPアドレス172.18.0.3を割り当てられて接続されていることがわかります。

docker network inspect my-net
[
{
"Name": "my-net",
"Id": "05f28107caa4fc699ea71c07a0cb7a17f6be8ee65f6001ed549da137e555b648",
"Created": "2022-08-02T09:05:20.250288712Z",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": {},
"Config": [
{
"Subnet": "172.18.0.0/16",
"Gateway": "172.18.0.1"
}
]
},
"Internal": false,
"Attachable": false,
"Ingress": false,
"ConfigFrom": {
"Network": ""
},
"ConfigOnly": false,
"Containers": {
"af588368c67b8a273cf63a330ee5191838f261de1f3e455de39352e0e95deac4": {
"Name": "one",
"EndpointID": "ac85884c9058767b037b88102fe6c35fb65ebf91135fbce8df24a173b0defcaa",
"MacAddress": "02:42:ac:12:00:02",
"IPv4Address": "172.18.0.2/16",
"IPv6Address": ""
},
"b1509c6fcdf8b2f0860902f204115017c3e2cc074810b330921c96e88ffb408e": {
"Name": "two",
"EndpointID": "f6e40a7e06300dfad1f7f176af9e3ede26ef9394cb542647abcd4502d60c4ff9",
"MacAddress": "02:42:ac:12:00:03",
"IPv4Address": "172.18.0.3/16",
"IPv6Address": ""
}
},
"Options": {},
"Labels": {}
}
]

コンテナ間のネットワーキング

2つのコンテナがネットワーク上で通信できるかどうかをテストしましょう。

まず、oneコンテナからtwoコンテナにpingコマンドを使用してpingを送ります。コンテナ名をホスト名として使用できます。

docker exec one ping two
# PING two (172.18.0.3): 56 data bytes
# 64 bytes from 172.18.0.3: seq=0 ttl=64 time=0.114 ms
# 64 bytes from 172.18.0.3: seq=1 ttl=64 time=0.915 ms

次に、twoコンテナからoneコンテナにpingを送ります。

docker exec two ping one
# PING one (172.18.0.2): 56 data bytes
# 64 bytes from 172.18.0.2: seq=0 ttl=64 time=0.108 ms
# 64 bytes from 172.18.0.2: seq=1 ttl=64 time=0.734 ms
# 64 bytes from 172.18.0.2: seq=2 ttl=64 time=0.270 ms
# 64 bytes from 172.18.0.2: seq=3 ttl=64 time=0.353 ms
# 64 bytes from 172.18.0.2: seq=4 ttl=64 time=0.371 ms

両方のコンテナがスムーズに通信できることが確認できました。

ネットワークの削除

最後に、docker network rmコマンドを使用してmy-netネットワークを削除しましょう。

docker network rm my-net
# Error response from daemon: error while removing network: network my-net id 05f28107caa4fc699ea71c07a0cb7a17f6be8ee65f6001ed549da137e555b648 has active endpoints

削除しようとしているネットワークにアクティブなコンテナが存在する場合、ネットワークは削除されません。

その場合、ネットワークを削除する前に、そのネットワークに接続されているすべてのコンテナを停止する必要があります。

docker stop one two
# one
# two
docker network rm my-net
# my-net

ネットワークのクリーンアップ

ホスト上で複数のコンテナを実行していると、コンテナが接続されていないネットワークが残ることがあります。そのような場合、docker network pruneコマンドを使用して、不要なネットワークを一度にすべて削除できます。

docker network prune
WARNING! This will remove all custom networks not used by at least one container.
Are you sure you want to continue? [y/N] y

結論

この記事では、さまざまなdocker networkコマンドについて探りました:

  • ls
  • create
  • connect
  • disconnect
  • inspect
  • rm
  • prune

ネットワークの理解は、Dockerコンテナを扱う際に重要です。データベースのコンテナ化やコンテナクラスタリングの実装など、複数のコンテナを効果的に管理するための重要なスキルです。

参考文献

Docker Volume

· 6分の読み時間
Haril Song
Owner, Software Engineer at 42dot

概要

Dockerコンテナはデフォルトで完全に隔離されています。つまり、コンテナ内のデータはホストマシンからアクセスできません。これは、コンテナのライフサイクルがその内部データに完全に依存していることを意味します。簡単に言えば、コンテナが削除されると、そのデータも失われます。

では、ログやデータベース情報などの重要なデータをコンテナのライフサイクルに依存せずに永続的に保存するにはどうすればよいでしょうか?

ここで ボリューム が登場します。

PostgreSQLのローカルインストール

簡単な例でPostgreSQLをインストールして使用しながら、ボリュームについて探ってみましょう。

ボリュームを使用しない場合

1. イメージをプルする

docker run -p 5432:5432 --name postgres -e POSTGRES_PASSWORD=1234 -d postgres

2. PostgreSQLに接続する

docker exec -it postgres psql -U postgres

3. ユーザーを作成する

create user testuser password '1234' superuser;

4. データベースを作成する

create database testdb owner testuser;

DBeaverDataGrip などのツールを使用してユーザーやデータベースを作成することもできます。

作業が終わったら、docker stop postgres でコンテナを停止できます。docker ps -a でコンテナリストを確認すると、コンテナが停止しているが削除されていないことがわかります。

$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
5c72a3d21021 postgres "docker-entrypoint.s…" 54 seconds ago Exited (0) 43 seconds ago postgres

この状態では、docker start postgres でコンテナを再起動でき、データはまだ存在しています。

これを確認してみましょう。

PostgreSQLで \list コマンドを使用すると、testdb データベースがまだ存在していることがわかります。

postgres=# \list
List of databases
Name | Owner | Encoding | Collate | Ctype | Access privileges
-----------+----------+----------+------------+------------+-----------------------
postgres | postgres | UTF8 | en_US.utf8 | en_US.utf8 |
template0 | postgres | UTF8 | en_US.utf8 | en_US.utf8 | =c/postgres +
| | | | | postgres=CTc/postgres
template1 | postgres | UTF8 | en_US.utf8 | en_US.utf8 | =c/postgres +
| | | | | postgres=CTc/postgres
testdb | testuser | UTF8 | en_US.utf8 | en_US.utf8 |
(4 rows)

しかし、docker rm オプションを使用してコンテナを完全に削除した場合はどうなるでしょうか?

docker rm postgres を実行し、その後 docker run を再度実行すると、新しいコンテナが作成され、testdb とユーザーが消えていることがわかります。

$ docker rm postgres
postgres
$ docker run -p 5432:5432 --name postgres -e POSTGRES_PASSWORD=1234 -d postgres
67c5c39658f5a21a833fd2fab6058f509ddac110c72749092335eec5516177c2
$ docker exec -it postgres psql -U postgres
psql (14.4 (Debian 14.4-1.pgdg110+1))
Type "help" for help.

postgres=# \list
List of databases
Name | Owner | Encoding | Collate | Ctype | Access privileges
-----------+----------+----------+------------+------------+-----------------------
postgres | postgres | UTF8 | en_US.utf8 | en_US.utf8 |
template0 | postgres | UTF8 | en_US.utf8 | en_US.utf8 | =c/postgres +
| | | | | postgres=CTc/postgres
template1 | postgres | UTF8 | en_US.utf8 | en_US.utf8 | =c/postgres +
| | | | | postgres=CTc/postgres
(3 rows)

postgres=#

ボリュームを使用する場合

まず、ボリュームを作成します。

$ docker volume create postgres
postgres

ls コマンドでボリュームの作成を確認できます。

$ docker volume ls
DRIVER VOLUME NAME
local postgres

次に、作成したボリュームをマウントしてPostgreSQLコンテナを実行します。

$ docker run -p 5432:5432 --name postgres -e POSTGRES_PASSWORD=1234 -v postgres:/var/lib/postgresql/data -d postgres
002c552fe092da485ee30235d809c835eeb08bd7c00e6f91a2f172618682c48e

その後の手順は、ボリュームを使用しない場合と同じです。これで、docker rm を使用してコンテナを完全に削除しても、データはボリュームに残り、失われることはありません。

前述のように、ログファイルやバックアップデータの長期保存には、ボリュームを使用してデータの永続性を確保することができます。

結論

Dockerボリュームとは何か、そしてそれをどのように使用するかをPostgreSQLの例を通じて探りました。ボリュームはDockerコンテナにおけるデータ管理の重要なメカニズムです。コンテナの性質に応じて適切にボリュームを使用することで、データを安全かつ簡単に管理でき、慣れれば開発の生産性を大幅に向上させることができます。詳細な情報については、公式ドキュメント を参照してください。

参考文献

[Jacoco] マルチモジュールプロジェクトのためのJacocoレポートの集約

· 3分の読み時間
Haril Song
Owner, Software Engineer at 42dot

概要

Gradle 7.4から、複数のJacocoテストレポートを1つの統合レポートに集約する機能が追加されました。以前は、複数のモジュールにまたがるテスト結果を1つのファイルで見るのは非常に困難でしたが、今ではこれらのレポートを統合することが非常に便利になりました。

使用方法

レポートを収集するためのサブモジュールの作成

現在のプロジェクト構成は、applicationというモジュールと、applicationモジュールで使用されるlistutilsといった他のモジュールで構成されています。

code-coverage-reportモジュールを追加することで、applicationlistutilsモジュールからテストレポートを収集できます。

プロジェクト構成は次のようになります:

  • application
  • utils
  • list
  • code-coverage-report

jacoco-report-aggregationプラグインの追加

// code-coverage-report/build.gradle
plugins {
id 'base'
id 'jacoco-report-aggregation'
}

repositories {
mavenCentral()
}

dependencies {
jacocoAggregation project(":application")
}

これで、./gradlew testCodeCoverageReportを実行することで、すべてのモジュールのテスト結果を集約したJacocoレポートを生成できます。

jacoco-directory

警告

集約機能を使用するには、jarファイルが必要です。jar { enable = false }と設定している場合は、trueに変更する必要があります。

更新 22-09-28

Gradleのマルチプロジェクトセットアップの場合、単一プロジェクトで正しく除外されたパッケージが集約レポートでは除外されない問題があります。

次の設定を追加することで、特定のパッケージを除外したレポートを生成できます。

testCodeCoverageReport {
reports {
csv.required = true
xml.required = false
}
getClassDirectories().setFrom(files(
[project(':api'), project(':utils'), project(':core')].collect {
it.fileTree(dir: "${it.buildDir}/classes/java/main", exclude: [
'**/dto/**',
'**/config/**',
'**/output/**',
])
}
))
}

次のステップ

Gradleでjacoco-aggregation-reportと一緒に導入されたjvm-test-suiteプラグインも非常に有用です。これらのプラグインは補完的な関係にあるため、一緒に使用することをお勧めします。

参考

なぜDockerなのか?

· 9分の読み時間
Haril Song
Owner, Software Engineer at 42dot
情報

この記事は社内情報共有のために書かれており、Java開発環境を基に説明しています。

Dockerとは?

情報

Linuxコンテナを作成・使用するためのコンテナ技術であり、この技術をサポートする最大の企業の名前でもあり、オープンソースプロジェクトの名前でもあります。

deploy-history 誰もが一度はDockerを検索したときに見たことがある画像

2013年に導入されたDockerは、インフラの世界をコンテナ中心のものに変革しました。多くのアプリケーションがコンテナを使用してデプロイされ、Dockerfileを作成してイメージをビルドし、コンテナをデプロイすることが一般的な開発プロセスとなりました。2019年のDockerConプレゼンテーションでは、1052億回ものコンテナイメージのプルが報告されました。

Dockerを使用することで、非常に軽量なモジュール型の仮想マシンのようにコンテナを扱うことができます。さらに、コンテナは柔軟にビルド、デプロイ、コピー、移動が可能で、クラウド向けのアプリケーション最適化をサポートします。

Dockerコンテナの利点

どこでも一貫した動作

コンテナランタイムがインストールされている限り、Dockerコンテナはどこでも同じ動作を保証します。例えば、チームメンバーAがWindows OSを使用し、チームメンバーBがMacOSを使用している場合でも、Dockerfileを通じてイメージを共有することで、OSに関係なく同じ結果を確認できます。デプロイの場合も同様です。コンテナが正常に動作することが確認されていれば、追加の設定なしでどこでも正常に動作します。

モジュール性

Dockerのコンテナ化アプローチは、アプリケーションの一部を分解、更新、または回復する能力に焦点を当てています。サービス指向アーキテクチャ(SOA)のように、複数のアプリケーション間でプロセスを共有するマイクロサービスベースのアプローチを採用できます。

レイヤリングとイメージバージョン管理

各Dockerイメージファイルは一連のレイヤーで構成されており、これらが一つのイメージに結合されます。

Dockerは新しいコンテナをビルドする際にこれらのレイヤーを再利用するため、ビルドプロセスが非常に速くなります。中間の変更はイメージ間で共有され、速度、スケーラビリティ、効率が向上します。

高速なデプロイ

Dockerベースのコンテナはデプロイ時間を数秒に短縮できます。OSを起動してコンテナを追加または移動する必要がないため、デプロイ時間が大幅に短縮されます。さらに、高速なデプロイ速度により、コンテナによって生成されたデータの作成と削除がコスト効率よく簡単に行え、ユーザーはそれが正しく行われたかどうかを心配する必要がありません。

要するに、Docker技術は効率性を強調し、より細かく制御可能なマイクロサービスベースのアプローチを提供します

ロールバック

Dockerを使用してデプロイする際、イメージはタグ付きで使用されます。例えば、バージョン1.2のイメージを使用してデプロイし、リポジトリにバージョン1.1のイメージがまだある場合、jarファイルを再準備することなくコマンドを実行するだけで済みます。

docker run --name app image:1.2
docker stop app

## バージョン1.1を実行
docker run --name app image:1.1

Docker使用前後の比較

Dockerコンテナを使用することで、従来の方法に比べてはるかに迅速かつ柔軟なデプロイが可能になります。

Dockerコンテナを使用しないデプロイ

  1. ローカルマシンでデプロイするjarファイルをパッケージ化。
  2. scpなどのファイル転送プロトコルを使用してjarファイルを本番サーバーに転送。
  3. ステータス管理のためにsystemctlを使用してサービスファイルを作成。
  4. systemctl start appでアプリケーションを実行。

複数のアプリが1つのサーバーで実行されている場合、停止したアプリを見つけるのは非常に複雑になります。複数のサーバーで複数のアプリを実行する場合も同様で、各サーバーでコマンドを実行する必要があり、非常に疲れるプロセスです。

Dockerコンテナを使用したデプロイ

  1. Dockerfileを使用してアプリケーションのイメージを作成。→ ビルド ⚒️
  2. DockerhubやGitlabレジストリなどのリポジトリにイメージをプッシュ。→ シッピング🚢
  3. 本番サーバーでdocker run imageを使用してアプリケーションを実行。

複雑なパス設定やファイル転送プロセスに時間を浪費する必要はありません。Dockerはどの環境でも動作し、どこでも実行され、リソースを効率的に使用します。

Dockerは単一のコンテナを効果的に管理するように設計されています。しかし、数百のコンテナやコンテナ化されたアプリを使用し始めると、管理とオーケストレーションが非常に困難になります。すべてのコンテナにネットワーキング、セキュリティ、テレメトリなどのサービスを提供するためには、一歩引いてそれらをグループ化する必要があります。ここでKubernetes1が登場します。

いつ使用すべきか?

開発者はほぼすべての状況でDockerを非常に有用と感じるでしょう。実際、Dockerは開発、デプロイ、運用において従来の方法よりも優れていることが多いため、Dockerコンテナは常に最優先で検討すべきです。

  1. ローカルマシンでPostgreSQLのような開発データベースが必要なとき。
  2. 新しい技術をテストまたは迅速に採用したいとき。
  3. ローカルマシンに直接インストールまたはアンインストールが難しいソフトウェアがあるとき(例:WindowsでJavaを再インストールするのは悪夢です)。
  4. フロントエンドチームなど、他のチームから最新のデプロイバージョンをローカルマシンで実行したいとき。
  5. 本番サーバーをNCPからAWSに切り替える必要があるとき。

シンプルなAPIサーバー:

docker run --name rest-server -p 80:8080 songkg7/rest-server
# curlを使用
curl http://localhost/ping

# httpieを使用
http localhost/ping

ポート80がコンテナのポート8080にマッピングされているため、コンテナとの通信がうまくいくことが確認できます。

よく使われるDocker Runオプション

--name : コンテナに名前を付ける

-p : コンテナのポートをホストに公開する

--rm : コンテナが終了したときに自動的に削除する

-i : インタラクティブモード、アタッチされていなくてもSTDINを開いたままにする

-t : 擬似TTYを割り当て、ターミナルに似た環境を作成する

-v : ボリュームをバインドマウントする

結論

Dockerコンテナを使用することで、従来のデプロイ方法で発生する問題を解決しながら、便利な操作が可能になります。次回は、アプリケーションのイメージを作成するDockerfileについて見ていきます。

参考文献


Footnotes

  1. Kubernetes

Kubernetesを探る

· 6分の読み時間
Haril Song
Owner, Software Engineer at 42dot

Kubernetesとは?

Kubernetesは以下の機能を提供します:

  • サービスディスカバリとロードバランシング
  • ストレージオーケストレーション
  • 自動ロールアウトとロールバック
  • 自動ビンパッキング
  • 自動スケーリング
  • シークレットと設定管理

詳細については公式ドキュメントを参照してください。

Kubernetesを実行する方法はいくつかありますが、公式サイトではデモンストレーションにminikubeを使用しています。この記事では、Docker Desktopを使ったKubernetesの利用に焦点を当てます。minikubeの使い方を学びたい場合は、公式サイトを参照してください。

では、minikubeについて簡単に触れてみましょう。

Minikube

インストール

brew install minikube

使用方法

コマンドは直感的でシンプルなので、説明はほとんど不要です。

minikube start
minikube dashboard
minikube stop
# 使用後のリソースをクリーンアップ
minikube delete --all

利点

minikubeは、シークレットの設定などの詳細な設定が不要なため、開発目的に適しています。

欠点

一つの大きな欠点は、ダッシュボードを表示するコマンドがハングアップすることがある点です。この問題が主な理由で、この記事を書く際にはminikubeを使用していません。

Docker Desktop

インストール

Docker DesktopのメニューからKubernetesを有効にするだけです。

enable

ダッシュボード

Kubernetesダッシュボードはデフォルトでは有効になっていません。以下のコマンドで有効にできます:

kubectl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/v2.5.0/aio/deploy/recommended.yaml

ダッシュボードの起動

kubectl proxy

このリンクからダッシュボードにアクセスできます。

dashboard

ログインするにはトークンが必要です。トークンの作成方法を見てみましょう。

シークレット

まず、関連ファイルを別々に保存するためにkubernetesフォルダを作成します。

mkdir kubernetes && cd kubernetes
警告

ダッシュボードアカウントに管理者権限を付与することはセキュリティリスクを伴うため、実際の運用で使用する際には注意が必要です。

dashboard-adminuser.yaml

apiVersion: v1
kind: ServiceAccount
metadata:
name: admin-user
namespace: kubernetes-dashboard
kubectl apply -f dashboard-adminuser.yaml

cluster-role-binding.yml

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: admin-user
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: cluster-admin
subjects:
- kind: ServiceAccount
name: admin-user
namespace: kubernetes-dashboard
kubectl apply -f cluster-role-binding.yaml

トークンの作成

kubectl -n kubernetes-dashboard create token admin-user
eyJhbGciOiJSUzI1NiIsImtpZCI6IjVjQjhWQVdpeWdLTlJYeXVKSUpxZndQUkoxdzU3eXFvM2dtMHJQZGY4TUkifQ.eyJhdWQiOlsiaHR0cHM6Ly9rdWJlcm5ldGVzLmRlZmF1bHQuc3ZjLmNsdXN0ZXIubG9jYWwiXSwiZXhwIjox7jU4NTA3NTY1LCJpYXQiOjE2NTg1MDM5NjUsImlzcyI6Imh0dHBzOi8va3ViZXJuZXRlcy5kZWZhdWx0LnN2Yy5jbHVzdGVyLmxvY2FsIiwia3ViZXJuZXRlcy5pbyI6eyJuYW4lc3BhY2UiOiJrdWJlcm5ldGVzLWRhc2hib2FyZCIsInNlcnZpY2VhY2NvdW55Ijp7Im5hbWUiOiJhZG1pbi11c2VyIiwidWlkIjoiZTRkODM5NjQtZWE2MC00ZWI0LTk1NDgtZjFjNWQ3YWM4ZGQ3In19LCJuYmYiOjE2NTg1MDM5NjUsInN1YiI6InN5c3RlbTpzZXJ2aWNlYWNjb3VudDprdWJlcm5ldGVzLWRhc2hib2FyZDphZG1pbi11c2VyIn1.RjoUaQnhTVKvzpAx_rToItI8HTZsr-6brMHWL63ca1_D4QIMCxU-zz7HFK04tCvOwyOTWw603XPDCv-ovjs1lM6A3tdgncqs8z1oTRamM4E-Sum8oi7cKnmVFSLjfLKqQxapBvZF5x-SxJ8Myla-izQxYkCtbWIlc6JfShxCSBJvfwSGW8c6kKdYdJv1QQdU1BfPY1sVz__cLNPA70_OpoosHevfVV86hsMvxCwVkNQHIpGlBX-NPog4nLY4gfuCMxKqjdVh8wLT7yS-E3sUJiXCcPJ2-BFSen4y-RIDbg18qbCtE3hQBr033Mfuly1Wc12UkU4bQeiF5SerODDn-g

生成されたトークンを使用してログインします。

welcome-view アクセス成功!

デプロイメントの作成

イメージを使用してデプロイメントを作成します。この記事では、事前に準備されたgolangを使用したウェブサーバーを使用します。

kubectl create deployment rest-server --image=songkg7/rest-server

コマンドが正常に実行されると、ダッシュボードでの変更を簡単に監視できます。

create-deployment デプロイメント作成後、ダッシュボードが即座に更新されます。

しかし、CLIを使用してこれを確認する方法も学びましょう(根本的な方法です...!)。

ステータスの確認

kubectl get deployments

get-deployment

デプロイメントが作成されると、ポッドも同時に生成されます。

kubectl get pods -o wide

get-pods

すべてが正常に動作していることを確認したら、ウェブサーバーにリクエストを送信してみましょう。curlの代わりにhttpie1を使用します。curlに慣れている場合は、それを使用しても構いません。

http localhost:8080/ping

error

すべてが正常に動作しているように見えるのに、なぜ応答を受け取れないのでしょうか? 🤔

これは、サービスがまだ外部に公開されていないためです。デフォルトでは、Kubernetesのポッドは内部でのみ通信できます。サービスを外部に公開しましょう。

サービスの公開

kubectl expose deployment rest-server --type=LoadBalancer --port=8080

サービスがポート8080を使用しているため、このポートを開きます。異なるポートを使用すると接続に問題が生じる可能性があります。

では、再度リクエストを送信してみましょう。

http localhost:8080/ping

200

成功した応答を受け取ることができます。

参考資料


Footnotes

  1. Elegant httpie

[Java] コレクションをよりコレクションらしくする - Iterable

· 4分の読み時間
Haril Song
Owner, Software Engineer at 42dot

概要

// Iterableを実装するJavaのコレクション。
public interface Collection<E> extends Iterable<E>

ファーストクラスコレクションはオブジェクトを扱う上で非常に便利な方法です。しかし、「ファーストクラスコレクション」という名前にもかかわらず、実際にはCollectionをフィールドとして保持しているだけで、実際にはCollectionではないため、Collectionが提供するさまざまなメソッドを使用することはできません。この記事では、Iterableを使用してファーストクラスコレクションをより実際のCollectionに近づける方法を紹介します。

簡単な例を見てみましょう。

@Value
public class LottoNumber {
int value;

public static LottoNumber create(int value) {
return new LottoNumber(value);
}
}
public class LottoNumbers {

private final List<LottoNumber> lottoNumbers;

private LottoNumbers(List<LottoNumber> lottoNumbers) {
this.lottoNumbers = lottoNumbers;
}

public static LottoNumbers create(LottoNumber... numbers) {
return new LottoNumbers(List.of(numbers));
}

// Listのメソッドを使用するためにisEmpty()メソッドを委譲します。
public boolean isEmpty() {
return lottoNumbers.isEmpty();
}
}

LottoNumbersLottoNumberをリストとして保持するファーストクラスコレクションです。リストが空かどうかを確認するために、isEmpty()を実装しています。

isEmpty()の簡単なテストを書いてみましょう。

@Test
void isEmpty() {
LottoNumber lottoNumber = LottoNumber.create(7);
LottoNumbers lottoNumbers = LottoNumbers.create(lottoNumber);

assertThat(lottoNumbers.isEmpty()).isFalse();
}

悪くはありませんが、AssertJはコレクションをテストするためのさまざまなメソッドを提供しています。

  • has..
  • contains...
  • isEmpty()

ファーストクラスコレクションはCollectionではないため、これらの便利なアサートメソッドを使用することはできません。

より正確には、iterator()がないと要素を反復処理できないため、これらを使用することができません。iterator()を使用するには、Iterableを実装するだけです。

実装は非常に簡単です。

public class LottoNumbers implements Iterable<LottoNumber> {

//...

@Override
public Iterator<LottoNumber> iterator() {
return lottoNumbers.iterator();
}
}

ファーストクラスコレクションはすでにCollectionを持っているので、isEmpty()を委譲したのと同じように、単にそれを返すだけです。

@Test
void isEmpty_iterable() {
LottoNumber lottoNumber = LottoNumber.create(7);
LottoNumbers lottoNumbers = LottoNumbers.create(lottoNumber);

assertThat(lottoNumbers).containsExactly(lottoNumber);
assertThat(lottoNumbers).isNotEmpty();
assertThat(lottoNumbers).hasSize(1);
}

これでさまざまなテストメソッドを使用できるようになりました。

テストだけでなく、機能の実装においても便利に使用できます。

for (LottoNumber lottoNumber : lottoNumbers) {
System.out.println("lottoNumber: " + lottoNumber);
}

これはforループがiterator()を使用するため可能です。

結論

Iterableを実装することで、より豊富な機能を使用することができます。実装は難しくなく、機能拡張に近いので、ファーストクラスコレクションを持っている場合は積極的にIterableを活用しましょう。

エレガントなHTTP CLI、HTTPie

· 2分の読み時間
Haril Song
Owner, Software Engineer at 42dot

概要

curlコマンドを置き換えることができるCLIツール

Linuxを頻繁に使用する開発者であれば、curlコマンドをよく使うでしょう。サーバーから外部APIリクエストを送信するための必須コマンドですが、出力の可読性が低いという欠点があります。HTTPieはこの欠点を解消できる興味深いツールなので、紹介しましょう。

インストール

Macユーザーの場合、brewを使って簡単にインストールできます。

brew install httpie

CentOSの場合、yumを使ってインストールできます。

yum install epel-release
yum install httpie

使用方法

まず、curlを使ってGETリクエストを送信する方法です。

curl https://httpie.io/hello

curl-get

次に、HTTPieを使って比較してみましょう。

https httpie.io/hello

get

コマンドのあらゆる面で可読性が大幅に向上しています。レスポンスとヘッダーの値がデフォルトで含まれているため、別のコマンドを使用せずに一目でさまざまな情報を得ることができます。

コマンドではhttpshttpが区別されることに注意してください。

http localhost:8080

公式サイトに記載されているように、POSTリクエストを送信することもできます。

http -a USERNAME POST https://api.github.com/repos/httpie/httpie/issues/83/comments body='HTTPie is awesome! :heart:'

その他のさまざまな機能についてはGitHubで説明されているので、うまく活用すれば生産性を大幅に向上させることができます。

参考

ゲッターとセッターに関する真実と誤解

· 6分の読み時間
Haril Song
Owner, Software Engineer at 42dot

Googleで「getter/setter」を検索すると、数多くの記事が見つかります。その多くは、カプセル化や情報隠蔽といったキーワードに焦点を当てて、getter/setterを使用する理由を説明しています。

一般的な説明では、フィールド変数をprivateとして宣言し、外部からのアクセスを防ぎ、getter/setterを通じてのみ公開することでカプセル化が達成されるとされています。

しかし、getter/setterを使用することで本当にデータをカプセル化できるのでしょうか?

実際には、getter/setterではカプセル化を全く達成できません。 カプセル化を達成するためには、ゲッターとセッターの使用を避けるべきです。これを理解するためには、カプセル化の明確な理解が必要です。

カプセル化とは?

オブジェクト指向プログラミングにおけるカプセル化には、オブジェクトの属性(データフィールド)と動作(メソッド)を一緒にまとめることと、オブジェクトの実装の詳細を内部に隠すことの二つの側面があります。 - Wikipedia

カプセル化とは、外部のエンティティがオブジェクトの内部属性を完全に知ることができないようにすることを意味します。

なぜゲッターとセッターはカプセル化を達成できないのか

学んだように、カプセル化は外部のエンティティがオブジェクトの内部属性を知ることができないようにすることを指します。しかし、getter/setterは特定のフィールドが存在することを外部に露呈しています。例を見てみましょう。

public class Student {

private String name;
private int age;

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}

public String introduce() {
return String.format("私の名前は%sで、年齢は%d歳です。", name, age);
}
}
class StudentTest {

@Test
void student() {
Student student = new Student();
student.setName("ジョン");
student.setAge(20);
String introduce = student.introduce();

assertThat(student.getName()).isEqualTo("ジョン");
assertThat(student.getAge()).isEqualTo(20);
assertThat(introduce).isEqualTo("私の名前はジョンで、年齢は20歳です。");
}
}

Studentクラスの外部から、そのクラスにはnameageという属性があることが明らかです。この状態をカプセル化されていると考えられるでしょうか?

もしStudentからage属性を削除した場合、getter/setterを使用しているすべての場所で変更が必要になります。これにより強い結合が生じます。

真のカプセル化とは、オブジェクトの内部構造の変更が外部のエンティティに影響を与えないことを意味します。公開インターフェースを除いて。

内部実装を隠してみましょう。

public class Student {

private String name;
private int age;

public Student(String name, int age) {
this.name = name;
this.age = age;
}

public String introduce() {
return String.format("私の名前は%sで、年齢は%d歳です。", name, age);
}
}
class StudentTest {

@Test
void student() {
Student student = new Student("ジョン", 20);
String introduce = student.introduce();

assertThat(introduce).isEqualTo("私の名前はジョンで、年齢は20歳です。");
}
}

このように、オブジェクトは公開インターフェースを通じて内部実装を露呈しません。どのようなデータを保持しているかを知ることができず、変更も防ぎ、メッセージを通じてのみ通信します。

結論

カプセル化はオブジェクト指向設計において重要なトピックであり、外部要因に依存しない設計を強調します。カプセル化のレベルについては意見が分かれ、gettersetterの両方を使用しないことを推奨する人もいれば、getterの使用は許容されるとする人もいます。

個人的には、可能な限りgetterの使用を避けるべきだと考えていますが、特にテストにおいては、ゲッターやセッターがあるとテストコードの記述が容易になる場合があります。カプセル化のレベルを決定するには、現在の状況や開発中のコードの目的に依存します。

良い設計は常にトレードオフの過程を経て生まれます。

情報

すべてのサンプルコードはGitHubで確認できます。

「sitemap.xmlが見つかりません」問題の解決方法

· 1分の読み時間
Haril Song
Owner, Software Engineer at 42dot

ブログのインデックスをGoogleに登録するためにsitemap.xmlを登録していましたが、「sitemapが見つかりません」というエラーメッセージばかりが表示されていました。最終的に解決方法を見つけたので、ここで共有します。

この方法がすべてのケースで解決するわけではありませんが、試してみる価値はあると思います。

以下のコマンドを実行するだけです:

curl https://www.google.com/ping\?sitemap\={あなたのsitemapのパス}

そして、再度サーチコンソールを確認すると...!

sitemap-success ほぼ1ヶ月かかってやっと解決しました...😢

ついにsitemapが認識されました。

お役に立てれば幸いです!

参考